xref: /illumos-gate/usr/src/uts/common/io/dls/dls.c (revision 843e19887f64dde75055cf8842fc4db2171eff45)
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 	if (dip->di_soft_ring_list != NULL) {
312 		soft_ring_set_destroy(dip->di_soft_ring_list,
313 		    dip->di_soft_ring_size);
314 		dip->di_soft_ring_list = NULL;
315 	}
316 	dip->di_soft_ring_size = 0;
317 
318 	kmem_cache_free(i_dls_impl_cachep, dip);
319 
320 	/*
321 	 * Decrement the reference count to allow the cache to be destroyed
322 	 * if there are no more dls_impl_t.
323 	 */
324 	atomic_add_32(&i_dls_impl_count, -1);
325 
326 	/*
327 	 * Release our reference to the dls_vlan_t allowing that to be
328 	 * destroyed if there are no more dls_impl_t. An unreferenced tagged
329 	 * vlan gets destroyed automatically.
330 	 */
331 	dls_vlan_rele(dvp);
332 }
333 
334 mac_handle_t
335 dls_mac(dls_channel_t dc)
336 {
337 	return (((dls_impl_t *)dc)->di_mh);
338 }
339 
340 uint16_t
341 dls_vid(dls_channel_t dc)
342 {
343 	return (((dls_impl_t *)dc)->di_dvp->dv_id);
344 }
345 
346 int
347 dls_bind(dls_channel_t dc, uint32_t sap)
348 {
349 	dls_impl_t	*dip = (dls_impl_t *)dc;
350 	dls_link_t	*dlp;
351 	uint32_t	dls_sap;
352 
353 	/*
354 	 * Check to see the value is legal for the media type.
355 	 */
356 	if (!mac_sap_verify(dip->di_mh, sap, &dls_sap))
357 		return (EINVAL);
358 	if (dip->di_promisc & DLS_PROMISC_SAP)
359 		dls_sap = DLS_SAP_PROMISC;
360 
361 	/*
362 	 * Set up the dls_impl_t to mark it as able to receive packets.
363 	 */
364 	rw_enter(&(dip->di_lock), RW_WRITER);
365 	ASSERT(!dip->di_bound);
366 	dip->di_sap = sap;
367 	dip->di_bound = B_TRUE;
368 	rw_exit(&(dip->di_lock));
369 
370 	/*
371 	 * Now bind the dls_impl_t by adding it into the hash table in the
372 	 * dls_link_t.
373 	 *
374 	 * NOTE: This must be done without the dls_impl_t lock being held
375 	 *	 otherwise deadlock may ensue.
376 	 */
377 	dlp = dip->di_dvp->dv_dlp;
378 	dls_link_add(dlp, dls_sap, dip);
379 
380 	return (0);
381 }
382 
383 void
384 dls_unbind(dls_channel_t dc)
385 {
386 	dls_impl_t	*dip = (dls_impl_t *)dc;
387 	dls_link_t	*dlp;
388 
389 	/*
390 	 * Unbind the dls_impl_t by removing it from the hash table in the
391 	 * dls_link_t.
392 	 *
393 	 * NOTE: This must be done without the dls_impl_t lock being held
394 	 *	 otherise deadlock may enuse.
395 	 */
396 	dlp = dip->di_dvp->dv_dlp;
397 	dls_link_remove(dlp, dip);
398 
399 	/*
400 	 * Mark the dls_impl_t as unable to receive packets This will make
401 	 * sure that 'receives in flight' will not come our way.
402 	 */
403 	dip->di_bound = B_FALSE;
404 }
405 
406 int
407 dls_promisc(dls_channel_t dc, uint32_t flags)
408 {
409 	dls_impl_t	*dip = (dls_impl_t *)dc;
410 	dls_link_t	*dlp;
411 	int		err = 0;
412 
413 	ASSERT(!(flags & ~(DLS_PROMISC_SAP | DLS_PROMISC_MULTI |
414 	    DLS_PROMISC_PHYS)));
415 
416 	/*
417 	 * Check if we need to turn on 'all sap' mode.
418 	 */
419 	rw_enter(&(dip->di_lock), RW_WRITER);
420 	dlp = dip->di_dvp->dv_dlp;
421 	if ((flags & DLS_PROMISC_SAP) &&
422 	    !(dip->di_promisc & DLS_PROMISC_SAP)) {
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 		dls_link_add(dlp, DLS_SAP_PROMISC, dip);
430 		rw_enter(&(dip->di_lock), RW_WRITER);
431 		goto multi;
432 	}
433 
434 	/*
435 	 * Check if we need to turn off 'all sap' mode.
436 	 */
437 	if (!(flags & DLS_PROMISC_SAP) &&
438 	    (dip->di_promisc & DLS_PROMISC_SAP)) {
439 		uint32_t dls_sap;
440 
441 		dip->di_promisc &= ~DLS_PROMISC_SAP;
442 		if (!dip->di_bound)
443 			goto multi;
444 
445 		rw_exit(&(dip->di_lock));
446 		dls_link_remove(dlp, dip);
447 		(void) mac_sap_verify(dip->di_mh, dip->di_sap, &dls_sap);
448 		dls_link_add(dlp, dls_sap, dip);
449 		rw_enter(&(dip->di_lock), RW_WRITER);
450 	}
451 
452 multi:
453 	/*
454 	 * It's easiest to add the txloop handler up-front; if promiscuous
455 	 * mode cannot be enabled, then we'll remove it before returning.
456 	 * Use dl_promisc_lock to prevent racing with another thread also
457 	 * manipulating the promiscuous state on another dls_impl_t associated
458 	 * with the same dls_link_t.
459 	 */
460 	mutex_enter(&dlp->dl_promisc_lock);
461 	if (dlp->dl_npromisc == 0 &&
462 	    (flags & (DLS_PROMISC_MULTI|DLS_PROMISC_PHYS))) {
463 		ASSERT(dlp->dl_mth == NULL);
464 		dlp->dl_mth = mac_txloop_add(dlp->dl_mh, dlp->dl_txloop, dlp);
465 	}
466 
467 	/*
468 	 * Turn on or off 'all multicast' mode, if necessary.
469 	 */
470 	if (flags & DLS_PROMISC_MULTI) {
471 		if (!(dip->di_promisc & DLS_PROMISC_MULTI)) {
472 			err = mac_promisc_set(dip->di_mh, B_TRUE, MAC_PROMISC);
473 			if (err != 0)
474 				goto done;
475 			dip->di_promisc |= DLS_PROMISC_MULTI;
476 			dlp->dl_npromisc++;
477 		}
478 	} else {
479 		if (dip->di_promisc & DLS_PROMISC_MULTI) {
480 			err = mac_promisc_set(dip->di_mh, B_FALSE, MAC_PROMISC);
481 			if (err != 0)
482 				goto done;
483 			dip->di_promisc &= ~DLS_PROMISC_MULTI;
484 			dlp->dl_npromisc--;
485 		}
486 	}
487 
488 	/*
489 	 * Turn on or off 'all physical' mode, if necessary.
490 	 */
491 	if (flags & DLS_PROMISC_PHYS) {
492 		if (!(dip->di_promisc & DLS_PROMISC_PHYS)) {
493 			err = mac_promisc_set(dip->di_mh, B_TRUE, MAC_PROMISC);
494 			if (err != 0)
495 				goto done;
496 			dip->di_promisc |= DLS_PROMISC_PHYS;
497 			dlp->dl_npromisc++;
498 		}
499 	} else {
500 		if (dip->di_promisc & DLS_PROMISC_PHYS) {
501 			err = mac_promisc_set(dip->di_mh, B_FALSE, MAC_PROMISC);
502 			if (err != 0)
503 				goto done;
504 			dip->di_promisc &= ~DLS_PROMISC_PHYS;
505 			dlp->dl_npromisc--;
506 		}
507 	}
508 
509 done:
510 	if (dlp->dl_npromisc == 0 && dlp->dl_mth != NULL) {
511 		mac_txloop_remove(dlp->dl_mh, dlp->dl_mth);
512 		dlp->dl_mth = NULL;
513 	}
514 
515 	ASSERT(dlp->dl_npromisc == 0 || dlp->dl_mth != NULL);
516 	mutex_exit(&dlp->dl_promisc_lock);
517 
518 	rw_exit(&(dip->di_lock));
519 	return (err);
520 }
521 
522 int
523 dls_multicst_add(dls_channel_t dc, const uint8_t *addr)
524 {
525 	dls_impl_t		*dip = (dls_impl_t *)dc;
526 	int			err;
527 	dls_multicst_addr_t	**pp;
528 	dls_multicst_addr_t	*p;
529 	uint_t			addr_length;
530 
531 	/*
532 	 * Check whether the address is in the list of enabled addresses for
533 	 * this dls_impl_t.
534 	 */
535 	rw_enter(&(dip->di_lock), RW_WRITER);
536 	addr_length = dip->di_mip->mi_addr_length;
537 	for (pp = &(dip->di_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
538 		if (bcmp(addr, p->dma_addr, addr_length) == 0) {
539 			/*
540 			 * It is there so there's nothing to do.
541 			 */
542 			err = 0;
543 			goto done;
544 		}
545 	}
546 
547 	/*
548 	 * Allocate a new list item.
549 	 */
550 	if ((p = kmem_zalloc(sizeof (dls_multicst_addr_t),
551 	    KM_NOSLEEP)) == NULL) {
552 		err = ENOMEM;
553 		goto done;
554 	}
555 
556 	/*
557 	 * Enable the address at the MAC.
558 	 */
559 	if ((err = mac_multicst_add(dip->di_mh, addr)) != 0) {
560 		kmem_free(p, sizeof (dls_multicst_addr_t));
561 		goto done;
562 	}
563 
564 	/*
565 	 * The address is now enabled at the MAC so add it to the list.
566 	 */
567 	bcopy(addr, p->dma_addr, addr_length);
568 	*pp = p;
569 
570 done:
571 	rw_exit(&(dip->di_lock));
572 	return (err);
573 }
574 
575 int
576 dls_multicst_remove(dls_channel_t dc, const uint8_t *addr)
577 {
578 	dls_impl_t		*dip = (dls_impl_t *)dc;
579 	int			err;
580 	dls_multicst_addr_t	**pp;
581 	dls_multicst_addr_t	*p;
582 	uint_t			addr_length;
583 
584 	/*
585 	 * Find the address in the list of enabled addresses for this
586 	 * dls_impl_t.
587 	 */
588 	rw_enter(&(dip->di_lock), RW_WRITER);
589 	addr_length = dip->di_mip->mi_addr_length;
590 	for (pp = &(dip->di_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
591 		if (bcmp(addr, p->dma_addr, addr_length) == 0)
592 			break;
593 	}
594 
595 	/*
596 	 * If we walked to the end of the list then the given address is
597 	 * not currently enabled for this dls_impl_t.
598 	 */
599 	if (p == NULL) {
600 		err = ENOENT;
601 		goto done;
602 	}
603 
604 	/*
605 	 * Disable the address at the MAC.
606 	 */
607 	if ((err = mac_multicst_remove(dip->di_mh, addr)) != 0)
608 		goto done;
609 
610 	/*
611 	 * Remove the address from the list.
612 	 */
613 	*pp = p->dma_nextp;
614 	kmem_free(p, sizeof (dls_multicst_addr_t));
615 
616 done:
617 	rw_exit(&(dip->di_lock));
618 	return (err);
619 }
620 
621 mblk_t *
622 dls_header(dls_channel_t dc, const uint8_t *addr, uint16_t sap, uint_t pri,
623     mblk_t **payloadp)
624 {
625 	dls_impl_t *dip = (dls_impl_t *)dc;
626 	uint16_t vid;
627 	size_t extra_len;
628 	uint16_t mac_sap;
629 	mblk_t *mp, *payload;
630 	boolean_t is_ethernet = (dip->di_mip->mi_media == DL_ETHER);
631 	struct ether_vlan_header *evhp;
632 
633 	vid = dip->di_dvp->dv_id;
634 	payload = (payloadp == NULL) ? NULL : (*payloadp);
635 
636 	/*
637 	 * If the following conditions are satisfied:
638 	 *	- This is not a ETHERTYPE_VLAN listener; and
639 	 *	- This is either a VLAN stream or this is a physical stream
640 	 *	  but the priority is not 0.
641 	 *
642 	 * then we know ahead of time that we'll need to fill in additional
643 	 * VLAN information in the link-layer header. We will tell the MAC
644 	 * layer to pre-allocate some space at the end of the Ethernet
645 	 * header for us.
646 	 */
647 	if (is_ethernet && sap != ETHERTYPE_VLAN &&
648 	    (vid != VLAN_ID_NONE || pri != 0)) {
649 		extra_len = sizeof (struct ether_vlan_header) -
650 		    sizeof (struct ether_header);
651 		mac_sap = ETHERTYPE_VLAN;
652 	} else {
653 		extra_len = 0;
654 		mac_sap = sap;
655 	}
656 
657 	mp = mac_header(dip->di_mh, addr, mac_sap, payload, extra_len);
658 	if (mp == NULL)
659 		return (NULL);
660 
661 	if ((vid == VLAN_ID_NONE && pri == 0) || !is_ethernet)
662 		return (mp);
663 
664 	/*
665 	 * Fill in the tag information.
666 	 */
667 	ASSERT(MBLKL(mp) == sizeof (struct ether_header));
668 	if (extra_len != 0) {
669 		mp->b_wptr += extra_len;
670 		evhp = (struct ether_vlan_header *)mp->b_rptr;
671 		evhp->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI, vid));
672 		evhp->ether_type = htons(sap);
673 	} else {
674 		/*
675 		 * The stream is ETHERTYPE_VLAN listener, so its VLAN tag is
676 		 * in the payload. Update the priority.
677 		 */
678 		struct ether_vlan_extinfo *extinfo;
679 		size_t len = sizeof (struct ether_vlan_extinfo);
680 
681 		ASSERT(sap == ETHERTYPE_VLAN);
682 		ASSERT(payload != NULL);
683 
684 		if ((DB_REF(payload) > 1) || (MBLKL(payload) < len)) {
685 			mblk_t *newmp;
686 
687 			/*
688 			 * Because some DLS consumers only check the db_ref
689 			 * count of the first mblk, we pullup 'payload' into
690 			 * a single mblk.
691 			 */
692 			newmp = msgpullup(payload, -1);
693 			if ((newmp == NULL) || (MBLKL(newmp) < len)) {
694 				freemsg(newmp);
695 				freemsg(mp);
696 				return (NULL);
697 			} else {
698 				freemsg(payload);
699 				*payloadp = payload = newmp;
700 			}
701 		}
702 
703 		extinfo = (struct ether_vlan_extinfo *)payload->b_rptr;
704 		extinfo->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI,
705 		    VLAN_ID(ntohs(extinfo->ether_tci))));
706 	}
707 	return (mp);
708 }
709 
710 int
711 dls_header_info(dls_channel_t dc, mblk_t *mp, mac_header_info_t *mhip)
712 {
713 	return (dls_link_header_info(((dls_impl_t *)dc)->di_dvp->dv_dlp,
714 	    mp, mhip));
715 }
716 
717 void
718 dls_rx_set(dls_channel_t dc, dls_rx_t rx, void *arg)
719 {
720 	dls_impl_t	*dip = (dls_impl_t *)dc;
721 
722 	rw_enter(&(dip->di_lock), RW_WRITER);
723 	dip->di_rx = rx;
724 	dip->di_rx_arg = arg;
725 	rw_exit(&(dip->di_lock));
726 }
727 
728 mblk_t *
729 dls_tx(dls_channel_t dc, mblk_t *mp)
730 {
731 	const mac_txinfo_t *mtp = ((dls_impl_t *)dc)->di_txinfo;
732 
733 	return (mtp->mt_fn(mtp->mt_arg, mp));
734 }
735 
736 boolean_t
737 dls_accept(dls_impl_t *dip, mac_header_info_t *mhip, dls_rx_t *di_rx,
738     void **di_rx_arg)
739 {
740 	dls_multicst_addr_t	*dmap;
741 	size_t			addr_length = dip->di_mip->mi_addr_length;
742 
743 	/*
744 	 * We must not accept packets if the dls_impl_t is not marked as bound
745 	 * or is being removed.
746 	 */
747 	rw_enter(&(dip->di_lock), RW_READER);
748 	if (!dip->di_bound || dip->di_removing)
749 		goto refuse;
750 
751 	/*
752 	 * If the dls_impl_t is in 'all physical' mode then always accept.
753 	 */
754 	if (dip->di_promisc & DLS_PROMISC_PHYS)
755 		goto accept;
756 
757 	switch (mhip->mhi_dsttype) {
758 	case MAC_ADDRTYPE_UNICAST:
759 		/*
760 		 * Check to see if the destination address matches the
761 		 * dls_impl_t unicast address.
762 		 */
763 		if (memcmp(mhip->mhi_daddr, dip->di_unicst_addr, addr_length) ==
764 		    0) {
765 			goto accept;
766 		}
767 		break;
768 	case MAC_ADDRTYPE_MULTICAST:
769 		/*
770 		 * Check the address against the list of addresses enabled
771 		 * for this dls_impl_t or accept it unconditionally if the
772 		 * dls_impl_t is in 'all multicast' mode.
773 		 */
774 		if (dip->di_promisc & DLS_PROMISC_MULTI)
775 			goto accept;
776 		for (dmap = dip->di_dmap; dmap != NULL;
777 		    dmap = dmap->dma_nextp) {
778 			if (memcmp(mhip->mhi_daddr, dmap->dma_addr,
779 			    addr_length) == 0) {
780 				goto accept;
781 			}
782 		}
783 		break;
784 	case MAC_ADDRTYPE_BROADCAST:
785 		/*
786 		 * If the address is broadcast then the dls_impl_t will
787 		 * always accept it.
788 		 */
789 		goto accept;
790 	}
791 
792 refuse:
793 	rw_exit(&(dip->di_lock));
794 	return (B_FALSE);
795 
796 accept:
797 	/*
798 	 * Since we hold di_lock here, the returned di_rx and di_rx_arg will
799 	 * always be in sync.
800 	 */
801 	*di_rx = dip->di_rx;
802 	*di_rx_arg = dip->di_rx_arg;
803 	rw_exit(&(dip->di_lock));
804 	return (B_TRUE);
805 }
806 
807 /* ARGSUSED */
808 boolean_t
809 dls_accept_loopback(dls_impl_t *dip, mac_header_info_t *mhip, dls_rx_t *di_rx,
810     void **di_rx_arg)
811 {
812 	/*
813 	 * We must not accept packets if the dls_impl_t is not marked as bound
814 	 * or is being removed.
815 	 */
816 	rw_enter(&(dip->di_lock), RW_READER);
817 	if (!dip->di_bound || dip->di_removing)
818 		goto refuse;
819 
820 	/*
821 	 * A dls_impl_t should only accept loopback packets if it is in
822 	 * 'all physical' mode.
823 	 */
824 	if (dip->di_promisc & DLS_PROMISC_PHYS)
825 		goto accept;
826 
827 refuse:
828 	rw_exit(&(dip->di_lock));
829 	return (B_FALSE);
830 
831 accept:
832 	/*
833 	 * Since we hold di_lock here, the returned di_rx and di_rx_arg will
834 	 * always be in sync.
835 	 */
836 	*di_rx = dip->di_rx;
837 	*di_rx_arg = dip->di_rx_arg;
838 	rw_exit(&(dip->di_lock));
839 	return (B_TRUE);
840 }
841 
842 boolean_t
843 dls_active_set(dls_channel_t dc)
844 {
845 	dls_impl_t	*dip = (dls_impl_t *)dc;
846 	dls_link_t	*dlp = dip->di_dvp->dv_dlp;
847 
848 	rw_enter(&dip->di_lock, RW_WRITER);
849 
850 	/* If we're already active, then there's nothing more to do. */
851 	if (dip->di_active) {
852 		rw_exit(&dip->di_lock);
853 		return (B_TRUE);
854 	}
855 
856 	/*
857 	 * If this is the first active client on this link, notify
858 	 * the mac that we're becoming an active client.
859 	 */
860 	if (dlp->dl_nactive == 0 && !mac_active_shareable_set(dlp->dl_mh)) {
861 		rw_exit(&dip->di_lock);
862 		return (B_FALSE);
863 	}
864 	dip->di_active = B_TRUE;
865 	mutex_enter(&dlp->dl_lock);
866 	dlp->dl_nactive++;
867 	mutex_exit(&dlp->dl_lock);
868 	rw_exit(&dip->di_lock);
869 	return (B_TRUE);
870 }
871 
872 void
873 dls_active_clear(dls_channel_t dc)
874 {
875 	dls_impl_t	*dip = (dls_impl_t *)dc;
876 	dls_link_t	*dlp = dip->di_dvp->dv_dlp;
877 
878 	rw_enter(&dip->di_lock, RW_WRITER);
879 
880 	if (!dip->di_active)
881 		goto out;
882 	dip->di_active = B_FALSE;
883 
884 	mutex_enter(&dlp->dl_lock);
885 	if (--dlp->dl_nactive == 0)
886 		mac_active_clear(dip->di_mh);
887 	mutex_exit(&dlp->dl_lock);
888 out:
889 	rw_exit(&dip->di_lock);
890 }
891 
892 dev_info_t *
893 dls_finddevinfo(dev_t dev)
894 {
895 	return (dls_vlan_finddevinfo(dev));
896 }
897 
898 int
899 dls_ppa_from_minor(minor_t minor, t_uscalar_t *ppa)
900 {
901 	return (dls_vlan_ppa_from_minor(minor, ppa));
902 }
903