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 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * functions to handle legacy ndd ioctls 31 */ 32 #include <sys/types.h> 33 #include <sys/mac.h> 34 #include <sys/mac_impl.h> 35 #include <inet/nd.h> 36 #include <sys/mac_ether.h> 37 #include <sys/policy.h> 38 #include <sys/strsun.h> 39 40 static int mac_ndd_set_ioctl(mac_impl_t *, mblk_t *, int, int *); 41 static int mac_ndd_get_ioctl(mac_impl_t *, mblk_t *, int, int *); 42 static int mac_ndd_get_names(mac_impl_t *, mblk_t *); 43 static boolean_t mac_add_name(mblk_t *, char *, int); 44 45 /* 46 * add "<name> (<rwtag>) " into the mblk, allocating more memory if needed. 47 */ 48 static boolean_t 49 mac_add_name(mblk_t *mp, char *name, int ndd_flags) 50 { 51 char *cp, *rwtag; 52 int len, flags; 53 54 flags = (ndd_flags & (MAC_PROP_PERM_WRITE|MAC_PROP_PERM_READ)); 55 switch (flags) { 56 case 0: 57 rwtag = "no read or write"; 58 break; 59 case MAC_PROP_PERM_WRITE: 60 rwtag = "write only"; 61 break; 62 case MAC_PROP_PERM_READ: 63 rwtag = "read only"; 64 break; 65 default: 66 rwtag = "read and write"; 67 break; 68 } 69 70 while (mp->b_cont != NULL) 71 mp = mp->b_cont; 72 /* 73 * allocate space for name, <space>, '(', rwtag, ')', and 74 * two terminating null chars. 75 */ 76 len = strlen(name) + strlen(rwtag) + 6; 77 if (mp->b_wptr + len >= mp->b_datap->db_lim) { 78 mp->b_cont = allocb(len, BPRI_HI); 79 mp = mp->b_cont; 80 if (mp != NULL) 81 return (B_FALSE); 82 } 83 cp = (char *)mp->b_wptr; 84 (void) snprintf(cp, len, "%s (%s)", name, rwtag); 85 mp->b_wptr += strnlen(cp, len); 86 mp->b_wptr++; /* skip past the terminating \0 */ 87 return (B_TRUE); 88 } 89 90 91 /* 92 * handle a query for "ndd -get \?". The result is put into mp, and 93 * more memory is allocated if needed. The resulting size of the data 94 * is returned. 95 */ 96 static int 97 mac_ndd_get_names(mac_impl_t *mip, mblk_t *mp) 98 { 99 int size_out, i; 100 mblk_t *tmp; 101 mac_priv_prop_t *mpriv; 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 (!mac_add_name(mp, mip->mi_type->mt_mapping[i].mp_name, 109 mip->mi_type->mt_mapping[i].mp_flags)) 110 return (-1); 111 } 112 113 /* now the driver's ndd variables */ 114 for (i = 0; i < mip->mi_priv_prop_count; i++) { 115 116 mpriv = &mip->mi_priv_prop[i]; 117 118 /* skip over the "_" */ 119 if (!mac_add_name(mp, &mpriv->mpp_name[1], mpriv->mpp_flags)) 120 return (-1); 121 } 122 123 tmp = mp; 124 while (tmp->b_cont != NULL) 125 tmp = tmp->b_cont; 126 *tmp->b_wptr++ = '\0'; 127 size_out = msgdsize(mp); 128 return (size_out); 129 } 130 131 132 /* 133 * Handle legacy ndd ioctls for ND_GET and ND_SET. 134 */ 135 void 136 mac_ndd_ioctl(mac_impl_t *mip, queue_t *wq, mblk_t *mp) 137 { 138 IOCP iocp; 139 int cmd, err, rval; 140 141 iocp = (IOCP)mp->b_rptr; 142 if (iocp->ioc_count == 0 || mp->b_cont == NULL) { 143 err = EINVAL; 144 goto done; 145 } 146 147 cmd = iocp->ioc_cmd; 148 149 if (cmd == ND_SET) { 150 err = mac_ndd_set_ioctl(mip, mp, iocp->ioc_count, &rval); 151 } else if (cmd == ND_GET) { 152 err = mac_ndd_get_ioctl(mip, mp, iocp->ioc_count, &rval); 153 } 154 done: 155 if (err == 0) 156 miocack(wq, mp, msgdsize(mp->b_cont), rval); 157 else 158 miocnak(wq, mp, 0, err); 159 } 160 161 static int 162 mac_ndd_get_ioctl(mac_impl_t *mip, mblk_t *mp, int avail, int *rval) 163 { 164 mblk_t *mp1; 165 char *valp; 166 uchar_t *value; 167 uint32_t new_value; 168 int size_out, i; 169 int status = EINVAL; 170 char *name, priv_name[MAXLINKPROPNAME]; 171 uint8_t u8; 172 uint16_t u16; 173 uint32_t u32; 174 uint64_t u64; 175 176 if (mp->b_cont == NULL || avail < 2) 177 return (EINVAL); 178 valp = (char *)mp->b_cont->b_rptr; 179 mp1 = allocb(avail, BPRI_HI); /* the returned buffer */ 180 if (mp1 == NULL) 181 return (ENOMEM); 182 183 if (strcmp(valp, "?") == 0) { 184 /* 185 * handle "ndd -get <..> \?" queries. 186 */ 187 size_out = mac_ndd_get_names(mip, mp1); 188 if (size_out < 0) { 189 status = ENOMEM; 190 goto get_done; 191 } 192 if (size_out > avail) { 193 int excess; 194 char *cp; 195 /* 196 * need more user buffer space. Return as many 197 * mblks as will fit and return the needed 198 * buffer size in ioc_rval. 199 */ 200 excess = size_out - avail; 201 *rval = size_out; /* what's needed */ 202 size_out -= excess; 203 (void) adjmsg(mp1, -(excess + 1)); 204 cp = (char *)mp1->b_wptr; 205 *cp = '\0'; 206 } 207 status = 0; 208 goto get_done; 209 } 210 211 ASSERT(mip->mi_callbacks->mc_callbacks & MC_GETPROP); 212 name = valp; 213 valp = (char *)mp1->b_rptr; 214 mp1->b_wptr = mp1->b_rptr; 215 216 /* first lookup ndd <-> public property mapping */ 217 for (i = 0; i < mip->mi_type->mt_mappingcount; i++) { 218 if (strcmp(name, mip->mi_type->mt_mapping[i].mp_name) != 0) 219 continue; 220 221 switch (mip->mi_type->mt_mapping[i].mp_valsize) { 222 case 1: 223 value = (uchar_t *)&u8; 224 break; 225 case 2: 226 value = (uchar_t *)&u16; 227 break; 228 case 4: 229 value = (uchar_t *)&u32; 230 break; 231 default: 232 value = (uchar_t *)&u64; 233 break; 234 } 235 236 if ((mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_MAP_KSTAT) 237 != 0) { 238 u64 = mac_stat_get((mac_handle_t)mip, 239 mip->mi_type->mt_mapping[i].mp_kstat); 240 status = 0; 241 /* 242 * ether_stats are all always KSTAT_DATA_UINT32 243 */ 244 new_value = u32 = (long)u64; 245 } else { 246 status = mip->mi_callbacks->mc_getprop(mip->mi_driver, 247 name, mip->mi_type->mt_mapping[i].mp_prop_id, 0, 248 mip->mi_type->mt_mapping[i].mp_valsize, value); 249 switch (mip->mi_type->mt_mapping[i].mp_valsize) { 250 case 1: 251 new_value = u8; 252 break; 253 case 2: 254 new_value = u16; 255 break; 256 case 4: 257 new_value = u32; 258 break; 259 case 8: 260 /* 261 * The only uint64_t is for speed, which is 262 * converted to Mbps in ndd reports. 263 */ 264 new_value = (u64/1000000); 265 break; 266 } 267 } 268 269 if (status != 0) 270 goto get_done; 271 272 (void) snprintf(valp, avail, "%d", new_value); 273 goto update_reply; 274 } 275 276 /* 277 * could not find a public property. try the private prop route 278 * where all string processing will be done by the driver. 279 */ 280 (void) snprintf(priv_name, sizeof (priv_name), "_%s", name); 281 status = mip->mi_callbacks->mc_getprop(mip->mi_driver, priv_name, 282 MAC_PROP_PRIVATE, 0, avail - 2, mp1->b_rptr); 283 if (status != 0) 284 goto get_done; 285 286 update_reply: 287 size_out += strnlen((const char *)mp1->b_rptr, avail); 288 valp += size_out; 289 *valp++ = '\0'; /* need \0\0 */ 290 *valp++ = '\0'; 291 mp1->b_wptr = (uchar_t *)valp; 292 *rval = 0; 293 294 get_done: 295 freemsg(mp->b_cont); 296 if (status == 0) 297 mp->b_cont = mp1; 298 else { 299 freemsg(mp1); 300 mp->b_cont = NULL; 301 } 302 return (status); 303 } 304 305 static int 306 mac_ndd_set_ioctl(mac_impl_t *mip, mblk_t *mp, int avail, int *rval) 307 { 308 mblk_t *mp1; 309 char *valp, *name, *new_valuep; 310 uchar_t *vp; 311 long new_value; 312 int status, i; 313 uint8_t u8; 314 uint16_t u16; 315 uint32_t u32; 316 IOCP iocp; 317 char priv_name[MAXLINKPROPNAME]; 318 319 if (avail == 0 || !(mp1 = mp->b_cont)) 320 return (EINVAL); 321 322 if (mp1->b_cont) { 323 freemsg(mp1->b_cont); 324 mp1->b_cont = NULL; 325 } 326 mp1->b_datap->db_lim[-1] = '\0'; 327 valp = (char *)mp1->b_rptr; 328 name = valp; 329 *rval = 0; 330 while (*valp++) 331 ; 332 if (valp >= (char *)mp1->b_wptr) 333 valp = NULL; 334 335 new_valuep = valp; 336 if (ddi_strtol(valp, NULL, 0, &new_value) != 0) 337 goto priv_prop; 338 339 iocp = (IOCP)mp->b_rptr; 340 if (valp != NULL && 341 ((iocp->ioc_cr == NULL) || 342 ((status = secpolicy_net_config(iocp->ioc_cr, B_FALSE)) != 0))) 343 return (status); 344 345 status = EINVAL; 346 347 /* first lookup ndd <-> public property mapping */ 348 for (i = 0; i < mip->mi_type->mt_mappingcount; i++) { 349 if (strcmp(name, mip->mi_type->mt_mapping[i].mp_name) != 0) 350 continue; 351 352 if (mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_MAP_KSTAT) 353 return (EINVAL); 354 355 if (new_value > mip->mi_type->mt_mapping[i].mp_maxval || 356 new_value < mip->mi_type->mt_mapping[i].mp_minval || 357 (mip->mi_type->mt_mapping[i].mp_flags & MAC_PROP_PERM_WRITE) 358 == 0) 359 return (EINVAL); 360 switch (mip->mi_type->mt_mapping[i].mp_valsize) { 361 case 1: 362 u8 = (uint8_t)new_value; 363 vp = (uchar_t *)&u8; 364 break; 365 case 2: 366 u16 = (uint16_t)new_value; 367 vp = (uchar_t *)&u16; 368 break; 369 case 4: 370 u32 = (uint32_t)new_value; 371 vp = (uchar_t *)&u32; 372 break; 373 case 8: 374 vp = (uchar_t *)&new_value; 375 break; 376 default: 377 return (ENOTSUP); 378 } 379 380 status = mip->mi_callbacks->mc_setprop(mip->mi_driver, 381 name, mip->mi_type->mt_mapping[i].mp_prop_id, 382 mip->mi_type->mt_mapping[i].mp_valsize, (const void *)vp); 383 goto done; 384 } 385 386 priv_prop: 387 (void) snprintf(priv_name, sizeof (priv_name), "_%s", name); 388 status = mip->mi_callbacks->mc_setprop(mip->mi_driver, priv_name, 389 MAC_PROP_PRIVATE, strlen(new_valuep), new_valuep); 390 done: 391 freemsg(mp1); 392 mp->b_cont = NULL; 393 return (status); 394 } 395