aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Herbert <tom@herbertland.com>2015-12-15 18:41:38 -0500
committerDavid S. Miller <davem@davemloft.net>2015-12-15 23:25:20 -0500
commit7f00feaf107645d95a6d87e99b4d141ac0a08efd (patch)
tree207ff2184419083aa1a70fc37ba8a5ce54135ae9
parentfc9e50f5a5a4e1fa9ba2756f745a13e693cf6a06 (diff)
ila: Add generic ILA translation facility
This patch implements an ILA tanslation table. This table can be configured with identifier to locator mappings, and can be be queried to resolve a mapping. Queries can be parameterized based on interface, direction (incoming or outoing), and matching locator. The table is implemented using rhashtable and is configured via netlink (through "ip ila .." in iproute). The table may be used as alternative means to do do ILA tanslations other than the lw tunnels Signed-off-by: Tom Herbert <tom@herbertland.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/ila.h18
-rw-r--r--include/uapi/linux/ila.h22
-rw-r--r--net/ipv6/ila/Makefile2
-rw-r--r--net/ipv6/ila/ila.h2
-rw-r--r--net/ipv6/ila/ila_common.c8
-rw-r--r--net/ipv6/ila/ila_xlat.c680
6 files changed, 731 insertions, 1 deletions
diff --git a/include/net/ila.h b/include/net/ila.h
new file mode 100644
index 000000000000..9f4f43e94ae4
--- /dev/null
+++ b/include/net/ila.h
@@ -0,0 +1,18 @@
1/*
2 * ILA kernel interface
3 *
4 * Copyright (c) 2015 Tom Herbert <tom@herbertland.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 */
11
12#ifndef _NET_ILA_H
13#define _NET_ILA_H
14
15int ila_xlat_outgoing(struct sk_buff *skb);
16int ila_xlat_incoming(struct sk_buff *skb);
17
18#endif /* _NET_ILA_H */
diff --git a/include/uapi/linux/ila.h b/include/uapi/linux/ila.h
index 7ed9e670814e..abde7bbd6f3b 100644
--- a/include/uapi/linux/ila.h
+++ b/include/uapi/linux/ila.h
@@ -3,13 +3,35 @@
3#ifndef _UAPI_LINUX_ILA_H 3#ifndef _UAPI_LINUX_ILA_H
4#define _UAPI_LINUX_ILA_H 4#define _UAPI_LINUX_ILA_H
5 5
6/* NETLINK_GENERIC related info */
7#define ILA_GENL_NAME "ila"
8#define ILA_GENL_VERSION 0x1
9
6enum { 10enum {
7 ILA_ATTR_UNSPEC, 11 ILA_ATTR_UNSPEC,
8 ILA_ATTR_LOCATOR, /* u64 */ 12 ILA_ATTR_LOCATOR, /* u64 */
13 ILA_ATTR_IDENTIFIER, /* u64 */
14 ILA_ATTR_LOCATOR_MATCH, /* u64 */
15 ILA_ATTR_IFINDEX, /* s32 */
16 ILA_ATTR_DIR, /* u32 */
9 17
10 __ILA_ATTR_MAX, 18 __ILA_ATTR_MAX,
11}; 19};
12 20
13#define ILA_ATTR_MAX (__ILA_ATTR_MAX - 1) 21#define ILA_ATTR_MAX (__ILA_ATTR_MAX - 1)
14 22
23enum {
24 ILA_CMD_UNSPEC,
25 ILA_CMD_ADD,
26 ILA_CMD_DEL,
27 ILA_CMD_GET,
28
29 __ILA_CMD_MAX,
30};
31
32#define ILA_CMD_MAX (__ILA_CMD_MAX - 1)
33
34#define ILA_DIR_IN (1 << 0)
35#define ILA_DIR_OUT (1 << 1)
36
15#endif /* _UAPI_LINUX_ILA_H */ 37#endif /* _UAPI_LINUX_ILA_H */
diff --git a/net/ipv6/ila/Makefile b/net/ipv6/ila/Makefile
index 31d136be2f21..4b32e5921e5c 100644
--- a/net/ipv6/ila/Makefile
+++ b/net/ipv6/ila/Makefile
@@ -4,4 +4,4 @@
4 4
5obj-$(CONFIG_IPV6_ILA) += ila.o 5obj-$(CONFIG_IPV6_ILA) += ila.o
6 6
7ila-objs := ila_common.o ila_lwt.o 7ila-objs := ila_common.o ila_lwt.o ila_xlat.o
diff --git a/net/ipv6/ila/ila.h b/net/ipv6/ila/ila.h
index b94081ff2f8a..28542cb2b387 100644
--- a/net/ipv6/ila/ila.h
+++ b/net/ipv6/ila/ila.h
@@ -42,5 +42,7 @@ void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p);
42 42
43int ila_lwt_init(void); 43int ila_lwt_init(void);
44void ila_lwt_fini(void); 44void ila_lwt_fini(void);
45int ila_xlat_init(void);
46void ila_xlat_fini(void);
45 47
46#endif /* __ILA_H */ 48#endif /* __ILA_H */
diff --git a/net/ipv6/ila/ila_common.c b/net/ipv6/ila/ila_common.c
index 64e1904991ac..32dc9aab7297 100644
--- a/net/ipv6/ila/ila_common.c
+++ b/net/ipv6/ila/ila_common.c
@@ -80,12 +80,20 @@ static int __init ila_init(void)
80 if (ret) 80 if (ret)
81 goto fail_lwt; 81 goto fail_lwt;
82 82
83 ret = ila_xlat_init();
84 if (ret)
85 goto fail_xlat;
86
87 return 0;
88fail_xlat:
89 ila_lwt_fini();
83fail_lwt: 90fail_lwt:
84 return ret; 91 return ret;
85} 92}
86 93
87static void __exit ila_fini(void) 94static void __exit ila_fini(void)
88{ 95{
96 ila_xlat_fini();
89 ila_lwt_fini(); 97 ila_lwt_fini();
90} 98}
91 99
diff --git a/net/ipv6/ila/ila_xlat.c b/net/ipv6/ila/ila_xlat.c
new file mode 100644
index 000000000000..295ca29a23c3
--- /dev/null
+++ b/net/ipv6/ila/ila_xlat.c
@@ -0,0 +1,680 @@
1#include <linux/jhash.h>
2#include <linux/netfilter.h>
3#include <linux/rcupdate.h>
4#include <linux/rhashtable.h>
5#include <linux/vmalloc.h>
6#include <net/genetlink.h>
7#include <net/ila.h>
8#include <net/netns/generic.h>
9#include <uapi/linux/genetlink.h>
10#include "ila.h"
11
12struct ila_xlat_params {
13 struct ila_params ip;
14 __be64 identifier;
15 int ifindex;
16 unsigned int dir;
17};
18
19struct ila_map {
20 struct ila_xlat_params p;
21 struct rhash_head node;
22 struct ila_map __rcu *next;
23 struct rcu_head rcu;
24};
25
26static unsigned int ila_net_id;
27
28struct ila_net {
29 struct rhashtable rhash_table;
30 spinlock_t *locks; /* Bucket locks for entry manipulation */
31 unsigned int locks_mask;
32 bool hooks_registered;
33};
34
35#define LOCKS_PER_CPU 10
36
37static int alloc_ila_locks(struct ila_net *ilan)
38{
39 unsigned int i, size;
40 unsigned int nr_pcpus = num_possible_cpus();
41
42 nr_pcpus = min_t(unsigned int, nr_pcpus, 32UL);
43 size = roundup_pow_of_two(nr_pcpus * LOCKS_PER_CPU);
44
45 if (sizeof(spinlock_t) != 0) {
46#ifdef CONFIG_NUMA
47 if (size * sizeof(spinlock_t) > PAGE_SIZE)
48 ilan->locks = vmalloc(size * sizeof(spinlock_t));
49 else
50#endif
51 ilan->locks = kmalloc_array(size, sizeof(spinlock_t),
52 GFP_KERNEL);
53 if (!ilan->locks)
54 return -ENOMEM;
55 for (i = 0; i < size; i++)
56 spin_lock_init(&ilan->locks[i]);
57 }
58 ilan->locks_mask = size - 1;
59
60 return 0;
61}
62
63static u32 hashrnd __read_mostly;
64static __always_inline void __ila_hash_secret_init(void)
65{
66 net_get_random_once(&hashrnd, sizeof(hashrnd));
67}
68
69static inline u32 ila_identifier_hash(__be64 identifier)
70{
71 u32 *v = (u32 *)&identifier;
72
73 return jhash_2words(v[0], v[1], hashrnd);
74}
75
76static inline spinlock_t *ila_get_lock(struct ila_net *ilan, __be64 identifier)
77{
78 return &ilan->locks[ila_identifier_hash(identifier) & ilan->locks_mask];
79}
80
81static inline int ila_cmp_wildcards(struct ila_map *ila, __be64 loc,
82 int ifindex, unsigned int dir)
83{
84 return (ila->p.ip.locator_match && ila->p.ip.locator_match != loc) ||
85 (ila->p.ifindex && ila->p.ifindex != ifindex) ||
86 !(ila->p.dir & dir);
87}
88
89static inline int ila_cmp_params(struct ila_map *ila, struct ila_xlat_params *p)
90{
91 return (ila->p.ip.locator_match != p->ip.locator_match) ||
92 (ila->p.ifindex != p->ifindex) ||
93 (ila->p.dir != p->dir);
94}
95
96static int ila_cmpfn(struct rhashtable_compare_arg *arg,
97 const void *obj)
98{
99 const struct ila_map *ila = obj;
100
101 return (ila->p.identifier != *(__be64 *)arg->key);
102}
103
104static inline int ila_order(struct ila_map *ila)
105{
106 int score = 0;
107
108 if (ila->p.ip.locator_match)
109 score += 1 << 0;
110
111 if (ila->p.ifindex)
112 score += 1 << 1;
113
114 return score;
115}
116
117static const struct rhashtable_params rht_params = {
118 .nelem_hint = 1024,
119 .head_offset = offsetof(struct ila_map, node),
120 .key_offset = offsetof(struct ila_map, p.identifier),
121 .key_len = sizeof(u64), /* identifier */
122 .max_size = 1048576,
123 .min_size = 256,
124 .automatic_shrinking = true,
125 .obj_cmpfn = ila_cmpfn,
126};
127
128static struct genl_family ila_nl_family = {
129 .id = GENL_ID_GENERATE,
130 .hdrsize = 0,
131 .name = ILA_GENL_NAME,
132 .version = ILA_GENL_VERSION,
133 .maxattr = ILA_ATTR_MAX,
134 .netnsok = true,
135 .parallel_ops = true,
136};
137
138static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
139 [ILA_ATTR_IDENTIFIER] = { .type = NLA_U64, },
140 [ILA_ATTR_LOCATOR] = { .type = NLA_U64, },
141 [ILA_ATTR_LOCATOR_MATCH] = { .type = NLA_U64, },
142 [ILA_ATTR_IFINDEX] = { .type = NLA_U32, },
143 [ILA_ATTR_DIR] = { .type = NLA_U32, },
144};
145
146static int parse_nl_config(struct genl_info *info,
147 struct ila_xlat_params *p)
148{
149 memset(p, 0, sizeof(*p));
150
151 if (info->attrs[ILA_ATTR_IDENTIFIER])
152 p->identifier = (__force __be64)nla_get_u64(
153 info->attrs[ILA_ATTR_IDENTIFIER]);
154
155 if (info->attrs[ILA_ATTR_LOCATOR])
156 p->ip.locator = (__force __be64)nla_get_u64(
157 info->attrs[ILA_ATTR_LOCATOR]);
158
159 if (info->attrs[ILA_ATTR_LOCATOR_MATCH])
160 p->ip.locator_match = (__force __be64)nla_get_u64(
161 info->attrs[ILA_ATTR_LOCATOR_MATCH]);
162
163 if (info->attrs[ILA_ATTR_IFINDEX])
164 p->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]);
165
166 if (info->attrs[ILA_ATTR_DIR])
167 p->dir = nla_get_u32(info->attrs[ILA_ATTR_DIR]);
168
169 return 0;
170}
171
172/* Must be called with rcu readlock */
173static inline struct ila_map *ila_lookup_wildcards(__be64 id, __be64 loc,
174 int ifindex,
175 unsigned int dir,
176 struct ila_net *ilan)
177{
178 struct ila_map *ila;
179
180 ila = rhashtable_lookup_fast(&ilan->rhash_table, &id, rht_params);
181 while (ila) {
182 if (!ila_cmp_wildcards(ila, loc, ifindex, dir))
183 return ila;
184 ila = rcu_access_pointer(ila->next);
185 }
186
187 return NULL;
188}
189
190/* Must be called with rcu readlock */
191static inline struct ila_map *ila_lookup_by_params(struct ila_xlat_params *p,
192 struct ila_net *ilan)
193{
194 struct ila_map *ila;
195
196 ila = rhashtable_lookup_fast(&ilan->rhash_table, &p->identifier,
197 rht_params);
198 while (ila) {
199 if (!ila_cmp_params(ila, p))
200 return ila;
201 ila = rcu_access_pointer(ila->next);
202 }
203
204 return NULL;
205}
206
207static inline void ila_release(struct ila_map *ila)
208{
209 kfree_rcu(ila, rcu);
210}
211
212static void ila_free_cb(void *ptr, void *arg)
213{
214 struct ila_map *ila = (struct ila_map *)ptr, *next;
215
216 /* Assume rcu_readlock held */
217 while (ila) {
218 next = rcu_access_pointer(ila->next);
219 ila_release(ila);
220 ila = next;
221 }
222}
223
224static int ila_xlat_addr(struct sk_buff *skb, int dir);
225
226static unsigned int
227ila_nf_input(void *priv,
228 struct sk_buff *skb,
229 const struct nf_hook_state *state)
230{
231 ila_xlat_addr(skb, ILA_DIR_IN);
232 return NF_ACCEPT;
233}
234
235static struct nf_hook_ops ila_nf_hook_ops[] __read_mostly = {
236 {
237 .hook = ila_nf_input,
238 .pf = NFPROTO_IPV6,
239 .hooknum = NF_INET_PRE_ROUTING,
240 .priority = -1,
241 },
242};
243
244static int ila_add_mapping(struct net *net, struct ila_xlat_params *p)
245{
246 struct ila_net *ilan = net_generic(net, ila_net_id);
247 struct ila_map *ila, *head;
248 spinlock_t *lock = ila_get_lock(ilan, p->identifier);
249 int err = 0, order;
250
251 if (!ilan->hooks_registered) {
252 /* We defer registering net hooks in the namespace until the
253 * first mapping is added.
254 */
255 err = nf_register_net_hooks(net, ila_nf_hook_ops,
256 ARRAY_SIZE(ila_nf_hook_ops));
257 if (err)
258 return err;
259
260 ilan->hooks_registered = true;
261 }
262
263 ila = kzalloc(sizeof(*ila), GFP_KERNEL);
264 if (!ila)
265 return -ENOMEM;
266
267 ila->p = *p;
268
269 if (p->ip.locator_match) {
270 /* Precompute checksum difference for translation since we
271 * know both the old identifier and the new one.
272 */
273 ila->p.ip.csum_diff = compute_csum_diff8(
274 (__be32 *)&p->ip.locator_match,
275 (__be32 *)&p->ip.locator);
276 }
277
278 order = ila_order(ila);
279
280 spin_lock(lock);
281
282 head = rhashtable_lookup_fast(&ilan->rhash_table, &p->identifier,
283 rht_params);
284 if (!head) {
285 /* New entry for the rhash_table */
286 err = rhashtable_lookup_insert_fast(&ilan->rhash_table,
287 &ila->node, rht_params);
288 } else {
289 struct ila_map *tila = head, *prev = NULL;
290
291 do {
292 if (!ila_cmp_params(tila, p)) {
293 err = -EEXIST;
294 goto out;
295 }
296
297 if (order > ila_order(tila))
298 break;
299
300 prev = tila;
301 tila = rcu_dereference_protected(tila->next,
302 lockdep_is_held(lock));
303 } while (tila);
304
305 if (prev) {
306 /* Insert in sub list of head */
307 RCU_INIT_POINTER(ila->next, tila);
308 rcu_assign_pointer(prev->next, ila);
309 } else {
310 /* Make this ila new head */
311 RCU_INIT_POINTER(ila->next, head);
312 err = rhashtable_replace_fast(&ilan->rhash_table,
313 &head->node,
314 &ila->node, rht_params);
315 if (err)
316 goto out;
317 }
318 }
319
320out:
321 spin_unlock(lock);
322
323 if (err)
324 kfree(ila);
325
326 return err;
327}
328
329static int ila_del_mapping(struct net *net, struct ila_xlat_params *p)
330{
331 struct ila_net *ilan = net_generic(net, ila_net_id);
332 struct ila_map *ila, *head, *prev;
333 spinlock_t *lock = ila_get_lock(ilan, p->identifier);
334 int err = -ENOENT;
335
336 spin_lock(lock);
337
338 head = rhashtable_lookup_fast(&ilan->rhash_table,
339 &p->identifier, rht_params);
340 ila = head;
341
342 prev = NULL;
343
344 while (ila) {
345 if (ila_cmp_params(ila, p)) {
346 prev = ila;
347 ila = rcu_dereference_protected(ila->next,
348 lockdep_is_held(lock));
349 continue;
350 }
351
352 err = 0;
353
354 if (prev) {
355 /* Not head, just delete from list */
356 rcu_assign_pointer(prev->next, ila->next);
357 } else {
358 /* It is the head. If there is something in the
359 * sublist we need to make a new head.
360 */
361 head = rcu_dereference_protected(ila->next,
362 lockdep_is_held(lock));
363 if (head) {
364 /* Put first entry in the sublist into the
365 * table
366 */
367 err = rhashtable_replace_fast(
368 &ilan->rhash_table, &ila->node,
369 &head->node, rht_params);
370 if (err)
371 goto out;
372 } else {
373 /* Entry no longer used */
374 err = rhashtable_remove_fast(&ilan->rhash_table,
375 &ila->node,
376 rht_params);
377 }
378 }
379
380 ila_release(ila);
381
382 break;
383 }
384
385out:
386 spin_unlock(lock);
387
388 return err;
389}
390
391static int ila_nl_cmd_add_mapping(struct sk_buff *skb, struct genl_info *info)
392{
393 struct net *net = genl_info_net(info);
394 struct ila_xlat_params p;
395 int err;
396
397 err = parse_nl_config(info, &p);
398 if (err)
399 return err;
400
401 return ila_add_mapping(net, &p);
402}
403
404static int ila_nl_cmd_del_mapping(struct sk_buff *skb, struct genl_info *info)
405{
406 struct net *net = genl_info_net(info);
407 struct ila_xlat_params p;
408 int err;
409
410 err = parse_nl_config(info, &p);
411 if (err)
412 return err;
413
414 ila_del_mapping(net, &p);
415
416 return 0;
417}
418
419static int ila_fill_info(struct ila_map *ila, struct sk_buff *msg)
420{
421 if (nla_put_u64(msg, ILA_ATTR_IDENTIFIER,
422 (__force u64)ila->p.identifier) ||
423 nla_put_u64(msg, ILA_ATTR_LOCATOR,
424 (__force u64)ila->p.ip.locator) ||
425 nla_put_u64(msg, ILA_ATTR_LOCATOR_MATCH,
426 (__force u64)ila->p.ip.locator_match) ||
427 nla_put_s32(msg, ILA_ATTR_IFINDEX, ila->p.ifindex) ||
428 nla_put_u32(msg, ILA_ATTR_DIR, ila->p.dir))
429 return -1;
430
431 return 0;
432}
433
434static int ila_dump_info(struct ila_map *ila,
435 u32 portid, u32 seq, u32 flags,
436 struct sk_buff *skb, u8 cmd)
437{
438 void *hdr;
439
440 hdr = genlmsg_put(skb, portid, seq, &ila_nl_family, flags, cmd);
441 if (!hdr)
442 return -ENOMEM;
443
444 if (ila_fill_info(ila, skb) < 0)
445 goto nla_put_failure;
446
447 genlmsg_end(skb, hdr);
448 return 0;
449
450nla_put_failure:
451 genlmsg_cancel(skb, hdr);
452 return -EMSGSIZE;
453}
454
455static int ila_nl_cmd_get_mapping(struct sk_buff *skb, struct genl_info *info)
456{
457 struct net *net = genl_info_net(info);
458 struct ila_net *ilan = net_generic(net, ila_net_id);
459 struct sk_buff *msg;
460 struct ila_xlat_params p;
461 struct ila_map *ila;
462 int ret;
463
464 ret = parse_nl_config(info, &p);
465 if (ret)
466 return ret;
467
468 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
469 if (!msg)
470 return -ENOMEM;
471
472 rcu_read_lock();
473
474 ila = ila_lookup_by_params(&p, ilan);
475 if (ila) {
476 ret = ila_dump_info(ila,
477 info->snd_portid,
478 info->snd_seq, 0, msg,
479 info->genlhdr->cmd);
480 }
481
482 rcu_read_unlock();
483
484 if (ret < 0)
485 goto out_free;
486
487 return genlmsg_reply(msg, info);
488
489out_free:
490 nlmsg_free(msg);
491 return ret;
492}
493
494struct ila_dump_iter {
495 struct rhashtable_iter rhiter;
496};
497
498static int ila_nl_dump_start(struct netlink_callback *cb)
499{
500 struct net *net = sock_net(cb->skb->sk);
501 struct ila_net *ilan = net_generic(net, ila_net_id);
502 struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args;
503
504 return rhashtable_walk_init(&ilan->rhash_table, &iter->rhiter);
505}
506
507static int ila_nl_dump_done(struct netlink_callback *cb)
508{
509 struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args;
510
511 rhashtable_walk_exit(&iter->rhiter);
512
513 return 0;
514}
515
516static int ila_nl_dump(struct sk_buff *skb, struct netlink_callback *cb)
517{
518 struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args;
519 struct rhashtable_iter *rhiter = &iter->rhiter;
520 struct ila_map *ila;
521 int ret;
522
523 ret = rhashtable_walk_start(rhiter);
524 if (ret && ret != -EAGAIN)
525 goto done;
526
527 for (;;) {
528 ila = rhashtable_walk_next(rhiter);
529
530 if (IS_ERR(ila)) {
531 if (PTR_ERR(ila) == -EAGAIN)
532 continue;
533 ret = PTR_ERR(ila);
534 goto done;
535 } else if (!ila) {
536 break;
537 }
538
539 while (ila) {
540 ret = ila_dump_info(ila, NETLINK_CB(cb->skb).portid,
541 cb->nlh->nlmsg_seq, NLM_F_MULTI,
542 skb, ILA_CMD_GET);
543 if (ret)
544 goto done;
545
546 ila = rcu_access_pointer(ila->next);
547 }
548 }
549
550 ret = skb->len;
551
552done:
553 rhashtable_walk_stop(rhiter);
554 return ret;
555}
556
557static const struct genl_ops ila_nl_ops[] = {
558 {
559 .cmd = ILA_CMD_ADD,
560 .doit = ila_nl_cmd_add_mapping,
561 .policy = ila_nl_policy,
562 .flags = GENL_ADMIN_PERM,
563 },
564 {
565 .cmd = ILA_CMD_DEL,
566 .doit = ila_nl_cmd_del_mapping,
567 .policy = ila_nl_policy,
568 .flags = GENL_ADMIN_PERM,
569 },
570 {
571 .cmd = ILA_CMD_GET,
572 .doit = ila_nl_cmd_get_mapping,
573 .start = ila_nl_dump_start,
574 .dumpit = ila_nl_dump,
575 .done = ila_nl_dump_done,
576 .policy = ila_nl_policy,
577 },
578};
579
580#define ILA_HASH_TABLE_SIZE 1024
581
582static __net_init int ila_init_net(struct net *net)
583{
584 int err;
585 struct ila_net *ilan = net_generic(net, ila_net_id);
586
587 err = alloc_ila_locks(ilan);
588 if (err)
589 return err;
590
591 rhashtable_init(&ilan->rhash_table, &rht_params);
592
593 return 0;
594}
595
596static __net_exit void ila_exit_net(struct net *net)
597{
598 struct ila_net *ilan = net_generic(net, ila_net_id);
599
600 rhashtable_free_and_destroy(&ilan->rhash_table, ila_free_cb, NULL);
601
602 kvfree(ilan->locks);
603
604 if (ilan->hooks_registered)
605 nf_unregister_net_hooks(net, ila_nf_hook_ops,
606 ARRAY_SIZE(ila_nf_hook_ops));
607}
608
609static struct pernet_operations ila_net_ops = {
610 .init = ila_init_net,
611 .exit = ila_exit_net,
612 .id = &ila_net_id,
613 .size = sizeof(struct ila_net),
614};
615
616static int ila_xlat_addr(struct sk_buff *skb, int dir)
617{
618 struct ila_map *ila;
619 struct ipv6hdr *ip6h = ipv6_hdr(skb);
620 struct net *net = dev_net(skb->dev);
621 struct ila_net *ilan = net_generic(net, ila_net_id);
622 __be64 identifier, locator_match;
623 size_t nhoff;
624
625 /* Assumes skb contains a valid IPv6 header that is pulled */
626
627 identifier = *(__be64 *)&ip6h->daddr.in6_u.u6_addr8[8];
628 locator_match = *(__be64 *)&ip6h->daddr.in6_u.u6_addr8[0];
629 nhoff = sizeof(struct ipv6hdr);
630
631 rcu_read_lock();
632
633 ila = ila_lookup_wildcards(identifier, locator_match,
634 skb->dev->ifindex, dir, ilan);
635 if (ila)
636 update_ipv6_locator(skb, &ila->p.ip);
637
638 rcu_read_unlock();
639
640 return 0;
641}
642
643int ila_xlat_incoming(struct sk_buff *skb)
644{
645 return ila_xlat_addr(skb, ILA_DIR_IN);
646}
647EXPORT_SYMBOL(ila_xlat_incoming);
648
649int ila_xlat_outgoing(struct sk_buff *skb)
650{
651 return ila_xlat_addr(skb, ILA_DIR_OUT);
652}
653EXPORT_SYMBOL(ila_xlat_outgoing);
654
655int ila_xlat_init(void)
656{
657 int ret;
658
659 ret = register_pernet_device(&ila_net_ops);
660 if (ret)
661 goto exit;
662
663 ret = genl_register_family_with_ops(&ila_nl_family,
664 ila_nl_ops);
665 if (ret < 0)
666 goto unregister;
667
668 return 0;
669
670unregister:
671 unregister_pernet_device(&ila_net_ops);
672exit:
673 return ret;
674}
675
676void ila_xlat_fini(void)
677{
678 genl_unregister_family(&ila_nl_family);
679 unregister_pernet_device(&ila_net_ops);
680}