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
37static int mac_ndd_set_ioctl(mac_impl_t *, mblk_t *, int, int *);
38static int mac_ndd_get_ioctl(mac_impl_t *, mblk_t *, int, int *);
39static int mac_ndd_get_names(mac_impl_t *, mblk_t *);
40static boolean_t mac_add_name(mblk_t *, char *, int);
41
42/*
43 * add "<name> (<rwtag>) " into the mblk, allocating more memory if needed.
44 */
45static boolean_t
46mac_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 */
93static int
94mac_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 */
156void
157mac_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	}
175done:
176	if (err == 0)
177		miocack(wq, mp, msgdsize(mp->b_cont), rval);
178	else
179		miocnak(wq, mp, 0, err);
180}
181
182static int
183mac_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
307update_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
315get_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
326static int
327mac_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
407priv_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);
411done:
412	freemsg(mp1);
413	mp->b_cont = NULL;
414	return (status);
415}
416