aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/xfrm6_tunnel.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/xfrm6_tunnel.c')
-rw-r--r--net/ipv6/xfrm6_tunnel.c234
1 files changed, 130 insertions, 104 deletions
diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
index 81a95c00e503..2ce3a8278f26 100644
--- a/net/ipv6/xfrm6_tunnel.c
+++ b/net/ipv6/xfrm6_tunnel.c
@@ -23,41 +23,51 @@
23 */ 23 */
24#include <linux/module.h> 24#include <linux/module.h>
25#include <linux/xfrm.h> 25#include <linux/xfrm.h>
26#include <linux/list.h> 26#include <linux/slab.h>
27#include <linux/rculist.h>
27#include <net/ip.h> 28#include <net/ip.h>
28#include <net/xfrm.h> 29#include <net/xfrm.h>
29#include <net/ipv6.h> 30#include <net/ipv6.h>
30#include <linux/ipv6.h> 31#include <linux/ipv6.h>
31#include <linux/icmpv6.h> 32#include <linux/icmpv6.h>
32#include <linux/mutex.h> 33#include <linux/mutex.h>
34#include <net/netns/generic.h>
35
36#define XFRM6_TUNNEL_SPI_BYADDR_HSIZE 256
37#define XFRM6_TUNNEL_SPI_BYSPI_HSIZE 256
38
39#define XFRM6_TUNNEL_SPI_MIN 1
40#define XFRM6_TUNNEL_SPI_MAX 0xffffffff
41
42struct xfrm6_tunnel_net {
43 struct hlist_head spi_byaddr[XFRM6_TUNNEL_SPI_BYADDR_HSIZE];
44 struct hlist_head spi_byspi[XFRM6_TUNNEL_SPI_BYSPI_HSIZE];
45 u32 spi;
46};
47
48static int xfrm6_tunnel_net_id __read_mostly;
49static inline struct xfrm6_tunnel_net *xfrm6_tunnel_pernet(struct net *net)
50{
51 return net_generic(net, xfrm6_tunnel_net_id);
52}
33 53
34/* 54/*
35 * xfrm_tunnel_spi things are for allocating unique id ("spi") 55 * xfrm_tunnel_spi things are for allocating unique id ("spi")
36 * per xfrm_address_t. 56 * per xfrm_address_t.
37 */ 57 */
38struct xfrm6_tunnel_spi { 58struct xfrm6_tunnel_spi {
39 struct hlist_node list_byaddr; 59 struct hlist_node list_byaddr;
40 struct hlist_node list_byspi; 60 struct hlist_node list_byspi;
41 xfrm_address_t addr; 61 xfrm_address_t addr;
42 u32 spi; 62 u32 spi;
43 atomic_t refcnt; 63 atomic_t refcnt;
64 struct rcu_head rcu_head;
44}; 65};
45 66
46static DEFINE_RWLOCK(xfrm6_tunnel_spi_lock); 67static DEFINE_SPINLOCK(xfrm6_tunnel_spi_lock);
47
48static u32 xfrm6_tunnel_spi;
49
50#define XFRM6_TUNNEL_SPI_MIN 1
51#define XFRM6_TUNNEL_SPI_MAX 0xffffffff
52 68
53static struct kmem_cache *xfrm6_tunnel_spi_kmem __read_mostly; 69static struct kmem_cache *xfrm6_tunnel_spi_kmem __read_mostly;
54 70
55#define XFRM6_TUNNEL_SPI_BYADDR_HSIZE 256
56#define XFRM6_TUNNEL_SPI_BYSPI_HSIZE 256
57
58static struct hlist_head xfrm6_tunnel_spi_byaddr[XFRM6_TUNNEL_SPI_BYADDR_HSIZE];
59static struct hlist_head xfrm6_tunnel_spi_byspi[XFRM6_TUNNEL_SPI_BYSPI_HSIZE];
60
61static inline unsigned xfrm6_tunnel_spi_hash_byaddr(xfrm_address_t *addr) 71static inline unsigned xfrm6_tunnel_spi_hash_byaddr(xfrm_address_t *addr)
62{ 72{
63 unsigned h; 73 unsigned h;
@@ -75,49 +85,14 @@ static inline unsigned xfrm6_tunnel_spi_hash_byspi(u32 spi)
75 return spi % XFRM6_TUNNEL_SPI_BYSPI_HSIZE; 85 return spi % XFRM6_TUNNEL_SPI_BYSPI_HSIZE;
76} 86}
77 87
78 88static struct xfrm6_tunnel_spi *__xfrm6_tunnel_spi_lookup(struct net *net, xfrm_address_t *saddr)
79static int xfrm6_tunnel_spi_init(void)
80{
81 int i;
82
83 xfrm6_tunnel_spi = 0;
84 xfrm6_tunnel_spi_kmem = kmem_cache_create("xfrm6_tunnel_spi",
85 sizeof(struct xfrm6_tunnel_spi),
86 0, SLAB_HWCACHE_ALIGN,
87 NULL);
88 if (!xfrm6_tunnel_spi_kmem)
89 return -ENOMEM;
90
91 for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
92 INIT_HLIST_HEAD(&xfrm6_tunnel_spi_byaddr[i]);
93 for (i = 0; i < XFRM6_TUNNEL_SPI_BYSPI_HSIZE; i++)
94 INIT_HLIST_HEAD(&xfrm6_tunnel_spi_byspi[i]);
95 return 0;
96}
97
98static void xfrm6_tunnel_spi_fini(void)
99{
100 int i;
101
102 for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++) {
103 if (!hlist_empty(&xfrm6_tunnel_spi_byaddr[i]))
104 return;
105 }
106 for (i = 0; i < XFRM6_TUNNEL_SPI_BYSPI_HSIZE; i++) {
107 if (!hlist_empty(&xfrm6_tunnel_spi_byspi[i]))
108 return;
109 }
110 kmem_cache_destroy(xfrm6_tunnel_spi_kmem);
111 xfrm6_tunnel_spi_kmem = NULL;
112}
113
114static struct xfrm6_tunnel_spi *__xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr)
115{ 89{
90 struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
116 struct xfrm6_tunnel_spi *x6spi; 91 struct xfrm6_tunnel_spi *x6spi;
117 struct hlist_node *pos; 92 struct hlist_node *pos;
118 93
119 hlist_for_each_entry(x6spi, pos, 94 hlist_for_each_entry_rcu(x6spi, pos,
120 &xfrm6_tunnel_spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)], 95 &xfrm6_tn->spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)],
121 list_byaddr) { 96 list_byaddr) {
122 if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) 97 if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0)
123 return x6spi; 98 return x6spi;
@@ -126,28 +101,29 @@ static struct xfrm6_tunnel_spi *__xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr)
126 return NULL; 101 return NULL;
127} 102}
128 103
129__be32 xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr) 104__be32 xfrm6_tunnel_spi_lookup(struct net *net, xfrm_address_t *saddr)
130{ 105{
131 struct xfrm6_tunnel_spi *x6spi; 106 struct xfrm6_tunnel_spi *x6spi;
132 u32 spi; 107 u32 spi;
133 108
134 read_lock_bh(&xfrm6_tunnel_spi_lock); 109 rcu_read_lock_bh();
135 x6spi = __xfrm6_tunnel_spi_lookup(saddr); 110 x6spi = __xfrm6_tunnel_spi_lookup(net, saddr);
136 spi = x6spi ? x6spi->spi : 0; 111 spi = x6spi ? x6spi->spi : 0;
137 read_unlock_bh(&xfrm6_tunnel_spi_lock); 112 rcu_read_unlock_bh();
138 return htonl(spi); 113 return htonl(spi);
139} 114}
140 115
141EXPORT_SYMBOL(xfrm6_tunnel_spi_lookup); 116EXPORT_SYMBOL(xfrm6_tunnel_spi_lookup);
142 117
143static int __xfrm6_tunnel_spi_check(u32 spi) 118static int __xfrm6_tunnel_spi_check(struct net *net, u32 spi)
144{ 119{
120 struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
145 struct xfrm6_tunnel_spi *x6spi; 121 struct xfrm6_tunnel_spi *x6spi;
146 int index = xfrm6_tunnel_spi_hash_byspi(spi); 122 int index = xfrm6_tunnel_spi_hash_byspi(spi);
147 struct hlist_node *pos; 123 struct hlist_node *pos;
148 124
149 hlist_for_each_entry(x6spi, pos, 125 hlist_for_each_entry(x6spi, pos,
150 &xfrm6_tunnel_spi_byspi[index], 126 &xfrm6_tn->spi_byspi[index],
151 list_byspi) { 127 list_byspi) {
152 if (x6spi->spi == spi) 128 if (x6spi->spi == spi)
153 return -1; 129 return -1;
@@ -155,32 +131,33 @@ static int __xfrm6_tunnel_spi_check(u32 spi)
155 return index; 131 return index;
156} 132}
157 133
158static u32 __xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr) 134static u32 __xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr)
159{ 135{
136 struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
160 u32 spi; 137 u32 spi;
161 struct xfrm6_tunnel_spi *x6spi; 138 struct xfrm6_tunnel_spi *x6spi;
162 int index; 139 int index;
163 140
164 if (xfrm6_tunnel_spi < XFRM6_TUNNEL_SPI_MIN || 141 if (xfrm6_tn->spi < XFRM6_TUNNEL_SPI_MIN ||
165 xfrm6_tunnel_spi >= XFRM6_TUNNEL_SPI_MAX) 142 xfrm6_tn->spi >= XFRM6_TUNNEL_SPI_MAX)
166 xfrm6_tunnel_spi = XFRM6_TUNNEL_SPI_MIN; 143 xfrm6_tn->spi = XFRM6_TUNNEL_SPI_MIN;
167 else 144 else
168 xfrm6_tunnel_spi++; 145 xfrm6_tn->spi++;
169 146
170 for (spi = xfrm6_tunnel_spi; spi <= XFRM6_TUNNEL_SPI_MAX; spi++) { 147 for (spi = xfrm6_tn->spi; spi <= XFRM6_TUNNEL_SPI_MAX; spi++) {
171 index = __xfrm6_tunnel_spi_check(spi); 148 index = __xfrm6_tunnel_spi_check(net, spi);
172 if (index >= 0) 149 if (index >= 0)
173 goto alloc_spi; 150 goto alloc_spi;
174 } 151 }
175 for (spi = XFRM6_TUNNEL_SPI_MIN; spi < xfrm6_tunnel_spi; spi++) { 152 for (spi = XFRM6_TUNNEL_SPI_MIN; spi < xfrm6_tn->spi; spi++) {
176 index = __xfrm6_tunnel_spi_check(spi); 153 index = __xfrm6_tunnel_spi_check(net, spi);
177 if (index >= 0) 154 if (index >= 0)
178 goto alloc_spi; 155 goto alloc_spi;
179 } 156 }
180 spi = 0; 157 spi = 0;
181 goto out; 158 goto out;
182alloc_spi: 159alloc_spi:
183 xfrm6_tunnel_spi = spi; 160 xfrm6_tn->spi = spi;
184 x6spi = kmem_cache_alloc(xfrm6_tunnel_spi_kmem, GFP_ATOMIC); 161 x6spi = kmem_cache_alloc(xfrm6_tunnel_spi_kmem, GFP_ATOMIC);
185 if (!x6spi) 162 if (!x6spi)
186 goto out; 163 goto out;
@@ -189,54 +166,61 @@ alloc_spi:
189 x6spi->spi = spi; 166 x6spi->spi = spi;
190 atomic_set(&x6spi->refcnt, 1); 167 atomic_set(&x6spi->refcnt, 1);
191 168
192 hlist_add_head(&x6spi->list_byspi, &xfrm6_tunnel_spi_byspi[index]); 169 hlist_add_head_rcu(&x6spi->list_byspi, &xfrm6_tn->spi_byspi[index]);
193 170
194 index = xfrm6_tunnel_spi_hash_byaddr(saddr); 171 index = xfrm6_tunnel_spi_hash_byaddr(saddr);
195 hlist_add_head(&x6spi->list_byaddr, &xfrm6_tunnel_spi_byaddr[index]); 172 hlist_add_head_rcu(&x6spi->list_byaddr, &xfrm6_tn->spi_byaddr[index]);
196out: 173out:
197 return spi; 174 return spi;
198} 175}
199 176
200__be32 xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr) 177__be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr)
201{ 178{
202 struct xfrm6_tunnel_spi *x6spi; 179 struct xfrm6_tunnel_spi *x6spi;
203 u32 spi; 180 u32 spi;
204 181
205 write_lock_bh(&xfrm6_tunnel_spi_lock); 182 spin_lock_bh(&xfrm6_tunnel_spi_lock);
206 x6spi = __xfrm6_tunnel_spi_lookup(saddr); 183 x6spi = __xfrm6_tunnel_spi_lookup(net, saddr);
207 if (x6spi) { 184 if (x6spi) {
208 atomic_inc(&x6spi->refcnt); 185 atomic_inc(&x6spi->refcnt);
209 spi = x6spi->spi; 186 spi = x6spi->spi;
210 } else 187 } else
211 spi = __xfrm6_tunnel_alloc_spi(saddr); 188 spi = __xfrm6_tunnel_alloc_spi(net, saddr);
212 write_unlock_bh(&xfrm6_tunnel_spi_lock); 189 spin_unlock_bh(&xfrm6_tunnel_spi_lock);
213 190
214 return htonl(spi); 191 return htonl(spi);
215} 192}
216 193
217EXPORT_SYMBOL(xfrm6_tunnel_alloc_spi); 194EXPORT_SYMBOL(xfrm6_tunnel_alloc_spi);
218 195
219void xfrm6_tunnel_free_spi(xfrm_address_t *saddr) 196static void x6spi_destroy_rcu(struct rcu_head *head)
197{
198 kmem_cache_free(xfrm6_tunnel_spi_kmem,
199 container_of(head, struct xfrm6_tunnel_spi, rcu_head));
200}
201
202void xfrm6_tunnel_free_spi(struct net *net, xfrm_address_t *saddr)
220{ 203{
204 struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
221 struct xfrm6_tunnel_spi *x6spi; 205 struct xfrm6_tunnel_spi *x6spi;
222 struct hlist_node *pos, *n; 206 struct hlist_node *pos, *n;
223 207
224 write_lock_bh(&xfrm6_tunnel_spi_lock); 208 spin_lock_bh(&xfrm6_tunnel_spi_lock);
225 209
226 hlist_for_each_entry_safe(x6spi, pos, n, 210 hlist_for_each_entry_safe(x6spi, pos, n,
227 &xfrm6_tunnel_spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)], 211 &xfrm6_tn->spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)],
228 list_byaddr) 212 list_byaddr)
229 { 213 {
230 if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) { 214 if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) {
231 if (atomic_dec_and_test(&x6spi->refcnt)) { 215 if (atomic_dec_and_test(&x6spi->refcnt)) {
232 hlist_del(&x6spi->list_byaddr); 216 hlist_del_rcu(&x6spi->list_byaddr);
233 hlist_del(&x6spi->list_byspi); 217 hlist_del_rcu(&x6spi->list_byspi);
234 kmem_cache_free(xfrm6_tunnel_spi_kmem, x6spi); 218 call_rcu(&x6spi->rcu_head, x6spi_destroy_rcu);
235 break; 219 break;
236 } 220 }
237 } 221 }
238 } 222 }
239 write_unlock_bh(&xfrm6_tunnel_spi_lock); 223 spin_unlock_bh(&xfrm6_tunnel_spi_lock);
240} 224}
241 225
242EXPORT_SYMBOL(xfrm6_tunnel_free_spi); 226EXPORT_SYMBOL(xfrm6_tunnel_free_spi);
@@ -254,10 +238,11 @@ static int xfrm6_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
254 238
255static int xfrm6_tunnel_rcv(struct sk_buff *skb) 239static int xfrm6_tunnel_rcv(struct sk_buff *skb)
256{ 240{
241 struct net *net = dev_net(skb->dev);
257 struct ipv6hdr *iph = ipv6_hdr(skb); 242 struct ipv6hdr *iph = ipv6_hdr(skb);
258 __be32 spi; 243 __be32 spi;
259 244
260 spi = xfrm6_tunnel_spi_lookup((xfrm_address_t *)&iph->saddr); 245 spi = xfrm6_tunnel_spi_lookup(net, (xfrm_address_t *)&iph->saddr);
261 return xfrm6_rcv_spi(skb, IPPROTO_IPV6, spi) > 0 ? : 0; 246 return xfrm6_rcv_spi(skb, IPPROTO_IPV6, spi) > 0 ? : 0;
262} 247}
263 248
@@ -317,7 +302,9 @@ static int xfrm6_tunnel_init_state(struct xfrm_state *x)
317 302
318static void xfrm6_tunnel_destroy(struct xfrm_state *x) 303static void xfrm6_tunnel_destroy(struct xfrm_state *x)
319{ 304{
320 xfrm6_tunnel_free_spi((xfrm_address_t *)&x->props.saddr); 305 struct net *net = xs_net(x);
306
307 xfrm6_tunnel_free_spi(net, (xfrm_address_t *)&x->props.saddr);
321} 308}
322 309
323static const struct xfrm_type xfrm6_tunnel_type = { 310static const struct xfrm_type xfrm6_tunnel_type = {
@@ -342,34 +329,73 @@ static struct xfrm6_tunnel xfrm46_tunnel_handler = {
342 .priority = 2, 329 .priority = 2,
343}; 330};
344 331
332static int __net_init xfrm6_tunnel_net_init(struct net *net)
333{
334 struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
335 unsigned int i;
336
337 for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
338 INIT_HLIST_HEAD(&xfrm6_tn->spi_byaddr[i]);
339 for (i = 0; i < XFRM6_TUNNEL_SPI_BYSPI_HSIZE; i++)
340 INIT_HLIST_HEAD(&xfrm6_tn->spi_byspi[i]);
341 xfrm6_tn->spi = 0;
342
343 return 0;
344}
345
346static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
347{
348}
349
350static struct pernet_operations xfrm6_tunnel_net_ops = {
351 .init = xfrm6_tunnel_net_init,
352 .exit = xfrm6_tunnel_net_exit,
353 .id = &xfrm6_tunnel_net_id,
354 .size = sizeof(struct xfrm6_tunnel_net),
355};
356
345static int __init xfrm6_tunnel_init(void) 357static int __init xfrm6_tunnel_init(void)
346{ 358{
347 if (xfrm_register_type(&xfrm6_tunnel_type, AF_INET6) < 0) 359 int rv;
348 goto err; 360
349 if (xfrm6_tunnel_register(&xfrm6_tunnel_handler, AF_INET6)) 361 xfrm6_tunnel_spi_kmem = kmem_cache_create("xfrm6_tunnel_spi",
350 goto unreg; 362 sizeof(struct xfrm6_tunnel_spi),
351 if (xfrm6_tunnel_register(&xfrm46_tunnel_handler, AF_INET)) 363 0, SLAB_HWCACHE_ALIGN,
352 goto dereg6; 364 NULL);
353 if (xfrm6_tunnel_spi_init() < 0) 365 if (!xfrm6_tunnel_spi_kmem)
354 goto dereg46; 366 return -ENOMEM;
367 rv = register_pernet_subsys(&xfrm6_tunnel_net_ops);
368 if (rv < 0)
369 goto out_pernet;
370 rv = xfrm_register_type(&xfrm6_tunnel_type, AF_INET6);
371 if (rv < 0)
372 goto out_type;
373 rv = xfrm6_tunnel_register(&xfrm6_tunnel_handler, AF_INET6);
374 if (rv < 0)
375 goto out_xfrm6;
376 rv = xfrm6_tunnel_register(&xfrm46_tunnel_handler, AF_INET);
377 if (rv < 0)
378 goto out_xfrm46;
355 return 0; 379 return 0;
356 380
357dereg46: 381out_xfrm46:
358 xfrm6_tunnel_deregister(&xfrm46_tunnel_handler, AF_INET);
359dereg6:
360 xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6); 382 xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6);
361unreg: 383out_xfrm6:
362 xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6); 384 xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
363err: 385out_type:
364 return -EAGAIN; 386 unregister_pernet_subsys(&xfrm6_tunnel_net_ops);
387out_pernet:
388 kmem_cache_destroy(xfrm6_tunnel_spi_kmem);
389 return rv;
365} 390}
366 391
367static void __exit xfrm6_tunnel_fini(void) 392static void __exit xfrm6_tunnel_fini(void)
368{ 393{
369 xfrm6_tunnel_spi_fini();
370 xfrm6_tunnel_deregister(&xfrm46_tunnel_handler, AF_INET); 394 xfrm6_tunnel_deregister(&xfrm46_tunnel_handler, AF_INET);
371 xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6); 395 xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6);
372 xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6); 396 xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
397 unregister_pernet_subsys(&xfrm6_tunnel_net_ops);
398 kmem_cache_destroy(xfrm6_tunnel_spi_kmem);
373} 399}
374 400
375module_init(xfrm6_tunnel_init); 401module_init(xfrm6_tunnel_init);