Hi,

ok, at least some progress has happened:

-Replaced device-specific oper_state method with NETIF_F_STACKED flag to 
select between IF_OPER_DOWN or IF_OPER_LOWERLAYERDOWN
-sysfs support to read operstate
-completed netlink support (Jamal, Thomas, can you verify the code?)
-added netif_oper_up() query function
-treat IF_OPER_UNKNOWN equivalent to IF_OPER_UP in some cases to have compat 
to devices that do not set carrier state
-adopted vlan drivers
-verified operation with starfire, vlan, loopback. IF_OPER_UNKNOWN won't 
propagate to upper layers when stacking devices.

Todo:
-complete sysfs
-test netlink userspace interaction
-docs

Stefan
diff -X dontdiff -ur linux-2.6.14/include/linux/if.h linux-2.6.14-rfc2863/include/linux/if.h
--- linux-2.6.14/include/linux/if.h	2005-11-02 11:07:32.000000000 +0100
+++ linux-2.6.14-rfc2863/include/linux/if.h	2005-11-18 20:14:25.000000000 +0100
@@ -33,7 +33,7 @@
 #define	IFF_LOOPBACK	0x8		/* is a loopback net		*/
 #define	IFF_POINTOPOINT	0x10		/* interface is has p-p link	*/
 #define	IFF_NOTRAILERS	0x20		/* avoid use of trailers	*/
-#define	IFF_RUNNING	0x40		/* interface running and carrier ok */
+#define	IFF_RUNNING	0x40		/* interface RFC2863 OPER_UP	*/
 #define	IFF_NOARP	0x80		/* no ARP protocol		*/
 #define	IFF_PROMISC	0x100		/* receive all packets		*/
 #define	IFF_ALLMULTI	0x200		/* receive all multicast packets*/
@@ -43,12 +43,16 @@
 
 #define IFF_MULTICAST	0x1000		/* Supports multicast		*/
 
-#define IFF_VOLATILE	(IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_MASTER|IFF_SLAVE|IFF_RUNNING)
-
 #define IFF_PORTSEL	0x2000          /* can set media type		*/
 #define IFF_AUTOMEDIA	0x4000		/* auto media select active	*/
 #define IFF_DYNAMIC	0x8000		/* dialup device with changing addresses*/
 
+#define IFF_CARRIER	0x10000		/* driver signals carrier	*/
+#define IFF_DORMANT	0x20000		/* driver signals dormant	*/
+
+#define IFF_VOLATILE	(IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|\
+		IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_CARRIER|IFF_DORMANT)
+
 /* Private (from user) interface flags (netdevice->priv_flags). */
 #define IFF_802_1Q_VLAN 0x1             /* 802.1Q VLAN device.          */
 #define IFF_EBRIDGE	0x2		/* Ethernet bridging device.	*/
@@ -80,6 +84,22 @@
 #define IF_PROTO_FR_ETH_PVC 0x200B
 #define IF_PROTO_RAW    0x200C          /* RAW Socket                   */
 
+/* RFC 2863 operational status */
+enum {
+	IF_OPER_UNKNOWN,
+	IF_OPER_NOTPRESENT,
+	IF_OPER_DOWN,
+	IF_OPER_LOWERLAYERDOWN,
+	IF_OPER_TESTING,
+	IF_OPER_DORMANT,
+	IF_OPER_UP,
+};
+
+/* link modes */
+enum {
+	IF_LINK_MODE_DEFAULT,
+	IF_LINK_MODE_DORMANT,	/* limit upward transition to dormant */
+};
 
 /*
  *	Device mapping structure. I'd just gone off and designed a 
diff -X dontdiff -ur linux-2.6.14/include/linux/if_vlan.h linux-2.6.14-rfc2863/include/linux/if_vlan.h
--- linux-2.6.14/include/linux/if_vlan.h	2005-11-02 11:08:10.000000000 +0100
+++ linux-2.6.14-rfc2863/include/linux/if_vlan.h	2005-11-28 13:50:07.000000000 +0100
@@ -64,6 +64,8 @@
 /* found in socket.c */
 extern void vlan_ioctl_set(int (*hook)(void __user *));
 
+extern void vlan_transfer_operstate(const struct net_device *dev, struct net_device *vlandev);
+
 #define VLAN_NAME "vlan"
 
 /* if this changes, algorithm will have to be reworked because this
diff -X dontdiff -ur linux-2.6.14/include/linux/netdevice.h linux-2.6.14-rfc2863/include/linux/netdevice.h
--- linux-2.6.14/include/linux/netdevice.h	2005-11-02 11:08:10.000000000 +0100
+++ linux-2.6.14-rfc2863/include/linux/netdevice.h	2005-11-28 13:33:35.000000000 +0100
@@ -230,7 +230,8 @@
 	__LINK_STATE_SCHED,
 	__LINK_STATE_NOCARRIER,
 	__LINK_STATE_RX_SCHED,
-	__LINK_STATE_LINKWATCH_PENDING
+	__LINK_STATE_LINKWATCH_PENDING,
+	__LINK_STATE_DORMANT,
 };
 
 
@@ -308,6 +309,7 @@
 #define NETIF_F_VLAN_CHALLENGED	1024	/* Device cannot handle VLAN packets */
 #define NETIF_F_TSO		2048	/* Can offload TCP/IP segmentation */
 #define NETIF_F_LLTX		4096	/* LockLess TX */
+#define NETIF_F_STACKED		8192	/* Interface is stacked */
 
 	struct net_device	*next_sched;
 
@@ -334,11 +336,14 @@
 	 */
 
 
-	unsigned short		flags;	/* interface flags (a la BSD)	*/
+	unsigned int		flags;	/* interface flags (a la BSD)	*/
 	unsigned short		gflags;
         unsigned short          priv_flags; /* Like 'flags' but invisible to userspace. */
 	unsigned short		padded;	/* How much padding added by alloc_netdev() */
 
+	unsigned char		operstate; /* RFC2863 operstate */
+	unsigned char		link_mode; /* mapping policy to operstate */
+
 	unsigned		mtu;	/* interface MTU value		*/
 	unsigned short		type;	/* interface hardware type	*/
 	unsigned short		hard_header_len;	/* hardware hdr length	*/
@@ -712,6 +717,10 @@
 /* Carrier loss detection, dial on demand. The functions netif_carrier_on
  * and _off may be called from IRQ context, but it is caller
  * who is responsible for serialization of these calls.
+ *
+ * The name carrier is inappropriate, these functions should really be
+ * called netif_lowerlayer_*() because they represent the state of any
+ * kind of lower layer not just hardware media.
  */
 
 extern void linkwatch_fire_event(struct net_device *dev);
@@ -727,6 +736,29 @@
 
 extern void netif_carrier_off(struct net_device *dev);
 
+static inline void netif_dormant_on(struct net_device *dev)
+{
+	if (!test_and_set_bit(__LINK_STATE_DORMANT, &dev->state))
+		linkwatch_fire_event(dev);
+}
+
+static inline void netif_dormant_off(struct net_device *dev)
+{
+	if (test_and_clear_bit(__LINK_STATE_DORMANT, &dev->state))
+		linkwatch_fire_event(dev);
+}
+
+static inline int netif_dormant(const struct net_device *dev)
+{
+	return test_bit(__LINK_STATE_DORMANT, &dev->state);
+}
+
+
+static inline int netif_oper_up(const struct net_device *dev) {
+	return (dev->operstate == IF_OPER_UP ||
+		dev->operstate == IF_OPER_UNKNOWN /* backward compat */);
+}
+
 /* Hot-plugging. */
 static inline int netif_device_present(struct net_device *dev)
 {
diff -X dontdiff -ur linux-2.6.14/include/linux/rtnetlink.h linux-2.6.14-rfc2863/include/linux/rtnetlink.h
--- linux-2.6.14/include/linux/rtnetlink.h	2005-11-02 11:08:11.000000000 +0100
+++ linux-2.6.14-rfc2863/include/linux/rtnetlink.h	2005-11-18 20:14:05.000000000 +0100
@@ -733,6 +733,8 @@
 #define IFLA_MAP IFLA_MAP
 	IFLA_WEIGHT,
 #define IFLA_WEIGHT IFLA_WEIGHT
+	IFLA_OPERSTATE,
+	IFLA_LINKMODE,
 	__IFLA_MAX
 };
 
diff -X dontdiff -ur linux-2.6.14/net/8021q/vlan.c linux-2.6.14-rfc2863/net/8021q/vlan.c
--- linux-2.6.14/net/8021q/vlan.c	2005-11-02 11:07:35.000000000 +0100
+++ linux-2.6.14-rfc2863/net/8021q/vlan.c	2005-11-28 13:53:27.000000000 +0100
@@ -68,7 +68,7 @@
 
 /* Bits of netdev state that are propagated from real device to virtual */
 #define VLAN_LINK_STATE_MASK \
-	((1<<__LINK_STATE_PRESENT)|(1<<__LINK_STATE_NOCARRIER))
+	((1<<__LINK_STATE_PRESENT)|(1<<__LINK_STATE_NOCARRIER)|(1<<__LINK_STATE_DORMANT))
 
 /* End of global variables definitions. */
 
@@ -325,6 +325,8 @@
 	 */
 	new_dev->get_stats = vlan_dev_get_stats;
 
+	new_dev->features |= NETIF_F_STACKED;
+
 	/* Make this thing known as a VLAN device */
 	new_dev->priv_flags |= IFF_802_1Q_VLAN;
 				
@@ -343,6 +345,26 @@
 	new_dev->do_ioctl = vlan_dev_ioctl;
 }
 
+void vlan_transfer_operstate(const struct net_device *dev, struct net_device *vlandev)
+{
+	if (netif_carrier_ok(dev)) {
+		if (!netif_carrier_ok(vlandev))
+			netif_carrier_on(vlandev);
+	} else {
+		if (netif_carrier_ok(vlandev))
+			netif_carrier_off(vlandev);
+	}
+
+	/* Have to respect userspace enforced dormant state
+	 * of real device, also must allow supplicant running
+	 * on VLAN device
+	 */
+	if (dev->operstate == IF_OPER_DORMANT)
+		netif_dormant_on(vlandev);
+	else
+		netif_dormant_off(vlandev);
+}
+
 /*  Attach a VLAN device to a mac address (ie Ethernet Card).
  *  Returns the device that was created, or NULL if there was
  *  an error of some kind.
@@ -578,13 +600,7 @@
 			if (!vlandev)
 				continue;
 
-			if (netif_carrier_ok(dev)) {
-				if (!netif_carrier_ok(vlandev))
-					netif_carrier_on(vlandev);
-			} else {
-				if (netif_carrier_ok(vlandev))
-					netif_carrier_off(vlandev);
-			}
+			vlan_transfer_operstate(dev, vlandev);
 
 			if ((vlandev->state & VLAN_LINK_STATE_MASK) != flgs) {
 				vlandev->state = (vlandev->state &~ VLAN_LINK_STATE_MASK) 
diff -X dontdiff -ur linux-2.6.14/net/8021q/vlan_dev.c linux-2.6.14-rfc2863/net/8021q/vlan_dev.c
--- linux-2.6.14/net/8021q/vlan_dev.c	2005-11-02 11:08:11.000000000 +0100
+++ linux-2.6.14-rfc2863/net/8021q/vlan_dev.c	2005-11-28 13:56:33.000000000 +0100
@@ -786,6 +786,8 @@
 {
 	if (!(VLAN_DEV_INFO(dev)->real_dev->flags & IFF_UP))
 		return -ENETDOWN;
+	vlan_transfer_operstate(VLAN_DEV_INFO(dev)->real_dev, dev);	
+	linkwatch_fire_event(dev); /* force event to setup operstate */
 
 	return 0;
 }
diff -X dontdiff -ur linux-2.6.14/net/core/dev.c linux-2.6.14-rfc2863/net/core/dev.c
--- linux-2.6.14/net/core/dev.c	2005-11-06 17:35:22.000000000 +0100
+++ linux-2.6.14-rfc2863/net/core/dev.c	2005-11-28 13:33:58.000000000 +0100
@@ -2141,12 +2141,20 @@
 
 	flags = (dev->flags & ~(IFF_PROMISC |
 				IFF_ALLMULTI |
-				IFF_RUNNING)) | 
+				IFF_RUNNING |
+				IFF_CARRIER |
+				IFF_DORMANT)) | 
 		(dev->gflags & (IFF_PROMISC |
 				IFF_ALLMULTI));
 
-	if (netif_running(dev) && netif_carrier_ok(dev))
-		flags |= IFF_RUNNING;
+	if (netif_running(dev)) {
+		if (netif_oper_up(dev))
+			flags |= IFF_RUNNING;
+		if (netif_carrier_ok(dev))
+			flags |= IFF_CARRIER;
+		if (netif_dormant(dev))
+			flags |= IFF_DORMANT;
+	}		
 
 	return flags;
 }
diff -X dontdiff -ur linux-2.6.14/net/core/link_watch.c linux-2.6.14-rfc2863/net/core/link_watch.c
--- linux-2.6.14/net/core/link_watch.c	2005-06-17 21:48:29.000000000 +0200
+++ linux-2.6.14-rfc2863/net/core/link_watch.c	2005-11-27 15:09:05.000000000 +0100
@@ -49,6 +49,34 @@
 /* Avoid kmalloc() for most systems */
 static struct lw_event singleevent;
 
+static inline unsigned char default_operstate(const struct net_device *dev) {
+	if (!netif_carrier_ok(dev))
+		return dev->features&NETIF_F_STACKED?IF_OPER_LOWERLAYERDOWN:IF_OPER_DOWN;
+	if (netif_dormant(dev)) return IF_OPER_DORMANT;
+	return IF_OPER_UP;
+}	
+
+
+static void rfc2863_policy(struct net_device *dev) {
+	unsigned char operstate = default_operstate(dev);
+	
+	if (operstate == dev->operstate) return;
+
+	switch(dev->link_mode) {
+	case IF_LINK_MODE_DORMANT:
+		if (operstate == IF_OPER_UP) operstate = IF_OPER_DORMANT;
+		break;
+	case IF_LINK_MODE_DEFAULT:
+	default:
+		break;
+	}
+
+	write_lock_bh(&dev_base_lock);
+	dev->operstate = operstate;
+	write_unlock_bh(&dev_base_lock);
+}
+
+	
 /* Must be called with the rtnl semaphore held */
 void linkwatch_run_queue(void)
 {
@@ -81,6 +109,7 @@
 			} else
 				dev_deactivate(dev);
 
+			rfc2863_policy(dev);
 			netdev_state_change(dev);
 		}
 
diff -X dontdiff -ur linux-2.6.14/net/core/net-sysfs.c linux-2.6.14-rfc2863/net/core/net-sysfs.c
--- linux-2.6.14/net/core/net-sysfs.c	2005-06-17 21:48:29.000000000 +0200
+++ linux-2.6.14-rfc2863/net/core/net-sysfs.c	2005-11-28 12:57:45.000000000 +0100
@@ -136,9 +136,34 @@
 	return -EINVAL;
 }
 
+static const char *operstates[] = {
+	"unknown",
+	NULL, /* notpresent, currently unused */
+	"down",
+	"lowerlayerdown",
+	NULL, /* testing, currently unused */
+	"dormant",
+	"up"
+};
+	
+static ssize_t show_operstate(struct class_device *dev, char *buf)
+{
+	const struct net_device *netdev = to_net_dev(dev);
+	unsigned char operstate;
+
+	read_lock(&dev_base_lock);
+	operstate = netdev->operstate;
+	if (!netif_running(netdev)) operstate = IF_OPER_DOWN;
+	read_unlock(&dev_base_lock);
+
+	if (operstate >= sizeof(operstates)) return -EINVAL; /* should not happen */
+	return sprintf(buf, "%s\n", operstates[operstate]);
+}
+
 static CLASS_DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
 static CLASS_DEVICE_ATTR(broadcast, S_IRUGO, show_broadcast, NULL);
 static CLASS_DEVICE_ATTR(carrier, S_IRUGO, show_carrier, NULL);
+static CLASS_DEVICE_ATTR(operstate, S_IRUGO, show_operstate, NULL);
 
 /* read-write attributes */
 NETDEVICE_SHOW(mtu, fmt_dec);
@@ -215,6 +240,7 @@
 	&class_device_attr_address,
 	&class_device_attr_broadcast,
 	&class_device_attr_carrier,
+	&class_device_attr_operstate,
 	NULL
 };
 
diff -X dontdiff -ur linux-2.6.14/net/core/rtnetlink.c linux-2.6.14-rfc2863/net/core/rtnetlink.c
--- linux-2.6.14/net/core/rtnetlink.c	2005-11-02 11:08:12.000000000 +0100
+++ linux-2.6.14-rfc2863/net/core/rtnetlink.c	2005-11-28 13:33:31.000000000 +0100
@@ -178,6 +178,31 @@
 }
 
 
+static void set_operstate(struct net_device *dev, unsigned char transition) {
+	unsigned char operstate = dev->operstate;
+	ASSERT_RTNL();
+
+	switch(transition) {
+	case IF_OPER_UP:
+		if (operstate == IF_OPER_DORMANT ||
+		    operstate == IF_OPER_UNKNOWN)
+			operstate = IF_OPER_UP;
+		break;
+	case IF_OPER_DORMANT:
+		if (operstate == IF_OPER_UP ||
+		    operstate == IF_OPER_UNKNOWN)
+			operstate = IF_OPER_DORMANT;
+		break;
+	}
+
+	if (dev->operstate != operstate) {
+		write_lock_bh(&dev_base_lock);
+		dev->operstate = operstate;
+		write_unlock_bh(&dev_base_lock);
+		netdev_state_change(dev);
+	}	
+}
+
 static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
 				 int type, u32 pid, u32 seq, u32 change, 
 				 unsigned int flags)
@@ -208,6 +233,13 @@
 	}
 
 	if (1) {
+		u8 operstate = dev->operstate;
+		u8 link_mode = dev->link_mode;
+		RTA_PUT(skb, IFLA_OPERSTATE, sizeof(operstate), &operstate);
+		RTA_PUT(skb, IFLA_LINKMODE, sizeof(link_mode), &link_mode);
+	}	
+
+	if (1) {
 		struct rtnl_link_ifmap map = {
 			.mem_start   = dev->mem_start,
 			.mem_end     = dev->mem_end,
@@ -398,6 +430,22 @@
 		dev->weight = *((u32 *) RTA_DATA(ida[IFLA_WEIGHT - 1]));
 	}
 
+	if (ida[IFLA_OPERSTATE - 1]) {
+		if (ida[IFLA_OPERSTATE - 1]->rta_len != RTA_LENGTH(sizeof(u8)))
+			goto out;
+
+		set_operstate(dev, *((u8 *) RTA_DATA(ida[IFLA_OPERSTATE - 1])));
+	}	
+
+	if (ida[IFLA_LINKMODE - 1]) {
+		if (ida[IFLA_LINKMODE - 1]->rta_len != RTA_LENGTH(sizeof(u8)))
+			goto out;
+
+		write_lock_bh(&dev_base_lock);
+		dev->link_mode = *((u8 *) RTA_DATA(ida[IFLA_LINKMODE - 1]));
+		write_unlock_bh(&dev_base_lock);
+	}
+
 	if (ifm->ifi_index >= 0 && ida[IFLA_IFNAME - 1]) {
 		char ifname[IFNAMSIZ];
 

Reply via email to