xref: /onnv-gate/usr/src/uts/common/io/dls/dls.c (revision 9024:8f3a34e0ccbb)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51502Sericheng  * Common Development and Distribution License (the "License").
61502Sericheng  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
228833SVenu.Iyer@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate  * Data-Link Services Module
280Sstevel@tonic-gate  */
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #include	<sys/strsun.h>
310Sstevel@tonic-gate #include	<sys/vlan.h>
328275SEric Cheng #include	<sys/dld_impl.h>
338893SMichael.Lim@Sun.COM #include	<sys/mac_client_priv.h>
340Sstevel@tonic-gate 
358275SEric Cheng int
368275SEric Cheng dls_open(dls_link_t *dlp, dls_dl_handle_t ddh, dld_str_t *dsp)
378275SEric Cheng {
388275SEric Cheng 	zoneid_t	zid = getzoneid();
398275SEric Cheng 	boolean_t	local;
408893SMichael.Lim@Sun.COM 	int		err;
410Sstevel@tonic-gate 
428275SEric Cheng 	/*
438275SEric Cheng 	 * Check whether this client belongs to the zone of this dlp. Note that
448275SEric Cheng 	 * a global zone client is allowed to open a local zone dlp.
458275SEric Cheng 	 */
468275SEric Cheng 	if (zid != GLOBAL_ZONEID && dlp->dl_zid != zid)
478275SEric Cheng 		return (ENOENT);
481184Skrgopi 
498893SMichael.Lim@Sun.COM 	if ((err = mac_start(dlp->dl_mh)) != 0)
508893SMichael.Lim@Sun.COM 		return (err);
518893SMichael.Lim@Sun.COM 
528275SEric Cheng 	local = (zid == dlp->dl_zid);
538275SEric Cheng 	dlp->dl_zone_ref += (local ? 1 : 0);
545895Syz147064 
558275SEric Cheng 	/*
568275SEric Cheng 	 * Cache a copy of the MAC interface handle, a pointer to the
578275SEric Cheng 	 * immutable MAC info.
588275SEric Cheng 	 */
598275SEric Cheng 	dsp->ds_dlp = dlp;
608275SEric Cheng 	dsp->ds_mh = dlp->dl_mh;
618275SEric Cheng 	dsp->ds_mch = dlp->dl_mch;
628275SEric Cheng 	dsp->ds_mip = dlp->dl_mip;
638275SEric Cheng 	dsp->ds_ddh = ddh;
648275SEric Cheng 	dsp->ds_local = local;
650Sstevel@tonic-gate 
668275SEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
670Sstevel@tonic-gate 	return (0);
680Sstevel@tonic-gate }
690Sstevel@tonic-gate 
708275SEric Cheng void
718275SEric Cheng dls_close(dld_str_t *dsp)
720Sstevel@tonic-gate {
738275SEric Cheng 	dls_link_t		*dlp = dsp->ds_dlp;
748275SEric Cheng 	dls_multicst_addr_t	*p;
758275SEric Cheng 	dls_multicst_addr_t	*nextp;
768275SEric Cheng 	uint32_t		old_flags;
770Sstevel@tonic-gate 
788275SEric Cheng 	ASSERT(dsp->ds_datathr_cnt == 0);
798275SEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
800Sstevel@tonic-gate 
818275SEric Cheng 	if (dsp->ds_local)
828275SEric Cheng 		dlp->dl_zone_ref--;
838275SEric Cheng 	dsp->ds_local = B_FALSE;
840Sstevel@tonic-gate 
858275SEric Cheng 	/*
868275SEric Cheng 	 * Walk the list of multicast addresses, disabling each at the MAC.
878275SEric Cheng 	 * Note that we must remove multicast address before
888275SEric Cheng 	 * mac_unicast_remove() (called by dls_active_clear()) because
898275SEric Cheng 	 * mac_multicast_remove() relies on the unicast flows on the mac
908275SEric Cheng 	 * client.
918275SEric Cheng 	 */
928275SEric Cheng 	for (p = dsp->ds_dmap; p != NULL; p = nextp) {
938275SEric Cheng 		(void) mac_multicast_remove(dsp->ds_mch, p->dma_addr);
948275SEric Cheng 		nextp = p->dma_nextp;
958275SEric Cheng 		kmem_free(p, sizeof (dls_multicst_addr_t));
960Sstevel@tonic-gate 	}
978275SEric Cheng 	dsp->ds_dmap = NULL;
988275SEric Cheng 
998275SEric Cheng 	dls_active_clear(dsp);
1000Sstevel@tonic-gate 
1018275SEric Cheng 	/*
1028275SEric Cheng 	 * If the dld_str_t is bound then unbind it.
1038275SEric Cheng 	 */
1048275SEric Cheng 	if (dsp->ds_dlstate == DL_IDLE) {
1058275SEric Cheng 		(void) dls_unbind(dsp);
1068275SEric Cheng 		dsp->ds_dlstate = DL_UNBOUND;
1071184Skrgopi 	}
1081184Skrgopi 
1098275SEric Cheng 	/*
1108275SEric Cheng 	 * If the MAC has been set in promiscuous mode then disable it.
1118275SEric Cheng 	 * This needs to be done before resetting ds_rx.
1128275SEric Cheng 	 */
1138275SEric Cheng 	old_flags = dsp->ds_promisc;
1148275SEric Cheng 	dsp->ds_promisc = 0;
1158275SEric Cheng 	(void) dls_promisc(dsp, old_flags);
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate 	/*
1188275SEric Cheng 	 * At this point we have cutoff inbound packet flow from the mac
1198275SEric Cheng 	 * for this 'dsp'. The dls_link_remove above cut off packets meant
1208275SEric Cheng 	 * for us and waited for upcalls to finish. Similarly the dls_promisc
1218275SEric Cheng 	 * reset above waited for promisc callbacks to finish. Now we can
1228275SEric Cheng 	 * safely reset ds_rx to NULL
1230Sstevel@tonic-gate 	 */
1248275SEric Cheng 	dsp->ds_rx = NULL;
1258275SEric Cheng 	dsp->ds_rx_arg = NULL;
1268275SEric Cheng 
1278275SEric Cheng 	dsp->ds_dlp = NULL;
1288275SEric Cheng 
1298893SMichael.Lim@Sun.COM 	mac_stop(dsp->ds_mh);
1308893SMichael.Lim@Sun.COM 
1318275SEric Cheng 	/*
1328275SEric Cheng 	 * Release our reference to the dls_link_t allowing that to be
1338275SEric Cheng 	 * destroyed if there are no more dls_impl_t.
1348275SEric Cheng 	 */
1358275SEric Cheng 	dls_link_rele(dlp);
1360Sstevel@tonic-gate }
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate int
1398275SEric Cheng dls_bind(dld_str_t *dsp, uint32_t sap)
1400Sstevel@tonic-gate {
1418275SEric Cheng 	uint32_t	dls_sap;
1428275SEric Cheng 
1438275SEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
1448275SEric Cheng 
1450Sstevel@tonic-gate 	/*
1468275SEric Cheng 	 * Check to see the value is legal for the media type.
1470Sstevel@tonic-gate 	 */
1488275SEric Cheng 	if (!mac_sap_verify(dsp->ds_mh, sap, &dls_sap))
1498275SEric Cheng 		return (EINVAL);
1508275SEric Cheng 
1518275SEric Cheng 	if (dsp->ds_promisc & DLS_PROMISC_SAP)
1528275SEric Cheng 		dls_sap = DLS_SAP_PROMISC;
1538275SEric Cheng 
1548275SEric Cheng 	/*
1558275SEric Cheng 	 * Set up the dld_str_t to mark it as able to receive packets.
1568275SEric Cheng 	 */
1578275SEric Cheng 	dsp->ds_sap = sap;
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate 	/*
1608275SEric Cheng 	 * The MAC layer does the VLAN demultiplexing and will only pass up
1618275SEric Cheng 	 * untagged packets to non-promiscuous primary MAC clients. In order to
1628275SEric Cheng 	 * support the binding to the VLAN SAP which is required by DLPI, dls
1638275SEric Cheng 	 * needs to get a copy of all tagged packets when the client binds to
1648275SEric Cheng 	 * the VLAN SAP. We do this by registering a separate promiscuous
1658275SEric Cheng 	 * callback for each dls client binding to that SAP.
1668275SEric Cheng 	 *
1678275SEric Cheng 	 * Note: even though there are two promiscuous handles in dld_str_t,
1688275SEric Cheng 	 * ds_mph is for the regular promiscuous mode, ds_vlan_mph is the handle
1698275SEric Cheng 	 * to receive VLAN pkt when promiscuous mode is not on. Only one of
1708275SEric Cheng 	 * them can be non-NULL at the same time, to avoid receiving dup copies
1718275SEric Cheng 	 * of pkts.
1720Sstevel@tonic-gate 	 */
1738275SEric Cheng 	if (sap == ETHERTYPE_VLAN && dsp->ds_promisc == 0) {
1748275SEric Cheng 		int err;
1758275SEric Cheng 
1768275SEric Cheng 		if (dsp->ds_vlan_mph != NULL)
1778275SEric Cheng 			return (EINVAL);
1788275SEric Cheng 		err = mac_promisc_add(dsp->ds_mch,
1798275SEric Cheng 		    MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
1808275SEric Cheng 		    &dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
1818275SEric Cheng 		return (err);
1828275SEric Cheng 	}
1838275SEric Cheng 
1848275SEric Cheng 	/*
1858275SEric Cheng 	 * Now bind the dld_str_t by adding it into the hash table in the
1868275SEric Cheng 	 * dls_link_t.
1878275SEric Cheng 	 */
1888275SEric Cheng 	dls_link_add(dsp->ds_dlp, dls_sap, dsp);
1890Sstevel@tonic-gate 	return (0);
1900Sstevel@tonic-gate }
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate int
1938275SEric Cheng dls_unbind(dld_str_t *dsp)
1940Sstevel@tonic-gate {
1958275SEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
1965895Syz147064 
1975895Syz147064 	/*
1988275SEric Cheng 	 * For VLAN SAP, there was a promisc handle registered when dls_bind.
1998275SEric Cheng 	 * When unbind this dls link, we need to remove the promisc handle.
2008275SEric Cheng 	 * See comments in dls_bind().
2015895Syz147064 	 */
2028275SEric Cheng 	if (dsp->ds_vlan_mph != NULL) {
2038275SEric Cheng 		int err;
2045895Syz147064 
2058275SEric Cheng 		err = mac_promisc_remove(dsp->ds_vlan_mph);
2068275SEric Cheng 		ASSERT(err == 0);
2078275SEric Cheng 		dsp->ds_vlan_mph = NULL;
2088275SEric Cheng 		return (err);
2098275SEric Cheng 	}
2105895Syz147064 
2118275SEric Cheng 	/*
2128275SEric Cheng 	 * Unbind the dld_str_t by removing it from the hash table in the
2138275SEric Cheng 	 * dls_link_t.
2148275SEric Cheng 	 */
2158275SEric Cheng 	dls_link_remove(dsp->ds_dlp, dsp);
2168275SEric Cheng 	dsp->ds_sap = 0;
2178275SEric Cheng 	return (0);
2180Sstevel@tonic-gate }
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate int
2218275SEric Cheng dls_promisc(dld_str_t *dsp, uint32_t old_flags)
2220Sstevel@tonic-gate {
2238275SEric Cheng 	int		err = 0;
2248275SEric Cheng 
2258275SEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
2268275SEric Cheng 	ASSERT(!(dsp->ds_promisc & ~(DLS_PROMISC_SAP | DLS_PROMISC_MULTI |
2278275SEric Cheng 	    DLS_PROMISC_PHYS)));
2288275SEric Cheng 
2298275SEric Cheng 	if (old_flags == 0 && dsp->ds_promisc != 0) {
2308275SEric Cheng 		/*
2318275SEric Cheng 		 * If only DLS_PROMISC_SAP, we don't turn on the
2328275SEric Cheng 		 * physical promisc mode
2338275SEric Cheng 		 */
2348275SEric Cheng 		err = mac_promisc_add(dsp->ds_mch, MAC_CLIENT_PROMISC_ALL,
2358275SEric Cheng 		    dls_rx_promisc, dsp, &dsp->ds_mph,
2368275SEric Cheng 		    (dsp->ds_promisc != DLS_PROMISC_SAP) ? 0 :
2378275SEric Cheng 		    MAC_PROMISC_FLAGS_NO_PHYS);
2388275SEric Cheng 		if (err != 0)
2398275SEric Cheng 			return (err);
2400Sstevel@tonic-gate 
2418275SEric Cheng 		/* Remove vlan promisc handle to avoid sending dup copy up */
2428275SEric Cheng 		if (dsp->ds_vlan_mph != NULL) {
2438275SEric Cheng 			err = mac_promisc_remove(dsp->ds_vlan_mph);
2448275SEric Cheng 			dsp->ds_vlan_mph = NULL;
2458275SEric Cheng 		}
2468275SEric Cheng 	} else if (old_flags != 0 && dsp->ds_promisc == 0) {
2478275SEric Cheng 		ASSERT(dsp->ds_mph != NULL);
2488275SEric Cheng 		err = mac_promisc_remove(dsp->ds_mph);
2498275SEric Cheng 		/*
2508275SEric Cheng 		 * The failure only relates to resetting the device promiscuity
2518275SEric Cheng 		 * The mac layer does not fail in freeing up the promiscuous
2528275SEric Cheng 		 * data structures, and so we clear the ds_mph. The dld stream
2538275SEric Cheng 		 * may be closing and we can't fail that.
2548275SEric Cheng 		 */
2558275SEric Cheng 		dsp->ds_mph = NULL;
2568275SEric Cheng 		if (err != 0)
2578275SEric Cheng 			return (err);
2580Sstevel@tonic-gate 
2598275SEric Cheng 		if (dsp->ds_sap == ETHERTYPE_VLAN &&
2608275SEric Cheng 		    dsp->ds_dlstate != DL_UNBOUND) {
2618275SEric Cheng 			int err;
2628275SEric Cheng 
2638275SEric Cheng 			if (dsp->ds_vlan_mph != NULL)
2648275SEric Cheng 				return (EINVAL);
2658275SEric Cheng 			err = mac_promisc_add(dsp->ds_mch,
2668275SEric Cheng 			    MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
2678275SEric Cheng 			    &dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
2688275SEric Cheng 			return (err);
2698275SEric Cheng 		}
2708275SEric Cheng 	} else if (old_flags == DLS_PROMISC_SAP && dsp->ds_promisc != 0 &&
2718275SEric Cheng 	    dsp->ds_promisc != old_flags) {
2728275SEric Cheng 		/*
2738275SEric Cheng 		 * If the old flag is PROMISC_SAP, but the current flag has
2748275SEric Cheng 		 * changed to some new non-zero value, we need to turn the
2758275SEric Cheng 		 * physical promiscuous mode.
2768275SEric Cheng 		 */
2778275SEric Cheng 		ASSERT(dsp->ds_mph != NULL);
2788275SEric Cheng 		err = mac_promisc_remove(dsp->ds_mph);
2798275SEric Cheng 		if (err != 0)
2808275SEric Cheng 			return (err);
2818275SEric Cheng 		err = mac_promisc_add(dsp->ds_mch, MAC_CLIENT_PROMISC_ALL,
2828275SEric Cheng 		    dls_rx_promisc, dsp, &dsp->ds_mph, 0);
2835895Syz147064 	}
2845895Syz147064 
2855895Syz147064 	return (err);
2865895Syz147064 }
2875895Syz147064 
2888275SEric Cheng int
2898275SEric Cheng dls_multicst_add(dld_str_t *dsp, const uint8_t *addr)
2900Sstevel@tonic-gate {
2910Sstevel@tonic-gate 	int			err;
2920Sstevel@tonic-gate 	dls_multicst_addr_t	**pp;
2930Sstevel@tonic-gate 	dls_multicst_addr_t	*p;
2940Sstevel@tonic-gate 	uint_t			addr_length;
2950Sstevel@tonic-gate 
2968275SEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
2978275SEric Cheng 
2980Sstevel@tonic-gate 	/*
2990Sstevel@tonic-gate 	 * Check whether the address is in the list of enabled addresses for
3008275SEric Cheng 	 * this dld_str_t.
3010Sstevel@tonic-gate 	 */
3028275SEric Cheng 	addr_length = dsp->ds_mip->mi_addr_length;
3038275SEric Cheng 
3048275SEric Cheng 	/*
3058275SEric Cheng 	 * Protect against concurrent access of ds_dmap by data threads using
3068275SEric Cheng 	 * ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
3078275SEric Cheng 	 * remove operations. Dropping the ds_rw_lock across mac calls is thus
3088275SEric Cheng 	 * ok and is also required by the locking protocol.
3098275SEric Cheng 	 */
3108275SEric Cheng 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
3118275SEric Cheng 	for (pp = &(dsp->ds_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
3120Sstevel@tonic-gate 		if (bcmp(addr, p->dma_addr, addr_length) == 0) {
3130Sstevel@tonic-gate 			/*
3140Sstevel@tonic-gate 			 * It is there so there's nothing to do.
3150Sstevel@tonic-gate 			 */
3160Sstevel@tonic-gate 			err = 0;
3170Sstevel@tonic-gate 			goto done;
3180Sstevel@tonic-gate 		}
3190Sstevel@tonic-gate 	}
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	/*
3228275SEric Cheng 	 * Allocate a new list item and add it to the list.
3230Sstevel@tonic-gate 	 */
3248275SEric Cheng 	p = kmem_zalloc(sizeof (dls_multicst_addr_t), KM_SLEEP);
3258275SEric Cheng 	bcopy(addr, p->dma_addr, addr_length);
3268275SEric Cheng 	*pp = p;
3278275SEric Cheng 	rw_exit(&dsp->ds_rw_lock);
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	/*
3300Sstevel@tonic-gate 	 * Enable the address at the MAC.
3310Sstevel@tonic-gate 	 */
3328275SEric Cheng 	err = mac_multicast_add(dsp->ds_mch, addr);
3338275SEric Cheng 	if (err == 0)
3348275SEric Cheng 		return (0);
3350Sstevel@tonic-gate 
3368275SEric Cheng 	/* Undo the operation as it has failed */
3378275SEric Cheng 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
3388275SEric Cheng 	ASSERT(*pp == p && p->dma_nextp == NULL);
3398275SEric Cheng 	*pp = NULL;
3408275SEric Cheng 	kmem_free(p, sizeof (dls_multicst_addr_t));
3410Sstevel@tonic-gate done:
3428275SEric Cheng 	rw_exit(&dsp->ds_rw_lock);
3430Sstevel@tonic-gate 	return (err);
3440Sstevel@tonic-gate }
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate int
3478275SEric Cheng dls_multicst_remove(dld_str_t *dsp, const uint8_t *addr)
3480Sstevel@tonic-gate {
3490Sstevel@tonic-gate 	dls_multicst_addr_t	**pp;
3500Sstevel@tonic-gate 	dls_multicst_addr_t	*p;
3510Sstevel@tonic-gate 	uint_t			addr_length;
3520Sstevel@tonic-gate 
3538275SEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
3548275SEric Cheng 
3550Sstevel@tonic-gate 	/*
3560Sstevel@tonic-gate 	 * Find the address in the list of enabled addresses for this
3578275SEric Cheng 	 * dld_str_t.
3580Sstevel@tonic-gate 	 */
3598275SEric Cheng 	addr_length = dsp->ds_mip->mi_addr_length;
3608275SEric Cheng 
3618275SEric Cheng 	/*
3628275SEric Cheng 	 * Protect against concurrent access to ds_dmap by data threads using
3638275SEric Cheng 	 * ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
3648275SEric Cheng 	 * remove operations. Dropping the ds_rw_lock across mac calls is thus
3658275SEric Cheng 	 * ok and is also required by the locking protocol.
3668275SEric Cheng 	 */
3678275SEric Cheng 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
3688275SEric Cheng 	for (pp = &(dsp->ds_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
3690Sstevel@tonic-gate 		if (bcmp(addr, p->dma_addr, addr_length) == 0)
3700Sstevel@tonic-gate 			break;
3710Sstevel@tonic-gate 	}
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	/*
3740Sstevel@tonic-gate 	 * If we walked to the end of the list then the given address is
3758275SEric Cheng 	 * not currently enabled for this dld_str_t.
3760Sstevel@tonic-gate 	 */
3770Sstevel@tonic-gate 	if (p == NULL) {
3788275SEric Cheng 		rw_exit(&dsp->ds_rw_lock);
3798275SEric Cheng 		return (ENOENT);
3800Sstevel@tonic-gate 	}
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	/*
3830Sstevel@tonic-gate 	 * Remove the address from the list.
3840Sstevel@tonic-gate 	 */
3850Sstevel@tonic-gate 	*pp = p->dma_nextp;
3868275SEric Cheng 	rw_exit(&dsp->ds_rw_lock);
3870Sstevel@tonic-gate 
3888275SEric Cheng 	/*
3898275SEric Cheng 	 * Disable the address at the MAC.
3908275SEric Cheng 	 */
3918275SEric Cheng 	mac_multicast_remove(dsp->ds_mch, addr);
3928275SEric Cheng 	kmem_free(p, sizeof (dls_multicst_addr_t));
3938275SEric Cheng 	return (0);
3940Sstevel@tonic-gate }
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate mblk_t *
3978275SEric Cheng dls_header(dld_str_t *dsp, const uint8_t *addr, uint16_t sap, uint_t pri,
3982760Sdg199075     mblk_t **payloadp)
3990Sstevel@tonic-gate {
4002760Sdg199075 	uint16_t vid;
4012760Sdg199075 	size_t extra_len;
4022760Sdg199075 	uint16_t mac_sap;
4032760Sdg199075 	mblk_t *mp, *payload;
4048275SEric Cheng 	boolean_t is_ethernet = (dsp->ds_mip->mi_media == DL_ETHER);
4052311Sseb 	struct ether_vlan_header *evhp;
4060Sstevel@tonic-gate 
4078275SEric Cheng 	vid = mac_client_vid(dsp->ds_mch);
4082760Sdg199075 	payload = (payloadp == NULL) ? NULL : (*payloadp);
4092760Sdg199075 
4102760Sdg199075 	/*
4118874SSebastien.Roy@Sun.COM 	 * In the case of Ethernet, we need to tell mac_header() if we need
4128874SSebastien.Roy@Sun.COM 	 * extra room beyond the Ethernet header for a VLAN header.  We'll
4138874SSebastien.Roy@Sun.COM 	 * need to add a VLAN header if this isn't an ETHERTYPE_VLAN listener
4148874SSebastien.Roy@Sun.COM 	 * (because such streams will be handling VLAN headers on their own)
4158874SSebastien.Roy@Sun.COM 	 * and one of the following conditions is satisfied:
4162760Sdg199075 	 *
4178874SSebastien.Roy@Sun.COM 	 * - This is a VLAN stream
4188874SSebastien.Roy@Sun.COM 	 * - This is a physical stream, the priority is not 0, and user
4198874SSebastien.Roy@Sun.COM 	 *   priority tagging is allowed.
4202760Sdg199075 	 */
4212760Sdg199075 	if (is_ethernet && sap != ETHERTYPE_VLAN &&
4228874SSebastien.Roy@Sun.COM 	    (vid != VLAN_ID_NONE ||
4238874SSebastien.Roy@Sun.COM 	    (pri != 0 && dsp->ds_dlp->dl_tagmode != LINK_TAGMODE_VLANONLY))) {
4242311Sseb 		extra_len = sizeof (struct ether_vlan_header) -
4252311Sseb 		    sizeof (struct ether_header);
4262760Sdg199075 		mac_sap = ETHERTYPE_VLAN;
4272311Sseb 	} else {
4282311Sseb 		extra_len = 0;
4292311Sseb 		mac_sap = sap;
4302311Sseb 	}
4312311Sseb 
4328275SEric Cheng 	mp = mac_header(dsp->ds_mh, addr, mac_sap, payload, extra_len);
4332760Sdg199075 	if (mp == NULL)
4342760Sdg199075 		return (NULL);
4352760Sdg199075 
4368874SSebastien.Roy@Sun.COM 	if ((vid == VLAN_ID_NONE && (pri == 0 ||
4378874SSebastien.Roy@Sun.COM 	    dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_VLANONLY)) || !is_ethernet)
4382311Sseb 		return (mp);
4392311Sseb 
4402760Sdg199075 	/*
4412760Sdg199075 	 * Fill in the tag information.
4422760Sdg199075 	 */
4432311Sseb 	ASSERT(MBLKL(mp) == sizeof (struct ether_header));
4442760Sdg199075 	if (extra_len != 0) {
4452760Sdg199075 		mp->b_wptr += extra_len;
4462760Sdg199075 		evhp = (struct ether_vlan_header *)mp->b_rptr;
4472760Sdg199075 		evhp->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI, vid));
4482760Sdg199075 		evhp->ether_type = htons(sap);
4492760Sdg199075 	} else {
4502760Sdg199075 		/*
4512760Sdg199075 		 * The stream is ETHERTYPE_VLAN listener, so its VLAN tag is
4522760Sdg199075 		 * in the payload. Update the priority.
4532760Sdg199075 		 */
4542760Sdg199075 		struct ether_vlan_extinfo *extinfo;
4552760Sdg199075 		size_t len = sizeof (struct ether_vlan_extinfo);
4562760Sdg199075 
4572760Sdg199075 		ASSERT(sap == ETHERTYPE_VLAN);
4582760Sdg199075 		ASSERT(payload != NULL);
4592760Sdg199075 
4602760Sdg199075 		if ((DB_REF(payload) > 1) || (MBLKL(payload) < len)) {
4612760Sdg199075 			mblk_t *newmp;
4622760Sdg199075 
4632760Sdg199075 			/*
4642760Sdg199075 			 * Because some DLS consumers only check the db_ref
4652760Sdg199075 			 * count of the first mblk, we pullup 'payload' into
4662760Sdg199075 			 * a single mblk.
4672760Sdg199075 			 */
4682760Sdg199075 			newmp = msgpullup(payload, -1);
4692760Sdg199075 			if ((newmp == NULL) || (MBLKL(newmp) < len)) {
4702760Sdg199075 				freemsg(newmp);
4712760Sdg199075 				freemsg(mp);
4722760Sdg199075 				return (NULL);
4732760Sdg199075 			} else {
4742760Sdg199075 				freemsg(payload);
4752760Sdg199075 				*payloadp = payload = newmp;
4762760Sdg199075 			}
4772760Sdg199075 		}
4782760Sdg199075 
4792760Sdg199075 		extinfo = (struct ether_vlan_extinfo *)payload->b_rptr;
4802760Sdg199075 		extinfo->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI,
4812760Sdg199075 		    VLAN_ID(ntohs(extinfo->ether_tci))));
4822760Sdg199075 	}
4832311Sseb 	return (mp);
4840Sstevel@tonic-gate }
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate void
4878275SEric Cheng dls_rx_set(dld_str_t *dsp, dls_rx_t rx, void *arg)
4880Sstevel@tonic-gate {
4898275SEric Cheng 	mutex_enter(&dsp->ds_lock);
4908275SEric Cheng 	dsp->ds_rx = rx;
4918275SEric Cheng 	dsp->ds_rx_arg = arg;
4928275SEric Cheng 	mutex_exit(&dsp->ds_lock);
4930Sstevel@tonic-gate }
4940Sstevel@tonic-gate 
4958275SEric Cheng static boolean_t
4968275SEric Cheng dls_accept_common(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
4978275SEric Cheng     void **ds_rx_arg, boolean_t promisc, boolean_t promisc_loopback)
4980Sstevel@tonic-gate {
4990Sstevel@tonic-gate 	dls_multicst_addr_t	*dmap;
5008275SEric Cheng 	size_t			addr_length = dsp->ds_mip->mi_addr_length;
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	/*
5038275SEric Cheng 	 * We must not accept packets if the dld_str_t is not marked as bound
5040Sstevel@tonic-gate 	 * or is being removed.
5050Sstevel@tonic-gate 	 */
5068275SEric Cheng 	if (dsp->ds_dlstate != DL_IDLE)
5070Sstevel@tonic-gate 		goto refuse;
5080Sstevel@tonic-gate 
5098275SEric Cheng 	if (dsp->ds_promisc != 0) {
5108275SEric Cheng 		/*
5118275SEric Cheng 		 * Filter out packets that arrived from the data path
5128275SEric Cheng 		 * (i_dls_link_rx) when promisc mode is on.
5138275SEric Cheng 		 */
5148275SEric Cheng 		if (!promisc)
5158275SEric Cheng 			goto refuse;
5168275SEric Cheng 		/*
5178275SEric Cheng 		 * If the dls_impl_t is in 'all physical' mode then
5188275SEric Cheng 		 * always accept.
5198275SEric Cheng 		 */
5208275SEric Cheng 		if (dsp->ds_promisc & DLS_PROMISC_PHYS)
5218275SEric Cheng 			goto accept;
5220Sstevel@tonic-gate 
5238275SEric Cheng 		/*
5248275SEric Cheng 		 * Loopback packets i.e. packets sent out by DLS on a given
5258275SEric Cheng 		 * mac end point, will be accepted back by DLS on loopback
5268275SEric Cheng 		 * from the mac, only in the 'all physical' mode which has been
5278275SEric Cheng 		 * covered by the previous check above
5288275SEric Cheng 		 */
5298275SEric Cheng 		if (promisc_loopback)
5308275SEric Cheng 			goto refuse;
5318275SEric Cheng 	}
5325895Syz147064 
5332311Sseb 	switch (mhip->mhi_dsttype) {
5342311Sseb 	case MAC_ADDRTYPE_UNICAST:
5358275SEric Cheng 	case MAC_ADDRTYPE_BROADCAST:
5362311Sseb 		/*
5378275SEric Cheng 		 * We can accept unicast and broadcast packets because
5388275SEric Cheng 		 * filtering is already done by the mac layer.
5392311Sseb 		 */
5408275SEric Cheng 		goto accept;
5412311Sseb 	case MAC_ADDRTYPE_MULTICAST:
5422311Sseb 		/*
5438275SEric Cheng 		 * Additional filtering is needed for multicast addresses
5448275SEric Cheng 		 * because different streams may be interested in different
5458275SEric Cheng 		 * addresses.
5462311Sseb 		 */
5478275SEric Cheng 		if (dsp->ds_promisc & DLS_PROMISC_MULTI)
5482311Sseb 			goto accept;
5498275SEric Cheng 
5508275SEric Cheng 		rw_enter(&dsp->ds_rw_lock, RW_READER);
5518275SEric Cheng 		for (dmap = dsp->ds_dmap; dmap != NULL;
5522311Sseb 		    dmap = dmap->dma_nextp) {
5532311Sseb 			if (memcmp(mhip->mhi_daddr, dmap->dma_addr,
5542311Sseb 			    addr_length) == 0) {
5558275SEric Cheng 				rw_exit(&dsp->ds_rw_lock);
5562311Sseb 				goto accept;
5572311Sseb 			}
5582311Sseb 		}
5598275SEric Cheng 		rw_exit(&dsp->ds_rw_lock);
5602311Sseb 		break;
5610Sstevel@tonic-gate 	}
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate refuse:
5640Sstevel@tonic-gate 	return (B_FALSE);
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate accept:
567449Sericheng 	/*
5688275SEric Cheng 	 * the returned ds_rx and ds_rx_arg will always be in sync.
569449Sericheng 	 */
5708275SEric Cheng 	mutex_enter(&dsp->ds_lock);
5718275SEric Cheng 	*ds_rx = dsp->ds_rx;
5728275SEric Cheng 	*ds_rx_arg = dsp->ds_rx_arg;
5738275SEric Cheng 	mutex_exit(&dsp->ds_lock);
5748275SEric Cheng 
5750Sstevel@tonic-gate 	return (B_TRUE);
5760Sstevel@tonic-gate }
5770Sstevel@tonic-gate 
5782760Sdg199075 /* ARGSUSED */
5790Sstevel@tonic-gate boolean_t
5808275SEric Cheng dls_accept(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
5818275SEric Cheng     void **ds_rx_arg)
5820Sstevel@tonic-gate {
5838275SEric Cheng 	return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_FALSE,
5848275SEric Cheng 	    B_FALSE));
5850Sstevel@tonic-gate }
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate boolean_t
5888275SEric Cheng dls_accept_promisc(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
5898275SEric Cheng     void **ds_rx_arg, boolean_t loopback)
5908275SEric Cheng {
5918275SEric Cheng 	return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_TRUE,
5928275SEric Cheng 	    loopback));
5938275SEric Cheng }
5948275SEric Cheng 
5958275SEric Cheng int
5965895Syz147064 dls_mac_active_set(dls_link_t *dlp)
5975895Syz147064 {
5988275SEric Cheng 	int err = 0;
5995895Syz147064 
6005895Syz147064 	/*
6018275SEric Cheng 	 * First client; add the primary unicast address.
6025895Syz147064 	 */
6038275SEric Cheng 	if (dlp->dl_nactive == 0) {
6048275SEric Cheng 		/*
6058275SEric Cheng 		 * First client; add the primary unicast address.
6068275SEric Cheng 		 */
6078275SEric Cheng 		mac_diag_t diag;
6088275SEric Cheng 
6098275SEric Cheng 		/* request the primary MAC address */
610*9024SVenu.Iyer@Sun.COM 		if ((err = mac_unicast_add(dlp->dl_mch, NULL,
611*9024SVenu.Iyer@Sun.COM 		    MAC_UNICAST_PRIMARY | MAC_UNICAST_TAG_DISABLE |
612*9024SVenu.Iyer@Sun.COM 		    MAC_UNICAST_DISABLE_TX_VID_CHECK, &dlp->dl_mah, 0,
6138275SEric Cheng 		    &diag)) != 0) {
6148275SEric Cheng 			return (err);
6158275SEric Cheng 		}
6168275SEric Cheng 
6178275SEric Cheng 		/*
6188275SEric Cheng 		 * Set the function to start receiving packets.
6198275SEric Cheng 		 */
6208275SEric Cheng 		mac_rx_set(dlp->dl_mch, i_dls_link_rx, dlp);
6215895Syz147064 	}
6225895Syz147064 	dlp->dl_nactive++;
6238275SEric Cheng 	return (0);
6245895Syz147064 }
6255895Syz147064 
6265895Syz147064 void
6275895Syz147064 dls_mac_active_clear(dls_link_t *dlp)
6285895Syz147064 {
6298275SEric Cheng 	if (--dlp->dl_nactive == 0) {
6308275SEric Cheng 		ASSERT(dlp->dl_mah != NULL);
6318275SEric Cheng 		(void) mac_unicast_remove(dlp->dl_mch, dlp->dl_mah);
6328275SEric Cheng 		dlp->dl_mah = NULL;
6338275SEric Cheng 		mac_rx_clear(dlp->dl_mch);
6348275SEric Cheng 	}
6355895Syz147064 }
6365895Syz147064 
6378275SEric Cheng int
6388275SEric Cheng dls_active_set(dld_str_t *dsp)
6390Sstevel@tonic-gate {
6408275SEric Cheng 	int err = 0;
6410Sstevel@tonic-gate 
6428275SEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	/* If we're already active, then there's nothing more to do. */
6458275SEric Cheng 	if (dsp->ds_active)
6468275SEric Cheng 		return (0);
6478275SEric Cheng 
6488275SEric Cheng 	if ((err = dls_mac_active_set(dsp->ds_dlp)) != 0) {
6498275SEric Cheng 		/* except for ENXIO all other errors are mapped to EBUSY */
6508275SEric Cheng 		if (err != ENXIO)
6518275SEric Cheng 			return (EBUSY);
6528275SEric Cheng 		return (err);
6530Sstevel@tonic-gate 	}
6540Sstevel@tonic-gate 
6558275SEric Cheng 	dsp->ds_active = B_TRUE;
6568275SEric Cheng 	return (0);
6570Sstevel@tonic-gate }
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate void
6608275SEric Cheng dls_active_clear(dld_str_t *dsp)
6610Sstevel@tonic-gate {
6628275SEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
6630Sstevel@tonic-gate 
6648275SEric Cheng 	if (!dsp->ds_active)
6658275SEric Cheng 		return;
6660Sstevel@tonic-gate 
6678275SEric Cheng 	dls_mac_active_clear(dsp->ds_dlp);
6688275SEric Cheng 	dsp->ds_active = B_FALSE;
6690Sstevel@tonic-gate }
670