xref: /onnv-gate/usr/src/uts/common/io/mac/mac_ndd.c (revision 12169:b5c6274610de)
16512Ssowmini /*
26512Ssowmini  * CDDL HEADER START
36512Ssowmini  *
46512Ssowmini  * The contents of this file are subject to the terms of the
56512Ssowmini  * Common Development and Distribution License (the "License").
66512Ssowmini  * You may not use this file except in compliance with the License.
76512Ssowmini  *
86512Ssowmini  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96512Ssowmini  * or http://www.opensolaris.org/os/licensing.
106512Ssowmini  * See the License for the specific language governing permissions
116512Ssowmini  * and limitations under the License.
126512Ssowmini  *
136512Ssowmini  * When distributing Covered Code, include this CDDL HEADER in each
146512Ssowmini  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156512Ssowmini  * If applicable, add the following below this CDDL HEADER, with the
166512Ssowmini  * fields enclosed by brackets "[]" replaced with your own identifying
176512Ssowmini  * information: Portions Copyright [yyyy] [name of copyright owner]
186512Ssowmini  *
196512Ssowmini  * CDDL HEADER END
206512Ssowmini  */
216512Ssowmini /*
22*12169SPrakash.Jalan@Sun.COM  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
236512Ssowmini  */
246512Ssowmini 
256512Ssowmini /*
266512Ssowmini  * functions to handle legacy ndd  ioctls
276512Ssowmini  */
286512Ssowmini #include <sys/types.h>
296512Ssowmini #include <sys/mac.h>
306512Ssowmini #include <sys/mac_impl.h>
3111878SVenu.Iyer@Sun.COM #include <sys/mac_client_priv.h>
326512Ssowmini #include <inet/nd.h>
336512Ssowmini #include <sys/mac_ether.h>
346512Ssowmini #include <sys/policy.h>
356512Ssowmini #include <sys/strsun.h>
366512Ssowmini 
376512Ssowmini static int mac_ndd_set_ioctl(mac_impl_t *, mblk_t *, int, int *);
386512Ssowmini static int mac_ndd_get_ioctl(mac_impl_t *, mblk_t *, int, int *);
396512Ssowmini static int mac_ndd_get_names(mac_impl_t *, mblk_t *);
406512Ssowmini static boolean_t mac_add_name(mblk_t *, char *, int);
416512Ssowmini 
426512Ssowmini /*
436512Ssowmini  * add "<name> (<rwtag>) " into the mblk, allocating more memory if needed.
446512Ssowmini  */
456512Ssowmini static boolean_t
mac_add_name(mblk_t * mp,char * name,int ndd_flags)466512Ssowmini mac_add_name(mblk_t *mp, char *name, int ndd_flags)
476512Ssowmini {
486512Ssowmini 	char *cp, *rwtag;
496512Ssowmini 	int len, flags;
506512Ssowmini 
516512Ssowmini 	flags = (ndd_flags & (MAC_PROP_PERM_WRITE|MAC_PROP_PERM_READ));
526512Ssowmini 	switch (flags) {
536512Ssowmini 	case 0:
546512Ssowmini 		rwtag = "no read or write";
556512Ssowmini 		break;
566512Ssowmini 	case MAC_PROP_PERM_WRITE:
576512Ssowmini 		rwtag = "write only";
586512Ssowmini 		break;
596512Ssowmini 	case MAC_PROP_PERM_READ:
606512Ssowmini 		rwtag = "read only";
616512Ssowmini 		break;
626512Ssowmini 	default:
636512Ssowmini 		rwtag = "read and write";
646512Ssowmini 		break;
656512Ssowmini 	}
666512Ssowmini 
676512Ssowmini 	while (mp->b_cont != NULL)
686512Ssowmini 		mp = mp->b_cont;
696512Ssowmini 	/*
706512Ssowmini 	 * allocate space for name, <space>, '(', rwtag, ')', and
716512Ssowmini 	 * two terminating null chars.
726512Ssowmini 	 */
736512Ssowmini 	len = strlen(name) + strlen(rwtag) + 6;
746512Ssowmini 	if (mp->b_wptr + len >= mp->b_datap->db_lim) {
756512Ssowmini 		mp->b_cont = allocb(len, BPRI_HI);
766512Ssowmini 		mp = mp->b_cont;
77*12169SPrakash.Jalan@Sun.COM 		if (mp == NULL)
786512Ssowmini 			return (B_FALSE);
796512Ssowmini 	}
806512Ssowmini 	cp = (char *)mp->b_wptr;
816512Ssowmini 	(void) snprintf(cp, len, "%s (%s)", name, rwtag);
826512Ssowmini 	mp->b_wptr += strnlen(cp, len);
836512Ssowmini 	mp->b_wptr++; /* skip past the terminating \0 */
846512Ssowmini 	return (B_TRUE);
856512Ssowmini }
866512Ssowmini 
876512Ssowmini 
886512Ssowmini /*
896512Ssowmini  * handle a query for "ndd -get \?". The result is put into mp, and
906512Ssowmini  * more memory is allocated if needed. The resulting size of the data
916512Ssowmini  * is returned.
926512Ssowmini  */
936512Ssowmini static int
mac_ndd_get_names(mac_impl_t * mip,mblk_t * mp)946512Ssowmini mac_ndd_get_names(mac_impl_t *mip, mblk_t *mp)
956512Ssowmini {
966512Ssowmini 	int size_out, i;
976512Ssowmini 	mblk_t *tmp;
988118SVasumathi.Sundaram@Sun.COM 	uint_t permflags;
998118SVasumathi.Sundaram@Sun.COM 	int status;
1008118SVasumathi.Sundaram@Sun.COM 	uint64_t value;
10111878SVenu.Iyer@Sun.COM 	char *prop_name;
1026512Ssowmini 
1036512Ssowmini 	if (!mac_add_name(mp, "?", MAC_PROP_PERM_READ))
1046512Ssowmini 		return (-1);
1056512Ssowmini 
1066512Ssowmini 	/* first the known ndd mappings */
1076512Ssowmini 	for (i = 0; i < mip->mi_type->mt_mappingcount; i++) {
1088118SVasumathi.Sundaram@Sun.COM 		if ((mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_MAP_KSTAT)
1098118SVasumathi.Sundaram@Sun.COM 		    != 0)
1108118SVasumathi.Sundaram@Sun.COM 			permflags = MAC_PROP_PERM_READ;
1118118SVasumathi.Sundaram@Sun.COM 		else {
1128118SVasumathi.Sundaram@Sun.COM 			status = mip->mi_callbacks->mc_getprop(mip->mi_driver,
1138118SVasumathi.Sundaram@Sun.COM 			    mip->mi_type->mt_mapping[i].mp_name,
1148118SVasumathi.Sundaram@Sun.COM 			    mip->mi_type->mt_mapping[i].mp_prop_id,
11511878SVenu.Iyer@Sun.COM 			    mip->mi_type->mt_mapping[i].mp_valsize, &value);
11611878SVenu.Iyer@Sun.COM 			if (status != 0)
11711878SVenu.Iyer@Sun.COM 				continue;
11811878SVenu.Iyer@Sun.COM 			status = mac_prop_info((mac_handle_t)mip,
11911878SVenu.Iyer@Sun.COM 			    mip->mi_type->mt_mapping[i].mp_prop_id,
12011878SVenu.Iyer@Sun.COM 			    mip->mi_type->mt_mapping[i].mp_name, NULL, 0,
12111878SVenu.Iyer@Sun.COM 			    NULL, &permflags);
1228118SVasumathi.Sundaram@Sun.COM 			if (status != 0)
1239623SVasumathi.Sundaram@Sun.COM 				continue;
1248118SVasumathi.Sundaram@Sun.COM 		}
1256512Ssowmini 		if (!mac_add_name(mp, mip->mi_type->mt_mapping[i].mp_name,
1268118SVasumathi.Sundaram@Sun.COM 		    permflags))
1276512Ssowmini 			return (-1);
1286512Ssowmini 	}
1296512Ssowmini 
1306512Ssowmini 	/* now the driver's ndd variables */
1316512Ssowmini 	for (i = 0; i < mip->mi_priv_prop_count; i++) {
1326512Ssowmini 
13311878SVenu.Iyer@Sun.COM 		prop_name = mip->mi_priv_prop[i];
13411878SVenu.Iyer@Sun.COM 
13511878SVenu.Iyer@Sun.COM 		if (mac_prop_info((mac_handle_t)mip, MAC_PROP_PRIVATE,
13611878SVenu.Iyer@Sun.COM 		    prop_name, NULL, 0, NULL, &permflags) != 0)
13711878SVenu.Iyer@Sun.COM 			return (-1);
1386512Ssowmini 
1396512Ssowmini 		/* skip over the "_" */
14011878SVenu.Iyer@Sun.COM 		if (!mac_add_name(mp, &prop_name[1], permflags))
1416512Ssowmini 			return (-1);
1426512Ssowmini 	}
1436512Ssowmini 
1446512Ssowmini 	tmp = mp;
1456512Ssowmini 	while (tmp->b_cont != NULL)
1466512Ssowmini 		tmp = tmp->b_cont;
1476512Ssowmini 	*tmp->b_wptr++ = '\0';
1486512Ssowmini 	size_out = msgdsize(mp);
1496512Ssowmini 	return (size_out);
1506512Ssowmini }
1516512Ssowmini 
1526512Ssowmini 
1536512Ssowmini /*
1546512Ssowmini  * Handle legacy ndd ioctls for ND_GET and ND_SET.
1556512Ssowmini  */
1566512Ssowmini void
mac_ndd_ioctl(mac_impl_t * mip,queue_t * wq,mblk_t * mp)1576512Ssowmini mac_ndd_ioctl(mac_impl_t *mip, queue_t *wq, mblk_t *mp)
1586512Ssowmini {
1596512Ssowmini 	IOCP    iocp;
1606512Ssowmini 	int	cmd, err, rval;
1616512Ssowmini 
1626512Ssowmini 	iocp = (IOCP)mp->b_rptr;
1636512Ssowmini 	if (iocp->ioc_count == 0 || mp->b_cont == NULL) {
1646512Ssowmini 		err = EINVAL;
1656512Ssowmini 		goto done;
1666512Ssowmini 	}
1676512Ssowmini 
1686512Ssowmini 	cmd = iocp->ioc_cmd;
1696512Ssowmini 
1706512Ssowmini 	if (cmd == ND_SET) {
1716512Ssowmini 		err = mac_ndd_set_ioctl(mip, mp, iocp->ioc_count, &rval);
1726512Ssowmini 	} else if (cmd == ND_GET) {
1736512Ssowmini 		err = mac_ndd_get_ioctl(mip, mp, iocp->ioc_count, &rval);
1746512Ssowmini 	}
1756512Ssowmini done:
1766512Ssowmini 	if (err == 0)
1776512Ssowmini 		miocack(wq, mp, msgdsize(mp->b_cont), rval);
1786512Ssowmini 	else
1796512Ssowmini 		miocnak(wq, mp, 0, err);
1806512Ssowmini }
1816512Ssowmini 
1826512Ssowmini static int
mac_ndd_get_ioctl(mac_impl_t * mip,mblk_t * mp,int avail,int * rval)1836512Ssowmini mac_ndd_get_ioctl(mac_impl_t *mip, mblk_t *mp, int avail, int *rval)
1846512Ssowmini {
1856512Ssowmini 	mblk_t		*mp1;
1866512Ssowmini 	char		*valp;
1876512Ssowmini 	uchar_t 	*value;
1886512Ssowmini 	uint32_t	new_value;
189*12169SPrakash.Jalan@Sun.COM 	int		size_out = 0, i;
1906512Ssowmini 	int		status = EINVAL;
1916512Ssowmini 	char		*name, priv_name[MAXLINKPROPNAME];
1926512Ssowmini 	uint8_t		u8;
1936512Ssowmini 	uint16_t	u16;
1946512Ssowmini 	uint32_t	u32;
1956512Ssowmini 	uint64_t	u64;
1966512Ssowmini 
1976512Ssowmini 	if (mp->b_cont == NULL || avail < 2)
1986512Ssowmini 		return (EINVAL);
1996512Ssowmini 	valp = (char *)mp->b_cont->b_rptr;
2006512Ssowmini 	mp1 = allocb(avail, BPRI_HI); /* the returned buffer */
2016512Ssowmini 	if (mp1 == NULL)
2026512Ssowmini 		return (ENOMEM);
2036512Ssowmini 
2046512Ssowmini 	if (strcmp(valp, "?") == 0) {
2056512Ssowmini 		/*
2066512Ssowmini 		 * handle "ndd -get <..> \?" queries.
2076512Ssowmini 		 */
2086512Ssowmini 		size_out = mac_ndd_get_names(mip, mp1);
2096512Ssowmini 		if (size_out < 0) {
2106512Ssowmini 			status = ENOMEM;
2116512Ssowmini 			goto get_done;
2126512Ssowmini 		}
2136512Ssowmini 		if (size_out > avail) {
2146512Ssowmini 			int excess;
2156512Ssowmini 			char *cp;
2166512Ssowmini 			/*
2176512Ssowmini 			 * need more user buffer space. Return as many
2186512Ssowmini 			 * mblks as will fit and return the needed
2196512Ssowmini 			 * buffer size in ioc_rval.
2206512Ssowmini 			 */
2216512Ssowmini 			excess = size_out - avail;
2226512Ssowmini 			*rval = size_out; /* what's needed */
2236512Ssowmini 			size_out -= excess;
2246512Ssowmini 			(void) adjmsg(mp1, -(excess + 1));
2256512Ssowmini 			cp = (char *)mp1->b_wptr;
2266512Ssowmini 			*cp = '\0';
2276512Ssowmini 		}
2286512Ssowmini 		status = 0;
2296512Ssowmini 		goto get_done;
2306512Ssowmini 	}
2316512Ssowmini 
2326512Ssowmini 	ASSERT(mip->mi_callbacks->mc_callbacks & MC_GETPROP);
2336512Ssowmini 	name = valp;
2346512Ssowmini 	valp = (char *)mp1->b_rptr;
2356512Ssowmini 	mp1->b_wptr = mp1->b_rptr;
2366512Ssowmini 
2376512Ssowmini 	/* first lookup ndd <-> public property mapping */
2386512Ssowmini 	for (i = 0; i < mip->mi_type->mt_mappingcount; i++) {
2396512Ssowmini 		if (strcmp(name, mip->mi_type->mt_mapping[i].mp_name) != 0)
2406512Ssowmini 			continue;
2416512Ssowmini 
2426512Ssowmini 		switch (mip->mi_type->mt_mapping[i].mp_valsize) {
2436512Ssowmini 		case 1:
2446512Ssowmini 			value = (uchar_t *)&u8;
2456512Ssowmini 			break;
2466512Ssowmini 		case 2:
2476512Ssowmini 			value = (uchar_t *)&u16;
2486512Ssowmini 			break;
2496512Ssowmini 		case 4:
2506512Ssowmini 			value = (uchar_t *)&u32;
2516512Ssowmini 			break;
2526512Ssowmini 		default:
2536512Ssowmini 			value = (uchar_t *)&u64;
2546512Ssowmini 			break;
2556512Ssowmini 		}
2566512Ssowmini 
2576512Ssowmini 		if ((mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_MAP_KSTAT)
2586512Ssowmini 		    != 0) {
2596512Ssowmini 			u64 = mac_stat_get((mac_handle_t)mip,
2606512Ssowmini 			    mip->mi_type->mt_mapping[i].mp_kstat);
2616512Ssowmini 			status = 0;
2626512Ssowmini 			/*
2636512Ssowmini 			 * ether_stats are all always KSTAT_DATA_UINT32
2646512Ssowmini 			 */
2656512Ssowmini 			new_value = u32 = (long)u64;
2666512Ssowmini 		} else {
2676512Ssowmini 			status = mip->mi_callbacks->mc_getprop(mip->mi_driver,
26811878SVenu.Iyer@Sun.COM 			    name, mip->mi_type->mt_mapping[i].mp_prop_id,
26911878SVenu.Iyer@Sun.COM 			    mip->mi_type->mt_mapping[i].mp_valsize, value);
2706512Ssowmini 			switch (mip->mi_type->mt_mapping[i].mp_valsize) {
2716512Ssowmini 			case 1:
2726512Ssowmini 				new_value = u8;
2736512Ssowmini 				break;
2746512Ssowmini 			case 2:
2756512Ssowmini 				new_value = u16;
2766512Ssowmini 				break;
2776512Ssowmini 			case 4:
2786512Ssowmini 				new_value = u32;
2796512Ssowmini 				break;
2806512Ssowmini 			case 8:
2816512Ssowmini 				/*
2826512Ssowmini 				 * The only uint64_t is for speed, which is
2836512Ssowmini 				 * converted to Mbps in ndd reports.
2846512Ssowmini 				 */
2856512Ssowmini 				new_value = (u64/1000000);
2866512Ssowmini 				break;
2876512Ssowmini 			}
2886512Ssowmini 		}
2896512Ssowmini 
2906512Ssowmini 		if (status != 0)
2916512Ssowmini 			goto get_done;
2926512Ssowmini 
2936512Ssowmini 		(void) snprintf(valp, avail, "%d", new_value);
2946512Ssowmini 		goto update_reply;
2956512Ssowmini 	}
2966512Ssowmini 
2976512Ssowmini 	/*
2986512Ssowmini 	 * could not find a public property. try the private prop route
2996512Ssowmini 	 * where all string processing will be done by the driver.
3006512Ssowmini 	 */
3016512Ssowmini 	(void) snprintf(priv_name, sizeof (priv_name), "_%s", name);
3026512Ssowmini 	status = mip->mi_callbacks->mc_getprop(mip->mi_driver, priv_name,
30311878SVenu.Iyer@Sun.COM 	    MAC_PROP_PRIVATE, avail - 2, mp1->b_rptr);
3046512Ssowmini 	if (status != 0)
3056512Ssowmini 		goto get_done;
3066512Ssowmini 
3076512Ssowmini update_reply:
3086512Ssowmini 	size_out += strnlen((const char *)mp1->b_rptr, avail);
3096512Ssowmini 	valp += size_out;
3106512Ssowmini 	*valp++ = '\0'; /* need \0\0 */
3116512Ssowmini 	*valp++ = '\0';
3126512Ssowmini 	mp1->b_wptr = (uchar_t *)valp;
3136512Ssowmini 	*rval = 0;
3146512Ssowmini 
3156512Ssowmini get_done:
3166512Ssowmini 	freemsg(mp->b_cont);
3176512Ssowmini 	if (status == 0)
3186512Ssowmini 		mp->b_cont = mp1;
3196512Ssowmini 	else {
3206512Ssowmini 		freemsg(mp1);
3216512Ssowmini 		mp->b_cont = NULL;
3226512Ssowmini 	}
3236512Ssowmini 	return (status);
3246512Ssowmini }
3256512Ssowmini 
3266512Ssowmini static int
mac_ndd_set_ioctl(mac_impl_t * mip,mblk_t * mp,int avail,int * rval)3276512Ssowmini mac_ndd_set_ioctl(mac_impl_t *mip, mblk_t *mp, int avail, int *rval)
3286512Ssowmini {
3296512Ssowmini 	mblk_t  	*mp1;
3306512Ssowmini 	char		*valp, *name, *new_valuep;
3316512Ssowmini 	uchar_t 	*vp;
3326512Ssowmini 	long		new_value;
3336512Ssowmini 	int		status, i;
3346512Ssowmini 	uint8_t		u8;
3356512Ssowmini 	uint16_t	u16;
3366512Ssowmini 	uint32_t	u32;
3376512Ssowmini 	IOCP		iocp;
3386512Ssowmini 	char		priv_name[MAXLINKPROPNAME];
3396512Ssowmini 
3406512Ssowmini 	if (avail == 0 || !(mp1 = mp->b_cont))
3416512Ssowmini 		return (EINVAL);
3426512Ssowmini 
3436512Ssowmini 	if (mp1->b_cont) {
3446512Ssowmini 		freemsg(mp1->b_cont);
3456512Ssowmini 		mp1->b_cont = NULL;
3466512Ssowmini 	}
3476512Ssowmini 	mp1->b_datap->db_lim[-1] = '\0';
3486512Ssowmini 	valp = (char *)mp1->b_rptr;
3496512Ssowmini 	name = valp;
3506512Ssowmini 	*rval = 0;
3516512Ssowmini 	while (*valp++)
3526512Ssowmini 		;
3536512Ssowmini 	if (valp >= (char *)mp1->b_wptr)
3546512Ssowmini 		valp = NULL;
3556512Ssowmini 
3566512Ssowmini 	new_valuep = valp;
3576512Ssowmini 	if (ddi_strtol(valp, NULL, 0, &new_value) != 0)
3586512Ssowmini 		goto priv_prop;
3596512Ssowmini 
3606512Ssowmini 	iocp = (IOCP)mp->b_rptr;
3616512Ssowmini 	if (valp != NULL &&
3626512Ssowmini 	    ((iocp->ioc_cr == NULL) ||
3636512Ssowmini 	    ((status = secpolicy_net_config(iocp->ioc_cr, B_FALSE)) != 0)))
3646512Ssowmini 		return (status);
3656512Ssowmini 
3666512Ssowmini 	status = EINVAL;
3676512Ssowmini 
3686512Ssowmini 	/* first lookup ndd <-> public property mapping */
3696512Ssowmini 	for (i = 0; i < mip->mi_type->mt_mappingcount; i++) {
3706512Ssowmini 		if (strcmp(name, mip->mi_type->mt_mapping[i].mp_name) != 0)
3716512Ssowmini 			continue;
3726512Ssowmini 
3736512Ssowmini 		if (mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_MAP_KSTAT)
3746512Ssowmini 			return (EINVAL);
3756512Ssowmini 
3766512Ssowmini 		if (new_value > mip->mi_type->mt_mapping[i].mp_maxval ||
3776512Ssowmini 		    new_value < mip->mi_type->mt_mapping[i].mp_minval ||
3786512Ssowmini 		    (mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_PERM_WRITE)
3796512Ssowmini 		    == 0)
3806512Ssowmini 			return (EINVAL);
3816512Ssowmini 		switch (mip->mi_type->mt_mapping[i].mp_valsize) {
3826512Ssowmini 		case 1:
3836512Ssowmini 			u8 = (uint8_t)new_value;
3846512Ssowmini 			vp = (uchar_t *)&u8;
3856512Ssowmini 			break;
3866512Ssowmini 		case 2:
3876512Ssowmini 			u16 = (uint16_t)new_value;
3886512Ssowmini 			vp = (uchar_t *)&u16;
3896512Ssowmini 			break;
3906512Ssowmini 		case 4:
3916512Ssowmini 			u32 = (uint32_t)new_value;
3926512Ssowmini 			vp = (uchar_t *)&u32;
3936512Ssowmini 			break;
3946512Ssowmini 		case 8:
3956512Ssowmini 			vp = (uchar_t *)&new_value;
3966512Ssowmini 			break;
3976512Ssowmini 		default:
3986512Ssowmini 			return (ENOTSUP);
3996512Ssowmini 		}
4006512Ssowmini 
4016512Ssowmini 		status = mip->mi_callbacks->mc_setprop(mip->mi_driver,
4026512Ssowmini 		    name, mip->mi_type->mt_mapping[i].mp_prop_id,
4036512Ssowmini 		    mip->mi_type->mt_mapping[i].mp_valsize, (const void *)vp);
4046512Ssowmini 		goto done;
4056512Ssowmini 	}
4066512Ssowmini 
4076512Ssowmini priv_prop:
4086512Ssowmini 	(void) snprintf(priv_name, sizeof (priv_name), "_%s", name);
4096512Ssowmini 	status = mip->mi_callbacks->mc_setprop(mip->mi_driver, priv_name,
4106789Sam223141 	    MAC_PROP_PRIVATE, strlen(new_valuep), new_valuep);
4116512Ssowmini done:
4126512Ssowmini 	freemsg(mp1);
4136512Ssowmini 	mp->b_cont = NULL;
4146512Ssowmini 	return (status);
4156512Ssowmini }
416