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