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