Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 17 Apr 2004 15:57:58 +1200
From:      Andrew Thompson <andy@fud.org.nz>
To:        current@freebsd.org
Subject:   RFC: ported NetBSD if_bridge
Message-ID:  <20040417035758.GA66806@kate.fud.org.nz>

next in thread | raw e-mail | index | archive | help

--MGYHOYXEY6WxJCY8
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hi,


I have ported over the bridging code from NetBSD and am looking for feedback.
My main question is, 'do people want this in the tree?'


The benefits over the current bridge are:
 * ability to manage the bridge table
 * spanning tree support
 * the snazzy brconfig utility
 * clonable pseudo-interface (is that a benefit?)


I need a bit of help on the following:
 * testing
 * the header includes, I have made a mess of them :)
 * locking?
 * updating the brconfig man page
 * some way to support the sysctl net.link.ether.bridge.config (?)


You will need to rebuild your kernel as some structs have changed, then
# cd /sys/modules/if_bridge
# make && make install
# cd /usr/src/sbin/brconfig
# make && make install
# kldload if_bridge
# ifconfig bridge0 create
# brconfig bridge0 add <if1> add <if2>


The attached diff is for todays current, and is also available @
http://www.fud.org.nz/~andy/if_bridge.diff


Comments welcome, good or bad.


cheers,

Andrew

--MGYHOYXEY6WxJCY8
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="if_bridge.diff"

diff -urN sys.orig/modules/if_bridge/Makefile sys/modules/if_bridge/Makefile
--- sys.orig/modules/if_bridge/Makefile	Thu Jan  1 12:00:00 1970
+++ sys/modules/if_bridge/Makefile	Sat Apr 17 15:27:12 2004
@@ -0,0 +1,8 @@
+# $FreeBSD: src/sys/modules/bridge/Makefile,v 1.3 2003/09/23 17:54:04 sam Exp $
+
+.PATH:	${.CURDIR}/../../net
+KMOD=	if_bridge
+SRCS=	if_bridge.c
+SRCS+=	bridgestp.c
+
+.include <bsd.kmod.mk>
diff -urN sys.orig/net/bridgestp.c sys/net/bridgestp.c
--- sys.orig/net/bridgestp.c	Thu Jan  1 12:00:00 1970
+++ sys/net/bridgestp.c	Sat Apr 17 13:19:41 2004
@@ -0,0 +1,1119 @@
+/*	$NetBSD: bridgestp.c,v 1.5 2003/11/28 08:56:48 keihan Exp $	*/
+
+/*
+ * Copyright (c) 2000 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.
+ *
+ * OpenBSD: bridgestp.c,v 1.5 2001/03/22 03:48:29 jason Exp
+ */
+
+/*
+ * Implementation of the spanning tree protocol as defined in
+ * ISO/IEC Final DIS 15802-3 (IEEE P802.1D/D17), May 25, 1998.
+ * (In English: IEEE 802.1D, Draft 17, 1998)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/kernel.h>
+#include <sys/callout.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/if_llc.h>
+#include <net/if_media.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <net/if_bridgevar.h>
+
+/* BPDU message types */
+#define	BSTP_MSGTYPE_CFG	0x00		/* Configuration */
+#define	BSTP_MSGTYPE_TCN	0x80		/* Topology chg notification */
+
+/* BPDU flags */
+#define	BSTP_FLAG_TC		0x01		/* Topology change */
+#define	BSTP_FLAG_TCA		0x80		/* Topology change ack */
+
+#define	BSTP_MESSAGE_AGE_INCR	(1 * 256)	/* in 256ths of a second */
+#define	BSTP_TICK_VAL		(1 * 256)	/* in 256ths of a second */
+
+/*
+ * Because BPDU's do not make nicely aligned structures, two different
+ * declarations are used: bstp_?bpdu (wire representation, packed) and
+ * bstp_*_unit (internal, nicely aligned version).
+ */
+
+/* configuration bridge protocol data unit */
+struct bstp_cbpdu {
+	uint8_t		cbu_dsap;		/* LLC: destination sap */
+	uint8_t		cbu_ssap;		/* LLC: source sap */
+	uint8_t		cbu_ctl;		/* LLC: control */
+	uint16_t	cbu_protoid;		/* protocol id */
+	uint8_t		cbu_protover;		/* protocol version */
+	uint8_t		cbu_bpdutype;		/* message type */
+	uint8_t		cbu_flags;		/* flags (below) */
+
+	/* root id */
+	uint16_t	cbu_rootpri;		/* root priority */
+	uint8_t	cbu_rootaddr[6];	/* root address */
+
+	uint32_t	cbu_rootpathcost;	/* root path cost */
+
+	/* bridge id */
+	uint16_t	cbu_bridgepri;		/* bridge priority */
+	uint8_t		cbu_bridgeaddr[6];	/* bridge address */
+
+	uint16_t	cbu_portid;		/* port id */
+	uint16_t	cbu_messageage;		/* current message age */
+	uint16_t	cbu_maxage;		/* maximum age */
+	uint16_t	cbu_hellotime;		/* hello time */
+	uint16_t	cbu_forwarddelay;	/* forwarding delay */
+} __attribute__((__packed__));
+
+/* topology change notification bridge protocol data unit */
+struct bstp_tbpdu {
+	uint8_t		tbu_dsap;		/* LLC: destination sap */
+	uint8_t		tbu_ssap;		/* LLC: source sap */
+	uint8_t		tbu_ctl;		/* LLC: control */
+	uint16_t	tbu_protoid;		/* protocol id */
+	uint8_t		tbu_protover;		/* protocol version */
+	uint8_t		tbu_bpdutype;		/* message type */
+} __attribute__((__packed__));
+
+const uint8_t bstp_etheraddr[] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+
+void	bstp_initialize_port(struct bridge_softc *, struct bridge_iflist *);
+void	bstp_ifupdstatus(struct bridge_softc *, struct bridge_iflist *);
+void	bstp_enable_port(struct bridge_softc *, struct bridge_iflist *);
+void	bstp_disable_port(struct bridge_softc *, struct bridge_iflist *);
+void	bstp_enable_change_detection(struct bridge_iflist *);
+void	bstp_disable_change_detection(struct bridge_iflist *);
+int	bstp_root_bridge(struct bridge_softc *sc);
+int	bstp_supersedes_port_info(struct bridge_softc *,
+	    struct bridge_iflist *, struct bstp_config_unit *);
+int	bstp_designated_port(struct bridge_softc *, struct bridge_iflist *);
+int	bstp_designated_for_some_port(struct bridge_softc *);
+void	bstp_transmit_config(struct bridge_softc *, struct bridge_iflist *);
+void	bstp_transmit_tcn(struct bridge_softc *);
+void	bstp_received_config_bpdu(struct bridge_softc *,
+	    struct bridge_iflist *, struct bstp_config_unit *);
+void	bstp_received_tcn_bpdu(struct bridge_softc *, struct bridge_iflist *,
+	    struct bstp_tcn_unit *);
+void	bstp_record_config_information(struct bridge_softc *,
+	    struct bridge_iflist *, struct bstp_config_unit *);
+void	bstp_record_config_timeout_values(struct bridge_softc *,
+	    struct bstp_config_unit *);
+void	bstp_config_bpdu_generation(struct bridge_softc *);
+void	bstp_send_config_bpdu(struct bridge_softc *, struct bridge_iflist *,
+	    struct bstp_config_unit *);
+void	bstp_configuration_update(struct bridge_softc *);
+void	bstp_root_selection(struct bridge_softc *);
+void	bstp_designated_port_selection(struct bridge_softc *);
+void	bstp_become_designated_port(struct bridge_softc *,
+	    struct bridge_iflist *);
+void	bstp_port_state_selection(struct bridge_softc *);
+void	bstp_make_forwarding(struct bridge_softc *, struct bridge_iflist *);
+void	bstp_make_blocking(struct bridge_softc *, struct bridge_iflist *);
+void	bstp_set_port_state(struct bridge_iflist *, uint8_t);
+void	bstp_set_bridge_priority(struct bridge_softc *, uint64_t);
+void	bstp_set_port_priority(struct bridge_softc *, struct bridge_iflist *,
+	    uint16_t);
+void	bstp_set_path_cost(struct bridge_softc *, struct bridge_iflist *,
+	    uint32_t);
+void	bstp_topology_change_detection(struct bridge_softc *);
+void	bstp_topology_change_acknowledged(struct bridge_softc *);
+void	bstp_acknowledge_topology_change(struct bridge_softc *,
+	    struct bridge_iflist *);
+
+void	bstp_tick(void *);
+void	bstp_timer_start(struct bridge_timer *, uint16_t);
+void	bstp_timer_stop(struct bridge_timer *);
+int	bstp_timer_expired(struct bridge_timer *, uint16_t);
+
+void	bstp_hold_timer_expiry(struct bridge_softc *, struct bridge_iflist *);
+void	bstp_message_age_timer_expiry(struct bridge_softc *,
+	    struct bridge_iflist *);
+void	bstp_forward_delay_timer_expiry(struct bridge_softc *,
+	    struct bridge_iflist *);
+void	bstp_topology_change_timer_expiry(struct bridge_softc *);
+void	bstp_tcn_timer_expiry(struct bridge_softc *);
+void	bstp_hello_timer_expiry(struct bridge_softc *);
+
+void
+bstp_transmit_config(struct bridge_softc *sc, struct bridge_iflist *bif)
+{
+	if (bif->bif_hold_timer.active) {
+		bif->bif_config_pending = 1;
+		return;
+	}
+
+	bif->bif_config_bpdu.cu_message_type = BSTP_MSGTYPE_CFG;
+	bif->bif_config_bpdu.cu_rootid = sc->sc_designated_root;
+	bif->bif_config_bpdu.cu_root_path_cost = sc->sc_root_path_cost;
+	bif->bif_config_bpdu.cu_bridge_id = sc->sc_bridge_id;
+	bif->bif_config_bpdu.cu_port_id = bif->bif_port_id;
+
+	if (bstp_root_bridge(sc))
+		bif->bif_config_bpdu.cu_message_age = 0;
+	else
+		bif->bif_config_bpdu.cu_message_age =
+		    sc->sc_root_port->bif_message_age_timer.value +
+		    BSTP_MESSAGE_AGE_INCR;
+
+	bif->bif_config_bpdu.cu_max_age = sc->sc_max_age;
+	bif->bif_config_bpdu.cu_hello_time = sc->sc_hello_time;
+	bif->bif_config_bpdu.cu_forward_delay = sc->sc_forward_delay;
+	bif->bif_config_bpdu.cu_topology_change_acknowledgment
+	    = bif->bif_topology_change_acknowledge;
+	bif->bif_config_bpdu.cu_topology_change = sc->sc_topology_change;
+
+	if (bif->bif_config_bpdu.cu_message_age < sc->sc_max_age) {
+		bif->bif_topology_change_acknowledge = 0;
+		bif->bif_config_pending = 0;
+		bstp_send_config_bpdu(sc, bif, &bif->bif_config_bpdu);
+		bstp_timer_start(&bif->bif_hold_timer, 0);
+	}
+}
+
+void
+bstp_send_config_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif,
+    struct bstp_config_unit *cu)
+{
+	struct ifnet *ifp;
+	struct mbuf *m;
+	struct ether_header *eh;
+	struct bstp_cbpdu bpdu;
+	int s;
+
+	ifp = bif->bif_ifp;
+
+	if ((ifp->if_flags & IFF_RUNNING) == 0)
+		return;
+
+	MGETHDR(m, M_DONTWAIT, MT_DATA);
+	if (m == NULL)
+		return;
+
+	eh = mtod(m, struct ether_header *);
+
+	m->m_pkthdr.rcvif = ifp;
+	m->m_pkthdr.len = sizeof(*eh) + sizeof(bpdu);
+	m->m_len = m->m_pkthdr.len;
+
+	bpdu.cbu_ssap = bpdu.cbu_dsap = LLC_8021D_LSAP;
+	bpdu.cbu_ctl = LLC_UI;
+	bpdu.cbu_protoid = htons(0);
+	bpdu.cbu_protover = 0;
+	bpdu.cbu_bpdutype = cu->cu_message_type;
+	bpdu.cbu_flags = (cu->cu_topology_change ? BSTP_FLAG_TC : 0) |
+	    (cu->cu_topology_change_acknowledgment ? BSTP_FLAG_TCA : 0);
+
+	bpdu.cbu_rootpri = htons(cu->cu_rootid >> 48);
+	bpdu.cbu_rootaddr[0] = cu->cu_rootid >> 40;
+	bpdu.cbu_rootaddr[1] = cu->cu_rootid >> 32;
+	bpdu.cbu_rootaddr[2] = cu->cu_rootid >> 24;
+	bpdu.cbu_rootaddr[3] = cu->cu_rootid >> 16;
+	bpdu.cbu_rootaddr[4] = cu->cu_rootid >> 8;
+	bpdu.cbu_rootaddr[5] = cu->cu_rootid >> 0;
+
+	bpdu.cbu_rootpathcost = htonl(cu->cu_root_path_cost);
+
+	bpdu.cbu_bridgepri = htons(cu->cu_rootid >> 48);
+	bpdu.cbu_bridgeaddr[0] = cu->cu_rootid >> 40;
+	bpdu.cbu_bridgeaddr[1] = cu->cu_rootid >> 32;
+	bpdu.cbu_bridgeaddr[2] = cu->cu_rootid >> 24;
+	bpdu.cbu_bridgeaddr[3] = cu->cu_rootid >> 16;
+	bpdu.cbu_bridgeaddr[4] = cu->cu_rootid >> 8;
+	bpdu.cbu_bridgeaddr[5] = cu->cu_rootid >> 0;
+
+	bpdu.cbu_portid = htons(cu->cu_port_id);
+	bpdu.cbu_messageage = htons(cu->cu_message_age);
+	bpdu.cbu_maxage = htons(cu->cu_max_age);
+	bpdu.cbu_hellotime = htons(cu->cu_hello_time);
+	bpdu.cbu_forwarddelay = htons(cu->cu_forward_delay);
+
+	memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN);
+	memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN);
+	eh->ether_type = htons(sizeof(bpdu));
+
+	memcpy(mtod(m, caddr_t) + sizeof(*eh), &bpdu, sizeof(bpdu));
+
+	s = splnet();
+	bridge_enqueue(sc, ifp, m, 0);
+	splx(s);
+}
+
+int
+bstp_root_bridge(struct bridge_softc *sc)
+{
+	return (sc->sc_designated_root == sc->sc_bridge_id);
+}
+
+int
+bstp_supersedes_port_info(struct bridge_softc *sc, struct bridge_iflist *bif,
+    struct bstp_config_unit *cu)
+{
+	if (cu->cu_rootid < bif->bif_designated_root)
+		return (1);
+	if (cu->cu_rootid > bif->bif_designated_root)
+		return (0);
+
+	if (cu->cu_root_path_cost < bif->bif_designated_cost)
+		return (1);
+	if (cu->cu_root_path_cost > bif->bif_designated_cost)
+		return (0);
+
+	if (cu->cu_bridge_id < bif->bif_designated_bridge)
+		return (1);
+	if (cu->cu_bridge_id > bif->bif_designated_bridge)
+		return (0);
+
+	if (sc->sc_bridge_id != cu->cu_bridge_id)
+		return (1);
+	if (cu->cu_port_id <= bif->bif_designated_port)
+		return (1);
+	return (0);
+}
+
+void
+bstp_record_config_information(struct bridge_softc *sc,
+    struct bridge_iflist *bif, struct bstp_config_unit *cu)
+{
+	bif->bif_designated_root = cu->cu_rootid;
+	bif->bif_designated_cost = cu->cu_root_path_cost;
+	bif->bif_designated_bridge = cu->cu_bridge_id;
+	bif->bif_designated_port = cu->cu_port_id;
+	bstp_timer_start(&bif->bif_message_age_timer, cu->cu_message_age);
+}
+
+void
+bstp_record_config_timeout_values(struct bridge_softc *sc,
+    struct bstp_config_unit *config)
+{
+	sc->sc_max_age = config->cu_max_age;
+	sc->sc_hello_time = config->cu_hello_time;
+	sc->sc_forward_delay = config->cu_forward_delay;
+	sc->sc_topology_change = config->cu_topology_change;
+}
+
+void
+bstp_config_bpdu_generation(struct bridge_softc *sc)
+{
+	struct bridge_iflist *bif;
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if ((bif->bif_flags & IFBIF_STP) == 0)
+			continue;
+		if (bstp_designated_port(sc, bif) &&
+		    (bif->bif_state != BSTP_IFSTATE_DISABLED))
+			bstp_transmit_config(sc, bif);
+	}
+}
+
+int
+bstp_designated_port(struct bridge_softc *sc, struct bridge_iflist *bif)
+{
+	return ((bif->bif_designated_bridge == sc->sc_bridge_id)
+	    && (bif->bif_designated_port == bif->bif_port_id));
+}
+
+void
+bstp_transmit_tcn(struct bridge_softc *sc)
+{
+	struct bstp_tbpdu bpdu;
+	struct bridge_iflist *bif = sc->sc_root_port;
+	struct ifnet *ifp = bif->bif_ifp;
+	struct ether_header *eh;
+	struct mbuf *m;
+	int s;
+
+	if ((ifp->if_flags & IFF_RUNNING) == 0)
+		return;
+
+	MGETHDR(m, M_DONTWAIT, MT_DATA);
+	if (m == NULL)
+		return;
+
+	m->m_pkthdr.rcvif = ifp;
+	m->m_pkthdr.len = sizeof(*eh) + sizeof(bpdu);
+	m->m_len = m->m_pkthdr.len;
+
+	eh = mtod(m, struct ether_header *);
+
+	memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN);
+	memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN);
+	eh->ether_type = htons(sizeof(bpdu));
+
+	bpdu.tbu_ssap = bpdu.tbu_dsap = LLC_8021D_LSAP;
+	bpdu.tbu_ctl = LLC_UI;
+	bpdu.tbu_protoid = 0;
+	bpdu.tbu_protover = 0;
+	bpdu.tbu_bpdutype = BSTP_MSGTYPE_TCN;
+
+	memcpy(mtod(m, caddr_t) + sizeof(*eh), &bpdu, sizeof(bpdu));
+
+	s = splnet();
+	bridge_enqueue(sc, ifp, m, 0);
+	splx(s);
+}
+
+void
+bstp_configuration_update(struct bridge_softc *sc)
+{
+	bstp_root_selection(sc);
+	bstp_designated_port_selection(sc);
+}
+
+void
+bstp_root_selection(struct bridge_softc *sc)
+{
+	struct bridge_iflist *root_port = NULL, *bif;
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if ((bif->bif_flags & IFBIF_STP) == 0)
+			continue;
+		if (bstp_designated_port(sc, bif))
+			continue;
+		if (bif->bif_state == BSTP_IFSTATE_DISABLED)
+			continue;
+		if (bif->bif_designated_root >= sc->sc_bridge_id)
+			continue;
+		if (root_port == NULL)
+			goto set_port;
+
+		if (bif->bif_designated_root < root_port->bif_designated_root)
+			goto set_port;
+		if (bif->bif_designated_root > root_port->bif_designated_root)
+			continue;
+
+		if ((bif->bif_designated_cost + bif->bif_path_cost) <
+		    (root_port->bif_designated_cost + root_port->bif_path_cost))
+			goto set_port;
+		if ((bif->bif_designated_cost + bif->bif_path_cost) >
+		    (root_port->bif_designated_cost + root_port->bif_path_cost))
+			continue;
+
+		if (bif->bif_designated_bridge <
+		    root_port->bif_designated_bridge)
+			goto set_port;
+		if (bif->bif_designated_bridge >
+		    root_port->bif_designated_bridge)
+			continue;
+
+		if (bif->bif_designated_port < root_port->bif_designated_port)
+			goto set_port;
+		if (bif->bif_designated_port > root_port->bif_designated_port)
+			continue;
+
+		if (bif->bif_port_id >= root_port->bif_port_id)
+			continue;
+set_port:
+		root_port = bif;
+	}
+
+	sc->sc_root_port = root_port;
+	if (root_port == NULL) {
+		sc->sc_designated_root = sc->sc_bridge_id;
+		sc->sc_root_path_cost = 0;
+	} else {
+		sc->sc_designated_root = root_port->bif_designated_root;
+		sc->sc_root_path_cost = root_port->bif_designated_cost +
+		    root_port->bif_path_cost;
+	}
+}
+
+void
+bstp_designated_port_selection(struct bridge_softc *sc)
+{
+	struct bridge_iflist *bif;
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if ((bif->bif_flags & IFBIF_STP) == 0)
+			continue;
+		if (bstp_designated_port(sc, bif))
+			goto designated;
+		if (bif->bif_designated_root != sc->sc_designated_root)
+			goto designated;
+
+		if (sc->sc_root_path_cost < bif->bif_designated_cost)
+			goto designated;
+		if (sc->sc_root_path_cost > bif->bif_designated_cost)
+			continue;
+
+		if (sc->sc_bridge_id < bif->bif_designated_bridge)
+			goto designated;
+		if (sc->sc_bridge_id > bif->bif_designated_bridge)
+			continue;
+
+		if (bif->bif_port_id > bif->bif_designated_port)
+			continue;
+designated:
+		bstp_become_designated_port(sc, bif);
+	}
+}
+
+void
+bstp_become_designated_port(struct bridge_softc *sc, struct bridge_iflist *bif)
+{
+	bif->bif_designated_root = sc->sc_designated_root;
+	bif->bif_designated_cost = sc->sc_root_path_cost;
+	bif->bif_designated_bridge = sc->sc_bridge_id;
+	bif->bif_designated_port = bif->bif_port_id;
+}
+
+void
+bstp_port_state_selection(struct bridge_softc *sc)
+{
+	struct bridge_iflist *bif;
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if ((bif->bif_flags & IFBIF_STP) == 0)
+			continue;
+		if (bif == sc->sc_root_port) {
+			bif->bif_config_pending = 0;
+			bif->bif_topology_change_acknowledge = 0;
+			bstp_make_forwarding(sc, bif);
+		} else if (bstp_designated_port(sc, bif)) {
+			bstp_timer_stop(&bif->bif_message_age_timer);
+			bstp_make_forwarding(sc, bif);
+		} else {
+			bif->bif_config_pending = 0;
+			bif->bif_topology_change_acknowledge = 0;
+			bstp_make_blocking(sc, bif);
+		}
+	}
+}
+
+void
+bstp_make_forwarding(struct bridge_softc *sc, struct bridge_iflist *bif)
+{
+	if (bif->bif_state == BSTP_IFSTATE_BLOCKING) {
+		bstp_set_port_state(bif, BSTP_IFSTATE_LISTENING);
+		bstp_timer_start(&bif->bif_forward_delay_timer, 0);
+	}
+}
+
+void
+bstp_make_blocking(struct bridge_softc *sc, struct bridge_iflist *bif)
+{
+	if ((bif->bif_state != BSTP_IFSTATE_DISABLED) &&
+	    (bif->bif_state != BSTP_IFSTATE_BLOCKING)) {
+		if ((bif->bif_state == BSTP_IFSTATE_FORWARDING) ||
+		    (bif->bif_state == BSTP_IFSTATE_LEARNING)) {
+			if (bif->bif_change_detection_enabled) {
+				bstp_topology_change_detection(sc);
+			}
+		}
+		bstp_set_port_state(bif, BSTP_IFSTATE_BLOCKING);
+		bstp_timer_stop(&bif->bif_forward_delay_timer);
+	}
+}
+
+void
+bstp_set_port_state(struct bridge_iflist *bif, uint8_t state)
+{
+	bif->bif_state = state;
+}
+
+void
+bstp_topology_change_detection(struct bridge_softc *sc)
+{
+	if (bstp_root_bridge(sc)) {
+		sc->sc_topology_change = 1;
+		bstp_timer_start(&sc->sc_topology_change_timer, 0);
+	} else if (!sc->sc_topology_change_detected) {
+		bstp_transmit_tcn(sc);
+		bstp_timer_start(&sc->sc_tcn_timer, 0);
+	}
+	sc->sc_topology_change_detected = 1;
+}
+
+void
+bstp_topology_change_acknowledged(struct bridge_softc *sc)
+{
+	sc->sc_topology_change_detected = 0;
+	bstp_timer_stop(&sc->sc_tcn_timer);
+}
+
+void
+bstp_acknowledge_topology_change(struct bridge_softc *sc,
+    struct bridge_iflist *bif)
+{
+	bif->bif_topology_change_acknowledge = 1;
+	bstp_transmit_config(sc, bif);
+}
+
+struct mbuf *
+bstp_input(struct ifnet *ifp, struct mbuf *m)
+{
+	struct bridge_softc *sc = ifp->if_bridge;
+	struct bridge_iflist *bif = NULL;
+	struct ether_header *eh;
+	struct bstp_tbpdu tpdu;
+	struct bstp_cbpdu cpdu;
+	struct bstp_config_unit cu;
+	struct bstp_tcn_unit tu;
+	uint16_t len;
+
+	eh = mtod(m, struct ether_header *);
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if ((bif->bif_flags & IFBIF_STP) == 0)
+			continue;
+		if (bif->bif_ifp == ifp)
+			break;
+	}
+	if (bif == NULL)
+		goto out;
+
+	len = ntohs(eh->ether_type);
+	if (len < sizeof(tpdu))
+		goto out;
+
+	m_adj(m, ETHER_HDR_LEN);
+
+	if (m->m_pkthdr.len > len)
+		m_adj(m, len - m->m_pkthdr.len);
+	if (m->m_len < sizeof(tpdu) &&
+	    (m = m_pullup(m, sizeof(tpdu))) == NULL)
+		goto out;
+
+	memcpy(&tpdu, mtod(m, caddr_t), sizeof(tpdu));
+
+	if (tpdu.tbu_dsap != LLC_8021D_LSAP ||
+	    tpdu.tbu_ssap != LLC_8021D_LSAP ||
+	    tpdu.tbu_ctl != LLC_UI)
+		goto out;
+	if (tpdu.tbu_protoid != 0 || tpdu.tbu_protover != 0)
+		goto out;
+
+	switch (tpdu.tbu_bpdutype) {
+	case BSTP_MSGTYPE_TCN:
+		tu.tu_message_type = tpdu.tbu_bpdutype;
+		bstp_received_tcn_bpdu(sc, bif, &tu);
+		break;
+	case BSTP_MSGTYPE_CFG:
+		if (m->m_len < sizeof(cpdu) &&
+		    (m = m_pullup(m, sizeof(cpdu))) == NULL)
+			goto out;
+		memcpy(&cpdu, mtod(m, caddr_t), sizeof(cpdu));
+
+		cu.cu_rootid =
+		    (((uint64_t)ntohs(cpdu.cbu_rootpri)) << 48) |
+		    (((uint64_t)cpdu.cbu_rootaddr[0]) << 40) |
+		    (((uint64_t)cpdu.cbu_rootaddr[1]) << 32) |
+		    (((uint64_t)cpdu.cbu_rootaddr[2]) << 24) |
+		    (((uint64_t)cpdu.cbu_rootaddr[3]) << 16) |
+		    (((uint64_t)cpdu.cbu_rootaddr[4]) << 8) |
+		    (((uint64_t)cpdu.cbu_rootaddr[5]) << 0);
+		    
+		cu.cu_bridge_id =
+		    (((uint64_t)ntohs(cpdu.cbu_bridgepri)) << 48) |
+		    (((uint64_t)cpdu.cbu_bridgeaddr[0]) << 40) |
+		    (((uint64_t)cpdu.cbu_bridgeaddr[1]) << 32) |
+		    (((uint64_t)cpdu.cbu_bridgeaddr[2]) << 24) |
+		    (((uint64_t)cpdu.cbu_bridgeaddr[3]) << 16) |
+		    (((uint64_t)cpdu.cbu_bridgeaddr[4]) << 8) |
+		    (((uint64_t)cpdu.cbu_bridgeaddr[5]) << 0);
+
+		cu.cu_root_path_cost = ntohl(cpdu.cbu_rootpathcost);
+		cu.cu_message_age = ntohs(cpdu.cbu_messageage);
+		cu.cu_max_age = ntohs(cpdu.cbu_maxage);
+		cu.cu_hello_time = ntohs(cpdu.cbu_hellotime);
+		cu.cu_forward_delay = ntohs(cpdu.cbu_forwarddelay);
+		cu.cu_port_id = ntohs(cpdu.cbu_portid);
+		cu.cu_message_type = cpdu.cbu_bpdutype;
+		cu.cu_topology_change_acknowledgment =
+		    (cpdu.cbu_flags & BSTP_FLAG_TCA) ? 1 : 0;
+		cu.cu_topology_change =
+		    (cpdu.cbu_flags & BSTP_FLAG_TC) ? 1 : 0;
+		bstp_received_config_bpdu(sc, bif, &cu);
+		break;
+	default:
+		goto out;
+	}
+
+ out:
+	if (m)
+		m_freem(m);
+	return (NULL);
+}
+
+void
+bstp_received_config_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif,
+    struct bstp_config_unit *cu)
+{
+	int root;
+
+	root = bstp_root_bridge(sc);
+
+	if (bif->bif_state != BSTP_IFSTATE_DISABLED) {
+		if (bstp_supersedes_port_info(sc, bif, cu)) {
+			bstp_record_config_information(sc, bif, cu);
+			bstp_configuration_update(sc);
+			bstp_port_state_selection(sc);
+
+			if ((bstp_root_bridge(sc) == 0) && root) {
+				bstp_timer_stop(&sc->sc_hello_timer);
+
+				if (sc->sc_topology_change_detected) {
+					bstp_timer_stop(
+					    &sc->sc_topology_change_timer);
+					bstp_transmit_tcn(sc);
+					bstp_timer_start(&sc->sc_tcn_timer, 0);
+				}
+			}
+
+			if (bif == sc->sc_root_port) {
+				bstp_record_config_timeout_values(sc, cu);
+				bstp_config_bpdu_generation(sc);
+
+				if (cu->cu_topology_change_acknowledgment)
+					bstp_topology_change_acknowledged(sc);
+			}
+		} else if (bstp_designated_port(sc, bif))
+			bstp_transmit_config(sc, bif);
+	}
+}
+
+void
+bstp_received_tcn_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif,
+    struct bstp_tcn_unit *tcn)
+{
+	if (bif->bif_state != BSTP_IFSTATE_DISABLED &&
+	    bstp_designated_port(sc, bif)) {
+		bstp_topology_change_detection(sc);
+		bstp_acknowledge_topology_change(sc, bif);
+	}
+}
+
+void
+bstp_hello_timer_expiry(struct bridge_softc *sc)
+{
+	bstp_config_bpdu_generation(sc);
+	bstp_timer_start(&sc->sc_hello_timer, 0);
+}
+
+void
+bstp_message_age_timer_expiry(struct bridge_softc *sc,
+    struct bridge_iflist *bif)
+{
+	int root;
+
+	root = bstp_root_bridge(sc);
+	bstp_become_designated_port(sc, bif);
+	bstp_configuration_update(sc);
+	bstp_port_state_selection(sc);
+
+	if ((bstp_root_bridge(sc)) && (root == 0)) {
+		sc->sc_max_age = sc->sc_bridge_max_age;
+		sc->sc_hello_time = sc->sc_bridge_hello_time;
+		sc->sc_forward_delay = sc->sc_bridge_forward_delay;
+
+		bstp_topology_change_detection(sc);
+		bstp_timer_stop(&sc->sc_tcn_timer);
+		bstp_config_bpdu_generation(sc);
+		bstp_timer_start(&sc->sc_hello_timer, 0);
+	}
+}
+
+void
+bstp_forward_delay_timer_expiry(struct bridge_softc *sc,
+    struct bridge_iflist *bif)
+{
+	if (bif->bif_state == BSTP_IFSTATE_LISTENING) {
+		bstp_set_port_state(bif, BSTP_IFSTATE_LEARNING);
+		bstp_timer_start(&bif->bif_forward_delay_timer, 0);
+	} else if (bif->bif_state == BSTP_IFSTATE_LEARNING) {
+		bstp_set_port_state(bif, BSTP_IFSTATE_FORWARDING);
+		if (bstp_designated_for_some_port(sc) &&
+		    bif->bif_change_detection_enabled)
+			bstp_topology_change_detection(sc);
+	}
+}
+
+int
+bstp_designated_for_some_port(struct bridge_softc *sc)
+{
+
+	struct bridge_iflist *bif;
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if ((bif->bif_flags & IFBIF_STP) == 0)
+			continue;
+		if (bif->bif_designated_bridge == sc->sc_bridge_id)
+			return (1);
+	}
+	return (0);
+}
+
+void
+bstp_tcn_timer_expiry(struct bridge_softc *sc)
+{
+	bstp_transmit_tcn(sc);
+	bstp_timer_start(&sc->sc_tcn_timer, 0);
+}
+
+void
+bstp_topology_change_timer_expiry(struct bridge_softc *sc)
+{
+	sc->sc_topology_change_detected = 0;
+	sc->sc_topology_change = 0;
+}
+
+void
+bstp_hold_timer_expiry(struct bridge_softc *sc, struct bridge_iflist *bif)
+{
+	if (bif->bif_config_pending)
+		bstp_transmit_config(sc, bif);
+}
+
+void
+bstp_initialization(struct bridge_softc *sc)
+{
+	struct bridge_iflist *bif, *mif;
+
+	mif = NULL;
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if ((bif->bif_flags & IFBIF_STP) == 0)
+			continue;
+		if (bif->bif_ifp->if_type != IFT_ETHER)
+			continue;
+		bif->bif_port_id = (bif->bif_priority << 8) |
+		    (bif->bif_ifp->if_index & 0xff);
+
+		if (mif == NULL) {
+			mif = bif;
+			continue;
+		}
+		if (memcmp(IF_LLADDR(bif->bif_ifp),
+		    IF_LLADDR(mif->bif_ifp), ETHER_ADDR_LEN) < 0) {
+			mif = bif;
+			continue;
+		}
+	}
+	if (mif == NULL) {
+		bstp_stop(sc);
+		return;
+	}
+
+	sc->sc_bridge_id =
+	    (((uint64_t)sc->sc_bridge_priority) << 48) |
+	    (((uint64_t)IF_LLADDR(mif->bif_ifp)[0]) << 40) |
+	    (((uint64_t)IF_LLADDR(mif->bif_ifp)[1]) << 32) |
+	    (IF_LLADDR(mif->bif_ifp)[2] << 24) |
+	    (IF_LLADDR(mif->bif_ifp)[3] << 16) |
+	    (IF_LLADDR(mif->bif_ifp)[4] << 8) |
+	    (IF_LLADDR(mif->bif_ifp)[5]);
+
+	sc->sc_designated_root = sc->sc_bridge_id;
+	sc->sc_root_path_cost = 0;
+	sc->sc_root_port = NULL;
+
+	sc->sc_max_age = sc->sc_bridge_max_age;
+	sc->sc_hello_time = sc->sc_bridge_hello_time;
+	sc->sc_forward_delay = sc->sc_bridge_forward_delay;
+	sc->sc_topology_change_detected = 0;
+	sc->sc_topology_change = 0;
+	bstp_timer_stop(&sc->sc_tcn_timer);
+	bstp_timer_stop(&sc->sc_topology_change_timer);
+
+	if (callout_pending(&sc->sc_bstpcallout) == 0)
+		callout_reset(&sc->sc_bstpcallout, hz,
+		    bstp_tick, sc);
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if (bif->bif_flags & IFBIF_STP)
+			bstp_enable_port(sc, bif);
+		else
+			bstp_disable_port(sc, bif);
+	}
+
+	bstp_port_state_selection(sc);
+	bstp_config_bpdu_generation(sc);
+	bstp_timer_start(&sc->sc_hello_timer, 0);
+}
+
+void
+bstp_stop(struct bridge_softc *sc)
+{
+	struct bridge_iflist *bif;
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		bstp_set_port_state(bif, BSTP_IFSTATE_DISABLED);
+		bstp_timer_stop(&bif->bif_hold_timer);
+		bstp_timer_stop(&bif->bif_message_age_timer);
+		bstp_timer_stop(&bif->bif_forward_delay_timer);
+	}
+
+	callout_stop(&sc->sc_bstpcallout);
+
+	bstp_timer_stop(&sc->sc_topology_change_timer);
+	bstp_timer_stop(&sc->sc_tcn_timer);
+	bstp_timer_stop(&sc->sc_hello_timer);
+
+}
+
+void
+bstp_initialize_port(struct bridge_softc *sc, struct bridge_iflist *bif)
+{
+	bstp_become_designated_port(sc, bif);
+	bstp_set_port_state(bif, BSTP_IFSTATE_BLOCKING);
+	bif->bif_topology_change_acknowledge = 0;
+	bif->bif_config_pending = 0;
+	bif->bif_change_detection_enabled = 1;
+	bstp_timer_stop(&bif->bif_message_age_timer);
+	bstp_timer_stop(&bif->bif_forward_delay_timer);
+	bstp_timer_stop(&bif->bif_hold_timer);
+}
+
+void
+bstp_enable_port(struct bridge_softc *sc, struct bridge_iflist *bif)
+{
+	bstp_initialize_port(sc, bif);
+	bstp_port_state_selection(sc);
+}
+
+void
+bstp_disable_port(struct bridge_softc *sc, struct bridge_iflist *bif)
+{
+	int root;
+
+	root = bstp_root_bridge(sc);
+	bstp_become_designated_port(sc, bif);
+	bstp_set_port_state(bif, BSTP_IFSTATE_DISABLED);
+	bif->bif_topology_change_acknowledge = 0;
+	bif->bif_config_pending = 0;
+	bstp_timer_stop(&bif->bif_message_age_timer);
+	bstp_timer_stop(&bif->bif_forward_delay_timer);
+	bstp_configuration_update(sc);
+	bstp_port_state_selection(sc);
+
+	if (bstp_root_bridge(sc) && (root == 0)) {
+		sc->sc_max_age = sc->sc_bridge_max_age;
+		sc->sc_hello_time = sc->sc_bridge_hello_time;
+		sc->sc_forward_delay = sc->sc_bridge_forward_delay;
+
+		bstp_topology_change_detection(sc);
+		bstp_timer_stop(&sc->sc_tcn_timer);
+		bstp_config_bpdu_generation(sc);
+		bstp_timer_start(&sc->sc_hello_timer, 0);
+	}
+}
+
+void
+bstp_set_bridge_priority(struct bridge_softc *sc, uint64_t new_bridge_id)
+{
+	struct bridge_iflist *bif;
+	int root;
+
+	root = bstp_root_bridge(sc);
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if ((bif->bif_flags & IFBIF_STP) == 0)
+			continue;
+		if (bstp_designated_port(sc, bif))
+			bif->bif_designated_bridge = new_bridge_id;
+	}
+
+	sc->sc_bridge_id = new_bridge_id;
+
+	bstp_configuration_update(sc);
+	bstp_port_state_selection(sc);
+
+	if (bstp_root_bridge(sc) && (root == 0)) {
+		sc->sc_max_age = sc->sc_bridge_max_age;
+		sc->sc_hello_time = sc->sc_bridge_hello_time;
+		sc->sc_forward_delay = sc->sc_bridge_forward_delay;
+
+		bstp_topology_change_detection(sc);
+		bstp_timer_stop(&sc->sc_tcn_timer);
+		bstp_config_bpdu_generation(sc);
+		bstp_timer_start(&sc->sc_hello_timer, 0);
+	}
+}
+
+void
+bstp_set_port_priority(struct bridge_softc *sc, struct bridge_iflist *bif,
+    uint16_t new_port_id)
+{
+	if (bstp_designated_port(sc, bif))
+		bif->bif_designated_port = new_port_id;
+
+	bif->bif_port_id = new_port_id;
+
+	if ((sc->sc_bridge_id == bif->bif_designated_bridge) &&
+	    (bif->bif_port_id < bif->bif_designated_port)) {
+		bstp_become_designated_port(sc, bif);
+		bstp_port_state_selection(sc);
+	}
+}
+
+void
+bstp_set_path_cost(struct bridge_softc *sc, struct bridge_iflist *bif,
+    uint32_t path_cost)
+{
+	bif->bif_path_cost = path_cost;
+	bstp_configuration_update(sc);
+	bstp_port_state_selection(sc);
+}
+
+void
+bstp_enable_change_detection(struct bridge_iflist *bif)
+{
+	bif->bif_change_detection_enabled = 1;
+}
+
+void
+bstp_disable_change_detection(struct bridge_iflist *bif)
+{
+	bif->bif_change_detection_enabled = 0;
+}
+
+void
+bstp_ifupdstatus(struct bridge_softc *sc, struct bridge_iflist *bif)
+{
+	struct ifnet *ifp = bif->bif_ifp;
+	struct ifmediareq ifmr;
+	int error = 0;
+
+	bzero((char *)&ifmr, sizeof(ifmr));
+	error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr);
+	
+	if ((error == 0) && (ifp->if_flags & IFF_UP)) {
+	 	if (ifmr.ifm_status & IFM_ACTIVE) {
+			if (bif->bif_state == BSTP_IFSTATE_DISABLED)
+				bstp_enable_port(sc, bif);
+
+		} else {
+			if (bif->bif_state != BSTP_IFSTATE_DISABLED)
+				bstp_disable_port(sc, bif);
+		}
+		return;
+	}
+
+	if (bif->bif_state != BSTP_IFSTATE_DISABLED)
+		bstp_disable_port(sc, bif);
+}
+
+void
+bstp_tick(void *arg)
+{
+	struct bridge_softc *sc = arg;
+	struct bridge_iflist *bif;
+	int s;
+
+	s = splnet();
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if ((bif->bif_flags & IFBIF_STP) == 0)
+			continue;
+		/*
+		 * XXX This can cause a lag in "link does away"
+		 * XXX and "spanning tree gets updated".  We need
+		 * XXX come sort of callback from the link state
+		 * XXX update code to kick spanning tree.
+		 * XXX --thorpej@NetBSD.org
+		 */
+		bstp_ifupdstatus(sc, bif);
+	}
+
+	if (bstp_timer_expired(&sc->sc_hello_timer, sc->sc_hello_time))
+		bstp_hello_timer_expiry(sc);
+
+	if (bstp_timer_expired(&sc->sc_tcn_timer, sc->sc_bridge_hello_time))
+		bstp_tcn_timer_expiry(sc);
+
+	if (bstp_timer_expired(&sc->sc_topology_change_timer,
+	    sc->sc_topology_change_time))
+		bstp_topology_change_timer_expiry(sc);
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if ((bif->bif_flags & IFBIF_STP) == 0)
+			continue;
+		if (bstp_timer_expired(&bif->bif_message_age_timer,
+		    sc->sc_max_age))
+			bstp_message_age_timer_expiry(sc, bif);
+	}
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if ((bif->bif_flags & IFBIF_STP) == 0)
+			continue;
+		if (bstp_timer_expired(&bif->bif_forward_delay_timer,
+		    sc->sc_forward_delay))
+			bstp_forward_delay_timer_expiry(sc, bif);
+
+		if (bstp_timer_expired(&bif->bif_hold_timer,
+		    sc->sc_hold_time))
+			bstp_hold_timer_expiry(sc, bif);
+	}
+
+	if (sc->sc_if.if_flags & IFF_RUNNING)
+		callout_reset(&sc->sc_bstpcallout, hz, bstp_tick, sc);
+
+	splx(s);
+}
+
+void
+bstp_timer_start(struct bridge_timer *t, uint16_t v)
+{
+	t->value = v;
+	t->active = 1;
+}
+
+void
+bstp_timer_stop(struct bridge_timer *t)
+{
+	t->value = 0;
+	t->active = 0;
+}
+
+int
+bstp_timer_expired(struct bridge_timer *t, uint16_t v)
+{
+	if (t->active == 0)
+		return (0);
+	t->value += BSTP_TICK_VAL;
+	if (t->value >= v) {
+		bstp_timer_stop(t);
+		return (1);
+	}
+	return (0);
+
+}
diff -urN sys.orig/net/if.h sys/net/if.h
--- sys.orig/net/if.h	Thu Apr 15 22:08:18 2004
+++ sys/net/if.h	Thu Apr 15 19:57:20 2004
@@ -290,6 +290,13 @@
 	int	*ifm_ulist;		/* media words */
 };
 
+struct  ifdrv {
+	char            ifd_name[IFNAMSIZ];     /* if name, e.g. "en0" */
+	unsigned long   ifd_cmd;
+	size_t          ifd_len;
+	void            *ifd_data;
+};
+
 /* 
  * Structure used to retrieve aux status data from interfaces.
  * Kernel suppliers to this interface should respect the formatting
diff -urN sys.orig/net/if_bridge.c sys/net/if_bridge.c
--- sys.orig/net/if_bridge.c	Thu Jan  1 12:00:00 1970
+++ sys/net/if_bridge.c	Sat Apr 17 15:12:00 2004
@@ -0,0 +1,2231 @@
+/*	$NetBSD: if_bridge.c,v 1.21 2003/12/09 19:33:22 augustss Exp $	*/
+
+/*
+ * Copyright 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * 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 for the NetBSD Project by
+ *	Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+
+/* 
+ * Copyright (c) 1999, 2000 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.
+ *
+ * OpenBSD: if_bridge.c,v 1.60 2001/06/15 03:38:33 itojun Exp
+ */
+
+/*
+ * Network interface bridge support.
+ *
+ * TODO:
+ *
+ *	- Currently only supports Ethernet-like interfaces (Ethernet,
+ *	  802.11, VLANs on Ethernet, etc.)  Figure out a nice way
+ *	  to bridge other types of interfaces (FDDI-FDDI, and maybe
+ *	  consider heterogenous bridges).
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/protosw.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/socket.h> /* for net/if.h */
+#include <sys/sockio.h>
+#include <sys/ctype.h>  /* string functions */
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <vm/uma.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/if_var.h>
+
+#include <netinet/in.h> /* for struct arpcom */
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h> /* for struct arpcom */
+#include <net/if_bridgevar.h>
+
+#include <net/route.h>
+#include <netinet/ip_fw.h>
+#include <netinet/ip_dummynet.h>
+#include <net/bridge.h>
+
+#define PR_NOWAIT	M_NOWAIT
+#define pool_get(p, f)  uma_zalloc(*(p), (f))
+#define pool_put(p, o)  uma_zfree(*(p), (o))
+
+#define UMA_CREATE(var, type, desc) \
+                var = uma_zcreate(desc, sizeof(type),   \
+                        NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
+#define UMA_DESTROY(var) \
+                if(var) uma_zdestroy(var)
+
+
+/*
+ * Size of the route hash table.  Must be a power of two.
+ */
+#ifndef BRIDGE_RTHASH_SIZE
+#define	BRIDGE_RTHASH_SIZE		1024
+#endif
+
+#define	BRIDGE_RTHASH_MASK		(BRIDGE_RTHASH_SIZE - 1)
+
+/*
+ * Maximum number of addresses to cache.
+ */
+#ifndef BRIDGE_RTABLE_MAX
+#define	BRIDGE_RTABLE_MAX		100
+#endif
+
+/*
+ * Spanning tree defaults.
+ */
+#define	BSTP_DEFAULT_MAX_AGE		(20 * 256)
+#define	BSTP_DEFAULT_HELLO_TIME		(2 * 256)
+#define	BSTP_DEFAULT_FORWARD_DELAY	(15 * 256)
+#define	BSTP_DEFAULT_HOLD_TIME		(1 * 256)
+#define	BSTP_DEFAULT_BRIDGE_PRIORITY	0x8000
+#define	BSTP_DEFAULT_PORT_PRIORITY	0x80
+#define	BSTP_DEFAULT_PATH_COST		55
+
+/*
+ * Timeout (in seconds) for entries learned dynamically.
+ */
+#ifndef BRIDGE_RTABLE_TIMEOUT
+#define	BRIDGE_RTABLE_TIMEOUT		(20 * 60)	/* same as ARP */
+#endif
+
+/*
+ * Number of seconds between walks of the route list.
+ */
+#ifndef BRIDGE_RTABLE_PRUNE_PERIOD
+#define	BRIDGE_RTABLE_PRUNE_PERIOD	(5 * 60)
+#endif
+
+extern	struct mbuf *(*bridge_input_p)(struct ifnet *, struct mbuf *);
+extern	int (*bridge_output_p)(struct ifnet *, struct mbuf *,
+		struct sockaddr *, struct rtentry *);
+
+int	bridge_rtable_prune_period = BRIDGE_RTABLE_PRUNE_PERIOD;
+
+uma_zone_t bridge_rtnode_pool;
+
+int	bridge_clone_create(struct if_clone *, int);
+void	bridge_clone_destroy(struct ifnet *);
+
+int	bridge_ioctl(struct ifnet *, u_long, caddr_t);
+static void	bridge_init(void *);
+void	bridge_stop(struct ifnet *, int);
+void	bridge_start(struct ifnet *);
+
+void	bridge_forward(struct bridge_softc *, struct mbuf *m);
+
+void	bridge_timer(void *);
+
+void	bridge_broadcast(struct bridge_softc *, struct ifnet *, struct mbuf *);
+
+int	bridge_rtupdate(struct bridge_softc *, const uint8_t *,
+	    struct ifnet *, int, uint8_t);
+struct ifnet *bridge_rtlookup(struct bridge_softc *, const uint8_t *);
+void	bridge_rttrim(struct bridge_softc *);
+void	bridge_rtage(struct bridge_softc *);
+void	bridge_rtflush(struct bridge_softc *, int);
+int	bridge_rtdaddr(struct bridge_softc *, const uint8_t *);
+void	bridge_rtdelete(struct bridge_softc *, struct ifnet *ifp);
+
+int	bridge_rtable_init(struct bridge_softc *);
+void	bridge_rtable_fini(struct bridge_softc *);
+
+struct bridge_rtnode *bridge_rtnode_lookup(struct bridge_softc *,
+	    const uint8_t *);
+int	bridge_rtnode_insert(struct bridge_softc *, struct bridge_rtnode *);
+void	bridge_rtnode_destroy(struct bridge_softc *, struct bridge_rtnode *);
+
+struct bridge_iflist *bridge_lookup_member(struct bridge_softc *,
+	    const char *name);
+struct bridge_iflist *bridge_lookup_member_if(struct bridge_softc *,
+	    struct ifnet *ifp);
+void	bridge_delete_member(struct bridge_softc *, struct bridge_iflist *);
+
+int	bridge_ioctl_add(struct bridge_softc *, void *);
+int	bridge_ioctl_del(struct bridge_softc *, void *);
+int	bridge_ioctl_gifflags(struct bridge_softc *, void *);
+int	bridge_ioctl_sifflags(struct bridge_softc *, void *);
+int	bridge_ioctl_scache(struct bridge_softc *, void *);
+int	bridge_ioctl_gcache(struct bridge_softc *, void *);
+int	bridge_ioctl_gifs(struct bridge_softc *, void *);
+int	bridge_ioctl_rts(struct bridge_softc *, void *);
+int	bridge_ioctl_saddr(struct bridge_softc *, void *);
+int	bridge_ioctl_sto(struct bridge_softc *, void *);
+int	bridge_ioctl_gto(struct bridge_softc *, void *);
+int	bridge_ioctl_daddr(struct bridge_softc *, void *);
+int	bridge_ioctl_flush(struct bridge_softc *, void *);
+int	bridge_ioctl_gpri(struct bridge_softc *, void *);
+int	bridge_ioctl_spri(struct bridge_softc *, void *);
+int	bridge_ioctl_ght(struct bridge_softc *, void *);
+int	bridge_ioctl_sht(struct bridge_softc *, void *);
+int	bridge_ioctl_gfd(struct bridge_softc *, void *);
+int	bridge_ioctl_sfd(struct bridge_softc *, void *);
+int	bridge_ioctl_gma(struct bridge_softc *, void *);
+int	bridge_ioctl_sma(struct bridge_softc *, void *);
+int	bridge_ioctl_sifprio(struct bridge_softc *, void *);
+int	bridge_ioctl_sifcost(struct bridge_softc *, void *);
+#ifdef BRIDGE_IPF
+int	bridge_ioctl_gfilt(struct bridge_softc *, void *);
+int	bridge_ioctl_sfilt(struct bridge_softc *, void *);
+static int bridge_ipf(void *, struct mbuf **, struct ifnet *, int);
+static int bridge_ip_checkbasic(struct mbuf **mp);
+# ifdef INET6
+static int bridge_ip6_checkbasic(struct mbuf **mp);
+# endif /* INET6 */
+#endif /* BRIDGE_IPF */
+
+struct bridge_control {
+	int	(*bc_func)(struct bridge_softc *, void *);
+	int	bc_argsize;
+	int	bc_flags;
+};
+
+#define	BC_F_COPYIN		0x01	/* copy arguments in */
+#define	BC_F_COPYOUT		0x02	/* copy arguments out */
+#define	BC_F_SUSER		0x04	/* do super-user check */
+
+const struct bridge_control bridge_control_table[] = {
+	{ bridge_ioctl_add,		sizeof(struct ifbreq),
+	  BC_F_COPYIN|BC_F_SUSER },
+	{ bridge_ioctl_del,		sizeof(struct ifbreq),
+	  BC_F_COPYIN|BC_F_SUSER },
+
+	{ bridge_ioctl_gifflags,	sizeof(struct ifbreq),
+	  BC_F_COPYIN|BC_F_COPYOUT },
+	{ bridge_ioctl_sifflags,	sizeof(struct ifbreq),
+	  BC_F_COPYIN|BC_F_SUSER },
+
+	{ bridge_ioctl_scache,		sizeof(struct ifbrparam),
+	  BC_F_COPYIN|BC_F_SUSER },
+	{ bridge_ioctl_gcache,		sizeof(struct ifbrparam),
+	  BC_F_COPYOUT },
+
+	{ bridge_ioctl_gifs,		sizeof(struct ifbifconf),
+	  BC_F_COPYIN|BC_F_COPYOUT },
+	{ bridge_ioctl_rts,		sizeof(struct ifbaconf),
+	  BC_F_COPYIN|BC_F_COPYOUT },
+
+	{ bridge_ioctl_saddr,		sizeof(struct ifbareq),
+	  BC_F_COPYIN|BC_F_SUSER },
+
+	{ bridge_ioctl_sto,		sizeof(struct ifbrparam),
+	  BC_F_COPYIN|BC_F_SUSER },
+	{ bridge_ioctl_gto,		sizeof(struct ifbrparam),
+	  BC_F_COPYOUT },
+
+	{ bridge_ioctl_daddr,		sizeof(struct ifbareq),
+	  BC_F_COPYIN|BC_F_SUSER },
+
+	{ bridge_ioctl_flush,		sizeof(struct ifbreq),
+	  BC_F_COPYIN|BC_F_SUSER },
+
+	{ bridge_ioctl_gpri,		sizeof(struct ifbrparam),
+	  BC_F_COPYOUT },
+	{ bridge_ioctl_spri,		sizeof(struct ifbrparam),
+	  BC_F_COPYIN|BC_F_SUSER },
+
+	{ bridge_ioctl_ght,		sizeof(struct ifbrparam),
+	  BC_F_COPYOUT },
+	{ bridge_ioctl_sht,		sizeof(struct ifbrparam),
+	  BC_F_COPYIN|BC_F_SUSER },
+
+	{ bridge_ioctl_gfd,		sizeof(struct ifbrparam),
+	  BC_F_COPYOUT },
+	{ bridge_ioctl_sfd,		sizeof(struct ifbrparam),
+	  BC_F_COPYIN|BC_F_SUSER },
+
+	{ bridge_ioctl_gma,		sizeof(struct ifbrparam),
+	  BC_F_COPYOUT },
+	{ bridge_ioctl_sma,		sizeof(struct ifbrparam),
+	  BC_F_COPYIN|BC_F_SUSER },
+
+	{ bridge_ioctl_sifprio,		sizeof(struct ifbreq),
+	  BC_F_COPYIN|BC_F_SUSER },
+
+	{ bridge_ioctl_sifcost,		sizeof(struct ifbreq),
+	  BC_F_COPYIN|BC_F_SUSER },
+#ifdef BRIDGE_IPF
+	{ bridge_ioctl_gfilt,		sizeof(struct ifbrparam),
+	  BC_F_COPYOUT },
+	{ bridge_ioctl_sfilt,		sizeof(struct ifbrparam),
+	  BC_F_COPYIN|BC_F_SUSER },
+#endif /* BRIDGE_IPF */
+};
+const int bridge_control_table_size =
+    sizeof(bridge_control_table) / sizeof(bridge_control_table[0]);
+
+LIST_HEAD(, bridge_softc) bridge_list;
+
+struct if_clone bridge_cloner =
+    IF_CLONE_INITIALIZER("bridge", bridge_clone_create, bridge_clone_destroy,
+		    0, IF_MAXUNIT);
+
+
+static int
+bridge_modevent(module_t mod, int type, void *data) 
+{ 
+
+        switch (type) { 
+        case MOD_LOAD: 
+                if_clone_attach(&bridge_cloner);
+		UMA_CREATE(bridge_rtnode_pool,    struct bridge_rtnode, "brtpl");
+		LIST_INIT(&bridge_list);		
+		bridge_input_p = bridge_input;
+		bridge_output_p = bridge_output;
+		printf("if_bridge loaded\n");
+                break; 
+        case MOD_UNLOAD: 
+                if_clone_detach(&bridge_cloner);
+		UMA_DESTROY(bridge_rtnode_pool);
+		while (!LIST_EMPTY(&bridge_list))
+			bridge_clone_destroy(&LIST_FIRST(&bridge_list)->sc_if);
+		bridge_input_p = NULL;
+		bridge_output_p = NULL;
+		printf("if_bridge unloaded\n");
+                break;
+        } 
+        return 0; 
+} 
+
+static moduledata_t bridge_mod = { 
+        "if_bridge", 
+        bridge_modevent, 
+        0
+}; 
+
+DECLARE_MODULE(if_bridge, bridge_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
+
+
+/*
+ * bridge_clone_create:
+ *
+ *	Create a new bridge instance.
+ */
+int
+bridge_clone_create(struct if_clone *ifc, int unit)
+{
+	struct bridge_softc *sc;
+	struct ifnet *ifp;
+	int s;
+
+	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK);
+	memset(sc, 0, sizeof(*sc));
+	ifp = &sc->sc_if;
+
+	sc->sc_brtmax = BRIDGE_RTABLE_MAX;
+	sc->sc_brttimeout = BRIDGE_RTABLE_TIMEOUT;
+	sc->sc_bridge_max_age = BSTP_DEFAULT_MAX_AGE;   
+	sc->sc_bridge_hello_time = BSTP_DEFAULT_HELLO_TIME;
+	sc->sc_bridge_forward_delay = BSTP_DEFAULT_FORWARD_DELAY;
+	sc->sc_bridge_priority = BSTP_DEFAULT_BRIDGE_PRIORITY;
+	sc->sc_hold_time = BSTP_DEFAULT_HOLD_TIME;
+	sc->sc_filter_flags = 0;
+
+	/* Initialize our routing table. */
+	bridge_rtable_init(sc);
+
+	callout_init(&sc->sc_brcallout, 0);
+	callout_init(&sc->sc_bstpcallout, 0);
+
+	LIST_INIT(&sc->sc_iflist);
+
+	ifp->if_softc = sc;
+	if_initname(ifp, ifc->ifc_name, unit);
+	ifp->if_mtu = ETHERMTU;
+	ifp->if_ioctl = bridge_ioctl;
+	ifp->if_output = bridge_output;
+	ifp->if_start = bridge_start;
+	//ifp->if_done = bridge_stop;
+	ifp->if_init = bridge_init;
+	ifp->if_type = IFT_BRIDGE;
+	ifp->if_addrlen = 0;
+	//ifp->if_dlt = DLT_EN10MB;
+	ifp->if_hdrlen = ETHER_HDR_LEN;
+
+	if_attach(ifp);
+
+	//if_alloc_sadl(ifp);
+
+	s = splnet();
+	LIST_INSERT_HEAD(&bridge_list, sc, sc_list);
+	splx(s);
+
+	return (0);
+}
+
+/*
+ * bridge_clone_destroy:
+ *
+ *	Destroy a bridge instance.
+ */
+void
+bridge_clone_destroy(struct ifnet *ifp)
+{
+	struct bridge_softc *sc = ifp->if_softc;
+	struct bridge_iflist *bif;
+	int s;
+
+	s = splnet();
+
+	bridge_stop(ifp, 1);
+
+	while ((bif = LIST_FIRST(&sc->sc_iflist)) != NULL)
+		bridge_delete_member(sc, bif);
+
+	LIST_REMOVE(sc, sc_list);
+
+	splx(s);
+
+	if_detach(ifp);
+
+	/* Tear down the routing table. */
+	bridge_rtable_fini(sc);
+
+	free(sc, M_DEVBUF);
+}
+
+/*
+ * bridge_ioctl:
+ *
+ *	Handle a control request from the operator.
+ */
+int
+bridge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+	struct bridge_softc *sc = ifp->if_softc;
+	struct thread *td = curthread;
+	union {
+		struct ifbreq ifbreq;
+		struct ifbifconf ifbifconf;
+		struct ifbareq ifbareq;
+		struct ifbaconf ifbaconf;
+		struct ifbrparam ifbrparam;
+	} args;
+	struct ifdrv *ifd = (struct ifdrv *) data;
+	const struct bridge_control *bc;
+	int s, error = 0;
+
+	s = splnet();
+
+	switch (cmd) {
+	
+	case SIOCGDRVSPEC:
+	case SIOCSDRVSPEC:
+		if (ifd->ifd_cmd >= bridge_control_table_size) {
+			error = EINVAL;
+			break;
+		}
+		bc = &bridge_control_table[ifd->ifd_cmd];
+
+		if (cmd == SIOCGDRVSPEC &&
+		    (bc->bc_flags & BC_F_COPYOUT) == 0) {
+			error = EINVAL;
+			break;
+		}
+		else if (cmd == SIOCSDRVSPEC &&
+		    (bc->bc_flags & BC_F_COPYOUT) != 0) {
+			error = EINVAL;
+			break;
+		}
+
+		if (bc->bc_flags & BC_F_SUSER) {
+			error = suser(td);
+			if (error)
+				break;
+		}
+
+		if (ifd->ifd_len != bc->bc_argsize ||
+		    ifd->ifd_len > sizeof(args)) {
+			error = EINVAL;
+			break;
+		}
+
+		if (bc->bc_flags & BC_F_COPYIN) {
+			error = copyin(ifd->ifd_data, &args, ifd->ifd_len);
+			if (error)
+				break;
+		}
+
+		error = (*bc->bc_func)(sc, &args);
+		if (error)
+			break;
+
+		if (bc->bc_flags & BC_F_COPYOUT)
+			error = copyout(&args, ifd->ifd_data, ifd->ifd_len);
+
+		break;
+
+	case SIOCSIFFLAGS:
+		if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) == IFF_RUNNING) {
+			/*
+			 * If interface is marked down and it is running,
+			 * then stop and disable it.
+			 */
+			//(*ifp->if_stop)(ifp, 1);
+		} else if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) == IFF_UP) {
+			/*
+			 * If interface is marked up and it is stopped, then
+			 * start it.
+			 */
+			(*ifp->if_init)(ifp);
+		}
+		break;
+
+	default:
+		error = ENOTTY;
+		break;
+	}
+
+	splx(s);
+
+	return (error);
+}
+
+/*
+ * bridge_lookup_member:
+ *
+ *	Lookup a bridge member interface.  Must be called at splnet().
+ */
+struct bridge_iflist *
+bridge_lookup_member(struct bridge_softc *sc, const char *name)
+{
+	struct bridge_iflist *bif;
+	struct ifnet *ifp;
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		ifp = bif->bif_ifp;
+		if (strcmp(ifp->if_xname, name) == 0)
+			return (bif);
+	}
+
+	return (NULL);
+}
+
+/*
+ * bridge_lookup_member_if:
+ *
+ *	Lookup a bridge member interface by ifnet*.  Must be called at splnet().
+ */
+struct bridge_iflist *
+bridge_lookup_member_if(struct bridge_softc *sc, struct ifnet *member_ifp)
+{
+	struct bridge_iflist *bif;
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if (bif->bif_ifp == member_ifp)
+			return (bif);
+	}
+
+	return (NULL);
+}
+
+/*
+ * bridge_delete_member:
+ *
+ *	Delete the specified member interface.
+ */
+void
+bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif)
+{
+	struct ifnet *ifs = bif->bif_ifp;
+
+	switch (ifs->if_type) {
+	case IFT_ETHER:
+		/*
+		 * Take the interface out of promiscuous mode.
+		 */
+		(void) ifpromisc(ifs, 0);
+		break;
+
+	default:
+#ifdef DIAGNOSTIC
+		panic("bridge_delete_member: impossible");
+#endif
+		break;
+	}
+
+	ifs->if_bridge = NULL;
+	LIST_REMOVE(bif, bif_next);
+
+	bridge_rtdelete(sc, ifs);
+
+	free(bif, M_DEVBUF);
+
+	if (sc->sc_if.if_flags & IFF_RUNNING)
+		bstp_initialization(sc);
+}
+
+int
+bridge_ioctl_add(struct bridge_softc *sc, void *arg)
+{
+	struct ifbreq *req = arg;
+	struct bridge_iflist *bif = NULL;
+	struct ifnet *ifs;
+	int error = 0;
+
+	ifs = ifunit(req->ifbr_ifsname);
+	if (ifs == NULL)
+		return (ENOENT);
+
+	if (sc->sc_if.if_mtu != ifs->if_mtu)
+		return (EINVAL);
+
+	if (ifs->if_bridge == sc)
+		return (EEXIST);
+
+	if (ifs->if_bridge != NULL)
+		return (EBUSY);
+
+	bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT);
+	if (bif == NULL)
+		return (ENOMEM);
+
+	switch (ifs->if_type) {
+	case IFT_ETHER:
+		/*
+		 * Place the interface into promiscuous mode.
+		 */
+		error = ifpromisc(ifs, 1);
+		if (error)
+			goto out;
+		break;
+
+	default:
+		error = EINVAL;
+		goto out;
+	}
+
+	bif->bif_ifp = ifs;
+	bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER;
+	bif->bif_priority = BSTP_DEFAULT_PORT_PRIORITY;
+	bif->bif_path_cost = BSTP_DEFAULT_PATH_COST;
+
+	ifs->if_bridge = sc;
+	LIST_INSERT_HEAD(&sc->sc_iflist, bif, bif_next);
+
+	if (sc->sc_if.if_flags & IFF_RUNNING)
+		bstp_initialization(sc);
+	else
+		bstp_stop(sc);
+
+ out:
+	if (error) {
+		if (bif != NULL)
+			free(bif, M_DEVBUF);
+	}
+	return (error);
+}
+
+int
+bridge_ioctl_del(struct bridge_softc *sc, void *arg)
+{
+	struct ifbreq *req = arg;
+	struct bridge_iflist *bif;
+
+	bif = bridge_lookup_member(sc, req->ifbr_ifsname);
+	if (bif == NULL)
+		return (ENOENT);
+
+	bridge_delete_member(sc, bif);
+
+	return (0);
+}
+
+int
+bridge_ioctl_gifflags(struct bridge_softc *sc, void *arg)
+{
+	struct ifbreq *req = arg;
+	struct bridge_iflist *bif;
+
+	bif = bridge_lookup_member(sc, req->ifbr_ifsname);
+	if (bif == NULL)
+		return (ENOENT);
+
+	req->ifbr_ifsflags = bif->bif_flags;
+	req->ifbr_state = bif->bif_state;
+	req->ifbr_priority = bif->bif_priority;
+	req->ifbr_path_cost = bif->bif_path_cost;
+	req->ifbr_portno = bif->bif_ifp->if_index & 0xff;
+
+	return (0);
+}
+
+int
+bridge_ioctl_sifflags(struct bridge_softc *sc, void *arg)
+{
+	struct ifbreq *req = arg;
+	struct bridge_iflist *bif;
+
+	bif = bridge_lookup_member(sc, req->ifbr_ifsname);
+	if (bif == NULL)
+		return (ENOENT);
+
+	if (req->ifbr_ifsflags & IFBIF_STP) {
+		switch (bif->bif_ifp->if_type) {
+		case IFT_ETHER:
+			/* These can do spanning tree. */
+			break;
+
+		default:
+			/* Nothing else can. */
+			return (EINVAL);
+		}
+	}
+
+	bif->bif_flags = req->ifbr_ifsflags;
+
+	if (sc->sc_if.if_flags & IFF_RUNNING)
+		bstp_initialization(sc);
+
+	return (0);
+}
+
+int
+bridge_ioctl_scache(struct bridge_softc *sc, void *arg)
+{
+	struct ifbrparam *param = arg;
+
+	sc->sc_brtmax = param->ifbrp_csize;
+	bridge_rttrim(sc);
+
+	return (0);
+}
+
+int
+bridge_ioctl_gcache(struct bridge_softc *sc, void *arg)
+{
+	struct ifbrparam *param = arg;
+
+	param->ifbrp_csize = sc->sc_brtmax;
+
+	return (0);
+}
+
+int
+bridge_ioctl_gifs(struct bridge_softc *sc, void *arg)
+{
+	struct ifbifconf *bifc = arg;
+	struct bridge_iflist *bif;
+	struct ifbreq breq;
+	int count, len, error = 0;
+
+	count = 0;
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next)
+		count++;
+
+	if (bifc->ifbic_len == 0) {
+		bifc->ifbic_len = sizeof(breq) * count;
+		return (0);
+	}
+
+	count = 0;
+	len = bifc->ifbic_len;
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		if (len < sizeof(breq))
+			break;
+
+		strlcpy(breq.ifbr_ifsname, bif->bif_ifp->if_xname,
+		    sizeof(breq.ifbr_ifsname));
+		breq.ifbr_ifsflags = bif->bif_flags;
+		breq.ifbr_state = bif->bif_state;
+		breq.ifbr_priority = bif->bif_priority;
+		breq.ifbr_path_cost = bif->bif_path_cost;
+		breq.ifbr_portno = bif->bif_ifp->if_index & 0xff;
+		error = copyout(&breq, bifc->ifbic_req + count, sizeof(breq));
+		if (error)
+			break;
+		count++;
+		len -= sizeof(breq);
+	}
+
+	bifc->ifbic_len = sizeof(breq) * count;
+	return (error);
+}
+
+int
+bridge_ioctl_rts(struct bridge_softc *sc, void *arg)
+{
+	struct ifbaconf *bac = arg;
+	struct bridge_rtnode *brt;
+	struct ifbareq bareq;
+	struct timeval tv;
+	int count = 0, error = 0, len;
+
+	if (bac->ifbac_len == 0)
+		return (0);
+
+	getmicrotime(&tv);
+
+	len = bac->ifbac_len;
+	LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) {
+		if (len < sizeof(bareq))
+			goto out;
+		strlcpy(bareq.ifba_ifsname, brt->brt_ifp->if_xname,
+		    sizeof(bareq.ifba_ifsname));
+		memcpy(bareq.ifba_dst, brt->brt_addr, sizeof(brt->brt_addr));
+		if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)
+			bareq.ifba_expire = brt->brt_expire - tv.tv_sec;
+		else
+			bareq.ifba_expire = 0;
+		bareq.ifba_flags = brt->brt_flags;
+
+		error = copyout(&bareq, bac->ifbac_req + count, sizeof(bareq));
+		if (error)
+			goto out;
+		count++;
+		len -= sizeof(bareq);
+	}
+ out:
+	bac->ifbac_len = sizeof(bareq) * count;
+	return (error);
+}
+
+int
+bridge_ioctl_saddr(struct bridge_softc *sc, void *arg)
+{
+	struct ifbareq *req = arg;
+	struct bridge_iflist *bif;
+	int error;
+
+	bif = bridge_lookup_member(sc, req->ifba_ifsname);
+	if (bif == NULL)
+		return (ENOENT);
+
+	error = bridge_rtupdate(sc, req->ifba_dst, bif->bif_ifp, 1,
+	    req->ifba_flags);
+
+	return (error);
+}
+
+int
+bridge_ioctl_sto(struct bridge_softc *sc, void *arg)
+{
+	struct ifbrparam *param = arg;
+
+	sc->sc_brttimeout = param->ifbrp_ctime;
+
+	return (0);
+}
+
+int
+bridge_ioctl_gto(struct bridge_softc *sc, void *arg)
+{
+	struct ifbrparam *param = arg;
+
+	param->ifbrp_ctime = sc->sc_brttimeout;
+
+	return (0);
+}
+
+int
+bridge_ioctl_daddr(struct bridge_softc *sc, void *arg)
+{
+	struct ifbareq *req = arg;
+
+	return (bridge_rtdaddr(sc, req->ifba_dst));
+}
+
+int
+bridge_ioctl_flush(struct bridge_softc *sc, void *arg)
+{
+	struct ifbreq *req = arg;
+
+	bridge_rtflush(sc, req->ifbr_ifsflags);
+
+	return (0);
+}
+
+int
+bridge_ioctl_gpri(struct bridge_softc *sc, void *arg)
+{
+	struct ifbrparam *param = arg;
+
+	param->ifbrp_prio = sc->sc_bridge_priority;
+
+	return (0);
+}
+
+int
+bridge_ioctl_spri(struct bridge_softc *sc, void *arg)
+{
+	struct ifbrparam *param = arg;
+
+	sc->sc_bridge_priority = param->ifbrp_prio;
+
+	if (sc->sc_if.if_flags & IFF_RUNNING)
+		bstp_initialization(sc);
+
+	return (0);
+}
+
+int
+bridge_ioctl_ght(struct bridge_softc *sc, void *arg)
+{
+	struct ifbrparam *param = arg;
+
+	param->ifbrp_hellotime = sc->sc_bridge_hello_time >> 8;
+
+	return (0);
+}
+
+int
+bridge_ioctl_sht(struct bridge_softc *sc, void *arg)
+{
+	struct ifbrparam *param = arg;
+
+	if (param->ifbrp_hellotime == 0)
+		return (EINVAL);
+	sc->sc_bridge_hello_time = param->ifbrp_hellotime << 8;
+
+	if (sc->sc_if.if_flags & IFF_RUNNING)
+		bstp_initialization(sc);
+
+	return (0);
+}
+
+int
+bridge_ioctl_gfd(struct bridge_softc *sc, void *arg)
+{
+	struct ifbrparam *param = arg;
+
+	param->ifbrp_fwddelay = sc->sc_bridge_forward_delay >> 8;
+
+	return (0);
+}
+
+int
+bridge_ioctl_sfd(struct bridge_softc *sc, void *arg)
+{
+	struct ifbrparam *param = arg; 
+
+	if (param->ifbrp_fwddelay == 0)
+		return (EINVAL);
+	sc->sc_bridge_forward_delay = param->ifbrp_fwddelay << 8;
+
+	if (sc->sc_if.if_flags & IFF_RUNNING)
+		bstp_initialization(sc);
+
+	return (0);
+}
+
+int
+bridge_ioctl_gma(struct bridge_softc *sc, void *arg)
+{
+	struct ifbrparam *param = arg;
+
+	param->ifbrp_maxage = sc->sc_bridge_max_age >> 8;
+
+	return (0);
+}
+
+int
+bridge_ioctl_sma(struct bridge_softc *sc, void *arg)
+{
+	struct ifbrparam *param = arg;
+
+	if (param->ifbrp_maxage == 0)
+		return (EINVAL);
+	sc->sc_bridge_max_age = param->ifbrp_maxage << 8;
+
+	if (sc->sc_if.if_flags & IFF_RUNNING)
+		bstp_initialization(sc);
+
+	return (0);
+}
+
+int
+bridge_ioctl_sifprio(struct bridge_softc *sc, void *arg)
+{
+	struct ifbreq *req = arg;
+	struct bridge_iflist *bif;
+
+	bif = bridge_lookup_member(sc, req->ifbr_ifsname);
+	if (bif == NULL)
+		return (ENOENT);
+
+	bif->bif_priority = req->ifbr_priority;
+
+	if (sc->sc_if.if_flags & IFF_RUNNING)
+		bstp_initialization(sc);
+
+	return (0);
+}
+
+#ifdef BRIDGE_IPF
+int
+bridge_ioctl_gfilt(struct bridge_softc *sc, void *arg)
+{
+	struct ifbrparam *param = arg;
+
+	param->ifbrp_filter = sc->sc_filter_flags;
+
+	return (0);
+}
+
+int
+bridge_ioctl_sfilt(struct bridge_softc *sc, void *arg)
+{
+	struct ifbrparam *param = arg; 
+	uint32_t nflags, oflags;
+
+	if (param->ifbrp_filter & ~IFBF_FILT_MASK)
+		return (EINVAL);
+
+	nflags = param->ifbrp_filter;
+	oflags = sc->sc_filter_flags;
+
+	if ((nflags & IFBF_FILT_USEIPF) && !(oflags & IFBF_FILT_USEIPF)) {
+		pfil_add_hook((void *)bridge_ipf, NULL, PFIL_IN|PFIL_OUT,
+			&sc->sc_if.if_pfil);
+	}
+	if (!(nflags & IFBF_FILT_USEIPF) && (oflags & IFBF_FILT_USEIPF)) {
+		pfil_remove_hook((void *)bridge_ipf, NULL, PFIL_IN|PFIL_OUT,
+			&sc->sc_if.if_pfil);
+	}
+
+	sc->sc_filter_flags = nflags;
+
+	return (0);
+}
+#endif /* BRIDGE_IPF */
+
+int
+bridge_ioctl_sifcost(struct bridge_softc *sc, void *arg)
+{
+	struct ifbreq *req = arg;
+	struct bridge_iflist *bif;
+
+	bif = bridge_lookup_member(sc, req->ifbr_ifsname);
+	if (bif == NULL)
+		return (ENOENT);
+
+	bif->bif_path_cost = req->ifbr_path_cost;
+
+	if (sc->sc_if.if_flags & IFF_RUNNING)
+		bstp_initialization(sc);
+
+	return (0);
+}
+
+/*
+ * bridge_ifdetach:
+ *
+ *	Detach an interface from a bridge.  Called when a member
+ *	interface is detaching.
+ */
+void
+bridge_ifdetach(struct ifnet *ifp)
+{
+	struct bridge_softc *sc = ifp->if_bridge;
+	struct ifbreq breq;
+
+	memset(&breq, 0, sizeof(breq));
+	sprintf(breq.ifbr_ifsname, ifp->if_xname);
+
+	(void) bridge_ioctl_del(sc, &breq);
+}
+
+/*
+ * bridge_init:
+ *
+ *	Initialize a bridge interface.
+ */
+static void
+bridge_init(void *xsc)
+{
+	struct bridge_softc *sc = (struct bridge_softc *)xsc;
+	struct ifnet *ifp = &sc->sc_if;
+
+	printf("bridge_init: %s\n", ifp->if_xname);
+
+	if (ifp->if_flags & IFF_RUNNING)
+		return;
+
+	callout_reset(&sc->sc_brcallout, bridge_rtable_prune_period * hz,
+	    bridge_timer, sc);
+
+	ifp->if_flags |= IFF_RUNNING;
+	bstp_initialization(sc);
+	return; 
+}
+
+/*
+ * bridge_stop:
+ *
+ *	Stop the bridge interface.
+ */
+void
+bridge_stop(struct ifnet *ifp, int disable)
+{
+	struct bridge_softc *sc = ifp->if_softc;
+
+	if ((ifp->if_flags & IFF_RUNNING) == 0)
+		return;
+
+	callout_stop(&sc->sc_brcallout);
+	bstp_stop(sc);
+
+	bridge_rtflush(sc, IFBF_FLUSHDYN);
+
+	ifp->if_flags &= ~IFF_RUNNING;
+}
+
+/*
+ * bridge_enqueue:
+ *
+ *	Enqueue a packet on a bridge member interface.
+ *
+ *	NOTE: must be called at splnet().
+ */
+__inline void
+bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m,
+    int runfilt)
+{
+	int len;
+	short mflags;
+
+#ifdef PFIL_HOOKS
+	if (runfilt) {
+		if (pfil_run_hooks(&sc->sc_if.if_pfil, &m,
+		    dst_ifp, PFIL_OUT) != 0) {
+			if (m != NULL)
+				m_freem(m);
+			return;
+		}
+		if (m == NULL)
+			return;
+	}
+#endif /* PFIL_HOOKS */
+
+#ifdef ALTQ
+	/*
+	 * If ALTQ is enabled on the member interface, do
+	 * classification; the queueing discipline might
+	 * not require classification, but might require
+	 * the address family/header pointer in the pktattr.
+	 */
+	if (ALTQ_IS_ENABLED(&dst_ifp->if_snd)) {
+		/* XXX IFT_ETHER */
+		altq_etherclassify(&dst_ifp->if_snd, m, &pktattr);
+	}
+#endif /* ALTQ */
+
+	len = m->m_pkthdr.len;
+	mflags = m->m_flags;
+	IF_ENQUEUE(&dst_ifp->if_snd, m);
+
+	sc->sc_if.if_opackets++;
+	sc->sc_if.if_obytes += len;
+
+	dst_ifp->if_obytes += len;
+
+	if (mflags & M_MCAST) {
+		sc->sc_if.if_omcasts++;
+		dst_ifp->if_omcasts++;
+	}
+
+	if ((dst_ifp->if_flags & IFF_OACTIVE) == 0)
+		(*dst_ifp->if_start)(dst_ifp);
+}
+
+/*
+ * bridge_output:
+ *
+ *	Send output from a bridge member interface.  This
+ *	performs the bridging function for locally originated
+ *	packets.
+ *
+ *	The mbuf has the Ethernet header already attached.  We must
+ *	enqueue or free the mbuf before returning.
+ */
+int
+bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
+    struct rtentry *rt)
+{
+	struct ether_header *eh;
+	struct ifnet *dst_if;
+	struct bridge_softc *sc;
+	int s;
+
+	//printf("bridge_output: %s\n", ifp->if_xname);
+
+	if (m->m_len < ETHER_HDR_LEN) {
+		m = m_pullup(m, ETHER_HDR_LEN);
+		if (m == NULL)
+			return (0);
+	}
+
+	eh = mtod(m, struct ether_header *);
+	sc = ifp->if_bridge;
+
+	s = splnet();
+
+	/*
+	 * If bridge is down, but the original output interface is up,
+	 * go ahead and send out that interface.  Otherwise, the packet
+	 * is dropped below.
+	 */
+	if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) {
+		dst_if = ifp;
+		goto sendunicast;
+	}
+
+	/*
+	 * If the packet is a multicast, or we don't know a better way to
+	 * get there, send to all interfaces.
+	 */
+	if (ETHER_IS_MULTICAST(eh->ether_dhost))
+		dst_if = NULL;
+	else
+		dst_if = bridge_rtlookup(sc, eh->ether_dhost);
+	if (dst_if == NULL) {
+		struct bridge_iflist *bif;
+		struct mbuf *mc;
+		int used = 0;
+
+		LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+			dst_if = bif->bif_ifp;
+			if ((dst_if->if_flags & IFF_RUNNING) == 0)
+				continue;
+
+			/*
+			 * If this is not the original output interface,
+			 * and the interface is participating in spanning
+			 * tree, make sure the port is in a state that
+			 * allows forwarding.
+			 */
+			if (dst_if != ifp &&
+			    (bif->bif_flags & IFBIF_STP) != 0) {
+				switch (bif->bif_state) {
+				case BSTP_IFSTATE_BLOCKING:
+				case BSTP_IFSTATE_LISTENING:
+				case BSTP_IFSTATE_DISABLED:
+					continue;
+				}
+			}
+
+			if (LIST_NEXT(bif, bif_next) == NULL) {
+				used = 1;
+				mc = m;
+			} else {
+				mc = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
+				if (mc == NULL) {
+					sc->sc_if.if_oerrors++;
+					continue;
+				}
+			}
+
+			bridge_enqueue(sc, dst_if, mc, 0);
+		}
+		if (used == 0)
+			m_freem(m);
+		splx(s);
+		return (0);
+	}
+
+ sendunicast:
+	/*
+	 * XXX Spanning tree consideration here?
+	 */
+
+	if ((dst_if->if_flags & IFF_RUNNING) == 0) {
+		m_freem(m);
+		splx(s);
+		return (0);
+	}
+
+	bridge_enqueue(sc, dst_if, m, 0);
+
+	splx(s);
+	return (0);
+}
+
+/*
+ * bridge_start:
+ *
+ *	Start output on a bridge.
+ *
+ *	NOTE: This routine should never be called in this implementation.
+ */
+void
+bridge_start(struct ifnet *ifp)
+{
+
+	printf("%s: bridge_start() called\n", ifp->if_xname);
+}
+
+/*
+ * bridge_forward:
+ *
+ *	The forwarding function of the bridge.
+ */
+void
+bridge_forward(struct bridge_softc *sc, struct mbuf *m)
+{
+	struct bridge_iflist *bif;
+	struct ifnet *src_if, *dst_if;
+	struct ether_header *eh;
+
+	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;
+	}
+
+	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);
+			return;
+		}
+	}
+
+	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) != 0 &&
+	    bif->bif_state == BSTP_IFSTATE_LEARNING) {
+		m_freem(m);
+		return;
+	}
+
+	/*
+	 * 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);
+			return;
+		}
+	} 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;
+#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;
+		}
+	}
+
+	bridge_enqueue(sc, dst_if, m, 1);
+}
+
+/*
+ * bridge_input:
+ *
+ *	Receive input from a member interface.  Queue the packet for
+ *	bridging if it is not for us.
+ */
+struct mbuf *
+bridge_input(struct ifnet *ifp, struct mbuf *m)
+{
+	struct bridge_softc *sc = ifp->if_bridge;
+	struct bridge_iflist *bif;
+	struct ether_header *eh;
+	struct mbuf *mc;
+
+	//printf("bridge_input: %s\n", ifp->if_xname);
+
+	if ((sc->sc_if.if_flags & IFF_RUNNING) == 0)
+		return (m);
+
+	bif = bridge_lookup_member_if(sc, ifp);
+	if (bif == NULL)
+		return (m);
+
+	eh = mtod(m, struct ether_header *);
+
+	if (m->m_flags & (M_BCAST|M_MCAST)) {
+		/* Tap off 802.1D packets; they do not get forwarded. */
+		if (memcmp(eh->ether_dhost, bstp_etheraddr,
+		    ETHER_ADDR_LEN) == 0) {
+			m = bstp_input(ifp, m);
+			if (m == NULL)
+				return (NULL);
+		}
+
+		if (bif->bif_flags & IFBIF_STP) {
+			switch (bif->bif_state) {
+			case BSTP_IFSTATE_BLOCKING:
+			case BSTP_IFSTATE_LISTENING:
+			case BSTP_IFSTATE_DISABLED:
+				return (m);
+			}
+		}
+
+		/*
+		 * Make a deep copy of the packet and enqueue the copy
+		 * for bridge processing; return the original packet for
+		 * local processing.
+		 */
+		mc = m_dup(m, M_DONTWAIT);
+		if (mc == NULL)
+			return (m);
+
+		/* Perform the bridge forwarding function with the copy. */
+		bridge_forward(sc, mc);
+
+		/* Return the original packet for local processing. */
+		return (m);
+	}
+
+	if (bif->bif_flags & IFBIF_STP) {
+		switch (bif->bif_state) {
+		case BSTP_IFSTATE_BLOCKING:
+		case BSTP_IFSTATE_LISTENING:
+		case BSTP_IFSTATE_DISABLED:
+			return (m);
+		}
+	}
+
+	/*
+	 * Unicast.  Make sure it's not for us.
+	 */
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		/* It is destined for us. */
+		if (memcmp(IF_LLADDR(bif->bif_ifp), eh->ether_dhost,
+		    ETHER_ADDR_LEN) == 0) {
+			if (bif->bif_flags & IFBIF_LEARNING)
+				(void) bridge_rtupdate(sc,
+				    eh->ether_shost, ifp, 0, IFBAF_DYNAMIC);
+			m->m_pkthdr.rcvif = bif->bif_ifp;
+			return (m);
+		}
+
+		/* We just received a packet that we sent out. */
+		if (memcmp(IF_LLADDR(bif->bif_ifp), eh->ether_shost,
+		    ETHER_ADDR_LEN) == 0) {
+			m_freem(m);
+			return (NULL);
+		}
+	}
+
+	/* Perform the bridge forwarding function. */
+	bridge_forward(sc, m);
+
+	return (NULL);
+}
+
+/*
+ * bridge_broadcast:
+ *
+ *	Send a frame to all interfaces that are members of
+ *	the bridge, except for the one on which the packet
+ *	arrived.
+ */
+void
+bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if,
+    struct mbuf *m)
+{
+	struct bridge_iflist *bif;
+	struct mbuf *mc;
+	struct ifnet *dst_if;
+	int used = 0;
+
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		dst_if = bif->bif_ifp;
+		if (dst_if == src_if)
+			continue;
+
+		if (bif->bif_flags & IFBIF_STP) {
+			switch (bif->bif_state) {
+			case BSTP_IFSTATE_BLOCKING:
+			case BSTP_IFSTATE_DISABLED:
+				continue;
+			}
+		}
+
+		if ((bif->bif_flags & IFBIF_DISCOVER) == 0 &&
+		    (m->m_flags & (M_BCAST|M_MCAST)) == 0)
+			continue;
+
+		if ((dst_if->if_flags & IFF_RUNNING) == 0)
+			continue;
+
+		if (LIST_NEXT(bif, bif_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;
+			}
+		}
+
+		bridge_enqueue(sc, dst_if, mc, 1);
+	}
+	if (used == 0)
+		m_freem(m);
+}
+
+/*
+ * bridge_rtupdate:
+ *
+ *	Add a bridge routing entry.
+ */
+int
+bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst,
+    struct ifnet *dst_if, int setflags, uint8_t flags)
+{
+	struct bridge_rtnode *brt;
+	struct timeval tv;
+	int error;
+
+	/*
+	 * A route for this destination might already exist.  If so,
+	 * update it, otherwise create a new one.
+	 */
+	getmicrotime(&tv);
+	if ((brt = bridge_rtnode_lookup(sc, dst)) == NULL) {
+		if (sc->sc_brtcnt >= sc->sc_brtmax)
+			return (ENOSPC);
+
+		/*
+		 * Allocate a new bridge forwarding node, and
+		 * initialize the expiration time and Ethernet
+		 * address.
+		 */
+		brt = pool_get(&bridge_rtnode_pool, PR_NOWAIT);
+		if (brt == NULL)
+			return (ENOMEM);
+
+		memset(brt, 0, sizeof(*brt));
+		brt->brt_expire = tv.tv_sec + sc->sc_brttimeout;
+		brt->brt_flags = IFBAF_DYNAMIC;
+		memcpy(brt->brt_addr, dst, ETHER_ADDR_LEN);
+
+		if ((error = bridge_rtnode_insert(sc, brt)) != 0) {
+			pool_put(&bridge_rtnode_pool, brt);
+			return (error);
+		}
+	}
+
+	brt->brt_ifp = dst_if;
+	if (setflags) {
+		brt->brt_flags = flags;
+		brt->brt_expire = (flags & IFBAF_STATIC) ? 0 :
+		    tv.tv_sec + sc->sc_brttimeout;
+	}
+
+	return (0);
+}
+
+/*
+ * bridge_rtlookup:
+ *
+ *	Lookup the destination interface for an address.
+ */
+struct ifnet *
+bridge_rtlookup(struct bridge_softc *sc, const uint8_t *addr)
+{
+	struct bridge_rtnode *brt;
+
+	if ((brt = bridge_rtnode_lookup(sc, addr)) == NULL)
+		return (NULL);
+
+	return (brt->brt_ifp);
+}
+
+/*
+ * bridge_rttrim:
+ *
+ *	Trim the routine table so that we have a number
+ *	of routing entries less than or equal to the
+ *	maximum number.
+ */
+void
+bridge_rttrim(struct bridge_softc *sc)
+{
+	struct bridge_rtnode *brt, *nbrt;
+
+	/* Make sure we actually need to do this. */
+	if (sc->sc_brtcnt <= sc->sc_brtmax)
+		return;
+
+	/* Force an aging cycle; this might trim enough addresses. */
+	bridge_rtage(sc);
+	if (sc->sc_brtcnt <= sc->sc_brtmax)
+		return;
+
+	for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) {
+		nbrt = LIST_NEXT(brt, brt_list);
+		if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) {
+			bridge_rtnode_destroy(sc, brt);
+			if (sc->sc_brtcnt <= sc->sc_brtmax)
+				return;
+		}
+	}
+}
+
+/*
+ * bridge_timer:
+ *
+ *	Aging timer for the bridge.
+ */
+void
+bridge_timer(void *arg)
+{
+	struct bridge_softc *sc = arg;
+	int s;
+
+	s = splnet();
+	bridge_rtage(sc);
+	splx(s);
+
+	if (sc->sc_if.if_flags & IFF_RUNNING)
+		callout_reset(&sc->sc_brcallout,
+		    bridge_rtable_prune_period * hz, bridge_timer, sc);
+}
+
+/*
+ * bridge_rtage:
+ *
+ *	Perform an aging cycle.
+ */
+void
+bridge_rtage(struct bridge_softc *sc)
+{
+	struct bridge_rtnode *brt, *nbrt;
+	struct timeval tv;
+
+	getmicrotime(&tv);
+
+	for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) {
+		nbrt = LIST_NEXT(brt, brt_list);
+		if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) {
+			if (tv.tv_sec >= brt->brt_expire)
+				bridge_rtnode_destroy(sc, brt);
+		}
+	}
+}
+
+/*
+ * bridge_rtflush:
+ *
+ *	Remove all dynamic addresses from the bridge.
+ */
+void
+bridge_rtflush(struct bridge_softc *sc, int full)
+{
+	struct bridge_rtnode *brt, *nbrt;
+
+	for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) {
+		nbrt = LIST_NEXT(brt, brt_list);
+		if (full || (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)
+			bridge_rtnode_destroy(sc, brt);
+	}
+}
+
+/*
+ * bridge_rtdaddr:
+ *
+ *	Remove an address from the table.
+ */
+int
+bridge_rtdaddr(struct bridge_softc *sc, const uint8_t *addr)
+{
+	struct bridge_rtnode *brt;
+
+	if ((brt = bridge_rtnode_lookup(sc, addr)) == NULL)
+		return (ENOENT);
+
+	bridge_rtnode_destroy(sc, brt);
+	return (0);
+}
+
+/*
+ * bridge_rtdelete:
+ *
+ *	Delete routes to a speicifc member interface.
+ */
+void
+bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp)
+{
+	struct bridge_rtnode *brt, *nbrt;
+
+	for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) {
+		nbrt = LIST_NEXT(brt, brt_list);
+		if (brt->brt_ifp == ifp)
+			bridge_rtnode_destroy(sc, brt);
+	}
+}
+
+/*
+ * bridge_rtable_init:
+ *
+ *	Initialize the route table for this bridge.
+ */
+int
+bridge_rtable_init(struct bridge_softc *sc)
+{
+	int i;
+
+	sc->sc_rthash = malloc(sizeof(*sc->sc_rthash) * BRIDGE_RTHASH_SIZE,
+	    M_DEVBUF, M_NOWAIT);
+	if (sc->sc_rthash == NULL)
+		return (ENOMEM);
+
+	for (i = 0; i < BRIDGE_RTHASH_SIZE; i++)
+		LIST_INIT(&sc->sc_rthash[i]);
+
+	sc->sc_rthash_key = arc4random();
+
+	LIST_INIT(&sc->sc_rtlist);
+
+	return (0);
+}
+
+/*
+ * bridge_rtable_fini:
+ *
+ *	Deconstruct the route table for this bridge.
+ */
+void
+bridge_rtable_fini(struct bridge_softc *sc)
+{
+
+	free(sc->sc_rthash, M_DEVBUF);
+}
+
+/*
+ * The following hash function is adapted from "Hash Functions" by Bob Jenkins
+ * ("Algorithm Alley", Dr. Dobbs Journal, September 1997).
+ */
+#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 (/*CONSTCOND*/0)
+
+static __inline uint32_t
+bridge_rthash(struct bridge_softc *sc, const uint8_t *addr)
+{
+	uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = sc->sc_rthash_key;
+
+	b += addr[5] << 8;
+	b += addr[4];
+	a += addr[3] << 24;
+	a += addr[2] << 16;
+	a += addr[1] << 8;
+	a += addr[0];
+
+	mix(a, b, c);
+
+	return (c & BRIDGE_RTHASH_MASK);
+}
+
+#undef mix
+
+/*
+ * bridge_rtnode_lookup:
+ *
+ *	Look up a bridge route node for the specified destination.
+ */
+struct bridge_rtnode *
+bridge_rtnode_lookup(struct bridge_softc *sc, const uint8_t *addr)
+{
+	struct bridge_rtnode *brt;
+	uint32_t hash;
+	int dir;
+
+	hash = bridge_rthash(sc, addr);
+	LIST_FOREACH(brt, &sc->sc_rthash[hash], brt_hash) {
+		dir = memcmp(addr, brt->brt_addr, ETHER_ADDR_LEN);
+		if (dir == 0)
+			return (brt);
+		if (dir > 0)
+			return (NULL);
+	}
+
+	return (NULL);
+}
+
+/*
+ * bridge_rtnode_insert:
+ *
+ *	Insert the specified bridge node into the route table.  We
+ *	assume the entry is not already in the table.
+ */
+int
+bridge_rtnode_insert(struct bridge_softc *sc, struct bridge_rtnode *brt)
+{
+	struct bridge_rtnode *lbrt;
+	uint32_t hash;
+	int dir;
+
+	hash = bridge_rthash(sc, brt->brt_addr);
+
+	lbrt = LIST_FIRST(&sc->sc_rthash[hash]);
+	if (lbrt == NULL) {
+		LIST_INSERT_HEAD(&sc->sc_rthash[hash], brt, brt_hash);
+		goto out;
+	}
+
+	do {
+		dir = memcmp(brt->brt_addr, lbrt->brt_addr, ETHER_ADDR_LEN);
+		if (dir == 0)
+			return (EEXIST);
+		if (dir > 0) {
+			LIST_INSERT_BEFORE(lbrt, brt, brt_hash);
+			goto out;
+		}
+		if (LIST_NEXT(lbrt, brt_hash) == NULL) {
+			LIST_INSERT_AFTER(lbrt, brt, brt_hash);
+			goto out;
+		}
+		lbrt = LIST_NEXT(lbrt, brt_hash);
+	} while (lbrt != NULL);
+
+#ifdef DIAGNOSTIC
+	panic("bridge_rtnode_insert: impossible");
+#endif
+
+ out:
+	LIST_INSERT_HEAD(&sc->sc_rtlist, brt, brt_list);
+	sc->sc_brtcnt++;
+
+	return (0);
+}
+
+/*
+ * bridge_rtnode_destroy:
+ *
+ *	Destroy a bridge rtnode.
+ */
+void
+bridge_rtnode_destroy(struct bridge_softc *sc, struct bridge_rtnode *brt)
+{
+
+	LIST_REMOVE(brt, brt_hash);
+
+	LIST_REMOVE(brt, brt_list);
+	sc->sc_brtcnt--;
+	pool_put(&bridge_rtnode_pool, brt);
+}
+
+#ifdef BRIDGE_IPF
+extern struct pfil_head inet_pfil_hook;                 /* XXX */
+extern struct pfil_head inet6_pfil_hook;                /* XXX */
+
+/*
+ * Send bridge packets through IPF if they are one of the types IPF can deal
+ * with, or if they are ARP or REVARP.  (IPF will pass ARP and REVARP without
+ * question.)
+ */
+static int bridge_ipf(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
+{
+	int snap, error;
+	struct ether_header *eh1, eh2;
+	struct llc llc;
+	u_int16_t ether_type;
+
+	snap = 0;
+	error = -1;	/* Default error if not error == 0 */
+	eh1 = mtod(*mp, struct ether_header *);
+	ether_type = ntohs(eh1->ether_type);
+
+	/*
+	 * Check for SNAP/LLC.
+	 */
+        if (ether_type < ETHERMTU) { 
+                struct llc *llc = (struct llc *)(eh1 + 1);
+                            
+                if ((*mp)->m_len >= ETHER_HDR_LEN + 8 &&
+                    llc->llc_dsap == LLC_SNAP_LSAP &&
+                    llc->llc_ssap == LLC_SNAP_LSAP &&
+                    llc->llc_control == LLC_UI) {
+                	ether_type = htons(llc->llc_un.type_snap.ether_type);
+			snap = 1;
+                }
+        }       
+
+	/*
+	 * If we're trying to filter bridge traffic, don't look at anything
+	 * other than IP and ARP traffic.  If the filter doesn't understand
+	 * IPv6, don't allow IPv6 through the bridge either.  This is lame
+	 * since if we really wanted, say, an AppleTalk filter, we are hosed,
+	 * but of course we don't have an AppleTalk filter to begin with.
+	 * (Note that since IPF doesn't understand ARP it will pass *ALL*
+	 * ARP traffic.)
+	 */
+	switch (ether_type) {
+		case ETHERTYPE_ARP:
+		case ETHERTYPE_REVARP:
+			return 0; /* Automatically pass */
+		case ETHERTYPE_IP:
+# ifdef INET6
+		case ETHERTYPE_IPV6:
+# endif /* INET6 */
+			break;
+		default:
+			goto bad;
+	}
+
+	/* Strip off the Ethernet header and keep a copy. */
+	m_copydata(*mp, 0, ETHER_HDR_LEN, (caddr_t) &eh2);
+	m_adj(*mp, ETHER_HDR_LEN);
+
+	/* Strip off snap header, if present */
+	if (snap) {
+		m_copydata(*mp, 0, sizeof(struct llc), (caddr_t) &llc);
+		m_adj(*mp, sizeof(struct llc));
+	}
+
+	/*
+	 * Check basic packet sanity and run IPF through pfil.
+	 */
+	switch (ether_type)
+	{
+	case ETHERTYPE_IP :
+		error = (dir == PFIL_IN) ? bridge_ip_checkbasic(mp) : 0;
+		if (error == 0)
+			error = pfil_run_hooks(&inet_pfil_hook, mp, ifp, dir);
+		break;
+# ifdef INET6
+	case ETHERTYPE_IPV6 :
+		error = (dir == PFIL_IN) ? bridge_ip6_checkbasic(mp) : 0;
+		if (error == 0)
+			error = pfil_run_hooks(&inet6_pfil_hook, mp, ifp, dir);
+		break;
+# endif
+	default :
+		error = 0;
+		break;
+	}
+
+	if (*mp == NULL)
+		return error;
+	if (error != 0)
+		goto bad;
+
+	error = -1;
+
+	/*
+	 * Finally, put everything back the way it was and return
+	 */
+	if (snap) {
+		M_PREPEND(*mp, sizeof(struct llc), M_DONTWAIT);
+		if (*mp == NULL)
+			return error;
+		bcopy(&llc, mtod(*mp, caddr_t), sizeof(struct llc));
+	}
+
+	M_PREPEND(*mp, ETHER_HDR_LEN, M_DONTWAIT);
+	if (*mp == NULL)
+		return error;
+	bcopy(&eh2, mtod(*mp, caddr_t), ETHER_HDR_LEN);
+
+	return 0;
+
+    bad:
+	m_freem(*mp);
+	*mp = NULL;
+	return error;
+}
+
+/*
+ * Perform basic checks on header size since
+ * IPF assumes ip_input has already processed
+ * it for it.  Cut-and-pasted from ip_input.c.
+ * Given how simple the IPv6 version is,
+ * does the IPv4 version really need to be
+ * this complicated?
+ *
+ * XXX Should we update ipstat here, or not?
+ * XXX Right now we update ipstat but not
+ * XXX csum_counter.
+ */
+static int
+bridge_ip_checkbasic(struct mbuf **mp)
+{
+	struct mbuf *m = *mp;
+	struct ip *ip;
+	int len, hlen;
+
+	if (*mp == NULL)
+		return -1;
+
+	if (IP_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) {
+		if ((m = m_copyup(m, sizeof(struct ip),
+			(max_linkhdr + 3) & ~3)) == NULL) {
+			/* XXXJRT new stat, please */
+			ipstat.ips_toosmall++;
+			goto bad;
+		}
+	} else if (__predict_false(m->m_len < sizeof (struct ip))) {
+		if ((m = m_pullup(m, sizeof (struct ip))) == NULL) {
+			ipstat.ips_toosmall++;
+			goto bad;
+		} 
+	}
+	ip = mtod(m, struct ip *);
+	if (ip == NULL) goto bad;
+
+	if (ip->ip_v != IPVERSION) {
+		ipstat.ips_badvers++;
+		goto bad;
+	}
+	hlen = ip->ip_hl << 2;
+	if (hlen < sizeof(struct ip)) { /* minimum header length */
+		ipstat.ips_badhlen++;
+		goto bad;
+	}
+	if (hlen > m->m_len) {
+		if ((m = m_pullup(m, hlen)) == 0) {
+			ipstat.ips_badhlen++;
+			goto bad;
+		}
+		ip = mtod(m, struct ip *);
+		if (ip == NULL) goto bad;
+	}
+
+        switch (m->m_pkthdr.csum_flags &
+                ((m->m_pkthdr.rcvif->if_csum_flags_rx & M_CSUM_IPv4) |
+                 M_CSUM_IPv4_BAD)) {
+        case M_CSUM_IPv4|M_CSUM_IPv4_BAD:
+                /* INET_CSUM_COUNTER_INCR(&ip_hwcsum_bad); */
+                goto bad;
+
+        case M_CSUM_IPv4:
+                /* Checksum was okay. */
+                /* INET_CSUM_COUNTER_INCR(&ip_hwcsum_ok); */
+                break;
+
+        default:
+                /* Must compute it ourselves. */
+                /* INET_CSUM_COUNTER_INCR(&ip_swcsum); */
+                if (in_cksum(m, hlen) != 0)
+                        goto bad;
+                break;
+        }
+
+        /* Retrieve the packet length. */
+        len = ntohs(ip->ip_len);
+
+        /*
+         * Check for additional length bogosity
+         */
+        if (len < hlen) {
+                ipstat.ips_badlen++;
+                goto bad;
+        }
+
+        /*
+         * Check that the amount of data in the buffers
+         * is as at least much as the IP header would have us expect.
+         * Drop packet if shorter than we expect.
+         */
+        if (m->m_pkthdr.len < len) {
+                ipstat.ips_tooshort++;
+                goto bad;
+        }
+
+	/* Checks out, proceed */
+	*mp = m;
+	return 0;
+
+    bad:
+	*mp = m;
+	return -1;
+}
+
+# ifdef INET6
+/*
+ * Same as above, but for IPv6.
+ * Cut-and-pasted from ip6_input.c.
+ * XXX Should we update ip6stat, or not?
+ */
+static int
+bridge_ip6_checkbasic(struct mbuf **mp)
+{
+	struct mbuf *m = *mp;
+	struct ip6_hdr *ip6;
+
+        /*
+         * If the IPv6 header is not aligned, slurp it up into a new
+         * mbuf with space for link headers, in the event we forward
+         * it.  Otherwise, if it is aligned, make sure the entire base
+         * IPv6 header is in the first mbuf of the chain.
+         */
+        if (IP6_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) {
+                struct ifnet *inifp = m->m_pkthdr.rcvif;
+                if ((m = m_copyup(m, sizeof(struct ip6_hdr),
+                                  (max_linkhdr + 3) & ~3)) == NULL) {
+                        /* XXXJRT new stat, please */
+                        ip6stat.ip6s_toosmall++;
+                        in6_ifstat_inc(inifp, ifs6_in_hdrerr);
+                        goto bad;
+                }
+        } else if (__predict_false(m->m_len < sizeof(struct ip6_hdr))) {
+                struct ifnet *inifp = m->m_pkthdr.rcvif;
+                if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
+                        ip6stat.ip6s_toosmall++;
+                        in6_ifstat_inc(inifp, ifs6_in_hdrerr);
+                        goto bad;
+                }
+        }
+
+        ip6 = mtod(m, struct ip6_hdr *);
+
+        if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
+                ip6stat.ip6s_badvers++;
+                in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr);
+                goto bad;
+        }
+
+	/* Checks out, proceed */
+	*mp = m;
+	return 0;
+
+    bad:
+	*mp = m;
+	return -1;
+}
+# endif /* INET6 */
+#endif /* BRIDGE_IPF */
diff -urN sys.orig/net/if_bridgevar.h sys/net/if_bridgevar.h
--- sys.orig/net/if_bridgevar.h	Thu Jan  1 12:00:00 1970
+++ sys/net/if_bridgevar.h	Wed Apr 14 18:09:25 2004
@@ -0,0 +1,317 @@
+/*	$NetBSD: if_bridgevar.h,v 1.4 2003/07/08 07:13:50 itojun Exp $	*/
+
+/*
+ * Copyright 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * 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 for the NetBSD Project by
+ *	Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+
+/* 
+ * Copyright (c) 1999, 2000 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.
+ *
+ * OpenBSD: if_bridge.h,v 1.14 2001/03/22 03:48:29 jason Exp
+ */
+
+/*
+ * Data structure and control definitions for bridge interfaces.
+ */
+
+#include <sys/callout.h>
+#include <sys/queue.h>
+
+/*
+ * Commands used in the SIOCSDRVSPEC ioctl.  Note the lookup of the
+ * bridge interface itself is keyed off the ifdrv structure.
+ */
+#define	BRDGADD			0	/* add bridge member (ifbreq) */
+#define	BRDGDEL			1	/* delete bridge member (ifbreq) */
+#define	BRDGGIFFLGS		2	/* get member if flags (ifbreq) */
+#define	BRDGSIFFLGS		3	/* set member if flags (ifbreq) */
+#define	BRDGSCACHE		4	/* set cache size (ifbrparam) */
+#define	BRDGGCACHE		5	/* get cache size (ifbrparam) */
+#define	BRDGGIFS		6	/* get member list (ifbifconf) */
+#define	BRDGRTS			7	/* get address list (ifbaconf) */
+#define	BRDGSADDR		8	/* set static address (ifbareq) */
+#define	BRDGSTO			9	/* set cache timeout (ifbrparam) */
+#define	BRDGGTO			10	/* get cache timeout (ifbrparam) */
+#define	BRDGDADDR		11	/* delete address (ifbareq) */
+#define	BRDGFLUSH		12	/* flush address cache (ifbreq) */
+
+#define	BRDGGPRI		13	/* get priority (ifbrparam) */
+#define	BRDGSPRI		14	/* set priority (ifbrparam) */
+#define	BRDGGHT			15	/* get hello time (ifbrparam) */
+#define	BRDGSHT			16	/* set hello time (ifbrparam) */
+#define	BRDGGFD			17	/* get forward delay (ifbrparam) */
+#define	BRDGSFD			18	/* set forward delay (ifbrparam) */
+#define	BRDGGMA			19	/* get max age (ifbrparam) */
+#define	BRDGSMA			20	/* set max age (ifbrparam) */
+#define	BRDGSIFPRIO		21	/* set if priority (ifbreq) */
+#define BRDGSIFCOST		22	/* set if path cost (ifbreq) */
+#define BRDGGFILT	        23	/* get filter flags (ifbrparam) */
+#define BRDGSFILT	        24	/* set filter flags (ifbrparam) */
+
+/*
+ * Generic bridge control request.
+ */
+struct ifbreq {
+	char		ifbr_ifsname[IFNAMSIZ];	/* member if name */
+	uint32_t	ifbr_ifsflags;		/* member if flags */
+	uint8_t		ifbr_state;		/* member if STP state */
+	uint8_t		ifbr_priority;		/* member if STP priority */
+	uint8_t		ifbr_path_cost;		/* member if STP cost */
+	uint8_t		ifbr_portno;		/* member if port number */
+};
+
+/* BRDGGIFFLAGS, BRDGSIFFLAGS */
+#define	IFBIF_LEARNING		0x01	/* if can learn */
+#define	IFBIF_DISCOVER		0x02	/* if sends packets w/ unknown dest. */
+#define	IFBIF_STP		0x04	/* if participates in spanning tree */
+
+#define	IFBIFBITS	"\020\1LEARNING\2DISCOVER\3STP"
+
+/* BRDGFLUSH */
+#define	IFBF_FLUSHDYN		0x00	/* flush learned addresses only */
+#define	IFBF_FLUSHALL		0x01	/* flush all addresses */
+
+/* BRDGSFILT */
+#define IFBF_FILT_USEIPF	0x00000001 /* enable ipf on bridge */
+#define IFBF_FILT_MASK		0x00000001 /* mask of valid values */
+
+/* STP port states */
+#define	BSTP_IFSTATE_DISABLED	0
+#define	BSTP_IFSTATE_LISTENING	1
+#define	BSTP_IFSTATE_LEARNING	2
+#define	BSTP_IFSTATE_FORWARDING	3
+#define	BSTP_IFSTATE_BLOCKING	4
+
+/*
+ * Interface list structure.
+ */
+struct ifbifconf {
+	uint32_t	ifbic_len;	/* buffer size */
+	union {
+		caddr_t	ifbicu_buf;
+		struct ifbreq *ifbicu_req;
+	} ifbic_ifbicu;
+#define	ifbic_buf	ifbic_ifbicu.ifbicu_buf
+#define	ifbic_req	ifbic_ifbicu.ifbicu_req
+};
+
+/*
+ * Bridge address request.
+ */
+struct ifbareq {
+	char		ifba_ifsname[IFNAMSIZ];	/* member if name */
+	unsigned long	ifba_expire;		/* address expire time */
+	uint8_t		ifba_flags;		/* address flags */
+	uint8_t		ifba_dst[ETHER_ADDR_LEN];/* destination address */
+};
+
+#define	IFBAF_TYPEMASK	0x03	/* address type mask */
+#define	IFBAF_DYNAMIC	0x00	/* dynamically learned address */
+#define	IFBAF_STATIC	0x01	/* static address */
+
+#define	IFBAFBITS	"\020\1STATIC"
+
+/*
+ * Address list structure.
+ */
+struct ifbaconf {
+	uint32_t	ifbac_len;	/* buffer size */
+	union {
+		caddr_t ifbacu_buf;
+		struct ifbareq *ifbacu_req;
+	} ifbac_ifbacu;
+#define	ifbac_buf	ifbac_ifbacu.ifbacu_buf
+#define	ifbac_req	ifbac_ifbacu.ifbacu_req
+};
+
+/*
+ * Bridge parameter structure.
+ */
+struct ifbrparam {
+	union {
+		uint32_t ifbrpu_int32;
+		uint16_t ifbrpu_int16;
+		uint8_t ifbrpu_int8;
+	} ifbrp_ifbrpu;
+};
+#define	ifbrp_csize	ifbrp_ifbrpu.ifbrpu_int32	/* cache size */
+#define	ifbrp_ctime	ifbrp_ifbrpu.ifbrpu_int32	/* cache time (sec) */
+#define	ifbrp_prio	ifbrp_ifbrpu.ifbrpu_int16	/* bridge priority */
+#define	ifbrp_hellotime	ifbrp_ifbrpu.ifbrpu_int8	/* hello time (sec) */
+#define	ifbrp_fwddelay	ifbrp_ifbrpu.ifbrpu_int8	/* fwd time (sec) */
+#define	ifbrp_maxage	ifbrp_ifbrpu.ifbrpu_int8	/* max age (sec) */
+#define	ifbrp_filter	ifbrp_ifbrpu.ifbrpu_int32	/* filtering flags */
+
+#ifdef _KERNEL
+/*
+ * Timekeeping structure used in spanning tree code.
+ */
+struct bridge_timer {
+	uint16_t	active;
+	uint16_t	value;
+};
+
+struct bstp_config_unit {
+	uint64_t	cu_rootid;
+	uint64_t	cu_bridge_id;
+	uint32_t	cu_root_path_cost;
+	uint16_t	cu_message_age;
+	uint16_t	cu_max_age;
+	uint16_t	cu_hello_time;
+	uint16_t	cu_forward_delay;
+	uint16_t	cu_port_id;
+	uint8_t		cu_message_type;
+	uint8_t		cu_topology_change_acknowledgment;
+	uint8_t		cu_topology_change;
+};
+
+struct bstp_tcn_unit {
+	uint8_t		tu_message_type;
+};
+
+/*
+ * Bridge interface list entry.
+ */
+struct bridge_iflist {
+	LIST_ENTRY(bridge_iflist) bif_next;
+	uint64_t		bif_designated_root;
+	uint64_t		bif_designated_bridge;
+	uint32_t		bif_path_cost;
+	uint32_t		bif_designated_cost;
+	struct bridge_timer	bif_hold_timer;
+	struct bridge_timer	bif_message_age_timer;
+	struct bridge_timer	bif_forward_delay_timer;
+	struct bstp_config_unit	bif_config_bpdu;
+	uint16_t		bif_port_id;
+	uint16_t		bif_designated_port;
+	uint8_t			bif_state;
+	uint8_t			bif_topology_change_acknowledge;
+	uint8_t			bif_config_pending;
+	uint8_t			bif_change_detection_enabled;
+	uint8_t			bif_priority;
+	struct ifnet		*bif_ifp;	/* member if */
+	uint32_t		bif_flags;	/* member if flags */
+};
+
+/*
+ * Bridge route node.
+ */
+struct bridge_rtnode {
+	LIST_ENTRY(bridge_rtnode) brt_hash;	/* hash table linkage */
+	LIST_ENTRY(bridge_rtnode) brt_list;	/* list linkage */
+	struct ifnet		*brt_ifp;	/* destination if */
+	unsigned long		brt_expire;	/* expiration time */
+	uint8_t			brt_flags;	/* address flags */
+	uint8_t			brt_addr[ETHER_ADDR_LEN];
+};
+
+/*
+ * Software state for each bridge.
+ */
+struct bridge_softc {
+	struct ifnet		sc_if;
+	LIST_ENTRY(bridge_softc) sc_list;
+	uint64_t		sc_designated_root;
+	uint64_t		sc_bridge_id;
+	struct bridge_iflist	*sc_root_port;
+	uint32_t		sc_root_path_cost;
+	uint16_t		sc_max_age;
+	uint16_t		sc_hello_time;
+	uint16_t		sc_forward_delay;
+	uint16_t		sc_bridge_max_age;
+	uint16_t		sc_bridge_hello_time;
+	uint16_t		sc_bridge_forward_delay;
+	uint16_t		sc_topology_change_time;
+	uint16_t		sc_hold_time;
+	uint16_t		sc_bridge_priority;
+	uint8_t			sc_topology_change_detected;
+	uint8_t			sc_topology_change;
+	struct bridge_timer	sc_hello_timer;
+	struct bridge_timer	sc_topology_change_timer;
+	struct bridge_timer	sc_tcn_timer;
+	uint32_t		sc_brtmax;	/* max # of addresses */
+	uint32_t		sc_brtcnt;	/* cur. # of addresses */
+	uint32_t		sc_brttimeout;	/* rt timeout in seconds */
+	struct callout		sc_brcallout;	/* bridge callout */
+	struct callout		sc_bstpcallout;	/* STP callout */
+	LIST_HEAD(, bridge_iflist) sc_iflist;	/* member interface list */
+	LIST_HEAD(, bridge_rtnode) *sc_rthash;	/* our forwarding table */
+	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 */
+};
+
+extern const uint8_t bstp_etheraddr[];
+
+void	bridge_ifdetach(struct ifnet *);
+
+int	bridge_output(struct ifnet *, struct mbuf *, struct sockaddr *,
+	    struct rtentry *);
+struct mbuf *bridge_input(struct ifnet *, struct mbuf *);
+
+void	bstp_initialization(struct bridge_softc *);
+void	bstp_stop(struct bridge_softc *);
+struct mbuf *bstp_input(struct ifnet *, struct mbuf *);
+
+void	bridge_enqueue(struct bridge_softc *, struct ifnet *, struct mbuf *,
+	    int);
+
+#endif /* _KERNEL */
diff -urN sys.orig/net/if_ethersubr.c sys/net/if_ethersubr.c
--- sys.orig/net/if_ethersubr.c	Thu Apr 15 22:08:18 2004
+++ sys/net/if_ethersubr.c	Thu Apr 15 19:47:22 2004
@@ -58,6 +58,7 @@
 #include <net/bpf.h>
 #include <net/ethernet.h>
 #include <net/bridge.h>
+#include <net/if_bridgevar.h>
 #include <net/if_vlan_var.h>
 
 #if defined(INET) || defined(INET6)
@@ -107,6 +108,10 @@
 bdgtakeifaces_t *bdgtakeifaces_ptr;
 struct bdg_softc *ifp2sc;
 
+struct mbuf *(*bridge_input_p)(struct ifnet *, struct mbuf *); 
+int     (*bridge_output_p)(struct ifnet *, struct mbuf *, 
+		struct sockaddr *, struct rtentry *);
+
 static const u_char etherbroadcastaddr[ETHER_ADDR_LEN] =
 			{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
@@ -284,6 +289,14 @@
 		(void)memcpy(eh->ether_shost, ac->ac_enaddr,
 			sizeof(eh->ether_shost));
 
+       /*
+	* Bridges require special output handling.
+	*/
+	if (ifp->if_bridge) {
+		KASSERT(bridge_output_p != NULL,("ether_input: if_bridge not loaded!"));
+		return ((*bridge_output_p)(ifp, m, NULL, NULL));
+	}
+
 	/*
 	 * If a simplex interface, and the packet is being sent to our
 	 * Ethernet address or a broadcast address, loopback a copy.
@@ -559,6 +572,28 @@
 		(*ng_ether_input_p)(ifp, &m);
 		if (m == NULL)
 			return;
+	}
+
+	/*
+	* Tap the packet off here for a bridge.  bridge_input()
+	* will return NULL if it has consumed the packet, otherwise
+	* it gets processed as normal.  Note that bridge_input()
+	* will always return the original packet if we need to
+	* process it locally.
+	*/
+	if (ifp->if_bridge) {
+		KASSERT(bridge_input_p != NULL,("ether_input: if_bridge not loaded!"));
+		/* clear M_PROMISC, in case the packets comes from a vlan */
+		//m->m_flags &= ~M_PROMISC;
+		m = (*bridge_input_p)(ifp, m);
+		if (m == NULL)
+			return;
+		/*
+		* Bridge has determined that the packet is for us.
+		* Update our interface pointer -- we may have had
+		* to "bridge" the packet locally.
+		*/
+		ifp = m->m_pkthdr.rcvif;
 	}
 
 	/* Check for bridging mode */
diff -urN sys.orig/net/if_llc.h sys/net/if_llc.h
--- sys.orig/net/if_llc.h	Thu Apr 15 22:08:18 2004
+++ sys/net/if_llc.h	Thu Apr 15 22:26:41 2004
@@ -149,6 +149,7 @@
 /*
  * ISO PDTR 10178 contains among others
  */
+#define LLC_8021D_LSAP	0x42
 #define LLC_X25_LSAP	0x7e
 #define LLC_SNAP_LSAP	0xaa
 #define LLC_ISO_LSAP	0xfe
diff -urN sys.orig/net/if_types.h sys/net/if_types.h
--- sys.orig/net/if_types.h	Thu Apr 15 22:08:18 2004
+++ sys/net/if_types.h	Thu Apr 15 22:25:04 2004
@@ -238,7 +238,7 @@
 #define	IFT_ATMVCIENDPT		   0xc2 /* ATM VCI End Point */
 #define	IFT_OPTICALCHANNEL	   0xc3 /* Optical Channel */
 #define	IFT_OPTICALTRANSPORT	   0xc4 /* Optical Transport */
-
+#define	IFT_BRIDGE		   0xd1 /* Transparent bridge interface */
 #define	IFT_STF			   0xd7	/* 6to4 interface */
 
 /* not based on IANA assignments */
diff -urN sys.orig/net/if_var.h sys/net/if_var.h
--- sys.orig/net/if_var.h	Sat Apr 17 15:20:13 2004
+++ sys/net/if_var.h	Sat Apr 17 15:33:52 2004
@@ -174,6 +174,8 @@
 	struct	ifqueue if_snd;		/* output queue */
 	const u_int8_t *if_broadcastaddr; /* linklevel broadcast bytestring */
 
+	void	*if_bridge;		/* bridge glue */
+
 	struct	lltable *lltables;	/* list of L3-L2 resolution tables */
 
 	struct	label *if_label;	/* interface MAC label */
diff -urN sys.orig/sys/sockio.h sys/sys/sockio.h
--- sys.orig/sys/sockio.h	Thu Apr 15 22:08:29 2004
+++ sys/sys/sockio.h	Sat Apr 17 15:37:36 2004
@@ -105,6 +105,11 @@
 #define	SIOCGPRIVATE_0	_IOWR('i', 80, struct ifreq)	/* device private 0 */
 #define	SIOCGPRIVATE_1	_IOWR('i', 81, struct ifreq)	/* device private 1 */
 
+#define	SIOCSDRVSPEC	_IOW('i', 123, struct ifdrv)	/* set driver-specific
+								  parameters */
+#define	SIOCGDRVSPEC	_IOWR('i', 123, struct ifdrv)	/* get driver-specific
+								  parameters */
+
 #define	SIOCIFCREATE	_IOWR('i', 122, struct ifreq)	/* create clone if */
 #define	SIOCIFDESTROY	 _IOW('i', 121, struct ifreq)	/* destroy clone if */
 #define	SIOCIFGCLONERS	_IOWR('i', 120, struct if_clonereq) /* get cloners */
diff -urN sbin.orig/brconfig/Makefile sbin/brconfig/Makefile
--- sbin.orig/brconfig/Makefile	Thu Jan  1 12:00:00 1970
+++ sbin/brconfig/Makefile	Thu Apr 15 22:28:53 2004
@@ -0,0 +1,6 @@
+#	$NetBSD: Makefile,v 1.1 2001/08/17 21:42:10 thorpej Exp $
+
+PROG=	brconfig
+MAN=	brconfig.8
+
+.include <bsd.prog.mk>
diff -urN sbin.orig/brconfig/brconfig.8 sbin/brconfig/brconfig.8
--- sbin.orig/brconfig/brconfig.8	Thu Jan  1 12:00:00 1970
+++ sbin/brconfig/brconfig.8	Thu Apr 15 22:28:53 2004
@@ -0,0 +1,269 @@
+.\"	$NetBSD: brconfig.8,v 1.11 2003/04/29 16:39:00 wiz Exp $
+.\"
+.\" Copyright 2001 Wasabi Systems, Inc.
+.\" All rights reserved.
+.\"
+.\" Written by Jason R. Thorpe for Wasabi Systems, Inc.
+.\"
+.\" 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 for the NetBSD Project by
+.\"	Wasabi Systems, Inc.
+.\" 4. The name of Wasabi Systems, Inc. may not be used to endorse
+.\"    or promote products derived from this software without specific prior
+.\"    written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+.\" 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.
+.\"
+.Dd March 19, 2003
+.Dt BRCONFIG 8
+.Os
+.Sh NAME
+.Nm brconfig
+.Nd configure network bridge parameters
+.Sh SYNOPSIS
+.Nm
+.Fl a
+.Nm
+.Ar bridge
+.Nm
+.Ar bridge
+.Ar command
+.Op Ar args ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to configure network bridge parameters and retrieve
+network bridge parameters and status from the kernel.
+The bridging function is implemented by the
+.Xr bridge 4
+driver.
+.Pp
+A network bridge creates a logical link between two or more
+IEEE 802 networks that use the same (or
+.Dq similar enough )
+framing format.
+For example, it is possible to bridge Ethernet
+and 802.11 networks together, but it is not possible to bridge
+Ethernet and Token Ring together.
+.Pp
+Bridge interfaces are created using the
+.Xr ifconfig 8
+command's
+.Dq create
+sub-command.
+All other bridge configuration is performed using
+.Nm .
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a
+Display the status of all bridge devices present on the system.
+This flag is mutually exclusive with all other sub-commands.
+.El
+.Pp
+All other operations require that a bridge be specified.
+If a bridge is specified with no sub-commands,
+the status of that bridge is displayed.
+The following sub-commands are available:
+.Pp
+.Bl -tag -width indent
+.It Cm up
+Start forwarding packets on the bridge.
+.It Cm down
+Stop forwarding packets on the bridge.
+.It Cm add Ar interface
+Add the interface named by
+.Ar interface
+as a member of the bridge.
+The interface is put into promiscuous mode
+so that it can receive every packet sent on the network.
+.It Cm delete Ar interface
+Remove the interface named by
+.Ar interface
+from the bridge.
+Promiscuous mode is disabled on the interface when
+it is removed from the bridge.
+.It Cm maxaddr Ar size
+Set the size of the bridge address cache to
+.Ar size .
+The default is 100 entries.
+.It Cm timeout Ar seconds
+Set the timeout of address cache entries to
+.Ar seconds
+seconds.
+If
+.Ar seconds
+is zero, then address cache entries will not be expired.
+The default is 240 seconds.
+.It Cm deladdr Ar address
+Delete
+.Ar address
+from the address cache.
+.It Cm flush
+Delete all dynamically-learned addresses from the address cache.
+.It Cm flushall
+Delete all addresses, including static addresses, from the address cache.
+.It Cm discover Ar interface
+Mark an interface as a
+.Dq discovering
+interface.
+When the bridge has no address cache entry
+(either dynamic or static)
+for the destination address of a packet,
+the bridge will forward the packet to all
+member interfaces marked as
+.Dq discovering .
+This is the default for all interfaces added to a bridge.
+.It Cm -discover Ar interface
+Clear the
+.Dq discovering
+attribute on a member interface.
+For packets without the
+.Dq discovering
+attribute, the only packets forwarded on the interface are broadcast
+or multicast packets and packets for which the destination address
+is known to be on the interface's segment.
+.It Cm ipf
+Enable IPF packet filtering on the bridge.
+The current implementation passes
+all ARP and RARP packets through the bridge while filtering IP and ICMP
+packets through IPF.
+.It Cm -ipf
+Disable IPF packet filtering on the bridge (the default).
+.It Cm learn Ar interface
+Mark an interface as a
+.Dq learning
+interface.
+When a packet arrives on such an interface, the source
+address of the packet is entered into the address cache as being a
+destination address on the interface's segment.
+This is the default for all interfaces added to a bridge.
+.It Cm -learn Ar interface
+Clear the
+.Dq learning
+attribute on a member interface.
+.It Cm stp Ar interface
+Enable Spanning Tree protocol on
+.Ar interface .
+The
+.Xr bridge 4
+driver has support for the IEEE 802.1D Spanning Tree protocol (STP).
+Spanning Tree is used to detect and remove loops in a network topology.
+.It Cm -stp Ar interface
+Disable Spanning Tree protocol on
+.Ar interface .
+This is the default for all interfaces added to a bridge.
+.It Cm maxage Ar seconds
+Set the time that a Spanning Tree protocol configuration is valid.
+The default is 20 seconds.
+The minimum is 1 second and the maximum is 255 seconds.
+.It Cm fwddelay Ar seconds
+Set the time that must pass before an interface begins forwarding
+packets when Spanning Tree is enabled.
+The default is 15 seconds.
+The minimum is 1 second and the maximum is 255 seconds.
+.It Cm hellotime Ar seconds
+Set the time between broadcasting of Spanning Tree protocol
+configuration messages.
+The default is 2 seconds.
+The minimum is 1 second and the maximum is 255 seconds.
+.It Cm priority Ar value
+Set the bridge priority for Spanning Tree.
+The default is 32768.
+The minimum is 0 and the maximum is 65536.
+.It Cm ifpriority Ar interface Ar value
+Set the Spanning Tree priority of
+.Ar interface
+to
+.Ar value .
+The default is 128.
+The minimum is 0 and the maximum is 255.
+.It Cm ifpathcost Ar interface Ar value
+Set the Spanning Tree path cost of
+.Ar interface
+to
+.Ar value .
+The default is 55.
+The minimum is 0 and the maximum is 65535.
+.El
+.Sh EXAMPLES
+The following then placed in the file
+.Pa /etc/ifconfig.bridge0
+will cause the a bridge called
+.Sq bridge0
+to be created, and will add the interfaces
+.Sq ray0
+and
+.Sq fxp0
+to the bridge, and then enable packet forwarding.
+Such a configuration could be used to implement a simple
+802.11-to-Ethernet bridge (assuming the 802.11 interface is
+in ad-hoc mode).
+.Bd -literal -offset indent
+create
+!brconfig $int add ray0 add fxp0 up
+.Ed
+.Pp
+Consider a system with two 4-port Ethernet boards.
+The following placed in the file
+.Pa /etc/ifconfig.bridge0
+will cause a bridge consisting of all 8 ports with Spanning Tree
+enabled to be created:
+.Bd -literal -offset indent
+create
+!brconfig $int \e
+    add tlp0 stp tlp0 \e
+    add tlp1 stp tlp1 \e
+    add tlp2 stp tlp2 \e
+    add tlp3 stp tlp3 \e
+    add tlp4 stp tlp4 \e
+    add tlp5 stp tlp5 \e
+    add tlp6 stp tlp6 \e
+    add tlp7 stp tlp7 \e
+    up
+.Ed
+.Sh SEE ALSO
+.Xr bridge 4 ,
+.Xr ifconfig.if 5 ,
+.Xr ifconfig 8 ,
+.Xr ipf 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Nx 1.6 .
+.Sh AUTHORS
+The
+.Xr bridge 4
+driver and
+.Nm
+utility were originally written by
+.An Jason L. Wright
+.Aq jason@thought.net
+as part of an undergraduate independent study at the
+University of North Carolina at Greensboro.
+.Pp
+This version of the
+.Nm
+utility was written from scratch by
+.An Jason R. Thorpe
+.Aq thorpej@wasabisystems.com .
diff -urN sbin.orig/brconfig/brconfig.c sbin/brconfig/brconfig.c
--- sbin.orig/brconfig/brconfig.c	Thu Jan  1 12:00:00 1970
+++ sbin/brconfig/brconfig.c	Thu Apr 15 22:28:53 2004
@@ -0,0 +1,860 @@
+/*	$NetBSD: brconfig.c,v 1.7 2003/09/19 08:39:09 itojun Exp $	*/
+
+/*
+ * Copyright 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * 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 for the NetBSD Project by
+ *	Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+
+/*
+ * brconfig(8) --
+ *
+ *	Configuration utility for the bridge(4) driver.
+ */
+#include <sys/cdefs.h>
+
+#ifndef lint
+__RCSID("$NetBSD: brconfig.c,v 1.7 2003/09/19 08:39:09 itojun Exp $");
+#endif
+
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/if_var.h>
+#include <netinet/in.h> /* for struct arpcom */
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <net/if_bridgevar.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+
+struct command {
+	const char *cmd_keyword;
+	int	cmd_argcnt;
+	int	cmd_flags;
+	void	(*cmd_func)(const struct command *, int, const char *,
+		    char **);
+};
+
+#define	CMD_INVERT	0x01	/* "invert" the sense of the command */
+
+void	cmd_add(const struct command *, int, const char *, char **);
+void	cmd_delete(const struct command *, int, const char *, char **);
+void	cmd_up(const struct command *, int, const char *, char **);
+void	cmd_down(const struct command *, int, const char *, char **);
+void	cmd_discover(const struct command *, int, const char *, char **);
+void	cmd_learn(const struct command *, int, const char *, char **);
+void	cmd_flush(const struct command *, int, const char *, char **);
+void	cmd_flushall(const struct command *, int, const char *, char **);
+void	cmd_static(const struct command *, int, const char *, char **);
+void	cmd_deladdr(const struct command *, int, const char *, char **);
+void	cmd_addr(const struct command *, int, const char *, char **);
+void	cmd_maxaddr(const struct command *, int, const char *, char **);
+void	cmd_hellotime(const struct command *, int, const char *, char **);
+void	cmd_fwddelay(const struct command *, int, const char *, char **);
+void	cmd_maxage(const struct command *, int, const char *, char **);
+void	cmd_priority(const struct command *, int, const char *, char **);
+void	cmd_ifpriority(const struct command *, int, const char *, char **);
+void	cmd_ifpathcost(const struct command *, int, const char *, char **);
+void	cmd_timeout(const struct command *, int, const char *, char **);
+void	cmd_stp(const struct command *, int, const char *, char **);
+void	cmd_ipf(const struct command *, int, const char *, char **);
+
+const struct command command_table[] = {
+	{ "add",		1,	0,		cmd_add },
+	{ "delete",		1,	0,		cmd_delete },
+
+	{ "up",			0,	0,		cmd_up },
+	{ "down",		0,	0,		cmd_down },
+
+	{ "discover",		1,	0,		cmd_discover },
+	{ "-discover",		1,	CMD_INVERT,	cmd_discover },
+
+	{ "learn",		1,	0,		cmd_learn },
+	{ "-learn",		1,	CMD_INVERT,	cmd_learn },
+
+	{ "flush",		0,	0,		cmd_flush },
+	{ "flushall",		0,	0,		cmd_flushall },
+
+	{ "static",		2,	0,		cmd_static },
+	{ "deladdr",		1,	0,		cmd_deladdr },
+
+	{ "addr",		0,	0,		cmd_addr },
+	{ "maxaddr",		1,	0,		cmd_maxaddr },
+
+	{ "hellotime",		1,	0,		cmd_hellotime },
+	{ "fwddelay",		1,	0,		cmd_fwddelay },
+	{ "maxage",		1,	0,		cmd_maxage },
+	{ "priority",		1,	0,		cmd_priority },
+	{ "ifpriority",		2,	0,		cmd_ifpriority },
+	{ "ifpathcost",		2,	0,		cmd_ifpathcost },
+	{ "timeout",		1,	0,		cmd_timeout },
+	{ "stp",		1,	0,		cmd_stp },
+	{ "-stp",		1,	CMD_INVERT,	cmd_stp },
+
+        { "ipf",                0,      0,              cmd_ipf },
+        { "-ipf",               0,      CMD_INVERT,     cmd_ipf },
+
+	{ NULL,			0,	0,		NULL },
+};
+
+void	printall(int);
+void	status(int, const char *);
+int	is_bridge(const char *);
+void	show_config(int, const char *, const char *);
+void	show_interfaces(int, const char *, const char *);
+void	show_addresses(int, const char *, const char *);
+int	get_val(const char *, u_long *);
+int	do_cmd(int, const char *, u_long, void *, size_t, int);
+void	do_ifflag(int, const char *, int, int);
+void	do_bridgeflag(int, const char *, const char *, int, int);
+
+void	printb(const char *, u_int, const char *);
+
+int	main(int, char *[]);
+void	usage(void);
+
+int	aflag;
+
+struct ifreq g_ifr;
+int	g_ifr_updated;
+
+#define	IFFBITS \
+"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6NOTRAILERS\7RUNNING\10NOARP\
+\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2\20MULTICAST"
+
+int
+main(int argc, char *argv[])
+{
+	const struct command *cmd;
+	char *bridge;
+	int sock, ch;
+
+	if (argc < 2)
+		usage();
+
+	sock = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock < 0)
+		err(1, "socket");
+
+	while ((ch = getopt(argc, argv, "a")) != -1) {
+		switch (ch) {
+		case 'a':
+			aflag = 1;
+			break;
+
+		default:
+			usage();
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (aflag) {
+		if (argc != 0)
+			usage();
+		printall(sock);
+		exit(0);
+	}
+
+	if (argc == 0)
+		usage();
+
+	bridge = argv[0];
+
+	if (is_bridge(bridge) == 0)
+		errx(1, "%s is not a bridge", bridge);
+
+	/* Get a copy of the interface flags. */
+	strlcpy(g_ifr.ifr_name, bridge, sizeof(g_ifr.ifr_name));
+	if (ioctl(sock, SIOCGIFFLAGS, &g_ifr) < 0)
+		err(1, "unable to get interface flags");
+
+	argc--;
+	argv++;
+
+	if (argc == 0) {
+		status(sock, bridge);
+		exit(0);
+	}
+
+	while (argc != 0) {
+		for (cmd = command_table; cmd->cmd_keyword != NULL; cmd++) {
+			if (strcmp(cmd->cmd_keyword, argv[0]) == 0)
+				break;
+		}
+		if (cmd->cmd_keyword == NULL)
+			errx(1, "unknown command: %s", argv[0]);
+
+		argc--;
+		argv++;
+
+		if (argc < cmd->cmd_argcnt)
+			errx(1, "command %s requires %d argument%s",
+			    cmd->cmd_keyword, cmd->cmd_argcnt,
+			    cmd->cmd_argcnt == 1 ? "" : "s");
+
+		(*cmd->cmd_func)(cmd, sock, bridge, argv);
+
+		argc -= cmd->cmd_argcnt;
+		argv += cmd->cmd_argcnt;
+	}
+
+	/* If the flags changed, update them. */
+	if (g_ifr_updated && ioctl(sock, SIOCSIFFLAGS, &g_ifr) < 0)
+		err(1, "unable to set interface flags");
+
+	exit (0);
+}
+
+void
+usage(void)
+{
+	static const char *usage_strings[] = {
+		"-a",
+		"<bridge>",
+		"<bridge> up|down",
+		"<bridge> addr",
+		"<bridge> add <interface>",
+		"<bridge> delete <interface>",
+		"<bridge> maxaddr <size>",
+		"<bridge> timeout <time>",
+		"<bridge> static <interface> <address>",
+		"<bridge> deladdr <address>",
+		"<bridge> flush",
+		"<bridge> flushall",
+		"<bridge> ipf|-ipf",
+		"<bridge> discover|-discover <interface>",
+		"<bridge> learn|-learn <interface>",
+		"<bridge> stp|-stp <interface>",
+		"<bridge> maxage <time>",
+		"<bridge> fwddelay <time>",
+		"<bridge> hellotime <time>",
+		"<bridge> priority <value>",
+		"<bridge> ifpriority <interface> <value>",
+		"<bridge> ifpathcost <interface> <value>",
+		NULL,
+	};
+	extern const char *__progname;
+	int i;
+
+	for (i = 0; usage_strings[i] != NULL; i++)
+		fprintf(stderr, "%s %s %s\n",
+		    i == 0 ? "usage:" : "      ",
+		    __progname, usage_strings[i]);
+
+	exit(1);
+}
+
+int
+is_bridge(const char *bridge)
+{
+
+	if (strncmp(bridge, "bridge", 6) != 0 ||
+	    isdigit(bridge[6]) == 0)
+		return (0);
+
+	return (1);
+}
+
+void
+printb(const char *s, u_int v, const char *bits)
+{
+	int i, any = 0;
+	char c;
+
+	if (bits && *bits == 8)
+		printf("%s=%o", s, v);
+	else
+		printf("%s=%x", s, v);
+	bits++;
+	if (bits) { 
+		putchar('<');
+		while ((i = *bits++) != 0) {
+			if (v & (1 << (i-1))) {
+				if (any)
+					putchar(',');
+				any = 1;
+				for (; (c = *bits) > 32; bits++)
+					putchar(c);
+			} else
+				for (; *bits > 32; bits++)
+					;
+		}
+		putchar('>');
+	}
+}
+
+void
+printall(int sock)
+{
+	struct ifaddrs *ifap, *ifa;
+	char *p;
+
+	if (getifaddrs(&ifap) != 0)
+		err(1, "getifaddrs");
+	p = NULL;
+	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+		if (is_bridge(ifa->ifa_name) == 0)
+			continue;
+		if (p != NULL && strcmp(p, ifa->ifa_name) == 0)
+			continue;
+		p = ifa->ifa_name;
+		status(sock, ifa->ifa_name);
+	}
+
+	freeifaddrs(ifap);
+}
+
+void
+status(int sock, const char *bridge)
+{
+	struct ifreq ifr;
+	struct ifbrparam bp1, bp2;
+
+	memset(&ifr, 0, sizeof(ifr));
+
+	strlcpy(ifr.ifr_name, bridge, sizeof(ifr.ifr_name));
+	if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
+		err(1, "unable to get flags");
+
+	printf("%s: ", bridge);
+	printb("flags", ifr.ifr_flags, IFFBITS);
+	printf("\n");
+
+	printf("\tConfiguration:\n");
+	show_config(sock, bridge, "\t\t");
+
+	printf("\tInterfaces:\n");
+	show_interfaces(sock, bridge, "\t\t");
+
+	if (do_cmd(sock, bridge, BRDGGCACHE, &bp1, sizeof(bp1), 0) < 0)
+		err(1, "unable to get address cache size");
+	if (do_cmd(sock, bridge, BRDGGTO, &bp2, sizeof(bp2), 0) < 0)
+		err(1, "unable to get address timeout");
+
+	printf("\tAddress cache (max cache: %u, timeout: %u):\n",
+	    bp1.ifbrp_csize, bp2.ifbrp_ctime);
+	show_addresses(sock, bridge, "\t\t");
+}
+
+void
+show_config(int sock, const char *bridge, const char *prefix)
+{
+	struct ifbrparam param;
+	u_int32_t ipfflags;
+	u_int16_t pri;
+	u_int8_t ht, fd, ma;
+
+	if (do_cmd(sock, bridge, BRDGGPRI, &param, sizeof(param), 0) < 0)
+		err(1, "unable to get bridge priority");
+	pri = param.ifbrp_prio;
+
+	if (do_cmd(sock, bridge, BRDGGHT, &param, sizeof(param), 0) < 0)
+		err(1, "unable to get hellotime");
+	ht = param.ifbrp_hellotime;
+
+	if (do_cmd(sock, bridge, BRDGGFD, &param, sizeof(param), 0) < 0)
+		err(1, "unable to get forward delay");
+	fd = param.ifbrp_fwddelay;
+
+	if (do_cmd(sock, bridge, BRDGGMA, &param, sizeof(param), 0) < 0)
+		err(1, "unable to get max age");
+	ma = param.ifbrp_maxage;
+
+	printf("%spriority %u hellotime %u fwddelay %u maxage %u\n",
+	    prefix, pri, ht, fd, ma);
+
+	if (do_cmd(sock, bridge, BRDGGFILT, &param, sizeof(param), 0) < 0) {
+		/* err(1, "unable to get ipfilter status"); */
+		param.ifbrp_filter = 0;
+	}
+
+	ipfflags = param.ifbrp_filter;
+	printf("%sipfilter %s flags 0x%x\n", prefix,
+		(ipfflags & IFBF_FILT_USEIPF) ? "enabled" : "disabled",
+		ipfflags);
+}
+
+void
+show_interfaces(int sock, const char *bridge, const char *prefix)
+{
+	static const char *stpstates[] = {
+		"disabled",
+		"listening",
+		"learning",
+		"forwarding",
+		"blocking",
+	};
+	struct ifbifconf bifc;
+	struct ifbreq *req;
+	char *inbuf = NULL, *ninbuf;
+	int i, len = 8192;
+
+	for (;;) {
+		ninbuf = realloc(inbuf, len);
+		if (ninbuf == NULL)
+			err(1, "unable to allocate interface buffer");
+		bifc.ifbic_len = len;
+		bifc.ifbic_buf = inbuf = ninbuf;
+		if (do_cmd(sock, bridge, BRDGGIFS, &bifc, sizeof(bifc), 0) < 0)
+			err(1, "unable to get interface list");
+		if ((bifc.ifbic_len + sizeof(*req)) < len)
+			break;
+		len *= 2;
+	}
+
+	for (i = 0; i < bifc.ifbic_len / sizeof(*req); i++) {
+		req = bifc.ifbic_req + i;
+		printf("%s%s ", prefix, req->ifbr_ifsname);
+		printb("flags", req->ifbr_ifsflags, IFBIFBITS);
+		printf("\n");
+		printf("%s\t", prefix);
+		printf("port %u priority %u",
+		    req->ifbr_portno, req->ifbr_priority);
+		if (req->ifbr_ifsflags & IFBIF_STP) {
+			printf(" path cost %u", req->ifbr_path_cost);
+			if (req->ifbr_state <
+			    sizeof(stpstates) / sizeof(stpstates[0]))
+				printf(" %s", stpstates[req->ifbr_state]);
+			else
+				printf(" <unknown state %d>",
+				    req->ifbr_state);
+		}
+		printf("\n");
+	}
+
+	free(inbuf);
+}
+
+void
+show_addresses(int sock, const char *bridge, const char *prefix)
+{
+	struct ifbaconf ifbac;
+	struct ifbareq *ifba;
+	char *inbuf = NULL, *ninbuf;
+	int i, len = 8192;
+	struct ether_addr ea;
+
+	for (;;) {
+		ninbuf = realloc(inbuf, len);
+		if (ninbuf == NULL)
+			err(1, "unable to allocate address buffer");
+		ifbac.ifbac_len = len;
+		ifbac.ifbac_buf = inbuf = ninbuf;
+		if (do_cmd(sock, bridge, BRDGRTS, &ifbac, sizeof(ifbac), 0) < 0)
+			err(1, "unable to get address cache");
+		if ((ifbac.ifbac_len + sizeof(*ifba)) < len)
+			break;
+		len *= 2;
+	}
+
+	for (i = 0; i < ifbac.ifbac_len / sizeof(*ifba); i++) {
+		ifba = ifbac.ifbac_req + i;
+		memcpy(ea.octet, ifba->ifba_dst,
+		    sizeof(ea.octet));
+		printf("%s%s %s %lu ", prefix, ether_ntoa(&ea),
+		    ifba->ifba_ifsname, ifba->ifba_expire);
+		printb("flags", ifba->ifba_flags, IFBAFBITS);
+		printf("\n");
+	}
+
+	free(inbuf);
+}
+
+int
+get_val(const char *cp, u_long *valp)
+{
+	char *endptr;
+	u_long val;
+
+	errno = 0;
+	val = strtoul(cp, &endptr, 0);
+	if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE)
+		return (-1);
+
+	*valp = val;
+	return (0);
+}
+
+int
+do_cmd(int sock, const char *bridge, u_long op, void *arg, size_t argsize,
+    int set)
+{
+	struct ifdrv ifd;
+
+	memset(&ifd, 0, sizeof(ifd));
+
+	strlcpy(ifd.ifd_name, bridge, sizeof(ifd.ifd_name));
+	ifd.ifd_cmd = op;
+	ifd.ifd_len = argsize;
+	ifd.ifd_data = arg;
+
+	return (ioctl(sock, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd));
+}
+
+void
+do_ifflag(int sock, const char *bridge, int flag, int set)
+{
+
+	if (set)
+		g_ifr.ifr_flags |= flag;
+	else
+		g_ifr.ifr_flags &= ~flag;
+
+	g_ifr_updated = 1;
+}
+
+void
+do_bridgeflag(int sock, const char *bridge, const char *ifs, int flag,
+    int set)
+{
+	struct ifbreq req;
+
+	strlcpy(req.ifbr_ifsname, ifs, sizeof(req.ifbr_ifsname));
+
+	if (do_cmd(sock, bridge, BRDGGIFFLGS, &req, sizeof(req), 0) < 0)
+		err(1, "unable to get bridge flags");
+
+	if (set)
+		req.ifbr_ifsflags |= flag;
+	else
+		req.ifbr_ifsflags &= ~flag;
+
+	if (do_cmd(sock, bridge, BRDGSIFFLGS, &req, sizeof(req), 1) < 0)
+		err(1, "unable to set bridge flags");
+}
+
+void
+cmd_add(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+	struct ifbreq req;
+
+	memset(&req, 0, sizeof(req));
+
+	strlcpy(req.ifbr_ifsname, argv[0], sizeof(req.ifbr_ifsname));
+	if (do_cmd(sock, bridge, BRDGADD, &req, sizeof(req), 1) < 0)
+		err(1, "%s %s", cmd->cmd_keyword, argv[0]);
+}
+
+void
+cmd_delete(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+	struct ifbreq req;
+
+	memset(&req, 0, sizeof(req));
+	strlcpy(req.ifbr_ifsname, argv[0], sizeof(req.ifbr_ifsname));
+	if (do_cmd(sock, bridge, BRDGDEL, &req, sizeof(req), 1) < 0)
+		err(1, "%s %s", cmd->cmd_keyword, argv[0]);
+}
+
+void
+cmd_up(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+
+	do_ifflag(sock, bridge, IFF_UP, 1);
+}
+
+void
+cmd_down(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+
+	do_ifflag(sock, bridge, IFF_UP, 0);
+}
+
+void
+cmd_discover(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+
+	do_bridgeflag(sock, bridge, argv[0], IFBIF_DISCOVER,
+	    (cmd->cmd_flags & CMD_INVERT) ? 0 : 1);
+}
+
+void
+cmd_learn(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+
+	do_bridgeflag(sock, bridge, argv[0], IFBIF_LEARNING,
+	    (cmd->cmd_flags & CMD_INVERT) ? 0 : 1);
+}
+
+void
+cmd_stp(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+
+	do_bridgeflag(sock, bridge, argv[0], IFBIF_STP,
+	    (cmd->cmd_flags & CMD_INVERT) ? 0 : 1);
+}
+
+void
+cmd_flush(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+	struct ifbreq req;
+
+	memset(&req, 0, sizeof(req));
+	req.ifbr_ifsflags = IFBF_FLUSHDYN;
+	if (do_cmd(sock, bridge, BRDGFLUSH, &req, sizeof(req), 1) < 0)
+		err(1, "%s", cmd->cmd_keyword);
+}
+
+void
+cmd_flushall(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+	struct ifbreq req;
+
+	memset(&req, 0, sizeof(req));
+	req.ifbr_ifsflags = IFBF_FLUSHALL;
+	if (do_cmd(sock, bridge, BRDGFLUSH, &req, sizeof(req), 1) < 0)
+		err(1, "%s", cmd->cmd_keyword);
+}
+
+void
+cmd_static(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+	struct ifbareq req;
+	struct ether_addr *ea;
+
+	memset(&req, 0, sizeof(req));
+	strlcpy(req.ifba_ifsname, argv[0], sizeof(req.ifba_ifsname));
+
+	ea = ether_aton(argv[1]);
+	if (ea == NULL)
+		errx(1, "%s: invalid address: %s", cmd->cmd_keyword, argv[1]);
+
+	memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst));
+	req.ifba_flags = IFBAF_STATIC;
+
+	if (do_cmd(sock, bridge, BRDGSADDR, &req, sizeof(req), 1) < 0)
+		err(1, "%s %s %s", cmd->cmd_keyword, argv[0], argv[1]);
+}
+
+void
+cmd_deladdr(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+	struct ifbareq req;
+	struct ether_addr *ea;
+
+	memset(&req, 0, sizeof(req));
+
+	ea = ether_aton(argv[0]);
+	if (ea == NULL)
+		errx(1, "%s: invalid address: %s", cmd->cmd_keyword, argv[0]);
+
+	memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst));
+
+	if (do_cmd(sock, bridge, BRDGDADDR, &req, sizeof(req), 1) < 0)
+		err(1, "%s %s", cmd->cmd_keyword, argv[0]);
+}
+
+void
+cmd_addr(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+
+	show_addresses(sock, bridge, "\t");
+}
+
+void
+cmd_maxaddr(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+	struct ifbrparam param;
+	u_long val;
+
+	if (get_val(argv[0], &val) < 0 || (val & ~0xffffffff) != 0)
+		errx(1, "%s: invalid value: %s", cmd->cmd_keyword, argv[0]);
+
+	param.ifbrp_csize = val & 0xffffffff;
+
+	if (do_cmd(sock, bridge, BRDGSCACHE, &param, sizeof(param), 1) < 0)
+		err(1, "%s %s", cmd->cmd_keyword, argv[0]);
+}
+
+void
+cmd_hellotime(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+	struct ifbrparam param;
+	u_long val;
+
+	if (get_val(argv[0], &val) < 0 || (val & ~0xff) != 0)
+		errx(1, "%s: invalid value: %s", cmd->cmd_keyword, argv[0]);
+
+	param.ifbrp_hellotime = val & 0xff;
+
+	if (do_cmd(sock, bridge, BRDGSHT, &param, sizeof(param), 1) < 0)
+		err(1, "%s %s", cmd->cmd_keyword, argv[0]);
+}
+
+void
+cmd_fwddelay(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+	struct ifbrparam param;
+	u_long val;
+
+	if (get_val(argv[0], &val) < 0 || (val & ~0xff) != 0)
+		errx(1, "%s: invalid value: %s", cmd->cmd_keyword, argv[0]);
+
+	param.ifbrp_fwddelay = val & 0xff;
+
+	if (do_cmd(sock, bridge, BRDGSFD, &param, sizeof(param), 1) < 0)
+		err(1, "%s %s", cmd->cmd_keyword, argv[0]);
+}
+
+void
+cmd_maxage(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+	struct ifbrparam param;
+	u_long val;
+
+	if (get_val(argv[0], &val) < 0 || (val & ~0xff) != 0)
+		errx(1, "%s: invalid value: %s", cmd->cmd_keyword, argv[0]);
+
+	param.ifbrp_maxage = val & 0xff;
+
+	if (do_cmd(sock, bridge, BRDGSMA, &param, sizeof(param), 1) < 0)
+		err(1, "%s %s", cmd->cmd_keyword, argv[0]);
+}
+
+void
+cmd_priority(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+	struct ifbrparam param;
+	u_long val;
+
+	if (get_val(argv[0], &val) < 0 || (val & ~0xffff) != 0)
+		errx(1, "%s: invalid value: %s", cmd->cmd_keyword, argv[0]);
+
+	param.ifbrp_prio = val & 0xffff;
+
+	if (do_cmd(sock, bridge, BRDGSPRI, &param, sizeof(param), 1) < 0)
+		err(1, "%s %s", cmd->cmd_keyword, argv[0]);
+}
+
+void
+cmd_ifpriority(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+	struct ifbreq req;
+	u_long val;
+
+	memset(&req, 0, sizeof(req));
+
+	if (get_val(argv[1], &val) < 0 || (val & ~0xff) != 0)
+		errx(1, "%s: invalid value: %s", cmd->cmd_keyword, argv[1]);
+
+	strlcpy(req.ifbr_ifsname, argv[0], sizeof(req.ifbr_ifsname));
+	req.ifbr_priority = val & 0xff;
+
+	if (do_cmd(sock, bridge, BRDGSIFPRIO, &req, sizeof(req), 1) < 0)
+		err(1, "%s %s", cmd->cmd_keyword, argv[0]);
+}
+
+void
+cmd_ifpathcost(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+	struct ifbreq req;
+	u_long val;
+
+	memset(&req, 0, sizeof(req));
+
+	if (get_val(argv[1], &val) < 0 || (val & ~0xff) != 0)
+		errx(1, "%s: invalid value: %s", cmd->cmd_keyword, argv[1]);
+
+	strlcpy(req.ifbr_ifsname, argv[0], sizeof(req.ifbr_ifsname));
+	req.ifbr_path_cost = val & 0xffff;
+
+	if (do_cmd(sock, bridge, BRDGSIFCOST, &req, sizeof(req), 1) < 0)
+		err(1, "%s %s", cmd->cmd_keyword, argv[0]);
+}
+
+void
+cmd_timeout(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+	struct ifbrparam param;
+	u_long val;
+
+	if (get_val(argv[0], &val) < 0 || (val & ~0xffffffff) != 0)
+		errx(1, "%s: invalid value: %s", cmd->cmd_keyword, argv[0]);
+
+	param.ifbrp_ctime = val & 0xffffffff;
+
+	if (do_cmd(sock, bridge, BRDGSTO, &param, sizeof(param), 1) < 0)
+		err(1, "%s %s", cmd->cmd_keyword, argv[0]);
+}
+
+void
+cmd_ipf(const struct command *cmd, int sock, const char *bridge,
+    char **argv)
+{
+        struct ifbrparam param;
+
+        if (do_cmd(sock, bridge, BRDGGFILT, &param, sizeof(param), 0) < 0)
+		err(1, "%s", cmd->cmd_keyword);
+
+        param.ifbrp_filter &= ~IFBF_FILT_USEIPF;
+        param.ifbrp_filter |= (cmd->cmd_flags & CMD_INVERT) ? 0 : IFBF_FILT_USEIPF;
+        if (do_cmd(sock, bridge, BRDGSFILT, &param, sizeof(param), 1) < 0)
+		err(1, "%s %x", cmd->cmd_keyword, param.ifbrp_filter);
+}

--MGYHOYXEY6WxJCY8--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20040417035758.GA66806>