mac_ndd.c revision 6789:4c5ca96fbed3
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
40static int mac_ndd_set_ioctl(mac_impl_t *, mblk_t *, int, int *);
41static int mac_ndd_get_ioctl(mac_impl_t *, mblk_t *, int, int *);
42static int mac_ndd_get_names(mac_impl_t *, mblk_t *);
43static boolean_t mac_add_name(mblk_t *, char *, int);
44
45/*
46 * add "<name> (<rwtag>) " into the mblk, allocating more memory if needed.
47 */
48static boolean_t
49mac_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 */
96static int
97mac_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 */
135void
136mac_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	}
154done:
155	if (err == 0)
156		miocack(wq, mp, msgdsize(mp->b_cont), rval);
157	else
158		miocnak(wq, mp, 0, err);
159}
160
161static int
162mac_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
286update_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
294get_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
305static int
306mac_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
386priv_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);
390done:
391	freemsg(mp1);
392	mp->b_cont = NULL;
393	return (status);
394}
395