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;