xref: /onnv-gate/usr/src/uts/common/io/dls/dls.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * Data-Link Services Module
31*0Sstevel@tonic-gate  */
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #include	<sys/types.h>
34*0Sstevel@tonic-gate #include	<sys/stream.h>
35*0Sstevel@tonic-gate #include	<sys/strsun.h>
36*0Sstevel@tonic-gate #include	<sys/sysmacros.h>
37*0Sstevel@tonic-gate #include	<sys/atomic.h>
38*0Sstevel@tonic-gate #include	<sys/ght.h>
39*0Sstevel@tonic-gate #include	<sys/dlpi.h>
40*0Sstevel@tonic-gate #include	<sys/vlan.h>
41*0Sstevel@tonic-gate #include	<sys/ethernet.h>
42*0Sstevel@tonic-gate #include	<sys/byteorder.h>
43*0Sstevel@tonic-gate #include	<sys/mac.h>
44*0Sstevel@tonic-gate 
45*0Sstevel@tonic-gate #include	<sys/dls.h>
46*0Sstevel@tonic-gate #include	<sys/dls_impl.h>
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate static kmem_cache_t	*i_dls_impl_cachep;
49*0Sstevel@tonic-gate static uint32_t		i_dls_impl_count;
50*0Sstevel@tonic-gate 
51*0Sstevel@tonic-gate /*
52*0Sstevel@tonic-gate  * Private functions.
53*0Sstevel@tonic-gate  */
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate /*ARGSUSED*/
56*0Sstevel@tonic-gate static int
57*0Sstevel@tonic-gate i_dls_constructor(void *buf, void *arg, int kmflag)
58*0Sstevel@tonic-gate {
59*0Sstevel@tonic-gate 	dls_impl_t	*dip = buf;
60*0Sstevel@tonic-gate 
61*0Sstevel@tonic-gate 	bzero(buf, sizeof (dls_impl_t));
62*0Sstevel@tonic-gate 
63*0Sstevel@tonic-gate 	rw_init(&(dip->di_lock), NULL, RW_DRIVER, NULL);
64*0Sstevel@tonic-gate 	return (0);
65*0Sstevel@tonic-gate }
66*0Sstevel@tonic-gate 
67*0Sstevel@tonic-gate /*ARGSUSED*/
68*0Sstevel@tonic-gate static void
69*0Sstevel@tonic-gate i_dls_destructor(void *buf, void *arg)
70*0Sstevel@tonic-gate {
71*0Sstevel@tonic-gate 	dls_impl_t	*dip = buf;
72*0Sstevel@tonic-gate 
73*0Sstevel@tonic-gate 	ASSERT(dip->di_dvp == NULL);
74*0Sstevel@tonic-gate 	ASSERT(dip->di_mnh == NULL);
75*0Sstevel@tonic-gate 	ASSERT(dip->di_dmap == NULL);
76*0Sstevel@tonic-gate 	ASSERT(!dip->di_bound);
77*0Sstevel@tonic-gate 	ASSERT(dip->di_rx == NULL);
78*0Sstevel@tonic-gate 	ASSERT(dip->di_tx == NULL);
79*0Sstevel@tonic-gate 
80*0Sstevel@tonic-gate 	rw_destroy(&(dip->di_lock));
81*0Sstevel@tonic-gate }
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate static void
84*0Sstevel@tonic-gate i_dls_notify(void *arg, mac_notify_type_t type)
85*0Sstevel@tonic-gate {
86*0Sstevel@tonic-gate 	dls_impl_t		*dip = arg;
87*0Sstevel@tonic-gate 
88*0Sstevel@tonic-gate 	switch (type) {
89*0Sstevel@tonic-gate 	case MAC_NOTE_UNICST:
90*0Sstevel@tonic-gate 		mac_unicst_get(dip->di_mh, dip->di_unicst_addr);
91*0Sstevel@tonic-gate 		break;
92*0Sstevel@tonic-gate 
93*0Sstevel@tonic-gate 	case MAC_NOTE_PROMISC:
94*0Sstevel@tonic-gate 		/*
95*0Sstevel@tonic-gate 		 * Every time the MAC interface changes promiscuity we
96*0Sstevel@tonic-gate 		 * need to reset the transmit function:
97*0Sstevel@tonic-gate 		 *
98*0Sstevel@tonic-gate 		 * - In non-promiscuous mode we simply copy the function
99*0Sstevel@tonic-gate 		 *   pointer from the MAC module.
100*0Sstevel@tonic-gate 		 *
101*0Sstevel@tonic-gate 		 * - In promiscuous mode we use mac_txloop(), which will
102*0Sstevel@tonic-gate 		 *   handle the loopback case.
103*0Sstevel@tonic-gate 		 */
104*0Sstevel@tonic-gate 		if (mac_promisc_get(dip->di_mh, MAC_PROMISC)) {
105*0Sstevel@tonic-gate 			dip->di_tx = mac_txloop;
106*0Sstevel@tonic-gate 			dip->di_tx_arg = dip->di_mh;
107*0Sstevel@tonic-gate 		} else {
108*0Sstevel@tonic-gate 			mac_tx_get(dip->di_mh, &dip->di_tx, &dip->di_tx_arg);
109*0Sstevel@tonic-gate 		}
110*0Sstevel@tonic-gate 		break;
111*0Sstevel@tonic-gate 	}
112*0Sstevel@tonic-gate }
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate static mblk_t *
115*0Sstevel@tonic-gate i_dls_ether_header(dls_impl_t *dip, const uint8_t *daddr, uint16_t sap,
116*0Sstevel@tonic-gate     uint_t pri)
117*0Sstevel@tonic-gate {
118*0Sstevel@tonic-gate 	struct ether_header		*ehp;
119*0Sstevel@tonic-gate 	struct ether_vlan_header	*evhp;
120*0Sstevel@tonic-gate 	const mac_info_t		*mip;
121*0Sstevel@tonic-gate 	uint_t				addr_length;
122*0Sstevel@tonic-gate 	uint16_t			vid;
123*0Sstevel@tonic-gate 	mblk_t				*mp;
124*0Sstevel@tonic-gate 
125*0Sstevel@tonic-gate 	mip = dip->di_mip;
126*0Sstevel@tonic-gate 	addr_length = mip->mi_addr_length;
127*0Sstevel@tonic-gate 
128*0Sstevel@tonic-gate 	/*
129*0Sstevel@tonic-gate 	 * Check whether the DLSAP value is legal for ethernet.
130*0Sstevel@tonic-gate 	 */
131*0Sstevel@tonic-gate 	if (!SAP_LEGAL(mip->mi_media, sap))
132*0Sstevel@tonic-gate 		return (NULL);
133*0Sstevel@tonic-gate 
134*0Sstevel@tonic-gate 	/*
135*0Sstevel@tonic-gate 	 * If the interface is a VLAN interface then we need VLAN packet
136*0Sstevel@tonic-gate 	 * headers.
137*0Sstevel@tonic-gate 	 */
138*0Sstevel@tonic-gate 	if ((vid = dip->di_dvp->dv_id) != VLAN_ID_NONE)
139*0Sstevel@tonic-gate 		goto vlan;
140*0Sstevel@tonic-gate 
141*0Sstevel@tonic-gate 	/*
142*0Sstevel@tonic-gate 	 * Allocate a normal ethernet packet header.
143*0Sstevel@tonic-gate 	 */
144*0Sstevel@tonic-gate 	if ((mp = allocb(sizeof (struct ether_header), BPRI_HI)) == NULL)
145*0Sstevel@tonic-gate 		return (NULL);
146*0Sstevel@tonic-gate 
147*0Sstevel@tonic-gate 	/*
148*0Sstevel@tonic-gate 	 * Copy in the given address as the destination, our current unicast
149*0Sstevel@tonic-gate 	 * address as the source and the given sap as the type/length.
150*0Sstevel@tonic-gate 	 */
151*0Sstevel@tonic-gate 	ehp = (struct ether_header *)mp->b_rptr;
152*0Sstevel@tonic-gate 	bcopy(daddr, &(ehp->ether_dhost), addr_length);
153*0Sstevel@tonic-gate 	bcopy(dip->di_unicst_addr, &(ehp->ether_shost), addr_length);
154*0Sstevel@tonic-gate 	ehp->ether_type = htons(sap);
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate 	mp->b_wptr += sizeof (struct ether_header);
157*0Sstevel@tonic-gate 	return (mp);
158*0Sstevel@tonic-gate 
159*0Sstevel@tonic-gate vlan:
160*0Sstevel@tonic-gate 	/*
161*0Sstevel@tonic-gate 	 * Allocate a VLAN ethernet packet header.
162*0Sstevel@tonic-gate 	 */
163*0Sstevel@tonic-gate 	if ((mp = allocb(sizeof (struct ether_vlan_header), BPRI_HI)) == NULL)
164*0Sstevel@tonic-gate 		return (NULL);
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate 	/*
167*0Sstevel@tonic-gate 	 * Copy in the given address as the destination, our current unicast
168*0Sstevel@tonic-gate 	 * address as the source, the VLAN tpid and tci and the given sap as
169*0Sstevel@tonic-gate 	 * the type/length.
170*0Sstevel@tonic-gate 	 */
171*0Sstevel@tonic-gate 	evhp = (struct ether_vlan_header *)mp->b_rptr;
172*0Sstevel@tonic-gate 	bcopy(daddr, &(evhp->ether_dhost), addr_length);
173*0Sstevel@tonic-gate 	bcopy(dip->di_unicst_addr, &(evhp->ether_shost), addr_length);
174*0Sstevel@tonic-gate 	evhp->ether_tpid = htons(VLAN_TPID);
175*0Sstevel@tonic-gate 	evhp->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI, vid));
176*0Sstevel@tonic-gate 	evhp->ether_type = htons(sap);
177*0Sstevel@tonic-gate 
178*0Sstevel@tonic-gate 	mp->b_wptr += sizeof (struct ether_vlan_header);
179*0Sstevel@tonic-gate 	return (mp);
180*0Sstevel@tonic-gate }
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate /*ARGSUSED*/
183*0Sstevel@tonic-gate static void
184*0Sstevel@tonic-gate i_dls_ether_header_info(dls_impl_t *dip, mblk_t *mp, dls_header_info_t *dhip)
185*0Sstevel@tonic-gate {
186*0Sstevel@tonic-gate 	struct ether_header		*ehp;
187*0Sstevel@tonic-gate 	struct ether_vlan_header	*evhp;
188*0Sstevel@tonic-gate 	uint16_t			type_length;
189*0Sstevel@tonic-gate 	uint16_t			tci;
190*0Sstevel@tonic-gate 
191*0Sstevel@tonic-gate 	ASSERT(MBLKL(mp) >= sizeof (struct ether_header));
192*0Sstevel@tonic-gate 	ehp = (struct ether_header *)mp->b_rptr;
193*0Sstevel@tonic-gate 
194*0Sstevel@tonic-gate 	/*
195*0Sstevel@tonic-gate 	 * Determine whether to parse a normal or VLAN ethernet header.
196*0Sstevel@tonic-gate 	 */
197*0Sstevel@tonic-gate 	if ((type_length = ntohs(ehp->ether_type)) == VLAN_TPID)
198*0Sstevel@tonic-gate 		goto vlan;
199*0Sstevel@tonic-gate 
200*0Sstevel@tonic-gate 	/*
201*0Sstevel@tonic-gate 	 * Specify the length of the header.
202*0Sstevel@tonic-gate 	 */
203*0Sstevel@tonic-gate 	dhip->dhi_length = sizeof (struct ether_header);
204*0Sstevel@tonic-gate 
205*0Sstevel@tonic-gate 	/*
206*0Sstevel@tonic-gate 	 * Get the destination address.
207*0Sstevel@tonic-gate 	 */
208*0Sstevel@tonic-gate 	dhip->dhi_daddr = (const uint8_t *)&(ehp->ether_dhost);
209*0Sstevel@tonic-gate 
210*0Sstevel@tonic-gate 	/*
211*0Sstevel@tonic-gate 	 * If the destination address was a group address then
212*0Sstevel@tonic-gate 	 * dl_group_address field should be non-zero.
213*0Sstevel@tonic-gate 	 */
214*0Sstevel@tonic-gate 	dhip->dhi_isgroup = (dhip->dhi_daddr[0] & 0x01);
215*0Sstevel@tonic-gate 
216*0Sstevel@tonic-gate 	/*
217*0Sstevel@tonic-gate 	 * Get the source address.
218*0Sstevel@tonic-gate 	 */
219*0Sstevel@tonic-gate 	dhip->dhi_saddr = (uint8_t *)&(ehp->ether_shost);
220*0Sstevel@tonic-gate 
221*0Sstevel@tonic-gate 	/*
222*0Sstevel@tonic-gate 	 * Get the ethertype
223*0Sstevel@tonic-gate 	 */
224*0Sstevel@tonic-gate 	dhip->dhi_ethertype = (type_length > ETHERMTU) ? type_length : 0;
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate 	/*
227*0Sstevel@tonic-gate 	 * The VLAN identifier must be VLAN_ID_NONE.
228*0Sstevel@tonic-gate 	 */
229*0Sstevel@tonic-gate 	dhip->dhi_vid = VLAN_ID_NONE;
230*0Sstevel@tonic-gate 
231*0Sstevel@tonic-gate 	return;
232*0Sstevel@tonic-gate 
233*0Sstevel@tonic-gate vlan:
234*0Sstevel@tonic-gate 	ASSERT(MBLKL(mp) >= sizeof (struct ether_vlan_header));
235*0Sstevel@tonic-gate 	evhp = (struct ether_vlan_header *)mp->b_rptr;
236*0Sstevel@tonic-gate 
237*0Sstevel@tonic-gate 	/*
238*0Sstevel@tonic-gate 	 * Specify the length of the header.
239*0Sstevel@tonic-gate 	 */
240*0Sstevel@tonic-gate 	dhip->dhi_length = sizeof (struct ether_vlan_header);
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate 	/*
243*0Sstevel@tonic-gate 	 * Get the destination address.
244*0Sstevel@tonic-gate 	 */
245*0Sstevel@tonic-gate 	dhip->dhi_daddr = (const uint8_t *)&(evhp->ether_dhost);
246*0Sstevel@tonic-gate 
247*0Sstevel@tonic-gate 	/*
248*0Sstevel@tonic-gate 	 * If the destination address was a group address then
249*0Sstevel@tonic-gate 	 * dl_group_address field should be non-zero.
250*0Sstevel@tonic-gate 	 */
251*0Sstevel@tonic-gate 	dhip->dhi_isgroup = (dhip->dhi_daddr[0] & 0x01);
252*0Sstevel@tonic-gate 
253*0Sstevel@tonic-gate 	/*
254*0Sstevel@tonic-gate 	 * Get the source address.
255*0Sstevel@tonic-gate 	 */
256*0Sstevel@tonic-gate 	dhip->dhi_saddr = (uint8_t *)&(evhp->ether_shost);
257*0Sstevel@tonic-gate 
258*0Sstevel@tonic-gate 	/*
259*0Sstevel@tonic-gate 	 * Get the ethertype
260*0Sstevel@tonic-gate 	 */
261*0Sstevel@tonic-gate 	type_length = ntohs(evhp->ether_type);
262*0Sstevel@tonic-gate 	dhip->dhi_ethertype = (type_length > ETHERMTU) ? type_length : 0;
263*0Sstevel@tonic-gate 	ASSERT(dhip->dhi_ethertype != VLAN_TPID);
264*0Sstevel@tonic-gate 
265*0Sstevel@tonic-gate 	/*
266*0Sstevel@tonic-gate 	 * Get the VLAN identifier.
267*0Sstevel@tonic-gate 	 */
268*0Sstevel@tonic-gate 	tci = ntohs(evhp->ether_tci);
269*0Sstevel@tonic-gate 	dhip->dhi_vid = VLAN_ID(tci);
270*0Sstevel@tonic-gate }
271*0Sstevel@tonic-gate 
272*0Sstevel@tonic-gate /*
273*0Sstevel@tonic-gate  * Module initialization functions.
274*0Sstevel@tonic-gate  */
275*0Sstevel@tonic-gate 
276*0Sstevel@tonic-gate void
277*0Sstevel@tonic-gate dls_init(void)
278*0Sstevel@tonic-gate {
279*0Sstevel@tonic-gate 	/*
280*0Sstevel@tonic-gate 	 * Create a kmem_cache of dls_impl_t.
281*0Sstevel@tonic-gate 	 */
282*0Sstevel@tonic-gate 	i_dls_impl_cachep = kmem_cache_create("dls_cache",
283*0Sstevel@tonic-gate 	    sizeof (dls_impl_t), 0, i_dls_constructor, i_dls_destructor, NULL,
284*0Sstevel@tonic-gate 	    NULL, NULL, 0);
285*0Sstevel@tonic-gate 	ASSERT(i_dls_impl_cachep != NULL);
286*0Sstevel@tonic-gate }
287*0Sstevel@tonic-gate 
288*0Sstevel@tonic-gate int
289*0Sstevel@tonic-gate dls_fini(void)
290*0Sstevel@tonic-gate {
291*0Sstevel@tonic-gate 	/*
292*0Sstevel@tonic-gate 	 * If there are any dls_impl_t in use then return EBUSY.
293*0Sstevel@tonic-gate 	 */
294*0Sstevel@tonic-gate 	if (i_dls_impl_count != 0)
295*0Sstevel@tonic-gate 		return (EBUSY);
296*0Sstevel@tonic-gate 
297*0Sstevel@tonic-gate 	/*
298*0Sstevel@tonic-gate 	 * Destroy the kmem_cache.
299*0Sstevel@tonic-gate 	 */
300*0Sstevel@tonic-gate 	kmem_cache_destroy(i_dls_impl_cachep);
301*0Sstevel@tonic-gate 	return (0);
302*0Sstevel@tonic-gate }
303*0Sstevel@tonic-gate 
304*0Sstevel@tonic-gate /*
305*0Sstevel@tonic-gate  * Client function.
306*0Sstevel@tonic-gate  */
307*0Sstevel@tonic-gate 
308*0Sstevel@tonic-gate int
309*0Sstevel@tonic-gate dls_create(const char *name, const char *dev, uint_t port, uint16_t vid)
310*0Sstevel@tonic-gate {
311*0Sstevel@tonic-gate 	return (dls_vlan_create(name, dev, port, vid));
312*0Sstevel@tonic-gate }
313*0Sstevel@tonic-gate 
314*0Sstevel@tonic-gate int
315*0Sstevel@tonic-gate dls_destroy(const char *name)
316*0Sstevel@tonic-gate {
317*0Sstevel@tonic-gate 	return (dls_vlan_destroy(name));
318*0Sstevel@tonic-gate }
319*0Sstevel@tonic-gate 
320*0Sstevel@tonic-gate int
321*0Sstevel@tonic-gate dls_open(const char *name, dls_channel_t *dcp)
322*0Sstevel@tonic-gate {
323*0Sstevel@tonic-gate 	dls_impl_t	*dip;
324*0Sstevel@tonic-gate 	dls_vlan_t	*dvp;
325*0Sstevel@tonic-gate 	dls_link_t	*dlp;
326*0Sstevel@tonic-gate 	int		err;
327*0Sstevel@tonic-gate 
328*0Sstevel@tonic-gate 	/*
329*0Sstevel@tonic-gate 	 * Get a reference to the named dls_vlan_t.
330*0Sstevel@tonic-gate 	 */
331*0Sstevel@tonic-gate 	if ((err = dls_vlan_hold(name, &dvp)) != 0)
332*0Sstevel@tonic-gate 		return (err);
333*0Sstevel@tonic-gate 
334*0Sstevel@tonic-gate 	/*
335*0Sstevel@tonic-gate 	 * Allocate a new dls_impl_t.
336*0Sstevel@tonic-gate 	 */
337*0Sstevel@tonic-gate 	dip = kmem_cache_alloc(i_dls_impl_cachep, KM_SLEEP);
338*0Sstevel@tonic-gate 	dip->di_dvp = dvp;
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate 	/*
341*0Sstevel@tonic-gate 	 * Cache a copy of the MAC interface handle, a pointer to the
342*0Sstevel@tonic-gate 	 * immutable MAC info and a copy of the current MAC address.
343*0Sstevel@tonic-gate 	 */
344*0Sstevel@tonic-gate 	dlp = dvp->dv_dlp;
345*0Sstevel@tonic-gate 	dip->di_mh = dlp->dl_mh;
346*0Sstevel@tonic-gate 	dip->di_mip = dlp->dl_mip;
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 	mac_unicst_get(dip->di_mh, dip->di_unicst_addr);
349*0Sstevel@tonic-gate 
350*0Sstevel@tonic-gate 	/*
351*0Sstevel@tonic-gate 	 * Set the MAC transmit function.
352*0Sstevel@tonic-gate 	 *
353*0Sstevel@tonic-gate 	 * NOTE: We know that the MAC is not in promiscuous mode so we
354*0Sstevel@tonic-gate 	 *	 simply copy the values from the MAC.
355*0Sstevel@tonic-gate 	 *	 (See comment in i_dls_notify() for further explanation).
356*0Sstevel@tonic-gate 	 */
357*0Sstevel@tonic-gate 	mac_tx_get(dip->di_mh, &dip->di_tx, &dip->di_tx_arg);
358*0Sstevel@tonic-gate 
359*0Sstevel@tonic-gate 	/*
360*0Sstevel@tonic-gate 	 * Set up packet header constructor and parser functions. (We currently
361*0Sstevel@tonic-gate 	 * only support ethernet).
362*0Sstevel@tonic-gate 	 */
363*0Sstevel@tonic-gate 	ASSERT(dip->di_mip->mi_media == DL_ETHER);
364*0Sstevel@tonic-gate 	dip->di_header = i_dls_ether_header;
365*0Sstevel@tonic-gate 	dip->di_header_info = i_dls_ether_header_info;
366*0Sstevel@tonic-gate 
367*0Sstevel@tonic-gate 	/*
368*0Sstevel@tonic-gate 	 * Add a notification function so that we get updates from the MAC.
369*0Sstevel@tonic-gate 	 */
370*0Sstevel@tonic-gate 	dip->di_mnh = mac_notify_add(dip->di_mh, i_dls_notify, (void *)dip);
371*0Sstevel@tonic-gate 
372*0Sstevel@tonic-gate 	/*
373*0Sstevel@tonic-gate 	 * Bump the kmem_cache count to make sure it is not prematurely
374*0Sstevel@tonic-gate 	 * destroyed.
375*0Sstevel@tonic-gate 	 */
376*0Sstevel@tonic-gate 	atomic_add_32(&i_dls_impl_count, 1);
377*0Sstevel@tonic-gate 
378*0Sstevel@tonic-gate 	/*
379*0Sstevel@tonic-gate 	 * Hand back a reference to the dls_impl_t.
380*0Sstevel@tonic-gate 	 */
381*0Sstevel@tonic-gate 	*dcp = (dls_channel_t)dip;
382*0Sstevel@tonic-gate 	return (0);
383*0Sstevel@tonic-gate }
384*0Sstevel@tonic-gate 
385*0Sstevel@tonic-gate void
386*0Sstevel@tonic-gate dls_close(dls_channel_t dc)
387*0Sstevel@tonic-gate {
388*0Sstevel@tonic-gate 	dls_impl_t		*dip = (dls_impl_t *)dc;
389*0Sstevel@tonic-gate 	dls_vlan_t		*dvp;
390*0Sstevel@tonic-gate 	dls_link_t		*dlp;
391*0Sstevel@tonic-gate 	dls_multicst_addr_t	*p;
392*0Sstevel@tonic-gate 	dls_multicst_addr_t	*nextp;
393*0Sstevel@tonic-gate 
394*0Sstevel@tonic-gate 	dls_active_clear(dc);
395*0Sstevel@tonic-gate 
396*0Sstevel@tonic-gate 	rw_enter(&(dip->di_lock), RW_WRITER);
397*0Sstevel@tonic-gate 
398*0Sstevel@tonic-gate 	/*
399*0Sstevel@tonic-gate 	 * Remove the notify function.
400*0Sstevel@tonic-gate 	 */
401*0Sstevel@tonic-gate 	mac_notify_remove(dip->di_mh, dip->di_mnh);
402*0Sstevel@tonic-gate 	dip->di_mnh = NULL;
403*0Sstevel@tonic-gate 
404*0Sstevel@tonic-gate 	/*
405*0Sstevel@tonic-gate 	 * If the dls_impl_t is bound then unbind it.
406*0Sstevel@tonic-gate 	 */
407*0Sstevel@tonic-gate 	dvp = dip->di_dvp;
408*0Sstevel@tonic-gate 	dlp = dvp->dv_dlp;
409*0Sstevel@tonic-gate 
410*0Sstevel@tonic-gate 	if (dip->di_bound) {
411*0Sstevel@tonic-gate 		rw_exit(&(dip->di_lock));
412*0Sstevel@tonic-gate 		dls_link_remove(dlp, dip);
413*0Sstevel@tonic-gate 		rw_enter(&(dip->di_lock), RW_WRITER);
414*0Sstevel@tonic-gate 		dip->di_rx = NULL;
415*0Sstevel@tonic-gate 		dip->di_rx_arg = NULL;
416*0Sstevel@tonic-gate 		dip->di_bound = B_FALSE;
417*0Sstevel@tonic-gate 	}
418*0Sstevel@tonic-gate 
419*0Sstevel@tonic-gate 	/*
420*0Sstevel@tonic-gate 	 * Walk the list of multicast addresses, disabling each at the MAC.
421*0Sstevel@tonic-gate 	 */
422*0Sstevel@tonic-gate 	for (p = dip->di_dmap; p != NULL; p = nextp) {
423*0Sstevel@tonic-gate 		(void) mac_multicst_remove(dip->di_mh, p->dma_addr);
424*0Sstevel@tonic-gate 		nextp = p->dma_nextp;
425*0Sstevel@tonic-gate 		kmem_free(p, sizeof (dls_multicst_addr_t));
426*0Sstevel@tonic-gate 	}
427*0Sstevel@tonic-gate 	dip->di_dmap = NULL;
428*0Sstevel@tonic-gate 
429*0Sstevel@tonic-gate 	rw_exit(&(dip->di_lock));
430*0Sstevel@tonic-gate 
431*0Sstevel@tonic-gate 	/*
432*0Sstevel@tonic-gate 	 * If the MAC has been set in promiscuous mode then disable it.
433*0Sstevel@tonic-gate 	 */
434*0Sstevel@tonic-gate 	(void) dls_promisc(dc, 0);
435*0Sstevel@tonic-gate 
436*0Sstevel@tonic-gate 	/*
437*0Sstevel@tonic-gate 	 * Free the dls_impl_t back to the cache.
438*0Sstevel@tonic-gate 	 */
439*0Sstevel@tonic-gate 	dip->di_dvp = NULL;
440*0Sstevel@tonic-gate 	dip->di_tx = NULL;
441*0Sstevel@tonic-gate 	dip->di_tx_arg = NULL;
442*0Sstevel@tonic-gate 	kmem_cache_free(i_dls_impl_cachep, dip);
443*0Sstevel@tonic-gate 
444*0Sstevel@tonic-gate 	/*
445*0Sstevel@tonic-gate 	 * Decrement the reference count to allow the cache to be destroyed
446*0Sstevel@tonic-gate 	 * if there are no more dls_impl_t.
447*0Sstevel@tonic-gate 	 */
448*0Sstevel@tonic-gate 	atomic_add_32(&i_dls_impl_count, -1);
449*0Sstevel@tonic-gate 
450*0Sstevel@tonic-gate 	/*
451*0Sstevel@tonic-gate 	 * Release our reference to the dls_vlan_t allowing that to be
452*0Sstevel@tonic-gate 	 * destroyed if there are no more dls_impl_t.
453*0Sstevel@tonic-gate 	 */
454*0Sstevel@tonic-gate 	dls_vlan_rele(dvp);
455*0Sstevel@tonic-gate }
456*0Sstevel@tonic-gate 
457*0Sstevel@tonic-gate mac_handle_t
458*0Sstevel@tonic-gate dls_mac(dls_channel_t dc)
459*0Sstevel@tonic-gate {
460*0Sstevel@tonic-gate 	dls_impl_t	*dip = (dls_impl_t *)dc;
461*0Sstevel@tonic-gate 
462*0Sstevel@tonic-gate 	return (dip->di_mh);
463*0Sstevel@tonic-gate }
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate uint16_t
466*0Sstevel@tonic-gate dls_vid(dls_channel_t dc)
467*0Sstevel@tonic-gate {
468*0Sstevel@tonic-gate 	dls_impl_t	*dip = (dls_impl_t *)dc;
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate 	return (dip->di_dvp->dv_id);
471*0Sstevel@tonic-gate }
472*0Sstevel@tonic-gate 
473*0Sstevel@tonic-gate int
474*0Sstevel@tonic-gate dls_bind(dls_channel_t dc, uint16_t sap)
475*0Sstevel@tonic-gate {
476*0Sstevel@tonic-gate 	dls_impl_t	*dip = (dls_impl_t *)dc;
477*0Sstevel@tonic-gate 	dls_link_t	*dlp;
478*0Sstevel@tonic-gate 
479*0Sstevel@tonic-gate 	/*
480*0Sstevel@tonic-gate 	 * Check to see the value is legal for the media type.
481*0Sstevel@tonic-gate 	 */
482*0Sstevel@tonic-gate 	if (!SAP_LEGAL(dip->di_mip->mi_media, sap))
483*0Sstevel@tonic-gate 		return (EINVAL);
484*0Sstevel@tonic-gate 
485*0Sstevel@tonic-gate 	/*
486*0Sstevel@tonic-gate 	 * Set up the dls_impl_t to mark it as able to receive packets.
487*0Sstevel@tonic-gate 	 */
488*0Sstevel@tonic-gate 	rw_enter(&(dip->di_lock), RW_WRITER);
489*0Sstevel@tonic-gate 	ASSERT(!dip->di_bound);
490*0Sstevel@tonic-gate 	dip->di_sap = sap;
491*0Sstevel@tonic-gate 	dip->di_bound = B_TRUE;
492*0Sstevel@tonic-gate 	rw_exit(&(dip->di_lock));
493*0Sstevel@tonic-gate 
494*0Sstevel@tonic-gate 	/*
495*0Sstevel@tonic-gate 	 * Now bind the dls_impl_t by adding it into the hash table in the
496*0Sstevel@tonic-gate 	 * dls_link_t.
497*0Sstevel@tonic-gate 	 *
498*0Sstevel@tonic-gate 	 * NOTE: This must be done without the dls_impl_t lock being held
499*0Sstevel@tonic-gate 	 *	 otherwise deadlock may ensue.
500*0Sstevel@tonic-gate 	 */
501*0Sstevel@tonic-gate 	dlp = dip->di_dvp->dv_dlp;
502*0Sstevel@tonic-gate 	dls_link_add(dlp,
503*0Sstevel@tonic-gate 	    (dip->di_promisc & DLS_PROMISC_SAP) ? DLS_SAP_PROMISC :
504*0Sstevel@tonic-gate 	    (uint32_t)sap, dip);
505*0Sstevel@tonic-gate 
506*0Sstevel@tonic-gate 	return (0);
507*0Sstevel@tonic-gate }
508*0Sstevel@tonic-gate 
509*0Sstevel@tonic-gate void
510*0Sstevel@tonic-gate dls_unbind(dls_channel_t dc)
511*0Sstevel@tonic-gate {
512*0Sstevel@tonic-gate 	dls_impl_t	*dip = (dls_impl_t *)dc;
513*0Sstevel@tonic-gate 	dls_link_t	*dlp;
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 	/*
516*0Sstevel@tonic-gate 	 * Unbind the dls_impl_t by removing it from the hash table in the
517*0Sstevel@tonic-gate 	 * dls_link_t.
518*0Sstevel@tonic-gate 	 *
519*0Sstevel@tonic-gate 	 * NOTE: This must be done without the dls_impl_t lock being held
520*0Sstevel@tonic-gate 	 *	 otherise deadlock may enuse.
521*0Sstevel@tonic-gate 	 */
522*0Sstevel@tonic-gate 	dlp = dip->di_dvp->dv_dlp;
523*0Sstevel@tonic-gate 	dls_link_remove(dlp, dip);
524*0Sstevel@tonic-gate 
525*0Sstevel@tonic-gate 	/*
526*0Sstevel@tonic-gate 	 * Mark the dls_impl_t as unable to receive packets This will make
527*0Sstevel@tonic-gate 	 * sure that 'receives in flight' will not come our way.
528*0Sstevel@tonic-gate 	 */
529*0Sstevel@tonic-gate 	dip->di_bound = B_FALSE;
530*0Sstevel@tonic-gate }
531*0Sstevel@tonic-gate 
532*0Sstevel@tonic-gate int
533*0Sstevel@tonic-gate dls_promisc(dls_channel_t dc, uint32_t flags)
534*0Sstevel@tonic-gate {
535*0Sstevel@tonic-gate 	dls_impl_t	*dip = (dls_impl_t *)dc;
536*0Sstevel@tonic-gate 	dls_link_t	*dlp;
537*0Sstevel@tonic-gate 	int		err = 0;
538*0Sstevel@tonic-gate 
539*0Sstevel@tonic-gate 	ASSERT(!(flags & ~(DLS_PROMISC_SAP | DLS_PROMISC_MULTI |
540*0Sstevel@tonic-gate 	    DLS_PROMISC_PHYS)));
541*0Sstevel@tonic-gate 
542*0Sstevel@tonic-gate 	/*
543*0Sstevel@tonic-gate 	 * Check if we need to turn on 'all sap' mode.
544*0Sstevel@tonic-gate 	 */
545*0Sstevel@tonic-gate 	rw_enter(&(dip->di_lock), RW_WRITER);
546*0Sstevel@tonic-gate 	dlp = dip->di_dvp->dv_dlp;
547*0Sstevel@tonic-gate 	if ((flags & DLS_PROMISC_SAP) &&
548*0Sstevel@tonic-gate 	    !(dip->di_promisc & DLS_PROMISC_SAP)) {
549*0Sstevel@tonic-gate 		dip->di_promisc |= DLS_PROMISC_SAP;
550*0Sstevel@tonic-gate 		if (!dip->di_bound)
551*0Sstevel@tonic-gate 			goto multi;
552*0Sstevel@tonic-gate 
553*0Sstevel@tonic-gate 		rw_exit(&(dip->di_lock));
554*0Sstevel@tonic-gate 		dls_link_remove(dlp, dip);
555*0Sstevel@tonic-gate 		dls_link_add(dlp, DLS_SAP_PROMISC, dip);
556*0Sstevel@tonic-gate 		rw_enter(&(dip->di_lock), RW_WRITER);
557*0Sstevel@tonic-gate 		goto multi;
558*0Sstevel@tonic-gate 	}
559*0Sstevel@tonic-gate 
560*0Sstevel@tonic-gate 	/*
561*0Sstevel@tonic-gate 	 * Check if we need to turn off 'all sap' mode.
562*0Sstevel@tonic-gate 	 */
563*0Sstevel@tonic-gate 	if (!(flags & DLS_PROMISC_SAP) &&
564*0Sstevel@tonic-gate 	    (dip->di_promisc & DLS_PROMISC_SAP)) {
565*0Sstevel@tonic-gate 		dip->di_promisc &= ~DLS_PROMISC_SAP;
566*0Sstevel@tonic-gate 		if (!dip->di_bound)
567*0Sstevel@tonic-gate 			goto multi;
568*0Sstevel@tonic-gate 
569*0Sstevel@tonic-gate 		rw_exit(&(dip->di_lock));
570*0Sstevel@tonic-gate 		dls_link_remove(dlp, dip);
571*0Sstevel@tonic-gate 		dls_link_add(dlp, dip->di_sap, dip);
572*0Sstevel@tonic-gate 		rw_enter(&(dip->di_lock), RW_WRITER);
573*0Sstevel@tonic-gate 	}
574*0Sstevel@tonic-gate 
575*0Sstevel@tonic-gate multi:
576*0Sstevel@tonic-gate 	/*
577*0Sstevel@tonic-gate 	 * It's easiest to add the txloop handler up-front; if promiscuous
578*0Sstevel@tonic-gate 	 * mode cannot be enabled, then we'll remove it before returning.
579*0Sstevel@tonic-gate 	 */
580*0Sstevel@tonic-gate 	if (dlp->dl_npromisc == 0 &&
581*0Sstevel@tonic-gate 	    (flags & (DLS_PROMISC_MULTI|DLS_PROMISC_PHYS))) {
582*0Sstevel@tonic-gate 		ASSERT(dlp->dl_mth == NULL);
583*0Sstevel@tonic-gate 		dlp->dl_mth = mac_txloop_add(dlp->dl_mh, dlp->dl_loopback, dlp);
584*0Sstevel@tonic-gate 	}
585*0Sstevel@tonic-gate 
586*0Sstevel@tonic-gate 	/*
587*0Sstevel@tonic-gate 	 * Check if we need to turn on 'all multicast' mode.
588*0Sstevel@tonic-gate 	 */
589*0Sstevel@tonic-gate 	if ((flags & DLS_PROMISC_MULTI) &&
590*0Sstevel@tonic-gate 	    !(dip->di_promisc & DLS_PROMISC_MULTI)) {
591*0Sstevel@tonic-gate 		err = mac_promisc_set(dip->di_mh, B_TRUE, MAC_PROMISC);
592*0Sstevel@tonic-gate 		if (err != 0)
593*0Sstevel@tonic-gate 			goto done;
594*0Sstevel@tonic-gate 		dip->di_promisc |= DLS_PROMISC_MULTI;
595*0Sstevel@tonic-gate 		dlp->dl_npromisc++;
596*0Sstevel@tonic-gate 		goto phys;
597*0Sstevel@tonic-gate 	}
598*0Sstevel@tonic-gate 
599*0Sstevel@tonic-gate 	/*
600*0Sstevel@tonic-gate 	 * Check if we need to turn off 'all multicast' mode.
601*0Sstevel@tonic-gate 	 */
602*0Sstevel@tonic-gate 	if (!(flags & DLS_PROMISC_MULTI) &&
603*0Sstevel@tonic-gate 	    (dip->di_promisc & DLS_PROMISC_MULTI)) {
604*0Sstevel@tonic-gate 		err = mac_promisc_set(dip->di_mh, B_FALSE, MAC_PROMISC);
605*0Sstevel@tonic-gate 		if (err != 0)
606*0Sstevel@tonic-gate 			goto done;
607*0Sstevel@tonic-gate 		dip->di_promisc &= ~DLS_PROMISC_MULTI;
608*0Sstevel@tonic-gate 		dlp->dl_npromisc--;
609*0Sstevel@tonic-gate 	}
610*0Sstevel@tonic-gate 
611*0Sstevel@tonic-gate phys:
612*0Sstevel@tonic-gate 	/*
613*0Sstevel@tonic-gate 	 * Check if we need to turn on 'all physical' mode.
614*0Sstevel@tonic-gate 	 */
615*0Sstevel@tonic-gate 	if ((flags & DLS_PROMISC_PHYS) &&
616*0Sstevel@tonic-gate 	    !(dip->di_promisc & DLS_PROMISC_PHYS)) {
617*0Sstevel@tonic-gate 		err = mac_promisc_set(dip->di_mh, B_TRUE, MAC_PROMISC);
618*0Sstevel@tonic-gate 		if (err != 0)
619*0Sstevel@tonic-gate 			goto done;
620*0Sstevel@tonic-gate 		dip->di_promisc |= DLS_PROMISC_PHYS;
621*0Sstevel@tonic-gate 		dlp->dl_npromisc++;
622*0Sstevel@tonic-gate 		goto done;
623*0Sstevel@tonic-gate 	}
624*0Sstevel@tonic-gate 
625*0Sstevel@tonic-gate 	/*
626*0Sstevel@tonic-gate 	 * Check if we need to turn off 'all physical' mode.
627*0Sstevel@tonic-gate 	 */
628*0Sstevel@tonic-gate 	if (!(flags & DLS_PROMISC_PHYS) &&
629*0Sstevel@tonic-gate 	    (dip->di_promisc & DLS_PROMISC_PHYS)) {
630*0Sstevel@tonic-gate 		err = mac_promisc_set(dip->di_mh, B_FALSE, MAC_PROMISC);
631*0Sstevel@tonic-gate 		if (err != 0)
632*0Sstevel@tonic-gate 			goto done;
633*0Sstevel@tonic-gate 		dip->di_promisc &= ~DLS_PROMISC_PHYS;
634*0Sstevel@tonic-gate 		dlp->dl_npromisc--;
635*0Sstevel@tonic-gate 	}
636*0Sstevel@tonic-gate 
637*0Sstevel@tonic-gate done:
638*0Sstevel@tonic-gate 	if (dlp->dl_npromisc == 0 && dlp->dl_mth != NULL) {
639*0Sstevel@tonic-gate 		mac_txloop_remove(dlp->dl_mh, dlp->dl_mth);
640*0Sstevel@tonic-gate 		dlp->dl_mth = NULL;
641*0Sstevel@tonic-gate 	}
642*0Sstevel@tonic-gate 
643*0Sstevel@tonic-gate 	ASSERT(dlp->dl_npromisc == 0 || dlp->dl_mth != NULL);
644*0Sstevel@tonic-gate 
645*0Sstevel@tonic-gate 	rw_exit(&(dip->di_lock));
646*0Sstevel@tonic-gate 	return (err);
647*0Sstevel@tonic-gate }
648*0Sstevel@tonic-gate 
649*0Sstevel@tonic-gate int
650*0Sstevel@tonic-gate dls_multicst_add(dls_channel_t dc, const uint8_t *addr)
651*0Sstevel@tonic-gate {
652*0Sstevel@tonic-gate 	dls_impl_t		*dip = (dls_impl_t *)dc;
653*0Sstevel@tonic-gate 	int			err;
654*0Sstevel@tonic-gate 	dls_multicst_addr_t	**pp;
655*0Sstevel@tonic-gate 	dls_multicst_addr_t	*p;
656*0Sstevel@tonic-gate 	uint_t			addr_length;
657*0Sstevel@tonic-gate 
658*0Sstevel@tonic-gate 	/*
659*0Sstevel@tonic-gate 	 * Check whether the address is in the list of enabled addresses for
660*0Sstevel@tonic-gate 	 * this dls_impl_t.
661*0Sstevel@tonic-gate 	 */
662*0Sstevel@tonic-gate 	rw_enter(&(dip->di_lock), RW_WRITER);
663*0Sstevel@tonic-gate 	addr_length = dip->di_mip->mi_addr_length;
664*0Sstevel@tonic-gate 	for (pp = &(dip->di_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
665*0Sstevel@tonic-gate 		if (bcmp(addr, p->dma_addr, addr_length) == 0) {
666*0Sstevel@tonic-gate 			/*
667*0Sstevel@tonic-gate 			 * It is there so there's nothing to do.
668*0Sstevel@tonic-gate 			 */
669*0Sstevel@tonic-gate 			err = 0;
670*0Sstevel@tonic-gate 			goto done;
671*0Sstevel@tonic-gate 		}
672*0Sstevel@tonic-gate 	}
673*0Sstevel@tonic-gate 
674*0Sstevel@tonic-gate 	/*
675*0Sstevel@tonic-gate 	 * Allocate a new list item.
676*0Sstevel@tonic-gate 	 */
677*0Sstevel@tonic-gate 	if ((p = kmem_zalloc(sizeof (dls_multicst_addr_t),
678*0Sstevel@tonic-gate 	    KM_NOSLEEP)) == NULL) {
679*0Sstevel@tonic-gate 		err = ENOMEM;
680*0Sstevel@tonic-gate 		goto done;
681*0Sstevel@tonic-gate 	}
682*0Sstevel@tonic-gate 
683*0Sstevel@tonic-gate 	/*
684*0Sstevel@tonic-gate 	 * Enable the address at the MAC.
685*0Sstevel@tonic-gate 	 */
686*0Sstevel@tonic-gate 	if ((err = mac_multicst_add(dip->di_mh, addr)) != 0) {
687*0Sstevel@tonic-gate 		kmem_free(p, sizeof (dls_multicst_addr_t));
688*0Sstevel@tonic-gate 		goto done;
689*0Sstevel@tonic-gate 	}
690*0Sstevel@tonic-gate 
691*0Sstevel@tonic-gate 	/*
692*0Sstevel@tonic-gate 	 * The address is now enabled at the MAC so add it to the list.
693*0Sstevel@tonic-gate 	 */
694*0Sstevel@tonic-gate 	bcopy(addr, p->dma_addr, addr_length);
695*0Sstevel@tonic-gate 	*pp = p;
696*0Sstevel@tonic-gate 
697*0Sstevel@tonic-gate done:
698*0Sstevel@tonic-gate 	rw_exit(&(dip->di_lock));
699*0Sstevel@tonic-gate 	return (err);
700*0Sstevel@tonic-gate }
701*0Sstevel@tonic-gate 
702*0Sstevel@tonic-gate int
703*0Sstevel@tonic-gate dls_multicst_remove(dls_channel_t dc, const uint8_t *addr)
704*0Sstevel@tonic-gate {
705*0Sstevel@tonic-gate 	dls_impl_t		*dip = (dls_impl_t *)dc;
706*0Sstevel@tonic-gate 	int			err;
707*0Sstevel@tonic-gate 	dls_multicst_addr_t	**pp;
708*0Sstevel@tonic-gate 	dls_multicst_addr_t	*p;
709*0Sstevel@tonic-gate 	uint_t			addr_length;
710*0Sstevel@tonic-gate 
711*0Sstevel@tonic-gate 	/*
712*0Sstevel@tonic-gate 	 * Find the address in the list of enabled addresses for this
713*0Sstevel@tonic-gate 	 * dls_impl_t.
714*0Sstevel@tonic-gate 	 */
715*0Sstevel@tonic-gate 	rw_enter(&(dip->di_lock), RW_WRITER);
716*0Sstevel@tonic-gate 	addr_length = dip->di_mip->mi_addr_length;
717*0Sstevel@tonic-gate 	for (pp = &(dip->di_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
718*0Sstevel@tonic-gate 		if (bcmp(addr, p->dma_addr, addr_length) == 0)
719*0Sstevel@tonic-gate 			break;
720*0Sstevel@tonic-gate 	}
721*0Sstevel@tonic-gate 
722*0Sstevel@tonic-gate 	/*
723*0Sstevel@tonic-gate 	 * If we walked to the end of the list then the given address is
724*0Sstevel@tonic-gate 	 * not currently enabled for this dls_impl_t.
725*0Sstevel@tonic-gate 	 */
726*0Sstevel@tonic-gate 	if (p == NULL) {
727*0Sstevel@tonic-gate 		err = ENOENT;
728*0Sstevel@tonic-gate 		goto done;
729*0Sstevel@tonic-gate 	}
730*0Sstevel@tonic-gate 
731*0Sstevel@tonic-gate 	/*
732*0Sstevel@tonic-gate 	 * Disable the address at the MAC.
733*0Sstevel@tonic-gate 	 */
734*0Sstevel@tonic-gate 	if ((err = mac_multicst_remove(dip->di_mh, addr)) != 0)
735*0Sstevel@tonic-gate 		goto done;
736*0Sstevel@tonic-gate 
737*0Sstevel@tonic-gate 	/*
738*0Sstevel@tonic-gate 	 * Remove the address from the list.
739*0Sstevel@tonic-gate 	 */
740*0Sstevel@tonic-gate 	*pp = p->dma_nextp;
741*0Sstevel@tonic-gate 	kmem_free(p, sizeof (dls_multicst_addr_t));
742*0Sstevel@tonic-gate 
743*0Sstevel@tonic-gate done:
744*0Sstevel@tonic-gate 	rw_exit(&(dip->di_lock));
745*0Sstevel@tonic-gate 	return (err);
746*0Sstevel@tonic-gate }
747*0Sstevel@tonic-gate 
748*0Sstevel@tonic-gate mblk_t *
749*0Sstevel@tonic-gate dls_header(dls_channel_t dc, const uint8_t *addr, uint16_t sap, uint_t pri)
750*0Sstevel@tonic-gate {
751*0Sstevel@tonic-gate 	dls_impl_t	*dip = (dls_impl_t *)dc;
752*0Sstevel@tonic-gate 
753*0Sstevel@tonic-gate 	return (dip->di_header(dip, addr, sap, pri));
754*0Sstevel@tonic-gate }
755*0Sstevel@tonic-gate 
756*0Sstevel@tonic-gate void
757*0Sstevel@tonic-gate dls_header_info(dls_channel_t dc, mblk_t *mp, dls_header_info_t *dhip)
758*0Sstevel@tonic-gate {
759*0Sstevel@tonic-gate 	dls_impl_t	*dip = (dls_impl_t *)dc;
760*0Sstevel@tonic-gate 
761*0Sstevel@tonic-gate 	dip->di_header_info(dip, mp, dhip);
762*0Sstevel@tonic-gate }
763*0Sstevel@tonic-gate 
764*0Sstevel@tonic-gate void
765*0Sstevel@tonic-gate dls_rx_set(dls_channel_t dc, dls_rx_t rx, void *arg)
766*0Sstevel@tonic-gate {
767*0Sstevel@tonic-gate 	dls_impl_t	*dip = (dls_impl_t *)dc;
768*0Sstevel@tonic-gate 
769*0Sstevel@tonic-gate 	rw_enter(&(dip->di_lock), RW_WRITER);
770*0Sstevel@tonic-gate 	dip->di_rx = rx;
771*0Sstevel@tonic-gate 	dip->di_rx_arg = arg;
772*0Sstevel@tonic-gate 	rw_exit(&(dip->di_lock));
773*0Sstevel@tonic-gate }
774*0Sstevel@tonic-gate 
775*0Sstevel@tonic-gate mblk_t *
776*0Sstevel@tonic-gate dls_tx(dls_channel_t dc, mblk_t *mp)
777*0Sstevel@tonic-gate {
778*0Sstevel@tonic-gate 	dls_impl_t	*dip = (dls_impl_t *)dc;
779*0Sstevel@tonic-gate 
780*0Sstevel@tonic-gate 	return (dip->di_tx(dip->di_tx_arg, mp));
781*0Sstevel@tonic-gate }
782*0Sstevel@tonic-gate 
783*0Sstevel@tonic-gate /*
784*0Sstevel@tonic-gate  * Exported functions.
785*0Sstevel@tonic-gate  */
786*0Sstevel@tonic-gate 
787*0Sstevel@tonic-gate #define	ADDR_MATCH(_addr_a, _addr_b, _length, _match)			\
788*0Sstevel@tonic-gate 	{								\
789*0Sstevel@tonic-gate 		uint_t	i;						\
790*0Sstevel@tonic-gate 									\
791*0Sstevel@tonic-gate 		/*							\
792*0Sstevel@tonic-gate 		 * Make sure the addresses are 16 bit aligned and that	\
793*0Sstevel@tonic-gate 		 * the length is an even number of octets.		\
794*0Sstevel@tonic-gate 		 */							\
795*0Sstevel@tonic-gate 		ASSERT(IS_P2ALIGNED((_addr_a), sizeof (uint16_t)));	\
796*0Sstevel@tonic-gate 		ASSERT(IS_P2ALIGNED((_addr_b), sizeof (uint16_t)));	\
797*0Sstevel@tonic-gate 		ASSERT((_length & 1) == 0);				\
798*0Sstevel@tonic-gate 									\
799*0Sstevel@tonic-gate 		(_match) = B_TRUE;					\
800*0Sstevel@tonic-gate 		for (i = 0; i < (_length) >> 1; i++) {			\
801*0Sstevel@tonic-gate 			if (((uint16_t *)(_addr_a))[i] !=		\
802*0Sstevel@tonic-gate 			    ((uint16_t *)(_addr_b))[i]) {		\
803*0Sstevel@tonic-gate 				(_match) = B_FALSE;			\
804*0Sstevel@tonic-gate 				break;					\
805*0Sstevel@tonic-gate 			}						\
806*0Sstevel@tonic-gate 		}							\
807*0Sstevel@tonic-gate 	}
808*0Sstevel@tonic-gate 
809*0Sstevel@tonic-gate boolean_t
810*0Sstevel@tonic-gate dls_accept(dls_impl_t *dip, const uint8_t *daddr)
811*0Sstevel@tonic-gate {
812*0Sstevel@tonic-gate 	boolean_t		match;
813*0Sstevel@tonic-gate 	dls_multicst_addr_t	*dmap;
814*0Sstevel@tonic-gate 	uint_t			addr_length = dip->di_mip->mi_addr_length;
815*0Sstevel@tonic-gate 
816*0Sstevel@tonic-gate 	/*
817*0Sstevel@tonic-gate 	 * We must not accept packets if the dls_impl_t is not marked as bound
818*0Sstevel@tonic-gate 	 * or is being removed.
819*0Sstevel@tonic-gate 	 */
820*0Sstevel@tonic-gate 	rw_enter(&(dip->di_lock), RW_READER);
821*0Sstevel@tonic-gate 	if (!dip->di_bound || dip->di_removing)
822*0Sstevel@tonic-gate 		goto refuse;
823*0Sstevel@tonic-gate 
824*0Sstevel@tonic-gate 	/*
825*0Sstevel@tonic-gate 	 * If the dls_impl_t is in 'all physical' mode then always accept.
826*0Sstevel@tonic-gate 	 */
827*0Sstevel@tonic-gate 	if (dip->di_promisc & DLS_PROMISC_PHYS)
828*0Sstevel@tonic-gate 		goto accept;
829*0Sstevel@tonic-gate 
830*0Sstevel@tonic-gate 	/*
831*0Sstevel@tonic-gate 	 * Check to see if the destination address matches the dls_impl_t
832*0Sstevel@tonic-gate 	 * unicast address.
833*0Sstevel@tonic-gate 	 */
834*0Sstevel@tonic-gate 	ADDR_MATCH(daddr, dip->di_unicst_addr, addr_length, match);
835*0Sstevel@tonic-gate 	if (match)
836*0Sstevel@tonic-gate 		goto accept;
837*0Sstevel@tonic-gate 
838*0Sstevel@tonic-gate 	/*
839*0Sstevel@tonic-gate 	 * Check for a 'group' address. If it is not then refuse it since we
840*0Sstevel@tonic-gate 	 * already know it does not match the unicast address.
841*0Sstevel@tonic-gate 	 */
842*0Sstevel@tonic-gate 	if (!(daddr[0] & 0x01))
843*0Sstevel@tonic-gate 		goto refuse;
844*0Sstevel@tonic-gate 
845*0Sstevel@tonic-gate 	/*
846*0Sstevel@tonic-gate 	 * If the address is broadcast then the dls_impl_t will always accept
847*0Sstevel@tonic-gate 	 * it.
848*0Sstevel@tonic-gate 	 */
849*0Sstevel@tonic-gate 	ADDR_MATCH(daddr, dip->di_mip->mi_brdcst_addr, addr_length,
850*0Sstevel@tonic-gate 	    match);
851*0Sstevel@tonic-gate 	if (match)
852*0Sstevel@tonic-gate 		goto accept;
853*0Sstevel@tonic-gate 
854*0Sstevel@tonic-gate 	/*
855*0Sstevel@tonic-gate 	 * If a group address is not broadcast then it must be multicast so
856*0Sstevel@tonic-gate 	 * check it against the list of addresses enabled for this dls_impl_t
857*0Sstevel@tonic-gate 	 * or accept it unconditionally if the dls_impl_t is in 'all
858*0Sstevel@tonic-gate 	 * multicast' mode.
859*0Sstevel@tonic-gate 	 */
860*0Sstevel@tonic-gate 	if (dip->di_promisc & DLS_PROMISC_MULTI)
861*0Sstevel@tonic-gate 		goto accept;
862*0Sstevel@tonic-gate 
863*0Sstevel@tonic-gate 	for (dmap = dip->di_dmap; dmap != NULL; dmap = dmap->dma_nextp) {
864*0Sstevel@tonic-gate 		ADDR_MATCH(daddr, dmap->dma_addr, addr_length, match);
865*0Sstevel@tonic-gate 		if (match)
866*0Sstevel@tonic-gate 			goto accept;
867*0Sstevel@tonic-gate 	}
868*0Sstevel@tonic-gate 
869*0Sstevel@tonic-gate refuse:
870*0Sstevel@tonic-gate 	rw_exit(&(dip->di_lock));
871*0Sstevel@tonic-gate 	return (B_FALSE);
872*0Sstevel@tonic-gate 
873*0Sstevel@tonic-gate accept:
874*0Sstevel@tonic-gate 	rw_exit(&(dip->di_lock));
875*0Sstevel@tonic-gate 	return (B_TRUE);
876*0Sstevel@tonic-gate }
877*0Sstevel@tonic-gate 
878*0Sstevel@tonic-gate /*ARGSUSED*/
879*0Sstevel@tonic-gate boolean_t
880*0Sstevel@tonic-gate dls_accept_loopback(dls_impl_t *dip, const uint8_t *daddr)
881*0Sstevel@tonic-gate {
882*0Sstevel@tonic-gate 	/*
883*0Sstevel@tonic-gate 	 * We must not accept packets if the dls_impl_t is not marked as bound
884*0Sstevel@tonic-gate 	 * or is being removed.
885*0Sstevel@tonic-gate 	 */
886*0Sstevel@tonic-gate 	rw_enter(&(dip->di_lock), RW_READER);
887*0Sstevel@tonic-gate 	if (!dip->di_bound || dip->di_removing)
888*0Sstevel@tonic-gate 		goto refuse;
889*0Sstevel@tonic-gate 
890*0Sstevel@tonic-gate 	/*
891*0Sstevel@tonic-gate 	 * A dls_impl_t should only accept loopback packets if it is in
892*0Sstevel@tonic-gate 	 * 'all physical' mode.
893*0Sstevel@tonic-gate 	 */
894*0Sstevel@tonic-gate 	if (dip->di_promisc & DLS_PROMISC_PHYS)
895*0Sstevel@tonic-gate 		goto accept;
896*0Sstevel@tonic-gate 
897*0Sstevel@tonic-gate refuse:
898*0Sstevel@tonic-gate 	rw_exit(&(dip->di_lock));
899*0Sstevel@tonic-gate 	return (B_FALSE);
900*0Sstevel@tonic-gate 
901*0Sstevel@tonic-gate accept:
902*0Sstevel@tonic-gate 	rw_exit(&(dip->di_lock));
903*0Sstevel@tonic-gate 	return (B_TRUE);
904*0Sstevel@tonic-gate }
905*0Sstevel@tonic-gate 
906*0Sstevel@tonic-gate boolean_t
907*0Sstevel@tonic-gate dls_active_set(dls_channel_t dc)
908*0Sstevel@tonic-gate {
909*0Sstevel@tonic-gate 	dls_impl_t	*dip = (dls_impl_t *)dc;
910*0Sstevel@tonic-gate 	dls_link_t	*dlp = dip->di_dvp->dv_dlp;
911*0Sstevel@tonic-gate 
912*0Sstevel@tonic-gate 	rw_enter(&dip->di_lock, RW_WRITER);
913*0Sstevel@tonic-gate 
914*0Sstevel@tonic-gate 	/* If we're already active, then there's nothing more to do. */
915*0Sstevel@tonic-gate 	if (dip->di_active) {
916*0Sstevel@tonic-gate 		rw_exit(&dip->di_lock);
917*0Sstevel@tonic-gate 		return (B_TRUE);
918*0Sstevel@tonic-gate 	}
919*0Sstevel@tonic-gate 
920*0Sstevel@tonic-gate 	/*
921*0Sstevel@tonic-gate 	 * If this is the first active client on this link, notify
922*0Sstevel@tonic-gate 	 * the mac that we're becoming an active client.
923*0Sstevel@tonic-gate 	 */
924*0Sstevel@tonic-gate 	if (dlp->dl_nactive == 0 && !mac_active_set(dlp->dl_mh)) {
925*0Sstevel@tonic-gate 		rw_exit(&dip->di_lock);
926*0Sstevel@tonic-gate 		return (B_FALSE);
927*0Sstevel@tonic-gate 	}
928*0Sstevel@tonic-gate 	dip->di_active = B_TRUE;
929*0Sstevel@tonic-gate 	mutex_enter(&dlp->dl_lock);
930*0Sstevel@tonic-gate 	dlp->dl_nactive++;
931*0Sstevel@tonic-gate 	mutex_exit(&dlp->dl_lock);
932*0Sstevel@tonic-gate 	rw_exit(&dip->di_lock);
933*0Sstevel@tonic-gate 	return (B_TRUE);
934*0Sstevel@tonic-gate }
935*0Sstevel@tonic-gate 
936*0Sstevel@tonic-gate void
937*0Sstevel@tonic-gate dls_active_clear(dls_channel_t dc)
938*0Sstevel@tonic-gate {
939*0Sstevel@tonic-gate 	dls_impl_t	*dip = (dls_impl_t *)dc;
940*0Sstevel@tonic-gate 	dls_link_t	*dlp = dip->di_dvp->dv_dlp;
941*0Sstevel@tonic-gate 
942*0Sstevel@tonic-gate 	rw_enter(&dip->di_lock, RW_WRITER);
943*0Sstevel@tonic-gate 
944*0Sstevel@tonic-gate 	if (!dip->di_active)
945*0Sstevel@tonic-gate 		goto out;
946*0Sstevel@tonic-gate 	dip->di_active = B_FALSE;
947*0Sstevel@tonic-gate 
948*0Sstevel@tonic-gate 	mutex_enter(&dlp->dl_lock);
949*0Sstevel@tonic-gate 	if (--dlp->dl_nactive == 0)
950*0Sstevel@tonic-gate 		mac_active_clear(dip->di_mh);
951*0Sstevel@tonic-gate 	mutex_exit(&dlp->dl_lock);
952*0Sstevel@tonic-gate out:
953*0Sstevel@tonic-gate 	rw_exit(&dip->di_lock);
954*0Sstevel@tonic-gate }
955