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