Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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
 29#include "netdevsim.h"
 30
 31struct nsim_fib_entry {
 32	u64 max;
 33	u64 num;
 34};
 35
 36struct nsim_per_fib_data {
 37	struct nsim_fib_entry fib;
 38	struct nsim_fib_entry rules;
 39};
 40
 41struct nsim_fib_data {
 42	struct notifier_block fib_nb;
 43	struct nsim_per_fib_data ipv4;
 44	struct nsim_per_fib_data ipv6;
 45	struct rhashtable fib_rt_ht;
 46	struct list_head fib_rt_list;
 47	spinlock_t fib_lock;	/* Protects hashtable, list and accounting */
 48	struct devlink *devlink;
 49};
 50
 51struct nsim_fib_rt_key {
 52	unsigned char addr[sizeof(struct in6_addr)];
 53	unsigned char prefix_len;
 54	int family;
 55	u32 tb_id;
 56};
 57
 58struct nsim_fib_rt {
 59	struct nsim_fib_rt_key key;
 60	struct rhash_head ht_node;
 61	struct list_head list;	/* Member of fib_rt_list */
 62};
 63
 64struct nsim_fib4_rt {
 65	struct nsim_fib_rt common;
 66	struct fib_info *fi;
 67	u8 tos;
 68	u8 type;
 69};
 70
 71struct nsim_fib6_rt {
 72	struct nsim_fib_rt common;
 73	struct list_head nh_list;
 74	unsigned int nhs;
 75};
 76
 77struct nsim_fib6_rt_nh {
 78	struct list_head list;	/* Member of nh_list */
 79	struct fib6_info *rt;
 80};
 81
 82static const struct rhashtable_params nsim_fib_rt_ht_params = {
 83	.key_offset = offsetof(struct nsim_fib_rt, key),
 84	.head_offset = offsetof(struct nsim_fib_rt, ht_node),
 85	.key_len = sizeof(struct nsim_fib_rt_key),
 86	.automatic_shrinking = true,
 87};
 88
 89u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
 90		     enum nsim_resource_id res_id, bool max)
 91{
 92	struct nsim_fib_entry *entry;
 93
 94	switch (res_id) {
 95	case NSIM_RESOURCE_IPV4_FIB:
 96		entry = &fib_data->ipv4.fib;
 97		break;
 98	case NSIM_RESOURCE_IPV4_FIB_RULES:
 99		entry = &fib_data->ipv4.rules;
100		break;
101	case NSIM_RESOURCE_IPV6_FIB:
102		entry = &fib_data->ipv6.fib;
103		break;
104	case NSIM_RESOURCE_IPV6_FIB_RULES:
105		entry = &fib_data->ipv6.rules;
106		break;
107	default:
108		return 0;
109	}
110
111	return max ? entry->max : entry->num;
112}
113
114static void nsim_fib_set_max(struct nsim_fib_data *fib_data,
115			     enum nsim_resource_id res_id, u64 val)
116{
117	struct nsim_fib_entry *entry;
118
119	switch (res_id) {
120	case NSIM_RESOURCE_IPV4_FIB:
121		entry = &fib_data->ipv4.fib;
122		break;
123	case NSIM_RESOURCE_IPV4_FIB_RULES:
124		entry = &fib_data->ipv4.rules;
125		break;
126	case NSIM_RESOURCE_IPV6_FIB:
127		entry = &fib_data->ipv6.fib;
128		break;
129	case NSIM_RESOURCE_IPV6_FIB_RULES:
130		entry = &fib_data->ipv6.rules;
131		break;
132	default:
133		WARN_ON(1);
134		return;
135	}
136	entry->max = val;
137}
138
139static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
140				 struct netlink_ext_ack *extack)
141{
142	int err = 0;
143
144	if (add) {
145		if (entry->num < entry->max) {
146			entry->num++;
147		} else {
148			err = -ENOSPC;
149			NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
150		}
151	} else {
152		entry->num--;
153	}
154
155	return err;
156}
157
158static int nsim_fib_rule_event(struct nsim_fib_data *data,
159			       struct fib_notifier_info *info, bool add)
160{
161	struct netlink_ext_ack *extack = info->extack;
162	int err = 0;
163
164	switch (info->family) {
165	case AF_INET:
166		err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
167		break;
168	case AF_INET6:
169		err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
170		break;
171	}
172
173	return err;
174}
175
176static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
177			    struct netlink_ext_ack *extack)
178{
179	int err = 0;
180
181	if (add) {
182		if (entry->num < entry->max) {
183			entry->num++;
184		} else {
185			err = -ENOSPC;
186			NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
187		}
188	} else {
189		entry->num--;
190	}
191
192	return err;
193}
194
195static void nsim_fib_rt_init(struct nsim_fib_data *data,
196			     struct nsim_fib_rt *fib_rt, const void *addr,
197			     size_t addr_len, unsigned int prefix_len,
198			     int family, u32 tb_id)
199{
200	memcpy(fib_rt->key.addr, addr, addr_len);
201	fib_rt->key.prefix_len = prefix_len;
202	fib_rt->key.family = family;
203	fib_rt->key.tb_id = tb_id;
204	list_add(&fib_rt->list, &data->fib_rt_list);
205}
206
207static void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt)
208{
209	list_del(&fib_rt->list);
210}
211
212static struct nsim_fib_rt *nsim_fib_rt_lookup(struct rhashtable *fib_rt_ht,
213					      const void *addr, size_t addr_len,
214					      unsigned int prefix_len,
215					      int family, u32 tb_id)
216{
217	struct nsim_fib_rt_key key;
218
219	memset(&key, 0, sizeof(key));
220	memcpy(key.addr, addr, addr_len);
221	key.prefix_len = prefix_len;
222	key.family = family;
223	key.tb_id = tb_id;
224
225	return rhashtable_lookup_fast(fib_rt_ht, &key, nsim_fib_rt_ht_params);
226}
227
228static struct nsim_fib4_rt *
229nsim_fib4_rt_create(struct nsim_fib_data *data,
230		    struct fib_entry_notifier_info *fen_info)
231{
232	struct nsim_fib4_rt *fib4_rt;
233
234	fib4_rt = kzalloc(sizeof(*fib4_rt), GFP_ATOMIC);
235	if (!fib4_rt)
236		return NULL;
237
238	nsim_fib_rt_init(data, &fib4_rt->common, &fen_info->dst, sizeof(u32),
239			 fen_info->dst_len, AF_INET, fen_info->tb_id);
240
241	fib4_rt->fi = fen_info->fi;
242	fib_info_hold(fib4_rt->fi);
243	fib4_rt->tos = fen_info->tos;
244	fib4_rt->type = fen_info->type;
245
246	return fib4_rt;
247}
248
249static void nsim_fib4_rt_destroy(struct nsim_fib4_rt *fib4_rt)
250{
251	fib_info_put(fib4_rt->fi);
252	nsim_fib_rt_fini(&fib4_rt->common);
253	kfree(fib4_rt);
254}
255
256static struct nsim_fib4_rt *
257nsim_fib4_rt_lookup(struct rhashtable *fib_rt_ht,
258		    const struct fib_entry_notifier_info *fen_info)
259{
260	struct nsim_fib_rt *fib_rt;
261
262	fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &fen_info->dst, sizeof(u32),
263				    fen_info->dst_len, AF_INET,
264				    fen_info->tb_id);
265	if (!fib_rt)
266		return NULL;
267
268	return container_of(fib_rt, struct nsim_fib4_rt, common);
269}
270
271static void nsim_fib4_rt_hw_flags_set(struct net *net,
272				      const struct nsim_fib4_rt *fib4_rt,
273				      bool trap)
274{
275	u32 *p_dst = (u32 *) fib4_rt->common.key.addr;
276	int dst_len = fib4_rt->common.key.prefix_len;
277	struct fib_rt_info fri;
278
279	fri.fi = fib4_rt->fi;
280	fri.tb_id = fib4_rt->common.key.tb_id;
281	fri.dst = cpu_to_be32(*p_dst);
282	fri.dst_len = dst_len;
283	fri.tos = fib4_rt->tos;
284	fri.type = fib4_rt->type;
285	fri.offload = false;
286	fri.trap = trap;
287	fib_alias_hw_flags_set(net, &fri);
288}
289
290static int nsim_fib4_rt_add(struct nsim_fib_data *data,
291			    struct nsim_fib4_rt *fib4_rt,
292			    struct netlink_ext_ack *extack)
293{
294	struct net *net = devlink_net(data->devlink);
295	int err;
296
297	err = nsim_fib_account(&data->ipv4.fib, true, extack);
298	if (err)
299		return err;
300
301	err = rhashtable_insert_fast(&data->fib_rt_ht,
302				     &fib4_rt->common.ht_node,
303				     nsim_fib_rt_ht_params);
304	if (err) {
305		NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv4 route");
306		goto err_fib_dismiss;
307	}
308
309	nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
310
311	return 0;
312
313err_fib_dismiss:
314	nsim_fib_account(&data->ipv4.fib, false, extack);
315	return err;
316}
317
318static int nsim_fib4_rt_replace(struct nsim_fib_data *data,
319				struct nsim_fib4_rt *fib4_rt,
320				struct nsim_fib4_rt *fib4_rt_old,
321				struct netlink_ext_ack *extack)
322{
323	struct net *net = devlink_net(data->devlink);
324	int err;
325
326	/* We are replacing a route, so no need to change the accounting. */
327	err = rhashtable_replace_fast(&data->fib_rt_ht,
328				      &fib4_rt_old->common.ht_node,
329				      &fib4_rt->common.ht_node,
330				      nsim_fib_rt_ht_params);
331	if (err) {
332		NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv4 route");
333		return err;
334	}
335
336	nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
337
338	nsim_fib4_rt_hw_flags_set(net, fib4_rt_old, false);
339	nsim_fib4_rt_destroy(fib4_rt_old);
340
341	return 0;
342}
343
344static int nsim_fib4_rt_insert(struct nsim_fib_data *data,
345			       struct fib_entry_notifier_info *fen_info)
346{
347	struct netlink_ext_ack *extack = fen_info->info.extack;
348	struct nsim_fib4_rt *fib4_rt, *fib4_rt_old;
349	int err;
350
351	fib4_rt = nsim_fib4_rt_create(data, fen_info);
352	if (!fib4_rt)
353		return -ENOMEM;
354
355	fib4_rt_old = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
356	if (!fib4_rt_old)
357		err = nsim_fib4_rt_add(data, fib4_rt, extack);
358	else
359		err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old, extack);
360
361	if (err)
362		nsim_fib4_rt_destroy(fib4_rt);
363
364	return err;
365}
366
367static void nsim_fib4_rt_remove(struct nsim_fib_data *data,
368				const 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;
372
373	fib4_rt = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
374	if (WARN_ON_ONCE(!fib4_rt))
375		return;
376
377	rhashtable_remove_fast(&data->fib_rt_ht, &fib4_rt->common.ht_node,
378			       nsim_fib_rt_ht_params);
379	nsim_fib_account(&data->ipv4.fib, false, extack);
380	nsim_fib4_rt_destroy(fib4_rt);
381}
382
383static int nsim_fib4_event(struct nsim_fib_data *data,
384			   struct fib_notifier_info *info,
385			   unsigned long event)
386{
387	struct fib_entry_notifier_info *fen_info;
388	int err = 0;
389
390	fen_info = container_of(info, struct fib_entry_notifier_info, info);
391
392	if (fen_info->fi->nh) {
393		NL_SET_ERR_MSG_MOD(info->extack, "IPv4 route with nexthop objects is not supported");
394		return 0;
395	}
396
397	switch (event) {
398	case FIB_EVENT_ENTRY_REPLACE:
399		err = nsim_fib4_rt_insert(data, fen_info);
400		break;
401	case FIB_EVENT_ENTRY_DEL:
402		nsim_fib4_rt_remove(data, fen_info);
403		break;
404	default:
405		break;
406	}
407
408	return err;
409}
410
411static struct nsim_fib6_rt_nh *
412nsim_fib6_rt_nh_find(const struct nsim_fib6_rt *fib6_rt,
413		     const struct fib6_info *rt)
414{
415	struct nsim_fib6_rt_nh *fib6_rt_nh;
416
417	list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list) {
418		if (fib6_rt_nh->rt == rt)
419			return fib6_rt_nh;
420	}
421
422	return NULL;
423}
424
425static int nsim_fib6_rt_nh_add(struct nsim_fib6_rt *fib6_rt,
426			       struct fib6_info *rt)
427{
428	struct nsim_fib6_rt_nh *fib6_rt_nh;
429
430	fib6_rt_nh = kzalloc(sizeof(*fib6_rt_nh), GFP_ATOMIC);
431	if (!fib6_rt_nh)
432		return -ENOMEM;
433
434	fib6_info_hold(rt);
435	fib6_rt_nh->rt = rt;
436	list_add_tail(&fib6_rt_nh->list, &fib6_rt->nh_list);
437	fib6_rt->nhs++;
438
439	return 0;
440}
441
442static void nsim_fib6_rt_nh_del(struct nsim_fib6_rt *fib6_rt,
443				const struct fib6_info *rt)
444{
445	struct nsim_fib6_rt_nh *fib6_rt_nh;
446
447	fib6_rt_nh = nsim_fib6_rt_nh_find(fib6_rt, rt);
448	if (WARN_ON_ONCE(!fib6_rt_nh))
449		return;
450
451	fib6_rt->nhs--;
452	list_del(&fib6_rt_nh->list);
453#if IS_ENABLED(CONFIG_IPV6)
454	fib6_info_release(fib6_rt_nh->rt);
455#endif
456	kfree(fib6_rt_nh);
457}
458
459static struct nsim_fib6_rt *
460nsim_fib6_rt_create(struct nsim_fib_data *data,
461		    struct fib6_entry_notifier_info *fen6_info)
462{
463	struct fib6_info *iter, *rt = fen6_info->rt;
464	struct nsim_fib6_rt *fib6_rt;
465	int i = 0;
466	int err;
467
468	fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_ATOMIC);
469	if (!fib6_rt)
470		return ERR_PTR(-ENOMEM);
471
472	nsim_fib_rt_init(data, &fib6_rt->common, &rt->fib6_dst.addr,
473			 sizeof(rt->fib6_dst.addr), rt->fib6_dst.plen, AF_INET6,
474			 rt->fib6_table->tb6_id);
475
476	/* We consider a multipath IPv6 route as one entry, but it can be made
477	 * up from several fib6_info structs (one for each nexthop), so we
478	 * add them all to the same list under the entry.
479	 */
480	INIT_LIST_HEAD(&fib6_rt->nh_list);
481
482	err = nsim_fib6_rt_nh_add(fib6_rt, rt);
483	if (err)
484		goto err_fib_rt_fini;
485
486	if (!fen6_info->nsiblings)
487		return fib6_rt;
488
489	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
490		if (i == fen6_info->nsiblings)
491			break;
492
493		err = nsim_fib6_rt_nh_add(fib6_rt, iter);
494		if (err)
495			goto err_fib6_rt_nh_del;
496		i++;
497	}
498	WARN_ON_ONCE(i != fen6_info->nsiblings);
499
500	return fib6_rt;
501
502err_fib6_rt_nh_del:
503	list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
504					     fib6_siblings)
505		nsim_fib6_rt_nh_del(fib6_rt, iter);
506	nsim_fib6_rt_nh_del(fib6_rt, rt);
507err_fib_rt_fini:
508	nsim_fib_rt_fini(&fib6_rt->common);
509	kfree(fib6_rt);
510	return ERR_PTR(err);
511}
512
513static void nsim_fib6_rt_destroy(struct nsim_fib6_rt *fib6_rt)
514{
515	struct nsim_fib6_rt_nh *iter, *tmp;
516
517	list_for_each_entry_safe(iter, tmp, &fib6_rt->nh_list, list)
518		nsim_fib6_rt_nh_del(fib6_rt, iter->rt);
519	WARN_ON_ONCE(!list_empty(&fib6_rt->nh_list));
520	nsim_fib_rt_fini(&fib6_rt->common);
521	kfree(fib6_rt);
522}
523
524static struct nsim_fib6_rt *
525nsim_fib6_rt_lookup(struct rhashtable *fib_rt_ht, const struct fib6_info *rt)
526{
527	struct nsim_fib_rt *fib_rt;
528
529	fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &rt->fib6_dst.addr,
530				    sizeof(rt->fib6_dst.addr),
531				    rt->fib6_dst.plen, AF_INET6,
532				    rt->fib6_table->tb6_id);
533	if (!fib_rt)
534		return NULL;
535
536	return container_of(fib_rt, struct nsim_fib6_rt, common);
537}
538
539static int nsim_fib6_rt_append(struct nsim_fib_data *data,
540			       struct fib6_entry_notifier_info *fen6_info)
541{
542	struct fib6_info *iter, *rt = fen6_info->rt;
543	struct nsim_fib6_rt *fib6_rt;
544	int i = 0;
545	int err;
546
547	fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
548	if (WARN_ON_ONCE(!fib6_rt))
549		return -EINVAL;
550
551	err = nsim_fib6_rt_nh_add(fib6_rt, rt);
552	if (err)
553		return err;
554	rt->trap = true;
555
556	if (!fen6_info->nsiblings)
557		return 0;
558
559	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
560		if (i == fen6_info->nsiblings)
561			break;
562
563		err = nsim_fib6_rt_nh_add(fib6_rt, iter);
564		if (err)
565			goto err_fib6_rt_nh_del;
566		iter->trap = true;
567		i++;
568	}
569	WARN_ON_ONCE(i != fen6_info->nsiblings);
570
571	return 0;
572
573err_fib6_rt_nh_del:
574	list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
575					     fib6_siblings) {
576		iter->trap = false;
577		nsim_fib6_rt_nh_del(fib6_rt, iter);
578	}
579	rt->trap = false;
580	nsim_fib6_rt_nh_del(fib6_rt, rt);
581	return err;
582}
583
584static void nsim_fib6_rt_hw_flags_set(const struct nsim_fib6_rt *fib6_rt,
585				      bool trap)
586{
587	struct nsim_fib6_rt_nh *fib6_rt_nh;
588
589	list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list)
590		fib6_info_hw_flags_set(fib6_rt_nh->rt, false, trap);
591}
592
593static int nsim_fib6_rt_add(struct nsim_fib_data *data,
594			    struct nsim_fib6_rt *fib6_rt,
595			    struct netlink_ext_ack *extack)
596{
597	int err;
598
599	err = nsim_fib_account(&data->ipv6.fib, true, extack);
600	if (err)
601		return err;
602
603	err = rhashtable_insert_fast(&data->fib_rt_ht,
604				     &fib6_rt->common.ht_node,
605				     nsim_fib_rt_ht_params);
606	if (err) {
607		NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv6 route");
608		goto err_fib_dismiss;
609	}
610
611	nsim_fib6_rt_hw_flags_set(fib6_rt, true);
612
613	return 0;
614
615err_fib_dismiss:
616	nsim_fib_account(&data->ipv6.fib, false, extack);
617	return err;
618}
619
620static int nsim_fib6_rt_replace(struct nsim_fib_data *data,
621				struct nsim_fib6_rt *fib6_rt,
622				struct nsim_fib6_rt *fib6_rt_old,
623				struct netlink_ext_ack *extack)
624{
625	int err;
626
627	/* We are replacing a route, so no need to change the accounting. */
628	err = rhashtable_replace_fast(&data->fib_rt_ht,
629				      &fib6_rt_old->common.ht_node,
630				      &fib6_rt->common.ht_node,
631				      nsim_fib_rt_ht_params);
632	if (err) {
633		NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv6 route");
634		return err;
635	}
636
637	nsim_fib6_rt_hw_flags_set(fib6_rt, true);
638
639	nsim_fib6_rt_hw_flags_set(fib6_rt_old, false);
640	nsim_fib6_rt_destroy(fib6_rt_old);
641
642	return 0;
643}
644
645static int nsim_fib6_rt_insert(struct nsim_fib_data *data,
646			       struct fib6_entry_notifier_info *fen6_info)
647{
648	struct netlink_ext_ack *extack = fen6_info->info.extack;
649	struct nsim_fib6_rt *fib6_rt, *fib6_rt_old;
650	int err;
651
652	fib6_rt = nsim_fib6_rt_create(data, fen6_info);
653	if (IS_ERR(fib6_rt))
654		return PTR_ERR(fib6_rt);
655
656	fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt);
657	if (!fib6_rt_old)
658		err = nsim_fib6_rt_add(data, fib6_rt, extack);
659	else
660		err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old, extack);
661
662	if (err)
663		nsim_fib6_rt_destroy(fib6_rt);
664
665	return err;
666}
667
668static void
669nsim_fib6_rt_remove(struct nsim_fib_data *data,
670		    const struct fib6_entry_notifier_info *fen6_info)
671{
672	struct netlink_ext_ack *extack = fen6_info->info.extack;
673	struct nsim_fib6_rt *fib6_rt;
674
675	/* Multipath routes are first added to the FIB trie and only then
676	 * notified. If we vetoed the addition, we will get a delete
677	 * notification for a route we do not have. Therefore, do not warn if
678	 * route was not found.
679	 */
680	fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt);
681	if (!fib6_rt)
682		return;
683
684	/* If not all the nexthops are deleted, then only reduce the nexthop
685	 * group.
686	 */
687	if (fen6_info->nsiblings + 1 != fib6_rt->nhs) {
688		nsim_fib6_rt_nh_del(fib6_rt, fen6_info->rt);
689		return;
690	}
691
692	rhashtable_remove_fast(&data->fib_rt_ht, &fib6_rt->common.ht_node,
693			       nsim_fib_rt_ht_params);
694	nsim_fib_account(&data->ipv6.fib, false, extack);
695	nsim_fib6_rt_destroy(fib6_rt);
696}
697
698static int nsim_fib6_event(struct nsim_fib_data *data,
699			   struct fib_notifier_info *info,
700			   unsigned long event)
701{
702	struct fib6_entry_notifier_info *fen6_info;
703	int err = 0;
704
705	fen6_info = container_of(info, struct fib6_entry_notifier_info, info);
706
707	if (fen6_info->rt->nh) {
708		NL_SET_ERR_MSG_MOD(info->extack, "IPv6 route with nexthop objects is not supported");
709		return 0;
710	}
711
712	if (fen6_info->rt->fib6_src.plen) {
713		NL_SET_ERR_MSG_MOD(info->extack, "IPv6 source-specific route is not supported");
714		return 0;
715	}
716
717	switch (event) {
718	case FIB_EVENT_ENTRY_REPLACE:
719		err = nsim_fib6_rt_insert(data, fen6_info);
720		break;
721	case FIB_EVENT_ENTRY_APPEND:
722		err = nsim_fib6_rt_append(data, fen6_info);
723		break;
724	case FIB_EVENT_ENTRY_DEL:
725		nsim_fib6_rt_remove(data, fen6_info);
726		break;
727	default:
728		break;
729	}
730
731	return err;
732}
733
734static int nsim_fib_event(struct nsim_fib_data *data,
735			  struct fib_notifier_info *info, unsigned long event)
736{
737	int err = 0;
738
739	switch (info->family) {
740	case AF_INET:
741		err = nsim_fib4_event(data, info, event);
742		break;
743	case AF_INET6:
744		err = nsim_fib6_event(data, info, event);
745		break;
746	}
747
748	return err;
749}
750
751static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
752			     void *ptr)
753{
754	struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
755						  fib_nb);
756	struct fib_notifier_info *info = ptr;
757	int err = 0;
758
759	/* IPv6 routes can be added via RAs from softIRQ. */
760	spin_lock_bh(&data->fib_lock);
761
762	switch (event) {
763	case FIB_EVENT_RULE_ADD:
764	case FIB_EVENT_RULE_DEL:
765		err = nsim_fib_rule_event(data, info,
766					  event == FIB_EVENT_RULE_ADD);
767		break;
768
769	case FIB_EVENT_ENTRY_REPLACE:
770	case FIB_EVENT_ENTRY_APPEND:
771	case FIB_EVENT_ENTRY_DEL:
772		err = nsim_fib_event(data, info, event);
773		break;
774	}
775
776	spin_unlock_bh(&data->fib_lock);
777
778	return notifier_from_errno(err);
779}
780
781static void nsim_fib4_rt_free(struct nsim_fib_rt *fib_rt,
782			      struct nsim_fib_data *data)
783{
784	struct devlink *devlink = data->devlink;
785	struct nsim_fib4_rt *fib4_rt;
786
787	fib4_rt = container_of(fib_rt, struct nsim_fib4_rt, common);
788	nsim_fib4_rt_hw_flags_set(devlink_net(devlink), fib4_rt, false);
789	nsim_fib_account(&data->ipv4.fib, false, NULL);
790	nsim_fib4_rt_destroy(fib4_rt);
791}
792
793static void nsim_fib6_rt_free(struct nsim_fib_rt *fib_rt,
794			      struct nsim_fib_data *data)
795{
796	struct nsim_fib6_rt *fib6_rt;
797
798	fib6_rt = container_of(fib_rt, struct nsim_fib6_rt, common);
799	nsim_fib6_rt_hw_flags_set(fib6_rt, false);
800	nsim_fib_account(&data->ipv6.fib, false, NULL);
801	nsim_fib6_rt_destroy(fib6_rt);
802}
803
804static void nsim_fib_rt_free(void *ptr, void *arg)
805{
806	struct nsim_fib_rt *fib_rt = ptr;
807	struct nsim_fib_data *data = arg;
808
809	switch (fib_rt->key.family) {
810	case AF_INET:
811		nsim_fib4_rt_free(fib_rt, data);
812		break;
813	case AF_INET6:
814		nsim_fib6_rt_free(fib_rt, data);
815		break;
816	default:
817		WARN_ON_ONCE(1);
818	}
819}
820
821/* inconsistent dump, trying again */
822static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
823{
824	struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
825						  fib_nb);
826	struct nsim_fib_rt *fib_rt, *fib_rt_tmp;
827
828	/* The notifier block is still not registered, so we do not need to
829	 * take any locks here.
830	 */
831	list_for_each_entry_safe(fib_rt, fib_rt_tmp, &data->fib_rt_list, list) {
832		rhashtable_remove_fast(&data->fib_rt_ht, &fib_rt->ht_node,
833				       nsim_fib_rt_ht_params);
834		nsim_fib_rt_free(fib_rt, data);
835	}
836
837	data->ipv4.rules.num = 0ULL;
838	data->ipv6.rules.num = 0ULL;
839}
840
841static u64 nsim_fib_ipv4_resource_occ_get(void *priv)
842{
843	struct nsim_fib_data *data = priv;
844
845	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false);
846}
847
848static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv)
849{
850	struct nsim_fib_data *data = priv;
851
852	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false);
853}
854
855static u64 nsim_fib_ipv6_resource_occ_get(void *priv)
856{
857	struct nsim_fib_data *data = priv;
858
859	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false);
860}
861
862static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv)
863{
864	struct nsim_fib_data *data = priv;
865
866	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false);
867}
868
869static void nsim_fib_set_max_all(struct nsim_fib_data *data,
870				 struct devlink *devlink)
871{
872	enum nsim_resource_id res_ids[] = {
873		NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
874		NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
875	};
876	int i;
877
878	for (i = 0; i < ARRAY_SIZE(res_ids); i++) {
879		int err;
880		u64 val;
881
882		err = devlink_resource_size_get(devlink, res_ids[i], &val);
883		if (err)
884			val = (u64) -1;
885		nsim_fib_set_max(data, res_ids[i], val);
886	}
887}
888
889struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
890				      struct netlink_ext_ack *extack)
891{
892	struct nsim_fib_data *data;
893	int err;
894
895	data = kzalloc(sizeof(*data), GFP_KERNEL);
896	if (!data)
897		return ERR_PTR(-ENOMEM);
898	data->devlink = devlink;
899
900	spin_lock_init(&data->fib_lock);
901	INIT_LIST_HEAD(&data->fib_rt_list);
902	err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params);
903	if (err)
904		goto err_data_free;
905
906	nsim_fib_set_max_all(data, devlink);
907
908	data->fib_nb.notifier_call = nsim_fib_event_nb;
909	err = register_fib_notifier(devlink_net(devlink), &data->fib_nb,
910				    nsim_fib_dump_inconsistent, extack);
911	if (err) {
912		pr_err("Failed to register fib notifier\n");
913		goto err_rhashtable_destroy;
914	}
915
916	devlink_resource_occ_get_register(devlink,
917					  NSIM_RESOURCE_IPV4_FIB,
918					  nsim_fib_ipv4_resource_occ_get,
919					  data);
920	devlink_resource_occ_get_register(devlink,
921					  NSIM_RESOURCE_IPV4_FIB_RULES,
922					  nsim_fib_ipv4_rules_res_occ_get,
923					  data);
924	devlink_resource_occ_get_register(devlink,
925					  NSIM_RESOURCE_IPV6_FIB,
926					  nsim_fib_ipv6_resource_occ_get,
927					  data);
928	devlink_resource_occ_get_register(devlink,
929					  NSIM_RESOURCE_IPV6_FIB_RULES,
930					  nsim_fib_ipv6_rules_res_occ_get,
931					  data);
932	return data;
933
934err_rhashtable_destroy:
935	rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
936				    data);
937err_data_free:
938	kfree(data);
939	return ERR_PTR(err);
940}
941
942void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
943{
944	devlink_resource_occ_get_unregister(devlink,
945					    NSIM_RESOURCE_IPV6_FIB_RULES);
946	devlink_resource_occ_get_unregister(devlink,
947					    NSIM_RESOURCE_IPV6_FIB);
948	devlink_resource_occ_get_unregister(devlink,
949					    NSIM_RESOURCE_IPV4_FIB_RULES);
950	devlink_resource_occ_get_unregister(devlink,
951					    NSIM_RESOURCE_IPV4_FIB);
952	unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);
953	rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
954				    data);
955	WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
956	kfree(data);
957}