diff options
-rw-r--r-- | sys/net/if_bridge.c | 356 |
1 files changed, 184 insertions, 172 deletions
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index 1ce9c96cdd6..f98e4e6fa2e 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_bridge.c,v 1.26 2000/02/19 08:59:05 niklas Exp $ */ +/* $OpenBSD: if_bridge.c,v 1.27 2000/02/21 17:38:07 jason Exp $ */ /* * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) @@ -94,13 +94,6 @@ #define BRIDGE_RTABLE_TIMEOUT 240 #endif -/* - * This really should be defined in if_llc.h but in case it isn't. - */ -#ifndef llc_snap -#define llc_snap llc_un.type_snap -#endif - extern int ifqmaxlen; /* @@ -148,12 +141,13 @@ struct bridge_softc { LIST_HEAD(bridge_rthead, bridge_rtnode) *sc_rts;/* hash table */ }; -/* - * Ethernet header and SNAP header - */ -struct ehllc { - struct ether_header eh; - struct llc llc; +/* SNAP LLC header */ +struct snap { + u_int8_t dsap; + u_int8_t ssap; + u_int8_t control; + u_int8_t org[3]; + u_int16_t type; }; struct bridge_softc bridgectl[NBRIDGE]; @@ -179,7 +173,7 @@ struct ifnet * bridge_rtupdate __P((struct bridge_softc *, struct ifnet * bridge_rtlookup __P((struct bridge_softc *, struct ether_addr *)); u_int32_t bridge_hash __P((struct ether_addr *)); -struct mbuf *bridge_blocknonip __P((struct mbuf *)); +int bridge_blocknonip __P((struct ether_header *, struct mbuf *)); int bridge_addrule __P((struct bridge_iflist *, struct ifbrlreq *, int out)); int bridge_flushrule __P((struct bridge_iflist *)); @@ -197,10 +191,8 @@ u_int8_t bridge_filterrule __P((struct brl_node *, struct ether_header *)); /* * Filter hooks */ -#define BRIDGE_FILTER_PASS 0 -#define BRIDGE_FILTER_DROP 1 -int bridge_filter __P((struct bridge_softc *, struct ifnet *, - struct ether_header *, struct mbuf **)); +struct mbuf *bridge_filter __P((struct bridge_softc *, struct ifnet *, + struct ether_header *, struct mbuf *m)); #endif void @@ -759,8 +751,6 @@ bridge_output(ifp, m, sa, rt) struct ifnet *dst_if; struct ether_addr *src, *dst; struct bridge_softc *sc; - struct bridge_iflist *p; - struct mbuf *mc; int s; if (m->m_len < sizeof(*eh)) { @@ -791,20 +781,29 @@ bridge_output(ifp, m, sa, rt) */ dst_if = bridge_rtlookup(sc, dst); if (dst_if == NULL || eh->ether_dhost[0] & 1) { + struct bridge_iflist *p; + struct mbuf *mc; + int used = 0; + for (p = LIST_FIRST(&sc->sc_iflist); p != NULL; - p = LIST_NEXT(p, next)) { + p = LIST_NEXT(p, next)) { if ((p->ifp->if_flags & IFF_RUNNING) == 0) continue; - if (IF_QFULL(&p->ifp->if_snd)) { sc->sc_if.if_oerrors++; continue; } - mc = m_copym(m, 0, M_COPYALL, M_NOWAIT); - if (mc == NULL) { - sc->sc_if.if_oerrors++; - continue; + if (LIST_NEXT(p, next) == NULL) { + used = 1; + mc = m; + } + else { + mc = m_copym(m, 0, M_COPYALL, M_NOWAIT); + if (mc == NULL) { + sc->sc_if.if_oerrors++; + continue; + } } sc->sc_if.if_opackets++; @@ -813,7 +812,8 @@ bridge_output(ifp, m, sa, rt) if ((p->ifp->if_flags & IFF_OACTIVE) == 0) (*p->ifp->if_start)(p->ifp); } - m_freem(m); + if (!used) + m_freem(m); splx(s); return (0); } @@ -880,7 +880,7 @@ bridgeintr_frame(sc, m) struct ifnet *src_if, *dst_if; struct bridge_iflist *ifl; struct ether_addr *dst, *src; - struct ether_header *eh; + struct ether_header eh; if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) { m_freem(m); @@ -907,34 +907,33 @@ bridgeintr_frame(sc, m) return; } - if (m->m_len < sizeof(*eh)) { - m = m_pullup(m, sizeof(*eh)); - if (m == NULL) - return; + if (m->m_pkthdr.len < sizeof(eh)) { + m_freem(m); + return; } - eh = mtod(m, struct ether_header *); - dst = (struct ether_addr *)&eh->ether_dhost[0]; - src = (struct ether_addr *)&eh->ether_shost[0]; + m_copydata(m, 0, sizeof(struct ether_header), (caddr_t)&eh); + dst = (struct ether_addr *)&eh.ether_dhost[0]; + src = (struct ether_addr *)&eh.ether_shost[0]; /* * If interface is learning, and if source address * is not broadcast or multicast, record it's address. */ if ((ifl->bif_flags & IFBIF_LEARNING) && - (eh->ether_shost[0] & 1) == 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)) + (eh.ether_shost[0] & 1) == 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)) bridge_rtupdate(sc, src, src_if, 0, IFBAF_DYNAMIC); /* * If packet is unicast, destined for someone on "this" * side of the bridge, drop it. */ - if (m->m_flags & (M_BCAST | M_MCAST)) { + if ((m->m_flags & (M_BCAST | M_MCAST)) == 0) { dst_if = bridge_rtlookup(sc, dst); if (dst_if == src_if) { m_freem(m); @@ -973,28 +972,22 @@ bridgeintr_frame(sc, m) } } - if (ifl->bif_flags & IFBIF_BLOCKNONIP) { - m = bridge_blocknonip(m); - if (m == NULL) - return; - eh = mtod(m, struct ether_header *); - dst = (struct ether_addr *)&eh->ether_dhost[0]; - src = (struct ether_addr *)&eh->ether_shost[0]; + 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) == + bridge_filterrule(SIMPLEQ_FIRST(&ifl->bif_brlin), &eh) == BRL_ACTION_BLOCK) { m_freem(m); return; } #if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM)) - if (bridge_filter(sc, src_if, eh, &m) == BRIDGE_FILTER_DROP) { - if (m != NULL) - m_freem(m); + m = bridge_filter(sc, src_if, &eh, m); + if (m == NULL) return; - } #endif /* @@ -1003,7 +996,9 @@ bridgeintr_frame(sc, m) */ if ((m->m_flags & (M_BCAST | M_MCAST)) || dst_if == NULL) { sc->sc_if.if_imcasts++; - bridge_broadcast(sc, src_if, eh, m); + s = splimp(); + bridge_broadcast(sc, src_if, &eh, m); + splx(s); return; } @@ -1019,7 +1014,7 @@ bridgeintr_frame(sc, m) 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) == + bridge_filterrule(SIMPLEQ_FIRST(&ifl->bif_brlout), &eh) == BRL_ACTION_BLOCK) { m_freem(m); return; @@ -1040,8 +1035,8 @@ bridgeintr_frame(sc, m) } /* - * Receive input from an interface. Rebroadcast if necessary to other - * bridge members. + * 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) @@ -1062,6 +1057,9 @@ bridge_input(ifp, eh, m) 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); @@ -1186,10 +1184,10 @@ bridge_broadcast(sc, ifp, eh, m) } } - if (p->bif_flags & IFBIF_BLOCKNONIP) { - mc = bridge_blocknonip(mc); - if (mc == NULL) - continue; + if (p->bif_flags & IFBIF_BLOCKNONIP && + bridge_blocknonip(eh, mc)) { + m_freem(mc); + continue; } sc->sc_if.if_opackets++; @@ -1662,41 +1660,52 @@ done: return (error); } -struct mbuf * -bridge_blocknonip(m) +/* + * Block non-ip frames: + * Returns 0 if frame is ip, and 1 if it should be dropped. + */ +int +bridge_blocknonip(eh, m) + struct ether_header *eh; struct mbuf *m; { - struct ehllc *ehllc; + struct snap snap; u_int16_t etype; - if (m->m_len < sizeof(struct ehllc)) { - m = m_pullup(m, sizeof(struct ehllc)); - if (m == NULL) - return (m); - } - - ehllc = mtod(m, struct ehllc *); - etype = ntohs(ehllc->eh.ether_type); + if (m->m_pkthdr.len < sizeof(struct ether_header)) + return (1); - if (etype == ETHERTYPE_ARP || etype == ETHERTYPE_REVARP || - etype == ETHERTYPE_IP || etype == ETHERTYPE_IPV6) - return (m); + etype = ntohs(eh->ether_type); + switch (etype) { + case ETHERTYPE_ARP: + case ETHERTYPE_REVARP: + case ETHERTYPE_IP: + case ETHERTYPE_IPV6: + return (0); + } if (etype > ETHERMTU) - goto notip; - - if (ehllc->llc.llc_control == LLC_UI && - ehllc->llc.llc_dsap == LLC_SNAP_LSAP && - ehllc->llc.llc_ssap == LLC_SNAP_LSAP) { - etype = ntohs(ehllc->llc.llc_snap.ether_type); - if (etype == ETHERTYPE_ARP || etype == ETHERTYPE_REVARP || - etype == ETHERTYPE_IP || etype == ETHERTYPE_IPV6) - return (m); + return (1); + + if (m->m_pkthdr.len < + (sizeof(struct ether_header) + sizeof(struct snap))) + return (1); + + m_copydata(m, sizeof(struct ether_header), sizeof(struct snap), + (caddr_t)&snap); + + etype = ntohs(snap.type); + if (snap.dsap == LLC_SNAP_LSAP && snap.ssap == LLC_SNAP_LSAP && + snap.control == LLC_UI && + snap.org[0] == 0 && snap.org[1] == 0 && snap.org[2] == 0 && + (etype == ETHERTYPE_ARP || + etype == ETHERTYPE_REVARP || + etype == ETHERTYPE_IP || + etype == ETHERTYPE_IPV6)) { + return (0); } -notip: - m_freem(m); - return (NULL); + return (1); } u_int8_t @@ -1785,110 +1794,87 @@ bridge_flushrule(bif) #if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM)) /* + * Maximum sized IP header + */ +union maxip { + struct ip ip; + u_int32_t _padding[16]; +}; + +/* * Filter IP packets by peeking into the ethernet frame. This violates * the ISO model, but allows us to act as a IP filter at the data link * layer. As a result, most of this code will look familiar to those * who've read net/if_ethersubr.c and netinet/ip_input.c */ -int -bridge_filter(sc, ifp, eh, np) +struct mbuf * +bridge_filter(sc, ifp, eh, m) struct bridge_softc *sc; struct ifnet *ifp; struct ether_header *eh; - struct mbuf **np; + struct mbuf *m; { - struct mbuf *m = *np; - struct ehllc *ehllc; + struct snap snap; + int hassnap = 0; struct ip *ip; - u_int16_t etype; - int hlen, r, off = sizeof(struct ether_header); + int hlen; if (fr_checkp == NULL) - return (BRIDGE_FILTER_PASS); + return (m); - if (m->m_len < sizeof(struct ehllc)) { - m = m_pullup(m, sizeof(struct ehllc)); - *np = m; - if (m == NULL) - return (BRIDGE_FILTER_DROP); - } + if (eh->ether_type != htons(ETHERTYPE_IP)) { + if (eh->ether_type > ETHERMTU || + m->m_pkthdr.len < (sizeof(struct snap) + + sizeof(struct ether_header))) + return (m); - ehllc = mtod(m, struct ehllc *); - etype = ntohs(ehllc->eh.ether_type); - if (etype != ETHERTYPE_IP) { - if (etype > ETHERMTU) /* Can't be SNAP */ - return (BRIDGE_FILTER_PASS); - - if (ehllc->llc.llc_control != LLC_UI || - ehllc->llc.llc_dsap != LLC_SNAP_LSAP || - ehllc->llc.llc_ssap != LLC_SNAP_LSAP || - ehllc->llc.llc_snap.org_code[0] != 0 || - ehllc->llc.llc_snap.org_code[1] != 0 || - ehllc->llc.llc_snap.org_code[2] != 0 || - ntohs(ehllc->llc.llc_snap.ether_type) != ETHERTYPE_IP) - return (BRIDGE_FILTER_PASS); - off += 8; + m_copydata(m, sizeof(struct ether_header), + sizeof(struct snap), (caddr_t)&snap); + + if (snap.dsap != LLC_SNAP_LSAP || snap.ssap != LLC_SNAP_LSAP || + snap.control != LLC_UI || + snap.org[0] != 0 || snap.org[1] != 0 || snap.org[2] || + snap.type != htons(ETHERTYPE_IP)) + return (m); + hassnap = 1; } - /* - * We need a full copy because we're going to be destructive - * to the packet before we pass it to the ip filter code. - * XXX This needs to be turned into a munge -> check -> - * XXX unmunge section, for now, we copy. - * XXX Copy at offset 0 so that the mbuf header is copied, too. - */ - m = m_copym2(m, 0, M_COPYALL, M_NOWAIT); - m_adj(m, off); - if (m == NULL) - return (BRIDGE_FILTER_DROP); + m_adj(m, sizeof(struct ether_header)); + if (hassnap) + m_adj(m, sizeof(struct snap)); - /* - * Pull up the IP header - */ - if (m->m_len < sizeof(struct ip)) { - m = m_pullup(m, sizeof(struct ip)); - if (m == NULL) - return (BRIDGE_FILTER_DROP); - } + if (m->m_pkthdr.len < sizeof(struct ip)) + goto dropit; - /* - * Examine the ip header, and drop invalid packets - */ + /* Copy minimal header, and drop invalids */ + if (m->m_len < sizeof(struct ip) && + (m = m_pullup(m, sizeof(struct ip))) == NULL) + return (NULL); ip = mtod(m, struct ip *); - if (ip->ip_v != IPVERSION) { - r = BRIDGE_FILTER_DROP; - goto out; - } - hlen = ip->ip_hl << 2; /* get whole header length */ - if (hlen < sizeof(struct ip)) { - r = BRIDGE_FILTER_DROP; - goto out; - } - if (hlen > m->m_len) { /* pull up whole header */ - if ((m = m_pullup(m, hlen)) == 0) { - r = BRIDGE_FILTER_DROP; - goto out; - } + if (ip->ip_v != IPVERSION) + goto dropit; + + hlen = ip->ip_hl << 2; /* get whole header length */ + if (hlen < sizeof(struct ip)) + goto dropit; + if (hlen > m->m_len) { + if ((m = m_pullup(m, sizeof(struct ip))) == NULL) + return (NULL); ip = mtod(m, struct ip *); } - if ((ip->ip_sum = in_cksum(m, hlen)) != 0) { - r = BRIDGE_FILTER_DROP; - goto out; - } + + if ((ip->ip_sum = in_cksum(m, hlen)) != 0) + goto dropit; NTOHS(ip->ip_len); - if (ip->ip_len < hlen) { - r = BRIDGE_FILTER_DROP; - goto out; - } + if (ip->ip_len < hlen) + goto dropit; NTOHS(ip->ip_id); NTOHS(ip->ip_off); - if (m->m_pkthdr.len < ip->ip_len) { - r = BRIDGE_FILTER_DROP; - goto out; - } + if (m->m_pkthdr.len < ip->ip_len) + goto dropit; if (m->m_pkthdr.len > ip->ip_len) { if (m->m_len == m->m_pkthdr.len) { m->m_len = ip->ip_len; @@ -1899,13 +1885,39 @@ bridge_filter(sc, ifp, eh, np) /* Finally, we get to filter the packet! */ if (fr_checkp && (*fr_checkp)(ip, hlen, ifp, 0, &m)) - return (BRIDGE_FILTER_DROP); + return (NULL); + + /* Rebuild the IP header */ + if (m->m_len < hlen && ((m = m_pullup(m, hlen)) == NULL)) + return (NULL); + if (m->m_len < sizeof(struct ip)) + goto dropit; + ip = mtod(m, struct ip *); + HTONS(ip->ip_len); + HTONS(ip->ip_id); + HTONS(ip->ip_off); + ip->ip_sum = in_cksum(m, hlen); + + /* Reattach SNAP header */ + if (hassnap) { + M_PREPEND(m, sizeof(snap), M_DONTWAIT); + if (m == NULL) + goto dropit; + bcopy(&snap, mtod(m, caddr_t), sizeof(snap)); + } + + /* Reattach ethernet header */ + M_PREPEND(m, sizeof(*eh), M_DONTWAIT); + if (m == NULL) + goto dropit; + bcopy(eh, mtod(m, caddr_t), sizeof(*eh)); - r = BRIDGE_FILTER_PASS; + return (m); -out: - m_freem(m); - return (r); +dropit: + if (m != NULL) + m_freem(m); + return (NULL); } #endif |