nexthop: Use enum to encode notification type
[linux-block.git] / drivers / net / netdevsim / fib.c
CommitLineData
37923ed6
DA
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
48bb9eb4
IS
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>
37923ed6
DA
23#include <net/fib_notifier.h>
24#include <net/ip_fib.h>
25#include <net/ip6_fib.h>
26#include <net/fib_rules.h>
a5facc4c 27#include <net/net_namespace.h>
8fa84742 28#include <net/nexthop.h>
37923ed6
DA
29
30#include "netdevsim.h"
31
32struct nsim_fib_entry {
33 u64 max;
34 u64 num;
35};
36
37struct nsim_per_fib_data {
38 struct nsim_fib_entry fib;
39 struct nsim_fib_entry rules;
40};
41
42struct nsim_fib_data {
a5facc4c 43 struct notifier_block fib_nb;
37923ed6
DA
44 struct nsim_per_fib_data ipv4;
45 struct nsim_per_fib_data ipv6;
35266255 46 struct nsim_fib_entry nexthops;
48bb9eb4
IS
47 struct rhashtable fib_rt_ht;
48 struct list_head fib_rt_list;
49 spinlock_t fib_lock; /* Protects hashtable, list and accounting */
8fa84742
IS
50 struct notifier_block nexthop_nb;
51 struct rhashtable nexthop_ht;
48bb9eb4
IS
52 struct devlink *devlink;
53};
54
55struct 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
62struct 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
68struct nsim_fib4_rt {
69 struct nsim_fib_rt common;
70 struct fib_info *fi;
71 u8 tos;
72 u8 type;
73};
74
75struct nsim_fib6_rt {
76 struct nsim_fib_rt common;
77 struct list_head nh_list;
78 unsigned int nhs;
79};
80
81struct nsim_fib6_rt_nh {
82 struct list_head list; /* Member of nh_list */
83 struct fib6_info *rt;
84};
85
86static 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,
37923ed6
DA
91};
92
8fa84742
IS
93struct nsim_nexthop {
94 struct rhash_head ht_node;
95 u64 occ;
96 u32 id;
97};
98
99static 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
a5facc4c
JP
106u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
107 enum nsim_resource_id res_id, bool max)
37923ed6 108{
37923ed6
DA
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;
35266255
IS
124 case NSIM_RESOURCE_NEXTHOPS:
125 entry = &fib_data->nexthops;
126 break;
37923ed6
DA
127 default:
128 return 0;
129 }
130
131 return max ? entry->max : entry->num;
132}
133
75ba029f
JP
134static void nsim_fib_set_max(struct nsim_fib_data *fib_data,
135 enum nsim_resource_id res_id, u64 val)
37923ed6 136{
37923ed6 137 struct nsim_fib_entry *entry;
37923ed6
DA
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;
35266255
IS
152 case NSIM_RESOURCE_NEXTHOPS:
153 entry = &fib_data->nexthops;
154 break;
37923ed6 155 default:
75ba029f
JP
156 WARN_ON(1);
157 return;
7fa76d77 158 }
75ba029f 159 entry->max = val;
37923ed6
DA
160}
161
162static 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
a5facc4c
JP
181static int nsim_fib_rule_event(struct nsim_fib_data *data,
182 struct fib_notifier_info *info, bool add)
37923ed6 183{
37923ed6
DA
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
199static 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
48bb9eb4
IS
218static 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
230static void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt)
231{
232 list_del(&fib_rt->list);
233}
234
235static 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
251static struct nsim_fib4_rt *
252nsim_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
272static 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
279static struct nsim_fib4_rt *
280nsim_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
294static 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
313static 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
336err_fib_dismiss:
337 nsim_fib_account(&data->ipv4.fib, false, extack);
338 return err;
339}
340
341static 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
367static 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
390static 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
406static 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
48bb9eb4
IS
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
429static struct nsim_fib6_rt_nh *
430nsim_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
443static 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
460static 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
477static struct nsim_fib6_rt *
478nsim_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)
41cdc741 488 return ERR_PTR(-ENOMEM);
48bb9eb4
IS
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
520err_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);
525err_fib_rt_fini:
526 nsim_fib_rt_fini(&fib6_rt->common);
527 kfree(fib6_rt);
528 return ERR_PTR(err);
529}
530
531static 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
542static struct nsim_fib6_rt *
543nsim_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
557static 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
591err_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
602static 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
611static 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
633err_fib_dismiss:
634 nsim_fib_account(&data->ipv6.fib, false, extack);
635 return err;
636}
637
638static 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
663static 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);
41cdc741
ED
671 if (IS_ERR(fib6_rt))
672 return PTR_ERR(fib6_rt);
48bb9eb4
IS
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
686static void
687nsim_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
716static 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
48bb9eb4
IS
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
a5facc4c 747static int nsim_fib_event(struct nsim_fib_data *data,
48bb9eb4 748 struct fib_notifier_info *info, unsigned long event)
37923ed6 749{
37923ed6
DA
750 int err = 0;
751
752 switch (info->family) {
753 case AF_INET:
48bb9eb4 754 err = nsim_fib4_event(data, info, event);
37923ed6
DA
755 break;
756 case AF_INET6:
48bb9eb4 757 err = nsim_fib6_event(data, info, event);
37923ed6
DA
758 break;
759 }
760
761 return err;
762}
763
764static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
765 void *ptr)
766{
a5facc4c
JP
767 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
768 fib_nb);
37923ed6
DA
769 struct fib_notifier_info *info = ptr;
770 int err = 0;
771
48bb9eb4
IS
772 /* IPv6 routes can be added via RAs from softIRQ. */
773 spin_lock_bh(&data->fib_lock);
774
37923ed6 775 switch (event) {
df561f66 776 case FIB_EVENT_RULE_ADD:
37923ed6 777 case FIB_EVENT_RULE_DEL:
a5facc4c
JP
778 err = nsim_fib_rule_event(data, info,
779 event == FIB_EVENT_RULE_ADD);
37923ed6
DA
780 break;
781
df561f66
GS
782 case FIB_EVENT_ENTRY_REPLACE:
783 case FIB_EVENT_ENTRY_APPEND:
37923ed6 784 case FIB_EVENT_ENTRY_DEL:
48bb9eb4 785 err = nsim_fib_event(data, info, event);
37923ed6
DA
786 break;
787 }
788
48bb9eb4
IS
789 spin_unlock_bh(&data->fib_lock);
790
37923ed6
DA
791 return notifier_from_errno(err);
792}
793
48bb9eb4
IS
794static 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
806static 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
817static 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
37923ed6
DA
834/* inconsistent dump, trying again */
835static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
836{
a5facc4c
JP
837 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
838 fib_nb);
48bb9eb4
IS
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 }
59c84b9f 849
a5facc4c 850 data->ipv4.rules.num = 0ULL;
a5facc4c
JP
851 data->ipv6.rules.num = 0ULL;
852}
59c84b9f 853
8fa84742
IS
854static 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)
09ad6bec 863 return ERR_PTR(-ENOMEM);
8fa84742
IS
864
865 nexthop->id = info->id;
866
867 /* Determine the number of nexthop entries the new nexthop will
868 * occupy.
869 */
870
09ad6bec
IS
871 switch (info->type) {
872 case NH_NOTIFIER_INFO_TYPE_SINGLE:
8fa84742 873 occ = 1;
09ad6bec
IS
874 break;
875 case NH_NOTIFIER_INFO_TYPE_GRP:
876 for (i = 0; i < info->nh_grp->num_nh; i++)
877 occ += info->nh_grp->nh_entries[i].weight;
878 break;
879 default:
880 NL_SET_ERR_MSG_MOD(info->extack, "Unsupported nexthop type");
881 kfree(nexthop);
882 return ERR_PTR(-EOPNOTSUPP);
8fa84742
IS
883 }
884
8fa84742
IS
885 nexthop->occ = occ;
886 return nexthop;
887}
888
889static void nsim_nexthop_destroy(struct nsim_nexthop *nexthop)
890{
891 kfree(nexthop);
892}
893
894static int nsim_nexthop_account(struct nsim_fib_data *data, u64 occ,
895 bool add, struct netlink_ext_ack *extack)
896{
897 int err = 0;
898
899 if (add) {
900 if (data->nexthops.num + occ <= data->nexthops.max) {
901 data->nexthops.num += occ;
902 } else {
903 err = -ENOSPC;
904 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported nexthops");
905 }
906 } else {
907 if (WARN_ON(occ > data->nexthops.num))
908 return -EINVAL;
909 data->nexthops.num -= occ;
910 }
911
912 return err;
913}
914
915static int nsim_nexthop_add(struct nsim_fib_data *data,
916 struct nsim_nexthop *nexthop,
917 struct netlink_ext_ack *extack)
918{
919 struct net *net = devlink_net(data->devlink);
920 int err;
921
922 err = nsim_nexthop_account(data, nexthop->occ, true, extack);
923 if (err)
924 return err;
925
926 err = rhashtable_insert_fast(&data->nexthop_ht, &nexthop->ht_node,
927 nsim_nexthop_ht_params);
928 if (err) {
929 NL_SET_ERR_MSG_MOD(extack, "Failed to insert nexthop");
930 goto err_nexthop_dismiss;
931 }
932
933 nexthop_set_hw_flags(net, nexthop->id, false, true);
934
935 return 0;
936
937err_nexthop_dismiss:
938 nsim_nexthop_account(data, nexthop->occ, false, extack);
939 return err;
940}
941
942static int nsim_nexthop_replace(struct nsim_fib_data *data,
943 struct nsim_nexthop *nexthop,
944 struct nsim_nexthop *nexthop_old,
945 struct netlink_ext_ack *extack)
946{
947 struct net *net = devlink_net(data->devlink);
948 int err;
949
950 err = nsim_nexthop_account(data, nexthop->occ, true, extack);
951 if (err)
952 return err;
953
954 err = rhashtable_replace_fast(&data->nexthop_ht,
955 &nexthop_old->ht_node, &nexthop->ht_node,
956 nsim_nexthop_ht_params);
957 if (err) {
958 NL_SET_ERR_MSG_MOD(extack, "Failed to replace nexthop");
959 goto err_nexthop_dismiss;
960 }
961
962 nexthop_set_hw_flags(net, nexthop->id, false, true);
963 nsim_nexthop_account(data, nexthop_old->occ, false, extack);
964 nsim_nexthop_destroy(nexthop_old);
965
966 return 0;
967
968err_nexthop_dismiss:
969 nsim_nexthop_account(data, nexthop->occ, false, extack);
970 return err;
971}
972
973static int nsim_nexthop_insert(struct nsim_fib_data *data,
974 struct nh_notifier_info *info)
975{
976 struct nsim_nexthop *nexthop, *nexthop_old;
977 int err;
978
979 nexthop = nsim_nexthop_create(data, info);
09ad6bec
IS
980 if (IS_ERR(nexthop))
981 return PTR_ERR(nexthop);
8fa84742
IS
982
983 nexthop_old = rhashtable_lookup_fast(&data->nexthop_ht, &info->id,
984 nsim_nexthop_ht_params);
985 if (!nexthop_old)
986 err = nsim_nexthop_add(data, nexthop, info->extack);
987 else
988 err = nsim_nexthop_replace(data, nexthop, nexthop_old,
989 info->extack);
990
991 if (err)
992 nsim_nexthop_destroy(nexthop);
993
994 return err;
995}
996
997static void nsim_nexthop_remove(struct nsim_fib_data *data,
998 struct nh_notifier_info *info)
999{
1000 struct nsim_nexthop *nexthop;
1001
1002 nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &info->id,
1003 nsim_nexthop_ht_params);
1004 if (!nexthop)
1005 return;
1006
1007 rhashtable_remove_fast(&data->nexthop_ht, &nexthop->ht_node,
1008 nsim_nexthop_ht_params);
1009 nsim_nexthop_account(data, nexthop->occ, false, info->extack);
1010 nsim_nexthop_destroy(nexthop);
1011}
1012
1013static int nsim_nexthop_event_nb(struct notifier_block *nb, unsigned long event,
1014 void *ptr)
1015{
1016 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
1017 nexthop_nb);
1018 struct nh_notifier_info *info = ptr;
1019 int err = 0;
1020
1021 ASSERT_RTNL();
1022
1023 switch (event) {
1024 case NEXTHOP_EVENT_REPLACE:
1025 err = nsim_nexthop_insert(data, info);
1026 break;
1027 case NEXTHOP_EVENT_DEL:
1028 nsim_nexthop_remove(data, info);
1029 break;
1030 default:
1031 break;
1032 }
1033
1034 return notifier_from_errno(err);
1035}
1036
1037static void nsim_nexthop_free(void *ptr, void *arg)
1038{
1039 struct nsim_nexthop *nexthop = ptr;
1040 struct nsim_fib_data *data = arg;
1041 struct net *net;
1042
1043 net = devlink_net(data->devlink);
1044 nexthop_set_hw_flags(net, nexthop->id, false, false);
1045 nsim_nexthop_account(data, nexthop->occ, false, NULL);
1046 nsim_nexthop_destroy(nexthop);
1047}
1048
a5facc4c
JP
1049static u64 nsim_fib_ipv4_resource_occ_get(void *priv)
1050{
1051 struct nsim_fib_data *data = priv;
37923ed6 1052
a5facc4c 1053 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false);
37923ed6
DA
1054}
1055
a5facc4c 1056static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv)
59c84b9f 1057{
a5facc4c 1058 struct nsim_fib_data *data = priv;
37923ed6 1059
a5facc4c
JP
1060 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false);
1061}
37923ed6 1062
a5facc4c
JP
1063static u64 nsim_fib_ipv6_resource_occ_get(void *priv)
1064{
1065 struct nsim_fib_data *data = priv;
37923ed6 1066
a5facc4c 1067 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false);
59c84b9f 1068}
37923ed6 1069
a5facc4c 1070static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv)
59c84b9f 1071{
a5facc4c
JP
1072 struct nsim_fib_data *data = priv;
1073
1074 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false);
5fc49422
JP
1075}
1076
35266255
IS
1077static u64 nsim_fib_nexthops_res_occ_get(void *priv)
1078{
1079 struct nsim_fib_data *data = priv;
1080
1081 return nsim_fib_get_val(data, NSIM_RESOURCE_NEXTHOPS, false);
1082}
1083
75ba029f
JP
1084static void nsim_fib_set_max_all(struct nsim_fib_data *data,
1085 struct devlink *devlink)
1086{
1087 enum nsim_resource_id res_ids[] = {
1088 NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
35266255
IS
1089 NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES,
1090 NSIM_RESOURCE_NEXTHOPS,
75ba029f
JP
1091 };
1092 int i;
1093
1094 for (i = 0; i < ARRAY_SIZE(res_ids); i++) {
1095 int err;
1096 u64 val;
1097
1098 err = devlink_resource_size_get(devlink, res_ids[i], &val);
1099 if (err)
1100 val = (u64) -1;
1101 nsim_fib_set_max(data, res_ids[i], val);
1102 }
1103}
1104
1105struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
1106 struct netlink_ext_ack *extack)
5fc49422 1107{
a5facc4c 1108 struct nsim_fib_data *data;
59c84b9f
DA
1109 int err;
1110
a5facc4c
JP
1111 data = kzalloc(sizeof(*data), GFP_KERNEL);
1112 if (!data)
1113 return ERR_PTR(-ENOMEM);
48bb9eb4
IS
1114 data->devlink = devlink;
1115
8fa84742
IS
1116 err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params);
1117 if (err)
1118 goto err_data_free;
1119
48bb9eb4
IS
1120 spin_lock_init(&data->fib_lock);
1121 INIT_LIST_HEAD(&data->fib_rt_list);
1122 err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params);
1123 if (err)
8fa84742 1124 goto err_rhashtable_nexthop_destroy;
59c84b9f 1125
75ba029f 1126 nsim_fib_set_max_all(data, devlink);
a5facc4c 1127
8fa84742
IS
1128 data->nexthop_nb.notifier_call = nsim_nexthop_event_nb;
1129 err = register_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb,
1130 extack);
1131 if (err) {
1132 pr_err("Failed to register nexthop notifier\n");
1133 goto err_rhashtable_fib_destroy;
1134 }
1135
a5facc4c 1136 data->fib_nb.notifier_call = nsim_fib_event_nb;
4f174bbc 1137 err = register_fib_notifier(devlink_net(devlink), &data->fib_nb,
75ba029f 1138 nsim_fib_dump_inconsistent, extack);
a5facc4c 1139 if (err) {
59c84b9f 1140 pr_err("Failed to register fib notifier\n");
8fa84742 1141 goto err_nexthop_nb_unregister;
59c84b9f
DA
1142 }
1143
a5facc4c
JP
1144 devlink_resource_occ_get_register(devlink,
1145 NSIM_RESOURCE_IPV4_FIB,
1146 nsim_fib_ipv4_resource_occ_get,
1147 data);
1148 devlink_resource_occ_get_register(devlink,
1149 NSIM_RESOURCE_IPV4_FIB_RULES,
1150 nsim_fib_ipv4_rules_res_occ_get,
1151 data);
1152 devlink_resource_occ_get_register(devlink,
1153 NSIM_RESOURCE_IPV6_FIB,
1154 nsim_fib_ipv6_resource_occ_get,
1155 data);
1156 devlink_resource_occ_get_register(devlink,
1157 NSIM_RESOURCE_IPV6_FIB_RULES,
1158 nsim_fib_ipv6_rules_res_occ_get,
1159 data);
35266255
IS
1160 devlink_resource_occ_get_register(devlink,
1161 NSIM_RESOURCE_NEXTHOPS,
1162 nsim_fib_nexthops_res_occ_get,
1163 data);
a5facc4c
JP
1164 return data;
1165
8fa84742
IS
1166err_nexthop_nb_unregister:
1167 unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
1168err_rhashtable_fib_destroy:
48bb9eb4
IS
1169 rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
1170 data);
8fa84742
IS
1171err_rhashtable_nexthop_destroy:
1172 rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1173 data);
48bb9eb4 1174err_data_free:
a5facc4c
JP
1175 kfree(data);
1176 return ERR_PTR(err);
1177}
1178
1179void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
1180{
35266255
IS
1181 devlink_resource_occ_get_unregister(devlink,
1182 NSIM_RESOURCE_NEXTHOPS);
a5facc4c
JP
1183 devlink_resource_occ_get_unregister(devlink,
1184 NSIM_RESOURCE_IPV6_FIB_RULES);
1185 devlink_resource_occ_get_unregister(devlink,
1186 NSIM_RESOURCE_IPV6_FIB);
1187 devlink_resource_occ_get_unregister(devlink,
1188 NSIM_RESOURCE_IPV4_FIB_RULES);
1189 devlink_resource_occ_get_unregister(devlink,
1190 NSIM_RESOURCE_IPV4_FIB);
4f174bbc 1191 unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);
8fa84742 1192 unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
48bb9eb4
IS
1193 rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
1194 data);
8fa84742
IS
1195 rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1196 data);
48bb9eb4 1197 WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
a5facc4c 1198 kfree(data);
37923ed6 1199}