~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/ipv6/ila/ila_xlat.c

Version: ~ [ linux-5.11 ] ~ [ linux-5.10.17 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.99 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.176 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.221 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.257 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.257 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 
 12 struct ila_xlat_params {
 13         struct ila_params ip;
 14         int ifindex;
 15 };
 16 
 17 struct ila_map {
 18         struct ila_xlat_params xp;
 19         struct rhash_head node;
 20         struct ila_map __rcu *next;
 21         struct rcu_head rcu;
 22 };
 23 
 24 static unsigned int ila_net_id;
 25 
 26 struct ila_net {
 27         struct rhashtable rhash_table;
 28         spinlock_t *locks; /* Bucket locks for entry manipulation */
 29         unsigned int locks_mask;
 30         bool hooks_registered;
 31 };
 32 
 33 #define LOCKS_PER_CPU 10
 34 
 35 static int alloc_ila_locks(struct ila_net *ilan)
 36 {
 37         unsigned int i, size;
 38         unsigned int nr_pcpus = num_possible_cpus();
 39 
 40         nr_pcpus = min_t(unsigned int, nr_pcpus, 32UL);
 41         size = roundup_pow_of_two(nr_pcpus * LOCKS_PER_CPU);
 42 
 43         if (sizeof(spinlock_t) != 0) {
 44 #ifdef CONFIG_NUMA
 45                 if (size * sizeof(spinlock_t) > PAGE_SIZE)
 46                         ilan->locks = vmalloc(size * sizeof(spinlock_t));
 47                 else
 48 #endif
 49                 ilan->locks = kmalloc_array(size, sizeof(spinlock_t),
 50                                             GFP_KERNEL);
 51                 if (!ilan->locks)
 52                         return -ENOMEM;
 53                 for (i = 0; i < size; i++)
 54                         spin_lock_init(&ilan->locks[i]);
 55         }
 56         ilan->locks_mask = size - 1;
 57 
 58         return 0;
 59 }
 60 
 61 static u32 hashrnd __read_mostly;
 62 static __always_inline void __ila_hash_secret_init(void)
 63 {
 64         net_get_random_once(&hashrnd, sizeof(hashrnd));
 65 }
 66 
 67 static inline u32 ila_locator_hash(struct ila_locator loc)
 68 {
 69         u32 *v = (u32 *)loc.v32;
 70 
 71         return jhash_2words(v[0], v[1], hashrnd);
 72 }
 73 
 74 static inline spinlock_t *ila_get_lock(struct ila_net *ilan,
 75                                        struct ila_locator loc)
 76 {
 77         return &ilan->locks[ila_locator_hash(loc) & ilan->locks_mask];
 78 }
 79 
 80 static inline int ila_cmp_wildcards(struct ila_map *ila,
 81                                     struct ila_addr *iaddr, int ifindex)
 82 {
 83         return (ila->xp.ifindex && ila->xp.ifindex != ifindex);
 84 }
 85 
 86 static inline int ila_cmp_params(struct ila_map *ila,
 87                                  struct ila_xlat_params *xp)
 88 {
 89         return (ila->xp.ifindex != xp->ifindex);
 90 }
 91 
 92 static int ila_cmpfn(struct rhashtable_compare_arg *arg,
 93                      const void *obj)
 94 {
 95         const struct ila_map *ila = obj;
 96 
 97         return (ila->xp.ip.locator_match.v64 != *(__be64 *)arg->key);
 98 }
 99 
100 static inline int ila_order(struct ila_map *ila)
101 {
102         int score = 0;
103 
104         if (ila->xp.ifindex)
105                 score += 1 << 1;
106 
107         return score;
108 }
109 
110 static const struct rhashtable_params rht_params = {
111         .nelem_hint = 1024,
112         .head_offset = offsetof(struct ila_map, node),
113         .key_offset = offsetof(struct ila_map, xp.ip.locator_match),
114         .key_len = sizeof(u64), /* identifier */
115         .max_size = 1048576,
116         .min_size = 256,
117         .automatic_shrinking = true,
118         .obj_cmpfn = ila_cmpfn,
119 };
120 
121 static struct genl_family ila_nl_family = {
122         .id             = GENL_ID_GENERATE,
123         .hdrsize        = 0,
124         .name           = ILA_GENL_NAME,
125         .version        = ILA_GENL_VERSION,
126         .maxattr        = ILA_ATTR_MAX,
127         .netnsok        = true,
128         .parallel_ops   = true,
129 };
130 
131 static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
132         [ILA_ATTR_LOCATOR] = { .type = NLA_U64, },
133         [ILA_ATTR_LOCATOR_MATCH] = { .type = NLA_U64, },
134         [ILA_ATTR_IFINDEX] = { .type = NLA_U32, },
135         [ILA_ATTR_CSUM_MODE] = { .type = NLA_U8, },
136 };
137 
138 static int parse_nl_config(struct genl_info *info,
139                            struct ila_xlat_params *xp)
140 {
141         memset(xp, 0, sizeof(*xp));
142 
143         if (info->attrs[ILA_ATTR_LOCATOR])
144                 xp->ip.locator.v64 = (__force __be64)nla_get_u64(
145                         info->attrs[ILA_ATTR_LOCATOR]);
146 
147         if (info->attrs[ILA_ATTR_LOCATOR_MATCH])
148                 xp->ip.locator_match.v64 = (__force __be64)nla_get_u64(
149                         info->attrs[ILA_ATTR_LOCATOR_MATCH]);
150 
151         if (info->attrs[ILA_ATTR_CSUM_MODE])
152                 xp->ip.csum_mode = nla_get_u8(info->attrs[ILA_ATTR_CSUM_MODE]);
153 
154         if (info->attrs[ILA_ATTR_IFINDEX])
155                 xp->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]);
156 
157         return 0;
158 }
159 
160 /* Must be called with rcu readlock */
161 static inline struct ila_map *ila_lookup_wildcards(struct ila_addr *iaddr,
162                                                    int ifindex,
163                                                    struct ila_net *ilan)
164 {
165         struct ila_map *ila;
166 
167         ila = rhashtable_lookup_fast(&ilan->rhash_table, &iaddr->loc,
168                                      rht_params);
169         while (ila) {
170                 if (!ila_cmp_wildcards(ila, iaddr, ifindex))
171                         return ila;
172                 ila = rcu_access_pointer(ila->next);
173         }
174 
175         return NULL;
176 }
177 
178 /* Must be called with rcu readlock */
179 static inline struct ila_map *ila_lookup_by_params(struct ila_xlat_params *xp,
180                                                    struct ila_net *ilan)
181 {
182         struct ila_map *ila;
183 
184         ila = rhashtable_lookup_fast(&ilan->rhash_table,
185                                      &xp->ip.locator_match,
186                                      rht_params);
187         while (ila) {
188                 if (!ila_cmp_params(ila, xp))
189                         return ila;
190                 ila = rcu_access_pointer(ila->next);
191         }
192 
193         return NULL;
194 }
195 
196 static inline void ila_release(struct ila_map *ila)
197 {
198         kfree_rcu(ila, rcu);
199 }
200 
201 static void ila_free_cb(void *ptr, void *arg)
202 {
203         struct ila_map *ila = (struct ila_map *)ptr, *next;
204 
205         /* Assume rcu_readlock held */
206         while (ila) {
207                 next = rcu_access_pointer(ila->next);
208                 ila_release(ila);
209                 ila = next;
210         }
211 }
212 
213 static int ila_xlat_addr(struct sk_buff *skb);
214 
215 static unsigned int
216 ila_nf_input(void *priv,
217              struct sk_buff *skb,
218              const struct nf_hook_state *state)
219 {
220         ila_xlat_addr(skb);
221         return NF_ACCEPT;
222 }
223 
224 static struct nf_hook_ops ila_nf_hook_ops[] __read_mostly = {
225         {
226                 .hook = ila_nf_input,
227                 .pf = NFPROTO_IPV6,
228                 .hooknum = NF_INET_PRE_ROUTING,
229                 .priority = -1,
230         },
231 };
232 
233 static int ila_add_mapping(struct net *net, struct ila_xlat_params *xp)
234 {
235         struct ila_net *ilan = net_generic(net, ila_net_id);
236         struct ila_map *ila, *head;
237         spinlock_t *lock = ila_get_lock(ilan, xp->ip.locator_match);
238         int err = 0, order;
239 
240         if (!ilan->hooks_registered) {
241                 /* We defer registering net hooks in the namespace until the
242                  * first mapping is added.
243                  */
244                 err = nf_register_net_hooks(net, ila_nf_hook_ops,
245                                             ARRAY_SIZE(ila_nf_hook_ops));
246                 if (err)
247                         return err;
248 
249                 ilan->hooks_registered = true;
250         }
251 
252         ila = kzalloc(sizeof(*ila), GFP_KERNEL);
253         if (!ila)
254                 return -ENOMEM;
255 
256         ila_init_saved_csum(&xp->ip);
257 
258         ila->xp = *xp;
259 
260         order = ila_order(ila);
261 
262         spin_lock(lock);
263 
264         head = rhashtable_lookup_fast(&ilan->rhash_table,
265                                       &xp->ip.locator_match,
266                                       rht_params);
267         if (!head) {
268                 /* New entry for the rhash_table */
269                 err = rhashtable_lookup_insert_fast(&ilan->rhash_table,
270                                                     &ila->node, rht_params);
271         } else {
272                 struct ila_map *tila = head, *prev = NULL;
273 
274                 do {
275                         if (!ila_cmp_params(tila, xp)) {
276                                 err = -EEXIST;
277                                 goto out;
278                         }
279 
280                         if (order > ila_order(tila))
281                                 break;
282 
283                         prev = tila;
284                         tila = rcu_dereference_protected(tila->next,
285                                 lockdep_is_held(lock));
286                 } while (tila);
287 
288                 if (prev) {
289                         /* Insert in sub list of head */
290                         RCU_INIT_POINTER(ila->next, tila);
291                         rcu_assign_pointer(prev->next, ila);
292                 } else {
293                         /* Make this ila new head */
294                         RCU_INIT_POINTER(ila->next, head);
295                         err = rhashtable_replace_fast(&ilan->rhash_table,
296                                                       &head->node,
297                                                       &ila->node, rht_params);
298                         if (err)
299                                 goto out;
300                 }
301         }
302 
303 out:
304         spin_unlock(lock);
305 
306         if (err)
307                 kfree(ila);
308 
309         return err;
310 }
311 
312 static int ila_del_mapping(struct net *net, struct ila_xlat_params *xp)
313 {
314         struct ila_net *ilan = net_generic(net, ila_net_id);
315         struct ila_map *ila, *head, *prev;
316         spinlock_t *lock = ila_get_lock(ilan, xp->ip.locator_match);
317         int err = -ENOENT;
318 
319         spin_lock(lock);
320 
321         head = rhashtable_lookup_fast(&ilan->rhash_table,
322                                       &xp->ip.locator_match, rht_params);
323         ila = head;
324 
325         prev = NULL;
326 
327         while (ila) {
328                 if (ila_cmp_params(ila, xp)) {
329                         prev = ila;
330                         ila = rcu_dereference_protected(ila->next,
331                                                         lockdep_is_held(lock));
332                         continue;
333                 }
334 
335                 err = 0;
336 
337                 if (prev) {
338                         /* Not head, just delete from list */
339                         rcu_assign_pointer(prev->next, ila->next);
340                 } else {
341                         /* It is the head. If there is something in the
342                          * sublist we need to make a new head.
343                          */
344                         head = rcu_dereference_protected(ila->next,
345                                                          lockdep_is_held(lock));
346                         if (head) {
347                                 /* Put first entry in the sublist into the
348                                  * table
349                                  */
350                                 err = rhashtable_replace_fast(
351                                         &ilan->rhash_table, &ila->node,
352                                         &head->node, rht_params);
353                                 if (err)
354                                         goto out;
355                         } else {
356                                 /* Entry no longer used */
357                                 err = rhashtable_remove_fast(&ilan->rhash_table,
358                                                              &ila->node,
359                                                              rht_params);
360                         }
361                 }
362 
363                 ila_release(ila);
364 
365                 break;
366         }
367 
368 out:
369         spin_unlock(lock);
370 
371         return err;
372 }
373 
374 static int ila_nl_cmd_add_mapping(struct sk_buff *skb, struct genl_info *info)
375 {
376         struct net *net = genl_info_net(info);
377         struct ila_xlat_params p;
378         int err;
379 
380         err = parse_nl_config(info, &p);
381         if (err)
382                 return err;
383 
384         return ila_add_mapping(net, &p);
385 }
386 
387 static int ila_nl_cmd_del_mapping(struct sk_buff *skb, struct genl_info *info)
388 {
389         struct net *net = genl_info_net(info);
390         struct ila_xlat_params xp;
391         int err;
392 
393         err = parse_nl_config(info, &xp);
394         if (err)
395                 return err;
396 
397         ila_del_mapping(net, &xp);
398 
399         return 0;
400 }
401 
402 static int ila_fill_info(struct ila_map *ila, struct sk_buff *msg)
403 {
404         if (nla_put_u64_64bit(msg, ILA_ATTR_LOCATOR,
405                               (__force u64)ila->xp.ip.locator.v64,
406                               ILA_ATTR_PAD) ||
407             nla_put_u64_64bit(msg, ILA_ATTR_LOCATOR_MATCH,
408                               (__force u64)ila->xp.ip.locator_match.v64,
409                               ILA_ATTR_PAD) ||
410             nla_put_s32(msg, ILA_ATTR_IFINDEX, ila->xp.ifindex) ||
411             nla_put_u32(msg, ILA_ATTR_CSUM_MODE, ila->xp.ip.csum_mode))
412                 return -1;
413 
414         return 0;
415 }
416 
417 static int ila_dump_info(struct ila_map *ila,
418                          u32 portid, u32 seq, u32 flags,
419                          struct sk_buff *skb, u8 cmd)
420 {
421         void *hdr;
422 
423         hdr = genlmsg_put(skb, portid, seq, &ila_nl_family, flags, cmd);
424         if (!hdr)
425                 return -ENOMEM;
426 
427         if (ila_fill_info(ila, skb) < 0)
428                 goto nla_put_failure;
429 
430         genlmsg_end(skb, hdr);
431         return 0;
432 
433 nla_put_failure:
434         genlmsg_cancel(skb, hdr);
435         return -EMSGSIZE;
436 }
437 
438 static int ila_nl_cmd_get_mapping(struct sk_buff *skb, struct genl_info *info)
439 {
440         struct net *net = genl_info_net(info);
441         struct ila_net *ilan = net_generic(net, ila_net_id);
442         struct sk_buff *msg;
443         struct ila_xlat_params xp;
444         struct ila_map *ila;
445         int ret;
446 
447         ret = parse_nl_config(info, &xp);
448         if (ret)
449                 return ret;
450 
451         msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
452         if (!msg)
453                 return -ENOMEM;
454 
455         rcu_read_lock();
456 
457         ila = ila_lookup_by_params(&xp, ilan);
458         if (ila) {
459                 ret = ila_dump_info(ila,
460                                     info->snd_portid,
461                                     info->snd_seq, 0, msg,
462                                     info->genlhdr->cmd);
463         }
464 
465         rcu_read_unlock();
466 
467         if (ret < 0)
468                 goto out_free;
469 
470         return genlmsg_reply(msg, info);
471 
472 out_free:
473         nlmsg_free(msg);
474         return ret;
475 }
476 
477 struct ila_dump_iter {
478         struct rhashtable_iter rhiter;
479 };
480 
481 static int ila_nl_dump_start(struct netlink_callback *cb)
482 {
483         struct net *net = sock_net(cb->skb->sk);
484         struct ila_net *ilan = net_generic(net, ila_net_id);
485         struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args;
486 
487         return rhashtable_walk_init(&ilan->rhash_table, &iter->rhiter,
488                                     GFP_KERNEL);
489 }
490 
491 static int ila_nl_dump_done(struct netlink_callback *cb)
492 {
493         struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args;
494 
495         rhashtable_walk_exit(&iter->rhiter);
496 
497         return 0;
498 }
499 
500 static int ila_nl_dump(struct sk_buff *skb, struct netlink_callback *cb)
501 {
502         struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args;
503         struct rhashtable_iter *rhiter = &iter->rhiter;
504         struct ila_map *ila;
505         int ret;
506 
507         ret = rhashtable_walk_start(rhiter);
508         if (ret && ret != -EAGAIN)
509                 goto done;
510 
511         for (;;) {
512                 ila = rhashtable_walk_next(rhiter);
513 
514                 if (IS_ERR(ila)) {
515                         if (PTR_ERR(ila) == -EAGAIN)
516                                 continue;
517                         ret = PTR_ERR(ila);
518                         goto done;
519                 } else if (!ila) {
520                         break;
521                 }
522 
523                 while (ila) {
524                         ret =  ila_dump_info(ila, NETLINK_CB(cb->skb).portid,
525                                              cb->nlh->nlmsg_seq, NLM_F_MULTI,
526                                              skb, ILA_CMD_GET);
527                         if (ret)
528                                 goto done;
529 
530                         ila = rcu_access_pointer(ila->next);
531                 }
532         }
533 
534         ret = skb->len;
535 
536 done:
537         rhashtable_walk_stop(rhiter);
538         return ret;
539 }
540 
541 static const struct genl_ops ila_nl_ops[] = {
542         {
543                 .cmd = ILA_CMD_ADD,
544                 .doit = ila_nl_cmd_add_mapping,
545                 .policy = ila_nl_policy,
546                 .flags = GENL_ADMIN_PERM,
547         },
548         {
549                 .cmd = ILA_CMD_DEL,
550                 .doit = ila_nl_cmd_del_mapping,
551                 .policy = ila_nl_policy,
552                 .flags = GENL_ADMIN_PERM,
553         },
554         {
555                 .cmd = ILA_CMD_GET,
556                 .doit = ila_nl_cmd_get_mapping,
557                 .start = ila_nl_dump_start,
558                 .dumpit = ila_nl_dump,
559                 .done = ila_nl_dump_done,
560                 .policy = ila_nl_policy,
561         },
562 };
563 
564 #define ILA_HASH_TABLE_SIZE 1024
565 
566 static __net_init int ila_init_net(struct net *net)
567 {
568         int err;
569         struct ila_net *ilan = net_generic(net, ila_net_id);
570 
571         err = alloc_ila_locks(ilan);
572         if (err)
573                 return err;
574 
575         rhashtable_init(&ilan->rhash_table, &rht_params);
576 
577         return 0;
578 }
579 
580 static __net_exit void ila_exit_net(struct net *net)
581 {
582         struct ila_net *ilan = net_generic(net, ila_net_id);
583 
584         rhashtable_free_and_destroy(&ilan->rhash_table, ila_free_cb, NULL);
585 
586         kvfree(ilan->locks);
587 
588         if (ilan->hooks_registered)
589                 nf_unregister_net_hooks(net, ila_nf_hook_ops,
590                                         ARRAY_SIZE(ila_nf_hook_ops));
591 }
592 
593 static struct pernet_operations ila_net_ops = {
594         .init = ila_init_net,
595         .exit = ila_exit_net,
596         .id   = &ila_net_id,
597         .size = sizeof(struct ila_net),
598 };
599 
600 static int ila_xlat_addr(struct sk_buff *skb)
601 {
602         struct ila_map *ila;
603         struct ipv6hdr *ip6h = ipv6_hdr(skb);
604         struct net *net = dev_net(skb->dev);
605         struct ila_net *ilan = net_generic(net, ila_net_id);
606         struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
607 
608         /* Assumes skb contains a valid IPv6 header that is pulled */
609 
610         if (!ila_addr_is_ila(iaddr)) {
611                 /* Type indicates this is not an ILA address */
612                 return 0;
613         }
614 
615         rcu_read_lock();
616 
617         ila = ila_lookup_wildcards(iaddr, skb->dev->ifindex, ilan);
618         if (ila)
619                 ila_update_ipv6_locator(skb, &ila->xp.ip);
620 
621         rcu_read_unlock();
622 
623         return 0;
624 }
625 
626 int ila_xlat_init(void)
627 {
628         int ret;
629 
630         ret = register_pernet_device(&ila_net_ops);
631         if (ret)
632                 goto exit;
633 
634         ret = genl_register_family_with_ops(&ila_nl_family,
635                                             ila_nl_ops);
636         if (ret < 0)
637                 goto unregister;
638 
639         return 0;
640 
641 unregister:
642         unregister_pernet_device(&ila_net_ops);
643 exit:
644         return ret;
645 }
646 
647 void ila_xlat_fini(void)
648 {
649         genl_unregister_family(&ila_nl_family);
650         unregister_pernet_device(&ila_net_ops);
651 }
652 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp