xref: /netbsd-src/sys/net/if_bridge.c (revision c9e0a13b6e3d74db73a38ceca211df00b41debde)
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