kern/142927: handle parent interface link layer address changes in if_vlan(4)

Nikolay Denev ndenev at gmail.com
Mon Jan 18 06:00:19 UTC 2010


>Number:         142927
>Category:       kern
>Synopsis:       handle parent interface link layer address changes in if_vlan(4)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Jan 18 06:00:16 UTC 2010
>Closed-Date:
>Last-Modified:
>Originator:     Nikolay Denev
>Release:        8.0-STABLE
>Organization:
>Environment:
FreeBSD nas.totalterror.net 8.0-STABLE FreeBSD 8.0-STABLE #58: Sat Jan 16 14:54:22 EET 2010     ndenev at nas.totalterror.net:/usr/obj/usr/src/sys/NAS  amd64
>Description:
When if_vlan(4) interfaces are being configured on another network interface the parent's MAC address is being setup as the if_vlan(4)'s interface link layer address.
The problem is that if the parent interface link layer address is changed later, the if_vlan(4)'s link layer address remains the same and it stops functioning.

>How-To-Repeat:
This can happen if one for example uses if_vlan(4) on top of if_lagg(4) and configures them in rc.conf using the ${interface}.${vlan} method.
The rc scipts create the "empty" if_lagg(4) interface with MAC of 00:00:00:00:00:00 then attach the vlans on it copying the same all zeroes MAC address, and then it adds the physical interfaces to the if_lagg(4) interface giving it's MAC address, but this leaves the vlans with all zeroes MACs and not working.
>Fix:
* Declare a new EVENTHANDLER called "iflladdr_event" similar to the "ifaddr_event" in sys/net/if_var.h
* Teach sys/net/if.c:ifhwioctl() to invoke the new handler on SIOCSIFLLADDR commands.
* Teach if_bridge(4) to invoke the handler when it's MAC address is changed due to adding or removing interfaces. (not all adds/removes, only those that change if_bridge's lladdr)
* Teach if_lagg(4) to invoke the handler in its lagg_lladdr() function.
* Teach if_vlan(4) to register/deregister the handler on module load/unload and add vlan_iflladdr() function to change the llladdrs on the attached vlans of the parent interface that fired the event.



Patch attached with submission follows:

diff -ru .zfs/snapshot/orig/sys/net/if.c sys/net/if.c
--- .zfs/snapshot/orig/sys/net/if.c	2010-01-11 08:14:58.274826988 +0200
+++ sys/net/if.c	2010-01-15 11:23:40.194343979 +0200
@@ -2341,6 +2341,7 @@
 			return (error);
 		error = if_setlladdr(ifp,
 		    ifr->ifr_addr.sa_data, ifr->ifr_addr.sa_len);
+		EVENTHANDLER_INVOKE(iflladdr_event, ifp);
 		break;
 
 	case SIOCAIFGROUP:
diff -ru .zfs/snapshot/orig/sys/net/if_bridge.c sys/net/if_bridge.c
--- .zfs/snapshot/orig/sys/net/if_bridge.c	2009-09-09 23:39:06.796873085 +0300
+++ sys/net/if_bridge.c	2010-01-16 17:10:43.754373955 +0200
@@ -915,6 +915,7 @@
 			    IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN);
 			sc->sc_ifaddr = fif;
 		}
+		EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp);
 	}
 
 	bridge_mutecaps(sc);	/* recalcuate now this interface is removed */
@@ -1031,6 +1032,7 @@
 	    !memcmp(IF_LLADDR(sc->sc_ifp), sc->sc_defaddr, ETHER_ADDR_LEN)) {
 		bcopy(IF_LLADDR(ifs), IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN);
 		sc->sc_ifaddr = ifs;
+		EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp);
 	}
 
 	ifs->if_bridge = sc;
diff -ru .zfs/snapshot/orig/sys/net/if_lagg.c sys/net/if_lagg.c
--- .zfs/snapshot/orig/sys/net/if_lagg.c	2009-08-24 07:32:17.222829477 +0300
+++ sys/net/if_lagg.c	2010-01-16 17:04:04.012810623 +0200
@@ -303,6 +303,7 @@
 	/* Let the protocol know the MAC has changed */
 	if (sc->sc_lladdr != NULL)
 		(*sc->sc_lladdr)(sc);
+	EVENTHANDLER_INVOKE(iflladdr_event, ifp);
 }
 
 static void
diff -ru .zfs/snapshot/orig/sys/net/if_var.h sys/net/if_var.h
--- .zfs/snapshot/orig/sys/net/if_var.h	2010-01-11 08:14:58.845828172 +0200
+++ sys/net/if_var.h	2010-01-15 11:19:31.291719218 +0200
@@ -341,6 +341,9 @@
 } while(0)
 
 #ifdef _KERNEL
+/* interface link layer address change event */
+typedef void (*iflladdr_event_handler_t)(void *, struct ifnet *);
+EVENTHANDLER_DECLARE(iflladdr_event, iflladdr_event_handler_t);
 /* interface address change event */
 typedef void (*ifaddr_event_handler_t)(void *, struct ifnet *);
 EVENTHANDLER_DECLARE(ifaddr_event, ifaddr_event_handler_t);
diff -ru .zfs/snapshot/orig/sys/net/if_vlan.c sys/net/if_vlan.c
--- .zfs/snapshot/orig/sys/net/if_vlan.c	2010-01-11 08:14:58.882827943 +0200
+++ sys/net/if_vlan.c	2010-01-17 23:27:24.830200364 +0200
@@ -138,6 +138,7 @@
 static MALLOC_DEFINE(M_VLAN, VLANNAME, "802.1Q Virtual LAN Interface");
 
 static eventhandler_tag ifdetach_tag;
+static eventhandler_tag iflladdr_tag;
 
 /*
  * We have a global mutex, that is used to serialize configuration
@@ -199,6 +200,7 @@
 static	int vlan_clone_destroy(struct if_clone *, struct ifnet *);
 
 static	void vlan_ifdetach(void *arg, struct ifnet *ifp);
+static  void vlan_iflladdr(void *arg, struct ifnet *ifp);
 
 static	struct if_clone vlan_cloner = IFC_CLONE_INITIALIZER(VLANNAME, NULL,
     IF_MAXUNIT, NULL, vlan_clone_match, vlan_clone_create, vlan_clone_destroy);
@@ -463,6 +465,41 @@
 }
 
 /*
+ * A handler for parent interface link layer address changes.
+ * If the parent interface link layer address is changed we
+ * should also change it on all children vlans.
+ */
+static void
+vlan_iflladdr(void *arg __unused, struct ifnet *ifp)
+{
+	struct ifvlan *ifv;
+	int i;
+
+	/*
+	 * Check if it's a trunk interface first of all
+	 * to avoid needless locking.
+	 */
+	if (ifp->if_vlantrunk == NULL)
+		return;
+
+	VLAN_LOCK();
+	/*
+	 * OK, it's a trunk.  Loop over and change all vlan's lladdrs on it.
+	 */
+#ifdef VLAN_ARRAY
+	for (i = 0; i < VLAN_ARRAY_SIZE; i++)
+		if ((ifv = ifp->if_vlantrunk->vlans[i]))
+			if_setlladdr(ifv->ifv_ifp, IF_LLADDR(ifp), ETHER_ADDR_LEN);
+#else /* VLAN_ARRAY */
+	for (i = 0; i < (1 << ifp->if_vlantrunk->hwidth); i++)
+		LIST_FOREACH(ifv, &ifp->if_vlantrunk->hash[i], ifv_list)
+			if_setlladdr(ifv->ifv_ifp, IF_LLADDR(ifp), ETHER_ADDR_LEN);
+#endif /* VLAN_ARRAY */
+	VLAN_UNLOCK();
+
+}
+
+/*
  * A handler for network interface departure events.
  * Track departure of trunks here so that we don't access invalid
  * pointers or whatever if a trunk is ripped from under us, e.g.,
@@ -537,6 +574,10 @@
 		    vlan_ifdetach, NULL, EVENTHANDLER_PRI_ANY);
 		if (ifdetach_tag == NULL)
 			return (ENOMEM);
+		iflladdr_tag = EVENTHANDLER_REGISTER(iflladdr_event,
+		    vlan_iflladdr, NULL, EVENTHANDLER_PRI_ANY);
+		if (iflladdr_tag == NULL)
+			return (ENOMEM);
 		VLAN_LOCK_INIT();
 		vlan_input_p = vlan_input;
 		vlan_link_state_p = vlan_link_state;
@@ -555,6 +596,7 @@
 	case MOD_UNLOAD:
 		if_clone_detach(&vlan_cloner);
 		EVENTHANDLER_DEREGISTER(ifnet_departure_event, ifdetach_tag);
+		EVENTHANDLER_DEREGISTER(iflladdr_event, iflladdr_tag);
 		vlan_input_p = NULL;
 		vlan_link_state_p = NULL;
 		vlan_trunk_cap_p = NULL;


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list