xref: /illumos-gate/usr/src/uts/common/io/mac/mac_ndd.c (revision 4f364e7c95ee7fd9d5bbeddc1940e92405bb0e72)
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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*
26  * functions to handle legacy ndd  ioctls
27  */
28 #include <sys/types.h>
29 #include <sys/mac.h>
30 #include <sys/mac_impl.h>
31 #include <sys/mac_client_priv.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 	uint_t permflags;
99 	int status;
100 	uint64_t value;
101 	char *prop_name;
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 		if ((mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_MAP_KSTAT)
109 		    != 0)
110 			permflags = MAC_PROP_PERM_READ;
111 		else {
112 			status = mip->mi_callbacks->mc_getprop(mip->mi_driver,
113 			    mip->mi_type->mt_mapping[i].mp_name,
114 			    mip->mi_type->mt_mapping[i].mp_prop_id,
115 			    mip->mi_type->mt_mapping[i].mp_valsize, &value);
116 			if (status != 0)
117 				continue;
118 			status = mac_prop_info((mac_handle_t)mip,
119 			    mip->mi_type->mt_mapping[i].mp_prop_id,
120 			    mip->mi_type->mt_mapping[i].mp_name, NULL, 0,
121 			    NULL, &permflags);
122 			if (status != 0)
123 				continue;
124 		}
125 		if (!mac_add_name(mp, mip->mi_type->mt_mapping[i].mp_name,
126 		    permflags))
127 			return (-1);
128 	}
129 
130 	/* now the driver's ndd variables */
131 	for (i = 0; i < mip->mi_priv_prop_count; i++) {
132 
133 		prop_name = mip->mi_priv_prop[i];
134 
135 		if (mac_prop_info((mac_handle_t)mip, MAC_PROP_PRIVATE,
136 		    prop_name, NULL, 0, NULL, &permflags) != 0)
137 			return (-1);
138 
139 		/* skip over the "_" */
140 		if (!mac_add_name(mp, &prop_name[1], permflags))
141 			return (-1);
142 	}
143 
144 	tmp = mp;
145 	while (tmp->b_cont != NULL)
146 		tmp = tmp->b_cont;
147 	*tmp->b_wptr++ = '\0';
148 	size_out = msgdsize(mp);
149 	return (size_out);
150 }
151 
152 
153 /*
154  * Handle legacy ndd ioctls for ND_GET and ND_SET.
155  */
156 void
157 mac_ndd_ioctl(mac_impl_t *mip, queue_t *wq, mblk_t *mp)
158 {
159 	IOCP    iocp;
160 	int	cmd, err, rval;
161 
162 	iocp = (IOCP)mp->b_rptr;
163 	if (iocp->ioc_count == 0 || mp->b_cont == NULL) {
164 		err = EINVAL;
165 		goto done;
166 	}
167 
168 	cmd = iocp->ioc_cmd;
169 
170 	if (cmd == ND_SET) {
171 		err = mac_ndd_set_ioctl(mip, mp, iocp->ioc_count, &rval);
172 	} else if (cmd == ND_GET) {
173 		err = mac_ndd_get_ioctl(mip, mp, iocp->ioc_count, &rval);
174 	}
175 done:
176 	if (err == 0)
177 		miocack(wq, mp, msgdsize(mp->b_cont), rval);
178 	else
179 		miocnak(wq, mp, 0, err);
180 }
181 
182 static int
183 mac_ndd_get_ioctl(mac_impl_t *mip, mblk_t *mp, int avail, int *rval)
184 {
185 	mblk_t		*mp1;
186 	char		*valp;
187 	uchar_t 	*value;
188 	uint32_t	new_value;
189 	int		size_out = 0, i;
190 	int		status = EINVAL;
191 	char		*name, priv_name[MAXLINKPROPNAME];
192 	uint8_t		u8;
193 	uint16_t	u16;
194 	uint32_t	u32;
195 	uint64_t	u64;
196 
197 	if (mp->b_cont == NULL || avail < 2)
198 		return (EINVAL);
199 	valp = (char *)mp->b_cont->b_rptr;
200 	mp1 = allocb(avail, BPRI_HI); /* the returned buffer */
201 	if (mp1 == NULL)
202 		return (ENOMEM);
203 
204 	if (strcmp(valp, "?") == 0) {
205 		/*
206 		 * handle "ndd -get <..> \?" queries.
207 		 */
208 		size_out = mac_ndd_get_names(mip, mp1);
209 		if (size_out < 0) {
210 			status = ENOMEM;
211 			goto get_done;
212 		}
213 		if (size_out > avail) {
214 			int excess;
215 			char *cp;
216 			/*
217 			 * need more user buffer space. Return as many
218 			 * mblks as will fit and return the needed
219 			 * buffer size in ioc_rval.
220 			 */
221 			excess = size_out - avail;
222 			*rval = size_out; /* what's needed */
223 			size_out -= excess;
224 			(void) adjmsg(mp1, -(excess + 1));
225 			cp = (char *)mp1->b_wptr;
226 			*cp = '\0';
227 		}
228 		status = 0;
229 		goto get_done;
230 	}
231 
232 	ASSERT(mip->mi_callbacks->mc_callbacks & MC_GETPROP);
233 	name = valp;
234 	valp = (char *)mp1->b_rptr;
235 	mp1->b_wptr = mp1->b_rptr;
236 
237 	/* first lookup ndd <-> public property mapping */
238 	for (i = 0; i < mip->mi_type->mt_mappingcount; i++) {
239 		if (strcmp(name, mip->mi_type->mt_mapping[i].mp_name) != 0)
240 			continue;
241 
242 		switch (mip->mi_type->mt_mapping[i].mp_valsize) {
243 		case 1:
244 			value = (uchar_t *)&u8;
245 			break;
246 		case 2:
247 			value = (uchar_t *)&u16;
248 			break;
249 		case 4:
250 			value = (uchar_t *)&u32;
251 			break;
252 		default:
253 			value = (uchar_t *)&u64;
254 			break;
255 		}
256 
257 		if ((mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_MAP_KSTAT)
258 		    != 0) {
259 			u64 = mac_stat_get((mac_handle_t)mip,
260 			    mip->mi_type->mt_mapping[i].mp_kstat);
261 			status = 0;
262 			/*
263 			 * ether_stats are all always KSTAT_DATA_UINT32
264 			 */
265 			new_value = u32 = (long)u64;
266 		} else {
267 			status = mip->mi_callbacks->mc_getprop(mip->mi_driver,
268 			    name, mip->mi_type->mt_mapping[i].mp_prop_id,
269 			    mip->mi_type->mt_mapping[i].mp_valsize, value);
270 			switch (mip->mi_type->mt_mapping[i].mp_valsize) {
271 			case 1:
272 				new_value = u8;
273 				break;
274 			case 2:
275 				new_value = u16;
276 				break;
277 			case 4:
278 				new_value = u32;
279 				break;
280 			case 8:
281 				/*
282 				 * The only uint64_t is for speed, which is
283 				 * converted to Mbps in ndd reports.
284 				 */
285 				new_value = (u64/1000000);
286 				break;
287 			}
288 		}
289 
290 		if (status != 0)
291 			goto get_done;
292 
293 		(void) snprintf(valp, avail, "%d", new_value);
294 		goto update_reply;
295 	}
296 
297 	/*
298 	 * could not find a public property. try the private prop route
299 	 * where all string processing will be done by the driver.
300 	 */
301 	(void) snprintf(priv_name, sizeof (priv_name), "_%s", name);
302 	status = mip->mi_callbacks->mc_getprop(mip->mi_driver, priv_name,
303 	    MAC_PROP_PRIVATE, avail - 2, mp1->b_rptr);
304 	if (status != 0)
305 		goto get_done;
306 
307 update_reply:
308 	size_out += strnlen((const char *)mp1->b_rptr, avail);
309 	valp += size_out;
310 	*valp++ = '\0'; /* need \0\0 */
311 	*valp++ = '\0';
312 	mp1->b_wptr = (uchar_t *)valp;
313 	*rval = 0;
314 
315 get_done:
316 	freemsg(mp->b_cont);
317 	if (status == 0)
318 		mp->b_cont = mp1;
319 	else {
320 		freemsg(mp1);
321 		mp->b_cont = NULL;
322 	}
323 	return (status);
324 }
325 
326 static int
327 mac_ndd_set_ioctl(mac_impl_t *mip, mblk_t *mp, int avail, int *rval)
328 {
329 	mblk_t  	*mp1;
330 	char		*valp, *name, *new_valuep;
331 	uchar_t 	*vp;
332 	long		new_value;
333 	int		status, i;
334 	uint8_t		u8;
335 	uint16_t	u16;
336 	uint32_t	u32;
337 	IOCP		iocp;
338 	char		priv_name[MAXLINKPROPNAME];
339 
340 	if (avail == 0 || !(mp1 = mp->b_cont))
341 		return (EINVAL);
342 
343 	if (mp1->b_cont) {
344 		freemsg(mp1->b_cont);
345 		mp1->b_cont = NULL;
346 	}
347 	mp1->b_datap->db_lim[-1] = '\0';
348 	valp = (char *)mp1->b_rptr;
349 	name = valp;
350 	*rval = 0;
351 	while (*valp++)
352 		;
353 	if (valp >= (char *)mp1->b_wptr)
354 		valp = NULL;
355 
356 	new_valuep = valp;
357 	if (ddi_strtol(valp, NULL, 0, &new_value) != 0)
358 		goto priv_prop;
359 
360 	iocp = (IOCP)mp->b_rptr;
361 	if (valp != NULL &&
362 	    ((iocp->ioc_cr == NULL) ||
363 	    ((status = secpolicy_net_config(iocp->ioc_cr, B_FALSE)) != 0)))
364 		return (status);
365 
366 	status = EINVAL;
367 
368 	/* first lookup ndd <-> public property mapping */
369 	for (i = 0; i < mip->mi_type->mt_mappingcount; i++) {
370 		if (strcmp(name, mip->mi_type->mt_mapping[i].mp_name) != 0)
371 			continue;
372 
373 		if (mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_MAP_KSTAT)
374 			return (EINVAL);
375 
376 		if (new_value > mip->mi_type->mt_mapping[i].mp_maxval ||
377 		    new_value < mip->mi_type->mt_mapping[i].mp_minval ||
378 		    (mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_PERM_WRITE)
379 		    == 0)
380 			return (EINVAL);
381 		switch (mip->mi_type->mt_mapping[i].mp_valsize) {
382 		case 1:
383 			u8 = (uint8_t)new_value;
384 			vp = (uchar_t *)&u8;
385 			break;
386 		case 2:
387 			u16 = (uint16_t)new_value;
388 			vp = (uchar_t *)&u16;
389 			break;
390 		case 4:
391 			u32 = (uint32_t)new_value;
392 			vp = (uchar_t *)&u32;
393 			break;
394 		case 8:
395 			vp = (uchar_t *)&new_value;
396 			break;
397 		default:
398 			return (ENOTSUP);
399 		}
400 
401 		status = mip->mi_callbacks->mc_setprop(mip->mi_driver,
402 		    name, mip->mi_type->mt_mapping[i].mp_prop_id,
403 		    mip->mi_type->mt_mapping[i].mp_valsize, (const void *)vp);
404 		goto done;
405 	}
406 
407 priv_prop:
408 	(void) snprintf(priv_name, sizeof (priv_name), "_%s", name);
409 	status = mip->mi_callbacks->mc_setprop(mip->mi_driver, priv_name,
410 	    MAC_PROP_PRIVATE, strlen(new_valuep), new_valuep);
411 done:
412 	freemsg(mp1);
413 	mp->b_cont = NULL;
414 	return (status);
415 }
416