149808fa4SAndrew Thompson /* $NetBSD: if_bridge.c,v 1.31 2005/06/01 19:45:34 jdc Exp $ */ 231997bf2SAndrew Thompson 3fe267a55SPedro F. Giffuni /*- 4fe267a55SPedro F. Giffuni * SPDX-License-Identifier: BSD-4-Clause 5fe267a55SPedro F. Giffuni * 631997bf2SAndrew Thompson * Copyright 2001 Wasabi Systems, Inc. 731997bf2SAndrew Thompson * All rights reserved. 831997bf2SAndrew Thompson * 931997bf2SAndrew Thompson * Written by Jason R. Thorpe for Wasabi Systems, Inc. 1031997bf2SAndrew Thompson * 1131997bf2SAndrew Thompson * Redistribution and use in source and binary forms, with or without 1231997bf2SAndrew Thompson * modification, are permitted provided that the following conditions 1331997bf2SAndrew Thompson * are met: 1431997bf2SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 1531997bf2SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1631997bf2SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1731997bf2SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1831997bf2SAndrew Thompson * documentation and/or other materials provided with the distribution. 1931997bf2SAndrew Thompson * 3. All advertising materials mentioning features or use of this software 2031997bf2SAndrew Thompson * must display the following acknowledgement: 2131997bf2SAndrew Thompson * This product includes software developed for the NetBSD Project by 2231997bf2SAndrew Thompson * Wasabi Systems, Inc. 2331997bf2SAndrew Thompson * 4. The name of Wasabi Systems, Inc. may not be used to endorse 2431997bf2SAndrew Thompson * or promote products derived from this software without specific prior 2531997bf2SAndrew Thompson * written permission. 2631997bf2SAndrew Thompson * 2731997bf2SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 2831997bf2SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2931997bf2SAndrew Thompson * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 3031997bf2SAndrew Thompson * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 3131997bf2SAndrew Thompson * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 3231997bf2SAndrew Thompson * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3331997bf2SAndrew Thompson * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3431997bf2SAndrew Thompson * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3531997bf2SAndrew Thompson * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3631997bf2SAndrew Thompson * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3731997bf2SAndrew Thompson * POSSIBILITY OF SUCH DAMAGE. 3831997bf2SAndrew Thompson */ 3931997bf2SAndrew Thompson 4031997bf2SAndrew Thompson /* 4131997bf2SAndrew Thompson * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) 4231997bf2SAndrew Thompson * All rights reserved. 4331997bf2SAndrew Thompson * 4431997bf2SAndrew Thompson * Redistribution and use in source and binary forms, with or without 4531997bf2SAndrew Thompson * modification, are permitted provided that the following conditions 4631997bf2SAndrew Thompson * are met: 4731997bf2SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 4831997bf2SAndrew Thompson * notice, this list of conditions and the following disclaimer. 4931997bf2SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 5031997bf2SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 5131997bf2SAndrew Thompson * documentation and/or other materials provided with the distribution. 5231997bf2SAndrew Thompson * 5331997bf2SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 5431997bf2SAndrew Thompson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 5531997bf2SAndrew Thompson * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5631997bf2SAndrew Thompson * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 5731997bf2SAndrew Thompson * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 5831997bf2SAndrew Thompson * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 5931997bf2SAndrew Thompson * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 6031997bf2SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 6131997bf2SAndrew Thompson * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 6231997bf2SAndrew Thompson * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 6331997bf2SAndrew Thompson * POSSIBILITY OF SUCH DAMAGE. 6431997bf2SAndrew Thompson * 6531997bf2SAndrew Thompson * OpenBSD: if_bridge.c,v 1.60 2001/06/15 03:38:33 itojun Exp 6631997bf2SAndrew Thompson */ 6731997bf2SAndrew Thompson 6831997bf2SAndrew Thompson /* 6931997bf2SAndrew Thompson * Network interface bridge support. 7031997bf2SAndrew Thompson * 7131997bf2SAndrew Thompson * TODO: 7231997bf2SAndrew Thompson * 7331997bf2SAndrew Thompson * - Currently only supports Ethernet-like interfaces (Ethernet, 7431997bf2SAndrew Thompson * 802.11, VLANs on Ethernet, etc.) Figure out a nice way 750437c8e3SBrooks Davis * to bridge other types of interfaces (maybe consider 760437c8e3SBrooks Davis * heterogeneous bridges). 7731997bf2SAndrew Thompson */ 7831997bf2SAndrew Thompson 7931997bf2SAndrew Thompson #include <sys/cdefs.h> 8031997bf2SAndrew Thompson #include "opt_inet.h" 8131997bf2SAndrew Thompson #include "opt_inet6.h" 8231997bf2SAndrew Thompson 8331997bf2SAndrew Thompson #include <sys/param.h> 84c3322cb9SGleb Smirnoff #include <sys/eventhandler.h> 8531997bf2SAndrew Thompson #include <sys/mbuf.h> 8631997bf2SAndrew Thompson #include <sys/malloc.h> 8731997bf2SAndrew Thompson #include <sys/protosw.h> 8831997bf2SAndrew Thompson #include <sys/systm.h> 890fe082e7SAndrew Thompson #include <sys/jail.h> 9031997bf2SAndrew Thompson #include <sys/time.h> 9131997bf2SAndrew Thompson #include <sys/socket.h> /* for net/if.h */ 9231997bf2SAndrew Thompson #include <sys/sockio.h> 9331997bf2SAndrew Thompson #include <sys/ctype.h> /* string functions */ 9431997bf2SAndrew Thompson #include <sys/kernel.h> 9531997bf2SAndrew Thompson #include <sys/random.h> 9673d480aeSAndrew Thompson #include <sys/syslog.h> 9731997bf2SAndrew Thompson #include <sys/sysctl.h> 9831997bf2SAndrew Thompson #include <vm/uma.h> 9931997bf2SAndrew Thompson #include <sys/module.h> 100acd3428bSRobert Watson #include <sys/priv.h> 10131997bf2SAndrew Thompson #include <sys/proc.h> 10231997bf2SAndrew Thompson #include <sys/lock.h> 10331997bf2SAndrew Thompson #include <sys/mutex.h> 10431997bf2SAndrew Thompson 10531997bf2SAndrew Thompson #include <net/bpf.h> 10631997bf2SAndrew Thompson #include <net/if.h> 10731997bf2SAndrew Thompson #include <net/if_clone.h> 10831997bf2SAndrew Thompson #include <net/if_dl.h> 10931997bf2SAndrew Thompson #include <net/if_types.h> 11031997bf2SAndrew Thompson #include <net/if_var.h> 1112c2b37adSJustin Hibbits #include <net/if_private.h> 11231997bf2SAndrew Thompson #include <net/pfil.h> 1130b4b0b0fSJulian Elischer #include <net/vnet.h> 11431997bf2SAndrew Thompson 115833e8dc5SGleb Smirnoff #include <netinet/in.h> 11631997bf2SAndrew Thompson #include <netinet/in_systm.h> 11731997bf2SAndrew Thompson #include <netinet/in_var.h> 11831997bf2SAndrew Thompson #include <netinet/ip.h> 11931997bf2SAndrew Thompson #include <netinet/ip_var.h> 12031997bf2SAndrew Thompson #ifdef INET6 12131997bf2SAndrew Thompson #include <netinet/ip6.h> 12231997bf2SAndrew Thompson #include <netinet6/ip6_var.h> 123af805644SHiroki Sato #include <netinet6/in6_ifattach.h> 12431997bf2SAndrew Thompson #endif 125259d2d54SBjoern A. Zeeb #if defined(INET) || defined(INET6) 126b3a1f937SAndrew Thompson #include <netinet/ip_carp.h> 127b3a1f937SAndrew Thompson #endif 12831997bf2SAndrew Thompson #include <machine/in_cksum.h> 129833e8dc5SGleb Smirnoff #include <netinet/if_ether.h> 13096e47153SAndrew Thompson #include <net/bridgestp.h> 13131997bf2SAndrew Thompson #include <net/if_bridgevar.h> 13231997bf2SAndrew Thompson #include <net/if_llc.h> 13322dcc3c1SAndrew Thompson #include <net/if_vlan_var.h> 13431997bf2SAndrew Thompson 13531997bf2SAndrew Thompson #include <net/route.h> 13631997bf2SAndrew Thompson 13731997bf2SAndrew Thompson /* 138ef84dd8fSLexi Winter * At various points in the code we need to know if we're hooked into the INET 139ef84dd8fSLexi Winter * and/or INET6 pfil. Define some macros to do that based on which IP versions 140ef84dd8fSLexi Winter * are enabled in the kernel. This avoids littering the rest of the code with 141ef84dd8fSLexi Winter * #ifnet INET6 to avoid referencing V_inet6_pfil_head. 142ef84dd8fSLexi Winter */ 143ef84dd8fSLexi Winter #ifdef INET6 144ef84dd8fSLexi Winter #define PFIL_HOOKED_IN_INET6 PFIL_HOOKED_IN(V_inet6_pfil_head) 145ef84dd8fSLexi Winter #define PFIL_HOOKED_OUT_INET6 PFIL_HOOKED_OUT(V_inet6_pfil_head) 146ef84dd8fSLexi Winter #else 147ef84dd8fSLexi Winter #define PFIL_HOOKED_IN_INET6 false 148ef84dd8fSLexi Winter #define PFIL_HOOKED_OUT_INET6 false 149ef84dd8fSLexi Winter #endif 150ef84dd8fSLexi Winter 151ef84dd8fSLexi Winter #ifdef INET 152ef84dd8fSLexi Winter #define PFIL_HOOKED_IN_INET PFIL_HOOKED_IN(V_inet_pfil_head) 153ef84dd8fSLexi Winter #define PFIL_HOOKED_OUT_INET PFIL_HOOKED_OUT(V_inet_pfil_head) 154ef84dd8fSLexi Winter #else 155ef84dd8fSLexi Winter #define PFIL_HOOKED_IN_INET false 156ef84dd8fSLexi Winter #define PFIL_HOOKED_OUT_INET false 157ef84dd8fSLexi Winter #endif 158ef84dd8fSLexi Winter 159ef84dd8fSLexi Winter #define PFIL_HOOKED_IN_46 (PFIL_HOOKED_IN_INET6 || PFIL_HOOKED_IN_INET) 160ef84dd8fSLexi Winter #define PFIL_HOOKED_OUT_46 (PFIL_HOOKED_OUT_INET6 || PFIL_HOOKED_OUT_INET) 161ef84dd8fSLexi Winter 162ef84dd8fSLexi Winter /* 16331997bf2SAndrew Thompson * Size of the route hash table. Must be a power of two. 16431997bf2SAndrew Thompson */ 16531997bf2SAndrew Thompson #ifndef BRIDGE_RTHASH_SIZE 16631997bf2SAndrew Thompson #define BRIDGE_RTHASH_SIZE 1024 16731997bf2SAndrew Thompson #endif 16831997bf2SAndrew Thompson 16931997bf2SAndrew Thompson #define BRIDGE_RTHASH_MASK (BRIDGE_RTHASH_SIZE - 1) 17031997bf2SAndrew Thompson 17131997bf2SAndrew Thompson /* 17270b23a45SAndrew Thompson * Default maximum number of addresses to cache. 17331997bf2SAndrew Thompson */ 17431997bf2SAndrew Thompson #ifndef BRIDGE_RTABLE_MAX 17570b23a45SAndrew Thompson #define BRIDGE_RTABLE_MAX 2000 17631997bf2SAndrew Thompson #endif 17731997bf2SAndrew Thompson 17831997bf2SAndrew Thompson /* 17931997bf2SAndrew Thompson * Timeout (in seconds) for entries learned dynamically. 18031997bf2SAndrew Thompson */ 18131997bf2SAndrew Thompson #ifndef BRIDGE_RTABLE_TIMEOUT 18231997bf2SAndrew Thompson #define BRIDGE_RTABLE_TIMEOUT (20 * 60) /* same as ARP */ 18331997bf2SAndrew Thompson #endif 18431997bf2SAndrew Thompson 18531997bf2SAndrew Thompson /* 18631997bf2SAndrew Thompson * Number of seconds between walks of the route list. 18731997bf2SAndrew Thompson */ 18831997bf2SAndrew Thompson #ifndef BRIDGE_RTABLE_PRUNE_PERIOD 18931997bf2SAndrew Thompson #define BRIDGE_RTABLE_PRUNE_PERIOD (5 * 60) 19031997bf2SAndrew Thompson #endif 19131997bf2SAndrew Thompson 1927c2fb83aSAndrew Thompson /* 193ec29c623SAndrew Thompson * List of capabilities to possibly mask on the member interface. 1947c2fb83aSAndrew Thompson */ 19584e63372SAlexander Motin #define BRIDGE_IFCAPS_MASK (IFCAP_TOE|IFCAP_TSO|IFCAP_TXCSUM|\ 196*2bbfbf80SMark Johnston IFCAP_TXCSUM_IPV6|IFCAP_MEXTPG) 1977c2fb83aSAndrew Thompson 1989674cf0eSAndrew Thompson /* 1993de029efSJack F Vogel * List of capabilities to strip 2003de029efSJack F Vogel */ 2013de029efSJack F Vogel #define BRIDGE_IFCAPS_STRIP IFCAP_LRO 2023de029efSJack F Vogel 2033de029efSJack F Vogel /* 20433b1fe11SKristof Provost * Bridge locking 2054af1bd81SKristof Provost * 2064af1bd81SKristof Provost * The bridge relies heavily on the epoch(9) system to protect its data 2074af1bd81SKristof Provost * structures. This means we can safely use CK_LISTs while in NET_EPOCH, but we 2084af1bd81SKristof Provost * must ensure there is only one writer at a time. 2094af1bd81SKristof Provost * 2104af1bd81SKristof Provost * That is: for read accesses we only need to be in NET_EPOCH, but for write 2114af1bd81SKristof Provost * accesses we must hold: 2124af1bd81SKristof Provost * 2134af1bd81SKristof Provost * - BRIDGE_RT_LOCK, for any change to bridge_rtnodes 2144af1bd81SKristof Provost * - BRIDGE_LOCK, for any other change 2154af1bd81SKristof Provost * 216f7faa4adSGordon Bergling * The BRIDGE_LOCK is a sleepable lock, because it is held across ioctl() 2174af1bd81SKristof Provost * calls to bridge member interfaces and these ioctl()s can sleep. 2184af1bd81SKristof Provost * The BRIDGE_RT_LOCK is a non-sleepable mutex, because it is sometimes 2194af1bd81SKristof Provost * required while we're in NET_EPOCH and then we're not allowed to sleep. 22033b1fe11SKristof Provost */ 22133b1fe11SKristof Provost #define BRIDGE_LOCK_INIT(_sc) do { \ 2224af1bd81SKristof Provost sx_init(&(_sc)->sc_sx, "if_bridge"); \ 2234af1bd81SKristof Provost mtx_init(&(_sc)->sc_rt_mtx, "if_bridge rt", NULL, MTX_DEF); \ 22433b1fe11SKristof Provost } while (0) 22533b1fe11SKristof Provost #define BRIDGE_LOCK_DESTROY(_sc) do { \ 2264af1bd81SKristof Provost sx_destroy(&(_sc)->sc_sx); \ 2274af1bd81SKristof Provost mtx_destroy(&(_sc)->sc_rt_mtx); \ 22833b1fe11SKristof Provost } while (0) 2294af1bd81SKristof Provost #define BRIDGE_LOCK(_sc) sx_xlock(&(_sc)->sc_sx) 2304af1bd81SKristof Provost #define BRIDGE_UNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx) 2314af1bd81SKristof Provost #define BRIDGE_LOCK_ASSERT(_sc) sx_assert(&(_sc)->sc_sx, SX_XLOCKED) 2324af1bd81SKristof Provost #define BRIDGE_LOCK_OR_NET_EPOCH_ASSERT(_sc) \ 2334af1bd81SKristof Provost MPASS(in_epoch(net_epoch_preempt) || sx_xlocked(&(_sc)->sc_sx)) 2344af1bd81SKristof Provost #define BRIDGE_UNLOCK_ASSERT(_sc) sx_assert(&(_sc)->sc_sx, SX_UNLOCKED) 2354af1bd81SKristof Provost #define BRIDGE_RT_LOCK(_sc) mtx_lock(&(_sc)->sc_rt_mtx) 2364af1bd81SKristof Provost #define BRIDGE_RT_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_rt_mtx) 2374af1bd81SKristof Provost #define BRIDGE_RT_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_rt_mtx, MA_OWNED) 2384af1bd81SKristof Provost #define BRIDGE_RT_LOCK_OR_NET_EPOCH_ASSERT(_sc) \ 2394af1bd81SKristof Provost MPASS(in_epoch(net_epoch_preempt) || mtx_owned(&(_sc)->sc_rt_mtx)) 24033b1fe11SKristof Provost 24133b1fe11SKristof Provost /* 2429674cf0eSAndrew Thompson * Bridge interface list entry. 2439674cf0eSAndrew Thompson */ 2449674cf0eSAndrew Thompson struct bridge_iflist { 245dd00a42aSKristof Provost CK_LIST_ENTRY(bridge_iflist) bif_next; 2469674cf0eSAndrew Thompson struct ifnet *bif_ifp; /* member if */ 2479674cf0eSAndrew Thompson struct bstp_port bif_stp; /* STP state */ 2489674cf0eSAndrew Thompson uint32_t bif_flags; /* member if flags */ 249ec29c623SAndrew Thompson int bif_savedcaps; /* saved capabilities */ 2505f33ec7bSAndrew Thompson uint32_t bif_addrmax; /* max # of addresses */ 2515f33ec7bSAndrew Thompson uint32_t bif_addrcnt; /* cur. # of addresses */ 2525f33ec7bSAndrew Thompson uint32_t bif_addrexceeded;/* # of address violations */ 253fffd27e5SKristof Provost struct epoch_context bif_epoch_ctx; 2549674cf0eSAndrew Thompson }; 2559674cf0eSAndrew Thompson 2569674cf0eSAndrew Thompson /* 2579674cf0eSAndrew Thompson * Bridge route node. 2589674cf0eSAndrew Thompson */ 2599674cf0eSAndrew Thompson struct bridge_rtnode { 260dd00a42aSKristof Provost CK_LIST_ENTRY(bridge_rtnode) brt_hash; /* hash table linkage */ 261dd00a42aSKristof Provost CK_LIST_ENTRY(bridge_rtnode) brt_list; /* list linkage */ 2625f33ec7bSAndrew Thompson struct bridge_iflist *brt_dst; /* destination if */ 2639674cf0eSAndrew Thompson unsigned long brt_expire; /* expiration time */ 2649674cf0eSAndrew Thompson uint8_t brt_flags; /* address flags */ 2659674cf0eSAndrew Thompson uint8_t brt_addr[ETHER_ADDR_LEN]; 26622dcc3c1SAndrew Thompson uint16_t brt_vlan; /* vlan id */ 267fffd27e5SKristof Provost struct vnet *brt_vnet; 268fffd27e5SKristof Provost struct epoch_context brt_epoch_ctx; 2699674cf0eSAndrew Thompson }; 2705f33ec7bSAndrew Thompson #define brt_ifp brt_dst->bif_ifp 2719674cf0eSAndrew Thompson 2729674cf0eSAndrew Thompson /* 2739674cf0eSAndrew Thompson * Software state for each bridge. 2749674cf0eSAndrew Thompson */ 2759674cf0eSAndrew Thompson struct bridge_softc { 2769674cf0eSAndrew Thompson struct ifnet *sc_ifp; /* make this an interface */ 2779674cf0eSAndrew Thompson LIST_ENTRY(bridge_softc) sc_list; 2784af1bd81SKristof Provost struct sx sc_sx; 2794af1bd81SKristof Provost struct mtx sc_rt_mtx; 2809674cf0eSAndrew Thompson uint32_t sc_brtmax; /* max # of addresses */ 2819674cf0eSAndrew Thompson uint32_t sc_brtcnt; /* cur. # of addresses */ 2829674cf0eSAndrew Thompson uint32_t sc_brttimeout; /* rt timeout in seconds */ 2839674cf0eSAndrew Thompson struct callout sc_brcallout; /* bridge callout */ 284dd00a42aSKristof Provost CK_LIST_HEAD(, bridge_iflist) sc_iflist; /* member interface list */ 285dd00a42aSKristof Provost CK_LIST_HEAD(, bridge_rtnode) *sc_rthash; /* our forwarding table */ 286dd00a42aSKristof Provost CK_LIST_HEAD(, bridge_rtnode) sc_rtlist; /* list version of above */ 2879674cf0eSAndrew Thompson uint32_t sc_rthash_key; /* key for hash */ 288dd00a42aSKristof Provost CK_LIST_HEAD(, bridge_iflist) sc_spanlist; /* span ports list */ 2899674cf0eSAndrew Thompson struct bstp_state sc_stp; /* STP state */ 29051383c37SAndrew Thompson uint32_t sc_brtexceeded; /* # of cache drops */ 29166c84010SAndrew Thompson struct ifnet *sc_ifaddr; /* member mac copied from */ 2926b7e0c1cSKyle Evans struct ether_addr sc_defaddr; /* Default MAC address */ 293d862b165SMark Johnston if_input_fn_t sc_if_input; /* Saved copy of if_input */ 294fffd27e5SKristof Provost struct epoch_context sc_epoch_ctx; 2959674cf0eSAndrew Thompson }; 2969674cf0eSAndrew Thompson 2974af1bd81SKristof Provost VNET_DEFINE_STATIC(struct sx, bridge_list_sx); 2984af1bd81SKristof Provost #define V_bridge_list_sx VNET(bridge_list_sx) 29925792b11SHiroki Sato static eventhandler_tag bridge_detach_cookie; 300e0a87e8aSAndrew Thompson 30131997bf2SAndrew Thompson int bridge_rtable_prune_period = BRIDGE_RTABLE_PRUNE_PERIOD; 30231997bf2SAndrew Thompson 303a87407ffSKyle Evans VNET_DEFINE_STATIC(uma_zone_t, bridge_rtnode_zone); 304a87407ffSKyle Evans #define V_bridge_rtnode_zone VNET(bridge_rtnode_zone) 30531997bf2SAndrew Thompson 30691ebcbe0SAlexander V. Chernikov static int bridge_clone_create(struct if_clone *, char *, size_t, 30791ebcbe0SAlexander V. Chernikov struct ifc_data *, struct ifnet **); 30891ebcbe0SAlexander V. Chernikov static int bridge_clone_destroy(struct if_clone *, struct ifnet *, uint32_t); 30931997bf2SAndrew Thompson 3106b32f3d3SAndrew Thompson static int bridge_ioctl(struct ifnet *, u_long, caddr_t); 311ec29c623SAndrew Thompson static void bridge_mutecaps(struct bridge_softc *); 312ec29c623SAndrew Thompson static void bridge_set_ifcap(struct bridge_softc *, struct bridge_iflist *, 313ec29c623SAndrew Thompson int); 314e0a87e8aSAndrew Thompson static void bridge_ifdetach(void *arg __unused, struct ifnet *); 31531997bf2SAndrew Thompson static void bridge_init(void *); 3166b32f3d3SAndrew Thompson static void bridge_dummynet(struct mbuf *, struct ifnet *); 3176b32f3d3SAndrew Thompson static void bridge_stop(struct ifnet *, int); 3183582a9f6SGleb Smirnoff static int bridge_transmit(struct ifnet *, struct mbuf *); 319eb680a63SLuiz Otavio O Souza #ifdef ALTQ 320eb680a63SLuiz Otavio O Souza static void bridge_altq_start(if_t); 321eb680a63SLuiz Otavio O Souza static int bridge_altq_transmit(if_t, struct mbuf *); 322eb680a63SLuiz Otavio O Souza #endif 3233582a9f6SGleb Smirnoff static void bridge_qflush(struct ifnet *); 3246b32f3d3SAndrew Thompson static struct mbuf *bridge_input(struct ifnet *, struct mbuf *); 325d862b165SMark Johnston static void bridge_inject(struct ifnet *, struct mbuf *); 3266b32f3d3SAndrew Thompson static int bridge_output(struct ifnet *, struct mbuf *, struct sockaddr *, 327ae4b6259SAlexander V. Chernikov struct rtentry *); 3283582a9f6SGleb Smirnoff static int bridge_enqueue(struct bridge_softc *, struct ifnet *, 3299674cf0eSAndrew Thompson struct mbuf *); 3309674cf0eSAndrew Thompson static void bridge_rtdelete(struct bridge_softc *, struct ifnet *ifp, int); 33131997bf2SAndrew Thompson 33285ce7297SAndrew Thompson static void bridge_forward(struct bridge_softc *, struct bridge_iflist *, 33385ce7297SAndrew Thompson struct mbuf *m); 33431997bf2SAndrew Thompson 3356b32f3d3SAndrew Thompson static void bridge_timer(void *); 33631997bf2SAndrew Thompson 3376b32f3d3SAndrew Thompson static void bridge_broadcast(struct bridge_softc *, struct ifnet *, 3386b32f3d3SAndrew Thompson struct mbuf *, int); 33991f6764eSAndrew Thompson static void bridge_span(struct bridge_softc *, struct mbuf *); 34031997bf2SAndrew Thompson 3416b32f3d3SAndrew Thompson static int bridge_rtupdate(struct bridge_softc *, const uint8_t *, 34222dcc3c1SAndrew Thompson uint16_t, struct bridge_iflist *, int, uint8_t); 34322dcc3c1SAndrew Thompson static struct ifnet *bridge_rtlookup(struct bridge_softc *, const uint8_t *, 34422dcc3c1SAndrew Thompson uint16_t); 3456b32f3d3SAndrew Thompson static void bridge_rttrim(struct bridge_softc *); 3466b32f3d3SAndrew Thompson static void bridge_rtage(struct bridge_softc *); 3476b32f3d3SAndrew Thompson static void bridge_rtflush(struct bridge_softc *, int); 34822dcc3c1SAndrew Thompson static int bridge_rtdaddr(struct bridge_softc *, const uint8_t *, 34922dcc3c1SAndrew Thompson uint16_t); 35031997bf2SAndrew Thompson 3513e92ee8aSAndrew Thompson static void bridge_rtable_init(struct bridge_softc *); 3526b32f3d3SAndrew Thompson static void bridge_rtable_fini(struct bridge_softc *); 35331997bf2SAndrew Thompson 3546637e0f3SAndrew Thompson static int bridge_rtnode_addr_cmp(const uint8_t *, const uint8_t *); 3556b32f3d3SAndrew Thompson static struct bridge_rtnode *bridge_rtnode_lookup(struct bridge_softc *, 35622dcc3c1SAndrew Thompson const uint8_t *, uint16_t); 3576b32f3d3SAndrew Thompson static int bridge_rtnode_insert(struct bridge_softc *, 3586b32f3d3SAndrew Thompson struct bridge_rtnode *); 3596b32f3d3SAndrew Thompson static void bridge_rtnode_destroy(struct bridge_softc *, 3606b32f3d3SAndrew Thompson struct bridge_rtnode *); 3613fab7669SAndrew Thompson static void bridge_rtable_expire(struct ifnet *, int); 36273d480aeSAndrew Thompson static void bridge_state_change(struct ifnet *, int); 36331997bf2SAndrew Thompson 3646b32f3d3SAndrew Thompson static struct bridge_iflist *bridge_lookup_member(struct bridge_softc *, 36531997bf2SAndrew Thompson const char *name); 3666b32f3d3SAndrew Thompson static struct bridge_iflist *bridge_lookup_member_if(struct bridge_softc *, 36731997bf2SAndrew Thompson struct ifnet *ifp); 3686b32f3d3SAndrew Thompson static void bridge_delete_member(struct bridge_softc *, 3691a266137SAndrew Thompson struct bridge_iflist *, int); 370e0a87e8aSAndrew Thompson static void bridge_delete_span(struct bridge_softc *, 371e0a87e8aSAndrew Thompson struct bridge_iflist *); 37231997bf2SAndrew Thompson 3736b32f3d3SAndrew Thompson static int bridge_ioctl_add(struct bridge_softc *, void *); 3746b32f3d3SAndrew Thompson static int bridge_ioctl_del(struct bridge_softc *, void *); 3756b32f3d3SAndrew Thompson static int bridge_ioctl_gifflags(struct bridge_softc *, void *); 3766b32f3d3SAndrew Thompson static int bridge_ioctl_sifflags(struct bridge_softc *, void *); 3776b32f3d3SAndrew Thompson static int bridge_ioctl_scache(struct bridge_softc *, void *); 3786b32f3d3SAndrew Thompson static int bridge_ioctl_gcache(struct bridge_softc *, void *); 3796b32f3d3SAndrew Thompson static int bridge_ioctl_gifs(struct bridge_softc *, void *); 3806b32f3d3SAndrew Thompson static int bridge_ioctl_rts(struct bridge_softc *, void *); 3816b32f3d3SAndrew Thompson static int bridge_ioctl_saddr(struct bridge_softc *, void *); 3826b32f3d3SAndrew Thompson static int bridge_ioctl_sto(struct bridge_softc *, void *); 3836b32f3d3SAndrew Thompson static int bridge_ioctl_gto(struct bridge_softc *, void *); 3846b32f3d3SAndrew Thompson static int bridge_ioctl_daddr(struct bridge_softc *, void *); 3856b32f3d3SAndrew Thompson static int bridge_ioctl_flush(struct bridge_softc *, void *); 3866b32f3d3SAndrew Thompson static int bridge_ioctl_gpri(struct bridge_softc *, void *); 3876b32f3d3SAndrew Thompson static int bridge_ioctl_spri(struct bridge_softc *, void *); 3886b32f3d3SAndrew Thompson static int bridge_ioctl_ght(struct bridge_softc *, void *); 3896b32f3d3SAndrew Thompson static int bridge_ioctl_sht(struct bridge_softc *, void *); 3906b32f3d3SAndrew Thompson static int bridge_ioctl_gfd(struct bridge_softc *, void *); 3916b32f3d3SAndrew Thompson static int bridge_ioctl_sfd(struct bridge_softc *, void *); 3926b32f3d3SAndrew Thompson static int bridge_ioctl_gma(struct bridge_softc *, void *); 3936b32f3d3SAndrew Thompson static int bridge_ioctl_sma(struct bridge_softc *, void *); 3946b32f3d3SAndrew Thompson static int bridge_ioctl_sifprio(struct bridge_softc *, void *); 3956b32f3d3SAndrew Thompson static int bridge_ioctl_sifcost(struct bridge_softc *, void *); 3965f33ec7bSAndrew Thompson static int bridge_ioctl_sifmaxaddr(struct bridge_softc *, void *); 39791f6764eSAndrew Thompson static int bridge_ioctl_addspan(struct bridge_softc *, void *); 39891f6764eSAndrew Thompson static int bridge_ioctl_delspan(struct bridge_softc *, void *); 39951383c37SAndrew Thompson static int bridge_ioctl_gbparam(struct bridge_softc *, void *); 40051383c37SAndrew Thompson static int bridge_ioctl_grte(struct bridge_softc *, void *); 40151383c37SAndrew Thompson static int bridge_ioctl_gifsstp(struct bridge_softc *, void *); 4023fab7669SAndrew Thompson static int bridge_ioctl_sproto(struct bridge_softc *, void *); 4033fab7669SAndrew Thompson static int bridge_ioctl_stxhc(struct bridge_softc *, void *); 4046b32f3d3SAndrew Thompson static int bridge_pfil(struct mbuf **, struct ifnet *, struct ifnet *, 4056b32f3d3SAndrew Thompson int); 40665767e61SLexi Winter #ifdef INET 40731997bf2SAndrew Thompson static int bridge_ip_checkbasic(struct mbuf **mp); 40865767e61SLexi Winter static int bridge_fragment(struct ifnet *, struct mbuf **mp, 40965767e61SLexi Winter struct ether_header *, int, struct llc *); 41065767e61SLexi Winter #endif /* INET */ 41131997bf2SAndrew Thompson #ifdef INET6 41231997bf2SAndrew Thompson static int bridge_ip6_checkbasic(struct mbuf **mp); 41331997bf2SAndrew Thompson #endif /* INET6 */ 41473585176SZhenlei Huang static void bridge_linkstate(struct ifnet *ifp); 41573585176SZhenlei Huang static void bridge_linkcheck(struct bridge_softc *sc); 4167702d401SAndrew Thompson 417b0e38a13SKristof Provost /* 418b0e38a13SKristof Provost * Use the "null" value from IEEE 802.1Q-2014 Table 9-2 419b0e38a13SKristof Provost * to indicate untagged frames. 420b0e38a13SKristof Provost */ 42122dcc3c1SAndrew Thompson #define VLANTAGOF(_m) \ 422b0e38a13SKristof Provost (_m->m_flags & M_VLANTAG) ? EVL_VLANOFTAG(_m->m_pkthdr.ether_vtag) : DOT1Q_VID_NULL 42322dcc3c1SAndrew Thompson 424e5bda9fbSAndrew Thompson static struct bstp_cb_ops bridge_ops = { 425e5bda9fbSAndrew Thompson .bcb_state = bridge_state_change, 426e5bda9fbSAndrew Thompson .bcb_rtage = bridge_rtable_expire 427e5bda9fbSAndrew Thompson }; 428e5bda9fbSAndrew Thompson 42931997bf2SAndrew Thompson SYSCTL_DECL(_net_link); 4307029da5cSPawel Biernacki static SYSCTL_NODE(_net_link, IFT_BRIDGE, bridge, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 4317029da5cSPawel Biernacki "Bridge"); 43231997bf2SAndrew Thompson 433c5127526SHiroki Sato /* only pass IP[46] packets when pfil is enabled */ 4345f901c92SAndrew Turner VNET_DEFINE_STATIC(int, pfil_onlyip) = 1; 435c5127526SHiroki Sato #define V_pfil_onlyip VNET(pfil_onlyip) 436c5127526SHiroki Sato SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_onlyip, 437c5127526SHiroki Sato CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_onlyip), 0, 438c5127526SHiroki Sato "Only pass IP packets when pfil is enabled"); 439c5127526SHiroki Sato 440c5127526SHiroki Sato /* run pfil hooks on the bridge interface */ 44122893e58SKristof Provost VNET_DEFINE_STATIC(int, pfil_bridge) = 0; 442c5127526SHiroki Sato #define V_pfil_bridge VNET(pfil_bridge) 443c5127526SHiroki Sato SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_bridge, 444c5127526SHiroki Sato CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_bridge), 0, 445c5127526SHiroki Sato "Packet filter on the bridge interface"); 446c5127526SHiroki Sato 447c5127526SHiroki Sato /* layer2 filter with ipfw */ 4485f901c92SAndrew Turner VNET_DEFINE_STATIC(int, pfil_ipfw); 449c5127526SHiroki Sato #define V_pfil_ipfw VNET(pfil_ipfw) 450c5127526SHiroki Sato 451c5127526SHiroki Sato /* layer2 ARP filter with ipfw */ 4525f901c92SAndrew Turner VNET_DEFINE_STATIC(int, pfil_ipfw_arp); 453c5127526SHiroki Sato #define V_pfil_ipfw_arp VNET(pfil_ipfw_arp) 454c5127526SHiroki Sato SYSCTL_INT(_net_link_bridge, OID_AUTO, ipfw_arp, 455c5127526SHiroki Sato CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_ipfw_arp), 0, 456c5127526SHiroki Sato "Filter ARP packets through IPFW layer2"); 457c5127526SHiroki Sato 458c5127526SHiroki Sato /* run pfil hooks on the member interface */ 45922893e58SKristof Provost VNET_DEFINE_STATIC(int, pfil_member) = 0; 460c5127526SHiroki Sato #define V_pfil_member VNET(pfil_member) 461c5127526SHiroki Sato SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_member, 462c5127526SHiroki Sato CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_member), 0, 463c5127526SHiroki Sato "Packet filter on the member interface"); 464c5127526SHiroki Sato 465c5127526SHiroki Sato /* run pfil hooks on the physical interface for locally destined packets */ 4665f901c92SAndrew Turner VNET_DEFINE_STATIC(int, pfil_local_phys); 467c5127526SHiroki Sato #define V_pfil_local_phys VNET(pfil_local_phys) 468c5127526SHiroki Sato SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_local_phys, 469c5127526SHiroki Sato CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_local_phys), 0, 47031e4cb54SAndrew Thompson "Packet filter on the physical interface for locally destined packets"); 471c5127526SHiroki Sato 472c5127526SHiroki Sato /* log STP state changes */ 4735f901c92SAndrew Turner VNET_DEFINE_STATIC(int, log_stp); 474c5127526SHiroki Sato #define V_log_stp VNET(log_stp) 475c5127526SHiroki Sato SYSCTL_INT(_net_link_bridge, OID_AUTO, log_stp, 476c5127526SHiroki Sato CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(log_stp), 0, 477c5127526SHiroki Sato "Log STP state changes"); 478c5127526SHiroki Sato 479c5127526SHiroki Sato /* share MAC with first bridge member */ 4805f901c92SAndrew Turner VNET_DEFINE_STATIC(int, bridge_inherit_mac); 481c5127526SHiroki Sato #define V_bridge_inherit_mac VNET(bridge_inherit_mac) 482c5127526SHiroki Sato SYSCTL_INT(_net_link_bridge, OID_AUTO, inherit_mac, 483c5127526SHiroki Sato CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(bridge_inherit_mac), 0, 4846945d73bSAndrew Thompson "Inherit MAC address from the first bridge member"); 48531997bf2SAndrew Thompson 4865f901c92SAndrew Turner VNET_DEFINE_STATIC(int, allow_llz_overlap) = 0; 4879fcd8e9eSHiroki Sato #define V_allow_llz_overlap VNET(allow_llz_overlap) 4886df8a710SGleb Smirnoff SYSCTL_INT(_net_link_bridge, OID_AUTO, allow_llz_overlap, 489921e5f56SBryan Drewery CTLFLAG_RW | CTLFLAG_VNET, &VNET_NAME(allow_llz_overlap), 0, 490c5127526SHiroki Sato "Allow overlap of link-local scope " 4919fcd8e9eSHiroki Sato "zones of a bridge interface and the member interfaces"); 4929fcd8e9eSHiroki Sato 4932d3614fbSZhenlei Huang /* log MAC address port flapping */ 4942d3614fbSZhenlei Huang VNET_DEFINE_STATIC(bool, log_mac_flap) = true; 4952d3614fbSZhenlei Huang #define V_log_mac_flap VNET(log_mac_flap) 4962d3614fbSZhenlei Huang SYSCTL_BOOL(_net_link_bridge, OID_AUTO, log_mac_flap, 4972d3614fbSZhenlei Huang CTLFLAG_RW | CTLFLAG_VNET, &VNET_NAME(log_mac_flap), true, 4982d3614fbSZhenlei Huang "Log MAC address port flapping"); 4992d3614fbSZhenlei Huang 5002d3614fbSZhenlei Huang VNET_DEFINE_STATIC(int, log_interval) = 5; 5012d3614fbSZhenlei Huang VNET_DEFINE_STATIC(int, log_count) = 0; 5022d3614fbSZhenlei Huang VNET_DEFINE_STATIC(struct timeval, log_last) = { 0 }; 5032d3614fbSZhenlei Huang 5042d3614fbSZhenlei Huang #define V_log_interval VNET(log_interval) 5052d3614fbSZhenlei Huang #define V_log_count VNET(log_count) 5062d3614fbSZhenlei Huang #define V_log_last VNET(log_last) 5072d3614fbSZhenlei Huang 50831997bf2SAndrew Thompson struct bridge_control { 50931997bf2SAndrew Thompson int (*bc_func)(struct bridge_softc *, void *); 51031997bf2SAndrew Thompson int bc_argsize; 51131997bf2SAndrew Thompson int bc_flags; 51231997bf2SAndrew Thompson }; 51331997bf2SAndrew Thompson 51431997bf2SAndrew Thompson #define BC_F_COPYIN 0x01 /* copy arguments in */ 51531997bf2SAndrew Thompson #define BC_F_COPYOUT 0x02 /* copy arguments out */ 51631997bf2SAndrew Thompson #define BC_F_SUSER 0x04 /* do super-user check */ 51731997bf2SAndrew Thompson 5183bc099ebSMark Johnston static const struct bridge_control bridge_control_table[] = { 51931997bf2SAndrew Thompson { bridge_ioctl_add, sizeof(struct ifbreq), 52031997bf2SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 52131997bf2SAndrew Thompson { bridge_ioctl_del, sizeof(struct ifbreq), 52231997bf2SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 52331997bf2SAndrew Thompson 52431997bf2SAndrew Thompson { bridge_ioctl_gifflags, sizeof(struct ifbreq), 52531997bf2SAndrew Thompson BC_F_COPYIN|BC_F_COPYOUT }, 52631997bf2SAndrew Thompson { bridge_ioctl_sifflags, sizeof(struct ifbreq), 52731997bf2SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 52831997bf2SAndrew Thompson 52931997bf2SAndrew Thompson { bridge_ioctl_scache, sizeof(struct ifbrparam), 53031997bf2SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 53131997bf2SAndrew Thompson { bridge_ioctl_gcache, sizeof(struct ifbrparam), 53231997bf2SAndrew Thompson BC_F_COPYOUT }, 53331997bf2SAndrew Thompson 53431997bf2SAndrew Thompson { bridge_ioctl_gifs, sizeof(struct ifbifconf), 53531997bf2SAndrew Thompson BC_F_COPYIN|BC_F_COPYOUT }, 53631997bf2SAndrew Thompson { bridge_ioctl_rts, sizeof(struct ifbaconf), 53731997bf2SAndrew Thompson BC_F_COPYIN|BC_F_COPYOUT }, 53831997bf2SAndrew Thompson 53931997bf2SAndrew Thompson { bridge_ioctl_saddr, sizeof(struct ifbareq), 54031997bf2SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 54131997bf2SAndrew Thompson 54231997bf2SAndrew Thompson { bridge_ioctl_sto, sizeof(struct ifbrparam), 54331997bf2SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 54431997bf2SAndrew Thompson { bridge_ioctl_gto, sizeof(struct ifbrparam), 54531997bf2SAndrew Thompson BC_F_COPYOUT }, 54631997bf2SAndrew Thompson 54731997bf2SAndrew Thompson { bridge_ioctl_daddr, sizeof(struct ifbareq), 54831997bf2SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 54931997bf2SAndrew Thompson 55031997bf2SAndrew Thompson { bridge_ioctl_flush, sizeof(struct ifbreq), 55131997bf2SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 55231997bf2SAndrew Thompson 55331997bf2SAndrew Thompson { bridge_ioctl_gpri, sizeof(struct ifbrparam), 55431997bf2SAndrew Thompson BC_F_COPYOUT }, 55531997bf2SAndrew Thompson { bridge_ioctl_spri, sizeof(struct ifbrparam), 55631997bf2SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 55731997bf2SAndrew Thompson 55831997bf2SAndrew Thompson { bridge_ioctl_ght, sizeof(struct ifbrparam), 55931997bf2SAndrew Thompson BC_F_COPYOUT }, 56031997bf2SAndrew Thompson { bridge_ioctl_sht, sizeof(struct ifbrparam), 56131997bf2SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 56231997bf2SAndrew Thompson 56331997bf2SAndrew Thompson { bridge_ioctl_gfd, sizeof(struct ifbrparam), 56431997bf2SAndrew Thompson BC_F_COPYOUT }, 56531997bf2SAndrew Thompson { bridge_ioctl_sfd, sizeof(struct ifbrparam), 56631997bf2SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 56731997bf2SAndrew Thompson 56831997bf2SAndrew Thompson { bridge_ioctl_gma, sizeof(struct ifbrparam), 56931997bf2SAndrew Thompson BC_F_COPYOUT }, 57031997bf2SAndrew Thompson { bridge_ioctl_sma, sizeof(struct ifbrparam), 57131997bf2SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 57231997bf2SAndrew Thompson 57331997bf2SAndrew Thompson { bridge_ioctl_sifprio, sizeof(struct ifbreq), 57431997bf2SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 57531997bf2SAndrew Thompson 57631997bf2SAndrew Thompson { bridge_ioctl_sifcost, sizeof(struct ifbreq), 57731997bf2SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 57891f6764eSAndrew Thompson 57991f6764eSAndrew Thompson { bridge_ioctl_addspan, sizeof(struct ifbreq), 58091f6764eSAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 58191f6764eSAndrew Thompson { bridge_ioctl_delspan, sizeof(struct ifbreq), 58291f6764eSAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 58351383c37SAndrew Thompson 58451383c37SAndrew Thompson { bridge_ioctl_gbparam, sizeof(struct ifbropreq), 58551383c37SAndrew Thompson BC_F_COPYOUT }, 58651383c37SAndrew Thompson 58751383c37SAndrew Thompson { bridge_ioctl_grte, sizeof(struct ifbrparam), 58851383c37SAndrew Thompson BC_F_COPYOUT }, 58951383c37SAndrew Thompson 59051383c37SAndrew Thompson { bridge_ioctl_gifsstp, sizeof(struct ifbpstpconf), 591b8f45801SShteryana Shopova BC_F_COPYIN|BC_F_COPYOUT }, 5923fab7669SAndrew Thompson 5933fab7669SAndrew Thompson { bridge_ioctl_sproto, sizeof(struct ifbrparam), 5943fab7669SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 5953fab7669SAndrew Thompson 5963fab7669SAndrew Thompson { bridge_ioctl_stxhc, sizeof(struct ifbrparam), 5973fab7669SAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 5985f33ec7bSAndrew Thompson 5995f33ec7bSAndrew Thompson { bridge_ioctl_sifmaxaddr, sizeof(struct ifbreq), 6005f33ec7bSAndrew Thompson BC_F_COPYIN|BC_F_SUSER }, 6015f33ec7bSAndrew Thompson 60231997bf2SAndrew Thompson }; 6033bc099ebSMark Johnston static const int bridge_control_table_size = nitems(bridge_control_table); 60431997bf2SAndrew Thompson 6055f901c92SAndrew Turner VNET_DEFINE_STATIC(LIST_HEAD(, bridge_softc), bridge_list); 606c5127526SHiroki Sato #define V_bridge_list VNET(bridge_list) 6074af1bd81SKristof Provost #define BRIDGE_LIST_LOCK_INIT(x) sx_init(&V_bridge_list_sx, \ 6084af1bd81SKristof Provost "if_bridge list") 6094af1bd81SKristof Provost #define BRIDGE_LIST_LOCK_DESTROY(x) sx_destroy(&V_bridge_list_sx) 6104af1bd81SKristof Provost #define BRIDGE_LIST_LOCK(x) sx_xlock(&V_bridge_list_sx) 6114af1bd81SKristof Provost #define BRIDGE_LIST_UNLOCK(x) sx_xunlock(&V_bridge_list_sx) 612e0a87e8aSAndrew Thompson 6135f901c92SAndrew Turner VNET_DEFINE_STATIC(struct if_clone *, bridge_cloner); 614c5127526SHiroki Sato #define V_bridge_cloner VNET(bridge_cloner) 615c5127526SHiroki Sato 61642a58907SGleb Smirnoff static const char bridge_name[] = "bridge"; 61731997bf2SAndrew Thompson 618c5127526SHiroki Sato static void 619c5127526SHiroki Sato vnet_bridge_init(const void *unused __unused) 620c5127526SHiroki Sato { 621c5127526SHiroki Sato 622a87407ffSKyle Evans V_bridge_rtnode_zone = uma_zcreate("bridge_rtnode", 623a87407ffSKyle Evans sizeof(struct bridge_rtnode), NULL, NULL, NULL, NULL, 624a87407ffSKyle Evans UMA_ALIGN_PTR, 0); 625c5127526SHiroki Sato BRIDGE_LIST_LOCK_INIT(); 626c5127526SHiroki Sato LIST_INIT(&V_bridge_list); 62791ebcbe0SAlexander V. Chernikov 62891ebcbe0SAlexander V. Chernikov struct if_clone_addreq req = { 62991ebcbe0SAlexander V. Chernikov .create_f = bridge_clone_create, 63091ebcbe0SAlexander V. Chernikov .destroy_f = bridge_clone_destroy, 63191ebcbe0SAlexander V. Chernikov .flags = IFC_F_AUTOUNIT, 63291ebcbe0SAlexander V. Chernikov }; 63391ebcbe0SAlexander V. Chernikov V_bridge_cloner = ifc_attach_cloner(bridge_name, &req); 634c5127526SHiroki Sato } 635c5127526SHiroki Sato VNET_SYSINIT(vnet_bridge_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 636c5127526SHiroki Sato vnet_bridge_init, NULL); 637c5127526SHiroki Sato 638c5127526SHiroki Sato static void 639c5127526SHiroki Sato vnet_bridge_uninit(const void *unused __unused) 640c5127526SHiroki Sato { 641c5127526SHiroki Sato 64291ebcbe0SAlexander V. Chernikov ifc_detach_cloner(V_bridge_cloner); 64325792b11SHiroki Sato V_bridge_cloner = NULL; 644c5127526SHiroki Sato BRIDGE_LIST_LOCK_DESTROY(); 645fffd27e5SKristof Provost 646fffd27e5SKristof Provost /* Callbacks may use the UMA zone. */ 647150486f6SZhenlei Huang NET_EPOCH_DRAIN_CALLBACKS(); 648fffd27e5SKristof Provost 649a87407ffSKyle Evans uma_zdestroy(V_bridge_rtnode_zone); 650c5127526SHiroki Sato } 65189856f7eSBjoern A. Zeeb VNET_SYSUNINIT(vnet_bridge_uninit, SI_SUB_PSEUDO, SI_ORDER_ANY, 652c5127526SHiroki Sato vnet_bridge_uninit, NULL); 653c5127526SHiroki Sato 65431997bf2SAndrew Thompson static int 65531997bf2SAndrew Thompson bridge_modevent(module_t mod, int type, void *data) 65631997bf2SAndrew Thompson { 65731997bf2SAndrew Thompson 65831997bf2SAndrew Thompson switch (type) { 65931997bf2SAndrew Thompson case MOD_LOAD: 660c8b01292SAndrew Thompson bridge_dn_p = bridge_dummynet; 661e0a87e8aSAndrew Thompson bridge_detach_cookie = EVENTHANDLER_REGISTER( 662e0a87e8aSAndrew Thompson ifnet_departure_event, bridge_ifdetach, NULL, 663e0a87e8aSAndrew Thompson EVENTHANDLER_PRI_ANY); 66431997bf2SAndrew Thompson break; 66531997bf2SAndrew Thompson case MOD_UNLOAD: 666e0a87e8aSAndrew Thompson EVENTHANDLER_DEREGISTER(ifnet_departure_event, 667e0a87e8aSAndrew Thompson bridge_detach_cookie); 668c8b01292SAndrew Thompson bridge_dn_p = NULL; 66931997bf2SAndrew Thompson break; 67031997bf2SAndrew Thompson default: 671dc1b1b7bSAndrew Thompson return (EOPNOTSUPP); 67231997bf2SAndrew Thompson } 673dc1b1b7bSAndrew Thompson return (0); 67431997bf2SAndrew Thompson } 67531997bf2SAndrew Thompson 67631997bf2SAndrew Thompson static moduledata_t bridge_mod = { 67731997bf2SAndrew Thompson "if_bridge", 67831997bf2SAndrew Thompson bridge_modevent, 6799823d527SKevin Lo 0 68031997bf2SAndrew Thompson }; 68131997bf2SAndrew Thompson 68231997bf2SAndrew Thompson DECLARE_MODULE(if_bridge, bridge_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 683ed9de14dSKristof Provost MODULE_VERSION(if_bridge, 1); 684a4eb85b6SAndrew Thompson MODULE_DEPEND(if_bridge, bridgestp, 1, 1, 1); 68531997bf2SAndrew Thompson 68682116c33SAndrew Thompson /* 687c7dada99SKevin Lo * handler for net.link.bridge.ipfw 68882116c33SAndrew Thompson */ 68982116c33SAndrew Thompson static int 69082116c33SAndrew Thompson sysctl_pfil_ipfw(SYSCTL_HANDLER_ARGS) 69182116c33SAndrew Thompson { 692c5127526SHiroki Sato int enable = V_pfil_ipfw; 69382116c33SAndrew Thompson int error; 69482116c33SAndrew Thompson 69582116c33SAndrew Thompson error = sysctl_handle_int(oidp, &enable, 0, req); 696c5127526SHiroki Sato enable &= 1; 69782116c33SAndrew Thompson 698c5127526SHiroki Sato if (enable != V_pfil_ipfw) { 699c5127526SHiroki Sato V_pfil_ipfw = enable; 70082116c33SAndrew Thompson 70182116c33SAndrew Thompson /* 7026b743820SAndrew Thompson * Disable pfil so that ipfw doesnt run twice, if the user 7036b743820SAndrew Thompson * really wants both then they can re-enable pfil_bridge and/or 704a47f91cdSAndrew Thompson * pfil_member. Also allow non-ip packets as ipfw can filter by 705a47f91cdSAndrew Thompson * layer2 type. 70682116c33SAndrew Thompson */ 707c5127526SHiroki Sato if (V_pfil_ipfw) { 708c5127526SHiroki Sato V_pfil_onlyip = 0; 709c5127526SHiroki Sato V_pfil_bridge = 0; 710c5127526SHiroki Sato V_pfil_member = 0; 71182116c33SAndrew Thompson } 71282116c33SAndrew Thompson } 71382116c33SAndrew Thompson 714dc1b1b7bSAndrew Thompson return (error); 71582116c33SAndrew Thompson } 716c5127526SHiroki Sato SYSCTL_PROC(_net_link_bridge, OID_AUTO, ipfw, 7177029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_VNET | CTLFLAG_NEEDGIANT, 718c5127526SHiroki Sato &VNET_NAME(pfil_ipfw), 0, &sysctl_pfil_ipfw, "I", 719c5127526SHiroki Sato "Layer2 filter with IPFW"); 72031997bf2SAndrew Thompson 72138c09513SKristof Provost #ifdef VIMAGE 72238c09513SKristof Provost static void 72338c09513SKristof Provost bridge_reassign(struct ifnet *ifp, struct vnet *newvnet, char *arg) 72438c09513SKristof Provost { 72538c09513SKristof Provost struct bridge_softc *sc = ifp->if_softc; 72638c09513SKristof Provost struct bridge_iflist *bif; 72738c09513SKristof Provost 72838c09513SKristof Provost BRIDGE_LOCK(sc); 72938c09513SKristof Provost 73038c09513SKristof Provost while ((bif = CK_LIST_FIRST(&sc->sc_iflist)) != NULL) 73138c09513SKristof Provost bridge_delete_member(sc, bif, 0); 73238c09513SKristof Provost 73338c09513SKristof Provost while ((bif = CK_LIST_FIRST(&sc->sc_spanlist)) != NULL) { 73438c09513SKristof Provost bridge_delete_span(sc, bif); 73538c09513SKristof Provost } 73638c09513SKristof Provost 73738c09513SKristof Provost BRIDGE_UNLOCK(sc); 73838c09513SKristof Provost 73938c09513SKristof Provost ether_reassign(ifp, newvnet, arg); 74038c09513SKristof Provost } 74138c09513SKristof Provost #endif 74238c09513SKristof Provost 74331997bf2SAndrew Thompson /* 74431997bf2SAndrew Thompson * bridge_clone_create: 74531997bf2SAndrew Thompson * 74631997bf2SAndrew Thompson * Create a new bridge instance. 74731997bf2SAndrew Thompson */ 7486b32f3d3SAndrew Thompson static int 74991ebcbe0SAlexander V. Chernikov bridge_clone_create(struct if_clone *ifc, char *name, size_t len, 75091ebcbe0SAlexander V. Chernikov struct ifc_data *ifd, struct ifnet **ifpp) 75131997bf2SAndrew Thompson { 752fac24ad7SKristof Provost struct bridge_softc *sc; 753fac24ad7SKristof Provost struct ifnet *ifp; 75431997bf2SAndrew Thompson 75531997bf2SAndrew Thompson sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); 756e7acea82SAndrew Thompson ifp = sc->sc_ifp = if_alloc(IFT_ETHER); 75731997bf2SAndrew Thompson 7589c68675bSAndrew Thompson BRIDGE_LOCK_INIT(sc); 75931997bf2SAndrew Thompson sc->sc_brtmax = BRIDGE_RTABLE_MAX; 76031997bf2SAndrew Thompson sc->sc_brttimeout = BRIDGE_RTABLE_TIMEOUT; 76131997bf2SAndrew Thompson 76231997bf2SAndrew Thompson /* Initialize our routing table. */ 76331997bf2SAndrew Thompson bridge_rtable_init(sc); 76431997bf2SAndrew Thompson 7654af1bd81SKristof Provost callout_init_mtx(&sc->sc_brcallout, &sc->sc_rt_mtx, 0); 76631997bf2SAndrew Thompson 767dd00a42aSKristof Provost CK_LIST_INIT(&sc->sc_iflist); 768dd00a42aSKristof Provost CK_LIST_INIT(&sc->sc_spanlist); 76931997bf2SAndrew Thompson 77031997bf2SAndrew Thompson ifp->if_softc = sc; 77191ebcbe0SAlexander V. Chernikov if_initname(ifp, bridge_name, ifd->unit); 7724ec528c7SAndrew Thompson ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 77331997bf2SAndrew Thompson ifp->if_ioctl = bridge_ioctl; 774eb680a63SLuiz Otavio O Souza #ifdef ALTQ 775eb680a63SLuiz Otavio O Souza ifp->if_start = bridge_altq_start; 776eb680a63SLuiz Otavio O Souza ifp->if_transmit = bridge_altq_transmit; 777eb680a63SLuiz Otavio O Souza IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 778eb680a63SLuiz Otavio O Souza ifp->if_snd.ifq_drv_maxlen = 0; 779eb680a63SLuiz Otavio O Souza IFQ_SET_READY(&ifp->if_snd); 780eb680a63SLuiz Otavio O Souza #else 7813582a9f6SGleb Smirnoff ifp->if_transmit = bridge_transmit; 782eb680a63SLuiz Otavio O Souza #endif 7833582a9f6SGleb Smirnoff ifp->if_qflush = bridge_qflush; 78431997bf2SAndrew Thompson ifp->if_init = bridge_init; 78531997bf2SAndrew Thompson ifp->if_type = IFT_BRIDGE; 78631997bf2SAndrew Thompson 7873c3aa8c1SKyle Evans ether_gen_addr(ifp, &sc->sc_defaddr); 78831997bf2SAndrew Thompson 789e5bda9fbSAndrew Thompson bstp_attach(&sc->sc_stp, &bridge_ops); 7906b7e0c1cSKyle Evans ether_ifattach(ifp, sc->sc_defaddr.octet); 79131997bf2SAndrew Thompson /* Now undo some of the damage... */ 79231997bf2SAndrew Thompson ifp->if_baudrate = 0; 79331997bf2SAndrew Thompson ifp->if_type = IFT_BRIDGE; 79438c09513SKristof Provost #ifdef VIMAGE 79538c09513SKristof Provost ifp->if_reassign = bridge_reassign; 79638c09513SKristof Provost #endif 797d862b165SMark Johnston sc->sc_if_input = ifp->if_input; /* ether_input */ 798d862b165SMark Johnston ifp->if_input = bridge_inject; 799d862b165SMark Johnston 800d862b165SMark Johnston /* 801d862b165SMark Johnston * Allow BRIDGE_INPUT() to pass in packets originating from the bridge 802d862b165SMark Johnston * itself via bridge_inject(). This is required for netmap but 803d862b165SMark Johnston * otherwise has no effect. 804d862b165SMark Johnston */ 805d862b165SMark Johnston ifp->if_bridge_input = bridge_input; 80631997bf2SAndrew Thompson 807c5127526SHiroki Sato BRIDGE_LIST_LOCK(); 808c5127526SHiroki Sato LIST_INSERT_HEAD(&V_bridge_list, sc, sc_list); 809c5127526SHiroki Sato BRIDGE_LIST_UNLOCK(); 81091ebcbe0SAlexander V. Chernikov *ifpp = ifp; 811e0a87e8aSAndrew Thompson 81231997bf2SAndrew Thompson return (0); 81331997bf2SAndrew Thompson } 81431997bf2SAndrew Thompson 815fffd27e5SKristof Provost static void 816fffd27e5SKristof Provost bridge_clone_destroy_cb(struct epoch_context *ctx) 817fffd27e5SKristof Provost { 818fffd27e5SKristof Provost struct bridge_softc *sc; 819fffd27e5SKristof Provost 820fffd27e5SKristof Provost sc = __containerof(ctx, struct bridge_softc, sc_epoch_ctx); 821fffd27e5SKristof Provost 822fffd27e5SKristof Provost BRIDGE_LOCK_DESTROY(sc); 823fffd27e5SKristof Provost free(sc, M_DEVBUF); 824fffd27e5SKristof Provost } 825fffd27e5SKristof Provost 82631997bf2SAndrew Thompson /* 82731997bf2SAndrew Thompson * bridge_clone_destroy: 82831997bf2SAndrew Thompson * 82931997bf2SAndrew Thompson * Destroy a bridge instance. 83031997bf2SAndrew Thompson */ 83191ebcbe0SAlexander V. Chernikov static int 83291ebcbe0SAlexander V. Chernikov bridge_clone_destroy(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags) 83331997bf2SAndrew Thompson { 83431997bf2SAndrew Thompson struct bridge_softc *sc = ifp->if_softc; 83531997bf2SAndrew Thompson struct bridge_iflist *bif; 836fffd27e5SKristof Provost struct epoch_tracker et; 83731997bf2SAndrew Thompson 83831997bf2SAndrew Thompson BRIDGE_LOCK(sc); 83931997bf2SAndrew Thompson 84031997bf2SAndrew Thompson bridge_stop(ifp, 1); 84168e84b98SAndrew Thompson ifp->if_flags &= ~IFF_UP; 84231997bf2SAndrew Thompson 843dd00a42aSKristof Provost while ((bif = CK_LIST_FIRST(&sc->sc_iflist)) != NULL) 8441a266137SAndrew Thompson bridge_delete_member(sc, bif, 0); 84531997bf2SAndrew Thompson 846dd00a42aSKristof Provost while ((bif = CK_LIST_FIRST(&sc->sc_spanlist)) != NULL) { 847e0a87e8aSAndrew Thompson bridge_delete_span(sc, bif); 84891f6764eSAndrew Thompson } 84991f6764eSAndrew Thompson 850a87407ffSKyle Evans /* Tear down the routing table. */ 851a87407ffSKyle Evans bridge_rtable_fini(sc); 852a87407ffSKyle Evans 85331997bf2SAndrew Thompson BRIDGE_UNLOCK(sc); 85431997bf2SAndrew Thompson 8554af1bd81SKristof Provost NET_EPOCH_ENTER(et); 8564af1bd81SKristof Provost 85723e76431SAndrew Thompson callout_drain(&sc->sc_brcallout); 85823e76431SAndrew Thompson 859c5127526SHiroki Sato BRIDGE_LIST_LOCK(); 860e0a87e8aSAndrew Thompson LIST_REMOVE(sc, sc_list); 861c5127526SHiroki Sato BRIDGE_LIST_UNLOCK(); 862e0a87e8aSAndrew Thompson 86396e47153SAndrew Thompson bstp_detach(&sc->sc_stp); 864eb680a63SLuiz Otavio O Souza #ifdef ALTQ 865eb680a63SLuiz Otavio O Souza IFQ_PURGE(&ifp->if_snd); 866eb680a63SLuiz Otavio O Souza #endif 867fffd27e5SKristof Provost NET_EPOCH_EXIT(et); 868fffd27e5SKristof Provost 86931997bf2SAndrew Thompson ether_ifdetach(ifp); 8704b22573aSBrooks Davis if_free(ifp); 87131997bf2SAndrew Thompson 872fffd27e5SKristof Provost NET_EPOCH_CALL(bridge_clone_destroy_cb, &sc->sc_epoch_ctx); 87391ebcbe0SAlexander V. Chernikov 87491ebcbe0SAlexander V. Chernikov return (0); 87531997bf2SAndrew Thompson } 87631997bf2SAndrew Thompson 87731997bf2SAndrew Thompson /* 87831997bf2SAndrew Thompson * bridge_ioctl: 87931997bf2SAndrew Thompson * 88031997bf2SAndrew Thompson * Handle a control request from the operator. 88131997bf2SAndrew Thompson */ 8826b32f3d3SAndrew Thompson static int 88331997bf2SAndrew Thompson bridge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 88431997bf2SAndrew Thompson { 88531997bf2SAndrew Thompson struct bridge_softc *sc = ifp->if_softc; 886c2a5f1a5SHiroki Sato struct ifreq *ifr = (struct ifreq *)data; 887c2a5f1a5SHiroki Sato struct bridge_iflist *bif; 88831997bf2SAndrew Thompson struct thread *td = curthread; 88931997bf2SAndrew Thompson union { 89031997bf2SAndrew Thompson struct ifbreq ifbreq; 89131997bf2SAndrew Thompson struct ifbifconf ifbifconf; 89231997bf2SAndrew Thompson struct ifbareq ifbareq; 89331997bf2SAndrew Thompson struct ifbaconf ifbaconf; 89431997bf2SAndrew Thompson struct ifbrparam ifbrparam; 8956c32e05cSAndrew Thompson struct ifbropreq ifbropreq; 89631997bf2SAndrew Thompson } args; 89731997bf2SAndrew Thompson struct ifdrv *ifd = (struct ifdrv *) data; 89831997bf2SAndrew Thompson const struct bridge_control *bc; 89984becee1SAlexander Motin int error = 0, oldmtu; 900fffd27e5SKristof Provost 9014af1bd81SKristof Provost BRIDGE_LOCK(sc); 90231997bf2SAndrew Thompson 90331997bf2SAndrew Thompson switch (cmd) { 90459280079SAndrew Thompson case SIOCADDMULTI: 90559280079SAndrew Thompson case SIOCDELMULTI: 90659280079SAndrew Thompson break; 90759280079SAndrew Thompson 90831997bf2SAndrew Thompson case SIOCGDRVSPEC: 90931997bf2SAndrew Thompson case SIOCSDRVSPEC: 91031997bf2SAndrew Thompson if (ifd->ifd_cmd >= bridge_control_table_size) { 91131997bf2SAndrew Thompson error = EINVAL; 91231997bf2SAndrew Thompson break; 91331997bf2SAndrew Thompson } 91431997bf2SAndrew Thompson bc = &bridge_control_table[ifd->ifd_cmd]; 91531997bf2SAndrew Thompson 91631997bf2SAndrew Thompson if (cmd == SIOCGDRVSPEC && 91731997bf2SAndrew Thompson (bc->bc_flags & BC_F_COPYOUT) == 0) { 91831997bf2SAndrew Thompson error = EINVAL; 91931997bf2SAndrew Thompson break; 92031997bf2SAndrew Thompson } 92131997bf2SAndrew Thompson else if (cmd == SIOCSDRVSPEC && 92231997bf2SAndrew Thompson (bc->bc_flags & BC_F_COPYOUT) != 0) { 92331997bf2SAndrew Thompson error = EINVAL; 92431997bf2SAndrew Thompson break; 92531997bf2SAndrew Thompson } 92631997bf2SAndrew Thompson 92731997bf2SAndrew Thompson if (bc->bc_flags & BC_F_SUSER) { 928acd3428bSRobert Watson error = priv_check(td, PRIV_NET_BRIDGE); 92931997bf2SAndrew Thompson if (error) 93031997bf2SAndrew Thompson break; 93131997bf2SAndrew Thompson } 93231997bf2SAndrew Thompson 93331997bf2SAndrew Thompson if (ifd->ifd_len != bc->bc_argsize || 93431997bf2SAndrew Thompson ifd->ifd_len > sizeof(args)) { 93531997bf2SAndrew Thompson error = EINVAL; 93631997bf2SAndrew Thompson break; 93731997bf2SAndrew Thompson } 93831997bf2SAndrew Thompson 939dc1b1b7bSAndrew Thompson bzero(&args, sizeof(args)); 94031997bf2SAndrew Thompson if (bc->bc_flags & BC_F_COPYIN) { 94131997bf2SAndrew Thompson error = copyin(ifd->ifd_data, &args, ifd->ifd_len); 94231997bf2SAndrew Thompson if (error) 94331997bf2SAndrew Thompson break; 94431997bf2SAndrew Thompson } 94531997bf2SAndrew Thompson 94684becee1SAlexander Motin oldmtu = ifp->if_mtu; 94731997bf2SAndrew Thompson error = (*bc->bc_func)(sc, &args); 94831997bf2SAndrew Thompson if (error) 94931997bf2SAndrew Thompson break; 95031997bf2SAndrew Thompson 95184becee1SAlexander Motin /* 95284becee1SAlexander Motin * Bridge MTU may change during addition of the first port. 95384becee1SAlexander Motin * If it did, do network layer specific procedure. 95484becee1SAlexander Motin */ 95566bdbcd5SAlexander V. Chernikov if (ifp->if_mtu != oldmtu) 95666bdbcd5SAlexander V. Chernikov if_notifymtu(ifp); 95784becee1SAlexander Motin 95831997bf2SAndrew Thompson if (bc->bc_flags & BC_F_COPYOUT) 95931997bf2SAndrew Thompson error = copyout(&args, ifd->ifd_data, ifd->ifd_len); 96031997bf2SAndrew Thompson 96131997bf2SAndrew Thompson break; 96231997bf2SAndrew Thompson 96331997bf2SAndrew Thompson case SIOCSIFFLAGS: 96413f4c340SRobert Watson if (!(ifp->if_flags & IFF_UP) && 96513f4c340SRobert Watson (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 96631997bf2SAndrew Thompson /* 96731997bf2SAndrew Thompson * If interface is marked down and it is running, 96831997bf2SAndrew Thompson * then stop and disable it. 96931997bf2SAndrew Thompson */ 97031997bf2SAndrew Thompson bridge_stop(ifp, 1); 97113f4c340SRobert Watson } else if ((ifp->if_flags & IFF_UP) && 97213f4c340SRobert Watson !(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 97331997bf2SAndrew Thompson /* 97431997bf2SAndrew Thompson * If interface is marked up and it is stopped, then 97531997bf2SAndrew Thompson * start it. 97631997bf2SAndrew Thompson */ 9774af1bd81SKristof Provost BRIDGE_UNLOCK(sc); 978ca6c404cSAndrew Thompson (*ifp->if_init)(sc); 9794af1bd81SKristof Provost BRIDGE_LOCK(sc); 98031997bf2SAndrew Thompson } 98131997bf2SAndrew Thompson break; 98231997bf2SAndrew Thompson 98331997bf2SAndrew Thompson case SIOCSIFMTU: 98433306493SKristof Provost oldmtu = sc->sc_ifp->if_mtu; 98533306493SKristof Provost 986319a5d08SEugene Grosbein if (ifr->ifr_mtu < IF_MINMTU) { 98731997bf2SAndrew Thompson error = EINVAL; 98831997bf2SAndrew Thompson break; 989c2a5f1a5SHiroki Sato } 990dd00a42aSKristof Provost if (CK_LIST_EMPTY(&sc->sc_iflist)) { 991c2a5f1a5SHiroki Sato sc->sc_ifp->if_mtu = ifr->ifr_mtu; 992c2a5f1a5SHiroki Sato break; 993c2a5f1a5SHiroki Sato } 994dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { 99533306493SKristof Provost error = (*bif->bif_ifp->if_ioctl)(bif->bif_ifp, 99633306493SKristof Provost SIOCSIFMTU, (caddr_t)ifr); 99733306493SKristof Provost if (error != 0) { 99833306493SKristof Provost log(LOG_NOTICE, "%s: invalid MTU: %u for" 99933306493SKristof Provost " member %s\n", sc->sc_ifp->if_xname, 100033306493SKristof Provost ifr->ifr_mtu, 100133306493SKristof Provost bif->bif_ifp->if_xname); 1002c2a5f1a5SHiroki Sato error = EINVAL; 1003c2a5f1a5SHiroki Sato break; 1004c2a5f1a5SHiroki Sato } 1005c2a5f1a5SHiroki Sato } 100633306493SKristof Provost if (error) { 100733306493SKristof Provost /* Restore the previous MTU on all member interfaces. */ 100833306493SKristof Provost ifr->ifr_mtu = oldmtu; 100933306493SKristof Provost CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { 101033306493SKristof Provost (*bif->bif_ifp->if_ioctl)(bif->bif_ifp, 101133306493SKristof Provost SIOCSIFMTU, (caddr_t)ifr); 101233306493SKristof Provost } 101333306493SKristof Provost } else { 1014c2a5f1a5SHiroki Sato sc->sc_ifp->if_mtu = ifr->ifr_mtu; 101533306493SKristof Provost } 1016c2a5f1a5SHiroki Sato break; 101731997bf2SAndrew Thompson default: 101831997bf2SAndrew Thompson /* 101931997bf2SAndrew Thompson * drop the lock as ether_ioctl() will call bridge_start() and 102031997bf2SAndrew Thompson * cause the lock to be recursed. 102131997bf2SAndrew Thompson */ 10224af1bd81SKristof Provost BRIDGE_UNLOCK(sc); 102331997bf2SAndrew Thompson error = ether_ioctl(ifp, cmd, data); 10244af1bd81SKristof Provost BRIDGE_LOCK(sc); 102531997bf2SAndrew Thompson break; 102631997bf2SAndrew Thompson } 102731997bf2SAndrew Thompson 10284af1bd81SKristof Provost BRIDGE_UNLOCK(sc); 1029fffd27e5SKristof Provost 103031997bf2SAndrew Thompson return (error); 103131997bf2SAndrew Thompson } 103231997bf2SAndrew Thompson 103331997bf2SAndrew Thompson /* 10347c2fb83aSAndrew Thompson * bridge_mutecaps: 10357c2fb83aSAndrew Thompson * 10367c2fb83aSAndrew Thompson * Clear or restore unwanted capabilities on the member interface 10377c2fb83aSAndrew Thompson */ 10387c2fb83aSAndrew Thompson static void 1039ec29c623SAndrew Thompson bridge_mutecaps(struct bridge_softc *sc) 1040ec29c623SAndrew Thompson { 1041ec29c623SAndrew Thompson struct bridge_iflist *bif; 1042ec29c623SAndrew Thompson int enabled, mask; 1043ec29c623SAndrew Thompson 1044fffd27e5SKristof Provost BRIDGE_LOCK_ASSERT(sc); 1045fffd27e5SKristof Provost 1046ec29c623SAndrew Thompson /* Initial bitmask of capabilities to test */ 1047ec29c623SAndrew Thompson mask = BRIDGE_IFCAPS_MASK; 1048ec29c623SAndrew Thompson 1049dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { 1050ec29c623SAndrew Thompson /* Every member must support it or its disabled */ 1051ec29c623SAndrew Thompson mask &= bif->bif_savedcaps; 1052ec29c623SAndrew Thompson } 1053ec29c623SAndrew Thompson 1054dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { 1055ec29c623SAndrew Thompson enabled = bif->bif_ifp->if_capenable; 10563de029efSJack F Vogel enabled &= ~BRIDGE_IFCAPS_STRIP; 1057ec29c623SAndrew Thompson /* strip off mask bits and enable them again if allowed */ 1058ec29c623SAndrew Thompson enabled &= ~BRIDGE_IFCAPS_MASK; 1059ec29c623SAndrew Thompson enabled |= mask; 1060ec29c623SAndrew Thompson bridge_set_ifcap(sc, bif, enabled); 1061ec29c623SAndrew Thompson } 1062ec29c623SAndrew Thompson } 1063ec29c623SAndrew Thompson 1064ec29c623SAndrew Thompson static void 1065ec29c623SAndrew Thompson bridge_set_ifcap(struct bridge_softc *sc, struct bridge_iflist *bif, int set) 10667c2fb83aSAndrew Thompson { 10677c2fb83aSAndrew Thompson struct ifnet *ifp = bif->bif_ifp; 10687c2fb83aSAndrew Thompson struct ifreq ifr; 1069c3c93809SAlexander Motin int error, mask, stuck; 10707c2fb83aSAndrew Thompson 1071dc1b1b7bSAndrew Thompson bzero(&ifr, sizeof(ifr)); 1072ec29c623SAndrew Thompson ifr.ifr_reqcap = set; 10737c2fb83aSAndrew Thompson 1074ec29c623SAndrew Thompson if (ifp->if_capenable != set) { 10757c2fb83aSAndrew Thompson error = (*ifp->if_ioctl)(ifp, SIOCSIFCAP, (caddr_t)&ifr); 1076ec29c623SAndrew Thompson if (error) 1077ec29c623SAndrew Thompson if_printf(sc->sc_ifp, 1078ebe42881SAlexander Motin "error setting capabilities on %s: %d\n", 1079ebe42881SAlexander Motin ifp->if_xname, error); 1080c3c93809SAlexander Motin mask = BRIDGE_IFCAPS_MASK | BRIDGE_IFCAPS_STRIP; 1081c3c93809SAlexander Motin stuck = ifp->if_capenable & mask & ~set; 1082c3c93809SAlexander Motin if (stuck != 0) 1083ebe42881SAlexander Motin if_printf(sc->sc_ifp, 1084ebe42881SAlexander Motin "can't disable some capabilities on %s: 0x%x\n", 1085c3c93809SAlexander Motin ifp->if_xname, stuck); 10867c2fb83aSAndrew Thompson } 10877c2fb83aSAndrew Thompson } 10887c2fb83aSAndrew Thompson 10897c2fb83aSAndrew Thompson /* 109031997bf2SAndrew Thompson * bridge_lookup_member: 109131997bf2SAndrew Thompson * 109231997bf2SAndrew Thompson * Lookup a bridge member interface. 109331997bf2SAndrew Thompson */ 10944c843479SAndrew Thompson static struct bridge_iflist * 109531997bf2SAndrew Thompson bridge_lookup_member(struct bridge_softc *sc, const char *name) 109631997bf2SAndrew Thompson { 109731997bf2SAndrew Thompson struct bridge_iflist *bif; 109831997bf2SAndrew Thompson struct ifnet *ifp; 109931997bf2SAndrew Thompson 11004af1bd81SKristof Provost BRIDGE_LOCK_OR_NET_EPOCH_ASSERT(sc); 110131997bf2SAndrew Thompson 1102dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { 110331997bf2SAndrew Thompson ifp = bif->bif_ifp; 110431997bf2SAndrew Thompson if (strcmp(ifp->if_xname, name) == 0) 110531997bf2SAndrew Thompson return (bif); 110631997bf2SAndrew Thompson } 110731997bf2SAndrew Thompson 110831997bf2SAndrew Thompson return (NULL); 110931997bf2SAndrew Thompson } 111031997bf2SAndrew Thompson 111131997bf2SAndrew Thompson /* 111231997bf2SAndrew Thompson * bridge_lookup_member_if: 111331997bf2SAndrew Thompson * 111431997bf2SAndrew Thompson * Lookup a bridge member interface by ifnet*. 111531997bf2SAndrew Thompson */ 11164c843479SAndrew Thompson static struct bridge_iflist * 111731997bf2SAndrew Thompson bridge_lookup_member_if(struct bridge_softc *sc, struct ifnet *member_ifp) 111831997bf2SAndrew Thompson { 111931997bf2SAndrew Thompson struct bridge_iflist *bif; 112031997bf2SAndrew Thompson 11214af1bd81SKristof Provost BRIDGE_LOCK_OR_NET_EPOCH_ASSERT(sc); 112231997bf2SAndrew Thompson 1123dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { 112431997bf2SAndrew Thompson if (bif->bif_ifp == member_ifp) 112531997bf2SAndrew Thompson return (bif); 112631997bf2SAndrew Thompson } 112731997bf2SAndrew Thompson 112831997bf2SAndrew Thompson return (NULL); 112931997bf2SAndrew Thompson } 113031997bf2SAndrew Thompson 1131fffd27e5SKristof Provost static void 1132fffd27e5SKristof Provost bridge_delete_member_cb(struct epoch_context *ctx) 1133fffd27e5SKristof Provost { 1134fffd27e5SKristof Provost struct bridge_iflist *bif; 1135fffd27e5SKristof Provost 1136fffd27e5SKristof Provost bif = __containerof(ctx, struct bridge_iflist, bif_epoch_ctx); 1137fffd27e5SKristof Provost 1138fffd27e5SKristof Provost free(bif, M_DEVBUF); 1139fffd27e5SKristof Provost } 1140fffd27e5SKristof Provost 114131997bf2SAndrew Thompson /* 114231997bf2SAndrew Thompson * bridge_delete_member: 114331997bf2SAndrew Thompson * 114431997bf2SAndrew Thompson * Delete the specified member interface. 114531997bf2SAndrew Thompson */ 11466b32f3d3SAndrew Thompson static void 11471a266137SAndrew Thompson bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif, 11481a266137SAndrew Thompson int gone) 114931997bf2SAndrew Thompson { 115031997bf2SAndrew Thompson struct ifnet *ifs = bif->bif_ifp; 1151fe878019SPhilip Paeps struct ifnet *fif = NULL; 1152dd00a42aSKristof Provost struct bridge_iflist *bifl; 115331997bf2SAndrew Thompson 115431997bf2SAndrew Thompson BRIDGE_LOCK_ASSERT(sc); 115531997bf2SAndrew Thompson 115696e47153SAndrew Thompson if (bif->bif_flags & IFBIF_STP) 1157071fff62SAndrew Thompson bstp_disable(&bif->bif_stp); 115896e47153SAndrew Thompson 115931997bf2SAndrew Thompson ifs->if_bridge = NULL; 1160dd00a42aSKristof Provost CK_LIST_REMOVE(bif, bif_next); 116131997bf2SAndrew Thompson 1162fe878019SPhilip Paeps /* 1163fe878019SPhilip Paeps * If removing the interface that gave the bridge its mac address, set 1164fe878019SPhilip Paeps * the mac address of the bridge to the address of the next member, or 1165fe878019SPhilip Paeps * to its default address if no members are left. 1166fe878019SPhilip Paeps */ 1167c5127526SHiroki Sato if (V_bridge_inherit_mac && sc->sc_ifaddr == ifs) { 1168dd00a42aSKristof Provost if (CK_LIST_EMPTY(&sc->sc_iflist)) { 11696b7e0c1cSKyle Evans bcopy(&sc->sc_defaddr, 1170fe878019SPhilip Paeps IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); 117166c84010SAndrew Thompson sc->sc_ifaddr = NULL; 117266c84010SAndrew Thompson } else { 1173dd00a42aSKristof Provost bifl = CK_LIST_FIRST(&sc->sc_iflist); 1174dd00a42aSKristof Provost fif = bifl->bif_ifp; 1175fe878019SPhilip Paeps bcopy(IF_LLADDR(fif), 1176fe878019SPhilip Paeps IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); 117766c84010SAndrew Thompson sc->sc_ifaddr = fif; 1178fe878019SPhilip Paeps } 1179ea4ca115SAndrew Thompson EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp); 1180fe878019SPhilip Paeps } 1181fe878019SPhilip Paeps 118208e34823SAndrew Thompson bridge_linkcheck(sc); 1183ec29c623SAndrew Thompson bridge_mutecaps(sc); /* recalcuate now this interface is removed */ 11844af1bd81SKristof Provost BRIDGE_RT_LOCK(sc); 118531997bf2SAndrew Thompson bridge_rtdelete(sc, ifs, IFBF_FLUSHALL); 11864af1bd81SKristof Provost BRIDGE_RT_UNLOCK(sc); 11875f33ec7bSAndrew Thompson KASSERT(bif->bif_addrcnt == 0, 11885f33ec7bSAndrew Thompson ("%s: %d bridge routes referenced", __func__, bif->bif_addrcnt)); 118931997bf2SAndrew Thompson 11905c30b378SMatt Macy ifs->if_bridge_output = NULL; 11915c30b378SMatt Macy ifs->if_bridge_input = NULL; 11925c30b378SMatt Macy ifs->if_bridge_linkstate = NULL; 11933f11aba7SAndrew Thompson if (!gone) { 11943f11aba7SAndrew Thompson switch (ifs->if_type) { 11953f11aba7SAndrew Thompson case IFT_ETHER: 11963f11aba7SAndrew Thompson case IFT_L2VLAN: 11973f11aba7SAndrew Thompson /* 1198581e6970SKristof Provost * Take the interface out of promiscuous mode, but only 1199581e6970SKristof Provost * if it was promiscuous in the first place. It might 1200581e6970SKristof Provost * not be if we're in the bridge_ioctl_add() error path. 12013f11aba7SAndrew Thompson */ 1202581e6970SKristof Provost if (ifs->if_flags & IFF_PROMISC) 12033f11aba7SAndrew Thompson (void) ifpromisc(ifs, 0); 12043f11aba7SAndrew Thompson break; 12053f11aba7SAndrew Thompson 12063f11aba7SAndrew Thompson case IFT_GIF: 12073f11aba7SAndrew Thompson break; 12083f11aba7SAndrew Thompson 12093f11aba7SAndrew Thompson default: 12103f11aba7SAndrew Thompson #ifdef DIAGNOSTIC 12113f11aba7SAndrew Thompson panic("bridge_delete_member: impossible"); 12123f11aba7SAndrew Thompson #endif 12133f11aba7SAndrew Thompson break; 12143f11aba7SAndrew Thompson } 12153f11aba7SAndrew Thompson /* reneable any interface capabilities */ 12163f11aba7SAndrew Thompson bridge_set_ifcap(sc, bif, bif->bif_savedcaps); 12173f11aba7SAndrew Thompson } 1218071fff62SAndrew Thompson bstp_destroy(&bif->bif_stp); /* prepare to free */ 1219fffd27e5SKristof Provost 1220fffd27e5SKristof Provost NET_EPOCH_CALL(bridge_delete_member_cb, &bif->bif_epoch_ctx); 122131997bf2SAndrew Thompson } 122231997bf2SAndrew Thompson 1223e0a87e8aSAndrew Thompson /* 1224e0a87e8aSAndrew Thompson * bridge_delete_span: 1225e0a87e8aSAndrew Thompson * 1226e0a87e8aSAndrew Thompson * Delete the specified span interface. 1227e0a87e8aSAndrew Thompson */ 1228e0a87e8aSAndrew Thompson static void 1229e0a87e8aSAndrew Thompson bridge_delete_span(struct bridge_softc *sc, struct bridge_iflist *bif) 1230e0a87e8aSAndrew Thompson { 1231e0a87e8aSAndrew Thompson BRIDGE_LOCK_ASSERT(sc); 1232e0a87e8aSAndrew Thompson 1233e0a87e8aSAndrew Thompson KASSERT(bif->bif_ifp->if_bridge == NULL, 1234e0a87e8aSAndrew Thompson ("%s: not a span interface", __func__)); 1235e0a87e8aSAndrew Thompson 1236dd00a42aSKristof Provost CK_LIST_REMOVE(bif, bif_next); 1237fffd27e5SKristof Provost 1238fffd27e5SKristof Provost NET_EPOCH_CALL(bridge_delete_member_cb, &bif->bif_epoch_ctx); 1239e0a87e8aSAndrew Thompson } 1240e0a87e8aSAndrew Thompson 12416b32f3d3SAndrew Thompson static int 124231997bf2SAndrew Thompson bridge_ioctl_add(struct bridge_softc *sc, void *arg) 124331997bf2SAndrew Thompson { 124431997bf2SAndrew Thompson struct ifbreq *req = arg; 124531997bf2SAndrew Thompson struct bridge_iflist *bif = NULL; 124631997bf2SAndrew Thompson struct ifnet *ifs; 124731997bf2SAndrew Thompson int error = 0; 124831997bf2SAndrew Thompson 124931997bf2SAndrew Thompson ifs = ifunit(req->ifbr_ifsname); 125031997bf2SAndrew Thompson if (ifs == NULL) 125131997bf2SAndrew Thompson return (ENOENT); 1252ec29c623SAndrew Thompson if (ifs->if_ioctl == NULL) /* must be supported */ 1253ec29c623SAndrew Thompson return (EINVAL); 125431997bf2SAndrew Thompson 125591f6764eSAndrew Thompson /* If it's in the span list, it can't be a member. */ 1256dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) 125791f6764eSAndrew Thompson if (ifs == bif->bif_ifp) 125891f6764eSAndrew Thompson return (EBUSY); 125991f6764eSAndrew Thompson 126031997bf2SAndrew Thompson if (ifs->if_bridge == sc) 126131997bf2SAndrew Thompson return (EEXIST); 126231997bf2SAndrew Thompson 126331997bf2SAndrew Thompson if (ifs->if_bridge != NULL) 126431997bf2SAndrew Thompson return (EBUSY); 126531997bf2SAndrew Thompson 126631997bf2SAndrew Thompson switch (ifs->if_type) { 126731997bf2SAndrew Thompson case IFT_ETHER: 126831997bf2SAndrew Thompson case IFT_L2VLAN: 126973ff045cSAndrew Thompson case IFT_GIF: 12703f11aba7SAndrew Thompson /* permitted interface types */ 127173ff045cSAndrew Thompson break; 127231997bf2SAndrew Thompson default: 1273af805644SHiroki Sato return (EINVAL); 127431997bf2SAndrew Thompson } 127531997bf2SAndrew Thompson 1276af805644SHiroki Sato #ifdef INET6 1277af805644SHiroki Sato /* 1278af805644SHiroki Sato * Two valid inet6 addresses with link-local scope must not be 1279af805644SHiroki Sato * on the parent interface and the member interfaces at the 1280af805644SHiroki Sato * same time. This restriction is needed to prevent violation 1281af805644SHiroki Sato * of link-local scope zone. Attempts to add a member 1282af805644SHiroki Sato * interface which has inet6 addresses when the parent has 1283af805644SHiroki Sato * inet6 triggers removal of all inet6 addresses on the member 1284af805644SHiroki Sato * interface. 1285af805644SHiroki Sato */ 1286af805644SHiroki Sato 1287af805644SHiroki Sato /* Check if the parent interface has a link-local scope addr. */ 12889fcd8e9eSHiroki Sato if (V_allow_llz_overlap == 0 && 12899fcd8e9eSHiroki Sato in6ifa_llaonifp(sc->sc_ifp) != NULL) { 1290af805644SHiroki Sato /* 1291af805644SHiroki Sato * If any, remove all inet6 addresses from the member 1292af805644SHiroki Sato * interfaces. 1293af805644SHiroki Sato */ 1294dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { 1295af805644SHiroki Sato if (in6ifa_llaonifp(bif->bif_ifp)) { 1296af805644SHiroki Sato in6_ifdetach(bif->bif_ifp); 1297af805644SHiroki Sato if_printf(sc->sc_ifp, 1298af805644SHiroki Sato "IPv6 addresses on %s have been removed " 1299af805644SHiroki Sato "before adding it as a member to prevent " 1300af805644SHiroki Sato "IPv6 address scope violation.\n", 1301af805644SHiroki Sato bif->bif_ifp->if_xname); 1302af805644SHiroki Sato } 1303af805644SHiroki Sato } 1304af805644SHiroki Sato if (in6ifa_llaonifp(ifs)) { 1305af805644SHiroki Sato in6_ifdetach(ifs); 1306af805644SHiroki Sato if_printf(sc->sc_ifp, 1307af805644SHiroki Sato "IPv6 addresses on %s have been removed " 1308af805644SHiroki Sato "before adding it as a member to prevent " 1309af805644SHiroki Sato "IPv6 address scope violation.\n", 1310af805644SHiroki Sato ifs->if_xname); 1311af805644SHiroki Sato } 1312af805644SHiroki Sato } 1313af805644SHiroki Sato #endif 1314c2a5f1a5SHiroki Sato /* Allow the first Ethernet member to define the MTU */ 1315dd00a42aSKristof Provost if (CK_LIST_EMPTY(&sc->sc_iflist)) 1316c2a5f1a5SHiroki Sato sc->sc_ifp->if_mtu = ifs->if_mtu; 1317c2a5f1a5SHiroki Sato else if (sc->sc_ifp->if_mtu != ifs->if_mtu) { 13181865ebfbSKristof Provost struct ifreq ifr; 13191865ebfbSKristof Provost 13201865ebfbSKristof Provost snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", 13211865ebfbSKristof Provost ifs->if_xname); 13221865ebfbSKristof Provost ifr.ifr_mtu = sc->sc_ifp->if_mtu; 13231865ebfbSKristof Provost 13241865ebfbSKristof Provost error = (*ifs->if_ioctl)(ifs, 13251865ebfbSKristof Provost SIOCSIFMTU, (caddr_t)&ifr); 13261865ebfbSKristof Provost if (error != 0) { 13271865ebfbSKristof Provost log(LOG_NOTICE, "%s: invalid MTU: %u for" 13281865ebfbSKristof Provost " new member %s\n", sc->sc_ifp->if_xname, 13291865ebfbSKristof Provost ifr.ifr_mtu, 13301865ebfbSKristof Provost ifs->if_xname); 1331af805644SHiroki Sato return (EINVAL); 1332c2a5f1a5SHiroki Sato } 13331865ebfbSKristof Provost } 1334c2a5f1a5SHiroki Sato 1335af805644SHiroki Sato bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO); 1336af805644SHiroki Sato if (bif == NULL) 1337af805644SHiroki Sato return (ENOMEM); 1338af805644SHiroki Sato 1339af805644SHiroki Sato bif->bif_ifp = ifs; 1340af805644SHiroki Sato bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER; 1341af805644SHiroki Sato bif->bif_savedcaps = ifs->if_capenable; 1342af805644SHiroki Sato 1343fe878019SPhilip Paeps /* 1344fe878019SPhilip Paeps * Assign the interface's MAC address to the bridge if it's the first 1345fe878019SPhilip Paeps * member and the MAC address of the bridge has not been changed from 1346fe878019SPhilip Paeps * the default randomly generated one. 1347fe878019SPhilip Paeps */ 1348dd00a42aSKristof Provost if (V_bridge_inherit_mac && CK_LIST_EMPTY(&sc->sc_iflist) && 13496b7e0c1cSKyle Evans !memcmp(IF_LLADDR(sc->sc_ifp), sc->sc_defaddr.octet, ETHER_ADDR_LEN)) { 1350fe878019SPhilip Paeps bcopy(IF_LLADDR(ifs), IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); 135166c84010SAndrew Thompson sc->sc_ifaddr = ifs; 1352ea4ca115SAndrew Thompson EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp); 135366c84010SAndrew Thompson } 1354fe878019SPhilip Paeps 135531997bf2SAndrew Thompson ifs->if_bridge = sc; 13565c30b378SMatt Macy ifs->if_bridge_output = bridge_output; 13575c30b378SMatt Macy ifs->if_bridge_input = bridge_input; 13585c30b378SMatt Macy ifs->if_bridge_linkstate = bridge_linkstate; 1359071fff62SAndrew Thompson bstp_create(&sc->sc_stp, &bif->bif_stp, bif->bif_ifp); 136031997bf2SAndrew Thompson /* 136131997bf2SAndrew Thompson * XXX: XLOCK HERE!?! 136231997bf2SAndrew Thompson * 136331997bf2SAndrew Thompson * NOTE: insert_***HEAD*** should be safe for the traversals. 136431997bf2SAndrew Thompson */ 1365dd00a42aSKristof Provost CK_LIST_INSERT_HEAD(&sc->sc_iflist, bif, bif_next); 136631997bf2SAndrew Thompson 1367ec29c623SAndrew Thompson /* Set interface capabilities to the intersection set of all members */ 1368ec29c623SAndrew Thompson bridge_mutecaps(sc); 136908e34823SAndrew Thompson bridge_linkcheck(sc); 13703f11aba7SAndrew Thompson 13717702d401SAndrew Thompson /* Place the interface into promiscuous mode */ 13723f11aba7SAndrew Thompson switch (ifs->if_type) { 13733f11aba7SAndrew Thompson case IFT_ETHER: 13743f11aba7SAndrew Thompson case IFT_L2VLAN: 13753f11aba7SAndrew Thompson error = ifpromisc(ifs, 1); 13763f11aba7SAndrew Thompson break; 13773f11aba7SAndrew Thompson } 1378af805644SHiroki Sato 1379581e6970SKristof Provost if (error) 1380af805644SHiroki Sato bridge_delete_member(sc, bif, 0); 138131997bf2SAndrew Thompson return (error); 138231997bf2SAndrew Thompson } 138331997bf2SAndrew Thompson 13846b32f3d3SAndrew Thompson static int 138531997bf2SAndrew Thompson bridge_ioctl_del(struct bridge_softc *sc, void *arg) 138631997bf2SAndrew Thompson { 138731997bf2SAndrew Thompson struct ifbreq *req = arg; 138831997bf2SAndrew Thompson struct bridge_iflist *bif; 138931997bf2SAndrew Thompson 139031997bf2SAndrew Thompson bif = bridge_lookup_member(sc, req->ifbr_ifsname); 139131997bf2SAndrew Thompson if (bif == NULL) 139231997bf2SAndrew Thompson return (ENOENT); 139331997bf2SAndrew Thompson 13941a266137SAndrew Thompson bridge_delete_member(sc, bif, 0); 139531997bf2SAndrew Thompson 139631997bf2SAndrew Thompson return (0); 139731997bf2SAndrew Thompson } 139831997bf2SAndrew Thompson 13996b32f3d3SAndrew Thompson static int 140031997bf2SAndrew Thompson bridge_ioctl_gifflags(struct bridge_softc *sc, void *arg) 140131997bf2SAndrew Thompson { 140231997bf2SAndrew Thompson struct ifbreq *req = arg; 140331997bf2SAndrew Thompson struct bridge_iflist *bif; 14043fab7669SAndrew Thompson struct bstp_port *bp; 140531997bf2SAndrew Thompson 140631997bf2SAndrew Thompson bif = bridge_lookup_member(sc, req->ifbr_ifsname); 140731997bf2SAndrew Thompson if (bif == NULL) 140831997bf2SAndrew Thompson return (ENOENT); 140931997bf2SAndrew Thompson 14103fab7669SAndrew Thompson bp = &bif->bif_stp; 141131997bf2SAndrew Thompson req->ifbr_ifsflags = bif->bif_flags; 14123fab7669SAndrew Thompson req->ifbr_state = bp->bp_state; 14133fab7669SAndrew Thompson req->ifbr_priority = bp->bp_priority; 14143fab7669SAndrew Thompson req->ifbr_path_cost = bp->bp_path_cost; 14153fab7669SAndrew Thompson req->ifbr_portno = bif->bif_ifp->if_index & 0xfff; 14163fab7669SAndrew Thompson req->ifbr_proto = bp->bp_protover; 14173fab7669SAndrew Thompson req->ifbr_role = bp->bp_role; 14183fab7669SAndrew Thompson req->ifbr_stpflags = bp->bp_flags; 14195f33ec7bSAndrew Thompson req->ifbr_addrcnt = bif->bif_addrcnt; 14205f33ec7bSAndrew Thompson req->ifbr_addrmax = bif->bif_addrmax; 14215f33ec7bSAndrew Thompson req->ifbr_addrexceeded = bif->bif_addrexceeded; 142231997bf2SAndrew Thompson 14236c32e05cSAndrew Thompson /* Copy STP state options as flags */ 14246c32e05cSAndrew Thompson if (bp->bp_operedge) 14256c32e05cSAndrew Thompson req->ifbr_ifsflags |= IFBIF_BSTP_EDGE; 14266c32e05cSAndrew Thompson if (bp->bp_flags & BSTP_PORT_AUTOEDGE) 14276c32e05cSAndrew Thompson req->ifbr_ifsflags |= IFBIF_BSTP_AUTOEDGE; 142878709605SAndrew Thompson if (bp->bp_ptp_link) 142978709605SAndrew Thompson req->ifbr_ifsflags |= IFBIF_BSTP_PTP; 143078709605SAndrew Thompson if (bp->bp_flags & BSTP_PORT_AUTOPTP) 143178709605SAndrew Thompson req->ifbr_ifsflags |= IFBIF_BSTP_AUTOPTP; 1432daacddcaSShteryana Shopova if (bp->bp_flags & BSTP_PORT_ADMEDGE) 1433daacddcaSShteryana Shopova req->ifbr_ifsflags |= IFBIF_BSTP_ADMEDGE; 1434daacddcaSShteryana Shopova if (bp->bp_flags & BSTP_PORT_ADMCOST) 1435daacddcaSShteryana Shopova req->ifbr_ifsflags |= IFBIF_BSTP_ADMCOST; 143631997bf2SAndrew Thompson return (0); 143731997bf2SAndrew Thompson } 143831997bf2SAndrew Thompson 14396b32f3d3SAndrew Thompson static int 144031997bf2SAndrew Thompson bridge_ioctl_sifflags(struct bridge_softc *sc, void *arg) 144131997bf2SAndrew Thompson { 144289fa9c34SKristof Provost struct epoch_tracker et; 144331997bf2SAndrew Thompson struct ifbreq *req = arg; 144431997bf2SAndrew Thompson struct bridge_iflist *bif; 14456c32e05cSAndrew Thompson struct bstp_port *bp; 144696e47153SAndrew Thompson int error; 144731997bf2SAndrew Thompson 144831997bf2SAndrew Thompson bif = bridge_lookup_member(sc, req->ifbr_ifsname); 144931997bf2SAndrew Thompson if (bif == NULL) 145031997bf2SAndrew Thompson return (ENOENT); 14516c32e05cSAndrew Thompson bp = &bif->bif_stp; 145231997bf2SAndrew Thompson 145391f6764eSAndrew Thompson if (req->ifbr_ifsflags & IFBIF_SPAN) 145491f6764eSAndrew Thompson /* SPAN is readonly */ 145591f6764eSAndrew Thompson return (EINVAL); 145691f6764eSAndrew Thompson 145789fa9c34SKristof Provost NET_EPOCH_ENTER(et); 145889fa9c34SKristof Provost 145931997bf2SAndrew Thompson if (req->ifbr_ifsflags & IFBIF_STP) { 146096e47153SAndrew Thompson if ((bif->bif_flags & IFBIF_STP) == 0) { 1461071fff62SAndrew Thompson error = bstp_enable(&bif->bif_stp); 146289fa9c34SKristof Provost if (error) { 146389fa9c34SKristof Provost NET_EPOCH_EXIT(et); 146496e47153SAndrew Thompson return (error); 146531997bf2SAndrew Thompson } 146689fa9c34SKristof Provost } 146796e47153SAndrew Thompson } else { 146896e47153SAndrew Thompson if ((bif->bif_flags & IFBIF_STP) != 0) 1469071fff62SAndrew Thompson bstp_disable(&bif->bif_stp); 147031997bf2SAndrew Thompson } 147131997bf2SAndrew Thompson 14726c32e05cSAndrew Thompson /* Pass on STP flags */ 14736c32e05cSAndrew Thompson bstp_set_edge(bp, req->ifbr_ifsflags & IFBIF_BSTP_EDGE ? 1 : 0); 14746c32e05cSAndrew Thompson bstp_set_autoedge(bp, req->ifbr_ifsflags & IFBIF_BSTP_AUTOEDGE ? 1 : 0); 147578709605SAndrew Thompson bstp_set_ptp(bp, req->ifbr_ifsflags & IFBIF_BSTP_PTP ? 1 : 0); 147678709605SAndrew Thompson bstp_set_autoptp(bp, req->ifbr_ifsflags & IFBIF_BSTP_AUTOPTP ? 1 : 0); 14776c32e05cSAndrew Thompson 14786c32e05cSAndrew Thompson /* Save the bits relating to the bridge */ 14796c32e05cSAndrew Thompson bif->bif_flags = req->ifbr_ifsflags & IFBIFMASK; 148031997bf2SAndrew Thompson 148189fa9c34SKristof Provost NET_EPOCH_EXIT(et); 148289fa9c34SKristof Provost 148331997bf2SAndrew Thompson return (0); 148431997bf2SAndrew Thompson } 148531997bf2SAndrew Thompson 14866b32f3d3SAndrew Thompson static int 148731997bf2SAndrew Thompson bridge_ioctl_scache(struct bridge_softc *sc, void *arg) 148831997bf2SAndrew Thompson { 148931997bf2SAndrew Thompson struct ifbrparam *param = arg; 149031997bf2SAndrew Thompson 149131997bf2SAndrew Thompson sc->sc_brtmax = param->ifbrp_csize; 149231997bf2SAndrew Thompson bridge_rttrim(sc); 149331997bf2SAndrew Thompson 149431997bf2SAndrew Thompson return (0); 149531997bf2SAndrew Thompson } 149631997bf2SAndrew Thompson 14976b32f3d3SAndrew Thompson static int 149831997bf2SAndrew Thompson bridge_ioctl_gcache(struct bridge_softc *sc, void *arg) 149931997bf2SAndrew Thompson { 150031997bf2SAndrew Thompson struct ifbrparam *param = arg; 150131997bf2SAndrew Thompson 150231997bf2SAndrew Thompson param->ifbrp_csize = sc->sc_brtmax; 150331997bf2SAndrew Thompson 150431997bf2SAndrew Thompson return (0); 150531997bf2SAndrew Thompson } 150631997bf2SAndrew Thompson 15076b32f3d3SAndrew Thompson static int 150831997bf2SAndrew Thompson bridge_ioctl_gifs(struct bridge_softc *sc, void *arg) 150931997bf2SAndrew Thompson { 151031997bf2SAndrew Thompson struct ifbifconf *bifc = arg; 151131997bf2SAndrew Thompson struct bridge_iflist *bif; 151231997bf2SAndrew Thompson struct ifbreq breq; 151382056f42SAndrew Thompson char *buf, *outbuf; 151482056f42SAndrew Thompson int count, buflen, len, error = 0; 151531997bf2SAndrew Thompson 151631997bf2SAndrew Thompson count = 0; 1517dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) 151831997bf2SAndrew Thompson count++; 1519dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) 152091f6764eSAndrew Thompson count++; 152131997bf2SAndrew Thompson 152282056f42SAndrew Thompson buflen = sizeof(breq) * count; 152331997bf2SAndrew Thompson if (bifc->ifbic_len == 0) { 152482056f42SAndrew Thompson bifc->ifbic_len = buflen; 152531997bf2SAndrew Thompson return (0); 152631997bf2SAndrew Thompson } 152793ed6adeSKristof Provost outbuf = malloc(buflen, M_TEMP, M_NOWAIT | M_ZERO); 152893ed6adeSKristof Provost if (outbuf == NULL) 152993ed6adeSKristof Provost return (ENOMEM); 153031997bf2SAndrew Thompson 153131997bf2SAndrew Thompson count = 0; 153282056f42SAndrew Thompson buf = outbuf; 153382056f42SAndrew Thompson len = min(bifc->ifbic_len, buflen); 1534dc1b1b7bSAndrew Thompson bzero(&breq, sizeof(breq)); 1535dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { 153631997bf2SAndrew Thompson if (len < sizeof(breq)) 153731997bf2SAndrew Thompson break; 153831997bf2SAndrew Thompson 153931997bf2SAndrew Thompson strlcpy(breq.ifbr_ifsname, bif->bif_ifp->if_xname, 154031997bf2SAndrew Thompson sizeof(breq.ifbr_ifsname)); 15416c32e05cSAndrew Thompson /* Fill in the ifbreq structure */ 15426c32e05cSAndrew Thompson error = bridge_ioctl_gifflags(sc, &breq); 15436c32e05cSAndrew Thompson if (error) 15446c32e05cSAndrew Thompson break; 154582056f42SAndrew Thompson memcpy(buf, &breq, sizeof(breq)); 154631997bf2SAndrew Thompson count++; 154782056f42SAndrew Thompson buf += sizeof(breq); 154831997bf2SAndrew Thompson len -= sizeof(breq); 154931997bf2SAndrew Thompson } 1550dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) { 155191f6764eSAndrew Thompson if (len < sizeof(breq)) 155291f6764eSAndrew Thompson break; 155391f6764eSAndrew Thompson 155491f6764eSAndrew Thompson strlcpy(breq.ifbr_ifsname, bif->bif_ifp->if_xname, 155591f6764eSAndrew Thompson sizeof(breq.ifbr_ifsname)); 155691f6764eSAndrew Thompson breq.ifbr_ifsflags = bif->bif_flags; 15573fab7669SAndrew Thompson breq.ifbr_portno = bif->bif_ifp->if_index & 0xfff; 155882056f42SAndrew Thompson memcpy(buf, &breq, sizeof(breq)); 155991f6764eSAndrew Thompson count++; 156082056f42SAndrew Thompson buf += sizeof(breq); 156191f6764eSAndrew Thompson len -= sizeof(breq); 156291f6764eSAndrew Thompson } 156331997bf2SAndrew Thompson 156431997bf2SAndrew Thompson bifc->ifbic_len = sizeof(breq) * count; 156582056f42SAndrew Thompson error = copyout(outbuf, bifc->ifbic_req, bifc->ifbic_len); 156682056f42SAndrew Thompson free(outbuf, M_TEMP); 156731997bf2SAndrew Thompson return (error); 156831997bf2SAndrew Thompson } 156931997bf2SAndrew Thompson 15706b32f3d3SAndrew Thompson static int 157131997bf2SAndrew Thompson bridge_ioctl_rts(struct bridge_softc *sc, void *arg) 157231997bf2SAndrew Thompson { 157331997bf2SAndrew Thompson struct ifbaconf *bac = arg; 157431997bf2SAndrew Thompson struct bridge_rtnode *brt; 157531997bf2SAndrew Thompson struct ifbareq bareq; 157682056f42SAndrew Thompson char *buf, *outbuf; 157782056f42SAndrew Thompson int count, buflen, len, error = 0; 157831997bf2SAndrew Thompson 157931997bf2SAndrew Thompson if (bac->ifbac_len == 0) 158031997bf2SAndrew Thompson return (0); 158131997bf2SAndrew Thompson 158282056f42SAndrew Thompson count = 0; 1583dd00a42aSKristof Provost CK_LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) 158482056f42SAndrew Thompson count++; 158582056f42SAndrew Thompson buflen = sizeof(bareq) * count; 158682056f42SAndrew Thompson 158793ed6adeSKristof Provost outbuf = malloc(buflen, M_TEMP, M_NOWAIT | M_ZERO); 158893ed6adeSKristof Provost if (outbuf == NULL) 158993ed6adeSKristof Provost return (ENOMEM); 159082056f42SAndrew Thompson 159182056f42SAndrew Thompson count = 0; 159282056f42SAndrew Thompson buf = outbuf; 159382056f42SAndrew Thompson len = min(bac->ifbac_len, buflen); 1594dc1b1b7bSAndrew Thompson bzero(&bareq, sizeof(bareq)); 1595dd00a42aSKristof Provost CK_LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { 159631997bf2SAndrew Thompson if (len < sizeof(bareq)) 159731997bf2SAndrew Thompson goto out; 159831997bf2SAndrew Thompson strlcpy(bareq.ifba_ifsname, brt->brt_ifp->if_xname, 159931997bf2SAndrew Thompson sizeof(bareq.ifba_ifsname)); 160031997bf2SAndrew Thompson memcpy(bareq.ifba_dst, brt->brt_addr, sizeof(brt->brt_addr)); 160122dcc3c1SAndrew Thompson bareq.ifba_vlan = brt->brt_vlan; 160231997bf2SAndrew Thompson if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC && 1603bc9f74c7SAndrew Thompson time_uptime < brt->brt_expire) 1604bc9f74c7SAndrew Thompson bareq.ifba_expire = brt->brt_expire - time_uptime; 160531997bf2SAndrew Thompson else 160631997bf2SAndrew Thompson bareq.ifba_expire = 0; 160731997bf2SAndrew Thompson bareq.ifba_flags = brt->brt_flags; 160831997bf2SAndrew Thompson 160982056f42SAndrew Thompson memcpy(buf, &bareq, sizeof(bareq)); 161031997bf2SAndrew Thompson count++; 161182056f42SAndrew Thompson buf += sizeof(bareq); 161231997bf2SAndrew Thompson len -= sizeof(bareq); 161331997bf2SAndrew Thompson } 161431997bf2SAndrew Thompson out: 161531997bf2SAndrew Thompson bac->ifbac_len = sizeof(bareq) * count; 161682056f42SAndrew Thompson error = copyout(outbuf, bac->ifbac_req, bac->ifbac_len); 161782056f42SAndrew Thompson free(outbuf, M_TEMP); 161831997bf2SAndrew Thompson return (error); 161931997bf2SAndrew Thompson } 162031997bf2SAndrew Thompson 16216b32f3d3SAndrew Thompson static int 162231997bf2SAndrew Thompson bridge_ioctl_saddr(struct bridge_softc *sc, void *arg) 162331997bf2SAndrew Thompson { 162431997bf2SAndrew Thompson struct ifbareq *req = arg; 162531997bf2SAndrew Thompson struct bridge_iflist *bif; 16264af1bd81SKristof Provost struct epoch_tracker et; 162731997bf2SAndrew Thompson int error; 162831997bf2SAndrew Thompson 16294af1bd81SKristof Provost NET_EPOCH_ENTER(et); 163031997bf2SAndrew Thompson bif = bridge_lookup_member(sc, req->ifba_ifsname); 16314af1bd81SKristof Provost if (bif == NULL) { 16324af1bd81SKristof Provost NET_EPOCH_EXIT(et); 163331997bf2SAndrew Thompson return (ENOENT); 16344af1bd81SKristof Provost } 163531997bf2SAndrew Thompson 1636fffd27e5SKristof Provost /* bridge_rtupdate() may acquire the lock. */ 163722dcc3c1SAndrew Thompson error = bridge_rtupdate(sc, req->ifba_dst, req->ifba_vlan, bif, 1, 163831997bf2SAndrew Thompson req->ifba_flags); 16394af1bd81SKristof Provost NET_EPOCH_EXIT(et); 164031997bf2SAndrew Thompson 164131997bf2SAndrew Thompson return (error); 164231997bf2SAndrew Thompson } 164331997bf2SAndrew Thompson 16446b32f3d3SAndrew Thompson static int 164531997bf2SAndrew Thompson bridge_ioctl_sto(struct bridge_softc *sc, void *arg) 164631997bf2SAndrew Thompson { 164731997bf2SAndrew Thompson struct ifbrparam *param = arg; 164831997bf2SAndrew Thompson 164931997bf2SAndrew Thompson sc->sc_brttimeout = param->ifbrp_ctime; 165031997bf2SAndrew Thompson return (0); 165131997bf2SAndrew Thompson } 165231997bf2SAndrew Thompson 16536b32f3d3SAndrew Thompson static int 165431997bf2SAndrew Thompson bridge_ioctl_gto(struct bridge_softc *sc, void *arg) 165531997bf2SAndrew Thompson { 165631997bf2SAndrew Thompson struct ifbrparam *param = arg; 165731997bf2SAndrew Thompson 165831997bf2SAndrew Thompson param->ifbrp_ctime = sc->sc_brttimeout; 165931997bf2SAndrew Thompson return (0); 166031997bf2SAndrew Thompson } 166131997bf2SAndrew Thompson 16626b32f3d3SAndrew Thompson static int 166331997bf2SAndrew Thompson bridge_ioctl_daddr(struct bridge_softc *sc, void *arg) 166431997bf2SAndrew Thompson { 166531997bf2SAndrew Thompson struct ifbareq *req = arg; 1666b0e38a13SKristof Provost int vlan = req->ifba_vlan; 166731997bf2SAndrew Thompson 1668b0e38a13SKristof Provost /* Userspace uses '0' to mean 'any vlan' */ 1669b0e38a13SKristof Provost if (vlan == 0) 1670b0e38a13SKristof Provost vlan = DOT1Q_VID_RSVD_IMPL; 1671b0e38a13SKristof Provost 1672b0e38a13SKristof Provost return (bridge_rtdaddr(sc, req->ifba_dst, vlan)); 167331997bf2SAndrew Thompson } 167431997bf2SAndrew Thompson 16756b32f3d3SAndrew Thompson static int 167631997bf2SAndrew Thompson bridge_ioctl_flush(struct bridge_softc *sc, void *arg) 167731997bf2SAndrew Thompson { 167831997bf2SAndrew Thompson struct ifbreq *req = arg; 167931997bf2SAndrew Thompson 16804af1bd81SKristof Provost BRIDGE_RT_LOCK(sc); 168131997bf2SAndrew Thompson bridge_rtflush(sc, req->ifbr_ifsflags); 16824af1bd81SKristof Provost BRIDGE_RT_UNLOCK(sc); 16834af1bd81SKristof Provost 168431997bf2SAndrew Thompson return (0); 168531997bf2SAndrew Thompson } 168631997bf2SAndrew Thompson 16876b32f3d3SAndrew Thompson static int 168831997bf2SAndrew Thompson bridge_ioctl_gpri(struct bridge_softc *sc, void *arg) 168931997bf2SAndrew Thompson { 169031997bf2SAndrew Thompson struct ifbrparam *param = arg; 169196e47153SAndrew Thompson struct bstp_state *bs = &sc->sc_stp; 169231997bf2SAndrew Thompson 169396e47153SAndrew Thompson param->ifbrp_prio = bs->bs_bridge_priority; 169431997bf2SAndrew Thompson return (0); 169531997bf2SAndrew Thompson } 169631997bf2SAndrew Thompson 16976b32f3d3SAndrew Thompson static int 169831997bf2SAndrew Thompson bridge_ioctl_spri(struct bridge_softc *sc, void *arg) 169931997bf2SAndrew Thompson { 170031997bf2SAndrew Thompson struct ifbrparam *param = arg; 170131997bf2SAndrew Thompson 17023fab7669SAndrew Thompson return (bstp_set_priority(&sc->sc_stp, param->ifbrp_prio)); 170331997bf2SAndrew Thompson } 170431997bf2SAndrew Thompson 17056b32f3d3SAndrew Thompson static int 170631997bf2SAndrew Thompson bridge_ioctl_ght(struct bridge_softc *sc, void *arg) 170731997bf2SAndrew Thompson { 170831997bf2SAndrew Thompson struct ifbrparam *param = arg; 170996e47153SAndrew Thompson struct bstp_state *bs = &sc->sc_stp; 171031997bf2SAndrew Thompson 17113fab7669SAndrew Thompson param->ifbrp_hellotime = bs->bs_bridge_htime >> 8; 171231997bf2SAndrew Thompson return (0); 171331997bf2SAndrew Thompson } 171431997bf2SAndrew Thompson 17156b32f3d3SAndrew Thompson static int 171631997bf2SAndrew Thompson bridge_ioctl_sht(struct bridge_softc *sc, void *arg) 171731997bf2SAndrew Thompson { 171831997bf2SAndrew Thompson struct ifbrparam *param = arg; 171931997bf2SAndrew Thompson 17203fab7669SAndrew Thompson return (bstp_set_htime(&sc->sc_stp, param->ifbrp_hellotime)); 172131997bf2SAndrew Thompson } 172231997bf2SAndrew Thompson 17236b32f3d3SAndrew Thompson static int 172431997bf2SAndrew Thompson bridge_ioctl_gfd(struct bridge_softc *sc, void *arg) 172531997bf2SAndrew Thompson { 172631997bf2SAndrew Thompson struct ifbrparam *param = arg; 172796e47153SAndrew Thompson struct bstp_state *bs = &sc->sc_stp; 172831997bf2SAndrew Thompson 17293fab7669SAndrew Thompson param->ifbrp_fwddelay = bs->bs_bridge_fdelay >> 8; 173031997bf2SAndrew Thompson return (0); 173131997bf2SAndrew Thompson } 173231997bf2SAndrew Thompson 17336b32f3d3SAndrew Thompson static int 173431997bf2SAndrew Thompson bridge_ioctl_sfd(struct bridge_softc *sc, void *arg) 173531997bf2SAndrew Thompson { 173631997bf2SAndrew Thompson struct ifbrparam *param = arg; 173731997bf2SAndrew Thompson 17383fab7669SAndrew Thompson return (bstp_set_fdelay(&sc->sc_stp, param->ifbrp_fwddelay)); 173931997bf2SAndrew Thompson } 174031997bf2SAndrew Thompson 17416b32f3d3SAndrew Thompson static int 174231997bf2SAndrew Thompson bridge_ioctl_gma(struct bridge_softc *sc, void *arg) 174331997bf2SAndrew Thompson { 174431997bf2SAndrew Thompson struct ifbrparam *param = arg; 174596e47153SAndrew Thompson struct bstp_state *bs = &sc->sc_stp; 174631997bf2SAndrew Thompson 174796e47153SAndrew Thompson param->ifbrp_maxage = bs->bs_bridge_max_age >> 8; 174831997bf2SAndrew Thompson return (0); 174931997bf2SAndrew Thompson } 175031997bf2SAndrew Thompson 17516b32f3d3SAndrew Thompson static int 175231997bf2SAndrew Thompson bridge_ioctl_sma(struct bridge_softc *sc, void *arg) 175331997bf2SAndrew Thompson { 175431997bf2SAndrew Thompson struct ifbrparam *param = arg; 175531997bf2SAndrew Thompson 17563fab7669SAndrew Thompson return (bstp_set_maxage(&sc->sc_stp, param->ifbrp_maxage)); 175731997bf2SAndrew Thompson } 175831997bf2SAndrew Thompson 17596b32f3d3SAndrew Thompson static int 176031997bf2SAndrew Thompson bridge_ioctl_sifprio(struct bridge_softc *sc, void *arg) 176131997bf2SAndrew Thompson { 176231997bf2SAndrew Thompson struct ifbreq *req = arg; 176331997bf2SAndrew Thompson struct bridge_iflist *bif; 176431997bf2SAndrew Thompson 176531997bf2SAndrew Thompson bif = bridge_lookup_member(sc, req->ifbr_ifsname); 176631997bf2SAndrew Thompson if (bif == NULL) 176731997bf2SAndrew Thompson return (ENOENT); 176831997bf2SAndrew Thompson 17693fab7669SAndrew Thompson return (bstp_set_port_priority(&bif->bif_stp, req->ifbr_priority)); 177031997bf2SAndrew Thompson } 177131997bf2SAndrew Thompson 17726b32f3d3SAndrew Thompson static int 177331997bf2SAndrew Thompson bridge_ioctl_sifcost(struct bridge_softc *sc, void *arg) 177431997bf2SAndrew Thompson { 177531997bf2SAndrew Thompson struct ifbreq *req = arg; 177631997bf2SAndrew Thompson struct bridge_iflist *bif; 177731997bf2SAndrew Thompson 177831997bf2SAndrew Thompson bif = bridge_lookup_member(sc, req->ifbr_ifsname); 177931997bf2SAndrew Thompson if (bif == NULL) 178031997bf2SAndrew Thompson return (ENOENT); 178131997bf2SAndrew Thompson 17823fab7669SAndrew Thompson return (bstp_set_path_cost(&bif->bif_stp, req->ifbr_path_cost)); 178331997bf2SAndrew Thompson } 178431997bf2SAndrew Thompson 178591f6764eSAndrew Thompson static int 17865f33ec7bSAndrew Thompson bridge_ioctl_sifmaxaddr(struct bridge_softc *sc, void *arg) 17875f33ec7bSAndrew Thompson { 17885f33ec7bSAndrew Thompson struct ifbreq *req = arg; 17895f33ec7bSAndrew Thompson struct bridge_iflist *bif; 17905f33ec7bSAndrew Thompson 17915f33ec7bSAndrew Thompson bif = bridge_lookup_member(sc, req->ifbr_ifsname); 17925f33ec7bSAndrew Thompson if (bif == NULL) 17935f33ec7bSAndrew Thompson return (ENOENT); 17945f33ec7bSAndrew Thompson 17955f33ec7bSAndrew Thompson bif->bif_addrmax = req->ifbr_addrmax; 17965f33ec7bSAndrew Thompson return (0); 17975f33ec7bSAndrew Thompson } 17985f33ec7bSAndrew Thompson 17995f33ec7bSAndrew Thompson static int 180091f6764eSAndrew Thompson bridge_ioctl_addspan(struct bridge_softc *sc, void *arg) 180191f6764eSAndrew Thompson { 180291f6764eSAndrew Thompson struct ifbreq *req = arg; 180391f6764eSAndrew Thompson struct bridge_iflist *bif = NULL; 180491f6764eSAndrew Thompson struct ifnet *ifs; 180591f6764eSAndrew Thompson 180691f6764eSAndrew Thompson ifs = ifunit(req->ifbr_ifsname); 180791f6764eSAndrew Thompson if (ifs == NULL) 180891f6764eSAndrew Thompson return (ENOENT); 180991f6764eSAndrew Thompson 1810dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) 181191f6764eSAndrew Thompson if (ifs == bif->bif_ifp) 181291f6764eSAndrew Thompson return (EBUSY); 181391f6764eSAndrew Thompson 181491f6764eSAndrew Thompson if (ifs->if_bridge != NULL) 181591f6764eSAndrew Thompson return (EBUSY); 181691f6764eSAndrew Thompson 181791f6764eSAndrew Thompson switch (ifs->if_type) { 181891f6764eSAndrew Thompson case IFT_ETHER: 1819690d7938SAndrew Thompson case IFT_GIF: 182091f6764eSAndrew Thompson case IFT_L2VLAN: 182191f6764eSAndrew Thompson break; 182291f6764eSAndrew Thompson default: 182391f6764eSAndrew Thompson return (EINVAL); 182491f6764eSAndrew Thompson } 182591f6764eSAndrew Thompson 182691f6764eSAndrew Thompson bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO); 182791f6764eSAndrew Thompson if (bif == NULL) 182891f6764eSAndrew Thompson return (ENOMEM); 182991f6764eSAndrew Thompson 183091f6764eSAndrew Thompson bif->bif_ifp = ifs; 183191f6764eSAndrew Thompson bif->bif_flags = IFBIF_SPAN; 183291f6764eSAndrew Thompson 1833dd00a42aSKristof Provost CK_LIST_INSERT_HEAD(&sc->sc_spanlist, bif, bif_next); 183491f6764eSAndrew Thompson 183591f6764eSAndrew Thompson return (0); 183691f6764eSAndrew Thompson } 183791f6764eSAndrew Thompson 183891f6764eSAndrew Thompson static int 183991f6764eSAndrew Thompson bridge_ioctl_delspan(struct bridge_softc *sc, void *arg) 184091f6764eSAndrew Thompson { 184191f6764eSAndrew Thompson struct ifbreq *req = arg; 184291f6764eSAndrew Thompson struct bridge_iflist *bif; 184391f6764eSAndrew Thompson struct ifnet *ifs; 184491f6764eSAndrew Thompson 184591f6764eSAndrew Thompson ifs = ifunit(req->ifbr_ifsname); 184691f6764eSAndrew Thompson if (ifs == NULL) 184791f6764eSAndrew Thompson return (ENOENT); 184891f6764eSAndrew Thompson 1849dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) 185091f6764eSAndrew Thompson if (ifs == bif->bif_ifp) 185191f6764eSAndrew Thompson break; 185291f6764eSAndrew Thompson 185391f6764eSAndrew Thompson if (bif == NULL) 185491f6764eSAndrew Thompson return (ENOENT); 185591f6764eSAndrew Thompson 1856e0a87e8aSAndrew Thompson bridge_delete_span(sc, bif); 185791f6764eSAndrew Thompson 185891f6764eSAndrew Thompson return (0); 185991f6764eSAndrew Thompson } 186091f6764eSAndrew Thompson 186151383c37SAndrew Thompson static int 186251383c37SAndrew Thompson bridge_ioctl_gbparam(struct bridge_softc *sc, void *arg) 186351383c37SAndrew Thompson { 186451383c37SAndrew Thompson struct ifbropreq *req = arg; 18653fab7669SAndrew Thompson struct bstp_state *bs = &sc->sc_stp; 186651383c37SAndrew Thompson struct bstp_port *root_port; 186751383c37SAndrew Thompson 18683fab7669SAndrew Thompson req->ifbop_maxage = bs->bs_bridge_max_age >> 8; 18693fab7669SAndrew Thompson req->ifbop_hellotime = bs->bs_bridge_htime >> 8; 18703fab7669SAndrew Thompson req->ifbop_fwddelay = bs->bs_bridge_fdelay >> 8; 187151383c37SAndrew Thompson 18723fab7669SAndrew Thompson root_port = bs->bs_root_port; 187351383c37SAndrew Thompson if (root_port == NULL) 187451383c37SAndrew Thompson req->ifbop_root_port = 0; 187551383c37SAndrew Thompson else 187651383c37SAndrew Thompson req->ifbop_root_port = root_port->bp_ifp->if_index; 187751383c37SAndrew Thompson 18783fab7669SAndrew Thompson req->ifbop_holdcount = bs->bs_txholdcount; 18793fab7669SAndrew Thompson req->ifbop_priority = bs->bs_bridge_priority; 18803fab7669SAndrew Thompson req->ifbop_protocol = bs->bs_protover; 18813fab7669SAndrew Thompson req->ifbop_root_path_cost = bs->bs_root_pv.pv_cost; 18826c32e05cSAndrew Thompson req->ifbop_bridgeid = bs->bs_bridge_pv.pv_dbridge_id; 18833fab7669SAndrew Thompson req->ifbop_designated_root = bs->bs_root_pv.pv_root_id; 18846c32e05cSAndrew Thompson req->ifbop_designated_bridge = bs->bs_root_pv.pv_dbridge_id; 18853fab7669SAndrew Thompson req->ifbop_last_tc_time.tv_sec = bs->bs_last_tc_time.tv_sec; 18863fab7669SAndrew Thompson req->ifbop_last_tc_time.tv_usec = bs->bs_last_tc_time.tv_usec; 188751383c37SAndrew Thompson 188851383c37SAndrew Thompson return (0); 188951383c37SAndrew Thompson } 189051383c37SAndrew Thompson 189151383c37SAndrew Thompson static int 189251383c37SAndrew Thompson bridge_ioctl_grte(struct bridge_softc *sc, void *arg) 189351383c37SAndrew Thompson { 189451383c37SAndrew Thompson struct ifbrparam *param = arg; 189551383c37SAndrew Thompson 189651383c37SAndrew Thompson param->ifbrp_cexceeded = sc->sc_brtexceeded; 189751383c37SAndrew Thompson return (0); 189851383c37SAndrew Thompson } 189951383c37SAndrew Thompson 190051383c37SAndrew Thompson static int 190151383c37SAndrew Thompson bridge_ioctl_gifsstp(struct bridge_softc *sc, void *arg) 190251383c37SAndrew Thompson { 190351383c37SAndrew Thompson struct ifbpstpconf *bifstp = arg; 190451383c37SAndrew Thompson struct bridge_iflist *bif; 19053fab7669SAndrew Thompson struct bstp_port *bp; 190651383c37SAndrew Thompson struct ifbpstpreq bpreq; 190782056f42SAndrew Thompson char *buf, *outbuf; 190882056f42SAndrew Thompson int count, buflen, len, error = 0; 190951383c37SAndrew Thompson 191051383c37SAndrew Thompson count = 0; 1911dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { 191251383c37SAndrew Thompson if ((bif->bif_flags & IFBIF_STP) != 0) 191351383c37SAndrew Thompson count++; 191451383c37SAndrew Thompson } 191551383c37SAndrew Thompson 191682056f42SAndrew Thompson buflen = sizeof(bpreq) * count; 191751383c37SAndrew Thompson if (bifstp->ifbpstp_len == 0) { 191882056f42SAndrew Thompson bifstp->ifbpstp_len = buflen; 191951383c37SAndrew Thompson return (0); 192051383c37SAndrew Thompson } 192151383c37SAndrew Thompson 192293ed6adeSKristof Provost outbuf = malloc(buflen, M_TEMP, M_NOWAIT | M_ZERO); 192393ed6adeSKristof Provost if (outbuf == NULL) 192493ed6adeSKristof Provost return (ENOMEM); 192582056f42SAndrew Thompson 192651383c37SAndrew Thompson count = 0; 192782056f42SAndrew Thompson buf = outbuf; 192882056f42SAndrew Thompson len = min(bifstp->ifbpstp_len, buflen); 192951383c37SAndrew Thompson bzero(&bpreq, sizeof(bpreq)); 1930dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { 193151383c37SAndrew Thompson if (len < sizeof(bpreq)) 193251383c37SAndrew Thompson break; 193351383c37SAndrew Thompson 193451383c37SAndrew Thompson if ((bif->bif_flags & IFBIF_STP) == 0) 193551383c37SAndrew Thompson continue; 193651383c37SAndrew Thompson 19373fab7669SAndrew Thompson bp = &bif->bif_stp; 19383fab7669SAndrew Thompson bpreq.ifbp_portno = bif->bif_ifp->if_index & 0xfff; 19393fab7669SAndrew Thompson bpreq.ifbp_fwd_trans = bp->bp_forward_transitions; 19403fab7669SAndrew Thompson bpreq.ifbp_design_cost = bp->bp_desg_pv.pv_cost; 19413fab7669SAndrew Thompson bpreq.ifbp_design_port = bp->bp_desg_pv.pv_port_id; 19423fab7669SAndrew Thompson bpreq.ifbp_design_bridge = bp->bp_desg_pv.pv_dbridge_id; 19433fab7669SAndrew Thompson bpreq.ifbp_design_root = bp->bp_desg_pv.pv_root_id; 194451383c37SAndrew Thompson 194582056f42SAndrew Thompson memcpy(buf, &bpreq, sizeof(bpreq)); 194651383c37SAndrew Thompson count++; 194782056f42SAndrew Thompson buf += sizeof(bpreq); 194851383c37SAndrew Thompson len -= sizeof(bpreq); 194951383c37SAndrew Thompson } 195051383c37SAndrew Thompson 195151383c37SAndrew Thompson bifstp->ifbpstp_len = sizeof(bpreq) * count; 195282056f42SAndrew Thompson error = copyout(outbuf, bifstp->ifbpstp_req, bifstp->ifbpstp_len); 195382056f42SAndrew Thompson free(outbuf, M_TEMP); 195451383c37SAndrew Thompson return (error); 195551383c37SAndrew Thompson } 195651383c37SAndrew Thompson 19573fab7669SAndrew Thompson static int 19583fab7669SAndrew Thompson bridge_ioctl_sproto(struct bridge_softc *sc, void *arg) 19593fab7669SAndrew Thompson { 19603fab7669SAndrew Thompson struct ifbrparam *param = arg; 19613fab7669SAndrew Thompson 19623fab7669SAndrew Thompson return (bstp_set_protocol(&sc->sc_stp, param->ifbrp_proto)); 19633fab7669SAndrew Thompson } 19643fab7669SAndrew Thompson 19653fab7669SAndrew Thompson static int 19663fab7669SAndrew Thompson bridge_ioctl_stxhc(struct bridge_softc *sc, void *arg) 19673fab7669SAndrew Thompson { 19683fab7669SAndrew Thompson struct ifbrparam *param = arg; 19693fab7669SAndrew Thompson 19703fab7669SAndrew Thompson return (bstp_set_holdcount(&sc->sc_stp, param->ifbrp_txhc)); 19713fab7669SAndrew Thompson } 19723fab7669SAndrew Thompson 197331997bf2SAndrew Thompson /* 197431997bf2SAndrew Thompson * bridge_ifdetach: 197531997bf2SAndrew Thompson * 197631997bf2SAndrew Thompson * Detach an interface from a bridge. Called when a member 197731997bf2SAndrew Thompson * interface is detaching. 197831997bf2SAndrew Thompson */ 19796b32f3d3SAndrew Thompson static void 1980e0a87e8aSAndrew Thompson bridge_ifdetach(void *arg __unused, struct ifnet *ifp) 198131997bf2SAndrew Thompson { 198231997bf2SAndrew Thompson struct bridge_softc *sc = ifp->if_bridge; 19831a266137SAndrew Thompson struct bridge_iflist *bif; 198431997bf2SAndrew Thompson 198583a3ff21SMark Johnston if (ifp->if_flags & IFF_RENAMING) 198683a3ff21SMark Johnston return; 198725792b11SHiroki Sato if (V_bridge_cloner == NULL) { 198825792b11SHiroki Sato /* 198925792b11SHiroki Sato * This detach handler can be called after 199025792b11SHiroki Sato * vnet_bridge_uninit(). Just return in that case. 199125792b11SHiroki Sato */ 199225792b11SHiroki Sato return; 199325792b11SHiroki Sato } 1994e0a87e8aSAndrew Thompson /* Check if the interface is a bridge member */ 1995e0a87e8aSAndrew Thompson if (sc != NULL) { 199620a65f37SAndrew Thompson BRIDGE_LOCK(sc); 199731997bf2SAndrew Thompson 19981a266137SAndrew Thompson bif = bridge_lookup_member_if(sc, ifp); 1999e0a87e8aSAndrew Thompson if (bif != NULL) 20001a266137SAndrew Thompson bridge_delete_member(sc, bif, 1); 20011a266137SAndrew Thompson 200220a65f37SAndrew Thompson BRIDGE_UNLOCK(sc); 2003e0a87e8aSAndrew Thompson return; 2004e0a87e8aSAndrew Thompson } 2005e0a87e8aSAndrew Thompson 2006e0a87e8aSAndrew Thompson /* Check if the interface is a span port */ 2007c5127526SHiroki Sato BRIDGE_LIST_LOCK(); 2008c5127526SHiroki Sato LIST_FOREACH(sc, &V_bridge_list, sc_list) { 2009e0a87e8aSAndrew Thompson BRIDGE_LOCK(sc); 2010dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) 2011e0a87e8aSAndrew Thompson if (ifp == bif->bif_ifp) { 2012e0a87e8aSAndrew Thompson bridge_delete_span(sc, bif); 2013e0a87e8aSAndrew Thompson break; 2014e0a87e8aSAndrew Thompson } 2015e0a87e8aSAndrew Thompson 2016e0a87e8aSAndrew Thompson BRIDGE_UNLOCK(sc); 2017e0a87e8aSAndrew Thompson } 2018c5127526SHiroki Sato BRIDGE_LIST_UNLOCK(); 201931997bf2SAndrew Thompson } 202031997bf2SAndrew Thompson 202131997bf2SAndrew Thompson /* 202231997bf2SAndrew Thompson * bridge_init: 202331997bf2SAndrew Thompson * 202431997bf2SAndrew Thompson * Initialize a bridge interface. 202531997bf2SAndrew Thompson */ 202631997bf2SAndrew Thompson static void 202731997bf2SAndrew Thompson bridge_init(void *xsc) 202831997bf2SAndrew Thompson { 202931997bf2SAndrew Thompson struct bridge_softc *sc = (struct bridge_softc *)xsc; 2030fc74a9f9SBrooks Davis struct ifnet *ifp = sc->sc_ifp; 203131997bf2SAndrew Thompson 203213f4c340SRobert Watson if (ifp->if_drv_flags & IFF_DRV_RUNNING) 203331997bf2SAndrew Thompson return; 203431997bf2SAndrew Thompson 203523e76431SAndrew Thompson BRIDGE_LOCK(sc); 203631997bf2SAndrew Thompson callout_reset(&sc->sc_brcallout, bridge_rtable_prune_period * hz, 203731997bf2SAndrew Thompson bridge_timer, sc); 203831997bf2SAndrew Thompson 203913f4c340SRobert Watson ifp->if_drv_flags |= IFF_DRV_RUNNING; 204096e47153SAndrew Thompson bstp_init(&sc->sc_stp); /* Initialize Spanning Tree */ 204196e47153SAndrew Thompson 2042a1c0fd4dSAndrew Thompson BRIDGE_UNLOCK(sc); 204331997bf2SAndrew Thompson } 204431997bf2SAndrew Thompson 204531997bf2SAndrew Thompson /* 204631997bf2SAndrew Thompson * bridge_stop: 204731997bf2SAndrew Thompson * 204831997bf2SAndrew Thompson * Stop the bridge interface. 204931997bf2SAndrew Thompson */ 20506b32f3d3SAndrew Thompson static void 205131997bf2SAndrew Thompson bridge_stop(struct ifnet *ifp, int disable) 205231997bf2SAndrew Thompson { 205331997bf2SAndrew Thompson struct bridge_softc *sc = ifp->if_softc; 205431997bf2SAndrew Thompson 2055a1c0fd4dSAndrew Thompson BRIDGE_LOCK_ASSERT(sc); 2056a1c0fd4dSAndrew Thompson 205713f4c340SRobert Watson if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 205831997bf2SAndrew Thompson return; 205931997bf2SAndrew Thompson 20604af1bd81SKristof Provost BRIDGE_RT_LOCK(sc); 206131997bf2SAndrew Thompson callout_stop(&sc->sc_brcallout); 20624af1bd81SKristof Provost 206396e47153SAndrew Thompson bstp_stop(&sc->sc_stp); 206431997bf2SAndrew Thompson 206531997bf2SAndrew Thompson bridge_rtflush(sc, IFBF_FLUSHDYN); 20664af1bd81SKristof Provost BRIDGE_RT_UNLOCK(sc); 206731997bf2SAndrew Thompson 206813f4c340SRobert Watson ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 206931997bf2SAndrew Thompson } 207031997bf2SAndrew Thompson 207131997bf2SAndrew Thompson /* 207231997bf2SAndrew Thompson * bridge_enqueue: 207331997bf2SAndrew Thompson * 207431997bf2SAndrew Thompson * Enqueue a packet on a bridge member interface. 207531997bf2SAndrew Thompson * 207631997bf2SAndrew Thompson */ 20773582a9f6SGleb Smirnoff static int 2078ea32e732SAndrew Thompson bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m) 207931997bf2SAndrew Thompson { 20807f87a57cSAndrew Thompson int len, err = 0; 208131997bf2SAndrew Thompson short mflags; 20827f87a57cSAndrew Thompson struct mbuf *m0; 208331997bf2SAndrew Thompson 2084615fccc5SAndrew Thompson /* We may be sending a fragment so traverse the mbuf */ 20857f87a57cSAndrew Thompson for (; m; m = m0) { 20867f87a57cSAndrew Thompson m0 = m->m_nextpkt; 20877f87a57cSAndrew Thompson m->m_nextpkt = NULL; 208880cd7c75SGleb Smirnoff len = m->m_pkthdr.len; 208980cd7c75SGleb Smirnoff mflags = m->m_flags; 20907f87a57cSAndrew Thompson 209160e87ca8SAndrew Thompson /* 209260e87ca8SAndrew Thompson * If underlying interface can not do VLAN tag insertion itself 209360e87ca8SAndrew Thompson * then attach a packet tag that holds it. 209460e87ca8SAndrew Thompson */ 209560e87ca8SAndrew Thompson if ((m->m_flags & M_VLANTAG) && 209660e87ca8SAndrew Thompson (dst_ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) { 209760e87ca8SAndrew Thompson m = ether_vlanencap(m, m->m_pkthdr.ether_vtag); 209860e87ca8SAndrew Thompson if (m == NULL) { 209960e87ca8SAndrew Thompson if_printf(dst_ifp, 210060e87ca8SAndrew Thompson "unable to prepend VLAN header\n"); 21013751dddbSGleb Smirnoff if_inc_counter(dst_ifp, IFCOUNTER_OERRORS, 1); 210260e87ca8SAndrew Thompson continue; 210360e87ca8SAndrew Thompson } 210460e87ca8SAndrew Thompson m->m_flags &= ~M_VLANTAG; 210560e87ca8SAndrew Thompson } 210660e87ca8SAndrew Thompson 2107f18598a4SKristof Provost M_ASSERTPKTHDR(m); /* We shouldn't transmit mbuf without pkthdr */ 2108bf7a35deSEd Maste if ((err = dst_ifp->if_transmit(dst_ifp, m))) { 210951088797SMark Johnston int n; 211051088797SMark Johnston 211151088797SMark Johnston for (m = m0, n = 1; m != NULL; m = m0, n++) { 211251088797SMark Johnston m0 = m->m_nextpkt; 211351088797SMark Johnston m_freem(m); 211451088797SMark Johnston } 211551088797SMark Johnston if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, n); 2116bf7a35deSEd Maste break; 2117bf7a35deSEd Maste } 21187f87a57cSAndrew Thompson 21193751dddbSGleb Smirnoff if_inc_counter(sc->sc_ifp, IFCOUNTER_OPACKETS, 1); 21203751dddbSGleb Smirnoff if_inc_counter(sc->sc_ifp, IFCOUNTER_OBYTES, len); 21215d322040SSam Leffler if (mflags & M_MCAST) 21223751dddbSGleb Smirnoff if_inc_counter(sc->sc_ifp, IFCOUNTER_OMCASTS, 1); 212331997bf2SAndrew Thompson } 21243582a9f6SGleb Smirnoff 21253582a9f6SGleb Smirnoff return (err); 212631997bf2SAndrew Thompson } 212731997bf2SAndrew Thompson 212831997bf2SAndrew Thompson /* 2129c8b01292SAndrew Thompson * bridge_dummynet: 2130c8b01292SAndrew Thompson * 2131c8b01292SAndrew Thompson * Receive a queued packet from dummynet and pass it on to the output 2132c8b01292SAndrew Thompson * interface. 2133c8b01292SAndrew Thompson * 2134c8b01292SAndrew Thompson * The mbuf has the Ethernet header already attached. 2135c8b01292SAndrew Thompson */ 21366b32f3d3SAndrew Thompson static void 2137c8b01292SAndrew Thompson bridge_dummynet(struct mbuf *m, struct ifnet *ifp) 2138c8b01292SAndrew Thompson { 2139c8b01292SAndrew Thompson struct bridge_softc *sc; 2140c8b01292SAndrew Thompson 2141c8b01292SAndrew Thompson sc = ifp->if_bridge; 2142c8b01292SAndrew Thompson 2143c8b01292SAndrew Thompson /* 2144c8b01292SAndrew Thompson * The packet didnt originate from a member interface. This should only 2145c8b01292SAndrew Thompson * ever happen if a member interface is removed while packets are 2146c8b01292SAndrew Thompson * queued for it. 2147c8b01292SAndrew Thompson */ 21482c67c57cSMax Laier if (sc == NULL) { 2149c8b01292SAndrew Thompson m_freem(m); 2150c8b01292SAndrew Thompson return; 21512c67c57cSMax Laier } 2152c8b01292SAndrew Thompson 2153ef84dd8fSLexi Winter if (PFIL_HOOKED_OUT_46) { 2154ea32e732SAndrew Thompson if (bridge_pfil(&m, sc->sc_ifp, ifp, PFIL_OUT) != 0) 2155ea32e732SAndrew Thompson return; 2156ea32e732SAndrew Thompson if (m == NULL) 2157ea32e732SAndrew Thompson return; 2158ea32e732SAndrew Thompson } 2159ea32e732SAndrew Thompson 2160ea32e732SAndrew Thompson bridge_enqueue(sc, ifp, m); 2161c8b01292SAndrew Thompson } 2162c8b01292SAndrew Thompson 2163c8b01292SAndrew Thompson /* 216431997bf2SAndrew Thompson * bridge_output: 216531997bf2SAndrew Thompson * 216631997bf2SAndrew Thompson * Send output from a bridge member interface. This 216731997bf2SAndrew Thompson * performs the bridging function for locally originated 216831997bf2SAndrew Thompson * packets. 216931997bf2SAndrew Thompson * 217031997bf2SAndrew Thompson * The mbuf has the Ethernet header already attached. We must 217131997bf2SAndrew Thompson * enqueue or free the mbuf before returning. 217231997bf2SAndrew Thompson */ 21736b32f3d3SAndrew Thompson static int 217431997bf2SAndrew Thompson bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, 2175ae4b6259SAlexander V. Chernikov struct rtentry *rt) 217631997bf2SAndrew Thompson { 217731997bf2SAndrew Thompson struct ether_header *eh; 2178d8b98543SKyle Evans struct ifnet *bifp, *dst_if; 217931997bf2SAndrew Thompson struct bridge_softc *sc; 218022dcc3c1SAndrew Thompson uint16_t vlan; 218131997bf2SAndrew Thompson 2182fffd27e5SKristof Provost NET_EPOCH_ASSERT(); 2183fffd27e5SKristof Provost 218431997bf2SAndrew Thompson if (m->m_len < ETHER_HDR_LEN) { 218531997bf2SAndrew Thompson m = m_pullup(m, ETHER_HDR_LEN); 218631997bf2SAndrew Thompson if (m == NULL) 218731997bf2SAndrew Thompson return (0); 218831997bf2SAndrew Thompson } 218931997bf2SAndrew Thompson 219031997bf2SAndrew Thompson eh = mtod(m, struct ether_header *); 219131997bf2SAndrew Thompson sc = ifp->if_bridge; 219222dcc3c1SAndrew Thompson vlan = VLANTAGOF(m); 219331997bf2SAndrew Thompson 2194d8b98543SKyle Evans bifp = sc->sc_ifp; 219531997bf2SAndrew Thompson 219631997bf2SAndrew Thompson /* 219731997bf2SAndrew Thompson * If bridge is down, but the original output interface is up, 219831997bf2SAndrew Thompson * go ahead and send out that interface. Otherwise, the packet 219931997bf2SAndrew Thompson * is dropped below. 220031997bf2SAndrew Thompson */ 2201d8b98543SKyle Evans if ((bifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 220231997bf2SAndrew Thompson dst_if = ifp; 220331997bf2SAndrew Thompson goto sendunicast; 220431997bf2SAndrew Thompson } 220531997bf2SAndrew Thompson 220631997bf2SAndrew Thompson /* 220731997bf2SAndrew Thompson * If the packet is a multicast, or we don't know a better way to 220831997bf2SAndrew Thompson * get there, send to all interfaces. 220931997bf2SAndrew Thompson */ 221031997bf2SAndrew Thompson if (ETHER_IS_MULTICAST(eh->ether_dhost)) 221131997bf2SAndrew Thompson dst_if = NULL; 221231997bf2SAndrew Thompson else 221322dcc3c1SAndrew Thompson dst_if = bridge_rtlookup(sc, eh->ether_dhost, vlan); 2214d8b98543SKyle Evans /* Tap any traffic not passing back out the originating interface */ 2215d8b98543SKyle Evans if (dst_if != ifp) 2216d8b98543SKyle Evans ETHER_BPF_MTAP(bifp, m); 221731997bf2SAndrew Thompson if (dst_if == NULL) { 221831997bf2SAndrew Thompson struct bridge_iflist *bif; 221931997bf2SAndrew Thompson struct mbuf *mc; 2220fffd27e5SKristof Provost int used = 0; 222131997bf2SAndrew Thompson 2222b34b8d67SAndrew Thompson bridge_span(sc, m); 2223b34b8d67SAndrew Thompson 2224dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { 222531997bf2SAndrew Thompson dst_if = bif->bif_ifp; 222673ff045cSAndrew Thompson 222773ff045cSAndrew Thompson if (dst_if->if_type == IFT_GIF) 222873ff045cSAndrew Thompson continue; 222913f4c340SRobert Watson if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) 223031997bf2SAndrew Thompson continue; 223131997bf2SAndrew Thompson 223231997bf2SAndrew Thompson /* 223331997bf2SAndrew Thompson * If this is not the original output interface, 223431997bf2SAndrew Thompson * and the interface is participating in spanning 223531997bf2SAndrew Thompson * tree, make sure the port is in a state that 223631997bf2SAndrew Thompson * allows forwarding. 223731997bf2SAndrew Thompson */ 22383fab7669SAndrew Thompson if (dst_if != ifp && (bif->bif_flags & IFBIF_STP) && 22393fab7669SAndrew Thompson bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) 224031997bf2SAndrew Thompson continue; 224131997bf2SAndrew Thompson 2242dd00a42aSKristof Provost if (CK_LIST_NEXT(bif, bif_next) == NULL) { 224331997bf2SAndrew Thompson used = 1; 224431997bf2SAndrew Thompson mc = m; 224531997bf2SAndrew Thompson } else { 224636637dd1SKristof Provost mc = m_dup(m, M_NOWAIT); 224731997bf2SAndrew Thompson if (mc == NULL) { 2248d8b98543SKyle Evans if_inc_counter(bifp, IFCOUNTER_OERRORS, 1); 224931997bf2SAndrew Thompson continue; 225031997bf2SAndrew Thompson } 225131997bf2SAndrew Thompson } 225231997bf2SAndrew Thompson 2253ea32e732SAndrew Thompson bridge_enqueue(sc, dst_if, mc); 225431997bf2SAndrew Thompson } 225531997bf2SAndrew Thompson if (used == 0) 225631997bf2SAndrew Thompson m_freem(m); 225731997bf2SAndrew Thompson return (0); 225831997bf2SAndrew Thompson } 225931997bf2SAndrew Thompson 226031997bf2SAndrew Thompson sendunicast: 226131997bf2SAndrew Thompson /* 226231997bf2SAndrew Thompson * XXX Spanning tree consideration here? 226331997bf2SAndrew Thompson */ 226431997bf2SAndrew Thompson 226591f6764eSAndrew Thompson bridge_span(sc, m); 226613f4c340SRobert Watson if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) { 226731997bf2SAndrew Thompson m_freem(m); 226831997bf2SAndrew Thompson return (0); 226931997bf2SAndrew Thompson } 227031997bf2SAndrew Thompson 2271ea32e732SAndrew Thompson bridge_enqueue(sc, dst_if, m); 227231997bf2SAndrew Thompson return (0); 227331997bf2SAndrew Thompson } 227431997bf2SAndrew Thompson 227531997bf2SAndrew Thompson /* 22763582a9f6SGleb Smirnoff * bridge_transmit: 227731997bf2SAndrew Thompson * 22783582a9f6SGleb Smirnoff * Do output on a bridge. 227931997bf2SAndrew Thompson * 228031997bf2SAndrew Thompson */ 22813582a9f6SGleb Smirnoff static int 22823582a9f6SGleb Smirnoff bridge_transmit(struct ifnet *ifp, struct mbuf *m) 228331997bf2SAndrew Thompson { 228431997bf2SAndrew Thompson struct bridge_softc *sc; 2285da1fc67fSGleb Smirnoff struct ether_header *eh; 2286da1fc67fSGleb Smirnoff struct ifnet *dst_if; 22873582a9f6SGleb Smirnoff int error = 0; 228831997bf2SAndrew Thompson 228931997bf2SAndrew Thompson sc = ifp->if_softc; 229031997bf2SAndrew Thompson 22913565f9bcSAndrew Thompson ETHER_BPF_MTAP(ifp, m); 229231997bf2SAndrew Thompson 2293da1fc67fSGleb Smirnoff eh = mtod(m, struct ether_header *); 229431997bf2SAndrew Thompson 2295da1fc67fSGleb Smirnoff if (((m->m_flags & (M_BCAST|M_MCAST)) == 0) && 2296fd7edfcdSBen Wilber (dst_if = bridge_rtlookup(sc, eh->ether_dhost, DOT1Q_VID_NULL)) != 2297fd7edfcdSBen Wilber NULL) { 22983582a9f6SGleb Smirnoff error = bridge_enqueue(sc, dst_if, m); 22993582a9f6SGleb Smirnoff } else 23003582a9f6SGleb Smirnoff bridge_broadcast(sc, ifp, m, 0); 23013582a9f6SGleb Smirnoff 23023582a9f6SGleb Smirnoff return (error); 230331997bf2SAndrew Thompson } 230431997bf2SAndrew Thompson 2305eb680a63SLuiz Otavio O Souza #ifdef ALTQ 2306eb680a63SLuiz Otavio O Souza static void 2307eb680a63SLuiz Otavio O Souza bridge_altq_start(if_t ifp) 2308eb680a63SLuiz Otavio O Souza { 2309eb680a63SLuiz Otavio O Souza struct ifaltq *ifq = &ifp->if_snd; 2310eb680a63SLuiz Otavio O Souza struct mbuf *m; 2311eb680a63SLuiz Otavio O Souza 2312eb680a63SLuiz Otavio O Souza IFQ_LOCK(ifq); 2313eb680a63SLuiz Otavio O Souza IFQ_DEQUEUE_NOLOCK(ifq, m); 2314eb680a63SLuiz Otavio O Souza while (m != NULL) { 2315eb680a63SLuiz Otavio O Souza bridge_transmit(ifp, m); 2316eb680a63SLuiz Otavio O Souza IFQ_DEQUEUE_NOLOCK(ifq, m); 2317eb680a63SLuiz Otavio O Souza } 2318eb680a63SLuiz Otavio O Souza IFQ_UNLOCK(ifq); 2319eb680a63SLuiz Otavio O Souza } 2320eb680a63SLuiz Otavio O Souza 2321eb680a63SLuiz Otavio O Souza static int 2322eb680a63SLuiz Otavio O Souza bridge_altq_transmit(if_t ifp, struct mbuf *m) 2323eb680a63SLuiz Otavio O Souza { 2324eb680a63SLuiz Otavio O Souza int err; 2325eb680a63SLuiz Otavio O Souza 2326eb680a63SLuiz Otavio O Souza if (ALTQ_IS_ENABLED(&ifp->if_snd)) { 2327eb680a63SLuiz Otavio O Souza IFQ_ENQUEUE(&ifp->if_snd, m, err); 2328eb680a63SLuiz Otavio O Souza if (err == 0) 2329eb680a63SLuiz Otavio O Souza bridge_altq_start(ifp); 2330eb680a63SLuiz Otavio O Souza } else 2331eb680a63SLuiz Otavio O Souza err = bridge_transmit(ifp, m); 2332eb680a63SLuiz Otavio O Souza 2333eb680a63SLuiz Otavio O Souza return (err); 2334eb680a63SLuiz Otavio O Souza } 2335eb680a63SLuiz Otavio O Souza #endif /* ALTQ */ 2336eb680a63SLuiz Otavio O Souza 23373582a9f6SGleb Smirnoff /* 23383582a9f6SGleb Smirnoff * The ifp->if_qflush entry point for if_bridge(4) is no-op. 23393582a9f6SGleb Smirnoff */ 23403582a9f6SGleb Smirnoff static void 23413582a9f6SGleb Smirnoff bridge_qflush(struct ifnet *ifp __unused) 23423582a9f6SGleb Smirnoff { 234331997bf2SAndrew Thompson } 234431997bf2SAndrew Thompson 234531997bf2SAndrew Thompson /* 234631997bf2SAndrew Thompson * bridge_forward: 234731997bf2SAndrew Thompson * 234831997bf2SAndrew Thompson * The forwarding function of the bridge. 234931997bf2SAndrew Thompson * 235031997bf2SAndrew Thompson * NOTE: Releases the lock on return. 235131997bf2SAndrew Thompson */ 23526b32f3d3SAndrew Thompson static void 235385ce7297SAndrew Thompson bridge_forward(struct bridge_softc *sc, struct bridge_iflist *sbif, 235485ce7297SAndrew Thompson struct mbuf *m) 235531997bf2SAndrew Thompson { 235685ce7297SAndrew Thompson struct bridge_iflist *dbif; 235731997bf2SAndrew Thompson struct ifnet *src_if, *dst_if, *ifp; 235831997bf2SAndrew Thompson struct ether_header *eh; 235922dcc3c1SAndrew Thompson uint16_t vlan; 2360905925d3SAndrew Thompson uint8_t *dst; 23615f33ec7bSAndrew Thompson int error; 236231997bf2SAndrew Thompson 2363fffd27e5SKristof Provost NET_EPOCH_ASSERT(); 2364fffd27e5SKristof Provost 236531997bf2SAndrew Thompson src_if = m->m_pkthdr.rcvif; 2366fc74a9f9SBrooks Davis ifp = sc->sc_ifp; 236731997bf2SAndrew Thompson 23683751dddbSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 23693751dddbSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); 237022dcc3c1SAndrew Thompson vlan = VLANTAGOF(m); 237131997bf2SAndrew Thompson 237285ce7297SAndrew Thompson if ((sbif->bif_flags & IFBIF_STP) && 23738411d52aSAndrew Thompson sbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) 23748411d52aSAndrew Thompson goto drop; 237531997bf2SAndrew Thompson 237631997bf2SAndrew Thompson eh = mtod(m, struct ether_header *); 2377905925d3SAndrew Thompson dst = eh->ether_dhost; 237831997bf2SAndrew Thompson 23795f33ec7bSAndrew Thompson /* If the interface is learning, record the address. */ 23805f33ec7bSAndrew Thompson if (sbif->bif_flags & IFBIF_LEARNING) { 23815f33ec7bSAndrew Thompson error = bridge_rtupdate(sc, eh->ether_shost, vlan, 238285ce7297SAndrew Thompson sbif, 0, IFBAF_DYNAMIC); 23835f33ec7bSAndrew Thompson /* 23845f33ec7bSAndrew Thompson * If the interface has addresses limits then deny any source 23855f33ec7bSAndrew Thompson * that is not in the cache. 23865f33ec7bSAndrew Thompson */ 23878411d52aSAndrew Thompson if (error && sbif->bif_addrmax) 23888411d52aSAndrew Thompson goto drop; 238931997bf2SAndrew Thompson } 239031997bf2SAndrew Thompson 239185ce7297SAndrew Thompson if ((sbif->bif_flags & IFBIF_STP) != 0 && 23928411d52aSAndrew Thompson sbif->bif_stp.bp_state == BSTP_IFSTATE_LEARNING) 23938411d52aSAndrew Thompson goto drop; 239431997bf2SAndrew Thompson 2395d862b165SMark Johnston #ifdef DEV_NETMAP 2396d862b165SMark Johnston /* 2397d862b165SMark Johnston * Hand the packet to netmap only if it wasn't injected by netmap 2398d862b165SMark Johnston * itself. 2399d862b165SMark Johnston */ 2400d862b165SMark Johnston if ((m->m_flags & M_BRIDGE_INJECT) == 0 && 2401d862b165SMark Johnston (if_getcapenable(ifp) & IFCAP_NETMAP) != 0) { 2402d862b165SMark Johnston ifp->if_input(ifp, m); 2403d862b165SMark Johnston return; 2404d862b165SMark Johnston } 2405d862b165SMark Johnston m->m_flags &= ~M_BRIDGE_INJECT; 2406d862b165SMark Johnston #endif 2407d862b165SMark Johnston 240831997bf2SAndrew Thompson /* 240931997bf2SAndrew Thompson * At this point, the port either doesn't participate 241031997bf2SAndrew Thompson * in spanning tree or it is in the forwarding state. 241131997bf2SAndrew Thompson */ 241231997bf2SAndrew Thompson 241331997bf2SAndrew Thompson /* 241431997bf2SAndrew Thompson * If the packet is unicast, destined for someone on 241531997bf2SAndrew Thompson * "this" side of the bridge, drop it. 241631997bf2SAndrew Thompson */ 241731997bf2SAndrew Thompson if ((m->m_flags & (M_BCAST|M_MCAST)) == 0) { 2418905925d3SAndrew Thompson dst_if = bridge_rtlookup(sc, dst, vlan); 24198411d52aSAndrew Thompson if (src_if == dst_if) 24208411d52aSAndrew Thompson goto drop; 242131997bf2SAndrew Thompson } else { 2422905925d3SAndrew Thompson /* 2423905925d3SAndrew Thompson * Check if its a reserved multicast address, any address 2424905925d3SAndrew Thompson * listed in 802.1D section 7.12.6 may not be forwarded by the 2425905925d3SAndrew Thompson * bridge. 2426905925d3SAndrew Thompson * This is currently 01-80-C2-00-00-00 to 01-80-C2-00-00-0F 2427905925d3SAndrew Thompson */ 2428905925d3SAndrew Thompson if (dst[0] == 0x01 && dst[1] == 0x80 && 2429905925d3SAndrew Thompson dst[2] == 0xc2 && dst[3] == 0x00 && 2430905925d3SAndrew Thompson dst[4] == 0x00 && dst[5] <= 0x0f) 2431905925d3SAndrew Thompson goto drop; 2432905925d3SAndrew Thompson 243331997bf2SAndrew Thompson /* ...forward it to all interfaces. */ 24343751dddbSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IMCASTS, 1); 243531997bf2SAndrew Thompson dst_if = NULL; 243631997bf2SAndrew Thompson } 243731997bf2SAndrew Thompson 24386f75ef18SChristian S.J. Peron /* 24396f75ef18SChristian S.J. Peron * If we have a destination interface which is a member of our bridge, 24406f75ef18SChristian S.J. Peron * OR this is a unicast packet, push it through the bpf(4) machinery. 24416f75ef18SChristian S.J. Peron * For broadcast or multicast packets, don't bother because it will 24426f75ef18SChristian S.J. Peron * be reinjected into ether_input. We do this before we pass the packets 24436f75ef18SChristian S.J. Peron * through the pfil(9) framework, as it is possible that pfil(9) will 24446f75ef18SChristian S.J. Peron * drop the packet, or possibly modify it, making it difficult to debug 24456f75ef18SChristian S.J. Peron * firewall issues on the bridge. 24466f75ef18SChristian S.J. Peron */ 24476f75ef18SChristian S.J. Peron if (dst_if != NULL || (m->m_flags & (M_BCAST | M_MCAST)) == 0) 24483565f9bcSAndrew Thompson ETHER_BPF_MTAP(ifp, m); 24496f75ef18SChristian S.J. Peron 245031997bf2SAndrew Thompson /* run the packet filter */ 2451ef84dd8fSLexi Winter if (PFIL_HOOKED_IN_46) { 245231997bf2SAndrew Thompson if (bridge_pfil(&m, ifp, src_if, PFIL_IN) != 0) 245331997bf2SAndrew Thompson return; 2454ea32e732SAndrew Thompson if (m == NULL) 245531997bf2SAndrew Thompson return; 245631997bf2SAndrew Thompson } 245731997bf2SAndrew Thompson 245831997bf2SAndrew Thompson if (dst_if == NULL) { 2459d5edd47eSAndrew Thompson bridge_broadcast(sc, src_if, m, 1); 246031997bf2SAndrew Thompson return; 246131997bf2SAndrew Thompson } 246231997bf2SAndrew Thompson 246331997bf2SAndrew Thompson /* 246431997bf2SAndrew Thompson * At this point, we're dealing with a unicast frame 246531997bf2SAndrew Thompson * going to a different interface. 246631997bf2SAndrew Thompson */ 24678411d52aSAndrew Thompson if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) 24688411d52aSAndrew Thompson goto drop; 24698411d52aSAndrew Thompson 247085ce7297SAndrew Thompson dbif = bridge_lookup_member_if(sc, dst_if); 24718411d52aSAndrew Thompson if (dbif == NULL) 247231997bf2SAndrew Thompson /* Not a member of the bridge (anymore?) */ 24738411d52aSAndrew Thompson goto drop; 247431997bf2SAndrew Thompson 247585ce7297SAndrew Thompson /* Private segments can not talk to each other */ 24768411d52aSAndrew Thompson if (sbif->bif_flags & dbif->bif_flags & IFBIF_PRIVATE) 24778411d52aSAndrew Thompson goto drop; 247885ce7297SAndrew Thompson 247985ce7297SAndrew Thompson if ((dbif->bif_flags & IFBIF_STP) && 24808411d52aSAndrew Thompson dbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) 24818411d52aSAndrew Thompson goto drop; 248231997bf2SAndrew Thompson 2483ef84dd8fSLexi Winter if (PFIL_HOOKED_OUT_46) { 24848411d52aSAndrew Thompson if (bridge_pfil(&m, ifp, dst_if, PFIL_OUT) != 0) 2485ea32e732SAndrew Thompson return; 2486ea32e732SAndrew Thompson if (m == NULL) 2487ea32e732SAndrew Thompson return; 2488ea32e732SAndrew Thompson } 2489ea32e732SAndrew Thompson 2490ea32e732SAndrew Thompson bridge_enqueue(sc, dst_if, m); 24918411d52aSAndrew Thompson return; 24928411d52aSAndrew Thompson 24938411d52aSAndrew Thompson drop: 24948411d52aSAndrew Thompson m_freem(m); 249531997bf2SAndrew Thompson } 249631997bf2SAndrew Thompson 249731997bf2SAndrew Thompson /* 249831997bf2SAndrew Thompson * bridge_input: 249931997bf2SAndrew Thompson * 250031997bf2SAndrew Thompson * Receive input from a member interface. Queue the packet for 250131997bf2SAndrew Thompson * bridging if it is not for us. 250231997bf2SAndrew Thompson */ 25034c843479SAndrew Thompson static struct mbuf * 250431997bf2SAndrew Thompson bridge_input(struct ifnet *ifp, struct mbuf *m) 250531997bf2SAndrew Thompson { 2506d862b165SMark Johnston struct bridge_softc *sc; 25073df7fad0SAndrew Thompson struct bridge_iflist *bif, *bif2; 250859280079SAndrew Thompson struct ifnet *bifp; 250931997bf2SAndrew Thompson struct ether_header *eh; 251059280079SAndrew Thompson struct mbuf *mc, *mc2; 251122dcc3c1SAndrew Thompson uint16_t vlan; 25125f33ec7bSAndrew Thompson int error; 251331997bf2SAndrew Thompson 2514fffd27e5SKristof Provost NET_EPOCH_ASSERT(); 2515fffd27e5SKristof Provost 2516d862b165SMark Johnston eh = mtod(m, struct ether_header *); 251722dcc3c1SAndrew Thompson vlan = VLANTAGOF(m); 251859280079SAndrew Thompson 2519d862b165SMark Johnston sc = ifp->if_bridge; 2520d862b165SMark Johnston if (sc == NULL) { 2521d862b165SMark Johnston /* 2522d862b165SMark Johnston * This packet originated from the bridge itself, so it must 2523d862b165SMark Johnston * have been transmitted by netmap. Derive the "source" 2524d862b165SMark Johnston * interface from the source address and drop the packet if the 2525d862b165SMark Johnston * source address isn't known. 2526d862b165SMark Johnston */ 2527d862b165SMark Johnston KASSERT((m->m_flags & M_BRIDGE_INJECT) != 0, 2528d862b165SMark Johnston ("%s: ifnet %p missing a bridge softc", __func__, ifp)); 2529d862b165SMark Johnston sc = if_getsoftc(ifp); 2530d862b165SMark Johnston ifp = bridge_rtlookup(sc, eh->ether_shost, vlan); 2531d862b165SMark Johnston if (ifp == NULL) { 2532d862b165SMark Johnston if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1); 2533d862b165SMark Johnston m_freem(m); 2534d862b165SMark Johnston return (NULL); 2535d862b165SMark Johnston } 2536d862b165SMark Johnston m->m_pkthdr.rcvif = ifp; 2537d862b165SMark Johnston } 2538d862b165SMark Johnston bifp = sc->sc_ifp; 2539d862b165SMark Johnston if ((bifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 2540d862b165SMark Johnston return (m); 2541d862b165SMark Johnston 25426f75ef18SChristian S.J. Peron /* 25436f75ef18SChristian S.J. Peron * Implement support for bridge monitoring. If this flag has been 25446f75ef18SChristian S.J. Peron * set on this interface, discard the packet once we push it through 25456f75ef18SChristian S.J. Peron * the bpf(4) machinery, but before we do, increment the byte and 25466f75ef18SChristian S.J. Peron * packet counters associated with this interface. 25476f75ef18SChristian S.J. Peron */ 25486f75ef18SChristian S.J. Peron if ((bifp->if_flags & IFF_MONITOR) != 0) { 25496f75ef18SChristian S.J. Peron m->m_pkthdr.rcvif = bifp; 25503565f9bcSAndrew Thompson ETHER_BPF_MTAP(bifp, m); 25513751dddbSGleb Smirnoff if_inc_counter(bifp, IFCOUNTER_IPACKETS, 1); 25523751dddbSGleb Smirnoff if_inc_counter(bifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); 255367be76c0SChristian S.J. Peron m_freem(m); 25546f75ef18SChristian S.J. Peron return (NULL); 25556f75ef18SChristian S.J. Peron } 255631997bf2SAndrew Thompson bif = bridge_lookup_member_if(sc, ifp); 255731997bf2SAndrew Thompson if (bif == NULL) { 255831997bf2SAndrew Thompson return (m); 255931997bf2SAndrew Thompson } 256031997bf2SAndrew Thompson 256191f6764eSAndrew Thompson bridge_span(sc, m); 256291f6764eSAndrew Thompson 256382912c1fSAndrew Thompson if (m->m_flags & (M_BCAST|M_MCAST)) { 256431997bf2SAndrew Thompson /* Tap off 802.1D packets; they do not get forwarded. */ 256531997bf2SAndrew Thompson if (memcmp(eh->ether_dhost, bstp_etheraddr, 256631997bf2SAndrew Thompson ETHER_ADDR_LEN) == 0) { 25674661f862SAndrew Thompson bstp_input(&bif->bif_stp, ifp, m); /* consumes mbuf */ 256831997bf2SAndrew Thompson return (NULL); 256931997bf2SAndrew Thompson } 257031997bf2SAndrew Thompson 25713fab7669SAndrew Thompson if ((bif->bif_flags & IFBIF_STP) && 25723fab7669SAndrew Thompson bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) { 257331997bf2SAndrew Thompson return (m); 257431997bf2SAndrew Thompson } 257531997bf2SAndrew Thompson 257631997bf2SAndrew Thompson /* 257731997bf2SAndrew Thompson * Make a deep copy of the packet and enqueue the copy 257831997bf2SAndrew Thompson * for bridge processing; return the original packet for 257931997bf2SAndrew Thompson * local processing. 258031997bf2SAndrew Thompson */ 2581eb1b1807SGleb Smirnoff mc = m_dup(m, M_NOWAIT); 258231997bf2SAndrew Thompson if (mc == NULL) { 258331997bf2SAndrew Thompson return (m); 258431997bf2SAndrew Thompson } 258531997bf2SAndrew Thompson 258631997bf2SAndrew Thompson /* Perform the bridge forwarding function with the copy. */ 258785ce7297SAndrew Thompson bridge_forward(sc, bif, mc); 258831997bf2SAndrew Thompson 2589d862b165SMark Johnston #ifdef DEV_NETMAP 2590d862b165SMark Johnston /* 2591d862b165SMark Johnston * If netmap is enabled and has not already seen this packet, 2592d862b165SMark Johnston * then it will be consumed by bridge_forward(). 2593d862b165SMark Johnston */ 2594d862b165SMark Johnston if ((if_getcapenable(bifp) & IFCAP_NETMAP) != 0 && 2595d862b165SMark Johnston (m->m_flags & M_BRIDGE_INJECT) == 0) { 2596d862b165SMark Johnston m_freem(m); 2597d862b165SMark Johnston return (NULL); 2598d862b165SMark Johnston } 2599d862b165SMark Johnston #endif 2600d862b165SMark Johnston 260159280079SAndrew Thompson /* 260259280079SAndrew Thompson * Reinject the mbuf as arriving on the bridge so we have a 260359280079SAndrew Thompson * chance at claiming multicast packets. We can not loop back 260459280079SAndrew Thompson * here from ether_input as a bridge is never a member of a 260559280079SAndrew Thompson * bridge. 260659280079SAndrew Thompson */ 260759280079SAndrew Thompson KASSERT(bifp->if_bridge == NULL, 260859280079SAndrew Thompson ("loop created in bridge_input")); 2609eb1b1807SGleb Smirnoff mc2 = m_dup(m, M_NOWAIT); 26107536320fSAndrew Thompson if (mc2 != NULL) { 26117536320fSAndrew Thompson /* Keep the layer3 header aligned */ 26127536320fSAndrew Thompson int i = min(mc2->m_pkthdr.len, max_protohdr); 26137536320fSAndrew Thompson mc2 = m_copyup(mc2, i, ETHER_ALIGN); 26147536320fSAndrew Thompson } 261559280079SAndrew Thompson if (mc2 != NULL) { 261659280079SAndrew Thompson mc2->m_pkthdr.rcvif = bifp; 2617d862b165SMark Johnston mc2->m_flags &= ~M_BRIDGE_INJECT; 2618d862b165SMark Johnston sc->sc_if_input(bifp, mc2); 261959280079SAndrew Thompson } 262059280079SAndrew Thompson 262131997bf2SAndrew Thompson /* Return the original packet for local processing. */ 262231997bf2SAndrew Thompson return (m); 262331997bf2SAndrew Thompson } 262431997bf2SAndrew Thompson 26253fab7669SAndrew Thompson if ((bif->bif_flags & IFBIF_STP) && 26263fab7669SAndrew Thompson bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) { 262731997bf2SAndrew Thompson return (m); 262831997bf2SAndrew Thompson } 262931997bf2SAndrew Thompson 263082bbdde4SMark Johnston #if defined(INET) || defined(INET6) 263182bbdde4SMark Johnston #define CARP_CHECK_WE_ARE_DST(iface) \ 263282bbdde4SMark Johnston ((iface)->if_carp && (*carp_forus_p)((iface), eh->ether_dhost)) 263382bbdde4SMark Johnston #define CARP_CHECK_WE_ARE_SRC(iface) \ 263482bbdde4SMark Johnston ((iface)->if_carp && (*carp_forus_p)((iface), eh->ether_shost)) 26353d0a65c8SRoman Kurakin #else 263682bbdde4SMark Johnston #define CARP_CHECK_WE_ARE_DST(iface) false 263782bbdde4SMark Johnston #define CARP_CHECK_WE_ARE_SRC(iface) false 2638b3a1f937SAndrew Thompson #endif 26393d0a65c8SRoman Kurakin 2640d862b165SMark Johnston #ifdef DEV_NETMAP 2641d862b165SMark Johnston #define GRAB_FOR_NETMAP(ifp, m) do { \ 2642d862b165SMark Johnston if ((if_getcapenable(ifp) & IFCAP_NETMAP) != 0 && \ 2643d862b165SMark Johnston ((m)->m_flags & M_BRIDGE_INJECT) == 0) { \ 2644d862b165SMark Johnston (ifp)->if_input(ifp, m); \ 2645d862b165SMark Johnston return (NULL); \ 2646d862b165SMark Johnston } \ 2647d862b165SMark Johnston } while (0) 2648d862b165SMark Johnston #else 2649d862b165SMark Johnston #define GRAB_FOR_NETMAP(ifp, m) 2650d862b165SMark Johnston #endif 2651d862b165SMark Johnston 26523d0a65c8SRoman Kurakin #define GRAB_OUR_PACKETS(iface) \ 26533d0a65c8SRoman Kurakin if ((iface)->if_type == IFT_GIF) \ 26543d0a65c8SRoman Kurakin continue; \ 26553d0a65c8SRoman Kurakin /* It is destined for us. */ \ 265682bbdde4SMark Johnston if (memcmp(IF_LLADDR(iface), eh->ether_dhost, ETHER_ADDR_LEN) == 0 || \ 265782bbdde4SMark Johnston CARP_CHECK_WE_ARE_DST(iface)) { \ 26585f33ec7bSAndrew Thompson if (bif->bif_flags & IFBIF_LEARNING) { \ 26595f33ec7bSAndrew Thompson error = bridge_rtupdate(sc, eh->ether_shost, \ 266022dcc3c1SAndrew Thompson vlan, bif, 0, IFBAF_DYNAMIC); \ 26615f33ec7bSAndrew Thompson if (error && bif->bif_addrmax) { \ 26625f33ec7bSAndrew Thompson m_freem(m); \ 26635f33ec7bSAndrew Thompson return (NULL); \ 26645f33ec7bSAndrew Thompson } \ 26655f33ec7bSAndrew Thompson } \ 26663d0a65c8SRoman Kurakin m->m_pkthdr.rcvif = iface; \ 266793c9d319SKyle Evans if ((iface) == ifp) { \ 266893c9d319SKyle Evans /* Skip bridge processing... src == dest */ \ 266993c9d319SKyle Evans return (m); \ 267093c9d319SKyle Evans } \ 267193c9d319SKyle Evans /* It's passing over or to the bridge, locally. */ \ 267293c9d319SKyle Evans ETHER_BPF_MTAP(bifp, m); \ 267393c9d319SKyle Evans if_inc_counter(bifp, IFCOUNTER_IPACKETS, 1); \ 267493c9d319SKyle Evans if_inc_counter(bifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);\ 2675d862b165SMark Johnston /* Hand the packet over to netmap if necessary. */ \ 2676d862b165SMark Johnston GRAB_FOR_NETMAP(bifp, m); \ 267793c9d319SKyle Evans /* Filter on the physical interface. */ \ 2678ef84dd8fSLexi Winter if (V_pfil_local_phys && PFIL_HOOKED_IN_46) { \ 267993c9d319SKyle Evans if (bridge_pfil(&m, NULL, ifp, \ 268093c9d319SKyle Evans PFIL_IN) != 0 || m == NULL) { \ 268193c9d319SKyle Evans return (NULL); \ 268293c9d319SKyle Evans } \ 268393c9d319SKyle Evans } \ 2684d8b98543SKyle Evans if ((iface) != bifp) \ 2685d8b98543SKyle Evans ETHER_BPF_MTAP(iface, m); \ 26863d0a65c8SRoman Kurakin return (m); \ 26873d0a65c8SRoman Kurakin } \ 26883d0a65c8SRoman Kurakin \ 26893d0a65c8SRoman Kurakin /* We just received a packet that we sent out. */ \ 269082bbdde4SMark Johnston if (memcmp(IF_LLADDR(iface), eh->ether_shost, ETHER_ADDR_LEN) == 0 || \ 269182bbdde4SMark Johnston CARP_CHECK_WE_ARE_SRC(iface)) { \ 26923d0a65c8SRoman Kurakin m_freem(m); \ 26933d0a65c8SRoman Kurakin return (NULL); \ 269431997bf2SAndrew Thompson } 269531997bf2SAndrew Thompson 26963d0a65c8SRoman Kurakin /* 2697fdf229b1SAndrew Thompson * Unicast. Make sure it's not for the bridge. 2698fdf229b1SAndrew Thompson */ 2699fdf229b1SAndrew Thompson do { GRAB_OUR_PACKETS(bifp) } while (0); 2700fdf229b1SAndrew Thompson 2701fdf229b1SAndrew Thompson /* 27023d0a65c8SRoman Kurakin * Give a chance for ifp at first priority. This will help when the 27033d0a65c8SRoman Kurakin * packet comes through the interface like VLAN's with the same MACs 27043d0a65c8SRoman Kurakin * on several interfaces from the same bridge. This also will save 27053d0a65c8SRoman Kurakin * some CPU cycles in case the destination interface and the input 27063d0a65c8SRoman Kurakin * interface (eq ifp) are the same. 27073d0a65c8SRoman Kurakin */ 27083d0a65c8SRoman Kurakin do { GRAB_OUR_PACKETS(ifp) } while (0); 27093d0a65c8SRoman Kurakin 27103d0a65c8SRoman Kurakin /* Now check the all bridge members. */ 2711dd00a42aSKristof Provost CK_LIST_FOREACH(bif2, &sc->sc_iflist, bif_next) { 27123d0a65c8SRoman Kurakin GRAB_OUR_PACKETS(bif2->bif_ifp) 271331997bf2SAndrew Thompson } 27143d0a65c8SRoman Kurakin 271582bbdde4SMark Johnston #undef CARP_CHECK_WE_ARE_DST 271682bbdde4SMark Johnston #undef CARP_CHECK_WE_ARE_SRC 2717d862b165SMark Johnston #undef GRAB_FOR_NETMAP 27183d0a65c8SRoman Kurakin #undef GRAB_OUR_PACKETS 271931997bf2SAndrew Thompson 272031997bf2SAndrew Thompson /* Perform the bridge forwarding function. */ 272185ce7297SAndrew Thompson bridge_forward(sc, bif, m); 272231997bf2SAndrew Thompson 272331997bf2SAndrew Thompson return (NULL); 272431997bf2SAndrew Thompson } 272531997bf2SAndrew Thompson 272631997bf2SAndrew Thompson /* 2727d862b165SMark Johnston * Inject a packet back into the host ethernet stack. This will generally only 2728d862b165SMark Johnston * be used by netmap when an application writes to the host TX ring. The 2729d862b165SMark Johnston * M_BRIDGE_INJECT flag ensures that the packet is re-routed to the bridge 2730d862b165SMark Johnston * interface after ethernet processing. 2731d862b165SMark Johnston */ 2732d862b165SMark Johnston static void 2733d862b165SMark Johnston bridge_inject(struct ifnet *ifp, struct mbuf *m) 2734d862b165SMark Johnston { 2735d862b165SMark Johnston struct bridge_softc *sc; 2736d862b165SMark Johnston 2737d862b165SMark Johnston KASSERT((if_getcapenable(ifp) & IFCAP_NETMAP) != 0, 2738d862b165SMark Johnston ("%s: iface %s is not running in netmap mode", 2739d862b165SMark Johnston __func__, if_name(ifp))); 2740d862b165SMark Johnston KASSERT((m->m_flags & M_BRIDGE_INJECT) == 0, 2741d862b165SMark Johnston ("%s: mbuf %p has M_BRIDGE_INJECT set", __func__, m)); 2742d862b165SMark Johnston 2743d862b165SMark Johnston m->m_flags |= M_BRIDGE_INJECT; 2744d862b165SMark Johnston sc = if_getsoftc(ifp); 2745d862b165SMark Johnston sc->sc_if_input(ifp, m); 2746d862b165SMark Johnston } 2747d862b165SMark Johnston 2748d862b165SMark Johnston /* 274931997bf2SAndrew Thompson * bridge_broadcast: 275031997bf2SAndrew Thompson * 275131997bf2SAndrew Thompson * Send a frame to all interfaces that are members of 275231997bf2SAndrew Thompson * the bridge, except for the one on which the packet 275331997bf2SAndrew Thompson * arrived. 275431997bf2SAndrew Thompson * 275531997bf2SAndrew Thompson * NOTE: Releases the lock on return. 275631997bf2SAndrew Thompson */ 27576b32f3d3SAndrew Thompson static void 275831997bf2SAndrew Thompson bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if, 2759d5edd47eSAndrew Thompson struct mbuf *m, int runfilt) 276031997bf2SAndrew Thompson { 276185ce7297SAndrew Thompson struct bridge_iflist *dbif, *sbif; 276231997bf2SAndrew Thompson struct mbuf *mc; 276331997bf2SAndrew Thompson struct ifnet *dst_if; 2764fffd27e5SKristof Provost int used = 0, i; 2765fffd27e5SKristof Provost 2766fffd27e5SKristof Provost NET_EPOCH_ASSERT(); 276731997bf2SAndrew Thompson 276885ce7297SAndrew Thompson sbif = bridge_lookup_member_if(sc, src_if); 276985ce7297SAndrew Thompson 2770ea32e732SAndrew Thompson /* Filter on the bridge interface before broadcasting */ 2771ef84dd8fSLexi Winter if (runfilt && PFIL_HOOKED_OUT_46) { 2772ea32e732SAndrew Thompson if (bridge_pfil(&m, sc->sc_ifp, NULL, PFIL_OUT) != 0) 2773fffd27e5SKristof Provost return; 2774ea32e732SAndrew Thompson if (m == NULL) 2775fffd27e5SKristof Provost return; 2776ea32e732SAndrew Thompson } 2777ea32e732SAndrew Thompson 2778dd00a42aSKristof Provost CK_LIST_FOREACH(dbif, &sc->sc_iflist, bif_next) { 277985ce7297SAndrew Thompson dst_if = dbif->bif_ifp; 278031997bf2SAndrew Thompson if (dst_if == src_if) 278131997bf2SAndrew Thompson continue; 278231997bf2SAndrew Thompson 278385ce7297SAndrew Thompson /* Private segments can not talk to each other */ 278485ce7297SAndrew Thompson if (sbif && (sbif->bif_flags & dbif->bif_flags & IFBIF_PRIVATE)) 278531997bf2SAndrew Thompson continue; 278631997bf2SAndrew Thompson 278785ce7297SAndrew Thompson if ((dbif->bif_flags & IFBIF_STP) && 278885ce7297SAndrew Thompson dbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) 278985ce7297SAndrew Thompson continue; 279085ce7297SAndrew Thompson 279185ce7297SAndrew Thompson if ((dbif->bif_flags & IFBIF_DISCOVER) == 0 && 279231997bf2SAndrew Thompson (m->m_flags & (M_BCAST|M_MCAST)) == 0) 279331997bf2SAndrew Thompson continue; 279431997bf2SAndrew Thompson 279513f4c340SRobert Watson if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) 279631997bf2SAndrew Thompson continue; 279731997bf2SAndrew Thompson 2798dd00a42aSKristof Provost if (CK_LIST_NEXT(dbif, bif_next) == NULL) { 279931997bf2SAndrew Thompson mc = m; 280031997bf2SAndrew Thompson used = 1; 280131997bf2SAndrew Thompson } else { 2802eb1b1807SGleb Smirnoff mc = m_dup(m, M_NOWAIT); 280331997bf2SAndrew Thompson if (mc == NULL) { 28043751dddbSGleb Smirnoff if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); 280531997bf2SAndrew Thompson continue; 280631997bf2SAndrew Thompson } 280731997bf2SAndrew Thompson } 280831997bf2SAndrew Thompson 2809ea32e732SAndrew Thompson /* 2810ea32e732SAndrew Thompson * Filter on the output interface. Pass a NULL bridge interface 2811ea32e732SAndrew Thompson * pointer so we do not redundantly filter on the bridge for 2812ea32e732SAndrew Thompson * each interface we broadcast on. 2813ea32e732SAndrew Thompson */ 2814ef84dd8fSLexi Winter if (runfilt && PFIL_HOOKED_OUT_46) { 28155cb7f13aSRoman Kurakin if (used == 0) { 28165cb7f13aSRoman Kurakin /* Keep the layer3 header aligned */ 28175cb7f13aSRoman Kurakin i = min(mc->m_pkthdr.len, max_protohdr); 28185cb7f13aSRoman Kurakin mc = m_copyup(mc, i, ETHER_ALIGN); 28195cb7f13aSRoman Kurakin if (mc == NULL) { 28203751dddbSGleb Smirnoff if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); 28215cb7f13aSRoman Kurakin continue; 28225cb7f13aSRoman Kurakin } 28235cb7f13aSRoman Kurakin } 2824bb4b5f54SAndrew Thompson if (bridge_pfil(&mc, NULL, dst_if, PFIL_OUT) != 0) 2825bb4b5f54SAndrew Thompson continue; 2826bb4b5f54SAndrew Thompson if (mc == NULL) 2827bb4b5f54SAndrew Thompson continue; 2828ea32e732SAndrew Thompson } 2829ea32e732SAndrew Thompson 2830ea32e732SAndrew Thompson bridge_enqueue(sc, dst_if, mc); 283131997bf2SAndrew Thompson } 283231997bf2SAndrew Thompson if (used == 0) 283331997bf2SAndrew Thompson m_freem(m); 283431997bf2SAndrew Thompson } 283531997bf2SAndrew Thompson 283631997bf2SAndrew Thompson /* 283791f6764eSAndrew Thompson * bridge_span: 283891f6764eSAndrew Thompson * 283991f6764eSAndrew Thompson * Duplicate a packet out one or more interfaces that are in span mode, 284091f6764eSAndrew Thompson * the original mbuf is unmodified. 284191f6764eSAndrew Thompson */ 284291f6764eSAndrew Thompson static void 284391f6764eSAndrew Thompson bridge_span(struct bridge_softc *sc, struct mbuf *m) 284491f6764eSAndrew Thompson { 284591f6764eSAndrew Thompson struct bridge_iflist *bif; 284691f6764eSAndrew Thompson struct ifnet *dst_if; 284791f6764eSAndrew Thompson struct mbuf *mc; 284891f6764eSAndrew Thompson 2849fffd27e5SKristof Provost NET_EPOCH_ASSERT(); 2850fffd27e5SKristof Provost 2851dd00a42aSKristof Provost if (CK_LIST_EMPTY(&sc->sc_spanlist)) 285291f6764eSAndrew Thompson return; 285391f6764eSAndrew Thompson 2854dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) { 285591f6764eSAndrew Thompson dst_if = bif->bif_ifp; 285691f6764eSAndrew Thompson 285791f6764eSAndrew Thompson if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) 285891f6764eSAndrew Thompson continue; 285991f6764eSAndrew Thompson 286036637dd1SKristof Provost mc = m_dup(m, M_NOWAIT); 286191f6764eSAndrew Thompson if (mc == NULL) { 28623751dddbSGleb Smirnoff if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); 286391f6764eSAndrew Thompson continue; 286491f6764eSAndrew Thompson } 286591f6764eSAndrew Thompson 286691f6764eSAndrew Thompson bridge_enqueue(sc, dst_if, mc); 286791f6764eSAndrew Thompson } 286891f6764eSAndrew Thompson } 286991f6764eSAndrew Thompson 287091f6764eSAndrew Thompson /* 287131997bf2SAndrew Thompson * bridge_rtupdate: 287231997bf2SAndrew Thompson * 287331997bf2SAndrew Thompson * Add a bridge routing entry. 287431997bf2SAndrew Thompson */ 28756b32f3d3SAndrew Thompson static int 287622dcc3c1SAndrew Thompson bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, uint16_t vlan, 28773df7fad0SAndrew Thompson struct bridge_iflist *bif, int setflags, uint8_t flags) 287831997bf2SAndrew Thompson { 287931997bf2SAndrew Thompson struct bridge_rtnode *brt; 28802d3614fbSZhenlei Huang struct bridge_iflist *obif; 288131997bf2SAndrew Thompson int error; 288231997bf2SAndrew Thompson 28834af1bd81SKristof Provost BRIDGE_LOCK_OR_NET_EPOCH_ASSERT(sc); 288431997bf2SAndrew Thompson 28855f33ec7bSAndrew Thompson /* Check the source address is valid and not multicast. */ 28865f33ec7bSAndrew Thompson if (ETHER_IS_MULTICAST(dst) || 28875f33ec7bSAndrew Thompson (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 && 28885f33ec7bSAndrew Thompson dst[3] == 0 && dst[4] == 0 && dst[5] == 0) != 0) 28895f33ec7bSAndrew Thompson return (EINVAL); 28905f33ec7bSAndrew Thompson 289131997bf2SAndrew Thompson /* 289231997bf2SAndrew Thompson * A route for this destination might already exist. If so, 289331997bf2SAndrew Thompson * update it, otherwise create a new one. 289431997bf2SAndrew Thompson */ 289522dcc3c1SAndrew Thompson if ((brt = bridge_rtnode_lookup(sc, dst, vlan)) == NULL) { 28964af1bd81SKristof Provost BRIDGE_RT_LOCK(sc); 2897fffd27e5SKristof Provost 2898fffd27e5SKristof Provost /* Check again, now that we have the lock. There could have 2899fffd27e5SKristof Provost * been a race and we only want to insert this once. */ 29002d3614fbSZhenlei Huang if (bridge_rtnode_lookup(sc, dst, vlan) != NULL) { 29014af1bd81SKristof Provost BRIDGE_RT_UNLOCK(sc); 2902fffd27e5SKristof Provost return (0); 2903fffd27e5SKristof Provost } 2904fffd27e5SKristof Provost 290551383c37SAndrew Thompson if (sc->sc_brtcnt >= sc->sc_brtmax) { 290651383c37SAndrew Thompson sc->sc_brtexceeded++; 29074af1bd81SKristof Provost BRIDGE_RT_UNLOCK(sc); 290831997bf2SAndrew Thompson return (ENOSPC); 290951383c37SAndrew Thompson } 29105f33ec7bSAndrew Thompson /* Check per interface address limits (if enabled) */ 29115f33ec7bSAndrew Thompson if (bif->bif_addrmax && bif->bif_addrcnt >= bif->bif_addrmax) { 29125f33ec7bSAndrew Thompson bif->bif_addrexceeded++; 29134af1bd81SKristof Provost BRIDGE_RT_UNLOCK(sc); 29145f33ec7bSAndrew Thompson return (ENOSPC); 29155f33ec7bSAndrew Thompson } 291631997bf2SAndrew Thompson 291731997bf2SAndrew Thompson /* 291831997bf2SAndrew Thompson * Allocate a new bridge forwarding node, and 291931997bf2SAndrew Thompson * initialize the expiration time and Ethernet 292031997bf2SAndrew Thompson * address. 292131997bf2SAndrew Thompson */ 2922a87407ffSKyle Evans brt = uma_zalloc(V_bridge_rtnode_zone, M_NOWAIT | M_ZERO); 2923fffd27e5SKristof Provost if (brt == NULL) { 29244af1bd81SKristof Provost BRIDGE_RT_UNLOCK(sc); 292531997bf2SAndrew Thompson return (ENOMEM); 2926fffd27e5SKristof Provost } 2927fffd27e5SKristof Provost brt->brt_vnet = curvnet; 292831997bf2SAndrew Thompson 29293df7fad0SAndrew Thompson if (bif->bif_flags & IFBIF_STICKY) 29303df7fad0SAndrew Thompson brt->brt_flags = IFBAF_STICKY; 29313df7fad0SAndrew Thompson else 293231997bf2SAndrew Thompson brt->brt_flags = IFBAF_DYNAMIC; 29333df7fad0SAndrew Thompson 293431997bf2SAndrew Thompson memcpy(brt->brt_addr, dst, ETHER_ADDR_LEN); 293522dcc3c1SAndrew Thompson brt->brt_vlan = vlan; 293631997bf2SAndrew Thompson 2937f3546eacSKristof Provost brt->brt_dst = bif; 293831997bf2SAndrew Thompson if ((error = bridge_rtnode_insert(sc, brt)) != 0) { 2939a87407ffSKyle Evans uma_zfree(V_bridge_rtnode_zone, brt); 29404af1bd81SKristof Provost BRIDGE_RT_UNLOCK(sc); 294131997bf2SAndrew Thompson return (error); 294231997bf2SAndrew Thompson } 29435f33ec7bSAndrew Thompson bif->bif_addrcnt++; 2944fffd27e5SKristof Provost 29454af1bd81SKristof Provost BRIDGE_RT_UNLOCK(sc); 294631997bf2SAndrew Thompson } 294731997bf2SAndrew Thompson 29485f33ec7bSAndrew Thompson if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC && 29492d3614fbSZhenlei Huang (obif = brt->brt_dst) != bif) { 2950f3546eacSKristof Provost MPASS(obif != NULL); 2951f3546eacSKristof Provost 29524af1bd81SKristof Provost BRIDGE_RT_LOCK(sc); 29535f33ec7bSAndrew Thompson brt->brt_dst->bif_addrcnt--; 29545f33ec7bSAndrew Thompson brt->brt_dst = bif; 29555f33ec7bSAndrew Thompson brt->brt_dst->bif_addrcnt++; 29564af1bd81SKristof Provost BRIDGE_RT_UNLOCK(sc); 29572d3614fbSZhenlei Huang 29582d3614fbSZhenlei Huang if (V_log_mac_flap && 29592d3614fbSZhenlei Huang ppsratecheck(&V_log_last, &V_log_count, V_log_interval)) { 29602d3614fbSZhenlei Huang log(LOG_NOTICE, 29619af6f426SZhenlei Huang "%s: mac address %6D vlan %d moved from %s to %s\n", 29622d3614fbSZhenlei Huang sc->sc_ifp->if_xname, 29639af6f426SZhenlei Huang &brt->brt_addr[0], ":", 29642d3614fbSZhenlei Huang brt->brt_vlan, 29652d3614fbSZhenlei Huang obif->bif_ifp->if_xname, 29662d3614fbSZhenlei Huang bif->bif_ifp->if_xname); 29672d3614fbSZhenlei Huang } 29685f33ec7bSAndrew Thompson } 29695f33ec7bSAndrew Thompson 2970f0feaf4fSAndrew Thompson if ((flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) 297194e45ae5SAndrew Thompson brt->brt_expire = time_uptime + sc->sc_brttimeout; 2972ef9ac7c4SAndrew Thompson if (setflags) 2973ef9ac7c4SAndrew Thompson brt->brt_flags = flags; 297431997bf2SAndrew Thompson 297531997bf2SAndrew Thompson return (0); 297631997bf2SAndrew Thompson } 297731997bf2SAndrew Thompson 297831997bf2SAndrew Thompson /* 297931997bf2SAndrew Thompson * bridge_rtlookup: 298031997bf2SAndrew Thompson * 298131997bf2SAndrew Thompson * Lookup the destination interface for an address. 298231997bf2SAndrew Thompson */ 29834c843479SAndrew Thompson static struct ifnet * 298422dcc3c1SAndrew Thompson bridge_rtlookup(struct bridge_softc *sc, const uint8_t *addr, uint16_t vlan) 298531997bf2SAndrew Thompson { 298631997bf2SAndrew Thompson struct bridge_rtnode *brt; 298731997bf2SAndrew Thompson 2988fffd27e5SKristof Provost NET_EPOCH_ASSERT(); 298931997bf2SAndrew Thompson 299022dcc3c1SAndrew Thompson if ((brt = bridge_rtnode_lookup(sc, addr, vlan)) == NULL) 299131997bf2SAndrew Thompson return (NULL); 299231997bf2SAndrew Thompson 299331997bf2SAndrew Thompson return (brt->brt_ifp); 299431997bf2SAndrew Thompson } 299531997bf2SAndrew Thompson 299631997bf2SAndrew Thompson /* 299731997bf2SAndrew Thompson * bridge_rttrim: 299831997bf2SAndrew Thompson * 299931997bf2SAndrew Thompson * Trim the routine table so that we have a number 300031997bf2SAndrew Thompson * of routing entries less than or equal to the 300131997bf2SAndrew Thompson * maximum number. 300231997bf2SAndrew Thompson */ 30036b32f3d3SAndrew Thompson static void 300431997bf2SAndrew Thompson bridge_rttrim(struct bridge_softc *sc) 300531997bf2SAndrew Thompson { 300631997bf2SAndrew Thompson struct bridge_rtnode *brt, *nbrt; 300731997bf2SAndrew Thompson 3008fffd27e5SKristof Provost NET_EPOCH_ASSERT(); 30094af1bd81SKristof Provost BRIDGE_RT_LOCK_ASSERT(sc); 301031997bf2SAndrew Thompson 301131997bf2SAndrew Thompson /* Make sure we actually need to do this. */ 301231997bf2SAndrew Thompson if (sc->sc_brtcnt <= sc->sc_brtmax) 301331997bf2SAndrew Thompson return; 301431997bf2SAndrew Thompson 301531997bf2SAndrew Thompson /* Force an aging cycle; this might trim enough addresses. */ 301631997bf2SAndrew Thompson bridge_rtage(sc); 301731997bf2SAndrew Thompson if (sc->sc_brtcnt <= sc->sc_brtmax) 301831997bf2SAndrew Thompson return; 301931997bf2SAndrew Thompson 3020dd00a42aSKristof Provost CK_LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { 302131997bf2SAndrew Thompson if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { 302231997bf2SAndrew Thompson bridge_rtnode_destroy(sc, brt); 302331997bf2SAndrew Thompson if (sc->sc_brtcnt <= sc->sc_brtmax) 302431997bf2SAndrew Thompson return; 302531997bf2SAndrew Thompson } 302631997bf2SAndrew Thompson } 302731997bf2SAndrew Thompson } 302831997bf2SAndrew Thompson 302931997bf2SAndrew Thompson /* 303031997bf2SAndrew Thompson * bridge_timer: 303131997bf2SAndrew Thompson * 303231997bf2SAndrew Thompson * Aging timer for the bridge. 303331997bf2SAndrew Thompson */ 30346b32f3d3SAndrew Thompson static void 303531997bf2SAndrew Thompson bridge_timer(void *arg) 303631997bf2SAndrew Thompson { 303731997bf2SAndrew Thompson struct bridge_softc *sc = arg; 303831997bf2SAndrew Thompson 30394af1bd81SKristof Provost BRIDGE_RT_LOCK_ASSERT(sc); 304023e76431SAndrew Thompson 3041a87407ffSKyle Evans /* Destruction of rtnodes requires a proper vnet context */ 3042a87407ffSKyle Evans CURVNET_SET(sc->sc_ifp->if_vnet); 304331997bf2SAndrew Thompson bridge_rtage(sc); 304431997bf2SAndrew Thompson 304513f4c340SRobert Watson if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) 304631997bf2SAndrew Thompson callout_reset(&sc->sc_brcallout, 304731997bf2SAndrew Thompson bridge_rtable_prune_period * hz, bridge_timer, sc); 3048a87407ffSKyle Evans CURVNET_RESTORE(); 304931997bf2SAndrew Thompson } 305031997bf2SAndrew Thompson 305131997bf2SAndrew Thompson /* 305231997bf2SAndrew Thompson * bridge_rtage: 305331997bf2SAndrew Thompson * 305431997bf2SAndrew Thompson * Perform an aging cycle. 305531997bf2SAndrew Thompson */ 30566b32f3d3SAndrew Thompson static void 305731997bf2SAndrew Thompson bridge_rtage(struct bridge_softc *sc) 305831997bf2SAndrew Thompson { 305931997bf2SAndrew Thompson struct bridge_rtnode *brt, *nbrt; 306031997bf2SAndrew Thompson 30614af1bd81SKristof Provost BRIDGE_RT_LOCK_ASSERT(sc); 306231997bf2SAndrew Thompson 3063dd00a42aSKristof Provost CK_LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { 306431997bf2SAndrew Thompson if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { 3065bc9f74c7SAndrew Thompson if (time_uptime >= brt->brt_expire) 306631997bf2SAndrew Thompson bridge_rtnode_destroy(sc, brt); 306731997bf2SAndrew Thompson } 306831997bf2SAndrew Thompson } 306931997bf2SAndrew Thompson } 307031997bf2SAndrew Thompson 307131997bf2SAndrew Thompson /* 307231997bf2SAndrew Thompson * bridge_rtflush: 307331997bf2SAndrew Thompson * 307431997bf2SAndrew Thompson * Remove all dynamic addresses from the bridge. 307531997bf2SAndrew Thompson */ 30766b32f3d3SAndrew Thompson static void 307731997bf2SAndrew Thompson bridge_rtflush(struct bridge_softc *sc, int full) 307831997bf2SAndrew Thompson { 307931997bf2SAndrew Thompson struct bridge_rtnode *brt, *nbrt; 308031997bf2SAndrew Thompson 30814af1bd81SKristof Provost BRIDGE_RT_LOCK_ASSERT(sc); 308231997bf2SAndrew Thompson 3083dd00a42aSKristof Provost CK_LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { 308431997bf2SAndrew Thompson if (full || (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) 308531997bf2SAndrew Thompson bridge_rtnode_destroy(sc, brt); 308631997bf2SAndrew Thompson } 308731997bf2SAndrew Thompson } 308831997bf2SAndrew Thompson 308931997bf2SAndrew Thompson /* 309031997bf2SAndrew Thompson * bridge_rtdaddr: 309131997bf2SAndrew Thompson * 309231997bf2SAndrew Thompson * Remove an address from the table. 309331997bf2SAndrew Thompson */ 30946b32f3d3SAndrew Thompson static int 309522dcc3c1SAndrew Thompson bridge_rtdaddr(struct bridge_softc *sc, const uint8_t *addr, uint16_t vlan) 309631997bf2SAndrew Thompson { 309731997bf2SAndrew Thompson struct bridge_rtnode *brt; 309822dcc3c1SAndrew Thompson int found = 0; 309931997bf2SAndrew Thompson 31004af1bd81SKristof Provost BRIDGE_RT_LOCK(sc); 310131997bf2SAndrew Thompson 310222dcc3c1SAndrew Thompson /* 3103b0e38a13SKristof Provost * If vlan is DOT1Q_VID_RSVD_IMPL then we want to delete for all vlans 3104b0e38a13SKristof Provost * so the lookup may return more than one. 310522dcc3c1SAndrew Thompson */ 310622dcc3c1SAndrew Thompson while ((brt = bridge_rtnode_lookup(sc, addr, vlan)) != NULL) { 310731997bf2SAndrew Thompson bridge_rtnode_destroy(sc, brt); 310822dcc3c1SAndrew Thompson found = 1; 310922dcc3c1SAndrew Thompson } 311022dcc3c1SAndrew Thompson 31114af1bd81SKristof Provost BRIDGE_RT_UNLOCK(sc); 31124af1bd81SKristof Provost 311322dcc3c1SAndrew Thompson return (found ? 0 : ENOENT); 311431997bf2SAndrew Thompson } 311531997bf2SAndrew Thompson 311631997bf2SAndrew Thompson /* 311731997bf2SAndrew Thompson * bridge_rtdelete: 311831997bf2SAndrew Thompson * 311931997bf2SAndrew Thompson * Delete routes to a speicifc member interface. 312031997bf2SAndrew Thompson */ 31219674cf0eSAndrew Thompson static void 312231997bf2SAndrew Thompson bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp, int full) 312331997bf2SAndrew Thompson { 312431997bf2SAndrew Thompson struct bridge_rtnode *brt, *nbrt; 312531997bf2SAndrew Thompson 31264af1bd81SKristof Provost BRIDGE_RT_LOCK_ASSERT(sc); 312731997bf2SAndrew Thompson 3128dd00a42aSKristof Provost CK_LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { 312931997bf2SAndrew Thompson if (brt->brt_ifp == ifp && (full || 313031997bf2SAndrew Thompson (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)) 313131997bf2SAndrew Thompson bridge_rtnode_destroy(sc, brt); 313231997bf2SAndrew Thompson } 313331997bf2SAndrew Thompson } 313431997bf2SAndrew Thompson 313531997bf2SAndrew Thompson /* 313631997bf2SAndrew Thompson * bridge_rtable_init: 313731997bf2SAndrew Thompson * 313831997bf2SAndrew Thompson * Initialize the route table for this bridge. 313931997bf2SAndrew Thompson */ 31403e92ee8aSAndrew Thompson static void 314131997bf2SAndrew Thompson bridge_rtable_init(struct bridge_softc *sc) 314231997bf2SAndrew Thompson { 314331997bf2SAndrew Thompson int i; 314431997bf2SAndrew Thompson 314531997bf2SAndrew Thompson sc->sc_rthash = malloc(sizeof(*sc->sc_rthash) * BRIDGE_RTHASH_SIZE, 31463e92ee8aSAndrew Thompson M_DEVBUF, M_WAITOK); 314731997bf2SAndrew Thompson 314831997bf2SAndrew Thompson for (i = 0; i < BRIDGE_RTHASH_SIZE; i++) 3149dd00a42aSKristof Provost CK_LIST_INIT(&sc->sc_rthash[i]); 315031997bf2SAndrew Thompson 315131997bf2SAndrew Thompson sc->sc_rthash_key = arc4random(); 3152dd00a42aSKristof Provost CK_LIST_INIT(&sc->sc_rtlist); 315331997bf2SAndrew Thompson } 315431997bf2SAndrew Thompson 315531997bf2SAndrew Thompson /* 315631997bf2SAndrew Thompson * bridge_rtable_fini: 315731997bf2SAndrew Thompson * 315831997bf2SAndrew Thompson * Deconstruct the route table for this bridge. 315931997bf2SAndrew Thompson */ 31606b32f3d3SAndrew Thompson static void 316131997bf2SAndrew Thompson bridge_rtable_fini(struct bridge_softc *sc) 316231997bf2SAndrew Thompson { 316331997bf2SAndrew Thompson 31645f33ec7bSAndrew Thompson KASSERT(sc->sc_brtcnt == 0, 31655f33ec7bSAndrew Thompson ("%s: %d bridge routes referenced", __func__, sc->sc_brtcnt)); 316631997bf2SAndrew Thompson free(sc->sc_rthash, M_DEVBUF); 316731997bf2SAndrew Thompson } 316831997bf2SAndrew Thompson 316931997bf2SAndrew Thompson /* 317031997bf2SAndrew Thompson * The following hash function is adapted from "Hash Functions" by Bob Jenkins 317131997bf2SAndrew Thompson * ("Algorithm Alley", Dr. Dobbs Journal, September 1997). 317231997bf2SAndrew Thompson */ 317331997bf2SAndrew Thompson #define mix(a, b, c) \ 317431997bf2SAndrew Thompson do { \ 317531997bf2SAndrew Thompson a -= b; a -= c; a ^= (c >> 13); \ 317631997bf2SAndrew Thompson b -= c; b -= a; b ^= (a << 8); \ 317731997bf2SAndrew Thompson c -= a; c -= b; c ^= (b >> 13); \ 317831997bf2SAndrew Thompson a -= b; a -= c; a ^= (c >> 12); \ 317931997bf2SAndrew Thompson b -= c; b -= a; b ^= (a << 16); \ 318031997bf2SAndrew Thompson c -= a; c -= b; c ^= (b >> 5); \ 318131997bf2SAndrew Thompson a -= b; a -= c; a ^= (c >> 3); \ 318231997bf2SAndrew Thompson b -= c; b -= a; b ^= (a << 10); \ 318331997bf2SAndrew Thompson c -= a; c -= b; c ^= (b >> 15); \ 318431997bf2SAndrew Thompson } while (/*CONSTCOND*/0) 318531997bf2SAndrew Thompson 318631997bf2SAndrew Thompson static __inline uint32_t 318731997bf2SAndrew Thompson bridge_rthash(struct bridge_softc *sc, const uint8_t *addr) 318831997bf2SAndrew Thompson { 318931997bf2SAndrew Thompson uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = sc->sc_rthash_key; 319031997bf2SAndrew Thompson 319131997bf2SAndrew Thompson b += addr[5] << 8; 319231997bf2SAndrew Thompson b += addr[4]; 319331997bf2SAndrew Thompson a += addr[3] << 24; 319431997bf2SAndrew Thompson a += addr[2] << 16; 319531997bf2SAndrew Thompson a += addr[1] << 8; 319631997bf2SAndrew Thompson a += addr[0]; 319731997bf2SAndrew Thompson 319831997bf2SAndrew Thompson mix(a, b, c); 319931997bf2SAndrew Thompson 320031997bf2SAndrew Thompson return (c & BRIDGE_RTHASH_MASK); 320131997bf2SAndrew Thompson } 320231997bf2SAndrew Thompson 320331997bf2SAndrew Thompson #undef mix 320431997bf2SAndrew Thompson 32056637e0f3SAndrew Thompson static int 32066637e0f3SAndrew Thompson bridge_rtnode_addr_cmp(const uint8_t *a, const uint8_t *b) 32076637e0f3SAndrew Thompson { 32086637e0f3SAndrew Thompson int i, d; 32096637e0f3SAndrew Thompson 32106637e0f3SAndrew Thompson for (i = 0, d = 0; i < ETHER_ADDR_LEN && d == 0; i++) { 32116637e0f3SAndrew Thompson d = ((int)a[i]) - ((int)b[i]); 32126637e0f3SAndrew Thompson } 32136637e0f3SAndrew Thompson 32146637e0f3SAndrew Thompson return (d); 32156637e0f3SAndrew Thompson } 32166637e0f3SAndrew Thompson 321731997bf2SAndrew Thompson /* 321831997bf2SAndrew Thompson * bridge_rtnode_lookup: 321931997bf2SAndrew Thompson * 322022dcc3c1SAndrew Thompson * Look up a bridge route node for the specified destination. Compare the 322122dcc3c1SAndrew Thompson * vlan id or if zero then just return the first match. 322231997bf2SAndrew Thompson */ 32234c843479SAndrew Thompson static struct bridge_rtnode * 322422dcc3c1SAndrew Thompson bridge_rtnode_lookup(struct bridge_softc *sc, const uint8_t *addr, uint16_t vlan) 322531997bf2SAndrew Thompson { 322631997bf2SAndrew Thompson struct bridge_rtnode *brt; 322731997bf2SAndrew Thompson uint32_t hash; 322831997bf2SAndrew Thompson int dir; 322931997bf2SAndrew Thompson 32304af1bd81SKristof Provost BRIDGE_RT_LOCK_OR_NET_EPOCH_ASSERT(sc); 323131997bf2SAndrew Thompson 323231997bf2SAndrew Thompson hash = bridge_rthash(sc, addr); 3233dd00a42aSKristof Provost CK_LIST_FOREACH(brt, &sc->sc_rthash[hash], brt_hash) { 32346637e0f3SAndrew Thompson dir = bridge_rtnode_addr_cmp(addr, brt->brt_addr); 3235b0e38a13SKristof Provost if (dir == 0 && (brt->brt_vlan == vlan || vlan == DOT1Q_VID_RSVD_IMPL)) 323631997bf2SAndrew Thompson return (brt); 323731997bf2SAndrew Thompson if (dir > 0) 323831997bf2SAndrew Thompson return (NULL); 323931997bf2SAndrew Thompson } 324031997bf2SAndrew Thompson 324131997bf2SAndrew Thompson return (NULL); 324231997bf2SAndrew Thompson } 324331997bf2SAndrew Thompson 324431997bf2SAndrew Thompson /* 324531997bf2SAndrew Thompson * bridge_rtnode_insert: 324631997bf2SAndrew Thompson * 324731997bf2SAndrew Thompson * Insert the specified bridge node into the route table. We 324831997bf2SAndrew Thompson * assume the entry is not already in the table. 324931997bf2SAndrew Thompson */ 32506b32f3d3SAndrew Thompson static int 325131997bf2SAndrew Thompson bridge_rtnode_insert(struct bridge_softc *sc, struct bridge_rtnode *brt) 325231997bf2SAndrew Thompson { 325331997bf2SAndrew Thompson struct bridge_rtnode *lbrt; 325431997bf2SAndrew Thompson uint32_t hash; 325531997bf2SAndrew Thompson int dir; 325631997bf2SAndrew Thompson 32574af1bd81SKristof Provost BRIDGE_RT_LOCK_ASSERT(sc); 325831997bf2SAndrew Thompson 325931997bf2SAndrew Thompson hash = bridge_rthash(sc, brt->brt_addr); 326031997bf2SAndrew Thompson 3261dd00a42aSKristof Provost lbrt = CK_LIST_FIRST(&sc->sc_rthash[hash]); 326231997bf2SAndrew Thompson if (lbrt == NULL) { 3263dd00a42aSKristof Provost CK_LIST_INSERT_HEAD(&sc->sc_rthash[hash], brt, brt_hash); 326431997bf2SAndrew Thompson goto out; 326531997bf2SAndrew Thompson } 326631997bf2SAndrew Thompson 326731997bf2SAndrew Thompson do { 32686637e0f3SAndrew Thompson dir = bridge_rtnode_addr_cmp(brt->brt_addr, lbrt->brt_addr); 326922dcc3c1SAndrew Thompson if (dir == 0 && brt->brt_vlan == lbrt->brt_vlan) 327031997bf2SAndrew Thompson return (EEXIST); 327131997bf2SAndrew Thompson if (dir > 0) { 3272dd00a42aSKristof Provost CK_LIST_INSERT_BEFORE(lbrt, brt, brt_hash); 327331997bf2SAndrew Thompson goto out; 327431997bf2SAndrew Thompson } 3275dd00a42aSKristof Provost if (CK_LIST_NEXT(lbrt, brt_hash) == NULL) { 3276dd00a42aSKristof Provost CK_LIST_INSERT_AFTER(lbrt, brt, brt_hash); 327731997bf2SAndrew Thompson goto out; 327831997bf2SAndrew Thompson } 3279dd00a42aSKristof Provost lbrt = CK_LIST_NEXT(lbrt, brt_hash); 328031997bf2SAndrew Thompson } while (lbrt != NULL); 328131997bf2SAndrew Thompson 328231997bf2SAndrew Thompson #ifdef DIAGNOSTIC 328331997bf2SAndrew Thompson panic("bridge_rtnode_insert: impossible"); 328431997bf2SAndrew Thompson #endif 328531997bf2SAndrew Thompson 328631997bf2SAndrew Thompson out: 3287dd00a42aSKristof Provost CK_LIST_INSERT_HEAD(&sc->sc_rtlist, brt, brt_list); 328831997bf2SAndrew Thompson sc->sc_brtcnt++; 328931997bf2SAndrew Thompson 329031997bf2SAndrew Thompson return (0); 329131997bf2SAndrew Thompson } 329231997bf2SAndrew Thompson 3293fffd27e5SKristof Provost static void 3294fffd27e5SKristof Provost bridge_rtnode_destroy_cb(struct epoch_context *ctx) 3295fffd27e5SKristof Provost { 3296fffd27e5SKristof Provost struct bridge_rtnode *brt; 3297fffd27e5SKristof Provost 3298fffd27e5SKristof Provost brt = __containerof(ctx, struct bridge_rtnode, brt_epoch_ctx); 3299fffd27e5SKristof Provost 3300fffd27e5SKristof Provost CURVNET_SET(brt->brt_vnet); 3301fffd27e5SKristof Provost uma_zfree(V_bridge_rtnode_zone, brt); 3302fffd27e5SKristof Provost CURVNET_RESTORE(); 3303fffd27e5SKristof Provost } 3304fffd27e5SKristof Provost 330531997bf2SAndrew Thompson /* 330631997bf2SAndrew Thompson * bridge_rtnode_destroy: 330731997bf2SAndrew Thompson * 330831997bf2SAndrew Thompson * Destroy a bridge rtnode. 330931997bf2SAndrew Thompson */ 33106b32f3d3SAndrew Thompson static void 331131997bf2SAndrew Thompson bridge_rtnode_destroy(struct bridge_softc *sc, struct bridge_rtnode *brt) 331231997bf2SAndrew Thompson { 33134af1bd81SKristof Provost BRIDGE_RT_LOCK_ASSERT(sc); 331431997bf2SAndrew Thompson 3315dd00a42aSKristof Provost CK_LIST_REMOVE(brt, brt_hash); 331631997bf2SAndrew Thompson 3317dd00a42aSKristof Provost CK_LIST_REMOVE(brt, brt_list); 331831997bf2SAndrew Thompson sc->sc_brtcnt--; 33195f33ec7bSAndrew Thompson brt->brt_dst->bif_addrcnt--; 3320fffd27e5SKristof Provost 3321fffd27e5SKristof Provost NET_EPOCH_CALL(bridge_rtnode_destroy_cb, &brt->brt_epoch_ctx); 332231997bf2SAndrew Thompson } 332331997bf2SAndrew Thompson 332431997bf2SAndrew Thompson /* 33253fab7669SAndrew Thompson * bridge_rtable_expire: 33263fab7669SAndrew Thompson * 33273fab7669SAndrew Thompson * Set the expiry time for all routes on an interface. 33283fab7669SAndrew Thompson */ 33293fab7669SAndrew Thompson static void 33303fab7669SAndrew Thompson bridge_rtable_expire(struct ifnet *ifp, int age) 33313fab7669SAndrew Thompson { 33323fab7669SAndrew Thompson struct bridge_softc *sc = ifp->if_bridge; 33333fab7669SAndrew Thompson struct bridge_rtnode *brt; 33343fab7669SAndrew Thompson 333543d3127cSKristof Provost CURVNET_SET(ifp->if_vnet); 33364af1bd81SKristof Provost BRIDGE_RT_LOCK(sc); 33373fab7669SAndrew Thompson 33383fab7669SAndrew Thompson /* 33393fab7669SAndrew Thompson * If the age is zero then flush, otherwise set all the expiry times to 33403fab7669SAndrew Thompson * age for the interface 33413fab7669SAndrew Thompson */ 33423fab7669SAndrew Thompson if (age == 0) 33433fab7669SAndrew Thompson bridge_rtdelete(sc, ifp, IFBF_FLUSHDYN); 33443fab7669SAndrew Thompson else { 3345dd00a42aSKristof Provost CK_LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { 33463fab7669SAndrew Thompson /* Cap the expiry time to 'age' */ 33473fab7669SAndrew Thompson if (brt->brt_ifp == ifp && 33483fab7669SAndrew Thompson brt->brt_expire > time_uptime + age && 33493fab7669SAndrew Thompson (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) 33503fab7669SAndrew Thompson brt->brt_expire = time_uptime + age; 33513fab7669SAndrew Thompson } 33523fab7669SAndrew Thompson } 33534af1bd81SKristof Provost BRIDGE_RT_UNLOCK(sc); 335443d3127cSKristof Provost CURVNET_RESTORE(); 33553fab7669SAndrew Thompson } 33563fab7669SAndrew Thompson 33573fab7669SAndrew Thompson /* 335873d480aeSAndrew Thompson * bridge_state_change: 335973d480aeSAndrew Thompson * 336073d480aeSAndrew Thompson * Callback from the bridgestp code when a port changes states. 336173d480aeSAndrew Thompson */ 336273d480aeSAndrew Thompson static void 336373d480aeSAndrew Thompson bridge_state_change(struct ifnet *ifp, int state) 336473d480aeSAndrew Thompson { 336573d480aeSAndrew Thompson struct bridge_softc *sc = ifp->if_bridge; 336673d480aeSAndrew Thompson static const char *stpstates[] = { 336773d480aeSAndrew Thompson "disabled", 336873d480aeSAndrew Thompson "listening", 336973d480aeSAndrew Thompson "learning", 337073d480aeSAndrew Thompson "forwarding", 337173d480aeSAndrew Thompson "blocking", 33723fab7669SAndrew Thompson "discarding" 337373d480aeSAndrew Thompson }; 337473d480aeSAndrew Thompson 33751c27e6c3SHiroki Sato CURVNET_SET(ifp->if_vnet); 3376c5127526SHiroki Sato if (V_log_stp) 337773d480aeSAndrew Thompson log(LOG_NOTICE, "%s: state changed to %s on %s\n", 337873d480aeSAndrew Thompson sc->sc_ifp->if_xname, stpstates[state], ifp->if_xname); 33791c27e6c3SHiroki Sato CURVNET_RESTORE(); 338073d480aeSAndrew Thompson } 338173d480aeSAndrew Thompson 338273d480aeSAndrew Thompson /* 338331997bf2SAndrew Thompson * Send bridge packets through pfil if they are one of the types pfil can deal 338431997bf2SAndrew Thompson * with, or if they are ARP or REVARP. (pfil will pass ARP and REVARP without 3385ea32e732SAndrew Thompson * question.) If *bifp or *ifp are NULL then packet filtering is skipped for 3386ea32e732SAndrew Thompson * that interface. 338731997bf2SAndrew Thompson */ 33886b743820SAndrew Thompson static int 33896b743820SAndrew Thompson bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir) 339031997bf2SAndrew Thompson { 339165767e61SLexi Winter int snap, error, i; 339231997bf2SAndrew Thompson struct ether_header *eh1, eh2; 339349808fa4SAndrew Thompson struct llc llc1; 339431997bf2SAndrew Thompson u_int16_t ether_type; 3395b252313fSGleb Smirnoff pfil_return_t rv; 339665767e61SLexi Winter #ifdef INET 339765767e61SLexi Winter struct ip *ip = NULL; 339865767e61SLexi Winter int hlen = 0; 339965767e61SLexi Winter #endif 340031997bf2SAndrew Thompson 340131997bf2SAndrew Thompson snap = 0; 340231997bf2SAndrew Thompson error = -1; /* Default error if not error == 0 */ 340382116c33SAndrew Thompson 34045adfb0ccSAndrew Thompson #if 0 340564cb8505SAndrew Thompson /* we may return with the IP fields swapped, ensure its not shared */ 340664cb8505SAndrew Thompson KASSERT(M_WRITABLE(*mp), ("%s: modifying a shared mbuf", __func__)); 34075adfb0ccSAndrew Thompson #endif 340864cb8505SAndrew Thompson 3409c5127526SHiroki Sato if (V_pfil_bridge == 0 && V_pfil_member == 0 && V_pfil_ipfw == 0) 3410dc1b1b7bSAndrew Thompson return (0); /* filtering is disabled */ 3411a47f91cdSAndrew Thompson 341282116c33SAndrew Thompson i = min((*mp)->m_pkthdr.len, max_protohdr); 341382116c33SAndrew Thompson if ((*mp)->m_len < i) { 341482116c33SAndrew Thompson *mp = m_pullup(*mp, i); 341582116c33SAndrew Thompson if (*mp == NULL) { 341682116c33SAndrew Thompson printf("%s: m_pullup failed\n", __func__); 3417dc1b1b7bSAndrew Thompson return (-1); 341882116c33SAndrew Thompson } 341982116c33SAndrew Thompson } 342082116c33SAndrew Thompson 342131997bf2SAndrew Thompson eh1 = mtod(*mp, struct ether_header *); 342231997bf2SAndrew Thompson ether_type = ntohs(eh1->ether_type); 342331997bf2SAndrew Thompson 342431997bf2SAndrew Thompson /* 342531997bf2SAndrew Thompson * Check for SNAP/LLC. 342631997bf2SAndrew Thompson */ 342731997bf2SAndrew Thompson if (ether_type < ETHERMTU) { 342849808fa4SAndrew Thompson struct llc *llc2 = (struct llc *)(eh1 + 1); 342931997bf2SAndrew Thompson 343031997bf2SAndrew Thompson if ((*mp)->m_len >= ETHER_HDR_LEN + 8 && 343149808fa4SAndrew Thompson llc2->llc_dsap == LLC_SNAP_LSAP && 343249808fa4SAndrew Thompson llc2->llc_ssap == LLC_SNAP_LSAP && 343349808fa4SAndrew Thompson llc2->llc_control == LLC_UI) { 343449808fa4SAndrew Thompson ether_type = htons(llc2->llc_un.type_snap.ether_type); 343531997bf2SAndrew Thompson snap = 1; 343631997bf2SAndrew Thompson } 343731997bf2SAndrew Thompson } 343831997bf2SAndrew Thompson 34390a6f8a50SAndrew Thompson /* 344065767e61SLexi Winter * If we're trying to filter bridge traffic, only look at traffic for 344165767e61SLexi Winter * protocols available in the kernel (IPv4 and/or IPv6) to avoid 344265767e61SLexi Winter * passing traffic for an unsupported protocol to the filter. This is 344365767e61SLexi Winter * lame since if we really wanted, say, an AppleTalk filter, we are 344465767e61SLexi Winter * hosed, but of course we don't have an AppleTalk filter to begin 344565767e61SLexi Winter * with. (Note that since pfil doesn't understand ARP it will pass 344665767e61SLexi Winter * *ALL* ARP traffic.) 34470a6f8a50SAndrew Thompson */ 34480a6f8a50SAndrew Thompson switch (ether_type) { 344965767e61SLexi Winter #ifdef INET 34500a6f8a50SAndrew Thompson case ETHERTYPE_ARP: 34510a6f8a50SAndrew Thompson case ETHERTYPE_REVARP: 3452c5127526SHiroki Sato if (V_pfil_ipfw_arp == 0) 34530a6f8a50SAndrew Thompson return (0); /* Automatically pass */ 34540a6f8a50SAndrew Thompson 345565767e61SLexi Winter /* FALLTHROUGH */ 34560a6f8a50SAndrew Thompson case ETHERTYPE_IP: 345765767e61SLexi Winter #endif 34580a6f8a50SAndrew Thompson #ifdef INET6 34590a6f8a50SAndrew Thompson case ETHERTYPE_IPV6: 34600a6f8a50SAndrew Thompson #endif /* INET6 */ 34610a6f8a50SAndrew Thompson break; 346265767e61SLexi Winter 34630a6f8a50SAndrew Thompson default: 34640a6f8a50SAndrew Thompson /* 346565767e61SLexi Winter * We get here if the packet isn't from a supported 346665767e61SLexi Winter * protocol. Check to see if the user wants to pass 346765767e61SLexi Winter * non-IP packets, these will not be checked by pfil(9) 346865767e61SLexi Winter * and passed unconditionally so the default is to 346965767e61SLexi Winter * drop. 34700a6f8a50SAndrew Thompson */ 3471c5127526SHiroki Sato if (V_pfil_onlyip) 34720a6f8a50SAndrew Thompson goto bad; 34730a6f8a50SAndrew Thompson } 34740a6f8a50SAndrew Thompson 34757d4317bdSAlexander V. Chernikov /* Run the packet through pfil before stripping link headers */ 3476b252313fSGleb Smirnoff if (PFIL_HOOKED_OUT(V_link_pfil_head) && V_pfil_ipfw != 0 && 34777d4317bdSAlexander V. Chernikov dir == PFIL_OUT && ifp != NULL) { 3478a2256150SGleb Smirnoff switch (pfil_mbuf_out(V_link_pfil_head, mp, ifp, NULL)) { 3479b252313fSGleb Smirnoff case PFIL_DROPPED: 34808d5c56daSGleb Smirnoff return (EACCES); 3481b252313fSGleb Smirnoff case PFIL_CONSUMED: 3482b252313fSGleb Smirnoff return (0); 3483b252313fSGleb Smirnoff } 34847d4317bdSAlexander V. Chernikov } 34857d4317bdSAlexander V. Chernikov 348631997bf2SAndrew Thompson /* Strip off the Ethernet header and keep a copy. */ 348731997bf2SAndrew Thompson m_copydata(*mp, 0, ETHER_HDR_LEN, (caddr_t) &eh2); 348831997bf2SAndrew Thompson m_adj(*mp, ETHER_HDR_LEN); 348931997bf2SAndrew Thompson 349031997bf2SAndrew Thompson /* Strip off snap header, if present */ 349131997bf2SAndrew Thompson if (snap) { 349249808fa4SAndrew Thompson m_copydata(*mp, 0, sizeof(struct llc), (caddr_t) &llc1); 349331997bf2SAndrew Thompson m_adj(*mp, sizeof(struct llc)); 349431997bf2SAndrew Thompson } 349531997bf2SAndrew Thompson 34962fcb030aSAndrew Thompson /* 34972fcb030aSAndrew Thompson * Check the IP header for alignment and errors 34982fcb030aSAndrew Thompson */ 34992fcb030aSAndrew Thompson if (dir == PFIL_IN) { 35002fcb030aSAndrew Thompson switch (ether_type) { 350165767e61SLexi Winter #ifdef INET 35022fcb030aSAndrew Thompson case ETHERTYPE_IP: 35032fcb030aSAndrew Thompson error = bridge_ip_checkbasic(mp); 35042fcb030aSAndrew Thompson break; 350565767e61SLexi Winter #endif 35062fcb030aSAndrew Thompson #ifdef INET6 35072fcb030aSAndrew Thompson case ETHERTYPE_IPV6: 35082fcb030aSAndrew Thompson error = bridge_ip6_checkbasic(mp); 35092fcb030aSAndrew Thompson break; 35102fcb030aSAndrew Thompson #endif /* INET6 */ 35112fcb030aSAndrew Thompson default: 35122fcb030aSAndrew Thompson error = 0; 35132fcb030aSAndrew Thompson } 35142fcb030aSAndrew Thompson if (error) 35152fcb030aSAndrew Thompson goto bad; 35162fcb030aSAndrew Thompson } 35172fcb030aSAndrew Thompson 35182fcb030aSAndrew Thompson error = 0; 35192fcb030aSAndrew Thompson 352031997bf2SAndrew Thompson /* 35210a6f8a50SAndrew Thompson * Run the packet through pfil 352231997bf2SAndrew Thompson */ 3523b252313fSGleb Smirnoff rv = PFIL_PASS; 3524dc1b1b7bSAndrew Thompson switch (ether_type) { 352565767e61SLexi Winter #ifdef INET 352631997bf2SAndrew Thompson case ETHERTYPE_IP: 352731997bf2SAndrew Thompson /* 352831997bf2SAndrew Thompson * Run pfil on the member interface and the bridge, both can 352931997bf2SAndrew Thompson * be skipped by clearing pfil_member or pfil_bridge. 353031997bf2SAndrew Thompson * 353131997bf2SAndrew Thompson * Keep the order: 353231997bf2SAndrew Thompson * in_if -> bridge_if -> out_if 353331997bf2SAndrew Thompson */ 3534b252313fSGleb Smirnoff if (V_pfil_bridge && dir == PFIL_OUT && bifp != NULL && (rv = 3535a2256150SGleb Smirnoff pfil_mbuf_out(V_inet_pfil_head, mp, bifp, NULL)) != 3536b252313fSGleb Smirnoff PFIL_PASS) 3537c8b01292SAndrew Thompson break; 3538c8b01292SAndrew Thompson 3539a2256150SGleb Smirnoff if (V_pfil_member && ifp != NULL) { 3540a2256150SGleb Smirnoff rv = (dir == PFIL_OUT) ? 3541a2256150SGleb Smirnoff pfil_mbuf_out(V_inet_pfil_head, mp, ifp, NULL) : 3542a2256150SGleb Smirnoff pfil_mbuf_in(V_inet_pfil_head, mp, ifp, NULL); 3543a2256150SGleb Smirnoff if (rv != PFIL_PASS) 3544c8b01292SAndrew Thompson break; 3545a2256150SGleb Smirnoff } 3546c8b01292SAndrew Thompson 3547b252313fSGleb Smirnoff if (V_pfil_bridge && dir == PFIL_IN && bifp != NULL && (rv = 3548a2256150SGleb Smirnoff pfil_mbuf_in(V_inet_pfil_head, mp, bifp, NULL)) != 3549b252313fSGleb Smirnoff PFIL_PASS) 35507f87a57cSAndrew Thompson break; 35517f87a57cSAndrew Thompson 35527f87a57cSAndrew Thompson /* check if we need to fragment the packet */ 3553f18598a4SKristof Provost /* bridge_fragment generates a mbuf chain of packets */ 3554f18598a4SKristof Provost /* that already include eth headers */ 3555c5127526SHiroki Sato if (V_pfil_member && ifp != NULL && dir == PFIL_OUT) { 35567f87a57cSAndrew Thompson i = (*mp)->m_pkthdr.len; 35577f87a57cSAndrew Thompson if (i > ifp->if_mtu) { 3558f18598a4SKristof Provost error = bridge_fragment(ifp, mp, &eh2, snap, 35597f87a57cSAndrew Thompson &llc1); 35607f87a57cSAndrew Thompson return (error); 35617f87a57cSAndrew Thompson } 35627f87a57cSAndrew Thompson } 35637f87a57cSAndrew Thompson 356421d172a3SGleb Smirnoff /* Recalculate the ip checksum. */ 356531997bf2SAndrew Thompson ip = mtod(*mp, struct ip *); 35662557a639SDaniel Hartmeier hlen = ip->ip_hl << 2; 35672557a639SDaniel Hartmeier if (hlen < sizeof(struct ip)) 35682557a639SDaniel Hartmeier goto bad; 35692557a639SDaniel Hartmeier if (hlen > (*mp)->m_len) { 3570155d72c4SPedro F. Giffuni if ((*mp = m_pullup(*mp, hlen)) == NULL) 35712557a639SDaniel Hartmeier goto bad; 35722557a639SDaniel Hartmeier ip = mtod(*mp, struct ip *); 35732557a639SDaniel Hartmeier if (ip == NULL) 35742557a639SDaniel Hartmeier goto bad; 35752557a639SDaniel Hartmeier } 35762557a639SDaniel Hartmeier ip->ip_sum = 0; 35772557a639SDaniel Hartmeier if (hlen == sizeof(struct ip)) 35782557a639SDaniel Hartmeier ip->ip_sum = in_cksum_hdr(ip); 35792557a639SDaniel Hartmeier else 35802557a639SDaniel Hartmeier ip->ip_sum = in_cksum(*mp, hlen); 358131997bf2SAndrew Thompson 358231997bf2SAndrew Thompson break; 358373585176SZhenlei Huang #endif /* INET */ 358431997bf2SAndrew Thompson #ifdef INET6 358531997bf2SAndrew Thompson case ETHERTYPE_IPV6: 3586b252313fSGleb Smirnoff if (V_pfil_bridge && dir == PFIL_OUT && bifp != NULL && (rv = 3587a2256150SGleb Smirnoff pfil_mbuf_out(V_inet6_pfil_head, mp, bifp, NULL)) != 3588b252313fSGleb Smirnoff PFIL_PASS) 3589c8b01292SAndrew Thompson break; 3590c8b01292SAndrew Thompson 3591a2256150SGleb Smirnoff if (V_pfil_member && ifp != NULL) { 3592a2256150SGleb Smirnoff rv = (dir == PFIL_OUT) ? 3593a2256150SGleb Smirnoff pfil_mbuf_out(V_inet6_pfil_head, mp, ifp, NULL) : 3594a2256150SGleb Smirnoff pfil_mbuf_in(V_inet6_pfil_head, mp, ifp, NULL); 3595a2256150SGleb Smirnoff if (rv != PFIL_PASS) 3596c8b01292SAndrew Thompson break; 3597a2256150SGleb Smirnoff } 3598c8b01292SAndrew Thompson 3599b252313fSGleb Smirnoff if (V_pfil_bridge && dir == PFIL_IN && bifp != NULL && (rv = 3600a2256150SGleb Smirnoff pfil_mbuf_in(V_inet6_pfil_head, mp, bifp, NULL)) != 3601b252313fSGleb Smirnoff PFIL_PASS) 3602b252313fSGleb Smirnoff break; 360331997bf2SAndrew Thompson break; 360431997bf2SAndrew Thompson #endif 360531997bf2SAndrew Thompson } 360631997bf2SAndrew Thompson 3607b252313fSGleb Smirnoff switch (rv) { 3608b252313fSGleb Smirnoff case PFIL_CONSUMED: 3609b252313fSGleb Smirnoff return (0); 3610b252313fSGleb Smirnoff case PFIL_DROPPED: 36118d5c56daSGleb Smirnoff return (EACCES); 3612b252313fSGleb Smirnoff default: 3613b252313fSGleb Smirnoff break; 3614b252313fSGleb Smirnoff } 361531997bf2SAndrew Thompson 361631997bf2SAndrew Thompson error = -1; 361731997bf2SAndrew Thompson 361831997bf2SAndrew Thompson /* 361931997bf2SAndrew Thompson * Finally, put everything back the way it was and return 362031997bf2SAndrew Thompson */ 362131997bf2SAndrew Thompson if (snap) { 3622eb1b1807SGleb Smirnoff M_PREPEND(*mp, sizeof(struct llc), M_NOWAIT); 362331997bf2SAndrew Thompson if (*mp == NULL) 3624dc1b1b7bSAndrew Thompson return (error); 362549808fa4SAndrew Thompson bcopy(&llc1, mtod(*mp, caddr_t), sizeof(struct llc)); 362631997bf2SAndrew Thompson } 362731997bf2SAndrew Thompson 3628eb1b1807SGleb Smirnoff M_PREPEND(*mp, ETHER_HDR_LEN, M_NOWAIT); 362931997bf2SAndrew Thompson if (*mp == NULL) 3630dc1b1b7bSAndrew Thompson return (error); 363131997bf2SAndrew Thompson bcopy(&eh2, mtod(*mp, caddr_t), ETHER_HDR_LEN); 363231997bf2SAndrew Thompson 3633dc1b1b7bSAndrew Thompson return (0); 363431997bf2SAndrew Thompson 363531997bf2SAndrew Thompson bad: 363631997bf2SAndrew Thompson m_freem(*mp); 363731997bf2SAndrew Thompson *mp = NULL; 3638dc1b1b7bSAndrew Thompson return (error); 363931997bf2SAndrew Thompson } 364031997bf2SAndrew Thompson 364165767e61SLexi Winter #ifdef INET 364231997bf2SAndrew Thompson /* 364331997bf2SAndrew Thompson * Perform basic checks on header size since 364431997bf2SAndrew Thompson * pfil assumes ip_input has already processed 364531997bf2SAndrew Thompson * it for it. Cut-and-pasted from ip_input.c. 364631997bf2SAndrew Thompson * Given how simple the IPv6 version is, 364731997bf2SAndrew Thompson * does the IPv4 version really need to be 364831997bf2SAndrew Thompson * this complicated? 364931997bf2SAndrew Thompson * 365031997bf2SAndrew Thompson * XXX Should we update ipstat here, or not? 365131997bf2SAndrew Thompson * XXX Right now we update ipstat but not 365231997bf2SAndrew Thompson * XXX csum_counter. 365331997bf2SAndrew Thompson */ 365431997bf2SAndrew Thompson static int 365531997bf2SAndrew Thompson bridge_ip_checkbasic(struct mbuf **mp) 365631997bf2SAndrew Thompson { 365731997bf2SAndrew Thompson struct mbuf *m = *mp; 365831997bf2SAndrew Thompson struct ip *ip; 365931997bf2SAndrew Thompson int len, hlen; 366031997bf2SAndrew Thompson u_short sum; 366131997bf2SAndrew Thompson 366231997bf2SAndrew Thompson if (*mp == NULL) 3663dc1b1b7bSAndrew Thompson return (-1); 366431997bf2SAndrew Thompson 36652fcb030aSAndrew Thompson if (IP_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) { 36662fcb030aSAndrew Thompson if ((m = m_copyup(m, sizeof(struct ip), 36672fcb030aSAndrew Thompson (max_linkhdr + 3) & ~3)) == NULL) { 36682fcb030aSAndrew Thompson /* XXXJRT new stat, please */ 3669315e3e38SRobert Watson KMOD_IPSTAT_INC(ips_toosmall); 36702fcb030aSAndrew Thompson goto bad; 36712fcb030aSAndrew Thompson } 36722fcb030aSAndrew Thompson } else if (__predict_false(m->m_len < sizeof (struct ip))) { 367331997bf2SAndrew Thompson if ((m = m_pullup(m, sizeof (struct ip))) == NULL) { 3674315e3e38SRobert Watson KMOD_IPSTAT_INC(ips_toosmall); 367531997bf2SAndrew Thompson goto bad; 367631997bf2SAndrew Thompson } 367731997bf2SAndrew Thompson } 367831997bf2SAndrew Thompson ip = mtod(m, struct ip *); 367931997bf2SAndrew Thompson if (ip == NULL) goto bad; 368031997bf2SAndrew Thompson 368131997bf2SAndrew Thompson if (ip->ip_v != IPVERSION) { 3682315e3e38SRobert Watson KMOD_IPSTAT_INC(ips_badvers); 368331997bf2SAndrew Thompson goto bad; 368431997bf2SAndrew Thompson } 368531997bf2SAndrew Thompson hlen = ip->ip_hl << 2; 368631997bf2SAndrew Thompson if (hlen < sizeof(struct ip)) { /* minimum header length */ 3687315e3e38SRobert Watson KMOD_IPSTAT_INC(ips_badhlen); 368831997bf2SAndrew Thompson goto bad; 368931997bf2SAndrew Thompson } 369031997bf2SAndrew Thompson if (hlen > m->m_len) { 3691155d72c4SPedro F. Giffuni if ((m = m_pullup(m, hlen)) == NULL) { 3692315e3e38SRobert Watson KMOD_IPSTAT_INC(ips_badhlen); 369331997bf2SAndrew Thompson goto bad; 369431997bf2SAndrew Thompson } 369531997bf2SAndrew Thompson ip = mtod(m, struct ip *); 369631997bf2SAndrew Thompson if (ip == NULL) goto bad; 369731997bf2SAndrew Thompson } 369831997bf2SAndrew Thompson 369931997bf2SAndrew Thompson if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) { 370031997bf2SAndrew Thompson sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID); 370131997bf2SAndrew Thompson } else { 370231997bf2SAndrew Thompson if (hlen == sizeof(struct ip)) { 370331997bf2SAndrew Thompson sum = in_cksum_hdr(ip); 370431997bf2SAndrew Thompson } else { 370531997bf2SAndrew Thompson sum = in_cksum(m, hlen); 370631997bf2SAndrew Thompson } 370731997bf2SAndrew Thompson } 370831997bf2SAndrew Thompson if (sum) { 3709315e3e38SRobert Watson KMOD_IPSTAT_INC(ips_badsum); 371031997bf2SAndrew Thompson goto bad; 371131997bf2SAndrew Thompson } 371231997bf2SAndrew Thompson 371331997bf2SAndrew Thompson /* Retrieve the packet length. */ 371431997bf2SAndrew Thompson len = ntohs(ip->ip_len); 371531997bf2SAndrew Thompson 371631997bf2SAndrew Thompson /* 371731997bf2SAndrew Thompson * Check for additional length bogosity 371831997bf2SAndrew Thompson */ 371931997bf2SAndrew Thompson if (len < hlen) { 3720315e3e38SRobert Watson KMOD_IPSTAT_INC(ips_badlen); 372131997bf2SAndrew Thompson goto bad; 372231997bf2SAndrew Thompson } 372331997bf2SAndrew Thompson 372431997bf2SAndrew Thompson /* 372531997bf2SAndrew Thompson * Check that the amount of data in the buffers 372631997bf2SAndrew Thompson * is as at least much as the IP header would have us expect. 372731997bf2SAndrew Thompson * Drop packet if shorter than we expect. 372831997bf2SAndrew Thompson */ 372931997bf2SAndrew Thompson if (m->m_pkthdr.len < len) { 3730315e3e38SRobert Watson KMOD_IPSTAT_INC(ips_tooshort); 373131997bf2SAndrew Thompson goto bad; 373231997bf2SAndrew Thompson } 373331997bf2SAndrew Thompson 373431997bf2SAndrew Thompson /* Checks out, proceed */ 373531997bf2SAndrew Thompson *mp = m; 3736dc1b1b7bSAndrew Thompson return (0); 373731997bf2SAndrew Thompson 373831997bf2SAndrew Thompson bad: 373931997bf2SAndrew Thompson *mp = m; 3740dc1b1b7bSAndrew Thompson return (-1); 374131997bf2SAndrew Thompson } 374273585176SZhenlei Huang #endif /* INET */ 374331997bf2SAndrew Thompson 374431997bf2SAndrew Thompson #ifdef INET6 374531997bf2SAndrew Thompson /* 374631997bf2SAndrew Thompson * Same as above, but for IPv6. 374731997bf2SAndrew Thompson * Cut-and-pasted from ip6_input.c. 374831997bf2SAndrew Thompson * XXX Should we update ip6stat, or not? 374931997bf2SAndrew Thompson */ 375031997bf2SAndrew Thompson static int 375131997bf2SAndrew Thompson bridge_ip6_checkbasic(struct mbuf **mp) 375231997bf2SAndrew Thompson { 375331997bf2SAndrew Thompson struct mbuf *m = *mp; 375431997bf2SAndrew Thompson struct ip6_hdr *ip6; 375531997bf2SAndrew Thompson 375631997bf2SAndrew Thompson /* 375731997bf2SAndrew Thompson * If the IPv6 header is not aligned, slurp it up into a new 375831997bf2SAndrew Thompson * mbuf with space for link headers, in the event we forward 375931997bf2SAndrew Thompson * it. Otherwise, if it is aligned, make sure the entire base 376031997bf2SAndrew Thompson * IPv6 header is in the first mbuf of the chain. 37612fcb030aSAndrew Thompson */ 376231997bf2SAndrew Thompson if (IP6_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) { 376331997bf2SAndrew Thompson struct ifnet *inifp = m->m_pkthdr.rcvif; 376431997bf2SAndrew Thompson if ((m = m_copyup(m, sizeof(struct ip6_hdr), 376531997bf2SAndrew Thompson (max_linkhdr + 3) & ~3)) == NULL) { 37662fcb030aSAndrew Thompson /* XXXJRT new stat, please */ 37679cb8d207SAndrey V. Elsukov IP6STAT_INC(ip6s_toosmall); 376831997bf2SAndrew Thompson in6_ifstat_inc(inifp, ifs6_in_hdrerr); 376931997bf2SAndrew Thompson goto bad; 377031997bf2SAndrew Thompson } 37712fcb030aSAndrew Thompson } else if (__predict_false(m->m_len < sizeof(struct ip6_hdr))) { 377231997bf2SAndrew Thompson struct ifnet *inifp = m->m_pkthdr.rcvif; 377331997bf2SAndrew Thompson if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) { 37749cb8d207SAndrey V. Elsukov IP6STAT_INC(ip6s_toosmall); 377531997bf2SAndrew Thompson in6_ifstat_inc(inifp, ifs6_in_hdrerr); 377631997bf2SAndrew Thompson goto bad; 377731997bf2SAndrew Thompson } 377831997bf2SAndrew Thompson } 377931997bf2SAndrew Thompson 378031997bf2SAndrew Thompson ip6 = mtod(m, struct ip6_hdr *); 378131997bf2SAndrew Thompson 378231997bf2SAndrew Thompson if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { 37839cb8d207SAndrey V. Elsukov IP6STAT_INC(ip6s_badvers); 378431997bf2SAndrew Thompson in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); 378531997bf2SAndrew Thompson goto bad; 378631997bf2SAndrew Thompson } 378731997bf2SAndrew Thompson 378831997bf2SAndrew Thompson /* Checks out, proceed */ 378931997bf2SAndrew Thompson *mp = m; 3790dc1b1b7bSAndrew Thompson return (0); 379131997bf2SAndrew Thompson 379231997bf2SAndrew Thompson bad: 379331997bf2SAndrew Thompson *mp = m; 3794dc1b1b7bSAndrew Thompson return (-1); 379531997bf2SAndrew Thompson } 379631997bf2SAndrew Thompson #endif /* INET6 */ 37977f87a57cSAndrew Thompson 379865767e61SLexi Winter #ifdef INET 37997f87a57cSAndrew Thompson /* 38007f87a57cSAndrew Thompson * bridge_fragment: 38017f87a57cSAndrew Thompson * 3802f18598a4SKristof Provost * Fragment mbuf chain in multiple packets and prepend ethernet header. 38037f87a57cSAndrew Thompson */ 38047f87a57cSAndrew Thompson static int 3805f18598a4SKristof Provost bridge_fragment(struct ifnet *ifp, struct mbuf **mp, struct ether_header *eh, 38067f87a57cSAndrew Thompson int snap, struct llc *llc) 38077f87a57cSAndrew Thompson { 3808f18598a4SKristof Provost struct mbuf *m = *mp, *nextpkt = NULL, *mprev = NULL, *mcur = NULL; 38097f87a57cSAndrew Thompson struct ip *ip; 38107f87a57cSAndrew Thompson int error = -1; 38117f87a57cSAndrew Thompson 38127f87a57cSAndrew Thompson if (m->m_len < sizeof(struct ip) && 38137f87a57cSAndrew Thompson (m = m_pullup(m, sizeof(struct ip))) == NULL) 3814f18598a4SKristof Provost goto dropit; 38157f87a57cSAndrew Thompson ip = mtod(m, struct ip *); 38167f87a57cSAndrew Thompson 3817078468edSGleb Smirnoff m->m_pkthdr.csum_flags |= CSUM_IP; 3818078468edSGleb Smirnoff error = ip_fragment(ip, &m, ifp->if_mtu, ifp->if_hwassist); 38197f87a57cSAndrew Thompson if (error) 3820f18598a4SKristof Provost goto dropit; 38217f87a57cSAndrew Thompson 3822f18598a4SKristof Provost /* 3823f18598a4SKristof Provost * Walk the chain and re-add the Ethernet header for 3824f18598a4SKristof Provost * each mbuf packet. 3825f18598a4SKristof Provost */ 3826f18598a4SKristof Provost for (mcur = m; mcur; mcur = mcur->m_nextpkt) { 3827f18598a4SKristof Provost nextpkt = mcur->m_nextpkt; 3828f18598a4SKristof Provost mcur->m_nextpkt = NULL; 38297f87a57cSAndrew Thompson if (snap) { 3830f18598a4SKristof Provost M_PREPEND(mcur, sizeof(struct llc), M_NOWAIT); 3831f18598a4SKristof Provost if (mcur == NULL) { 38327f87a57cSAndrew Thompson error = ENOBUFS; 3833f18598a4SKristof Provost if (mprev != NULL) 3834f18598a4SKristof Provost mprev->m_nextpkt = nextpkt; 3835f18598a4SKristof Provost goto dropit; 38367f87a57cSAndrew Thompson } 3837f18598a4SKristof Provost bcopy(llc, mtod(mcur, caddr_t),sizeof(struct llc)); 38387f87a57cSAndrew Thompson } 38397f87a57cSAndrew Thompson 3840f18598a4SKristof Provost M_PREPEND(mcur, ETHER_HDR_LEN, M_NOWAIT); 3841f18598a4SKristof Provost if (mcur == NULL) { 3842f18598a4SKristof Provost error = ENOBUFS; 3843f18598a4SKristof Provost if (mprev != NULL) 3844f18598a4SKristof Provost mprev->m_nextpkt = nextpkt; 3845f18598a4SKristof Provost goto dropit; 3846f18598a4SKristof Provost } 3847f18598a4SKristof Provost bcopy(eh, mtod(mcur, caddr_t), ETHER_HDR_LEN); 3848f18598a4SKristof Provost 3849f18598a4SKristof Provost /* 3850f18598a4SKristof Provost * The previous two M_PREPEND could have inserted one or two 3851f18598a4SKristof Provost * mbufs in front so we have to update the previous packet's 3852f18598a4SKristof Provost * m_nextpkt. 3853f18598a4SKristof Provost */ 3854f18598a4SKristof Provost mcur->m_nextpkt = nextpkt; 3855f18598a4SKristof Provost if (mprev != NULL) 3856f18598a4SKristof Provost mprev->m_nextpkt = mcur; 3857f18598a4SKristof Provost else { 3858f18598a4SKristof Provost /* The first mbuf in the original chain needs to be 3859f18598a4SKristof Provost * updated. */ 3860f18598a4SKristof Provost *mp = mcur; 3861f18598a4SKristof Provost } 3862f18598a4SKristof Provost mprev = mcur; 3863f18598a4SKristof Provost } 3864f18598a4SKristof Provost 3865315e3e38SRobert Watson KMOD_IPSTAT_INC(ips_fragmented); 38667f87a57cSAndrew Thompson return (error); 38677f87a57cSAndrew Thompson 3868f18598a4SKristof Provost dropit: 3869f18598a4SKristof Provost for (mcur = *mp; mcur; mcur = m) { /* droping the full packet chain */ 3870f18598a4SKristof Provost m = mcur->m_nextpkt; 3871f18598a4SKristof Provost m_freem(mcur); 3872f18598a4SKristof Provost } 38737f87a57cSAndrew Thompson return (error); 38747f87a57cSAndrew Thompson } 387573585176SZhenlei Huang #endif /* INET */ 38767702d401SAndrew Thompson 38777702d401SAndrew Thompson static void 38787702d401SAndrew Thompson bridge_linkstate(struct ifnet *ifp) 38797702d401SAndrew Thompson { 38807702d401SAndrew Thompson struct bridge_softc *sc = ifp->if_bridge; 388108e34823SAndrew Thompson struct bridge_iflist *bif; 3882fffd27e5SKristof Provost struct epoch_tracker et; 38837702d401SAndrew Thompson 3884fffd27e5SKristof Provost NET_EPOCH_ENTER(et); 3885fffd27e5SKristof Provost 38867702d401SAndrew Thompson bif = bridge_lookup_member_if(sc, ifp); 38877702d401SAndrew Thompson if (bif == NULL) { 3888fffd27e5SKristof Provost NET_EPOCH_EXIT(et); 38897702d401SAndrew Thompson return; 38907702d401SAndrew Thompson } 389108e34823SAndrew Thompson bridge_linkcheck(sc); 389208e34823SAndrew Thompson 389308e34823SAndrew Thompson bstp_linkstate(&bif->bif_stp); 3894fffd27e5SKristof Provost 3895fffd27e5SKristof Provost NET_EPOCH_EXIT(et); 389608e34823SAndrew Thompson } 389708e34823SAndrew Thompson 389808e34823SAndrew Thompson static void 389908e34823SAndrew Thompson bridge_linkcheck(struct bridge_softc *sc) 390008e34823SAndrew Thompson { 390108e34823SAndrew Thompson struct bridge_iflist *bif; 390208e34823SAndrew Thompson int new_link, hasls; 390308e34823SAndrew Thompson 39044af1bd81SKristof Provost BRIDGE_LOCK_OR_NET_EPOCH_ASSERT(sc); 3905fffd27e5SKristof Provost 39067702d401SAndrew Thompson new_link = LINK_STATE_DOWN; 39077702d401SAndrew Thompson hasls = 0; 39087702d401SAndrew Thompson /* Our link is considered up if at least one of our ports is active */ 3909dd00a42aSKristof Provost CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { 391008e34823SAndrew Thompson if (bif->bif_ifp->if_capabilities & IFCAP_LINKSTATE) 39117702d401SAndrew Thompson hasls++; 391208e34823SAndrew Thompson if (bif->bif_ifp->if_link_state == LINK_STATE_UP) { 39137702d401SAndrew Thompson new_link = LINK_STATE_UP; 39147702d401SAndrew Thompson break; 39157702d401SAndrew Thompson } 39167702d401SAndrew Thompson } 3917dd00a42aSKristof Provost if (!CK_LIST_EMPTY(&sc->sc_iflist) && !hasls) { 39187702d401SAndrew Thompson /* If no interfaces support link-state then we default to up */ 39197702d401SAndrew Thompson new_link = LINK_STATE_UP; 39207702d401SAndrew Thompson } 39217702d401SAndrew Thompson if_link_state_change(sc->sc_ifp, new_link); 39227702d401SAndrew Thompson } 3923