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