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 /*
2212325SCathy.Zhou@Sun.COM * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate */
240Sstevel@tonic-gate
250Sstevel@tonic-gate /*
260Sstevel@tonic-gate * Data-Link Driver
270Sstevel@tonic-gate */
280Sstevel@tonic-gate #include <sys/sysmacros.h>
298275SEric Cheng #include <sys/strsubr.h>
300Sstevel@tonic-gate #include <sys/strsun.h>
310Sstevel@tonic-gate #include <sys/vlan.h>
320Sstevel@tonic-gate #include <sys/dld_impl.h>
338275SEric Cheng #include <sys/mac_client.h>
348275SEric Cheng #include <sys/mac_client_impl.h>
358275SEric Cheng #include <sys/mac_client_priv.h>
360Sstevel@tonic-gate
378275SEric Cheng typedef void proto_reqfunc_t(dld_str_t *, mblk_t *);
380Sstevel@tonic-gate
390Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req,
400Sstevel@tonic-gate proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req,
410Sstevel@tonic-gate proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req,
420Sstevel@tonic-gate proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req,
435895Syz147064 proto_notify_req, proto_passive_req;
440Sstevel@tonic-gate
458275SEric Cheng static void proto_capability_advertise(dld_str_t *, mblk_t *);
468275SEric Cheng static int dld_capab_poll_disable(dld_str_t *, dld_capab_poll_t *);
4711021SEric.Cheng@Sun.COM static boolean_t check_mod_above(queue_t *, const char *);
481184Skrgopi
490Sstevel@tonic-gate #define DL_ACK_PENDING(state) \
500Sstevel@tonic-gate ((state) == DL_ATTACH_PENDING || \
510Sstevel@tonic-gate (state) == DL_DETACH_PENDING || \
520Sstevel@tonic-gate (state) == DL_BIND_PENDING || \
530Sstevel@tonic-gate (state) == DL_UNBIND_PENDING)
540Sstevel@tonic-gate
550Sstevel@tonic-gate /*
56269Sericheng * Process a DLPI protocol message.
57269Sericheng * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ,
58269Sericheng * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an
59269Sericheng * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t
60269Sericheng * as 'passive' and forbids it from being subsequently made 'active'
61269Sericheng * by the above primitives.
620Sstevel@tonic-gate */
630Sstevel@tonic-gate void
dld_proto(dld_str_t * dsp,mblk_t * mp)648275SEric Cheng dld_proto(dld_str_t *dsp, mblk_t *mp)
650Sstevel@tonic-gate {
660Sstevel@tonic-gate t_uscalar_t prim;
670Sstevel@tonic-gate
688275SEric Cheng if (MBLKL(mp) < sizeof (t_uscalar_t)) {
698275SEric Cheng freemsg(mp);
708275SEric Cheng return;
718275SEric Cheng }
728275SEric Cheng prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
730Sstevel@tonic-gate
74269Sericheng switch (prim) {
75269Sericheng case DL_INFO_REQ:
768275SEric Cheng proto_info_req(dsp, mp);
77269Sericheng break;
78269Sericheng case DL_BIND_REQ:
798275SEric Cheng proto_bind_req(dsp, mp);
80269Sericheng break;
81269Sericheng case DL_UNBIND_REQ:
828275SEric Cheng proto_unbind_req(dsp, mp);
838275SEric Cheng break;
848275SEric Cheng case DL_UNITDATA_REQ:
858275SEric Cheng proto_unitdata_req(dsp, mp);
86269Sericheng break;
87269Sericheng case DL_UDQOS_REQ:
888275SEric Cheng proto_udqos_req(dsp, mp);
89269Sericheng break;
90269Sericheng case DL_ATTACH_REQ:
918275SEric Cheng proto_attach_req(dsp, mp);
92269Sericheng break;
93269Sericheng case DL_DETACH_REQ:
948275SEric Cheng proto_detach_req(dsp, mp);
95269Sericheng break;
96269Sericheng case DL_ENABMULTI_REQ:
978275SEric Cheng proto_enabmulti_req(dsp, mp);
98269Sericheng break;
99269Sericheng case DL_DISABMULTI_REQ:
1008275SEric Cheng proto_disabmulti_req(dsp, mp);
101269Sericheng break;
102269Sericheng case DL_PROMISCON_REQ:
1038275SEric Cheng proto_promiscon_req(dsp, mp);
104269Sericheng break;
105269Sericheng case DL_PROMISCOFF_REQ:
1068275SEric Cheng proto_promiscoff_req(dsp, mp);
107269Sericheng break;
108269Sericheng case DL_PHYS_ADDR_REQ:
1098275SEric Cheng proto_physaddr_req(dsp, mp);
110269Sericheng break;
111269Sericheng case DL_SET_PHYS_ADDR_REQ:
1128275SEric Cheng proto_setphysaddr_req(dsp, mp);
113269Sericheng break;
114269Sericheng case DL_NOTIFY_REQ:
1158275SEric Cheng proto_notify_req(dsp, mp);
116269Sericheng break;
117269Sericheng case DL_CAPABILITY_REQ:
1188275SEric Cheng proto_capability_req(dsp, mp);
119269Sericheng break;
120269Sericheng case DL_PASSIVE_REQ:
1218275SEric Cheng proto_passive_req(dsp, mp);
122269Sericheng break;
123269Sericheng default:
1248275SEric Cheng proto_req(dsp, mp);
125269Sericheng break;
1260Sstevel@tonic-gate }
1270Sstevel@tonic-gate }
1280Sstevel@tonic-gate
129269Sericheng #define NEG(x) -(x)
1300Sstevel@tonic-gate typedef struct dl_info_ack_wrapper {
1310Sstevel@tonic-gate dl_info_ack_t dl_info;
1322311Sseb uint8_t dl_addr[MAXMACADDRLEN + sizeof (uint16_t)];
1332311Sseb uint8_t dl_brdcst_addr[MAXMACADDRLEN];
1340Sstevel@tonic-gate dl_qos_cl_range1_t dl_qos_range1;
1350Sstevel@tonic-gate dl_qos_cl_sel1_t dl_qos_sel1;
1360Sstevel@tonic-gate } dl_info_ack_wrapper_t;
1370Sstevel@tonic-gate
1380Sstevel@tonic-gate /*
139269Sericheng * DL_INFO_REQ
1400Sstevel@tonic-gate */
1418275SEric Cheng static void
proto_info_req(dld_str_t * dsp,mblk_t * mp)1428275SEric Cheng proto_info_req(dld_str_t *dsp, mblk_t *mp)
1430Sstevel@tonic-gate {
1440Sstevel@tonic-gate dl_info_ack_wrapper_t *dlwp;
1450Sstevel@tonic-gate dl_info_ack_t *dlp;
1460Sstevel@tonic-gate dl_qos_cl_sel1_t *selp;
1470Sstevel@tonic-gate dl_qos_cl_range1_t *rangep;
1480Sstevel@tonic-gate uint8_t *addr;
1490Sstevel@tonic-gate uint8_t *brdcst_addr;
1500Sstevel@tonic-gate uint_t addr_length;
1510Sstevel@tonic-gate uint_t sap_length;
152269Sericheng mac_info_t minfo;
153269Sericheng mac_info_t *minfop;
154269Sericheng queue_t *q = dsp->ds_wq;
1550Sstevel@tonic-gate
1560Sstevel@tonic-gate /*
1570Sstevel@tonic-gate * Swap the request message for one large enough to contain the
1580Sstevel@tonic-gate * wrapper structure defined above.
1590Sstevel@tonic-gate */
160269Sericheng if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t),
1610Sstevel@tonic-gate M_PCPROTO, 0)) == NULL)
1628275SEric Cheng return;
1630Sstevel@tonic-gate
1640Sstevel@tonic-gate bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t));
1650Sstevel@tonic-gate dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr;
1660Sstevel@tonic-gate
1670Sstevel@tonic-gate dlp = &(dlwp->dl_info);
1680Sstevel@tonic-gate ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr);
1690Sstevel@tonic-gate
1700Sstevel@tonic-gate dlp->dl_primitive = DL_INFO_ACK;
1710Sstevel@tonic-gate
1720Sstevel@tonic-gate /*
1730Sstevel@tonic-gate * Set up the sub-structure pointers.
1740Sstevel@tonic-gate */
1750Sstevel@tonic-gate addr = dlwp->dl_addr;
1760Sstevel@tonic-gate brdcst_addr = dlwp->dl_brdcst_addr;
1770Sstevel@tonic-gate rangep = &(dlwp->dl_qos_range1);
1780Sstevel@tonic-gate selp = &(dlwp->dl_qos_sel1);
1790Sstevel@tonic-gate
1800Sstevel@tonic-gate /*
1810Sstevel@tonic-gate * This driver supports only version 2 connectionless DLPI provider
1820Sstevel@tonic-gate * nodes.
1830Sstevel@tonic-gate */
1840Sstevel@tonic-gate dlp->dl_service_mode = DL_CLDLS;
1850Sstevel@tonic-gate dlp->dl_version = DL_VERSION_2;
1860Sstevel@tonic-gate
1870Sstevel@tonic-gate /*
188269Sericheng * Set the style of the provider
1890Sstevel@tonic-gate */
190269Sericheng dlp->dl_provider_style = dsp->ds_style;
1910Sstevel@tonic-gate ASSERT(dlp->dl_provider_style == DL_STYLE1 ||
1920Sstevel@tonic-gate dlp->dl_provider_style == DL_STYLE2);
1930Sstevel@tonic-gate
1940Sstevel@tonic-gate /*
1950Sstevel@tonic-gate * Set the current DLPI state.
1960Sstevel@tonic-gate */
1970Sstevel@tonic-gate dlp->dl_current_state = dsp->ds_dlstate;
1980Sstevel@tonic-gate
1990Sstevel@tonic-gate /*
200269Sericheng * Gratuitously set the media type. This is to deal with modules
201269Sericheng * that assume the media type is known prior to DL_ATTACH_REQ
2020Sstevel@tonic-gate * being completed.
2030Sstevel@tonic-gate */
2040Sstevel@tonic-gate dlp->dl_mac_type = DL_ETHER;
2050Sstevel@tonic-gate
2060Sstevel@tonic-gate /*
207269Sericheng * If the stream is not at least attached we try to retrieve the
208269Sericheng * mac_info using mac_info_get()
2090Sstevel@tonic-gate */
2100Sstevel@tonic-gate if (dsp->ds_dlstate == DL_UNATTACHED ||
2110Sstevel@tonic-gate dsp->ds_dlstate == DL_ATTACH_PENDING ||
212269Sericheng dsp->ds_dlstate == DL_DETACH_PENDING) {
213269Sericheng if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) {
214269Sericheng /*
215269Sericheng * Cannot find mac_info. giving up.
216269Sericheng */
217269Sericheng goto done;
218269Sericheng }
219269Sericheng minfop = &minfo;
220269Sericheng } else {
221269Sericheng minfop = (mac_info_t *)dsp->ds_mip;
2225903Ssowmini /* We can only get the sdu if we're attached. */
2235903Ssowmini mac_sdu_get(dsp->ds_mh, &dlp->dl_min_sdu, &dlp->dl_max_sdu);
224269Sericheng }
2250Sstevel@tonic-gate
2260Sstevel@tonic-gate /*
2270Sstevel@tonic-gate * Set the media type (properly this time).
2280Sstevel@tonic-gate */
2293147Sxc151355 if (dsp->ds_native)
2303147Sxc151355 dlp->dl_mac_type = minfop->mi_nativemedia;
2313147Sxc151355 else
2323147Sxc151355 dlp->dl_mac_type = minfop->mi_media;
2330Sstevel@tonic-gate
2340Sstevel@tonic-gate /*
2350Sstevel@tonic-gate * Set the DLSAP length. We only support 16 bit values and they
2360Sstevel@tonic-gate * appear after the MAC address portion of DLSAP addresses.
2370Sstevel@tonic-gate */
2380Sstevel@tonic-gate sap_length = sizeof (uint16_t);
2390Sstevel@tonic-gate dlp->dl_sap_length = NEG(sap_length);
2400Sstevel@tonic-gate
241269Sericheng addr_length = minfop->mi_addr_length;
2420Sstevel@tonic-gate
2430Sstevel@tonic-gate /*
2440Sstevel@tonic-gate * Copy in the media broadcast address.
2450Sstevel@tonic-gate */
2462311Sseb if (minfop->mi_brdcst_addr != NULL) {
2472311Sseb dlp->dl_brdcst_addr_offset =
2482311Sseb (uintptr_t)brdcst_addr - (uintptr_t)dlp;
2492311Sseb bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length);
2502311Sseb dlp->dl_brdcst_addr_length = addr_length;
2512311Sseb }
2520Sstevel@tonic-gate
2538874SSebastien.Roy@Sun.COM /* Only VLAN links and links that have a normal tag mode support QOS. */
2548968SSebastien.Roy@Sun.COM if ((dsp->ds_mch != NULL &&
2558968SSebastien.Roy@Sun.COM mac_client_vid(dsp->ds_mch) != VLAN_ID_NONE) ||
2568968SSebastien.Roy@Sun.COM (dsp->ds_dlp != NULL &&
2578968SSebastien.Roy@Sun.COM dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_NORMAL)) {
2588874SSebastien.Roy@Sun.COM dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp;
2598874SSebastien.Roy@Sun.COM dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t);
2600Sstevel@tonic-gate
2618874SSebastien.Roy@Sun.COM rangep->dl_qos_type = DL_QOS_CL_RANGE1;
2628874SSebastien.Roy@Sun.COM rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN;
2638874SSebastien.Roy@Sun.COM rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
2648874SSebastien.Roy@Sun.COM rangep->dl_protection.dl_min = DL_UNKNOWN;
2658874SSebastien.Roy@Sun.COM rangep->dl_protection.dl_max = DL_UNKNOWN;
2668874SSebastien.Roy@Sun.COM rangep->dl_residual_error = DL_UNKNOWN;
2670Sstevel@tonic-gate
2688874SSebastien.Roy@Sun.COM /*
2698874SSebastien.Roy@Sun.COM * Specify the supported range of priorities.
2708874SSebastien.Roy@Sun.COM */
2718874SSebastien.Roy@Sun.COM rangep->dl_priority.dl_min = 0;
2728874SSebastien.Roy@Sun.COM rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1;
2730Sstevel@tonic-gate
2748874SSebastien.Roy@Sun.COM dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp;
2758874SSebastien.Roy@Sun.COM dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t);
2760Sstevel@tonic-gate
2778874SSebastien.Roy@Sun.COM selp->dl_qos_type = DL_QOS_CL_SEL1;
2788874SSebastien.Roy@Sun.COM selp->dl_trans_delay = DL_UNKNOWN;
2798874SSebastien.Roy@Sun.COM selp->dl_protection = DL_UNKNOWN;
2808874SSebastien.Roy@Sun.COM selp->dl_residual_error = DL_UNKNOWN;
2812760Sdg199075
2828874SSebastien.Roy@Sun.COM /*
2838874SSebastien.Roy@Sun.COM * Specify the current priority (which can be changed by
2848874SSebastien.Roy@Sun.COM * the DL_UDQOS_REQ primitive).
2858874SSebastien.Roy@Sun.COM */
2868874SSebastien.Roy@Sun.COM selp->dl_priority = dsp->ds_pri;
2878874SSebastien.Roy@Sun.COM }
2880Sstevel@tonic-gate
2890Sstevel@tonic-gate dlp->dl_addr_length = addr_length + sizeof (uint16_t);
2900Sstevel@tonic-gate if (dsp->ds_dlstate == DL_IDLE) {
2910Sstevel@tonic-gate /*
2920Sstevel@tonic-gate * The stream is bound. Therefore we can formulate a valid
2930Sstevel@tonic-gate * DLSAP address.
2940Sstevel@tonic-gate */
2950Sstevel@tonic-gate dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp;
2962311Sseb if (addr_length > 0)
2978275SEric Cheng mac_unicast_primary_get(dsp->ds_mh, addr);
2988275SEric Cheng
2990Sstevel@tonic-gate *(uint16_t *)(addr + addr_length) = dsp->ds_sap;
3000Sstevel@tonic-gate }
3010Sstevel@tonic-gate
3020Sstevel@tonic-gate done:
30311474SJonathan.Adams@Sun.COM IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0);
30411474SJonathan.Adams@Sun.COM IMPLY(dlp->dl_qos_range_offset != 0,
30511474SJonathan.Adams@Sun.COM dlp->dl_qos_range_length != 0);
30611474SJonathan.Adams@Sun.COM IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0);
30711474SJonathan.Adams@Sun.COM IMPLY(dlp->dl_brdcst_addr_offset != 0,
30811474SJonathan.Adams@Sun.COM dlp->dl_brdcst_addr_length != 0);
3090Sstevel@tonic-gate
310269Sericheng qreply(q, mp);
311269Sericheng }
312269Sericheng
313269Sericheng /*
314269Sericheng * DL_ATTACH_REQ
315269Sericheng */
3168275SEric Cheng static void
proto_attach_req(dld_str_t * dsp,mblk_t * mp)3178275SEric Cheng proto_attach_req(dld_str_t *dsp, mblk_t *mp)
318269Sericheng {
3198275SEric Cheng dl_attach_req_t *dlp = (dl_attach_req_t *)mp->b_rptr;
320269Sericheng int err = 0;
321269Sericheng t_uscalar_t dl_err;
322269Sericheng queue_t *q = dsp->ds_wq;
323269Sericheng
324269Sericheng if (MBLKL(mp) < sizeof (dl_attach_req_t) ||
325269Sericheng dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) {
326269Sericheng dl_err = DL_BADPRIM;
327269Sericheng goto failed;
328269Sericheng }
329269Sericheng
330269Sericheng if (dsp->ds_dlstate != DL_UNATTACHED) {
331269Sericheng dl_err = DL_OUTSTATE;
332269Sericheng goto failed;
333269Sericheng }
334269Sericheng
335269Sericheng dsp->ds_dlstate = DL_ATTACH_PENDING;
336269Sericheng
337269Sericheng err = dld_str_attach(dsp, dlp->dl_ppa);
338269Sericheng if (err != 0) {
339269Sericheng switch (err) {
340269Sericheng case ENOENT:
341269Sericheng dl_err = DL_BADPPA;
342269Sericheng err = 0;
343269Sericheng break;
344269Sericheng default:
345269Sericheng dl_err = DL_SYSERR;
346269Sericheng break;
347269Sericheng }
348269Sericheng dsp->ds_dlstate = DL_UNATTACHED;
349269Sericheng goto failed;
350269Sericheng }
351269Sericheng ASSERT(dsp->ds_dlstate == DL_UNBOUND);
3528275SEric Cheng dlokack(q, mp, DL_ATTACH_REQ);
3538275SEric Cheng return;
354269Sericheng
355269Sericheng failed:
356269Sericheng dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err);
3570Sstevel@tonic-gate }
3580Sstevel@tonic-gate
3598275SEric Cheng /*
3608275SEric Cheng * DL_DETACH_REQ
3618275SEric Cheng */
3628275SEric Cheng static void
proto_detach_req(dld_str_t * dsp,mblk_t * mp)3638275SEric Cheng proto_detach_req(dld_str_t *dsp, mblk_t *mp)
3640Sstevel@tonic-gate {
365269Sericheng queue_t *q = dsp->ds_wq;
366269Sericheng t_uscalar_t dl_err;
3670Sstevel@tonic-gate
368269Sericheng if (MBLKL(mp) < sizeof (dl_detach_req_t)) {
369269Sericheng dl_err = DL_BADPRIM;
370269Sericheng goto failed;
371269Sericheng }
3720Sstevel@tonic-gate
373269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) {
374269Sericheng dl_err = DL_OUTSTATE;
375269Sericheng goto failed;
3760Sstevel@tonic-gate }
3770Sstevel@tonic-gate
378269Sericheng if (dsp->ds_style == DL_STYLE1) {
379269Sericheng dl_err = DL_BADPRIM;
380269Sericheng goto failed;
381269Sericheng }
382269Sericheng
3838275SEric Cheng ASSERT(dsp->ds_datathr_cnt == 0);
384269Sericheng dsp->ds_dlstate = DL_DETACH_PENDING;
3858275SEric Cheng
3865895Syz147064 dld_str_detach(dsp);
3875895Syz147064 dlokack(dsp->ds_wq, mp, DL_DETACH_REQ);
3888275SEric Cheng return;
3898275SEric Cheng
390269Sericheng failed:
391269Sericheng dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0);
3920Sstevel@tonic-gate }
3930Sstevel@tonic-gate
3940Sstevel@tonic-gate /*
395269Sericheng * DL_BIND_REQ
3960Sstevel@tonic-gate */
3978275SEric Cheng static void
proto_bind_req(dld_str_t * dsp,mblk_t * mp)3988275SEric Cheng proto_bind_req(dld_str_t *dsp, mblk_t *mp)
3990Sstevel@tonic-gate {
4008275SEric Cheng dl_bind_req_t *dlp = (dl_bind_req_t *)mp->b_rptr;
401269Sericheng int err = 0;
4023037Syz147064 uint8_t dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)];
4033037Syz147064 uint_t dlsap_addr_length;
404269Sericheng t_uscalar_t dl_err;
405269Sericheng t_scalar_t sap;
406269Sericheng queue_t *q = dsp->ds_wq;
4078275SEric Cheng mac_perim_handle_t mph;
4088275SEric Cheng void *mdip;
4098275SEric Cheng int32_t intr_cpu;
410269Sericheng
411269Sericheng if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
412269Sericheng dl_err = DL_BADPRIM;
413269Sericheng goto failed;
414269Sericheng }
415269Sericheng
416269Sericheng if (dlp->dl_xidtest_flg != 0) {
417269Sericheng dl_err = DL_NOAUTO;
418269Sericheng goto failed;
419269Sericheng }
420269Sericheng
421269Sericheng if (dlp->dl_service_mode != DL_CLDLS) {
422269Sericheng dl_err = DL_UNSUPPORTED;
423269Sericheng goto failed;
424269Sericheng }
425269Sericheng
426269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) {
427269Sericheng dl_err = DL_OUTSTATE;
428269Sericheng goto failed;
429269Sericheng }
4300Sstevel@tonic-gate
4318275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph);
4328275SEric Cheng
4339073SCathy.Zhou@Sun.COM if ((err = dls_active_set(dsp)) != 0) {
434269Sericheng dl_err = DL_SYSERR;
4358275SEric Cheng goto failed2;
436269Sericheng }
437269Sericheng
4388275SEric Cheng dsp->ds_dlstate = DL_BIND_PENDING;
439269Sericheng /*
440269Sericheng * Set the receive callback.
441269Sericheng */
4428275SEric Cheng dls_rx_set(dsp, (dsp->ds_mode == DLD_RAW) ?
443269Sericheng dld_str_rx_raw : dld_str_rx_unitdata, dsp);
4440Sstevel@tonic-gate
445269Sericheng /*
446269Sericheng * Bind the channel such that it can receive packets.
447269Sericheng */
4485895Syz147064 sap = dlp->dl_sap;
44911021SEric.Cheng@Sun.COM dsp->ds_nonip = !check_mod_above(dsp->ds_rq, "ip") &&
45011021SEric.Cheng@Sun.COM !check_mod_above(dsp->ds_rq, "arp");
45111021SEric.Cheng@Sun.COM
4528275SEric Cheng err = dls_bind(dsp, sap);
453269Sericheng if (err != 0) {
454269Sericheng switch (err) {
455269Sericheng case EINVAL:
456269Sericheng dl_err = DL_BADADDR;
457269Sericheng err = 0;
458269Sericheng break;
459269Sericheng default:
460269Sericheng dl_err = DL_SYSERR;
461269Sericheng break;
462269Sericheng }
4635895Syz147064
4648275SEric Cheng dsp->ds_dlstate = DL_UNBOUND;
4659073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_FALSE);
4668275SEric Cheng goto failed2;
4678275SEric Cheng }
468269Sericheng
4698275SEric Cheng intr_cpu = mac_client_intr_cpu(dsp->ds_mch);
4708275SEric Cheng mdip = mac_get_devinfo(dsp->ds_mh);
4718275SEric Cheng mac_perim_exit(mph);
4728275SEric Cheng
4738275SEric Cheng /*
4748275SEric Cheng * We do this after we get out of the perim to avoid deadlocks
4758275SEric Cheng * etc. since part of mac_client_retarget_intr is to walk the
4768275SEric Cheng * device tree in order to find and retarget the interrupts.
4778275SEric Cheng */
47811878SVenu.Iyer@Sun.COM if (intr_cpu != -1)
47911878SVenu.Iyer@Sun.COM mac_client_set_intr_cpu(mdip, dsp->ds_mch, intr_cpu);
4800Sstevel@tonic-gate
4810Sstevel@tonic-gate /*
4820Sstevel@tonic-gate * Copy in MAC address.
4830Sstevel@tonic-gate */
4843037Syz147064 dlsap_addr_length = dsp->ds_mip->mi_addr_length;
4858275SEric Cheng mac_unicast_primary_get(dsp->ds_mh, dlsap_addr);
4860Sstevel@tonic-gate
4870Sstevel@tonic-gate /*
4883037Syz147064 * Copy in the SAP.
4890Sstevel@tonic-gate */
4905895Syz147064 *(uint16_t *)(dlsap_addr + dlsap_addr_length) = sap;
4913037Syz147064 dlsap_addr_length += sizeof (uint16_t);
4920Sstevel@tonic-gate
4930Sstevel@tonic-gate dsp->ds_dlstate = DL_IDLE;
4943037Syz147064 dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0);
4958275SEric Cheng return;
4968275SEric Cheng
4978275SEric Cheng failed2:
4988275SEric Cheng mac_perim_exit(mph);
4990Sstevel@tonic-gate failed:
500269Sericheng dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
5010Sstevel@tonic-gate }
5020Sstevel@tonic-gate
5030Sstevel@tonic-gate /*
504269Sericheng * DL_UNBIND_REQ
5050Sstevel@tonic-gate */
5068275SEric Cheng static void
proto_unbind_req(dld_str_t * dsp,mblk_t * mp)5078275SEric Cheng proto_unbind_req(dld_str_t *dsp, mblk_t *mp)
5080Sstevel@tonic-gate {
5095895Syz147064 queue_t *q = dsp->ds_wq;
5105895Syz147064 t_uscalar_t dl_err;
5118275SEric Cheng mac_perim_handle_t mph;
512269Sericheng
5135895Syz147064 if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
5145895Syz147064 dl_err = DL_BADPRIM;
5155895Syz147064 goto failed;
5165895Syz147064 }
5175895Syz147064
5185895Syz147064 if (dsp->ds_dlstate != DL_IDLE) {
5195895Syz147064 dl_err = DL_OUTSTATE;
5205895Syz147064 goto failed;
5215895Syz147064 }
522269Sericheng
5238275SEric Cheng mutex_enter(&dsp->ds_lock);
5248275SEric Cheng while (dsp->ds_datathr_cnt != 0)
5258275SEric Cheng cv_wait(&dsp->ds_datathr_cv, &dsp->ds_lock);
526269Sericheng
5278275SEric Cheng dsp->ds_dlstate = DL_UNBIND_PENDING;
5288275SEric Cheng mutex_exit(&dsp->ds_lock);
5298275SEric Cheng
5308275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph);
531269Sericheng /*
532269Sericheng * Unbind the channel to stop packets being received.
533269Sericheng */
5349044SGirish.Moodalbail@Sun.COM dls_unbind(dsp);
5355895Syz147064
5365895Syz147064 /*
537269Sericheng * Disable polling mode, if it is enabled.
538269Sericheng */
5398275SEric Cheng (void) dld_capab_poll_disable(dsp, NULL);
5405895Syz147064
5415895Syz147064 /*
5423115Syl150051 * Clear LSO flags.
5433115Syl150051 */
5443115Syl150051 dsp->ds_lso = B_FALSE;
5453115Syl150051 dsp->ds_lso_max = 0;
5463115Syl150051
5473115Syl150051 /*
5488275SEric Cheng * Clear the receive callback.
5498275SEric Cheng */
5508275SEric Cheng dls_rx_set(dsp, NULL, NULL);
5518275SEric Cheng dsp->ds_direct = B_FALSE;
5528275SEric Cheng
5538275SEric Cheng /*
554269Sericheng * Set the mode back to the default (unitdata).
555269Sericheng */
556269Sericheng dsp->ds_mode = DLD_UNITDATA;
5571353Sericheng dsp->ds_dlstate = DL_UNBOUND;
5581353Sericheng
5599073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_FALSE);
5608275SEric Cheng mac_perim_exit(mph);
5618275SEric Cheng dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ);
5628275SEric Cheng return;
563269Sericheng failed:
564269Sericheng dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0);
5650Sstevel@tonic-gate }
5660Sstevel@tonic-gate
5670Sstevel@tonic-gate /*
568269Sericheng * DL_PROMISCON_REQ
5690Sstevel@tonic-gate */
5708275SEric Cheng static void
proto_promiscon_req(dld_str_t * dsp,mblk_t * mp)5718275SEric Cheng proto_promiscon_req(dld_str_t *dsp, mblk_t *mp)
5720Sstevel@tonic-gate {
5738275SEric Cheng dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)mp->b_rptr;
574269Sericheng int err = 0;
575269Sericheng t_uscalar_t dl_err;
5768275SEric Cheng uint32_t promisc_saved;
577269Sericheng queue_t *q = dsp->ds_wq;
5788275SEric Cheng mac_perim_handle_t mph;
579269Sericheng
580269Sericheng if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) {
581269Sericheng dl_err = DL_BADPRIM;
582269Sericheng goto failed;
583269Sericheng }
584269Sericheng
585269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED ||
586269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) {
587269Sericheng dl_err = DL_OUTSTATE;
5880Sstevel@tonic-gate goto failed;
589269Sericheng }
5900Sstevel@tonic-gate
5918275SEric Cheng promisc_saved = dsp->ds_promisc;
592269Sericheng switch (dlp->dl_level) {
593269Sericheng case DL_PROMISC_SAP:
5948275SEric Cheng dsp->ds_promisc |= DLS_PROMISC_SAP;
595269Sericheng break;
5968275SEric Cheng
5975895Syz147064 case DL_PROMISC_MULTI:
5988275SEric Cheng dsp->ds_promisc |= DLS_PROMISC_MULTI;
5995895Syz147064 break;
6008275SEric Cheng
601269Sericheng case DL_PROMISC_PHYS:
6028275SEric Cheng dsp->ds_promisc |= DLS_PROMISC_PHYS;
603269Sericheng break;
6048275SEric Cheng
605269Sericheng default:
606269Sericheng dl_err = DL_NOTSUPPORTED;
607269Sericheng goto failed;
608269Sericheng }
6090Sstevel@tonic-gate
6108275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph);
6118275SEric Cheng
6129073SCathy.Zhou@Sun.COM if ((promisc_saved == 0) && (err = dls_active_set(dsp)) != 0) {
6138275SEric Cheng dsp->ds_promisc = promisc_saved;
614269Sericheng dl_err = DL_SYSERR;
6158275SEric Cheng goto failed2;
616269Sericheng }
617269Sericheng
618269Sericheng /*
619269Sericheng * Adjust channel promiscuity.
620269Sericheng */
6218275SEric Cheng err = dls_promisc(dsp, promisc_saved);
6228275SEric Cheng
623269Sericheng if (err != 0) {
624269Sericheng dl_err = DL_SYSERR;
6258275SEric Cheng dsp->ds_promisc = promisc_saved;
6269073SCathy.Zhou@Sun.COM if (promisc_saved == 0)
6279073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_FALSE);
6288275SEric Cheng goto failed2;
629269Sericheng }
630269Sericheng
6318275SEric Cheng mac_perim_exit(mph);
6328275SEric Cheng
6338275SEric Cheng dlokack(q, mp, DL_PROMISCON_REQ);
6348275SEric Cheng return;
635269Sericheng
6368275SEric Cheng failed2:
6378275SEric Cheng mac_perim_exit(mph);
6380Sstevel@tonic-gate failed:
639269Sericheng dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err);
6400Sstevel@tonic-gate }
6410Sstevel@tonic-gate
6420Sstevel@tonic-gate /*
643269Sericheng * DL_PROMISCOFF_REQ
6440Sstevel@tonic-gate */
6458275SEric Cheng static void
proto_promiscoff_req(dld_str_t * dsp,mblk_t * mp)6468275SEric Cheng proto_promiscoff_req(dld_str_t *dsp, mblk_t *mp)
6470Sstevel@tonic-gate {
6488275SEric Cheng dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)mp->b_rptr;
649269Sericheng int err = 0;
650269Sericheng t_uscalar_t dl_err;
6518275SEric Cheng uint32_t promisc_saved;
652269Sericheng queue_t *q = dsp->ds_wq;
6538275SEric Cheng mac_perim_handle_t mph;
654269Sericheng
655269Sericheng if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
656269Sericheng dl_err = DL_BADPRIM;
6570Sstevel@tonic-gate goto failed;
658269Sericheng }
6590Sstevel@tonic-gate
660269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED ||
661269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) {
662269Sericheng dl_err = DL_OUTSTATE;
6630Sstevel@tonic-gate goto failed;
664269Sericheng }
6650Sstevel@tonic-gate
6668275SEric Cheng promisc_saved = dsp->ds_promisc;
667269Sericheng switch (dlp->dl_level) {
668269Sericheng case DL_PROMISC_SAP:
6698275SEric Cheng if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) {
6708275SEric Cheng dl_err = DL_NOTENAB;
6718275SEric Cheng goto failed;
6728275SEric Cheng }
6738275SEric Cheng dsp->ds_promisc &= ~DLS_PROMISC_SAP;
6740Sstevel@tonic-gate break;
6758275SEric Cheng
676269Sericheng case DL_PROMISC_MULTI:
6778275SEric Cheng if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) {
6788275SEric Cheng dl_err = DL_NOTENAB;
6798275SEric Cheng goto failed;
6808275SEric Cheng }
6818275SEric Cheng dsp->ds_promisc &= ~DLS_PROMISC_MULTI;
682269Sericheng break;
6838275SEric Cheng
684269Sericheng case DL_PROMISC_PHYS:
6858275SEric Cheng if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) {
6868275SEric Cheng dl_err = DL_NOTENAB;
6878275SEric Cheng goto failed;
6888275SEric Cheng }
6898275SEric Cheng dsp->ds_promisc &= ~DLS_PROMISC_PHYS;
6900Sstevel@tonic-gate break;
6918275SEric Cheng
6920Sstevel@tonic-gate default:
693269Sericheng dl_err = DL_NOTSUPPORTED;
694269Sericheng goto failed;
695269Sericheng }
696269Sericheng
6978275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph);
6988275SEric Cheng /*
6998275SEric Cheng * Adjust channel promiscuity.
7008275SEric Cheng */
7018275SEric Cheng err = dls_promisc(dsp, promisc_saved);
7025895Syz147064
703269Sericheng if (err != 0) {
7049073SCathy.Zhou@Sun.COM mac_perim_exit(mph);
7050Sstevel@tonic-gate dl_err = DL_SYSERR;
706269Sericheng goto failed;
707269Sericheng }
7089073SCathy.Zhou@Sun.COM
7099073SCathy.Zhou@Sun.COM if (dsp->ds_promisc == 0)
7109073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_FALSE);
7119073SCathy.Zhou@Sun.COM
7129073SCathy.Zhou@Sun.COM mac_perim_exit(mph);
7139073SCathy.Zhou@Sun.COM
714269Sericheng dlokack(q, mp, DL_PROMISCOFF_REQ);
7158275SEric Cheng return;
716269Sericheng failed:
717269Sericheng dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
718269Sericheng }
719269Sericheng
720269Sericheng /*
721269Sericheng * DL_ENABMULTI_REQ
722269Sericheng */
7238275SEric Cheng static void
proto_enabmulti_req(dld_str_t * dsp,mblk_t * mp)7248275SEric Cheng proto_enabmulti_req(dld_str_t *dsp, mblk_t *mp)
725269Sericheng {
7268275SEric Cheng dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)mp->b_rptr;
727269Sericheng int err = 0;
728269Sericheng t_uscalar_t dl_err;
729269Sericheng queue_t *q = dsp->ds_wq;
7308275SEric Cheng mac_perim_handle_t mph;
731269Sericheng
732269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED ||
733269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) {
734269Sericheng dl_err = DL_OUTSTATE;
735269Sericheng goto failed;
736269Sericheng }
737269Sericheng
738269Sericheng if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
739269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
740269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
741269Sericheng dl_err = DL_BADPRIM;
742269Sericheng goto failed;
743269Sericheng }
744269Sericheng
7458275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph);
7468275SEric Cheng
7479073SCathy.Zhou@Sun.COM if ((dsp->ds_dmap == NULL) && (err = dls_active_set(dsp)) != 0) {
748269Sericheng dl_err = DL_SYSERR;
7498275SEric Cheng goto failed2;
7500Sstevel@tonic-gate }
7510Sstevel@tonic-gate
7528275SEric Cheng err = dls_multicst_add(dsp, mp->b_rptr + dlp->dl_addr_offset);
753269Sericheng if (err != 0) {
754269Sericheng switch (err) {
755269Sericheng case EINVAL:
756269Sericheng dl_err = DL_BADADDR;
757269Sericheng err = 0;
758269Sericheng break;
759269Sericheng case ENOSPC:
760269Sericheng dl_err = DL_TOOMANY;
761269Sericheng err = 0;
762269Sericheng break;
763269Sericheng default:
764269Sericheng dl_err = DL_SYSERR;
765269Sericheng break;
766269Sericheng }
7679073SCathy.Zhou@Sun.COM if (dsp->ds_dmap == NULL)
7689073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_FALSE);
7698275SEric Cheng goto failed2;
770269Sericheng }
771269Sericheng
7728275SEric Cheng mac_perim_exit(mph);
7738275SEric Cheng
7748275SEric Cheng dlokack(q, mp, DL_ENABMULTI_REQ);
7758275SEric Cheng return;
776269Sericheng
7778275SEric Cheng failed2:
7788275SEric Cheng mac_perim_exit(mph);
779269Sericheng failed:
780269Sericheng dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
781269Sericheng }
782269Sericheng
783269Sericheng /*
784269Sericheng * DL_DISABMULTI_REQ
785269Sericheng */
7868275SEric Cheng static void
proto_disabmulti_req(dld_str_t * dsp,mblk_t * mp)7878275SEric Cheng proto_disabmulti_req(dld_str_t *dsp, mblk_t *mp)
788269Sericheng {
7898275SEric Cheng dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)mp->b_rptr;
790269Sericheng int err = 0;
791269Sericheng t_uscalar_t dl_err;
792269Sericheng queue_t *q = dsp->ds_wq;
7938275SEric Cheng mac_perim_handle_t mph;
794269Sericheng
795269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED ||
796269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) {
797269Sericheng dl_err = DL_OUTSTATE;
798269Sericheng goto failed;
799269Sericheng }
800269Sericheng
801269Sericheng if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
802269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
803269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
804269Sericheng dl_err = DL_BADPRIM;
805269Sericheng goto failed;
806269Sericheng }
807269Sericheng
8088275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph);
8098275SEric Cheng err = dls_multicst_remove(dsp, mp->b_rptr + dlp->dl_addr_offset);
8109073SCathy.Zhou@Sun.COM if ((err == 0) && (dsp->ds_dmap == NULL))
8119073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_FALSE);
8128275SEric Cheng mac_perim_exit(mph);
8138275SEric Cheng
814269Sericheng if (err != 0) {
8158275SEric Cheng switch (err) {
816269Sericheng case EINVAL:
817269Sericheng dl_err = DL_BADADDR;
818269Sericheng err = 0;
819269Sericheng break;
8208275SEric Cheng
821269Sericheng case ENOENT:
822269Sericheng dl_err = DL_NOTENAB;
823269Sericheng err = 0;
824269Sericheng break;
8258275SEric Cheng
826269Sericheng default:
827269Sericheng dl_err = DL_SYSERR;
828269Sericheng break;
829269Sericheng }
830269Sericheng goto failed;
831269Sericheng }
832269Sericheng dlokack(q, mp, DL_DISABMULTI_REQ);
8338275SEric Cheng return;
834269Sericheng failed:
835269Sericheng dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
8360Sstevel@tonic-gate }
8370Sstevel@tonic-gate
8380Sstevel@tonic-gate /*
839269Sericheng * DL_PHYS_ADDR_REQ
8400Sstevel@tonic-gate */
8418275SEric Cheng static void
proto_physaddr_req(dld_str_t * dsp,mblk_t * mp)8428275SEric Cheng proto_physaddr_req(dld_str_t *dsp, mblk_t *mp)
8430Sstevel@tonic-gate {
8448275SEric Cheng dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)mp->b_rptr;
845269Sericheng queue_t *q = dsp->ds_wq;
84610616SSebastien.Roy@Sun.COM t_uscalar_t dl_err = 0;
84710616SSebastien.Roy@Sun.COM char *addr = NULL;
848269Sericheng uint_t addr_length;
849269Sericheng
850269Sericheng if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
851269Sericheng dl_err = DL_BADPRIM;
85210616SSebastien.Roy@Sun.COM goto done;
853269Sericheng }
854269Sericheng
855269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED ||
856269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) {
857269Sericheng dl_err = DL_OUTSTATE;
85810616SSebastien.Roy@Sun.COM goto done;
859269Sericheng }
8600Sstevel@tonic-gate
861269Sericheng addr_length = dsp->ds_mip->mi_addr_length;
8626353Sdr146992 if (addr_length > 0) {
8638275SEric Cheng addr = kmem_alloc(addr_length, KM_SLEEP);
86410616SSebastien.Roy@Sun.COM switch (dlp->dl_addr_type) {
86510616SSebastien.Roy@Sun.COM case DL_CURR_PHYS_ADDR:
8668275SEric Cheng mac_unicast_primary_get(dsp->ds_mh, (uint8_t *)addr);
86710616SSebastien.Roy@Sun.COM break;
86810616SSebastien.Roy@Sun.COM case DL_FACT_PHYS_ADDR:
8698275SEric Cheng bcopy(dsp->ds_mip->mi_unicst_addr, addr, addr_length);
87010616SSebastien.Roy@Sun.COM break;
87110616SSebastien.Roy@Sun.COM case DL_CURR_DEST_ADDR:
87210616SSebastien.Roy@Sun.COM if (!mac_dst_get(dsp->ds_mh, (uint8_t *)addr))
87310616SSebastien.Roy@Sun.COM dl_err = DL_NOTSUPPORTED;
87410616SSebastien.Roy@Sun.COM break;
87510616SSebastien.Roy@Sun.COM default:
87610616SSebastien.Roy@Sun.COM dl_err = DL_UNSUPPORTED;
87710616SSebastien.Roy@Sun.COM }
87810616SSebastien.Roy@Sun.COM }
87910616SSebastien.Roy@Sun.COM done:
88010616SSebastien.Roy@Sun.COM if (dl_err == 0)
8816353Sdr146992 dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
88210616SSebastien.Roy@Sun.COM else
88310616SSebastien.Roy@Sun.COM dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
88410616SSebastien.Roy@Sun.COM if (addr != NULL)
8856353Sdr146992 kmem_free(addr, addr_length);
886269Sericheng }
8870Sstevel@tonic-gate
888269Sericheng /*
889269Sericheng * DL_SET_PHYS_ADDR_REQ
890269Sericheng */
8918275SEric Cheng static void
proto_setphysaddr_req(dld_str_t * dsp,mblk_t * mp)8928275SEric Cheng proto_setphysaddr_req(dld_str_t *dsp, mblk_t *mp)
893269Sericheng {
8948275SEric Cheng dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)mp->b_rptr;
895269Sericheng int err = 0;
896269Sericheng t_uscalar_t dl_err;
897269Sericheng queue_t *q = dsp->ds_wq;
8988275SEric Cheng mac_perim_handle_t mph;
8990Sstevel@tonic-gate
900269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED ||
901269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) {
902269Sericheng dl_err = DL_OUTSTATE;
903269Sericheng goto failed;
904269Sericheng }
905269Sericheng
906269Sericheng if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
907269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
908269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
909269Sericheng dl_err = DL_BADPRIM;
910269Sericheng goto failed;
9110Sstevel@tonic-gate }
9120Sstevel@tonic-gate
9138275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, &mph);
9148275SEric Cheng
9159073SCathy.Zhou@Sun.COM if ((err = dls_active_set(dsp)) != 0) {
916269Sericheng dl_err = DL_SYSERR;
9178275SEric Cheng goto failed2;
918269Sericheng }
919269Sericheng
92010734SEric Cheng /*
92110734SEric Cheng * If mac-nospoof is enabled and the link is owned by a
92210734SEric Cheng * non-global zone, changing the mac address is not allowed.
92310734SEric Cheng */
92410734SEric Cheng if (dsp->ds_dlp->dl_zid != GLOBAL_ZONEID &&
92510734SEric Cheng mac_protect_enabled(dsp->ds_mch, MPT_MACNOSPOOF)) {
92610734SEric Cheng dls_active_clear(dsp, B_FALSE);
92710734SEric Cheng err = EACCES;
92810734SEric Cheng goto failed2;
92910734SEric Cheng }
93010734SEric Cheng
9318275SEric Cheng err = mac_unicast_primary_set(dsp->ds_mh,
9328275SEric Cheng mp->b_rptr + dlp->dl_addr_offset);
933269Sericheng if (err != 0) {
934269Sericheng switch (err) {
935269Sericheng case EINVAL:
936269Sericheng dl_err = DL_BADADDR;
937269Sericheng err = 0;
938269Sericheng break;
939269Sericheng
940269Sericheng default:
941269Sericheng dl_err = DL_SYSERR;
942269Sericheng break;
943269Sericheng }
9449073SCathy.Zhou@Sun.COM dls_active_clear(dsp, B_FALSE);
9458275SEric Cheng goto failed2;
946269Sericheng
947269Sericheng }
9485895Syz147064
9498275SEric Cheng mac_perim_exit(mph);
9508275SEric Cheng
9518275SEric Cheng dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
9528275SEric Cheng return;
953269Sericheng
9548275SEric Cheng failed2:
9558275SEric Cheng mac_perim_exit(mph);
956269Sericheng failed:
957269Sericheng dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
958269Sericheng }
959269Sericheng
960269Sericheng /*
961269Sericheng * DL_UDQOS_REQ
962269Sericheng */
9638275SEric Cheng static void
proto_udqos_req(dld_str_t * dsp,mblk_t * mp)9648275SEric Cheng proto_udqos_req(dld_str_t *dsp, mblk_t *mp)
965269Sericheng {
9668275SEric Cheng dl_udqos_req_t *dlp = (dl_udqos_req_t *)mp->b_rptr;
967269Sericheng dl_qos_cl_sel1_t *selp;
968269Sericheng int off, len;
969269Sericheng t_uscalar_t dl_err;
970269Sericheng queue_t *q = dsp->ds_wq;
971269Sericheng
972269Sericheng off = dlp->dl_qos_offset;
973269Sericheng len = dlp->dl_qos_length;
974269Sericheng
975269Sericheng if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
976269Sericheng dl_err = DL_BADPRIM;
977269Sericheng goto failed;
978269Sericheng }
979269Sericheng
980269Sericheng selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
981269Sericheng if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
982269Sericheng dl_err = DL_BADQOSTYPE;
983269Sericheng goto failed;
984269Sericheng }
985269Sericheng
9862760Sdg199075 if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
987269Sericheng selp->dl_priority < 0) {
988269Sericheng dl_err = DL_BADQOSPARAM;
989269Sericheng goto failed;
990269Sericheng }
991269Sericheng
9925895Syz147064 dsp->ds_pri = selp->dl_priority;
993269Sericheng dlokack(q, mp, DL_UDQOS_REQ);
9948275SEric Cheng return;
995269Sericheng failed:
996269Sericheng dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
9970Sstevel@tonic-gate }
9980Sstevel@tonic-gate
9991184Skrgopi static boolean_t
check_mod_above(queue_t * q,const char * mod)100011021SEric.Cheng@Sun.COM check_mod_above(queue_t *q, const char *mod)
10011184Skrgopi {
10021184Skrgopi queue_t *next_q;
10031184Skrgopi boolean_t ret = B_TRUE;
10041184Skrgopi
10051184Skrgopi claimstr(q);
10061184Skrgopi next_q = q->q_next;
100711021SEric.Cheng@Sun.COM if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, mod) != 0)
10081184Skrgopi ret = B_FALSE;
10091184Skrgopi releasestr(q);
10101184Skrgopi return (ret);
10111184Skrgopi }
10121184Skrgopi
10130Sstevel@tonic-gate /*
1014269Sericheng * DL_CAPABILITY_REQ
10150Sstevel@tonic-gate */
10168275SEric Cheng static void
proto_capability_req(dld_str_t * dsp,mblk_t * mp)10178275SEric Cheng proto_capability_req(dld_str_t *dsp, mblk_t *mp)
10180Sstevel@tonic-gate {
10198275SEric Cheng dl_capability_req_t *dlp = (dl_capability_req_t *)mp->b_rptr;
1020269Sericheng dl_capability_sub_t *sp;
1021269Sericheng size_t size, len;
1022269Sericheng offset_t off, end;
1023269Sericheng t_uscalar_t dl_err;
1024269Sericheng queue_t *q = dsp->ds_wq;
1025269Sericheng
1026269Sericheng if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1027269Sericheng dl_err = DL_BADPRIM;
1028269Sericheng goto failed;
1029269Sericheng }
1030269Sericheng
1031269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED ||
1032269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) {
1033269Sericheng dl_err = DL_OUTSTATE;
1034269Sericheng goto failed;
1035269Sericheng }
1036269Sericheng
1037269Sericheng /*
1038269Sericheng * This request is overloaded. If there are no requested capabilities
1039269Sericheng * then we just want to acknowledge with all the capabilities we
1040269Sericheng * support. Otherwise we enable the set of capabilities requested.
1041269Sericheng */
1042269Sericheng if (dlp->dl_sub_length == 0) {
10438275SEric Cheng proto_capability_advertise(dsp, mp);
10448275SEric Cheng return;
1045269Sericheng }
1046269Sericheng
1047269Sericheng if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1048269Sericheng dl_err = DL_BADPRIM;
1049269Sericheng goto failed;
1050269Sericheng }
1051269Sericheng
1052269Sericheng dlp->dl_primitive = DL_CAPABILITY_ACK;
1053269Sericheng
1054269Sericheng off = dlp->dl_sub_offset;
1055269Sericheng len = dlp->dl_sub_length;
10560Sstevel@tonic-gate
10570Sstevel@tonic-gate /*
1058269Sericheng * Walk the list of capabilities to be enabled.
10590Sstevel@tonic-gate */
1060269Sericheng for (end = off + len; off < end; ) {
1061269Sericheng sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1062269Sericheng size = sizeof (dl_capability_sub_t) + sp->dl_length;
1063269Sericheng
1064269Sericheng if (off + size > end ||
1065269Sericheng !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1066269Sericheng dl_err = DL_BADPRIM;
1067269Sericheng goto failed;
1068269Sericheng }
1069269Sericheng
1070269Sericheng switch (sp->dl_cap) {
1071269Sericheng /*
1072269Sericheng * TCP/IP checksum offload to hardware.
1073269Sericheng */
1074269Sericheng case DL_CAPAB_HCKSUM: {
1075269Sericheng dl_capab_hcksum_t *hcksump;
1076269Sericheng dl_capab_hcksum_t hcksum;
1077269Sericheng
1078269Sericheng hcksump = (dl_capab_hcksum_t *)&sp[1];
1079269Sericheng /*
1080269Sericheng * Copy for alignment.
1081269Sericheng */
1082269Sericheng bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1083269Sericheng dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1084269Sericheng bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1085269Sericheng break;
1086269Sericheng }
1087269Sericheng
10888275SEric Cheng case DL_CAPAB_DLD: {
10898275SEric Cheng dl_capab_dld_t *dldp;
10908275SEric Cheng dl_capab_dld_t dld;
10913115Syl150051
10928275SEric Cheng dldp = (dl_capab_dld_t *)&sp[1];
1093269Sericheng /*
1094269Sericheng * Copy for alignment.
1095269Sericheng */
10968275SEric Cheng bcopy(dldp, &dld, sizeof (dl_capab_dld_t));
10978275SEric Cheng dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq);
10988275SEric Cheng bcopy(&dld, dldp, sizeof (dl_capab_dld_t));
1099269Sericheng break;
1100269Sericheng }
1101269Sericheng default:
1102269Sericheng break;
1103269Sericheng }
1104269Sericheng off += size;
1105269Sericheng }
1106269Sericheng qreply(q, mp);
11078275SEric Cheng return;
1108269Sericheng failed:
1109269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
11100Sstevel@tonic-gate }
11110Sstevel@tonic-gate
11120Sstevel@tonic-gate /*
1113269Sericheng * DL_NOTIFY_REQ
11140Sstevel@tonic-gate */
11158275SEric Cheng static void
proto_notify_req(dld_str_t * dsp,mblk_t * mp)11168275SEric Cheng proto_notify_req(dld_str_t *dsp, mblk_t *mp)
11170Sstevel@tonic-gate {
11188275SEric Cheng dl_notify_req_t *dlp = (dl_notify_req_t *)mp->b_rptr;
1119269Sericheng t_uscalar_t dl_err;
1120269Sericheng queue_t *q = dsp->ds_wq;
1121269Sericheng uint_t note =
1122269Sericheng DL_NOTE_PROMISC_ON_PHYS |
1123269Sericheng DL_NOTE_PROMISC_OFF_PHYS |
1124269Sericheng DL_NOTE_PHYS_ADDR |
1125269Sericheng DL_NOTE_LINK_UP |
1126269Sericheng DL_NOTE_LINK_DOWN |
11272311Sseb DL_NOTE_CAPAB_RENEG |
11288910SGirish.Moodalbail@Sun.COM DL_NOTE_FASTPATH_FLUSH |
112910616SSebastien.Roy@Sun.COM DL_NOTE_SPEED |
113012748SSowmini.Varadhan@oracle.COM DL_NOTE_SDU_SIZE|
1131*13123SErik.Nordmark@Sun.COM DL_NOTE_SDU_SIZE2|
113212748SSowmini.Varadhan@oracle.COM DL_NOTE_ALLOWED_IPS;
11330Sstevel@tonic-gate
1134269Sericheng if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1135269Sericheng dl_err = DL_BADPRIM;
1136269Sericheng goto failed;
1137269Sericheng }
11380Sstevel@tonic-gate
1139269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED ||
1140269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) {
1141269Sericheng dl_err = DL_OUTSTATE;
1142269Sericheng goto failed;
11430Sstevel@tonic-gate }
11440Sstevel@tonic-gate
11455895Syz147064 note &= ~(mac_no_notification(dsp->ds_mh));
11465895Syz147064
1147269Sericheng /*
1148269Sericheng * Cache the notifications that are being enabled.
1149269Sericheng */
1150269Sericheng dsp->ds_notifications = dlp->dl_notifications & note;
1151269Sericheng /*
1152269Sericheng * The ACK carries all notifications regardless of which set is
1153269Sericheng * being enabled.
1154269Sericheng */
1155269Sericheng dlnotifyack(q, mp, note);
1156269Sericheng
1157269Sericheng /*
11588275SEric Cheng * Generate DL_NOTIFY_IND messages for each enabled notification.
1159269Sericheng */
1160269Sericheng if (dsp->ds_notifications != 0) {
1161269Sericheng dld_str_notify_ind(dsp);
1162269Sericheng }
11638275SEric Cheng return;
1164269Sericheng failed:
1165269Sericheng dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
11660Sstevel@tonic-gate }
11670Sstevel@tonic-gate
11680Sstevel@tonic-gate /*
11698275SEric Cheng * DL_UINTDATA_REQ
11700Sstevel@tonic-gate */
11715895Syz147064 void
proto_unitdata_req(dld_str_t * dsp,mblk_t * mp)11728275SEric Cheng proto_unitdata_req(dld_str_t *dsp, mblk_t *mp)
11730Sstevel@tonic-gate {
1174269Sericheng queue_t *q = dsp->ds_wq;
11755895Syz147064 dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_rptr;
1176269Sericheng off_t off;
1177269Sericheng size_t len, size;
1178269Sericheng const uint8_t *addr;
1179269Sericheng uint16_t sap;
1180269Sericheng uint_t addr_length;
11812311Sseb mblk_t *bp, *payload;
1182269Sericheng uint32_t start, stuff, end, value, flags;
1183269Sericheng t_uscalar_t dl_err;
11845903Ssowmini uint_t max_sdu;
1185269Sericheng
1186269Sericheng if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
11878275SEric Cheng dlerrorack(q, mp, DL_UNITDATA_REQ, DL_BADPRIM, 0);
11888275SEric Cheng return;
1189269Sericheng }
1190269Sericheng
11918275SEric Cheng mutex_enter(&dsp->ds_lock);
11928275SEric Cheng if (dsp->ds_dlstate != DL_IDLE) {
11938275SEric Cheng mutex_exit(&dsp->ds_lock);
11948275SEric Cheng dlerrorack(q, mp, DL_UNITDATA_REQ, DL_OUTSTATE, 0);
11958275SEric Cheng return;
11968275SEric Cheng }
11978275SEric Cheng DLD_DATATHR_INC(dsp);
11988275SEric Cheng mutex_exit(&dsp->ds_lock);
11998275SEric Cheng
1200269Sericheng addr_length = dsp->ds_mip->mi_addr_length;
1201269Sericheng
1202269Sericheng off = dlp->dl_dest_addr_offset;
1203269Sericheng len = dlp->dl_dest_addr_length;
1204269Sericheng
1205269Sericheng if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1206269Sericheng dl_err = DL_BADPRIM;
1207269Sericheng goto failed;
1208269Sericheng }
1209269Sericheng
1210269Sericheng if (len != addr_length + sizeof (uint16_t)) {
1211269Sericheng dl_err = DL_BADADDR;
1212269Sericheng goto failed;
1213269Sericheng }
1214269Sericheng
1215269Sericheng addr = mp->b_rptr + off;
1216269Sericheng sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1217269Sericheng
1218269Sericheng /*
1219269Sericheng * Check the length of the packet and the block types.
1220269Sericheng */
1221269Sericheng size = 0;
12222311Sseb payload = mp->b_cont;
12232311Sseb for (bp = payload; bp != NULL; bp = bp->b_cont) {
1224269Sericheng if (DB_TYPE(bp) != M_DATA)
1225269Sericheng goto baddata;
1226269Sericheng
1227269Sericheng size += MBLKL(bp);
1228269Sericheng }
1229269Sericheng
12305903Ssowmini mac_sdu_get(dsp->ds_mh, NULL, &max_sdu);
12315903Ssowmini if (size > max_sdu)
1232269Sericheng goto baddata;
1233269Sericheng
1234269Sericheng /*
1235269Sericheng * Build a packet header.
1236269Sericheng */
12378275SEric Cheng if ((bp = dls_header(dsp, addr, sap, dlp->dl_priority.dl_max,
12382760Sdg199075 &payload)) == NULL) {
1239269Sericheng dl_err = DL_BADADDR;
1240269Sericheng goto failed;
1241269Sericheng }
1242269Sericheng
1243269Sericheng /*
1244269Sericheng * We no longer need the M_PROTO header, so free it.
1245269Sericheng */
1246269Sericheng freeb(mp);
1247269Sericheng
1248269Sericheng /*
1249269Sericheng * Transfer the checksum offload information if it is present.
1250269Sericheng */
12512311Sseb hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value,
1252269Sericheng &flags);
12532311Sseb (void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0);
1254269Sericheng
1255269Sericheng /*
1256269Sericheng * Link the payload onto the new header.
1257269Sericheng */
1258269Sericheng ASSERT(bp->b_cont == NULL);
12592311Sseb bp->b_cont = payload;
12608275SEric Cheng
12618275SEric Cheng /*
12628275SEric Cheng * No lock can be held across modules and putnext()'s,
12638275SEric Cheng * which can happen here with the call from DLD_TX().
12648275SEric Cheng */
12658275SEric Cheng if (DLD_TX(dsp, bp, 0, 0) != NULL) {
12668275SEric Cheng /* flow-controlled */
12678275SEric Cheng DLD_SETQFULL(dsp);
12688275SEric Cheng }
12698275SEric Cheng DLD_DATATHR_DCR(dsp);
12705895Syz147064 return;
12718275SEric Cheng
1272269Sericheng failed:
1273269Sericheng dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
12748275SEric Cheng DLD_DATATHR_DCR(dsp);
12755895Syz147064 return;
1276269Sericheng
1277269Sericheng baddata:
1278269Sericheng dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
12798275SEric Cheng DLD_DATATHR_DCR(dsp);
1280269Sericheng }
1281269Sericheng
1282269Sericheng /*
1283269Sericheng * DL_PASSIVE_REQ
1284269Sericheng */
12858275SEric Cheng static void
proto_passive_req(dld_str_t * dsp,mblk_t * mp)12868275SEric Cheng proto_passive_req(dld_str_t *dsp, mblk_t *mp)
1287269Sericheng {
1288269Sericheng t_uscalar_t dl_err;
1289269Sericheng
12905895Syz147064 /*
1291269Sericheng * If we've already become active by issuing an active primitive,
1292269Sericheng * then it's too late to try to become passive.
1293269Sericheng */
1294269Sericheng if (dsp->ds_passivestate == DLD_ACTIVE) {
1295269Sericheng dl_err = DL_OUTSTATE;
1296269Sericheng goto failed;
1297269Sericheng }
1298269Sericheng
1299269Sericheng if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1300269Sericheng dl_err = DL_BADPRIM;
1301269Sericheng goto failed;
1302269Sericheng }
1303269Sericheng
1304269Sericheng dsp->ds_passivestate = DLD_PASSIVE;
1305269Sericheng dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
13068275SEric Cheng return;
1307269Sericheng failed:
1308269Sericheng dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1309269Sericheng }
1310269Sericheng
13118275SEric Cheng
1312269Sericheng /*
1313269Sericheng * Catch-all handler.
1314269Sericheng */
13158275SEric Cheng static void
proto_req(dld_str_t * dsp,mblk_t * mp)13168275SEric Cheng proto_req(dld_str_t *dsp, mblk_t *mp)
13178275SEric Cheng {
13188275SEric Cheng union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr;
13198275SEric Cheng
13208275SEric Cheng dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
13218275SEric Cheng }
13228275SEric Cheng
13238275SEric Cheng static int
dld_capab_perim(dld_str_t * dsp,void * data,uint_t flags)13248275SEric Cheng dld_capab_perim(dld_str_t *dsp, void *data, uint_t flags)
1325269Sericheng {
13268275SEric Cheng switch (flags) {
13278275SEric Cheng case DLD_ENABLE:
13288275SEric Cheng mac_perim_enter_by_mh(dsp->ds_mh, (mac_perim_handle_t *)data);
13298275SEric Cheng return (0);
13308275SEric Cheng
13318275SEric Cheng case DLD_DISABLE:
13328275SEric Cheng mac_perim_exit((mac_perim_handle_t)data);
13338275SEric Cheng return (0);
13348275SEric Cheng
13358275SEric Cheng case DLD_QUERY:
13368275SEric Cheng return (mac_perim_held(dsp->ds_mh));
13378275SEric Cheng }
13388275SEric Cheng return (0);
13390Sstevel@tonic-gate }
13400Sstevel@tonic-gate
13418275SEric Cheng static int
dld_capab_direct(dld_str_t * dsp,void * data,uint_t flags)13428275SEric Cheng dld_capab_direct(dld_str_t *dsp, void *data, uint_t flags)
13430Sstevel@tonic-gate {
13448275SEric Cheng dld_capab_direct_t *direct = data;
13458275SEric Cheng
13468275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
13470Sstevel@tonic-gate
13488275SEric Cheng switch (flags) {
13498275SEric Cheng case DLD_ENABLE:
13508275SEric Cheng dls_rx_set(dsp, (dls_rx_t)direct->di_rx_cf,
13518275SEric Cheng direct->di_rx_ch);
13528833SVenu.Iyer@Sun.COM
13538275SEric Cheng direct->di_tx_df = (uintptr_t)str_mdata_fastpath_put;
13548275SEric Cheng direct->di_tx_dh = dsp;
13558275SEric Cheng direct->di_tx_cb_df = (uintptr_t)mac_client_tx_notify;
13568275SEric Cheng direct->di_tx_cb_dh = dsp->ds_mch;
13578833SVenu.Iyer@Sun.COM direct->di_tx_fctl_df = (uintptr_t)mac_tx_is_flow_blocked;
13588833SVenu.Iyer@Sun.COM direct->di_tx_fctl_dh = dsp->ds_mch;
13598833SVenu.Iyer@Sun.COM
13608275SEric Cheng dsp->ds_direct = B_TRUE;
13618275SEric Cheng
13628275SEric Cheng return (0);
1363269Sericheng
13648275SEric Cheng case DLD_DISABLE:
13658275SEric Cheng dls_rx_set(dsp, (dsp->ds_mode == DLD_FASTPATH) ?
13668275SEric Cheng dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp);
13678275SEric Cheng dsp->ds_direct = B_FALSE;
13688275SEric Cheng
13698275SEric Cheng return (0);
13708275SEric Cheng }
13718275SEric Cheng return (ENOTSUP);
13728275SEric Cheng }
13730Sstevel@tonic-gate
13748275SEric Cheng /*
13758275SEric Cheng * dld_capab_poll_enable()
13768275SEric Cheng *
13778275SEric Cheng * This function is misnamed. All polling and fanouts are run out of the
13788275SEric Cheng * lower mac (in case of VNIC and the only mac in case of NICs). The
13798275SEric Cheng * availability of Rx ring and promiscous mode is all taken care between
13808275SEric Cheng * the soft ring set (mac_srs), the Rx ring, and S/W classifier. Any
13818275SEric Cheng * fanout necessary is done by the soft rings that are part of the
13828275SEric Cheng * mac_srs (by default mac_srs sends the packets up via a TCP and
13838275SEric Cheng * non TCP soft ring).
13848275SEric Cheng *
13858275SEric Cheng * The mac_srs (or its associated soft rings) always store the ill_rx_ring
13868275SEric Cheng * (the cookie returned when they registered with IP during plumb) as their
13878275SEric Cheng * 2nd argument which is passed up as mac_resource_handle_t. The upcall
13888275SEric Cheng * function and 1st argument is what the caller registered when they
13898275SEric Cheng * called mac_rx_classify_flow_add() to register the flow. For VNIC,
13908275SEric Cheng * the function is vnic_rx and argument is vnic_t. For regular NIC
13918275SEric Cheng * case, it mac_rx_default and mac_handle_t. As explained above, the
13928275SEric Cheng * mac_srs (or its soft ring) will add the ill_rx_ring (mac_resource_handle_t)
13938275SEric Cheng * from its stored 2nd argument.
13948275SEric Cheng */
13958275SEric Cheng static int
dld_capab_poll_enable(dld_str_t * dsp,dld_capab_poll_t * poll)13968275SEric Cheng dld_capab_poll_enable(dld_str_t *dsp, dld_capab_poll_t *poll)
13978275SEric Cheng {
13988275SEric Cheng if (dsp->ds_polling)
13998275SEric Cheng return (EINVAL);
14008275SEric Cheng
14018275SEric Cheng if ((dld_opt & DLD_OPT_NO_POLL) != 0 || dsp->ds_mode == DLD_RAW)
14028275SEric Cheng return (ENOTSUP);
14030Sstevel@tonic-gate
14040Sstevel@tonic-gate /*
14058275SEric Cheng * Enable client polling if and only if DLS bypass is possible.
14068275SEric Cheng * Special cases like VLANs need DLS processing in the Rx data path.
14078275SEric Cheng * In such a case we can neither allow the client (IP) to directly
14088275SEric Cheng * poll the softring (since DLS processing hasn't been done) nor can
14098275SEric Cheng * we allow DLS bypass.
14100Sstevel@tonic-gate */
14118275SEric Cheng if (!mac_rx_bypass_set(dsp->ds_mch, dsp->ds_rx, dsp->ds_rx_arg))
14128275SEric Cheng return (ENOTSUP);
14130Sstevel@tonic-gate
14140Sstevel@tonic-gate /*
14158275SEric Cheng * Register soft ring resources. This will come in handy later if
14168275SEric Cheng * the user decides to modify CPU bindings to use more CPUs for the
14178275SEric Cheng * device in which case we will switch to fanout using soft rings.
14180Sstevel@tonic-gate */
14198275SEric Cheng mac_resource_set_common(dsp->ds_mch,
14208275SEric Cheng (mac_resource_add_t)poll->poll_ring_add_cf,
14218275SEric Cheng (mac_resource_remove_t)poll->poll_ring_remove_cf,
14228275SEric Cheng (mac_resource_quiesce_t)poll->poll_ring_quiesce_cf,
14238275SEric Cheng (mac_resource_restart_t)poll->poll_ring_restart_cf,
14248275SEric Cheng (mac_resource_bind_t)poll->poll_ring_bind_cf,
14258275SEric Cheng poll->poll_ring_ch);
14268275SEric Cheng
14278275SEric Cheng mac_client_poll_enable(dsp->ds_mch);
14280Sstevel@tonic-gate
14290Sstevel@tonic-gate dsp->ds_polling = B_TRUE;
14308275SEric Cheng return (0);
14318275SEric Cheng }
14328275SEric Cheng
14338275SEric Cheng /* ARGSUSED */
14348275SEric Cheng static int
dld_capab_poll_disable(dld_str_t * dsp,dld_capab_poll_t * poll)14358275SEric Cheng dld_capab_poll_disable(dld_str_t *dsp, dld_capab_poll_t *poll)
14368275SEric Cheng {
14378275SEric Cheng if (!dsp->ds_polling)
14388275SEric Cheng return (EINVAL);
14398275SEric Cheng
14408275SEric Cheng mac_client_poll_disable(dsp->ds_mch);
14418275SEric Cheng mac_resource_set(dsp->ds_mch, NULL, NULL);
14428275SEric Cheng
14438275SEric Cheng dsp->ds_polling = B_FALSE;
14448275SEric Cheng return (0);
14450Sstevel@tonic-gate }
14460Sstevel@tonic-gate
14478275SEric Cheng static int
dld_capab_poll(dld_str_t * dsp,void * data,uint_t flags)14488275SEric Cheng dld_capab_poll(dld_str_t *dsp, void *data, uint_t flags)
14498275SEric Cheng {
14508275SEric Cheng dld_capab_poll_t *poll = data;
14518275SEric Cheng
14528275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
14538275SEric Cheng
14548275SEric Cheng switch (flags) {
14558275SEric Cheng case DLD_ENABLE:
14568275SEric Cheng return (dld_capab_poll_enable(dsp, poll));
14578275SEric Cheng case DLD_DISABLE:
14588275SEric Cheng return (dld_capab_poll_disable(dsp, poll));
14598275SEric Cheng }
14608275SEric Cheng return (ENOTSUP);
14618275SEric Cheng }
14628275SEric Cheng
14638275SEric Cheng static int
dld_capab_lso(dld_str_t * dsp,void * data,uint_t flags)14648275SEric Cheng dld_capab_lso(dld_str_t *dsp, void *data, uint_t flags)
14651184Skrgopi {
14668275SEric Cheng dld_capab_lso_t *lso = data;
14678275SEric Cheng
14688275SEric Cheng ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
14698275SEric Cheng
14708275SEric Cheng switch (flags) {
14718275SEric Cheng case DLD_ENABLE: {
14728275SEric Cheng mac_capab_lso_t mac_lso;
14731184Skrgopi
14748275SEric Cheng /*
14758275SEric Cheng * Check if LSO is supported on this MAC & enable LSO
14768275SEric Cheng * accordingly.
14778275SEric Cheng */
14788275SEric Cheng if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) {
14798275SEric Cheng lso->lso_max = mac_lso.lso_basic_tcp_ipv4.lso_max;
14808275SEric Cheng lso->lso_flags = 0;
14818275SEric Cheng /* translate the flag for mac clients */
14828275SEric Cheng if ((mac_lso.lso_flags & LSO_TX_BASIC_TCP_IPV4) != 0)
148311042SErik.Nordmark@Sun.COM lso->lso_flags |= DLD_LSO_BASIC_TCP_IPV4;
14848275SEric Cheng dsp->ds_lso = B_TRUE;
14858275SEric Cheng dsp->ds_lso_max = lso->lso_max;
14868275SEric Cheng } else {
14878275SEric Cheng dsp->ds_lso = B_FALSE;
14888275SEric Cheng dsp->ds_lso_max = 0;
14898275SEric Cheng return (ENOTSUP);
14908275SEric Cheng }
14918275SEric Cheng return (0);
14928275SEric Cheng }
14938275SEric Cheng case DLD_DISABLE: {
14948275SEric Cheng dsp->ds_lso = B_FALSE;
14958275SEric Cheng dsp->ds_lso_max = 0;
14968275SEric Cheng return (0);
14978275SEric Cheng }
14988275SEric Cheng }
14998275SEric Cheng return (ENOTSUP);
15008275SEric Cheng }
15018275SEric Cheng
15028275SEric Cheng static int
dld_capab(dld_str_t * dsp,uint_t type,void * data,uint_t flags)15038275SEric Cheng dld_capab(dld_str_t *dsp, uint_t type, void *data, uint_t flags)
15048275SEric Cheng {
15058275SEric Cheng int err;
15061184Skrgopi
15071184Skrgopi /*
15088275SEric Cheng * Don't enable direct callback capabilities unless the caller is
15098275SEric Cheng * the IP client. When a module is inserted in a stream (_I_INSERT)
15108275SEric Cheng * the stack initiates capability disable, but due to races, the
15118275SEric Cheng * module insertion may complete before the capability disable
15128275SEric Cheng * completes. So we limit the check to DLD_ENABLE case.
15131184Skrgopi */
15148275SEric Cheng if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) &&
151511021SEric.Cheng@Sun.COM (dsp->ds_sap != ETHERTYPE_IP ||
151611021SEric.Cheng@Sun.COM !check_mod_above(dsp->ds_rq, "ip"))) {
15178275SEric Cheng return (ENOTSUP);
15188275SEric Cheng }
15191184Skrgopi
15208275SEric Cheng switch (type) {
15218275SEric Cheng case DLD_CAPAB_DIRECT:
15228275SEric Cheng err = dld_capab_direct(dsp, data, flags);
15238275SEric Cheng break;
15241184Skrgopi
15258275SEric Cheng case DLD_CAPAB_POLL:
15268275SEric Cheng err = dld_capab_poll(dsp, data, flags);
15278275SEric Cheng break;
15281184Skrgopi
15298275SEric Cheng case DLD_CAPAB_PERIM:
15308275SEric Cheng err = dld_capab_perim(dsp, data, flags);
15318275SEric Cheng break;
15321184Skrgopi
15338275SEric Cheng case DLD_CAPAB_LSO:
15348275SEric Cheng err = dld_capab_lso(dsp, data, flags);
15358275SEric Cheng break;
15368275SEric Cheng
15378275SEric Cheng default:
15388275SEric Cheng err = ENOTSUP;
15398275SEric Cheng break;
15401184Skrgopi }
15418275SEric Cheng
15428275SEric Cheng return (err);
15431184Skrgopi }
15441184Skrgopi
15450Sstevel@tonic-gate /*
15460Sstevel@tonic-gate * DL_CAPABILITY_ACK/DL_ERROR_ACK
15470Sstevel@tonic-gate */
15488275SEric Cheng static void
proto_capability_advertise(dld_str_t * dsp,mblk_t * mp)1549269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
15500Sstevel@tonic-gate {
15510Sstevel@tonic-gate dl_capability_ack_t *dlap;
15520Sstevel@tonic-gate dl_capability_sub_t *dlsp;
15530Sstevel@tonic-gate size_t subsize;
15548275SEric Cheng dl_capab_dld_t dld;
15550Sstevel@tonic-gate dl_capab_hcksum_t hcksum;
15560Sstevel@tonic-gate dl_capab_zerocopy_t zcopy;
155711076SCathy.Zhou@Sun.COM dl_capab_vrrp_t vrrp;
155811076SCathy.Zhou@Sun.COM mac_capab_vrrp_t vrrp_capab;
15590Sstevel@tonic-gate uint8_t *ptr;
1560269Sericheng queue_t *q = dsp->ds_wq;
1561269Sericheng mblk_t *mp1;
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;
15710Sstevel@tonic-gate
15720Sstevel@tonic-gate /*
157312325SCathy.Zhou@Sun.COM * Check if checksum offload is supported on this MAC.
15740Sstevel@tonic-gate */
15755895Syz147064 bzero(&hcksum, sizeof (dl_capab_hcksum_t));
157612325SCathy.Zhou@Sun.COM if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM,
15772311Sseb &hcksum.hcksum_txflags)) {
15785895Syz147064 if (hcksum.hcksum_txflags != 0) {
15795895Syz147064 hcksum_capable = B_TRUE;
15805895Syz147064 subsize += sizeof (dl_capability_sub_t) +
15815895Syz147064 sizeof (dl_capab_hcksum_t);
15825895Syz147064 }
15830Sstevel@tonic-gate }
15840Sstevel@tonic-gate
15850Sstevel@tonic-gate /*
15865895Syz147064 * Check if zerocopy is supported on this interface.
15875895Syz147064 * If advertising DL_CAPAB_ZEROCOPY has not been explicitly disabled
15885895Syz147064 * then reserve space for that capability.
15890Sstevel@tonic-gate */
15905895Syz147064 if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_ZCOPY, NULL) &&
15915895Syz147064 !(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
15925895Syz147064 zcopy_capable = B_TRUE;
15930Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) +
15940Sstevel@tonic-gate sizeof (dl_capab_zerocopy_t);
15950Sstevel@tonic-gate }
15960Sstevel@tonic-gate
15970Sstevel@tonic-gate /*
15988275SEric Cheng * Direct capability negotiation interface between IP and DLD
15998275SEric Cheng */
160011021SEric.Cheng@Sun.COM if (dsp->ds_sap == ETHERTYPE_IP && check_mod_above(dsp->ds_rq, "ip")) {
16018275SEric Cheng dld_capable = B_TRUE;
16028275SEric Cheng subsize += sizeof (dl_capability_sub_t) +
16038275SEric Cheng sizeof (dl_capab_dld_t);
16048275SEric Cheng }
16058275SEric Cheng
16068275SEric Cheng /*
160711076SCathy.Zhou@Sun.COM * Check if vrrp is supported on this interface. If so, reserve
160811076SCathy.Zhou@Sun.COM * space for that capability.
160911076SCathy.Zhou@Sun.COM */
161011076SCathy.Zhou@Sun.COM if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_VRRP, &vrrp_capab)) {
161111076SCathy.Zhou@Sun.COM vrrp_capable = B_TRUE;
161211076SCathy.Zhou@Sun.COM subsize += sizeof (dl_capability_sub_t) +
161311076SCathy.Zhou@Sun.COM sizeof (dl_capab_vrrp_t);
161411076SCathy.Zhou@Sun.COM }
161511076SCathy.Zhou@Sun.COM
161611076SCathy.Zhou@Sun.COM /*
1617269Sericheng * If there are no capabilities to advertise or if we
1618269Sericheng * can't allocate a response, send a DL_ERROR_ACK.
16190Sstevel@tonic-gate */
16201184Skrgopi if ((mp1 = reallocb(mp,
1621269Sericheng sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1622269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
16238275SEric Cheng return;
16240Sstevel@tonic-gate }
16250Sstevel@tonic-gate
1626269Sericheng mp = mp1;
1627269Sericheng DB_TYPE(mp) = M_PROTO;
1628269Sericheng mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1629269Sericheng bzero(mp->b_rptr, MBLKL(mp));
16300Sstevel@tonic-gate dlap = (dl_capability_ack_t *)mp->b_rptr;
16310Sstevel@tonic-gate dlap->dl_primitive = DL_CAPABILITY_ACK;
16320Sstevel@tonic-gate dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
16330Sstevel@tonic-gate dlap->dl_sub_length = subsize;
16340Sstevel@tonic-gate ptr = (uint8_t *)&dlap[1];
16350Sstevel@tonic-gate
16360Sstevel@tonic-gate /*
16370Sstevel@tonic-gate * TCP/IP checksum offload.
16380Sstevel@tonic-gate */
16395895Syz147064 if (hcksum_capable) {
16400Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr;
16410Sstevel@tonic-gate
16420Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_HCKSUM;
16430Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_hcksum_t);
16440Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t);
16450Sstevel@tonic-gate
16460Sstevel@tonic-gate hcksum.hcksum_version = HCKSUM_VERSION_1;
16470Sstevel@tonic-gate dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
16480Sstevel@tonic-gate bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
16490Sstevel@tonic-gate ptr += sizeof (dl_capab_hcksum_t);
16500Sstevel@tonic-gate }
16510Sstevel@tonic-gate
16520Sstevel@tonic-gate /*
16530Sstevel@tonic-gate * Zero copy
16540Sstevel@tonic-gate */
16555895Syz147064 if (zcopy_capable) {
16560Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr;
16570Sstevel@tonic-gate
16580Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
16590Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
16600Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t);
16610Sstevel@tonic-gate
16620Sstevel@tonic-gate bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
16630Sstevel@tonic-gate zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
16640Sstevel@tonic-gate zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
16650Sstevel@tonic-gate
16660Sstevel@tonic-gate dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
16670Sstevel@tonic-gate bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
16680Sstevel@tonic-gate ptr += sizeof (dl_capab_zerocopy_t);
16690Sstevel@tonic-gate }
16700Sstevel@tonic-gate
16718275SEric Cheng /*
167211076SCathy.Zhou@Sun.COM * VRRP capability negotiation
167311076SCathy.Zhou@Sun.COM */
167411076SCathy.Zhou@Sun.COM if (vrrp_capable) {
167511076SCathy.Zhou@Sun.COM dlsp = (dl_capability_sub_t *)ptr;
167611076SCathy.Zhou@Sun.COM dlsp->dl_cap = DL_CAPAB_VRRP;
167711076SCathy.Zhou@Sun.COM dlsp->dl_length = sizeof (dl_capab_vrrp_t);
167811076SCathy.Zhou@Sun.COM ptr += sizeof (dl_capability_sub_t);
167911076SCathy.Zhou@Sun.COM
168011076SCathy.Zhou@Sun.COM bzero(&vrrp, sizeof (dl_capab_vrrp_t));
168111076SCathy.Zhou@Sun.COM vrrp.vrrp_af = vrrp_capab.mcv_af;
168211076SCathy.Zhou@Sun.COM bcopy(&vrrp, ptr, sizeof (dl_capab_vrrp_t));
168311076SCathy.Zhou@Sun.COM ptr += sizeof (dl_capab_vrrp_t);
168411076SCathy.Zhou@Sun.COM }
168511076SCathy.Zhou@Sun.COM
168611076SCathy.Zhou@Sun.COM /*
16878275SEric Cheng * Direct capability negotiation interface between IP and DLD.
16888275SEric Cheng * Refer to dld.h for details.
16898275SEric Cheng */
16908275SEric Cheng if (dld_capable) {
16918275SEric Cheng dlsp = (dl_capability_sub_t *)ptr;
16928275SEric Cheng dlsp->dl_cap = DL_CAPAB_DLD;
16938275SEric Cheng dlsp->dl_length = sizeof (dl_capab_dld_t);
16948275SEric Cheng ptr += sizeof (dl_capability_sub_t);
1695269Sericheng
16968275SEric Cheng bzero(&dld, sizeof (dl_capab_dld_t));
16978275SEric Cheng dld.dld_version = DLD_CURRENT_VERSION;
16988275SEric Cheng dld.dld_capab = (uintptr_t)dld_capab;
16998275SEric Cheng dld.dld_capab_handle = (uintptr_t)dsp;
17008275SEric Cheng
17018275SEric Cheng dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq);
17028275SEric Cheng bcopy(&dld, ptr, sizeof (dl_capab_dld_t));
17038275SEric Cheng ptr += sizeof (dl_capab_dld_t);
17048275SEric Cheng }
17058275SEric Cheng
17068275SEric Cheng ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1707269Sericheng qreply(q, mp);
17080Sstevel@tonic-gate }
17095113Syz147064
17105113Syz147064 /*
17115113Syz147064 * Disable any enabled capabilities.
17125113Syz147064 */
17135113Syz147064 void
dld_capabilities_disable(dld_str_t * dsp)17145113Syz147064 dld_capabilities_disable(dld_str_t *dsp)
17155113Syz147064 {
17165113Syz147064 if (dsp->ds_polling)
17178275SEric Cheng (void) dld_capab_poll_disable(dsp, NULL);
17185113Syz147064 }
1719