From owner-freebsd-bugs@FreeBSD.ORG Mon Jan 18 06:00:18 2010 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id E846610656AD for ; Mon, 18 Jan 2010 06:00:18 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id DF6B68FC2E for ; Mon, 18 Jan 2010 06:00:16 +0000 (UTC) Received: from freefall.freebsd.org (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.3/8.14.3) with ESMTP id o0I60Gqv045898 for ; Mon, 18 Jan 2010 06:00:16 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.3/8.14.3/Submit) id o0I60Gqn045897; Mon, 18 Jan 2010 06:00:16 GMT (envelope-from gnats) Resent-Date: Mon, 18 Jan 2010 06:00:16 GMT Resent-Message-Id: <201001180600.o0I60Gqn045897@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Nikolay Denev Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 740D1106566C for ; Mon, 18 Jan 2010 05:59:29 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21]) by mx1.freebsd.org (Postfix) with ESMTP id 61C468FC17 for ; Mon, 18 Jan 2010 05:59:29 +0000 (UTC) Received: from www.freebsd.org (localhost [127.0.0.1]) by www.freebsd.org (8.14.3/8.14.3) with ESMTP id o0I5xTve080001 for ; Mon, 18 Jan 2010 05:59:29 GMT (envelope-from nobody@www.freebsd.org) Received: (from nobody@localhost) by www.freebsd.org (8.14.3/8.14.3/Submit) id o0I5xTMh080000; Mon, 18 Jan 2010 05:59:29 GMT (envelope-from nobody) Message-Id: <201001180559.o0I5xTMh080000@www.freebsd.org> Date: Mon, 18 Jan 2010 05:59:29 GMT From: Nikolay Denev To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Cc: Subject: kern/142927: handle parent interface link layer address changes in if_vlan(4) X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 18 Jan 2010 06:00:19 -0000 >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@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: