Page MenuHomeFreeBSD

D58730.diff
No OneTemporary

D58730.diff

diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -192,6 +192,13 @@
#define BRIDGE_RTABLE_PRUNE_PERIOD (5 * 60)
#endif
+/*
+ * Number of seconds between walks of the multicast database member list.
+ */
+#ifndef BRIDGE_MDB_PRUNE_PERIOD
+#define BRIDGE_MDB_PRUNE_PERIOD (5 * 60)
+#endif
+
/*
* List of capabilities to possibly mask on the member interface.
*/
@@ -213,7 +220,7 @@
* That is: for read accesses we only need to be in NET_EPOCH, but for write
* accesses we must hold:
*
- * - BRIDGE_RT_LOCK, for any change to bridge_rtnodes
+ * - BRIDGE_RT_LOCK, for any change to bridge_rtnodes or MDB state
* - BRIDGE_LOCK, for any other change
*
* The BRIDGE_LOCK is a sleepable lock, because it is held across ioctl()
@@ -278,6 +285,31 @@
};
#define brt_ifp brt_dst->bif_ifp
+/*
+ * Bridge multicast database entry. The MDB records which member interfaces
+ * have listeners for a multicast group.
+ */
+struct bridge_mdb_key {
+ sa_family_t bmk_family;
+ ether_vlanid_t bmk_vlan;
+ union {
+ struct in_addr bmk_ip4;
+ struct in6_addr bmk_ip6;
+ } bmk_addr;
+};
+
+struct bridge_mdb_member {
+ CK_LIST_ENTRY(bridge_mdb_member) bmm_next;
+ struct bridge_iflist *bmm_bif;
+ unsigned long bmm_expire;
+};
+
+struct bridge_mdb_entry {
+ CK_LIST_ENTRY(bridge_mdb_entry) bmd_next;
+ struct bridge_mdb_key bmd_key;
+ CK_LIST_HEAD(, bridge_mdb_member) bmd_members;
+};
+
/*
* Software state for each bridge.
*/
@@ -291,9 +323,13 @@
uint32_t sc_brtcnt; /* cur. # of addresses */
uint32_t sc_brttimeout; /* rt timeout in seconds */
struct callout sc_brcallout; /* bridge callout */
+ struct callout sc_mdbcallout; /* MDB aging callout */
CK_LIST_HEAD(, bridge_iflist) sc_iflist; /* member interface list */
CK_LIST_HEAD(, bridge_rtnode) *sc_rthash; /* our forwarding table */
CK_LIST_HEAD(, bridge_rtnode) sc_rtlist; /* list version of above */
+ CK_LIST_HEAD(, bridge_mdb_entry) sc_mdb; /* multicast database */
+ uint32_t sc_mdbcnt; /* # of multicast groups */
+ uint32_t sc_mdbmembercnt; /* # of multicast members */
uint32_t sc_rthash_key; /* key for hash */
CK_LIST_HEAD(, bridge_iflist) sc_spanlist; /* span ports list */
struct bstp_state sc_stp; /* STP state */
@@ -347,6 +383,7 @@
struct mbuf *m);
static bool bridge_member_ifaddrs(void);
static void bridge_timer(void *);
+static void bridge_mdb_timer(void *);
static void bridge_broadcast(struct bridge_softc *, struct ifnet *,
struct mbuf *, int);
@@ -367,6 +404,14 @@
static void bridge_rtable_init(struct bridge_softc *);
static void bridge_rtable_fini(struct bridge_softc *);
+static void bridge_mdb_init(struct bridge_softc *);
+static void bridge_mdb_fini(struct bridge_softc *);
+static void bridge_mdb_prune(struct bridge_softc *);
+static void bridge_mdb_flush_member(struct bridge_softc *,
+ struct bridge_iflist *);
+static int bridge_mdb_add_member(struct bridge_softc *, sa_family_t,
+ const void *, ether_vlanid_t, struct bridge_iflist *,
+ unsigned int) __unused;
static int bridge_rtnode_addr_cmp(const uint8_t *, const uint8_t *);
static struct bridge_rtnode *bridge_rtnode_lookup(struct bridge_softc *,
@@ -875,8 +920,10 @@
/* Initialize our routing table. */
bridge_rtable_init(sc);
+ bridge_mdb_init(sc);
callout_init_mtx(&sc->sc_brcallout, &sc->sc_rt_mtx, 0);
+ callout_init_mtx(&sc->sc_mdbcallout, &sc->sc_rt_mtx, 0);
CK_LIST_INIT(&sc->sc_iflist);
CK_LIST_INIT(&sc->sc_spanlist);
@@ -939,6 +986,241 @@
return ((sc->sc_group_fwd_mask & bit) != 0);
}
+static void
+bridge_mdb_init(struct bridge_softc *sc)
+{
+ CK_LIST_INIT(&sc->sc_mdb);
+ sc->sc_mdbcnt = 0;
+ sc->sc_mdbmembercnt = 0;
+}
+
+static int
+bridge_mdb_key_init(struct bridge_mdb_key *key, sa_family_t family,
+ const void *addr, ether_vlanid_t vlan)
+{
+ memset(key, 0, sizeof(*key));
+ key->bmk_family = family;
+ key->bmk_vlan = vlan;
+
+ switch (family) {
+#ifdef INET
+ case AF_INET:
+ memcpy(&key->bmk_addr.bmk_ip4, addr, sizeof(struct in_addr));
+ return (0);
+#endif
+#ifdef INET6
+ case AF_INET6:
+ memcpy(&key->bmk_addr.bmk_ip6, addr, sizeof(struct in6_addr));
+ return (0);
+#endif
+ default:
+ return (EAFNOSUPPORT);
+ }
+}
+
+static bool
+bridge_mdb_key_equal(const struct bridge_mdb_key *a,
+ const struct bridge_mdb_key *b)
+{
+ if (a->bmk_family != b->bmk_family || a->bmk_vlan != b->bmk_vlan)
+ return (false);
+
+ switch (a->bmk_family) {
+#ifdef INET
+ case AF_INET:
+ return (memcmp(&a->bmk_addr.bmk_ip4, &b->bmk_addr.bmk_ip4,
+ sizeof(struct in_addr)) == 0);
+#endif
+#ifdef INET6
+ case AF_INET6:
+ return (memcmp(&a->bmk_addr.bmk_ip6, &b->bmk_addr.bmk_ip6,
+ sizeof(struct in6_addr)) == 0);
+#endif
+ default:
+ return (false);
+ }
+}
+
+static struct bridge_mdb_entry *
+bridge_mdb_lookup(struct bridge_softc *sc, const struct bridge_mdb_key *key)
+{
+ struct bridge_mdb_entry *bmd;
+
+ BRIDGE_RT_LOCK_ASSERT(sc);
+
+ CK_LIST_FOREACH(bmd, &sc->sc_mdb, bmd_next) {
+ if (bridge_mdb_key_equal(&bmd->bmd_key, key))
+ return (bmd);
+ }
+
+ return (NULL);
+}
+
+static struct bridge_mdb_member *
+bridge_mdb_member_lookup(struct bridge_mdb_entry *bmd,
+ struct bridge_iflist *bif)
+{
+ struct bridge_mdb_member *bmm;
+
+ CK_LIST_FOREACH(bmm, &bmd->bmd_members, bmm_next) {
+ if (bmm->bmm_bif == bif)
+ return (bmm);
+ }
+
+ return (NULL);
+}
+
+static void
+bridge_mdb_destroy_entry(struct bridge_softc *sc, struct bridge_mdb_entry *bmd)
+{
+ struct bridge_mdb_member *bmm;
+
+ BRIDGE_RT_LOCK_ASSERT(sc);
+
+ while ((bmm = CK_LIST_FIRST(&bmd->bmd_members)) != NULL) {
+ CK_LIST_REMOVE(bmm, bmm_next);
+ sc->sc_mdbmembercnt--;
+ free(bmm, M_DEVBUF);
+ }
+
+ CK_LIST_REMOVE(bmd, bmd_next);
+ sc->sc_mdbcnt--;
+ free(bmd, M_DEVBUF);
+}
+
+static bool
+bridge_mdb_remove_member(struct bridge_softc *sc, struct bridge_mdb_entry *bmd,
+ struct bridge_mdb_member *bmm)
+{
+ BRIDGE_RT_LOCK_ASSERT(sc);
+
+ CK_LIST_REMOVE(bmm, bmm_next);
+ sc->sc_mdbmembercnt--;
+ free(bmm, M_DEVBUF);
+
+ if (!CK_LIST_EMPTY(&bmd->bmd_members))
+ return (false);
+
+ CK_LIST_REMOVE(bmd, bmd_next);
+ sc->sc_mdbcnt--;
+ free(bmd, M_DEVBUF);
+ return (true);
+}
+
+static int
+bridge_mdb_add_member(struct bridge_softc *sc, sa_family_t family,
+ const void *addr, ether_vlanid_t vlan, struct bridge_iflist *bif,
+ unsigned int lifetime)
+{
+ struct bridge_mdb_entry *bmd, *new_bmd;
+ struct bridge_mdb_member *bmm, *new_bmm;
+ struct bridge_mdb_key key;
+ bool created_bmd;
+ int error;
+
+ error = bridge_mdb_key_init(&key, family, addr, vlan);
+ if (error != 0)
+ return (error);
+
+ new_bmd = malloc(sizeof(*new_bmd), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (new_bmd != NULL) {
+ new_bmd->bmd_key = key;
+ CK_LIST_INIT(&new_bmd->bmd_members);
+ }
+
+ new_bmm = malloc(sizeof(*new_bmm), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (new_bmm != NULL)
+ new_bmm->bmm_bif = bif;
+
+ created_bmd = false;
+ BRIDGE_RT_LOCK(sc);
+ bmd = bridge_mdb_lookup(sc, &key);
+ if (bmd == NULL) {
+ if (new_bmd == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ bmd = new_bmd;
+ new_bmd = NULL;
+ CK_LIST_INSERT_HEAD(&sc->sc_mdb, bmd, bmd_next);
+ sc->sc_mdbcnt++;
+ created_bmd = true;
+ }
+
+ bmm = bridge_mdb_member_lookup(bmd, bif);
+ if (bmm == NULL) {
+ if (new_bmm == NULL) {
+ if (created_bmd)
+ bridge_mdb_destroy_entry(sc, bmd);
+ error = ENOMEM;
+ goto out;
+ }
+ bmm = new_bmm;
+ new_bmm = NULL;
+ CK_LIST_INSERT_HEAD(&bmd->bmd_members, bmm, bmm_next);
+ sc->sc_mdbmembercnt++;
+ }
+ bmm->bmm_expire = lifetime == 0 ? 0 : time_uptime + lifetime;
+out:
+ BRIDGE_RT_UNLOCK(sc);
+
+ if (new_bmd != NULL)
+ free(new_bmd, M_DEVBUF);
+ if (new_bmm != NULL)
+ free(new_bmm, M_DEVBUF);
+
+ return (error);
+}
+
+static void
+bridge_mdb_flush_member(struct bridge_softc *sc, struct bridge_iflist *bif)
+{
+ struct bridge_mdb_entry *bmd, *nbmd;
+ struct bridge_mdb_member *bmm, *nbmm;
+
+ BRIDGE_RT_LOCK_ASSERT(sc);
+
+ CK_LIST_FOREACH_SAFE(bmd, &sc->sc_mdb, bmd_next, nbmd) {
+ CK_LIST_FOREACH_SAFE(bmm, &bmd->bmd_members, bmm_next, nbmm) {
+ if (bmm->bmm_bif == bif &&
+ bridge_mdb_remove_member(sc, bmd, bmm))
+ break;
+ }
+ }
+}
+
+static void
+bridge_mdb_prune(struct bridge_softc *sc)
+{
+ struct bridge_mdb_entry *bmd, *nbmd;
+ struct bridge_mdb_member *bmm, *nbmm;
+
+ BRIDGE_RT_LOCK_ASSERT(sc);
+
+ CK_LIST_FOREACH_SAFE(bmd, &sc->sc_mdb, bmd_next, nbmd) {
+ CK_LIST_FOREACH_SAFE(bmm, &bmd->bmd_members, bmm_next, nbmm) {
+ if (bmm->bmm_expire != 0 &&
+ time_uptime >= bmm->bmm_expire &&
+ bridge_mdb_remove_member(sc, bmd, bmm))
+ break;
+ }
+ }
+}
+
+static void
+bridge_mdb_fini(struct bridge_softc *sc)
+{
+ struct bridge_mdb_entry *bmd;
+
+ BRIDGE_RT_LOCK_ASSERT(sc);
+
+ while ((bmd = CK_LIST_FIRST(&sc->sc_mdb)) != NULL)
+ bridge_mdb_destroy_entry(sc, bmd);
+
+ KASSERT(sc->sc_mdbcnt == 0 && sc->sc_mdbmembercnt == 0,
+ ("%s: MDB entries remain", __func__));
+}
+
static void
bridge_clone_destroy_cb(struct epoch_context *ctx)
{
@@ -979,6 +1261,11 @@
BRIDGE_UNLOCK(sc);
callout_drain(&sc->sc_brcallout);
+ callout_drain(&sc->sc_mdbcallout);
+
+ BRIDGE_RT_LOCK(sc);
+ bridge_mdb_fini(sc);
+ BRIDGE_RT_UNLOCK(sc);
BRIDGE_LIST_LOCK();
LIST_REMOVE(sc, sc_list);
@@ -1306,6 +1593,7 @@
bridge_linkcheck(sc);
bridge_mutecaps(sc); /* recalcuate now this interface is removed */
BRIDGE_RT_LOCK(sc);
+ bridge_mdb_flush_member(sc, bif);
bridge_rtdelete(sc, ifs, IFBF_FLUSHALL);
BRIDGE_RT_UNLOCK(sc);
KASSERT(bif->bif_addrcnt == 0,
@@ -2376,6 +2664,8 @@
BRIDGE_LOCK(sc);
callout_reset(&sc->sc_brcallout, bridge_rtable_prune_period * hz,
bridge_timer, sc);
+ callout_reset(&sc->sc_mdbcallout, BRIDGE_MDB_PRUNE_PERIOD * hz,
+ bridge_mdb_timer, sc);
ifp->if_drv_flags |= IFF_DRV_RUNNING;
bstp_init(&sc->sc_stp); /* Initialize Spanning Tree */
@@ -2400,6 +2690,7 @@
BRIDGE_RT_LOCK(sc);
callout_stop(&sc->sc_brcallout);
+ callout_stop(&sc->sc_mdbcallout);
bstp_stop(&sc->sc_stp);
@@ -3632,6 +3923,27 @@
CURVNET_RESTORE();
}
+/*
+ * bridge_mdb_timer:
+ *
+ * Aging timer for the multicast database.
+ */
+static void
+bridge_mdb_timer(void *arg)
+{
+ struct bridge_softc *sc = arg;
+
+ BRIDGE_RT_LOCK_ASSERT(sc);
+
+ CURVNET_SET(sc->sc_ifp->if_vnet);
+ bridge_mdb_prune(sc);
+
+ if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING)
+ callout_reset(&sc->sc_mdbcallout,
+ BRIDGE_MDB_PRUNE_PERIOD * hz, bridge_mdb_timer, sc);
+ CURVNET_RESTORE();
+}
+
/*
* bridge_rtage:
*
diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h
--- a/sys/net/if_bridgevar.h
+++ b/sys/net/if_bridgevar.h
@@ -247,8 +247,7 @@
#define ifbrp_csize ifbrp_ifbrpu.ifbrpu_int32 /* cache size */
#define ifbrp_ctime ifbrp_ifbrpu.ifbrpu_int32 /* cache time (sec) */
#define ifbrp_prio ifbrp_ifbrpu.ifbrpu_int16 /* bridge priority */
-#define ifbrp_group_fwd_mask ifbrp_ifbrpu.ifbrpu_int16 /* reserved group
- * fwd mask */
+#define ifbrp_group_fwd_mask ifbrp_ifbrpu.ifbrpu_int16 /* reserved group fwd mask */
#define ifbrp_proto ifbrp_ifbrpu.ifbrpu_int8 /* bridge protocol */
#define ifbrp_txhc ifbrp_ifbrpu.ifbrpu_int8 /* bpdu tx holdcount */
#define ifbrp_hellotime ifbrp_ifbrpu.ifbrpu_int8 /* hello time (sec) */
diff --git a/tests/sys/net/if_bridge_test.sh b/tests/sys/net/if_bridge_test.sh
--- a/tests/sys/net/if_bridge_test.sh
+++ b/tests/sys/net/if_bridge_test.sh
@@ -824,6 +824,58 @@
vnet_cleanup
}
+bridge_mdb_vnet_topology_create()
+{
+ mdb_sender_epair=$(vnet_mkepair)
+ mdb_listener_epair=$(vnet_mkepair)
+ mdb_idle_epair=$(vnet_mkepair)
+
+ vnet_mkjail mdbs ${mdb_sender_epair}b
+ vnet_mkjail mdbl ${mdb_listener_epair}b
+ vnet_mkjail mdbi ${mdb_idle_epair}b
+
+ jexec mdbs ifconfig ${mdb_sender_epair}b 192.0.2.1/24 up
+ jexec mdbl ifconfig ${mdb_listener_epair}b 192.0.2.2/24 up
+ jexec mdbi ifconfig ${mdb_idle_epair}b 192.0.2.3/24 up
+
+ mdb_bridge=$(vnet_mkbridge)
+ ifconfig ${mdb_bridge} up
+
+ ifconfig ${mdb_sender_epair}a up
+ ifconfig ${mdb_listener_epair}a up
+ ifconfig ${mdb_idle_epair}a up
+ ifconfig ${mdb_bridge} addm ${mdb_sender_epair}a
+ ifconfig ${mdb_bridge} addm ${mdb_listener_epair}a
+ ifconfig ${mdb_bridge} addm ${mdb_idle_epair}a
+}
+
+atf_test_case "mdb_vnet_topology" "cleanup"
+mdb_vnet_topology_head()
+{
+ atf_set descr 'bridge multicast database VNET topology scaffold'
+ atf_set require.user root
+}
+
+mdb_vnet_topology_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ bridge_mdb_vnet_topology_create
+
+ atf_check -s exit:0 -o inline:"3\n" \
+ sh -c "ifconfig ${mdb_bridge} | grep member: | wc -l | xargs"
+ atf_check -s exit:0 -o ignore \
+ jexec mdbs ping -c 1 -t 1 192.0.2.2
+ atf_check -s exit:0 -o ignore \
+ jexec mdbs ping -c 1 -t 1 192.0.2.3
+}
+
+mdb_vnet_topology_cleanup()
+{
+ vnet_cleanup
+}
+
atf_test_case "member_ifaddrs_enabled" "cleanup"
member_ifaddrs_enabled_head()
{
@@ -1507,6 +1559,7 @@
atf_add_test_case "vlan"
atf_add_test_case "many_bridge_members"
atf_add_test_case "group_fwd_mask"
+ atf_add_test_case "mdb_vnet_topology"
atf_add_test_case "member_ifaddrs_enabled"
atf_add_test_case "member_ifaddrs_disabled"
atf_add_test_case "member_ifaddrs_vlan"

File Metadata

Mime Type
text/plain
Expires
Fri, Aug 21, 4:55 AM (21 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37021579
Default Alt Text
D58730.diff (12 KB)

Event Timeline