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