xref: /linux/net/ipv6/xfrm6_protocol.c (revision 0146dca7)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27e14ea15SSteffen Klassert /* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6.
37e14ea15SSteffen Klassert  *
47e14ea15SSteffen Klassert  * Copyright (C) 2013 secunet Security Networks AG
57e14ea15SSteffen Klassert  *
67e14ea15SSteffen Klassert  * Author:
77e14ea15SSteffen Klassert  * Steffen Klassert <steffen.klassert@secunet.com>
87e14ea15SSteffen Klassert  *
97e14ea15SSteffen Klassert  * Based on:
107e14ea15SSteffen Klassert  * net/ipv4/xfrm4_protocol.c
117e14ea15SSteffen Klassert  */
127e14ea15SSteffen Klassert 
137e14ea15SSteffen Klassert #include <linux/init.h>
147e14ea15SSteffen Klassert #include <linux/mutex.h>
157e14ea15SSteffen Klassert #include <linux/skbuff.h>
167e14ea15SSteffen Klassert #include <linux/icmpv6.h>
17*0146dca7SSabrina Dubroca #include <net/ip6_route.h>
187e14ea15SSteffen Klassert #include <net/ipv6.h>
197e14ea15SSteffen Klassert #include <net/protocol.h>
207e14ea15SSteffen Klassert #include <net/xfrm.h>
217e14ea15SSteffen Klassert 
227e14ea15SSteffen Klassert static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly;
237e14ea15SSteffen Klassert static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly;
247e14ea15SSteffen Klassert static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly;
257e14ea15SSteffen Klassert static DEFINE_MUTEX(xfrm6_protocol_mutex);
267e14ea15SSteffen Klassert 
proto_handlers(u8 protocol)277e14ea15SSteffen Klassert static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol)
287e14ea15SSteffen Klassert {
297e14ea15SSteffen Klassert 	switch (protocol) {
307e14ea15SSteffen Klassert 	case IPPROTO_ESP:
317e14ea15SSteffen Klassert 		return &esp6_handlers;
327e14ea15SSteffen Klassert 	case IPPROTO_AH:
337e14ea15SSteffen Klassert 		return &ah6_handlers;
347e14ea15SSteffen Klassert 	case IPPROTO_COMP:
357e14ea15SSteffen Klassert 		return &ipcomp6_handlers;
367e14ea15SSteffen Klassert 	}
377e14ea15SSteffen Klassert 
387e14ea15SSteffen Klassert 	return NULL;
397e14ea15SSteffen Klassert }
407e14ea15SSteffen Klassert 
417e14ea15SSteffen Klassert #define for_each_protocol_rcu(head, handler)		\
427e14ea15SSteffen Klassert 	for (handler = rcu_dereference(head);		\
437e14ea15SSteffen Klassert 	     handler != NULL;				\
447e14ea15SSteffen Klassert 	     handler = rcu_dereference(handler->next))	\
457e14ea15SSteffen Klassert 
xfrm6_rcv_cb(struct sk_buff * skb,u8 protocol,int err)46bb9cd077SFlorian Westphal static int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err)
477e14ea15SSteffen Klassert {
487e14ea15SSteffen Klassert 	int ret;
497e14ea15SSteffen Klassert 	struct xfrm6_protocol *handler;
50edb666f0SSteffen Klassert 	struct xfrm6_protocol __rcu **head = proto_handlers(protocol);
51edb666f0SSteffen Klassert 
52edb666f0SSteffen Klassert 	if (!head)
53edb666f0SSteffen Klassert 		return 0;
547e14ea15SSteffen Klassert 
557e14ea15SSteffen Klassert 	for_each_protocol_rcu(*proto_handlers(protocol), handler)
567e14ea15SSteffen Klassert 		if ((ret = handler->cb_handler(skb, err)) <= 0)
577e14ea15SSteffen Klassert 			return ret;
587e14ea15SSteffen Klassert 
597e14ea15SSteffen Klassert 	return 0;
607e14ea15SSteffen Klassert }
617e14ea15SSteffen Klassert 
xfrm6_rcv_encap(struct sk_buff * skb,int nexthdr,__be32 spi,int encap_type)62*0146dca7SSabrina Dubroca int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
63*0146dca7SSabrina Dubroca 		    int encap_type)
64*0146dca7SSabrina Dubroca {
65*0146dca7SSabrina Dubroca 	int ret;
66*0146dca7SSabrina Dubroca 	struct xfrm6_protocol *handler;
67*0146dca7SSabrina Dubroca 	struct xfrm6_protocol __rcu **head = proto_handlers(nexthdr);
68*0146dca7SSabrina Dubroca 
69*0146dca7SSabrina Dubroca 	XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
70*0146dca7SSabrina Dubroca 	XFRM_SPI_SKB_CB(skb)->family = AF_INET6;
71*0146dca7SSabrina Dubroca 	XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr);
72*0146dca7SSabrina Dubroca 
73*0146dca7SSabrina Dubroca 	if (!head)
74*0146dca7SSabrina Dubroca 		goto out;
75*0146dca7SSabrina Dubroca 
76*0146dca7SSabrina Dubroca 	if (!skb_dst(skb)) {
77*0146dca7SSabrina Dubroca 		const struct ipv6hdr *ip6h = ipv6_hdr(skb);
78*0146dca7SSabrina Dubroca 		int flags = RT6_LOOKUP_F_HAS_SADDR;
79*0146dca7SSabrina Dubroca 		struct dst_entry *dst;
80*0146dca7SSabrina Dubroca 		struct flowi6 fl6 = {
81*0146dca7SSabrina Dubroca 			.flowi6_iif   = skb->dev->ifindex,
82*0146dca7SSabrina Dubroca 			.daddr        = ip6h->daddr,
83*0146dca7SSabrina Dubroca 			.saddr        = ip6h->saddr,
84*0146dca7SSabrina Dubroca 			.flowlabel    = ip6_flowinfo(ip6h),
85*0146dca7SSabrina Dubroca 			.flowi6_mark  = skb->mark,
86*0146dca7SSabrina Dubroca 			.flowi6_proto = ip6h->nexthdr,
87*0146dca7SSabrina Dubroca 		};
88*0146dca7SSabrina Dubroca 
89*0146dca7SSabrina Dubroca 		dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6,
90*0146dca7SSabrina Dubroca 					     skb, flags);
91*0146dca7SSabrina Dubroca 		if (dst->error)
92*0146dca7SSabrina Dubroca 			goto drop;
93*0146dca7SSabrina Dubroca 		skb_dst_set(skb, dst);
94*0146dca7SSabrina Dubroca 	}
95*0146dca7SSabrina Dubroca 
96*0146dca7SSabrina Dubroca 	for_each_protocol_rcu(*head, handler)
97*0146dca7SSabrina Dubroca 		if ((ret = handler->input_handler(skb, nexthdr, spi, encap_type)) != -EINVAL)
98*0146dca7SSabrina Dubroca 			return ret;
99*0146dca7SSabrina Dubroca 
100*0146dca7SSabrina Dubroca out:
101*0146dca7SSabrina Dubroca 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
102*0146dca7SSabrina Dubroca 
103*0146dca7SSabrina Dubroca drop:
104*0146dca7SSabrina Dubroca 	kfree_skb(skb);
105*0146dca7SSabrina Dubroca 	return 0;
106*0146dca7SSabrina Dubroca }
107*0146dca7SSabrina Dubroca EXPORT_SYMBOL(xfrm6_rcv_encap);
108*0146dca7SSabrina Dubroca 
xfrm6_esp_rcv(struct sk_buff * skb)1097e14ea15SSteffen Klassert static int xfrm6_esp_rcv(struct sk_buff *skb)
1107e14ea15SSteffen Klassert {
1117e14ea15SSteffen Klassert 	int ret;
1127e14ea15SSteffen Klassert 	struct xfrm6_protocol *handler;
1137e14ea15SSteffen Klassert 
1147e14ea15SSteffen Klassert 	XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
1157e14ea15SSteffen Klassert 
1167e14ea15SSteffen Klassert 	for_each_protocol_rcu(esp6_handlers, handler)
1177e14ea15SSteffen Klassert 		if ((ret = handler->handler(skb)) != -EINVAL)
1187e14ea15SSteffen Klassert 			return ret;
1197e14ea15SSteffen Klassert 
1207e14ea15SSteffen Klassert 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
1217e14ea15SSteffen Klassert 
1227e14ea15SSteffen Klassert 	kfree_skb(skb);
1237e14ea15SSteffen Klassert 	return 0;
1247e14ea15SSteffen Klassert }
1257e14ea15SSteffen Klassert 
xfrm6_esp_err(struct sk_buff * skb,struct inet6_skb_parm * opt,u8 type,u8 code,int offset,__be32 info)12632bbd879SStefano Brivio static int xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
1277e14ea15SSteffen Klassert 			  u8 type, u8 code, int offset, __be32 info)
1287e14ea15SSteffen Klassert {
1297e14ea15SSteffen Klassert 	struct xfrm6_protocol *handler;
1307e14ea15SSteffen Klassert 
1317e14ea15SSteffen Klassert 	for_each_protocol_rcu(esp6_handlers, handler)
1327e14ea15SSteffen Klassert 		if (!handler->err_handler(skb, opt, type, code, offset, info))
13332bbd879SStefano Brivio 			return 0;
13432bbd879SStefano Brivio 
13532bbd879SStefano Brivio 	return -ENOENT;
1367e14ea15SSteffen Klassert }
1377e14ea15SSteffen Klassert 
xfrm6_ah_rcv(struct sk_buff * skb)1387e14ea15SSteffen Klassert static int xfrm6_ah_rcv(struct sk_buff *skb)
1397e14ea15SSteffen Klassert {
1407e14ea15SSteffen Klassert 	int ret;
1417e14ea15SSteffen Klassert 	struct xfrm6_protocol *handler;
1427e14ea15SSteffen Klassert 
1437e14ea15SSteffen Klassert 	XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
1447e14ea15SSteffen Klassert 
1457e14ea15SSteffen Klassert 	for_each_protocol_rcu(ah6_handlers, handler)
1467e14ea15SSteffen Klassert 		if ((ret = handler->handler(skb)) != -EINVAL)
1477e14ea15SSteffen Klassert 			return ret;
1487e14ea15SSteffen Klassert 
1497e14ea15SSteffen Klassert 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
1507e14ea15SSteffen Klassert 
1517e14ea15SSteffen Klassert 	kfree_skb(skb);
1527e14ea15SSteffen Klassert 	return 0;
1537e14ea15SSteffen Klassert }
1547e14ea15SSteffen Klassert 
xfrm6_ah_err(struct sk_buff * skb,struct inet6_skb_parm * opt,u8 type,u8 code,int offset,__be32 info)15532bbd879SStefano Brivio static int xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
1567e14ea15SSteffen Klassert 			 u8 type, u8 code, int offset, __be32 info)
1577e14ea15SSteffen Klassert {
1587e14ea15SSteffen Klassert 	struct xfrm6_protocol *handler;
1597e14ea15SSteffen Klassert 
1607e14ea15SSteffen Klassert 	for_each_protocol_rcu(ah6_handlers, handler)
1617e14ea15SSteffen Klassert 		if (!handler->err_handler(skb, opt, type, code, offset, info))
16232bbd879SStefano Brivio 			return 0;
16332bbd879SStefano Brivio 
16432bbd879SStefano Brivio 	return -ENOENT;
1657e14ea15SSteffen Klassert }
1667e14ea15SSteffen Klassert 
xfrm6_ipcomp_rcv(struct sk_buff * skb)1677e14ea15SSteffen Klassert static int xfrm6_ipcomp_rcv(struct sk_buff *skb)
1687e14ea15SSteffen Klassert {
1697e14ea15SSteffen Klassert 	int ret;
1707e14ea15SSteffen Klassert 	struct xfrm6_protocol *handler;
1717e14ea15SSteffen Klassert 
1727e14ea15SSteffen Klassert 	XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
1737e14ea15SSteffen Klassert 
1747e14ea15SSteffen Klassert 	for_each_protocol_rcu(ipcomp6_handlers, handler)
1757e14ea15SSteffen Klassert 		if ((ret = handler->handler(skb)) != -EINVAL)
1767e14ea15SSteffen Klassert 			return ret;
1777e14ea15SSteffen Klassert 
1787e14ea15SSteffen Klassert 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
1797e14ea15SSteffen Klassert 
1807e14ea15SSteffen Klassert 	kfree_skb(skb);
1817e14ea15SSteffen Klassert 	return 0;
1827e14ea15SSteffen Klassert }
1837e14ea15SSteffen Klassert 
xfrm6_ipcomp_err(struct sk_buff * skb,struct inet6_skb_parm * opt,u8 type,u8 code,int offset,__be32 info)18432bbd879SStefano Brivio static int xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
1857e14ea15SSteffen Klassert 			     u8 type, u8 code, int offset, __be32 info)
1867e14ea15SSteffen Klassert {
1877e14ea15SSteffen Klassert 	struct xfrm6_protocol *handler;
1887e14ea15SSteffen Klassert 
1897e14ea15SSteffen Klassert 	for_each_protocol_rcu(ipcomp6_handlers, handler)
1907e14ea15SSteffen Klassert 		if (!handler->err_handler(skb, opt, type, code, offset, info))
19132bbd879SStefano Brivio 			return 0;
19232bbd879SStefano Brivio 
19332bbd879SStefano Brivio 	return -ENOENT;
1947e14ea15SSteffen Klassert }
1957e14ea15SSteffen Klassert 
1967e14ea15SSteffen Klassert static const struct inet6_protocol esp6_protocol = {
1977e14ea15SSteffen Klassert 	.handler	=	xfrm6_esp_rcv,
1987e14ea15SSteffen Klassert 	.err_handler	=	xfrm6_esp_err,
1997e14ea15SSteffen Klassert 	.flags		=	INET6_PROTO_NOPOLICY,
2007e14ea15SSteffen Klassert };
2017e14ea15SSteffen Klassert 
2027e14ea15SSteffen Klassert static const struct inet6_protocol ah6_protocol = {
2037e14ea15SSteffen Klassert 	.handler	=	xfrm6_ah_rcv,
2047e14ea15SSteffen Klassert 	.err_handler	=	xfrm6_ah_err,
2057e14ea15SSteffen Klassert 	.flags		=	INET6_PROTO_NOPOLICY,
2067e14ea15SSteffen Klassert };
2077e14ea15SSteffen Klassert 
2087e14ea15SSteffen Klassert static const struct inet6_protocol ipcomp6_protocol = {
2097e14ea15SSteffen Klassert 	.handler	=	xfrm6_ipcomp_rcv,
2107e14ea15SSteffen Klassert 	.err_handler	=	xfrm6_ipcomp_err,
2117e14ea15SSteffen Klassert 	.flags		=	INET6_PROTO_NOPOLICY,
2127e14ea15SSteffen Klassert };
2137e14ea15SSteffen Klassert 
214960fdfdeSFlorian Westphal static const struct xfrm_input_afinfo xfrm6_input_afinfo = {
2157e14ea15SSteffen Klassert 	.family		=	AF_INET6,
2167e14ea15SSteffen Klassert 	.callback	=	xfrm6_rcv_cb,
2177e14ea15SSteffen Klassert };
2187e14ea15SSteffen Klassert 
netproto(unsigned char protocol)2197e14ea15SSteffen Klassert static inline const struct inet6_protocol *netproto(unsigned char protocol)
2207e14ea15SSteffen Klassert {
2217e14ea15SSteffen Klassert 	switch (protocol) {
2227e14ea15SSteffen Klassert 	case IPPROTO_ESP:
2237e14ea15SSteffen Klassert 		return &esp6_protocol;
2247e14ea15SSteffen Klassert 	case IPPROTO_AH:
2257e14ea15SSteffen Klassert 		return &ah6_protocol;
2267e14ea15SSteffen Klassert 	case IPPROTO_COMP:
2277e14ea15SSteffen Klassert 		return &ipcomp6_protocol;
2287e14ea15SSteffen Klassert 	}
2297e14ea15SSteffen Klassert 
2307e14ea15SSteffen Klassert 	return NULL;
2317e14ea15SSteffen Klassert }
2327e14ea15SSteffen Klassert 
xfrm6_protocol_register(struct xfrm6_protocol * handler,unsigned char protocol)2337e14ea15SSteffen Klassert int xfrm6_protocol_register(struct xfrm6_protocol *handler,
2347e14ea15SSteffen Klassert 			    unsigned char protocol)
2357e14ea15SSteffen Klassert {
2367e14ea15SSteffen Klassert 	struct xfrm6_protocol __rcu **pprev;
2377e14ea15SSteffen Klassert 	struct xfrm6_protocol *t;
2387e14ea15SSteffen Klassert 	bool add_netproto = false;
2397e14ea15SSteffen Klassert 	int ret = -EEXIST;
2407e14ea15SSteffen Klassert 	int priority = handler->priority;
2417e14ea15SSteffen Klassert 
242edb666f0SSteffen Klassert 	if (!proto_handlers(protocol) || !netproto(protocol))
243edb666f0SSteffen Klassert 		return -EINVAL;
244edb666f0SSteffen Klassert 
2457e14ea15SSteffen Klassert 	mutex_lock(&xfrm6_protocol_mutex);
2467e14ea15SSteffen Klassert 
2477e14ea15SSteffen Klassert 	if (!rcu_dereference_protected(*proto_handlers(protocol),
2487e14ea15SSteffen Klassert 				       lockdep_is_held(&xfrm6_protocol_mutex)))
2497e14ea15SSteffen Klassert 		add_netproto = true;
2507e14ea15SSteffen Klassert 
2517e14ea15SSteffen Klassert 	for (pprev = proto_handlers(protocol);
2527e14ea15SSteffen Klassert 	     (t = rcu_dereference_protected(*pprev,
2537e14ea15SSteffen Klassert 			lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
2547e14ea15SSteffen Klassert 	     pprev = &t->next) {
2557e14ea15SSteffen Klassert 		if (t->priority < priority)
2567e14ea15SSteffen Klassert 			break;
2577e14ea15SSteffen Klassert 		if (t->priority == priority)
2587e14ea15SSteffen Klassert 			goto err;
2597e14ea15SSteffen Klassert 	}
2607e14ea15SSteffen Klassert 
2617e14ea15SSteffen Klassert 	handler->next = *pprev;
2627e14ea15SSteffen Klassert 	rcu_assign_pointer(*pprev, handler);
2637e14ea15SSteffen Klassert 
2647e14ea15SSteffen Klassert 	ret = 0;
2657e14ea15SSteffen Klassert 
2667e14ea15SSteffen Klassert err:
2677e14ea15SSteffen Klassert 	mutex_unlock(&xfrm6_protocol_mutex);
2687e14ea15SSteffen Klassert 
2697e14ea15SSteffen Klassert 	if (add_netproto) {
2707e14ea15SSteffen Klassert 		if (inet6_add_protocol(netproto(protocol), protocol)) {
2717e14ea15SSteffen Klassert 			pr_err("%s: can't add protocol\n", __func__);
2727e14ea15SSteffen Klassert 			ret = -EAGAIN;
2737e14ea15SSteffen Klassert 		}
2747e14ea15SSteffen Klassert 	}
2757e14ea15SSteffen Klassert 
2767e14ea15SSteffen Klassert 	return ret;
2777e14ea15SSteffen Klassert }
2787e14ea15SSteffen Klassert EXPORT_SYMBOL(xfrm6_protocol_register);
2797e14ea15SSteffen Klassert 
xfrm6_protocol_deregister(struct xfrm6_protocol * handler,unsigned char protocol)2807e14ea15SSteffen Klassert int xfrm6_protocol_deregister(struct xfrm6_protocol *handler,
2817e14ea15SSteffen Klassert 			      unsigned char protocol)
2827e14ea15SSteffen Klassert {
2837e14ea15SSteffen Klassert 	struct xfrm6_protocol __rcu **pprev;
2847e14ea15SSteffen Klassert 	struct xfrm6_protocol *t;
2857e14ea15SSteffen Klassert 	int ret = -ENOENT;
2867e14ea15SSteffen Klassert 
287edb666f0SSteffen Klassert 	if (!proto_handlers(protocol) || !netproto(protocol))
288edb666f0SSteffen Klassert 		return -EINVAL;
289edb666f0SSteffen Klassert 
2907e14ea15SSteffen Klassert 	mutex_lock(&xfrm6_protocol_mutex);
2917e14ea15SSteffen Klassert 
2927e14ea15SSteffen Klassert 	for (pprev = proto_handlers(protocol);
2937e14ea15SSteffen Klassert 	     (t = rcu_dereference_protected(*pprev,
2947e14ea15SSteffen Klassert 			lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
2957e14ea15SSteffen Klassert 	     pprev = &t->next) {
2967e14ea15SSteffen Klassert 		if (t == handler) {
2977e14ea15SSteffen Klassert 			*pprev = handler->next;
2987e14ea15SSteffen Klassert 			ret = 0;
2997e14ea15SSteffen Klassert 			break;
3007e14ea15SSteffen Klassert 		}
3017e14ea15SSteffen Klassert 	}
3027e14ea15SSteffen Klassert 
3037e14ea15SSteffen Klassert 	if (!rcu_dereference_protected(*proto_handlers(protocol),
3047e14ea15SSteffen Klassert 				       lockdep_is_held(&xfrm6_protocol_mutex))) {
3057e14ea15SSteffen Klassert 		if (inet6_del_protocol(netproto(protocol), protocol) < 0) {
3067e14ea15SSteffen Klassert 			pr_err("%s: can't remove protocol\n", __func__);
3077e14ea15SSteffen Klassert 			ret = -EAGAIN;
3087e14ea15SSteffen Klassert 		}
3097e14ea15SSteffen Klassert 	}
3107e14ea15SSteffen Klassert 
3117e14ea15SSteffen Klassert 	mutex_unlock(&xfrm6_protocol_mutex);
3127e14ea15SSteffen Klassert 
3137e14ea15SSteffen Klassert 	synchronize_net();
3147e14ea15SSteffen Klassert 
3157e14ea15SSteffen Klassert 	return ret;
3167e14ea15SSteffen Klassert }
3177e14ea15SSteffen Klassert EXPORT_SYMBOL(xfrm6_protocol_deregister);
3187e14ea15SSteffen Klassert 
xfrm6_protocol_init(void)3197e14ea15SSteffen Klassert int __init xfrm6_protocol_init(void)
3207e14ea15SSteffen Klassert {
3217e14ea15SSteffen Klassert 	return xfrm_input_register_afinfo(&xfrm6_input_afinfo);
3227e14ea15SSteffen Klassert }
3237e14ea15SSteffen Klassert 
xfrm6_protocol_fini(void)3247e14ea15SSteffen Klassert void xfrm6_protocol_fini(void)
3257e14ea15SSteffen Klassert {
3267e14ea15SSteffen Klassert 	xfrm_input_unregister_afinfo(&xfrm6_input_afinfo);
3277e14ea15SSteffen Klassert }
328