Bug 164369

Summary: [if_bridge] [patch] two STP bridges have the same id
Product: Base System Reporter: nvass
Component: kernAssignee: Adrian Chadd <adrian>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 10.0-CURRENT   
Hardware: Any   
OS: Any   
Attachments:
Description Flags
file.diff
none
bridge-stpmac2.diff none

Description nvass 2012-01-22 11:30:11 UTC
The current code in STP selects the id of a bridge from all available ethernet ifnets regardless if they are part of the said bridge. This is problematic when more than one STP bridges exist, that is, more than one bridges will have the same bridge id.

Fix: The MAC address candidates for the bridge id should be only from the bridge's members

Patch attached with submission follows:
How-To-Repeat: ifconfig bridge0 create
ifconfig bridge1 create
ifconfig bridge0 addm em0 addm em1 stp em0 stp em1
ifconfig bridge1 addm em2 addm em3 stp em2 stp em3

The resulting bridges are:
bridge0: flags=8802<BROADCAST,SIMPLEX,MULTICAST> metric 0 mtu 1500
        ether 02:46:61:bb:95:00
        nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
        id 08:00:27:0f:88:a5 priority 32768 hellotime 2 fwddelay 15
        maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200
        root id 08:00:27:0f:88:a5 priority 32768 ifcost 0 port 0
        member: em1 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP>
                ifmaxaddr 0 port 3 priority 128 path cost 20000 proto rstp
                role designated state discarding
        member: em0 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP>
                ifmaxaddr 0 port 1 priority 128 path cost 20000 proto rstp
                role designated state discarding

bridge1: flags=8802<BROADCAST,SIMPLEX,MULTICAST> metric 0 mtu 1500
        ether 02:46:61:bb:95:01
        nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
        id 08:00:27:0f:88:a5 priority 32768 hellotime 2 fwddelay 15
        maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200
        root id 08:00:27:0f:88:a5 priority 32768 ifcost 0 port 0
        member: em3 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP>
                ifmaxaddr 0 port 5 priority 128 path cost 20000 proto rstp
                role designated state discarding
        member: em2 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP>
                ifmaxaddr 0 port 4 priority 128 path cost 20000 proto rstp
                role designated state discarding

Both have the same id
Comment 1 Mark Linimon freebsd_committer freebsd_triage 2012-01-22 18:35:36 UTC
Responsible Changed
From-To: freebsd-bugs->freebsd-net
Comment 2 Adrian Chadd freebsd_committer freebsd_triage 2012-01-22 19:48:49 UTC
Responsible Changed
From-To: freebsd-net->adrian

I'll poke this.
Comment 3 Andrew Thompson freebsd_committer freebsd_triage 2012-02-23 00:43:28 UTC
Hi Nikos,


I have had a look at this patch. I originally ended up with the same
change as OpenBSD made but I realised why you did it differently, to
include all bridge ports and not just those with stp enabled.

Here is a different version of your change, while it is more churn I
think it ends up with more readable code. If the bridge port list is
empty we jump straight out and avoid looping when 'bp' is always null.



regards,
Andrew
Comment 4 nvass 2012-02-24 11:30:48 UTC
On 2/23/2012 1:43 AM, Andrew Thompson wrote:
> Hi Nikos,

Hi Andrew,

> I have had a look at this patch. I originally ended up with the same
> change as OpenBSD made but I realised why you did it differently, to
> include all bridge ports and not just those with stp enabled.
>
> Here is a different version of your change, while it is more churn I
> think it ends up with more readable code. If the bridge port list is
> empty we jump straight out and avoid looping when 'bp' is always null.

I just tested your patch and it works as wanted. Could you please
commit it?

I would prefer the bridge id to be sticky, that is:
Get the bridge id from the first ethernet added to
the bridge and stick to it while it is a member of
the bridge. This way there is going to be one topology
change when the bridge enters the STP domain. If
the said interface leaves the bridge(unlikely), the
bridge will have to change its ID to another one.

But I don't have time to look at it at the moment:(

Plus the new behavior is good enough for me...

Thanks, Nikos
Comment 5 dfilter service freebsd_committer freebsd_triage 2012-02-24 17:51:02 UTC
Author: thompsa
Date: Fri Feb 24 17:50:36 2012
New Revision: 232118
URL: http://svn.freebsd.org/changeset/base/232118

Log:
  Only look for a usable MAC address for the bridge ID from ports within our
  bridge, this allows us to have more than one independent bridge in the same
  STP domain.
  
  PR:		kern/164369
  Submitted by:	Nikos Vassiliadis (earlier version)
  MFC after:	2 weeks

Modified:
  head/sys/net/bridgestp.c

Modified: head/sys/net/bridgestp.c
==============================================================================
--- head/sys/net/bridgestp.c	Fri Feb 24 17:50:23 2012	(r232117)
+++ head/sys/net/bridgestp.c	Fri Feb 24 17:50:36 2012	(r232118)
@@ -2013,24 +2013,33 @@ bstp_reinit(struct bstp_state *bs)
 	struct bstp_port *bp;
 	struct ifnet *ifp, *mif;
 	u_char *e_addr;
+	void *bridgeptr;
 	static const u_char llzero[ETHER_ADDR_LEN];	/* 00:00:00:00:00:00 */
 
 	BSTP_LOCK_ASSERT(bs);
 
+	if (LIST_EMPTY(&bs->bs_bplist))
+		goto disablestp;
+
 	mif = NULL;
+	bridgeptr = LIST_FIRST(&bs->bs_bplist)->bp_ifp->if_bridge;
+	KASSERT(bridgeptr != NULL, ("Invalid bridge pointer"));
 	/*
 	 * Search through the Ethernet adapters and find the one with the
-	 * lowest value. The adapter which we take the MAC address from does
-	 * not need to be part of the bridge, it just needs to be a unique
-	 * value.
+	 * lowest value. Make sure the adapter which we take the MAC address
+	 * from is part of this bridge, so we can have more than one independent
+	 * bridges in the same STP domain.
 	 */
 	IFNET_RLOCK_NOSLEEP();
 	TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
 		if (ifp->if_type != IFT_ETHER)
-			continue;
+			continue;	/* Not Ethernet */
+
+		if (ifp->if_bridge != bridgeptr)
+			continue;	/* Not part of our bridge */
 
 		if (bstp_addr_cmp(IF_LLADDR(ifp), llzero) == 0)
-			continue;
+			continue;	/* No mac address set */
 
 		if (mif == NULL) {
 			mif = ifp;
@@ -2042,21 +2051,8 @@ bstp_reinit(struct bstp_state *bs)
 		}
 	}
 	IFNET_RUNLOCK_NOSLEEP();
-
-	if (LIST_EMPTY(&bs->bs_bplist) || mif == NULL) {
-		/* Set the bridge and root id (lower bits) to zero */
-		bs->bs_bridge_pv.pv_dbridge_id =
-		    ((uint64_t)bs->bs_bridge_priority) << 48;
-		bs->bs_bridge_pv.pv_root_id = bs->bs_bridge_pv.pv_dbridge_id;
-		bs->bs_root_pv = bs->bs_bridge_pv;
-		/* Disable any remaining ports, they will have no MAC address */
-		LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
-			bp->bp_infois = BSTP_INFO_DISABLED;
-			bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
-		}
-		callout_stop(&bs->bs_bstpcallout);
-		return;
-	}
+	if (mif == NULL)
+		goto disablestp;
 
 	e_addr = IF_LLADDR(mif);
 	bs->bs_bridge_pv.pv_dbridge_id =
@@ -2084,6 +2080,20 @@ bstp_reinit(struct bstp_state *bs)
 
 	bstp_assign_roles(bs);
 	bstp_timer_start(&bs->bs_link_timer, BSTP_LINK_TIMER);
+	return;
+
+disablestp:
+	/* Set the bridge and root id (lower bits) to zero */
+	bs->bs_bridge_pv.pv_dbridge_id =
+	    ((uint64_t)bs->bs_bridge_priority) << 48;
+	bs->bs_bridge_pv.pv_root_id = bs->bs_bridge_pv.pv_dbridge_id;
+	bs->bs_root_pv = bs->bs_bridge_pv;
+	/* Disable any remaining ports, they will have no MAC address */
+	LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
+		bp->bp_infois = BSTP_INFO_DISABLED;
+		bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
+	}
+	callout_stop(&bs->bs_bstpcallout);
 }
 
 static int
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
Comment 6 nvass 2012-03-09 18:55:42 UTC
Could you close this PR?
Comment 7 dfilter service freebsd_committer freebsd_triage 2012-05-26 08:35:56 UTC
Author: thompsa
Date: Sat May 26 07:35:44 2012
New Revision: 236048
URL: http://svn.freebsd.org/changeset/base/236048

Log:
  MFC r232118
  
   Only look for a usable MAC address for the bridge ID from ports within our
   bridge, this allows us to have more than one independent bridge in the same
   STP domain.
  
  PR:		kern/164369
  Submitted by:	Nikos Vassiliadis (earlier version)

Modified:
  stable/9/sys/net/bridgestp.c
Directory Properties:
  stable/9/sys/   (props changed)
  stable/9/sys/amd64/include/xen/   (props changed)
  stable/9/sys/boot/   (props changed)
  stable/9/sys/boot/i386/efi/   (props changed)
  stable/9/sys/boot/ia64/efi/   (props changed)
  stable/9/sys/boot/ia64/ski/   (props changed)
  stable/9/sys/boot/powerpc/boot1.chrp/   (props changed)
  stable/9/sys/boot/powerpc/ofw/   (props changed)
  stable/9/sys/cddl/contrib/opensolaris/   (props changed)
  stable/9/sys/conf/   (props changed)
  stable/9/sys/contrib/dev/acpica/   (props changed)
  stable/9/sys/contrib/octeon-sdk/   (props changed)
  stable/9/sys/contrib/pf/   (props changed)
  stable/9/sys/contrib/x86emu/   (props changed)
  stable/9/sys/dev/   (props changed)
  stable/9/sys/dev/e1000/   (props changed)
  stable/9/sys/dev/ixgbe/   (props changed)
  stable/9/sys/fs/   (props changed)
  stable/9/sys/fs/ntfs/   (props changed)
  stable/9/sys/modules/   (props changed)

Modified: stable/9/sys/net/bridgestp.c
==============================================================================
--- stable/9/sys/net/bridgestp.c	Sat May 26 07:34:46 2012	(r236047)
+++ stable/9/sys/net/bridgestp.c	Sat May 26 07:35:44 2012	(r236048)
@@ -2013,24 +2013,33 @@ bstp_reinit(struct bstp_state *bs)
 	struct bstp_port *bp;
 	struct ifnet *ifp, *mif;
 	u_char *e_addr;
+	void *bridgeptr;
 	static const u_char llzero[ETHER_ADDR_LEN];	/* 00:00:00:00:00:00 */
 
 	BSTP_LOCK_ASSERT(bs);
 
+	if (LIST_EMPTY(&bs->bs_bplist))
+		goto disablestp;
+
 	mif = NULL;
+	bridgeptr = LIST_FIRST(&bs->bs_bplist)->bp_ifp->if_bridge;
+	KASSERT(bridgeptr != NULL, ("Invalid bridge pointer"));
 	/*
 	 * Search through the Ethernet adapters and find the one with the
-	 * lowest value. The adapter which we take the MAC address from does
-	 * not need to be part of the bridge, it just needs to be a unique
-	 * value.
+	 * lowest value. Make sure the adapter which we take the MAC address
+	 * from is part of this bridge, so we can have more than one independent
+	 * bridges in the same STP domain.
 	 */
 	IFNET_RLOCK_NOSLEEP();
 	TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
 		if (ifp->if_type != IFT_ETHER)
-			continue;
+			continue;	/* Not Ethernet */
+
+		if (ifp->if_bridge != bridgeptr)
+			continue;	/* Not part of our bridge */
 
 		if (bstp_addr_cmp(IF_LLADDR(ifp), llzero) == 0)
-			continue;
+			continue;	/* No mac address set */
 
 		if (mif == NULL) {
 			mif = ifp;
@@ -2042,21 +2051,8 @@ bstp_reinit(struct bstp_state *bs)
 		}
 	}
 	IFNET_RUNLOCK_NOSLEEP();
-
-	if (LIST_EMPTY(&bs->bs_bplist) || mif == NULL) {
-		/* Set the bridge and root id (lower bits) to zero */
-		bs->bs_bridge_pv.pv_dbridge_id =
-		    ((uint64_t)bs->bs_bridge_priority) << 48;
-		bs->bs_bridge_pv.pv_root_id = bs->bs_bridge_pv.pv_dbridge_id;
-		bs->bs_root_pv = bs->bs_bridge_pv;
-		/* Disable any remaining ports, they will have no MAC address */
-		LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
-			bp->bp_infois = BSTP_INFO_DISABLED;
-			bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
-		}
-		callout_stop(&bs->bs_bstpcallout);
-		return;
-	}
+	if (mif == NULL)
+		goto disablestp;
 
 	e_addr = IF_LLADDR(mif);
 	bs->bs_bridge_pv.pv_dbridge_id =
@@ -2084,6 +2080,20 @@ bstp_reinit(struct bstp_state *bs)
 
 	bstp_assign_roles(bs);
 	bstp_timer_start(&bs->bs_link_timer, BSTP_LINK_TIMER);
+	return;
+
+disablestp:
+	/* Set the bridge and root id (lower bits) to zero */
+	bs->bs_bridge_pv.pv_dbridge_id =
+	    ((uint64_t)bs->bs_bridge_priority) << 48;
+	bs->bs_bridge_pv.pv_root_id = bs->bs_bridge_pv.pv_dbridge_id;
+	bs->bs_root_pv = bs->bs_bridge_pv;
+	/* Disable any remaining ports, they will have no MAC address */
+	LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
+		bp->bp_infois = BSTP_INFO_DISABLED;
+		bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
+	}
+	callout_stop(&bs->bs_bstpcallout);
 }
 
 static int
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
Comment 8 dfilter service freebsd_committer freebsd_triage 2012-05-26 08:59:32 UTC
Author: thompsa
Date: Sat May 26 07:58:58 2012
New Revision: 236056
URL: http://svn.freebsd.org/changeset/base/236056

Log:
  MFC r232118
  
   Only look for a usable MAC address for the bridge ID from ports within our
   bridge, this allows us to have more than one independent bridge in the same
   STP domain.
  
  PR:		kern/164369
  Submitted by:	Nikos Vassiliadis (earlier version)

Modified:
  stable/8/sys/net/bridgestp.c
Directory Properties:
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/boot/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)
  stable/8/sys/dev/e1000/   (props changed)

Modified: stable/8/sys/net/bridgestp.c
==============================================================================
--- stable/8/sys/net/bridgestp.c	Sat May 26 07:58:12 2012	(r236055)
+++ stable/8/sys/net/bridgestp.c	Sat May 26 07:58:58 2012	(r236056)
@@ -2013,24 +2013,33 @@ bstp_reinit(struct bstp_state *bs)
 	struct bstp_port *bp;
 	struct ifnet *ifp, *mif;
 	u_char *e_addr;
+	void *bridgeptr;
 	static const u_char llzero[ETHER_ADDR_LEN];	/* 00:00:00:00:00:00 */
 
 	BSTP_LOCK_ASSERT(bs);
 
+	if (LIST_EMPTY(&bs->bs_bplist))
+		goto disablestp;
+
 	mif = NULL;
+	bridgeptr = LIST_FIRST(&bs->bs_bplist)->bp_ifp->if_bridge;
+	KASSERT(bridgeptr != NULL, ("Invalid bridge pointer"));
 	/*
 	 * Search through the Ethernet adapters and find the one with the
-	 * lowest value. The adapter which we take the MAC address from does
-	 * not need to be part of the bridge, it just needs to be a unique
-	 * value.
+	 * lowest value. Make sure the adapter which we take the MAC address
+	 * from is part of this bridge, so we can have more than one independent
+	 * bridges in the same STP domain.
 	 */
 	IFNET_RLOCK_NOSLEEP();
 	TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
 		if (ifp->if_type != IFT_ETHER)
-			continue;
+			continue;	/* Not Ethernet */
+
+		if (ifp->if_bridge != bridgeptr)
+			continue;	/* Not part of our bridge */
 
 		if (bstp_addr_cmp(IF_LLADDR(ifp), llzero) == 0)
-			continue;
+			continue;	/* No mac address set */
 
 		if (mif == NULL) {
 			mif = ifp;
@@ -2042,21 +2051,8 @@ bstp_reinit(struct bstp_state *bs)
 		}
 	}
 	IFNET_RUNLOCK_NOSLEEP();
-
-	if (LIST_EMPTY(&bs->bs_bplist) || mif == NULL) {
-		/* Set the bridge and root id (lower bits) to zero */
-		bs->bs_bridge_pv.pv_dbridge_id =
-		    ((uint64_t)bs->bs_bridge_priority) << 48;
-		bs->bs_bridge_pv.pv_root_id = bs->bs_bridge_pv.pv_dbridge_id;
-		bs->bs_root_pv = bs->bs_bridge_pv;
-		/* Disable any remaining ports, they will have no MAC address */
-		LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
-			bp->bp_infois = BSTP_INFO_DISABLED;
-			bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
-		}
-		callout_stop(&bs->bs_bstpcallout);
-		return;
-	}
+	if (mif == NULL)
+		goto disablestp;
 
 	e_addr = IF_LLADDR(mif);
 	bs->bs_bridge_pv.pv_dbridge_id =
@@ -2084,6 +2080,20 @@ bstp_reinit(struct bstp_state *bs)
 
 	bstp_assign_roles(bs);
 	bstp_timer_start(&bs->bs_link_timer, BSTP_LINK_TIMER);
+	return;
+
+disablestp:
+	/* Set the bridge and root id (lower bits) to zero */
+	bs->bs_bridge_pv.pv_dbridge_id =
+	    ((uint64_t)bs->bs_bridge_priority) << 48;
+	bs->bs_bridge_pv.pv_root_id = bs->bs_bridge_pv.pv_dbridge_id;
+	bs->bs_root_pv = bs->bs_bridge_pv;
+	/* Disable any remaining ports, they will have no MAC address */
+	LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
+		bp->bp_infois = BSTP_INFO_DISABLED;
+		bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
+	}
+	callout_stop(&bs->bs_bstpcallout);
 }
 
 static int
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
Comment 9 nvass 2016-11-23 00:08:12 UTC
This was fixed a long time ago