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