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().
This commit is contained in:
parent
481ff5ace4
commit
1fd1b49685
@ -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 @@ __KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.64 2009/01/18 10:28:55 mrg Exp $");
|
||||
#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 int bridge_init(struct ifnet *);
|
||||
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 @@ bridge_clone_create(struct if_clone *ifc, int unit)
|
||||
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 @@ bridge_clone_create(struct if_clone *ifc, int unit)
|
||||
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 @@ bridge_clone_destroy(struct ifnet *ifp)
|
||||
/* Tear down the routing table. */
|
||||
bridge_rtable_fini(sc);
|
||||
|
||||
|
||||
|
||||
softint_disestablish(sc->sc_softintr);
|
||||
|
||||
free(sc, M_DEVBUF);
|
||||
|
||||
return (0);
|
||||
@ -1305,124 +1318,139 @@ bridge_start(struct ifnet *ifp)
|
||||
* 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;
|
||||
|
||||
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);
|
||||
if ((sc->sc_if.if_flags & IFF_RUNNING) == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (bif->bif_flags & IFBIF_STP) {
|
||||
switch (bif->bif_state) {
|
||||
case BSTP_IFSTATE_BLOCKING:
|
||||
case BSTP_IFSTATE_LISTENING:
|
||||
case BSTP_IFSTATE_DISABLED:
|
||||
s = splbio();
|
||||
while (1) {
|
||||
IFQ_POLL(&sc->sc_if.if_snd, m);
|
||||
if (m == NULL)
|
||||
break;
|
||||
IFQ_DEQUEUE(&sc->sc_if.if_snd, m);
|
||||
|
||||
src_if = m->m_pkthdr.rcvif;
|
||||
|
||||
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 (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 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);
|
||||
}
|
||||
eh = mtod(m, struct ether_header *);
|
||||
|
||||
if ((bif->bif_flags & IFBIF_STP) != 0 &&
|
||||
bif->bif_state == BSTP_IFSTATE_LEARNING) {
|
||||
m_freem(m);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
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 (bif->bif_flags & IFBIF_STP) {
|
||||
switch (bif->bif_state) {
|
||||
case BSTP_IFSTATE_DISABLED:
|
||||
case BSTP_IFSTATE_BLOCKING:
|
||||
m_freem(m);
|
||||
return;
|
||||
if (dst_if == NULL) {
|
||||
bridge_broadcast(sc, src_if, m);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
bridge_enqueue(sc, dst_if, m, 1);
|
||||
/*
|
||||
* 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);
|
||||
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);
|
||||
}
|
||||
splx(s);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1430,6 +1458,7 @@ bridge_forward(struct bridge_softc *sc, struct mbuf *m)
|
||||
*
|
||||
* 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 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
|
||||
* 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 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
|
||||
}
|
||||
|
||||
/* 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 @@ bridge_ipf(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
|
||||
/*
|
||||
* Check basic packet sanity and run IPF through pfil.
|
||||
*/
|
||||
KASSERT(!cpu_intr_p());
|
||||
switch (ether_type)
|
||||
{
|
||||
case ETHERTYPE_IP :
|
||||
|
@ -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 @@ struct bridge_softc {
|
||||
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[];
|
||||
|
Loading…
x
Reference in New Issue
Block a user