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