xref: /onnv-gate/usr/src/uts/common/io/bridge.c (revision 11878:ac93462db6d7)
110491SRishi.Srivatsavai@Sun.COM /*
210491SRishi.Srivatsavai@Sun.COM  * CDDL HEADER START
310491SRishi.Srivatsavai@Sun.COM  *
410491SRishi.Srivatsavai@Sun.COM  * The contents of this file are subject to the terms of the
510491SRishi.Srivatsavai@Sun.COM  * Common Development and Distribution License (the "License").
610491SRishi.Srivatsavai@Sun.COM  * You may not use this file except in compliance with the License.
710491SRishi.Srivatsavai@Sun.COM  *
810491SRishi.Srivatsavai@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910491SRishi.Srivatsavai@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010491SRishi.Srivatsavai@Sun.COM  * See the License for the specific language governing permissions
1110491SRishi.Srivatsavai@Sun.COM  * and limitations under the License.
1210491SRishi.Srivatsavai@Sun.COM  *
1310491SRishi.Srivatsavai@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410491SRishi.Srivatsavai@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510491SRishi.Srivatsavai@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610491SRishi.Srivatsavai@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710491SRishi.Srivatsavai@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810491SRishi.Srivatsavai@Sun.COM  *
1910491SRishi.Srivatsavai@Sun.COM  * CDDL HEADER END
2010491SRishi.Srivatsavai@Sun.COM  */
2110491SRishi.Srivatsavai@Sun.COM 
2210491SRishi.Srivatsavai@Sun.COM /*
2311483SRishi.Srivatsavai@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2410491SRishi.Srivatsavai@Sun.COM  * Use is subject to license terms.
2510491SRishi.Srivatsavai@Sun.COM  */
2610491SRishi.Srivatsavai@Sun.COM 
2710491SRishi.Srivatsavai@Sun.COM /*
2810491SRishi.Srivatsavai@Sun.COM  * This module implements a STREAMS driver that provides layer-two (Ethernet)
2910491SRishi.Srivatsavai@Sun.COM  * bridging functionality.  The STREAMS interface is used to provide
3010491SRishi.Srivatsavai@Sun.COM  * observability (snoop/wireshark) and control, but not for interface plumbing.
3110491SRishi.Srivatsavai@Sun.COM  */
3210491SRishi.Srivatsavai@Sun.COM 
3310491SRishi.Srivatsavai@Sun.COM #include <sys/types.h>
3410491SRishi.Srivatsavai@Sun.COM #include <sys/bitmap.h>
3510491SRishi.Srivatsavai@Sun.COM #include <sys/cmn_err.h>
3610491SRishi.Srivatsavai@Sun.COM #include <sys/conf.h>
3710491SRishi.Srivatsavai@Sun.COM #include <sys/ddi.h>
3810491SRishi.Srivatsavai@Sun.COM #include <sys/errno.h>
3910491SRishi.Srivatsavai@Sun.COM #include <sys/kstat.h>
4010491SRishi.Srivatsavai@Sun.COM #include <sys/modctl.h>
4110491SRishi.Srivatsavai@Sun.COM #include <sys/note.h>
4210491SRishi.Srivatsavai@Sun.COM #include <sys/param.h>
4310491SRishi.Srivatsavai@Sun.COM #include <sys/policy.h>
4410491SRishi.Srivatsavai@Sun.COM #include <sys/sdt.h>
4510491SRishi.Srivatsavai@Sun.COM #include <sys/stat.h>
4610491SRishi.Srivatsavai@Sun.COM #include <sys/stream.h>
4710491SRishi.Srivatsavai@Sun.COM #include <sys/stropts.h>
4810491SRishi.Srivatsavai@Sun.COM #include <sys/strsun.h>
4910491SRishi.Srivatsavai@Sun.COM #include <sys/sunddi.h>
5010491SRishi.Srivatsavai@Sun.COM #include <sys/sysmacros.h>
5110491SRishi.Srivatsavai@Sun.COM #include <sys/systm.h>
5210491SRishi.Srivatsavai@Sun.COM #include <sys/time.h>
5310491SRishi.Srivatsavai@Sun.COM #include <sys/dlpi.h>
5410491SRishi.Srivatsavai@Sun.COM #include <sys/dls.h>
5510491SRishi.Srivatsavai@Sun.COM #include <sys/mac_ether.h>
5610491SRishi.Srivatsavai@Sun.COM #include <sys/mac_provider.h>
5710491SRishi.Srivatsavai@Sun.COM #include <sys/mac_client_priv.h>
5810491SRishi.Srivatsavai@Sun.COM #include <sys/mac_impl.h>
5910491SRishi.Srivatsavai@Sun.COM #include <sys/vlan.h>
6010491SRishi.Srivatsavai@Sun.COM #include <net/bridge.h>
6110491SRishi.Srivatsavai@Sun.COM #include <net/bridge_impl.h>
6210491SRishi.Srivatsavai@Sun.COM #include <net/trill.h>
6310530SRishi.Srivatsavai@Sun.COM #include <sys/dld_ioc.h>
6410491SRishi.Srivatsavai@Sun.COM 
6510491SRishi.Srivatsavai@Sun.COM /*
6610491SRishi.Srivatsavai@Sun.COM  * Locks and reference counts: object lifetime and design.
6710491SRishi.Srivatsavai@Sun.COM  *
6810491SRishi.Srivatsavai@Sun.COM  * bridge_mac_t
6910491SRishi.Srivatsavai@Sun.COM  *   Bridge mac (snoop) instances are in bmac_list, which is protected by
7010491SRishi.Srivatsavai@Sun.COM  *   bmac_rwlock.  They're allocated by bmac_alloc and freed by bridge_timer().
7110491SRishi.Srivatsavai@Sun.COM  *   Every bridge_inst_t has a single bridge_mac_t, but when bridge_inst_t goes
7210491SRishi.Srivatsavai@Sun.COM  *   away, the bridge_mac_t remains until either all of the users go away
7310491SRishi.Srivatsavai@Sun.COM  *   (detected by a timer) or until the instance is picked up again by the same
7410491SRishi.Srivatsavai@Sun.COM  *   bridge starting back up.
7510491SRishi.Srivatsavai@Sun.COM  *
7610491SRishi.Srivatsavai@Sun.COM  * bridge_inst_t
7710491SRishi.Srivatsavai@Sun.COM  *   Bridge instances are in inst_list, which is protected by inst_lock.
7810491SRishi.Srivatsavai@Sun.COM  *   They're allocated by inst_alloc() and freed by inst_free().  After
7910491SRishi.Srivatsavai@Sun.COM  *   allocation, an instance is placed in inst_list, and the reference count is
8010491SRishi.Srivatsavai@Sun.COM  *   incremented to represent this.  That reference is decremented when the
8110491SRishi.Srivatsavai@Sun.COM  *   BIF_SHUTDOWN flag is set, and no new increments may occur.  When the last
8210491SRishi.Srivatsavai@Sun.COM  *   reference is freed, the instance is removed from the list.
8310491SRishi.Srivatsavai@Sun.COM  *
8410491SRishi.Srivatsavai@Sun.COM  *   Bridge instances have lists of links and an AVL tree of forwarding
8510491SRishi.Srivatsavai@Sun.COM  *   entries.  Each of these structures holds one reference on the bridge
8610491SRishi.Srivatsavai@Sun.COM  *   instance.  These lists and tree are protected by bi_rwlock.
8710491SRishi.Srivatsavai@Sun.COM  *
8810491SRishi.Srivatsavai@Sun.COM  * bridge_stream_t
8910491SRishi.Srivatsavai@Sun.COM  *   Bridge streams are allocated by stream_alloc() and freed by stream_free().
9010491SRishi.Srivatsavai@Sun.COM  *   These streams are created when "bridged" opens /dev/bridgectl, and are
9110491SRishi.Srivatsavai@Sun.COM  *   used to create new bridge instances (via BRIOC_NEWBRIDGE) and control the
9210491SRishi.Srivatsavai@Sun.COM  *   links on the bridge.  When a stream closes, the bridge instance created is
9310491SRishi.Srivatsavai@Sun.COM  *   destroyed.  There's at most one bridge instance for a given control
9410491SRishi.Srivatsavai@Sun.COM  *   stream.
9510491SRishi.Srivatsavai@Sun.COM  *
9610491SRishi.Srivatsavai@Sun.COM  * bridge_link_t
9710491SRishi.Srivatsavai@Sun.COM  *   Links are allocated by bridge_add_link() and freed by link_free().  The
9810491SRishi.Srivatsavai@Sun.COM  *   bi_links list holds a reference to the link.  When the BLF_DELETED flag is
9910491SRishi.Srivatsavai@Sun.COM  *   set, that reference is dropped.  The link isn't removed from the list
10010491SRishi.Srivatsavai@Sun.COM  *   until the last reference drops.  Each forwarding entry that uses a given
10110491SRishi.Srivatsavai@Sun.COM  *   link holds a reference, as does each thread transmitting a packet via the
10210491SRishi.Srivatsavai@Sun.COM  *   link.  The MAC layer calls in via bridge_ref_cb() to hold a reference on
10310491SRishi.Srivatsavai@Sun.COM  *   a link when transmitting.
10410491SRishi.Srivatsavai@Sun.COM  *
10510491SRishi.Srivatsavai@Sun.COM  *   It's important that once BLF_DELETED is set, there's no way for the
10610491SRishi.Srivatsavai@Sun.COM  *   reference count to increase again.  If it can, then the link may be
10710491SRishi.Srivatsavai@Sun.COM  *   double-freed.  The BLF_FREED flag is intended for use with assertions to
10810491SRishi.Srivatsavai@Sun.COM  *   guard against this in testing.
10910491SRishi.Srivatsavai@Sun.COM  *
11010491SRishi.Srivatsavai@Sun.COM  * bridge_fwd_t
11110491SRishi.Srivatsavai@Sun.COM  *   Bridge forwarding entries are allocated by bridge_recv_cb() and freed by
11210491SRishi.Srivatsavai@Sun.COM  *   fwd_free().  The bi_fwd AVL tree holds one reference to the entry.  Unlike
11310491SRishi.Srivatsavai@Sun.COM  *   other data structures, the reference is dropped when the entry is removed
11410491SRishi.Srivatsavai@Sun.COM  *   from the tree by fwd_delete(), and the BFF_INTREE flag is removed.  Each
11510491SRishi.Srivatsavai@Sun.COM  *   thread that's forwarding a packet to a known destination holds a reference
11610491SRishi.Srivatsavai@Sun.COM  *   to a forwarding entry.
11710491SRishi.Srivatsavai@Sun.COM  *
11810491SRishi.Srivatsavai@Sun.COM  * TRILL notes:
11910491SRishi.Srivatsavai@Sun.COM  *
12010491SRishi.Srivatsavai@Sun.COM  *   The TRILL module does all of its I/O through bridging.  It uses references
12110491SRishi.Srivatsavai@Sun.COM  *   on the bridge_inst_t and bridge_link_t structures, and has seven entry
12210491SRishi.Srivatsavai@Sun.COM  *   points and four callbacks.  One entry point is for setting the callbacks
12310491SRishi.Srivatsavai@Sun.COM  *   (bridge_trill_register_cb).  There are four entry points for taking bridge
12410491SRishi.Srivatsavai@Sun.COM  *   and link references (bridge_trill_{br,ln}{ref,unref}).  The final two
12510491SRishi.Srivatsavai@Sun.COM  *   entry points are for decapsulated packets from TRILL (bridge_trill_decaps)
12610491SRishi.Srivatsavai@Sun.COM  *   that need to be bridged locally, and for TRILL-encapsulated output packets
12710491SRishi.Srivatsavai@Sun.COM  *   (bridge_trill_output).
12810491SRishi.Srivatsavai@Sun.COM  *
12910491SRishi.Srivatsavai@Sun.COM  *   The four callbacks comprise two notification functions for bridges and
13010491SRishi.Srivatsavai@Sun.COM  *   links being deleted, one function for raw received TRILL packets, and one
13110491SRishi.Srivatsavai@Sun.COM  *   for bridge output to non-local TRILL destinations (tunnel entry).
13210491SRishi.Srivatsavai@Sun.COM  */
13310491SRishi.Srivatsavai@Sun.COM 
13410491SRishi.Srivatsavai@Sun.COM /*
13510491SRishi.Srivatsavai@Sun.COM  * Ethernet reserved multicast addresses for TRILL; used also in TRILL module.
13610491SRishi.Srivatsavai@Sun.COM  */
13710491SRishi.Srivatsavai@Sun.COM const uint8_t all_isis_rbridges[] = ALL_ISIS_RBRIDGES;
13810491SRishi.Srivatsavai@Sun.COM static const uint8_t all_esadi_rbridges[] = ALL_ESADI_RBRIDGES;
13910491SRishi.Srivatsavai@Sun.COM const uint8_t bridge_group_address[] = BRIDGE_GROUP_ADDRESS;
14010491SRishi.Srivatsavai@Sun.COM 
14110491SRishi.Srivatsavai@Sun.COM static const char *inst_kstats_list[] = { KSINST_NAMES };
14210491SRishi.Srivatsavai@Sun.COM static const char *link_kstats_list[] = { KSLINK_NAMES };
14310491SRishi.Srivatsavai@Sun.COM 
14410491SRishi.Srivatsavai@Sun.COM #define	KREF(p, m, vn)	p->m.vn.value.ui64
14510491SRishi.Srivatsavai@Sun.COM #define	KINCR(p, m, vn)	++KREF(p, m, vn)
14610491SRishi.Srivatsavai@Sun.COM #define	KDECR(p, m, vn)	--KREF(p, m, vn)
14710491SRishi.Srivatsavai@Sun.COM 
14810491SRishi.Srivatsavai@Sun.COM #define	KIPINCR(p, vn)	KINCR(p, bi_kstats, vn)
14910491SRishi.Srivatsavai@Sun.COM #define	KIPDECR(p, vn)	KDECR(p, bi_kstats, vn)
15010491SRishi.Srivatsavai@Sun.COM #define	KLPINCR(p, vn)	KINCR(p, bl_kstats, vn)
15110491SRishi.Srivatsavai@Sun.COM 
15210491SRishi.Srivatsavai@Sun.COM #define	KIINCR(vn)	KIPINCR(bip, vn)
15310491SRishi.Srivatsavai@Sun.COM #define	KIDECR(vn)	KIPDECR(bip, vn)
15410491SRishi.Srivatsavai@Sun.COM #define	KLINCR(vn)	KLPINCR(blp, vn)
15510491SRishi.Srivatsavai@Sun.COM 
15610491SRishi.Srivatsavai@Sun.COM #define	Dim(x)		(sizeof (x) / sizeof (*(x)))
15710491SRishi.Srivatsavai@Sun.COM 
15810491SRishi.Srivatsavai@Sun.COM /* Amount of overhead added when encapsulating with VLAN headers */
15910491SRishi.Srivatsavai@Sun.COM #define	VLAN_INCR	(sizeof (struct ether_vlan_header) -	\
16010491SRishi.Srivatsavai@Sun.COM 			sizeof (struct ether_header))
16110491SRishi.Srivatsavai@Sun.COM 
16210491SRishi.Srivatsavai@Sun.COM static dev_info_t *bridge_dev_info;
16310491SRishi.Srivatsavai@Sun.COM static major_t bridge_major;
16410491SRishi.Srivatsavai@Sun.COM static ddi_taskq_t *bridge_taskq;
16510491SRishi.Srivatsavai@Sun.COM 
16610491SRishi.Srivatsavai@Sun.COM /*
16710491SRishi.Srivatsavai@Sun.COM  * These are the bridge instance management data structures.  The mutex lock
16810491SRishi.Srivatsavai@Sun.COM  * protects the list of bridge instances.  A reference count is then used on
16910491SRishi.Srivatsavai@Sun.COM  * each instance to determine when to free it.  We use mac_minor_hold() to
17010491SRishi.Srivatsavai@Sun.COM  * allocate minor_t values, which are used both for self-cloning /dev/net/
17110491SRishi.Srivatsavai@Sun.COM  * device nodes as well as client streams.  Minor node 0 is reserved for the
17210491SRishi.Srivatsavai@Sun.COM  * allocation control node.
17310491SRishi.Srivatsavai@Sun.COM  */
17410491SRishi.Srivatsavai@Sun.COM static list_t inst_list;
17510491SRishi.Srivatsavai@Sun.COM static kcondvar_t inst_cv;		/* Allows us to wait for shutdown */
17610491SRishi.Srivatsavai@Sun.COM static kmutex_t inst_lock;
17710491SRishi.Srivatsavai@Sun.COM 
17810491SRishi.Srivatsavai@Sun.COM static krwlock_t bmac_rwlock;
17910491SRishi.Srivatsavai@Sun.COM static list_t bmac_list;
18010491SRishi.Srivatsavai@Sun.COM 
18110491SRishi.Srivatsavai@Sun.COM /* Wait for taskq entries that use STREAMS */
18210491SRishi.Srivatsavai@Sun.COM static kcondvar_t stream_ref_cv;
18310491SRishi.Srivatsavai@Sun.COM static kmutex_t stream_ref_lock;
18410491SRishi.Srivatsavai@Sun.COM 
18510491SRishi.Srivatsavai@Sun.COM static timeout_id_t bridge_timerid;
18610491SRishi.Srivatsavai@Sun.COM static clock_t bridge_scan_interval;
18710491SRishi.Srivatsavai@Sun.COM static clock_t bridge_fwd_age;
18810491SRishi.Srivatsavai@Sun.COM 
18910491SRishi.Srivatsavai@Sun.COM static bridge_inst_t *bridge_find_name(const char *);
19010491SRishi.Srivatsavai@Sun.COM static void bridge_timer(void *);
19110491SRishi.Srivatsavai@Sun.COM static void bridge_unref(bridge_inst_t *);
19210491SRishi.Srivatsavai@Sun.COM 
19310491SRishi.Srivatsavai@Sun.COM static const uint8_t zero_addr[ETHERADDRL] = { 0 };
19410491SRishi.Srivatsavai@Sun.COM 
19510491SRishi.Srivatsavai@Sun.COM /* Global TRILL linkage */
19610491SRishi.Srivatsavai@Sun.COM static trill_recv_pkt_t trill_recv_fn;
19710491SRishi.Srivatsavai@Sun.COM static trill_encap_pkt_t trill_encap_fn;
19810491SRishi.Srivatsavai@Sun.COM static trill_br_dstr_t trill_brdstr_fn;
19910491SRishi.Srivatsavai@Sun.COM static trill_ln_dstr_t trill_lndstr_fn;
20010491SRishi.Srivatsavai@Sun.COM 
20110491SRishi.Srivatsavai@Sun.COM /* special settings to accommodate DLD flow control; see dld_str.c */
20210491SRishi.Srivatsavai@Sun.COM static struct module_info bridge_dld_modinfo = {
20310491SRishi.Srivatsavai@Sun.COM 	0,			/* mi_idnum */
20411109SRishi.Srivatsavai@Sun.COM 	BRIDGE_DEV_NAME,	/* mi_idname */
20510491SRishi.Srivatsavai@Sun.COM 	0,			/* mi_minpsz */
20610491SRishi.Srivatsavai@Sun.COM 	INFPSZ,			/* mi_maxpsz */
20710491SRishi.Srivatsavai@Sun.COM 	1,			/* mi_hiwat */
20810491SRishi.Srivatsavai@Sun.COM 	0			/* mi_lowat */
20910491SRishi.Srivatsavai@Sun.COM };
21010491SRishi.Srivatsavai@Sun.COM 
21110491SRishi.Srivatsavai@Sun.COM static struct qinit bridge_dld_rinit = {
21210491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_putp */
21310491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_srvp */
21410491SRishi.Srivatsavai@Sun.COM 	dld_open,		/* qi_qopen */
21510491SRishi.Srivatsavai@Sun.COM 	dld_close,		/* qi_qclose */
21610491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qadmin */
21710491SRishi.Srivatsavai@Sun.COM 	&bridge_dld_modinfo,	/* qi_minfo */
21810491SRishi.Srivatsavai@Sun.COM 	NULL			/* qi_mstat */
21910491SRishi.Srivatsavai@Sun.COM };
22010491SRishi.Srivatsavai@Sun.COM 
22110491SRishi.Srivatsavai@Sun.COM static struct qinit bridge_dld_winit = {
22210491SRishi.Srivatsavai@Sun.COM 	(int (*)())dld_wput,	/* qi_putp */
22310491SRishi.Srivatsavai@Sun.COM 	(int (*)())dld_wsrv,	/* qi_srvp */
22410491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qopen */
22510491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qclose */
22610491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qadmin */
22710491SRishi.Srivatsavai@Sun.COM 	&bridge_dld_modinfo,	/* qi_minfo */
22810491SRishi.Srivatsavai@Sun.COM 	NULL			/* qi_mstat */
22910491SRishi.Srivatsavai@Sun.COM };
23010491SRishi.Srivatsavai@Sun.COM 
23110491SRishi.Srivatsavai@Sun.COM static int bridge_ioc_listfwd(void *, intptr_t, int, cred_t *, int *);
23210491SRishi.Srivatsavai@Sun.COM 
23310491SRishi.Srivatsavai@Sun.COM /* GLDv3 control ioctls used by Bridging */
23410491SRishi.Srivatsavai@Sun.COM static dld_ioc_info_t bridge_ioc_list[] = {
23510491SRishi.Srivatsavai@Sun.COM 	{BRIDGE_IOC_LISTFWD, DLDCOPYINOUT, sizeof (bridge_listfwd_t),
23610491SRishi.Srivatsavai@Sun.COM 	    bridge_ioc_listfwd, NULL},
23710491SRishi.Srivatsavai@Sun.COM };
23810491SRishi.Srivatsavai@Sun.COM 
23910491SRishi.Srivatsavai@Sun.COM /*
24010491SRishi.Srivatsavai@Sun.COM  * Given a bridge mac pointer, get a ref-held pointer to the corresponding
24110491SRishi.Srivatsavai@Sun.COM  * bridge instance, if any.  We must hold the global bmac_rwlock so that
24210491SRishi.Srivatsavai@Sun.COM  * bm_inst doesn't slide out from under us.
24310491SRishi.Srivatsavai@Sun.COM  */
24410491SRishi.Srivatsavai@Sun.COM static bridge_inst_t *
mac_to_inst(const bridge_mac_t * bmp)24510491SRishi.Srivatsavai@Sun.COM mac_to_inst(const bridge_mac_t *bmp)
24610491SRishi.Srivatsavai@Sun.COM {
24710491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
24810491SRishi.Srivatsavai@Sun.COM 
24910491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bmac_rwlock, RW_READER);
25010491SRishi.Srivatsavai@Sun.COM 	if ((bip = bmp->bm_inst) != NULL)
25110491SRishi.Srivatsavai@Sun.COM 		atomic_inc_uint(&bip->bi_refs);
25210491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bmac_rwlock);
25310491SRishi.Srivatsavai@Sun.COM 	return (bip);
25410491SRishi.Srivatsavai@Sun.COM }
25510491SRishi.Srivatsavai@Sun.COM 
25610491SRishi.Srivatsavai@Sun.COM static void
link_sdu_fail(bridge_link_t * blp,boolean_t failed,mblk_t ** mlist)25710491SRishi.Srivatsavai@Sun.COM link_sdu_fail(bridge_link_t *blp, boolean_t failed, mblk_t **mlist)
25810491SRishi.Srivatsavai@Sun.COM {
25910491SRishi.Srivatsavai@Sun.COM 	mblk_t *mp;
26010491SRishi.Srivatsavai@Sun.COM 	bridge_ctl_t *bcp;
26110491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blcmp;
26210491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
26310491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp;
26410491SRishi.Srivatsavai@Sun.COM 
26510491SRishi.Srivatsavai@Sun.COM 	if (failed) {
26610491SRishi.Srivatsavai@Sun.COM 		if (blp->bl_flags & BLF_SDUFAIL)
26710491SRishi.Srivatsavai@Sun.COM 			return;
26810491SRishi.Srivatsavai@Sun.COM 		blp->bl_flags |= BLF_SDUFAIL;
26910491SRishi.Srivatsavai@Sun.COM 	} else {
27010491SRishi.Srivatsavai@Sun.COM 		if (!(blp->bl_flags & BLF_SDUFAIL))
27110491SRishi.Srivatsavai@Sun.COM 			return;
27210491SRishi.Srivatsavai@Sun.COM 		blp->bl_flags &= ~BLF_SDUFAIL;
27310491SRishi.Srivatsavai@Sun.COM 	}
27410491SRishi.Srivatsavai@Sun.COM 
27510491SRishi.Srivatsavai@Sun.COM 	/*
27610491SRishi.Srivatsavai@Sun.COM 	 * If this link is otherwise up, then check if there are any other
27710491SRishi.Srivatsavai@Sun.COM 	 * non-failed non-down links.  If not, then we control the state of the
27810491SRishi.Srivatsavai@Sun.COM 	 * whole bridge.
27910491SRishi.Srivatsavai@Sun.COM 	 */
28010491SRishi.Srivatsavai@Sun.COM 	bip = blp->bl_inst;
28110491SRishi.Srivatsavai@Sun.COM 	bmp = bip->bi_mac;
28210491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_linkstate != LINK_STATE_DOWN) {
28310491SRishi.Srivatsavai@Sun.COM 		for (blcmp = list_head(&bip->bi_links); blcmp != NULL;
28410491SRishi.Srivatsavai@Sun.COM 		    blcmp = list_next(&bip->bi_links, blcmp)) {
28510491SRishi.Srivatsavai@Sun.COM 			if (blp != blcmp &&
28610491SRishi.Srivatsavai@Sun.COM 			    !(blcmp->bl_flags & (BLF_DELETED|BLF_SDUFAIL)) &&
28710491SRishi.Srivatsavai@Sun.COM 			    blcmp->bl_linkstate != LINK_STATE_DOWN)
28810491SRishi.Srivatsavai@Sun.COM 				break;
28910491SRishi.Srivatsavai@Sun.COM 		}
29010491SRishi.Srivatsavai@Sun.COM 		if (blcmp == NULL) {
29110491SRishi.Srivatsavai@Sun.COM 			bmp->bm_linkstate = failed ? LINK_STATE_DOWN :
29210491SRishi.Srivatsavai@Sun.COM 			    LINK_STATE_UP;
29310491SRishi.Srivatsavai@Sun.COM 			mac_link_redo(bmp->bm_mh, bmp->bm_linkstate);
29410491SRishi.Srivatsavai@Sun.COM 		}
29510491SRishi.Srivatsavai@Sun.COM 	}
29610491SRishi.Srivatsavai@Sun.COM 
29710491SRishi.Srivatsavai@Sun.COM 	/*
29810491SRishi.Srivatsavai@Sun.COM 	 * If we're becoming failed, then the link's current true state needs
29910491SRishi.Srivatsavai@Sun.COM 	 * to be reflected upwards to this link's clients.  If we're becoming
30010491SRishi.Srivatsavai@Sun.COM 	 * unfailed, then we get the state of the bridge instead on all
30110491SRishi.Srivatsavai@Sun.COM 	 * clients.
30210491SRishi.Srivatsavai@Sun.COM 	 */
30310491SRishi.Srivatsavai@Sun.COM 	if (failed) {
30410491SRishi.Srivatsavai@Sun.COM 		if (bmp->bm_linkstate != blp->bl_linkstate)
30510491SRishi.Srivatsavai@Sun.COM 			mac_link_redo(blp->bl_mh, blp->bl_linkstate);
30610491SRishi.Srivatsavai@Sun.COM 	} else {
30710491SRishi.Srivatsavai@Sun.COM 		mac_link_redo(blp->bl_mh, bmp->bm_linkstate);
30810491SRishi.Srivatsavai@Sun.COM 	}
30910491SRishi.Srivatsavai@Sun.COM 
31010491SRishi.Srivatsavai@Sun.COM 	/* get the current mblk we're going to send up */
31110491SRishi.Srivatsavai@Sun.COM 	if ((mp = blp->bl_lfailmp) == NULL &&
31210491SRishi.Srivatsavai@Sun.COM 	    (mp = allocb(sizeof (bridge_ctl_t), BPRI_MED)) == NULL)
31310491SRishi.Srivatsavai@Sun.COM 		return;
31410491SRishi.Srivatsavai@Sun.COM 
31510491SRishi.Srivatsavai@Sun.COM 	/* get a new one for next time */
31610491SRishi.Srivatsavai@Sun.COM 	blp->bl_lfailmp = allocb(sizeof (bridge_ctl_t), BPRI_MED);
31710491SRishi.Srivatsavai@Sun.COM 
31810491SRishi.Srivatsavai@Sun.COM 	/* if none for next time, then report only failures */
31910491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_lfailmp == NULL && !failed) {
32010491SRishi.Srivatsavai@Sun.COM 		blp->bl_lfailmp = mp;
32110491SRishi.Srivatsavai@Sun.COM 		return;
32210491SRishi.Srivatsavai@Sun.COM 	}
32310491SRishi.Srivatsavai@Sun.COM 
32410491SRishi.Srivatsavai@Sun.COM 	/* LINTED: alignment */
32510491SRishi.Srivatsavai@Sun.COM 	bcp = (bridge_ctl_t *)mp->b_rptr;
32610491SRishi.Srivatsavai@Sun.COM 	bcp->bc_linkid = blp->bl_linkid;
32710491SRishi.Srivatsavai@Sun.COM 	bcp->bc_failed = failed;
32810491SRishi.Srivatsavai@Sun.COM 	mp->b_wptr = (uchar_t *)(bcp + 1);
32910491SRishi.Srivatsavai@Sun.COM 	mp->b_next = *mlist;
33010491SRishi.Srivatsavai@Sun.COM 	*mlist = mp;
33110491SRishi.Srivatsavai@Sun.COM }
33210491SRishi.Srivatsavai@Sun.COM 
33310491SRishi.Srivatsavai@Sun.COM /*
33410491SRishi.Srivatsavai@Sun.COM  * Send control messages (link SDU changes) using the stream to the
33510491SRishi.Srivatsavai@Sun.COM  * bridge instance daemon.
33610491SRishi.Srivatsavai@Sun.COM  */
33710491SRishi.Srivatsavai@Sun.COM static void
send_up_messages(bridge_inst_t * bip,mblk_t * mp)33810491SRishi.Srivatsavai@Sun.COM send_up_messages(bridge_inst_t *bip, mblk_t *mp)
33910491SRishi.Srivatsavai@Sun.COM {
34010491SRishi.Srivatsavai@Sun.COM 	mblk_t *mnext;
34110491SRishi.Srivatsavai@Sun.COM 	queue_t *rq;
34210491SRishi.Srivatsavai@Sun.COM 
34310491SRishi.Srivatsavai@Sun.COM 	rq = bip->bi_control->bs_wq;
34410491SRishi.Srivatsavai@Sun.COM 	rq = OTHERQ(rq);
34510491SRishi.Srivatsavai@Sun.COM 	while (mp != NULL) {
34610491SRishi.Srivatsavai@Sun.COM 		mnext = mp->b_next;
34710491SRishi.Srivatsavai@Sun.COM 		mp->b_next = NULL;
34810491SRishi.Srivatsavai@Sun.COM 		putnext(rq, mp);
34910491SRishi.Srivatsavai@Sun.COM 		mp = mnext;
35010491SRishi.Srivatsavai@Sun.COM 	}
35110491SRishi.Srivatsavai@Sun.COM }
35210491SRishi.Srivatsavai@Sun.COM 
35310491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
35410491SRishi.Srivatsavai@Sun.COM static int
bridge_m_getstat(void * arg,uint_t stat,uint64_t * val)35510491SRishi.Srivatsavai@Sun.COM bridge_m_getstat(void *arg, uint_t stat, uint64_t *val)
35610491SRishi.Srivatsavai@Sun.COM {
35710491SRishi.Srivatsavai@Sun.COM 	return (ENOTSUP);
35810491SRishi.Srivatsavai@Sun.COM }
35910491SRishi.Srivatsavai@Sun.COM 
36010491SRishi.Srivatsavai@Sun.COM static int
bridge_m_start(void * arg)36110491SRishi.Srivatsavai@Sun.COM bridge_m_start(void *arg)
36210491SRishi.Srivatsavai@Sun.COM {
36310491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp = arg;
36410491SRishi.Srivatsavai@Sun.COM 
36510491SRishi.Srivatsavai@Sun.COM 	bmp->bm_flags |= BMF_STARTED;
36610491SRishi.Srivatsavai@Sun.COM 	return (0);
36710491SRishi.Srivatsavai@Sun.COM }
36810491SRishi.Srivatsavai@Sun.COM 
36910491SRishi.Srivatsavai@Sun.COM static void
bridge_m_stop(void * arg)37010491SRishi.Srivatsavai@Sun.COM bridge_m_stop(void *arg)
37110491SRishi.Srivatsavai@Sun.COM {
37210491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp = arg;
37310491SRishi.Srivatsavai@Sun.COM 
37410491SRishi.Srivatsavai@Sun.COM 	bmp->bm_flags &= ~BMF_STARTED;
37510491SRishi.Srivatsavai@Sun.COM }
37610491SRishi.Srivatsavai@Sun.COM 
37710491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
37810491SRishi.Srivatsavai@Sun.COM static int
bridge_m_setpromisc(void * arg,boolean_t on)37910491SRishi.Srivatsavai@Sun.COM bridge_m_setpromisc(void *arg, boolean_t on)
38010491SRishi.Srivatsavai@Sun.COM {
38110491SRishi.Srivatsavai@Sun.COM 	return (0);
38210491SRishi.Srivatsavai@Sun.COM }
38310491SRishi.Srivatsavai@Sun.COM 
38410491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
38510491SRishi.Srivatsavai@Sun.COM static int
bridge_m_multicst(void * arg,boolean_t add,const uint8_t * mca)38610491SRishi.Srivatsavai@Sun.COM bridge_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
38710491SRishi.Srivatsavai@Sun.COM {
38810491SRishi.Srivatsavai@Sun.COM 	return (0);
38910491SRishi.Srivatsavai@Sun.COM }
39010491SRishi.Srivatsavai@Sun.COM 
39110491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
39210491SRishi.Srivatsavai@Sun.COM static int
bridge_m_unicst(void * arg,const uint8_t * macaddr)39310491SRishi.Srivatsavai@Sun.COM bridge_m_unicst(void *arg, const uint8_t *macaddr)
39410491SRishi.Srivatsavai@Sun.COM {
39510491SRishi.Srivatsavai@Sun.COM 	return (ENOTSUP);
39610491SRishi.Srivatsavai@Sun.COM }
39710491SRishi.Srivatsavai@Sun.COM 
39810491SRishi.Srivatsavai@Sun.COM static mblk_t *
bridge_m_tx(void * arg,mblk_t * mp)39910491SRishi.Srivatsavai@Sun.COM bridge_m_tx(void *arg, mblk_t *mp)
40010491SRishi.Srivatsavai@Sun.COM {
40110491SRishi.Srivatsavai@Sun.COM 	_NOTE(ARGUNUSED(arg));
40210491SRishi.Srivatsavai@Sun.COM 	freemsgchain(mp);
40310491SRishi.Srivatsavai@Sun.COM 	return (NULL);
40410491SRishi.Srivatsavai@Sun.COM }
40510491SRishi.Srivatsavai@Sun.COM 
40610491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
40710491SRishi.Srivatsavai@Sun.COM static int
bridge_ioc_listfwd(void * karg,intptr_t arg,int mode,cred_t * cred,int * rvalp)40810491SRishi.Srivatsavai@Sun.COM bridge_ioc_listfwd(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
40910491SRishi.Srivatsavai@Sun.COM {
41010491SRishi.Srivatsavai@Sun.COM 	bridge_listfwd_t *blf = karg;
41110491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
41210491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp, match;
41310491SRishi.Srivatsavai@Sun.COM 	avl_index_t where;
41410491SRishi.Srivatsavai@Sun.COM 
41510491SRishi.Srivatsavai@Sun.COM 	bip = bridge_find_name(blf->blf_name);
41610491SRishi.Srivatsavai@Sun.COM 	if (bip == NULL)
41710491SRishi.Srivatsavai@Sun.COM 		return (ENOENT);
41810491SRishi.Srivatsavai@Sun.COM 
41910491SRishi.Srivatsavai@Sun.COM 	bcopy(blf->blf_dest, match.bf_dest, ETHERADDRL);
42010491SRishi.Srivatsavai@Sun.COM 	match.bf_flags |= BFF_VLANLOCAL;
42110491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_READER);
42210491SRishi.Srivatsavai@Sun.COM 	if ((bfp = avl_find(&bip->bi_fwd, &match, &where)) == NULL)
42310491SRishi.Srivatsavai@Sun.COM 		bfp = avl_nearest(&bip->bi_fwd, where, AVL_AFTER);
42410491SRishi.Srivatsavai@Sun.COM 	else
42510491SRishi.Srivatsavai@Sun.COM 		bfp = AVL_NEXT(&bip->bi_fwd, bfp);
42610491SRishi.Srivatsavai@Sun.COM 	if (bfp == NULL) {
42710491SRishi.Srivatsavai@Sun.COM 		bzero(blf, sizeof (*blf));
42810491SRishi.Srivatsavai@Sun.COM 	} else {
42910491SRishi.Srivatsavai@Sun.COM 		bcopy(bfp->bf_dest, blf->blf_dest, ETHERADDRL);
43010491SRishi.Srivatsavai@Sun.COM 		blf->blf_trill_nick = bfp->bf_trill_nick;
43110491SRishi.Srivatsavai@Sun.COM 		blf->blf_ms_age =
43211066Srafael.vanoni@sun.com 		    drv_hztousec(ddi_get_lbolt() - bfp->bf_lastheard) / 1000;
43310491SRishi.Srivatsavai@Sun.COM 		blf->blf_is_local =
43410491SRishi.Srivatsavai@Sun.COM 		    (bfp->bf_flags & BFF_LOCALADDR) != 0;
43510491SRishi.Srivatsavai@Sun.COM 		blf->blf_linkid = bfp->bf_links[0]->bl_linkid;
43610491SRishi.Srivatsavai@Sun.COM 	}
43710491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
43810491SRishi.Srivatsavai@Sun.COM 	bridge_unref(bip);
43910491SRishi.Srivatsavai@Sun.COM 	return (0);
44010491SRishi.Srivatsavai@Sun.COM }
44110491SRishi.Srivatsavai@Sun.COM 
44210491SRishi.Srivatsavai@Sun.COM 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)44310491SRishi.Srivatsavai@Sun.COM bridge_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
44410491SRishi.Srivatsavai@Sun.COM     uint_t pr_valsize, const void *pr_val)
44510491SRishi.Srivatsavai@Sun.COM {
44610491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp = arg;
44710491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
44810491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp;
44910491SRishi.Srivatsavai@Sun.COM 	int err;
45010491SRishi.Srivatsavai@Sun.COM 	uint_t maxsdu;
45110491SRishi.Srivatsavai@Sun.COM 	mblk_t *mlist;
45210491SRishi.Srivatsavai@Sun.COM 
45310491SRishi.Srivatsavai@Sun.COM 	_NOTE(ARGUNUSED(pr_name));
45410491SRishi.Srivatsavai@Sun.COM 	switch (pr_num) {
45510491SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_MTU:
45610491SRishi.Srivatsavai@Sun.COM 		if (pr_valsize < sizeof (bmp->bm_maxsdu)) {
45710491SRishi.Srivatsavai@Sun.COM 			err = EINVAL;
45810491SRishi.Srivatsavai@Sun.COM 			break;
45910491SRishi.Srivatsavai@Sun.COM 		}
46010491SRishi.Srivatsavai@Sun.COM 		(void) bcopy(pr_val, &maxsdu, sizeof (maxsdu));
46110491SRishi.Srivatsavai@Sun.COM 		if (maxsdu == bmp->bm_maxsdu) {
46210491SRishi.Srivatsavai@Sun.COM 			err = 0;
46310491SRishi.Srivatsavai@Sun.COM 		} else if ((bip = mac_to_inst(bmp)) == NULL) {
46410491SRishi.Srivatsavai@Sun.COM 			err = ENXIO;
46510491SRishi.Srivatsavai@Sun.COM 		} else {
46610491SRishi.Srivatsavai@Sun.COM 			rw_enter(&bip->bi_rwlock, RW_WRITER);
46710491SRishi.Srivatsavai@Sun.COM 			mlist = NULL;
46810491SRishi.Srivatsavai@Sun.COM 			for (blp = list_head(&bip->bi_links); blp != NULL;
46910491SRishi.Srivatsavai@Sun.COM 			    blp = list_next(&bip->bi_links, blp)) {
47010491SRishi.Srivatsavai@Sun.COM 				if (blp->bl_flags & BLF_DELETED)
47110491SRishi.Srivatsavai@Sun.COM 					continue;
47210491SRishi.Srivatsavai@Sun.COM 				if (blp->bl_maxsdu == maxsdu)
47310491SRishi.Srivatsavai@Sun.COM 					link_sdu_fail(blp, B_FALSE, &mlist);
47410491SRishi.Srivatsavai@Sun.COM 				else if (blp->bl_maxsdu == bmp->bm_maxsdu)
47510491SRishi.Srivatsavai@Sun.COM 					link_sdu_fail(blp, B_TRUE, &mlist);
47610491SRishi.Srivatsavai@Sun.COM 			}
47710491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bip->bi_rwlock);
47810491SRishi.Srivatsavai@Sun.COM 			bmp->bm_maxsdu = maxsdu;
47910491SRishi.Srivatsavai@Sun.COM 			(void) mac_maxsdu_update(bmp->bm_mh, maxsdu);
48010491SRishi.Srivatsavai@Sun.COM 			send_up_messages(bip, mlist);
48110491SRishi.Srivatsavai@Sun.COM 			bridge_unref(bip);
48210491SRishi.Srivatsavai@Sun.COM 			err = 0;
48310491SRishi.Srivatsavai@Sun.COM 		}
48410491SRishi.Srivatsavai@Sun.COM 		break;
48510491SRishi.Srivatsavai@Sun.COM 
48610491SRishi.Srivatsavai@Sun.COM 	default:
48710491SRishi.Srivatsavai@Sun.COM 		err = ENOTSUP;
48810491SRishi.Srivatsavai@Sun.COM 		break;
48910491SRishi.Srivatsavai@Sun.COM 	}
49010491SRishi.Srivatsavai@Sun.COM 	return (err);
49110491SRishi.Srivatsavai@Sun.COM }
49210491SRishi.Srivatsavai@Sun.COM 
49310491SRishi.Srivatsavai@Sun.COM static int
bridge_m_getprop(void * arg,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,void * pr_val)49410491SRishi.Srivatsavai@Sun.COM bridge_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
495*11878SVenu.Iyer@Sun.COM     uint_t pr_valsize, void *pr_val)
49610491SRishi.Srivatsavai@Sun.COM {
49710491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp = arg;
49810491SRishi.Srivatsavai@Sun.COM 	int err = 0;
49910491SRishi.Srivatsavai@Sun.COM 
50010491SRishi.Srivatsavai@Sun.COM 	_NOTE(ARGUNUSED(pr_name));
50110491SRishi.Srivatsavai@Sun.COM 	switch (pr_num) {
50210491SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_STATUS:
503*11878SVenu.Iyer@Sun.COM 		ASSERT(pr_valsize >= sizeof (bmp->bm_linkstate));
504*11878SVenu.Iyer@Sun.COM 		bcopy(&bmp->bm_linkstate, pr_val, sizeof (&bmp->bm_linkstate));
50510491SRishi.Srivatsavai@Sun.COM 		break;
50610491SRishi.Srivatsavai@Sun.COM 
50710491SRishi.Srivatsavai@Sun.COM 	default:
50810491SRishi.Srivatsavai@Sun.COM 		err = ENOTSUP;
50910491SRishi.Srivatsavai@Sun.COM 		break;
51010491SRishi.Srivatsavai@Sun.COM 	}
51110491SRishi.Srivatsavai@Sun.COM 	return (err);
51210491SRishi.Srivatsavai@Sun.COM }
51310491SRishi.Srivatsavai@Sun.COM 
514*11878SVenu.Iyer@Sun.COM static void
bridge_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t pr_num,mac_prop_info_handle_t prh)515*11878SVenu.Iyer@Sun.COM bridge_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
516*11878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t prh)
517*11878SVenu.Iyer@Sun.COM {
518*11878SVenu.Iyer@Sun.COM 	bridge_mac_t *bmp = arg;
519*11878SVenu.Iyer@Sun.COM 
520*11878SVenu.Iyer@Sun.COM 	_NOTE(ARGUNUSED(pr_name));
521*11878SVenu.Iyer@Sun.COM 
522*11878SVenu.Iyer@Sun.COM 	switch (pr_num) {
523*11878SVenu.Iyer@Sun.COM 	case MAC_PROP_MTU:
524*11878SVenu.Iyer@Sun.COM 		mac_prop_info_set_range_uint32(prh, bmp->bm_maxsdu,
525*11878SVenu.Iyer@Sun.COM 		    bmp->bm_maxsdu);
526*11878SVenu.Iyer@Sun.COM 		break;
527*11878SVenu.Iyer@Sun.COM 	case MAC_PROP_STATUS:
528*11878SVenu.Iyer@Sun.COM 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
529*11878SVenu.Iyer@Sun.COM 		break;
530*11878SVenu.Iyer@Sun.COM 	}
531*11878SVenu.Iyer@Sun.COM }
532*11878SVenu.Iyer@Sun.COM 
53310491SRishi.Srivatsavai@Sun.COM static mac_callbacks_t bridge_m_callbacks = {
534*11878SVenu.Iyer@Sun.COM 	MC_SETPROP | MC_GETPROP | MC_PROPINFO,
53510491SRishi.Srivatsavai@Sun.COM 	bridge_m_getstat,
53610491SRishi.Srivatsavai@Sun.COM 	bridge_m_start,
53710491SRishi.Srivatsavai@Sun.COM 	bridge_m_stop,
53810491SRishi.Srivatsavai@Sun.COM 	bridge_m_setpromisc,
53910491SRishi.Srivatsavai@Sun.COM 	bridge_m_multicst,
54010491SRishi.Srivatsavai@Sun.COM 	bridge_m_unicst,
54110491SRishi.Srivatsavai@Sun.COM 	bridge_m_tx,
542*11878SVenu.Iyer@Sun.COM 	NULL,	/* reserved */
54310491SRishi.Srivatsavai@Sun.COM 	NULL,	/* ioctl */
54410491SRishi.Srivatsavai@Sun.COM 	NULL,	/* getcapab */
54510491SRishi.Srivatsavai@Sun.COM 	NULL,	/* open */
54610491SRishi.Srivatsavai@Sun.COM 	NULL,	/* close */
54710491SRishi.Srivatsavai@Sun.COM 	bridge_m_setprop,
548*11878SVenu.Iyer@Sun.COM 	bridge_m_getprop,
549*11878SVenu.Iyer@Sun.COM 	bridge_m_propinfo
55010491SRishi.Srivatsavai@Sun.COM };
55110491SRishi.Srivatsavai@Sun.COM 
55210491SRishi.Srivatsavai@Sun.COM /*
55310491SRishi.Srivatsavai@Sun.COM  * Create kstats from a list.
55410491SRishi.Srivatsavai@Sun.COM  */
55510491SRishi.Srivatsavai@Sun.COM static kstat_t *
kstat_setup(kstat_named_t * knt,const char ** names,int nstat,const char * unitname)55610491SRishi.Srivatsavai@Sun.COM kstat_setup(kstat_named_t *knt, const char **names, int nstat,
55710491SRishi.Srivatsavai@Sun.COM     const char *unitname)
55810491SRishi.Srivatsavai@Sun.COM {
55910491SRishi.Srivatsavai@Sun.COM 	kstat_t *ksp;
56010491SRishi.Srivatsavai@Sun.COM 	int i;
56110491SRishi.Srivatsavai@Sun.COM 
56210491SRishi.Srivatsavai@Sun.COM 	for (i = 0; i < nstat; i++)
56310491SRishi.Srivatsavai@Sun.COM 		kstat_named_init(&knt[i], names[i], KSTAT_DATA_UINT64);
56410491SRishi.Srivatsavai@Sun.COM 
56511109SRishi.Srivatsavai@Sun.COM 	ksp = kstat_create_zone(BRIDGE_DEV_NAME, 0, unitname, "net",
56610491SRishi.Srivatsavai@Sun.COM 	    KSTAT_TYPE_NAMED, nstat, KSTAT_FLAG_VIRTUAL, GLOBAL_ZONEID);
56710491SRishi.Srivatsavai@Sun.COM 	if (ksp != NULL) {
56810491SRishi.Srivatsavai@Sun.COM 		ksp->ks_data = knt;
56910491SRishi.Srivatsavai@Sun.COM 		kstat_install(ksp);
57010491SRishi.Srivatsavai@Sun.COM 	}
57110491SRishi.Srivatsavai@Sun.COM 	return (ksp);
57210491SRishi.Srivatsavai@Sun.COM }
57310491SRishi.Srivatsavai@Sun.COM 
57410491SRishi.Srivatsavai@Sun.COM /*
57510491SRishi.Srivatsavai@Sun.COM  * Find an existing bridge_mac_t structure or allocate a new one for the given
57610491SRishi.Srivatsavai@Sun.COM  * bridge instance.  This creates the mac driver instance that snoop can use.
57710491SRishi.Srivatsavai@Sun.COM  */
57810491SRishi.Srivatsavai@Sun.COM static int
bmac_alloc(bridge_inst_t * bip,bridge_mac_t ** bmacp)57910491SRishi.Srivatsavai@Sun.COM bmac_alloc(bridge_inst_t *bip, bridge_mac_t **bmacp)
58010491SRishi.Srivatsavai@Sun.COM {
58110491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp, *bnew;
58210491SRishi.Srivatsavai@Sun.COM 	mac_register_t *mac;
58310491SRishi.Srivatsavai@Sun.COM 	int err;
58410491SRishi.Srivatsavai@Sun.COM 
58510491SRishi.Srivatsavai@Sun.COM 	*bmacp = NULL;
58610491SRishi.Srivatsavai@Sun.COM 	if ((mac = mac_alloc(MAC_VERSION)) == NULL)
58710491SRishi.Srivatsavai@Sun.COM 		return (EINVAL);
58810491SRishi.Srivatsavai@Sun.COM 
58910491SRishi.Srivatsavai@Sun.COM 	bnew = kmem_zalloc(sizeof (*bnew), KM_SLEEP);
59010491SRishi.Srivatsavai@Sun.COM 
59110491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bmac_rwlock, RW_WRITER);
59210491SRishi.Srivatsavai@Sun.COM 	for (bmp = list_head(&bmac_list); bmp != NULL;
59310491SRishi.Srivatsavai@Sun.COM 	    bmp = list_next(&bmac_list, bmp)) {
59410491SRishi.Srivatsavai@Sun.COM 		if (strcmp(bip->bi_name, bmp->bm_name) == 0) {
59510491SRishi.Srivatsavai@Sun.COM 			ASSERT(bmp->bm_inst == NULL);
59610491SRishi.Srivatsavai@Sun.COM 			bmp->bm_inst = bip;
59710491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bmac_rwlock);
59810491SRishi.Srivatsavai@Sun.COM 			kmem_free(bnew, sizeof (*bnew));
59910491SRishi.Srivatsavai@Sun.COM 			mac_free(mac);
60010491SRishi.Srivatsavai@Sun.COM 			*bmacp = bmp;
60110491SRishi.Srivatsavai@Sun.COM 			return (0);
60210491SRishi.Srivatsavai@Sun.COM 		}
60310491SRishi.Srivatsavai@Sun.COM 	}
60410491SRishi.Srivatsavai@Sun.COM 
60510491SRishi.Srivatsavai@Sun.COM 	mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
60610491SRishi.Srivatsavai@Sun.COM 	mac->m_driver = bnew;
60710491SRishi.Srivatsavai@Sun.COM 	mac->m_dip = bridge_dev_info;
60810491SRishi.Srivatsavai@Sun.COM 	mac->m_instance = (uint_t)-1;
60910491SRishi.Srivatsavai@Sun.COM 	mac->m_src_addr = (uint8_t *)zero_addr;
61010491SRishi.Srivatsavai@Sun.COM 	mac->m_callbacks = &bridge_m_callbacks;
61110491SRishi.Srivatsavai@Sun.COM 
61210491SRishi.Srivatsavai@Sun.COM 	/*
61310491SRishi.Srivatsavai@Sun.COM 	 * Note that the SDU limits are irrelevant, as nobody transmits on the
61410491SRishi.Srivatsavai@Sun.COM 	 * bridge node itself.  It's mainly for monitoring but we allow
61510491SRishi.Srivatsavai@Sun.COM 	 * setting the bridge MTU for quick transition of all links part of the
61610491SRishi.Srivatsavai@Sun.COM 	 * bridge to a new MTU.
61710491SRishi.Srivatsavai@Sun.COM 	 */
61810491SRishi.Srivatsavai@Sun.COM 	mac->m_min_sdu = 1;
61910491SRishi.Srivatsavai@Sun.COM 	mac->m_max_sdu = 1500;
62010491SRishi.Srivatsavai@Sun.COM 	err = mac_register(mac, &bnew->bm_mh);
62110491SRishi.Srivatsavai@Sun.COM 	mac_free(mac);
62210491SRishi.Srivatsavai@Sun.COM 	if (err != 0) {
62310491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bmac_rwlock);
62410491SRishi.Srivatsavai@Sun.COM 		kmem_free(bnew, sizeof (*bnew));
62510491SRishi.Srivatsavai@Sun.COM 		return (err);
62610491SRishi.Srivatsavai@Sun.COM 	}
62710491SRishi.Srivatsavai@Sun.COM 
62810491SRishi.Srivatsavai@Sun.COM 	bnew->bm_inst = bip;
62910491SRishi.Srivatsavai@Sun.COM 	(void) strcpy(bnew->bm_name, bip->bi_name);
63010491SRishi.Srivatsavai@Sun.COM 	if (list_is_empty(&bmac_list)) {
63110491SRishi.Srivatsavai@Sun.COM 		bridge_timerid = timeout(bridge_timer, NULL,
63210491SRishi.Srivatsavai@Sun.COM 		    bridge_scan_interval);
63310491SRishi.Srivatsavai@Sun.COM 	}
63410491SRishi.Srivatsavai@Sun.COM 	list_insert_tail(&bmac_list, bnew);
63510491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bmac_rwlock);
63610491SRishi.Srivatsavai@Sun.COM 
63710491SRishi.Srivatsavai@Sun.COM 	/*
63810491SRishi.Srivatsavai@Sun.COM 	 * Mark the MAC as unable to go "active" so that only passive clients
63910491SRishi.Srivatsavai@Sun.COM 	 * (such as snoop) can bind to it.
64010491SRishi.Srivatsavai@Sun.COM 	 */
64110491SRishi.Srivatsavai@Sun.COM 	mac_no_active(bnew->bm_mh);
64210491SRishi.Srivatsavai@Sun.COM 	*bmacp = bnew;
64310491SRishi.Srivatsavai@Sun.COM 	return (0);
64410491SRishi.Srivatsavai@Sun.COM }
64510491SRishi.Srivatsavai@Sun.COM 
64610491SRishi.Srivatsavai@Sun.COM /*
64710491SRishi.Srivatsavai@Sun.COM  * Disconnect the given bridge_mac_t from its bridge instance.  The bridge
64810491SRishi.Srivatsavai@Sun.COM  * instance is going away.  The mac instance can't go away until the clients
64910491SRishi.Srivatsavai@Sun.COM  * are gone (see bridge_timer).
65010491SRishi.Srivatsavai@Sun.COM  */
65110491SRishi.Srivatsavai@Sun.COM static void
bmac_disconnect(bridge_mac_t * bmp)65210491SRishi.Srivatsavai@Sun.COM bmac_disconnect(bridge_mac_t *bmp)
65310491SRishi.Srivatsavai@Sun.COM {
65410491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
65510491SRishi.Srivatsavai@Sun.COM 
65610491SRishi.Srivatsavai@Sun.COM 	bmp->bm_linkstate = LINK_STATE_DOWN;
65710491SRishi.Srivatsavai@Sun.COM 	mac_link_redo(bmp->bm_mh, LINK_STATE_DOWN);
65810491SRishi.Srivatsavai@Sun.COM 
65910491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bmac_rwlock, RW_READER);
66010491SRishi.Srivatsavai@Sun.COM 	bip = bmp->bm_inst;
66110491SRishi.Srivatsavai@Sun.COM 	bip->bi_mac = NULL;
66210491SRishi.Srivatsavai@Sun.COM 	bmp->bm_inst = NULL;
66310491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bmac_rwlock);
66410491SRishi.Srivatsavai@Sun.COM }
66510491SRishi.Srivatsavai@Sun.COM 
66610491SRishi.Srivatsavai@Sun.COM /* This is used by the avl trees to sort forwarding table entries */
66710491SRishi.Srivatsavai@Sun.COM static int
fwd_compare(const void * addr1,const void * addr2)66810491SRishi.Srivatsavai@Sun.COM fwd_compare(const void *addr1, const void *addr2)
66910491SRishi.Srivatsavai@Sun.COM {
67010491SRishi.Srivatsavai@Sun.COM 	const bridge_fwd_t *fwd1 = addr1;
67110491SRishi.Srivatsavai@Sun.COM 	const bridge_fwd_t *fwd2 = addr2;
67210491SRishi.Srivatsavai@Sun.COM 	int diff = memcmp(fwd1->bf_dest, fwd2->bf_dest, ETHERADDRL);
67310491SRishi.Srivatsavai@Sun.COM 
67410491SRishi.Srivatsavai@Sun.COM 	if (diff != 0)
67510491SRishi.Srivatsavai@Sun.COM 		return (diff > 0 ? 1 : -1);
67610491SRishi.Srivatsavai@Sun.COM 
67710491SRishi.Srivatsavai@Sun.COM 	if ((fwd1->bf_flags ^ fwd2->bf_flags) & BFF_VLANLOCAL) {
67810491SRishi.Srivatsavai@Sun.COM 		if (fwd1->bf_vlanid > fwd2->bf_vlanid)
67910491SRishi.Srivatsavai@Sun.COM 			return (1);
68010491SRishi.Srivatsavai@Sun.COM 		else if (fwd1->bf_vlanid < fwd2->bf_vlanid)
68110491SRishi.Srivatsavai@Sun.COM 			return (-1);
68210491SRishi.Srivatsavai@Sun.COM 	}
68310491SRishi.Srivatsavai@Sun.COM 	return (0);
68410491SRishi.Srivatsavai@Sun.COM }
68510491SRishi.Srivatsavai@Sun.COM 
68610491SRishi.Srivatsavai@Sun.COM static void
inst_free(bridge_inst_t * bip)68710491SRishi.Srivatsavai@Sun.COM inst_free(bridge_inst_t *bip)
68810491SRishi.Srivatsavai@Sun.COM {
68910491SRishi.Srivatsavai@Sun.COM 	ASSERT(bip->bi_mac == NULL);
69010491SRishi.Srivatsavai@Sun.COM 	rw_destroy(&bip->bi_rwlock);
69110491SRishi.Srivatsavai@Sun.COM 	list_destroy(&bip->bi_links);
69210491SRishi.Srivatsavai@Sun.COM 	cv_destroy(&bip->bi_linkwait);
69310491SRishi.Srivatsavai@Sun.COM 	avl_destroy(&bip->bi_fwd);
69410491SRishi.Srivatsavai@Sun.COM 	if (bip->bi_ksp != NULL)
69510491SRishi.Srivatsavai@Sun.COM 		kstat_delete(bip->bi_ksp);
69610491SRishi.Srivatsavai@Sun.COM 	kmem_free(bip, sizeof (*bip));
69710491SRishi.Srivatsavai@Sun.COM }
69810491SRishi.Srivatsavai@Sun.COM 
69910491SRishi.Srivatsavai@Sun.COM static bridge_inst_t *
inst_alloc(const char * bridge)70010491SRishi.Srivatsavai@Sun.COM inst_alloc(const char *bridge)
70110491SRishi.Srivatsavai@Sun.COM {
70210491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
70310491SRishi.Srivatsavai@Sun.COM 
70410491SRishi.Srivatsavai@Sun.COM 	bip = kmem_zalloc(sizeof (*bip), KM_SLEEP);
70510491SRishi.Srivatsavai@Sun.COM 	bip->bi_refs = 1;
70610491SRishi.Srivatsavai@Sun.COM 	(void) strcpy(bip->bi_name, bridge);
70710491SRishi.Srivatsavai@Sun.COM 	rw_init(&bip->bi_rwlock, NULL, RW_DRIVER, NULL);
70810491SRishi.Srivatsavai@Sun.COM 	list_create(&bip->bi_links, sizeof (bridge_link_t),
70910491SRishi.Srivatsavai@Sun.COM 	    offsetof(bridge_link_t, bl_node));
71010491SRishi.Srivatsavai@Sun.COM 	cv_init(&bip->bi_linkwait, NULL, CV_DRIVER, NULL);
71110491SRishi.Srivatsavai@Sun.COM 	avl_create(&bip->bi_fwd, fwd_compare, sizeof (bridge_fwd_t),
71210491SRishi.Srivatsavai@Sun.COM 	    offsetof(bridge_fwd_t, bf_node));
71310491SRishi.Srivatsavai@Sun.COM 	return (bip);
71410491SRishi.Srivatsavai@Sun.COM }
71510491SRishi.Srivatsavai@Sun.COM 
71610491SRishi.Srivatsavai@Sun.COM static bridge_inst_t *
bridge_find_name(const char * bridge)71710491SRishi.Srivatsavai@Sun.COM bridge_find_name(const char *bridge)
71810491SRishi.Srivatsavai@Sun.COM {
71910491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
72010491SRishi.Srivatsavai@Sun.COM 
72110491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&inst_lock);
72210491SRishi.Srivatsavai@Sun.COM 	for (bip = list_head(&inst_list); bip != NULL;
72310491SRishi.Srivatsavai@Sun.COM 	    bip = list_next(&inst_list, bip)) {
72410491SRishi.Srivatsavai@Sun.COM 		if (!(bip->bi_flags & BIF_SHUTDOWN) &&
72510491SRishi.Srivatsavai@Sun.COM 		    strcmp(bridge, bip->bi_name) == 0) {
72610491SRishi.Srivatsavai@Sun.COM 			atomic_inc_uint(&bip->bi_refs);
72710491SRishi.Srivatsavai@Sun.COM 			break;
72810491SRishi.Srivatsavai@Sun.COM 		}
72910491SRishi.Srivatsavai@Sun.COM 	}
73010491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&inst_lock);
73110491SRishi.Srivatsavai@Sun.COM 
73210491SRishi.Srivatsavai@Sun.COM 	return (bip);
73310491SRishi.Srivatsavai@Sun.COM }
73410491SRishi.Srivatsavai@Sun.COM 
73510491SRishi.Srivatsavai@Sun.COM static int
bridge_create(datalink_id_t linkid,const char * bridge,bridge_inst_t ** bipc,cred_t * cred)73610616SSebastien.Roy@Sun.COM bridge_create(datalink_id_t linkid, const char *bridge, bridge_inst_t **bipc,
73710616SSebastien.Roy@Sun.COM     cred_t *cred)
73810491SRishi.Srivatsavai@Sun.COM {
73910491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip, *bipnew;
74010491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp = NULL;
74110491SRishi.Srivatsavai@Sun.COM 	int err;
74210491SRishi.Srivatsavai@Sun.COM 
74310491SRishi.Srivatsavai@Sun.COM 	*bipc = NULL;
74410491SRishi.Srivatsavai@Sun.COM 	bipnew = inst_alloc(bridge);
74510491SRishi.Srivatsavai@Sun.COM 
74610491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&inst_lock);
74710491SRishi.Srivatsavai@Sun.COM lookup_retry:
74810491SRishi.Srivatsavai@Sun.COM 	for (bip = list_head(&inst_list); bip != NULL;
74910491SRishi.Srivatsavai@Sun.COM 	    bip = list_next(&inst_list, bip)) {
75010491SRishi.Srivatsavai@Sun.COM 		if (strcmp(bridge, bip->bi_name) == 0)
75110491SRishi.Srivatsavai@Sun.COM 			break;
75210491SRishi.Srivatsavai@Sun.COM 	}
75310491SRishi.Srivatsavai@Sun.COM 
75410491SRishi.Srivatsavai@Sun.COM 	/* This should not take long; if it does, we've got a design problem */
75510491SRishi.Srivatsavai@Sun.COM 	if (bip != NULL && (bip->bi_flags & BIF_SHUTDOWN)) {
75610491SRishi.Srivatsavai@Sun.COM 		cv_wait(&inst_cv, &inst_lock);
75710491SRishi.Srivatsavai@Sun.COM 		goto lookup_retry;
75810491SRishi.Srivatsavai@Sun.COM 	}
75910491SRishi.Srivatsavai@Sun.COM 
76011109SRishi.Srivatsavai@Sun.COM 	if (bip == NULL) {
76110491SRishi.Srivatsavai@Sun.COM 		bip = bipnew;
76210491SRishi.Srivatsavai@Sun.COM 		bipnew = NULL;
76310491SRishi.Srivatsavai@Sun.COM 		list_insert_tail(&inst_list, bip);
76410491SRishi.Srivatsavai@Sun.COM 	}
76510491SRishi.Srivatsavai@Sun.COM 
76610491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&inst_lock);
76711109SRishi.Srivatsavai@Sun.COM 	if (bipnew != NULL) {
76811109SRishi.Srivatsavai@Sun.COM 		inst_free(bipnew);
76911109SRishi.Srivatsavai@Sun.COM 		return (EEXIST);
77011109SRishi.Srivatsavai@Sun.COM 	}
77110491SRishi.Srivatsavai@Sun.COM 
77210491SRishi.Srivatsavai@Sun.COM 	bip->bi_ksp = kstat_setup((kstat_named_t *)&bip->bi_kstats,
77310491SRishi.Srivatsavai@Sun.COM 	    inst_kstats_list, Dim(inst_kstats_list), bip->bi_name);
77410491SRishi.Srivatsavai@Sun.COM 
77510491SRishi.Srivatsavai@Sun.COM 	err = bmac_alloc(bip, &bmp);
77610491SRishi.Srivatsavai@Sun.COM 	if ((bip->bi_mac = bmp) == NULL)
77710491SRishi.Srivatsavai@Sun.COM 		goto fail_create;
77810491SRishi.Srivatsavai@Sun.COM 
77910491SRishi.Srivatsavai@Sun.COM 	/*
78010491SRishi.Srivatsavai@Sun.COM 	 * bm_inst is set, so the timer cannot yank the DLS rug from under us.
78110491SRishi.Srivatsavai@Sun.COM 	 * No extra locking is needed here.
78210491SRishi.Srivatsavai@Sun.COM 	 */
78310491SRishi.Srivatsavai@Sun.COM 	if (!(bmp->bm_flags & BMF_DLS)) {
78410616SSebastien.Roy@Sun.COM 		err = dls_devnet_create(bmp->bm_mh, linkid, crgetzoneid(cred));
78510616SSebastien.Roy@Sun.COM 		if (err != 0)
78610491SRishi.Srivatsavai@Sun.COM 			goto fail_create;
78710491SRishi.Srivatsavai@Sun.COM 		bmp->bm_flags |= BMF_DLS;
78810491SRishi.Srivatsavai@Sun.COM 	}
78910491SRishi.Srivatsavai@Sun.COM 
79010491SRishi.Srivatsavai@Sun.COM 	bip->bi_dev = makedevice(bridge_major, mac_minor(bmp->bm_mh));
79110491SRishi.Srivatsavai@Sun.COM 	*bipc = bip;
79210491SRishi.Srivatsavai@Sun.COM 	return (0);
79310491SRishi.Srivatsavai@Sun.COM 
79410491SRishi.Srivatsavai@Sun.COM fail_create:
79511109SRishi.Srivatsavai@Sun.COM 	ASSERT(bip->bi_trilldata == NULL);
79611109SRishi.Srivatsavai@Sun.COM 	bip->bi_flags |= BIF_SHUTDOWN;
79711109SRishi.Srivatsavai@Sun.COM 	bridge_unref(bip);
79810491SRishi.Srivatsavai@Sun.COM 	return (err);
79910491SRishi.Srivatsavai@Sun.COM }
80010491SRishi.Srivatsavai@Sun.COM 
80110491SRishi.Srivatsavai@Sun.COM static void
bridge_unref(bridge_inst_t * bip)80210491SRishi.Srivatsavai@Sun.COM bridge_unref(bridge_inst_t *bip)
80310491SRishi.Srivatsavai@Sun.COM {
80410491SRishi.Srivatsavai@Sun.COM 	if (atomic_dec_uint_nv(&bip->bi_refs) == 0) {
80510491SRishi.Srivatsavai@Sun.COM 		ASSERT(bip->bi_flags & BIF_SHUTDOWN);
80610491SRishi.Srivatsavai@Sun.COM 		/* free up mac for reuse before leaving global list */
80710491SRishi.Srivatsavai@Sun.COM 		if (bip->bi_mac != NULL)
80810491SRishi.Srivatsavai@Sun.COM 			bmac_disconnect(bip->bi_mac);
80910491SRishi.Srivatsavai@Sun.COM 		mutex_enter(&inst_lock);
81010491SRishi.Srivatsavai@Sun.COM 		list_remove(&inst_list, bip);
81110491SRishi.Srivatsavai@Sun.COM 		cv_broadcast(&inst_cv);
81210491SRishi.Srivatsavai@Sun.COM 		mutex_exit(&inst_lock);
81310491SRishi.Srivatsavai@Sun.COM 		inst_free(bip);
81410491SRishi.Srivatsavai@Sun.COM 	}
81510491SRishi.Srivatsavai@Sun.COM }
81610491SRishi.Srivatsavai@Sun.COM 
81710491SRishi.Srivatsavai@Sun.COM /*
81810491SRishi.Srivatsavai@Sun.COM  * Stream instances are used only for allocating bridges and serving as a
81910491SRishi.Srivatsavai@Sun.COM  * control node.  They serve no data-handling function.
82010491SRishi.Srivatsavai@Sun.COM  */
82110491SRishi.Srivatsavai@Sun.COM static bridge_stream_t *
stream_alloc(void)82210491SRishi.Srivatsavai@Sun.COM stream_alloc(void)
82310491SRishi.Srivatsavai@Sun.COM {
82410491SRishi.Srivatsavai@Sun.COM 	bridge_stream_t *bsp;
82510491SRishi.Srivatsavai@Sun.COM 	minor_t mn;
82610491SRishi.Srivatsavai@Sun.COM 
82710491SRishi.Srivatsavai@Sun.COM 	if ((mn = mac_minor_hold(B_FALSE)) == 0)
82810491SRishi.Srivatsavai@Sun.COM 		return (NULL);
82910491SRishi.Srivatsavai@Sun.COM 	bsp = kmem_zalloc(sizeof (*bsp), KM_SLEEP);
83010491SRishi.Srivatsavai@Sun.COM 	bsp->bs_minor = mn;
83110491SRishi.Srivatsavai@Sun.COM 	return (bsp);
83210491SRishi.Srivatsavai@Sun.COM }
83310491SRishi.Srivatsavai@Sun.COM 
83410491SRishi.Srivatsavai@Sun.COM static void
stream_free(bridge_stream_t * bsp)83510491SRishi.Srivatsavai@Sun.COM stream_free(bridge_stream_t *bsp)
83610491SRishi.Srivatsavai@Sun.COM {
83710491SRishi.Srivatsavai@Sun.COM 	mac_minor_rele(bsp->bs_minor);
83810491SRishi.Srivatsavai@Sun.COM 	kmem_free(bsp, sizeof (*bsp));
83910491SRishi.Srivatsavai@Sun.COM }
84010491SRishi.Srivatsavai@Sun.COM 
84110491SRishi.Srivatsavai@Sun.COM /* Reference hold/release functions for STREAMS-related taskq */
84210491SRishi.Srivatsavai@Sun.COM static void
stream_ref(bridge_stream_t * bsp)84310491SRishi.Srivatsavai@Sun.COM stream_ref(bridge_stream_t *bsp)
84410491SRishi.Srivatsavai@Sun.COM {
84510491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&stream_ref_lock);
84610491SRishi.Srivatsavai@Sun.COM 	bsp->bs_taskq_cnt++;
84710491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&stream_ref_lock);
84810491SRishi.Srivatsavai@Sun.COM }
84910491SRishi.Srivatsavai@Sun.COM 
85010491SRishi.Srivatsavai@Sun.COM static void
stream_unref(bridge_stream_t * bsp)85110491SRishi.Srivatsavai@Sun.COM stream_unref(bridge_stream_t *bsp)
85210491SRishi.Srivatsavai@Sun.COM {
85310491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&stream_ref_lock);
85410491SRishi.Srivatsavai@Sun.COM 	if (--bsp->bs_taskq_cnt == 0)
85510491SRishi.Srivatsavai@Sun.COM 		cv_broadcast(&stream_ref_cv);
85610491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&stream_ref_lock);
85710491SRishi.Srivatsavai@Sun.COM }
85810491SRishi.Srivatsavai@Sun.COM 
85910491SRishi.Srivatsavai@Sun.COM static void
link_free(bridge_link_t * blp)86010491SRishi.Srivatsavai@Sun.COM link_free(bridge_link_t *blp)
86110491SRishi.Srivatsavai@Sun.COM {
86210491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;
86310491SRishi.Srivatsavai@Sun.COM 
86410491SRishi.Srivatsavai@Sun.COM 	ASSERT(!(blp->bl_flags & BLF_FREED));
86510491SRishi.Srivatsavai@Sun.COM 	blp->bl_flags |= BLF_FREED;
86610491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_ksp != NULL)
86710491SRishi.Srivatsavai@Sun.COM 		kstat_delete(blp->bl_ksp);
86810491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_lfailmp != NULL)
86910491SRishi.Srivatsavai@Sun.COM 		freeb(blp->bl_lfailmp);
87010491SRishi.Srivatsavai@Sun.COM 	cv_destroy(&blp->bl_trillwait);
87110491SRishi.Srivatsavai@Sun.COM 	mutex_destroy(&blp->bl_trilllock);
87210491SRishi.Srivatsavai@Sun.COM 	kmem_free(blp, sizeof (*blp));
87310491SRishi.Srivatsavai@Sun.COM 	/* Don't unreference the bridge until the MAC is closed */
87410491SRishi.Srivatsavai@Sun.COM 	bridge_unref(bip);
87510491SRishi.Srivatsavai@Sun.COM }
87610491SRishi.Srivatsavai@Sun.COM 
87710491SRishi.Srivatsavai@Sun.COM static void
link_unref(bridge_link_t * blp)87810491SRishi.Srivatsavai@Sun.COM link_unref(bridge_link_t *blp)
87910491SRishi.Srivatsavai@Sun.COM {
88010491SRishi.Srivatsavai@Sun.COM 	if (atomic_dec_uint_nv(&blp->bl_refs) == 0) {
88110491SRishi.Srivatsavai@Sun.COM 		bridge_inst_t *bip = blp->bl_inst;
88210491SRishi.Srivatsavai@Sun.COM 
88310491SRishi.Srivatsavai@Sun.COM 		ASSERT(blp->bl_flags & BLF_DELETED);
88410491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_WRITER);
88511483SRishi.Srivatsavai@Sun.COM 		if (blp->bl_flags & BLF_LINK_ADDED)
88611483SRishi.Srivatsavai@Sun.COM 			list_remove(&bip->bi_links, blp);
88710491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
88810491SRishi.Srivatsavai@Sun.COM 		if (bip->bi_trilldata != NULL && list_is_empty(&bip->bi_links))
88910491SRishi.Srivatsavai@Sun.COM 			cv_broadcast(&bip->bi_linkwait);
89010491SRishi.Srivatsavai@Sun.COM 		link_free(blp);
89110491SRishi.Srivatsavai@Sun.COM 	}
89210491SRishi.Srivatsavai@Sun.COM }
89310491SRishi.Srivatsavai@Sun.COM 
89410491SRishi.Srivatsavai@Sun.COM static bridge_fwd_t *
fwd_alloc(const uint8_t * addr,uint_t nlinks,uint16_t nick)89510491SRishi.Srivatsavai@Sun.COM fwd_alloc(const uint8_t *addr, uint_t nlinks, uint16_t nick)
89610491SRishi.Srivatsavai@Sun.COM {
89710491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp;
89810491SRishi.Srivatsavai@Sun.COM 
89910491SRishi.Srivatsavai@Sun.COM 	bfp = kmem_zalloc(sizeof (*bfp) + (nlinks * sizeof (bridge_link_t *)),
90010491SRishi.Srivatsavai@Sun.COM 	    KM_NOSLEEP);
90110491SRishi.Srivatsavai@Sun.COM 	if (bfp != NULL) {
90210491SRishi.Srivatsavai@Sun.COM 		bcopy(addr, bfp->bf_dest, ETHERADDRL);
90311066Srafael.vanoni@sun.com 		bfp->bf_lastheard = ddi_get_lbolt();
90410491SRishi.Srivatsavai@Sun.COM 		bfp->bf_maxlinks = nlinks;
90510491SRishi.Srivatsavai@Sun.COM 		bfp->bf_links = (bridge_link_t **)(bfp + 1);
90610491SRishi.Srivatsavai@Sun.COM 		bfp->bf_trill_nick = nick;
90710491SRishi.Srivatsavai@Sun.COM 	}
90810491SRishi.Srivatsavai@Sun.COM 	return (bfp);
90910491SRishi.Srivatsavai@Sun.COM }
91010491SRishi.Srivatsavai@Sun.COM 
91110491SRishi.Srivatsavai@Sun.COM static bridge_fwd_t *
fwd_find(bridge_inst_t * bip,const uint8_t * addr,uint16_t vlanid)91210491SRishi.Srivatsavai@Sun.COM fwd_find(bridge_inst_t *bip, const uint8_t *addr, uint16_t vlanid)
91310491SRishi.Srivatsavai@Sun.COM {
91410491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp, *vbfp;
91510491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t match;
91610491SRishi.Srivatsavai@Sun.COM 
91710491SRishi.Srivatsavai@Sun.COM 	bcopy(addr, match.bf_dest, ETHERADDRL);
91810491SRishi.Srivatsavai@Sun.COM 	match.bf_flags = 0;
91910491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_READER);
92010491SRishi.Srivatsavai@Sun.COM 	if ((bfp = avl_find(&bip->bi_fwd, &match, NULL)) != NULL) {
92110491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_vlanid != vlanid && bfp->bf_vcnt > 0) {
92210491SRishi.Srivatsavai@Sun.COM 			match.bf_vlanid = vlanid;
92310491SRishi.Srivatsavai@Sun.COM 			match.bf_flags = BFF_VLANLOCAL;
92410491SRishi.Srivatsavai@Sun.COM 			vbfp = avl_find(&bip->bi_fwd, &match, NULL);
92510491SRishi.Srivatsavai@Sun.COM 			if (vbfp != NULL)
92610491SRishi.Srivatsavai@Sun.COM 				bfp = vbfp;
92710491SRishi.Srivatsavai@Sun.COM 		}
92810491SRishi.Srivatsavai@Sun.COM 		atomic_inc_uint(&bfp->bf_refs);
92910491SRishi.Srivatsavai@Sun.COM 	}
93010491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
93110491SRishi.Srivatsavai@Sun.COM 	return (bfp);
93210491SRishi.Srivatsavai@Sun.COM }
93310491SRishi.Srivatsavai@Sun.COM 
93410491SRishi.Srivatsavai@Sun.COM static void
fwd_free(bridge_fwd_t * bfp)93510491SRishi.Srivatsavai@Sun.COM fwd_free(bridge_fwd_t *bfp)
93610491SRishi.Srivatsavai@Sun.COM {
93710491SRishi.Srivatsavai@Sun.COM 	uint_t i;
93810491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = bfp->bf_links[0]->bl_inst;
93910491SRishi.Srivatsavai@Sun.COM 
94010491SRishi.Srivatsavai@Sun.COM 	KIDECR(bki_count);
94110491SRishi.Srivatsavai@Sun.COM 	for (i = 0; i < bfp->bf_nlinks; i++)
94210491SRishi.Srivatsavai@Sun.COM 		link_unref(bfp->bf_links[i]);
94310491SRishi.Srivatsavai@Sun.COM 	kmem_free(bfp,
94410491SRishi.Srivatsavai@Sun.COM 	    sizeof (*bfp) + bfp->bf_maxlinks * sizeof (bridge_link_t *));
94510491SRishi.Srivatsavai@Sun.COM }
94610491SRishi.Srivatsavai@Sun.COM 
94710491SRishi.Srivatsavai@Sun.COM static void
fwd_unref(bridge_fwd_t * bfp)94810491SRishi.Srivatsavai@Sun.COM fwd_unref(bridge_fwd_t *bfp)
94910491SRishi.Srivatsavai@Sun.COM {
95010491SRishi.Srivatsavai@Sun.COM 	if (atomic_dec_uint_nv(&bfp->bf_refs) == 0) {
95110491SRishi.Srivatsavai@Sun.COM 		ASSERT(!(bfp->bf_flags & BFF_INTREE));
95210491SRishi.Srivatsavai@Sun.COM 		fwd_free(bfp);
95310491SRishi.Srivatsavai@Sun.COM 	}
95410491SRishi.Srivatsavai@Sun.COM }
95510491SRishi.Srivatsavai@Sun.COM 
95610491SRishi.Srivatsavai@Sun.COM static void
fwd_delete(bridge_fwd_t * bfp)95710491SRishi.Srivatsavai@Sun.COM fwd_delete(bridge_fwd_t *bfp)
95810491SRishi.Srivatsavai@Sun.COM {
95910491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
96010491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfpzero;
96110491SRishi.Srivatsavai@Sun.COM 
96210491SRishi.Srivatsavai@Sun.COM 	if (bfp->bf_flags & BFF_INTREE) {
96310491SRishi.Srivatsavai@Sun.COM 		ASSERT(bfp->bf_nlinks > 0);
96410491SRishi.Srivatsavai@Sun.COM 		bip = bfp->bf_links[0]->bl_inst;
96510491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_WRITER);
96610491SRishi.Srivatsavai@Sun.COM 		/* Another thread could beat us to this */
96710491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_flags & BFF_INTREE) {
96810491SRishi.Srivatsavai@Sun.COM 			avl_remove(&bip->bi_fwd, bfp);
96910491SRishi.Srivatsavai@Sun.COM 			bfp->bf_flags &= ~BFF_INTREE;
97010491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_flags & BFF_VLANLOCAL) {
97110491SRishi.Srivatsavai@Sun.COM 				bfp->bf_flags &= ~BFF_VLANLOCAL;
97210491SRishi.Srivatsavai@Sun.COM 				bfpzero = avl_find(&bip->bi_fwd, bfp, NULL);
97310491SRishi.Srivatsavai@Sun.COM 				if (bfpzero != NULL && bfpzero->bf_vcnt > 0)
97410491SRishi.Srivatsavai@Sun.COM 					bfpzero->bf_vcnt--;
97510491SRishi.Srivatsavai@Sun.COM 			}
97610491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bip->bi_rwlock);
97710491SRishi.Srivatsavai@Sun.COM 			fwd_unref(bfp);		/* no longer in avl tree */
97810491SRishi.Srivatsavai@Sun.COM 		} else {
97910491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bip->bi_rwlock);
98010491SRishi.Srivatsavai@Sun.COM 		}
98110491SRishi.Srivatsavai@Sun.COM 	}
98210491SRishi.Srivatsavai@Sun.COM }
98310491SRishi.Srivatsavai@Sun.COM 
98410491SRishi.Srivatsavai@Sun.COM static boolean_t
fwd_insert(bridge_inst_t * bip,bridge_fwd_t * bfp)98510491SRishi.Srivatsavai@Sun.COM fwd_insert(bridge_inst_t *bip, bridge_fwd_t *bfp)
98610491SRishi.Srivatsavai@Sun.COM {
98710491SRishi.Srivatsavai@Sun.COM 	avl_index_t idx;
98810491SRishi.Srivatsavai@Sun.COM 	boolean_t retv;
98910491SRishi.Srivatsavai@Sun.COM 
99010491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_WRITER);
99110491SRishi.Srivatsavai@Sun.COM 	if (!(bip->bi_flags & BIF_SHUTDOWN) &&
99210491SRishi.Srivatsavai@Sun.COM 	    avl_numnodes(&bip->bi_fwd) < bip->bi_tablemax &&
99310491SRishi.Srivatsavai@Sun.COM 	    avl_find(&bip->bi_fwd, bfp, &idx) == NULL) {
99410491SRishi.Srivatsavai@Sun.COM 		avl_insert(&bip->bi_fwd, bfp, idx);
99510491SRishi.Srivatsavai@Sun.COM 		bfp->bf_flags |= BFF_INTREE;
99610491SRishi.Srivatsavai@Sun.COM 		atomic_inc_uint(&bfp->bf_refs);	/* avl entry */
99710491SRishi.Srivatsavai@Sun.COM 		retv = B_TRUE;
99810491SRishi.Srivatsavai@Sun.COM 	} else {
99910491SRishi.Srivatsavai@Sun.COM 		retv = B_FALSE;
100010491SRishi.Srivatsavai@Sun.COM 	}
100110491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
100210491SRishi.Srivatsavai@Sun.COM 	return (retv);
100310491SRishi.Srivatsavai@Sun.COM }
100410491SRishi.Srivatsavai@Sun.COM 
100510491SRishi.Srivatsavai@Sun.COM static void
fwd_update_local(bridge_link_t * blp,const uint8_t * oldaddr,const uint8_t * newaddr)100610491SRishi.Srivatsavai@Sun.COM fwd_update_local(bridge_link_t *blp, const uint8_t *oldaddr,
100710491SRishi.Srivatsavai@Sun.COM     const uint8_t *newaddr)
100810491SRishi.Srivatsavai@Sun.COM {
100910491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;
101010491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp, *bfnew;
101110491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t match;
101210491SRishi.Srivatsavai@Sun.COM 	avl_index_t idx;
101310491SRishi.Srivatsavai@Sun.COM 	boolean_t drop_ref = B_FALSE;
101410491SRishi.Srivatsavai@Sun.COM 
101510491SRishi.Srivatsavai@Sun.COM 	if (bcmp(oldaddr, newaddr, ETHERADDRL) == 0)
101610491SRishi.Srivatsavai@Sun.COM 		return;
101710491SRishi.Srivatsavai@Sun.COM 
101810491SRishi.Srivatsavai@Sun.COM 	if (bcmp(oldaddr, zero_addr, ETHERADDRL) == 0)
101910491SRishi.Srivatsavai@Sun.COM 		goto no_old_addr;
102010491SRishi.Srivatsavai@Sun.COM 
102110491SRishi.Srivatsavai@Sun.COM 	/*
102210491SRishi.Srivatsavai@Sun.COM 	 * Find the previous entry, and remove our link from it.
102310491SRishi.Srivatsavai@Sun.COM 	 */
102410491SRishi.Srivatsavai@Sun.COM 	bcopy(oldaddr, match.bf_dest, ETHERADDRL);
102510491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_WRITER);
102610491SRishi.Srivatsavai@Sun.COM 	if ((bfp = avl_find(&bip->bi_fwd, &match, NULL)) != NULL) {
102710491SRishi.Srivatsavai@Sun.COM 		int i;
102810491SRishi.Srivatsavai@Sun.COM 
102910491SRishi.Srivatsavai@Sun.COM 		/*
103010491SRishi.Srivatsavai@Sun.COM 		 * See if we're in the list, and remove if so.
103110491SRishi.Srivatsavai@Sun.COM 		 */
103210491SRishi.Srivatsavai@Sun.COM 		for (i = 0; i < bfp->bf_nlinks; i++) {
103310491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_links[i] == blp) {
103410491SRishi.Srivatsavai@Sun.COM 				/*
103510491SRishi.Srivatsavai@Sun.COM 				 * We assume writes are atomic, so no special
103610491SRishi.Srivatsavai@Sun.COM 				 * MT handling is needed.  The list length is
103710491SRishi.Srivatsavai@Sun.COM 				 * decremented first, and then we remove
103810491SRishi.Srivatsavai@Sun.COM 				 * entries.
103910491SRishi.Srivatsavai@Sun.COM 				 */
104010491SRishi.Srivatsavai@Sun.COM 				bfp->bf_nlinks--;
104110491SRishi.Srivatsavai@Sun.COM 				for (; i < bfp->bf_nlinks; i++)
104210491SRishi.Srivatsavai@Sun.COM 					bfp->bf_links[i] = bfp->bf_links[i + 1];
104310491SRishi.Srivatsavai@Sun.COM 				drop_ref = B_TRUE;
104410491SRishi.Srivatsavai@Sun.COM 				break;
104510491SRishi.Srivatsavai@Sun.COM 			}
104610491SRishi.Srivatsavai@Sun.COM 		}
104710491SRishi.Srivatsavai@Sun.COM 		/* If no more links, then remove and free up */
104810491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_nlinks == 0) {
104910491SRishi.Srivatsavai@Sun.COM 			avl_remove(&bip->bi_fwd, bfp);
105010491SRishi.Srivatsavai@Sun.COM 			bfp->bf_flags &= ~BFF_INTREE;
105110491SRishi.Srivatsavai@Sun.COM 		} else {
105210491SRishi.Srivatsavai@Sun.COM 			bfp = NULL;
105310491SRishi.Srivatsavai@Sun.COM 		}
105410491SRishi.Srivatsavai@Sun.COM 	}
105510491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
105610491SRishi.Srivatsavai@Sun.COM 	if (bfp != NULL)
105710491SRishi.Srivatsavai@Sun.COM 		fwd_unref(bfp);		/* no longer in avl tree */
105810491SRishi.Srivatsavai@Sun.COM 
105910491SRishi.Srivatsavai@Sun.COM 	/*
106010491SRishi.Srivatsavai@Sun.COM 	 * Now get the new link address and add this link to the list.  The
106110491SRishi.Srivatsavai@Sun.COM 	 * list should be of length 1 unless the user has configured multiple
106210491SRishi.Srivatsavai@Sun.COM 	 * NICs with the same address.  (That's an incorrect configuration, but
106310491SRishi.Srivatsavai@Sun.COM 	 * we support it anyway.)
106410491SRishi.Srivatsavai@Sun.COM 	 */
106510491SRishi.Srivatsavai@Sun.COM no_old_addr:
106610491SRishi.Srivatsavai@Sun.COM 	bfp = NULL;
106710491SRishi.Srivatsavai@Sun.COM 	if ((bip->bi_flags & BIF_SHUTDOWN) ||
106810491SRishi.Srivatsavai@Sun.COM 	    bcmp(newaddr, zero_addr, ETHERADDRL) == 0)
106910491SRishi.Srivatsavai@Sun.COM 		goto no_new_addr;
107010491SRishi.Srivatsavai@Sun.COM 
107110491SRishi.Srivatsavai@Sun.COM 	bcopy(newaddr, match.bf_dest, ETHERADDRL);
107210491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_WRITER);
107310491SRishi.Srivatsavai@Sun.COM 	if ((bfp = avl_find(&bip->bi_fwd, &match, &idx)) == NULL) {
107410491SRishi.Srivatsavai@Sun.COM 		bfnew = fwd_alloc(newaddr, 1, RBRIDGE_NICKNAME_NONE);
107510491SRishi.Srivatsavai@Sun.COM 		if (bfnew != NULL)
107610491SRishi.Srivatsavai@Sun.COM 			KIINCR(bki_count);
107710491SRishi.Srivatsavai@Sun.COM 	} else if (bfp->bf_nlinks < bfp->bf_maxlinks) {
107810491SRishi.Srivatsavai@Sun.COM 		/* special case: link fits in existing entry */
107910491SRishi.Srivatsavai@Sun.COM 		bfnew = bfp;
108010491SRishi.Srivatsavai@Sun.COM 	} else {
108110491SRishi.Srivatsavai@Sun.COM 		bfnew = fwd_alloc(newaddr, bfp->bf_nlinks + 1,
108210491SRishi.Srivatsavai@Sun.COM 		    RBRIDGE_NICKNAME_NONE);
108310491SRishi.Srivatsavai@Sun.COM 		if (bfnew != NULL) {
108410491SRishi.Srivatsavai@Sun.COM 			KIINCR(bki_count);
108510491SRishi.Srivatsavai@Sun.COM 			avl_remove(&bip->bi_fwd, bfp);
108610491SRishi.Srivatsavai@Sun.COM 			bfp->bf_flags &= ~BFF_INTREE;
108710491SRishi.Srivatsavai@Sun.COM 			bfnew->bf_nlinks = bfp->bf_nlinks;
108810491SRishi.Srivatsavai@Sun.COM 			bcopy(bfp->bf_links, bfnew->bf_links,
108910491SRishi.Srivatsavai@Sun.COM 			    bfp->bf_nlinks * sizeof (bfp));
109010491SRishi.Srivatsavai@Sun.COM 			/* reset the idx value due to removal above */
109110491SRishi.Srivatsavai@Sun.COM 			(void) avl_find(&bip->bi_fwd, &match, &idx);
109210491SRishi.Srivatsavai@Sun.COM 		}
109310491SRishi.Srivatsavai@Sun.COM 	}
109410491SRishi.Srivatsavai@Sun.COM 
109510491SRishi.Srivatsavai@Sun.COM 	if (bfnew != NULL) {
109610491SRishi.Srivatsavai@Sun.COM 		bfnew->bf_links[bfnew->bf_nlinks++] = blp;
109710491SRishi.Srivatsavai@Sun.COM 		if (drop_ref)
109810491SRishi.Srivatsavai@Sun.COM 			drop_ref = B_FALSE;
109910491SRishi.Srivatsavai@Sun.COM 		else
110010491SRishi.Srivatsavai@Sun.COM 			atomic_inc_uint(&blp->bl_refs);	/* bf_links entry */
110110491SRishi.Srivatsavai@Sun.COM 
110210491SRishi.Srivatsavai@Sun.COM 		if (bfnew != bfp) {
110310491SRishi.Srivatsavai@Sun.COM 			/* local addresses are not subject to table limits */
110410491SRishi.Srivatsavai@Sun.COM 			avl_insert(&bip->bi_fwd, bfnew, idx);
110510491SRishi.Srivatsavai@Sun.COM 			bfnew->bf_flags |= (BFF_INTREE | BFF_LOCALADDR);
110610491SRishi.Srivatsavai@Sun.COM 			atomic_inc_uint(&bfnew->bf_refs);	/* avl entry */
110710491SRishi.Srivatsavai@Sun.COM 		}
110810491SRishi.Srivatsavai@Sun.COM 	}
110910491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
111010491SRishi.Srivatsavai@Sun.COM 
111110491SRishi.Srivatsavai@Sun.COM no_new_addr:
111210491SRishi.Srivatsavai@Sun.COM 	/*
111310491SRishi.Srivatsavai@Sun.COM 	 * If we found an existing entry and we replaced it with a new one,
111410491SRishi.Srivatsavai@Sun.COM 	 * then drop the table reference from the old one.  We removed it from
111510491SRishi.Srivatsavai@Sun.COM 	 * the AVL tree above.
111610491SRishi.Srivatsavai@Sun.COM 	 */
111710491SRishi.Srivatsavai@Sun.COM 	if (bfnew != NULL && bfp != NULL && bfnew != bfp)
111810491SRishi.Srivatsavai@Sun.COM 		fwd_unref(bfp);
111910491SRishi.Srivatsavai@Sun.COM 
112010491SRishi.Srivatsavai@Sun.COM 	/* Account for removed entry. */
112110491SRishi.Srivatsavai@Sun.COM 	if (drop_ref)
112210491SRishi.Srivatsavai@Sun.COM 		link_unref(blp);
112310491SRishi.Srivatsavai@Sun.COM }
112410491SRishi.Srivatsavai@Sun.COM 
112510491SRishi.Srivatsavai@Sun.COM static void
bridge_new_unicst(bridge_link_t * blp)112610491SRishi.Srivatsavai@Sun.COM bridge_new_unicst(bridge_link_t *blp)
112710491SRishi.Srivatsavai@Sun.COM {
112810491SRishi.Srivatsavai@Sun.COM 	uint8_t new_mac[ETHERADDRL];
112910491SRishi.Srivatsavai@Sun.COM 
113010491SRishi.Srivatsavai@Sun.COM 	mac_unicast_primary_get(blp->bl_mh, new_mac);
113110491SRishi.Srivatsavai@Sun.COM 	fwd_update_local(blp, blp->bl_local_mac, new_mac);
113210491SRishi.Srivatsavai@Sun.COM 	bcopy(new_mac, blp->bl_local_mac, ETHERADDRL);
113310491SRishi.Srivatsavai@Sun.COM }
113410491SRishi.Srivatsavai@Sun.COM 
113510491SRishi.Srivatsavai@Sun.COM /*
113610491SRishi.Srivatsavai@Sun.COM  * We must shut down a link prior to freeing it, and doing that requires
113710491SRishi.Srivatsavai@Sun.COM  * blocking to wait for running MAC threads while holding a reference.  This is
113810491SRishi.Srivatsavai@Sun.COM  * run from a taskq to accomplish proper link shutdown followed by reference
113910491SRishi.Srivatsavai@Sun.COM  * drop.
114010491SRishi.Srivatsavai@Sun.COM  */
114110491SRishi.Srivatsavai@Sun.COM static void
link_shutdown(void * arg)114210491SRishi.Srivatsavai@Sun.COM link_shutdown(void *arg)
114310491SRishi.Srivatsavai@Sun.COM {
114410491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp = arg;
114510491SRishi.Srivatsavai@Sun.COM 	mac_handle_t mh = blp->bl_mh;
114610491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
114710491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp, *bfnext;
114810491SRishi.Srivatsavai@Sun.COM 	avl_tree_t fwd_scavenge;
114910491SRishi.Srivatsavai@Sun.COM 	int i;
115010491SRishi.Srivatsavai@Sun.COM 
115110491SRishi.Srivatsavai@Sun.COM 	/*
115210491SRishi.Srivatsavai@Sun.COM 	 * This link is being destroyed.  Notify TRILL now that it's no longer
115310491SRishi.Srivatsavai@Sun.COM 	 * possible to send packets.  Data packets may still arrive until TRILL
115410491SRishi.Srivatsavai@Sun.COM 	 * calls bridge_trill_lnunref.
115510491SRishi.Srivatsavai@Sun.COM 	 */
115610491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_trilldata != NULL)
115710491SRishi.Srivatsavai@Sun.COM 		trill_lndstr_fn(blp->bl_trilldata, blp);
115810491SRishi.Srivatsavai@Sun.COM 
115910491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_flags & BLF_PROM_ADDED)
116010491SRishi.Srivatsavai@Sun.COM 		(void) mac_promisc_remove(blp->bl_mphp);
116110491SRishi.Srivatsavai@Sun.COM 
116210491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_flags & BLF_SET_BRIDGE)
116310491SRishi.Srivatsavai@Sun.COM 		mac_bridge_clear(mh, (mac_handle_t)blp);
116410491SRishi.Srivatsavai@Sun.COM 
116510491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_flags & BLF_MARGIN_ADDED) {
116610501SRishi.Srivatsavai@Sun.COM 		(void) mac_notify_remove(blp->bl_mnh, B_TRUE);
116710491SRishi.Srivatsavai@Sun.COM 		(void) mac_margin_remove(mh, blp->bl_margin);
116810491SRishi.Srivatsavai@Sun.COM 	}
116910491SRishi.Srivatsavai@Sun.COM 
117010491SRishi.Srivatsavai@Sun.COM 	/* Tell the clients the real link state when we leave */
117110491SRishi.Srivatsavai@Sun.COM 	mac_link_redo(blp->bl_mh,
117210491SRishi.Srivatsavai@Sun.COM 	    mac_stat_get(blp->bl_mh, MAC_STAT_LOWLINK_STATE));
117310491SRishi.Srivatsavai@Sun.COM 
117410491SRishi.Srivatsavai@Sun.COM 	/* Destroy all of the forwarding entries related to this link */
117510491SRishi.Srivatsavai@Sun.COM 	avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t),
117610491SRishi.Srivatsavai@Sun.COM 	    offsetof(bridge_fwd_t, bf_node));
117710491SRishi.Srivatsavai@Sun.COM 	bip = blp->bl_inst;
117810491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_WRITER);
117910491SRishi.Srivatsavai@Sun.COM 	bfnext = avl_first(&bip->bi_fwd);
118010491SRishi.Srivatsavai@Sun.COM 	while ((bfp = bfnext) != NULL) {
118110491SRishi.Srivatsavai@Sun.COM 		bfnext = AVL_NEXT(&bip->bi_fwd, bfp);
118210491SRishi.Srivatsavai@Sun.COM 		for (i = 0; i < bfp->bf_nlinks; i++) {
118310491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_links[i] == blp)
118410491SRishi.Srivatsavai@Sun.COM 				break;
118510491SRishi.Srivatsavai@Sun.COM 		}
118610491SRishi.Srivatsavai@Sun.COM 		if (i >= bfp->bf_nlinks)
118710491SRishi.Srivatsavai@Sun.COM 			continue;
118810491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_nlinks > 1) {
118910491SRishi.Srivatsavai@Sun.COM 			/* note that this can't be the last reference */
119010491SRishi.Srivatsavai@Sun.COM 			link_unref(blp);
119110491SRishi.Srivatsavai@Sun.COM 			bfp->bf_nlinks--;
119210491SRishi.Srivatsavai@Sun.COM 			for (; i < bfp->bf_nlinks; i++)
119310491SRishi.Srivatsavai@Sun.COM 				bfp->bf_links[i] = bfp->bf_links[i + 1];
119410491SRishi.Srivatsavai@Sun.COM 		} else {
119510491SRishi.Srivatsavai@Sun.COM 			ASSERT(bfp->bf_flags & BFF_INTREE);
119610491SRishi.Srivatsavai@Sun.COM 			avl_remove(&bip->bi_fwd, bfp);
119710491SRishi.Srivatsavai@Sun.COM 			bfp->bf_flags &= ~BFF_INTREE;
119810491SRishi.Srivatsavai@Sun.COM 			avl_add(&fwd_scavenge, bfp);
119910491SRishi.Srivatsavai@Sun.COM 		}
120010491SRishi.Srivatsavai@Sun.COM 	}
120110491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
120210491SRishi.Srivatsavai@Sun.COM 	bfnext = avl_first(&fwd_scavenge);
120310491SRishi.Srivatsavai@Sun.COM 	while ((bfp = bfnext) != NULL) {
120410491SRishi.Srivatsavai@Sun.COM 		bfnext = AVL_NEXT(&fwd_scavenge, bfp);
120510491SRishi.Srivatsavai@Sun.COM 		avl_remove(&fwd_scavenge, bfp);
120610491SRishi.Srivatsavai@Sun.COM 		fwd_unref(bfp);
120710491SRishi.Srivatsavai@Sun.COM 	}
120810491SRishi.Srivatsavai@Sun.COM 	avl_destroy(&fwd_scavenge);
120910491SRishi.Srivatsavai@Sun.COM 
121010491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_flags & BLF_CLIENT_OPEN)
121110491SRishi.Srivatsavai@Sun.COM 		mac_client_close(blp->bl_mch, 0);
121210491SRishi.Srivatsavai@Sun.COM 
121310491SRishi.Srivatsavai@Sun.COM 	mac_close(mh);
121410491SRishi.Srivatsavai@Sun.COM 
121510491SRishi.Srivatsavai@Sun.COM 	/*
121610491SRishi.Srivatsavai@Sun.COM 	 * We are now completely removed from the active list, so drop the
121710491SRishi.Srivatsavai@Sun.COM 	 * reference (see bridge_add_link).
121810491SRishi.Srivatsavai@Sun.COM 	 */
121910491SRishi.Srivatsavai@Sun.COM 	link_unref(blp);
122010491SRishi.Srivatsavai@Sun.COM }
122110491SRishi.Srivatsavai@Sun.COM 
122210491SRishi.Srivatsavai@Sun.COM static void
shutdown_inst(bridge_inst_t * bip)122310491SRishi.Srivatsavai@Sun.COM shutdown_inst(bridge_inst_t *bip)
122410491SRishi.Srivatsavai@Sun.COM {
122510491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp, *blnext;
122610491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp;
122710491SRishi.Srivatsavai@Sun.COM 
122810491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&inst_lock);
122910491SRishi.Srivatsavai@Sun.COM 	if (bip->bi_flags & BIF_SHUTDOWN) {
123010491SRishi.Srivatsavai@Sun.COM 		mutex_exit(&inst_lock);
123110491SRishi.Srivatsavai@Sun.COM 		return;
123210491SRishi.Srivatsavai@Sun.COM 	}
123310491SRishi.Srivatsavai@Sun.COM 
123410491SRishi.Srivatsavai@Sun.COM 	/*
123510491SRishi.Srivatsavai@Sun.COM 	 * Once on the inst_list, the bridge instance must not leave that list
123610491SRishi.Srivatsavai@Sun.COM 	 * without having the shutdown flag set first.  When the shutdown flag
123710491SRishi.Srivatsavai@Sun.COM 	 * is set, we own the list reference, so we must drop it before
123810491SRishi.Srivatsavai@Sun.COM 	 * returning.
123910491SRishi.Srivatsavai@Sun.COM 	 */
124010491SRishi.Srivatsavai@Sun.COM 	bip->bi_flags |= BIF_SHUTDOWN;
124110491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&inst_lock);
124210491SRishi.Srivatsavai@Sun.COM 
124310491SRishi.Srivatsavai@Sun.COM 	bip->bi_control = NULL;
124410491SRishi.Srivatsavai@Sun.COM 
124510491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_READER);
124610491SRishi.Srivatsavai@Sun.COM 	blnext = list_head(&bip->bi_links);
124710491SRishi.Srivatsavai@Sun.COM 	while ((blp = blnext) != NULL) {
124810491SRishi.Srivatsavai@Sun.COM 		blnext = list_next(&bip->bi_links, blp);
124910491SRishi.Srivatsavai@Sun.COM 		if (!(blp->bl_flags & BLF_DELETED)) {
125010491SRishi.Srivatsavai@Sun.COM 			blp->bl_flags |= BLF_DELETED;
125110491SRishi.Srivatsavai@Sun.COM 			(void) ddi_taskq_dispatch(bridge_taskq, link_shutdown,
125210491SRishi.Srivatsavai@Sun.COM 			    blp, DDI_SLEEP);
125310491SRishi.Srivatsavai@Sun.COM 		}
125410491SRishi.Srivatsavai@Sun.COM 	}
125510491SRishi.Srivatsavai@Sun.COM 	while ((bfp = avl_first(&bip->bi_fwd)) != NULL) {
125610491SRishi.Srivatsavai@Sun.COM 		atomic_inc_uint(&bfp->bf_refs);
125710491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
125810491SRishi.Srivatsavai@Sun.COM 		fwd_delete(bfp);
125910491SRishi.Srivatsavai@Sun.COM 		fwd_unref(bfp);
126010491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_READER);
126110491SRishi.Srivatsavai@Sun.COM 	}
126210491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
126310491SRishi.Srivatsavai@Sun.COM 
126410491SRishi.Srivatsavai@Sun.COM 	/*
126510491SRishi.Srivatsavai@Sun.COM 	 * This bridge is being destroyed.  Notify TRILL once all of the
126610491SRishi.Srivatsavai@Sun.COM 	 * links are all gone.
126710491SRishi.Srivatsavai@Sun.COM 	 */
126810491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&inst_lock);
126910491SRishi.Srivatsavai@Sun.COM 	while (bip->bi_trilldata != NULL && !list_is_empty(&bip->bi_links))
127010491SRishi.Srivatsavai@Sun.COM 		cv_wait(&bip->bi_linkwait, &inst_lock);
127110491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&inst_lock);
127210491SRishi.Srivatsavai@Sun.COM 	if (bip->bi_trilldata != NULL)
127310491SRishi.Srivatsavai@Sun.COM 		trill_brdstr_fn(bip->bi_trilldata, bip);
127410491SRishi.Srivatsavai@Sun.COM 
127510491SRishi.Srivatsavai@Sun.COM 	bridge_unref(bip);
127610491SRishi.Srivatsavai@Sun.COM }
127710491SRishi.Srivatsavai@Sun.COM 
127810491SRishi.Srivatsavai@Sun.COM /*
127910491SRishi.Srivatsavai@Sun.COM  * This is called once by the TRILL module when it starts up.  It just sets the
128010491SRishi.Srivatsavai@Sun.COM  * global TRILL callback function pointers -- data transmit/receive and bridge
128110491SRishi.Srivatsavai@Sun.COM  * and link destroy notification.  There's only one TRILL module, so only one
128210491SRishi.Srivatsavai@Sun.COM  * registration is needed.
128310491SRishi.Srivatsavai@Sun.COM  *
128410491SRishi.Srivatsavai@Sun.COM  * TRILL should call this function with NULL pointers before unloading.  It
128510491SRishi.Srivatsavai@Sun.COM  * must not do so before dropping all references to bridges and links.  We
128610491SRishi.Srivatsavai@Sun.COM  * assert that this is true on debug builds.
128710491SRishi.Srivatsavai@Sun.COM  */
128810491SRishi.Srivatsavai@Sun.COM 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)128910491SRishi.Srivatsavai@Sun.COM bridge_trill_register_cb(trill_recv_pkt_t recv_fn, trill_encap_pkt_t encap_fn,
129010491SRishi.Srivatsavai@Sun.COM     trill_br_dstr_t brdstr_fn, trill_ln_dstr_t lndstr_fn)
129110491SRishi.Srivatsavai@Sun.COM {
129210491SRishi.Srivatsavai@Sun.COM #ifdef DEBUG
129310491SRishi.Srivatsavai@Sun.COM 	if (recv_fn == NULL && trill_recv_fn != NULL) {
129410491SRishi.Srivatsavai@Sun.COM 		bridge_inst_t *bip;
129510491SRishi.Srivatsavai@Sun.COM 		bridge_link_t *blp;
129610491SRishi.Srivatsavai@Sun.COM 
129710491SRishi.Srivatsavai@Sun.COM 		mutex_enter(&inst_lock);
129810491SRishi.Srivatsavai@Sun.COM 		for (bip = list_head(&inst_list); bip != NULL;
129910491SRishi.Srivatsavai@Sun.COM 		    bip = list_next(&inst_list, bip)) {
130010491SRishi.Srivatsavai@Sun.COM 			ASSERT(bip->bi_trilldata == NULL);
130110491SRishi.Srivatsavai@Sun.COM 			rw_enter(&bip->bi_rwlock, RW_READER);
130210491SRishi.Srivatsavai@Sun.COM 			for (blp = list_head(&bip->bi_links); blp != NULL;
130310491SRishi.Srivatsavai@Sun.COM 			    blp = list_next(&bip->bi_links, blp)) {
130410491SRishi.Srivatsavai@Sun.COM 				ASSERT(blp->bl_trilldata == NULL);
130510491SRishi.Srivatsavai@Sun.COM 			}
130610491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bip->bi_rwlock);
130710491SRishi.Srivatsavai@Sun.COM 		}
130810491SRishi.Srivatsavai@Sun.COM 		mutex_exit(&inst_lock);
130910491SRishi.Srivatsavai@Sun.COM 	}
131010491SRishi.Srivatsavai@Sun.COM #endif
131110491SRishi.Srivatsavai@Sun.COM 	trill_recv_fn = recv_fn;
131210491SRishi.Srivatsavai@Sun.COM 	trill_encap_fn = encap_fn;
131310491SRishi.Srivatsavai@Sun.COM 	trill_brdstr_fn = brdstr_fn;
131410491SRishi.Srivatsavai@Sun.COM 	trill_lndstr_fn = lndstr_fn;
131510491SRishi.Srivatsavai@Sun.COM }
131610491SRishi.Srivatsavai@Sun.COM 
131710491SRishi.Srivatsavai@Sun.COM /*
131810491SRishi.Srivatsavai@Sun.COM  * This registers the TRILL instance pointer with a bridge.  Before this
131910491SRishi.Srivatsavai@Sun.COM  * pointer is set, the forwarding, TRILL receive, and bridge destructor
132010491SRishi.Srivatsavai@Sun.COM  * functions won't be called.
132110491SRishi.Srivatsavai@Sun.COM  *
132210491SRishi.Srivatsavai@Sun.COM  * TRILL holds a reference on a bridge with this call.  It must free the
132310491SRishi.Srivatsavai@Sun.COM  * reference by calling the unregister function below.
132410491SRishi.Srivatsavai@Sun.COM  */
132510491SRishi.Srivatsavai@Sun.COM bridge_inst_t *
bridge_trill_brref(const char * bname,void * ptr)132610491SRishi.Srivatsavai@Sun.COM bridge_trill_brref(const char *bname, void *ptr)
132710491SRishi.Srivatsavai@Sun.COM {
132810491SRishi.Srivatsavai@Sun.COM 	char bridge[MAXLINKNAMELEN];
132910491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
133010491SRishi.Srivatsavai@Sun.COM 
133110491SRishi.Srivatsavai@Sun.COM 	(void) snprintf(bridge, MAXLINKNAMELEN, "%s0", bname);
133210491SRishi.Srivatsavai@Sun.COM 	bip = bridge_find_name(bridge);
133310491SRishi.Srivatsavai@Sun.COM 	if (bip != NULL) {
133410491SRishi.Srivatsavai@Sun.COM 		ASSERT(bip->bi_trilldata == NULL && ptr != NULL);
133510491SRishi.Srivatsavai@Sun.COM 		bip->bi_trilldata = ptr;
133610491SRishi.Srivatsavai@Sun.COM 	}
133710491SRishi.Srivatsavai@Sun.COM 	return (bip);
133810491SRishi.Srivatsavai@Sun.COM }
133910491SRishi.Srivatsavai@Sun.COM 
134010491SRishi.Srivatsavai@Sun.COM void
bridge_trill_brunref(bridge_inst_t * bip)134110491SRishi.Srivatsavai@Sun.COM bridge_trill_brunref(bridge_inst_t *bip)
134210491SRishi.Srivatsavai@Sun.COM {
134310491SRishi.Srivatsavai@Sun.COM 	ASSERT(bip->bi_trilldata != NULL);
134410491SRishi.Srivatsavai@Sun.COM 	bip->bi_trilldata = NULL;
134510491SRishi.Srivatsavai@Sun.COM 	bridge_unref(bip);
134610491SRishi.Srivatsavai@Sun.COM }
134710491SRishi.Srivatsavai@Sun.COM 
134810491SRishi.Srivatsavai@Sun.COM /*
134910491SRishi.Srivatsavai@Sun.COM  * TRILL calls this function when referencing a particular link on a bridge.
135010491SRishi.Srivatsavai@Sun.COM  *
135110491SRishi.Srivatsavai@Sun.COM  * It holds a reference on the link, so TRILL must clear out the reference when
135210491SRishi.Srivatsavai@Sun.COM  * it's done with the link (on unbinding).
135310491SRishi.Srivatsavai@Sun.COM  */
135410491SRishi.Srivatsavai@Sun.COM bridge_link_t *
bridge_trill_lnref(bridge_inst_t * bip,datalink_id_t linkid,void * ptr)135510491SRishi.Srivatsavai@Sun.COM bridge_trill_lnref(bridge_inst_t *bip, datalink_id_t linkid, void *ptr)
135610491SRishi.Srivatsavai@Sun.COM {
135710491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp;
135810491SRishi.Srivatsavai@Sun.COM 
135910491SRishi.Srivatsavai@Sun.COM 	ASSERT(ptr != NULL);
136010491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_READER);
136110491SRishi.Srivatsavai@Sun.COM 	for (blp = list_head(&bip->bi_links); blp != NULL;
136210491SRishi.Srivatsavai@Sun.COM 	    blp = list_next(&bip->bi_links, blp)) {
136310491SRishi.Srivatsavai@Sun.COM 		if (!(blp->bl_flags & BLF_DELETED) &&
136410491SRishi.Srivatsavai@Sun.COM 		    blp->bl_linkid == linkid && blp->bl_trilldata == NULL) {
136510491SRishi.Srivatsavai@Sun.COM 			blp->bl_trilldata = ptr;
136610491SRishi.Srivatsavai@Sun.COM 			blp->bl_flags &= ~BLF_TRILLACTIVE;
136710491SRishi.Srivatsavai@Sun.COM 			(void) memset(blp->bl_afs, 0, sizeof (blp->bl_afs));
136810491SRishi.Srivatsavai@Sun.COM 			atomic_inc_uint(&blp->bl_refs);
136910491SRishi.Srivatsavai@Sun.COM 			break;
137010491SRishi.Srivatsavai@Sun.COM 		}
137110491SRishi.Srivatsavai@Sun.COM 	}
137210491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
137310491SRishi.Srivatsavai@Sun.COM 	return (blp);
137410491SRishi.Srivatsavai@Sun.COM }
137510491SRishi.Srivatsavai@Sun.COM 
137610491SRishi.Srivatsavai@Sun.COM void
bridge_trill_lnunref(bridge_link_t * blp)137710491SRishi.Srivatsavai@Sun.COM bridge_trill_lnunref(bridge_link_t *blp)
137810491SRishi.Srivatsavai@Sun.COM {
137910491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&blp->bl_trilllock);
138010491SRishi.Srivatsavai@Sun.COM 	ASSERT(blp->bl_trilldata != NULL);
138110491SRishi.Srivatsavai@Sun.COM 	blp->bl_trilldata = NULL;
138210491SRishi.Srivatsavai@Sun.COM 	blp->bl_flags &= ~BLF_TRILLACTIVE;
138310491SRishi.Srivatsavai@Sun.COM 	while (blp->bl_trillthreads > 0)
138410491SRishi.Srivatsavai@Sun.COM 		cv_wait(&blp->bl_trillwait, &blp->bl_trilllock);
138510491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&blp->bl_trilllock);
138610491SRishi.Srivatsavai@Sun.COM 	(void) memset(blp->bl_afs, 0xff, sizeof (blp->bl_afs));
138710491SRishi.Srivatsavai@Sun.COM 	link_unref(blp);
138810491SRishi.Srivatsavai@Sun.COM }
138910491SRishi.Srivatsavai@Sun.COM 
139010491SRishi.Srivatsavai@Sun.COM /*
139110491SRishi.Srivatsavai@Sun.COM  * This periodic timer performs three functions:
139210491SRishi.Srivatsavai@Sun.COM  *  1. It scans the list of learned forwarding entries, and removes ones that
139310491SRishi.Srivatsavai@Sun.COM  *     haven't been heard from in a while.  The time limit is backed down if
139410491SRishi.Srivatsavai@Sun.COM  *     we're above the configured table limit.
139510491SRishi.Srivatsavai@Sun.COM  *  2. It walks the links and decays away the bl_learns counter.
139610491SRishi.Srivatsavai@Sun.COM  *  3. It scans the observability node entries looking for ones that can be
139710491SRishi.Srivatsavai@Sun.COM  *     freed up.
139810491SRishi.Srivatsavai@Sun.COM  */
139910491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
140010491SRishi.Srivatsavai@Sun.COM static void
bridge_timer(void * arg)140110491SRishi.Srivatsavai@Sun.COM bridge_timer(void *arg)
140210491SRishi.Srivatsavai@Sun.COM {
140310491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
140410491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp, *bfnext;
140510491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp, *bmnext;
140610491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp;
140710491SRishi.Srivatsavai@Sun.COM 	int err;
140810491SRishi.Srivatsavai@Sun.COM 	datalink_id_t tmpid;
140910491SRishi.Srivatsavai@Sun.COM 	avl_tree_t fwd_scavenge;
141010491SRishi.Srivatsavai@Sun.COM 	clock_t age_limit;
141110491SRishi.Srivatsavai@Sun.COM 	uint32_t ldecay;
141210491SRishi.Srivatsavai@Sun.COM 
141310491SRishi.Srivatsavai@Sun.COM 	avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t),
141410491SRishi.Srivatsavai@Sun.COM 	    offsetof(bridge_fwd_t, bf_node));
141510491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&inst_lock);
141610491SRishi.Srivatsavai@Sun.COM 	for (bip = list_head(&inst_list); bip != NULL;
141710491SRishi.Srivatsavai@Sun.COM 	    bip = list_next(&inst_list, bip)) {
141810491SRishi.Srivatsavai@Sun.COM 		if (bip->bi_flags & BIF_SHUTDOWN)
141910491SRishi.Srivatsavai@Sun.COM 			continue;
142010491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_WRITER);
142110491SRishi.Srivatsavai@Sun.COM 		/* compute scaled maximum age based on table limit */
142210491SRishi.Srivatsavai@Sun.COM 		if (avl_numnodes(&bip->bi_fwd) > bip->bi_tablemax)
142310491SRishi.Srivatsavai@Sun.COM 			bip->bi_tshift++;
142410491SRishi.Srivatsavai@Sun.COM 		else
142510491SRishi.Srivatsavai@Sun.COM 			bip->bi_tshift = 0;
142610491SRishi.Srivatsavai@Sun.COM 		if ((age_limit = bridge_fwd_age >> bip->bi_tshift) == 0) {
142710491SRishi.Srivatsavai@Sun.COM 			if (bip->bi_tshift != 0)
142810491SRishi.Srivatsavai@Sun.COM 				bip->bi_tshift--;
142910491SRishi.Srivatsavai@Sun.COM 			age_limit = 1;
143010491SRishi.Srivatsavai@Sun.COM 		}
143110491SRishi.Srivatsavai@Sun.COM 		bfnext = avl_first(&bip->bi_fwd);
143210491SRishi.Srivatsavai@Sun.COM 		while ((bfp = bfnext) != NULL) {
143310491SRishi.Srivatsavai@Sun.COM 			bfnext = AVL_NEXT(&bip->bi_fwd, bfp);
143410491SRishi.Srivatsavai@Sun.COM 			if (!(bfp->bf_flags & BFF_LOCALADDR) &&
143511066Srafael.vanoni@sun.com 			    (ddi_get_lbolt() - bfp->bf_lastheard) > age_limit) {
143610491SRishi.Srivatsavai@Sun.COM 				ASSERT(bfp->bf_flags & BFF_INTREE);
143710491SRishi.Srivatsavai@Sun.COM 				avl_remove(&bip->bi_fwd, bfp);
143810491SRishi.Srivatsavai@Sun.COM 				bfp->bf_flags &= ~BFF_INTREE;
143910491SRishi.Srivatsavai@Sun.COM 				avl_add(&fwd_scavenge, bfp);
144010491SRishi.Srivatsavai@Sun.COM 			}
144110491SRishi.Srivatsavai@Sun.COM 		}
144210491SRishi.Srivatsavai@Sun.COM 		for (blp = list_head(&bip->bi_links); blp != NULL;
144310491SRishi.Srivatsavai@Sun.COM 		    blp = list_next(&bip->bi_links, blp)) {
144410491SRishi.Srivatsavai@Sun.COM 			ldecay = mac_get_ldecay(blp->bl_mh);
144510491SRishi.Srivatsavai@Sun.COM 			if (ldecay >= blp->bl_learns)
144610491SRishi.Srivatsavai@Sun.COM 				blp->bl_learns = 0;
144710491SRishi.Srivatsavai@Sun.COM 			else
144810491SRishi.Srivatsavai@Sun.COM 				atomic_add_int(&blp->bl_learns, -(int)ldecay);
144910491SRishi.Srivatsavai@Sun.COM 		}
145010491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
145110491SRishi.Srivatsavai@Sun.COM 		bfnext = avl_first(&fwd_scavenge);
145210491SRishi.Srivatsavai@Sun.COM 		while ((bfp = bfnext) != NULL) {
145310491SRishi.Srivatsavai@Sun.COM 			bfnext = AVL_NEXT(&fwd_scavenge, bfp);
145410491SRishi.Srivatsavai@Sun.COM 			avl_remove(&fwd_scavenge, bfp);
145510491SRishi.Srivatsavai@Sun.COM 			KIINCR(bki_expire);
145610491SRishi.Srivatsavai@Sun.COM 			fwd_unref(bfp);	/* drop tree reference */
145710491SRishi.Srivatsavai@Sun.COM 		}
145810491SRishi.Srivatsavai@Sun.COM 	}
145910491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&inst_lock);
146010491SRishi.Srivatsavai@Sun.COM 	avl_destroy(&fwd_scavenge);
146110491SRishi.Srivatsavai@Sun.COM 
146210491SRishi.Srivatsavai@Sun.COM 	/*
146310491SRishi.Srivatsavai@Sun.COM 	 * Scan the bridge_mac_t entries and try to free up the ones that are
146410491SRishi.Srivatsavai@Sun.COM 	 * no longer active.  This must be done by polling, as neither DLS nor
146510491SRishi.Srivatsavai@Sun.COM 	 * MAC provides a driver any sort of positive control over clients.
146610491SRishi.Srivatsavai@Sun.COM 	 */
146710491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bmac_rwlock, RW_WRITER);
146810491SRishi.Srivatsavai@Sun.COM 	bmnext = list_head(&bmac_list);
146910491SRishi.Srivatsavai@Sun.COM 	while ((bmp = bmnext) != NULL) {
147010491SRishi.Srivatsavai@Sun.COM 		bmnext = list_next(&bmac_list, bmp);
147110491SRishi.Srivatsavai@Sun.COM 
147210491SRishi.Srivatsavai@Sun.COM 		/* ignore active bridges */
147310491SRishi.Srivatsavai@Sun.COM 		if (bmp->bm_inst != NULL)
147410491SRishi.Srivatsavai@Sun.COM 			continue;
147510491SRishi.Srivatsavai@Sun.COM 
147610491SRishi.Srivatsavai@Sun.COM 		if (bmp->bm_flags & BMF_DLS) {
147710491SRishi.Srivatsavai@Sun.COM 			err = dls_devnet_destroy(bmp->bm_mh, &tmpid, B_FALSE);
147810491SRishi.Srivatsavai@Sun.COM 			ASSERT(err == 0 || err == EBUSY);
147910491SRishi.Srivatsavai@Sun.COM 			if (err == 0)
148010491SRishi.Srivatsavai@Sun.COM 				bmp->bm_flags &= ~BMF_DLS;
148110491SRishi.Srivatsavai@Sun.COM 		}
148210491SRishi.Srivatsavai@Sun.COM 
148310491SRishi.Srivatsavai@Sun.COM 		if (!(bmp->bm_flags & BMF_DLS)) {
148410491SRishi.Srivatsavai@Sun.COM 			err = mac_unregister(bmp->bm_mh);
148510491SRishi.Srivatsavai@Sun.COM 			ASSERT(err == 0 || err == EBUSY);
148610491SRishi.Srivatsavai@Sun.COM 			if (err == 0) {
148710491SRishi.Srivatsavai@Sun.COM 				list_remove(&bmac_list, bmp);
148810491SRishi.Srivatsavai@Sun.COM 				kmem_free(bmp, sizeof (*bmp));
148910491SRishi.Srivatsavai@Sun.COM 			}
149010491SRishi.Srivatsavai@Sun.COM 		}
149110491SRishi.Srivatsavai@Sun.COM 	}
149210491SRishi.Srivatsavai@Sun.COM 	if (list_is_empty(&bmac_list)) {
149310491SRishi.Srivatsavai@Sun.COM 		bridge_timerid = 0;
149410491SRishi.Srivatsavai@Sun.COM 	} else {
149510491SRishi.Srivatsavai@Sun.COM 		bridge_timerid = timeout(bridge_timer, NULL,
149610491SRishi.Srivatsavai@Sun.COM 		    bridge_scan_interval);
149710491SRishi.Srivatsavai@Sun.COM 	}
149810491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bmac_rwlock);
149910491SRishi.Srivatsavai@Sun.COM }
150010491SRishi.Srivatsavai@Sun.COM 
150110491SRishi.Srivatsavai@Sun.COM static int
bridge_open(queue_t * rq,dev_t * devp,int oflag,int sflag,cred_t * credp)150210491SRishi.Srivatsavai@Sun.COM bridge_open(queue_t *rq, dev_t *devp, int oflag, int sflag, cred_t *credp)
150310491SRishi.Srivatsavai@Sun.COM {
150410491SRishi.Srivatsavai@Sun.COM 	bridge_stream_t	*bsp;
150510491SRishi.Srivatsavai@Sun.COM 
150610491SRishi.Srivatsavai@Sun.COM 	if (rq->q_ptr != NULL)
150710491SRishi.Srivatsavai@Sun.COM 		return (0);
150810491SRishi.Srivatsavai@Sun.COM 
150910491SRishi.Srivatsavai@Sun.COM 	if (sflag & MODOPEN)
151010491SRishi.Srivatsavai@Sun.COM 		return (EINVAL);
151110491SRishi.Srivatsavai@Sun.COM 
151210491SRishi.Srivatsavai@Sun.COM 	/*
151310491SRishi.Srivatsavai@Sun.COM 	 * Check the minor node number being opened.  This tells us which
151410491SRishi.Srivatsavai@Sun.COM 	 * bridge instance the user wants.
151510491SRishi.Srivatsavai@Sun.COM 	 */
151610491SRishi.Srivatsavai@Sun.COM 	if (getminor(*devp) != 0) {
151710491SRishi.Srivatsavai@Sun.COM 		/*
151810491SRishi.Srivatsavai@Sun.COM 		 * This is a regular DLPI stream for snoop or the like.
151910491SRishi.Srivatsavai@Sun.COM 		 * Redirect it through DLD.
152010491SRishi.Srivatsavai@Sun.COM 		 */
152110491SRishi.Srivatsavai@Sun.COM 		rq->q_qinfo = &bridge_dld_rinit;
152210491SRishi.Srivatsavai@Sun.COM 		OTHERQ(rq)->q_qinfo = &bridge_dld_winit;
152310491SRishi.Srivatsavai@Sun.COM 		return (dld_open(rq, devp, oflag, sflag, credp));
152410491SRishi.Srivatsavai@Sun.COM 	} else {
152510491SRishi.Srivatsavai@Sun.COM 		/*
152610491SRishi.Srivatsavai@Sun.COM 		 * Allocate the bridge control stream structure.
152710491SRishi.Srivatsavai@Sun.COM 		 */
152810491SRishi.Srivatsavai@Sun.COM 		if ((bsp = stream_alloc()) == NULL)
152910491SRishi.Srivatsavai@Sun.COM 			return (ENOSR);
153010491SRishi.Srivatsavai@Sun.COM 		rq->q_ptr = WR(rq)->q_ptr = (caddr_t)bsp;
153110491SRishi.Srivatsavai@Sun.COM 		bsp->bs_wq = WR(rq);
153210491SRishi.Srivatsavai@Sun.COM 		*devp = makedevice(getmajor(*devp), bsp->bs_minor);
153310491SRishi.Srivatsavai@Sun.COM 		qprocson(rq);
153410491SRishi.Srivatsavai@Sun.COM 		return (0);
153510491SRishi.Srivatsavai@Sun.COM 	}
153610491SRishi.Srivatsavai@Sun.COM }
153710491SRishi.Srivatsavai@Sun.COM 
153810491SRishi.Srivatsavai@Sun.COM /*
153910491SRishi.Srivatsavai@Sun.COM  * This is used only for bridge control streams.  DLPI goes through dld
154010491SRishi.Srivatsavai@Sun.COM  * instead.
154110491SRishi.Srivatsavai@Sun.COM  */
154210491SRishi.Srivatsavai@Sun.COM static int
bridge_close(queue_t * rq)154310491SRishi.Srivatsavai@Sun.COM bridge_close(queue_t *rq)
154410491SRishi.Srivatsavai@Sun.COM {
154510491SRishi.Srivatsavai@Sun.COM 	bridge_stream_t	*bsp = rq->q_ptr;
154610491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
154710491SRishi.Srivatsavai@Sun.COM 
154810491SRishi.Srivatsavai@Sun.COM 	/*
154910491SRishi.Srivatsavai@Sun.COM 	 * Wait for any stray taskq (add/delete link) entries related to this
155010491SRishi.Srivatsavai@Sun.COM 	 * stream to leave the system.
155110491SRishi.Srivatsavai@Sun.COM 	 */
155210491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&stream_ref_lock);
155310491SRishi.Srivatsavai@Sun.COM 	while (bsp->bs_taskq_cnt != 0)
155410491SRishi.Srivatsavai@Sun.COM 		cv_wait(&stream_ref_cv, &stream_ref_lock);
155510491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&stream_ref_lock);
155610491SRishi.Srivatsavai@Sun.COM 
155710491SRishi.Srivatsavai@Sun.COM 	qprocsoff(rq);
155810491SRishi.Srivatsavai@Sun.COM 	if ((bip = bsp->bs_inst) != NULL)
155910491SRishi.Srivatsavai@Sun.COM 		shutdown_inst(bip);
156010491SRishi.Srivatsavai@Sun.COM 	rq->q_ptr = WR(rq)->q_ptr = NULL;
156110491SRishi.Srivatsavai@Sun.COM 	stream_free(bsp);
156210491SRishi.Srivatsavai@Sun.COM 	if (bip != NULL)
156310491SRishi.Srivatsavai@Sun.COM 		bridge_unref(bip);
156410491SRishi.Srivatsavai@Sun.COM 
156510491SRishi.Srivatsavai@Sun.COM 	return (0);
156610491SRishi.Srivatsavai@Sun.COM }
156710491SRishi.Srivatsavai@Sun.COM 
156810491SRishi.Srivatsavai@Sun.COM static void
bridge_learn(bridge_link_t * blp,const uint8_t * saddr,uint16_t ingress_nick,uint16_t vlanid)156910491SRishi.Srivatsavai@Sun.COM bridge_learn(bridge_link_t *blp, const uint8_t *saddr, uint16_t ingress_nick,
157010491SRishi.Srivatsavai@Sun.COM     uint16_t vlanid)
157110491SRishi.Srivatsavai@Sun.COM {
157210491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;
157310491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp, *bfpnew;
157410491SRishi.Srivatsavai@Sun.COM 	int i;
157510491SRishi.Srivatsavai@Sun.COM 	boolean_t replaced = B_FALSE;
157610491SRishi.Srivatsavai@Sun.COM 
157710491SRishi.Srivatsavai@Sun.COM 	/* Ignore multi-destination address used as source; it's nonsense. */
157810491SRishi.Srivatsavai@Sun.COM 	if (*saddr & 1)
157910491SRishi.Srivatsavai@Sun.COM 		return;
158010491SRishi.Srivatsavai@Sun.COM 
158110491SRishi.Srivatsavai@Sun.COM 	/*
158210491SRishi.Srivatsavai@Sun.COM 	 * If the source is known, then check whether it belongs on this link.
158310491SRishi.Srivatsavai@Sun.COM 	 * If not, and this isn't a fixed local address, then we've detected a
158410491SRishi.Srivatsavai@Sun.COM 	 * move.  If it's not known, learn it.
158510491SRishi.Srivatsavai@Sun.COM 	 */
158610491SRishi.Srivatsavai@Sun.COM 	if ((bfp = fwd_find(bip, saddr, vlanid)) != NULL) {
158710491SRishi.Srivatsavai@Sun.COM 		/*
158810491SRishi.Srivatsavai@Sun.COM 		 * If the packet has a fixed local source address, then there's
158910491SRishi.Srivatsavai@Sun.COM 		 * nothing we can learn.  We must quit.  If this was a received
159010491SRishi.Srivatsavai@Sun.COM 		 * packet, then the sender has stolen our address, but there's
159110491SRishi.Srivatsavai@Sun.COM 		 * nothing we can do.  If it's a transmitted packet, then
159210491SRishi.Srivatsavai@Sun.COM 		 * that's the normal case.
159310491SRishi.Srivatsavai@Sun.COM 		 */
159410491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_flags & BFF_LOCALADDR) {
159510491SRishi.Srivatsavai@Sun.COM 			fwd_unref(bfp);
159610491SRishi.Srivatsavai@Sun.COM 			return;
159710491SRishi.Srivatsavai@Sun.COM 		}
159810491SRishi.Srivatsavai@Sun.COM 
159910491SRishi.Srivatsavai@Sun.COM 		/*
160010491SRishi.Srivatsavai@Sun.COM 		 * Check if the link (and TRILL sender, if any) being used is
160110491SRishi.Srivatsavai@Sun.COM 		 * among the ones registered for this address.  If so, then
160210491SRishi.Srivatsavai@Sun.COM 		 * this is information that we already know.
160310491SRishi.Srivatsavai@Sun.COM 		 */
160410491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_trill_nick == ingress_nick) {
160510491SRishi.Srivatsavai@Sun.COM 			for (i = 0; i < bfp->bf_nlinks; i++) {
160610491SRishi.Srivatsavai@Sun.COM 				if (bfp->bf_links[i] == blp) {
160711066Srafael.vanoni@sun.com 					bfp->bf_lastheard = ddi_get_lbolt();
160810491SRishi.Srivatsavai@Sun.COM 					fwd_unref(bfp);
160910491SRishi.Srivatsavai@Sun.COM 					return;
161010491SRishi.Srivatsavai@Sun.COM 				}
161110491SRishi.Srivatsavai@Sun.COM 			}
161210491SRishi.Srivatsavai@Sun.COM 		}
161310491SRishi.Srivatsavai@Sun.COM 	}
161410491SRishi.Srivatsavai@Sun.COM 
161510491SRishi.Srivatsavai@Sun.COM 	/*
161610491SRishi.Srivatsavai@Sun.COM 	 * Note that we intentionally "unlearn" things that appear to be under
161710491SRishi.Srivatsavai@Sun.COM 	 * attack on this link.  The forwarding cache is a negative thing for
161810491SRishi.Srivatsavai@Sun.COM 	 * security -- it disables reachability as a performance optimization
161910491SRishi.Srivatsavai@Sun.COM 	 * -- so leaving out entries optimizes for success and defends against
162010491SRishi.Srivatsavai@Sun.COM 	 * the attack.  Thus, the bare increment without a check in the delete
162110491SRishi.Srivatsavai@Sun.COM 	 * code above is right.  (And it's ok if we skid over the limit a
162210491SRishi.Srivatsavai@Sun.COM 	 * little, so there's no syncronization needed on the test.)
162310491SRishi.Srivatsavai@Sun.COM 	 */
162410491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_learns >= mac_get_llimit(blp->bl_mh)) {
162510491SRishi.Srivatsavai@Sun.COM 		if (bfp != NULL) {
162610491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_vcnt == 0)
162710491SRishi.Srivatsavai@Sun.COM 				fwd_delete(bfp);
162810491SRishi.Srivatsavai@Sun.COM 			fwd_unref(bfp);
162910491SRishi.Srivatsavai@Sun.COM 		}
163010491SRishi.Srivatsavai@Sun.COM 		return;
163110491SRishi.Srivatsavai@Sun.COM 	}
163210491SRishi.Srivatsavai@Sun.COM 
163310491SRishi.Srivatsavai@Sun.COM 	atomic_inc_uint(&blp->bl_learns);
163410491SRishi.Srivatsavai@Sun.COM 
163510491SRishi.Srivatsavai@Sun.COM 	if ((bfpnew = fwd_alloc(saddr, 1, ingress_nick)) == NULL) {
163610491SRishi.Srivatsavai@Sun.COM 		if (bfp != NULL)
163710491SRishi.Srivatsavai@Sun.COM 			fwd_unref(bfp);
163810491SRishi.Srivatsavai@Sun.COM 		return;
163910491SRishi.Srivatsavai@Sun.COM 	}
164010491SRishi.Srivatsavai@Sun.COM 	KIINCR(bki_count);
164110491SRishi.Srivatsavai@Sun.COM 
164210491SRishi.Srivatsavai@Sun.COM 	if (bfp != NULL) {
164310491SRishi.Srivatsavai@Sun.COM 		/*
164410491SRishi.Srivatsavai@Sun.COM 		 * If this is a new destination for the same VLAN, then delete
164510491SRishi.Srivatsavai@Sun.COM 		 * so that we can update.  If it's a different VLAN, then we're
164610491SRishi.Srivatsavai@Sun.COM 		 * not going to delete the original.  Split off instead into an
164710491SRishi.Srivatsavai@Sun.COM 		 * IVL entry.
164810491SRishi.Srivatsavai@Sun.COM 		 */
164910491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_vlanid == vlanid) {
165010491SRishi.Srivatsavai@Sun.COM 			/* save the count of IVL duplicates */
165110491SRishi.Srivatsavai@Sun.COM 			bfpnew->bf_vcnt = bfp->bf_vcnt;
165210491SRishi.Srivatsavai@Sun.COM 
165310491SRishi.Srivatsavai@Sun.COM 			/* entry deletes count as learning events */
165410491SRishi.Srivatsavai@Sun.COM 			atomic_inc_uint(&blp->bl_learns);
165510491SRishi.Srivatsavai@Sun.COM 
165610491SRishi.Srivatsavai@Sun.COM 			/* destroy and create anew; node moved */
165710491SRishi.Srivatsavai@Sun.COM 			fwd_delete(bfp);
165810491SRishi.Srivatsavai@Sun.COM 			replaced = B_TRUE;
165910491SRishi.Srivatsavai@Sun.COM 			KIINCR(bki_moved);
166010491SRishi.Srivatsavai@Sun.COM 		} else {
166110491SRishi.Srivatsavai@Sun.COM 			bfp->bf_vcnt++;
166210491SRishi.Srivatsavai@Sun.COM 			bfpnew->bf_flags |= BFF_VLANLOCAL;
166310491SRishi.Srivatsavai@Sun.COM 		}
166410491SRishi.Srivatsavai@Sun.COM 		fwd_unref(bfp);
166510491SRishi.Srivatsavai@Sun.COM 	}
166610491SRishi.Srivatsavai@Sun.COM 	bfpnew->bf_links[0] = blp;
166710491SRishi.Srivatsavai@Sun.COM 	bfpnew->bf_nlinks = 1;
166810491SRishi.Srivatsavai@Sun.COM 	atomic_inc_uint(&blp->bl_refs);	/* bf_links entry */
166910491SRishi.Srivatsavai@Sun.COM 	if (!fwd_insert(bip, bfpnew))
167010491SRishi.Srivatsavai@Sun.COM 		fwd_free(bfpnew);
167110491SRishi.Srivatsavai@Sun.COM 	else if (!replaced)
167210491SRishi.Srivatsavai@Sun.COM 		KIINCR(bki_source);
167310491SRishi.Srivatsavai@Sun.COM }
167410491SRishi.Srivatsavai@Sun.COM 
167510491SRishi.Srivatsavai@Sun.COM /*
167610491SRishi.Srivatsavai@Sun.COM  * Process the VLAN headers for output on a given link.  There are several
167710491SRishi.Srivatsavai@Sun.COM  * cases (noting that we don't map VLANs):
167810491SRishi.Srivatsavai@Sun.COM  *   1. The input packet is good as it is; either
167910491SRishi.Srivatsavai@Sun.COM  *	a. It has no tag, and output has same PVID
168010491SRishi.Srivatsavai@Sun.COM  *	b. It has a non-zero priority-only tag for PVID, and b_band is same
168110491SRishi.Srivatsavai@Sun.COM  *	c. It has a tag with VLAN different from PVID, and b_band is same
168210491SRishi.Srivatsavai@Sun.COM  *   2. The tag must change: non-zero b_band is different from tag priority
168310491SRishi.Srivatsavai@Sun.COM  *   3. The packet has a tag and should not (VLAN same as PVID, b_band zero)
168410491SRishi.Srivatsavai@Sun.COM  *   4. The packet has no tag and needs one:
168510491SRishi.Srivatsavai@Sun.COM  *      a. VLAN ID same as PVID, but b_band is non-zero
168610491SRishi.Srivatsavai@Sun.COM  *      b. VLAN ID different from PVID
168710491SRishi.Srivatsavai@Sun.COM  * We exclude case 1 first, then modify the packet.  Note that output packets
168810491SRishi.Srivatsavai@Sun.COM  * get a priority set by the mblk, not by the header, because QoS in bridging
168910491SRishi.Srivatsavai@Sun.COM  * requires priority recalculation at each node.
169010491SRishi.Srivatsavai@Sun.COM  *
169110491SRishi.Srivatsavai@Sun.COM  * The passed-in tci is the "impossible" value 0xFFFF when no tag is present.
169210491SRishi.Srivatsavai@Sun.COM  */
169310491SRishi.Srivatsavai@Sun.COM static mblk_t *
reform_vlan_header(mblk_t * mp,uint16_t vlanid,uint16_t tci,uint16_t pvid)169410491SRishi.Srivatsavai@Sun.COM reform_vlan_header(mblk_t *mp, uint16_t vlanid, uint16_t tci, uint16_t pvid)
169510491SRishi.Srivatsavai@Sun.COM {
169610491SRishi.Srivatsavai@Sun.COM 	boolean_t source_has_tag = (tci != 0xFFFF);
169710491SRishi.Srivatsavai@Sun.COM 	mblk_t *mpcopy;
169810491SRishi.Srivatsavai@Sun.COM 	size_t mlen, minlen;
169910491SRishi.Srivatsavai@Sun.COM 	struct ether_vlan_header *evh;
170010491SRishi.Srivatsavai@Sun.COM 	int pri;
170110491SRishi.Srivatsavai@Sun.COM 
170210491SRishi.Srivatsavai@Sun.COM 	/* This helps centralize error handling in the caller. */
170310491SRishi.Srivatsavai@Sun.COM 	if (mp == NULL)
170410491SRishi.Srivatsavai@Sun.COM 		return (mp);
170510491SRishi.Srivatsavai@Sun.COM 
170610491SRishi.Srivatsavai@Sun.COM 	/* No forwarded packet can have hardware checksum enabled */
170710491SRishi.Srivatsavai@Sun.COM 	DB_CKSUMFLAGS(mp) = 0;
170810491SRishi.Srivatsavai@Sun.COM 
170910491SRishi.Srivatsavai@Sun.COM 	/* Get the no-modification cases out of the way first */
171010491SRishi.Srivatsavai@Sun.COM 	if (!source_has_tag && vlanid == pvid)		/* 1a */
171110491SRishi.Srivatsavai@Sun.COM 		return (mp);
171210491SRishi.Srivatsavai@Sun.COM 
171310491SRishi.Srivatsavai@Sun.COM 	pri = VLAN_PRI(tci);
171410491SRishi.Srivatsavai@Sun.COM 	if (source_has_tag && mp->b_band == pri) {
171510491SRishi.Srivatsavai@Sun.COM 		if (vlanid != pvid)			/* 1c */
171610491SRishi.Srivatsavai@Sun.COM 			return (mp);
171710491SRishi.Srivatsavai@Sun.COM 		if (pri != 0 && VLAN_ID(tci) == 0)	/* 1b */
171810491SRishi.Srivatsavai@Sun.COM 			return (mp);
171910491SRishi.Srivatsavai@Sun.COM 	}
172010491SRishi.Srivatsavai@Sun.COM 
172110491SRishi.Srivatsavai@Sun.COM 	/*
172210491SRishi.Srivatsavai@Sun.COM 	 * We now know that we must modify the packet.  Prepare for that.  Note
172310491SRishi.Srivatsavai@Sun.COM 	 * that if a tag is present, the caller has already done a pullup for
172410491SRishi.Srivatsavai@Sun.COM 	 * the VLAN header, so we're good to go.
172510491SRishi.Srivatsavai@Sun.COM 	 */
172610491SRishi.Srivatsavai@Sun.COM 	if (MBLKL(mp) < sizeof (struct ether_header)) {
172710491SRishi.Srivatsavai@Sun.COM 		mpcopy = msgpullup(mp, sizeof (struct ether_header));
172810491SRishi.Srivatsavai@Sun.COM 		if (mpcopy == NULL) {
172910491SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
173010491SRishi.Srivatsavai@Sun.COM 			return (NULL);
173110491SRishi.Srivatsavai@Sun.COM 		}
173210491SRishi.Srivatsavai@Sun.COM 		mp = mpcopy;
173310491SRishi.Srivatsavai@Sun.COM 	}
173410491SRishi.Srivatsavai@Sun.COM 	if (DB_REF(mp) > 1 || !IS_P2ALIGNED(mp->b_rptr, sizeof (uint16_t)) ||
173510491SRishi.Srivatsavai@Sun.COM 	    (!source_has_tag && MBLKTAIL(mp) < VLAN_INCR)) {
173610491SRishi.Srivatsavai@Sun.COM 		minlen = mlen = MBLKL(mp);
173710491SRishi.Srivatsavai@Sun.COM 		if (!source_has_tag)
173810491SRishi.Srivatsavai@Sun.COM 			minlen += VLAN_INCR;
173910491SRishi.Srivatsavai@Sun.COM 		ASSERT(minlen >= sizeof (struct ether_vlan_header));
174010491SRishi.Srivatsavai@Sun.COM 		/*
174110491SRishi.Srivatsavai@Sun.COM 		 * We're willing to copy some data to avoid fragmentation, but
174210491SRishi.Srivatsavai@Sun.COM 		 * not a lot.
174310491SRishi.Srivatsavai@Sun.COM 		 */
174410491SRishi.Srivatsavai@Sun.COM 		if (minlen > 256)
174510491SRishi.Srivatsavai@Sun.COM 			minlen = sizeof (struct ether_vlan_header);
174610491SRishi.Srivatsavai@Sun.COM 		mpcopy = allocb(minlen, BPRI_MED);
174710491SRishi.Srivatsavai@Sun.COM 		if (mpcopy == NULL) {
174810491SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
174910491SRishi.Srivatsavai@Sun.COM 			return (NULL);
175010491SRishi.Srivatsavai@Sun.COM 		}
175110491SRishi.Srivatsavai@Sun.COM 		if (mlen <= minlen) {
175210491SRishi.Srivatsavai@Sun.COM 			/* We toss the first mblk when we can. */
175310491SRishi.Srivatsavai@Sun.COM 			bcopy(mp->b_rptr, mpcopy->b_rptr, mlen);
175410491SRishi.Srivatsavai@Sun.COM 			mpcopy->b_wptr += mlen;
175510491SRishi.Srivatsavai@Sun.COM 			mpcopy->b_cont = mp->b_cont;
175610491SRishi.Srivatsavai@Sun.COM 			freeb(mp);
175710491SRishi.Srivatsavai@Sun.COM 		} else {
175810491SRishi.Srivatsavai@Sun.COM 			/* If not, then just copy what we need */
175910491SRishi.Srivatsavai@Sun.COM 			if (!source_has_tag)
176010491SRishi.Srivatsavai@Sun.COM 				minlen = sizeof (struct ether_header);
176110491SRishi.Srivatsavai@Sun.COM 			bcopy(mp->b_rptr, mpcopy->b_rptr, minlen);
176210491SRishi.Srivatsavai@Sun.COM 			mpcopy->b_wptr += minlen;
176310491SRishi.Srivatsavai@Sun.COM 			mpcopy->b_cont = mp;
176410491SRishi.Srivatsavai@Sun.COM 			mp->b_rptr += minlen;
176510491SRishi.Srivatsavai@Sun.COM 		}
176610491SRishi.Srivatsavai@Sun.COM 		mp = mpcopy;
176710491SRishi.Srivatsavai@Sun.COM 	}
176810491SRishi.Srivatsavai@Sun.COM 
176910491SRishi.Srivatsavai@Sun.COM 	/* LINTED: pointer alignment */
177010491SRishi.Srivatsavai@Sun.COM 	evh = (struct ether_vlan_header *)mp->b_rptr;
177110491SRishi.Srivatsavai@Sun.COM 	if (source_has_tag) {
177210491SRishi.Srivatsavai@Sun.COM 		if (mp->b_band == 0 && vlanid == pvid) {	/* 3 */
177310491SRishi.Srivatsavai@Sun.COM 			evh->ether_tpid = evh->ether_type;
177410491SRishi.Srivatsavai@Sun.COM 			mlen = MBLKL(mp);
177510491SRishi.Srivatsavai@Sun.COM 			if (mlen > sizeof (struct ether_vlan_header))
177610491SRishi.Srivatsavai@Sun.COM 				ovbcopy(mp->b_rptr +
177710491SRishi.Srivatsavai@Sun.COM 				    sizeof (struct ether_vlan_header),
177810491SRishi.Srivatsavai@Sun.COM 				    mp->b_rptr + sizeof (struct ether_header),
177910491SRishi.Srivatsavai@Sun.COM 				    mlen - sizeof (struct ether_vlan_header));
178010491SRishi.Srivatsavai@Sun.COM 			mp->b_wptr -= VLAN_INCR;
178110491SRishi.Srivatsavai@Sun.COM 		} else {					/* 2 */
178210491SRishi.Srivatsavai@Sun.COM 			if (vlanid == pvid)
178310491SRishi.Srivatsavai@Sun.COM 				vlanid = VLAN_ID_NONE;
178410491SRishi.Srivatsavai@Sun.COM 			tci = VLAN_TCI(mp->b_band, ETHER_CFI, vlanid);
178510491SRishi.Srivatsavai@Sun.COM 			evh->ether_tci = htons(tci);
178610491SRishi.Srivatsavai@Sun.COM 		}
178710491SRishi.Srivatsavai@Sun.COM 	} else {
178810491SRishi.Srivatsavai@Sun.COM 		/* case 4: no header present, but one is needed */
178910491SRishi.Srivatsavai@Sun.COM 		mlen = MBLKL(mp);
179010491SRishi.Srivatsavai@Sun.COM 		if (mlen > sizeof (struct ether_header))
179110491SRishi.Srivatsavai@Sun.COM 			ovbcopy(mp->b_rptr + sizeof (struct ether_header),
179210491SRishi.Srivatsavai@Sun.COM 			    mp->b_rptr + sizeof (struct ether_vlan_header),
179310491SRishi.Srivatsavai@Sun.COM 			    mlen - sizeof (struct ether_header));
179410491SRishi.Srivatsavai@Sun.COM 		mp->b_wptr += VLAN_INCR;
179510491SRishi.Srivatsavai@Sun.COM 		ASSERT(mp->b_wptr <= DB_LIM(mp));
179610491SRishi.Srivatsavai@Sun.COM 		if (vlanid == pvid)
179710491SRishi.Srivatsavai@Sun.COM 			vlanid = VLAN_ID_NONE;
179810491SRishi.Srivatsavai@Sun.COM 		tci = VLAN_TCI(mp->b_band, ETHER_CFI, vlanid);
179910491SRishi.Srivatsavai@Sun.COM 		evh->ether_type = evh->ether_tpid;
180010491SRishi.Srivatsavai@Sun.COM 		evh->ether_tpid = htons(ETHERTYPE_VLAN);
180110491SRishi.Srivatsavai@Sun.COM 		evh->ether_tci = htons(tci);
180210491SRishi.Srivatsavai@Sun.COM 	}
180310491SRishi.Srivatsavai@Sun.COM 	return (mp);
180410491SRishi.Srivatsavai@Sun.COM }
180510491SRishi.Srivatsavai@Sun.COM 
180610491SRishi.Srivatsavai@Sun.COM /* Record VLAN information and strip header if requested . */
180710491SRishi.Srivatsavai@Sun.COM static void
update_header(mblk_t * mp,mac_header_info_t * hdr_info,boolean_t striphdr)180810491SRishi.Srivatsavai@Sun.COM update_header(mblk_t *mp, mac_header_info_t *hdr_info, boolean_t striphdr)
180910491SRishi.Srivatsavai@Sun.COM {
181010491SRishi.Srivatsavai@Sun.COM 	if (hdr_info->mhi_bindsap == ETHERTYPE_VLAN) {
181110491SRishi.Srivatsavai@Sun.COM 		struct ether_vlan_header *evhp;
181210491SRishi.Srivatsavai@Sun.COM 		uint16_t ether_type;
181310491SRishi.Srivatsavai@Sun.COM 
181410491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
181510491SRishi.Srivatsavai@Sun.COM 		evhp = (struct ether_vlan_header *)mp->b_rptr;
181610491SRishi.Srivatsavai@Sun.COM 		hdr_info->mhi_istagged = B_TRUE;
181710491SRishi.Srivatsavai@Sun.COM 		hdr_info->mhi_tci = ntohs(evhp->ether_tci);
181810491SRishi.Srivatsavai@Sun.COM 		if (striphdr) {
181910491SRishi.Srivatsavai@Sun.COM 			/*
182010491SRishi.Srivatsavai@Sun.COM 			 * For VLAN tagged frames update the ether_type
182110491SRishi.Srivatsavai@Sun.COM 			 * in hdr_info before stripping the header.
182210491SRishi.Srivatsavai@Sun.COM 			 */
182310491SRishi.Srivatsavai@Sun.COM 			ether_type = ntohs(evhp->ether_type);
182410491SRishi.Srivatsavai@Sun.COM 			hdr_info->mhi_origsap = ether_type;
182510491SRishi.Srivatsavai@Sun.COM 			hdr_info->mhi_bindsap = (ether_type > ETHERMTU) ?
182610491SRishi.Srivatsavai@Sun.COM 			    ether_type : DLS_SAP_LLC;
182710491SRishi.Srivatsavai@Sun.COM 			mp->b_rptr = (uchar_t *)(evhp + 1);
182810491SRishi.Srivatsavai@Sun.COM 		}
182910491SRishi.Srivatsavai@Sun.COM 	} else {
183010491SRishi.Srivatsavai@Sun.COM 		hdr_info->mhi_istagged = B_FALSE;
183110491SRishi.Srivatsavai@Sun.COM 		hdr_info->mhi_tci = VLAN_ID_NONE;
183210491SRishi.Srivatsavai@Sun.COM 		if (striphdr)
183310491SRishi.Srivatsavai@Sun.COM 			mp->b_rptr += sizeof (struct ether_header);
183410491SRishi.Srivatsavai@Sun.COM 	}
183510491SRishi.Srivatsavai@Sun.COM }
183610491SRishi.Srivatsavai@Sun.COM 
183710491SRishi.Srivatsavai@Sun.COM /*
183810491SRishi.Srivatsavai@Sun.COM  * Return B_TRUE if we're allowed to send on this link with the given VLAN ID.
183910491SRishi.Srivatsavai@Sun.COM  */
184010491SRishi.Srivatsavai@Sun.COM static boolean_t
bridge_can_send(bridge_link_t * blp,uint16_t vlanid)184110491SRishi.Srivatsavai@Sun.COM bridge_can_send(bridge_link_t *blp, uint16_t vlanid)
184210491SRishi.Srivatsavai@Sun.COM {
184310491SRishi.Srivatsavai@Sun.COM 	ASSERT(vlanid != VLAN_ID_NONE);
184410491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_flags & BLF_DELETED)
184510491SRishi.Srivatsavai@Sun.COM 		return (B_FALSE);
184610491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_trilldata == NULL && blp->bl_state != BLS_FORWARDING)
184710491SRishi.Srivatsavai@Sun.COM 		return (B_FALSE);
184810491SRishi.Srivatsavai@Sun.COM 	return (BRIDGE_VLAN_ISSET(blp, vlanid) && BRIDGE_AF_ISSET(blp, vlanid));
184910491SRishi.Srivatsavai@Sun.COM }
185010491SRishi.Srivatsavai@Sun.COM 
185110491SRishi.Srivatsavai@Sun.COM /*
185210491SRishi.Srivatsavai@Sun.COM  * This function scans the bridge forwarding tables in order to forward a given
185310491SRishi.Srivatsavai@Sun.COM  * packet.  If the packet either doesn't need forwarding (the current link is
185410491SRishi.Srivatsavai@Sun.COM  * correct) or the current link needs a copy as well, then the packet is
185510491SRishi.Srivatsavai@Sun.COM  * returned to the caller.
185610491SRishi.Srivatsavai@Sun.COM  *
185710491SRishi.Srivatsavai@Sun.COM  * If a packet has been decapsulated from TRILL, then it must *NOT* reenter a
185810491SRishi.Srivatsavai@Sun.COM  * TRILL tunnel.  If the destination points there, then drop instead.
185910491SRishi.Srivatsavai@Sun.COM  */
186010491SRishi.Srivatsavai@Sun.COM 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)186110491SRishi.Srivatsavai@Sun.COM bridge_forward(bridge_link_t *blp, mac_header_info_t *hdr_info, mblk_t *mp,
186210491SRishi.Srivatsavai@Sun.COM     uint16_t vlanid, uint16_t tci, boolean_t from_trill, boolean_t is_xmit)
186310491SRishi.Srivatsavai@Sun.COM {
186410491SRishi.Srivatsavai@Sun.COM 	mblk_t *mpsend, *mpcopy;
186510491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;
186610491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blpsend, *blpnext;
186710491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp;
186810491SRishi.Srivatsavai@Sun.COM 	uint_t i;
186910491SRishi.Srivatsavai@Sun.COM 	boolean_t selfseen = B_FALSE;
187010491SRishi.Srivatsavai@Sun.COM 	void *tdp;
187110491SRishi.Srivatsavai@Sun.COM 	const uint8_t *daddr = hdr_info->mhi_daddr;
187210491SRishi.Srivatsavai@Sun.COM 
187310491SRishi.Srivatsavai@Sun.COM 	/*
187410491SRishi.Srivatsavai@Sun.COM 	 * Check for the IEEE "reserved" multicast addresses.  Messages sent to
187510491SRishi.Srivatsavai@Sun.COM 	 * these addresses are used for link-local control (STP and pause), and
187610491SRishi.Srivatsavai@Sun.COM 	 * are never forwarded or redirected.
187710491SRishi.Srivatsavai@Sun.COM 	 */
187810491SRishi.Srivatsavai@Sun.COM 	if (daddr[0] == 1 && daddr[1] == 0x80 && daddr[2] == 0xc2 &&
187910491SRishi.Srivatsavai@Sun.COM 	    daddr[3] == 0 && daddr[4] == 0 && (daddr[5] & 0xf0) == 0) {
188010491SRishi.Srivatsavai@Sun.COM 		if (from_trill) {
188110491SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
188210491SRishi.Srivatsavai@Sun.COM 			mp = NULL;
188310491SRishi.Srivatsavai@Sun.COM 		}
188410491SRishi.Srivatsavai@Sun.COM 		return (mp);
188510491SRishi.Srivatsavai@Sun.COM 	}
188610491SRishi.Srivatsavai@Sun.COM 
188710491SRishi.Srivatsavai@Sun.COM 	if ((bfp = fwd_find(bip, daddr, vlanid)) != NULL) {
188810491SRishi.Srivatsavai@Sun.COM 
188910491SRishi.Srivatsavai@Sun.COM 		/*
189010491SRishi.Srivatsavai@Sun.COM 		 * If trill indicates a destination for this node, then it's
189110491SRishi.Srivatsavai@Sun.COM 		 * clearly not intended for local delivery.  We must tell TRILL
189210491SRishi.Srivatsavai@Sun.COM 		 * to encapsulate, as long as we didn't just decapsulate it.
189310491SRishi.Srivatsavai@Sun.COM 		 */
189410491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_trill_nick != RBRIDGE_NICKNAME_NONE) {
189510491SRishi.Srivatsavai@Sun.COM 			/*
189610491SRishi.Srivatsavai@Sun.COM 			 * Error case: can't reencapsulate if the protocols are
189710491SRishi.Srivatsavai@Sun.COM 			 * working correctly.
189810491SRishi.Srivatsavai@Sun.COM 			 */
189910491SRishi.Srivatsavai@Sun.COM 			if (from_trill) {
190010491SRishi.Srivatsavai@Sun.COM 				freemsg(mp);
190110491SRishi.Srivatsavai@Sun.COM 				return (NULL);
190210491SRishi.Srivatsavai@Sun.COM 			}
190310491SRishi.Srivatsavai@Sun.COM 			mutex_enter(&blp->bl_trilllock);
190410491SRishi.Srivatsavai@Sun.COM 			if ((tdp = blp->bl_trilldata) != NULL) {
190510491SRishi.Srivatsavai@Sun.COM 				blp->bl_trillthreads++;
190610491SRishi.Srivatsavai@Sun.COM 				mutex_exit(&blp->bl_trilllock);
190710491SRishi.Srivatsavai@Sun.COM 				update_header(mp, hdr_info, B_FALSE);
190810491SRishi.Srivatsavai@Sun.COM 				if (is_xmit)
190910491SRishi.Srivatsavai@Sun.COM 					mp = mac_fix_cksum(mp);
191010491SRishi.Srivatsavai@Sun.COM 				/* all trill data frames have Inner.VLAN */
191110491SRishi.Srivatsavai@Sun.COM 				mp = reform_vlan_header(mp, vlanid, tci, 0);
191210491SRishi.Srivatsavai@Sun.COM 				if (mp == NULL) {
191310491SRishi.Srivatsavai@Sun.COM 					KIINCR(bki_drops);
191410491SRishi.Srivatsavai@Sun.COM 					fwd_unref(bfp);
191510491SRishi.Srivatsavai@Sun.COM 					return (NULL);
191610491SRishi.Srivatsavai@Sun.COM 				}
191710491SRishi.Srivatsavai@Sun.COM 				trill_encap_fn(tdp, blp, hdr_info, mp,
191810491SRishi.Srivatsavai@Sun.COM 				    bfp->bf_trill_nick);
191910491SRishi.Srivatsavai@Sun.COM 				mutex_enter(&blp->bl_trilllock);
192010491SRishi.Srivatsavai@Sun.COM 				if (--blp->bl_trillthreads == 0 &&
192110491SRishi.Srivatsavai@Sun.COM 				    blp->bl_trilldata == NULL)
192210491SRishi.Srivatsavai@Sun.COM 					cv_broadcast(&blp->bl_trillwait);
192310491SRishi.Srivatsavai@Sun.COM 			}
192410491SRishi.Srivatsavai@Sun.COM 			mutex_exit(&blp->bl_trilllock);
192510491SRishi.Srivatsavai@Sun.COM 
192610491SRishi.Srivatsavai@Sun.COM 			/* if TRILL has been disabled, then kill this stray */
192710491SRishi.Srivatsavai@Sun.COM 			if (tdp == NULL) {
192810491SRishi.Srivatsavai@Sun.COM 				freemsg(mp);
192910491SRishi.Srivatsavai@Sun.COM 				fwd_delete(bfp);
193010491SRishi.Srivatsavai@Sun.COM 			}
193110491SRishi.Srivatsavai@Sun.COM 			fwd_unref(bfp);
193210491SRishi.Srivatsavai@Sun.COM 			return (NULL);
193310491SRishi.Srivatsavai@Sun.COM 		}
193410491SRishi.Srivatsavai@Sun.COM 
193510491SRishi.Srivatsavai@Sun.COM 		/* find first link we can send on */
193610491SRishi.Srivatsavai@Sun.COM 		for (i = 0; i < bfp->bf_nlinks; i++) {
193710491SRishi.Srivatsavai@Sun.COM 			blpsend = bfp->bf_links[i];
193810491SRishi.Srivatsavai@Sun.COM 			if (blpsend == blp)
193910491SRishi.Srivatsavai@Sun.COM 				selfseen = B_TRUE;
194010491SRishi.Srivatsavai@Sun.COM 			else if (bridge_can_send(blpsend, vlanid))
194110491SRishi.Srivatsavai@Sun.COM 				break;
194210491SRishi.Srivatsavai@Sun.COM 		}
194310491SRishi.Srivatsavai@Sun.COM 
194410491SRishi.Srivatsavai@Sun.COM 		while (i < bfp->bf_nlinks) {
194510491SRishi.Srivatsavai@Sun.COM 			blpsend = bfp->bf_links[i];
194610491SRishi.Srivatsavai@Sun.COM 			for (i++; i < bfp->bf_nlinks; i++) {
194710491SRishi.Srivatsavai@Sun.COM 				blpnext = bfp->bf_links[i];
194810491SRishi.Srivatsavai@Sun.COM 				if (blpnext == blp)
194910491SRishi.Srivatsavai@Sun.COM 					selfseen = B_TRUE;
195010491SRishi.Srivatsavai@Sun.COM 				else if (bridge_can_send(blpnext, vlanid))
195110491SRishi.Srivatsavai@Sun.COM 					break;
195210491SRishi.Srivatsavai@Sun.COM 			}
195310491SRishi.Srivatsavai@Sun.COM 			if (i == bfp->bf_nlinks && !selfseen) {
195410491SRishi.Srivatsavai@Sun.COM 				mpsend = mp;
195510491SRishi.Srivatsavai@Sun.COM 				mp = NULL;
195610491SRishi.Srivatsavai@Sun.COM 			} else {
195710491SRishi.Srivatsavai@Sun.COM 				mpsend = copymsg(mp);
195810491SRishi.Srivatsavai@Sun.COM 			}
195910491SRishi.Srivatsavai@Sun.COM 
196010491SRishi.Srivatsavai@Sun.COM 			if (!from_trill && is_xmit)
196110491SRishi.Srivatsavai@Sun.COM 				mpsend = mac_fix_cksum(mpsend);
196210491SRishi.Srivatsavai@Sun.COM 
196310491SRishi.Srivatsavai@Sun.COM 			mpsend = reform_vlan_header(mpsend, vlanid, tci,
196410491SRishi.Srivatsavai@Sun.COM 			    blpsend->bl_pvid);
196510491SRishi.Srivatsavai@Sun.COM 			if (mpsend == NULL) {
196610491SRishi.Srivatsavai@Sun.COM 				KIINCR(bki_drops);
196710491SRishi.Srivatsavai@Sun.COM 				continue;
196810491SRishi.Srivatsavai@Sun.COM 			}
196910491SRishi.Srivatsavai@Sun.COM 
197010491SRishi.Srivatsavai@Sun.COM 			KIINCR(bki_forwards);
197110491SRishi.Srivatsavai@Sun.COM 			/*
197210491SRishi.Srivatsavai@Sun.COM 			 * No need to bump up the link reference count, as
197310491SRishi.Srivatsavai@Sun.COM 			 * the forwarding entry itself holds a reference to
197410491SRishi.Srivatsavai@Sun.COM 			 * the link.
197510491SRishi.Srivatsavai@Sun.COM 			 */
197610491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_flags & BFF_LOCALADDR) {
197710491SRishi.Srivatsavai@Sun.COM 				mac_rx_common(blpsend->bl_mh, NULL, mpsend);
197810491SRishi.Srivatsavai@Sun.COM 			} else {
197910491SRishi.Srivatsavai@Sun.COM 				KLPINCR(blpsend, bkl_xmit);
198010491SRishi.Srivatsavai@Sun.COM 				MAC_RING_TX(blpsend->bl_mh, NULL, mpsend,
198110491SRishi.Srivatsavai@Sun.COM 				    mpsend);
198210491SRishi.Srivatsavai@Sun.COM 				freemsg(mpsend);
198310491SRishi.Srivatsavai@Sun.COM 			}
198410491SRishi.Srivatsavai@Sun.COM 		}
198510491SRishi.Srivatsavai@Sun.COM 		/*
198610491SRishi.Srivatsavai@Sun.COM 		 * Handle a special case: if we're transmitting to the original
198710491SRishi.Srivatsavai@Sun.COM 		 * link, then check whether the localaddr flag is set.  If it
198810491SRishi.Srivatsavai@Sun.COM 		 * is, then receive instead.  This doesn't happen with ordinary
198910491SRishi.Srivatsavai@Sun.COM 		 * bridging, but does happen often with TRILL decapsulation.
199010491SRishi.Srivatsavai@Sun.COM 		 */
199110491SRishi.Srivatsavai@Sun.COM 		if (mp != NULL && is_xmit && (bfp->bf_flags & BFF_LOCALADDR)) {
199210491SRishi.Srivatsavai@Sun.COM 			mac_rx_common(blp->bl_mh, NULL, mp);
199310491SRishi.Srivatsavai@Sun.COM 			mp = NULL;
199410491SRishi.Srivatsavai@Sun.COM 		}
199510491SRishi.Srivatsavai@Sun.COM 		fwd_unref(bfp);
199610491SRishi.Srivatsavai@Sun.COM 	} else {
199710491SRishi.Srivatsavai@Sun.COM 		/*
199810491SRishi.Srivatsavai@Sun.COM 		 * TRILL has two cases to handle.  If the packet is off the
199910491SRishi.Srivatsavai@Sun.COM 		 * wire (not from TRILL), then we need to send up into the
200010491SRishi.Srivatsavai@Sun.COM 		 * TRILL module to have the distribution tree computed.  If the
200110491SRishi.Srivatsavai@Sun.COM 		 * packet is from TRILL (decapsulated), then we're part of the
200210491SRishi.Srivatsavai@Sun.COM 		 * distribution tree, and we need to copy the packet on member
200310491SRishi.Srivatsavai@Sun.COM 		 * interfaces.
200410491SRishi.Srivatsavai@Sun.COM 		 *
200510491SRishi.Srivatsavai@Sun.COM 		 * Thus, the from TRILL case is identical to the STP case.
200610491SRishi.Srivatsavai@Sun.COM 		 */
200710491SRishi.Srivatsavai@Sun.COM 		if (!from_trill && blp->bl_trilldata != NULL) {
200810491SRishi.Srivatsavai@Sun.COM 			mutex_enter(&blp->bl_trilllock);
200910491SRishi.Srivatsavai@Sun.COM 			if ((tdp = blp->bl_trilldata) != NULL) {
201010491SRishi.Srivatsavai@Sun.COM 				blp->bl_trillthreads++;
201110491SRishi.Srivatsavai@Sun.COM 				mutex_exit(&blp->bl_trilllock);
201210491SRishi.Srivatsavai@Sun.COM 				if ((mpsend = copymsg(mp)) != NULL) {
201310491SRishi.Srivatsavai@Sun.COM 					update_header(mpsend,
201410491SRishi.Srivatsavai@Sun.COM 					    hdr_info, B_FALSE);
201510491SRishi.Srivatsavai@Sun.COM 					/*
201610491SRishi.Srivatsavai@Sun.COM 					 * all trill data frames have
201710491SRishi.Srivatsavai@Sun.COM 					 * Inner.VLAN
201810491SRishi.Srivatsavai@Sun.COM 					 */
201910491SRishi.Srivatsavai@Sun.COM 					mpsend = reform_vlan_header(mpsend,
202010491SRishi.Srivatsavai@Sun.COM 					    vlanid, tci, 0);
202110491SRishi.Srivatsavai@Sun.COM 					if (mpsend == NULL) {
202210491SRishi.Srivatsavai@Sun.COM 						KIINCR(bki_drops);
202310491SRishi.Srivatsavai@Sun.COM 					} else {
202410491SRishi.Srivatsavai@Sun.COM 						trill_encap_fn(tdp, blp,
202510491SRishi.Srivatsavai@Sun.COM 						    hdr_info, mpsend,
202610491SRishi.Srivatsavai@Sun.COM 						    RBRIDGE_NICKNAME_NONE);
202710491SRishi.Srivatsavai@Sun.COM 					}
202810491SRishi.Srivatsavai@Sun.COM 				}
202910491SRishi.Srivatsavai@Sun.COM 				mutex_enter(&blp->bl_trilllock);
203010491SRishi.Srivatsavai@Sun.COM 				if (--blp->bl_trillthreads == 0 &&
203110491SRishi.Srivatsavai@Sun.COM 				    blp->bl_trilldata == NULL)
203210491SRishi.Srivatsavai@Sun.COM 					cv_broadcast(&blp->bl_trillwait);
203310491SRishi.Srivatsavai@Sun.COM 			}
203410491SRishi.Srivatsavai@Sun.COM 			mutex_exit(&blp->bl_trilllock);
203510491SRishi.Srivatsavai@Sun.COM 		}
203610491SRishi.Srivatsavai@Sun.COM 
203710491SRishi.Srivatsavai@Sun.COM 		/*
203810491SRishi.Srivatsavai@Sun.COM 		 * This is an unknown destination, so flood.
203910491SRishi.Srivatsavai@Sun.COM 		 */
204010491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_READER);
204110491SRishi.Srivatsavai@Sun.COM 		for (blpnext = list_head(&bip->bi_links); blpnext != NULL;
204210491SRishi.Srivatsavai@Sun.COM 		    blpnext = list_next(&bip->bi_links, blpnext)) {
204310491SRishi.Srivatsavai@Sun.COM 			if (blpnext == blp)
204410491SRishi.Srivatsavai@Sun.COM 				selfseen = B_TRUE;
204510491SRishi.Srivatsavai@Sun.COM 			else if (bridge_can_send(blpnext, vlanid))
204610491SRishi.Srivatsavai@Sun.COM 				break;
204710491SRishi.Srivatsavai@Sun.COM 		}
204810491SRishi.Srivatsavai@Sun.COM 		if (blpnext != NULL)
204910491SRishi.Srivatsavai@Sun.COM 			atomic_inc_uint(&blpnext->bl_refs);
205010491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
205110491SRishi.Srivatsavai@Sun.COM 		while ((blpsend = blpnext) != NULL) {
205210491SRishi.Srivatsavai@Sun.COM 			rw_enter(&bip->bi_rwlock, RW_READER);
205310491SRishi.Srivatsavai@Sun.COM 			for (blpnext = list_next(&bip->bi_links, blpsend);
205410491SRishi.Srivatsavai@Sun.COM 			    blpnext != NULL;
205510491SRishi.Srivatsavai@Sun.COM 			    blpnext = list_next(&bip->bi_links, blpnext)) {
205610491SRishi.Srivatsavai@Sun.COM 				if (blpnext == blp)
205710491SRishi.Srivatsavai@Sun.COM 					selfseen = B_TRUE;
205810491SRishi.Srivatsavai@Sun.COM 				else if (bridge_can_send(blpnext, vlanid))
205910491SRishi.Srivatsavai@Sun.COM 					break;
206010491SRishi.Srivatsavai@Sun.COM 			}
206110491SRishi.Srivatsavai@Sun.COM 			if (blpnext != NULL)
206210491SRishi.Srivatsavai@Sun.COM 				atomic_inc_uint(&blpnext->bl_refs);
206310491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bip->bi_rwlock);
206410491SRishi.Srivatsavai@Sun.COM 			if (blpnext == NULL && !selfseen) {
206510491SRishi.Srivatsavai@Sun.COM 				mpsend = mp;
206610491SRishi.Srivatsavai@Sun.COM 				mp = NULL;
206710491SRishi.Srivatsavai@Sun.COM 			} else {
206810491SRishi.Srivatsavai@Sun.COM 				mpsend = copymsg(mp);
206910491SRishi.Srivatsavai@Sun.COM 			}
207010491SRishi.Srivatsavai@Sun.COM 
207110491SRishi.Srivatsavai@Sun.COM 			if (!from_trill && is_xmit)
207210491SRishi.Srivatsavai@Sun.COM 				mpsend = mac_fix_cksum(mpsend);
207310491SRishi.Srivatsavai@Sun.COM 
207410491SRishi.Srivatsavai@Sun.COM 			mpsend = reform_vlan_header(mpsend, vlanid, tci,
207510491SRishi.Srivatsavai@Sun.COM 			    blpsend->bl_pvid);
207610491SRishi.Srivatsavai@Sun.COM 			if (mpsend == NULL) {
207710491SRishi.Srivatsavai@Sun.COM 				KIINCR(bki_drops);
207810491SRishi.Srivatsavai@Sun.COM 				continue;
207910491SRishi.Srivatsavai@Sun.COM 			}
208010491SRishi.Srivatsavai@Sun.COM 
208110491SRishi.Srivatsavai@Sun.COM 			if (hdr_info->mhi_dsttype == MAC_ADDRTYPE_UNICAST)
208210491SRishi.Srivatsavai@Sun.COM 				KIINCR(bki_unknown);
208310491SRishi.Srivatsavai@Sun.COM 			else
208410491SRishi.Srivatsavai@Sun.COM 				KIINCR(bki_mbcast);
208510491SRishi.Srivatsavai@Sun.COM 			KLPINCR(blpsend, bkl_xmit);
208610491SRishi.Srivatsavai@Sun.COM 			if ((mpcopy = copymsg(mpsend)) != NULL)
208710491SRishi.Srivatsavai@Sun.COM 				mac_rx_common(blpsend->bl_mh, NULL, mpcopy);
208810491SRishi.Srivatsavai@Sun.COM 			MAC_RING_TX(blpsend->bl_mh, NULL, mpsend, mpsend);
208910491SRishi.Srivatsavai@Sun.COM 			freemsg(mpsend);
209010491SRishi.Srivatsavai@Sun.COM 			link_unref(blpsend);
209110491SRishi.Srivatsavai@Sun.COM 		}
209210491SRishi.Srivatsavai@Sun.COM 	}
209310491SRishi.Srivatsavai@Sun.COM 
209410491SRishi.Srivatsavai@Sun.COM 	/*
209510491SRishi.Srivatsavai@Sun.COM 	 * At this point, if np is non-NULL, it means that the caller needs to
209610491SRishi.Srivatsavai@Sun.COM 	 * continue on the selected link.
209710491SRishi.Srivatsavai@Sun.COM 	 */
209810491SRishi.Srivatsavai@Sun.COM 	return (mp);
209910491SRishi.Srivatsavai@Sun.COM }
210010491SRishi.Srivatsavai@Sun.COM 
210110491SRishi.Srivatsavai@Sun.COM /*
210210491SRishi.Srivatsavai@Sun.COM  * Extract and validate the VLAN information for a given packet.  This checks
210310491SRishi.Srivatsavai@Sun.COM  * conformance with the rules for use of the PVID on the link, and for the
210410491SRishi.Srivatsavai@Sun.COM  * allowed (configured) VLAN set.
210510491SRishi.Srivatsavai@Sun.COM  *
210610491SRishi.Srivatsavai@Sun.COM  * Returns B_TRUE if the packet passes, B_FALSE if it fails.
210710491SRishi.Srivatsavai@Sun.COM  */
210810491SRishi.Srivatsavai@Sun.COM 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)210910491SRishi.Srivatsavai@Sun.COM bridge_get_vlan(bridge_link_t *blp, mac_header_info_t *hdr_info, mblk_t *mp,
211010491SRishi.Srivatsavai@Sun.COM     uint16_t *vlanidp, uint16_t *tcip)
211110491SRishi.Srivatsavai@Sun.COM {
211210491SRishi.Srivatsavai@Sun.COM 	uint16_t tci, vlanid;
211310491SRishi.Srivatsavai@Sun.COM 
211410491SRishi.Srivatsavai@Sun.COM 	if (hdr_info->mhi_bindsap == ETHERTYPE_VLAN) {
211510491SRishi.Srivatsavai@Sun.COM 		ptrdiff_t tpos = offsetof(struct ether_vlan_header, ether_tci);
211610491SRishi.Srivatsavai@Sun.COM 		ptrdiff_t mlen;
211710491SRishi.Srivatsavai@Sun.COM 
211810491SRishi.Srivatsavai@Sun.COM 		/*
211910491SRishi.Srivatsavai@Sun.COM 		 * Extract the VLAN ID information, regardless of alignment,
212010491SRishi.Srivatsavai@Sun.COM 		 * and without a pullup.  This isn't attractive, but we do this
212110491SRishi.Srivatsavai@Sun.COM 		 * to avoid having to deal with the pointers stashed in
212210491SRishi.Srivatsavai@Sun.COM 		 * hdr_info moving around or having the caller deal with a new
212310491SRishi.Srivatsavai@Sun.COM 		 * mblk_t pointer.
212410491SRishi.Srivatsavai@Sun.COM 		 */
212510491SRishi.Srivatsavai@Sun.COM 		while (mp != NULL) {
212610491SRishi.Srivatsavai@Sun.COM 			mlen = MBLKL(mp);
212710491SRishi.Srivatsavai@Sun.COM 			if (mlen > tpos && mlen > 0)
212810491SRishi.Srivatsavai@Sun.COM 				break;
212910491SRishi.Srivatsavai@Sun.COM 			tpos -= mlen;
213010491SRishi.Srivatsavai@Sun.COM 			mp = mp->b_cont;
213110491SRishi.Srivatsavai@Sun.COM 		}
213210491SRishi.Srivatsavai@Sun.COM 		if (mp == NULL)
213310491SRishi.Srivatsavai@Sun.COM 			return (B_FALSE);
213410491SRishi.Srivatsavai@Sun.COM 		tci = mp->b_rptr[tpos] << 8;
213510491SRishi.Srivatsavai@Sun.COM 		if (++tpos >= mlen) {
213610491SRishi.Srivatsavai@Sun.COM 			do {
213710491SRishi.Srivatsavai@Sun.COM 				mp = mp->b_cont;
213810491SRishi.Srivatsavai@Sun.COM 			} while (mp != NULL && MBLKL(mp) == 0);
213910491SRishi.Srivatsavai@Sun.COM 			if (mp == NULL)
214010491SRishi.Srivatsavai@Sun.COM 				return (B_FALSE);
214110491SRishi.Srivatsavai@Sun.COM 			tpos = 0;
214210491SRishi.Srivatsavai@Sun.COM 		}
214310491SRishi.Srivatsavai@Sun.COM 		tci |= mp->b_rptr[tpos];
214410491SRishi.Srivatsavai@Sun.COM 
214510491SRishi.Srivatsavai@Sun.COM 		vlanid = VLAN_ID(tci);
214610491SRishi.Srivatsavai@Sun.COM 		if (VLAN_CFI(tci) != ETHER_CFI || vlanid > VLAN_ID_MAX)
214710491SRishi.Srivatsavai@Sun.COM 			return (B_FALSE);
214810491SRishi.Srivatsavai@Sun.COM 		if (vlanid == VLAN_ID_NONE || vlanid == blp->bl_pvid)
214910491SRishi.Srivatsavai@Sun.COM 			goto input_no_vlan;
215010491SRishi.Srivatsavai@Sun.COM 		if (!BRIDGE_VLAN_ISSET(blp, vlanid))
215110491SRishi.Srivatsavai@Sun.COM 			return (B_FALSE);
215210491SRishi.Srivatsavai@Sun.COM 	} else {
215310491SRishi.Srivatsavai@Sun.COM 		tci = 0xFFFF;
215410491SRishi.Srivatsavai@Sun.COM input_no_vlan:
215510491SRishi.Srivatsavai@Sun.COM 		/*
215610491SRishi.Srivatsavai@Sun.COM 		 * If PVID is set to zero, then untagged traffic is not
215710491SRishi.Srivatsavai@Sun.COM 		 * supported here.  Do not learn or forward.
215810491SRishi.Srivatsavai@Sun.COM 		 */
215910491SRishi.Srivatsavai@Sun.COM 		if ((vlanid = blp->bl_pvid) == VLAN_ID_NONE)
216010491SRishi.Srivatsavai@Sun.COM 			return (B_FALSE);
216110491SRishi.Srivatsavai@Sun.COM 	}
216210491SRishi.Srivatsavai@Sun.COM 
216310491SRishi.Srivatsavai@Sun.COM 	*tcip = tci;
216410491SRishi.Srivatsavai@Sun.COM 	*vlanidp = vlanid;
216510491SRishi.Srivatsavai@Sun.COM 	return (B_TRUE);
216610491SRishi.Srivatsavai@Sun.COM }
216710491SRishi.Srivatsavai@Sun.COM 
216810491SRishi.Srivatsavai@Sun.COM /*
216910491SRishi.Srivatsavai@Sun.COM  * Handle MAC notifications.
217010491SRishi.Srivatsavai@Sun.COM  */
217110491SRishi.Srivatsavai@Sun.COM static void
bridge_notify_cb(void * arg,mac_notify_type_t note_type)217210491SRishi.Srivatsavai@Sun.COM bridge_notify_cb(void *arg, mac_notify_type_t note_type)
217310491SRishi.Srivatsavai@Sun.COM {
217410491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp = arg;
217510491SRishi.Srivatsavai@Sun.COM 
217610491SRishi.Srivatsavai@Sun.COM 	switch (note_type) {
217710491SRishi.Srivatsavai@Sun.COM 	case MAC_NOTE_UNICST:
217810491SRishi.Srivatsavai@Sun.COM 		bridge_new_unicst(blp);
217910491SRishi.Srivatsavai@Sun.COM 		break;
218010491SRishi.Srivatsavai@Sun.COM 
218110491SRishi.Srivatsavai@Sun.COM 	case MAC_NOTE_SDU_SIZE: {
218210491SRishi.Srivatsavai@Sun.COM 		uint_t maxsdu;
218310491SRishi.Srivatsavai@Sun.COM 		bridge_inst_t *bip = blp->bl_inst;
218410491SRishi.Srivatsavai@Sun.COM 		bridge_mac_t *bmp = bip->bi_mac;
218510491SRishi.Srivatsavai@Sun.COM 		boolean_t notify = B_FALSE;
218610491SRishi.Srivatsavai@Sun.COM 		mblk_t *mlist = NULL;
218710491SRishi.Srivatsavai@Sun.COM 
218810491SRishi.Srivatsavai@Sun.COM 		mac_sdu_get(blp->bl_mh, NULL, &maxsdu);
218910491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_READER);
219010491SRishi.Srivatsavai@Sun.COM 		if (list_prev(&bip->bi_links, blp) == NULL &&
219110491SRishi.Srivatsavai@Sun.COM 		    list_next(&bip->bi_links, blp) == NULL) {
219210491SRishi.Srivatsavai@Sun.COM 			notify = (maxsdu != bmp->bm_maxsdu);
219310491SRishi.Srivatsavai@Sun.COM 			bmp->bm_maxsdu = maxsdu;
219410491SRishi.Srivatsavai@Sun.COM 		}
219510491SRishi.Srivatsavai@Sun.COM 		blp->bl_maxsdu = maxsdu;
219610491SRishi.Srivatsavai@Sun.COM 		if (maxsdu != bmp->bm_maxsdu)
219710491SRishi.Srivatsavai@Sun.COM 			link_sdu_fail(blp, B_TRUE, &mlist);
219810491SRishi.Srivatsavai@Sun.COM 		else if (notify)
219910491SRishi.Srivatsavai@Sun.COM 			(void) mac_maxsdu_update(bmp->bm_mh, maxsdu);
220010491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
220110491SRishi.Srivatsavai@Sun.COM 		send_up_messages(bip, mlist);
220210491SRishi.Srivatsavai@Sun.COM 		break;
220310491SRishi.Srivatsavai@Sun.COM 	}
220410491SRishi.Srivatsavai@Sun.COM 	}
220510491SRishi.Srivatsavai@Sun.COM }
220610491SRishi.Srivatsavai@Sun.COM 
220710491SRishi.Srivatsavai@Sun.COM /*
220810491SRishi.Srivatsavai@Sun.COM  * This is called by the MAC layer.  As with the transmit side, we're right in
220910491SRishi.Srivatsavai@Sun.COM  * the data path for all I/O on this port, so if we don't need to forward this
221010491SRishi.Srivatsavai@Sun.COM  * packet anywhere, we have to send it upwards via mac_rx_common.
221110491SRishi.Srivatsavai@Sun.COM  */
221210491SRishi.Srivatsavai@Sun.COM static void
bridge_recv_cb(mac_handle_t mh,mac_resource_handle_t rsrc,mblk_t * mpnext)221310491SRishi.Srivatsavai@Sun.COM bridge_recv_cb(mac_handle_t mh, mac_resource_handle_t rsrc, mblk_t *mpnext)
221410491SRishi.Srivatsavai@Sun.COM {
221510491SRishi.Srivatsavai@Sun.COM 	mblk_t *mp, *mpcopy;
221610491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp = (bridge_link_t *)mh;
221710491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;
221810491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp = bip->bi_mac;
221910491SRishi.Srivatsavai@Sun.COM 	mac_header_info_t hdr_info;
222010491SRishi.Srivatsavai@Sun.COM 	uint16_t vlanid, tci;
222110491SRishi.Srivatsavai@Sun.COM 	boolean_t trillmode = B_FALSE;
222210491SRishi.Srivatsavai@Sun.COM 
222310491SRishi.Srivatsavai@Sun.COM 	KIINCR(bki_recv);
222410491SRishi.Srivatsavai@Sun.COM 	KLINCR(bkl_recv);
222510491SRishi.Srivatsavai@Sun.COM 
222610491SRishi.Srivatsavai@Sun.COM 	/*
222710491SRishi.Srivatsavai@Sun.COM 	 * Regardless of state, check for inbound TRILL packets when TRILL is
222810491SRishi.Srivatsavai@Sun.COM 	 * active.  These are pulled out of band and sent for TRILL handling.
222910491SRishi.Srivatsavai@Sun.COM 	 */
223010491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_trilldata != NULL) {
223110491SRishi.Srivatsavai@Sun.COM 		void *tdp;
223210491SRishi.Srivatsavai@Sun.COM 		mblk_t *newhead;
223310491SRishi.Srivatsavai@Sun.COM 		mblk_t *tail = NULL;
223410491SRishi.Srivatsavai@Sun.COM 
223510491SRishi.Srivatsavai@Sun.COM 		mutex_enter(&blp->bl_trilllock);
223610491SRishi.Srivatsavai@Sun.COM 		if ((tdp = blp->bl_trilldata) != NULL) {
223710491SRishi.Srivatsavai@Sun.COM 			blp->bl_trillthreads++;
223810491SRishi.Srivatsavai@Sun.COM 			mutex_exit(&blp->bl_trilllock);
223910491SRishi.Srivatsavai@Sun.COM 			trillmode = B_TRUE;
224010491SRishi.Srivatsavai@Sun.COM 			newhead = mpnext;
224110491SRishi.Srivatsavai@Sun.COM 			while ((mp = mpnext) != NULL) {
224210491SRishi.Srivatsavai@Sun.COM 				boolean_t raw_isis, bridge_group;
224310491SRishi.Srivatsavai@Sun.COM 
224410491SRishi.Srivatsavai@Sun.COM 				mpnext = mp->b_next;
224510491SRishi.Srivatsavai@Sun.COM 
224610491SRishi.Srivatsavai@Sun.COM 				/*
224710491SRishi.Srivatsavai@Sun.COM 				 * If the header isn't readable, then leave on
224810491SRishi.Srivatsavai@Sun.COM 				 * the list and continue.
224910491SRishi.Srivatsavai@Sun.COM 				 */
225010491SRishi.Srivatsavai@Sun.COM 				if (mac_header_info(blp->bl_mh, mp,
225110491SRishi.Srivatsavai@Sun.COM 				    &hdr_info) != 0) {
225210491SRishi.Srivatsavai@Sun.COM 					tail = mp;
225310491SRishi.Srivatsavai@Sun.COM 					continue;
225410491SRishi.Srivatsavai@Sun.COM 				}
225510491SRishi.Srivatsavai@Sun.COM 
225610491SRishi.Srivatsavai@Sun.COM 				/*
225710491SRishi.Srivatsavai@Sun.COM 				 * The TRILL document specifies that, on
225810491SRishi.Srivatsavai@Sun.COM 				 * Ethernet alone, IS-IS packets arrive with
225910491SRishi.Srivatsavai@Sun.COM 				 * LLC rather than Ethertype, and using a
226010491SRishi.Srivatsavai@Sun.COM 				 * specific destination address.  We must check
226110491SRishi.Srivatsavai@Sun.COM 				 * for that here.  Also, we need to give BPDUs
226210491SRishi.Srivatsavai@Sun.COM 				 * to TRILL for processing.
226310491SRishi.Srivatsavai@Sun.COM 				 */
226410491SRishi.Srivatsavai@Sun.COM 				raw_isis = bridge_group = B_FALSE;
226510491SRishi.Srivatsavai@Sun.COM 				if (hdr_info.mhi_dsttype ==
226610491SRishi.Srivatsavai@Sun.COM 				    MAC_ADDRTYPE_MULTICAST) {
226710491SRishi.Srivatsavai@Sun.COM 					if (memcmp(hdr_info.mhi_daddr,
226810491SRishi.Srivatsavai@Sun.COM 					    all_isis_rbridges, ETHERADDRL) == 0)
226910491SRishi.Srivatsavai@Sun.COM 						raw_isis = B_TRUE;
227010491SRishi.Srivatsavai@Sun.COM 					else if (memcmp(hdr_info.mhi_daddr,
227110491SRishi.Srivatsavai@Sun.COM 					    bridge_group_address, ETHERADDRL) ==
227210491SRishi.Srivatsavai@Sun.COM 					    0)
227310491SRishi.Srivatsavai@Sun.COM 						bridge_group = B_TRUE;
227410491SRishi.Srivatsavai@Sun.COM 				}
227510491SRishi.Srivatsavai@Sun.COM 				if (!raw_isis && !bridge_group &&
227610491SRishi.Srivatsavai@Sun.COM 				    hdr_info.mhi_bindsap != ETHERTYPE_TRILL &&
227710491SRishi.Srivatsavai@Sun.COM 				    (hdr_info.mhi_bindsap != ETHERTYPE_VLAN ||
227810491SRishi.Srivatsavai@Sun.COM 				    /* LINTED: alignment */
227910491SRishi.Srivatsavai@Sun.COM 				    ((struct ether_vlan_header *)mp->b_rptr)->
228010491SRishi.Srivatsavai@Sun.COM 				    ether_type != htons(ETHERTYPE_TRILL))) {
228110491SRishi.Srivatsavai@Sun.COM 					tail = mp;
228210491SRishi.Srivatsavai@Sun.COM 					continue;
228310491SRishi.Srivatsavai@Sun.COM 				}
228410491SRishi.Srivatsavai@Sun.COM 
228510491SRishi.Srivatsavai@Sun.COM 				/*
228610491SRishi.Srivatsavai@Sun.COM 				 * We've got TRILL input.  Remove from the list
228710491SRishi.Srivatsavai@Sun.COM 				 * and send up through the TRILL module.  (Send
228810491SRishi.Srivatsavai@Sun.COM 				 * a copy through promiscuous receive just to
228910491SRishi.Srivatsavai@Sun.COM 				 * support snooping on TRILL.  Order isn't
229010491SRishi.Srivatsavai@Sun.COM 				 * preserved strictly, but that doesn't matter
229110491SRishi.Srivatsavai@Sun.COM 				 * here.)
229210491SRishi.Srivatsavai@Sun.COM 				 */
229310491SRishi.Srivatsavai@Sun.COM 				if (tail != NULL)
229410491SRishi.Srivatsavai@Sun.COM 					tail->b_next = mpnext;
229510491SRishi.Srivatsavai@Sun.COM 				mp->b_next = NULL;
229610491SRishi.Srivatsavai@Sun.COM 				if (mp == newhead)
229710491SRishi.Srivatsavai@Sun.COM 					newhead = mpnext;
229810491SRishi.Srivatsavai@Sun.COM 				mac_trill_snoop(blp->bl_mh, mp);
229910491SRishi.Srivatsavai@Sun.COM 				update_header(mp, &hdr_info, B_TRUE);
230010491SRishi.Srivatsavai@Sun.COM 				/*
230110491SRishi.Srivatsavai@Sun.COM 				 * On raw IS-IS and BPDU frames, we have to
230210491SRishi.Srivatsavai@Sun.COM 				 * make sure that the length is trimmed
230310491SRishi.Srivatsavai@Sun.COM 				 * properly.  We use origsap in order to cope
230410491SRishi.Srivatsavai@Sun.COM 				 * with jumbograms for IS-IS.  (Regular mac
230510491SRishi.Srivatsavai@Sun.COM 				 * can't.)
230610491SRishi.Srivatsavai@Sun.COM 				 */
230710491SRishi.Srivatsavai@Sun.COM 				if (raw_isis || bridge_group) {
230810491SRishi.Srivatsavai@Sun.COM 					size_t msglen = msgdsize(mp);
230910491SRishi.Srivatsavai@Sun.COM 
231010491SRishi.Srivatsavai@Sun.COM 					if (msglen > hdr_info.mhi_origsap) {
231110491SRishi.Srivatsavai@Sun.COM 						(void) adjmsg(mp,
231210491SRishi.Srivatsavai@Sun.COM 						    hdr_info.mhi_origsap -
231310491SRishi.Srivatsavai@Sun.COM 						    msglen);
231410491SRishi.Srivatsavai@Sun.COM 					} else if (msglen <
231510491SRishi.Srivatsavai@Sun.COM 					    hdr_info.mhi_origsap) {
231610491SRishi.Srivatsavai@Sun.COM 						freemsg(mp);
231710491SRishi.Srivatsavai@Sun.COM 						continue;
231810491SRishi.Srivatsavai@Sun.COM 					}
231910491SRishi.Srivatsavai@Sun.COM 				}
232010491SRishi.Srivatsavai@Sun.COM 				trill_recv_fn(tdp, blp, rsrc, mp, &hdr_info);
232110491SRishi.Srivatsavai@Sun.COM 			}
232210491SRishi.Srivatsavai@Sun.COM 			mpnext = newhead;
232310491SRishi.Srivatsavai@Sun.COM 			mutex_enter(&blp->bl_trilllock);
232410491SRishi.Srivatsavai@Sun.COM 			if (--blp->bl_trillthreads == 0 &&
232510491SRishi.Srivatsavai@Sun.COM 			    blp->bl_trilldata == NULL)
232610491SRishi.Srivatsavai@Sun.COM 				cv_broadcast(&blp->bl_trillwait);
232710491SRishi.Srivatsavai@Sun.COM 		}
232810491SRishi.Srivatsavai@Sun.COM 		mutex_exit(&blp->bl_trilllock);
232910491SRishi.Srivatsavai@Sun.COM 		if (mpnext == NULL)
233010491SRishi.Srivatsavai@Sun.COM 			return;
233110491SRishi.Srivatsavai@Sun.COM 	}
233210491SRishi.Srivatsavai@Sun.COM 
233310491SRishi.Srivatsavai@Sun.COM 	/*
233410491SRishi.Srivatsavai@Sun.COM 	 * If this is a TRILL RBridge, then just check whether this link is
233510491SRishi.Srivatsavai@Sun.COM 	 * used at all for forwarding.  If not, then we're done.
233610491SRishi.Srivatsavai@Sun.COM 	 */
233710491SRishi.Srivatsavai@Sun.COM 	if (trillmode) {
233810491SRishi.Srivatsavai@Sun.COM 		if (!(blp->bl_flags & BLF_TRILLACTIVE) ||
233910491SRishi.Srivatsavai@Sun.COM 		    (blp->bl_flags & BLF_SDUFAIL)) {
234010491SRishi.Srivatsavai@Sun.COM 			mac_rx_common(blp->bl_mh, rsrc, mpnext);
234110491SRishi.Srivatsavai@Sun.COM 			return;
234210491SRishi.Srivatsavai@Sun.COM 		}
234310491SRishi.Srivatsavai@Sun.COM 	} else {
234410491SRishi.Srivatsavai@Sun.COM 		/*
234510491SRishi.Srivatsavai@Sun.COM 		 * For regular (STP) bridges, if we're in blocking or listening
234610491SRishi.Srivatsavai@Sun.COM 		 * state, then do nothing.  We don't learn or forward until
234710491SRishi.Srivatsavai@Sun.COM 		 * told to do so.
234810491SRishi.Srivatsavai@Sun.COM 		 */
234910491SRishi.Srivatsavai@Sun.COM 		if (blp->bl_state == BLS_BLOCKLISTEN) {
235010491SRishi.Srivatsavai@Sun.COM 			mac_rx_common(blp->bl_mh, rsrc, mpnext);
235110491SRishi.Srivatsavai@Sun.COM 			return;
235210491SRishi.Srivatsavai@Sun.COM 		}
235310491SRishi.Srivatsavai@Sun.COM 	}
235410491SRishi.Srivatsavai@Sun.COM 
235510491SRishi.Srivatsavai@Sun.COM 	/*
235610491SRishi.Srivatsavai@Sun.COM 	 * Send a copy of the message chain up to the observability node users.
235710491SRishi.Srivatsavai@Sun.COM 	 * For TRILL, we must obey the VLAN AF rules, so we go packet-by-
235810491SRishi.Srivatsavai@Sun.COM 	 * packet.
235910491SRishi.Srivatsavai@Sun.COM 	 */
236010491SRishi.Srivatsavai@Sun.COM 	if (!trillmode && blp->bl_state == BLS_FORWARDING &&
236110491SRishi.Srivatsavai@Sun.COM 	    (bmp->bm_flags & BMF_STARTED) &&
236210491SRishi.Srivatsavai@Sun.COM 	    (mp = copymsgchain(mpnext)) != NULL) {
236310491SRishi.Srivatsavai@Sun.COM 		mac_rx(bmp->bm_mh, NULL, mp);
236410491SRishi.Srivatsavai@Sun.COM 	}
236510491SRishi.Srivatsavai@Sun.COM 
236610491SRishi.Srivatsavai@Sun.COM 	/*
236710491SRishi.Srivatsavai@Sun.COM 	 * We must be in learning or forwarding state, or using TRILL on a link
236810491SRishi.Srivatsavai@Sun.COM 	 * with one or more VLANs active.  For each packet in the list, process
236910491SRishi.Srivatsavai@Sun.COM 	 * the source address, and then attempt to forward.
237010491SRishi.Srivatsavai@Sun.COM 	 */
237110491SRishi.Srivatsavai@Sun.COM 	while ((mp = mpnext) != NULL) {
237210491SRishi.Srivatsavai@Sun.COM 		mpnext = mp->b_next;
237310491SRishi.Srivatsavai@Sun.COM 		mp->b_next = NULL;
237410491SRishi.Srivatsavai@Sun.COM 
237510491SRishi.Srivatsavai@Sun.COM 		/*
237610491SRishi.Srivatsavai@Sun.COM 		 * If we can't decode the header or if the header specifies a
237710491SRishi.Srivatsavai@Sun.COM 		 * multicast source address (impossible!), then don't bother
237810491SRishi.Srivatsavai@Sun.COM 		 * learning or forwarding, but go ahead and forward up the
237910491SRishi.Srivatsavai@Sun.COM 		 * stack for subsequent processing.
238010491SRishi.Srivatsavai@Sun.COM 		 */
238110491SRishi.Srivatsavai@Sun.COM 		if (mac_header_info(blp->bl_mh, mp, &hdr_info) != 0 ||
238210491SRishi.Srivatsavai@Sun.COM 		    (hdr_info.mhi_saddr[0] & 1) != 0) {
238310491SRishi.Srivatsavai@Sun.COM 			KIINCR(bki_drops);
238410491SRishi.Srivatsavai@Sun.COM 			KLINCR(bkl_drops);
238510491SRishi.Srivatsavai@Sun.COM 			mac_rx_common(blp->bl_mh, rsrc, mp);
238610491SRishi.Srivatsavai@Sun.COM 			continue;
238710491SRishi.Srivatsavai@Sun.COM 		}
238810491SRishi.Srivatsavai@Sun.COM 
238910491SRishi.Srivatsavai@Sun.COM 		/*
239010491SRishi.Srivatsavai@Sun.COM 		 * Extract and validate the VLAN ID for this packet.
239110491SRishi.Srivatsavai@Sun.COM 		 */
239210491SRishi.Srivatsavai@Sun.COM 		if (!bridge_get_vlan(blp, &hdr_info, mp, &vlanid, &tci) ||
239310491SRishi.Srivatsavai@Sun.COM 		    !BRIDGE_AF_ISSET(blp, vlanid)) {
239410491SRishi.Srivatsavai@Sun.COM 			mac_rx_common(blp->bl_mh, rsrc, mp);
239510491SRishi.Srivatsavai@Sun.COM 			continue;
239610491SRishi.Srivatsavai@Sun.COM 		}
239710491SRishi.Srivatsavai@Sun.COM 
239810491SRishi.Srivatsavai@Sun.COM 		if (trillmode) {
239910491SRishi.Srivatsavai@Sun.COM 			/*
240010491SRishi.Srivatsavai@Sun.COM 			 * Special test required by TRILL document: must
240110491SRishi.Srivatsavai@Sun.COM 			 * discard frames with outer address set to ESADI.
240210491SRishi.Srivatsavai@Sun.COM 			 */
240310491SRishi.Srivatsavai@Sun.COM 			if (memcmp(hdr_info.mhi_daddr, all_esadi_rbridges,
240410491SRishi.Srivatsavai@Sun.COM 			    ETHERADDRL) == 0) {
240510491SRishi.Srivatsavai@Sun.COM 				mac_rx_common(blp->bl_mh, rsrc, mp);
240610491SRishi.Srivatsavai@Sun.COM 				continue;
240710491SRishi.Srivatsavai@Sun.COM 			}
240810491SRishi.Srivatsavai@Sun.COM 
240910491SRishi.Srivatsavai@Sun.COM 			/*
241010491SRishi.Srivatsavai@Sun.COM 			 * If we're in TRILL mode, then the call above to get
241110491SRishi.Srivatsavai@Sun.COM 			 * the VLAN ID has also checked that we're the
241210491SRishi.Srivatsavai@Sun.COM 			 * appointed forwarder, so report that we're handling
241310491SRishi.Srivatsavai@Sun.COM 			 * this packet to any observability node users.
241410491SRishi.Srivatsavai@Sun.COM 			 */
241510491SRishi.Srivatsavai@Sun.COM 			if ((bmp->bm_flags & BMF_STARTED) &&
241610491SRishi.Srivatsavai@Sun.COM 			    (mpcopy = copymsg(mp)) != NULL)
241710491SRishi.Srivatsavai@Sun.COM 				mac_rx(bmp->bm_mh, NULL, mpcopy);
241810491SRishi.Srivatsavai@Sun.COM 		}
241910491SRishi.Srivatsavai@Sun.COM 
242010491SRishi.Srivatsavai@Sun.COM 		/*
242110491SRishi.Srivatsavai@Sun.COM 		 * First process the source address and learn from it.  For
242210491SRishi.Srivatsavai@Sun.COM 		 * TRILL, we learn only if we're the appointed forwarder.
242310491SRishi.Srivatsavai@Sun.COM 		 */
242410491SRishi.Srivatsavai@Sun.COM 		bridge_learn(blp, hdr_info.mhi_saddr, RBRIDGE_NICKNAME_NONE,
242510491SRishi.Srivatsavai@Sun.COM 		    vlanid);
242610491SRishi.Srivatsavai@Sun.COM 
242710491SRishi.Srivatsavai@Sun.COM 		/*
242810491SRishi.Srivatsavai@Sun.COM 		 * Now check whether we're forwarding and look up the
242910491SRishi.Srivatsavai@Sun.COM 		 * destination.  If we can forward, do so.
243010491SRishi.Srivatsavai@Sun.COM 		 */
243110491SRishi.Srivatsavai@Sun.COM 		if (trillmode || blp->bl_state == BLS_FORWARDING) {
243210491SRishi.Srivatsavai@Sun.COM 			mp = bridge_forward(blp, &hdr_info, mp, vlanid, tci,
243310491SRishi.Srivatsavai@Sun.COM 			    B_FALSE, B_FALSE);
243410491SRishi.Srivatsavai@Sun.COM 		}
243510491SRishi.Srivatsavai@Sun.COM 		if (mp != NULL)
243610491SRishi.Srivatsavai@Sun.COM 			mac_rx_common(blp->bl_mh, rsrc, mp);
243710491SRishi.Srivatsavai@Sun.COM 	}
243810491SRishi.Srivatsavai@Sun.COM }
243910491SRishi.Srivatsavai@Sun.COM 
244010491SRishi.Srivatsavai@Sun.COM 
244110491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
244210491SRishi.Srivatsavai@Sun.COM static mblk_t *
bridge_xmit_cb(mac_handle_t mh,mac_ring_handle_t rh,mblk_t * mpnext)244310491SRishi.Srivatsavai@Sun.COM bridge_xmit_cb(mac_handle_t mh, mac_ring_handle_t rh, mblk_t *mpnext)
244410491SRishi.Srivatsavai@Sun.COM {
244510491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp = (bridge_link_t *)mh;
244610491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;
244710491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp = bip->bi_mac;
244810491SRishi.Srivatsavai@Sun.COM 	mac_header_info_t hdr_info;
244910491SRishi.Srivatsavai@Sun.COM 	uint16_t vlanid, tci;
245010491SRishi.Srivatsavai@Sun.COM 	mblk_t *mp, *mpcopy;
245110491SRishi.Srivatsavai@Sun.COM 	boolean_t trillmode;
245210491SRishi.Srivatsavai@Sun.COM 
245310491SRishi.Srivatsavai@Sun.COM 	trillmode = blp->bl_trilldata != NULL;
245410491SRishi.Srivatsavai@Sun.COM 
245510491SRishi.Srivatsavai@Sun.COM 	/*
245610491SRishi.Srivatsavai@Sun.COM 	 * If we're using STP and we're in blocking or listening state, or if
245710491SRishi.Srivatsavai@Sun.COM 	 * we're using TRILL and no VLANs are active, then behave as though the
245810491SRishi.Srivatsavai@Sun.COM 	 * bridge isn't here at all, and send on the local link alone.
245910491SRishi.Srivatsavai@Sun.COM 	 */
246010491SRishi.Srivatsavai@Sun.COM 	if ((!trillmode && blp->bl_state == BLS_BLOCKLISTEN) ||
246110491SRishi.Srivatsavai@Sun.COM 	    (trillmode &&
246210491SRishi.Srivatsavai@Sun.COM 	    (!(blp->bl_flags & BLF_TRILLACTIVE) ||
246310491SRishi.Srivatsavai@Sun.COM 	    (blp->bl_flags & BLF_SDUFAIL)))) {
246410491SRishi.Srivatsavai@Sun.COM 		KIINCR(bki_sent);
246510491SRishi.Srivatsavai@Sun.COM 		KLINCR(bkl_xmit);
246610491SRishi.Srivatsavai@Sun.COM 		MAC_RING_TX(blp->bl_mh, rh, mpnext, mp);
246710491SRishi.Srivatsavai@Sun.COM 		return (mp);
246810491SRishi.Srivatsavai@Sun.COM 	}
246910491SRishi.Srivatsavai@Sun.COM 
247010491SRishi.Srivatsavai@Sun.COM 	/*
247110491SRishi.Srivatsavai@Sun.COM 	 * Send a copy of the message up to the observability node users.
247210491SRishi.Srivatsavai@Sun.COM 	 * TRILL needs to check on a packet-by-packet basis.
247310491SRishi.Srivatsavai@Sun.COM 	 */
247410491SRishi.Srivatsavai@Sun.COM 	if (!trillmode && blp->bl_state == BLS_FORWARDING &&
247510491SRishi.Srivatsavai@Sun.COM 	    (bmp->bm_flags & BMF_STARTED) &&
247610491SRishi.Srivatsavai@Sun.COM 	    (mp = copymsgchain(mpnext)) != NULL) {
247710491SRishi.Srivatsavai@Sun.COM 		mac_rx(bmp->bm_mh, NULL, mp);
247810491SRishi.Srivatsavai@Sun.COM 	}
247910491SRishi.Srivatsavai@Sun.COM 
248010491SRishi.Srivatsavai@Sun.COM 	while ((mp = mpnext) != NULL) {
248110491SRishi.Srivatsavai@Sun.COM 		mpnext = mp->b_next;
248210491SRishi.Srivatsavai@Sun.COM 		mp->b_next = NULL;
248310491SRishi.Srivatsavai@Sun.COM 
248410491SRishi.Srivatsavai@Sun.COM 		if (mac_header_info(blp->bl_mh, mp, &hdr_info) != 0) {
248510491SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
248610491SRishi.Srivatsavai@Sun.COM 			continue;
248710491SRishi.Srivatsavai@Sun.COM 		}
248810491SRishi.Srivatsavai@Sun.COM 
248910491SRishi.Srivatsavai@Sun.COM 		/*
249010491SRishi.Srivatsavai@Sun.COM 		 * Extract and validate the VLAN ID for this packet.
249110491SRishi.Srivatsavai@Sun.COM 		 */
249210491SRishi.Srivatsavai@Sun.COM 		if (!bridge_get_vlan(blp, &hdr_info, mp, &vlanid, &tci) ||
249310491SRishi.Srivatsavai@Sun.COM 		    !BRIDGE_AF_ISSET(blp, vlanid)) {
249410491SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
249510491SRishi.Srivatsavai@Sun.COM 			continue;
249610491SRishi.Srivatsavai@Sun.COM 		}
249710491SRishi.Srivatsavai@Sun.COM 
249810491SRishi.Srivatsavai@Sun.COM 		/*
249910491SRishi.Srivatsavai@Sun.COM 		 * If we're using TRILL, then we've now validated that we're
250010491SRishi.Srivatsavai@Sun.COM 		 * the forwarder for this VLAN, so go ahead and let
250110491SRishi.Srivatsavai@Sun.COM 		 * observability node users know about the packet.
250210491SRishi.Srivatsavai@Sun.COM 		 */
250310491SRishi.Srivatsavai@Sun.COM 		if (trillmode && (bmp->bm_flags & BMF_STARTED) &&
250410491SRishi.Srivatsavai@Sun.COM 		    (mpcopy = copymsg(mp)) != NULL) {
250510491SRishi.Srivatsavai@Sun.COM 			mac_rx(bmp->bm_mh, NULL, mpcopy);
250610491SRishi.Srivatsavai@Sun.COM 		}
250710491SRishi.Srivatsavai@Sun.COM 
250810491SRishi.Srivatsavai@Sun.COM 		/*
250910491SRishi.Srivatsavai@Sun.COM 		 * We have to learn from our own transmitted packets, because
251010491SRishi.Srivatsavai@Sun.COM 		 * there may be a Solaris DLPI raw sender (who can specify his
251110491SRishi.Srivatsavai@Sun.COM 		 * own source address) using promiscuous mode for receive.  The
251210491SRishi.Srivatsavai@Sun.COM 		 * mac layer information won't (and can't) tell us everything
251310491SRishi.Srivatsavai@Sun.COM 		 * we need to know.
251410491SRishi.Srivatsavai@Sun.COM 		 */
251510491SRishi.Srivatsavai@Sun.COM 		bridge_learn(blp, hdr_info.mhi_saddr, RBRIDGE_NICKNAME_NONE,
251610491SRishi.Srivatsavai@Sun.COM 		    vlanid);
251710491SRishi.Srivatsavai@Sun.COM 
251810491SRishi.Srivatsavai@Sun.COM 		/* attempt forwarding */
251910491SRishi.Srivatsavai@Sun.COM 		if (trillmode || blp->bl_state == BLS_FORWARDING) {
252010491SRishi.Srivatsavai@Sun.COM 			mp = bridge_forward(blp, &hdr_info, mp, vlanid, tci,
252110491SRishi.Srivatsavai@Sun.COM 			    B_FALSE, B_TRUE);
252210491SRishi.Srivatsavai@Sun.COM 		}
252310491SRishi.Srivatsavai@Sun.COM 		if (mp != NULL) {
252410491SRishi.Srivatsavai@Sun.COM 			MAC_RING_TX(blp->bl_mh, rh, mp, mp);
252510491SRishi.Srivatsavai@Sun.COM 			if (mp == NULL) {
252610491SRishi.Srivatsavai@Sun.COM 				KIINCR(bki_sent);
252710491SRishi.Srivatsavai@Sun.COM 				KLINCR(bkl_xmit);
252810491SRishi.Srivatsavai@Sun.COM 			}
252910491SRishi.Srivatsavai@Sun.COM 		}
253010491SRishi.Srivatsavai@Sun.COM 		/*
253110491SRishi.Srivatsavai@Sun.COM 		 * If we get stuck, then stop.  Don't let the user's output
253210491SRishi.Srivatsavai@Sun.COM 		 * packets get out of order.  (More importantly: don't try to
253310491SRishi.Srivatsavai@Sun.COM 		 * bridge the same packet multiple times if flow control is
253410491SRishi.Srivatsavai@Sun.COM 		 * asserted.)
253510491SRishi.Srivatsavai@Sun.COM 		 */
253610491SRishi.Srivatsavai@Sun.COM 		if (mp != NULL) {
253710491SRishi.Srivatsavai@Sun.COM 			mp->b_next = mpnext;
253810491SRishi.Srivatsavai@Sun.COM 			break;
253910491SRishi.Srivatsavai@Sun.COM 		}
254010491SRishi.Srivatsavai@Sun.COM 	}
254110491SRishi.Srivatsavai@Sun.COM 	return (mp);
254210491SRishi.Srivatsavai@Sun.COM }
254310491SRishi.Srivatsavai@Sun.COM 
254410491SRishi.Srivatsavai@Sun.COM /*
254510491SRishi.Srivatsavai@Sun.COM  * This is called by TRILL when it decapsulates an packet, and we must forward
254610491SRishi.Srivatsavai@Sun.COM  * locally.  On failure, we just drop.
254710491SRishi.Srivatsavai@Sun.COM  *
254810491SRishi.Srivatsavai@Sun.COM  * Note that the ingress_nick reported by TRILL must not represent this local
254910491SRishi.Srivatsavai@Sun.COM  * node.
255010491SRishi.Srivatsavai@Sun.COM  */
255110491SRishi.Srivatsavai@Sun.COM void
bridge_trill_decaps(bridge_link_t * blp,mblk_t * mp,uint16_t ingress_nick)255210491SRishi.Srivatsavai@Sun.COM bridge_trill_decaps(bridge_link_t *blp, mblk_t *mp, uint16_t ingress_nick)
255310491SRishi.Srivatsavai@Sun.COM {
255410491SRishi.Srivatsavai@Sun.COM 	mac_header_info_t hdr_info;
255510491SRishi.Srivatsavai@Sun.COM 	uint16_t vlanid, tci;
255610491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;	/* used by macros */
255710491SRishi.Srivatsavai@Sun.COM 	mblk_t *mpcopy;
255810491SRishi.Srivatsavai@Sun.COM 
255910491SRishi.Srivatsavai@Sun.COM 	if (mac_header_info(blp->bl_mh, mp, &hdr_info) != 0) {
256010491SRishi.Srivatsavai@Sun.COM 		freemsg(mp);
256110491SRishi.Srivatsavai@Sun.COM 		return;
256210491SRishi.Srivatsavai@Sun.COM 	}
256310491SRishi.Srivatsavai@Sun.COM 
256410491SRishi.Srivatsavai@Sun.COM 	/* Extract VLAN ID for this packet. */
256510491SRishi.Srivatsavai@Sun.COM 	if (hdr_info.mhi_bindsap == ETHERTYPE_VLAN) {
256610491SRishi.Srivatsavai@Sun.COM 		struct ether_vlan_header *evhp;
256710491SRishi.Srivatsavai@Sun.COM 
256810491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
256910491SRishi.Srivatsavai@Sun.COM 		evhp = (struct ether_vlan_header *)mp->b_rptr;
257010491SRishi.Srivatsavai@Sun.COM 		tci = ntohs(evhp->ether_tci);
257110491SRishi.Srivatsavai@Sun.COM 		vlanid = VLAN_ID(tci);
257210491SRishi.Srivatsavai@Sun.COM 	} else {
257310491SRishi.Srivatsavai@Sun.COM 		/* Inner VLAN headers are required in TRILL data packets */
257410491SRishi.Srivatsavai@Sun.COM 		DTRACE_PROBE3(bridge__trill__decaps__novlan, bridge_link_t *,
257510491SRishi.Srivatsavai@Sun.COM 		    blp, mblk_t *, mp, uint16_t, ingress_nick);
257610491SRishi.Srivatsavai@Sun.COM 		freemsg(mp);
257710491SRishi.Srivatsavai@Sun.COM 		return;
257810491SRishi.Srivatsavai@Sun.COM 	}
257910491SRishi.Srivatsavai@Sun.COM 
258010491SRishi.Srivatsavai@Sun.COM 	/* Learn the location of this sender in the RBridge network */
258110491SRishi.Srivatsavai@Sun.COM 	bridge_learn(blp, hdr_info.mhi_saddr, ingress_nick, vlanid);
258210491SRishi.Srivatsavai@Sun.COM 
258310491SRishi.Srivatsavai@Sun.COM 	/* attempt forwarding */
258410491SRishi.Srivatsavai@Sun.COM 	mp = bridge_forward(blp, &hdr_info, mp, vlanid, tci, B_TRUE, B_TRUE);
258510491SRishi.Srivatsavai@Sun.COM 	if (mp != NULL) {
258610491SRishi.Srivatsavai@Sun.COM 		if (bridge_can_send(blp, vlanid)) {
258710491SRishi.Srivatsavai@Sun.COM 			/* Deliver a copy locally as well */
258810491SRishi.Srivatsavai@Sun.COM 			if ((mpcopy = copymsg(mp)) != NULL)
258910491SRishi.Srivatsavai@Sun.COM 				mac_rx_common(blp->bl_mh, NULL, mpcopy);
259010491SRishi.Srivatsavai@Sun.COM 			MAC_RING_TX(blp->bl_mh, NULL, mp, mp);
259110491SRishi.Srivatsavai@Sun.COM 		}
259210491SRishi.Srivatsavai@Sun.COM 		if (mp == NULL) {
259310491SRishi.Srivatsavai@Sun.COM 			KIINCR(bki_sent);
259410491SRishi.Srivatsavai@Sun.COM 			KLINCR(bkl_xmit);
259510491SRishi.Srivatsavai@Sun.COM 		} else {
259610491SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
259710491SRishi.Srivatsavai@Sun.COM 		}
259810491SRishi.Srivatsavai@Sun.COM 	}
259910491SRishi.Srivatsavai@Sun.COM }
260010491SRishi.Srivatsavai@Sun.COM 
260110491SRishi.Srivatsavai@Sun.COM /*
260210491SRishi.Srivatsavai@Sun.COM  * This function is used by TRILL _only_ to transmit TRILL-encapsulated
260310491SRishi.Srivatsavai@Sun.COM  * packets.  It sends on a single underlying link and does not bridge.
260410491SRishi.Srivatsavai@Sun.COM  */
260510491SRishi.Srivatsavai@Sun.COM mblk_t *
bridge_trill_output(bridge_link_t * blp,mblk_t * mp)260610491SRishi.Srivatsavai@Sun.COM bridge_trill_output(bridge_link_t *blp, mblk_t *mp)
260710491SRishi.Srivatsavai@Sun.COM {
260810491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;	/* used by macros */
260910491SRishi.Srivatsavai@Sun.COM 
261010491SRishi.Srivatsavai@Sun.COM 	mac_trill_snoop(blp->bl_mh, mp);
261110491SRishi.Srivatsavai@Sun.COM 	MAC_RING_TX(blp->bl_mh, NULL, mp, mp);
261210491SRishi.Srivatsavai@Sun.COM 	if (mp == NULL) {
261310491SRishi.Srivatsavai@Sun.COM 		KIINCR(bki_sent);
261410491SRishi.Srivatsavai@Sun.COM 		KLINCR(bkl_xmit);
261510491SRishi.Srivatsavai@Sun.COM 	}
261610491SRishi.Srivatsavai@Sun.COM 	return (mp);
261710491SRishi.Srivatsavai@Sun.COM }
261810491SRishi.Srivatsavai@Sun.COM 
261910491SRishi.Srivatsavai@Sun.COM /*
262010491SRishi.Srivatsavai@Sun.COM  * Set the "appointed forwarder" flag array for this link.  TRILL controls
262110491SRishi.Srivatsavai@Sun.COM  * forwarding on a VLAN basis.  The "trillactive" flag is an optimization for
262210491SRishi.Srivatsavai@Sun.COM  * the forwarder.
262310491SRishi.Srivatsavai@Sun.COM  */
262410491SRishi.Srivatsavai@Sun.COM void
bridge_trill_setvlans(bridge_link_t * blp,const uint8_t * arr)262510491SRishi.Srivatsavai@Sun.COM bridge_trill_setvlans(bridge_link_t *blp, const uint8_t *arr)
262610491SRishi.Srivatsavai@Sun.COM {
262710491SRishi.Srivatsavai@Sun.COM 	int i;
262810491SRishi.Srivatsavai@Sun.COM 	uint_t newflags = 0;
262910491SRishi.Srivatsavai@Sun.COM 
263010491SRishi.Srivatsavai@Sun.COM 	for (i = 0; i < BRIDGE_VLAN_ARR_SIZE; i++) {
263110491SRishi.Srivatsavai@Sun.COM 		if ((blp->bl_afs[i] = arr[i]) != 0)
263210491SRishi.Srivatsavai@Sun.COM 			newflags = BLF_TRILLACTIVE;
263310491SRishi.Srivatsavai@Sun.COM 	}
263410491SRishi.Srivatsavai@Sun.COM 	blp->bl_flags = (blp->bl_flags & ~BLF_TRILLACTIVE) | newflags;
263510491SRishi.Srivatsavai@Sun.COM }
263610491SRishi.Srivatsavai@Sun.COM 
263710491SRishi.Srivatsavai@Sun.COM void
bridge_trill_flush(bridge_link_t * blp,uint16_t vlan,boolean_t dotrill)263810491SRishi.Srivatsavai@Sun.COM bridge_trill_flush(bridge_link_t *blp, uint16_t vlan, boolean_t dotrill)
263910491SRishi.Srivatsavai@Sun.COM {
264010491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;
264110491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp, *bfnext;
264210491SRishi.Srivatsavai@Sun.COM 	avl_tree_t fwd_scavenge;
264310491SRishi.Srivatsavai@Sun.COM 	int i;
264410491SRishi.Srivatsavai@Sun.COM 
264510491SRishi.Srivatsavai@Sun.COM 	_NOTE(ARGUNUSED(vlan));
264610491SRishi.Srivatsavai@Sun.COM 
264710491SRishi.Srivatsavai@Sun.COM 	avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t),
264810491SRishi.Srivatsavai@Sun.COM 	    offsetof(bridge_fwd_t, bf_node));
264910491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_WRITER);
265010491SRishi.Srivatsavai@Sun.COM 	bfnext = avl_first(&bip->bi_fwd);
265110491SRishi.Srivatsavai@Sun.COM 	while ((bfp = bfnext) != NULL) {
265210491SRishi.Srivatsavai@Sun.COM 		bfnext = AVL_NEXT(&bip->bi_fwd, bfp);
265310491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_flags & BFF_LOCALADDR)
265410491SRishi.Srivatsavai@Sun.COM 			continue;
265510491SRishi.Srivatsavai@Sun.COM 		if (dotrill) {
265610491SRishi.Srivatsavai@Sun.COM 			/* port doesn't matter if we're flushing TRILL */
265710491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_trill_nick == RBRIDGE_NICKNAME_NONE)
265810491SRishi.Srivatsavai@Sun.COM 				continue;
265910491SRishi.Srivatsavai@Sun.COM 		} else {
266010491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_trill_nick != RBRIDGE_NICKNAME_NONE)
266110491SRishi.Srivatsavai@Sun.COM 				continue;
266210491SRishi.Srivatsavai@Sun.COM 			for (i = 0; i < bfp->bf_nlinks; i++) {
266310491SRishi.Srivatsavai@Sun.COM 				if (bfp->bf_links[i] == blp)
266410491SRishi.Srivatsavai@Sun.COM 					break;
266510491SRishi.Srivatsavai@Sun.COM 			}
266610491SRishi.Srivatsavai@Sun.COM 			if (i >= bfp->bf_nlinks)
266710491SRishi.Srivatsavai@Sun.COM 				continue;
266810491SRishi.Srivatsavai@Sun.COM 		}
266910491SRishi.Srivatsavai@Sun.COM 		ASSERT(bfp->bf_flags & BFF_INTREE);
267010491SRishi.Srivatsavai@Sun.COM 		avl_remove(&bip->bi_fwd, bfp);
267110491SRishi.Srivatsavai@Sun.COM 		bfp->bf_flags &= ~BFF_INTREE;
267210491SRishi.Srivatsavai@Sun.COM 		avl_add(&fwd_scavenge, bfp);
267310491SRishi.Srivatsavai@Sun.COM 	}
267410491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
267510491SRishi.Srivatsavai@Sun.COM 	bfnext = avl_first(&fwd_scavenge);
267610491SRishi.Srivatsavai@Sun.COM 	while ((bfp = bfnext) != NULL) {
267710491SRishi.Srivatsavai@Sun.COM 		bfnext = AVL_NEXT(&fwd_scavenge, bfp);
267810491SRishi.Srivatsavai@Sun.COM 		avl_remove(&fwd_scavenge, bfp);
267910491SRishi.Srivatsavai@Sun.COM 		fwd_unref(bfp);
268010491SRishi.Srivatsavai@Sun.COM 	}
268110491SRishi.Srivatsavai@Sun.COM 	avl_destroy(&fwd_scavenge);
268210491SRishi.Srivatsavai@Sun.COM }
268310491SRishi.Srivatsavai@Sun.COM 
268410491SRishi.Srivatsavai@Sun.COM /*
268510491SRishi.Srivatsavai@Sun.COM  * Let the mac module take or drop a reference to a bridge link.  When this is
268610491SRishi.Srivatsavai@Sun.COM  * called, the mac module is holding the mi_bridge_lock, so the link cannot be
268710491SRishi.Srivatsavai@Sun.COM  * in the process of entering or leaving a bridge.
268810491SRishi.Srivatsavai@Sun.COM  */
268910491SRishi.Srivatsavai@Sun.COM static void
bridge_ref_cb(mac_handle_t mh,boolean_t hold)269010491SRishi.Srivatsavai@Sun.COM bridge_ref_cb(mac_handle_t mh, boolean_t hold)
269110491SRishi.Srivatsavai@Sun.COM {
269210491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp = (bridge_link_t *)mh;
269310491SRishi.Srivatsavai@Sun.COM 
269410491SRishi.Srivatsavai@Sun.COM 	if (hold)
269510491SRishi.Srivatsavai@Sun.COM 		atomic_inc_uint(&blp->bl_refs);
269610491SRishi.Srivatsavai@Sun.COM 	else
269710491SRishi.Srivatsavai@Sun.COM 		link_unref(blp);
269810491SRishi.Srivatsavai@Sun.COM }
269910491SRishi.Srivatsavai@Sun.COM 
270010491SRishi.Srivatsavai@Sun.COM /*
270110491SRishi.Srivatsavai@Sun.COM  * Handle link state changes reported by the mac layer.  This acts as a filter
270210491SRishi.Srivatsavai@Sun.COM  * for link state changes: if a link is reporting down, but there are other
270310491SRishi.Srivatsavai@Sun.COM  * links still up on the bridge, then the state is changed to "up."  When the
270410491SRishi.Srivatsavai@Sun.COM  * last link goes down, all are marked down, and when the first link goes up,
270510491SRishi.Srivatsavai@Sun.COM  * all are marked up.  (Recursion is avoided by the use of the "redo" function.)
270610491SRishi.Srivatsavai@Sun.COM  *
270710491SRishi.Srivatsavai@Sun.COM  * We treat unknown as equivalent to "up."
270810491SRishi.Srivatsavai@Sun.COM  */
270910491SRishi.Srivatsavai@Sun.COM static link_state_t
bridge_ls_cb(mac_handle_t mh,link_state_t newls)271010491SRishi.Srivatsavai@Sun.COM bridge_ls_cb(mac_handle_t mh, link_state_t newls)
271110491SRishi.Srivatsavai@Sun.COM {
271210491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp = (bridge_link_t *)mh;
271310491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blcmp;
271410491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
271510491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp;
271610491SRishi.Srivatsavai@Sun.COM 
271710491SRishi.Srivatsavai@Sun.COM 	if (newls != LINK_STATE_DOWN && blp->bl_linkstate != LINK_STATE_DOWN ||
271810491SRishi.Srivatsavai@Sun.COM 	    (blp->bl_flags & (BLF_DELETED|BLF_SDUFAIL))) {
271910491SRishi.Srivatsavai@Sun.COM 		blp->bl_linkstate = newls;
272010491SRishi.Srivatsavai@Sun.COM 		return (newls);
272110491SRishi.Srivatsavai@Sun.COM 	}
272210491SRishi.Srivatsavai@Sun.COM 
272310491SRishi.Srivatsavai@Sun.COM 	/*
272410491SRishi.Srivatsavai@Sun.COM 	 * Scan first to see if there are any other non-down links.  If there
272510491SRishi.Srivatsavai@Sun.COM 	 * are, then we're done.  Otherwise, if all others are down, then the
272610491SRishi.Srivatsavai@Sun.COM 	 * state of this link is the state of the bridge.
272710491SRishi.Srivatsavai@Sun.COM 	 */
272810491SRishi.Srivatsavai@Sun.COM 	bip = blp->bl_inst;
272910491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_WRITER);
273010491SRishi.Srivatsavai@Sun.COM 	for (blcmp = list_head(&bip->bi_links); blcmp != NULL;
273110491SRishi.Srivatsavai@Sun.COM 	    blcmp = list_next(&bip->bi_links, blcmp)) {
273210491SRishi.Srivatsavai@Sun.COM 		if (blcmp != blp &&
273310491SRishi.Srivatsavai@Sun.COM 		    !(blcmp->bl_flags & (BLF_DELETED|BLF_SDUFAIL)) &&
273410491SRishi.Srivatsavai@Sun.COM 		    blcmp->bl_linkstate != LINK_STATE_DOWN)
273510491SRishi.Srivatsavai@Sun.COM 			break;
273610491SRishi.Srivatsavai@Sun.COM 	}
273710491SRishi.Srivatsavai@Sun.COM 
273810491SRishi.Srivatsavai@Sun.COM 	if (blcmp != NULL) {
273910491SRishi.Srivatsavai@Sun.COM 		/*
274010491SRishi.Srivatsavai@Sun.COM 		 * If there are other links that are considered up, then tell
274110491SRishi.Srivatsavai@Sun.COM 		 * the caller that the link is actually still up, regardless of
274210491SRishi.Srivatsavai@Sun.COM 		 * this link's underlying state.
274310491SRishi.Srivatsavai@Sun.COM 		 */
274410491SRishi.Srivatsavai@Sun.COM 		blp->bl_linkstate = newls;
274510491SRishi.Srivatsavai@Sun.COM 		newls = LINK_STATE_UP;
274610491SRishi.Srivatsavai@Sun.COM 	} else if (blp->bl_linkstate != newls) {
274710491SRishi.Srivatsavai@Sun.COM 		/*
274810491SRishi.Srivatsavai@Sun.COM 		 * If we've found no other 'up' links, and this link has
274910491SRishi.Srivatsavai@Sun.COM 		 * changed state, then report the new state of the bridge to
275010491SRishi.Srivatsavai@Sun.COM 		 * all other clients.
275110491SRishi.Srivatsavai@Sun.COM 		 */
275210491SRishi.Srivatsavai@Sun.COM 		blp->bl_linkstate = newls;
275310491SRishi.Srivatsavai@Sun.COM 		for (blcmp = list_head(&bip->bi_links); blcmp != NULL;
275410491SRishi.Srivatsavai@Sun.COM 		    blcmp = list_next(&bip->bi_links, blcmp)) {
275510491SRishi.Srivatsavai@Sun.COM 			if (blcmp != blp && !(blcmp->bl_flags & BLF_DELETED))
275610491SRishi.Srivatsavai@Sun.COM 				mac_link_redo(blcmp->bl_mh, newls);
275710491SRishi.Srivatsavai@Sun.COM 		}
275810491SRishi.Srivatsavai@Sun.COM 		bmp = bip->bi_mac;
275910491SRishi.Srivatsavai@Sun.COM 		if ((bmp->bm_linkstate = newls) != LINK_STATE_DOWN)
276010491SRishi.Srivatsavai@Sun.COM 			bmp->bm_linkstate = LINK_STATE_UP;
276110491SRishi.Srivatsavai@Sun.COM 		mac_link_redo(bmp->bm_mh, bmp->bm_linkstate);
276210491SRishi.Srivatsavai@Sun.COM 	}
276310491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
276410491SRishi.Srivatsavai@Sun.COM 	return (newls);
276510491SRishi.Srivatsavai@Sun.COM }
276610491SRishi.Srivatsavai@Sun.COM 
276710491SRishi.Srivatsavai@Sun.COM static void
bridge_add_link(void * arg)276810491SRishi.Srivatsavai@Sun.COM bridge_add_link(void *arg)
276910491SRishi.Srivatsavai@Sun.COM {
277010491SRishi.Srivatsavai@Sun.COM 	mblk_t *mp = arg;
277110491SRishi.Srivatsavai@Sun.COM 	bridge_stream_t *bsp;
277210491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip, *bipt;
277310491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp;
277410491SRishi.Srivatsavai@Sun.COM 	datalink_id_t linkid;
277510491SRishi.Srivatsavai@Sun.COM 	int err;
277610491SRishi.Srivatsavai@Sun.COM 	mac_handle_t mh;
277710491SRishi.Srivatsavai@Sun.COM 	uint_t maxsdu;
277810491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp = NULL, *blpt;
277910491SRishi.Srivatsavai@Sun.COM 	const mac_info_t *mip;
278010491SRishi.Srivatsavai@Sun.COM 	boolean_t macopen = B_FALSE;
278110491SRishi.Srivatsavai@Sun.COM 	char linkname[MAXLINKNAMELEN];
278210491SRishi.Srivatsavai@Sun.COM 	char kstatname[KSTAT_STRLEN];
278310491SRishi.Srivatsavai@Sun.COM 	int i;
278410491SRishi.Srivatsavai@Sun.COM 	link_state_t linkstate;
278510491SRishi.Srivatsavai@Sun.COM 	mblk_t *mlist;
278610491SRishi.Srivatsavai@Sun.COM 
278710491SRishi.Srivatsavai@Sun.COM 	bsp = (bridge_stream_t *)mp->b_next;
278810491SRishi.Srivatsavai@Sun.COM 	mp->b_next = NULL;
278910491SRishi.Srivatsavai@Sun.COM 	bip = bsp->bs_inst;
279010491SRishi.Srivatsavai@Sun.COM 	/* LINTED: alignment */
279110491SRishi.Srivatsavai@Sun.COM 	linkid = *(datalink_id_t *)mp->b_cont->b_rptr;
279210491SRishi.Srivatsavai@Sun.COM 
279310491SRishi.Srivatsavai@Sun.COM 	/*
279410491SRishi.Srivatsavai@Sun.COM 	 * First make sure that there is no other bridge that has this link.
279510491SRishi.Srivatsavai@Sun.COM 	 * We don't want to overlap operations from two bridges; the MAC layer
279610491SRishi.Srivatsavai@Sun.COM 	 * supports only one bridge on a given MAC at a time.
279710491SRishi.Srivatsavai@Sun.COM 	 *
279810491SRishi.Srivatsavai@Sun.COM 	 * We rely on the fact that there's just one taskq thread for the
279910491SRishi.Srivatsavai@Sun.COM 	 * bridging module: once we've checked for a duplicate, we can drop the
280010491SRishi.Srivatsavai@Sun.COM 	 * lock, because no other thread could possibly be adding another link
280110491SRishi.Srivatsavai@Sun.COM 	 * until we're done.
280210491SRishi.Srivatsavai@Sun.COM 	 */
280310491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&inst_lock);
280410491SRishi.Srivatsavai@Sun.COM 	for (bipt = list_head(&inst_list); bipt != NULL;
280510491SRishi.Srivatsavai@Sun.COM 	    bipt = list_next(&inst_list, bipt)) {
280610491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bipt->bi_rwlock, RW_READER);
280710491SRishi.Srivatsavai@Sun.COM 		for (blpt = list_head(&bipt->bi_links); blpt != NULL;
280810491SRishi.Srivatsavai@Sun.COM 		    blpt = list_next(&bipt->bi_links, blpt)) {
280910491SRishi.Srivatsavai@Sun.COM 			if (linkid == blpt->bl_linkid)
281010491SRishi.Srivatsavai@Sun.COM 				break;
281110491SRishi.Srivatsavai@Sun.COM 		}
281210491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bipt->bi_rwlock);
281310491SRishi.Srivatsavai@Sun.COM 		if (blpt != NULL)
281410491SRishi.Srivatsavai@Sun.COM 			break;
281510491SRishi.Srivatsavai@Sun.COM 	}
281610491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&inst_lock);
281710491SRishi.Srivatsavai@Sun.COM 	if (bipt != NULL) {
281810491SRishi.Srivatsavai@Sun.COM 		err = EBUSY;
281910491SRishi.Srivatsavai@Sun.COM 		goto fail;
282010491SRishi.Srivatsavai@Sun.COM 	}
282110491SRishi.Srivatsavai@Sun.COM 
282210491SRishi.Srivatsavai@Sun.COM 	if ((err = mac_open_by_linkid(linkid, &mh)) != 0)
282310491SRishi.Srivatsavai@Sun.COM 		goto fail;
282410491SRishi.Srivatsavai@Sun.COM 	macopen = B_TRUE;
282510491SRishi.Srivatsavai@Sun.COM 
282610491SRishi.Srivatsavai@Sun.COM 	/* we bridge only Ethernet */
282710491SRishi.Srivatsavai@Sun.COM 	mip = mac_info(mh);
282810491SRishi.Srivatsavai@Sun.COM 	if (mip->mi_media != DL_ETHER) {
282910491SRishi.Srivatsavai@Sun.COM 		err = ENOTSUP;
283010491SRishi.Srivatsavai@Sun.COM 		goto fail;
283110491SRishi.Srivatsavai@Sun.COM 	}
283210491SRishi.Srivatsavai@Sun.COM 
283310491SRishi.Srivatsavai@Sun.COM 	/*
283410491SRishi.Srivatsavai@Sun.COM 	 * Get the current maximum SDU on this interface.  If there are other
283510491SRishi.Srivatsavai@Sun.COM 	 * links on the bridge, then this one must match, or it errors out.
283610491SRishi.Srivatsavai@Sun.COM 	 * Otherwise, the first link becomes the standard for the new bridge.
283710491SRishi.Srivatsavai@Sun.COM 	 */
283810491SRishi.Srivatsavai@Sun.COM 	mac_sdu_get(mh, NULL, &maxsdu);
283910491SRishi.Srivatsavai@Sun.COM 	bmp = bip->bi_mac;
284010491SRishi.Srivatsavai@Sun.COM 	if (list_is_empty(&bip->bi_links)) {
284110491SRishi.Srivatsavai@Sun.COM 		bmp->bm_maxsdu = maxsdu;
284210491SRishi.Srivatsavai@Sun.COM 		(void) mac_maxsdu_update(bmp->bm_mh, maxsdu);
284310491SRishi.Srivatsavai@Sun.COM 	}
284410491SRishi.Srivatsavai@Sun.COM 
284510491SRishi.Srivatsavai@Sun.COM 	/* figure the kstat name; also used as the mac client name */
284610491SRishi.Srivatsavai@Sun.COM 	i = MBLKL(mp->b_cont) - sizeof (datalink_id_t);
284710491SRishi.Srivatsavai@Sun.COM 	if (i < 0 || i >= MAXLINKNAMELEN)
284810491SRishi.Srivatsavai@Sun.COM 		i = MAXLINKNAMELEN - 1;
284910491SRishi.Srivatsavai@Sun.COM 	bcopy(mp->b_cont->b_rptr + sizeof (datalink_id_t), linkname, i);
285010491SRishi.Srivatsavai@Sun.COM 	linkname[i] = '\0';
285110491SRishi.Srivatsavai@Sun.COM 	(void) snprintf(kstatname, sizeof (kstatname), "%s-%s", bip->bi_name,
285210491SRishi.Srivatsavai@Sun.COM 	    linkname);
285310491SRishi.Srivatsavai@Sun.COM 
285410491SRishi.Srivatsavai@Sun.COM 	if ((blp = kmem_zalloc(sizeof (*blp), KM_NOSLEEP)) == NULL) {
285510491SRishi.Srivatsavai@Sun.COM 		err = ENOMEM;
285610491SRishi.Srivatsavai@Sun.COM 		goto fail;
285710491SRishi.Srivatsavai@Sun.COM 	}
285810491SRishi.Srivatsavai@Sun.COM 	blp->bl_lfailmp = allocb(sizeof (bridge_ctl_t), BPRI_MED);
285910491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_lfailmp == NULL) {
286010491SRishi.Srivatsavai@Sun.COM 		kmem_free(blp, sizeof (*blp));
286111483SRishi.Srivatsavai@Sun.COM 		blp = NULL;
286210491SRishi.Srivatsavai@Sun.COM 		err = ENOMEM;
286310491SRishi.Srivatsavai@Sun.COM 		goto fail;
286410491SRishi.Srivatsavai@Sun.COM 	}
286510491SRishi.Srivatsavai@Sun.COM 
286611483SRishi.Srivatsavai@Sun.COM 	blp->bl_refs = 1;
286710491SRishi.Srivatsavai@Sun.COM 	atomic_inc_uint(&bip->bi_refs);
286810491SRishi.Srivatsavai@Sun.COM 	blp->bl_inst = bip;
286910491SRishi.Srivatsavai@Sun.COM 	blp->bl_mh = mh;
287010491SRishi.Srivatsavai@Sun.COM 	blp->bl_linkid = linkid;
287110491SRishi.Srivatsavai@Sun.COM 	blp->bl_maxsdu = maxsdu;
287210491SRishi.Srivatsavai@Sun.COM 	cv_init(&blp->bl_trillwait, NULL, CV_DRIVER, NULL);
287310491SRishi.Srivatsavai@Sun.COM 	mutex_init(&blp->bl_trilllock, NULL, MUTEX_DRIVER, NULL);
287410491SRishi.Srivatsavai@Sun.COM 	(void) memset(blp->bl_afs, 0xff, sizeof (blp->bl_afs));
287510491SRishi.Srivatsavai@Sun.COM 
287610491SRishi.Srivatsavai@Sun.COM 	err = mac_client_open(mh, &blp->bl_mch, kstatname, 0);
287710491SRishi.Srivatsavai@Sun.COM 	if (err != 0)
287810491SRishi.Srivatsavai@Sun.COM 		goto fail;
287910491SRishi.Srivatsavai@Sun.COM 	blp->bl_flags |= BLF_CLIENT_OPEN;
288010491SRishi.Srivatsavai@Sun.COM 
288110491SRishi.Srivatsavai@Sun.COM 	err = mac_margin_add(mh, &blp->bl_margin, B_TRUE);
288210491SRishi.Srivatsavai@Sun.COM 	if (err != 0)
288310491SRishi.Srivatsavai@Sun.COM 		goto fail;
288410491SRishi.Srivatsavai@Sun.COM 	blp->bl_flags |= BLF_MARGIN_ADDED;
288510491SRishi.Srivatsavai@Sun.COM 
288610491SRishi.Srivatsavai@Sun.COM 	blp->bl_mnh = mac_notify_add(mh, bridge_notify_cb, blp);
288710491SRishi.Srivatsavai@Sun.COM 
288811483SRishi.Srivatsavai@Sun.COM 	/* Enable Bridging on the link */
288910491SRishi.Srivatsavai@Sun.COM 	err = mac_bridge_set(mh, (mac_handle_t)blp);
289010491SRishi.Srivatsavai@Sun.COM 	if (err != 0)
289110491SRishi.Srivatsavai@Sun.COM 		goto fail;
289210491SRishi.Srivatsavai@Sun.COM 	blp->bl_flags |= BLF_SET_BRIDGE;
289310491SRishi.Srivatsavai@Sun.COM 
289410491SRishi.Srivatsavai@Sun.COM 	err = mac_promisc_add(blp->bl_mch, MAC_CLIENT_PROMISC_ALL, NULL,
289510491SRishi.Srivatsavai@Sun.COM 	    blp, &blp->bl_mphp, MAC_PROMISC_FLAGS_NO_TX_LOOP);
289610491SRishi.Srivatsavai@Sun.COM 	if (err != 0)
289710491SRishi.Srivatsavai@Sun.COM 		goto fail;
289810491SRishi.Srivatsavai@Sun.COM 	blp->bl_flags |= BLF_PROM_ADDED;
289910491SRishi.Srivatsavai@Sun.COM 
290010491SRishi.Srivatsavai@Sun.COM 	bridge_new_unicst(blp);
290110491SRishi.Srivatsavai@Sun.COM 
290210491SRishi.Srivatsavai@Sun.COM 	blp->bl_ksp = kstat_setup((kstat_named_t *)&blp->bl_kstats,
290310491SRishi.Srivatsavai@Sun.COM 	    link_kstats_list, Dim(link_kstats_list), kstatname);
290410491SRishi.Srivatsavai@Sun.COM 
290510491SRishi.Srivatsavai@Sun.COM 	/*
290610491SRishi.Srivatsavai@Sun.COM 	 * The link holds a reference to the bridge instance, so that the
290710491SRishi.Srivatsavai@Sun.COM 	 * instance can't go away before the link is freed.  The insertion into
290811483SRishi.Srivatsavai@Sun.COM 	 * bi_links holds a reference on the link (reference set to 1 above).
290911483SRishi.Srivatsavai@Sun.COM 	 * When marking as removed from bi_links (BLF_DELETED), drop the
291011483SRishi.Srivatsavai@Sun.COM 	 * reference on the link. When freeing the link, drop the reference on
291111483SRishi.Srivatsavai@Sun.COM 	 * the instance. BLF_LINK_ADDED tracks link insertion in bi_links list.
291210491SRishi.Srivatsavai@Sun.COM 	 */
291310491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_WRITER);
291410491SRishi.Srivatsavai@Sun.COM 	list_insert_tail(&bip->bi_links, blp);
291511483SRishi.Srivatsavai@Sun.COM 	blp->bl_flags |= BLF_LINK_ADDED;
291610491SRishi.Srivatsavai@Sun.COM 
291710491SRishi.Srivatsavai@Sun.COM 	/*
291810491SRishi.Srivatsavai@Sun.COM 	 * If the new link is no good on this bridge, then let the daemon know
291910491SRishi.Srivatsavai@Sun.COM 	 * about the problem.
292010491SRishi.Srivatsavai@Sun.COM 	 */
292110491SRishi.Srivatsavai@Sun.COM 	mlist = NULL;
292210491SRishi.Srivatsavai@Sun.COM 	if (maxsdu != bmp->bm_maxsdu)
292310491SRishi.Srivatsavai@Sun.COM 		link_sdu_fail(blp, B_TRUE, &mlist);
292410491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
292510491SRishi.Srivatsavai@Sun.COM 	send_up_messages(bip, mlist);
292610491SRishi.Srivatsavai@Sun.COM 
292710491SRishi.Srivatsavai@Sun.COM 	/*
292810491SRishi.Srivatsavai@Sun.COM 	 * Trigger a link state update so that if this link is the first one
292910491SRishi.Srivatsavai@Sun.COM 	 * "up" in the bridge, then we notify everyone.  This triggers a trip
293010491SRishi.Srivatsavai@Sun.COM 	 * through bridge_ls_cb.
293110491SRishi.Srivatsavai@Sun.COM 	 */
293210491SRishi.Srivatsavai@Sun.COM 	linkstate = mac_stat_get(mh, MAC_STAT_LOWLINK_STATE);
293310491SRishi.Srivatsavai@Sun.COM 	blp->bl_linkstate = LINK_STATE_DOWN;
293410491SRishi.Srivatsavai@Sun.COM 	mac_link_update(mh, linkstate);
293510491SRishi.Srivatsavai@Sun.COM 
293610491SRishi.Srivatsavai@Sun.COM 	/*
293710491SRishi.Srivatsavai@Sun.COM 	 * We now need to report back to the stream that invoked us, and then
293810491SRishi.Srivatsavai@Sun.COM 	 * drop the reference on the stream that we're holding.
293910491SRishi.Srivatsavai@Sun.COM 	 */
294010491SRishi.Srivatsavai@Sun.COM 	miocack(bsp->bs_wq, mp, 0, 0);
294110491SRishi.Srivatsavai@Sun.COM 	stream_unref(bsp);
294210491SRishi.Srivatsavai@Sun.COM 	return;
294310491SRishi.Srivatsavai@Sun.COM 
294410491SRishi.Srivatsavai@Sun.COM fail:
294510491SRishi.Srivatsavai@Sun.COM 	if (blp == NULL) {
294610491SRishi.Srivatsavai@Sun.COM 		if (macopen)
294710491SRishi.Srivatsavai@Sun.COM 			mac_close(mh);
294810491SRishi.Srivatsavai@Sun.COM 	} else {
294910491SRishi.Srivatsavai@Sun.COM 		link_shutdown(blp);
295010491SRishi.Srivatsavai@Sun.COM 	}
295110491SRishi.Srivatsavai@Sun.COM 	miocnak(bsp->bs_wq, mp, 0, err);
295210491SRishi.Srivatsavai@Sun.COM 	stream_unref(bsp);
295310491SRishi.Srivatsavai@Sun.COM }
295410491SRishi.Srivatsavai@Sun.COM 
295510491SRishi.Srivatsavai@Sun.COM static void
bridge_rem_link(void * arg)295610491SRishi.Srivatsavai@Sun.COM bridge_rem_link(void *arg)
295710491SRishi.Srivatsavai@Sun.COM {
295810491SRishi.Srivatsavai@Sun.COM 	mblk_t *mp = arg;
295910491SRishi.Srivatsavai@Sun.COM 	bridge_stream_t *bsp;
296010491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
296110491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp;
296210491SRishi.Srivatsavai@Sun.COM 	datalink_id_t linkid;
296310491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp, *blsave;
296410491SRishi.Srivatsavai@Sun.COM 	boolean_t found;
296510491SRishi.Srivatsavai@Sun.COM 	mblk_t *mlist;
296610491SRishi.Srivatsavai@Sun.COM 
296710491SRishi.Srivatsavai@Sun.COM 	bsp = (bridge_stream_t *)mp->b_next;
296810491SRishi.Srivatsavai@Sun.COM 	mp->b_next = NULL;
296910491SRishi.Srivatsavai@Sun.COM 	bip = bsp->bs_inst;
297010491SRishi.Srivatsavai@Sun.COM 	/* LINTED: alignment */
297110491SRishi.Srivatsavai@Sun.COM 	linkid = *(datalink_id_t *)mp->b_cont->b_rptr;
297210491SRishi.Srivatsavai@Sun.COM 
297310491SRishi.Srivatsavai@Sun.COM 	/*
297410491SRishi.Srivatsavai@Sun.COM 	 * We become reader here so that we can loop over the other links and
297510491SRishi.Srivatsavai@Sun.COM 	 * deliver link up/down notification.
297610491SRishi.Srivatsavai@Sun.COM 	 */
297710491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_READER);
297810491SRishi.Srivatsavai@Sun.COM 	found = B_FALSE;
297910491SRishi.Srivatsavai@Sun.COM 	for (blp = list_head(&bip->bi_links); blp != NULL;
298010491SRishi.Srivatsavai@Sun.COM 	    blp = list_next(&bip->bi_links, blp)) {
298110491SRishi.Srivatsavai@Sun.COM 		if (blp->bl_linkid == linkid &&
298210491SRishi.Srivatsavai@Sun.COM 		    !(blp->bl_flags & BLF_DELETED)) {
298310491SRishi.Srivatsavai@Sun.COM 			blp->bl_flags |= BLF_DELETED;
298410491SRishi.Srivatsavai@Sun.COM 			(void) ddi_taskq_dispatch(bridge_taskq, link_shutdown,
298510491SRishi.Srivatsavai@Sun.COM 			    blp, DDI_SLEEP);
298610491SRishi.Srivatsavai@Sun.COM 			found = B_TRUE;
298710491SRishi.Srivatsavai@Sun.COM 			break;
298810491SRishi.Srivatsavai@Sun.COM 		}
298910491SRishi.Srivatsavai@Sun.COM 	}
299010491SRishi.Srivatsavai@Sun.COM 
299110491SRishi.Srivatsavai@Sun.COM 	/*
299210491SRishi.Srivatsavai@Sun.COM 	 * Check if this link is up and the remainder of the links are all
299310491SRishi.Srivatsavai@Sun.COM 	 * down.
299410491SRishi.Srivatsavai@Sun.COM 	 */
299510491SRishi.Srivatsavai@Sun.COM 	if (blp != NULL && blp->bl_linkstate != LINK_STATE_DOWN) {
299610491SRishi.Srivatsavai@Sun.COM 		for (blp = list_head(&bip->bi_links); blp != NULL;
299710491SRishi.Srivatsavai@Sun.COM 		    blp = list_next(&bip->bi_links, blp)) {
299810491SRishi.Srivatsavai@Sun.COM 			if (blp->bl_linkstate != LINK_STATE_DOWN &&
299910491SRishi.Srivatsavai@Sun.COM 			    !(blp->bl_flags & (BLF_DELETED|BLF_SDUFAIL)))
300010491SRishi.Srivatsavai@Sun.COM 				break;
300110491SRishi.Srivatsavai@Sun.COM 		}
300210491SRishi.Srivatsavai@Sun.COM 		if (blp == NULL) {
300310491SRishi.Srivatsavai@Sun.COM 			for (blp = list_head(&bip->bi_links); blp != NULL;
300410491SRishi.Srivatsavai@Sun.COM 			    blp = list_next(&bip->bi_links, blp)) {
300510491SRishi.Srivatsavai@Sun.COM 				if (!(blp->bl_flags & BLF_DELETED))
300610491SRishi.Srivatsavai@Sun.COM 					mac_link_redo(blp->bl_mh,
300710491SRishi.Srivatsavai@Sun.COM 					    LINK_STATE_DOWN);
300810491SRishi.Srivatsavai@Sun.COM 			}
300910491SRishi.Srivatsavai@Sun.COM 			bmp = bip->bi_mac;
301010491SRishi.Srivatsavai@Sun.COM 			bmp->bm_linkstate = LINK_STATE_DOWN;
301110491SRishi.Srivatsavai@Sun.COM 			mac_link_redo(bmp->bm_mh, LINK_STATE_DOWN);
301210491SRishi.Srivatsavai@Sun.COM 		}
301310491SRishi.Srivatsavai@Sun.COM 	}
301410491SRishi.Srivatsavai@Sun.COM 
301510491SRishi.Srivatsavai@Sun.COM 	/*
301610491SRishi.Srivatsavai@Sun.COM 	 * Check if there's just one working link left on the bridge.  If so,
301710491SRishi.Srivatsavai@Sun.COM 	 * then that link is now authoritative for bridge MTU.
301810491SRishi.Srivatsavai@Sun.COM 	 */
301910491SRishi.Srivatsavai@Sun.COM 	blsave = NULL;
302010491SRishi.Srivatsavai@Sun.COM 	for (blp = list_head(&bip->bi_links); blp != NULL;
302110491SRishi.Srivatsavai@Sun.COM 	    blp = list_next(&bip->bi_links, blp)) {
302210491SRishi.Srivatsavai@Sun.COM 		if (!(blp->bl_flags & BLF_DELETED)) {
302310491SRishi.Srivatsavai@Sun.COM 			if (blsave == NULL)
302410491SRishi.Srivatsavai@Sun.COM 				blsave = blp;
302510491SRishi.Srivatsavai@Sun.COM 			else
302610491SRishi.Srivatsavai@Sun.COM 				break;
302710491SRishi.Srivatsavai@Sun.COM 		}
302810491SRishi.Srivatsavai@Sun.COM 	}
302910491SRishi.Srivatsavai@Sun.COM 	mlist = NULL;
303010491SRishi.Srivatsavai@Sun.COM 	bmp = bip->bi_mac;
303110491SRishi.Srivatsavai@Sun.COM 	if (blsave != NULL && blp == NULL &&
303210491SRishi.Srivatsavai@Sun.COM 	    blsave->bl_maxsdu != bmp->bm_maxsdu) {
303310491SRishi.Srivatsavai@Sun.COM 		bmp->bm_maxsdu = blsave->bl_maxsdu;
303410491SRishi.Srivatsavai@Sun.COM 		(void) mac_maxsdu_update(bmp->bm_mh, blsave->bl_maxsdu);
303510491SRishi.Srivatsavai@Sun.COM 		link_sdu_fail(blsave, B_FALSE, &mlist);
303610491SRishi.Srivatsavai@Sun.COM 	}
303710491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
303810491SRishi.Srivatsavai@Sun.COM 	send_up_messages(bip, mlist);
303910491SRishi.Srivatsavai@Sun.COM 
304010491SRishi.Srivatsavai@Sun.COM 	if (found)
304110491SRishi.Srivatsavai@Sun.COM 		miocack(bsp->bs_wq, mp, 0, 0);
304210491SRishi.Srivatsavai@Sun.COM 	else
304310491SRishi.Srivatsavai@Sun.COM 		miocnak(bsp->bs_wq, mp, 0, ENOENT);
304410491SRishi.Srivatsavai@Sun.COM 	stream_unref(bsp);
304510491SRishi.Srivatsavai@Sun.COM }
304610491SRishi.Srivatsavai@Sun.COM 
304710491SRishi.Srivatsavai@Sun.COM /*
304810491SRishi.Srivatsavai@Sun.COM  * This function intentionally returns with bi_rwlock held; it is intended for
304910491SRishi.Srivatsavai@Sun.COM  * quick checks and updates.
305010491SRishi.Srivatsavai@Sun.COM  */
305110491SRishi.Srivatsavai@Sun.COM static bridge_link_t *
enter_link(bridge_inst_t * bip,datalink_id_t linkid)305210491SRishi.Srivatsavai@Sun.COM enter_link(bridge_inst_t *bip, datalink_id_t linkid)
305310491SRishi.Srivatsavai@Sun.COM {
305410491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp;
305510491SRishi.Srivatsavai@Sun.COM 
305610491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_READER);
305710491SRishi.Srivatsavai@Sun.COM 	for (blp = list_head(&bip->bi_links); blp != NULL;
305810491SRishi.Srivatsavai@Sun.COM 	    blp = list_next(&bip->bi_links, blp)) {
305910491SRishi.Srivatsavai@Sun.COM 		if (blp->bl_linkid == linkid && !(blp->bl_flags & BLF_DELETED))
306010491SRishi.Srivatsavai@Sun.COM 			break;
306110491SRishi.Srivatsavai@Sun.COM 	}
306210491SRishi.Srivatsavai@Sun.COM 	return (blp);
306310491SRishi.Srivatsavai@Sun.COM }
306410491SRishi.Srivatsavai@Sun.COM 
306510491SRishi.Srivatsavai@Sun.COM static void
bridge_ioctl(queue_t * wq,mblk_t * mp)306610491SRishi.Srivatsavai@Sun.COM bridge_ioctl(queue_t *wq, mblk_t *mp)
306710491SRishi.Srivatsavai@Sun.COM {
306810491SRishi.Srivatsavai@Sun.COM 	bridge_stream_t *bsp = wq->q_ptr;
306910491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
307010491SRishi.Srivatsavai@Sun.COM 	struct iocblk *iop;
307110491SRishi.Srivatsavai@Sun.COM 	int rc = EINVAL;
307210491SRishi.Srivatsavai@Sun.COM 	int len = 0;
307310491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp;
307410491SRishi.Srivatsavai@Sun.COM 	cred_t *cr;
307510491SRishi.Srivatsavai@Sun.COM 
307610491SRishi.Srivatsavai@Sun.COM 	/* LINTED: alignment */
307710491SRishi.Srivatsavai@Sun.COM 	iop = (struct iocblk *)mp->b_rptr;
307810491SRishi.Srivatsavai@Sun.COM 
307910491SRishi.Srivatsavai@Sun.COM 	/*
308010491SRishi.Srivatsavai@Sun.COM 	 * For now, all of the bridge ioctls are privileged.
308110491SRishi.Srivatsavai@Sun.COM 	 */
308210491SRishi.Srivatsavai@Sun.COM 	if ((cr = msg_getcred(mp, NULL)) == NULL)
308310491SRishi.Srivatsavai@Sun.COM 		cr = iop->ioc_cr;
308410491SRishi.Srivatsavai@Sun.COM 	if (cr != NULL && secpolicy_net_config(cr, B_FALSE) != 0) {
308510491SRishi.Srivatsavai@Sun.COM 		miocnak(wq, mp, 0, EPERM);
308610491SRishi.Srivatsavai@Sun.COM 		return;
308710491SRishi.Srivatsavai@Sun.COM 	}
308810491SRishi.Srivatsavai@Sun.COM 
308910491SRishi.Srivatsavai@Sun.COM 	switch (iop->ioc_cmd) {
309010491SRishi.Srivatsavai@Sun.COM 	case BRIOC_NEWBRIDGE: {
309110491SRishi.Srivatsavai@Sun.COM 		bridge_newbridge_t *bnb;
309210491SRishi.Srivatsavai@Sun.COM 
309310491SRishi.Srivatsavai@Sun.COM 		if (bsp->bs_inst != NULL ||
309410491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (bridge_newbridge_t))) != 0)
309510491SRishi.Srivatsavai@Sun.COM 			break;
309610491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
309710491SRishi.Srivatsavai@Sun.COM 		bnb = (bridge_newbridge_t *)mp->b_cont->b_rptr;
309810491SRishi.Srivatsavai@Sun.COM 		bnb->bnb_name[MAXNAMELEN-1] = '\0';
309910616SSebastien.Roy@Sun.COM 		rc = bridge_create(bnb->bnb_linkid, bnb->bnb_name, &bip, cr);
310010616SSebastien.Roy@Sun.COM 		if (rc != 0)
310110491SRishi.Srivatsavai@Sun.COM 			break;
310210491SRishi.Srivatsavai@Sun.COM 
310310491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_WRITER);
310410491SRishi.Srivatsavai@Sun.COM 		if (bip->bi_control != NULL) {
310510491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bip->bi_rwlock);
310610491SRishi.Srivatsavai@Sun.COM 			bridge_unref(bip);
310710491SRishi.Srivatsavai@Sun.COM 			rc = EBUSY;
310810491SRishi.Srivatsavai@Sun.COM 		} else {
310910491SRishi.Srivatsavai@Sun.COM 			atomic_inc_uint(&bip->bi_refs);
311010491SRishi.Srivatsavai@Sun.COM 			bsp->bs_inst = bip;	/* stream holds reference */
311110491SRishi.Srivatsavai@Sun.COM 			bip->bi_control = bsp;
311210491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bip->bi_rwlock);
311310491SRishi.Srivatsavai@Sun.COM 			rc = 0;
311410491SRishi.Srivatsavai@Sun.COM 		}
311510491SRishi.Srivatsavai@Sun.COM 		break;
311610491SRishi.Srivatsavai@Sun.COM 	}
311710491SRishi.Srivatsavai@Sun.COM 
311810491SRishi.Srivatsavai@Sun.COM 	case BRIOC_ADDLINK:
311910491SRishi.Srivatsavai@Sun.COM 		if ((bip = bsp->bs_inst) == NULL ||
312010491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (datalink_id_t))) != 0)
312110491SRishi.Srivatsavai@Sun.COM 			break;
312210491SRishi.Srivatsavai@Sun.COM 		/*
312310491SRishi.Srivatsavai@Sun.COM 		 * We cannot perform the action in this thread, because we're
312410491SRishi.Srivatsavai@Sun.COM 		 * not in process context, and we may already be holding
312510491SRishi.Srivatsavai@Sun.COM 		 * MAC-related locks.  Place the request on taskq.
312610491SRishi.Srivatsavai@Sun.COM 		 */
312710491SRishi.Srivatsavai@Sun.COM 		mp->b_next = (mblk_t *)bsp;
312810491SRishi.Srivatsavai@Sun.COM 		stream_ref(bsp);
312910491SRishi.Srivatsavai@Sun.COM 		(void) ddi_taskq_dispatch(bridge_taskq, bridge_add_link, mp,
313010491SRishi.Srivatsavai@Sun.COM 		    DDI_SLEEP);
313110491SRishi.Srivatsavai@Sun.COM 		return;
313210491SRishi.Srivatsavai@Sun.COM 
313310491SRishi.Srivatsavai@Sun.COM 	case BRIOC_REMLINK:
313410491SRishi.Srivatsavai@Sun.COM 		if ((bip = bsp->bs_inst) == NULL ||
313510491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (datalink_id_t))) != 0)
313610491SRishi.Srivatsavai@Sun.COM 			break;
313710491SRishi.Srivatsavai@Sun.COM 		/*
313810491SRishi.Srivatsavai@Sun.COM 		 * We cannot perform the action in this thread, because we're
313910491SRishi.Srivatsavai@Sun.COM 		 * not in process context, and we may already be holding
314010491SRishi.Srivatsavai@Sun.COM 		 * MAC-related locks.  Place the request on taskq.
314110491SRishi.Srivatsavai@Sun.COM 		 */
314210491SRishi.Srivatsavai@Sun.COM 		mp->b_next = (mblk_t *)bsp;
314310491SRishi.Srivatsavai@Sun.COM 		stream_ref(bsp);
314410491SRishi.Srivatsavai@Sun.COM 		(void) ddi_taskq_dispatch(bridge_taskq, bridge_rem_link, mp,
314510491SRishi.Srivatsavai@Sun.COM 		    DDI_SLEEP);
314610491SRishi.Srivatsavai@Sun.COM 		return;
314710491SRishi.Srivatsavai@Sun.COM 
314810491SRishi.Srivatsavai@Sun.COM 	case BRIOC_SETSTATE: {
314910491SRishi.Srivatsavai@Sun.COM 		bridge_setstate_t *bss;
315010491SRishi.Srivatsavai@Sun.COM 
315110491SRishi.Srivatsavai@Sun.COM 		if ((bip = bsp->bs_inst) == NULL ||
315210491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (*bss))) != 0)
315310491SRishi.Srivatsavai@Sun.COM 			break;
315410491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
315510491SRishi.Srivatsavai@Sun.COM 		bss = (bridge_setstate_t *)mp->b_cont->b_rptr;
315610491SRishi.Srivatsavai@Sun.COM 		if ((blp = enter_link(bip, bss->bss_linkid)) == NULL) {
315710491SRishi.Srivatsavai@Sun.COM 			rc = ENOENT;
315810491SRishi.Srivatsavai@Sun.COM 		} else {
315910491SRishi.Srivatsavai@Sun.COM 			rc = 0;
316010491SRishi.Srivatsavai@Sun.COM 			blp->bl_state = bss->bss_state;
316110491SRishi.Srivatsavai@Sun.COM 		}
316210491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
316310491SRishi.Srivatsavai@Sun.COM 		break;
316410491SRishi.Srivatsavai@Sun.COM 	}
316510491SRishi.Srivatsavai@Sun.COM 
316610491SRishi.Srivatsavai@Sun.COM 	case BRIOC_SETPVID: {
316710491SRishi.Srivatsavai@Sun.COM 		bridge_setpvid_t *bsv;
316810491SRishi.Srivatsavai@Sun.COM 
316910491SRishi.Srivatsavai@Sun.COM 		if ((bip = bsp->bs_inst) == NULL ||
317010491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (*bsv))) != 0)
317110491SRishi.Srivatsavai@Sun.COM 			break;
317210491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
317310491SRishi.Srivatsavai@Sun.COM 		bsv = (bridge_setpvid_t *)mp->b_cont->b_rptr;
317410491SRishi.Srivatsavai@Sun.COM 		if (bsv->bsv_vlan > VLAN_ID_MAX)
317510491SRishi.Srivatsavai@Sun.COM 			break;
317610491SRishi.Srivatsavai@Sun.COM 		if ((blp = enter_link(bip, bsv->bsv_linkid)) == NULL) {
317710491SRishi.Srivatsavai@Sun.COM 			rc = ENOENT;
317810491SRishi.Srivatsavai@Sun.COM 		} else if (blp->bl_pvid == bsv->bsv_vlan) {
317910491SRishi.Srivatsavai@Sun.COM 			rc = 0;
318010491SRishi.Srivatsavai@Sun.COM 		} else {
318110491SRishi.Srivatsavai@Sun.COM 			rc = 0;
318210491SRishi.Srivatsavai@Sun.COM 			BRIDGE_VLAN_CLR(blp, blp->bl_pvid);
318310491SRishi.Srivatsavai@Sun.COM 			blp->bl_pvid = bsv->bsv_vlan;
318410491SRishi.Srivatsavai@Sun.COM 			if (blp->bl_pvid != 0)
318510491SRishi.Srivatsavai@Sun.COM 				BRIDGE_VLAN_SET(blp, blp->bl_pvid);
318610491SRishi.Srivatsavai@Sun.COM 		}
318710491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
318810491SRishi.Srivatsavai@Sun.COM 		break;
318910491SRishi.Srivatsavai@Sun.COM 	}
319010491SRishi.Srivatsavai@Sun.COM 
319110491SRishi.Srivatsavai@Sun.COM 	case BRIOC_VLANENAB: {
319210491SRishi.Srivatsavai@Sun.COM 		bridge_vlanenab_t *bve;
319310491SRishi.Srivatsavai@Sun.COM 
319410491SRishi.Srivatsavai@Sun.COM 		if ((bip = bsp->bs_inst) == NULL ||
319510491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (*bve))) != 0)
319610491SRishi.Srivatsavai@Sun.COM 			break;
319710491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
319810491SRishi.Srivatsavai@Sun.COM 		bve = (bridge_vlanenab_t *)mp->b_cont->b_rptr;
319910491SRishi.Srivatsavai@Sun.COM 		if (bve->bve_vlan > VLAN_ID_MAX)
320010491SRishi.Srivatsavai@Sun.COM 			break;
320110491SRishi.Srivatsavai@Sun.COM 		if ((blp = enter_link(bip, bve->bve_linkid)) == NULL) {
320210491SRishi.Srivatsavai@Sun.COM 			rc = ENOENT;
320310491SRishi.Srivatsavai@Sun.COM 		} else {
320410491SRishi.Srivatsavai@Sun.COM 			rc = 0;
320510491SRishi.Srivatsavai@Sun.COM 			/* special case: vlan 0 means "all" */
320610491SRishi.Srivatsavai@Sun.COM 			if (bve->bve_vlan == 0) {
320710491SRishi.Srivatsavai@Sun.COM 				(void) memset(blp->bl_vlans,
320810491SRishi.Srivatsavai@Sun.COM 				    bve->bve_onoff ? ~0 : 0,
320910491SRishi.Srivatsavai@Sun.COM 				    sizeof (blp->bl_vlans));
321010491SRishi.Srivatsavai@Sun.COM 				BRIDGE_VLAN_CLR(blp, 0);
321110491SRishi.Srivatsavai@Sun.COM 				if (blp->bl_pvid != 0)
321210491SRishi.Srivatsavai@Sun.COM 					BRIDGE_VLAN_SET(blp, blp->bl_pvid);
321310491SRishi.Srivatsavai@Sun.COM 			} else if (bve->bve_vlan == blp->bl_pvid) {
321410491SRishi.Srivatsavai@Sun.COM 				rc = EINVAL;
321510491SRishi.Srivatsavai@Sun.COM 			} else if (bve->bve_onoff) {
321610491SRishi.Srivatsavai@Sun.COM 				BRIDGE_VLAN_SET(blp, bve->bve_vlan);
321710491SRishi.Srivatsavai@Sun.COM 			} else {
321810491SRishi.Srivatsavai@Sun.COM 				BRIDGE_VLAN_CLR(blp, bve->bve_vlan);
321910491SRishi.Srivatsavai@Sun.COM 			}
322010491SRishi.Srivatsavai@Sun.COM 		}
322110491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
322210491SRishi.Srivatsavai@Sun.COM 		break;
322310491SRishi.Srivatsavai@Sun.COM 	}
322410491SRishi.Srivatsavai@Sun.COM 
322510491SRishi.Srivatsavai@Sun.COM 	case BRIOC_FLUSHFWD: {
322610491SRishi.Srivatsavai@Sun.COM 		bridge_flushfwd_t *bff;
322710491SRishi.Srivatsavai@Sun.COM 		bridge_fwd_t *bfp, *bfnext;
322810491SRishi.Srivatsavai@Sun.COM 		avl_tree_t fwd_scavenge;
322910491SRishi.Srivatsavai@Sun.COM 		int i;
323010491SRishi.Srivatsavai@Sun.COM 
323110491SRishi.Srivatsavai@Sun.COM 		if ((bip = bsp->bs_inst) == NULL ||
323210491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (*bff))) != 0)
323310491SRishi.Srivatsavai@Sun.COM 			break;
323410491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
323510491SRishi.Srivatsavai@Sun.COM 		bff = (bridge_flushfwd_t *)mp->b_cont->b_rptr;
323610491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_WRITER);
323710491SRishi.Srivatsavai@Sun.COM 		/* This case means "all" */
323810491SRishi.Srivatsavai@Sun.COM 		if (bff->bff_linkid == DATALINK_INVALID_LINKID) {
323910491SRishi.Srivatsavai@Sun.COM 			blp = NULL;
324010491SRishi.Srivatsavai@Sun.COM 		} else {
324110491SRishi.Srivatsavai@Sun.COM 			for (blp = list_head(&bip->bi_links); blp != NULL;
324210491SRishi.Srivatsavai@Sun.COM 			    blp = list_next(&bip->bi_links, blp)) {
324310491SRishi.Srivatsavai@Sun.COM 				if (blp->bl_linkid == bff->bff_linkid &&
324410491SRishi.Srivatsavai@Sun.COM 				    !(blp->bl_flags & BLF_DELETED))
324510491SRishi.Srivatsavai@Sun.COM 					break;
324610491SRishi.Srivatsavai@Sun.COM 			}
324710491SRishi.Srivatsavai@Sun.COM 			if (blp == NULL) {
324810491SRishi.Srivatsavai@Sun.COM 				rc = ENOENT;
324910491SRishi.Srivatsavai@Sun.COM 				rw_exit(&bip->bi_rwlock);
325010491SRishi.Srivatsavai@Sun.COM 				break;
325110491SRishi.Srivatsavai@Sun.COM 			}
325210491SRishi.Srivatsavai@Sun.COM 		}
325310491SRishi.Srivatsavai@Sun.COM 		avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t),
325410491SRishi.Srivatsavai@Sun.COM 		    offsetof(bridge_fwd_t, bf_node));
325510491SRishi.Srivatsavai@Sun.COM 		bfnext = avl_first(&bip->bi_fwd);
325610491SRishi.Srivatsavai@Sun.COM 		while ((bfp = bfnext) != NULL) {
325710491SRishi.Srivatsavai@Sun.COM 			bfnext = AVL_NEXT(&bip->bi_fwd, bfp);
325810491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_flags & BFF_LOCALADDR)
325910491SRishi.Srivatsavai@Sun.COM 				continue;
326010491SRishi.Srivatsavai@Sun.COM 			if (blp != NULL) {
326110491SRishi.Srivatsavai@Sun.COM 				for (i = 0; i < bfp->bf_maxlinks; i++) {
326210491SRishi.Srivatsavai@Sun.COM 					if (bfp->bf_links[i] == blp)
326310491SRishi.Srivatsavai@Sun.COM 						break;
326410491SRishi.Srivatsavai@Sun.COM 				}
326510491SRishi.Srivatsavai@Sun.COM 				/*
326610491SRishi.Srivatsavai@Sun.COM 				 * If the link is there and we're excluding,
326710491SRishi.Srivatsavai@Sun.COM 				 * then skip.  If the link is not there and
326810491SRishi.Srivatsavai@Sun.COM 				 * we're doing only that link, then skip.
326910491SRishi.Srivatsavai@Sun.COM 				 */
327010491SRishi.Srivatsavai@Sun.COM 				if ((i < bfp->bf_maxlinks) == bff->bff_exclude)
327110491SRishi.Srivatsavai@Sun.COM 					continue;
327210491SRishi.Srivatsavai@Sun.COM 			}
327310491SRishi.Srivatsavai@Sun.COM 			ASSERT(bfp->bf_flags & BFF_INTREE);
327410491SRishi.Srivatsavai@Sun.COM 			avl_remove(&bip->bi_fwd, bfp);
327510491SRishi.Srivatsavai@Sun.COM 			bfp->bf_flags &= ~BFF_INTREE;
327610491SRishi.Srivatsavai@Sun.COM 			avl_add(&fwd_scavenge, bfp);
327710491SRishi.Srivatsavai@Sun.COM 		}
327810491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
327910491SRishi.Srivatsavai@Sun.COM 		bfnext = avl_first(&fwd_scavenge);
328010491SRishi.Srivatsavai@Sun.COM 		while ((bfp = bfnext) != NULL) {
328110491SRishi.Srivatsavai@Sun.COM 			bfnext = AVL_NEXT(&fwd_scavenge, bfp);
328210491SRishi.Srivatsavai@Sun.COM 			avl_remove(&fwd_scavenge, bfp);
328310491SRishi.Srivatsavai@Sun.COM 			fwd_unref(bfp);	/* drop tree reference */
328410491SRishi.Srivatsavai@Sun.COM 		}
328510491SRishi.Srivatsavai@Sun.COM 		avl_destroy(&fwd_scavenge);
328610491SRishi.Srivatsavai@Sun.COM 		break;
328710491SRishi.Srivatsavai@Sun.COM 	}
328810491SRishi.Srivatsavai@Sun.COM 
328910491SRishi.Srivatsavai@Sun.COM 	case BRIOC_TABLEMAX:
329010491SRishi.Srivatsavai@Sun.COM 		if ((bip = bsp->bs_inst) == NULL ||
329110491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (uint32_t))) != 0)
329210491SRishi.Srivatsavai@Sun.COM 			break;
329310491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
329410491SRishi.Srivatsavai@Sun.COM 		bip->bi_tablemax = *(uint32_t *)mp->b_cont->b_rptr;
329510491SRishi.Srivatsavai@Sun.COM 		break;
329610491SRishi.Srivatsavai@Sun.COM 	}
329710491SRishi.Srivatsavai@Sun.COM 
329810491SRishi.Srivatsavai@Sun.COM 	if (rc == 0)
329910491SRishi.Srivatsavai@Sun.COM 		miocack(wq, mp, len, 0);
330010491SRishi.Srivatsavai@Sun.COM 	else
330110491SRishi.Srivatsavai@Sun.COM 		miocnak(wq, mp, 0, rc);
330210491SRishi.Srivatsavai@Sun.COM }
330310491SRishi.Srivatsavai@Sun.COM 
330410491SRishi.Srivatsavai@Sun.COM static void
bridge_wput(queue_t * wq,mblk_t * mp)330510491SRishi.Srivatsavai@Sun.COM bridge_wput(queue_t *wq, mblk_t *mp)
330610491SRishi.Srivatsavai@Sun.COM {
330710491SRishi.Srivatsavai@Sun.COM 	switch (DB_TYPE(mp)) {
330810491SRishi.Srivatsavai@Sun.COM 	case M_IOCTL:
330910491SRishi.Srivatsavai@Sun.COM 		bridge_ioctl(wq, mp);
331010491SRishi.Srivatsavai@Sun.COM 		break;
331110491SRishi.Srivatsavai@Sun.COM 	case M_FLUSH:
331210491SRishi.Srivatsavai@Sun.COM 		if (*mp->b_rptr & FLUSHW)
331310491SRishi.Srivatsavai@Sun.COM 			*mp->b_rptr &= ~FLUSHW;
331410491SRishi.Srivatsavai@Sun.COM 		if (*mp->b_rptr & FLUSHR)
331510491SRishi.Srivatsavai@Sun.COM 			qreply(wq, mp);
331610491SRishi.Srivatsavai@Sun.COM 		else
331710491SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
331810491SRishi.Srivatsavai@Sun.COM 		break;
331910491SRishi.Srivatsavai@Sun.COM 	default:
332010491SRishi.Srivatsavai@Sun.COM 		freemsg(mp);
332110491SRishi.Srivatsavai@Sun.COM 		break;
332210491SRishi.Srivatsavai@Sun.COM 	}
332310491SRishi.Srivatsavai@Sun.COM }
332410491SRishi.Srivatsavai@Sun.COM 
332510491SRishi.Srivatsavai@Sun.COM /*
332610491SRishi.Srivatsavai@Sun.COM  * This function allocates the main data structures for the bridge driver and
332710491SRishi.Srivatsavai@Sun.COM  * connects us into devfs.
332810491SRishi.Srivatsavai@Sun.COM  */
332910491SRishi.Srivatsavai@Sun.COM static void
bridge_inst_init(void)333010491SRishi.Srivatsavai@Sun.COM bridge_inst_init(void)
333110491SRishi.Srivatsavai@Sun.COM {
333210491SRishi.Srivatsavai@Sun.COM 	bridge_scan_interval = 5 * drv_usectohz(1000000);
333310491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_age = 25 * drv_usectohz(1000000);
333410491SRishi.Srivatsavai@Sun.COM 
333510491SRishi.Srivatsavai@Sun.COM 	rw_init(&bmac_rwlock, NULL, RW_DRIVER, NULL);
333610491SRishi.Srivatsavai@Sun.COM 	list_create(&bmac_list, sizeof (bridge_mac_t),
333710491SRishi.Srivatsavai@Sun.COM 	    offsetof(bridge_mac_t, bm_node));
333810491SRishi.Srivatsavai@Sun.COM 	list_create(&inst_list, sizeof (bridge_inst_t),
333910491SRishi.Srivatsavai@Sun.COM 	    offsetof(bridge_inst_t, bi_node));
334010491SRishi.Srivatsavai@Sun.COM 	cv_init(&inst_cv, NULL, CV_DRIVER, NULL);
334110491SRishi.Srivatsavai@Sun.COM 	mutex_init(&inst_lock, NULL, MUTEX_DRIVER, NULL);
334210491SRishi.Srivatsavai@Sun.COM 	cv_init(&stream_ref_cv, NULL, CV_DRIVER, NULL);
334310491SRishi.Srivatsavai@Sun.COM 	mutex_init(&stream_ref_lock, NULL, MUTEX_DRIVER, NULL);
334410491SRishi.Srivatsavai@Sun.COM 
334510491SRishi.Srivatsavai@Sun.COM 	mac_bridge_vectors(bridge_xmit_cb, bridge_recv_cb, bridge_ref_cb,
334610491SRishi.Srivatsavai@Sun.COM 	    bridge_ls_cb);
334710491SRishi.Srivatsavai@Sun.COM }
334810491SRishi.Srivatsavai@Sun.COM 
334910491SRishi.Srivatsavai@Sun.COM /*
335010491SRishi.Srivatsavai@Sun.COM  * This function disconnects from devfs and destroys all data structures in
335110491SRishi.Srivatsavai@Sun.COM  * preparation for unload.  It's assumed that there are no active bridge
335210491SRishi.Srivatsavai@Sun.COM  * references left at this point.
335310491SRishi.Srivatsavai@Sun.COM  */
335410491SRishi.Srivatsavai@Sun.COM static void
bridge_inst_fini(void)335510491SRishi.Srivatsavai@Sun.COM bridge_inst_fini(void)
335610491SRishi.Srivatsavai@Sun.COM {
335710491SRishi.Srivatsavai@Sun.COM 	mac_bridge_vectors(NULL, NULL, NULL, NULL);
335810491SRishi.Srivatsavai@Sun.COM 	if (bridge_timerid != 0)
335910491SRishi.Srivatsavai@Sun.COM 		(void) untimeout(bridge_timerid);
336010491SRishi.Srivatsavai@Sun.COM 	rw_destroy(&bmac_rwlock);
336110491SRishi.Srivatsavai@Sun.COM 	list_destroy(&bmac_list);
336210491SRishi.Srivatsavai@Sun.COM 	list_destroy(&inst_list);
336310491SRishi.Srivatsavai@Sun.COM 	cv_destroy(&inst_cv);
336410491SRishi.Srivatsavai@Sun.COM 	mutex_destroy(&inst_lock);
336510491SRishi.Srivatsavai@Sun.COM 	cv_destroy(&stream_ref_cv);
336610491SRishi.Srivatsavai@Sun.COM 	mutex_destroy(&stream_ref_lock);
336710491SRishi.Srivatsavai@Sun.COM }
336810491SRishi.Srivatsavai@Sun.COM 
336910491SRishi.Srivatsavai@Sun.COM /*
337010491SRishi.Srivatsavai@Sun.COM  * bridge_attach()
337110491SRishi.Srivatsavai@Sun.COM  *
337210491SRishi.Srivatsavai@Sun.COM  * Description:
337310491SRishi.Srivatsavai@Sun.COM  *    Attach bridge driver to the system.
337410491SRishi.Srivatsavai@Sun.COM  */
337510491SRishi.Srivatsavai@Sun.COM static int
bridge_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)337610491SRishi.Srivatsavai@Sun.COM bridge_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
337710491SRishi.Srivatsavai@Sun.COM {
337810491SRishi.Srivatsavai@Sun.COM 	if (cmd != DDI_ATTACH)
337910491SRishi.Srivatsavai@Sun.COM 		return (DDI_FAILURE);
338010491SRishi.Srivatsavai@Sun.COM 
338110491SRishi.Srivatsavai@Sun.COM 	if (ddi_create_minor_node(dip, BRIDGE_CTL, S_IFCHR, 0, DDI_PSEUDO,
338210491SRishi.Srivatsavai@Sun.COM 	    CLONE_DEV) == DDI_FAILURE) {
338310491SRishi.Srivatsavai@Sun.COM 		return (DDI_FAILURE);
338410491SRishi.Srivatsavai@Sun.COM 	}
338510491SRishi.Srivatsavai@Sun.COM 
338610491SRishi.Srivatsavai@Sun.COM 	if (dld_ioc_register(BRIDGE_IOC, bridge_ioc_list,
338710491SRishi.Srivatsavai@Sun.COM 	    DLDIOCCNT(bridge_ioc_list)) != 0) {
338810491SRishi.Srivatsavai@Sun.COM 		ddi_remove_minor_node(dip, BRIDGE_CTL);
338910491SRishi.Srivatsavai@Sun.COM 		return (DDI_FAILURE);
339010491SRishi.Srivatsavai@Sun.COM 	}
339110491SRishi.Srivatsavai@Sun.COM 
339210491SRishi.Srivatsavai@Sun.COM 	bridge_dev_info = dip;
339310491SRishi.Srivatsavai@Sun.COM 	bridge_major = ddi_driver_major(dip);
339411109SRishi.Srivatsavai@Sun.COM 	bridge_taskq = ddi_taskq_create(dip, BRIDGE_DEV_NAME, 1,
339511109SRishi.Srivatsavai@Sun.COM 	    TASKQ_DEFAULTPRI, 0);
339610491SRishi.Srivatsavai@Sun.COM 	return (DDI_SUCCESS);
339710491SRishi.Srivatsavai@Sun.COM }
339810491SRishi.Srivatsavai@Sun.COM 
339910491SRishi.Srivatsavai@Sun.COM /*
340010491SRishi.Srivatsavai@Sun.COM  * bridge_detach()
340110491SRishi.Srivatsavai@Sun.COM  *
340210491SRishi.Srivatsavai@Sun.COM  * Description:
340310491SRishi.Srivatsavai@Sun.COM  *    Detach an interface to the system.
340410491SRishi.Srivatsavai@Sun.COM  */
340510491SRishi.Srivatsavai@Sun.COM static int
bridge_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)340610491SRishi.Srivatsavai@Sun.COM bridge_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
340710491SRishi.Srivatsavai@Sun.COM {
340810491SRishi.Srivatsavai@Sun.COM 	if (cmd != DDI_DETACH)
340910491SRishi.Srivatsavai@Sun.COM 		return (DDI_FAILURE);
341010491SRishi.Srivatsavai@Sun.COM 
341110491SRishi.Srivatsavai@Sun.COM 	ddi_remove_minor_node(dip, NULL);
341210491SRishi.Srivatsavai@Sun.COM 	ddi_taskq_destroy(bridge_taskq);
341310491SRishi.Srivatsavai@Sun.COM 	bridge_dev_info = NULL;
341410491SRishi.Srivatsavai@Sun.COM 	return (DDI_SUCCESS);
341510491SRishi.Srivatsavai@Sun.COM }
341610491SRishi.Srivatsavai@Sun.COM 
341710491SRishi.Srivatsavai@Sun.COM /*
341810491SRishi.Srivatsavai@Sun.COM  * bridge_info()
341910491SRishi.Srivatsavai@Sun.COM  *
342010491SRishi.Srivatsavai@Sun.COM  * Description:
342110491SRishi.Srivatsavai@Sun.COM  *    Translate "dev_t" to a pointer to the associated "dev_info_t".
342210491SRishi.Srivatsavai@Sun.COM  */
342310491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
342410491SRishi.Srivatsavai@Sun.COM static int
bridge_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)342510491SRishi.Srivatsavai@Sun.COM bridge_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
342610491SRishi.Srivatsavai@Sun.COM 	void **result)
342710491SRishi.Srivatsavai@Sun.COM {
342810491SRishi.Srivatsavai@Sun.COM 	int	rc;
342910491SRishi.Srivatsavai@Sun.COM 
343010491SRishi.Srivatsavai@Sun.COM 	switch (infocmd) {
343110491SRishi.Srivatsavai@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
343210491SRishi.Srivatsavai@Sun.COM 		if (bridge_dev_info == NULL) {
343310491SRishi.Srivatsavai@Sun.COM 			rc = DDI_FAILURE;
343410491SRishi.Srivatsavai@Sun.COM 		} else {
343510491SRishi.Srivatsavai@Sun.COM 			*result = (void *)bridge_dev_info;
343610491SRishi.Srivatsavai@Sun.COM 			rc = DDI_SUCCESS;
343710491SRishi.Srivatsavai@Sun.COM 		}
343810491SRishi.Srivatsavai@Sun.COM 		break;
343910491SRishi.Srivatsavai@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
344010491SRishi.Srivatsavai@Sun.COM 		*result = NULL;
344110491SRishi.Srivatsavai@Sun.COM 		rc = DDI_SUCCESS;
344210491SRishi.Srivatsavai@Sun.COM 		break;
344310491SRishi.Srivatsavai@Sun.COM 	default:
344410491SRishi.Srivatsavai@Sun.COM 		rc = DDI_FAILURE;
344510491SRishi.Srivatsavai@Sun.COM 		break;
344610491SRishi.Srivatsavai@Sun.COM 	}
344710491SRishi.Srivatsavai@Sun.COM 	return (rc);
344810491SRishi.Srivatsavai@Sun.COM }
344910491SRishi.Srivatsavai@Sun.COM 
345010491SRishi.Srivatsavai@Sun.COM static struct module_info bridge_modinfo = {
345110491SRishi.Srivatsavai@Sun.COM 	2105,			/* mi_idnum */
345211109SRishi.Srivatsavai@Sun.COM 	BRIDGE_DEV_NAME,	/* mi_idname */
345310491SRishi.Srivatsavai@Sun.COM 	0,			/* mi_minpsz */
345410491SRishi.Srivatsavai@Sun.COM 	16384,			/* mi_maxpsz */
345510491SRishi.Srivatsavai@Sun.COM 	65536,			/* mi_hiwat */
345610491SRishi.Srivatsavai@Sun.COM 	128			/* mi_lowat */
345710491SRishi.Srivatsavai@Sun.COM };
345810491SRishi.Srivatsavai@Sun.COM 
345910491SRishi.Srivatsavai@Sun.COM static struct qinit bridge_rinit = {
346010491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_putp */
346110491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_srvp */
346210491SRishi.Srivatsavai@Sun.COM 	bridge_open,		/* qi_qopen */
346310491SRishi.Srivatsavai@Sun.COM 	bridge_close,		/* qi_qclose */
346410491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qadmin */
346510491SRishi.Srivatsavai@Sun.COM 	&bridge_modinfo,	/* qi_minfo */
346610491SRishi.Srivatsavai@Sun.COM 	NULL			/* qi_mstat */
346710491SRishi.Srivatsavai@Sun.COM };
346810491SRishi.Srivatsavai@Sun.COM 
346910491SRishi.Srivatsavai@Sun.COM static struct qinit bridge_winit = {
347010491SRishi.Srivatsavai@Sun.COM 	(int (*)())bridge_wput, /* qi_putp */
347110491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_srvp */
347210491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qopen */
347310491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qclose */
347410491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qadmin */
347510491SRishi.Srivatsavai@Sun.COM 	&bridge_modinfo,	/* qi_minfo */
347610491SRishi.Srivatsavai@Sun.COM 	NULL			/* qi_mstat */
347710491SRishi.Srivatsavai@Sun.COM };
347810491SRishi.Srivatsavai@Sun.COM 
347910491SRishi.Srivatsavai@Sun.COM static struct streamtab bridge_tab = {
348010491SRishi.Srivatsavai@Sun.COM 	&bridge_rinit,	/* st_rdinit */
348110491SRishi.Srivatsavai@Sun.COM 	&bridge_winit	/* st_wrinit */
348210491SRishi.Srivatsavai@Sun.COM };
348310491SRishi.Srivatsavai@Sun.COM 
348410491SRishi.Srivatsavai@Sun.COM /* No STREAMS perimeters; we do all our own locking */
348510491SRishi.Srivatsavai@Sun.COM DDI_DEFINE_STREAM_OPS(bridge_ops, nulldev, nulldev, bridge_attach,
348610491SRishi.Srivatsavai@Sun.COM     bridge_detach, nodev, bridge_info, D_NEW | D_MP, &bridge_tab,
348710491SRishi.Srivatsavai@Sun.COM     ddi_quiesce_not_supported);
348810491SRishi.Srivatsavai@Sun.COM 
348910491SRishi.Srivatsavai@Sun.COM static struct modldrv modldrv = {
349010491SRishi.Srivatsavai@Sun.COM 	&mod_driverops,
349110491SRishi.Srivatsavai@Sun.COM 	"bridging driver",
349210491SRishi.Srivatsavai@Sun.COM 	&bridge_ops
349310491SRishi.Srivatsavai@Sun.COM };
349410491SRishi.Srivatsavai@Sun.COM 
349510491SRishi.Srivatsavai@Sun.COM static struct modlinkage modlinkage = {
349610491SRishi.Srivatsavai@Sun.COM 	MODREV_1,
349710491SRishi.Srivatsavai@Sun.COM 	(void *)&modldrv,
349810491SRishi.Srivatsavai@Sun.COM 	NULL
349910491SRishi.Srivatsavai@Sun.COM };
350010491SRishi.Srivatsavai@Sun.COM 
350110491SRishi.Srivatsavai@Sun.COM int
_init(void)350210491SRishi.Srivatsavai@Sun.COM _init(void)
350310491SRishi.Srivatsavai@Sun.COM {
350410491SRishi.Srivatsavai@Sun.COM 	int retv;
350510491SRishi.Srivatsavai@Sun.COM 
350611109SRishi.Srivatsavai@Sun.COM 	mac_init_ops(NULL, BRIDGE_DEV_NAME);
350710491SRishi.Srivatsavai@Sun.COM 	bridge_inst_init();
350810491SRishi.Srivatsavai@Sun.COM 	if ((retv = mod_install(&modlinkage)) != 0)
350910491SRishi.Srivatsavai@Sun.COM 		bridge_inst_fini();
351010491SRishi.Srivatsavai@Sun.COM 	return (retv);
351110491SRishi.Srivatsavai@Sun.COM }
351210491SRishi.Srivatsavai@Sun.COM 
351310491SRishi.Srivatsavai@Sun.COM int
_fini(void)351410491SRishi.Srivatsavai@Sun.COM _fini(void)
351510491SRishi.Srivatsavai@Sun.COM {
351610491SRishi.Srivatsavai@Sun.COM 	int retv;
351710491SRishi.Srivatsavai@Sun.COM 
351810491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bmac_rwlock, RW_READER);
351910491SRishi.Srivatsavai@Sun.COM 	retv = list_is_empty(&bmac_list) ? 0 : EBUSY;
352010491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bmac_rwlock);
352110491SRishi.Srivatsavai@Sun.COM 	if (retv == 0 &&
352210491SRishi.Srivatsavai@Sun.COM 	    (retv = mod_remove(&modlinkage)) == 0)
352310491SRishi.Srivatsavai@Sun.COM 		bridge_inst_fini();
352410491SRishi.Srivatsavai@Sun.COM 	return (retv);
352510491SRishi.Srivatsavai@Sun.COM }
352610491SRishi.Srivatsavai@Sun.COM 
352710491SRishi.Srivatsavai@Sun.COM int
_info(struct modinfo * modinfop)352810491SRishi.Srivatsavai@Sun.COM _info(struct modinfo *modinfop)
352910491SRishi.Srivatsavai@Sun.COM {
353010491SRishi.Srivatsavai@Sun.COM 	return (mod_info(&modlinkage, modinfop));
353110491SRishi.Srivatsavai@Sun.COM }
3532