diff options
-rw-r--r-- | include/net/xfrm.h | 31 | ||||
-rw-r--r-- | net/ipv4/Makefile | 2 | ||||
-rw-r--r-- | net/ipv4/xfrm4_input.c | 9 | ||||
-rw-r--r-- | net/ipv4/xfrm4_protocol.c | 268 | ||||
-rw-r--r-- | net/xfrm/xfrm_input.c | 17 |
5 files changed, 310 insertions, 17 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 45332acac022..345a15084557 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h | |||
@@ -1347,6 +1347,18 @@ struct xfrm_algo_desc { | |||
1347 | struct sadb_alg desc; | 1347 | struct sadb_alg desc; |
1348 | }; | 1348 | }; |
1349 | 1349 | ||
1350 | /* XFRM protocol handlers. */ | ||
1351 | struct xfrm4_protocol { | ||
1352 | int (*handler)(struct sk_buff *skb); | ||
1353 | int (*input_handler)(struct sk_buff *skb, int nexthdr, __be32 spi, | ||
1354 | int encap_type); | ||
1355 | int (*cb_handler)(struct sk_buff *skb, int err); | ||
1356 | int (*err_handler)(struct sk_buff *skb, u32 info); | ||
1357 | |||
1358 | struct xfrm4_protocol __rcu *next; | ||
1359 | int priority; | ||
1360 | }; | ||
1361 | |||
1350 | /* XFRM tunnel handlers. */ | 1362 | /* XFRM tunnel handlers. */ |
1351 | struct xfrm_tunnel { | 1363 | struct xfrm_tunnel { |
1352 | int (*handler)(struct sk_buff *skb); | 1364 | int (*handler)(struct sk_buff *skb); |
@@ -1498,13 +1510,18 @@ int xfrm4_rcv(struct sk_buff *skb); | |||
1498 | 1510 | ||
1499 | static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi) | 1511 | static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi) |
1500 | { | 1512 | { |
1501 | return xfrm4_rcv_encap(skb, nexthdr, spi, 0); | 1513 | XFRM_SPI_SKB_CB(skb)->family = AF_INET; |
1514 | XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); | ||
1515 | return xfrm_input(skb, nexthdr, spi, 0); | ||
1502 | } | 1516 | } |
1503 | 1517 | ||
1504 | int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb); | 1518 | int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb); |
1505 | int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb); | 1519 | int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb); |
1506 | int xfrm4_output(struct sk_buff *skb); | 1520 | int xfrm4_output(struct sk_buff *skb); |
1507 | int xfrm4_output_finish(struct sk_buff *skb); | 1521 | int xfrm4_output_finish(struct sk_buff *skb); |
1522 | int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err); | ||
1523 | int xfrm4_protocol_register(struct xfrm4_protocol *handler, unsigned char protocol); | ||
1524 | int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, unsigned char protocol); | ||
1508 | int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family); | 1525 | int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family); |
1509 | int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family); | 1526 | int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family); |
1510 | void xfrm4_local_error(struct sk_buff *skb, u32 mtu); | 1527 | void xfrm4_local_error(struct sk_buff *skb, u32 mtu); |
@@ -1752,4 +1769,16 @@ static inline int xfrm_mark_put(struct sk_buff *skb, const struct xfrm_mark *m) | |||
1752 | return ret; | 1769 | return ret; |
1753 | } | 1770 | } |
1754 | 1771 | ||
1772 | static inline int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, | ||
1773 | u8 protocol, int err) | ||
1774 | { | ||
1775 | switch(family) { | ||
1776 | #ifdef CONFIG_INET | ||
1777 | case AF_INET: | ||
1778 | return xfrm4_rcv_cb(skb, protocol, err); | ||
1779 | #endif | ||
1780 | } | ||
1781 | return 0; | ||
1782 | } | ||
1783 | |||
1755 | #endif /* _NET_XFRM_H */ | 1784 | #endif /* _NET_XFRM_H */ |
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index f8c49ce5b283..f032688d20d3 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile | |||
@@ -55,4 +55,4 @@ obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o | |||
55 | obj-$(CONFIG_NETLABEL) += cipso_ipv4.o | 55 | obj-$(CONFIG_NETLABEL) += cipso_ipv4.o |
56 | 56 | ||
57 | obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ | 57 | obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ |
58 | xfrm4_output.o | 58 | xfrm4_output.o xfrm4_protocol.o |
diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index 1f12c8b45864..aac6197b7a71 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c | |||
@@ -37,15 +37,6 @@ drop: | |||
37 | return NET_RX_DROP; | 37 | return NET_RX_DROP; |
38 | } | 38 | } |
39 | 39 | ||
40 | int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, | ||
41 | int encap_type) | ||
42 | { | ||
43 | XFRM_SPI_SKB_CB(skb)->family = AF_INET; | ||
44 | XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); | ||
45 | return xfrm_input(skb, nexthdr, spi, encap_type); | ||
46 | } | ||
47 | EXPORT_SYMBOL(xfrm4_rcv_encap); | ||
48 | |||
49 | int xfrm4_transport_finish(struct sk_buff *skb, int async) | 40 | int xfrm4_transport_finish(struct sk_buff *skb, int async) |
50 | { | 41 | { |
51 | struct iphdr *iph = ip_hdr(skb); | 42 | struct iphdr *iph = ip_hdr(skb); |
diff --git a/net/ipv4/xfrm4_protocol.c b/net/ipv4/xfrm4_protocol.c new file mode 100644 index 000000000000..862a26c2014f --- /dev/null +++ b/net/ipv4/xfrm4_protocol.c | |||
@@ -0,0 +1,268 @@ | |||
1 | /* xfrm4_protocol.c - Generic xfrm protocol multiplexer. | ||
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/tunnel4.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 <net/icmp.h> | ||
21 | #include <net/ip.h> | ||
22 | #include <net/protocol.h> | ||
23 | #include <net/xfrm.h> | ||
24 | |||
25 | static struct xfrm4_protocol __rcu *esp4_handlers __read_mostly; | ||
26 | static struct xfrm4_protocol __rcu *ah4_handlers __read_mostly; | ||
27 | static struct xfrm4_protocol __rcu *ipcomp4_handlers __read_mostly; | ||
28 | static DEFINE_MUTEX(xfrm4_protocol_mutex); | ||
29 | |||
30 | static inline struct xfrm4_protocol __rcu **proto_handlers(u8 protocol) | ||
31 | { | ||
32 | switch (protocol) { | ||
33 | case IPPROTO_ESP: | ||
34 | return &esp4_handlers; | ||
35 | case IPPROTO_AH: | ||
36 | return &ah4_handlers; | ||
37 | case IPPROTO_COMP: | ||
38 | return &ipcomp4_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 xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err) | ||
50 | { | ||
51 | int ret; | ||
52 | struct xfrm4_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(xfrm4_rcv_cb); | ||
61 | |||
62 | int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, | ||
63 | int encap_type) | ||
64 | { | ||
65 | int ret; | ||
66 | struct xfrm4_protocol *handler; | ||
67 | |||
68 | XFRM_SPI_SKB_CB(skb)->family = AF_INET; | ||
69 | XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); | ||
70 | |||
71 | for_each_protocol_rcu(*proto_handlers(nexthdr), handler) | ||
72 | if ((ret = handler->input_handler(skb, nexthdr, spi, encap_type)) != -EINVAL) | ||
73 | return ret; | ||
74 | |||
75 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); | ||
76 | |||
77 | kfree_skb(skb); | ||
78 | return 0; | ||
79 | } | ||
80 | EXPORT_SYMBOL(xfrm4_rcv_encap); | ||
81 | |||
82 | static int xfrm4_esp_rcv(struct sk_buff *skb) | ||
83 | { | ||
84 | int ret; | ||
85 | struct xfrm4_protocol *handler; | ||
86 | |||
87 | for_each_protocol_rcu(esp4_handlers, handler) | ||
88 | if ((ret = handler->handler(skb)) != -EINVAL) | ||
89 | return ret; | ||
90 | |||
91 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); | ||
92 | |||
93 | kfree_skb(skb); | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static void xfrm4_esp_err(struct sk_buff *skb, u32 info) | ||
98 | { | ||
99 | struct xfrm4_protocol *handler; | ||
100 | |||
101 | for_each_protocol_rcu(esp4_handlers, handler) | ||
102 | if (!handler->err_handler(skb, info)) | ||
103 | break; | ||
104 | } | ||
105 | |||
106 | static int xfrm4_ah_rcv(struct sk_buff *skb) | ||
107 | { | ||
108 | int ret; | ||
109 | struct xfrm4_protocol *handler; | ||
110 | |||
111 | for_each_protocol_rcu(ah4_handlers, handler) | ||
112 | if ((ret = handler->handler(skb)) != -EINVAL) | ||
113 | return ret;; | ||
114 | |||
115 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); | ||
116 | |||
117 | kfree_skb(skb); | ||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | static void xfrm4_ah_err(struct sk_buff *skb, u32 info) | ||
122 | { | ||
123 | struct xfrm4_protocol *handler; | ||
124 | |||
125 | for_each_protocol_rcu(ah4_handlers, handler) | ||
126 | if (!handler->err_handler(skb, info)) | ||
127 | break; | ||
128 | } | ||
129 | |||
130 | static int xfrm4_ipcomp_rcv(struct sk_buff *skb) | ||
131 | { | ||
132 | int ret; | ||
133 | struct xfrm4_protocol *handler; | ||
134 | |||
135 | for_each_protocol_rcu(ipcomp4_handlers, handler) | ||
136 | if ((ret = handler->handler(skb)) != -EINVAL) | ||
137 | return ret; | ||
138 | |||
139 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); | ||
140 | |||
141 | kfree_skb(skb); | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | static void xfrm4_ipcomp_err(struct sk_buff *skb, u32 info) | ||
146 | { | ||
147 | struct xfrm4_protocol *handler; | ||
148 | |||
149 | for_each_protocol_rcu(ipcomp4_handlers, handler) | ||
150 | if (!handler->err_handler(skb, info)) | ||
151 | break; | ||
152 | } | ||
153 | |||
154 | static const struct net_protocol esp4_protocol = { | ||
155 | .handler = xfrm4_esp_rcv, | ||
156 | .err_handler = xfrm4_esp_err, | ||
157 | .no_policy = 1, | ||
158 | .netns_ok = 1, | ||
159 | }; | ||
160 | |||
161 | static const struct net_protocol ah4_protocol = { | ||
162 | .handler = xfrm4_ah_rcv, | ||
163 | .err_handler = xfrm4_ah_err, | ||
164 | .no_policy = 1, | ||
165 | .netns_ok = 1, | ||
166 | }; | ||
167 | |||
168 | static const struct net_protocol ipcomp4_protocol = { | ||
169 | .handler = xfrm4_ipcomp_rcv, | ||
170 | .err_handler = xfrm4_ipcomp_err, | ||
171 | .no_policy = 1, | ||
172 | .netns_ok = 1, | ||
173 | }; | ||
174 | |||
175 | static inline const struct net_protocol *netproto(unsigned char protocol) | ||
176 | { | ||
177 | switch (protocol) { | ||
178 | case IPPROTO_ESP: | ||
179 | return &esp4_protocol; | ||
180 | case IPPROTO_AH: | ||
181 | return &ah4_protocol; | ||
182 | case IPPROTO_COMP: | ||
183 | return &ipcomp4_protocol; | ||
184 | } | ||
185 | |||
186 | return NULL; | ||
187 | } | ||
188 | |||
189 | int xfrm4_protocol_register(struct xfrm4_protocol *handler, | ||
190 | unsigned char protocol) | ||
191 | { | ||
192 | struct xfrm4_protocol __rcu **pprev; | ||
193 | struct xfrm4_protocol *t; | ||
194 | bool add_netproto = false; | ||
195 | |||
196 | int ret = -EEXIST; | ||
197 | int priority = handler->priority; | ||
198 | |||
199 | mutex_lock(&xfrm4_protocol_mutex); | ||
200 | |||
201 | if (!rcu_dereference_protected(*proto_handlers(protocol), | ||
202 | lockdep_is_held(&xfrm4_protocol_mutex))) | ||
203 | add_netproto = true; | ||
204 | |||
205 | for (pprev = proto_handlers(protocol); | ||
206 | (t = rcu_dereference_protected(*pprev, | ||
207 | lockdep_is_held(&xfrm4_protocol_mutex))) != NULL; | ||
208 | pprev = &t->next) { | ||
209 | if (t->priority < priority) | ||
210 | break; | ||
211 | if (t->priority == priority) | ||
212 | goto err; | ||
213 | } | ||
214 | |||
215 | handler->next = *pprev; | ||
216 | rcu_assign_pointer(*pprev, handler); | ||
217 | |||
218 | ret = 0; | ||
219 | |||
220 | err: | ||
221 | mutex_unlock(&xfrm4_protocol_mutex); | ||
222 | |||
223 | if (add_netproto) { | ||
224 | if (inet_add_protocol(netproto(protocol), protocol)) { | ||
225 | pr_err("%s: can't add protocol\n", __func__); | ||
226 | ret = -EAGAIN; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | return ret; | ||
231 | } | ||
232 | EXPORT_SYMBOL(xfrm4_protocol_register); | ||
233 | |||
234 | int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, | ||
235 | unsigned char protocol) | ||
236 | { | ||
237 | struct xfrm4_protocol __rcu **pprev; | ||
238 | struct xfrm4_protocol *t; | ||
239 | int ret = -ENOENT; | ||
240 | |||
241 | mutex_lock(&xfrm4_protocol_mutex); | ||
242 | |||
243 | for (pprev = proto_handlers(protocol); | ||
244 | (t = rcu_dereference_protected(*pprev, | ||
245 | lockdep_is_held(&xfrm4_protocol_mutex))) != NULL; | ||
246 | pprev = &t->next) { | ||
247 | if (t == handler) { | ||
248 | *pprev = handler->next; | ||
249 | ret = 0; | ||
250 | break; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | if (!rcu_dereference_protected(*proto_handlers(protocol), | ||
255 | lockdep_is_held(&xfrm4_protocol_mutex))) { | ||
256 | if (inet_del_protocol(netproto(protocol), protocol) < 0) { | ||
257 | pr_err("%s: can't remove protocol\n", __func__); | ||
258 | ret = -EAGAIN; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | mutex_unlock(&xfrm4_protocol_mutex); | ||
263 | |||
264 | synchronize_net(); | ||
265 | |||
266 | return ret; | ||
267 | } | ||
268 | EXPORT_SYMBOL(xfrm4_protocol_deregister); | ||
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 6c7ac016ce3a..99e3a9e5285e 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c | |||
@@ -108,7 +108,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) | |||
108 | int err; | 108 | int err; |
109 | __be32 seq; | 109 | __be32 seq; |
110 | __be32 seq_hi; | 110 | __be32 seq_hi; |
111 | struct xfrm_state *x; | 111 | struct xfrm_state *x = NULL; |
112 | xfrm_address_t *daddr; | 112 | xfrm_address_t *daddr; |
113 | struct xfrm_mode *inner_mode; | 113 | struct xfrm_mode *inner_mode; |
114 | unsigned int family; | 114 | unsigned int family; |
@@ -120,9 +120,14 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) | |||
120 | async = 1; | 120 | async = 1; |
121 | x = xfrm_input_state(skb); | 121 | x = xfrm_input_state(skb); |
122 | seq = XFRM_SKB_CB(skb)->seq.input.low; | 122 | seq = XFRM_SKB_CB(skb)->seq.input.low; |
123 | family = x->outer_mode->afinfo->family; | ||
123 | goto resume; | 124 | goto resume; |
124 | } | 125 | } |
125 | 126 | ||
127 | daddr = (xfrm_address_t *)(skb_network_header(skb) + | ||
128 | XFRM_SPI_SKB_CB(skb)->daddroff); | ||
129 | family = XFRM_SPI_SKB_CB(skb)->family; | ||
130 | |||
126 | /* Allocate new secpath or COW existing one. */ | 131 | /* Allocate new secpath or COW existing one. */ |
127 | if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { | 132 | if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { |
128 | struct sec_path *sp; | 133 | struct sec_path *sp; |
@@ -137,10 +142,6 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) | |||
137 | skb->sp = sp; | 142 | skb->sp = sp; |
138 | } | 143 | } |
139 | 144 | ||
140 | daddr = (xfrm_address_t *)(skb_network_header(skb) + | ||
141 | XFRM_SPI_SKB_CB(skb)->daddroff); | ||
142 | family = XFRM_SPI_SKB_CB(skb)->family; | ||
143 | |||
144 | seq = 0; | 145 | seq = 0; |
145 | if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) { | 146 | if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) { |
146 | XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR); | 147 | XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR); |
@@ -201,7 +202,6 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) | |||
201 | 202 | ||
202 | if (nexthdr == -EINPROGRESS) | 203 | if (nexthdr == -EINPROGRESS) |
203 | return 0; | 204 | return 0; |
204 | |||
205 | resume: | 205 | resume: |
206 | spin_lock(&x->lock); | 206 | spin_lock(&x->lock); |
207 | if (nexthdr <= 0) { | 207 | if (nexthdr <= 0) { |
@@ -263,6 +263,10 @@ resume: | |||
263 | } | 263 | } |
264 | } while (!err); | 264 | } while (!err); |
265 | 265 | ||
266 | err = xfrm_rcv_cb(skb, family, x->type->proto, 0); | ||
267 | if (err) | ||
268 | goto drop; | ||
269 | |||
266 | nf_reset(skb); | 270 | nf_reset(skb); |
267 | 271 | ||
268 | if (decaps) { | 272 | if (decaps) { |
@@ -276,6 +280,7 @@ resume: | |||
276 | drop_unlock: | 280 | drop_unlock: |
277 | spin_unlock(&x->lock); | 281 | spin_unlock(&x->lock); |
278 | drop: | 282 | drop: |
283 | xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1); | ||
279 | kfree_skb(skb); | 284 | kfree_skb(skb); |
280 | return 0; | 285 | return 0; |
281 | } | 286 | } |