Module Name: src
Committed By: bouyer
Date: Sat Apr 4 10:00:23 UTC 2009
Modified Files:
src/sys/net: if_bridge.c if_bridgevar.h
Log Message:
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.64 -r1.65 src/sys/net/if_bridge.c
cvs rdiff -u -r1.13 -r1.14 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.64 src/sys/net/if_bridge.c:1.65
--- src/sys/net/if_bridge.c:1.64 Sun Jan 18 10:28:55 2009
+++ src/sys/net/if_bridge.c Sat Apr 4 10:00:23 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: if_bridge.c,v 1.64 2009/01/18 10:28:55 mrg Exp $ */
+/* $NetBSD: if_bridge.c,v 1.65 2009/04/04 10:00:23 bouyer Exp $ */
/*
* Copyright 2001 Wasabi Systems, Inc.
@@ -80,7 +80,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.64 2009/01/18 10:28:55 mrg Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.65 2009/04/04 10:00:23 bouyer 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);
@@ -1305,124 +1318,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);
}
/*
@@ -1430,6 +1458,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)
@@ -1471,12 +1500,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);
@@ -1524,7 +1558,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);
}
@@ -2010,6 +2050,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.13 src/sys/net/if_bridgevar.h:1.14
--- src/sys/net/if_bridgevar.h:1.13 Sun Jan 18 10:28:55 2009
+++ src/sys/net/if_bridgevar.h Sat Apr 4 10:00:23 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: if_bridgevar.h,v 1.13 2009/01/18 10:28:55 mrg Exp $ */
+/* $NetBSD: if_bridgevar.h,v 1.14 2009/04/04 10:00:23 bouyer Exp $ */
/*
* Copyright 2001 Wasabi Systems, Inc.
@@ -301,6 +301,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[];