blob: 0294fef577fab134c451d6180cd8aab87fc8ab01 [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * xfrm4_policy.c
4 *
5 * Changes:
6 * Kazunori MIYAZAWA @USAGI
7 * YOSHIFUJI Hideaki @USAGI
8 * Split up af-specific portion
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09009 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 */
11
Herbert Xu66cdb3c2007-11-13 21:37:28 -080012#include <linux/err.h>
13#include <linux/kernel.h>
Herbert Xuaabc9762005-05-03 16:27:10 -070014#include <linux/inetdevice.h>
Herbert Xu45ff5a32007-11-13 21:35:32 -080015#include <net/dst.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <net/xfrm.h>
17#include <net/ip.h>
David Ahern385add92015-09-29 20:07:13 -070018#include <net/l3mdev.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019
David S. Miller8f01cb02011-05-09 15:13:28 -070020static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4,
David Ahern42a7b322015-08-10 16:58:11 -060021 int tos, int oif,
David S. Miller8f01cb02011-05-09 15:13:28 -070022 const xfrm_address_t *saddr,
Lorenzo Colitti077fbac2017-08-11 02:11:33 +090023 const xfrm_address_t *daddr,
24 u32 mark)
Linus Torvalds1da177e2005-04-16 15:20:36 -070025{
Herbert Xu66cdb3c2007-11-13 21:37:28 -080026 struct rtable *rt;
Patrick McHardya1e59ab2006-09-19 12:57:34 -070027
David S. Miller8f01cb02011-05-09 15:13:28 -070028 memset(fl4, 0, sizeof(*fl4));
29 fl4->daddr = daddr->a4;
30 fl4->flowi4_tos = tos;
David Ahern40867d72022-03-14 14:45:51 -060031 fl4->flowi4_l3mdev = l3mdev_master_ifindex_by_index(net, oif);
Lorenzo Colitti077fbac2017-08-11 02:11:33 +090032 fl4->flowi4_mark = mark;
Herbert Xu66cdb3c2007-11-13 21:37:28 -080033 if (saddr)
David S. Miller8f01cb02011-05-09 15:13:28 -070034 fl4->saddr = saddr->a4;
Herbert Xu66cdb3c2007-11-13 21:37:28 -080035
David S. Miller8f01cb02011-05-09 15:13:28 -070036 rt = __ip_route_output_key(net, fl4);
David S. Millerb23dd4f2011-03-02 14:31:35 -080037 if (!IS_ERR(rt))
38 return &rt->dst;
39
40 return ERR_CAST(rt);
Herbert Xu66cdb3c2007-11-13 21:37:28 -080041}
42
David Ahern42a7b322015-08-10 16:58:11 -060043static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, int oif,
David S. Miller8f01cb02011-05-09 15:13:28 -070044 const xfrm_address_t *saddr,
Lorenzo Colitti077fbac2017-08-11 02:11:33 +090045 const xfrm_address_t *daddr,
46 u32 mark)
David S. Miller8f01cb02011-05-09 15:13:28 -070047{
48 struct flowi4 fl4;
49
Lorenzo Colitti077fbac2017-08-11 02:11:33 +090050 return __xfrm4_dst_lookup(net, &fl4, tos, oif, saddr, daddr, mark);
David S. Miller8f01cb02011-05-09 15:13:28 -070051}
52
David Ahern42a7b322015-08-10 16:58:11 -060053static int xfrm4_get_saddr(struct net *net, int oif,
Lorenzo Colitti077fbac2017-08-11 02:11:33 +090054 xfrm_address_t *saddr, xfrm_address_t *daddr,
55 u32 mark)
Herbert Xu66cdb3c2007-11-13 21:37:28 -080056{
57 struct dst_entry *dst;
David S. Miller8f01cb02011-05-09 15:13:28 -070058 struct flowi4 fl4;
Herbert Xu66cdb3c2007-11-13 21:37:28 -080059
Lorenzo Colitti077fbac2017-08-11 02:11:33 +090060 dst = __xfrm4_dst_lookup(net, &fl4, 0, oif, NULL, daddr, mark);
Herbert Xu66cdb3c2007-11-13 21:37:28 -080061 if (IS_ERR(dst))
62 return -EHOSTUNREACH;
63
David S. Miller8f01cb02011-05-09 15:13:28 -070064 saddr->a4 = fl4.saddr;
Herbert Xu66cdb3c2007-11-13 21:37:28 -080065 dst_release(dst);
66 return 0;
Patrick McHardya1e59ab2006-09-19 12:57:34 -070067}
68
Herbert Xu87c1e122010-03-02 02:51:56 +000069static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
David S. Miller0c7b3ee2011-02-22 17:48:57 -080070 const struct flowi *fl)
Herbert Xu25ee3282007-12-11 09:32:34 -080071{
Eric Dumazet05d6d492024-04-29 13:30:09 +000072 struct rtable *rt = dst_rtable(xdst->route);
David S. Miller7e1dc7b2011-03-12 02:42:11 -050073 const struct flowi4 *fl4 = &fl->u.ip4;
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
Yan, Zhengb7323392011-10-22 21:58:20 +000075 xdst->u.rt.rt_iif = fl4->flowi4_iif;
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
Herbert Xu25ee3282007-12-11 09:32:34 -080077 xdst->u.dst.dev = dev;
Jakub Kicinskid62607c2022-06-07 21:39:55 -070078 netdev_hold(dev, &xdst->u.dst.dev_tracker, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -070079
Herbert Xu25ee3282007-12-11 09:32:34 -080080 /* Sheit... I remember I did this right. Apparently,
81 * it was magically lost, so this code needs audit */
David S. Miller9917e1e82012-07-17 14:44:26 -070082 xdst->u.rt.rt_is_input = rt->rt_is_input;
Herbert Xu25ee3282007-12-11 09:32:34 -080083 xdst->u.rt.rt_flags = rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST |
84 RTCF_LOCAL);
85 xdst->u.rt.rt_type = rt->rt_type;
David Ahern77d5bc72019-09-17 10:39:49 -070086 xdst->u.rt.rt_uses_gateway = rt->rt_uses_gateway;
David Ahern1550c172019-04-05 16:30:27 -070087 xdst->u.rt.rt_gw_family = rt->rt_gw_family;
88 if (rt->rt_gw_family == AF_INET)
89 xdst->u.rt.rt_gw4 = rt->rt_gw4;
David Ahern0f5f7d72019-04-05 16:30:29 -070090 else if (rt->rt_gw_family == AF_INET6)
91 xdst->u.rt.rt_gw6 = rt->rt_gw6;
David S. Miller59436342012-07-10 06:58:42 -070092 xdst->u.rt.rt_pmtu = rt->rt_pmtu;
Sabrina Dubrocad52e5a72018-03-14 10:21:14 +010093 xdst->u.rt.rt_mtu_locked = rt->rt_mtu_locked;
Xin Long510c3212018-02-14 19:06:02 +080094 rt_add_uncached_list(&xdst->u.rt);
Miika Komu43372262007-02-06 14:27:32 -080095
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070097}
98
David S. Miller6700c272012-07-17 03:29:28 -070099static void xfrm4_update_pmtu(struct dst_entry *dst, struct sock *sk,
Hangbin Liubd085ef2019-12-22 10:51:09 +0800100 struct sk_buff *skb, u32 mtu,
101 bool confirm_neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102{
103 struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
104 struct dst_entry *path = xdst->route;
105
Hangbin Liubd085ef2019-12-22 10:51:09 +0800106 path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107}
108
David S. Miller6700c272012-07-17 03:29:28 -0700109static void xfrm4_redirect(struct dst_entry *dst, struct sock *sk,
110 struct sk_buff *skb)
David S. Miller55be7a92012-07-11 21:27:49 -0700111{
112 struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
113 struct dst_entry *path = xdst->route;
114
David S. Miller6700c272012-07-17 03:29:28 -0700115 path->ops->redirect(path, sk, skb);
David S. Miller55be7a92012-07-11 21:27:49 -0700116}
117
Herbert Xuaabc9762005-05-03 16:27:10 -0700118static void xfrm4_dst_destroy(struct dst_entry *dst)
119{
120 struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
121
David S. Miller62fa8a82011-01-26 20:51:05 -0800122 dst_destroy_metrics_generic(dst);
Maxime Bizon418a7302023-04-20 20:25:08 +0200123 rt_del_uncached_list(&xdst->u.rt);
Herbert Xuaabc9762005-05-03 16:27:10 -0700124 xfrm_dst_destroy(xdst);
125}
126
Dan Streetmana8a572a2015-10-29 09:51:16 -0400127static struct dst_ops xfrm4_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 .family = AF_INET,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 .update_pmtu = xfrm4_update_pmtu,
David S. Miller55be7a92012-07-11 21:27:49 -0700130 .redirect = xfrm4_redirect,
David S. Miller62fa8a82011-01-26 20:51:05 -0800131 .cow_metrics = dst_cow_metrics_generic,
Herbert Xuaabc9762005-05-03 16:27:10 -0700132 .destroy = xfrm4_dst_destroy,
Zhengchao Shao43c28172023-08-21 16:41:04 +0800133 .ifdown = xfrm_dst_ifdown,
Herbert Xu862b82c2007-11-13 21:43:11 -0800134 .local_out = __ip_local_out,
Florian Westphal3c2a89d2017-07-17 13:57:20 +0200135 .gc_thresh = 32768,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136};
137
Florian Westphal37b10382017-02-07 15:00:19 +0100138static const struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
Dan Streetmana8a572a2015-10-29 09:51:16 -0400139 .dst_ops = &xfrm4_dst_ops_template,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 .dst_lookup = xfrm4_dst_lookup,
Patrick McHardya1e59ab2006-09-19 12:57:34 -0700141 .get_saddr = xfrm4_get_saddr,
Herbert Xu25ee3282007-12-11 09:32:34 -0800142 .fill_dst = xfrm4_fill_dst,
David S. Miller2774c132011-03-01 14:59:04 -0800143 .blackhole_route = ipv4_blackhole_route,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144};
145
Randy Dunlapf8167002009-08-04 20:18:33 -0700146#ifdef CONFIG_SYSCTL
Neil Hormana44a4a02009-07-27 08:22:46 +0000147static struct ctl_table xfrm4_policy_table[] = {
148 {
Neil Hormana44a4a02009-07-27 08:22:46 +0000149 .procname = "xfrm4_gc_thresh",
Alexey Dobriyand7c75442010-01-24 22:47:53 -0800150 .data = &init_net.xfrm.xfrm4_dst_ops.gc_thresh,
Neil Hormana44a4a02009-07-27 08:22:46 +0000151 .maxlen = sizeof(int),
152 .mode = 0644,
153 .proc_handler = proc_dointvec,
154 },
Neil Hormana44a4a02009-07-27 08:22:46 +0000155};
156
Arnd Bergmann318d3cc2016-06-16 15:59:25 +0200157static __net_init int xfrm4_net_sysctl_init(struct net *net)
Michal Kubecek8d068872013-02-06 10:46:33 +0100158{
159 struct ctl_table *table;
160 struct ctl_table_header *hdr;
161
162 table = xfrm4_policy_table;
163 if (!net_eq(net, &init_net)) {
164 table = kmemdup(table, sizeof(xfrm4_policy_table), GFP_KERNEL);
165 if (!table)
166 goto err_alloc;
167
168 table[0].data = &net->xfrm.xfrm4_dst_ops.gc_thresh;
169 }
170
Joel Granadosc8997102023-08-09 12:50:03 +0200171 hdr = register_net_sysctl_sz(net, "net/ipv4", table,
172 ARRAY_SIZE(xfrm4_policy_table));
Michal Kubecek8d068872013-02-06 10:46:33 +0100173 if (!hdr)
174 goto err_reg;
175
176 net->ipv4.xfrm4_hdr = hdr;
177 return 0;
178
179err_reg:
180 if (!net_eq(net, &init_net))
181 kfree(table);
182err_alloc:
183 return -ENOMEM;
184}
185
Arnd Bergmann318d3cc2016-06-16 15:59:25 +0200186static __net_exit void xfrm4_net_sysctl_exit(struct net *net)
Michal Kubecek8d068872013-02-06 10:46:33 +0100187{
Thomas Weißschuhbfa858f2024-04-18 11:40:08 +0200188 const struct ctl_table *table;
Michal Kubecek8d068872013-02-06 10:46:33 +0100189
Ian Morris51456b22015-04-03 09:17:26 +0100190 if (!net->ipv4.xfrm4_hdr)
Michal Kubecek8d068872013-02-06 10:46:33 +0100191 return;
192
193 table = net->ipv4.xfrm4_hdr->ctl_table_arg;
194 unregister_net_sysctl_table(net->ipv4.xfrm4_hdr);
195 if (!net_eq(net, &init_net))
196 kfree(table);
197}
Dan Streetmana8a572a2015-10-29 09:51:16 -0400198#else /* CONFIG_SYSCTL */
Arnd Bergmann318d3cc2016-06-16 15:59:25 +0200199static inline int xfrm4_net_sysctl_init(struct net *net)
Dan Streetmana8a572a2015-10-29 09:51:16 -0400200{
201 return 0;
202}
203
Arnd Bergmann318d3cc2016-06-16 15:59:25 +0200204static inline void xfrm4_net_sysctl_exit(struct net *net)
Dan Streetmana8a572a2015-10-29 09:51:16 -0400205{
206}
207#endif
208
209static int __net_init xfrm4_net_init(struct net *net)
210{
211 int ret;
212
213 memcpy(&net->xfrm.xfrm4_dst_ops, &xfrm4_dst_ops_template,
214 sizeof(xfrm4_dst_ops_template));
215 ret = dst_entries_init(&net->xfrm.xfrm4_dst_ops);
216 if (ret)
217 return ret;
218
219 ret = xfrm4_net_sysctl_init(net);
220 if (ret)
221 dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
222
223 return ret;
224}
225
226static void __net_exit xfrm4_net_exit(struct net *net)
227{
228 xfrm4_net_sysctl_exit(net);
229 dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
230}
Michal Kubecek8d068872013-02-06 10:46:33 +0100231
232static struct pernet_operations __net_initdata xfrm4_net_ops = {
233 .init = xfrm4_net_init,
234 .exit = xfrm4_net_exit,
235};
Neil Hormana44a4a02009-07-27 08:22:46 +0000236
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237static void __init xfrm4_policy_init(void)
238{
Florian Westphala2817d82017-02-07 15:00:17 +0100239 xfrm_policy_register_afinfo(&xfrm4_policy_afinfo, AF_INET);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240}
241
Steffen Klassert703fb942012-11-13 08:52:24 +0100242void __init xfrm4_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243{
Alexey Dobriyand7c75442010-01-24 22:47:53 -0800244 xfrm4_state_init();
245 xfrm4_policy_init();
Steffen Klassert2f32b512014-03-14 07:28:07 +0100246 xfrm4_protocol_init();
Michal Kubecek8d068872013-02-06 10:46:33 +0100247 register_pernet_subsys(&xfrm4_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248}