xref: /titanic_51/usr/src/uts/common/io/bridge.c (revision 56a3cd3dcb3f5ee44668549e9ca623a435558fcd)
14eaa4710SRishi Srivatsavai /*
24eaa4710SRishi Srivatsavai  * CDDL HEADER START
34eaa4710SRishi Srivatsavai  *
44eaa4710SRishi Srivatsavai  * The contents of this file are subject to the terms of the
54eaa4710SRishi Srivatsavai  * Common Development and Distribution License (the "License").
64eaa4710SRishi Srivatsavai  * You may not use this file except in compliance with the License.
74eaa4710SRishi Srivatsavai  *
84eaa4710SRishi Srivatsavai  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94eaa4710SRishi Srivatsavai  * or http://www.opensolaris.org/os/licensing.
104eaa4710SRishi Srivatsavai  * See the License for the specific language governing permissions
114eaa4710SRishi Srivatsavai  * and limitations under the License.
124eaa4710SRishi Srivatsavai  *
134eaa4710SRishi Srivatsavai  * When distributing Covered Code, include this CDDL HEADER in each
144eaa4710SRishi Srivatsavai  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154eaa4710SRishi Srivatsavai  * If applicable, add the following below this CDDL HEADER, with the
164eaa4710SRishi Srivatsavai  * fields enclosed by brackets "[]" replaced with your own identifying
174eaa4710SRishi Srivatsavai  * information: Portions Copyright [yyyy] [name of copyright owner]
184eaa4710SRishi Srivatsavai  *
194eaa4710SRishi Srivatsavai  * CDDL HEADER END
204eaa4710SRishi Srivatsavai  */
214eaa4710SRishi Srivatsavai 
224eaa4710SRishi Srivatsavai /*
234eaa4710SRishi Srivatsavai  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
244eaa4710SRishi Srivatsavai  * Use is subject to license terms.
254eaa4710SRishi Srivatsavai  */
264eaa4710SRishi Srivatsavai 
274eaa4710SRishi Srivatsavai /*
284eaa4710SRishi Srivatsavai  * This module implements a STREAMS driver that provides layer-two (Ethernet)
294eaa4710SRishi Srivatsavai  * bridging functionality.  The STREAMS interface is used to provide
304eaa4710SRishi Srivatsavai  * observability (snoop/wireshark) and control, but not for interface plumbing.
314eaa4710SRishi Srivatsavai  */
324eaa4710SRishi Srivatsavai 
334eaa4710SRishi Srivatsavai #include <sys/types.h>
344eaa4710SRishi Srivatsavai #include <sys/bitmap.h>
354eaa4710SRishi Srivatsavai #include <sys/cmn_err.h>
364eaa4710SRishi Srivatsavai #include <sys/conf.h>
374eaa4710SRishi Srivatsavai #include <sys/ddi.h>
384eaa4710SRishi Srivatsavai #include <sys/errno.h>
394eaa4710SRishi Srivatsavai #include <sys/kstat.h>
404eaa4710SRishi Srivatsavai #include <sys/modctl.h>
414eaa4710SRishi Srivatsavai #include <sys/note.h>
424eaa4710SRishi Srivatsavai #include <sys/param.h>
434eaa4710SRishi Srivatsavai #include <sys/policy.h>
444eaa4710SRishi Srivatsavai #include <sys/sdt.h>
454eaa4710SRishi Srivatsavai #include <sys/stat.h>
464eaa4710SRishi Srivatsavai #include <sys/stream.h>
474eaa4710SRishi Srivatsavai #include <sys/stropts.h>
484eaa4710SRishi Srivatsavai #include <sys/strsun.h>
494eaa4710SRishi Srivatsavai #include <sys/sunddi.h>
504eaa4710SRishi Srivatsavai #include <sys/sysmacros.h>
514eaa4710SRishi Srivatsavai #include <sys/systm.h>
524eaa4710SRishi Srivatsavai #include <sys/time.h>
534eaa4710SRishi Srivatsavai #include <sys/dlpi.h>
544eaa4710SRishi Srivatsavai #include <sys/dls.h>
554eaa4710SRishi Srivatsavai #include <sys/mac_ether.h>
564eaa4710SRishi Srivatsavai #include <sys/mac_provider.h>
574eaa4710SRishi Srivatsavai #include <sys/mac_client_priv.h>
584eaa4710SRishi Srivatsavai #include <sys/mac_impl.h>
594eaa4710SRishi Srivatsavai #include <sys/vlan.h>
604eaa4710SRishi Srivatsavai #include <net/bridge.h>
614eaa4710SRishi Srivatsavai #include <net/bridge_impl.h>
624eaa4710SRishi Srivatsavai #include <net/trill.h>
63*56a3cd3dSRishi Srivatsavai #include <sys/dld_ioc.h>
644eaa4710SRishi Srivatsavai 
654eaa4710SRishi Srivatsavai /*
664eaa4710SRishi Srivatsavai  * Locks and reference counts: object lifetime and design.
674eaa4710SRishi Srivatsavai  *
684eaa4710SRishi Srivatsavai  * bridge_mac_t
694eaa4710SRishi Srivatsavai  *   Bridge mac (snoop) instances are in bmac_list, which is protected by
704eaa4710SRishi Srivatsavai  *   bmac_rwlock.  They're allocated by bmac_alloc and freed by bridge_timer().
714eaa4710SRishi Srivatsavai  *   Every bridge_inst_t has a single bridge_mac_t, but when bridge_inst_t goes
724eaa4710SRishi Srivatsavai  *   away, the bridge_mac_t remains until either all of the users go away
734eaa4710SRishi Srivatsavai  *   (detected by a timer) or until the instance is picked up again by the same
744eaa4710SRishi Srivatsavai  *   bridge starting back up.
754eaa4710SRishi Srivatsavai  *
764eaa4710SRishi Srivatsavai  * bridge_inst_t
774eaa4710SRishi Srivatsavai  *   Bridge instances are in inst_list, which is protected by inst_lock.
784eaa4710SRishi Srivatsavai  *   They're allocated by inst_alloc() and freed by inst_free().  After
794eaa4710SRishi Srivatsavai  *   allocation, an instance is placed in inst_list, and the reference count is
804eaa4710SRishi Srivatsavai  *   incremented to represent this.  That reference is decremented when the
814eaa4710SRishi Srivatsavai  *   BIF_SHUTDOWN flag is set, and no new increments may occur.  When the last
824eaa4710SRishi Srivatsavai  *   reference is freed, the instance is removed from the list.
834eaa4710SRishi Srivatsavai  *
844eaa4710SRishi Srivatsavai  *   Bridge instances have lists of links and an AVL tree of forwarding
854eaa4710SRishi Srivatsavai  *   entries.  Each of these structures holds one reference on the bridge
864eaa4710SRishi Srivatsavai  *   instance.  These lists and tree are protected by bi_rwlock.
874eaa4710SRishi Srivatsavai  *
884eaa4710SRishi Srivatsavai  * bridge_stream_t
894eaa4710SRishi Srivatsavai  *   Bridge streams are allocated by stream_alloc() and freed by stream_free().
904eaa4710SRishi Srivatsavai  *   These streams are created when "bridged" opens /dev/bridgectl, and are
914eaa4710SRishi Srivatsavai  *   used to create new bridge instances (via BRIOC_NEWBRIDGE) and control the
924eaa4710SRishi Srivatsavai  *   links on the bridge.  When a stream closes, the bridge instance created is
934eaa4710SRishi Srivatsavai  *   destroyed.  There's at most one bridge instance for a given control
944eaa4710SRishi Srivatsavai  *   stream.
954eaa4710SRishi Srivatsavai  *
964eaa4710SRishi Srivatsavai  * bridge_link_t
974eaa4710SRishi Srivatsavai  *   Links are allocated by bridge_add_link() and freed by link_free().  The
984eaa4710SRishi Srivatsavai  *   bi_links list holds a reference to the link.  When the BLF_DELETED flag is
994eaa4710SRishi Srivatsavai  *   set, that reference is dropped.  The link isn't removed from the list
1004eaa4710SRishi Srivatsavai  *   until the last reference drops.  Each forwarding entry that uses a given
1014eaa4710SRishi Srivatsavai  *   link holds a reference, as does each thread transmitting a packet via the
1024eaa4710SRishi Srivatsavai  *   link.  The MAC layer calls in via bridge_ref_cb() to hold a reference on
1034eaa4710SRishi Srivatsavai  *   a link when transmitting.
1044eaa4710SRishi Srivatsavai  *
1054eaa4710SRishi Srivatsavai  *   It's important that once BLF_DELETED is set, there's no way for the
1064eaa4710SRishi Srivatsavai  *   reference count to increase again.  If it can, then the link may be
1074eaa4710SRishi Srivatsavai  *   double-freed.  The BLF_FREED flag is intended for use with assertions to
1084eaa4710SRishi Srivatsavai  *   guard against this in testing.
1094eaa4710SRishi Srivatsavai  *
1104eaa4710SRishi Srivatsavai  * bridge_fwd_t
1114eaa4710SRishi Srivatsavai  *   Bridge forwarding entries are allocated by bridge_recv_cb() and freed by
1124eaa4710SRishi Srivatsavai  *   fwd_free().  The bi_fwd AVL tree holds one reference to the entry.  Unlike
1134eaa4710SRishi Srivatsavai  *   other data structures, the reference is dropped when the entry is removed
1144eaa4710SRishi Srivatsavai  *   from the tree by fwd_delete(), and the BFF_INTREE flag is removed.  Each
1154eaa4710SRishi Srivatsavai  *   thread that's forwarding a packet to a known destination holds a reference
1164eaa4710SRishi Srivatsavai  *   to a forwarding entry.
1174eaa4710SRishi Srivatsavai  *
1184eaa4710SRishi Srivatsavai  * TRILL notes:
1194eaa4710SRishi Srivatsavai  *
1204eaa4710SRishi Srivatsavai  *   The TRILL module does all of its I/O through bridging.  It uses references
1214eaa4710SRishi Srivatsavai  *   on the bridge_inst_t and bridge_link_t structures, and has seven entry
1224eaa4710SRishi Srivatsavai  *   points and four callbacks.  One entry point is for setting the callbacks
1234eaa4710SRishi Srivatsavai  *   (bridge_trill_register_cb).  There are four entry points for taking bridge
1244eaa4710SRishi Srivatsavai  *   and link references (bridge_trill_{br,ln}{ref,unref}).  The final two
1254eaa4710SRishi Srivatsavai  *   entry points are for decapsulated packets from TRILL (bridge_trill_decaps)
1264eaa4710SRishi Srivatsavai  *   that need to be bridged locally, and for TRILL-encapsulated output packets
1274eaa4710SRishi Srivatsavai  *   (bridge_trill_output).
1284eaa4710SRishi Srivatsavai  *
1294eaa4710SRishi Srivatsavai  *   The four callbacks comprise two notification functions for bridges and
1304eaa4710SRishi Srivatsavai  *   links being deleted, one function for raw received TRILL packets, and one
1314eaa4710SRishi Srivatsavai  *   for bridge output to non-local TRILL destinations (tunnel entry).
1324eaa4710SRishi Srivatsavai  */
1334eaa4710SRishi Srivatsavai 
1344eaa4710SRishi Srivatsavai /*
1354eaa4710SRishi Srivatsavai  * Ethernet reserved multicast addresses for TRILL; used also in TRILL module.
1364eaa4710SRishi Srivatsavai  */
1374eaa4710SRishi Srivatsavai const uint8_t all_isis_rbridges[] = ALL_ISIS_RBRIDGES;
1384eaa4710SRishi Srivatsavai static const uint8_t all_esadi_rbridges[] = ALL_ESADI_RBRIDGES;
1394eaa4710SRishi Srivatsavai const uint8_t bridge_group_address[] = BRIDGE_GROUP_ADDRESS;
1404eaa4710SRishi Srivatsavai 
1414eaa4710SRishi Srivatsavai static const char *inst_kstats_list[] = { KSINST_NAMES };
1424eaa4710SRishi Srivatsavai static const char *link_kstats_list[] = { KSLINK_NAMES };
1434eaa4710SRishi Srivatsavai 
1444eaa4710SRishi Srivatsavai #define	KREF(p, m, vn)	p->m.vn.value.ui64
1454eaa4710SRishi Srivatsavai #define	KINCR(p, m, vn)	++KREF(p, m, vn)
1464eaa4710SRishi Srivatsavai #define	KDECR(p, m, vn)	--KREF(p, m, vn)
1474eaa4710SRishi Srivatsavai 
1484eaa4710SRishi Srivatsavai #define	KIPINCR(p, vn)	KINCR(p, bi_kstats, vn)
1494eaa4710SRishi Srivatsavai #define	KIPDECR(p, vn)	KDECR(p, bi_kstats, vn)
1504eaa4710SRishi Srivatsavai #define	KLPINCR(p, vn)	KINCR(p, bl_kstats, vn)
1514eaa4710SRishi Srivatsavai 
1524eaa4710SRishi Srivatsavai #define	KIINCR(vn)	KIPINCR(bip, vn)
1534eaa4710SRishi Srivatsavai #define	KIDECR(vn)	KIPDECR(bip, vn)
1544eaa4710SRishi Srivatsavai #define	KLINCR(vn)	KLPINCR(blp, vn)
1554eaa4710SRishi Srivatsavai 
1564eaa4710SRishi Srivatsavai #define	Dim(x)		(sizeof (x) / sizeof (*(x)))
1574eaa4710SRishi Srivatsavai 
1584eaa4710SRishi Srivatsavai /* Amount of overhead added when encapsulating with VLAN headers */
1594eaa4710SRishi Srivatsavai #define	VLAN_INCR	(sizeof (struct ether_vlan_header) -	\
1604eaa4710SRishi Srivatsavai 			sizeof (struct ether_header))
1614eaa4710SRishi Srivatsavai 
1624eaa4710SRishi Srivatsavai static dev_info_t *bridge_dev_info;
1634eaa4710SRishi Srivatsavai static major_t bridge_major;
1644eaa4710SRishi Srivatsavai static ddi_taskq_t *bridge_taskq;
1654eaa4710SRishi Srivatsavai 
1664eaa4710SRishi Srivatsavai /*
1674eaa4710SRishi Srivatsavai  * These are the bridge instance management data structures.  The mutex lock
1684eaa4710SRishi Srivatsavai  * protects the list of bridge instances.  A reference count is then used on
1694eaa4710SRishi Srivatsavai  * each instance to determine when to free it.  We use mac_minor_hold() to
1704eaa4710SRishi Srivatsavai  * allocate minor_t values, which are used both for self-cloning /dev/net/
1714eaa4710SRishi Srivatsavai  * device nodes as well as client streams.  Minor node 0 is reserved for the
1724eaa4710SRishi Srivatsavai  * allocation control node.
1734eaa4710SRishi Srivatsavai  */
1744eaa4710SRishi Srivatsavai static list_t inst_list;
1754eaa4710SRishi Srivatsavai static kcondvar_t inst_cv;		/* Allows us to wait for shutdown */
1764eaa4710SRishi Srivatsavai static kmutex_t inst_lock;
1774eaa4710SRishi Srivatsavai 
1784eaa4710SRishi Srivatsavai static krwlock_t bmac_rwlock;
1794eaa4710SRishi Srivatsavai static list_t bmac_list;
1804eaa4710SRishi Srivatsavai 
1814eaa4710SRishi Srivatsavai /* Wait for taskq entries that use STREAMS */
1824eaa4710SRishi Srivatsavai static kcondvar_t stream_ref_cv;
1834eaa4710SRishi Srivatsavai static kmutex_t stream_ref_lock;
1844eaa4710SRishi Srivatsavai 
1854eaa4710SRishi Srivatsavai static timeout_id_t bridge_timerid;
1864eaa4710SRishi Srivatsavai static clock_t bridge_scan_interval;
1874eaa4710SRishi Srivatsavai static clock_t bridge_fwd_age;
1884eaa4710SRishi Srivatsavai 
1894eaa4710SRishi Srivatsavai static bridge_inst_t *bridge_find_name(const char *);
1904eaa4710SRishi Srivatsavai static void bridge_timer(void *);
1914eaa4710SRishi Srivatsavai static void bridge_unref(bridge_inst_t *);
1924eaa4710SRishi Srivatsavai 
1934eaa4710SRishi Srivatsavai static const uint8_t zero_addr[ETHERADDRL] = { 0 };
1944eaa4710SRishi Srivatsavai 
1954eaa4710SRishi Srivatsavai /* Global TRILL linkage */
1964eaa4710SRishi Srivatsavai static trill_recv_pkt_t trill_recv_fn;
1974eaa4710SRishi Srivatsavai static trill_encap_pkt_t trill_encap_fn;
1984eaa4710SRishi Srivatsavai static trill_br_dstr_t trill_brdstr_fn;
1994eaa4710SRishi Srivatsavai static trill_ln_dstr_t trill_lndstr_fn;
2004eaa4710SRishi Srivatsavai 
2014eaa4710SRishi Srivatsavai /* special settings to accommodate DLD flow control; see dld_str.c */
2024eaa4710SRishi Srivatsavai static struct module_info bridge_dld_modinfo = {
2034eaa4710SRishi Srivatsavai 	0,			/* mi_idnum */
2044eaa4710SRishi Srivatsavai 	"bridge",		/* mi_idname */
2054eaa4710SRishi Srivatsavai 	0,			/* mi_minpsz */
2064eaa4710SRishi Srivatsavai 	INFPSZ,			/* mi_maxpsz */
2074eaa4710SRishi Srivatsavai 	1,			/* mi_hiwat */
2084eaa4710SRishi Srivatsavai 	0			/* mi_lowat */
2094eaa4710SRishi Srivatsavai };
2104eaa4710SRishi Srivatsavai 
2114eaa4710SRishi Srivatsavai static struct qinit bridge_dld_rinit = {
2124eaa4710SRishi Srivatsavai 	NULL,			/* qi_putp */
2134eaa4710SRishi Srivatsavai 	NULL,			/* qi_srvp */
2144eaa4710SRishi Srivatsavai 	dld_open,		/* qi_qopen */
2154eaa4710SRishi Srivatsavai 	dld_close,		/* qi_qclose */
2164eaa4710SRishi Srivatsavai 	NULL,			/* qi_qadmin */
2174eaa4710SRishi Srivatsavai 	&bridge_dld_modinfo,	/* qi_minfo */
2184eaa4710SRishi Srivatsavai 	NULL			/* qi_mstat */
2194eaa4710SRishi Srivatsavai };
2204eaa4710SRishi Srivatsavai 
2214eaa4710SRishi Srivatsavai static struct qinit bridge_dld_winit = {
2224eaa4710SRishi Srivatsavai 	(int (*)())dld_wput,	/* qi_putp */
2234eaa4710SRishi Srivatsavai 	(int (*)())dld_wsrv,	/* qi_srvp */
2244eaa4710SRishi Srivatsavai 	NULL,			/* qi_qopen */
2254eaa4710SRishi Srivatsavai 	NULL,			/* qi_qclose */
2264eaa4710SRishi Srivatsavai 	NULL,			/* qi_qadmin */
2274eaa4710SRishi Srivatsavai 	&bridge_dld_modinfo,	/* qi_minfo */
2284eaa4710SRishi Srivatsavai 	NULL			/* qi_mstat */
2294eaa4710SRishi Srivatsavai };
2304eaa4710SRishi Srivatsavai 
2314eaa4710SRishi Srivatsavai static int bridge_ioc_listfwd(void *, intptr_t, int, cred_t *, int *);
2324eaa4710SRishi Srivatsavai 
2334eaa4710SRishi Srivatsavai /* GLDv3 control ioctls used by Bridging */
2344eaa4710SRishi Srivatsavai static dld_ioc_info_t bridge_ioc_list[] = {
2354eaa4710SRishi Srivatsavai 	{BRIDGE_IOC_LISTFWD, DLDCOPYINOUT, sizeof (bridge_listfwd_t),
2364eaa4710SRishi Srivatsavai 	    bridge_ioc_listfwd, NULL},
2374eaa4710SRishi Srivatsavai };
2384eaa4710SRishi Srivatsavai 
2394eaa4710SRishi Srivatsavai /*
2404eaa4710SRishi Srivatsavai  * Given a bridge mac pointer, get a ref-held pointer to the corresponding
2414eaa4710SRishi Srivatsavai  * bridge instance, if any.  We must hold the global bmac_rwlock so that
2424eaa4710SRishi Srivatsavai  * bm_inst doesn't slide out from under us.
2434eaa4710SRishi Srivatsavai  */
2444eaa4710SRishi Srivatsavai static bridge_inst_t *
2454eaa4710SRishi Srivatsavai mac_to_inst(const bridge_mac_t *bmp)
2464eaa4710SRishi Srivatsavai {
2474eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
2484eaa4710SRishi Srivatsavai 
2494eaa4710SRishi Srivatsavai 	rw_enter(&bmac_rwlock, RW_READER);
2504eaa4710SRishi Srivatsavai 	if ((bip = bmp->bm_inst) != NULL)
2514eaa4710SRishi Srivatsavai 		atomic_inc_uint(&bip->bi_refs);
2524eaa4710SRishi Srivatsavai 	rw_exit(&bmac_rwlock);
2534eaa4710SRishi Srivatsavai 	return (bip);
2544eaa4710SRishi Srivatsavai }
2554eaa4710SRishi Srivatsavai 
2564eaa4710SRishi Srivatsavai static void
2574eaa4710SRishi Srivatsavai link_sdu_fail(bridge_link_t *blp, boolean_t failed, mblk_t **mlist)
2584eaa4710SRishi Srivatsavai {
2594eaa4710SRishi Srivatsavai 	mblk_t *mp;
2604eaa4710SRishi Srivatsavai 	bridge_ctl_t *bcp;
2614eaa4710SRishi Srivatsavai 	bridge_link_t *blcmp;
2624eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
2634eaa4710SRishi Srivatsavai 	bridge_mac_t *bmp;
2644eaa4710SRishi Srivatsavai 
2654eaa4710SRishi Srivatsavai 	if (failed) {
2664eaa4710SRishi Srivatsavai 		if (blp->bl_flags & BLF_SDUFAIL)
2674eaa4710SRishi Srivatsavai 			return;
2684eaa4710SRishi Srivatsavai 		blp->bl_flags |= BLF_SDUFAIL;
2694eaa4710SRishi Srivatsavai 	} else {
2704eaa4710SRishi Srivatsavai 		if (!(blp->bl_flags & BLF_SDUFAIL))
2714eaa4710SRishi Srivatsavai 			return;
2724eaa4710SRishi Srivatsavai 		blp->bl_flags &= ~BLF_SDUFAIL;
2734eaa4710SRishi Srivatsavai 	}
2744eaa4710SRishi Srivatsavai 
2754eaa4710SRishi Srivatsavai 	/*
2764eaa4710SRishi Srivatsavai 	 * If this link is otherwise up, then check if there are any other
2774eaa4710SRishi Srivatsavai 	 * non-failed non-down links.  If not, then we control the state of the
2784eaa4710SRishi Srivatsavai 	 * whole bridge.
2794eaa4710SRishi Srivatsavai 	 */
2804eaa4710SRishi Srivatsavai 	bip = blp->bl_inst;
2814eaa4710SRishi Srivatsavai 	bmp = bip->bi_mac;
2824eaa4710SRishi Srivatsavai 	if (blp->bl_linkstate != LINK_STATE_DOWN) {
2834eaa4710SRishi Srivatsavai 		for (blcmp = list_head(&bip->bi_links); blcmp != NULL;
2844eaa4710SRishi Srivatsavai 		    blcmp = list_next(&bip->bi_links, blcmp)) {
2854eaa4710SRishi Srivatsavai 			if (blp != blcmp &&
2864eaa4710SRishi Srivatsavai 			    !(blcmp->bl_flags & (BLF_DELETED|BLF_SDUFAIL)) &&
2874eaa4710SRishi Srivatsavai 			    blcmp->bl_linkstate != LINK_STATE_DOWN)
2884eaa4710SRishi Srivatsavai 				break;
2894eaa4710SRishi Srivatsavai 		}
2904eaa4710SRishi Srivatsavai 		if (blcmp == NULL) {
2914eaa4710SRishi Srivatsavai 			bmp->bm_linkstate = failed ? LINK_STATE_DOWN :
2924eaa4710SRishi Srivatsavai 			    LINK_STATE_UP;
2934eaa4710SRishi Srivatsavai 			mac_link_redo(bmp->bm_mh, bmp->bm_linkstate);
2944eaa4710SRishi Srivatsavai 		}
2954eaa4710SRishi Srivatsavai 	}
2964eaa4710SRishi Srivatsavai 
2974eaa4710SRishi Srivatsavai 	/*
2984eaa4710SRishi Srivatsavai 	 * If we're becoming failed, then the link's current true state needs
2994eaa4710SRishi Srivatsavai 	 * to be reflected upwards to this link's clients.  If we're becoming
3004eaa4710SRishi Srivatsavai 	 * unfailed, then we get the state of the bridge instead on all
3014eaa4710SRishi Srivatsavai 	 * clients.
3024eaa4710SRishi Srivatsavai 	 */
3034eaa4710SRishi Srivatsavai 	if (failed) {
3044eaa4710SRishi Srivatsavai 		if (bmp->bm_linkstate != blp->bl_linkstate)
3054eaa4710SRishi Srivatsavai 			mac_link_redo(blp->bl_mh, blp->bl_linkstate);
3064eaa4710SRishi Srivatsavai 	} else {
3074eaa4710SRishi Srivatsavai 		mac_link_redo(blp->bl_mh, bmp->bm_linkstate);
3084eaa4710SRishi Srivatsavai 	}
3094eaa4710SRishi Srivatsavai 
3104eaa4710SRishi Srivatsavai 	/* get the current mblk we're going to send up */
3114eaa4710SRishi Srivatsavai 	if ((mp = blp->bl_lfailmp) == NULL &&
3124eaa4710SRishi Srivatsavai 	    (mp = allocb(sizeof (bridge_ctl_t), BPRI_MED)) == NULL)
3134eaa4710SRishi Srivatsavai 		return;
3144eaa4710SRishi Srivatsavai 
3154eaa4710SRishi Srivatsavai 	/* get a new one for next time */
3164eaa4710SRishi Srivatsavai 	blp->bl_lfailmp = allocb(sizeof (bridge_ctl_t), BPRI_MED);
3174eaa4710SRishi Srivatsavai 
3184eaa4710SRishi Srivatsavai 	/* if none for next time, then report only failures */
3194eaa4710SRishi Srivatsavai 	if (blp->bl_lfailmp == NULL && !failed) {
3204eaa4710SRishi Srivatsavai 		blp->bl_lfailmp = mp;
3214eaa4710SRishi Srivatsavai 		return;
3224eaa4710SRishi Srivatsavai 	}
3234eaa4710SRishi Srivatsavai 
3244eaa4710SRishi Srivatsavai 	/* LINTED: alignment */
3254eaa4710SRishi Srivatsavai 	bcp = (bridge_ctl_t *)mp->b_rptr;
3264eaa4710SRishi Srivatsavai 	bcp->bc_linkid = blp->bl_linkid;
3274eaa4710SRishi Srivatsavai 	bcp->bc_failed = failed;
3284eaa4710SRishi Srivatsavai 	mp->b_wptr = (uchar_t *)(bcp + 1);
3294eaa4710SRishi Srivatsavai 	mp->b_next = *mlist;
3304eaa4710SRishi Srivatsavai 	*mlist = mp;
3314eaa4710SRishi Srivatsavai }
3324eaa4710SRishi Srivatsavai 
3334eaa4710SRishi Srivatsavai /*
3344eaa4710SRishi Srivatsavai  * Send control messages (link SDU changes) using the stream to the
3354eaa4710SRishi Srivatsavai  * bridge instance daemon.
3364eaa4710SRishi Srivatsavai  */
3374eaa4710SRishi Srivatsavai static void
3384eaa4710SRishi Srivatsavai send_up_messages(bridge_inst_t *bip, mblk_t *mp)
3394eaa4710SRishi Srivatsavai {
3404eaa4710SRishi Srivatsavai 	mblk_t *mnext;
3414eaa4710SRishi Srivatsavai 	queue_t *rq;
3424eaa4710SRishi Srivatsavai 
3434eaa4710SRishi Srivatsavai 	rq = bip->bi_control->bs_wq;
3444eaa4710SRishi Srivatsavai 	rq = OTHERQ(rq);
3454eaa4710SRishi Srivatsavai 	while (mp != NULL) {
3464eaa4710SRishi Srivatsavai 		mnext = mp->b_next;
3474eaa4710SRishi Srivatsavai 		mp->b_next = NULL;
3484eaa4710SRishi Srivatsavai 		putnext(rq, mp);
3494eaa4710SRishi Srivatsavai 		mp = mnext;
3504eaa4710SRishi Srivatsavai 	}
3514eaa4710SRishi Srivatsavai }
3524eaa4710SRishi Srivatsavai 
3534eaa4710SRishi Srivatsavai /* ARGSUSED */
3544eaa4710SRishi Srivatsavai static int
3554eaa4710SRishi Srivatsavai bridge_m_getstat(void *arg, uint_t stat, uint64_t *val)
3564eaa4710SRishi Srivatsavai {
3574eaa4710SRishi Srivatsavai 	return (ENOTSUP);
3584eaa4710SRishi Srivatsavai }
3594eaa4710SRishi Srivatsavai 
3604eaa4710SRishi Srivatsavai static int
3614eaa4710SRishi Srivatsavai bridge_m_start(void *arg)
3624eaa4710SRishi Srivatsavai {
3634eaa4710SRishi Srivatsavai 	bridge_mac_t *bmp = arg;
3644eaa4710SRishi Srivatsavai 
3654eaa4710SRishi Srivatsavai 	bmp->bm_flags |= BMF_STARTED;
3664eaa4710SRishi Srivatsavai 	return (0);
3674eaa4710SRishi Srivatsavai }
3684eaa4710SRishi Srivatsavai 
3694eaa4710SRishi Srivatsavai static void
3704eaa4710SRishi Srivatsavai bridge_m_stop(void *arg)
3714eaa4710SRishi Srivatsavai {
3724eaa4710SRishi Srivatsavai 	bridge_mac_t *bmp = arg;
3734eaa4710SRishi Srivatsavai 
3744eaa4710SRishi Srivatsavai 	bmp->bm_flags &= ~BMF_STARTED;
3754eaa4710SRishi Srivatsavai }
3764eaa4710SRishi Srivatsavai 
3774eaa4710SRishi Srivatsavai /* ARGSUSED */
3784eaa4710SRishi Srivatsavai static int
3794eaa4710SRishi Srivatsavai bridge_m_setpromisc(void *arg, boolean_t on)
3804eaa4710SRishi Srivatsavai {
3814eaa4710SRishi Srivatsavai 	return (0);
3824eaa4710SRishi Srivatsavai }
3834eaa4710SRishi Srivatsavai 
3844eaa4710SRishi Srivatsavai /* ARGSUSED */
3854eaa4710SRishi Srivatsavai static int
3864eaa4710SRishi Srivatsavai bridge_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
3874eaa4710SRishi Srivatsavai {
3884eaa4710SRishi Srivatsavai 	return (0);
3894eaa4710SRishi Srivatsavai }
3904eaa4710SRishi Srivatsavai 
3914eaa4710SRishi Srivatsavai /* ARGSUSED */
3924eaa4710SRishi Srivatsavai static int
3934eaa4710SRishi Srivatsavai bridge_m_unicst(void *arg, const uint8_t *macaddr)
3944eaa4710SRishi Srivatsavai {
3954eaa4710SRishi Srivatsavai 	return (ENOTSUP);
3964eaa4710SRishi Srivatsavai }
3974eaa4710SRishi Srivatsavai 
3984eaa4710SRishi Srivatsavai static mblk_t *
3994eaa4710SRishi Srivatsavai bridge_m_tx(void *arg, mblk_t *mp)
4004eaa4710SRishi Srivatsavai {
4014eaa4710SRishi Srivatsavai 	_NOTE(ARGUNUSED(arg));
4024eaa4710SRishi Srivatsavai 	freemsgchain(mp);
4034eaa4710SRishi Srivatsavai 	return (NULL);
4044eaa4710SRishi Srivatsavai }
4054eaa4710SRishi Srivatsavai 
4064eaa4710SRishi Srivatsavai /* ARGSUSED */
4074eaa4710SRishi Srivatsavai static int
4084eaa4710SRishi Srivatsavai bridge_ioc_listfwd(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
4094eaa4710SRishi Srivatsavai {
4104eaa4710SRishi Srivatsavai 	bridge_listfwd_t *blf = karg;
4114eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
4124eaa4710SRishi Srivatsavai 	bridge_fwd_t *bfp, match;
4134eaa4710SRishi Srivatsavai 	avl_index_t where;
4144eaa4710SRishi Srivatsavai 
4154eaa4710SRishi Srivatsavai 	bip = bridge_find_name(blf->blf_name);
4164eaa4710SRishi Srivatsavai 	if (bip == NULL)
4174eaa4710SRishi Srivatsavai 		return (ENOENT);
4184eaa4710SRishi Srivatsavai 
4194eaa4710SRishi Srivatsavai 	bcopy(blf->blf_dest, match.bf_dest, ETHERADDRL);
4204eaa4710SRishi Srivatsavai 	match.bf_flags |= BFF_VLANLOCAL;
4214eaa4710SRishi Srivatsavai 	rw_enter(&bip->bi_rwlock, RW_READER);
4224eaa4710SRishi Srivatsavai 	if ((bfp = avl_find(&bip->bi_fwd, &match, &where)) == NULL)
4234eaa4710SRishi Srivatsavai 		bfp = avl_nearest(&bip->bi_fwd, where, AVL_AFTER);
4244eaa4710SRishi Srivatsavai 	else
4254eaa4710SRishi Srivatsavai 		bfp = AVL_NEXT(&bip->bi_fwd, bfp);
4264eaa4710SRishi Srivatsavai 	if (bfp == NULL) {
4274eaa4710SRishi Srivatsavai 		bzero(blf, sizeof (*blf));
4284eaa4710SRishi Srivatsavai 	} else {
4294eaa4710SRishi Srivatsavai 		bcopy(bfp->bf_dest, blf->blf_dest, ETHERADDRL);
4304eaa4710SRishi Srivatsavai 		blf->blf_trill_nick = bfp->bf_trill_nick;
4314eaa4710SRishi Srivatsavai 		blf->blf_ms_age =
4324eaa4710SRishi Srivatsavai 		    drv_hztousec(lbolt - bfp->bf_lastheard) / 1000;
4334eaa4710SRishi Srivatsavai 		blf->blf_is_local =
4344eaa4710SRishi Srivatsavai 		    (bfp->bf_flags & BFF_LOCALADDR) != 0;
4354eaa4710SRishi Srivatsavai 		blf->blf_linkid = bfp->bf_links[0]->bl_linkid;
4364eaa4710SRishi Srivatsavai 	}
4374eaa4710SRishi Srivatsavai 	rw_exit(&bip->bi_rwlock);
4384eaa4710SRishi Srivatsavai 	bridge_unref(bip);
4394eaa4710SRishi Srivatsavai 	return (0);
4404eaa4710SRishi Srivatsavai }
4414eaa4710SRishi Srivatsavai 
4424eaa4710SRishi Srivatsavai static int
4434eaa4710SRishi Srivatsavai bridge_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
4444eaa4710SRishi Srivatsavai     uint_t pr_valsize, const void *pr_val)
4454eaa4710SRishi Srivatsavai {
4464eaa4710SRishi Srivatsavai 	bridge_mac_t *bmp = arg;
4474eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
4484eaa4710SRishi Srivatsavai 	bridge_link_t *blp;
4494eaa4710SRishi Srivatsavai 	int err;
4504eaa4710SRishi Srivatsavai 	uint_t maxsdu;
4514eaa4710SRishi Srivatsavai 	mblk_t *mlist;
4524eaa4710SRishi Srivatsavai 
4534eaa4710SRishi Srivatsavai 	_NOTE(ARGUNUSED(pr_name));
4544eaa4710SRishi Srivatsavai 	switch (pr_num) {
4554eaa4710SRishi Srivatsavai 	case MAC_PROP_MTU:
4564eaa4710SRishi Srivatsavai 		if (pr_valsize < sizeof (bmp->bm_maxsdu)) {
4574eaa4710SRishi Srivatsavai 			err = EINVAL;
4584eaa4710SRishi Srivatsavai 			break;
4594eaa4710SRishi Srivatsavai 		}
4604eaa4710SRishi Srivatsavai 		(void) bcopy(pr_val, &maxsdu, sizeof (maxsdu));
4614eaa4710SRishi Srivatsavai 		if (maxsdu == bmp->bm_maxsdu) {
4624eaa4710SRishi Srivatsavai 			err = 0;
4634eaa4710SRishi Srivatsavai 		} else if ((bip = mac_to_inst(bmp)) == NULL) {
4644eaa4710SRishi Srivatsavai 			err = ENXIO;
4654eaa4710SRishi Srivatsavai 		} else {
4664eaa4710SRishi Srivatsavai 			rw_enter(&bip->bi_rwlock, RW_WRITER);
4674eaa4710SRishi Srivatsavai 			mlist = NULL;
4684eaa4710SRishi Srivatsavai 			for (blp = list_head(&bip->bi_links); blp != NULL;
4694eaa4710SRishi Srivatsavai 			    blp = list_next(&bip->bi_links, blp)) {
4704eaa4710SRishi Srivatsavai 				if (blp->bl_flags & BLF_DELETED)
4714eaa4710SRishi Srivatsavai 					continue;
4724eaa4710SRishi Srivatsavai 				if (blp->bl_maxsdu == maxsdu)
4734eaa4710SRishi Srivatsavai 					link_sdu_fail(blp, B_FALSE, &mlist);
4744eaa4710SRishi Srivatsavai 				else if (blp->bl_maxsdu == bmp->bm_maxsdu)
4754eaa4710SRishi Srivatsavai 					link_sdu_fail(blp, B_TRUE, &mlist);
4764eaa4710SRishi Srivatsavai 			}
4774eaa4710SRishi Srivatsavai 			rw_exit(&bip->bi_rwlock);
4784eaa4710SRishi Srivatsavai 			bmp->bm_maxsdu = maxsdu;
4794eaa4710SRishi Srivatsavai 			(void) mac_maxsdu_update(bmp->bm_mh, maxsdu);
4804eaa4710SRishi Srivatsavai 			send_up_messages(bip, mlist);
4814eaa4710SRishi Srivatsavai 			bridge_unref(bip);
4824eaa4710SRishi Srivatsavai 			err = 0;
4834eaa4710SRishi Srivatsavai 		}
4844eaa4710SRishi Srivatsavai 		break;
4854eaa4710SRishi Srivatsavai 
4864eaa4710SRishi Srivatsavai 	default:
4874eaa4710SRishi Srivatsavai 		err = ENOTSUP;
4884eaa4710SRishi Srivatsavai 		break;
4894eaa4710SRishi Srivatsavai 	}
4904eaa4710SRishi Srivatsavai 	return (err);
4914eaa4710SRishi Srivatsavai }
4924eaa4710SRishi Srivatsavai 
4934eaa4710SRishi Srivatsavai static int
4944eaa4710SRishi Srivatsavai bridge_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
4954eaa4710SRishi Srivatsavai     uint_t pr_flags, uint_t pr_valsize, void *pr_val, uint_t *perm)
4964eaa4710SRishi Srivatsavai {
4974eaa4710SRishi Srivatsavai 	bridge_mac_t *bmp = arg;
4984eaa4710SRishi Srivatsavai 	int err = 0;
4994eaa4710SRishi Srivatsavai 
5004eaa4710SRishi Srivatsavai 	_NOTE(ARGUNUSED(pr_name));
5014eaa4710SRishi Srivatsavai 	switch (pr_num) {
5024eaa4710SRishi Srivatsavai 	case MAC_PROP_MTU: {
5034eaa4710SRishi Srivatsavai 		mac_propval_range_t range;
5044eaa4710SRishi Srivatsavai 
5054eaa4710SRishi Srivatsavai 		if (!(pr_flags & MAC_PROP_POSSIBLE))
5064eaa4710SRishi Srivatsavai 			return (ENOTSUP);
5074eaa4710SRishi Srivatsavai 		if (pr_valsize < sizeof (mac_propval_range_t))
5084eaa4710SRishi Srivatsavai 			return (EINVAL);
5094eaa4710SRishi Srivatsavai 		range.mpr_count = 1;
5104eaa4710SRishi Srivatsavai 		range.mpr_type = MAC_PROPVAL_UINT32;
5114eaa4710SRishi Srivatsavai 		range.range_uint32[0].mpur_min =
5124eaa4710SRishi Srivatsavai 		    range.range_uint32[0].mpur_max = bmp->bm_maxsdu;
5134eaa4710SRishi Srivatsavai 		bcopy(&range, pr_val, sizeof (range));
5144eaa4710SRishi Srivatsavai 		*perm = MAC_PROP_PERM_RW;
5154eaa4710SRishi Srivatsavai 		break;
5164eaa4710SRishi Srivatsavai 	}
5174eaa4710SRishi Srivatsavai 	case MAC_PROP_STATUS:
5184eaa4710SRishi Srivatsavai 		if (pr_valsize < sizeof (bmp->bm_linkstate)) {
5194eaa4710SRishi Srivatsavai 			err = EINVAL;
5204eaa4710SRishi Srivatsavai 		} else {
5214eaa4710SRishi Srivatsavai 			bcopy(&bmp->bm_linkstate, pr_val,
5224eaa4710SRishi Srivatsavai 			    sizeof (&bmp->bm_linkstate));
5234eaa4710SRishi Srivatsavai 			*perm = MAC_PROP_PERM_READ;
5244eaa4710SRishi Srivatsavai 		}
5254eaa4710SRishi Srivatsavai 		break;
5264eaa4710SRishi Srivatsavai 
5274eaa4710SRishi Srivatsavai 	default:
5284eaa4710SRishi Srivatsavai 		err = ENOTSUP;
5294eaa4710SRishi Srivatsavai 		break;
5304eaa4710SRishi Srivatsavai 	}
5314eaa4710SRishi Srivatsavai 	return (err);
5324eaa4710SRishi Srivatsavai }
5334eaa4710SRishi Srivatsavai 
5344eaa4710SRishi Srivatsavai static mac_callbacks_t bridge_m_callbacks = {
5354eaa4710SRishi Srivatsavai 	MC_SETPROP | MC_GETPROP,
5364eaa4710SRishi Srivatsavai 	bridge_m_getstat,
5374eaa4710SRishi Srivatsavai 	bridge_m_start,
5384eaa4710SRishi Srivatsavai 	bridge_m_stop,
5394eaa4710SRishi Srivatsavai 	bridge_m_setpromisc,
5404eaa4710SRishi Srivatsavai 	bridge_m_multicst,
5414eaa4710SRishi Srivatsavai 	bridge_m_unicst,
5424eaa4710SRishi Srivatsavai 	bridge_m_tx,
5434eaa4710SRishi Srivatsavai 	NULL,	/* ioctl */
5444eaa4710SRishi Srivatsavai 	NULL,	/* getcapab */
5454eaa4710SRishi Srivatsavai 	NULL,	/* open */
5464eaa4710SRishi Srivatsavai 	NULL,	/* close */
5474eaa4710SRishi Srivatsavai 	bridge_m_setprop,
5484eaa4710SRishi Srivatsavai 	bridge_m_getprop
5494eaa4710SRishi Srivatsavai };
5504eaa4710SRishi Srivatsavai 
5514eaa4710SRishi Srivatsavai /*
5524eaa4710SRishi Srivatsavai  * Create kstats from a list.
5534eaa4710SRishi Srivatsavai  */
5544eaa4710SRishi Srivatsavai static kstat_t *
5554eaa4710SRishi Srivatsavai kstat_setup(kstat_named_t *knt, const char **names, int nstat,
5564eaa4710SRishi Srivatsavai     const char *unitname)
5574eaa4710SRishi Srivatsavai {
5584eaa4710SRishi Srivatsavai 	kstat_t *ksp;
5594eaa4710SRishi Srivatsavai 	int i;
5604eaa4710SRishi Srivatsavai 
5614eaa4710SRishi Srivatsavai 	for (i = 0; i < nstat; i++)
5624eaa4710SRishi Srivatsavai 		kstat_named_init(&knt[i], names[i], KSTAT_DATA_UINT64);
5634eaa4710SRishi Srivatsavai 
5644eaa4710SRishi Srivatsavai 	ksp = kstat_create_zone("bridge", 0, unitname, "net",
5654eaa4710SRishi Srivatsavai 	    KSTAT_TYPE_NAMED, nstat, KSTAT_FLAG_VIRTUAL, GLOBAL_ZONEID);
5664eaa4710SRishi Srivatsavai 	if (ksp != NULL) {
5674eaa4710SRishi Srivatsavai 		ksp->ks_data = knt;
5684eaa4710SRishi Srivatsavai 		kstat_install(ksp);
5694eaa4710SRishi Srivatsavai 	}
5704eaa4710SRishi Srivatsavai 	return (ksp);
5714eaa4710SRishi Srivatsavai }
5724eaa4710SRishi Srivatsavai 
5734eaa4710SRishi Srivatsavai /*
5744eaa4710SRishi Srivatsavai  * Find an existing bridge_mac_t structure or allocate a new one for the given
5754eaa4710SRishi Srivatsavai  * bridge instance.  This creates the mac driver instance that snoop can use.
5764eaa4710SRishi Srivatsavai  */
5774eaa4710SRishi Srivatsavai static int
5784eaa4710SRishi Srivatsavai bmac_alloc(bridge_inst_t *bip, bridge_mac_t **bmacp)
5794eaa4710SRishi Srivatsavai {
5804eaa4710SRishi Srivatsavai 	bridge_mac_t *bmp, *bnew;
5814eaa4710SRishi Srivatsavai 	mac_register_t *mac;
5824eaa4710SRishi Srivatsavai 	int err;
5834eaa4710SRishi Srivatsavai 
5844eaa4710SRishi Srivatsavai 	*bmacp = NULL;
5854eaa4710SRishi Srivatsavai 	if ((mac = mac_alloc(MAC_VERSION)) == NULL)
5864eaa4710SRishi Srivatsavai 		return (EINVAL);
5874eaa4710SRishi Srivatsavai 
5884eaa4710SRishi Srivatsavai 	bnew = kmem_zalloc(sizeof (*bnew), KM_SLEEP);
5894eaa4710SRishi Srivatsavai 
5904eaa4710SRishi Srivatsavai 	rw_enter(&bmac_rwlock, RW_WRITER);
5914eaa4710SRishi Srivatsavai 	for (bmp = list_head(&bmac_list); bmp != NULL;
5924eaa4710SRishi Srivatsavai 	    bmp = list_next(&bmac_list, bmp)) {
5934eaa4710SRishi Srivatsavai 		if (strcmp(bip->bi_name, bmp->bm_name) == 0) {
5944eaa4710SRishi Srivatsavai 			ASSERT(bmp->bm_inst == NULL);
5954eaa4710SRishi Srivatsavai 			bmp->bm_inst = bip;
5964eaa4710SRishi Srivatsavai 			rw_exit(&bmac_rwlock);
5974eaa4710SRishi Srivatsavai 			kmem_free(bnew, sizeof (*bnew));
5984eaa4710SRishi Srivatsavai 			mac_free(mac);
5994eaa4710SRishi Srivatsavai 			*bmacp = bmp;
6004eaa4710SRishi Srivatsavai 			return (0);
6014eaa4710SRishi Srivatsavai 		}
6024eaa4710SRishi Srivatsavai 	}
6034eaa4710SRishi Srivatsavai 
6044eaa4710SRishi Srivatsavai 	mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
6054eaa4710SRishi Srivatsavai 	mac->m_driver = bnew;
6064eaa4710SRishi Srivatsavai 	mac->m_dip = bridge_dev_info;
6074eaa4710SRishi Srivatsavai 	mac->m_instance = (uint_t)-1;
6084eaa4710SRishi Srivatsavai 	mac->m_src_addr = (uint8_t *)zero_addr;
6094eaa4710SRishi Srivatsavai 	mac->m_callbacks = &bridge_m_callbacks;
6104eaa4710SRishi Srivatsavai 
6114eaa4710SRishi Srivatsavai 	/*
6124eaa4710SRishi Srivatsavai 	 * Note that the SDU limits are irrelevant, as nobody transmits on the
6134eaa4710SRishi Srivatsavai 	 * bridge node itself.  It's mainly for monitoring but we allow
6144eaa4710SRishi Srivatsavai 	 * setting the bridge MTU for quick transition of all links part of the
6154eaa4710SRishi Srivatsavai 	 * bridge to a new MTU.
6164eaa4710SRishi Srivatsavai 	 */
6174eaa4710SRishi Srivatsavai 	mac->m_min_sdu = 1;
6184eaa4710SRishi Srivatsavai 	mac->m_max_sdu = 1500;
6194eaa4710SRishi Srivatsavai 	err = mac_register(mac, &bnew->bm_mh);
6204eaa4710SRishi Srivatsavai 	mac_free(mac);
6214eaa4710SRishi Srivatsavai 	if (err != 0) {
6224eaa4710SRishi Srivatsavai 		rw_exit(&bmac_rwlock);
6234eaa4710SRishi Srivatsavai 		kmem_free(bnew, sizeof (*bnew));
6244eaa4710SRishi Srivatsavai 		return (err);
6254eaa4710SRishi Srivatsavai 	}
6264eaa4710SRishi Srivatsavai 
6274eaa4710SRishi Srivatsavai 	bnew->bm_inst = bip;
6284eaa4710SRishi Srivatsavai 	(void) strcpy(bnew->bm_name, bip->bi_name);
6294eaa4710SRishi Srivatsavai 	if (list_is_empty(&bmac_list)) {
6304eaa4710SRishi Srivatsavai 		bridge_timerid = timeout(bridge_timer, NULL,
6314eaa4710SRishi Srivatsavai 		    bridge_scan_interval);
6324eaa4710SRishi Srivatsavai 	}
6334eaa4710SRishi Srivatsavai 	list_insert_tail(&bmac_list, bnew);
6344eaa4710SRishi Srivatsavai 	rw_exit(&bmac_rwlock);
6354eaa4710SRishi Srivatsavai 
6364eaa4710SRishi Srivatsavai 	/*
6374eaa4710SRishi Srivatsavai 	 * Mark the MAC as unable to go "active" so that only passive clients
6384eaa4710SRishi Srivatsavai 	 * (such as snoop) can bind to it.
6394eaa4710SRishi Srivatsavai 	 */
6404eaa4710SRishi Srivatsavai 	mac_no_active(bnew->bm_mh);
6414eaa4710SRishi Srivatsavai 	*bmacp = bnew;
6424eaa4710SRishi Srivatsavai 	return (0);
6434eaa4710SRishi Srivatsavai }
6444eaa4710SRishi Srivatsavai 
6454eaa4710SRishi Srivatsavai /*
6464eaa4710SRishi Srivatsavai  * Disconnect the given bridge_mac_t from its bridge instance.  The bridge
6474eaa4710SRishi Srivatsavai  * instance is going away.  The mac instance can't go away until the clients
6484eaa4710SRishi Srivatsavai  * are gone (see bridge_timer).
6494eaa4710SRishi Srivatsavai  */
6504eaa4710SRishi Srivatsavai static void
6514eaa4710SRishi Srivatsavai bmac_disconnect(bridge_mac_t *bmp)
6524eaa4710SRishi Srivatsavai {
6534eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
6544eaa4710SRishi Srivatsavai 
6554eaa4710SRishi Srivatsavai 	bmp->bm_linkstate = LINK_STATE_DOWN;
6564eaa4710SRishi Srivatsavai 	mac_link_redo(bmp->bm_mh, LINK_STATE_DOWN);
6574eaa4710SRishi Srivatsavai 
6584eaa4710SRishi Srivatsavai 	rw_enter(&bmac_rwlock, RW_READER);
6594eaa4710SRishi Srivatsavai 	bip = bmp->bm_inst;
6604eaa4710SRishi Srivatsavai 	bip->bi_mac = NULL;
6614eaa4710SRishi Srivatsavai 	bmp->bm_inst = NULL;
6624eaa4710SRishi Srivatsavai 	rw_exit(&bmac_rwlock);
6634eaa4710SRishi Srivatsavai }
6644eaa4710SRishi Srivatsavai 
6654eaa4710SRishi Srivatsavai /* This is used by the avl trees to sort forwarding table entries */
6664eaa4710SRishi Srivatsavai static int
6674eaa4710SRishi Srivatsavai fwd_compare(const void *addr1, const void *addr2)
6684eaa4710SRishi Srivatsavai {
6694eaa4710SRishi Srivatsavai 	const bridge_fwd_t *fwd1 = addr1;
6704eaa4710SRishi Srivatsavai 	const bridge_fwd_t *fwd2 = addr2;
6714eaa4710SRishi Srivatsavai 	int diff = memcmp(fwd1->bf_dest, fwd2->bf_dest, ETHERADDRL);
6724eaa4710SRishi Srivatsavai 
6734eaa4710SRishi Srivatsavai 	if (diff != 0)
6744eaa4710SRishi Srivatsavai 		return (diff > 0 ? 1 : -1);
6754eaa4710SRishi Srivatsavai 
6764eaa4710SRishi Srivatsavai 	if ((fwd1->bf_flags ^ fwd2->bf_flags) & BFF_VLANLOCAL) {
6774eaa4710SRishi Srivatsavai 		if (fwd1->bf_vlanid > fwd2->bf_vlanid)
6784eaa4710SRishi Srivatsavai 			return (1);
6794eaa4710SRishi Srivatsavai 		else if (fwd1->bf_vlanid < fwd2->bf_vlanid)
6804eaa4710SRishi Srivatsavai 			return (-1);
6814eaa4710SRishi Srivatsavai 	}
6824eaa4710SRishi Srivatsavai 	return (0);
6834eaa4710SRishi Srivatsavai }
6844eaa4710SRishi Srivatsavai 
6854eaa4710SRishi Srivatsavai static void
6864eaa4710SRishi Srivatsavai inst_free(bridge_inst_t *bip)
6874eaa4710SRishi Srivatsavai {
6884eaa4710SRishi Srivatsavai 	ASSERT(bip->bi_mac == NULL);
6894eaa4710SRishi Srivatsavai 	rw_destroy(&bip->bi_rwlock);
6904eaa4710SRishi Srivatsavai 	list_destroy(&bip->bi_links);
6914eaa4710SRishi Srivatsavai 	cv_destroy(&bip->bi_linkwait);
6924eaa4710SRishi Srivatsavai 	avl_destroy(&bip->bi_fwd);
6934eaa4710SRishi Srivatsavai 	if (bip->bi_ksp != NULL)
6944eaa4710SRishi Srivatsavai 		kstat_delete(bip->bi_ksp);
6954eaa4710SRishi Srivatsavai 	kmem_free(bip, sizeof (*bip));
6964eaa4710SRishi Srivatsavai }
6974eaa4710SRishi Srivatsavai 
6984eaa4710SRishi Srivatsavai static bridge_inst_t *
6994eaa4710SRishi Srivatsavai inst_alloc(const char *bridge)
7004eaa4710SRishi Srivatsavai {
7014eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
7024eaa4710SRishi Srivatsavai 
7034eaa4710SRishi Srivatsavai 	bip = kmem_zalloc(sizeof (*bip), KM_SLEEP);
7044eaa4710SRishi Srivatsavai 	bip->bi_refs = 1;
7054eaa4710SRishi Srivatsavai 	(void) strcpy(bip->bi_name, bridge);
7064eaa4710SRishi Srivatsavai 	rw_init(&bip->bi_rwlock, NULL, RW_DRIVER, NULL);
7074eaa4710SRishi Srivatsavai 	list_create(&bip->bi_links, sizeof (bridge_link_t),
7084eaa4710SRishi Srivatsavai 	    offsetof(bridge_link_t, bl_node));
7094eaa4710SRishi Srivatsavai 	cv_init(&bip->bi_linkwait, NULL, CV_DRIVER, NULL);
7104eaa4710SRishi Srivatsavai 	avl_create(&bip->bi_fwd, fwd_compare, sizeof (bridge_fwd_t),
7114eaa4710SRishi Srivatsavai 	    offsetof(bridge_fwd_t, bf_node));
7124eaa4710SRishi Srivatsavai 	return (bip);
7134eaa4710SRishi Srivatsavai }
7144eaa4710SRishi Srivatsavai 
7154eaa4710SRishi Srivatsavai static bridge_inst_t *
7164eaa4710SRishi Srivatsavai bridge_find_name(const char *bridge)
7174eaa4710SRishi Srivatsavai {
7184eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
7194eaa4710SRishi Srivatsavai 
7204eaa4710SRishi Srivatsavai 	mutex_enter(&inst_lock);
7214eaa4710SRishi Srivatsavai 	for (bip = list_head(&inst_list); bip != NULL;
7224eaa4710SRishi Srivatsavai 	    bip = list_next(&inst_list, bip)) {
7234eaa4710SRishi Srivatsavai 		if (!(bip->bi_flags & BIF_SHUTDOWN) &&
7244eaa4710SRishi Srivatsavai 		    strcmp(bridge, bip->bi_name) == 0) {
7254eaa4710SRishi Srivatsavai 			atomic_inc_uint(&bip->bi_refs);
7264eaa4710SRishi Srivatsavai 			break;
7274eaa4710SRishi Srivatsavai 		}
7284eaa4710SRishi Srivatsavai 	}
7294eaa4710SRishi Srivatsavai 	mutex_exit(&inst_lock);
7304eaa4710SRishi Srivatsavai 
7314eaa4710SRishi Srivatsavai 	return (bip);
7324eaa4710SRishi Srivatsavai }
7334eaa4710SRishi Srivatsavai 
7344eaa4710SRishi Srivatsavai static int
7354eaa4710SRishi Srivatsavai bridge_create(datalink_id_t linkid, const char *bridge, bridge_inst_t **bipc)
7364eaa4710SRishi Srivatsavai {
7374eaa4710SRishi Srivatsavai 	bridge_inst_t *bip, *bipnew;
7384eaa4710SRishi Srivatsavai 	bridge_mac_t *bmp = NULL;
7394eaa4710SRishi Srivatsavai 	int err;
7404eaa4710SRishi Srivatsavai 
7414eaa4710SRishi Srivatsavai 	*bipc = NULL;
7424eaa4710SRishi Srivatsavai 	bipnew = inst_alloc(bridge);
7434eaa4710SRishi Srivatsavai 
7444eaa4710SRishi Srivatsavai 	mutex_enter(&inst_lock);
7454eaa4710SRishi Srivatsavai lookup_retry:
7464eaa4710SRishi Srivatsavai 	for (bip = list_head(&inst_list); bip != NULL;
7474eaa4710SRishi Srivatsavai 	    bip = list_next(&inst_list, bip)) {
7484eaa4710SRishi Srivatsavai 		if (strcmp(bridge, bip->bi_name) == 0)
7494eaa4710SRishi Srivatsavai 			break;
7504eaa4710SRishi Srivatsavai 	}
7514eaa4710SRishi Srivatsavai 
7524eaa4710SRishi Srivatsavai 	/* This should not take long; if it does, we've got a design problem */
7534eaa4710SRishi Srivatsavai 	if (bip != NULL && (bip->bi_flags & BIF_SHUTDOWN)) {
7544eaa4710SRishi Srivatsavai 		cv_wait(&inst_cv, &inst_lock);
7554eaa4710SRishi Srivatsavai 		goto lookup_retry;
7564eaa4710SRishi Srivatsavai 	}
7574eaa4710SRishi Srivatsavai 
7584eaa4710SRishi Srivatsavai 	if (bip != NULL) {
7594eaa4710SRishi Srivatsavai 		/* We weren't expecting to find anything */
7604eaa4710SRishi Srivatsavai 		bip = NULL;
7614eaa4710SRishi Srivatsavai 		err = EEXIST;
7624eaa4710SRishi Srivatsavai 	} else {
7634eaa4710SRishi Srivatsavai 		bip = bipnew;
7644eaa4710SRishi Srivatsavai 		bipnew = NULL;
7654eaa4710SRishi Srivatsavai 		list_insert_tail(&inst_list, bip);
7664eaa4710SRishi Srivatsavai 	}
7674eaa4710SRishi Srivatsavai 
7684eaa4710SRishi Srivatsavai 	mutex_exit(&inst_lock);
7694eaa4710SRishi Srivatsavai 	if (bip == NULL)
7704eaa4710SRishi Srivatsavai 		goto fail;
7714eaa4710SRishi Srivatsavai 
7724eaa4710SRishi Srivatsavai 	bip->bi_ksp = kstat_setup((kstat_named_t *)&bip->bi_kstats,
7734eaa4710SRishi Srivatsavai 	    inst_kstats_list, Dim(inst_kstats_list), bip->bi_name);
7744eaa4710SRishi Srivatsavai 
7754eaa4710SRishi Srivatsavai 	err = bmac_alloc(bip, &bmp);
7764eaa4710SRishi Srivatsavai 	if ((bip->bi_mac = bmp) == NULL)
7774eaa4710SRishi Srivatsavai 		goto fail_create;
7784eaa4710SRishi Srivatsavai 
7794eaa4710SRishi Srivatsavai 	/*
7804eaa4710SRishi Srivatsavai 	 * bm_inst is set, so the timer cannot yank the DLS rug from under us.
7814eaa4710SRishi Srivatsavai 	 * No extra locking is needed here.
7824eaa4710SRishi Srivatsavai 	 */
7834eaa4710SRishi Srivatsavai 	if (!(bmp->bm_flags & BMF_DLS)) {
7844eaa4710SRishi Srivatsavai 		if ((err = dls_devnet_create(bmp->bm_mh, linkid)) != 0)
7854eaa4710SRishi Srivatsavai 			goto fail_create;
7864eaa4710SRishi Srivatsavai 		bmp->bm_flags |= BMF_DLS;
7874eaa4710SRishi Srivatsavai 	}
7884eaa4710SRishi Srivatsavai 
7894eaa4710SRishi Srivatsavai 	bip->bi_dev = makedevice(bridge_major, mac_minor(bmp->bm_mh));
7904eaa4710SRishi Srivatsavai 	*bipc = bip;
7914eaa4710SRishi Srivatsavai 	return (0);
7924eaa4710SRishi Srivatsavai 
7934eaa4710SRishi Srivatsavai fail_create:
7944eaa4710SRishi Srivatsavai 	if (bmp != NULL)
7954eaa4710SRishi Srivatsavai 		bmac_disconnect(bip->bi_mac);
7964eaa4710SRishi Srivatsavai 	bipnew = bip;
7974eaa4710SRishi Srivatsavai fail:
7984eaa4710SRishi Srivatsavai 	ASSERT(bipnew->bi_trilldata == NULL);
7994eaa4710SRishi Srivatsavai 	bipnew->bi_flags |= BIF_SHUTDOWN;
8004eaa4710SRishi Srivatsavai 	inst_free(bipnew);
8014eaa4710SRishi Srivatsavai 	return (err);
8024eaa4710SRishi Srivatsavai }
8034eaa4710SRishi Srivatsavai 
8044eaa4710SRishi Srivatsavai static void
8054eaa4710SRishi Srivatsavai bridge_unref(bridge_inst_t *bip)
8064eaa4710SRishi Srivatsavai {
8074eaa4710SRishi Srivatsavai 	if (atomic_dec_uint_nv(&bip->bi_refs) == 0) {
8084eaa4710SRishi Srivatsavai 		ASSERT(bip->bi_flags & BIF_SHUTDOWN);
8094eaa4710SRishi Srivatsavai 		/* free up mac for reuse before leaving global list */
8104eaa4710SRishi Srivatsavai 		if (bip->bi_mac != NULL)
8114eaa4710SRishi Srivatsavai 			bmac_disconnect(bip->bi_mac);
8124eaa4710SRishi Srivatsavai 		mutex_enter(&inst_lock);
8134eaa4710SRishi Srivatsavai 		list_remove(&inst_list, bip);
8144eaa4710SRishi Srivatsavai 		cv_broadcast(&inst_cv);
8154eaa4710SRishi Srivatsavai 		mutex_exit(&inst_lock);
8164eaa4710SRishi Srivatsavai 		inst_free(bip);
8174eaa4710SRishi Srivatsavai 	}
8184eaa4710SRishi Srivatsavai }
8194eaa4710SRishi Srivatsavai 
8204eaa4710SRishi Srivatsavai /*
8214eaa4710SRishi Srivatsavai  * Stream instances are used only for allocating bridges and serving as a
8224eaa4710SRishi Srivatsavai  * control node.  They serve no data-handling function.
8234eaa4710SRishi Srivatsavai  */
8244eaa4710SRishi Srivatsavai static bridge_stream_t *
8254eaa4710SRishi Srivatsavai stream_alloc(void)
8264eaa4710SRishi Srivatsavai {
8274eaa4710SRishi Srivatsavai 	bridge_stream_t *bsp;
8284eaa4710SRishi Srivatsavai 	minor_t mn;
8294eaa4710SRishi Srivatsavai 
8304eaa4710SRishi Srivatsavai 	if ((mn = mac_minor_hold(B_FALSE)) == 0)
8314eaa4710SRishi Srivatsavai 		return (NULL);
8324eaa4710SRishi Srivatsavai 	bsp = kmem_zalloc(sizeof (*bsp), KM_SLEEP);
8334eaa4710SRishi Srivatsavai 	bsp->bs_minor = mn;
8344eaa4710SRishi Srivatsavai 	return (bsp);
8354eaa4710SRishi Srivatsavai }
8364eaa4710SRishi Srivatsavai 
8374eaa4710SRishi Srivatsavai static void
8384eaa4710SRishi Srivatsavai stream_free(bridge_stream_t *bsp)
8394eaa4710SRishi Srivatsavai {
8404eaa4710SRishi Srivatsavai 	mac_minor_rele(bsp->bs_minor);
8414eaa4710SRishi Srivatsavai 	kmem_free(bsp, sizeof (*bsp));
8424eaa4710SRishi Srivatsavai }
8434eaa4710SRishi Srivatsavai 
8444eaa4710SRishi Srivatsavai /* Reference hold/release functions for STREAMS-related taskq */
8454eaa4710SRishi Srivatsavai static void
8464eaa4710SRishi Srivatsavai stream_ref(bridge_stream_t *bsp)
8474eaa4710SRishi Srivatsavai {
8484eaa4710SRishi Srivatsavai 	mutex_enter(&stream_ref_lock);
8494eaa4710SRishi Srivatsavai 	bsp->bs_taskq_cnt++;
8504eaa4710SRishi Srivatsavai 	mutex_exit(&stream_ref_lock);
8514eaa4710SRishi Srivatsavai }
8524eaa4710SRishi Srivatsavai 
8534eaa4710SRishi Srivatsavai static void
8544eaa4710SRishi Srivatsavai stream_unref(bridge_stream_t *bsp)
8554eaa4710SRishi Srivatsavai {
8564eaa4710SRishi Srivatsavai 	mutex_enter(&stream_ref_lock);
8574eaa4710SRishi Srivatsavai 	if (--bsp->bs_taskq_cnt == 0)
8584eaa4710SRishi Srivatsavai 		cv_broadcast(&stream_ref_cv);
8594eaa4710SRishi Srivatsavai 	mutex_exit(&stream_ref_lock);
8604eaa4710SRishi Srivatsavai }
8614eaa4710SRishi Srivatsavai 
8624eaa4710SRishi Srivatsavai static void
8634eaa4710SRishi Srivatsavai link_free(bridge_link_t *blp)
8644eaa4710SRishi Srivatsavai {
8654eaa4710SRishi Srivatsavai 	bridge_inst_t *bip = blp->bl_inst;
8664eaa4710SRishi Srivatsavai 
8674eaa4710SRishi Srivatsavai 	ASSERT(!(blp->bl_flags & BLF_FREED));
8684eaa4710SRishi Srivatsavai 	blp->bl_flags |= BLF_FREED;
8694eaa4710SRishi Srivatsavai 	if (blp->bl_ksp != NULL)
8704eaa4710SRishi Srivatsavai 		kstat_delete(blp->bl_ksp);
8714eaa4710SRishi Srivatsavai 	if (blp->bl_lfailmp != NULL)
8724eaa4710SRishi Srivatsavai 		freeb(blp->bl_lfailmp);
8734eaa4710SRishi Srivatsavai 	cv_destroy(&blp->bl_trillwait);
8744eaa4710SRishi Srivatsavai 	mutex_destroy(&blp->bl_trilllock);
8754eaa4710SRishi Srivatsavai 	kmem_free(blp, sizeof (*blp));
8764eaa4710SRishi Srivatsavai 	/* Don't unreference the bridge until the MAC is closed */
8774eaa4710SRishi Srivatsavai 	bridge_unref(bip);
8784eaa4710SRishi Srivatsavai }
8794eaa4710SRishi Srivatsavai 
8804eaa4710SRishi Srivatsavai static void
8814eaa4710SRishi Srivatsavai link_unref(bridge_link_t *blp)
8824eaa4710SRishi Srivatsavai {
8834eaa4710SRishi Srivatsavai 	if (atomic_dec_uint_nv(&blp->bl_refs) == 0) {
8844eaa4710SRishi Srivatsavai 		bridge_inst_t *bip = blp->bl_inst;
8854eaa4710SRishi Srivatsavai 
8864eaa4710SRishi Srivatsavai 		ASSERT(blp->bl_flags & BLF_DELETED);
8874eaa4710SRishi Srivatsavai 		rw_enter(&bip->bi_rwlock, RW_WRITER);
8884eaa4710SRishi Srivatsavai 		list_remove(&bip->bi_links, blp);
8894eaa4710SRishi Srivatsavai 		rw_exit(&bip->bi_rwlock);
8904eaa4710SRishi Srivatsavai 		if (bip->bi_trilldata != NULL && list_is_empty(&bip->bi_links))
8914eaa4710SRishi Srivatsavai 			cv_broadcast(&bip->bi_linkwait);
8924eaa4710SRishi Srivatsavai 		link_free(blp);
8934eaa4710SRishi Srivatsavai 	}
8944eaa4710SRishi Srivatsavai }
8954eaa4710SRishi Srivatsavai 
8964eaa4710SRishi Srivatsavai static bridge_fwd_t *
8974eaa4710SRishi Srivatsavai fwd_alloc(const uint8_t *addr, uint_t nlinks, uint16_t nick)
8984eaa4710SRishi Srivatsavai {
8994eaa4710SRishi Srivatsavai 	bridge_fwd_t *bfp;
9004eaa4710SRishi Srivatsavai 
9014eaa4710SRishi Srivatsavai 	bfp = kmem_zalloc(sizeof (*bfp) + (nlinks * sizeof (bridge_link_t *)),
9024eaa4710SRishi Srivatsavai 	    KM_NOSLEEP);
9034eaa4710SRishi Srivatsavai 	if (bfp != NULL) {
9044eaa4710SRishi Srivatsavai 		bcopy(addr, bfp->bf_dest, ETHERADDRL);
9054eaa4710SRishi Srivatsavai 		bfp->bf_lastheard = lbolt;
9064eaa4710SRishi Srivatsavai 		bfp->bf_maxlinks = nlinks;
9074eaa4710SRishi Srivatsavai 		bfp->bf_links = (bridge_link_t **)(bfp + 1);
9084eaa4710SRishi Srivatsavai 		bfp->bf_trill_nick = nick;
9094eaa4710SRishi Srivatsavai 	}
9104eaa4710SRishi Srivatsavai 	return (bfp);
9114eaa4710SRishi Srivatsavai }
9124eaa4710SRishi Srivatsavai 
9134eaa4710SRishi Srivatsavai static bridge_fwd_t *
9144eaa4710SRishi Srivatsavai fwd_find(bridge_inst_t *bip, const uint8_t *addr, uint16_t vlanid)
9154eaa4710SRishi Srivatsavai {
9164eaa4710SRishi Srivatsavai 	bridge_fwd_t *bfp, *vbfp;
9174eaa4710SRishi Srivatsavai 	bridge_fwd_t match;
9184eaa4710SRishi Srivatsavai 
9194eaa4710SRishi Srivatsavai 	bcopy(addr, match.bf_dest, ETHERADDRL);
9204eaa4710SRishi Srivatsavai 	match.bf_flags = 0;
9214eaa4710SRishi Srivatsavai 	rw_enter(&bip->bi_rwlock, RW_READER);
9224eaa4710SRishi Srivatsavai 	if ((bfp = avl_find(&bip->bi_fwd, &match, NULL)) != NULL) {
9234eaa4710SRishi Srivatsavai 		if (bfp->bf_vlanid != vlanid && bfp->bf_vcnt > 0) {
9244eaa4710SRishi Srivatsavai 			match.bf_vlanid = vlanid;
9254eaa4710SRishi Srivatsavai 			match.bf_flags = BFF_VLANLOCAL;
9264eaa4710SRishi Srivatsavai 			vbfp = avl_find(&bip->bi_fwd, &match, NULL);
9274eaa4710SRishi Srivatsavai 			if (vbfp != NULL)
9284eaa4710SRishi Srivatsavai 				bfp = vbfp;
9294eaa4710SRishi Srivatsavai 		}
9304eaa4710SRishi Srivatsavai 		atomic_inc_uint(&bfp->bf_refs);
9314eaa4710SRishi Srivatsavai 	}
9324eaa4710SRishi Srivatsavai 	rw_exit(&bip->bi_rwlock);
9334eaa4710SRishi Srivatsavai 	return (bfp);
9344eaa4710SRishi Srivatsavai }
9354eaa4710SRishi Srivatsavai 
9364eaa4710SRishi Srivatsavai static void
9374eaa4710SRishi Srivatsavai fwd_free(bridge_fwd_t *bfp)
9384eaa4710SRishi Srivatsavai {
9394eaa4710SRishi Srivatsavai 	uint_t i;
9404eaa4710SRishi Srivatsavai 	bridge_inst_t *bip = bfp->bf_links[0]->bl_inst;
9414eaa4710SRishi Srivatsavai 
9424eaa4710SRishi Srivatsavai 	KIDECR(bki_count);
9434eaa4710SRishi Srivatsavai 	for (i = 0; i < bfp->bf_nlinks; i++)
9444eaa4710SRishi Srivatsavai 		link_unref(bfp->bf_links[i]);
9454eaa4710SRishi Srivatsavai 	kmem_free(bfp,
9464eaa4710SRishi Srivatsavai 	    sizeof (*bfp) + bfp->bf_maxlinks * sizeof (bridge_link_t *));
9474eaa4710SRishi Srivatsavai }
9484eaa4710SRishi Srivatsavai 
9494eaa4710SRishi Srivatsavai static void
9504eaa4710SRishi Srivatsavai fwd_unref(bridge_fwd_t *bfp)
9514eaa4710SRishi Srivatsavai {
9524eaa4710SRishi Srivatsavai 	if (atomic_dec_uint_nv(&bfp->bf_refs) == 0) {
9534eaa4710SRishi Srivatsavai 		ASSERT(!(bfp->bf_flags & BFF_INTREE));
9544eaa4710SRishi Srivatsavai 		fwd_free(bfp);
9554eaa4710SRishi Srivatsavai 	}
9564eaa4710SRishi Srivatsavai }
9574eaa4710SRishi Srivatsavai 
9584eaa4710SRishi Srivatsavai static void
9594eaa4710SRishi Srivatsavai fwd_delete(bridge_fwd_t *bfp)
9604eaa4710SRishi Srivatsavai {
9614eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
9624eaa4710SRishi Srivatsavai 	bridge_fwd_t *bfpzero;
9634eaa4710SRishi Srivatsavai 
9644eaa4710SRishi Srivatsavai 	if (bfp->bf_flags & BFF_INTREE) {
9654eaa4710SRishi Srivatsavai 		ASSERT(bfp->bf_nlinks > 0);
9664eaa4710SRishi Srivatsavai 		bip = bfp->bf_links[0]->bl_inst;
9674eaa4710SRishi Srivatsavai 		rw_enter(&bip->bi_rwlock, RW_WRITER);
9684eaa4710SRishi Srivatsavai 		/* Another thread could beat us to this */
9694eaa4710SRishi Srivatsavai 		if (bfp->bf_flags & BFF_INTREE) {
9704eaa4710SRishi Srivatsavai 			avl_remove(&bip->bi_fwd, bfp);
9714eaa4710SRishi Srivatsavai 			bfp->bf_flags &= ~BFF_INTREE;
9724eaa4710SRishi Srivatsavai 			if (bfp->bf_flags & BFF_VLANLOCAL) {
9734eaa4710SRishi Srivatsavai 				bfp->bf_flags &= ~BFF_VLANLOCAL;
9744eaa4710SRishi Srivatsavai 				bfpzero = avl_find(&bip->bi_fwd, bfp, NULL);
9754eaa4710SRishi Srivatsavai 				if (bfpzero != NULL && bfpzero->bf_vcnt > 0)
9764eaa4710SRishi Srivatsavai 					bfpzero->bf_vcnt--;
9774eaa4710SRishi Srivatsavai 			}
9784eaa4710SRishi Srivatsavai 			rw_exit(&bip->bi_rwlock);
9794eaa4710SRishi Srivatsavai 			fwd_unref(bfp);		/* no longer in avl tree */
9804eaa4710SRishi Srivatsavai 		} else {
9814eaa4710SRishi Srivatsavai 			rw_exit(&bip->bi_rwlock);
9824eaa4710SRishi Srivatsavai 		}
9834eaa4710SRishi Srivatsavai 	}
9844eaa4710SRishi Srivatsavai }
9854eaa4710SRishi Srivatsavai 
9864eaa4710SRishi Srivatsavai static boolean_t
9874eaa4710SRishi Srivatsavai fwd_insert(bridge_inst_t *bip, bridge_fwd_t *bfp)
9884eaa4710SRishi Srivatsavai {
9894eaa4710SRishi Srivatsavai 	avl_index_t idx;
9904eaa4710SRishi Srivatsavai 	boolean_t retv;
9914eaa4710SRishi Srivatsavai 
9924eaa4710SRishi Srivatsavai 	rw_enter(&bip->bi_rwlock, RW_WRITER);
9934eaa4710SRishi Srivatsavai 	if (!(bip->bi_flags & BIF_SHUTDOWN) &&
9944eaa4710SRishi Srivatsavai 	    avl_numnodes(&bip->bi_fwd) < bip->bi_tablemax &&
9954eaa4710SRishi Srivatsavai 	    avl_find(&bip->bi_fwd, bfp, &idx) == NULL) {
9964eaa4710SRishi Srivatsavai 		avl_insert(&bip->bi_fwd, bfp, idx);
9974eaa4710SRishi Srivatsavai 		bfp->bf_flags |= BFF_INTREE;
9984eaa4710SRishi Srivatsavai 		atomic_inc_uint(&bfp->bf_refs);	/* avl entry */
9994eaa4710SRishi Srivatsavai 		retv = B_TRUE;
10004eaa4710SRishi Srivatsavai 	} else {
10014eaa4710SRishi Srivatsavai 		retv = B_FALSE;
10024eaa4710SRishi Srivatsavai 	}
10034eaa4710SRishi Srivatsavai 	rw_exit(&bip->bi_rwlock);
10044eaa4710SRishi Srivatsavai 	return (retv);
10054eaa4710SRishi Srivatsavai }
10064eaa4710SRishi Srivatsavai 
10074eaa4710SRishi Srivatsavai static void
10084eaa4710SRishi Srivatsavai fwd_update_local(bridge_link_t *blp, const uint8_t *oldaddr,
10094eaa4710SRishi Srivatsavai     const uint8_t *newaddr)
10104eaa4710SRishi Srivatsavai {
10114eaa4710SRishi Srivatsavai 	bridge_inst_t *bip = blp->bl_inst;
10124eaa4710SRishi Srivatsavai 	bridge_fwd_t *bfp, *bfnew;
10134eaa4710SRishi Srivatsavai 	bridge_fwd_t match;
10144eaa4710SRishi Srivatsavai 	avl_index_t idx;
10154eaa4710SRishi Srivatsavai 	boolean_t drop_ref = B_FALSE;
10164eaa4710SRishi Srivatsavai 
10174eaa4710SRishi Srivatsavai 	if (bcmp(oldaddr, newaddr, ETHERADDRL) == 0)
10184eaa4710SRishi Srivatsavai 		return;
10194eaa4710SRishi Srivatsavai 
10204eaa4710SRishi Srivatsavai 	if (bcmp(oldaddr, zero_addr, ETHERADDRL) == 0)
10214eaa4710SRishi Srivatsavai 		goto no_old_addr;
10224eaa4710SRishi Srivatsavai 
10234eaa4710SRishi Srivatsavai 	/*
10244eaa4710SRishi Srivatsavai 	 * Find the previous entry, and remove our link from it.
10254eaa4710SRishi Srivatsavai 	 */
10264eaa4710SRishi Srivatsavai 	bcopy(oldaddr, match.bf_dest, ETHERADDRL);
10274eaa4710SRishi Srivatsavai 	rw_enter(&bip->bi_rwlock, RW_WRITER);
10284eaa4710SRishi Srivatsavai 	if ((bfp = avl_find(&bip->bi_fwd, &match, NULL)) != NULL) {
10294eaa4710SRishi Srivatsavai 		int i;
10304eaa4710SRishi Srivatsavai 
10314eaa4710SRishi Srivatsavai 		/*
10324eaa4710SRishi Srivatsavai 		 * See if we're in the list, and remove if so.
10334eaa4710SRishi Srivatsavai 		 */
10344eaa4710SRishi Srivatsavai 		for (i = 0; i < bfp->bf_nlinks; i++) {
10354eaa4710SRishi Srivatsavai 			if (bfp->bf_links[i] == blp) {
10364eaa4710SRishi Srivatsavai 				/*
10374eaa4710SRishi Srivatsavai 				 * We assume writes are atomic, so no special
10384eaa4710SRishi Srivatsavai 				 * MT handling is needed.  The list length is
10394eaa4710SRishi Srivatsavai 				 * decremented first, and then we remove
10404eaa4710SRishi Srivatsavai 				 * entries.
10414eaa4710SRishi Srivatsavai 				 */
10424eaa4710SRishi Srivatsavai 				bfp->bf_nlinks--;
10434eaa4710SRishi Srivatsavai 				for (; i < bfp->bf_nlinks; i++)
10444eaa4710SRishi Srivatsavai 					bfp->bf_links[i] = bfp->bf_links[i + 1];
10454eaa4710SRishi Srivatsavai 				drop_ref = B_TRUE;
10464eaa4710SRishi Srivatsavai 				break;
10474eaa4710SRishi Srivatsavai 			}
10484eaa4710SRishi Srivatsavai 		}
10494eaa4710SRishi Srivatsavai 		/* If no more links, then remove and free up */
10504eaa4710SRishi Srivatsavai 		if (bfp->bf_nlinks == 0) {
10514eaa4710SRishi Srivatsavai 			avl_remove(&bip->bi_fwd, bfp);
10524eaa4710SRishi Srivatsavai 			bfp->bf_flags &= ~BFF_INTREE;
10534eaa4710SRishi Srivatsavai 		} else {
10544eaa4710SRishi Srivatsavai 			bfp = NULL;
10554eaa4710SRishi Srivatsavai 		}
10564eaa4710SRishi Srivatsavai 	}
10574eaa4710SRishi Srivatsavai 	rw_exit(&bip->bi_rwlock);
10584eaa4710SRishi Srivatsavai 	if (bfp != NULL)
10594eaa4710SRishi Srivatsavai 		fwd_unref(bfp);		/* no longer in avl tree */
10604eaa4710SRishi Srivatsavai 
10614eaa4710SRishi Srivatsavai 	/*
10624eaa4710SRishi Srivatsavai 	 * Now get the new link address and add this link to the list.  The
10634eaa4710SRishi Srivatsavai 	 * list should be of length 1 unless the user has configured multiple
10644eaa4710SRishi Srivatsavai 	 * NICs with the same address.  (That's an incorrect configuration, but
10654eaa4710SRishi Srivatsavai 	 * we support it anyway.)
10664eaa4710SRishi Srivatsavai 	 */
10674eaa4710SRishi Srivatsavai no_old_addr:
10684eaa4710SRishi Srivatsavai 	bfp = NULL;
10694eaa4710SRishi Srivatsavai 	if ((bip->bi_flags & BIF_SHUTDOWN) ||
10704eaa4710SRishi Srivatsavai 	    bcmp(newaddr, zero_addr, ETHERADDRL) == 0)
10714eaa4710SRishi Srivatsavai 		goto no_new_addr;
10724eaa4710SRishi Srivatsavai 
10734eaa4710SRishi Srivatsavai 	bcopy(newaddr, match.bf_dest, ETHERADDRL);
10744eaa4710SRishi Srivatsavai 	rw_enter(&bip->bi_rwlock, RW_WRITER);
10754eaa4710SRishi Srivatsavai 	if ((bfp = avl_find(&bip->bi_fwd, &match, &idx)) == NULL) {
10764eaa4710SRishi Srivatsavai 		bfnew = fwd_alloc(newaddr, 1, RBRIDGE_NICKNAME_NONE);
10774eaa4710SRishi Srivatsavai 		if (bfnew != NULL)
10784eaa4710SRishi Srivatsavai 			KIINCR(bki_count);
10794eaa4710SRishi Srivatsavai 	} else if (bfp->bf_nlinks < bfp->bf_maxlinks) {
10804eaa4710SRishi Srivatsavai 		/* special case: link fits in existing entry */
10814eaa4710SRishi Srivatsavai 		bfnew = bfp;
10824eaa4710SRishi Srivatsavai 	} else {
10834eaa4710SRishi Srivatsavai 		bfnew = fwd_alloc(newaddr, bfp->bf_nlinks + 1,
10844eaa4710SRishi Srivatsavai 		    RBRIDGE_NICKNAME_NONE);
10854eaa4710SRishi Srivatsavai 		if (bfnew != NULL) {
10864eaa4710SRishi Srivatsavai 			KIINCR(bki_count);
10874eaa4710SRishi Srivatsavai 			avl_remove(&bip->bi_fwd, bfp);
10884eaa4710SRishi Srivatsavai 			bfp->bf_flags &= ~BFF_INTREE;
10894eaa4710SRishi Srivatsavai 			bfnew->bf_nlinks = bfp->bf_nlinks;
10904eaa4710SRishi Srivatsavai 			bcopy(bfp->bf_links, bfnew->bf_links,
10914eaa4710SRishi Srivatsavai 			    bfp->bf_nlinks * sizeof (bfp));
10924eaa4710SRishi Srivatsavai 			/* reset the idx value due to removal above */
10934eaa4710SRishi Srivatsavai 			(void) avl_find(&bip->bi_fwd, &match, &idx);
10944eaa4710SRishi Srivatsavai 		}
10954eaa4710SRishi Srivatsavai 	}
10964eaa4710SRishi Srivatsavai 
10974eaa4710SRishi Srivatsavai 	if (bfnew != NULL) {
10984eaa4710SRishi Srivatsavai 		bfnew->bf_links[bfnew->bf_nlinks++] = blp;
10994eaa4710SRishi Srivatsavai 		if (drop_ref)
11004eaa4710SRishi Srivatsavai 			drop_ref = B_FALSE;
11014eaa4710SRishi Srivatsavai 		else
11024eaa4710SRishi Srivatsavai 			atomic_inc_uint(&blp->bl_refs);	/* bf_links entry */
11034eaa4710SRishi Srivatsavai 
11044eaa4710SRishi Srivatsavai 		if (bfnew != bfp) {
11054eaa4710SRishi Srivatsavai 			/* local addresses are not subject to table limits */
11064eaa4710SRishi Srivatsavai 			avl_insert(&bip->bi_fwd, bfnew, idx);
11074eaa4710SRishi Srivatsavai 			bfnew->bf_flags |= (BFF_INTREE | BFF_LOCALADDR);
11084eaa4710SRishi Srivatsavai 			atomic_inc_uint(&bfnew->bf_refs);	/* avl entry */
11094eaa4710SRishi Srivatsavai 		}
11104eaa4710SRishi Srivatsavai 	}
11114eaa4710SRishi Srivatsavai 	rw_exit(&bip->bi_rwlock);
11124eaa4710SRishi Srivatsavai 
11134eaa4710SRishi Srivatsavai no_new_addr:
11144eaa4710SRishi Srivatsavai 	/*
11154eaa4710SRishi Srivatsavai 	 * If we found an existing entry and we replaced it with a new one,
11164eaa4710SRishi Srivatsavai 	 * then drop the table reference from the old one.  We removed it from
11174eaa4710SRishi Srivatsavai 	 * the AVL tree above.
11184eaa4710SRishi Srivatsavai 	 */
11194eaa4710SRishi Srivatsavai 	if (bfnew != NULL && bfp != NULL && bfnew != bfp)
11204eaa4710SRishi Srivatsavai 		fwd_unref(bfp);
11214eaa4710SRishi Srivatsavai 
11224eaa4710SRishi Srivatsavai 	/* Account for removed entry. */
11234eaa4710SRishi Srivatsavai 	if (drop_ref)
11244eaa4710SRishi Srivatsavai 		link_unref(blp);
11254eaa4710SRishi Srivatsavai }
11264eaa4710SRishi Srivatsavai 
11274eaa4710SRishi Srivatsavai static void
11284eaa4710SRishi Srivatsavai bridge_new_unicst(bridge_link_t *blp)
11294eaa4710SRishi Srivatsavai {
11304eaa4710SRishi Srivatsavai 	uint8_t new_mac[ETHERADDRL];
11314eaa4710SRishi Srivatsavai 
11324eaa4710SRishi Srivatsavai 	mac_unicast_primary_get(blp->bl_mh, new_mac);
11334eaa4710SRishi Srivatsavai 	fwd_update_local(blp, blp->bl_local_mac, new_mac);
11344eaa4710SRishi Srivatsavai 	bcopy(new_mac, blp->bl_local_mac, ETHERADDRL);
11354eaa4710SRishi Srivatsavai }
11364eaa4710SRishi Srivatsavai 
11374eaa4710SRishi Srivatsavai /*
11384eaa4710SRishi Srivatsavai  * We must shut down a link prior to freeing it, and doing that requires
11394eaa4710SRishi Srivatsavai  * blocking to wait for running MAC threads while holding a reference.  This is
11404eaa4710SRishi Srivatsavai  * run from a taskq to accomplish proper link shutdown followed by reference
11414eaa4710SRishi Srivatsavai  * drop.
11424eaa4710SRishi Srivatsavai  */
11434eaa4710SRishi Srivatsavai static void
11444eaa4710SRishi Srivatsavai link_shutdown(void *arg)
11454eaa4710SRishi Srivatsavai {
11464eaa4710SRishi Srivatsavai 	bridge_link_t *blp = arg;
11474eaa4710SRishi Srivatsavai 	mac_handle_t mh = blp->bl_mh;
11484eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
11494eaa4710SRishi Srivatsavai 	bridge_fwd_t *bfp, *bfnext;
11504eaa4710SRishi Srivatsavai 	avl_tree_t fwd_scavenge;
11514eaa4710SRishi Srivatsavai 	int i;
11524eaa4710SRishi Srivatsavai 
11534eaa4710SRishi Srivatsavai 	/*
11544eaa4710SRishi Srivatsavai 	 * This link is being destroyed.  Notify TRILL now that it's no longer
11554eaa4710SRishi Srivatsavai 	 * possible to send packets.  Data packets may still arrive until TRILL
11564eaa4710SRishi Srivatsavai 	 * calls bridge_trill_lnunref.
11574eaa4710SRishi Srivatsavai 	 */
11584eaa4710SRishi Srivatsavai 	if (blp->bl_trilldata != NULL)
11594eaa4710SRishi Srivatsavai 		trill_lndstr_fn(blp->bl_trilldata, blp);
11604eaa4710SRishi Srivatsavai 
11614eaa4710SRishi Srivatsavai 	if (blp->bl_flags & BLF_PROM_ADDED)
11624eaa4710SRishi Srivatsavai 		(void) mac_promisc_remove(blp->bl_mphp);
11634eaa4710SRishi Srivatsavai 
11644eaa4710SRishi Srivatsavai 	if (blp->bl_flags & BLF_SET_BRIDGE)
11654eaa4710SRishi Srivatsavai 		mac_bridge_clear(mh, (mac_handle_t)blp);
11664eaa4710SRishi Srivatsavai 
11674eaa4710SRishi Srivatsavai 	if (blp->bl_flags & BLF_MARGIN_ADDED) {
11684249d844SRishi Srivatsavai 		(void) mac_notify_remove(blp->bl_mnh, B_TRUE);
11694eaa4710SRishi Srivatsavai 		(void) mac_margin_remove(mh, blp->bl_margin);
11704eaa4710SRishi Srivatsavai 	}
11714eaa4710SRishi Srivatsavai 
11724eaa4710SRishi Srivatsavai 	/* Tell the clients the real link state when we leave */
11734eaa4710SRishi Srivatsavai 	mac_link_redo(blp->bl_mh,
11744eaa4710SRishi Srivatsavai 	    mac_stat_get(blp->bl_mh, MAC_STAT_LOWLINK_STATE));
11754eaa4710SRishi Srivatsavai 
11764eaa4710SRishi Srivatsavai 	/* Destroy all of the forwarding entries related to this link */
11774eaa4710SRishi Srivatsavai 	avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t),
11784eaa4710SRishi Srivatsavai 	    offsetof(bridge_fwd_t, bf_node));
11794eaa4710SRishi Srivatsavai 	bip = blp->bl_inst;
11804eaa4710SRishi Srivatsavai 	rw_enter(&bip->bi_rwlock, RW_WRITER);
11814eaa4710SRishi Srivatsavai 	bfnext = avl_first(&bip->bi_fwd);
11824eaa4710SRishi Srivatsavai 	while ((bfp = bfnext) != NULL) {
11834eaa4710SRishi Srivatsavai 		bfnext = AVL_NEXT(&bip->bi_fwd, bfp);
11844eaa4710SRishi Srivatsavai 		for (i = 0; i < bfp->bf_nlinks; i++) {
11854eaa4710SRishi Srivatsavai 			if (bfp->bf_links[i] == blp)
11864eaa4710SRishi Srivatsavai 				break;
11874eaa4710SRishi Srivatsavai 		}
11884eaa4710SRishi Srivatsavai 		if (i >= bfp->bf_nlinks)
11894eaa4710SRishi Srivatsavai 			continue;
11904eaa4710SRishi Srivatsavai 		if (bfp->bf_nlinks > 1) {
11914eaa4710SRishi Srivatsavai 			/* note that this can't be the last reference */
11924eaa4710SRishi Srivatsavai 			link_unref(blp);
11934eaa4710SRishi Srivatsavai 			bfp->bf_nlinks--;
11944eaa4710SRishi Srivatsavai 			for (; i < bfp->bf_nlinks; i++)
11954eaa4710SRishi Srivatsavai 				bfp->bf_links[i] = bfp->bf_links[i + 1];
11964eaa4710SRishi Srivatsavai 		} else {
11974eaa4710SRishi Srivatsavai 			ASSERT(bfp->bf_flags & BFF_INTREE);
11984eaa4710SRishi Srivatsavai 			avl_remove(&bip->bi_fwd, bfp);
11994eaa4710SRishi Srivatsavai 			bfp->bf_flags &= ~BFF_INTREE;
12004eaa4710SRishi Srivatsavai 			avl_add(&fwd_scavenge, bfp);
12014eaa4710SRishi Srivatsavai 		}
12024eaa4710SRishi Srivatsavai 	}
12034eaa4710SRishi Srivatsavai 	rw_exit(&bip->bi_rwlock);
12044eaa4710SRishi Srivatsavai 	bfnext = avl_first(&fwd_scavenge);
12054eaa4710SRishi Srivatsavai 	while ((bfp = bfnext) != NULL) {
12064eaa4710SRishi Srivatsavai 		bfnext = AVL_NEXT(&fwd_scavenge, bfp);
12074eaa4710SRishi Srivatsavai 		avl_remove(&fwd_scavenge, bfp);
12084eaa4710SRishi Srivatsavai 		fwd_unref(bfp);
12094eaa4710SRishi Srivatsavai 	}
12104eaa4710SRishi Srivatsavai 	avl_destroy(&fwd_scavenge);
12114eaa4710SRishi Srivatsavai 
12124eaa4710SRishi Srivatsavai 	if (blp->bl_flags & BLF_CLIENT_OPEN)
12134eaa4710SRishi Srivatsavai 		mac_client_close(blp->bl_mch, 0);
12144eaa4710SRishi Srivatsavai 
12154eaa4710SRishi Srivatsavai 	mac_close(mh);
12164eaa4710SRishi Srivatsavai 
12174eaa4710SRishi Srivatsavai 	/*
12184eaa4710SRishi Srivatsavai 	 * We are now completely removed from the active list, so drop the
12194eaa4710SRishi Srivatsavai 	 * reference (see bridge_add_link).
12204eaa4710SRishi Srivatsavai 	 */
12214eaa4710SRishi Srivatsavai 	link_unref(blp);
12224eaa4710SRishi Srivatsavai }
12234eaa4710SRishi Srivatsavai 
12244eaa4710SRishi Srivatsavai static void
12254eaa4710SRishi Srivatsavai shutdown_inst(bridge_inst_t *bip)
12264eaa4710SRishi Srivatsavai {
12274eaa4710SRishi Srivatsavai 	bridge_link_t *blp, *blnext;
12284eaa4710SRishi Srivatsavai 	bridge_fwd_t *bfp;
12294eaa4710SRishi Srivatsavai 
12304eaa4710SRishi Srivatsavai 	mutex_enter(&inst_lock);
12314eaa4710SRishi Srivatsavai 	if (bip->bi_flags & BIF_SHUTDOWN) {
12324eaa4710SRishi Srivatsavai 		mutex_exit(&inst_lock);
12334eaa4710SRishi Srivatsavai 		return;
12344eaa4710SRishi Srivatsavai 	}
12354eaa4710SRishi Srivatsavai 
12364eaa4710SRishi Srivatsavai 	/*
12374eaa4710SRishi Srivatsavai 	 * Once on the inst_list, the bridge instance must not leave that list
12384eaa4710SRishi Srivatsavai 	 * without having the shutdown flag set first.  When the shutdown flag
12394eaa4710SRishi Srivatsavai 	 * is set, we own the list reference, so we must drop it before
12404eaa4710SRishi Srivatsavai 	 * returning.
12414eaa4710SRishi Srivatsavai 	 */
12424eaa4710SRishi Srivatsavai 	bip->bi_flags |= BIF_SHUTDOWN;
12434eaa4710SRishi Srivatsavai 	mutex_exit(&inst_lock);
12444eaa4710SRishi Srivatsavai 
12454eaa4710SRishi Srivatsavai 	bip->bi_control = NULL;
12464eaa4710SRishi Srivatsavai 
12474eaa4710SRishi Srivatsavai 	rw_enter(&bip->bi_rwlock, RW_READER);
12484eaa4710SRishi Srivatsavai 	blnext = list_head(&bip->bi_links);
12494eaa4710SRishi Srivatsavai 	while ((blp = blnext) != NULL) {
12504eaa4710SRishi Srivatsavai 		blnext = list_next(&bip->bi_links, blp);
12514eaa4710SRishi Srivatsavai 		if (!(blp->bl_flags & BLF_DELETED)) {
12524eaa4710SRishi Srivatsavai 			blp->bl_flags |= BLF_DELETED;
12534eaa4710SRishi Srivatsavai 			(void) ddi_taskq_dispatch(bridge_taskq, link_shutdown,
12544eaa4710SRishi Srivatsavai 			    blp, DDI_SLEEP);
12554eaa4710SRishi Srivatsavai 		}
12564eaa4710SRishi Srivatsavai 	}
12574eaa4710SRishi Srivatsavai 	while ((bfp = avl_first(&bip->bi_fwd)) != NULL) {
12584eaa4710SRishi Srivatsavai 		atomic_inc_uint(&bfp->bf_refs);
12594eaa4710SRishi Srivatsavai 		rw_exit(&bip->bi_rwlock);
12604eaa4710SRishi Srivatsavai 		fwd_delete(bfp);
12614eaa4710SRishi Srivatsavai 		fwd_unref(bfp);
12624eaa4710SRishi Srivatsavai 		rw_enter(&bip->bi_rwlock, RW_READER);
12634eaa4710SRishi Srivatsavai 	}
12644eaa4710SRishi Srivatsavai 	rw_exit(&bip->bi_rwlock);
12654eaa4710SRishi Srivatsavai 
12664eaa4710SRishi Srivatsavai 	/*
12674eaa4710SRishi Srivatsavai 	 * This bridge is being destroyed.  Notify TRILL once all of the
12684eaa4710SRishi Srivatsavai 	 * links are all gone.
12694eaa4710SRishi Srivatsavai 	 */
12704eaa4710SRishi Srivatsavai 	mutex_enter(&inst_lock);
12714eaa4710SRishi Srivatsavai 	while (bip->bi_trilldata != NULL && !list_is_empty(&bip->bi_links))
12724eaa4710SRishi Srivatsavai 		cv_wait(&bip->bi_linkwait, &inst_lock);
12734eaa4710SRishi Srivatsavai 	mutex_exit(&inst_lock);
12744eaa4710SRishi Srivatsavai 	if (bip->bi_trilldata != NULL)
12754eaa4710SRishi Srivatsavai 		trill_brdstr_fn(bip->bi_trilldata, bip);
12764eaa4710SRishi Srivatsavai 
12774eaa4710SRishi Srivatsavai 	bridge_unref(bip);
12784eaa4710SRishi Srivatsavai }
12794eaa4710SRishi Srivatsavai 
12804eaa4710SRishi Srivatsavai /*
12814eaa4710SRishi Srivatsavai  * This is called once by the TRILL module when it starts up.  It just sets the
12824eaa4710SRishi Srivatsavai  * global TRILL callback function pointers -- data transmit/receive and bridge
12834eaa4710SRishi Srivatsavai  * and link destroy notification.  There's only one TRILL module, so only one
12844eaa4710SRishi Srivatsavai  * registration is needed.
12854eaa4710SRishi Srivatsavai  *
12864eaa4710SRishi Srivatsavai  * TRILL should call this function with NULL pointers before unloading.  It
12874eaa4710SRishi Srivatsavai  * must not do so before dropping all references to bridges and links.  We
12884eaa4710SRishi Srivatsavai  * assert that this is true on debug builds.
12894eaa4710SRishi Srivatsavai  */
12904eaa4710SRishi Srivatsavai void
12914eaa4710SRishi Srivatsavai bridge_trill_register_cb(trill_recv_pkt_t recv_fn, trill_encap_pkt_t encap_fn,
12924eaa4710SRishi Srivatsavai     trill_br_dstr_t brdstr_fn, trill_ln_dstr_t lndstr_fn)
12934eaa4710SRishi Srivatsavai {
12944eaa4710SRishi Srivatsavai #ifdef DEBUG
12954eaa4710SRishi Srivatsavai 	if (recv_fn == NULL && trill_recv_fn != NULL) {
12964eaa4710SRishi Srivatsavai 		bridge_inst_t *bip;
12974eaa4710SRishi Srivatsavai 		bridge_link_t *blp;
12984eaa4710SRishi Srivatsavai 
12994eaa4710SRishi Srivatsavai 		mutex_enter(&inst_lock);
13004eaa4710SRishi Srivatsavai 		for (bip = list_head(&inst_list); bip != NULL;
13014eaa4710SRishi Srivatsavai 		    bip = list_next(&inst_list, bip)) {
13024eaa4710SRishi Srivatsavai 			ASSERT(bip->bi_trilldata == NULL);
13034eaa4710SRishi Srivatsavai 			rw_enter(&bip->bi_rwlock, RW_READER);
13044eaa4710SRishi Srivatsavai 			for (blp = list_head(&bip->bi_links); blp != NULL;
13054eaa4710SRishi Srivatsavai 			    blp = list_next(&bip->bi_links, blp)) {
13064eaa4710SRishi Srivatsavai 				ASSERT(blp->bl_trilldata == NULL);
13074eaa4710SRishi Srivatsavai 			}
13084eaa4710SRishi Srivatsavai 			rw_exit(&bip->bi_rwlock);
13094eaa4710SRishi Srivatsavai 		}
13104eaa4710SRishi Srivatsavai 		mutex_exit(&inst_lock);
13114eaa4710SRishi Srivatsavai 	}
13124eaa4710SRishi Srivatsavai #endif
13134eaa4710SRishi Srivatsavai 	trill_recv_fn = recv_fn;
13144eaa4710SRishi Srivatsavai 	trill_encap_fn = encap_fn;
13154eaa4710SRishi Srivatsavai 	trill_brdstr_fn = brdstr_fn;
13164eaa4710SRishi Srivatsavai 	trill_lndstr_fn = lndstr_fn;
13174eaa4710SRishi Srivatsavai }
13184eaa4710SRishi Srivatsavai 
13194eaa4710SRishi Srivatsavai /*
13204eaa4710SRishi Srivatsavai  * This registers the TRILL instance pointer with a bridge.  Before this
13214eaa4710SRishi Srivatsavai  * pointer is set, the forwarding, TRILL receive, and bridge destructor
13224eaa4710SRishi Srivatsavai  * functions won't be called.
13234eaa4710SRishi Srivatsavai  *
13244eaa4710SRishi Srivatsavai  * TRILL holds a reference on a bridge with this call.  It must free the
13254eaa4710SRishi Srivatsavai  * reference by calling the unregister function below.
13264eaa4710SRishi Srivatsavai  */
13274eaa4710SRishi Srivatsavai bridge_inst_t *
13284eaa4710SRishi Srivatsavai bridge_trill_brref(const char *bname, void *ptr)
13294eaa4710SRishi Srivatsavai {
13304eaa4710SRishi Srivatsavai 	char bridge[MAXLINKNAMELEN];
13314eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
13324eaa4710SRishi Srivatsavai 
13334eaa4710SRishi Srivatsavai 	(void) snprintf(bridge, MAXLINKNAMELEN, "%s0", bname);
13344eaa4710SRishi Srivatsavai 	bip = bridge_find_name(bridge);
13354eaa4710SRishi Srivatsavai 	if (bip != NULL) {
13364eaa4710SRishi Srivatsavai 		ASSERT(bip->bi_trilldata == NULL && ptr != NULL);
13374eaa4710SRishi Srivatsavai 		bip->bi_trilldata = ptr;
13384eaa4710SRishi Srivatsavai 	}
13394eaa4710SRishi Srivatsavai 	return (bip);
13404eaa4710SRishi Srivatsavai }
13414eaa4710SRishi Srivatsavai 
13424eaa4710SRishi Srivatsavai void
13434eaa4710SRishi Srivatsavai bridge_trill_brunref(bridge_inst_t *bip)
13444eaa4710SRishi Srivatsavai {
13454eaa4710SRishi Srivatsavai 	ASSERT(bip->bi_trilldata != NULL);
13464eaa4710SRishi Srivatsavai 	bip->bi_trilldata = NULL;
13474eaa4710SRishi Srivatsavai 	bridge_unref(bip);
13484eaa4710SRishi Srivatsavai }
13494eaa4710SRishi Srivatsavai 
13504eaa4710SRishi Srivatsavai /*
13514eaa4710SRishi Srivatsavai  * TRILL calls this function when referencing a particular link on a bridge.
13524eaa4710SRishi Srivatsavai  *
13534eaa4710SRishi Srivatsavai  * It holds a reference on the link, so TRILL must clear out the reference when
13544eaa4710SRishi Srivatsavai  * it's done with the link (on unbinding).
13554eaa4710SRishi Srivatsavai  */
13564eaa4710SRishi Srivatsavai bridge_link_t *
13574eaa4710SRishi Srivatsavai bridge_trill_lnref(bridge_inst_t *bip, datalink_id_t linkid, void *ptr)
13584eaa4710SRishi Srivatsavai {
13594eaa4710SRishi Srivatsavai 	bridge_link_t *blp;
13604eaa4710SRishi Srivatsavai 
13614eaa4710SRishi Srivatsavai 	ASSERT(ptr != NULL);
13624eaa4710SRishi Srivatsavai 	rw_enter(&bip->bi_rwlock, RW_READER);
13634eaa4710SRishi Srivatsavai 	for (blp = list_head(&bip->bi_links); blp != NULL;
13644eaa4710SRishi Srivatsavai 	    blp = list_next(&bip->bi_links, blp)) {
13654eaa4710SRishi Srivatsavai 		if (!(blp->bl_flags & BLF_DELETED) &&
13664eaa4710SRishi Srivatsavai 		    blp->bl_linkid == linkid && blp->bl_trilldata == NULL) {
13674eaa4710SRishi Srivatsavai 			blp->bl_trilldata = ptr;
13684eaa4710SRishi Srivatsavai 			blp->bl_flags &= ~BLF_TRILLACTIVE;
13694eaa4710SRishi Srivatsavai 			(void) memset(blp->bl_afs, 0, sizeof (blp->bl_afs));
13704eaa4710SRishi Srivatsavai 			atomic_inc_uint(&blp->bl_refs);
13714eaa4710SRishi Srivatsavai 			break;
13724eaa4710SRishi Srivatsavai 		}
13734eaa4710SRishi Srivatsavai 	}
13744eaa4710SRishi Srivatsavai 	rw_exit(&bip->bi_rwlock);
13754eaa4710SRishi Srivatsavai 	return (blp);
13764eaa4710SRishi Srivatsavai }
13774eaa4710SRishi Srivatsavai 
13784eaa4710SRishi Srivatsavai void
13794eaa4710SRishi Srivatsavai bridge_trill_lnunref(bridge_link_t *blp)
13804eaa4710SRishi Srivatsavai {
13814eaa4710SRishi Srivatsavai 	mutex_enter(&blp->bl_trilllock);
13824eaa4710SRishi Srivatsavai 	ASSERT(blp->bl_trilldata != NULL);
13834eaa4710SRishi Srivatsavai 	blp->bl_trilldata = NULL;
13844eaa4710SRishi Srivatsavai 	blp->bl_flags &= ~BLF_TRILLACTIVE;
13854eaa4710SRishi Srivatsavai 	while (blp->bl_trillthreads > 0)
13864eaa4710SRishi Srivatsavai 		cv_wait(&blp->bl_trillwait, &blp->bl_trilllock);
13874eaa4710SRishi Srivatsavai 	mutex_exit(&blp->bl_trilllock);
13884eaa4710SRishi Srivatsavai 	(void) memset(blp->bl_afs, 0xff, sizeof (blp->bl_afs));
13894eaa4710SRishi Srivatsavai 	link_unref(blp);
13904eaa4710SRishi Srivatsavai }
13914eaa4710SRishi Srivatsavai 
13924eaa4710SRishi Srivatsavai /*
13934eaa4710SRishi Srivatsavai  * This periodic timer performs three functions:
13944eaa4710SRishi Srivatsavai  *  1. It scans the list of learned forwarding entries, and removes ones that
13954eaa4710SRishi Srivatsavai  *     haven't been heard from in a while.  The time limit is backed down if
13964eaa4710SRishi Srivatsavai  *     we're above the configured table limit.
13974eaa4710SRishi Srivatsavai  *  2. It walks the links and decays away the bl_learns counter.
13984eaa4710SRishi Srivatsavai  *  3. It scans the observability node entries looking for ones that can be
13994eaa4710SRishi Srivatsavai  *     freed up.
14004eaa4710SRishi Srivatsavai  */
14014eaa4710SRishi Srivatsavai /* ARGSUSED */
14024eaa4710SRishi Srivatsavai static void
14034eaa4710SRishi Srivatsavai bridge_timer(void *arg)
14044eaa4710SRishi Srivatsavai {
14054eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
14064eaa4710SRishi Srivatsavai 	bridge_fwd_t *bfp, *bfnext;
14074eaa4710SRishi Srivatsavai 	bridge_mac_t *bmp, *bmnext;
14084eaa4710SRishi Srivatsavai 	bridge_link_t *blp;
14094eaa4710SRishi Srivatsavai 	int err;
14104eaa4710SRishi Srivatsavai 	datalink_id_t tmpid;
14114eaa4710SRishi Srivatsavai 	avl_tree_t fwd_scavenge;
14124eaa4710SRishi Srivatsavai 	clock_t age_limit;
14134eaa4710SRishi Srivatsavai 	uint32_t ldecay;
14144eaa4710SRishi Srivatsavai 
14154eaa4710SRishi Srivatsavai 	avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t),
14164eaa4710SRishi Srivatsavai 	    offsetof(bridge_fwd_t, bf_node));
14174eaa4710SRishi Srivatsavai 	mutex_enter(&inst_lock);
14184eaa4710SRishi Srivatsavai 	for (bip = list_head(&inst_list); bip != NULL;
14194eaa4710SRishi Srivatsavai 	    bip = list_next(&inst_list, bip)) {
14204eaa4710SRishi Srivatsavai 		if (bip->bi_flags & BIF_SHUTDOWN)
14214eaa4710SRishi Srivatsavai 			continue;
14224eaa4710SRishi Srivatsavai 		rw_enter(&bip->bi_rwlock, RW_WRITER);
14234eaa4710SRishi Srivatsavai 		/* compute scaled maximum age based on table limit */
14244eaa4710SRishi Srivatsavai 		if (avl_numnodes(&bip->bi_fwd) > bip->bi_tablemax)
14254eaa4710SRishi Srivatsavai 			bip->bi_tshift++;
14264eaa4710SRishi Srivatsavai 		else
14274eaa4710SRishi Srivatsavai 			bip->bi_tshift = 0;
14284eaa4710SRishi Srivatsavai 		if ((age_limit = bridge_fwd_age >> bip->bi_tshift) == 0) {
14294eaa4710SRishi Srivatsavai 			if (bip->bi_tshift != 0)
14304eaa4710SRishi Srivatsavai 				bip->bi_tshift--;
14314eaa4710SRishi Srivatsavai 			age_limit = 1;
14324eaa4710SRishi Srivatsavai 		}
14334eaa4710SRishi Srivatsavai 		bfnext = avl_first(&bip->bi_fwd);
14344eaa4710SRishi Srivatsavai 		while ((bfp = bfnext) != NULL) {
14354eaa4710SRishi Srivatsavai 			bfnext = AVL_NEXT(&bip->bi_fwd, bfp);
14364eaa4710SRishi Srivatsavai 			if (!(bfp->bf_flags & BFF_LOCALADDR) &&
14374eaa4710SRishi Srivatsavai 			    (lbolt - bfp->bf_lastheard) > age_limit) {
14384eaa4710SRishi Srivatsavai 				ASSERT(bfp->bf_flags & BFF_INTREE);
14394eaa4710SRishi Srivatsavai 				avl_remove(&bip->bi_fwd, bfp);
14404eaa4710SRishi Srivatsavai 				bfp->bf_flags &= ~BFF_INTREE;
14414eaa4710SRishi Srivatsavai 				avl_add(&fwd_scavenge, bfp);
14424eaa4710SRishi Srivatsavai 			}
14434eaa4710SRishi Srivatsavai 		}
14444eaa4710SRishi Srivatsavai 		for (blp = list_head(&bip->bi_links); blp != NULL;
14454eaa4710SRishi Srivatsavai 		    blp = list_next(&bip->bi_links, blp)) {
14464eaa4710SRishi Srivatsavai 			ldecay = mac_get_ldecay(blp->bl_mh);
14474eaa4710SRishi Srivatsavai 			if (ldecay >= blp->bl_learns)
14484eaa4710SRishi Srivatsavai 				blp->bl_learns = 0;
14494eaa4710SRishi Srivatsavai 			else
14504eaa4710SRishi Srivatsavai 				atomic_add_int(&blp->bl_learns, -(int)ldecay);
14514eaa4710SRishi Srivatsavai 		}
14524eaa4710SRishi Srivatsavai 		rw_exit(&bip->bi_rwlock);
14534eaa4710SRishi Srivatsavai 		bfnext = avl_first(&fwd_scavenge);
14544eaa4710SRishi Srivatsavai 		while ((bfp = bfnext) != NULL) {
14554eaa4710SRishi Srivatsavai 			bfnext = AVL_NEXT(&fwd_scavenge, bfp);
14564eaa4710SRishi Srivatsavai 			avl_remove(&fwd_scavenge, bfp);
14574eaa4710SRishi Srivatsavai 			KIINCR(bki_expire);
14584eaa4710SRishi Srivatsavai 			fwd_unref(bfp);	/* drop tree reference */
14594eaa4710SRishi Srivatsavai 		}
14604eaa4710SRishi Srivatsavai 	}
14614eaa4710SRishi Srivatsavai 	mutex_exit(&inst_lock);
14624eaa4710SRishi Srivatsavai 	avl_destroy(&fwd_scavenge);
14634eaa4710SRishi Srivatsavai 
14644eaa4710SRishi Srivatsavai 	/*
14654eaa4710SRishi Srivatsavai 	 * Scan the bridge_mac_t entries and try to free up the ones that are
14664eaa4710SRishi Srivatsavai 	 * no longer active.  This must be done by polling, as neither DLS nor
14674eaa4710SRishi Srivatsavai 	 * MAC provides a driver any sort of positive control over clients.
14684eaa4710SRishi Srivatsavai 	 */
14694eaa4710SRishi Srivatsavai 	rw_enter(&bmac_rwlock, RW_WRITER);
14704eaa4710SRishi Srivatsavai 	bmnext = list_head(&bmac_list);
14714eaa4710SRishi Srivatsavai 	while ((bmp = bmnext) != NULL) {
14724eaa4710SRishi Srivatsavai 		bmnext = list_next(&bmac_list, bmp);
14734eaa4710SRishi Srivatsavai 
14744eaa4710SRishi Srivatsavai 		/* ignore active bridges */
14754eaa4710SRishi Srivatsavai 		if (bmp->bm_inst != NULL)
14764eaa4710SRishi Srivatsavai 			continue;
14774eaa4710SRishi Srivatsavai 
14784eaa4710SRishi Srivatsavai 		if (bmp->bm_flags & BMF_DLS) {
14794eaa4710SRishi Srivatsavai 			err = dls_devnet_destroy(bmp->bm_mh, &tmpid, B_FALSE);
14804eaa4710SRishi Srivatsavai 			ASSERT(err == 0 || err == EBUSY);
14814eaa4710SRishi Srivatsavai 			if (err == 0)
14824eaa4710SRishi Srivatsavai 				bmp->bm_flags &= ~BMF_DLS;
14834eaa4710SRishi Srivatsavai 		}
14844eaa4710SRishi Srivatsavai 
14854eaa4710SRishi Srivatsavai 		if (!(bmp->bm_flags & BMF_DLS)) {
14864eaa4710SRishi Srivatsavai 			err = mac_unregister(bmp->bm_mh);
14874eaa4710SRishi Srivatsavai 			ASSERT(err == 0 || err == EBUSY);
14884eaa4710SRishi Srivatsavai 			if (err == 0) {
14894eaa4710SRishi Srivatsavai 				list_remove(&bmac_list, bmp);
14904eaa4710SRishi Srivatsavai 				kmem_free(bmp, sizeof (*bmp));
14914eaa4710SRishi Srivatsavai 			}
14924eaa4710SRishi Srivatsavai 		}
14934eaa4710SRishi Srivatsavai 	}
14944eaa4710SRishi Srivatsavai 	if (list_is_empty(&bmac_list)) {
14954eaa4710SRishi Srivatsavai 		bridge_timerid = 0;
14964eaa4710SRishi Srivatsavai 	} else {
14974eaa4710SRishi Srivatsavai 		bridge_timerid = timeout(bridge_timer, NULL,
14984eaa4710SRishi Srivatsavai 		    bridge_scan_interval);
14994eaa4710SRishi Srivatsavai 	}
15004eaa4710SRishi Srivatsavai 	rw_exit(&bmac_rwlock);
15014eaa4710SRishi Srivatsavai }
15024eaa4710SRishi Srivatsavai 
15034eaa4710SRishi Srivatsavai static int
15044eaa4710SRishi Srivatsavai bridge_open(queue_t *rq, dev_t *devp, int oflag, int sflag, cred_t *credp)
15054eaa4710SRishi Srivatsavai {
15064eaa4710SRishi Srivatsavai 	bridge_stream_t	*bsp;
15074eaa4710SRishi Srivatsavai 
15084eaa4710SRishi Srivatsavai 	if (rq->q_ptr != NULL)
15094eaa4710SRishi Srivatsavai 		return (0);
15104eaa4710SRishi Srivatsavai 
15114eaa4710SRishi Srivatsavai 	if (sflag & MODOPEN)
15124eaa4710SRishi Srivatsavai 		return (EINVAL);
15134eaa4710SRishi Srivatsavai 
15144eaa4710SRishi Srivatsavai 	/*
15154eaa4710SRishi Srivatsavai 	 * Check the minor node number being opened.  This tells us which
15164eaa4710SRishi Srivatsavai 	 * bridge instance the user wants.
15174eaa4710SRishi Srivatsavai 	 */
15184eaa4710SRishi Srivatsavai 	if (getminor(*devp) != 0) {
15194eaa4710SRishi Srivatsavai 		/*
15204eaa4710SRishi Srivatsavai 		 * This is a regular DLPI stream for snoop or the like.
15214eaa4710SRishi Srivatsavai 		 * Redirect it through DLD.
15224eaa4710SRishi Srivatsavai 		 */
15234eaa4710SRishi Srivatsavai 		rq->q_qinfo = &bridge_dld_rinit;
15244eaa4710SRishi Srivatsavai 		OTHERQ(rq)->q_qinfo = &bridge_dld_winit;
15254eaa4710SRishi Srivatsavai 		return (dld_open(rq, devp, oflag, sflag, credp));
15264eaa4710SRishi Srivatsavai 	} else {
15274eaa4710SRishi Srivatsavai 		/*
15284eaa4710SRishi Srivatsavai 		 * Allocate the bridge control stream structure.
15294eaa4710SRishi Srivatsavai 		 */
15304eaa4710SRishi Srivatsavai 		if ((bsp = stream_alloc()) == NULL)
15314eaa4710SRishi Srivatsavai 			return (ENOSR);
15324eaa4710SRishi Srivatsavai 		rq->q_ptr = WR(rq)->q_ptr = (caddr_t)bsp;
15334eaa4710SRishi Srivatsavai 		bsp->bs_wq = WR(rq);
15344eaa4710SRishi Srivatsavai 		*devp = makedevice(getmajor(*devp), bsp->bs_minor);
15354eaa4710SRishi Srivatsavai 		qprocson(rq);
15364eaa4710SRishi Srivatsavai 		return (0);
15374eaa4710SRishi Srivatsavai 	}
15384eaa4710SRishi Srivatsavai }
15394eaa4710SRishi Srivatsavai 
15404eaa4710SRishi Srivatsavai /*
15414eaa4710SRishi Srivatsavai  * This is used only for bridge control streams.  DLPI goes through dld
15424eaa4710SRishi Srivatsavai  * instead.
15434eaa4710SRishi Srivatsavai  */
15444eaa4710SRishi Srivatsavai static int
15454eaa4710SRishi Srivatsavai bridge_close(queue_t *rq)
15464eaa4710SRishi Srivatsavai {
15474eaa4710SRishi Srivatsavai 	bridge_stream_t	*bsp = rq->q_ptr;
15484eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
15494eaa4710SRishi Srivatsavai 
15504eaa4710SRishi Srivatsavai 	/*
15514eaa4710SRishi Srivatsavai 	 * Wait for any stray taskq (add/delete link) entries related to this
15524eaa4710SRishi Srivatsavai 	 * stream to leave the system.
15534eaa4710SRishi Srivatsavai 	 */
15544eaa4710SRishi Srivatsavai 	mutex_enter(&stream_ref_lock);
15554eaa4710SRishi Srivatsavai 	while (bsp->bs_taskq_cnt != 0)
15564eaa4710SRishi Srivatsavai 		cv_wait(&stream_ref_cv, &stream_ref_lock);
15574eaa4710SRishi Srivatsavai 	mutex_exit(&stream_ref_lock);
15584eaa4710SRishi Srivatsavai 
15594eaa4710SRishi Srivatsavai 	qprocsoff(rq);
15604eaa4710SRishi Srivatsavai 	if ((bip = bsp->bs_inst) != NULL)
15614eaa4710SRishi Srivatsavai 		shutdown_inst(bip);
15624eaa4710SRishi Srivatsavai 	rq->q_ptr = WR(rq)->q_ptr = NULL;
15634eaa4710SRishi Srivatsavai 	stream_free(bsp);
15644eaa4710SRishi Srivatsavai 	if (bip != NULL)
15654eaa4710SRishi Srivatsavai 		bridge_unref(bip);
15664eaa4710SRishi Srivatsavai 
15674eaa4710SRishi Srivatsavai 	return (0);
15684eaa4710SRishi Srivatsavai }
15694eaa4710SRishi Srivatsavai 
15704eaa4710SRishi Srivatsavai static void
15714eaa4710SRishi Srivatsavai bridge_learn(bridge_link_t *blp, const uint8_t *saddr, uint16_t ingress_nick,
15724eaa4710SRishi Srivatsavai     uint16_t vlanid)
15734eaa4710SRishi Srivatsavai {
15744eaa4710SRishi Srivatsavai 	bridge_inst_t *bip = blp->bl_inst;
15754eaa4710SRishi Srivatsavai 	bridge_fwd_t *bfp, *bfpnew;
15764eaa4710SRishi Srivatsavai 	int i;
15774eaa4710SRishi Srivatsavai 	boolean_t replaced = B_FALSE;
15784eaa4710SRishi Srivatsavai 
15794eaa4710SRishi Srivatsavai 	/* Ignore multi-destination address used as source; it's nonsense. */
15804eaa4710SRishi Srivatsavai 	if (*saddr & 1)
15814eaa4710SRishi Srivatsavai 		return;
15824eaa4710SRishi Srivatsavai 
15834eaa4710SRishi Srivatsavai 	/*
15844eaa4710SRishi Srivatsavai 	 * If the source is known, then check whether it belongs on this link.
15854eaa4710SRishi Srivatsavai 	 * If not, and this isn't a fixed local address, then we've detected a
15864eaa4710SRishi Srivatsavai 	 * move.  If it's not known, learn it.
15874eaa4710SRishi Srivatsavai 	 */
15884eaa4710SRishi Srivatsavai 	if ((bfp = fwd_find(bip, saddr, vlanid)) != NULL) {
15894eaa4710SRishi Srivatsavai 		/*
15904eaa4710SRishi Srivatsavai 		 * If the packet has a fixed local source address, then there's
15914eaa4710SRishi Srivatsavai 		 * nothing we can learn.  We must quit.  If this was a received
15924eaa4710SRishi Srivatsavai 		 * packet, then the sender has stolen our address, but there's
15934eaa4710SRishi Srivatsavai 		 * nothing we can do.  If it's a transmitted packet, then
15944eaa4710SRishi Srivatsavai 		 * that's the normal case.
15954eaa4710SRishi Srivatsavai 		 */
15964eaa4710SRishi Srivatsavai 		if (bfp->bf_flags & BFF_LOCALADDR) {
15974eaa4710SRishi Srivatsavai 			fwd_unref(bfp);
15984eaa4710SRishi Srivatsavai 			return;
15994eaa4710SRishi Srivatsavai 		}
16004eaa4710SRishi Srivatsavai 
16014eaa4710SRishi Srivatsavai 		/*
16024eaa4710SRishi Srivatsavai 		 * Check if the link (and TRILL sender, if any) being used is
16034eaa4710SRishi Srivatsavai 		 * among the ones registered for this address.  If so, then
16044eaa4710SRishi Srivatsavai 		 * this is information that we already know.
16054eaa4710SRishi Srivatsavai 		 */
16064eaa4710SRishi Srivatsavai 		if (bfp->bf_trill_nick == ingress_nick) {
16074eaa4710SRishi Srivatsavai 			for (i = 0; i < bfp->bf_nlinks; i++) {
16084eaa4710SRishi Srivatsavai 				if (bfp->bf_links[i] == blp) {
16094eaa4710SRishi Srivatsavai 					bfp->bf_lastheard = lbolt;
16104eaa4710SRishi Srivatsavai 					fwd_unref(bfp);
16114eaa4710SRishi Srivatsavai 					return;
16124eaa4710SRishi Srivatsavai 				}
16134eaa4710SRishi Srivatsavai 			}
16144eaa4710SRishi Srivatsavai 		}
16154eaa4710SRishi Srivatsavai 	}
16164eaa4710SRishi Srivatsavai 
16174eaa4710SRishi Srivatsavai 	/*
16184eaa4710SRishi Srivatsavai 	 * Note that we intentionally "unlearn" things that appear to be under
16194eaa4710SRishi Srivatsavai 	 * attack on this link.  The forwarding cache is a negative thing for
16204eaa4710SRishi Srivatsavai 	 * security -- it disables reachability as a performance optimization
16214eaa4710SRishi Srivatsavai 	 * -- so leaving out entries optimizes for success and defends against
16224eaa4710SRishi Srivatsavai 	 * the attack.  Thus, the bare increment without a check in the delete
16234eaa4710SRishi Srivatsavai 	 * code above is right.  (And it's ok if we skid over the limit a
16244eaa4710SRishi Srivatsavai 	 * little, so there's no syncronization needed on the test.)
16254eaa4710SRishi Srivatsavai 	 */
16264eaa4710SRishi Srivatsavai 	if (blp->bl_learns >= mac_get_llimit(blp->bl_mh)) {
16274eaa4710SRishi Srivatsavai 		if (bfp != NULL) {
16284eaa4710SRishi Srivatsavai 			if (bfp->bf_vcnt == 0)
16294eaa4710SRishi Srivatsavai 				fwd_delete(bfp);
16304eaa4710SRishi Srivatsavai 			fwd_unref(bfp);
16314eaa4710SRishi Srivatsavai 		}
16324eaa4710SRishi Srivatsavai 		return;
16334eaa4710SRishi Srivatsavai 	}
16344eaa4710SRishi Srivatsavai 
16354eaa4710SRishi Srivatsavai 	atomic_inc_uint(&blp->bl_learns);
16364eaa4710SRishi Srivatsavai 
16374eaa4710SRishi Srivatsavai 	if ((bfpnew = fwd_alloc(saddr, 1, ingress_nick)) == NULL) {
16384eaa4710SRishi Srivatsavai 		if (bfp != NULL)
16394eaa4710SRishi Srivatsavai 			fwd_unref(bfp);
16404eaa4710SRishi Srivatsavai 		return;
16414eaa4710SRishi Srivatsavai 	}
16424eaa4710SRishi Srivatsavai 	KIINCR(bki_count);
16434eaa4710SRishi Srivatsavai 
16444eaa4710SRishi Srivatsavai 	if (bfp != NULL) {
16454eaa4710SRishi Srivatsavai 		/*
16464eaa4710SRishi Srivatsavai 		 * If this is a new destination for the same VLAN, then delete
16474eaa4710SRishi Srivatsavai 		 * so that we can update.  If it's a different VLAN, then we're
16484eaa4710SRishi Srivatsavai 		 * not going to delete the original.  Split off instead into an
16494eaa4710SRishi Srivatsavai 		 * IVL entry.
16504eaa4710SRishi Srivatsavai 		 */
16514eaa4710SRishi Srivatsavai 		if (bfp->bf_vlanid == vlanid) {
16524eaa4710SRishi Srivatsavai 			/* save the count of IVL duplicates */
16534eaa4710SRishi Srivatsavai 			bfpnew->bf_vcnt = bfp->bf_vcnt;
16544eaa4710SRishi Srivatsavai 
16554eaa4710SRishi Srivatsavai 			/* entry deletes count as learning events */
16564eaa4710SRishi Srivatsavai 			atomic_inc_uint(&blp->bl_learns);
16574eaa4710SRishi Srivatsavai 
16584eaa4710SRishi Srivatsavai 			/* destroy and create anew; node moved */
16594eaa4710SRishi Srivatsavai 			fwd_delete(bfp);
16604eaa4710SRishi Srivatsavai 			replaced = B_TRUE;
16614eaa4710SRishi Srivatsavai 			KIINCR(bki_moved);
16624eaa4710SRishi Srivatsavai 		} else {
16634eaa4710SRishi Srivatsavai 			bfp->bf_vcnt++;
16644eaa4710SRishi Srivatsavai 			bfpnew->bf_flags |= BFF_VLANLOCAL;
16654eaa4710SRishi Srivatsavai 		}
16664eaa4710SRishi Srivatsavai 		fwd_unref(bfp);
16674eaa4710SRishi Srivatsavai 	}
16684eaa4710SRishi Srivatsavai 	bfpnew->bf_links[0] = blp;
16694eaa4710SRishi Srivatsavai 	bfpnew->bf_nlinks = 1;
16704eaa4710SRishi Srivatsavai 	atomic_inc_uint(&blp->bl_refs);	/* bf_links entry */
16714eaa4710SRishi Srivatsavai 	if (!fwd_insert(bip, bfpnew))
16724eaa4710SRishi Srivatsavai 		fwd_free(bfpnew);
16734eaa4710SRishi Srivatsavai 	else if (!replaced)
16744eaa4710SRishi Srivatsavai 		KIINCR(bki_source);
16754eaa4710SRishi Srivatsavai }
16764eaa4710SRishi Srivatsavai 
16774eaa4710SRishi Srivatsavai /*
16784eaa4710SRishi Srivatsavai  * Process the VLAN headers for output on a given link.  There are several
16794eaa4710SRishi Srivatsavai  * cases (noting that we don't map VLANs):
16804eaa4710SRishi Srivatsavai  *   1. The input packet is good as it is; either
16814eaa4710SRishi Srivatsavai  *	a. It has no tag, and output has same PVID
16824eaa4710SRishi Srivatsavai  *	b. It has a non-zero priority-only tag for PVID, and b_band is same
16834eaa4710SRishi Srivatsavai  *	c. It has a tag with VLAN different from PVID, and b_band is same
16844eaa4710SRishi Srivatsavai  *   2. The tag must change: non-zero b_band is different from tag priority
16854eaa4710SRishi Srivatsavai  *   3. The packet has a tag and should not (VLAN same as PVID, b_band zero)
16864eaa4710SRishi Srivatsavai  *   4. The packet has no tag and needs one:
16874eaa4710SRishi Srivatsavai  *      a. VLAN ID same as PVID, but b_band is non-zero
16884eaa4710SRishi Srivatsavai  *      b. VLAN ID different from PVID
16894eaa4710SRishi Srivatsavai  * We exclude case 1 first, then modify the packet.  Note that output packets
16904eaa4710SRishi Srivatsavai  * get a priority set by the mblk, not by the header, because QoS in bridging
16914eaa4710SRishi Srivatsavai  * requires priority recalculation at each node.
16924eaa4710SRishi Srivatsavai  *
16934eaa4710SRishi Srivatsavai  * The passed-in tci is the "impossible" value 0xFFFF when no tag is present.
16944eaa4710SRishi Srivatsavai  */
16954eaa4710SRishi Srivatsavai static mblk_t *
16964eaa4710SRishi Srivatsavai reform_vlan_header(mblk_t *mp, uint16_t vlanid, uint16_t tci, uint16_t pvid)
16974eaa4710SRishi Srivatsavai {
16984eaa4710SRishi Srivatsavai 	boolean_t source_has_tag = (tci != 0xFFFF);
16994eaa4710SRishi Srivatsavai 	mblk_t *mpcopy;
17004eaa4710SRishi Srivatsavai 	size_t mlen, minlen;
17014eaa4710SRishi Srivatsavai 	struct ether_vlan_header *evh;
17024eaa4710SRishi Srivatsavai 	int pri;
17034eaa4710SRishi Srivatsavai 
17044eaa4710SRishi Srivatsavai 	/* This helps centralize error handling in the caller. */
17054eaa4710SRishi Srivatsavai 	if (mp == NULL)
17064eaa4710SRishi Srivatsavai 		return (mp);
17074eaa4710SRishi Srivatsavai 
17084eaa4710SRishi Srivatsavai 	/* No forwarded packet can have hardware checksum enabled */
17094eaa4710SRishi Srivatsavai 	DB_CKSUMFLAGS(mp) = 0;
17104eaa4710SRishi Srivatsavai 
17114eaa4710SRishi Srivatsavai 	/* Get the no-modification cases out of the way first */
17124eaa4710SRishi Srivatsavai 	if (!source_has_tag && vlanid == pvid)		/* 1a */
17134eaa4710SRishi Srivatsavai 		return (mp);
17144eaa4710SRishi Srivatsavai 
17154eaa4710SRishi Srivatsavai 	pri = VLAN_PRI(tci);
17164eaa4710SRishi Srivatsavai 	if (source_has_tag && mp->b_band == pri) {
17174eaa4710SRishi Srivatsavai 		if (vlanid != pvid)			/* 1c */
17184eaa4710SRishi Srivatsavai 			return (mp);
17194eaa4710SRishi Srivatsavai 		if (pri != 0 && VLAN_ID(tci) == 0)	/* 1b */
17204eaa4710SRishi Srivatsavai 			return (mp);
17214eaa4710SRishi Srivatsavai 	}
17224eaa4710SRishi Srivatsavai 
17234eaa4710SRishi Srivatsavai 	/*
17244eaa4710SRishi Srivatsavai 	 * We now know that we must modify the packet.  Prepare for that.  Note
17254eaa4710SRishi Srivatsavai 	 * that if a tag is present, the caller has already done a pullup for
17264eaa4710SRishi Srivatsavai 	 * the VLAN header, so we're good to go.
17274eaa4710SRishi Srivatsavai 	 */
17284eaa4710SRishi Srivatsavai 	if (MBLKL(mp) < sizeof (struct ether_header)) {
17294eaa4710SRishi Srivatsavai 		mpcopy = msgpullup(mp, sizeof (struct ether_header));
17304eaa4710SRishi Srivatsavai 		if (mpcopy == NULL) {
17314eaa4710SRishi Srivatsavai 			freemsg(mp);
17324eaa4710SRishi Srivatsavai 			return (NULL);
17334eaa4710SRishi Srivatsavai 		}
17344eaa4710SRishi Srivatsavai 		mp = mpcopy;
17354eaa4710SRishi Srivatsavai 	}
17364eaa4710SRishi Srivatsavai 	if (DB_REF(mp) > 1 || !IS_P2ALIGNED(mp->b_rptr, sizeof (uint16_t)) ||
17374eaa4710SRishi Srivatsavai 	    (!source_has_tag && MBLKTAIL(mp) < VLAN_INCR)) {
17384eaa4710SRishi Srivatsavai 		minlen = mlen = MBLKL(mp);
17394eaa4710SRishi Srivatsavai 		if (!source_has_tag)
17404eaa4710SRishi Srivatsavai 			minlen += VLAN_INCR;
17414eaa4710SRishi Srivatsavai 		ASSERT(minlen >= sizeof (struct ether_vlan_header));
17424eaa4710SRishi Srivatsavai 		/*
17434eaa4710SRishi Srivatsavai 		 * We're willing to copy some data to avoid fragmentation, but
17444eaa4710SRishi Srivatsavai 		 * not a lot.
17454eaa4710SRishi Srivatsavai 		 */
17464eaa4710SRishi Srivatsavai 		if (minlen > 256)
17474eaa4710SRishi Srivatsavai 			minlen = sizeof (struct ether_vlan_header);
17484eaa4710SRishi Srivatsavai 		mpcopy = allocb(minlen, BPRI_MED);
17494eaa4710SRishi Srivatsavai 		if (mpcopy == NULL) {
17504eaa4710SRishi Srivatsavai 			freemsg(mp);
17514eaa4710SRishi Srivatsavai 			return (NULL);
17524eaa4710SRishi Srivatsavai 		}
17534eaa4710SRishi Srivatsavai 		if (mlen <= minlen) {
17544eaa4710SRishi Srivatsavai 			/* We toss the first mblk when we can. */
17554eaa4710SRishi Srivatsavai 			bcopy(mp->b_rptr, mpcopy->b_rptr, mlen);
17564eaa4710SRishi Srivatsavai 			mpcopy->b_wptr += mlen;
17574eaa4710SRishi Srivatsavai 			mpcopy->b_cont = mp->b_cont;
17584eaa4710SRishi Srivatsavai 			freeb(mp);
17594eaa4710SRishi Srivatsavai 		} else {
17604eaa4710SRishi Srivatsavai 			/* If not, then just copy what we need */
17614eaa4710SRishi Srivatsavai 			if (!source_has_tag)
17624eaa4710SRishi Srivatsavai 				minlen = sizeof (struct ether_header);
17634eaa4710SRishi Srivatsavai 			bcopy(mp->b_rptr, mpcopy->b_rptr, minlen);
17644eaa4710SRishi Srivatsavai 			mpcopy->b_wptr += minlen;
17654eaa4710SRishi Srivatsavai 			mpcopy->b_cont = mp;
17664eaa4710SRishi Srivatsavai 			mp->b_rptr += minlen;
17674eaa4710SRishi Srivatsavai 		}
17684eaa4710SRishi Srivatsavai 		mp = mpcopy;
17694eaa4710SRishi Srivatsavai 	}
17704eaa4710SRishi Srivatsavai 
17714eaa4710SRishi Srivatsavai 	/* LINTED: pointer alignment */
17724eaa4710SRishi Srivatsavai 	evh = (struct ether_vlan_header *)mp->b_rptr;
17734eaa4710SRishi Srivatsavai 	if (source_has_tag) {
17744eaa4710SRishi Srivatsavai 		if (mp->b_band == 0 && vlanid == pvid) {	/* 3 */
17754eaa4710SRishi Srivatsavai 			evh->ether_tpid = evh->ether_type;
17764eaa4710SRishi Srivatsavai 			mlen = MBLKL(mp);
17774eaa4710SRishi Srivatsavai 			if (mlen > sizeof (struct ether_vlan_header))
17784eaa4710SRishi Srivatsavai 				ovbcopy(mp->b_rptr +
17794eaa4710SRishi Srivatsavai 				    sizeof (struct ether_vlan_header),
17804eaa4710SRishi Srivatsavai 				    mp->b_rptr + sizeof (struct ether_header),
17814eaa4710SRishi Srivatsavai 				    mlen - sizeof (struct ether_vlan_header));
17824eaa4710SRishi Srivatsavai 			mp->b_wptr -= VLAN_INCR;
17834eaa4710SRishi Srivatsavai 		} else {					/* 2 */
17844eaa4710SRishi Srivatsavai 			if (vlanid == pvid)
17854eaa4710SRishi Srivatsavai 				vlanid = VLAN_ID_NONE;
17864eaa4710SRishi Srivatsavai 			tci = VLAN_TCI(mp->b_band, ETHER_CFI, vlanid);
17874eaa4710SRishi Srivatsavai 			evh->ether_tci = htons(tci);
17884eaa4710SRishi Srivatsavai 		}
17894eaa4710SRishi Srivatsavai 	} else {
17904eaa4710SRishi Srivatsavai 		/* case 4: no header present, but one is needed */
17914eaa4710SRishi Srivatsavai 		mlen = MBLKL(mp);
17924eaa4710SRishi Srivatsavai 		if (mlen > sizeof (struct ether_header))
17934eaa4710SRishi Srivatsavai 			ovbcopy(mp->b_rptr + sizeof (struct ether_header),
17944eaa4710SRishi Srivatsavai 			    mp->b_rptr + sizeof (struct ether_vlan_header),
17954eaa4710SRishi Srivatsavai 			    mlen - sizeof (struct ether_header));
17964eaa4710SRishi Srivatsavai 		mp->b_wptr += VLAN_INCR;
17974eaa4710SRishi Srivatsavai 		ASSERT(mp->b_wptr <= DB_LIM(mp));
17984eaa4710SRishi Srivatsavai 		if (vlanid == pvid)
17994eaa4710SRishi Srivatsavai 			vlanid = VLAN_ID_NONE;
18004eaa4710SRishi Srivatsavai 		tci = VLAN_TCI(mp->b_band, ETHER_CFI, vlanid);
18014eaa4710SRishi Srivatsavai 		evh->ether_type = evh->ether_tpid;
18024eaa4710SRishi Srivatsavai 		evh->ether_tpid = htons(ETHERTYPE_VLAN);
18034eaa4710SRishi Srivatsavai 		evh->ether_tci = htons(tci);
18044eaa4710SRishi Srivatsavai 	}
18054eaa4710SRishi Srivatsavai 	return (mp);
18064eaa4710SRishi Srivatsavai }
18074eaa4710SRishi Srivatsavai 
18084eaa4710SRishi Srivatsavai /* Record VLAN information and strip header if requested . */
18094eaa4710SRishi Srivatsavai static void
18104eaa4710SRishi Srivatsavai update_header(mblk_t *mp, mac_header_info_t *hdr_info, boolean_t striphdr)
18114eaa4710SRishi Srivatsavai {
18124eaa4710SRishi Srivatsavai 	if (hdr_info->mhi_bindsap == ETHERTYPE_VLAN) {
18134eaa4710SRishi Srivatsavai 		struct ether_vlan_header *evhp;
18144eaa4710SRishi Srivatsavai 		uint16_t ether_type;
18154eaa4710SRishi Srivatsavai 
18164eaa4710SRishi Srivatsavai 		/* LINTED: alignment */
18174eaa4710SRishi Srivatsavai 		evhp = (struct ether_vlan_header *)mp->b_rptr;
18184eaa4710SRishi Srivatsavai 		hdr_info->mhi_istagged = B_TRUE;
18194eaa4710SRishi Srivatsavai 		hdr_info->mhi_tci = ntohs(evhp->ether_tci);
18204eaa4710SRishi Srivatsavai 		if (striphdr) {
18214eaa4710SRishi Srivatsavai 			/*
18224eaa4710SRishi Srivatsavai 			 * For VLAN tagged frames update the ether_type
18234eaa4710SRishi Srivatsavai 			 * in hdr_info before stripping the header.
18244eaa4710SRishi Srivatsavai 			 */
18254eaa4710SRishi Srivatsavai 			ether_type = ntohs(evhp->ether_type);
18264eaa4710SRishi Srivatsavai 			hdr_info->mhi_origsap = ether_type;
18274eaa4710SRishi Srivatsavai 			hdr_info->mhi_bindsap = (ether_type > ETHERMTU) ?
18284eaa4710SRishi Srivatsavai 			    ether_type : DLS_SAP_LLC;
18294eaa4710SRishi Srivatsavai 			mp->b_rptr = (uchar_t *)(evhp + 1);
18304eaa4710SRishi Srivatsavai 		}
18314eaa4710SRishi Srivatsavai 	} else {
18324eaa4710SRishi Srivatsavai 		hdr_info->mhi_istagged = B_FALSE;
18334eaa4710SRishi Srivatsavai 		hdr_info->mhi_tci = VLAN_ID_NONE;
18344eaa4710SRishi Srivatsavai 		if (striphdr)
18354eaa4710SRishi Srivatsavai 			mp->b_rptr += sizeof (struct ether_header);
18364eaa4710SRishi Srivatsavai 	}
18374eaa4710SRishi Srivatsavai }
18384eaa4710SRishi Srivatsavai 
18394eaa4710SRishi Srivatsavai /*
18404eaa4710SRishi Srivatsavai  * Return B_TRUE if we're allowed to send on this link with the given VLAN ID.
18414eaa4710SRishi Srivatsavai  */
18424eaa4710SRishi Srivatsavai static boolean_t
18434eaa4710SRishi Srivatsavai bridge_can_send(bridge_link_t *blp, uint16_t vlanid)
18444eaa4710SRishi Srivatsavai {
18454eaa4710SRishi Srivatsavai 	ASSERT(vlanid != VLAN_ID_NONE);
18464eaa4710SRishi Srivatsavai 	if (blp->bl_flags & BLF_DELETED)
18474eaa4710SRishi Srivatsavai 		return (B_FALSE);
18484eaa4710SRishi Srivatsavai 	if (blp->bl_trilldata == NULL && blp->bl_state != BLS_FORWARDING)
18494eaa4710SRishi Srivatsavai 		return (B_FALSE);
18504eaa4710SRishi Srivatsavai 	return (BRIDGE_VLAN_ISSET(blp, vlanid) && BRIDGE_AF_ISSET(blp, vlanid));
18514eaa4710SRishi Srivatsavai }
18524eaa4710SRishi Srivatsavai 
18534eaa4710SRishi Srivatsavai /*
18544eaa4710SRishi Srivatsavai  * This function scans the bridge forwarding tables in order to forward a given
18554eaa4710SRishi Srivatsavai  * packet.  If the packet either doesn't need forwarding (the current link is
18564eaa4710SRishi Srivatsavai  * correct) or the current link needs a copy as well, then the packet is
18574eaa4710SRishi Srivatsavai  * returned to the caller.
18584eaa4710SRishi Srivatsavai  *
18594eaa4710SRishi Srivatsavai  * If a packet has been decapsulated from TRILL, then it must *NOT* reenter a
18604eaa4710SRishi Srivatsavai  * TRILL tunnel.  If the destination points there, then drop instead.
18614eaa4710SRishi Srivatsavai  */
18624eaa4710SRishi Srivatsavai static mblk_t *
18634eaa4710SRishi Srivatsavai bridge_forward(bridge_link_t *blp, mac_header_info_t *hdr_info, mblk_t *mp,
18644eaa4710SRishi Srivatsavai     uint16_t vlanid, uint16_t tci, boolean_t from_trill, boolean_t is_xmit)
18654eaa4710SRishi Srivatsavai {
18664eaa4710SRishi Srivatsavai 	mblk_t *mpsend, *mpcopy;
18674eaa4710SRishi Srivatsavai 	bridge_inst_t *bip = blp->bl_inst;
18684eaa4710SRishi Srivatsavai 	bridge_link_t *blpsend, *blpnext;
18694eaa4710SRishi Srivatsavai 	bridge_fwd_t *bfp;
18704eaa4710SRishi Srivatsavai 	uint_t i;
18714eaa4710SRishi Srivatsavai 	boolean_t selfseen = B_FALSE;
18724eaa4710SRishi Srivatsavai 	void *tdp;
18734eaa4710SRishi Srivatsavai 	const uint8_t *daddr = hdr_info->mhi_daddr;
18744eaa4710SRishi Srivatsavai 
18754eaa4710SRishi Srivatsavai 	/*
18764eaa4710SRishi Srivatsavai 	 * Check for the IEEE "reserved" multicast addresses.  Messages sent to
18774eaa4710SRishi Srivatsavai 	 * these addresses are used for link-local control (STP and pause), and
18784eaa4710SRishi Srivatsavai 	 * are never forwarded or redirected.
18794eaa4710SRishi Srivatsavai 	 */
18804eaa4710SRishi Srivatsavai 	if (daddr[0] == 1 && daddr[1] == 0x80 && daddr[2] == 0xc2 &&
18814eaa4710SRishi Srivatsavai 	    daddr[3] == 0 && daddr[4] == 0 && (daddr[5] & 0xf0) == 0) {
18824eaa4710SRishi Srivatsavai 		if (from_trill) {
18834eaa4710SRishi Srivatsavai 			freemsg(mp);
18844eaa4710SRishi Srivatsavai 			mp = NULL;
18854eaa4710SRishi Srivatsavai 		}
18864eaa4710SRishi Srivatsavai 		return (mp);
18874eaa4710SRishi Srivatsavai 	}
18884eaa4710SRishi Srivatsavai 
18894eaa4710SRishi Srivatsavai 	if ((bfp = fwd_find(bip, daddr, vlanid)) != NULL) {
18904eaa4710SRishi Srivatsavai 
18914eaa4710SRishi Srivatsavai 		/*
18924eaa4710SRishi Srivatsavai 		 * If trill indicates a destination for this node, then it's
18934eaa4710SRishi Srivatsavai 		 * clearly not intended for local delivery.  We must tell TRILL
18944eaa4710SRishi Srivatsavai 		 * to encapsulate, as long as we didn't just decapsulate it.
18954eaa4710SRishi Srivatsavai 		 */
18964eaa4710SRishi Srivatsavai 		if (bfp->bf_trill_nick != RBRIDGE_NICKNAME_NONE) {
18974eaa4710SRishi Srivatsavai 			/*
18984eaa4710SRishi Srivatsavai 			 * Error case: can't reencapsulate if the protocols are
18994eaa4710SRishi Srivatsavai 			 * working correctly.
19004eaa4710SRishi Srivatsavai 			 */
19014eaa4710SRishi Srivatsavai 			if (from_trill) {
19024eaa4710SRishi Srivatsavai 				freemsg(mp);
19034eaa4710SRishi Srivatsavai 				return (NULL);
19044eaa4710SRishi Srivatsavai 			}
19054eaa4710SRishi Srivatsavai 			mutex_enter(&blp->bl_trilllock);
19064eaa4710SRishi Srivatsavai 			if ((tdp = blp->bl_trilldata) != NULL) {
19074eaa4710SRishi Srivatsavai 				blp->bl_trillthreads++;
19084eaa4710SRishi Srivatsavai 				mutex_exit(&blp->bl_trilllock);
19094eaa4710SRishi Srivatsavai 				update_header(mp, hdr_info, B_FALSE);
19104eaa4710SRishi Srivatsavai 				if (is_xmit)
19114eaa4710SRishi Srivatsavai 					mp = mac_fix_cksum(mp);
19124eaa4710SRishi Srivatsavai 				/* all trill data frames have Inner.VLAN */
19134eaa4710SRishi Srivatsavai 				mp = reform_vlan_header(mp, vlanid, tci, 0);
19144eaa4710SRishi Srivatsavai 				if (mp == NULL) {
19154eaa4710SRishi Srivatsavai 					KIINCR(bki_drops);
19164eaa4710SRishi Srivatsavai 					fwd_unref(bfp);
19174eaa4710SRishi Srivatsavai 					return (NULL);
19184eaa4710SRishi Srivatsavai 				}
19194eaa4710SRishi Srivatsavai 				trill_encap_fn(tdp, blp, hdr_info, mp,
19204eaa4710SRishi Srivatsavai 				    bfp->bf_trill_nick);
19214eaa4710SRishi Srivatsavai 				mutex_enter(&blp->bl_trilllock);
19224eaa4710SRishi Srivatsavai 				if (--blp->bl_trillthreads == 0 &&
19234eaa4710SRishi Srivatsavai 				    blp->bl_trilldata == NULL)
19244eaa4710SRishi Srivatsavai 					cv_broadcast(&blp->bl_trillwait);
19254eaa4710SRishi Srivatsavai 			}
19264eaa4710SRishi Srivatsavai 			mutex_exit(&blp->bl_trilllock);
19274eaa4710SRishi Srivatsavai 
19284eaa4710SRishi Srivatsavai 			/* if TRILL has been disabled, then kill this stray */
19294eaa4710SRishi Srivatsavai 			if (tdp == NULL) {
19304eaa4710SRishi Srivatsavai 				freemsg(mp);
19314eaa4710SRishi Srivatsavai 				fwd_delete(bfp);
19324eaa4710SRishi Srivatsavai 			}
19334eaa4710SRishi Srivatsavai 			fwd_unref(bfp);
19344eaa4710SRishi Srivatsavai 			return (NULL);
19354eaa4710SRishi Srivatsavai 		}
19364eaa4710SRishi Srivatsavai 
19374eaa4710SRishi Srivatsavai 		/* find first link we can send on */
19384eaa4710SRishi Srivatsavai 		for (i = 0; i < bfp->bf_nlinks; i++) {
19394eaa4710SRishi Srivatsavai 			blpsend = bfp->bf_links[i];
19404eaa4710SRishi Srivatsavai 			if (blpsend == blp)
19414eaa4710SRishi Srivatsavai 				selfseen = B_TRUE;
19424eaa4710SRishi Srivatsavai 			else if (bridge_can_send(blpsend, vlanid))
19434eaa4710SRishi Srivatsavai 				break;
19444eaa4710SRishi Srivatsavai 		}
19454eaa4710SRishi Srivatsavai 
19464eaa4710SRishi Srivatsavai 		while (i < bfp->bf_nlinks) {
19474eaa4710SRishi Srivatsavai 			blpsend = bfp->bf_links[i];
19484eaa4710SRishi Srivatsavai 			for (i++; i < bfp->bf_nlinks; i++) {
19494eaa4710SRishi Srivatsavai 				blpnext = bfp->bf_links[i];
19504eaa4710SRishi Srivatsavai 				if (blpnext == blp)
19514eaa4710SRishi Srivatsavai 					selfseen = B_TRUE;
19524eaa4710SRishi Srivatsavai 				else if (bridge_can_send(blpnext, vlanid))
19534eaa4710SRishi Srivatsavai 					break;
19544eaa4710SRishi Srivatsavai 			}
19554eaa4710SRishi Srivatsavai 			if (i == bfp->bf_nlinks && !selfseen) {
19564eaa4710SRishi Srivatsavai 				mpsend = mp;
19574eaa4710SRishi Srivatsavai 				mp = NULL;
19584eaa4710SRishi Srivatsavai 			} else {
19594eaa4710SRishi Srivatsavai 				mpsend = copymsg(mp);
19604eaa4710SRishi Srivatsavai 			}
19614eaa4710SRishi Srivatsavai 
19624eaa4710SRishi Srivatsavai 			if (!from_trill && is_xmit)
19634eaa4710SRishi Srivatsavai 				mpsend = mac_fix_cksum(mpsend);
19644eaa4710SRishi Srivatsavai 
19654eaa4710SRishi Srivatsavai 			mpsend = reform_vlan_header(mpsend, vlanid, tci,
19664eaa4710SRishi Srivatsavai 			    blpsend->bl_pvid);
19674eaa4710SRishi Srivatsavai 			if (mpsend == NULL) {
19684eaa4710SRishi Srivatsavai 				KIINCR(bki_drops);
19694eaa4710SRishi Srivatsavai 				continue;
19704eaa4710SRishi Srivatsavai 			}
19714eaa4710SRishi Srivatsavai 
19724eaa4710SRishi Srivatsavai 			KIINCR(bki_forwards);
19734eaa4710SRishi Srivatsavai 			/*
19744eaa4710SRishi Srivatsavai 			 * No need to bump up the link reference count, as
19754eaa4710SRishi Srivatsavai 			 * the forwarding entry itself holds a reference to
19764eaa4710SRishi Srivatsavai 			 * the link.
19774eaa4710SRishi Srivatsavai 			 */
19784eaa4710SRishi Srivatsavai 			if (bfp->bf_flags & BFF_LOCALADDR) {
19794eaa4710SRishi Srivatsavai 				mac_rx_common(blpsend->bl_mh, NULL, mpsend);
19804eaa4710SRishi Srivatsavai 			} else {
19814eaa4710SRishi Srivatsavai 				KLPINCR(blpsend, bkl_xmit);
19824eaa4710SRishi Srivatsavai 				MAC_RING_TX(blpsend->bl_mh, NULL, mpsend,
19834eaa4710SRishi Srivatsavai 				    mpsend);
19844eaa4710SRishi Srivatsavai 				freemsg(mpsend);
19854eaa4710SRishi Srivatsavai 			}
19864eaa4710SRishi Srivatsavai 		}
19874eaa4710SRishi Srivatsavai 		/*
19884eaa4710SRishi Srivatsavai 		 * Handle a special case: if we're transmitting to the original
19894eaa4710SRishi Srivatsavai 		 * link, then check whether the localaddr flag is set.  If it
19904eaa4710SRishi Srivatsavai 		 * is, then receive instead.  This doesn't happen with ordinary
19914eaa4710SRishi Srivatsavai 		 * bridging, but does happen often with TRILL decapsulation.
19924eaa4710SRishi Srivatsavai 		 */
19934eaa4710SRishi Srivatsavai 		if (mp != NULL && is_xmit && (bfp->bf_flags & BFF_LOCALADDR)) {
19944eaa4710SRishi Srivatsavai 			mac_rx_common(blp->bl_mh, NULL, mp);
19954eaa4710SRishi Srivatsavai 			mp = NULL;
19964eaa4710SRishi Srivatsavai 		}
19974eaa4710SRishi Srivatsavai 		fwd_unref(bfp);
19984eaa4710SRishi Srivatsavai 	} else {
19994eaa4710SRishi Srivatsavai 		/*
20004eaa4710SRishi Srivatsavai 		 * TRILL has two cases to handle.  If the packet is off the
20014eaa4710SRishi Srivatsavai 		 * wire (not from TRILL), then we need to send up into the
20024eaa4710SRishi Srivatsavai 		 * TRILL module to have the distribution tree computed.  If the
20034eaa4710SRishi Srivatsavai 		 * packet is from TRILL (decapsulated), then we're part of the
20044eaa4710SRishi Srivatsavai 		 * distribution tree, and we need to copy the packet on member
20054eaa4710SRishi Srivatsavai 		 * interfaces.
20064eaa4710SRishi Srivatsavai 		 *
20074eaa4710SRishi Srivatsavai 		 * Thus, the from TRILL case is identical to the STP case.
20084eaa4710SRishi Srivatsavai 		 */
20094eaa4710SRishi Srivatsavai 		if (!from_trill && blp->bl_trilldata != NULL) {
20104eaa4710SRishi Srivatsavai 			mutex_enter(&blp->bl_trilllock);
20114eaa4710SRishi Srivatsavai 			if ((tdp = blp->bl_trilldata) != NULL) {
20124eaa4710SRishi Srivatsavai 				blp->bl_trillthreads++;
20134eaa4710SRishi Srivatsavai 				mutex_exit(&blp->bl_trilllock);
20144eaa4710SRishi Srivatsavai 				if ((mpsend = copymsg(mp)) != NULL) {
20154eaa4710SRishi Srivatsavai 					update_header(mpsend,
20164eaa4710SRishi Srivatsavai 					    hdr_info, B_FALSE);
20174eaa4710SRishi Srivatsavai 					/*
20184eaa4710SRishi Srivatsavai 					 * all trill data frames have
20194eaa4710SRishi Srivatsavai 					 * Inner.VLAN
20204eaa4710SRishi Srivatsavai 					 */
20214eaa4710SRishi Srivatsavai 					mpsend = reform_vlan_header(mpsend,
20224eaa4710SRishi Srivatsavai 					    vlanid, tci, 0);
20234eaa4710SRishi Srivatsavai 					if (mpsend == NULL) {
20244eaa4710SRishi Srivatsavai 						KIINCR(bki_drops);
20254eaa4710SRishi Srivatsavai 					} else {
20264eaa4710SRishi Srivatsavai 						trill_encap_fn(tdp, blp,
20274eaa4710SRishi Srivatsavai 						    hdr_info, mpsend,
20284eaa4710SRishi Srivatsavai 						    RBRIDGE_NICKNAME_NONE);
20294eaa4710SRishi Srivatsavai 					}
20304eaa4710SRishi Srivatsavai 				}
20314eaa4710SRishi Srivatsavai 				mutex_enter(&blp->bl_trilllock);
20324eaa4710SRishi Srivatsavai 				if (--blp->bl_trillthreads == 0 &&
20334eaa4710SRishi Srivatsavai 				    blp->bl_trilldata == NULL)
20344eaa4710SRishi Srivatsavai 					cv_broadcast(&blp->bl_trillwait);
20354eaa4710SRishi Srivatsavai 			}
20364eaa4710SRishi Srivatsavai 			mutex_exit(&blp->bl_trilllock);
20374eaa4710SRishi Srivatsavai 		}
20384eaa4710SRishi Srivatsavai 
20394eaa4710SRishi Srivatsavai 		/*
20404eaa4710SRishi Srivatsavai 		 * This is an unknown destination, so flood.
20414eaa4710SRishi Srivatsavai 		 */
20424eaa4710SRishi Srivatsavai 		rw_enter(&bip->bi_rwlock, RW_READER);
20434eaa4710SRishi Srivatsavai 		for (blpnext = list_head(&bip->bi_links); blpnext != NULL;
20444eaa4710SRishi Srivatsavai 		    blpnext = list_next(&bip->bi_links, blpnext)) {
20454eaa4710SRishi Srivatsavai 			if (blpnext == blp)
20464eaa4710SRishi Srivatsavai 				selfseen = B_TRUE;
20474eaa4710SRishi Srivatsavai 			else if (bridge_can_send(blpnext, vlanid))
20484eaa4710SRishi Srivatsavai 				break;
20494eaa4710SRishi Srivatsavai 		}
20504eaa4710SRishi Srivatsavai 		if (blpnext != NULL)
20514eaa4710SRishi Srivatsavai 			atomic_inc_uint(&blpnext->bl_refs);
20524eaa4710SRishi Srivatsavai 		rw_exit(&bip->bi_rwlock);
20534eaa4710SRishi Srivatsavai 		while ((blpsend = blpnext) != NULL) {
20544eaa4710SRishi Srivatsavai 			rw_enter(&bip->bi_rwlock, RW_READER);
20554eaa4710SRishi Srivatsavai 			for (blpnext = list_next(&bip->bi_links, blpsend);
20564eaa4710SRishi Srivatsavai 			    blpnext != NULL;
20574eaa4710SRishi Srivatsavai 			    blpnext = list_next(&bip->bi_links, blpnext)) {
20584eaa4710SRishi Srivatsavai 				if (blpnext == blp)
20594eaa4710SRishi Srivatsavai 					selfseen = B_TRUE;
20604eaa4710SRishi Srivatsavai 				else if (bridge_can_send(blpnext, vlanid))
20614eaa4710SRishi Srivatsavai 					break;
20624eaa4710SRishi Srivatsavai 			}
20634eaa4710SRishi Srivatsavai 			if (blpnext != NULL)
20644eaa4710SRishi Srivatsavai 				atomic_inc_uint(&blpnext->bl_refs);
20654eaa4710SRishi Srivatsavai 			rw_exit(&bip->bi_rwlock);
20664eaa4710SRishi Srivatsavai 			if (blpnext == NULL && !selfseen) {
20674eaa4710SRishi Srivatsavai 				mpsend = mp;
20684eaa4710SRishi Srivatsavai 				mp = NULL;
20694eaa4710SRishi Srivatsavai 			} else {
20704eaa4710SRishi Srivatsavai 				mpsend = copymsg(mp);
20714eaa4710SRishi Srivatsavai 			}
20724eaa4710SRishi Srivatsavai 
20734eaa4710SRishi Srivatsavai 			if (!from_trill && is_xmit)
20744eaa4710SRishi Srivatsavai 				mpsend = mac_fix_cksum(mpsend);
20754eaa4710SRishi Srivatsavai 
20764eaa4710SRishi Srivatsavai 			mpsend = reform_vlan_header(mpsend, vlanid, tci,
20774eaa4710SRishi Srivatsavai 			    blpsend->bl_pvid);
20784eaa4710SRishi Srivatsavai 			if (mpsend == NULL) {
20794eaa4710SRishi Srivatsavai 				KIINCR(bki_drops);
20804eaa4710SRishi Srivatsavai 				continue;
20814eaa4710SRishi Srivatsavai 			}
20824eaa4710SRishi Srivatsavai 
20834eaa4710SRishi Srivatsavai 			if (hdr_info->mhi_dsttype == MAC_ADDRTYPE_UNICAST)
20844eaa4710SRishi Srivatsavai 				KIINCR(bki_unknown);
20854eaa4710SRishi Srivatsavai 			else
20864eaa4710SRishi Srivatsavai 				KIINCR(bki_mbcast);
20874eaa4710SRishi Srivatsavai 			KLPINCR(blpsend, bkl_xmit);
20884eaa4710SRishi Srivatsavai 			if ((mpcopy = copymsg(mpsend)) != NULL)
20894eaa4710SRishi Srivatsavai 				mac_rx_common(blpsend->bl_mh, NULL, mpcopy);
20904eaa4710SRishi Srivatsavai 			MAC_RING_TX(blpsend->bl_mh, NULL, mpsend, mpsend);
20914eaa4710SRishi Srivatsavai 			freemsg(mpsend);
20924eaa4710SRishi Srivatsavai 			link_unref(blpsend);
20934eaa4710SRishi Srivatsavai 		}
20944eaa4710SRishi Srivatsavai 	}
20954eaa4710SRishi Srivatsavai 
20964eaa4710SRishi Srivatsavai 	/*
20974eaa4710SRishi Srivatsavai 	 * At this point, if np is non-NULL, it means that the caller needs to
20984eaa4710SRishi Srivatsavai 	 * continue on the selected link.
20994eaa4710SRishi Srivatsavai 	 */
21004eaa4710SRishi Srivatsavai 	return (mp);
21014eaa4710SRishi Srivatsavai }
21024eaa4710SRishi Srivatsavai 
21034eaa4710SRishi Srivatsavai /*
21044eaa4710SRishi Srivatsavai  * Extract and validate the VLAN information for a given packet.  This checks
21054eaa4710SRishi Srivatsavai  * conformance with the rules for use of the PVID on the link, and for the
21064eaa4710SRishi Srivatsavai  * allowed (configured) VLAN set.
21074eaa4710SRishi Srivatsavai  *
21084eaa4710SRishi Srivatsavai  * Returns B_TRUE if the packet passes, B_FALSE if it fails.
21094eaa4710SRishi Srivatsavai  */
21104eaa4710SRishi Srivatsavai static boolean_t
21114eaa4710SRishi Srivatsavai bridge_get_vlan(bridge_link_t *blp, mac_header_info_t *hdr_info, mblk_t *mp,
21124eaa4710SRishi Srivatsavai     uint16_t *vlanidp, uint16_t *tcip)
21134eaa4710SRishi Srivatsavai {
21144eaa4710SRishi Srivatsavai 	uint16_t tci, vlanid;
21154eaa4710SRishi Srivatsavai 
21164eaa4710SRishi Srivatsavai 	if (hdr_info->mhi_bindsap == ETHERTYPE_VLAN) {
21174eaa4710SRishi Srivatsavai 		ptrdiff_t tpos = offsetof(struct ether_vlan_header, ether_tci);
21184eaa4710SRishi Srivatsavai 		ptrdiff_t mlen;
21194eaa4710SRishi Srivatsavai 
21204eaa4710SRishi Srivatsavai 		/*
21214eaa4710SRishi Srivatsavai 		 * Extract the VLAN ID information, regardless of alignment,
21224eaa4710SRishi Srivatsavai 		 * and without a pullup.  This isn't attractive, but we do this
21234eaa4710SRishi Srivatsavai 		 * to avoid having to deal with the pointers stashed in
21244eaa4710SRishi Srivatsavai 		 * hdr_info moving around or having the caller deal with a new
21254eaa4710SRishi Srivatsavai 		 * mblk_t pointer.
21264eaa4710SRishi Srivatsavai 		 */
21274eaa4710SRishi Srivatsavai 		while (mp != NULL) {
21284eaa4710SRishi Srivatsavai 			mlen = MBLKL(mp);
21294eaa4710SRishi Srivatsavai 			if (mlen > tpos && mlen > 0)
21304eaa4710SRishi Srivatsavai 				break;
21314eaa4710SRishi Srivatsavai 			tpos -= mlen;
21324eaa4710SRishi Srivatsavai 			mp = mp->b_cont;
21334eaa4710SRishi Srivatsavai 		}
21344eaa4710SRishi Srivatsavai 		if (mp == NULL)
21354eaa4710SRishi Srivatsavai 			return (B_FALSE);
21364eaa4710SRishi Srivatsavai 		tci = mp->b_rptr[tpos] << 8;
21374eaa4710SRishi Srivatsavai 		if (++tpos >= mlen) {
21384eaa4710SRishi Srivatsavai 			do {
21394eaa4710SRishi Srivatsavai 				mp = mp->b_cont;
21404eaa4710SRishi Srivatsavai 			} while (mp != NULL && MBLKL(mp) == 0);
21414eaa4710SRishi Srivatsavai 			if (mp == NULL)
21424eaa4710SRishi Srivatsavai 				return (B_FALSE);
21434eaa4710SRishi Srivatsavai 			tpos = 0;
21444eaa4710SRishi Srivatsavai 		}
21454eaa4710SRishi Srivatsavai 		tci |= mp->b_rptr[tpos];
21464eaa4710SRishi Srivatsavai 
21474eaa4710SRishi Srivatsavai 		vlanid = VLAN_ID(tci);
21484eaa4710SRishi Srivatsavai 		if (VLAN_CFI(tci) != ETHER_CFI || vlanid > VLAN_ID_MAX)
21494eaa4710SRishi Srivatsavai 			return (B_FALSE);
21504eaa4710SRishi Srivatsavai 		if (vlanid == VLAN_ID_NONE || vlanid == blp->bl_pvid)
21514eaa4710SRishi Srivatsavai 			goto input_no_vlan;
21524eaa4710SRishi Srivatsavai 		if (!BRIDGE_VLAN_ISSET(blp, vlanid))
21534eaa4710SRishi Srivatsavai 			return (B_FALSE);
21544eaa4710SRishi Srivatsavai 	} else {
21554eaa4710SRishi Srivatsavai 		tci = 0xFFFF;
21564eaa4710SRishi Srivatsavai input_no_vlan:
21574eaa4710SRishi Srivatsavai 		/*
21584eaa4710SRishi Srivatsavai 		 * If PVID is set to zero, then untagged traffic is not
21594eaa4710SRishi Srivatsavai 		 * supported here.  Do not learn or forward.
21604eaa4710SRishi Srivatsavai 		 */
21614eaa4710SRishi Srivatsavai 		if ((vlanid = blp->bl_pvid) == VLAN_ID_NONE)
21624eaa4710SRishi Srivatsavai 			return (B_FALSE);
21634eaa4710SRishi Srivatsavai 	}
21644eaa4710SRishi Srivatsavai 
21654eaa4710SRishi Srivatsavai 	*tcip = tci;
21664eaa4710SRishi Srivatsavai 	*vlanidp = vlanid;
21674eaa4710SRishi Srivatsavai 	return (B_TRUE);
21684eaa4710SRishi Srivatsavai }
21694eaa4710SRishi Srivatsavai 
21704eaa4710SRishi Srivatsavai /*
21714eaa4710SRishi Srivatsavai  * Handle MAC notifications.
21724eaa4710SRishi Srivatsavai  */
21734eaa4710SRishi Srivatsavai static void
21744eaa4710SRishi Srivatsavai bridge_notify_cb(void *arg, mac_notify_type_t note_type)
21754eaa4710SRishi Srivatsavai {
21764eaa4710SRishi Srivatsavai 	bridge_link_t *blp = arg;
21774eaa4710SRishi Srivatsavai 
21784eaa4710SRishi Srivatsavai 	switch (note_type) {
21794eaa4710SRishi Srivatsavai 	case MAC_NOTE_UNICST:
21804eaa4710SRishi Srivatsavai 		bridge_new_unicst(blp);
21814eaa4710SRishi Srivatsavai 		break;
21824eaa4710SRishi Srivatsavai 
21834eaa4710SRishi Srivatsavai 	case MAC_NOTE_SDU_SIZE: {
21844eaa4710SRishi Srivatsavai 		uint_t maxsdu;
21854eaa4710SRishi Srivatsavai 		bridge_inst_t *bip = blp->bl_inst;
21864eaa4710SRishi Srivatsavai 		bridge_mac_t *bmp = bip->bi_mac;
21874eaa4710SRishi Srivatsavai 		boolean_t notify = B_FALSE;
21884eaa4710SRishi Srivatsavai 		mblk_t *mlist = NULL;
21894eaa4710SRishi Srivatsavai 
21904eaa4710SRishi Srivatsavai 		mac_sdu_get(blp->bl_mh, NULL, &maxsdu);
21914eaa4710SRishi Srivatsavai 		rw_enter(&bip->bi_rwlock, RW_READER);
21924eaa4710SRishi Srivatsavai 		if (list_prev(&bip->bi_links, blp) == NULL &&
21934eaa4710SRishi Srivatsavai 		    list_next(&bip->bi_links, blp) == NULL) {
21944eaa4710SRishi Srivatsavai 			notify = (maxsdu != bmp->bm_maxsdu);
21954eaa4710SRishi Srivatsavai 			bmp->bm_maxsdu = maxsdu;
21964eaa4710SRishi Srivatsavai 		}
21974eaa4710SRishi Srivatsavai 		blp->bl_maxsdu = maxsdu;
21984eaa4710SRishi Srivatsavai 		if (maxsdu != bmp->bm_maxsdu)
21994eaa4710SRishi Srivatsavai 			link_sdu_fail(blp, B_TRUE, &mlist);
22004eaa4710SRishi Srivatsavai 		else if (notify)
22014eaa4710SRishi Srivatsavai 			(void) mac_maxsdu_update(bmp->bm_mh, maxsdu);
22024eaa4710SRishi Srivatsavai 		rw_exit(&bip->bi_rwlock);
22034eaa4710SRishi Srivatsavai 		send_up_messages(bip, mlist);
22044eaa4710SRishi Srivatsavai 		break;
22054eaa4710SRishi Srivatsavai 	}
22064eaa4710SRishi Srivatsavai 	}
22074eaa4710SRishi Srivatsavai }
22084eaa4710SRishi Srivatsavai 
22094eaa4710SRishi Srivatsavai /*
22104eaa4710SRishi Srivatsavai  * This is called by the MAC layer.  As with the transmit side, we're right in
22114eaa4710SRishi Srivatsavai  * the data path for all I/O on this port, so if we don't need to forward this
22124eaa4710SRishi Srivatsavai  * packet anywhere, we have to send it upwards via mac_rx_common.
22134eaa4710SRishi Srivatsavai  */
22144eaa4710SRishi Srivatsavai static void
22154eaa4710SRishi Srivatsavai bridge_recv_cb(mac_handle_t mh, mac_resource_handle_t rsrc, mblk_t *mpnext)
22164eaa4710SRishi Srivatsavai {
22174eaa4710SRishi Srivatsavai 	mblk_t *mp, *mpcopy;
22184eaa4710SRishi Srivatsavai 	bridge_link_t *blp = (bridge_link_t *)mh;
22194eaa4710SRishi Srivatsavai 	bridge_inst_t *bip = blp->bl_inst;
22204eaa4710SRishi Srivatsavai 	bridge_mac_t *bmp = bip->bi_mac;
22214eaa4710SRishi Srivatsavai 	mac_header_info_t hdr_info;
22224eaa4710SRishi Srivatsavai 	uint16_t vlanid, tci;
22234eaa4710SRishi Srivatsavai 	boolean_t trillmode = B_FALSE;
22244eaa4710SRishi Srivatsavai 
22254eaa4710SRishi Srivatsavai 	KIINCR(bki_recv);
22264eaa4710SRishi Srivatsavai 	KLINCR(bkl_recv);
22274eaa4710SRishi Srivatsavai 
22284eaa4710SRishi Srivatsavai 	/*
22294eaa4710SRishi Srivatsavai 	 * Regardless of state, check for inbound TRILL packets when TRILL is
22304eaa4710SRishi Srivatsavai 	 * active.  These are pulled out of band and sent for TRILL handling.
22314eaa4710SRishi Srivatsavai 	 */
22324eaa4710SRishi Srivatsavai 	if (blp->bl_trilldata != NULL) {
22334eaa4710SRishi Srivatsavai 		void *tdp;
22344eaa4710SRishi Srivatsavai 		mblk_t *newhead;
22354eaa4710SRishi Srivatsavai 		mblk_t *tail = NULL;
22364eaa4710SRishi Srivatsavai 
22374eaa4710SRishi Srivatsavai 		mutex_enter(&blp->bl_trilllock);
22384eaa4710SRishi Srivatsavai 		if ((tdp = blp->bl_trilldata) != NULL) {
22394eaa4710SRishi Srivatsavai 			blp->bl_trillthreads++;
22404eaa4710SRishi Srivatsavai 			mutex_exit(&blp->bl_trilllock);
22414eaa4710SRishi Srivatsavai 			trillmode = B_TRUE;
22424eaa4710SRishi Srivatsavai 			newhead = mpnext;
22434eaa4710SRishi Srivatsavai 			while ((mp = mpnext) != NULL) {
22444eaa4710SRishi Srivatsavai 				boolean_t raw_isis, bridge_group;
22454eaa4710SRishi Srivatsavai 
22464eaa4710SRishi Srivatsavai 				mpnext = mp->b_next;
22474eaa4710SRishi Srivatsavai 
22484eaa4710SRishi Srivatsavai 				/*
22494eaa4710SRishi Srivatsavai 				 * If the header isn't readable, then leave on
22504eaa4710SRishi Srivatsavai 				 * the list and continue.
22514eaa4710SRishi Srivatsavai 				 */
22524eaa4710SRishi Srivatsavai 				if (mac_header_info(blp->bl_mh, mp,
22534eaa4710SRishi Srivatsavai 				    &hdr_info) != 0) {
22544eaa4710SRishi Srivatsavai 					tail = mp;
22554eaa4710SRishi Srivatsavai 					continue;
22564eaa4710SRishi Srivatsavai 				}
22574eaa4710SRishi Srivatsavai 
22584eaa4710SRishi Srivatsavai 				/*
22594eaa4710SRishi Srivatsavai 				 * The TRILL document specifies that, on
22604eaa4710SRishi Srivatsavai 				 * Ethernet alone, IS-IS packets arrive with
22614eaa4710SRishi Srivatsavai 				 * LLC rather than Ethertype, and using a
22624eaa4710SRishi Srivatsavai 				 * specific destination address.  We must check
22634eaa4710SRishi Srivatsavai 				 * for that here.  Also, we need to give BPDUs
22644eaa4710SRishi Srivatsavai 				 * to TRILL for processing.
22654eaa4710SRishi Srivatsavai 				 */
22664eaa4710SRishi Srivatsavai 				raw_isis = bridge_group = B_FALSE;
22674eaa4710SRishi Srivatsavai 				if (hdr_info.mhi_dsttype ==
22684eaa4710SRishi Srivatsavai 				    MAC_ADDRTYPE_MULTICAST) {
22694eaa4710SRishi Srivatsavai 					if (memcmp(hdr_info.mhi_daddr,
22704eaa4710SRishi Srivatsavai 					    all_isis_rbridges, ETHERADDRL) == 0)
22714eaa4710SRishi Srivatsavai 						raw_isis = B_TRUE;
22724eaa4710SRishi Srivatsavai 					else if (memcmp(hdr_info.mhi_daddr,
22734eaa4710SRishi Srivatsavai 					    bridge_group_address, ETHERADDRL) ==
22744eaa4710SRishi Srivatsavai 					    0)
22754eaa4710SRishi Srivatsavai 						bridge_group = B_TRUE;
22764eaa4710SRishi Srivatsavai 				}
22774eaa4710SRishi Srivatsavai 				if (!raw_isis && !bridge_group &&
22784eaa4710SRishi Srivatsavai 				    hdr_info.mhi_bindsap != ETHERTYPE_TRILL &&
22794eaa4710SRishi Srivatsavai 				    (hdr_info.mhi_bindsap != ETHERTYPE_VLAN ||
22804eaa4710SRishi Srivatsavai 				    /* LINTED: alignment */
22814eaa4710SRishi Srivatsavai 				    ((struct ether_vlan_header *)mp->b_rptr)->
22824eaa4710SRishi Srivatsavai 				    ether_type != htons(ETHERTYPE_TRILL))) {
22834eaa4710SRishi Srivatsavai 					tail = mp;
22844eaa4710SRishi Srivatsavai 					continue;
22854eaa4710SRishi Srivatsavai 				}
22864eaa4710SRishi Srivatsavai 
22874eaa4710SRishi Srivatsavai 				/*
22884eaa4710SRishi Srivatsavai 				 * We've got TRILL input.  Remove from the list
22894eaa4710SRishi Srivatsavai 				 * and send up through the TRILL module.  (Send
22904eaa4710SRishi Srivatsavai 				 * a copy through promiscuous receive just to
22914eaa4710SRishi Srivatsavai 				 * support snooping on TRILL.  Order isn't
22924eaa4710SRishi Srivatsavai 				 * preserved strictly, but that doesn't matter
22934eaa4710SRishi Srivatsavai 				 * here.)
22944eaa4710SRishi Srivatsavai 				 */
22954eaa4710SRishi Srivatsavai 				if (tail != NULL)
22964eaa4710SRishi Srivatsavai 					tail->b_next = mpnext;
22974eaa4710SRishi Srivatsavai 				mp->b_next = NULL;
22984eaa4710SRishi Srivatsavai 				if (mp == newhead)
22994eaa4710SRishi Srivatsavai 					newhead = mpnext;
23004eaa4710SRishi Srivatsavai 				mac_trill_snoop(blp->bl_mh, mp);
23014eaa4710SRishi Srivatsavai 				update_header(mp, &hdr_info, B_TRUE);
23024eaa4710SRishi Srivatsavai 				/*
23034eaa4710SRishi Srivatsavai 				 * On raw IS-IS and BPDU frames, we have to
23044eaa4710SRishi Srivatsavai 				 * make sure that the length is trimmed
23054eaa4710SRishi Srivatsavai 				 * properly.  We use origsap in order to cope
23064eaa4710SRishi Srivatsavai 				 * with jumbograms for IS-IS.  (Regular mac
23074eaa4710SRishi Srivatsavai 				 * can't.)
23084eaa4710SRishi Srivatsavai 				 */
23094eaa4710SRishi Srivatsavai 				if (raw_isis || bridge_group) {
23104eaa4710SRishi Srivatsavai 					size_t msglen = msgdsize(mp);
23114eaa4710SRishi Srivatsavai 
23124eaa4710SRishi Srivatsavai 					if (msglen > hdr_info.mhi_origsap) {
23134eaa4710SRishi Srivatsavai 						(void) adjmsg(mp,
23144eaa4710SRishi Srivatsavai 						    hdr_info.mhi_origsap -
23154eaa4710SRishi Srivatsavai 						    msglen);
23164eaa4710SRishi Srivatsavai 					} else if (msglen <
23174eaa4710SRishi Srivatsavai 					    hdr_info.mhi_origsap) {
23184eaa4710SRishi Srivatsavai 						freemsg(mp);
23194eaa4710SRishi Srivatsavai 						continue;
23204eaa4710SRishi Srivatsavai 					}
23214eaa4710SRishi Srivatsavai 				}
23224eaa4710SRishi Srivatsavai 				trill_recv_fn(tdp, blp, rsrc, mp, &hdr_info);
23234eaa4710SRishi Srivatsavai 			}
23244eaa4710SRishi Srivatsavai 			mpnext = newhead;
23254eaa4710SRishi Srivatsavai 			mutex_enter(&blp->bl_trilllock);
23264eaa4710SRishi Srivatsavai 			if (--blp->bl_trillthreads == 0 &&
23274eaa4710SRishi Srivatsavai 			    blp->bl_trilldata == NULL)
23284eaa4710SRishi Srivatsavai 				cv_broadcast(&blp->bl_trillwait);
23294eaa4710SRishi Srivatsavai 		}
23304eaa4710SRishi Srivatsavai 		mutex_exit(&blp->bl_trilllock);
23314eaa4710SRishi Srivatsavai 		if (mpnext == NULL)
23324eaa4710SRishi Srivatsavai 			return;
23334eaa4710SRishi Srivatsavai 	}
23344eaa4710SRishi Srivatsavai 
23354eaa4710SRishi Srivatsavai 	/*
23364eaa4710SRishi Srivatsavai 	 * If this is a TRILL RBridge, then just check whether this link is
23374eaa4710SRishi Srivatsavai 	 * used at all for forwarding.  If not, then we're done.
23384eaa4710SRishi Srivatsavai 	 */
23394eaa4710SRishi Srivatsavai 	if (trillmode) {
23404eaa4710SRishi Srivatsavai 		if (!(blp->bl_flags & BLF_TRILLACTIVE) ||
23414eaa4710SRishi Srivatsavai 		    (blp->bl_flags & BLF_SDUFAIL)) {
23424eaa4710SRishi Srivatsavai 			mac_rx_common(blp->bl_mh, rsrc, mpnext);
23434eaa4710SRishi Srivatsavai 			return;
23444eaa4710SRishi Srivatsavai 		}
23454eaa4710SRishi Srivatsavai 	} else {
23464eaa4710SRishi Srivatsavai 		/*
23474eaa4710SRishi Srivatsavai 		 * For regular (STP) bridges, if we're in blocking or listening
23484eaa4710SRishi Srivatsavai 		 * state, then do nothing.  We don't learn or forward until
23494eaa4710SRishi Srivatsavai 		 * told to do so.
23504eaa4710SRishi Srivatsavai 		 */
23514eaa4710SRishi Srivatsavai 		if (blp->bl_state == BLS_BLOCKLISTEN) {
23524eaa4710SRishi Srivatsavai 			mac_rx_common(blp->bl_mh, rsrc, mpnext);
23534eaa4710SRishi Srivatsavai 			return;
23544eaa4710SRishi Srivatsavai 		}
23554eaa4710SRishi Srivatsavai 	}
23564eaa4710SRishi Srivatsavai 
23574eaa4710SRishi Srivatsavai 	/*
23584eaa4710SRishi Srivatsavai 	 * Send a copy of the message chain up to the observability node users.
23594eaa4710SRishi Srivatsavai 	 * For TRILL, we must obey the VLAN AF rules, so we go packet-by-
23604eaa4710SRishi Srivatsavai 	 * packet.
23614eaa4710SRishi Srivatsavai 	 */
23624eaa4710SRishi Srivatsavai 	if (!trillmode && blp->bl_state == BLS_FORWARDING &&
23634eaa4710SRishi Srivatsavai 	    (bmp->bm_flags & BMF_STARTED) &&
23644eaa4710SRishi Srivatsavai 	    (mp = copymsgchain(mpnext)) != NULL) {
23654eaa4710SRishi Srivatsavai 		mac_rx(bmp->bm_mh, NULL, mp);
23664eaa4710SRishi Srivatsavai 	}
23674eaa4710SRishi Srivatsavai 
23684eaa4710SRishi Srivatsavai 	/*
23694eaa4710SRishi Srivatsavai 	 * We must be in learning or forwarding state, or using TRILL on a link
23704eaa4710SRishi Srivatsavai 	 * with one or more VLANs active.  For each packet in the list, process
23714eaa4710SRishi Srivatsavai 	 * the source address, and then attempt to forward.
23724eaa4710SRishi Srivatsavai 	 */
23734eaa4710SRishi Srivatsavai 	while ((mp = mpnext) != NULL) {
23744eaa4710SRishi Srivatsavai 		mpnext = mp->b_next;
23754eaa4710SRishi Srivatsavai 		mp->b_next = NULL;
23764eaa4710SRishi Srivatsavai 
23774eaa4710SRishi Srivatsavai 		/*
23784eaa4710SRishi Srivatsavai 		 * If we can't decode the header or if the header specifies a
23794eaa4710SRishi Srivatsavai 		 * multicast source address (impossible!), then don't bother
23804eaa4710SRishi Srivatsavai 		 * learning or forwarding, but go ahead and forward up the
23814eaa4710SRishi Srivatsavai 		 * stack for subsequent processing.
23824eaa4710SRishi Srivatsavai 		 */
23834eaa4710SRishi Srivatsavai 		if (mac_header_info(blp->bl_mh, mp, &hdr_info) != 0 ||
23844eaa4710SRishi Srivatsavai 		    (hdr_info.mhi_saddr[0] & 1) != 0) {
23854eaa4710SRishi Srivatsavai 			KIINCR(bki_drops);
23864eaa4710SRishi Srivatsavai 			KLINCR(bkl_drops);
23874eaa4710SRishi Srivatsavai 			mac_rx_common(blp->bl_mh, rsrc, mp);
23884eaa4710SRishi Srivatsavai 			continue;
23894eaa4710SRishi Srivatsavai 		}
23904eaa4710SRishi Srivatsavai 
23914eaa4710SRishi Srivatsavai 		/*
23924eaa4710SRishi Srivatsavai 		 * Extract and validate the VLAN ID for this packet.
23934eaa4710SRishi Srivatsavai 		 */
23944eaa4710SRishi Srivatsavai 		if (!bridge_get_vlan(blp, &hdr_info, mp, &vlanid, &tci) ||
23954eaa4710SRishi Srivatsavai 		    !BRIDGE_AF_ISSET(blp, vlanid)) {
23964eaa4710SRishi Srivatsavai 			mac_rx_common(blp->bl_mh, rsrc, mp);
23974eaa4710SRishi Srivatsavai 			continue;
23984eaa4710SRishi Srivatsavai 		}
23994eaa4710SRishi Srivatsavai 
24004eaa4710SRishi Srivatsavai 		if (trillmode) {
24014eaa4710SRishi Srivatsavai 			/*
24024eaa4710SRishi Srivatsavai 			 * Special test required by TRILL document: must
24034eaa4710SRishi Srivatsavai 			 * discard frames with outer address set to ESADI.
24044eaa4710SRishi Srivatsavai 			 */
24054eaa4710SRishi Srivatsavai 			if (memcmp(hdr_info.mhi_daddr, all_esadi_rbridges,
24064eaa4710SRishi Srivatsavai 			    ETHERADDRL) == 0) {
24074eaa4710SRishi Srivatsavai 				mac_rx_common(blp->bl_mh, rsrc, mp);
24084eaa4710SRishi Srivatsavai 				continue;
24094eaa4710SRishi Srivatsavai 			}
24104eaa4710SRishi Srivatsavai 
24114eaa4710SRishi Srivatsavai 			/*
24124eaa4710SRishi Srivatsavai 			 * If we're in TRILL mode, then the call above to get
24134eaa4710SRishi Srivatsavai 			 * the VLAN ID has also checked that we're the
24144eaa4710SRishi Srivatsavai 			 * appointed forwarder, so report that we're handling
24154eaa4710SRishi Srivatsavai 			 * this packet to any observability node users.
24164eaa4710SRishi Srivatsavai 			 */
24174eaa4710SRishi Srivatsavai 			if ((bmp->bm_flags & BMF_STARTED) &&
24184eaa4710SRishi Srivatsavai 			    (mpcopy = copymsg(mp)) != NULL)
24194eaa4710SRishi Srivatsavai 				mac_rx(bmp->bm_mh, NULL, mpcopy);
24204eaa4710SRishi Srivatsavai 		}
24214eaa4710SRishi Srivatsavai 
24224eaa4710SRishi Srivatsavai 		/*
24234eaa4710SRishi Srivatsavai 		 * First process the source address and learn from it.  For
24244eaa4710SRishi Srivatsavai 		 * TRILL, we learn only if we're the appointed forwarder.
24254eaa4710SRishi Srivatsavai 		 */
24264eaa4710SRishi Srivatsavai 		bridge_learn(blp, hdr_info.mhi_saddr, RBRIDGE_NICKNAME_NONE,
24274eaa4710SRishi Srivatsavai 		    vlanid);
24284eaa4710SRishi Srivatsavai 
24294eaa4710SRishi Srivatsavai 		/*
24304eaa4710SRishi Srivatsavai 		 * Now check whether we're forwarding and look up the
24314eaa4710SRishi Srivatsavai 		 * destination.  If we can forward, do so.
24324eaa4710SRishi Srivatsavai 		 */
24334eaa4710SRishi Srivatsavai 		if (trillmode || blp->bl_state == BLS_FORWARDING) {
24344eaa4710SRishi Srivatsavai 			mp = bridge_forward(blp, &hdr_info, mp, vlanid, tci,
24354eaa4710SRishi Srivatsavai 			    B_FALSE, B_FALSE);
24364eaa4710SRishi Srivatsavai 		}
24374eaa4710SRishi Srivatsavai 		if (mp != NULL)
24384eaa4710SRishi Srivatsavai 			mac_rx_common(blp->bl_mh, rsrc, mp);
24394eaa4710SRishi Srivatsavai 	}
24404eaa4710SRishi Srivatsavai }
24414eaa4710SRishi Srivatsavai 
24424eaa4710SRishi Srivatsavai 
24434eaa4710SRishi Srivatsavai /* ARGSUSED */
24444eaa4710SRishi Srivatsavai static mblk_t *
24454eaa4710SRishi Srivatsavai bridge_xmit_cb(mac_handle_t mh, mac_ring_handle_t rh, mblk_t *mpnext)
24464eaa4710SRishi Srivatsavai {
24474eaa4710SRishi Srivatsavai 	bridge_link_t *blp = (bridge_link_t *)mh;
24484eaa4710SRishi Srivatsavai 	bridge_inst_t *bip = blp->bl_inst;
24494eaa4710SRishi Srivatsavai 	bridge_mac_t *bmp = bip->bi_mac;
24504eaa4710SRishi Srivatsavai 	mac_header_info_t hdr_info;
24514eaa4710SRishi Srivatsavai 	uint16_t vlanid, tci;
24524eaa4710SRishi Srivatsavai 	mblk_t *mp, *mpcopy;
24534eaa4710SRishi Srivatsavai 	boolean_t trillmode;
24544eaa4710SRishi Srivatsavai 
24554eaa4710SRishi Srivatsavai 	trillmode = blp->bl_trilldata != NULL;
24564eaa4710SRishi Srivatsavai 
24574eaa4710SRishi Srivatsavai 	/*
24584eaa4710SRishi Srivatsavai 	 * If we're using STP and we're in blocking or listening state, or if
24594eaa4710SRishi Srivatsavai 	 * we're using TRILL and no VLANs are active, then behave as though the
24604eaa4710SRishi Srivatsavai 	 * bridge isn't here at all, and send on the local link alone.
24614eaa4710SRishi Srivatsavai 	 */
24624eaa4710SRishi Srivatsavai 	if ((!trillmode && blp->bl_state == BLS_BLOCKLISTEN) ||
24634eaa4710SRishi Srivatsavai 	    (trillmode &&
24644eaa4710SRishi Srivatsavai 	    (!(blp->bl_flags & BLF_TRILLACTIVE) ||
24654eaa4710SRishi Srivatsavai 	    (blp->bl_flags & BLF_SDUFAIL)))) {
24664eaa4710SRishi Srivatsavai 		KIINCR(bki_sent);
24674eaa4710SRishi Srivatsavai 		KLINCR(bkl_xmit);
24684eaa4710SRishi Srivatsavai 		MAC_RING_TX(blp->bl_mh, rh, mpnext, mp);
24694eaa4710SRishi Srivatsavai 		return (mp);
24704eaa4710SRishi Srivatsavai 	}
24714eaa4710SRishi Srivatsavai 
24724eaa4710SRishi Srivatsavai 	/*
24734eaa4710SRishi Srivatsavai 	 * Send a copy of the message up to the observability node users.
24744eaa4710SRishi Srivatsavai 	 * TRILL needs to check on a packet-by-packet basis.
24754eaa4710SRishi Srivatsavai 	 */
24764eaa4710SRishi Srivatsavai 	if (!trillmode && blp->bl_state == BLS_FORWARDING &&
24774eaa4710SRishi Srivatsavai 	    (bmp->bm_flags & BMF_STARTED) &&
24784eaa4710SRishi Srivatsavai 	    (mp = copymsgchain(mpnext)) != NULL) {
24794eaa4710SRishi Srivatsavai 		mac_rx(bmp->bm_mh, NULL, mp);
24804eaa4710SRishi Srivatsavai 	}
24814eaa4710SRishi Srivatsavai 
24824eaa4710SRishi Srivatsavai 	while ((mp = mpnext) != NULL) {
24834eaa4710SRishi Srivatsavai 		mpnext = mp->b_next;
24844eaa4710SRishi Srivatsavai 		mp->b_next = NULL;
24854eaa4710SRishi Srivatsavai 
24864eaa4710SRishi Srivatsavai 		if (mac_header_info(blp->bl_mh, mp, &hdr_info) != 0) {
24874eaa4710SRishi Srivatsavai 			freemsg(mp);
24884eaa4710SRishi Srivatsavai 			continue;
24894eaa4710SRishi Srivatsavai 		}
24904eaa4710SRishi Srivatsavai 
24914eaa4710SRishi Srivatsavai 		/*
24924eaa4710SRishi Srivatsavai 		 * Extract and validate the VLAN ID for this packet.
24934eaa4710SRishi Srivatsavai 		 */
24944eaa4710SRishi Srivatsavai 		if (!bridge_get_vlan(blp, &hdr_info, mp, &vlanid, &tci) ||
24954eaa4710SRishi Srivatsavai 		    !BRIDGE_AF_ISSET(blp, vlanid)) {
24964eaa4710SRishi Srivatsavai 			freemsg(mp);
24974eaa4710SRishi Srivatsavai 			continue;
24984eaa4710SRishi Srivatsavai 		}
24994eaa4710SRishi Srivatsavai 
25004eaa4710SRishi Srivatsavai 		/*
25014eaa4710SRishi Srivatsavai 		 * If we're using TRILL, then we've now validated that we're
25024eaa4710SRishi Srivatsavai 		 * the forwarder for this VLAN, so go ahead and let
25034eaa4710SRishi Srivatsavai 		 * observability node users know about the packet.
25044eaa4710SRishi Srivatsavai 		 */
25054eaa4710SRishi Srivatsavai 		if (trillmode && (bmp->bm_flags & BMF_STARTED) &&
25064eaa4710SRishi Srivatsavai 		    (mpcopy = copymsg(mp)) != NULL) {
25074eaa4710SRishi Srivatsavai 			mac_rx(bmp->bm_mh, NULL, mpcopy);
25084eaa4710SRishi Srivatsavai 		}
25094eaa4710SRishi Srivatsavai 
25104eaa4710SRishi Srivatsavai 		/*
25114eaa4710SRishi Srivatsavai 		 * We have to learn from our own transmitted packets, because
25124eaa4710SRishi Srivatsavai 		 * there may be a Solaris DLPI raw sender (who can specify his
25134eaa4710SRishi Srivatsavai 		 * own source address) using promiscuous mode for receive.  The
25144eaa4710SRishi Srivatsavai 		 * mac layer information won't (and can't) tell us everything
25154eaa4710SRishi Srivatsavai 		 * we need to know.
25164eaa4710SRishi Srivatsavai 		 */
25174eaa4710SRishi Srivatsavai 		bridge_learn(blp, hdr_info.mhi_saddr, RBRIDGE_NICKNAME_NONE,
25184eaa4710SRishi Srivatsavai 		    vlanid);
25194eaa4710SRishi Srivatsavai 
25204eaa4710SRishi Srivatsavai 		/* attempt forwarding */
25214eaa4710SRishi Srivatsavai 		if (trillmode || blp->bl_state == BLS_FORWARDING) {
25224eaa4710SRishi Srivatsavai 			mp = bridge_forward(blp, &hdr_info, mp, vlanid, tci,
25234eaa4710SRishi Srivatsavai 			    B_FALSE, B_TRUE);
25244eaa4710SRishi Srivatsavai 		}
25254eaa4710SRishi Srivatsavai 		if (mp != NULL) {
25264eaa4710SRishi Srivatsavai 			MAC_RING_TX(blp->bl_mh, rh, mp, mp);
25274eaa4710SRishi Srivatsavai 			if (mp == NULL) {
25284eaa4710SRishi Srivatsavai 				KIINCR(bki_sent);
25294eaa4710SRishi Srivatsavai 				KLINCR(bkl_xmit);
25304eaa4710SRishi Srivatsavai 			}
25314eaa4710SRishi Srivatsavai 		}
25324eaa4710SRishi Srivatsavai 		/*
25334eaa4710SRishi Srivatsavai 		 * If we get stuck, then stop.  Don't let the user's output
25344eaa4710SRishi Srivatsavai 		 * packets get out of order.  (More importantly: don't try to
25354eaa4710SRishi Srivatsavai 		 * bridge the same packet multiple times if flow control is
25364eaa4710SRishi Srivatsavai 		 * asserted.)
25374eaa4710SRishi Srivatsavai 		 */
25384eaa4710SRishi Srivatsavai 		if (mp != NULL) {
25394eaa4710SRishi Srivatsavai 			mp->b_next = mpnext;
25404eaa4710SRishi Srivatsavai 			break;
25414eaa4710SRishi Srivatsavai 		}
25424eaa4710SRishi Srivatsavai 	}
25434eaa4710SRishi Srivatsavai 	return (mp);
25444eaa4710SRishi Srivatsavai }
25454eaa4710SRishi Srivatsavai 
25464eaa4710SRishi Srivatsavai /*
25474eaa4710SRishi Srivatsavai  * This is called by TRILL when it decapsulates an packet, and we must forward
25484eaa4710SRishi Srivatsavai  * locally.  On failure, we just drop.
25494eaa4710SRishi Srivatsavai  *
25504eaa4710SRishi Srivatsavai  * Note that the ingress_nick reported by TRILL must not represent this local
25514eaa4710SRishi Srivatsavai  * node.
25524eaa4710SRishi Srivatsavai  */
25534eaa4710SRishi Srivatsavai void
25544eaa4710SRishi Srivatsavai bridge_trill_decaps(bridge_link_t *blp, mblk_t *mp, uint16_t ingress_nick)
25554eaa4710SRishi Srivatsavai {
25564eaa4710SRishi Srivatsavai 	mac_header_info_t hdr_info;
25574eaa4710SRishi Srivatsavai 	uint16_t vlanid, tci;
25584eaa4710SRishi Srivatsavai 	bridge_inst_t *bip = blp->bl_inst;	/* used by macros */
25594eaa4710SRishi Srivatsavai 	mblk_t *mpcopy;
25604eaa4710SRishi Srivatsavai 
25614eaa4710SRishi Srivatsavai 	if (mac_header_info(blp->bl_mh, mp, &hdr_info) != 0) {
25624eaa4710SRishi Srivatsavai 		freemsg(mp);
25634eaa4710SRishi Srivatsavai 		return;
25644eaa4710SRishi Srivatsavai 	}
25654eaa4710SRishi Srivatsavai 
25664eaa4710SRishi Srivatsavai 	/* Extract VLAN ID for this packet. */
25674eaa4710SRishi Srivatsavai 	if (hdr_info.mhi_bindsap == ETHERTYPE_VLAN) {
25684eaa4710SRishi Srivatsavai 		struct ether_vlan_header *evhp;
25694eaa4710SRishi Srivatsavai 
25704eaa4710SRishi Srivatsavai 		/* LINTED: alignment */
25714eaa4710SRishi Srivatsavai 		evhp = (struct ether_vlan_header *)mp->b_rptr;
25724eaa4710SRishi Srivatsavai 		tci = ntohs(evhp->ether_tci);
25734eaa4710SRishi Srivatsavai 		vlanid = VLAN_ID(tci);
25744eaa4710SRishi Srivatsavai 	} else {
25754eaa4710SRishi Srivatsavai 		/* Inner VLAN headers are required in TRILL data packets */
25764eaa4710SRishi Srivatsavai 		DTRACE_PROBE3(bridge__trill__decaps__novlan, bridge_link_t *,
25774eaa4710SRishi Srivatsavai 		    blp, mblk_t *, mp, uint16_t, ingress_nick);
25784eaa4710SRishi Srivatsavai 		freemsg(mp);
25794eaa4710SRishi Srivatsavai 		return;
25804eaa4710SRishi Srivatsavai 	}
25814eaa4710SRishi Srivatsavai 
25824eaa4710SRishi Srivatsavai 	/* Learn the location of this sender in the RBridge network */
25834eaa4710SRishi Srivatsavai 	bridge_learn(blp, hdr_info.mhi_saddr, ingress_nick, vlanid);
25844eaa4710SRishi Srivatsavai 
25854eaa4710SRishi Srivatsavai 	/* attempt forwarding */
25864eaa4710SRishi Srivatsavai 	mp = bridge_forward(blp, &hdr_info, mp, vlanid, tci, B_TRUE, B_TRUE);
25874eaa4710SRishi Srivatsavai 	if (mp != NULL) {
25884eaa4710SRishi Srivatsavai 		if (bridge_can_send(blp, vlanid)) {
25894eaa4710SRishi Srivatsavai 			/* Deliver a copy locally as well */
25904eaa4710SRishi Srivatsavai 			if ((mpcopy = copymsg(mp)) != NULL)
25914eaa4710SRishi Srivatsavai 				mac_rx_common(blp->bl_mh, NULL, mpcopy);
25924eaa4710SRishi Srivatsavai 			MAC_RING_TX(blp->bl_mh, NULL, mp, mp);
25934eaa4710SRishi Srivatsavai 		}
25944eaa4710SRishi Srivatsavai 		if (mp == NULL) {
25954eaa4710SRishi Srivatsavai 			KIINCR(bki_sent);
25964eaa4710SRishi Srivatsavai 			KLINCR(bkl_xmit);
25974eaa4710SRishi Srivatsavai 		} else {
25984eaa4710SRishi Srivatsavai 			freemsg(mp);
25994eaa4710SRishi Srivatsavai 		}
26004eaa4710SRishi Srivatsavai 	}
26014eaa4710SRishi Srivatsavai }
26024eaa4710SRishi Srivatsavai 
26034eaa4710SRishi Srivatsavai /*
26044eaa4710SRishi Srivatsavai  * This function is used by TRILL _only_ to transmit TRILL-encapsulated
26054eaa4710SRishi Srivatsavai  * packets.  It sends on a single underlying link and does not bridge.
26064eaa4710SRishi Srivatsavai  */
26074eaa4710SRishi Srivatsavai mblk_t *
26084eaa4710SRishi Srivatsavai bridge_trill_output(bridge_link_t *blp, mblk_t *mp)
26094eaa4710SRishi Srivatsavai {
26104eaa4710SRishi Srivatsavai 	bridge_inst_t *bip = blp->bl_inst;	/* used by macros */
26114eaa4710SRishi Srivatsavai 
26124eaa4710SRishi Srivatsavai 	mac_trill_snoop(blp->bl_mh, mp);
26134eaa4710SRishi Srivatsavai 	MAC_RING_TX(blp->bl_mh, NULL, mp, mp);
26144eaa4710SRishi Srivatsavai 	if (mp == NULL) {
26154eaa4710SRishi Srivatsavai 		KIINCR(bki_sent);
26164eaa4710SRishi Srivatsavai 		KLINCR(bkl_xmit);
26174eaa4710SRishi Srivatsavai 	}
26184eaa4710SRishi Srivatsavai 	return (mp);
26194eaa4710SRishi Srivatsavai }
26204eaa4710SRishi Srivatsavai 
26214eaa4710SRishi Srivatsavai /*
26224eaa4710SRishi Srivatsavai  * Set the "appointed forwarder" flag array for this link.  TRILL controls
26234eaa4710SRishi Srivatsavai  * forwarding on a VLAN basis.  The "trillactive" flag is an optimization for
26244eaa4710SRishi Srivatsavai  * the forwarder.
26254eaa4710SRishi Srivatsavai  */
26264eaa4710SRishi Srivatsavai void
26274eaa4710SRishi Srivatsavai bridge_trill_setvlans(bridge_link_t *blp, const uint8_t *arr)
26284eaa4710SRishi Srivatsavai {
26294eaa4710SRishi Srivatsavai 	int i;
26304eaa4710SRishi Srivatsavai 	uint_t newflags = 0;
26314eaa4710SRishi Srivatsavai 
26324eaa4710SRishi Srivatsavai 	for (i = 0; i < BRIDGE_VLAN_ARR_SIZE; i++) {
26334eaa4710SRishi Srivatsavai 		if ((blp->bl_afs[i] = arr[i]) != 0)
26344eaa4710SRishi Srivatsavai 			newflags = BLF_TRILLACTIVE;
26354eaa4710SRishi Srivatsavai 	}
26364eaa4710SRishi Srivatsavai 	blp->bl_flags = (blp->bl_flags & ~BLF_TRILLACTIVE) | newflags;
26374eaa4710SRishi Srivatsavai }
26384eaa4710SRishi Srivatsavai 
26394eaa4710SRishi Srivatsavai void
26404eaa4710SRishi Srivatsavai bridge_trill_flush(bridge_link_t *blp, uint16_t vlan, boolean_t dotrill)
26414eaa4710SRishi Srivatsavai {
26424eaa4710SRishi Srivatsavai 	bridge_inst_t *bip = blp->bl_inst;
26434eaa4710SRishi Srivatsavai 	bridge_fwd_t *bfp, *bfnext;
26444eaa4710SRishi Srivatsavai 	avl_tree_t fwd_scavenge;
26454eaa4710SRishi Srivatsavai 	int i;
26464eaa4710SRishi Srivatsavai 
26474eaa4710SRishi Srivatsavai 	_NOTE(ARGUNUSED(vlan));
26484eaa4710SRishi Srivatsavai 
26494eaa4710SRishi Srivatsavai 	avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t),
26504eaa4710SRishi Srivatsavai 	    offsetof(bridge_fwd_t, bf_node));
26514eaa4710SRishi Srivatsavai 	rw_enter(&bip->bi_rwlock, RW_WRITER);
26524eaa4710SRishi Srivatsavai 	bfnext = avl_first(&bip->bi_fwd);
26534eaa4710SRishi Srivatsavai 	while ((bfp = bfnext) != NULL) {
26544eaa4710SRishi Srivatsavai 		bfnext = AVL_NEXT(&bip->bi_fwd, bfp);
26554eaa4710SRishi Srivatsavai 		if (bfp->bf_flags & BFF_LOCALADDR)
26564eaa4710SRishi Srivatsavai 			continue;
26574eaa4710SRishi Srivatsavai 		if (dotrill) {
26584eaa4710SRishi Srivatsavai 			/* port doesn't matter if we're flushing TRILL */
26594eaa4710SRishi Srivatsavai 			if (bfp->bf_trill_nick == RBRIDGE_NICKNAME_NONE)
26604eaa4710SRishi Srivatsavai 				continue;
26614eaa4710SRishi Srivatsavai 		} else {
26624eaa4710SRishi Srivatsavai 			if (bfp->bf_trill_nick != RBRIDGE_NICKNAME_NONE)
26634eaa4710SRishi Srivatsavai 				continue;
26644eaa4710SRishi Srivatsavai 			for (i = 0; i < bfp->bf_nlinks; i++) {
26654eaa4710SRishi Srivatsavai 				if (bfp->bf_links[i] == blp)
26664eaa4710SRishi Srivatsavai 					break;
26674eaa4710SRishi Srivatsavai 			}
26684eaa4710SRishi Srivatsavai 			if (i >= bfp->bf_nlinks)
26694eaa4710SRishi Srivatsavai 				continue;
26704eaa4710SRishi Srivatsavai 		}
26714eaa4710SRishi Srivatsavai 		ASSERT(bfp->bf_flags & BFF_INTREE);
26724eaa4710SRishi Srivatsavai 		avl_remove(&bip->bi_fwd, bfp);
26734eaa4710SRishi Srivatsavai 		bfp->bf_flags &= ~BFF_INTREE;
26744eaa4710SRishi Srivatsavai 		avl_add(&fwd_scavenge, bfp);
26754eaa4710SRishi Srivatsavai 	}
26764eaa4710SRishi Srivatsavai 	rw_exit(&bip->bi_rwlock);
26774eaa4710SRishi Srivatsavai 	bfnext = avl_first(&fwd_scavenge);
26784eaa4710SRishi Srivatsavai 	while ((bfp = bfnext) != NULL) {
26794eaa4710SRishi Srivatsavai 		bfnext = AVL_NEXT(&fwd_scavenge, bfp);
26804eaa4710SRishi Srivatsavai 		avl_remove(&fwd_scavenge, bfp);
26814eaa4710SRishi Srivatsavai 		fwd_unref(bfp);
26824eaa4710SRishi Srivatsavai 	}
26834eaa4710SRishi Srivatsavai 	avl_destroy(&fwd_scavenge);
26844eaa4710SRishi Srivatsavai }
26854eaa4710SRishi Srivatsavai 
26864eaa4710SRishi Srivatsavai /*
26874eaa4710SRishi Srivatsavai  * Let the mac module take or drop a reference to a bridge link.  When this is
26884eaa4710SRishi Srivatsavai  * called, the mac module is holding the mi_bridge_lock, so the link cannot be
26894eaa4710SRishi Srivatsavai  * in the process of entering or leaving a bridge.
26904eaa4710SRishi Srivatsavai  */
26914eaa4710SRishi Srivatsavai static void
26924eaa4710SRishi Srivatsavai bridge_ref_cb(mac_handle_t mh, boolean_t hold)
26934eaa4710SRishi Srivatsavai {
26944eaa4710SRishi Srivatsavai 	bridge_link_t *blp = (bridge_link_t *)mh;
26954eaa4710SRishi Srivatsavai 
26964eaa4710SRishi Srivatsavai 	if (hold)
26974eaa4710SRishi Srivatsavai 		atomic_inc_uint(&blp->bl_refs);
26984eaa4710SRishi Srivatsavai 	else
26994eaa4710SRishi Srivatsavai 		link_unref(blp);
27004eaa4710SRishi Srivatsavai }
27014eaa4710SRishi Srivatsavai 
27024eaa4710SRishi Srivatsavai /*
27034eaa4710SRishi Srivatsavai  * Handle link state changes reported by the mac layer.  This acts as a filter
27044eaa4710SRishi Srivatsavai  * for link state changes: if a link is reporting down, but there are other
27054eaa4710SRishi Srivatsavai  * links still up on the bridge, then the state is changed to "up."  When the
27064eaa4710SRishi Srivatsavai  * last link goes down, all are marked down, and when the first link goes up,
27074eaa4710SRishi Srivatsavai  * all are marked up.  (Recursion is avoided by the use of the "redo" function.)
27084eaa4710SRishi Srivatsavai  *
27094eaa4710SRishi Srivatsavai  * We treat unknown as equivalent to "up."
27104eaa4710SRishi Srivatsavai  */
27114eaa4710SRishi Srivatsavai static link_state_t
27124eaa4710SRishi Srivatsavai bridge_ls_cb(mac_handle_t mh, link_state_t newls)
27134eaa4710SRishi Srivatsavai {
27144eaa4710SRishi Srivatsavai 	bridge_link_t *blp = (bridge_link_t *)mh;
27154eaa4710SRishi Srivatsavai 	bridge_link_t *blcmp;
27164eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
27174eaa4710SRishi Srivatsavai 	bridge_mac_t *bmp;
27184eaa4710SRishi Srivatsavai 
27194eaa4710SRishi Srivatsavai 	if (newls != LINK_STATE_DOWN && blp->bl_linkstate != LINK_STATE_DOWN ||
27204eaa4710SRishi Srivatsavai 	    (blp->bl_flags & (BLF_DELETED|BLF_SDUFAIL))) {
27214eaa4710SRishi Srivatsavai 		blp->bl_linkstate = newls;
27224eaa4710SRishi Srivatsavai 		return (newls);
27234eaa4710SRishi Srivatsavai 	}
27244eaa4710SRishi Srivatsavai 
27254eaa4710SRishi Srivatsavai 	/*
27264eaa4710SRishi Srivatsavai 	 * Scan first to see if there are any other non-down links.  If there
27274eaa4710SRishi Srivatsavai 	 * are, then we're done.  Otherwise, if all others are down, then the
27284eaa4710SRishi Srivatsavai 	 * state of this link is the state of the bridge.
27294eaa4710SRishi Srivatsavai 	 */
27304eaa4710SRishi Srivatsavai 	bip = blp->bl_inst;
27314eaa4710SRishi Srivatsavai 	rw_enter(&bip->bi_rwlock, RW_WRITER);
27324eaa4710SRishi Srivatsavai 	for (blcmp = list_head(&bip->bi_links); blcmp != NULL;
27334eaa4710SRishi Srivatsavai 	    blcmp = list_next(&bip->bi_links, blcmp)) {
27344eaa4710SRishi Srivatsavai 		if (blcmp != blp &&
27354eaa4710SRishi Srivatsavai 		    !(blcmp->bl_flags & (BLF_DELETED|BLF_SDUFAIL)) &&
27364eaa4710SRishi Srivatsavai 		    blcmp->bl_linkstate != LINK_STATE_DOWN)
27374eaa4710SRishi Srivatsavai 			break;
27384eaa4710SRishi Srivatsavai 	}
27394eaa4710SRishi Srivatsavai 
27404eaa4710SRishi Srivatsavai 	if (blcmp != NULL) {
27414eaa4710SRishi Srivatsavai 		/*
27424eaa4710SRishi Srivatsavai 		 * If there are other links that are considered up, then tell
27434eaa4710SRishi Srivatsavai 		 * the caller that the link is actually still up, regardless of
27444eaa4710SRishi Srivatsavai 		 * this link's underlying state.
27454eaa4710SRishi Srivatsavai 		 */
27464eaa4710SRishi Srivatsavai 		blp->bl_linkstate = newls;
27474eaa4710SRishi Srivatsavai 		newls = LINK_STATE_UP;
27484eaa4710SRishi Srivatsavai 	} else if (blp->bl_linkstate != newls) {
27494eaa4710SRishi Srivatsavai 		/*
27504eaa4710SRishi Srivatsavai 		 * If we've found no other 'up' links, and this link has
27514eaa4710SRishi Srivatsavai 		 * changed state, then report the new state of the bridge to
27524eaa4710SRishi Srivatsavai 		 * all other clients.
27534eaa4710SRishi Srivatsavai 		 */
27544eaa4710SRishi Srivatsavai 		blp->bl_linkstate = newls;
27554eaa4710SRishi Srivatsavai 		for (blcmp = list_head(&bip->bi_links); blcmp != NULL;
27564eaa4710SRishi Srivatsavai 		    blcmp = list_next(&bip->bi_links, blcmp)) {
27574eaa4710SRishi Srivatsavai 			if (blcmp != blp && !(blcmp->bl_flags & BLF_DELETED))
27584eaa4710SRishi Srivatsavai 				mac_link_redo(blcmp->bl_mh, newls);
27594eaa4710SRishi Srivatsavai 		}
27604eaa4710SRishi Srivatsavai 		bmp = bip->bi_mac;
27614eaa4710SRishi Srivatsavai 		if ((bmp->bm_linkstate = newls) != LINK_STATE_DOWN)
27624eaa4710SRishi Srivatsavai 			bmp->bm_linkstate = LINK_STATE_UP;
27634eaa4710SRishi Srivatsavai 		mac_link_redo(bmp->bm_mh, bmp->bm_linkstate);
27644eaa4710SRishi Srivatsavai 	}
27654eaa4710SRishi Srivatsavai 	rw_exit(&bip->bi_rwlock);
27664eaa4710SRishi Srivatsavai 	return (newls);
27674eaa4710SRishi Srivatsavai }
27684eaa4710SRishi Srivatsavai 
27694eaa4710SRishi Srivatsavai static void
27704eaa4710SRishi Srivatsavai bridge_add_link(void *arg)
27714eaa4710SRishi Srivatsavai {
27724eaa4710SRishi Srivatsavai 	mblk_t *mp = arg;
27734eaa4710SRishi Srivatsavai 	bridge_stream_t *bsp;
27744eaa4710SRishi Srivatsavai 	bridge_inst_t *bip, *bipt;
27754eaa4710SRishi Srivatsavai 	bridge_mac_t *bmp;
27764eaa4710SRishi Srivatsavai 	datalink_id_t linkid;
27774eaa4710SRishi Srivatsavai 	int err;
27784eaa4710SRishi Srivatsavai 	mac_handle_t mh;
27794eaa4710SRishi Srivatsavai 	uint_t maxsdu;
27804eaa4710SRishi Srivatsavai 	bridge_link_t *blp = NULL, *blpt;
27814eaa4710SRishi Srivatsavai 	const mac_info_t *mip;
27824eaa4710SRishi Srivatsavai 	boolean_t macopen = B_FALSE;
27834eaa4710SRishi Srivatsavai 	char linkname[MAXLINKNAMELEN];
27844eaa4710SRishi Srivatsavai 	char kstatname[KSTAT_STRLEN];
27854eaa4710SRishi Srivatsavai 	int i;
27864eaa4710SRishi Srivatsavai 	link_state_t linkstate;
27874eaa4710SRishi Srivatsavai 	mblk_t *mlist;
27884eaa4710SRishi Srivatsavai 
27894eaa4710SRishi Srivatsavai 	bsp = (bridge_stream_t *)mp->b_next;
27904eaa4710SRishi Srivatsavai 	mp->b_next = NULL;
27914eaa4710SRishi Srivatsavai 	bip = bsp->bs_inst;
27924eaa4710SRishi Srivatsavai 	/* LINTED: alignment */
27934eaa4710SRishi Srivatsavai 	linkid = *(datalink_id_t *)mp->b_cont->b_rptr;
27944eaa4710SRishi Srivatsavai 
27954eaa4710SRishi Srivatsavai 	/*
27964eaa4710SRishi Srivatsavai 	 * First make sure that there is no other bridge that has this link.
27974eaa4710SRishi Srivatsavai 	 * We don't want to overlap operations from two bridges; the MAC layer
27984eaa4710SRishi Srivatsavai 	 * supports only one bridge on a given MAC at a time.
27994eaa4710SRishi Srivatsavai 	 *
28004eaa4710SRishi Srivatsavai 	 * We rely on the fact that there's just one taskq thread for the
28014eaa4710SRishi Srivatsavai 	 * bridging module: once we've checked for a duplicate, we can drop the
28024eaa4710SRishi Srivatsavai 	 * lock, because no other thread could possibly be adding another link
28034eaa4710SRishi Srivatsavai 	 * until we're done.
28044eaa4710SRishi Srivatsavai 	 */
28054eaa4710SRishi Srivatsavai 	mutex_enter(&inst_lock);
28064eaa4710SRishi Srivatsavai 	for (bipt = list_head(&inst_list); bipt != NULL;
28074eaa4710SRishi Srivatsavai 	    bipt = list_next(&inst_list, bipt)) {
28084eaa4710SRishi Srivatsavai 		rw_enter(&bipt->bi_rwlock, RW_READER);
28094eaa4710SRishi Srivatsavai 		for (blpt = list_head(&bipt->bi_links); blpt != NULL;
28104eaa4710SRishi Srivatsavai 		    blpt = list_next(&bipt->bi_links, blpt)) {
28114eaa4710SRishi Srivatsavai 			if (linkid == blpt->bl_linkid)
28124eaa4710SRishi Srivatsavai 				break;
28134eaa4710SRishi Srivatsavai 		}
28144eaa4710SRishi Srivatsavai 		rw_exit(&bipt->bi_rwlock);
28154eaa4710SRishi Srivatsavai 		if (blpt != NULL)
28164eaa4710SRishi Srivatsavai 			break;
28174eaa4710SRishi Srivatsavai 	}
28184eaa4710SRishi Srivatsavai 	mutex_exit(&inst_lock);
28194eaa4710SRishi Srivatsavai 	if (bipt != NULL) {
28204eaa4710SRishi Srivatsavai 		err = EBUSY;
28214eaa4710SRishi Srivatsavai 		goto fail;
28224eaa4710SRishi Srivatsavai 	}
28234eaa4710SRishi Srivatsavai 
28244eaa4710SRishi Srivatsavai 	if ((err = mac_open_by_linkid(linkid, &mh)) != 0)
28254eaa4710SRishi Srivatsavai 		goto fail;
28264eaa4710SRishi Srivatsavai 	macopen = B_TRUE;
28274eaa4710SRishi Srivatsavai 
28284eaa4710SRishi Srivatsavai 	/* we bridge only Ethernet */
28294eaa4710SRishi Srivatsavai 	mip = mac_info(mh);
28304eaa4710SRishi Srivatsavai 	if (mip->mi_media != DL_ETHER) {
28314eaa4710SRishi Srivatsavai 		err = ENOTSUP;
28324eaa4710SRishi Srivatsavai 		goto fail;
28334eaa4710SRishi Srivatsavai 	}
28344eaa4710SRishi Srivatsavai 
28354eaa4710SRishi Srivatsavai 	/*
28364eaa4710SRishi Srivatsavai 	 * Get the current maximum SDU on this interface.  If there are other
28374eaa4710SRishi Srivatsavai 	 * links on the bridge, then this one must match, or it errors out.
28384eaa4710SRishi Srivatsavai 	 * Otherwise, the first link becomes the standard for the new bridge.
28394eaa4710SRishi Srivatsavai 	 */
28404eaa4710SRishi Srivatsavai 	mac_sdu_get(mh, NULL, &maxsdu);
28414eaa4710SRishi Srivatsavai 	bmp = bip->bi_mac;
28424eaa4710SRishi Srivatsavai 	if (list_is_empty(&bip->bi_links)) {
28434eaa4710SRishi Srivatsavai 		bmp->bm_maxsdu = maxsdu;
28444eaa4710SRishi Srivatsavai 		(void) mac_maxsdu_update(bmp->bm_mh, maxsdu);
28454eaa4710SRishi Srivatsavai 	}
28464eaa4710SRishi Srivatsavai 
28474eaa4710SRishi Srivatsavai 	/* figure the kstat name; also used as the mac client name */
28484eaa4710SRishi Srivatsavai 	i = MBLKL(mp->b_cont) - sizeof (datalink_id_t);
28494eaa4710SRishi Srivatsavai 	if (i < 0 || i >= MAXLINKNAMELEN)
28504eaa4710SRishi Srivatsavai 		i = MAXLINKNAMELEN - 1;
28514eaa4710SRishi Srivatsavai 	bcopy(mp->b_cont->b_rptr + sizeof (datalink_id_t), linkname, i);
28524eaa4710SRishi Srivatsavai 	linkname[i] = '\0';
28534eaa4710SRishi Srivatsavai 	(void) snprintf(kstatname, sizeof (kstatname), "%s-%s", bip->bi_name,
28544eaa4710SRishi Srivatsavai 	    linkname);
28554eaa4710SRishi Srivatsavai 
28564eaa4710SRishi Srivatsavai 	if ((blp = kmem_zalloc(sizeof (*blp), KM_NOSLEEP)) == NULL) {
28574eaa4710SRishi Srivatsavai 		err = ENOMEM;
28584eaa4710SRishi Srivatsavai 		goto fail;
28594eaa4710SRishi Srivatsavai 	}
28604eaa4710SRishi Srivatsavai 	blp->bl_lfailmp = allocb(sizeof (bridge_ctl_t), BPRI_MED);
28614eaa4710SRishi Srivatsavai 	if (blp->bl_lfailmp == NULL) {
28624eaa4710SRishi Srivatsavai 		kmem_free(blp, sizeof (*blp));
28634eaa4710SRishi Srivatsavai 		err = ENOMEM;
28644eaa4710SRishi Srivatsavai 		goto fail;
28654eaa4710SRishi Srivatsavai 	}
28664eaa4710SRishi Srivatsavai 
28674eaa4710SRishi Srivatsavai 	atomic_inc_uint(&bip->bi_refs);
28684eaa4710SRishi Srivatsavai 	blp->bl_inst = bip;
28694eaa4710SRishi Srivatsavai 	blp->bl_mh = mh;
28704eaa4710SRishi Srivatsavai 	blp->bl_linkid = linkid;
28714eaa4710SRishi Srivatsavai 	blp->bl_maxsdu = maxsdu;
28724eaa4710SRishi Srivatsavai 	cv_init(&blp->bl_trillwait, NULL, CV_DRIVER, NULL);
28734eaa4710SRishi Srivatsavai 	mutex_init(&blp->bl_trilllock, NULL, MUTEX_DRIVER, NULL);
28744eaa4710SRishi Srivatsavai 	(void) memset(blp->bl_afs, 0xff, sizeof (blp->bl_afs));
28754eaa4710SRishi Srivatsavai 
28764eaa4710SRishi Srivatsavai 	err = mac_client_open(mh, &blp->bl_mch, kstatname, 0);
28774eaa4710SRishi Srivatsavai 	if (err != 0)
28784eaa4710SRishi Srivatsavai 		goto fail;
28794eaa4710SRishi Srivatsavai 	blp->bl_flags |= BLF_CLIENT_OPEN;
28804eaa4710SRishi Srivatsavai 
28814eaa4710SRishi Srivatsavai 	err = mac_margin_add(mh, &blp->bl_margin, B_TRUE);
28824eaa4710SRishi Srivatsavai 	if (err != 0)
28834eaa4710SRishi Srivatsavai 		goto fail;
28844eaa4710SRishi Srivatsavai 	blp->bl_flags |= BLF_MARGIN_ADDED;
28854eaa4710SRishi Srivatsavai 
28864eaa4710SRishi Srivatsavai 	blp->bl_mnh = mac_notify_add(mh, bridge_notify_cb, blp);
28874eaa4710SRishi Srivatsavai 
28884eaa4710SRishi Srivatsavai 	err = mac_bridge_set(mh, (mac_handle_t)blp);
28894eaa4710SRishi Srivatsavai 	if (err != 0)
28904eaa4710SRishi Srivatsavai 		goto fail;
28914eaa4710SRishi Srivatsavai 	blp->bl_flags |= BLF_SET_BRIDGE;
28924eaa4710SRishi Srivatsavai 
28934eaa4710SRishi Srivatsavai 	err = mac_promisc_add(blp->bl_mch, MAC_CLIENT_PROMISC_ALL, NULL,
28944eaa4710SRishi Srivatsavai 	    blp, &blp->bl_mphp, MAC_PROMISC_FLAGS_NO_TX_LOOP);
28954eaa4710SRishi Srivatsavai 	if (err != 0)
28964eaa4710SRishi Srivatsavai 		goto fail;
28974eaa4710SRishi Srivatsavai 	blp->bl_flags |= BLF_PROM_ADDED;
28984eaa4710SRishi Srivatsavai 
28994eaa4710SRishi Srivatsavai 	bridge_new_unicst(blp);
29004eaa4710SRishi Srivatsavai 
29014eaa4710SRishi Srivatsavai 	blp->bl_ksp = kstat_setup((kstat_named_t *)&blp->bl_kstats,
29024eaa4710SRishi Srivatsavai 	    link_kstats_list, Dim(link_kstats_list), kstatname);
29034eaa4710SRishi Srivatsavai 
29044eaa4710SRishi Srivatsavai 	/*
29054eaa4710SRishi Srivatsavai 	 * The link holds a reference to the bridge instance, so that the
29064eaa4710SRishi Srivatsavai 	 * instance can't go away before the link is freed.  The insertion into
29074eaa4710SRishi Srivatsavai 	 * bi_links holds a reference on the link.  When marking as removed
29084eaa4710SRishi Srivatsavai 	 * from bi_links (BLF_DELETED), drop the reference on the link.  When
29094eaa4710SRishi Srivatsavai 	 * freeing the link, drop the reference on the instance.
29104eaa4710SRishi Srivatsavai 	 */
29114eaa4710SRishi Srivatsavai 	rw_enter(&bip->bi_rwlock, RW_WRITER);
29124eaa4710SRishi Srivatsavai 	list_insert_tail(&bip->bi_links, blp);
29134eaa4710SRishi Srivatsavai 	atomic_inc_uint(&blp->bl_refs);
29144eaa4710SRishi Srivatsavai 
29154eaa4710SRishi Srivatsavai 	/*
29164eaa4710SRishi Srivatsavai 	 * If the new link is no good on this bridge, then let the daemon know
29174eaa4710SRishi Srivatsavai 	 * about the problem.
29184eaa4710SRishi Srivatsavai 	 */
29194eaa4710SRishi Srivatsavai 	mlist = NULL;
29204eaa4710SRishi Srivatsavai 	if (maxsdu != bmp->bm_maxsdu)
29214eaa4710SRishi Srivatsavai 		link_sdu_fail(blp, B_TRUE, &mlist);
29224eaa4710SRishi Srivatsavai 	rw_exit(&bip->bi_rwlock);
29234eaa4710SRishi Srivatsavai 	send_up_messages(bip, mlist);
29244eaa4710SRishi Srivatsavai 
29254eaa4710SRishi Srivatsavai 	/*
29264eaa4710SRishi Srivatsavai 	 * Trigger a link state update so that if this link is the first one
29274eaa4710SRishi Srivatsavai 	 * "up" in the bridge, then we notify everyone.  This triggers a trip
29284eaa4710SRishi Srivatsavai 	 * through bridge_ls_cb.
29294eaa4710SRishi Srivatsavai 	 */
29304eaa4710SRishi Srivatsavai 	linkstate = mac_stat_get(mh, MAC_STAT_LOWLINK_STATE);
29314eaa4710SRishi Srivatsavai 	blp->bl_linkstate = LINK_STATE_DOWN;
29324eaa4710SRishi Srivatsavai 	mac_link_update(mh, linkstate);
29334eaa4710SRishi Srivatsavai 
29344eaa4710SRishi Srivatsavai 	/*
29354eaa4710SRishi Srivatsavai 	 * We now need to report back to the stream that invoked us, and then
29364eaa4710SRishi Srivatsavai 	 * drop the reference on the stream that we're holding.
29374eaa4710SRishi Srivatsavai 	 */
29384eaa4710SRishi Srivatsavai 	miocack(bsp->bs_wq, mp, 0, 0);
29394eaa4710SRishi Srivatsavai 	stream_unref(bsp);
29404eaa4710SRishi Srivatsavai 	return;
29414eaa4710SRishi Srivatsavai 
29424eaa4710SRishi Srivatsavai fail:
29434eaa4710SRishi Srivatsavai 	if (blp == NULL) {
29444eaa4710SRishi Srivatsavai 		if (macopen)
29454eaa4710SRishi Srivatsavai 			mac_close(mh);
29464eaa4710SRishi Srivatsavai 	} else {
29474eaa4710SRishi Srivatsavai 		link_shutdown(blp);
29484eaa4710SRishi Srivatsavai 		link_free(blp);
29494eaa4710SRishi Srivatsavai 	}
29504eaa4710SRishi Srivatsavai 	miocnak(bsp->bs_wq, mp, 0, err);
29514eaa4710SRishi Srivatsavai 	stream_unref(bsp);
29524eaa4710SRishi Srivatsavai }
29534eaa4710SRishi Srivatsavai 
29544eaa4710SRishi Srivatsavai static void
29554eaa4710SRishi Srivatsavai bridge_rem_link(void *arg)
29564eaa4710SRishi Srivatsavai {
29574eaa4710SRishi Srivatsavai 	mblk_t *mp = arg;
29584eaa4710SRishi Srivatsavai 	bridge_stream_t *bsp;
29594eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
29604eaa4710SRishi Srivatsavai 	bridge_mac_t *bmp;
29614eaa4710SRishi Srivatsavai 	datalink_id_t linkid;
29624eaa4710SRishi Srivatsavai 	bridge_link_t *blp, *blsave;
29634eaa4710SRishi Srivatsavai 	boolean_t found;
29644eaa4710SRishi Srivatsavai 	mblk_t *mlist;
29654eaa4710SRishi Srivatsavai 
29664eaa4710SRishi Srivatsavai 	bsp = (bridge_stream_t *)mp->b_next;
29674eaa4710SRishi Srivatsavai 	mp->b_next = NULL;
29684eaa4710SRishi Srivatsavai 	bip = bsp->bs_inst;
29694eaa4710SRishi Srivatsavai 	/* LINTED: alignment */
29704eaa4710SRishi Srivatsavai 	linkid = *(datalink_id_t *)mp->b_cont->b_rptr;
29714eaa4710SRishi Srivatsavai 
29724eaa4710SRishi Srivatsavai 	/*
29734eaa4710SRishi Srivatsavai 	 * We become reader here so that we can loop over the other links and
29744eaa4710SRishi Srivatsavai 	 * deliver link up/down notification.
29754eaa4710SRishi Srivatsavai 	 */
29764eaa4710SRishi Srivatsavai 	rw_enter(&bip->bi_rwlock, RW_READER);
29774eaa4710SRishi Srivatsavai 	found = B_FALSE;
29784eaa4710SRishi Srivatsavai 	for (blp = list_head(&bip->bi_links); blp != NULL;
29794eaa4710SRishi Srivatsavai 	    blp = list_next(&bip->bi_links, blp)) {
29804eaa4710SRishi Srivatsavai 		if (blp->bl_linkid == linkid &&
29814eaa4710SRishi Srivatsavai 		    !(blp->bl_flags & BLF_DELETED)) {
29824eaa4710SRishi Srivatsavai 			blp->bl_flags |= BLF_DELETED;
29834eaa4710SRishi Srivatsavai 			(void) ddi_taskq_dispatch(bridge_taskq, link_shutdown,
29844eaa4710SRishi Srivatsavai 			    blp, DDI_SLEEP);
29854eaa4710SRishi Srivatsavai 			found = B_TRUE;
29864eaa4710SRishi Srivatsavai 			break;
29874eaa4710SRishi Srivatsavai 		}
29884eaa4710SRishi Srivatsavai 	}
29894eaa4710SRishi Srivatsavai 
29904eaa4710SRishi Srivatsavai 	/*
29914eaa4710SRishi Srivatsavai 	 * Check if this link is up and the remainder of the links are all
29924eaa4710SRishi Srivatsavai 	 * down.
29934eaa4710SRishi Srivatsavai 	 */
29944eaa4710SRishi Srivatsavai 	if (blp != NULL && blp->bl_linkstate != LINK_STATE_DOWN) {
29954eaa4710SRishi Srivatsavai 		for (blp = list_head(&bip->bi_links); blp != NULL;
29964eaa4710SRishi Srivatsavai 		    blp = list_next(&bip->bi_links, blp)) {
29974eaa4710SRishi Srivatsavai 			if (blp->bl_linkstate != LINK_STATE_DOWN &&
29984eaa4710SRishi Srivatsavai 			    !(blp->bl_flags & (BLF_DELETED|BLF_SDUFAIL)))
29994eaa4710SRishi Srivatsavai 				break;
30004eaa4710SRishi Srivatsavai 		}
30014eaa4710SRishi Srivatsavai 		if (blp == NULL) {
30024eaa4710SRishi Srivatsavai 			for (blp = list_head(&bip->bi_links); blp != NULL;
30034eaa4710SRishi Srivatsavai 			    blp = list_next(&bip->bi_links, blp)) {
30044eaa4710SRishi Srivatsavai 				if (!(blp->bl_flags & BLF_DELETED))
30054eaa4710SRishi Srivatsavai 					mac_link_redo(blp->bl_mh,
30064eaa4710SRishi Srivatsavai 					    LINK_STATE_DOWN);
30074eaa4710SRishi Srivatsavai 			}
30084eaa4710SRishi Srivatsavai 			bmp = bip->bi_mac;
30094eaa4710SRishi Srivatsavai 			bmp->bm_linkstate = LINK_STATE_DOWN;
30104eaa4710SRishi Srivatsavai 			mac_link_redo(bmp->bm_mh, LINK_STATE_DOWN);
30114eaa4710SRishi Srivatsavai 		}
30124eaa4710SRishi Srivatsavai 	}
30134eaa4710SRishi Srivatsavai 
30144eaa4710SRishi Srivatsavai 	/*
30154eaa4710SRishi Srivatsavai 	 * Check if there's just one working link left on the bridge.  If so,
30164eaa4710SRishi Srivatsavai 	 * then that link is now authoritative for bridge MTU.
30174eaa4710SRishi Srivatsavai 	 */
30184eaa4710SRishi Srivatsavai 	blsave = NULL;
30194eaa4710SRishi Srivatsavai 	for (blp = list_head(&bip->bi_links); blp != NULL;
30204eaa4710SRishi Srivatsavai 	    blp = list_next(&bip->bi_links, blp)) {
30214eaa4710SRishi Srivatsavai 		if (!(blp->bl_flags & BLF_DELETED)) {
30224eaa4710SRishi Srivatsavai 			if (blsave == NULL)
30234eaa4710SRishi Srivatsavai 				blsave = blp;
30244eaa4710SRishi Srivatsavai 			else
30254eaa4710SRishi Srivatsavai 				break;
30264eaa4710SRishi Srivatsavai 		}
30274eaa4710SRishi Srivatsavai 	}
30284eaa4710SRishi Srivatsavai 	mlist = NULL;
30294eaa4710SRishi Srivatsavai 	bmp = bip->bi_mac;
30304eaa4710SRishi Srivatsavai 	if (blsave != NULL && blp == NULL &&
30314eaa4710SRishi Srivatsavai 	    blsave->bl_maxsdu != bmp->bm_maxsdu) {
30324eaa4710SRishi Srivatsavai 		bmp->bm_maxsdu = blsave->bl_maxsdu;
30334eaa4710SRishi Srivatsavai 		(void) mac_maxsdu_update(bmp->bm_mh, blsave->bl_maxsdu);
30344eaa4710SRishi Srivatsavai 		link_sdu_fail(blsave, B_FALSE, &mlist);
30354eaa4710SRishi Srivatsavai 	}
30364eaa4710SRishi Srivatsavai 	rw_exit(&bip->bi_rwlock);
30374eaa4710SRishi Srivatsavai 	send_up_messages(bip, mlist);
30384eaa4710SRishi Srivatsavai 
30394eaa4710SRishi Srivatsavai 	if (found)
30404eaa4710SRishi Srivatsavai 		miocack(bsp->bs_wq, mp, 0, 0);
30414eaa4710SRishi Srivatsavai 	else
30424eaa4710SRishi Srivatsavai 		miocnak(bsp->bs_wq, mp, 0, ENOENT);
30434eaa4710SRishi Srivatsavai 	stream_unref(bsp);
30444eaa4710SRishi Srivatsavai }
30454eaa4710SRishi Srivatsavai 
30464eaa4710SRishi Srivatsavai /*
30474eaa4710SRishi Srivatsavai  * This function intentionally returns with bi_rwlock held; it is intended for
30484eaa4710SRishi Srivatsavai  * quick checks and updates.
30494eaa4710SRishi Srivatsavai  */
30504eaa4710SRishi Srivatsavai static bridge_link_t *
30514eaa4710SRishi Srivatsavai enter_link(bridge_inst_t *bip, datalink_id_t linkid)
30524eaa4710SRishi Srivatsavai {
30534eaa4710SRishi Srivatsavai 	bridge_link_t *blp;
30544eaa4710SRishi Srivatsavai 
30554eaa4710SRishi Srivatsavai 	rw_enter(&bip->bi_rwlock, RW_READER);
30564eaa4710SRishi Srivatsavai 	for (blp = list_head(&bip->bi_links); blp != NULL;
30574eaa4710SRishi Srivatsavai 	    blp = list_next(&bip->bi_links, blp)) {
30584eaa4710SRishi Srivatsavai 		if (blp->bl_linkid == linkid && !(blp->bl_flags & BLF_DELETED))
30594eaa4710SRishi Srivatsavai 			break;
30604eaa4710SRishi Srivatsavai 	}
30614eaa4710SRishi Srivatsavai 	return (blp);
30624eaa4710SRishi Srivatsavai }
30634eaa4710SRishi Srivatsavai 
30644eaa4710SRishi Srivatsavai static void
30654eaa4710SRishi Srivatsavai bridge_ioctl(queue_t *wq, mblk_t *mp)
30664eaa4710SRishi Srivatsavai {
30674eaa4710SRishi Srivatsavai 	bridge_stream_t *bsp = wq->q_ptr;
30684eaa4710SRishi Srivatsavai 	bridge_inst_t *bip;
30694eaa4710SRishi Srivatsavai 	struct iocblk *iop;
30704eaa4710SRishi Srivatsavai 	int rc = EINVAL;
30714eaa4710SRishi Srivatsavai 	int len = 0;
30724eaa4710SRishi Srivatsavai 	bridge_link_t *blp;
30734eaa4710SRishi Srivatsavai 	cred_t *cr;
30744eaa4710SRishi Srivatsavai 
30754eaa4710SRishi Srivatsavai 	/* LINTED: alignment */
30764eaa4710SRishi Srivatsavai 	iop = (struct iocblk *)mp->b_rptr;
30774eaa4710SRishi Srivatsavai 
30784eaa4710SRishi Srivatsavai 	/*
30794eaa4710SRishi Srivatsavai 	 * For now, all of the bridge ioctls are privileged.
30804eaa4710SRishi Srivatsavai 	 */
30814eaa4710SRishi Srivatsavai 	if ((cr = msg_getcred(mp, NULL)) == NULL)
30824eaa4710SRishi Srivatsavai 		cr = iop->ioc_cr;
30834eaa4710SRishi Srivatsavai 	if (cr != NULL && secpolicy_net_config(cr, B_FALSE) != 0) {
30844eaa4710SRishi Srivatsavai 		miocnak(wq, mp, 0, EPERM);
30854eaa4710SRishi Srivatsavai 		return;
30864eaa4710SRishi Srivatsavai 	}
30874eaa4710SRishi Srivatsavai 
30884eaa4710SRishi Srivatsavai 	switch (iop->ioc_cmd) {
30894eaa4710SRishi Srivatsavai 	case BRIOC_NEWBRIDGE: {
30904eaa4710SRishi Srivatsavai 		bridge_newbridge_t *bnb;
30914eaa4710SRishi Srivatsavai 
30924eaa4710SRishi Srivatsavai 		if (bsp->bs_inst != NULL ||
30934eaa4710SRishi Srivatsavai 		    (rc = miocpullup(mp, sizeof (bridge_newbridge_t))) != 0)
30944eaa4710SRishi Srivatsavai 			break;
30954eaa4710SRishi Srivatsavai 		/* LINTED: alignment */
30964eaa4710SRishi Srivatsavai 		bnb = (bridge_newbridge_t *)mp->b_cont->b_rptr;
30974eaa4710SRishi Srivatsavai 		bnb->bnb_name[MAXNAMELEN-1] = '\0';
30984eaa4710SRishi Srivatsavai 		if ((rc = bridge_create(bnb->bnb_linkid,
30994eaa4710SRishi Srivatsavai 		    bnb->bnb_name, &bip)) != 0)
31004eaa4710SRishi Srivatsavai 			break;
31014eaa4710SRishi Srivatsavai 
31024eaa4710SRishi Srivatsavai 		rw_enter(&bip->bi_rwlock, RW_WRITER);
31034eaa4710SRishi Srivatsavai 		if (bip->bi_control != NULL) {
31044eaa4710SRishi Srivatsavai 			rw_exit(&bip->bi_rwlock);
31054eaa4710SRishi Srivatsavai 			bridge_unref(bip);
31064eaa4710SRishi Srivatsavai 			rc = EBUSY;
31074eaa4710SRishi Srivatsavai 		} else {
31084eaa4710SRishi Srivatsavai 			atomic_inc_uint(&bip->bi_refs);
31094eaa4710SRishi Srivatsavai 			bsp->bs_inst = bip;	/* stream holds reference */
31104eaa4710SRishi Srivatsavai 			bip->bi_control = bsp;
31114eaa4710SRishi Srivatsavai 			rw_exit(&bip->bi_rwlock);
31124eaa4710SRishi Srivatsavai 			rc = 0;
31134eaa4710SRishi Srivatsavai 		}
31144eaa4710SRishi Srivatsavai 		break;
31154eaa4710SRishi Srivatsavai 	}
31164eaa4710SRishi Srivatsavai 
31174eaa4710SRishi Srivatsavai 	case BRIOC_ADDLINK:
31184eaa4710SRishi Srivatsavai 		if ((bip = bsp->bs_inst) == NULL ||
31194eaa4710SRishi Srivatsavai 		    (rc = miocpullup(mp, sizeof (datalink_id_t))) != 0)
31204eaa4710SRishi Srivatsavai 			break;
31214eaa4710SRishi Srivatsavai 		/*
31224eaa4710SRishi Srivatsavai 		 * We cannot perform the action in this thread, because we're
31234eaa4710SRishi Srivatsavai 		 * not in process context, and we may already be holding
31244eaa4710SRishi Srivatsavai 		 * MAC-related locks.  Place the request on taskq.
31254eaa4710SRishi Srivatsavai 		 */
31264eaa4710SRishi Srivatsavai 		mp->b_next = (mblk_t *)bsp;
31274eaa4710SRishi Srivatsavai 		stream_ref(bsp);
31284eaa4710SRishi Srivatsavai 		(void) ddi_taskq_dispatch(bridge_taskq, bridge_add_link, mp,
31294eaa4710SRishi Srivatsavai 		    DDI_SLEEP);
31304eaa4710SRishi Srivatsavai 		return;
31314eaa4710SRishi Srivatsavai 
31324eaa4710SRishi Srivatsavai 	case BRIOC_REMLINK:
31334eaa4710SRishi Srivatsavai 		if ((bip = bsp->bs_inst) == NULL ||
31344eaa4710SRishi Srivatsavai 		    (rc = miocpullup(mp, sizeof (datalink_id_t))) != 0)
31354eaa4710SRishi Srivatsavai 			break;
31364eaa4710SRishi Srivatsavai 		/*
31374eaa4710SRishi Srivatsavai 		 * We cannot perform the action in this thread, because we're
31384eaa4710SRishi Srivatsavai 		 * not in process context, and we may already be holding
31394eaa4710SRishi Srivatsavai 		 * MAC-related locks.  Place the request on taskq.
31404eaa4710SRishi Srivatsavai 		 */
31414eaa4710SRishi Srivatsavai 		mp->b_next = (mblk_t *)bsp;
31424eaa4710SRishi Srivatsavai 		stream_ref(bsp);
31434eaa4710SRishi Srivatsavai 		(void) ddi_taskq_dispatch(bridge_taskq, bridge_rem_link, mp,
31444eaa4710SRishi Srivatsavai 		    DDI_SLEEP);
31454eaa4710SRishi Srivatsavai 		return;
31464eaa4710SRishi Srivatsavai 
31474eaa4710SRishi Srivatsavai 	case BRIOC_SETSTATE: {
31484eaa4710SRishi Srivatsavai 		bridge_setstate_t *bss;
31494eaa4710SRishi Srivatsavai 
31504eaa4710SRishi Srivatsavai 		if ((bip = bsp->bs_inst) == NULL ||
31514eaa4710SRishi Srivatsavai 		    (rc = miocpullup(mp, sizeof (*bss))) != 0)
31524eaa4710SRishi Srivatsavai 			break;
31534eaa4710SRishi Srivatsavai 		/* LINTED: alignment */
31544eaa4710SRishi Srivatsavai 		bss = (bridge_setstate_t *)mp->b_cont->b_rptr;
31554eaa4710SRishi Srivatsavai 		if ((blp = enter_link(bip, bss->bss_linkid)) == NULL) {
31564eaa4710SRishi Srivatsavai 			rc = ENOENT;
31574eaa4710SRishi Srivatsavai 		} else {
31584eaa4710SRishi Srivatsavai 			rc = 0;
31594eaa4710SRishi Srivatsavai 			blp->bl_state = bss->bss_state;
31604eaa4710SRishi Srivatsavai 		}
31614eaa4710SRishi Srivatsavai 		rw_exit(&bip->bi_rwlock);
31624eaa4710SRishi Srivatsavai 		break;
31634eaa4710SRishi Srivatsavai 	}
31644eaa4710SRishi Srivatsavai 
31654eaa4710SRishi Srivatsavai 	case BRIOC_SETPVID: {
31664eaa4710SRishi Srivatsavai 		bridge_setpvid_t *bsv;
31674eaa4710SRishi Srivatsavai 
31684eaa4710SRishi Srivatsavai 		if ((bip = bsp->bs_inst) == NULL ||
31694eaa4710SRishi Srivatsavai 		    (rc = miocpullup(mp, sizeof (*bsv))) != 0)
31704eaa4710SRishi Srivatsavai 			break;
31714eaa4710SRishi Srivatsavai 		/* LINTED: alignment */
31724eaa4710SRishi Srivatsavai 		bsv = (bridge_setpvid_t *)mp->b_cont->b_rptr;
31734eaa4710SRishi Srivatsavai 		if (bsv->bsv_vlan > VLAN_ID_MAX)
31744eaa4710SRishi Srivatsavai 			break;
31754eaa4710SRishi Srivatsavai 		if ((blp = enter_link(bip, bsv->bsv_linkid)) == NULL) {
31764eaa4710SRishi Srivatsavai 			rc = ENOENT;
31774eaa4710SRishi Srivatsavai 		} else if (blp->bl_pvid == bsv->bsv_vlan) {
31784eaa4710SRishi Srivatsavai 			rc = 0;
31794eaa4710SRishi Srivatsavai 		} else {
31804eaa4710SRishi Srivatsavai 			rc = 0;
31814eaa4710SRishi Srivatsavai 			BRIDGE_VLAN_CLR(blp, blp->bl_pvid);
31824eaa4710SRishi Srivatsavai 			blp->bl_pvid = bsv->bsv_vlan;
31834eaa4710SRishi Srivatsavai 			if (blp->bl_pvid != 0)
31844eaa4710SRishi Srivatsavai 				BRIDGE_VLAN_SET(blp, blp->bl_pvid);
31854eaa4710SRishi Srivatsavai 		}
31864eaa4710SRishi Srivatsavai 		rw_exit(&bip->bi_rwlock);
31874eaa4710SRishi Srivatsavai 		break;
31884eaa4710SRishi Srivatsavai 	}
31894eaa4710SRishi Srivatsavai 
31904eaa4710SRishi Srivatsavai 	case BRIOC_VLANENAB: {
31914eaa4710SRishi Srivatsavai 		bridge_vlanenab_t *bve;
31924eaa4710SRishi Srivatsavai 
31934eaa4710SRishi Srivatsavai 		if ((bip = bsp->bs_inst) == NULL ||
31944eaa4710SRishi Srivatsavai 		    (rc = miocpullup(mp, sizeof (*bve))) != 0)
31954eaa4710SRishi Srivatsavai 			break;
31964eaa4710SRishi Srivatsavai 		/* LINTED: alignment */
31974eaa4710SRishi Srivatsavai 		bve = (bridge_vlanenab_t *)mp->b_cont->b_rptr;
31984eaa4710SRishi Srivatsavai 		if (bve->bve_vlan > VLAN_ID_MAX)
31994eaa4710SRishi Srivatsavai 			break;
32004eaa4710SRishi Srivatsavai 		if ((blp = enter_link(bip, bve->bve_linkid)) == NULL) {
32014eaa4710SRishi Srivatsavai 			rc = ENOENT;
32024eaa4710SRishi Srivatsavai 		} else {
32034eaa4710SRishi Srivatsavai 			rc = 0;
32044eaa4710SRishi Srivatsavai 			/* special case: vlan 0 means "all" */
32054eaa4710SRishi Srivatsavai 			if (bve->bve_vlan == 0) {
32064eaa4710SRishi Srivatsavai 				(void) memset(blp->bl_vlans,
32074eaa4710SRishi Srivatsavai 				    bve->bve_onoff ? ~0 : 0,
32084eaa4710SRishi Srivatsavai 				    sizeof (blp->bl_vlans));
32094eaa4710SRishi Srivatsavai 				BRIDGE_VLAN_CLR(blp, 0);
32104eaa4710SRishi Srivatsavai 				if (blp->bl_pvid != 0)
32114eaa4710SRishi Srivatsavai 					BRIDGE_VLAN_SET(blp, blp->bl_pvid);
32124eaa4710SRishi Srivatsavai 			} else if (bve->bve_vlan == blp->bl_pvid) {
32134eaa4710SRishi Srivatsavai 				rc = EINVAL;
32144eaa4710SRishi Srivatsavai 			} else if (bve->bve_onoff) {
32154eaa4710SRishi Srivatsavai 				BRIDGE_VLAN_SET(blp, bve->bve_vlan);
32164eaa4710SRishi Srivatsavai 			} else {
32174eaa4710SRishi Srivatsavai 				BRIDGE_VLAN_CLR(blp, bve->bve_vlan);
32184eaa4710SRishi Srivatsavai 			}
32194eaa4710SRishi Srivatsavai 		}
32204eaa4710SRishi Srivatsavai 		rw_exit(&bip->bi_rwlock);
32214eaa4710SRishi Srivatsavai 		break;
32224eaa4710SRishi Srivatsavai 	}
32234eaa4710SRishi Srivatsavai 
32244eaa4710SRishi Srivatsavai 	case BRIOC_FLUSHFWD: {
32254eaa4710SRishi Srivatsavai 		bridge_flushfwd_t *bff;
32264eaa4710SRishi Srivatsavai 		bridge_fwd_t *bfp, *bfnext;
32274eaa4710SRishi Srivatsavai 		avl_tree_t fwd_scavenge;
32284eaa4710SRishi Srivatsavai 		int i;
32294eaa4710SRishi Srivatsavai 
32304eaa4710SRishi Srivatsavai 		if ((bip = bsp->bs_inst) == NULL ||
32314eaa4710SRishi Srivatsavai 		    (rc = miocpullup(mp, sizeof (*bff))) != 0)
32324eaa4710SRishi Srivatsavai 			break;
32334eaa4710SRishi Srivatsavai 		/* LINTED: alignment */
32344eaa4710SRishi Srivatsavai 		bff = (bridge_flushfwd_t *)mp->b_cont->b_rptr;
32354eaa4710SRishi Srivatsavai 		rw_enter(&bip->bi_rwlock, RW_WRITER);
32364eaa4710SRishi Srivatsavai 		/* This case means "all" */
32374eaa4710SRishi Srivatsavai 		if (bff->bff_linkid == DATALINK_INVALID_LINKID) {
32384eaa4710SRishi Srivatsavai 			blp = NULL;
32394eaa4710SRishi Srivatsavai 		} else {
32404eaa4710SRishi Srivatsavai 			for (blp = list_head(&bip->bi_links); blp != NULL;
32414eaa4710SRishi Srivatsavai 			    blp = list_next(&bip->bi_links, blp)) {
32424eaa4710SRishi Srivatsavai 				if (blp->bl_linkid == bff->bff_linkid &&
32434eaa4710SRishi Srivatsavai 				    !(blp->bl_flags & BLF_DELETED))
32444eaa4710SRishi Srivatsavai 					break;
32454eaa4710SRishi Srivatsavai 			}
32464eaa4710SRishi Srivatsavai 			if (blp == NULL) {
32474eaa4710SRishi Srivatsavai 				rc = ENOENT;
32484eaa4710SRishi Srivatsavai 				rw_exit(&bip->bi_rwlock);
32494eaa4710SRishi Srivatsavai 				break;
32504eaa4710SRishi Srivatsavai 			}
32514eaa4710SRishi Srivatsavai 		}
32524eaa4710SRishi Srivatsavai 		avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t),
32534eaa4710SRishi Srivatsavai 		    offsetof(bridge_fwd_t, bf_node));
32544eaa4710SRishi Srivatsavai 		bfnext = avl_first(&bip->bi_fwd);
32554eaa4710SRishi Srivatsavai 		while ((bfp = bfnext) != NULL) {
32564eaa4710SRishi Srivatsavai 			bfnext = AVL_NEXT(&bip->bi_fwd, bfp);
32574eaa4710SRishi Srivatsavai 			if (bfp->bf_flags & BFF_LOCALADDR)
32584eaa4710SRishi Srivatsavai 				continue;
32594eaa4710SRishi Srivatsavai 			if (blp != NULL) {
32604eaa4710SRishi Srivatsavai 				for (i = 0; i < bfp->bf_maxlinks; i++) {
32614eaa4710SRishi Srivatsavai 					if (bfp->bf_links[i] == blp)
32624eaa4710SRishi Srivatsavai 						break;
32634eaa4710SRishi Srivatsavai 				}
32644eaa4710SRishi Srivatsavai 				/*
32654eaa4710SRishi Srivatsavai 				 * If the link is there and we're excluding,
32664eaa4710SRishi Srivatsavai 				 * then skip.  If the link is not there and
32674eaa4710SRishi Srivatsavai 				 * we're doing only that link, then skip.
32684eaa4710SRishi Srivatsavai 				 */
32694eaa4710SRishi Srivatsavai 				if ((i < bfp->bf_maxlinks) == bff->bff_exclude)
32704eaa4710SRishi Srivatsavai 					continue;
32714eaa4710SRishi Srivatsavai 			}
32724eaa4710SRishi Srivatsavai 			ASSERT(bfp->bf_flags & BFF_INTREE);
32734eaa4710SRishi Srivatsavai 			avl_remove(&bip->bi_fwd, bfp);
32744eaa4710SRishi Srivatsavai 			bfp->bf_flags &= ~BFF_INTREE;
32754eaa4710SRishi Srivatsavai 			avl_add(&fwd_scavenge, bfp);
32764eaa4710SRishi Srivatsavai 		}
32774eaa4710SRishi Srivatsavai 		rw_exit(&bip->bi_rwlock);
32784eaa4710SRishi Srivatsavai 		bfnext = avl_first(&fwd_scavenge);
32794eaa4710SRishi Srivatsavai 		while ((bfp = bfnext) != NULL) {
32804eaa4710SRishi Srivatsavai 			bfnext = AVL_NEXT(&fwd_scavenge, bfp);
32814eaa4710SRishi Srivatsavai 			avl_remove(&fwd_scavenge, bfp);
32824eaa4710SRishi Srivatsavai 			fwd_unref(bfp);	/* drop tree reference */
32834eaa4710SRishi Srivatsavai 		}
32844eaa4710SRishi Srivatsavai 		avl_destroy(&fwd_scavenge);
32854eaa4710SRishi Srivatsavai 		break;
32864eaa4710SRishi Srivatsavai 	}
32874eaa4710SRishi Srivatsavai 
32884eaa4710SRishi Srivatsavai 	case BRIOC_TABLEMAX:
32894eaa4710SRishi Srivatsavai 		if ((bip = bsp->bs_inst) == NULL ||
32904eaa4710SRishi Srivatsavai 		    (rc = miocpullup(mp, sizeof (uint32_t))) != 0)
32914eaa4710SRishi Srivatsavai 			break;
32924eaa4710SRishi Srivatsavai 		/* LINTED: alignment */
32934eaa4710SRishi Srivatsavai 		bip->bi_tablemax = *(uint32_t *)mp->b_cont->b_rptr;
32944eaa4710SRishi Srivatsavai 		break;
32954eaa4710SRishi Srivatsavai 	}
32964eaa4710SRishi Srivatsavai 
32974eaa4710SRishi Srivatsavai 	if (rc == 0)
32984eaa4710SRishi Srivatsavai 		miocack(wq, mp, len, 0);
32994eaa4710SRishi Srivatsavai 	else
33004eaa4710SRishi Srivatsavai 		miocnak(wq, mp, 0, rc);
33014eaa4710SRishi Srivatsavai }
33024eaa4710SRishi Srivatsavai 
33034eaa4710SRishi Srivatsavai static void
33044eaa4710SRishi Srivatsavai bridge_wput(queue_t *wq, mblk_t *mp)
33054eaa4710SRishi Srivatsavai {
33064eaa4710SRishi Srivatsavai 	switch (DB_TYPE(mp)) {
33074eaa4710SRishi Srivatsavai 	case M_IOCTL:
33084eaa4710SRishi Srivatsavai 		bridge_ioctl(wq, mp);
33094eaa4710SRishi Srivatsavai 		break;
33104eaa4710SRishi Srivatsavai 	case M_FLUSH:
33114eaa4710SRishi Srivatsavai 		if (*mp->b_rptr & FLUSHW)
33124eaa4710SRishi Srivatsavai 			*mp->b_rptr &= ~FLUSHW;
33134eaa4710SRishi Srivatsavai 		if (*mp->b_rptr & FLUSHR)
33144eaa4710SRishi Srivatsavai 			qreply(wq, mp);
33154eaa4710SRishi Srivatsavai 		else
33164eaa4710SRishi Srivatsavai 			freemsg(mp);
33174eaa4710SRishi Srivatsavai 		break;
33184eaa4710SRishi Srivatsavai 	default:
33194eaa4710SRishi Srivatsavai 		freemsg(mp);
33204eaa4710SRishi Srivatsavai 		break;
33214eaa4710SRishi Srivatsavai 	}
33224eaa4710SRishi Srivatsavai }
33234eaa4710SRishi Srivatsavai 
33244eaa4710SRishi Srivatsavai /*
33254eaa4710SRishi Srivatsavai  * This function allocates the main data structures for the bridge driver and
33264eaa4710SRishi Srivatsavai  * connects us into devfs.
33274eaa4710SRishi Srivatsavai  */
33284eaa4710SRishi Srivatsavai static void
33294eaa4710SRishi Srivatsavai bridge_inst_init(void)
33304eaa4710SRishi Srivatsavai {
33314eaa4710SRishi Srivatsavai 	bridge_scan_interval = 5 * drv_usectohz(1000000);
33324eaa4710SRishi Srivatsavai 	bridge_fwd_age = 25 * drv_usectohz(1000000);
33334eaa4710SRishi Srivatsavai 
33344eaa4710SRishi Srivatsavai 	rw_init(&bmac_rwlock, NULL, RW_DRIVER, NULL);
33354eaa4710SRishi Srivatsavai 	list_create(&bmac_list, sizeof (bridge_mac_t),
33364eaa4710SRishi Srivatsavai 	    offsetof(bridge_mac_t, bm_node));
33374eaa4710SRishi Srivatsavai 	list_create(&inst_list, sizeof (bridge_inst_t),
33384eaa4710SRishi Srivatsavai 	    offsetof(bridge_inst_t, bi_node));
33394eaa4710SRishi Srivatsavai 	cv_init(&inst_cv, NULL, CV_DRIVER, NULL);
33404eaa4710SRishi Srivatsavai 	mutex_init(&inst_lock, NULL, MUTEX_DRIVER, NULL);
33414eaa4710SRishi Srivatsavai 	cv_init(&stream_ref_cv, NULL, CV_DRIVER, NULL);
33424eaa4710SRishi Srivatsavai 	mutex_init(&stream_ref_lock, NULL, MUTEX_DRIVER, NULL);
33434eaa4710SRishi Srivatsavai 
33444eaa4710SRishi Srivatsavai 	mac_bridge_vectors(bridge_xmit_cb, bridge_recv_cb, bridge_ref_cb,
33454eaa4710SRishi Srivatsavai 	    bridge_ls_cb);
33464eaa4710SRishi Srivatsavai }
33474eaa4710SRishi Srivatsavai 
33484eaa4710SRishi Srivatsavai /*
33494eaa4710SRishi Srivatsavai  * This function disconnects from devfs and destroys all data structures in
33504eaa4710SRishi Srivatsavai  * preparation for unload.  It's assumed that there are no active bridge
33514eaa4710SRishi Srivatsavai  * references left at this point.
33524eaa4710SRishi Srivatsavai  */
33534eaa4710SRishi Srivatsavai static void
33544eaa4710SRishi Srivatsavai bridge_inst_fini(void)
33554eaa4710SRishi Srivatsavai {
33564eaa4710SRishi Srivatsavai 	mac_bridge_vectors(NULL, NULL, NULL, NULL);
33574eaa4710SRishi Srivatsavai 	if (bridge_timerid != 0)
33584eaa4710SRishi Srivatsavai 		(void) untimeout(bridge_timerid);
33594eaa4710SRishi Srivatsavai 	rw_destroy(&bmac_rwlock);
33604eaa4710SRishi Srivatsavai 	list_destroy(&bmac_list);
33614eaa4710SRishi Srivatsavai 	list_destroy(&inst_list);
33624eaa4710SRishi Srivatsavai 	cv_destroy(&inst_cv);
33634eaa4710SRishi Srivatsavai 	mutex_destroy(&inst_lock);
33644eaa4710SRishi Srivatsavai 	cv_destroy(&stream_ref_cv);
33654eaa4710SRishi Srivatsavai 	mutex_destroy(&stream_ref_lock);
33664eaa4710SRishi Srivatsavai }
33674eaa4710SRishi Srivatsavai 
33684eaa4710SRishi Srivatsavai /*
33694eaa4710SRishi Srivatsavai  * bridge_attach()
33704eaa4710SRishi Srivatsavai  *
33714eaa4710SRishi Srivatsavai  * Description:
33724eaa4710SRishi Srivatsavai  *    Attach bridge driver to the system.
33734eaa4710SRishi Srivatsavai  */
33744eaa4710SRishi Srivatsavai static int
33754eaa4710SRishi Srivatsavai bridge_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
33764eaa4710SRishi Srivatsavai {
33774eaa4710SRishi Srivatsavai 	if (cmd != DDI_ATTACH)
33784eaa4710SRishi Srivatsavai 		return (DDI_FAILURE);
33794eaa4710SRishi Srivatsavai 
33804eaa4710SRishi Srivatsavai 	if (ddi_create_minor_node(dip, BRIDGE_CTL, S_IFCHR, 0, DDI_PSEUDO,
33814eaa4710SRishi Srivatsavai 	    CLONE_DEV) == DDI_FAILURE) {
33824eaa4710SRishi Srivatsavai 		return (DDI_FAILURE);
33834eaa4710SRishi Srivatsavai 	}
33844eaa4710SRishi Srivatsavai 
33854eaa4710SRishi Srivatsavai 	if (dld_ioc_register(BRIDGE_IOC, bridge_ioc_list,
33864eaa4710SRishi Srivatsavai 	    DLDIOCCNT(bridge_ioc_list)) != 0) {
33874eaa4710SRishi Srivatsavai 		ddi_remove_minor_node(dip, BRIDGE_CTL);
33884eaa4710SRishi Srivatsavai 		return (DDI_FAILURE);
33894eaa4710SRishi Srivatsavai 	}
33904eaa4710SRishi Srivatsavai 
33914eaa4710SRishi Srivatsavai 	bridge_dev_info = dip;
33924eaa4710SRishi Srivatsavai 	bridge_major = ddi_driver_major(dip);
33934eaa4710SRishi Srivatsavai 	bridge_taskq = ddi_taskq_create(dip, "bridge", 1, TASKQ_DEFAULTPRI, 0);
33944eaa4710SRishi Srivatsavai 	return (DDI_SUCCESS);
33954eaa4710SRishi Srivatsavai }
33964eaa4710SRishi Srivatsavai 
33974eaa4710SRishi Srivatsavai /*
33984eaa4710SRishi Srivatsavai  * bridge_detach()
33994eaa4710SRishi Srivatsavai  *
34004eaa4710SRishi Srivatsavai  * Description:
34014eaa4710SRishi Srivatsavai  *    Detach an interface to the system.
34024eaa4710SRishi Srivatsavai  */
34034eaa4710SRishi Srivatsavai static int
34044eaa4710SRishi Srivatsavai bridge_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
34054eaa4710SRishi Srivatsavai {
34064eaa4710SRishi Srivatsavai 	if (cmd != DDI_DETACH)
34074eaa4710SRishi Srivatsavai 		return (DDI_FAILURE);
34084eaa4710SRishi Srivatsavai 
34094eaa4710SRishi Srivatsavai 	ddi_remove_minor_node(dip, NULL);
34104eaa4710SRishi Srivatsavai 	ddi_taskq_destroy(bridge_taskq);
34114eaa4710SRishi Srivatsavai 	bridge_dev_info = NULL;
34124eaa4710SRishi Srivatsavai 	return (DDI_SUCCESS);
34134eaa4710SRishi Srivatsavai }
34144eaa4710SRishi Srivatsavai 
34154eaa4710SRishi Srivatsavai /*
34164eaa4710SRishi Srivatsavai  * bridge_info()
34174eaa4710SRishi Srivatsavai  *
34184eaa4710SRishi Srivatsavai  * Description:
34194eaa4710SRishi Srivatsavai  *    Translate "dev_t" to a pointer to the associated "dev_info_t".
34204eaa4710SRishi Srivatsavai  */
34214eaa4710SRishi Srivatsavai /* ARGSUSED */
34224eaa4710SRishi Srivatsavai static int
34234eaa4710SRishi Srivatsavai bridge_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
34244eaa4710SRishi Srivatsavai 	void **result)
34254eaa4710SRishi Srivatsavai {
34264eaa4710SRishi Srivatsavai 	int	rc;
34274eaa4710SRishi Srivatsavai 
34284eaa4710SRishi Srivatsavai 	switch (infocmd) {
34294eaa4710SRishi Srivatsavai 	case DDI_INFO_DEVT2DEVINFO:
34304eaa4710SRishi Srivatsavai 		if (bridge_dev_info == NULL) {
34314eaa4710SRishi Srivatsavai 			rc = DDI_FAILURE;
34324eaa4710SRishi Srivatsavai 		} else {
34334eaa4710SRishi Srivatsavai 			*result = (void *)bridge_dev_info;
34344eaa4710SRishi Srivatsavai 			rc = DDI_SUCCESS;
34354eaa4710SRishi Srivatsavai 		}
34364eaa4710SRishi Srivatsavai 		break;
34374eaa4710SRishi Srivatsavai 	case DDI_INFO_DEVT2INSTANCE:
34384eaa4710SRishi Srivatsavai 		*result = NULL;
34394eaa4710SRishi Srivatsavai 		rc = DDI_SUCCESS;
34404eaa4710SRishi Srivatsavai 		break;
34414eaa4710SRishi Srivatsavai 	default:
34424eaa4710SRishi Srivatsavai 		rc = DDI_FAILURE;
34434eaa4710SRishi Srivatsavai 		break;
34444eaa4710SRishi Srivatsavai 	}
34454eaa4710SRishi Srivatsavai 	return (rc);
34464eaa4710SRishi Srivatsavai }
34474eaa4710SRishi Srivatsavai 
34484eaa4710SRishi Srivatsavai static struct module_info bridge_modinfo = {
34494eaa4710SRishi Srivatsavai 	2105,			/* mi_idnum */
34504eaa4710SRishi Srivatsavai 	"bridge",		/* mi_idname */
34514eaa4710SRishi Srivatsavai 	0,			/* mi_minpsz */
34524eaa4710SRishi Srivatsavai 	16384,			/* mi_maxpsz */
34534eaa4710SRishi Srivatsavai 	65536,			/* mi_hiwat */
34544eaa4710SRishi Srivatsavai 	128			/* mi_lowat */
34554eaa4710SRishi Srivatsavai };
34564eaa4710SRishi Srivatsavai 
34574eaa4710SRishi Srivatsavai static struct qinit bridge_rinit = {
34584eaa4710SRishi Srivatsavai 	NULL,			/* qi_putp */
34594eaa4710SRishi Srivatsavai 	NULL,			/* qi_srvp */
34604eaa4710SRishi Srivatsavai 	bridge_open,		/* qi_qopen */
34614eaa4710SRishi Srivatsavai 	bridge_close,		/* qi_qclose */
34624eaa4710SRishi Srivatsavai 	NULL,			/* qi_qadmin */
34634eaa4710SRishi Srivatsavai 	&bridge_modinfo,	/* qi_minfo */
34644eaa4710SRishi Srivatsavai 	NULL			/* qi_mstat */
34654eaa4710SRishi Srivatsavai };
34664eaa4710SRishi Srivatsavai 
34674eaa4710SRishi Srivatsavai static struct qinit bridge_winit = {
34684eaa4710SRishi Srivatsavai 	(int (*)())bridge_wput, /* qi_putp */
34694eaa4710SRishi Srivatsavai 	NULL,			/* qi_srvp */
34704eaa4710SRishi Srivatsavai 	NULL,			/* qi_qopen */
34714eaa4710SRishi Srivatsavai 	NULL,			/* qi_qclose */
34724eaa4710SRishi Srivatsavai 	NULL,			/* qi_qadmin */
34734eaa4710SRishi Srivatsavai 	&bridge_modinfo,	/* qi_minfo */
34744eaa4710SRishi Srivatsavai 	NULL			/* qi_mstat */
34754eaa4710SRishi Srivatsavai };
34764eaa4710SRishi Srivatsavai 
34774eaa4710SRishi Srivatsavai static struct streamtab bridge_tab = {
34784eaa4710SRishi Srivatsavai 	&bridge_rinit,	/* st_rdinit */
34794eaa4710SRishi Srivatsavai 	&bridge_winit	/* st_wrinit */
34804eaa4710SRishi Srivatsavai };
34814eaa4710SRishi Srivatsavai 
34824eaa4710SRishi Srivatsavai /* No STREAMS perimeters; we do all our own locking */
34834eaa4710SRishi Srivatsavai DDI_DEFINE_STREAM_OPS(bridge_ops, nulldev, nulldev, bridge_attach,
34844eaa4710SRishi Srivatsavai     bridge_detach, nodev, bridge_info, D_NEW | D_MP, &bridge_tab,
34854eaa4710SRishi Srivatsavai     ddi_quiesce_not_supported);
34864eaa4710SRishi Srivatsavai 
34874eaa4710SRishi Srivatsavai static struct modldrv modldrv = {
34884eaa4710SRishi Srivatsavai 	&mod_driverops,
34894eaa4710SRishi Srivatsavai 	"bridging driver",
34904eaa4710SRishi Srivatsavai 	&bridge_ops
34914eaa4710SRishi Srivatsavai };
34924eaa4710SRishi Srivatsavai 
34934eaa4710SRishi Srivatsavai static struct modlinkage modlinkage = {
34944eaa4710SRishi Srivatsavai 	MODREV_1,
34954eaa4710SRishi Srivatsavai 	(void *)&modldrv,
34964eaa4710SRishi Srivatsavai 	NULL
34974eaa4710SRishi Srivatsavai };
34984eaa4710SRishi Srivatsavai 
34994eaa4710SRishi Srivatsavai int
35004eaa4710SRishi Srivatsavai _init(void)
35014eaa4710SRishi Srivatsavai {
35024eaa4710SRishi Srivatsavai 	int retv;
35034eaa4710SRishi Srivatsavai 
35044eaa4710SRishi Srivatsavai 	bridge_inst_init();
35054eaa4710SRishi Srivatsavai 	if ((retv = mod_install(&modlinkage)) != 0)
35064eaa4710SRishi Srivatsavai 		bridge_inst_fini();
35074eaa4710SRishi Srivatsavai 	return (retv);
35084eaa4710SRishi Srivatsavai }
35094eaa4710SRishi Srivatsavai 
35104eaa4710SRishi Srivatsavai int
35114eaa4710SRishi Srivatsavai _fini(void)
35124eaa4710SRishi Srivatsavai {
35134eaa4710SRishi Srivatsavai 	int retv;
35144eaa4710SRishi Srivatsavai 
35154eaa4710SRishi Srivatsavai 	rw_enter(&bmac_rwlock, RW_READER);
35164eaa4710SRishi Srivatsavai 	retv = list_is_empty(&bmac_list) ? 0 : EBUSY;
35174eaa4710SRishi Srivatsavai 	rw_exit(&bmac_rwlock);
35184eaa4710SRishi Srivatsavai 	if (retv == 0 &&
35194eaa4710SRishi Srivatsavai 	    (retv = mod_remove(&modlinkage)) == 0)
35204eaa4710SRishi Srivatsavai 		bridge_inst_fini();
35214eaa4710SRishi Srivatsavai 	return (retv);
35224eaa4710SRishi Srivatsavai }
35234eaa4710SRishi Srivatsavai 
35244eaa4710SRishi Srivatsavai int
35254eaa4710SRishi Srivatsavai _info(struct modinfo *modinfop)
35264eaa4710SRishi Srivatsavai {
35274eaa4710SRishi Srivatsavai 	return (mod_info(&modlinkage, modinfop));
35284eaa4710SRishi Srivatsavai }
3529