summaryrefslogtreecommitdiff
path: root/sys/net
diff options
context:
space:
mode:
authorJason Wright <jason@cvs.openbsd.org>1999-02-26 17:01:37 +0000
committerJason Wright <jason@cvs.openbsd.org>1999-02-26 17:01:37 +0000
commit842ed679f15acf3904a23c6859a05c53e4ebb9cc (patch)
tree49b379fdcac3f9f3aa3ce1fe6ea2b32dcba41d98 /sys/net
parented730bf4e3f6e7f647c18257936abfa1e63940f0 (diff)
Ethernet bridge/IP firewall driver.
Diffstat (limited to 'sys/net')
-rw-r--r--sys/net/if.h3
-rw-r--r--sys/net/if_bridge.c1186
-rw-r--r--sys/net/if_bridge.h59
-rw-r--r--sys/net/if_ethersubr.c47
4 files changed, 1291 insertions, 4 deletions
diff --git a/sys/net/if.h b/sys/net/if.h
index d8df7aa835d..934e7e54443 100644
--- a/sys/net/if.h
+++ b/sys/net/if.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if.h,v 1.9 1999/01/07 06:03:28 deraadt Exp $ */
+/* $OpenBSD: if.h,v 1.10 1999/02/26 17:01:31 jason Exp $ */
/* $NetBSD: if.h,v 1.23 1996/05/07 02:40:27 thorpej Exp $ */
/*
@@ -119,6 +119,7 @@ struct ifnet { /* and the entries */
char if_xname[IFNAMSIZ]; /* external name (name + unit) */
int if_pcount; /* number of promiscuous listeners */
caddr_t if_bpf; /* packet filter structure */
+ caddr_t if_bridge; /* bridge structure */
u_short if_index; /* numeric abbreviation for this if */
short if_timer; /* time 'til if_watchdog called */
short if_flags; /* up/down, broadcast, etc. */
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
new file mode 100644
index 00000000000..f16c51cf12d
--- /dev/null
+++ b/sys/net/if_bridge.c
@@ -0,0 +1,1186 @@
+/* $OpenBSD: if_bridge.c,v 1.1 1999/02/26 17:01:31 jason Exp $ */
+
+/*
+ * Copyright (c) 1999 Jason L. Wright (jason@thought.net)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jason L. Wright
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bridge.h"
+#if NBRIDGE > 0
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#ifdef IPFILTER
+#include <netinet/ip_fil_compat.h>
+#include <netinet/ip_fil.h>
+#endif
+#endif
+
+#include <net/if_bridge.h>
+
+#ifndef BRIDGE_RTABLE_SIZE
+#define BRIDGE_RTABLE_SIZE 1024
+#endif
+#define BRIDGE_RTABLE_MASK (BRIDGE_RTABLE_SIZE - 1)
+
+extern int ifqmaxlen;
+
+/*
+ * Bridge interface list
+ */
+struct bridge_iflist {
+ struct ifnet *ifp; /* member interface */
+ u_int16_t bif_flags; /* member flags */
+ LIST_ENTRY(bridge_iflist) next; /* next in list */
+};
+
+/*
+ * Bridge route node
+ */
+struct bridge_rtnode {
+ struct ifnet *brt_if; /* destination ifs */
+ struct ether_addr brt_addr; /* destination addr */
+ LIST_ENTRY(bridge_rtnode) brt_next; /* next in list */
+ u_int16_t brt_age; /* age counter */
+};
+
+/*
+ * Software state for each bridge
+ */
+struct bridge_softc {
+ struct ifnet sc_if; /* the interface */
+ u_int32_t sc_brtageidx; /* route age index */
+ LIST_HEAD(, bridge_iflist) sc_iflist; /* interface list */
+ LIST_HEAD(bridge_rthead, bridge_rtnode) *sc_rts;/* hash table */
+};
+
+struct bridge_softc bridgectl[NBRIDGE];
+
+void bridgeattach __P((int));
+int bridge_ioctl __P((struct ifnet *, u_long, caddr_t));
+void bridge_start __P((struct ifnet *));
+struct mbuf * bridge_broadcast __P((struct bridge_softc *sc,
+ struct ifnet *, struct ether_header *, struct mbuf *));
+void bridge_stop __P((struct bridge_softc *));
+void bridge_init __P((struct bridge_softc *));
+
+int bridge_rtfind __P((struct bridge_softc *, struct ifbrtreq *));
+void bridge_rtage __P((void *));
+void bridge_rtdelete __P((struct bridge_softc *, struct ifnet *));
+struct ifnet * bridge_rtupdate __P((struct bridge_softc *,
+ struct ether_addr *, struct ifnet *ifp));
+struct ifnet * bridge_rtlookup __P((struct bridge_softc *,
+ struct ether_addr *));
+u_int32_t bridge_hash __P((struct ether_addr *));
+
+#define ETHERADDR_IS_IP_MCAST(a) \
+ /* struct etheraddr *a; */ \
+ ((a)->ether_addr_octet[0] == 0x01 && \
+ (a)->ether_addr_octet[1] == 0x00 && \
+ (a)->ether_addr_octet[2] == 0x5e)
+
+#if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM))
+/*
+ * 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 *));
+#endif
+
+void
+bridgeattach(unused)
+ int unused;
+{
+ register int i;
+ struct ifnet *ifp;
+
+ for (i = 0; i < NBRIDGE; i++) {
+ LIST_INIT(&bridgectl[i].sc_iflist);
+
+ ifp = &bridgectl[i].sc_if;
+ sprintf(ifp->if_xname, "bridge%d", i);
+ ifp->if_softc = &bridgectl[i];
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_ioctl = bridge_ioctl;
+ ifp->if_output = bridge_output;
+ ifp->if_start = bridge_start;
+ ifp->if_type = IFT_PROPVIRTUAL;
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+ ifp->if_hdrlen = sizeof(struct ether_header);
+
+ if_attach(ifp);
+ }
+}
+
+int
+bridge_ioctl(ifp, cmd, data)
+ struct ifnet *ifp;
+ u_long cmd;
+ caddr_t data;
+{
+ struct proc *prc = curproc; /* XXX */
+ struct ifnet *ifs;
+ struct bridge_softc *sc = (struct bridge_softc *)ifp->if_softc;
+ struct ifbreq *req = (struct ifbreq *)data;
+ struct ifbrtreq *brtreq = (struct ifbrtreq *)data;
+ int error = 0, s, i;
+ struct bridge_iflist *p;
+
+ s = splimp();
+ switch(cmd) {
+ case SIOCBRDGADD:
+ /*
+ * Only root can add interfaces.
+ */
+ if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
+ break;
+
+ /*
+ * Get pointer to ifnet structure for the named interface.
+ */
+ ifs = ifunit(req->ifsname);
+ if (ifs == NULL) { /* no such interface */
+ error = ENOENT;
+ break;
+ }
+
+ /*
+ * Check to see if this interface is already a member.
+ */
+ if (ifs->if_bridge == (caddr_t)sc) {
+ error = EEXIST;
+ break;
+ }
+
+ /*
+ * Make sure it's not a member of another bridge.
+ */
+ if (ifs->if_bridge != NULL) {
+ error = EBUSY;
+ break;
+ }
+
+ /*
+ * Make sure it is an ethernet interface.
+ */
+ if (ifs->if_type != IFT_ETHER) {
+ error = EINVAL;
+ break;
+ }
+
+ /*
+ * Put interface into promiscuous mode.
+ */
+ error = ifpromisc(ifs, 1);
+ if (error != 0)
+ break;
+
+ /*
+ * Allocate list entry.
+ */
+ p = (struct bridge_iflist *) malloc(
+ sizeof(struct bridge_iflist), M_DEVBUF, M_NOWAIT);
+ if (p == NULL) { /* list alloc failed */
+ error = ENOMEM;
+ ifpromisc(ifs, 0); /* decr promisc cnt */
+ break;
+ }
+
+ /*
+ * Add to interface list, and give the interface a pointer
+ * back to us.
+ */
+ p->ifp = ifs;
+ LIST_INSERT_HEAD(&sc->sc_iflist, p, next);
+ ifs->if_bridge = (caddr_t)sc;
+ break;
+ case SIOCBRDGDEL:
+ /*
+ * Only root can delete interfaces.
+ */
+ if ((error = suser(prc->p_ucred, &prc->p_acflag)) != 0)
+ break;
+
+ p = LIST_FIRST(&sc->sc_iflist);
+ while (p != NULL) {
+ if (strncmp(p->ifp->if_xname, req->ifsname,
+ sizeof(p->ifp->if_xname)) == 0) {
+ /*
+ * Remove the pointer back to us.
+ */
+ p->ifp->if_bridge = NULL;
+
+ /*
+ * Decrement promisc count
+ */
+ error = ifpromisc(p->ifp, 0);
+
+ /*
+ * Finally, remove from list, delete
+ * routes from that interface, and reclaim
+ * memory.
+ */
+ LIST_REMOVE(p, next);
+ bridge_rtdelete(sc, p->ifp);
+ free(p, M_DEVBUF);
+ break;
+ }
+ p = LIST_NEXT(p, next);
+ }
+ if (p == NULL) {
+ error = ENOENT;
+ break;
+ }
+ break;
+ case SIOCBRDGIDX:
+ /*
+ * Search through list looking for right element.
+ */
+ i = 0;
+ p = LIST_FIRST(&sc->sc_iflist);
+ while (p != NULL && i < req->index) {
+ i++;
+ p = LIST_NEXT(p, next);
+ }
+
+ /*
+ * Make sure we didn't go off the end of the list.
+ */
+ if (p == NULL || req->index != i) {
+ error = ENOENT;
+ break;
+ }
+ /* found it */
+ strncpy(req->ifsname, p->ifp->if_xname, sizeof(req->ifsname)-1);
+ req->ifsname[sizeof(req->ifsname)-1] = '\0';
+ break;
+ case SIOCBRDGRT:
+ if ((ifp->if_flags & IFF_RUNNING) == 0) {
+ error = ENETDOWN;
+ break;
+ }
+ error = bridge_rtfind(sc, brtreq);
+ break;
+ case SIOCSIFFLAGS:
+ if ((ifp->if_flags & IFF_UP) == IFF_UP)
+ bridge_init(sc);
+
+ if ((ifp->if_flags & IFF_UP) == 0)
+ bridge_stop(sc);
+
+ break;
+ default:
+ error = EINVAL;
+ }
+ splx(s);
+ return (error);
+}
+
+void
+bridge_init(sc)
+ struct bridge_softc *sc;
+{
+ struct ifnet *ifp = &sc->sc_if;
+ int i;
+ int s;
+
+ if ((ifp->if_flags & IFF_RUNNING) == IFF_RUNNING)
+ return;
+
+ sc->sc_brtageidx = 0;
+
+ s = splhigh();
+ if (sc->sc_rts == NULL) {
+ sc->sc_rts = (struct bridge_rthead *)malloc(
+ BRIDGE_RTABLE_SIZE * (sizeof(struct bridge_rthead)),
+ M_DEVBUF, M_NOWAIT);
+ if (sc->sc_rts == NULL) {
+ splx(s);
+ return;
+ }
+
+ for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
+ LIST_INIT(&sc->sc_rts[i]);
+ }
+ }
+ ifp->if_flags |= IFF_RUNNING;
+ splx(s);
+
+ timeout(bridge_rtage, sc, 2 * hz);
+}
+
+/*
+ * Stop the bridge and deallocate the routing table.
+ */
+void
+bridge_stop(sc)
+ struct bridge_softc *sc;
+{
+ struct ifnet *ifp = &sc->sc_if;
+ struct bridge_rtnode *n, *p;
+ int i, s;
+
+ /*
+ * If we're not running, there's nothing to do.
+ */
+ if ((ifp->if_flags & IFF_RUNNING) == 0)
+ return;
+
+ untimeout(bridge_rtage, sc);
+
+ /*
+ * Free the routing table, if necessary.
+ */
+ if (sc->sc_rts != NULL) {
+ s = splhigh();
+ for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
+ n = LIST_FIRST(&sc->sc_rts[i]);
+ while (n != NULL) {
+ p = LIST_NEXT(n, brt_next);
+ LIST_REMOVE(n, brt_next);
+ free(n, M_DEVBUF);
+ n = p;
+ }
+ }
+ free(sc->sc_rts, M_DEVBUF);
+ sc->sc_rts = NULL;
+ splx(s);
+ }
+ ifp->if_flags &= ~IFF_RUNNING;
+}
+
+/*
+ * Send output from the bridge. The mbuf has the ethernet header
+ * already attached. We must free the mbuf before exitting.
+ */
+int
+bridge_output(ifp, m, sa, rt)
+ struct ifnet *ifp;
+ struct mbuf *m;
+ struct sockaddr *sa;
+ struct rtentry *rt;
+{
+ struct ether_header *eh;
+ struct ifnet *dst_if;
+ struct ether_addr *src, *dst;
+ struct arpcom *ac = (struct arpcom *)ifp;
+ struct bridge_softc *sc;
+ struct bridge_iflist *p;
+ struct mbuf *mc;
+ int s;
+
+ if (m->m_len < sizeof(*eh)) {
+ m = m_pullup(m, sizeof(*eh));
+ if (m == NULL)
+ return (0);
+ }
+ eh = mtod(m, struct ether_header *);
+ dst = (struct ether_addr *)&eh->ether_dhost[0];
+ src = (struct ether_addr *)&eh->ether_shost[0];
+ sc = (struct bridge_softc *)ifp->if_bridge;
+
+ s = splimp();
+
+ /*
+ * If the packet is a broadcast or we don't know a better way to
+ * get there, we must broadcast with header rewriting.
+ */
+ dst_if = bridge_rtlookup(sc, dst);
+ if (dst_if == NULL || eh->ether_dhost[0] & 1) {
+ for (p = LIST_FIRST(&sc->sc_iflist); p != NULL;
+ p = LIST_NEXT(p, next)) {
+ /*
+ * Make sure interface is running.
+ */
+ if ((p->ifp->if_flags & IFF_RUNNING) == 0)
+ continue;
+
+ /*
+ * Make sure there's room in the queue.
+ */
+ if (IF_QFULL(&p->ifp->if_snd)) {
+ sc->sc_if.if_oerrors++;
+ continue;
+ }
+
+ /*
+ * Make a full copy of the packet (sigh)
+ */
+ mc = m_copym2(m, 0, M_COPYALL, M_NOWAIT);
+ if (mc == NULL) {
+ sc->sc_if.if_oerrors++;
+ continue;
+ }
+
+ /*
+ * If packet does not have a multicast or broadcast
+ * destination, rewrite the header to contain
+ * the current interface's address.
+ */
+ if ((eh->ether_shost[0] & 1) == 0) {
+ struct arpcom *cac = (struct arpcom *)p->ifp;
+ struct ether_header *ceh;
+ struct ether_addr *csrc;
+
+ /*
+ * Pull up ethernet header.
+ */
+ if (mc->m_len < sizeof(*ceh)) {
+ mc = m_pullup(mc, sizeof(*ceh));
+ if (mc == NULL)
+ continue;
+ }
+ ceh = mtod(mc, struct ether_header *);
+ csrc = (struct ether_addr *)
+ &ceh->ether_shost[0];
+ bcopy(cac->ac_enaddr, csrc, ETHER_ADDR_LEN);
+ }
+
+ /*
+ * Update stats, queue the packet, and start it.
+ */
+ sc->sc_if.if_opackets++;
+ sc->sc_if.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);
+ }
+ m_freem(m);
+ splx(s);
+ return (0);
+ }
+
+ bcopy(ac->ac_enaddr, src, ETHER_ADDR_LEN);
+ if ((dst_if->if_flags & IFF_RUNNING) == 0) {
+ m_freem(m);
+ splx(s);
+ return (0);
+ }
+ if (IF_QFULL(&dst_if->if_snd)) {
+ sc->sc_if.if_oerrors++;
+ m_freem(m);
+ splx(s);
+ return (0);
+ }
+ sc->sc_if.if_opackets++;
+ sc->sc_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);
+ return (0);
+}
+
+/*
+ * Start output on the bridge. This function should never be called.
+ */
+void
+bridge_start(ifp)
+ struct ifnet *ifp;
+{
+}
+
+/*
+ * Receive input from an interface. Rebroadcast if necessary to other
+ * bridge members.
+ */
+struct mbuf *
+bridge_input(ifp, eh, m)
+ struct ifnet *ifp;
+ struct ether_header *eh;
+ struct mbuf *m;
+{
+ struct bridge_softc *sc;
+ struct arpcom *ac;
+ struct ether_addr *dst, *src;
+ struct ifnet *dst_if;
+ int s;
+
+ /*
+ * Make sure this interface is a bridge member.
+ */
+ if (ifp == NULL || ifp->if_bridge == NULL || m == NULL)
+ return (m);
+
+ sc = (struct bridge_softc *)ifp->if_bridge;
+
+ s = splimp();
+
+ /*
+ * See if we're running.
+ */
+ if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) {
+ splx(s);
+ return (m);
+ }
+
+ sc->sc_if.if_lastchange = time;
+ sc->sc_if.if_ipackets++;
+ sc->sc_if.if_ibytes += m->m_pkthdr.len;
+
+ /*
+ * See if the destination of this frame matches the interface
+ * it came in on. If so, we don't need to do anything.
+ */
+ ac = (struct arpcom *)ifp;
+ if (bcmp(ac->ac_enaddr, eh->ether_dhost, ETHER_ADDR_LEN) == 0) {
+ splx(s);
+ return (m);
+ }
+
+ dst = (struct ether_addr *)&eh->ether_dhost[0];
+ src = (struct ether_addr *)&eh->ether_shost[0];
+
+ /*
+ * If source address is not broadcast or multicast, record
+ * it's address.
+ */
+ if ((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, ifp);
+
+ /*
+ * If packet is unicast, destined for someone on "this"
+ * side of the bridge, drop it.
+ */
+ dst_if = bridge_rtlookup(sc, dst);
+ if ((m->m_flags & (M_BCAST|M_MCAST)) == 0 && dst_if == ifp) {
+ m_freem(m);
+ splx(s);
+ return (NULL);
+ }
+
+ /*
+ * 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)) {
+ splx(s);
+ return (m);
+ }
+ if (sc->sc_if.if_flags & IFF_LINK0 &&
+ ETHERADDR_IS_IP_MCAST(dst)) {
+ splx(s);
+ return (m);
+ }
+ if (sc->sc_if.if_flags & IFF_LINK1 &&
+ !ETHERADDR_IS_IP_MCAST(dst)) {
+ splx(s);
+ return (m);
+ }
+ }
+
+#if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM))
+ /*
+ * Pass the packet to the ip filtering code and drop
+ * here if necessary.
+ */
+ if (bridge_filter(sc, ifp, eh, m) == BRIDGE_FILTER_DROP) {
+ if (m->m_flags & (M_BCAST|M_MCAST)) {
+ /*
+ * Broadcasts should be passed down if filtered
+ * by the bridge, so that they can be filtered
+ * by the interface itself.
+ */
+ splx(s);
+ return (m);
+ }
+ m_freem(m);
+ splx(s);
+ return (NULL);
+ }
+#endif
+
+ /*
+ * If the packet is a multicast or broadcast, then forward it
+ * and pass it up to our higher layers.
+ */
+ if (m->m_flags & (M_BCAST|M_MCAST)) {
+ ifp->if_imcasts++;
+ m = bridge_broadcast(sc, ifp, eh, m);
+ splx(s);
+ return (m);
+ }
+
+ /*
+ * If sucessful lookup, forward packet to that interface only.
+ */
+ if (dst_if != NULL) {
+ /*
+ * Make sure target interface is running.
+ */
+ if ((dst_if->if_flags & IFF_RUNNING) == 0) {
+ m_freem(m);
+ splx(s);
+ return (NULL);
+ }
+
+ /*
+ * Make sure the interface has room in its queue.
+ */
+ if (IF_QFULL(&dst_if->if_snd)) {
+ sc->sc_if.if_oerrors++;
+ m_freem(m);
+ splx(s);
+ return (NULL);
+ }
+
+ /*
+ * Prepend the ethernet header on to the buffer.
+ */
+ M_PREPEND(m, sizeof(*eh), M_DONTWAIT);
+ if (m == NULL) {
+ sc->sc_if.if_oerrors++;
+ return (NULL);
+ }
+ *mtod(m, struct ether_header *) = *eh;
+
+ /*
+ * Update statistics.
+ */
+ sc->sc_if.if_opackets++;
+ sc->sc_if.if_obytes += m->m_pkthdr.len;
+
+ /*
+ * Put it in the interface queue and start transmission.
+ */
+ IF_ENQUEUE(&dst_if->if_snd, m);
+ if ((dst_if->if_flags & IFF_OACTIVE) == 0)
+ (*dst_if->if_start)(dst_if);
+
+ splx(s);
+ return (NULL);
+ }
+
+ /*
+ * Packet must be forwarded to all interfaces.
+ */
+ m = bridge_broadcast(sc, ifp, eh, m);
+ splx(s);
+ return (m);
+}
+
+/*
+ * 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.
+ */
+struct mbuf *
+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;
+
+ /*
+ * Tack on ethernet header
+ */
+ M_PREPEND(m, sizeof(*eh), M_DONTWAIT);
+ if (m == NULL)
+ return (NULL);
+ *mtod(m, struct ether_header *) = *eh;
+
+ 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;
+
+ /*
+ * Make sure target interface is actually running.
+ */
+ if ((p->ifp->if_flags & IFF_RUNNING) == 0)
+ continue;
+
+ /*
+ * Make sure the interface has room in its queue.
+ */
+ if (IF_QFULL(&p->ifp->if_snd)) {
+ sc->sc_if.if_oerrors++;
+ continue;
+ }
+
+ /*
+ * Make a copy of the packet.
+ */
+ mc = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
+ if (mc == NULL) {
+ sc->sc_if.if_oerrors++;
+ continue;
+ }
+
+ /*
+ * Update statistics
+ */
+ sc->sc_if.if_opackets++;
+ sc->sc_if.if_obytes += m->m_pkthdr.len;
+ if ((eh->ether_shost[0] & 1) == 0)
+ ifp->if_omcasts++;
+
+ /*
+ * Put it in interface queue and start transmission.
+ */
+ IF_ENQUEUE(&p->ifp->if_snd, mc);
+ if ((p->ifp->if_flags & IFF_OACTIVE) == 0)
+ (*p->ifp->if_start)(p->ifp);
+ }
+
+ /*
+ * Strip header back off
+ */
+ m_adj(m, sizeof(struct ether_header));
+ return (m);
+}
+
+struct ifnet *
+bridge_rtupdate(sc, ea, ifp)
+ struct bridge_softc *sc;
+ struct ether_addr *ea;
+ struct ifnet *ifp;
+{
+ struct bridge_rtnode *p, *q;
+ u_int32_t h;
+ int s, dir;
+
+ s = splhigh();
+ if (sc->sc_rts == NULL) {
+ splx(s);
+ return (NULL);
+ }
+
+ h = bridge_hash(ea);
+ p = LIST_FIRST(&sc->sc_rts[h]);
+ if (p == NULL) {
+ p = (struct bridge_rtnode *)
+ malloc(sizeof(struct bridge_rtnode),
+ M_DEVBUF, M_NOWAIT);
+ if (p == NULL) {
+ splx(s);
+ return (NULL);
+ }
+
+ bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
+ p->brt_if = ifp;
+ p->brt_age = 1;
+ LIST_INSERT_HEAD(&sc->sc_rts[h], p, brt_next);
+ splx(s);
+ return (ifp);
+ }
+
+ do {
+ q = p;
+ p = LIST_NEXT(p, brt_next);
+
+ dir = bcmp(ea, &q->brt_addr, sizeof(q->brt_addr));
+ if (dir == 0) {
+ q->brt_if = ifp;
+ q->brt_age = 1;
+ splx(s);
+ return (ifp);
+ }
+
+ if (dir > 0) {
+ p = (struct bridge_rtnode *)
+ malloc(sizeof(struct bridge_rtnode),
+ M_DEVBUF, M_NOWAIT);
+ if (p == NULL) {
+ splx(s);
+ return (NULL);
+ }
+
+ bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
+ p->brt_if = ifp;
+ p->brt_age = 1;
+ LIST_INSERT_BEFORE(q, p, brt_next);
+ splx(s);
+ return (ifp);
+ }
+
+ if (p == NULL) {
+ p = (struct bridge_rtnode *)
+ malloc(sizeof(struct bridge_rtnode),
+ M_DEVBUF, M_NOWAIT);
+ if (p == NULL) {
+ splx(s);
+ return (NULL);
+ }
+
+ bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
+ p->brt_if = ifp;
+ p->brt_age = 1;
+ LIST_INSERT_AFTER(q, p, brt_next);
+ splx(s);
+ return (ifp);
+ }
+ } while (p != NULL);
+
+ splx(s);
+ return (NULL);
+}
+
+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) {
+ splx(s);
+ return (NULL);
+ }
+
+ h = bridge_hash(ea);
+ p = LIST_FIRST(&sc->sc_rts[h]);
+ while (p != NULL) {
+ dir = bcmp(ea, &p->brt_addr, sizeof(p->brt_addr));
+ if (dir == 0) {
+ splx(s);
+ return (p->brt_if);
+ }
+
+ if (dir > 0) {
+ splx(s);
+ return (NULL);
+ }
+
+ p = LIST_NEXT(p, brt_next);
+ }
+ 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);
+}
+
+void
+bridge_rtage(vsc)
+ void *vsc;
+{
+ struct bridge_softc *sc = (struct bridge_softc *)vsc;
+ struct bridge_rtnode *n, *p;
+ int s;
+
+ s = splhigh();
+ if (sc->sc_rts == NULL) {
+ splx(s);
+ return;
+ }
+
+ n = LIST_FIRST(&sc->sc_rts[sc->sc_brtageidx]);
+ while (n != NULL) {
+ if (n->brt_age) {
+ n->brt_age = 0;
+ n = LIST_NEXT(n, brt_next);
+ }
+ else {
+ p = LIST_NEXT(n, brt_next);
+#if 1
+ printf("RTAGE(%s,%x:%x:%x:%x:%x:%x)\n",
+ n->brt_if->if_xname,
+ n->brt_addr.ether_addr_octet[0],
+ n->brt_addr.ether_addr_octet[1],
+ n->brt_addr.ether_addr_octet[2],
+ n->brt_addr.ether_addr_octet[3],
+ n->brt_addr.ether_addr_octet[4],
+ n->brt_addr.ether_addr_octet[5]);
+#endif
+ LIST_REMOVE(n, brt_next);
+ free(n, M_DEVBUF);
+ n = p;
+ }
+ }
+ sc->sc_brtageidx = (sc->sc_brtageidx + 1) % BRIDGE_RTABLE_SIZE;
+ splx(s);
+}
+
+/*
+ * Delete routes to a specific interface member.
+ */
+void
+bridge_rtdelete(sc, ifp)
+ struct bridge_softc *sc;
+ struct ifnet *ifp;
+{
+ int i, s;
+ struct bridge_rtnode *n, *p;
+
+ s = splhigh();
+ if (sc->sc_rts == NULL) {
+ splx(s);
+ return;
+ }
+
+ /*
+ * Loop through all of the hash buckets and traverse each
+ * chain looking for routes to this interface.
+ */
+ for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
+ n = LIST_FIRST(&sc->sc_rts[i]);
+ while (n != NULL) {
+ if (n->brt_if == ifp) { /* found one */
+ p = LIST_NEXT(n, brt_next);
+ LIST_REMOVE(n, brt_next);
+ free(n, M_DEVBUF);
+ n = p;
+ }
+ else
+ n = LIST_NEXT(n, brt_next);
+ }
+ }
+
+ splx(s);
+}
+
+/*
+ * Find the route for a given index value in rq->index.
+ * Returns 0 on success, otherwise an error value.
+ */
+int
+bridge_rtfind(sc, rq)
+ struct bridge_softc *sc;
+ struct ifbrtreq *rq;
+{
+ int i, s;
+ u_int32_t cnt;
+ struct bridge_rtnode *n;
+
+ s = splhigh();
+ if (sc->sc_rts == NULL) {
+ splx(s);
+ return (ENOENT);
+ }
+
+ for (cnt = 0, i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
+ n = LIST_FIRST(&sc->sc_rts[i]);
+ while (n != NULL) {
+ if (cnt == rq->index) {
+ bcopy(&n->brt_addr, &rq->dst, sizeof(rq->dst));
+ bcopy(n->brt_if->if_xname, rq->ifsname,
+ IFNAMSIZ);
+ rq->age = n->brt_age;
+ splx(s);
+ return (0);
+ }
+ n = LIST_NEXT(n, brt_next);
+ cnt++;
+ }
+ }
+ splx(s);
+ return (ENOENT);
+}
+
+#if defined(INET) && (defined(IPFILTER) || defined(IPFILTER_LKM))
+/*
+ * 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, n)
+ struct bridge_softc *sc;
+ struct ifnet *ifp;
+ struct ether_header *eh;
+ struct mbuf *n;
+{
+ struct mbuf *m, *m0;
+ struct ip *ip;
+ u_int16_t etype;
+ int hlen, r;
+
+ if (fr_checkp == NULL)
+ return (BRIDGE_FILTER_PASS);
+
+ /*
+ * XXX TODO: Handle LSAP packets
+ */
+ etype = ntohs(eh->ether_type);
+ if (etype != ETHERTYPE_IP)
+ return (BRIDGE_FILTER_PASS);
+
+ /*
+ * 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.
+ */
+ m = m_copym2(n, 0, M_COPYALL, M_NOWAIT);
+ if (m == NULL)
+ return (BRIDGE_FILTER_DROP);
+
+ m->m_pkthdr.rcvif = &sc->sc_if;
+
+ /*
+ * 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);
+ }
+
+ /*
+ * Examine the ip header, and drop invalid packets
+ */
+ 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;
+ }
+ ip = mtod(m, struct ip *);
+ }
+ if ((ip->ip_sum = in_cksum(m, hlen)) != 0) {
+ r = BRIDGE_FILTER_DROP;
+ goto out;
+ }
+
+ NTOHS(ip->ip_len);
+ if (ip->ip_len < hlen) {
+ r = BRIDGE_FILTER_DROP;
+ goto out;
+ }
+ 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) {
+ if (m->m_len == m->m_pkthdr.len) {
+ m->m_len = ip->ip_len;
+ m->m_pkthdr.len = ip->ip_len;
+ }
+ else
+ m_adj(m, ip->ip_len - m->m_pkthdr.len);
+ }
+
+ /*
+ * Finally, we get to filter the packet!
+ */
+ m0 = m;
+ if (fr_checkp && (*fr_checkp)(ip, hlen, m->m_pkthdr.rcvif, 0, &m0))
+ return (BRIDGE_FILTER_DROP);
+ ip = mtod(m = m0, struct ip *);
+ r = BRIDGE_FILTER_PASS;
+
+out:
+ m_freem(m);
+ return (r);
+}
+#endif
+
+#endif /* NBRIDGE */
diff --git a/sys/net/if_bridge.h b/sys/net/if_bridge.h
new file mode 100644
index 00000000000..d127c091604
--- /dev/null
+++ b/sys/net/if_bridge.h
@@ -0,0 +1,59 @@
+/* $OpenBSD: if_bridge.h,v 1.1 1999/02/26 17:01:32 jason Exp $ */
+
+/*
+ * Copyright (c) 1999 Jason L. Wright (jason@thought.net)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jason L. Wright
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Bridge control request: (add/delete/iterate) member interfaces.
+ */
+struct ifbreq {
+ char ifbname[IFNAMSIZ]; /* bridge ifs name */
+ char ifsname[IFNAMSIZ]; /* member ifs name */
+ u_int32_t index; /* iteration index */
+};
+
+/*
+ * Bridge routing request: iterate known routes.
+ */
+struct ifbrtreq {
+ char ifbname[IFNAMSIZ]; /* bridge ifs name */
+ u_int32_t index; /* iteration index */
+ struct ether_addr dst; /* destination addr */
+ char ifsname[IFNAMSIZ]; /* destination ifs */
+ u_int16_t age; /* route age */
+};
+
+#ifdef _KERNEL
+struct mbuf * bridge_input __P((struct ifnet *, struct ether_header *,
+ struct mbuf *));
+int bridge_output __P((struct ifnet *, struct mbuf *,
+ struct sockaddr *, struct rtentry *rt));
+#endif /* _KERNEL */
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c
index 34d489de121..e7b8b53e452 100644
--- a/sys/net/if_ethersubr.c
+++ b/sys/net/if_ethersubr.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_ethersubr.c,v 1.27 1999/01/08 00:56:45 deraadt Exp $ */
+/* $OpenBSD: if_ethersubr.c,v 1.28 1999/02/26 17:01:32 jason Exp $ */
/* $NetBSD: if_ethersubr.c,v 1.19 1996/05/07 02:40:30 thorpej Exp $ */
/*
@@ -74,6 +74,11 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#endif
#include <netinet/if_ether.h>
+#include "bridge.h"
+#if NBRIDGE > 0
+#include <net/if_bridge.h>
+#endif
+
#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
@@ -451,6 +456,18 @@ ether_output(ifp, m0, dst, rt0)
bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
bcopy((caddr_t)ac->ac_enaddr, (caddr_t)eh->ether_shost,
sizeof(eh->ether_shost));
+
+#if NBRIDGE > 0
+ /*
+ * Interfaces that are bridge members need special handling
+ * for output.
+ */
+ if (ifp->if_bridge) {
+ bridge_output(ifp, m, NULL, NULL);
+ return (error);
+ }
+#endif
+
s = splimp();
/*
* Queue message on interface, and start output if interface
@@ -491,9 +508,7 @@ ether_input(ifp, eh, m)
u_int16_t etype;
int s, llcfound = 0;
register struct llc *l;
-#if defined(ISO)
struct arpcom *ac = (struct arpcom *)ifp;
-#endif
if ((ifp->if_flags & IFF_UP) == 0) {
m_freem(m);
@@ -511,6 +526,32 @@ ether_input(ifp, eh, m)
if (m->m_flags & (M_BCAST|M_MCAST))
ifp->if_imcasts++;
+#if NBRIDGE > 0
+ /*
+ * Tap the packet off here for a bridge, if configured and
+ * active for this interface. bridge_input returns
+ * NULL if it has consumed the packet, otherwise, it
+ * gets processed as normal.
+ */
+ if (ifp->if_bridge) {
+ m = bridge_input(ifp, eh, m);
+ if (m == NULL)
+ return;
+ }
+#endif
+ /*
+ * If packet is unicast and we're in promiscuous mode, make sure it
+ * is for us. Drop otherwise.
+ */
+ if ((m->m_flags & (M_BCAST|M_MCAST)) == 0 &&
+ (ifp->if_flags & IFF_PROMISC)) {
+ if (bcmp(ac->ac_enaddr, (caddr_t)eh->ether_dhost,
+ ETHER_ADDR_LEN)) {
+ m_freem(m);
+ return;
+ }
+ }
+
decapsulate:
etype = ntohs(eh->ether_type);
switch (etype) {