xref: /openbmc/linux/net/ipv6/xfrm6_protocol.c (revision b34e08d5)
1 /* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6.
2  *
3  * Copyright (C) 2013 secunet Security Networks AG
4  *
5  * Author:
6  * Steffen Klassert <steffen.klassert@secunet.com>
7  *
8  * Based on:
9  * net/ipv4/xfrm4_protocol.c
10  *
11  *	This program is free software; you can redistribute it and/or
12  *	modify it under the terms of the GNU General Public License
13  *	as published by the Free Software Foundation; either version
14  *	2 of the License, or (at your option) any later version.
15  */
16 
17 #include <linux/init.h>
18 #include <linux/mutex.h>
19 #include <linux/skbuff.h>
20 #include <linux/icmpv6.h>
21 #include <net/ipv6.h>
22 #include <net/protocol.h>
23 #include <net/xfrm.h>
24 
25 static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly;
26 static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly;
27 static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly;
28 static DEFINE_MUTEX(xfrm6_protocol_mutex);
29 
30 static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol)
31 {
32 	switch (protocol) {
33 	case IPPROTO_ESP:
34 		return &esp6_handlers;
35 	case IPPROTO_AH:
36 		return &ah6_handlers;
37 	case IPPROTO_COMP:
38 		return &ipcomp6_handlers;
39 	}
40 
41 	return NULL;
42 }
43 
44 #define for_each_protocol_rcu(head, handler)		\
45 	for (handler = rcu_dereference(head);		\
46 	     handler != NULL;				\
47 	     handler = rcu_dereference(handler->next))	\
48 
49 int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err)
50 {
51 	int ret;
52 	struct xfrm6_protocol *handler;
53 
54 	for_each_protocol_rcu(*proto_handlers(protocol), handler)
55 		if ((ret = handler->cb_handler(skb, err)) <= 0)
56 			return ret;
57 
58 	return 0;
59 }
60 EXPORT_SYMBOL(xfrm6_rcv_cb);
61 
62 static int xfrm6_esp_rcv(struct sk_buff *skb)
63 {
64 	int ret;
65 	struct xfrm6_protocol *handler;
66 
67 	XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
68 
69 	for_each_protocol_rcu(esp6_handlers, handler)
70 		if ((ret = handler->handler(skb)) != -EINVAL)
71 			return ret;
72 
73 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
74 
75 	kfree_skb(skb);
76 	return 0;
77 }
78 
79 static void xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
80 			  u8 type, u8 code, int offset, __be32 info)
81 {
82 	struct xfrm6_protocol *handler;
83 
84 	for_each_protocol_rcu(esp6_handlers, handler)
85 		if (!handler->err_handler(skb, opt, type, code, offset, info))
86 			break;
87 }
88 
89 static int xfrm6_ah_rcv(struct sk_buff *skb)
90 {
91 	int ret;
92 	struct xfrm6_protocol *handler;
93 
94 	XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
95 
96 	for_each_protocol_rcu(ah6_handlers, handler)
97 		if ((ret = handler->handler(skb)) != -EINVAL)
98 			return ret;
99 
100 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
101 
102 	kfree_skb(skb);
103 	return 0;
104 }
105 
106 static void xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
107 			 u8 type, u8 code, int offset, __be32 info)
108 {
109 	struct xfrm6_protocol *handler;
110 
111 	for_each_protocol_rcu(ah6_handlers, handler)
112 		if (!handler->err_handler(skb, opt, type, code, offset, info))
113 			break;
114 }
115 
116 static int xfrm6_ipcomp_rcv(struct sk_buff *skb)
117 {
118 	int ret;
119 	struct xfrm6_protocol *handler;
120 
121 	XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
122 
123 	for_each_protocol_rcu(ipcomp6_handlers, handler)
124 		if ((ret = handler->handler(skb)) != -EINVAL)
125 			return ret;
126 
127 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
128 
129 	kfree_skb(skb);
130 	return 0;
131 }
132 
133 static void xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
134 			     u8 type, u8 code, int offset, __be32 info)
135 {
136 	struct xfrm6_protocol *handler;
137 
138 	for_each_protocol_rcu(ipcomp6_handlers, handler)
139 		if (!handler->err_handler(skb, opt, type, code, offset, info))
140 			break;
141 }
142 
143 static const struct inet6_protocol esp6_protocol = {
144 	.handler	=	xfrm6_esp_rcv,
145 	.err_handler	=	xfrm6_esp_err,
146 	.flags		=	INET6_PROTO_NOPOLICY,
147 };
148 
149 static const struct inet6_protocol ah6_protocol = {
150 	.handler	=	xfrm6_ah_rcv,
151 	.err_handler	=	xfrm6_ah_err,
152 	.flags		=	INET6_PROTO_NOPOLICY,
153 };
154 
155 static const struct inet6_protocol ipcomp6_protocol = {
156 	.handler	=	xfrm6_ipcomp_rcv,
157 	.err_handler	=	xfrm6_ipcomp_err,
158 	.flags		=	INET6_PROTO_NOPOLICY,
159 };
160 
161 static struct xfrm_input_afinfo xfrm6_input_afinfo = {
162 	.family		=	AF_INET6,
163 	.owner		=	THIS_MODULE,
164 	.callback	=	xfrm6_rcv_cb,
165 };
166 
167 static inline const struct inet6_protocol *netproto(unsigned char protocol)
168 {
169 	switch (protocol) {
170 	case IPPROTO_ESP:
171 		return &esp6_protocol;
172 	case IPPROTO_AH:
173 		return &ah6_protocol;
174 	case IPPROTO_COMP:
175 		return &ipcomp6_protocol;
176 	}
177 
178 	return NULL;
179 }
180 
181 int xfrm6_protocol_register(struct xfrm6_protocol *handler,
182 			    unsigned char protocol)
183 {
184 	struct xfrm6_protocol __rcu **pprev;
185 	struct xfrm6_protocol *t;
186 	bool add_netproto = false;
187 
188 	int ret = -EEXIST;
189 	int priority = handler->priority;
190 
191 	mutex_lock(&xfrm6_protocol_mutex);
192 
193 	if (!rcu_dereference_protected(*proto_handlers(protocol),
194 				       lockdep_is_held(&xfrm6_protocol_mutex)))
195 		add_netproto = true;
196 
197 	for (pprev = proto_handlers(protocol);
198 	     (t = rcu_dereference_protected(*pprev,
199 			lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
200 	     pprev = &t->next) {
201 		if (t->priority < priority)
202 			break;
203 		if (t->priority == priority)
204 			goto err;
205 	}
206 
207 	handler->next = *pprev;
208 	rcu_assign_pointer(*pprev, handler);
209 
210 	ret = 0;
211 
212 err:
213 	mutex_unlock(&xfrm6_protocol_mutex);
214 
215 	if (add_netproto) {
216 		if (inet6_add_protocol(netproto(protocol), protocol)) {
217 			pr_err("%s: can't add protocol\n", __func__);
218 			ret = -EAGAIN;
219 		}
220 	}
221 
222 	return ret;
223 }
224 EXPORT_SYMBOL(xfrm6_protocol_register);
225 
226 int xfrm6_protocol_deregister(struct xfrm6_protocol *handler,
227 			      unsigned char protocol)
228 {
229 	struct xfrm6_protocol __rcu **pprev;
230 	struct xfrm6_protocol *t;
231 	int ret = -ENOENT;
232 
233 	mutex_lock(&xfrm6_protocol_mutex);
234 
235 	for (pprev = proto_handlers(protocol);
236 	     (t = rcu_dereference_protected(*pprev,
237 			lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
238 	     pprev = &t->next) {
239 		if (t == handler) {
240 			*pprev = handler->next;
241 			ret = 0;
242 			break;
243 		}
244 	}
245 
246 	if (!rcu_dereference_protected(*proto_handlers(protocol),
247 				       lockdep_is_held(&xfrm6_protocol_mutex))) {
248 		if (inet6_del_protocol(netproto(protocol), protocol) < 0) {
249 			pr_err("%s: can't remove protocol\n", __func__);
250 			ret = -EAGAIN;
251 		}
252 	}
253 
254 	mutex_unlock(&xfrm6_protocol_mutex);
255 
256 	synchronize_net();
257 
258 	return ret;
259 }
260 EXPORT_SYMBOL(xfrm6_protocol_deregister);
261 
262 int __init xfrm6_protocol_init(void)
263 {
264 	return xfrm_input_register_afinfo(&xfrm6_input_afinfo);
265 }
266 
267 void xfrm6_protocol_fini(void)
268 {
269 	xfrm_input_unregister_afinfo(&xfrm6_input_afinfo);
270 }
271