Changeset View
Standalone View
sys/net/if_bridge.c
| Show First 20 Lines • Show All 183 Lines • ▼ Show 20 Lines | |||||
| */ | */ | ||||
| #define BRIDGE_IFCAPS_STRIP IFCAP_LRO | #define BRIDGE_IFCAPS_STRIP IFCAP_LRO | ||||
| /* | /* | ||||
| * Bridge locking | * Bridge locking | ||||
| */ | */ | ||||
| #define BRIDGE_LOCK_INIT(_sc) do { \ | #define BRIDGE_LOCK_INIT(_sc) do { \ | ||||
| mtx_init(&(_sc)->sc_mtx, "if_bridge", NULL, MTX_DEF); \ | mtx_init(&(_sc)->sc_mtx, "if_bridge", NULL, MTX_DEF); \ | ||||
| cv_init(&(_sc)->sc_cv, "if_bridge_cv"); \ | |||||
| } while (0) | } while (0) | ||||
| #define BRIDGE_LOCK_DESTROY(_sc) do { \ | #define BRIDGE_LOCK_DESTROY(_sc) do { \ | ||||
| mtx_destroy(&(_sc)->sc_mtx); \ | mtx_destroy(&(_sc)->sc_mtx); \ | ||||
| cv_destroy(&(_sc)->sc_cv); \ | |||||
| } while (0) | } while (0) | ||||
| #define BRIDGE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) | #define BRIDGE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) | ||||
| #define BRIDGE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) | #define BRIDGE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) | ||||
| #define BRIDGE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) | #define BRIDGE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) | ||||
| #define BRIDGE_UNLOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED) | #define BRIDGE_UNLOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED) | ||||
| #define BRIDGE_LOCK2REF(_sc, _err) do { \ | |||||
| mtx_assert(&(_sc)->sc_mtx, MA_OWNED); \ | |||||
| if ((_sc)->sc_iflist_xcnt > 0) \ | |||||
| (_err) = EBUSY; \ | |||||
| else \ | |||||
| (_sc)->sc_iflist_ref++; \ | |||||
| mtx_unlock(&(_sc)->sc_mtx); \ | |||||
| } while (0) | |||||
| #define BRIDGE_UNREF(_sc) do { \ | |||||
| mtx_lock(&(_sc)->sc_mtx); \ | |||||
| (_sc)->sc_iflist_ref--; \ | |||||
| if (((_sc)->sc_iflist_xcnt > 0) && ((_sc)->sc_iflist_ref == 0)) \ | |||||
| cv_broadcast(&(_sc)->sc_cv); \ | |||||
| mtx_unlock(&(_sc)->sc_mtx); \ | |||||
| } while (0) | |||||
| #define BRIDGE_XLOCK(_sc) do { \ | |||||
| mtx_assert(&(_sc)->sc_mtx, MA_OWNED); \ | |||||
| (_sc)->sc_iflist_xcnt++; \ | |||||
| while ((_sc)->sc_iflist_ref > 0) \ | |||||
| cv_wait(&(_sc)->sc_cv, &(_sc)->sc_mtx); \ | |||||
| } while (0) | |||||
| #define BRIDGE_XDROP(_sc) do { \ | |||||
| mtx_assert(&(_sc)->sc_mtx, MA_OWNED); \ | |||||
| (_sc)->sc_iflist_xcnt--; \ | |||||
| } while (0) | |||||
melifaro: Are `BRIDGE_LOCK2REF`/ `BRIDGE_UNREF` still needed? | |||||
| /* | /* | ||||
| * Bridge interface list entry. | * Bridge interface list entry. | ||||
| */ | */ | ||||
| struct bridge_iflist { | struct bridge_iflist { | ||||
| CK_LIST_ENTRY(bridge_iflist) bif_next; | CK_LIST_ENTRY(bridge_iflist) bif_next; | ||||
| struct ifnet *bif_ifp; /* member if */ | struct ifnet *bif_ifp; /* member if */ | ||||
| struct bstp_port bif_stp; /* STP state */ | struct bstp_port bif_stp; /* STP state */ | ||||
| uint32_t bif_flags; /* member if flags */ | uint32_t bif_flags; /* member if flags */ | ||||
| int bif_savedcaps; /* saved capabilities */ | int bif_savedcaps; /* saved capabilities */ | ||||
| uint32_t bif_addrmax; /* max # of addresses */ | uint32_t bif_addrmax; /* max # of addresses */ | ||||
| uint32_t bif_addrcnt; /* cur. # of addresses */ | uint32_t bif_addrcnt; /* cur. # of addresses */ | ||||
| uint32_t bif_addrexceeded;/* # of address violations */ | uint32_t bif_addrexceeded;/* # of address violations */ | ||||
| struct epoch_context bif_epoch_ctx; | |||||
Not Done Inline ActionsExtra blank lines (here and elsewhere)? emaste: Extra blank lines (here and elsewhere)? | |||||
| }; | }; | ||||
| /* | /* | ||||
| * Bridge route node. | * Bridge route node. | ||||
| */ | */ | ||||
| struct bridge_rtnode { | struct bridge_rtnode { | ||||
| CK_LIST_ENTRY(bridge_rtnode) brt_hash; /* hash table linkage */ | CK_LIST_ENTRY(bridge_rtnode) brt_hash; /* hash table linkage */ | ||||
| CK_LIST_ENTRY(bridge_rtnode) brt_list; /* list linkage */ | CK_LIST_ENTRY(bridge_rtnode) brt_list; /* list linkage */ | ||||
| struct bridge_iflist *brt_dst; /* destination if */ | struct bridge_iflist *brt_dst; /* destination if */ | ||||
| unsigned long brt_expire; /* expiration time */ | unsigned long brt_expire; /* expiration time */ | ||||
| uint8_t brt_flags; /* address flags */ | uint8_t brt_flags; /* address flags */ | ||||
| uint8_t brt_addr[ETHER_ADDR_LEN]; | uint8_t brt_addr[ETHER_ADDR_LEN]; | ||||
| uint16_t brt_vlan; /* vlan id */ | uint16_t brt_vlan; /* vlan id */ | ||||
| struct vnet *brt_vnet; | |||||
| struct epoch_context brt_epoch_ctx; | |||||
| }; | }; | ||||
| #define brt_ifp brt_dst->bif_ifp | #define brt_ifp brt_dst->bif_ifp | ||||
| /* | /* | ||||
| * Software state for each bridge. | * Software state for each bridge. | ||||
| */ | */ | ||||
| struct bridge_softc { | struct bridge_softc { | ||||
| struct ifnet *sc_ifp; /* make this an interface */ | struct ifnet *sc_ifp; /* make this an interface */ | ||||
| LIST_ENTRY(bridge_softc) sc_list; | LIST_ENTRY(bridge_softc) sc_list; | ||||
| struct mtx sc_mtx; | struct mtx sc_mtx; | ||||
| struct cv sc_cv; | |||||
| uint32_t sc_brtmax; /* max # of addresses */ | uint32_t sc_brtmax; /* max # of addresses */ | ||||
| uint32_t sc_brtcnt; /* cur. # of addresses */ | uint32_t sc_brtcnt; /* cur. # of addresses */ | ||||
| uint32_t sc_brttimeout; /* rt timeout in seconds */ | uint32_t sc_brttimeout; /* rt timeout in seconds */ | ||||
| struct callout sc_brcallout; /* bridge callout */ | struct callout sc_brcallout; /* bridge callout */ | ||||
| uint32_t sc_iflist_ref; /* refcount for sc_iflist */ | |||||
| uint32_t sc_iflist_xcnt; /* refcount for sc_iflist */ | |||||
| CK_LIST_HEAD(, bridge_iflist) sc_iflist; /* member interface list */ | CK_LIST_HEAD(, bridge_iflist) sc_iflist; /* member interface list */ | ||||
| CK_LIST_HEAD(, bridge_rtnode) *sc_rthash; /* our forwarding table */ | CK_LIST_HEAD(, bridge_rtnode) *sc_rthash; /* our forwarding table */ | ||||
| CK_LIST_HEAD(, bridge_rtnode) sc_rtlist; /* list version of above */ | CK_LIST_HEAD(, bridge_rtnode) sc_rtlist; /* list version of above */ | ||||
| uint32_t sc_rthash_key; /* key for hash */ | uint32_t sc_rthash_key; /* key for hash */ | ||||
| CK_LIST_HEAD(, bridge_iflist) sc_spanlist; /* span ports list */ | CK_LIST_HEAD(, bridge_iflist) sc_spanlist; /* span ports list */ | ||||
| struct bstp_state sc_stp; /* STP state */ | struct bstp_state sc_stp; /* STP state */ | ||||
| uint32_t sc_brtexceeded; /* # of cache drops */ | uint32_t sc_brtexceeded; /* # of cache drops */ | ||||
| struct ifnet *sc_ifaddr; /* member mac copied from */ | struct ifnet *sc_ifaddr; /* member mac copied from */ | ||||
| struct ether_addr sc_defaddr; /* Default MAC address */ | struct ether_addr sc_defaddr; /* Default MAC address */ | ||||
| struct epoch_context sc_epoch_ctx; | |||||
| }; | }; | ||||
| VNET_DEFINE_STATIC(struct mtx, bridge_list_mtx); | VNET_DEFINE_STATIC(struct mtx, bridge_list_mtx); | ||||
| #define V_bridge_list_mtx VNET(bridge_list_mtx) | #define V_bridge_list_mtx VNET(bridge_list_mtx) | ||||
| static eventhandler_tag bridge_detach_cookie; | static eventhandler_tag bridge_detach_cookie; | ||||
| int bridge_rtable_prune_period = BRIDGE_RTABLE_PRUNE_PERIOD; | int bridge_rtable_prune_period = BRIDGE_RTABLE_PRUNE_PERIOD; | ||||
| ▲ Show 20 Lines • Show All 304 Lines • ▼ Show 20 Lines | |||||
| static void | static void | ||||
| vnet_bridge_uninit(const void *unused __unused) | vnet_bridge_uninit(const void *unused __unused) | ||||
| { | { | ||||
| if_clone_detach(V_bridge_cloner); | if_clone_detach(V_bridge_cloner); | ||||
| V_bridge_cloner = NULL; | V_bridge_cloner = NULL; | ||||
| BRIDGE_LIST_LOCK_DESTROY(); | BRIDGE_LIST_LOCK_DESTROY(); | ||||
| /* Callbacks may use the UMA zone. */ | |||||
Not Done Inline Actionsnon-style(9) comment, but maybe can shorten it to just e.g. "callbacks may be using the UMA zone"? emaste: non-style(9) comment, but maybe can shorten it to just e.g. "callbacks may be using the UMA… | |||||
Not Done Inline ActionsOops, my example should have used sentence case, /* Callbacks may use the UMA zone. */ emaste: Oops, my example should have used sentence case, `/* Callbacks may use the UMA zone. */` | |||||
| epoch_drain_callbacks(net_epoch_preempt); | |||||
| uma_zdestroy(V_bridge_rtnode_zone); | uma_zdestroy(V_bridge_rtnode_zone); | ||||
| } | } | ||||
| VNET_SYSUNINIT(vnet_bridge_uninit, SI_SUB_PSEUDO, SI_ORDER_ANY, | VNET_SYSUNINIT(vnet_bridge_uninit, SI_SUB_PSEUDO, SI_ORDER_ANY, | ||||
| vnet_bridge_uninit, NULL); | vnet_bridge_uninit, NULL); | ||||
| static int | static int | ||||
| bridge_modevent(module_t mod, int type, void *data) | bridge_modevent(module_t mod, int type, void *data) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 146 Lines • ▼ Show 20 Lines | bridge_clone_create(struct if_clone *ifc, int unit, caddr_t params) | ||||
| BRIDGE_LIST_LOCK(); | BRIDGE_LIST_LOCK(); | ||||
| LIST_INSERT_HEAD(&V_bridge_list, sc, sc_list); | LIST_INSERT_HEAD(&V_bridge_list, sc, sc_list); | ||||
| BRIDGE_LIST_UNLOCK(); | BRIDGE_LIST_UNLOCK(); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static void | |||||
| bridge_clone_destroy_cb(struct epoch_context *ctx) | |||||
| { | |||||
| struct bridge_softc *sc; | |||||
| sc = __containerof(ctx, struct bridge_softc, sc_epoch_ctx); | |||||
Not Done Inline Actionshrmph emaste: hrmph | |||||
Not Done Inline ActionsYeah. I don't really like the __containerof() either. It's the very definition of a layering violation. However, I don't think there's a way around it without substantially rewriting if_bridge. Perhaps we can make this look a little less unsightly with a macro? philip: Yeah. I don't really like the `__containerof()` either. It's the very definition of a… | |||||
Done Inline ActionsIt's how every NET_EPOCH_CALL user in the kernel does it. We could extend struct epoch_context with a void* to pass this sort of thing, but it'd mean that struct bridge_softc would contain a struct epoch_context, which in turn would have a pointer to the struct bridge_softc. We could also wrap it in a macro, but all that'd do is hide what's going on, and we'd still have to pass all of the same arguments, or have different macros for the different structures we use this for. kp: It's how every NET_EPOCH_CALL user in the kernel does it.
We could extend struct epoch_context… | |||||
| BRIDGE_LOCK_DESTROY(sc); | |||||
| free(sc, M_DEVBUF); | |||||
| } | |||||
| /* | /* | ||||
| * bridge_clone_destroy: | * bridge_clone_destroy: | ||||
| * | * | ||||
| * Destroy a bridge instance. | * Destroy a bridge instance. | ||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_clone_destroy(struct ifnet *ifp) | bridge_clone_destroy(struct ifnet *ifp) | ||||
| { | { | ||||
| struct bridge_softc *sc = ifp->if_softc; | struct bridge_softc *sc = ifp->if_softc; | ||||
| struct bridge_iflist *bif; | struct bridge_iflist *bif; | ||||
| struct epoch_tracker et; | |||||
| NET_EPOCH_ENTER(et); | |||||
| BRIDGE_LOCK(sc); | BRIDGE_LOCK(sc); | ||||
| bridge_stop(ifp, 1); | bridge_stop(ifp, 1); | ||||
| ifp->if_flags &= ~IFF_UP; | ifp->if_flags &= ~IFF_UP; | ||||
| while ((bif = CK_LIST_FIRST(&sc->sc_iflist)) != NULL) | while ((bif = CK_LIST_FIRST(&sc->sc_iflist)) != NULL) | ||||
| bridge_delete_member(sc, bif, 0); | bridge_delete_member(sc, bif, 0); | ||||
| while ((bif = CK_LIST_FIRST(&sc->sc_spanlist)) != NULL) { | while ((bif = CK_LIST_FIRST(&sc->sc_spanlist)) != NULL) { | ||||
| bridge_delete_span(sc, bif); | bridge_delete_span(sc, bif); | ||||
| } | } | ||||
| /* Tear down the routing table. */ | /* Tear down the routing table. */ | ||||
| bridge_rtable_fini(sc); | bridge_rtable_fini(sc); | ||||
| BRIDGE_UNLOCK(sc); | BRIDGE_UNLOCK(sc); | ||||
| callout_drain(&sc->sc_brcallout); | callout_drain(&sc->sc_brcallout); | ||||
| BRIDGE_LIST_LOCK(); | BRIDGE_LIST_LOCK(); | ||||
| LIST_REMOVE(sc, sc_list); | LIST_REMOVE(sc, sc_list); | ||||
| BRIDGE_LIST_UNLOCK(); | BRIDGE_LIST_UNLOCK(); | ||||
| bstp_detach(&sc->sc_stp); | bstp_detach(&sc->sc_stp); | ||||
| NET_EPOCH_EXIT(et); | |||||
| ether_ifdetach(ifp); | ether_ifdetach(ifp); | ||||
| if_free(ifp); | if_free(ifp); | ||||
| BRIDGE_LOCK_DESTROY(sc); | NET_EPOCH_CALL(bridge_clone_destroy_cb, &sc->sc_epoch_ctx); | ||||
| free(sc, M_DEVBUF); | |||||
| } | } | ||||
| /* | /* | ||||
| * bridge_ioctl: | * bridge_ioctl: | ||||
| * | * | ||||
| * Handle a control request from the operator. | * Handle a control request from the operator. | ||||
| */ | */ | ||||
| static int | static int | ||||
| Show All 9 Lines | union { | ||||
| struct ifbareq ifbareq; | struct ifbareq ifbareq; | ||||
| struct ifbaconf ifbaconf; | struct ifbaconf ifbaconf; | ||||
| struct ifbrparam ifbrparam; | struct ifbrparam ifbrparam; | ||||
| struct ifbropreq ifbropreq; | struct ifbropreq ifbropreq; | ||||
| } args; | } args; | ||||
| struct ifdrv *ifd = (struct ifdrv *) data; | struct ifdrv *ifd = (struct ifdrv *) data; | ||||
| const struct bridge_control *bc; | const struct bridge_control *bc; | ||||
| int error = 0, oldmtu; | int error = 0, oldmtu; | ||||
| struct epoch_tracker et; | |||||
| NET_EPOCH_ENTER(et); | |||||
| switch (cmd) { | switch (cmd) { | ||||
| case SIOCADDMULTI: | case SIOCADDMULTI: | ||||
| case SIOCDELMULTI: | case SIOCDELMULTI: | ||||
| break; | break; | ||||
| case SIOCGDRVSPEC: | case SIOCGDRVSPEC: | ||||
| case SIOCSDRVSPEC: | case SIOCSDRVSPEC: | ||||
| ▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | default: | ||||
| /* | /* | ||||
| * drop the lock as ether_ioctl() will call bridge_start() and | * drop the lock as ether_ioctl() will call bridge_start() and | ||||
| * cause the lock to be recursed. | * cause the lock to be recursed. | ||||
| */ | */ | ||||
| error = ether_ioctl(ifp, cmd, data); | error = ether_ioctl(ifp, cmd, data); | ||||
| break; | break; | ||||
| } | } | ||||
| NET_EPOCH_EXIT(et); | |||||
| return (error); | return (error); | ||||
| } | } | ||||
| /* | /* | ||||
| * bridge_mutecaps: | * bridge_mutecaps: | ||||
| * | * | ||||
| * Clear or restore unwanted capabilities on the member interface | * Clear or restore unwanted capabilities on the member interface | ||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_mutecaps(struct bridge_softc *sc) | bridge_mutecaps(struct bridge_softc *sc) | ||||
| { | { | ||||
| struct bridge_iflist *bif; | struct bridge_iflist *bif; | ||||
| int enabled, mask; | int enabled, mask; | ||||
| BRIDGE_LOCK_ASSERT(sc); | |||||
| /* Initial bitmask of capabilities to test */ | /* Initial bitmask of capabilities to test */ | ||||
| mask = BRIDGE_IFCAPS_MASK; | mask = BRIDGE_IFCAPS_MASK; | ||||
| CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | ||||
| /* Every member must support it or its disabled */ | /* Every member must support it or its disabled */ | ||||
| mask &= bif->bif_savedcaps; | mask &= bif->bif_savedcaps; | ||||
| } | } | ||||
| BRIDGE_XLOCK(sc); | |||||
| CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | ||||
| enabled = bif->bif_ifp->if_capenable; | enabled = bif->bif_ifp->if_capenable; | ||||
| enabled &= ~BRIDGE_IFCAPS_STRIP; | enabled &= ~BRIDGE_IFCAPS_STRIP; | ||||
| /* strip off mask bits and enable them again if allowed */ | /* strip off mask bits and enable them again if allowed */ | ||||
| enabled &= ~BRIDGE_IFCAPS_MASK; | enabled &= ~BRIDGE_IFCAPS_MASK; | ||||
| enabled |= mask; | enabled |= mask; | ||||
| BRIDGE_UNLOCK(sc); | BRIDGE_UNLOCK(sc); | ||||
| bridge_set_ifcap(sc, bif, enabled); | bridge_set_ifcap(sc, bif, enabled); | ||||
| BRIDGE_LOCK(sc); | BRIDGE_LOCK(sc); | ||||
| } | } | ||||
| BRIDGE_XDROP(sc); | |||||
| } | } | ||||
| static void | static void | ||||
| bridge_set_ifcap(struct bridge_softc *sc, struct bridge_iflist *bif, int set) | bridge_set_ifcap(struct bridge_softc *sc, struct bridge_iflist *bif, int set) | ||||
| { | { | ||||
| struct ifnet *ifp = bif->bif_ifp; | struct ifnet *ifp = bif->bif_ifp; | ||||
| struct ifreq ifr; | struct ifreq ifr; | ||||
| int error, mask, stuck; | int error, mask, stuck; | ||||
| Show All 24 Lines | |||||
| * Lookup a bridge member interface. | * Lookup a bridge member interface. | ||||
| */ | */ | ||||
| static struct bridge_iflist * | static struct bridge_iflist * | ||||
| bridge_lookup_member(struct bridge_softc *sc, const char *name) | bridge_lookup_member(struct bridge_softc *sc, const char *name) | ||||
| { | { | ||||
| struct bridge_iflist *bif; | struct bridge_iflist *bif; | ||||
| struct ifnet *ifp; | struct ifnet *ifp; | ||||
| BRIDGE_LOCK_ASSERT(sc); | NET_EPOCH_ASSERT(); | ||||
| CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | ||||
| ifp = bif->bif_ifp; | ifp = bif->bif_ifp; | ||||
| if (strcmp(ifp->if_xname, name) == 0) | if (strcmp(ifp->if_xname, name) == 0) | ||||
| return (bif); | return (bif); | ||||
| } | } | ||||
| return (NULL); | return (NULL); | ||||
| } | } | ||||
| /* | /* | ||||
| * bridge_lookup_member_if: | * bridge_lookup_member_if: | ||||
| * | * | ||||
| * Lookup a bridge member interface by ifnet*. | * Lookup a bridge member interface by ifnet*. | ||||
| */ | */ | ||||
| static struct bridge_iflist * | static struct bridge_iflist * | ||||
| bridge_lookup_member_if(struct bridge_softc *sc, struct ifnet *member_ifp) | bridge_lookup_member_if(struct bridge_softc *sc, struct ifnet *member_ifp) | ||||
| { | { | ||||
| struct bridge_iflist *bif; | struct bridge_iflist *bif; | ||||
| BRIDGE_LOCK_ASSERT(sc); | NET_EPOCH_ASSERT(); | ||||
| CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | ||||
| if (bif->bif_ifp == member_ifp) | if (bif->bif_ifp == member_ifp) | ||||
| return (bif); | return (bif); | ||||
| } | } | ||||
| return (NULL); | return (NULL); | ||||
| } | } | ||||
| static void | |||||
| bridge_delete_member_cb(struct epoch_context *ctx) | |||||
| { | |||||
| struct bridge_iflist *bif; | |||||
| bif = __containerof(ctx, struct bridge_iflist, bif_epoch_ctx); | |||||
| free(bif, M_DEVBUF); | |||||
| } | |||||
| /* | /* | ||||
| * bridge_delete_member: | * bridge_delete_member: | ||||
| * | * | ||||
| * Delete the specified member interface. | * Delete the specified member interface. | ||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif, | bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif, | ||||
| int gone) | int gone) | ||||
| { | { | ||||
| struct ifnet *ifs = bif->bif_ifp; | struct ifnet *ifs = bif->bif_ifp; | ||||
| struct ifnet *fif = NULL; | struct ifnet *fif = NULL; | ||||
| struct bridge_iflist *bifl; | struct bridge_iflist *bifl; | ||||
| BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
| if (bif->bif_flags & IFBIF_STP) | if (bif->bif_flags & IFBIF_STP) | ||||
| bstp_disable(&bif->bif_stp); | bstp_disable(&bif->bif_stp); | ||||
| ifs->if_bridge = NULL; | ifs->if_bridge = NULL; | ||||
| BRIDGE_XLOCK(sc); | |||||
| CK_LIST_REMOVE(bif, bif_next); | CK_LIST_REMOVE(bif, bif_next); | ||||
| BRIDGE_XDROP(sc); | |||||
| /* | /* | ||||
| * If removing the interface that gave the bridge its mac address, set | * If removing the interface that gave the bridge its mac address, set | ||||
| * the mac address of the bridge to the address of the next member, or | * the mac address of the bridge to the address of the next member, or | ||||
| * to its default address if no members are left. | * to its default address if no members are left. | ||||
| */ | */ | ||||
| if (V_bridge_inherit_mac && sc->sc_ifaddr == ifs) { | if (V_bridge_inherit_mac && sc->sc_ifaddr == ifs) { | ||||
| if (CK_LIST_EMPTY(&sc->sc_iflist)) { | if (CK_LIST_EMPTY(&sc->sc_iflist)) { | ||||
| ▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
| #endif | #endif | ||||
| break; | break; | ||||
| } | } | ||||
| /* reneable any interface capabilities */ | /* reneable any interface capabilities */ | ||||
| bridge_set_ifcap(sc, bif, bif->bif_savedcaps); | bridge_set_ifcap(sc, bif, bif->bif_savedcaps); | ||||
| } | } | ||||
| bstp_destroy(&bif->bif_stp); /* prepare to free */ | bstp_destroy(&bif->bif_stp); /* prepare to free */ | ||||
| BRIDGE_LOCK(sc); | BRIDGE_LOCK(sc); | ||||
| free(bif, M_DEVBUF); | |||||
| NET_EPOCH_CALL(bridge_delete_member_cb, &bif->bif_epoch_ctx); | |||||
| } | } | ||||
| /* | /* | ||||
| * bridge_delete_span: | * bridge_delete_span: | ||||
| * | * | ||||
| * Delete the specified span interface. | * Delete the specified span interface. | ||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_delete_span(struct bridge_softc *sc, struct bridge_iflist *bif) | bridge_delete_span(struct bridge_softc *sc, struct bridge_iflist *bif) | ||||
| { | { | ||||
| BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
| KASSERT(bif->bif_ifp->if_bridge == NULL, | KASSERT(bif->bif_ifp->if_bridge == NULL, | ||||
| ("%s: not a span interface", __func__)); | ("%s: not a span interface", __func__)); | ||||
| CK_LIST_REMOVE(bif, bif_next); | CK_LIST_REMOVE(bif, bif_next); | ||||
| free(bif, M_DEVBUF); | |||||
| NET_EPOCH_CALL(bridge_delete_member_cb, &bif->bif_epoch_ctx); | |||||
| } | } | ||||
| static int | static int | ||||
| bridge_ioctl_add(struct bridge_softc *sc, void *arg) | bridge_ioctl_add(struct bridge_softc *sc, void *arg) | ||||
| { | { | ||||
| struct ifbreq *req = arg; | struct ifbreq *req = arg; | ||||
| struct bridge_iflist *bif = NULL; | struct bridge_iflist *bif = NULL; | ||||
| struct ifnet *ifs; | struct ifnet *ifs; | ||||
| Show All 39 Lines | #ifdef INET6 | ||||
| /* Check if the parent interface has a link-local scope addr. */ | /* Check if the parent interface has a link-local scope addr. */ | ||||
| if (V_allow_llz_overlap == 0 && | if (V_allow_llz_overlap == 0 && | ||||
| in6ifa_llaonifp(sc->sc_ifp) != NULL) { | in6ifa_llaonifp(sc->sc_ifp) != NULL) { | ||||
| /* | /* | ||||
| * If any, remove all inet6 addresses from the member | * If any, remove all inet6 addresses from the member | ||||
| * interfaces. | * interfaces. | ||||
| */ | */ | ||||
| BRIDGE_XLOCK(sc); | |||||
| CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | ||||
| if (in6ifa_llaonifp(bif->bif_ifp)) { | if (in6ifa_llaonifp(bif->bif_ifp)) { | ||||
| BRIDGE_UNLOCK(sc); | BRIDGE_UNLOCK(sc); | ||||
| in6_ifdetach(bif->bif_ifp); | in6_ifdetach(bif->bif_ifp); | ||||
| BRIDGE_LOCK(sc); | BRIDGE_LOCK(sc); | ||||
| if_printf(sc->sc_ifp, | if_printf(sc->sc_ifp, | ||||
| "IPv6 addresses on %s have been removed " | "IPv6 addresses on %s have been removed " | ||||
| "before adding it as a member to prevent " | "before adding it as a member to prevent " | ||||
| "IPv6 address scope violation.\n", | "IPv6 address scope violation.\n", | ||||
| bif->bif_ifp->if_xname); | bif->bif_ifp->if_xname); | ||||
| } | } | ||||
| } | } | ||||
| BRIDGE_XDROP(sc); | |||||
| if (in6ifa_llaonifp(ifs)) { | if (in6ifa_llaonifp(ifs)) { | ||||
| BRIDGE_UNLOCK(sc); | BRIDGE_UNLOCK(sc); | ||||
| in6_ifdetach(ifs); | in6_ifdetach(ifs); | ||||
| BRIDGE_LOCK(sc); | BRIDGE_LOCK(sc); | ||||
| if_printf(sc->sc_ifp, | if_printf(sc->sc_ifp, | ||||
| "IPv6 addresses on %s have been removed " | "IPv6 addresses on %s have been removed " | ||||
| "before adding it as a member to prevent " | "before adding it as a member to prevent " | ||||
| "IPv6 address scope violation.\n", | "IPv6 address scope violation.\n", | ||||
| ▲ Show 20 Lines • Show All 297 Lines • ▼ Show 20 Lines | |||||
| static int | static int | ||||
| bridge_ioctl_saddr(struct bridge_softc *sc, void *arg) | bridge_ioctl_saddr(struct bridge_softc *sc, void *arg) | ||||
| { | { | ||||
| struct ifbareq *req = arg; | struct ifbareq *req = arg; | ||||
| struct bridge_iflist *bif; | struct bridge_iflist *bif; | ||||
| int error; | int error; | ||||
| NET_EPOCH_ASSERT(); | |||||
| bif = bridge_lookup_member(sc, req->ifba_ifsname); | bif = bridge_lookup_member(sc, req->ifba_ifsname); | ||||
| if (bif == NULL) | if (bif == NULL) | ||||
| return (ENOENT); | return (ENOENT); | ||||
| /* bridge_rtupdate() may acquire the lock. */ | |||||
| BRIDGE_UNLOCK(sc); | |||||
| error = bridge_rtupdate(sc, req->ifba_dst, req->ifba_vlan, bif, 1, | error = bridge_rtupdate(sc, req->ifba_dst, req->ifba_vlan, bif, 1, | ||||
| req->ifba_flags); | req->ifba_flags); | ||||
| BRIDGE_LOCK(sc); | |||||
| return (error); | return (error); | ||||
| } | } | ||||
| static int | static int | ||||
| bridge_ioctl_sto(struct bridge_softc *sc, void *arg) | bridge_ioctl_sto(struct bridge_softc *sc, void *arg) | ||||
| { | { | ||||
| struct ifbrparam *param = arg; | struct ifbrparam *param = arg; | ||||
| ▲ Show 20 Lines • Show All 322 Lines • ▼ Show 20 Lines | |||||
| * Detach an interface from a bridge. Called when a member | * Detach an interface from a bridge. Called when a member | ||||
| * interface is detaching. | * interface is detaching. | ||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_ifdetach(void *arg __unused, struct ifnet *ifp) | bridge_ifdetach(void *arg __unused, struct ifnet *ifp) | ||||
| { | { | ||||
| struct bridge_softc *sc = ifp->if_bridge; | struct bridge_softc *sc = ifp->if_bridge; | ||||
| struct bridge_iflist *bif; | struct bridge_iflist *bif; | ||||
| struct epoch_tracker et; | |||||
| if (ifp->if_flags & IFF_RENAMING) | if (ifp->if_flags & IFF_RENAMING) | ||||
| return; | return; | ||||
| if (V_bridge_cloner == NULL) { | if (V_bridge_cloner == NULL) { | ||||
| /* | /* | ||||
| * This detach handler can be called after | * This detach handler can be called after | ||||
| * vnet_bridge_uninit(). Just return in that case. | * vnet_bridge_uninit(). Just return in that case. | ||||
| */ | */ | ||||
| return; | return; | ||||
| } | } | ||||
| NET_EPOCH_ENTER(et); | |||||
| /* Check if the interface is a bridge member */ | /* Check if the interface is a bridge member */ | ||||
| if (sc != NULL) { | if (sc != NULL) { | ||||
| BRIDGE_LOCK(sc); | BRIDGE_LOCK(sc); | ||||
| bif = bridge_lookup_member_if(sc, ifp); | bif = bridge_lookup_member_if(sc, ifp); | ||||
| if (bif != NULL) | if (bif != NULL) | ||||
| bridge_delete_member(sc, bif, 1); | bridge_delete_member(sc, bif, 1); | ||||
| BRIDGE_UNLOCK(sc); | BRIDGE_UNLOCK(sc); | ||||
| NET_EPOCH_EXIT(et); | |||||
| return; | return; | ||||
| } | } | ||||
| /* Check if the interface is a span port */ | /* Check if the interface is a span port */ | ||||
| BRIDGE_LIST_LOCK(); | BRIDGE_LIST_LOCK(); | ||||
| LIST_FOREACH(sc, &V_bridge_list, sc_list) { | LIST_FOREACH(sc, &V_bridge_list, sc_list) { | ||||
| BRIDGE_LOCK(sc); | BRIDGE_LOCK(sc); | ||||
| CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) | CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) | ||||
| if (ifp == bif->bif_ifp) { | if (ifp == bif->bif_ifp) { | ||||
| bridge_delete_span(sc, bif); | bridge_delete_span(sc, bif); | ||||
| break; | break; | ||||
| } | } | ||||
| BRIDGE_UNLOCK(sc); | BRIDGE_UNLOCK(sc); | ||||
| } | } | ||||
| BRIDGE_LIST_UNLOCK(); | BRIDGE_LIST_UNLOCK(); | ||||
| NET_EPOCH_EXIT(et); | |||||
| } | } | ||||
| /* | /* | ||||
| * bridge_init: | * bridge_init: | ||||
| * | * | ||||
| * Initialize a bridge interface. | * Initialize a bridge interface. | ||||
| */ | */ | ||||
| static void | static void | ||||
| Show All 20 Lines | |||||
| * | * | ||||
| * Stop the bridge interface. | * Stop the bridge interface. | ||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_stop(struct ifnet *ifp, int disable) | bridge_stop(struct ifnet *ifp, int disable) | ||||
| { | { | ||||
| struct bridge_softc *sc = ifp->if_softc; | struct bridge_softc *sc = ifp->if_softc; | ||||
| NET_EPOCH_ASSERT(); | |||||
| BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
| if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) | if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) | ||||
| return; | return; | ||||
| callout_stop(&sc->sc_brcallout); | callout_stop(&sc->sc_brcallout); | ||||
| bstp_stop(&sc->sc_stp); | bstp_stop(&sc->sc_stp); | ||||
| ▲ Show 20 Lines • Show All 107 Lines • ▼ Show 20 Lines | |||||
| bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, | bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, | ||||
| struct rtentry *rt) | struct rtentry *rt) | ||||
| { | { | ||||
| struct ether_header *eh; | struct ether_header *eh; | ||||
| struct ifnet *bifp, *dst_if; | struct ifnet *bifp, *dst_if; | ||||
| struct bridge_softc *sc; | struct bridge_softc *sc; | ||||
| uint16_t vlan; | uint16_t vlan; | ||||
| NET_EPOCH_ASSERT(); | |||||
| if (m->m_len < ETHER_HDR_LEN) { | if (m->m_len < ETHER_HDR_LEN) { | ||||
| m = m_pullup(m, ETHER_HDR_LEN); | m = m_pullup(m, ETHER_HDR_LEN); | ||||
| if (m == NULL) | if (m == NULL) | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| eh = mtod(m, struct ether_header *); | eh = mtod(m, struct ether_header *); | ||||
| sc = ifp->if_bridge; | sc = ifp->if_bridge; | ||||
| vlan = VLANTAGOF(m); | vlan = VLANTAGOF(m); | ||||
| BRIDGE_LOCK(sc); | |||||
| bifp = sc->sc_ifp; | bifp = sc->sc_ifp; | ||||
| /* | /* | ||||
| * If bridge is down, but the original output interface is up, | * If bridge is down, but the original output interface is up, | ||||
| * go ahead and send out that interface. Otherwise, the packet | * go ahead and send out that interface. Otherwise, the packet | ||||
| * is dropped below. | * is dropped below. | ||||
| */ | */ | ||||
| if ((bifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { | if ((bifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { | ||||
| Show All 10 Lines | bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, | ||||
| else | else | ||||
| dst_if = bridge_rtlookup(sc, eh->ether_dhost, vlan); | dst_if = bridge_rtlookup(sc, eh->ether_dhost, vlan); | ||||
| /* Tap any traffic not passing back out the originating interface */ | /* Tap any traffic not passing back out the originating interface */ | ||||
| if (dst_if != ifp) | if (dst_if != ifp) | ||||
| ETHER_BPF_MTAP(bifp, m); | ETHER_BPF_MTAP(bifp, m); | ||||
| if (dst_if == NULL) { | if (dst_if == NULL) { | ||||
| struct bridge_iflist *bif; | struct bridge_iflist *bif; | ||||
| struct mbuf *mc; | struct mbuf *mc; | ||||
| int error = 0, used = 0; | int used = 0; | ||||
| bridge_span(sc, m); | bridge_span(sc, m); | ||||
| BRIDGE_LOCK2REF(sc, error); | |||||
| if (error) { | |||||
| m_freem(m); | |||||
| return (0); | |||||
| } | |||||
| CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | ||||
| dst_if = bif->bif_ifp; | dst_if = bif->bif_ifp; | ||||
| if (dst_if->if_type == IFT_GIF) | if (dst_if->if_type == IFT_GIF) | ||||
| continue; | continue; | ||||
| if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) | if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) | ||||
| continue; | continue; | ||||
| Show All 17 Lines | CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| } | } | ||||
| bridge_enqueue(sc, dst_if, mc); | bridge_enqueue(sc, dst_if, mc); | ||||
| } | } | ||||
| if (used == 0) | if (used == 0) | ||||
| m_freem(m); | m_freem(m); | ||||
| BRIDGE_UNREF(sc); | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| sendunicast: | sendunicast: | ||||
| /* | /* | ||||
| * XXX Spanning tree consideration here? | * XXX Spanning tree consideration here? | ||||
| */ | */ | ||||
| bridge_span(sc, m); | bridge_span(sc, m); | ||||
| if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) { | if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) { | ||||
| m_freem(m); | m_freem(m); | ||||
| BRIDGE_UNLOCK(sc); | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| BRIDGE_UNLOCK(sc); | |||||
| bridge_enqueue(sc, dst_if, m); | bridge_enqueue(sc, dst_if, m); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| /* | /* | ||||
| * bridge_transmit: | * bridge_transmit: | ||||
| * | * | ||||
| * Do output on a bridge. | * Do output on a bridge. | ||||
| * | * | ||||
| */ | */ | ||||
| static int | static int | ||||
| bridge_transmit(struct ifnet *ifp, struct mbuf *m) | bridge_transmit(struct ifnet *ifp, struct mbuf *m) | ||||
| { | { | ||||
| struct bridge_softc *sc; | struct bridge_softc *sc; | ||||
| struct ether_header *eh; | struct ether_header *eh; | ||||
| struct ifnet *dst_if; | struct ifnet *dst_if; | ||||
| int error = 0; | int error = 0; | ||||
| sc = ifp->if_softc; | sc = ifp->if_softc; | ||||
| ETHER_BPF_MTAP(ifp, m); | ETHER_BPF_MTAP(ifp, m); | ||||
| eh = mtod(m, struct ether_header *); | eh = mtod(m, struct ether_header *); | ||||
| BRIDGE_LOCK(sc); | |||||
| if (((m->m_flags & (M_BCAST|M_MCAST)) == 0) && | if (((m->m_flags & (M_BCAST|M_MCAST)) == 0) && | ||||
| (dst_if = bridge_rtlookup(sc, eh->ether_dhost, 1)) != NULL) { | (dst_if = bridge_rtlookup(sc, eh->ether_dhost, 1)) != NULL) { | ||||
| BRIDGE_UNLOCK(sc); | |||||
| error = bridge_enqueue(sc, dst_if, m); | error = bridge_enqueue(sc, dst_if, m); | ||||
| } else | } else | ||||
| bridge_broadcast(sc, ifp, m, 0); | bridge_broadcast(sc, ifp, m, 0); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| /* | /* | ||||
| Show All 17 Lines | |||||
| { | { | ||||
| struct bridge_iflist *dbif; | struct bridge_iflist *dbif; | ||||
| struct ifnet *src_if, *dst_if, *ifp; | struct ifnet *src_if, *dst_if, *ifp; | ||||
| struct ether_header *eh; | struct ether_header *eh; | ||||
| uint16_t vlan; | uint16_t vlan; | ||||
| uint8_t *dst; | uint8_t *dst; | ||||
| int error; | int error; | ||||
| NET_EPOCH_ASSERT(); | |||||
| src_if = m->m_pkthdr.rcvif; | src_if = m->m_pkthdr.rcvif; | ||||
| ifp = sc->sc_ifp; | ifp = sc->sc_ifp; | ||||
| if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); | if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); | ||||
| if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); | if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); | ||||
| vlan = VLANTAGOF(m); | vlan = VLANTAGOF(m); | ||||
| if ((sbif->bif_flags & IFBIF_STP) && | if ((sbif->bif_flags & IFBIF_STP) && | ||||
| ▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | if (dst_if != NULL || (m->m_flags & (M_BCAST | M_MCAST)) == 0) | ||||
| ETHER_BPF_MTAP(ifp, m); | ETHER_BPF_MTAP(ifp, m); | ||||
| /* run the packet filter */ | /* run the packet filter */ | ||||
| if (PFIL_HOOKED_IN(V_inet_pfil_head) | if (PFIL_HOOKED_IN(V_inet_pfil_head) | ||||
| #ifdef INET6 | #ifdef INET6 | ||||
| || PFIL_HOOKED_IN(V_inet6_pfil_head) | || PFIL_HOOKED_IN(V_inet6_pfil_head) | ||||
| #endif | #endif | ||||
| ) { | ) { | ||||
| BRIDGE_UNLOCK(sc); | |||||
| if (bridge_pfil(&m, ifp, src_if, PFIL_IN) != 0) | if (bridge_pfil(&m, ifp, src_if, PFIL_IN) != 0) | ||||
| return; | return; | ||||
| if (m == NULL) | if (m == NULL) | ||||
| return; | return; | ||||
| BRIDGE_LOCK(sc); | |||||
| } | } | ||||
| if (dst_if == NULL) { | if (dst_if == NULL) { | ||||
| bridge_broadcast(sc, src_if, m, 1); | bridge_broadcast(sc, src_if, m, 1); | ||||
| return; | return; | ||||
| } | } | ||||
| /* | /* | ||||
| Show All 11 Lines | #endif | ||||
| /* Private segments can not talk to each other */ | /* Private segments can not talk to each other */ | ||||
| if (sbif->bif_flags & dbif->bif_flags & IFBIF_PRIVATE) | if (sbif->bif_flags & dbif->bif_flags & IFBIF_PRIVATE) | ||||
| goto drop; | goto drop; | ||||
| if ((dbif->bif_flags & IFBIF_STP) && | if ((dbif->bif_flags & IFBIF_STP) && | ||||
| dbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) | dbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) | ||||
| goto drop; | goto drop; | ||||
| BRIDGE_UNLOCK(sc); | |||||
| if (PFIL_HOOKED_OUT(V_inet_pfil_head) | if (PFIL_HOOKED_OUT(V_inet_pfil_head) | ||||
| #ifdef INET6 | #ifdef INET6 | ||||
| || PFIL_HOOKED_OUT(V_inet6_pfil_head) | || PFIL_HOOKED_OUT(V_inet6_pfil_head) | ||||
| #endif | #endif | ||||
| ) { | ) { | ||||
| if (bridge_pfil(&m, ifp, dst_if, PFIL_OUT) != 0) | if (bridge_pfil(&m, ifp, dst_if, PFIL_OUT) != 0) | ||||
| return; | return; | ||||
| if (m == NULL) | if (m == NULL) | ||||
| return; | return; | ||||
| } | } | ||||
| bridge_enqueue(sc, dst_if, m); | bridge_enqueue(sc, dst_if, m); | ||||
| return; | return; | ||||
| drop: | drop: | ||||
| BRIDGE_UNLOCK(sc); | |||||
| m_freem(m); | m_freem(m); | ||||
| } | } | ||||
| /* | /* | ||||
| * bridge_input: | * bridge_input: | ||||
| * | * | ||||
| * Receive input from a member interface. Queue the packet for | * Receive input from a member interface. Queue the packet for | ||||
| * bridging if it is not for us. | * bridging if it is not for us. | ||||
| */ | */ | ||||
| static struct mbuf * | static struct mbuf * | ||||
| bridge_input(struct ifnet *ifp, struct mbuf *m) | bridge_input(struct ifnet *ifp, struct mbuf *m) | ||||
| { | { | ||||
| struct bridge_softc *sc = ifp->if_bridge; | struct bridge_softc *sc = ifp->if_bridge; | ||||
| struct bridge_iflist *bif, *bif2; | struct bridge_iflist *bif, *bif2; | ||||
| struct ifnet *bifp; | struct ifnet *bifp; | ||||
| struct ether_header *eh; | struct ether_header *eh; | ||||
| struct mbuf *mc, *mc2; | struct mbuf *mc, *mc2; | ||||
| uint16_t vlan; | uint16_t vlan; | ||||
| int error; | int error; | ||||
| NET_EPOCH_ASSERT(); | |||||
| if ((sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) | if ((sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) | ||||
| return (m); | return (m); | ||||
| bifp = sc->sc_ifp; | bifp = sc->sc_ifp; | ||||
| vlan = VLANTAGOF(m); | vlan = VLANTAGOF(m); | ||||
| /* | /* | ||||
| * Implement support for bridge monitoring. If this flag has been | * Implement support for bridge monitoring. If this flag has been | ||||
| * set on this interface, discard the packet once we push it through | * set on this interface, discard the packet once we push it through | ||||
| * the bpf(4) machinery, but before we do, increment the byte and | * the bpf(4) machinery, but before we do, increment the byte and | ||||
| * packet counters associated with this interface. | * packet counters associated with this interface. | ||||
| */ | */ | ||||
| if ((bifp->if_flags & IFF_MONITOR) != 0) { | if ((bifp->if_flags & IFF_MONITOR) != 0) { | ||||
| m->m_pkthdr.rcvif = bifp; | m->m_pkthdr.rcvif = bifp; | ||||
| ETHER_BPF_MTAP(bifp, m); | ETHER_BPF_MTAP(bifp, m); | ||||
| if_inc_counter(bifp, IFCOUNTER_IPACKETS, 1); | if_inc_counter(bifp, IFCOUNTER_IPACKETS, 1); | ||||
| if_inc_counter(bifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); | if_inc_counter(bifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); | ||||
| m_freem(m); | m_freem(m); | ||||
| return (NULL); | return (NULL); | ||||
| } | } | ||||
| BRIDGE_LOCK(sc); | |||||
| bif = bridge_lookup_member_if(sc, ifp); | bif = bridge_lookup_member_if(sc, ifp); | ||||
| if (bif == NULL) { | if (bif == NULL) { | ||||
| BRIDGE_UNLOCK(sc); | |||||
| return (m); | return (m); | ||||
| } | } | ||||
| eh = mtod(m, struct ether_header *); | eh = mtod(m, struct ether_header *); | ||||
| bridge_span(sc, m); | bridge_span(sc, m); | ||||
| if (m->m_flags & (M_BCAST|M_MCAST)) { | if (m->m_flags & (M_BCAST|M_MCAST)) { | ||||
| /* Tap off 802.1D packets; they do not get forwarded. */ | /* Tap off 802.1D packets; they do not get forwarded. */ | ||||
| if (memcmp(eh->ether_dhost, bstp_etheraddr, | if (memcmp(eh->ether_dhost, bstp_etheraddr, | ||||
| ETHER_ADDR_LEN) == 0) { | ETHER_ADDR_LEN) == 0) { | ||||
| bstp_input(&bif->bif_stp, ifp, m); /* consumes mbuf */ | bstp_input(&bif->bif_stp, ifp, m); /* consumes mbuf */ | ||||
| BRIDGE_UNLOCK(sc); | |||||
| return (NULL); | return (NULL); | ||||
| } | } | ||||
| if ((bif->bif_flags & IFBIF_STP) && | if ((bif->bif_flags & IFBIF_STP) && | ||||
| bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) { | bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) { | ||||
| BRIDGE_UNLOCK(sc); | |||||
| return (m); | return (m); | ||||
| } | } | ||||
| /* | /* | ||||
| * Make a deep copy of the packet and enqueue the copy | * Make a deep copy of the packet and enqueue the copy | ||||
| * for bridge processing; return the original packet for | * for bridge processing; return the original packet for | ||||
| * local processing. | * local processing. | ||||
| */ | */ | ||||
| mc = m_dup(m, M_NOWAIT); | mc = m_dup(m, M_NOWAIT); | ||||
| if (mc == NULL) { | if (mc == NULL) { | ||||
| BRIDGE_UNLOCK(sc); | |||||
| return (m); | return (m); | ||||
| } | } | ||||
| /* Perform the bridge forwarding function with the copy. */ | /* Perform the bridge forwarding function with the copy. */ | ||||
| bridge_forward(sc, bif, mc); | bridge_forward(sc, bif, mc); | ||||
| /* | /* | ||||
| * Reinject the mbuf as arriving on the bridge so we have a | * Reinject the mbuf as arriving on the bridge so we have a | ||||
| Show All 15 Lines | if (m->m_flags & (M_BCAST|M_MCAST)) { | ||||
| } | } | ||||
| /* Return the original packet for local processing. */ | /* Return the original packet for local processing. */ | ||||
| return (m); | return (m); | ||||
| } | } | ||||
| if ((bif->bif_flags & IFBIF_STP) && | if ((bif->bif_flags & IFBIF_STP) && | ||||
| bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) { | bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) { | ||||
| BRIDGE_UNLOCK(sc); | |||||
| return (m); | return (m); | ||||
| } | } | ||||
| #if (defined(INET) || defined(INET6)) | #if (defined(INET) || defined(INET6)) | ||||
| # define OR_CARP_CHECK_WE_ARE_DST(iface) \ | # define OR_CARP_CHECK_WE_ARE_DST(iface) \ | ||||
| || ((iface)->if_carp \ | || ((iface)->if_carp \ | ||||
| && (*carp_forus_p)((iface), eh->ether_dhost)) | && (*carp_forus_p)((iface), eh->ether_dhost)) | ||||
| # define OR_CARP_CHECK_WE_ARE_SRC(iface) \ | # define OR_CARP_CHECK_WE_ARE_SRC(iface) \ | ||||
| Show All 17 Lines | #define GRAB_OUR_PACKETS(iface) \ | ||||
| /* It is destined for us. */ \ | /* It is destined for us. */ \ | ||||
| if (memcmp(IF_LLADDR((iface)), eh->ether_dhost, ETHER_ADDR_LEN) == 0 \ | if (memcmp(IF_LLADDR((iface)), eh->ether_dhost, ETHER_ADDR_LEN) == 0 \ | ||||
| OR_CARP_CHECK_WE_ARE_DST((iface)) \ | OR_CARP_CHECK_WE_ARE_DST((iface)) \ | ||||
| ) { \ | ) { \ | ||||
| if (bif->bif_flags & IFBIF_LEARNING) { \ | if (bif->bif_flags & IFBIF_LEARNING) { \ | ||||
| error = bridge_rtupdate(sc, eh->ether_shost, \ | error = bridge_rtupdate(sc, eh->ether_shost, \ | ||||
| vlan, bif, 0, IFBAF_DYNAMIC); \ | vlan, bif, 0, IFBAF_DYNAMIC); \ | ||||
| if (error && bif->bif_addrmax) { \ | if (error && bif->bif_addrmax) { \ | ||||
| BRIDGE_UNLOCK(sc); \ | |||||
| m_freem(m); \ | m_freem(m); \ | ||||
| return (NULL); \ | return (NULL); \ | ||||
| } \ | } \ | ||||
| } \ | } \ | ||||
| m->m_pkthdr.rcvif = iface; \ | m->m_pkthdr.rcvif = iface; \ | ||||
| if ((iface) == ifp) { \ | if ((iface) == ifp) { \ | ||||
| /* Skip bridge processing... src == dest */ \ | /* Skip bridge processing... src == dest */ \ | ||||
| BRIDGE_UNLOCK(sc); \ | |||||
| return (m); \ | return (m); \ | ||||
| } \ | } \ | ||||
| /* It's passing over or to the bridge, locally. */ \ | /* It's passing over or to the bridge, locally. */ \ | ||||
| ETHER_BPF_MTAP(bifp, m); \ | ETHER_BPF_MTAP(bifp, m); \ | ||||
| if_inc_counter(bifp, IFCOUNTER_IPACKETS, 1); \ | if_inc_counter(bifp, IFCOUNTER_IPACKETS, 1); \ | ||||
| if_inc_counter(bifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); \ | if_inc_counter(bifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); \ | ||||
| /* Filter on the physical interface. */ \ | /* Filter on the physical interface. */ \ | ||||
| if (V_pfil_local_phys && (PFIL_HOOKED_IN(V_inet_pfil_head) \ | if (V_pfil_local_phys && (PFIL_HOOKED_IN(V_inet_pfil_head) \ | ||||
| OR_PFIL_HOOKED_INET6)) { \ | OR_PFIL_HOOKED_INET6)) { \ | ||||
| if (bridge_pfil(&m, NULL, ifp, \ | if (bridge_pfil(&m, NULL, ifp, \ | ||||
| PFIL_IN) != 0 || m == NULL) { \ | PFIL_IN) != 0 || m == NULL) { \ | ||||
| BRIDGE_UNLOCK(sc); \ | |||||
| return (NULL); \ | return (NULL); \ | ||||
| } \ | } \ | ||||
| } \ | } \ | ||||
| if ((iface) != bifp) \ | if ((iface) != bifp) \ | ||||
| ETHER_BPF_MTAP(iface, m); \ | ETHER_BPF_MTAP(iface, m); \ | ||||
| BRIDGE_UNLOCK(sc); \ | |||||
| return (m); \ | return (m); \ | ||||
| } \ | } \ | ||||
| \ | \ | ||||
| /* We just received a packet that we sent out. */ \ | /* We just received a packet that we sent out. */ \ | ||||
| if (memcmp(IF_LLADDR((iface)), eh->ether_shost, ETHER_ADDR_LEN) == 0 \ | if (memcmp(IF_LLADDR((iface)), eh->ether_shost, ETHER_ADDR_LEN) == 0 \ | ||||
| OR_CARP_CHECK_WE_ARE_SRC((iface)) \ | OR_CARP_CHECK_WE_ARE_SRC((iface)) \ | ||||
| ) { \ | ) { \ | ||||
| BRIDGE_UNLOCK(sc); \ | |||||
| m_freem(m); \ | m_freem(m); \ | ||||
| return (NULL); \ | return (NULL); \ | ||||
| } | } | ||||
| /* | /* | ||||
| * Unicast. Make sure it's not for the bridge. | * Unicast. Make sure it's not for the bridge. | ||||
| */ | */ | ||||
| do { GRAB_OUR_PACKETS(bifp) } while (0); | do { GRAB_OUR_PACKETS(bifp) } while (0); | ||||
| Show All 34 Lines | |||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if, | bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if, | ||||
| struct mbuf *m, int runfilt) | struct mbuf *m, int runfilt) | ||||
| { | { | ||||
| struct bridge_iflist *dbif, *sbif; | struct bridge_iflist *dbif, *sbif; | ||||
| struct mbuf *mc; | struct mbuf *mc; | ||||
| struct ifnet *dst_if; | struct ifnet *dst_if; | ||||
| int error = 0, used = 0, i; | int used = 0, i; | ||||
| NET_EPOCH_ASSERT(); | |||||
| sbif = bridge_lookup_member_if(sc, src_if); | sbif = bridge_lookup_member_if(sc, src_if); | ||||
| BRIDGE_LOCK2REF(sc, error); | |||||
| if (error) { | |||||
| m_freem(m); | |||||
| return; | |||||
| } | |||||
| /* Filter on the bridge interface before broadcasting */ | /* Filter on the bridge interface before broadcasting */ | ||||
| if (runfilt && (PFIL_HOOKED_OUT(V_inet_pfil_head) | if (runfilt && (PFIL_HOOKED_OUT(V_inet_pfil_head) | ||||
| #ifdef INET6 | #ifdef INET6 | ||||
| || PFIL_HOOKED_OUT(V_inet6_pfil_head) | || PFIL_HOOKED_OUT(V_inet6_pfil_head) | ||||
| #endif | #endif | ||||
| )) { | )) { | ||||
| if (bridge_pfil(&m, sc->sc_ifp, NULL, PFIL_OUT) != 0) | if (bridge_pfil(&m, sc->sc_ifp, NULL, PFIL_OUT) != 0) | ||||
| goto out; | return; | ||||
| if (m == NULL) | if (m == NULL) | ||||
| goto out; | return; | ||||
| } | } | ||||
| CK_LIST_FOREACH(dbif, &sc->sc_iflist, bif_next) { | CK_LIST_FOREACH(dbif, &sc->sc_iflist, bif_next) { | ||||
| dst_if = dbif->bif_ifp; | dst_if = dbif->bif_ifp; | ||||
| if (dst_if == src_if) | if (dst_if == src_if) | ||||
| continue; | continue; | ||||
| /* Private segments can not talk to each other */ | /* Private segments can not talk to each other */ | ||||
| ▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | #endif | ||||
| if (mc == NULL) | if (mc == NULL) | ||||
| continue; | continue; | ||||
| } | } | ||||
| bridge_enqueue(sc, dst_if, mc); | bridge_enqueue(sc, dst_if, mc); | ||||
| } | } | ||||
| if (used == 0) | if (used == 0) | ||||
| m_freem(m); | m_freem(m); | ||||
| out: | |||||
| BRIDGE_UNREF(sc); | |||||
| } | } | ||||
| /* | /* | ||||
| * bridge_span: | * bridge_span: | ||||
| * | * | ||||
| * Duplicate a packet out one or more interfaces that are in span mode, | * Duplicate a packet out one or more interfaces that are in span mode, | ||||
| * the original mbuf is unmodified. | * the original mbuf is unmodified. | ||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_span(struct bridge_softc *sc, struct mbuf *m) | bridge_span(struct bridge_softc *sc, struct mbuf *m) | ||||
| { | { | ||||
| struct bridge_iflist *bif; | struct bridge_iflist *bif; | ||||
| struct ifnet *dst_if; | struct ifnet *dst_if; | ||||
| struct mbuf *mc; | struct mbuf *mc; | ||||
| NET_EPOCH_ASSERT(); | |||||
| if (CK_LIST_EMPTY(&sc->sc_spanlist)) | if (CK_LIST_EMPTY(&sc->sc_spanlist)) | ||||
| return; | return; | ||||
| CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) { | CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) { | ||||
| dst_if = bif->bif_ifp; | dst_if = bif->bif_ifp; | ||||
| if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) | if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) | ||||
| continue; | continue; | ||||
| Show All 15 Lines | |||||
| */ | */ | ||||
| static int | static int | ||||
| bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, uint16_t vlan, | bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, uint16_t vlan, | ||||
| struct bridge_iflist *bif, int setflags, uint8_t flags) | struct bridge_iflist *bif, int setflags, uint8_t flags) | ||||
| { | { | ||||
| struct bridge_rtnode *brt; | struct bridge_rtnode *brt; | ||||
| int error; | int error; | ||||
| BRIDGE_LOCK_ASSERT(sc); | NET_EPOCH_ASSERT(); | ||||
| BRIDGE_UNLOCK_ASSERT(sc); | |||||
| /* Check the source address is valid and not multicast. */ | /* Check the source address is valid and not multicast. */ | ||||
| if (ETHER_IS_MULTICAST(dst) || | if (ETHER_IS_MULTICAST(dst) || | ||||
| (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 && | (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 && | ||||
| dst[3] == 0 && dst[4] == 0 && dst[5] == 0) != 0) | dst[3] == 0 && dst[4] == 0 && dst[5] == 0) != 0) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| /* 802.1p frames map to vlan 1 */ | /* 802.1p frames map to vlan 1 */ | ||||
| if (vlan == 0) | if (vlan == 0) | ||||
| vlan = 1; | vlan = 1; | ||||
| /* | /* | ||||
| * A route for this destination might already exist. If so, | * A route for this destination might already exist. If so, | ||||
| * update it, otherwise create a new one. | * update it, otherwise create a new one. | ||||
| */ | */ | ||||
| if ((brt = bridge_rtnode_lookup(sc, dst, vlan)) == NULL) { | if ((brt = bridge_rtnode_lookup(sc, dst, vlan)) == NULL) { | ||||
| BRIDGE_LOCK(sc); | |||||
| /* Check again, now that we have the lock. There could have | |||||
| * been a race and we only want to insert this once. */ | |||||
| if ((brt = bridge_rtnode_lookup(sc, dst, vlan)) != NULL) { | |||||
| BRIDGE_UNLOCK(sc); | |||||
| return (0); | |||||
| } | |||||
| if (sc->sc_brtcnt >= sc->sc_brtmax) { | if (sc->sc_brtcnt >= sc->sc_brtmax) { | ||||
| sc->sc_brtexceeded++; | sc->sc_brtexceeded++; | ||||
| BRIDGE_UNLOCK(sc); | |||||
| return (ENOSPC); | return (ENOSPC); | ||||
| } | } | ||||
| /* Check per interface address limits (if enabled) */ | /* Check per interface address limits (if enabled) */ | ||||
| if (bif->bif_addrmax && bif->bif_addrcnt >= bif->bif_addrmax) { | if (bif->bif_addrmax && bif->bif_addrcnt >= bif->bif_addrmax) { | ||||
| bif->bif_addrexceeded++; | bif->bif_addrexceeded++; | ||||
| BRIDGE_UNLOCK(sc); | |||||
| return (ENOSPC); | return (ENOSPC); | ||||
| } | } | ||||
| /* | /* | ||||
| * Allocate a new bridge forwarding node, and | * Allocate a new bridge forwarding node, and | ||||
| * initialize the expiration time and Ethernet | * initialize the expiration time and Ethernet | ||||
| * address. | * address. | ||||
| */ | */ | ||||
| brt = uma_zalloc(V_bridge_rtnode_zone, M_NOWAIT | M_ZERO); | brt = uma_zalloc(V_bridge_rtnode_zone, M_NOWAIT | M_ZERO); | ||||
| if (brt == NULL) | if (brt == NULL) { | ||||
| BRIDGE_UNLOCK(sc); | |||||
| return (ENOMEM); | return (ENOMEM); | ||||
| } | |||||
| brt->brt_vnet = curvnet; | |||||
| if (bif->bif_flags & IFBIF_STICKY) | if (bif->bif_flags & IFBIF_STICKY) | ||||
| brt->brt_flags = IFBAF_STICKY; | brt->brt_flags = IFBAF_STICKY; | ||||
| else | else | ||||
| brt->brt_flags = IFBAF_DYNAMIC; | brt->brt_flags = IFBAF_DYNAMIC; | ||||
| memcpy(brt->brt_addr, dst, ETHER_ADDR_LEN); | memcpy(brt->brt_addr, dst, ETHER_ADDR_LEN); | ||||
| brt->brt_vlan = vlan; | brt->brt_vlan = vlan; | ||||
| if ((error = bridge_rtnode_insert(sc, brt)) != 0) { | if ((error = bridge_rtnode_insert(sc, brt)) != 0) { | ||||
| uma_zfree(V_bridge_rtnode_zone, brt); | uma_zfree(V_bridge_rtnode_zone, brt); | ||||
| BRIDGE_UNLOCK(sc); | |||||
| return (error); | return (error); | ||||
| } | } | ||||
| brt->brt_dst = bif; | brt->brt_dst = bif; | ||||
| bif->bif_addrcnt++; | bif->bif_addrcnt++; | ||||
| BRIDGE_UNLOCK(sc); | |||||
| } | } | ||||
| if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC && | if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC && | ||||
| brt->brt_dst != bif) { | brt->brt_dst != bif) { | ||||
| BRIDGE_LOCK(sc); | |||||
| brt->brt_dst->bif_addrcnt--; | brt->brt_dst->bif_addrcnt--; | ||||
| brt->brt_dst = bif; | brt->brt_dst = bif; | ||||
| brt->brt_dst->bif_addrcnt++; | brt->brt_dst->bif_addrcnt++; | ||||
| BRIDGE_UNLOCK(sc); | |||||
| } | } | ||||
| if ((flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) | if ((flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) | ||||
| brt->brt_expire = time_uptime + sc->sc_brttimeout; | brt->brt_expire = time_uptime + sc->sc_brttimeout; | ||||
| if (setflags) | if (setflags) | ||||
| brt->brt_flags = flags; | brt->brt_flags = flags; | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| /* | /* | ||||
| * bridge_rtlookup: | * bridge_rtlookup: | ||||
| * | * | ||||
| * Lookup the destination interface for an address. | * Lookup the destination interface for an address. | ||||
| */ | */ | ||||
| static struct ifnet * | static struct ifnet * | ||||
| bridge_rtlookup(struct bridge_softc *sc, const uint8_t *addr, uint16_t vlan) | bridge_rtlookup(struct bridge_softc *sc, const uint8_t *addr, uint16_t vlan) | ||||
| { | { | ||||
| struct bridge_rtnode *brt; | struct bridge_rtnode *brt; | ||||
| BRIDGE_LOCK_ASSERT(sc); | NET_EPOCH_ASSERT(); | ||||
| if ((brt = bridge_rtnode_lookup(sc, addr, vlan)) == NULL) | if ((brt = bridge_rtnode_lookup(sc, addr, vlan)) == NULL) | ||||
| return (NULL); | return (NULL); | ||||
| return (brt->brt_ifp); | return (brt->brt_ifp); | ||||
| } | } | ||||
| /* | /* | ||||
| * bridge_rttrim: | * bridge_rttrim: | ||||
| * | * | ||||
| * Trim the routine table so that we have a number | * Trim the routine table so that we have a number | ||||
| * of routing entries less than or equal to the | * of routing entries less than or equal to the | ||||
| * maximum number. | * maximum number. | ||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_rttrim(struct bridge_softc *sc) | bridge_rttrim(struct bridge_softc *sc) | ||||
| { | { | ||||
| struct bridge_rtnode *brt, *nbrt; | struct bridge_rtnode *brt, *nbrt; | ||||
| NET_EPOCH_ASSERT(); | |||||
| BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
| /* Make sure we actually need to do this. */ | /* Make sure we actually need to do this. */ | ||||
| if (sc->sc_brtcnt <= sc->sc_brtmax) | if (sc->sc_brtcnt <= sc->sc_brtmax) | ||||
| return; | return; | ||||
| /* Force an aging cycle; this might trim enough addresses. */ | /* Force an aging cycle; this might trim enough addresses. */ | ||||
| bridge_rtage(sc); | bridge_rtage(sc); | ||||
| Show All 13 Lines | |||||
| * bridge_timer: | * bridge_timer: | ||||
| * | * | ||||
| * Aging timer for the bridge. | * Aging timer for the bridge. | ||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_timer(void *arg) | bridge_timer(void *arg) | ||||
| { | { | ||||
| struct bridge_softc *sc = arg; | struct bridge_softc *sc = arg; | ||||
| struct epoch_tracker et; | |||||
| NET_EPOCH_ENTER(et); | |||||
| BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
| /* Destruction of rtnodes requires a proper vnet context */ | /* Destruction of rtnodes requires a proper vnet context */ | ||||
| CURVNET_SET(sc->sc_ifp->if_vnet); | CURVNET_SET(sc->sc_ifp->if_vnet); | ||||
| bridge_rtage(sc); | bridge_rtage(sc); | ||||
| if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) | if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) | ||||
| callout_reset(&sc->sc_brcallout, | callout_reset(&sc->sc_brcallout, | ||||
| bridge_rtable_prune_period * hz, bridge_timer, sc); | bridge_rtable_prune_period * hz, bridge_timer, sc); | ||||
| CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
| NET_EPOCH_EXIT(et); | |||||
| } | } | ||||
| /* | /* | ||||
| * bridge_rtage: | * bridge_rtage: | ||||
| * | * | ||||
| * Perform an aging cycle. | * Perform an aging cycle. | ||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_rtage(struct bridge_softc *sc) | bridge_rtage(struct bridge_softc *sc) | ||||
| { | { | ||||
| struct bridge_rtnode *brt, *nbrt; | struct bridge_rtnode *brt, *nbrt; | ||||
| NET_EPOCH_ASSERT(); | |||||
| BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
| CK_LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { | CK_LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { | ||||
| if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { | if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { | ||||
| if (time_uptime >= brt->brt_expire) | if (time_uptime >= brt->brt_expire) | ||||
| bridge_rtnode_destroy(sc, brt); | bridge_rtnode_destroy(sc, brt); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* | /* | ||||
| * bridge_rtflush: | * bridge_rtflush: | ||||
| * | * | ||||
| * Remove all dynamic addresses from the bridge. | * Remove all dynamic addresses from the bridge. | ||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_rtflush(struct bridge_softc *sc, int full) | bridge_rtflush(struct bridge_softc *sc, int full) | ||||
| { | { | ||||
| struct bridge_rtnode *brt, *nbrt; | struct bridge_rtnode *brt, *nbrt; | ||||
| NET_EPOCH_ASSERT(); | |||||
| BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
| CK_LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { | CK_LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { | ||||
| if (full || (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) | if (full || (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) | ||||
| bridge_rtnode_destroy(sc, brt); | bridge_rtnode_destroy(sc, brt); | ||||
| } | } | ||||
| } | } | ||||
| /* | /* | ||||
| * bridge_rtdaddr: | * bridge_rtdaddr: | ||||
| * | * | ||||
| * Remove an address from the table. | * Remove an address from the table. | ||||
| */ | */ | ||||
| static int | static int | ||||
| bridge_rtdaddr(struct bridge_softc *sc, const uint8_t *addr, uint16_t vlan) | bridge_rtdaddr(struct bridge_softc *sc, const uint8_t *addr, uint16_t vlan) | ||||
| { | { | ||||
| struct bridge_rtnode *brt; | struct bridge_rtnode *brt; | ||||
| int found = 0; | int found = 0; | ||||
| NET_EPOCH_ASSERT(); | |||||
| BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
| /* | /* | ||||
| * If vlan is zero then we want to delete for all vlans so the lookup | * If vlan is zero then we want to delete for all vlans so the lookup | ||||
| * may return more than one. | * may return more than one. | ||||
| */ | */ | ||||
| while ((brt = bridge_rtnode_lookup(sc, addr, vlan)) != NULL) { | while ((brt = bridge_rtnode_lookup(sc, addr, vlan)) != NULL) { | ||||
| bridge_rtnode_destroy(sc, brt); | bridge_rtnode_destroy(sc, brt); | ||||
| found = 1; | found = 1; | ||||
| } | } | ||||
| return (found ? 0 : ENOENT); | return (found ? 0 : ENOENT); | ||||
| } | } | ||||
| /* | /* | ||||
| * bridge_rtdelete: | * bridge_rtdelete: | ||||
| * | * | ||||
| * Delete routes to a speicifc member interface. | * Delete routes to a speicifc member interface. | ||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp, int full) | bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp, int full) | ||||
| { | { | ||||
| struct bridge_rtnode *brt, *nbrt; | struct bridge_rtnode *brt, *nbrt; | ||||
| NET_EPOCH_ASSERT(); | |||||
| BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
| CK_LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { | CK_LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { | ||||
| if (brt->brt_ifp == ifp && (full || | if (brt->brt_ifp == ifp && (full || | ||||
| (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)) | (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)) | ||||
| bridge_rtnode_destroy(sc, brt); | bridge_rtnode_destroy(sc, brt); | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | |||||
| */ | */ | ||||
| static struct bridge_rtnode * | static struct bridge_rtnode * | ||||
| bridge_rtnode_lookup(struct bridge_softc *sc, const uint8_t *addr, uint16_t vlan) | bridge_rtnode_lookup(struct bridge_softc *sc, const uint8_t *addr, uint16_t vlan) | ||||
| { | { | ||||
| struct bridge_rtnode *brt; | struct bridge_rtnode *brt; | ||||
| uint32_t hash; | uint32_t hash; | ||||
| int dir; | int dir; | ||||
| BRIDGE_LOCK_ASSERT(sc); | NET_EPOCH_ASSERT(); | ||||
| hash = bridge_rthash(sc, addr); | hash = bridge_rthash(sc, addr); | ||||
| CK_LIST_FOREACH(brt, &sc->sc_rthash[hash], brt_hash) { | CK_LIST_FOREACH(brt, &sc->sc_rthash[hash], brt_hash) { | ||||
| dir = bridge_rtnode_addr_cmp(addr, brt->brt_addr); | dir = bridge_rtnode_addr_cmp(addr, brt->brt_addr); | ||||
| if (dir == 0 && (brt->brt_vlan == vlan || vlan == 0)) | if (dir == 0 && (brt->brt_vlan == vlan || vlan == 0)) | ||||
| return (brt); | return (brt); | ||||
| if (dir > 0) | if (dir > 0) | ||||
| return (NULL); | return (NULL); | ||||
| ▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | |||||
| out: | out: | ||||
| CK_LIST_INSERT_HEAD(&sc->sc_rtlist, brt, brt_list); | CK_LIST_INSERT_HEAD(&sc->sc_rtlist, brt, brt_list); | ||||
| sc->sc_brtcnt++; | sc->sc_brtcnt++; | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static void | |||||
| bridge_rtnode_destroy_cb(struct epoch_context *ctx) | |||||
| { | |||||
| struct bridge_rtnode *brt; | |||||
| brt = __containerof(ctx, struct bridge_rtnode, brt_epoch_ctx); | |||||
| CURVNET_SET(brt->brt_vnet); | |||||
| uma_zfree(V_bridge_rtnode_zone, brt); | |||||
| CURVNET_RESTORE(); | |||||
| } | |||||
| /* | /* | ||||
| * bridge_rtnode_destroy: | * bridge_rtnode_destroy: | ||||
| * | * | ||||
| * Destroy a bridge rtnode. | * Destroy a bridge rtnode. | ||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_rtnode_destroy(struct bridge_softc *sc, struct bridge_rtnode *brt) | bridge_rtnode_destroy(struct bridge_softc *sc, struct bridge_rtnode *brt) | ||||
| { | { | ||||
| NET_EPOCH_ASSERT(); | |||||
| BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
| CK_LIST_REMOVE(brt, brt_hash); | CK_LIST_REMOVE(brt, brt_hash); | ||||
| CK_LIST_REMOVE(brt, brt_list); | CK_LIST_REMOVE(brt, brt_list); | ||||
| sc->sc_brtcnt--; | sc->sc_brtcnt--; | ||||
| brt->brt_dst->bif_addrcnt--; | brt->brt_dst->bif_addrcnt--; | ||||
| uma_zfree(V_bridge_rtnode_zone, brt); | |||||
| NET_EPOCH_CALL(bridge_rtnode_destroy_cb, &brt->brt_epoch_ctx); | |||||
| } | } | ||||
| /* | /* | ||||
| * bridge_rtable_expire: | * bridge_rtable_expire: | ||||
| * | * | ||||
| * Set the expiry time for all routes on an interface. | * Set the expiry time for all routes on an interface. | ||||
| */ | */ | ||||
| static void | static void | ||||
| bridge_rtable_expire(struct ifnet *ifp, int age) | bridge_rtable_expire(struct ifnet *ifp, int age) | ||||
| { | { | ||||
| struct bridge_softc *sc = ifp->if_bridge; | struct bridge_softc *sc = ifp->if_bridge; | ||||
| struct bridge_rtnode *brt; | struct bridge_rtnode *brt; | ||||
| struct epoch_tracker et; | |||||
| NET_EPOCH_ENTER(et); | |||||
| CURVNET_SET(ifp->if_vnet); | CURVNET_SET(ifp->if_vnet); | ||||
| BRIDGE_LOCK(sc); | BRIDGE_LOCK(sc); | ||||
| /* | /* | ||||
| * If the age is zero then flush, otherwise set all the expiry times to | * If the age is zero then flush, otherwise set all the expiry times to | ||||
| * age for the interface | * age for the interface | ||||
| */ | */ | ||||
| if (age == 0) | if (age == 0) | ||||
| bridge_rtdelete(sc, ifp, IFBF_FLUSHDYN); | bridge_rtdelete(sc, ifp, IFBF_FLUSHDYN); | ||||
| else { | else { | ||||
| CK_LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { | CK_LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { | ||||
| /* Cap the expiry time to 'age' */ | /* Cap the expiry time to 'age' */ | ||||
| if (brt->brt_ifp == ifp && | if (brt->brt_ifp == ifp && | ||||
| brt->brt_expire > time_uptime + age && | brt->brt_expire > time_uptime + age && | ||||
| (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) | (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) | ||||
| brt->brt_expire = time_uptime + age; | brt->brt_expire = time_uptime + age; | ||||
| } | } | ||||
| } | } | ||||
| BRIDGE_UNLOCK(sc); | BRIDGE_UNLOCK(sc); | ||||
| CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
| NET_EPOCH_EXIT(et); | |||||
| } | } | ||||
| /* | /* | ||||
| * bridge_state_change: | * bridge_state_change: | ||||
| * | * | ||||
| * Callback from the bridgestp code when a port changes states. | * Callback from the bridgestp code when a port changes states. | ||||
| */ | */ | ||||
| static void | static void | ||||
| ▲ Show 20 Lines • Show All 489 Lines • ▼ Show 20 Lines | dropit: | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| static void | static void | ||||
| bridge_linkstate(struct ifnet *ifp) | bridge_linkstate(struct ifnet *ifp) | ||||
| { | { | ||||
| struct bridge_softc *sc = ifp->if_bridge; | struct bridge_softc *sc = ifp->if_bridge; | ||||
| struct bridge_iflist *bif; | struct bridge_iflist *bif; | ||||
| struct epoch_tracker et; | |||||
| BRIDGE_LOCK(sc); | NET_EPOCH_ENTER(et); | ||||
| bif = bridge_lookup_member_if(sc, ifp); | bif = bridge_lookup_member_if(sc, ifp); | ||||
| if (bif == NULL) { | if (bif == NULL) { | ||||
| BRIDGE_UNLOCK(sc); | NET_EPOCH_EXIT(et); | ||||
| return; | return; | ||||
| } | } | ||||
| bridge_linkcheck(sc); | bridge_linkcheck(sc); | ||||
| BRIDGE_UNLOCK(sc); | |||||
| bstp_linkstate(&bif->bif_stp); | bstp_linkstate(&bif->bif_stp); | ||||
| NET_EPOCH_EXIT(et); | |||||
| } | } | ||||
| static void | static void | ||||
| bridge_linkcheck(struct bridge_softc *sc) | bridge_linkcheck(struct bridge_softc *sc) | ||||
| { | { | ||||
| struct bridge_iflist *bif; | struct bridge_iflist *bif; | ||||
| int new_link, hasls; | int new_link, hasls; | ||||
| BRIDGE_LOCK_ASSERT(sc); | NET_EPOCH_ASSERT(); | ||||
| new_link = LINK_STATE_DOWN; | new_link = LINK_STATE_DOWN; | ||||
| hasls = 0; | hasls = 0; | ||||
| /* Our link is considered up if at least one of our ports is active */ | /* Our link is considered up if at least one of our ports is active */ | ||||
| CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | ||||
| if (bif->bif_ifp->if_capabilities & IFCAP_LINKSTATE) | if (bif->bif_ifp->if_capabilities & IFCAP_LINKSTATE) | ||||
| hasls++; | hasls++; | ||||
| if (bif->bif_ifp->if_link_state == LINK_STATE_UP) { | if (bif->bif_ifp->if_link_state == LINK_STATE_UP) { | ||||
| new_link = LINK_STATE_UP; | new_link = LINK_STATE_UP; | ||||
| Show All 9 Lines | |||||
Are BRIDGE_LOCK2REF/ BRIDGE_UNREF still needed?