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 /* 2211474SJonathan.Adams@Sun.COM * Copyright 2010 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 Driver 280Sstevel@tonic-gate */ 290Sstevel@tonic-gate #include <sys/sysmacros.h> 308275SEric Cheng #include <sys/strsubr.h> 310Sstevel@tonic-gate #include <sys/strsun.h> 320Sstevel@tonic-gate #include <sys/vlan.h> 330Sstevel@tonic-gate #include <sys/dld_impl.h> 348275SEric Cheng #include <sys/mac_client.h> 358275SEric Cheng #include <sys/mac_client_impl.h> 368275SEric Cheng #include <sys/mac_client_priv.h> 370Sstevel@tonic-gate 388275SEric Cheng typedef void proto_reqfunc_t(dld_str_t *, mblk_t *); 390Sstevel@tonic-gate 400Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req, 410Sstevel@tonic-gate proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req, 420Sstevel@tonic-gate proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req, 430Sstevel@tonic-gate proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req, 445895Syz147064 proto_notify_req, proto_passive_req; 450Sstevel@tonic-gate 468275SEric Cheng static void proto_capability_advertise(dld_str_t *, mblk_t *); 478275SEric Cheng static int dld_capab_poll_disable(dld_str_t *, dld_capab_poll_t *); 4811021SEric.Cheng@Sun.COM static boolean_t check_mod_above(queue_t *, const char *); 491184Skrgopi 500Sstevel@tonic-gate #define DL_ACK_PENDING(state) \ 510Sstevel@tonic-gate ((state) == DL_ATTACH_PENDING || \ 520Sstevel@tonic-gate (state) == DL_DETACH_PENDING || \ 530Sstevel@tonic-gate (state) == DL_BIND_PENDING || \ 540Sstevel@tonic-gate (state) == DL_UNBIND_PENDING) 550Sstevel@tonic-gate 560Sstevel@tonic-gate /* 57269Sericheng * Process a DLPI protocol message. 58269Sericheng * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ, 59269Sericheng * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an 60269Sericheng * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t 61269Sericheng * as 'passive' and forbids it from being subsequently made 'active' 62269Sericheng * by the above primitives. 630Sstevel@tonic-gate */ 640Sstevel@tonic-gate void 658275SEric Cheng dld_proto(dld_str_t *dsp, mblk_t *mp) 660Sstevel@tonic-gate { 670Sstevel@tonic-gate t_uscalar_t prim; 680Sstevel@tonic-gate 698275SEric Cheng if (MBLKL(mp) < sizeof (t_uscalar_t)) { 708275SEric Cheng freemsg(mp); 718275SEric Cheng return; 728275SEric Cheng } 738275SEric Cheng prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive; 740Sstevel@tonic-gate 75269Sericheng switch (prim) { 76269Sericheng case DL_INFO_REQ: 778275SEric Cheng proto_info_req(dsp, mp); 78269Sericheng break; 79269Sericheng case DL_BIND_REQ: 808275SEric Cheng proto_bind_req(dsp, mp); 81269Sericheng break; 82269Sericheng case DL_UNBIND_REQ: 838275SEric Cheng proto_unbind_req(dsp, mp); 848275SEric Cheng break; 858275SEric Cheng case DL_UNITDATA_REQ: 868275SEric Cheng proto_unitdata_req(dsp, mp); 87269Sericheng break; 88269Sericheng case DL_UDQOS_REQ: 898275SEric Cheng proto_udqos_req(dsp, mp); 90269Sericheng break; 91269Sericheng case DL_ATTACH_REQ: 928275SEric Cheng proto_attach_req(dsp, mp); 93269Sericheng break; 94269Sericheng case DL_DETACH_REQ: 958275SEric Cheng proto_detach_req(dsp, mp); 96269Sericheng break; 97269Sericheng case DL_ENABMULTI_REQ: 988275SEric Cheng proto_enabmulti_req(dsp, mp); 99269Sericheng break; 100269Sericheng case DL_DISABMULTI_REQ: 1018275SEric Cheng proto_disabmulti_req(dsp, mp); 102269Sericheng break; 103269Sericheng case DL_PROMISCON_REQ: 1048275SEric Cheng proto_promiscon_req(dsp, mp); 105269Sericheng break; 106269Sericheng case DL_PROMISCOFF_REQ: 1078275SEric Cheng proto_promiscoff_req(dsp, mp); 108269Sericheng break; 109269Sericheng case DL_PHYS_ADDR_REQ: 1108275SEric Cheng proto_physaddr_req(dsp, mp); 111269Sericheng break; 112269Sericheng case DL_SET_PHYS_ADDR_REQ: 1138275SEric Cheng proto_setphysaddr_req(dsp, mp); 114269Sericheng break; 115269Sericheng case DL_NOTIFY_REQ: 1168275SEric Cheng proto_notify_req(dsp, mp); 117269Sericheng break; 118269Sericheng case DL_CAPABILITY_REQ: 1198275SEric Cheng proto_capability_req(dsp, mp); 120269Sericheng break; 121269Sericheng case DL_PASSIVE_REQ: 1228275SEric Cheng proto_passive_req(dsp, mp); 123269Sericheng break; 124269Sericheng default: 1258275SEric Cheng proto_req(dsp, mp); 126269Sericheng break; 1270Sstevel@tonic-gate } 1280Sstevel@tonic-gate } 1290Sstevel@tonic-gate 130269Sericheng #define NEG(x) -(x) 1310Sstevel@tonic-gate typedef struct dl_info_ack_wrapper { 1320Sstevel@tonic-gate dl_info_ack_t dl_info; 1332311Sseb uint8_t dl_addr[MAXMACADDRLEN + sizeof (uint16_t)]; 1342311Sseb uint8_t dl_brdcst_addr[MAXMACADDRLEN]; 1350Sstevel@tonic-gate dl_qos_cl_range1_t dl_qos_range1; 1360Sstevel@tonic-gate dl_qos_cl_sel1_t dl_qos_sel1; 1370Sstevel@tonic-gate } dl_info_ack_wrapper_t; 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate /* 140269Sericheng * DL_INFO_REQ 1410Sstevel@tonic-gate */ 1428275SEric Cheng static void 1438275SEric Cheng proto_info_req(dld_str_t *dsp, mblk_t *mp) 1440Sstevel@tonic-gate { 1450Sstevel@tonic-gate dl_info_ack_wrapper_t *dlwp; 1460Sstevel@tonic-gate dl_info_ack_t *dlp; 1470Sstevel@tonic-gate dl_qos_cl_sel1_t *selp; 1480Sstevel@tonic-gate dl_qos_cl_range1_t *rangep; 1490Sstevel@tonic-gate uint8_t *addr; 1500Sstevel@tonic-gate uint8_t *brdcst_addr; 1510Sstevel@tonic-gate uint_t addr_length; 1520Sstevel@tonic-gate uint_t sap_length; 153269Sericheng mac_info_t minfo; 154269Sericheng mac_info_t *minfop; 155269Sericheng queue_t *q = dsp->ds_wq; 1560Sstevel@tonic-gate 1570Sstevel@tonic-gate /* 1580Sstevel@tonic-gate * Swap the request message for one large enough to contain the 1590Sstevel@tonic-gate * wrapper structure defined above. 1600Sstevel@tonic-gate */ 161269Sericheng if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t), 1620Sstevel@tonic-gate M_PCPROTO, 0)) == NULL) 1638275SEric Cheng return; 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t)); 1660Sstevel@tonic-gate dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr; 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate dlp = &(dlwp->dl_info); 1690Sstevel@tonic-gate ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr); 1700Sstevel@tonic-gate 1710Sstevel@tonic-gate dlp->dl_primitive = DL_INFO_ACK; 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate /* 1740Sstevel@tonic-gate * Set up the sub-structure pointers. 1750Sstevel@tonic-gate */ 1760Sstevel@tonic-gate addr = dlwp->dl_addr; 1770Sstevel@tonic-gate brdcst_addr = dlwp->dl_brdcst_addr; 1780Sstevel@tonic-gate rangep = &(dlwp->dl_qos_range1); 1790Sstevel@tonic-gate selp = &(dlwp->dl_qos_sel1); 1800Sstevel@tonic-gate 1810Sstevel@tonic-gate /* 1820Sstevel@tonic-gate * This driver supports only version 2 connectionless DLPI provider 1830Sstevel@tonic-gate * nodes. 1840Sstevel@tonic-gate */ 1850Sstevel@tonic-gate dlp->dl_service_mode = DL_CLDLS; 1860Sstevel@tonic-gate dlp->dl_version = DL_VERSION_2; 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate /* 189269Sericheng * Set the style of the provider 1900Sstevel@tonic-gate */ 191269Sericheng dlp->dl_provider_style = dsp->ds_style; 1920Sstevel@tonic-gate ASSERT(dlp->dl_provider_style == DL_STYLE1 || 1930Sstevel@tonic-gate dlp->dl_provider_style == DL_STYLE2); 1940Sstevel@tonic-gate 1950Sstevel@tonic-gate /* 1960Sstevel@tonic-gate * Set the current DLPI state. 1970Sstevel@tonic-gate */ 1980Sstevel@tonic-gate dlp->dl_current_state = dsp->ds_dlstate; 1990Sstevel@tonic-gate 2000Sstevel@tonic-gate /* 201269Sericheng * Gratuitously set the media type. This is to deal with modules 202269Sericheng * that assume the media type is known prior to DL_ATTACH_REQ 2030Sstevel@tonic-gate * being completed. 2040Sstevel@tonic-gate */ 2050Sstevel@tonic-gate dlp->dl_mac_type = DL_ETHER; 2060Sstevel@tonic-gate 2070Sstevel@tonic-gate /* 208269Sericheng * If the stream is not at least attached we try to retrieve the 209269Sericheng * mac_info using mac_info_get() 2100Sstevel@tonic-gate */ 2110Sstevel@tonic-gate if (dsp->ds_dlstate == DL_UNATTACHED || 2120Sstevel@tonic-gate dsp->ds_dlstate == DL_ATTACH_PENDING || 213269Sericheng dsp->ds_dlstate == DL_DETACH_PENDING) { 214269Sericheng if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) { 215269Sericheng /* 216269Sericheng * Cannot find mac_info. giving up. 217269Sericheng */ 218269Sericheng goto done; 219269Sericheng } 220269Sericheng minfop = &minfo; 221269Sericheng } else { 222269Sericheng minfop = (mac_info_t *)dsp->ds_mip; 2235903Ssowmini /* We can only get the sdu if we're attached. */ 2245903Ssowmini mac_sdu_get(dsp->ds_mh, &dlp->dl_min_sdu, &dlp->dl_max_sdu); 225269Sericheng } 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate /* 2280Sstevel@tonic-gate * Set the media type (properly this time). 2290Sstevel@tonic-gate */ 2303147Sxc151355 if (dsp->ds_native) 2313147Sxc151355 dlp->dl_mac_type = minfop->mi_nativemedia; 2323147Sxc151355 else 2333147Sxc151355 dlp->dl_mac_type = minfop->mi_media; 2340Sstevel@tonic-gate 2350Sstevel@tonic-gate /* 2360Sstevel@tonic-gate * Set the DLSAP length. We only support 16 bit values and they 2370Sstevel@tonic-gate * appear after the MAC address portion of DLSAP addresses. 2380Sstevel@tonic-gate */ 2390Sstevel@tonic-gate sap_length = sizeof (uint16_t); 2400Sstevel@tonic-gate dlp->dl_sap_length = NEG(sap_length); 2410Sstevel@tonic-gate 242269Sericheng addr_length = minfop->mi_addr_length; 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate /* 2450Sstevel@tonic-gate * Copy in the media broadcast address. 2460Sstevel@tonic-gate */ 2472311Sseb if (minfop->mi_brdcst_addr != NULL) { 2482311Sseb dlp->dl_brdcst_addr_offset = 2492311Sseb (uintptr_t)brdcst_addr - (uintptr_t)dlp; 2502311Sseb bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length); 2512311Sseb dlp->dl_brdcst_addr_length = addr_length; 2522311Sseb } 2530Sstevel@tonic-gate 2548874SSebastien.Roy@Sun.COM /* Only VLAN links and links that have a normal tag mode support QOS. */ 2558968SSebastien.Roy@Sun.COM if ((dsp->ds_mch != NULL && 2568968SSebastien.Roy@Sun.COM mac_client_vid(dsp->ds_mch) != VLAN_ID_NONE) || 2578968SSebastien.Roy@Sun.COM (dsp->ds_dlp != NULL && 2588968SSebastien.Roy@Sun.COM dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_NORMAL)) { 2598874SSebastien.Roy@Sun.COM dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp; 2608874SSebastien.Roy@Sun.COM dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t); 2610Sstevel@tonic-gate 2628874SSebastien.Roy@Sun.COM rangep->dl_qos_type = DL_QOS_CL_RANGE1; 2638874SSebastien.Roy@Sun.COM rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN; 2648874SSebastien.Roy@Sun.COM rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN; 2658874SSebastien.Roy@Sun.COM rangep->dl_protection.dl_min = DL_UNKNOWN; 2668874SSebastien.Roy@Sun.COM rangep->dl_protection.dl_max = DL_UNKNOWN; 2678874SSebastien.Roy@Sun.COM rangep->dl_residual_error = DL_UNKNOWN; 2680Sstevel@tonic-gate 2698874SSebastien.Roy@Sun.COM /* 2708874SSebastien.Roy@Sun.COM * Specify the supported range of priorities. 2718874SSebastien.Roy@Sun.COM */ 2728874SSebastien.Roy@Sun.COM rangep->dl_priority.dl_min = 0; 2738874SSebastien.Roy@Sun.COM rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1; 2740Sstevel@tonic-gate 2758874SSebastien.Roy@Sun.COM dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp; 2768874SSebastien.Roy@Sun.COM dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t); 2770Sstevel@tonic-gate 2788874SSebastien.Roy@Sun.COM selp->dl_qos_type = DL_QOS_CL_SEL1; 2798874SSebastien.Roy@Sun.COM selp->dl_trans_delay = DL_UNKNOWN; 2808874SSebastien.Roy@Sun.COM selp->dl_protection = DL_UNKNOWN; 2818874SSebastien.Roy@Sun.COM selp->dl_residual_error = DL_UNKNOWN; 2822760Sdg199075 2838874SSebastien.Roy@Sun.COM /* 2848874SSebastien.Roy@Sun.COM * Specify the current priority (which can be changed by 2858874SSebastien.Roy@Sun.COM * the DL_UDQOS_REQ primitive). 2868874SSebastien.Roy@Sun.COM */ 2878874SSebastien.Roy@Sun.COM selp->dl_priority = dsp->ds_pri; 2888874SSebastien.Roy@Sun.COM } 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate dlp->dl_addr_length = addr_length + sizeof (uint16_t); 2910Sstevel@tonic-gate if (dsp->ds_dlstate == DL_IDLE) { 2920Sstevel@tonic-gate /* 2930Sstevel@tonic-gate * The stream is bound. Therefore we can formulate a valid 2940Sstevel@tonic-gate * DLSAP address. 2950Sstevel@tonic-gate */ 2960Sstevel@tonic-gate dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp; 2972311Sseb if (addr_length > 0) 2988275SEric Cheng mac_unicast_primary_get(dsp->ds_mh, addr); 2998275SEric Cheng 3000Sstevel@tonic-gate *(uint16_t *)(addr + addr_length) = dsp->ds_sap; 3010Sstevel@tonic-gate } 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate done: 30411474SJonathan.Adams@Sun.COM IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0); 30511474SJonathan.Adams@Sun.COM IMPLY(dlp->dl_qos_range_offset != 0, 30611474SJonathan.Adams@Sun.COM dlp->dl_qos_range_length != 0); 30711474SJonathan.Adams@Sun.COM IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0); 30811474SJonathan.Adams@Sun.COM IMPLY(dlp->dl_brdcst_addr_offset != 0, 30911474SJonathan.Adams@Sun.COM dlp->dl_brdcst_addr_length != 0); 3100Sstevel@tonic-gate 311269Sericheng qreply(q, mp); 312269Sericheng } 313269Sericheng 314269Sericheng /* 315269Sericheng * DL_ATTACH_REQ 316269Sericheng */ 3178275SEric Cheng static void 3188275SEric Cheng proto_attach_req(dld_str_t *dsp, mblk_t *mp) 319269Sericheng { 3208275SEric Cheng dl_attach_req_t *dlp = (dl_attach_req_t *)mp->b_rptr; 321269Sericheng int err = 0; 322269Sericheng t_uscalar_t dl_err; 323269Sericheng queue_t *q = dsp->ds_wq; 324269Sericheng 325269Sericheng if (MBLKL(mp) < sizeof (dl_attach_req_t) || 326269Sericheng dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) { 327269Sericheng dl_err = DL_BADPRIM; 328269Sericheng goto failed; 329269Sericheng } 330269Sericheng 331269Sericheng if (dsp->ds_dlstate != DL_UNATTACHED) { 332269Sericheng dl_err = DL_OUTSTATE; 333269Sericheng goto failed; 334269Sericheng } 335269Sericheng 336269Sericheng dsp->ds_dlstate = DL_ATTACH_PENDING; 337269Sericheng 338269Sericheng err = dld_str_attach(dsp, dlp->dl_ppa); 339269Sericheng if (err != 0) { 340269Sericheng switch (err) { 341269Sericheng case ENOENT: 342269Sericheng dl_err = DL_BADPPA; 343269Sericheng err = 0; 344269Sericheng break; 345269Sericheng default: 346269Sericheng dl_err = DL_SYSERR; 347269Sericheng break; 348269Sericheng } 349269Sericheng dsp->ds_dlstate = DL_UNATTACHED; 350269Sericheng goto failed; 351269Sericheng } 352269Sericheng ASSERT(dsp->ds_dlstate == DL_UNBOUND); 3538275SEric Cheng dlokack(q, mp, DL_ATTACH_REQ); 3548275SEric Cheng return; 355269Sericheng 356269Sericheng failed: 357269Sericheng dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err); 3580Sstevel@tonic-gate } 3590Sstevel@tonic-gate 3608275SEric Cheng /* 3618275SEric Cheng * DL_DETACH_REQ 3628275SEric Cheng */ 3638275SEric Cheng static void 3648275SEric Cheng proto_detach_req(dld_str_t *dsp, mblk_t *mp) 3650Sstevel@tonic-gate { 366269Sericheng queue_t *q = dsp->ds_wq; 367269Sericheng t_uscalar_t dl_err; 3680Sstevel@tonic-gate 369269Sericheng if (MBLKL(mp) < sizeof (dl_detach_req_t)) { 370269Sericheng dl_err = DL_BADPRIM; 371269Sericheng goto failed; 372269Sericheng } 3730Sstevel@tonic-gate 374269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 375269Sericheng dl_err = DL_OUTSTATE; 376269Sericheng goto failed; 3770Sstevel@tonic-gate } 3780Sstevel@tonic-gate 379269Sericheng if (dsp->ds_style == DL_STYLE1) { 380269Sericheng dl_err = DL_BADPRIM; 381269Sericheng goto failed; 382269Sericheng } 383269Sericheng 3848275SEric Cheng ASSERT(dsp->ds_datathr_cnt == 0); 385269Sericheng dsp->ds_dlstate = DL_DETACH_PENDING; 3868275SEric Cheng 3875895Syz147064 dld_str_detach(dsp); 3885895Syz147064 dlokack(dsp->ds_wq, mp, DL_DETACH_REQ); 3898275SEric Cheng return; 3908275SEric Cheng 391269Sericheng failed: 392269Sericheng dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0); 3930Sstevel@tonic-gate } 3940Sstevel@tonic-gate 3950Sstevel@tonic-gate /* 396269Sericheng * DL_BIND_REQ 3970Sstevel@tonic-gate */ 3988275SEric Cheng static void 3998275SEric Cheng proto_bind_req(dld_str_t *dsp, mblk_t *mp) 4000Sstevel@tonic-gate { 4018275SEric Cheng dl_bind_req_t *dlp = (dl_bind_req_t *)mp->b_rptr; 402269Sericheng int err = 0; 4033037Syz147064 uint8_t dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)]; 4043037Syz147064 uint_t dlsap_addr_length; 405269Sericheng t_uscalar_t dl_err; 406269Sericheng t_scalar_t sap; 407269Sericheng queue_t *q = dsp->ds_wq; 4088275SEric Cheng mac_perim_handle_t mph; 4098275SEric Cheng void *mdip; 4108275SEric Cheng int32_t intr_cpu; 411269Sericheng 412269Sericheng if (MBLKL(mp) < sizeof (dl_bind_req_t)) { 413269Sericheng dl_err = DL_BADPRIM; 414269Sericheng goto failed; 415269Sericheng } 416269Sericheng 417269Sericheng if (dlp->dl_xidtest_flg != 0) { 418269Sericheng dl_err = DL_NOAUTO; 419269Sericheng goto failed; 420269Sericheng } 421269Sericheng 422269Sericheng if (dlp->dl_service_mode != DL_CLDLS) { 423269Sericheng dl_err = DL_UNSUPPORTED; 424269Sericheng goto failed; 425269Sericheng } 426269Sericheng 427269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 428269Sericheng dl_err = DL_OUTSTATE; 429269Sericheng goto failed; 430269Sericheng } 4310Sstevel@tonic-gate 4328275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 4338275SEric Cheng 4349073SCathy.Zhou@Sun.COM if ((err = dls_active_set(dsp)) != 0) { 435269Sericheng dl_err = DL_SYSERR; 4368275SEric Cheng goto failed2; 437269Sericheng } 438269Sericheng 4398275SEric Cheng dsp->ds_dlstate = DL_BIND_PENDING; 440269Sericheng /* 441269Sericheng * Set the receive callback. 442269Sericheng */ 4438275SEric Cheng dls_rx_set(dsp, (dsp->ds_mode == DLD_RAW) ? 444269Sericheng dld_str_rx_raw : dld_str_rx_unitdata, dsp); 4450Sstevel@tonic-gate 446269Sericheng /* 447269Sericheng * Bind the channel such that it can receive packets. 448269Sericheng */ 4495895Syz147064 sap = dlp->dl_sap; 45011021SEric.Cheng@Sun.COM dsp->ds_nonip = !check_mod_above(dsp->ds_rq, "ip") && 45111021SEric.Cheng@Sun.COM !check_mod_above(dsp->ds_rq, "arp"); 45211021SEric.Cheng@Sun.COM 4538275SEric Cheng err = dls_bind(dsp, sap); 454269Sericheng if (err != 0) { 455269Sericheng switch (err) { 456269Sericheng case EINVAL: 457269Sericheng dl_err = DL_BADADDR; 458269Sericheng err = 0; 459269Sericheng break; 460269Sericheng default: 461269Sericheng dl_err = DL_SYSERR; 462269Sericheng break; 463269Sericheng } 4645895Syz147064 4658275SEric Cheng dsp->ds_dlstate = DL_UNBOUND; 4669073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_FALSE); 4678275SEric Cheng goto failed2; 4688275SEric Cheng } 469269Sericheng 4708275SEric Cheng intr_cpu = mac_client_intr_cpu(dsp->ds_mch); 4718275SEric Cheng mdip = mac_get_devinfo(dsp->ds_mh); 4728275SEric Cheng mac_perim_exit(mph); 4738275SEric Cheng 4748275SEric Cheng /* 4758275SEric Cheng * We do this after we get out of the perim to avoid deadlocks 4768275SEric Cheng * etc. since part of mac_client_retarget_intr is to walk the 4778275SEric Cheng * device tree in order to find and retarget the interrupts. 4788275SEric Cheng */ 479*11878SVenu.Iyer@Sun.COM if (intr_cpu != -1) 480*11878SVenu.Iyer@Sun.COM mac_client_set_intr_cpu(mdip, dsp->ds_mch, intr_cpu); 4810Sstevel@tonic-gate 4820Sstevel@tonic-gate /* 4830Sstevel@tonic-gate * Copy in MAC address. 4840Sstevel@tonic-gate */ 4853037Syz147064 dlsap_addr_length = dsp->ds_mip->mi_addr_length; 4868275SEric Cheng mac_unicast_primary_get(dsp->ds_mh, dlsap_addr); 4870Sstevel@tonic-gate 4880Sstevel@tonic-gate /* 4893037Syz147064 * Copy in the SAP. 4900Sstevel@tonic-gate */ 4915895Syz147064 *(uint16_t *)(dlsap_addr + dlsap_addr_length) = sap; 4923037Syz147064 dlsap_addr_length += sizeof (uint16_t); 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate dsp->ds_dlstate = DL_IDLE; 4953037Syz147064 dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0); 4968275SEric Cheng return; 4978275SEric Cheng 4988275SEric Cheng failed2: 4998275SEric Cheng mac_perim_exit(mph); 5000Sstevel@tonic-gate failed: 501269Sericheng dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err); 5020Sstevel@tonic-gate } 5030Sstevel@tonic-gate 5040Sstevel@tonic-gate /* 505269Sericheng * DL_UNBIND_REQ 5060Sstevel@tonic-gate */ 5078275SEric Cheng static void 5088275SEric Cheng proto_unbind_req(dld_str_t *dsp, mblk_t *mp) 5090Sstevel@tonic-gate { 5105895Syz147064 queue_t *q = dsp->ds_wq; 5115895Syz147064 t_uscalar_t dl_err; 5128275SEric Cheng mac_perim_handle_t mph; 513269Sericheng 5145895Syz147064 if (MBLKL(mp) < sizeof (dl_unbind_req_t)) { 5155895Syz147064 dl_err = DL_BADPRIM; 5165895Syz147064 goto failed; 5175895Syz147064 } 5185895Syz147064 5195895Syz147064 if (dsp->ds_dlstate != DL_IDLE) { 5205895Syz147064 dl_err = DL_OUTSTATE; 5215895Syz147064 goto failed; 5225895Syz147064 } 523269Sericheng 5248275SEric Cheng mutex_enter(&dsp->ds_lock); 5258275SEric Cheng while (dsp->ds_datathr_cnt != 0) 5268275SEric Cheng cv_wait(&dsp->ds_datathr_cv, &dsp->ds_lock); 527269Sericheng 5288275SEric Cheng dsp->ds_dlstate = DL_UNBIND_PENDING; 5298275SEric Cheng mutex_exit(&dsp->ds_lock); 5308275SEric Cheng 5318275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 532269Sericheng /* 533269Sericheng * Unbind the channel to stop packets being received. 534269Sericheng */ 5359044SGirish.Moodalbail@Sun.COM dls_unbind(dsp); 5365895Syz147064 5375895Syz147064 /* 538269Sericheng * Disable polling mode, if it is enabled. 539269Sericheng */ 5408275SEric Cheng (void) dld_capab_poll_disable(dsp, NULL); 5415895Syz147064 5425895Syz147064 /* 5433115Syl150051 * Clear LSO flags. 5443115Syl150051 */ 5453115Syl150051 dsp->ds_lso = B_FALSE; 5463115Syl150051 dsp->ds_lso_max = 0; 5473115Syl150051 5483115Syl150051 /* 5498275SEric Cheng * Clear the receive callback. 5508275SEric Cheng */ 5518275SEric Cheng dls_rx_set(dsp, NULL, NULL); 5528275SEric Cheng dsp->ds_direct = B_FALSE; 5538275SEric Cheng 5548275SEric Cheng /* 555269Sericheng * Set the mode back to the default (unitdata). 556269Sericheng */ 557269Sericheng dsp->ds_mode = DLD_UNITDATA; 5581353Sericheng dsp->ds_dlstate = DL_UNBOUND; 5591353Sericheng 5609073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_FALSE); 5618275SEric Cheng mac_perim_exit(mph); 5628275SEric Cheng dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ); 5638275SEric Cheng return; 564269Sericheng failed: 565269Sericheng dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0); 5660Sstevel@tonic-gate } 5670Sstevel@tonic-gate 5680Sstevel@tonic-gate /* 569269Sericheng * DL_PROMISCON_REQ 5700Sstevel@tonic-gate */ 5718275SEric Cheng static void 5728275SEric Cheng proto_promiscon_req(dld_str_t *dsp, mblk_t *mp) 5730Sstevel@tonic-gate { 5748275SEric Cheng dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)mp->b_rptr; 575269Sericheng int err = 0; 576269Sericheng t_uscalar_t dl_err; 5778275SEric Cheng uint32_t promisc_saved; 578269Sericheng queue_t *q = dsp->ds_wq; 5798275SEric Cheng mac_perim_handle_t mph; 580269Sericheng 581269Sericheng if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) { 582269Sericheng dl_err = DL_BADPRIM; 583269Sericheng goto failed; 584269Sericheng } 585269Sericheng 586269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 587269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 588269Sericheng dl_err = DL_OUTSTATE; 5890Sstevel@tonic-gate goto failed; 590269Sericheng } 5910Sstevel@tonic-gate 5928275SEric Cheng promisc_saved = dsp->ds_promisc; 593269Sericheng switch (dlp->dl_level) { 594269Sericheng case DL_PROMISC_SAP: 5958275SEric Cheng dsp->ds_promisc |= DLS_PROMISC_SAP; 596269Sericheng break; 5978275SEric Cheng 5985895Syz147064 case DL_PROMISC_MULTI: 5998275SEric Cheng dsp->ds_promisc |= DLS_PROMISC_MULTI; 6005895Syz147064 break; 6018275SEric Cheng 602269Sericheng case DL_PROMISC_PHYS: 6038275SEric Cheng dsp->ds_promisc |= DLS_PROMISC_PHYS; 604269Sericheng break; 6058275SEric Cheng 606269Sericheng default: 607269Sericheng dl_err = DL_NOTSUPPORTED; 608269Sericheng goto failed; 609269Sericheng } 6100Sstevel@tonic-gate 6118275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 6128275SEric Cheng 6139073SCathy.Zhou@Sun.COM if ((promisc_saved == 0) && (err = dls_active_set(dsp)) != 0) { 6148275SEric Cheng dsp->ds_promisc = promisc_saved; 615269Sericheng dl_err = DL_SYSERR; 6168275SEric Cheng goto failed2; 617269Sericheng } 618269Sericheng 619269Sericheng /* 620269Sericheng * Adjust channel promiscuity. 621269Sericheng */ 6228275SEric Cheng err = dls_promisc(dsp, promisc_saved); 6238275SEric Cheng 624269Sericheng if (err != 0) { 625269Sericheng dl_err = DL_SYSERR; 6268275SEric Cheng dsp->ds_promisc = promisc_saved; 6279073SCathy.Zhou@Sun.COM if (promisc_saved == 0) 6289073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_FALSE); 6298275SEric Cheng goto failed2; 630269Sericheng } 631269Sericheng 6328275SEric Cheng mac_perim_exit(mph); 6338275SEric Cheng 6348275SEric Cheng dlokack(q, mp, DL_PROMISCON_REQ); 6358275SEric Cheng return; 636269Sericheng 6378275SEric Cheng failed2: 6388275SEric Cheng mac_perim_exit(mph); 6390Sstevel@tonic-gate failed: 640269Sericheng dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err); 6410Sstevel@tonic-gate } 6420Sstevel@tonic-gate 6430Sstevel@tonic-gate /* 644269Sericheng * DL_PROMISCOFF_REQ 6450Sstevel@tonic-gate */ 6468275SEric Cheng static void 6478275SEric Cheng proto_promiscoff_req(dld_str_t *dsp, mblk_t *mp) 6480Sstevel@tonic-gate { 6498275SEric Cheng dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)mp->b_rptr; 650269Sericheng int err = 0; 651269Sericheng t_uscalar_t dl_err; 6528275SEric Cheng uint32_t promisc_saved; 653269Sericheng queue_t *q = dsp->ds_wq; 6548275SEric Cheng mac_perim_handle_t mph; 655269Sericheng 656269Sericheng if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) { 657269Sericheng dl_err = DL_BADPRIM; 6580Sstevel@tonic-gate goto failed; 659269Sericheng } 6600Sstevel@tonic-gate 661269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 662269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 663269Sericheng dl_err = DL_OUTSTATE; 6640Sstevel@tonic-gate goto failed; 665269Sericheng } 6660Sstevel@tonic-gate 6678275SEric Cheng promisc_saved = dsp->ds_promisc; 668269Sericheng switch (dlp->dl_level) { 669269Sericheng case DL_PROMISC_SAP: 6708275SEric Cheng if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) { 6718275SEric Cheng dl_err = DL_NOTENAB; 6728275SEric Cheng goto failed; 6738275SEric Cheng } 6748275SEric Cheng dsp->ds_promisc &= ~DLS_PROMISC_SAP; 6750Sstevel@tonic-gate break; 6768275SEric Cheng 677269Sericheng case DL_PROMISC_MULTI: 6788275SEric Cheng if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) { 6798275SEric Cheng dl_err = DL_NOTENAB; 6808275SEric Cheng goto failed; 6818275SEric Cheng } 6828275SEric Cheng dsp->ds_promisc &= ~DLS_PROMISC_MULTI; 683269Sericheng break; 6848275SEric Cheng 685269Sericheng case DL_PROMISC_PHYS: 6868275SEric Cheng if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) { 6878275SEric Cheng dl_err = DL_NOTENAB; 6888275SEric Cheng goto failed; 6898275SEric Cheng } 6908275SEric Cheng dsp->ds_promisc &= ~DLS_PROMISC_PHYS; 6910Sstevel@tonic-gate break; 6928275SEric Cheng 6930Sstevel@tonic-gate default: 694269Sericheng dl_err = DL_NOTSUPPORTED; 695269Sericheng goto failed; 696269Sericheng } 697269Sericheng 6988275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 6998275SEric Cheng /* 7008275SEric Cheng * Adjust channel promiscuity. 7018275SEric Cheng */ 7028275SEric Cheng err = dls_promisc(dsp, promisc_saved); 7035895Syz147064 704269Sericheng if (err != 0) { 7059073SCathy.Zhou@Sun.COM mac_perim_exit(mph); 7060Sstevel@tonic-gate dl_err = DL_SYSERR; 707269Sericheng goto failed; 708269Sericheng } 7099073SCathy.Zhou@Sun.COM 7109073SCathy.Zhou@Sun.COM if (dsp->ds_promisc == 0) 7119073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_FALSE); 7129073SCathy.Zhou@Sun.COM 7139073SCathy.Zhou@Sun.COM mac_perim_exit(mph); 7149073SCathy.Zhou@Sun.COM 715269Sericheng dlokack(q, mp, DL_PROMISCOFF_REQ); 7168275SEric Cheng return; 717269Sericheng failed: 718269Sericheng dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err); 719269Sericheng } 720269Sericheng 721269Sericheng /* 722269Sericheng * DL_ENABMULTI_REQ 723269Sericheng */ 7248275SEric Cheng static void 7258275SEric Cheng proto_enabmulti_req(dld_str_t *dsp, mblk_t *mp) 726269Sericheng { 7278275SEric Cheng dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)mp->b_rptr; 728269Sericheng int err = 0; 729269Sericheng t_uscalar_t dl_err; 730269Sericheng queue_t *q = dsp->ds_wq; 7318275SEric Cheng mac_perim_handle_t mph; 732269Sericheng 733269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 734269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 735269Sericheng dl_err = DL_OUTSTATE; 736269Sericheng goto failed; 737269Sericheng } 738269Sericheng 739269Sericheng if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) || 740269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 741269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 742269Sericheng dl_err = DL_BADPRIM; 743269Sericheng goto failed; 744269Sericheng } 745269Sericheng 7468275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 7478275SEric Cheng 7489073SCathy.Zhou@Sun.COM if ((dsp->ds_dmap == NULL) && (err = dls_active_set(dsp)) != 0) { 749269Sericheng dl_err = DL_SYSERR; 7508275SEric Cheng goto failed2; 7510Sstevel@tonic-gate } 7520Sstevel@tonic-gate 7538275SEric Cheng err = dls_multicst_add(dsp, mp->b_rptr + dlp->dl_addr_offset); 754269Sericheng if (err != 0) { 755269Sericheng switch (err) { 756269Sericheng case EINVAL: 757269Sericheng dl_err = DL_BADADDR; 758269Sericheng err = 0; 759269Sericheng break; 760269Sericheng case ENOSPC: 761269Sericheng dl_err = DL_TOOMANY; 762269Sericheng err = 0; 763269Sericheng break; 764269Sericheng default: 765269Sericheng dl_err = DL_SYSERR; 766269Sericheng break; 767269Sericheng } 7689073SCathy.Zhou@Sun.COM if (dsp->ds_dmap == NULL) 7699073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_FALSE); 7708275SEric Cheng goto failed2; 771269Sericheng } 772269Sericheng 7738275SEric Cheng mac_perim_exit(mph); 7748275SEric Cheng 7758275SEric Cheng dlokack(q, mp, DL_ENABMULTI_REQ); 7768275SEric Cheng return; 777269Sericheng 7788275SEric Cheng failed2: 7798275SEric Cheng mac_perim_exit(mph); 780269Sericheng failed: 781269Sericheng dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err); 782269Sericheng } 783269Sericheng 784269Sericheng /* 785269Sericheng * DL_DISABMULTI_REQ 786269Sericheng */ 7878275SEric Cheng static void 7888275SEric Cheng proto_disabmulti_req(dld_str_t *dsp, mblk_t *mp) 789269Sericheng { 7908275SEric Cheng dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)mp->b_rptr; 791269Sericheng int err = 0; 792269Sericheng t_uscalar_t dl_err; 793269Sericheng queue_t *q = dsp->ds_wq; 7948275SEric Cheng mac_perim_handle_t mph; 795269Sericheng 796269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 797269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 798269Sericheng dl_err = DL_OUTSTATE; 799269Sericheng goto failed; 800269Sericheng } 801269Sericheng 802269Sericheng if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) || 803269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 804269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 805269Sericheng dl_err = DL_BADPRIM; 806269Sericheng goto failed; 807269Sericheng } 808269Sericheng 8098275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 8108275SEric Cheng err = dls_multicst_remove(dsp, mp->b_rptr + dlp->dl_addr_offset); 8119073SCathy.Zhou@Sun.COM if ((err == 0) && (dsp->ds_dmap == NULL)) 8129073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_FALSE); 8138275SEric Cheng mac_perim_exit(mph); 8148275SEric Cheng 815269Sericheng if (err != 0) { 8168275SEric Cheng switch (err) { 817269Sericheng case EINVAL: 818269Sericheng dl_err = DL_BADADDR; 819269Sericheng err = 0; 820269Sericheng break; 8218275SEric Cheng 822269Sericheng case ENOENT: 823269Sericheng dl_err = DL_NOTENAB; 824269Sericheng err = 0; 825269Sericheng break; 8268275SEric Cheng 827269Sericheng default: 828269Sericheng dl_err = DL_SYSERR; 829269Sericheng break; 830269Sericheng } 831269Sericheng goto failed; 832269Sericheng } 833269Sericheng dlokack(q, mp, DL_DISABMULTI_REQ); 8348275SEric Cheng return; 835269Sericheng failed: 836269Sericheng dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err); 8370Sstevel@tonic-gate } 8380Sstevel@tonic-gate 8390Sstevel@tonic-gate /* 840269Sericheng * DL_PHYS_ADDR_REQ 8410Sstevel@tonic-gate */ 8428275SEric Cheng static void 8438275SEric Cheng proto_physaddr_req(dld_str_t *dsp, mblk_t *mp) 8440Sstevel@tonic-gate { 8458275SEric Cheng dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)mp->b_rptr; 846269Sericheng queue_t *q = dsp->ds_wq; 84710616SSebastien.Roy@Sun.COM t_uscalar_t dl_err = 0; 84810616SSebastien.Roy@Sun.COM char *addr = NULL; 849269Sericheng uint_t addr_length; 850269Sericheng 851269Sericheng if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) { 852269Sericheng dl_err = DL_BADPRIM; 85310616SSebastien.Roy@Sun.COM goto done; 854269Sericheng } 855269Sericheng 856269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 857269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 858269Sericheng dl_err = DL_OUTSTATE; 85910616SSebastien.Roy@Sun.COM goto done; 860269Sericheng } 8610Sstevel@tonic-gate 862269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 8636353Sdr146992 if (addr_length > 0) { 8648275SEric Cheng addr = kmem_alloc(addr_length, KM_SLEEP); 86510616SSebastien.Roy@Sun.COM switch (dlp->dl_addr_type) { 86610616SSebastien.Roy@Sun.COM case DL_CURR_PHYS_ADDR: 8678275SEric Cheng mac_unicast_primary_get(dsp->ds_mh, (uint8_t *)addr); 86810616SSebastien.Roy@Sun.COM break; 86910616SSebastien.Roy@Sun.COM case DL_FACT_PHYS_ADDR: 8708275SEric Cheng bcopy(dsp->ds_mip->mi_unicst_addr, addr, addr_length); 87110616SSebastien.Roy@Sun.COM break; 87210616SSebastien.Roy@Sun.COM case DL_CURR_DEST_ADDR: 87310616SSebastien.Roy@Sun.COM if (!mac_dst_get(dsp->ds_mh, (uint8_t *)addr)) 87410616SSebastien.Roy@Sun.COM dl_err = DL_NOTSUPPORTED; 87510616SSebastien.Roy@Sun.COM break; 87610616SSebastien.Roy@Sun.COM default: 87710616SSebastien.Roy@Sun.COM dl_err = DL_UNSUPPORTED; 87810616SSebastien.Roy@Sun.COM } 87910616SSebastien.Roy@Sun.COM } 88010616SSebastien.Roy@Sun.COM done: 88110616SSebastien.Roy@Sun.COM if (dl_err == 0) 8826353Sdr146992 dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length); 88310616SSebastien.Roy@Sun.COM else 88410616SSebastien.Roy@Sun.COM dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0); 88510616SSebastien.Roy@Sun.COM if (addr != NULL) 8866353Sdr146992 kmem_free(addr, addr_length); 887269Sericheng } 8880Sstevel@tonic-gate 889269Sericheng /* 890269Sericheng * DL_SET_PHYS_ADDR_REQ 891269Sericheng */ 8928275SEric Cheng static void 8938275SEric Cheng proto_setphysaddr_req(dld_str_t *dsp, mblk_t *mp) 894269Sericheng { 8958275SEric Cheng dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)mp->b_rptr; 896269Sericheng int err = 0; 897269Sericheng t_uscalar_t dl_err; 898269Sericheng queue_t *q = dsp->ds_wq; 8998275SEric Cheng mac_perim_handle_t mph; 9000Sstevel@tonic-gate 901269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 902269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 903269Sericheng dl_err = DL_OUTSTATE; 904269Sericheng goto failed; 905269Sericheng } 906269Sericheng 907269Sericheng if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) || 908269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 909269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 910269Sericheng dl_err = DL_BADPRIM; 911269Sericheng goto failed; 9120Sstevel@tonic-gate } 9130Sstevel@tonic-gate 9148275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph); 9158275SEric Cheng 9169073SCathy.Zhou@Sun.COM if ((err = dls_active_set(dsp)) != 0) { 917269Sericheng dl_err = DL_SYSERR; 9188275SEric Cheng goto failed2; 919269Sericheng } 920269Sericheng 92110734SEric Cheng /* 92210734SEric Cheng * If mac-nospoof is enabled and the link is owned by a 92310734SEric Cheng * non-global zone, changing the mac address is not allowed. 92410734SEric Cheng */ 92510734SEric Cheng if (dsp->ds_dlp->dl_zid != GLOBAL_ZONEID && 92610734SEric Cheng mac_protect_enabled(dsp->ds_mch, MPT_MACNOSPOOF)) { 92710734SEric Cheng dls_active_clear(dsp, B_FALSE); 92810734SEric Cheng err = EACCES; 92910734SEric Cheng goto failed2; 93010734SEric Cheng } 93110734SEric Cheng 9328275SEric Cheng err = mac_unicast_primary_set(dsp->ds_mh, 9338275SEric Cheng mp->b_rptr + dlp->dl_addr_offset); 934269Sericheng if (err != 0) { 935269Sericheng switch (err) { 936269Sericheng case EINVAL: 937269Sericheng dl_err = DL_BADADDR; 938269Sericheng err = 0; 939269Sericheng break; 940269Sericheng 941269Sericheng default: 942269Sericheng dl_err = DL_SYSERR; 943269Sericheng break; 944269Sericheng } 9459073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_FALSE); 9468275SEric Cheng goto failed2; 947269Sericheng 948269Sericheng } 9495895Syz147064 9508275SEric Cheng mac_perim_exit(mph); 9518275SEric Cheng 9528275SEric Cheng dlokack(q, mp, DL_SET_PHYS_ADDR_REQ); 9538275SEric Cheng return; 954269Sericheng 9558275SEric Cheng failed2: 9568275SEric Cheng mac_perim_exit(mph); 957269Sericheng failed: 958269Sericheng dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err); 959269Sericheng } 960269Sericheng 961269Sericheng /* 962269Sericheng * DL_UDQOS_REQ 963269Sericheng */ 9648275SEric Cheng static void 9658275SEric Cheng proto_udqos_req(dld_str_t *dsp, mblk_t *mp) 966269Sericheng { 9678275SEric Cheng dl_udqos_req_t *dlp = (dl_udqos_req_t *)mp->b_rptr; 968269Sericheng dl_qos_cl_sel1_t *selp; 969269Sericheng int off, len; 970269Sericheng t_uscalar_t dl_err; 971269Sericheng queue_t *q = dsp->ds_wq; 972269Sericheng 973269Sericheng off = dlp->dl_qos_offset; 974269Sericheng len = dlp->dl_qos_length; 975269Sericheng 976269Sericheng if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) { 977269Sericheng dl_err = DL_BADPRIM; 978269Sericheng goto failed; 979269Sericheng } 980269Sericheng 981269Sericheng selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off); 982269Sericheng if (selp->dl_qos_type != DL_QOS_CL_SEL1) { 983269Sericheng dl_err = DL_BADQOSTYPE; 984269Sericheng goto failed; 985269Sericheng } 986269Sericheng 9872760Sdg199075 if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 || 988269Sericheng selp->dl_priority < 0) { 989269Sericheng dl_err = DL_BADQOSPARAM; 990269Sericheng goto failed; 991269Sericheng } 992269Sericheng 9935895Syz147064 dsp->ds_pri = selp->dl_priority; 994269Sericheng dlokack(q, mp, DL_UDQOS_REQ); 9958275SEric Cheng return; 996269Sericheng failed: 997269Sericheng dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0); 9980Sstevel@tonic-gate } 9990Sstevel@tonic-gate 10001184Skrgopi static boolean_t 100111021SEric.Cheng@Sun.COM check_mod_above(queue_t *q, const char *mod) 10021184Skrgopi { 10031184Skrgopi queue_t *next_q; 10041184Skrgopi boolean_t ret = B_TRUE; 10051184Skrgopi 10061184Skrgopi claimstr(q); 10071184Skrgopi next_q = q->q_next; 100811021SEric.Cheng@Sun.COM if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, mod) != 0) 10091184Skrgopi ret = B_FALSE; 10101184Skrgopi releasestr(q); 10111184Skrgopi return (ret); 10121184Skrgopi } 10131184Skrgopi 10140Sstevel@tonic-gate /* 1015269Sericheng * DL_CAPABILITY_REQ 10160Sstevel@tonic-gate */ 10178275SEric Cheng static void 10188275SEric Cheng proto_capability_req(dld_str_t *dsp, mblk_t *mp) 10190Sstevel@tonic-gate { 10208275SEric Cheng dl_capability_req_t *dlp = (dl_capability_req_t *)mp->b_rptr; 1021269Sericheng dl_capability_sub_t *sp; 1022269Sericheng size_t size, len; 1023269Sericheng offset_t off, end; 1024269Sericheng t_uscalar_t dl_err; 1025269Sericheng queue_t *q = dsp->ds_wq; 1026269Sericheng 1027269Sericheng if (MBLKL(mp) < sizeof (dl_capability_req_t)) { 1028269Sericheng dl_err = DL_BADPRIM; 1029269Sericheng goto failed; 1030269Sericheng } 1031269Sericheng 1032269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1033269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1034269Sericheng dl_err = DL_OUTSTATE; 1035269Sericheng goto failed; 1036269Sericheng } 1037269Sericheng 1038269Sericheng /* 1039269Sericheng * This request is overloaded. If there are no requested capabilities 1040269Sericheng * then we just want to acknowledge with all the capabilities we 1041269Sericheng * support. Otherwise we enable the set of capabilities requested. 1042269Sericheng */ 1043269Sericheng if (dlp->dl_sub_length == 0) { 10448275SEric Cheng proto_capability_advertise(dsp, mp); 10458275SEric Cheng return; 1046269Sericheng } 1047269Sericheng 1048269Sericheng if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) { 1049269Sericheng dl_err = DL_BADPRIM; 1050269Sericheng goto failed; 1051269Sericheng } 1052269Sericheng 1053269Sericheng dlp->dl_primitive = DL_CAPABILITY_ACK; 1054269Sericheng 1055269Sericheng off = dlp->dl_sub_offset; 1056269Sericheng len = dlp->dl_sub_length; 10570Sstevel@tonic-gate 10580Sstevel@tonic-gate /* 1059269Sericheng * Walk the list of capabilities to be enabled. 10600Sstevel@tonic-gate */ 1061269Sericheng for (end = off + len; off < end; ) { 1062269Sericheng sp = (dl_capability_sub_t *)(mp->b_rptr + off); 1063269Sericheng size = sizeof (dl_capability_sub_t) + sp->dl_length; 1064269Sericheng 1065269Sericheng if (off + size > end || 1066269Sericheng !IS_P2ALIGNED(off, sizeof (uint32_t))) { 1067269Sericheng dl_err = DL_BADPRIM; 1068269Sericheng goto failed; 1069269Sericheng } 1070269Sericheng 1071269Sericheng switch (sp->dl_cap) { 1072269Sericheng /* 1073269Sericheng * TCP/IP checksum offload to hardware. 1074269Sericheng */ 1075269Sericheng case DL_CAPAB_HCKSUM: { 1076269Sericheng dl_capab_hcksum_t *hcksump; 1077269Sericheng dl_capab_hcksum_t hcksum; 1078269Sericheng 1079269Sericheng hcksump = (dl_capab_hcksum_t *)&sp[1]; 1080269Sericheng /* 1081269Sericheng * Copy for alignment. 1082269Sericheng */ 1083269Sericheng bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t)); 1084269Sericheng dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 1085269Sericheng bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t)); 1086269Sericheng break; 1087269Sericheng } 1088269Sericheng 10898275SEric Cheng case DL_CAPAB_DLD: { 10908275SEric Cheng dl_capab_dld_t *dldp; 10918275SEric Cheng dl_capab_dld_t dld; 10923115Syl150051 10938275SEric Cheng dldp = (dl_capab_dld_t *)&sp[1]; 1094269Sericheng /* 1095269Sericheng * Copy for alignment. 1096269Sericheng */ 10978275SEric Cheng bcopy(dldp, &dld, sizeof (dl_capab_dld_t)); 10988275SEric Cheng dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq); 10998275SEric Cheng bcopy(&dld, dldp, sizeof (dl_capab_dld_t)); 1100269Sericheng break; 1101269Sericheng } 1102269Sericheng default: 1103269Sericheng break; 1104269Sericheng } 1105269Sericheng off += size; 1106269Sericheng } 1107269Sericheng qreply(q, mp); 11088275SEric Cheng return; 1109269Sericheng failed: 1110269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0); 11110Sstevel@tonic-gate } 11120Sstevel@tonic-gate 11130Sstevel@tonic-gate /* 1114269Sericheng * DL_NOTIFY_REQ 11150Sstevel@tonic-gate */ 11168275SEric Cheng static void 11178275SEric Cheng proto_notify_req(dld_str_t *dsp, mblk_t *mp) 11180Sstevel@tonic-gate { 11198275SEric Cheng dl_notify_req_t *dlp = (dl_notify_req_t *)mp->b_rptr; 1120269Sericheng t_uscalar_t dl_err; 1121269Sericheng queue_t *q = dsp->ds_wq; 1122269Sericheng uint_t note = 1123269Sericheng DL_NOTE_PROMISC_ON_PHYS | 1124269Sericheng DL_NOTE_PROMISC_OFF_PHYS | 1125269Sericheng DL_NOTE_PHYS_ADDR | 1126269Sericheng DL_NOTE_LINK_UP | 1127269Sericheng DL_NOTE_LINK_DOWN | 11282311Sseb DL_NOTE_CAPAB_RENEG | 11298910SGirish.Moodalbail@Sun.COM DL_NOTE_FASTPATH_FLUSH | 113010616SSebastien.Roy@Sun.COM DL_NOTE_SPEED | 113110616SSebastien.Roy@Sun.COM DL_NOTE_SDU_SIZE; 11320Sstevel@tonic-gate 1133269Sericheng if (MBLKL(mp) < sizeof (dl_notify_req_t)) { 1134269Sericheng dl_err = DL_BADPRIM; 1135269Sericheng goto failed; 1136269Sericheng } 11370Sstevel@tonic-gate 1138269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1139269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1140269Sericheng dl_err = DL_OUTSTATE; 1141269Sericheng goto failed; 11420Sstevel@tonic-gate } 11430Sstevel@tonic-gate 11445895Syz147064 note &= ~(mac_no_notification(dsp->ds_mh)); 11455895Syz147064 1146269Sericheng /* 1147269Sericheng * Cache the notifications that are being enabled. 1148269Sericheng */ 1149269Sericheng dsp->ds_notifications = dlp->dl_notifications & note; 1150269Sericheng /* 1151269Sericheng * The ACK carries all notifications regardless of which set is 1152269Sericheng * being enabled. 1153269Sericheng */ 1154269Sericheng dlnotifyack(q, mp, note); 1155269Sericheng 1156269Sericheng /* 11578275SEric Cheng * Generate DL_NOTIFY_IND messages for each enabled notification. 1158269Sericheng */ 1159269Sericheng if (dsp->ds_notifications != 0) { 1160269Sericheng dld_str_notify_ind(dsp); 1161269Sericheng } 11628275SEric Cheng return; 1163269Sericheng failed: 1164269Sericheng dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0); 11650Sstevel@tonic-gate } 11660Sstevel@tonic-gate 11670Sstevel@tonic-gate /* 11688275SEric Cheng * DL_UINTDATA_REQ 11690Sstevel@tonic-gate */ 11705895Syz147064 void 11718275SEric Cheng proto_unitdata_req(dld_str_t *dsp, mblk_t *mp) 11720Sstevel@tonic-gate { 1173269Sericheng queue_t *q = dsp->ds_wq; 11745895Syz147064 dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_rptr; 1175269Sericheng off_t off; 1176269Sericheng size_t len, size; 1177269Sericheng const uint8_t *addr; 1178269Sericheng uint16_t sap; 1179269Sericheng uint_t addr_length; 11802311Sseb mblk_t *bp, *payload; 1181269Sericheng uint32_t start, stuff, end, value, flags; 1182269Sericheng t_uscalar_t dl_err; 11835903Ssowmini uint_t max_sdu; 1184269Sericheng 1185269Sericheng if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) { 11868275SEric Cheng dlerrorack(q, mp, DL_UNITDATA_REQ, DL_BADPRIM, 0); 11878275SEric Cheng return; 1188269Sericheng } 1189269Sericheng 11908275SEric Cheng mutex_enter(&dsp->ds_lock); 11918275SEric Cheng if (dsp->ds_dlstate != DL_IDLE) { 11928275SEric Cheng mutex_exit(&dsp->ds_lock); 11938275SEric Cheng dlerrorack(q, mp, DL_UNITDATA_REQ, DL_OUTSTATE, 0); 11948275SEric Cheng return; 11958275SEric Cheng } 11968275SEric Cheng DLD_DATATHR_INC(dsp); 11978275SEric Cheng mutex_exit(&dsp->ds_lock); 11988275SEric Cheng 1199269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 1200269Sericheng 1201269Sericheng off = dlp->dl_dest_addr_offset; 1202269Sericheng len = dlp->dl_dest_addr_length; 1203269Sericheng 1204269Sericheng if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) { 1205269Sericheng dl_err = DL_BADPRIM; 1206269Sericheng goto failed; 1207269Sericheng } 1208269Sericheng 1209269Sericheng if (len != addr_length + sizeof (uint16_t)) { 1210269Sericheng dl_err = DL_BADADDR; 1211269Sericheng goto failed; 1212269Sericheng } 1213269Sericheng 1214269Sericheng addr = mp->b_rptr + off; 1215269Sericheng sap = *(uint16_t *)(mp->b_rptr + off + addr_length); 1216269Sericheng 1217269Sericheng /* 1218269Sericheng * Check the length of the packet and the block types. 1219269Sericheng */ 1220269Sericheng size = 0; 12212311Sseb payload = mp->b_cont; 12222311Sseb for (bp = payload; bp != NULL; bp = bp->b_cont) { 1223269Sericheng if (DB_TYPE(bp) != M_DATA) 1224269Sericheng goto baddata; 1225269Sericheng 1226269Sericheng size += MBLKL(bp); 1227269Sericheng } 1228269Sericheng 12295903Ssowmini mac_sdu_get(dsp->ds_mh, NULL, &max_sdu); 12305903Ssowmini if (size > max_sdu) 1231269Sericheng goto baddata; 1232269Sericheng 1233269Sericheng /* 1234269Sericheng * Build a packet header. 1235269Sericheng */ 12368275SEric Cheng if ((bp = dls_header(dsp, addr, sap, dlp->dl_priority.dl_max, 12372760Sdg199075 &payload)) == NULL) { 1238269Sericheng dl_err = DL_BADADDR; 1239269Sericheng goto failed; 1240269Sericheng } 1241269Sericheng 1242269Sericheng /* 1243269Sericheng * We no longer need the M_PROTO header, so free it. 1244269Sericheng */ 1245269Sericheng freeb(mp); 1246269Sericheng 1247269Sericheng /* 1248269Sericheng * Transfer the checksum offload information if it is present. 1249269Sericheng */ 12502311Sseb hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value, 1251269Sericheng &flags); 12522311Sseb (void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0); 1253269Sericheng 1254269Sericheng /* 1255269Sericheng * Link the payload onto the new header. 1256269Sericheng */ 1257269Sericheng ASSERT(bp->b_cont == NULL); 12582311Sseb bp->b_cont = payload; 12598275SEric Cheng 12608275SEric Cheng /* 12618275SEric Cheng * No lock can be held across modules and putnext()'s, 12628275SEric Cheng * which can happen here with the call from DLD_TX(). 12638275SEric Cheng */ 12648275SEric Cheng if (DLD_TX(dsp, bp, 0, 0) != NULL) { 12658275SEric Cheng /* flow-controlled */ 12668275SEric Cheng DLD_SETQFULL(dsp); 12678275SEric Cheng } 12688275SEric Cheng DLD_DATATHR_DCR(dsp); 12695895Syz147064 return; 12708275SEric Cheng 1271269Sericheng failed: 1272269Sericheng dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0); 12738275SEric Cheng DLD_DATATHR_DCR(dsp); 12745895Syz147064 return; 1275269Sericheng 1276269Sericheng baddata: 1277269Sericheng dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0); 12788275SEric Cheng DLD_DATATHR_DCR(dsp); 1279269Sericheng } 1280269Sericheng 1281269Sericheng /* 1282269Sericheng * DL_PASSIVE_REQ 1283269Sericheng */ 12848275SEric Cheng static void 12858275SEric Cheng proto_passive_req(dld_str_t *dsp, mblk_t *mp) 1286269Sericheng { 1287269Sericheng t_uscalar_t dl_err; 1288269Sericheng 12895895Syz147064 /* 1290269Sericheng * If we've already become active by issuing an active primitive, 1291269Sericheng * then it's too late to try to become passive. 1292269Sericheng */ 1293269Sericheng if (dsp->ds_passivestate == DLD_ACTIVE) { 1294269Sericheng dl_err = DL_OUTSTATE; 1295269Sericheng goto failed; 1296269Sericheng } 1297269Sericheng 1298269Sericheng if (MBLKL(mp) < sizeof (dl_passive_req_t)) { 1299269Sericheng dl_err = DL_BADPRIM; 1300269Sericheng goto failed; 1301269Sericheng } 1302269Sericheng 1303269Sericheng dsp->ds_passivestate = DLD_PASSIVE; 1304269Sericheng dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ); 13058275SEric Cheng return; 1306269Sericheng failed: 1307269Sericheng dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0); 1308269Sericheng } 1309269Sericheng 13108275SEric Cheng 1311269Sericheng /* 1312269Sericheng * Catch-all handler. 1313269Sericheng */ 13148275SEric Cheng static void 13158275SEric Cheng proto_req(dld_str_t *dsp, mblk_t *mp) 13168275SEric Cheng { 13178275SEric Cheng union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr; 13188275SEric Cheng 13198275SEric Cheng dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0); 13208275SEric Cheng } 13218275SEric Cheng 13228275SEric Cheng static int 13238275SEric Cheng dld_capab_perim(dld_str_t *dsp, void *data, uint_t flags) 1324269Sericheng { 13258275SEric Cheng switch (flags) { 13268275SEric Cheng case DLD_ENABLE: 13278275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, (mac_perim_handle_t *)data); 13288275SEric Cheng return (0); 13298275SEric Cheng 13308275SEric Cheng case DLD_DISABLE: 13318275SEric Cheng mac_perim_exit((mac_perim_handle_t)data); 13328275SEric Cheng return (0); 13338275SEric Cheng 13348275SEric Cheng case DLD_QUERY: 13358275SEric Cheng return (mac_perim_held(dsp->ds_mh)); 13368275SEric Cheng } 13378275SEric Cheng return (0); 13380Sstevel@tonic-gate } 13390Sstevel@tonic-gate 13408275SEric Cheng static int 13418275SEric Cheng dld_capab_direct(dld_str_t *dsp, void *data, uint_t flags) 13420Sstevel@tonic-gate { 13438275SEric Cheng dld_capab_direct_t *direct = data; 13448275SEric Cheng 13458275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); 13460Sstevel@tonic-gate 13478275SEric Cheng switch (flags) { 13488275SEric Cheng case DLD_ENABLE: 13498275SEric Cheng dls_rx_set(dsp, (dls_rx_t)direct->di_rx_cf, 13508275SEric Cheng direct->di_rx_ch); 13518833SVenu.Iyer@Sun.COM 13528275SEric Cheng direct->di_tx_df = (uintptr_t)str_mdata_fastpath_put; 13538275SEric Cheng direct->di_tx_dh = dsp; 13548275SEric Cheng direct->di_tx_cb_df = (uintptr_t)mac_client_tx_notify; 13558275SEric Cheng direct->di_tx_cb_dh = dsp->ds_mch; 13568833SVenu.Iyer@Sun.COM direct->di_tx_fctl_df = (uintptr_t)mac_tx_is_flow_blocked; 13578833SVenu.Iyer@Sun.COM direct->di_tx_fctl_dh = dsp->ds_mch; 13588833SVenu.Iyer@Sun.COM 13598275SEric Cheng dsp->ds_direct = B_TRUE; 13608275SEric Cheng 13618275SEric Cheng return (0); 1362269Sericheng 13638275SEric Cheng case DLD_DISABLE: 13648275SEric Cheng dls_rx_set(dsp, (dsp->ds_mode == DLD_FASTPATH) ? 13658275SEric Cheng dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp); 13668275SEric Cheng dsp->ds_direct = B_FALSE; 13678275SEric Cheng 13688275SEric Cheng return (0); 13698275SEric Cheng } 13708275SEric Cheng return (ENOTSUP); 13718275SEric Cheng } 13720Sstevel@tonic-gate 13738275SEric Cheng /* 13748275SEric Cheng * dld_capab_poll_enable() 13758275SEric Cheng * 13768275SEric Cheng * This function is misnamed. All polling and fanouts are run out of the 13778275SEric Cheng * lower mac (in case of VNIC and the only mac in case of NICs). The 13788275SEric Cheng * availability of Rx ring and promiscous mode is all taken care between 13798275SEric Cheng * the soft ring set (mac_srs), the Rx ring, and S/W classifier. Any 13808275SEric Cheng * fanout necessary is done by the soft rings that are part of the 13818275SEric Cheng * mac_srs (by default mac_srs sends the packets up via a TCP and 13828275SEric Cheng * non TCP soft ring). 13838275SEric Cheng * 13848275SEric Cheng * The mac_srs (or its associated soft rings) always store the ill_rx_ring 13858275SEric Cheng * (the cookie returned when they registered with IP during plumb) as their 13868275SEric Cheng * 2nd argument which is passed up as mac_resource_handle_t. The upcall 13878275SEric Cheng * function and 1st argument is what the caller registered when they 13888275SEric Cheng * called mac_rx_classify_flow_add() to register the flow. For VNIC, 13898275SEric Cheng * the function is vnic_rx and argument is vnic_t. For regular NIC 13908275SEric Cheng * case, it mac_rx_default and mac_handle_t. As explained above, the 13918275SEric Cheng * mac_srs (or its soft ring) will add the ill_rx_ring (mac_resource_handle_t) 13928275SEric Cheng * from its stored 2nd argument. 13938275SEric Cheng */ 13948275SEric Cheng static int 13958275SEric Cheng dld_capab_poll_enable(dld_str_t *dsp, dld_capab_poll_t *poll) 13968275SEric Cheng { 13978275SEric Cheng if (dsp->ds_polling) 13988275SEric Cheng return (EINVAL); 13998275SEric Cheng 14008275SEric Cheng if ((dld_opt & DLD_OPT_NO_POLL) != 0 || dsp->ds_mode == DLD_RAW) 14018275SEric Cheng return (ENOTSUP); 14020Sstevel@tonic-gate 14030Sstevel@tonic-gate /* 14048275SEric Cheng * Enable client polling if and only if DLS bypass is possible. 14058275SEric Cheng * Special cases like VLANs need DLS processing in the Rx data path. 14068275SEric Cheng * In such a case we can neither allow the client (IP) to directly 14078275SEric Cheng * poll the softring (since DLS processing hasn't been done) nor can 14088275SEric Cheng * we allow DLS bypass. 14090Sstevel@tonic-gate */ 14108275SEric Cheng if (!mac_rx_bypass_set(dsp->ds_mch, dsp->ds_rx, dsp->ds_rx_arg)) 14118275SEric Cheng return (ENOTSUP); 14120Sstevel@tonic-gate 14130Sstevel@tonic-gate /* 14148275SEric Cheng * Register soft ring resources. This will come in handy later if 14158275SEric Cheng * the user decides to modify CPU bindings to use more CPUs for the 14168275SEric Cheng * device in which case we will switch to fanout using soft rings. 14170Sstevel@tonic-gate */ 14188275SEric Cheng mac_resource_set_common(dsp->ds_mch, 14198275SEric Cheng (mac_resource_add_t)poll->poll_ring_add_cf, 14208275SEric Cheng (mac_resource_remove_t)poll->poll_ring_remove_cf, 14218275SEric Cheng (mac_resource_quiesce_t)poll->poll_ring_quiesce_cf, 14228275SEric Cheng (mac_resource_restart_t)poll->poll_ring_restart_cf, 14238275SEric Cheng (mac_resource_bind_t)poll->poll_ring_bind_cf, 14248275SEric Cheng poll->poll_ring_ch); 14258275SEric Cheng 14268275SEric Cheng mac_client_poll_enable(dsp->ds_mch); 14270Sstevel@tonic-gate 14280Sstevel@tonic-gate dsp->ds_polling = B_TRUE; 14298275SEric Cheng return (0); 14308275SEric Cheng } 14318275SEric Cheng 14328275SEric Cheng /* ARGSUSED */ 14338275SEric Cheng static int 14348275SEric Cheng dld_capab_poll_disable(dld_str_t *dsp, dld_capab_poll_t *poll) 14358275SEric Cheng { 14368275SEric Cheng if (!dsp->ds_polling) 14378275SEric Cheng return (EINVAL); 14388275SEric Cheng 14398275SEric Cheng mac_client_poll_disable(dsp->ds_mch); 14408275SEric Cheng mac_resource_set(dsp->ds_mch, NULL, NULL); 14418275SEric Cheng 14428275SEric Cheng dsp->ds_polling = B_FALSE; 14438275SEric Cheng return (0); 14440Sstevel@tonic-gate } 14450Sstevel@tonic-gate 14468275SEric Cheng static int 14478275SEric Cheng dld_capab_poll(dld_str_t *dsp, void *data, uint_t flags) 14488275SEric Cheng { 14498275SEric Cheng dld_capab_poll_t *poll = data; 14508275SEric Cheng 14518275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); 14528275SEric Cheng 14538275SEric Cheng switch (flags) { 14548275SEric Cheng case DLD_ENABLE: 14558275SEric Cheng return (dld_capab_poll_enable(dsp, poll)); 14568275SEric Cheng case DLD_DISABLE: 14578275SEric Cheng return (dld_capab_poll_disable(dsp, poll)); 14588275SEric Cheng } 14598275SEric Cheng return (ENOTSUP); 14608275SEric Cheng } 14618275SEric Cheng 14628275SEric Cheng static int 14638275SEric Cheng dld_capab_lso(dld_str_t *dsp, void *data, uint_t flags) 14641184Skrgopi { 14658275SEric Cheng dld_capab_lso_t *lso = data; 14668275SEric Cheng 14678275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); 14688275SEric Cheng 14698275SEric Cheng switch (flags) { 14708275SEric Cheng case DLD_ENABLE: { 14718275SEric Cheng mac_capab_lso_t mac_lso; 14721184Skrgopi 14738275SEric Cheng /* 14748275SEric Cheng * Check if LSO is supported on this MAC & enable LSO 14758275SEric Cheng * accordingly. 14768275SEric Cheng */ 14778275SEric Cheng if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) { 14788275SEric Cheng lso->lso_max = mac_lso.lso_basic_tcp_ipv4.lso_max; 14798275SEric Cheng lso->lso_flags = 0; 14808275SEric Cheng /* translate the flag for mac clients */ 14818275SEric Cheng if ((mac_lso.lso_flags & LSO_TX_BASIC_TCP_IPV4) != 0) 148211042SErik.Nordmark@Sun.COM lso->lso_flags |= DLD_LSO_BASIC_TCP_IPV4; 14838275SEric Cheng dsp->ds_lso = B_TRUE; 14848275SEric Cheng dsp->ds_lso_max = lso->lso_max; 14858275SEric Cheng } else { 14868275SEric Cheng dsp->ds_lso = B_FALSE; 14878275SEric Cheng dsp->ds_lso_max = 0; 14888275SEric Cheng return (ENOTSUP); 14898275SEric Cheng } 14908275SEric Cheng return (0); 14918275SEric Cheng } 14928275SEric Cheng case DLD_DISABLE: { 14938275SEric Cheng dsp->ds_lso = B_FALSE; 14948275SEric Cheng dsp->ds_lso_max = 0; 14958275SEric Cheng return (0); 14968275SEric Cheng } 14978275SEric Cheng } 14988275SEric Cheng return (ENOTSUP); 14998275SEric Cheng } 15008275SEric Cheng 15018275SEric Cheng static int 15028275SEric Cheng dld_capab(dld_str_t *dsp, uint_t type, void *data, uint_t flags) 15038275SEric Cheng { 15048275SEric Cheng int err; 15051184Skrgopi 15061184Skrgopi /* 15078275SEric Cheng * Don't enable direct callback capabilities unless the caller is 15088275SEric Cheng * the IP client. When a module is inserted in a stream (_I_INSERT) 15098275SEric Cheng * the stack initiates capability disable, but due to races, the 15108275SEric Cheng * module insertion may complete before the capability disable 15118275SEric Cheng * completes. So we limit the check to DLD_ENABLE case. 15121184Skrgopi */ 15138275SEric Cheng if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) && 151411021SEric.Cheng@Sun.COM (dsp->ds_sap != ETHERTYPE_IP || 151511021SEric.Cheng@Sun.COM !check_mod_above(dsp->ds_rq, "ip"))) { 15168275SEric Cheng return (ENOTSUP); 15178275SEric Cheng } 15181184Skrgopi 15198275SEric Cheng switch (type) { 15208275SEric Cheng case DLD_CAPAB_DIRECT: 15218275SEric Cheng err = dld_capab_direct(dsp, data, flags); 15228275SEric Cheng break; 15231184Skrgopi 15248275SEric Cheng case DLD_CAPAB_POLL: 15258275SEric Cheng err = dld_capab_poll(dsp, data, flags); 15268275SEric Cheng break; 15271184Skrgopi 15288275SEric Cheng case DLD_CAPAB_PERIM: 15298275SEric Cheng err = dld_capab_perim(dsp, data, flags); 15308275SEric Cheng break; 15311184Skrgopi 15328275SEric Cheng case DLD_CAPAB_LSO: 15338275SEric Cheng err = dld_capab_lso(dsp, data, flags); 15348275SEric Cheng break; 15358275SEric Cheng 15368275SEric Cheng default: 15378275SEric Cheng err = ENOTSUP; 15388275SEric Cheng break; 15391184Skrgopi } 15408275SEric Cheng 15418275SEric Cheng return (err); 15421184Skrgopi } 15431184Skrgopi 15440Sstevel@tonic-gate /* 15450Sstevel@tonic-gate * DL_CAPABILITY_ACK/DL_ERROR_ACK 15460Sstevel@tonic-gate */ 15478275SEric Cheng static void 1548269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp) 15490Sstevel@tonic-gate { 15500Sstevel@tonic-gate dl_capability_ack_t *dlap; 15510Sstevel@tonic-gate dl_capability_sub_t *dlsp; 15520Sstevel@tonic-gate size_t subsize; 15538275SEric Cheng dl_capab_dld_t dld; 15540Sstevel@tonic-gate dl_capab_hcksum_t hcksum; 15550Sstevel@tonic-gate dl_capab_zerocopy_t zcopy; 155611076SCathy.Zhou@Sun.COM dl_capab_vrrp_t vrrp; 155711076SCathy.Zhou@Sun.COM mac_capab_vrrp_t vrrp_capab; 15580Sstevel@tonic-gate uint8_t *ptr; 1559269Sericheng queue_t *q = dsp->ds_wq; 1560269Sericheng mblk_t *mp1; 15618275SEric Cheng boolean_t is_vlan; 15625895Syz147064 boolean_t hcksum_capable = B_FALSE; 15635895Syz147064 boolean_t zcopy_capable = B_FALSE; 15648275SEric Cheng boolean_t dld_capable = B_FALSE; 156511076SCathy.Zhou@Sun.COM boolean_t vrrp_capable = B_FALSE; 15660Sstevel@tonic-gate 15670Sstevel@tonic-gate /* 15680Sstevel@tonic-gate * Initially assume no capabilities. 15690Sstevel@tonic-gate */ 15700Sstevel@tonic-gate subsize = 0; 15718275SEric Cheng is_vlan = (mac_client_vid(dsp->ds_mch) != VLAN_ID_NONE); 15720Sstevel@tonic-gate 15730Sstevel@tonic-gate /* 15745895Syz147064 * Check if checksum offload is supported on this MAC. Don't 15755895Syz147064 * advertise DL_CAPAB_HCKSUM if the underlying MAC is VLAN incapable, 15765895Syz147064 * since it might not be able to do the hardware checksum offload 15775895Syz147064 * with the correct offset. 15780Sstevel@tonic-gate */ 15795895Syz147064 bzero(&hcksum, sizeof (dl_capab_hcksum_t)); 15805895Syz147064 if ((!is_vlan || (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_NATIVEVLAN, 15815895Syz147064 NULL))) && mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM, 15822311Sseb &hcksum.hcksum_txflags)) { 15835895Syz147064 if (hcksum.hcksum_txflags != 0) { 15845895Syz147064 hcksum_capable = B_TRUE; 15855895Syz147064 subsize += sizeof (dl_capability_sub_t) + 15865895Syz147064 sizeof (dl_capab_hcksum_t); 15875895Syz147064 } 15880Sstevel@tonic-gate } 15890Sstevel@tonic-gate 15900Sstevel@tonic-gate /* 15915895Syz147064 * Check if zerocopy is supported on this interface. 15925895Syz147064 * If advertising DL_CAPAB_ZEROCOPY has not been explicitly disabled 15935895Syz147064 * then reserve space for that capability. 15940Sstevel@tonic-gate */ 15955895Syz147064 if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_ZCOPY, NULL) && 15965895Syz147064 !(dld_opt & DLD_OPT_NO_ZEROCOPY)) { 15975895Syz147064 zcopy_capable = B_TRUE; 15980Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 15990Sstevel@tonic-gate sizeof (dl_capab_zerocopy_t); 16000Sstevel@tonic-gate } 16010Sstevel@tonic-gate 16020Sstevel@tonic-gate /* 16038275SEric Cheng * Direct capability negotiation interface between IP and DLD 16048275SEric Cheng */ 160511021SEric.Cheng@Sun.COM if (dsp->ds_sap == ETHERTYPE_IP && check_mod_above(dsp->ds_rq, "ip")) { 16068275SEric Cheng dld_capable = B_TRUE; 16078275SEric Cheng subsize += sizeof (dl_capability_sub_t) + 16088275SEric Cheng sizeof (dl_capab_dld_t); 16098275SEric Cheng } 16108275SEric Cheng 16118275SEric Cheng /* 161211076SCathy.Zhou@Sun.COM * Check if vrrp is supported on this interface. If so, reserve 161311076SCathy.Zhou@Sun.COM * space for that capability. 161411076SCathy.Zhou@Sun.COM */ 161511076SCathy.Zhou@Sun.COM if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_VRRP, &vrrp_capab)) { 161611076SCathy.Zhou@Sun.COM vrrp_capable = B_TRUE; 161711076SCathy.Zhou@Sun.COM subsize += sizeof (dl_capability_sub_t) + 161811076SCathy.Zhou@Sun.COM sizeof (dl_capab_vrrp_t); 161911076SCathy.Zhou@Sun.COM } 162011076SCathy.Zhou@Sun.COM 162111076SCathy.Zhou@Sun.COM /* 1622269Sericheng * If there are no capabilities to advertise or if we 1623269Sericheng * can't allocate a response, send a DL_ERROR_ACK. 16240Sstevel@tonic-gate */ 16251184Skrgopi if ((mp1 = reallocb(mp, 1626269Sericheng sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) { 1627269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0); 16288275SEric Cheng return; 16290Sstevel@tonic-gate } 16300Sstevel@tonic-gate 1631269Sericheng mp = mp1; 1632269Sericheng DB_TYPE(mp) = M_PROTO; 1633269Sericheng mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize; 1634269Sericheng bzero(mp->b_rptr, MBLKL(mp)); 16350Sstevel@tonic-gate dlap = (dl_capability_ack_t *)mp->b_rptr; 16360Sstevel@tonic-gate dlap->dl_primitive = DL_CAPABILITY_ACK; 16370Sstevel@tonic-gate dlap->dl_sub_offset = sizeof (dl_capability_ack_t); 16380Sstevel@tonic-gate dlap->dl_sub_length = subsize; 16390Sstevel@tonic-gate ptr = (uint8_t *)&dlap[1]; 16400Sstevel@tonic-gate 16410Sstevel@tonic-gate /* 16420Sstevel@tonic-gate * TCP/IP checksum offload. 16430Sstevel@tonic-gate */ 16445895Syz147064 if (hcksum_capable) { 16450Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 16460Sstevel@tonic-gate 16470Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_HCKSUM; 16480Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_hcksum_t); 16490Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 16500Sstevel@tonic-gate 16510Sstevel@tonic-gate hcksum.hcksum_version = HCKSUM_VERSION_1; 16520Sstevel@tonic-gate dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 16530Sstevel@tonic-gate bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t)); 16540Sstevel@tonic-gate ptr += sizeof (dl_capab_hcksum_t); 16550Sstevel@tonic-gate } 16560Sstevel@tonic-gate 16570Sstevel@tonic-gate /* 16580Sstevel@tonic-gate * Zero copy 16590Sstevel@tonic-gate */ 16605895Syz147064 if (zcopy_capable) { 16610Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 16620Sstevel@tonic-gate 16630Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_ZEROCOPY; 16640Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_zerocopy_t); 16650Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 16660Sstevel@tonic-gate 16670Sstevel@tonic-gate bzero(&zcopy, sizeof (dl_capab_zerocopy_t)); 16680Sstevel@tonic-gate zcopy.zerocopy_version = ZEROCOPY_VERSION_1; 16690Sstevel@tonic-gate zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM; 16700Sstevel@tonic-gate 16710Sstevel@tonic-gate dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq); 16720Sstevel@tonic-gate bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t)); 16730Sstevel@tonic-gate ptr += sizeof (dl_capab_zerocopy_t); 16740Sstevel@tonic-gate } 16750Sstevel@tonic-gate 16768275SEric Cheng /* 167711076SCathy.Zhou@Sun.COM * VRRP capability negotiation 167811076SCathy.Zhou@Sun.COM */ 167911076SCathy.Zhou@Sun.COM if (vrrp_capable) { 168011076SCathy.Zhou@Sun.COM dlsp = (dl_capability_sub_t *)ptr; 168111076SCathy.Zhou@Sun.COM dlsp->dl_cap = DL_CAPAB_VRRP; 168211076SCathy.Zhou@Sun.COM dlsp->dl_length = sizeof (dl_capab_vrrp_t); 168311076SCathy.Zhou@Sun.COM ptr += sizeof (dl_capability_sub_t); 168411076SCathy.Zhou@Sun.COM 168511076SCathy.Zhou@Sun.COM bzero(&vrrp, sizeof (dl_capab_vrrp_t)); 168611076SCathy.Zhou@Sun.COM vrrp.vrrp_af = vrrp_capab.mcv_af; 168711076SCathy.Zhou@Sun.COM bcopy(&vrrp, ptr, sizeof (dl_capab_vrrp_t)); 168811076SCathy.Zhou@Sun.COM ptr += sizeof (dl_capab_vrrp_t); 168911076SCathy.Zhou@Sun.COM } 169011076SCathy.Zhou@Sun.COM 169111076SCathy.Zhou@Sun.COM /* 16928275SEric Cheng * Direct capability negotiation interface between IP and DLD. 16938275SEric Cheng * Refer to dld.h for details. 16948275SEric Cheng */ 16958275SEric Cheng if (dld_capable) { 16968275SEric Cheng dlsp = (dl_capability_sub_t *)ptr; 16978275SEric Cheng dlsp->dl_cap = DL_CAPAB_DLD; 16988275SEric Cheng dlsp->dl_length = sizeof (dl_capab_dld_t); 16998275SEric Cheng ptr += sizeof (dl_capability_sub_t); 1700269Sericheng 17018275SEric Cheng bzero(&dld, sizeof (dl_capab_dld_t)); 17028275SEric Cheng dld.dld_version = DLD_CURRENT_VERSION; 17038275SEric Cheng dld.dld_capab = (uintptr_t)dld_capab; 17048275SEric Cheng dld.dld_capab_handle = (uintptr_t)dsp; 17058275SEric Cheng 17068275SEric Cheng dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq); 17078275SEric Cheng bcopy(&dld, ptr, sizeof (dl_capab_dld_t)); 17088275SEric Cheng ptr += sizeof (dl_capab_dld_t); 17098275SEric Cheng } 17108275SEric Cheng 17118275SEric Cheng ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize); 1712269Sericheng qreply(q, mp); 17130Sstevel@tonic-gate } 17145113Syz147064 17155113Syz147064 /* 17165113Syz147064 * Disable any enabled capabilities. 17175113Syz147064 */ 17185113Syz147064 void 17195113Syz147064 dld_capabilities_disable(dld_str_t *dsp) 17205113Syz147064 { 17215113Syz147064 if (dsp->ds_polling) 17228275SEric Cheng (void) dld_capab_poll_disable(dsp, NULL); 17235113Syz147064 } 1724