diff --git a/sbin/ifconfig/ifbridge.c b/sbin/ifconfig/ifbridge.c index 36e308bb7655..c42213f064e3 100644 --- a/sbin/ifconfig/ifbridge.c +++ b/sbin/ifconfig/ifbridge.c @@ -289,6 +289,28 @@ unsetbridge_learn(const char *val, int d, int s, const struct afswtch *afp) do_bridgeflag(s, val, IFBIF_LEARNING, 0); } +static void +setbridge_span(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGADDS, &req, sizeof(req), 1) < 0) + err(1, "BRDGADDS %s", val); +} + +static void +unsetbridge_span(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGDELS, &req, sizeof(req), 1) < 0) + err(1, "BRDGDELS %s", val); +} + static void setbridge_stp(const char *val, int d, int s, const struct afswtch *afp) { @@ -506,6 +528,8 @@ static struct cmd bridge_cmds[] = { DEF_CMD_ARG("-discover", unsetbridge_discover), DEF_CMD_ARG("learn", setbridge_learn), DEF_CMD_ARG("-learn", unsetbridge_learn), + DEF_CMD_ARG("span", setbridge_span), + DEF_CMD_ARG("-span", unsetbridge_span), DEF_CMD_ARG("stp", setbridge_stp), DEF_CMD_ARG("-stp", unsetbridge_stp), DEF_CMD("flush", 0, setbridge_flush), diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index 5f0853b4625f..0aa6e6736fac 100644 --- a/sbin/ifconfig/ifconfig.8 +++ b/sbin/ifconfig/ifconfig.8 @@ -1106,6 +1106,17 @@ This is the default for all interfaces added to a bridge. Clear the .Dq learning attribute on a member interface. +.It Cm span Ar interface +Add the interface named by +.Ar interface +as a span port on the bridge. +Span ports transmit a copy of every frame received by the bridge. +This is most useful for snooping a bridged network passively on +another host connected to one of the span ports of the bridge. +.It Cm -span Ar interface +Delete the interface named by +.Ar interface +from the list of span ports of the bridge. .It Cm stp Ar interface Enable Spanning Tree protocol on .Ar interface . diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index 42b0ca336594..4d36c96a7221 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -193,6 +193,7 @@ static void bridge_timer(void *); static void bridge_broadcast(struct bridge_softc *, struct ifnet *, struct mbuf *, int); +static void bridge_span(struct bridge_softc *, struct mbuf *); static int bridge_rtupdate(struct bridge_softc *, const uint8_t *, struct ifnet *, int, uint8_t); @@ -242,6 +243,8 @@ static int bridge_ioctl_gma(struct bridge_softc *, void *); static int bridge_ioctl_sma(struct bridge_softc *, void *); static int bridge_ioctl_sifprio(struct bridge_softc *, void *); static int bridge_ioctl_sifcost(struct bridge_softc *, void *); +static int bridge_ioctl_addspan(struct bridge_softc *, void *); +static int bridge_ioctl_delspan(struct bridge_softc *, void *); static int bridge_pfil(struct mbuf **, struct ifnet *, struct ifnet *, int); static int bridge_ip_checkbasic(struct mbuf **mp); @@ -330,6 +333,11 @@ const struct bridge_control bridge_control_table[] = { { bridge_ioctl_sifcost, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_addspan, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_SUSER }, + { bridge_ioctl_delspan, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_SUSER }, }; const int bridge_control_table_size = sizeof(bridge_control_table) / sizeof(bridge_control_table[0]); @@ -443,6 +451,7 @@ bridge_clone_create(struct if_clone *ifc, int unit) callout_init_mtx(&sc->sc_bstpcallout, &sc->sc_mtx, 0); LIST_INIT(&sc->sc_iflist); + LIST_INIT(&sc->sc_spanlist); ifp->if_softc = sc; if_initname(ifp, ifc->ifc_name, unit); @@ -494,6 +503,11 @@ bridge_clone_destroy(struct ifnet *ifp) while ((bif = LIST_FIRST(&sc->sc_iflist)) != NULL) bridge_delete_member(sc, bif, 0); + while ((bif = LIST_FIRST(&sc->sc_spanlist)) != NULL) { + LIST_REMOVE(bif, bif_next); + free(bif, M_DEVBUF); + } + BRIDGE_UNLOCK(sc); callout_drain(&sc->sc_brcallout); @@ -724,6 +738,11 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg) if (ifs == NULL) return (ENOENT); + /* If it's in the span list, it can't be a member. */ + LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) + if (ifs == bif->bif_ifp) + return (EBUSY); + /* Allow the first member to define the MTU */ if (LIST_EMPTY(&sc->sc_iflist)) sc->sc_ifp->if_mtu = ifs->if_mtu; @@ -834,6 +853,10 @@ bridge_ioctl_sifflags(struct bridge_softc *sc, void *arg) if (bif == NULL) return (ENOENT); + if (req->ifbr_ifsflags & IFBIF_SPAN) + /* SPAN is readonly */ + return (EINVAL); + if (req->ifbr_ifsflags & IFBIF_STP) { switch (bif->bif_ifp->if_type) { case IFT_ETHER: @@ -892,6 +915,8 @@ bridge_ioctl_gifs(struct bridge_softc *sc, void *arg) count = 0; LIST_FOREACH(bif, &sc->sc_iflist, bif_next) count++; + LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) + count++; if (bifc->ifbic_len == 0) { bifc->ifbic_len = sizeof(breq) * count; @@ -917,6 +942,23 @@ bridge_ioctl_gifs(struct bridge_softc *sc, void *arg) count++; len -= sizeof(breq); } + LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) { + if (len < sizeof(breq)) + break; + + strlcpy(breq.ifbr_ifsname, bif->bif_ifp->if_xname, + sizeof(breq.ifbr_ifsname)); + breq.ifbr_ifsflags = bif->bif_flags; + breq.ifbr_state = bif->bif_state; + breq.ifbr_priority = bif->bif_priority; + breq.ifbr_path_cost = bif->bif_path_cost; + breq.ifbr_portno = bif->bif_ifp->if_index & 0xff; + error = copyout(&breq, bifc->ifbic_req + count, sizeof(breq)); + if (error) + break; + count++; + len -= sizeof(breq); + } bifc->ifbic_len = sizeof(breq) * count; return (error); @@ -1182,6 +1224,73 @@ bridge_ioctl_sifcost(struct bridge_softc *sc, void *arg) return (0); } +static int +bridge_ioctl_addspan(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + struct bridge_iflist *bif = NULL; + struct ifnet *ifs; + + BRIDGE_LOCK_ASSERT(sc); + + ifs = ifunit(req->ifbr_ifsname); + if (ifs == NULL) + return (ENOENT); + + LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) + if (ifs == bif->bif_ifp) + return (EBUSY); + + if (ifs->if_bridge != NULL) + return (EBUSY); + + switch (ifs->if_type) { + case IFT_ETHER: + case IFT_L2VLAN: + break; + default: + return (EINVAL); + } + + bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO); + if (bif == NULL) + return (ENOMEM); + + bif->bif_ifp = ifs; + bif->bif_flags = IFBIF_SPAN; + + LIST_INSERT_HEAD(&sc->sc_spanlist, bif, bif_next); + + return (0); +} + +static int +bridge_ioctl_delspan(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + struct bridge_iflist *bif; + + struct ifnet *ifs; + + BRIDGE_LOCK_ASSERT(sc); + + ifs = ifunit(req->ifbr_ifsname); + if (ifs == NULL) + return (ENOENT); + + LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) + if (ifs == bif->bif_ifp) + break; + + if (bif == NULL) + return (ENOENT); + + LIST_REMOVE(bif, bif_next); + free(bif, M_DEVBUF); + + return (0); +} + /* * bridge_ifdetach: * @@ -1386,6 +1495,9 @@ bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, m_freem(m); return (0); } + + bridge_span(sc, m); + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { dst_if = bif->bif_ifp; if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) @@ -1431,6 +1543,7 @@ bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, * XXX Spanning tree consideration here? */ + bridge_span(sc, m); if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) { m_freem(m); BRIDGE_UNLOCK(sc); @@ -1694,6 +1807,8 @@ bridge_input(struct ifnet *ifp, struct mbuf *m) return (m); } + bridge_span(sc, m); + if (ETHER_IS_MULTICAST(eh->ether_dhost)) { /* Tap off 802.1D packets; they do not get forwarded. */ if (memcmp(eh->ether_dhost, bstp_etheraddr, @@ -1886,6 +2001,38 @@ bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if, BRIDGE_UNREF(sc); } +/* + * bridge_span: + * + * Duplicate a packet out one or more interfaces that are in span mode, + * the original mbuf is unmodified. + */ +static void +bridge_span(struct bridge_softc *sc, struct mbuf *m) +{ + struct bridge_iflist *bif; + struct ifnet *dst_if; + struct mbuf *mc; + + if (LIST_EMPTY(&sc->sc_spanlist)) + return; + + LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) { + dst_if = bif->bif_ifp; + + if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) + continue; + + mc = m_copypacket(m, M_DONTWAIT); + if (mc == NULL) { + sc->sc_ifp->if_oerrors++; + continue; + } + + bridge_enqueue(sc, dst_if, mc); + } +} + /* * bridge_rtupdate: * diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h index 98dfcda34dbf..7ec672a1f6b3 100644 --- a/sys/net/if_bridgevar.h +++ b/sys/net/if_bridgevar.h @@ -106,6 +106,8 @@ #define BRDGSMA 20 /* set max age (ifbrparam) */ #define BRDGSIFPRIO 21 /* set if priority (ifbreq) */ #define BRDGSIFCOST 22 /* set if path cost (ifbreq) */ +#define BRDGADDS 23 /* add bridge span member (ifbreq) */ +#define BRDGDELS 24 /* delete bridge span member (ifbreq) */ /* * Generic bridge control request. @@ -123,8 +125,9 @@ struct ifbreq { #define IFBIF_LEARNING 0x01 /* if can learn */ #define IFBIF_DISCOVER 0x02 /* if sends packets w/ unknown dest. */ #define IFBIF_STP 0x04 /* if participates in spanning tree */ +#define IFBIF_SPAN 0x08 /* if is a span port */ -#define IFBIFBITS "\020\1LEARNING\2DISCOVER\3STP" +#define IFBIFBITS "\020\1LEARNING\2DISCOVER\3STP\4SPAN" /* BRDGFLUSH */ #define IFBF_FLUSHDYN 0x00 /* flush learned addresses only */ @@ -295,6 +298,7 @@ struct bridge_softc { LIST_HEAD(, bridge_rtnode) *sc_rthash; /* our forwarding table */ LIST_HEAD(, bridge_rtnode) sc_rtlist; /* list version of above */ uint32_t sc_rthash_key; /* key for hash */ + LIST_HEAD(, bridge_iflist) sc_spanlist; /* span ports list */ }; #define BRIDGE_LOCK_INIT(_sc) do { \