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 2009 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 <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 mac_priv_prop_t *mpriv; 99 uint_t permflags; 100 int status; 101 uint64_t value; 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 permflags = MAC_PROP_PERM_RW; 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 0, mip->mi_type->mt_mapping[i].mp_valsize, 117 &value, &permflags); 118 if (status != 0) 119 continue; 120 } 121 if (!mac_add_name(mp, mip->mi_type->mt_mapping[i].mp_name, 122 permflags)) 123 return (-1); 124 } 125 126 /* now the driver's ndd variables */ 127 for (i = 0; i < mip->mi_priv_prop_count; i++) { 128 129 mpriv = &mip->mi_priv_prop[i]; 130 131 /* skip over the "_" */ 132 if (!mac_add_name(mp, &mpriv->mpp_name[1], mpriv->mpp_flags)) 133 return (-1); 134 } 135 136 tmp = mp; 137 while (tmp->b_cont != NULL) 138 tmp = tmp->b_cont; 139 *tmp->b_wptr++ = '\0'; 140 size_out = msgdsize(mp); 141 return (size_out); 142 } 143 144 145 /* 146 * Handle legacy ndd ioctls for ND_GET and ND_SET. 147 */ 148 void 149 mac_ndd_ioctl(mac_impl_t *mip, queue_t *wq, mblk_t *mp) 150 { 151 IOCP iocp; 152 int cmd, err, rval; 153 154 iocp = (IOCP)mp->b_rptr; 155 if (iocp->ioc_count == 0 || mp->b_cont == NULL) { 156 err = EINVAL; 157 goto done; 158 } 159 160 cmd = iocp->ioc_cmd; 161 162 if (cmd == ND_SET) { 163 err = mac_ndd_set_ioctl(mip, mp, iocp->ioc_count, &rval); 164 } else if (cmd == ND_GET) { 165 err = mac_ndd_get_ioctl(mip, mp, iocp->ioc_count, &rval); 166 } 167 done: 168 if (err == 0) 169 miocack(wq, mp, msgdsize(mp->b_cont), rval); 170 else 171 miocnak(wq, mp, 0, err); 172 } 173 174 static int 175 mac_ndd_get_ioctl(mac_impl_t *mip, mblk_t *mp, int avail, int *rval) 176 { 177 mblk_t *mp1; 178 char *valp; 179 uchar_t *value; 180 uint32_t new_value; 181 int size_out, i; 182 int status = EINVAL; 183 char *name, priv_name[MAXLINKPROPNAME]; 184 uint8_t u8; 185 uint16_t u16; 186 uint32_t u32; 187 uint64_t u64; 188 uint_t perm; 189 190 if (mp->b_cont == NULL || avail < 2) 191 return (EINVAL); 192 valp = (char *)mp->b_cont->b_rptr; 193 mp1 = allocb(avail, BPRI_HI); /* the returned buffer */ 194 if (mp1 == NULL) 195 return (ENOMEM); 196 197 if (strcmp(valp, "?") == 0) { 198 /* 199 * handle "ndd -get <..> \?" queries. 200 */ 201 size_out = mac_ndd_get_names(mip, mp1); 202 if (size_out < 0) { 203 status = ENOMEM; 204 goto get_done; 205 } 206 if (size_out > avail) { 207 int excess; 208 char *cp; 209 /* 210 * need more user buffer space. Return as many 211 * mblks as will fit and return the needed 212 * buffer size in ioc_rval. 213 */ 214 excess = size_out - avail; 215 *rval = size_out; /* what's needed */ 216 size_out -= excess; 217 (void) adjmsg(mp1, -(excess + 1)); 218 cp = (char *)mp1->b_wptr; 219 *cp = '\0'; 220 } 221 status = 0; 222 goto get_done; 223 } 224 225 ASSERT(mip->mi_callbacks->mc_callbacks & MC_GETPROP); 226 name = valp; 227 valp = (char *)mp1->b_rptr; 228 mp1->b_wptr = mp1->b_rptr; 229 230 /* first lookup ndd <-> public property mapping */ 231 for (i = 0; i < mip->mi_type->mt_mappingcount; i++) { 232 if (strcmp(name, mip->mi_type->mt_mapping[i].mp_name) != 0) 233 continue; 234 235 switch (mip->mi_type->mt_mapping[i].mp_valsize) { 236 case 1: 237 value = (uchar_t *)&u8; 238 break; 239 case 2: 240 value = (uchar_t *)&u16; 241 break; 242 case 4: 243 value = (uchar_t *)&u32; 244 break; 245 default: 246 value = (uchar_t *)&u64; 247 break; 248 } 249 250 if ((mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_MAP_KSTAT) 251 != 0) { 252 u64 = mac_stat_get((mac_handle_t)mip, 253 mip->mi_type->mt_mapping[i].mp_kstat); 254 status = 0; 255 /* 256 * ether_stats are all always KSTAT_DATA_UINT32 257 */ 258 new_value = u32 = (long)u64; 259 } else { 260 status = mip->mi_callbacks->mc_getprop(mip->mi_driver, 261 name, mip->mi_type->mt_mapping[i].mp_prop_id, 0, 262 mip->mi_type->mt_mapping[i].mp_valsize, value, 263 &perm); 264 switch (mip->mi_type->mt_mapping[i].mp_valsize) { 265 case 1: 266 new_value = u8; 267 break; 268 case 2: 269 new_value = u16; 270 break; 271 case 4: 272 new_value = u32; 273 break; 274 case 8: 275 /* 276 * The only uint64_t is for speed, which is 277 * converted to Mbps in ndd reports. 278 */ 279 new_value = (u64/1000000); 280 break; 281 } 282 } 283 284 if (status != 0) 285 goto get_done; 286 287 (void) snprintf(valp, avail, "%d", new_value); 288 goto update_reply; 289 } 290 291 /* 292 * could not find a public property. try the private prop route 293 * where all string processing will be done by the driver. 294 */ 295 (void) snprintf(priv_name, sizeof (priv_name), "_%s", name); 296 status = mip->mi_callbacks->mc_getprop(mip->mi_driver, priv_name, 297 MAC_PROP_PRIVATE, 0, avail - 2, mp1->b_rptr, &perm); 298 if (status != 0) 299 goto get_done; 300 301 update_reply: 302 size_out += strnlen((const char *)mp1->b_rptr, avail); 303 valp += size_out; 304 *valp++ = '\0'; /* need \0\0 */ 305 *valp++ = '\0'; 306 mp1->b_wptr = (uchar_t *)valp; 307 *rval = 0; 308 309 get_done: 310 freemsg(mp->b_cont); 311 if (status == 0) 312 mp->b_cont = mp1; 313 else { 314 freemsg(mp1); 315 mp->b_cont = NULL; 316 } 317 return (status); 318 } 319 320 static int 321 mac_ndd_set_ioctl(mac_impl_t *mip, mblk_t *mp, int avail, int *rval) 322 { 323 mblk_t *mp1; 324 char *valp, *name, *new_valuep; 325 uchar_t *vp; 326 long new_value; 327 int status, i; 328 uint8_t u8; 329 uint16_t u16; 330 uint32_t u32; 331 IOCP iocp; 332 char priv_name[MAXLINKPROPNAME]; 333 334 if (avail == 0 || !(mp1 = mp->b_cont)) 335 return (EINVAL); 336 337 if (mp1->b_cont) { 338 freemsg(mp1->b_cont); 339 mp1->b_cont = NULL; 340 } 341 mp1->b_datap->db_lim[-1] = '\0'; 342 valp = (char *)mp1->b_rptr; 343 name = valp; 344 *rval = 0; 345 while (*valp++) 346 ; 347 if (valp >= (char *)mp1->b_wptr) 348 valp = NULL; 349 350 new_valuep = valp; 351 if (ddi_strtol(valp, NULL, 0, &new_value) != 0) 352 goto priv_prop; 353 354 iocp = (IOCP)mp->b_rptr; 355 if (valp != NULL && 356 ((iocp->ioc_cr == NULL) || 357 ((status = secpolicy_net_config(iocp->ioc_cr, B_FALSE)) != 0))) 358 return (status); 359 360 status = EINVAL; 361 362 /* first lookup ndd <-> public property mapping */ 363 for (i = 0; i < mip->mi_type->mt_mappingcount; i++) { 364 if (strcmp(name, mip->mi_type->mt_mapping[i].mp_name) != 0) 365 continue; 366 367 if (mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_MAP_KSTAT) 368 return (EINVAL); 369 370 if (new_value > mip->mi_type->mt_mapping[i].mp_maxval || 371 new_value < mip->mi_type->mt_mapping[i].mp_minval || 372 (mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_PERM_WRITE) 373 == 0) 374 return (EINVAL); 375 switch (mip->mi_type->mt_mapping[i].mp_valsize) { 376 case 1: 377 u8 = (uint8_t)new_value; 378 vp = (uchar_t *)&u8; 379 break; 380 case 2: 381 u16 = (uint16_t)new_value; 382 vp = (uchar_t *)&u16; 383 break; 384 case 4: 385 u32 = (uint32_t)new_value; 386 vp = (uchar_t *)&u32; 387 break; 388 case 8: 389 vp = (uchar_t *)&new_value; 390 break; 391 default: 392 return (ENOTSUP); 393 } 394 395 status = mip->mi_callbacks->mc_setprop(mip->mi_driver, 396 name, mip->mi_type->mt_mapping[i].mp_prop_id, 397 mip->mi_type->mt_mapping[i].mp_valsize, (const void *)vp); 398 goto done; 399 } 400 401 priv_prop: 402 (void) snprintf(priv_name, sizeof (priv_name), "_%s", name); 403 status = mip->mi_callbacks->mc_setprop(mip->mi_driver, priv_name, 404 MAC_PROP_PRIVATE, strlen(new_valuep), new_valuep); 405 done: 406 freemsg(mp1); 407 mp->b_cont = NULL; 408 return (status); 409 } 410