xref: /illumos-gate/usr/src/uts/common/io/dls/dls.c (revision dd72704bd9e794056c558153663c739e2012d721)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright 2012, Nexenta Systems, Inc. All rights reserved.
25  */
26 
27 /*
28  * Copyright 2019 Joyent, Inc.
29  */
30 
31 /*
32  * Data-Link Services Module
33  */
34 
35 #include	<sys/strsun.h>
36 #include	<sys/vlan.h>
37 #include	<sys/dld_impl.h>
38 #include	<sys/mac_client_priv.h>
39 
40 int
41 dls_open(dls_link_t *dlp, dls_dl_handle_t ddh, dld_str_t *dsp)
42 {
43 	zoneid_t	zid = getzoneid();
44 	boolean_t	local;
45 	int		err;
46 
47 	/*
48 	 * Check whether this client belongs to the zone of this dlp. Note that
49 	 * a global zone client is allowed to open a local zone dlp.
50 	 */
51 	if (zid != GLOBAL_ZONEID && dlp->dl_zid != zid)
52 		return (ENOENT);
53 
54 	/*
55 	 * mac_start() is required for non-legacy MACs to show accurate
56 	 * kstats even before the interface is brought up. For legacy
57 	 * drivers, this is not needed. Further, calling mac_start() for
58 	 * legacy drivers would make the shared-lower-stream to stay in
59 	 * the DL_IDLE state, which in turn causes performance regression.
60 	 */
61 	if (!mac_capab_get(dlp->dl_mh, MAC_CAPAB_LEGACY, NULL) &&
62 	    ((err = mac_start(dlp->dl_mh)) != 0)) {
63 		return (err);
64 	}
65 
66 	local = (zid == dlp->dl_zid);
67 	dlp->dl_zone_ref += (local ? 1 : 0);
68 
69 	/*
70 	 * Cache a copy of the MAC interface handle, a pointer to the
71 	 * immutable MAC info.
72 	 */
73 	dsp->ds_dlp = dlp;
74 	dsp->ds_mh = dlp->dl_mh;
75 	dsp->ds_mch = dlp->dl_mch;
76 	dsp->ds_mip = dlp->dl_mip;
77 	dsp->ds_ddh = ddh;
78 	dsp->ds_local = local;
79 
80 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
81 	return (0);
82 }
83 
84 void
85 dls_close(dld_str_t *dsp)
86 {
87 	dls_link_t		*dlp = dsp->ds_dlp;
88 	dls_multicst_addr_t	*p;
89 	dls_multicst_addr_t	*nextp;
90 
91 	ASSERT(dsp->ds_datathr_cnt == 0);
92 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
93 
94 	if (dsp->ds_local)
95 		dlp->dl_zone_ref--;
96 	dsp->ds_local = B_FALSE;
97 
98 	/*
99 	 * Walk the list of multicast addresses, disabling each at the MAC.
100 	 * Note that we must remove multicast address before
101 	 * mac_unicast_remove() (called by dls_active_clear()) because
102 	 * mac_multicast_remove() relies on the unicast flows on the mac
103 	 * client.
104 	 */
105 	for (p = dsp->ds_dmap; p != NULL; p = nextp) {
106 		(void) mac_multicast_remove(dsp->ds_mch, p->dma_addr);
107 		nextp = p->dma_nextp;
108 		kmem_free(p, sizeof (dls_multicst_addr_t));
109 	}
110 	dsp->ds_dmap = NULL;
111 
112 	dls_active_clear(dsp, B_TRUE);
113 
114 	/*
115 	 * If the dld_str_t is bound then unbind it.
116 	 */
117 	if (dsp->ds_dlstate == DL_IDLE) {
118 		dls_unbind(dsp);
119 		dsp->ds_dlstate = DL_UNBOUND;
120 	}
121 
122 	/*
123 	 * If the MAC has been set in promiscuous mode then disable it.
124 	 * This needs to be done before resetting ds_rx.
125 	 */
126 	(void) dls_promisc(dsp, 0);
127 
128 	/*
129 	 * At this point we have cutoff inbound packet flow from the mac
130 	 * for this 'dsp'. The dls_link_remove above cut off packets meant
131 	 * for us and waited for upcalls to finish. Similarly the dls_promisc
132 	 * reset above waited for promisc callbacks to finish. Now we can
133 	 * safely reset ds_rx to NULL
134 	 */
135 	dsp->ds_rx = NULL;
136 	dsp->ds_rx_arg = NULL;
137 
138 	dsp->ds_dlp = NULL;
139 
140 	if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_LEGACY, NULL))
141 		mac_stop(dsp->ds_mh);
142 
143 	/*
144 	 * Release our reference to the dls_link_t allowing that to be
145 	 * destroyed if there are no more dls_impl_t.
146 	 */
147 	dls_link_rele(dlp);
148 }
149 
150 int
151 dls_bind(dld_str_t *dsp, uint32_t sap)
152 {
153 	uint32_t	dls_sap;
154 
155 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
156 
157 	/*
158 	 * Check to see the value is legal for the media type.
159 	 */
160 	if (!mac_sap_verify(dsp->ds_mh, sap, &dls_sap))
161 		return (EINVAL);
162 
163 	if (dsp->ds_promisc & DLS_PROMISC_SAP)
164 		dls_sap = DLS_SAP_PROMISC;
165 
166 	/*
167 	 * Set up the dld_str_t to mark it as able to receive packets.
168 	 */
169 	dsp->ds_sap = sap;
170 
171 	/*
172 	 * The MAC layer does the VLAN demultiplexing and will only pass up
173 	 * untagged packets to non-promiscuous primary MAC clients. In order to
174 	 * support binding to the VLAN SAP, which is required by DLPI, DLS
175 	 * needs to get a copy of all tagged packets when the client binds to
176 	 * the VLAN SAP. We do this by registering a separate promiscuous
177 	 * callback for each DLS client binding to that SAP.
178 	 *
179 	 * Note: even though there are two promiscuous handles in dld_str_t,
180 	 * ds_mph is for the regular promiscuous mode, ds_vlan_mph is the handle
181 	 * to receive VLAN traffic when promiscuous mode is not on. Only one of
182 	 * them can be non-NULL at the same time, to avoid receiving duplicate
183 	 * copies of packets.
184 	 */
185 	if (sap == ETHERTYPE_VLAN && dsp->ds_promisc == 0) {
186 		int err;
187 
188 		if (dsp->ds_vlan_mph != NULL)
189 			return (EINVAL);
190 		err = mac_promisc_add(dsp->ds_mch,
191 		    MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
192 		    &dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
193 
194 		if (err == 0 && dsp->ds_nonip &&
195 		    dsp->ds_dlp->dl_nonip_cnt++ == 0)
196 			mac_rx_bypass_disable(dsp->ds_mch);
197 
198 		return (err);
199 	}
200 
201 	/*
202 	 * Now bind the dld_str_t by adding it into the hash table in the
203 	 * dls_link_t.
204 	 */
205 	dls_link_add(dsp->ds_dlp, dls_sap, dsp);
206 	if (dsp->ds_nonip && dsp->ds_dlp->dl_nonip_cnt++ == 0)
207 		mac_rx_bypass_disable(dsp->ds_mch);
208 
209 	return (0);
210 }
211 
212 void
213 dls_unbind(dld_str_t *dsp)
214 {
215 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
216 
217 	if (dsp->ds_nonip && --dsp->ds_dlp->dl_nonip_cnt == 0)
218 		mac_rx_bypass_enable(dsp->ds_mch);
219 
220 	/*
221 	 * A VLAN SAP does not actually add itself to the STREAM head today.
222 	 * While we initially set up a VLAN handle below, it's possible that
223 	 * something else will have come in and clobbered it.
224 	 */
225 	if (dsp->ds_sap == ETHERTYPE_VLAN) {
226 		if (dsp->ds_vlan_mph != NULL) {
227 			mac_promisc_remove(dsp->ds_vlan_mph);
228 			dsp->ds_vlan_mph = NULL;
229 		}
230 		return;
231 	}
232 
233 	/*
234 	 * Unbind the dld_str_t by removing it from the hash table in the
235 	 * dls_link_t.
236 	 */
237 	dls_link_remove(dsp->ds_dlp, dsp);
238 	dsp->ds_sap = 0;
239 }
240 
241 /*
242  * In order to prevent promiscuous-mode processing with dsp->ds_promisc
243  * set to inaccurate values, this function sets dsp->ds_promisc with new
244  * flags.  For enabling (mac_promisc_add), the flags are set prior to the
245  * actual enabling.  For disabling (mac_promisc_remove), the flags are set
246  * after the actual disabling.
247  */
248 int
249 dls_promisc(dld_str_t *dsp, uint32_t new_flags)
250 {
251 	int err = 0;
252 	uint32_t old_flags = dsp->ds_promisc;
253 	const uint32_t option_flags = DLS_PROMISC_RX_ONLY;
254 	uint32_t old_type = old_flags & ~option_flags;
255 	uint32_t new_type = new_flags & ~option_flags;
256 	mac_client_promisc_type_t mptype = MAC_CLIENT_PROMISC_ALL;
257 	uint16_t mac_flags = 0;
258 
259 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
260 	ASSERT(!(new_flags & ~(DLS_PROMISC_SAP | DLS_PROMISC_MULTI |
261 	    DLS_PROMISC_PHYS | option_flags)));
262 
263 	/*
264 	 * If the user has only requested DLS_PROMISC_MULTI then we need to make
265 	 * sure that they don't see all packets.
266 	 */
267 	if (new_type == DLS_PROMISC_MULTI)
268 		mptype = MAC_CLIENT_PROMISC_MULTI;
269 
270 	/*
271 	 * Look at new flags and figure out the correct mac promisc flags.
272 	 * If we've only requested DLS_PROMISC_SAP and not _MULTI or _PHYS,
273 	 * don't turn on physical promisc mode.
274 	 */
275 	if (new_flags & DLS_PROMISC_RX_ONLY)
276 		mac_flags |= MAC_PROMISC_FLAGS_NO_TX_LOOP;
277 	if (new_type == DLS_PROMISC_SAP)
278 		mac_flags |= MAC_PROMISC_FLAGS_NO_PHYS;
279 
280 	/*
281 	 * There are three cases we care about here with respect to MAC. Going
282 	 * from nothing to something, something to nothing, something to
283 	 * something where we need to change how we're getting stuff from mac.
284 	 * In the last case, as long as they're not equal, we need to assume
285 	 * something has changed and do something about it.
286 	 */
287 	if (old_type == 0 && new_type == 0) {
288 		/*
289 		 * If there are only option flags in the old and new flags
290 		 * then there is no need to talk to MAC. Just update the flags.
291 		 */
292 		dsp->ds_promisc = new_flags;
293 	} else if (old_type == 0 && new_type != 0) {
294 		dsp->ds_promisc = new_flags;
295 		err = mac_promisc_add(dsp->ds_mch, mptype,
296 		    dls_rx_promisc, dsp, &dsp->ds_mph, mac_flags);
297 		if (err != 0) {
298 			dsp->ds_promisc = old_flags;
299 			return (err);
300 		}
301 
302 		/* Remove vlan promisc handle to avoid sending dup copy up */
303 		if (dsp->ds_vlan_mph != NULL) {
304 			mac_promisc_remove(dsp->ds_vlan_mph);
305 			dsp->ds_vlan_mph = NULL;
306 		}
307 	} else if (old_type != 0 && new_type == 0) {
308 		ASSERT(dsp->ds_mph != NULL);
309 
310 		mac_promisc_remove(dsp->ds_mph);
311 		dsp->ds_promisc = new_flags;
312 		dsp->ds_mph = NULL;
313 
314 		if (dsp->ds_sap == ETHERTYPE_VLAN &&
315 		    dsp->ds_dlstate != DL_UNBOUND) {
316 			if (dsp->ds_vlan_mph != NULL)
317 				return (EINVAL);
318 			err = mac_promisc_add(dsp->ds_mch,
319 			    MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
320 			    &dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
321 		}
322 	} else if (new_flags != old_flags) {
323 		ASSERT(dsp->ds_mph != NULL);
324 		mac_promisc_remove(dsp->ds_mph);
325 		/* Honors both after-remove and before-add semantics! */
326 		dsp->ds_promisc = new_flags;
327 		err = mac_promisc_add(dsp->ds_mch, mptype,
328 		    dls_rx_promisc, dsp, &dsp->ds_mph, mac_flags);
329 		if (err != 0)
330 			dsp->ds_promisc = old_flags;
331 	}
332 
333 	return (err);
334 }
335 
336 int
337 dls_multicst_add(dld_str_t *dsp, const uint8_t *addr)
338 {
339 	int			err;
340 	dls_multicst_addr_t	**pp;
341 	dls_multicst_addr_t	*p;
342 	uint_t			addr_length;
343 
344 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
345 
346 	/*
347 	 * Check whether the address is in the list of enabled addresses for
348 	 * this dld_str_t.
349 	 */
350 	addr_length = dsp->ds_mip->mi_addr_length;
351 
352 	/*
353 	 * Protect against concurrent access of ds_dmap by data threads using
354 	 * ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
355 	 * remove operations. Dropping the ds_rw_lock across mac calls is thus
356 	 * ok and is also required by the locking protocol.
357 	 */
358 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
359 	for (pp = &(dsp->ds_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
360 		if (bcmp(addr, p->dma_addr, addr_length) == 0) {
361 			/*
362 			 * It is there so there's nothing to do.
363 			 */
364 			err = 0;
365 			goto done;
366 		}
367 	}
368 
369 	/*
370 	 * Allocate a new list item and add it to the list.
371 	 */
372 	p = kmem_zalloc(sizeof (dls_multicst_addr_t), KM_SLEEP);
373 	bcopy(addr, p->dma_addr, addr_length);
374 	*pp = p;
375 	rw_exit(&dsp->ds_rw_lock);
376 
377 	/*
378 	 * Enable the address at the MAC.
379 	 */
380 	err = mac_multicast_add(dsp->ds_mch, addr);
381 	if (err == 0)
382 		return (0);
383 
384 	/* Undo the operation as it has failed */
385 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
386 	ASSERT(*pp == p && p->dma_nextp == NULL);
387 	*pp = NULL;
388 	kmem_free(p, sizeof (dls_multicst_addr_t));
389 done:
390 	rw_exit(&dsp->ds_rw_lock);
391 	return (err);
392 }
393 
394 int
395 dls_multicst_remove(dld_str_t *dsp, const uint8_t *addr)
396 {
397 	dls_multicst_addr_t	**pp;
398 	dls_multicst_addr_t	*p;
399 	uint_t			addr_length;
400 
401 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
402 
403 	/*
404 	 * Find the address in the list of enabled addresses for this
405 	 * dld_str_t.
406 	 */
407 	addr_length = dsp->ds_mip->mi_addr_length;
408 
409 	/*
410 	 * Protect against concurrent access to ds_dmap by data threads using
411 	 * ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
412 	 * remove operations. Dropping the ds_rw_lock across mac calls is thus
413 	 * ok and is also required by the locking protocol.
414 	 */
415 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
416 	for (pp = &(dsp->ds_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
417 		if (bcmp(addr, p->dma_addr, addr_length) == 0)
418 			break;
419 	}
420 
421 	/*
422 	 * If we walked to the end of the list then the given address is
423 	 * not currently enabled for this dld_str_t.
424 	 */
425 	if (p == NULL) {
426 		rw_exit(&dsp->ds_rw_lock);
427 		return (ENOENT);
428 	}
429 
430 	/*
431 	 * Remove the address from the list.
432 	 */
433 	*pp = p->dma_nextp;
434 	rw_exit(&dsp->ds_rw_lock);
435 
436 	/*
437 	 * Disable the address at the MAC.
438 	 */
439 	mac_multicast_remove(dsp->ds_mch, addr);
440 	kmem_free(p, sizeof (dls_multicst_addr_t));
441 	return (0);
442 }
443 
444 mblk_t *
445 dls_header(dld_str_t *dsp, const uint8_t *addr, uint16_t sap, uint_t pri,
446     mblk_t **payloadp)
447 {
448 	uint16_t vid;
449 	size_t extra_len;
450 	uint16_t mac_sap;
451 	mblk_t *mp, *payload;
452 	boolean_t is_ethernet = (dsp->ds_mip->mi_media == DL_ETHER);
453 	struct ether_vlan_header *evhp;
454 
455 	vid = mac_client_vid(dsp->ds_mch);
456 	payload = (payloadp == NULL) ? NULL : (*payloadp);
457 
458 	/*
459 	 * In the case of Ethernet, we need to tell mac_header() if we need
460 	 * extra room beyond the Ethernet header for a VLAN header.  We'll
461 	 * need to add a VLAN header if this isn't an ETHERTYPE_VLAN listener
462 	 * (because such streams will be handling VLAN headers on their own)
463 	 * and one of the following conditions is satisfied:
464 	 *
465 	 * - This is a VLAN stream
466 	 * - This is a physical stream, the priority is not 0, and user
467 	 *   priority tagging is allowed.
468 	 */
469 	if (is_ethernet && sap != ETHERTYPE_VLAN &&
470 	    (vid != VLAN_ID_NONE ||
471 	    (pri != 0 && dsp->ds_dlp->dl_tagmode != LINK_TAGMODE_VLANONLY))) {
472 		extra_len = sizeof (struct ether_vlan_header) -
473 		    sizeof (struct ether_header);
474 		mac_sap = ETHERTYPE_VLAN;
475 	} else {
476 		extra_len = 0;
477 		mac_sap = sap;
478 	}
479 
480 	mp = mac_header(dsp->ds_mh, addr, mac_sap, payload, extra_len);
481 	if (mp == NULL)
482 		return (NULL);
483 
484 	if ((vid == VLAN_ID_NONE && (pri == 0 ||
485 	    dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_VLANONLY)) || !is_ethernet)
486 		return (mp);
487 
488 	/*
489 	 * Fill in the tag information.
490 	 */
491 	ASSERT(MBLKL(mp) == sizeof (struct ether_header));
492 	if (extra_len != 0) {
493 		mp->b_wptr += extra_len;
494 		evhp = (struct ether_vlan_header *)mp->b_rptr;
495 		evhp->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI, vid));
496 		evhp->ether_type = htons(sap);
497 	} else {
498 		/*
499 		 * The stream is ETHERTYPE_VLAN listener, so its VLAN tag is
500 		 * in the payload. Update the priority.
501 		 */
502 		struct ether_vlan_extinfo *extinfo;
503 		size_t len = sizeof (struct ether_vlan_extinfo);
504 
505 		ASSERT(sap == ETHERTYPE_VLAN);
506 		ASSERT(payload != NULL);
507 
508 		if ((DB_REF(payload) > 1) || (MBLKL(payload) < len)) {
509 			mblk_t *newmp;
510 
511 			/*
512 			 * Because some DLS consumers only check the db_ref
513 			 * count of the first mblk, we pullup 'payload' into
514 			 * a single mblk.
515 			 */
516 			newmp = msgpullup(payload, -1);
517 			if ((newmp == NULL) || (MBLKL(newmp) < len)) {
518 				freemsg(newmp);
519 				freemsg(mp);
520 				return (NULL);
521 			} else {
522 				freemsg(payload);
523 				*payloadp = payload = newmp;
524 			}
525 		}
526 
527 		extinfo = (struct ether_vlan_extinfo *)payload->b_rptr;
528 		extinfo->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI,
529 		    VLAN_ID(ntohs(extinfo->ether_tci))));
530 	}
531 	return (mp);
532 }
533 
534 void
535 dls_rx_set(dld_str_t *dsp, dls_rx_t rx, void *arg)
536 {
537 	mutex_enter(&dsp->ds_lock);
538 	dsp->ds_rx = rx;
539 	dsp->ds_rx_arg = arg;
540 	mutex_exit(&dsp->ds_lock);
541 }
542 
543 static boolean_t
544 dls_accept_common(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
545     void **ds_rx_arg, boolean_t promisc, boolean_t promisc_loopback)
546 {
547 	dls_multicst_addr_t	*dmap;
548 	size_t			addr_length = dsp->ds_mip->mi_addr_length;
549 
550 	/*
551 	 * We must not accept packets if the dld_str_t is not marked as bound
552 	 * or is being removed.
553 	 */
554 	if (dsp->ds_dlstate != DL_IDLE)
555 		goto refuse;
556 
557 	if (dsp->ds_promisc != 0) {
558 		/*
559 		 * Filter out packets that arrived from the data path
560 		 * (i_dls_link_rx) when promisc mode is on. We need to correlate
561 		 * the ds_promisc flags with the mac header destination type. If
562 		 * only DLS_PROMISC_MULTI is enabled, we need to only reject
563 		 * multicast packets as those are the only ones which filter up
564 		 * the promiscuous path. If we have DLS_PROMISC_PHYS or
565 		 * DLS_PROMISC_SAP set, then we know that we'll be seeing
566 		 * everything, so we should drop it now.
567 		 */
568 		if (!promisc && !(dsp->ds_promisc == DLS_PROMISC_MULTI &&
569 		    mhip->mhi_dsttype != MAC_ADDRTYPE_MULTICAST))
570 			goto refuse;
571 		/*
572 		 * If the dls_impl_t is in 'all physical' mode then
573 		 * always accept.
574 		 */
575 		if (dsp->ds_promisc & DLS_PROMISC_PHYS)
576 			goto accept;
577 
578 		/*
579 		 * Loopback packets i.e. packets sent out by DLS on a given
580 		 * mac end point, will be accepted back by DLS on loopback
581 		 * from the mac, only in the 'all physical' mode which has been
582 		 * covered by the previous check above
583 		 */
584 		if (promisc_loopback)
585 			goto refuse;
586 	}
587 
588 	switch (mhip->mhi_dsttype) {
589 	case MAC_ADDRTYPE_UNICAST:
590 	case MAC_ADDRTYPE_BROADCAST:
591 		/*
592 		 * We can accept unicast and broadcast packets because
593 		 * filtering is already done by the mac layer.
594 		 */
595 		goto accept;
596 	case MAC_ADDRTYPE_MULTICAST:
597 		/*
598 		 * Additional filtering is needed for multicast addresses
599 		 * because different streams may be interested in different
600 		 * addresses.
601 		 */
602 		if (dsp->ds_promisc & DLS_PROMISC_MULTI)
603 			goto accept;
604 
605 		rw_enter(&dsp->ds_rw_lock, RW_READER);
606 		for (dmap = dsp->ds_dmap; dmap != NULL;
607 		    dmap = dmap->dma_nextp) {
608 			if (memcmp(mhip->mhi_daddr, dmap->dma_addr,
609 			    addr_length) == 0) {
610 				rw_exit(&dsp->ds_rw_lock);
611 				goto accept;
612 			}
613 		}
614 		rw_exit(&dsp->ds_rw_lock);
615 		break;
616 	}
617 
618 refuse:
619 	return (B_FALSE);
620 
621 accept:
622 	/*
623 	 * the returned ds_rx and ds_rx_arg will always be in sync.
624 	 */
625 	mutex_enter(&dsp->ds_lock);
626 	*ds_rx = dsp->ds_rx;
627 	*ds_rx_arg = dsp->ds_rx_arg;
628 	mutex_exit(&dsp->ds_lock);
629 
630 	return (B_TRUE);
631 }
632 
633 /* ARGSUSED */
634 boolean_t
635 dls_accept(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
636     void **ds_rx_arg)
637 {
638 	return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_FALSE,
639 	    B_FALSE));
640 }
641 
642 boolean_t
643 dls_accept_promisc(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
644     void **ds_rx_arg, boolean_t loopback)
645 {
646 	return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_TRUE,
647 	    loopback));
648 }
649 
650 int
651 dls_mac_active_set(dls_link_t *dlp)
652 {
653 	int err = 0;
654 
655 	/*
656 	 * First client; add the primary unicast address.
657 	 */
658 	if (dlp->dl_nactive == 0) {
659 		/*
660 		 * First client; add the primary unicast address.
661 		 */
662 		mac_diag_t diag;
663 
664 		/* request the primary MAC address */
665 		if ((err = mac_unicast_add(dlp->dl_mch, NULL,
666 		    MAC_UNICAST_PRIMARY | MAC_UNICAST_TAG_DISABLE |
667 		    MAC_UNICAST_DISABLE_TX_VID_CHECK, &dlp->dl_mah,
668 		    VLAN_ID_NONE, &diag)) != 0) {
669 			return (err);
670 		}
671 
672 		/*
673 		 * Set the function to start receiving packets.
674 		 */
675 		mac_rx_set(dlp->dl_mch, i_dls_link_rx, dlp);
676 	}
677 	dlp->dl_nactive++;
678 	return (0);
679 }
680 
681 void
682 dls_mac_active_clear(dls_link_t *dlp)
683 {
684 	if (--dlp->dl_nactive == 0) {
685 		ASSERT(dlp->dl_mah != NULL);
686 		(void) mac_unicast_remove(dlp->dl_mch, dlp->dl_mah);
687 		dlp->dl_mah = NULL;
688 		mac_rx_clear(dlp->dl_mch);
689 	}
690 }
691 
692 int
693 dls_active_set(dld_str_t *dsp)
694 {
695 	int err = 0;
696 
697 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
698 
699 	if (dsp->ds_passivestate == DLD_PASSIVE)
700 		return (0);
701 
702 	/* If we're already active, then there's nothing more to do. */
703 	if ((dsp->ds_nactive == 0) &&
704 	    ((err = dls_mac_active_set(dsp->ds_dlp)) != 0)) {
705 		/* except for ENXIO all other errors are mapped to EBUSY */
706 		if (err != ENXIO)
707 			return (EBUSY);
708 		return (err);
709 	}
710 
711 	dsp->ds_passivestate = DLD_ACTIVE;
712 	dsp->ds_nactive++;
713 	return (0);
714 }
715 
716 /*
717  * Note that dls_active_set() is called whenever an active operation
718  * (DL_BIND_REQ, DL_ENABMULTI_REQ ...) is processed and
719  * dls_active_clear(dsp, B_FALSE) is called whenever the active operation
720  * is being undone (DL_UNBIND_REQ, DL_DISABMULTI_REQ ...). In some cases,
721  * a stream is closed without every active operation being undone and we
722  * need to clear all the "active" states by calling
723  * dls_active_clear(dsp, B_TRUE).
724  */
725 void
726 dls_active_clear(dld_str_t *dsp, boolean_t all)
727 {
728 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
729 
730 	if (dsp->ds_passivestate == DLD_PASSIVE)
731 		return;
732 
733 	if (all && dsp->ds_nactive == 0)
734 		return;
735 
736 	ASSERT(dsp->ds_nactive > 0);
737 
738 	dsp->ds_nactive -= (all ? dsp->ds_nactive : 1);
739 	if (dsp->ds_nactive != 0)
740 		return;
741 
742 	ASSERT(dsp->ds_passivestate == DLD_ACTIVE);
743 	dls_mac_active_clear(dsp->ds_dlp);
744 	dsp->ds_passivestate = DLD_UNINITIALIZED;
745 }
746