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