From 7c61d4937070d33c4d0334734b83992b2c9af31c Mon Sep 17 00:00:00 2001 From: Andrew Thompson Date: Tue, 25 May 2010 02:36:06 +0000 Subject: [PATCH] MFC r202588 Declare a new EVENTHANDLER called iflladdr_event which signals that the L2 address on an interface has changed. This lets stacked interfaces such as vlan(4) detect that their lower interface has changed and adjust things in order to keep working. Previously this situation broke at least vlan(4) and lagg(4) configurations. The EVENTHANDLER_INVOKE call was not placed within if_setlladdr() due to the risk of a loop. PR: kern/142927 Submitted by: Nikolay Denev MFC r202611 Do not hold the lock over if_setlladdr() as it calls into the interface driver init routine. --- sys/net/if.c | 1 + sys/net/if_bridge.c | 2 ++ sys/net/if_lagg.c | 1 + sys/net/if_var.h | 3 +++ sys/net/if_vlan.c | 47 ++++++++++++++++++++++++++++++++++++++++ sys/netgraph/ng_eiface.c | 1 + sys/netgraph/ng_ether.c | 1 + sys/netgraph/ng_fec.c | 1 + 8 files changed, 57 insertions(+) diff --git a/sys/net/if.c b/sys/net/if.c index ea677e4d82e0..51ac2734e96d 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -2416,6 +2416,7 @@ ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td) 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 --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index fe4e18b32506..5f33dd547a1b 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -937,6 +937,7 @@ bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif, 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 */ @@ -1052,6 +1053,7 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg) !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 --git a/sys/net/if_lagg.c b/sys/net/if_lagg.c index 1deab5628fc1..5246c74dc1eb 100644 --- a/sys/net/if_lagg.c +++ b/sys/net/if_lagg.c @@ -303,6 +303,7 @@ lagg_lladdr(struct lagg_softc *sc, uint8_t *lladdr) /* 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 --git a/sys/net/if_var.h b/sys/net/if_var.h index 7685e8be7569..7c24a471c529 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -342,6 +342,9 @@ void if_maddr_runlock(struct ifnet *ifp); /* if_multiaddrs */ } 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 --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c index 3bce028e23ef..5fadf777954c 100644 --- a/sys/net/if_vlan.c +++ b/sys/net/if_vlan.c @@ -139,6 +139,7 @@ SYSCTL_INT(_net_link_vlan, OID_AUTO, soft_pad, CTLFLAG_RW, &soft_pad, 0, 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 @@ -200,6 +201,7 @@ static int vlan_clone_create(struct if_clone *, char *, size_t, caddr_t); 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,46 @@ vlan_setmulti(struct ifnet *ifp) return (0); } +/* + * 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; +#ifndef VLAN_ARRAY + struct ifvlan *next; +#endif + 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])) { +#else /* VLAN_ARRAY */ + for (i = 0; i < (1 << ifp->if_vlantrunk->hwidth); i++) + LIST_FOREACH_SAFE(ifv, &ifp->if_vlantrunk->hash[i], ifv_list, next) { +#endif /* VLAN_ARRAY */ + VLAN_UNLOCK(); + if_setlladdr(ifv->ifv_ifp, IF_LLADDR(ifp), ETHER_ADDR_LEN); + VLAN_LOCK(); + } + VLAN_UNLOCK(); + +} + /* * A handler for network interface departure events. * Track departure of trunks here so that we don't access invalid @@ -538,6 +580,10 @@ vlan_modevent(module_t mod, int type, void *data) 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; @@ -556,6 +602,7 @@ vlan_modevent(module_t mod, int type, void *data) 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; diff --git a/sys/netgraph/ng_eiface.c b/sys/netgraph/ng_eiface.c index ce23683c59b7..2cf2a74fe886 100644 --- a/sys/netgraph/ng_eiface.c +++ b/sys/netgraph/ng_eiface.c @@ -432,6 +432,7 @@ ng_eiface_rcvmsg(node_p node, item_p item, hook_p lasthook) } error = if_setlladdr(priv->ifp, (u_char *)msg->data, ETHER_ADDR_LEN); + EVENTHANDLER_INVOKE(iflladdr_event, priv->ifp); break; } diff --git a/sys/netgraph/ng_ether.c b/sys/netgraph/ng_ether.c index dc38d4157db6..5abc5aaebcba 100644 --- a/sys/netgraph/ng_ether.c +++ b/sys/netgraph/ng_ether.c @@ -481,6 +481,7 @@ ng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) } error = if_setlladdr(priv->ifp, (u_char *)msg->data, ETHER_ADDR_LEN); + EVENTHANDLER_INVOKE(iflladdr_event, priv->ifp); break; } case NGM_ETHER_GET_PROMISC: diff --git a/sys/netgraph/ng_fec.c b/sys/netgraph/ng_fec.c index e694dd8de1d9..4e4dcaeb58b9 100644 --- a/sys/netgraph/ng_fec.c +++ b/sys/netgraph/ng_fec.c @@ -433,6 +433,7 @@ ng_fec_addport(struct ng_fec_private *priv, char *iface) /* Set up phony MAC address. */ if_setlladdr(bifp, IF_LLADDR(ifp), ETHER_ADDR_LEN); + EVENTHANDLER_INVOKE(iflladdr_event, bifp); /* Save original input vector */ new->fec_if_input = bifp->if_input;