From owner-freebsd-current@FreeBSD.ORG Fri Apr 16 20:58:10 2004 Return-Path: Delivered-To: freebsd-current@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 45F8616A4CE for ; Fri, 16 Apr 2004 20:58:10 -0700 (PDT) Received: from kate.fud.org.nz (203-79-110-29.cable.paradise.net.nz [203.79.110.29]) by mx1.FreeBSD.org (Postfix) with ESMTP id EEB2A43D2F for ; Fri, 16 Apr 2004 20:57:59 -0700 (PDT) (envelope-from andy@fud.org.nz) Received: by kate.fud.org.nz (Postfix, from userid 1001) id B17A317044; Sat, 17 Apr 2004 15:57:58 +1200 (NZST) Date: Sat, 17 Apr 2004 15:57:58 +1200 From: Andrew Thompson To: current@freebsd.org Message-ID: <20040417035758.GA66806@kate.fud.org.nz> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="MGYHOYXEY6WxJCY8" Content-Disposition: inline User-Agent: Mutt/1.5.6i Subject: RFC: ported NetBSD if_bridge X-BeenThere: freebsd-current@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: Discussions about the use of FreeBSD-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 17 Apr 2004 03:58:10 -0000 --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 add 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 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include /* for net/if.h */ +#include +#include /* string functions */ +#include +#include +#include + +#include +#include +#include +#include + +#include /* for struct arpcom */ +#include +#include +#include +#include /* for struct arpcom */ +#include + +#include +#include +#include +#include + +#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 +#include + +/* + * 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 #include #include +#include #include #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 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 + +#ifndef lint +__RCSID("$NetBSD: brconfig.c,v 1.7 2003/09/19 08:39:09 itojun Exp $"); +#endif + + +#include +#include +#include + +#include +#include +#include +#include +#include /* for struct arpcom */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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", + "", + " up|down", + " addr", + " add ", + " delete ", + " maxaddr ", + " timeout