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