xref: /openbmc/linux/drivers/net/netdevsim/fib.c (revision 36acd5e2)
1 /*
2  * Copyright (c) 2018 Cumulus Networks. All rights reserved.
3  * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
4  *
5  * This software is licensed under the GNU General License Version 2,
6  * June 1991 as shown in the file COPYING in the top-level directory of this
7  * source tree.
8  *
9  * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
10  * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
11  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12  * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
13  * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
14  * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
15  */
16 
17 #include <linux/in6.h>
18 #include <linux/kernel.h>
19 #include <linux/list.h>
20 #include <linux/rhashtable.h>
21 #include <linux/spinlock_types.h>
22 #include <linux/types.h>
23 #include <net/fib_notifier.h>
24 #include <net/ip_fib.h>
25 #include <net/ip6_fib.h>
26 #include <net/fib_rules.h>
27 #include <net/net_namespace.h>
28 #include <net/nexthop.h>
29 
30 #include "netdevsim.h"
31 
32 struct nsim_fib_entry {
33 	u64 max;
34 	u64 num;
35 };
36 
37 struct nsim_per_fib_data {
38 	struct nsim_fib_entry fib;
39 	struct nsim_fib_entry rules;
40 };
41 
42 struct nsim_fib_data {
43 	struct notifier_block fib_nb;
44 	struct nsim_per_fib_data ipv4;
45 	struct nsim_per_fib_data ipv6;
46 	struct nsim_fib_entry nexthops;
47 	struct rhashtable fib_rt_ht;
48 	struct list_head fib_rt_list;
49 	spinlock_t fib_lock;	/* Protects hashtable, list and accounting */
50 	struct notifier_block nexthop_nb;
51 	struct rhashtable nexthop_ht;
52 	struct devlink *devlink;
53 };
54 
55 struct nsim_fib_rt_key {
56 	unsigned char addr[sizeof(struct in6_addr)];
57 	unsigned char prefix_len;
58 	int family;
59 	u32 tb_id;
60 };
61 
62 struct nsim_fib_rt {
63 	struct nsim_fib_rt_key key;
64 	struct rhash_head ht_node;
65 	struct list_head list;	/* Member of fib_rt_list */
66 };
67 
68 struct nsim_fib4_rt {
69 	struct nsim_fib_rt common;
70 	struct fib_info *fi;
71 	u8 tos;
72 	u8 type;
73 };
74 
75 struct nsim_fib6_rt {
76 	struct nsim_fib_rt common;
77 	struct list_head nh_list;
78 	unsigned int nhs;
79 };
80 
81 struct nsim_fib6_rt_nh {
82 	struct list_head list;	/* Member of nh_list */
83 	struct fib6_info *rt;
84 };
85 
86 static const struct rhashtable_params nsim_fib_rt_ht_params = {
87 	.key_offset = offsetof(struct nsim_fib_rt, key),
88 	.head_offset = offsetof(struct nsim_fib_rt, ht_node),
89 	.key_len = sizeof(struct nsim_fib_rt_key),
90 	.automatic_shrinking = true,
91 };
92 
93 struct nsim_nexthop {
94 	struct rhash_head ht_node;
95 	u64 occ;
96 	u32 id;
97 };
98 
99 static const struct rhashtable_params nsim_nexthop_ht_params = {
100 	.key_offset = offsetof(struct nsim_nexthop, id),
101 	.head_offset = offsetof(struct nsim_nexthop, ht_node),
102 	.key_len = sizeof(u32),
103 	.automatic_shrinking = true,
104 };
105 
106 u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
107 		     enum nsim_resource_id res_id, bool max)
108 {
109 	struct nsim_fib_entry *entry;
110 
111 	switch (res_id) {
112 	case NSIM_RESOURCE_IPV4_FIB:
113 		entry = &fib_data->ipv4.fib;
114 		break;
115 	case NSIM_RESOURCE_IPV4_FIB_RULES:
116 		entry = &fib_data->ipv4.rules;
117 		break;
118 	case NSIM_RESOURCE_IPV6_FIB:
119 		entry = &fib_data->ipv6.fib;
120 		break;
121 	case NSIM_RESOURCE_IPV6_FIB_RULES:
122 		entry = &fib_data->ipv6.rules;
123 		break;
124 	case NSIM_RESOURCE_NEXTHOPS:
125 		entry = &fib_data->nexthops;
126 		break;
127 	default:
128 		return 0;
129 	}
130 
131 	return max ? entry->max : entry->num;
132 }
133 
134 static void nsim_fib_set_max(struct nsim_fib_data *fib_data,
135 			     enum nsim_resource_id res_id, u64 val)
136 {
137 	struct nsim_fib_entry *entry;
138 
139 	switch (res_id) {
140 	case NSIM_RESOURCE_IPV4_FIB:
141 		entry = &fib_data->ipv4.fib;
142 		break;
143 	case NSIM_RESOURCE_IPV4_FIB_RULES:
144 		entry = &fib_data->ipv4.rules;
145 		break;
146 	case NSIM_RESOURCE_IPV6_FIB:
147 		entry = &fib_data->ipv6.fib;
148 		break;
149 	case NSIM_RESOURCE_IPV6_FIB_RULES:
150 		entry = &fib_data->ipv6.rules;
151 		break;
152 	case NSIM_RESOURCE_NEXTHOPS:
153 		entry = &fib_data->nexthops;
154 		break;
155 	default:
156 		WARN_ON(1);
157 		return;
158 	}
159 	entry->max = val;
160 }
161 
162 static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
163 				 struct netlink_ext_ack *extack)
164 {
165 	int err = 0;
166 
167 	if (add) {
168 		if (entry->num < entry->max) {
169 			entry->num++;
170 		} else {
171 			err = -ENOSPC;
172 			NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
173 		}
174 	} else {
175 		entry->num--;
176 	}
177 
178 	return err;
179 }
180 
181 static int nsim_fib_rule_event(struct nsim_fib_data *data,
182 			       struct fib_notifier_info *info, bool add)
183 {
184 	struct netlink_ext_ack *extack = info->extack;
185 	int err = 0;
186 
187 	switch (info->family) {
188 	case AF_INET:
189 		err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
190 		break;
191 	case AF_INET6:
192 		err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
193 		break;
194 	}
195 
196 	return err;
197 }
198 
199 static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
200 			    struct netlink_ext_ack *extack)
201 {
202 	int err = 0;
203 
204 	if (add) {
205 		if (entry->num < entry->max) {
206 			entry->num++;
207 		} else {
208 			err = -ENOSPC;
209 			NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
210 		}
211 	} else {
212 		entry->num--;
213 	}
214 
215 	return err;
216 }
217 
218 static void nsim_fib_rt_init(struct nsim_fib_data *data,
219 			     struct nsim_fib_rt *fib_rt, const void *addr,
220 			     size_t addr_len, unsigned int prefix_len,
221 			     int family, u32 tb_id)
222 {
223 	memcpy(fib_rt->key.addr, addr, addr_len);
224 	fib_rt->key.prefix_len = prefix_len;
225 	fib_rt->key.family = family;
226 	fib_rt->key.tb_id = tb_id;
227 	list_add(&fib_rt->list, &data->fib_rt_list);
228 }
229 
230 static void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt)
231 {
232 	list_del(&fib_rt->list);
233 }
234 
235 static struct nsim_fib_rt *nsim_fib_rt_lookup(struct rhashtable *fib_rt_ht,
236 					      const void *addr, size_t addr_len,
237 					      unsigned int prefix_len,
238 					      int family, u32 tb_id)
239 {
240 	struct nsim_fib_rt_key key;
241 
242 	memset(&key, 0, sizeof(key));
243 	memcpy(key.addr, addr, addr_len);
244 	key.prefix_len = prefix_len;
245 	key.family = family;
246 	key.tb_id = tb_id;
247 
248 	return rhashtable_lookup_fast(fib_rt_ht, &key, nsim_fib_rt_ht_params);
249 }
250 
251 static struct nsim_fib4_rt *
252 nsim_fib4_rt_create(struct nsim_fib_data *data,
253 		    struct fib_entry_notifier_info *fen_info)
254 {
255 	struct nsim_fib4_rt *fib4_rt;
256 
257 	fib4_rt = kzalloc(sizeof(*fib4_rt), GFP_ATOMIC);
258 	if (!fib4_rt)
259 		return NULL;
260 
261 	nsim_fib_rt_init(data, &fib4_rt->common, &fen_info->dst, sizeof(u32),
262 			 fen_info->dst_len, AF_INET, fen_info->tb_id);
263 
264 	fib4_rt->fi = fen_info->fi;
265 	fib_info_hold(fib4_rt->fi);
266 	fib4_rt->tos = fen_info->tos;
267 	fib4_rt->type = fen_info->type;
268 
269 	return fib4_rt;
270 }
271 
272 static void nsim_fib4_rt_destroy(struct nsim_fib4_rt *fib4_rt)
273 {
274 	fib_info_put(fib4_rt->fi);
275 	nsim_fib_rt_fini(&fib4_rt->common);
276 	kfree(fib4_rt);
277 }
278 
279 static struct nsim_fib4_rt *
280 nsim_fib4_rt_lookup(struct rhashtable *fib_rt_ht,
281 		    const struct fib_entry_notifier_info *fen_info)
282 {
283 	struct nsim_fib_rt *fib_rt;
284 
285 	fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &fen_info->dst, sizeof(u32),
286 				    fen_info->dst_len, AF_INET,
287 				    fen_info->tb_id);
288 	if (!fib_rt)
289 		return NULL;
290 
291 	return container_of(fib_rt, struct nsim_fib4_rt, common);
292 }
293 
294 static void nsim_fib4_rt_hw_flags_set(struct net *net,
295 				      const struct nsim_fib4_rt *fib4_rt,
296 				      bool trap)
297 {
298 	u32 *p_dst = (u32 *) fib4_rt->common.key.addr;
299 	int dst_len = fib4_rt->common.key.prefix_len;
300 	struct fib_rt_info fri;
301 
302 	fri.fi = fib4_rt->fi;
303 	fri.tb_id = fib4_rt->common.key.tb_id;
304 	fri.dst = cpu_to_be32(*p_dst);
305 	fri.dst_len = dst_len;
306 	fri.tos = fib4_rt->tos;
307 	fri.type = fib4_rt->type;
308 	fri.offload = false;
309 	fri.trap = trap;
310 	fib_alias_hw_flags_set(net, &fri);
311 }
312 
313 static int nsim_fib4_rt_add(struct nsim_fib_data *data,
314 			    struct nsim_fib4_rt *fib4_rt,
315 			    struct netlink_ext_ack *extack)
316 {
317 	struct net *net = devlink_net(data->devlink);
318 	int err;
319 
320 	err = nsim_fib_account(&data->ipv4.fib, true, extack);
321 	if (err)
322 		return err;
323 
324 	err = rhashtable_insert_fast(&data->fib_rt_ht,
325 				     &fib4_rt->common.ht_node,
326 				     nsim_fib_rt_ht_params);
327 	if (err) {
328 		NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv4 route");
329 		goto err_fib_dismiss;
330 	}
331 
332 	nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
333 
334 	return 0;
335 
336 err_fib_dismiss:
337 	nsim_fib_account(&data->ipv4.fib, false, extack);
338 	return err;
339 }
340 
341 static int nsim_fib4_rt_replace(struct nsim_fib_data *data,
342 				struct nsim_fib4_rt *fib4_rt,
343 				struct nsim_fib4_rt *fib4_rt_old,
344 				struct netlink_ext_ack *extack)
345 {
346 	struct net *net = devlink_net(data->devlink);
347 	int err;
348 
349 	/* We are replacing a route, so no need to change the accounting. */
350 	err = rhashtable_replace_fast(&data->fib_rt_ht,
351 				      &fib4_rt_old->common.ht_node,
352 				      &fib4_rt->common.ht_node,
353 				      nsim_fib_rt_ht_params);
354 	if (err) {
355 		NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv4 route");
356 		return err;
357 	}
358 
359 	nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
360 
361 	nsim_fib4_rt_hw_flags_set(net, fib4_rt_old, false);
362 	nsim_fib4_rt_destroy(fib4_rt_old);
363 
364 	return 0;
365 }
366 
367 static int nsim_fib4_rt_insert(struct nsim_fib_data *data,
368 			       struct fib_entry_notifier_info *fen_info)
369 {
370 	struct netlink_ext_ack *extack = fen_info->info.extack;
371 	struct nsim_fib4_rt *fib4_rt, *fib4_rt_old;
372 	int err;
373 
374 	fib4_rt = nsim_fib4_rt_create(data, fen_info);
375 	if (!fib4_rt)
376 		return -ENOMEM;
377 
378 	fib4_rt_old = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
379 	if (!fib4_rt_old)
380 		err = nsim_fib4_rt_add(data, fib4_rt, extack);
381 	else
382 		err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old, extack);
383 
384 	if (err)
385 		nsim_fib4_rt_destroy(fib4_rt);
386 
387 	return err;
388 }
389 
390 static void nsim_fib4_rt_remove(struct nsim_fib_data *data,
391 				const struct fib_entry_notifier_info *fen_info)
392 {
393 	struct netlink_ext_ack *extack = fen_info->info.extack;
394 	struct nsim_fib4_rt *fib4_rt;
395 
396 	fib4_rt = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
397 	if (WARN_ON_ONCE(!fib4_rt))
398 		return;
399 
400 	rhashtable_remove_fast(&data->fib_rt_ht, &fib4_rt->common.ht_node,
401 			       nsim_fib_rt_ht_params);
402 	nsim_fib_account(&data->ipv4.fib, false, extack);
403 	nsim_fib4_rt_destroy(fib4_rt);
404 }
405 
406 static int nsim_fib4_event(struct nsim_fib_data *data,
407 			   struct fib_notifier_info *info,
408 			   unsigned long event)
409 {
410 	struct fib_entry_notifier_info *fen_info;
411 	int err = 0;
412 
413 	fen_info = container_of(info, struct fib_entry_notifier_info, info);
414 
415 	switch (event) {
416 	case FIB_EVENT_ENTRY_REPLACE:
417 		err = nsim_fib4_rt_insert(data, fen_info);
418 		break;
419 	case FIB_EVENT_ENTRY_DEL:
420 		nsim_fib4_rt_remove(data, fen_info);
421 		break;
422 	default:
423 		break;
424 	}
425 
426 	return err;
427 }
428 
429 static struct nsim_fib6_rt_nh *
430 nsim_fib6_rt_nh_find(const struct nsim_fib6_rt *fib6_rt,
431 		     const struct fib6_info *rt)
432 {
433 	struct nsim_fib6_rt_nh *fib6_rt_nh;
434 
435 	list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list) {
436 		if (fib6_rt_nh->rt == rt)
437 			return fib6_rt_nh;
438 	}
439 
440 	return NULL;
441 }
442 
443 static int nsim_fib6_rt_nh_add(struct nsim_fib6_rt *fib6_rt,
444 			       struct fib6_info *rt)
445 {
446 	struct nsim_fib6_rt_nh *fib6_rt_nh;
447 
448 	fib6_rt_nh = kzalloc(sizeof(*fib6_rt_nh), GFP_ATOMIC);
449 	if (!fib6_rt_nh)
450 		return -ENOMEM;
451 
452 	fib6_info_hold(rt);
453 	fib6_rt_nh->rt = rt;
454 	list_add_tail(&fib6_rt_nh->list, &fib6_rt->nh_list);
455 	fib6_rt->nhs++;
456 
457 	return 0;
458 }
459 
460 static void nsim_fib6_rt_nh_del(struct nsim_fib6_rt *fib6_rt,
461 				const struct fib6_info *rt)
462 {
463 	struct nsim_fib6_rt_nh *fib6_rt_nh;
464 
465 	fib6_rt_nh = nsim_fib6_rt_nh_find(fib6_rt, rt);
466 	if (WARN_ON_ONCE(!fib6_rt_nh))
467 		return;
468 
469 	fib6_rt->nhs--;
470 	list_del(&fib6_rt_nh->list);
471 #if IS_ENABLED(CONFIG_IPV6)
472 	fib6_info_release(fib6_rt_nh->rt);
473 #endif
474 	kfree(fib6_rt_nh);
475 }
476 
477 static struct nsim_fib6_rt *
478 nsim_fib6_rt_create(struct nsim_fib_data *data,
479 		    struct fib6_entry_notifier_info *fen6_info)
480 {
481 	struct fib6_info *iter, *rt = fen6_info->rt;
482 	struct nsim_fib6_rt *fib6_rt;
483 	int i = 0;
484 	int err;
485 
486 	fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_ATOMIC);
487 	if (!fib6_rt)
488 		return ERR_PTR(-ENOMEM);
489 
490 	nsim_fib_rt_init(data, &fib6_rt->common, &rt->fib6_dst.addr,
491 			 sizeof(rt->fib6_dst.addr), rt->fib6_dst.plen, AF_INET6,
492 			 rt->fib6_table->tb6_id);
493 
494 	/* We consider a multipath IPv6 route as one entry, but it can be made
495 	 * up from several fib6_info structs (one for each nexthop), so we
496 	 * add them all to the same list under the entry.
497 	 */
498 	INIT_LIST_HEAD(&fib6_rt->nh_list);
499 
500 	err = nsim_fib6_rt_nh_add(fib6_rt, rt);
501 	if (err)
502 		goto err_fib_rt_fini;
503 
504 	if (!fen6_info->nsiblings)
505 		return fib6_rt;
506 
507 	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
508 		if (i == fen6_info->nsiblings)
509 			break;
510 
511 		err = nsim_fib6_rt_nh_add(fib6_rt, iter);
512 		if (err)
513 			goto err_fib6_rt_nh_del;
514 		i++;
515 	}
516 	WARN_ON_ONCE(i != fen6_info->nsiblings);
517 
518 	return fib6_rt;
519 
520 err_fib6_rt_nh_del:
521 	list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
522 					     fib6_siblings)
523 		nsim_fib6_rt_nh_del(fib6_rt, iter);
524 	nsim_fib6_rt_nh_del(fib6_rt, rt);
525 err_fib_rt_fini:
526 	nsim_fib_rt_fini(&fib6_rt->common);
527 	kfree(fib6_rt);
528 	return ERR_PTR(err);
529 }
530 
531 static void nsim_fib6_rt_destroy(struct nsim_fib6_rt *fib6_rt)
532 {
533 	struct nsim_fib6_rt_nh *iter, *tmp;
534 
535 	list_for_each_entry_safe(iter, tmp, &fib6_rt->nh_list, list)
536 		nsim_fib6_rt_nh_del(fib6_rt, iter->rt);
537 	WARN_ON_ONCE(!list_empty(&fib6_rt->nh_list));
538 	nsim_fib_rt_fini(&fib6_rt->common);
539 	kfree(fib6_rt);
540 }
541 
542 static struct nsim_fib6_rt *
543 nsim_fib6_rt_lookup(struct rhashtable *fib_rt_ht, const struct fib6_info *rt)
544 {
545 	struct nsim_fib_rt *fib_rt;
546 
547 	fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &rt->fib6_dst.addr,
548 				    sizeof(rt->fib6_dst.addr),
549 				    rt->fib6_dst.plen, AF_INET6,
550 				    rt->fib6_table->tb6_id);
551 	if (!fib_rt)
552 		return NULL;
553 
554 	return container_of(fib_rt, struct nsim_fib6_rt, common);
555 }
556 
557 static int nsim_fib6_rt_append(struct nsim_fib_data *data,
558 			       struct fib6_entry_notifier_info *fen6_info)
559 {
560 	struct fib6_info *iter, *rt = fen6_info->rt;
561 	struct nsim_fib6_rt *fib6_rt;
562 	int i = 0;
563 	int err;
564 
565 	fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
566 	if (WARN_ON_ONCE(!fib6_rt))
567 		return -EINVAL;
568 
569 	err = nsim_fib6_rt_nh_add(fib6_rt, rt);
570 	if (err)
571 		return err;
572 	rt->trap = true;
573 
574 	if (!fen6_info->nsiblings)
575 		return 0;
576 
577 	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
578 		if (i == fen6_info->nsiblings)
579 			break;
580 
581 		err = nsim_fib6_rt_nh_add(fib6_rt, iter);
582 		if (err)
583 			goto err_fib6_rt_nh_del;
584 		iter->trap = true;
585 		i++;
586 	}
587 	WARN_ON_ONCE(i != fen6_info->nsiblings);
588 
589 	return 0;
590 
591 err_fib6_rt_nh_del:
592 	list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
593 					     fib6_siblings) {
594 		iter->trap = false;
595 		nsim_fib6_rt_nh_del(fib6_rt, iter);
596 	}
597 	rt->trap = false;
598 	nsim_fib6_rt_nh_del(fib6_rt, rt);
599 	return err;
600 }
601 
602 static void nsim_fib6_rt_hw_flags_set(const struct nsim_fib6_rt *fib6_rt,
603 				      bool trap)
604 {
605 	struct nsim_fib6_rt_nh *fib6_rt_nh;
606 
607 	list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list)
608 		fib6_info_hw_flags_set(fib6_rt_nh->rt, false, trap);
609 }
610 
611 static int nsim_fib6_rt_add(struct nsim_fib_data *data,
612 			    struct nsim_fib6_rt *fib6_rt,
613 			    struct netlink_ext_ack *extack)
614 {
615 	int err;
616 
617 	err = nsim_fib_account(&data->ipv6.fib, true, extack);
618 	if (err)
619 		return err;
620 
621 	err = rhashtable_insert_fast(&data->fib_rt_ht,
622 				     &fib6_rt->common.ht_node,
623 				     nsim_fib_rt_ht_params);
624 	if (err) {
625 		NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv6 route");
626 		goto err_fib_dismiss;
627 	}
628 
629 	nsim_fib6_rt_hw_flags_set(fib6_rt, true);
630 
631 	return 0;
632 
633 err_fib_dismiss:
634 	nsim_fib_account(&data->ipv6.fib, false, extack);
635 	return err;
636 }
637 
638 static int nsim_fib6_rt_replace(struct nsim_fib_data *data,
639 				struct nsim_fib6_rt *fib6_rt,
640 				struct nsim_fib6_rt *fib6_rt_old,
641 				struct netlink_ext_ack *extack)
642 {
643 	int err;
644 
645 	/* We are replacing a route, so no need to change the accounting. */
646 	err = rhashtable_replace_fast(&data->fib_rt_ht,
647 				      &fib6_rt_old->common.ht_node,
648 				      &fib6_rt->common.ht_node,
649 				      nsim_fib_rt_ht_params);
650 	if (err) {
651 		NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv6 route");
652 		return err;
653 	}
654 
655 	nsim_fib6_rt_hw_flags_set(fib6_rt, true);
656 
657 	nsim_fib6_rt_hw_flags_set(fib6_rt_old, false);
658 	nsim_fib6_rt_destroy(fib6_rt_old);
659 
660 	return 0;
661 }
662 
663 static int nsim_fib6_rt_insert(struct nsim_fib_data *data,
664 			       struct fib6_entry_notifier_info *fen6_info)
665 {
666 	struct netlink_ext_ack *extack = fen6_info->info.extack;
667 	struct nsim_fib6_rt *fib6_rt, *fib6_rt_old;
668 	int err;
669 
670 	fib6_rt = nsim_fib6_rt_create(data, fen6_info);
671 	if (IS_ERR(fib6_rt))
672 		return PTR_ERR(fib6_rt);
673 
674 	fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt);
675 	if (!fib6_rt_old)
676 		err = nsim_fib6_rt_add(data, fib6_rt, extack);
677 	else
678 		err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old, extack);
679 
680 	if (err)
681 		nsim_fib6_rt_destroy(fib6_rt);
682 
683 	return err;
684 }
685 
686 static void
687 nsim_fib6_rt_remove(struct nsim_fib_data *data,
688 		    const struct fib6_entry_notifier_info *fen6_info)
689 {
690 	struct netlink_ext_ack *extack = fen6_info->info.extack;
691 	struct nsim_fib6_rt *fib6_rt;
692 
693 	/* Multipath routes are first added to the FIB trie and only then
694 	 * notified. If we vetoed the addition, we will get a delete
695 	 * notification for a route we do not have. Therefore, do not warn if
696 	 * route was not found.
697 	 */
698 	fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt);
699 	if (!fib6_rt)
700 		return;
701 
702 	/* If not all the nexthops are deleted, then only reduce the nexthop
703 	 * group.
704 	 */
705 	if (fen6_info->nsiblings + 1 != fib6_rt->nhs) {
706 		nsim_fib6_rt_nh_del(fib6_rt, fen6_info->rt);
707 		return;
708 	}
709 
710 	rhashtable_remove_fast(&data->fib_rt_ht, &fib6_rt->common.ht_node,
711 			       nsim_fib_rt_ht_params);
712 	nsim_fib_account(&data->ipv6.fib, false, extack);
713 	nsim_fib6_rt_destroy(fib6_rt);
714 }
715 
716 static int nsim_fib6_event(struct nsim_fib_data *data,
717 			   struct fib_notifier_info *info,
718 			   unsigned long event)
719 {
720 	struct fib6_entry_notifier_info *fen6_info;
721 	int err = 0;
722 
723 	fen6_info = container_of(info, struct fib6_entry_notifier_info, info);
724 
725 	if (fen6_info->rt->fib6_src.plen) {
726 		NL_SET_ERR_MSG_MOD(info->extack, "IPv6 source-specific route is not supported");
727 		return 0;
728 	}
729 
730 	switch (event) {
731 	case FIB_EVENT_ENTRY_REPLACE:
732 		err = nsim_fib6_rt_insert(data, fen6_info);
733 		break;
734 	case FIB_EVENT_ENTRY_APPEND:
735 		err = nsim_fib6_rt_append(data, fen6_info);
736 		break;
737 	case FIB_EVENT_ENTRY_DEL:
738 		nsim_fib6_rt_remove(data, fen6_info);
739 		break;
740 	default:
741 		break;
742 	}
743 
744 	return err;
745 }
746 
747 static int nsim_fib_event(struct nsim_fib_data *data,
748 			  struct fib_notifier_info *info, unsigned long event)
749 {
750 	int err = 0;
751 
752 	switch (info->family) {
753 	case AF_INET:
754 		err = nsim_fib4_event(data, info, event);
755 		break;
756 	case AF_INET6:
757 		err = nsim_fib6_event(data, info, event);
758 		break;
759 	}
760 
761 	return err;
762 }
763 
764 static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
765 			     void *ptr)
766 {
767 	struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
768 						  fib_nb);
769 	struct fib_notifier_info *info = ptr;
770 	int err = 0;
771 
772 	/* IPv6 routes can be added via RAs from softIRQ. */
773 	spin_lock_bh(&data->fib_lock);
774 
775 	switch (event) {
776 	case FIB_EVENT_RULE_ADD:
777 	case FIB_EVENT_RULE_DEL:
778 		err = nsim_fib_rule_event(data, info,
779 					  event == FIB_EVENT_RULE_ADD);
780 		break;
781 
782 	case FIB_EVENT_ENTRY_REPLACE:
783 	case FIB_EVENT_ENTRY_APPEND:
784 	case FIB_EVENT_ENTRY_DEL:
785 		err = nsim_fib_event(data, info, event);
786 		break;
787 	}
788 
789 	spin_unlock_bh(&data->fib_lock);
790 
791 	return notifier_from_errno(err);
792 }
793 
794 static void nsim_fib4_rt_free(struct nsim_fib_rt *fib_rt,
795 			      struct nsim_fib_data *data)
796 {
797 	struct devlink *devlink = data->devlink;
798 	struct nsim_fib4_rt *fib4_rt;
799 
800 	fib4_rt = container_of(fib_rt, struct nsim_fib4_rt, common);
801 	nsim_fib4_rt_hw_flags_set(devlink_net(devlink), fib4_rt, false);
802 	nsim_fib_account(&data->ipv4.fib, false, NULL);
803 	nsim_fib4_rt_destroy(fib4_rt);
804 }
805 
806 static void nsim_fib6_rt_free(struct nsim_fib_rt *fib_rt,
807 			      struct nsim_fib_data *data)
808 {
809 	struct nsim_fib6_rt *fib6_rt;
810 
811 	fib6_rt = container_of(fib_rt, struct nsim_fib6_rt, common);
812 	nsim_fib6_rt_hw_flags_set(fib6_rt, false);
813 	nsim_fib_account(&data->ipv6.fib, false, NULL);
814 	nsim_fib6_rt_destroy(fib6_rt);
815 }
816 
817 static void nsim_fib_rt_free(void *ptr, void *arg)
818 {
819 	struct nsim_fib_rt *fib_rt = ptr;
820 	struct nsim_fib_data *data = arg;
821 
822 	switch (fib_rt->key.family) {
823 	case AF_INET:
824 		nsim_fib4_rt_free(fib_rt, data);
825 		break;
826 	case AF_INET6:
827 		nsim_fib6_rt_free(fib_rt, data);
828 		break;
829 	default:
830 		WARN_ON_ONCE(1);
831 	}
832 }
833 
834 /* inconsistent dump, trying again */
835 static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
836 {
837 	struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
838 						  fib_nb);
839 	struct nsim_fib_rt *fib_rt, *fib_rt_tmp;
840 
841 	/* The notifier block is still not registered, so we do not need to
842 	 * take any locks here.
843 	 */
844 	list_for_each_entry_safe(fib_rt, fib_rt_tmp, &data->fib_rt_list, list) {
845 		rhashtable_remove_fast(&data->fib_rt_ht, &fib_rt->ht_node,
846 				       nsim_fib_rt_ht_params);
847 		nsim_fib_rt_free(fib_rt, data);
848 	}
849 
850 	data->ipv4.rules.num = 0ULL;
851 	data->ipv6.rules.num = 0ULL;
852 }
853 
854 static struct nsim_nexthop *nsim_nexthop_create(struct nsim_fib_data *data,
855 						struct nh_notifier_info *info)
856 {
857 	struct nsim_nexthop *nexthop;
858 	u64 occ = 0;
859 	int i;
860 
861 	nexthop = kzalloc(sizeof(*nexthop), GFP_KERNEL);
862 	if (!nexthop)
863 		return NULL;
864 
865 	nexthop->id = info->id;
866 
867 	/* Determine the number of nexthop entries the new nexthop will
868 	 * occupy.
869 	 */
870 
871 	if (!info->is_grp) {
872 		occ = 1;
873 		goto out;
874 	}
875 
876 	for (i = 0; i < info->nh_grp->num_nh; i++)
877 		occ += info->nh_grp->nh_entries[i].weight;
878 
879 out:
880 	nexthop->occ = occ;
881 	return nexthop;
882 }
883 
884 static void nsim_nexthop_destroy(struct nsim_nexthop *nexthop)
885 {
886 	kfree(nexthop);
887 }
888 
889 static int nsim_nexthop_account(struct nsim_fib_data *data, u64 occ,
890 				bool add, struct netlink_ext_ack *extack)
891 {
892 	int err = 0;
893 
894 	if (add) {
895 		if (data->nexthops.num + occ <= data->nexthops.max) {
896 			data->nexthops.num += occ;
897 		} else {
898 			err = -ENOSPC;
899 			NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported nexthops");
900 		}
901 	} else {
902 		if (WARN_ON(occ > data->nexthops.num))
903 			return -EINVAL;
904 		data->nexthops.num -= occ;
905 	}
906 
907 	return err;
908 }
909 
910 static int nsim_nexthop_add(struct nsim_fib_data *data,
911 			    struct nsim_nexthop *nexthop,
912 			    struct netlink_ext_ack *extack)
913 {
914 	struct net *net = devlink_net(data->devlink);
915 	int err;
916 
917 	err = nsim_nexthop_account(data, nexthop->occ, true, extack);
918 	if (err)
919 		return err;
920 
921 	err = rhashtable_insert_fast(&data->nexthop_ht, &nexthop->ht_node,
922 				     nsim_nexthop_ht_params);
923 	if (err) {
924 		NL_SET_ERR_MSG_MOD(extack, "Failed to insert nexthop");
925 		goto err_nexthop_dismiss;
926 	}
927 
928 	nexthop_set_hw_flags(net, nexthop->id, false, true);
929 
930 	return 0;
931 
932 err_nexthop_dismiss:
933 	nsim_nexthop_account(data, nexthop->occ, false, extack);
934 	return err;
935 }
936 
937 static int nsim_nexthop_replace(struct nsim_fib_data *data,
938 				struct nsim_nexthop *nexthop,
939 				struct nsim_nexthop *nexthop_old,
940 				struct netlink_ext_ack *extack)
941 {
942 	struct net *net = devlink_net(data->devlink);
943 	int err;
944 
945 	err = nsim_nexthop_account(data, nexthop->occ, true, extack);
946 	if (err)
947 		return err;
948 
949 	err = rhashtable_replace_fast(&data->nexthop_ht,
950 				      &nexthop_old->ht_node, &nexthop->ht_node,
951 				      nsim_nexthop_ht_params);
952 	if (err) {
953 		NL_SET_ERR_MSG_MOD(extack, "Failed to replace nexthop");
954 		goto err_nexthop_dismiss;
955 	}
956 
957 	nexthop_set_hw_flags(net, nexthop->id, false, true);
958 	nsim_nexthop_account(data, nexthop_old->occ, false, extack);
959 	nsim_nexthop_destroy(nexthop_old);
960 
961 	return 0;
962 
963 err_nexthop_dismiss:
964 	nsim_nexthop_account(data, nexthop->occ, false, extack);
965 	return err;
966 }
967 
968 static int nsim_nexthop_insert(struct nsim_fib_data *data,
969 			       struct nh_notifier_info *info)
970 {
971 	struct nsim_nexthop *nexthop, *nexthop_old;
972 	int err;
973 
974 	nexthop = nsim_nexthop_create(data, info);
975 	if (!nexthop)
976 		return -ENOMEM;
977 
978 	nexthop_old = rhashtable_lookup_fast(&data->nexthop_ht, &info->id,
979 					     nsim_nexthop_ht_params);
980 	if (!nexthop_old)
981 		err = nsim_nexthop_add(data, nexthop, info->extack);
982 	else
983 		err = nsim_nexthop_replace(data, nexthop, nexthop_old,
984 					   info->extack);
985 
986 	if (err)
987 		nsim_nexthop_destroy(nexthop);
988 
989 	return err;
990 }
991 
992 static void nsim_nexthop_remove(struct nsim_fib_data *data,
993 				struct nh_notifier_info *info)
994 {
995 	struct nsim_nexthop *nexthop;
996 
997 	nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &info->id,
998 					 nsim_nexthop_ht_params);
999 	if (!nexthop)
1000 		return;
1001 
1002 	rhashtable_remove_fast(&data->nexthop_ht, &nexthop->ht_node,
1003 			       nsim_nexthop_ht_params);
1004 	nsim_nexthop_account(data, nexthop->occ, false, info->extack);
1005 	nsim_nexthop_destroy(nexthop);
1006 }
1007 
1008 static int nsim_nexthop_event_nb(struct notifier_block *nb, unsigned long event,
1009 				 void *ptr)
1010 {
1011 	struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
1012 						  nexthop_nb);
1013 	struct nh_notifier_info *info = ptr;
1014 	int err = 0;
1015 
1016 	ASSERT_RTNL();
1017 
1018 	switch (event) {
1019 	case NEXTHOP_EVENT_REPLACE:
1020 		err = nsim_nexthop_insert(data, info);
1021 		break;
1022 	case NEXTHOP_EVENT_DEL:
1023 		nsim_nexthop_remove(data, info);
1024 		break;
1025 	default:
1026 		break;
1027 	}
1028 
1029 	return notifier_from_errno(err);
1030 }
1031 
1032 static void nsim_nexthop_free(void *ptr, void *arg)
1033 {
1034 	struct nsim_nexthop *nexthop = ptr;
1035 	struct nsim_fib_data *data = arg;
1036 	struct net *net;
1037 
1038 	net = devlink_net(data->devlink);
1039 	nexthop_set_hw_flags(net, nexthop->id, false, false);
1040 	nsim_nexthop_account(data, nexthop->occ, false, NULL);
1041 	nsim_nexthop_destroy(nexthop);
1042 }
1043 
1044 static u64 nsim_fib_ipv4_resource_occ_get(void *priv)
1045 {
1046 	struct nsim_fib_data *data = priv;
1047 
1048 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false);
1049 }
1050 
1051 static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv)
1052 {
1053 	struct nsim_fib_data *data = priv;
1054 
1055 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false);
1056 }
1057 
1058 static u64 nsim_fib_ipv6_resource_occ_get(void *priv)
1059 {
1060 	struct nsim_fib_data *data = priv;
1061 
1062 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false);
1063 }
1064 
1065 static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv)
1066 {
1067 	struct nsim_fib_data *data = priv;
1068 
1069 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false);
1070 }
1071 
1072 static u64 nsim_fib_nexthops_res_occ_get(void *priv)
1073 {
1074 	struct nsim_fib_data *data = priv;
1075 
1076 	return nsim_fib_get_val(data, NSIM_RESOURCE_NEXTHOPS, false);
1077 }
1078 
1079 static void nsim_fib_set_max_all(struct nsim_fib_data *data,
1080 				 struct devlink *devlink)
1081 {
1082 	enum nsim_resource_id res_ids[] = {
1083 		NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
1084 		NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES,
1085 		NSIM_RESOURCE_NEXTHOPS,
1086 	};
1087 	int i;
1088 
1089 	for (i = 0; i < ARRAY_SIZE(res_ids); i++) {
1090 		int err;
1091 		u64 val;
1092 
1093 		err = devlink_resource_size_get(devlink, res_ids[i], &val);
1094 		if (err)
1095 			val = (u64) -1;
1096 		nsim_fib_set_max(data, res_ids[i], val);
1097 	}
1098 }
1099 
1100 struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
1101 				      struct netlink_ext_ack *extack)
1102 {
1103 	struct nsim_fib_data *data;
1104 	int err;
1105 
1106 	data = kzalloc(sizeof(*data), GFP_KERNEL);
1107 	if (!data)
1108 		return ERR_PTR(-ENOMEM);
1109 	data->devlink = devlink;
1110 
1111 	err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params);
1112 	if (err)
1113 		goto err_data_free;
1114 
1115 	spin_lock_init(&data->fib_lock);
1116 	INIT_LIST_HEAD(&data->fib_rt_list);
1117 	err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params);
1118 	if (err)
1119 		goto err_rhashtable_nexthop_destroy;
1120 
1121 	nsim_fib_set_max_all(data, devlink);
1122 
1123 	data->nexthop_nb.notifier_call = nsim_nexthop_event_nb;
1124 	err = register_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb,
1125 					extack);
1126 	if (err) {
1127 		pr_err("Failed to register nexthop notifier\n");
1128 		goto err_rhashtable_fib_destroy;
1129 	}
1130 
1131 	data->fib_nb.notifier_call = nsim_fib_event_nb;
1132 	err = register_fib_notifier(devlink_net(devlink), &data->fib_nb,
1133 				    nsim_fib_dump_inconsistent, extack);
1134 	if (err) {
1135 		pr_err("Failed to register fib notifier\n");
1136 		goto err_nexthop_nb_unregister;
1137 	}
1138 
1139 	devlink_resource_occ_get_register(devlink,
1140 					  NSIM_RESOURCE_IPV4_FIB,
1141 					  nsim_fib_ipv4_resource_occ_get,
1142 					  data);
1143 	devlink_resource_occ_get_register(devlink,
1144 					  NSIM_RESOURCE_IPV4_FIB_RULES,
1145 					  nsim_fib_ipv4_rules_res_occ_get,
1146 					  data);
1147 	devlink_resource_occ_get_register(devlink,
1148 					  NSIM_RESOURCE_IPV6_FIB,
1149 					  nsim_fib_ipv6_resource_occ_get,
1150 					  data);
1151 	devlink_resource_occ_get_register(devlink,
1152 					  NSIM_RESOURCE_IPV6_FIB_RULES,
1153 					  nsim_fib_ipv6_rules_res_occ_get,
1154 					  data);
1155 	devlink_resource_occ_get_register(devlink,
1156 					  NSIM_RESOURCE_NEXTHOPS,
1157 					  nsim_fib_nexthops_res_occ_get,
1158 					  data);
1159 	return data;
1160 
1161 err_nexthop_nb_unregister:
1162 	unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
1163 err_rhashtable_fib_destroy:
1164 	rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
1165 				    data);
1166 err_rhashtable_nexthop_destroy:
1167 	rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1168 				    data);
1169 err_data_free:
1170 	kfree(data);
1171 	return ERR_PTR(err);
1172 }
1173 
1174 void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
1175 {
1176 	devlink_resource_occ_get_unregister(devlink,
1177 					    NSIM_RESOURCE_NEXTHOPS);
1178 	devlink_resource_occ_get_unregister(devlink,
1179 					    NSIM_RESOURCE_IPV6_FIB_RULES);
1180 	devlink_resource_occ_get_unregister(devlink,
1181 					    NSIM_RESOURCE_IPV6_FIB);
1182 	devlink_resource_occ_get_unregister(devlink,
1183 					    NSIM_RESOURCE_IPV4_FIB_RULES);
1184 	devlink_resource_occ_get_unregister(devlink,
1185 					    NSIM_RESOURCE_IPV4_FIB);
1186 	unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);
1187 	unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
1188 	rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
1189 				    data);
1190 	rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1191 				    data);
1192 	WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
1193 	kfree(data);
1194 }
1195