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 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 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 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 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 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 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 207 static void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt) 208 { 209 list_del(&fib_rt->list); 210 } 211 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 * 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 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 * 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 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 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 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 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 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 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 * 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 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 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 * 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 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 * 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 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 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 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 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 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 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 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 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 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: /* fall through */ 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: /* fall through */ 770 case FIB_EVENT_ENTRY_APPEND: /* fall through */ 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 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 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 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 */ 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 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 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 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 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 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 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 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