History

rtnetlink: add support for protodown reason

netdev protodown is a mechanism that allows protocols to hold an interface down. It was initially introduced in the kernel to hold links down by a multihoming protocol. There was also an attempt to introduce protodown reason at the time but was rejected. protodown and protodown reason is supported by almost every switching and routing platform. It was ok for a while to live without a protodown reason. But, its become more critical now given more than one protocol may need to keep a link down on a system at the same time. eg: vrrp peer node, port security, multihoming protocol. Its common for Network operators and protocol developers to look for such a reason on a networking box (Its also known as errDisable by most networking operators) This patch adds support for link protodown reason attribute. There are two ways to maintain protodown reasons. (a) enumerate every possible reason code in kernel - A protocol developer has to make a request and have that appear in a certain kernel version (b) provide the bits in the kernel, and allow user-space (sysadmin or NOS distributions) to manage the bit-to-reasonname map. - This makes extending reason codes easier (kind of like the iproute2 table to vrf-name map /etc/iproute2/rt_tables.d/) This patch takes approach (b). a few things about the patch: - It treats the protodown reason bits as counter to indicate active protodown users - Since protodown attribute is already an exposed UAPI, the reason is not enforced on a protodown set. Its a no-op if not used. the patch follows the below algorithm: - presence of reason bits set indicates protodown is in use - user can set protodown and protodown reason in a single or multiple setlink operations - setlink operation to clear protodown, will return -EBUSY if there are active protodown reason bits - reason is not included in link dumps if not used example with patched iproute2: $cat /etc/iproute2/protodown_reasons.d/r.conf 0 mlag 1 evpn 2 vrrp 3 psecurity $ip link set dev vxlan0 protodown on protodown_reason vrrp on $ip link set dev vxlan0 protodown_reason mlag on $ip link show 14: vxlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether f6:06:be:17:91:e7 brd ff:ff:ff:ff:ff:ff protodown on <mlag,vrrp> $ip link set dev vxlan0 protodown_reason mlag off $ip link set dev vxlan0 protodown off protodown_reason vrrp off

Roopa Prabhu committed 6 years ago

Commit 829eb201 parent 8f3f3304 files · +147 −5Browse files

Unified · Split

4 files
  1. include/linux/netdevice.h +4 −0
  2. include/uapi/linux/if_link.h +10 −0
  3. net/core/dev.c +25 −0
  4. net/core/rtnetlink.c +108 −5
include/linux/netdevice.h+4−0
@@ -2058,6 +2058,8 @@ struct net_device { · 2057 unmodified lines
2058 struct timer_list watchdog_timer;2058 struct timer_list watchdog_timer;
2059 int watchdog_timeo;2059 int watchdog_timeo;
2060 2060
2061 u32 proto_down_reason;
2062
2061 struct list_head todo_list;2063 struct list_head todo_list;
2062 int __percpu *pcpu_refcnt;2064 int __percpu *pcpu_refcnt;
2063 2065
@@ -3810,6 +3812,8 @@ int dev_get_port_parent_id(struct net_device *dev, · 1746 unmodified lines
3810bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b);3812bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b);
3811int dev_change_proto_down(struct net_device *dev, bool proto_down);3813int dev_change_proto_down(struct net_device *dev, bool proto_down);
3812int dev_change_proto_down_generic(struct net_device *dev, bool proto_down);3814int dev_change_proto_down_generic(struct net_device *dev, bool proto_down);
3815void dev_change_proto_down_reason(struct net_device *dev, unsigned long mask,
3816 u32 value);
3813struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev, bool *again);3817struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev, bool *again);
3814struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,3818struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
3815 struct netdev_queue *txq, int *ret);3819 struct netdev_queue *txq, int *ret);
include/uapi/linux/if_link.h+10−0
@@ -170,12 +170,22 @@ enum { · 169 unmodified lines
170 IFLA_PROP_LIST,170 IFLA_PROP_LIST,
171 IFLA_ALT_IFNAME, /* Alternative ifname */171 IFLA_ALT_IFNAME, /* Alternative ifname */
172 IFLA_PERM_ADDRESS,172 IFLA_PERM_ADDRESS,
173 IFLA_PROTO_DOWN_REASON,
173 __IFLA_MAX174 __IFLA_MAX
174};175};
175 176
176 177
177#define IFLA_MAX (__IFLA_MAX - 1)178#define IFLA_MAX (__IFLA_MAX - 1)
178 179
180enum {
181 IFLA_PROTO_DOWN_REASON_UNSPEC,
182 IFLA_PROTO_DOWN_REASON_MASK, /* u32, mask for reason bits */
183 IFLA_PROTO_DOWN_REASON_VALUE, /* u32, reason bit value */
184
185 __IFLA_PROTO_DOWN_REASON_CNT,
186 IFLA_PROTO_DOWN_REASON_MAX = __IFLA_PROTO_DOWN_REASON_CNT - 1
187};
188
179/* backwards compatibility for userspace */189/* backwards compatibility for userspace */
180#ifndef __KERNEL__190#ifndef __KERNEL__
181#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))191#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
net/core/dev.c+25−0
@@ -8715,6 +8715,31 @@ int dev_change_proto_down_generic(struct net_device *dev, bool proto_down) · 8714 unmodified lines
8715}8715}
8716EXPORT_SYMBOL(dev_change_proto_down_generic);8716EXPORT_SYMBOL(dev_change_proto_down_generic);
8717 8717
8718/**
8719 * dev_change_proto_down_reason - proto down reason
8720 *
8721 * @dev: device
8722 * @mask: proto down mask
8723 * @value: proto down value
8724 */
8725void dev_change_proto_down_reason(struct net_device *dev, unsigned long mask,
8726 u32 value)
8727{
8728 int b;
8729
8730 if (!mask) {
8731 dev->proto_down_reason = value;
8732 } else {
8733 for_each_set_bit(b, &mask, 32) {
8734 if (value & (1 << b))
8735 dev->proto_down_reason |= BIT(b);
8736 else
8737 dev->proto_down_reason &= ~BIT(b);
8738 }
8739 }
8740}
8741EXPORT_SYMBOL(dev_change_proto_down_reason);
8742
8718u32 __dev_xdp_query(struct net_device *dev, bpf_op_t bpf_op,8743u32 __dev_xdp_query(struct net_device *dev, bpf_op_t bpf_op,
8719 enum bpf_netdev_command cmd)8744 enum bpf_netdev_command cmd)
8720{8745{
net/core/rtnetlink.c+108−5
@@ -1000,6 +1000,16 @@ static size_t rtnl_prop_list_size(const struct net_device *dev) · 999 unmodified lines
1000 return size;1000 return size;
1001}1001}
1002 1002
1003static size_t rtnl_proto_down_size(const struct net_device *dev)
1004{
1005 size_t size = nla_total_size(1);
1006
1007 if (dev->proto_down_reason)
1008 size += nla_total_size(0) + nla_total_size(4);
1009
1010 return size;
1011}
1012
1003static noinline size_t if_nlmsg_size(const struct net_device *dev,1013static noinline size_t if_nlmsg_size(const struct net_device *dev,
1004 u32 ext_filter_mask)1014 u32 ext_filter_mask)
1005{1015{
@@ -1041,7 +1051,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, · 35 unmodified lines
1041 + nla_total_size(4) /* IFLA_EVENT */1051 + nla_total_size(4) /* IFLA_EVENT */
1042 + nla_total_size(4) /* IFLA_NEW_NETNSID */1052 + nla_total_size(4) /* IFLA_NEW_NETNSID */
1043 + nla_total_size(4) /* IFLA_NEW_IFINDEX */1053 + nla_total_size(4) /* IFLA_NEW_IFINDEX */
1044 + nla_total_size(1) /* IFLA_PROTO_DOWN */1054 + rtnl_proto_down_size(dev) /* proto down */
1045 + nla_total_size(4) /* IFLA_TARGET_NETNSID */1055 + nla_total_size(4) /* IFLA_TARGET_NETNSID */
1046 + nla_total_size(4) /* IFLA_CARRIER_UP_COUNT */1056 + nla_total_size(4) /* IFLA_CARRIER_UP_COUNT */
1047 + nla_total_size(4) /* IFLA_CARRIER_DOWN_COUNT */1057 + nla_total_size(4) /* IFLA_CARRIER_DOWN_COUNT */
@@ -1658,6 +1668,35 @@ nest_cancel: · 610 unmodified lines
1658 return ret;1668 return ret;
1659}1669}
1660 1670
1671static int rtnl_fill_proto_down(struct sk_buff *skb,
1672 const struct net_device *dev)
1673{
1674 struct nlattr *pr;
1675 u32 preason;
1676
1677 if (nla_put_u8(skb, IFLA_PROTO_DOWN, dev->proto_down))
1678 goto nla_put_failure;
1679
1680 preason = dev->proto_down_reason;
1681 if (!preason)
1682 return 0;
1683
1684 pr = nla_nest_start(skb, IFLA_PROTO_DOWN_REASON);
1685 if (!pr)
1686 return -EMSGSIZE;
1687
1688 if (nla_put_u32(skb, IFLA_PROTO_DOWN_REASON_VALUE, preason)) {
1689 nla_nest_cancel(skb, pr);
1690 goto nla_put_failure;
1691 }
1692
1693 nla_nest_end(skb, pr);
1694 return 0;
1695
1696nla_put_failure:
1697 return -EMSGSIZE;
1698}
1699
1661static int rtnl_fill_ifinfo(struct sk_buff *skb,1700static int rtnl_fill_ifinfo(struct sk_buff *skb,
1662 struct net_device *dev, struct net *src_net,1701 struct net_device *dev, struct net *src_net,
1663 int type, u32 pid, u32 seq, u32 change,1702 int type, u32 pid, u32 seq, u32 change,
@@ -1708,13 +1747,15 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, · 44 unmodified lines
1708 nla_put_u32(skb, IFLA_CARRIER_CHANGES,1747 nla_put_u32(skb, IFLA_CARRIER_CHANGES,
1709 atomic_read(&dev->carrier_up_count) +1748 atomic_read(&dev->carrier_up_count) +
1710 atomic_read(&dev->carrier_down_count)) ||1749 atomic_read(&dev->carrier_down_count)) ||
1711 nla_put_u8(skb, IFLA_PROTO_DOWN, dev->proto_down) ||
1712 nla_put_u32(skb, IFLA_CARRIER_UP_COUNT,1750 nla_put_u32(skb, IFLA_CARRIER_UP_COUNT,
1713 atomic_read(&dev->carrier_up_count)) ||1751 atomic_read(&dev->carrier_up_count)) ||
1714 nla_put_u32(skb, IFLA_CARRIER_DOWN_COUNT,1752 nla_put_u32(skb, IFLA_CARRIER_DOWN_COUNT,
1715 atomic_read(&dev->carrier_down_count)))1753 atomic_read(&dev->carrier_down_count)))
1716 goto nla_put_failure;1754 goto nla_put_failure;
1717 1755
1756 if (rtnl_fill_proto_down(skb, dev))
1757 goto nla_put_failure;
1758
1718 if (event != IFLA_EVENT_NONE) {1759 if (event != IFLA_EVENT_NONE) {
1719 if (nla_put_u32(skb, IFLA_EVENT, event))1760 if (nla_put_u32(skb, IFLA_EVENT, event))
1720 goto nla_put_failure;1761 goto nla_put_failure;
@@ -1834,6 +1875,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { · 113 unmodified lines
1834 [IFLA_ALT_IFNAME] = { .type = NLA_STRING,1875 [IFLA_ALT_IFNAME] = { .type = NLA_STRING,
1835 .len = ALTIFNAMSIZ - 1 },1876 .len = ALTIFNAMSIZ - 1 },
1836 [IFLA_PERM_ADDRESS] = { .type = NLA_REJECT },1877 [IFLA_PERM_ADDRESS] = { .type = NLA_REJECT },
1878 [IFLA_PROTO_DOWN_REASON] = { .type = NLA_NESTED },
1837};1879};
1838 1880
1839static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {1881static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
@@ -2483,6 +2525,67 @@ static int do_set_master(struct net_device *dev, int ifindex, · 643 unmodified lines
2483 return 0;2525 return 0;
2484}2526}
2485 2527
2528static const struct nla_policy ifla_proto_down_reason_policy[IFLA_PROTO_DOWN_REASON_VALUE + 1] = {
2529 [IFLA_PROTO_DOWN_REASON_MASK] = { .type = NLA_U32 },
2530 [IFLA_PROTO_DOWN_REASON_VALUE] = { .type = NLA_U32 },
2531};
2532
2533static int do_set_proto_down(struct net_device *dev,
2534 struct nlattr *nl_proto_down,
2535 struct nlattr *nl_proto_down_reason,
2536 struct netlink_ext_ack *extack)
2537{
2538 struct nlattr *pdreason[IFLA_PROTO_DOWN_REASON_MAX + 1];
2539 const struct net_device_ops *ops = dev->netdev_ops;
2540 unsigned long mask = 0;
2541 u32 value;
2542 bool proto_down;
2543 int err;
2544
2545 if (!ops->ndo_change_proto_down) {
2546 NL_SET_ERR_MSG(extack, "Protodown not supported by device");
2547 return -EOPNOTSUPP;
2548 }
2549
2550 if (nl_proto_down_reason) {
2551 err = nla_parse_nested_deprecated(pdreason,
2552 IFLA_PROTO_DOWN_REASON_MAX,
2553 nl_proto_down_reason,
2554 ifla_proto_down_reason_policy,
2555 NULL);
2556 if (err < 0)
2557 return err;
2558
2559 if (!pdreason[IFLA_PROTO_DOWN_REASON_VALUE]) {
2560 NL_SET_ERR_MSG(extack, "Invalid protodown reason value");
2561 return -EINVAL;
2562 }
2563
2564 value = nla_get_u32(pdreason[IFLA_PROTO_DOWN_REASON_VALUE]);
2565
2566 if (pdreason[IFLA_PROTO_DOWN_REASON_MASK])
2567 mask = nla_get_u32(pdreason[IFLA_PROTO_DOWN_REASON_MASK]);
2568
2569 dev_change_proto_down_reason(dev, mask, value);
2570 }
2571
2572 if (nl_proto_down) {
2573 proto_down = nla_get_u8(nl_proto_down);
2574
2575 /* Dont turn off protodown if there are active reasons */
2576 if (!proto_down && dev->proto_down_reason) {
2577 NL_SET_ERR_MSG(extack, "Cannot clear protodown, active reasons");
2578 return -EBUSY;
2579 }
2580 err = dev_change_proto_down(dev,
2581 proto_down);
2582 if (err)
2583 return err;
2584 }
2585
2586 return 0;
2587}
2588
2486#define DO_SETLINK_MODIFIED 0x012589#define DO_SETLINK_MODIFIED 0x01
2487/* notify flag means notify + modified. */2590/* notify flag means notify + modified. */
2488#define DO_SETLINK_NOTIFY 0x032591#define DO_SETLINK_NOTIFY 0x03
@@ -2771,9 +2874,9 @@ static int do_setlink(const struct sk_buff *skb, · 282 unmodified lines
2771 }2874 }
2772 err = 0;2875 err = 0;
2773 2876
2774 if (tb[IFLA_PROTO_DOWN]) {2877 if (tb[IFLA_PROTO_DOWN] || tb[IFLA_PROTO_DOWN_REASON]) {
2775 err = dev_change_proto_down(dev,2878 err = do_set_proto_down(dev, tb[IFLA_PROTO_DOWN],
2776 nla_get_u8(tb[IFLA_PROTO_DOWN]));2879 tb[IFLA_PROTO_DOWN_REASON], extack);
2777 if (err)2880 if (err)
2778 goto errout;2881 goto errout;
2779 status |= DO_SETLINK_NOTIFY;2882 status |= DO_SETLINK_NOTIFY;