Module Name:    src
Committed By:   snj
Date:           Sat Apr  4 18:00:03 UTC 2009

Modified Files:
        src/sys/net [netbsd-5]: if_bridge.c if_bridgevar.h

Log Message:
Pull up following revision(s) (requested by bouyer in ticket #660):
        sys/net/if_bridge.c: revision 1.65
        sys/net/if_bridgevar.h: revision 1.14
Fix for if_start() and pfil_hook() being called from hardware interrupt
context (reported on various mailing-lists, and part of PR kern/41114,
causing panic in pf(4) and possibly ipf(4) when BRIDGE_IPF is used).
Defer bridge_forward() to a software interrupt; bridge_input() enqueues
mbufs to ifp->if_snd which is handled in bridge_forward().


To generate a diff of this commit:
cvs rdiff -u -r1.62 -r1.62.6.1 src/sys/net/if_bridge.c
cvs rdiff -u -r1.11 -r1.11.46.1 src/sys/net/if_bridgevar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if_bridge.c
diff -u src/sys/net/if_bridge.c:1.62 src/sys/net/if_bridge.c:1.62.6.1
--- src/sys/net/if_bridge.c:1.62	Sun Jun 15 16:37:21 2008
+++ src/sys/net/if_bridge.c	Sat Apr  4 18:00:03 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_bridge.c,v 1.62 2008/06/15 16:37:21 christos Exp $	*/
+/*	$NetBSD: if_bridge.c,v 1.62.6.1 2009/04/04 18:00:03 snj Exp $	*/
 
 /*
  * Copyright 2001 Wasabi Systems, Inc.
@@ -80,7 +80,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.62 2008/06/15 16:37:21 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.62.6.1 2009/04/04 18:00:03 snj Exp $");
 
 #include "opt_bridge_ipf.h"
 #include "opt_inet.h"
@@ -97,6 +97,7 @@
 #include <sys/proc.h>
 #include <sys/pool.h>
 #include <sys/kauth.h>
+#include <sys/cpu.h>
 
 #if NBPFILTER > 0
 #include <net/bpf.h>
@@ -185,7 +186,7 @@
 static void	bridge_stop(struct ifnet *, int);
 static void	bridge_start(struct ifnet *);
 
-static void	bridge_forward(struct bridge_softc *, struct mbuf *m);
+static void	bridge_forward(void *);
 
 static void	bridge_timer(void *);
 
@@ -350,6 +351,13 @@
 	sc->sc_hold_time = BSTP_DEFAULT_HOLD_TIME;
 	sc->sc_filter_flags = 0;
 
+	/* software interrupt to do the work */
+	sc->sc_softintr = softint_establish(SOFTINT_NET, bridge_forward, sc);
+	if (sc->sc_softintr == NULL) {
+		free(sc, M_DEVBUF);
+		return ENOMEM;
+	}
+
 	/* Initialize our routing table. */
 	bridge_rtable_init(sc);
 
@@ -370,6 +378,7 @@
 	ifp->if_addrlen = 0;
 	ifp->if_dlt = DLT_EN10MB;
 	ifp->if_hdrlen = ETHER_HDR_LEN;
+	IFQ_SET_READY(&ifp->if_snd);
 
 	if_attach(ifp);
 
@@ -410,6 +419,10 @@
 	/* Tear down the routing table. */
 	bridge_rtable_fini(sc);
 
+
+
+	softint_disestablish(sc->sc_softintr);
+
 	free(sc, M_DEVBUF);
 
 	return (0);
@@ -1298,124 +1311,139 @@
  *	The forwarding function of the bridge.
  */
 static void
-bridge_forward(struct bridge_softc *sc, struct mbuf *m)
+bridge_forward(void *v)
 {
+	struct bridge_softc *sc = v;
+	struct mbuf *m;
 	struct bridge_iflist *bif;
 	struct ifnet *src_if, *dst_if;
 	struct ether_header *eh;
+	int s;
 
-	src_if = m->m_pkthdr.rcvif;
+	if ((sc->sc_if.if_flags & IFF_RUNNING) == 0)
+		return;
 
-	sc->sc_if.if_ipackets++;
-	sc->sc_if.if_ibytes += m->m_pkthdr.len;
+	s = splbio();
+	while (1) {
+		IFQ_POLL(&sc->sc_if.if_snd, m);
+		if (m == NULL)
+			break;
+		IFQ_DEQUEUE(&sc->sc_if.if_snd, m);
 
-	/*
-	 * Look up the bridge_iflist.
-	 */
-	bif = bridge_lookup_member_if(sc, src_if);
-	if (bif == NULL) {
-		/* Interface is not a bridge member (anymore?) */
-		m_freem(m);
-		return;
-	}
+		src_if = m->m_pkthdr.rcvif;
 
-	if (bif->bif_flags & IFBIF_STP) {
-		switch (bif->bif_state) {
-		case BSTP_IFSTATE_BLOCKING:
-		case BSTP_IFSTATE_LISTENING:
-		case BSTP_IFSTATE_DISABLED:
+		sc->sc_if.if_ipackets++;
+		sc->sc_if.if_ibytes += m->m_pkthdr.len;
+
+		/*
+		 * Look up the bridge_iflist.
+		 */
+		bif = bridge_lookup_member_if(sc, src_if);
+		if (bif == NULL) {
+			/* Interface is not a bridge member (anymore?) */
 			m_freem(m);
-			return;
+			continue;
 		}
-	}
 
-	eh = mtod(m, struct ether_header *);
-
-	/*
-	 * If the interface is learning, and the source
-	 * address is valid and not multicast, record
-	 * the address.
-	 */
-	if ((bif->bif_flags & IFBIF_LEARNING) != 0 &&
-	    ETHER_IS_MULTICAST(eh->ether_shost) == 0 &&
-	    (eh->ether_shost[0] == 0 &&
-	     eh->ether_shost[1] == 0 &&
-	     eh->ether_shost[2] == 0 &&
-	     eh->ether_shost[3] == 0 &&
-	     eh->ether_shost[4] == 0 &&
-	     eh->ether_shost[5] == 0) == 0) {
-		(void) bridge_rtupdate(sc, eh->ether_shost,
-		    src_if, 0, IFBAF_DYNAMIC);
-	}
+		if (bif->bif_flags & IFBIF_STP) {
+			switch (bif->bif_state) {
+			case BSTP_IFSTATE_BLOCKING:
+			case BSTP_IFSTATE_LISTENING:
+			case BSTP_IFSTATE_DISABLED:
+				m_freem(m);
+				continue;
+			}
+		}
 
-	if ((bif->bif_flags & IFBIF_STP) != 0 &&
-	    bif->bif_state == BSTP_IFSTATE_LEARNING) {
-		m_freem(m);
-		return;
-	}
+		eh = mtod(m, struct ether_header *);
 
-	/*
-	 * At this point, the port either doesn't participate
-	 * in spanning tree or it is in the forwarding state.
-	 */
+		/*
+		 * If the interface is learning, and the source
+		 * address is valid and not multicast, record
+		 * the address.
+		 */
+		if ((bif->bif_flags & IFBIF_LEARNING) != 0 &&
+		    ETHER_IS_MULTICAST(eh->ether_shost) == 0 &&
+		    (eh->ether_shost[0] == 0 &&
+		     eh->ether_shost[1] == 0 &&
+		     eh->ether_shost[2] == 0 &&
+		     eh->ether_shost[3] == 0 &&
+		     eh->ether_shost[4] == 0 &&
+		     eh->ether_shost[5] == 0) == 0) {
+			(void) bridge_rtupdate(sc, eh->ether_shost,
+			    src_if, 0, IFBAF_DYNAMIC);
+		}
 
-	/*
-	 * If the packet is unicast, destined for someone on
-	 * "this" side of the bridge, drop it.
-	 */
-	if ((m->m_flags & (M_BCAST|M_MCAST)) == 0) {
-		dst_if = bridge_rtlookup(sc, eh->ether_dhost);
-		if (src_if == dst_if) {
+		if ((bif->bif_flags & IFBIF_STP) != 0 &&
+		    bif->bif_state == BSTP_IFSTATE_LEARNING) {
 			m_freem(m);
-			return;
+			continue;
+		}
+
+		/*
+		 * At this point, the port either doesn't participate
+		 * in spanning tree or it is in the forwarding state.
+		 */
+
+		/*
+		 * If the packet is unicast, destined for someone on
+		 * "this" side of the bridge, drop it.
+		 */
+		if ((m->m_flags & (M_BCAST|M_MCAST)) == 0) {
+			dst_if = bridge_rtlookup(sc, eh->ether_dhost);
+			if (src_if == dst_if) {
+				m_freem(m);
+				continue;
+			}
+		} else {
+			/* ...forward it to all interfaces. */
+			sc->sc_if.if_imcasts++;
+			dst_if = NULL;
 		}
-	} else {
-		/* ...forward it to all interfaces. */
-		sc->sc_if.if_imcasts++;
-		dst_if = NULL;
-	}
 
 #ifdef PFIL_HOOKS
-	if (pfil_run_hooks(&sc->sc_if.if_pfil, &m,
-	    m->m_pkthdr.rcvif, PFIL_IN) != 0) {
-		if (m != NULL)
-			m_freem(m);
-		return;
-	}
-	if (m == NULL)
-		return;
+		if (pfil_run_hooks(&sc->sc_if.if_pfil, &m,
+		    m->m_pkthdr.rcvif, PFIL_IN) != 0) {
+			if (m != NULL)
+				m_freem(m);
+			continue;
+		}
+		if (m == NULL)
+			continue;
 #endif /* PFIL_HOOKS */
 
-	if (dst_if == NULL) {
-		bridge_broadcast(sc, src_if, m);
-		return;
-	}
-
-	/*
-	 * At this point, we're dealing with a unicast frame
-	 * going to a different interface.
-	 */
-	if ((dst_if->if_flags & IFF_RUNNING) == 0) {
-		m_freem(m);
-		return;
-	}
-	bif = bridge_lookup_member_if(sc, dst_if);
-	if (bif == NULL) {
-		/* Not a member of the bridge (anymore?) */
-		m_freem(m);
-		return;
-	}
+		if (dst_if == NULL) {
+			bridge_broadcast(sc, src_if, m);
+			continue;
+		}
 
-	if (bif->bif_flags & IFBIF_STP) {
-		switch (bif->bif_state) {
-		case BSTP_IFSTATE_DISABLED:
-		case BSTP_IFSTATE_BLOCKING:
+		/*
+		 * At this point, we're dealing with a unicast frame
+		 * going to a different interface.
+		 */
+		if ((dst_if->if_flags & IFF_RUNNING) == 0) {
 			m_freem(m);
-			return;
+			continue;
+		}
+		bif = bridge_lookup_member_if(sc, dst_if);
+		if (bif == NULL) {
+			/* Not a member of the bridge (anymore?) */
+			m_freem(m);
+			continue;
+		}
+
+		if (bif->bif_flags & IFBIF_STP) {
+			switch (bif->bif_state) {
+			case BSTP_IFSTATE_DISABLED:
+			case BSTP_IFSTATE_BLOCKING:
+				m_freem(m);
+				continue;
+			}
 		}
-	}
 
-	bridge_enqueue(sc, dst_if, m, 1);
+		bridge_enqueue(sc, dst_if, m, 1);
+	}
+	splx(s);
 }
 
 /*
@@ -1423,6 +1451,7 @@
  *
  *	Receive input from a member interface.  Queue the packet for
  *	bridging if it is not for us.
+ *	should be called at splbio()
  */
 struct mbuf *
 bridge_input(struct ifnet *ifp, struct mbuf *m)
@@ -1464,12 +1493,17 @@
 		 * for bridge processing; return the original packet for
 		 * local processing.
 		 */
+		if (IF_QFULL(&sc->sc_if.if_snd)) {
+			IF_DROP(&sc->sc_if.if_snd);
+			return (m);
+		}
 		mc = m_dup(m, 0, M_COPYALL, M_NOWAIT);
 		if (mc == NULL)
 			return (m);
 
 		/* Perform the bridge forwarding function with the copy. */
-		bridge_forward(sc, mc);
+		IF_ENQUEUE(&sc->sc_if.if_snd, mc);
+		softint_schedule(sc->sc_softintr);
 
 		/* Return the original packet for local processing. */
 		return (m);
@@ -1517,7 +1551,13 @@
 	}
 
 	/* Perform the bridge forwarding function. */
-	bridge_forward(sc, m);
+	if (IF_QFULL(&sc->sc_if.if_snd)) {
+		IF_DROP(&sc->sc_if.if_snd);
+		m_freem(m);
+		return (NULL);
+	}
+	IF_ENQUEUE(&sc->sc_if.if_snd, m);
+	softint_schedule(sc->sc_softintr);
 
 	return (NULL);
 }
@@ -2003,6 +2043,7 @@
 	/*
 	 * Check basic packet sanity and run IPF through pfil.
 	 */
+	KASSERT(!cpu_intr_p());
 	switch (ether_type)
 	{
 	case ETHERTYPE_IP :

Index: src/sys/net/if_bridgevar.h
diff -u src/sys/net/if_bridgevar.h:1.11 src/sys/net/if_bridgevar.h:1.11.46.1
--- src/sys/net/if_bridgevar.h:1.11	Mon Jul  9 21:11:00 2007
+++ src/sys/net/if_bridgevar.h	Sat Apr  4 18:00:03 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_bridgevar.h,v 1.11 2007/07/09 21:11:00 ad Exp $	*/
+/*	$NetBSD: if_bridgevar.h,v 1.11.46.1 2009/04/04 18:00:03 snj Exp $	*/
 
 /*
  * Copyright 2001 Wasabi Systems, Inc.
@@ -300,6 +300,7 @@
 	LIST_HEAD(, bridge_rtnode) sc_rtlist;	/* list version of above */
 	uint32_t		sc_rthash_key;	/* key for hash */
 	uint32_t		sc_filter_flags; /* ipf and flags */
+	void			*sc_softintr;
 };
 
 extern const uint8_t bstp_etheraddr[];

Reply via email to