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