at v5.11-rc3 1194 lines 29 kB view raw
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 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 { 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 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, 91}; 92 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 106u64 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 134static 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 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 181static 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 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 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 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) 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 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); 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 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 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 747static 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 764static 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 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 834/* inconsistent dump, trying again */ 835static 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 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) 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 879out: 880 nexthop->occ = occ; 881 return nexthop; 882} 883 884static void nsim_nexthop_destroy(struct nsim_nexthop *nexthop) 885{ 886 kfree(nexthop); 887} 888 889static 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 910static 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 932err_nexthop_dismiss: 933 nsim_nexthop_account(data, nexthop->occ, false, extack); 934 return err; 935} 936 937static 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 963err_nexthop_dismiss: 964 nsim_nexthop_account(data, nexthop->occ, false, extack); 965 return err; 966} 967 968static 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 992static 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 1008static 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 1032static 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 1044static 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 1051static 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 1058static 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 1065static 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 1072static 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 1079static 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 1100struct 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 1161err_nexthop_nb_unregister: 1162 unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb); 1163err_rhashtable_fib_destroy: 1164 rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free, 1165 data); 1166err_rhashtable_nexthop_destroy: 1167 rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free, 1168 data); 1169err_data_free: 1170 kfree(data); 1171 return ERR_PTR(err); 1172} 1173 1174void 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}