if_bridge.c
来自「嵌入式操作系统ECOS的网络开发包」· C语言 代码 · 共 2,021 行 · 第 1/4 页
C
2,021 行
/*
* Multicast packets get handled a little differently:
* If interface is:
* -link0,-link1 (default) Forward all multicast
* as broadcast.
* -link0,link1 Drop non-IP multicast, forward
* as broadcast IP multicast.
* link0,-link1 Drop IP multicast, forward as
* broadcast non-IP multicast.
* link0,link1 Drop all multicast.
*/
if (m->m_flags & M_MCAST) {
if ((sc->sc_if.if_flags &
(IFF_LINK0 | IFF_LINK1)) ==
(IFF_LINK0 | IFF_LINK1)) {
m_freem(m);
return;
}
if (sc->sc_if.if_flags & IFF_LINK0 &&
ETHERADDR_IS_IP_MCAST(dst)) {
m_freem(m);
return;
}
if (sc->sc_if.if_flags & IFF_LINK1 &&
!ETHERADDR_IS_IP_MCAST(dst)) {
m_freem(m);
return;
}
}
if (ifl->bif_flags & IFBIF_BLOCKNONIP && bridge_blocknonip(&eh, m)) {
m_freem(m);
return;
}
if (SIMPLEQ_FIRST(&ifl->bif_brlin) &&
bridge_filterrule(SIMPLEQ_FIRST(&ifl->bif_brlin), &eh) ==
BRL_ACTION_BLOCK) {
m_freem(m);
return;
}
#if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM))
m = bridge_filter(sc, src_if, &eh, m);
if (m == NULL)
return;
#endif
/*
* If the packet is a multicast or broadcast OR if we don't
* know any better, forward it to all interfaces.
*/
if ((m->m_flags & (M_BCAST | M_MCAST)) || dst_if == NULL) {
sc->sc_if.if_imcasts++;
s = splimp();
bridge_broadcast(sc, src_if, &eh, m);
splx(s);
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;
}
ifl = LIST_FIRST(&sc->sc_iflist);
while (ifl != NULL && ifl->ifp != dst_if)
ifl = LIST_NEXT(ifl, next);
if (SIMPLEQ_FIRST(&ifl->bif_brlout) &&
bridge_filterrule(SIMPLEQ_FIRST(&ifl->bif_brlout), &eh) ==
BRL_ACTION_BLOCK) {
m_freem(m);
return;
}
s = splimp();
if (IF_QFULL(&dst_if->if_snd)) {
sc->sc_if.if_oerrors++;
m_freem(m);
splx(s);
return;
}
sc->sc_if.if_opackets++;
sc->sc_if.if_obytes += m->m_pkthdr.len;
// Also count the bytes in the outgoing interface; normally
// done in if_ethersubr.c but here we bypass that route.
dst_if->if_obytes += m->m_pkthdr.len;
IF_ENQUEUE(&dst_if->if_snd, m);
if ((dst_if->if_flags & IFF_OACTIVE) == 0)
(*dst_if->if_start)(dst_if);
splx(s);
}
/*
* Receive input from an interface. Queue the packet for bridging if its
* not for us, and schedule an interrupt.
*/
struct mbuf *
bridge_input(ifp, eh, m)
struct ifnet *ifp;
struct ether_header *eh;
struct mbuf *m;
{
struct bridge_softc *sc;
int s;
struct bridge_iflist *ifl;
struct arpcom *ac;
struct mbuf *mc;
/*
* Make sure this interface is a bridge member.
*/
if (ifp == NULL || ifp->if_bridge == NULL || m == NULL)
return (m);
if ((m->m_flags & M_PKTHDR) == 0)
panic("bridge_input(): no HDR");
sc = (struct bridge_softc *)ifp->if_bridge;
if ((sc->sc_if.if_flags & IFF_RUNNING) == 0)
return (m);
if (m->m_flags & (M_BCAST | M_MCAST)) {
/*
* make a copy of 'm' with 'eh' tacked on to the
* beginning. Return 'm' for local processing
* and enqueue the copy. Schedule netisr.
*/
mc = m_copym2(m, 0, M_COPYALL, M_NOWAIT);
if (mc == NULL)
return (m);
M_PREPEND(mc, sizeof(struct ether_header), M_DONTWAIT);
if (mc == NULL)
return (m);
bcopy(eh, mtod(mc, caddr_t), sizeof(struct ether_header));
s = splimp();
if (IF_QFULL(&sc->sc_if.if_snd)) {
m_freem(mc);
splx(s);
return (m);
}
IF_ENQUEUE(&sc->sc_if.if_snd, mc);
splx(s);
schednetisr(NETISR_BRIDGE);
return (m);
}
/*
* Unicast, make sure it's not for us.
*/
for (ifl = LIST_FIRST(&sc->sc_iflist);ifl; ifl = LIST_NEXT(ifl,next)) {
if (ifl->ifp->if_type != IFT_ETHER)
continue;
ac = (struct arpcom *)ifl->ifp;
if (bcmp(ac->ac_enaddr, eh->ether_dhost, ETHER_ADDR_LEN) == 0) {
if (ifl->bif_flags & IFBIF_LEARNING)
bridge_rtupdate(sc,
(struct ether_addr *)&eh->ether_shost,
ifp, 0, IFBAF_DYNAMIC);
m->m_pkthdr.rcvif = ifl->ifp;
return (m);
}
if (bcmp(ac->ac_enaddr, eh->ether_shost, ETHER_ADDR_LEN) == 0) {
m_freem(m);
return (NULL);
}
}
M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
if (m == NULL)
return (NULL);
bcopy(eh, mtod(m, caddr_t), sizeof(struct ether_header));
s = splimp();
if (IF_QFULL(&sc->sc_if.if_snd)) {
m_freem(m);
splx(s);
return (NULL);
}
IF_ENQUEUE(&sc->sc_if.if_snd, m);
splx(s);
schednetisr(NETISR_BRIDGE);
return (NULL);
}
/*
* Send a frame to all interfaces that are members of the bridge
* (except the one it came in on). This code assumes that it is
* running at splnet or higher.
*/
void
bridge_broadcast(sc, ifp, eh, m)
struct bridge_softc *sc;
struct ifnet *ifp;
struct ether_header *eh;
struct mbuf *m;
{
struct bridge_iflist *p;
struct mbuf *mc;
int used = 0;
for (p = LIST_FIRST(&sc->sc_iflist); p; p = LIST_NEXT(p, next)) {
/*
* Don't retransmit out of the same interface where
* the packet was received from.
*/
if (p->ifp->if_index == ifp->if_index)
continue;
if ((p->bif_flags & IFBIF_DISCOVER) == 0 &&
(m->m_flags & (M_BCAST | M_MCAST)) == 0)
continue;
if ((p->ifp->if_flags & IFF_RUNNING) == 0)
continue;
if (IF_QFULL(&p->ifp->if_snd)) {
sc->sc_if.if_oerrors++;
continue;
}
if (SIMPLEQ_FIRST(&p->bif_brlout) &&
bridge_filterrule(SIMPLEQ_FIRST(&p->bif_brlout), eh) ==
BRL_ACTION_BLOCK)
continue;
/* If last one, reuse the passed-in mbuf */
if (LIST_NEXT(p, next) == NULL) {
mc = m;
used = 1;
} else {
mc = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
if (mc == NULL) {
sc->sc_if.if_oerrors++;
continue;
}
}
if (p->bif_flags & IFBIF_BLOCKNONIP &&
bridge_blocknonip(eh, mc)) {
m_freem(mc);
continue;
}
sc->sc_if.if_opackets++;
sc->sc_if.if_obytes += mc->m_pkthdr.len;
if (ifp && ((eh->ether_shost[0] & 1) == 0) )
ifp->if_omcasts++;
// Also count the bytes in the outgoing interface; normally
// done in if_ethersubr.c but here we bypass that route.
p->ifp->if_obytes += m->m_pkthdr.len;
IF_ENQUEUE(&p->ifp->if_snd, mc);
if ((p->ifp->if_flags & IFF_OACTIVE) == 0)
(*p->ifp->if_start)(p->ifp);
}
if (!used)
m_freem(m);
}
struct ifnet *
bridge_rtupdate(sc, ea, ifp, setflags, flags)
struct bridge_softc *sc;
struct ether_addr *ea;
struct ifnet *ifp;
int setflags;
u_int8_t flags;
{
struct bridge_rtnode *p, *q;
u_int32_t h;
int s, dir;
s = splhigh();
if (sc->sc_rts == NULL) {
if (setflags && flags == IFBAF_STATIC) {
sc->sc_rts = (struct bridge_rthead *)malloc(
BRIDGE_RTABLE_SIZE *
(sizeof(struct bridge_rthead)),M_DEVBUF,M_NOWAIT);
if (sc->sc_rts == NULL)
goto done;
for (h = 0; h < BRIDGE_RTABLE_SIZE; h++)
LIST_INIT(&sc->sc_rts[h]);
} else
goto done;
}
h = bridge_hash(ea);
p = LIST_FIRST(&sc->sc_rts[h]);
if (p == NULL) {
if (sc->sc_brtcnt >= sc->sc_brtmax)
goto done;
p = (struct bridge_rtnode *)malloc(
sizeof(struct bridge_rtnode), M_DEVBUF, M_NOWAIT);
if (p == NULL)
goto done;
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
p->brt_if = ifp;
p->brt_age = 1;
if (setflags)
p->brt_flags = flags;
else
p->brt_flags = IFBAF_DYNAMIC;
LIST_INSERT_HEAD(&sc->sc_rts[h], p, brt_next);
sc->sc_brtcnt++;
goto want;
}
do {
q = p;
p = LIST_NEXT(p, brt_next);
dir = memcmp(ea, &q->brt_addr, sizeof(q->brt_addr));
if (dir == 0) {
if (setflags) {
q->brt_if = ifp;
q->brt_flags = flags;
}
if (q->brt_if == ifp)
q->brt_age = 1;
ifp = q->brt_if;
goto want;
}
if (dir > 0) {
if (sc->sc_brtcnt >= sc->sc_brtmax)
goto done;
p = (struct bridge_rtnode *)malloc(
sizeof(struct bridge_rtnode), M_DEVBUF, M_NOWAIT);
if (p == NULL)
goto done;
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
p->brt_if = ifp;
p->brt_age = 1;
if (setflags)
p->brt_flags = flags;
else
p->brt_flags = IFBAF_DYNAMIC;
LIST_INSERT_BEFORE(q, p, brt_next);
sc->sc_brtcnt++;
goto want;
}
if (p == NULL) {
if (sc->sc_brtcnt >= sc->sc_brtmax)
goto done;
p = (struct bridge_rtnode *)malloc(
sizeof(struct bridge_rtnode), M_DEVBUF, M_NOWAIT);
if (p == NULL)
goto done;
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
p->brt_if = ifp;
p->brt_age = 1;
if (setflags)
p->brt_flags = flags;
else
p->brt_flags = IFBAF_DYNAMIC;
LIST_INSERT_AFTER(q, p, brt_next);
sc->sc_brtcnt++;
goto want;
}
} while (p != NULL);
done:
ifp = NULL;
want:
splx(s);
return (ifp);
}
struct ifnet *
bridge_rtlookup(sc, ea)
struct bridge_softc *sc;
struct ether_addr *ea;
{
struct bridge_rtnode *p;
u_int32_t h;
int s, dir;
/*
* Lock out everything else
*/
s = splhigh();
if (sc->sc_rts == NULL)
goto fail;
h = bridge_hash(ea);
p = LIST_FIRST(&sc->sc_rts[h]);
while (p != NULL) {
dir = memcmp(ea, &p->brt_addr, sizeof(p->brt_addr));
if (dir == 0) {
splx(s);
return (p->brt_if);
}
if (dir > 0)
goto fail;
p = LIST_NEXT(p, brt_next);
}
fail:
splx(s);
return (NULL);
}
/*
* The following hash function is adapted from 'Hash Functions' by Bob Jenkins
* ("Algorithm Alley", Dr. Dobbs Journal, September 1997).
* "You may use this code any way you wish, private, educational, or
* commercial. It's free."
*/
#define mix(a,b,c) \
do { \
a -= b; a -= c; a ^= (c >> 13); \
b -= c; b -= a; b ^= (a << 8); \
c -= a; c -= b; c ^= (b >> 13); \
a -= b; a -= c; a ^= (c >> 12); \
b -= c; b -= a; b ^= (a << 16); \
c -= a; c -= b; c ^= (b >> 5); \
a -= b; a -= c; a ^= (c >> 3); \
b -= c; b -= a; b ^= (a << 10); \
c -= a; c -= b; c ^= (b >> 15); \
} while(0)
u_int32_t
bridge_hash(addr)
struct ether_addr *addr;
{
u_int32_t a = 0x9e3779b9, b = 0x9e3779b9, c = 0xdeadbeef;
b += addr->ether_addr_octet[5] << 8;
b += addr->ether_addr_octet[4];
a += addr->ether_addr_octet[3] << 24;
a += addr->ether_addr_octet[2] << 16;
a += addr->ether_addr_octet[1] << 8;
a += addr->ether_addr_octet[0];
mix(a, b, c);
return (c & BRIDGE_RTABLE_MASK);
}
/*
* Trim the routing table so that we've got a number of routes
* less than or equal to the maximum.
*/
void
bridge_rttrim(sc)
struct bridge_softc *sc;
{
struct bridge_rtnode *n, *p;
int s, i;
s = splhigh();
if (sc->sc_rts == NULL)
goto done;
/*
* Make sure we have to trim the address table
*/
if (sc->sc_brtcnt <= sc->sc_brtmax)
goto done;
/*
* Force an aging cycle, this might trim enough addresses.
*/
splx(s);
bridge_rtage(sc);
s = splhigh();
if (sc->sc_brtcnt <= sc->sc_brtmax)
goto done;
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
n = LIST_FIRST(&sc->sc_rts[i]);
while (n != NULL) {
p = LIST_NEXT(n, brt_next);
if ((n->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) {
LIST_REMOVE(n, brt_next);
sc->sc_brtcnt--;
free(n, M_DEVBUF);
n = p;
if (sc->sc_brtcnt <= sc->sc_brtmax)
goto done;
}
}
}
done:
if (sc->sc_rts != NULL && sc->sc_brtcnt == 0 &&
(sc->sc_if.if_flags & IFF_UP) == 0) {
free(sc->sc_rts, M_DEVBUF);
sc->sc_rts = NULL;
}
splx(s);
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?