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