Announcing ethernet link state changes via route socket

Hasso Tepper hasso at estpak.ee
Fri Mar 16 01:13:04 PDT 2007


Announcing link state changes to the userspace via route socket is critical
to get routing related applications to work correctly - routing protocols
are most important, but other applications might benefit from it as well.

This code is adapted from OpenBSD and is only the first basic step in path
to get correct support. Only em(4) is tested because I don't have hardware
to test others, although bge(4) might get my attention as well in the
future. 

There is one fundamental problem I'd like to get feedback about from
developers - how to make various kernel stuff react on link state changes?
For example various interfaces which are somehow attached to the interface
(bridges, carp, vlans etc) should react. Ideally routing table should react
as well (yes, no BSD or Linux kernel does that yet, but they should).
Personally I like the idea of the hooks OpenBSD uses, but as I'm not very
familiar of DragonFlyBSD, my personal opinion might be very far from good
solution ;).


regards,

-- 
Hasso Tepper
diff -r 9d42302b182d sys/dev/netif/em/if_em.c
--- a/sys/dev/netif/em/if_em.c	Thu Mar 15 21:41:11 2007 +0000
+++ b/sys/dev/netif/em/if_em.c	Thu Mar 15 20:05:40 2007 +0200
@@ -1788,6 +1788,9 @@ static void
 static void
 em_update_link_status(struct adapter *adapter)
 {
+	struct ifnet *ifp;
+	ifp = &adapter->interface_data.ac_if;
+
 	if (E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_LU) {
 		if (adapter->link_active == 0) {
 			em_get_speed_and_duplex(&adapter->hw, 
@@ -1812,16 +1815,13 @@ em_update_link_status(struct adapter *ad
 			}
 			adapter->link_active = 1;
 			adapter->smartspeed = 0;
-#ifdef notyet
 			ifp->if_baudrate = adapter->link_speed * 1000000;
-			if_link_state_change(ifp, LINK_STATE_UP);
-#endif
+			ifp->if_link_state = LINK_STATE_UP;
+			if_link_state_change(ifp);
 		}
 	} else {
 		if (adapter->link_active == 1) {
-#ifdef notyet
 			ifp->if_baudrate = 0;
-#endif
 			adapter->link_speed = 0;
 			adapter->link_duplex = 0;
 			if (bootverbose) {
@@ -1829,9 +1829,8 @@ em_update_link_status(struct adapter *ad
 					  "Link is Down\n");
 			}
 			adapter->link_active = 0;
-#ifdef notyet
-			if_link_state_change(ifp, LINK_STATE_DOWN);
-#endif
+			ifp->if_link_state = LINK_STATE_DOWN;
+			if_link_state_change(ifp);
 		}
 	}
 }
diff -r 9d42302b182d sys/dev/netif/mii_layer/mii_physubr.c
--- a/sys/dev/netif/mii_layer/mii_physubr.c	Thu Mar 15 21:41:11 2007 +0000
+++ b/sys/dev/netif/mii_layer/mii_physubr.c	Thu Mar 15 20:10:03 2007 +0200
@@ -500,42 +500,61 @@ mii_phy_tick(struct mii_softc *sc)
 	return (0);
 }
 
-#ifdef notyet
-static void
+static int
 mii_phy_statusmsg(struct mii_softc *sc)
 {
 	struct mii_data *mii = sc->mii_pdata;
 	struct ifnet *ifp = mii->mii_ifp;
-	int s;
-
-	crit_enter();
+	int baudrate, link_state, announce = 0;
+
 	if (mii->mii_media_status & IFM_AVALID) {
 		if (mii->mii_media_status & IFM_ACTIVE)
-			if_link_state_change(ifp, LINK_STATE_UP);
+			link_state = LINK_STATE_UP;
 		else
-			if_link_state_change(ifp, LINK_STATE_DOWN);
+			link_state = LINK_STATE_DOWN;
 	} else
-		if_link_state_change(ifp, LINK_STATE_UNKNOWN);
-	crit_exit();
-
-	ifp->if_baudrate = ifmedia_baudrate(mii->mii_media_active);
-}
-#endif
+		link_state = LINK_STATE_UNKNOWN;
+
+	baudrate = ifmedia_baudrate(mii->mii_media_active);
+
+	if (link_state != ifp->if_link_state) {
+		ifp->if_link_state = link_state;
+		/*
+		 * XXX Right here we'd like to notify protocols
+		 * XXX that the link status has changed, so that
+		 * XXX e.g. Duplicate Address Detection can restart.
+		 */
+		announce = 1;
+	}
+
+	if (baudrate != ifp->if_baudrate) {
+		ifp->if_baudrate = baudrate;
+		announce = 1;
+	}
+
+	return (announce);
+}
 
 void
 mii_phy_update(struct mii_softc *sc, int cmd)
 {
 	struct mii_data *mii = sc->mii_pdata;
+	struct ifnet *ifp = mii->mii_ifp;
+	int announce;
 
 	if (sc->mii_media_active != mii->mii_media_active ||
 	    sc->mii_media_status != mii->mii_media_status ||
 	    cmd == MII_MEDIACHG) {
-#ifdef notyet
-		mii_phy_statusmsg(sc);
-#endif
+		announce = mii_phy_statusmsg(sc);
 		MIIBUS_STATCHG(sc->mii_dev);
 		sc->mii_media_active = mii->mii_media_active;
 		sc->mii_media_status = mii->mii_media_status;
+
+		if (announce) {
+			crit_enter();
+			if_link_state_change(ifp);
+			crit_exit();
+		}
 	}
 }
 
diff -r 9d42302b182d sys/net/if.c
--- a/sys/net/if.c	Thu Mar 15 21:41:11 2007 +0000
+++ b/sys/net/if.c	Fri Mar 16 06:58:52 2007 +0200
@@ -975,6 +975,16 @@ if_up(struct ifnet *ifp)
 {
 
 	if_route(ifp, IFF_UP, AF_UNSPEC);
+}
+
+/*
+ * Process a link state change.
+ * NOTE: must be called at splsoftnet or equivalent.
+ */
+void
+if_link_state_change(struct ifnet *ifp)
+{
+	rt_ifmsg(ifp);
 }
 
 /*
diff -r 9d42302b182d sys/net/if.h
--- a/sys/net/if.h	Thu Mar 15 21:41:11 2007 +0000
+++ b/sys/net/if.h	Thu Mar 15 20:08:58 2007 +0200
@@ -56,6 +56,13 @@
 
 #endif
 
+/*
+ * Values for if_link_state.
+ */
+#define	LINK_STATE_UNKNOWN	0	/* link invalid/unknown */
+#define	LINK_STATE_DOWN		1	/* link is down */
+#define	LINK_STATE_UP		2	/* link is up */
+
 struct ifnet;
 
 /*
@@ -111,6 +118,7 @@ struct if_data {
 	u_char	ifi_xmitquota;		/* polling quota for xmit intrs */
 	u_long	ifi_mtu;		/* maximum transmission unit */
 	u_long	ifi_metric;		/* routing metric (external only) */
+	u_long  ifi_link_state;		/* current link state */
 	u_long	ifi_baudrate;		/* linespeed */
 	/* volatile statistics */
 	u_long	ifi_ipackets;		/* packets received on interface */
diff -r 9d42302b182d sys/net/if_media.c
--- a/sys/net/if_media.c	Thu Mar 15 21:41:11 2007 +0000
+++ b/sys/net/if_media.c	Thu Mar 15 13:57:11 2007 +0200
@@ -360,6 +360,24 @@ ifmedia_match(struct ifmedia *ifm, int t
 	return match;
 }
 
+struct ifmedia_baudrate ifmedia_baudrate_descriptions[] =
+    IFM_BAUDRATE_DESCRIPTIONS;
+
+int
+ifmedia_baudrate(int mword)
+{
+	int i;
+
+	for (i = 0; ifmedia_baudrate_descriptions[i].ifmb_word != 0; i++) {
+		if ((mword & (IFM_NMASK|IFM_TMASK)) ==
+		    ifmedia_baudrate_descriptions[i].ifmb_word)
+			return (ifmedia_baudrate_descriptions[i].ifmb_baudrate);
+	}
+
+	/* Not known. */
+	return (0);
+}
+
 #ifdef IFMEDIA_DEBUG
 struct ifmedia_description ifm_type_descriptions[] =
     IFM_TYPE_DESCRIPTIONS;
diff -r 9d42302b182d sys/net/if_media.h
--- a/sys/net/if_media.h	Thu Mar 15 21:41:11 2007 +0000
+++ b/sys/net/if_media.h	Fri Mar 16 06:56:20 2007 +0200
@@ -115,6 +115,8 @@ int	ifmedia_ioctl(struct ifnet *ifp, str
 int	ifmedia_ioctl(struct ifnet *ifp, struct ifreq *ifr,
 	    struct ifmedia *ifm, u_long cmd);
 
+/* Compute baudrate for a given media. */
+int	ifmedia_baudrate(int);
 #endif /*_KERNEL */
 
 /*
@@ -541,4 +543,57 @@ struct ifmedia_description {
 	{ 0, NULL },							\
 }
 
+/*
+ * Baudrate descriptions for the various media types.
+ */
+struct ifmedia_baudrate {
+	int	ifmb_word;		/* media word */
+	int	ifmb_baudrate;		/* corresponding baudrate */
+};
+
+#define IFM_BAUDRATE_DESCRIPTIONS {					\
+	{ IFM_ETHER|IFM_10_T,		IF_Mbps(10) },			\
+	{ IFM_ETHER|IFM_10_2,		IF_Mbps(10) },			\
+	{ IFM_ETHER|IFM_10_5,		IF_Mbps(10) },			\
+	{ IFM_ETHER|IFM_100_TX,		IF_Mbps(100) },			\
+	{ IFM_ETHER|IFM_100_FX,		IF_Mbps(100) },			\
+	{ IFM_ETHER|IFM_100_T4,		IF_Mbps(100) },			\
+	{ IFM_ETHER|IFM_100_VG,		IF_Mbps(100) },			\
+	{ IFM_ETHER|IFM_100_T2,		IF_Mbps(100) },			\
+	{ IFM_ETHER|IFM_1000_SX,	IF_Mbps(1000) },		\
+	{ IFM_ETHER|IFM_10_STP,		IF_Mbps(10) },			\
+	{ IFM_ETHER|IFM_10_FL,		IF_Mbps(10) },			\
+	{ IFM_ETHER|IFM_1000_LX,	IF_Mbps(1000) },		\
+	{ IFM_ETHER|IFM_1000_CX,	IF_Mbps(1000) },		\
+	{ IFM_ETHER|IFM_1000_T,		IF_Mbps(1000) },		\
+	{ IFM_ETHER|IFM_HPNA_1,		IF_Mbps(1) },			\
+									\
+	{ IFM_TOKEN|IFM_TOK_STP4,	IF_Mbps(4) },			\
+	{ IFM_TOKEN|IFM_TOK_STP16,	IF_Mbps(16) },			\
+	{ IFM_TOKEN|IFM_TOK_UTP4,	IF_Mbps(4) },			\
+	{ IFM_TOKEN|IFM_TOK_UTP16,	IF_Mbps(16) },			\
+									\
+	{ IFM_FDDI|IFM_FDDI_SMF,	IF_Mbps(100) },			\
+	{ IFM_FDDI|IFM_FDDI_MMF,	IF_Mbps(100) },			\
+	{ IFM_FDDI|IFM_FDDI_UTP,	IF_Mbps(100) },			\
+									\
+	{ IFM_IEEE80211|IFM_IEEE80211_FH1, IF_Mbps(1) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_FH2, IF_Mbps(2) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS1, IF_Mbps(1) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS2, IF_Mbps(2) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS5, IF_Mbps(5) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS11, IF_Mbps(11) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS22, IF_Mbps(22) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM6, IF_Mbps(6) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM9, IF_Mbps(9) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM12, IF_Mbps(12) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM18, IF_Mbps(18) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM24, IF_Mbps(24) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM36, IF_Mbps(36) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM48, IF_Mbps(48) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM54, IF_Mbps(54) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM72, IF_Mbps(72) },		\
+									\
+	{ 0, 0 },							\
+}
 #endif	/* _NET_IF_MEDIA_H_ */
diff -r 9d42302b182d sys/net/if_var.h
--- a/sys/net/if_var.h	Thu Mar 15 21:41:11 2007 +0000
+++ b/sys/net/if_var.h	Thu Mar 15 14:18:56 2007 +0200
@@ -228,6 +228,7 @@ typedef void if_init_f_t (void *);
 #define	if_addrlen	if_data.ifi_addrlen
 #define	if_hdrlen	if_data.ifi_hdrlen
 #define	if_metric	if_data.ifi_metric
+#define	if_link_state	if_data.ifi_link_state
 #define	if_baudrate	if_data.ifi_baudrate
 #define	if_hwassist	if_data.ifi_hwassist
 #define	if_ipackets	if_data.ifi_ipackets
@@ -462,6 +463,7 @@ int	if_delmulti(struct ifnet *, struct s
 int	if_delmulti(struct ifnet *, struct sockaddr *);
 void	if_detach(struct ifnet *);
 void	if_down(struct ifnet *);
+void	if_link_state_change(struct ifnet *);
 void	if_initname(struct ifnet *, const char *, int);
 int	if_printf(struct ifnet *, const char *, ...) __printflike(2, 3);
 void	if_route(struct ifnet *, int flag, int fam);





More information about the Submit mailing list