xref: /illumos-gate/usr/src/uts/common/io/mac/mac_ndd.c (revision e9af4bc0b1cc30cea75d6ad4aa2fde97d985e9be)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * functions to handle legacy ndd  ioctls
28  */
29 #include <sys/types.h>
30 #include <sys/mac.h>
31 #include <sys/mac_impl.h>
32 #include <inet/nd.h>
33 #include <sys/mac_ether.h>
34 #include <sys/policy.h>
35 #include <sys/strsun.h>
36 
37 static int mac_ndd_set_ioctl(mac_impl_t *, mblk_t *, int, int *);
38 static int mac_ndd_get_ioctl(mac_impl_t *, mblk_t *, int, int *);
39 static int mac_ndd_get_names(mac_impl_t *, mblk_t *);
40 static boolean_t mac_add_name(mblk_t *, char *, int);
41 
42 /*
43  * add "<name> (<rwtag>) " into the mblk, allocating more memory if needed.
44  */
45 static boolean_t
46 mac_add_name(mblk_t *mp, char *name, int ndd_flags)
47 {
48 	char *cp, *rwtag;
49 	int len, flags;
50 
51 	flags = (ndd_flags & (MAC_PROP_PERM_WRITE|MAC_PROP_PERM_READ));
52 	switch (flags) {
53 	case 0:
54 		rwtag = "no read or write";
55 		break;
56 	case MAC_PROP_PERM_WRITE:
57 		rwtag = "write only";
58 		break;
59 	case MAC_PROP_PERM_READ:
60 		rwtag = "read only";
61 		break;
62 	default:
63 		rwtag = "read and write";
64 		break;
65 	}
66 
67 	while (mp->b_cont != NULL)
68 		mp = mp->b_cont;
69 	/*
70 	 * allocate space for name, <space>, '(', rwtag, ')', and
71 	 * two terminating null chars.
72 	 */
73 	len = strlen(name) + strlen(rwtag) + 6;
74 	if (mp->b_wptr + len >= mp->b_datap->db_lim) {
75 		mp->b_cont = allocb(len, BPRI_HI);
76 		mp = mp->b_cont;
77 		if (mp != NULL)
78 			return (B_FALSE);
79 	}
80 	cp = (char *)mp->b_wptr;
81 	(void) snprintf(cp, len, "%s (%s)", name, rwtag);
82 	mp->b_wptr += strnlen(cp, len);
83 	mp->b_wptr++; /* skip past the terminating \0 */
84 	return (B_TRUE);
85 }
86 
87 
88 /*
89  * handle a query for "ndd -get \?". The result is put into mp, and
90  * more memory is allocated if needed. The resulting size of the data
91  * is returned.
92  */
93 static int
94 mac_ndd_get_names(mac_impl_t *mip, mblk_t *mp)
95 {
96 	int size_out, i;
97 	mblk_t *tmp;
98 	mac_priv_prop_t *mpriv;
99 	uint_t permflags;
100 	int status;
101 	uint64_t value;
102 
103 	if (!mac_add_name(mp, "?", MAC_PROP_PERM_READ))
104 		return (-1);
105 
106 	/* first the known ndd mappings */
107 	for (i = 0; i < mip->mi_type->mt_mappingcount; i++) {
108 		permflags = MAC_PROP_PERM_RW;
109 		if ((mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_MAP_KSTAT)
110 		    != 0)
111 			permflags = MAC_PROP_PERM_READ;
112 		else {
113 			status = mip->mi_callbacks->mc_getprop(mip->mi_driver,
114 			    mip->mi_type->mt_mapping[i].mp_name,
115 			    mip->mi_type->mt_mapping[i].mp_prop_id,
116 			    0, mip->mi_type->mt_mapping[i].mp_valsize,
117 			    &value, &permflags);
118 			if (status != 0)
119 				continue;
120 		}
121 		if (!mac_add_name(mp, mip->mi_type->mt_mapping[i].mp_name,
122 		    permflags))
123 			return (-1);
124 	}
125 
126 	/* now the driver's ndd variables */
127 	for (i = 0; i < mip->mi_priv_prop_count; i++) {
128 
129 		mpriv = &mip->mi_priv_prop[i];
130 
131 		/* skip over the "_" */
132 		if (!mac_add_name(mp, &mpriv->mpp_name[1], mpriv->mpp_flags))
133 			return (-1);
134 	}
135 
136 	tmp = mp;
137 	while (tmp->b_cont != NULL)
138 		tmp = tmp->b_cont;
139 	*tmp->b_wptr++ = '\0';
140 	size_out = msgdsize(mp);
141 	return (size_out);
142 }
143 
144 
145 /*
146  * Handle legacy ndd ioctls for ND_GET and ND_SET.
147  */
148 void
149 mac_ndd_ioctl(mac_impl_t *mip, queue_t *wq, mblk_t *mp)
150 {
151 	IOCP    iocp;
152 	int	cmd, err, rval;
153 
154 	iocp = (IOCP)mp->b_rptr;
155 	if (iocp->ioc_count == 0 || mp->b_cont == NULL) {
156 		err = EINVAL;
157 		goto done;
158 	}
159 
160 	cmd = iocp->ioc_cmd;
161 
162 	if (cmd == ND_SET) {
163 		err = mac_ndd_set_ioctl(mip, mp, iocp->ioc_count, &rval);
164 	} else if (cmd == ND_GET) {
165 		err = mac_ndd_get_ioctl(mip, mp, iocp->ioc_count, &rval);
166 	}
167 done:
168 	if (err == 0)
169 		miocack(wq, mp, msgdsize(mp->b_cont), rval);
170 	else
171 		miocnak(wq, mp, 0, err);
172 }
173 
174 static int
175 mac_ndd_get_ioctl(mac_impl_t *mip, mblk_t *mp, int avail, int *rval)
176 {
177 	mblk_t		*mp1;
178 	char		*valp;
179 	uchar_t 	*value;
180 	uint32_t	new_value;
181 	int		size_out, i;
182 	int		status = EINVAL;
183 	char		*name, priv_name[MAXLINKPROPNAME];
184 	uint8_t		u8;
185 	uint16_t	u16;
186 	uint32_t	u32;
187 	uint64_t	u64;
188 	uint_t		perm;
189 
190 	if (mp->b_cont == NULL || avail < 2)
191 		return (EINVAL);
192 	valp = (char *)mp->b_cont->b_rptr;
193 	mp1 = allocb(avail, BPRI_HI); /* the returned buffer */
194 	if (mp1 == NULL)
195 		return (ENOMEM);
196 
197 	if (strcmp(valp, "?") == 0) {
198 		/*
199 		 * handle "ndd -get <..> \?" queries.
200 		 */
201 		size_out = mac_ndd_get_names(mip, mp1);
202 		if (size_out < 0) {
203 			status = ENOMEM;
204 			goto get_done;
205 		}
206 		if (size_out > avail) {
207 			int excess;
208 			char *cp;
209 			/*
210 			 * need more user buffer space. Return as many
211 			 * mblks as will fit and return the needed
212 			 * buffer size in ioc_rval.
213 			 */
214 			excess = size_out - avail;
215 			*rval = size_out; /* what's needed */
216 			size_out -= excess;
217 			(void) adjmsg(mp1, -(excess + 1));
218 			cp = (char *)mp1->b_wptr;
219 			*cp = '\0';
220 		}
221 		status = 0;
222 		goto get_done;
223 	}
224 
225 	ASSERT(mip->mi_callbacks->mc_callbacks & MC_GETPROP);
226 	name = valp;
227 	valp = (char *)mp1->b_rptr;
228 	mp1->b_wptr = mp1->b_rptr;
229 
230 	/* first lookup ndd <-> public property mapping */
231 	for (i = 0; i < mip->mi_type->mt_mappingcount; i++) {
232 		if (strcmp(name, mip->mi_type->mt_mapping[i].mp_name) != 0)
233 			continue;
234 
235 		switch (mip->mi_type->mt_mapping[i].mp_valsize) {
236 		case 1:
237 			value = (uchar_t *)&u8;
238 			break;
239 		case 2:
240 			value = (uchar_t *)&u16;
241 			break;
242 		case 4:
243 			value = (uchar_t *)&u32;
244 			break;
245 		default:
246 			value = (uchar_t *)&u64;
247 			break;
248 		}
249 
250 		if ((mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_MAP_KSTAT)
251 		    != 0) {
252 			u64 = mac_stat_get((mac_handle_t)mip,
253 			    mip->mi_type->mt_mapping[i].mp_kstat);
254 			status = 0;
255 			/*
256 			 * ether_stats are all always KSTAT_DATA_UINT32
257 			 */
258 			new_value = u32 = (long)u64;
259 		} else {
260 			status = mip->mi_callbacks->mc_getprop(mip->mi_driver,
261 			    name, mip->mi_type->mt_mapping[i].mp_prop_id, 0,
262 			    mip->mi_type->mt_mapping[i].mp_valsize, value,
263 			    &perm);
264 			switch (mip->mi_type->mt_mapping[i].mp_valsize) {
265 			case 1:
266 				new_value = u8;
267 				break;
268 			case 2:
269 				new_value = u16;
270 				break;
271 			case 4:
272 				new_value = u32;
273 				break;
274 			case 8:
275 				/*
276 				 * The only uint64_t is for speed, which is
277 				 * converted to Mbps in ndd reports.
278 				 */
279 				new_value = (u64/1000000);
280 				break;
281 			}
282 		}
283 
284 		if (status != 0)
285 			goto get_done;
286 
287 		(void) snprintf(valp, avail, "%d", new_value);
288 		goto update_reply;
289 	}
290 
291 	/*
292 	 * could not find a public property. try the private prop route
293 	 * where all string processing will be done by the driver.
294 	 */
295 	(void) snprintf(priv_name, sizeof (priv_name), "_%s", name);
296 	status = mip->mi_callbacks->mc_getprop(mip->mi_driver, priv_name,
297 	    MAC_PROP_PRIVATE, 0, avail - 2, mp1->b_rptr, &perm);
298 	if (status != 0)
299 		goto get_done;
300 
301 update_reply:
302 	size_out += strnlen((const char *)mp1->b_rptr, avail);
303 	valp += size_out;
304 	*valp++ = '\0'; /* need \0\0 */
305 	*valp++ = '\0';
306 	mp1->b_wptr = (uchar_t *)valp;
307 	*rval = 0;
308 
309 get_done:
310 	freemsg(mp->b_cont);
311 	if (status == 0)
312 		mp->b_cont = mp1;
313 	else {
314 		freemsg(mp1);
315 		mp->b_cont = NULL;
316 	}
317 	return (status);
318 }
319 
320 static int
321 mac_ndd_set_ioctl(mac_impl_t *mip, mblk_t *mp, int avail, int *rval)
322 {
323 	mblk_t  	*mp1;
324 	char		*valp, *name, *new_valuep;
325 	uchar_t 	*vp;
326 	long		new_value;
327 	int		status, i;
328 	uint8_t		u8;
329 	uint16_t	u16;
330 	uint32_t	u32;
331 	IOCP		iocp;
332 	char		priv_name[MAXLINKPROPNAME];
333 
334 	if (avail == 0 || !(mp1 = mp->b_cont))
335 		return (EINVAL);
336 
337 	if (mp1->b_cont) {
338 		freemsg(mp1->b_cont);
339 		mp1->b_cont = NULL;
340 	}
341 	mp1->b_datap->db_lim[-1] = '\0';
342 	valp = (char *)mp1->b_rptr;
343 	name = valp;
344 	*rval = 0;
345 	while (*valp++)
346 		;
347 	if (valp >= (char *)mp1->b_wptr)
348 		valp = NULL;
349 
350 	new_valuep = valp;
351 	if (ddi_strtol(valp, NULL, 0, &new_value) != 0)
352 		goto priv_prop;
353 
354 	iocp = (IOCP)mp->b_rptr;
355 	if (valp != NULL &&
356 	    ((iocp->ioc_cr == NULL) ||
357 	    ((status = secpolicy_net_config(iocp->ioc_cr, B_FALSE)) != 0)))
358 		return (status);
359 
360 	status = EINVAL;
361 
362 	/* first lookup ndd <-> public property mapping */
363 	for (i = 0; i < mip->mi_type->mt_mappingcount; i++) {
364 		if (strcmp(name, mip->mi_type->mt_mapping[i].mp_name) != 0)
365 			continue;
366 
367 		if (mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_MAP_KSTAT)
368 			return (EINVAL);
369 
370 		if (new_value > mip->mi_type->mt_mapping[i].mp_maxval ||
371 		    new_value < mip->mi_type->mt_mapping[i].mp_minval ||
372 		    (mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_PERM_WRITE)
373 		    == 0)
374 			return (EINVAL);
375 		switch (mip->mi_type->mt_mapping[i].mp_valsize) {
376 		case 1:
377 			u8 = (uint8_t)new_value;
378 			vp = (uchar_t *)&u8;
379 			break;
380 		case 2:
381 			u16 = (uint16_t)new_value;
382 			vp = (uchar_t *)&u16;
383 			break;
384 		case 4:
385 			u32 = (uint32_t)new_value;
386 			vp = (uchar_t *)&u32;
387 			break;
388 		case 8:
389 			vp = (uchar_t *)&new_value;
390 			break;
391 		default:
392 			return (ENOTSUP);
393 		}
394 
395 		status = mip->mi_callbacks->mc_setprop(mip->mi_driver,
396 		    name, mip->mi_type->mt_mapping[i].mp_prop_id,
397 		    mip->mi_type->mt_mapping[i].mp_valsize, (const void *)vp);
398 		goto done;
399 	}
400 
401 priv_prop:
402 	(void) snprintf(priv_name, sizeof (priv_name), "_%s", name);
403 	status = mip->mi_callbacks->mc_setprop(mip->mi_driver, priv_name,
404 	    MAC_PROP_PRIVATE, strlen(new_valuep), new_valuep);
405 done:
406 	freemsg(mp1);
407 	mp->b_cont = NULL;
408 	return (status);
409 }
410