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 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 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 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 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 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