1*c9e0a13bSozaki-r /* $NetBSD: if_bridge.c,v 1.196 2024/12/16 05:21:24 ozaki-r Exp $ */ 2460da35fSthorpej 3460da35fSthorpej /* 4460da35fSthorpej * Copyright 2001 Wasabi Systems, Inc. 5460da35fSthorpej * All rights reserved. 6460da35fSthorpej * 7460da35fSthorpej * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8460da35fSthorpej * 9460da35fSthorpej * Redistribution and use in source and binary forms, with or without 10460da35fSthorpej * modification, are permitted provided that the following conditions 11460da35fSthorpej * are met: 12460da35fSthorpej * 1. Redistributions of source code must retain the above copyright 13460da35fSthorpej * notice, this list of conditions and the following disclaimer. 14460da35fSthorpej * 2. Redistributions in binary form must reproduce the above copyright 15460da35fSthorpej * notice, this list of conditions and the following disclaimer in the 16460da35fSthorpej * documentation and/or other materials provided with the distribution. 17460da35fSthorpej * 3. All advertising materials mentioning features or use of this software 18460da35fSthorpej * must display the following acknowledgement: 19460da35fSthorpej * This product includes software developed for the NetBSD Project by 20460da35fSthorpej * Wasabi Systems, Inc. 21460da35fSthorpej * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22460da35fSthorpej * or promote products derived from this software without specific prior 23460da35fSthorpej * written permission. 24460da35fSthorpej * 25460da35fSthorpej * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26460da35fSthorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27460da35fSthorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28460da35fSthorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29460da35fSthorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30460da35fSthorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31460da35fSthorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32460da35fSthorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33460da35fSthorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34460da35fSthorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35460da35fSthorpej * POSSIBILITY OF SUCH DAMAGE. 36460da35fSthorpej */ 37460da35fSthorpej 38460da35fSthorpej /* 39460da35fSthorpej * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) 40460da35fSthorpej * All rights reserved. 41460da35fSthorpej * 42460da35fSthorpej * Redistribution and use in source and binary forms, with or without 43460da35fSthorpej * modification, are permitted provided that the following conditions 44460da35fSthorpej * are met: 45460da35fSthorpej * 1. Redistributions of source code must retain the above copyright 46460da35fSthorpej * notice, this list of conditions and the following disclaimer. 47460da35fSthorpej * 2. Redistributions in binary form must reproduce the above copyright 48460da35fSthorpej * notice, this list of conditions and the following disclaimer in the 49460da35fSthorpej * documentation and/or other materials provided with the distribution. 50460da35fSthorpej * 3. All advertising materials mentioning features or use of this software 51460da35fSthorpej * must display the following acknowledgement: 52460da35fSthorpej * This product includes software developed by Jason L. Wright 53460da35fSthorpej * 4. The name of the author may not be used to endorse or promote products 54460da35fSthorpej * derived from this software without specific prior written permission. 55460da35fSthorpej * 56460da35fSthorpej * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 57460da35fSthorpej * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 58460da35fSthorpej * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 59460da35fSthorpej * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 60460da35fSthorpej * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 61460da35fSthorpej * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 62460da35fSthorpej * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 63460da35fSthorpej * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 64460da35fSthorpej * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 65460da35fSthorpej * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 66460da35fSthorpej * POSSIBILITY OF SUCH DAMAGE. 67460da35fSthorpej * 68460da35fSthorpej * OpenBSD: if_bridge.c,v 1.60 2001/06/15 03:38:33 itojun Exp 69460da35fSthorpej */ 70460da35fSthorpej 71460da35fSthorpej /* 72460da35fSthorpej * Network interface bridge support. 73460da35fSthorpej * 74460da35fSthorpej * TODO: 75460da35fSthorpej * 76460da35fSthorpej * - Currently only supports Ethernet-like interfaces (Ethernet, 77460da35fSthorpej * 802.11, VLANs on Ethernet, etc.) Figure out a nice way 78460da35fSthorpej * to bridge other types of interfaces (FDDI-FDDI, and maybe 79460da35fSthorpej * consider heterogenous bridges). 80460da35fSthorpej */ 81460da35fSthorpej 8234d65a34Slukem #include <sys/cdefs.h> 83*c9e0a13bSozaki-r __KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.196 2024/12/16 05:21:24 ozaki-r Exp $"); 8434d65a34Slukem 8555fde99bSpooka #ifdef _KERNEL_OPT 86e8e38ce5Sjdc #include "opt_inet.h" 87d81e97faSozaki-r #include "opt_net_mpsafe.h" 8855fde99bSpooka #endif /* _KERNEL_OPT */ 89460da35fSthorpej 90460da35fSthorpej #include <sys/param.h> 91460da35fSthorpej #include <sys/kernel.h> 92460da35fSthorpej #include <sys/mbuf.h> 93460da35fSthorpej #include <sys/queue.h> 94460da35fSthorpej #include <sys/socket.h> 950c046062Sbouyer #include <sys/socketvar.h> /* for softnet_lock */ 96460da35fSthorpej #include <sys/sockio.h> 97460da35fSthorpej #include <sys/systm.h> 98460da35fSthorpej #include <sys/proc.h> 99460da35fSthorpej #include <sys/pool.h> 100874fef37Selad #include <sys/kauth.h> 1011fd1b496Sbouyer #include <sys/cpu.h> 1023afd44cfStls #include <sys/cprng.h> 103a4bec2ddSozaki-r #include <sys/mutex.h> 104df0047d0Sozaki-r #include <sys/kmem.h> 105008f982cSozaki-r #include <sys/syslog.h> 106460da35fSthorpej 107460da35fSthorpej #include <net/bpf.h> 108460da35fSthorpej #include <net/if.h> 109460da35fSthorpej #include <net/if_dl.h> 110460da35fSthorpej #include <net/if_types.h> 111460da35fSthorpej #include <net/if_llc.h> 112460da35fSthorpej 113460da35fSthorpej #include <net/if_ether.h> 114460da35fSthorpej #include <net/if_bridgevar.h> 11573240bb1Srin #include <net/ether_sw_offload.h> 116460da35fSthorpej 117557877d0Schristos /* Used for bridge_ip[6]_checkbasic */ 1186b857c22Sperseant #include <netinet/in.h> 1196b857c22Sperseant #include <netinet/in_systm.h> 1206b857c22Sperseant #include <netinet/ip.h> 1216b857c22Sperseant #include <netinet/ip_var.h> 1220dd41b37Sthorpej #include <netinet/ip_private.h> /* XXX */ 1236b857c22Sperseant #include <netinet/ip6.h> 1246b857c22Sperseant #include <netinet6/in6_var.h> 125cb38e6c4Smartin #include <netinet6/ip6_var.h> 1260dd41b37Sthorpej #include <netinet6/ip6_private.h> /* XXX */ 1276b857c22Sperseant 128460da35fSthorpej /* 129460da35fSthorpej * Size of the route hash table. Must be a power of two. 130460da35fSthorpej */ 131460da35fSthorpej #ifndef BRIDGE_RTHASH_SIZE 132460da35fSthorpej #define BRIDGE_RTHASH_SIZE 1024 133460da35fSthorpej #endif 134460da35fSthorpej 135460da35fSthorpej #define BRIDGE_RTHASH_MASK (BRIDGE_RTHASH_SIZE - 1) 136460da35fSthorpej 1374876c304Sliamjfoy #include "carp.h" 1384876c304Sliamjfoy #if NCARP > 0 1394876c304Sliamjfoy #include <netinet/in.h> 1404876c304Sliamjfoy #include <netinet/in_var.h> 1414876c304Sliamjfoy #include <netinet/ip_carp.h> 1424876c304Sliamjfoy #endif 1434876c304Sliamjfoy 144e7ae23fdSchristos #include "ioconf.h" 145e7ae23fdSchristos 146ef434b48Smatt __CTASSERT(sizeof(struct ifbifconf) == sizeof(struct ifbaconf)); 147ef434b48Smatt __CTASSERT(offsetof(struct ifbifconf, ifbic_len) == offsetof(struct ifbaconf, ifbac_len)); 148ef434b48Smatt __CTASSERT(offsetof(struct ifbifconf, ifbic_buf) == offsetof(struct ifbaconf, ifbac_buf)); 149ef434b48Smatt 150460da35fSthorpej /* 151460da35fSthorpej * Maximum number of addresses to cache. 152460da35fSthorpej */ 153460da35fSthorpej #ifndef BRIDGE_RTABLE_MAX 154460da35fSthorpej #define BRIDGE_RTABLE_MAX 100 155460da35fSthorpej #endif 156460da35fSthorpej 157460da35fSthorpej /* 158460da35fSthorpej * Spanning tree defaults. 159460da35fSthorpej */ 160460da35fSthorpej #define BSTP_DEFAULT_MAX_AGE (20 * 256) 161460da35fSthorpej #define BSTP_DEFAULT_HELLO_TIME (2 * 256) 162460da35fSthorpej #define BSTP_DEFAULT_FORWARD_DELAY (15 * 256) 163460da35fSthorpej #define BSTP_DEFAULT_HOLD_TIME (1 * 256) 164460da35fSthorpej #define BSTP_DEFAULT_BRIDGE_PRIORITY 0x8000 165460da35fSthorpej #define BSTP_DEFAULT_PORT_PRIORITY 0x80 166460da35fSthorpej #define BSTP_DEFAULT_PATH_COST 55 167460da35fSthorpej 168460da35fSthorpej /* 169460da35fSthorpej * Timeout (in seconds) for entries learned dynamically. 170460da35fSthorpej */ 171460da35fSthorpej #ifndef BRIDGE_RTABLE_TIMEOUT 172460da35fSthorpej #define BRIDGE_RTABLE_TIMEOUT (20 * 60) /* same as ARP */ 173460da35fSthorpej #endif 174460da35fSthorpej 175460da35fSthorpej /* 176460da35fSthorpej * Number of seconds between walks of the route list. 177460da35fSthorpej */ 178460da35fSthorpej #ifndef BRIDGE_RTABLE_PRUNE_PERIOD 179460da35fSthorpej #define BRIDGE_RTABLE_PRUNE_PERIOD (5 * 60) 180460da35fSthorpej #endif 181460da35fSthorpej 182adc61740Sozaki-r #define BRIDGE_RT_LOCK(_sc) mutex_enter((_sc)->sc_rtlist_lock) 183adc61740Sozaki-r #define BRIDGE_RT_UNLOCK(_sc) mutex_exit((_sc)->sc_rtlist_lock) 184adc61740Sozaki-r #define BRIDGE_RT_LOCKED(_sc) mutex_owned((_sc)->sc_rtlist_lock) 185e85cdef1Sozaki-r 186e85cdef1Sozaki-r #define BRIDGE_RT_PSZ_PERFORM(_sc) \ 187ca1f3d84Sozaki-r pserialize_perform((_sc)->sc_rtlist_psz) 188e85cdef1Sozaki-r 1897a003d61Sozaki-r #define BRIDGE_RTLIST_READER_FOREACH(_brt, _sc) \ 1907a003d61Sozaki-r PSLIST_READER_FOREACH((_brt), &((_sc)->sc_rtlist), \ 1917a003d61Sozaki-r struct bridge_rtnode, brt_list) 1927a003d61Sozaki-r #define BRIDGE_RTLIST_WRITER_FOREACH(_brt, _sc) \ 1937a003d61Sozaki-r PSLIST_WRITER_FOREACH((_brt), &((_sc)->sc_rtlist), \ 1947a003d61Sozaki-r struct bridge_rtnode, brt_list) 1957a003d61Sozaki-r #define BRIDGE_RTLIST_WRITER_INSERT_HEAD(_sc, _brt) \ 1967a003d61Sozaki-r PSLIST_WRITER_INSERT_HEAD(&(_sc)->sc_rtlist, brt, brt_list) 1977a003d61Sozaki-r #define BRIDGE_RTLIST_WRITER_REMOVE(_brt) \ 1987a003d61Sozaki-r PSLIST_WRITER_REMOVE((_brt), brt_list) 1997a003d61Sozaki-r 2007a003d61Sozaki-r #define BRIDGE_RTHASH_READER_FOREACH(_brt, _sc, _hash) \ 2017a003d61Sozaki-r PSLIST_READER_FOREACH((_brt), &(_sc)->sc_rthash[(_hash)], \ 2027a003d61Sozaki-r struct bridge_rtnode, brt_hash) 2037a003d61Sozaki-r #define BRIDGE_RTHASH_WRITER_FOREACH(_brt, _sc, _hash) \ 2047a003d61Sozaki-r PSLIST_WRITER_FOREACH((_brt), &(_sc)->sc_rthash[(_hash)], \ 2057a003d61Sozaki-r struct bridge_rtnode, brt_hash) 2067a003d61Sozaki-r #define BRIDGE_RTHASH_WRITER_INSERT_HEAD(_sc, _hash, _brt) \ 2077a003d61Sozaki-r PSLIST_WRITER_INSERT_HEAD(&(_sc)->sc_rthash[(_hash)], brt, brt_hash) 2087a003d61Sozaki-r #define BRIDGE_RTHASH_WRITER_INSERT_AFTER(_brt, _new) \ 2097a003d61Sozaki-r PSLIST_WRITER_INSERT_AFTER((_brt), (_new), brt_hash) 2107a003d61Sozaki-r #define BRIDGE_RTHASH_WRITER_REMOVE(_brt) \ 2117a003d61Sozaki-r PSLIST_WRITER_REMOVE((_brt), brt_hash) 212d81e97faSozaki-r 213d81e97faSozaki-r #ifdef NET_MPSAFE 214d81e97faSozaki-r #define DECLARE_LOCK_VARIABLE 215d81e97faSozaki-r #define ACQUIRE_GLOBAL_LOCKS() do { } while (0) 216d81e97faSozaki-r #define RELEASE_GLOBAL_LOCKS() do { } while (0) 217d81e97faSozaki-r #else 218d81e97faSozaki-r #define DECLARE_LOCK_VARIABLE int __s 219d81e97faSozaki-r #define ACQUIRE_GLOBAL_LOCKS() do { \ 220d81e97faSozaki-r KERNEL_LOCK(1, NULL); \ 221d81e97faSozaki-r mutex_enter(softnet_lock); \ 222c26964baSozaki-r __s = splsoftnet(); \ 223d81e97faSozaki-r } while (0) 224d81e97faSozaki-r #define RELEASE_GLOBAL_LOCKS() do { \ 225d81e97faSozaki-r splx(__s); \ 226d81e97faSozaki-r mutex_exit(softnet_lock); \ 227d81e97faSozaki-r KERNEL_UNLOCK_ONE(NULL); \ 228d81e97faSozaki-r } while (0) 229d81e97faSozaki-r #endif 230e85cdef1Sozaki-r 231cc3dd2e0Sozaki-r struct psref_class *bridge_psref_class __read_mostly; 232cc3dd2e0Sozaki-r 233460da35fSthorpej int bridge_rtable_prune_period = BRIDGE_RTABLE_PRUNE_PERIOD; 234460da35fSthorpej 23563eac52bSthorpej static struct pool bridge_rtnode_pool; 236460da35fSthorpej 23763eac52bSthorpej static int bridge_clone_create(struct if_clone *, int); 23863eac52bSthorpej static int bridge_clone_destroy(struct ifnet *); 239460da35fSthorpej 24053524e44Schristos static int bridge_ioctl(struct ifnet *, u_long, void *); 24163eac52bSthorpej static int bridge_init(struct ifnet *); 24263eac52bSthorpej static void bridge_stop(struct ifnet *, int); 24363eac52bSthorpej static void bridge_start(struct ifnet *); 244c06d6dc9Syamaguchi static void bridge_ifdetach(void *); 245460da35fSthorpej 246cb4cb631Sozaki-r static void bridge_input(struct ifnet *, struct mbuf *); 247b7a310caSozaki-r static void bridge_forward(struct bridge_softc *, struct mbuf *); 248460da35fSthorpej 24963eac52bSthorpej static void bridge_timer(void *); 250460da35fSthorpej 251008f982cSozaki-r static void bridge_broadcast(struct bridge_softc *, struct ifnet *, bool, 25263eac52bSthorpej struct mbuf *); 253460da35fSthorpej 25463eac52bSthorpej static int bridge_rtupdate(struct bridge_softc *, const uint8_t *, 255460da35fSthorpej struct ifnet *, int, uint8_t); 25663eac52bSthorpej static struct ifnet *bridge_rtlookup(struct bridge_softc *, const uint8_t *); 25763eac52bSthorpej static void bridge_rttrim(struct bridge_softc *); 25863eac52bSthorpej static void bridge_rtage(struct bridge_softc *); 259e85cdef1Sozaki-r static void bridge_rtage_work(struct work *, void *); 26063eac52bSthorpej static void bridge_rtflush(struct bridge_softc *, int); 26163eac52bSthorpej static int bridge_rtdaddr(struct bridge_softc *, const uint8_t *); 26263eac52bSthorpej static void bridge_rtdelete(struct bridge_softc *, struct ifnet *ifp); 263460da35fSthorpej 264df0047d0Sozaki-r static void bridge_rtable_init(struct bridge_softc *); 26563eac52bSthorpej static void bridge_rtable_fini(struct bridge_softc *); 266460da35fSthorpej 26763eac52bSthorpej static struct bridge_rtnode *bridge_rtnode_lookup(struct bridge_softc *, 268460da35fSthorpej const uint8_t *); 26963eac52bSthorpej static int bridge_rtnode_insert(struct bridge_softc *, 27063eac52bSthorpej struct bridge_rtnode *); 271e85cdef1Sozaki-r static void bridge_rtnode_remove(struct bridge_softc *, 27263eac52bSthorpej struct bridge_rtnode *); 273e85cdef1Sozaki-r static void bridge_rtnode_destroy(struct bridge_rtnode *); 274460da35fSthorpej 27563eac52bSthorpej static struct bridge_iflist *bridge_lookup_member(struct bridge_softc *, 276cc3dd2e0Sozaki-r const char *name, 277cc3dd2e0Sozaki-r struct psref *); 27863eac52bSthorpej static struct bridge_iflist *bridge_lookup_member_if(struct bridge_softc *, 279cc3dd2e0Sozaki-r struct ifnet *ifp, 280cc3dd2e0Sozaki-r struct psref *); 281cc3dd2e0Sozaki-r static void bridge_release_member(struct bridge_softc *, struct bridge_iflist *, 282cc3dd2e0Sozaki-r struct psref *); 28363eac52bSthorpej static void bridge_delete_member(struct bridge_softc *, 28463eac52bSthorpej struct bridge_iflist *); 285cc3dd2e0Sozaki-r static void bridge_acquire_member(struct bridge_softc *sc, 286cc3dd2e0Sozaki-r struct bridge_iflist *, 287cc3dd2e0Sozaki-r struct psref *); 288460da35fSthorpej 28963eac52bSthorpej static int bridge_ioctl_add(struct bridge_softc *, void *); 29063eac52bSthorpej static int bridge_ioctl_del(struct bridge_softc *, void *); 29163eac52bSthorpej static int bridge_ioctl_gifflags(struct bridge_softc *, void *); 29263eac52bSthorpej static int bridge_ioctl_sifflags(struct bridge_softc *, void *); 29363eac52bSthorpej static int bridge_ioctl_scache(struct bridge_softc *, void *); 29463eac52bSthorpej static int bridge_ioctl_gcache(struct bridge_softc *, void *); 29563eac52bSthorpej static int bridge_ioctl_gifs(struct bridge_softc *, void *); 29663eac52bSthorpej static int bridge_ioctl_rts(struct bridge_softc *, void *); 29763eac52bSthorpej static int bridge_ioctl_saddr(struct bridge_softc *, void *); 29863eac52bSthorpej static int bridge_ioctl_sto(struct bridge_softc *, void *); 29963eac52bSthorpej static int bridge_ioctl_gto(struct bridge_softc *, void *); 30063eac52bSthorpej static int bridge_ioctl_daddr(struct bridge_softc *, void *); 30163eac52bSthorpej static int bridge_ioctl_flush(struct bridge_softc *, void *); 30263eac52bSthorpej static int bridge_ioctl_gpri(struct bridge_softc *, void *); 30363eac52bSthorpej static int bridge_ioctl_spri(struct bridge_softc *, void *); 30463eac52bSthorpej static int bridge_ioctl_ght(struct bridge_softc *, void *); 30563eac52bSthorpej static int bridge_ioctl_sht(struct bridge_softc *, void *); 30663eac52bSthorpej static int bridge_ioctl_gfd(struct bridge_softc *, void *); 30763eac52bSthorpej static int bridge_ioctl_sfd(struct bridge_softc *, void *); 30863eac52bSthorpej static int bridge_ioctl_gma(struct bridge_softc *, void *); 30963eac52bSthorpej static int bridge_ioctl_sma(struct bridge_softc *, void *); 31063eac52bSthorpej static int bridge_ioctl_sifprio(struct bridge_softc *, void *); 31163eac52bSthorpej static int bridge_ioctl_sifcost(struct bridge_softc *, void *); 31263eac52bSthorpej static int bridge_ioctl_gfilt(struct bridge_softc *, void *); 31363eac52bSthorpej static int bridge_ioctl_sfilt(struct bridge_softc *, void *); 3146b857c22Sperseant static int bridge_ipf(void *, struct mbuf **, struct ifnet *, int); 3156b857c22Sperseant static int bridge_ip_checkbasic(struct mbuf **mp); 3166b857c22Sperseant # ifdef INET6 3176b857c22Sperseant static int bridge_ip6_checkbasic(struct mbuf **mp); 3186b857c22Sperseant # endif /* INET6 */ 319460da35fSthorpej 320460da35fSthorpej struct bridge_control { 321460da35fSthorpej int (*bc_func)(struct bridge_softc *, void *); 322460da35fSthorpej int bc_argsize; 323460da35fSthorpej int bc_flags; 324460da35fSthorpej }; 325460da35fSthorpej 326460da35fSthorpej #define BC_F_COPYIN 0x01 /* copy arguments in */ 327460da35fSthorpej #define BC_F_COPYOUT 0x02 /* copy arguments out */ 328460da35fSthorpej #define BC_F_SUSER 0x04 /* do super-user check */ 329ef434b48Smatt #define BC_F_XLATEIN 0x08 /* xlate arguments in */ 330ef434b48Smatt #define BC_F_XLATEOUT 0x10 /* xlate arguments out */ 331460da35fSthorpej 33263eac52bSthorpej static const struct bridge_control bridge_control_table[] = { 333ebc5b361Sdyoung [BRDGADD] = {bridge_ioctl_add, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER}, 334ebc5b361Sdyoung [BRDGDEL] = {bridge_ioctl_del, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER}, 335460da35fSthorpej 336ebc5b361Sdyoung [BRDGGIFFLGS] = {bridge_ioctl_gifflags, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_COPYOUT}, 337ebc5b361Sdyoung [BRDGSIFFLGS] = {bridge_ioctl_sifflags, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER}, 338460da35fSthorpej 339ebc5b361Sdyoung [BRDGSCACHE] = {bridge_ioctl_scache, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER}, 340ebc5b361Sdyoung [BRDGGCACHE] = {bridge_ioctl_gcache, sizeof(struct ifbrparam), BC_F_COPYOUT}, 341460da35fSthorpej 342ef434b48Smatt [OBRDGGIFS] = {bridge_ioctl_gifs, sizeof(struct ifbifconf), BC_F_COPYIN|BC_F_COPYOUT}, 343ef434b48Smatt [OBRDGRTS] = {bridge_ioctl_rts, sizeof(struct ifbaconf), BC_F_COPYIN|BC_F_COPYOUT}, 344460da35fSthorpej 345ebc5b361Sdyoung [BRDGSADDR] = {bridge_ioctl_saddr, sizeof(struct ifbareq), BC_F_COPYIN|BC_F_SUSER}, 346460da35fSthorpej 347ebc5b361Sdyoung [BRDGSTO] = {bridge_ioctl_sto, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER}, 348ebc5b361Sdyoung [BRDGGTO] = {bridge_ioctl_gto, sizeof(struct ifbrparam), BC_F_COPYOUT}, 349460da35fSthorpej 350ebc5b361Sdyoung [BRDGDADDR] = {bridge_ioctl_daddr, sizeof(struct ifbareq), BC_F_COPYIN|BC_F_SUSER}, 351460da35fSthorpej 352ebc5b361Sdyoung [BRDGFLUSH] = {bridge_ioctl_flush, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER}, 353460da35fSthorpej 354ebc5b361Sdyoung [BRDGGPRI] = {bridge_ioctl_gpri, sizeof(struct ifbrparam), BC_F_COPYOUT}, 355ebc5b361Sdyoung [BRDGSPRI] = {bridge_ioctl_spri, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER}, 356460da35fSthorpej 357ebc5b361Sdyoung [BRDGGHT] = {bridge_ioctl_ght, sizeof(struct ifbrparam), BC_F_COPYOUT}, 358ebc5b361Sdyoung [BRDGSHT] = {bridge_ioctl_sht, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER}, 359460da35fSthorpej 360ebc5b361Sdyoung [BRDGGFD] = {bridge_ioctl_gfd, sizeof(struct ifbrparam), BC_F_COPYOUT}, 361ebc5b361Sdyoung [BRDGSFD] = {bridge_ioctl_sfd, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER}, 362460da35fSthorpej 363ebc5b361Sdyoung [BRDGGMA] = {bridge_ioctl_gma, sizeof(struct ifbrparam), BC_F_COPYOUT}, 364ebc5b361Sdyoung [BRDGSMA] = {bridge_ioctl_sma, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER}, 365460da35fSthorpej 366ebc5b361Sdyoung [BRDGSIFPRIO] = {bridge_ioctl_sifprio, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER}, 3674a3c894eSbouyer 368ebc5b361Sdyoung [BRDGSIFCOST] = {bridge_ioctl_sifcost, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER}, 369424f3890Smaxv 370ebc5b361Sdyoung [BRDGGFILT] = {bridge_ioctl_gfilt, sizeof(struct ifbrparam), BC_F_COPYOUT}, 371ebc5b361Sdyoung [BRDGSFILT] = {bridge_ioctl_sfilt, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER}, 372424f3890Smaxv 373ef434b48Smatt [BRDGGIFS] = {bridge_ioctl_gifs, sizeof(struct ifbifconf), BC_F_XLATEIN|BC_F_XLATEOUT}, 374ef434b48Smatt [BRDGRTS] = {bridge_ioctl_rts, sizeof(struct ifbaconf), BC_F_XLATEIN|BC_F_XLATEOUT}, 375460da35fSthorpej }; 376ef434b48Smatt 37758cb526fSdyoung static const int bridge_control_table_size = __arraycount(bridge_control_table); 378460da35fSthorpej 37963eac52bSthorpej static struct if_clone bridge_cloner = 380460da35fSthorpej IF_CLONE_INITIALIZER("bridge", bridge_clone_create, bridge_clone_destroy); 381460da35fSthorpej 382460da35fSthorpej /* 383460da35fSthorpej * bridgeattach: 384460da35fSthorpej * 385460da35fSthorpej * Pseudo-device attach routine. 386460da35fSthorpej */ 387460da35fSthorpej void 388168cd830Schristos bridgeattach(int n) 389460da35fSthorpej { 390460da35fSthorpej 391460da35fSthorpej pool_init(&bridge_rtnode_pool, sizeof(struct bridge_rtnode), 39259d979c5Sad 0, 0, 0, "brtpl", NULL, IPL_NET); 393460da35fSthorpej 394cc3dd2e0Sozaki-r bridge_psref_class = psref_class_create("bridge", IPL_SOFTNET); 395cc3dd2e0Sozaki-r 396460da35fSthorpej if_clone_attach(&bridge_cloner); 397460da35fSthorpej } 398460da35fSthorpej 399460da35fSthorpej /* 400460da35fSthorpej * bridge_clone_create: 401460da35fSthorpej * 402460da35fSthorpej * Create a new bridge instance. 403460da35fSthorpej */ 40463eac52bSthorpej static int 405460da35fSthorpej bridge_clone_create(struct if_clone *ifc, int unit) 406460da35fSthorpej { 407460da35fSthorpej struct bridge_softc *sc; 408460da35fSthorpej struct ifnet *ifp; 409d81e97faSozaki-r int error; 410460da35fSthorpej 411df0047d0Sozaki-r sc = kmem_zalloc(sizeof(*sc), KM_SLEEP); 412460da35fSthorpej ifp = &sc->sc_if; 413460da35fSthorpej 414460da35fSthorpej sc->sc_brtmax = BRIDGE_RTABLE_MAX; 415460da35fSthorpej sc->sc_brttimeout = BRIDGE_RTABLE_TIMEOUT; 416460da35fSthorpej sc->sc_bridge_max_age = BSTP_DEFAULT_MAX_AGE; 417460da35fSthorpej sc->sc_bridge_hello_time = BSTP_DEFAULT_HELLO_TIME; 418460da35fSthorpej sc->sc_bridge_forward_delay = BSTP_DEFAULT_FORWARD_DELAY; 419460da35fSthorpej sc->sc_bridge_priority = BSTP_DEFAULT_BRIDGE_PRIORITY; 420460da35fSthorpej sc->sc_hold_time = BSTP_DEFAULT_HOLD_TIME; 4216b857c22Sperseant sc->sc_filter_flags = 0; 422460da35fSthorpej 423460da35fSthorpej /* Initialize our routing table. */ 424460da35fSthorpej bridge_rtable_init(sc); 425460da35fSthorpej 426e85cdef1Sozaki-r error = workqueue_create(&sc->sc_rtage_wq, "bridge_rtage", 427d81e97faSozaki-r bridge_rtage_work, sc, PRI_SOFTNET, IPL_SOFTNET, WQ_MPSAFE); 428e85cdef1Sozaki-r if (error) 429e85cdef1Sozaki-r panic("%s: workqueue_create %d\n", __func__, error); 430e85cdef1Sozaki-r 43101a838ccSozaki-r callout_init(&sc->sc_brcallout, CALLOUT_MPSAFE); 43201a838ccSozaki-r callout_init(&sc->sc_bstpcallout, CALLOUT_MPSAFE); 433460da35fSthorpej 434cc3dd2e0Sozaki-r mutex_init(&sc->sc_iflist_psref.bip_lock, MUTEX_DEFAULT, IPL_NONE); 435cc3dd2e0Sozaki-r PSLIST_INIT(&sc->sc_iflist_psref.bip_iflist); 436cc3dd2e0Sozaki-r sc->sc_iflist_psref.bip_psz = pserialize_create(); 437460da35fSthorpej 438bc168f27Schristos if_initname(ifp, ifc->ifc_name, unit); 439460da35fSthorpej ifp->if_softc = sc; 440436bca6cSozaki-r #ifdef NET_MPSAFE 4411f764773Sroy ifp->if_extflags = IFEF_MPSAFE; 442436bca6cSozaki-r #endif 443460da35fSthorpej ifp->if_mtu = ETHERMTU; 444460da35fSthorpej ifp->if_ioctl = bridge_ioctl; 445460da35fSthorpej ifp->if_output = bridge_output; 446460da35fSthorpej ifp->if_start = bridge_start; 447460da35fSthorpej ifp->if_stop = bridge_stop; 448460da35fSthorpej ifp->if_init = bridge_init; 449d64426f3Sitojun ifp->if_type = IFT_BRIDGE; 450460da35fSthorpej ifp->if_addrlen = 0; 451460da35fSthorpej ifp->if_dlt = DLT_EN10MB; 452460da35fSthorpej ifp->if_hdrlen = ETHER_HDR_LEN; 453076e3579Sriastradh if_initialize(ifp); 4541f764773Sroy 4551f764773Sroy /* 4561f764773Sroy * Set the link state to down. 4571f764773Sroy * When interfaces are added the link state will reflect 4581f764773Sroy * the best link state of the combined interfaces. 4591f764773Sroy */ 4601f764773Sroy ifp->if_link_state = LINK_STATE_DOWN; 4611f764773Sroy 462460da35fSthorpej if_alloc_sadl(ifp); 46374ffb1c2Sozaki-r if_register(ifp); 464460da35fSthorpej 465a40cbde0Smsaitoh return 0; 466460da35fSthorpej } 467460da35fSthorpej 468460da35fSthorpej /* 469460da35fSthorpej * bridge_clone_destroy: 470460da35fSthorpej * 471460da35fSthorpej * Destroy a bridge instance. 472460da35fSthorpej */ 47363eac52bSthorpej static int 474460da35fSthorpej bridge_clone_destroy(struct ifnet *ifp) 475460da35fSthorpej { 476460da35fSthorpej struct bridge_softc *sc = ifp->if_softc; 477460da35fSthorpej struct bridge_iflist *bif; 478460da35fSthorpej 4791de7a25cSmaxv if ((ifp->if_flags & IFF_RUNNING) != 0) 480460da35fSthorpej bridge_stop(ifp, 1); 481460da35fSthorpej 482a1c877a9Sozaki-r BRIDGE_LOCK(sc); 483814cd05cSozaki-r for (;;) { 484cc3dd2e0Sozaki-r bif = PSLIST_WRITER_FIRST(&sc->sc_iflist_psref.bip_iflist, struct bridge_iflist, 485814cd05cSozaki-r bif_next); 486814cd05cSozaki-r if (bif == NULL) 487814cd05cSozaki-r break; 488460da35fSthorpej bridge_delete_member(sc, bif); 489814cd05cSozaki-r } 490cc3dd2e0Sozaki-r PSLIST_DESTROY(&sc->sc_iflist_psref.bip_iflist); 491a1c877a9Sozaki-r BRIDGE_UNLOCK(sc); 492460da35fSthorpej 493460da35fSthorpej if_detach(ifp); 494460da35fSthorpej 495460da35fSthorpej /* Tear down the routing table. */ 496460da35fSthorpej bridge_rtable_fini(sc); 497460da35fSthorpej 498cc3dd2e0Sozaki-r pserialize_destroy(sc->sc_iflist_psref.bip_psz); 499cc3dd2e0Sozaki-r mutex_destroy(&sc->sc_iflist_psref.bip_lock); 500a40cbde0Smsaitoh callout_destroy(&sc->sc_brcallout); 501a40cbde0Smsaitoh callout_destroy(&sc->sc_bstpcallout); 502e85cdef1Sozaki-r workqueue_destroy(sc->sc_rtage_wq); 503df0047d0Sozaki-r kmem_free(sc, sizeof(*sc)); 504b9c49ebfSpeter 505a40cbde0Smsaitoh return 0; 506460da35fSthorpej } 507460da35fSthorpej 508460da35fSthorpej /* 509460da35fSthorpej * bridge_ioctl: 510460da35fSthorpej * 511460da35fSthorpej * Handle a control request from the operator. 512460da35fSthorpej */ 51363eac52bSthorpej static int 51453524e44Schristos bridge_ioctl(struct ifnet *ifp, u_long cmd, void *data) 515460da35fSthorpej { 516460da35fSthorpej struct bridge_softc *sc = ifp->if_softc; 517f474dcebSad struct lwp *l = curlwp; /* XXX */ 518460da35fSthorpej union { 519460da35fSthorpej struct ifbreq ifbreq; 520460da35fSthorpej struct ifbifconf ifbifconf; 521460da35fSthorpej struct ifbareq ifbareq; 522460da35fSthorpej struct ifbaconf ifbaconf; 523460da35fSthorpej struct ifbrparam ifbrparam; 524460da35fSthorpej } args; 525460da35fSthorpej struct ifdrv *ifd = (struct ifdrv *) data; 5266f53f02aSelad const struct bridge_control *bc = NULL; /* XXXGCC */ 527c5e9ae90Sskrll int error = 0; 528460da35fSthorpej 529c26964baSozaki-r /* Authorize command before calling splsoftnet(). */ 530460da35fSthorpej switch (cmd) { 531460da35fSthorpej case SIOCGDRVSPEC: 532460da35fSthorpej case SIOCSDRVSPEC: 533ef434b48Smatt if (ifd->ifd_cmd >= bridge_control_table_size 534ef434b48Smatt || (bc = &bridge_control_table[ifd->ifd_cmd]) == NULL) { 535460da35fSthorpej error = EINVAL; 5369c2bd940Scegger return error; 537460da35fSthorpej } 5386f53f02aSelad 5396f53f02aSelad /* We only care about BC_F_SUSER at this point. */ 5406f53f02aSelad if ((bc->bc_flags & BC_F_SUSER) == 0) 5416f53f02aSelad break; 5426f53f02aSelad 5430c9d8d15Selad error = kauth_authorize_network(l->l_cred, 5440c9d8d15Selad KAUTH_NETWORK_INTERFACE_BRIDGE, 5450c9d8d15Selad cmd == SIOCGDRVSPEC ? 5460c9d8d15Selad KAUTH_REQ_NETWORK_INTERFACE_BRIDGE_GETPRIV : 547268cd672Swiz KAUTH_REQ_NETWORK_INTERFACE_BRIDGE_SETPRIV, 5480c9d8d15Selad ifd, NULL, NULL); 5496f53f02aSelad if (error) 550a40cbde0Smsaitoh return error; 5516f53f02aSelad 5526f53f02aSelad break; 5536f53f02aSelad } 5546f53f02aSelad 555c5e9ae90Sskrll const int s = splsoftnet(); 5566f53f02aSelad 5576f53f02aSelad switch (cmd) { 5586f53f02aSelad case SIOCGDRVSPEC: 5596f53f02aSelad case SIOCSDRVSPEC: 5609c2bd940Scegger KASSERT(bc != NULL); 561460da35fSthorpej if (cmd == SIOCGDRVSPEC && 562ef434b48Smatt (bc->bc_flags & (BC_F_COPYOUT|BC_F_XLATEOUT)) == 0) { 563dcfad963Skristerw error = EINVAL; 564dcfad963Skristerw break; 565dcfad963Skristerw } 566460da35fSthorpej else if (cmd == SIOCSDRVSPEC && 567ef434b48Smatt (bc->bc_flags & (BC_F_COPYOUT|BC_F_XLATEOUT)) != 0) { 568dcfad963Skristerw error = EINVAL; 569dcfad963Skristerw break; 570dcfad963Skristerw } 571460da35fSthorpej 572c26964baSozaki-r /* BC_F_SUSER is checked above, before splsoftnet(). */ 573460da35fSthorpej 574ef434b48Smatt if ((bc->bc_flags & (BC_F_XLATEIN|BC_F_XLATEOUT)) == 0 575ef434b48Smatt && (ifd->ifd_len != bc->bc_argsize 576ef434b48Smatt || ifd->ifd_len > sizeof(args))) { 577460da35fSthorpej error = EINVAL; 578460da35fSthorpej break; 579460da35fSthorpej } 580460da35fSthorpej 5811482d7ceSchristos memset(&args, 0, sizeof(args)); 582460da35fSthorpej if (bc->bc_flags & BC_F_COPYIN) { 583460da35fSthorpej error = copyin(ifd->ifd_data, &args, ifd->ifd_len); 584460da35fSthorpej if (error) 585460da35fSthorpej break; 586ef434b48Smatt } else if (bc->bc_flags & BC_F_XLATEIN) { 587ef434b48Smatt args.ifbifconf.ifbic_len = ifd->ifd_len; 588ef434b48Smatt args.ifbifconf.ifbic_buf = ifd->ifd_data; 589460da35fSthorpej } 590460da35fSthorpej 591460da35fSthorpej error = (*bc->bc_func)(sc, &args); 592460da35fSthorpej if (error) 593460da35fSthorpej break; 594460da35fSthorpej 595ef434b48Smatt if (bc->bc_flags & BC_F_COPYOUT) { 596460da35fSthorpej error = copyout(&args, ifd->ifd_data, ifd->ifd_len); 597ef434b48Smatt } else if (bc->bc_flags & BC_F_XLATEOUT) { 598ef434b48Smatt ifd->ifd_len = args.ifbifconf.ifbic_len; 599ef434b48Smatt ifd->ifd_data = args.ifbifconf.ifbic_buf; 600ef434b48Smatt } 601460da35fSthorpej break; 602460da35fSthorpej 603460da35fSthorpej case SIOCSIFFLAGS: 604de87fe67Sdyoung if ((error = ifioctl_common(ifp, cmd, data)) != 0) 605de87fe67Sdyoung break; 606de87fe67Sdyoung switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) { 607de87fe67Sdyoung case IFF_RUNNING: 608460da35fSthorpej /* 609460da35fSthorpej * If interface is marked down and it is running, 610460da35fSthorpej * then stop and disable it. 611460da35fSthorpej */ 61266fd63daSriastradh if_stop(ifp, 1); 613de87fe67Sdyoung break; 614de87fe67Sdyoung case IFF_UP: 615460da35fSthorpej /* 616460da35fSthorpej * If interface is marked up and it is stopped, then 617460da35fSthorpej * start it. 618460da35fSthorpej */ 619b4d088cbSriastradh error = if_init(ifp); 620de87fe67Sdyoung break; 621de87fe67Sdyoung default: 622de87fe67Sdyoung break; 623460da35fSthorpej } 624460da35fSthorpej break; 625460da35fSthorpej 62677deda08Sozaki-r case SIOCSIFMTU: 62777deda08Sozaki-r if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET) 62877deda08Sozaki-r error = 0; 62977deda08Sozaki-r break; 63077deda08Sozaki-r 631f91b8005Sjdolecek case SIOCGIFCAP: 632f91b8005Sjdolecek { 633f91b8005Sjdolecek struct ifcapreq *ifcr = (struct ifcapreq *)data; 634f91b8005Sjdolecek ifcr->ifcr_capabilities = sc->sc_capenable; 635f91b8005Sjdolecek ifcr->ifcr_capenable = sc->sc_capenable; 636f91b8005Sjdolecek break; 637f91b8005Sjdolecek } 638f91b8005Sjdolecek 639460da35fSthorpej default: 640de87fe67Sdyoung error = ifioctl_common(ifp, cmd, data); 641460da35fSthorpej break; 642460da35fSthorpej } 643460da35fSthorpej 644460da35fSthorpej splx(s); 645460da35fSthorpej 646a40cbde0Smsaitoh return error; 647460da35fSthorpej } 648460da35fSthorpej 649460da35fSthorpej /* 650460da35fSthorpej * bridge_lookup_member: 651460da35fSthorpej * 6529161a49eSozaki-r * Lookup a bridge member interface. 653460da35fSthorpej */ 65463eac52bSthorpej static struct bridge_iflist * 655cc3dd2e0Sozaki-r bridge_lookup_member(struct bridge_softc *sc, const char *name, struct psref *psref) 656460da35fSthorpej { 657460da35fSthorpej struct bridge_iflist *bif; 658460da35fSthorpej struct ifnet *ifp; 659cba69a87Sozaki-r int s; 660460da35fSthorpej 6612977110aSozaki-r s = pserialize_read_enter(); 6629161a49eSozaki-r 663cc3dd2e0Sozaki-r BRIDGE_IFLIST_READER_FOREACH(bif, sc) { 664460da35fSthorpej ifp = bif->bif_ifp; 665460da35fSthorpej if (strcmp(ifp->if_xname, name) == 0) 6669161a49eSozaki-r break; 667460da35fSthorpej } 668cc3dd2e0Sozaki-r if (bif != NULL) 669cc3dd2e0Sozaki-r bridge_acquire_member(sc, bif, psref); 670460da35fSthorpej 6712977110aSozaki-r pserialize_read_exit(s); 6729161a49eSozaki-r 6739161a49eSozaki-r return bif; 674460da35fSthorpej } 675460da35fSthorpej 676460da35fSthorpej /* 677f54ddc3cSmartin * bridge_lookup_member_if: 678f54ddc3cSmartin * 6799161a49eSozaki-r * Lookup a bridge member interface by ifnet*. 680f54ddc3cSmartin */ 68163eac52bSthorpej static struct bridge_iflist * 682cc3dd2e0Sozaki-r bridge_lookup_member_if(struct bridge_softc *sc, struct ifnet *member_ifp, 683cc3dd2e0Sozaki-r struct psref *psref) 684f54ddc3cSmartin { 685f54ddc3cSmartin struct bridge_iflist *bif; 686cba69a87Sozaki-r int s; 687f54ddc3cSmartin 6882977110aSozaki-r s = pserialize_read_enter(); 6899161a49eSozaki-r 6909161a49eSozaki-r bif = member_ifp->if_bridgeif; 691cc3dd2e0Sozaki-r if (bif != NULL) { 692cc3dd2e0Sozaki-r psref_acquire(psref, &bif->bif_psref, 693cc3dd2e0Sozaki-r bridge_psref_class); 694cc3dd2e0Sozaki-r } 6959161a49eSozaki-r 6962977110aSozaki-r pserialize_read_exit(s); 697cba69a87Sozaki-r 698cba69a87Sozaki-r return bif; 699cba69a87Sozaki-r } 700cba69a87Sozaki-r 701cc3dd2e0Sozaki-r static void 702cc3dd2e0Sozaki-r bridge_acquire_member(struct bridge_softc *sc, struct bridge_iflist *bif, 703cc3dd2e0Sozaki-r struct psref *psref) 704cba69a87Sozaki-r { 705d81e97faSozaki-r 706cc3dd2e0Sozaki-r psref_acquire(psref, &bif->bif_psref, bridge_psref_class); 707f54ddc3cSmartin } 708f54ddc3cSmartin 7099161a49eSozaki-r /* 7109161a49eSozaki-r * bridge_release_member: 7119161a49eSozaki-r * 7129161a49eSozaki-r * Release the specified member interface. 7139161a49eSozaki-r */ 7149161a49eSozaki-r static void 715cc3dd2e0Sozaki-r bridge_release_member(struct bridge_softc *sc, struct bridge_iflist *bif, 716cc3dd2e0Sozaki-r struct psref *psref) 7179161a49eSozaki-r { 718cba69a87Sozaki-r 719cc3dd2e0Sozaki-r psref_release(psref, &bif->bif_psref, bridge_psref_class); 720f54ddc3cSmartin } 721f54ddc3cSmartin 722f54ddc3cSmartin /* 723460da35fSthorpej * bridge_delete_member: 724460da35fSthorpej * 725460da35fSthorpej * Delete the specified member interface. 726460da35fSthorpej */ 72763eac52bSthorpej static void 728460da35fSthorpej bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif) 729460da35fSthorpej { 730460da35fSthorpej struct ifnet *ifs = bif->bif_ifp; 731460da35fSthorpej 732a1c877a9Sozaki-r KASSERT(BRIDGE_LOCKED(sc)); 733460da35fSthorpej 7349c4cd063Sozaki-r ifs->_if_input = ether_input; 735460da35fSthorpej ifs->if_bridge = NULL; 7369161a49eSozaki-r ifs->if_bridgeif = NULL; 7379161a49eSozaki-r 738814cd05cSozaki-r PSLIST_WRITER_REMOVE(bif, bif_next); 739cba69a87Sozaki-r BRIDGE_PSZ_PERFORM(sc); 740b2fb0ac7Syamaguchi 741b2fb0ac7Syamaguchi if_linkstate_change_disestablish(ifs, 742b2fb0ac7Syamaguchi bif->bif_linkstate_hook, BRIDGE_LOCK_OBJ(sc)); 743c06d6dc9Syamaguchi ether_ifdetachhook_disestablish(ifs, 744c06d6dc9Syamaguchi bif->bif_ifdetach_hook, BRIDGE_LOCK_OBJ(sc)); 745b2fb0ac7Syamaguchi 746b7a310caSozaki-r BRIDGE_UNLOCK(sc); 747460da35fSthorpej 748b6dd10ffSrin switch (ifs->if_type) { 749b6dd10ffSrin case IFT_ETHER: 750b6dd10ffSrin case IFT_L2TP: 751b6dd10ffSrin /* 752b6dd10ffSrin * Take the interface out of promiscuous mode. 753b6dd10ffSrin * Don't call it with holding a spin lock. 754b6dd10ffSrin */ 755b6dd10ffSrin (void) ifpromisc(ifs, 0); 756b6dd10ffSrin IFNET_LOCK(ifs); 757b6dd10ffSrin (void) ether_disable_vlan_mtu(ifs); 758b6dd10ffSrin IFNET_UNLOCK(ifs); 759b6dd10ffSrin break; 760b6dd10ffSrin default: 761b6dd10ffSrin #ifdef DIAGNOSTIC 762b6dd10ffSrin panic("%s: impossible", __func__); 763b6dd10ffSrin #endif 764b6dd10ffSrin break; 765b6dd10ffSrin } 766b6dd10ffSrin 767cc3dd2e0Sozaki-r psref_target_destroy(&bif->bif_psref, bridge_psref_class); 768cc3dd2e0Sozaki-r 769ba236d2eSozaki-r PSLIST_ENTRY_DESTROY(bif, bif_next); 770df0047d0Sozaki-r kmem_free(bif, sizeof(*bif)); 771a1c877a9Sozaki-r 772a1c877a9Sozaki-r BRIDGE_LOCK(sc); 773460da35fSthorpej } 774460da35fSthorpej 77573240bb1Srin /* 77673240bb1Srin * bridge_calc_csum_flags: 77773240bb1Srin * 77873240bb1Srin * Calculate logical and b/w csum flags each member interface supports. 77973240bb1Srin */ 78073240bb1Srin void 78173240bb1Srin bridge_calc_csum_flags(struct bridge_softc *sc) 78273240bb1Srin { 78373240bb1Srin struct bridge_iflist *bif; 7846d5b2531Sjdolecek struct ifnet *ifs = NULL; 78573240bb1Srin int flags = ~0; 786f91b8005Sjdolecek int capenable = ~0; 78773240bb1Srin 78873240bb1Srin BRIDGE_LOCK(sc); 78973240bb1Srin BRIDGE_IFLIST_READER_FOREACH(bif, sc) { 79073240bb1Srin ifs = bif->bif_ifp; 79173240bb1Srin flags &= ifs->if_csum_flags_tx; 792f91b8005Sjdolecek capenable &= ifs->if_capenable; 79373240bb1Srin } 79473240bb1Srin sc->sc_csum_flags_tx = flags; 7956d5b2531Sjdolecek sc->sc_capenable = (ifs != NULL) ? capenable : 0; 79673240bb1Srin BRIDGE_UNLOCK(sc); 79773240bb1Srin } 79873240bb1Srin 7991f764773Sroy /* 8001f764773Sroy * bridge_calc_link_state: 8011f764773Sroy * 8021f764773Sroy * Calculate the link state based on each member interface. 8031f764773Sroy */ 804b2fb0ac7Syamaguchi static void 805b2fb0ac7Syamaguchi bridge_calc_link_state(void *xsc) 8061f764773Sroy { 807b2fb0ac7Syamaguchi struct bridge_softc *sc = xsc; 8081f764773Sroy struct bridge_iflist *bif; 8091f764773Sroy struct ifnet *ifs; 8101f764773Sroy int link_state = LINK_STATE_DOWN; 8111f764773Sroy 8121f764773Sroy BRIDGE_LOCK(sc); 8131f764773Sroy BRIDGE_IFLIST_READER_FOREACH(bif, sc) { 8141f764773Sroy ifs = bif->bif_ifp; 8151f764773Sroy if (ifs->if_link_state == LINK_STATE_UP) { 8161f764773Sroy link_state = LINK_STATE_UP; 8171f764773Sroy break; 8181f764773Sroy } 8191f764773Sroy if (ifs->if_link_state == LINK_STATE_UNKNOWN) 8201f764773Sroy link_state = LINK_STATE_UNKNOWN; 8211f764773Sroy } 8221f764773Sroy if_link_state_change(&sc->sc_if, link_state); 8231f764773Sroy BRIDGE_UNLOCK(sc); 8241f764773Sroy } 8251f764773Sroy 82663eac52bSthorpej static int 827460da35fSthorpej bridge_ioctl_add(struct bridge_softc *sc, void *arg) 828460da35fSthorpej { 829460da35fSthorpej struct ifbreq *req = arg; 830460da35fSthorpej struct bridge_iflist *bif = NULL; 831460da35fSthorpej struct ifnet *ifs; 832460da35fSthorpej int error = 0; 8332ccabb7fSozaki-r struct psref psref; 834460da35fSthorpej 8352ccabb7fSozaki-r ifs = if_get(req->ifbr_ifsname, &psref); 836460da35fSthorpej if (ifs == NULL) 837a40cbde0Smsaitoh return ENOENT; 838460da35fSthorpej 8392ccabb7fSozaki-r if (ifs->if_bridge == sc) { 8402ccabb7fSozaki-r error = EEXIST; 8412ccabb7fSozaki-r goto out; 8422ccabb7fSozaki-r } 843460da35fSthorpej 8442ccabb7fSozaki-r if (ifs->if_bridge != NULL) { 8452ccabb7fSozaki-r error = EBUSY; 8462ccabb7fSozaki-r goto out; 8472ccabb7fSozaki-r } 848460da35fSthorpej 8492ccabb7fSozaki-r if (ifs->_if_input != ether_input) { 8502ccabb7fSozaki-r error = EINVAL; 8512ccabb7fSozaki-r goto out; 8522ccabb7fSozaki-r } 853cb4cb631Sozaki-r 854a2a285faSozaki-r /* FIXME: doesn't work with non-IFF_SIMPLEX interfaces */ 8552ccabb7fSozaki-r if ((ifs->if_flags & IFF_SIMPLEX) == 0) { 8562ccabb7fSozaki-r error = EINVAL; 8572ccabb7fSozaki-r goto out; 8582ccabb7fSozaki-r } 859a2a285faSozaki-r 860df0047d0Sozaki-r bif = kmem_alloc(sizeof(*bif), KM_SLEEP); 861460da35fSthorpej 862460da35fSthorpej switch (ifs->if_type) { 863460da35fSthorpej case IFT_ETHER: 864939a415aSknakahara if (sc->sc_if.if_mtu != ifs->if_mtu) { 865b1e0f9eeSjdolecek /* Change MTU of added interface to bridge MTU */ 866b1e0f9eeSjdolecek struct ifreq ifr; 867b1e0f9eeSjdolecek memset(&ifr, 0, sizeof(ifr)); 868b1e0f9eeSjdolecek ifr.ifr_mtu = sc->sc_if.if_mtu; 869b1e0f9eeSjdolecek IFNET_LOCK(ifs); 870daeb11daSriastradh error = if_ioctl(ifs, SIOCSIFMTU, &ifr); 871b1e0f9eeSjdolecek IFNET_UNLOCK(ifs); 872b1e0f9eeSjdolecek if (error != 0) 873939a415aSknakahara goto out; 874939a415aSknakahara } 875939a415aSknakahara /* FALLTHROUGH */ 876939a415aSknakahara case IFT_L2TP: 877cb1c111aSozaki-r IFNET_LOCK(ifs); 878db9a3449Sozaki-r error = ether_enable_vlan_mtu(ifs); 879cb1c111aSozaki-r IFNET_UNLOCK(ifs); 880e07f95a1Sozaki-r if (error > 0) 88188b3ee5eSchristos goto out; 882460da35fSthorpej /* 883460da35fSthorpej * Place the interface into promiscuous mode. 884460da35fSthorpej */ 885460da35fSthorpej error = ifpromisc(ifs, 1); 886460da35fSthorpej if (error) 887460da35fSthorpej goto out; 888460da35fSthorpej break; 889460da35fSthorpej default: 890d1f2e630Sjdolecek error = EINVAL; 891d1f2e630Sjdolecek goto out; 892460da35fSthorpej } 893460da35fSthorpej 894460da35fSthorpej bif->bif_ifp = ifs; 895460da35fSthorpej bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER; 896460da35fSthorpej bif->bif_priority = BSTP_DEFAULT_PORT_PRIORITY; 897460da35fSthorpej bif->bif_path_cost = BSTP_DEFAULT_PATH_COST; 898b2fb0ac7Syamaguchi bif->bif_linkstate_hook = if_linkstate_change_establish(ifs, 899b2fb0ac7Syamaguchi bridge_calc_link_state, sc); 900814cd05cSozaki-r PSLIST_ENTRY_INIT(bif, bif_next); 901cc3dd2e0Sozaki-r psref_target_init(&bif->bif_psref, bridge_psref_class); 9029161a49eSozaki-r 9039161a49eSozaki-r BRIDGE_LOCK(sc); 904460da35fSthorpej 905460da35fSthorpej ifs->if_bridge = sc; 9069161a49eSozaki-r ifs->if_bridgeif = bif; 907cc3dd2e0Sozaki-r PSLIST_WRITER_INSERT_HEAD(&sc->sc_iflist_psref.bip_iflist, bif, bif_next); 9089c4cd063Sozaki-r ifs->_if_input = bridge_input; 909460da35fSthorpej 9109161a49eSozaki-r BRIDGE_UNLOCK(sc); 9119161a49eSozaki-r 912c06d6dc9Syamaguchi bif->bif_ifdetach_hook = ether_ifdetachhook_establish(ifs, 913c06d6dc9Syamaguchi bridge_ifdetach, (void *)ifs); 914c06d6dc9Syamaguchi 91573240bb1Srin bridge_calc_csum_flags(sc); 9161f764773Sroy bridge_calc_link_state(sc); 91773240bb1Srin 918460da35fSthorpej if (sc->sc_if.if_flags & IFF_RUNNING) 919460da35fSthorpej bstp_initialization(sc); 920460da35fSthorpej else 921460da35fSthorpej bstp_stop(sc); 922460da35fSthorpej 923460da35fSthorpej out: 9242ccabb7fSozaki-r if_put(ifs, &psref); 925460da35fSthorpej if (error) { 926460da35fSthorpej if (bif != NULL) 927df0047d0Sozaki-r kmem_free(bif, sizeof(*bif)); 928460da35fSthorpej } 929a40cbde0Smsaitoh return error; 930460da35fSthorpej } 931460da35fSthorpej 93263eac52bSthorpej static int 933460da35fSthorpej bridge_ioctl_del(struct bridge_softc *sc, void *arg) 934460da35fSthorpej { 935460da35fSthorpej struct ifbreq *req = arg; 9369161a49eSozaki-r const char *name = req->ifbr_ifsname; 937460da35fSthorpej struct bridge_iflist *bif; 9389161a49eSozaki-r struct ifnet *ifs; 939460da35fSthorpej 9409161a49eSozaki-r BRIDGE_LOCK(sc); 9419161a49eSozaki-r 9429161a49eSozaki-r /* 9439161a49eSozaki-r * Don't use bridge_lookup_member. We want to get a member 9449161a49eSozaki-r * with bif_refs == 0. 9459161a49eSozaki-r */ 946cc3dd2e0Sozaki-r BRIDGE_IFLIST_WRITER_FOREACH(bif, sc) { 9479161a49eSozaki-r ifs = bif->bif_ifp; 9489161a49eSozaki-r if (strcmp(ifs->if_xname, name) == 0) 9499161a49eSozaki-r break; 9509161a49eSozaki-r } 9519161a49eSozaki-r 9529161a49eSozaki-r if (bif == NULL) { 9539161a49eSozaki-r BRIDGE_UNLOCK(sc); 9549161a49eSozaki-r return ENOENT; 9559161a49eSozaki-r } 956460da35fSthorpej 957460da35fSthorpej bridge_delete_member(sc, bif); 958460da35fSthorpej 959a1c877a9Sozaki-r BRIDGE_UNLOCK(sc); 9609161a49eSozaki-r 9619161a49eSozaki-r bridge_rtdelete(sc, ifs); 96273240bb1Srin bridge_calc_csum_flags(sc); 9631f764773Sroy bridge_calc_link_state(sc); 9649161a49eSozaki-r 9659161a49eSozaki-r if (sc->sc_if.if_flags & IFF_RUNNING) 9669161a49eSozaki-r bstp_initialization(sc); 9679161a49eSozaki-r 9689161a49eSozaki-r return 0; 969460da35fSthorpej } 970460da35fSthorpej 97163eac52bSthorpej static int 972460da35fSthorpej bridge_ioctl_gifflags(struct bridge_softc *sc, void *arg) 973460da35fSthorpej { 974460da35fSthorpej struct ifbreq *req = arg; 975460da35fSthorpej struct bridge_iflist *bif; 976cc3dd2e0Sozaki-r struct psref psref; 977460da35fSthorpej 978cc3dd2e0Sozaki-r bif = bridge_lookup_member(sc, req->ifbr_ifsname, &psref); 979460da35fSthorpej if (bif == NULL) 980a40cbde0Smsaitoh return ENOENT; 981460da35fSthorpej 982460da35fSthorpej req->ifbr_ifsflags = bif->bif_flags; 983460da35fSthorpej req->ifbr_state = bif->bif_state; 984460da35fSthorpej req->ifbr_priority = bif->bif_priority; 9854a3c894eSbouyer req->ifbr_path_cost = bif->bif_path_cost; 986460da35fSthorpej req->ifbr_portno = bif->bif_ifp->if_index & 0xff; 987460da35fSthorpej 988cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 9899161a49eSozaki-r 990a40cbde0Smsaitoh return 0; 991460da35fSthorpej } 992460da35fSthorpej 99363eac52bSthorpej static int 994460da35fSthorpej bridge_ioctl_sifflags(struct bridge_softc *sc, void *arg) 995460da35fSthorpej { 996460da35fSthorpej struct ifbreq *req = arg; 997460da35fSthorpej struct bridge_iflist *bif; 998cc3dd2e0Sozaki-r struct psref psref; 999460da35fSthorpej 1000cc3dd2e0Sozaki-r bif = bridge_lookup_member(sc, req->ifbr_ifsname, &psref); 1001460da35fSthorpej if (bif == NULL) 1002a40cbde0Smsaitoh return ENOENT; 1003460da35fSthorpej 1004460da35fSthorpej if (req->ifbr_ifsflags & IFBIF_STP) { 1005460da35fSthorpej switch (bif->bif_ifp->if_type) { 1006460da35fSthorpej case IFT_ETHER: 1007939a415aSknakahara case IFT_L2TP: 1008460da35fSthorpej /* These can do spanning tree. */ 1009460da35fSthorpej break; 1010460da35fSthorpej 1011460da35fSthorpej default: 1012460da35fSthorpej /* Nothing else can. */ 1013cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 1014a40cbde0Smsaitoh return EINVAL; 1015460da35fSthorpej } 1016460da35fSthorpej } 1017460da35fSthorpej 1018008f982cSozaki-r if (bif->bif_flags & IFBIF_PROTECTED) { 1019008f982cSozaki-r if ((req->ifbr_ifsflags & IFBIF_PROTECTED) == 0) { 1020008f982cSozaki-r log(LOG_INFO, "%s: disabling protection on %s\n", 1021008f982cSozaki-r sc->sc_if.if_xname, bif->bif_ifp->if_xname); 1022008f982cSozaki-r } 1023008f982cSozaki-r } else { 1024008f982cSozaki-r if (req->ifbr_ifsflags & IFBIF_PROTECTED) { 1025008f982cSozaki-r log(LOG_INFO, "%s: enabling protection on %s\n", 1026008f982cSozaki-r sc->sc_if.if_xname, bif->bif_ifp->if_xname); 1027008f982cSozaki-r } 1028008f982cSozaki-r } 1029008f982cSozaki-r 1030460da35fSthorpej bif->bif_flags = req->ifbr_ifsflags; 1031460da35fSthorpej 1032cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 10339161a49eSozaki-r 1034460da35fSthorpej if (sc->sc_if.if_flags & IFF_RUNNING) 1035460da35fSthorpej bstp_initialization(sc); 1036460da35fSthorpej 1037a40cbde0Smsaitoh return 0; 1038460da35fSthorpej } 1039460da35fSthorpej 104063eac52bSthorpej static int 1041460da35fSthorpej bridge_ioctl_scache(struct bridge_softc *sc, void *arg) 1042460da35fSthorpej { 1043460da35fSthorpej struct ifbrparam *param = arg; 1044460da35fSthorpej 1045460da35fSthorpej sc->sc_brtmax = param->ifbrp_csize; 1046460da35fSthorpej bridge_rttrim(sc); 1047460da35fSthorpej 1048a40cbde0Smsaitoh return 0; 1049460da35fSthorpej } 1050460da35fSthorpej 105163eac52bSthorpej static int 1052460da35fSthorpej bridge_ioctl_gcache(struct bridge_softc *sc, void *arg) 1053460da35fSthorpej { 1054460da35fSthorpej struct ifbrparam *param = arg; 1055460da35fSthorpej 1056460da35fSthorpej param->ifbrp_csize = sc->sc_brtmax; 1057460da35fSthorpej 1058a40cbde0Smsaitoh return 0; 1059460da35fSthorpej } 1060460da35fSthorpej 106163eac52bSthorpej static int 1062460da35fSthorpej bridge_ioctl_gifs(struct bridge_softc *sc, void *arg) 1063460da35fSthorpej { 1064460da35fSthorpej struct ifbifconf *bifc = arg; 1065460da35fSthorpej struct bridge_iflist *bif; 10662e472fa1Sozaki-r struct ifbreq *breqs; 10672e472fa1Sozaki-r int i, count, error = 0; 1068460da35fSthorpej 1069df0047d0Sozaki-r retry: 10709161a49eSozaki-r BRIDGE_LOCK(sc); 1071460da35fSthorpej count = 0; 1072cc3dd2e0Sozaki-r BRIDGE_IFLIST_WRITER_FOREACH(bif, sc) 1073460da35fSthorpej count++; 1074df0047d0Sozaki-r BRIDGE_UNLOCK(sc); 1075df0047d0Sozaki-r 1076df0047d0Sozaki-r if (count == 0) { 1077df0047d0Sozaki-r bifc->ifbic_len = 0; 1078df0047d0Sozaki-r return 0; 1079df0047d0Sozaki-r } 1080460da35fSthorpej 10812e472fa1Sozaki-r if (bifc->ifbic_len == 0 || bifc->ifbic_len < (sizeof(*breqs) * count)) { 10822e472fa1Sozaki-r /* Tell that a larger buffer is needed */ 10832e472fa1Sozaki-r bifc->ifbic_len = sizeof(*breqs) * count; 10842e472fa1Sozaki-r return 0; 1085460da35fSthorpej } 1086460da35fSthorpej 1087df0047d0Sozaki-r breqs = kmem_alloc(sizeof(*breqs) * count, KM_SLEEP); 1088df0047d0Sozaki-r 1089df0047d0Sozaki-r BRIDGE_LOCK(sc); 1090df0047d0Sozaki-r 1091df0047d0Sozaki-r i = 0; 1092cc3dd2e0Sozaki-r BRIDGE_IFLIST_WRITER_FOREACH(bif, sc) 1093df0047d0Sozaki-r i++; 1094df0047d0Sozaki-r if (i > count) { 1095df0047d0Sozaki-r /* 1096df0047d0Sozaki-r * The number of members has been increased. 1097df0047d0Sozaki-r * We need more memory! 1098df0047d0Sozaki-r */ 1099df0047d0Sozaki-r BRIDGE_UNLOCK(sc); 1100df0047d0Sozaki-r kmem_free(breqs, sizeof(*breqs) * count); 1101df0047d0Sozaki-r goto retry; 1102df0047d0Sozaki-r } 1103460da35fSthorpej 11042e472fa1Sozaki-r i = 0; 1105cc3dd2e0Sozaki-r BRIDGE_IFLIST_WRITER_FOREACH(bif, sc) { 11062e472fa1Sozaki-r struct ifbreq *breq = &breqs[i++]; 11072e472fa1Sozaki-r memset(breq, 0, sizeof(*breq)); 11082e472fa1Sozaki-r 11092e472fa1Sozaki-r strlcpy(breq->ifbr_ifsname, bif->bif_ifp->if_xname, 11102e472fa1Sozaki-r sizeof(breq->ifbr_ifsname)); 11112e472fa1Sozaki-r breq->ifbr_ifsflags = bif->bif_flags; 11122e472fa1Sozaki-r breq->ifbr_state = bif->bif_state; 11132e472fa1Sozaki-r breq->ifbr_priority = bif->bif_priority; 11142e472fa1Sozaki-r breq->ifbr_path_cost = bif->bif_path_cost; 11152e472fa1Sozaki-r breq->ifbr_portno = bif->bif_ifp->if_index & 0xff; 11162e472fa1Sozaki-r } 11172e472fa1Sozaki-r 11182e472fa1Sozaki-r /* Don't call copyout with holding the mutex */ 11192e472fa1Sozaki-r BRIDGE_UNLOCK(sc); 11202e472fa1Sozaki-r 11212e472fa1Sozaki-r for (i = 0; i < count; i++) { 11222e472fa1Sozaki-r error = copyout(&breqs[i], bifc->ifbic_req + i, sizeof(*breqs)); 1123460da35fSthorpej if (error) 1124460da35fSthorpej break; 1125460da35fSthorpej } 11262e472fa1Sozaki-r bifc->ifbic_len = sizeof(*breqs) * i; 1127460da35fSthorpej 1128df0047d0Sozaki-r kmem_free(breqs, sizeof(*breqs) * count); 11299161a49eSozaki-r 11302e472fa1Sozaki-r return error; 1131460da35fSthorpej } 1132460da35fSthorpej 113363eac52bSthorpej static int 1134460da35fSthorpej bridge_ioctl_rts(struct bridge_softc *sc, void *arg) 1135460da35fSthorpej { 1136460da35fSthorpej struct ifbaconf *bac = arg; 1137460da35fSthorpej struct bridge_rtnode *brt; 1138460da35fSthorpej struct ifbareq bareq; 1139460da35fSthorpej int count = 0, error = 0, len; 1140460da35fSthorpej 1141460da35fSthorpej if (bac->ifbac_len == 0) 1142a40cbde0Smsaitoh return 0; 1143460da35fSthorpej 1144b7a310caSozaki-r BRIDGE_RT_LOCK(sc); 11459161a49eSozaki-r 114663f183afSozaki-r /* The passed buffer is not enough, tell a required size. */ 114763f183afSozaki-r if (bac->ifbac_len < (sizeof(bareq) * sc->sc_brtcnt)) { 114863f183afSozaki-r count = sc->sc_brtcnt; 114963f183afSozaki-r goto out; 115063f183afSozaki-r } 115163f183afSozaki-r 1152460da35fSthorpej len = bac->ifbac_len; 11537a003d61Sozaki-r BRIDGE_RTLIST_WRITER_FOREACH(brt, sc) { 1154460da35fSthorpej if (len < sizeof(bareq)) 1155460da35fSthorpej goto out; 11561482d7ceSchristos memset(&bareq, 0, sizeof(bareq)); 11574008ec12Sitojun strlcpy(bareq.ifba_ifsname, brt->brt_ifp->if_xname, 11584008ec12Sitojun sizeof(bareq.ifba_ifsname)); 1159460da35fSthorpej memcpy(bareq.ifba_dst, brt->brt_addr, sizeof(brt->brt_addr)); 1160de4337abSkardel if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { 1161de4337abSkardel bareq.ifba_expire = brt->brt_expire - time_uptime; 1162de4337abSkardel } else 11639b50223aSthorpej bareq.ifba_expire = 0; 1164460da35fSthorpej bareq.ifba_flags = brt->brt_flags; 1165460da35fSthorpej 1166460da35fSthorpej error = copyout(&bareq, bac->ifbac_req + count, sizeof(bareq)); 1167460da35fSthorpej if (error) 1168460da35fSthorpej goto out; 1169460da35fSthorpej count++; 1170460da35fSthorpej len -= sizeof(bareq); 1171460da35fSthorpej } 1172460da35fSthorpej out: 1173b7a310caSozaki-r BRIDGE_RT_UNLOCK(sc); 11749161a49eSozaki-r 1175460da35fSthorpej bac->ifbac_len = sizeof(bareq) * count; 1176a40cbde0Smsaitoh return error; 1177460da35fSthorpej } 1178460da35fSthorpej 117963eac52bSthorpej static int 1180460da35fSthorpej bridge_ioctl_saddr(struct bridge_softc *sc, void *arg) 1181460da35fSthorpej { 1182460da35fSthorpej struct ifbareq *req = arg; 1183460da35fSthorpej struct bridge_iflist *bif; 1184460da35fSthorpej int error; 1185cc3dd2e0Sozaki-r struct psref psref; 1186460da35fSthorpej 1187cc3dd2e0Sozaki-r bif = bridge_lookup_member(sc, req->ifba_ifsname, &psref); 1188460da35fSthorpej if (bif == NULL) 1189a40cbde0Smsaitoh return ENOENT; 1190460da35fSthorpej 1191460da35fSthorpej error = bridge_rtupdate(sc, req->ifba_dst, bif->bif_ifp, 1, 1192460da35fSthorpej req->ifba_flags); 1193460da35fSthorpej 1194cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 11959161a49eSozaki-r 1196a40cbde0Smsaitoh return error; 1197460da35fSthorpej } 1198460da35fSthorpej 119963eac52bSthorpej static int 1200460da35fSthorpej bridge_ioctl_sto(struct bridge_softc *sc, void *arg) 1201460da35fSthorpej { 1202460da35fSthorpej struct ifbrparam *param = arg; 1203460da35fSthorpej 1204460da35fSthorpej sc->sc_brttimeout = param->ifbrp_ctime; 1205460da35fSthorpej 1206a40cbde0Smsaitoh return 0; 1207460da35fSthorpej } 1208460da35fSthorpej 120963eac52bSthorpej static int 1210460da35fSthorpej bridge_ioctl_gto(struct bridge_softc *sc, void *arg) 1211460da35fSthorpej { 1212460da35fSthorpej struct ifbrparam *param = arg; 1213460da35fSthorpej 1214460da35fSthorpej param->ifbrp_ctime = sc->sc_brttimeout; 1215460da35fSthorpej 1216a40cbde0Smsaitoh return 0; 1217460da35fSthorpej } 1218460da35fSthorpej 121963eac52bSthorpej static int 1220460da35fSthorpej bridge_ioctl_daddr(struct bridge_softc *sc, void *arg) 1221460da35fSthorpej { 1222460da35fSthorpej struct ifbareq *req = arg; 1223460da35fSthorpej 1224460da35fSthorpej return (bridge_rtdaddr(sc, req->ifba_dst)); 1225460da35fSthorpej } 1226460da35fSthorpej 122763eac52bSthorpej static int 1228460da35fSthorpej bridge_ioctl_flush(struct bridge_softc *sc, void *arg) 1229460da35fSthorpej { 1230460da35fSthorpej struct ifbreq *req = arg; 1231460da35fSthorpej 1232460da35fSthorpej bridge_rtflush(sc, req->ifbr_ifsflags); 1233460da35fSthorpej 1234a40cbde0Smsaitoh return 0; 1235460da35fSthorpej } 1236460da35fSthorpej 123763eac52bSthorpej static int 1238460da35fSthorpej bridge_ioctl_gpri(struct bridge_softc *sc, void *arg) 1239460da35fSthorpej { 1240460da35fSthorpej struct ifbrparam *param = arg; 1241460da35fSthorpej 1242460da35fSthorpej param->ifbrp_prio = sc->sc_bridge_priority; 1243460da35fSthorpej 1244a40cbde0Smsaitoh return 0; 1245460da35fSthorpej } 1246460da35fSthorpej 124763eac52bSthorpej static int 1248460da35fSthorpej bridge_ioctl_spri(struct bridge_softc *sc, void *arg) 1249460da35fSthorpej { 1250460da35fSthorpej struct ifbrparam *param = arg; 1251460da35fSthorpej 1252460da35fSthorpej sc->sc_bridge_priority = param->ifbrp_prio; 1253460da35fSthorpej 1254460da35fSthorpej if (sc->sc_if.if_flags & IFF_RUNNING) 1255460da35fSthorpej bstp_initialization(sc); 1256460da35fSthorpej 1257a40cbde0Smsaitoh return 0; 1258460da35fSthorpej } 1259460da35fSthorpej 126063eac52bSthorpej static int 1261460da35fSthorpej bridge_ioctl_ght(struct bridge_softc *sc, void *arg) 1262460da35fSthorpej { 1263460da35fSthorpej struct ifbrparam *param = arg; 1264460da35fSthorpej 1265460da35fSthorpej param->ifbrp_hellotime = sc->sc_bridge_hello_time >> 8; 1266460da35fSthorpej 1267a40cbde0Smsaitoh return 0; 1268460da35fSthorpej } 1269460da35fSthorpej 127063eac52bSthorpej static int 1271460da35fSthorpej bridge_ioctl_sht(struct bridge_softc *sc, void *arg) 1272460da35fSthorpej { 1273460da35fSthorpej struct ifbrparam *param = arg; 1274460da35fSthorpej 1275460da35fSthorpej if (param->ifbrp_hellotime == 0) 1276a40cbde0Smsaitoh return EINVAL; 1277460da35fSthorpej sc->sc_bridge_hello_time = param->ifbrp_hellotime << 8; 1278460da35fSthorpej 1279460da35fSthorpej if (sc->sc_if.if_flags & IFF_RUNNING) 1280460da35fSthorpej bstp_initialization(sc); 1281460da35fSthorpej 1282a40cbde0Smsaitoh return 0; 1283460da35fSthorpej } 1284460da35fSthorpej 128563eac52bSthorpej static int 1286460da35fSthorpej bridge_ioctl_gfd(struct bridge_softc *sc, void *arg) 1287460da35fSthorpej { 1288460da35fSthorpej struct ifbrparam *param = arg; 1289460da35fSthorpej 1290460da35fSthorpej param->ifbrp_fwddelay = sc->sc_bridge_forward_delay >> 8; 1291460da35fSthorpej 1292a40cbde0Smsaitoh return 0; 1293460da35fSthorpej } 1294460da35fSthorpej 129563eac52bSthorpej static int 1296460da35fSthorpej bridge_ioctl_sfd(struct bridge_softc *sc, void *arg) 1297460da35fSthorpej { 1298460da35fSthorpej struct ifbrparam *param = arg; 1299460da35fSthorpej 1300460da35fSthorpej if (param->ifbrp_fwddelay == 0) 1301a40cbde0Smsaitoh return EINVAL; 1302460da35fSthorpej sc->sc_bridge_forward_delay = param->ifbrp_fwddelay << 8; 1303460da35fSthorpej 1304460da35fSthorpej if (sc->sc_if.if_flags & IFF_RUNNING) 1305460da35fSthorpej bstp_initialization(sc); 1306460da35fSthorpej 1307a40cbde0Smsaitoh return 0; 1308460da35fSthorpej } 1309460da35fSthorpej 131063eac52bSthorpej static int 1311460da35fSthorpej bridge_ioctl_gma(struct bridge_softc *sc, void *arg) 1312460da35fSthorpej { 1313460da35fSthorpej struct ifbrparam *param = arg; 1314460da35fSthorpej 1315460da35fSthorpej param->ifbrp_maxage = sc->sc_bridge_max_age >> 8; 1316460da35fSthorpej 1317a40cbde0Smsaitoh return 0; 1318460da35fSthorpej } 1319460da35fSthorpej 132063eac52bSthorpej static int 1321460da35fSthorpej bridge_ioctl_sma(struct bridge_softc *sc, void *arg) 1322460da35fSthorpej { 1323460da35fSthorpej struct ifbrparam *param = arg; 1324460da35fSthorpej 1325460da35fSthorpej if (param->ifbrp_maxage == 0) 1326a40cbde0Smsaitoh return EINVAL; 1327460da35fSthorpej sc->sc_bridge_max_age = param->ifbrp_maxage << 8; 1328460da35fSthorpej 1329460da35fSthorpej if (sc->sc_if.if_flags & IFF_RUNNING) 1330460da35fSthorpej bstp_initialization(sc); 1331460da35fSthorpej 1332a40cbde0Smsaitoh return 0; 1333460da35fSthorpej } 1334460da35fSthorpej 133563eac52bSthorpej static int 1336460da35fSthorpej bridge_ioctl_sifprio(struct bridge_softc *sc, void *arg) 1337460da35fSthorpej { 1338460da35fSthorpej struct ifbreq *req = arg; 1339460da35fSthorpej struct bridge_iflist *bif; 1340cc3dd2e0Sozaki-r struct psref psref; 1341460da35fSthorpej 1342cc3dd2e0Sozaki-r bif = bridge_lookup_member(sc, req->ifbr_ifsname, &psref); 1343460da35fSthorpej if (bif == NULL) 1344a40cbde0Smsaitoh return ENOENT; 1345460da35fSthorpej 1346460da35fSthorpej bif->bif_priority = req->ifbr_priority; 1347460da35fSthorpej 1348460da35fSthorpej if (sc->sc_if.if_flags & IFF_RUNNING) 1349460da35fSthorpej bstp_initialization(sc); 1350460da35fSthorpej 1351cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 13529161a49eSozaki-r 1353a40cbde0Smsaitoh return 0; 1354460da35fSthorpej } 1355460da35fSthorpej 135663eac52bSthorpej static int 13576b857c22Sperseant bridge_ioctl_gfilt(struct bridge_softc *sc, void *arg) 13586b857c22Sperseant { 13596b857c22Sperseant struct ifbrparam *param = arg; 13606b857c22Sperseant 13616b857c22Sperseant param->ifbrp_filter = sc->sc_filter_flags; 13626b857c22Sperseant 1363a40cbde0Smsaitoh return 0; 13646b857c22Sperseant } 13656b857c22Sperseant 136663eac52bSthorpej static int 13676b857c22Sperseant bridge_ioctl_sfilt(struct bridge_softc *sc, void *arg) 13686b857c22Sperseant { 13696b857c22Sperseant struct ifbrparam *param = arg; 13706b857c22Sperseant uint32_t nflags, oflags; 13716b857c22Sperseant 13726b857c22Sperseant if (param->ifbrp_filter & ~IFBF_FILT_MASK) 1373a40cbde0Smsaitoh return EINVAL; 13746b857c22Sperseant 13756b857c22Sperseant nflags = param->ifbrp_filter; 13766b857c22Sperseant oflags = sc->sc_filter_flags; 13776b857c22Sperseant 13786b857c22Sperseant if ((nflags & IFBF_FILT_USEIPF) && !(oflags & IFBF_FILT_USEIPF)) { 13796b857c22Sperseant pfil_add_hook((void *)bridge_ipf, NULL, PFIL_IN|PFIL_OUT, 1380f04a92b1Srmind sc->sc_if.if_pfil); 13816b857c22Sperseant } 13826b857c22Sperseant if (!(nflags & IFBF_FILT_USEIPF) && (oflags & IFBF_FILT_USEIPF)) { 13836b857c22Sperseant pfil_remove_hook((void *)bridge_ipf, NULL, PFIL_IN|PFIL_OUT, 1384f04a92b1Srmind sc->sc_if.if_pfil); 13856b857c22Sperseant } 13866b857c22Sperseant 13876b857c22Sperseant sc->sc_filter_flags = nflags; 13886b857c22Sperseant 1389a40cbde0Smsaitoh return 0; 13906b857c22Sperseant } 13916b857c22Sperseant 139263eac52bSthorpej static int 13934a3c894eSbouyer bridge_ioctl_sifcost(struct bridge_softc *sc, void *arg) 13944a3c894eSbouyer { 13954a3c894eSbouyer struct ifbreq *req = arg; 13964a3c894eSbouyer struct bridge_iflist *bif; 1397cc3dd2e0Sozaki-r struct psref psref; 13984a3c894eSbouyer 1399cc3dd2e0Sozaki-r bif = bridge_lookup_member(sc, req->ifbr_ifsname, &psref); 14004a3c894eSbouyer if (bif == NULL) 1401a40cbde0Smsaitoh return ENOENT; 14024a3c894eSbouyer 14034a3c894eSbouyer bif->bif_path_cost = req->ifbr_path_cost; 14044a3c894eSbouyer 14054a3c894eSbouyer if (sc->sc_if.if_flags & IFF_RUNNING) 14064a3c894eSbouyer bstp_initialization(sc); 14074a3c894eSbouyer 1408cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 14099161a49eSozaki-r 1410a40cbde0Smsaitoh return 0; 14114a3c894eSbouyer } 14124a3c894eSbouyer 1413460da35fSthorpej /* 1414460da35fSthorpej * bridge_ifdetach: 1415460da35fSthorpej * 1416460da35fSthorpej * Detach an interface from a bridge. Called when a member 1417460da35fSthorpej * interface is detaching. 1418460da35fSthorpej */ 1419c06d6dc9Syamaguchi static void 1420c06d6dc9Syamaguchi bridge_ifdetach(void *xifs) 1421460da35fSthorpej { 1422c06d6dc9Syamaguchi struct ifnet *ifs; 1423c06d6dc9Syamaguchi struct bridge_softc *sc; 1424460da35fSthorpej struct ifbreq breq; 1425460da35fSthorpej 1426c06d6dc9Syamaguchi ifs = (struct ifnet *)xifs; 1427c06d6dc9Syamaguchi sc = ifs->if_bridge; 1428c06d6dc9Syamaguchi 14299161a49eSozaki-r /* ioctl_lock should prevent this from happening */ 14309161a49eSozaki-r KASSERT(sc != NULL); 14319161a49eSozaki-r 1432460da35fSthorpej memset(&breq, 0, sizeof(breq)); 1433c06d6dc9Syamaguchi strlcpy(breq.ifbr_ifsname, ifs->if_xname, sizeof(breq.ifbr_ifsname)); 1434460da35fSthorpej 1435460da35fSthorpej (void) bridge_ioctl_del(sc, &breq); 1436460da35fSthorpej } 1437460da35fSthorpej 1438460da35fSthorpej /* 1439460da35fSthorpej * bridge_init: 1440460da35fSthorpej * 1441460da35fSthorpej * Initialize a bridge interface. 1442460da35fSthorpej */ 144363eac52bSthorpej static int 1444460da35fSthorpej bridge_init(struct ifnet *ifp) 1445460da35fSthorpej { 1446460da35fSthorpej struct bridge_softc *sc = ifp->if_softc; 1447460da35fSthorpej 14482fe451f2Sozaki-r KASSERT((ifp->if_flags & IFF_RUNNING) == 0); 1449460da35fSthorpej 1450460da35fSthorpej callout_reset(&sc->sc_brcallout, bridge_rtable_prune_period * hz, 1451460da35fSthorpej bridge_timer, sc); 14522fe451f2Sozaki-r bstp_initialization(sc); 1453460da35fSthorpej 1454460da35fSthorpej ifp->if_flags |= IFF_RUNNING; 1455a40cbde0Smsaitoh return 0; 1456460da35fSthorpej } 1457460da35fSthorpej 1458460da35fSthorpej /* 1459460da35fSthorpej * bridge_stop: 1460460da35fSthorpej * 1461460da35fSthorpej * Stop the bridge interface. 1462460da35fSthorpej */ 146363eac52bSthorpej static void 1464168cd830Schristos bridge_stop(struct ifnet *ifp, int disable) 1465460da35fSthorpej { 1466460da35fSthorpej struct bridge_softc *sc = ifp->if_softc; 1467460da35fSthorpej 14682fe451f2Sozaki-r KASSERT((ifp->if_flags & IFF_RUNNING) != 0); 14692fe451f2Sozaki-r ifp->if_flags &= ~IFF_RUNNING; 1470460da35fSthorpej 1471ffdebce9Sozaki-r callout_halt(&sc->sc_brcallout, NULL); 1472ffdebce9Sozaki-r workqueue_wait(sc->sc_rtage_wq, &sc->sc_rtage_wk); 1473460da35fSthorpej bstp_stop(sc); 1474460da35fSthorpej bridge_rtflush(sc, IFBF_FLUSHDYN); 1475460da35fSthorpej } 1476460da35fSthorpej 1477460da35fSthorpej /* 1478460da35fSthorpej * bridge_enqueue: 1479460da35fSthorpej * 1480460da35fSthorpej * Enqueue a packet on a bridge member interface. 1481460da35fSthorpej */ 148263eac52bSthorpej void 148332263965Sjdc bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m, 148432263965Sjdc int runfilt) 1485460da35fSthorpej { 1486460da35fSthorpej int len, error; 1487460da35fSthorpej short mflags; 1488460da35fSthorpej 148932263965Sjdc if (runfilt) { 1490f04a92b1Srmind if (pfil_run_hooks(sc->sc_if.if_pfil, &m, 149199807188Sjdc dst_ifp, PFIL_OUT) != 0) { 14926b857c22Sperseant m_freem(m); 14936b857c22Sperseant return; 14946b857c22Sperseant } 149599807188Sjdc if (m == NULL) 149699807188Sjdc return; 149732263965Sjdc } 14986b857c22Sperseant 1499460da35fSthorpej #ifdef ALTQ 150016fd6057Sknakahara KERNEL_LOCK(1, NULL); 1501460da35fSthorpej /* 1502460da35fSthorpej * If ALTQ is enabled on the member interface, do 1503460da35fSthorpej * classification; the queueing discipline might 1504460da35fSthorpej * not require classification, but might require 1505460da35fSthorpej * the address family/header pointer in the pktattr. 1506460da35fSthorpej */ 1507460da35fSthorpej if (ALTQ_IS_ENABLED(&dst_ifp->if_snd)) { 1508460da35fSthorpej /* XXX IFT_ETHER */ 150940b1061cSknakahara altq_etherclassify(&dst_ifp->if_snd, m); 1510460da35fSthorpej } 151116fd6057Sknakahara KERNEL_UNLOCK_ONE(NULL); 1512460da35fSthorpej #endif /* ALTQ */ 1513460da35fSthorpej 1514f17fac57Syamaguchi if (vlan_has_tag(m) && 1515f17fac57Syamaguchi !vlan_is_hwtag_enabled(dst_ifp)) { 1516f17fac57Syamaguchi (void)ether_inject_vlantag(&m, ETHERTYPE_VLAN, 1517f17fac57Syamaguchi vlan_get_tag(m)); 1518f17fac57Syamaguchi if (m == NULL) { 1519f17fac57Syamaguchi if_statinc(&sc->sc_if, if_oerrors); 1520f17fac57Syamaguchi return; 1521f17fac57Syamaguchi } 1522f17fac57Syamaguchi } 1523f17fac57Syamaguchi 1524460da35fSthorpej len = m->m_pkthdr.len; 1525460da35fSthorpej mflags = m->m_flags; 15269161a49eSozaki-r 1527e41ad56bSknakahara error = if_transmit_lock(dst_ifp, m); 1528460da35fSthorpej if (error) { 1529460da35fSthorpej /* mbuf is already freed */ 153070b554e6Sthorpej if_statinc(&sc->sc_if, if_oerrors); 1531460da35fSthorpej return; 1532460da35fSthorpej } 1533460da35fSthorpej 153470b554e6Sthorpej net_stat_ref_t nsr = IF_STAT_GETREF(&sc->sc_if); 1535be6f2fceSriastradh if_statinc_ref(&sc->sc_if, nsr, if_opackets); 1536be6f2fceSriastradh if_statadd_ref(&sc->sc_if, nsr, if_obytes, len); 15379f3a294eSknakahara if (mflags & M_MCAST) 1538be6f2fceSriastradh if_statinc_ref(&sc->sc_if, nsr, if_omcasts); 153970b554e6Sthorpej IF_STAT_PUTREF(&sc->sc_if); 1540460da35fSthorpej } 1541460da35fSthorpej 1542460da35fSthorpej /* 1543460da35fSthorpej * bridge_output: 1544460da35fSthorpej * 1545460da35fSthorpej * Send output from a bridge member interface. This 1546460da35fSthorpej * performs the bridging function for locally originated 1547460da35fSthorpej * packets. 1548460da35fSthorpej * 1549460da35fSthorpej * The mbuf has the Ethernet header already attached. We must 1550460da35fSthorpej * enqueue or free the mbuf before returning. 1551460da35fSthorpej */ 1552460da35fSthorpej int 15535493f188Sdyoung bridge_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa, 15542cf7873bSozaki-r const struct rtentry *rt) 1555460da35fSthorpej { 1556460da35fSthorpej struct ether_header *eh; 1557460da35fSthorpej struct ifnet *dst_if; 1558460da35fSthorpej struct bridge_softc *sc; 155973240bb1Srin struct mbuf *n; 1560f2ee615fSozaki-r int s, bound; 1561460da35fSthorpej 1562ded2d2ffSknakahara /* 1563ded2d2ffSknakahara * bridge_output() is called from ether_output(), furthermore 1564ded2d2ffSknakahara * ifp argument doesn't point to bridge(4). So, don't assert 1565ab3cd725Sozaki-r * IFEF_MPSAFE here. 1566ded2d2ffSknakahara */ 1567ded2d2ffSknakahara 15681a475b3cSjdolecek KASSERT(m->m_len >= ETHER_HDR_LEN); 1569460da35fSthorpej 1570460da35fSthorpej eh = mtod(m, struct ether_header *); 1571460da35fSthorpej sc = ifp->if_bridge; 1572460da35fSthorpej 157388be616fSroy if (ETHER_IS_MULTICAST(eh->ether_dhost)) { 157488be616fSroy if (memcmp(etherbroadcastaddr, 157588be616fSroy eh->ether_dhost, ETHER_ADDR_LEN) == 0) 157688be616fSroy m->m_flags |= M_BCAST; 157788be616fSroy else 157888be616fSroy m->m_flags |= M_MCAST; 157988be616fSroy } 158088be616fSroy 1581460da35fSthorpej /* 1582460da35fSthorpej * If bridge is down, but the original output interface is up, 1583460da35fSthorpej * go ahead and send out that interface. Otherwise, the packet 1584460da35fSthorpej * is dropped below. 1585460da35fSthorpej */ 15869161a49eSozaki-r if (__predict_false(sc == NULL) || 15879161a49eSozaki-r (sc->sc_if.if_flags & IFF_RUNNING) == 0) { 1588460da35fSthorpej dst_if = ifp; 158973240bb1Srin goto unicast_asis; 1590460da35fSthorpej } 1591460da35fSthorpej 1592460da35fSthorpej /* 1593460da35fSthorpej * If the packet is a multicast, or we don't know a better way to 1594460da35fSthorpej * get there, send to all interfaces. 1595460da35fSthorpej */ 159688be616fSroy if ((m->m_flags & (M_MCAST | M_BCAST)) != 0) 1597460da35fSthorpej dst_if = NULL; 1598460da35fSthorpej else 1599460da35fSthorpej dst_if = bridge_rtlookup(sc, eh->ether_dhost); 160073240bb1Srin 160173240bb1Srin /* 160273240bb1Srin * In general, we need to handle TX offload in software before 160373240bb1Srin * enqueueing a packet. However, we can send it as is in the 160473240bb1Srin * cases of unicast via (1) the source interface, or (2) an 160573240bb1Srin * interface which supports the specified offload options. 160673240bb1Srin * For multicast or broadcast, send it as is only if (3) all 160773240bb1Srin * the member interfaces support the specified options. 160873240bb1Srin */ 160973240bb1Srin 161073240bb1Srin /* 161173240bb1Srin * Unicast via the source interface. 161273240bb1Srin */ 161373240bb1Srin if (dst_if == ifp) 161473240bb1Srin goto unicast_asis; 161573240bb1Srin 161673240bb1Srin /* 161773240bb1Srin * Unicast via other interface. 161873240bb1Srin */ 161973240bb1Srin if (dst_if != NULL) { 162073240bb1Srin KASSERT(m->m_flags & M_PKTHDR); 162173240bb1Srin if (TX_OFFLOAD_SUPPORTED(dst_if->if_csum_flags_tx, 162273240bb1Srin m->m_pkthdr.csum_flags)) { 162373240bb1Srin /* 162473240bb1Srin * Unicast via an interface which supports the 162573240bb1Srin * specified offload options. 162673240bb1Srin */ 162773240bb1Srin goto unicast_asis; 162873240bb1Srin } 162973240bb1Srin 163073240bb1Srin /* 163173240bb1Srin * Handle TX offload in software. For TSO, a packet is 163273240bb1Srin * split into multiple chunks. Thus, the return value of 1633b4a16023Srin * ether_sw_offload_tx() is mbuf queue consists of them. 163473240bb1Srin */ 163573240bb1Srin m = ether_sw_offload_tx(ifp, m); 163673240bb1Srin if (m == NULL) 163773240bb1Srin return 0; 163873240bb1Srin 163973240bb1Srin do { 164073240bb1Srin n = m->m_nextpkt; 164173240bb1Srin if ((dst_if->if_flags & IFF_RUNNING) == 0) 164273240bb1Srin m_freem(m); 164373240bb1Srin else 164473240bb1Srin bridge_enqueue(sc, dst_if, m, 0); 164573240bb1Srin m = n; 164673240bb1Srin } while (m != NULL); 164773240bb1Srin 164873240bb1Srin return 0; 164973240bb1Srin } 165073240bb1Srin 165173240bb1Srin /* 165273240bb1Srin * Multicast or broadcast. 165373240bb1Srin */ 165473240bb1Srin if (TX_OFFLOAD_SUPPORTED(sc->sc_csum_flags_tx, 165573240bb1Srin m->m_pkthdr.csum_flags)) { 165673240bb1Srin /* 165773240bb1Srin * Specified TX offload options are supported by all 165873240bb1Srin * the member interfaces of this bridge. 165973240bb1Srin */ 166073240bb1Srin m->m_nextpkt = NULL; /* XXX */ 166173240bb1Srin } else { 166273240bb1Srin /* 166373240bb1Srin * Otherwise, handle TX offload in software. 166473240bb1Srin */ 166573240bb1Srin m = ether_sw_offload_tx(ifp, m); 166673240bb1Srin if (m == NULL) 166773240bb1Srin return 0; 166873240bb1Srin } 166973240bb1Srin 1670f2ee615fSozaki-r /* 1671f2ee615fSozaki-r * When we use pppoe over bridge, bridge_output() can be called 1672f2ee615fSozaki-r * in a lwp context by pppoe_timeout_wk(). 1673f2ee615fSozaki-r */ 1674f2ee615fSozaki-r bound = curlwp_bind(); 167573240bb1Srin do { 167688be616fSroy /* XXX Should call bridge_broadcast, but there are locking 167788be616fSroy * issues which need resolving first. */ 1678460da35fSthorpej struct bridge_iflist *bif; 1679460da35fSthorpej struct mbuf *mc; 1680aabc63fcSroy bool used = false; 1681460da35fSthorpej 168273240bb1Srin n = m->m_nextpkt; 168373240bb1Srin 16842977110aSozaki-r s = pserialize_read_enter(); 1685cc3dd2e0Sozaki-r BRIDGE_IFLIST_READER_FOREACH(bif, sc) { 1686cc3dd2e0Sozaki-r struct psref psref; 1687cc3dd2e0Sozaki-r 1688cc3dd2e0Sozaki-r bridge_acquire_member(sc, bif, &psref); 16892977110aSozaki-r pserialize_read_exit(s); 1690cba69a87Sozaki-r 1691460da35fSthorpej dst_if = bif->bif_ifp; 1692460da35fSthorpej if ((dst_if->if_flags & IFF_RUNNING) == 0) 1693cba69a87Sozaki-r goto next; 1694460da35fSthorpej 1695460da35fSthorpej /* 1696460da35fSthorpej * If this is not the original output interface, 1697460da35fSthorpej * and the interface is participating in spanning 1698460da35fSthorpej * tree, make sure the port is in a state that 1699460da35fSthorpej * allows forwarding. 1700460da35fSthorpej */ 1701460da35fSthorpej if (dst_if != ifp && 1702460da35fSthorpej (bif->bif_flags & IFBIF_STP) != 0) { 1703460da35fSthorpej switch (bif->bif_state) { 1704460da35fSthorpej case BSTP_IFSTATE_BLOCKING: 1705460da35fSthorpej case BSTP_IFSTATE_LISTENING: 1706460da35fSthorpej case BSTP_IFSTATE_DISABLED: 1707cba69a87Sozaki-r goto next; 1708460da35fSthorpej } 1709460da35fSthorpej } 1710460da35fSthorpej 1711814cd05cSozaki-r if (PSLIST_READER_NEXT(bif, struct bridge_iflist, 171288be616fSroy bif_next) == NULL && 171388be616fSroy ((m->m_flags & (M_MCAST | M_BCAST)) == 0 || 171488be616fSroy dst_if == ifp)) 171588be616fSroy { 1716aabc63fcSroy used = true; 1717460da35fSthorpej mc = m; 1718460da35fSthorpej } else { 17198553f24dSmsaitoh mc = m_copypacket(m, M_DONTWAIT); 1720460da35fSthorpej if (mc == NULL) { 172170b554e6Sthorpej if_statinc(&sc->sc_if, if_oerrors); 1722cba69a87Sozaki-r goto next; 1723460da35fSthorpej } 1724460da35fSthorpej } 1725460da35fSthorpej 172632263965Sjdc bridge_enqueue(sc, dst_if, mc, 0); 172788be616fSroy 172888be616fSroy if ((m->m_flags & (M_MCAST | M_BCAST)) != 0 && 172988be616fSroy dst_if != ifp) 173088be616fSroy { 173188be616fSroy if (PSLIST_READER_NEXT(bif, 173288be616fSroy struct bridge_iflist, bif_next) == NULL) 173388be616fSroy { 173488be616fSroy used = true; 173588be616fSroy mc = m; 173688be616fSroy } else { 17378553f24dSmsaitoh mc = m_copypacket(m, M_DONTWAIT); 173888be616fSroy if (mc == NULL) { 173970b554e6Sthorpej if_statinc(&sc->sc_if, 174070b554e6Sthorpej if_oerrors); 174188be616fSroy goto next; 174288be616fSroy } 174388be616fSroy } 174488be616fSroy 1745d938d837Sozaki-r m_set_rcvif(mc, dst_if); 174688be616fSroy mc->m_flags &= ~M_PROMISC; 174788be616fSroy 1748c5e9ae90Sskrll const int _s = splsoftnet(); 17494f93a8deSozaki-r KERNEL_LOCK_UNLESS_IFP_MPSAFE(dst_if); 175088be616fSroy ether_input(dst_if, mc); 17514f93a8deSozaki-r KERNEL_UNLOCK_UNLESS_IFP_MPSAFE(dst_if); 1752c5e9ae90Sskrll splx(_s); 175388be616fSroy } 175488be616fSroy 1755cba69a87Sozaki-r next: 17562977110aSozaki-r s = pserialize_read_enter(); 1757cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 1758aabc63fcSroy 1759aabc63fcSroy /* Guarantee we don't re-enter the loop as we already 1760aabc63fcSroy * decided we're at the end. */ 1761aabc63fcSroy if (used) 1762aabc63fcSroy break; 1763460da35fSthorpej } 17642977110aSozaki-r pserialize_read_exit(s); 17659161a49eSozaki-r 1766aabc63fcSroy if (!used) 1767460da35fSthorpej m_freem(m); 1768460da35fSthorpej 176973240bb1Srin m = n; 177073240bb1Srin } while (m != NULL); 1771f2ee615fSozaki-r curlwp_bindx(bound); 1772f2ee615fSozaki-r 177373240bb1Srin return 0; 177473240bb1Srin 177573240bb1Srin unicast_asis: 1776460da35fSthorpej /* 1777460da35fSthorpej * XXX Spanning tree consideration here? 1778460da35fSthorpej */ 177973240bb1Srin if ((dst_if->if_flags & IFF_RUNNING) == 0) 1780460da35fSthorpej m_freem(m); 178173240bb1Srin else 178232263965Sjdc bridge_enqueue(sc, dst_if, m, 0); 1783a40cbde0Smsaitoh return 0; 1784460da35fSthorpej } 1785460da35fSthorpej 1786460da35fSthorpej /* 1787460da35fSthorpej * bridge_start: 1788460da35fSthorpej * 1789460da35fSthorpej * Start output on a bridge. 1790460da35fSthorpej * 1791460da35fSthorpej * NOTE: This routine should never be called in this implementation. 1792460da35fSthorpej */ 179363eac52bSthorpej static void 1794460da35fSthorpej bridge_start(struct ifnet *ifp) 1795460da35fSthorpej { 1796460da35fSthorpej 1797460da35fSthorpej printf("%s: bridge_start() called\n", ifp->if_xname); 1798460da35fSthorpej } 1799460da35fSthorpej 1800460da35fSthorpej /* 1801460da35fSthorpej * bridge_forward: 1802460da35fSthorpej * 1803e58307e4Saugustss * The forwarding function of the bridge. 1804460da35fSthorpej */ 180563eac52bSthorpej static void 1806b7a310caSozaki-r bridge_forward(struct bridge_softc *sc, struct mbuf *m) 1807460da35fSthorpej { 1808460da35fSthorpej struct bridge_iflist *bif; 1809460da35fSthorpej struct ifnet *src_if, *dst_if; 1810460da35fSthorpej struct ether_header *eh; 1811cc3dd2e0Sozaki-r struct psref psref; 1812fe6d4275Sozaki-r struct psref psref_src; 1813d81e97faSozaki-r DECLARE_LOCK_VARIABLE; 1814008f982cSozaki-r bool src_if_protected; 18151fd1b496Sbouyer 1816fe6d4275Sozaki-r src_if = m_get_rcvif_psref(m, &psref_src); 1817fe6d4275Sozaki-r if (src_if == NULL) { 1818fe6d4275Sozaki-r /* Interface is being destroyed? */ 1819*c9e0a13bSozaki-r goto discard; 1820fe6d4275Sozaki-r } 1821460da35fSthorpej 182270b554e6Sthorpej if_statadd2(&sc->sc_if, if_ipackets, 1, if_ibytes, m->m_pkthdr.len); 1823460da35fSthorpej 1824460da35fSthorpej /* 1825460da35fSthorpej * Look up the bridge_iflist. 1826460da35fSthorpej */ 1827cc3dd2e0Sozaki-r bif = bridge_lookup_member_if(sc, src_if, &psref); 1828460da35fSthorpej if (bif == NULL) { 1829460da35fSthorpej /* Interface is not a bridge member (anymore?) */ 1830*c9e0a13bSozaki-r goto discard; 1831460da35fSthorpej } 1832460da35fSthorpej 1833460da35fSthorpej if (bif->bif_flags & IFBIF_STP) { 1834460da35fSthorpej switch (bif->bif_state) { 1835460da35fSthorpej case BSTP_IFSTATE_BLOCKING: 1836460da35fSthorpej case BSTP_IFSTATE_LISTENING: 1837460da35fSthorpej case BSTP_IFSTATE_DISABLED: 1838cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 1839*c9e0a13bSozaki-r goto discard; 1840460da35fSthorpej } 1841460da35fSthorpej } 1842460da35fSthorpej 1843460da35fSthorpej eh = mtod(m, struct ether_header *); 1844460da35fSthorpej 1845460da35fSthorpej /* 1846460da35fSthorpej * If the interface is learning, and the source 1847460da35fSthorpej * address is valid and not multicast, record 1848460da35fSthorpej * the address. 1849460da35fSthorpej */ 1850460da35fSthorpej if ((bif->bif_flags & IFBIF_LEARNING) != 0 && 1851460da35fSthorpej ETHER_IS_MULTICAST(eh->ether_shost) == 0 && 1852460da35fSthorpej (eh->ether_shost[0] == 0 && 1853460da35fSthorpej eh->ether_shost[1] == 0 && 1854460da35fSthorpej eh->ether_shost[2] == 0 && 1855460da35fSthorpej eh->ether_shost[3] == 0 && 1856460da35fSthorpej eh->ether_shost[4] == 0 && 1857460da35fSthorpej eh->ether_shost[5] == 0) == 0) { 1858460da35fSthorpej (void) bridge_rtupdate(sc, eh->ether_shost, 1859460da35fSthorpej src_if, 0, IFBAF_DYNAMIC); 1860460da35fSthorpej } 1861460da35fSthorpej 1862460da35fSthorpej if ((bif->bif_flags & IFBIF_STP) != 0 && 1863460da35fSthorpej bif->bif_state == BSTP_IFSTATE_LEARNING) { 1864cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 1865*c9e0a13bSozaki-r goto discard; 1866460da35fSthorpej } 1867460da35fSthorpej 1868008f982cSozaki-r src_if_protected = ((bif->bif_flags & IFBIF_PROTECTED) != 0); 1869008f982cSozaki-r 1870cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 18719161a49eSozaki-r 1872460da35fSthorpej /* 1873460da35fSthorpej * At this point, the port either doesn't participate 1874460da35fSthorpej * in spanning tree or it is in the forwarding state. 1875460da35fSthorpej */ 1876460da35fSthorpej 1877460da35fSthorpej /* 1878460da35fSthorpej * If the packet is unicast, destined for someone on 1879460da35fSthorpej * "this" side of the bridge, drop it. 1880460da35fSthorpej */ 1881460da35fSthorpej if ((m->m_flags & (M_BCAST|M_MCAST)) == 0) { 1882460da35fSthorpej dst_if = bridge_rtlookup(sc, eh->ether_dhost); 1883*c9e0a13bSozaki-r if (src_if == dst_if) 1884*c9e0a13bSozaki-r goto discard; 1885460da35fSthorpej } else { 1886460da35fSthorpej /* ...forward it to all interfaces. */ 188770b554e6Sthorpej if_statinc(&sc->sc_if, if_imcasts); 1888460da35fSthorpej dst_if = NULL; 1889460da35fSthorpej } 1890460da35fSthorpej 1891*c9e0a13bSozaki-r if (pfil_run_hooks(sc->sc_if.if_pfil, &m, src_if, PFIL_IN) != 0 || 1892*c9e0a13bSozaki-r m == NULL) { 1893*c9e0a13bSozaki-r goto discard; 18946b857c22Sperseant } 18956b857c22Sperseant 1896460da35fSthorpej if (dst_if == NULL) { 1897008f982cSozaki-r bridge_broadcast(sc, src_if, src_if_protected, m); 1898b7a310caSozaki-r goto out; 1899460da35fSthorpej } 1900460da35fSthorpej 1901fe6d4275Sozaki-r m_put_rcvif_psref(src_if, &psref_src); 1902fe6d4275Sozaki-r src_if = NULL; 1903fe6d4275Sozaki-r 1904460da35fSthorpej /* 1905460da35fSthorpej * At this point, we're dealing with a unicast frame 1906460da35fSthorpej * going to a different interface. 1907460da35fSthorpej */ 1908*c9e0a13bSozaki-r if ((dst_if->if_flags & IFF_RUNNING) == 0) 1909*c9e0a13bSozaki-r goto discard; 19109161a49eSozaki-r 1911cc3dd2e0Sozaki-r bif = bridge_lookup_member_if(sc, dst_if, &psref); 1912460da35fSthorpej if (bif == NULL) { 1913460da35fSthorpej /* Not a member of the bridge (anymore?) */ 1914*c9e0a13bSozaki-r goto discard; 1915460da35fSthorpej } 1916460da35fSthorpej 1917460da35fSthorpej if (bif->bif_flags & IFBIF_STP) { 1918460da35fSthorpej switch (bif->bif_state) { 1919460da35fSthorpej case BSTP_IFSTATE_DISABLED: 1920460da35fSthorpej case BSTP_IFSTATE_BLOCKING: 1921cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 1922*c9e0a13bSozaki-r goto discard; 1923460da35fSthorpej } 1924460da35fSthorpej } 1925460da35fSthorpej 1926008f982cSozaki-r if ((bif->bif_flags & IFBIF_PROTECTED) && src_if_protected) { 1927008f982cSozaki-r bridge_release_member(sc, bif, &psref); 1928*c9e0a13bSozaki-r goto discard; 1929008f982cSozaki-r } 1930008f982cSozaki-r 1931cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 19329161a49eSozaki-r 193322e37dedSmsaitoh /* 1934ad29c409Smsaitoh * Before enqueueing this packet to the destination interface, 1935ad29c409Smsaitoh * clear any in-bound checksum flags to prevent them from being 1936ad29c409Smsaitoh * misused as out-bound flags. 193722e37dedSmsaitoh */ 193822e37dedSmsaitoh m->m_pkthdr.csum_flags = 0; 193922e37dedSmsaitoh 1940d81e97faSozaki-r ACQUIRE_GLOBAL_LOCKS(); 194132263965Sjdc bridge_enqueue(sc, dst_if, m, 1); 1942d81e97faSozaki-r RELEASE_GLOBAL_LOCKS(); 1943b7a310caSozaki-r out: 1944fe6d4275Sozaki-r if (src_if != NULL) 1945fe6d4275Sozaki-r m_put_rcvif_psref(src_if, &psref_src); 1946b7a310caSozaki-r return; 1947*c9e0a13bSozaki-r 1948*c9e0a13bSozaki-r discard: 1949*c9e0a13bSozaki-r m_freem(m); 1950*c9e0a13bSozaki-r goto out; 19511fd1b496Sbouyer } 1952460da35fSthorpej 19538f92986dSozaki-r static bool 19548f92986dSozaki-r bstp_state_before_learning(struct bridge_iflist *bif) 19558f92986dSozaki-r { 19568f92986dSozaki-r if (bif->bif_flags & IFBIF_STP) { 19578f92986dSozaki-r switch (bif->bif_state) { 19588f92986dSozaki-r case BSTP_IFSTATE_BLOCKING: 19598f92986dSozaki-r case BSTP_IFSTATE_LISTENING: 19608f92986dSozaki-r case BSTP_IFSTATE_DISABLED: 19618f92986dSozaki-r return true; 19628f92986dSozaki-r } 19638f92986dSozaki-r } 19648f92986dSozaki-r return false; 19658f92986dSozaki-r } 19668f92986dSozaki-r 19678f92986dSozaki-r static bool 19688f92986dSozaki-r bridge_ourether(struct bridge_iflist *bif, struct ether_header *eh, int src) 19698f92986dSozaki-r { 19708f92986dSozaki-r uint8_t *ether = src ? eh->ether_shost : eh->ether_dhost; 19718f92986dSozaki-r 19728f92986dSozaki-r if (memcmp(CLLADDR(bif->bif_ifp->if_sadl), ether, ETHER_ADDR_LEN) == 0 19738f92986dSozaki-r #if NCARP > 0 19748f92986dSozaki-r || (bif->bif_ifp->if_carp && 19758f92986dSozaki-r carp_ourether(bif->bif_ifp->if_carp, eh, IFT_ETHER, src) != NULL) 19768f92986dSozaki-r #endif /* NCARP > 0 */ 19778f92986dSozaki-r ) 19788f92986dSozaki-r return true; 19798f92986dSozaki-r 19808f92986dSozaki-r return false; 19818f92986dSozaki-r } 19828f92986dSozaki-r 1983460da35fSthorpej /* 1984460da35fSthorpej * bridge_input: 1985460da35fSthorpej * 1986460da35fSthorpej * Receive input from a member interface. Queue the packet for 1987460da35fSthorpej * bridging if it is not for us. 1988460da35fSthorpej */ 1989cb4cb631Sozaki-r static void 1990460da35fSthorpej bridge_input(struct ifnet *ifp, struct mbuf *m) 1991460da35fSthorpej { 1992460da35fSthorpej struct bridge_softc *sc = ifp->if_bridge; 1993460da35fSthorpej struct bridge_iflist *bif; 1994460da35fSthorpej struct ether_header *eh; 1995cc3dd2e0Sozaki-r struct psref psref; 19965c4a7693Sozaki-r int bound; 1997d81e97faSozaki-r DECLARE_LOCK_VARIABLE; 1998460da35fSthorpej 19999c4cd063Sozaki-r KASSERT(!cpu_intr_p()); 20009c4cd063Sozaki-r 20019161a49eSozaki-r if (__predict_false(sc == NULL) || 20029161a49eSozaki-r (sc->sc_if.if_flags & IFF_RUNNING) == 0) { 2003d81e97faSozaki-r ACQUIRE_GLOBAL_LOCKS(); 2004cb4cb631Sozaki-r ether_input(ifp, m); 2005d81e97faSozaki-r RELEASE_GLOBAL_LOCKS(); 2006cb4cb631Sozaki-r return; 2007cb4cb631Sozaki-r } 2008460da35fSthorpej 20095c4a7693Sozaki-r bound = curlwp_bind(); 2010cc3dd2e0Sozaki-r bif = bridge_lookup_member_if(sc, ifp, &psref); 2011cb4cb631Sozaki-r if (bif == NULL) { 20125c4a7693Sozaki-r curlwp_bindx(bound); 2013d81e97faSozaki-r ACQUIRE_GLOBAL_LOCKS(); 2014cb4cb631Sozaki-r ether_input(ifp, m); 2015d81e97faSozaki-r RELEASE_GLOBAL_LOCKS(); 2016cb4cb631Sozaki-r return; 2017cb4cb631Sozaki-r } 2018460da35fSthorpej 2019460da35fSthorpej eh = mtod(m, struct ether_header *); 2020460da35fSthorpej 2021cb4cb631Sozaki-r if (ETHER_IS_MULTICAST(eh->ether_dhost)) { 2022cb4cb631Sozaki-r if (memcmp(etherbroadcastaddr, 2023cb4cb631Sozaki-r eh->ether_dhost, ETHER_ADDR_LEN) == 0) 2024cb4cb631Sozaki-r m->m_flags |= M_BCAST; 2025cb4cb631Sozaki-r else 2026cb4cb631Sozaki-r m->m_flags |= M_MCAST; 2027cb4cb631Sozaki-r } 2028cb4cb631Sozaki-r 2029460da35fSthorpej /* 203010c5c987Sozaki-r * A 'fast' path for packets addressed to interfaces that are 203110c5c987Sozaki-r * part of this bridge. 2032460da35fSthorpej */ 203310c5c987Sozaki-r if (!(m->m_flags & (M_BCAST|M_MCAST)) && 203410c5c987Sozaki-r !bstp_state_before_learning(bif)) { 203510c5c987Sozaki-r struct bridge_iflist *_bif; 20364ad4b3a9Sozaki-r struct ifnet *_ifp = NULL; 2037cba69a87Sozaki-r int s; 2038cc3dd2e0Sozaki-r struct psref _psref; 2039460da35fSthorpej 20402977110aSozaki-r s = pserialize_read_enter(); 2041cc3dd2e0Sozaki-r BRIDGE_IFLIST_READER_FOREACH(_bif, sc) { 2042460da35fSthorpej /* It is destined for us. */ 204310c5c987Sozaki-r if (bridge_ourether(_bif, eh, 0)) { 2044cc3dd2e0Sozaki-r bridge_acquire_member(sc, _bif, &_psref); 20452977110aSozaki-r pserialize_read_exit(s); 204610c5c987Sozaki-r if (_bif->bif_flags & IFBIF_LEARNING) 2047460da35fSthorpej (void) bridge_rtupdate(sc, 2048460da35fSthorpej eh->ether_shost, ifp, 0, IFBAF_DYNAMIC); 2049d938d837Sozaki-r m_set_rcvif(m, _bif->bif_ifp); 2050d938d837Sozaki-r _ifp = _bif->bif_ifp; 2051cc3dd2e0Sozaki-r bridge_release_member(sc, _bif, &_psref); 2052cba69a87Sozaki-r goto out; 2053460da35fSthorpej } 2054460da35fSthorpej 2055460da35fSthorpej /* We just received a packet that we sent out. */ 20564ad4b3a9Sozaki-r if (bridge_ourether(_bif, eh, 1)) 20579161a49eSozaki-r break; 2058460da35fSthorpej } 20592977110aSozaki-r pserialize_read_exit(s); 2060cba69a87Sozaki-r out: 20619161a49eSozaki-r 20629161a49eSozaki-r if (_bif != NULL) { 2063cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 20645c4a7693Sozaki-r curlwp_bindx(bound); 206518566c8cSozaki-r if (_ifp != NULL) { 206618566c8cSozaki-r m->m_flags &= ~M_PROMISC; 2067d81e97faSozaki-r ACQUIRE_GLOBAL_LOCKS(); 20684ad4b3a9Sozaki-r ether_input(_ifp, m); 2069d81e97faSozaki-r RELEASE_GLOBAL_LOCKS(); 207018566c8cSozaki-r } else 20714ad4b3a9Sozaki-r m_freem(m); 20729161a49eSozaki-r return; 20739161a49eSozaki-r } 207410c5c987Sozaki-r } 2075460da35fSthorpej 207610c5c987Sozaki-r /* Tap off 802.1D packets; they do not get forwarded. */ 207710c5c987Sozaki-r if (bif->bif_flags & IFBIF_STP && 207810c5c987Sozaki-r memcmp(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN) == 0) { 207910c5c987Sozaki-r bstp_input(sc, bif, m); 2080cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 20815c4a7693Sozaki-r curlwp_bindx(bound); 208210c5c987Sozaki-r return; 208310c5c987Sozaki-r } 208410c5c987Sozaki-r 208510c5c987Sozaki-r /* 208610c5c987Sozaki-r * A normal switch would discard the packet here, but that's not what 208710c5c987Sozaki-r * we've done historically. This also prevents some obnoxious behaviour. 208810c5c987Sozaki-r */ 208910c5c987Sozaki-r if (bstp_state_before_learning(bif)) { 2090cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 20915c4a7693Sozaki-r curlwp_bindx(bound); 2092d81e97faSozaki-r ACQUIRE_GLOBAL_LOCKS(); 20934ad4b3a9Sozaki-r ether_input(ifp, m); 2094d81e97faSozaki-r RELEASE_GLOBAL_LOCKS(); 209510c5c987Sozaki-r return; 209610c5c987Sozaki-r } 209710c5c987Sozaki-r 2098cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 20999161a49eSozaki-r 2100b7a310caSozaki-r bridge_forward(sc, m); 21015c4a7693Sozaki-r 21025c4a7693Sozaki-r curlwp_bindx(bound); 2103ef5da9a9Sozaki-r } 2104460da35fSthorpej 2105460da35fSthorpej /* 2106460da35fSthorpej * bridge_broadcast: 2107460da35fSthorpej * 2108460da35fSthorpej * Send a frame to all interfaces that are members of 2109460da35fSthorpej * the bridge, except for the one on which the packet 2110460da35fSthorpej * arrived. 2111460da35fSthorpej */ 211263eac52bSthorpej static void 2113460da35fSthorpej bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if, 2114008f982cSozaki-r bool src_if_protected, struct mbuf *m) 2115460da35fSthorpej { 2116460da35fSthorpej struct bridge_iflist *bif; 2117460da35fSthorpej struct mbuf *mc; 2118460da35fSthorpej struct ifnet *dst_if; 211918566c8cSozaki-r bool bmcast; 2120cba69a87Sozaki-r int s; 2121d81e97faSozaki-r DECLARE_LOCK_VARIABLE; 212210c5c987Sozaki-r 212318566c8cSozaki-r bmcast = m->m_flags & (M_BCAST|M_MCAST); 2124460da35fSthorpej 21252977110aSozaki-r s = pserialize_read_enter(); 2126cc3dd2e0Sozaki-r BRIDGE_IFLIST_READER_FOREACH(bif, sc) { 2127cc3dd2e0Sozaki-r struct psref psref; 2128cc3dd2e0Sozaki-r 2129cc3dd2e0Sozaki-r bridge_acquire_member(sc, bif, &psref); 21302977110aSozaki-r pserialize_read_exit(s); 2131cba69a87Sozaki-r 2132460da35fSthorpej dst_if = bif->bif_ifp; 2133460da35fSthorpej 2134460da35fSthorpej if (bif->bif_flags & IFBIF_STP) { 2135460da35fSthorpej switch (bif->bif_state) { 2136460da35fSthorpej case BSTP_IFSTATE_BLOCKING: 2137460da35fSthorpej case BSTP_IFSTATE_DISABLED: 2138cba69a87Sozaki-r goto next; 2139460da35fSthorpej } 2140460da35fSthorpej } 2141460da35fSthorpej 214210c5c987Sozaki-r if ((bif->bif_flags & IFBIF_DISCOVER) == 0 && !bmcast) 2143cba69a87Sozaki-r goto next; 2144460da35fSthorpej 2145460da35fSthorpej if ((dst_if->if_flags & IFF_RUNNING) == 0) 2146cba69a87Sozaki-r goto next; 2147460da35fSthorpej 214818566c8cSozaki-r if (dst_if != src_if) { 2149008f982cSozaki-r if ((bif->bif_flags & IFBIF_PROTECTED) && 2150008f982cSozaki-r src_if_protected) { 2151008f982cSozaki-r goto next; 2152008f982cSozaki-r } 2153008f982cSozaki-r 21548553f24dSmsaitoh mc = m_copypacket(m, M_DONTWAIT); 2155460da35fSthorpej if (mc == NULL) { 215670b554e6Sthorpej if_statinc(&sc->sc_if, if_oerrors); 2157cba69a87Sozaki-r goto next; 2158460da35fSthorpej } 215922e37dedSmsaitoh /* 2160ad29c409Smsaitoh * Before enqueueing this packet to the destination 2161ad29c409Smsaitoh * interface, clear any in-bound checksum flags to 2162ad29c409Smsaitoh * prevent them from being misused as out-bound flags. 216322e37dedSmsaitoh */ 2164ad29c409Smsaitoh mc->m_pkthdr.csum_flags = 0; 216522e37dedSmsaitoh 2166d81e97faSozaki-r ACQUIRE_GLOBAL_LOCKS(); 216718566c8cSozaki-r bridge_enqueue(sc, dst_if, mc, 1); 2168d81e97faSozaki-r RELEASE_GLOBAL_LOCKS(); 2169460da35fSthorpej } 2170460da35fSthorpej 217118566c8cSozaki-r if (bmcast) { 21728553f24dSmsaitoh mc = m_copypacket(m, M_DONTWAIT); 217318566c8cSozaki-r if (mc == NULL) { 217470b554e6Sthorpej if_statinc(&sc->sc_if, if_oerrors); 217518566c8cSozaki-r goto next; 217618566c8cSozaki-r } 21779d8cab40Sjdolecek /* 21789d8cab40Sjdolecek * Before enqueueing this packet to the destination 21799d8cab40Sjdolecek * interface, clear any in-bound checksum flags to 21809d8cab40Sjdolecek * prevent them from being misused as out-bound flags. 21819d8cab40Sjdolecek */ 21829d8cab40Sjdolecek mc->m_pkthdr.csum_flags = 0; 218318566c8cSozaki-r 2184d938d837Sozaki-r m_set_rcvif(mc, dst_if); 218518566c8cSozaki-r mc->m_flags &= ~M_PROMISC; 2186d81e97faSozaki-r 2187d81e97faSozaki-r ACQUIRE_GLOBAL_LOCKS(); 218818566c8cSozaki-r ether_input(dst_if, mc); 2189d81e97faSozaki-r RELEASE_GLOBAL_LOCKS(); 219018566c8cSozaki-r } 2191cba69a87Sozaki-r next: 21922977110aSozaki-r s = pserialize_read_enter(); 2193cc3dd2e0Sozaki-r bridge_release_member(sc, bif, &psref); 2194460da35fSthorpej } 21952977110aSozaki-r pserialize_read_exit(s); 219610c5c987Sozaki-r 2197460da35fSthorpej m_freem(m); 2198460da35fSthorpej } 2199460da35fSthorpej 2200e85cdef1Sozaki-r static int 2201e85cdef1Sozaki-r bridge_rtalloc(struct bridge_softc *sc, const uint8_t *dst, 2202e85cdef1Sozaki-r struct bridge_rtnode **brtp) 2203e85cdef1Sozaki-r { 2204e85cdef1Sozaki-r struct bridge_rtnode *brt; 2205e85cdef1Sozaki-r int error; 2206e85cdef1Sozaki-r 2207e85cdef1Sozaki-r if (sc->sc_brtcnt >= sc->sc_brtmax) 2208e85cdef1Sozaki-r return ENOSPC; 2209e85cdef1Sozaki-r 2210e85cdef1Sozaki-r /* 2211e85cdef1Sozaki-r * Allocate a new bridge forwarding node, and 2212e85cdef1Sozaki-r * initialize the expiration time and Ethernet 2213e85cdef1Sozaki-r * address. 2214e85cdef1Sozaki-r */ 2215e85cdef1Sozaki-r brt = pool_get(&bridge_rtnode_pool, PR_NOWAIT); 2216e85cdef1Sozaki-r if (brt == NULL) 2217e85cdef1Sozaki-r return ENOMEM; 2218e85cdef1Sozaki-r 2219e85cdef1Sozaki-r memset(brt, 0, sizeof(*brt)); 2220e85cdef1Sozaki-r brt->brt_expire = time_uptime + sc->sc_brttimeout; 2221e85cdef1Sozaki-r brt->brt_flags = IFBAF_DYNAMIC; 2222e85cdef1Sozaki-r memcpy(brt->brt_addr, dst, ETHER_ADDR_LEN); 2223f811c85eSozaki-r PSLIST_ENTRY_INIT(brt, brt_list); 2224f811c85eSozaki-r PSLIST_ENTRY_INIT(brt, brt_hash); 2225e85cdef1Sozaki-r 2226b7a310caSozaki-r BRIDGE_RT_LOCK(sc); 2227e85cdef1Sozaki-r error = bridge_rtnode_insert(sc, brt); 2228b7a310caSozaki-r BRIDGE_RT_UNLOCK(sc); 2229e85cdef1Sozaki-r 2230e85cdef1Sozaki-r if (error != 0) { 2231e85cdef1Sozaki-r pool_put(&bridge_rtnode_pool, brt); 2232e85cdef1Sozaki-r return error; 2233e85cdef1Sozaki-r } 2234e85cdef1Sozaki-r 2235e85cdef1Sozaki-r *brtp = brt; 2236e85cdef1Sozaki-r return 0; 2237e85cdef1Sozaki-r } 2238e85cdef1Sozaki-r 2239460da35fSthorpej /* 2240460da35fSthorpej * bridge_rtupdate: 2241460da35fSthorpej * 2242460da35fSthorpej * Add a bridge routing entry. 2243460da35fSthorpej */ 224463eac52bSthorpej static int 2245460da35fSthorpej bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, 2246460da35fSthorpej struct ifnet *dst_if, int setflags, uint8_t flags) 2247460da35fSthorpej { 2248460da35fSthorpej struct bridge_rtnode *brt; 2249e85cdef1Sozaki-r int s; 22509161a49eSozaki-r 2251e85cdef1Sozaki-r again: 2252460da35fSthorpej /* 2253460da35fSthorpej * A route for this destination might already exist. If so, 2254460da35fSthorpej * update it, otherwise create a new one. 2255460da35fSthorpej */ 22562977110aSozaki-r s = pserialize_read_enter(); 2257e85cdef1Sozaki-r brt = bridge_rtnode_lookup(sc, dst); 2258460da35fSthorpej 2259e85cdef1Sozaki-r if (brt != NULL) { 2260460da35fSthorpej brt->brt_ifp = dst_if; 2261460da35fSthorpej if (setflags) { 2262460da35fSthorpej brt->brt_flags = flags; 2263de4337abSkardel if (flags & IFBAF_STATIC) 2264de4337abSkardel brt->brt_expire = 0; 2265de4337abSkardel else 2266de4337abSkardel brt->brt_expire = time_uptime + sc->sc_brttimeout; 22670af74700Sozaki-r } else { 22680af74700Sozaki-r if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) 22690af74700Sozaki-r brt->brt_expire = time_uptime + sc->sc_brttimeout; 2270460da35fSthorpej } 2271e85cdef1Sozaki-r } 22722977110aSozaki-r pserialize_read_exit(s); 2273460da35fSthorpej 2274e85cdef1Sozaki-r if (brt == NULL) { 2275e85cdef1Sozaki-r int r; 22769161a49eSozaki-r 2277e85cdef1Sozaki-r r = bridge_rtalloc(sc, dst, &brt); 2278e85cdef1Sozaki-r if (r != 0) 2279e85cdef1Sozaki-r return r; 2280e85cdef1Sozaki-r goto again; 2281e85cdef1Sozaki-r } 2282e85cdef1Sozaki-r 2283e85cdef1Sozaki-r return 0; 2284460da35fSthorpej } 2285460da35fSthorpej 2286460da35fSthorpej /* 2287460da35fSthorpej * bridge_rtlookup: 2288460da35fSthorpej * 2289460da35fSthorpej * Lookup the destination interface for an address. 2290460da35fSthorpej */ 229163eac52bSthorpej static struct ifnet * 2292460da35fSthorpej bridge_rtlookup(struct bridge_softc *sc, const uint8_t *addr) 2293460da35fSthorpej { 2294460da35fSthorpej struct bridge_rtnode *brt; 22959161a49eSozaki-r struct ifnet *ifs = NULL; 2296e85cdef1Sozaki-r int s; 2297460da35fSthorpej 22982977110aSozaki-r s = pserialize_read_enter(); 22999161a49eSozaki-r brt = bridge_rtnode_lookup(sc, addr); 23009161a49eSozaki-r if (brt != NULL) 23019161a49eSozaki-r ifs = brt->brt_ifp; 23022977110aSozaki-r pserialize_read_exit(s); 23039161a49eSozaki-r 23049161a49eSozaki-r return ifs; 2305460da35fSthorpej } 2306460da35fSthorpej 2307e85cdef1Sozaki-r typedef bool (*bridge_iterate_cb_t) 2308e85cdef1Sozaki-r (struct bridge_softc *, struct bridge_rtnode *, bool *, void *); 2309e85cdef1Sozaki-r 2310e85cdef1Sozaki-r /* 2311e85cdef1Sozaki-r * bridge_rtlist_iterate_remove: 2312e85cdef1Sozaki-r * 2313e85cdef1Sozaki-r * It iterates on sc->sc_rtlist and removes rtnodes of it which func 2314e85cdef1Sozaki-r * callback judges to remove. Removals of rtnodes are done in a manner 2315e85cdef1Sozaki-r * of pserialize. To this end, all kmem_* operations are placed out of 2316e85cdef1Sozaki-r * mutexes. 2317e85cdef1Sozaki-r */ 2318e85cdef1Sozaki-r static void 2319e85cdef1Sozaki-r bridge_rtlist_iterate_remove(struct bridge_softc *sc, bridge_iterate_cb_t func, void *arg) 2320e85cdef1Sozaki-r { 23217a003d61Sozaki-r struct bridge_rtnode *brt; 2322e85cdef1Sozaki-r struct bridge_rtnode **brt_list; 2323e85cdef1Sozaki-r int i, count; 2324e85cdef1Sozaki-r 2325e85cdef1Sozaki-r retry: 2326e85cdef1Sozaki-r count = sc->sc_brtcnt; 2327e85cdef1Sozaki-r if (count == 0) 2328e85cdef1Sozaki-r return; 23292f8be187Smaxv brt_list = kmem_alloc(sizeof(*brt_list) * count, KM_SLEEP); 2330e85cdef1Sozaki-r 2331e85cdef1Sozaki-r BRIDGE_RT_LOCK(sc); 2332e85cdef1Sozaki-r if (__predict_false(sc->sc_brtcnt > count)) { 2333e85cdef1Sozaki-r /* The rtnodes increased, we need more memory */ 2334e85cdef1Sozaki-r BRIDGE_RT_UNLOCK(sc); 2335e85cdef1Sozaki-r kmem_free(brt_list, sizeof(*brt_list) * count); 2336e85cdef1Sozaki-r goto retry; 2337e85cdef1Sozaki-r } 2338e85cdef1Sozaki-r 2339e85cdef1Sozaki-r i = 0; 23407a003d61Sozaki-r /* 23417a003d61Sozaki-r * We don't need to use a _SAFE variant here because we know 23427a003d61Sozaki-r * that a removed item keeps its next pointer as-is thanks to 23437a003d61Sozaki-r * pslist(9) and isn't freed in the loop. 23447a003d61Sozaki-r */ 23457a003d61Sozaki-r BRIDGE_RTLIST_WRITER_FOREACH(brt, sc) { 2346e85cdef1Sozaki-r bool need_break = false; 2347e85cdef1Sozaki-r if (func(sc, brt, &need_break, arg)) { 2348e85cdef1Sozaki-r bridge_rtnode_remove(sc, brt); 2349e85cdef1Sozaki-r brt_list[i++] = brt; 2350e85cdef1Sozaki-r } 2351e85cdef1Sozaki-r if (need_break) 2352e85cdef1Sozaki-r break; 2353e85cdef1Sozaki-r } 2354e85cdef1Sozaki-r 2355e85cdef1Sozaki-r if (i > 0) 2356e85cdef1Sozaki-r BRIDGE_RT_PSZ_PERFORM(sc); 2357e85cdef1Sozaki-r BRIDGE_RT_UNLOCK(sc); 2358e85cdef1Sozaki-r 2359e85cdef1Sozaki-r while (--i >= 0) 2360e85cdef1Sozaki-r bridge_rtnode_destroy(brt_list[i]); 2361e85cdef1Sozaki-r 2362e85cdef1Sozaki-r kmem_free(brt_list, sizeof(*brt_list) * count); 2363e85cdef1Sozaki-r } 2364e85cdef1Sozaki-r 2365e85cdef1Sozaki-r static bool 2366e85cdef1Sozaki-r bridge_rttrim0_cb(struct bridge_softc *sc, struct bridge_rtnode *brt, 2367e85cdef1Sozaki-r bool *need_break, void *arg) 2368e85cdef1Sozaki-r { 2369e85cdef1Sozaki-r if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { 2370e85cdef1Sozaki-r /* Take into account of the subsequent removal */ 2371e85cdef1Sozaki-r if ((sc->sc_brtcnt - 1) <= sc->sc_brtmax) 2372e85cdef1Sozaki-r *need_break = true; 2373e85cdef1Sozaki-r return true; 2374e85cdef1Sozaki-r } else 2375e85cdef1Sozaki-r return false; 2376e85cdef1Sozaki-r } 2377e85cdef1Sozaki-r 2378e85cdef1Sozaki-r static void 2379e85cdef1Sozaki-r bridge_rttrim0(struct bridge_softc *sc) 2380e85cdef1Sozaki-r { 2381e85cdef1Sozaki-r bridge_rtlist_iterate_remove(sc, bridge_rttrim0_cb, NULL); 2382e85cdef1Sozaki-r } 2383e85cdef1Sozaki-r 2384460da35fSthorpej /* 2385460da35fSthorpej * bridge_rttrim: 2386460da35fSthorpej * 2387460da35fSthorpej * Trim the routine table so that we have a number 2388460da35fSthorpej * of routing entries less than or equal to the 2389460da35fSthorpej * maximum number. 2390460da35fSthorpej */ 239163eac52bSthorpej static void 2392460da35fSthorpej bridge_rttrim(struct bridge_softc *sc) 2393460da35fSthorpej { 23949161a49eSozaki-r 2395460da35fSthorpej /* Make sure we actually need to do this. */ 2396460da35fSthorpej if (sc->sc_brtcnt <= sc->sc_brtmax) 2397e85cdef1Sozaki-r return; 2398460da35fSthorpej 2399460da35fSthorpej /* Force an aging cycle; this might trim enough addresses. */ 2400460da35fSthorpej bridge_rtage(sc); 2401460da35fSthorpej if (sc->sc_brtcnt <= sc->sc_brtmax) 2402e85cdef1Sozaki-r return; 2403460da35fSthorpej 2404e85cdef1Sozaki-r bridge_rttrim0(sc); 24059161a49eSozaki-r 2406460da35fSthorpej return; 2407460da35fSthorpej } 2408460da35fSthorpej 2409460da35fSthorpej /* 2410460da35fSthorpej * bridge_timer: 2411460da35fSthorpej * 2412460da35fSthorpej * Aging timer for the bridge. 2413460da35fSthorpej */ 241463eac52bSthorpej static void 2415460da35fSthorpej bridge_timer(void *arg) 2416460da35fSthorpej { 2417460da35fSthorpej struct bridge_softc *sc = arg; 2418460da35fSthorpej 2419057a6a48Sozaki-r workqueue_enqueue(sc->sc_rtage_wq, &sc->sc_rtage_wk, NULL); 2420e85cdef1Sozaki-r } 2421e85cdef1Sozaki-r 2422e85cdef1Sozaki-r static void 2423e85cdef1Sozaki-r bridge_rtage_work(struct work *wk, void *arg) 2424e85cdef1Sozaki-r { 2425e85cdef1Sozaki-r struct bridge_softc *sc = arg; 2426e85cdef1Sozaki-r 2427057a6a48Sozaki-r KASSERT(wk == &sc->sc_rtage_wk); 24289161a49eSozaki-r 2429460da35fSthorpej bridge_rtage(sc); 2430460da35fSthorpej 2431460da35fSthorpej if (sc->sc_if.if_flags & IFF_RUNNING) 2432460da35fSthorpej callout_reset(&sc->sc_brcallout, 2433460da35fSthorpej bridge_rtable_prune_period * hz, bridge_timer, sc); 2434e85cdef1Sozaki-r } 24359161a49eSozaki-r 2436e85cdef1Sozaki-r static bool 2437e85cdef1Sozaki-r bridge_rtage_cb(struct bridge_softc *sc, struct bridge_rtnode *brt, 2438e85cdef1Sozaki-r bool *need_break, void *arg) 2439e85cdef1Sozaki-r { 2440e85cdef1Sozaki-r if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC && 2441e85cdef1Sozaki-r time_uptime >= brt->brt_expire) 2442e85cdef1Sozaki-r return true; 2443e85cdef1Sozaki-r else 2444e85cdef1Sozaki-r return false; 2445460da35fSthorpej } 2446460da35fSthorpej 2447460da35fSthorpej /* 2448460da35fSthorpej * bridge_rtage: 2449460da35fSthorpej * 2450460da35fSthorpej * Perform an aging cycle. 2451460da35fSthorpej */ 245263eac52bSthorpej static void 2453460da35fSthorpej bridge_rtage(struct bridge_softc *sc) 2454460da35fSthorpej { 2455e85cdef1Sozaki-r bridge_rtlist_iterate_remove(sc, bridge_rtage_cb, NULL); 2456460da35fSthorpej } 2457e85cdef1Sozaki-r 2458e85cdef1Sozaki-r 2459e85cdef1Sozaki-r static bool 2460e85cdef1Sozaki-r bridge_rtflush_cb(struct bridge_softc *sc, struct bridge_rtnode *brt, 2461e85cdef1Sozaki-r bool *need_break, void *arg) 2462e85cdef1Sozaki-r { 2463e85cdef1Sozaki-r int full = *(int*)arg; 2464e85cdef1Sozaki-r 2465e85cdef1Sozaki-r if (full || (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) 2466e85cdef1Sozaki-r return true; 2467e85cdef1Sozaki-r else 2468e85cdef1Sozaki-r return false; 2469460da35fSthorpej } 2470460da35fSthorpej 2471460da35fSthorpej /* 2472460da35fSthorpej * bridge_rtflush: 2473460da35fSthorpej * 2474460da35fSthorpej * Remove all dynamic addresses from the bridge. 2475460da35fSthorpej */ 247663eac52bSthorpej static void 2477460da35fSthorpej bridge_rtflush(struct bridge_softc *sc, int full) 2478460da35fSthorpej { 2479e85cdef1Sozaki-r bridge_rtlist_iterate_remove(sc, bridge_rtflush_cb, &full); 2480460da35fSthorpej } 2481460da35fSthorpej 2482460da35fSthorpej /* 2483460da35fSthorpej * bridge_rtdaddr: 2484460da35fSthorpej * 2485460da35fSthorpej * Remove an address from the table. 2486460da35fSthorpej */ 248763eac52bSthorpej static int 2488460da35fSthorpej bridge_rtdaddr(struct bridge_softc *sc, const uint8_t *addr) 2489460da35fSthorpej { 2490460da35fSthorpej struct bridge_rtnode *brt; 2491460da35fSthorpej 2492e85cdef1Sozaki-r BRIDGE_RT_LOCK(sc); 24939161a49eSozaki-r if ((brt = bridge_rtnode_lookup(sc, addr)) == NULL) { 2494e85cdef1Sozaki-r BRIDGE_RT_UNLOCK(sc); 2495e85cdef1Sozaki-r return ENOENT; 24969161a49eSozaki-r } 2497e85cdef1Sozaki-r bridge_rtnode_remove(sc, brt); 2498e85cdef1Sozaki-r BRIDGE_RT_PSZ_PERFORM(sc); 2499e85cdef1Sozaki-r BRIDGE_RT_UNLOCK(sc); 2500460da35fSthorpej 2501e85cdef1Sozaki-r bridge_rtnode_destroy(brt); 25029161a49eSozaki-r 2503e85cdef1Sozaki-r return 0; 2504460da35fSthorpej } 2505460da35fSthorpej 2506460da35fSthorpej /* 2507460da35fSthorpej * bridge_rtdelete: 2508460da35fSthorpej * 2509460da35fSthorpej * Delete routes to a speicifc member interface. 2510460da35fSthorpej */ 251163eac52bSthorpej static void 2512460da35fSthorpej bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp) 2513460da35fSthorpej { 2514f1d17afeSozaki-r struct bridge_rtnode *brt; 2515460da35fSthorpej 2516db4bbd8bSozaki-r /* XXX pserialize_perform for each entry is slow */ 2517db4bbd8bSozaki-r again: 2518e85cdef1Sozaki-r BRIDGE_RT_LOCK(sc); 25197a003d61Sozaki-r BRIDGE_RTLIST_WRITER_FOREACH(brt, sc) { 2520460da35fSthorpej if (brt->brt_ifp == ifp) 2521e85cdef1Sozaki-r break; 2522460da35fSthorpej } 2523e85cdef1Sozaki-r if (brt == NULL) { 2524e85cdef1Sozaki-r BRIDGE_RT_UNLOCK(sc); 2525e85cdef1Sozaki-r return; 2526e85cdef1Sozaki-r } 2527e85cdef1Sozaki-r bridge_rtnode_remove(sc, brt); 2528e85cdef1Sozaki-r BRIDGE_RT_PSZ_PERFORM(sc); 2529e85cdef1Sozaki-r BRIDGE_RT_UNLOCK(sc); 25309161a49eSozaki-r 2531e85cdef1Sozaki-r bridge_rtnode_destroy(brt); 2532db4bbd8bSozaki-r 2533db4bbd8bSozaki-r goto again; 2534460da35fSthorpej } 2535460da35fSthorpej 2536460da35fSthorpej /* 2537460da35fSthorpej * bridge_rtable_init: 2538460da35fSthorpej * 2539460da35fSthorpej * Initialize the route table for this bridge. 2540460da35fSthorpej */ 2541df0047d0Sozaki-r static void 2542460da35fSthorpej bridge_rtable_init(struct bridge_softc *sc) 2543460da35fSthorpej { 2544460da35fSthorpej int i; 2545460da35fSthorpej 2546df0047d0Sozaki-r sc->sc_rthash = kmem_alloc(sizeof(*sc->sc_rthash) * BRIDGE_RTHASH_SIZE, 2547df0047d0Sozaki-r KM_SLEEP); 2548460da35fSthorpej 2549460da35fSthorpej for (i = 0; i < BRIDGE_RTHASH_SIZE; i++) 25507a003d61Sozaki-r PSLIST_INIT(&sc->sc_rthash[i]); 2551460da35fSthorpej 25523afd44cfStls sc->sc_rthash_key = cprng_fast32(); 2553460da35fSthorpej 25547a003d61Sozaki-r PSLIST_INIT(&sc->sc_rtlist); 2555460da35fSthorpej 2556e85cdef1Sozaki-r sc->sc_rtlist_psz = pserialize_create(); 2557e85cdef1Sozaki-r sc->sc_rtlist_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_SOFTNET); 2558460da35fSthorpej } 2559460da35fSthorpej 2560460da35fSthorpej /* 2561460da35fSthorpej * bridge_rtable_fini: 2562460da35fSthorpej * 2563460da35fSthorpej * Deconstruct the route table for this bridge. 2564460da35fSthorpej */ 256563eac52bSthorpej static void 2566460da35fSthorpej bridge_rtable_fini(struct bridge_softc *sc) 2567460da35fSthorpej { 2568460da35fSthorpej 2569df0047d0Sozaki-r kmem_free(sc->sc_rthash, sizeof(*sc->sc_rthash) * BRIDGE_RTHASH_SIZE); 25709161a49eSozaki-r mutex_obj_free(sc->sc_rtlist_lock); 2571e85cdef1Sozaki-r pserialize_destroy(sc->sc_rtlist_psz); 2572460da35fSthorpej } 2573460da35fSthorpej 2574460da35fSthorpej /* 2575460da35fSthorpej * The following hash function is adapted from "Hash Functions" by Bob Jenkins 2576460da35fSthorpej * ("Algorithm Alley", Dr. Dobbs Journal, September 1997). 2577460da35fSthorpej */ 2578460da35fSthorpej #define mix(a, b, c) \ 2579460da35fSthorpej do { \ 2580460da35fSthorpej a -= b; a -= c; a ^= (c >> 13); \ 2581460da35fSthorpej b -= c; b -= a; b ^= (a << 8); \ 2582460da35fSthorpej c -= a; c -= b; c ^= (b >> 13); \ 2583460da35fSthorpej a -= b; a -= c; a ^= (c >> 12); \ 2584460da35fSthorpej b -= c; b -= a; b ^= (a << 16); \ 2585460da35fSthorpej c -= a; c -= b; c ^= (b >> 5); \ 2586460da35fSthorpej a -= b; a -= c; a ^= (c >> 3); \ 2587460da35fSthorpej b -= c; b -= a; b ^= (a << 10); \ 2588460da35fSthorpej c -= a; c -= b; c ^= (b >> 15); \ 2589460da35fSthorpej } while (/*CONSTCOND*/0) 2590460da35fSthorpej 25910f0296d8Sperry static inline uint32_t 2592460da35fSthorpej bridge_rthash(struct bridge_softc *sc, const uint8_t *addr) 2593460da35fSthorpej { 2594460da35fSthorpej uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = sc->sc_rthash_key; 2595460da35fSthorpej 2596460da35fSthorpej b += addr[5] << 8; 2597460da35fSthorpej b += addr[4]; 2598d3751be2Smsaitoh a += (uint32_t)addr[3] << 24; 2599460da35fSthorpej a += addr[2] << 16; 2600460da35fSthorpej a += addr[1] << 8; 2601460da35fSthorpej a += addr[0]; 2602460da35fSthorpej 2603460da35fSthorpej mix(a, b, c); 2604460da35fSthorpej 2605460da35fSthorpej return (c & BRIDGE_RTHASH_MASK); 2606460da35fSthorpej } 2607460da35fSthorpej 2608460da35fSthorpej #undef mix 2609460da35fSthorpej 2610460da35fSthorpej /* 2611460da35fSthorpej * bridge_rtnode_lookup: 2612460da35fSthorpej * 2613460da35fSthorpej * Look up a bridge route node for the specified destination. 2614460da35fSthorpej */ 261563eac52bSthorpej static struct bridge_rtnode * 2616460da35fSthorpej bridge_rtnode_lookup(struct bridge_softc *sc, const uint8_t *addr) 2617460da35fSthorpej { 2618460da35fSthorpej struct bridge_rtnode *brt; 2619460da35fSthorpej uint32_t hash; 2620460da35fSthorpej int dir; 2621460da35fSthorpej 2622460da35fSthorpej hash = bridge_rthash(sc, addr); 26237a003d61Sozaki-r BRIDGE_RTHASH_READER_FOREACH(brt, sc, hash) { 2624460da35fSthorpej dir = memcmp(addr, brt->brt_addr, ETHER_ADDR_LEN); 2625460da35fSthorpej if (dir == 0) 2626a40cbde0Smsaitoh return brt; 2627460da35fSthorpej if (dir > 0) 2628a40cbde0Smsaitoh return NULL; 2629460da35fSthorpej } 2630460da35fSthorpej 2631a40cbde0Smsaitoh return NULL; 2632460da35fSthorpej } 2633460da35fSthorpej 2634460da35fSthorpej /* 2635460da35fSthorpej * bridge_rtnode_insert: 2636460da35fSthorpej * 2637460da35fSthorpej * Insert the specified bridge node into the route table. We 2638460da35fSthorpej * assume the entry is not already in the table. 2639460da35fSthorpej */ 264063eac52bSthorpej static int 2641460da35fSthorpej bridge_rtnode_insert(struct bridge_softc *sc, struct bridge_rtnode *brt) 2642460da35fSthorpej { 26435cf068d7Sozaki-r struct bridge_rtnode *lbrt, *prev = NULL; 2644460da35fSthorpej uint32_t hash; 2645460da35fSthorpej 2646b7a310caSozaki-r KASSERT(BRIDGE_RT_LOCKED(sc)); 26479161a49eSozaki-r 2648460da35fSthorpej hash = bridge_rthash(sc, brt->brt_addr); 26497a003d61Sozaki-r BRIDGE_RTHASH_WRITER_FOREACH(lbrt, sc, hash) { 26505cf068d7Sozaki-r int dir = memcmp(brt->brt_addr, lbrt->brt_addr, ETHER_ADDR_LEN); 2651460da35fSthorpej if (dir == 0) 2652a40cbde0Smsaitoh return EEXIST; 26535cf068d7Sozaki-r if (dir > 0) 26545cf068d7Sozaki-r break; 26555cf068d7Sozaki-r prev = lbrt; 2656460da35fSthorpej } 26575cf068d7Sozaki-r if (prev == NULL) 26587a003d61Sozaki-r BRIDGE_RTHASH_WRITER_INSERT_HEAD(sc, hash, brt); 26595cf068d7Sozaki-r else 26607a003d61Sozaki-r BRIDGE_RTHASH_WRITER_INSERT_AFTER(prev, brt); 2661460da35fSthorpej 26627a003d61Sozaki-r BRIDGE_RTLIST_WRITER_INSERT_HEAD(sc, brt); 2663460da35fSthorpej sc->sc_brtcnt++; 2664460da35fSthorpej 2665a40cbde0Smsaitoh return 0; 2666460da35fSthorpej } 2667460da35fSthorpej 2668460da35fSthorpej /* 2669e85cdef1Sozaki-r * bridge_rtnode_remove: 2670e85cdef1Sozaki-r * 2671e85cdef1Sozaki-r * Remove a bridge rtnode from the rthash and the rtlist of a bridge. 2672e85cdef1Sozaki-r */ 2673e85cdef1Sozaki-r static void 2674e85cdef1Sozaki-r bridge_rtnode_remove(struct bridge_softc *sc, struct bridge_rtnode *brt) 2675e85cdef1Sozaki-r { 2676e85cdef1Sozaki-r 2677b7a310caSozaki-r KASSERT(BRIDGE_RT_LOCKED(sc)); 2678e85cdef1Sozaki-r 26797a003d61Sozaki-r BRIDGE_RTHASH_WRITER_REMOVE(brt); 26807a003d61Sozaki-r BRIDGE_RTLIST_WRITER_REMOVE(brt); 2681e85cdef1Sozaki-r sc->sc_brtcnt--; 2682e85cdef1Sozaki-r } 2683e85cdef1Sozaki-r 2684e85cdef1Sozaki-r /* 2685460da35fSthorpej * bridge_rtnode_destroy: 2686460da35fSthorpej * 2687460da35fSthorpej * Destroy a bridge rtnode. 2688460da35fSthorpej */ 268963eac52bSthorpej static void 2690e85cdef1Sozaki-r bridge_rtnode_destroy(struct bridge_rtnode *brt) 2691460da35fSthorpej { 26929161a49eSozaki-r 2693f811c85eSozaki-r PSLIST_ENTRY_DESTROY(brt, brt_list); 2694f811c85eSozaki-r PSLIST_ENTRY_DESTROY(brt, brt_hash); 2695460da35fSthorpej pool_put(&bridge_rtnode_pool, brt); 2696460da35fSthorpej } 26976b857c22Sperseant 2698f04a92b1Srmind extern pfil_head_t *inet_pfil_hook; /* XXX */ 2699f04a92b1Srmind extern pfil_head_t *inet6_pfil_hook; /* XXX */ 27006b857c22Sperseant 27016b857c22Sperseant /* 27026b857c22Sperseant * Send bridge packets through IPF if they are one of the types IPF can deal 27036b857c22Sperseant * with, or if they are ARP or REVARP. (IPF will pass ARP and REVARP without 27046b857c22Sperseant * question.) 27056b857c22Sperseant */ 270663eac52bSthorpej static int 2707168cd830Schristos bridge_ipf(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) 27086b857c22Sperseant { 270999807188Sjdc int snap, error; 271099807188Sjdc struct ether_header *eh1, eh2; 27119e2e82ebSjdc struct llc llc1; 27122b028087Smatt uint16_t ether_type; 27136b857c22Sperseant 27146b857c22Sperseant snap = 0; 27156b857c22Sperseant error = -1; /* Default error if not error == 0 */ 271699807188Sjdc eh1 = mtod(*mp, struct ether_header *); 271799807188Sjdc ether_type = ntohs(eh1->ether_type); 27186b857c22Sperseant 27196b857c22Sperseant /* 27206b857c22Sperseant * Check for SNAP/LLC. 27216b857c22Sperseant */ 27226b857c22Sperseant if (ether_type < ETHERMTU) { 27239e2e82ebSjdc struct llc *llc2 = (struct llc *)(eh1 + 1); 27246b857c22Sperseant 27256b857c22Sperseant if ((*mp)->m_len >= ETHER_HDR_LEN + 8 && 27269e2e82ebSjdc llc2->llc_dsap == LLC_SNAP_LSAP && 27279e2e82ebSjdc llc2->llc_ssap == LLC_SNAP_LSAP && 27289e2e82ebSjdc llc2->llc_control == LLC_UI) { 27299e2e82ebSjdc ether_type = htons(llc2->llc_un.type_snap.ether_type); 27306b857c22Sperseant snap = 1; 27316b857c22Sperseant } 27326b857c22Sperseant } 27336b857c22Sperseant 2734f17fac57Syamaguchi /* drop VLAN traffic untagged by hardware offloading */ 2735f17fac57Syamaguchi if (vlan_has_tag(*mp)) 2736f17fac57Syamaguchi goto bad; 2737f17fac57Syamaguchi 27386b857c22Sperseant /* 27396b857c22Sperseant * If we're trying to filter bridge traffic, don't look at anything 27406b857c22Sperseant * other than IP and ARP traffic. If the filter doesn't understand 27416b857c22Sperseant * IPv6, don't allow IPv6 through the bridge either. This is lame 27426b857c22Sperseant * since if we really wanted, say, an AppleTalk filter, we are hosed, 27436b857c22Sperseant * but of course we don't have an AppleTalk filter to begin with. 27446b857c22Sperseant * (Note that since IPF doesn't understand ARP it will pass *ALL* 27456b857c22Sperseant * ARP traffic.) 27466b857c22Sperseant */ 27476b857c22Sperseant switch (ether_type) { 27486b857c22Sperseant case ETHERTYPE_ARP: 27496b857c22Sperseant case ETHERTYPE_REVARP: 27506b857c22Sperseant return 0; /* Automatically pass */ 27516b857c22Sperseant case ETHERTYPE_IP: 27526b857c22Sperseant # ifdef INET6 27536b857c22Sperseant case ETHERTYPE_IPV6: 27546b857c22Sperseant # endif /* INET6 */ 27556b857c22Sperseant break; 27566b857c22Sperseant default: 27576b857c22Sperseant goto bad; 27586b857c22Sperseant } 27596b857c22Sperseant 276099807188Sjdc /* Strip off the Ethernet header and keep a copy. */ 276153524e44Schristos m_copydata(*mp, 0, ETHER_HDR_LEN, (void *) &eh2); 276299807188Sjdc m_adj(*mp, ETHER_HDR_LEN); 276399807188Sjdc 27646b857c22Sperseant /* Strip off snap header, if present */ 27656b857c22Sperseant if (snap) { 276653524e44Schristos m_copydata(*mp, 0, sizeof(struct llc), (void *) &llc1); 276799807188Sjdc m_adj(*mp, sizeof(struct llc)); 2768125ccd91Schristos } 27696b857c22Sperseant 27706b857c22Sperseant /* 277199807188Sjdc * Check basic packet sanity and run IPF through pfil. 27726b857c22Sperseant */ 27731fd1b496Sbouyer KASSERT(!cpu_intr_p()); 277499807188Sjdc switch (ether_type) 277599807188Sjdc { 277699807188Sjdc case ETHERTYPE_IP : 27779faa3310Schristos error = bridge_ip_checkbasic(mp); 277899807188Sjdc if (error == 0) 2779f04a92b1Srmind error = pfil_run_hooks(inet_pfil_hook, mp, ifp, dir); 278099807188Sjdc break; 27816b857c22Sperseant # ifdef INET6 278299807188Sjdc case ETHERTYPE_IPV6 : 27839faa3310Schristos error = bridge_ip6_checkbasic(mp); 278499807188Sjdc if (error == 0) 2785f04a92b1Srmind error = pfil_run_hooks(inet6_pfil_hook, mp, ifp, dir); 278699807188Sjdc break; 27876b857c22Sperseant # endif 278899807188Sjdc default : 278999807188Sjdc error = 0; 279099807188Sjdc break; 279199807188Sjdc } 279299807188Sjdc 279399807188Sjdc if (*mp == NULL) 279499807188Sjdc return error; 279599807188Sjdc if (error != 0) 279699807188Sjdc goto bad; 279799807188Sjdc 279899807188Sjdc error = -1; 27996b857c22Sperseant 28006b857c22Sperseant /* 28016b857c22Sperseant * Finally, put everything back the way it was and return 28026b857c22Sperseant */ 280332263965Sjdc if (snap) { 280499807188Sjdc M_PREPEND(*mp, sizeof(struct llc), M_DONTWAIT); 280599807188Sjdc if (*mp == NULL) 280699807188Sjdc return error; 280753524e44Schristos bcopy(&llc1, mtod(*mp, void *), sizeof(struct llc)); 280832263965Sjdc } 280999807188Sjdc 281099807188Sjdc M_PREPEND(*mp, ETHER_HDR_LEN, M_DONTWAIT); 281199807188Sjdc if (*mp == NULL) 281299807188Sjdc return error; 281353524e44Schristos bcopy(&eh2, mtod(*mp, void *), ETHER_HDR_LEN); 281432263965Sjdc 28156b857c22Sperseant return 0; 28166b857c22Sperseant 28176b857c22Sperseant bad: 28186b857c22Sperseant m_freem(*mp); 28196b857c22Sperseant *mp = NULL; 28206b857c22Sperseant return error; 28216b857c22Sperseant } 28226b857c22Sperseant 28236b857c22Sperseant /* 28246b857c22Sperseant * Perform basic checks on header size since 28256b857c22Sperseant * IPF assumes ip_input has already processed 28266b857c22Sperseant * it for it. Cut-and-pasted from ip_input.c. 28276b857c22Sperseant * Given how simple the IPv6 version is, 28286b857c22Sperseant * does the IPv4 version really need to be 28296b857c22Sperseant * this complicated? 28306b857c22Sperseant * 28316b857c22Sperseant * XXX Should we update ipstat here, or not? 28326b857c22Sperseant * XXX Right now we update ipstat but not 28336b857c22Sperseant * XXX csum_counter. 28346b857c22Sperseant */ 28356b857c22Sperseant static int 28366b857c22Sperseant bridge_ip_checkbasic(struct mbuf **mp) 28376b857c22Sperseant { 28386b857c22Sperseant struct mbuf *m = *mp; 28396b857c22Sperseant struct ip *ip; 28406b857c22Sperseant int len, hlen; 28416b857c22Sperseant 28426b857c22Sperseant if (*mp == NULL) 28436b857c22Sperseant return -1; 28446b857c22Sperseant 28452143da87Schristos if (M_GET_ALIGNED_HDR(&m, struct ip, true) != 0) { 28466b857c22Sperseant /* XXXJRT new stat, please */ 28477ff8d08aSthorpej ip_statinc(IP_STAT_TOOSMALL); 28486b857c22Sperseant goto bad; 28496b857c22Sperseant } 28506b857c22Sperseant ip = mtod(m, struct ip *); 28516b857c22Sperseant if (ip == NULL) goto bad; 28526b857c22Sperseant 28536b857c22Sperseant if (ip->ip_v != IPVERSION) { 28547ff8d08aSthorpej ip_statinc(IP_STAT_BADVERS); 28556b857c22Sperseant goto bad; 28566b857c22Sperseant } 28576b857c22Sperseant hlen = ip->ip_hl << 2; 28586b857c22Sperseant if (hlen < sizeof(struct ip)) { /* minimum header length */ 28597ff8d08aSthorpej ip_statinc(IP_STAT_BADHLEN); 28606b857c22Sperseant goto bad; 28616b857c22Sperseant } 28626b857c22Sperseant if (hlen > m->m_len) { 28636b857c22Sperseant if ((m = m_pullup(m, hlen)) == 0) { 28647ff8d08aSthorpej ip_statinc(IP_STAT_BADHLEN); 28656b857c22Sperseant goto bad; 28666b857c22Sperseant } 28676b857c22Sperseant ip = mtod(m, struct ip *); 28686b857c22Sperseant if (ip == NULL) goto bad; 28696b857c22Sperseant } 28706b857c22Sperseant 28716b857c22Sperseant switch (m->m_pkthdr.csum_flags & 2872fe6d4275Sozaki-r ((m_get_rcvif_NOMPSAFE(m)->if_csum_flags_rx & M_CSUM_IPv4) | 28736b857c22Sperseant M_CSUM_IPv4_BAD)) { 28746b857c22Sperseant case M_CSUM_IPv4|M_CSUM_IPv4_BAD: 28756b857c22Sperseant /* INET_CSUM_COUNTER_INCR(&ip_hwcsum_bad); */ 28766b857c22Sperseant goto bad; 28776b857c22Sperseant 28786b857c22Sperseant case M_CSUM_IPv4: 28796b857c22Sperseant /* Checksum was okay. */ 28806b857c22Sperseant /* INET_CSUM_COUNTER_INCR(&ip_hwcsum_ok); */ 28816b857c22Sperseant break; 28826b857c22Sperseant 28836b857c22Sperseant default: 28846b857c22Sperseant /* Must compute it ourselves. */ 28856b857c22Sperseant /* INET_CSUM_COUNTER_INCR(&ip_swcsum); */ 28866b857c22Sperseant if (in_cksum(m, hlen) != 0) 28876b857c22Sperseant goto bad; 28886b857c22Sperseant break; 28896b857c22Sperseant } 28906b857c22Sperseant 28916b857c22Sperseant /* Retrieve the packet length. */ 28926b857c22Sperseant len = ntohs(ip->ip_len); 28936b857c22Sperseant 28946b857c22Sperseant /* 28956b857c22Sperseant * Check for additional length bogosity 28966b857c22Sperseant */ 28976b857c22Sperseant if (len < hlen) { 28987ff8d08aSthorpej ip_statinc(IP_STAT_BADLEN); 28996b857c22Sperseant goto bad; 29006b857c22Sperseant } 29016b857c22Sperseant 29026b857c22Sperseant /* 29036b857c22Sperseant * Check that the amount of data in the buffers 29046b857c22Sperseant * is as at least much as the IP header would have us expect. 29056b857c22Sperseant * Drop packet if shorter than we expect. 29066b857c22Sperseant */ 29076b857c22Sperseant if (m->m_pkthdr.len < len) { 29087ff8d08aSthorpej ip_statinc(IP_STAT_TOOSHORT); 29096b857c22Sperseant goto bad; 29106b857c22Sperseant } 29116b857c22Sperseant 29126b857c22Sperseant /* Checks out, proceed */ 29136b857c22Sperseant *mp = m; 29146b857c22Sperseant return 0; 29156b857c22Sperseant 29166b857c22Sperseant bad: 29176b857c22Sperseant *mp = m; 29186b857c22Sperseant return -1; 29196b857c22Sperseant } 29206b857c22Sperseant 29216b857c22Sperseant # ifdef INET6 29226b857c22Sperseant /* 29236b857c22Sperseant * Same as above, but for IPv6. 29246b857c22Sperseant * Cut-and-pasted from ip6_input.c. 29256b857c22Sperseant * XXX Should we update ip6stat, or not? 29266b857c22Sperseant */ 29276b857c22Sperseant static int 29286b857c22Sperseant bridge_ip6_checkbasic(struct mbuf **mp) 29296b857c22Sperseant { 29306b857c22Sperseant struct mbuf *m = *mp; 2931e8e38ce5Sjdc struct ip6_hdr *ip6; 29326b857c22Sperseant 29336b857c22Sperseant /* 29346b857c22Sperseant * If the IPv6 header is not aligned, slurp it up into a new 29356b857c22Sperseant * mbuf with space for link headers, in the event we forward 29366b857c22Sperseant * it. Otherwise, if it is aligned, make sure the entire base 29376b857c22Sperseant * IPv6 header is in the first mbuf of the chain. 29386b857c22Sperseant */ 29392143da87Schristos if (M_GET_ALIGNED_HDR(&m, struct ip6_hdr, true) != 0) { 2940fe6d4275Sozaki-r struct ifnet *inifp = m_get_rcvif_NOMPSAFE(m); 29416b857c22Sperseant /* XXXJRT new stat, please */ 29420dd41b37Sthorpej ip6_statinc(IP6_STAT_TOOSMALL); 29436b857c22Sperseant in6_ifstat_inc(inifp, ifs6_in_hdrerr); 29446b857c22Sperseant goto bad; 29456b857c22Sperseant } 29466b857c22Sperseant 29476b857c22Sperseant ip6 = mtod(m, struct ip6_hdr *); 29486b857c22Sperseant 29496b857c22Sperseant if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { 29500dd41b37Sthorpej ip6_statinc(IP6_STAT_BADVERS); 2951fe6d4275Sozaki-r in6_ifstat_inc(m_get_rcvif_NOMPSAFE(m), ifs6_in_hdrerr); 29526b857c22Sperseant goto bad; 29536b857c22Sperseant } 29546b857c22Sperseant 29556b857c22Sperseant /* Checks out, proceed */ 29566b857c22Sperseant *mp = m; 29576b857c22Sperseant return 0; 29586b857c22Sperseant 29596b857c22Sperseant bad: 29606b857c22Sperseant *mp = m; 29616b857c22Sperseant return -1; 29626b857c22Sperseant } 29636b857c22Sperseant # endif /* INET6 */ 2964