Am Freitag 18 November 2005 04:03 schrieb jamal:

> > ok, I'll produce a patch then - first version will be available tomorrow
> > evening.
>
> beauty.

Ok, here is the first 'merger' patch. At this stage, it is only compilation 
tested against 2.6.14.

This is the data flow:

-Device changes __LINK_STATE_(NOCARRIER|DORMANT) flag
-linkwatch is scheduled
-During linkwatch, the device is queried for it's operational state either 
using a device specified method or a generic one
-Operational state is limited to IF_OPER_DORMANT if link mode tells so
-state is written to dev->operstate
-notification is sent out via netdev_state_change() as before

Userspace can intervene after setting link mode to IF_LINK_MODE_DORMANT 
(implementation incomplete as I'm yet not familiar enough with writing values 
through netlink, Thomas, can you help here?)
-Calls pseudo_set_operstate()
-operstate is either changed from DORMANT to UP or reverse
-notification is sent out via netdev_state_change()

linkwatch and pseudo* as serialized by rtnl and dev_base_lock.

To demonstrate a stacked device, I've implemented a private RFC2863 state 
query for VLANs that goes to LOWERLAYERDOWN if the underlying device is DOWN.

With the device specific RFC2863 state query method, we could even limit 
LINK_STATE_DORMANT to the devices that need it, but I have the impression 
that this is generic enough to be part of struct net_device.

I'm currently emitting more netlink notifications than you've shown in your 
state diagram. I'll comment on this later, still got other stuff to do for 
today.

> -> UP->DORMANT; i am not sure if that transition is needed.

Anyway, this transition is needed. Switches can be configured to rerun 802.1X 
regularly after initial authentication.

> I will attempt some ascii art shortly. Actually if you agree with it
> please include it in your patch so it is easier for people to make
> changes in the future.

Yes, I think I have to write a short text for the Documentation/ directory 
anyway.

Stefan
diff -X dontdiff -urp 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 -urp 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-18 19:53:32.000000000 +0100
@@ -230,7 +230,8 @@ enum netdev_state_t
 	__LINK_STATE_SCHED,
 	__LINK_STATE_NOCARRIER,
 	__LINK_STATE_RX_SCHED,
-	__LINK_STATE_LINKWATCH_PENDING
+	__LINK_STATE_LINKWATCH_PENDING,
+	__LINK_STATE_DORMANT,
 };
 
 
@@ -334,11 +335,14 @@ struct net_device
 	 */
 
 
-	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	*/
@@ -491,6 +495,8 @@ struct net_device
 	void                    (*poll_controller)(struct net_device *dev);
 #endif
 
+	unsigned char		(*oper_state)(const struct net_device *dev, unsigned char genstate);
+
 	/* bridge stuff */
 	struct net_bridge_port	*br_port;
 
@@ -712,6 +718,10 @@ static inline void dev_put(struct net_de
 /* 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 +737,23 @@ extern void netif_carrier_on(struct net_
 
 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);
+}
+
 /* Hot-plugging. */
 static inline int netif_device_present(struct net_device *dev)
 {
diff -X dontdiff -urp 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 @@ enum
 #define IFLA_MAP IFLA_MAP
 	IFLA_WEIGHT,
 #define IFLA_WEIGHT IFLA_WEIGHT
+	IFLA_OPERSTATE,
+	IFLA_LINKMODE,
 	__IFLA_MAX
 };
 
diff -X dontdiff -urp 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-18 20:54:12.000000000 +0100
@@ -68,7 +68,7 @@ static struct packet_type vlan_packet_ty
 
 /* 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. */
 
@@ -198,6 +198,16 @@ static void vlan_rcu_free(struct rcu_hea
 }
 
 
+/* Return RFC2863 state of VLAN device. Dataflow:
+ * -state change in real device triggers vlan_device_event()
+ * -linkwatch_fire_event() schedules event if needed
+ * -we get called, use operstate of underlying device only
+ */
+static unsigned char vlan_oper_state(const struct net_device *dev, unsigned char genstate) {
+	const struct net_device *real_dev = VLAN_DEV_INFO(dev)->real_dev;
+	return real_dev->operstate==IF_OPER_DOWN?IF_OPER_LOWERLAYERDOWN:real_dev->operstate;
+}
+
 /* This returns 0 if everything went fine.
  * It will return 1 if the group was killed as a result.
  * A negative return indicates failure.
@@ -341,6 +351,7 @@ static void vlan_setup(struct net_device
 	new_dev->set_multicast_list = vlan_dev_set_multicast_list;
 	new_dev->destructor = free_netdev;
 	new_dev->do_ioctl = vlan_dev_ioctl;
+	new_dev->oper_state = vlan_oper_state;
 }
 
 /*  Attach a VLAN device to a mac address (ie Ethernet Card).
@@ -586,6 +597,11 @@ static int vlan_device_event(struct noti
 					netif_carrier_off(vlandev);
 			}
 
+			if (dev->operstate != vlandev->operstate &&
+			    !(dev->operstate == IF_OPER_DOWN &&
+			      vlandev->operstate == IF_OPER_LOWERLAYERDOWN))
+				linkwatch_fire_event(vlandev);
+
 			if ((vlandev->state & VLAN_LINK_STATE_MASK) != flgs) {
 				vlandev->state = (vlandev->state &~ VLAN_LINK_STATE_MASK) 
 					| flgs;
diff -X dontdiff -urp 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-18 21:14:00.000000000 +0100
@@ -2141,12 +2141,20 @@ unsigned dev_get_flags(const struct net_
 
 	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 (dev->operstate == IF_OPER_UP)
+			flags |= IFF_RUNNING;
+		if (netif_carrier_ok(dev))
+			flags |= IFF_CARRIER;
+		if (netif_dormant(dev))
+			flags |= IFF_DORMANT;
+	}		
 
 	return flags;
 }
diff -X dontdiff -urp 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-18 20:00:11.000000000 +0100
@@ -49,6 +49,34 @@ struct lw_event {
 /* 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 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 (dev->oper_state) operstate = dev->oper_state(dev, operstate);
+	
+	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 @@ void linkwatch_run_queue(void)
 			} else
 				dev_deactivate(dev);
 
+			rfc2863_policy(dev);
 			netdev_state_change(dev);
 		}
 
diff -X dontdiff -urp 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-18 21:32:26.000000000 +0100
@@ -178,6 +178,34 @@ rtattr_failure:
 }
 
 
+static void pseudo_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_UP;
+		break;
+	case IF_OPER_DORMANT:
+		if (operstate == IF_OPER_UP) 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 void pseudo_set_linkmode(struct net_device *dev, unsigned char linkmode) {
+	ASSERT_RTNL();
+	write_lock_bh(&dev_base_lock);
+	dev->link_mode = linkmode;
+	write_unlock_bh(&dev_base_lock);
+}
+
 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 +236,13 @@ static int rtnetlink_fill_ifinfo(struct 
 	}
 
 	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,

Reply via email to