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 21 Lines | 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 */ | ||||
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 { | ||||
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 */ | ||||
▲ Show 20 Lines • Show All 332 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 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; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 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) | ||||
▲ Show 20 Lines • Show All 64 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 20 Lines • Show All 366 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 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); | |||||
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); | ||||
} | } | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 222 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) | ||||
{ | { | ||||
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 | ||||
▲ Show 20 Lines • Show All 522 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?