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