diff options
Diffstat (limited to 'drivers/net')
| -rw-r--r-- | drivers/net/netdevsim/dev.c | 282 | ||||
| -rw-r--r-- | drivers/net/netdevsim/netdevsim.h | 1 |
2 files changed, 282 insertions, 1 deletions
diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index a570da406d1d..c217049552f7 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c | |||
| @@ -17,11 +17,20 @@ | |||
| 17 | 17 | ||
| 18 | #include <linux/debugfs.h> | 18 | #include <linux/debugfs.h> |
| 19 | #include <linux/device.h> | 19 | #include <linux/device.h> |
| 20 | #include <linux/etherdevice.h> | ||
| 21 | #include <linux/inet.h> | ||
| 22 | #include <linux/jiffies.h> | ||
| 23 | #include <linux/kernel.h> | ||
| 20 | #include <linux/list.h> | 24 | #include <linux/list.h> |
| 21 | #include <linux/mutex.h> | 25 | #include <linux/mutex.h> |
| 22 | #include <linux/random.h> | 26 | #include <linux/random.h> |
| 23 | #include <linux/rtnetlink.h> | 27 | #include <linux/rtnetlink.h> |
| 28 | #include <linux/workqueue.h> | ||
| 24 | #include <net/devlink.h> | 29 | #include <net/devlink.h> |
| 30 | #include <net/ip.h> | ||
| 31 | #include <uapi/linux/devlink.h> | ||
| 32 | #include <uapi/linux/ip.h> | ||
| 33 | #include <uapi/linux/udp.h> | ||
| 25 | 34 | ||
| 26 | #include "netdevsim.h" | 35 | #include "netdevsim.h" |
| 27 | 36 | ||
| @@ -302,6 +311,218 @@ static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev) | |||
| 302 | devlink_region_destroy(nsim_dev->dummy_region); | 311 | devlink_region_destroy(nsim_dev->dummy_region); |
| 303 | } | 312 | } |
| 304 | 313 | ||
| 314 | struct nsim_trap_item { | ||
| 315 | void *trap_ctx; | ||
| 316 | enum devlink_trap_action action; | ||
| 317 | }; | ||
| 318 | |||
| 319 | struct nsim_trap_data { | ||
| 320 | struct delayed_work trap_report_dw; | ||
| 321 | struct nsim_trap_item *trap_items_arr; | ||
| 322 | struct nsim_dev *nsim_dev; | ||
| 323 | spinlock_t trap_lock; /* Protects trap_items_arr */ | ||
| 324 | }; | ||
| 325 | |||
| 326 | /* All driver-specific traps must be documented in | ||
| 327 | * Documentation/networking/devlink-trap-netdevsim.rst | ||
| 328 | */ | ||
| 329 | enum { | ||
| 330 | NSIM_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX, | ||
| 331 | NSIM_TRAP_ID_FID_MISS, | ||
| 332 | }; | ||
| 333 | |||
| 334 | #define NSIM_TRAP_NAME_FID_MISS "fid_miss" | ||
| 335 | |||
| 336 | #define NSIM_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT | ||
| 337 | |||
| 338 | #define NSIM_TRAP_DROP(_id, _group_id) \ | ||
| 339 | DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ | ||
| 340 | DEVLINK_TRAP_GROUP_GENERIC(_group_id), \ | ||
| 341 | NSIM_TRAP_METADATA) | ||
| 342 | #define NSIM_TRAP_EXCEPTION(_id, _group_id) \ | ||
| 343 | DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id, \ | ||
| 344 | DEVLINK_TRAP_GROUP_GENERIC(_group_id), \ | ||
| 345 | NSIM_TRAP_METADATA) | ||
| 346 | #define NSIM_TRAP_DRIVER_EXCEPTION(_id, _group_id) \ | ||
| 347 | DEVLINK_TRAP_DRIVER(EXCEPTION, TRAP, NSIM_TRAP_ID_##_id, \ | ||
| 348 | NSIM_TRAP_NAME_##_id, \ | ||
| 349 | DEVLINK_TRAP_GROUP_GENERIC(_group_id), \ | ||
| 350 | NSIM_TRAP_METADATA) | ||
| 351 | |||
| 352 | static const struct devlink_trap nsim_traps_arr[] = { | ||
| 353 | NSIM_TRAP_DROP(SMAC_MC, L2_DROPS), | ||
| 354 | NSIM_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS), | ||
| 355 | NSIM_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS), | ||
| 356 | NSIM_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS), | ||
| 357 | NSIM_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS), | ||
| 358 | NSIM_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS), | ||
| 359 | NSIM_TRAP_DRIVER_EXCEPTION(FID_MISS, L2_DROPS), | ||
| 360 | NSIM_TRAP_DROP(BLACKHOLE_ROUTE, L3_DROPS), | ||
| 361 | NSIM_TRAP_EXCEPTION(TTL_ERROR, L3_DROPS), | ||
| 362 | NSIM_TRAP_DROP(TAIL_DROP, BUFFER_DROPS), | ||
| 363 | }; | ||
| 364 | |||
| 365 | #define NSIM_TRAP_L4_DATA_LEN 100 | ||
| 366 | |||
| 367 | static struct sk_buff *nsim_dev_trap_skb_build(void) | ||
| 368 | { | ||
| 369 | int tot_len, data_len = NSIM_TRAP_L4_DATA_LEN; | ||
| 370 | struct sk_buff *skb; | ||
| 371 | struct udphdr *udph; | ||
| 372 | struct ethhdr *eth; | ||
| 373 | struct iphdr *iph; | ||
| 374 | |||
| 375 | skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); | ||
| 376 | if (!skb) | ||
| 377 | return NULL; | ||
| 378 | tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + data_len; | ||
| 379 | |||
| 380 | eth = skb_put(skb, sizeof(struct ethhdr)); | ||
| 381 | eth_random_addr(eth->h_dest); | ||
| 382 | eth_random_addr(eth->h_source); | ||
| 383 | eth->h_proto = htons(ETH_P_IP); | ||
| 384 | skb->protocol = htons(ETH_P_IP); | ||
| 385 | |||
| 386 | iph = skb_put(skb, sizeof(struct iphdr)); | ||
| 387 | iph->protocol = IPPROTO_UDP; | ||
| 388 | iph->saddr = in_aton("192.0.2.1"); | ||
| 389 | iph->daddr = in_aton("198.51.100.1"); | ||
| 390 | iph->version = 0x4; | ||
| 391 | iph->frag_off = 0; | ||
| 392 | iph->ihl = 0x5; | ||
| 393 | iph->tot_len = htons(tot_len); | ||
| 394 | iph->ttl = 100; | ||
| 395 | ip_send_check(iph); | ||
| 396 | |||
| 397 | udph = skb_put_zero(skb, sizeof(struct udphdr) + data_len); | ||
| 398 | get_random_bytes(&udph->source, sizeof(u16)); | ||
| 399 | get_random_bytes(&udph->dest, sizeof(u16)); | ||
| 400 | udph->len = htons(sizeof(struct udphdr) + data_len); | ||
| 401 | |||
| 402 | return skb; | ||
| 403 | } | ||
| 404 | |||
| 405 | static void nsim_dev_trap_report(struct nsim_dev_port *nsim_dev_port) | ||
| 406 | { | ||
| 407 | struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev; | ||
| 408 | struct devlink *devlink = priv_to_devlink(nsim_dev); | ||
| 409 | struct nsim_trap_data *nsim_trap_data; | ||
| 410 | int i; | ||
| 411 | |||
| 412 | nsim_trap_data = nsim_dev->trap_data; | ||
| 413 | |||
| 414 | spin_lock(&nsim_trap_data->trap_lock); | ||
| 415 | for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) { | ||
| 416 | struct nsim_trap_item *nsim_trap_item; | ||
| 417 | struct sk_buff *skb; | ||
| 418 | |||
| 419 | nsim_trap_item = &nsim_trap_data->trap_items_arr[i]; | ||
| 420 | if (nsim_trap_item->action == DEVLINK_TRAP_ACTION_DROP) | ||
| 421 | continue; | ||
| 422 | |||
| 423 | skb = nsim_dev_trap_skb_build(); | ||
| 424 | if (!skb) | ||
| 425 | continue; | ||
| 426 | skb->dev = nsim_dev_port->ns->netdev; | ||
| 427 | |||
| 428 | /* Trapped packets are usually passed to devlink in softIRQ, | ||
| 429 | * but in this case they are generated in a workqueue. Disable | ||
| 430 | * softIRQs to prevent lockdep from complaining about | ||
| 431 | * "incosistent lock state". | ||
| 432 | */ | ||
| 433 | local_bh_disable(); | ||
| 434 | devlink_trap_report(devlink, skb, nsim_trap_item->trap_ctx, | ||
| 435 | &nsim_dev_port->devlink_port); | ||
| 436 | local_bh_enable(); | ||
| 437 | consume_skb(skb); | ||
| 438 | } | ||
| 439 | spin_unlock(&nsim_trap_data->trap_lock); | ||
| 440 | } | ||
| 441 | |||
| 442 | #define NSIM_TRAP_REPORT_INTERVAL_MS 100 | ||
| 443 | |||
| 444 | static void nsim_dev_trap_report_work(struct work_struct *work) | ||
| 445 | { | ||
| 446 | struct nsim_trap_data *nsim_trap_data; | ||
| 447 | struct nsim_dev_port *nsim_dev_port; | ||
| 448 | struct nsim_dev *nsim_dev; | ||
| 449 | |||
| 450 | nsim_trap_data = container_of(work, struct nsim_trap_data, | ||
| 451 | trap_report_dw.work); | ||
| 452 | nsim_dev = nsim_trap_data->nsim_dev; | ||
| 453 | |||
| 454 | /* For each running port and enabled packet trap, generate a UDP | ||
| 455 | * packet with a random 5-tuple and report it. | ||
| 456 | */ | ||
| 457 | mutex_lock(&nsim_dev->port_list_lock); | ||
| 458 | list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) { | ||
| 459 | if (!netif_running(nsim_dev_port->ns->netdev)) | ||
| 460 | continue; | ||
| 461 | |||
| 462 | nsim_dev_trap_report(nsim_dev_port); | ||
| 463 | } | ||
| 464 | mutex_unlock(&nsim_dev->port_list_lock); | ||
| 465 | |||
| 466 | schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, | ||
| 467 | msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS)); | ||
| 468 | } | ||
| 469 | |||
| 470 | static int nsim_dev_traps_init(struct devlink *devlink) | ||
| 471 | { | ||
| 472 | struct nsim_dev *nsim_dev = devlink_priv(devlink); | ||
| 473 | struct nsim_trap_data *nsim_trap_data; | ||
| 474 | int err; | ||
| 475 | |||
| 476 | nsim_trap_data = kzalloc(sizeof(*nsim_trap_data), GFP_KERNEL); | ||
| 477 | if (!nsim_trap_data) | ||
| 478 | return -ENOMEM; | ||
| 479 | |||
| 480 | nsim_trap_data->trap_items_arr = kcalloc(ARRAY_SIZE(nsim_traps_arr), | ||
| 481 | sizeof(struct nsim_trap_item), | ||
| 482 | GFP_KERNEL); | ||
| 483 | if (!nsim_trap_data->trap_items_arr) { | ||
| 484 | err = -ENOMEM; | ||
| 485 | goto err_trap_data_free; | ||
| 486 | } | ||
| 487 | |||
| 488 | /* The lock is used to protect the action state of the registered | ||
| 489 | * traps. The value is written by user and read in delayed work when | ||
| 490 | * iterating over all the traps. | ||
| 491 | */ | ||
| 492 | spin_lock_init(&nsim_trap_data->trap_lock); | ||
| 493 | nsim_trap_data->nsim_dev = nsim_dev; | ||
| 494 | nsim_dev->trap_data = nsim_trap_data; | ||
| 495 | |||
| 496 | err = devlink_traps_register(devlink, nsim_traps_arr, | ||
| 497 | ARRAY_SIZE(nsim_traps_arr), NULL); | ||
| 498 | if (err) | ||
| 499 | goto err_trap_items_free; | ||
| 500 | |||
| 501 | INIT_DELAYED_WORK(&nsim_dev->trap_data->trap_report_dw, | ||
| 502 | nsim_dev_trap_report_work); | ||
| 503 | schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, | ||
| 504 | msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS)); | ||
| 505 | |||
| 506 | return 0; | ||
| 507 | |||
| 508 | err_trap_items_free: | ||
| 509 | kfree(nsim_trap_data->trap_items_arr); | ||
| 510 | err_trap_data_free: | ||
| 511 | kfree(nsim_trap_data); | ||
| 512 | return err; | ||
| 513 | } | ||
| 514 | |||
| 515 | static void nsim_dev_traps_exit(struct devlink *devlink) | ||
| 516 | { | ||
| 517 | struct nsim_dev *nsim_dev = devlink_priv(devlink); | ||
| 518 | |||
| 519 | cancel_delayed_work_sync(&nsim_dev->trap_data->trap_report_dw); | ||
| 520 | devlink_traps_unregister(devlink, nsim_traps_arr, | ||
| 521 | ARRAY_SIZE(nsim_traps_arr)); | ||
| 522 | kfree(nsim_dev->trap_data->trap_items_arr); | ||
| 523 | kfree(nsim_dev->trap_data); | ||
| 524 | } | ||
| 525 | |||
| 305 | static int nsim_dev_reload(struct devlink *devlink, | 526 | static int nsim_dev_reload(struct devlink *devlink, |
| 306 | struct netlink_ext_ack *extack) | 527 | struct netlink_ext_ack *extack) |
| 307 | { | 528 | { |
| @@ -369,9 +590,61 @@ static int nsim_dev_flash_update(struct devlink *devlink, const char *file_name, | |||
| 369 | return 0; | 590 | return 0; |
| 370 | } | 591 | } |
| 371 | 592 | ||
| 593 | static struct nsim_trap_item * | ||
| 594 | nsim_dev_trap_item_lookup(struct nsim_dev *nsim_dev, u16 trap_id) | ||
| 595 | { | ||
| 596 | struct nsim_trap_data *nsim_trap_data = nsim_dev->trap_data; | ||
| 597 | int i; | ||
| 598 | |||
| 599 | for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) { | ||
| 600 | if (nsim_traps_arr[i].id == trap_id) | ||
| 601 | return &nsim_trap_data->trap_items_arr[i]; | ||
| 602 | } | ||
| 603 | |||
| 604 | return NULL; | ||
| 605 | } | ||
| 606 | |||
| 607 | static int nsim_dev_devlink_trap_init(struct devlink *devlink, | ||
| 608 | const struct devlink_trap *trap, | ||
| 609 | void *trap_ctx) | ||
| 610 | { | ||
| 611 | struct nsim_dev *nsim_dev = devlink_priv(devlink); | ||
| 612 | struct nsim_trap_item *nsim_trap_item; | ||
| 613 | |||
| 614 | nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id); | ||
| 615 | if (WARN_ON(!nsim_trap_item)) | ||
| 616 | return -ENOENT; | ||
| 617 | |||
| 618 | nsim_trap_item->trap_ctx = trap_ctx; | ||
| 619 | nsim_trap_item->action = trap->init_action; | ||
| 620 | |||
| 621 | return 0; | ||
| 622 | } | ||
| 623 | |||
| 624 | static int | ||
| 625 | nsim_dev_devlink_trap_action_set(struct devlink *devlink, | ||
| 626 | const struct devlink_trap *trap, | ||
| 627 | enum devlink_trap_action action) | ||
| 628 | { | ||
| 629 | struct nsim_dev *nsim_dev = devlink_priv(devlink); | ||
| 630 | struct nsim_trap_item *nsim_trap_item; | ||
| 631 | |||
| 632 | nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id); | ||
| 633 | if (WARN_ON(!nsim_trap_item)) | ||
| 634 | return -ENOENT; | ||
| 635 | |||
| 636 | spin_lock(&nsim_dev->trap_data->trap_lock); | ||
| 637 | nsim_trap_item->action = action; | ||
| 638 | spin_unlock(&nsim_dev->trap_data->trap_lock); | ||
| 639 | |||
| 640 | return 0; | ||
| 641 | } | ||
| 642 | |||
| 372 | static const struct devlink_ops nsim_dev_devlink_ops = { | 643 | static const struct devlink_ops nsim_dev_devlink_ops = { |
| 373 | .reload = nsim_dev_reload, | 644 | .reload = nsim_dev_reload, |
| 374 | .flash_update = nsim_dev_flash_update, | 645 | .flash_update = nsim_dev_flash_update, |
| 646 | .trap_init = nsim_dev_devlink_trap_init, | ||
| 647 | .trap_action_set = nsim_dev_devlink_trap_action_set, | ||
| 375 | }; | 648 | }; |
| 376 | 649 | ||
| 377 | #define NSIM_DEV_MAX_MACS_DEFAULT 32 | 650 | #define NSIM_DEV_MAX_MACS_DEFAULT 32 |
| @@ -421,10 +694,14 @@ nsim_dev_create(struct nsim_bus_dev *nsim_bus_dev, unsigned int port_count) | |||
| 421 | if (err) | 694 | if (err) |
| 422 | goto err_params_unregister; | 695 | goto err_params_unregister; |
| 423 | 696 | ||
| 424 | err = nsim_dev_debugfs_init(nsim_dev); | 697 | err = nsim_dev_traps_init(devlink); |
| 425 | if (err) | 698 | if (err) |
| 426 | goto err_dummy_region_exit; | 699 | goto err_dummy_region_exit; |
| 427 | 700 | ||
| 701 | err = nsim_dev_debugfs_init(nsim_dev); | ||
| 702 | if (err) | ||
| 703 | goto err_traps_exit; | ||
| 704 | |||
| 428 | err = nsim_bpf_dev_init(nsim_dev); | 705 | err = nsim_bpf_dev_init(nsim_dev); |
| 429 | if (err) | 706 | if (err) |
| 430 | goto err_debugfs_exit; | 707 | goto err_debugfs_exit; |
| @@ -434,6 +711,8 @@ nsim_dev_create(struct nsim_bus_dev *nsim_bus_dev, unsigned int port_count) | |||
| 434 | 711 | ||
| 435 | err_debugfs_exit: | 712 | err_debugfs_exit: |
| 436 | nsim_dev_debugfs_exit(nsim_dev); | 713 | nsim_dev_debugfs_exit(nsim_dev); |
| 714 | err_traps_exit: | ||
| 715 | nsim_dev_traps_exit(devlink); | ||
| 437 | err_dummy_region_exit: | 716 | err_dummy_region_exit: |
| 438 | nsim_dev_dummy_region_exit(nsim_dev); | 717 | nsim_dev_dummy_region_exit(nsim_dev); |
| 439 | err_params_unregister: | 718 | err_params_unregister: |
| @@ -456,6 +735,7 @@ static void nsim_dev_destroy(struct nsim_dev *nsim_dev) | |||
| 456 | 735 | ||
| 457 | nsim_bpf_dev_exit(nsim_dev); | 736 | nsim_bpf_dev_exit(nsim_dev); |
| 458 | nsim_dev_debugfs_exit(nsim_dev); | 737 | nsim_dev_debugfs_exit(nsim_dev); |
| 738 | nsim_dev_traps_exit(devlink); | ||
| 459 | nsim_dev_dummy_region_exit(nsim_dev); | 739 | nsim_dev_dummy_region_exit(nsim_dev); |
| 460 | devlink_params_unregister(devlink, nsim_devlink_params, | 740 | devlink_params_unregister(devlink, nsim_devlink_params, |
| 461 | ARRAY_SIZE(nsim_devlink_params)); | 741 | ARRAY_SIZE(nsim_devlink_params)); |
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index 4c758c6919f5..262a6978bbca 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h | |||
| @@ -145,6 +145,7 @@ struct nsim_dev_port { | |||
| 145 | struct nsim_dev { | 145 | struct nsim_dev { |
| 146 | struct nsim_bus_dev *nsim_bus_dev; | 146 | struct nsim_bus_dev *nsim_bus_dev; |
| 147 | struct nsim_fib_data *fib_data; | 147 | struct nsim_fib_data *fib_data; |
| 148 | struct nsim_trap_data *trap_data; | ||
| 148 | struct dentry *ddir; | 149 | struct dentry *ddir; |
| 149 | struct dentry *ports_ddir; | 150 | struct dentry *ports_ddir; |
| 150 | struct bpf_offload_dev *bpf_dev; | 151 | struct bpf_offload_dev *bpf_dev; |
