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
dls_open(dls_link_t * dlp,dls_dl_handle_t ddh,dld_str_t * dsp)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
499073SCathy.Zhou@Sun.COM /*
509073SCathy.Zhou@Sun.COM * mac_start() is required for non-legacy MACs to show accurate
519073SCathy.Zhou@Sun.COM * kstats even before the interface is brought up. For legacy
529073SCathy.Zhou@Sun.COM * drivers, this is not needed. Further, calling mac_start() for
539073SCathy.Zhou@Sun.COM * legacy drivers would make the shared-lower-stream to stay in
549073SCathy.Zhou@Sun.COM * the DL_IDLE state, which in turn causes performance regression.
559073SCathy.Zhou@Sun.COM */
569073SCathy.Zhou@Sun.COM if (!mac_capab_get(dlp->dl_mh, MAC_CAPAB_LEGACY, NULL) &&
579073SCathy.Zhou@Sun.COM ((err = mac_start(dlp->dl_mh)) != 0)) {
588893SMichael.Lim@Sun.COM return (err);
599073SCathy.Zhou@Sun.COM }
608893SMichael.Lim@Sun.COM
618275SEric Cheng local = (zid == dlp->dl_zid);
628275SEric Cheng dlp->dl_zone_ref += (local ? 1 : 0);
635895Syz147064
648275SEric Cheng /*
658275SEric Cheng * Cache a copy of the MAC interface handle, a pointer to the
668275SEric Cheng * immutable MAC info.
678275SEric Cheng */
688275SEric Cheng dsp->ds_dlp = dlp;
698275SEric Cheng dsp->ds_mh = dlp->dl_mh;
708275SEric Cheng dsp->ds_mch = dlp->dl_mch;
718275SEric Cheng dsp->ds_mip = dlp->dl_mip;
728275SEric Cheng dsp->ds_ddh = ddh;
738275SEric Cheng dsp->ds_local = local;
740Sstevel@tonic-gate
758275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
760Sstevel@tonic-gate return (0);
770Sstevel@tonic-gate }
780Sstevel@tonic-gate
798275SEric Cheng void
dls_close(dld_str_t * dsp)808275SEric Cheng dls_close(dld_str_t *dsp)
810Sstevel@tonic-gate {
828275SEric Cheng dls_link_t *dlp = dsp->ds_dlp;
838275SEric Cheng dls_multicst_addr_t *p;
848275SEric Cheng dls_multicst_addr_t *nextp;
858275SEric Cheng uint32_t old_flags;
860Sstevel@tonic-gate
878275SEric Cheng ASSERT(dsp->ds_datathr_cnt == 0);
888275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
890Sstevel@tonic-gate
908275SEric Cheng if (dsp->ds_local)
918275SEric Cheng dlp->dl_zone_ref--;
928275SEric Cheng dsp->ds_local = B_FALSE;
930Sstevel@tonic-gate
948275SEric Cheng /*
958275SEric Cheng * Walk the list of multicast addresses, disabling each at the MAC.
968275SEric Cheng * Note that we must remove multicast address before
978275SEric Cheng * mac_unicast_remove() (called by dls_active_clear()) because
988275SEric Cheng * mac_multicast_remove() relies on the unicast flows on the mac
998275SEric Cheng * client.
1008275SEric Cheng */
1018275SEric Cheng for (p = dsp->ds_dmap; p != NULL; p = nextp) {
1028275SEric Cheng (void) mac_multicast_remove(dsp->ds_mch, p->dma_addr);
1038275SEric Cheng nextp = p->dma_nextp;
1048275SEric Cheng kmem_free(p, sizeof (dls_multicst_addr_t));
1050Sstevel@tonic-gate }
1068275SEric Cheng dsp->ds_dmap = NULL;
1078275SEric Cheng
1089073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_TRUE);
1090Sstevel@tonic-gate
1108275SEric Cheng /*
1118275SEric Cheng * If the dld_str_t is bound then unbind it.
1128275SEric Cheng */
1138275SEric Cheng if (dsp->ds_dlstate == DL_IDLE) {
1149044SGirish.Moodalbail@Sun.COM dls_unbind(dsp);
1158275SEric Cheng dsp->ds_dlstate = DL_UNBOUND;
1161184Skrgopi }
1171184Skrgopi
1188275SEric Cheng /*
1198275SEric Cheng * If the MAC has been set in promiscuous mode then disable it.
1208275SEric Cheng * This needs to be done before resetting ds_rx.
1218275SEric Cheng */
1228275SEric Cheng old_flags = dsp->ds_promisc;
1238275SEric Cheng dsp->ds_promisc = 0;
1248275SEric Cheng (void) dls_promisc(dsp, old_flags);
1250Sstevel@tonic-gate
1260Sstevel@tonic-gate /*
1278275SEric Cheng * At this point we have cutoff inbound packet flow from the mac
1288275SEric Cheng * for this 'dsp'. The dls_link_remove above cut off packets meant
1298275SEric Cheng * for us and waited for upcalls to finish. Similarly the dls_promisc
1308275SEric Cheng * reset above waited for promisc callbacks to finish. Now we can
1318275SEric Cheng * safely reset ds_rx to NULL
1320Sstevel@tonic-gate */
1338275SEric Cheng dsp->ds_rx = NULL;
1348275SEric Cheng dsp->ds_rx_arg = NULL;
1358275SEric Cheng
1368275SEric Cheng dsp->ds_dlp = NULL;
1378275SEric Cheng
1389073SCathy.Zhou@Sun.COM if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_LEGACY, NULL))
1399073SCathy.Zhou@Sun.COM mac_stop(dsp->ds_mh);
1408893SMichael.Lim@Sun.COM
1418275SEric Cheng /*
1428275SEric Cheng * Release our reference to the dls_link_t allowing that to be
1438275SEric Cheng * destroyed if there are no more dls_impl_t.
1448275SEric Cheng */
1458275SEric Cheng dls_link_rele(dlp);
1460Sstevel@tonic-gate }
1470Sstevel@tonic-gate
1480Sstevel@tonic-gate int
dls_bind(dld_str_t * dsp,uint32_t sap)1498275SEric Cheng dls_bind(dld_str_t *dsp, uint32_t sap)
1500Sstevel@tonic-gate {
1518275SEric Cheng uint32_t dls_sap;
1528275SEric Cheng
1538275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
1548275SEric Cheng
1550Sstevel@tonic-gate /*
1568275SEric Cheng * Check to see the value is legal for the media type.
1570Sstevel@tonic-gate */
1588275SEric Cheng if (!mac_sap_verify(dsp->ds_mh, sap, &dls_sap))
1598275SEric Cheng return (EINVAL);
1608275SEric Cheng
1618275SEric Cheng if (dsp->ds_promisc & DLS_PROMISC_SAP)
1628275SEric Cheng dls_sap = DLS_SAP_PROMISC;
1638275SEric Cheng
1648275SEric Cheng /*
1658275SEric Cheng * Set up the dld_str_t to mark it as able to receive packets.
1668275SEric Cheng */
1678275SEric Cheng dsp->ds_sap = sap;
1680Sstevel@tonic-gate
1690Sstevel@tonic-gate /*
1708275SEric Cheng * The MAC layer does the VLAN demultiplexing and will only pass up
1718275SEric Cheng * untagged packets to non-promiscuous primary MAC clients. In order to
1728275SEric Cheng * support the binding to the VLAN SAP which is required by DLPI, dls
1738275SEric Cheng * needs to get a copy of all tagged packets when the client binds to
1748275SEric Cheng * the VLAN SAP. We do this by registering a separate promiscuous
1758275SEric Cheng * callback for each dls client binding to that SAP.
1768275SEric Cheng *
1778275SEric Cheng * Note: even though there are two promiscuous handles in dld_str_t,
1788275SEric Cheng * ds_mph is for the regular promiscuous mode, ds_vlan_mph is the handle
1798275SEric Cheng * to receive VLAN pkt when promiscuous mode is not on. Only one of
1808275SEric Cheng * them can be non-NULL at the same time, to avoid receiving dup copies
1818275SEric Cheng * of pkts.
1820Sstevel@tonic-gate */
1838275SEric Cheng if (sap == ETHERTYPE_VLAN && dsp->ds_promisc == 0) {
1848275SEric Cheng int err;
1858275SEric Cheng
1868275SEric Cheng if (dsp->ds_vlan_mph != NULL)
1878275SEric Cheng return (EINVAL);
1888275SEric Cheng err = mac_promisc_add(dsp->ds_mch,
1898275SEric Cheng MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
1908275SEric Cheng &dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
191*11021SEric.Cheng@Sun.COM
192*11021SEric.Cheng@Sun.COM if (err == 0 && dsp->ds_nonip &&
193*11021SEric.Cheng@Sun.COM dsp->ds_dlp->dl_nonip_cnt++ == 0)
194*11021SEric.Cheng@Sun.COM mac_rx_bypass_disable(dsp->ds_mch);
195*11021SEric.Cheng@Sun.COM
1968275SEric Cheng return (err);
1978275SEric Cheng }
1988275SEric Cheng
1998275SEric Cheng /*
2008275SEric Cheng * Now bind the dld_str_t by adding it into the hash table in the
2018275SEric Cheng * dls_link_t.
2028275SEric Cheng */
2038275SEric Cheng dls_link_add(dsp->ds_dlp, dls_sap, dsp);
204*11021SEric.Cheng@Sun.COM if (dsp->ds_nonip && dsp->ds_dlp->dl_nonip_cnt++ == 0)
205*11021SEric.Cheng@Sun.COM mac_rx_bypass_disable(dsp->ds_mch);
206*11021SEric.Cheng@Sun.COM
2070Sstevel@tonic-gate return (0);
2080Sstevel@tonic-gate }
2090Sstevel@tonic-gate
2109044SGirish.Moodalbail@Sun.COM void
dls_unbind(dld_str_t * dsp)2118275SEric Cheng dls_unbind(dld_str_t *dsp)
2120Sstevel@tonic-gate {
2138275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
2145895Syz147064
215*11021SEric.Cheng@Sun.COM if (dsp->ds_nonip && --dsp->ds_dlp->dl_nonip_cnt == 0)
216*11021SEric.Cheng@Sun.COM mac_rx_bypass_enable(dsp->ds_mch);
217*11021SEric.Cheng@Sun.COM
2185895Syz147064 /*
2198275SEric Cheng * For VLAN SAP, there was a promisc handle registered when dls_bind.
2208275SEric Cheng * When unbind this dls link, we need to remove the promisc handle.
2218275SEric Cheng * See comments in dls_bind().
2225895Syz147064 */
2238275SEric Cheng if (dsp->ds_vlan_mph != NULL) {
2249044SGirish.Moodalbail@Sun.COM mac_promisc_remove(dsp->ds_vlan_mph);
2258275SEric Cheng dsp->ds_vlan_mph = NULL;
2269044SGirish.Moodalbail@Sun.COM return;
2278275SEric Cheng }
2285895Syz147064
2298275SEric Cheng /*
2308275SEric Cheng * Unbind the dld_str_t by removing it from the hash table in the
2318275SEric Cheng * dls_link_t.
2328275SEric Cheng */
2338275SEric Cheng dls_link_remove(dsp->ds_dlp, dsp);
2348275SEric Cheng dsp->ds_sap = 0;
2350Sstevel@tonic-gate }
2360Sstevel@tonic-gate
2370Sstevel@tonic-gate int
dls_promisc(dld_str_t * dsp,uint32_t old_flags)2388275SEric Cheng dls_promisc(dld_str_t *dsp, uint32_t old_flags)
2390Sstevel@tonic-gate {
2408275SEric Cheng int err = 0;
2418275SEric Cheng
2428275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
2438275SEric Cheng ASSERT(!(dsp->ds_promisc & ~(DLS_PROMISC_SAP | DLS_PROMISC_MULTI |
2448275SEric Cheng DLS_PROMISC_PHYS)));
2458275SEric Cheng
2468275SEric Cheng if (old_flags == 0 && dsp->ds_promisc != 0) {
2478275SEric Cheng /*
2488275SEric Cheng * If only DLS_PROMISC_SAP, we don't turn on the
2498275SEric Cheng * physical promisc mode
2508275SEric Cheng */
2518275SEric Cheng err = mac_promisc_add(dsp->ds_mch, MAC_CLIENT_PROMISC_ALL,
2528275SEric Cheng dls_rx_promisc, dsp, &dsp->ds_mph,
2538275SEric Cheng (dsp->ds_promisc != DLS_PROMISC_SAP) ? 0 :
2548275SEric Cheng MAC_PROMISC_FLAGS_NO_PHYS);
2558275SEric Cheng if (err != 0)
2568275SEric Cheng return (err);
2570Sstevel@tonic-gate
2588275SEric Cheng /* Remove vlan promisc handle to avoid sending dup copy up */
2598275SEric Cheng if (dsp->ds_vlan_mph != NULL) {
2609044SGirish.Moodalbail@Sun.COM mac_promisc_remove(dsp->ds_vlan_mph);
2618275SEric Cheng dsp->ds_vlan_mph = NULL;
2628275SEric Cheng }
2638275SEric Cheng } else if (old_flags != 0 && dsp->ds_promisc == 0) {
2648275SEric Cheng ASSERT(dsp->ds_mph != NULL);
2659044SGirish.Moodalbail@Sun.COM
2669044SGirish.Moodalbail@Sun.COM mac_promisc_remove(dsp->ds_mph);
2678275SEric Cheng dsp->ds_mph = NULL;
2680Sstevel@tonic-gate
2698275SEric Cheng if (dsp->ds_sap == ETHERTYPE_VLAN &&
2708275SEric Cheng dsp->ds_dlstate != DL_UNBOUND) {
2718275SEric Cheng int err;
2728275SEric Cheng
2738275SEric Cheng if (dsp->ds_vlan_mph != NULL)
2748275SEric Cheng return (EINVAL);
2758275SEric Cheng err = mac_promisc_add(dsp->ds_mch,
2768275SEric Cheng MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
2778275SEric Cheng &dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
2788275SEric Cheng return (err);
2798275SEric Cheng }
2808275SEric Cheng } else if (old_flags == DLS_PROMISC_SAP && dsp->ds_promisc != 0 &&
2818275SEric Cheng dsp->ds_promisc != old_flags) {
2828275SEric Cheng /*
2838275SEric Cheng * If the old flag is PROMISC_SAP, but the current flag has
2848275SEric Cheng * changed to some new non-zero value, we need to turn the
2858275SEric Cheng * physical promiscuous mode.
2868275SEric Cheng */
2878275SEric Cheng ASSERT(dsp->ds_mph != NULL);
2889044SGirish.Moodalbail@Sun.COM mac_promisc_remove(dsp->ds_mph);
2898275SEric Cheng err = mac_promisc_add(dsp->ds_mch, MAC_CLIENT_PROMISC_ALL,
2908275SEric Cheng dls_rx_promisc, dsp, &dsp->ds_mph, 0);
2915895Syz147064 }
2925895Syz147064
2935895Syz147064 return (err);
2945895Syz147064 }
2955895Syz147064
2968275SEric Cheng int
dls_multicst_add(dld_str_t * dsp,const uint8_t * addr)2978275SEric Cheng dls_multicst_add(dld_str_t *dsp, const uint8_t *addr)
2980Sstevel@tonic-gate {
2990Sstevel@tonic-gate int err;
3000Sstevel@tonic-gate dls_multicst_addr_t **pp;
3010Sstevel@tonic-gate dls_multicst_addr_t *p;
3020Sstevel@tonic-gate uint_t addr_length;
3030Sstevel@tonic-gate
3048275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
3058275SEric Cheng
3060Sstevel@tonic-gate /*
3070Sstevel@tonic-gate * Check whether the address is in the list of enabled addresses for
3088275SEric Cheng * this dld_str_t.
3090Sstevel@tonic-gate */
3108275SEric Cheng addr_length = dsp->ds_mip->mi_addr_length;
3118275SEric Cheng
3128275SEric Cheng /*
3138275SEric Cheng * Protect against concurrent access of ds_dmap by data threads using
3148275SEric Cheng * ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
3158275SEric Cheng * remove operations. Dropping the ds_rw_lock across mac calls is thus
3168275SEric Cheng * ok and is also required by the locking protocol.
3178275SEric Cheng */
3188275SEric Cheng rw_enter(&dsp->ds_rw_lock, RW_WRITER);
3198275SEric Cheng for (pp = &(dsp->ds_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
3200Sstevel@tonic-gate if (bcmp(addr, p->dma_addr, addr_length) == 0) {
3210Sstevel@tonic-gate /*
3220Sstevel@tonic-gate * It is there so there's nothing to do.
3230Sstevel@tonic-gate */
3240Sstevel@tonic-gate err = 0;
3250Sstevel@tonic-gate goto done;
3260Sstevel@tonic-gate }
3270Sstevel@tonic-gate }
3280Sstevel@tonic-gate
3290Sstevel@tonic-gate /*
3308275SEric Cheng * Allocate a new list item and add it to the list.
3310Sstevel@tonic-gate */
3328275SEric Cheng p = kmem_zalloc(sizeof (dls_multicst_addr_t), KM_SLEEP);
3338275SEric Cheng bcopy(addr, p->dma_addr, addr_length);
3348275SEric Cheng *pp = p;
3358275SEric Cheng rw_exit(&dsp->ds_rw_lock);
3360Sstevel@tonic-gate
3370Sstevel@tonic-gate /*
3380Sstevel@tonic-gate * Enable the address at the MAC.
3390Sstevel@tonic-gate */
3408275SEric Cheng err = mac_multicast_add(dsp->ds_mch, addr);
3418275SEric Cheng if (err == 0)
3428275SEric Cheng return (0);
3430Sstevel@tonic-gate
3448275SEric Cheng /* Undo the operation as it has failed */
3458275SEric Cheng rw_enter(&dsp->ds_rw_lock, RW_WRITER);
3468275SEric Cheng ASSERT(*pp == p && p->dma_nextp == NULL);
3478275SEric Cheng *pp = NULL;
3488275SEric Cheng kmem_free(p, sizeof (dls_multicst_addr_t));
3490Sstevel@tonic-gate done:
3508275SEric Cheng rw_exit(&dsp->ds_rw_lock);
3510Sstevel@tonic-gate return (err);
3520Sstevel@tonic-gate }
3530Sstevel@tonic-gate
3540Sstevel@tonic-gate int
dls_multicst_remove(dld_str_t * dsp,const uint8_t * addr)3558275SEric Cheng dls_multicst_remove(dld_str_t *dsp, const uint8_t *addr)
3560Sstevel@tonic-gate {
3570Sstevel@tonic-gate dls_multicst_addr_t **pp;
3580Sstevel@tonic-gate dls_multicst_addr_t *p;
3590Sstevel@tonic-gate uint_t addr_length;
3600Sstevel@tonic-gate
3618275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
3628275SEric Cheng
3630Sstevel@tonic-gate /*
3640Sstevel@tonic-gate * Find the address in the list of enabled addresses for this
3658275SEric Cheng * dld_str_t.
3660Sstevel@tonic-gate */
3678275SEric Cheng addr_length = dsp->ds_mip->mi_addr_length;
3688275SEric Cheng
3698275SEric Cheng /*
3708275SEric Cheng * Protect against concurrent access to ds_dmap by data threads using
3718275SEric Cheng * ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
3728275SEric Cheng * remove operations. Dropping the ds_rw_lock across mac calls is thus
3738275SEric Cheng * ok and is also required by the locking protocol.
3748275SEric Cheng */
3758275SEric Cheng rw_enter(&dsp->ds_rw_lock, RW_WRITER);
3768275SEric Cheng for (pp = &(dsp->ds_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
3770Sstevel@tonic-gate if (bcmp(addr, p->dma_addr, addr_length) == 0)
3780Sstevel@tonic-gate break;
3790Sstevel@tonic-gate }
3800Sstevel@tonic-gate
3810Sstevel@tonic-gate /*
3820Sstevel@tonic-gate * If we walked to the end of the list then the given address is
3838275SEric Cheng * not currently enabled for this dld_str_t.
3840Sstevel@tonic-gate */
3850Sstevel@tonic-gate if (p == NULL) {
3868275SEric Cheng rw_exit(&dsp->ds_rw_lock);
3878275SEric Cheng return (ENOENT);
3880Sstevel@tonic-gate }
3890Sstevel@tonic-gate
3900Sstevel@tonic-gate /*
3910Sstevel@tonic-gate * Remove the address from the list.
3920Sstevel@tonic-gate */
3930Sstevel@tonic-gate *pp = p->dma_nextp;
3948275SEric Cheng rw_exit(&dsp->ds_rw_lock);
3950Sstevel@tonic-gate
3968275SEric Cheng /*
3978275SEric Cheng * Disable the address at the MAC.
3988275SEric Cheng */
3998275SEric Cheng mac_multicast_remove(dsp->ds_mch, addr);
4008275SEric Cheng kmem_free(p, sizeof (dls_multicst_addr_t));
4018275SEric Cheng return (0);
4020Sstevel@tonic-gate }
4030Sstevel@tonic-gate
4040Sstevel@tonic-gate mblk_t *
dls_header(dld_str_t * dsp,const uint8_t * addr,uint16_t sap,uint_t pri,mblk_t ** payloadp)4058275SEric Cheng dls_header(dld_str_t *dsp, const uint8_t *addr, uint16_t sap, uint_t pri,
4062760Sdg199075 mblk_t **payloadp)
4070Sstevel@tonic-gate {
4082760Sdg199075 uint16_t vid;
4092760Sdg199075 size_t extra_len;
4102760Sdg199075 uint16_t mac_sap;
4112760Sdg199075 mblk_t *mp, *payload;
4128275SEric Cheng boolean_t is_ethernet = (dsp->ds_mip->mi_media == DL_ETHER);
4132311Sseb struct ether_vlan_header *evhp;
4140Sstevel@tonic-gate
4158275SEric Cheng vid = mac_client_vid(dsp->ds_mch);
4162760Sdg199075 payload = (payloadp == NULL) ? NULL : (*payloadp);
4172760Sdg199075
4182760Sdg199075 /*
4198874SSebastien.Roy@Sun.COM * In the case of Ethernet, we need to tell mac_header() if we need
4208874SSebastien.Roy@Sun.COM * extra room beyond the Ethernet header for a VLAN header. We'll
4218874SSebastien.Roy@Sun.COM * need to add a VLAN header if this isn't an ETHERTYPE_VLAN listener
4228874SSebastien.Roy@Sun.COM * (because such streams will be handling VLAN headers on their own)
4238874SSebastien.Roy@Sun.COM * and one of the following conditions is satisfied:
4242760Sdg199075 *
4258874SSebastien.Roy@Sun.COM * - This is a VLAN stream
4268874SSebastien.Roy@Sun.COM * - This is a physical stream, the priority is not 0, and user
4278874SSebastien.Roy@Sun.COM * priority tagging is allowed.
4282760Sdg199075 */
4292760Sdg199075 if (is_ethernet && sap != ETHERTYPE_VLAN &&
4308874SSebastien.Roy@Sun.COM (vid != VLAN_ID_NONE ||
4318874SSebastien.Roy@Sun.COM (pri != 0 && dsp->ds_dlp->dl_tagmode != LINK_TAGMODE_VLANONLY))) {
4322311Sseb extra_len = sizeof (struct ether_vlan_header) -
4332311Sseb sizeof (struct ether_header);
4342760Sdg199075 mac_sap = ETHERTYPE_VLAN;
4352311Sseb } else {
4362311Sseb extra_len = 0;
4372311Sseb mac_sap = sap;
4382311Sseb }
4392311Sseb
4408275SEric Cheng mp = mac_header(dsp->ds_mh, addr, mac_sap, payload, extra_len);
4412760Sdg199075 if (mp == NULL)
4422760Sdg199075 return (NULL);
4432760Sdg199075
4448874SSebastien.Roy@Sun.COM if ((vid == VLAN_ID_NONE && (pri == 0 ||
4458874SSebastien.Roy@Sun.COM dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_VLANONLY)) || !is_ethernet)
4462311Sseb return (mp);
4472311Sseb
4482760Sdg199075 /*
4492760Sdg199075 * Fill in the tag information.
4502760Sdg199075 */
4512311Sseb ASSERT(MBLKL(mp) == sizeof (struct ether_header));
4522760Sdg199075 if (extra_len != 0) {
4532760Sdg199075 mp->b_wptr += extra_len;
4542760Sdg199075 evhp = (struct ether_vlan_header *)mp->b_rptr;
4552760Sdg199075 evhp->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI, vid));
4562760Sdg199075 evhp->ether_type = htons(sap);
4572760Sdg199075 } else {
4582760Sdg199075 /*
4592760Sdg199075 * The stream is ETHERTYPE_VLAN listener, so its VLAN tag is
4602760Sdg199075 * in the payload. Update the priority.
4612760Sdg199075 */
4622760Sdg199075 struct ether_vlan_extinfo *extinfo;
4632760Sdg199075 size_t len = sizeof (struct ether_vlan_extinfo);
4642760Sdg199075
4652760Sdg199075 ASSERT(sap == ETHERTYPE_VLAN);
4662760Sdg199075 ASSERT(payload != NULL);
4672760Sdg199075
4682760Sdg199075 if ((DB_REF(payload) > 1) || (MBLKL(payload) < len)) {
4692760Sdg199075 mblk_t *newmp;
4702760Sdg199075
4712760Sdg199075 /*
4722760Sdg199075 * Because some DLS consumers only check the db_ref
4732760Sdg199075 * count of the first mblk, we pullup 'payload' into
4742760Sdg199075 * a single mblk.
4752760Sdg199075 */
4762760Sdg199075 newmp = msgpullup(payload, -1);
4772760Sdg199075 if ((newmp == NULL) || (MBLKL(newmp) < len)) {
4782760Sdg199075 freemsg(newmp);
4792760Sdg199075 freemsg(mp);
4802760Sdg199075 return (NULL);
4812760Sdg199075 } else {
4822760Sdg199075 freemsg(payload);
4832760Sdg199075 *payloadp = payload = newmp;
4842760Sdg199075 }
4852760Sdg199075 }
4862760Sdg199075
4872760Sdg199075 extinfo = (struct ether_vlan_extinfo *)payload->b_rptr;
4882760Sdg199075 extinfo->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI,
4892760Sdg199075 VLAN_ID(ntohs(extinfo->ether_tci))));
4902760Sdg199075 }
4912311Sseb return (mp);
4920Sstevel@tonic-gate }
4930Sstevel@tonic-gate
4940Sstevel@tonic-gate void
dls_rx_set(dld_str_t * dsp,dls_rx_t rx,void * arg)4958275SEric Cheng dls_rx_set(dld_str_t *dsp, dls_rx_t rx, void *arg)
4960Sstevel@tonic-gate {
4978275SEric Cheng mutex_enter(&dsp->ds_lock);
4988275SEric Cheng dsp->ds_rx = rx;
4998275SEric Cheng dsp->ds_rx_arg = arg;
5008275SEric Cheng mutex_exit(&dsp->ds_lock);
5010Sstevel@tonic-gate }
5020Sstevel@tonic-gate
5038275SEric Cheng static boolean_t
dls_accept_common(dld_str_t * dsp,mac_header_info_t * mhip,dls_rx_t * ds_rx,void ** ds_rx_arg,boolean_t promisc,boolean_t promisc_loopback)5048275SEric Cheng dls_accept_common(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
5058275SEric Cheng void **ds_rx_arg, boolean_t promisc, boolean_t promisc_loopback)
5060Sstevel@tonic-gate {
5070Sstevel@tonic-gate dls_multicst_addr_t *dmap;
5088275SEric Cheng size_t addr_length = dsp->ds_mip->mi_addr_length;
5090Sstevel@tonic-gate
5100Sstevel@tonic-gate /*
5118275SEric Cheng * We must not accept packets if the dld_str_t is not marked as bound
5120Sstevel@tonic-gate * or is being removed.
5130Sstevel@tonic-gate */
5148275SEric Cheng if (dsp->ds_dlstate != DL_IDLE)
5150Sstevel@tonic-gate goto refuse;
5160Sstevel@tonic-gate
5178275SEric Cheng if (dsp->ds_promisc != 0) {
5188275SEric Cheng /*
5198275SEric Cheng * Filter out packets that arrived from the data path
5208275SEric Cheng * (i_dls_link_rx) when promisc mode is on.
5218275SEric Cheng */
5228275SEric Cheng if (!promisc)
5238275SEric Cheng goto refuse;
5248275SEric Cheng /*
5258275SEric Cheng * If the dls_impl_t is in 'all physical' mode then
5268275SEric Cheng * always accept.
5278275SEric Cheng */
5288275SEric Cheng if (dsp->ds_promisc & DLS_PROMISC_PHYS)
5298275SEric Cheng goto accept;
5300Sstevel@tonic-gate
5318275SEric Cheng /*
5328275SEric Cheng * Loopback packets i.e. packets sent out by DLS on a given
5338275SEric Cheng * mac end point, will be accepted back by DLS on loopback
5348275SEric Cheng * from the mac, only in the 'all physical' mode which has been
5358275SEric Cheng * covered by the previous check above
5368275SEric Cheng */
5378275SEric Cheng if (promisc_loopback)
5388275SEric Cheng goto refuse;
5398275SEric Cheng }
5405895Syz147064
5412311Sseb switch (mhip->mhi_dsttype) {
5422311Sseb case MAC_ADDRTYPE_UNICAST:
5438275SEric Cheng case MAC_ADDRTYPE_BROADCAST:
5442311Sseb /*
5458275SEric Cheng * We can accept unicast and broadcast packets because
5468275SEric Cheng * filtering is already done by the mac layer.
5472311Sseb */
5488275SEric Cheng goto accept;
5492311Sseb case MAC_ADDRTYPE_MULTICAST:
5502311Sseb /*
5518275SEric Cheng * Additional filtering is needed for multicast addresses
5528275SEric Cheng * because different streams may be interested in different
5538275SEric Cheng * addresses.
5542311Sseb */
5558275SEric Cheng if (dsp->ds_promisc & DLS_PROMISC_MULTI)
5562311Sseb goto accept;
5578275SEric Cheng
5588275SEric Cheng rw_enter(&dsp->ds_rw_lock, RW_READER);
5598275SEric Cheng for (dmap = dsp->ds_dmap; dmap != NULL;
5602311Sseb dmap = dmap->dma_nextp) {
5612311Sseb if (memcmp(mhip->mhi_daddr, dmap->dma_addr,
5622311Sseb addr_length) == 0) {
5638275SEric Cheng rw_exit(&dsp->ds_rw_lock);
5642311Sseb goto accept;
5652311Sseb }
5662311Sseb }
5678275SEric Cheng rw_exit(&dsp->ds_rw_lock);
5682311Sseb break;
5690Sstevel@tonic-gate }
5700Sstevel@tonic-gate
5710Sstevel@tonic-gate refuse:
5720Sstevel@tonic-gate return (B_FALSE);
5730Sstevel@tonic-gate
5740Sstevel@tonic-gate accept:
575449Sericheng /*
5768275SEric Cheng * the returned ds_rx and ds_rx_arg will always be in sync.
577449Sericheng */
5788275SEric Cheng mutex_enter(&dsp->ds_lock);
5798275SEric Cheng *ds_rx = dsp->ds_rx;
5808275SEric Cheng *ds_rx_arg = dsp->ds_rx_arg;
5818275SEric Cheng mutex_exit(&dsp->ds_lock);
5828275SEric Cheng
5830Sstevel@tonic-gate return (B_TRUE);
5840Sstevel@tonic-gate }
5850Sstevel@tonic-gate
5862760Sdg199075 /* ARGSUSED */
5870Sstevel@tonic-gate boolean_t
dls_accept(dld_str_t * dsp,mac_header_info_t * mhip,dls_rx_t * ds_rx,void ** ds_rx_arg)5888275SEric Cheng dls_accept(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
5898275SEric Cheng void **ds_rx_arg)
5900Sstevel@tonic-gate {
5918275SEric Cheng return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_FALSE,
5928275SEric Cheng B_FALSE));
5930Sstevel@tonic-gate }
5940Sstevel@tonic-gate
5950Sstevel@tonic-gate boolean_t
dls_accept_promisc(dld_str_t * dsp,mac_header_info_t * mhip,dls_rx_t * ds_rx,void ** ds_rx_arg,boolean_t loopback)5968275SEric Cheng dls_accept_promisc(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
5978275SEric Cheng void **ds_rx_arg, boolean_t loopback)
5988275SEric Cheng {
5998275SEric Cheng return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_TRUE,
6008275SEric Cheng loopback));
6018275SEric Cheng }
6028275SEric Cheng
6038275SEric Cheng int
dls_mac_active_set(dls_link_t * dlp)6045895Syz147064 dls_mac_active_set(dls_link_t *dlp)
6055895Syz147064 {
6068275SEric Cheng int err = 0;
6075895Syz147064
6085895Syz147064 /*
6098275SEric Cheng * First client; add the primary unicast address.
6105895Syz147064 */
6118275SEric Cheng if (dlp->dl_nactive == 0) {
6128275SEric Cheng /*
6138275SEric Cheng * First client; add the primary unicast address.
6148275SEric Cheng */
6158275SEric Cheng mac_diag_t diag;
6168275SEric Cheng
6178275SEric Cheng /* request the primary MAC address */
6189024SVenu.Iyer@Sun.COM if ((err = mac_unicast_add(dlp->dl_mch, NULL,
6199024SVenu.Iyer@Sun.COM MAC_UNICAST_PRIMARY | MAC_UNICAST_TAG_DISABLE |
6209024SVenu.Iyer@Sun.COM MAC_UNICAST_DISABLE_TX_VID_CHECK, &dlp->dl_mah, 0,
6218275SEric Cheng &diag)) != 0) {
6228275SEric Cheng return (err);
6238275SEric Cheng }
6248275SEric Cheng
6258275SEric Cheng /*
6268275SEric Cheng * Set the function to start receiving packets.
6278275SEric Cheng */
6288275SEric Cheng mac_rx_set(dlp->dl_mch, i_dls_link_rx, dlp);
6295895Syz147064 }
6305895Syz147064 dlp->dl_nactive++;
6318275SEric Cheng return (0);
6325895Syz147064 }
6335895Syz147064
6345895Syz147064 void
dls_mac_active_clear(dls_link_t * dlp)6355895Syz147064 dls_mac_active_clear(dls_link_t *dlp)
6365895Syz147064 {
6378275SEric Cheng if (--dlp->dl_nactive == 0) {
6388275SEric Cheng ASSERT(dlp->dl_mah != NULL);
6398275SEric Cheng (void) mac_unicast_remove(dlp->dl_mch, dlp->dl_mah);
6408275SEric Cheng dlp->dl_mah = NULL;
6418275SEric Cheng mac_rx_clear(dlp->dl_mch);
6428275SEric Cheng }
6435895Syz147064 }
6445895Syz147064
6458275SEric Cheng int
dls_active_set(dld_str_t * dsp)6468275SEric Cheng dls_active_set(dld_str_t *dsp)
6470Sstevel@tonic-gate {
6488275SEric Cheng int err = 0;
6490Sstevel@tonic-gate
6508275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
6510Sstevel@tonic-gate
6529073SCathy.Zhou@Sun.COM if (dsp->ds_passivestate == DLD_PASSIVE)
6538275SEric Cheng return (0);
6548275SEric Cheng
6559073SCathy.Zhou@Sun.COM /* If we're already active, then there's nothing more to do. */
6569073SCathy.Zhou@Sun.COM if ((dsp->ds_nactive == 0) &&
6579073SCathy.Zhou@Sun.COM ((err = dls_mac_active_set(dsp->ds_dlp)) != 0)) {
6588275SEric Cheng /* except for ENXIO all other errors are mapped to EBUSY */
6598275SEric Cheng if (err != ENXIO)
6608275SEric Cheng return (EBUSY);
6618275SEric Cheng return (err);
6620Sstevel@tonic-gate }
6630Sstevel@tonic-gate
6649073SCathy.Zhou@Sun.COM dsp->ds_passivestate = DLD_ACTIVE;
6659073SCathy.Zhou@Sun.COM dsp->ds_nactive++;
6668275SEric Cheng return (0);
6670Sstevel@tonic-gate }
6680Sstevel@tonic-gate
6699073SCathy.Zhou@Sun.COM /*
6709073SCathy.Zhou@Sun.COM * Note that dls_active_set() is called whenever an active operation
6719073SCathy.Zhou@Sun.COM * (DL_BIND_REQ, DL_ENABMULTI_REQ ...) is processed and
6729073SCathy.Zhou@Sun.COM * dls_active_clear(dsp, B_FALSE) is called whenever the active operation
6739073SCathy.Zhou@Sun.COM * is being undone (DL_UNBIND_REQ, DL_DISABMULTI_REQ ...). In some cases,
6749073SCathy.Zhou@Sun.COM * a stream is closed without every active operation being undone and we
6759073SCathy.Zhou@Sun.COM * need to clear all the "active" states by calling
6769073SCathy.Zhou@Sun.COM * dls_active_clear(dsp, B_TRUE).
6779073SCathy.Zhou@Sun.COM */
6780Sstevel@tonic-gate void
dls_active_clear(dld_str_t * dsp,boolean_t all)6799073SCathy.Zhou@Sun.COM dls_active_clear(dld_str_t *dsp, boolean_t all)
6800Sstevel@tonic-gate {
6818275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
6820Sstevel@tonic-gate
6839073SCathy.Zhou@Sun.COM if (dsp->ds_passivestate == DLD_PASSIVE)
6849073SCathy.Zhou@Sun.COM return;
6859073SCathy.Zhou@Sun.COM
6869073SCathy.Zhou@Sun.COM if (all && dsp->ds_nactive == 0)
6878275SEric Cheng return;
6880Sstevel@tonic-gate
6899073SCathy.Zhou@Sun.COM ASSERT(dsp->ds_nactive > 0);
6909073SCathy.Zhou@Sun.COM
6919073SCathy.Zhou@Sun.COM dsp->ds_nactive -= (all ? dsp->ds_nactive : 1);
6929073SCathy.Zhou@Sun.COM if (dsp->ds_nactive != 0)
6939073SCathy.Zhou@Sun.COM return;
6949073SCathy.Zhou@Sun.COM
6959073SCathy.Zhou@Sun.COM ASSERT(dsp->ds_passivestate == DLD_ACTIVE);
6968275SEric Cheng dls_mac_active_clear(dsp->ds_dlp);
6979073SCathy.Zhou@Sun.COM dsp->ds_passivestate = DLD_UNINITIALIZED;
6980Sstevel@tonic-gate }
699