xref: /openbmc/linux/net/ipv6/xfrm6_policy.c (revision 278002edb19bce2c628fafb0af936e77000f3a5b)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * xfrm6_policy.c: based on xfrm4_policy.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Authors:
61da177e4SLinus Torvalds  *	Mitsuru KANDA @USAGI
71da177e4SLinus Torvalds  *	Kazunori MIYAZAWA @USAGI
81da177e4SLinus Torvalds  *	Kunihiro Ishiguro <kunihiro@ipinfusion.com>
91da177e4SLinus Torvalds  *		IPv6 support
101da177e4SLinus Torvalds  *	YOSHIFUJI Hideaki
111da177e4SLinus Torvalds  *		Split up af-specific portion
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  */
141da177e4SLinus Torvalds 
1566cdb3caSHerbert Xu #include <linux/err.h>
1666cdb3caSHerbert Xu #include <linux/kernel.h>
17aabc9761SHerbert Xu #include <linux/netdevice.h>
18aabc9761SHerbert Xu #include <net/addrconf.h>
1945ff5a3fSHerbert Xu #include <net/dst.h>
201da177e4SLinus Torvalds #include <net/xfrm.h>
211da177e4SLinus Torvalds #include <net/ip.h>
221da177e4SLinus Torvalds #include <net/ipv6.h>
231da177e4SLinus Torvalds #include <net/ip6_route.h>
24385add90SDavid Ahern #include <net/l3mdev.h>
251da177e4SLinus Torvalds 
xfrm6_dst_lookup(const struct xfrm_dst_lookup_params * params)26ac1d820eSEyal Birger static struct dst_entry *xfrm6_dst_lookup(const struct xfrm_dst_lookup_params *params)
271da177e4SLinus Torvalds {
287e1dc7b6SDavid S. Miller 	struct flowi6 fl6;
2966cdb3caSHerbert Xu 	struct dst_entry *dst;
3066cdb3caSHerbert Xu 	int err;
3166cdb3caSHerbert Xu 
327e1dc7b6SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
33ac1d820eSEyal Birger 	fl6.flowi6_l3mdev = l3mdev_master_ifindex_by_index(params->net,
34ac1d820eSEyal Birger 							   params->oif);
35ac1d820eSEyal Birger 	fl6.flowi6_mark = params->mark;
36ac1d820eSEyal Birger 	memcpy(&fl6.daddr, params->daddr, sizeof(fl6.daddr));
37ac1d820eSEyal Birger 	if (params->saddr)
38ac1d820eSEyal Birger 		memcpy(&fl6.saddr, params->saddr, sizeof(fl6.saddr));
3966cdb3caSHerbert Xu 
40c1530660SEyal Birger 	fl6.flowi4_proto = params->ipproto;
41c1530660SEyal Birger 	fl6.uli = params->uli;
42c1530660SEyal Birger 
43ac1d820eSEyal Birger 	dst = ip6_route_output(params->net, NULL, &fl6);
4466cdb3caSHerbert Xu 
4566cdb3caSHerbert Xu 	err = dst->error;
4666cdb3caSHerbert Xu 	if (dst->error) {
474251320fSVille Nuorvala 		dst_release(dst);
4866cdb3caSHerbert Xu 		dst = ERR_PTR(err);
4966cdb3caSHerbert Xu 	}
5066cdb3caSHerbert Xu 
5166cdb3caSHerbert Xu 	return dst;
521da177e4SLinus Torvalds }
531da177e4SLinus Torvalds 
xfrm6_get_saddr(xfrm_address_t * saddr,const struct xfrm_dst_lookup_params * params)54ac1d820eSEyal Birger static int xfrm6_get_saddr(xfrm_address_t *saddr,
55ac1d820eSEyal Birger 			   const struct xfrm_dst_lookup_params *params)
56a1e59abfSPatrick McHardy {
5766cdb3caSHerbert Xu 	struct dst_entry *dst;
58191cd582SBrian Haley 	struct net_device *dev;
59f897d717SEric Dumazet 	struct inet6_dev *idev;
60a1e59abfSPatrick McHardy 
61ac1d820eSEyal Birger 	dst = xfrm6_dst_lookup(params);
6266cdb3caSHerbert Xu 	if (IS_ERR(dst))
63a1e59abfSPatrick McHardy 		return -EHOSTUNREACH;
6466cdb3caSHerbert Xu 
65f897d717SEric Dumazet 	idev = ip6_dst_idev(dst);
66f897d717SEric Dumazet 	if (!idev) {
67f897d717SEric Dumazet 		dst_release(dst);
68f897d717SEric Dumazet 		return -EHOSTUNREACH;
69f897d717SEric Dumazet 	}
70f897d717SEric Dumazet 	dev = idev->dev;
71ac1d820eSEyal Birger 	ipv6_dev_get_saddr(dev_net(dev), dev, &params->daddr->in6, 0,
72ac1d820eSEyal Birger 			   &saddr->in6);
7366cdb3caSHerbert Xu 	dst_release(dst);
7466cdb3caSHerbert Xu 	return 0;
75a1e59abfSPatrick McHardy }
76a1e59abfSPatrick McHardy 
xfrm6_fill_dst(struct xfrm_dst * xdst,struct net_device * dev,const struct flowi * fl)7787c1e12bSHerbert Xu static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
780c7b3eefSDavid S. Miller 			  const struct flowi *fl)
7925ee3286SHerbert Xu {
80*797a4c1fSEric Dumazet 	struct rt6_info *rt = dst_rt6_info(xdst->route);
811da177e4SLinus Torvalds 
8225ee3286SHerbert Xu 	xdst->u.dst.dev = dev;
83d62607c3SJakub Kicinski 	netdev_hold(dev, &xdst->u.dst.dev_tracker, GFP_ATOMIC);
841da177e4SLinus Torvalds 
85bc8e4b95SNicolas Dichtel 	xdst->u.rt6.rt6i_idev = in6_dev_get(dev);
8684c4a9dfSCong Wang 	if (!xdst->u.rt6.rt6i_idev) {
87d62607c3SJakub Kicinski 		netdev_put(dev, &xdst->u.dst.dev_tracker);
8825ee3286SHerbert Xu 		return -ENODEV;
8984c4a9dfSCong Wang 	}
90c82f963eSMiika Komu 
911da177e4SLinus Torvalds 	/* Sheit... I remember I did this right. Apparently,
921da177e4SLinus Torvalds 	 * it was magically lost, so this code needs audit */
9325ee3286SHerbert Xu 	xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST |
9425ee3286SHerbert Xu 						   RTF_LOCAL);
95b197df4fSMartin KaFai Lau 	xdst->route_cookie = rt6_get_cookie(rt);
9625ee3286SHerbert Xu 	xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway;
9725ee3286SHerbert Xu 	xdst->u.rt6.rt6i_dst = rt->rt6i_dst;
9825ee3286SHerbert Xu 	xdst->u.rt6.rt6i_src = rt->rt6i_src;
99510c321bSXin Long 	rt6_uncached_list_add(&xdst->u.rt6);
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	return 0;
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds 
xfrm6_update_pmtu(struct dst_entry * dst,struct sock * sk,struct sk_buff * skb,u32 mtu,bool confirm_neigh)1046700c270SDavid S. Miller static void xfrm6_update_pmtu(struct dst_entry *dst, struct sock *sk,
105bd085ef6SHangbin Liu 			      struct sk_buff *skb, u32 mtu,
106bd085ef6SHangbin Liu 			      bool confirm_neigh)
1071da177e4SLinus Torvalds {
1081da177e4SLinus Torvalds 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
1091da177e4SLinus Torvalds 	struct dst_entry *path = xdst->route;
1101da177e4SLinus Torvalds 
111bd085ef6SHangbin Liu 	path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh);
1121da177e4SLinus Torvalds }
1131da177e4SLinus Torvalds 
xfrm6_redirect(struct dst_entry * dst,struct sock * sk,struct sk_buff * skb)1146700c270SDavid S. Miller static void xfrm6_redirect(struct dst_entry *dst, struct sock *sk,
1156700c270SDavid S. Miller 			   struct sk_buff *skb)
116ec18d9a2SDavid S. Miller {
117ec18d9a2SDavid S. Miller 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
118ec18d9a2SDavid S. Miller 	struct dst_entry *path = xdst->route;
119ec18d9a2SDavid S. Miller 
1206700c270SDavid S. Miller 	path->ops->redirect(path, sk, skb);
121ec18d9a2SDavid S. Miller }
122ec18d9a2SDavid S. Miller 
xfrm6_dst_destroy(struct dst_entry * dst)123aabc9761SHerbert Xu static void xfrm6_dst_destroy(struct dst_entry *dst)
124aabc9761SHerbert Xu {
125aabc9761SHerbert Xu 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
126aabc9761SHerbert Xu 
12762fa8a84SDavid S. Miller 	dst_destroy_metrics_generic(dst);
128510c321bSXin Long 	rt6_uncached_list_del(&xdst->u.rt6);
129cc9b364bSZhang Changzhong 	if (likely(xdst->u.rt6.rt6i_idev))
130cc9b364bSZhang Changzhong 		in6_dev_put(xdst->u.rt6.rt6i_idev);
131aabc9761SHerbert Xu 	xfrm_dst_destroy(xdst);
132aabc9761SHerbert Xu }
133aabc9761SHerbert Xu 
xfrm6_dst_ifdown(struct dst_entry * dst,struct net_device * dev)13443c28172SZhengchao Shao static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev)
135aabc9761SHerbert Xu {
136aabc9761SHerbert Xu 	struct xfrm_dst *xdst;
137aabc9761SHerbert Xu 
138aabc9761SHerbert Xu 	xdst = (struct xfrm_dst *)dst;
139aabc9761SHerbert Xu 	if (xdst->u.rt6.rt6i_idev->dev == dev) {
1405a3e55d6SDenis V. Lunev 		struct inet6_dev *loopback_idev =
141c346dca1SYOSHIFUJI Hideaki 			in6_dev_get(dev_net(dev)->loopback_dev);
142aabc9761SHerbert Xu 
143aabc9761SHerbert Xu 		do {
144aabc9761SHerbert Xu 			in6_dev_put(xdst->u.rt6.rt6i_idev);
145aabc9761SHerbert Xu 			xdst->u.rt6.rt6i_idev = loopback_idev;
146aabc9761SHerbert Xu 			in6_dev_hold(loopback_idev);
147b92cf4aaSDavid Miller 			xdst = (struct xfrm_dst *)xfrm_dst_child(&xdst->u.dst);
148aabc9761SHerbert Xu 		} while (xdst->u.dst.xfrm);
149aabc9761SHerbert Xu 
150aabc9761SHerbert Xu 		__in6_dev_put(loopback_idev);
151aabc9761SHerbert Xu 	}
152aabc9761SHerbert Xu 
153aabc9761SHerbert Xu 	xfrm_dst_ifdown(dst, dev);
154aabc9761SHerbert Xu }
155aabc9761SHerbert Xu 
156a8a572a6SDan Streetman static struct dst_ops xfrm6_dst_ops_template = {
1571da177e4SLinus Torvalds 	.family =		AF_INET6,
1581da177e4SLinus Torvalds 	.update_pmtu =		xfrm6_update_pmtu,
159ec18d9a2SDavid S. Miller 	.redirect =		xfrm6_redirect,
16062fa8a84SDavid S. Miller 	.cow_metrics =		dst_cow_metrics_generic,
161aabc9761SHerbert Xu 	.destroy =		xfrm6_dst_destroy,
162aabc9761SHerbert Xu 	.ifdown =		xfrm6_dst_ifdown,
163862b82c6SHerbert Xu 	.local_out =		__ip6_local_out,
1643c2a89ddSFlorian Westphal 	.gc_thresh =		32768,
1651da177e4SLinus Torvalds };
1661da177e4SLinus Torvalds 
16737b10383SFlorian Westphal static const struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
168a8a572a6SDan Streetman 	.dst_ops =		&xfrm6_dst_ops_template,
1691da177e4SLinus Torvalds 	.dst_lookup =		xfrm6_dst_lookup,
170a1e59abfSPatrick McHardy 	.get_saddr =		xfrm6_get_saddr,
17125ee3286SHerbert Xu 	.fill_dst =		xfrm6_fill_dst,
1722774c131SDavid S. Miller 	.blackhole_route =	ip6_blackhole_route,
1731da177e4SLinus Torvalds };
1741da177e4SLinus Torvalds 
xfrm6_policy_init(void)1750013cabaSDaniel Lezcano static int __init xfrm6_policy_init(void)
1761da177e4SLinus Torvalds {
177a2817d8bSFlorian Westphal 	return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo, AF_INET6);
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds 
xfrm6_policy_fini(void)1801da177e4SLinus Torvalds static void xfrm6_policy_fini(void)
1811da177e4SLinus Torvalds {
1821da177e4SLinus Torvalds 	xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo);
1831da177e4SLinus Torvalds }
1841da177e4SLinus Torvalds 
185db71789cSDavid S. Miller #ifdef CONFIG_SYSCTL
186a44a4a00SNeil Horman static struct ctl_table xfrm6_policy_table[] = {
187a44a4a00SNeil Horman 	{
188a44a4a00SNeil Horman 		.procname       = "xfrm6_gc_thresh",
189d7c7544cSAlexey Dobriyan 		.data		= &init_net.xfrm.xfrm6_dst_ops.gc_thresh,
190a44a4a00SNeil Horman 		.maxlen		= sizeof(int),
191a44a4a00SNeil Horman 		.mode		= 0644,
192a44a4a00SNeil Horman 		.proc_handler   = proc_dointvec,
193a44a4a00SNeil Horman 	},
194a44a4a00SNeil Horman 	{ }
195a44a4a00SNeil Horman };
196a44a4a00SNeil Horman 
xfrm6_net_sysctl_init(struct net * net)197a8a572a6SDan Streetman static int __net_init xfrm6_net_sysctl_init(struct net *net)
1988d068875SMichal Kubecek {
1998d068875SMichal Kubecek 	struct ctl_table *table;
2008d068875SMichal Kubecek 	struct ctl_table_header *hdr;
2018d068875SMichal Kubecek 
2028d068875SMichal Kubecek 	table = xfrm6_policy_table;
2038d068875SMichal Kubecek 	if (!net_eq(net, &init_net)) {
2048d068875SMichal Kubecek 		table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL);
2058d068875SMichal Kubecek 		if (!table)
2068d068875SMichal Kubecek 			goto err_alloc;
2078d068875SMichal Kubecek 
2088d068875SMichal Kubecek 		table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh;
2098d068875SMichal Kubecek 	}
2108d068875SMichal Kubecek 
211c899710fSJoel Granados 	hdr = register_net_sysctl_sz(net, "net/ipv6", table,
212c899710fSJoel Granados 				     ARRAY_SIZE(xfrm6_policy_table));
2138d068875SMichal Kubecek 	if (!hdr)
2148d068875SMichal Kubecek 		goto err_reg;
2158d068875SMichal Kubecek 
2168d068875SMichal Kubecek 	net->ipv6.sysctl.xfrm6_hdr = hdr;
2178d068875SMichal Kubecek 	return 0;
2188d068875SMichal Kubecek 
2198d068875SMichal Kubecek err_reg:
2208d068875SMichal Kubecek 	if (!net_eq(net, &init_net))
2218d068875SMichal Kubecek 		kfree(table);
2228d068875SMichal Kubecek err_alloc:
2238d068875SMichal Kubecek 	return -ENOMEM;
2248d068875SMichal Kubecek }
2258d068875SMichal Kubecek 
xfrm6_net_sysctl_exit(struct net * net)226a8a572a6SDan Streetman static void __net_exit xfrm6_net_sysctl_exit(struct net *net)
2278d068875SMichal Kubecek {
2288d068875SMichal Kubecek 	struct ctl_table *table;
2298d068875SMichal Kubecek 
23063159f29SIan Morris 	if (!net->ipv6.sysctl.xfrm6_hdr)
2318d068875SMichal Kubecek 		return;
2328d068875SMichal Kubecek 
2338d068875SMichal Kubecek 	table = net->ipv6.sysctl.xfrm6_hdr->ctl_table_arg;
2348d068875SMichal Kubecek 	unregister_net_sysctl_table(net->ipv6.sysctl.xfrm6_hdr);
2358d068875SMichal Kubecek 	if (!net_eq(net, &init_net))
2368d068875SMichal Kubecek 		kfree(table);
2378d068875SMichal Kubecek }
238a8a572a6SDan Streetman #else /* CONFIG_SYSCTL */
xfrm6_net_sysctl_init(struct net * net)239318d3cc0SArnd Bergmann static inline int xfrm6_net_sysctl_init(struct net *net)
240a8a572a6SDan Streetman {
241a8a572a6SDan Streetman 	return 0;
242a8a572a6SDan Streetman }
243a8a572a6SDan Streetman 
xfrm6_net_sysctl_exit(struct net * net)244318d3cc0SArnd Bergmann static inline void xfrm6_net_sysctl_exit(struct net *net)
245a8a572a6SDan Streetman {
246a8a572a6SDan Streetman }
247a8a572a6SDan Streetman #endif
248a8a572a6SDan Streetman 
xfrm6_net_init(struct net * net)249a8a572a6SDan Streetman static int __net_init xfrm6_net_init(struct net *net)
250a8a572a6SDan Streetman {
251a8a572a6SDan Streetman 	int ret;
252a8a572a6SDan Streetman 
253a8a572a6SDan Streetman 	memcpy(&net->xfrm.xfrm6_dst_ops, &xfrm6_dst_ops_template,
254a8a572a6SDan Streetman 	       sizeof(xfrm6_dst_ops_template));
255a8a572a6SDan Streetman 	ret = dst_entries_init(&net->xfrm.xfrm6_dst_ops);
256a8a572a6SDan Streetman 	if (ret)
257a8a572a6SDan Streetman 		return ret;
258a8a572a6SDan Streetman 
259a8a572a6SDan Streetman 	ret = xfrm6_net_sysctl_init(net);
260a8a572a6SDan Streetman 	if (ret)
261a8a572a6SDan Streetman 		dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
262a8a572a6SDan Streetman 
263a8a572a6SDan Streetman 	return ret;
264a8a572a6SDan Streetman }
265a8a572a6SDan Streetman 
xfrm6_net_exit(struct net * net)266a8a572a6SDan Streetman static void __net_exit xfrm6_net_exit(struct net *net)
267a8a572a6SDan Streetman {
268a8a572a6SDan Streetman 	xfrm6_net_sysctl_exit(net);
269a8a572a6SDan Streetman 	dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
270a8a572a6SDan Streetman }
2718d068875SMichal Kubecek 
2728d068875SMichal Kubecek static struct pernet_operations xfrm6_net_ops = {
2738d068875SMichal Kubecek 	.init	= xfrm6_net_init,
2748d068875SMichal Kubecek 	.exit	= xfrm6_net_exit,
2758d068875SMichal Kubecek };
276a44a4a00SNeil Horman 
xfrm6_init(void)2770013cabaSDaniel Lezcano int __init xfrm6_init(void)
2781da177e4SLinus Torvalds {
2790013cabaSDaniel Lezcano 	int ret;
2800013cabaSDaniel Lezcano 
281d7c7544cSAlexey Dobriyan 	ret = xfrm6_policy_init();
282a8a572a6SDan Streetman 	if (ret)
283d7c7544cSAlexey Dobriyan 		goto out;
284d7c7544cSAlexey Dobriyan 	ret = xfrm6_state_init();
285d7c7544cSAlexey Dobriyan 	if (ret)
286d7c7544cSAlexey Dobriyan 		goto out_policy;
287d7c7544cSAlexey Dobriyan 
2887e14ea15SSteffen Klassert 	ret = xfrm6_protocol_init();
2897e14ea15SSteffen Klassert 	if (ret)
2907e14ea15SSteffen Klassert 		goto out_state;
2917e14ea15SSteffen Klassert 
29240781bfbSChen Zhongjin 	ret = register_pernet_subsys(&xfrm6_net_ops);
29340781bfbSChen Zhongjin 	if (ret)
29440781bfbSChen Zhongjin 		goto out_protocol;
2950013cabaSDaniel Lezcano out:
2960013cabaSDaniel Lezcano 	return ret;
29740781bfbSChen Zhongjin out_protocol:
29840781bfbSChen Zhongjin 	xfrm6_protocol_fini();
2997e14ea15SSteffen Klassert out_state:
3007e14ea15SSteffen Klassert 	xfrm6_state_fini();
3010013cabaSDaniel Lezcano out_policy:
3020013cabaSDaniel Lezcano 	xfrm6_policy_fini();
3030013cabaSDaniel Lezcano 	goto out;
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
xfrm6_fini(void)3061da177e4SLinus Torvalds void xfrm6_fini(void)
3071da177e4SLinus Torvalds {
3088d068875SMichal Kubecek 	unregister_pernet_subsys(&xfrm6_net_ops);
3097e14ea15SSteffen Klassert 	xfrm6_protocol_fini();
3101da177e4SLinus Torvalds 	xfrm6_policy_fini();
3111da177e4SLinus Torvalds 	xfrm6_state_fini();
3121da177e4SLinus Torvalds }
313