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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 #include <string.h> 32 #include <fcntl.h> 33 #include <unistd.h> 34 #include <stropts.h> 35 #include <stdlib.h> 36 #include <errno.h> 37 #include <strings.h> 38 #include <libintl.h> 39 #include <net/if_types.h> 40 #include <net/if_dl.h> 41 #include <libdladm_impl.h> 42 #include <libdlvnic.h> 43 44 /* 45 * VNIC administration library. 46 */ 47 48 #define VNIC_DEV "/devices/pseudo/vnic@0:" VNIC_CTL_NODE_NAME 49 50 /* 51 * Because by default the id is used as the DLPI device PPA and default 52 * VLAN PPA's are calculated as ((1000 * vid) + PPA), the largest id 53 * can't be > 999. We reserve the last 100 VNIC ids for automatic 54 * VNIC id assignment. 55 */ 56 #define DLADM_VNIC_MIN_VNIC_ID 1 /* total range */ 57 #define DLADM_VNIC_MAX_VNIC_ID 999 58 #define DLADM_VNIC_MIN_VNIC_SPEC_ID 1 /* specified by user */ 59 #define DLADM_VNIC_MAX_VNIC_SPEC_ID 899 60 #define DLADM_VNIC_MIN_VNIC_AUTO_ID 900 /* picked automatically */ 61 #define DLADM_VNIC_MAX_VNIC_AUTO_ID 999 62 63 #define DLADM_VNIC_NUM_VNIC_AUTO_ID (DLADM_VNIC_MAX_VNIC_AUTO_ID - \ 64 DLADM_VNIC_MIN_VNIC_AUTO_ID + 1) 65 66 /* Limits on buffer size for VNIC_IOC_INFO request */ 67 #define MIN_INFO_SIZE (4*1024) 68 #define MAX_INFO_SIZE (128*1024) 69 70 /* configuration database entry */ 71 typedef struct dladm_vnic_attr_db { 72 uint_t vt_vnic_id; 73 char vt_dev_name[MAXNAMELEN]; 74 vnic_mac_addr_type_t vt_mac_addr_type; 75 uint_t vt_mac_len; 76 uchar_t vt_mac_addr[MAXMACADDRLEN]; 77 } dladm_vnic_attr_db_t; 78 79 typedef struct dladm_vnic_up { 80 uint_t vu_vnic_id; 81 boolean_t vu_found; 82 int vu_fd; 83 } dladm_vnic_up_t; 84 85 typedef struct dladm_vnic_down { 86 uint32_t vd_vnic_id; 87 boolean_t vd_found; 88 } dladm_vnic_down_t; 89 90 typedef struct dladm_vnic_modify { 91 uint32_t vm_vnic_id; 92 boolean_t vm_found; 93 } dladm_vnic_modify_t; 94 95 typedef struct dladm_vnic_modify_attr { 96 vnic_mac_addr_type_t vm_mac_addr_type; 97 int vm_mac_len; 98 uchar_t vm_mac_addr[MAXMACADDRLEN]; 99 } dladm_vnic_modify_attr_t; 100 101 /* 102 * Send a create command to the VNIC driver. 103 */ 104 static dladm_status_t 105 i_dladm_vnic_create_sys(int fd, dladm_vnic_attr_db_t *attr) 106 { 107 int rc; 108 vnic_ioc_create_t ioc; 109 110 ioc.vc_vnic_id = attr->vt_vnic_id; 111 bcopy(attr->vt_dev_name, ioc.vc_dev_name, MAXNAMELEN); 112 ioc.vc_mac_addr_type = attr->vt_mac_addr_type; 113 ioc.vc_mac_len = attr->vt_mac_len; 114 bcopy(attr->vt_mac_addr, ioc.vc_mac_addr, attr->vt_mac_len); 115 116 rc = i_dladm_ioctl(fd, VNIC_IOC_CREATE, &ioc, sizeof (ioc)); 117 118 if (rc < 0) 119 return (dladm_errno2status(errno)); 120 121 return (DLADM_STATUS_OK); 122 } 123 124 /* 125 * Invoked to bring up a VNIC. 126 */ 127 static dladm_status_t 128 i_dladm_vnic_up(void *arg, dladm_vnic_attr_db_t *attr) 129 { 130 dladm_vnic_up_t *up = (dladm_vnic_up_t *)arg; 131 dladm_status_t status; 132 133 if (up->vu_vnic_id != 0 && up->vu_vnic_id != attr->vt_vnic_id) 134 return (DLADM_STATUS_OK); 135 136 up->vu_found = B_TRUE; 137 138 status = i_dladm_vnic_create_sys(up->vu_fd, attr); 139 if ((status != DLADM_STATUS_OK) && (up->vu_vnic_id != 0)) 140 return (status); 141 142 return (DLADM_STATUS_OK); 143 } 144 145 /* 146 * Send a modify command to the VNIC driver. 147 */ 148 static dladm_status_t 149 i_dladm_vnic_modify_sys(uint_t vnic_id, uint32_t modify_mask, 150 dladm_vnic_modify_attr_t *attr) 151 { 152 int rc; 153 int fd; 154 vnic_ioc_modify_t ioc; 155 156 ioc.vm_vnic_id = vnic_id; 157 158 ioc.vm_modify_mask = 0; 159 if (modify_mask & DLADM_VNIC_MODIFY_ADDR) 160 ioc.vm_modify_mask |= VNIC_IOC_MODIFY_ADDR; 161 162 ioc.vm_mac_addr_type = attr->vm_mac_addr_type; 163 ioc.vm_mac_len = attr->vm_mac_len; 164 bcopy(attr->vm_mac_addr, ioc.vm_mac_addr, MAXMACADDRLEN); 165 166 if ((fd = open(VNIC_DEV, O_RDWR)) < 0) 167 return (dladm_errno2status(errno)); 168 169 rc = i_dladm_ioctl(fd, VNIC_IOC_MODIFY, &ioc, sizeof (ioc)); 170 171 (void) close(fd); 172 173 if (rc < 0) 174 return (dladm_errno2status(errno)); 175 176 return (DLADM_STATUS_OK); 177 } 178 179 /* 180 * Walk through the vnics defined on the system and for each vnic <vnic>, 181 * invoke <fn>(<arg>, <vnic>); 182 */ 183 dladm_status_t 184 dladm_vnic_walk_sys(dladm_status_t (*fn)(void *, dladm_vnic_attr_sys_t *), 185 void *arg) 186 { 187 vnic_ioc_info_t *ioc; 188 vnic_ioc_info_vnic_t *vnic; 189 dladm_vnic_attr_sys_t attr; 190 int rc, i, bufsize, fd; 191 char *where; 192 dladm_status_t status = DLADM_STATUS_OK; 193 194 if ((fd = open(VNIC_DEV, O_RDWR)) == -1) 195 return (dladm_errno2status(errno)); 196 197 bufsize = MIN_INFO_SIZE; 198 ioc = (vnic_ioc_info_t *)calloc(1, bufsize); 199 if (ioc == NULL) { 200 (void) close(fd); 201 return (dladm_errno2status(ENOMEM)); 202 } 203 204 tryagain: 205 206 rc = i_dladm_ioctl(fd, VNIC_IOC_INFO, ioc, bufsize); 207 208 if (rc != 0) { 209 if (errno == ENOSPC) { 210 bufsize *= 2; 211 if (bufsize <= MAX_INFO_SIZE) { 212 ioc = (vnic_ioc_info_t *)realloc(ioc, bufsize); 213 if (ioc != NULL) { 214 bzero(ioc, bufsize); 215 goto tryagain; 216 } 217 } 218 } 219 status = dladm_errno2status(errno); 220 goto bail; 221 } 222 223 /* 224 * Go through each vnic returned by the vnic driver 225 */ 226 where = (char *)(ioc + 1); 227 228 for (i = 0; i < ioc->vi_nvnics; i++) { 229 /* LINTED E_BAD_PTR_CAST_ALIGN */ 230 vnic = (vnic_ioc_info_vnic_t *)where; 231 232 attr.va_vnic_id = vnic->vn_vnic_id; 233 bcopy(vnic->vn_dev_name, attr.va_dev_name, 234 MAXNAMELEN); 235 attr.va_mac_addr_type = vnic->vn_mac_addr_type; 236 bcopy(vnic->vn_mac_addr, attr.va_mac_addr, ETHERADDRL); 237 attr.va_mac_len = vnic->vn_mac_len; 238 where = (char *)(vnic + 1); 239 240 status = fn(arg, &attr); 241 if (status != DLADM_STATUS_OK) 242 goto bail; 243 } 244 245 bail: 246 free(ioc); 247 (void) close(fd); 248 249 return (status); 250 } 251 252 /* 253 * Remove a VNIC from the kernel. 254 */ 255 static dladm_status_t 256 i_dladm_vnic_delete_sys(int fd, dladm_vnic_attr_sys_t *attr) 257 { 258 vnic_ioc_delete_t ioc; 259 int rc; 260 261 ioc.vd_vnic_id = attr->va_vnic_id; 262 263 rc = i_dladm_ioctl(fd, VNIC_IOC_DELETE, &ioc, sizeof (ioc)); 264 265 if (rc < 0) 266 return (dladm_errno2status(errno)); 267 268 return (DLADM_STATUS_OK); 269 } 270 271 /* 272 * Invoked to bring down a VNIC. 273 */ 274 static dladm_status_t 275 i_dladm_vnic_down(void *arg, dladm_vnic_attr_sys_t *attr) 276 { 277 dladm_vnic_down_t *down = (dladm_vnic_down_t *)arg; 278 int fd; 279 dladm_status_t status; 280 281 if (down->vd_vnic_id != 0 && down->vd_vnic_id != attr->va_vnic_id) 282 return (DLADM_STATUS_OK); 283 284 down->vd_found = B_TRUE; 285 286 if ((fd = open(VNIC_DEV, O_RDWR)) < 0) 287 return (dladm_errno2status(errno)); 288 289 status = i_dladm_vnic_delete_sys(fd, attr); 290 if ((status != DLADM_STATUS_OK) && (down->vd_vnic_id != 0)) { 291 (void) close(fd); 292 return (status); 293 } 294 295 (void) close(fd); 296 return (DLADM_STATUS_OK); 297 } 298 299 /* 300 * Convert between MAC address types and their string representations. 301 */ 302 303 typedef struct dladm_vnic_addr_type_s { 304 char *va_str; 305 vnic_mac_addr_type_t va_type; 306 } dladm_vnic_addr_type_t; 307 308 static dladm_vnic_addr_type_t addr_types[] = { 309 {"fixed", VNIC_MAC_ADDR_TYPE_FIXED}, 310 }; 311 312 #define NADDR_TYPES (sizeof (addr_types) / sizeof (dladm_vnic_addr_type_t)) 313 314 /* returns B_TRUE if a matching type was found, B_FALSE otherwise */ 315 boolean_t 316 dladm_vnic_mac_addr_str_to_type(const char *str, vnic_mac_addr_type_t *val) 317 { 318 int i; 319 dladm_vnic_addr_type_t *type; 320 321 for (i = 0; i < NADDR_TYPES; i++) { 322 type = &addr_types[i]; 323 if (strncmp(str, type->va_str, strlen(type->va_str)) == 0) { 324 *val = type->va_type; 325 return (B_TRUE); 326 } 327 } 328 329 return (B_FALSE); 330 } 331 332 /* 333 * Select a VNIC id automatically. 334 */ 335 336 typedef struct dladm_vnic_auto_state_s { 337 uint_t as_nslots; 338 uint_t *as_slots; 339 } dladm_vnic_auto_state_t; 340 341 static dladm_status_t 342 i_dladm_vnic_create_auto_walker(void *arg, dladm_vnic_attr_sys_t *attr) 343 { 344 dladm_vnic_auto_state_t *state = arg; 345 346 if (attr->va_vnic_id < DLADM_VNIC_MIN_VNIC_AUTO_ID || 347 attr->va_vnic_id > DLADM_VNIC_MAX_VNIC_AUTO_ID) 348 return (DLADM_STATUS_OK); 349 350 state->as_slots[state->as_nslots++] = attr->va_vnic_id; 351 352 return (DLADM_STATUS_OK); 353 } 354 355 static int 356 i_dladm_vnic_compare(const void *p1, const void *p2) 357 { 358 uint_t i = *((uint_t *)p1); 359 uint_t j = *((uint_t *)p2); 360 361 if (i > j) 362 return (1); 363 if (i < j) 364 return (-1); 365 return (0); 366 } 367 368 /*ARGSUSED*/ 369 static dladm_status_t 370 i_dladm_vnic_get_auto_id(dladm_vnic_attr_db_t *attr, uint32_t *vnic_id_out) 371 { 372 dladm_vnic_auto_state_t state; 373 uint_t vnic_ids[DLADM_VNIC_NUM_VNIC_AUTO_ID]; 374 int i; 375 uint_t last_id, vnic_id; 376 dladm_status_t status; 377 378 /* 379 * Build a sorted array containing the existing VNIC ids in the range 380 * allocated for automatic allocation. 381 */ 382 state.as_nslots = 0; 383 state.as_slots = vnic_ids; 384 385 status = dladm_vnic_walk_sys(i_dladm_vnic_create_auto_walker, &state); 386 if (status != DLADM_STATUS_OK) 387 return (status); 388 389 qsort(vnic_ids, state.as_nslots, sizeof (uint_t), 390 i_dladm_vnic_compare); 391 392 /* 393 * Find a gap in the sequence of existing VNIC ids. 394 */ 395 last_id = DLADM_VNIC_MIN_VNIC_AUTO_ID - 1; 396 vnic_id = 0; 397 for (i = 0; i < state.as_nslots; i++) { 398 if (vnic_ids[i] > (last_id + 1)) { 399 vnic_id = last_id + 1; 400 break; 401 } 402 last_id = vnic_ids[i]; 403 } 404 405 if (vnic_id == 0) { 406 /* 407 * Did not find a gap between existing entries, see if we 408 * can add one. 409 */ 410 if (last_id + 1 > DLADM_VNIC_MAX_VNIC_AUTO_ID) 411 return (DLADM_STATUS_AUTOIDNOAVAILABLEID); 412 413 /* still have room for one more VNIC */ 414 vnic_id = last_id + 1; 415 } 416 417 *vnic_id_out = vnic_id; 418 419 return (DLADM_STATUS_OK); 420 } 421 422 /* 423 * Create a new VNIC. Update the configuration file and bring it up. 424 */ 425 dladm_status_t 426 dladm_vnic_create(uint_t vnic_id, char *dev_name, 427 vnic_mac_addr_type_t mac_addr_type, uchar_t *mac_addr, int mac_len, 428 uint_t *vnic_id_out, uint32_t flags) 429 { 430 dladm_vnic_attr_db_t attr; 431 int i; 432 boolean_t tempop = ((flags & DLADM_VNIC_OPT_TEMP) != 0); 433 boolean_t autoid = ((flags & DLADM_VNIC_OPT_AUTOID) != 0); 434 dladm_vnic_up_t up; 435 dladm_status_t status; 436 437 /* 438 * Sanity test arguments. 439 */ 440 if (autoid && !tempop) 441 return (DLADM_STATUS_AUTOIDNOTEMP); 442 443 if (!autoid && ((vnic_id < DLADM_VNIC_MIN_VNIC_SPEC_ID) || 444 (vnic_id > DLADM_VNIC_MAX_VNIC_SPEC_ID))) 445 return (DLADM_STATUS_INVALIDID); 446 447 if (mac_len > MAXMACADDRLEN) 448 return (DLADM_STATUS_INVALIDMACADDRLEN); 449 450 for (i = 0; i < NADDR_TYPES; i++) { 451 if (mac_addr_type == addr_types[i].va_type) 452 break; 453 } 454 if (i == NADDR_TYPES) 455 return (DLADM_STATUS_INVALIDMACADDRTYPE); 456 457 /* for now, only temporary creations are supported */ 458 if (!tempop) 459 return (dladm_errno2status(ENOTSUP)); 460 461 auto_again: 462 if (autoid) { 463 /* 464 * Find an unused VNIC id. 465 */ 466 status = i_dladm_vnic_get_auto_id(&attr, vnic_id_out); 467 if (status != DLADM_STATUS_OK) 468 return (status); 469 vnic_id = *vnic_id_out; 470 } 471 472 bzero(&attr, sizeof (attr)); 473 attr.vt_vnic_id = vnic_id; 474 (void) strncpy(attr.vt_dev_name, dev_name, 475 sizeof (attr.vt_dev_name) - 1); 476 attr.vt_mac_addr_type = mac_addr_type; 477 attr.vt_mac_len = mac_len; 478 bcopy(mac_addr, attr.vt_mac_addr, mac_len); 479 480 up.vu_vnic_id = vnic_id; 481 up.vu_found = B_FALSE; 482 up.vu_fd = open(VNIC_DEV, O_RDWR); 483 if (up.vu_fd < 0) 484 return (dladm_errno2status(errno)); 485 486 status = i_dladm_vnic_up((void *)&up, &attr); 487 (void) close(up.vu_fd); 488 489 if ((status == DLADM_STATUS_EXIST) && autoid) 490 goto auto_again; 491 492 return (status); 493 } 494 495 /* 496 * Modify the properties of a VNIC. 497 */ 498 dladm_status_t 499 dladm_vnic_modify(uint_t vnic_id, uint32_t modify_mask, 500 vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr, 501 uint32_t flags) 502 { 503 dladm_vnic_modify_attr_t new_attr; 504 boolean_t tempop = ((flags & DLADM_VNIC_OPT_TEMP) != 0); 505 506 if ((vnic_id < DLADM_VNIC_MIN_VNIC_ID) || 507 (vnic_id > DLADM_VNIC_MAX_VNIC_ID)) 508 return (DLADM_STATUS_INVALIDID); 509 510 /* for now, only temporary creations are supported */ 511 if (!tempop) 512 return (dladm_errno2status(ENOTSUP)); 513 514 bzero(&new_attr, sizeof (new_attr)); 515 516 if (modify_mask & DLADM_VNIC_MODIFY_ADDR) { 517 new_attr.vm_mac_addr_type = mac_addr_type; 518 new_attr.vm_mac_len = mac_len; 519 bcopy(mac_addr, new_attr.vm_mac_addr, MAXMACADDRLEN); 520 } 521 522 /* update the properties of the existing VNIC */ 523 return (i_dladm_vnic_modify_sys(vnic_id, modify_mask, &new_attr)); 524 } 525 526 527 /* 528 * Delete a VNIC. 529 */ 530 dladm_status_t 531 dladm_vnic_delete(uint_t vnic_id, uint32_t flags) 532 { 533 boolean_t tempop = ((flags & DLADM_VNIC_OPT_TEMP) != 0); 534 dladm_vnic_down_t down; 535 dladm_vnic_attr_sys_t sys_attr; 536 537 if ((vnic_id < DLADM_VNIC_MIN_VNIC_ID) || 538 (vnic_id > DLADM_VNIC_MAX_VNIC_ID)) 539 return (DLADM_STATUS_INVALIDID); 540 541 /* for now, only temporary deletes are supported */ 542 if (!tempop) 543 return (dladm_errno2status(ENOTSUP)); 544 545 down.vd_vnic_id = vnic_id; 546 down.vd_found = B_FALSE; 547 sys_attr.va_vnic_id = vnic_id; 548 return (i_dladm_vnic_down((void *)&down, &sys_attr)); 549 } 550