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
40int
41dls_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
84void
85dls_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
150int
151dls_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
212void
213dls_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 */
248int
249dls_promisc(dld_str_t *dsp, uint32_t new_flags)
250{
251	int err = 0;
252	uint32_t old_flags = dsp->ds_promisc;
253	mac_client_promisc_type_t mptype = MAC_CLIENT_PROMISC_ALL;
254
255	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
256	ASSERT(!(new_flags & ~(DLS_PROMISC_SAP | DLS_PROMISC_MULTI |
257	    DLS_PROMISC_PHYS)));
258
259	/*
260	 * If the user has only requested DLS_PROMISC_MULTI then we need to make
261	 * sure that they don't see all packets.
262	 */
263	if (new_flags == DLS_PROMISC_MULTI)
264		mptype = MAC_CLIENT_PROMISC_MULTI;
265
266	if (dsp->ds_promisc == 0 && new_flags != 0) {
267		/*
268		 * If only DLS_PROMISC_SAP, we don't turn on the
269		 * physical promisc mode
270		 */
271		dsp->ds_promisc = new_flags;
272		err = mac_promisc_add(dsp->ds_mch, mptype,
273		    dls_rx_promisc, dsp, &dsp->ds_mph,
274		    (new_flags != DLS_PROMISC_SAP) ? 0 :
275		    MAC_PROMISC_FLAGS_NO_PHYS);
276		if (err != 0) {
277			dsp->ds_promisc = old_flags;
278			return (err);
279		}
280
281		/* Remove vlan promisc handle to avoid sending dup copy up */
282		if (dsp->ds_vlan_mph != NULL) {
283			mac_promisc_remove(dsp->ds_vlan_mph);
284			dsp->ds_vlan_mph = NULL;
285		}
286	} else if (dsp->ds_promisc != 0 && new_flags == 0) {
287		ASSERT(dsp->ds_mph != NULL);
288
289		mac_promisc_remove(dsp->ds_mph);
290		dsp->ds_promisc = new_flags;
291		dsp->ds_mph = NULL;
292
293		if (dsp->ds_sap == ETHERTYPE_VLAN &&
294		    dsp->ds_dlstate != DL_UNBOUND) {
295			if (dsp->ds_vlan_mph != NULL)
296				return (EINVAL);
297			err = mac_promisc_add(dsp->ds_mch,
298			    MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
299			    &dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
300		}
301	} else if (dsp->ds_promisc == DLS_PROMISC_SAP && new_flags != 0 &&
302	    new_flags != dsp->ds_promisc) {
303		/*
304		 * If the old flag is PROMISC_SAP, but the current flag has
305		 * changed to some new non-zero value, we need to turn the
306		 * physical promiscuous mode.
307		 */
308		ASSERT(dsp->ds_mph != NULL);
309		mac_promisc_remove(dsp->ds_mph);
310		/* Honors both after-remove and before-add semantics! */
311		dsp->ds_promisc = new_flags;
312		err = mac_promisc_add(dsp->ds_mch, mptype,
313		    dls_rx_promisc, dsp, &dsp->ds_mph, 0);
314		if (err != 0)
315			dsp->ds_promisc = old_flags;
316	} else {
317		/* No adding or removing, but record the new flags anyway. */
318		dsp->ds_promisc = new_flags;
319	}
320
321	return (err);
322}
323
324int
325dls_multicst_add(dld_str_t *dsp, const uint8_t *addr)
326{
327	int			err;
328	dls_multicst_addr_t	**pp;
329	dls_multicst_addr_t	*p;
330	uint_t			addr_length;
331
332	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
333
334	/*
335	 * Check whether the address is in the list of enabled addresses for
336	 * this dld_str_t.
337	 */
338	addr_length = dsp->ds_mip->mi_addr_length;
339
340	/*
341	 * Protect against concurrent access of ds_dmap by data threads using
342	 * ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
343	 * remove operations. Dropping the ds_rw_lock across mac calls is thus
344	 * ok and is also required by the locking protocol.
345	 */
346	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
347	for (pp = &(dsp->ds_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
348		if (bcmp(addr, p->dma_addr, addr_length) == 0) {
349			/*
350			 * It is there so there's nothing to do.
351			 */
352			err = 0;
353			goto done;
354		}
355	}
356
357	/*
358	 * Allocate a new list item and add it to the list.
359	 */
360	p = kmem_zalloc(sizeof (dls_multicst_addr_t), KM_SLEEP);
361	bcopy(addr, p->dma_addr, addr_length);
362	*pp = p;
363	rw_exit(&dsp->ds_rw_lock);
364
365	/*
366	 * Enable the address at the MAC.
367	 */
368	err = mac_multicast_add(dsp->ds_mch, addr);
369	if (err == 0)
370		return (0);
371
372	/* Undo the operation as it has failed */
373	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
374	ASSERT(*pp == p && p->dma_nextp == NULL);
375	*pp = NULL;
376	kmem_free(p, sizeof (dls_multicst_addr_t));
377done:
378	rw_exit(&dsp->ds_rw_lock);
379	return (err);
380}
381
382int
383dls_multicst_remove(dld_str_t *dsp, const uint8_t *addr)
384{
385	dls_multicst_addr_t	**pp;
386	dls_multicst_addr_t	*p;
387	uint_t			addr_length;
388
389	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
390
391	/*
392	 * Find the address in the list of enabled addresses for this
393	 * dld_str_t.
394	 */
395	addr_length = dsp->ds_mip->mi_addr_length;
396
397	/*
398	 * Protect against concurrent access to ds_dmap by data threads using
399	 * ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
400	 * remove operations. Dropping the ds_rw_lock across mac calls is thus
401	 * ok and is also required by the locking protocol.
402	 */
403	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
404	for (pp = &(dsp->ds_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
405		if (bcmp(addr, p->dma_addr, addr_length) == 0)
406			break;
407	}
408
409	/*
410	 * If we walked to the end of the list then the given address is
411	 * not currently enabled for this dld_str_t.
412	 */
413	if (p == NULL) {
414		rw_exit(&dsp->ds_rw_lock);
415		return (ENOENT);
416	}
417
418	/*
419	 * Remove the address from the list.
420	 */
421	*pp = p->dma_nextp;
422	rw_exit(&dsp->ds_rw_lock);
423
424	/*
425	 * Disable the address at the MAC.
426	 */
427	mac_multicast_remove(dsp->ds_mch, addr);
428	kmem_free(p, sizeof (dls_multicst_addr_t));
429	return (0);
430}
431
432mblk_t *
433dls_header(dld_str_t *dsp, const uint8_t *addr, uint16_t sap, uint_t pri,
434    mblk_t **payloadp)
435{
436	uint16_t vid;
437	size_t extra_len;
438	uint16_t mac_sap;
439	mblk_t *mp, *payload;
440	boolean_t is_ethernet = (dsp->ds_mip->mi_media == DL_ETHER);
441	struct ether_vlan_header *evhp;
442
443	vid = mac_client_vid(dsp->ds_mch);
444	payload = (payloadp == NULL) ? NULL : (*payloadp);
445
446	/*
447	 * In the case of Ethernet, we need to tell mac_header() if we need
448	 * extra room beyond the Ethernet header for a VLAN header.  We'll
449	 * need to add a VLAN header if this isn't an ETHERTYPE_VLAN listener
450	 * (because such streams will be handling VLAN headers on their own)
451	 * and one of the following conditions is satisfied:
452	 *
453	 * - This is a VLAN stream
454	 * - This is a physical stream, the priority is not 0, and user
455	 *   priority tagging is allowed.
456	 */
457	if (is_ethernet && sap != ETHERTYPE_VLAN &&
458	    (vid != VLAN_ID_NONE ||
459	    (pri != 0 && dsp->ds_dlp->dl_tagmode != LINK_TAGMODE_VLANONLY))) {
460		extra_len = sizeof (struct ether_vlan_header) -
461		    sizeof (struct ether_header);
462		mac_sap = ETHERTYPE_VLAN;
463	} else {
464		extra_len = 0;
465		mac_sap = sap;
466	}
467
468	mp = mac_header(dsp->ds_mh, addr, mac_sap, payload, extra_len);
469	if (mp == NULL)
470		return (NULL);
471
472	if ((vid == VLAN_ID_NONE && (pri == 0 ||
473	    dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_VLANONLY)) || !is_ethernet)
474		return (mp);
475
476	/*
477	 * Fill in the tag information.
478	 */
479	ASSERT(MBLKL(mp) == sizeof (struct ether_header));
480	if (extra_len != 0) {
481		mp->b_wptr += extra_len;
482		evhp = (struct ether_vlan_header *)mp->b_rptr;
483		evhp->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI, vid));
484		evhp->ether_type = htons(sap);
485	} else {
486		/*
487		 * The stream is ETHERTYPE_VLAN listener, so its VLAN tag is
488		 * in the payload. Update the priority.
489		 */
490		struct ether_vlan_extinfo *extinfo;
491		size_t len = sizeof (struct ether_vlan_extinfo);
492
493		ASSERT(sap == ETHERTYPE_VLAN);
494		ASSERT(payload != NULL);
495
496		if ((DB_REF(payload) > 1) || (MBLKL(payload) < len)) {
497			mblk_t *newmp;
498
499			/*
500			 * Because some DLS consumers only check the db_ref
501			 * count of the first mblk, we pullup 'payload' into
502			 * a single mblk.
503			 */
504			newmp = msgpullup(payload, -1);
505			if ((newmp == NULL) || (MBLKL(newmp) < len)) {
506				freemsg(newmp);
507				freemsg(mp);
508				return (NULL);
509			} else {
510				freemsg(payload);
511				*payloadp = payload = newmp;
512			}
513		}
514
515		extinfo = (struct ether_vlan_extinfo *)payload->b_rptr;
516		extinfo->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI,
517		    VLAN_ID(ntohs(extinfo->ether_tci))));
518	}
519	return (mp);
520}
521
522void
523dls_rx_set(dld_str_t *dsp, dls_rx_t rx, void *arg)
524{
525	mutex_enter(&dsp->ds_lock);
526	dsp->ds_rx = rx;
527	dsp->ds_rx_arg = arg;
528	mutex_exit(&dsp->ds_lock);
529}
530
531static boolean_t
532dls_accept_common(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
533    void **ds_rx_arg, boolean_t promisc, boolean_t promisc_loopback)
534{
535	dls_multicst_addr_t	*dmap;
536	size_t			addr_length = dsp->ds_mip->mi_addr_length;
537
538	/*
539	 * We must not accept packets if the dld_str_t is not marked as bound
540	 * or is being removed.
541	 */
542	if (dsp->ds_dlstate != DL_IDLE)
543		goto refuse;
544
545	if (dsp->ds_promisc != 0) {
546		/*
547		 * Filter out packets that arrived from the data path
548		 * (i_dls_link_rx) when promisc mode is on. We need to correlate
549		 * the ds_promisc flags with the mac header destination type. If
550		 * only DLS_PROMISC_MULTI is enabled, we need to only reject
551		 * multicast packets as those are the only ones which filter up
552		 * the promiscuous path. If we have DLS_PROMISC_PHYS or
553		 * DLS_PROMISC_SAP set, then we know that we'll be seeing
554		 * everything, so we should drop it now.
555		 */
556		if (!promisc && !(dsp->ds_promisc == DLS_PROMISC_MULTI &&
557		    mhip->mhi_dsttype != MAC_ADDRTYPE_MULTICAST))
558			goto refuse;
559		/*
560		 * If the dls_impl_t is in 'all physical' mode then
561		 * always accept.
562		 */
563		if (dsp->ds_promisc & DLS_PROMISC_PHYS)
564			goto accept;
565
566		/*
567		 * Loopback packets i.e. packets sent out by DLS on a given
568		 * mac end point, will be accepted back by DLS on loopback
569		 * from the mac, only in the 'all physical' mode which has been
570		 * covered by the previous check above
571		 */
572		if (promisc_loopback)
573			goto refuse;
574	}
575
576	switch (mhip->mhi_dsttype) {
577	case MAC_ADDRTYPE_UNICAST:
578	case MAC_ADDRTYPE_BROADCAST:
579		/*
580		 * We can accept unicast and broadcast packets because
581		 * filtering is already done by the mac layer.
582		 */
583		goto accept;
584	case MAC_ADDRTYPE_MULTICAST:
585		/*
586		 * Additional filtering is needed for multicast addresses
587		 * because different streams may be interested in different
588		 * addresses.
589		 */
590		if (dsp->ds_promisc & DLS_PROMISC_MULTI)
591			goto accept;
592
593		rw_enter(&dsp->ds_rw_lock, RW_READER);
594		for (dmap = dsp->ds_dmap; dmap != NULL;
595		    dmap = dmap->dma_nextp) {
596			if (memcmp(mhip->mhi_daddr, dmap->dma_addr,
597			    addr_length) == 0) {
598				rw_exit(&dsp->ds_rw_lock);
599				goto accept;
600			}
601		}
602		rw_exit(&dsp->ds_rw_lock);
603		break;
604	}
605
606refuse:
607	return (B_FALSE);
608
609accept:
610	/*
611	 * the returned ds_rx and ds_rx_arg will always be in sync.
612	 */
613	mutex_enter(&dsp->ds_lock);
614	*ds_rx = dsp->ds_rx;
615	*ds_rx_arg = dsp->ds_rx_arg;
616	mutex_exit(&dsp->ds_lock);
617
618	return (B_TRUE);
619}
620
621/* ARGSUSED */
622boolean_t
623dls_accept(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
624    void **ds_rx_arg)
625{
626	return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_FALSE,
627	    B_FALSE));
628}
629
630boolean_t
631dls_accept_promisc(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
632    void **ds_rx_arg, boolean_t loopback)
633{
634	return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_TRUE,
635	    loopback));
636}
637
638int
639dls_mac_active_set(dls_link_t *dlp)
640{
641	int err = 0;
642
643	/*
644	 * First client; add the primary unicast address.
645	 */
646	if (dlp->dl_nactive == 0) {
647		/*
648		 * First client; add the primary unicast address.
649		 */
650		mac_diag_t diag;
651
652		/* request the primary MAC address */
653		if ((err = mac_unicast_add(dlp->dl_mch, NULL,
654		    MAC_UNICAST_PRIMARY | MAC_UNICAST_TAG_DISABLE |
655		    MAC_UNICAST_DISABLE_TX_VID_CHECK, &dlp->dl_mah,
656		    VLAN_ID_NONE, &diag)) != 0) {
657			return (err);
658		}
659
660		/*
661		 * Set the function to start receiving packets.
662		 */
663		mac_rx_set(dlp->dl_mch, i_dls_link_rx, dlp);
664	}
665	dlp->dl_nactive++;
666	return (0);
667}
668
669void
670dls_mac_active_clear(dls_link_t *dlp)
671{
672	if (--dlp->dl_nactive == 0) {
673		ASSERT(dlp->dl_mah != NULL);
674		(void) mac_unicast_remove(dlp->dl_mch, dlp->dl_mah);
675		dlp->dl_mah = NULL;
676		mac_rx_clear(dlp->dl_mch);
677	}
678}
679
680int
681dls_active_set(dld_str_t *dsp)
682{
683	int err = 0;
684
685	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
686
687	if (dsp->ds_passivestate == DLD_PASSIVE)
688		return (0);
689
690	/* If we're already active, then there's nothing more to do. */
691	if ((dsp->ds_nactive == 0) &&
692	    ((err = dls_mac_active_set(dsp->ds_dlp)) != 0)) {
693		/* except for ENXIO all other errors are mapped to EBUSY */
694		if (err != ENXIO)
695			return (EBUSY);
696		return (err);
697	}
698
699	dsp->ds_passivestate = DLD_ACTIVE;
700	dsp->ds_nactive++;
701	return (0);
702}
703
704/*
705 * Note that dls_active_set() is called whenever an active operation
706 * (DL_BIND_REQ, DL_ENABMULTI_REQ ...) is processed and
707 * dls_active_clear(dsp, B_FALSE) is called whenever the active operation
708 * is being undone (DL_UNBIND_REQ, DL_DISABMULTI_REQ ...). In some cases,
709 * a stream is closed without every active operation being undone and we
710 * need to clear all the "active" states by calling
711 * dls_active_clear(dsp, B_TRUE).
712 */
713void
714dls_active_clear(dld_str_t *dsp, boolean_t all)
715{
716	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
717
718	if (dsp->ds_passivestate == DLD_PASSIVE)
719		return;
720
721	if (all && dsp->ds_nactive == 0)
722		return;
723
724	ASSERT(dsp->ds_nactive > 0);
725
726	dsp->ds_nactive -= (all ? dsp->ds_nactive : 1);
727	if (dsp->ds_nactive != 0)
728		return;
729
730	ASSERT(dsp->ds_passivestate == DLD_ACTIVE);
731	dls_mac_active_clear(dsp->ds_dlp);
732	dsp->ds_passivestate = DLD_UNINITIALIZED;
733}
734