xref: /linux/net/ipv6/xfrm6_protocol.c (revision 32bbd8793f24b0d5beb1cdb33c45c75ad1140e4b)
17e14ea15SSteffen Klassert /* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6.
27e14ea15SSteffen Klassert  *
37e14ea15SSteffen Klassert  * Copyright (C) 2013 secunet Security Networks AG
47e14ea15SSteffen Klassert  *
57e14ea15SSteffen Klassert  * Author:
67e14ea15SSteffen Klassert  * Steffen Klassert <steffen.klassert@secunet.com>
77e14ea15SSteffen Klassert  *
87e14ea15SSteffen Klassert  * Based on:
97e14ea15SSteffen Klassert  * net/ipv4/xfrm4_protocol.c
107e14ea15SSteffen Klassert  *
117e14ea15SSteffen Klassert  *	This program is free software; you can redistribute it and/or
127e14ea15SSteffen Klassert  *	modify it under the terms of the GNU General Public License
137e14ea15SSteffen Klassert  *	as published by the Free Software Foundation; either version
147e14ea15SSteffen Klassert  *	2 of the License, or (at your option) any later version.
157e14ea15SSteffen Klassert  */
167e14ea15SSteffen Klassert 
177e14ea15SSteffen Klassert #include <linux/init.h>
187e14ea15SSteffen Klassert #include <linux/mutex.h>
197e14ea15SSteffen Klassert #include <linux/skbuff.h>
207e14ea15SSteffen Klassert #include <linux/icmpv6.h>
217e14ea15SSteffen Klassert #include <net/ipv6.h>
227e14ea15SSteffen Klassert #include <net/protocol.h>
237e14ea15SSteffen Klassert #include <net/xfrm.h>
247e14ea15SSteffen Klassert 
257e14ea15SSteffen Klassert static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly;
267e14ea15SSteffen Klassert static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly;
277e14ea15SSteffen Klassert static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly;
287e14ea15SSteffen Klassert static DEFINE_MUTEX(xfrm6_protocol_mutex);
297e14ea15SSteffen Klassert 
307e14ea15SSteffen Klassert static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol)
317e14ea15SSteffen Klassert {
327e14ea15SSteffen Klassert 	switch (protocol) {
337e14ea15SSteffen Klassert 	case IPPROTO_ESP:
347e14ea15SSteffen Klassert 		return &esp6_handlers;
357e14ea15SSteffen Klassert 	case IPPROTO_AH:
367e14ea15SSteffen Klassert 		return &ah6_handlers;
377e14ea15SSteffen Klassert 	case IPPROTO_COMP:
387e14ea15SSteffen Klassert 		return &ipcomp6_handlers;
397e14ea15SSteffen Klassert 	}
407e14ea15SSteffen Klassert 
417e14ea15SSteffen Klassert 	return NULL;
427e14ea15SSteffen Klassert }
437e14ea15SSteffen Klassert 
447e14ea15SSteffen Klassert #define for_each_protocol_rcu(head, handler)		\
457e14ea15SSteffen Klassert 	for (handler = rcu_dereference(head);		\
467e14ea15SSteffen Klassert 	     handler != NULL;				\
477e14ea15SSteffen Klassert 	     handler = rcu_dereference(handler->next))	\
487e14ea15SSteffen Klassert 
497e14ea15SSteffen Klassert int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err)
507e14ea15SSteffen Klassert {
517e14ea15SSteffen Klassert 	int ret;
527e14ea15SSteffen Klassert 	struct xfrm6_protocol *handler;
53edb666f0SSteffen Klassert 	struct xfrm6_protocol __rcu **head = proto_handlers(protocol);
54edb666f0SSteffen Klassert 
55edb666f0SSteffen Klassert 	if (!head)
56edb666f0SSteffen Klassert 		return 0;
577e14ea15SSteffen Klassert 
587e14ea15SSteffen Klassert 	for_each_protocol_rcu(*proto_handlers(protocol), handler)
597e14ea15SSteffen Klassert 		if ((ret = handler->cb_handler(skb, err)) <= 0)
607e14ea15SSteffen Klassert 			return ret;
617e14ea15SSteffen Klassert 
627e14ea15SSteffen Klassert 	return 0;
637e14ea15SSteffen Klassert }
647e14ea15SSteffen Klassert EXPORT_SYMBOL(xfrm6_rcv_cb);
657e14ea15SSteffen Klassert 
667e14ea15SSteffen Klassert static int xfrm6_esp_rcv(struct sk_buff *skb)
677e14ea15SSteffen Klassert {
687e14ea15SSteffen Klassert 	int ret;
697e14ea15SSteffen Klassert 	struct xfrm6_protocol *handler;
707e14ea15SSteffen Klassert 
717e14ea15SSteffen Klassert 	XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
727e14ea15SSteffen Klassert 
737e14ea15SSteffen Klassert 	for_each_protocol_rcu(esp6_handlers, handler)
747e14ea15SSteffen Klassert 		if ((ret = handler->handler(skb)) != -EINVAL)
757e14ea15SSteffen Klassert 			return ret;
767e14ea15SSteffen Klassert 
777e14ea15SSteffen Klassert 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
787e14ea15SSteffen Klassert 
797e14ea15SSteffen Klassert 	kfree_skb(skb);
807e14ea15SSteffen Klassert 	return 0;
817e14ea15SSteffen Klassert }
827e14ea15SSteffen Klassert 
83*32bbd879SStefano Brivio static int xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
847e14ea15SSteffen Klassert 			  u8 type, u8 code, int offset, __be32 info)
857e14ea15SSteffen Klassert {
867e14ea15SSteffen Klassert 	struct xfrm6_protocol *handler;
877e14ea15SSteffen Klassert 
887e14ea15SSteffen Klassert 	for_each_protocol_rcu(esp6_handlers, handler)
897e14ea15SSteffen Klassert 		if (!handler->err_handler(skb, opt, type, code, offset, info))
90*32bbd879SStefano Brivio 			return 0;
91*32bbd879SStefano Brivio 
92*32bbd879SStefano Brivio 	return -ENOENT;
937e14ea15SSteffen Klassert }
947e14ea15SSteffen Klassert 
957e14ea15SSteffen Klassert static int xfrm6_ah_rcv(struct sk_buff *skb)
967e14ea15SSteffen Klassert {
977e14ea15SSteffen Klassert 	int ret;
987e14ea15SSteffen Klassert 	struct xfrm6_protocol *handler;
997e14ea15SSteffen Klassert 
1007e14ea15SSteffen Klassert 	XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
1017e14ea15SSteffen Klassert 
1027e14ea15SSteffen Klassert 	for_each_protocol_rcu(ah6_handlers, handler)
1037e14ea15SSteffen Klassert 		if ((ret = handler->handler(skb)) != -EINVAL)
1047e14ea15SSteffen Klassert 			return ret;
1057e14ea15SSteffen Klassert 
1067e14ea15SSteffen Klassert 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
1077e14ea15SSteffen Klassert 
1087e14ea15SSteffen Klassert 	kfree_skb(skb);
1097e14ea15SSteffen Klassert 	return 0;
1107e14ea15SSteffen Klassert }
1117e14ea15SSteffen Klassert 
112*32bbd879SStefano Brivio static int xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
1137e14ea15SSteffen Klassert 			 u8 type, u8 code, int offset, __be32 info)
1147e14ea15SSteffen Klassert {
1157e14ea15SSteffen Klassert 	struct xfrm6_protocol *handler;
1167e14ea15SSteffen Klassert 
1177e14ea15SSteffen Klassert 	for_each_protocol_rcu(ah6_handlers, handler)
1187e14ea15SSteffen Klassert 		if (!handler->err_handler(skb, opt, type, code, offset, info))
119*32bbd879SStefano Brivio 			return 0;
120*32bbd879SStefano Brivio 
121*32bbd879SStefano Brivio 	return -ENOENT;
1227e14ea15SSteffen Klassert }
1237e14ea15SSteffen Klassert 
1247e14ea15SSteffen Klassert static int xfrm6_ipcomp_rcv(struct sk_buff *skb)
1257e14ea15SSteffen Klassert {
1267e14ea15SSteffen Klassert 	int ret;
1277e14ea15SSteffen Klassert 	struct xfrm6_protocol *handler;
1287e14ea15SSteffen Klassert 
1297e14ea15SSteffen Klassert 	XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
1307e14ea15SSteffen Klassert 
1317e14ea15SSteffen Klassert 	for_each_protocol_rcu(ipcomp6_handlers, handler)
1327e14ea15SSteffen Klassert 		if ((ret = handler->handler(skb)) != -EINVAL)
1337e14ea15SSteffen Klassert 			return ret;
1347e14ea15SSteffen Klassert 
1357e14ea15SSteffen Klassert 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
1367e14ea15SSteffen Klassert 
1377e14ea15SSteffen Klassert 	kfree_skb(skb);
1387e14ea15SSteffen Klassert 	return 0;
1397e14ea15SSteffen Klassert }
1407e14ea15SSteffen Klassert 
141*32bbd879SStefano Brivio static int xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
1427e14ea15SSteffen Klassert 			     u8 type, u8 code, int offset, __be32 info)
1437e14ea15SSteffen Klassert {
1447e14ea15SSteffen Klassert 	struct xfrm6_protocol *handler;
1457e14ea15SSteffen Klassert 
1467e14ea15SSteffen Klassert 	for_each_protocol_rcu(ipcomp6_handlers, handler)
1477e14ea15SSteffen Klassert 		if (!handler->err_handler(skb, opt, type, code, offset, info))
148*32bbd879SStefano Brivio 			return 0;
149*32bbd879SStefano Brivio 
150*32bbd879SStefano Brivio 	return -ENOENT;
1517e14ea15SSteffen Klassert }
1527e14ea15SSteffen Klassert 
1537e14ea15SSteffen Klassert static const struct inet6_protocol esp6_protocol = {
1547e14ea15SSteffen Klassert 	.handler	=	xfrm6_esp_rcv,
1557e14ea15SSteffen Klassert 	.err_handler	=	xfrm6_esp_err,
1567e14ea15SSteffen Klassert 	.flags		=	INET6_PROTO_NOPOLICY,
1577e14ea15SSteffen Klassert };
1587e14ea15SSteffen Klassert 
1597e14ea15SSteffen Klassert static const struct inet6_protocol ah6_protocol = {
1607e14ea15SSteffen Klassert 	.handler	=	xfrm6_ah_rcv,
1617e14ea15SSteffen Klassert 	.err_handler	=	xfrm6_ah_err,
1627e14ea15SSteffen Klassert 	.flags		=	INET6_PROTO_NOPOLICY,
1637e14ea15SSteffen Klassert };
1647e14ea15SSteffen Klassert 
1657e14ea15SSteffen Klassert static const struct inet6_protocol ipcomp6_protocol = {
1667e14ea15SSteffen Klassert 	.handler	=	xfrm6_ipcomp_rcv,
1677e14ea15SSteffen Klassert 	.err_handler	=	xfrm6_ipcomp_err,
1687e14ea15SSteffen Klassert 	.flags		=	INET6_PROTO_NOPOLICY,
1697e14ea15SSteffen Klassert };
1707e14ea15SSteffen Klassert 
171960fdfdeSFlorian Westphal static const struct xfrm_input_afinfo xfrm6_input_afinfo = {
1727e14ea15SSteffen Klassert 	.family		=	AF_INET6,
1737e14ea15SSteffen Klassert 	.callback	=	xfrm6_rcv_cb,
1747e14ea15SSteffen Klassert };
1757e14ea15SSteffen Klassert 
1767e14ea15SSteffen Klassert static inline const struct inet6_protocol *netproto(unsigned char protocol)
1777e14ea15SSteffen Klassert {
1787e14ea15SSteffen Klassert 	switch (protocol) {
1797e14ea15SSteffen Klassert 	case IPPROTO_ESP:
1807e14ea15SSteffen Klassert 		return &esp6_protocol;
1817e14ea15SSteffen Klassert 	case IPPROTO_AH:
1827e14ea15SSteffen Klassert 		return &ah6_protocol;
1837e14ea15SSteffen Klassert 	case IPPROTO_COMP:
1847e14ea15SSteffen Klassert 		return &ipcomp6_protocol;
1857e14ea15SSteffen Klassert 	}
1867e14ea15SSteffen Klassert 
1877e14ea15SSteffen Klassert 	return NULL;
1887e14ea15SSteffen Klassert }
1897e14ea15SSteffen Klassert 
1907e14ea15SSteffen Klassert int xfrm6_protocol_register(struct xfrm6_protocol *handler,
1917e14ea15SSteffen Klassert 			    unsigned char protocol)
1927e14ea15SSteffen Klassert {
1937e14ea15SSteffen Klassert 	struct xfrm6_protocol __rcu **pprev;
1947e14ea15SSteffen Klassert 	struct xfrm6_protocol *t;
1957e14ea15SSteffen Klassert 	bool add_netproto = false;
1967e14ea15SSteffen Klassert 	int ret = -EEXIST;
1977e14ea15SSteffen Klassert 	int priority = handler->priority;
1987e14ea15SSteffen Klassert 
199edb666f0SSteffen Klassert 	if (!proto_handlers(protocol) || !netproto(protocol))
200edb666f0SSteffen Klassert 		return -EINVAL;
201edb666f0SSteffen Klassert 
2027e14ea15SSteffen Klassert 	mutex_lock(&xfrm6_protocol_mutex);
2037e14ea15SSteffen Klassert 
2047e14ea15SSteffen Klassert 	if (!rcu_dereference_protected(*proto_handlers(protocol),
2057e14ea15SSteffen Klassert 				       lockdep_is_held(&xfrm6_protocol_mutex)))
2067e14ea15SSteffen Klassert 		add_netproto = true;
2077e14ea15SSteffen Klassert 
2087e14ea15SSteffen Klassert 	for (pprev = proto_handlers(protocol);
2097e14ea15SSteffen Klassert 	     (t = rcu_dereference_protected(*pprev,
2107e14ea15SSteffen Klassert 			lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
2117e14ea15SSteffen Klassert 	     pprev = &t->next) {
2127e14ea15SSteffen Klassert 		if (t->priority < priority)
2137e14ea15SSteffen Klassert 			break;
2147e14ea15SSteffen Klassert 		if (t->priority == priority)
2157e14ea15SSteffen Klassert 			goto err;
2167e14ea15SSteffen Klassert 	}
2177e14ea15SSteffen Klassert 
2187e14ea15SSteffen Klassert 	handler->next = *pprev;
2197e14ea15SSteffen Klassert 	rcu_assign_pointer(*pprev, handler);
2207e14ea15SSteffen Klassert 
2217e14ea15SSteffen Klassert 	ret = 0;
2227e14ea15SSteffen Klassert 
2237e14ea15SSteffen Klassert err:
2247e14ea15SSteffen Klassert 	mutex_unlock(&xfrm6_protocol_mutex);
2257e14ea15SSteffen Klassert 
2267e14ea15SSteffen Klassert 	if (add_netproto) {
2277e14ea15SSteffen Klassert 		if (inet6_add_protocol(netproto(protocol), protocol)) {
2287e14ea15SSteffen Klassert 			pr_err("%s: can't add protocol\n", __func__);
2297e14ea15SSteffen Klassert 			ret = -EAGAIN;
2307e14ea15SSteffen Klassert 		}
2317e14ea15SSteffen Klassert 	}
2327e14ea15SSteffen Klassert 
2337e14ea15SSteffen Klassert 	return ret;
2347e14ea15SSteffen Klassert }
2357e14ea15SSteffen Klassert EXPORT_SYMBOL(xfrm6_protocol_register);
2367e14ea15SSteffen Klassert 
2377e14ea15SSteffen Klassert int xfrm6_protocol_deregister(struct xfrm6_protocol *handler,
2387e14ea15SSteffen Klassert 			      unsigned char protocol)
2397e14ea15SSteffen Klassert {
2407e14ea15SSteffen Klassert 	struct xfrm6_protocol __rcu **pprev;
2417e14ea15SSteffen Klassert 	struct xfrm6_protocol *t;
2427e14ea15SSteffen Klassert 	int ret = -ENOENT;
2437e14ea15SSteffen Klassert 
244edb666f0SSteffen Klassert 	if (!proto_handlers(protocol) || !netproto(protocol))
245edb666f0SSteffen Klassert 		return -EINVAL;
246edb666f0SSteffen Klassert 
2477e14ea15SSteffen Klassert 	mutex_lock(&xfrm6_protocol_mutex);
2487e14ea15SSteffen Klassert 
2497e14ea15SSteffen Klassert 	for (pprev = proto_handlers(protocol);
2507e14ea15SSteffen Klassert 	     (t = rcu_dereference_protected(*pprev,
2517e14ea15SSteffen Klassert 			lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
2527e14ea15SSteffen Klassert 	     pprev = &t->next) {
2537e14ea15SSteffen Klassert 		if (t == handler) {
2547e14ea15SSteffen Klassert 			*pprev = handler->next;
2557e14ea15SSteffen Klassert 			ret = 0;
2567e14ea15SSteffen Klassert 			break;
2577e14ea15SSteffen Klassert 		}
2587e14ea15SSteffen Klassert 	}
2597e14ea15SSteffen Klassert 
2607e14ea15SSteffen Klassert 	if (!rcu_dereference_protected(*proto_handlers(protocol),
2617e14ea15SSteffen Klassert 				       lockdep_is_held(&xfrm6_protocol_mutex))) {
2627e14ea15SSteffen Klassert 		if (inet6_del_protocol(netproto(protocol), protocol) < 0) {
2637e14ea15SSteffen Klassert 			pr_err("%s: can't remove protocol\n", __func__);
2647e14ea15SSteffen Klassert 			ret = -EAGAIN;
2657e14ea15SSteffen Klassert 		}
2667e14ea15SSteffen Klassert 	}
2677e14ea15SSteffen Klassert 
2687e14ea15SSteffen Klassert 	mutex_unlock(&xfrm6_protocol_mutex);
2697e14ea15SSteffen Klassert 
2707e14ea15SSteffen Klassert 	synchronize_net();
2717e14ea15SSteffen Klassert 
2727e14ea15SSteffen Klassert 	return ret;
2737e14ea15SSteffen Klassert }
2747e14ea15SSteffen Klassert EXPORT_SYMBOL(xfrm6_protocol_deregister);
2757e14ea15SSteffen Klassert 
2767e14ea15SSteffen Klassert int __init xfrm6_protocol_init(void)
2777e14ea15SSteffen Klassert {
2787e14ea15SSteffen Klassert 	return xfrm_input_register_afinfo(&xfrm6_input_afinfo);
2797e14ea15SSteffen Klassert }
2807e14ea15SSteffen Klassert 
2817e14ea15SSteffen Klassert void xfrm6_protocol_fini(void)
2827e14ea15SSteffen Klassert {
2837e14ea15SSteffen Klassert 	xfrm_input_unregister_afinfo(&xfrm6_input_afinfo);
2847e14ea15SSteffen Klassert }
285