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
31 struct nsim_fib_entry {
32 u64 max;
33 u64 num;
34 };
35
36 struct nsim_per_fib_data {
37 struct nsim_fib_entry fib;
38 struct nsim_fib_entry rules;
39 };
40
41 struct 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
51 struct 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
58 struct 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
64 struct nsim_fib4_rt {
65 struct nsim_fib_rt common;
66 struct fib_info *fi;
67 u8 tos;
68 u8 type;
69 };
70
71 struct nsim_fib6_rt {
72 struct nsim_fib_rt common;
73 struct list_head nh_list;
74 unsigned int nhs;
75 };
76
77 struct nsim_fib6_rt_nh {
78 struct list_head list; /* Member of nh_list */
79 struct fib6_info *rt;
80 };
81
82 static 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
nsim_fib_get_val(struct nsim_fib_data * fib_data,enum nsim_resource_id res_id,bool max)89 u64 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
nsim_fib_set_max(struct nsim_fib_data * fib_data,enum nsim_resource_id res_id,u64 val)114 static 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
nsim_fib_rule_account(struct nsim_fib_entry * entry,bool add,struct netlink_ext_ack * extack)139 static 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
nsim_fib_rule_event(struct nsim_fib_data * data,struct fib_notifier_info * info,bool add)158 static 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
nsim_fib_account(struct nsim_fib_entry * entry,bool add,struct netlink_ext_ack * extack)176 static 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
nsim_fib_rt_init(struct nsim_fib_data * data,struct nsim_fib_rt * fib_rt,const void * addr,size_t addr_len,unsigned int prefix_len,int family,u32 tb_id)195 static 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
nsim_fib_rt_fini(struct nsim_fib_rt * fib_rt)207 static void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt)
208 {
209 list_del(&fib_rt->list);
210 }
211
nsim_fib_rt_lookup(struct rhashtable * fib_rt_ht,const void * addr,size_t addr_len,unsigned int prefix_len,int family,u32 tb_id)212 static 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
228 static struct nsim_fib4_rt *
nsim_fib4_rt_create(struct nsim_fib_data * data,struct fib_entry_notifier_info * fen_info)229 nsim_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
nsim_fib4_rt_destroy(struct nsim_fib4_rt * fib4_rt)249 static 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
256 static struct nsim_fib4_rt *
nsim_fib4_rt_lookup(struct rhashtable * fib_rt_ht,const struct fib_entry_notifier_info * fen_info)257 nsim_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
nsim_fib4_rt_hw_flags_set(struct net * net,const struct nsim_fib4_rt * fib4_rt,bool trap)271 static 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
nsim_fib4_rt_add(struct nsim_fib_data * data,struct nsim_fib4_rt * fib4_rt,struct netlink_ext_ack * extack)290 static 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
313 err_fib_dismiss:
314 nsim_fib_account(&data->ipv4.fib, false, extack);
315 return err;
316 }
317
nsim_fib4_rt_replace(struct nsim_fib_data * data,struct nsim_fib4_rt * fib4_rt,struct nsim_fib4_rt * fib4_rt_old,struct netlink_ext_ack * extack)318 static 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
nsim_fib4_rt_insert(struct nsim_fib_data * data,struct fib_entry_notifier_info * fen_info)344 static 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
nsim_fib4_rt_remove(struct nsim_fib_data * data,const struct fib_entry_notifier_info * fen_info)367 static 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
nsim_fib4_event(struct nsim_fib_data * data,struct fib_notifier_info * info,unsigned long event)383 static 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
411 static struct nsim_fib6_rt_nh *
nsim_fib6_rt_nh_find(const struct nsim_fib6_rt * fib6_rt,const struct fib6_info * rt)412 nsim_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
nsim_fib6_rt_nh_add(struct nsim_fib6_rt * fib6_rt,struct fib6_info * rt)425 static 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
nsim_fib6_rt_nh_del(struct nsim_fib6_rt * fib6_rt,const struct fib6_info * rt)442 static 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
459 static struct nsim_fib6_rt *
nsim_fib6_rt_create(struct nsim_fib_data * data,struct fib6_entry_notifier_info * fen6_info)460 nsim_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
502 err_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);
507 err_fib_rt_fini:
508 nsim_fib_rt_fini(&fib6_rt->common);
509 kfree(fib6_rt);
510 return ERR_PTR(err);
511 }
512
nsim_fib6_rt_destroy(struct nsim_fib6_rt * fib6_rt)513 static 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
524 static struct nsim_fib6_rt *
nsim_fib6_rt_lookup(struct rhashtable * fib_rt_ht,const struct fib6_info * rt)525 nsim_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
nsim_fib6_rt_append(struct nsim_fib_data * data,struct fib6_entry_notifier_info * fen6_info)539 static 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
573 err_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
nsim_fib6_rt_hw_flags_set(const struct nsim_fib6_rt * fib6_rt,bool trap)584 static 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
nsim_fib6_rt_add(struct nsim_fib_data * data,struct nsim_fib6_rt * fib6_rt,struct netlink_ext_ack * extack)593 static 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
615 err_fib_dismiss:
616 nsim_fib_account(&data->ipv6.fib, false, extack);
617 return err;
618 }
619
nsim_fib6_rt_replace(struct nsim_fib_data * data,struct nsim_fib6_rt * fib6_rt,struct nsim_fib6_rt * fib6_rt_old,struct netlink_ext_ack * extack)620 static 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
nsim_fib6_rt_insert(struct nsim_fib_data * data,struct fib6_entry_notifier_info * fen6_info)645 static 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
668 static void
nsim_fib6_rt_remove(struct nsim_fib_data * data,const struct fib6_entry_notifier_info * fen6_info)669 nsim_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
nsim_fib6_event(struct nsim_fib_data * data,struct fib_notifier_info * info,unsigned long event)698 static 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
nsim_fib_event(struct nsim_fib_data * data,struct fib_notifier_info * info,unsigned long event)734 static 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
nsim_fib_event_nb(struct notifier_block * nb,unsigned long event,void * ptr)751 static 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
nsim_fib4_rt_free(struct nsim_fib_rt * fib_rt,struct nsim_fib_data * data)781 static 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
nsim_fib6_rt_free(struct nsim_fib_rt * fib_rt,struct nsim_fib_data * data)793 static 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
nsim_fib_rt_free(void * ptr,void * arg)804 static 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 */
nsim_fib_dump_inconsistent(struct notifier_block * nb)822 static 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
nsim_fib_ipv4_resource_occ_get(void * priv)841 static 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
nsim_fib_ipv4_rules_res_occ_get(void * priv)848 static 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
nsim_fib_ipv6_resource_occ_get(void * priv)855 static 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
nsim_fib_ipv6_rules_res_occ_get(void * priv)862 static 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
nsim_fib_set_max_all(struct nsim_fib_data * data,struct devlink * devlink)869 static 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
nsim_fib_create(struct devlink * devlink,struct netlink_ext_ack * extack)889 struct 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
934 err_rhashtable_destroy:
935 rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
936 data);
937 err_data_free:
938 kfree(data);
939 return ERR_PTR(err);
940 }
941
nsim_fib_destroy(struct devlink * devlink,struct nsim_fib_data * data)942 void 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 }
958