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, |