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
20582058 struct timer_list watchdog_timer;
20592059 int watchdog_timeo;
20602060
2061+ u32 proto_down_reason;
2062+
20612063 struct list_head todo_list;
20622064 int __percpu *pcpu_refcnt;
20632065
@@ -3810,6 +3812,8 @@ int dev_get_port_parent_id(struct net_device *dev, · 1746 unmodified lines
38103812 bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b);
38113813 int dev_change_proto_down(struct net_device *dev, bool proto_down);
38123814 int dev_change_proto_down_generic(struct net_device *dev, bool proto_down);
3815+void dev_change_proto_down_reason(struct net_device *dev, unsigned long mask,
3816+ u32 value);
38133817 struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev, bool *again);
38143818 struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
38153819 struct netdev_queue *txq, int *ret);
include/uapi/linux/if_link.h+10−0
@@ -170,12 +170,22 @@ enum { · 169 unmodified lines
170170 IFLA_PROP_LIST,
171171 IFLA_ALT_IFNAME, /* Alternative ifname */
172172 IFLA_PERM_ADDRESS,
173+ IFLA_PROTO_DOWN_REASON,
173174 __IFLA_MAX
174175 };
175176
176177
177178 #define IFLA_MAX (__IFLA_MAX - 1)
178179
180+enum {
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+
179189 /* backwards compatibility for userspace */
180190 #ifndef __KERNEL__
181191 #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
87158715 }
87168716 EXPORT_SYMBOL(dev_change_proto_down_generic);
87178717
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+ */
8725+void 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+}
8741+EXPORT_SYMBOL(dev_change_proto_down_reason);
8742+
87188743 u32 __dev_xdp_query(struct net_device *dev, bpf_op_t bpf_op,
87198744 enum bpf_netdev_command cmd)
87208745 {
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
10001000 return size;
10011001 }
10021002
1003+static 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+
10031013 static noinline size_t if_nlmsg_size(const struct net_device *dev,
10041014 u32 ext_filter_mask)
10051015 {
@@ -1041,7 +1051,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, · 35 unmodified lines
10411051 + nla_total_size(4) /* IFLA_EVENT */
10421052 + nla_total_size(4) /* IFLA_NEW_NETNSID */
10431053 + nla_total_size(4) /* IFLA_NEW_IFINDEX */
1044− + nla_total_size(1) /* IFLA_PROTO_DOWN */
1054+ + rtnl_proto_down_size(dev) /* proto down */
10451055 + nla_total_size(4) /* IFLA_TARGET_NETNSID */
10461056 + nla_total_size(4) /* IFLA_CARRIER_UP_COUNT */
10471057 + nla_total_size(4) /* IFLA_CARRIER_DOWN_COUNT */
@@ -1658,6 +1668,35 @@ nest_cancel: · 610 unmodified lines
16581668 return ret;
16591669 }
16601670
1671+static 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+
1696+nla_put_failure:
1697+ return -EMSGSIZE;
1698+}
1699+
16611700 static int rtnl_fill_ifinfo(struct sk_buff *skb,
16621701 struct net_device *dev, struct net *src_net,
16631702 int type, u32 pid, u32 seq, u32 change,
@@ -1708,13 +1747,15 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, · 44 unmodified lines
17081747 nla_put_u32(skb, IFLA_CARRIER_CHANGES,
17091748 atomic_read(&dev->carrier_up_count) +
17101749 atomic_read(&dev->carrier_down_count)) ||
1711− nla_put_u8(skb, IFLA_PROTO_DOWN, dev->proto_down) ||
17121750 nla_put_u32(skb, IFLA_CARRIER_UP_COUNT,
17131751 atomic_read(&dev->carrier_up_count)) ||
17141752 nla_put_u32(skb, IFLA_CARRIER_DOWN_COUNT,
17151753 atomic_read(&dev->carrier_down_count)))
17161754 goto nla_put_failure;
17171755
1756+ if (rtnl_fill_proto_down(skb, dev))
1757+ goto nla_put_failure;
1758+
17181759 if (event != IFLA_EVENT_NONE) {
17191760 if (nla_put_u32(skb, IFLA_EVENT, event))
17201761 goto nla_put_failure;
@@ -1834,6 +1875,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { · 113 unmodified lines
18341875 [IFLA_ALT_IFNAME] = { .type = NLA_STRING,
18351876 .len = ALTIFNAMSIZ - 1 },
18361877 [IFLA_PERM_ADDRESS] = { .type = NLA_REJECT },
1878+ [IFLA_PROTO_DOWN_REASON] = { .type = NLA_NESTED },
18371879 };
18381880
18391881 static 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
24832525 return 0;
24842526 }
24852527
2528+static 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+
2533+static 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+
24862589 #define DO_SETLINK_MODIFIED 0x01
24872590 /* notify flag means notify + modified. */
24882591 #define DO_SETLINK_NOTIFY 0x03
@@ -2771,9 +2874,9 @@ static int do_setlink(const struct sk_buff *skb, · 282 unmodified lines
27712874 }
27722875 err = 0;
27732876
2774− if (tb[IFLA_PROTO_DOWN]) {
2775− err = dev_change_proto_down(dev,
2776− nla_get_u8(tb[IFLA_PROTO_DOWN]));
2877+ if (tb[IFLA_PROTO_DOWN] || tb[IFLA_PROTO_DOWN_REASON]) {
2878+ err = do_set_proto_down(dev, tb[IFLA_PROTO_DOWN],
2879+ tb[IFLA_PROTO_DOWN_REASON], extack);
27772880 if (err)
27782881 goto errout;
27792882 status |= DO_SETLINK_NOTIFY;