xref: /openbmc/linux/net/ipv6/ila/ila_xlat.c (revision b802fb99)
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 	__be64 identifier;
15 	int ifindex;
16 	unsigned int dir;
17 };
18 
19 struct 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 
26 static unsigned int ila_net_id;
27 
28 struct 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 
37 static 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 
63 static u32 hashrnd __read_mostly;
64 static __always_inline void __ila_hash_secret_init(void)
65 {
66 	net_get_random_once(&hashrnd, sizeof(hashrnd));
67 }
68 
69 static 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 
76 static 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 
81 static 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 
89 static 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 
96 static 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 
104 static 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 
117 static 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 
128 static 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 
138 static 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 
146 static 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 */
173 static 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 */
191 static 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 
207 static inline void ila_release(struct ila_map *ila)
208 {
209 	kfree_rcu(ila, rcu);
210 }
211 
212 static 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 
224 static int ila_xlat_addr(struct sk_buff *skb, int dir);
225 
226 static unsigned int
227 ila_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 
235 static 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 
244 static 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 
320 out:
321 	spin_unlock(lock);
322 
323 	if (err)
324 		kfree(ila);
325 
326 	return err;
327 }
328 
329 static 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 
385 out:
386 	spin_unlock(lock);
387 
388 	return err;
389 }
390 
391 static 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 
404 static 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 
419 static 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 
434 static 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 
450 nla_put_failure:
451 	genlmsg_cancel(skb, hdr);
452 	return -EMSGSIZE;
453 }
454 
455 static 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 
489 out_free:
490 	nlmsg_free(msg);
491 	return ret;
492 }
493 
494 struct ila_dump_iter {
495 	struct rhashtable_iter rhiter;
496 };
497 
498 static 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 
507 static 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 
516 static 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 
552 done:
553 	rhashtable_walk_stop(rhiter);
554 	return ret;
555 }
556 
557 static 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 
582 static __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 
596 static __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 
609 static 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 
616 static 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 
643 int ila_xlat_incoming(struct sk_buff *skb)
644 {
645 	return ila_xlat_addr(skb, ILA_DIR_IN);
646 }
647 EXPORT_SYMBOL(ila_xlat_incoming);
648 
649 int ila_xlat_outgoing(struct sk_buff *skb)
650 {
651 	return ila_xlat_addr(skb, ILA_DIR_OUT);
652 }
653 EXPORT_SYMBOL(ila_xlat_outgoing);
654 
655 int 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 
670 unregister:
671 	unregister_pernet_device(&ila_net_ops);
672 exit:
673 	return ret;
674 }
675 
676 void ila_xlat_fini(void)
677 {
678 	genl_unregister_family(&ila_nl_family);
679 	unregister_pernet_device(&ila_net_ops);
680 }
681