xref: /linux/net/ipv4/xfrm4_policy.c (revision 8c689a6eae2d83970e4f34753d513e96fb97a025)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * xfrm4_policy.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Changes:
51da177e4SLinus Torvalds  *	Kazunori MIYAZAWA @USAGI
61da177e4SLinus Torvalds  * 	YOSHIFUJI Hideaki @USAGI
71da177e4SLinus Torvalds  *		Split up af-specific portion
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds 
11aabc9761SHerbert Xu #include <linux/compiler.h>
12aabc9761SHerbert Xu #include <linux/inetdevice.h>
131da177e4SLinus Torvalds #include <net/xfrm.h>
141da177e4SLinus Torvalds #include <net/ip.h>
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds static struct dst_ops xfrm4_dst_ops;
171da177e4SLinus Torvalds static struct xfrm_policy_afinfo xfrm4_policy_afinfo;
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds static int xfrm4_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
201da177e4SLinus Torvalds {
211da177e4SLinus Torvalds 	return __ip_route_output_key((struct rtable**)dst, fl);
221da177e4SLinus Torvalds }
231da177e4SLinus Torvalds 
24a1e59abfSPatrick McHardy static int xfrm4_get_saddr(xfrm_address_t *saddr, xfrm_address_t *daddr)
25a1e59abfSPatrick McHardy {
26a1e59abfSPatrick McHardy 	struct rtable *rt;
27a1e59abfSPatrick McHardy 	struct flowi fl_tunnel = {
28a1e59abfSPatrick McHardy 		.nl_u = {
29a1e59abfSPatrick McHardy 			.ip4_u = {
30a1e59abfSPatrick McHardy 				.daddr = daddr->a4,
31a1e59abfSPatrick McHardy 			},
32a1e59abfSPatrick McHardy 		},
33a1e59abfSPatrick McHardy 	};
34a1e59abfSPatrick McHardy 
35a1e59abfSPatrick McHardy 	if (!xfrm4_dst_lookup((struct xfrm_dst **)&rt, &fl_tunnel)) {
36a1e59abfSPatrick McHardy 		saddr->a4 = rt->rt_src;
37a1e59abfSPatrick McHardy 		dst_release(&rt->u.dst);
38a1e59abfSPatrick McHardy 		return 0;
39a1e59abfSPatrick McHardy 	}
40a1e59abfSPatrick McHardy 	return -EHOSTUNREACH;
41a1e59abfSPatrick McHardy }
42a1e59abfSPatrick McHardy 
431da177e4SLinus Torvalds static struct dst_entry *
441da177e4SLinus Torvalds __xfrm4_find_bundle(struct flowi *fl, struct xfrm_policy *policy)
451da177e4SLinus Torvalds {
461da177e4SLinus Torvalds 	struct dst_entry *dst;
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds 	read_lock_bh(&policy->lock);
491da177e4SLinus Torvalds 	for (dst = policy->bundles; dst; dst = dst->next) {
501da177e4SLinus Torvalds 		struct xfrm_dst *xdst = (struct xfrm_dst*)dst;
511da177e4SLinus Torvalds 		if (xdst->u.rt.fl.oif == fl->oif &&	/*XXX*/
521da177e4SLinus Torvalds 		    xdst->u.rt.fl.fl4_dst == fl->fl4_dst &&
531da177e4SLinus Torvalds 	    	    xdst->u.rt.fl.fl4_src == fl->fl4_src &&
544da3089fSHerbert Xu 	    	    xdst->u.rt.fl.fl4_tos == fl->fl4_tos &&
555b368e61SVenkat Yekkirala 		    xfrm_bundle_ok(policy, xdst, fl, AF_INET, 0)) {
561da177e4SLinus Torvalds 			dst_clone(dst);
571da177e4SLinus Torvalds 			break;
581da177e4SLinus Torvalds 		}
591da177e4SLinus Torvalds 	}
601da177e4SLinus Torvalds 	read_unlock_bh(&policy->lock);
611da177e4SLinus Torvalds 	return dst;
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds /* Allocate chain of dst_entry's, attach known xfrm's, calculate
651da177e4SLinus Torvalds  * all the metrics... Shortly, bundle a bundle.
661da177e4SLinus Torvalds  */
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds static int
691da177e4SLinus Torvalds __xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
701da177e4SLinus Torvalds 		      struct flowi *fl, struct dst_entry **dst_p)
711da177e4SLinus Torvalds {
721da177e4SLinus Torvalds 	struct dst_entry *dst, *dst_prev;
731da177e4SLinus Torvalds 	struct rtable *rt0 = (struct rtable*)(*dst_p);
741da177e4SLinus Torvalds 	struct rtable *rt = rt0;
75*8c689a6eSAl Viro 	__be32 remote = fl->fl4_dst;
76*8c689a6eSAl Viro 	__be32 local  = fl->fl4_src;
771da177e4SLinus Torvalds 	struct flowi fl_tunnel = {
781da177e4SLinus Torvalds 		.nl_u = {
791da177e4SLinus Torvalds 			.ip4_u = {
801da177e4SLinus Torvalds 				.saddr = local,
814da3089fSHerbert Xu 				.daddr = remote,
824da3089fSHerbert Xu 				.tos = fl->fl4_tos
831da177e4SLinus Torvalds 			}
841da177e4SLinus Torvalds 		}
851da177e4SLinus Torvalds 	};
861da177e4SLinus Torvalds 	int i;
871da177e4SLinus Torvalds 	int err;
881da177e4SLinus Torvalds 	int header_len = 0;
891da177e4SLinus Torvalds 	int trailer_len = 0;
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds 	dst = dst_prev = NULL;
921da177e4SLinus Torvalds 	dst_hold(&rt->u.dst);
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds 	for (i = 0; i < nx; i++) {
951da177e4SLinus Torvalds 		struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops);
961da177e4SLinus Torvalds 		struct xfrm_dst *xdst;
971da177e4SLinus Torvalds 		int tunnel = 0;
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds 		if (unlikely(dst1 == NULL)) {
1001da177e4SLinus Torvalds 			err = -ENOBUFS;
1011da177e4SLinus Torvalds 			dst_release(&rt->u.dst);
1021da177e4SLinus Torvalds 			goto error;
1031da177e4SLinus Torvalds 		}
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 		if (!dst)
1061da177e4SLinus Torvalds 			dst = dst1;
1071da177e4SLinus Torvalds 		else {
1081da177e4SLinus Torvalds 			dst_prev->child = dst1;
1091da177e4SLinus Torvalds 			dst1->flags |= DST_NOHASH;
1101da177e4SLinus Torvalds 			dst_clone(dst1);
1111da177e4SLinus Torvalds 		}
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds 		xdst = (struct xfrm_dst *)dst1;
1141da177e4SLinus Torvalds 		xdst->route = &rt->u.dst;
1159d4a706dSDavid S. Miller 		xdst->genid = xfrm[i]->genid;
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 		dst1->next = dst_prev;
1181da177e4SLinus Torvalds 		dst_prev = dst1;
1197e49e6deSMasahide NAKAMURA 		if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
1201da177e4SLinus Torvalds 			remote = xfrm[i]->id.daddr.a4;
1211da177e4SLinus Torvalds 			local  = xfrm[i]->props.saddr.a4;
1221da177e4SLinus Torvalds 			tunnel = 1;
1231da177e4SLinus Torvalds 		}
1241da177e4SLinus Torvalds 		header_len += xfrm[i]->props.header_len;
1251da177e4SLinus Torvalds 		trailer_len += xfrm[i]->props.trailer_len;
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds 		if (tunnel) {
1281da177e4SLinus Torvalds 			fl_tunnel.fl4_src = local;
1291da177e4SLinus Torvalds 			fl_tunnel.fl4_dst = remote;
1301da177e4SLinus Torvalds 			err = xfrm_dst_lookup((struct xfrm_dst **)&rt,
1311da177e4SLinus Torvalds 					      &fl_tunnel, AF_INET);
1321da177e4SLinus Torvalds 			if (err)
1331da177e4SLinus Torvalds 				goto error;
1341da177e4SLinus Torvalds 		} else
1351da177e4SLinus Torvalds 			dst_hold(&rt->u.dst);
1361da177e4SLinus Torvalds 	}
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds 	dst_prev->child = &rt->u.dst;
1391da177e4SLinus Torvalds 	dst->path = &rt->u.dst;
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds 	*dst_p = dst;
1421da177e4SLinus Torvalds 	dst = dst_prev;
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds 	dst_prev = *dst_p;
1451da177e4SLinus Torvalds 	i = 0;
1461da177e4SLinus Torvalds 	for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
1471da177e4SLinus Torvalds 		struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;
1481da177e4SLinus Torvalds 		x->u.rt.fl = *fl;
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds 		dst_prev->xfrm = xfrm[i++];
1511da177e4SLinus Torvalds 		dst_prev->dev = rt->u.dst.dev;
1521da177e4SLinus Torvalds 		if (rt->u.dst.dev)
1531da177e4SLinus Torvalds 			dev_hold(rt->u.dst.dev);
1541da177e4SLinus Torvalds 		dst_prev->obsolete	= -1;
1551da177e4SLinus Torvalds 		dst_prev->flags	       |= DST_HOST;
1561da177e4SLinus Torvalds 		dst_prev->lastuse	= jiffies;
1571da177e4SLinus Torvalds 		dst_prev->header_len	= header_len;
1581b5c2299SMasahide NAKAMURA 		dst_prev->nfheader_len	= 0;
1591da177e4SLinus Torvalds 		dst_prev->trailer_len	= trailer_len;
1601da177e4SLinus Torvalds 		memcpy(&dst_prev->metrics, &x->route->metrics, sizeof(dst_prev->metrics));
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 		/* Copy neighbout for reachability confirmation */
1631da177e4SLinus Torvalds 		dst_prev->neighbour	= neigh_clone(rt->u.dst.neighbour);
1641da177e4SLinus Torvalds 		dst_prev->input		= rt->u.dst.input;
1651da177e4SLinus Torvalds 		dst_prev->output	= xfrm4_output;
1661da177e4SLinus Torvalds 		if (rt->peer)
1671da177e4SLinus Torvalds 			atomic_inc(&rt->peer->refcnt);
1681da177e4SLinus Torvalds 		x->u.rt.peer = rt->peer;
1691da177e4SLinus Torvalds 		/* Sheit... I remember I did this right. Apparently,
1701da177e4SLinus Torvalds 		 * it was magically lost, so this code needs audit */
1711da177e4SLinus Torvalds 		x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
1721da177e4SLinus Torvalds 		x->u.rt.rt_type = rt->rt_type;
1731da177e4SLinus Torvalds 		x->u.rt.rt_src = rt0->rt_src;
1741da177e4SLinus Torvalds 		x->u.rt.rt_dst = rt0->rt_dst;
1751da177e4SLinus Torvalds 		x->u.rt.rt_gateway = rt->rt_gateway;
1761da177e4SLinus Torvalds 		x->u.rt.rt_spec_dst = rt0->rt_spec_dst;
177aabc9761SHerbert Xu 		x->u.rt.idev = rt0->idev;
178aabc9761SHerbert Xu 		in_dev_hold(rt0->idev);
1791da177e4SLinus Torvalds 		header_len -= x->u.dst.xfrm->props.header_len;
1801da177e4SLinus Torvalds 		trailer_len -= x->u.dst.xfrm->props.trailer_len;
1811da177e4SLinus Torvalds 	}
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds 	xfrm_init_pmtu(dst);
1841da177e4SLinus Torvalds 	return 0;
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds error:
1871da177e4SLinus Torvalds 	if (dst)
1881da177e4SLinus Torvalds 		dst_free(dst);
1891da177e4SLinus Torvalds 	return err;
1901da177e4SLinus Torvalds }
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds static void
1931da177e4SLinus Torvalds _decode_session4(struct sk_buff *skb, struct flowi *fl)
1941da177e4SLinus Torvalds {
1951da177e4SLinus Torvalds 	struct iphdr *iph = skb->nh.iph;
1961da177e4SLinus Torvalds 	u8 *xprth = skb->nh.raw + iph->ihl*4;
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds 	memset(fl, 0, sizeof(struct flowi));
1991da177e4SLinus Torvalds 	if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) {
2001da177e4SLinus Torvalds 		switch (iph->protocol) {
2011da177e4SLinus Torvalds 		case IPPROTO_UDP:
2021da177e4SLinus Torvalds 		case IPPROTO_TCP:
2031da177e4SLinus Torvalds 		case IPPROTO_SCTP:
2049e999993SPatrick McHardy 		case IPPROTO_DCCP:
2051da177e4SLinus Torvalds 			if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
206*8c689a6eSAl Viro 				__be16 *ports = (__be16 *)xprth;
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds 				fl->fl_ip_sport = ports[0];
2091da177e4SLinus Torvalds 				fl->fl_ip_dport = ports[1];
2101da177e4SLinus Torvalds 			}
2111da177e4SLinus Torvalds 			break;
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds 		case IPPROTO_ICMP:
2141da177e4SLinus Torvalds 			if (pskb_may_pull(skb, xprth + 2 - skb->data)) {
2151da177e4SLinus Torvalds 				u8 *icmp = xprth;
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds 				fl->fl_icmp_type = icmp[0];
2181da177e4SLinus Torvalds 				fl->fl_icmp_code = icmp[1];
2191da177e4SLinus Torvalds 			}
2201da177e4SLinus Torvalds 			break;
2211da177e4SLinus Torvalds 
2221da177e4SLinus Torvalds 		case IPPROTO_ESP:
2231da177e4SLinus Torvalds 			if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
2244324a174SAl Viro 				__be32 *ehdr = (__be32 *)xprth;
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds 				fl->fl_ipsec_spi = ehdr[0];
2271da177e4SLinus Torvalds 			}
2281da177e4SLinus Torvalds 			break;
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds 		case IPPROTO_AH:
2311da177e4SLinus Torvalds 			if (pskb_may_pull(skb, xprth + 8 - skb->data)) {
2324324a174SAl Viro 				__be32 *ah_hdr = (__be32*)xprth;
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds 				fl->fl_ipsec_spi = ah_hdr[1];
2351da177e4SLinus Torvalds 			}
2361da177e4SLinus Torvalds 			break;
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds 		case IPPROTO_COMP:
2391da177e4SLinus Torvalds 			if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
2404324a174SAl Viro 				__be16 *ipcomp_hdr = (__be16 *)xprth;
2411da177e4SLinus Torvalds 
2424195f814SAlexey Dobriyan 				fl->fl_ipsec_spi = htonl(ntohs(ipcomp_hdr[1]));
2431da177e4SLinus Torvalds 			}
2441da177e4SLinus Torvalds 			break;
2451da177e4SLinus Torvalds 		default:
2461da177e4SLinus Torvalds 			fl->fl_ipsec_spi = 0;
2471da177e4SLinus Torvalds 			break;
2481da177e4SLinus Torvalds 		};
2491da177e4SLinus Torvalds 	}
2501da177e4SLinus Torvalds 	fl->proto = iph->protocol;
2511da177e4SLinus Torvalds 	fl->fl4_dst = iph->daddr;
2521da177e4SLinus Torvalds 	fl->fl4_src = iph->saddr;
2534da3089fSHerbert Xu 	fl->fl4_tos = iph->tos;
2541da177e4SLinus Torvalds }
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds static inline int xfrm4_garbage_collect(void)
2571da177e4SLinus Torvalds {
2581da177e4SLinus Torvalds 	xfrm4_policy_afinfo.garbage_collect();
2591da177e4SLinus Torvalds 	return (atomic_read(&xfrm4_dst_ops.entries) > xfrm4_dst_ops.gc_thresh*2);
2601da177e4SLinus Torvalds }
2611da177e4SLinus Torvalds 
2621da177e4SLinus Torvalds static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu)
2631da177e4SLinus Torvalds {
2641da177e4SLinus Torvalds 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
2651da177e4SLinus Torvalds 	struct dst_entry *path = xdst->route;
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 	path->ops->update_pmtu(path, mtu);
2681da177e4SLinus Torvalds }
2691da177e4SLinus Torvalds 
270aabc9761SHerbert Xu static void xfrm4_dst_destroy(struct dst_entry *dst)
271aabc9761SHerbert Xu {
272aabc9761SHerbert Xu 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
273aabc9761SHerbert Xu 
274aabc9761SHerbert Xu 	if (likely(xdst->u.rt.idev))
275aabc9761SHerbert Xu 		in_dev_put(xdst->u.rt.idev);
276aabc9761SHerbert Xu 	xfrm_dst_destroy(xdst);
277aabc9761SHerbert Xu }
278aabc9761SHerbert Xu 
279aabc9761SHerbert Xu static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
280aabc9761SHerbert Xu 			     int unregister)
281aabc9761SHerbert Xu {
282aabc9761SHerbert Xu 	struct xfrm_dst *xdst;
283aabc9761SHerbert Xu 
284aabc9761SHerbert Xu 	if (!unregister)
285aabc9761SHerbert Xu 		return;
286aabc9761SHerbert Xu 
287aabc9761SHerbert Xu 	xdst = (struct xfrm_dst *)dst;
288aabc9761SHerbert Xu 	if (xdst->u.rt.idev->dev == dev) {
289aabc9761SHerbert Xu 		struct in_device *loopback_idev = in_dev_get(&loopback_dev);
290aabc9761SHerbert Xu 		BUG_ON(!loopback_idev);
291aabc9761SHerbert Xu 
292aabc9761SHerbert Xu 		do {
293aabc9761SHerbert Xu 			in_dev_put(xdst->u.rt.idev);
294aabc9761SHerbert Xu 			xdst->u.rt.idev = loopback_idev;
295aabc9761SHerbert Xu 			in_dev_hold(loopback_idev);
296aabc9761SHerbert Xu 			xdst = (struct xfrm_dst *)xdst->u.dst.child;
297aabc9761SHerbert Xu 		} while (xdst->u.dst.xfrm);
298aabc9761SHerbert Xu 
299aabc9761SHerbert Xu 		__in_dev_put(loopback_idev);
300aabc9761SHerbert Xu 	}
301aabc9761SHerbert Xu 
302aabc9761SHerbert Xu 	xfrm_dst_ifdown(dst, dev);
303aabc9761SHerbert Xu }
304aabc9761SHerbert Xu 
3051da177e4SLinus Torvalds static struct dst_ops xfrm4_dst_ops = {
3061da177e4SLinus Torvalds 	.family =		AF_INET,
3071da177e4SLinus Torvalds 	.protocol =		__constant_htons(ETH_P_IP),
3081da177e4SLinus Torvalds 	.gc =			xfrm4_garbage_collect,
3091da177e4SLinus Torvalds 	.update_pmtu =		xfrm4_update_pmtu,
310aabc9761SHerbert Xu 	.destroy =		xfrm4_dst_destroy,
311aabc9761SHerbert Xu 	.ifdown =		xfrm4_dst_ifdown,
3121da177e4SLinus Torvalds 	.gc_thresh =		1024,
3131da177e4SLinus Torvalds 	.entry_size =		sizeof(struct xfrm_dst),
3141da177e4SLinus Torvalds };
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
3171da177e4SLinus Torvalds 	.family = 		AF_INET,
3181da177e4SLinus Torvalds 	.dst_ops =		&xfrm4_dst_ops,
3191da177e4SLinus Torvalds 	.dst_lookup =		xfrm4_dst_lookup,
320a1e59abfSPatrick McHardy 	.get_saddr =		xfrm4_get_saddr,
3211da177e4SLinus Torvalds 	.find_bundle = 		__xfrm4_find_bundle,
3221da177e4SLinus Torvalds 	.bundle_create =	__xfrm4_bundle_create,
3231da177e4SLinus Torvalds 	.decode_session =	_decode_session4,
3241da177e4SLinus Torvalds };
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds static void __init xfrm4_policy_init(void)
3271da177e4SLinus Torvalds {
3281da177e4SLinus Torvalds 	xfrm_policy_register_afinfo(&xfrm4_policy_afinfo);
3291da177e4SLinus Torvalds }
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds static void __exit xfrm4_policy_fini(void)
3321da177e4SLinus Torvalds {
3331da177e4SLinus Torvalds 	xfrm_policy_unregister_afinfo(&xfrm4_policy_afinfo);
3341da177e4SLinus Torvalds }
3351da177e4SLinus Torvalds 
3361da177e4SLinus Torvalds void __init xfrm4_init(void)
3371da177e4SLinus Torvalds {
3381da177e4SLinus Torvalds 	xfrm4_state_init();
3391da177e4SLinus Torvalds 	xfrm4_policy_init();
3401da177e4SLinus Torvalds }
3411da177e4SLinus Torvalds 
342