Index: stable/6/sys/net/bridgestp.c =================================================================== --- stable/6/sys/net/bridgestp.c (revision 151569) +++ stable/6/sys/net/bridgestp.c (revision 151570) @@ -1,1172 +1,1171 @@ /* $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 #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; BRIDGE_LOCK_ASSERT(sc); ifp = bif->bif_ifp; if ((ifp->if_drv_flags & IFF_DRV_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)); /* XXX: safe here?!? */ BRIDGE_UNLOCK(sc); bridge_enqueue(sc, ifp, m); BRIDGE_LOCK(sc); } 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; BRIDGE_LOCK_ASSERT(sc); if ((ifp->if_drv_flags & IFF_DRV_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)); /* XXX: safe here?!? */ BRIDGE_UNLOCK(sc); bridge_enqueue(sc, ifp, m); BRIDGE_LOCK(sc); } void bstp_configuration_update(struct bridge_softc *sc) { BRIDGE_LOCK_ASSERT(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; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); 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) { BRIDGE_LOCK_ASSERT(sc); 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); bridge_rtdelete(sc, bif->bif_ifp, IFBF_FLUSHDYN); 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; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); 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); bridge_rtdelete(sc, bif->bif_ifp, IFBF_FLUSHDYN); 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; BRIDGE_LOCK_ASSERT(sc); 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_linkstate(struct ifnet *ifp, int state) { struct bridge_softc *sc; struct bridge_iflist *bif; sc = ifp->if_bridge; BRIDGE_LOCK(sc); LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if ((bif->bif_flags & IFBIF_STP) == 0) continue; if (bif->bif_ifp == ifp) { bstp_ifupdstatus(sc, bif); break; } } BRIDGE_UNLOCK(sc); } void bstp_ifupdstatus(struct bridge_softc *sc, struct bridge_iflist *bif) { struct ifnet *ifp = bif->bif_ifp; struct ifmediareq ifmr; int error = 0; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); #if 0 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); } #endif 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_ifp->if_drv_flags & IFF_DRV_RUNNING) callout_reset(&sc->sc_bstpcallout, hz, bstp_tick, sc); } 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); } Index: stable/6/sys/net/if_bridge.c =================================================================== --- stable/6/sys/net/if_bridge.c (revision 151569) +++ stable/6/sys/net/if_bridge.c (revision 151570) @@ -1,2660 +1,2667 @@ /* $NetBSD: if_bridge.c,v 1.31 2005/06/01 19:45:34 jdc 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 "opt_inet.h" #include "opt_inet6.h" #include #include #include #include #include #include #include /* for net/if.h */ #include #include /* string functions */ #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include /* for struct arpcom */ #include #include #include #include #ifdef INET6 #include #include #endif #include #include /* for struct arpcom */ #include #include #include #include #include /* * 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 static struct mtx bridge_list_mtx; -extern struct mbuf *(*bridge_input_p)(struct ifnet *, struct mbuf *); -extern int (*bridge_output_p)(struct ifnet *, struct mbuf *, - struct sockaddr *, struct rtentry *); -extern void (*bridge_dn_p)(struct mbuf *, struct ifnet *); -extern void (*bridge_detach_p)(struct ifnet *); - int bridge_rtable_prune_period = BRIDGE_RTABLE_PRUNE_PERIOD; uma_zone_t bridge_rtnode_zone; int bridge_clone_create(struct if_clone *, int); void bridge_clone_destroy(struct ifnet *); int bridge_ioctl(struct ifnet *, u_long, caddr_t); +void bridge_ifdetach(struct ifnet *); static void bridge_init(void *); +void bridge_dummynet(struct mbuf *, struct ifnet *); void bridge_stop(struct ifnet *, int); void bridge_start(struct ifnet *); +struct mbuf *bridge_input(struct ifnet *, struct mbuf *); +int bridge_output(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); void bridge_forward(struct bridge_softc *, struct mbuf *m); void bridge_timer(void *); void bridge_broadcast(struct bridge_softc *, struct ifnet *, struct mbuf *, int); 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 *); 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 *); static int bridge_pfil(struct mbuf **, struct ifnet *, struct ifnet *, int); static int bridge_ip_checkbasic(struct mbuf **mp); # ifdef INET6 static int bridge_ip6_checkbasic(struct mbuf **mp); # endif /* INET6 */ SYSCTL_DECL(_net_link); SYSCTL_NODE(_net_link, IFT_BRIDGE, bridge, CTLFLAG_RW, 0, "Bridge"); static int pfil_bridge = 1; /* run pfil hooks on the bridge interface */ static int pfil_member = 1; /* run pfil hooks on the member interface */ static int pfil_ipfw = 0; /* layer2 filter with ipfw */ SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_bridge, CTLFLAG_RW, &pfil_bridge, 0, "Packet filter on the bridge interface"); SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_member, CTLFLAG_RW, &pfil_member, 0, "Packet filter on the member interface"); 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 }, }; const int bridge_control_table_size = sizeof(bridge_control_table) / sizeof(bridge_control_table[0]); +static const u_char etherbroadcastaddr[ETHER_ADDR_LEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + LIST_HEAD(, bridge_softc) bridge_list; IFC_SIMPLE_DECLARE(bridge, 0); static int bridge_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: mtx_init(&bridge_list_mtx, "if_bridge list", NULL, MTX_DEF); if_clone_attach(&bridge_cloner); bridge_rtnode_zone = uma_zcreate("bridge_rtnode", sizeof(struct bridge_rtnode), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); LIST_INIT(&bridge_list); bridge_input_p = bridge_input; bridge_output_p = bridge_output; bridge_dn_p = bridge_dummynet; bridge_detach_p = bridge_ifdetach; bstp_linkstate_p = bstp_linkstate; break; case MOD_UNLOAD: if_clone_detach(&bridge_cloner); while (!LIST_EMPTY(&bridge_list)) bridge_clone_destroy(LIST_FIRST(&bridge_list)->sc_ifp); uma_zdestroy(bridge_rtnode_zone); bridge_input_p = NULL; bridge_output_p = NULL; bridge_dn_p = NULL; bridge_detach_p = NULL; bstp_linkstate_p = NULL; mtx_destroy(&bridge_list_mtx); break; default: return EOPNOTSUPP; } return 0; } static moduledata_t bridge_mod = { "if_bridge", bridge_modevent, 0 }; DECLARE_MODULE(if_bridge, bridge_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); /* * handler for net.link.bridge.pfil_ipfw */ static int sysctl_pfil_ipfw(SYSCTL_HANDLER_ARGS) { int enable = pfil_ipfw; int error; error = sysctl_handle_int(oidp, &enable, 0, req); enable = (enable) ? 1 : 0; if (enable != pfil_ipfw) { pfil_ipfw = enable; /* * Disable pfil so that ipfw doesnt run twice, if the user really wants * both then they can re-enable pfil_bridge and/or pfil_member. */ if (pfil_ipfw) { pfil_bridge = 0; pfil_member = 0; } } return error; } SYSCTL_PROC(_net_link_bridge, OID_AUTO, ipfw, CTLTYPE_INT|CTLFLAG_RW, &pfil_ipfw, 0, &sysctl_pfil_ipfw, "I", "Layer2 filter with IPFW"); /* * 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; u_char eaddr[6]; sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); BRIDGE_LOCK_INIT(sc); ifp = sc->sc_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { free(sc, M_DEVBUF); return (ENOSPC); } 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; /* Initialize our routing table. */ bridge_rtable_init(sc); callout_init_mtx(&sc->sc_brcallout, &sc->sc_mtx, 0); callout_init_mtx(&sc->sc_bstpcallout, &sc->sc_mtx, 0); LIST_INIT(&sc->sc_iflist); ifp->if_softc = sc; if_initname(ifp, ifc->ifc_name, unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_MULTICAST; ifp->if_ioctl = bridge_ioctl; ifp->if_output = bridge_output; ifp->if_start = bridge_start; ifp->if_init = bridge_init; ifp->if_type = IFT_BRIDGE; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; IFQ_SET_READY(&ifp->if_snd); ifp->if_hdrlen = ETHER_HDR_LEN; /* * Generate a random ethernet address and use the private AC:DE:48 * OUI code. */ arc4rand(eaddr, ETHER_ADDR_LEN, 1); eaddr[0] = 0xAC; eaddr[1] = 0xDE; eaddr[2] = 0x48; ether_ifattach(ifp, eaddr); /* Now undo some of the damage... */ ifp->if_baudrate = 0; ifp->if_type = IFT_BRIDGE; mtx_lock(&bridge_list_mtx); LIST_INSERT_HEAD(&bridge_list, sc, sc_list); mtx_unlock(&bridge_list_mtx); 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; BRIDGE_LOCK(sc); bridge_stop(ifp, 1); ifp->if_flags &= ~IFF_UP; while ((bif = LIST_FIRST(&sc->sc_iflist)) != NULL) bridge_delete_member(sc, bif); BRIDGE_UNLOCK(sc); callout_drain(&sc->sc_brcallout); callout_drain(&sc->sc_bstpcallout); mtx_lock(&bridge_list_mtx); LIST_REMOVE(sc, sc_list); mtx_unlock(&bridge_list_mtx); ether_ifdetach(ifp); if_free_type(ifp, IFT_ETHER); /* Tear down the routing table. */ bridge_rtable_fini(sc); BRIDGE_LOCK_DESTROY(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 error = 0; BRIDGE_LOCK(sc); switch (cmd) { case SIOCADDMULTI: case SIOCDELMULTI: break; 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) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) { /* * If interface is marked down and it is running, * then stop and disable it. */ bridge_stop(ifp, 1); } else if ((ifp->if_flags & IFF_UP) && !(ifp->if_drv_flags & IFF_DRV_RUNNING)) { /* * If interface is marked up and it is stopped, then * start it. */ BRIDGE_UNLOCK(sc); (*ifp->if_init)(sc); } break; case SIOCSIFMTU: /* Do not allow the MTU to be changed on the bridge */ error = EINVAL; break; default: /* * drop the lock as ether_ioctl() will call bridge_start() and * cause the lock to be recursed. */ BRIDGE_UNLOCK(sc); error = ether_ioctl(ifp, cmd, data); break; } if (BRIDGE_LOCKED(sc)) BRIDGE_UNLOCK(sc); return (error); } /* * bridge_lookup_member: * * Lookup a bridge member interface. */ struct bridge_iflist * bridge_lookup_member(struct bridge_softc *sc, const char *name) { struct bridge_iflist *bif; struct ifnet *ifp; BRIDGE_LOCK_ASSERT(sc); 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*. */ struct bridge_iflist * bridge_lookup_member_if(struct bridge_softc *sc, struct ifnet *member_ifp) { struct bridge_iflist *bif; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); switch (ifs->if_type) { case IFT_ETHER: case IFT_L2VLAN: /* * 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; BRIDGE_XLOCK(sc); LIST_REMOVE(bif, bif_next); BRIDGE_XDROP(sc); bridge_rtdelete(sc, ifs, IFBF_FLUSHALL); free(bif, M_DEVBUF); if (sc->sc_ifp->if_drv_flags & IFF_DRV_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; BRIDGE_LOCK_ASSERT(sc); ifs = ifunit(req->ifbr_ifsname); if (ifs == NULL) return (ENOENT); /* Allow the first member to define the MTU */ if (LIST_EMPTY(&sc->sc_iflist)) sc->sc_ifp->if_mtu = ifs->if_mtu; else if (sc->sc_ifp->if_mtu != ifs->if_mtu) { if_printf(sc->sc_ifp, "invalid MTU for %s\n", ifs->if_xname); 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: case IFT_L2VLAN: /* * 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; /* * XXX: XLOCK HERE!?! * * NOTE: insert_***HEAD*** should be safe for the traversals. */ LIST_INSERT_HEAD(&sc->sc_iflist, bif, bif_next); if (sc->sc_ifp->if_drv_flags & IFF_DRV_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; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); 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_ifp->if_drv_flags & IFF_DRV_RUNNING) bstp_initialization(sc); return (0); } int bridge_ioctl_scache(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); 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 && tv.tv_sec < brt->brt_expire) 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; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); sc->sc_brttimeout = param->ifbrp_ctime; return (0); } int bridge_ioctl_gto(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; BRIDGE_LOCK_ASSERT(sc); param->ifbrp_ctime = sc->sc_brttimeout; return (0); } int bridge_ioctl_daddr(struct bridge_softc *sc, void *arg) { struct ifbareq *req = arg; BRIDGE_LOCK_ASSERT(sc); return (bridge_rtdaddr(sc, req->ifba_dst)); } int bridge_ioctl_flush(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; BRIDGE_LOCK_ASSERT(sc); bridge_rtflush(sc, req->ifbr_ifsflags); return (0); } int bridge_ioctl_gpri(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; BRIDGE_LOCK_ASSERT(sc); param->ifbrp_prio = sc->sc_bridge_priority; return (0); } int bridge_ioctl_spri(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; BRIDGE_LOCK_ASSERT(sc); sc->sc_bridge_priority = param->ifbrp_prio; if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) bstp_initialization(sc); return (0); } int bridge_ioctl_ght(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); if (param->ifbrp_hellotime == 0) return (EINVAL); sc->sc_bridge_hello_time = param->ifbrp_hellotime << 8; if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) bstp_initialization(sc); return (0); } int bridge_ioctl_gfd(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); if (param->ifbrp_fwddelay == 0) return (EINVAL); sc->sc_bridge_forward_delay = param->ifbrp_fwddelay << 8; if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) bstp_initialization(sc); return (0); } int bridge_ioctl_gma(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); if (param->ifbrp_maxage == 0) return (EINVAL); sc->sc_bridge_max_age = param->ifbrp_maxage << 8; if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) bstp_initialization(sc); return (0); } int bridge_ioctl_sifprio(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; BRIDGE_LOCK_ASSERT(sc); bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (ENOENT); bif->bif_priority = req->ifbr_priority; if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) bstp_initialization(sc); return (0); } int bridge_ioctl_sifcost(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; BRIDGE_LOCK_ASSERT(sc); bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (ENOENT); bif->bif_path_cost = req->ifbr_path_cost; if (sc->sc_ifp->if_drv_flags & IFF_DRV_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; BRIDGE_LOCK(sc); memset(&breq, 0, sizeof(breq)); snprintf(breq.ifbr_ifsname, sizeof(breq.ifbr_ifsname), ifp->if_xname); (void) bridge_ioctl_del(sc, &breq); BRIDGE_UNLOCK(sc); } /* * 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_ifp; if (ifp->if_drv_flags & IFF_DRV_RUNNING) return; BRIDGE_LOCK(sc); callout_reset(&sc->sc_brcallout, bridge_rtable_prune_period * hz, bridge_timer, sc); ifp->if_drv_flags |= IFF_DRV_RUNNING; bstp_initialization(sc); BRIDGE_UNLOCK(sc); return; } /* * bridge_stop: * * Stop the bridge interface. */ void bridge_stop(struct ifnet *ifp, int disable) { struct bridge_softc *sc = ifp->if_softc; BRIDGE_LOCK_ASSERT(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; callout_stop(&sc->sc_brcallout); bstp_stop(sc); bridge_rtflush(sc, IFBF_FLUSHDYN); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } /* * bridge_enqueue: * * Enqueue a packet on a bridge member interface. * */ __inline void bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m) { int len, err; short mflags; /* * Clear any in-bound checksum flags for this packet. */ m->m_pkthdr.csum_flags = 0; len = m->m_pkthdr.len; mflags = m->m_flags; IFQ_ENQUEUE(&dst_ifp->if_snd, m, err); if (err == 0) { sc->sc_ifp->if_opackets++; sc->sc_ifp->if_obytes += len; dst_ifp->if_obytes += len; if (mflags & M_MCAST) { sc->sc_ifp->if_omcasts++; dst_ifp->if_omcasts++; } } if ((dst_ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) (*dst_ifp->if_start)(dst_ifp); } /* * bridge_dummynet: * * Receive a queued packet from dummynet and pass it on to the output * interface. * * The mbuf has the Ethernet header already attached. */ void bridge_dummynet(struct mbuf *m, struct ifnet *ifp) { struct bridge_softc *sc; sc = ifp->if_bridge; /* * The packet didnt originate from a member interface. This should only * ever happen if a member interface is removed while packets are * queued for it. */ if (sc == NULL) { m_freem(m); return; } if (inet_pfil_hook.ph_busy_count >= 0 #ifdef INET6 || inet6_pfil_hook.ph_busy_count >= 0 #endif ) { if (bridge_pfil(&m, sc->sc_ifp, ifp, PFIL_OUT) != 0) return; if (m == NULL) return; } bridge_enqueue(sc, ifp, m); } /* * 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; 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; BRIDGE_LOCK(sc); /* * 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_ifp->if_drv_flags & IFF_DRV_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 error = 0, used = 0; BRIDGE_LOCK2REF(sc, error); if (error) { m_freem(m); return (0); } LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { dst_if = bif->bif_ifp; if ((dst_if->if_drv_flags & IFF_DRV_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_copypacket(m, M_DONTWAIT); if (mc == NULL) { sc->sc_ifp->if_oerrors++; continue; } } bridge_enqueue(sc, dst_if, mc); } if (used == 0) m_freem(m); BRIDGE_UNREF(sc); return (0); } sendunicast: /* * XXX Spanning tree consideration here? */ if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) { m_freem(m); BRIDGE_UNLOCK(sc); return (0); } BRIDGE_UNLOCK(sc); bridge_enqueue(sc, dst_if, m); return (0); } /* * bridge_start: * * Start output on a bridge. * */ void bridge_start(struct ifnet *ifp) { struct bridge_softc *sc; struct mbuf *m; struct ether_header *eh; struct ifnet *dst_if; sc = ifp->if_softc; ifp->if_drv_flags |= IFF_DRV_OACTIVE; for (;;) { IFQ_DEQUEUE(&ifp->if_snd, m); if (m == 0) break; BPF_MTAP(ifp, m); eh = mtod(m, struct ether_header *); dst_if = NULL; BRIDGE_LOCK(sc); if ((m->m_flags & (M_BCAST|M_MCAST)) == 0) { dst_if = bridge_rtlookup(sc, eh->ether_dhost); } if (dst_if == NULL) bridge_broadcast(sc, ifp, m, 0); else { BRIDGE_UNLOCK(sc); bridge_enqueue(sc, dst_if, m); } } ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; return; } /* * bridge_forward: * * The forwarding function of the bridge. * * NOTE: Releases the lock on return. */ void bridge_forward(struct bridge_softc *sc, struct mbuf *m) { struct bridge_iflist *bif; struct ifnet *src_if, *dst_if, *ifp; struct ether_header *eh; src_if = m->m_pkthdr.rcvif; BRIDGE_LOCK_ASSERT(sc); ifp = sc->sc_ifp; sc->sc_ifp->if_ipackets++; sc->sc_ifp->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?) */ BRIDGE_UNLOCK(sc); 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: BRIDGE_UNLOCK(sc); 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); BRIDGE_UNLOCK(sc); 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) { BRIDGE_UNLOCK(sc); m_freem(m); return; } } else { /* ...forward it to all interfaces. */ sc->sc_ifp->if_imcasts++; dst_if = NULL; } /* run the packet filter */ if (inet_pfil_hook.ph_busy_count >= 0 #ifdef INET6 || inet6_pfil_hook.ph_busy_count >= 0 #endif ) { BRIDGE_UNLOCK(sc); if (bridge_pfil(&m, ifp, src_if, PFIL_IN) != 0) return; if (m == NULL) return; BRIDGE_LOCK(sc); } if (dst_if == NULL) { /* tap off packets passing the bridge */ BPF_MTAP(ifp, m); bridge_broadcast(sc, src_if, m, 1); return; } /* * At this point, we're dealing with a unicast frame * going to a different interface. */ if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) { BRIDGE_UNLOCK(sc); m_freem(m); return; } bif = bridge_lookup_member_if(sc, dst_if); if (bif == NULL) { /* Not a member of the bridge (anymore?) */ BRIDGE_UNLOCK(sc); m_freem(m); return; } if (bif->bif_flags & IFBIF_STP) { switch (bif->bif_state) { case BSTP_IFSTATE_DISABLED: case BSTP_IFSTATE_BLOCKING: BRIDGE_UNLOCK(sc); m_freem(m); return; } } /* tap off packets passing the bridge */ BPF_MTAP(ifp, m); BRIDGE_UNLOCK(sc); if (inet_pfil_hook.ph_busy_count >= 0 #ifdef INET6 || inet6_pfil_hook.ph_busy_count >= 0 #endif ) { if (bridge_pfil(&m, sc->sc_ifp, dst_if, PFIL_OUT) != 0) return; if (m == NULL) return; } bridge_enqueue(sc, dst_if, m); } /* * 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 ifnet *bifp; struct ether_header *eh; struct mbuf *mc, *mc2; if ((sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return (m); bifp = sc->sc_ifp; BRIDGE_LOCK(sc); bif = bridge_lookup_member_if(sc, ifp); if (bif == NULL) { BRIDGE_UNLOCK(sc); return (m); } eh = mtod(m, struct ether_header *); if (memcmp(eh->ether_dhost, IFP2ENADDR(bifp), ETHER_ADDR_LEN) == 0) { /* * If the packet is for us, set the packets source as the * bridge, and return the packet back to ether_input for * local processing. */ /* XXX Do we tap the packet for the member interface too? * BPF_MTAP(&m->m_pkthdr.rcvif, m); */ /* Mark the packet as arriving on the bridge interface */ m->m_pkthdr.rcvif = bifp; BPF_MTAP(bifp, m); bifp->if_ipackets++; BRIDGE_UNLOCK(sc); return (m); } - if (m->m_flags & (M_BCAST|M_MCAST)) { + if (ETHER_IS_MULTICAST(eh->ether_dhost)) { /* 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) { BRIDGE_UNLOCK(sc); return (NULL); } } if (bif->bif_flags & IFBIF_STP) { switch (bif->bif_state) { case BSTP_IFSTATE_BLOCKING: case BSTP_IFSTATE_LISTENING: case BSTP_IFSTATE_DISABLED: BRIDGE_UNLOCK(sc); return (m); } } + if (bcmp(etherbroadcastaddr, eh->ether_dhost, + sizeof(etherbroadcastaddr)) == 0) + m->m_flags |= M_BCAST; + else + m->m_flags |= M_MCAST; + /* * 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) { BRIDGE_UNLOCK(sc); return (m); } /* Perform the bridge forwarding function with the copy. */ bridge_forward(sc, mc); /* * Reinject the mbuf as arriving on the bridge so we have a * chance at claiming multicast packets. We can not loop back * here from ether_input as a bridge is never a member of a * bridge. */ KASSERT(bifp->if_bridge == NULL, ("loop created in bridge_input")); mc2 = m_copypacket(m, M_DONTWAIT); if (mc2 != NULL) { mc2->m_pkthdr.rcvif = bifp; (*bifp->if_input)(bifp, mc2); } /* 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: BRIDGE_UNLOCK(sc); 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; BRIDGE_UNLOCK(sc); 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) { BRIDGE_UNLOCK(sc); 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. * * NOTE: Releases the lock on return. */ void bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if, struct mbuf *m, int runfilt) { struct bridge_iflist *bif; struct mbuf *mc; struct ifnet *dst_if; int error = 0, used = 0; BRIDGE_LOCK_ASSERT(sc); BRIDGE_LOCK2REF(sc, error); if (error) { m_freem(m); return; } /* Filter on the bridge interface before broadcasting */ if (runfilt && (inet_pfil_hook.ph_busy_count >= 0 #ifdef INET6 || inet6_pfil_hook.ph_busy_count >= 0 #endif )) { if (bridge_pfil(&m, sc->sc_ifp, NULL, PFIL_OUT) != 0) return; if (m == NULL) return; } 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_drv_flags & IFF_DRV_RUNNING) == 0) continue; if (LIST_NEXT(bif, bif_next) == NULL) { mc = m; used = 1; } else { mc = m_copypacket(m, M_DONTWAIT); if (mc == NULL) { sc->sc_ifp->if_oerrors++; continue; } } /* * Filter on the output interface. Pass a NULL bridge interface * pointer so we do not redundantly filter on the bridge for * each interface we broadcast on. */ if (runfilt && (inet_pfil_hook.ph_busy_count >= 0 #ifdef INET6 || inet6_pfil_hook.ph_busy_count >= 0 #endif )) { if (bridge_pfil(&m, NULL, dst_if, PFIL_OUT) != 0) return; if (m == NULL) return; } - + bridge_enqueue(sc, dst_if, mc); } if (used == 0) m_freem(m); BRIDGE_UNREF(sc); } /* * 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; BRIDGE_LOCK_ASSERT(sc); /* * 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 = uma_zalloc(bridge_rtnode_zone, M_NOWAIT | M_ZERO); if (brt == NULL) return (ENOMEM); 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) { uma_zfree(bridge_rtnode_zone, 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; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); /* 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; BRIDGE_LOCK_ASSERT(sc); bridge_rtage(sc); if (sc->sc_ifp->if_drv_flags & IFF_DRV_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; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); 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, int full) { struct bridge_rtnode *brt, *nbrt; BRIDGE_LOCK_ASSERT(sc); for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) { nbrt = LIST_NEXT(brt, brt_list); if (brt->brt_ifp == ifp && (full || (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)) 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; BRIDGE_LOCK_ASSERT(sc); 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; BRIDGE_LOCK_ASSERT(sc); 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) { BRIDGE_LOCK_ASSERT(sc); LIST_REMOVE(brt, brt_hash); LIST_REMOVE(brt, brt_list); sc->sc_brtcnt--; uma_zfree(bridge_rtnode_zone, brt); } /* * Send bridge packets through pfil if they are one of the types pfil can deal * with, or if they are ARP or REVARP. (pfil will pass ARP and REVARP without * question.) If *bifp or *ifp are NULL then packet filtering is skipped for * that interface. */ static int bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir) { int snap, error, i; struct ether_header *eh1, eh2; struct ip_fw_args args; struct ip *ip; struct llc llc1; u_int16_t ether_type; snap = 0; error = -1; /* Default error if not error == 0 */ i = min((*mp)->m_pkthdr.len, max_protohdr); if ((*mp)->m_len < i) { *mp = m_pullup(*mp, i); if (*mp == NULL) { printf("%s: m_pullup failed\n", __func__); return -1; } } eh1 = mtod(*mp, struct ether_header *); ether_type = ntohs(eh1->ether_type); /* * Check for SNAP/LLC. */ if (ether_type < ETHERMTU) { struct llc *llc2 = (struct llc *)(eh1 + 1); if ((*mp)->m_len >= ETHER_HDR_LEN + 8 && llc2->llc_dsap == LLC_SNAP_LSAP && llc2->llc_ssap == LLC_SNAP_LSAP && llc2->llc_control == LLC_UI) { ether_type = htons(llc2->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 pfil 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: /* * ipfw allows layer2 protocol filtering using * 'mac-type' so we will let the packet past, if * ipfw is disabled then drop it. */ if (!IPFW_LOADED || pfil_ipfw == 0) 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) &llc1); m_adj(*mp, sizeof(struct llc)); } /* * Check the IP header for alignment and errors */ if (dir == PFIL_IN) { switch (ether_type) { case ETHERTYPE_IP: error = bridge_ip_checkbasic(mp); break; # ifdef INET6 case ETHERTYPE_IPV6: error = bridge_ip6_checkbasic(mp); break; # endif /* INET6 */ default: error = 0; } if (error) goto bad; } if (IPFW_LOADED && pfil_ipfw != 0 && dir == PFIL_OUT && ifp != NULL) { error = -1; args.rule = ip_dn_claim_rule(*mp); if (args.rule != NULL && fw_one_pass) goto ipfwpass; /* packet already partially processed */ args.m = *mp; args.oif = ifp; args.next_hop = NULL; args.eh = &eh2; i = ip_fw_chk_ptr(&args); *mp = args.m; if (*mp == NULL) return error; if (DUMMYNET_LOADED && (i == IP_FW_DUMMYNET)) { /* put the Ethernet header back on */ M_PREPEND(*mp, ETHER_HDR_LEN, M_DONTWAIT); if (*mp == NULL) return error; bcopy(&eh2, mtod(*mp, caddr_t), ETHER_HDR_LEN); /* * Pass the pkt to dummynet, which consumes it. The * packet will return to us via bridge_dummynet(). */ args.oif = ifp; ip_dn_io_ptr(*mp, DN_TO_IFB_FWD, &args); return error; } if (i != IP_FW_PASS) /* drop */ goto bad; } ipfwpass: error = 0; /* * Run the packet through pfil */ switch (ether_type) { case ETHERTYPE_IP : /* * before calling the firewall, swap fields the same as * IP does. here we assume the header is contiguous */ ip = mtod(*mp, struct ip *); ip->ip_len = ntohs(ip->ip_len); ip->ip_off = ntohs(ip->ip_off); /* * Run pfil on the member interface and the bridge, both can * be skipped by clearing pfil_member or pfil_bridge. * * Keep the order: * in_if -> bridge_if -> out_if */ if (pfil_bridge && dir == PFIL_OUT && bifp != NULL) error = pfil_run_hooks(&inet_pfil_hook, mp, bifp, dir, NULL); if (*mp == NULL || error != 0) /* filter may consume */ break; if (pfil_member && ifp != NULL) error = pfil_run_hooks(&inet_pfil_hook, mp, ifp, dir, NULL); if (*mp == NULL || error != 0) /* filter may consume */ break; if (pfil_bridge && dir == PFIL_IN && bifp != NULL) error = pfil_run_hooks(&inet_pfil_hook, mp, bifp, dir, NULL); /* Restore ip and the fields ntohs()'d. */ if (*mp != NULL && error == 0) { ip = mtod(*mp, struct ip *); ip->ip_len = htons(ip->ip_len); ip->ip_off = htons(ip->ip_off); } break; # ifdef INET6 case ETHERTYPE_IPV6 : if (pfil_bridge && dir == PFIL_OUT && bifp != NULL) error = pfil_run_hooks(&inet6_pfil_hook, mp, bifp, dir, NULL); if (*mp == NULL || error != 0) /* filter may consume */ break; if (pfil_member && ifp != NULL) error = pfil_run_hooks(&inet6_pfil_hook, mp, ifp, dir, NULL); if (*mp == NULL || error != 0) /* filter may consume */ break; if (pfil_bridge && dir == PFIL_IN && bifp != NULL) error = pfil_run_hooks(&inet6_pfil_hook, mp, bifp, dir, NULL); 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(&llc1, 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 * pfil 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; u_short sum; 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; } if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) { sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID); } else { if (hlen == sizeof(struct ip)) { sum = in_cksum_hdr(ip); } else { sum = in_cksum(m, hlen); } } if (sum) { ipstat.ips_badsum++; goto bad; } /* 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 */ Index: stable/6/sys/net/if_bridgevar.h =================================================================== --- stable/6/sys/net/if_bridgevar.h (revision 151569) +++ stable/6/sys/net/if_bridgevar.h (revision 151570) @@ -1,357 +1,371 @@ /* $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 * * $FreeBSD$ */ /* * Data structure and control definitions for bridge interfaces. */ #include #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) */ /* * 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 */ /* 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) */ #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_ifp; /* make this an interface */ LIST_ENTRY(bridge_softc) sc_list; struct mtx sc_mtx; struct cv sc_cv; 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 */ uint32_t sc_iflist_ref; /* refcount for sc_iflist */ uint32_t sc_iflist_xcnt; /* refcount for sc_iflist */ 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 */ }; #define BRIDGE_LOCK_INIT(_sc) do { \ mtx_init(&(_sc)->sc_mtx, "if_bridge", NULL, MTX_DEF); \ cv_init(&(_sc)->sc_cv, "if_bridge_cv"); \ } while (0) #define BRIDGE_LOCK_DESTROY(_sc) do { \ mtx_destroy(&(_sc)->sc_mtx); \ cv_destroy(&(_sc)->sc_cv); \ } while (0) #define BRIDGE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define BRIDGE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define BRIDGE_LOCKED(_sc) mtx_owned(&(_sc)->sc_mtx) #define BRIDGE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define BRIDGE_LOCK2REF(_sc, _err) do { \ mtx_assert(&(_sc)->sc_mtx, MA_OWNED); \ if ((_sc)->sc_iflist_xcnt > 0) \ (_err) = EBUSY; \ else \ (_sc)->sc_iflist_ref++; \ mtx_unlock(&(_sc)->sc_mtx); \ } while (0) #define BRIDGE_UNREF(_sc) do { \ mtx_lock(&(_sc)->sc_mtx); \ (_sc)->sc_iflist_ref--; \ if (((_sc)->sc_iflist_xcnt > 0) && ((_sc)->sc_iflist_ref == 0)) \ cv_broadcast(&(_sc)->sc_cv); \ mtx_unlock(&(_sc)->sc_mtx); \ } while (0) #define BRIDGE_XLOCK(_sc) do { \ mtx_assert(&(_sc)->sc_mtx, MA_OWNED); \ (_sc)->sc_iflist_xcnt++; \ while ((_sc)->sc_iflist_ref > 0) \ cv_wait(&(_sc)->sc_cv, &(_sc)->sc_mtx); \ } while (0) #define BRIDGE_XDROP(_sc) do { \ mtx_assert(&(_sc)->sc_mtx, MA_OWNED); \ (_sc)->sc_iflist_xcnt--; \ } while (0) +#define BRIDGE_INPUT(_ifp, _m) do { \ + KASSERT(bridge_input_p != NULL, \ + ("%s: if_bridge not loaded!", __func__)); \ + _m = (*bridge_input_p)(_ifp, _m); \ + if (_m != NULL) \ + _ifp = _m->m_pkthdr.rcvif; \ +} while (0) + +#define BRIDGE_OUTPUT(_ifp, _m, _err) do { \ + KASSERT(bridge_output_p != NULL, \ + ("%s: if_bridge not loaded!", __func__)); \ + _err = (*bridge_output_p)(_ifp, _m, NULL, NULL); \ +} while (0) + extern const uint8_t bstp_etheraddr[]; -void bridge_ifdetach(struct ifnet *); +void bridge_enqueue(struct bridge_softc *, struct ifnet *, struct mbuf *); void bridge_rtdelete(struct bridge_softc *, struct ifnet *ifp, int); -int bridge_output(struct ifnet *, struct mbuf *, struct sockaddr *, - struct rtentry *); -void bridge_dummynet(struct mbuf *, struct ifnet *); -struct mbuf *bridge_input(struct ifnet *, struct mbuf *); +extern struct mbuf *(*bridge_input_p)(struct ifnet *, struct mbuf *); +extern int (*bridge_output_p)(struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *); +extern void (*bridge_dn_p)(struct mbuf *, struct ifnet *); +extern void (*bridge_detach_p)(struct ifnet *); +extern void (*bstp_linkstate_p)(struct ifnet *ifp, int state); -extern void (*bstp_linkstate_p)(struct ifnet *ifp, int state); - void bstp_initialization(struct bridge_softc *); void bstp_linkstate(struct ifnet *, int); void bstp_stop(struct bridge_softc *); struct mbuf *bstp_input(struct ifnet *, struct mbuf *); -void bridge_enqueue(struct bridge_softc *, struct ifnet *, struct mbuf *); #endif /* _KERNEL */ Index: stable/6/sys/net/if_ethersubr.c =================================================================== --- stable/6/sys/net/if_ethersubr.c (revision 151569) +++ stable/6/sys/net/if_ethersubr.c (revision 151570) @@ -1,1243 +1,1221 @@ /*- * Copyright (c) 1982, 1989, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. * * @(#)if_ethersubr.c 8.1 (Berkeley) 6/10/93 * $FreeBSD$ */ #include "opt_atalk.h" #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipx.h" #include "opt_bdg.h" #include "opt_mac.h" #include "opt_netgraph.h" #include "opt_carp.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #if defined(INET) || defined(INET6) #include #include #include #include #include #endif #ifdef INET6 #include #endif #ifdef DEV_CARP #include #endif #ifdef IPX #include #include #endif int (*ef_inputp)(struct ifnet*, struct ether_header *eh, struct mbuf *m); int (*ef_outputp)(struct ifnet *ifp, struct mbuf **mp, struct sockaddr *dst, short *tp, int *hlen); #ifdef NETATALK #include #include #include #define llc_snap_org_code llc_un.type_snap.org_code #define llc_snap_ether_type llc_un.type_snap.ether_type extern u_char at_org_code[3]; extern u_char aarp_org_code[3]; #endif /* NETATALK */ /* netgraph node hooks for ng_ether(4) */ void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m); int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); void (*ng_ether_attach_p)(struct ifnet *ifp); void (*ng_ether_detach_p)(struct ifnet *ifp); void (*vlan_input_p)(struct ifnet *, struct mbuf *); /* bridge support */ int do_bridge; bridge_in_t *bridge_in_ptr; bdg_forward_t *bdg_forward_ptr; 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 *); void (*bridge_dn_p)(struct mbuf *, struct ifnet *); void (*bridge_detach_p)(struct ifnet *ifp); static const u_char etherbroadcastaddr[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static int ether_resolvemulti(struct ifnet *, struct sockaddr **, struct sockaddr *); /* XXX: should be in an arp support file, not here */ MALLOC_DEFINE(M_ARPCOM, "arpcom", "802.* interface internals"); #define senderr(e) do { error = (e); goto bad;} while (0) #if defined(INET) || defined(INET6) int ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, struct ip_fw **rule, int shared); static int ether_ipfw; #endif /* * Ethernet output routine. * Encapsulate a packet of type family for the local net. * Use trailer local net encapsulation if enough data in first * packet leaves a multiple of 512 bytes of data in remainder. * Assumes that ifp is actually pointer to arpcom structure. */ int ether_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt0) { short type; int error, hdrcmplt = 0; u_char esrc[ETHER_ADDR_LEN], edst[ETHER_ADDR_LEN]; struct ether_header *eh; int loop_copy = 0; int hlen; /* link layer header length */ #ifdef MAC error = mac_check_ifnet_transmit(ifp, m); if (error) senderr(error); #endif if (ifp->if_flags & IFF_MONITOR) senderr(ENETDOWN); if (!((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))) senderr(ENETDOWN); hlen = ETHER_HDR_LEN; switch (dst->sa_family) { #ifdef INET case AF_INET: error = arpresolve(ifp, rt0, m, dst, edst); if (error) return (error == EWOULDBLOCK ? 0 : error); type = htons(ETHERTYPE_IP); break; case AF_ARP: { struct arphdr *ah; ah = mtod(m, struct arphdr *); ah->ar_hrd = htons(ARPHRD_ETHER); loop_copy = -1; /* if this is for us, don't do it */ switch(ntohs(ah->ar_op)) { case ARPOP_REVREQUEST: case ARPOP_REVREPLY: type = htons(ETHERTYPE_REVARP); break; case ARPOP_REQUEST: case ARPOP_REPLY: default: type = htons(ETHERTYPE_ARP); break; } if (m->m_flags & M_BCAST) bcopy(ifp->if_broadcastaddr, edst, ETHER_ADDR_LEN); else bcopy(ar_tha(ah), edst, ETHER_ADDR_LEN); } break; #endif #ifdef INET6 case AF_INET6: error = nd6_storelladdr(ifp, rt0, m, dst, (u_char *)edst); if (error) return error; type = htons(ETHERTYPE_IPV6); break; #endif #ifdef IPX case AF_IPX: if (ef_outputp) { error = ef_outputp(ifp, &m, dst, &type, &hlen); if (error) goto bad; } else type = htons(ETHERTYPE_IPX); bcopy((caddr_t)&(((struct sockaddr_ipx *)dst)->sipx_addr.x_host), (caddr_t)edst, sizeof (edst)); break; #endif #ifdef NETATALK case AF_APPLETALK: { struct at_ifaddr *aa; if ((aa = at_ifawithnet((struct sockaddr_at *)dst)) == NULL) senderr(EHOSTUNREACH); /* XXX */ if (!aarpresolve(ifp, m, (struct sockaddr_at *)dst, edst)) return (0); /* * In the phase 2 case, need to prepend an mbuf for the llc header. */ if ( aa->aa_flags & AFA_PHASE2 ) { struct llc llc; M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT); if (m == NULL) senderr(ENOBUFS); llc.llc_dsap = llc.llc_ssap = LLC_SNAP_LSAP; llc.llc_control = LLC_UI; bcopy(at_org_code, llc.llc_snap_org_code, sizeof(at_org_code)); llc.llc_snap_ether_type = htons( ETHERTYPE_AT ); bcopy(&llc, mtod(m, caddr_t), LLC_SNAPFRAMELEN); type = htons(m->m_pkthdr.len); hlen = LLC_SNAPFRAMELEN + ETHER_HDR_LEN; } else { type = htons(ETHERTYPE_AT); } break; } #endif /* NETATALK */ case pseudo_AF_HDRCMPLT: hdrcmplt = 1; eh = (struct ether_header *)dst->sa_data; (void)memcpy(esrc, eh->ether_shost, sizeof (esrc)); /* FALLTHROUGH */ case AF_UNSPEC: loop_copy = -1; /* if this is for us, don't do it */ eh = (struct ether_header *)dst->sa_data; (void)memcpy(edst, eh->ether_dhost, sizeof (edst)); type = eh->ether_type; break; default: if_printf(ifp, "can't handle af%d\n", dst->sa_family); senderr(EAFNOSUPPORT); } /* * Add local net header. If no space in first mbuf, * allocate another. */ M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); if (m == NULL) senderr(ENOBUFS); eh = mtod(m, struct ether_header *); (void)memcpy(&eh->ether_type, &type, sizeof(eh->ether_type)); (void)memcpy(eh->ether_dhost, edst, sizeof (edst)); if (hdrcmplt) (void)memcpy(eh->ether_shost, esrc, sizeof(eh->ether_shost)); else (void)memcpy(eh->ether_shost, IFP2ENADDR(ifp), 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)); + BRIDGE_OUTPUT(ifp, m, error); + return (error); } /* * If a simplex interface, and the packet is being sent to our * Ethernet address or a broadcast address, loopback a copy. * XXX To make a simplex device behave exactly like a duplex * device, we should copy in the case of sending to our own * ethernet address (thus letting the original actually appear * on the wire). However, we don't do that here for security * reasons and compatibility with the original behavior. */ if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1) && m_tag_find(m, PACKET_TAG_PF_ROUTED, NULL) == NULL) { int csum_flags = 0; if (m->m_pkthdr.csum_flags & CSUM_IP) csum_flags |= (CSUM_IP_CHECKED|CSUM_IP_VALID); if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) csum_flags |= (CSUM_DATA_VALID|CSUM_PSEUDO_HDR); if ((m->m_flags & M_BCAST) || (loop_copy > 0)) { struct mbuf *n; if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { n->m_pkthdr.csum_flags |= csum_flags; if (csum_flags & CSUM_DATA_VALID) n->m_pkthdr.csum_data = 0xffff; (void)if_simloop(ifp, n, dst->sa_family, hlen); } else ifp->if_iqdrops++; } else if (bcmp(eh->ether_dhost, eh->ether_shost, ETHER_ADDR_LEN) == 0) { m->m_pkthdr.csum_flags |= csum_flags; if (csum_flags & CSUM_DATA_VALID) m->m_pkthdr.csum_data = 0xffff; (void) if_simloop(ifp, m, dst->sa_family, hlen); return (0); /* XXX */ } } #ifdef DEV_CARP if (ifp->if_carp && (error = carp_output(ifp, m, dst, NULL))) goto bad; #endif /* Handle ng_ether(4) processing, if any */ if (IFP2AC(ifp)->ac_netgraph != NULL) { if ((error = (*ng_ether_output_p)(ifp, &m)) != 0) { bad: if (m != NULL) m_freem(m); return (error); } if (m == NULL) return (0); } /* Continue with link-layer output */ return ether_output_frame(ifp, m); } /* * Ethernet link layer output routine to send a raw frame to the device. * * This assumes that the 14 byte Ethernet header is present and contiguous * in the first mbuf (if BRIDGE'ing). */ int ether_output_frame(struct ifnet *ifp, struct mbuf *m) { #if defined(INET) || defined(INET6) struct ip_fw *rule = ip_dn_claim_rule(m); #else void *rule = NULL; #endif int error; if (rule == NULL && BDG_ACTIVE(ifp)) { /* * Beware, the bridge code notices the null rcvif and * uses that identify that it's being called from * ether_output as opposd to ether_input. Yech. */ m->m_pkthdr.rcvif = NULL; m = bdg_forward_ptr(m, ifp); if (m != NULL) m_freem(m); return (0); } #if defined(INET) || defined(INET6) if (IPFW_LOADED && ether_ipfw != 0) { if (ether_ipfw_chk(&m, ifp, &rule, 0) == 0) { if (m) { m_freem(m); return EACCES; /* pkt dropped */ } else return 0; /* consumed e.g. in a pipe */ } } #endif /* * Queue message on interface, update output statistics if * successful, and start output if interface not yet active. */ IFQ_HANDOFF(ifp, m, error); return (error); } #if defined(INET) || defined(INET6) /* * ipfw processing for ethernet packets (in and out). * The second parameter is NULL from ether_demux, and ifp from * ether_output_frame. This section of code could be used from * bridge.c as well as long as we use some extra info * to distinguish that case from ether_output_frame(); */ int ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, struct ip_fw **rule, int shared) { struct ether_header *eh; struct ether_header save_eh; struct mbuf *m; int i; struct ip_fw_args args; if (*rule != NULL && fw_one_pass) return 1; /* dummynet packet, already partially processed */ /* * I need some amt of data to be contiguous, and in case others need * the packet (shared==1) also better be in the first mbuf. */ m = *m0; i = min( m->m_pkthdr.len, max_protohdr); if ( shared || m->m_len < i) { m = m_pullup(m, i); if (m == NULL) { *m0 = m; return 0; } } eh = mtod(m, struct ether_header *); save_eh = *eh; /* save copy for restore below */ m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */ args.m = m; /* the packet we are looking at */ args.oif = dst; /* destination, if any */ args.rule = *rule; /* matching rule to restart */ args.next_hop = NULL; /* we do not support forward yet */ args.eh = &save_eh; /* MAC header for bridged/MAC packets */ i = ip_fw_chk_ptr(&args); m = args.m; if (m != NULL) { /* * Restore Ethernet header, as needed, in case the * mbuf chain was replaced by ipfw. */ M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); if (m == NULL) { *m0 = m; return 0; } if (eh != mtod(m, struct ether_header *)) bcopy(&save_eh, mtod(m, struct ether_header *), ETHER_HDR_LEN); } *m0 = m; *rule = args.rule; if (i == IP_FW_DENY) /* drop */ return 0; KASSERT(m != NULL, ("ether_ipfw_chk: m is NULL")); if (i == IP_FW_PASS) /* a PASS rule. */ return 1; if (DUMMYNET_LOADED && (i == IP_FW_DUMMYNET)) { /* * Pass the pkt to dummynet, which consumes it. * If shared, make a copy and keep the original. */ if (shared) { m = m_copypacket(m, M_DONTWAIT); if (m == NULL) return 0; } else { /* * Pass the original to dummynet and * nothing back to the caller */ *m0 = NULL ; } ip_dn_io_ptr(m, dst ? DN_TO_ETH_OUT: DN_TO_ETH_DEMUX, &args); return 0; } /* * XXX at some point add support for divert/forward actions. * If none of the above matches, we have to drop the pkt. */ return 0; } #endif /* * Process a received Ethernet packet; the packet is in the * mbuf chain m with the ethernet header at the front. */ static void ether_input(struct ifnet *ifp, struct mbuf *m) { struct ether_header *eh; u_short etype; /* * Do consistency checks to verify assumptions * made by code past this point. */ if ((m->m_flags & M_PKTHDR) == 0) { if_printf(ifp, "discard frame w/o packet header\n"); ifp->if_ierrors++; m_freem(m); return; } if (m->m_len < ETHER_HDR_LEN) { /* XXX maybe should pullup? */ if_printf(ifp, "discard frame w/o leading ethernet " "header (len %u pkt len %u)\n", m->m_len, m->m_pkthdr.len); ifp->if_ierrors++; m_freem(m); return; } eh = mtod(m, struct ether_header *); etype = ntohs(eh->ether_type); if (m->m_pkthdr.len > ETHER_MAX_FRAME(ifp, etype, m->m_flags & M_HASFCS)) { if_printf(ifp, "discard oversize frame " "(ether type %x flags %x len %u > max %lu)\n", etype, m->m_flags, m->m_pkthdr.len, ETHER_MAX_FRAME(ifp, etype, m->m_flags & M_HASFCS)); ifp->if_ierrors++; m_freem(m); return; } if (m->m_pkthdr.rcvif == NULL) { if_printf(ifp, "discard frame w/o interface pointer\n"); ifp->if_ierrors++; m_freem(m); return; } #ifdef DIAGNOSTIC if (m->m_pkthdr.rcvif != ifp) { if_printf(ifp, "Warning, frame marked as received on %s\n", m->m_pkthdr.rcvif->if_xname); } #endif #ifdef MAC /* * Tag the mbuf with an appropriate MAC label before any other * consumers can get to it. */ mac_create_mbuf_from_ifnet(ifp, m); #endif /* * Give bpf a chance at the packet. */ BPF_MTAP(ifp, m); if (ifp->if_flags & IFF_MONITOR) { /* * Interface marked for monitoring; discard packet. */ m_freem(m); return; } /* If the CRC is still on the packet, trim it off. */ if (m->m_flags & M_HASFCS) { m_adj(m, -ETHER_CRC_LEN); m->m_flags &= ~M_HASFCS; } ifp->if_ibytes += m->m_pkthdr.len; /* Handle ng_ether(4) processing, if any */ if (IFP2AC(ifp)->ac_netgraph != NULL) { (*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!")); - - /* Mark the packet as broadcast or multicast. This is also set - * further down the code in ether_demux() but since the bridge - * input routine rarely returns a mbuf for further processing, - * it is an acceptable duplication. - */ - if (ETHER_IS_MULTICAST(eh->ether_dhost)) { - if (bcmp(etherbroadcastaddr, eh->ether_dhost, - sizeof(etherbroadcastaddr)) == 0) - m->m_flags |= M_BCAST; - else - m->m_flags |= M_MCAST; - } - - m = (*bridge_input_p)(ifp, m); + BRIDGE_INPUT(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 */ if (BDG_ACTIVE(ifp) ) if ((m = bridge_in_ptr(ifp, m)) == NULL) return; /* First chunk of an mbuf contains good entropy */ if (harvest.ethernet) random_harvest(m, 16, 3, 0, RANDOM_NET); ether_demux(ifp, m); } /* * Upper layer processing for a received Ethernet packet. */ void ether_demux(struct ifnet *ifp, struct mbuf *m) { struct ether_header *eh; int isr; u_short ether_type; #if defined(NETATALK) struct llc *l; #endif #if defined(INET) || defined(INET6) struct ip_fw *rule = ip_dn_claim_rule(m); #endif KASSERT(ifp != NULL, ("ether_demux: NULL interface pointer")); eh = mtod(m, struct ether_header *); ether_type = ntohs(eh->ether_type); #if defined(INET) || defined(INET6) if (rule) /* packet was already bridged */ goto post_stats; #endif if (!(BDG_ACTIVE(ifp)) && !(ifp->if_bridge) && !((ether_type == ETHERTYPE_VLAN || m->m_flags & M_VLANTAG) && ifp->if_nvlans > 0)) { #ifdef DEV_CARP /* * XXX: Okay, we need to call carp_forus() and - if it is for * us jump over code that does the normal check * "IFP2ENADDR(ifp) == ether_dhost". The check sequence is a bit * different from OpenBSD, so we jump over as few code as * possible, to catch _all_ sanity checks. This needs * evaluation, to see if the carp ether_dhost values break any * of these checks! */ if (ifp->if_carp && carp_forus(ifp->if_carp, eh->ether_dhost)) goto pre_stats; #endif /* * Discard packet if upper layers shouldn't see it because it * was unicast to a different Ethernet address. If the driver * is working properly, then this situation can only happen * when the interface is in promiscuous mode. * * If VLANs are active, and this packet has a VLAN tag, do * not drop it here but pass it on to the VLAN layer, to * give them a chance to consider it as well (e. g. in case * bridging is only active on a VLAN). They will drop it if * it's undesired. */ if ((ifp->if_flags & IFF_PROMISC) != 0 && !ETHER_IS_MULTICAST(eh->ether_dhost) && bcmp(eh->ether_dhost, IFP2ENADDR(ifp), ETHER_ADDR_LEN) != 0 && (ifp->if_flags & IFF_PPROMISC) == 0) { m_freem(m); return; } } #ifdef DEV_CARP pre_stats: #endif /* Discard packet if interface is not up */ if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m); return; } if (ETHER_IS_MULTICAST(eh->ether_dhost)) { if (bcmp(etherbroadcastaddr, eh->ether_dhost, sizeof(etherbroadcastaddr)) == 0) m->m_flags |= M_BCAST; else m->m_flags |= M_MCAST; } if (m->m_flags & (M_BCAST|M_MCAST)) ifp->if_imcasts++; #if defined(INET) || defined(INET6) post_stats: if (IPFW_LOADED && ether_ipfw != 0) { if (ether_ipfw_chk(&m, NULL, &rule, 0) == 0) { if (m) m_freem(m); return; } } #endif /* * Check to see if the device performed the VLAN decapsulation and * provided us with the tag. */ if (m->m_flags & M_VLANTAG) { /* * If no VLANs are configured, drop. */ if (ifp->if_nvlans == 0) { ifp->if_noproto++; m_freem(m); return; } /* * vlan_input() will either recursively call ether_input() * or drop the packet. */ KASSERT(vlan_input_p != NULL,("ether_input: VLAN not loaded!")); (*vlan_input_p)(ifp, m); return; } /* * Handle protocols that expect to have the Ethernet header * (and possibly FCS) intact. */ switch (ether_type) { case ETHERTYPE_VLAN: if (ifp->if_nvlans != 0) { KASSERT(vlan_input_p,("ether_input: VLAN not loaded!")); (*vlan_input_p)(ifp, m); } else { ifp->if_noproto++; m_freem(m); } return; } /* Strip off Ethernet header. */ m_adj(m, ETHER_HDR_LEN); /* If the CRC is still on the packet, trim it off. */ if (m->m_flags & M_HASFCS) { m_adj(m, -ETHER_CRC_LEN); m->m_flags &= ~M_HASFCS; } switch (ether_type) { #ifdef INET case ETHERTYPE_IP: if (ip_fastforward(m)) return; isr = NETISR_IP; break; case ETHERTYPE_ARP: if (ifp->if_flags & IFF_NOARP) { /* Discard packet if ARP is disabled on interface */ m_freem(m); return; } isr = NETISR_ARP; break; #endif #ifdef IPX case ETHERTYPE_IPX: if (ef_inputp && ef_inputp(ifp, eh, m) == 0) return; isr = NETISR_IPX; break; #endif #ifdef INET6 case ETHERTYPE_IPV6: isr = NETISR_IPV6; break; #endif #ifdef NETATALK case ETHERTYPE_AT: isr = NETISR_ATALK1; break; case ETHERTYPE_AARP: isr = NETISR_AARP; break; #endif /* NETATALK */ default: #ifdef IPX if (ef_inputp && ef_inputp(ifp, eh, m) == 0) return; #endif /* IPX */ #if defined(NETATALK) if (ether_type > ETHERMTU) goto discard; l = mtod(m, struct llc *); if (l->llc_dsap == LLC_SNAP_LSAP && l->llc_ssap == LLC_SNAP_LSAP && l->llc_control == LLC_UI) { if (bcmp(&(l->llc_snap_org_code)[0], at_org_code, sizeof(at_org_code)) == 0 && ntohs(l->llc_snap_ether_type) == ETHERTYPE_AT) { m_adj(m, LLC_SNAPFRAMELEN); isr = NETISR_ATALK2; break; } if (bcmp(&(l->llc_snap_org_code)[0], aarp_org_code, sizeof(aarp_org_code)) == 0 && ntohs(l->llc_snap_ether_type) == ETHERTYPE_AARP) { m_adj(m, LLC_SNAPFRAMELEN); isr = NETISR_AARP; break; } } #endif /* NETATALK */ goto discard; } netisr_dispatch(isr, m); return; discard: /* * Packet is to be discarded. If netgraph is present, * hand the packet to it for last chance processing; * otherwise dispose of it. */ if (IFP2AC(ifp)->ac_netgraph != NULL) { /* * Put back the ethernet header so netgraph has a * consistent view of inbound packets. */ M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); (*ng_ether_input_orphan_p)(ifp, m); return; } m_freem(m); } /* * Convert Ethernet address to printable (loggable) representation. * This routine is for compatibility; it's better to just use * * printf("%6D", , ":"); * * since there's no static buffer involved. */ char * ether_sprintf(const u_char *ap) { static char etherbuf[18]; snprintf(etherbuf, sizeof (etherbuf), "%6D", ap, ":"); return (etherbuf); } /* * Perform common duties while attaching to interface list */ void ether_ifattach(struct ifnet *ifp, const u_int8_t *llc) { int i; struct ifaddr *ifa; struct sockaddr_dl *sdl; ifp->if_addrlen = ETHER_ADDR_LEN; ifp->if_hdrlen = ETHER_HDR_LEN; if_attach(ifp); ifp->if_mtu = ETHERMTU; ifp->if_output = ether_output; ifp->if_input = ether_input; ifp->if_resolvemulti = ether_resolvemulti; if (ifp->if_baudrate == 0) ifp->if_baudrate = IF_Mbps(10); /* just a default */ ifp->if_broadcastaddr = etherbroadcastaddr; ifa = ifaddr_byindex(ifp->if_index); KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__)); sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ifp->if_addrlen; bcopy(llc, LLADDR(sdl), ifp->if_addrlen); /* * XXX: This doesn't belong here; we do it until * XXX: all drivers are cleaned up */ if (llc != IFP2ENADDR(ifp)) bcopy(llc, IFP2ENADDR(ifp), ifp->if_addrlen); bpfattach(ifp, DLT_EN10MB, ETHER_HDR_LEN); if (ng_ether_attach_p != NULL) (*ng_ether_attach_p)(ifp); if (BDG_LOADED) bdgtakeifaces_ptr(); /* Announce Ethernet MAC address if non-zero. */ for (i = 0; i < ifp->if_addrlen; i++) if (llc[i] != 0) break; if (i != ifp->if_addrlen) if_printf(ifp, "Ethernet address: %6D\n", llc, ":"); if (debug_mpsafenet && (ifp->if_flags & IFF_NEEDSGIANT) != 0) if_printf(ifp, "if_start running deferred for Giant\n"); } /* * Perform common duties while detaching an Ethernet interface */ void ether_ifdetach(struct ifnet *ifp) { if (IFP2AC(ifp)->ac_netgraph != NULL) (*ng_ether_detach_p)(ifp); if (ifp->if_bridge) { KASSERT(bridge_detach_p != NULL, ("bridge_detach_p is NULL")); (*bridge_detach_p)(ifp); } bpfdetach(ifp); if_detach(ifp); if (BDG_LOADED) bdgtakeifaces_ptr(); } SYSCTL_DECL(_net_link); SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW, 0, "Ethernet"); #if defined(INET) || defined(INET6) SYSCTL_INT(_net_link_ether, OID_AUTO, ipfw, CTLFLAG_RW, ðer_ipfw,0,"Pass ether pkts through firewall"); #endif #if 0 /* * This is for reference. We have a table-driven version * of the little-endian crc32 generator, which is faster * than the double-loop. */ uint32_t ether_crc32_le(const uint8_t *buf, size_t len) { size_t i; uint32_t crc; int bit; uint8_t data; crc = 0xffffffff; /* initial value */ for (i = 0; i < len; i++) { for (data = *buf++, bit = 0; bit < 8; bit++, data >>= 1) carry = (crc ^ data) & 1; crc >>= 1; if (carry) crc = (crc ^ ETHER_CRC_POLY_LE); } return (crc); } #else uint32_t ether_crc32_le(const uint8_t *buf, size_t len) { static const uint32_t crctab[] = { 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; size_t i; uint32_t crc; crc = 0xffffffff; /* initial value */ for (i = 0; i < len; i++) { crc ^= buf[i]; crc = (crc >> 4) ^ crctab[crc & 0xf]; crc = (crc >> 4) ^ crctab[crc & 0xf]; } return (crc); } #endif uint32_t ether_crc32_be(const uint8_t *buf, size_t len) { size_t i; uint32_t crc, carry; int bit; uint8_t data; crc = 0xffffffff; /* initial value */ for (i = 0; i < len; i++) { for (data = *buf++, bit = 0; bit < 8; bit++, data >>= 1) { carry = ((crc & 0x80000000) ? 1 : 0) ^ (data & 0x01); crc <<= 1; if (carry) crc = (crc ^ ETHER_CRC_POLY_BE) | carry; } } return (crc); } int ether_ioctl(struct ifnet *ifp, int command, caddr_t data) { struct ifaddr *ifa = (struct ifaddr *) data; struct ifreq *ifr = (struct ifreq *) data; int error = 0; switch (command) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: ifp->if_init(ifp->if_softc); /* before arpwhohas */ arp_ifinit(ifp, ifa); break; #endif #ifdef IPX /* * XXX - This code is probably wrong */ case AF_IPX: { struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); if (ipx_nullhost(*ina)) ina->x_host = *(union ipx_host *) IFP2ENADDR(ifp); else { bcopy((caddr_t) ina->x_host.c_host, (caddr_t) IFP2ENADDR(ifp), ETHER_ADDR_LEN); } /* * Set new address */ ifp->if_init(ifp->if_softc); break; } #endif default: ifp->if_init(ifp->if_softc); break; } break; case SIOCGIFADDR: { struct sockaddr *sa; sa = (struct sockaddr *) & ifr->ifr_data; bcopy(IFP2ENADDR(ifp), (caddr_t) sa->sa_data, ETHER_ADDR_LEN); } break; case SIOCSIFMTU: /* * Set the interface MTU. */ if (ifr->ifr_mtu > ETHERMTU) { error = EINVAL; } else { ifp->if_mtu = ifr->ifr_mtu; } break; default: error = EINVAL; /* XXX netbsd has ENOTTY??? */ break; } return (error); } static int ether_resolvemulti(struct ifnet *ifp, struct sockaddr **llsa, struct sockaddr *sa) { struct sockaddr_dl *sdl; #ifdef INET struct sockaddr_in *sin; #endif #ifdef INET6 struct sockaddr_in6 *sin6; #endif u_char *e_addr; switch(sa->sa_family) { case AF_LINK: /* * No mapping needed. Just check that it's a valid MC address. */ sdl = (struct sockaddr_dl *)sa; e_addr = LLADDR(sdl); if (!ETHER_IS_MULTICAST(e_addr)) return EADDRNOTAVAIL; *llsa = 0; return 0; #ifdef INET case AF_INET: sin = (struct sockaddr_in *)sa; if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) return EADDRNOTAVAIL; MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR, M_NOWAIT|M_ZERO); if (sdl == NULL) return ENOMEM; sdl->sdl_len = sizeof *sdl; sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ETHER_ADDR_LEN; e_addr = LLADDR(sdl); ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr); *llsa = (struct sockaddr *)sdl; return 0; #endif #ifdef INET6 case AF_INET6: sin6 = (struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { /* * An IP6 address of 0 means listen to all * of the Ethernet multicast address used for IP6. * (This is used for multicast routers.) */ ifp->if_flags |= IFF_ALLMULTI; *llsa = 0; return 0; } if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) return EADDRNOTAVAIL; MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR, M_NOWAIT|M_ZERO); if (sdl == NULL) return (ENOMEM); sdl->sdl_len = sizeof *sdl; sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ETHER_ADDR_LEN; e_addr = LLADDR(sdl); ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr); *llsa = (struct sockaddr *)sdl; return 0; #endif default: /* * Well, the text isn't quite right, but it's the name * that counts... */ return EAFNOSUPPORT; } } static void* ether_alloc(u_char type, struct ifnet *ifp) { struct arpcom *ac; ac = malloc(sizeof(struct arpcom), M_ARPCOM, M_WAITOK | M_ZERO); ac->ac_ifp = ifp; return (ac); } static void ether_free(void *com, u_char type) { free(com, M_ARPCOM); } static int ether_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: if_register_com_alloc(IFT_ETHER, ether_alloc, ether_free); break; case MOD_UNLOAD: if_deregister_com_alloc(IFT_ETHER); break; default: return EOPNOTSUPP; } return (0); } static moduledata_t ether_mod = { "ether", ether_modevent, 0 }; DECLARE_MODULE(ether, ether_mod, SI_SUB_INIT_IF, SI_ORDER_ANY); MODULE_VERSION(ether, 1); Index: stable/6/sys/netgraph/ng_ether.c =================================================================== --- stable/6/sys/netgraph/ng_ether.c (revision 151569) +++ stable/6/sys/netgraph/ng_ether.c (revision 151570) @@ -1,777 +1,786 @@ /* * ng_ether.c */ /*- * Copyright (c) 1996-2000 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * Authors: Archie Cobbs * Julian Elischer * * $FreeBSD$ */ /* * ng_ether(4) netgraph node type */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #define IFP2NG(ifp) ((struct ng_node *)IFP2AC((ifp))->ac_netgraph) #define IFP2NG_SET(ifp, val) (IFP2AC((ifp))->ac_netgraph = (val)) /* Per-node private data */ struct private { struct ifnet *ifp; /* associated interface */ hook_p upper; /* upper hook connection */ hook_p lower; /* lower hook connection */ hook_p orphan; /* orphan hook connection */ u_char autoSrcAddr; /* always overwrite source address */ u_char promisc; /* promiscuous mode enabled */ u_long hwassist; /* hardware checksum capabilities */ u_int flags; /* flags e.g. really die */ }; typedef struct private *priv_p; /* Hook pointers used by if_ethersubr.c to callback to netgraph */ extern void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); extern void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m); extern int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); extern void (*ng_ether_attach_p)(struct ifnet *ifp); extern void (*ng_ether_detach_p)(struct ifnet *ifp); extern void (*ng_ether_link_state_p)(struct ifnet *ifp, int state); /* Functional hooks called from if_ethersubr.c */ static void ng_ether_input(struct ifnet *ifp, struct mbuf **mp); static void ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m); static int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); static void ng_ether_attach(struct ifnet *ifp); static void ng_ether_detach(struct ifnet *ifp); static void ng_ether_link_state(struct ifnet *ifp, int state); /* Other functions */ static int ng_ether_rcv_lower(node_p node, struct mbuf *m); static int ng_ether_rcv_upper(node_p node, struct mbuf *m); /* Netgraph node methods */ static ng_constructor_t ng_ether_constructor; static ng_rcvmsg_t ng_ether_rcvmsg; static ng_shutdown_t ng_ether_shutdown; static ng_newhook_t ng_ether_newhook; static ng_connect_t ng_ether_connect; static ng_rcvdata_t ng_ether_rcvdata; static ng_disconnect_t ng_ether_disconnect; static int ng_ether_mod_event(module_t mod, int event, void *data); /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_ether_cmdlist[] = { { NGM_ETHER_COOKIE, NGM_ETHER_GET_IFNAME, "getifname", NULL, &ng_parse_string_type }, { NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, "getifindex", NULL, &ng_parse_int32_type }, { NGM_ETHER_COOKIE, NGM_ETHER_GET_ENADDR, "getenaddr", NULL, &ng_parse_enaddr_type }, { NGM_ETHER_COOKIE, NGM_ETHER_SET_ENADDR, "setenaddr", &ng_parse_enaddr_type, NULL }, { NGM_ETHER_COOKIE, NGM_ETHER_GET_PROMISC, "getpromisc", NULL, &ng_parse_int32_type }, { NGM_ETHER_COOKIE, NGM_ETHER_SET_PROMISC, "setpromisc", &ng_parse_int32_type, NULL }, { NGM_ETHER_COOKIE, NGM_ETHER_GET_AUTOSRC, "getautosrc", NULL, &ng_parse_int32_type }, { NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC, "setautosrc", &ng_parse_int32_type, NULL }, { NGM_ETHER_COOKIE, NGM_ETHER_ADD_MULTI, "addmulti", &ng_parse_enaddr_type, NULL }, { NGM_ETHER_COOKIE, NGM_ETHER_DEL_MULTI, "delmulti", &ng_parse_enaddr_type, NULL }, { NGM_ETHER_COOKIE, NGM_ETHER_DETACH, "detach", NULL, NULL }, { 0 } }; static struct ng_type ng_ether_typestruct = { .version = NG_ABI_VERSION, .name = NG_ETHER_NODE_TYPE, .mod_event = ng_ether_mod_event, .constructor = ng_ether_constructor, .rcvmsg = ng_ether_rcvmsg, .shutdown = ng_ether_shutdown, .newhook = ng_ether_newhook, .connect = ng_ether_connect, .rcvdata = ng_ether_rcvdata, .disconnect = ng_ether_disconnect, .cmdlist = ng_ether_cmdlist, }; NETGRAPH_INIT(ether, &ng_ether_typestruct); /****************************************************************** ETHERNET FUNCTION HOOKS ******************************************************************/ /* * Handle a packet that has come in on an interface. We get to * look at it here before any upper layer protocols do. * * NOTE: this function will get called at splimp() */ static void ng_ether_input(struct ifnet *ifp, struct mbuf **mp) { const node_p node = IFP2NG(ifp); const priv_p priv = NG_NODE_PRIVATE(node); int error; /* If "lower" hook not connected, let packet continue */ if (priv->lower == NULL) return; NG_SEND_DATA_ONLY(error, priv->lower, *mp); /* sets *mp = NULL */ } /* * Handle a packet that has come in on an interface, and which * does not match any of our known protocols (an ``orphan''). * * NOTE: this function will get called at splimp() */ static void ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m) { const node_p node = IFP2NG(ifp); const priv_p priv = NG_NODE_PRIVATE(node); int error; /* If "orphan" hook not connected, discard packet */ if (priv->orphan == NULL) { m_freem(m); return; } NG_SEND_DATA_ONLY(error, priv->orphan, m); } /* * Handle a packet that is going out on an interface. * The Ethernet header is already attached to the mbuf. */ static int ng_ether_output(struct ifnet *ifp, struct mbuf **mp) { const node_p node = IFP2NG(ifp); const priv_p priv = NG_NODE_PRIVATE(node); int error = 0; /* If "upper" hook not connected, let packet continue */ if (priv->upper == NULL) return (0); /* Send it out "upper" hook */ NG_SEND_DATA_ONLY(error, priv->upper, *mp); return (error); } /* * A new Ethernet interface has been attached. * Create a new node for it, etc. */ static void ng_ether_attach(struct ifnet *ifp) { priv_p priv; node_p node; /* Create node */ KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __func__)); if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { log(LOG_ERR, "%s: can't %s for %s\n", __func__, "create node", ifp->if_xname); return; } /* Allocate private data */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); if (priv == NULL) { log(LOG_ERR, "%s: can't %s for %s\n", __func__, "allocate memory", ifp->if_xname); NG_NODE_UNREF(node); return; } NG_NODE_SET_PRIVATE(node, priv); priv->ifp = ifp; IFP2NG_SET(ifp, node); priv->autoSrcAddr = 1; priv->hwassist = ifp->if_hwassist; /* Try to give the node the same name as the interface */ if (ng_name_node(node, ifp->if_xname) != 0) { log(LOG_WARNING, "%s: can't name node %s\n", __func__, ifp->if_xname); } } /* * An Ethernet interface is being detached. * REALLY Destroy its node. */ static void ng_ether_detach(struct ifnet *ifp) { const node_p node = IFP2NG(ifp); const priv_p priv = NG_NODE_PRIVATE(node); NG_NODE_REALLY_DIE(node); /* Force real removal of node */ /* * We can't assume the ifnet is still around when we run shutdown * So zap it now. XXX We HOPE that anything running at this time * handles it (as it should in the non netgraph case). */ IFP2NG_SET(ifp, NULL); priv->ifp = NULL; /* XXX race if interrupted an output packet */ ng_rmnode_self(node); /* remove all netgraph parts */ } /* * Notify graph about link event. * if_link_state_change() has already checked that the state has changed. */ static void ng_ether_link_state(struct ifnet *ifp, int state) { const node_p node = IFP2NG(ifp); const priv_p priv = NG_NODE_PRIVATE(node); struct ng_mesg *msg; int cmd, dummy_error = 0; if (priv->lower == NULL) return; if (state == LINK_STATE_UP) cmd = NGM_LINK_IS_UP; else if (state == LINK_STATE_DOWN) cmd = NGM_LINK_IS_DOWN; else return; NG_MKMESSAGE(msg, NGM_FLOW_COOKIE, cmd, 0, M_NOWAIT); if (msg != NULL) NG_SEND_MSG_HOOK(dummy_error, node, msg, priv->lower, 0); } /****************************************************************** NETGRAPH NODE METHODS ******************************************************************/ /* * It is not possible or allowable to create a node of this type. * Nodes get created when the interface is attached (or, when * this node type's KLD is loaded). */ static int ng_ether_constructor(node_p node) { return (EINVAL); } /* * Check for attaching a new hook. */ static int ng_ether_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = NG_NODE_PRIVATE(node); hook_p *hookptr; /* Divert hook is an alias for lower */ if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) name = NG_ETHER_HOOK_LOWER; /* Which hook? */ if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) hookptr = &priv->upper; else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) hookptr = &priv->lower; else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) hookptr = &priv->orphan; else return (EINVAL); /* Check if already connected (shouldn't be, but doesn't hurt) */ if (*hookptr != NULL) return (EISCONN); /* Disable hardware checksums while 'upper' hook is connected */ if (hookptr == &priv->upper) priv->ifp->if_hwassist = 0; /* OK */ *hookptr = hook; return (0); } /* * Hooks are attached, adjust to force queueing. * We don't really care which hook it is. * they should all be queuing for outgoing data. */ static int ng_ether_connect(hook_p hook) { NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); return (0); } /* * Receive an incoming control message. */ static int ng_ether_rcvmsg(node_p node, item_p item, hook_p lasthook) { const priv_p priv = NG_NODE_PRIVATE(node); struct ng_mesg *resp = NULL; int error = 0; struct ng_mesg *msg; NGI_GET_MSG(item, msg); switch (msg->header.typecookie) { case NGM_ETHER_COOKIE: switch (msg->header.cmd) { case NGM_ETHER_GET_IFNAME: NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } strlcpy(resp->data, priv->ifp->if_xname, IFNAMSIZ); break; case NGM_ETHER_GET_IFINDEX: NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } *((u_int32_t *)resp->data) = priv->ifp->if_index; break; case NGM_ETHER_GET_ENADDR: NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } bcopy(IFP2ENADDR(priv->ifp), resp->data, ETHER_ADDR_LEN); break; case NGM_ETHER_SET_ENADDR: { if (msg->header.arglen != ETHER_ADDR_LEN) { error = EINVAL; break; } error = if_setlladdr(priv->ifp, (u_char *)msg->data, ETHER_ADDR_LEN); break; } case NGM_ETHER_GET_PROMISC: NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } *((u_int32_t *)resp->data) = priv->promisc; break; case NGM_ETHER_SET_PROMISC: { u_char want; if (msg->header.arglen != sizeof(u_int32_t)) { error = EINVAL; break; } want = !!*((u_int32_t *)msg->data); if (want ^ priv->promisc) { if ((error = ifpromisc(priv->ifp, want)) != 0) break; priv->promisc = want; } break; } case NGM_ETHER_GET_AUTOSRC: NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } *((u_int32_t *)resp->data) = priv->autoSrcAddr; break; case NGM_ETHER_SET_AUTOSRC: if (msg->header.arglen != sizeof(u_int32_t)) { error = EINVAL; break; } priv->autoSrcAddr = !!*((u_int32_t *)msg->data); break; case NGM_ETHER_ADD_MULTI: { struct sockaddr_dl sa_dl; struct ifmultiaddr *ifm; if (msg->header.arglen != ETHER_ADDR_LEN) { error = EINVAL; break; } bzero(&sa_dl, sizeof(struct sockaddr_dl)); sa_dl.sdl_len = sizeof(struct sockaddr_dl); sa_dl.sdl_family = AF_LINK; sa_dl.sdl_alen = ETHER_ADDR_LEN; bcopy((void *)msg->data, LLADDR(&sa_dl), ETHER_ADDR_LEN); error = if_addmulti(priv->ifp, (struct sockaddr *)&sa_dl, &ifm); break; } case NGM_ETHER_DEL_MULTI: { struct sockaddr_dl sa_dl; if (msg->header.arglen != ETHER_ADDR_LEN) { error = EINVAL; break; } bzero(&sa_dl, sizeof(struct sockaddr_dl)); sa_dl.sdl_len = sizeof(struct sockaddr_dl); sa_dl.sdl_family = AF_LINK; sa_dl.sdl_alen = ETHER_ADDR_LEN; bcopy((void *)msg->data, LLADDR(&sa_dl), ETHER_ADDR_LEN); error = if_delmulti(priv->ifp, (struct sockaddr *)&sa_dl); break; } case NGM_ETHER_DETACH: ng_ether_detach(priv->ifp); break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } NG_RESPOND_MSG(error, node, item, resp); NG_FREE_MSG(msg); return (error); } /* * Receive data on a hook. */ static int ng_ether_rcvdata(hook_p hook, item_p item) { const node_p node = NG_HOOK_NODE(hook); const priv_p priv = NG_NODE_PRIVATE(node); struct mbuf *m; NGI_GET_M(item, m); NG_FREE_ITEM(item); if (hook == priv->lower || hook == priv->orphan) return ng_ether_rcv_lower(node, m); if (hook == priv->upper) return ng_ether_rcv_upper(node, m); panic("%s: weird hook", __func__); #ifdef RESTARTABLE_PANICS /* so we don't get an error msg in LINT */ return (0); #endif } /* * Handle an mbuf received on the "lower" or "orphan" hook. */ static int ng_ether_rcv_lower(node_p node, struct mbuf *m) { const priv_p priv = NG_NODE_PRIVATE(node); struct ifnet *const ifp = priv->ifp; /* Check whether interface is ready for packets */ if (!((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))) { NG_FREE_M(m); return (ENETDOWN); } /* Make sure header is fully pulled up */ if (m->m_pkthdr.len < sizeof(struct ether_header)) { NG_FREE_M(m); return (EINVAL); } if (m->m_len < sizeof(struct ether_header) && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) return (ENOBUFS); /* Drop in the MAC address if desired */ if (priv->autoSrcAddr) { /* Make the mbuf writable if it's not already */ if (!M_WRITABLE(m) && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) return (ENOBUFS); /* Overwrite source MAC address */ bcopy(IFP2ENADDR(ifp), mtod(m, struct ether_header *)->ether_shost, ETHER_ADDR_LEN); } /* Send it on its way */ return ether_output_frame(ifp, m); } /* * Handle an mbuf received on the "upper" hook. */ static int ng_ether_rcv_upper(node_p node, struct mbuf *m) { const priv_p priv = NG_NODE_PRIVATE(node); + struct ifnet *ifp = priv->ifp; - m->m_pkthdr.rcvif = priv->ifp; + m->m_pkthdr.rcvif = ifp; if (BDG_ACTIVE(priv->ifp) ) if ((m = bridge_in_ptr(priv->ifp, m)) == NULL) return (0); + /* Pass the packet to the bridge, it may come back to us */ + if (ifp->if_bridge) { + BRIDGE_INPUT(ifp, m); + if (m == NULL) + return (0); + } + /* Route packet back in */ - ether_demux(priv->ifp, m); + ether_demux(ifp, m); return (0); } /* * Shutdown node. This resets the node but does not remove it * unless the REALLY_DIE flag is set. */ static int ng_ether_shutdown(node_p node) { const priv_p priv = NG_NODE_PRIVATE(node); if (node->nd_flags & NGF_REALLY_DIE) { /* * WE came here because the ethernet card is being unloaded, * so stop being persistant. * Actually undo all the things we did on creation. * Assume the ifp has already been freed. */ NG_NODE_SET_PRIVATE(node, NULL); FREE(priv, M_NETGRAPH); NG_NODE_UNREF(node); /* free node itself */ return (0); } if (priv->promisc) { /* disable promiscuous mode */ (void)ifpromisc(priv->ifp, 0); priv->promisc = 0; } priv->autoSrcAddr = 1; /* reset auto-src-addr flag */ NG_NODE_REVIVE(node); /* Signal ng_rmnode we are persisant */ return (0); } /* * Hook disconnection. */ static int ng_ether_disconnect(hook_p hook) { const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); if (hook == priv->upper) { priv->upper = NULL; if (priv->ifp != NULL) /* restore h/w csum */ priv->ifp->if_hwassist = priv->hwassist; } else if (hook == priv->lower) priv->lower = NULL; else if (hook == priv->orphan) priv->orphan = NULL; else panic("%s: weird hook", __func__); if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) ng_rmnode_self(NG_HOOK_NODE(hook)); /* reset node */ return (0); } /****************************************************************** INITIALIZATION ******************************************************************/ /* * Handle loading and unloading for this node type. */ static int ng_ether_mod_event(module_t mod, int event, void *data) { struct ifnet *ifp; int error = 0; int s; s = splnet(); switch (event) { case MOD_LOAD: /* Register function hooks */ if (ng_ether_attach_p != NULL) { error = EEXIST; break; } ng_ether_attach_p = ng_ether_attach; ng_ether_detach_p = ng_ether_detach; ng_ether_output_p = ng_ether_output; ng_ether_input_p = ng_ether_input; ng_ether_input_orphan_p = ng_ether_input_orphan; ng_ether_link_state_p = ng_ether_link_state; /* Create nodes for any already-existing Ethernet interfaces */ IFNET_RLOCK(); TAILQ_FOREACH(ifp, &ifnet, if_link) { if (ifp->if_type == IFT_ETHER || ifp->if_type == IFT_L2VLAN) ng_ether_attach(ifp); } IFNET_RUNLOCK(); break; case MOD_UNLOAD: /* * Note that the base code won't try to unload us until * all nodes have been removed, and that can't happen * until all Ethernet interfaces are removed. In any * case, we know there are no nodes left if the action * is MOD_UNLOAD, so there's no need to detach any nodes. */ /* Unregister function hooks */ ng_ether_attach_p = NULL; ng_ether_detach_p = NULL; ng_ether_output_p = NULL; ng_ether_input_p = NULL; ng_ether_input_orphan_p = NULL; ng_ether_link_state_p = NULL; break; default: error = EOPNOTSUPP; break; } splx(s); return (error); }