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