xref: /openbmc/linux/net/ipv4/xfrm4_policy.c (revision e50e86dbcabda570fc8a1435fe2fca97e9ab7312)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * xfrm4_policy.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Changes:
61da177e4SLinus Torvalds  *	Kazunori MIYAZAWA @USAGI
71da177e4SLinus Torvalds  * 	YOSHIFUJI Hideaki @USAGI
81da177e4SLinus Torvalds  *		Split up af-specific portion
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
1266cdb3caSHerbert Xu #include <linux/err.h>
1366cdb3caSHerbert Xu #include <linux/kernel.h>
14aabc9761SHerbert Xu #include <linux/inetdevice.h>
1545ff5a3fSHerbert Xu #include <net/dst.h>
161da177e4SLinus Torvalds #include <net/xfrm.h>
171da177e4SLinus Torvalds #include <net/ip.h>
18385add90SDavid Ahern #include <net/l3mdev.h>
191da177e4SLinus Torvalds 
__xfrm4_dst_lookup(struct flowi4 * fl4,const struct xfrm_dst_lookup_params * params)20ac1d820eSEyal Birger static struct dst_entry *__xfrm4_dst_lookup(struct flowi4 *fl4,
21ac1d820eSEyal Birger 					    const struct xfrm_dst_lookup_params *params)
221da177e4SLinus Torvalds {
2366cdb3caSHerbert Xu 	struct rtable *rt;
24a1e59abfSPatrick McHardy 
258f01cb08SDavid S. Miller 	memset(fl4, 0, sizeof(*fl4));
26ac1d820eSEyal Birger 	fl4->daddr = params->daddr->a4;
27ac1d820eSEyal Birger 	fl4->flowi4_tos = params->tos;
28ac1d820eSEyal Birger 	fl4->flowi4_l3mdev = l3mdev_master_ifindex_by_index(params->net,
29ac1d820eSEyal Birger 							    params->oif);
30ac1d820eSEyal Birger 	fl4->flowi4_mark = params->mark;
31ac1d820eSEyal Birger 	if (params->saddr)
32ac1d820eSEyal Birger 		fl4->saddr = params->saddr->a4;
33*c1530660SEyal Birger 	fl4->flowi4_proto = params->ipproto;
34*c1530660SEyal Birger 	fl4->uli = params->uli;
3566cdb3caSHerbert Xu 
36ac1d820eSEyal Birger 	rt = __ip_route_output_key(params->net, fl4);
37b23dd4feSDavid S. Miller 	if (!IS_ERR(rt))
38b23dd4feSDavid S. Miller 		return &rt->dst;
39b23dd4feSDavid S. Miller 
40b23dd4feSDavid S. Miller 	return ERR_CAST(rt);
41a1e59abfSPatrick McHardy }
4266cdb3caSHerbert Xu 
xfrm4_dst_lookup(const struct xfrm_dst_lookup_params * params)43ac1d820eSEyal Birger static struct dst_entry *xfrm4_dst_lookup(const struct xfrm_dst_lookup_params *params)
448f01cb08SDavid S. Miller {
458f01cb08SDavid S. Miller 	struct flowi4 fl4;
468f01cb08SDavid S. Miller 
47ac1d820eSEyal Birger 	return __xfrm4_dst_lookup(&fl4, params);
488f01cb08SDavid S. Miller }
498f01cb08SDavid S. Miller 
xfrm4_get_saddr(xfrm_address_t * saddr,const struct xfrm_dst_lookup_params * params)50ac1d820eSEyal Birger static int xfrm4_get_saddr(xfrm_address_t *saddr,
51ac1d820eSEyal Birger 			   const struct xfrm_dst_lookup_params *params)
5266cdb3caSHerbert Xu {
5366cdb3caSHerbert Xu 	struct dst_entry *dst;
548f01cb08SDavid S. Miller 	struct flowi4 fl4;
5566cdb3caSHerbert Xu 
56ac1d820eSEyal Birger 	dst = __xfrm4_dst_lookup(&fl4, params);
5766cdb3caSHerbert Xu 	if (IS_ERR(dst))
58a1e59abfSPatrick McHardy 		return -EHOSTUNREACH;
5966cdb3caSHerbert Xu 
608f01cb08SDavid S. Miller 	saddr->a4 = fl4.saddr;
6166cdb3caSHerbert Xu 	dst_release(dst);
6266cdb3caSHerbert Xu 	return 0;
63a1e59abfSPatrick McHardy }
64a1e59abfSPatrick McHardy 
xfrm4_fill_dst(struct xfrm_dst * xdst,struct net_device * dev,const struct flowi * fl)6587c1e12bSHerbert Xu static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
660c7b3eefSDavid S. Miller 			  const struct flowi *fl)
6725ee3286SHerbert Xu {
6825ee3286SHerbert Xu 	struct rtable *rt = (struct rtable *)xdst->route;
697e1dc7b6SDavid S. Miller 	const struct flowi4 *fl4 = &fl->u.ip4;
701da177e4SLinus Torvalds 
71b7323396SYan, Zheng 	xdst->u.rt.rt_iif = fl4->flowi4_iif;
721da177e4SLinus Torvalds 
7325ee3286SHerbert Xu 	xdst->u.dst.dev = dev;
74d62607c3SJakub Kicinski 	netdev_hold(dev, &xdst->u.dst.dev_tracker, GFP_ATOMIC);
7543372262SMiika Komu 
761da177e4SLinus Torvalds 	/* Sheit... I remember I did this right. Apparently,
771da177e4SLinus Torvalds 	 * it was magically lost, so this code needs audit */
789917e1e8SDavid S. Miller 	xdst->u.rt.rt_is_input = rt->rt_is_input;
7925ee3286SHerbert Xu 	xdst->u.rt.rt_flags = rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST |
8025ee3286SHerbert Xu 					      RTCF_LOCAL);
8125ee3286SHerbert Xu 	xdst->u.rt.rt_type = rt->rt_type;
8277d5bc7eSDavid Ahern 	xdst->u.rt.rt_uses_gateway = rt->rt_uses_gateway;
831550c171SDavid Ahern 	xdst->u.rt.rt_gw_family = rt->rt_gw_family;
841550c171SDavid Ahern 	if (rt->rt_gw_family == AF_INET)
851550c171SDavid Ahern 		xdst->u.rt.rt_gw4 = rt->rt_gw4;
860f5f7d7bSDavid Ahern 	else if (rt->rt_gw_family == AF_INET6)
870f5f7d7bSDavid Ahern 		xdst->u.rt.rt_gw6 = rt->rt_gw6;
885943634fSDavid S. Miller 	xdst->u.rt.rt_pmtu = rt->rt_pmtu;
89d52e5a7eSSabrina Dubroca 	xdst->u.rt.rt_mtu_locked = rt->rt_mtu_locked;
90510c321bSXin Long 	rt_add_uncached_list(&xdst->u.rt);
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds 	return 0;
931da177e4SLinus Torvalds }
941da177e4SLinus Torvalds 
xfrm4_update_pmtu(struct dst_entry * dst,struct sock * sk,struct sk_buff * skb,u32 mtu,bool confirm_neigh)956700c270SDavid S. Miller static void xfrm4_update_pmtu(struct dst_entry *dst, struct sock *sk,
96bd085ef6SHangbin Liu 			      struct sk_buff *skb, u32 mtu,
97bd085ef6SHangbin Liu 			      bool confirm_neigh)
981da177e4SLinus Torvalds {
991da177e4SLinus Torvalds 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
1001da177e4SLinus Torvalds 	struct dst_entry *path = xdst->route;
1011da177e4SLinus Torvalds 
102bd085ef6SHangbin Liu 	path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh);
1031da177e4SLinus Torvalds }
1041da177e4SLinus Torvalds 
xfrm4_redirect(struct dst_entry * dst,struct sock * sk,struct sk_buff * skb)1056700c270SDavid S. Miller static void xfrm4_redirect(struct dst_entry *dst, struct sock *sk,
1066700c270SDavid S. Miller 			   struct sk_buff *skb)
10755be7a9cSDavid S. Miller {
10855be7a9cSDavid S. Miller 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
10955be7a9cSDavid S. Miller 	struct dst_entry *path = xdst->route;
11055be7a9cSDavid S. Miller 
1116700c270SDavid S. Miller 	path->ops->redirect(path, sk, skb);
11255be7a9cSDavid S. Miller }
11355be7a9cSDavid S. Miller 
xfrm4_dst_destroy(struct dst_entry * dst)114aabc9761SHerbert Xu static void xfrm4_dst_destroy(struct dst_entry *dst)
115aabc9761SHerbert Xu {
116aabc9761SHerbert Xu 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
117aabc9761SHerbert Xu 
11862fa8a84SDavid S. Miller 	dst_destroy_metrics_generic(dst);
119510c321bSXin Long 	rt_del_uncached_list(&xdst->u.rt);
120aabc9761SHerbert Xu 	xfrm_dst_destroy(xdst);
121aabc9761SHerbert Xu }
122aabc9761SHerbert Xu 
123a8a572a6SDan Streetman static struct dst_ops xfrm4_dst_ops_template = {
1241da177e4SLinus Torvalds 	.family =		AF_INET,
1251da177e4SLinus Torvalds 	.update_pmtu =		xfrm4_update_pmtu,
12655be7a9cSDavid S. Miller 	.redirect =		xfrm4_redirect,
12762fa8a84SDavid S. Miller 	.cow_metrics =		dst_cow_metrics_generic,
128aabc9761SHerbert Xu 	.destroy =		xfrm4_dst_destroy,
12943c28172SZhengchao Shao 	.ifdown =		xfrm_dst_ifdown,
130862b82c6SHerbert Xu 	.local_out =		__ip_local_out,
1313c2a89ddSFlorian Westphal 	.gc_thresh =		32768,
1321da177e4SLinus Torvalds };
1331da177e4SLinus Torvalds 
13437b10383SFlorian Westphal static const struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
135a8a572a6SDan Streetman 	.dst_ops =		&xfrm4_dst_ops_template,
1361da177e4SLinus Torvalds 	.dst_lookup =		xfrm4_dst_lookup,
137a1e59abfSPatrick McHardy 	.get_saddr =		xfrm4_get_saddr,
13825ee3286SHerbert Xu 	.fill_dst =		xfrm4_fill_dst,
1392774c131SDavid S. Miller 	.blackhole_route =	ipv4_blackhole_route,
1401da177e4SLinus Torvalds };
1411da177e4SLinus Torvalds 
142f816700aSRandy Dunlap #ifdef CONFIG_SYSCTL
143a44a4a00SNeil Horman static struct ctl_table xfrm4_policy_table[] = {
144a44a4a00SNeil Horman 	{
145a44a4a00SNeil Horman 		.procname       = "xfrm4_gc_thresh",
146d7c7544cSAlexey Dobriyan 		.data           = &init_net.xfrm.xfrm4_dst_ops.gc_thresh,
147a44a4a00SNeil Horman 		.maxlen         = sizeof(int),
148a44a4a00SNeil Horman 		.mode           = 0644,
149a44a4a00SNeil Horman 		.proc_handler   = proc_dointvec,
150a44a4a00SNeil Horman 	},
151a44a4a00SNeil Horman 	{ }
152a44a4a00SNeil Horman };
153a44a4a00SNeil Horman 
xfrm4_net_sysctl_init(struct net * net)154318d3cc0SArnd Bergmann static __net_init int xfrm4_net_sysctl_init(struct net *net)
1558d068875SMichal Kubecek {
1568d068875SMichal Kubecek 	struct ctl_table *table;
1578d068875SMichal Kubecek 	struct ctl_table_header *hdr;
1588d068875SMichal Kubecek 
1598d068875SMichal Kubecek 	table = xfrm4_policy_table;
1608d068875SMichal Kubecek 	if (!net_eq(net, &init_net)) {
1618d068875SMichal Kubecek 		table = kmemdup(table, sizeof(xfrm4_policy_table), GFP_KERNEL);
1628d068875SMichal Kubecek 		if (!table)
1638d068875SMichal Kubecek 			goto err_alloc;
1648d068875SMichal Kubecek 
1658d068875SMichal Kubecek 		table[0].data = &net->xfrm.xfrm4_dst_ops.gc_thresh;
1668d068875SMichal Kubecek 	}
1678d068875SMichal Kubecek 
168c899710fSJoel Granados 	hdr = register_net_sysctl_sz(net, "net/ipv4", table,
169c899710fSJoel Granados 				     ARRAY_SIZE(xfrm4_policy_table));
1708d068875SMichal Kubecek 	if (!hdr)
1718d068875SMichal Kubecek 		goto err_reg;
1728d068875SMichal Kubecek 
1738d068875SMichal Kubecek 	net->ipv4.xfrm4_hdr = hdr;
1748d068875SMichal Kubecek 	return 0;
1758d068875SMichal Kubecek 
1768d068875SMichal Kubecek err_reg:
1778d068875SMichal Kubecek 	if (!net_eq(net, &init_net))
1788d068875SMichal Kubecek 		kfree(table);
1798d068875SMichal Kubecek err_alloc:
1808d068875SMichal Kubecek 	return -ENOMEM;
1818d068875SMichal Kubecek }
1828d068875SMichal Kubecek 
xfrm4_net_sysctl_exit(struct net * net)183318d3cc0SArnd Bergmann static __net_exit void xfrm4_net_sysctl_exit(struct net *net)
1848d068875SMichal Kubecek {
1858d068875SMichal Kubecek 	struct ctl_table *table;
1868d068875SMichal Kubecek 
18751456b29SIan Morris 	if (!net->ipv4.xfrm4_hdr)
1888d068875SMichal Kubecek 		return;
1898d068875SMichal Kubecek 
1908d068875SMichal Kubecek 	table = net->ipv4.xfrm4_hdr->ctl_table_arg;
1918d068875SMichal Kubecek 	unregister_net_sysctl_table(net->ipv4.xfrm4_hdr);
1928d068875SMichal Kubecek 	if (!net_eq(net, &init_net))
1938d068875SMichal Kubecek 		kfree(table);
1948d068875SMichal Kubecek }
195a8a572a6SDan Streetman #else /* CONFIG_SYSCTL */
xfrm4_net_sysctl_init(struct net * net)196318d3cc0SArnd Bergmann static inline int xfrm4_net_sysctl_init(struct net *net)
197a8a572a6SDan Streetman {
198a8a572a6SDan Streetman 	return 0;
199a8a572a6SDan Streetman }
200a8a572a6SDan Streetman 
xfrm4_net_sysctl_exit(struct net * net)201318d3cc0SArnd Bergmann static inline void xfrm4_net_sysctl_exit(struct net *net)
202a8a572a6SDan Streetman {
203a8a572a6SDan Streetman }
204a8a572a6SDan Streetman #endif
205a8a572a6SDan Streetman 
xfrm4_net_init(struct net * net)206a8a572a6SDan Streetman static int __net_init xfrm4_net_init(struct net *net)
207a8a572a6SDan Streetman {
208a8a572a6SDan Streetman 	int ret;
209a8a572a6SDan Streetman 
210a8a572a6SDan Streetman 	memcpy(&net->xfrm.xfrm4_dst_ops, &xfrm4_dst_ops_template,
211a8a572a6SDan Streetman 	       sizeof(xfrm4_dst_ops_template));
212a8a572a6SDan Streetman 	ret = dst_entries_init(&net->xfrm.xfrm4_dst_ops);
213a8a572a6SDan Streetman 	if (ret)
214a8a572a6SDan Streetman 		return ret;
215a8a572a6SDan Streetman 
216a8a572a6SDan Streetman 	ret = xfrm4_net_sysctl_init(net);
217a8a572a6SDan Streetman 	if (ret)
218a8a572a6SDan Streetman 		dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
219a8a572a6SDan Streetman 
220a8a572a6SDan Streetman 	return ret;
221a8a572a6SDan Streetman }
222a8a572a6SDan Streetman 
xfrm4_net_exit(struct net * net)223a8a572a6SDan Streetman static void __net_exit xfrm4_net_exit(struct net *net)
224a8a572a6SDan Streetman {
225a8a572a6SDan Streetman 	xfrm4_net_sysctl_exit(net);
226a8a572a6SDan Streetman 	dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
227a8a572a6SDan Streetman }
2288d068875SMichal Kubecek 
2298d068875SMichal Kubecek static struct pernet_operations __net_initdata xfrm4_net_ops = {
2308d068875SMichal Kubecek 	.init	= xfrm4_net_init,
2318d068875SMichal Kubecek 	.exit	= xfrm4_net_exit,
2328d068875SMichal Kubecek };
233a44a4a00SNeil Horman 
xfrm4_policy_init(void)2341da177e4SLinus Torvalds static void __init xfrm4_policy_init(void)
2351da177e4SLinus Torvalds {
236a2817d8bSFlorian Westphal 	xfrm_policy_register_afinfo(&xfrm4_policy_afinfo, AF_INET);
2371da177e4SLinus Torvalds }
2381da177e4SLinus Torvalds 
xfrm4_init(void)239703fb94eSSteffen Klassert void __init xfrm4_init(void)
2401da177e4SLinus Torvalds {
241d7c7544cSAlexey Dobriyan 	xfrm4_state_init();
242d7c7544cSAlexey Dobriyan 	xfrm4_policy_init();
2432f32b51bSSteffen Klassert 	xfrm4_protocol_init();
2448d068875SMichal Kubecek 	register_pernet_subsys(&xfrm4_net_ops);
2451da177e4SLinus Torvalds }
246