mac_ndd.c revision 11878:ac93462db6d7
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
38static int mac_ndd_set_ioctl(mac_impl_t *, mblk_t *, int, int *);
39static int mac_ndd_get_ioctl(mac_impl_t *, mblk_t *, int, int *);
40static int mac_ndd_get_names(mac_impl_t *, mblk_t *);
41static boolean_t mac_add_name(mblk_t *, char *, int);
42
43/*
44 * add "<name> (<rwtag>) " into the mblk, allocating more memory if needed.
45 */
46static boolean_t
47mac_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 */
94static int
95mac_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 */
157void
158mac_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	}
176done:
177	if (err == 0)
178		miocack(wq, mp, msgdsize(mp->b_cont), rval);
179	else
180		miocnak(wq, mp, 0, err);
181}
182
183static int
184mac_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
308update_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
316get_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
327static int
328mac_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
408priv_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);
412done:
413	freemsg(mp1);
414	mp->b_cont = NULL;
415	return (status);
416}
417