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