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