xref: /linux/net/ipv4/xfrm4_policy.c (revision 077fbac405bfc6d41419ad6c1725804ad4e9887c)
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 
1166cdb3caSHerbert Xu #include <linux/err.h>
1266cdb3caSHerbert Xu #include <linux/kernel.h>
13aabc9761SHerbert Xu #include <linux/inetdevice.h>
14cc9ff19dSTimo Teräs #include <linux/if_tunnel.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,
23*077fbac4SLorenzo Colitti 					    const xfrm_address_t *daddr,
24*077fbac4SLorenzo 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;
3111d7a0bbSDavid Ahern 	fl4->flowi4_oif = l3mdev_master_ifindex_by_index(net, oif);
32*077fbac4SLorenzo Colitti 	fl4->flowi4_mark = mark;
3366cdb3caSHerbert Xu 	if (saddr)
348f01cb08SDavid S. Miller 		fl4->saddr = saddr->a4;
3566cdb3caSHerbert Xu 
3658189ca7SDavid Ahern 	fl4->flowi4_flags = FLOWI_FLAG_SKIP_NH_OIF;
3758189ca7SDavid Ahern 
388f01cb08SDavid S. Miller 	rt = __ip_route_output_key(net, fl4);
39b23dd4feSDavid S. Miller 	if (!IS_ERR(rt))
40b23dd4feSDavid S. Miller 		return &rt->dst;
41b23dd4feSDavid S. Miller 
42b23dd4feSDavid S. Miller 	return ERR_CAST(rt);
43a1e59abfSPatrick McHardy }
4466cdb3caSHerbert Xu 
4542a7b32bSDavid Ahern static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, int oif,
468f01cb08SDavid S. Miller 					  const xfrm_address_t *saddr,
47*077fbac4SLorenzo Colitti 					  const xfrm_address_t *daddr,
48*077fbac4SLorenzo Colitti 					  u32 mark)
498f01cb08SDavid S. Miller {
508f01cb08SDavid S. Miller 	struct flowi4 fl4;
518f01cb08SDavid S. Miller 
52*077fbac4SLorenzo Colitti 	return __xfrm4_dst_lookup(net, &fl4, tos, oif, saddr, daddr, mark);
538f01cb08SDavid S. Miller }
548f01cb08SDavid S. Miller 
5542a7b32bSDavid Ahern static int xfrm4_get_saddr(struct net *net, int oif,
56*077fbac4SLorenzo Colitti 			   xfrm_address_t *saddr, xfrm_address_t *daddr,
57*077fbac4SLorenzo Colitti 			   u32 mark)
5866cdb3caSHerbert Xu {
5966cdb3caSHerbert Xu 	struct dst_entry *dst;
608f01cb08SDavid S. Miller 	struct flowi4 fl4;
6166cdb3caSHerbert Xu 
62*077fbac4SLorenzo Colitti 	dst = __xfrm4_dst_lookup(net, &fl4, 0, oif, NULL, daddr, mark);
6366cdb3caSHerbert Xu 	if (IS_ERR(dst))
64a1e59abfSPatrick McHardy 		return -EHOSTUNREACH;
6566cdb3caSHerbert Xu 
668f01cb08SDavid S. Miller 	saddr->a4 = fl4.saddr;
6766cdb3caSHerbert Xu 	dst_release(dst);
6866cdb3caSHerbert Xu 	return 0;
69a1e59abfSPatrick McHardy }
70a1e59abfSPatrick McHardy 
7105d84025SDavid S. Miller static int xfrm4_get_tos(const struct flowi *fl)
721da177e4SLinus Torvalds {
737e1dc7b6SDavid S. Miller 	return IPTOS_RT_MASK & fl->u.ip4.flowi4_tos; /* Strip ECN bits */
741da177e4SLinus Torvalds }
751da177e4SLinus Torvalds 
76a1b05140SMasahide NAKAMURA static int xfrm4_init_path(struct xfrm_dst *path, struct dst_entry *dst,
77a1b05140SMasahide NAKAMURA 			   int nfheader_len)
78a1b05140SMasahide NAKAMURA {
79a1b05140SMasahide NAKAMURA 	return 0;
80a1b05140SMasahide NAKAMURA }
81a1b05140SMasahide NAKAMURA 
8287c1e12bSHerbert Xu static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
830c7b3eefSDavid S. Miller 			  const struct flowi *fl)
8425ee3286SHerbert Xu {
8525ee3286SHerbert Xu 	struct rtable *rt = (struct rtable *)xdst->route;
867e1dc7b6SDavid S. Miller 	const struct flowi4 *fl4 = &fl->u.ip4;
871da177e4SLinus Torvalds 
88b7323396SYan, Zheng 	xdst->u.rt.rt_iif = fl4->flowi4_iif;
891da177e4SLinus Torvalds 
9025ee3286SHerbert Xu 	xdst->u.dst.dev = dev;
9125ee3286SHerbert Xu 	dev_hold(dev);
9243372262SMiika Komu 
931da177e4SLinus Torvalds 	/* Sheit... I remember I did this right. Apparently,
941da177e4SLinus Torvalds 	 * it was magically lost, so this code needs audit */
959917e1e8SDavid S. Miller 	xdst->u.rt.rt_is_input = rt->rt_is_input;
9625ee3286SHerbert Xu 	xdst->u.rt.rt_flags = rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST |
9725ee3286SHerbert Xu 					      RTCF_LOCAL);
9825ee3286SHerbert Xu 	xdst->u.rt.rt_type = rt->rt_type;
9925ee3286SHerbert Xu 	xdst->u.rt.rt_gateway = rt->rt_gateway;
100155e8336SJulian Anastasov 	xdst->u.rt.rt_uses_gateway = rt->rt_uses_gateway;
1015943634fSDavid S. Miller 	xdst->u.rt.rt_pmtu = rt->rt_pmtu;
102b7503e0cSDavid Ahern 	xdst->u.rt.rt_table_id = rt->rt_table_id;
103caacf05eSDavid S. Miller 	INIT_LIST_HEAD(&xdst->u.rt.rt_uncached);
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 	return 0;
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds static void
109d5422efeSHerbert Xu _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
1101da177e4SLinus Torvalds {
111b71d1d42SEric Dumazet 	const struct iphdr *iph = ip_hdr(skb);
112d56f90a7SArnaldo Carvalho de Melo 	u8 *xprth = skb_network_header(skb) + iph->ihl * 4;
1137e1dc7b6SDavid S. Miller 	struct flowi4 *fl4 = &fl->u.ip4;
11484502b5eSSteffen Klassert 	int oif = 0;
11584502b5eSSteffen Klassert 
116385add90SDavid Ahern 	if (skb_dst(skb))
117e0d56fddSDavid Ahern 		oif = skb_dst(skb)->dev->ifindex;
1181da177e4SLinus Torvalds 
1197e1dc7b6SDavid S. Miller 	memset(fl4, 0, sizeof(struct flowi4));
1207e1dc7b6SDavid S. Miller 	fl4->flowi4_mark = skb->mark;
12184502b5eSSteffen Klassert 	fl4->flowi4_oif = reverse ? skb->skb_iif : oif;
12244b451f1SPeter Kosyh 
12356f8a75cSPaul Gortmaker 	if (!ip_is_fragment(iph)) {
1241da177e4SLinus Torvalds 		switch (iph->protocol) {
1251da177e4SLinus Torvalds 		case IPPROTO_UDP:
126ba4e58ecSGerrit Renker 		case IPPROTO_UDPLITE:
1271da177e4SLinus Torvalds 		case IPPROTO_TCP:
1281da177e4SLinus Torvalds 		case IPPROTO_SCTP:
1299e999993SPatrick McHardy 		case IPPROTO_DCCP:
130c615c9f3SWei Yongjun 			if (xprth + 4 < skb->data ||
131c615c9f3SWei Yongjun 			    pskb_may_pull(skb, xprth + 4 - skb->data)) {
132ea673a4dSSteffen Klassert 				__be16 *ports;
133ea673a4dSSteffen Klassert 
134ea673a4dSSteffen Klassert 				xprth = skb_network_header(skb) + iph->ihl * 4;
135ea673a4dSSteffen Klassert 				ports = (__be16 *)xprth;
1361da177e4SLinus Torvalds 
1379cce96dfSDavid S. Miller 				fl4->fl4_sport = ports[!!reverse];
1389cce96dfSDavid S. Miller 				fl4->fl4_dport = ports[!reverse];
1391da177e4SLinus Torvalds 			}
1401da177e4SLinus Torvalds 			break;
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 		case IPPROTO_ICMP:
1431a14f1e5SSteffen Klassert 			if (xprth + 2 < skb->data ||
1441a14f1e5SSteffen Klassert 			    pskb_may_pull(skb, xprth + 2 - skb->data)) {
145ea673a4dSSteffen Klassert 				u8 *icmp;
146ea673a4dSSteffen Klassert 
147ea673a4dSSteffen Klassert 				xprth = skb_network_header(skb) + iph->ihl * 4;
148ea673a4dSSteffen Klassert 				icmp = xprth;
1491da177e4SLinus Torvalds 
1509cce96dfSDavid S. Miller 				fl4->fl4_icmp_type = icmp[0];
1519cce96dfSDavid S. Miller 				fl4->fl4_icmp_code = icmp[1];
1521da177e4SLinus Torvalds 			}
1531da177e4SLinus Torvalds 			break;
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 		case IPPROTO_ESP:
1561a14f1e5SSteffen Klassert 			if (xprth + 4 < skb->data ||
1571a14f1e5SSteffen Klassert 			    pskb_may_pull(skb, xprth + 4 - skb->data)) {
158ea673a4dSSteffen Klassert 				__be32 *ehdr;
159ea673a4dSSteffen Klassert 
160ea673a4dSSteffen Klassert 				xprth = skb_network_header(skb) + iph->ihl * 4;
161ea673a4dSSteffen Klassert 				ehdr = (__be32 *)xprth;
1621da177e4SLinus Torvalds 
1639cce96dfSDavid S. Miller 				fl4->fl4_ipsec_spi = ehdr[0];
1641da177e4SLinus Torvalds 			}
1651da177e4SLinus Torvalds 			break;
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds 		case IPPROTO_AH:
1681a14f1e5SSteffen Klassert 			if (xprth + 8 < skb->data ||
1691a14f1e5SSteffen Klassert 			    pskb_may_pull(skb, xprth + 8 - skb->data)) {
170ea673a4dSSteffen Klassert 				__be32 *ah_hdr;
171ea673a4dSSteffen Klassert 
172ea673a4dSSteffen Klassert 				xprth = skb_network_header(skb) + iph->ihl * 4;
173ea673a4dSSteffen Klassert 				ah_hdr = (__be32 *)xprth;
1741da177e4SLinus Torvalds 
1759cce96dfSDavid S. Miller 				fl4->fl4_ipsec_spi = ah_hdr[1];
1761da177e4SLinus Torvalds 			}
1771da177e4SLinus Torvalds 			break;
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 		case IPPROTO_COMP:
1801a14f1e5SSteffen Klassert 			if (xprth + 4 < skb->data ||
1811a14f1e5SSteffen Klassert 			    pskb_may_pull(skb, xprth + 4 - skb->data)) {
182ea673a4dSSteffen Klassert 				__be16 *ipcomp_hdr;
183ea673a4dSSteffen Klassert 
184ea673a4dSSteffen Klassert 				xprth = skb_network_header(skb) + iph->ihl * 4;
185ea673a4dSSteffen Klassert 				ipcomp_hdr = (__be16 *)xprth;
1861da177e4SLinus Torvalds 
1879cce96dfSDavid S. Miller 				fl4->fl4_ipsec_spi = htonl(ntohs(ipcomp_hdr[1]));
1881da177e4SLinus Torvalds 			}
1891da177e4SLinus Torvalds 			break;
190cc9ff19dSTimo Teräs 
191cc9ff19dSTimo Teräs 		case IPPROTO_GRE:
1921a14f1e5SSteffen Klassert 			if (xprth + 12 < skb->data ||
1931a14f1e5SSteffen Klassert 			    pskb_may_pull(skb, xprth + 12 - skb->data)) {
194ea673a4dSSteffen Klassert 				__be16 *greflags;
195ea673a4dSSteffen Klassert 				__be32 *gre_hdr;
196ea673a4dSSteffen Klassert 
197ea673a4dSSteffen Klassert 				xprth = skb_network_header(skb) + iph->ihl * 4;
198ea673a4dSSteffen Klassert 				greflags = (__be16 *)xprth;
199ea673a4dSSteffen Klassert 				gre_hdr = (__be32 *)xprth;
200cc9ff19dSTimo Teräs 
201cc9ff19dSTimo Teräs 				if (greflags[0] & GRE_KEY) {
202cc9ff19dSTimo Teräs 					if (greflags[0] & GRE_CSUM)
203cc9ff19dSTimo Teräs 						gre_hdr++;
2049cce96dfSDavid S. Miller 					fl4->fl4_gre_key = gre_hdr[1];
205cc9ff19dSTimo Teräs 				}
206cc9ff19dSTimo Teräs 			}
207cc9ff19dSTimo Teräs 			break;
208cc9ff19dSTimo Teräs 
2091da177e4SLinus Torvalds 		default:
2109cce96dfSDavid S. Miller 			fl4->fl4_ipsec_spi = 0;
2111da177e4SLinus Torvalds 			break;
2123ff50b79SStephen Hemminger 		}
2131da177e4SLinus Torvalds 	}
2147e1dc7b6SDavid S. Miller 	fl4->flowi4_proto = iph->protocol;
2157e1dc7b6SDavid S. Miller 	fl4->daddr = reverse ? iph->saddr : iph->daddr;
2167e1dc7b6SDavid S. Miller 	fl4->saddr = reverse ? iph->daddr : iph->saddr;
2177e1dc7b6SDavid S. Miller 	fl4->flowi4_tos = iph->tos;
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds 
2206700c270SDavid S. Miller static void xfrm4_update_pmtu(struct dst_entry *dst, struct sock *sk,
2216700c270SDavid S. Miller 			      struct sk_buff *skb, u32 mtu)
2221da177e4SLinus Torvalds {
2231da177e4SLinus Torvalds 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
2241da177e4SLinus Torvalds 	struct dst_entry *path = xdst->route;
2251da177e4SLinus Torvalds 
2266700c270SDavid S. Miller 	path->ops->update_pmtu(path, sk, skb, mtu);
2271da177e4SLinus Torvalds }
2281da177e4SLinus Torvalds 
2296700c270SDavid S. Miller static void xfrm4_redirect(struct dst_entry *dst, struct sock *sk,
2306700c270SDavid S. Miller 			   struct sk_buff *skb)
23155be7a9cSDavid S. Miller {
23255be7a9cSDavid S. Miller 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
23355be7a9cSDavid S. Miller 	struct dst_entry *path = xdst->route;
23455be7a9cSDavid S. Miller 
2356700c270SDavid S. Miller 	path->ops->redirect(path, sk, skb);
23655be7a9cSDavid S. Miller }
23755be7a9cSDavid S. Miller 
238aabc9761SHerbert Xu static void xfrm4_dst_destroy(struct dst_entry *dst)
239aabc9761SHerbert Xu {
240aabc9761SHerbert Xu 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
241aabc9761SHerbert Xu 
24262fa8a84SDavid S. Miller 	dst_destroy_metrics_generic(dst);
24362fa8a84SDavid S. Miller 
244aabc9761SHerbert Xu 	xfrm_dst_destroy(xdst);
245aabc9761SHerbert Xu }
246aabc9761SHerbert Xu 
247aabc9761SHerbert Xu static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
248aabc9761SHerbert Xu 			     int unregister)
249aabc9761SHerbert Xu {
250aabc9761SHerbert Xu 	if (!unregister)
251aabc9761SHerbert Xu 		return;
252aabc9761SHerbert Xu 
253aabc9761SHerbert Xu 	xfrm_dst_ifdown(dst, dev);
254aabc9761SHerbert Xu }
255aabc9761SHerbert Xu 
256a8a572a6SDan Streetman static struct dst_ops xfrm4_dst_ops_template = {
2571da177e4SLinus Torvalds 	.family =		AF_INET,
2581da177e4SLinus Torvalds 	.update_pmtu =		xfrm4_update_pmtu,
25955be7a9cSDavid S. Miller 	.redirect =		xfrm4_redirect,
26062fa8a84SDavid S. Miller 	.cow_metrics =		dst_cow_metrics_generic,
261aabc9761SHerbert Xu 	.destroy =		xfrm4_dst_destroy,
262aabc9761SHerbert Xu 	.ifdown =		xfrm4_dst_ifdown,
263862b82c6SHerbert Xu 	.local_out =		__ip_local_out,
2643c2a89ddSFlorian Westphal 	.gc_thresh =		32768,
2651da177e4SLinus Torvalds };
2661da177e4SLinus Torvalds 
26737b10383SFlorian Westphal static const struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
268a8a572a6SDan Streetman 	.dst_ops =		&xfrm4_dst_ops_template,
2691da177e4SLinus Torvalds 	.dst_lookup =		xfrm4_dst_lookup,
270a1e59abfSPatrick McHardy 	.get_saddr =		xfrm4_get_saddr,
2711da177e4SLinus Torvalds 	.decode_session =	_decode_session4,
27225ee3286SHerbert Xu 	.get_tos =		xfrm4_get_tos,
273a1b05140SMasahide NAKAMURA 	.init_path =		xfrm4_init_path,
27425ee3286SHerbert Xu 	.fill_dst =		xfrm4_fill_dst,
2752774c131SDavid S. Miller 	.blackhole_route =	ipv4_blackhole_route,
2761da177e4SLinus Torvalds };
2771da177e4SLinus Torvalds 
278f816700aSRandy Dunlap #ifdef CONFIG_SYSCTL
279a44a4a00SNeil Horman static struct ctl_table xfrm4_policy_table[] = {
280a44a4a00SNeil Horman 	{
281a44a4a00SNeil Horman 		.procname       = "xfrm4_gc_thresh",
282d7c7544cSAlexey Dobriyan 		.data           = &init_net.xfrm.xfrm4_dst_ops.gc_thresh,
283a44a4a00SNeil Horman 		.maxlen         = sizeof(int),
284a44a4a00SNeil Horman 		.mode           = 0644,
285a44a4a00SNeil Horman 		.proc_handler   = proc_dointvec,
286a44a4a00SNeil Horman 	},
287a44a4a00SNeil Horman 	{ }
288a44a4a00SNeil Horman };
289a44a4a00SNeil Horman 
290318d3cc0SArnd Bergmann static __net_init int xfrm4_net_sysctl_init(struct net *net)
2918d068875SMichal Kubecek {
2928d068875SMichal Kubecek 	struct ctl_table *table;
2938d068875SMichal Kubecek 	struct ctl_table_header *hdr;
2948d068875SMichal Kubecek 
2958d068875SMichal Kubecek 	table = xfrm4_policy_table;
2968d068875SMichal Kubecek 	if (!net_eq(net, &init_net)) {
2978d068875SMichal Kubecek 		table = kmemdup(table, sizeof(xfrm4_policy_table), GFP_KERNEL);
2988d068875SMichal Kubecek 		if (!table)
2998d068875SMichal Kubecek 			goto err_alloc;
3008d068875SMichal Kubecek 
3018d068875SMichal Kubecek 		table[0].data = &net->xfrm.xfrm4_dst_ops.gc_thresh;
3028d068875SMichal Kubecek 	}
3038d068875SMichal Kubecek 
3048d068875SMichal Kubecek 	hdr = register_net_sysctl(net, "net/ipv4", table);
3058d068875SMichal Kubecek 	if (!hdr)
3068d068875SMichal Kubecek 		goto err_reg;
3078d068875SMichal Kubecek 
3088d068875SMichal Kubecek 	net->ipv4.xfrm4_hdr = hdr;
3098d068875SMichal Kubecek 	return 0;
3108d068875SMichal Kubecek 
3118d068875SMichal Kubecek err_reg:
3128d068875SMichal Kubecek 	if (!net_eq(net, &init_net))
3138d068875SMichal Kubecek 		kfree(table);
3148d068875SMichal Kubecek err_alloc:
3158d068875SMichal Kubecek 	return -ENOMEM;
3168d068875SMichal Kubecek }
3178d068875SMichal Kubecek 
318318d3cc0SArnd Bergmann static __net_exit void xfrm4_net_sysctl_exit(struct net *net)
3198d068875SMichal Kubecek {
3208d068875SMichal Kubecek 	struct ctl_table *table;
3218d068875SMichal Kubecek 
32251456b29SIan Morris 	if (!net->ipv4.xfrm4_hdr)
3238d068875SMichal Kubecek 		return;
3248d068875SMichal Kubecek 
3258d068875SMichal Kubecek 	table = net->ipv4.xfrm4_hdr->ctl_table_arg;
3268d068875SMichal Kubecek 	unregister_net_sysctl_table(net->ipv4.xfrm4_hdr);
3278d068875SMichal Kubecek 	if (!net_eq(net, &init_net))
3288d068875SMichal Kubecek 		kfree(table);
3298d068875SMichal Kubecek }
330a8a572a6SDan Streetman #else /* CONFIG_SYSCTL */
331318d3cc0SArnd Bergmann static inline int xfrm4_net_sysctl_init(struct net *net)
332a8a572a6SDan Streetman {
333a8a572a6SDan Streetman 	return 0;
334a8a572a6SDan Streetman }
335a8a572a6SDan Streetman 
336318d3cc0SArnd Bergmann static inline void xfrm4_net_sysctl_exit(struct net *net)
337a8a572a6SDan Streetman {
338a8a572a6SDan Streetman }
339a8a572a6SDan Streetman #endif
340a8a572a6SDan Streetman 
341a8a572a6SDan Streetman static int __net_init xfrm4_net_init(struct net *net)
342a8a572a6SDan Streetman {
343a8a572a6SDan Streetman 	int ret;
344a8a572a6SDan Streetman 
345a8a572a6SDan Streetman 	memcpy(&net->xfrm.xfrm4_dst_ops, &xfrm4_dst_ops_template,
346a8a572a6SDan Streetman 	       sizeof(xfrm4_dst_ops_template));
347a8a572a6SDan Streetman 	ret = dst_entries_init(&net->xfrm.xfrm4_dst_ops);
348a8a572a6SDan Streetman 	if (ret)
349a8a572a6SDan Streetman 		return ret;
350a8a572a6SDan Streetman 
351a8a572a6SDan Streetman 	ret = xfrm4_net_sysctl_init(net);
352a8a572a6SDan Streetman 	if (ret)
353a8a572a6SDan Streetman 		dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
354a8a572a6SDan Streetman 
355a8a572a6SDan Streetman 	return ret;
356a8a572a6SDan Streetman }
357a8a572a6SDan Streetman 
358a8a572a6SDan Streetman static void __net_exit xfrm4_net_exit(struct net *net)
359a8a572a6SDan Streetman {
360a8a572a6SDan Streetman 	xfrm4_net_sysctl_exit(net);
361a8a572a6SDan Streetman 	dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
362a8a572a6SDan Streetman }
3638d068875SMichal Kubecek 
3648d068875SMichal Kubecek static struct pernet_operations __net_initdata xfrm4_net_ops = {
3658d068875SMichal Kubecek 	.init	= xfrm4_net_init,
3668d068875SMichal Kubecek 	.exit	= xfrm4_net_exit,
3678d068875SMichal Kubecek };
368a44a4a00SNeil Horman 
3691da177e4SLinus Torvalds static void __init xfrm4_policy_init(void)
3701da177e4SLinus Torvalds {
371a2817d8bSFlorian Westphal 	xfrm_policy_register_afinfo(&xfrm4_policy_afinfo, AF_INET);
3721da177e4SLinus Torvalds }
3731da177e4SLinus Torvalds 
374703fb94eSSteffen Klassert void __init xfrm4_init(void)
3751da177e4SLinus Torvalds {
376d7c7544cSAlexey Dobriyan 	xfrm4_state_init();
377d7c7544cSAlexey Dobriyan 	xfrm4_policy_init();
3782f32b51bSSteffen Klassert 	xfrm4_protocol_init();
3798d068875SMichal Kubecek 	register_pernet_subsys(&xfrm4_net_ops);
3801da177e4SLinus Torvalds }
3811da177e4SLinus Torvalds 
382