diff options
Diffstat (limited to 'net/mpls/af_mpls.c')
| -rw-r--r-- | net/mpls/af_mpls.c | 152 |
1 files changed, 141 insertions, 11 deletions
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index db8a2ea6d4de..1f93a5978f2a 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c | |||
| @@ -53,6 +53,11 @@ static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index) | |||
| 53 | return rt; | 53 | return rt; |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | static inline struct mpls_dev *mpls_dev_get(const struct net_device *dev) | ||
| 57 | { | ||
| 58 | return rcu_dereference_rtnl(dev->mpls_ptr); | ||
| 59 | } | ||
| 60 | |||
| 56 | static bool mpls_output_possible(const struct net_device *dev) | 61 | static bool mpls_output_possible(const struct net_device *dev) |
| 57 | { | 62 | { |
| 58 | return dev && (dev->flags & IFF_UP) && netif_carrier_ok(dev); | 63 | return dev && (dev->flags & IFF_UP) && netif_carrier_ok(dev); |
| @@ -136,6 +141,7 @@ static int mpls_forward(struct sk_buff *skb, struct net_device *dev, | |||
| 136 | struct mpls_route *rt; | 141 | struct mpls_route *rt; |
| 137 | struct mpls_entry_decoded dec; | 142 | struct mpls_entry_decoded dec; |
| 138 | struct net_device *out_dev; | 143 | struct net_device *out_dev; |
| 144 | struct mpls_dev *mdev; | ||
| 139 | unsigned int hh_len; | 145 | unsigned int hh_len; |
| 140 | unsigned int new_header_size; | 146 | unsigned int new_header_size; |
| 141 | unsigned int mtu; | 147 | unsigned int mtu; |
| @@ -143,6 +149,10 @@ static int mpls_forward(struct sk_buff *skb, struct net_device *dev, | |||
| 143 | 149 | ||
| 144 | /* Careful this entire function runs inside of an rcu critical section */ | 150 | /* Careful this entire function runs inside of an rcu critical section */ |
| 145 | 151 | ||
| 152 | mdev = mpls_dev_get(dev); | ||
| 153 | if (!mdev || !mdev->input_enabled) | ||
| 154 | goto drop; | ||
| 155 | |||
| 146 | if (skb->pkt_type != PACKET_HOST) | 156 | if (skb->pkt_type != PACKET_HOST) |
| 147 | goto drop; | 157 | goto drop; |
| 148 | 158 | ||
| @@ -352,9 +362,9 @@ static int mpls_route_add(struct mpls_route_config *cfg) | |||
| 352 | if (!dev) | 362 | if (!dev) |
| 353 | goto errout; | 363 | goto errout; |
| 354 | 364 | ||
| 355 | /* For now just support ethernet devices */ | 365 | /* Ensure this is a supported device */ |
| 356 | err = -EINVAL; | 366 | err = -EINVAL; |
| 357 | if ((dev->type != ARPHRD_ETHER) && (dev->type != ARPHRD_LOOPBACK)) | 367 | if (!mpls_dev_get(dev)) |
| 358 | goto errout; | 368 | goto errout; |
| 359 | 369 | ||
| 360 | err = -EINVAL; | 370 | err = -EINVAL; |
| @@ -428,10 +438,89 @@ errout: | |||
| 428 | return err; | 438 | return err; |
| 429 | } | 439 | } |
| 430 | 440 | ||
| 441 | #define MPLS_PERDEV_SYSCTL_OFFSET(field) \ | ||
| 442 | (&((struct mpls_dev *)0)->field) | ||
| 443 | |||
| 444 | static const struct ctl_table mpls_dev_table[] = { | ||
| 445 | { | ||
| 446 | .procname = "input", | ||
| 447 | .maxlen = sizeof(int), | ||
| 448 | .mode = 0644, | ||
| 449 | .proc_handler = proc_dointvec, | ||
| 450 | .data = MPLS_PERDEV_SYSCTL_OFFSET(input_enabled), | ||
| 451 | }, | ||
| 452 | { } | ||
| 453 | }; | ||
| 454 | |||
| 455 | static int mpls_dev_sysctl_register(struct net_device *dev, | ||
| 456 | struct mpls_dev *mdev) | ||
| 457 | { | ||
| 458 | char path[sizeof("net/mpls/conf/") + IFNAMSIZ]; | ||
| 459 | struct ctl_table *table; | ||
| 460 | int i; | ||
| 461 | |||
| 462 | table = kmemdup(&mpls_dev_table, sizeof(mpls_dev_table), GFP_KERNEL); | ||
| 463 | if (!table) | ||
| 464 | goto out; | ||
| 465 | |||
| 466 | /* Table data contains only offsets relative to the base of | ||
| 467 | * the mdev at this point, so make them absolute. | ||
| 468 | */ | ||
| 469 | for (i = 0; i < ARRAY_SIZE(mpls_dev_table); i++) | ||
| 470 | table[i].data = (char *)mdev + (uintptr_t)table[i].data; | ||
| 471 | |||
| 472 | snprintf(path, sizeof(path), "net/mpls/conf/%s", dev->name); | ||
| 473 | |||
| 474 | mdev->sysctl = register_net_sysctl(dev_net(dev), path, table); | ||
| 475 | if (!mdev->sysctl) | ||
| 476 | goto free; | ||
| 477 | |||
| 478 | return 0; | ||
| 479 | |||
| 480 | free: | ||
| 481 | kfree(table); | ||
| 482 | out: | ||
| 483 | return -ENOBUFS; | ||
| 484 | } | ||
| 485 | |||
| 486 | static void mpls_dev_sysctl_unregister(struct mpls_dev *mdev) | ||
| 487 | { | ||
| 488 | struct ctl_table *table; | ||
| 489 | |||
| 490 | table = mdev->sysctl->ctl_table_arg; | ||
| 491 | unregister_net_sysctl_table(mdev->sysctl); | ||
| 492 | kfree(table); | ||
| 493 | } | ||
| 494 | |||
| 495 | static struct mpls_dev *mpls_add_dev(struct net_device *dev) | ||
| 496 | { | ||
| 497 | struct mpls_dev *mdev; | ||
| 498 | int err = -ENOMEM; | ||
| 499 | |||
| 500 | ASSERT_RTNL(); | ||
| 501 | |||
| 502 | mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); | ||
| 503 | if (!mdev) | ||
| 504 | return ERR_PTR(err); | ||
| 505 | |||
| 506 | err = mpls_dev_sysctl_register(dev, mdev); | ||
| 507 | if (err) | ||
| 508 | goto free; | ||
| 509 | |||
| 510 | rcu_assign_pointer(dev->mpls_ptr, mdev); | ||
| 511 | |||
| 512 | return mdev; | ||
| 513 | |||
| 514 | free: | ||
| 515 | kfree(mdev); | ||
| 516 | return ERR_PTR(err); | ||
| 517 | } | ||
| 518 | |||
| 431 | static void mpls_ifdown(struct net_device *dev) | 519 | static void mpls_ifdown(struct net_device *dev) |
| 432 | { | 520 | { |
| 433 | struct mpls_route __rcu **platform_label; | 521 | struct mpls_route __rcu **platform_label; |
| 434 | struct net *net = dev_net(dev); | 522 | struct net *net = dev_net(dev); |
| 523 | struct mpls_dev *mdev; | ||
| 435 | unsigned index; | 524 | unsigned index; |
| 436 | 525 | ||
| 437 | platform_label = rtnl_dereference(net->mpls.platform_label); | 526 | platform_label = rtnl_dereference(net->mpls.platform_label); |
| @@ -443,17 +532,49 @@ static void mpls_ifdown(struct net_device *dev) | |||
| 443 | continue; | 532 | continue; |
| 444 | rt->rt_dev = NULL; | 533 | rt->rt_dev = NULL; |
| 445 | } | 534 | } |
| 535 | |||
| 536 | mdev = mpls_dev_get(dev); | ||
| 537 | if (!mdev) | ||
| 538 | return; | ||
| 539 | |||
| 540 | mpls_dev_sysctl_unregister(mdev); | ||
| 541 | |||
| 542 | RCU_INIT_POINTER(dev->mpls_ptr, NULL); | ||
| 543 | |||
| 544 | kfree_rcu(mdev, rcu); | ||
| 446 | } | 545 | } |
| 447 | 546 | ||
| 448 | static int mpls_dev_notify(struct notifier_block *this, unsigned long event, | 547 | static int mpls_dev_notify(struct notifier_block *this, unsigned long event, |
| 449 | void *ptr) | 548 | void *ptr) |
| 450 | { | 549 | { |
| 451 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | 550 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
| 551 | struct mpls_dev *mdev; | ||
| 452 | 552 | ||
| 453 | switch(event) { | 553 | switch(event) { |
| 554 | case NETDEV_REGISTER: | ||
| 555 | /* For now just support ethernet devices */ | ||
| 556 | if ((dev->type == ARPHRD_ETHER) || | ||
| 557 | (dev->type == ARPHRD_LOOPBACK)) { | ||
| 558 | mdev = mpls_add_dev(dev); | ||
| 559 | if (IS_ERR(mdev)) | ||
| 560 | return notifier_from_errno(PTR_ERR(mdev)); | ||
| 561 | } | ||
| 562 | break; | ||
| 563 | |||
| 454 | case NETDEV_UNREGISTER: | 564 | case NETDEV_UNREGISTER: |
| 455 | mpls_ifdown(dev); | 565 | mpls_ifdown(dev); |
| 456 | break; | 566 | break; |
| 567 | case NETDEV_CHANGENAME: | ||
| 568 | mdev = mpls_dev_get(dev); | ||
| 569 | if (mdev) { | ||
| 570 | int err; | ||
| 571 | |||
| 572 | mpls_dev_sysctl_unregister(mdev); | ||
| 573 | err = mpls_dev_sysctl_register(dev, mdev); | ||
| 574 | if (err) | ||
| 575 | return notifier_from_errno(err); | ||
| 576 | } | ||
| 577 | break; | ||
| 457 | } | 578 | } |
| 458 | return NOTIFY_OK; | 579 | return NOTIFY_OK; |
| 459 | } | 580 | } |
| @@ -536,6 +657,15 @@ int nla_get_labels(const struct nlattr *nla, | |||
| 536 | if ((dec.bos != bos) || dec.ttl || dec.tc) | 657 | if ((dec.bos != bos) || dec.ttl || dec.tc) |
| 537 | return -EINVAL; | 658 | return -EINVAL; |
| 538 | 659 | ||
| 660 | switch (dec.label) { | ||
| 661 | case MPLS_LABEL_IMPLNULL: | ||
| 662 | /* RFC3032: This is a label that an LSR may | ||
| 663 | * assign and distribute, but which never | ||
| 664 | * actually appears in the encapsulation. | ||
| 665 | */ | ||
| 666 | return -EINVAL; | ||
| 667 | } | ||
| 668 | |||
| 539 | label[i] = dec.label; | 669 | label[i] = dec.label; |
| 540 | } | 670 | } |
| 541 | *labels = nla_labels; | 671 | *labels = nla_labels; |
| @@ -816,7 +946,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) | |||
| 816 | } | 946 | } |
| 817 | 947 | ||
| 818 | /* In case the predefined labels need to be populated */ | 948 | /* In case the predefined labels need to be populated */ |
| 819 | if (limit > LABEL_IPV4_EXPLICIT_NULL) { | 949 | if (limit > MPLS_LABEL_IPV4NULL) { |
| 820 | struct net_device *lo = net->loopback_dev; | 950 | struct net_device *lo = net->loopback_dev; |
| 821 | rt0 = mpls_rt_alloc(lo->addr_len); | 951 | rt0 = mpls_rt_alloc(lo->addr_len); |
| 822 | if (!rt0) | 952 | if (!rt0) |
| @@ -826,7 +956,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) | |||
| 826 | rt0->rt_via_table = NEIGH_LINK_TABLE; | 956 | rt0->rt_via_table = NEIGH_LINK_TABLE; |
| 827 | memcpy(rt0->rt_via, lo->dev_addr, lo->addr_len); | 957 | memcpy(rt0->rt_via, lo->dev_addr, lo->addr_len); |
| 828 | } | 958 | } |
| 829 | if (limit > LABEL_IPV6_EXPLICIT_NULL) { | 959 | if (limit > MPLS_LABEL_IPV6NULL) { |
| 830 | struct net_device *lo = net->loopback_dev; | 960 | struct net_device *lo = net->loopback_dev; |
| 831 | rt2 = mpls_rt_alloc(lo->addr_len); | 961 | rt2 = mpls_rt_alloc(lo->addr_len); |
| 832 | if (!rt2) | 962 | if (!rt2) |
| @@ -854,15 +984,15 @@ static int resize_platform_label_table(struct net *net, size_t limit) | |||
| 854 | memcpy(labels, old, cp_size); | 984 | memcpy(labels, old, cp_size); |
| 855 | 985 | ||
| 856 | /* If needed set the predefined labels */ | 986 | /* If needed set the predefined labels */ |
| 857 | if ((old_limit <= LABEL_IPV6_EXPLICIT_NULL) && | 987 | if ((old_limit <= MPLS_LABEL_IPV6NULL) && |
| 858 | (limit > LABEL_IPV6_EXPLICIT_NULL)) { | 988 | (limit > MPLS_LABEL_IPV6NULL)) { |
| 859 | RCU_INIT_POINTER(labels[LABEL_IPV6_EXPLICIT_NULL], rt2); | 989 | RCU_INIT_POINTER(labels[MPLS_LABEL_IPV6NULL], rt2); |
| 860 | rt2 = NULL; | 990 | rt2 = NULL; |
| 861 | } | 991 | } |
| 862 | 992 | ||
| 863 | if ((old_limit <= LABEL_IPV4_EXPLICIT_NULL) && | 993 | if ((old_limit <= MPLS_LABEL_IPV4NULL) && |
| 864 | (limit > LABEL_IPV4_EXPLICIT_NULL)) { | 994 | (limit > MPLS_LABEL_IPV4NULL)) { |
| 865 | RCU_INIT_POINTER(labels[LABEL_IPV4_EXPLICIT_NULL], rt0); | 995 | RCU_INIT_POINTER(labels[MPLS_LABEL_IPV4NULL], rt0); |
| 866 | rt0 = NULL; | 996 | rt0 = NULL; |
| 867 | } | 997 | } |
| 868 | 998 | ||
| @@ -912,7 +1042,7 @@ static int mpls_platform_labels(struct ctl_table *table, int write, | |||
| 912 | return ret; | 1042 | return ret; |
| 913 | } | 1043 | } |
| 914 | 1044 | ||
| 915 | static struct ctl_table mpls_table[] = { | 1045 | static const struct ctl_table mpls_table[] = { |
| 916 | { | 1046 | { |
| 917 | .procname = "platform_labels", | 1047 | .procname = "platform_labels", |
| 918 | .data = NULL, | 1048 | .data = NULL, |
