@@ -1729,6 +1729,7 @@ struct net_device {
struct lock_class_key *qdisc_tx_busylock;
int group;
struct pm_qos_request pm_qos_req;
+ unsigned int proto_down; /* protocol bits to hold down the operstate */
};
#define to_net_dev(d) container_of(d, struct net_device, dev)
@@ -2945,6 +2946,8 @@ struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
int dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
bool is_skb_forwardable(struct net_device *dev, struct sk_buff *skb);
+void dev_set_proto_down(struct net_device *dev, unsigned int proto_down,
+ unsigned int proto_down_change);
extern int netdev_budget;
@@ -66,6 +66,8 @@
* @IFF_LOWER_UP: driver signals L1 up. Volatile.
* @IFF_DORMANT: driver signals dormant. Volatile.
* @IFF_ECHO: echo sent packets. Volatile.
+ * @IFF_PROTO_DOWN: protocol is down on the interface. Userspace can toggle
+ * this by toggling the net_device proto_down bits. Volatile.
*/
enum net_device_flags {
IFF_UP = 1<<0, /* sysfs */
@@ -87,6 +89,7 @@ enum net_device_flags {
IFF_LOWER_UP = 1<<16, /* volatile */
IFF_DORMANT = 1<<17, /* volatile */
IFF_ECHO = 1<<18, /* volatile */
+ IFF_PROTO_DOWN = 1<<19, /* volatile */
};
#define IFF_UP IFF_UP
@@ -108,9 +111,11 @@ enum net_device_flags {
#define IFF_LOWER_UP IFF_LOWER_UP
#define IFF_DORMANT IFF_DORMANT
#define IFF_ECHO IFF_ECHO
+#define IFF_PROTO_DOWN IFF_PROTO_DOWN
#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\
- IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT)
+ IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT|\
+ IFF_PROTO_DOWN)
#define IF_GET_IFACE 0x0001 /* for querying only */
#define IF_GET_PROTO 0x0002
@@ -156,6 +161,28 @@ enum {
IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */
};
+/**
+ * enum net_device_proto_down - &struct net_device proto_down
+ *
+ * These are the &struct net_device proto_down bits and define a list of
+ * protocols that could hold the device in a IFF_PROTO_DOWN state. Setting of
+ * these bits is triggered by userspace. Userspace can query and set these
+ * flags using userspace utilities. There is also a sysfs entry available
+ * which can only be queried. The sysfs entries are available via
+ * /sys/class/net/<dev>/proto_down
+ *
+ *
+ * @IF_LINK_PROTO_DOWN_MLAG: proto_down by a multi-chassis LAG application.
+ * @IF_LINK_PROTO_DOWN_STP: proto_down by an STP application.
+ */
+enum net_device_proto_down {
+ IF_LINK_PROTO_DOWN_MLAG = 1<<0,
+ IF_LINK_PROTO_DOWN_STP = 1<<1,
+};
+
+#define IF_LINK_PROTO_DOWN_MLAG IF_LINK_PROTO_DOWN_MLAG
+#define IF_LINK_PROTO_DOWN_STP IF_LINK_PROTO_DOWN_STP
+
/*
* Device mapping structure. I'd just gone off and designed a
* beautiful scheme using only loadable modules with arguments
@@ -80,6 +80,13 @@ struct rtnl_link_ifmap {
__u8 port;
};
+/* link protodown */
+struct rtnl_link_protodown {
+ __u32 proto_down; /* Bit mask of protocols that want
+ * to hold the device down */
+ __u32 proto_down_change; /* Change mask for proto_down */
+};
+
/*
* IFLA_AF_SPEC
* Contains nested attributes for address family specific attributes.
@@ -147,6 +154,8 @@ enum {
IFLA_CARRIER_CHANGES,
IFLA_PHYS_SWITCH_ID,
IFLA_LINK_NETNSID,
+ IFLA_LINKPROTODOWN, /* Bit mask of protocols that want to
+ * hold the device down */
__IFLA_MAX
};
@@ -537,7 +537,8 @@ static int vlan_dev_init(struct net_device *dev)
/* IFF_BROADCAST|IFF_MULTICAST; ??? */
dev->flags = real_dev->flags & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI |
- IFF_MASTER | IFF_SLAVE);
+ IFF_MASTER | IFF_SLAVE |
+ IFF_PROTO_DOWN);
dev->iflink = real_dev->ifindex;
dev->state = (real_dev->state & ((1<<__LINK_STATE_NOCARRIER) |
(1<<__LINK_STATE_DORMANT))) |
@@ -5780,6 +5780,42 @@ int dev_change_flags(struct net_device *dev, unsigned int flags)
}
EXPORT_SYMBOL(dev_change_flags);
+/**
+ * dev_set_proto_down - Set the link protocol state
+ * @dev: device
+ * @proto_down: bitmap of protocols that want to hold the link down
+ * @proto_down_change: bitmap of protocols that need to be changed
+ *
+ */
+void dev_set_proto_down(struct net_device *dev, unsigned int proto_down,
+ unsigned int proto_down_change)
+{
+ unsigned int old_flags;
+
+ proto_down = (proto_down & proto_down_change) |
+ (dev->proto_down & ~proto_down_change);
+ if (proto_down == dev->proto_down)
+ return;
+
+ old_flags = dev->flags & IFF_PROTO_DOWN;
+ write_lock_bh(&dev_base_lock);
+ dev->proto_down = proto_down;
+ if (dev->proto_down)
+ dev->flags |= IFF_PROTO_DOWN;
+ else
+ dev->flags &= ~IFF_PROTO_DOWN;
+ write_unlock_bh(&dev_base_lock);
+
+ if (old_flags != (dev->flags & IFF_PROTO_DOWN)) {
+ rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_PROTO_DOWN, GFP_KERNEL);
+ call_netdevice_notifiers(NETDEV_CHANGE, dev);
+
+ /* the operstate may need to be re-evaluated */
+ linkwatch_fire_event(dev);
+ }
+}
+EXPORT_SYMBOL(dev_set_proto_down);
+
static int __dev_set_mtu(struct net_device *dev, int new_mtu)
{
const struct net_device_ops *ops = dev->netdev_ops;
@@ -39,7 +39,7 @@ static DEFINE_SPINLOCK(lweventlist_lock);
static unsigned char default_operstate(const struct net_device *dev)
{
- if (!netif_carrier_ok(dev))
+ if (!netif_carrier_ok(dev) || (dev->flags & IFF_PROTO_DOWN))
return (dev->ifindex != dev->iflink ?
IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN);
@@ -113,6 +113,7 @@ NETDEVICE_SHOW_RO(iflink, fmt_dec);
NETDEVICE_SHOW_RO(ifindex, fmt_dec);
NETDEVICE_SHOW_RO(type, fmt_dec);
NETDEVICE_SHOW_RO(link_mode, fmt_dec);
+NETDEVICE_SHOW_RO(proto_down, fmt_hex);
static ssize_t format_name_assign_type(const struct net_device *dev, char *buf)
{
@@ -466,6 +467,7 @@ static struct attribute *net_class_attrs[] = {
&dev_attr_gro_flush_timeout.attr,
&dev_attr_phys_port_id.attr,
&dev_attr_phys_switch_id.attr,
+ &dev_attr_proto_down.attr,
NULL,
};
ATTRIBUTE_GROUPS(net_class);
@@ -876,6 +876,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
+ nla_total_size(1) /* IFLA_LINKMODE */
+ nla_total_size(4) /* IFLA_CARRIER_CHANGES */
+ nla_total_size(4) /* IFLA_LINK_NETNSID */
+ + nla_total_size(sizeof(struct rtnl_link_protodown))
+ nla_total_size(ext_filter_mask
& RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */
+ rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */
@@ -1059,8 +1060,17 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
.dma = dev->dma,
.port = dev->if_port,
};
+ struct rtnl_link_protodown link_proto_down = {
+ .proto_down = dev->proto_down,
+ .proto_down_change = ~0,
+ };
+
if (nla_put(skb, IFLA_MAP, sizeof(map), &map))
goto nla_put_failure;
+
+ if (nla_put(skb, IFLA_LINKPROTODOWN, sizeof(link_proto_down),
+ &link_proto_down))
+ goto nla_put_failure;
}
if (dev->addr_len) {
@@ -1249,6 +1259,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
[IFLA_CARRIER_CHANGES] = { .type = NLA_U32 }, /* ignored */
[IFLA_PHYS_SWITCH_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN },
[IFLA_LINK_NETNSID] = { .type = NLA_S32 },
+ [IFLA_LINKPROTODOWN] = { .len = sizeof(struct rtnl_link_protodown) },
};
static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
@@ -1680,6 +1691,14 @@ static int do_setlink(const struct sk_buff *skb,
write_unlock_bh(&dev_base_lock);
}
+ if (tb[IFLA_LINKPROTODOWN]) {
+ struct rtnl_link_protodown *link_proto_down;
+
+ link_proto_down = nla_data(tb[IFLA_LINKPROTODOWN]);
+ dev_set_proto_down(dev, link_proto_down->proto_down,
+ link_proto_down->proto_down_change);
+ }
+
if (tb[IFLA_VFINFO_LIST]) {
struct nlattr *attr;
int rem;
@@ -1919,6 +1938,8 @@ struct net_device *rtnl_create_link(struct net *net,
dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]);
if (tb[IFLA_GROUP])
dev_set_group(dev, nla_get_u32(tb[IFLA_GROUP]));
+ if (tb[IFLA_LINKPROTODOWN])
+ dev->proto_down = nla_get_u32(tb[IFLA_LINKPROTODOWN]);
return dev;