Merge tag 'microblaze-3.15-rc1' of git://git.monstr.eu/linux-2.6-microblaze
[cascardo/linux.git] / net / ipv6 / xfrm6_protocol.c
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 }