xref: /linux/net/ipv4/xfrm4_policy.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
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 
208f01cb08SDavid S. Miller static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4,
2142a7b32bSDavid Ahern 					    int tos, int oif,
225e6b930fSDavid S. Miller 					    const xfrm_address_t *saddr,
23077fbac4SLorenzo Colitti 					    const xfrm_address_t *daddr,
24077fbac4SLorenzo Colitti 					    u32 mark)
251da177e4SLinus Torvalds {
2666cdb3caSHerbert Xu 	struct rtable *rt;
27a1e59abfSPatrick McHardy 
288f01cb08SDavid S. Miller 	memset(fl4, 0, sizeof(*fl4));
298f01cb08SDavid S. Miller 	fl4->daddr = daddr->a4;
308f01cb08SDavid S. Miller 	fl4->flowi4_tos = tos;
3140867d74SDavid Ahern 	fl4->flowi4_l3mdev = l3mdev_master_ifindex_by_index(net, oif);
32077fbac4SLorenzo Colitti 	fl4->flowi4_mark = mark;
3366cdb3caSHerbert Xu 	if (saddr)
348f01cb08SDavid S. Miller 		fl4->saddr = saddr->a4;
3566cdb3caSHerbert Xu 
368f01cb08SDavid S. Miller 	rt = __ip_route_output_key(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 
4342a7b32bSDavid Ahern static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, int oif,
448f01cb08SDavid S. Miller 					  const xfrm_address_t *saddr,
45077fbac4SLorenzo Colitti 					  const xfrm_address_t *daddr,
46077fbac4SLorenzo Colitti 					  u32 mark)
478f01cb08SDavid S. Miller {
488f01cb08SDavid S. Miller 	struct flowi4 fl4;
498f01cb08SDavid S. Miller 
50077fbac4SLorenzo Colitti 	return __xfrm4_dst_lookup(net, &fl4, tos, oif, saddr, daddr, mark);
518f01cb08SDavid S. Miller }
528f01cb08SDavid S. Miller 
5342a7b32bSDavid Ahern static int xfrm4_get_saddr(struct net *net, int oif,
54077fbac4SLorenzo Colitti 			   xfrm_address_t *saddr, xfrm_address_t *daddr,
55077fbac4SLorenzo Colitti 			   u32 mark)
5666cdb3caSHerbert Xu {
5766cdb3caSHerbert Xu 	struct dst_entry *dst;
588f01cb08SDavid S. Miller 	struct flowi4 fl4;
5966cdb3caSHerbert Xu 
60077fbac4SLorenzo Colitti 	dst = __xfrm4_dst_lookup(net, &fl4, 0, oif, NULL, daddr, mark);
6166cdb3caSHerbert Xu 	if (IS_ERR(dst))
62a1e59abfSPatrick McHardy 		return -EHOSTUNREACH;
6366cdb3caSHerbert Xu 
648f01cb08SDavid S. Miller 	saddr->a4 = fl4.saddr;
6566cdb3caSHerbert Xu 	dst_release(dst);
6666cdb3caSHerbert Xu 	return 0;
67a1e59abfSPatrick McHardy }
68a1e59abfSPatrick McHardy 
6987c1e12bSHerbert Xu static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
700c7b3eefSDavid S. Miller 			  const struct flowi *fl)
7125ee3286SHerbert Xu {
72*05d6d492SEric Dumazet 	struct rtable *rt = dst_rtable(xdst->route);
737e1dc7b6SDavid S. Miller 	const struct flowi4 *fl4 = &fl->u.ip4;
741da177e4SLinus Torvalds 
75b7323396SYan, Zheng 	xdst->u.rt.rt_iif = fl4->flowi4_iif;
761da177e4SLinus Torvalds 
7725ee3286SHerbert Xu 	xdst->u.dst.dev = dev;
78d62607c3SJakub Kicinski 	netdev_hold(dev, &xdst->u.dst.dev_tracker, GFP_ATOMIC);
7943372262SMiika Komu 
801da177e4SLinus Torvalds 	/* Sheit... I remember I did this right. Apparently,
811da177e4SLinus Torvalds 	 * it was magically lost, so this code needs audit */
829917e1e8SDavid S. Miller 	xdst->u.rt.rt_is_input = rt->rt_is_input;
8325ee3286SHerbert Xu 	xdst->u.rt.rt_flags = rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST |
8425ee3286SHerbert Xu 					      RTCF_LOCAL);
8525ee3286SHerbert Xu 	xdst->u.rt.rt_type = rt->rt_type;
8677d5bc7eSDavid Ahern 	xdst->u.rt.rt_uses_gateway = rt->rt_uses_gateway;
871550c171SDavid Ahern 	xdst->u.rt.rt_gw_family = rt->rt_gw_family;
881550c171SDavid Ahern 	if (rt->rt_gw_family == AF_INET)
891550c171SDavid Ahern 		xdst->u.rt.rt_gw4 = rt->rt_gw4;
900f5f7d7bSDavid Ahern 	else if (rt->rt_gw_family == AF_INET6)
910f5f7d7bSDavid Ahern 		xdst->u.rt.rt_gw6 = rt->rt_gw6;
925943634fSDavid S. Miller 	xdst->u.rt.rt_pmtu = rt->rt_pmtu;
93d52e5a7eSSabrina Dubroca 	xdst->u.rt.rt_mtu_locked = rt->rt_mtu_locked;
94510c321bSXin Long 	rt_add_uncached_list(&xdst->u.rt);
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds 	return 0;
971da177e4SLinus Torvalds }
981da177e4SLinus Torvalds 
996700c270SDavid S. Miller static void xfrm4_update_pmtu(struct dst_entry *dst, struct sock *sk,
100bd085ef6SHangbin Liu 			      struct sk_buff *skb, u32 mtu,
101bd085ef6SHangbin Liu 			      bool confirm_neigh)
1021da177e4SLinus Torvalds {
1031da177e4SLinus Torvalds 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
1041da177e4SLinus Torvalds 	struct dst_entry *path = xdst->route;
1051da177e4SLinus Torvalds 
106bd085ef6SHangbin Liu 	path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh);
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds 
1096700c270SDavid S. Miller static void xfrm4_redirect(struct dst_entry *dst, struct sock *sk,
1106700c270SDavid S. Miller 			   struct sk_buff *skb)
11155be7a9cSDavid S. Miller {
11255be7a9cSDavid S. Miller 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
11355be7a9cSDavid S. Miller 	struct dst_entry *path = xdst->route;
11455be7a9cSDavid S. Miller 
1156700c270SDavid S. Miller 	path->ops->redirect(path, sk, skb);
11655be7a9cSDavid S. Miller }
11755be7a9cSDavid S. Miller 
118aabc9761SHerbert Xu static void xfrm4_dst_destroy(struct dst_entry *dst)
119aabc9761SHerbert Xu {
120aabc9761SHerbert Xu 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
121aabc9761SHerbert Xu 
12262fa8a84SDavid S. Miller 	dst_destroy_metrics_generic(dst);
123510c321bSXin Long 	rt_del_uncached_list(&xdst->u.rt);
124aabc9761SHerbert Xu 	xfrm_dst_destroy(xdst);
125aabc9761SHerbert Xu }
126aabc9761SHerbert Xu 
127a8a572a6SDan Streetman static struct dst_ops xfrm4_dst_ops_template = {
1281da177e4SLinus Torvalds 	.family =		AF_INET,
1291da177e4SLinus Torvalds 	.update_pmtu =		xfrm4_update_pmtu,
13055be7a9cSDavid S. Miller 	.redirect =		xfrm4_redirect,
13162fa8a84SDavid S. Miller 	.cow_metrics =		dst_cow_metrics_generic,
132aabc9761SHerbert Xu 	.destroy =		xfrm4_dst_destroy,
13343c28172SZhengchao Shao 	.ifdown =		xfrm_dst_ifdown,
134862b82c6SHerbert Xu 	.local_out =		__ip_local_out,
1353c2a89ddSFlorian Westphal 	.gc_thresh =		32768,
1361da177e4SLinus Torvalds };
1371da177e4SLinus Torvalds 
13837b10383SFlorian Westphal static const struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
139a8a572a6SDan Streetman 	.dst_ops =		&xfrm4_dst_ops_template,
1401da177e4SLinus Torvalds 	.dst_lookup =		xfrm4_dst_lookup,
141a1e59abfSPatrick McHardy 	.get_saddr =		xfrm4_get_saddr,
14225ee3286SHerbert Xu 	.fill_dst =		xfrm4_fill_dst,
1432774c131SDavid S. Miller 	.blackhole_route =	ipv4_blackhole_route,
1441da177e4SLinus Torvalds };
1451da177e4SLinus Torvalds 
146f816700aSRandy Dunlap #ifdef CONFIG_SYSCTL
147a44a4a00SNeil Horman static struct ctl_table xfrm4_policy_table[] = {
148a44a4a00SNeil Horman 	{
149a44a4a00SNeil Horman 		.procname       = "xfrm4_gc_thresh",
150d7c7544cSAlexey Dobriyan 		.data           = &init_net.xfrm.xfrm4_dst_ops.gc_thresh,
151a44a4a00SNeil Horman 		.maxlen         = sizeof(int),
152a44a4a00SNeil Horman 		.mode           = 0644,
153a44a4a00SNeil Horman 		.proc_handler   = proc_dointvec,
154a44a4a00SNeil Horman 	},
155a44a4a00SNeil Horman };
156a44a4a00SNeil Horman 
157318d3cc0SArnd Bergmann static __net_init int xfrm4_net_sysctl_init(struct net *net)
1588d068875SMichal Kubecek {
1598d068875SMichal Kubecek 	struct ctl_table *table;
1608d068875SMichal Kubecek 	struct ctl_table_header *hdr;
1618d068875SMichal Kubecek 
1628d068875SMichal Kubecek 	table = xfrm4_policy_table;
1638d068875SMichal Kubecek 	if (!net_eq(net, &init_net)) {
1648d068875SMichal Kubecek 		table = kmemdup(table, sizeof(xfrm4_policy_table), GFP_KERNEL);
1658d068875SMichal Kubecek 		if (!table)
1668d068875SMichal Kubecek 			goto err_alloc;
1678d068875SMichal Kubecek 
1688d068875SMichal Kubecek 		table[0].data = &net->xfrm.xfrm4_dst_ops.gc_thresh;
1698d068875SMichal Kubecek 	}
1708d068875SMichal Kubecek 
171c899710fSJoel Granados 	hdr = register_net_sysctl_sz(net, "net/ipv4", table,
172c899710fSJoel Granados 				     ARRAY_SIZE(xfrm4_policy_table));
1738d068875SMichal Kubecek 	if (!hdr)
1748d068875SMichal Kubecek 		goto err_reg;
1758d068875SMichal Kubecek 
1768d068875SMichal Kubecek 	net->ipv4.xfrm4_hdr = hdr;
1778d068875SMichal Kubecek 	return 0;
1788d068875SMichal Kubecek 
1798d068875SMichal Kubecek err_reg:
1808d068875SMichal Kubecek 	if (!net_eq(net, &init_net))
1818d068875SMichal Kubecek 		kfree(table);
1828d068875SMichal Kubecek err_alloc:
1838d068875SMichal Kubecek 	return -ENOMEM;
1848d068875SMichal Kubecek }
1858d068875SMichal Kubecek 
186318d3cc0SArnd Bergmann static __net_exit void xfrm4_net_sysctl_exit(struct net *net)
1878d068875SMichal Kubecek {
188bfa858f2SThomas Weißschuh 	const struct ctl_table *table;
1898d068875SMichal Kubecek 
19051456b29SIan Morris 	if (!net->ipv4.xfrm4_hdr)
1918d068875SMichal Kubecek 		return;
1928d068875SMichal Kubecek 
1938d068875SMichal Kubecek 	table = net->ipv4.xfrm4_hdr->ctl_table_arg;
1948d068875SMichal Kubecek 	unregister_net_sysctl_table(net->ipv4.xfrm4_hdr);
1958d068875SMichal Kubecek 	if (!net_eq(net, &init_net))
1968d068875SMichal Kubecek 		kfree(table);
1978d068875SMichal Kubecek }
198a8a572a6SDan Streetman #else /* CONFIG_SYSCTL */
199318d3cc0SArnd Bergmann static inline int xfrm4_net_sysctl_init(struct net *net)
200a8a572a6SDan Streetman {
201a8a572a6SDan Streetman 	return 0;
202a8a572a6SDan Streetman }
203a8a572a6SDan Streetman 
204318d3cc0SArnd Bergmann static inline void xfrm4_net_sysctl_exit(struct net *net)
205a8a572a6SDan Streetman {
206a8a572a6SDan Streetman }
207a8a572a6SDan Streetman #endif
208a8a572a6SDan Streetman 
209a8a572a6SDan Streetman static int __net_init xfrm4_net_init(struct net *net)
210a8a572a6SDan Streetman {
211a8a572a6SDan Streetman 	int ret;
212a8a572a6SDan Streetman 
213a8a572a6SDan Streetman 	memcpy(&net->xfrm.xfrm4_dst_ops, &xfrm4_dst_ops_template,
214a8a572a6SDan Streetman 	       sizeof(xfrm4_dst_ops_template));
215a8a572a6SDan Streetman 	ret = dst_entries_init(&net->xfrm.xfrm4_dst_ops);
216a8a572a6SDan Streetman 	if (ret)
217a8a572a6SDan Streetman 		return ret;
218a8a572a6SDan Streetman 
219a8a572a6SDan Streetman 	ret = xfrm4_net_sysctl_init(net);
220a8a572a6SDan Streetman 	if (ret)
221a8a572a6SDan Streetman 		dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
222a8a572a6SDan Streetman 
223a8a572a6SDan Streetman 	return ret;
224a8a572a6SDan Streetman }
225a8a572a6SDan Streetman 
226a8a572a6SDan Streetman static void __net_exit xfrm4_net_exit(struct net *net)
227a8a572a6SDan Streetman {
228a8a572a6SDan Streetman 	xfrm4_net_sysctl_exit(net);
229a8a572a6SDan Streetman 	dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
230a8a572a6SDan Streetman }
2318d068875SMichal Kubecek 
2328d068875SMichal Kubecek static struct pernet_operations __net_initdata xfrm4_net_ops = {
2338d068875SMichal Kubecek 	.init	= xfrm4_net_init,
2348d068875SMichal Kubecek 	.exit	= xfrm4_net_exit,
2358d068875SMichal Kubecek };
236a44a4a00SNeil Horman 
2371da177e4SLinus Torvalds static void __init xfrm4_policy_init(void)
2381da177e4SLinus Torvalds {
239a2817d8bSFlorian Westphal 	xfrm_policy_register_afinfo(&xfrm4_policy_afinfo, AF_INET);
2401da177e4SLinus Torvalds }
2411da177e4SLinus Torvalds 
242703fb94eSSteffen Klassert void __init xfrm4_init(void)
2431da177e4SLinus Torvalds {
244d7c7544cSAlexey Dobriyan 	xfrm4_state_init();
245d7c7544cSAlexey Dobriyan 	xfrm4_policy_init();
2462f32b51bSSteffen Klassert 	xfrm4_protocol_init();
2478d068875SMichal Kubecek 	register_pernet_subsys(&xfrm4_net_ops);
2481da177e4SLinus Torvalds }
249