xref: /illumos-gate/usr/src/uts/common/io/dld/dld_proto.c (revision 115f9ea8610878d992d097ec5df5c7c244c0bc49)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
56b6515e2Sericheng  * Common Development and Distribution License (the "License").
66b6515e2Sericheng  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
229056fcebSCathy Zhou  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23cabb4db9SDan McDonald  * Copyright 2012, Nexenta Systems, Inc. All rights reserved.
2415c07adcSJohn Levon  * Copyright (c) 2018, Joyent, Inc.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * Data-Link Driver
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
317c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
32da14cebeSEric Cheng #include <sys/strsun.h>
337c478bd9Sstevel@tonic-gate #include <sys/vlan.h>
347c478bd9Sstevel@tonic-gate #include <sys/dld_impl.h>
35da14cebeSEric Cheng #include <sys/mac_client.h>
36da14cebeSEric Cheng #include <sys/mac_client_impl.h>
37da14cebeSEric Cheng #include <sys/mac_client_priv.h>
387c478bd9Sstevel@tonic-gate 
39da14cebeSEric Cheng typedef void proto_reqfunc_t(dld_str_t *, mblk_t *);
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req,
427c478bd9Sstevel@tonic-gate     proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req,
437c478bd9Sstevel@tonic-gate     proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req,
447c478bd9Sstevel@tonic-gate     proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req,
45d62bc4baSyz147064     proto_notify_req, proto_passive_req;
467c478bd9Sstevel@tonic-gate 
47da14cebeSEric Cheng static void proto_capability_advertise(dld_str_t *, mblk_t *);
48da14cebeSEric Cheng static int dld_capab_poll_disable(dld_str_t *, dld_capab_poll_t *);
498d4cf8d8S static boolean_t check_mod_above(queue_t *, const char *);
504b46d1efSkrgopi 
517c478bd9Sstevel@tonic-gate #define	DL_ACK_PENDING(state) \
527c478bd9Sstevel@tonic-gate 	((state) == DL_ATTACH_PENDING || \
537c478bd9Sstevel@tonic-gate 	(state) == DL_DETACH_PENDING || \
547c478bd9Sstevel@tonic-gate 	(state) == DL_BIND_PENDING || \
557c478bd9Sstevel@tonic-gate 	(state) == DL_UNBIND_PENDING)
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate /*
58210db224Sericheng  * Process a DLPI protocol message.
59210db224Sericheng  * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ,
60210db224Sericheng  * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an
61210db224Sericheng  * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t
62210db224Sericheng  * as 'passive' and forbids it from being subsequently made 'active'
63210db224Sericheng  * by the above primitives.
647c478bd9Sstevel@tonic-gate  */
657c478bd9Sstevel@tonic-gate void
dld_proto(dld_str_t * dsp,mblk_t * mp)66da14cebeSEric Cheng dld_proto(dld_str_t *dsp, mblk_t *mp)
677c478bd9Sstevel@tonic-gate {
687c478bd9Sstevel@tonic-gate 	t_uscalar_t		prim;
697c478bd9Sstevel@tonic-gate 
70da14cebeSEric Cheng 	if (MBLKL(mp) < sizeof (t_uscalar_t)) {
71da14cebeSEric Cheng 		freemsg(mp);
72da14cebeSEric Cheng 		return;
73da14cebeSEric Cheng 	}
74da14cebeSEric Cheng 	prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
757c478bd9Sstevel@tonic-gate 
76210db224Sericheng 	switch (prim) {
77210db224Sericheng 	case DL_INFO_REQ:
78da14cebeSEric Cheng 		proto_info_req(dsp, mp);
79210db224Sericheng 		break;
80210db224Sericheng 	case DL_BIND_REQ:
81da14cebeSEric Cheng 		proto_bind_req(dsp, mp);
82210db224Sericheng 		break;
83210db224Sericheng 	case DL_UNBIND_REQ:
84da14cebeSEric Cheng 		proto_unbind_req(dsp, mp);
85da14cebeSEric Cheng 		break;
86da14cebeSEric Cheng 	case DL_UNITDATA_REQ:
87da14cebeSEric Cheng 		proto_unitdata_req(dsp, mp);
88210db224Sericheng 		break;
89210db224Sericheng 	case DL_UDQOS_REQ:
90da14cebeSEric Cheng 		proto_udqos_req(dsp, mp);
91210db224Sericheng 		break;
92210db224Sericheng 	case DL_ATTACH_REQ:
93da14cebeSEric Cheng 		proto_attach_req(dsp, mp);
94210db224Sericheng 		break;
95210db224Sericheng 	case DL_DETACH_REQ:
96da14cebeSEric Cheng 		proto_detach_req(dsp, mp);
97210db224Sericheng 		break;
98210db224Sericheng 	case DL_ENABMULTI_REQ:
99da14cebeSEric Cheng 		proto_enabmulti_req(dsp, mp);
100210db224Sericheng 		break;
101210db224Sericheng 	case DL_DISABMULTI_REQ:
102da14cebeSEric Cheng 		proto_disabmulti_req(dsp, mp);
103210db224Sericheng 		break;
104210db224Sericheng 	case DL_PROMISCON_REQ:
105da14cebeSEric Cheng 		proto_promiscon_req(dsp, mp);
106210db224Sericheng 		break;
107210db224Sericheng 	case DL_PROMISCOFF_REQ:
108da14cebeSEric Cheng 		proto_promiscoff_req(dsp, mp);
109210db224Sericheng 		break;
110210db224Sericheng 	case DL_PHYS_ADDR_REQ:
111da14cebeSEric Cheng 		proto_physaddr_req(dsp, mp);
112210db224Sericheng 		break;
113210db224Sericheng 	case DL_SET_PHYS_ADDR_REQ:
114da14cebeSEric Cheng 		proto_setphysaddr_req(dsp, mp);
115210db224Sericheng 		break;
116210db224Sericheng 	case DL_NOTIFY_REQ:
117da14cebeSEric Cheng 		proto_notify_req(dsp, mp);
118210db224Sericheng 		break;
119210db224Sericheng 	case DL_CAPABILITY_REQ:
120da14cebeSEric Cheng 		proto_capability_req(dsp, mp);
121210db224Sericheng 		break;
122210db224Sericheng 	case DL_PASSIVE_REQ:
123da14cebeSEric Cheng 		proto_passive_req(dsp, mp);
124210db224Sericheng 		break;
125210db224Sericheng 	default:
126da14cebeSEric Cheng 		proto_req(dsp, mp);
127210db224Sericheng 		break;
128210db224Sericheng 	}
1297c478bd9Sstevel@tonic-gate }
1307c478bd9Sstevel@tonic-gate 
131210db224Sericheng #define	NEG(x)	-(x)
1327c478bd9Sstevel@tonic-gate typedef struct dl_info_ack_wrapper {
1337c478bd9Sstevel@tonic-gate 	dl_info_ack_t		dl_info;
134ba2e4443Sseb 	uint8_t			dl_addr[MAXMACADDRLEN + sizeof (uint16_t)];
135ba2e4443Sseb 	uint8_t			dl_brdcst_addr[MAXMACADDRLEN];
1367c478bd9Sstevel@tonic-gate 	dl_qos_cl_range1_t	dl_qos_range1;
1377c478bd9Sstevel@tonic-gate 	dl_qos_cl_sel1_t	dl_qos_sel1;
1387c478bd9Sstevel@tonic-gate } dl_info_ack_wrapper_t;
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate /*
141210db224Sericheng  * DL_INFO_REQ
1427c478bd9Sstevel@tonic-gate  */
143da14cebeSEric Cheng static void
proto_info_req(dld_str_t * dsp,mblk_t * mp)144da14cebeSEric Cheng proto_info_req(dld_str_t *dsp, mblk_t *mp)
1457c478bd9Sstevel@tonic-gate {
1467c478bd9Sstevel@tonic-gate 	dl_info_ack_wrapper_t	*dlwp;
1477c478bd9Sstevel@tonic-gate 	dl_info_ack_t		*dlp;
1487c478bd9Sstevel@tonic-gate 	dl_qos_cl_sel1_t	*selp;
1497c478bd9Sstevel@tonic-gate 	dl_qos_cl_range1_t	*rangep;
1507c478bd9Sstevel@tonic-gate 	uint8_t			*addr;
1517c478bd9Sstevel@tonic-gate 	uint8_t			*brdcst_addr;
1527c478bd9Sstevel@tonic-gate 	uint_t			addr_length;
1537c478bd9Sstevel@tonic-gate 	uint_t			sap_length;
154210db224Sericheng 	mac_info_t		minfo;
155210db224Sericheng 	mac_info_t		*minfop;
156210db224Sericheng 	queue_t			*q = dsp->ds_wq;
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 	/*
1597c478bd9Sstevel@tonic-gate 	 * Swap the request message for one large enough to contain the
1607c478bd9Sstevel@tonic-gate 	 * wrapper structure defined above.
1617c478bd9Sstevel@tonic-gate 	 */
162210db224Sericheng 	if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t),
1637c478bd9Sstevel@tonic-gate 	    M_PCPROTO, 0)) == NULL)
164da14cebeSEric Cheng 		return;
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t));
1677c478bd9Sstevel@tonic-gate 	dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr;
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate 	dlp = &(dlwp->dl_info);
1707c478bd9Sstevel@tonic-gate 	ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr);
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 	dlp->dl_primitive = DL_INFO_ACK;
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	/*
1757c478bd9Sstevel@tonic-gate 	 * Set up the sub-structure pointers.
1767c478bd9Sstevel@tonic-gate 	 */
1777c478bd9Sstevel@tonic-gate 	addr = dlwp->dl_addr;
1787c478bd9Sstevel@tonic-gate 	brdcst_addr = dlwp->dl_brdcst_addr;
1797c478bd9Sstevel@tonic-gate 	rangep = &(dlwp->dl_qos_range1);
1807c478bd9Sstevel@tonic-gate 	selp = &(dlwp->dl_qos_sel1);
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate 	/*
1837c478bd9Sstevel@tonic-gate 	 * This driver supports only version 2 connectionless DLPI provider
1847c478bd9Sstevel@tonic-gate 	 * nodes.
1857c478bd9Sstevel@tonic-gate 	 */
1867c478bd9Sstevel@tonic-gate 	dlp->dl_service_mode = DL_CLDLS;
1877c478bd9Sstevel@tonic-gate 	dlp->dl_version = DL_VERSION_2;
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	/*
190210db224Sericheng 	 * Set the style of the provider
1917c478bd9Sstevel@tonic-gate 	 */
192210db224Sericheng 	dlp->dl_provider_style = dsp->ds_style;
1937c478bd9Sstevel@tonic-gate 	ASSERT(dlp->dl_provider_style == DL_STYLE1 ||
1947c478bd9Sstevel@tonic-gate 	    dlp->dl_provider_style == DL_STYLE2);
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	/*
1977c478bd9Sstevel@tonic-gate 	 * Set the current DLPI state.
1987c478bd9Sstevel@tonic-gate 	 */
1997c478bd9Sstevel@tonic-gate 	dlp->dl_current_state = dsp->ds_dlstate;
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	/*
202210db224Sericheng 	 * Gratuitously set the media type. This is to deal with modules
203210db224Sericheng 	 * that assume the media type is known prior to DL_ATTACH_REQ
2047c478bd9Sstevel@tonic-gate 	 * being completed.
2057c478bd9Sstevel@tonic-gate 	 */
2067c478bd9Sstevel@tonic-gate 	dlp->dl_mac_type = DL_ETHER;
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	/*
209210db224Sericheng 	 * If the stream is not at least attached we try to retrieve the
210210db224Sericheng 	 * mac_info using mac_info_get()
2117c478bd9Sstevel@tonic-gate 	 */
2127c478bd9Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
2137c478bd9Sstevel@tonic-gate 	    dsp->ds_dlstate == DL_ATTACH_PENDING ||
214210db224Sericheng 	    dsp->ds_dlstate == DL_DETACH_PENDING) {
215210db224Sericheng 		if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) {
216210db224Sericheng 			/*
217210db224Sericheng 			 * Cannot find mac_info. giving up.
218210db224Sericheng 			 */
2197c478bd9Sstevel@tonic-gate 			goto done;
220210db224Sericheng 		}
221210db224Sericheng 		minfop = &minfo;
222210db224Sericheng 	} else {
223210db224Sericheng 		minfop = (mac_info_t *)dsp->ds_mip;
224e7801d59Ssowmini 		/* We can only get the sdu if we're attached. */
225e7801d59Ssowmini 		mac_sdu_get(dsp->ds_mh, &dlp->dl_min_sdu, &dlp->dl_max_sdu);
226210db224Sericheng 	}
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 	/*
2297c478bd9Sstevel@tonic-gate 	 * Set the media type (properly this time).
2307c478bd9Sstevel@tonic-gate 	 */
2310ba2cbe9Sxc151355 	if (dsp->ds_native)
2320ba2cbe9Sxc151355 		dlp->dl_mac_type = minfop->mi_nativemedia;
2330ba2cbe9Sxc151355 	else
234210db224Sericheng 		dlp->dl_mac_type = minfop->mi_media;
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate 	/*
2377c478bd9Sstevel@tonic-gate 	 * Set the DLSAP length. We only support 16 bit values and they
2387c478bd9Sstevel@tonic-gate 	 * appear after the MAC address portion of DLSAP addresses.
2397c478bd9Sstevel@tonic-gate 	 */
2407c478bd9Sstevel@tonic-gate 	sap_length = sizeof (uint16_t);
2417c478bd9Sstevel@tonic-gate 	dlp->dl_sap_length = NEG(sap_length);
2427c478bd9Sstevel@tonic-gate 
243210db224Sericheng 	addr_length = minfop->mi_addr_length;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	/*
2467c478bd9Sstevel@tonic-gate 	 * Copy in the media broadcast address.
2477c478bd9Sstevel@tonic-gate 	 */
248ba2e4443Sseb 	if (minfop->mi_brdcst_addr != NULL) {
249ba2e4443Sseb 		dlp->dl_brdcst_addr_offset =
250ba2e4443Sseb 		    (uintptr_t)brdcst_addr - (uintptr_t)dlp;
251210db224Sericheng 		bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length);
2527c478bd9Sstevel@tonic-gate 		dlp->dl_brdcst_addr_length = addr_length;
253ba2e4443Sseb 	}
2547c478bd9Sstevel@tonic-gate 
255e75f0919SSebastien Roy 	/* Only VLAN links and links that have a normal tag mode support QOS. */
256a2da5912SSebastien Roy 	if ((dsp->ds_mch != NULL &&
257a2da5912SSebastien Roy 	    mac_client_vid(dsp->ds_mch) != VLAN_ID_NONE) ||
258a2da5912SSebastien Roy 	    (dsp->ds_dlp != NULL &&
259a2da5912SSebastien Roy 	    dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_NORMAL)) {
2607c478bd9Sstevel@tonic-gate 		dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp;
2617c478bd9Sstevel@tonic-gate 		dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t);
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate 		rangep->dl_qos_type = DL_QOS_CL_RANGE1;
2647c478bd9Sstevel@tonic-gate 		rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN;
2657c478bd9Sstevel@tonic-gate 		rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
2667c478bd9Sstevel@tonic-gate 		rangep->dl_protection.dl_min = DL_UNKNOWN;
2677c478bd9Sstevel@tonic-gate 		rangep->dl_protection.dl_max = DL_UNKNOWN;
2687c478bd9Sstevel@tonic-gate 		rangep->dl_residual_error = DL_UNKNOWN;
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate 		/*
2717c478bd9Sstevel@tonic-gate 		 * Specify the supported range of priorities.
2727c478bd9Sstevel@tonic-gate 		 */
2737c478bd9Sstevel@tonic-gate 		rangep->dl_priority.dl_min = 0;
2747c478bd9Sstevel@tonic-gate 		rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1;
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 		dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp;
2777c478bd9Sstevel@tonic-gate 		dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t);
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate 		selp->dl_qos_type = DL_QOS_CL_SEL1;
2807c478bd9Sstevel@tonic-gate 		selp->dl_trans_delay = DL_UNKNOWN;
2817c478bd9Sstevel@tonic-gate 		selp->dl_protection = DL_UNKNOWN;
2827c478bd9Sstevel@tonic-gate 		selp->dl_residual_error = DL_UNKNOWN;
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 		/*
2857c478bd9Sstevel@tonic-gate 		 * Specify the current priority (which can be changed by
2867c478bd9Sstevel@tonic-gate 		 * the DL_UDQOS_REQ primitive).
2877c478bd9Sstevel@tonic-gate 		 */
2887c478bd9Sstevel@tonic-gate 		selp->dl_priority = dsp->ds_pri;
289e75f0919SSebastien Roy 	}
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 	dlp->dl_addr_length = addr_length + sizeof (uint16_t);
2927c478bd9Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_IDLE) {
2937c478bd9Sstevel@tonic-gate 		/*
2947c478bd9Sstevel@tonic-gate 		 * The stream is bound. Therefore we can formulate a valid
2957c478bd9Sstevel@tonic-gate 		 * DLSAP address.
2967c478bd9Sstevel@tonic-gate 		 */
2977c478bd9Sstevel@tonic-gate 		dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp;
298ba2e4443Sseb 		if (addr_length > 0)
299da14cebeSEric Cheng 			mac_unicast_primary_get(dsp->ds_mh, addr);
300da14cebeSEric Cheng 
3017c478bd9Sstevel@tonic-gate 		*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
3027c478bd9Sstevel@tonic-gate 	}
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate done:
30556f33205SJonathan Adams 	IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0);
30656f33205SJonathan Adams 	IMPLY(dlp->dl_qos_range_offset != 0,
30756f33205SJonathan Adams 	    dlp->dl_qos_range_length != 0);
30856f33205SJonathan Adams 	IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0);
30956f33205SJonathan Adams 	IMPLY(dlp->dl_brdcst_addr_offset != 0,
31056f33205SJonathan Adams 	    dlp->dl_brdcst_addr_length != 0);
3117c478bd9Sstevel@tonic-gate 
312210db224Sericheng 	qreply(q, mp);
3137c478bd9Sstevel@tonic-gate }
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate /*
316210db224Sericheng  * DL_ATTACH_REQ
3177c478bd9Sstevel@tonic-gate  */
318da14cebeSEric Cheng static void
proto_attach_req(dld_str_t * dsp,mblk_t * mp)319da14cebeSEric Cheng proto_attach_req(dld_str_t *dsp, mblk_t *mp)
3207c478bd9Sstevel@tonic-gate {
321da14cebeSEric Cheng 	dl_attach_req_t	*dlp = (dl_attach_req_t *)mp->b_rptr;
322210db224Sericheng 	int		err = 0;
323210db224Sericheng 	t_uscalar_t	dl_err;
324210db224Sericheng 	queue_t		*q = dsp->ds_wq;
3257c478bd9Sstevel@tonic-gate 
326210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_attach_req_t) ||
327210db224Sericheng 	    dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) {
328210db224Sericheng 		dl_err = DL_BADPRIM;
3297c478bd9Sstevel@tonic-gate 		goto failed;
330210db224Sericheng 	}
3317c478bd9Sstevel@tonic-gate 
332210db224Sericheng 	if (dsp->ds_dlstate != DL_UNATTACHED) {
333210db224Sericheng 		dl_err = DL_OUTSTATE;
334210db224Sericheng 		goto failed;
335210db224Sericheng 	}
3367c478bd9Sstevel@tonic-gate 
337210db224Sericheng 	dsp->ds_dlstate = DL_ATTACH_PENDING;
338210db224Sericheng 
339210db224Sericheng 	err = dld_str_attach(dsp, dlp->dl_ppa);
340210db224Sericheng 	if (err != 0) {
3417c478bd9Sstevel@tonic-gate 		switch (err) {
3427c478bd9Sstevel@tonic-gate 		case ENOENT:
3437c478bd9Sstevel@tonic-gate 			dl_err = DL_BADPPA;
3447c478bd9Sstevel@tonic-gate 			err = 0;
3457c478bd9Sstevel@tonic-gate 			break;
3467c478bd9Sstevel@tonic-gate 		default:
3477c478bd9Sstevel@tonic-gate 			dl_err = DL_SYSERR;
3487c478bd9Sstevel@tonic-gate 			break;
3497c478bd9Sstevel@tonic-gate 		}
3507c478bd9Sstevel@tonic-gate 		dsp->ds_dlstate = DL_UNATTACHED;
351210db224Sericheng 		goto failed;
352210db224Sericheng 	}
353210db224Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBOUND);
354210db224Sericheng 	dlokack(q, mp, DL_ATTACH_REQ);
355da14cebeSEric Cheng 	return;
356da14cebeSEric Cheng 
357210db224Sericheng failed:
358210db224Sericheng 	dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err);
3597c478bd9Sstevel@tonic-gate }
3607c478bd9Sstevel@tonic-gate 
361da14cebeSEric Cheng /*
362da14cebeSEric Cheng  * DL_DETACH_REQ
363da14cebeSEric Cheng  */
364da14cebeSEric Cheng static void
proto_detach_req(dld_str_t * dsp,mblk_t * mp)365da14cebeSEric Cheng proto_detach_req(dld_str_t *dsp, mblk_t *mp)
3667c478bd9Sstevel@tonic-gate {
367210db224Sericheng 	queue_t		*q = dsp->ds_wq;
368210db224Sericheng 	t_uscalar_t	dl_err;
369210db224Sericheng 
370210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_detach_req_t)) {
371210db224Sericheng 		dl_err = DL_BADPRIM;
372210db224Sericheng 		goto failed;
373210db224Sericheng 	}
374210db224Sericheng 
375210db224Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
376210db224Sericheng 		dl_err = DL_OUTSTATE;
377210db224Sericheng 		goto failed;
378210db224Sericheng 	}
379210db224Sericheng 
380210db224Sericheng 	if (dsp->ds_style == DL_STYLE1) {
381210db224Sericheng 		dl_err = DL_BADPRIM;
382210db224Sericheng 		goto failed;
383210db224Sericheng 	}
384210db224Sericheng 
385da14cebeSEric Cheng 	ASSERT(dsp->ds_datathr_cnt == 0);
386210db224Sericheng 	dsp->ds_dlstate = DL_DETACH_PENDING;
387210db224Sericheng 
388da14cebeSEric Cheng 	dld_str_detach(dsp);
389d62bc4baSyz147064 	dlokack(dsp->ds_wq, mp, DL_DETACH_REQ);
390da14cebeSEric Cheng 	return;
391da14cebeSEric Cheng 
392210db224Sericheng failed:
393210db224Sericheng 	dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0);
3947c478bd9Sstevel@tonic-gate }
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate /*
397210db224Sericheng  * DL_BIND_REQ
3987c478bd9Sstevel@tonic-gate  */
399da14cebeSEric Cheng static void
proto_bind_req(dld_str_t * dsp,mblk_t * mp)400da14cebeSEric Cheng proto_bind_req(dld_str_t *dsp, mblk_t *mp)
4017c478bd9Sstevel@tonic-gate {
402da14cebeSEric Cheng 	dl_bind_req_t	*dlp = (dl_bind_req_t *)mp->b_rptr;
403210db224Sericheng 	int		err = 0;
4046f45d2aeSyz147064 	uint8_t		dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)];
4056f45d2aeSyz147064 	uint_t		dlsap_addr_length;
406210db224Sericheng 	t_uscalar_t	dl_err;
407210db224Sericheng 	t_scalar_t	sap;
408210db224Sericheng 	queue_t		*q = dsp->ds_wq;
409da14cebeSEric Cheng 	mac_perim_handle_t	mph;
410da14cebeSEric Cheng 	void		*mdip;
411da14cebeSEric Cheng 	int32_t		intr_cpu;
4127c478bd9Sstevel@tonic-gate 
413210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
414210db224Sericheng 		dl_err = DL_BADPRIM;
4157c478bd9Sstevel@tonic-gate 		goto failed;
416210db224Sericheng 	}
417210db224Sericheng 
418210db224Sericheng 	if (dlp->dl_xidtest_flg != 0) {
419210db224Sericheng 		dl_err = DL_NOAUTO;
420210db224Sericheng 		goto failed;
421210db224Sericheng 	}
422210db224Sericheng 
423210db224Sericheng 	if (dlp->dl_service_mode != DL_CLDLS) {
424210db224Sericheng 		dl_err = DL_UNSUPPORTED;
425210db224Sericheng 		goto failed;
426210db224Sericheng 	}
427210db224Sericheng 
428210db224Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
429210db224Sericheng 		dl_err = DL_OUTSTATE;
430210db224Sericheng 		goto failed;
431210db224Sericheng 	}
432210db224Sericheng 
433da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
434da14cebeSEric Cheng 
4355d460eafSCathy Zhou 	if ((err = dls_active_set(dsp)) != 0) {
436210db224Sericheng 		dl_err = DL_SYSERR;
437da14cebeSEric Cheng 		goto failed2;
438210db224Sericheng 	}
439210db224Sericheng 
440da14cebeSEric Cheng 	dsp->ds_dlstate = DL_BIND_PENDING;
441210db224Sericheng 	/*
442210db224Sericheng 	 * Set the receive callback.
443210db224Sericheng 	 */
444da14cebeSEric Cheng 	dls_rx_set(dsp, (dsp->ds_mode == DLD_RAW) ?
445210db224Sericheng 	    dld_str_rx_raw : dld_str_rx_unitdata, dsp);
446210db224Sericheng 
447210db224Sericheng 	/*
448210db224Sericheng 	 * Bind the channel such that it can receive packets.
449210db224Sericheng 	 */
450d62bc4baSyz147064 	sap = dlp->dl_sap;
4518d4cf8d8S 	dsp->ds_nonip = !check_mod_above(dsp->ds_rq, "ip") &&
4528d4cf8d8S 	    !check_mod_above(dsp->ds_rq, "arp");
4538d4cf8d8S 
454da14cebeSEric Cheng 	err = dls_bind(dsp, sap);
455210db224Sericheng 	if (err != 0) {
456210db224Sericheng 		switch (err) {
457210db224Sericheng 		case EINVAL:
458210db224Sericheng 			dl_err = DL_BADADDR;
459210db224Sericheng 			err = 0;
460210db224Sericheng 			break;
461210db224Sericheng 		default:
462210db224Sericheng 			dl_err = DL_SYSERR;
463210db224Sericheng 			break;
464210db224Sericheng 		}
465d62bc4baSyz147064 
466da14cebeSEric Cheng 		dsp->ds_dlstate = DL_UNBOUND;
4675d460eafSCathy Zhou 		dls_active_clear(dsp, B_FALSE);
468da14cebeSEric Cheng 		goto failed2;
469210db224Sericheng 	}
4707c478bd9Sstevel@tonic-gate 
471da14cebeSEric Cheng 	intr_cpu = mac_client_intr_cpu(dsp->ds_mch);
472da14cebeSEric Cheng 	mdip = mac_get_devinfo(dsp->ds_mh);
473da14cebeSEric Cheng 	mac_perim_exit(mph);
474da14cebeSEric Cheng 
475da14cebeSEric Cheng 	/*
476da14cebeSEric Cheng 	 * We do this after we get out of the perim to avoid deadlocks
477da14cebeSEric Cheng 	 * etc. since part of mac_client_retarget_intr is to walk the
478da14cebeSEric Cheng 	 * device tree in order to find and retarget the interrupts.
479da14cebeSEric Cheng 	 */
4800dc2366fSVenugopal Iyer 	if (intr_cpu != -1)
481da14cebeSEric Cheng 		mac_client_set_intr_cpu(mdip, dsp->ds_mch, intr_cpu);
482da14cebeSEric Cheng 
4837c478bd9Sstevel@tonic-gate 	/*
4847c478bd9Sstevel@tonic-gate 	 * Copy in MAC address.
4857c478bd9Sstevel@tonic-gate 	 */
4866f45d2aeSyz147064 	dlsap_addr_length = dsp->ds_mip->mi_addr_length;
487da14cebeSEric Cheng 	mac_unicast_primary_get(dsp->ds_mh, dlsap_addr);
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 	/*
4906f45d2aeSyz147064 	 * Copy in the SAP.
4917c478bd9Sstevel@tonic-gate 	 */
492d62bc4baSyz147064 	*(uint16_t *)(dlsap_addr + dlsap_addr_length) = sap;
4936f45d2aeSyz147064 	dlsap_addr_length += sizeof (uint16_t);
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 	dsp->ds_dlstate = DL_IDLE;
4966f45d2aeSyz147064 	dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0);
497da14cebeSEric Cheng 	return;
498da14cebeSEric Cheng 
499da14cebeSEric Cheng failed2:
500da14cebeSEric Cheng 	mac_perim_exit(mph);
5017c478bd9Sstevel@tonic-gate failed:
502210db224Sericheng 	dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
503210db224Sericheng }
504210db224Sericheng 
505210db224Sericheng /*
506210db224Sericheng  * DL_UNBIND_REQ
507210db224Sericheng  */
508da14cebeSEric Cheng static void
proto_unbind_req(dld_str_t * dsp,mblk_t * mp)509da14cebeSEric Cheng proto_unbind_req(dld_str_t *dsp, mblk_t *mp)
510210db224Sericheng {
511d62bc4baSyz147064 	queue_t		*q = dsp->ds_wq;
512d62bc4baSyz147064 	t_uscalar_t	dl_err;
513da14cebeSEric Cheng 	mac_perim_handle_t	mph;
514210db224Sericheng 
515d62bc4baSyz147064 	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
516d62bc4baSyz147064 		dl_err = DL_BADPRIM;
517d62bc4baSyz147064 		goto failed;
518d62bc4baSyz147064 	}
519d62bc4baSyz147064 
520d62bc4baSyz147064 	if (dsp->ds_dlstate != DL_IDLE) {
521d62bc4baSyz147064 		dl_err = DL_OUTSTATE;
522d62bc4baSyz147064 		goto failed;
523d62bc4baSyz147064 	}
524210db224Sericheng 
525da14cebeSEric Cheng 	mutex_enter(&dsp->ds_lock);
526da14cebeSEric Cheng 	while (dsp->ds_datathr_cnt != 0)
527da14cebeSEric Cheng 		cv_wait(&dsp->ds_datathr_cv, &dsp->ds_lock);
528210db224Sericheng 
529da14cebeSEric Cheng 	dsp->ds_dlstate = DL_UNBIND_PENDING;
530da14cebeSEric Cheng 	mutex_exit(&dsp->ds_lock);
531da14cebeSEric Cheng 
532da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
533210db224Sericheng 	/*
534210db224Sericheng 	 * Unbind the channel to stop packets being received.
535210db224Sericheng 	 */
5365ecc58b1SGirish Moodalbail 	dls_unbind(dsp);
537d62bc4baSyz147064 
538d62bc4baSyz147064 	/*
539210db224Sericheng 	 * Disable polling mode, if it is enabled.
540210db224Sericheng 	 */
541da14cebeSEric Cheng 	(void) dld_capab_poll_disable(dsp, NULL);
542d62bc4baSyz147064 
543d62bc4baSyz147064 	/*
5448347601bSyl150051 	 * Clear LSO flags.
5458347601bSyl150051 	 */
5468347601bSyl150051 	dsp->ds_lso = B_FALSE;
5478347601bSyl150051 	dsp->ds_lso_max = 0;
5488347601bSyl150051 
5498347601bSyl150051 	/*
550da14cebeSEric Cheng 	 * Clear the receive callback.
551da14cebeSEric Cheng 	 */
552da14cebeSEric Cheng 	dls_rx_set(dsp, NULL, NULL);
553da14cebeSEric Cheng 	dsp->ds_direct = B_FALSE;
554da14cebeSEric Cheng 
555da14cebeSEric Cheng 	/*
556210db224Sericheng 	 * Set the mode back to the default (unitdata).
557210db224Sericheng 	 */
558210db224Sericheng 	dsp->ds_mode = DLD_UNITDATA;
5596a0b2badSericheng 	dsp->ds_dlstate = DL_UNBOUND;
560210db224Sericheng 
5615d460eafSCathy Zhou 	dls_active_clear(dsp, B_FALSE);
562da14cebeSEric Cheng 	mac_perim_exit(mph);
563da14cebeSEric Cheng 	dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ);
564da14cebeSEric Cheng 	return;
565210db224Sericheng failed:
566210db224Sericheng 	dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0);
567210db224Sericheng }
568210db224Sericheng 
569210db224Sericheng /*
570210db224Sericheng  * DL_PROMISCON_REQ
571210db224Sericheng  */
572da14cebeSEric Cheng static void
proto_promiscon_req(dld_str_t * dsp,mblk_t * mp)573da14cebeSEric Cheng proto_promiscon_req(dld_str_t *dsp, mblk_t *mp)
574210db224Sericheng {
575da14cebeSEric Cheng 	dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)mp->b_rptr;
576210db224Sericheng 	int		err = 0;
577210db224Sericheng 	t_uscalar_t	dl_err;
578cabb4db9SDan McDonald 	uint32_t	new_flags, promisc_saved;
579210db224Sericheng 	queue_t		*q = dsp->ds_wq;
580da14cebeSEric Cheng 	mac_perim_handle_t	mph;
581210db224Sericheng 
582210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) {
583210db224Sericheng 		dl_err = DL_BADPRIM;
584210db224Sericheng 		goto failed;
585210db224Sericheng 	}
586210db224Sericheng 
587210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
588210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
589210db224Sericheng 		dl_err = DL_OUTSTATE;
590210db224Sericheng 		goto failed;
591210db224Sericheng 	}
592210db224Sericheng 
593cabb4db9SDan McDonald 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
594cabb4db9SDan McDonald 
595cabb4db9SDan McDonald 	new_flags = promisc_saved = dsp->ds_promisc;
596210db224Sericheng 	switch (dlp->dl_level) {
597210db224Sericheng 	case DL_PROMISC_SAP:
598cabb4db9SDan McDonald 		new_flags |= DLS_PROMISC_SAP;
599210db224Sericheng 		break;
600da14cebeSEric Cheng 
601210db224Sericheng 	case DL_PROMISC_MULTI:
602cabb4db9SDan McDonald 		new_flags |= DLS_PROMISC_MULTI;
603210db224Sericheng 		break;
604da14cebeSEric Cheng 
605210db224Sericheng 	case DL_PROMISC_PHYS:
606cabb4db9SDan McDonald 		new_flags |= DLS_PROMISC_PHYS;
6077c478bd9Sstevel@tonic-gate 		break;
608da14cebeSEric Cheng 
609*115f9ea8SRobert Mustacchi 	case DL_PROMISC_RX_ONLY:
610*115f9ea8SRobert Mustacchi 		new_flags |= DLS_PROMISC_RX_ONLY;
611*115f9ea8SRobert Mustacchi 		break;
612*115f9ea8SRobert Mustacchi 
6137c478bd9Sstevel@tonic-gate 	default:
614210db224Sericheng 		dl_err = DL_NOTSUPPORTED;
615ad7ed3feSRobert Mustacchi 		goto failed2;
616210db224Sericheng 	}
617210db224Sericheng 
6185d460eafSCathy Zhou 	if ((promisc_saved == 0) && (err = dls_active_set(dsp)) != 0) {
619cabb4db9SDan McDonald 		ASSERT(dsp->ds_promisc == promisc_saved);
6207c478bd9Sstevel@tonic-gate 		dl_err = DL_SYSERR;
621da14cebeSEric Cheng 		goto failed2;
622210db224Sericheng 	}
623210db224Sericheng 
624210db224Sericheng 	/*
625210db224Sericheng 	 * Adjust channel promiscuity.
626210db224Sericheng 	 */
627cabb4db9SDan McDonald 	err = dls_promisc(dsp, new_flags);
628da14cebeSEric Cheng 
629210db224Sericheng 	if (err != 0) {
630210db224Sericheng 		dl_err = DL_SYSERR;
631da14cebeSEric Cheng 		dsp->ds_promisc = promisc_saved;
6325d460eafSCathy Zhou 		if (promisc_saved == 0)
6335d460eafSCathy Zhou 			dls_active_clear(dsp, B_FALSE);
634da14cebeSEric Cheng 		goto failed2;
635210db224Sericheng 	}
636210db224Sericheng 
637da14cebeSEric Cheng 	mac_perim_exit(mph);
638da14cebeSEric Cheng 
639210db224Sericheng 	dlokack(q, mp, DL_PROMISCON_REQ);
640da14cebeSEric Cheng 	return;
641da14cebeSEric Cheng 
642da14cebeSEric Cheng failed2:
643da14cebeSEric Cheng 	mac_perim_exit(mph);
644210db224Sericheng failed:
645210db224Sericheng 	dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err);
646210db224Sericheng }
647210db224Sericheng 
648210db224Sericheng /*
649210db224Sericheng  * DL_PROMISCOFF_REQ
650210db224Sericheng  */
651da14cebeSEric Cheng static void
proto_promiscoff_req(dld_str_t * dsp,mblk_t * mp)652da14cebeSEric Cheng proto_promiscoff_req(dld_str_t *dsp, mblk_t *mp)
653210db224Sericheng {
654da14cebeSEric Cheng 	dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)mp->b_rptr;
655210db224Sericheng 	int		err = 0;
656210db224Sericheng 	t_uscalar_t	dl_err;
657cabb4db9SDan McDonald 	uint32_t	new_flags;
658210db224Sericheng 	queue_t		*q = dsp->ds_wq;
659da14cebeSEric Cheng 	mac_perim_handle_t	mph;
660210db224Sericheng 
661210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
662210db224Sericheng 		dl_err = DL_BADPRIM;
663210db224Sericheng 		goto failed;
664210db224Sericheng 	}
665210db224Sericheng 
666210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
667210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
668210db224Sericheng 		dl_err = DL_OUTSTATE;
669210db224Sericheng 		goto failed;
670210db224Sericheng 	}
671210db224Sericheng 
672cabb4db9SDan McDonald 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
673cabb4db9SDan McDonald 
674cabb4db9SDan McDonald 	new_flags = dsp->ds_promisc;
675210db224Sericheng 	switch (dlp->dl_level) {
676210db224Sericheng 	case DL_PROMISC_SAP:
677da14cebeSEric Cheng 		if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) {
678da14cebeSEric Cheng 			dl_err = DL_NOTENAB;
679cb6094b4SDan McDonald 			goto failed2;
680da14cebeSEric Cheng 		}
681cabb4db9SDan McDonald 		new_flags &= ~DLS_PROMISC_SAP;
6827c478bd9Sstevel@tonic-gate 		break;
683da14cebeSEric Cheng 
684210db224Sericheng 	case DL_PROMISC_MULTI:
685da14cebeSEric Cheng 		if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) {
686da14cebeSEric Cheng 			dl_err = DL_NOTENAB;
687cb6094b4SDan McDonald 			goto failed2;
688da14cebeSEric Cheng 		}
689cabb4db9SDan McDonald 		new_flags &= ~DLS_PROMISC_MULTI;
690210db224Sericheng 		break;
691da14cebeSEric Cheng 
692210db224Sericheng 	case DL_PROMISC_PHYS:
693da14cebeSEric Cheng 		if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) {
694da14cebeSEric Cheng 			dl_err = DL_NOTENAB;
695cb6094b4SDan McDonald 			goto failed2;
696da14cebeSEric Cheng 		}
697cabb4db9SDan McDonald 		new_flags &= ~DLS_PROMISC_PHYS;
698210db224Sericheng 		break;
699da14cebeSEric Cheng 
700*115f9ea8SRobert Mustacchi 	case DL_PROMISC_RX_ONLY:
701*115f9ea8SRobert Mustacchi 		if (!(dsp->ds_promisc & DLS_PROMISC_RX_ONLY)) {
702*115f9ea8SRobert Mustacchi 			dl_err = DL_NOTENAB;
703*115f9ea8SRobert Mustacchi 			goto failed2;
704*115f9ea8SRobert Mustacchi 		}
705*115f9ea8SRobert Mustacchi 		new_flags &= ~DLS_PROMISC_RX_ONLY;
706*115f9ea8SRobert Mustacchi 		break;
707*115f9ea8SRobert Mustacchi 
708210db224Sericheng 	default:
709210db224Sericheng 		dl_err = DL_NOTSUPPORTED;
710cb6094b4SDan McDonald 		goto failed2;
7117c478bd9Sstevel@tonic-gate 	}
7127c478bd9Sstevel@tonic-gate 
713da14cebeSEric Cheng 	/*
714da14cebeSEric Cheng 	 * Adjust channel promiscuity.
715da14cebeSEric Cheng 	 */
716cabb4db9SDan McDonald 	err = dls_promisc(dsp, new_flags);
717d62bc4baSyz147064 
718210db224Sericheng 	if (err != 0) {
719210db224Sericheng 		dl_err = DL_SYSERR;
720cb6094b4SDan McDonald 		goto failed2;
721210db224Sericheng 	}
7225d460eafSCathy Zhou 
723cabb4db9SDan McDonald 	ASSERT(dsp->ds_promisc == new_flags);
7245d460eafSCathy Zhou 	if (dsp->ds_promisc == 0)
7255d460eafSCathy Zhou 		dls_active_clear(dsp, B_FALSE);
7265d460eafSCathy Zhou 
7275d460eafSCathy Zhou 	mac_perim_exit(mph);
7285d460eafSCathy Zhou 
729210db224Sericheng 	dlokack(q, mp, DL_PROMISCOFF_REQ);
730da14cebeSEric Cheng 	return;
731cb6094b4SDan McDonald failed2:
732cb6094b4SDan McDonald 	mac_perim_exit(mph);
7337c478bd9Sstevel@tonic-gate failed:
734210db224Sericheng 	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
7357c478bd9Sstevel@tonic-gate }
7367c478bd9Sstevel@tonic-gate 
7377c478bd9Sstevel@tonic-gate /*
738210db224Sericheng  * DL_ENABMULTI_REQ
7397c478bd9Sstevel@tonic-gate  */
740da14cebeSEric Cheng static void
proto_enabmulti_req(dld_str_t * dsp,mblk_t * mp)741da14cebeSEric Cheng proto_enabmulti_req(dld_str_t *dsp, mblk_t *mp)
7427c478bd9Sstevel@tonic-gate {
743da14cebeSEric Cheng 	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)mp->b_rptr;
744210db224Sericheng 	int		err = 0;
745210db224Sericheng 	t_uscalar_t	dl_err;
746210db224Sericheng 	queue_t		*q = dsp->ds_wq;
747da14cebeSEric Cheng 	mac_perim_handle_t	mph;
7487c478bd9Sstevel@tonic-gate 
749210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
750210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
751210db224Sericheng 		dl_err = DL_OUTSTATE;
7527c478bd9Sstevel@tonic-gate 		goto failed;
753210db224Sericheng 	}
7547c478bd9Sstevel@tonic-gate 
755210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
756210db224Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
757210db224Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
758210db224Sericheng 		dl_err = DL_BADPRIM;
759210db224Sericheng 		goto failed;
760210db224Sericheng 	}
7617c478bd9Sstevel@tonic-gate 
762da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
763da14cebeSEric Cheng 
7645d460eafSCathy Zhou 	if ((dsp->ds_dmap == NULL) && (err = dls_active_set(dsp)) != 0) {
765210db224Sericheng 		dl_err = DL_SYSERR;
766da14cebeSEric Cheng 		goto failed2;
767210db224Sericheng 	}
768210db224Sericheng 
769da14cebeSEric Cheng 	err = dls_multicst_add(dsp, mp->b_rptr + dlp->dl_addr_offset);
770210db224Sericheng 	if (err != 0) {
7717c478bd9Sstevel@tonic-gate 		switch (err) {
7727c478bd9Sstevel@tonic-gate 		case EINVAL:
7737c478bd9Sstevel@tonic-gate 			dl_err = DL_BADADDR;
7747c478bd9Sstevel@tonic-gate 			err = 0;
7757c478bd9Sstevel@tonic-gate 			break;
7767c478bd9Sstevel@tonic-gate 		case ENOSPC:
7777c478bd9Sstevel@tonic-gate 			dl_err = DL_TOOMANY;
7787c478bd9Sstevel@tonic-gate 			err = 0;
7797c478bd9Sstevel@tonic-gate 			break;
7807c478bd9Sstevel@tonic-gate 		default:
7817c478bd9Sstevel@tonic-gate 			dl_err = DL_SYSERR;
7827c478bd9Sstevel@tonic-gate 			break;
7837c478bd9Sstevel@tonic-gate 		}
7845d460eafSCathy Zhou 		if (dsp->ds_dmap == NULL)
7855d460eafSCathy Zhou 			dls_active_clear(dsp, B_FALSE);
786da14cebeSEric Cheng 		goto failed2;
787210db224Sericheng 	}
788210db224Sericheng 
789da14cebeSEric Cheng 	mac_perim_exit(mph);
790da14cebeSEric Cheng 
791210db224Sericheng 	dlokack(q, mp, DL_ENABMULTI_REQ);
792da14cebeSEric Cheng 	return;
793da14cebeSEric Cheng 
794da14cebeSEric Cheng failed2:
795da14cebeSEric Cheng 	mac_perim_exit(mph);
796210db224Sericheng failed:
797210db224Sericheng 	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
7987c478bd9Sstevel@tonic-gate }
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate /*
801210db224Sericheng  * DL_DISABMULTI_REQ
8027c478bd9Sstevel@tonic-gate  */
803da14cebeSEric Cheng static void
proto_disabmulti_req(dld_str_t * dsp,mblk_t * mp)804da14cebeSEric Cheng proto_disabmulti_req(dld_str_t *dsp, mblk_t *mp)
8057c478bd9Sstevel@tonic-gate {
806da14cebeSEric Cheng 	dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)mp->b_rptr;
807210db224Sericheng 	int		err = 0;
808210db224Sericheng 	t_uscalar_t	dl_err;
809210db224Sericheng 	queue_t		*q = dsp->ds_wq;
810da14cebeSEric Cheng 	mac_perim_handle_t	mph;
8117c478bd9Sstevel@tonic-gate 
812210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
813210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
814210db224Sericheng 		dl_err = DL_OUTSTATE;
8157c478bd9Sstevel@tonic-gate 		goto failed;
816210db224Sericheng 	}
8177c478bd9Sstevel@tonic-gate 
818210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
819210db224Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
820210db224Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
821210db224Sericheng 		dl_err = DL_BADPRIM;
822210db224Sericheng 		goto failed;
823210db224Sericheng 	}
8247c478bd9Sstevel@tonic-gate 
825da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
826da14cebeSEric Cheng 	err = dls_multicst_remove(dsp, mp->b_rptr + dlp->dl_addr_offset);
8275d460eafSCathy Zhou 	if ((err == 0) && (dsp->ds_dmap == NULL))
8285d460eafSCathy Zhou 		dls_active_clear(dsp, B_FALSE);
829da14cebeSEric Cheng 	mac_perim_exit(mph);
830da14cebeSEric Cheng 
831210db224Sericheng 	if (err != 0) {
8327c478bd9Sstevel@tonic-gate 		switch (err) {
8337c478bd9Sstevel@tonic-gate 		case EINVAL:
8347c478bd9Sstevel@tonic-gate 			dl_err = DL_BADADDR;
8357c478bd9Sstevel@tonic-gate 			err = 0;
8367c478bd9Sstevel@tonic-gate 			break;
837da14cebeSEric Cheng 
8387c478bd9Sstevel@tonic-gate 		case ENOENT:
8397c478bd9Sstevel@tonic-gate 			dl_err = DL_NOTENAB;
8407c478bd9Sstevel@tonic-gate 			err = 0;
8417c478bd9Sstevel@tonic-gate 			break;
842da14cebeSEric Cheng 
8437c478bd9Sstevel@tonic-gate 		default:
8447c478bd9Sstevel@tonic-gate 			dl_err = DL_SYSERR;
8457c478bd9Sstevel@tonic-gate 			break;
8467c478bd9Sstevel@tonic-gate 		}
847210db224Sericheng 		goto failed;
848210db224Sericheng 	}
849210db224Sericheng 	dlokack(q, mp, DL_DISABMULTI_REQ);
850da14cebeSEric Cheng 	return;
851210db224Sericheng failed:
852210db224Sericheng 	dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
8537c478bd9Sstevel@tonic-gate }
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate /*
856210db224Sericheng  * DL_PHYS_ADDR_REQ
8577c478bd9Sstevel@tonic-gate  */
858da14cebeSEric Cheng static void
proto_physaddr_req(dld_str_t * dsp,mblk_t * mp)859da14cebeSEric Cheng proto_physaddr_req(dld_str_t *dsp, mblk_t *mp)
8607c478bd9Sstevel@tonic-gate {
861da14cebeSEric Cheng 	dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)mp->b_rptr;
862210db224Sericheng 	queue_t		*q = dsp->ds_wq;
8632b24ab6bSSebastien Roy 	t_uscalar_t	dl_err = 0;
8642b24ab6bSSebastien Roy 	char		*addr = NULL;
8657c478bd9Sstevel@tonic-gate 	uint_t		addr_length;
8667c478bd9Sstevel@tonic-gate 
867210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
868210db224Sericheng 		dl_err = DL_BADPRIM;
8692b24ab6bSSebastien Roy 		goto done;
870210db224Sericheng 	}
871210db224Sericheng 
872210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
873210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
874210db224Sericheng 		dl_err = DL_OUTSTATE;
8752b24ab6bSSebastien Roy 		goto done;
876210db224Sericheng 	}
877210db224Sericheng 
8787c478bd9Sstevel@tonic-gate 	addr_length = dsp->ds_mip->mi_addr_length;
879c08e5e1aSdr146992 	if (addr_length > 0) {
880da14cebeSEric Cheng 		addr = kmem_alloc(addr_length, KM_SLEEP);
8812b24ab6bSSebastien Roy 		switch (dlp->dl_addr_type) {
8822b24ab6bSSebastien Roy 		case DL_CURR_PHYS_ADDR:
883da14cebeSEric Cheng 			mac_unicast_primary_get(dsp->ds_mh, (uint8_t *)addr);
8842b24ab6bSSebastien Roy 			break;
8852b24ab6bSSebastien Roy 		case DL_FACT_PHYS_ADDR:
886da14cebeSEric Cheng 			bcopy(dsp->ds_mip->mi_unicst_addr, addr, addr_length);
8872b24ab6bSSebastien Roy 			break;
8882b24ab6bSSebastien Roy 		case DL_CURR_DEST_ADDR:
8892b24ab6bSSebastien Roy 			if (!mac_dst_get(dsp->ds_mh, (uint8_t *)addr))
8902b24ab6bSSebastien Roy 				dl_err = DL_NOTSUPPORTED;
8912b24ab6bSSebastien Roy 			break;
8922b24ab6bSSebastien Roy 		default:
8932b24ab6bSSebastien Roy 			dl_err = DL_UNSUPPORTED;
894c08e5e1aSdr146992 		}
8952b24ab6bSSebastien Roy 	}
8962b24ab6bSSebastien Roy done:
8972b24ab6bSSebastien Roy 	if (dl_err == 0)
8982b24ab6bSSebastien Roy 		dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
8992b24ab6bSSebastien Roy 	else
900210db224Sericheng 		dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
9012b24ab6bSSebastien Roy 	if (addr != NULL)
9022b24ab6bSSebastien Roy 		kmem_free(addr, addr_length);
903210db224Sericheng }
904210db224Sericheng 
905210db224Sericheng /*
906210db224Sericheng  * DL_SET_PHYS_ADDR_REQ
907210db224Sericheng  */
908da14cebeSEric Cheng static void
proto_setphysaddr_req(dld_str_t * dsp,mblk_t * mp)909da14cebeSEric Cheng proto_setphysaddr_req(dld_str_t *dsp, mblk_t *mp)
910210db224Sericheng {
911da14cebeSEric Cheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)mp->b_rptr;
912210db224Sericheng 	int		err = 0;
913210db224Sericheng 	t_uscalar_t	dl_err;
914210db224Sericheng 	queue_t		*q = dsp->ds_wq;
915da14cebeSEric Cheng 	mac_perim_handle_t	mph;
916210db224Sericheng 
917210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
918210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
919210db224Sericheng 		dl_err = DL_OUTSTATE;
920210db224Sericheng 		goto failed;
921210db224Sericheng 	}
922210db224Sericheng 
923210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
924210db224Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
925210db224Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
926210db224Sericheng 		dl_err = DL_BADPRIM;
927210db224Sericheng 		goto failed;
928210db224Sericheng 	}
929210db224Sericheng 
930da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
931da14cebeSEric Cheng 
9325d460eafSCathy Zhou 	if ((err = dls_active_set(dsp)) != 0) {
933210db224Sericheng 		dl_err = DL_SYSERR;
934da14cebeSEric Cheng 		goto failed2;
935210db224Sericheng 	}
936210db224Sericheng 
93725ec3e3dSEric Cheng 	/*
93825ec3e3dSEric Cheng 	 * If mac-nospoof is enabled and the link is owned by a
93925ec3e3dSEric Cheng 	 * non-global zone, changing the mac address is not allowed.
94025ec3e3dSEric Cheng 	 */
94125ec3e3dSEric Cheng 	if (dsp->ds_dlp->dl_zid != GLOBAL_ZONEID &&
94225ec3e3dSEric Cheng 	    mac_protect_enabled(dsp->ds_mch, MPT_MACNOSPOOF)) {
94325ec3e3dSEric Cheng 		dls_active_clear(dsp, B_FALSE);
94425ec3e3dSEric Cheng 		err = EACCES;
94525ec3e3dSEric Cheng 		goto failed2;
94625ec3e3dSEric Cheng 	}
94725ec3e3dSEric Cheng 
948da14cebeSEric Cheng 	err = mac_unicast_primary_set(dsp->ds_mh,
949da14cebeSEric Cheng 	    mp->b_rptr + dlp->dl_addr_offset);
950210db224Sericheng 	if (err != 0) {
9517c478bd9Sstevel@tonic-gate 		switch (err) {
9527c478bd9Sstevel@tonic-gate 		case EINVAL:
9537c478bd9Sstevel@tonic-gate 			dl_err = DL_BADADDR;
9547c478bd9Sstevel@tonic-gate 			err = 0;
9557c478bd9Sstevel@tonic-gate 			break;
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 		default:
9587c478bd9Sstevel@tonic-gate 			dl_err = DL_SYSERR;
9597c478bd9Sstevel@tonic-gate 			break;
9607c478bd9Sstevel@tonic-gate 		}
9615d460eafSCathy Zhou 		dls_active_clear(dsp, B_FALSE);
962da14cebeSEric Cheng 		goto failed2;
963da14cebeSEric Cheng 
964210db224Sericheng 	}
965d62bc4baSyz147064 
966da14cebeSEric Cheng 	mac_perim_exit(mph);
967da14cebeSEric Cheng 
968210db224Sericheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
969da14cebeSEric Cheng 	return;
970da14cebeSEric Cheng 
971da14cebeSEric Cheng failed2:
972da14cebeSEric Cheng 	mac_perim_exit(mph);
973210db224Sericheng failed:
974210db224Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
9757c478bd9Sstevel@tonic-gate }
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate /*
978210db224Sericheng  * DL_UDQOS_REQ
9797c478bd9Sstevel@tonic-gate  */
980da14cebeSEric Cheng static void
proto_udqos_req(dld_str_t * dsp,mblk_t * mp)981da14cebeSEric Cheng proto_udqos_req(dld_str_t *dsp, mblk_t *mp)
9827c478bd9Sstevel@tonic-gate {
983da14cebeSEric Cheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)mp->b_rptr;
984210db224Sericheng 	dl_qos_cl_sel1_t *selp;
985210db224Sericheng 	int		off, len;
986210db224Sericheng 	t_uscalar_t	dl_err;
987210db224Sericheng 	queue_t		*q = dsp->ds_wq;
988210db224Sericheng 
989210db224Sericheng 	off = dlp->dl_qos_offset;
990210db224Sericheng 	len = dlp->dl_qos_length;
991210db224Sericheng 
992210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
993210db224Sericheng 		dl_err = DL_BADPRIM;
994210db224Sericheng 		goto failed;
995210db224Sericheng 	}
996210db224Sericheng 
997210db224Sericheng 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
998210db224Sericheng 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
999210db224Sericheng 		dl_err = DL_BADQOSTYPE;
1000210db224Sericheng 		goto failed;
1001210db224Sericheng 	}
1002210db224Sericheng 
1003605445d5Sdg199075 	if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
1004210db224Sericheng 	    selp->dl_priority < 0) {
1005210db224Sericheng 		dl_err = DL_BADQOSPARAM;
1006210db224Sericheng 		goto failed;
1007210db224Sericheng 	}
1008210db224Sericheng 
1009d62bc4baSyz147064 	dsp->ds_pri = selp->dl_priority;
1010210db224Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
1011da14cebeSEric Cheng 	return;
1012210db224Sericheng failed:
1013210db224Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
1014210db224Sericheng }
1015210db224Sericheng 
10164b46d1efSkrgopi static boolean_t
check_mod_above(queue_t * q,const char * mod)10178d4cf8d8S check_mod_above(queue_t *q, const char *mod)
10184b46d1efSkrgopi {
10194b46d1efSkrgopi 	queue_t		*next_q;
10204b46d1efSkrgopi 	boolean_t	ret = B_TRUE;
10214b46d1efSkrgopi 
10224b46d1efSkrgopi 	claimstr(q);
10234b46d1efSkrgopi 	next_q = q->q_next;
10248d4cf8d8S 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, mod) != 0)
10254b46d1efSkrgopi 		ret = B_FALSE;
10264b46d1efSkrgopi 	releasestr(q);
10274b46d1efSkrgopi 	return (ret);
10284b46d1efSkrgopi }
10294b46d1efSkrgopi 
1030210db224Sericheng /*
1031210db224Sericheng  * DL_CAPABILITY_REQ
1032210db224Sericheng  */
1033da14cebeSEric Cheng static void
proto_capability_req(dld_str_t * dsp,mblk_t * mp)1034da14cebeSEric Cheng proto_capability_req(dld_str_t *dsp, mblk_t *mp)
1035210db224Sericheng {
1036da14cebeSEric Cheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)mp->b_rptr;
1037210db224Sericheng 	dl_capability_sub_t *sp;
1038210db224Sericheng 	size_t		size, len;
1039210db224Sericheng 	offset_t	off, end;
1040210db224Sericheng 	t_uscalar_t	dl_err;
1041210db224Sericheng 	queue_t		*q = dsp->ds_wq;
1042210db224Sericheng 
1043210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1044210db224Sericheng 		dl_err = DL_BADPRIM;
1045210db224Sericheng 		goto failed;
1046210db224Sericheng 	}
1047210db224Sericheng 
1048210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1049210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1050210db224Sericheng 		dl_err = DL_OUTSTATE;
1051210db224Sericheng 		goto failed;
1052210db224Sericheng 	}
1053210db224Sericheng 
1054210db224Sericheng 	/*
1055210db224Sericheng 	 * This request is overloaded. If there are no requested capabilities
1056210db224Sericheng 	 * then we just want to acknowledge with all the capabilities we
1057210db224Sericheng 	 * support. Otherwise we enable the set of capabilities requested.
1058210db224Sericheng 	 */
1059210db224Sericheng 	if (dlp->dl_sub_length == 0) {
1060da14cebeSEric Cheng 		proto_capability_advertise(dsp, mp);
1061da14cebeSEric Cheng 		return;
1062210db224Sericheng 	}
1063210db224Sericheng 
1064210db224Sericheng 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1065210db224Sericheng 		dl_err = DL_BADPRIM;
1066210db224Sericheng 		goto failed;
1067210db224Sericheng 	}
1068210db224Sericheng 
1069210db224Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
1070210db224Sericheng 
1071210db224Sericheng 	off = dlp->dl_sub_offset;
1072210db224Sericheng 	len = dlp->dl_sub_length;
1073210db224Sericheng 
1074210db224Sericheng 	/*
1075210db224Sericheng 	 * Walk the list of capabilities to be enabled.
1076210db224Sericheng 	 */
1077210db224Sericheng 	for (end = off + len; off < end; ) {
1078210db224Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1079210db224Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
1080210db224Sericheng 
1081210db224Sericheng 		if (off + size > end ||
1082210db224Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1083210db224Sericheng 			dl_err = DL_BADPRIM;
1084210db224Sericheng 			goto failed;
1085210db224Sericheng 		}
1086210db224Sericheng 
1087210db224Sericheng 		switch (sp->dl_cap) {
1088210db224Sericheng 		/*
1089210db224Sericheng 		 * TCP/IP checksum offload to hardware.
1090210db224Sericheng 		 */
1091210db224Sericheng 		case DL_CAPAB_HCKSUM: {
1092210db224Sericheng 			dl_capab_hcksum_t *hcksump;
1093210db224Sericheng 			dl_capab_hcksum_t hcksum;
1094210db224Sericheng 
1095210db224Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1096210db224Sericheng 			/*
1097210db224Sericheng 			 * Copy for alignment.
1098210db224Sericheng 			 */
1099210db224Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1100210db224Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1101210db224Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1102210db224Sericheng 			break;
1103210db224Sericheng 		}
1104210db224Sericheng 
1105da14cebeSEric Cheng 		case DL_CAPAB_DLD: {
1106da14cebeSEric Cheng 			dl_capab_dld_t	*dldp;
1107da14cebeSEric Cheng 			dl_capab_dld_t	dld;
11088347601bSyl150051 
1109da14cebeSEric Cheng 			dldp = (dl_capab_dld_t *)&sp[1];
11108347601bSyl150051 			/*
11118347601bSyl150051 			 * Copy for alignment.
11128347601bSyl150051 			 */
1113da14cebeSEric Cheng 			bcopy(dldp, &dld, sizeof (dl_capab_dld_t));
1114da14cebeSEric Cheng 			dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq);
1115da14cebeSEric Cheng 			bcopy(&dld, dldp, sizeof (dl_capab_dld_t));
1116210db224Sericheng 			break;
1117210db224Sericheng 		}
1118210db224Sericheng 		default:
1119210db224Sericheng 			break;
1120210db224Sericheng 		}
1121210db224Sericheng 		off += size;
1122210db224Sericheng 	}
1123210db224Sericheng 	qreply(q, mp);
1124da14cebeSEric Cheng 	return;
1125210db224Sericheng failed:
1126210db224Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
1127210db224Sericheng }
1128210db224Sericheng 
1129210db224Sericheng /*
1130210db224Sericheng  * DL_NOTIFY_REQ
1131210db224Sericheng  */
1132da14cebeSEric Cheng static void
proto_notify_req(dld_str_t * dsp,mblk_t * mp)1133da14cebeSEric Cheng proto_notify_req(dld_str_t *dsp, mblk_t *mp)
1134210db224Sericheng {
1135da14cebeSEric Cheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)mp->b_rptr;
1136210db224Sericheng 	t_uscalar_t	dl_err;
1137210db224Sericheng 	queue_t		*q = dsp->ds_wq;
1138210db224Sericheng 	uint_t		note =
1139210db224Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1140210db224Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1141210db224Sericheng 	    DL_NOTE_PHYS_ADDR |
1142210db224Sericheng 	    DL_NOTE_LINK_UP |
1143210db224Sericheng 	    DL_NOTE_LINK_DOWN |
1144ba2e4443Sseb 	    DL_NOTE_CAPAB_RENEG |
1145cef310fdSGirish Moodalbail 	    DL_NOTE_FASTPATH_FLUSH |
11462b24ab6bSSebastien Roy 	    DL_NOTE_SPEED |
1147550b6e40SSowmini Varadhan 	    DL_NOTE_SDU_SIZE|
11481eee170aSErik Nordmark 	    DL_NOTE_SDU_SIZE2|
1149550b6e40SSowmini Varadhan 	    DL_NOTE_ALLOWED_IPS;
1150210db224Sericheng 
1151210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1152210db224Sericheng 		dl_err = DL_BADPRIM;
1153210db224Sericheng 		goto failed;
1154210db224Sericheng 	}
1155210db224Sericheng 
1156210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1157210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1158210db224Sericheng 		dl_err = DL_OUTSTATE;
1159210db224Sericheng 		goto failed;
1160210db224Sericheng 	}
1161210db224Sericheng 
1162d62bc4baSyz147064 	note &= ~(mac_no_notification(dsp->ds_mh));
1163d62bc4baSyz147064 
1164210db224Sericheng 	/*
1165210db224Sericheng 	 * Cache the notifications that are being enabled.
1166210db224Sericheng 	 */
1167210db224Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
1168210db224Sericheng 	/*
1169210db224Sericheng 	 * The ACK carries all notifications regardless of which set is
1170210db224Sericheng 	 * being enabled.
1171210db224Sericheng 	 */
1172210db224Sericheng 	dlnotifyack(q, mp, note);
1173210db224Sericheng 
1174210db224Sericheng 	/*
1175da14cebeSEric Cheng 	 * Generate DL_NOTIFY_IND messages for each enabled notification.
1176210db224Sericheng 	 */
1177210db224Sericheng 	if (dsp->ds_notifications != 0) {
1178210db224Sericheng 		dld_str_notify_ind(dsp);
1179210db224Sericheng 	}
1180da14cebeSEric Cheng 	return;
1181210db224Sericheng failed:
1182210db224Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
1183210db224Sericheng }
1184210db224Sericheng 
1185210db224Sericheng /*
1186da14cebeSEric Cheng  * DL_UINTDATA_REQ
1187210db224Sericheng  */
1188d62bc4baSyz147064 void
proto_unitdata_req(dld_str_t * dsp,mblk_t * mp)1189da14cebeSEric Cheng proto_unitdata_req(dld_str_t *dsp, mblk_t *mp)
1190210db224Sericheng {
1191210db224Sericheng 	queue_t			*q = dsp->ds_wq;
1192d62bc4baSyz147064 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)mp->b_rptr;
1193210db224Sericheng 	off_t			off;
1194210db224Sericheng 	size_t			len, size;
1195210db224Sericheng 	const uint8_t		*addr;
1196210db224Sericheng 	uint16_t		sap;
1197210db224Sericheng 	uint_t			addr_length;
1198ba2e4443Sseb 	mblk_t			*bp, *payload;
1199210db224Sericheng 	t_uscalar_t		dl_err;
1200e7801d59Ssowmini 	uint_t			max_sdu;
1201210db224Sericheng 
1202210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1203da14cebeSEric Cheng 		dlerrorack(q, mp, DL_UNITDATA_REQ, DL_BADPRIM, 0);
1204da14cebeSEric Cheng 		return;
1205210db224Sericheng 	}
1206210db224Sericheng 
1207da14cebeSEric Cheng 	mutex_enter(&dsp->ds_lock);
1208da14cebeSEric Cheng 	if (dsp->ds_dlstate != DL_IDLE) {
1209da14cebeSEric Cheng 		mutex_exit(&dsp->ds_lock);
1210da14cebeSEric Cheng 		dlerrorack(q, mp, DL_UNITDATA_REQ, DL_OUTSTATE, 0);
1211da14cebeSEric Cheng 		return;
1212da14cebeSEric Cheng 	}
1213da14cebeSEric Cheng 	DLD_DATATHR_INC(dsp);
1214da14cebeSEric Cheng 	mutex_exit(&dsp->ds_lock);
1215da14cebeSEric Cheng 
1216210db224Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
1217210db224Sericheng 
1218210db224Sericheng 	off = dlp->dl_dest_addr_offset;
1219210db224Sericheng 	len = dlp->dl_dest_addr_length;
1220210db224Sericheng 
1221210db224Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1222210db224Sericheng 		dl_err = DL_BADPRIM;
1223210db224Sericheng 		goto failed;
1224210db224Sericheng 	}
1225210db224Sericheng 
1226210db224Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1227210db224Sericheng 		dl_err = DL_BADADDR;
1228210db224Sericheng 		goto failed;
1229210db224Sericheng 	}
1230210db224Sericheng 
1231210db224Sericheng 	addr = mp->b_rptr + off;
1232210db224Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1233210db224Sericheng 
1234210db224Sericheng 	/*
1235210db224Sericheng 	 * Check the length of the packet and the block types.
1236210db224Sericheng 	 */
1237210db224Sericheng 	size = 0;
1238ba2e4443Sseb 	payload = mp->b_cont;
1239ba2e4443Sseb 	for (bp = payload; bp != NULL; bp = bp->b_cont) {
1240210db224Sericheng 		if (DB_TYPE(bp) != M_DATA)
1241210db224Sericheng 			goto baddata;
1242210db224Sericheng 
1243210db224Sericheng 		size += MBLKL(bp);
1244210db224Sericheng 	}
1245210db224Sericheng 
1246e7801d59Ssowmini 	mac_sdu_get(dsp->ds_mh, NULL, &max_sdu);
1247e7801d59Ssowmini 	if (size > max_sdu)
1248210db224Sericheng 		goto baddata;
1249210db224Sericheng 
1250210db224Sericheng 	/*
1251210db224Sericheng 	 * Build a packet header.
1252210db224Sericheng 	 */
1253da14cebeSEric Cheng 	if ((bp = dls_header(dsp, addr, sap, dlp->dl_priority.dl_max,
1254605445d5Sdg199075 	    &payload)) == NULL) {
1255210db224Sericheng 		dl_err = DL_BADADDR;
1256210db224Sericheng 		goto failed;
1257210db224Sericheng 	}
1258210db224Sericheng 
1259210db224Sericheng 	/*
1260210db224Sericheng 	 * We no longer need the M_PROTO header, so free it.
1261210db224Sericheng 	 */
1262210db224Sericheng 	freeb(mp);
1263210db224Sericheng 
1264210db224Sericheng 	/*
1265210db224Sericheng 	 * Transfer the checksum offload information if it is present.
1266210db224Sericheng 	 */
1267ec71f88eSPatrick Mooney 	mac_hcksum_clone(payload, bp);
1268210db224Sericheng 
1269210db224Sericheng 	/*
1270210db224Sericheng 	 * Link the payload onto the new header.
1271210db224Sericheng 	 */
1272210db224Sericheng 	ASSERT(bp->b_cont == NULL);
1273ba2e4443Sseb 	bp->b_cont = payload;
1274da14cebeSEric Cheng 
1275da14cebeSEric Cheng 	/*
1276da14cebeSEric Cheng 	 * No lock can be held across modules and putnext()'s,
1277da14cebeSEric Cheng 	 * which can happen here with the call from DLD_TX().
1278da14cebeSEric Cheng 	 */
12798648b7dbSToomas Soome 	if (DLD_TX(dsp, bp, 0, 0) != 0) {
1280da14cebeSEric Cheng 		/* flow-controlled */
1281da14cebeSEric Cheng 		DLD_SETQFULL(dsp);
1282da14cebeSEric Cheng 	}
1283da14cebeSEric Cheng 	DLD_DATATHR_DCR(dsp);
1284d62bc4baSyz147064 	return;
1285da14cebeSEric Cheng 
1286210db224Sericheng failed:
1287210db224Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
1288da14cebeSEric Cheng 	DLD_DATATHR_DCR(dsp);
1289d62bc4baSyz147064 	return;
1290210db224Sericheng 
1291210db224Sericheng baddata:
1292210db224Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1293da14cebeSEric Cheng 	DLD_DATATHR_DCR(dsp);
1294210db224Sericheng }
1295210db224Sericheng 
1296210db224Sericheng /*
1297210db224Sericheng  * DL_PASSIVE_REQ
1298210db224Sericheng  */
1299da14cebeSEric Cheng static void
proto_passive_req(dld_str_t * dsp,mblk_t * mp)1300da14cebeSEric Cheng proto_passive_req(dld_str_t *dsp, mblk_t *mp)
1301210db224Sericheng {
1302210db224Sericheng 	t_uscalar_t dl_err;
1303210db224Sericheng 
1304d62bc4baSyz147064 	/*
1305210db224Sericheng 	 * If we've already become active by issuing an active primitive,
1306210db224Sericheng 	 * then it's too late to try to become passive.
1307210db224Sericheng 	 */
1308210db224Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1309210db224Sericheng 		dl_err = DL_OUTSTATE;
1310210db224Sericheng 		goto failed;
1311210db224Sericheng 	}
1312210db224Sericheng 
1313210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1314210db224Sericheng 		dl_err = DL_BADPRIM;
1315210db224Sericheng 		goto failed;
1316210db224Sericheng 	}
1317210db224Sericheng 
1318210db224Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1319210db224Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1320da14cebeSEric Cheng 	return;
1321210db224Sericheng failed:
1322210db224Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1323210db224Sericheng }
1324210db224Sericheng 
1325da14cebeSEric Cheng 
1326210db224Sericheng /*
1327210db224Sericheng  * Catch-all handler.
1328210db224Sericheng  */
1329da14cebeSEric Cheng static void
proto_req(dld_str_t * dsp,mblk_t * mp)1330da14cebeSEric Cheng proto_req(dld_str_t *dsp, mblk_t *mp)
1331210db224Sericheng {
1332da14cebeSEric Cheng 	union DL_primitives	*dlp = (union DL_primitives *)mp->b_rptr;
1333da14cebeSEric Cheng 
1334210db224Sericheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
13357c478bd9Sstevel@tonic-gate }
13367c478bd9Sstevel@tonic-gate 
1337da14cebeSEric Cheng static int
dld_capab_perim(dld_str_t * dsp,void * data,uint_t flags)1338da14cebeSEric Cheng dld_capab_perim(dld_str_t *dsp, void *data, uint_t flags)
13397c478bd9Sstevel@tonic-gate {
1340da14cebeSEric Cheng 	switch (flags) {
1341da14cebeSEric Cheng 	case DLD_ENABLE:
1342da14cebeSEric Cheng 		mac_perim_enter_by_mh(dsp->ds_mh, (mac_perim_handle_t *)data);
1343da14cebeSEric Cheng 		return (0);
13447c478bd9Sstevel@tonic-gate 
1345da14cebeSEric Cheng 	case DLD_DISABLE:
1346da14cebeSEric Cheng 		mac_perim_exit((mac_perim_handle_t)data);
1347da14cebeSEric Cheng 		return (0);
1348210db224Sericheng 
1349da14cebeSEric Cheng 	case DLD_QUERY:
1350da14cebeSEric Cheng 		return (mac_perim_held(dsp->ds_mh));
1351da14cebeSEric Cheng 	}
1352da14cebeSEric Cheng 	return (0);
13537c478bd9Sstevel@tonic-gate }
13547c478bd9Sstevel@tonic-gate 
1355da14cebeSEric Cheng static int
dld_capab_direct(dld_str_t * dsp,void * data,uint_t flags)1356da14cebeSEric Cheng dld_capab_direct(dld_str_t *dsp, void *data, uint_t flags)
13577c478bd9Sstevel@tonic-gate {
1358da14cebeSEric Cheng 	dld_capab_direct_t	*direct = data;
13597c478bd9Sstevel@tonic-gate 
1360da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
1361da14cebeSEric Cheng 
1362da14cebeSEric Cheng 	switch (flags) {
1363da14cebeSEric Cheng 	case DLD_ENABLE:
1364da14cebeSEric Cheng 		dls_rx_set(dsp, (dls_rx_t)direct->di_rx_cf,
1365da14cebeSEric Cheng 		    direct->di_rx_ch);
1366ae6aa22aSVenugopal Iyer 
1367da14cebeSEric Cheng 		direct->di_tx_df = (uintptr_t)str_mdata_fastpath_put;
1368da14cebeSEric Cheng 		direct->di_tx_dh = dsp;
1369da14cebeSEric Cheng 		direct->di_tx_cb_df = (uintptr_t)mac_client_tx_notify;
1370da14cebeSEric Cheng 		direct->di_tx_cb_dh = dsp->ds_mch;
1371ae6aa22aSVenugopal Iyer 		direct->di_tx_fctl_df = (uintptr_t)mac_tx_is_flow_blocked;
1372ae6aa22aSVenugopal Iyer 		direct->di_tx_fctl_dh = dsp->ds_mch;
1373ae6aa22aSVenugopal Iyer 
1374da14cebeSEric Cheng 		dsp->ds_direct = B_TRUE;
1375da14cebeSEric Cheng 
1376da14cebeSEric Cheng 		return (0);
1377da14cebeSEric Cheng 
1378da14cebeSEric Cheng 	case DLD_DISABLE:
1379da14cebeSEric Cheng 		dls_rx_set(dsp, (dsp->ds_mode == DLD_FASTPATH) ?
1380da14cebeSEric Cheng 		    dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp);
1381da14cebeSEric Cheng 		dsp->ds_direct = B_FALSE;
1382da14cebeSEric Cheng 
1383da14cebeSEric Cheng 		return (0);
1384da14cebeSEric Cheng 	}
1385da14cebeSEric Cheng 	return (ENOTSUP);
1386da14cebeSEric Cheng }
13877c478bd9Sstevel@tonic-gate 
13887c478bd9Sstevel@tonic-gate /*
138984de666eSRyan Zezeski  * This function is misnamed. All polling and fanouts are run out of
139084de666eSRyan Zezeski  * the lower MAC for VNICs and out of the MAC for NICs. The
139184de666eSRyan Zezeski  * availability of Rx rings and promiscous mode is taken care of
139284de666eSRyan Zezeski  * between the soft ring set (mac_srs), the Rx ring, and the SW
139384de666eSRyan Zezeski  * classifier. Fanout, if necessary, is done by the soft rings that
139484de666eSRyan Zezeski  * are part of the SRS. By default the SRS divvies up the packets
139584de666eSRyan Zezeski  * based on protocol: TCP, UDP, or Other (OTH).
1396da14cebeSEric Cheng  *
139784de666eSRyan Zezeski  * The SRS (or its associated soft rings) always store the ill_rx_ring
1398da14cebeSEric Cheng  * (the cookie returned when they registered with IP during plumb) as their
1399da14cebeSEric Cheng  * 2nd argument which is passed up as mac_resource_handle_t. The upcall
1400da14cebeSEric Cheng  * function and 1st argument is what the caller registered when they
1401da14cebeSEric Cheng  * called mac_rx_classify_flow_add() to register the flow. For VNIC,
1402da14cebeSEric Cheng  * the function is vnic_rx and argument is vnic_t. For regular NIC
1403da14cebeSEric Cheng  * case, it mac_rx_default and mac_handle_t. As explained above, the
140484de666eSRyan Zezeski  * SRS (or its soft ring) will add the ill_rx_ring (mac_resource_handle_t)
1405da14cebeSEric Cheng  * from its stored 2nd argument.
14067c478bd9Sstevel@tonic-gate  */
1407da14cebeSEric Cheng static int
dld_capab_poll_enable(dld_str_t * dsp,dld_capab_poll_t * poll)1408da14cebeSEric Cheng dld_capab_poll_enable(dld_str_t *dsp, dld_capab_poll_t *poll)
1409da14cebeSEric Cheng {
1410da14cebeSEric Cheng 	if (dsp->ds_polling)
1411da14cebeSEric Cheng 		return (EINVAL);
14127c478bd9Sstevel@tonic-gate 
1413da14cebeSEric Cheng 	if ((dld_opt & DLD_OPT_NO_POLL) != 0 || dsp->ds_mode == DLD_RAW)
1414da14cebeSEric Cheng 		return (ENOTSUP);
14157c478bd9Sstevel@tonic-gate 
14167c478bd9Sstevel@tonic-gate 	/*
141784de666eSRyan Zezeski 	 * Enable client polling if and only if DLS bypass is
141884de666eSRyan Zezeski 	 * possible. Some traffic requires DLS processing in the Rx
141984de666eSRyan Zezeski 	 * data path. In such a case we can neither allow the client
142084de666eSRyan Zezeski 	 * (IP) to directly poll the soft ring (since DLS processing
142184de666eSRyan Zezeski 	 * hasn't been done) nor can we allow DLS bypass.
14227c478bd9Sstevel@tonic-gate 	 */
1423da14cebeSEric Cheng 	if (!mac_rx_bypass_set(dsp->ds_mch, dsp->ds_rx, dsp->ds_rx_arg))
1424da14cebeSEric Cheng 		return (ENOTSUP);
14257c478bd9Sstevel@tonic-gate 
14267c478bd9Sstevel@tonic-gate 	/*
1427da14cebeSEric Cheng 	 * Register soft ring resources. This will come in handy later if
1428da14cebeSEric Cheng 	 * the user decides to modify CPU bindings to use more CPUs for the
1429da14cebeSEric Cheng 	 * device in which case we will switch to fanout using soft rings.
14307c478bd9Sstevel@tonic-gate 	 */
1431da14cebeSEric Cheng 	mac_resource_set_common(dsp->ds_mch,
1432da14cebeSEric Cheng 	    (mac_resource_add_t)poll->poll_ring_add_cf,
1433da14cebeSEric Cheng 	    (mac_resource_remove_t)poll->poll_ring_remove_cf,
1434da14cebeSEric Cheng 	    (mac_resource_quiesce_t)poll->poll_ring_quiesce_cf,
1435da14cebeSEric Cheng 	    (mac_resource_restart_t)poll->poll_ring_restart_cf,
1436da14cebeSEric Cheng 	    (mac_resource_bind_t)poll->poll_ring_bind_cf,
1437da14cebeSEric Cheng 	    poll->poll_ring_ch);
14387c478bd9Sstevel@tonic-gate 
1439da14cebeSEric Cheng 	mac_client_poll_enable(dsp->ds_mch);
1440da14cebeSEric Cheng 
14417c478bd9Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
1442da14cebeSEric Cheng 	return (0);
14437c478bd9Sstevel@tonic-gate }
14447c478bd9Sstevel@tonic-gate 
1445da14cebeSEric Cheng /* ARGSUSED */
1446da14cebeSEric Cheng static int
dld_capab_poll_disable(dld_str_t * dsp,dld_capab_poll_t * poll)1447da14cebeSEric Cheng dld_capab_poll_disable(dld_str_t *dsp, dld_capab_poll_t *poll)
14484b46d1efSkrgopi {
1449da14cebeSEric Cheng 	if (!dsp->ds_polling)
1450da14cebeSEric Cheng 		return (EINVAL);
14514b46d1efSkrgopi 
1452da14cebeSEric Cheng 	mac_client_poll_disable(dsp->ds_mch);
1453da14cebeSEric Cheng 	mac_resource_set(dsp->ds_mch, NULL, NULL);
1454da14cebeSEric Cheng 
1455da14cebeSEric Cheng 	dsp->ds_polling = B_FALSE;
1456da14cebeSEric Cheng 	return (0);
1457da14cebeSEric Cheng }
1458da14cebeSEric Cheng 
1459da14cebeSEric Cheng static int
dld_capab_poll(dld_str_t * dsp,void * data,uint_t flags)1460da14cebeSEric Cheng dld_capab_poll(dld_str_t *dsp, void *data, uint_t flags)
1461da14cebeSEric Cheng {
1462da14cebeSEric Cheng 	dld_capab_poll_t	*poll = data;
1463da14cebeSEric Cheng 
1464da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
1465da14cebeSEric Cheng 
1466da14cebeSEric Cheng 	switch (flags) {
1467da14cebeSEric Cheng 	case DLD_ENABLE:
1468da14cebeSEric Cheng 		return (dld_capab_poll_enable(dsp, poll));
1469da14cebeSEric Cheng 	case DLD_DISABLE:
1470da14cebeSEric Cheng 		return (dld_capab_poll_disable(dsp, poll));
1471da14cebeSEric Cheng 	}
1472da14cebeSEric Cheng 	return (ENOTSUP);
1473da14cebeSEric Cheng }
1474da14cebeSEric Cheng 
1475da14cebeSEric Cheng static int
dld_capab_lso(dld_str_t * dsp,void * data,uint_t flags)1476da14cebeSEric Cheng dld_capab_lso(dld_str_t *dsp, void *data, uint_t flags)
1477da14cebeSEric Cheng {
1478da14cebeSEric Cheng 	dld_capab_lso_t		*lso = data;
1479da14cebeSEric Cheng 
1480da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
1481da14cebeSEric Cheng 
1482da14cebeSEric Cheng 	switch (flags) {
1483da14cebeSEric Cheng 	case DLD_ENABLE: {
1484da14cebeSEric Cheng 		mac_capab_lso_t		mac_lso;
14854b46d1efSkrgopi 
14864b46d1efSkrgopi 		/*
1487da14cebeSEric Cheng 		 * Check if LSO is supported on this MAC & enable LSO
1488da14cebeSEric Cheng 		 * accordingly.
14894b46d1efSkrgopi 		 */
1490da14cebeSEric Cheng 		if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) {
149162366fbbSRobert Mustacchi 			lso->lso_max_tcpv4 = mac_lso.lso_basic_tcp_ipv4.lso_max;
149262366fbbSRobert Mustacchi 			lso->lso_max_tcpv6 = mac_lso.lso_basic_tcp_ipv6.lso_max;
1493da14cebeSEric Cheng 			lso->lso_flags = 0;
1494da14cebeSEric Cheng 			/* translate the flag for mac clients */
1495da14cebeSEric Cheng 			if ((mac_lso.lso_flags & LSO_TX_BASIC_TCP_IPV4) != 0)
1496bd670b35SErik Nordmark 				lso->lso_flags |= DLD_LSO_BASIC_TCP_IPV4;
149762366fbbSRobert Mustacchi 			if ((mac_lso.lso_flags & LSO_TX_BASIC_TCP_IPV6) != 0)
149862366fbbSRobert Mustacchi 				lso->lso_flags |= DLD_LSO_BASIC_TCP_IPV6;
149962366fbbSRobert Mustacchi 			dsp->ds_lso = lso->lso_flags != 0;
150062366fbbSRobert Mustacchi 			/*
150162366fbbSRobert Mustacchi 			 * DLS uses this to try and make sure that a raw ioctl
150262366fbbSRobert Mustacchi 			 * doesn't send too much data, but doesn't currently
150362366fbbSRobert Mustacchi 			 * check the actual SAP that is sending this (or that
150462366fbbSRobert Mustacchi 			 * it's TCP). So for now, just use the max value here.
150562366fbbSRobert Mustacchi 			 */
150662366fbbSRobert Mustacchi 			dsp->ds_lso_max = MAX(lso->lso_max_tcpv4,
150762366fbbSRobert Mustacchi 			    lso->lso_max_tcpv6);
1508da14cebeSEric Cheng 		} else {
1509da14cebeSEric Cheng 			dsp->ds_lso = B_FALSE;
1510da14cebeSEric Cheng 			dsp->ds_lso_max = 0;
1511da14cebeSEric Cheng 			return (ENOTSUP);
1512da14cebeSEric Cheng 		}
1513da14cebeSEric Cheng 		return (0);
1514da14cebeSEric Cheng 	}
1515da14cebeSEric Cheng 	case DLD_DISABLE: {
1516da14cebeSEric Cheng 		dsp->ds_lso = B_FALSE;
1517da14cebeSEric Cheng 		dsp->ds_lso_max = 0;
1518da14cebeSEric Cheng 		return (0);
1519da14cebeSEric Cheng 	}
1520da14cebeSEric Cheng 	}
1521da14cebeSEric Cheng 	return (ENOTSUP);
15224b46d1efSkrgopi }
15234b46d1efSkrgopi 
1524da14cebeSEric Cheng static int
dld_capab(dld_str_t * dsp,uint_t type,void * data,uint_t flags)1525da14cebeSEric Cheng dld_capab(dld_str_t *dsp, uint_t type, void *data, uint_t flags)
15264b46d1efSkrgopi {
1527da14cebeSEric Cheng 	int	err;
15284b46d1efSkrgopi 
15294b46d1efSkrgopi 	/*
1530da14cebeSEric Cheng 	 * Don't enable direct callback capabilities unless the caller is
1531da14cebeSEric Cheng 	 * the IP client. When a module is inserted in a stream (_I_INSERT)
1532da14cebeSEric Cheng 	 * the stack initiates capability disable, but due to races, the
1533da14cebeSEric Cheng 	 * module insertion may complete before the capability disable
1534da14cebeSEric Cheng 	 * completes. So we limit the check to DLD_ENABLE case.
15354b46d1efSkrgopi 	 */
1536da14cebeSEric Cheng 	if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) &&
153762366fbbSRobert Mustacchi 	    (!(dsp->ds_sap == ETHERTYPE_IP || dsp->ds_sap == ETHERTYPE_IPV6) ||
15388d4cf8d8S 	    !check_mod_above(dsp->ds_rq, "ip"))) {
1539da14cebeSEric Cheng 		return (ENOTSUP);
15404b46d1efSkrgopi 	}
15414b46d1efSkrgopi 
1542da14cebeSEric Cheng 	switch (type) {
1543da14cebeSEric Cheng 	case DLD_CAPAB_DIRECT:
154462366fbbSRobert Mustacchi 		if (dsp->ds_sap == ETHERTYPE_IPV6) {
154562366fbbSRobert Mustacchi 			err = ENOTSUP;
154662366fbbSRobert Mustacchi 			break;
154762366fbbSRobert Mustacchi 		}
1548da14cebeSEric Cheng 		err = dld_capab_direct(dsp, data, flags);
1549da14cebeSEric Cheng 		break;
15504b46d1efSkrgopi 
1551da14cebeSEric Cheng 	case DLD_CAPAB_POLL:
155262366fbbSRobert Mustacchi 		if (dsp->ds_sap == ETHERTYPE_IPV6) {
155362366fbbSRobert Mustacchi 			err = ENOTSUP;
155462366fbbSRobert Mustacchi 			break;
155562366fbbSRobert Mustacchi 		}
1556da14cebeSEric Cheng 		err =  dld_capab_poll(dsp, data, flags);
1557da14cebeSEric Cheng 		break;
1558da14cebeSEric Cheng 
1559da14cebeSEric Cheng 	case DLD_CAPAB_PERIM:
1560da14cebeSEric Cheng 		err = dld_capab_perim(dsp, data, flags);
1561da14cebeSEric Cheng 		break;
1562da14cebeSEric Cheng 
1563da14cebeSEric Cheng 	case DLD_CAPAB_LSO:
1564da14cebeSEric Cheng 		err = dld_capab_lso(dsp, data, flags);
1565da14cebeSEric Cheng 		break;
1566da14cebeSEric Cheng 
1567da14cebeSEric Cheng 	default:
1568da14cebeSEric Cheng 		err = ENOTSUP;
1569da14cebeSEric Cheng 		break;
15704b46d1efSkrgopi 	}
1571da14cebeSEric Cheng 
1572da14cebeSEric Cheng 	return (err);
15734b46d1efSkrgopi }
15744b46d1efSkrgopi 
15757c478bd9Sstevel@tonic-gate /*
15767c478bd9Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
15777c478bd9Sstevel@tonic-gate  */
1578da14cebeSEric Cheng static void
proto_capability_advertise(dld_str_t * dsp,mblk_t * mp)1579210db224Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
15807c478bd9Sstevel@tonic-gate {
15817c478bd9Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
15827c478bd9Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
15837c478bd9Sstevel@tonic-gate 	size_t			subsize;
1584da14cebeSEric Cheng 	dl_capab_dld_t		dld;
15857c478bd9Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
15867c478bd9Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
15871cb875aeSCathy Zhou 	dl_capab_vrrp_t		vrrp;
15881cb875aeSCathy Zhou 	mac_capab_vrrp_t	vrrp_capab;
15897c478bd9Sstevel@tonic-gate 	uint8_t			*ptr;
1590210db224Sericheng 	queue_t			*q = dsp->ds_wq;
1591210db224Sericheng 	mblk_t			*mp1;
1592d62bc4baSyz147064 	boolean_t		hcksum_capable = B_FALSE;
1593d62bc4baSyz147064 	boolean_t		zcopy_capable = B_FALSE;
1594da14cebeSEric Cheng 	boolean_t		dld_capable = B_FALSE;
15951cb875aeSCathy Zhou 	boolean_t		vrrp_capable = B_FALSE;
15967c478bd9Sstevel@tonic-gate 
15977c478bd9Sstevel@tonic-gate 	/*
15987c478bd9Sstevel@tonic-gate 	 * Initially assume no capabilities.
15997c478bd9Sstevel@tonic-gate 	 */
16007c478bd9Sstevel@tonic-gate 	subsize = 0;
16017c478bd9Sstevel@tonic-gate 
16027c478bd9Sstevel@tonic-gate 	/*
16039056fcebSCathy Zhou 	 * Check if checksum offload is supported on this MAC.
16047c478bd9Sstevel@tonic-gate 	 */
1605d62bc4baSyz147064 	bzero(&hcksum, sizeof (dl_capab_hcksum_t));
16069056fcebSCathy Zhou 	if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM,
1607ba2e4443Sseb 	    &hcksum.hcksum_txflags)) {
1608d62bc4baSyz147064 		if (hcksum.hcksum_txflags != 0) {
1609d62bc4baSyz147064 			hcksum_capable = B_TRUE;
16107c478bd9Sstevel@tonic-gate 			subsize += sizeof (dl_capability_sub_t) +
16117c478bd9Sstevel@tonic-gate 			    sizeof (dl_capab_hcksum_t);
16127c478bd9Sstevel@tonic-gate 		}
1613d62bc4baSyz147064 	}
16147c478bd9Sstevel@tonic-gate 
16157c478bd9Sstevel@tonic-gate 	/*
1616d62bc4baSyz147064 	 * Check if zerocopy is supported on this interface.
1617d62bc4baSyz147064 	 * If advertising DL_CAPAB_ZEROCOPY has not been explicitly disabled
1618d62bc4baSyz147064 	 * then reserve space for that capability.
16197c478bd9Sstevel@tonic-gate 	 */
1620d62bc4baSyz147064 	if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_ZCOPY, NULL) &&
1621d62bc4baSyz147064 	    !(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
1622d62bc4baSyz147064 		zcopy_capable = B_TRUE;
16237c478bd9Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
16247c478bd9Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
16257c478bd9Sstevel@tonic-gate 	}
16267c478bd9Sstevel@tonic-gate 
16277c478bd9Sstevel@tonic-gate 	/*
1628da14cebeSEric Cheng 	 * Direct capability negotiation interface between IP and DLD
1629da14cebeSEric Cheng 	 */
163062366fbbSRobert Mustacchi 	if ((dsp->ds_sap == ETHERTYPE_IP || dsp->ds_sap == ETHERTYPE_IPV6) &&
163162366fbbSRobert Mustacchi 	    check_mod_above(dsp->ds_rq, "ip")) {
1632da14cebeSEric Cheng 		dld_capable = B_TRUE;
1633da14cebeSEric Cheng 		subsize += sizeof (dl_capability_sub_t) +
1634da14cebeSEric Cheng 		    sizeof (dl_capab_dld_t);
1635da14cebeSEric Cheng 	}
1636da14cebeSEric Cheng 
1637da14cebeSEric Cheng 	/*
16381cb875aeSCathy Zhou 	 * Check if vrrp is supported on this interface. If so, reserve
16391cb875aeSCathy Zhou 	 * space for that capability.
16401cb875aeSCathy Zhou 	 */
16411cb875aeSCathy Zhou 	if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_VRRP, &vrrp_capab)) {
16421cb875aeSCathy Zhou 		vrrp_capable = B_TRUE;
16431cb875aeSCathy Zhou 		subsize += sizeof (dl_capability_sub_t) +
16441cb875aeSCathy Zhou 		    sizeof (dl_capab_vrrp_t);
16451cb875aeSCathy Zhou 	}
16461cb875aeSCathy Zhou 
16471cb875aeSCathy Zhou 	/*
1648210db224Sericheng 	 * If there are no capabilities to advertise or if we
1649210db224Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
16507c478bd9Sstevel@tonic-gate 	 */
16514b46d1efSkrgopi 	if ((mp1 = reallocb(mp,
1652210db224Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1653210db224Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1654da14cebeSEric Cheng 		return;
16557c478bd9Sstevel@tonic-gate 	}
16567c478bd9Sstevel@tonic-gate 
1657210db224Sericheng 	mp = mp1;
1658210db224Sericheng 	DB_TYPE(mp) = M_PROTO;
1659210db224Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1660210db224Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
16617c478bd9Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
16627c478bd9Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
16637c478bd9Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
16647c478bd9Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
16657c478bd9Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
16667c478bd9Sstevel@tonic-gate 
16677c478bd9Sstevel@tonic-gate 	/*
16687c478bd9Sstevel@tonic-gate 	 * TCP/IP checksum offload.
16697c478bd9Sstevel@tonic-gate 	 */
1670d62bc4baSyz147064 	if (hcksum_capable) {
16717c478bd9Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
16747c478bd9Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
16757c478bd9Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
16767c478bd9Sstevel@tonic-gate 
16777c478bd9Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
16787c478bd9Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
16797c478bd9Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
16807c478bd9Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
16817c478bd9Sstevel@tonic-gate 	}
16827c478bd9Sstevel@tonic-gate 
16837c478bd9Sstevel@tonic-gate 	/*
16847c478bd9Sstevel@tonic-gate 	 * Zero copy
16857c478bd9Sstevel@tonic-gate 	 */
1686d62bc4baSyz147064 	if (zcopy_capable) {
16877c478bd9Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
16887c478bd9Sstevel@tonic-gate 
16897c478bd9Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
16907c478bd9Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
16917c478bd9Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
16927c478bd9Sstevel@tonic-gate 
16937c478bd9Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
16947c478bd9Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
16957c478bd9Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
16967c478bd9Sstevel@tonic-gate 
16977c478bd9Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
16987c478bd9Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
16997c478bd9Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
17007c478bd9Sstevel@tonic-gate 	}
17017c478bd9Sstevel@tonic-gate 
1702da14cebeSEric Cheng 	/*
17031cb875aeSCathy Zhou 	 * VRRP capability negotiation
17041cb875aeSCathy Zhou 	 */
17051cb875aeSCathy Zhou 	if (vrrp_capable) {
17061cb875aeSCathy Zhou 		dlsp = (dl_capability_sub_t *)ptr;
17071cb875aeSCathy Zhou 		dlsp->dl_cap = DL_CAPAB_VRRP;
17081cb875aeSCathy Zhou 		dlsp->dl_length = sizeof (dl_capab_vrrp_t);
17091cb875aeSCathy Zhou 		ptr += sizeof (dl_capability_sub_t);
17101cb875aeSCathy Zhou 
17111cb875aeSCathy Zhou 		bzero(&vrrp, sizeof (dl_capab_vrrp_t));
17121cb875aeSCathy Zhou 		vrrp.vrrp_af = vrrp_capab.mcv_af;
17131cb875aeSCathy Zhou 		bcopy(&vrrp, ptr, sizeof (dl_capab_vrrp_t));
17141cb875aeSCathy Zhou 		ptr += sizeof (dl_capab_vrrp_t);
17151cb875aeSCathy Zhou 	}
17161cb875aeSCathy Zhou 
17171cb875aeSCathy Zhou 	/*
1718da14cebeSEric Cheng 	 * Direct capability negotiation interface between IP and DLD.
1719da14cebeSEric Cheng 	 * Refer to dld.h for details.
1720da14cebeSEric Cheng 	 */
1721da14cebeSEric Cheng 	if (dld_capable) {
1722da14cebeSEric Cheng 		dlsp = (dl_capability_sub_t *)ptr;
1723da14cebeSEric Cheng 		dlsp->dl_cap = DL_CAPAB_DLD;
1724da14cebeSEric Cheng 		dlsp->dl_length = sizeof (dl_capab_dld_t);
1725da14cebeSEric Cheng 		ptr += sizeof (dl_capability_sub_t);
17267c478bd9Sstevel@tonic-gate 
1727da14cebeSEric Cheng 		bzero(&dld, sizeof (dl_capab_dld_t));
1728da14cebeSEric Cheng 		dld.dld_version = DLD_CURRENT_VERSION;
1729da14cebeSEric Cheng 		dld.dld_capab = (uintptr_t)dld_capab;
1730da14cebeSEric Cheng 		dld.dld_capab_handle = (uintptr_t)dsp;
1731da14cebeSEric Cheng 
1732da14cebeSEric Cheng 		dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq);
1733da14cebeSEric Cheng 		bcopy(&dld, ptr, sizeof (dl_capab_dld_t));
1734da14cebeSEric Cheng 		ptr += sizeof (dl_capab_dld_t);
1735da14cebeSEric Cheng 	}
1736da14cebeSEric Cheng 
1737da14cebeSEric Cheng 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1738210db224Sericheng 	qreply(q, mp);
17398fb46f24Syz147064 }
17408fb46f24Syz147064 
17418fb46f24Syz147064 /*
17428fb46f24Syz147064  * Disable any enabled capabilities.
17438fb46f24Syz147064  */
17448fb46f24Syz147064 void
dld_capabilities_disable(dld_str_t * dsp)17458fb46f24Syz147064 dld_capabilities_disable(dld_str_t *dsp)
17468fb46f24Syz147064 {
17478fb46f24Syz147064 	if (dsp->ds_polling)
1748da14cebeSEric Cheng 		(void) dld_capab_poll_disable(dsp, NULL);
17497c478bd9Sstevel@tonic-gate }
1750