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