Changeset View
Standalone View
sys/net/if_bridge.c
Show First 20 Lines • Show All 193 Lines • ▼ Show 20 Lines | |||||
#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); \ | 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 { \ | #define BRIDGE_LOCK2REF(_sc, _err) do { \ | ||||
melifaro: Are `BRIDGE_LOCK2REF`/ `BRIDGE_UNREF` still needed? | |||||
mtx_assert(&(_sc)->sc_mtx, MA_OWNED); \ | mtx_assert(&(_sc)->sc_mtx, MA_OWNED); \ | ||||
if ((_sc)->sc_iflist_xcnt > 0) \ | if ((_sc)->sc_iflist_xcnt > 0) \ | ||||
(_err) = EBUSY; \ | (_err) = EBUSY; \ | ||||
else \ | else \ | ||||
(_sc)->sc_iflist_ref++; \ | (_sc)->sc_iflist_ref++; \ | ||||
mtx_unlock(&(_sc)->sc_mtx); \ | mtx_unlock(&(_sc)->sc_mtx); \ | ||||
} while (0) | } while (0) | ||||
#define BRIDGE_UNREF(_sc) do { \ | #define BRIDGE_UNREF(_sc) do { \ | ||||
Show All 13 Lines | #define BRIDGE_XDROP(_sc) do { \ | ||||
mtx_assert(&(_sc)->sc_mtx, MA_OWNED); \ | mtx_assert(&(_sc)->sc_mtx, MA_OWNED); \ | ||||
(_sc)->sc_iflist_xcnt--; \ | (_sc)->sc_iflist_xcnt--; \ | ||||
} while (0) | } while (0) | ||||
/* | /* | ||||
* Bridge interface list entry. | * Bridge interface list entry. | ||||
*/ | */ | ||||
struct bridge_iflist { | struct bridge_iflist { | ||||
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 */ | ||||
Not Done Inline ActionsExtra blank lines (here and elsewhere)? emaste: Extra blank lines (here and elsewhere)? | |||||
struct epoch_context bif_epoch_ctx; | |||||
}; | }; | ||||
/* | /* | ||||
* Bridge route node. | * Bridge route node. | ||||
*/ | */ | ||||
struct bridge_rtnode { | struct bridge_rtnode { | ||||
LIST_ENTRY(bridge_rtnode) brt_hash; /* hash table linkage */ | CK_LIST_ENTRY(bridge_rtnode) brt_hash; /* hash table linkage */ | ||||
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; | 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_ref; /* refcount for sc_iflist */ | ||||
uint32_t sc_iflist_xcnt; /* refcount for sc_iflist */ | uint32_t sc_iflist_xcnt; /* refcount for sc_iflist */ | ||||
LIST_HEAD(, bridge_iflist) sc_iflist; /* member interface list */ | CK_LIST_HEAD(, bridge_iflist) sc_iflist; /* member interface list */ | ||||
LIST_HEAD(, bridge_rtnode) *sc_rthash; /* our forwarding table */ | CK_LIST_HEAD(, bridge_rtnode) *sc_rthash; /* our forwarding table */ | ||||
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 */ | ||||
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(); | ||||
/* Before we can destroy the uma zone, because there are callbacks that | |||||
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. */` | |||||
* use it. */ | |||||
NET_EPOCH_WAIT(); | |||||
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 85 Lines • ▼ Show 20 Lines | bridge_clone_create(struct if_clone *ifc, int unit, caddr_t params) | ||||
sc->sc_brtmax = BRIDGE_RTABLE_MAX; | sc->sc_brtmax = BRIDGE_RTABLE_MAX; | ||||
sc->sc_brttimeout = BRIDGE_RTABLE_TIMEOUT; | sc->sc_brttimeout = BRIDGE_RTABLE_TIMEOUT; | ||||
/* Initialize our routing table. */ | /* Initialize our routing table. */ | ||||
bridge_rtable_init(sc); | bridge_rtable_init(sc); | ||||
callout_init_mtx(&sc->sc_brcallout, &sc->sc_mtx, 0); | callout_init_mtx(&sc->sc_brcallout, &sc->sc_mtx, 0); | ||||
LIST_INIT(&sc->sc_iflist); | CK_LIST_INIT(&sc->sc_iflist); | ||||
LIST_INIT(&sc->sc_spanlist); | CK_LIST_INIT(&sc->sc_spanlist); | ||||
ifp->if_softc = sc; | ifp->if_softc = sc; | ||||
if_initname(ifp, bridge_name, unit); | if_initname(ifp, bridge_name, unit); | ||||
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | ||||
ifp->if_ioctl = bridge_ioctl; | ifp->if_ioctl = bridge_ioctl; | ||||
ifp->if_transmit = bridge_transmit; | ifp->if_transmit = bridge_transmit; | ||||
ifp->if_qflush = bridge_qflush; | ifp->if_qflush = bridge_qflush; | ||||
ifp->if_init = bridge_init; | ifp->if_init = bridge_init; | ||||
▲ Show 20 Lines • Show All 43 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 = 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 = 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 76 Lines • ▼ Show 20 Lines | case SIOCSIFFLAGS: | ||||
} | } | ||||
break; | break; | ||||
case SIOCSIFMTU: | case SIOCSIFMTU: | ||||
if (ifr->ifr_mtu < 576) { | if (ifr->ifr_mtu < 576) { | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
if (LIST_EMPTY(&sc->sc_iflist)) { | if (CK_LIST_EMPTY(&sc->sc_iflist)) { | ||||
sc->sc_ifp->if_mtu = ifr->ifr_mtu; | sc->sc_ifp->if_mtu = ifr->ifr_mtu; | ||||
break; | break; | ||||
} | } | ||||
BRIDGE_LOCK(sc); | BRIDGE_LOCK(sc); | ||||
LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | ||||
if (bif->bif_ifp->if_mtu != ifr->ifr_mtu) { | if (bif->bif_ifp->if_mtu != ifr->ifr_mtu) { | ||||
log(LOG_NOTICE, "%s: invalid MTU: %u(%s)" | log(LOG_NOTICE, "%s: invalid MTU: %u(%s)" | ||||
" != %d\n", sc->sc_ifp->if_xname, | " != %d\n", sc->sc_ifp->if_xname, | ||||
bif->bif_ifp->if_mtu, | bif->bif_ifp->if_mtu, | ||||
bif->bif_ifp->if_xname, ifr->ifr_mtu); | bif->bif_ifp->if_xname, ifr->ifr_mtu); | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (!error) | if (!error) | ||||
sc->sc_ifp->if_mtu = ifr->ifr_mtu; | sc->sc_ifp->if_mtu = ifr->ifr_mtu; | ||||
BRIDGE_UNLOCK(sc); | BRIDGE_UNLOCK(sc); | ||||
break; | break; | ||||
default: | 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; | ||||
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); | BRIDGE_XLOCK(sc); | ||||
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); | ||||
Show All 35 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(); | ||||
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(); | ||||
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; | |||||
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); | BRIDGE_XLOCK(sc); | ||||
LIST_REMOVE(bif, bif_next); | CK_LIST_REMOVE(bif, bif_next); | ||||
BRIDGE_XDROP(sc); | 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 (LIST_EMPTY(&sc->sc_iflist)) { | if (CK_LIST_EMPTY(&sc->sc_iflist)) { | ||||
bcopy(&sc->sc_defaddr, | bcopy(&sc->sc_defaddr, | ||||
IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); | IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); | ||||
sc->sc_ifaddr = NULL; | sc->sc_ifaddr = NULL; | ||||
} else { | } else { | ||||
fif = LIST_FIRST(&sc->sc_iflist)->bif_ifp; | bifl = CK_LIST_FIRST(&sc->sc_iflist); | ||||
fif = bifl->bif_ifp; | |||||
bcopy(IF_LLADDR(fif), | bcopy(IF_LLADDR(fif), | ||||
IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); | IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); | ||||
sc->sc_ifaddr = fif; | sc->sc_ifaddr = fif; | ||||
} | } | ||||
EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp); | EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp); | ||||
} | } | ||||
bridge_linkcheck(sc); | bridge_linkcheck(sc); | ||||
Show All 28 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__)); | ||||
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; | ||||
int error = 0; | int error = 0; | ||||
ifs = ifunit(req->ifbr_ifsname); | ifs = ifunit(req->ifbr_ifsname); | ||||
if (ifs == NULL) | if (ifs == NULL) | ||||
return (ENOENT); | return (ENOENT); | ||||
if (ifs->if_ioctl == NULL) /* must be supported */ | if (ifs->if_ioctl == NULL) /* must be supported */ | ||||
return (EINVAL); | return (EINVAL); | ||||
/* If it's in the span list, it can't be a member. */ | /* If it's in the span list, it can't be a member. */ | ||||
LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) | CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) | ||||
if (ifs == bif->bif_ifp) | if (ifs == bif->bif_ifp) | ||||
return (EBUSY); | return (EBUSY); | ||||
if (ifs->if_bridge == sc) | if (ifs->if_bridge == sc) | ||||
return (EEXIST); | return (EEXIST); | ||||
if (ifs->if_bridge != NULL) | if (ifs->if_bridge != NULL) | ||||
return (EBUSY); | return (EBUSY); | ||||
Show All 22 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); | BRIDGE_XLOCK(sc); | ||||
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", | ||||
Show All 9 Lines | if (in6ifa_llaonifp(ifs)) { | ||||
"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", | ||||
ifs->if_xname); | ifs->if_xname); | ||||
} | } | ||||
} | } | ||||
#endif | #endif | ||||
/* Allow the first Ethernet member to define the MTU */ | /* Allow the first Ethernet member to define the MTU */ | ||||
if (LIST_EMPTY(&sc->sc_iflist)) | if (CK_LIST_EMPTY(&sc->sc_iflist)) | ||||
sc->sc_ifp->if_mtu = ifs->if_mtu; | sc->sc_ifp->if_mtu = ifs->if_mtu; | ||||
else if (sc->sc_ifp->if_mtu != ifs->if_mtu) { | else if (sc->sc_ifp->if_mtu != ifs->if_mtu) { | ||||
if_printf(sc->sc_ifp, "invalid MTU: %u(%s) != %u\n", | if_printf(sc->sc_ifp, "invalid MTU: %u(%s) != %u\n", | ||||
ifs->if_mtu, ifs->if_xname, sc->sc_ifp->if_mtu); | ifs->if_mtu, ifs->if_xname, sc->sc_ifp->if_mtu); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO); | bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO); | ||||
if (bif == NULL) | if (bif == NULL) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
bif->bif_ifp = ifs; | bif->bif_ifp = ifs; | ||||
bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER; | bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER; | ||||
bif->bif_savedcaps = ifs->if_capenable; | bif->bif_savedcaps = ifs->if_capenable; | ||||
/* | /* | ||||
* Assign the interface's MAC address to the bridge if it's the first | * Assign the interface's MAC address to the bridge if it's the first | ||||
* member and the MAC address of the bridge has not been changed from | * member and the MAC address of the bridge has not been changed from | ||||
* the default randomly generated one. | * the default randomly generated one. | ||||
*/ | */ | ||||
if (V_bridge_inherit_mac && LIST_EMPTY(&sc->sc_iflist) && | if (V_bridge_inherit_mac && CK_LIST_EMPTY(&sc->sc_iflist) && | ||||
!memcmp(IF_LLADDR(sc->sc_ifp), sc->sc_defaddr.octet, ETHER_ADDR_LEN)) { | !memcmp(IF_LLADDR(sc->sc_ifp), sc->sc_defaddr.octet, ETHER_ADDR_LEN)) { | ||||
bcopy(IF_LLADDR(ifs), IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); | bcopy(IF_LLADDR(ifs), IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); | ||||
sc->sc_ifaddr = ifs; | sc->sc_ifaddr = ifs; | ||||
EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp); | EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp); | ||||
} | } | ||||
ifs->if_bridge = sc; | ifs->if_bridge = sc; | ||||
ifs->if_bridge_output = bridge_output; | ifs->if_bridge_output = bridge_output; | ||||
ifs->if_bridge_input = bridge_input; | ifs->if_bridge_input = bridge_input; | ||||
ifs->if_bridge_linkstate = bridge_linkstate; | ifs->if_bridge_linkstate = bridge_linkstate; | ||||
bstp_create(&sc->sc_stp, &bif->bif_stp, bif->bif_ifp); | bstp_create(&sc->sc_stp, &bif->bif_stp, bif->bif_ifp); | ||||
/* | /* | ||||
* XXX: XLOCK HERE!?! | * XXX: XLOCK HERE!?! | ||||
* | * | ||||
* NOTE: insert_***HEAD*** should be safe for the traversals. | * NOTE: insert_***HEAD*** should be safe for the traversals. | ||||
*/ | */ | ||||
LIST_INSERT_HEAD(&sc->sc_iflist, bif, bif_next); | CK_LIST_INSERT_HEAD(&sc->sc_iflist, bif, bif_next); | ||||
/* Set interface capabilities to the intersection set of all members */ | /* Set interface capabilities to the intersection set of all members */ | ||||
bridge_mutecaps(sc); | bridge_mutecaps(sc); | ||||
bridge_linkcheck(sc); | bridge_linkcheck(sc); | ||||
/* Place the interface into promiscuous mode */ | /* Place the interface into promiscuous mode */ | ||||
switch (ifs->if_type) { | switch (ifs->if_type) { | ||||
case IFT_ETHER: | case IFT_ETHER: | ||||
▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
struct ifbifconf *bifc = arg; | struct ifbifconf *bifc = arg; | ||||
struct bridge_iflist *bif; | struct bridge_iflist *bif; | ||||
struct ifbreq breq; | struct ifbreq breq; | ||||
char *buf, *outbuf; | char *buf, *outbuf; | ||||
int count, buflen, len, error = 0; | int count, buflen, len, error = 0; | ||||
count = 0; | count = 0; | ||||
LIST_FOREACH(bif, &sc->sc_iflist, bif_next) | CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) | ||||
count++; | count++; | ||||
LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) | CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) | ||||
count++; | count++; | ||||
buflen = sizeof(breq) * count; | buflen = sizeof(breq) * count; | ||||
if (bifc->ifbic_len == 0) { | if (bifc->ifbic_len == 0) { | ||||
bifc->ifbic_len = buflen; | bifc->ifbic_len = buflen; | ||||
return (0); | return (0); | ||||
} | } | ||||
BRIDGE_UNLOCK(sc); | BRIDGE_UNLOCK(sc); | ||||
outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); | outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); | ||||
BRIDGE_LOCK(sc); | BRIDGE_LOCK(sc); | ||||
count = 0; | count = 0; | ||||
buf = outbuf; | buf = outbuf; | ||||
len = min(bifc->ifbic_len, buflen); | len = min(bifc->ifbic_len, buflen); | ||||
bzero(&breq, sizeof(breq)); | bzero(&breq, sizeof(breq)); | ||||
LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | ||||
if (len < sizeof(breq)) | if (len < sizeof(breq)) | ||||
break; | break; | ||||
strlcpy(breq.ifbr_ifsname, bif->bif_ifp->if_xname, | strlcpy(breq.ifbr_ifsname, bif->bif_ifp->if_xname, | ||||
sizeof(breq.ifbr_ifsname)); | sizeof(breq.ifbr_ifsname)); | ||||
/* Fill in the ifbreq structure */ | /* Fill in the ifbreq structure */ | ||||
error = bridge_ioctl_gifflags(sc, &breq); | error = bridge_ioctl_gifflags(sc, &breq); | ||||
if (error) | if (error) | ||||
break; | break; | ||||
memcpy(buf, &breq, sizeof(breq)); | memcpy(buf, &breq, sizeof(breq)); | ||||
count++; | count++; | ||||
buf += sizeof(breq); | buf += sizeof(breq); | ||||
len -= sizeof(breq); | len -= sizeof(breq); | ||||
} | } | ||||
LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) { | CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) { | ||||
if (len < sizeof(breq)) | if (len < sizeof(breq)) | ||||
break; | break; | ||||
strlcpy(breq.ifbr_ifsname, bif->bif_ifp->if_xname, | strlcpy(breq.ifbr_ifsname, bif->bif_ifp->if_xname, | ||||
sizeof(breq.ifbr_ifsname)); | sizeof(breq.ifbr_ifsname)); | ||||
breq.ifbr_ifsflags = bif->bif_flags; | breq.ifbr_ifsflags = bif->bif_flags; | ||||
breq.ifbr_portno = bif->bif_ifp->if_index & 0xfff; | breq.ifbr_portno = bif->bif_ifp->if_index & 0xfff; | ||||
memcpy(buf, &breq, sizeof(breq)); | memcpy(buf, &breq, sizeof(breq)); | ||||
Show All 18 Lines | bridge_ioctl_rts(struct bridge_softc *sc, void *arg) | ||||
struct ifbareq bareq; | struct ifbareq bareq; | ||||
char *buf, *outbuf; | char *buf, *outbuf; | ||||
int count, buflen, len, error = 0; | int count, buflen, len, error = 0; | ||||
if (bac->ifbac_len == 0) | if (bac->ifbac_len == 0) | ||||
return (0); | return (0); | ||||
count = 0; | count = 0; | ||||
LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) | CK_LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) | ||||
count++; | count++; | ||||
buflen = sizeof(bareq) * count; | buflen = sizeof(bareq) * count; | ||||
BRIDGE_UNLOCK(sc); | BRIDGE_UNLOCK(sc); | ||||
outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); | outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); | ||||
BRIDGE_LOCK(sc); | BRIDGE_LOCK(sc); | ||||
count = 0; | count = 0; | ||||
buf = outbuf; | buf = outbuf; | ||||
len = min(bac->ifbac_len, buflen); | len = min(bac->ifbac_len, buflen); | ||||
bzero(&bareq, sizeof(bareq)); | bzero(&bareq, sizeof(bareq)); | ||||
LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { | CK_LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { | ||||
if (len < sizeof(bareq)) | if (len < sizeof(bareq)) | ||||
goto out; | goto out; | ||||
strlcpy(bareq.ifba_ifsname, brt->brt_ifp->if_xname, | strlcpy(bareq.ifba_ifsname, brt->brt_ifp->if_xname, | ||||
sizeof(bareq.ifba_ifsname)); | sizeof(bareq.ifba_ifsname)); | ||||
memcpy(bareq.ifba_dst, brt->brt_addr, sizeof(brt->brt_addr)); | memcpy(bareq.ifba_dst, brt->brt_addr, sizeof(brt->brt_addr)); | ||||
bareq.ifba_vlan = brt->brt_vlan; | bareq.ifba_vlan = brt->brt_vlan; | ||||
if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC && | if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC && | ||||
time_uptime < brt->brt_expire) | time_uptime < brt->brt_expire) | ||||
Show All 18 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 146 Lines • ▼ Show 20 Lines | bridge_ioctl_addspan(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; | ||||
ifs = ifunit(req->ifbr_ifsname); | ifs = ifunit(req->ifbr_ifsname); | ||||
if (ifs == NULL) | if (ifs == NULL) | ||||
return (ENOENT); | return (ENOENT); | ||||
LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) | CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) | ||||
if (ifs == bif->bif_ifp) | if (ifs == bif->bif_ifp) | ||||
return (EBUSY); | return (EBUSY); | ||||
if (ifs->if_bridge != NULL) | if (ifs->if_bridge != NULL) | ||||
return (EBUSY); | return (EBUSY); | ||||
switch (ifs->if_type) { | switch (ifs->if_type) { | ||||
case IFT_ETHER: | case IFT_ETHER: | ||||
case IFT_GIF: | case IFT_GIF: | ||||
case IFT_L2VLAN: | case IFT_L2VLAN: | ||||
break; | break; | ||||
default: | default: | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO); | bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO); | ||||
if (bif == NULL) | if (bif == NULL) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
bif->bif_ifp = ifs; | bif->bif_ifp = ifs; | ||||
bif->bif_flags = IFBIF_SPAN; | bif->bif_flags = IFBIF_SPAN; | ||||
LIST_INSERT_HEAD(&sc->sc_spanlist, bif, bif_next); | CK_LIST_INSERT_HEAD(&sc->sc_spanlist, bif, bif_next); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
bridge_ioctl_delspan(struct bridge_softc *sc, void *arg) | bridge_ioctl_delspan(struct bridge_softc *sc, void *arg) | ||||
{ | { | ||||
struct ifbreq *req = arg; | struct ifbreq *req = arg; | ||||
struct bridge_iflist *bif; | struct bridge_iflist *bif; | ||||
struct ifnet *ifs; | struct ifnet *ifs; | ||||
ifs = ifunit(req->ifbr_ifsname); | ifs = ifunit(req->ifbr_ifsname); | ||||
if (ifs == NULL) | if (ifs == NULL) | ||||
return (ENOENT); | return (ENOENT); | ||||
LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) | CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) | ||||
if (ifs == bif->bif_ifp) | if (ifs == bif->bif_ifp) | ||||
break; | break; | ||||
if (bif == NULL) | if (bif == NULL) | ||||
return (ENOENT); | return (ENOENT); | ||||
bridge_delete_span(sc, bif); | bridge_delete_span(sc, bif); | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | bridge_ioctl_gifsstp(struct bridge_softc *sc, void *arg) | ||||
struct ifbpstpconf *bifstp = arg; | struct ifbpstpconf *bifstp = arg; | ||||
struct bridge_iflist *bif; | struct bridge_iflist *bif; | ||||
struct bstp_port *bp; | struct bstp_port *bp; | ||||
struct ifbpstpreq bpreq; | struct ifbpstpreq bpreq; | ||||
char *buf, *outbuf; | char *buf, *outbuf; | ||||
int count, buflen, len, error = 0; | int count, buflen, len, error = 0; | ||||
count = 0; | count = 0; | ||||
LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | ||||
if ((bif->bif_flags & IFBIF_STP) != 0) | if ((bif->bif_flags & IFBIF_STP) != 0) | ||||
count++; | count++; | ||||
} | } | ||||
buflen = sizeof(bpreq) * count; | buflen = sizeof(bpreq) * count; | ||||
if (bifstp->ifbpstp_len == 0) { | if (bifstp->ifbpstp_len == 0) { | ||||
bifstp->ifbpstp_len = buflen; | bifstp->ifbpstp_len = buflen; | ||||
return (0); | return (0); | ||||
} | } | ||||
BRIDGE_UNLOCK(sc); | BRIDGE_UNLOCK(sc); | ||||
outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); | outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); | ||||
BRIDGE_LOCK(sc); | BRIDGE_LOCK(sc); | ||||
count = 0; | count = 0; | ||||
buf = outbuf; | buf = outbuf; | ||||
len = min(bifstp->ifbpstp_len, buflen); | len = min(bifstp->ifbpstp_len, buflen); | ||||
bzero(&bpreq, sizeof(bpreq)); | bzero(&bpreq, sizeof(bpreq)); | ||||
LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | ||||
if (len < sizeof(bpreq)) | if (len < sizeof(bpreq)) | ||||
break; | break; | ||||
if ((bif->bif_flags & IFBIF_STP) == 0) | if ((bif->bif_flags & IFBIF_STP) == 0) | ||||
continue; | continue; | ||||
bp = &bif->bif_stp; | bp = &bif->bif_stp; | ||||
bpreq.ifbp_portno = bif->bif_ifp->if_index & 0xfff; | bpreq.ifbp_portno = bif->bif_ifp->if_index & 0xfff; | ||||
Show All 39 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); | ||||
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 20 Lines • Show All 143 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); | CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { | ||||
if (error) { | |||||
m_freem(m); | |||||
return (0); | |||||
} | |||||
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; | ||||
/* | /* | ||||
* If this is not the original output interface, | * If this is not the original output interface, | ||||
* and the interface is participating in spanning | * and the interface is participating in spanning | ||||
* tree, make sure the port is in a state that | * tree, make sure the port is in a state that | ||||
* allows forwarding. | * allows forwarding. | ||||
*/ | */ | ||||
if (dst_if != ifp && (bif->bif_flags & IFBIF_STP) && | if (dst_if != ifp && (bif->bif_flags & IFBIF_STP) && | ||||
bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) | bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) | ||||
continue; | continue; | ||||
if (LIST_NEXT(bif, bif_next) == NULL) { | if (CK_LIST_NEXT(bif, bif_next) == NULL) { | ||||
used = 1; | used = 1; | ||||
mc = m; | mc = m; | ||||
} else { | } else { | ||||
mc = m_copypacket(m, M_NOWAIT); | mc = m_copypacket(m, M_NOWAIT); | ||||
if (mc == NULL) { | if (mc == NULL) { | ||||
if_inc_counter(bifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(bifp, IFCOUNTER_OERRORS, 1); | ||||
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); | ||||
/* | /* | ||||
* Give a chance for ifp at first priority. This will help when the | * Give a chance for ifp at first priority. This will help when the | ||||
* packet comes through the interface like VLAN's with the same MACs | * packet comes through the interface like VLAN's with the same MACs | ||||
* on several interfaces from the same bridge. This also will save | * on several interfaces from the same bridge. This also will save | ||||
* some CPU cycles in case the destination interface and the input | * some CPU cycles in case the destination interface and the input | ||||
* interface (eq ifp) are the same. | * interface (eq ifp) are the same. | ||||
*/ | */ | ||||
do { GRAB_OUR_PACKETS(ifp) } while (0); | do { GRAB_OUR_PACKETS(ifp) } while (0); | ||||
/* Now check the all bridge members. */ | /* Now check the all bridge members. */ | ||||
LIST_FOREACH(bif2, &sc->sc_iflist, bif_next) { | CK_LIST_FOREACH(bif2, &sc->sc_iflist, bif_next) { | ||||
GRAB_OUR_PACKETS(bif2->bif_ifp) | GRAB_OUR_PACKETS(bif2->bif_ifp) | ||||
} | } | ||||
#undef OR_CARP_CHECK_WE_ARE_DST | #undef OR_CARP_CHECK_WE_ARE_DST | ||||
#undef OR_CARP_CHECK_WE_ARE_SRC | #undef OR_CARP_CHECK_WE_ARE_SRC | ||||
#undef OR_PFIL_HOOKED_INET6 | #undef OR_PFIL_HOOKED_INET6 | ||||
#undef GRAB_OUR_PACKETS | #undef GRAB_OUR_PACKETS | ||||
Show All 14 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; | ||||
} | } | ||||
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 */ | ||||
if (sbif && (sbif->bif_flags & dbif->bif_flags & IFBIF_PRIVATE)) | if (sbif && (sbif->bif_flags & dbif->bif_flags & IFBIF_PRIVATE)) | ||||
continue; | continue; | ||||
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) | ||||
continue; | continue; | ||||
if ((dbif->bif_flags & IFBIF_DISCOVER) == 0 && | if ((dbif->bif_flags & IFBIF_DISCOVER) == 0 && | ||||
(m->m_flags & (M_BCAST|M_MCAST)) == 0) | (m->m_flags & (M_BCAST|M_MCAST)) == 0) | ||||
continue; | continue; | ||||
if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) | if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) | ||||
continue; | continue; | ||||
if (LIST_NEXT(dbif, bif_next) == NULL) { | if (CK_LIST_NEXT(dbif, bif_next) == NULL) { | ||||
mc = m; | mc = m; | ||||
used = 1; | used = 1; | ||||
} else { | } else { | ||||
mc = m_dup(m, M_NOWAIT); | mc = m_dup(m, M_NOWAIT); | ||||
if (mc == NULL) { | if (mc == NULL) { | ||||
if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); | ||||
continue; | continue; | ||||
} | } | ||||
Show All 23 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; | ||||
if (LIST_EMPTY(&sc->sc_spanlist)) | NET_EPOCH_ASSERT(); | ||||
if (CK_LIST_EMPTY(&sc->sc_spanlist)) | |||||
return; | return; | ||||
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; | ||||
mc = m_copypacket(m, M_NOWAIT); | mc = m_copypacket(m, M_NOWAIT); | ||||
if (mc == NULL) { | if (mc == NULL) { | ||||
if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); | ||||
Show All 11 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); | ||||
} | } | ||||
/* | /* | ||||
Show All 14 Lines | bridge_rttrim(struct bridge_softc *sc) | ||||
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); | ||||
if (sc->sc_brtcnt <= sc->sc_brtmax) | if (sc->sc_brtcnt <= sc->sc_brtmax) | ||||
return; | return; | ||||
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) { | ||||
bridge_rtnode_destroy(sc, brt); | bridge_rtnode_destroy(sc, brt); | ||||
if (sc->sc_brtcnt <= sc->sc_brtmax) | if (sc->sc_brtcnt <= sc->sc_brtmax) | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
Show All 26 Lines | |||||
*/ | */ | ||||
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; | ||||
BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
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; | ||||
BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
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: | ||||
* | * | ||||
Show All 26 Lines | |||||
*/ | */ | ||||
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; | ||||
BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
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); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* bridge_rtable_init: | * bridge_rtable_init: | ||||
* | * | ||||
* Initialize the route table for this bridge. | * Initialize the route table for this bridge. | ||||
*/ | */ | ||||
static void | static void | ||||
bridge_rtable_init(struct bridge_softc *sc) | bridge_rtable_init(struct bridge_softc *sc) | ||||
{ | { | ||||
int i; | int i; | ||||
sc->sc_rthash = malloc(sizeof(*sc->sc_rthash) * BRIDGE_RTHASH_SIZE, | sc->sc_rthash = malloc(sizeof(*sc->sc_rthash) * BRIDGE_RTHASH_SIZE, | ||||
M_DEVBUF, M_WAITOK); | M_DEVBUF, M_WAITOK); | ||||
for (i = 0; i < BRIDGE_RTHASH_SIZE; i++) | for (i = 0; i < BRIDGE_RTHASH_SIZE; i++) | ||||
LIST_INIT(&sc->sc_rthash[i]); | CK_LIST_INIT(&sc->sc_rthash[i]); | ||||
sc->sc_rthash_key = arc4random(); | sc->sc_rthash_key = arc4random(); | ||||
LIST_INIT(&sc->sc_rtlist); | CK_LIST_INIT(&sc->sc_rtlist); | ||||
} | } | ||||
/* | /* | ||||
* bridge_rtable_fini: | * bridge_rtable_fini: | ||||
* | * | ||||
* Deconstruct the route table for this bridge. | * Deconstruct the route table for this bridge. | ||||
*/ | */ | ||||
static void | static void | ||||
▲ Show 20 Lines • Show All 61 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); | ||||
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); | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
Show All 11 Lines | bridge_rtnode_insert(struct bridge_softc *sc, struct bridge_rtnode *brt) | ||||
struct bridge_rtnode *lbrt; | struct bridge_rtnode *lbrt; | ||||
uint32_t hash; | uint32_t hash; | ||||
int dir; | int dir; | ||||
BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
hash = bridge_rthash(sc, brt->brt_addr); | hash = bridge_rthash(sc, brt->brt_addr); | ||||
lbrt = LIST_FIRST(&sc->sc_rthash[hash]); | lbrt = CK_LIST_FIRST(&sc->sc_rthash[hash]); | ||||
if (lbrt == NULL) { | if (lbrt == NULL) { | ||||
LIST_INSERT_HEAD(&sc->sc_rthash[hash], brt, brt_hash); | CK_LIST_INSERT_HEAD(&sc->sc_rthash[hash], brt, brt_hash); | ||||
goto out; | goto out; | ||||
} | } | ||||
do { | do { | ||||
dir = bridge_rtnode_addr_cmp(brt->brt_addr, lbrt->brt_addr); | dir = bridge_rtnode_addr_cmp(brt->brt_addr, lbrt->brt_addr); | ||||
if (dir == 0 && brt->brt_vlan == lbrt->brt_vlan) | if (dir == 0 && brt->brt_vlan == lbrt->brt_vlan) | ||||
return (EEXIST); | return (EEXIST); | ||||
if (dir > 0) { | if (dir > 0) { | ||||
LIST_INSERT_BEFORE(lbrt, brt, brt_hash); | CK_LIST_INSERT_BEFORE(lbrt, brt, brt_hash); | ||||
goto out; | goto out; | ||||
} | } | ||||
if (LIST_NEXT(lbrt, brt_hash) == NULL) { | if (CK_LIST_NEXT(lbrt, brt_hash) == NULL) { | ||||
LIST_INSERT_AFTER(lbrt, brt, brt_hash); | CK_LIST_INSERT_AFTER(lbrt, brt, brt_hash); | ||||
goto out; | goto out; | ||||
} | } | ||||
lbrt = LIST_NEXT(lbrt, brt_hash); | lbrt = CK_LIST_NEXT(lbrt, brt_hash); | ||||
} while (lbrt != NULL); | } while (lbrt != NULL); | ||||
#ifdef DIAGNOSTIC | #ifdef DIAGNOSTIC | ||||
panic("bridge_rtnode_insert: impossible"); | panic("bridge_rtnode_insert: impossible"); | ||||
#endif | #endif | ||||
out: | out: | ||||
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) | ||||
{ | { | ||||
BRIDGE_LOCK_ASSERT(sc); | BRIDGE_LOCK_ASSERT(sc); | ||||
LIST_REMOVE(brt, brt_hash); | CK_LIST_REMOVE(brt, brt_hash); | ||||
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; | ||||
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 { | ||||
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); | ||||
▲ Show 20 Lines • Show All 498 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; < |
Are BRIDGE_LOCK2REF/ BRIDGE_UNREF still needed?