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