xref: /onnv-gate/usr/src/uts/common/io/bridge.c (revision 10491:8893b747ecdf)
1*10491SRishi.Srivatsavai@Sun.COM /*
2*10491SRishi.Srivatsavai@Sun.COM  * CDDL HEADER START
3*10491SRishi.Srivatsavai@Sun.COM  *
4*10491SRishi.Srivatsavai@Sun.COM  * The contents of this file are subject to the terms of the
5*10491SRishi.Srivatsavai@Sun.COM  * Common Development and Distribution License (the "License").
6*10491SRishi.Srivatsavai@Sun.COM  * You may not use this file except in compliance with the License.
7*10491SRishi.Srivatsavai@Sun.COM  *
8*10491SRishi.Srivatsavai@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*10491SRishi.Srivatsavai@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*10491SRishi.Srivatsavai@Sun.COM  * See the License for the specific language governing permissions
11*10491SRishi.Srivatsavai@Sun.COM  * and limitations under the License.
12*10491SRishi.Srivatsavai@Sun.COM  *
13*10491SRishi.Srivatsavai@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*10491SRishi.Srivatsavai@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*10491SRishi.Srivatsavai@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*10491SRishi.Srivatsavai@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*10491SRishi.Srivatsavai@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*10491SRishi.Srivatsavai@Sun.COM  *
19*10491SRishi.Srivatsavai@Sun.COM  * CDDL HEADER END
20*10491SRishi.Srivatsavai@Sun.COM  */
21*10491SRishi.Srivatsavai@Sun.COM 
22*10491SRishi.Srivatsavai@Sun.COM /*
23*10491SRishi.Srivatsavai@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*10491SRishi.Srivatsavai@Sun.COM  * Use is subject to license terms.
25*10491SRishi.Srivatsavai@Sun.COM  */
26*10491SRishi.Srivatsavai@Sun.COM 
27*10491SRishi.Srivatsavai@Sun.COM /*
28*10491SRishi.Srivatsavai@Sun.COM  * This module implements a STREAMS driver that provides layer-two (Ethernet)
29*10491SRishi.Srivatsavai@Sun.COM  * bridging functionality.  The STREAMS interface is used to provide
30*10491SRishi.Srivatsavai@Sun.COM  * observability (snoop/wireshark) and control, but not for interface plumbing.
31*10491SRishi.Srivatsavai@Sun.COM  */
32*10491SRishi.Srivatsavai@Sun.COM 
33*10491SRishi.Srivatsavai@Sun.COM #include <sys/types.h>
34*10491SRishi.Srivatsavai@Sun.COM #include <sys/bitmap.h>
35*10491SRishi.Srivatsavai@Sun.COM #include <sys/cmn_err.h>
36*10491SRishi.Srivatsavai@Sun.COM #include <sys/conf.h>
37*10491SRishi.Srivatsavai@Sun.COM #include <sys/ddi.h>
38*10491SRishi.Srivatsavai@Sun.COM #include <sys/errno.h>
39*10491SRishi.Srivatsavai@Sun.COM #include <sys/kstat.h>
40*10491SRishi.Srivatsavai@Sun.COM #include <sys/modctl.h>
41*10491SRishi.Srivatsavai@Sun.COM #include <sys/note.h>
42*10491SRishi.Srivatsavai@Sun.COM #include <sys/param.h>
43*10491SRishi.Srivatsavai@Sun.COM #include <sys/policy.h>
44*10491SRishi.Srivatsavai@Sun.COM #include <sys/sdt.h>
45*10491SRishi.Srivatsavai@Sun.COM #include <sys/stat.h>
46*10491SRishi.Srivatsavai@Sun.COM #include <sys/stream.h>
47*10491SRishi.Srivatsavai@Sun.COM #include <sys/stropts.h>
48*10491SRishi.Srivatsavai@Sun.COM #include <sys/strsun.h>
49*10491SRishi.Srivatsavai@Sun.COM #include <sys/sunddi.h>
50*10491SRishi.Srivatsavai@Sun.COM #include <sys/sysmacros.h>
51*10491SRishi.Srivatsavai@Sun.COM #include <sys/systm.h>
52*10491SRishi.Srivatsavai@Sun.COM #include <sys/time.h>
53*10491SRishi.Srivatsavai@Sun.COM #include <sys/dlpi.h>
54*10491SRishi.Srivatsavai@Sun.COM #include <sys/dls.h>
55*10491SRishi.Srivatsavai@Sun.COM #include <sys/mac_ether.h>
56*10491SRishi.Srivatsavai@Sun.COM #include <sys/mac_provider.h>
57*10491SRishi.Srivatsavai@Sun.COM #include <sys/mac_client_priv.h>
58*10491SRishi.Srivatsavai@Sun.COM #include <sys/mac_impl.h>
59*10491SRishi.Srivatsavai@Sun.COM #include <sys/vlan.h>
60*10491SRishi.Srivatsavai@Sun.COM #include <net/bridge.h>
61*10491SRishi.Srivatsavai@Sun.COM #include <net/bridge_impl.h>
62*10491SRishi.Srivatsavai@Sun.COM #include <net/trill.h>
63*10491SRishi.Srivatsavai@Sun.COM 
64*10491SRishi.Srivatsavai@Sun.COM /*
65*10491SRishi.Srivatsavai@Sun.COM  * Locks and reference counts: object lifetime and design.
66*10491SRishi.Srivatsavai@Sun.COM  *
67*10491SRishi.Srivatsavai@Sun.COM  * bridge_mac_t
68*10491SRishi.Srivatsavai@Sun.COM  *   Bridge mac (snoop) instances are in bmac_list, which is protected by
69*10491SRishi.Srivatsavai@Sun.COM  *   bmac_rwlock.  They're allocated by bmac_alloc and freed by bridge_timer().
70*10491SRishi.Srivatsavai@Sun.COM  *   Every bridge_inst_t has a single bridge_mac_t, but when bridge_inst_t goes
71*10491SRishi.Srivatsavai@Sun.COM  *   away, the bridge_mac_t remains until either all of the users go away
72*10491SRishi.Srivatsavai@Sun.COM  *   (detected by a timer) or until the instance is picked up again by the same
73*10491SRishi.Srivatsavai@Sun.COM  *   bridge starting back up.
74*10491SRishi.Srivatsavai@Sun.COM  *
75*10491SRishi.Srivatsavai@Sun.COM  * bridge_inst_t
76*10491SRishi.Srivatsavai@Sun.COM  *   Bridge instances are in inst_list, which is protected by inst_lock.
77*10491SRishi.Srivatsavai@Sun.COM  *   They're allocated by inst_alloc() and freed by inst_free().  After
78*10491SRishi.Srivatsavai@Sun.COM  *   allocation, an instance is placed in inst_list, and the reference count is
79*10491SRishi.Srivatsavai@Sun.COM  *   incremented to represent this.  That reference is decremented when the
80*10491SRishi.Srivatsavai@Sun.COM  *   BIF_SHUTDOWN flag is set, and no new increments may occur.  When the last
81*10491SRishi.Srivatsavai@Sun.COM  *   reference is freed, the instance is removed from the list.
82*10491SRishi.Srivatsavai@Sun.COM  *
83*10491SRishi.Srivatsavai@Sun.COM  *   Bridge instances have lists of links and an AVL tree of forwarding
84*10491SRishi.Srivatsavai@Sun.COM  *   entries.  Each of these structures holds one reference on the bridge
85*10491SRishi.Srivatsavai@Sun.COM  *   instance.  These lists and tree are protected by bi_rwlock.
86*10491SRishi.Srivatsavai@Sun.COM  *
87*10491SRishi.Srivatsavai@Sun.COM  * bridge_stream_t
88*10491SRishi.Srivatsavai@Sun.COM  *   Bridge streams are allocated by stream_alloc() and freed by stream_free().
89*10491SRishi.Srivatsavai@Sun.COM  *   These streams are created when "bridged" opens /dev/bridgectl, and are
90*10491SRishi.Srivatsavai@Sun.COM  *   used to create new bridge instances (via BRIOC_NEWBRIDGE) and control the
91*10491SRishi.Srivatsavai@Sun.COM  *   links on the bridge.  When a stream closes, the bridge instance created is
92*10491SRishi.Srivatsavai@Sun.COM  *   destroyed.  There's at most one bridge instance for a given control
93*10491SRishi.Srivatsavai@Sun.COM  *   stream.
94*10491SRishi.Srivatsavai@Sun.COM  *
95*10491SRishi.Srivatsavai@Sun.COM  * bridge_link_t
96*10491SRishi.Srivatsavai@Sun.COM  *   Links are allocated by bridge_add_link() and freed by link_free().  The
97*10491SRishi.Srivatsavai@Sun.COM  *   bi_links list holds a reference to the link.  When the BLF_DELETED flag is
98*10491SRishi.Srivatsavai@Sun.COM  *   set, that reference is dropped.  The link isn't removed from the list
99*10491SRishi.Srivatsavai@Sun.COM  *   until the last reference drops.  Each forwarding entry that uses a given
100*10491SRishi.Srivatsavai@Sun.COM  *   link holds a reference, as does each thread transmitting a packet via the
101*10491SRishi.Srivatsavai@Sun.COM  *   link.  The MAC layer calls in via bridge_ref_cb() to hold a reference on
102*10491SRishi.Srivatsavai@Sun.COM  *   a link when transmitting.
103*10491SRishi.Srivatsavai@Sun.COM  *
104*10491SRishi.Srivatsavai@Sun.COM  *   It's important that once BLF_DELETED is set, there's no way for the
105*10491SRishi.Srivatsavai@Sun.COM  *   reference count to increase again.  If it can, then the link may be
106*10491SRishi.Srivatsavai@Sun.COM  *   double-freed.  The BLF_FREED flag is intended for use with assertions to
107*10491SRishi.Srivatsavai@Sun.COM  *   guard against this in testing.
108*10491SRishi.Srivatsavai@Sun.COM  *
109*10491SRishi.Srivatsavai@Sun.COM  * bridge_fwd_t
110*10491SRishi.Srivatsavai@Sun.COM  *   Bridge forwarding entries are allocated by bridge_recv_cb() and freed by
111*10491SRishi.Srivatsavai@Sun.COM  *   fwd_free().  The bi_fwd AVL tree holds one reference to the entry.  Unlike
112*10491SRishi.Srivatsavai@Sun.COM  *   other data structures, the reference is dropped when the entry is removed
113*10491SRishi.Srivatsavai@Sun.COM  *   from the tree by fwd_delete(), and the BFF_INTREE flag is removed.  Each
114*10491SRishi.Srivatsavai@Sun.COM  *   thread that's forwarding a packet to a known destination holds a reference
115*10491SRishi.Srivatsavai@Sun.COM  *   to a forwarding entry.
116*10491SRishi.Srivatsavai@Sun.COM  *
117*10491SRishi.Srivatsavai@Sun.COM  * TRILL notes:
118*10491SRishi.Srivatsavai@Sun.COM  *
119*10491SRishi.Srivatsavai@Sun.COM  *   The TRILL module does all of its I/O through bridging.  It uses references
120*10491SRishi.Srivatsavai@Sun.COM  *   on the bridge_inst_t and bridge_link_t structures, and has seven entry
121*10491SRishi.Srivatsavai@Sun.COM  *   points and four callbacks.  One entry point is for setting the callbacks
122*10491SRishi.Srivatsavai@Sun.COM  *   (bridge_trill_register_cb).  There are four entry points for taking bridge
123*10491SRishi.Srivatsavai@Sun.COM  *   and link references (bridge_trill_{br,ln}{ref,unref}).  The final two
124*10491SRishi.Srivatsavai@Sun.COM  *   entry points are for decapsulated packets from TRILL (bridge_trill_decaps)
125*10491SRishi.Srivatsavai@Sun.COM  *   that need to be bridged locally, and for TRILL-encapsulated output packets
126*10491SRishi.Srivatsavai@Sun.COM  *   (bridge_trill_output).
127*10491SRishi.Srivatsavai@Sun.COM  *
128*10491SRishi.Srivatsavai@Sun.COM  *   The four callbacks comprise two notification functions for bridges and
129*10491SRishi.Srivatsavai@Sun.COM  *   links being deleted, one function for raw received TRILL packets, and one
130*10491SRishi.Srivatsavai@Sun.COM  *   for bridge output to non-local TRILL destinations (tunnel entry).
131*10491SRishi.Srivatsavai@Sun.COM  */
132*10491SRishi.Srivatsavai@Sun.COM 
133*10491SRishi.Srivatsavai@Sun.COM /*
134*10491SRishi.Srivatsavai@Sun.COM  * Ethernet reserved multicast addresses for TRILL; used also in TRILL module.
135*10491SRishi.Srivatsavai@Sun.COM  */
136*10491SRishi.Srivatsavai@Sun.COM const uint8_t all_isis_rbridges[] = ALL_ISIS_RBRIDGES;
137*10491SRishi.Srivatsavai@Sun.COM static const uint8_t all_esadi_rbridges[] = ALL_ESADI_RBRIDGES;
138*10491SRishi.Srivatsavai@Sun.COM const uint8_t bridge_group_address[] = BRIDGE_GROUP_ADDRESS;
139*10491SRishi.Srivatsavai@Sun.COM 
140*10491SRishi.Srivatsavai@Sun.COM static const char *inst_kstats_list[] = { KSINST_NAMES };
141*10491SRishi.Srivatsavai@Sun.COM static const char *link_kstats_list[] = { KSLINK_NAMES };
142*10491SRishi.Srivatsavai@Sun.COM 
143*10491SRishi.Srivatsavai@Sun.COM #define	KREF(p, m, vn)	p->m.vn.value.ui64
144*10491SRishi.Srivatsavai@Sun.COM #define	KINCR(p, m, vn)	++KREF(p, m, vn)
145*10491SRishi.Srivatsavai@Sun.COM #define	KDECR(p, m, vn)	--KREF(p, m, vn)
146*10491SRishi.Srivatsavai@Sun.COM 
147*10491SRishi.Srivatsavai@Sun.COM #define	KIPINCR(p, vn)	KINCR(p, bi_kstats, vn)
148*10491SRishi.Srivatsavai@Sun.COM #define	KIPDECR(p, vn)	KDECR(p, bi_kstats, vn)
149*10491SRishi.Srivatsavai@Sun.COM #define	KLPINCR(p, vn)	KINCR(p, bl_kstats, vn)
150*10491SRishi.Srivatsavai@Sun.COM 
151*10491SRishi.Srivatsavai@Sun.COM #define	KIINCR(vn)	KIPINCR(bip, vn)
152*10491SRishi.Srivatsavai@Sun.COM #define	KIDECR(vn)	KIPDECR(bip, vn)
153*10491SRishi.Srivatsavai@Sun.COM #define	KLINCR(vn)	KLPINCR(blp, vn)
154*10491SRishi.Srivatsavai@Sun.COM 
155*10491SRishi.Srivatsavai@Sun.COM #define	Dim(x)		(sizeof (x) / sizeof (*(x)))
156*10491SRishi.Srivatsavai@Sun.COM 
157*10491SRishi.Srivatsavai@Sun.COM /* Amount of overhead added when encapsulating with VLAN headers */
158*10491SRishi.Srivatsavai@Sun.COM #define	VLAN_INCR	(sizeof (struct ether_vlan_header) -	\
159*10491SRishi.Srivatsavai@Sun.COM 			sizeof (struct ether_header))
160*10491SRishi.Srivatsavai@Sun.COM 
161*10491SRishi.Srivatsavai@Sun.COM static dev_info_t *bridge_dev_info;
162*10491SRishi.Srivatsavai@Sun.COM static major_t bridge_major;
163*10491SRishi.Srivatsavai@Sun.COM static ddi_taskq_t *bridge_taskq;
164*10491SRishi.Srivatsavai@Sun.COM 
165*10491SRishi.Srivatsavai@Sun.COM /*
166*10491SRishi.Srivatsavai@Sun.COM  * These are the bridge instance management data structures.  The mutex lock
167*10491SRishi.Srivatsavai@Sun.COM  * protects the list of bridge instances.  A reference count is then used on
168*10491SRishi.Srivatsavai@Sun.COM  * each instance to determine when to free it.  We use mac_minor_hold() to
169*10491SRishi.Srivatsavai@Sun.COM  * allocate minor_t values, which are used both for self-cloning /dev/net/
170*10491SRishi.Srivatsavai@Sun.COM  * device nodes as well as client streams.  Minor node 0 is reserved for the
171*10491SRishi.Srivatsavai@Sun.COM  * allocation control node.
172*10491SRishi.Srivatsavai@Sun.COM  */
173*10491SRishi.Srivatsavai@Sun.COM static list_t inst_list;
174*10491SRishi.Srivatsavai@Sun.COM static kcondvar_t inst_cv;		/* Allows us to wait for shutdown */
175*10491SRishi.Srivatsavai@Sun.COM static kmutex_t inst_lock;
176*10491SRishi.Srivatsavai@Sun.COM 
177*10491SRishi.Srivatsavai@Sun.COM static krwlock_t bmac_rwlock;
178*10491SRishi.Srivatsavai@Sun.COM static list_t bmac_list;
179*10491SRishi.Srivatsavai@Sun.COM 
180*10491SRishi.Srivatsavai@Sun.COM /* Wait for taskq entries that use STREAMS */
181*10491SRishi.Srivatsavai@Sun.COM static kcondvar_t stream_ref_cv;
182*10491SRishi.Srivatsavai@Sun.COM static kmutex_t stream_ref_lock;
183*10491SRishi.Srivatsavai@Sun.COM 
184*10491SRishi.Srivatsavai@Sun.COM static timeout_id_t bridge_timerid;
185*10491SRishi.Srivatsavai@Sun.COM static clock_t bridge_scan_interval;
186*10491SRishi.Srivatsavai@Sun.COM static clock_t bridge_fwd_age;
187*10491SRishi.Srivatsavai@Sun.COM 
188*10491SRishi.Srivatsavai@Sun.COM static bridge_inst_t *bridge_find_name(const char *);
189*10491SRishi.Srivatsavai@Sun.COM static void bridge_timer(void *);
190*10491SRishi.Srivatsavai@Sun.COM static void bridge_unref(bridge_inst_t *);
191*10491SRishi.Srivatsavai@Sun.COM 
192*10491SRishi.Srivatsavai@Sun.COM static const uint8_t zero_addr[ETHERADDRL] = { 0 };
193*10491SRishi.Srivatsavai@Sun.COM 
194*10491SRishi.Srivatsavai@Sun.COM /* Global TRILL linkage */
195*10491SRishi.Srivatsavai@Sun.COM static trill_recv_pkt_t trill_recv_fn;
196*10491SRishi.Srivatsavai@Sun.COM static trill_encap_pkt_t trill_encap_fn;
197*10491SRishi.Srivatsavai@Sun.COM static trill_br_dstr_t trill_brdstr_fn;
198*10491SRishi.Srivatsavai@Sun.COM static trill_ln_dstr_t trill_lndstr_fn;
199*10491SRishi.Srivatsavai@Sun.COM 
200*10491SRishi.Srivatsavai@Sun.COM /* special settings to accommodate DLD flow control; see dld_str.c */
201*10491SRishi.Srivatsavai@Sun.COM static struct module_info bridge_dld_modinfo = {
202*10491SRishi.Srivatsavai@Sun.COM 	0,			/* mi_idnum */
203*10491SRishi.Srivatsavai@Sun.COM 	"bridge",		/* mi_idname */
204*10491SRishi.Srivatsavai@Sun.COM 	0,			/* mi_minpsz */
205*10491SRishi.Srivatsavai@Sun.COM 	INFPSZ,			/* mi_maxpsz */
206*10491SRishi.Srivatsavai@Sun.COM 	1,			/* mi_hiwat */
207*10491SRishi.Srivatsavai@Sun.COM 	0			/* mi_lowat */
208*10491SRishi.Srivatsavai@Sun.COM };
209*10491SRishi.Srivatsavai@Sun.COM 
210*10491SRishi.Srivatsavai@Sun.COM static struct qinit bridge_dld_rinit = {
211*10491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_putp */
212*10491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_srvp */
213*10491SRishi.Srivatsavai@Sun.COM 	dld_open,		/* qi_qopen */
214*10491SRishi.Srivatsavai@Sun.COM 	dld_close,		/* qi_qclose */
215*10491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qadmin */
216*10491SRishi.Srivatsavai@Sun.COM 	&bridge_dld_modinfo,	/* qi_minfo */
217*10491SRishi.Srivatsavai@Sun.COM 	NULL			/* qi_mstat */
218*10491SRishi.Srivatsavai@Sun.COM };
219*10491SRishi.Srivatsavai@Sun.COM 
220*10491SRishi.Srivatsavai@Sun.COM static struct qinit bridge_dld_winit = {
221*10491SRishi.Srivatsavai@Sun.COM 	(int (*)())dld_wput,	/* qi_putp */
222*10491SRishi.Srivatsavai@Sun.COM 	(int (*)())dld_wsrv,	/* qi_srvp */
223*10491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qopen */
224*10491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qclose */
225*10491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qadmin */
226*10491SRishi.Srivatsavai@Sun.COM 	&bridge_dld_modinfo,	/* qi_minfo */
227*10491SRishi.Srivatsavai@Sun.COM 	NULL			/* qi_mstat */
228*10491SRishi.Srivatsavai@Sun.COM };
229*10491SRishi.Srivatsavai@Sun.COM 
230*10491SRishi.Srivatsavai@Sun.COM static int bridge_ioc_listfwd(void *, intptr_t, int, cred_t *, int *);
231*10491SRishi.Srivatsavai@Sun.COM 
232*10491SRishi.Srivatsavai@Sun.COM /* GLDv3 control ioctls used by Bridging */
233*10491SRishi.Srivatsavai@Sun.COM static dld_ioc_info_t bridge_ioc_list[] = {
234*10491SRishi.Srivatsavai@Sun.COM 	{BRIDGE_IOC_LISTFWD, DLDCOPYINOUT, sizeof (bridge_listfwd_t),
235*10491SRishi.Srivatsavai@Sun.COM 	    bridge_ioc_listfwd, NULL},
236*10491SRishi.Srivatsavai@Sun.COM };
237*10491SRishi.Srivatsavai@Sun.COM 
238*10491SRishi.Srivatsavai@Sun.COM /*
239*10491SRishi.Srivatsavai@Sun.COM  * Given a bridge mac pointer, get a ref-held pointer to the corresponding
240*10491SRishi.Srivatsavai@Sun.COM  * bridge instance, if any.  We must hold the global bmac_rwlock so that
241*10491SRishi.Srivatsavai@Sun.COM  * bm_inst doesn't slide out from under us.
242*10491SRishi.Srivatsavai@Sun.COM  */
243*10491SRishi.Srivatsavai@Sun.COM static bridge_inst_t *
244*10491SRishi.Srivatsavai@Sun.COM mac_to_inst(const bridge_mac_t *bmp)
245*10491SRishi.Srivatsavai@Sun.COM {
246*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
247*10491SRishi.Srivatsavai@Sun.COM 
248*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bmac_rwlock, RW_READER);
249*10491SRishi.Srivatsavai@Sun.COM 	if ((bip = bmp->bm_inst) != NULL)
250*10491SRishi.Srivatsavai@Sun.COM 		atomic_inc_uint(&bip->bi_refs);
251*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bmac_rwlock);
252*10491SRishi.Srivatsavai@Sun.COM 	return (bip);
253*10491SRishi.Srivatsavai@Sun.COM }
254*10491SRishi.Srivatsavai@Sun.COM 
255*10491SRishi.Srivatsavai@Sun.COM static void
256*10491SRishi.Srivatsavai@Sun.COM link_sdu_fail(bridge_link_t *blp, boolean_t failed, mblk_t **mlist)
257*10491SRishi.Srivatsavai@Sun.COM {
258*10491SRishi.Srivatsavai@Sun.COM 	mblk_t *mp;
259*10491SRishi.Srivatsavai@Sun.COM 	bridge_ctl_t *bcp;
260*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blcmp;
261*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
262*10491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp;
263*10491SRishi.Srivatsavai@Sun.COM 
264*10491SRishi.Srivatsavai@Sun.COM 	if (failed) {
265*10491SRishi.Srivatsavai@Sun.COM 		if (blp->bl_flags & BLF_SDUFAIL)
266*10491SRishi.Srivatsavai@Sun.COM 			return;
267*10491SRishi.Srivatsavai@Sun.COM 		blp->bl_flags |= BLF_SDUFAIL;
268*10491SRishi.Srivatsavai@Sun.COM 	} else {
269*10491SRishi.Srivatsavai@Sun.COM 		if (!(blp->bl_flags & BLF_SDUFAIL))
270*10491SRishi.Srivatsavai@Sun.COM 			return;
271*10491SRishi.Srivatsavai@Sun.COM 		blp->bl_flags &= ~BLF_SDUFAIL;
272*10491SRishi.Srivatsavai@Sun.COM 	}
273*10491SRishi.Srivatsavai@Sun.COM 
274*10491SRishi.Srivatsavai@Sun.COM 	/*
275*10491SRishi.Srivatsavai@Sun.COM 	 * If this link is otherwise up, then check if there are any other
276*10491SRishi.Srivatsavai@Sun.COM 	 * non-failed non-down links.  If not, then we control the state of the
277*10491SRishi.Srivatsavai@Sun.COM 	 * whole bridge.
278*10491SRishi.Srivatsavai@Sun.COM 	 */
279*10491SRishi.Srivatsavai@Sun.COM 	bip = blp->bl_inst;
280*10491SRishi.Srivatsavai@Sun.COM 	bmp = bip->bi_mac;
281*10491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_linkstate != LINK_STATE_DOWN) {
282*10491SRishi.Srivatsavai@Sun.COM 		for (blcmp = list_head(&bip->bi_links); blcmp != NULL;
283*10491SRishi.Srivatsavai@Sun.COM 		    blcmp = list_next(&bip->bi_links, blcmp)) {
284*10491SRishi.Srivatsavai@Sun.COM 			if (blp != blcmp &&
285*10491SRishi.Srivatsavai@Sun.COM 			    !(blcmp->bl_flags & (BLF_DELETED|BLF_SDUFAIL)) &&
286*10491SRishi.Srivatsavai@Sun.COM 			    blcmp->bl_linkstate != LINK_STATE_DOWN)
287*10491SRishi.Srivatsavai@Sun.COM 				break;
288*10491SRishi.Srivatsavai@Sun.COM 		}
289*10491SRishi.Srivatsavai@Sun.COM 		if (blcmp == NULL) {
290*10491SRishi.Srivatsavai@Sun.COM 			bmp->bm_linkstate = failed ? LINK_STATE_DOWN :
291*10491SRishi.Srivatsavai@Sun.COM 			    LINK_STATE_UP;
292*10491SRishi.Srivatsavai@Sun.COM 			mac_link_redo(bmp->bm_mh, bmp->bm_linkstate);
293*10491SRishi.Srivatsavai@Sun.COM 		}
294*10491SRishi.Srivatsavai@Sun.COM 	}
295*10491SRishi.Srivatsavai@Sun.COM 
296*10491SRishi.Srivatsavai@Sun.COM 	/*
297*10491SRishi.Srivatsavai@Sun.COM 	 * If we're becoming failed, then the link's current true state needs
298*10491SRishi.Srivatsavai@Sun.COM 	 * to be reflected upwards to this link's clients.  If we're becoming
299*10491SRishi.Srivatsavai@Sun.COM 	 * unfailed, then we get the state of the bridge instead on all
300*10491SRishi.Srivatsavai@Sun.COM 	 * clients.
301*10491SRishi.Srivatsavai@Sun.COM 	 */
302*10491SRishi.Srivatsavai@Sun.COM 	if (failed) {
303*10491SRishi.Srivatsavai@Sun.COM 		if (bmp->bm_linkstate != blp->bl_linkstate)
304*10491SRishi.Srivatsavai@Sun.COM 			mac_link_redo(blp->bl_mh, blp->bl_linkstate);
305*10491SRishi.Srivatsavai@Sun.COM 	} else {
306*10491SRishi.Srivatsavai@Sun.COM 		mac_link_redo(blp->bl_mh, bmp->bm_linkstate);
307*10491SRishi.Srivatsavai@Sun.COM 	}
308*10491SRishi.Srivatsavai@Sun.COM 
309*10491SRishi.Srivatsavai@Sun.COM 	/* get the current mblk we're going to send up */
310*10491SRishi.Srivatsavai@Sun.COM 	if ((mp = blp->bl_lfailmp) == NULL &&
311*10491SRishi.Srivatsavai@Sun.COM 	    (mp = allocb(sizeof (bridge_ctl_t), BPRI_MED)) == NULL)
312*10491SRishi.Srivatsavai@Sun.COM 		return;
313*10491SRishi.Srivatsavai@Sun.COM 
314*10491SRishi.Srivatsavai@Sun.COM 	/* get a new one for next time */
315*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_lfailmp = allocb(sizeof (bridge_ctl_t), BPRI_MED);
316*10491SRishi.Srivatsavai@Sun.COM 
317*10491SRishi.Srivatsavai@Sun.COM 	/* if none for next time, then report only failures */
318*10491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_lfailmp == NULL && !failed) {
319*10491SRishi.Srivatsavai@Sun.COM 		blp->bl_lfailmp = mp;
320*10491SRishi.Srivatsavai@Sun.COM 		return;
321*10491SRishi.Srivatsavai@Sun.COM 	}
322*10491SRishi.Srivatsavai@Sun.COM 
323*10491SRishi.Srivatsavai@Sun.COM 	/* LINTED: alignment */
324*10491SRishi.Srivatsavai@Sun.COM 	bcp = (bridge_ctl_t *)mp->b_rptr;
325*10491SRishi.Srivatsavai@Sun.COM 	bcp->bc_linkid = blp->bl_linkid;
326*10491SRishi.Srivatsavai@Sun.COM 	bcp->bc_failed = failed;
327*10491SRishi.Srivatsavai@Sun.COM 	mp->b_wptr = (uchar_t *)(bcp + 1);
328*10491SRishi.Srivatsavai@Sun.COM 	mp->b_next = *mlist;
329*10491SRishi.Srivatsavai@Sun.COM 	*mlist = mp;
330*10491SRishi.Srivatsavai@Sun.COM }
331*10491SRishi.Srivatsavai@Sun.COM 
332*10491SRishi.Srivatsavai@Sun.COM /*
333*10491SRishi.Srivatsavai@Sun.COM  * Send control messages (link SDU changes) using the stream to the
334*10491SRishi.Srivatsavai@Sun.COM  * bridge instance daemon.
335*10491SRishi.Srivatsavai@Sun.COM  */
336*10491SRishi.Srivatsavai@Sun.COM static void
337*10491SRishi.Srivatsavai@Sun.COM send_up_messages(bridge_inst_t *bip, mblk_t *mp)
338*10491SRishi.Srivatsavai@Sun.COM {
339*10491SRishi.Srivatsavai@Sun.COM 	mblk_t *mnext;
340*10491SRishi.Srivatsavai@Sun.COM 	queue_t *rq;
341*10491SRishi.Srivatsavai@Sun.COM 
342*10491SRishi.Srivatsavai@Sun.COM 	rq = bip->bi_control->bs_wq;
343*10491SRishi.Srivatsavai@Sun.COM 	rq = OTHERQ(rq);
344*10491SRishi.Srivatsavai@Sun.COM 	while (mp != NULL) {
345*10491SRishi.Srivatsavai@Sun.COM 		mnext = mp->b_next;
346*10491SRishi.Srivatsavai@Sun.COM 		mp->b_next = NULL;
347*10491SRishi.Srivatsavai@Sun.COM 		putnext(rq, mp);
348*10491SRishi.Srivatsavai@Sun.COM 		mp = mnext;
349*10491SRishi.Srivatsavai@Sun.COM 	}
350*10491SRishi.Srivatsavai@Sun.COM }
351*10491SRishi.Srivatsavai@Sun.COM 
352*10491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
353*10491SRishi.Srivatsavai@Sun.COM static int
354*10491SRishi.Srivatsavai@Sun.COM bridge_m_getstat(void *arg, uint_t stat, uint64_t *val)
355*10491SRishi.Srivatsavai@Sun.COM {
356*10491SRishi.Srivatsavai@Sun.COM 	return (ENOTSUP);
357*10491SRishi.Srivatsavai@Sun.COM }
358*10491SRishi.Srivatsavai@Sun.COM 
359*10491SRishi.Srivatsavai@Sun.COM static int
360*10491SRishi.Srivatsavai@Sun.COM bridge_m_start(void *arg)
361*10491SRishi.Srivatsavai@Sun.COM {
362*10491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp = arg;
363*10491SRishi.Srivatsavai@Sun.COM 
364*10491SRishi.Srivatsavai@Sun.COM 	bmp->bm_flags |= BMF_STARTED;
365*10491SRishi.Srivatsavai@Sun.COM 	return (0);
366*10491SRishi.Srivatsavai@Sun.COM }
367*10491SRishi.Srivatsavai@Sun.COM 
368*10491SRishi.Srivatsavai@Sun.COM static void
369*10491SRishi.Srivatsavai@Sun.COM bridge_m_stop(void *arg)
370*10491SRishi.Srivatsavai@Sun.COM {
371*10491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp = arg;
372*10491SRishi.Srivatsavai@Sun.COM 
373*10491SRishi.Srivatsavai@Sun.COM 	bmp->bm_flags &= ~BMF_STARTED;
374*10491SRishi.Srivatsavai@Sun.COM }
375*10491SRishi.Srivatsavai@Sun.COM 
376*10491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
377*10491SRishi.Srivatsavai@Sun.COM static int
378*10491SRishi.Srivatsavai@Sun.COM bridge_m_setpromisc(void *arg, boolean_t on)
379*10491SRishi.Srivatsavai@Sun.COM {
380*10491SRishi.Srivatsavai@Sun.COM 	return (0);
381*10491SRishi.Srivatsavai@Sun.COM }
382*10491SRishi.Srivatsavai@Sun.COM 
383*10491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
384*10491SRishi.Srivatsavai@Sun.COM static int
385*10491SRishi.Srivatsavai@Sun.COM bridge_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
386*10491SRishi.Srivatsavai@Sun.COM {
387*10491SRishi.Srivatsavai@Sun.COM 	return (0);
388*10491SRishi.Srivatsavai@Sun.COM }
389*10491SRishi.Srivatsavai@Sun.COM 
390*10491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
391*10491SRishi.Srivatsavai@Sun.COM static int
392*10491SRishi.Srivatsavai@Sun.COM bridge_m_unicst(void *arg, const uint8_t *macaddr)
393*10491SRishi.Srivatsavai@Sun.COM {
394*10491SRishi.Srivatsavai@Sun.COM 	return (ENOTSUP);
395*10491SRishi.Srivatsavai@Sun.COM }
396*10491SRishi.Srivatsavai@Sun.COM 
397*10491SRishi.Srivatsavai@Sun.COM static mblk_t *
398*10491SRishi.Srivatsavai@Sun.COM bridge_m_tx(void *arg, mblk_t *mp)
399*10491SRishi.Srivatsavai@Sun.COM {
400*10491SRishi.Srivatsavai@Sun.COM 	_NOTE(ARGUNUSED(arg));
401*10491SRishi.Srivatsavai@Sun.COM 	freemsgchain(mp);
402*10491SRishi.Srivatsavai@Sun.COM 	return (NULL);
403*10491SRishi.Srivatsavai@Sun.COM }
404*10491SRishi.Srivatsavai@Sun.COM 
405*10491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
406*10491SRishi.Srivatsavai@Sun.COM static int
407*10491SRishi.Srivatsavai@Sun.COM bridge_ioc_listfwd(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
408*10491SRishi.Srivatsavai@Sun.COM {
409*10491SRishi.Srivatsavai@Sun.COM 	bridge_listfwd_t *blf = karg;
410*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
411*10491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp, match;
412*10491SRishi.Srivatsavai@Sun.COM 	avl_index_t where;
413*10491SRishi.Srivatsavai@Sun.COM 
414*10491SRishi.Srivatsavai@Sun.COM 	bip = bridge_find_name(blf->blf_name);
415*10491SRishi.Srivatsavai@Sun.COM 	if (bip == NULL)
416*10491SRishi.Srivatsavai@Sun.COM 		return (ENOENT);
417*10491SRishi.Srivatsavai@Sun.COM 
418*10491SRishi.Srivatsavai@Sun.COM 	bcopy(blf->blf_dest, match.bf_dest, ETHERADDRL);
419*10491SRishi.Srivatsavai@Sun.COM 	match.bf_flags |= BFF_VLANLOCAL;
420*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_READER);
421*10491SRishi.Srivatsavai@Sun.COM 	if ((bfp = avl_find(&bip->bi_fwd, &match, &where)) == NULL)
422*10491SRishi.Srivatsavai@Sun.COM 		bfp = avl_nearest(&bip->bi_fwd, where, AVL_AFTER);
423*10491SRishi.Srivatsavai@Sun.COM 	else
424*10491SRishi.Srivatsavai@Sun.COM 		bfp = AVL_NEXT(&bip->bi_fwd, bfp);
425*10491SRishi.Srivatsavai@Sun.COM 	if (bfp == NULL) {
426*10491SRishi.Srivatsavai@Sun.COM 		bzero(blf, sizeof (*blf));
427*10491SRishi.Srivatsavai@Sun.COM 	} else {
428*10491SRishi.Srivatsavai@Sun.COM 		bcopy(bfp->bf_dest, blf->blf_dest, ETHERADDRL);
429*10491SRishi.Srivatsavai@Sun.COM 		blf->blf_trill_nick = bfp->bf_trill_nick;
430*10491SRishi.Srivatsavai@Sun.COM 		blf->blf_ms_age =
431*10491SRishi.Srivatsavai@Sun.COM 		    drv_hztousec(lbolt - bfp->bf_lastheard) / 1000;
432*10491SRishi.Srivatsavai@Sun.COM 		blf->blf_is_local =
433*10491SRishi.Srivatsavai@Sun.COM 		    (bfp->bf_flags & BFF_LOCALADDR) != 0;
434*10491SRishi.Srivatsavai@Sun.COM 		blf->blf_linkid = bfp->bf_links[0]->bl_linkid;
435*10491SRishi.Srivatsavai@Sun.COM 	}
436*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
437*10491SRishi.Srivatsavai@Sun.COM 	bridge_unref(bip);
438*10491SRishi.Srivatsavai@Sun.COM 	return (0);
439*10491SRishi.Srivatsavai@Sun.COM }
440*10491SRishi.Srivatsavai@Sun.COM 
441*10491SRishi.Srivatsavai@Sun.COM static int
442*10491SRishi.Srivatsavai@Sun.COM bridge_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
443*10491SRishi.Srivatsavai@Sun.COM     uint_t pr_valsize, const void *pr_val)
444*10491SRishi.Srivatsavai@Sun.COM {
445*10491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp = arg;
446*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
447*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp;
448*10491SRishi.Srivatsavai@Sun.COM 	int err;
449*10491SRishi.Srivatsavai@Sun.COM 	uint_t maxsdu;
450*10491SRishi.Srivatsavai@Sun.COM 	mblk_t *mlist;
451*10491SRishi.Srivatsavai@Sun.COM 
452*10491SRishi.Srivatsavai@Sun.COM 	_NOTE(ARGUNUSED(pr_name));
453*10491SRishi.Srivatsavai@Sun.COM 	switch (pr_num) {
454*10491SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_MTU:
455*10491SRishi.Srivatsavai@Sun.COM 		if (pr_valsize < sizeof (bmp->bm_maxsdu)) {
456*10491SRishi.Srivatsavai@Sun.COM 			err = EINVAL;
457*10491SRishi.Srivatsavai@Sun.COM 			break;
458*10491SRishi.Srivatsavai@Sun.COM 		}
459*10491SRishi.Srivatsavai@Sun.COM 		(void) bcopy(pr_val, &maxsdu, sizeof (maxsdu));
460*10491SRishi.Srivatsavai@Sun.COM 		if (maxsdu == bmp->bm_maxsdu) {
461*10491SRishi.Srivatsavai@Sun.COM 			err = 0;
462*10491SRishi.Srivatsavai@Sun.COM 		} else if ((bip = mac_to_inst(bmp)) == NULL) {
463*10491SRishi.Srivatsavai@Sun.COM 			err = ENXIO;
464*10491SRishi.Srivatsavai@Sun.COM 		} else {
465*10491SRishi.Srivatsavai@Sun.COM 			rw_enter(&bip->bi_rwlock, RW_WRITER);
466*10491SRishi.Srivatsavai@Sun.COM 			mlist = NULL;
467*10491SRishi.Srivatsavai@Sun.COM 			for (blp = list_head(&bip->bi_links); blp != NULL;
468*10491SRishi.Srivatsavai@Sun.COM 			    blp = list_next(&bip->bi_links, blp)) {
469*10491SRishi.Srivatsavai@Sun.COM 				if (blp->bl_flags & BLF_DELETED)
470*10491SRishi.Srivatsavai@Sun.COM 					continue;
471*10491SRishi.Srivatsavai@Sun.COM 				if (blp->bl_maxsdu == maxsdu)
472*10491SRishi.Srivatsavai@Sun.COM 					link_sdu_fail(blp, B_FALSE, &mlist);
473*10491SRishi.Srivatsavai@Sun.COM 				else if (blp->bl_maxsdu == bmp->bm_maxsdu)
474*10491SRishi.Srivatsavai@Sun.COM 					link_sdu_fail(blp, B_TRUE, &mlist);
475*10491SRishi.Srivatsavai@Sun.COM 			}
476*10491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bip->bi_rwlock);
477*10491SRishi.Srivatsavai@Sun.COM 			bmp->bm_maxsdu = maxsdu;
478*10491SRishi.Srivatsavai@Sun.COM 			(void) mac_maxsdu_update(bmp->bm_mh, maxsdu);
479*10491SRishi.Srivatsavai@Sun.COM 			send_up_messages(bip, mlist);
480*10491SRishi.Srivatsavai@Sun.COM 			bridge_unref(bip);
481*10491SRishi.Srivatsavai@Sun.COM 			err = 0;
482*10491SRishi.Srivatsavai@Sun.COM 		}
483*10491SRishi.Srivatsavai@Sun.COM 		break;
484*10491SRishi.Srivatsavai@Sun.COM 
485*10491SRishi.Srivatsavai@Sun.COM 	default:
486*10491SRishi.Srivatsavai@Sun.COM 		err = ENOTSUP;
487*10491SRishi.Srivatsavai@Sun.COM 		break;
488*10491SRishi.Srivatsavai@Sun.COM 	}
489*10491SRishi.Srivatsavai@Sun.COM 	return (err);
490*10491SRishi.Srivatsavai@Sun.COM }
491*10491SRishi.Srivatsavai@Sun.COM 
492*10491SRishi.Srivatsavai@Sun.COM static int
493*10491SRishi.Srivatsavai@Sun.COM bridge_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
494*10491SRishi.Srivatsavai@Sun.COM     uint_t pr_flags, uint_t pr_valsize, void *pr_val, uint_t *perm)
495*10491SRishi.Srivatsavai@Sun.COM {
496*10491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp = arg;
497*10491SRishi.Srivatsavai@Sun.COM 	int err = 0;
498*10491SRishi.Srivatsavai@Sun.COM 
499*10491SRishi.Srivatsavai@Sun.COM 	_NOTE(ARGUNUSED(pr_name));
500*10491SRishi.Srivatsavai@Sun.COM 	switch (pr_num) {
501*10491SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_MTU: {
502*10491SRishi.Srivatsavai@Sun.COM 		mac_propval_range_t range;
503*10491SRishi.Srivatsavai@Sun.COM 
504*10491SRishi.Srivatsavai@Sun.COM 		if (!(pr_flags & MAC_PROP_POSSIBLE))
505*10491SRishi.Srivatsavai@Sun.COM 			return (ENOTSUP);
506*10491SRishi.Srivatsavai@Sun.COM 		if (pr_valsize < sizeof (mac_propval_range_t))
507*10491SRishi.Srivatsavai@Sun.COM 			return (EINVAL);
508*10491SRishi.Srivatsavai@Sun.COM 		range.mpr_count = 1;
509*10491SRishi.Srivatsavai@Sun.COM 		range.mpr_type = MAC_PROPVAL_UINT32;
510*10491SRishi.Srivatsavai@Sun.COM 		range.range_uint32[0].mpur_min =
511*10491SRishi.Srivatsavai@Sun.COM 		    range.range_uint32[0].mpur_max = bmp->bm_maxsdu;
512*10491SRishi.Srivatsavai@Sun.COM 		bcopy(&range, pr_val, sizeof (range));
513*10491SRishi.Srivatsavai@Sun.COM 		*perm = MAC_PROP_PERM_RW;
514*10491SRishi.Srivatsavai@Sun.COM 		break;
515*10491SRishi.Srivatsavai@Sun.COM 	}
516*10491SRishi.Srivatsavai@Sun.COM 	case MAC_PROP_STATUS:
517*10491SRishi.Srivatsavai@Sun.COM 		if (pr_valsize < sizeof (bmp->bm_linkstate)) {
518*10491SRishi.Srivatsavai@Sun.COM 			err = EINVAL;
519*10491SRishi.Srivatsavai@Sun.COM 		} else {
520*10491SRishi.Srivatsavai@Sun.COM 			bcopy(&bmp->bm_linkstate, pr_val,
521*10491SRishi.Srivatsavai@Sun.COM 			    sizeof (&bmp->bm_linkstate));
522*10491SRishi.Srivatsavai@Sun.COM 			*perm = MAC_PROP_PERM_READ;
523*10491SRishi.Srivatsavai@Sun.COM 		}
524*10491SRishi.Srivatsavai@Sun.COM 		break;
525*10491SRishi.Srivatsavai@Sun.COM 
526*10491SRishi.Srivatsavai@Sun.COM 	default:
527*10491SRishi.Srivatsavai@Sun.COM 		err = ENOTSUP;
528*10491SRishi.Srivatsavai@Sun.COM 		break;
529*10491SRishi.Srivatsavai@Sun.COM 	}
530*10491SRishi.Srivatsavai@Sun.COM 	return (err);
531*10491SRishi.Srivatsavai@Sun.COM }
532*10491SRishi.Srivatsavai@Sun.COM 
533*10491SRishi.Srivatsavai@Sun.COM static mac_callbacks_t bridge_m_callbacks = {
534*10491SRishi.Srivatsavai@Sun.COM 	MC_SETPROP | MC_GETPROP,
535*10491SRishi.Srivatsavai@Sun.COM 	bridge_m_getstat,
536*10491SRishi.Srivatsavai@Sun.COM 	bridge_m_start,
537*10491SRishi.Srivatsavai@Sun.COM 	bridge_m_stop,
538*10491SRishi.Srivatsavai@Sun.COM 	bridge_m_setpromisc,
539*10491SRishi.Srivatsavai@Sun.COM 	bridge_m_multicst,
540*10491SRishi.Srivatsavai@Sun.COM 	bridge_m_unicst,
541*10491SRishi.Srivatsavai@Sun.COM 	bridge_m_tx,
542*10491SRishi.Srivatsavai@Sun.COM 	NULL,	/* ioctl */
543*10491SRishi.Srivatsavai@Sun.COM 	NULL,	/* getcapab */
544*10491SRishi.Srivatsavai@Sun.COM 	NULL,	/* open */
545*10491SRishi.Srivatsavai@Sun.COM 	NULL,	/* close */
546*10491SRishi.Srivatsavai@Sun.COM 	bridge_m_setprop,
547*10491SRishi.Srivatsavai@Sun.COM 	bridge_m_getprop
548*10491SRishi.Srivatsavai@Sun.COM };
549*10491SRishi.Srivatsavai@Sun.COM 
550*10491SRishi.Srivatsavai@Sun.COM /*
551*10491SRishi.Srivatsavai@Sun.COM  * Create kstats from a list.
552*10491SRishi.Srivatsavai@Sun.COM  */
553*10491SRishi.Srivatsavai@Sun.COM static kstat_t *
554*10491SRishi.Srivatsavai@Sun.COM kstat_setup(kstat_named_t *knt, const char **names, int nstat,
555*10491SRishi.Srivatsavai@Sun.COM     const char *unitname)
556*10491SRishi.Srivatsavai@Sun.COM {
557*10491SRishi.Srivatsavai@Sun.COM 	kstat_t *ksp;
558*10491SRishi.Srivatsavai@Sun.COM 	int i;
559*10491SRishi.Srivatsavai@Sun.COM 
560*10491SRishi.Srivatsavai@Sun.COM 	for (i = 0; i < nstat; i++)
561*10491SRishi.Srivatsavai@Sun.COM 		kstat_named_init(&knt[i], names[i], KSTAT_DATA_UINT64);
562*10491SRishi.Srivatsavai@Sun.COM 
563*10491SRishi.Srivatsavai@Sun.COM 	ksp = kstat_create_zone("bridge", 0, unitname, "net",
564*10491SRishi.Srivatsavai@Sun.COM 	    KSTAT_TYPE_NAMED, nstat, KSTAT_FLAG_VIRTUAL, GLOBAL_ZONEID);
565*10491SRishi.Srivatsavai@Sun.COM 	if (ksp != NULL) {
566*10491SRishi.Srivatsavai@Sun.COM 		ksp->ks_data = knt;
567*10491SRishi.Srivatsavai@Sun.COM 		kstat_install(ksp);
568*10491SRishi.Srivatsavai@Sun.COM 	}
569*10491SRishi.Srivatsavai@Sun.COM 	return (ksp);
570*10491SRishi.Srivatsavai@Sun.COM }
571*10491SRishi.Srivatsavai@Sun.COM 
572*10491SRishi.Srivatsavai@Sun.COM /*
573*10491SRishi.Srivatsavai@Sun.COM  * Find an existing bridge_mac_t structure or allocate a new one for the given
574*10491SRishi.Srivatsavai@Sun.COM  * bridge instance.  This creates the mac driver instance that snoop can use.
575*10491SRishi.Srivatsavai@Sun.COM  */
576*10491SRishi.Srivatsavai@Sun.COM static int
577*10491SRishi.Srivatsavai@Sun.COM bmac_alloc(bridge_inst_t *bip, bridge_mac_t **bmacp)
578*10491SRishi.Srivatsavai@Sun.COM {
579*10491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp, *bnew;
580*10491SRishi.Srivatsavai@Sun.COM 	mac_register_t *mac;
581*10491SRishi.Srivatsavai@Sun.COM 	int err;
582*10491SRishi.Srivatsavai@Sun.COM 
583*10491SRishi.Srivatsavai@Sun.COM 	*bmacp = NULL;
584*10491SRishi.Srivatsavai@Sun.COM 	if ((mac = mac_alloc(MAC_VERSION)) == NULL)
585*10491SRishi.Srivatsavai@Sun.COM 		return (EINVAL);
586*10491SRishi.Srivatsavai@Sun.COM 
587*10491SRishi.Srivatsavai@Sun.COM 	bnew = kmem_zalloc(sizeof (*bnew), KM_SLEEP);
588*10491SRishi.Srivatsavai@Sun.COM 
589*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bmac_rwlock, RW_WRITER);
590*10491SRishi.Srivatsavai@Sun.COM 	for (bmp = list_head(&bmac_list); bmp != NULL;
591*10491SRishi.Srivatsavai@Sun.COM 	    bmp = list_next(&bmac_list, bmp)) {
592*10491SRishi.Srivatsavai@Sun.COM 		if (strcmp(bip->bi_name, bmp->bm_name) == 0) {
593*10491SRishi.Srivatsavai@Sun.COM 			ASSERT(bmp->bm_inst == NULL);
594*10491SRishi.Srivatsavai@Sun.COM 			bmp->bm_inst = bip;
595*10491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bmac_rwlock);
596*10491SRishi.Srivatsavai@Sun.COM 			kmem_free(bnew, sizeof (*bnew));
597*10491SRishi.Srivatsavai@Sun.COM 			mac_free(mac);
598*10491SRishi.Srivatsavai@Sun.COM 			*bmacp = bmp;
599*10491SRishi.Srivatsavai@Sun.COM 			return (0);
600*10491SRishi.Srivatsavai@Sun.COM 		}
601*10491SRishi.Srivatsavai@Sun.COM 	}
602*10491SRishi.Srivatsavai@Sun.COM 
603*10491SRishi.Srivatsavai@Sun.COM 	mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
604*10491SRishi.Srivatsavai@Sun.COM 	mac->m_driver = bnew;
605*10491SRishi.Srivatsavai@Sun.COM 	mac->m_dip = bridge_dev_info;
606*10491SRishi.Srivatsavai@Sun.COM 	mac->m_instance = (uint_t)-1;
607*10491SRishi.Srivatsavai@Sun.COM 	mac->m_src_addr = (uint8_t *)zero_addr;
608*10491SRishi.Srivatsavai@Sun.COM 	mac->m_callbacks = &bridge_m_callbacks;
609*10491SRishi.Srivatsavai@Sun.COM 
610*10491SRishi.Srivatsavai@Sun.COM 	/*
611*10491SRishi.Srivatsavai@Sun.COM 	 * Note that the SDU limits are irrelevant, as nobody transmits on the
612*10491SRishi.Srivatsavai@Sun.COM 	 * bridge node itself.  It's mainly for monitoring but we allow
613*10491SRishi.Srivatsavai@Sun.COM 	 * setting the bridge MTU for quick transition of all links part of the
614*10491SRishi.Srivatsavai@Sun.COM 	 * bridge to a new MTU.
615*10491SRishi.Srivatsavai@Sun.COM 	 */
616*10491SRishi.Srivatsavai@Sun.COM 	mac->m_min_sdu = 1;
617*10491SRishi.Srivatsavai@Sun.COM 	mac->m_max_sdu = 1500;
618*10491SRishi.Srivatsavai@Sun.COM 	err = mac_register(mac, &bnew->bm_mh);
619*10491SRishi.Srivatsavai@Sun.COM 	mac_free(mac);
620*10491SRishi.Srivatsavai@Sun.COM 	if (err != 0) {
621*10491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bmac_rwlock);
622*10491SRishi.Srivatsavai@Sun.COM 		kmem_free(bnew, sizeof (*bnew));
623*10491SRishi.Srivatsavai@Sun.COM 		return (err);
624*10491SRishi.Srivatsavai@Sun.COM 	}
625*10491SRishi.Srivatsavai@Sun.COM 
626*10491SRishi.Srivatsavai@Sun.COM 	bnew->bm_inst = bip;
627*10491SRishi.Srivatsavai@Sun.COM 	(void) strcpy(bnew->bm_name, bip->bi_name);
628*10491SRishi.Srivatsavai@Sun.COM 	if (list_is_empty(&bmac_list)) {
629*10491SRishi.Srivatsavai@Sun.COM 		bridge_timerid = timeout(bridge_timer, NULL,
630*10491SRishi.Srivatsavai@Sun.COM 		    bridge_scan_interval);
631*10491SRishi.Srivatsavai@Sun.COM 	}
632*10491SRishi.Srivatsavai@Sun.COM 	list_insert_tail(&bmac_list, bnew);
633*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bmac_rwlock);
634*10491SRishi.Srivatsavai@Sun.COM 
635*10491SRishi.Srivatsavai@Sun.COM 	/*
636*10491SRishi.Srivatsavai@Sun.COM 	 * Mark the MAC as unable to go "active" so that only passive clients
637*10491SRishi.Srivatsavai@Sun.COM 	 * (such as snoop) can bind to it.
638*10491SRishi.Srivatsavai@Sun.COM 	 */
639*10491SRishi.Srivatsavai@Sun.COM 	mac_no_active(bnew->bm_mh);
640*10491SRishi.Srivatsavai@Sun.COM 	*bmacp = bnew;
641*10491SRishi.Srivatsavai@Sun.COM 	return (0);
642*10491SRishi.Srivatsavai@Sun.COM }
643*10491SRishi.Srivatsavai@Sun.COM 
644*10491SRishi.Srivatsavai@Sun.COM /*
645*10491SRishi.Srivatsavai@Sun.COM  * Disconnect the given bridge_mac_t from its bridge instance.  The bridge
646*10491SRishi.Srivatsavai@Sun.COM  * instance is going away.  The mac instance can't go away until the clients
647*10491SRishi.Srivatsavai@Sun.COM  * are gone (see bridge_timer).
648*10491SRishi.Srivatsavai@Sun.COM  */
649*10491SRishi.Srivatsavai@Sun.COM static void
650*10491SRishi.Srivatsavai@Sun.COM bmac_disconnect(bridge_mac_t *bmp)
651*10491SRishi.Srivatsavai@Sun.COM {
652*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
653*10491SRishi.Srivatsavai@Sun.COM 
654*10491SRishi.Srivatsavai@Sun.COM 	bmp->bm_linkstate = LINK_STATE_DOWN;
655*10491SRishi.Srivatsavai@Sun.COM 	mac_link_redo(bmp->bm_mh, LINK_STATE_DOWN);
656*10491SRishi.Srivatsavai@Sun.COM 
657*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bmac_rwlock, RW_READER);
658*10491SRishi.Srivatsavai@Sun.COM 	bip = bmp->bm_inst;
659*10491SRishi.Srivatsavai@Sun.COM 	bip->bi_mac = NULL;
660*10491SRishi.Srivatsavai@Sun.COM 	bmp->bm_inst = NULL;
661*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bmac_rwlock);
662*10491SRishi.Srivatsavai@Sun.COM }
663*10491SRishi.Srivatsavai@Sun.COM 
664*10491SRishi.Srivatsavai@Sun.COM /* This is used by the avl trees to sort forwarding table entries */
665*10491SRishi.Srivatsavai@Sun.COM static int
666*10491SRishi.Srivatsavai@Sun.COM fwd_compare(const void *addr1, const void *addr2)
667*10491SRishi.Srivatsavai@Sun.COM {
668*10491SRishi.Srivatsavai@Sun.COM 	const bridge_fwd_t *fwd1 = addr1;
669*10491SRishi.Srivatsavai@Sun.COM 	const bridge_fwd_t *fwd2 = addr2;
670*10491SRishi.Srivatsavai@Sun.COM 	int diff = memcmp(fwd1->bf_dest, fwd2->bf_dest, ETHERADDRL);
671*10491SRishi.Srivatsavai@Sun.COM 
672*10491SRishi.Srivatsavai@Sun.COM 	if (diff != 0)
673*10491SRishi.Srivatsavai@Sun.COM 		return (diff > 0 ? 1 : -1);
674*10491SRishi.Srivatsavai@Sun.COM 
675*10491SRishi.Srivatsavai@Sun.COM 	if ((fwd1->bf_flags ^ fwd2->bf_flags) & BFF_VLANLOCAL) {
676*10491SRishi.Srivatsavai@Sun.COM 		if (fwd1->bf_vlanid > fwd2->bf_vlanid)
677*10491SRishi.Srivatsavai@Sun.COM 			return (1);
678*10491SRishi.Srivatsavai@Sun.COM 		else if (fwd1->bf_vlanid < fwd2->bf_vlanid)
679*10491SRishi.Srivatsavai@Sun.COM 			return (-1);
680*10491SRishi.Srivatsavai@Sun.COM 	}
681*10491SRishi.Srivatsavai@Sun.COM 	return (0);
682*10491SRishi.Srivatsavai@Sun.COM }
683*10491SRishi.Srivatsavai@Sun.COM 
684*10491SRishi.Srivatsavai@Sun.COM static void
685*10491SRishi.Srivatsavai@Sun.COM inst_free(bridge_inst_t *bip)
686*10491SRishi.Srivatsavai@Sun.COM {
687*10491SRishi.Srivatsavai@Sun.COM 	ASSERT(bip->bi_mac == NULL);
688*10491SRishi.Srivatsavai@Sun.COM 	rw_destroy(&bip->bi_rwlock);
689*10491SRishi.Srivatsavai@Sun.COM 	list_destroy(&bip->bi_links);
690*10491SRishi.Srivatsavai@Sun.COM 	cv_destroy(&bip->bi_linkwait);
691*10491SRishi.Srivatsavai@Sun.COM 	avl_destroy(&bip->bi_fwd);
692*10491SRishi.Srivatsavai@Sun.COM 	if (bip->bi_ksp != NULL)
693*10491SRishi.Srivatsavai@Sun.COM 		kstat_delete(bip->bi_ksp);
694*10491SRishi.Srivatsavai@Sun.COM 	kmem_free(bip, sizeof (*bip));
695*10491SRishi.Srivatsavai@Sun.COM }
696*10491SRishi.Srivatsavai@Sun.COM 
697*10491SRishi.Srivatsavai@Sun.COM static bridge_inst_t *
698*10491SRishi.Srivatsavai@Sun.COM inst_alloc(const char *bridge)
699*10491SRishi.Srivatsavai@Sun.COM {
700*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
701*10491SRishi.Srivatsavai@Sun.COM 
702*10491SRishi.Srivatsavai@Sun.COM 	bip = kmem_zalloc(sizeof (*bip), KM_SLEEP);
703*10491SRishi.Srivatsavai@Sun.COM 	bip->bi_refs = 1;
704*10491SRishi.Srivatsavai@Sun.COM 	(void) strcpy(bip->bi_name, bridge);
705*10491SRishi.Srivatsavai@Sun.COM 	rw_init(&bip->bi_rwlock, NULL, RW_DRIVER, NULL);
706*10491SRishi.Srivatsavai@Sun.COM 	list_create(&bip->bi_links, sizeof (bridge_link_t),
707*10491SRishi.Srivatsavai@Sun.COM 	    offsetof(bridge_link_t, bl_node));
708*10491SRishi.Srivatsavai@Sun.COM 	cv_init(&bip->bi_linkwait, NULL, CV_DRIVER, NULL);
709*10491SRishi.Srivatsavai@Sun.COM 	avl_create(&bip->bi_fwd, fwd_compare, sizeof (bridge_fwd_t),
710*10491SRishi.Srivatsavai@Sun.COM 	    offsetof(bridge_fwd_t, bf_node));
711*10491SRishi.Srivatsavai@Sun.COM 	return (bip);
712*10491SRishi.Srivatsavai@Sun.COM }
713*10491SRishi.Srivatsavai@Sun.COM 
714*10491SRishi.Srivatsavai@Sun.COM static bridge_inst_t *
715*10491SRishi.Srivatsavai@Sun.COM bridge_find_name(const char *bridge)
716*10491SRishi.Srivatsavai@Sun.COM {
717*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
718*10491SRishi.Srivatsavai@Sun.COM 
719*10491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&inst_lock);
720*10491SRishi.Srivatsavai@Sun.COM 	for (bip = list_head(&inst_list); bip != NULL;
721*10491SRishi.Srivatsavai@Sun.COM 	    bip = list_next(&inst_list, bip)) {
722*10491SRishi.Srivatsavai@Sun.COM 		if (!(bip->bi_flags & BIF_SHUTDOWN) &&
723*10491SRishi.Srivatsavai@Sun.COM 		    strcmp(bridge, bip->bi_name) == 0) {
724*10491SRishi.Srivatsavai@Sun.COM 			atomic_inc_uint(&bip->bi_refs);
725*10491SRishi.Srivatsavai@Sun.COM 			break;
726*10491SRishi.Srivatsavai@Sun.COM 		}
727*10491SRishi.Srivatsavai@Sun.COM 	}
728*10491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&inst_lock);
729*10491SRishi.Srivatsavai@Sun.COM 
730*10491SRishi.Srivatsavai@Sun.COM 	return (bip);
731*10491SRishi.Srivatsavai@Sun.COM }
732*10491SRishi.Srivatsavai@Sun.COM 
733*10491SRishi.Srivatsavai@Sun.COM static int
734*10491SRishi.Srivatsavai@Sun.COM bridge_create(datalink_id_t linkid, const char *bridge, bridge_inst_t **bipc)
735*10491SRishi.Srivatsavai@Sun.COM {
736*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip, *bipnew;
737*10491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp = NULL;
738*10491SRishi.Srivatsavai@Sun.COM 	int err;
739*10491SRishi.Srivatsavai@Sun.COM 
740*10491SRishi.Srivatsavai@Sun.COM 	*bipc = NULL;
741*10491SRishi.Srivatsavai@Sun.COM 	bipnew = inst_alloc(bridge);
742*10491SRishi.Srivatsavai@Sun.COM 
743*10491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&inst_lock);
744*10491SRishi.Srivatsavai@Sun.COM lookup_retry:
745*10491SRishi.Srivatsavai@Sun.COM 	for (bip = list_head(&inst_list); bip != NULL;
746*10491SRishi.Srivatsavai@Sun.COM 	    bip = list_next(&inst_list, bip)) {
747*10491SRishi.Srivatsavai@Sun.COM 		if (strcmp(bridge, bip->bi_name) == 0)
748*10491SRishi.Srivatsavai@Sun.COM 			break;
749*10491SRishi.Srivatsavai@Sun.COM 	}
750*10491SRishi.Srivatsavai@Sun.COM 
751*10491SRishi.Srivatsavai@Sun.COM 	/* This should not take long; if it does, we've got a design problem */
752*10491SRishi.Srivatsavai@Sun.COM 	if (bip != NULL && (bip->bi_flags & BIF_SHUTDOWN)) {
753*10491SRishi.Srivatsavai@Sun.COM 		cv_wait(&inst_cv, &inst_lock);
754*10491SRishi.Srivatsavai@Sun.COM 		goto lookup_retry;
755*10491SRishi.Srivatsavai@Sun.COM 	}
756*10491SRishi.Srivatsavai@Sun.COM 
757*10491SRishi.Srivatsavai@Sun.COM 	if (bip != NULL) {
758*10491SRishi.Srivatsavai@Sun.COM 		/* We weren't expecting to find anything */
759*10491SRishi.Srivatsavai@Sun.COM 		bip = NULL;
760*10491SRishi.Srivatsavai@Sun.COM 		err = EEXIST;
761*10491SRishi.Srivatsavai@Sun.COM 	} else {
762*10491SRishi.Srivatsavai@Sun.COM 		bip = bipnew;
763*10491SRishi.Srivatsavai@Sun.COM 		bipnew = NULL;
764*10491SRishi.Srivatsavai@Sun.COM 		list_insert_tail(&inst_list, bip);
765*10491SRishi.Srivatsavai@Sun.COM 	}
766*10491SRishi.Srivatsavai@Sun.COM 
767*10491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&inst_lock);
768*10491SRishi.Srivatsavai@Sun.COM 	if (bip == NULL)
769*10491SRishi.Srivatsavai@Sun.COM 		goto fail;
770*10491SRishi.Srivatsavai@Sun.COM 
771*10491SRishi.Srivatsavai@Sun.COM 	bip->bi_ksp = kstat_setup((kstat_named_t *)&bip->bi_kstats,
772*10491SRishi.Srivatsavai@Sun.COM 	    inst_kstats_list, Dim(inst_kstats_list), bip->bi_name);
773*10491SRishi.Srivatsavai@Sun.COM 
774*10491SRishi.Srivatsavai@Sun.COM 	err = bmac_alloc(bip, &bmp);
775*10491SRishi.Srivatsavai@Sun.COM 	if ((bip->bi_mac = bmp) == NULL)
776*10491SRishi.Srivatsavai@Sun.COM 		goto fail_create;
777*10491SRishi.Srivatsavai@Sun.COM 
778*10491SRishi.Srivatsavai@Sun.COM 	/*
779*10491SRishi.Srivatsavai@Sun.COM 	 * bm_inst is set, so the timer cannot yank the DLS rug from under us.
780*10491SRishi.Srivatsavai@Sun.COM 	 * No extra locking is needed here.
781*10491SRishi.Srivatsavai@Sun.COM 	 */
782*10491SRishi.Srivatsavai@Sun.COM 	if (!(bmp->bm_flags & BMF_DLS)) {
783*10491SRishi.Srivatsavai@Sun.COM 		if ((err = dls_devnet_create(bmp->bm_mh, linkid)) != 0)
784*10491SRishi.Srivatsavai@Sun.COM 			goto fail_create;
785*10491SRishi.Srivatsavai@Sun.COM 		bmp->bm_flags |= BMF_DLS;
786*10491SRishi.Srivatsavai@Sun.COM 	}
787*10491SRishi.Srivatsavai@Sun.COM 
788*10491SRishi.Srivatsavai@Sun.COM 	bip->bi_dev = makedevice(bridge_major, mac_minor(bmp->bm_mh));
789*10491SRishi.Srivatsavai@Sun.COM 	*bipc = bip;
790*10491SRishi.Srivatsavai@Sun.COM 	return (0);
791*10491SRishi.Srivatsavai@Sun.COM 
792*10491SRishi.Srivatsavai@Sun.COM fail_create:
793*10491SRishi.Srivatsavai@Sun.COM 	if (bmp != NULL)
794*10491SRishi.Srivatsavai@Sun.COM 		bmac_disconnect(bip->bi_mac);
795*10491SRishi.Srivatsavai@Sun.COM 	bipnew = bip;
796*10491SRishi.Srivatsavai@Sun.COM fail:
797*10491SRishi.Srivatsavai@Sun.COM 	ASSERT(bipnew->bi_trilldata == NULL);
798*10491SRishi.Srivatsavai@Sun.COM 	bipnew->bi_flags |= BIF_SHUTDOWN;
799*10491SRishi.Srivatsavai@Sun.COM 	inst_free(bipnew);
800*10491SRishi.Srivatsavai@Sun.COM 	return (err);
801*10491SRishi.Srivatsavai@Sun.COM }
802*10491SRishi.Srivatsavai@Sun.COM 
803*10491SRishi.Srivatsavai@Sun.COM static void
804*10491SRishi.Srivatsavai@Sun.COM bridge_unref(bridge_inst_t *bip)
805*10491SRishi.Srivatsavai@Sun.COM {
806*10491SRishi.Srivatsavai@Sun.COM 	if (atomic_dec_uint_nv(&bip->bi_refs) == 0) {
807*10491SRishi.Srivatsavai@Sun.COM 		ASSERT(bip->bi_flags & BIF_SHUTDOWN);
808*10491SRishi.Srivatsavai@Sun.COM 		/* free up mac for reuse before leaving global list */
809*10491SRishi.Srivatsavai@Sun.COM 		if (bip->bi_mac != NULL)
810*10491SRishi.Srivatsavai@Sun.COM 			bmac_disconnect(bip->bi_mac);
811*10491SRishi.Srivatsavai@Sun.COM 		mutex_enter(&inst_lock);
812*10491SRishi.Srivatsavai@Sun.COM 		list_remove(&inst_list, bip);
813*10491SRishi.Srivatsavai@Sun.COM 		cv_broadcast(&inst_cv);
814*10491SRishi.Srivatsavai@Sun.COM 		mutex_exit(&inst_lock);
815*10491SRishi.Srivatsavai@Sun.COM 		inst_free(bip);
816*10491SRishi.Srivatsavai@Sun.COM 	}
817*10491SRishi.Srivatsavai@Sun.COM }
818*10491SRishi.Srivatsavai@Sun.COM 
819*10491SRishi.Srivatsavai@Sun.COM /*
820*10491SRishi.Srivatsavai@Sun.COM  * Stream instances are used only for allocating bridges and serving as a
821*10491SRishi.Srivatsavai@Sun.COM  * control node.  They serve no data-handling function.
822*10491SRishi.Srivatsavai@Sun.COM  */
823*10491SRishi.Srivatsavai@Sun.COM static bridge_stream_t *
824*10491SRishi.Srivatsavai@Sun.COM stream_alloc(void)
825*10491SRishi.Srivatsavai@Sun.COM {
826*10491SRishi.Srivatsavai@Sun.COM 	bridge_stream_t *bsp;
827*10491SRishi.Srivatsavai@Sun.COM 	minor_t mn;
828*10491SRishi.Srivatsavai@Sun.COM 
829*10491SRishi.Srivatsavai@Sun.COM 	if ((mn = mac_minor_hold(B_FALSE)) == 0)
830*10491SRishi.Srivatsavai@Sun.COM 		return (NULL);
831*10491SRishi.Srivatsavai@Sun.COM 	bsp = kmem_zalloc(sizeof (*bsp), KM_SLEEP);
832*10491SRishi.Srivatsavai@Sun.COM 	bsp->bs_minor = mn;
833*10491SRishi.Srivatsavai@Sun.COM 	return (bsp);
834*10491SRishi.Srivatsavai@Sun.COM }
835*10491SRishi.Srivatsavai@Sun.COM 
836*10491SRishi.Srivatsavai@Sun.COM static void
837*10491SRishi.Srivatsavai@Sun.COM stream_free(bridge_stream_t *bsp)
838*10491SRishi.Srivatsavai@Sun.COM {
839*10491SRishi.Srivatsavai@Sun.COM 	mac_minor_rele(bsp->bs_minor);
840*10491SRishi.Srivatsavai@Sun.COM 	kmem_free(bsp, sizeof (*bsp));
841*10491SRishi.Srivatsavai@Sun.COM }
842*10491SRishi.Srivatsavai@Sun.COM 
843*10491SRishi.Srivatsavai@Sun.COM /* Reference hold/release functions for STREAMS-related taskq */
844*10491SRishi.Srivatsavai@Sun.COM static void
845*10491SRishi.Srivatsavai@Sun.COM stream_ref(bridge_stream_t *bsp)
846*10491SRishi.Srivatsavai@Sun.COM {
847*10491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&stream_ref_lock);
848*10491SRishi.Srivatsavai@Sun.COM 	bsp->bs_taskq_cnt++;
849*10491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&stream_ref_lock);
850*10491SRishi.Srivatsavai@Sun.COM }
851*10491SRishi.Srivatsavai@Sun.COM 
852*10491SRishi.Srivatsavai@Sun.COM static void
853*10491SRishi.Srivatsavai@Sun.COM stream_unref(bridge_stream_t *bsp)
854*10491SRishi.Srivatsavai@Sun.COM {
855*10491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&stream_ref_lock);
856*10491SRishi.Srivatsavai@Sun.COM 	if (--bsp->bs_taskq_cnt == 0)
857*10491SRishi.Srivatsavai@Sun.COM 		cv_broadcast(&stream_ref_cv);
858*10491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&stream_ref_lock);
859*10491SRishi.Srivatsavai@Sun.COM }
860*10491SRishi.Srivatsavai@Sun.COM 
861*10491SRishi.Srivatsavai@Sun.COM static void
862*10491SRishi.Srivatsavai@Sun.COM link_free(bridge_link_t *blp)
863*10491SRishi.Srivatsavai@Sun.COM {
864*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;
865*10491SRishi.Srivatsavai@Sun.COM 
866*10491SRishi.Srivatsavai@Sun.COM 	ASSERT(!(blp->bl_flags & BLF_FREED));
867*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_flags |= BLF_FREED;
868*10491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_ksp != NULL)
869*10491SRishi.Srivatsavai@Sun.COM 		kstat_delete(blp->bl_ksp);
870*10491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_lfailmp != NULL)
871*10491SRishi.Srivatsavai@Sun.COM 		freeb(blp->bl_lfailmp);
872*10491SRishi.Srivatsavai@Sun.COM 	cv_destroy(&blp->bl_trillwait);
873*10491SRishi.Srivatsavai@Sun.COM 	mutex_destroy(&blp->bl_trilllock);
874*10491SRishi.Srivatsavai@Sun.COM 	kmem_free(blp, sizeof (*blp));
875*10491SRishi.Srivatsavai@Sun.COM 	/* Don't unreference the bridge until the MAC is closed */
876*10491SRishi.Srivatsavai@Sun.COM 	bridge_unref(bip);
877*10491SRishi.Srivatsavai@Sun.COM }
878*10491SRishi.Srivatsavai@Sun.COM 
879*10491SRishi.Srivatsavai@Sun.COM static void
880*10491SRishi.Srivatsavai@Sun.COM link_unref(bridge_link_t *blp)
881*10491SRishi.Srivatsavai@Sun.COM {
882*10491SRishi.Srivatsavai@Sun.COM 	if (atomic_dec_uint_nv(&blp->bl_refs) == 0) {
883*10491SRishi.Srivatsavai@Sun.COM 		bridge_inst_t *bip = blp->bl_inst;
884*10491SRishi.Srivatsavai@Sun.COM 
885*10491SRishi.Srivatsavai@Sun.COM 		ASSERT(blp->bl_flags & BLF_DELETED);
886*10491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_WRITER);
887*10491SRishi.Srivatsavai@Sun.COM 		list_remove(&bip->bi_links, blp);
888*10491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
889*10491SRishi.Srivatsavai@Sun.COM 		if (bip->bi_trilldata != NULL && list_is_empty(&bip->bi_links))
890*10491SRishi.Srivatsavai@Sun.COM 			cv_broadcast(&bip->bi_linkwait);
891*10491SRishi.Srivatsavai@Sun.COM 		link_free(blp);
892*10491SRishi.Srivatsavai@Sun.COM 	}
893*10491SRishi.Srivatsavai@Sun.COM }
894*10491SRishi.Srivatsavai@Sun.COM 
895*10491SRishi.Srivatsavai@Sun.COM static bridge_fwd_t *
896*10491SRishi.Srivatsavai@Sun.COM fwd_alloc(const uint8_t *addr, uint_t nlinks, uint16_t nick)
897*10491SRishi.Srivatsavai@Sun.COM {
898*10491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp;
899*10491SRishi.Srivatsavai@Sun.COM 
900*10491SRishi.Srivatsavai@Sun.COM 	bfp = kmem_zalloc(sizeof (*bfp) + (nlinks * sizeof (bridge_link_t *)),
901*10491SRishi.Srivatsavai@Sun.COM 	    KM_NOSLEEP);
902*10491SRishi.Srivatsavai@Sun.COM 	if (bfp != NULL) {
903*10491SRishi.Srivatsavai@Sun.COM 		bcopy(addr, bfp->bf_dest, ETHERADDRL);
904*10491SRishi.Srivatsavai@Sun.COM 		bfp->bf_lastheard = lbolt;
905*10491SRishi.Srivatsavai@Sun.COM 		bfp->bf_maxlinks = nlinks;
906*10491SRishi.Srivatsavai@Sun.COM 		bfp->bf_links = (bridge_link_t **)(bfp + 1);
907*10491SRishi.Srivatsavai@Sun.COM 		bfp->bf_trill_nick = nick;
908*10491SRishi.Srivatsavai@Sun.COM 	}
909*10491SRishi.Srivatsavai@Sun.COM 	return (bfp);
910*10491SRishi.Srivatsavai@Sun.COM }
911*10491SRishi.Srivatsavai@Sun.COM 
912*10491SRishi.Srivatsavai@Sun.COM static bridge_fwd_t *
913*10491SRishi.Srivatsavai@Sun.COM fwd_find(bridge_inst_t *bip, const uint8_t *addr, uint16_t vlanid)
914*10491SRishi.Srivatsavai@Sun.COM {
915*10491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp, *vbfp;
916*10491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t match;
917*10491SRishi.Srivatsavai@Sun.COM 
918*10491SRishi.Srivatsavai@Sun.COM 	bcopy(addr, match.bf_dest, ETHERADDRL);
919*10491SRishi.Srivatsavai@Sun.COM 	match.bf_flags = 0;
920*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_READER);
921*10491SRishi.Srivatsavai@Sun.COM 	if ((bfp = avl_find(&bip->bi_fwd, &match, NULL)) != NULL) {
922*10491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_vlanid != vlanid && bfp->bf_vcnt > 0) {
923*10491SRishi.Srivatsavai@Sun.COM 			match.bf_vlanid = vlanid;
924*10491SRishi.Srivatsavai@Sun.COM 			match.bf_flags = BFF_VLANLOCAL;
925*10491SRishi.Srivatsavai@Sun.COM 			vbfp = avl_find(&bip->bi_fwd, &match, NULL);
926*10491SRishi.Srivatsavai@Sun.COM 			if (vbfp != NULL)
927*10491SRishi.Srivatsavai@Sun.COM 				bfp = vbfp;
928*10491SRishi.Srivatsavai@Sun.COM 		}
929*10491SRishi.Srivatsavai@Sun.COM 		atomic_inc_uint(&bfp->bf_refs);
930*10491SRishi.Srivatsavai@Sun.COM 	}
931*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
932*10491SRishi.Srivatsavai@Sun.COM 	return (bfp);
933*10491SRishi.Srivatsavai@Sun.COM }
934*10491SRishi.Srivatsavai@Sun.COM 
935*10491SRishi.Srivatsavai@Sun.COM static void
936*10491SRishi.Srivatsavai@Sun.COM fwd_free(bridge_fwd_t *bfp)
937*10491SRishi.Srivatsavai@Sun.COM {
938*10491SRishi.Srivatsavai@Sun.COM 	uint_t i;
939*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = bfp->bf_links[0]->bl_inst;
940*10491SRishi.Srivatsavai@Sun.COM 
941*10491SRishi.Srivatsavai@Sun.COM 	KIDECR(bki_count);
942*10491SRishi.Srivatsavai@Sun.COM 	for (i = 0; i < bfp->bf_nlinks; i++)
943*10491SRishi.Srivatsavai@Sun.COM 		link_unref(bfp->bf_links[i]);
944*10491SRishi.Srivatsavai@Sun.COM 	kmem_free(bfp,
945*10491SRishi.Srivatsavai@Sun.COM 	    sizeof (*bfp) + bfp->bf_maxlinks * sizeof (bridge_link_t *));
946*10491SRishi.Srivatsavai@Sun.COM }
947*10491SRishi.Srivatsavai@Sun.COM 
948*10491SRishi.Srivatsavai@Sun.COM static void
949*10491SRishi.Srivatsavai@Sun.COM fwd_unref(bridge_fwd_t *bfp)
950*10491SRishi.Srivatsavai@Sun.COM {
951*10491SRishi.Srivatsavai@Sun.COM 	if (atomic_dec_uint_nv(&bfp->bf_refs) == 0) {
952*10491SRishi.Srivatsavai@Sun.COM 		ASSERT(!(bfp->bf_flags & BFF_INTREE));
953*10491SRishi.Srivatsavai@Sun.COM 		fwd_free(bfp);
954*10491SRishi.Srivatsavai@Sun.COM 	}
955*10491SRishi.Srivatsavai@Sun.COM }
956*10491SRishi.Srivatsavai@Sun.COM 
957*10491SRishi.Srivatsavai@Sun.COM static void
958*10491SRishi.Srivatsavai@Sun.COM fwd_delete(bridge_fwd_t *bfp)
959*10491SRishi.Srivatsavai@Sun.COM {
960*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
961*10491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfpzero;
962*10491SRishi.Srivatsavai@Sun.COM 
963*10491SRishi.Srivatsavai@Sun.COM 	if (bfp->bf_flags & BFF_INTREE) {
964*10491SRishi.Srivatsavai@Sun.COM 		ASSERT(bfp->bf_nlinks > 0);
965*10491SRishi.Srivatsavai@Sun.COM 		bip = bfp->bf_links[0]->bl_inst;
966*10491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_WRITER);
967*10491SRishi.Srivatsavai@Sun.COM 		/* Another thread could beat us to this */
968*10491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_flags & BFF_INTREE) {
969*10491SRishi.Srivatsavai@Sun.COM 			avl_remove(&bip->bi_fwd, bfp);
970*10491SRishi.Srivatsavai@Sun.COM 			bfp->bf_flags &= ~BFF_INTREE;
971*10491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_flags & BFF_VLANLOCAL) {
972*10491SRishi.Srivatsavai@Sun.COM 				bfp->bf_flags &= ~BFF_VLANLOCAL;
973*10491SRishi.Srivatsavai@Sun.COM 				bfpzero = avl_find(&bip->bi_fwd, bfp, NULL);
974*10491SRishi.Srivatsavai@Sun.COM 				if (bfpzero != NULL && bfpzero->bf_vcnt > 0)
975*10491SRishi.Srivatsavai@Sun.COM 					bfpzero->bf_vcnt--;
976*10491SRishi.Srivatsavai@Sun.COM 			}
977*10491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bip->bi_rwlock);
978*10491SRishi.Srivatsavai@Sun.COM 			fwd_unref(bfp);		/* no longer in avl tree */
979*10491SRishi.Srivatsavai@Sun.COM 		} else {
980*10491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bip->bi_rwlock);
981*10491SRishi.Srivatsavai@Sun.COM 		}
982*10491SRishi.Srivatsavai@Sun.COM 	}
983*10491SRishi.Srivatsavai@Sun.COM }
984*10491SRishi.Srivatsavai@Sun.COM 
985*10491SRishi.Srivatsavai@Sun.COM static boolean_t
986*10491SRishi.Srivatsavai@Sun.COM fwd_insert(bridge_inst_t *bip, bridge_fwd_t *bfp)
987*10491SRishi.Srivatsavai@Sun.COM {
988*10491SRishi.Srivatsavai@Sun.COM 	avl_index_t idx;
989*10491SRishi.Srivatsavai@Sun.COM 	boolean_t retv;
990*10491SRishi.Srivatsavai@Sun.COM 
991*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_WRITER);
992*10491SRishi.Srivatsavai@Sun.COM 	if (!(bip->bi_flags & BIF_SHUTDOWN) &&
993*10491SRishi.Srivatsavai@Sun.COM 	    avl_numnodes(&bip->bi_fwd) < bip->bi_tablemax &&
994*10491SRishi.Srivatsavai@Sun.COM 	    avl_find(&bip->bi_fwd, bfp, &idx) == NULL) {
995*10491SRishi.Srivatsavai@Sun.COM 		avl_insert(&bip->bi_fwd, bfp, idx);
996*10491SRishi.Srivatsavai@Sun.COM 		bfp->bf_flags |= BFF_INTREE;
997*10491SRishi.Srivatsavai@Sun.COM 		atomic_inc_uint(&bfp->bf_refs);	/* avl entry */
998*10491SRishi.Srivatsavai@Sun.COM 		retv = B_TRUE;
999*10491SRishi.Srivatsavai@Sun.COM 	} else {
1000*10491SRishi.Srivatsavai@Sun.COM 		retv = B_FALSE;
1001*10491SRishi.Srivatsavai@Sun.COM 	}
1002*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
1003*10491SRishi.Srivatsavai@Sun.COM 	return (retv);
1004*10491SRishi.Srivatsavai@Sun.COM }
1005*10491SRishi.Srivatsavai@Sun.COM 
1006*10491SRishi.Srivatsavai@Sun.COM static void
1007*10491SRishi.Srivatsavai@Sun.COM fwd_update_local(bridge_link_t *blp, const uint8_t *oldaddr,
1008*10491SRishi.Srivatsavai@Sun.COM     const uint8_t *newaddr)
1009*10491SRishi.Srivatsavai@Sun.COM {
1010*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;
1011*10491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp, *bfnew;
1012*10491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t match;
1013*10491SRishi.Srivatsavai@Sun.COM 	avl_index_t idx;
1014*10491SRishi.Srivatsavai@Sun.COM 	boolean_t drop_ref = B_FALSE;
1015*10491SRishi.Srivatsavai@Sun.COM 
1016*10491SRishi.Srivatsavai@Sun.COM 	if (bcmp(oldaddr, newaddr, ETHERADDRL) == 0)
1017*10491SRishi.Srivatsavai@Sun.COM 		return;
1018*10491SRishi.Srivatsavai@Sun.COM 
1019*10491SRishi.Srivatsavai@Sun.COM 	if (bcmp(oldaddr, zero_addr, ETHERADDRL) == 0)
1020*10491SRishi.Srivatsavai@Sun.COM 		goto no_old_addr;
1021*10491SRishi.Srivatsavai@Sun.COM 
1022*10491SRishi.Srivatsavai@Sun.COM 	/*
1023*10491SRishi.Srivatsavai@Sun.COM 	 * Find the previous entry, and remove our link from it.
1024*10491SRishi.Srivatsavai@Sun.COM 	 */
1025*10491SRishi.Srivatsavai@Sun.COM 	bcopy(oldaddr, match.bf_dest, ETHERADDRL);
1026*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_WRITER);
1027*10491SRishi.Srivatsavai@Sun.COM 	if ((bfp = avl_find(&bip->bi_fwd, &match, NULL)) != NULL) {
1028*10491SRishi.Srivatsavai@Sun.COM 		int i;
1029*10491SRishi.Srivatsavai@Sun.COM 
1030*10491SRishi.Srivatsavai@Sun.COM 		/*
1031*10491SRishi.Srivatsavai@Sun.COM 		 * See if we're in the list, and remove if so.
1032*10491SRishi.Srivatsavai@Sun.COM 		 */
1033*10491SRishi.Srivatsavai@Sun.COM 		for (i = 0; i < bfp->bf_nlinks; i++) {
1034*10491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_links[i] == blp) {
1035*10491SRishi.Srivatsavai@Sun.COM 				/*
1036*10491SRishi.Srivatsavai@Sun.COM 				 * We assume writes are atomic, so no special
1037*10491SRishi.Srivatsavai@Sun.COM 				 * MT handling is needed.  The list length is
1038*10491SRishi.Srivatsavai@Sun.COM 				 * decremented first, and then we remove
1039*10491SRishi.Srivatsavai@Sun.COM 				 * entries.
1040*10491SRishi.Srivatsavai@Sun.COM 				 */
1041*10491SRishi.Srivatsavai@Sun.COM 				bfp->bf_nlinks--;
1042*10491SRishi.Srivatsavai@Sun.COM 				for (; i < bfp->bf_nlinks; i++)
1043*10491SRishi.Srivatsavai@Sun.COM 					bfp->bf_links[i] = bfp->bf_links[i + 1];
1044*10491SRishi.Srivatsavai@Sun.COM 				drop_ref = B_TRUE;
1045*10491SRishi.Srivatsavai@Sun.COM 				break;
1046*10491SRishi.Srivatsavai@Sun.COM 			}
1047*10491SRishi.Srivatsavai@Sun.COM 		}
1048*10491SRishi.Srivatsavai@Sun.COM 		/* If no more links, then remove and free up */
1049*10491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_nlinks == 0) {
1050*10491SRishi.Srivatsavai@Sun.COM 			avl_remove(&bip->bi_fwd, bfp);
1051*10491SRishi.Srivatsavai@Sun.COM 			bfp->bf_flags &= ~BFF_INTREE;
1052*10491SRishi.Srivatsavai@Sun.COM 		} else {
1053*10491SRishi.Srivatsavai@Sun.COM 			bfp = NULL;
1054*10491SRishi.Srivatsavai@Sun.COM 		}
1055*10491SRishi.Srivatsavai@Sun.COM 	}
1056*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
1057*10491SRishi.Srivatsavai@Sun.COM 	if (bfp != NULL)
1058*10491SRishi.Srivatsavai@Sun.COM 		fwd_unref(bfp);		/* no longer in avl tree */
1059*10491SRishi.Srivatsavai@Sun.COM 
1060*10491SRishi.Srivatsavai@Sun.COM 	/*
1061*10491SRishi.Srivatsavai@Sun.COM 	 * Now get the new link address and add this link to the list.  The
1062*10491SRishi.Srivatsavai@Sun.COM 	 * list should be of length 1 unless the user has configured multiple
1063*10491SRishi.Srivatsavai@Sun.COM 	 * NICs with the same address.  (That's an incorrect configuration, but
1064*10491SRishi.Srivatsavai@Sun.COM 	 * we support it anyway.)
1065*10491SRishi.Srivatsavai@Sun.COM 	 */
1066*10491SRishi.Srivatsavai@Sun.COM no_old_addr:
1067*10491SRishi.Srivatsavai@Sun.COM 	bfp = NULL;
1068*10491SRishi.Srivatsavai@Sun.COM 	if ((bip->bi_flags & BIF_SHUTDOWN) ||
1069*10491SRishi.Srivatsavai@Sun.COM 	    bcmp(newaddr, zero_addr, ETHERADDRL) == 0)
1070*10491SRishi.Srivatsavai@Sun.COM 		goto no_new_addr;
1071*10491SRishi.Srivatsavai@Sun.COM 
1072*10491SRishi.Srivatsavai@Sun.COM 	bcopy(newaddr, match.bf_dest, ETHERADDRL);
1073*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_WRITER);
1074*10491SRishi.Srivatsavai@Sun.COM 	if ((bfp = avl_find(&bip->bi_fwd, &match, &idx)) == NULL) {
1075*10491SRishi.Srivatsavai@Sun.COM 		bfnew = fwd_alloc(newaddr, 1, RBRIDGE_NICKNAME_NONE);
1076*10491SRishi.Srivatsavai@Sun.COM 		if (bfnew != NULL)
1077*10491SRishi.Srivatsavai@Sun.COM 			KIINCR(bki_count);
1078*10491SRishi.Srivatsavai@Sun.COM 	} else if (bfp->bf_nlinks < bfp->bf_maxlinks) {
1079*10491SRishi.Srivatsavai@Sun.COM 		/* special case: link fits in existing entry */
1080*10491SRishi.Srivatsavai@Sun.COM 		bfnew = bfp;
1081*10491SRishi.Srivatsavai@Sun.COM 	} else {
1082*10491SRishi.Srivatsavai@Sun.COM 		bfnew = fwd_alloc(newaddr, bfp->bf_nlinks + 1,
1083*10491SRishi.Srivatsavai@Sun.COM 		    RBRIDGE_NICKNAME_NONE);
1084*10491SRishi.Srivatsavai@Sun.COM 		if (bfnew != NULL) {
1085*10491SRishi.Srivatsavai@Sun.COM 			KIINCR(bki_count);
1086*10491SRishi.Srivatsavai@Sun.COM 			avl_remove(&bip->bi_fwd, bfp);
1087*10491SRishi.Srivatsavai@Sun.COM 			bfp->bf_flags &= ~BFF_INTREE;
1088*10491SRishi.Srivatsavai@Sun.COM 			bfnew->bf_nlinks = bfp->bf_nlinks;
1089*10491SRishi.Srivatsavai@Sun.COM 			bcopy(bfp->bf_links, bfnew->bf_links,
1090*10491SRishi.Srivatsavai@Sun.COM 			    bfp->bf_nlinks * sizeof (bfp));
1091*10491SRishi.Srivatsavai@Sun.COM 			/* reset the idx value due to removal above */
1092*10491SRishi.Srivatsavai@Sun.COM 			(void) avl_find(&bip->bi_fwd, &match, &idx);
1093*10491SRishi.Srivatsavai@Sun.COM 		}
1094*10491SRishi.Srivatsavai@Sun.COM 	}
1095*10491SRishi.Srivatsavai@Sun.COM 
1096*10491SRishi.Srivatsavai@Sun.COM 	if (bfnew != NULL) {
1097*10491SRishi.Srivatsavai@Sun.COM 		bfnew->bf_links[bfnew->bf_nlinks++] = blp;
1098*10491SRishi.Srivatsavai@Sun.COM 		if (drop_ref)
1099*10491SRishi.Srivatsavai@Sun.COM 			drop_ref = B_FALSE;
1100*10491SRishi.Srivatsavai@Sun.COM 		else
1101*10491SRishi.Srivatsavai@Sun.COM 			atomic_inc_uint(&blp->bl_refs);	/* bf_links entry */
1102*10491SRishi.Srivatsavai@Sun.COM 
1103*10491SRishi.Srivatsavai@Sun.COM 		if (bfnew != bfp) {
1104*10491SRishi.Srivatsavai@Sun.COM 			/* local addresses are not subject to table limits */
1105*10491SRishi.Srivatsavai@Sun.COM 			avl_insert(&bip->bi_fwd, bfnew, idx);
1106*10491SRishi.Srivatsavai@Sun.COM 			bfnew->bf_flags |= (BFF_INTREE | BFF_LOCALADDR);
1107*10491SRishi.Srivatsavai@Sun.COM 			atomic_inc_uint(&bfnew->bf_refs);	/* avl entry */
1108*10491SRishi.Srivatsavai@Sun.COM 		}
1109*10491SRishi.Srivatsavai@Sun.COM 	}
1110*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
1111*10491SRishi.Srivatsavai@Sun.COM 
1112*10491SRishi.Srivatsavai@Sun.COM no_new_addr:
1113*10491SRishi.Srivatsavai@Sun.COM 	/*
1114*10491SRishi.Srivatsavai@Sun.COM 	 * If we found an existing entry and we replaced it with a new one,
1115*10491SRishi.Srivatsavai@Sun.COM 	 * then drop the table reference from the old one.  We removed it from
1116*10491SRishi.Srivatsavai@Sun.COM 	 * the AVL tree above.
1117*10491SRishi.Srivatsavai@Sun.COM 	 */
1118*10491SRishi.Srivatsavai@Sun.COM 	if (bfnew != NULL && bfp != NULL && bfnew != bfp)
1119*10491SRishi.Srivatsavai@Sun.COM 		fwd_unref(bfp);
1120*10491SRishi.Srivatsavai@Sun.COM 
1121*10491SRishi.Srivatsavai@Sun.COM 	/* Account for removed entry. */
1122*10491SRishi.Srivatsavai@Sun.COM 	if (drop_ref)
1123*10491SRishi.Srivatsavai@Sun.COM 		link_unref(blp);
1124*10491SRishi.Srivatsavai@Sun.COM }
1125*10491SRishi.Srivatsavai@Sun.COM 
1126*10491SRishi.Srivatsavai@Sun.COM static void
1127*10491SRishi.Srivatsavai@Sun.COM bridge_new_unicst(bridge_link_t *blp)
1128*10491SRishi.Srivatsavai@Sun.COM {
1129*10491SRishi.Srivatsavai@Sun.COM 	uint8_t new_mac[ETHERADDRL];
1130*10491SRishi.Srivatsavai@Sun.COM 
1131*10491SRishi.Srivatsavai@Sun.COM 	mac_unicast_primary_get(blp->bl_mh, new_mac);
1132*10491SRishi.Srivatsavai@Sun.COM 	fwd_update_local(blp, blp->bl_local_mac, new_mac);
1133*10491SRishi.Srivatsavai@Sun.COM 	bcopy(new_mac, blp->bl_local_mac, ETHERADDRL);
1134*10491SRishi.Srivatsavai@Sun.COM }
1135*10491SRishi.Srivatsavai@Sun.COM 
1136*10491SRishi.Srivatsavai@Sun.COM /*
1137*10491SRishi.Srivatsavai@Sun.COM  * We must shut down a link prior to freeing it, and doing that requires
1138*10491SRishi.Srivatsavai@Sun.COM  * blocking to wait for running MAC threads while holding a reference.  This is
1139*10491SRishi.Srivatsavai@Sun.COM  * run from a taskq to accomplish proper link shutdown followed by reference
1140*10491SRishi.Srivatsavai@Sun.COM  * drop.
1141*10491SRishi.Srivatsavai@Sun.COM  */
1142*10491SRishi.Srivatsavai@Sun.COM static void
1143*10491SRishi.Srivatsavai@Sun.COM link_shutdown(void *arg)
1144*10491SRishi.Srivatsavai@Sun.COM {
1145*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp = arg;
1146*10491SRishi.Srivatsavai@Sun.COM 	mac_handle_t mh = blp->bl_mh;
1147*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
1148*10491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp, *bfnext;
1149*10491SRishi.Srivatsavai@Sun.COM 	avl_tree_t fwd_scavenge;
1150*10491SRishi.Srivatsavai@Sun.COM 	int i;
1151*10491SRishi.Srivatsavai@Sun.COM 
1152*10491SRishi.Srivatsavai@Sun.COM 	/*
1153*10491SRishi.Srivatsavai@Sun.COM 	 * This link is being destroyed.  Notify TRILL now that it's no longer
1154*10491SRishi.Srivatsavai@Sun.COM 	 * possible to send packets.  Data packets may still arrive until TRILL
1155*10491SRishi.Srivatsavai@Sun.COM 	 * calls bridge_trill_lnunref.
1156*10491SRishi.Srivatsavai@Sun.COM 	 */
1157*10491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_trilldata != NULL)
1158*10491SRishi.Srivatsavai@Sun.COM 		trill_lndstr_fn(blp->bl_trilldata, blp);
1159*10491SRishi.Srivatsavai@Sun.COM 
1160*10491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_flags & BLF_PROM_ADDED)
1161*10491SRishi.Srivatsavai@Sun.COM 		(void) mac_promisc_remove(blp->bl_mphp);
1162*10491SRishi.Srivatsavai@Sun.COM 
1163*10491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_flags & BLF_SET_BRIDGE)
1164*10491SRishi.Srivatsavai@Sun.COM 		mac_bridge_clear(mh, (mac_handle_t)blp);
1165*10491SRishi.Srivatsavai@Sun.COM 
1166*10491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_flags & BLF_MARGIN_ADDED) {
1167*10491SRishi.Srivatsavai@Sun.COM 		mac_notify_remove(blp->bl_mnh, B_TRUE);
1168*10491SRishi.Srivatsavai@Sun.COM 		(void) mac_margin_remove(mh, blp->bl_margin);
1169*10491SRishi.Srivatsavai@Sun.COM 	}
1170*10491SRishi.Srivatsavai@Sun.COM 
1171*10491SRishi.Srivatsavai@Sun.COM 	/* Tell the clients the real link state when we leave */
1172*10491SRishi.Srivatsavai@Sun.COM 	mac_link_redo(blp->bl_mh,
1173*10491SRishi.Srivatsavai@Sun.COM 	    mac_stat_get(blp->bl_mh, MAC_STAT_LOWLINK_STATE));
1174*10491SRishi.Srivatsavai@Sun.COM 
1175*10491SRishi.Srivatsavai@Sun.COM 	/* Destroy all of the forwarding entries related to this link */
1176*10491SRishi.Srivatsavai@Sun.COM 	avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t),
1177*10491SRishi.Srivatsavai@Sun.COM 	    offsetof(bridge_fwd_t, bf_node));
1178*10491SRishi.Srivatsavai@Sun.COM 	bip = blp->bl_inst;
1179*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_WRITER);
1180*10491SRishi.Srivatsavai@Sun.COM 	bfnext = avl_first(&bip->bi_fwd);
1181*10491SRishi.Srivatsavai@Sun.COM 	while ((bfp = bfnext) != NULL) {
1182*10491SRishi.Srivatsavai@Sun.COM 		bfnext = AVL_NEXT(&bip->bi_fwd, bfp);
1183*10491SRishi.Srivatsavai@Sun.COM 		for (i = 0; i < bfp->bf_nlinks; i++) {
1184*10491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_links[i] == blp)
1185*10491SRishi.Srivatsavai@Sun.COM 				break;
1186*10491SRishi.Srivatsavai@Sun.COM 		}
1187*10491SRishi.Srivatsavai@Sun.COM 		if (i >= bfp->bf_nlinks)
1188*10491SRishi.Srivatsavai@Sun.COM 			continue;
1189*10491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_nlinks > 1) {
1190*10491SRishi.Srivatsavai@Sun.COM 			/* note that this can't be the last reference */
1191*10491SRishi.Srivatsavai@Sun.COM 			link_unref(blp);
1192*10491SRishi.Srivatsavai@Sun.COM 			bfp->bf_nlinks--;
1193*10491SRishi.Srivatsavai@Sun.COM 			for (; i < bfp->bf_nlinks; i++)
1194*10491SRishi.Srivatsavai@Sun.COM 				bfp->bf_links[i] = bfp->bf_links[i + 1];
1195*10491SRishi.Srivatsavai@Sun.COM 		} else {
1196*10491SRishi.Srivatsavai@Sun.COM 			ASSERT(bfp->bf_flags & BFF_INTREE);
1197*10491SRishi.Srivatsavai@Sun.COM 			avl_remove(&bip->bi_fwd, bfp);
1198*10491SRishi.Srivatsavai@Sun.COM 			bfp->bf_flags &= ~BFF_INTREE;
1199*10491SRishi.Srivatsavai@Sun.COM 			avl_add(&fwd_scavenge, bfp);
1200*10491SRishi.Srivatsavai@Sun.COM 		}
1201*10491SRishi.Srivatsavai@Sun.COM 	}
1202*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
1203*10491SRishi.Srivatsavai@Sun.COM 	bfnext = avl_first(&fwd_scavenge);
1204*10491SRishi.Srivatsavai@Sun.COM 	while ((bfp = bfnext) != NULL) {
1205*10491SRishi.Srivatsavai@Sun.COM 		bfnext = AVL_NEXT(&fwd_scavenge, bfp);
1206*10491SRishi.Srivatsavai@Sun.COM 		avl_remove(&fwd_scavenge, bfp);
1207*10491SRishi.Srivatsavai@Sun.COM 		fwd_unref(bfp);
1208*10491SRishi.Srivatsavai@Sun.COM 	}
1209*10491SRishi.Srivatsavai@Sun.COM 	avl_destroy(&fwd_scavenge);
1210*10491SRishi.Srivatsavai@Sun.COM 
1211*10491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_flags & BLF_CLIENT_OPEN)
1212*10491SRishi.Srivatsavai@Sun.COM 		mac_client_close(blp->bl_mch, 0);
1213*10491SRishi.Srivatsavai@Sun.COM 
1214*10491SRishi.Srivatsavai@Sun.COM 	mac_close(mh);
1215*10491SRishi.Srivatsavai@Sun.COM 
1216*10491SRishi.Srivatsavai@Sun.COM 	/*
1217*10491SRishi.Srivatsavai@Sun.COM 	 * We are now completely removed from the active list, so drop the
1218*10491SRishi.Srivatsavai@Sun.COM 	 * reference (see bridge_add_link).
1219*10491SRishi.Srivatsavai@Sun.COM 	 */
1220*10491SRishi.Srivatsavai@Sun.COM 	link_unref(blp);
1221*10491SRishi.Srivatsavai@Sun.COM }
1222*10491SRishi.Srivatsavai@Sun.COM 
1223*10491SRishi.Srivatsavai@Sun.COM static void
1224*10491SRishi.Srivatsavai@Sun.COM shutdown_inst(bridge_inst_t *bip)
1225*10491SRishi.Srivatsavai@Sun.COM {
1226*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp, *blnext;
1227*10491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp;
1228*10491SRishi.Srivatsavai@Sun.COM 
1229*10491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&inst_lock);
1230*10491SRishi.Srivatsavai@Sun.COM 	if (bip->bi_flags & BIF_SHUTDOWN) {
1231*10491SRishi.Srivatsavai@Sun.COM 		mutex_exit(&inst_lock);
1232*10491SRishi.Srivatsavai@Sun.COM 		return;
1233*10491SRishi.Srivatsavai@Sun.COM 	}
1234*10491SRishi.Srivatsavai@Sun.COM 
1235*10491SRishi.Srivatsavai@Sun.COM 	/*
1236*10491SRishi.Srivatsavai@Sun.COM 	 * Once on the inst_list, the bridge instance must not leave that list
1237*10491SRishi.Srivatsavai@Sun.COM 	 * without having the shutdown flag set first.  When the shutdown flag
1238*10491SRishi.Srivatsavai@Sun.COM 	 * is set, we own the list reference, so we must drop it before
1239*10491SRishi.Srivatsavai@Sun.COM 	 * returning.
1240*10491SRishi.Srivatsavai@Sun.COM 	 */
1241*10491SRishi.Srivatsavai@Sun.COM 	bip->bi_flags |= BIF_SHUTDOWN;
1242*10491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&inst_lock);
1243*10491SRishi.Srivatsavai@Sun.COM 
1244*10491SRishi.Srivatsavai@Sun.COM 	bip->bi_control = NULL;
1245*10491SRishi.Srivatsavai@Sun.COM 
1246*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_READER);
1247*10491SRishi.Srivatsavai@Sun.COM 	blnext = list_head(&bip->bi_links);
1248*10491SRishi.Srivatsavai@Sun.COM 	while ((blp = blnext) != NULL) {
1249*10491SRishi.Srivatsavai@Sun.COM 		blnext = list_next(&bip->bi_links, blp);
1250*10491SRishi.Srivatsavai@Sun.COM 		if (!(blp->bl_flags & BLF_DELETED)) {
1251*10491SRishi.Srivatsavai@Sun.COM 			blp->bl_flags |= BLF_DELETED;
1252*10491SRishi.Srivatsavai@Sun.COM 			(void) ddi_taskq_dispatch(bridge_taskq, link_shutdown,
1253*10491SRishi.Srivatsavai@Sun.COM 			    blp, DDI_SLEEP);
1254*10491SRishi.Srivatsavai@Sun.COM 		}
1255*10491SRishi.Srivatsavai@Sun.COM 	}
1256*10491SRishi.Srivatsavai@Sun.COM 	while ((bfp = avl_first(&bip->bi_fwd)) != NULL) {
1257*10491SRishi.Srivatsavai@Sun.COM 		atomic_inc_uint(&bfp->bf_refs);
1258*10491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
1259*10491SRishi.Srivatsavai@Sun.COM 		fwd_delete(bfp);
1260*10491SRishi.Srivatsavai@Sun.COM 		fwd_unref(bfp);
1261*10491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_READER);
1262*10491SRishi.Srivatsavai@Sun.COM 	}
1263*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
1264*10491SRishi.Srivatsavai@Sun.COM 
1265*10491SRishi.Srivatsavai@Sun.COM 	/*
1266*10491SRishi.Srivatsavai@Sun.COM 	 * This bridge is being destroyed.  Notify TRILL once all of the
1267*10491SRishi.Srivatsavai@Sun.COM 	 * links are all gone.
1268*10491SRishi.Srivatsavai@Sun.COM 	 */
1269*10491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&inst_lock);
1270*10491SRishi.Srivatsavai@Sun.COM 	while (bip->bi_trilldata != NULL && !list_is_empty(&bip->bi_links))
1271*10491SRishi.Srivatsavai@Sun.COM 		cv_wait(&bip->bi_linkwait, &inst_lock);
1272*10491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&inst_lock);
1273*10491SRishi.Srivatsavai@Sun.COM 	if (bip->bi_trilldata != NULL)
1274*10491SRishi.Srivatsavai@Sun.COM 		trill_brdstr_fn(bip->bi_trilldata, bip);
1275*10491SRishi.Srivatsavai@Sun.COM 
1276*10491SRishi.Srivatsavai@Sun.COM 	bridge_unref(bip);
1277*10491SRishi.Srivatsavai@Sun.COM }
1278*10491SRishi.Srivatsavai@Sun.COM 
1279*10491SRishi.Srivatsavai@Sun.COM /*
1280*10491SRishi.Srivatsavai@Sun.COM  * This is called once by the TRILL module when it starts up.  It just sets the
1281*10491SRishi.Srivatsavai@Sun.COM  * global TRILL callback function pointers -- data transmit/receive and bridge
1282*10491SRishi.Srivatsavai@Sun.COM  * and link destroy notification.  There's only one TRILL module, so only one
1283*10491SRishi.Srivatsavai@Sun.COM  * registration is needed.
1284*10491SRishi.Srivatsavai@Sun.COM  *
1285*10491SRishi.Srivatsavai@Sun.COM  * TRILL should call this function with NULL pointers before unloading.  It
1286*10491SRishi.Srivatsavai@Sun.COM  * must not do so before dropping all references to bridges and links.  We
1287*10491SRishi.Srivatsavai@Sun.COM  * assert that this is true on debug builds.
1288*10491SRishi.Srivatsavai@Sun.COM  */
1289*10491SRishi.Srivatsavai@Sun.COM void
1290*10491SRishi.Srivatsavai@Sun.COM bridge_trill_register_cb(trill_recv_pkt_t recv_fn, trill_encap_pkt_t encap_fn,
1291*10491SRishi.Srivatsavai@Sun.COM     trill_br_dstr_t brdstr_fn, trill_ln_dstr_t lndstr_fn)
1292*10491SRishi.Srivatsavai@Sun.COM {
1293*10491SRishi.Srivatsavai@Sun.COM #ifdef DEBUG
1294*10491SRishi.Srivatsavai@Sun.COM 	if (recv_fn == NULL && trill_recv_fn != NULL) {
1295*10491SRishi.Srivatsavai@Sun.COM 		bridge_inst_t *bip;
1296*10491SRishi.Srivatsavai@Sun.COM 		bridge_link_t *blp;
1297*10491SRishi.Srivatsavai@Sun.COM 
1298*10491SRishi.Srivatsavai@Sun.COM 		mutex_enter(&inst_lock);
1299*10491SRishi.Srivatsavai@Sun.COM 		for (bip = list_head(&inst_list); bip != NULL;
1300*10491SRishi.Srivatsavai@Sun.COM 		    bip = list_next(&inst_list, bip)) {
1301*10491SRishi.Srivatsavai@Sun.COM 			ASSERT(bip->bi_trilldata == NULL);
1302*10491SRishi.Srivatsavai@Sun.COM 			rw_enter(&bip->bi_rwlock, RW_READER);
1303*10491SRishi.Srivatsavai@Sun.COM 			for (blp = list_head(&bip->bi_links); blp != NULL;
1304*10491SRishi.Srivatsavai@Sun.COM 			    blp = list_next(&bip->bi_links, blp)) {
1305*10491SRishi.Srivatsavai@Sun.COM 				ASSERT(blp->bl_trilldata == NULL);
1306*10491SRishi.Srivatsavai@Sun.COM 			}
1307*10491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bip->bi_rwlock);
1308*10491SRishi.Srivatsavai@Sun.COM 		}
1309*10491SRishi.Srivatsavai@Sun.COM 		mutex_exit(&inst_lock);
1310*10491SRishi.Srivatsavai@Sun.COM 	}
1311*10491SRishi.Srivatsavai@Sun.COM #endif
1312*10491SRishi.Srivatsavai@Sun.COM 	trill_recv_fn = recv_fn;
1313*10491SRishi.Srivatsavai@Sun.COM 	trill_encap_fn = encap_fn;
1314*10491SRishi.Srivatsavai@Sun.COM 	trill_brdstr_fn = brdstr_fn;
1315*10491SRishi.Srivatsavai@Sun.COM 	trill_lndstr_fn = lndstr_fn;
1316*10491SRishi.Srivatsavai@Sun.COM }
1317*10491SRishi.Srivatsavai@Sun.COM 
1318*10491SRishi.Srivatsavai@Sun.COM /*
1319*10491SRishi.Srivatsavai@Sun.COM  * This registers the TRILL instance pointer with a bridge.  Before this
1320*10491SRishi.Srivatsavai@Sun.COM  * pointer is set, the forwarding, TRILL receive, and bridge destructor
1321*10491SRishi.Srivatsavai@Sun.COM  * functions won't be called.
1322*10491SRishi.Srivatsavai@Sun.COM  *
1323*10491SRishi.Srivatsavai@Sun.COM  * TRILL holds a reference on a bridge with this call.  It must free the
1324*10491SRishi.Srivatsavai@Sun.COM  * reference by calling the unregister function below.
1325*10491SRishi.Srivatsavai@Sun.COM  */
1326*10491SRishi.Srivatsavai@Sun.COM bridge_inst_t *
1327*10491SRishi.Srivatsavai@Sun.COM bridge_trill_brref(const char *bname, void *ptr)
1328*10491SRishi.Srivatsavai@Sun.COM {
1329*10491SRishi.Srivatsavai@Sun.COM 	char bridge[MAXLINKNAMELEN];
1330*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
1331*10491SRishi.Srivatsavai@Sun.COM 
1332*10491SRishi.Srivatsavai@Sun.COM 	(void) snprintf(bridge, MAXLINKNAMELEN, "%s0", bname);
1333*10491SRishi.Srivatsavai@Sun.COM 	bip = bridge_find_name(bridge);
1334*10491SRishi.Srivatsavai@Sun.COM 	if (bip != NULL) {
1335*10491SRishi.Srivatsavai@Sun.COM 		ASSERT(bip->bi_trilldata == NULL && ptr != NULL);
1336*10491SRishi.Srivatsavai@Sun.COM 		bip->bi_trilldata = ptr;
1337*10491SRishi.Srivatsavai@Sun.COM 	}
1338*10491SRishi.Srivatsavai@Sun.COM 	return (bip);
1339*10491SRishi.Srivatsavai@Sun.COM }
1340*10491SRishi.Srivatsavai@Sun.COM 
1341*10491SRishi.Srivatsavai@Sun.COM void
1342*10491SRishi.Srivatsavai@Sun.COM bridge_trill_brunref(bridge_inst_t *bip)
1343*10491SRishi.Srivatsavai@Sun.COM {
1344*10491SRishi.Srivatsavai@Sun.COM 	ASSERT(bip->bi_trilldata != NULL);
1345*10491SRishi.Srivatsavai@Sun.COM 	bip->bi_trilldata = NULL;
1346*10491SRishi.Srivatsavai@Sun.COM 	bridge_unref(bip);
1347*10491SRishi.Srivatsavai@Sun.COM }
1348*10491SRishi.Srivatsavai@Sun.COM 
1349*10491SRishi.Srivatsavai@Sun.COM /*
1350*10491SRishi.Srivatsavai@Sun.COM  * TRILL calls this function when referencing a particular link on a bridge.
1351*10491SRishi.Srivatsavai@Sun.COM  *
1352*10491SRishi.Srivatsavai@Sun.COM  * It holds a reference on the link, so TRILL must clear out the reference when
1353*10491SRishi.Srivatsavai@Sun.COM  * it's done with the link (on unbinding).
1354*10491SRishi.Srivatsavai@Sun.COM  */
1355*10491SRishi.Srivatsavai@Sun.COM bridge_link_t *
1356*10491SRishi.Srivatsavai@Sun.COM bridge_trill_lnref(bridge_inst_t *bip, datalink_id_t linkid, void *ptr)
1357*10491SRishi.Srivatsavai@Sun.COM {
1358*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp;
1359*10491SRishi.Srivatsavai@Sun.COM 
1360*10491SRishi.Srivatsavai@Sun.COM 	ASSERT(ptr != NULL);
1361*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_READER);
1362*10491SRishi.Srivatsavai@Sun.COM 	for (blp = list_head(&bip->bi_links); blp != NULL;
1363*10491SRishi.Srivatsavai@Sun.COM 	    blp = list_next(&bip->bi_links, blp)) {
1364*10491SRishi.Srivatsavai@Sun.COM 		if (!(blp->bl_flags & BLF_DELETED) &&
1365*10491SRishi.Srivatsavai@Sun.COM 		    blp->bl_linkid == linkid && blp->bl_trilldata == NULL) {
1366*10491SRishi.Srivatsavai@Sun.COM 			blp->bl_trilldata = ptr;
1367*10491SRishi.Srivatsavai@Sun.COM 			blp->bl_flags &= ~BLF_TRILLACTIVE;
1368*10491SRishi.Srivatsavai@Sun.COM 			(void) memset(blp->bl_afs, 0, sizeof (blp->bl_afs));
1369*10491SRishi.Srivatsavai@Sun.COM 			atomic_inc_uint(&blp->bl_refs);
1370*10491SRishi.Srivatsavai@Sun.COM 			break;
1371*10491SRishi.Srivatsavai@Sun.COM 		}
1372*10491SRishi.Srivatsavai@Sun.COM 	}
1373*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
1374*10491SRishi.Srivatsavai@Sun.COM 	return (blp);
1375*10491SRishi.Srivatsavai@Sun.COM }
1376*10491SRishi.Srivatsavai@Sun.COM 
1377*10491SRishi.Srivatsavai@Sun.COM void
1378*10491SRishi.Srivatsavai@Sun.COM bridge_trill_lnunref(bridge_link_t *blp)
1379*10491SRishi.Srivatsavai@Sun.COM {
1380*10491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&blp->bl_trilllock);
1381*10491SRishi.Srivatsavai@Sun.COM 	ASSERT(blp->bl_trilldata != NULL);
1382*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_trilldata = NULL;
1383*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_flags &= ~BLF_TRILLACTIVE;
1384*10491SRishi.Srivatsavai@Sun.COM 	while (blp->bl_trillthreads > 0)
1385*10491SRishi.Srivatsavai@Sun.COM 		cv_wait(&blp->bl_trillwait, &blp->bl_trilllock);
1386*10491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&blp->bl_trilllock);
1387*10491SRishi.Srivatsavai@Sun.COM 	(void) memset(blp->bl_afs, 0xff, sizeof (blp->bl_afs));
1388*10491SRishi.Srivatsavai@Sun.COM 	link_unref(blp);
1389*10491SRishi.Srivatsavai@Sun.COM }
1390*10491SRishi.Srivatsavai@Sun.COM 
1391*10491SRishi.Srivatsavai@Sun.COM /*
1392*10491SRishi.Srivatsavai@Sun.COM  * This periodic timer performs three functions:
1393*10491SRishi.Srivatsavai@Sun.COM  *  1. It scans the list of learned forwarding entries, and removes ones that
1394*10491SRishi.Srivatsavai@Sun.COM  *     haven't been heard from in a while.  The time limit is backed down if
1395*10491SRishi.Srivatsavai@Sun.COM  *     we're above the configured table limit.
1396*10491SRishi.Srivatsavai@Sun.COM  *  2. It walks the links and decays away the bl_learns counter.
1397*10491SRishi.Srivatsavai@Sun.COM  *  3. It scans the observability node entries looking for ones that can be
1398*10491SRishi.Srivatsavai@Sun.COM  *     freed up.
1399*10491SRishi.Srivatsavai@Sun.COM  */
1400*10491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
1401*10491SRishi.Srivatsavai@Sun.COM static void
1402*10491SRishi.Srivatsavai@Sun.COM bridge_timer(void *arg)
1403*10491SRishi.Srivatsavai@Sun.COM {
1404*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
1405*10491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp, *bfnext;
1406*10491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp, *bmnext;
1407*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp;
1408*10491SRishi.Srivatsavai@Sun.COM 	int err;
1409*10491SRishi.Srivatsavai@Sun.COM 	datalink_id_t tmpid;
1410*10491SRishi.Srivatsavai@Sun.COM 	avl_tree_t fwd_scavenge;
1411*10491SRishi.Srivatsavai@Sun.COM 	clock_t age_limit;
1412*10491SRishi.Srivatsavai@Sun.COM 	uint32_t ldecay;
1413*10491SRishi.Srivatsavai@Sun.COM 
1414*10491SRishi.Srivatsavai@Sun.COM 	avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t),
1415*10491SRishi.Srivatsavai@Sun.COM 	    offsetof(bridge_fwd_t, bf_node));
1416*10491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&inst_lock);
1417*10491SRishi.Srivatsavai@Sun.COM 	for (bip = list_head(&inst_list); bip != NULL;
1418*10491SRishi.Srivatsavai@Sun.COM 	    bip = list_next(&inst_list, bip)) {
1419*10491SRishi.Srivatsavai@Sun.COM 		if (bip->bi_flags & BIF_SHUTDOWN)
1420*10491SRishi.Srivatsavai@Sun.COM 			continue;
1421*10491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_WRITER);
1422*10491SRishi.Srivatsavai@Sun.COM 		/* compute scaled maximum age based on table limit */
1423*10491SRishi.Srivatsavai@Sun.COM 		if (avl_numnodes(&bip->bi_fwd) > bip->bi_tablemax)
1424*10491SRishi.Srivatsavai@Sun.COM 			bip->bi_tshift++;
1425*10491SRishi.Srivatsavai@Sun.COM 		else
1426*10491SRishi.Srivatsavai@Sun.COM 			bip->bi_tshift = 0;
1427*10491SRishi.Srivatsavai@Sun.COM 		if ((age_limit = bridge_fwd_age >> bip->bi_tshift) == 0) {
1428*10491SRishi.Srivatsavai@Sun.COM 			if (bip->bi_tshift != 0)
1429*10491SRishi.Srivatsavai@Sun.COM 				bip->bi_tshift--;
1430*10491SRishi.Srivatsavai@Sun.COM 			age_limit = 1;
1431*10491SRishi.Srivatsavai@Sun.COM 		}
1432*10491SRishi.Srivatsavai@Sun.COM 		bfnext = avl_first(&bip->bi_fwd);
1433*10491SRishi.Srivatsavai@Sun.COM 		while ((bfp = bfnext) != NULL) {
1434*10491SRishi.Srivatsavai@Sun.COM 			bfnext = AVL_NEXT(&bip->bi_fwd, bfp);
1435*10491SRishi.Srivatsavai@Sun.COM 			if (!(bfp->bf_flags & BFF_LOCALADDR) &&
1436*10491SRishi.Srivatsavai@Sun.COM 			    (lbolt - bfp->bf_lastheard) > age_limit) {
1437*10491SRishi.Srivatsavai@Sun.COM 				ASSERT(bfp->bf_flags & BFF_INTREE);
1438*10491SRishi.Srivatsavai@Sun.COM 				avl_remove(&bip->bi_fwd, bfp);
1439*10491SRishi.Srivatsavai@Sun.COM 				bfp->bf_flags &= ~BFF_INTREE;
1440*10491SRishi.Srivatsavai@Sun.COM 				avl_add(&fwd_scavenge, bfp);
1441*10491SRishi.Srivatsavai@Sun.COM 			}
1442*10491SRishi.Srivatsavai@Sun.COM 		}
1443*10491SRishi.Srivatsavai@Sun.COM 		for (blp = list_head(&bip->bi_links); blp != NULL;
1444*10491SRishi.Srivatsavai@Sun.COM 		    blp = list_next(&bip->bi_links, blp)) {
1445*10491SRishi.Srivatsavai@Sun.COM 			ldecay = mac_get_ldecay(blp->bl_mh);
1446*10491SRishi.Srivatsavai@Sun.COM 			if (ldecay >= blp->bl_learns)
1447*10491SRishi.Srivatsavai@Sun.COM 				blp->bl_learns = 0;
1448*10491SRishi.Srivatsavai@Sun.COM 			else
1449*10491SRishi.Srivatsavai@Sun.COM 				atomic_add_int(&blp->bl_learns, -(int)ldecay);
1450*10491SRishi.Srivatsavai@Sun.COM 		}
1451*10491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
1452*10491SRishi.Srivatsavai@Sun.COM 		bfnext = avl_first(&fwd_scavenge);
1453*10491SRishi.Srivatsavai@Sun.COM 		while ((bfp = bfnext) != NULL) {
1454*10491SRishi.Srivatsavai@Sun.COM 			bfnext = AVL_NEXT(&fwd_scavenge, bfp);
1455*10491SRishi.Srivatsavai@Sun.COM 			avl_remove(&fwd_scavenge, bfp);
1456*10491SRishi.Srivatsavai@Sun.COM 			KIINCR(bki_expire);
1457*10491SRishi.Srivatsavai@Sun.COM 			fwd_unref(bfp);	/* drop tree reference */
1458*10491SRishi.Srivatsavai@Sun.COM 		}
1459*10491SRishi.Srivatsavai@Sun.COM 	}
1460*10491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&inst_lock);
1461*10491SRishi.Srivatsavai@Sun.COM 	avl_destroy(&fwd_scavenge);
1462*10491SRishi.Srivatsavai@Sun.COM 
1463*10491SRishi.Srivatsavai@Sun.COM 	/*
1464*10491SRishi.Srivatsavai@Sun.COM 	 * Scan the bridge_mac_t entries and try to free up the ones that are
1465*10491SRishi.Srivatsavai@Sun.COM 	 * no longer active.  This must be done by polling, as neither DLS nor
1466*10491SRishi.Srivatsavai@Sun.COM 	 * MAC provides a driver any sort of positive control over clients.
1467*10491SRishi.Srivatsavai@Sun.COM 	 */
1468*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bmac_rwlock, RW_WRITER);
1469*10491SRishi.Srivatsavai@Sun.COM 	bmnext = list_head(&bmac_list);
1470*10491SRishi.Srivatsavai@Sun.COM 	while ((bmp = bmnext) != NULL) {
1471*10491SRishi.Srivatsavai@Sun.COM 		bmnext = list_next(&bmac_list, bmp);
1472*10491SRishi.Srivatsavai@Sun.COM 
1473*10491SRishi.Srivatsavai@Sun.COM 		/* ignore active bridges */
1474*10491SRishi.Srivatsavai@Sun.COM 		if (bmp->bm_inst != NULL)
1475*10491SRishi.Srivatsavai@Sun.COM 			continue;
1476*10491SRishi.Srivatsavai@Sun.COM 
1477*10491SRishi.Srivatsavai@Sun.COM 		if (bmp->bm_flags & BMF_DLS) {
1478*10491SRishi.Srivatsavai@Sun.COM 			err = dls_devnet_destroy(bmp->bm_mh, &tmpid, B_FALSE);
1479*10491SRishi.Srivatsavai@Sun.COM 			ASSERT(err == 0 || err == EBUSY);
1480*10491SRishi.Srivatsavai@Sun.COM 			if (err == 0)
1481*10491SRishi.Srivatsavai@Sun.COM 				bmp->bm_flags &= ~BMF_DLS;
1482*10491SRishi.Srivatsavai@Sun.COM 		}
1483*10491SRishi.Srivatsavai@Sun.COM 
1484*10491SRishi.Srivatsavai@Sun.COM 		if (!(bmp->bm_flags & BMF_DLS)) {
1485*10491SRishi.Srivatsavai@Sun.COM 			err = mac_unregister(bmp->bm_mh);
1486*10491SRishi.Srivatsavai@Sun.COM 			ASSERT(err == 0 || err == EBUSY);
1487*10491SRishi.Srivatsavai@Sun.COM 			if (err == 0) {
1488*10491SRishi.Srivatsavai@Sun.COM 				list_remove(&bmac_list, bmp);
1489*10491SRishi.Srivatsavai@Sun.COM 				kmem_free(bmp, sizeof (*bmp));
1490*10491SRishi.Srivatsavai@Sun.COM 			}
1491*10491SRishi.Srivatsavai@Sun.COM 		}
1492*10491SRishi.Srivatsavai@Sun.COM 	}
1493*10491SRishi.Srivatsavai@Sun.COM 	if (list_is_empty(&bmac_list)) {
1494*10491SRishi.Srivatsavai@Sun.COM 		bridge_timerid = 0;
1495*10491SRishi.Srivatsavai@Sun.COM 	} else {
1496*10491SRishi.Srivatsavai@Sun.COM 		bridge_timerid = timeout(bridge_timer, NULL,
1497*10491SRishi.Srivatsavai@Sun.COM 		    bridge_scan_interval);
1498*10491SRishi.Srivatsavai@Sun.COM 	}
1499*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bmac_rwlock);
1500*10491SRishi.Srivatsavai@Sun.COM }
1501*10491SRishi.Srivatsavai@Sun.COM 
1502*10491SRishi.Srivatsavai@Sun.COM static int
1503*10491SRishi.Srivatsavai@Sun.COM bridge_open(queue_t *rq, dev_t *devp, int oflag, int sflag, cred_t *credp)
1504*10491SRishi.Srivatsavai@Sun.COM {
1505*10491SRishi.Srivatsavai@Sun.COM 	bridge_stream_t	*bsp;
1506*10491SRishi.Srivatsavai@Sun.COM 
1507*10491SRishi.Srivatsavai@Sun.COM 	if (rq->q_ptr != NULL)
1508*10491SRishi.Srivatsavai@Sun.COM 		return (0);
1509*10491SRishi.Srivatsavai@Sun.COM 
1510*10491SRishi.Srivatsavai@Sun.COM 	if (sflag & MODOPEN)
1511*10491SRishi.Srivatsavai@Sun.COM 		return (EINVAL);
1512*10491SRishi.Srivatsavai@Sun.COM 
1513*10491SRishi.Srivatsavai@Sun.COM 	/*
1514*10491SRishi.Srivatsavai@Sun.COM 	 * Check the minor node number being opened.  This tells us which
1515*10491SRishi.Srivatsavai@Sun.COM 	 * bridge instance the user wants.
1516*10491SRishi.Srivatsavai@Sun.COM 	 */
1517*10491SRishi.Srivatsavai@Sun.COM 	if (getminor(*devp) != 0) {
1518*10491SRishi.Srivatsavai@Sun.COM 		/*
1519*10491SRishi.Srivatsavai@Sun.COM 		 * This is a regular DLPI stream for snoop or the like.
1520*10491SRishi.Srivatsavai@Sun.COM 		 * Redirect it through DLD.
1521*10491SRishi.Srivatsavai@Sun.COM 		 */
1522*10491SRishi.Srivatsavai@Sun.COM 		rq->q_qinfo = &bridge_dld_rinit;
1523*10491SRishi.Srivatsavai@Sun.COM 		OTHERQ(rq)->q_qinfo = &bridge_dld_winit;
1524*10491SRishi.Srivatsavai@Sun.COM 		return (dld_open(rq, devp, oflag, sflag, credp));
1525*10491SRishi.Srivatsavai@Sun.COM 	} else {
1526*10491SRishi.Srivatsavai@Sun.COM 		/*
1527*10491SRishi.Srivatsavai@Sun.COM 		 * Allocate the bridge control stream structure.
1528*10491SRishi.Srivatsavai@Sun.COM 		 */
1529*10491SRishi.Srivatsavai@Sun.COM 		if ((bsp = stream_alloc()) == NULL)
1530*10491SRishi.Srivatsavai@Sun.COM 			return (ENOSR);
1531*10491SRishi.Srivatsavai@Sun.COM 		rq->q_ptr = WR(rq)->q_ptr = (caddr_t)bsp;
1532*10491SRishi.Srivatsavai@Sun.COM 		bsp->bs_wq = WR(rq);
1533*10491SRishi.Srivatsavai@Sun.COM 		*devp = makedevice(getmajor(*devp), bsp->bs_minor);
1534*10491SRishi.Srivatsavai@Sun.COM 		qprocson(rq);
1535*10491SRishi.Srivatsavai@Sun.COM 		return (0);
1536*10491SRishi.Srivatsavai@Sun.COM 	}
1537*10491SRishi.Srivatsavai@Sun.COM }
1538*10491SRishi.Srivatsavai@Sun.COM 
1539*10491SRishi.Srivatsavai@Sun.COM /*
1540*10491SRishi.Srivatsavai@Sun.COM  * This is used only for bridge control streams.  DLPI goes through dld
1541*10491SRishi.Srivatsavai@Sun.COM  * instead.
1542*10491SRishi.Srivatsavai@Sun.COM  */
1543*10491SRishi.Srivatsavai@Sun.COM static int
1544*10491SRishi.Srivatsavai@Sun.COM bridge_close(queue_t *rq)
1545*10491SRishi.Srivatsavai@Sun.COM {
1546*10491SRishi.Srivatsavai@Sun.COM 	bridge_stream_t	*bsp = rq->q_ptr;
1547*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
1548*10491SRishi.Srivatsavai@Sun.COM 
1549*10491SRishi.Srivatsavai@Sun.COM 	/*
1550*10491SRishi.Srivatsavai@Sun.COM 	 * Wait for any stray taskq (add/delete link) entries related to this
1551*10491SRishi.Srivatsavai@Sun.COM 	 * stream to leave the system.
1552*10491SRishi.Srivatsavai@Sun.COM 	 */
1553*10491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&stream_ref_lock);
1554*10491SRishi.Srivatsavai@Sun.COM 	while (bsp->bs_taskq_cnt != 0)
1555*10491SRishi.Srivatsavai@Sun.COM 		cv_wait(&stream_ref_cv, &stream_ref_lock);
1556*10491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&stream_ref_lock);
1557*10491SRishi.Srivatsavai@Sun.COM 
1558*10491SRishi.Srivatsavai@Sun.COM 	qprocsoff(rq);
1559*10491SRishi.Srivatsavai@Sun.COM 	if ((bip = bsp->bs_inst) != NULL)
1560*10491SRishi.Srivatsavai@Sun.COM 		shutdown_inst(bip);
1561*10491SRishi.Srivatsavai@Sun.COM 	rq->q_ptr = WR(rq)->q_ptr = NULL;
1562*10491SRishi.Srivatsavai@Sun.COM 	stream_free(bsp);
1563*10491SRishi.Srivatsavai@Sun.COM 	if (bip != NULL)
1564*10491SRishi.Srivatsavai@Sun.COM 		bridge_unref(bip);
1565*10491SRishi.Srivatsavai@Sun.COM 
1566*10491SRishi.Srivatsavai@Sun.COM 	return (0);
1567*10491SRishi.Srivatsavai@Sun.COM }
1568*10491SRishi.Srivatsavai@Sun.COM 
1569*10491SRishi.Srivatsavai@Sun.COM static void
1570*10491SRishi.Srivatsavai@Sun.COM bridge_learn(bridge_link_t *blp, const uint8_t *saddr, uint16_t ingress_nick,
1571*10491SRishi.Srivatsavai@Sun.COM     uint16_t vlanid)
1572*10491SRishi.Srivatsavai@Sun.COM {
1573*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;
1574*10491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp, *bfpnew;
1575*10491SRishi.Srivatsavai@Sun.COM 	int i;
1576*10491SRishi.Srivatsavai@Sun.COM 	boolean_t replaced = B_FALSE;
1577*10491SRishi.Srivatsavai@Sun.COM 
1578*10491SRishi.Srivatsavai@Sun.COM 	/* Ignore multi-destination address used as source; it's nonsense. */
1579*10491SRishi.Srivatsavai@Sun.COM 	if (*saddr & 1)
1580*10491SRishi.Srivatsavai@Sun.COM 		return;
1581*10491SRishi.Srivatsavai@Sun.COM 
1582*10491SRishi.Srivatsavai@Sun.COM 	/*
1583*10491SRishi.Srivatsavai@Sun.COM 	 * If the source is known, then check whether it belongs on this link.
1584*10491SRishi.Srivatsavai@Sun.COM 	 * If not, and this isn't a fixed local address, then we've detected a
1585*10491SRishi.Srivatsavai@Sun.COM 	 * move.  If it's not known, learn it.
1586*10491SRishi.Srivatsavai@Sun.COM 	 */
1587*10491SRishi.Srivatsavai@Sun.COM 	if ((bfp = fwd_find(bip, saddr, vlanid)) != NULL) {
1588*10491SRishi.Srivatsavai@Sun.COM 		/*
1589*10491SRishi.Srivatsavai@Sun.COM 		 * If the packet has a fixed local source address, then there's
1590*10491SRishi.Srivatsavai@Sun.COM 		 * nothing we can learn.  We must quit.  If this was a received
1591*10491SRishi.Srivatsavai@Sun.COM 		 * packet, then the sender has stolen our address, but there's
1592*10491SRishi.Srivatsavai@Sun.COM 		 * nothing we can do.  If it's a transmitted packet, then
1593*10491SRishi.Srivatsavai@Sun.COM 		 * that's the normal case.
1594*10491SRishi.Srivatsavai@Sun.COM 		 */
1595*10491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_flags & BFF_LOCALADDR) {
1596*10491SRishi.Srivatsavai@Sun.COM 			fwd_unref(bfp);
1597*10491SRishi.Srivatsavai@Sun.COM 			return;
1598*10491SRishi.Srivatsavai@Sun.COM 		}
1599*10491SRishi.Srivatsavai@Sun.COM 
1600*10491SRishi.Srivatsavai@Sun.COM 		/*
1601*10491SRishi.Srivatsavai@Sun.COM 		 * Check if the link (and TRILL sender, if any) being used is
1602*10491SRishi.Srivatsavai@Sun.COM 		 * among the ones registered for this address.  If so, then
1603*10491SRishi.Srivatsavai@Sun.COM 		 * this is information that we already know.
1604*10491SRishi.Srivatsavai@Sun.COM 		 */
1605*10491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_trill_nick == ingress_nick) {
1606*10491SRishi.Srivatsavai@Sun.COM 			for (i = 0; i < bfp->bf_nlinks; i++) {
1607*10491SRishi.Srivatsavai@Sun.COM 				if (bfp->bf_links[i] == blp) {
1608*10491SRishi.Srivatsavai@Sun.COM 					bfp->bf_lastheard = lbolt;
1609*10491SRishi.Srivatsavai@Sun.COM 					fwd_unref(bfp);
1610*10491SRishi.Srivatsavai@Sun.COM 					return;
1611*10491SRishi.Srivatsavai@Sun.COM 				}
1612*10491SRishi.Srivatsavai@Sun.COM 			}
1613*10491SRishi.Srivatsavai@Sun.COM 		}
1614*10491SRishi.Srivatsavai@Sun.COM 	}
1615*10491SRishi.Srivatsavai@Sun.COM 
1616*10491SRishi.Srivatsavai@Sun.COM 	/*
1617*10491SRishi.Srivatsavai@Sun.COM 	 * Note that we intentionally "unlearn" things that appear to be under
1618*10491SRishi.Srivatsavai@Sun.COM 	 * attack on this link.  The forwarding cache is a negative thing for
1619*10491SRishi.Srivatsavai@Sun.COM 	 * security -- it disables reachability as a performance optimization
1620*10491SRishi.Srivatsavai@Sun.COM 	 * -- so leaving out entries optimizes for success and defends against
1621*10491SRishi.Srivatsavai@Sun.COM 	 * the attack.  Thus, the bare increment without a check in the delete
1622*10491SRishi.Srivatsavai@Sun.COM 	 * code above is right.  (And it's ok if we skid over the limit a
1623*10491SRishi.Srivatsavai@Sun.COM 	 * little, so there's no syncronization needed on the test.)
1624*10491SRishi.Srivatsavai@Sun.COM 	 */
1625*10491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_learns >= mac_get_llimit(blp->bl_mh)) {
1626*10491SRishi.Srivatsavai@Sun.COM 		if (bfp != NULL) {
1627*10491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_vcnt == 0)
1628*10491SRishi.Srivatsavai@Sun.COM 				fwd_delete(bfp);
1629*10491SRishi.Srivatsavai@Sun.COM 			fwd_unref(bfp);
1630*10491SRishi.Srivatsavai@Sun.COM 		}
1631*10491SRishi.Srivatsavai@Sun.COM 		return;
1632*10491SRishi.Srivatsavai@Sun.COM 	}
1633*10491SRishi.Srivatsavai@Sun.COM 
1634*10491SRishi.Srivatsavai@Sun.COM 	atomic_inc_uint(&blp->bl_learns);
1635*10491SRishi.Srivatsavai@Sun.COM 
1636*10491SRishi.Srivatsavai@Sun.COM 	if ((bfpnew = fwd_alloc(saddr, 1, ingress_nick)) == NULL) {
1637*10491SRishi.Srivatsavai@Sun.COM 		if (bfp != NULL)
1638*10491SRishi.Srivatsavai@Sun.COM 			fwd_unref(bfp);
1639*10491SRishi.Srivatsavai@Sun.COM 		return;
1640*10491SRishi.Srivatsavai@Sun.COM 	}
1641*10491SRishi.Srivatsavai@Sun.COM 	KIINCR(bki_count);
1642*10491SRishi.Srivatsavai@Sun.COM 
1643*10491SRishi.Srivatsavai@Sun.COM 	if (bfp != NULL) {
1644*10491SRishi.Srivatsavai@Sun.COM 		/*
1645*10491SRishi.Srivatsavai@Sun.COM 		 * If this is a new destination for the same VLAN, then delete
1646*10491SRishi.Srivatsavai@Sun.COM 		 * so that we can update.  If it's a different VLAN, then we're
1647*10491SRishi.Srivatsavai@Sun.COM 		 * not going to delete the original.  Split off instead into an
1648*10491SRishi.Srivatsavai@Sun.COM 		 * IVL entry.
1649*10491SRishi.Srivatsavai@Sun.COM 		 */
1650*10491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_vlanid == vlanid) {
1651*10491SRishi.Srivatsavai@Sun.COM 			/* save the count of IVL duplicates */
1652*10491SRishi.Srivatsavai@Sun.COM 			bfpnew->bf_vcnt = bfp->bf_vcnt;
1653*10491SRishi.Srivatsavai@Sun.COM 
1654*10491SRishi.Srivatsavai@Sun.COM 			/* entry deletes count as learning events */
1655*10491SRishi.Srivatsavai@Sun.COM 			atomic_inc_uint(&blp->bl_learns);
1656*10491SRishi.Srivatsavai@Sun.COM 
1657*10491SRishi.Srivatsavai@Sun.COM 			/* destroy and create anew; node moved */
1658*10491SRishi.Srivatsavai@Sun.COM 			fwd_delete(bfp);
1659*10491SRishi.Srivatsavai@Sun.COM 			replaced = B_TRUE;
1660*10491SRishi.Srivatsavai@Sun.COM 			KIINCR(bki_moved);
1661*10491SRishi.Srivatsavai@Sun.COM 		} else {
1662*10491SRishi.Srivatsavai@Sun.COM 			bfp->bf_vcnt++;
1663*10491SRishi.Srivatsavai@Sun.COM 			bfpnew->bf_flags |= BFF_VLANLOCAL;
1664*10491SRishi.Srivatsavai@Sun.COM 		}
1665*10491SRishi.Srivatsavai@Sun.COM 		fwd_unref(bfp);
1666*10491SRishi.Srivatsavai@Sun.COM 	}
1667*10491SRishi.Srivatsavai@Sun.COM 	bfpnew->bf_links[0] = blp;
1668*10491SRishi.Srivatsavai@Sun.COM 	bfpnew->bf_nlinks = 1;
1669*10491SRishi.Srivatsavai@Sun.COM 	atomic_inc_uint(&blp->bl_refs);	/* bf_links entry */
1670*10491SRishi.Srivatsavai@Sun.COM 	if (!fwd_insert(bip, bfpnew))
1671*10491SRishi.Srivatsavai@Sun.COM 		fwd_free(bfpnew);
1672*10491SRishi.Srivatsavai@Sun.COM 	else if (!replaced)
1673*10491SRishi.Srivatsavai@Sun.COM 		KIINCR(bki_source);
1674*10491SRishi.Srivatsavai@Sun.COM }
1675*10491SRishi.Srivatsavai@Sun.COM 
1676*10491SRishi.Srivatsavai@Sun.COM /*
1677*10491SRishi.Srivatsavai@Sun.COM  * Process the VLAN headers for output on a given link.  There are several
1678*10491SRishi.Srivatsavai@Sun.COM  * cases (noting that we don't map VLANs):
1679*10491SRishi.Srivatsavai@Sun.COM  *   1. The input packet is good as it is; either
1680*10491SRishi.Srivatsavai@Sun.COM  *	a. It has no tag, and output has same PVID
1681*10491SRishi.Srivatsavai@Sun.COM  *	b. It has a non-zero priority-only tag for PVID, and b_band is same
1682*10491SRishi.Srivatsavai@Sun.COM  *	c. It has a tag with VLAN different from PVID, and b_band is same
1683*10491SRishi.Srivatsavai@Sun.COM  *   2. The tag must change: non-zero b_band is different from tag priority
1684*10491SRishi.Srivatsavai@Sun.COM  *   3. The packet has a tag and should not (VLAN same as PVID, b_band zero)
1685*10491SRishi.Srivatsavai@Sun.COM  *   4. The packet has no tag and needs one:
1686*10491SRishi.Srivatsavai@Sun.COM  *      a. VLAN ID same as PVID, but b_band is non-zero
1687*10491SRishi.Srivatsavai@Sun.COM  *      b. VLAN ID different from PVID
1688*10491SRishi.Srivatsavai@Sun.COM  * We exclude case 1 first, then modify the packet.  Note that output packets
1689*10491SRishi.Srivatsavai@Sun.COM  * get a priority set by the mblk, not by the header, because QoS in bridging
1690*10491SRishi.Srivatsavai@Sun.COM  * requires priority recalculation at each node.
1691*10491SRishi.Srivatsavai@Sun.COM  *
1692*10491SRishi.Srivatsavai@Sun.COM  * The passed-in tci is the "impossible" value 0xFFFF when no tag is present.
1693*10491SRishi.Srivatsavai@Sun.COM  */
1694*10491SRishi.Srivatsavai@Sun.COM static mblk_t *
1695*10491SRishi.Srivatsavai@Sun.COM reform_vlan_header(mblk_t *mp, uint16_t vlanid, uint16_t tci, uint16_t pvid)
1696*10491SRishi.Srivatsavai@Sun.COM {
1697*10491SRishi.Srivatsavai@Sun.COM 	boolean_t source_has_tag = (tci != 0xFFFF);
1698*10491SRishi.Srivatsavai@Sun.COM 	mblk_t *mpcopy;
1699*10491SRishi.Srivatsavai@Sun.COM 	size_t mlen, minlen;
1700*10491SRishi.Srivatsavai@Sun.COM 	struct ether_vlan_header *evh;
1701*10491SRishi.Srivatsavai@Sun.COM 	int pri;
1702*10491SRishi.Srivatsavai@Sun.COM 
1703*10491SRishi.Srivatsavai@Sun.COM 	/* This helps centralize error handling in the caller. */
1704*10491SRishi.Srivatsavai@Sun.COM 	if (mp == NULL)
1705*10491SRishi.Srivatsavai@Sun.COM 		return (mp);
1706*10491SRishi.Srivatsavai@Sun.COM 
1707*10491SRishi.Srivatsavai@Sun.COM 	/* No forwarded packet can have hardware checksum enabled */
1708*10491SRishi.Srivatsavai@Sun.COM 	DB_CKSUMFLAGS(mp) = 0;
1709*10491SRishi.Srivatsavai@Sun.COM 
1710*10491SRishi.Srivatsavai@Sun.COM 	/* Get the no-modification cases out of the way first */
1711*10491SRishi.Srivatsavai@Sun.COM 	if (!source_has_tag && vlanid == pvid)		/* 1a */
1712*10491SRishi.Srivatsavai@Sun.COM 		return (mp);
1713*10491SRishi.Srivatsavai@Sun.COM 
1714*10491SRishi.Srivatsavai@Sun.COM 	pri = VLAN_PRI(tci);
1715*10491SRishi.Srivatsavai@Sun.COM 	if (source_has_tag && mp->b_band == pri) {
1716*10491SRishi.Srivatsavai@Sun.COM 		if (vlanid != pvid)			/* 1c */
1717*10491SRishi.Srivatsavai@Sun.COM 			return (mp);
1718*10491SRishi.Srivatsavai@Sun.COM 		if (pri != 0 && VLAN_ID(tci) == 0)	/* 1b */
1719*10491SRishi.Srivatsavai@Sun.COM 			return (mp);
1720*10491SRishi.Srivatsavai@Sun.COM 	}
1721*10491SRishi.Srivatsavai@Sun.COM 
1722*10491SRishi.Srivatsavai@Sun.COM 	/*
1723*10491SRishi.Srivatsavai@Sun.COM 	 * We now know that we must modify the packet.  Prepare for that.  Note
1724*10491SRishi.Srivatsavai@Sun.COM 	 * that if a tag is present, the caller has already done a pullup for
1725*10491SRishi.Srivatsavai@Sun.COM 	 * the VLAN header, so we're good to go.
1726*10491SRishi.Srivatsavai@Sun.COM 	 */
1727*10491SRishi.Srivatsavai@Sun.COM 	if (MBLKL(mp) < sizeof (struct ether_header)) {
1728*10491SRishi.Srivatsavai@Sun.COM 		mpcopy = msgpullup(mp, sizeof (struct ether_header));
1729*10491SRishi.Srivatsavai@Sun.COM 		if (mpcopy == NULL) {
1730*10491SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
1731*10491SRishi.Srivatsavai@Sun.COM 			return (NULL);
1732*10491SRishi.Srivatsavai@Sun.COM 		}
1733*10491SRishi.Srivatsavai@Sun.COM 		mp = mpcopy;
1734*10491SRishi.Srivatsavai@Sun.COM 	}
1735*10491SRishi.Srivatsavai@Sun.COM 	if (DB_REF(mp) > 1 || !IS_P2ALIGNED(mp->b_rptr, sizeof (uint16_t)) ||
1736*10491SRishi.Srivatsavai@Sun.COM 	    (!source_has_tag && MBLKTAIL(mp) < VLAN_INCR)) {
1737*10491SRishi.Srivatsavai@Sun.COM 		minlen = mlen = MBLKL(mp);
1738*10491SRishi.Srivatsavai@Sun.COM 		if (!source_has_tag)
1739*10491SRishi.Srivatsavai@Sun.COM 			minlen += VLAN_INCR;
1740*10491SRishi.Srivatsavai@Sun.COM 		ASSERT(minlen >= sizeof (struct ether_vlan_header));
1741*10491SRishi.Srivatsavai@Sun.COM 		/*
1742*10491SRishi.Srivatsavai@Sun.COM 		 * We're willing to copy some data to avoid fragmentation, but
1743*10491SRishi.Srivatsavai@Sun.COM 		 * not a lot.
1744*10491SRishi.Srivatsavai@Sun.COM 		 */
1745*10491SRishi.Srivatsavai@Sun.COM 		if (minlen > 256)
1746*10491SRishi.Srivatsavai@Sun.COM 			minlen = sizeof (struct ether_vlan_header);
1747*10491SRishi.Srivatsavai@Sun.COM 		mpcopy = allocb(minlen, BPRI_MED);
1748*10491SRishi.Srivatsavai@Sun.COM 		if (mpcopy == NULL) {
1749*10491SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
1750*10491SRishi.Srivatsavai@Sun.COM 			return (NULL);
1751*10491SRishi.Srivatsavai@Sun.COM 		}
1752*10491SRishi.Srivatsavai@Sun.COM 		if (mlen <= minlen) {
1753*10491SRishi.Srivatsavai@Sun.COM 			/* We toss the first mblk when we can. */
1754*10491SRishi.Srivatsavai@Sun.COM 			bcopy(mp->b_rptr, mpcopy->b_rptr, mlen);
1755*10491SRishi.Srivatsavai@Sun.COM 			mpcopy->b_wptr += mlen;
1756*10491SRishi.Srivatsavai@Sun.COM 			mpcopy->b_cont = mp->b_cont;
1757*10491SRishi.Srivatsavai@Sun.COM 			freeb(mp);
1758*10491SRishi.Srivatsavai@Sun.COM 		} else {
1759*10491SRishi.Srivatsavai@Sun.COM 			/* If not, then just copy what we need */
1760*10491SRishi.Srivatsavai@Sun.COM 			if (!source_has_tag)
1761*10491SRishi.Srivatsavai@Sun.COM 				minlen = sizeof (struct ether_header);
1762*10491SRishi.Srivatsavai@Sun.COM 			bcopy(mp->b_rptr, mpcopy->b_rptr, minlen);
1763*10491SRishi.Srivatsavai@Sun.COM 			mpcopy->b_wptr += minlen;
1764*10491SRishi.Srivatsavai@Sun.COM 			mpcopy->b_cont = mp;
1765*10491SRishi.Srivatsavai@Sun.COM 			mp->b_rptr += minlen;
1766*10491SRishi.Srivatsavai@Sun.COM 		}
1767*10491SRishi.Srivatsavai@Sun.COM 		mp = mpcopy;
1768*10491SRishi.Srivatsavai@Sun.COM 	}
1769*10491SRishi.Srivatsavai@Sun.COM 
1770*10491SRishi.Srivatsavai@Sun.COM 	/* LINTED: pointer alignment */
1771*10491SRishi.Srivatsavai@Sun.COM 	evh = (struct ether_vlan_header *)mp->b_rptr;
1772*10491SRishi.Srivatsavai@Sun.COM 	if (source_has_tag) {
1773*10491SRishi.Srivatsavai@Sun.COM 		if (mp->b_band == 0 && vlanid == pvid) {	/* 3 */
1774*10491SRishi.Srivatsavai@Sun.COM 			evh->ether_tpid = evh->ether_type;
1775*10491SRishi.Srivatsavai@Sun.COM 			mlen = MBLKL(mp);
1776*10491SRishi.Srivatsavai@Sun.COM 			if (mlen > sizeof (struct ether_vlan_header))
1777*10491SRishi.Srivatsavai@Sun.COM 				ovbcopy(mp->b_rptr +
1778*10491SRishi.Srivatsavai@Sun.COM 				    sizeof (struct ether_vlan_header),
1779*10491SRishi.Srivatsavai@Sun.COM 				    mp->b_rptr + sizeof (struct ether_header),
1780*10491SRishi.Srivatsavai@Sun.COM 				    mlen - sizeof (struct ether_vlan_header));
1781*10491SRishi.Srivatsavai@Sun.COM 			mp->b_wptr -= VLAN_INCR;
1782*10491SRishi.Srivatsavai@Sun.COM 		} else {					/* 2 */
1783*10491SRishi.Srivatsavai@Sun.COM 			if (vlanid == pvid)
1784*10491SRishi.Srivatsavai@Sun.COM 				vlanid = VLAN_ID_NONE;
1785*10491SRishi.Srivatsavai@Sun.COM 			tci = VLAN_TCI(mp->b_band, ETHER_CFI, vlanid);
1786*10491SRishi.Srivatsavai@Sun.COM 			evh->ether_tci = htons(tci);
1787*10491SRishi.Srivatsavai@Sun.COM 		}
1788*10491SRishi.Srivatsavai@Sun.COM 	} else {
1789*10491SRishi.Srivatsavai@Sun.COM 		/* case 4: no header present, but one is needed */
1790*10491SRishi.Srivatsavai@Sun.COM 		mlen = MBLKL(mp);
1791*10491SRishi.Srivatsavai@Sun.COM 		if (mlen > sizeof (struct ether_header))
1792*10491SRishi.Srivatsavai@Sun.COM 			ovbcopy(mp->b_rptr + sizeof (struct ether_header),
1793*10491SRishi.Srivatsavai@Sun.COM 			    mp->b_rptr + sizeof (struct ether_vlan_header),
1794*10491SRishi.Srivatsavai@Sun.COM 			    mlen - sizeof (struct ether_header));
1795*10491SRishi.Srivatsavai@Sun.COM 		mp->b_wptr += VLAN_INCR;
1796*10491SRishi.Srivatsavai@Sun.COM 		ASSERT(mp->b_wptr <= DB_LIM(mp));
1797*10491SRishi.Srivatsavai@Sun.COM 		if (vlanid == pvid)
1798*10491SRishi.Srivatsavai@Sun.COM 			vlanid = VLAN_ID_NONE;
1799*10491SRishi.Srivatsavai@Sun.COM 		tci = VLAN_TCI(mp->b_band, ETHER_CFI, vlanid);
1800*10491SRishi.Srivatsavai@Sun.COM 		evh->ether_type = evh->ether_tpid;
1801*10491SRishi.Srivatsavai@Sun.COM 		evh->ether_tpid = htons(ETHERTYPE_VLAN);
1802*10491SRishi.Srivatsavai@Sun.COM 		evh->ether_tci = htons(tci);
1803*10491SRishi.Srivatsavai@Sun.COM 	}
1804*10491SRishi.Srivatsavai@Sun.COM 	return (mp);
1805*10491SRishi.Srivatsavai@Sun.COM }
1806*10491SRishi.Srivatsavai@Sun.COM 
1807*10491SRishi.Srivatsavai@Sun.COM /* Record VLAN information and strip header if requested . */
1808*10491SRishi.Srivatsavai@Sun.COM static void
1809*10491SRishi.Srivatsavai@Sun.COM update_header(mblk_t *mp, mac_header_info_t *hdr_info, boolean_t striphdr)
1810*10491SRishi.Srivatsavai@Sun.COM {
1811*10491SRishi.Srivatsavai@Sun.COM 	if (hdr_info->mhi_bindsap == ETHERTYPE_VLAN) {
1812*10491SRishi.Srivatsavai@Sun.COM 		struct ether_vlan_header *evhp;
1813*10491SRishi.Srivatsavai@Sun.COM 		uint16_t ether_type;
1814*10491SRishi.Srivatsavai@Sun.COM 
1815*10491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
1816*10491SRishi.Srivatsavai@Sun.COM 		evhp = (struct ether_vlan_header *)mp->b_rptr;
1817*10491SRishi.Srivatsavai@Sun.COM 		hdr_info->mhi_istagged = B_TRUE;
1818*10491SRishi.Srivatsavai@Sun.COM 		hdr_info->mhi_tci = ntohs(evhp->ether_tci);
1819*10491SRishi.Srivatsavai@Sun.COM 		if (striphdr) {
1820*10491SRishi.Srivatsavai@Sun.COM 			/*
1821*10491SRishi.Srivatsavai@Sun.COM 			 * For VLAN tagged frames update the ether_type
1822*10491SRishi.Srivatsavai@Sun.COM 			 * in hdr_info before stripping the header.
1823*10491SRishi.Srivatsavai@Sun.COM 			 */
1824*10491SRishi.Srivatsavai@Sun.COM 			ether_type = ntohs(evhp->ether_type);
1825*10491SRishi.Srivatsavai@Sun.COM 			hdr_info->mhi_origsap = ether_type;
1826*10491SRishi.Srivatsavai@Sun.COM 			hdr_info->mhi_bindsap = (ether_type > ETHERMTU) ?
1827*10491SRishi.Srivatsavai@Sun.COM 			    ether_type : DLS_SAP_LLC;
1828*10491SRishi.Srivatsavai@Sun.COM 			mp->b_rptr = (uchar_t *)(evhp + 1);
1829*10491SRishi.Srivatsavai@Sun.COM 		}
1830*10491SRishi.Srivatsavai@Sun.COM 	} else {
1831*10491SRishi.Srivatsavai@Sun.COM 		hdr_info->mhi_istagged = B_FALSE;
1832*10491SRishi.Srivatsavai@Sun.COM 		hdr_info->mhi_tci = VLAN_ID_NONE;
1833*10491SRishi.Srivatsavai@Sun.COM 		if (striphdr)
1834*10491SRishi.Srivatsavai@Sun.COM 			mp->b_rptr += sizeof (struct ether_header);
1835*10491SRishi.Srivatsavai@Sun.COM 	}
1836*10491SRishi.Srivatsavai@Sun.COM }
1837*10491SRishi.Srivatsavai@Sun.COM 
1838*10491SRishi.Srivatsavai@Sun.COM /*
1839*10491SRishi.Srivatsavai@Sun.COM  * Return B_TRUE if we're allowed to send on this link with the given VLAN ID.
1840*10491SRishi.Srivatsavai@Sun.COM  */
1841*10491SRishi.Srivatsavai@Sun.COM static boolean_t
1842*10491SRishi.Srivatsavai@Sun.COM bridge_can_send(bridge_link_t *blp, uint16_t vlanid)
1843*10491SRishi.Srivatsavai@Sun.COM {
1844*10491SRishi.Srivatsavai@Sun.COM 	ASSERT(vlanid != VLAN_ID_NONE);
1845*10491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_flags & BLF_DELETED)
1846*10491SRishi.Srivatsavai@Sun.COM 		return (B_FALSE);
1847*10491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_trilldata == NULL && blp->bl_state != BLS_FORWARDING)
1848*10491SRishi.Srivatsavai@Sun.COM 		return (B_FALSE);
1849*10491SRishi.Srivatsavai@Sun.COM 	return (BRIDGE_VLAN_ISSET(blp, vlanid) && BRIDGE_AF_ISSET(blp, vlanid));
1850*10491SRishi.Srivatsavai@Sun.COM }
1851*10491SRishi.Srivatsavai@Sun.COM 
1852*10491SRishi.Srivatsavai@Sun.COM /*
1853*10491SRishi.Srivatsavai@Sun.COM  * This function scans the bridge forwarding tables in order to forward a given
1854*10491SRishi.Srivatsavai@Sun.COM  * packet.  If the packet either doesn't need forwarding (the current link is
1855*10491SRishi.Srivatsavai@Sun.COM  * correct) or the current link needs a copy as well, then the packet is
1856*10491SRishi.Srivatsavai@Sun.COM  * returned to the caller.
1857*10491SRishi.Srivatsavai@Sun.COM  *
1858*10491SRishi.Srivatsavai@Sun.COM  * If a packet has been decapsulated from TRILL, then it must *NOT* reenter a
1859*10491SRishi.Srivatsavai@Sun.COM  * TRILL tunnel.  If the destination points there, then drop instead.
1860*10491SRishi.Srivatsavai@Sun.COM  */
1861*10491SRishi.Srivatsavai@Sun.COM static mblk_t *
1862*10491SRishi.Srivatsavai@Sun.COM bridge_forward(bridge_link_t *blp, mac_header_info_t *hdr_info, mblk_t *mp,
1863*10491SRishi.Srivatsavai@Sun.COM     uint16_t vlanid, uint16_t tci, boolean_t from_trill, boolean_t is_xmit)
1864*10491SRishi.Srivatsavai@Sun.COM {
1865*10491SRishi.Srivatsavai@Sun.COM 	mblk_t *mpsend, *mpcopy;
1866*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;
1867*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blpsend, *blpnext;
1868*10491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp;
1869*10491SRishi.Srivatsavai@Sun.COM 	uint_t i;
1870*10491SRishi.Srivatsavai@Sun.COM 	boolean_t selfseen = B_FALSE;
1871*10491SRishi.Srivatsavai@Sun.COM 	void *tdp;
1872*10491SRishi.Srivatsavai@Sun.COM 	const uint8_t *daddr = hdr_info->mhi_daddr;
1873*10491SRishi.Srivatsavai@Sun.COM 
1874*10491SRishi.Srivatsavai@Sun.COM 	/*
1875*10491SRishi.Srivatsavai@Sun.COM 	 * Check for the IEEE "reserved" multicast addresses.  Messages sent to
1876*10491SRishi.Srivatsavai@Sun.COM 	 * these addresses are used for link-local control (STP and pause), and
1877*10491SRishi.Srivatsavai@Sun.COM 	 * are never forwarded or redirected.
1878*10491SRishi.Srivatsavai@Sun.COM 	 */
1879*10491SRishi.Srivatsavai@Sun.COM 	if (daddr[0] == 1 && daddr[1] == 0x80 && daddr[2] == 0xc2 &&
1880*10491SRishi.Srivatsavai@Sun.COM 	    daddr[3] == 0 && daddr[4] == 0 && (daddr[5] & 0xf0) == 0) {
1881*10491SRishi.Srivatsavai@Sun.COM 		if (from_trill) {
1882*10491SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
1883*10491SRishi.Srivatsavai@Sun.COM 			mp = NULL;
1884*10491SRishi.Srivatsavai@Sun.COM 		}
1885*10491SRishi.Srivatsavai@Sun.COM 		return (mp);
1886*10491SRishi.Srivatsavai@Sun.COM 	}
1887*10491SRishi.Srivatsavai@Sun.COM 
1888*10491SRishi.Srivatsavai@Sun.COM 	if ((bfp = fwd_find(bip, daddr, vlanid)) != NULL) {
1889*10491SRishi.Srivatsavai@Sun.COM 
1890*10491SRishi.Srivatsavai@Sun.COM 		/*
1891*10491SRishi.Srivatsavai@Sun.COM 		 * If trill indicates a destination for this node, then it's
1892*10491SRishi.Srivatsavai@Sun.COM 		 * clearly not intended for local delivery.  We must tell TRILL
1893*10491SRishi.Srivatsavai@Sun.COM 		 * to encapsulate, as long as we didn't just decapsulate it.
1894*10491SRishi.Srivatsavai@Sun.COM 		 */
1895*10491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_trill_nick != RBRIDGE_NICKNAME_NONE) {
1896*10491SRishi.Srivatsavai@Sun.COM 			/*
1897*10491SRishi.Srivatsavai@Sun.COM 			 * Error case: can't reencapsulate if the protocols are
1898*10491SRishi.Srivatsavai@Sun.COM 			 * working correctly.
1899*10491SRishi.Srivatsavai@Sun.COM 			 */
1900*10491SRishi.Srivatsavai@Sun.COM 			if (from_trill) {
1901*10491SRishi.Srivatsavai@Sun.COM 				freemsg(mp);
1902*10491SRishi.Srivatsavai@Sun.COM 				return (NULL);
1903*10491SRishi.Srivatsavai@Sun.COM 			}
1904*10491SRishi.Srivatsavai@Sun.COM 			mutex_enter(&blp->bl_trilllock);
1905*10491SRishi.Srivatsavai@Sun.COM 			if ((tdp = blp->bl_trilldata) != NULL) {
1906*10491SRishi.Srivatsavai@Sun.COM 				blp->bl_trillthreads++;
1907*10491SRishi.Srivatsavai@Sun.COM 				mutex_exit(&blp->bl_trilllock);
1908*10491SRishi.Srivatsavai@Sun.COM 				update_header(mp, hdr_info, B_FALSE);
1909*10491SRishi.Srivatsavai@Sun.COM 				if (is_xmit)
1910*10491SRishi.Srivatsavai@Sun.COM 					mp = mac_fix_cksum(mp);
1911*10491SRishi.Srivatsavai@Sun.COM 				/* all trill data frames have Inner.VLAN */
1912*10491SRishi.Srivatsavai@Sun.COM 				mp = reform_vlan_header(mp, vlanid, tci, 0);
1913*10491SRishi.Srivatsavai@Sun.COM 				if (mp == NULL) {
1914*10491SRishi.Srivatsavai@Sun.COM 					KIINCR(bki_drops);
1915*10491SRishi.Srivatsavai@Sun.COM 					fwd_unref(bfp);
1916*10491SRishi.Srivatsavai@Sun.COM 					return (NULL);
1917*10491SRishi.Srivatsavai@Sun.COM 				}
1918*10491SRishi.Srivatsavai@Sun.COM 				trill_encap_fn(tdp, blp, hdr_info, mp,
1919*10491SRishi.Srivatsavai@Sun.COM 				    bfp->bf_trill_nick);
1920*10491SRishi.Srivatsavai@Sun.COM 				mutex_enter(&blp->bl_trilllock);
1921*10491SRishi.Srivatsavai@Sun.COM 				if (--blp->bl_trillthreads == 0 &&
1922*10491SRishi.Srivatsavai@Sun.COM 				    blp->bl_trilldata == NULL)
1923*10491SRishi.Srivatsavai@Sun.COM 					cv_broadcast(&blp->bl_trillwait);
1924*10491SRishi.Srivatsavai@Sun.COM 			}
1925*10491SRishi.Srivatsavai@Sun.COM 			mutex_exit(&blp->bl_trilllock);
1926*10491SRishi.Srivatsavai@Sun.COM 
1927*10491SRishi.Srivatsavai@Sun.COM 			/* if TRILL has been disabled, then kill this stray */
1928*10491SRishi.Srivatsavai@Sun.COM 			if (tdp == NULL) {
1929*10491SRishi.Srivatsavai@Sun.COM 				freemsg(mp);
1930*10491SRishi.Srivatsavai@Sun.COM 				fwd_delete(bfp);
1931*10491SRishi.Srivatsavai@Sun.COM 			}
1932*10491SRishi.Srivatsavai@Sun.COM 			fwd_unref(bfp);
1933*10491SRishi.Srivatsavai@Sun.COM 			return (NULL);
1934*10491SRishi.Srivatsavai@Sun.COM 		}
1935*10491SRishi.Srivatsavai@Sun.COM 
1936*10491SRishi.Srivatsavai@Sun.COM 		/* find first link we can send on */
1937*10491SRishi.Srivatsavai@Sun.COM 		for (i = 0; i < bfp->bf_nlinks; i++) {
1938*10491SRishi.Srivatsavai@Sun.COM 			blpsend = bfp->bf_links[i];
1939*10491SRishi.Srivatsavai@Sun.COM 			if (blpsend == blp)
1940*10491SRishi.Srivatsavai@Sun.COM 				selfseen = B_TRUE;
1941*10491SRishi.Srivatsavai@Sun.COM 			else if (bridge_can_send(blpsend, vlanid))
1942*10491SRishi.Srivatsavai@Sun.COM 				break;
1943*10491SRishi.Srivatsavai@Sun.COM 		}
1944*10491SRishi.Srivatsavai@Sun.COM 
1945*10491SRishi.Srivatsavai@Sun.COM 		while (i < bfp->bf_nlinks) {
1946*10491SRishi.Srivatsavai@Sun.COM 			blpsend = bfp->bf_links[i];
1947*10491SRishi.Srivatsavai@Sun.COM 			for (i++; i < bfp->bf_nlinks; i++) {
1948*10491SRishi.Srivatsavai@Sun.COM 				blpnext = bfp->bf_links[i];
1949*10491SRishi.Srivatsavai@Sun.COM 				if (blpnext == blp)
1950*10491SRishi.Srivatsavai@Sun.COM 					selfseen = B_TRUE;
1951*10491SRishi.Srivatsavai@Sun.COM 				else if (bridge_can_send(blpnext, vlanid))
1952*10491SRishi.Srivatsavai@Sun.COM 					break;
1953*10491SRishi.Srivatsavai@Sun.COM 			}
1954*10491SRishi.Srivatsavai@Sun.COM 			if (i == bfp->bf_nlinks && !selfseen) {
1955*10491SRishi.Srivatsavai@Sun.COM 				mpsend = mp;
1956*10491SRishi.Srivatsavai@Sun.COM 				mp = NULL;
1957*10491SRishi.Srivatsavai@Sun.COM 			} else {
1958*10491SRishi.Srivatsavai@Sun.COM 				mpsend = copymsg(mp);
1959*10491SRishi.Srivatsavai@Sun.COM 			}
1960*10491SRishi.Srivatsavai@Sun.COM 
1961*10491SRishi.Srivatsavai@Sun.COM 			if (!from_trill && is_xmit)
1962*10491SRishi.Srivatsavai@Sun.COM 				mpsend = mac_fix_cksum(mpsend);
1963*10491SRishi.Srivatsavai@Sun.COM 
1964*10491SRishi.Srivatsavai@Sun.COM 			mpsend = reform_vlan_header(mpsend, vlanid, tci,
1965*10491SRishi.Srivatsavai@Sun.COM 			    blpsend->bl_pvid);
1966*10491SRishi.Srivatsavai@Sun.COM 			if (mpsend == NULL) {
1967*10491SRishi.Srivatsavai@Sun.COM 				KIINCR(bki_drops);
1968*10491SRishi.Srivatsavai@Sun.COM 				continue;
1969*10491SRishi.Srivatsavai@Sun.COM 			}
1970*10491SRishi.Srivatsavai@Sun.COM 
1971*10491SRishi.Srivatsavai@Sun.COM 			KIINCR(bki_forwards);
1972*10491SRishi.Srivatsavai@Sun.COM 			/*
1973*10491SRishi.Srivatsavai@Sun.COM 			 * No need to bump up the link reference count, as
1974*10491SRishi.Srivatsavai@Sun.COM 			 * the forwarding entry itself holds a reference to
1975*10491SRishi.Srivatsavai@Sun.COM 			 * the link.
1976*10491SRishi.Srivatsavai@Sun.COM 			 */
1977*10491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_flags & BFF_LOCALADDR) {
1978*10491SRishi.Srivatsavai@Sun.COM 				mac_rx_common(blpsend->bl_mh, NULL, mpsend);
1979*10491SRishi.Srivatsavai@Sun.COM 			} else {
1980*10491SRishi.Srivatsavai@Sun.COM 				KLPINCR(blpsend, bkl_xmit);
1981*10491SRishi.Srivatsavai@Sun.COM 				MAC_RING_TX(blpsend->bl_mh, NULL, mpsend,
1982*10491SRishi.Srivatsavai@Sun.COM 				    mpsend);
1983*10491SRishi.Srivatsavai@Sun.COM 				freemsg(mpsend);
1984*10491SRishi.Srivatsavai@Sun.COM 			}
1985*10491SRishi.Srivatsavai@Sun.COM 		}
1986*10491SRishi.Srivatsavai@Sun.COM 		/*
1987*10491SRishi.Srivatsavai@Sun.COM 		 * Handle a special case: if we're transmitting to the original
1988*10491SRishi.Srivatsavai@Sun.COM 		 * link, then check whether the localaddr flag is set.  If it
1989*10491SRishi.Srivatsavai@Sun.COM 		 * is, then receive instead.  This doesn't happen with ordinary
1990*10491SRishi.Srivatsavai@Sun.COM 		 * bridging, but does happen often with TRILL decapsulation.
1991*10491SRishi.Srivatsavai@Sun.COM 		 */
1992*10491SRishi.Srivatsavai@Sun.COM 		if (mp != NULL && is_xmit && (bfp->bf_flags & BFF_LOCALADDR)) {
1993*10491SRishi.Srivatsavai@Sun.COM 			mac_rx_common(blp->bl_mh, NULL, mp);
1994*10491SRishi.Srivatsavai@Sun.COM 			mp = NULL;
1995*10491SRishi.Srivatsavai@Sun.COM 		}
1996*10491SRishi.Srivatsavai@Sun.COM 		fwd_unref(bfp);
1997*10491SRishi.Srivatsavai@Sun.COM 	} else {
1998*10491SRishi.Srivatsavai@Sun.COM 		/*
1999*10491SRishi.Srivatsavai@Sun.COM 		 * TRILL has two cases to handle.  If the packet is off the
2000*10491SRishi.Srivatsavai@Sun.COM 		 * wire (not from TRILL), then we need to send up into the
2001*10491SRishi.Srivatsavai@Sun.COM 		 * TRILL module to have the distribution tree computed.  If the
2002*10491SRishi.Srivatsavai@Sun.COM 		 * packet is from TRILL (decapsulated), then we're part of the
2003*10491SRishi.Srivatsavai@Sun.COM 		 * distribution tree, and we need to copy the packet on member
2004*10491SRishi.Srivatsavai@Sun.COM 		 * interfaces.
2005*10491SRishi.Srivatsavai@Sun.COM 		 *
2006*10491SRishi.Srivatsavai@Sun.COM 		 * Thus, the from TRILL case is identical to the STP case.
2007*10491SRishi.Srivatsavai@Sun.COM 		 */
2008*10491SRishi.Srivatsavai@Sun.COM 		if (!from_trill && blp->bl_trilldata != NULL) {
2009*10491SRishi.Srivatsavai@Sun.COM 			mutex_enter(&blp->bl_trilllock);
2010*10491SRishi.Srivatsavai@Sun.COM 			if ((tdp = blp->bl_trilldata) != NULL) {
2011*10491SRishi.Srivatsavai@Sun.COM 				blp->bl_trillthreads++;
2012*10491SRishi.Srivatsavai@Sun.COM 				mutex_exit(&blp->bl_trilllock);
2013*10491SRishi.Srivatsavai@Sun.COM 				if ((mpsend = copymsg(mp)) != NULL) {
2014*10491SRishi.Srivatsavai@Sun.COM 					update_header(mpsend,
2015*10491SRishi.Srivatsavai@Sun.COM 					    hdr_info, B_FALSE);
2016*10491SRishi.Srivatsavai@Sun.COM 					/*
2017*10491SRishi.Srivatsavai@Sun.COM 					 * all trill data frames have
2018*10491SRishi.Srivatsavai@Sun.COM 					 * Inner.VLAN
2019*10491SRishi.Srivatsavai@Sun.COM 					 */
2020*10491SRishi.Srivatsavai@Sun.COM 					mpsend = reform_vlan_header(mpsend,
2021*10491SRishi.Srivatsavai@Sun.COM 					    vlanid, tci, 0);
2022*10491SRishi.Srivatsavai@Sun.COM 					if (mpsend == NULL) {
2023*10491SRishi.Srivatsavai@Sun.COM 						KIINCR(bki_drops);
2024*10491SRishi.Srivatsavai@Sun.COM 					} else {
2025*10491SRishi.Srivatsavai@Sun.COM 						trill_encap_fn(tdp, blp,
2026*10491SRishi.Srivatsavai@Sun.COM 						    hdr_info, mpsend,
2027*10491SRishi.Srivatsavai@Sun.COM 						    RBRIDGE_NICKNAME_NONE);
2028*10491SRishi.Srivatsavai@Sun.COM 					}
2029*10491SRishi.Srivatsavai@Sun.COM 				}
2030*10491SRishi.Srivatsavai@Sun.COM 				mutex_enter(&blp->bl_trilllock);
2031*10491SRishi.Srivatsavai@Sun.COM 				if (--blp->bl_trillthreads == 0 &&
2032*10491SRishi.Srivatsavai@Sun.COM 				    blp->bl_trilldata == NULL)
2033*10491SRishi.Srivatsavai@Sun.COM 					cv_broadcast(&blp->bl_trillwait);
2034*10491SRishi.Srivatsavai@Sun.COM 			}
2035*10491SRishi.Srivatsavai@Sun.COM 			mutex_exit(&blp->bl_trilllock);
2036*10491SRishi.Srivatsavai@Sun.COM 		}
2037*10491SRishi.Srivatsavai@Sun.COM 
2038*10491SRishi.Srivatsavai@Sun.COM 		/*
2039*10491SRishi.Srivatsavai@Sun.COM 		 * This is an unknown destination, so flood.
2040*10491SRishi.Srivatsavai@Sun.COM 		 */
2041*10491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_READER);
2042*10491SRishi.Srivatsavai@Sun.COM 		for (blpnext = list_head(&bip->bi_links); blpnext != NULL;
2043*10491SRishi.Srivatsavai@Sun.COM 		    blpnext = list_next(&bip->bi_links, blpnext)) {
2044*10491SRishi.Srivatsavai@Sun.COM 			if (blpnext == blp)
2045*10491SRishi.Srivatsavai@Sun.COM 				selfseen = B_TRUE;
2046*10491SRishi.Srivatsavai@Sun.COM 			else if (bridge_can_send(blpnext, vlanid))
2047*10491SRishi.Srivatsavai@Sun.COM 				break;
2048*10491SRishi.Srivatsavai@Sun.COM 		}
2049*10491SRishi.Srivatsavai@Sun.COM 		if (blpnext != NULL)
2050*10491SRishi.Srivatsavai@Sun.COM 			atomic_inc_uint(&blpnext->bl_refs);
2051*10491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
2052*10491SRishi.Srivatsavai@Sun.COM 		while ((blpsend = blpnext) != NULL) {
2053*10491SRishi.Srivatsavai@Sun.COM 			rw_enter(&bip->bi_rwlock, RW_READER);
2054*10491SRishi.Srivatsavai@Sun.COM 			for (blpnext = list_next(&bip->bi_links, blpsend);
2055*10491SRishi.Srivatsavai@Sun.COM 			    blpnext != NULL;
2056*10491SRishi.Srivatsavai@Sun.COM 			    blpnext = list_next(&bip->bi_links, blpnext)) {
2057*10491SRishi.Srivatsavai@Sun.COM 				if (blpnext == blp)
2058*10491SRishi.Srivatsavai@Sun.COM 					selfseen = B_TRUE;
2059*10491SRishi.Srivatsavai@Sun.COM 				else if (bridge_can_send(blpnext, vlanid))
2060*10491SRishi.Srivatsavai@Sun.COM 					break;
2061*10491SRishi.Srivatsavai@Sun.COM 			}
2062*10491SRishi.Srivatsavai@Sun.COM 			if (blpnext != NULL)
2063*10491SRishi.Srivatsavai@Sun.COM 				atomic_inc_uint(&blpnext->bl_refs);
2064*10491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bip->bi_rwlock);
2065*10491SRishi.Srivatsavai@Sun.COM 			if (blpnext == NULL && !selfseen) {
2066*10491SRishi.Srivatsavai@Sun.COM 				mpsend = mp;
2067*10491SRishi.Srivatsavai@Sun.COM 				mp = NULL;
2068*10491SRishi.Srivatsavai@Sun.COM 			} else {
2069*10491SRishi.Srivatsavai@Sun.COM 				mpsend = copymsg(mp);
2070*10491SRishi.Srivatsavai@Sun.COM 			}
2071*10491SRishi.Srivatsavai@Sun.COM 
2072*10491SRishi.Srivatsavai@Sun.COM 			if (!from_trill && is_xmit)
2073*10491SRishi.Srivatsavai@Sun.COM 				mpsend = mac_fix_cksum(mpsend);
2074*10491SRishi.Srivatsavai@Sun.COM 
2075*10491SRishi.Srivatsavai@Sun.COM 			mpsend = reform_vlan_header(mpsend, vlanid, tci,
2076*10491SRishi.Srivatsavai@Sun.COM 			    blpsend->bl_pvid);
2077*10491SRishi.Srivatsavai@Sun.COM 			if (mpsend == NULL) {
2078*10491SRishi.Srivatsavai@Sun.COM 				KIINCR(bki_drops);
2079*10491SRishi.Srivatsavai@Sun.COM 				continue;
2080*10491SRishi.Srivatsavai@Sun.COM 			}
2081*10491SRishi.Srivatsavai@Sun.COM 
2082*10491SRishi.Srivatsavai@Sun.COM 			if (hdr_info->mhi_dsttype == MAC_ADDRTYPE_UNICAST)
2083*10491SRishi.Srivatsavai@Sun.COM 				KIINCR(bki_unknown);
2084*10491SRishi.Srivatsavai@Sun.COM 			else
2085*10491SRishi.Srivatsavai@Sun.COM 				KIINCR(bki_mbcast);
2086*10491SRishi.Srivatsavai@Sun.COM 			KLPINCR(blpsend, bkl_xmit);
2087*10491SRishi.Srivatsavai@Sun.COM 			if ((mpcopy = copymsg(mpsend)) != NULL)
2088*10491SRishi.Srivatsavai@Sun.COM 				mac_rx_common(blpsend->bl_mh, NULL, mpcopy);
2089*10491SRishi.Srivatsavai@Sun.COM 			MAC_RING_TX(blpsend->bl_mh, NULL, mpsend, mpsend);
2090*10491SRishi.Srivatsavai@Sun.COM 			freemsg(mpsend);
2091*10491SRishi.Srivatsavai@Sun.COM 			link_unref(blpsend);
2092*10491SRishi.Srivatsavai@Sun.COM 		}
2093*10491SRishi.Srivatsavai@Sun.COM 	}
2094*10491SRishi.Srivatsavai@Sun.COM 
2095*10491SRishi.Srivatsavai@Sun.COM 	/*
2096*10491SRishi.Srivatsavai@Sun.COM 	 * At this point, if np is non-NULL, it means that the caller needs to
2097*10491SRishi.Srivatsavai@Sun.COM 	 * continue on the selected link.
2098*10491SRishi.Srivatsavai@Sun.COM 	 */
2099*10491SRishi.Srivatsavai@Sun.COM 	return (mp);
2100*10491SRishi.Srivatsavai@Sun.COM }
2101*10491SRishi.Srivatsavai@Sun.COM 
2102*10491SRishi.Srivatsavai@Sun.COM /*
2103*10491SRishi.Srivatsavai@Sun.COM  * Extract and validate the VLAN information for a given packet.  This checks
2104*10491SRishi.Srivatsavai@Sun.COM  * conformance with the rules for use of the PVID on the link, and for the
2105*10491SRishi.Srivatsavai@Sun.COM  * allowed (configured) VLAN set.
2106*10491SRishi.Srivatsavai@Sun.COM  *
2107*10491SRishi.Srivatsavai@Sun.COM  * Returns B_TRUE if the packet passes, B_FALSE if it fails.
2108*10491SRishi.Srivatsavai@Sun.COM  */
2109*10491SRishi.Srivatsavai@Sun.COM static boolean_t
2110*10491SRishi.Srivatsavai@Sun.COM bridge_get_vlan(bridge_link_t *blp, mac_header_info_t *hdr_info, mblk_t *mp,
2111*10491SRishi.Srivatsavai@Sun.COM     uint16_t *vlanidp, uint16_t *tcip)
2112*10491SRishi.Srivatsavai@Sun.COM {
2113*10491SRishi.Srivatsavai@Sun.COM 	uint16_t tci, vlanid;
2114*10491SRishi.Srivatsavai@Sun.COM 
2115*10491SRishi.Srivatsavai@Sun.COM 	if (hdr_info->mhi_bindsap == ETHERTYPE_VLAN) {
2116*10491SRishi.Srivatsavai@Sun.COM 		ptrdiff_t tpos = offsetof(struct ether_vlan_header, ether_tci);
2117*10491SRishi.Srivatsavai@Sun.COM 		ptrdiff_t mlen;
2118*10491SRishi.Srivatsavai@Sun.COM 
2119*10491SRishi.Srivatsavai@Sun.COM 		/*
2120*10491SRishi.Srivatsavai@Sun.COM 		 * Extract the VLAN ID information, regardless of alignment,
2121*10491SRishi.Srivatsavai@Sun.COM 		 * and without a pullup.  This isn't attractive, but we do this
2122*10491SRishi.Srivatsavai@Sun.COM 		 * to avoid having to deal with the pointers stashed in
2123*10491SRishi.Srivatsavai@Sun.COM 		 * hdr_info moving around or having the caller deal with a new
2124*10491SRishi.Srivatsavai@Sun.COM 		 * mblk_t pointer.
2125*10491SRishi.Srivatsavai@Sun.COM 		 */
2126*10491SRishi.Srivatsavai@Sun.COM 		while (mp != NULL) {
2127*10491SRishi.Srivatsavai@Sun.COM 			mlen = MBLKL(mp);
2128*10491SRishi.Srivatsavai@Sun.COM 			if (mlen > tpos && mlen > 0)
2129*10491SRishi.Srivatsavai@Sun.COM 				break;
2130*10491SRishi.Srivatsavai@Sun.COM 			tpos -= mlen;
2131*10491SRishi.Srivatsavai@Sun.COM 			mp = mp->b_cont;
2132*10491SRishi.Srivatsavai@Sun.COM 		}
2133*10491SRishi.Srivatsavai@Sun.COM 		if (mp == NULL)
2134*10491SRishi.Srivatsavai@Sun.COM 			return (B_FALSE);
2135*10491SRishi.Srivatsavai@Sun.COM 		tci = mp->b_rptr[tpos] << 8;
2136*10491SRishi.Srivatsavai@Sun.COM 		if (++tpos >= mlen) {
2137*10491SRishi.Srivatsavai@Sun.COM 			do {
2138*10491SRishi.Srivatsavai@Sun.COM 				mp = mp->b_cont;
2139*10491SRishi.Srivatsavai@Sun.COM 			} while (mp != NULL && MBLKL(mp) == 0);
2140*10491SRishi.Srivatsavai@Sun.COM 			if (mp == NULL)
2141*10491SRishi.Srivatsavai@Sun.COM 				return (B_FALSE);
2142*10491SRishi.Srivatsavai@Sun.COM 			tpos = 0;
2143*10491SRishi.Srivatsavai@Sun.COM 		}
2144*10491SRishi.Srivatsavai@Sun.COM 		tci |= mp->b_rptr[tpos];
2145*10491SRishi.Srivatsavai@Sun.COM 
2146*10491SRishi.Srivatsavai@Sun.COM 		vlanid = VLAN_ID(tci);
2147*10491SRishi.Srivatsavai@Sun.COM 		if (VLAN_CFI(tci) != ETHER_CFI || vlanid > VLAN_ID_MAX)
2148*10491SRishi.Srivatsavai@Sun.COM 			return (B_FALSE);
2149*10491SRishi.Srivatsavai@Sun.COM 		if (vlanid == VLAN_ID_NONE || vlanid == blp->bl_pvid)
2150*10491SRishi.Srivatsavai@Sun.COM 			goto input_no_vlan;
2151*10491SRishi.Srivatsavai@Sun.COM 		if (!BRIDGE_VLAN_ISSET(blp, vlanid))
2152*10491SRishi.Srivatsavai@Sun.COM 			return (B_FALSE);
2153*10491SRishi.Srivatsavai@Sun.COM 	} else {
2154*10491SRishi.Srivatsavai@Sun.COM 		tci = 0xFFFF;
2155*10491SRishi.Srivatsavai@Sun.COM input_no_vlan:
2156*10491SRishi.Srivatsavai@Sun.COM 		/*
2157*10491SRishi.Srivatsavai@Sun.COM 		 * If PVID is set to zero, then untagged traffic is not
2158*10491SRishi.Srivatsavai@Sun.COM 		 * supported here.  Do not learn or forward.
2159*10491SRishi.Srivatsavai@Sun.COM 		 */
2160*10491SRishi.Srivatsavai@Sun.COM 		if ((vlanid = blp->bl_pvid) == VLAN_ID_NONE)
2161*10491SRishi.Srivatsavai@Sun.COM 			return (B_FALSE);
2162*10491SRishi.Srivatsavai@Sun.COM 	}
2163*10491SRishi.Srivatsavai@Sun.COM 
2164*10491SRishi.Srivatsavai@Sun.COM 	*tcip = tci;
2165*10491SRishi.Srivatsavai@Sun.COM 	*vlanidp = vlanid;
2166*10491SRishi.Srivatsavai@Sun.COM 	return (B_TRUE);
2167*10491SRishi.Srivatsavai@Sun.COM }
2168*10491SRishi.Srivatsavai@Sun.COM 
2169*10491SRishi.Srivatsavai@Sun.COM /*
2170*10491SRishi.Srivatsavai@Sun.COM  * Handle MAC notifications.
2171*10491SRishi.Srivatsavai@Sun.COM  */
2172*10491SRishi.Srivatsavai@Sun.COM static void
2173*10491SRishi.Srivatsavai@Sun.COM bridge_notify_cb(void *arg, mac_notify_type_t note_type)
2174*10491SRishi.Srivatsavai@Sun.COM {
2175*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp = arg;
2176*10491SRishi.Srivatsavai@Sun.COM 
2177*10491SRishi.Srivatsavai@Sun.COM 	switch (note_type) {
2178*10491SRishi.Srivatsavai@Sun.COM 	case MAC_NOTE_UNICST:
2179*10491SRishi.Srivatsavai@Sun.COM 		bridge_new_unicst(blp);
2180*10491SRishi.Srivatsavai@Sun.COM 		break;
2181*10491SRishi.Srivatsavai@Sun.COM 
2182*10491SRishi.Srivatsavai@Sun.COM 	case MAC_NOTE_SDU_SIZE: {
2183*10491SRishi.Srivatsavai@Sun.COM 		uint_t maxsdu;
2184*10491SRishi.Srivatsavai@Sun.COM 		bridge_inst_t *bip = blp->bl_inst;
2185*10491SRishi.Srivatsavai@Sun.COM 		bridge_mac_t *bmp = bip->bi_mac;
2186*10491SRishi.Srivatsavai@Sun.COM 		boolean_t notify = B_FALSE;
2187*10491SRishi.Srivatsavai@Sun.COM 		mblk_t *mlist = NULL;
2188*10491SRishi.Srivatsavai@Sun.COM 
2189*10491SRishi.Srivatsavai@Sun.COM 		mac_sdu_get(blp->bl_mh, NULL, &maxsdu);
2190*10491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_READER);
2191*10491SRishi.Srivatsavai@Sun.COM 		if (list_prev(&bip->bi_links, blp) == NULL &&
2192*10491SRishi.Srivatsavai@Sun.COM 		    list_next(&bip->bi_links, blp) == NULL) {
2193*10491SRishi.Srivatsavai@Sun.COM 			notify = (maxsdu != bmp->bm_maxsdu);
2194*10491SRishi.Srivatsavai@Sun.COM 			bmp->bm_maxsdu = maxsdu;
2195*10491SRishi.Srivatsavai@Sun.COM 		}
2196*10491SRishi.Srivatsavai@Sun.COM 		blp->bl_maxsdu = maxsdu;
2197*10491SRishi.Srivatsavai@Sun.COM 		if (maxsdu != bmp->bm_maxsdu)
2198*10491SRishi.Srivatsavai@Sun.COM 			link_sdu_fail(blp, B_TRUE, &mlist);
2199*10491SRishi.Srivatsavai@Sun.COM 		else if (notify)
2200*10491SRishi.Srivatsavai@Sun.COM 			(void) mac_maxsdu_update(bmp->bm_mh, maxsdu);
2201*10491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
2202*10491SRishi.Srivatsavai@Sun.COM 		send_up_messages(bip, mlist);
2203*10491SRishi.Srivatsavai@Sun.COM 		break;
2204*10491SRishi.Srivatsavai@Sun.COM 	}
2205*10491SRishi.Srivatsavai@Sun.COM 	}
2206*10491SRishi.Srivatsavai@Sun.COM }
2207*10491SRishi.Srivatsavai@Sun.COM 
2208*10491SRishi.Srivatsavai@Sun.COM /*
2209*10491SRishi.Srivatsavai@Sun.COM  * This is called by the MAC layer.  As with the transmit side, we're right in
2210*10491SRishi.Srivatsavai@Sun.COM  * the data path for all I/O on this port, so if we don't need to forward this
2211*10491SRishi.Srivatsavai@Sun.COM  * packet anywhere, we have to send it upwards via mac_rx_common.
2212*10491SRishi.Srivatsavai@Sun.COM  */
2213*10491SRishi.Srivatsavai@Sun.COM static void
2214*10491SRishi.Srivatsavai@Sun.COM bridge_recv_cb(mac_handle_t mh, mac_resource_handle_t rsrc, mblk_t *mpnext)
2215*10491SRishi.Srivatsavai@Sun.COM {
2216*10491SRishi.Srivatsavai@Sun.COM 	mblk_t *mp, *mpcopy;
2217*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp = (bridge_link_t *)mh;
2218*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;
2219*10491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp = bip->bi_mac;
2220*10491SRishi.Srivatsavai@Sun.COM 	mac_header_info_t hdr_info;
2221*10491SRishi.Srivatsavai@Sun.COM 	uint16_t vlanid, tci;
2222*10491SRishi.Srivatsavai@Sun.COM 	boolean_t trillmode = B_FALSE;
2223*10491SRishi.Srivatsavai@Sun.COM 
2224*10491SRishi.Srivatsavai@Sun.COM 	KIINCR(bki_recv);
2225*10491SRishi.Srivatsavai@Sun.COM 	KLINCR(bkl_recv);
2226*10491SRishi.Srivatsavai@Sun.COM 
2227*10491SRishi.Srivatsavai@Sun.COM 	/*
2228*10491SRishi.Srivatsavai@Sun.COM 	 * Regardless of state, check for inbound TRILL packets when TRILL is
2229*10491SRishi.Srivatsavai@Sun.COM 	 * active.  These are pulled out of band and sent for TRILL handling.
2230*10491SRishi.Srivatsavai@Sun.COM 	 */
2231*10491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_trilldata != NULL) {
2232*10491SRishi.Srivatsavai@Sun.COM 		void *tdp;
2233*10491SRishi.Srivatsavai@Sun.COM 		mblk_t *newhead;
2234*10491SRishi.Srivatsavai@Sun.COM 		mblk_t *tail = NULL;
2235*10491SRishi.Srivatsavai@Sun.COM 
2236*10491SRishi.Srivatsavai@Sun.COM 		mutex_enter(&blp->bl_trilllock);
2237*10491SRishi.Srivatsavai@Sun.COM 		if ((tdp = blp->bl_trilldata) != NULL) {
2238*10491SRishi.Srivatsavai@Sun.COM 			blp->bl_trillthreads++;
2239*10491SRishi.Srivatsavai@Sun.COM 			mutex_exit(&blp->bl_trilllock);
2240*10491SRishi.Srivatsavai@Sun.COM 			trillmode = B_TRUE;
2241*10491SRishi.Srivatsavai@Sun.COM 			newhead = mpnext;
2242*10491SRishi.Srivatsavai@Sun.COM 			while ((mp = mpnext) != NULL) {
2243*10491SRishi.Srivatsavai@Sun.COM 				boolean_t raw_isis, bridge_group;
2244*10491SRishi.Srivatsavai@Sun.COM 
2245*10491SRishi.Srivatsavai@Sun.COM 				mpnext = mp->b_next;
2246*10491SRishi.Srivatsavai@Sun.COM 
2247*10491SRishi.Srivatsavai@Sun.COM 				/*
2248*10491SRishi.Srivatsavai@Sun.COM 				 * If the header isn't readable, then leave on
2249*10491SRishi.Srivatsavai@Sun.COM 				 * the list and continue.
2250*10491SRishi.Srivatsavai@Sun.COM 				 */
2251*10491SRishi.Srivatsavai@Sun.COM 				if (mac_header_info(blp->bl_mh, mp,
2252*10491SRishi.Srivatsavai@Sun.COM 				    &hdr_info) != 0) {
2253*10491SRishi.Srivatsavai@Sun.COM 					tail = mp;
2254*10491SRishi.Srivatsavai@Sun.COM 					continue;
2255*10491SRishi.Srivatsavai@Sun.COM 				}
2256*10491SRishi.Srivatsavai@Sun.COM 
2257*10491SRishi.Srivatsavai@Sun.COM 				/*
2258*10491SRishi.Srivatsavai@Sun.COM 				 * The TRILL document specifies that, on
2259*10491SRishi.Srivatsavai@Sun.COM 				 * Ethernet alone, IS-IS packets arrive with
2260*10491SRishi.Srivatsavai@Sun.COM 				 * LLC rather than Ethertype, and using a
2261*10491SRishi.Srivatsavai@Sun.COM 				 * specific destination address.  We must check
2262*10491SRishi.Srivatsavai@Sun.COM 				 * for that here.  Also, we need to give BPDUs
2263*10491SRishi.Srivatsavai@Sun.COM 				 * to TRILL for processing.
2264*10491SRishi.Srivatsavai@Sun.COM 				 */
2265*10491SRishi.Srivatsavai@Sun.COM 				raw_isis = bridge_group = B_FALSE;
2266*10491SRishi.Srivatsavai@Sun.COM 				if (hdr_info.mhi_dsttype ==
2267*10491SRishi.Srivatsavai@Sun.COM 				    MAC_ADDRTYPE_MULTICAST) {
2268*10491SRishi.Srivatsavai@Sun.COM 					if (memcmp(hdr_info.mhi_daddr,
2269*10491SRishi.Srivatsavai@Sun.COM 					    all_isis_rbridges, ETHERADDRL) == 0)
2270*10491SRishi.Srivatsavai@Sun.COM 						raw_isis = B_TRUE;
2271*10491SRishi.Srivatsavai@Sun.COM 					else if (memcmp(hdr_info.mhi_daddr,
2272*10491SRishi.Srivatsavai@Sun.COM 					    bridge_group_address, ETHERADDRL) ==
2273*10491SRishi.Srivatsavai@Sun.COM 					    0)
2274*10491SRishi.Srivatsavai@Sun.COM 						bridge_group = B_TRUE;
2275*10491SRishi.Srivatsavai@Sun.COM 				}
2276*10491SRishi.Srivatsavai@Sun.COM 				if (!raw_isis && !bridge_group &&
2277*10491SRishi.Srivatsavai@Sun.COM 				    hdr_info.mhi_bindsap != ETHERTYPE_TRILL &&
2278*10491SRishi.Srivatsavai@Sun.COM 				    (hdr_info.mhi_bindsap != ETHERTYPE_VLAN ||
2279*10491SRishi.Srivatsavai@Sun.COM 				    /* LINTED: alignment */
2280*10491SRishi.Srivatsavai@Sun.COM 				    ((struct ether_vlan_header *)mp->b_rptr)->
2281*10491SRishi.Srivatsavai@Sun.COM 				    ether_type != htons(ETHERTYPE_TRILL))) {
2282*10491SRishi.Srivatsavai@Sun.COM 					tail = mp;
2283*10491SRishi.Srivatsavai@Sun.COM 					continue;
2284*10491SRishi.Srivatsavai@Sun.COM 				}
2285*10491SRishi.Srivatsavai@Sun.COM 
2286*10491SRishi.Srivatsavai@Sun.COM 				/*
2287*10491SRishi.Srivatsavai@Sun.COM 				 * We've got TRILL input.  Remove from the list
2288*10491SRishi.Srivatsavai@Sun.COM 				 * and send up through the TRILL module.  (Send
2289*10491SRishi.Srivatsavai@Sun.COM 				 * a copy through promiscuous receive just to
2290*10491SRishi.Srivatsavai@Sun.COM 				 * support snooping on TRILL.  Order isn't
2291*10491SRishi.Srivatsavai@Sun.COM 				 * preserved strictly, but that doesn't matter
2292*10491SRishi.Srivatsavai@Sun.COM 				 * here.)
2293*10491SRishi.Srivatsavai@Sun.COM 				 */
2294*10491SRishi.Srivatsavai@Sun.COM 				if (tail != NULL)
2295*10491SRishi.Srivatsavai@Sun.COM 					tail->b_next = mpnext;
2296*10491SRishi.Srivatsavai@Sun.COM 				mp->b_next = NULL;
2297*10491SRishi.Srivatsavai@Sun.COM 				if (mp == newhead)
2298*10491SRishi.Srivatsavai@Sun.COM 					newhead = mpnext;
2299*10491SRishi.Srivatsavai@Sun.COM 				mac_trill_snoop(blp->bl_mh, mp);
2300*10491SRishi.Srivatsavai@Sun.COM 				update_header(mp, &hdr_info, B_TRUE);
2301*10491SRishi.Srivatsavai@Sun.COM 				/*
2302*10491SRishi.Srivatsavai@Sun.COM 				 * On raw IS-IS and BPDU frames, we have to
2303*10491SRishi.Srivatsavai@Sun.COM 				 * make sure that the length is trimmed
2304*10491SRishi.Srivatsavai@Sun.COM 				 * properly.  We use origsap in order to cope
2305*10491SRishi.Srivatsavai@Sun.COM 				 * with jumbograms for IS-IS.  (Regular mac
2306*10491SRishi.Srivatsavai@Sun.COM 				 * can't.)
2307*10491SRishi.Srivatsavai@Sun.COM 				 */
2308*10491SRishi.Srivatsavai@Sun.COM 				if (raw_isis || bridge_group) {
2309*10491SRishi.Srivatsavai@Sun.COM 					size_t msglen = msgdsize(mp);
2310*10491SRishi.Srivatsavai@Sun.COM 
2311*10491SRishi.Srivatsavai@Sun.COM 					if (msglen > hdr_info.mhi_origsap) {
2312*10491SRishi.Srivatsavai@Sun.COM 						(void) adjmsg(mp,
2313*10491SRishi.Srivatsavai@Sun.COM 						    hdr_info.mhi_origsap -
2314*10491SRishi.Srivatsavai@Sun.COM 						    msglen);
2315*10491SRishi.Srivatsavai@Sun.COM 					} else if (msglen <
2316*10491SRishi.Srivatsavai@Sun.COM 					    hdr_info.mhi_origsap) {
2317*10491SRishi.Srivatsavai@Sun.COM 						freemsg(mp);
2318*10491SRishi.Srivatsavai@Sun.COM 						continue;
2319*10491SRishi.Srivatsavai@Sun.COM 					}
2320*10491SRishi.Srivatsavai@Sun.COM 				}
2321*10491SRishi.Srivatsavai@Sun.COM 				trill_recv_fn(tdp, blp, rsrc, mp, &hdr_info);
2322*10491SRishi.Srivatsavai@Sun.COM 			}
2323*10491SRishi.Srivatsavai@Sun.COM 			mpnext = newhead;
2324*10491SRishi.Srivatsavai@Sun.COM 			mutex_enter(&blp->bl_trilllock);
2325*10491SRishi.Srivatsavai@Sun.COM 			if (--blp->bl_trillthreads == 0 &&
2326*10491SRishi.Srivatsavai@Sun.COM 			    blp->bl_trilldata == NULL)
2327*10491SRishi.Srivatsavai@Sun.COM 				cv_broadcast(&blp->bl_trillwait);
2328*10491SRishi.Srivatsavai@Sun.COM 		}
2329*10491SRishi.Srivatsavai@Sun.COM 		mutex_exit(&blp->bl_trilllock);
2330*10491SRishi.Srivatsavai@Sun.COM 		if (mpnext == NULL)
2331*10491SRishi.Srivatsavai@Sun.COM 			return;
2332*10491SRishi.Srivatsavai@Sun.COM 	}
2333*10491SRishi.Srivatsavai@Sun.COM 
2334*10491SRishi.Srivatsavai@Sun.COM 	/*
2335*10491SRishi.Srivatsavai@Sun.COM 	 * If this is a TRILL RBridge, then just check whether this link is
2336*10491SRishi.Srivatsavai@Sun.COM 	 * used at all for forwarding.  If not, then we're done.
2337*10491SRishi.Srivatsavai@Sun.COM 	 */
2338*10491SRishi.Srivatsavai@Sun.COM 	if (trillmode) {
2339*10491SRishi.Srivatsavai@Sun.COM 		if (!(blp->bl_flags & BLF_TRILLACTIVE) ||
2340*10491SRishi.Srivatsavai@Sun.COM 		    (blp->bl_flags & BLF_SDUFAIL)) {
2341*10491SRishi.Srivatsavai@Sun.COM 			mac_rx_common(blp->bl_mh, rsrc, mpnext);
2342*10491SRishi.Srivatsavai@Sun.COM 			return;
2343*10491SRishi.Srivatsavai@Sun.COM 		}
2344*10491SRishi.Srivatsavai@Sun.COM 	} else {
2345*10491SRishi.Srivatsavai@Sun.COM 		/*
2346*10491SRishi.Srivatsavai@Sun.COM 		 * For regular (STP) bridges, if we're in blocking or listening
2347*10491SRishi.Srivatsavai@Sun.COM 		 * state, then do nothing.  We don't learn or forward until
2348*10491SRishi.Srivatsavai@Sun.COM 		 * told to do so.
2349*10491SRishi.Srivatsavai@Sun.COM 		 */
2350*10491SRishi.Srivatsavai@Sun.COM 		if (blp->bl_state == BLS_BLOCKLISTEN) {
2351*10491SRishi.Srivatsavai@Sun.COM 			mac_rx_common(blp->bl_mh, rsrc, mpnext);
2352*10491SRishi.Srivatsavai@Sun.COM 			return;
2353*10491SRishi.Srivatsavai@Sun.COM 		}
2354*10491SRishi.Srivatsavai@Sun.COM 	}
2355*10491SRishi.Srivatsavai@Sun.COM 
2356*10491SRishi.Srivatsavai@Sun.COM 	/*
2357*10491SRishi.Srivatsavai@Sun.COM 	 * Send a copy of the message chain up to the observability node users.
2358*10491SRishi.Srivatsavai@Sun.COM 	 * For TRILL, we must obey the VLAN AF rules, so we go packet-by-
2359*10491SRishi.Srivatsavai@Sun.COM 	 * packet.
2360*10491SRishi.Srivatsavai@Sun.COM 	 */
2361*10491SRishi.Srivatsavai@Sun.COM 	if (!trillmode && blp->bl_state == BLS_FORWARDING &&
2362*10491SRishi.Srivatsavai@Sun.COM 	    (bmp->bm_flags & BMF_STARTED) &&
2363*10491SRishi.Srivatsavai@Sun.COM 	    (mp = copymsgchain(mpnext)) != NULL) {
2364*10491SRishi.Srivatsavai@Sun.COM 		mac_rx(bmp->bm_mh, NULL, mp);
2365*10491SRishi.Srivatsavai@Sun.COM 	}
2366*10491SRishi.Srivatsavai@Sun.COM 
2367*10491SRishi.Srivatsavai@Sun.COM 	/*
2368*10491SRishi.Srivatsavai@Sun.COM 	 * We must be in learning or forwarding state, or using TRILL on a link
2369*10491SRishi.Srivatsavai@Sun.COM 	 * with one or more VLANs active.  For each packet in the list, process
2370*10491SRishi.Srivatsavai@Sun.COM 	 * the source address, and then attempt to forward.
2371*10491SRishi.Srivatsavai@Sun.COM 	 */
2372*10491SRishi.Srivatsavai@Sun.COM 	while ((mp = mpnext) != NULL) {
2373*10491SRishi.Srivatsavai@Sun.COM 		mpnext = mp->b_next;
2374*10491SRishi.Srivatsavai@Sun.COM 		mp->b_next = NULL;
2375*10491SRishi.Srivatsavai@Sun.COM 
2376*10491SRishi.Srivatsavai@Sun.COM 		/*
2377*10491SRishi.Srivatsavai@Sun.COM 		 * If we can't decode the header or if the header specifies a
2378*10491SRishi.Srivatsavai@Sun.COM 		 * multicast source address (impossible!), then don't bother
2379*10491SRishi.Srivatsavai@Sun.COM 		 * learning or forwarding, but go ahead and forward up the
2380*10491SRishi.Srivatsavai@Sun.COM 		 * stack for subsequent processing.
2381*10491SRishi.Srivatsavai@Sun.COM 		 */
2382*10491SRishi.Srivatsavai@Sun.COM 		if (mac_header_info(blp->bl_mh, mp, &hdr_info) != 0 ||
2383*10491SRishi.Srivatsavai@Sun.COM 		    (hdr_info.mhi_saddr[0] & 1) != 0) {
2384*10491SRishi.Srivatsavai@Sun.COM 			KIINCR(bki_drops);
2385*10491SRishi.Srivatsavai@Sun.COM 			KLINCR(bkl_drops);
2386*10491SRishi.Srivatsavai@Sun.COM 			mac_rx_common(blp->bl_mh, rsrc, mp);
2387*10491SRishi.Srivatsavai@Sun.COM 			continue;
2388*10491SRishi.Srivatsavai@Sun.COM 		}
2389*10491SRishi.Srivatsavai@Sun.COM 
2390*10491SRishi.Srivatsavai@Sun.COM 		/*
2391*10491SRishi.Srivatsavai@Sun.COM 		 * Extract and validate the VLAN ID for this packet.
2392*10491SRishi.Srivatsavai@Sun.COM 		 */
2393*10491SRishi.Srivatsavai@Sun.COM 		if (!bridge_get_vlan(blp, &hdr_info, mp, &vlanid, &tci) ||
2394*10491SRishi.Srivatsavai@Sun.COM 		    !BRIDGE_AF_ISSET(blp, vlanid)) {
2395*10491SRishi.Srivatsavai@Sun.COM 			mac_rx_common(blp->bl_mh, rsrc, mp);
2396*10491SRishi.Srivatsavai@Sun.COM 			continue;
2397*10491SRishi.Srivatsavai@Sun.COM 		}
2398*10491SRishi.Srivatsavai@Sun.COM 
2399*10491SRishi.Srivatsavai@Sun.COM 		if (trillmode) {
2400*10491SRishi.Srivatsavai@Sun.COM 			/*
2401*10491SRishi.Srivatsavai@Sun.COM 			 * Special test required by TRILL document: must
2402*10491SRishi.Srivatsavai@Sun.COM 			 * discard frames with outer address set to ESADI.
2403*10491SRishi.Srivatsavai@Sun.COM 			 */
2404*10491SRishi.Srivatsavai@Sun.COM 			if (memcmp(hdr_info.mhi_daddr, all_esadi_rbridges,
2405*10491SRishi.Srivatsavai@Sun.COM 			    ETHERADDRL) == 0) {
2406*10491SRishi.Srivatsavai@Sun.COM 				mac_rx_common(blp->bl_mh, rsrc, mp);
2407*10491SRishi.Srivatsavai@Sun.COM 				continue;
2408*10491SRishi.Srivatsavai@Sun.COM 			}
2409*10491SRishi.Srivatsavai@Sun.COM 
2410*10491SRishi.Srivatsavai@Sun.COM 			/*
2411*10491SRishi.Srivatsavai@Sun.COM 			 * If we're in TRILL mode, then the call above to get
2412*10491SRishi.Srivatsavai@Sun.COM 			 * the VLAN ID has also checked that we're the
2413*10491SRishi.Srivatsavai@Sun.COM 			 * appointed forwarder, so report that we're handling
2414*10491SRishi.Srivatsavai@Sun.COM 			 * this packet to any observability node users.
2415*10491SRishi.Srivatsavai@Sun.COM 			 */
2416*10491SRishi.Srivatsavai@Sun.COM 			if ((bmp->bm_flags & BMF_STARTED) &&
2417*10491SRishi.Srivatsavai@Sun.COM 			    (mpcopy = copymsg(mp)) != NULL)
2418*10491SRishi.Srivatsavai@Sun.COM 				mac_rx(bmp->bm_mh, NULL, mpcopy);
2419*10491SRishi.Srivatsavai@Sun.COM 		}
2420*10491SRishi.Srivatsavai@Sun.COM 
2421*10491SRishi.Srivatsavai@Sun.COM 		/*
2422*10491SRishi.Srivatsavai@Sun.COM 		 * First process the source address and learn from it.  For
2423*10491SRishi.Srivatsavai@Sun.COM 		 * TRILL, we learn only if we're the appointed forwarder.
2424*10491SRishi.Srivatsavai@Sun.COM 		 */
2425*10491SRishi.Srivatsavai@Sun.COM 		bridge_learn(blp, hdr_info.mhi_saddr, RBRIDGE_NICKNAME_NONE,
2426*10491SRishi.Srivatsavai@Sun.COM 		    vlanid);
2427*10491SRishi.Srivatsavai@Sun.COM 
2428*10491SRishi.Srivatsavai@Sun.COM 		/*
2429*10491SRishi.Srivatsavai@Sun.COM 		 * Now check whether we're forwarding and look up the
2430*10491SRishi.Srivatsavai@Sun.COM 		 * destination.  If we can forward, do so.
2431*10491SRishi.Srivatsavai@Sun.COM 		 */
2432*10491SRishi.Srivatsavai@Sun.COM 		if (trillmode || blp->bl_state == BLS_FORWARDING) {
2433*10491SRishi.Srivatsavai@Sun.COM 			mp = bridge_forward(blp, &hdr_info, mp, vlanid, tci,
2434*10491SRishi.Srivatsavai@Sun.COM 			    B_FALSE, B_FALSE);
2435*10491SRishi.Srivatsavai@Sun.COM 		}
2436*10491SRishi.Srivatsavai@Sun.COM 		if (mp != NULL)
2437*10491SRishi.Srivatsavai@Sun.COM 			mac_rx_common(blp->bl_mh, rsrc, mp);
2438*10491SRishi.Srivatsavai@Sun.COM 	}
2439*10491SRishi.Srivatsavai@Sun.COM }
2440*10491SRishi.Srivatsavai@Sun.COM 
2441*10491SRishi.Srivatsavai@Sun.COM 
2442*10491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
2443*10491SRishi.Srivatsavai@Sun.COM static mblk_t *
2444*10491SRishi.Srivatsavai@Sun.COM bridge_xmit_cb(mac_handle_t mh, mac_ring_handle_t rh, mblk_t *mpnext)
2445*10491SRishi.Srivatsavai@Sun.COM {
2446*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp = (bridge_link_t *)mh;
2447*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;
2448*10491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp = bip->bi_mac;
2449*10491SRishi.Srivatsavai@Sun.COM 	mac_header_info_t hdr_info;
2450*10491SRishi.Srivatsavai@Sun.COM 	uint16_t vlanid, tci;
2451*10491SRishi.Srivatsavai@Sun.COM 	mblk_t *mp, *mpcopy;
2452*10491SRishi.Srivatsavai@Sun.COM 	boolean_t trillmode;
2453*10491SRishi.Srivatsavai@Sun.COM 
2454*10491SRishi.Srivatsavai@Sun.COM 	trillmode = blp->bl_trilldata != NULL;
2455*10491SRishi.Srivatsavai@Sun.COM 
2456*10491SRishi.Srivatsavai@Sun.COM 	/*
2457*10491SRishi.Srivatsavai@Sun.COM 	 * If we're using STP and we're in blocking or listening state, or if
2458*10491SRishi.Srivatsavai@Sun.COM 	 * we're using TRILL and no VLANs are active, then behave as though the
2459*10491SRishi.Srivatsavai@Sun.COM 	 * bridge isn't here at all, and send on the local link alone.
2460*10491SRishi.Srivatsavai@Sun.COM 	 */
2461*10491SRishi.Srivatsavai@Sun.COM 	if ((!trillmode && blp->bl_state == BLS_BLOCKLISTEN) ||
2462*10491SRishi.Srivatsavai@Sun.COM 	    (trillmode &&
2463*10491SRishi.Srivatsavai@Sun.COM 	    (!(blp->bl_flags & BLF_TRILLACTIVE) ||
2464*10491SRishi.Srivatsavai@Sun.COM 	    (blp->bl_flags & BLF_SDUFAIL)))) {
2465*10491SRishi.Srivatsavai@Sun.COM 		KIINCR(bki_sent);
2466*10491SRishi.Srivatsavai@Sun.COM 		KLINCR(bkl_xmit);
2467*10491SRishi.Srivatsavai@Sun.COM 		MAC_RING_TX(blp->bl_mh, rh, mpnext, mp);
2468*10491SRishi.Srivatsavai@Sun.COM 		return (mp);
2469*10491SRishi.Srivatsavai@Sun.COM 	}
2470*10491SRishi.Srivatsavai@Sun.COM 
2471*10491SRishi.Srivatsavai@Sun.COM 	/*
2472*10491SRishi.Srivatsavai@Sun.COM 	 * Send a copy of the message up to the observability node users.
2473*10491SRishi.Srivatsavai@Sun.COM 	 * TRILL needs to check on a packet-by-packet basis.
2474*10491SRishi.Srivatsavai@Sun.COM 	 */
2475*10491SRishi.Srivatsavai@Sun.COM 	if (!trillmode && blp->bl_state == BLS_FORWARDING &&
2476*10491SRishi.Srivatsavai@Sun.COM 	    (bmp->bm_flags & BMF_STARTED) &&
2477*10491SRishi.Srivatsavai@Sun.COM 	    (mp = copymsgchain(mpnext)) != NULL) {
2478*10491SRishi.Srivatsavai@Sun.COM 		mac_rx(bmp->bm_mh, NULL, mp);
2479*10491SRishi.Srivatsavai@Sun.COM 	}
2480*10491SRishi.Srivatsavai@Sun.COM 
2481*10491SRishi.Srivatsavai@Sun.COM 	while ((mp = mpnext) != NULL) {
2482*10491SRishi.Srivatsavai@Sun.COM 		mpnext = mp->b_next;
2483*10491SRishi.Srivatsavai@Sun.COM 		mp->b_next = NULL;
2484*10491SRishi.Srivatsavai@Sun.COM 
2485*10491SRishi.Srivatsavai@Sun.COM 		if (mac_header_info(blp->bl_mh, mp, &hdr_info) != 0) {
2486*10491SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
2487*10491SRishi.Srivatsavai@Sun.COM 			continue;
2488*10491SRishi.Srivatsavai@Sun.COM 		}
2489*10491SRishi.Srivatsavai@Sun.COM 
2490*10491SRishi.Srivatsavai@Sun.COM 		/*
2491*10491SRishi.Srivatsavai@Sun.COM 		 * Extract and validate the VLAN ID for this packet.
2492*10491SRishi.Srivatsavai@Sun.COM 		 */
2493*10491SRishi.Srivatsavai@Sun.COM 		if (!bridge_get_vlan(blp, &hdr_info, mp, &vlanid, &tci) ||
2494*10491SRishi.Srivatsavai@Sun.COM 		    !BRIDGE_AF_ISSET(blp, vlanid)) {
2495*10491SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
2496*10491SRishi.Srivatsavai@Sun.COM 			continue;
2497*10491SRishi.Srivatsavai@Sun.COM 		}
2498*10491SRishi.Srivatsavai@Sun.COM 
2499*10491SRishi.Srivatsavai@Sun.COM 		/*
2500*10491SRishi.Srivatsavai@Sun.COM 		 * If we're using TRILL, then we've now validated that we're
2501*10491SRishi.Srivatsavai@Sun.COM 		 * the forwarder for this VLAN, so go ahead and let
2502*10491SRishi.Srivatsavai@Sun.COM 		 * observability node users know about the packet.
2503*10491SRishi.Srivatsavai@Sun.COM 		 */
2504*10491SRishi.Srivatsavai@Sun.COM 		if (trillmode && (bmp->bm_flags & BMF_STARTED) &&
2505*10491SRishi.Srivatsavai@Sun.COM 		    (mpcopy = copymsg(mp)) != NULL) {
2506*10491SRishi.Srivatsavai@Sun.COM 			mac_rx(bmp->bm_mh, NULL, mpcopy);
2507*10491SRishi.Srivatsavai@Sun.COM 		}
2508*10491SRishi.Srivatsavai@Sun.COM 
2509*10491SRishi.Srivatsavai@Sun.COM 		/*
2510*10491SRishi.Srivatsavai@Sun.COM 		 * We have to learn from our own transmitted packets, because
2511*10491SRishi.Srivatsavai@Sun.COM 		 * there may be a Solaris DLPI raw sender (who can specify his
2512*10491SRishi.Srivatsavai@Sun.COM 		 * own source address) using promiscuous mode for receive.  The
2513*10491SRishi.Srivatsavai@Sun.COM 		 * mac layer information won't (and can't) tell us everything
2514*10491SRishi.Srivatsavai@Sun.COM 		 * we need to know.
2515*10491SRishi.Srivatsavai@Sun.COM 		 */
2516*10491SRishi.Srivatsavai@Sun.COM 		bridge_learn(blp, hdr_info.mhi_saddr, RBRIDGE_NICKNAME_NONE,
2517*10491SRishi.Srivatsavai@Sun.COM 		    vlanid);
2518*10491SRishi.Srivatsavai@Sun.COM 
2519*10491SRishi.Srivatsavai@Sun.COM 		/* attempt forwarding */
2520*10491SRishi.Srivatsavai@Sun.COM 		if (trillmode || blp->bl_state == BLS_FORWARDING) {
2521*10491SRishi.Srivatsavai@Sun.COM 			mp = bridge_forward(blp, &hdr_info, mp, vlanid, tci,
2522*10491SRishi.Srivatsavai@Sun.COM 			    B_FALSE, B_TRUE);
2523*10491SRishi.Srivatsavai@Sun.COM 		}
2524*10491SRishi.Srivatsavai@Sun.COM 		if (mp != NULL) {
2525*10491SRishi.Srivatsavai@Sun.COM 			MAC_RING_TX(blp->bl_mh, rh, mp, mp);
2526*10491SRishi.Srivatsavai@Sun.COM 			if (mp == NULL) {
2527*10491SRishi.Srivatsavai@Sun.COM 				KIINCR(bki_sent);
2528*10491SRishi.Srivatsavai@Sun.COM 				KLINCR(bkl_xmit);
2529*10491SRishi.Srivatsavai@Sun.COM 			}
2530*10491SRishi.Srivatsavai@Sun.COM 		}
2531*10491SRishi.Srivatsavai@Sun.COM 		/*
2532*10491SRishi.Srivatsavai@Sun.COM 		 * If we get stuck, then stop.  Don't let the user's output
2533*10491SRishi.Srivatsavai@Sun.COM 		 * packets get out of order.  (More importantly: don't try to
2534*10491SRishi.Srivatsavai@Sun.COM 		 * bridge the same packet multiple times if flow control is
2535*10491SRishi.Srivatsavai@Sun.COM 		 * asserted.)
2536*10491SRishi.Srivatsavai@Sun.COM 		 */
2537*10491SRishi.Srivatsavai@Sun.COM 		if (mp != NULL) {
2538*10491SRishi.Srivatsavai@Sun.COM 			mp->b_next = mpnext;
2539*10491SRishi.Srivatsavai@Sun.COM 			break;
2540*10491SRishi.Srivatsavai@Sun.COM 		}
2541*10491SRishi.Srivatsavai@Sun.COM 	}
2542*10491SRishi.Srivatsavai@Sun.COM 	return (mp);
2543*10491SRishi.Srivatsavai@Sun.COM }
2544*10491SRishi.Srivatsavai@Sun.COM 
2545*10491SRishi.Srivatsavai@Sun.COM /*
2546*10491SRishi.Srivatsavai@Sun.COM  * This is called by TRILL when it decapsulates an packet, and we must forward
2547*10491SRishi.Srivatsavai@Sun.COM  * locally.  On failure, we just drop.
2548*10491SRishi.Srivatsavai@Sun.COM  *
2549*10491SRishi.Srivatsavai@Sun.COM  * Note that the ingress_nick reported by TRILL must not represent this local
2550*10491SRishi.Srivatsavai@Sun.COM  * node.
2551*10491SRishi.Srivatsavai@Sun.COM  */
2552*10491SRishi.Srivatsavai@Sun.COM void
2553*10491SRishi.Srivatsavai@Sun.COM bridge_trill_decaps(bridge_link_t *blp, mblk_t *mp, uint16_t ingress_nick)
2554*10491SRishi.Srivatsavai@Sun.COM {
2555*10491SRishi.Srivatsavai@Sun.COM 	mac_header_info_t hdr_info;
2556*10491SRishi.Srivatsavai@Sun.COM 	uint16_t vlanid, tci;
2557*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;	/* used by macros */
2558*10491SRishi.Srivatsavai@Sun.COM 	mblk_t *mpcopy;
2559*10491SRishi.Srivatsavai@Sun.COM 
2560*10491SRishi.Srivatsavai@Sun.COM 	if (mac_header_info(blp->bl_mh, mp, &hdr_info) != 0) {
2561*10491SRishi.Srivatsavai@Sun.COM 		freemsg(mp);
2562*10491SRishi.Srivatsavai@Sun.COM 		return;
2563*10491SRishi.Srivatsavai@Sun.COM 	}
2564*10491SRishi.Srivatsavai@Sun.COM 
2565*10491SRishi.Srivatsavai@Sun.COM 	/* Extract VLAN ID for this packet. */
2566*10491SRishi.Srivatsavai@Sun.COM 	if (hdr_info.mhi_bindsap == ETHERTYPE_VLAN) {
2567*10491SRishi.Srivatsavai@Sun.COM 		struct ether_vlan_header *evhp;
2568*10491SRishi.Srivatsavai@Sun.COM 
2569*10491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
2570*10491SRishi.Srivatsavai@Sun.COM 		evhp = (struct ether_vlan_header *)mp->b_rptr;
2571*10491SRishi.Srivatsavai@Sun.COM 		tci = ntohs(evhp->ether_tci);
2572*10491SRishi.Srivatsavai@Sun.COM 		vlanid = VLAN_ID(tci);
2573*10491SRishi.Srivatsavai@Sun.COM 	} else {
2574*10491SRishi.Srivatsavai@Sun.COM 		/* Inner VLAN headers are required in TRILL data packets */
2575*10491SRishi.Srivatsavai@Sun.COM 		DTRACE_PROBE3(bridge__trill__decaps__novlan, bridge_link_t *,
2576*10491SRishi.Srivatsavai@Sun.COM 		    blp, mblk_t *, mp, uint16_t, ingress_nick);
2577*10491SRishi.Srivatsavai@Sun.COM 		freemsg(mp);
2578*10491SRishi.Srivatsavai@Sun.COM 		return;
2579*10491SRishi.Srivatsavai@Sun.COM 	}
2580*10491SRishi.Srivatsavai@Sun.COM 
2581*10491SRishi.Srivatsavai@Sun.COM 	/* Learn the location of this sender in the RBridge network */
2582*10491SRishi.Srivatsavai@Sun.COM 	bridge_learn(blp, hdr_info.mhi_saddr, ingress_nick, vlanid);
2583*10491SRishi.Srivatsavai@Sun.COM 
2584*10491SRishi.Srivatsavai@Sun.COM 	/* attempt forwarding */
2585*10491SRishi.Srivatsavai@Sun.COM 	mp = bridge_forward(blp, &hdr_info, mp, vlanid, tci, B_TRUE, B_TRUE);
2586*10491SRishi.Srivatsavai@Sun.COM 	if (mp != NULL) {
2587*10491SRishi.Srivatsavai@Sun.COM 		if (bridge_can_send(blp, vlanid)) {
2588*10491SRishi.Srivatsavai@Sun.COM 			/* Deliver a copy locally as well */
2589*10491SRishi.Srivatsavai@Sun.COM 			if ((mpcopy = copymsg(mp)) != NULL)
2590*10491SRishi.Srivatsavai@Sun.COM 				mac_rx_common(blp->bl_mh, NULL, mpcopy);
2591*10491SRishi.Srivatsavai@Sun.COM 			MAC_RING_TX(blp->bl_mh, NULL, mp, mp);
2592*10491SRishi.Srivatsavai@Sun.COM 		}
2593*10491SRishi.Srivatsavai@Sun.COM 		if (mp == NULL) {
2594*10491SRishi.Srivatsavai@Sun.COM 			KIINCR(bki_sent);
2595*10491SRishi.Srivatsavai@Sun.COM 			KLINCR(bkl_xmit);
2596*10491SRishi.Srivatsavai@Sun.COM 		} else {
2597*10491SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
2598*10491SRishi.Srivatsavai@Sun.COM 		}
2599*10491SRishi.Srivatsavai@Sun.COM 	}
2600*10491SRishi.Srivatsavai@Sun.COM }
2601*10491SRishi.Srivatsavai@Sun.COM 
2602*10491SRishi.Srivatsavai@Sun.COM /*
2603*10491SRishi.Srivatsavai@Sun.COM  * This function is used by TRILL _only_ to transmit TRILL-encapsulated
2604*10491SRishi.Srivatsavai@Sun.COM  * packets.  It sends on a single underlying link and does not bridge.
2605*10491SRishi.Srivatsavai@Sun.COM  */
2606*10491SRishi.Srivatsavai@Sun.COM mblk_t *
2607*10491SRishi.Srivatsavai@Sun.COM bridge_trill_output(bridge_link_t *blp, mblk_t *mp)
2608*10491SRishi.Srivatsavai@Sun.COM {
2609*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;	/* used by macros */
2610*10491SRishi.Srivatsavai@Sun.COM 
2611*10491SRishi.Srivatsavai@Sun.COM 	mac_trill_snoop(blp->bl_mh, mp);
2612*10491SRishi.Srivatsavai@Sun.COM 	MAC_RING_TX(blp->bl_mh, NULL, mp, mp);
2613*10491SRishi.Srivatsavai@Sun.COM 	if (mp == NULL) {
2614*10491SRishi.Srivatsavai@Sun.COM 		KIINCR(bki_sent);
2615*10491SRishi.Srivatsavai@Sun.COM 		KLINCR(bkl_xmit);
2616*10491SRishi.Srivatsavai@Sun.COM 	}
2617*10491SRishi.Srivatsavai@Sun.COM 	return (mp);
2618*10491SRishi.Srivatsavai@Sun.COM }
2619*10491SRishi.Srivatsavai@Sun.COM 
2620*10491SRishi.Srivatsavai@Sun.COM /*
2621*10491SRishi.Srivatsavai@Sun.COM  * Set the "appointed forwarder" flag array for this link.  TRILL controls
2622*10491SRishi.Srivatsavai@Sun.COM  * forwarding on a VLAN basis.  The "trillactive" flag is an optimization for
2623*10491SRishi.Srivatsavai@Sun.COM  * the forwarder.
2624*10491SRishi.Srivatsavai@Sun.COM  */
2625*10491SRishi.Srivatsavai@Sun.COM void
2626*10491SRishi.Srivatsavai@Sun.COM bridge_trill_setvlans(bridge_link_t *blp, const uint8_t *arr)
2627*10491SRishi.Srivatsavai@Sun.COM {
2628*10491SRishi.Srivatsavai@Sun.COM 	int i;
2629*10491SRishi.Srivatsavai@Sun.COM 	uint_t newflags = 0;
2630*10491SRishi.Srivatsavai@Sun.COM 
2631*10491SRishi.Srivatsavai@Sun.COM 	for (i = 0; i < BRIDGE_VLAN_ARR_SIZE; i++) {
2632*10491SRishi.Srivatsavai@Sun.COM 		if ((blp->bl_afs[i] = arr[i]) != 0)
2633*10491SRishi.Srivatsavai@Sun.COM 			newflags = BLF_TRILLACTIVE;
2634*10491SRishi.Srivatsavai@Sun.COM 	}
2635*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_flags = (blp->bl_flags & ~BLF_TRILLACTIVE) | newflags;
2636*10491SRishi.Srivatsavai@Sun.COM }
2637*10491SRishi.Srivatsavai@Sun.COM 
2638*10491SRishi.Srivatsavai@Sun.COM void
2639*10491SRishi.Srivatsavai@Sun.COM bridge_trill_flush(bridge_link_t *blp, uint16_t vlan, boolean_t dotrill)
2640*10491SRishi.Srivatsavai@Sun.COM {
2641*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip = blp->bl_inst;
2642*10491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_t *bfp, *bfnext;
2643*10491SRishi.Srivatsavai@Sun.COM 	avl_tree_t fwd_scavenge;
2644*10491SRishi.Srivatsavai@Sun.COM 	int i;
2645*10491SRishi.Srivatsavai@Sun.COM 
2646*10491SRishi.Srivatsavai@Sun.COM 	_NOTE(ARGUNUSED(vlan));
2647*10491SRishi.Srivatsavai@Sun.COM 
2648*10491SRishi.Srivatsavai@Sun.COM 	avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t),
2649*10491SRishi.Srivatsavai@Sun.COM 	    offsetof(bridge_fwd_t, bf_node));
2650*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_WRITER);
2651*10491SRishi.Srivatsavai@Sun.COM 	bfnext = avl_first(&bip->bi_fwd);
2652*10491SRishi.Srivatsavai@Sun.COM 	while ((bfp = bfnext) != NULL) {
2653*10491SRishi.Srivatsavai@Sun.COM 		bfnext = AVL_NEXT(&bip->bi_fwd, bfp);
2654*10491SRishi.Srivatsavai@Sun.COM 		if (bfp->bf_flags & BFF_LOCALADDR)
2655*10491SRishi.Srivatsavai@Sun.COM 			continue;
2656*10491SRishi.Srivatsavai@Sun.COM 		if (dotrill) {
2657*10491SRishi.Srivatsavai@Sun.COM 			/* port doesn't matter if we're flushing TRILL */
2658*10491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_trill_nick == RBRIDGE_NICKNAME_NONE)
2659*10491SRishi.Srivatsavai@Sun.COM 				continue;
2660*10491SRishi.Srivatsavai@Sun.COM 		} else {
2661*10491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_trill_nick != RBRIDGE_NICKNAME_NONE)
2662*10491SRishi.Srivatsavai@Sun.COM 				continue;
2663*10491SRishi.Srivatsavai@Sun.COM 			for (i = 0; i < bfp->bf_nlinks; i++) {
2664*10491SRishi.Srivatsavai@Sun.COM 				if (bfp->bf_links[i] == blp)
2665*10491SRishi.Srivatsavai@Sun.COM 					break;
2666*10491SRishi.Srivatsavai@Sun.COM 			}
2667*10491SRishi.Srivatsavai@Sun.COM 			if (i >= bfp->bf_nlinks)
2668*10491SRishi.Srivatsavai@Sun.COM 				continue;
2669*10491SRishi.Srivatsavai@Sun.COM 		}
2670*10491SRishi.Srivatsavai@Sun.COM 		ASSERT(bfp->bf_flags & BFF_INTREE);
2671*10491SRishi.Srivatsavai@Sun.COM 		avl_remove(&bip->bi_fwd, bfp);
2672*10491SRishi.Srivatsavai@Sun.COM 		bfp->bf_flags &= ~BFF_INTREE;
2673*10491SRishi.Srivatsavai@Sun.COM 		avl_add(&fwd_scavenge, bfp);
2674*10491SRishi.Srivatsavai@Sun.COM 	}
2675*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
2676*10491SRishi.Srivatsavai@Sun.COM 	bfnext = avl_first(&fwd_scavenge);
2677*10491SRishi.Srivatsavai@Sun.COM 	while ((bfp = bfnext) != NULL) {
2678*10491SRishi.Srivatsavai@Sun.COM 		bfnext = AVL_NEXT(&fwd_scavenge, bfp);
2679*10491SRishi.Srivatsavai@Sun.COM 		avl_remove(&fwd_scavenge, bfp);
2680*10491SRishi.Srivatsavai@Sun.COM 		fwd_unref(bfp);
2681*10491SRishi.Srivatsavai@Sun.COM 	}
2682*10491SRishi.Srivatsavai@Sun.COM 	avl_destroy(&fwd_scavenge);
2683*10491SRishi.Srivatsavai@Sun.COM }
2684*10491SRishi.Srivatsavai@Sun.COM 
2685*10491SRishi.Srivatsavai@Sun.COM /*
2686*10491SRishi.Srivatsavai@Sun.COM  * Let the mac module take or drop a reference to a bridge link.  When this is
2687*10491SRishi.Srivatsavai@Sun.COM  * called, the mac module is holding the mi_bridge_lock, so the link cannot be
2688*10491SRishi.Srivatsavai@Sun.COM  * in the process of entering or leaving a bridge.
2689*10491SRishi.Srivatsavai@Sun.COM  */
2690*10491SRishi.Srivatsavai@Sun.COM static void
2691*10491SRishi.Srivatsavai@Sun.COM bridge_ref_cb(mac_handle_t mh, boolean_t hold)
2692*10491SRishi.Srivatsavai@Sun.COM {
2693*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp = (bridge_link_t *)mh;
2694*10491SRishi.Srivatsavai@Sun.COM 
2695*10491SRishi.Srivatsavai@Sun.COM 	if (hold)
2696*10491SRishi.Srivatsavai@Sun.COM 		atomic_inc_uint(&blp->bl_refs);
2697*10491SRishi.Srivatsavai@Sun.COM 	else
2698*10491SRishi.Srivatsavai@Sun.COM 		link_unref(blp);
2699*10491SRishi.Srivatsavai@Sun.COM }
2700*10491SRishi.Srivatsavai@Sun.COM 
2701*10491SRishi.Srivatsavai@Sun.COM /*
2702*10491SRishi.Srivatsavai@Sun.COM  * Handle link state changes reported by the mac layer.  This acts as a filter
2703*10491SRishi.Srivatsavai@Sun.COM  * for link state changes: if a link is reporting down, but there are other
2704*10491SRishi.Srivatsavai@Sun.COM  * links still up on the bridge, then the state is changed to "up."  When the
2705*10491SRishi.Srivatsavai@Sun.COM  * last link goes down, all are marked down, and when the first link goes up,
2706*10491SRishi.Srivatsavai@Sun.COM  * all are marked up.  (Recursion is avoided by the use of the "redo" function.)
2707*10491SRishi.Srivatsavai@Sun.COM  *
2708*10491SRishi.Srivatsavai@Sun.COM  * We treat unknown as equivalent to "up."
2709*10491SRishi.Srivatsavai@Sun.COM  */
2710*10491SRishi.Srivatsavai@Sun.COM static link_state_t
2711*10491SRishi.Srivatsavai@Sun.COM bridge_ls_cb(mac_handle_t mh, link_state_t newls)
2712*10491SRishi.Srivatsavai@Sun.COM {
2713*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp = (bridge_link_t *)mh;
2714*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blcmp;
2715*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
2716*10491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp;
2717*10491SRishi.Srivatsavai@Sun.COM 
2718*10491SRishi.Srivatsavai@Sun.COM 	if (newls != LINK_STATE_DOWN && blp->bl_linkstate != LINK_STATE_DOWN ||
2719*10491SRishi.Srivatsavai@Sun.COM 	    (blp->bl_flags & (BLF_DELETED|BLF_SDUFAIL))) {
2720*10491SRishi.Srivatsavai@Sun.COM 		blp->bl_linkstate = newls;
2721*10491SRishi.Srivatsavai@Sun.COM 		return (newls);
2722*10491SRishi.Srivatsavai@Sun.COM 	}
2723*10491SRishi.Srivatsavai@Sun.COM 
2724*10491SRishi.Srivatsavai@Sun.COM 	/*
2725*10491SRishi.Srivatsavai@Sun.COM 	 * Scan first to see if there are any other non-down links.  If there
2726*10491SRishi.Srivatsavai@Sun.COM 	 * are, then we're done.  Otherwise, if all others are down, then the
2727*10491SRishi.Srivatsavai@Sun.COM 	 * state of this link is the state of the bridge.
2728*10491SRishi.Srivatsavai@Sun.COM 	 */
2729*10491SRishi.Srivatsavai@Sun.COM 	bip = blp->bl_inst;
2730*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_WRITER);
2731*10491SRishi.Srivatsavai@Sun.COM 	for (blcmp = list_head(&bip->bi_links); blcmp != NULL;
2732*10491SRishi.Srivatsavai@Sun.COM 	    blcmp = list_next(&bip->bi_links, blcmp)) {
2733*10491SRishi.Srivatsavai@Sun.COM 		if (blcmp != blp &&
2734*10491SRishi.Srivatsavai@Sun.COM 		    !(blcmp->bl_flags & (BLF_DELETED|BLF_SDUFAIL)) &&
2735*10491SRishi.Srivatsavai@Sun.COM 		    blcmp->bl_linkstate != LINK_STATE_DOWN)
2736*10491SRishi.Srivatsavai@Sun.COM 			break;
2737*10491SRishi.Srivatsavai@Sun.COM 	}
2738*10491SRishi.Srivatsavai@Sun.COM 
2739*10491SRishi.Srivatsavai@Sun.COM 	if (blcmp != NULL) {
2740*10491SRishi.Srivatsavai@Sun.COM 		/*
2741*10491SRishi.Srivatsavai@Sun.COM 		 * If there are other links that are considered up, then tell
2742*10491SRishi.Srivatsavai@Sun.COM 		 * the caller that the link is actually still up, regardless of
2743*10491SRishi.Srivatsavai@Sun.COM 		 * this link's underlying state.
2744*10491SRishi.Srivatsavai@Sun.COM 		 */
2745*10491SRishi.Srivatsavai@Sun.COM 		blp->bl_linkstate = newls;
2746*10491SRishi.Srivatsavai@Sun.COM 		newls = LINK_STATE_UP;
2747*10491SRishi.Srivatsavai@Sun.COM 	} else if (blp->bl_linkstate != newls) {
2748*10491SRishi.Srivatsavai@Sun.COM 		/*
2749*10491SRishi.Srivatsavai@Sun.COM 		 * If we've found no other 'up' links, and this link has
2750*10491SRishi.Srivatsavai@Sun.COM 		 * changed state, then report the new state of the bridge to
2751*10491SRishi.Srivatsavai@Sun.COM 		 * all other clients.
2752*10491SRishi.Srivatsavai@Sun.COM 		 */
2753*10491SRishi.Srivatsavai@Sun.COM 		blp->bl_linkstate = newls;
2754*10491SRishi.Srivatsavai@Sun.COM 		for (blcmp = list_head(&bip->bi_links); blcmp != NULL;
2755*10491SRishi.Srivatsavai@Sun.COM 		    blcmp = list_next(&bip->bi_links, blcmp)) {
2756*10491SRishi.Srivatsavai@Sun.COM 			if (blcmp != blp && !(blcmp->bl_flags & BLF_DELETED))
2757*10491SRishi.Srivatsavai@Sun.COM 				mac_link_redo(blcmp->bl_mh, newls);
2758*10491SRishi.Srivatsavai@Sun.COM 		}
2759*10491SRishi.Srivatsavai@Sun.COM 		bmp = bip->bi_mac;
2760*10491SRishi.Srivatsavai@Sun.COM 		if ((bmp->bm_linkstate = newls) != LINK_STATE_DOWN)
2761*10491SRishi.Srivatsavai@Sun.COM 			bmp->bm_linkstate = LINK_STATE_UP;
2762*10491SRishi.Srivatsavai@Sun.COM 		mac_link_redo(bmp->bm_mh, bmp->bm_linkstate);
2763*10491SRishi.Srivatsavai@Sun.COM 	}
2764*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
2765*10491SRishi.Srivatsavai@Sun.COM 	return (newls);
2766*10491SRishi.Srivatsavai@Sun.COM }
2767*10491SRishi.Srivatsavai@Sun.COM 
2768*10491SRishi.Srivatsavai@Sun.COM static void
2769*10491SRishi.Srivatsavai@Sun.COM bridge_add_link(void *arg)
2770*10491SRishi.Srivatsavai@Sun.COM {
2771*10491SRishi.Srivatsavai@Sun.COM 	mblk_t *mp = arg;
2772*10491SRishi.Srivatsavai@Sun.COM 	bridge_stream_t *bsp;
2773*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip, *bipt;
2774*10491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp;
2775*10491SRishi.Srivatsavai@Sun.COM 	datalink_id_t linkid;
2776*10491SRishi.Srivatsavai@Sun.COM 	int err;
2777*10491SRishi.Srivatsavai@Sun.COM 	mac_handle_t mh;
2778*10491SRishi.Srivatsavai@Sun.COM 	uint_t maxsdu;
2779*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp = NULL, *blpt;
2780*10491SRishi.Srivatsavai@Sun.COM 	const mac_info_t *mip;
2781*10491SRishi.Srivatsavai@Sun.COM 	boolean_t macopen = B_FALSE;
2782*10491SRishi.Srivatsavai@Sun.COM 	char linkname[MAXLINKNAMELEN];
2783*10491SRishi.Srivatsavai@Sun.COM 	char kstatname[KSTAT_STRLEN];
2784*10491SRishi.Srivatsavai@Sun.COM 	int i;
2785*10491SRishi.Srivatsavai@Sun.COM 	link_state_t linkstate;
2786*10491SRishi.Srivatsavai@Sun.COM 	mblk_t *mlist;
2787*10491SRishi.Srivatsavai@Sun.COM 
2788*10491SRishi.Srivatsavai@Sun.COM 	bsp = (bridge_stream_t *)mp->b_next;
2789*10491SRishi.Srivatsavai@Sun.COM 	mp->b_next = NULL;
2790*10491SRishi.Srivatsavai@Sun.COM 	bip = bsp->bs_inst;
2791*10491SRishi.Srivatsavai@Sun.COM 	/* LINTED: alignment */
2792*10491SRishi.Srivatsavai@Sun.COM 	linkid = *(datalink_id_t *)mp->b_cont->b_rptr;
2793*10491SRishi.Srivatsavai@Sun.COM 
2794*10491SRishi.Srivatsavai@Sun.COM 	/*
2795*10491SRishi.Srivatsavai@Sun.COM 	 * First make sure that there is no other bridge that has this link.
2796*10491SRishi.Srivatsavai@Sun.COM 	 * We don't want to overlap operations from two bridges; the MAC layer
2797*10491SRishi.Srivatsavai@Sun.COM 	 * supports only one bridge on a given MAC at a time.
2798*10491SRishi.Srivatsavai@Sun.COM 	 *
2799*10491SRishi.Srivatsavai@Sun.COM 	 * We rely on the fact that there's just one taskq thread for the
2800*10491SRishi.Srivatsavai@Sun.COM 	 * bridging module: once we've checked for a duplicate, we can drop the
2801*10491SRishi.Srivatsavai@Sun.COM 	 * lock, because no other thread could possibly be adding another link
2802*10491SRishi.Srivatsavai@Sun.COM 	 * until we're done.
2803*10491SRishi.Srivatsavai@Sun.COM 	 */
2804*10491SRishi.Srivatsavai@Sun.COM 	mutex_enter(&inst_lock);
2805*10491SRishi.Srivatsavai@Sun.COM 	for (bipt = list_head(&inst_list); bipt != NULL;
2806*10491SRishi.Srivatsavai@Sun.COM 	    bipt = list_next(&inst_list, bipt)) {
2807*10491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bipt->bi_rwlock, RW_READER);
2808*10491SRishi.Srivatsavai@Sun.COM 		for (blpt = list_head(&bipt->bi_links); blpt != NULL;
2809*10491SRishi.Srivatsavai@Sun.COM 		    blpt = list_next(&bipt->bi_links, blpt)) {
2810*10491SRishi.Srivatsavai@Sun.COM 			if (linkid == blpt->bl_linkid)
2811*10491SRishi.Srivatsavai@Sun.COM 				break;
2812*10491SRishi.Srivatsavai@Sun.COM 		}
2813*10491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bipt->bi_rwlock);
2814*10491SRishi.Srivatsavai@Sun.COM 		if (blpt != NULL)
2815*10491SRishi.Srivatsavai@Sun.COM 			break;
2816*10491SRishi.Srivatsavai@Sun.COM 	}
2817*10491SRishi.Srivatsavai@Sun.COM 	mutex_exit(&inst_lock);
2818*10491SRishi.Srivatsavai@Sun.COM 	if (bipt != NULL) {
2819*10491SRishi.Srivatsavai@Sun.COM 		err = EBUSY;
2820*10491SRishi.Srivatsavai@Sun.COM 		goto fail;
2821*10491SRishi.Srivatsavai@Sun.COM 	}
2822*10491SRishi.Srivatsavai@Sun.COM 
2823*10491SRishi.Srivatsavai@Sun.COM 	if ((err = mac_open_by_linkid(linkid, &mh)) != 0)
2824*10491SRishi.Srivatsavai@Sun.COM 		goto fail;
2825*10491SRishi.Srivatsavai@Sun.COM 	macopen = B_TRUE;
2826*10491SRishi.Srivatsavai@Sun.COM 
2827*10491SRishi.Srivatsavai@Sun.COM 	/* we bridge only Ethernet */
2828*10491SRishi.Srivatsavai@Sun.COM 	mip = mac_info(mh);
2829*10491SRishi.Srivatsavai@Sun.COM 	if (mip->mi_media != DL_ETHER) {
2830*10491SRishi.Srivatsavai@Sun.COM 		err = ENOTSUP;
2831*10491SRishi.Srivatsavai@Sun.COM 		goto fail;
2832*10491SRishi.Srivatsavai@Sun.COM 	}
2833*10491SRishi.Srivatsavai@Sun.COM 
2834*10491SRishi.Srivatsavai@Sun.COM 	/*
2835*10491SRishi.Srivatsavai@Sun.COM 	 * Get the current maximum SDU on this interface.  If there are other
2836*10491SRishi.Srivatsavai@Sun.COM 	 * links on the bridge, then this one must match, or it errors out.
2837*10491SRishi.Srivatsavai@Sun.COM 	 * Otherwise, the first link becomes the standard for the new bridge.
2838*10491SRishi.Srivatsavai@Sun.COM 	 */
2839*10491SRishi.Srivatsavai@Sun.COM 	mac_sdu_get(mh, NULL, &maxsdu);
2840*10491SRishi.Srivatsavai@Sun.COM 	bmp = bip->bi_mac;
2841*10491SRishi.Srivatsavai@Sun.COM 	if (list_is_empty(&bip->bi_links)) {
2842*10491SRishi.Srivatsavai@Sun.COM 		bmp->bm_maxsdu = maxsdu;
2843*10491SRishi.Srivatsavai@Sun.COM 		(void) mac_maxsdu_update(bmp->bm_mh, maxsdu);
2844*10491SRishi.Srivatsavai@Sun.COM 	}
2845*10491SRishi.Srivatsavai@Sun.COM 
2846*10491SRishi.Srivatsavai@Sun.COM 	/* figure the kstat name; also used as the mac client name */
2847*10491SRishi.Srivatsavai@Sun.COM 	i = MBLKL(mp->b_cont) - sizeof (datalink_id_t);
2848*10491SRishi.Srivatsavai@Sun.COM 	if (i < 0 || i >= MAXLINKNAMELEN)
2849*10491SRishi.Srivatsavai@Sun.COM 		i = MAXLINKNAMELEN - 1;
2850*10491SRishi.Srivatsavai@Sun.COM 	bcopy(mp->b_cont->b_rptr + sizeof (datalink_id_t), linkname, i);
2851*10491SRishi.Srivatsavai@Sun.COM 	linkname[i] = '\0';
2852*10491SRishi.Srivatsavai@Sun.COM 	(void) snprintf(kstatname, sizeof (kstatname), "%s-%s", bip->bi_name,
2853*10491SRishi.Srivatsavai@Sun.COM 	    linkname);
2854*10491SRishi.Srivatsavai@Sun.COM 
2855*10491SRishi.Srivatsavai@Sun.COM 	if ((blp = kmem_zalloc(sizeof (*blp), KM_NOSLEEP)) == NULL) {
2856*10491SRishi.Srivatsavai@Sun.COM 		err = ENOMEM;
2857*10491SRishi.Srivatsavai@Sun.COM 		goto fail;
2858*10491SRishi.Srivatsavai@Sun.COM 	}
2859*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_lfailmp = allocb(sizeof (bridge_ctl_t), BPRI_MED);
2860*10491SRishi.Srivatsavai@Sun.COM 	if (blp->bl_lfailmp == NULL) {
2861*10491SRishi.Srivatsavai@Sun.COM 		kmem_free(blp, sizeof (*blp));
2862*10491SRishi.Srivatsavai@Sun.COM 		err = ENOMEM;
2863*10491SRishi.Srivatsavai@Sun.COM 		goto fail;
2864*10491SRishi.Srivatsavai@Sun.COM 	}
2865*10491SRishi.Srivatsavai@Sun.COM 
2866*10491SRishi.Srivatsavai@Sun.COM 	atomic_inc_uint(&bip->bi_refs);
2867*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_inst = bip;
2868*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_mh = mh;
2869*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_linkid = linkid;
2870*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_maxsdu = maxsdu;
2871*10491SRishi.Srivatsavai@Sun.COM 	cv_init(&blp->bl_trillwait, NULL, CV_DRIVER, NULL);
2872*10491SRishi.Srivatsavai@Sun.COM 	mutex_init(&blp->bl_trilllock, NULL, MUTEX_DRIVER, NULL);
2873*10491SRishi.Srivatsavai@Sun.COM 	(void) memset(blp->bl_afs, 0xff, sizeof (blp->bl_afs));
2874*10491SRishi.Srivatsavai@Sun.COM 
2875*10491SRishi.Srivatsavai@Sun.COM 	err = mac_client_open(mh, &blp->bl_mch, kstatname, 0);
2876*10491SRishi.Srivatsavai@Sun.COM 	if (err != 0)
2877*10491SRishi.Srivatsavai@Sun.COM 		goto fail;
2878*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_flags |= BLF_CLIENT_OPEN;
2879*10491SRishi.Srivatsavai@Sun.COM 
2880*10491SRishi.Srivatsavai@Sun.COM 	err = mac_margin_add(mh, &blp->bl_margin, B_TRUE);
2881*10491SRishi.Srivatsavai@Sun.COM 	if (err != 0)
2882*10491SRishi.Srivatsavai@Sun.COM 		goto fail;
2883*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_flags |= BLF_MARGIN_ADDED;
2884*10491SRishi.Srivatsavai@Sun.COM 
2885*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_mnh = mac_notify_add(mh, bridge_notify_cb, blp);
2886*10491SRishi.Srivatsavai@Sun.COM 
2887*10491SRishi.Srivatsavai@Sun.COM 	err = mac_bridge_set(mh, (mac_handle_t)blp);
2888*10491SRishi.Srivatsavai@Sun.COM 	if (err != 0)
2889*10491SRishi.Srivatsavai@Sun.COM 		goto fail;
2890*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_flags |= BLF_SET_BRIDGE;
2891*10491SRishi.Srivatsavai@Sun.COM 
2892*10491SRishi.Srivatsavai@Sun.COM 	err = mac_promisc_add(blp->bl_mch, MAC_CLIENT_PROMISC_ALL, NULL,
2893*10491SRishi.Srivatsavai@Sun.COM 	    blp, &blp->bl_mphp, MAC_PROMISC_FLAGS_NO_TX_LOOP);
2894*10491SRishi.Srivatsavai@Sun.COM 	if (err != 0)
2895*10491SRishi.Srivatsavai@Sun.COM 		goto fail;
2896*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_flags |= BLF_PROM_ADDED;
2897*10491SRishi.Srivatsavai@Sun.COM 
2898*10491SRishi.Srivatsavai@Sun.COM 	bridge_new_unicst(blp);
2899*10491SRishi.Srivatsavai@Sun.COM 
2900*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_ksp = kstat_setup((kstat_named_t *)&blp->bl_kstats,
2901*10491SRishi.Srivatsavai@Sun.COM 	    link_kstats_list, Dim(link_kstats_list), kstatname);
2902*10491SRishi.Srivatsavai@Sun.COM 
2903*10491SRishi.Srivatsavai@Sun.COM 	/*
2904*10491SRishi.Srivatsavai@Sun.COM 	 * The link holds a reference to the bridge instance, so that the
2905*10491SRishi.Srivatsavai@Sun.COM 	 * instance can't go away before the link is freed.  The insertion into
2906*10491SRishi.Srivatsavai@Sun.COM 	 * bi_links holds a reference on the link.  When marking as removed
2907*10491SRishi.Srivatsavai@Sun.COM 	 * from bi_links (BLF_DELETED), drop the reference on the link.  When
2908*10491SRishi.Srivatsavai@Sun.COM 	 * freeing the link, drop the reference on the instance.
2909*10491SRishi.Srivatsavai@Sun.COM 	 */
2910*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_WRITER);
2911*10491SRishi.Srivatsavai@Sun.COM 	list_insert_tail(&bip->bi_links, blp);
2912*10491SRishi.Srivatsavai@Sun.COM 	atomic_inc_uint(&blp->bl_refs);
2913*10491SRishi.Srivatsavai@Sun.COM 
2914*10491SRishi.Srivatsavai@Sun.COM 	/*
2915*10491SRishi.Srivatsavai@Sun.COM 	 * If the new link is no good on this bridge, then let the daemon know
2916*10491SRishi.Srivatsavai@Sun.COM 	 * about the problem.
2917*10491SRishi.Srivatsavai@Sun.COM 	 */
2918*10491SRishi.Srivatsavai@Sun.COM 	mlist = NULL;
2919*10491SRishi.Srivatsavai@Sun.COM 	if (maxsdu != bmp->bm_maxsdu)
2920*10491SRishi.Srivatsavai@Sun.COM 		link_sdu_fail(blp, B_TRUE, &mlist);
2921*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
2922*10491SRishi.Srivatsavai@Sun.COM 	send_up_messages(bip, mlist);
2923*10491SRishi.Srivatsavai@Sun.COM 
2924*10491SRishi.Srivatsavai@Sun.COM 	/*
2925*10491SRishi.Srivatsavai@Sun.COM 	 * Trigger a link state update so that if this link is the first one
2926*10491SRishi.Srivatsavai@Sun.COM 	 * "up" in the bridge, then we notify everyone.  This triggers a trip
2927*10491SRishi.Srivatsavai@Sun.COM 	 * through bridge_ls_cb.
2928*10491SRishi.Srivatsavai@Sun.COM 	 */
2929*10491SRishi.Srivatsavai@Sun.COM 	linkstate = mac_stat_get(mh, MAC_STAT_LOWLINK_STATE);
2930*10491SRishi.Srivatsavai@Sun.COM 	blp->bl_linkstate = LINK_STATE_DOWN;
2931*10491SRishi.Srivatsavai@Sun.COM 	mac_link_update(mh, linkstate);
2932*10491SRishi.Srivatsavai@Sun.COM 
2933*10491SRishi.Srivatsavai@Sun.COM 	/*
2934*10491SRishi.Srivatsavai@Sun.COM 	 * We now need to report back to the stream that invoked us, and then
2935*10491SRishi.Srivatsavai@Sun.COM 	 * drop the reference on the stream that we're holding.
2936*10491SRishi.Srivatsavai@Sun.COM 	 */
2937*10491SRishi.Srivatsavai@Sun.COM 	miocack(bsp->bs_wq, mp, 0, 0);
2938*10491SRishi.Srivatsavai@Sun.COM 	stream_unref(bsp);
2939*10491SRishi.Srivatsavai@Sun.COM 	return;
2940*10491SRishi.Srivatsavai@Sun.COM 
2941*10491SRishi.Srivatsavai@Sun.COM fail:
2942*10491SRishi.Srivatsavai@Sun.COM 	if (blp == NULL) {
2943*10491SRishi.Srivatsavai@Sun.COM 		if (macopen)
2944*10491SRishi.Srivatsavai@Sun.COM 			mac_close(mh);
2945*10491SRishi.Srivatsavai@Sun.COM 	} else {
2946*10491SRishi.Srivatsavai@Sun.COM 		link_shutdown(blp);
2947*10491SRishi.Srivatsavai@Sun.COM 		link_free(blp);
2948*10491SRishi.Srivatsavai@Sun.COM 	}
2949*10491SRishi.Srivatsavai@Sun.COM 	miocnak(bsp->bs_wq, mp, 0, err);
2950*10491SRishi.Srivatsavai@Sun.COM 	stream_unref(bsp);
2951*10491SRishi.Srivatsavai@Sun.COM }
2952*10491SRishi.Srivatsavai@Sun.COM 
2953*10491SRishi.Srivatsavai@Sun.COM static void
2954*10491SRishi.Srivatsavai@Sun.COM bridge_rem_link(void *arg)
2955*10491SRishi.Srivatsavai@Sun.COM {
2956*10491SRishi.Srivatsavai@Sun.COM 	mblk_t *mp = arg;
2957*10491SRishi.Srivatsavai@Sun.COM 	bridge_stream_t *bsp;
2958*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
2959*10491SRishi.Srivatsavai@Sun.COM 	bridge_mac_t *bmp;
2960*10491SRishi.Srivatsavai@Sun.COM 	datalink_id_t linkid;
2961*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp, *blsave;
2962*10491SRishi.Srivatsavai@Sun.COM 	boolean_t found;
2963*10491SRishi.Srivatsavai@Sun.COM 	mblk_t *mlist;
2964*10491SRishi.Srivatsavai@Sun.COM 
2965*10491SRishi.Srivatsavai@Sun.COM 	bsp = (bridge_stream_t *)mp->b_next;
2966*10491SRishi.Srivatsavai@Sun.COM 	mp->b_next = NULL;
2967*10491SRishi.Srivatsavai@Sun.COM 	bip = bsp->bs_inst;
2968*10491SRishi.Srivatsavai@Sun.COM 	/* LINTED: alignment */
2969*10491SRishi.Srivatsavai@Sun.COM 	linkid = *(datalink_id_t *)mp->b_cont->b_rptr;
2970*10491SRishi.Srivatsavai@Sun.COM 
2971*10491SRishi.Srivatsavai@Sun.COM 	/*
2972*10491SRishi.Srivatsavai@Sun.COM 	 * We become reader here so that we can loop over the other links and
2973*10491SRishi.Srivatsavai@Sun.COM 	 * deliver link up/down notification.
2974*10491SRishi.Srivatsavai@Sun.COM 	 */
2975*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_READER);
2976*10491SRishi.Srivatsavai@Sun.COM 	found = B_FALSE;
2977*10491SRishi.Srivatsavai@Sun.COM 	for (blp = list_head(&bip->bi_links); blp != NULL;
2978*10491SRishi.Srivatsavai@Sun.COM 	    blp = list_next(&bip->bi_links, blp)) {
2979*10491SRishi.Srivatsavai@Sun.COM 		if (blp->bl_linkid == linkid &&
2980*10491SRishi.Srivatsavai@Sun.COM 		    !(blp->bl_flags & BLF_DELETED)) {
2981*10491SRishi.Srivatsavai@Sun.COM 			blp->bl_flags |= BLF_DELETED;
2982*10491SRishi.Srivatsavai@Sun.COM 			(void) ddi_taskq_dispatch(bridge_taskq, link_shutdown,
2983*10491SRishi.Srivatsavai@Sun.COM 			    blp, DDI_SLEEP);
2984*10491SRishi.Srivatsavai@Sun.COM 			found = B_TRUE;
2985*10491SRishi.Srivatsavai@Sun.COM 			break;
2986*10491SRishi.Srivatsavai@Sun.COM 		}
2987*10491SRishi.Srivatsavai@Sun.COM 	}
2988*10491SRishi.Srivatsavai@Sun.COM 
2989*10491SRishi.Srivatsavai@Sun.COM 	/*
2990*10491SRishi.Srivatsavai@Sun.COM 	 * Check if this link is up and the remainder of the links are all
2991*10491SRishi.Srivatsavai@Sun.COM 	 * down.
2992*10491SRishi.Srivatsavai@Sun.COM 	 */
2993*10491SRishi.Srivatsavai@Sun.COM 	if (blp != NULL && blp->bl_linkstate != LINK_STATE_DOWN) {
2994*10491SRishi.Srivatsavai@Sun.COM 		for (blp = list_head(&bip->bi_links); blp != NULL;
2995*10491SRishi.Srivatsavai@Sun.COM 		    blp = list_next(&bip->bi_links, blp)) {
2996*10491SRishi.Srivatsavai@Sun.COM 			if (blp->bl_linkstate != LINK_STATE_DOWN &&
2997*10491SRishi.Srivatsavai@Sun.COM 			    !(blp->bl_flags & (BLF_DELETED|BLF_SDUFAIL)))
2998*10491SRishi.Srivatsavai@Sun.COM 				break;
2999*10491SRishi.Srivatsavai@Sun.COM 		}
3000*10491SRishi.Srivatsavai@Sun.COM 		if (blp == NULL) {
3001*10491SRishi.Srivatsavai@Sun.COM 			for (blp = list_head(&bip->bi_links); blp != NULL;
3002*10491SRishi.Srivatsavai@Sun.COM 			    blp = list_next(&bip->bi_links, blp)) {
3003*10491SRishi.Srivatsavai@Sun.COM 				if (!(blp->bl_flags & BLF_DELETED))
3004*10491SRishi.Srivatsavai@Sun.COM 					mac_link_redo(blp->bl_mh,
3005*10491SRishi.Srivatsavai@Sun.COM 					    LINK_STATE_DOWN);
3006*10491SRishi.Srivatsavai@Sun.COM 			}
3007*10491SRishi.Srivatsavai@Sun.COM 			bmp = bip->bi_mac;
3008*10491SRishi.Srivatsavai@Sun.COM 			bmp->bm_linkstate = LINK_STATE_DOWN;
3009*10491SRishi.Srivatsavai@Sun.COM 			mac_link_redo(bmp->bm_mh, LINK_STATE_DOWN);
3010*10491SRishi.Srivatsavai@Sun.COM 		}
3011*10491SRishi.Srivatsavai@Sun.COM 	}
3012*10491SRishi.Srivatsavai@Sun.COM 
3013*10491SRishi.Srivatsavai@Sun.COM 	/*
3014*10491SRishi.Srivatsavai@Sun.COM 	 * Check if there's just one working link left on the bridge.  If so,
3015*10491SRishi.Srivatsavai@Sun.COM 	 * then that link is now authoritative for bridge MTU.
3016*10491SRishi.Srivatsavai@Sun.COM 	 */
3017*10491SRishi.Srivatsavai@Sun.COM 	blsave = NULL;
3018*10491SRishi.Srivatsavai@Sun.COM 	for (blp = list_head(&bip->bi_links); blp != NULL;
3019*10491SRishi.Srivatsavai@Sun.COM 	    blp = list_next(&bip->bi_links, blp)) {
3020*10491SRishi.Srivatsavai@Sun.COM 		if (!(blp->bl_flags & BLF_DELETED)) {
3021*10491SRishi.Srivatsavai@Sun.COM 			if (blsave == NULL)
3022*10491SRishi.Srivatsavai@Sun.COM 				blsave = blp;
3023*10491SRishi.Srivatsavai@Sun.COM 			else
3024*10491SRishi.Srivatsavai@Sun.COM 				break;
3025*10491SRishi.Srivatsavai@Sun.COM 		}
3026*10491SRishi.Srivatsavai@Sun.COM 	}
3027*10491SRishi.Srivatsavai@Sun.COM 	mlist = NULL;
3028*10491SRishi.Srivatsavai@Sun.COM 	bmp = bip->bi_mac;
3029*10491SRishi.Srivatsavai@Sun.COM 	if (blsave != NULL && blp == NULL &&
3030*10491SRishi.Srivatsavai@Sun.COM 	    blsave->bl_maxsdu != bmp->bm_maxsdu) {
3031*10491SRishi.Srivatsavai@Sun.COM 		bmp->bm_maxsdu = blsave->bl_maxsdu;
3032*10491SRishi.Srivatsavai@Sun.COM 		(void) mac_maxsdu_update(bmp->bm_mh, blsave->bl_maxsdu);
3033*10491SRishi.Srivatsavai@Sun.COM 		link_sdu_fail(blsave, B_FALSE, &mlist);
3034*10491SRishi.Srivatsavai@Sun.COM 	}
3035*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bip->bi_rwlock);
3036*10491SRishi.Srivatsavai@Sun.COM 	send_up_messages(bip, mlist);
3037*10491SRishi.Srivatsavai@Sun.COM 
3038*10491SRishi.Srivatsavai@Sun.COM 	if (found)
3039*10491SRishi.Srivatsavai@Sun.COM 		miocack(bsp->bs_wq, mp, 0, 0);
3040*10491SRishi.Srivatsavai@Sun.COM 	else
3041*10491SRishi.Srivatsavai@Sun.COM 		miocnak(bsp->bs_wq, mp, 0, ENOENT);
3042*10491SRishi.Srivatsavai@Sun.COM 	stream_unref(bsp);
3043*10491SRishi.Srivatsavai@Sun.COM }
3044*10491SRishi.Srivatsavai@Sun.COM 
3045*10491SRishi.Srivatsavai@Sun.COM /*
3046*10491SRishi.Srivatsavai@Sun.COM  * This function intentionally returns with bi_rwlock held; it is intended for
3047*10491SRishi.Srivatsavai@Sun.COM  * quick checks and updates.
3048*10491SRishi.Srivatsavai@Sun.COM  */
3049*10491SRishi.Srivatsavai@Sun.COM static bridge_link_t *
3050*10491SRishi.Srivatsavai@Sun.COM enter_link(bridge_inst_t *bip, datalink_id_t linkid)
3051*10491SRishi.Srivatsavai@Sun.COM {
3052*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp;
3053*10491SRishi.Srivatsavai@Sun.COM 
3054*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bip->bi_rwlock, RW_READER);
3055*10491SRishi.Srivatsavai@Sun.COM 	for (blp = list_head(&bip->bi_links); blp != NULL;
3056*10491SRishi.Srivatsavai@Sun.COM 	    blp = list_next(&bip->bi_links, blp)) {
3057*10491SRishi.Srivatsavai@Sun.COM 		if (blp->bl_linkid == linkid && !(blp->bl_flags & BLF_DELETED))
3058*10491SRishi.Srivatsavai@Sun.COM 			break;
3059*10491SRishi.Srivatsavai@Sun.COM 	}
3060*10491SRishi.Srivatsavai@Sun.COM 	return (blp);
3061*10491SRishi.Srivatsavai@Sun.COM }
3062*10491SRishi.Srivatsavai@Sun.COM 
3063*10491SRishi.Srivatsavai@Sun.COM static void
3064*10491SRishi.Srivatsavai@Sun.COM bridge_ioctl(queue_t *wq, mblk_t *mp)
3065*10491SRishi.Srivatsavai@Sun.COM {
3066*10491SRishi.Srivatsavai@Sun.COM 	bridge_stream_t *bsp = wq->q_ptr;
3067*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_t *bip;
3068*10491SRishi.Srivatsavai@Sun.COM 	struct iocblk *iop;
3069*10491SRishi.Srivatsavai@Sun.COM 	int rc = EINVAL;
3070*10491SRishi.Srivatsavai@Sun.COM 	int len = 0;
3071*10491SRishi.Srivatsavai@Sun.COM 	bridge_link_t *blp;
3072*10491SRishi.Srivatsavai@Sun.COM 	cred_t *cr;
3073*10491SRishi.Srivatsavai@Sun.COM 
3074*10491SRishi.Srivatsavai@Sun.COM 	/* LINTED: alignment */
3075*10491SRishi.Srivatsavai@Sun.COM 	iop = (struct iocblk *)mp->b_rptr;
3076*10491SRishi.Srivatsavai@Sun.COM 
3077*10491SRishi.Srivatsavai@Sun.COM 	/*
3078*10491SRishi.Srivatsavai@Sun.COM 	 * For now, all of the bridge ioctls are privileged.
3079*10491SRishi.Srivatsavai@Sun.COM 	 */
3080*10491SRishi.Srivatsavai@Sun.COM 	if ((cr = msg_getcred(mp, NULL)) == NULL)
3081*10491SRishi.Srivatsavai@Sun.COM 		cr = iop->ioc_cr;
3082*10491SRishi.Srivatsavai@Sun.COM 	if (cr != NULL && secpolicy_net_config(cr, B_FALSE) != 0) {
3083*10491SRishi.Srivatsavai@Sun.COM 		miocnak(wq, mp, 0, EPERM);
3084*10491SRishi.Srivatsavai@Sun.COM 		return;
3085*10491SRishi.Srivatsavai@Sun.COM 	}
3086*10491SRishi.Srivatsavai@Sun.COM 
3087*10491SRishi.Srivatsavai@Sun.COM 	switch (iop->ioc_cmd) {
3088*10491SRishi.Srivatsavai@Sun.COM 	case BRIOC_NEWBRIDGE: {
3089*10491SRishi.Srivatsavai@Sun.COM 		bridge_newbridge_t *bnb;
3090*10491SRishi.Srivatsavai@Sun.COM 
3091*10491SRishi.Srivatsavai@Sun.COM 		if (bsp->bs_inst != NULL ||
3092*10491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (bridge_newbridge_t))) != 0)
3093*10491SRishi.Srivatsavai@Sun.COM 			break;
3094*10491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
3095*10491SRishi.Srivatsavai@Sun.COM 		bnb = (bridge_newbridge_t *)mp->b_cont->b_rptr;
3096*10491SRishi.Srivatsavai@Sun.COM 		bnb->bnb_name[MAXNAMELEN-1] = '\0';
3097*10491SRishi.Srivatsavai@Sun.COM 		if ((rc = bridge_create(bnb->bnb_linkid,
3098*10491SRishi.Srivatsavai@Sun.COM 		    bnb->bnb_name, &bip)) != 0)
3099*10491SRishi.Srivatsavai@Sun.COM 			break;
3100*10491SRishi.Srivatsavai@Sun.COM 
3101*10491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_WRITER);
3102*10491SRishi.Srivatsavai@Sun.COM 		if (bip->bi_control != NULL) {
3103*10491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bip->bi_rwlock);
3104*10491SRishi.Srivatsavai@Sun.COM 			bridge_unref(bip);
3105*10491SRishi.Srivatsavai@Sun.COM 			rc = EBUSY;
3106*10491SRishi.Srivatsavai@Sun.COM 		} else {
3107*10491SRishi.Srivatsavai@Sun.COM 			atomic_inc_uint(&bip->bi_refs);
3108*10491SRishi.Srivatsavai@Sun.COM 			bsp->bs_inst = bip;	/* stream holds reference */
3109*10491SRishi.Srivatsavai@Sun.COM 			bip->bi_control = bsp;
3110*10491SRishi.Srivatsavai@Sun.COM 			rw_exit(&bip->bi_rwlock);
3111*10491SRishi.Srivatsavai@Sun.COM 			rc = 0;
3112*10491SRishi.Srivatsavai@Sun.COM 		}
3113*10491SRishi.Srivatsavai@Sun.COM 		break;
3114*10491SRishi.Srivatsavai@Sun.COM 	}
3115*10491SRishi.Srivatsavai@Sun.COM 
3116*10491SRishi.Srivatsavai@Sun.COM 	case BRIOC_ADDLINK:
3117*10491SRishi.Srivatsavai@Sun.COM 		if ((bip = bsp->bs_inst) == NULL ||
3118*10491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (datalink_id_t))) != 0)
3119*10491SRishi.Srivatsavai@Sun.COM 			break;
3120*10491SRishi.Srivatsavai@Sun.COM 		/*
3121*10491SRishi.Srivatsavai@Sun.COM 		 * We cannot perform the action in this thread, because we're
3122*10491SRishi.Srivatsavai@Sun.COM 		 * not in process context, and we may already be holding
3123*10491SRishi.Srivatsavai@Sun.COM 		 * MAC-related locks.  Place the request on taskq.
3124*10491SRishi.Srivatsavai@Sun.COM 		 */
3125*10491SRishi.Srivatsavai@Sun.COM 		mp->b_next = (mblk_t *)bsp;
3126*10491SRishi.Srivatsavai@Sun.COM 		stream_ref(bsp);
3127*10491SRishi.Srivatsavai@Sun.COM 		(void) ddi_taskq_dispatch(bridge_taskq, bridge_add_link, mp,
3128*10491SRishi.Srivatsavai@Sun.COM 		    DDI_SLEEP);
3129*10491SRishi.Srivatsavai@Sun.COM 		return;
3130*10491SRishi.Srivatsavai@Sun.COM 
3131*10491SRishi.Srivatsavai@Sun.COM 	case BRIOC_REMLINK:
3132*10491SRishi.Srivatsavai@Sun.COM 		if ((bip = bsp->bs_inst) == NULL ||
3133*10491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (datalink_id_t))) != 0)
3134*10491SRishi.Srivatsavai@Sun.COM 			break;
3135*10491SRishi.Srivatsavai@Sun.COM 		/*
3136*10491SRishi.Srivatsavai@Sun.COM 		 * We cannot perform the action in this thread, because we're
3137*10491SRishi.Srivatsavai@Sun.COM 		 * not in process context, and we may already be holding
3138*10491SRishi.Srivatsavai@Sun.COM 		 * MAC-related locks.  Place the request on taskq.
3139*10491SRishi.Srivatsavai@Sun.COM 		 */
3140*10491SRishi.Srivatsavai@Sun.COM 		mp->b_next = (mblk_t *)bsp;
3141*10491SRishi.Srivatsavai@Sun.COM 		stream_ref(bsp);
3142*10491SRishi.Srivatsavai@Sun.COM 		(void) ddi_taskq_dispatch(bridge_taskq, bridge_rem_link, mp,
3143*10491SRishi.Srivatsavai@Sun.COM 		    DDI_SLEEP);
3144*10491SRishi.Srivatsavai@Sun.COM 		return;
3145*10491SRishi.Srivatsavai@Sun.COM 
3146*10491SRishi.Srivatsavai@Sun.COM 	case BRIOC_SETSTATE: {
3147*10491SRishi.Srivatsavai@Sun.COM 		bridge_setstate_t *bss;
3148*10491SRishi.Srivatsavai@Sun.COM 
3149*10491SRishi.Srivatsavai@Sun.COM 		if ((bip = bsp->bs_inst) == NULL ||
3150*10491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (*bss))) != 0)
3151*10491SRishi.Srivatsavai@Sun.COM 			break;
3152*10491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
3153*10491SRishi.Srivatsavai@Sun.COM 		bss = (bridge_setstate_t *)mp->b_cont->b_rptr;
3154*10491SRishi.Srivatsavai@Sun.COM 		if ((blp = enter_link(bip, bss->bss_linkid)) == NULL) {
3155*10491SRishi.Srivatsavai@Sun.COM 			rc = ENOENT;
3156*10491SRishi.Srivatsavai@Sun.COM 		} else {
3157*10491SRishi.Srivatsavai@Sun.COM 			rc = 0;
3158*10491SRishi.Srivatsavai@Sun.COM 			blp->bl_state = bss->bss_state;
3159*10491SRishi.Srivatsavai@Sun.COM 		}
3160*10491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
3161*10491SRishi.Srivatsavai@Sun.COM 		break;
3162*10491SRishi.Srivatsavai@Sun.COM 	}
3163*10491SRishi.Srivatsavai@Sun.COM 
3164*10491SRishi.Srivatsavai@Sun.COM 	case BRIOC_SETPVID: {
3165*10491SRishi.Srivatsavai@Sun.COM 		bridge_setpvid_t *bsv;
3166*10491SRishi.Srivatsavai@Sun.COM 
3167*10491SRishi.Srivatsavai@Sun.COM 		if ((bip = bsp->bs_inst) == NULL ||
3168*10491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (*bsv))) != 0)
3169*10491SRishi.Srivatsavai@Sun.COM 			break;
3170*10491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
3171*10491SRishi.Srivatsavai@Sun.COM 		bsv = (bridge_setpvid_t *)mp->b_cont->b_rptr;
3172*10491SRishi.Srivatsavai@Sun.COM 		if (bsv->bsv_vlan > VLAN_ID_MAX)
3173*10491SRishi.Srivatsavai@Sun.COM 			break;
3174*10491SRishi.Srivatsavai@Sun.COM 		if ((blp = enter_link(bip, bsv->bsv_linkid)) == NULL) {
3175*10491SRishi.Srivatsavai@Sun.COM 			rc = ENOENT;
3176*10491SRishi.Srivatsavai@Sun.COM 		} else if (blp->bl_pvid == bsv->bsv_vlan) {
3177*10491SRishi.Srivatsavai@Sun.COM 			rc = 0;
3178*10491SRishi.Srivatsavai@Sun.COM 		} else {
3179*10491SRishi.Srivatsavai@Sun.COM 			rc = 0;
3180*10491SRishi.Srivatsavai@Sun.COM 			BRIDGE_VLAN_CLR(blp, blp->bl_pvid);
3181*10491SRishi.Srivatsavai@Sun.COM 			blp->bl_pvid = bsv->bsv_vlan;
3182*10491SRishi.Srivatsavai@Sun.COM 			if (blp->bl_pvid != 0)
3183*10491SRishi.Srivatsavai@Sun.COM 				BRIDGE_VLAN_SET(blp, blp->bl_pvid);
3184*10491SRishi.Srivatsavai@Sun.COM 		}
3185*10491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
3186*10491SRishi.Srivatsavai@Sun.COM 		break;
3187*10491SRishi.Srivatsavai@Sun.COM 	}
3188*10491SRishi.Srivatsavai@Sun.COM 
3189*10491SRishi.Srivatsavai@Sun.COM 	case BRIOC_VLANENAB: {
3190*10491SRishi.Srivatsavai@Sun.COM 		bridge_vlanenab_t *bve;
3191*10491SRishi.Srivatsavai@Sun.COM 
3192*10491SRishi.Srivatsavai@Sun.COM 		if ((bip = bsp->bs_inst) == NULL ||
3193*10491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (*bve))) != 0)
3194*10491SRishi.Srivatsavai@Sun.COM 			break;
3195*10491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
3196*10491SRishi.Srivatsavai@Sun.COM 		bve = (bridge_vlanenab_t *)mp->b_cont->b_rptr;
3197*10491SRishi.Srivatsavai@Sun.COM 		if (bve->bve_vlan > VLAN_ID_MAX)
3198*10491SRishi.Srivatsavai@Sun.COM 			break;
3199*10491SRishi.Srivatsavai@Sun.COM 		if ((blp = enter_link(bip, bve->bve_linkid)) == NULL) {
3200*10491SRishi.Srivatsavai@Sun.COM 			rc = ENOENT;
3201*10491SRishi.Srivatsavai@Sun.COM 		} else {
3202*10491SRishi.Srivatsavai@Sun.COM 			rc = 0;
3203*10491SRishi.Srivatsavai@Sun.COM 			/* special case: vlan 0 means "all" */
3204*10491SRishi.Srivatsavai@Sun.COM 			if (bve->bve_vlan == 0) {
3205*10491SRishi.Srivatsavai@Sun.COM 				(void) memset(blp->bl_vlans,
3206*10491SRishi.Srivatsavai@Sun.COM 				    bve->bve_onoff ? ~0 : 0,
3207*10491SRishi.Srivatsavai@Sun.COM 				    sizeof (blp->bl_vlans));
3208*10491SRishi.Srivatsavai@Sun.COM 				BRIDGE_VLAN_CLR(blp, 0);
3209*10491SRishi.Srivatsavai@Sun.COM 				if (blp->bl_pvid != 0)
3210*10491SRishi.Srivatsavai@Sun.COM 					BRIDGE_VLAN_SET(blp, blp->bl_pvid);
3211*10491SRishi.Srivatsavai@Sun.COM 			} else if (bve->bve_vlan == blp->bl_pvid) {
3212*10491SRishi.Srivatsavai@Sun.COM 				rc = EINVAL;
3213*10491SRishi.Srivatsavai@Sun.COM 			} else if (bve->bve_onoff) {
3214*10491SRishi.Srivatsavai@Sun.COM 				BRIDGE_VLAN_SET(blp, bve->bve_vlan);
3215*10491SRishi.Srivatsavai@Sun.COM 			} else {
3216*10491SRishi.Srivatsavai@Sun.COM 				BRIDGE_VLAN_CLR(blp, bve->bve_vlan);
3217*10491SRishi.Srivatsavai@Sun.COM 			}
3218*10491SRishi.Srivatsavai@Sun.COM 		}
3219*10491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
3220*10491SRishi.Srivatsavai@Sun.COM 		break;
3221*10491SRishi.Srivatsavai@Sun.COM 	}
3222*10491SRishi.Srivatsavai@Sun.COM 
3223*10491SRishi.Srivatsavai@Sun.COM 	case BRIOC_FLUSHFWD: {
3224*10491SRishi.Srivatsavai@Sun.COM 		bridge_flushfwd_t *bff;
3225*10491SRishi.Srivatsavai@Sun.COM 		bridge_fwd_t *bfp, *bfnext;
3226*10491SRishi.Srivatsavai@Sun.COM 		avl_tree_t fwd_scavenge;
3227*10491SRishi.Srivatsavai@Sun.COM 		int i;
3228*10491SRishi.Srivatsavai@Sun.COM 
3229*10491SRishi.Srivatsavai@Sun.COM 		if ((bip = bsp->bs_inst) == NULL ||
3230*10491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (*bff))) != 0)
3231*10491SRishi.Srivatsavai@Sun.COM 			break;
3232*10491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
3233*10491SRishi.Srivatsavai@Sun.COM 		bff = (bridge_flushfwd_t *)mp->b_cont->b_rptr;
3234*10491SRishi.Srivatsavai@Sun.COM 		rw_enter(&bip->bi_rwlock, RW_WRITER);
3235*10491SRishi.Srivatsavai@Sun.COM 		/* This case means "all" */
3236*10491SRishi.Srivatsavai@Sun.COM 		if (bff->bff_linkid == DATALINK_INVALID_LINKID) {
3237*10491SRishi.Srivatsavai@Sun.COM 			blp = NULL;
3238*10491SRishi.Srivatsavai@Sun.COM 		} else {
3239*10491SRishi.Srivatsavai@Sun.COM 			for (blp = list_head(&bip->bi_links); blp != NULL;
3240*10491SRishi.Srivatsavai@Sun.COM 			    blp = list_next(&bip->bi_links, blp)) {
3241*10491SRishi.Srivatsavai@Sun.COM 				if (blp->bl_linkid == bff->bff_linkid &&
3242*10491SRishi.Srivatsavai@Sun.COM 				    !(blp->bl_flags & BLF_DELETED))
3243*10491SRishi.Srivatsavai@Sun.COM 					break;
3244*10491SRishi.Srivatsavai@Sun.COM 			}
3245*10491SRishi.Srivatsavai@Sun.COM 			if (blp == NULL) {
3246*10491SRishi.Srivatsavai@Sun.COM 				rc = ENOENT;
3247*10491SRishi.Srivatsavai@Sun.COM 				rw_exit(&bip->bi_rwlock);
3248*10491SRishi.Srivatsavai@Sun.COM 				break;
3249*10491SRishi.Srivatsavai@Sun.COM 			}
3250*10491SRishi.Srivatsavai@Sun.COM 		}
3251*10491SRishi.Srivatsavai@Sun.COM 		avl_create(&fwd_scavenge, fwd_compare, sizeof (bridge_fwd_t),
3252*10491SRishi.Srivatsavai@Sun.COM 		    offsetof(bridge_fwd_t, bf_node));
3253*10491SRishi.Srivatsavai@Sun.COM 		bfnext = avl_first(&bip->bi_fwd);
3254*10491SRishi.Srivatsavai@Sun.COM 		while ((bfp = bfnext) != NULL) {
3255*10491SRishi.Srivatsavai@Sun.COM 			bfnext = AVL_NEXT(&bip->bi_fwd, bfp);
3256*10491SRishi.Srivatsavai@Sun.COM 			if (bfp->bf_flags & BFF_LOCALADDR)
3257*10491SRishi.Srivatsavai@Sun.COM 				continue;
3258*10491SRishi.Srivatsavai@Sun.COM 			if (blp != NULL) {
3259*10491SRishi.Srivatsavai@Sun.COM 				for (i = 0; i < bfp->bf_maxlinks; i++) {
3260*10491SRishi.Srivatsavai@Sun.COM 					if (bfp->bf_links[i] == blp)
3261*10491SRishi.Srivatsavai@Sun.COM 						break;
3262*10491SRishi.Srivatsavai@Sun.COM 				}
3263*10491SRishi.Srivatsavai@Sun.COM 				/*
3264*10491SRishi.Srivatsavai@Sun.COM 				 * If the link is there and we're excluding,
3265*10491SRishi.Srivatsavai@Sun.COM 				 * then skip.  If the link is not there and
3266*10491SRishi.Srivatsavai@Sun.COM 				 * we're doing only that link, then skip.
3267*10491SRishi.Srivatsavai@Sun.COM 				 */
3268*10491SRishi.Srivatsavai@Sun.COM 				if ((i < bfp->bf_maxlinks) == bff->bff_exclude)
3269*10491SRishi.Srivatsavai@Sun.COM 					continue;
3270*10491SRishi.Srivatsavai@Sun.COM 			}
3271*10491SRishi.Srivatsavai@Sun.COM 			ASSERT(bfp->bf_flags & BFF_INTREE);
3272*10491SRishi.Srivatsavai@Sun.COM 			avl_remove(&bip->bi_fwd, bfp);
3273*10491SRishi.Srivatsavai@Sun.COM 			bfp->bf_flags &= ~BFF_INTREE;
3274*10491SRishi.Srivatsavai@Sun.COM 			avl_add(&fwd_scavenge, bfp);
3275*10491SRishi.Srivatsavai@Sun.COM 		}
3276*10491SRishi.Srivatsavai@Sun.COM 		rw_exit(&bip->bi_rwlock);
3277*10491SRishi.Srivatsavai@Sun.COM 		bfnext = avl_first(&fwd_scavenge);
3278*10491SRishi.Srivatsavai@Sun.COM 		while ((bfp = bfnext) != NULL) {
3279*10491SRishi.Srivatsavai@Sun.COM 			bfnext = AVL_NEXT(&fwd_scavenge, bfp);
3280*10491SRishi.Srivatsavai@Sun.COM 			avl_remove(&fwd_scavenge, bfp);
3281*10491SRishi.Srivatsavai@Sun.COM 			fwd_unref(bfp);	/* drop tree reference */
3282*10491SRishi.Srivatsavai@Sun.COM 		}
3283*10491SRishi.Srivatsavai@Sun.COM 		avl_destroy(&fwd_scavenge);
3284*10491SRishi.Srivatsavai@Sun.COM 		break;
3285*10491SRishi.Srivatsavai@Sun.COM 	}
3286*10491SRishi.Srivatsavai@Sun.COM 
3287*10491SRishi.Srivatsavai@Sun.COM 	case BRIOC_TABLEMAX:
3288*10491SRishi.Srivatsavai@Sun.COM 		if ((bip = bsp->bs_inst) == NULL ||
3289*10491SRishi.Srivatsavai@Sun.COM 		    (rc = miocpullup(mp, sizeof (uint32_t))) != 0)
3290*10491SRishi.Srivatsavai@Sun.COM 			break;
3291*10491SRishi.Srivatsavai@Sun.COM 		/* LINTED: alignment */
3292*10491SRishi.Srivatsavai@Sun.COM 		bip->bi_tablemax = *(uint32_t *)mp->b_cont->b_rptr;
3293*10491SRishi.Srivatsavai@Sun.COM 		break;
3294*10491SRishi.Srivatsavai@Sun.COM 	}
3295*10491SRishi.Srivatsavai@Sun.COM 
3296*10491SRishi.Srivatsavai@Sun.COM 	if (rc == 0)
3297*10491SRishi.Srivatsavai@Sun.COM 		miocack(wq, mp, len, 0);
3298*10491SRishi.Srivatsavai@Sun.COM 	else
3299*10491SRishi.Srivatsavai@Sun.COM 		miocnak(wq, mp, 0, rc);
3300*10491SRishi.Srivatsavai@Sun.COM }
3301*10491SRishi.Srivatsavai@Sun.COM 
3302*10491SRishi.Srivatsavai@Sun.COM static void
3303*10491SRishi.Srivatsavai@Sun.COM bridge_wput(queue_t *wq, mblk_t *mp)
3304*10491SRishi.Srivatsavai@Sun.COM {
3305*10491SRishi.Srivatsavai@Sun.COM 	switch (DB_TYPE(mp)) {
3306*10491SRishi.Srivatsavai@Sun.COM 	case M_IOCTL:
3307*10491SRishi.Srivatsavai@Sun.COM 		bridge_ioctl(wq, mp);
3308*10491SRishi.Srivatsavai@Sun.COM 		break;
3309*10491SRishi.Srivatsavai@Sun.COM 	case M_FLUSH:
3310*10491SRishi.Srivatsavai@Sun.COM 		if (*mp->b_rptr & FLUSHW)
3311*10491SRishi.Srivatsavai@Sun.COM 			*mp->b_rptr &= ~FLUSHW;
3312*10491SRishi.Srivatsavai@Sun.COM 		if (*mp->b_rptr & FLUSHR)
3313*10491SRishi.Srivatsavai@Sun.COM 			qreply(wq, mp);
3314*10491SRishi.Srivatsavai@Sun.COM 		else
3315*10491SRishi.Srivatsavai@Sun.COM 			freemsg(mp);
3316*10491SRishi.Srivatsavai@Sun.COM 		break;
3317*10491SRishi.Srivatsavai@Sun.COM 	default:
3318*10491SRishi.Srivatsavai@Sun.COM 		freemsg(mp);
3319*10491SRishi.Srivatsavai@Sun.COM 		break;
3320*10491SRishi.Srivatsavai@Sun.COM 	}
3321*10491SRishi.Srivatsavai@Sun.COM }
3322*10491SRishi.Srivatsavai@Sun.COM 
3323*10491SRishi.Srivatsavai@Sun.COM /*
3324*10491SRishi.Srivatsavai@Sun.COM  * This function allocates the main data structures for the bridge driver and
3325*10491SRishi.Srivatsavai@Sun.COM  * connects us into devfs.
3326*10491SRishi.Srivatsavai@Sun.COM  */
3327*10491SRishi.Srivatsavai@Sun.COM static void
3328*10491SRishi.Srivatsavai@Sun.COM bridge_inst_init(void)
3329*10491SRishi.Srivatsavai@Sun.COM {
3330*10491SRishi.Srivatsavai@Sun.COM 	bridge_scan_interval = 5 * drv_usectohz(1000000);
3331*10491SRishi.Srivatsavai@Sun.COM 	bridge_fwd_age = 25 * drv_usectohz(1000000);
3332*10491SRishi.Srivatsavai@Sun.COM 
3333*10491SRishi.Srivatsavai@Sun.COM 	rw_init(&bmac_rwlock, NULL, RW_DRIVER, NULL);
3334*10491SRishi.Srivatsavai@Sun.COM 	list_create(&bmac_list, sizeof (bridge_mac_t),
3335*10491SRishi.Srivatsavai@Sun.COM 	    offsetof(bridge_mac_t, bm_node));
3336*10491SRishi.Srivatsavai@Sun.COM 	list_create(&inst_list, sizeof (bridge_inst_t),
3337*10491SRishi.Srivatsavai@Sun.COM 	    offsetof(bridge_inst_t, bi_node));
3338*10491SRishi.Srivatsavai@Sun.COM 	cv_init(&inst_cv, NULL, CV_DRIVER, NULL);
3339*10491SRishi.Srivatsavai@Sun.COM 	mutex_init(&inst_lock, NULL, MUTEX_DRIVER, NULL);
3340*10491SRishi.Srivatsavai@Sun.COM 	cv_init(&stream_ref_cv, NULL, CV_DRIVER, NULL);
3341*10491SRishi.Srivatsavai@Sun.COM 	mutex_init(&stream_ref_lock, NULL, MUTEX_DRIVER, NULL);
3342*10491SRishi.Srivatsavai@Sun.COM 
3343*10491SRishi.Srivatsavai@Sun.COM 	mac_bridge_vectors(bridge_xmit_cb, bridge_recv_cb, bridge_ref_cb,
3344*10491SRishi.Srivatsavai@Sun.COM 	    bridge_ls_cb);
3345*10491SRishi.Srivatsavai@Sun.COM }
3346*10491SRishi.Srivatsavai@Sun.COM 
3347*10491SRishi.Srivatsavai@Sun.COM /*
3348*10491SRishi.Srivatsavai@Sun.COM  * This function disconnects from devfs and destroys all data structures in
3349*10491SRishi.Srivatsavai@Sun.COM  * preparation for unload.  It's assumed that there are no active bridge
3350*10491SRishi.Srivatsavai@Sun.COM  * references left at this point.
3351*10491SRishi.Srivatsavai@Sun.COM  */
3352*10491SRishi.Srivatsavai@Sun.COM static void
3353*10491SRishi.Srivatsavai@Sun.COM bridge_inst_fini(void)
3354*10491SRishi.Srivatsavai@Sun.COM {
3355*10491SRishi.Srivatsavai@Sun.COM 	mac_bridge_vectors(NULL, NULL, NULL, NULL);
3356*10491SRishi.Srivatsavai@Sun.COM 	if (bridge_timerid != 0)
3357*10491SRishi.Srivatsavai@Sun.COM 		(void) untimeout(bridge_timerid);
3358*10491SRishi.Srivatsavai@Sun.COM 	rw_destroy(&bmac_rwlock);
3359*10491SRishi.Srivatsavai@Sun.COM 	list_destroy(&bmac_list);
3360*10491SRishi.Srivatsavai@Sun.COM 	list_destroy(&inst_list);
3361*10491SRishi.Srivatsavai@Sun.COM 	cv_destroy(&inst_cv);
3362*10491SRishi.Srivatsavai@Sun.COM 	mutex_destroy(&inst_lock);
3363*10491SRishi.Srivatsavai@Sun.COM 	cv_destroy(&stream_ref_cv);
3364*10491SRishi.Srivatsavai@Sun.COM 	mutex_destroy(&stream_ref_lock);
3365*10491SRishi.Srivatsavai@Sun.COM }
3366*10491SRishi.Srivatsavai@Sun.COM 
3367*10491SRishi.Srivatsavai@Sun.COM /*
3368*10491SRishi.Srivatsavai@Sun.COM  * bridge_attach()
3369*10491SRishi.Srivatsavai@Sun.COM  *
3370*10491SRishi.Srivatsavai@Sun.COM  * Description:
3371*10491SRishi.Srivatsavai@Sun.COM  *    Attach bridge driver to the system.
3372*10491SRishi.Srivatsavai@Sun.COM  */
3373*10491SRishi.Srivatsavai@Sun.COM static int
3374*10491SRishi.Srivatsavai@Sun.COM bridge_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3375*10491SRishi.Srivatsavai@Sun.COM {
3376*10491SRishi.Srivatsavai@Sun.COM 	if (cmd != DDI_ATTACH)
3377*10491SRishi.Srivatsavai@Sun.COM 		return (DDI_FAILURE);
3378*10491SRishi.Srivatsavai@Sun.COM 
3379*10491SRishi.Srivatsavai@Sun.COM 	if (ddi_create_minor_node(dip, BRIDGE_CTL, S_IFCHR, 0, DDI_PSEUDO,
3380*10491SRishi.Srivatsavai@Sun.COM 	    CLONE_DEV) == DDI_FAILURE) {
3381*10491SRishi.Srivatsavai@Sun.COM 		return (DDI_FAILURE);
3382*10491SRishi.Srivatsavai@Sun.COM 	}
3383*10491SRishi.Srivatsavai@Sun.COM 
3384*10491SRishi.Srivatsavai@Sun.COM 	if (dld_ioc_register(BRIDGE_IOC, bridge_ioc_list,
3385*10491SRishi.Srivatsavai@Sun.COM 	    DLDIOCCNT(bridge_ioc_list)) != 0) {
3386*10491SRishi.Srivatsavai@Sun.COM 		ddi_remove_minor_node(dip, BRIDGE_CTL);
3387*10491SRishi.Srivatsavai@Sun.COM 		return (DDI_FAILURE);
3388*10491SRishi.Srivatsavai@Sun.COM 	}
3389*10491SRishi.Srivatsavai@Sun.COM 
3390*10491SRishi.Srivatsavai@Sun.COM 	bridge_dev_info = dip;
3391*10491SRishi.Srivatsavai@Sun.COM 	bridge_major = ddi_driver_major(dip);
3392*10491SRishi.Srivatsavai@Sun.COM 	bridge_taskq = ddi_taskq_create(dip, "bridge", 1, TASKQ_DEFAULTPRI, 0);
3393*10491SRishi.Srivatsavai@Sun.COM 	return (DDI_SUCCESS);
3394*10491SRishi.Srivatsavai@Sun.COM }
3395*10491SRishi.Srivatsavai@Sun.COM 
3396*10491SRishi.Srivatsavai@Sun.COM /*
3397*10491SRishi.Srivatsavai@Sun.COM  * bridge_detach()
3398*10491SRishi.Srivatsavai@Sun.COM  *
3399*10491SRishi.Srivatsavai@Sun.COM  * Description:
3400*10491SRishi.Srivatsavai@Sun.COM  *    Detach an interface to the system.
3401*10491SRishi.Srivatsavai@Sun.COM  */
3402*10491SRishi.Srivatsavai@Sun.COM static int
3403*10491SRishi.Srivatsavai@Sun.COM bridge_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3404*10491SRishi.Srivatsavai@Sun.COM {
3405*10491SRishi.Srivatsavai@Sun.COM 	if (cmd != DDI_DETACH)
3406*10491SRishi.Srivatsavai@Sun.COM 		return (DDI_FAILURE);
3407*10491SRishi.Srivatsavai@Sun.COM 
3408*10491SRishi.Srivatsavai@Sun.COM 	ddi_remove_minor_node(dip, NULL);
3409*10491SRishi.Srivatsavai@Sun.COM 	ddi_taskq_destroy(bridge_taskq);
3410*10491SRishi.Srivatsavai@Sun.COM 	bridge_dev_info = NULL;
3411*10491SRishi.Srivatsavai@Sun.COM 	return (DDI_SUCCESS);
3412*10491SRishi.Srivatsavai@Sun.COM }
3413*10491SRishi.Srivatsavai@Sun.COM 
3414*10491SRishi.Srivatsavai@Sun.COM /*
3415*10491SRishi.Srivatsavai@Sun.COM  * bridge_info()
3416*10491SRishi.Srivatsavai@Sun.COM  *
3417*10491SRishi.Srivatsavai@Sun.COM  * Description:
3418*10491SRishi.Srivatsavai@Sun.COM  *    Translate "dev_t" to a pointer to the associated "dev_info_t".
3419*10491SRishi.Srivatsavai@Sun.COM  */
3420*10491SRishi.Srivatsavai@Sun.COM /* ARGSUSED */
3421*10491SRishi.Srivatsavai@Sun.COM static int
3422*10491SRishi.Srivatsavai@Sun.COM bridge_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
3423*10491SRishi.Srivatsavai@Sun.COM 	void **result)
3424*10491SRishi.Srivatsavai@Sun.COM {
3425*10491SRishi.Srivatsavai@Sun.COM 	int	rc;
3426*10491SRishi.Srivatsavai@Sun.COM 
3427*10491SRishi.Srivatsavai@Sun.COM 	switch (infocmd) {
3428*10491SRishi.Srivatsavai@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
3429*10491SRishi.Srivatsavai@Sun.COM 		if (bridge_dev_info == NULL) {
3430*10491SRishi.Srivatsavai@Sun.COM 			rc = DDI_FAILURE;
3431*10491SRishi.Srivatsavai@Sun.COM 		} else {
3432*10491SRishi.Srivatsavai@Sun.COM 			*result = (void *)bridge_dev_info;
3433*10491SRishi.Srivatsavai@Sun.COM 			rc = DDI_SUCCESS;
3434*10491SRishi.Srivatsavai@Sun.COM 		}
3435*10491SRishi.Srivatsavai@Sun.COM 		break;
3436*10491SRishi.Srivatsavai@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
3437*10491SRishi.Srivatsavai@Sun.COM 		*result = NULL;
3438*10491SRishi.Srivatsavai@Sun.COM 		rc = DDI_SUCCESS;
3439*10491SRishi.Srivatsavai@Sun.COM 		break;
3440*10491SRishi.Srivatsavai@Sun.COM 	default:
3441*10491SRishi.Srivatsavai@Sun.COM 		rc = DDI_FAILURE;
3442*10491SRishi.Srivatsavai@Sun.COM 		break;
3443*10491SRishi.Srivatsavai@Sun.COM 	}
3444*10491SRishi.Srivatsavai@Sun.COM 	return (rc);
3445*10491SRishi.Srivatsavai@Sun.COM }
3446*10491SRishi.Srivatsavai@Sun.COM 
3447*10491SRishi.Srivatsavai@Sun.COM static struct module_info bridge_modinfo = {
3448*10491SRishi.Srivatsavai@Sun.COM 	2105,			/* mi_idnum */
3449*10491SRishi.Srivatsavai@Sun.COM 	"bridge",		/* mi_idname */
3450*10491SRishi.Srivatsavai@Sun.COM 	0,			/* mi_minpsz */
3451*10491SRishi.Srivatsavai@Sun.COM 	16384,			/* mi_maxpsz */
3452*10491SRishi.Srivatsavai@Sun.COM 	65536,			/* mi_hiwat */
3453*10491SRishi.Srivatsavai@Sun.COM 	128			/* mi_lowat */
3454*10491SRishi.Srivatsavai@Sun.COM };
3455*10491SRishi.Srivatsavai@Sun.COM 
3456*10491SRishi.Srivatsavai@Sun.COM static struct qinit bridge_rinit = {
3457*10491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_putp */
3458*10491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_srvp */
3459*10491SRishi.Srivatsavai@Sun.COM 	bridge_open,		/* qi_qopen */
3460*10491SRishi.Srivatsavai@Sun.COM 	bridge_close,		/* qi_qclose */
3461*10491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qadmin */
3462*10491SRishi.Srivatsavai@Sun.COM 	&bridge_modinfo,	/* qi_minfo */
3463*10491SRishi.Srivatsavai@Sun.COM 	NULL			/* qi_mstat */
3464*10491SRishi.Srivatsavai@Sun.COM };
3465*10491SRishi.Srivatsavai@Sun.COM 
3466*10491SRishi.Srivatsavai@Sun.COM static struct qinit bridge_winit = {
3467*10491SRishi.Srivatsavai@Sun.COM 	(int (*)())bridge_wput, /* qi_putp */
3468*10491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_srvp */
3469*10491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qopen */
3470*10491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qclose */
3471*10491SRishi.Srivatsavai@Sun.COM 	NULL,			/* qi_qadmin */
3472*10491SRishi.Srivatsavai@Sun.COM 	&bridge_modinfo,	/* qi_minfo */
3473*10491SRishi.Srivatsavai@Sun.COM 	NULL			/* qi_mstat */
3474*10491SRishi.Srivatsavai@Sun.COM };
3475*10491SRishi.Srivatsavai@Sun.COM 
3476*10491SRishi.Srivatsavai@Sun.COM static struct streamtab bridge_tab = {
3477*10491SRishi.Srivatsavai@Sun.COM 	&bridge_rinit,	/* st_rdinit */
3478*10491SRishi.Srivatsavai@Sun.COM 	&bridge_winit	/* st_wrinit */
3479*10491SRishi.Srivatsavai@Sun.COM };
3480*10491SRishi.Srivatsavai@Sun.COM 
3481*10491SRishi.Srivatsavai@Sun.COM /* No STREAMS perimeters; we do all our own locking */
3482*10491SRishi.Srivatsavai@Sun.COM DDI_DEFINE_STREAM_OPS(bridge_ops, nulldev, nulldev, bridge_attach,
3483*10491SRishi.Srivatsavai@Sun.COM     bridge_detach, nodev, bridge_info, D_NEW | D_MP, &bridge_tab,
3484*10491SRishi.Srivatsavai@Sun.COM     ddi_quiesce_not_supported);
3485*10491SRishi.Srivatsavai@Sun.COM 
3486*10491SRishi.Srivatsavai@Sun.COM static struct modldrv modldrv = {
3487*10491SRishi.Srivatsavai@Sun.COM 	&mod_driverops,
3488*10491SRishi.Srivatsavai@Sun.COM 	"bridging driver",
3489*10491SRishi.Srivatsavai@Sun.COM 	&bridge_ops
3490*10491SRishi.Srivatsavai@Sun.COM };
3491*10491SRishi.Srivatsavai@Sun.COM 
3492*10491SRishi.Srivatsavai@Sun.COM static struct modlinkage modlinkage = {
3493*10491SRishi.Srivatsavai@Sun.COM 	MODREV_1,
3494*10491SRishi.Srivatsavai@Sun.COM 	(void *)&modldrv,
3495*10491SRishi.Srivatsavai@Sun.COM 	NULL
3496*10491SRishi.Srivatsavai@Sun.COM };
3497*10491SRishi.Srivatsavai@Sun.COM 
3498*10491SRishi.Srivatsavai@Sun.COM int
3499*10491SRishi.Srivatsavai@Sun.COM _init(void)
3500*10491SRishi.Srivatsavai@Sun.COM {
3501*10491SRishi.Srivatsavai@Sun.COM 	int retv;
3502*10491SRishi.Srivatsavai@Sun.COM 
3503*10491SRishi.Srivatsavai@Sun.COM 	bridge_inst_init();
3504*10491SRishi.Srivatsavai@Sun.COM 	if ((retv = mod_install(&modlinkage)) != 0)
3505*10491SRishi.Srivatsavai@Sun.COM 		bridge_inst_fini();
3506*10491SRishi.Srivatsavai@Sun.COM 	return (retv);
3507*10491SRishi.Srivatsavai@Sun.COM }
3508*10491SRishi.Srivatsavai@Sun.COM 
3509*10491SRishi.Srivatsavai@Sun.COM int
3510*10491SRishi.Srivatsavai@Sun.COM _fini(void)
3511*10491SRishi.Srivatsavai@Sun.COM {
3512*10491SRishi.Srivatsavai@Sun.COM 	int retv;
3513*10491SRishi.Srivatsavai@Sun.COM 
3514*10491SRishi.Srivatsavai@Sun.COM 	rw_enter(&bmac_rwlock, RW_READER);
3515*10491SRishi.Srivatsavai@Sun.COM 	retv = list_is_empty(&bmac_list) ? 0 : EBUSY;
3516*10491SRishi.Srivatsavai@Sun.COM 	rw_exit(&bmac_rwlock);
3517*10491SRishi.Srivatsavai@Sun.COM 	if (retv == 0 &&
3518*10491SRishi.Srivatsavai@Sun.COM 	    (retv = mod_remove(&modlinkage)) == 0)
3519*10491SRishi.Srivatsavai@Sun.COM 		bridge_inst_fini();
3520*10491SRishi.Srivatsavai@Sun.COM 	return (retv);
3521*10491SRishi.Srivatsavai@Sun.COM }
3522*10491SRishi.Srivatsavai@Sun.COM 
3523*10491SRishi.Srivatsavai@Sun.COM int
3524*10491SRishi.Srivatsavai@Sun.COM _info(struct modinfo *modinfop)
3525*10491SRishi.Srivatsavai@Sun.COM {
3526*10491SRishi.Srivatsavai@Sun.COM 	return (mod_info(&modlinkage, modinfop));
3527*10491SRishi.Srivatsavai@Sun.COM }
3528