diff options
author | David S. Miller <davem@davemloft.net> | 2015-04-22 14:24:55 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-04-22 14:24:55 -0400 |
commit | 0e2d686530d5e6c3c50c2c4f9ef8d6428252c35c (patch) | |
tree | e18ac4586481309da81bc43a30f3677e46a38858 | |
parent | 909d9faae2a447110aa061070145297fffe129cb (diff) | |
parent | 5a9ab0176198d91dfc153f5e6c5fdc5afa613607 (diff) |
Merge branch 'mpls'
Robert Shearman says:
====================
mpls: ABI changes for security and correctness
V2:
- don't treat loopback interfaces specially by enabling mpls by
default
These changes make mpls not be enabled by default on all
interfaces when in use for security, along with ensuring that a label
not valid as an outgoing label can be added in mpls routes.
This series contains three ABI/behaviour-affecting changes which have
been split out from "[PATCH net-next v4 0/6] mpls: Behaviour-changing
improvements" without any further modification. These changes need to
be considered for 4.1 otherwise we'll be stuck with the current
behaviour/ABI forever.
====================
Reviewed-by: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | Documentation/networking/mpls-sysctl.txt | 9 | ||||
-rw-r--r-- | include/linux/netdevice.h | 4 | ||||
-rw-r--r-- | net/mpls/af_mpls.c | 125 | ||||
-rw-r--r-- | net/mpls/internal.h | 6 |
4 files changed, 141 insertions, 3 deletions
diff --git a/Documentation/networking/mpls-sysctl.txt b/Documentation/networking/mpls-sysctl.txt index 639ddf0ece9b..9ed15f86c17c 100644 --- a/Documentation/networking/mpls-sysctl.txt +++ b/Documentation/networking/mpls-sysctl.txt | |||
@@ -18,3 +18,12 @@ platform_labels - INTEGER | |||
18 | 18 | ||
19 | Possible values: 0 - 1048575 | 19 | Possible values: 0 - 1048575 |
20 | Default: 0 | 20 | Default: 0 |
21 | |||
22 | conf/<interface>/input - BOOL | ||
23 | Control whether packets can be input on this interface. | ||
24 | |||
25 | If disabled, packets will be discarded without further | ||
26 | processing. | ||
27 | |||
28 | 0 - disabled (default) | ||
29 | not 0 - enabled | ||
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index bcbde799ec69..dae106a3a998 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -60,6 +60,7 @@ struct phy_device; | |||
60 | struct wireless_dev; | 60 | struct wireless_dev; |
61 | /* 802.15.4 specific */ | 61 | /* 802.15.4 specific */ |
62 | struct wpan_dev; | 62 | struct wpan_dev; |
63 | struct mpls_dev; | ||
63 | 64 | ||
64 | void netdev_set_default_ethtool_ops(struct net_device *dev, | 65 | void netdev_set_default_ethtool_ops(struct net_device *dev, |
65 | const struct ethtool_ops *ops); | 66 | const struct ethtool_ops *ops); |
@@ -1627,6 +1628,9 @@ struct net_device { | |||
1627 | void *ax25_ptr; | 1628 | void *ax25_ptr; |
1628 | struct wireless_dev *ieee80211_ptr; | 1629 | struct wireless_dev *ieee80211_ptr; |
1629 | struct wpan_dev *ieee802154_ptr; | 1630 | struct wpan_dev *ieee802154_ptr; |
1631 | #if IS_ENABLED(CONFIG_MPLS_ROUTING) | ||
1632 | struct mpls_dev __rcu *mpls_ptr; | ||
1633 | #endif | ||
1630 | 1634 | ||
1631 | /* | 1635 | /* |
1632 | * Cache lines mostly used on receive path (including eth_type_trans()) | 1636 | * Cache lines mostly used on receive path (including eth_type_trans()) |
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index db8a2ea6d4de..954810c76a86 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,14 +532,35 @@ 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(mdev); | ||
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; |
@@ -536,6 +646,15 @@ int nla_get_labels(const struct nlattr *nla, | |||
536 | if ((dec.bos != bos) || dec.ttl || dec.tc) | 646 | if ((dec.bos != bos) || dec.ttl || dec.tc) |
537 | return -EINVAL; | 647 | return -EINVAL; |
538 | 648 | ||
649 | switch (dec.label) { | ||
650 | case LABEL_IMPLICIT_NULL: | ||
651 | /* RFC3032: This is a label that an LSR may | ||
652 | * assign and distribute, but which never | ||
653 | * actually appears in the encapsulation. | ||
654 | */ | ||
655 | return -EINVAL; | ||
656 | } | ||
657 | |||
539 | label[i] = dec.label; | 658 | label[i] = dec.label; |
540 | } | 659 | } |
541 | *labels = nla_labels; | 660 | *labels = nla_labels; |
@@ -912,7 +1031,7 @@ static int mpls_platform_labels(struct ctl_table *table, int write, | |||
912 | return ret; | 1031 | return ret; |
913 | } | 1032 | } |
914 | 1033 | ||
915 | static struct ctl_table mpls_table[] = { | 1034 | static const struct ctl_table mpls_table[] = { |
916 | { | 1035 | { |
917 | .procname = "platform_labels", | 1036 | .procname = "platform_labels", |
918 | .data = NULL, | 1037 | .data = NULL, |
diff --git a/net/mpls/internal.h b/net/mpls/internal.h index fb6de92052c4..693877d69606 100644 --- a/net/mpls/internal.h +++ b/net/mpls/internal.h | |||
@@ -22,6 +22,12 @@ struct mpls_entry_decoded { | |||
22 | u8 bos; | 22 | u8 bos; |
23 | }; | 23 | }; |
24 | 24 | ||
25 | struct mpls_dev { | ||
26 | int input_enabled; | ||
27 | |||
28 | struct ctl_table_header *sysctl; | ||
29 | }; | ||
30 | |||
25 | struct sk_buff; | 31 | struct sk_buff; |
26 | 32 | ||
27 | static inline struct mpls_shim_hdr *mpls_hdr(const struct sk_buff *skb) | 33 | static inline struct mpls_shim_hdr *mpls_hdr(const struct sk_buff *skb) |