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) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 #include <stdio.h>
  26 #include <sys/types.h>
  27 #include <sys/stat.h>
  28 #include <string.h>
  29 #include <fcntl.h>
  30 #include <unistd.h>
  31 #include <stropts.h>
  32 #include <stdlib.h>
  33 #include <errno.h>
  34 #include <strings.h>
  35 #include <libintl.h>
  36 #include <net/if_types.h>
  37 #include <net/if_dl.h>
  38 #include <sys/dld.h>
  39 #include <libdladm_impl.h>
  40 #include <libvrrpadm.h>
  41 #include <libdllink.h>
  42 #include <libdlbridge.h>
  43 #include <libdlvnic.h>
  44 
  45 /*
  46  * VNIC administration library.
  47  */
  48 
  49 /*
  50  * Default random MAC address prefix (locally administered).
  51  */
  52 static char dladm_vnic_def_prefix[] = {0x02, 0x08, 0x20};
  53 
  54 static dladm_status_t   dladm_vnic_persist_conf(dladm_handle_t,
  55                             const char *name, dladm_vnic_attr_t *,
  56                             datalink_class_t);
  57 static const char       *dladm_vnic_macaddr2str(const uchar_t *, char *);
  58 static dladm_status_t   dladm_vnic_str2macaddr(const char *, uchar_t *);
  59 
  60 /*
  61  * Convert a diagnostic returned by the kernel into a dladm_status_t.
  62  */
  63 static dladm_status_t
  64 dladm_vnic_diag2status(vnic_ioc_diag_t ioc_diag)
  65 {
  66         switch (ioc_diag) {
  67         case VNIC_IOC_DIAG_NONE:
  68                 return (DLADM_STATUS_OK);
  69         case VNIC_IOC_DIAG_MACADDRLEN_INVALID:
  70                 return (DLADM_STATUS_INVALIDMACADDRLEN);
  71         case VNIC_IOC_DIAG_MACADDR_NIC:
  72                 return (DLADM_STATUS_INVALIDMACADDRNIC);
  73         case VNIC_IOC_DIAG_MACADDR_INUSE:
  74                 return (DLADM_STATUS_INVALIDMACADDRINUSE);
  75         case VNIC_IOC_DIAG_MACFACTORYSLOTINVALID:
  76                 return (DLADM_STATUS_MACFACTORYSLOTINVALID);
  77         case VNIC_IOC_DIAG_MACFACTORYSLOTUSED:
  78                 return (DLADM_STATUS_MACFACTORYSLOTUSED);
  79         case VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED:
  80                 return (DLADM_STATUS_MACFACTORYSLOTALLUSED);
  81         case VNIC_IOC_DIAG_MACFACTORYNOTSUP:
  82                 return (DLADM_STATUS_MACFACTORYNOTSUP);
  83         case VNIC_IOC_DIAG_MACPREFIX_INVALID:
  84                 return (DLADM_STATUS_INVALIDMACPREFIX);
  85         case VNIC_IOC_DIAG_MACPREFIXLEN_INVALID:
  86                 return (DLADM_STATUS_INVALIDMACPREFIXLEN);
  87         case VNIC_IOC_DIAG_MACMARGIN_INVALID:
  88                 return (DLADM_STATUS_INVALID_MACMARGIN);
  89         case VNIC_IOC_DIAG_NO_HWRINGS:
  90                 return (DLADM_STATUS_NO_HWRINGS);
  91         case VNIC_IOC_DIAG_MACADDR_INVALID:
  92                 return (DLADM_STATUS_INVALIDMACADDR);
  93         case VNIC_IOC_DIAG_MACMTU_INVALID:
  94                 return (DLADM_STATUS_INVALID_MTU);
  95         default:
  96                 return (DLADM_STATUS_FAILED);
  97         }
  98 }
  99 
 100 /*
 101  * Send a create command to the VNIC driver.
 102  */
 103 dladm_status_t
 104 i_dladm_vnic_create_sys(dladm_handle_t handle, dladm_vnic_attr_t *attr)
 105 {
 106         int rc;
 107         vnic_ioc_create_t ioc;
 108         dladm_status_t status = DLADM_STATUS_OK;
 109 
 110         bzero(&ioc, sizeof (ioc));
 111         ioc.vc_vnic_id = attr->va_vnic_id;
 112         ioc.vc_link_id = attr->va_link_id;
 113         ioc.vc_mac_addr_type = attr->va_mac_addr_type;
 114         ioc.vc_mac_len = attr->va_mac_len;
 115         ioc.vc_mac_slot = attr->va_mac_slot;
 116         ioc.vc_mac_prefix_len = attr->va_mac_prefix_len;
 117         ioc.vc_vid = attr->va_vid;
 118         ioc.vc_vrid = attr->va_vrid;
 119         ioc.vc_af = attr->va_af;
 120         ioc.vc_flags = attr->va_force ? VNIC_IOC_CREATE_FORCE : 0;
 121 
 122         if (attr->va_mac_len > 0 || ioc.vc_mac_prefix_len > 0)
 123                 bcopy(attr->va_mac_addr, ioc.vc_mac_addr, MAXMACADDRLEN);
 124         bcopy(&attr->va_resource_props, &ioc.vc_resource_props,
 125             sizeof (mac_resource_props_t));
 126         if (attr->va_link_id == DATALINK_INVALID_LINKID)
 127                 ioc.vc_flags |= VNIC_IOC_CREATE_ANCHOR;
 128 
 129         rc = ioctl(dladm_dld_fd(handle), VNIC_IOC_CREATE, &ioc);
 130         if (rc < 0)
 131                 status = dladm_errno2status(errno);
 132 
 133         if (status != DLADM_STATUS_OK) {
 134                 if (ioc.vc_diag != VNIC_IOC_DIAG_NONE)
 135                         status = dladm_vnic_diag2status(ioc.vc_diag);
 136         }
 137         if (status != DLADM_STATUS_OK)
 138                 return (status);
 139 
 140         attr->va_mac_addr_type = ioc.vc_mac_addr_type;
 141         switch (ioc.vc_mac_addr_type) {
 142         case VNIC_MAC_ADDR_TYPE_FACTORY:
 143                 attr->va_mac_slot = ioc.vc_mac_slot;
 144                 break;
 145         case VNIC_MAC_ADDR_TYPE_RANDOM:
 146                 bcopy(ioc.vc_mac_addr, attr->va_mac_addr, MAXMACADDRLEN);
 147                 attr->va_mac_len = ioc.vc_mac_len;
 148                 break;
 149         }
 150         return (status);
 151 }
 152 
 153 /*
 154  * Get the configuration information of the given VNIC.
 155  */
 156 static dladm_status_t
 157 i_dladm_vnic_info_active(dladm_handle_t handle, datalink_id_t linkid,
 158     dladm_vnic_attr_t *attrp)
 159 {
 160         vnic_ioc_info_t ioc;
 161         vnic_info_t *vnic;
 162         int rc;
 163         dladm_status_t status = DLADM_STATUS_OK;
 164 
 165         bzero(&ioc, sizeof (ioc));
 166         vnic = &ioc.vi_info;
 167         vnic->vn_vnic_id = linkid;
 168 
 169         rc = ioctl(dladm_dld_fd(handle), VNIC_IOC_INFO, &ioc);
 170         if (rc != 0) {
 171                 status = dladm_errno2status(errno);
 172                 goto bail;
 173         }
 174 
 175         attrp->va_vnic_id = vnic->vn_vnic_id;
 176         attrp->va_link_id = vnic->vn_link_id;
 177         attrp->va_mac_addr_type = vnic->vn_mac_addr_type;
 178         bcopy(vnic->vn_mac_addr, attrp->va_mac_addr, MAXMACADDRLEN);
 179         attrp->va_mac_len = vnic->vn_mac_len;
 180         attrp->va_mac_slot = vnic->vn_mac_slot;
 181         attrp->va_mac_prefix_len = vnic->vn_mac_prefix_len;
 182         attrp->va_vid = vnic->vn_vid;
 183         attrp->va_vrid = vnic->vn_vrid;
 184         attrp->va_af = vnic->vn_af;
 185         attrp->va_force = vnic->vn_force;
 186 
 187 bail:
 188         return (status);
 189 }
 190 
 191 static dladm_status_t
 192 i_dladm_vnic_info_persist(dladm_handle_t handle, datalink_id_t linkid,
 193     dladm_vnic_attr_t *attrp)
 194 {
 195         dladm_conf_t conf;
 196         dladm_status_t status;
 197         char macstr[ETHERADDRL * 3];
 198         char linkover[MAXLINKNAMELEN];
 199         uint64_t u64;
 200         datalink_class_t class;
 201 
 202         attrp->va_vnic_id = linkid;
 203         if ((status = dladm_getsnap_conf(handle, linkid, &conf)) !=
 204             DLADM_STATUS_OK)
 205                 return (status);
 206 
 207         status = dladm_get_conf_field(handle, conf, FLINKOVER, linkover,
 208             sizeof (linkover));
 209         if (status != DLADM_STATUS_OK) {
 210                 /*
 211                  * This isn't an error, etherstubs don't have a FLINKOVER
 212                  * property.
 213                  */
 214                 attrp->va_link_id = DATALINK_INVALID_LINKID;
 215         } else {
 216                 if ((status = dladm_name2info(handle, linkover,
 217                     &attrp->va_link_id, NULL, NULL, NULL)) != DLADM_STATUS_OK)
 218                         goto done;
 219         }
 220 
 221         if ((status = dladm_datalink_id2info(handle, linkid, NULL, &class,
 222             NULL, NULL, 0)) != DLADM_STATUS_OK)
 223                 goto done;
 224 
 225         if (class == DATALINK_CLASS_VLAN) {
 226                 if (attrp->va_link_id == DATALINK_INVALID_LINKID) {
 227                         status = DLADM_STATUS_BADARG;
 228                         goto done;
 229                 }
 230                 attrp->va_mac_addr_type = VNIC_MAC_ADDR_TYPE_PRIMARY;
 231                 attrp->va_mac_len = 0;
 232         } else {
 233                 status = dladm_get_conf_field(handle, conf, FMADDRTYPE, &u64,
 234                     sizeof (u64));
 235                 if (status != DLADM_STATUS_OK)
 236                         goto done;
 237 
 238                 attrp->va_mac_addr_type = (vnic_mac_addr_type_t)u64;
 239 
 240                 if ((status = dladm_get_conf_field(handle, conf, FVRID,
 241                     &u64, sizeof (u64))) != DLADM_STATUS_OK) {
 242                         attrp->va_vrid = VRRP_VRID_NONE;
 243                 } else {
 244                         attrp->va_vrid = (vrid_t)u64;
 245                 }
 246 
 247                 if ((status = dladm_get_conf_field(handle, conf, FVRAF,
 248                     &u64, sizeof (u64))) != DLADM_STATUS_OK) {
 249                         attrp->va_af = AF_UNSPEC;
 250                 } else {
 251                         attrp->va_af = (int)u64;
 252                 }
 253 
 254                 status = dladm_get_conf_field(handle, conf, FMADDRLEN, &u64,
 255                     sizeof (u64));
 256                 attrp->va_mac_len = ((status == DLADM_STATUS_OK) ?
 257                     (uint_t)u64 : ETHERADDRL);
 258 
 259                 status = dladm_get_conf_field(handle, conf, FMADDRSLOT, &u64,
 260                     sizeof (u64));
 261                 attrp->va_mac_slot = ((status == DLADM_STATUS_OK) ?
 262                     (int)u64 : -1);
 263 
 264                 status = dladm_get_conf_field(handle, conf, FMADDRPREFIXLEN,
 265                     &u64, sizeof (u64));
 266                 attrp->va_mac_prefix_len = ((status == DLADM_STATUS_OK) ?
 267                     (uint_t)u64 : sizeof (dladm_vnic_def_prefix));
 268 
 269                 status = dladm_get_conf_field(handle, conf, FMACADDR, macstr,
 270                     sizeof (macstr));
 271                 if (status != DLADM_STATUS_OK)
 272                         goto done;
 273 
 274                 status = dladm_vnic_str2macaddr(macstr, attrp->va_mac_addr);
 275                 if (status != DLADM_STATUS_OK)
 276                         goto done;
 277         }
 278 
 279         status = dladm_get_conf_field(handle, conf, FVLANID, &u64,
 280             sizeof (u64));
 281         attrp->va_vid = ((status == DLADM_STATUS_OK) ?  (uint16_t)u64 : 0);
 282 
 283         status = DLADM_STATUS_OK;
 284 done:
 285         dladm_destroy_conf(handle, conf);
 286         return (status);
 287 }
 288 
 289 dladm_status_t
 290 dladm_vnic_info(dladm_handle_t handle, datalink_id_t linkid,
 291     dladm_vnic_attr_t *attrp, uint32_t flags)
 292 {
 293         if (flags == DLADM_OPT_ACTIVE)
 294                 return (i_dladm_vnic_info_active(handle, linkid, attrp));
 295         else if (flags == DLADM_OPT_PERSIST)
 296                 return (i_dladm_vnic_info_persist(handle, linkid, attrp));
 297         else
 298                 return (DLADM_STATUS_BADARG);
 299 }
 300 
 301 /*
 302  * Remove a VNIC from the kernel.
 303  */
 304 dladm_status_t
 305 i_dladm_vnic_delete_sys(dladm_handle_t handle, datalink_id_t linkid)
 306 {
 307         vnic_ioc_delete_t ioc;
 308         dladm_status_t status = DLADM_STATUS_OK;
 309         int rc;
 310 
 311         ioc.vd_vnic_id = linkid;
 312 
 313         rc = ioctl(dladm_dld_fd(handle), VNIC_IOC_DELETE, &ioc);
 314         if (rc < 0)
 315                 status = dladm_errno2status(errno);
 316 
 317         return (status);
 318 }
 319 
 320 /*
 321  * Convert between MAC address types and their string representations.
 322  */
 323 
 324 typedef struct dladm_vnic_addr_type_s {
 325         const char              *va_str;
 326         vnic_mac_addr_type_t    va_type;
 327 } dladm_vnic_addr_type_t;
 328 
 329 static dladm_vnic_addr_type_t addr_types[] = {
 330         {"fixed", VNIC_MAC_ADDR_TYPE_FIXED},
 331         {"random", VNIC_MAC_ADDR_TYPE_RANDOM},
 332         {"factory", VNIC_MAC_ADDR_TYPE_FACTORY},
 333         {"auto", VNIC_MAC_ADDR_TYPE_AUTO},
 334         {"fixed", VNIC_MAC_ADDR_TYPE_PRIMARY},
 335         {"vrrp", VNIC_MAC_ADDR_TYPE_VRID}
 336 };
 337 
 338 #define NADDR_TYPES (sizeof (addr_types) / sizeof (dladm_vnic_addr_type_t))
 339 
 340 static const char *
 341 dladm_vnic_macaddrtype2str(vnic_mac_addr_type_t type)
 342 {
 343         int i;
 344 
 345         for (i = 0; i < NADDR_TYPES; i++) {
 346                 if (type == addr_types[i].va_type)
 347                         return (addr_types[i].va_str);
 348         }
 349         return (NULL);
 350 }
 351 
 352 dladm_status_t
 353 dladm_vnic_str2macaddrtype(const char *str, vnic_mac_addr_type_t *val)
 354 {
 355         int i;
 356         dladm_vnic_addr_type_t *type;
 357 
 358         for (i = 0; i < NADDR_TYPES; i++) {
 359                 type = &addr_types[i];
 360                 if (strncmp(str, type->va_str, strlen(type->va_str)) == 0) {
 361                         *val = type->va_type;
 362                         return (DLADM_STATUS_OK);
 363                 }
 364         }
 365         return (DLADM_STATUS_BADARG);
 366 }
 367 
 368 /*
 369  * Based on the VRRP specification, the virtual router MAC address associated
 370  * with a virtual router is an IEEE 802 MAC address in the following format:
 371  *
 372  * IPv4 case: 00-00-5E-00-01-{VRID} (in hex in internet standard bit-order)
 373  *
 374  * IPv6 case: 00-00-5E-00-02-{VRID} (in hex in internet standard bit-order)
 375  */
 376 static dladm_status_t
 377 i_dladm_vnic_vrrp_mac(vrid_t vrid, int af, uint8_t *mac, uint_t maclen)
 378 {
 379         if (maclen < ETHERADDRL || vrid < VRRP_VRID_MIN ||
 380             vrid > VRRP_VRID_MAX || (af != AF_INET && af != AF_INET6)) {
 381                 return (DLADM_STATUS_BADARG);
 382         }
 383 
 384         mac[0] = mac[1] = mac[3] = 0x0;
 385         mac[2] = 0x5e;
 386         mac[4] = (af == AF_INET) ? 0x01 : 0x02;
 387         mac[5] = vrid;
 388         return (DLADM_STATUS_OK);
 389 }
 390 
 391 /*
 392  * Create a new VNIC / VLAN. Update the configuration file and bring it up.
 393  * The "vrid" and "af" arguments are only required if the mac_addr_type is
 394  * VNIC_MAC_ADDR_TYPE_VRID. In that case, the MAC address will be caculated
 395  * based on the above algorithm.
 396  */
 397 dladm_status_t
 398 dladm_vnic_create(dladm_handle_t handle, const char *vnic, datalink_id_t linkid,
 399     vnic_mac_addr_type_t mac_addr_type, uchar_t *mac_addr, uint_t mac_len,
 400     int *mac_slot, uint_t mac_prefix_len, uint16_t vid, vrid_t vrid,
 401     int af, datalink_id_t *vnic_id_out, dladm_arg_list_t *proplist,
 402     uint32_t flags)
 403 {
 404         dladm_vnic_attr_t attr;
 405         datalink_id_t vnic_id;
 406         datalink_class_t class;
 407         uint32_t media = DL_ETHER;
 408         char name[MAXLINKNAMELEN];
 409         uchar_t tmp_addr[MAXMACADDRLEN];
 410         dladm_status_t status;
 411         boolean_t is_vlan;
 412         boolean_t is_etherstub;
 413         int i;
 414         boolean_t vnic_created = B_FALSE;
 415         boolean_t conf_set = B_FALSE;
 416 
 417         /*
 418          * Sanity test arguments.
 419          */
 420         if ((flags & DLADM_OPT_ACTIVE) == 0)
 421                 return (DLADM_STATUS_NOTSUP);
 422 
 423         is_vlan = ((flags & DLADM_OPT_VLAN) != 0);
 424         if (is_vlan && ((vid < 1 || vid > 4094)))
 425                 return (DLADM_STATUS_VIDINVAL);
 426 
 427         is_etherstub = (linkid == DATALINK_INVALID_LINKID);
 428 
 429         if (!dladm_vnic_macaddrtype2str(mac_addr_type))
 430                 return (DLADM_STATUS_INVALIDMACADDRTYPE);
 431 
 432         if ((flags & DLADM_OPT_ANCHOR) == 0) {
 433                 if ((status = dladm_datalink_id2info(handle, linkid, NULL,
 434                     &class, &media, NULL, 0)) != DLADM_STATUS_OK)
 435                         return (status);
 436 
 437                 if (class == DATALINK_CLASS_VNIC ||
 438                     class == DATALINK_CLASS_VLAN)
 439                         return (DLADM_STATUS_BADARG);
 440         } else {
 441                 /* it's an anchor VNIC */
 442                 if (linkid != DATALINK_INVALID_LINKID || vid != 0)
 443                         return (DLADM_STATUS_BADARG);
 444         }
 445 
 446         /*
 447          * Only VRRP VNIC need VRID and address family specified.
 448          */
 449         if (mac_addr_type != VNIC_MAC_ADDR_TYPE_VRID &&
 450             (af != AF_UNSPEC || vrid != VRRP_VRID_NONE)) {
 451                 return (DLADM_STATUS_BADARG);
 452         }
 453 
 454         /*
 455          * If a random address might be generated, but no prefix
 456          * was specified by the caller, use the default MAC address
 457          * prefix.
 458          */
 459         if ((mac_addr_type == VNIC_MAC_ADDR_TYPE_RANDOM ||
 460             mac_addr_type == VNIC_MAC_ADDR_TYPE_AUTO) &&
 461             mac_prefix_len == 0) {
 462                 mac_prefix_len = sizeof (dladm_vnic_def_prefix);
 463                 mac_addr = tmp_addr;
 464                 bcopy(dladm_vnic_def_prefix, mac_addr, mac_prefix_len);
 465         }
 466 
 467         /*
 468          * If this is a VRRP VNIC, generate its MAC address using the given
 469          * VRID and address family.
 470          */
 471         if (mac_addr_type == VNIC_MAC_ADDR_TYPE_VRID) {
 472                 /*
 473                  * VRRP VNICs must be created over ethernet data-links.
 474                  */
 475                 if (vrid < VRRP_VRID_MIN || vrid > VRRP_VRID_MAX ||
 476                     (af != AF_INET && af != AF_INET6) || mac_addr != NULL ||
 477                     mac_len != 0 || mac_prefix_len != 0 ||
 478                     (mac_slot != NULL && *mac_slot != -1) || is_etherstub ||
 479                     media != DL_ETHER) {
 480                         return (DLADM_STATUS_BADARG);
 481                 }
 482                 mac_len = ETHERADDRL;
 483                 mac_addr = tmp_addr;
 484                 status = i_dladm_vnic_vrrp_mac(vrid, af, mac_addr, mac_len);
 485                 if (status != DLADM_STATUS_OK)
 486                         return (status);
 487         }
 488 
 489         if (mac_len > MAXMACADDRLEN)
 490                 return (DLADM_STATUS_INVALIDMACADDRLEN);
 491 
 492         if (vnic == NULL) {
 493                 flags |= DLADM_OPT_PREFIX;
 494                 (void) strlcpy(name, "vnic", sizeof (name));
 495         } else {
 496                 (void) strlcpy(name, vnic, sizeof (name));
 497         }
 498 
 499         class = is_vlan ? DATALINK_CLASS_VLAN :
 500             (is_etherstub ? DATALINK_CLASS_ETHERSTUB : DATALINK_CLASS_VNIC);
 501         if ((status = dladm_create_datalink_id(handle, name, class,
 502             media, flags, &vnic_id)) != DLADM_STATUS_OK)
 503                 return (status);
 504 
 505         if ((flags & DLADM_OPT_PREFIX) != 0) {
 506                 (void) snprintf(name + 4, sizeof (name), "%llu", vnic_id);
 507                 flags &= ~DLADM_OPT_PREFIX;
 508         }
 509 
 510         bzero(&attr, sizeof (attr));
 511 
 512         /* Extract resource_ctl and cpu_list from proplist */
 513         if (proplist != NULL) {
 514                 status = dladm_link_proplist_extract(handle, proplist,
 515                     &attr.va_resource_props, 0);
 516                 if (status != DLADM_STATUS_OK)
 517                         goto done;
 518         }
 519 
 520         attr.va_vnic_id = vnic_id;
 521         attr.va_link_id = linkid;
 522         attr.va_mac_addr_type = mac_addr_type;
 523         attr.va_mac_len = mac_len;
 524         if (mac_slot != NULL)
 525                 attr.va_mac_slot = *mac_slot;
 526         if (mac_len > 0)
 527                 bcopy(mac_addr, attr.va_mac_addr, mac_len);
 528         else if (mac_prefix_len > 0)
 529                 bcopy(mac_addr, attr.va_mac_addr, mac_prefix_len);
 530         attr.va_mac_prefix_len = mac_prefix_len;
 531         attr.va_vid = vid;
 532         attr.va_vrid = vrid;
 533         attr.va_af = af;
 534         attr.va_force = (flags & DLADM_OPT_FORCE) != 0;
 535 
 536         status = i_dladm_vnic_create_sys(handle, &attr);
 537         if (status != DLADM_STATUS_OK)
 538                 goto done;
 539         vnic_created = B_TRUE;
 540 
 541         /* Save vnic configuration and its properties */
 542         if (!(flags & DLADM_OPT_PERSIST))
 543                 goto done;
 544 
 545         status = dladm_vnic_persist_conf(handle, name, &attr, class);
 546         if (status != DLADM_STATUS_OK)
 547                 goto done;
 548         conf_set = B_TRUE;
 549 
 550         if (proplist != NULL) {
 551                 for (i = 0; i < proplist->al_count; i++) {
 552                         dladm_arg_info_t        *aip = &proplist->al_info[i];
 553 
 554                         status = dladm_set_linkprop(handle, vnic_id,
 555                             aip->ai_name, aip->ai_val, aip->ai_count,
 556                             DLADM_OPT_PERSIST);
 557                         if (status != DLADM_STATUS_OK)
 558                                 break;
 559                 }
 560         }
 561 
 562 done:
 563         if (status != DLADM_STATUS_OK) {
 564                 if (conf_set)
 565                         (void) dladm_remove_conf(handle, vnic_id);
 566                 if (vnic_created)
 567                         (void) i_dladm_vnic_delete_sys(handle, vnic_id);
 568                 (void) dladm_destroy_datalink_id(handle, vnic_id, flags);
 569         } else {
 570                 if (vnic_id_out != NULL)
 571                         *vnic_id_out = vnic_id;
 572                 if (mac_slot != NULL)
 573                         *mac_slot = attr.va_mac_slot;
 574         }
 575 
 576         if (is_vlan) {
 577                 dladm_status_t stat2;
 578 
 579                 stat2 = dladm_bridge_refresh(handle, linkid);
 580                 if (status == DLADM_STATUS_OK && stat2 != DLADM_STATUS_OK)
 581                         status = stat2;
 582         }
 583         return (status);
 584 }
 585 
 586 /*
 587  * Delete a VNIC / VLAN.
 588  */
 589 dladm_status_t
 590 dladm_vnic_delete(dladm_handle_t handle, datalink_id_t linkid, uint32_t flags)
 591 {
 592         dladm_status_t status;
 593         datalink_class_t class;
 594 
 595         if (flags == 0)
 596                 return (DLADM_STATUS_BADARG);
 597 
 598         if ((dladm_datalink_id2info(handle, linkid, NULL, &class, NULL, NULL, 0)
 599             != DLADM_STATUS_OK))
 600                 return (DLADM_STATUS_BADARG);
 601 
 602         if ((flags & DLADM_OPT_VLAN) != 0) {
 603                 if (class != DATALINK_CLASS_VLAN)
 604                         return (DLADM_STATUS_BADARG);
 605         } else {
 606                 if (class != DATALINK_CLASS_VNIC &&
 607                     class != DATALINK_CLASS_ETHERSTUB)
 608                         return (DLADM_STATUS_BADARG);
 609         }
 610 
 611         if ((flags & DLADM_OPT_ACTIVE) != 0) {
 612                 status = i_dladm_vnic_delete_sys(handle, linkid);
 613                 if (status == DLADM_STATUS_OK) {
 614                         (void) dladm_set_linkprop(handle, linkid, NULL, NULL, 0,
 615                             DLADM_OPT_ACTIVE);
 616                         (void) dladm_destroy_datalink_id(handle, linkid,
 617                             DLADM_OPT_ACTIVE);
 618                 } else if (status != DLADM_STATUS_NOTFOUND ||
 619                     !(flags & DLADM_OPT_PERSIST)) {
 620                         return (status);
 621                 }
 622         }
 623         if ((flags & DLADM_OPT_PERSIST) != 0) {
 624                 (void) dladm_remove_conf(handle, linkid);
 625                 (void) dladm_destroy_datalink_id(handle, linkid,
 626                     DLADM_OPT_PERSIST);
 627         }
 628         return (dladm_bridge_refresh(handle, linkid));
 629 }
 630 
 631 static const char *
 632 dladm_vnic_macaddr2str(const uchar_t *mac, char *buf)
 633 {
 634         static char unknown_mac[] = {0, 0, 0, 0, 0, 0};
 635 
 636         if (buf == NULL)
 637                 return (NULL);
 638 
 639         if (bcmp(unknown_mac, mac, ETHERADDRL) == 0)
 640                 (void) strlcpy(buf, "unknown", DLADM_STRSIZE);
 641         else
 642                 return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER));
 643 
 644         return (buf);
 645 }
 646 
 647 static dladm_status_t
 648 dladm_vnic_str2macaddr(const char *str, uchar_t *buf)
 649 {
 650         int len = 0;
 651         uchar_t *b = _link_aton(str, &len);
 652 
 653         if (b == NULL || len >= MAXMACADDRLEN)
 654                 return (DLADM_STATUS_BADARG);
 655 
 656         bcopy(b, buf, len);
 657         free(b);
 658         return (DLADM_STATUS_OK);
 659 }
 660 
 661 
 662 static dladm_status_t
 663 dladm_vnic_persist_conf(dladm_handle_t handle, const char *name,
 664     dladm_vnic_attr_t *attrp, datalink_class_t class)
 665 {
 666         dladm_conf_t conf;
 667         dladm_status_t status;
 668         char macstr[ETHERADDRL * 3];
 669         char linkover[MAXLINKNAMELEN];
 670         uint64_t u64;
 671 
 672         if ((status = dladm_create_conf(handle, name, attrp->va_vnic_id,
 673             class, DL_ETHER, &conf)) != DLADM_STATUS_OK)
 674                 return (status);
 675 
 676         if (attrp->va_link_id != DATALINK_INVALID_LINKID) {
 677                 status = dladm_datalink_id2info(handle, attrp->va_link_id, NULL,
 678                     NULL, NULL, linkover, sizeof (linkover));
 679                 if (status != DLADM_STATUS_OK)
 680                         goto done;
 681                 status = dladm_set_conf_field(handle, conf, FLINKOVER,
 682                     DLADM_TYPE_STR, linkover);
 683                 if (status != DLADM_STATUS_OK)
 684                         goto done;
 685         }
 686 
 687         if (class != DATALINK_CLASS_VLAN) {
 688                 u64 = attrp->va_mac_addr_type;
 689                 status = dladm_set_conf_field(handle, conf, FMADDRTYPE,
 690                     DLADM_TYPE_UINT64, &u64);
 691                 if (status != DLADM_STATUS_OK)
 692                         goto done;
 693 
 694                 u64 = attrp->va_vrid;
 695                 status = dladm_set_conf_field(handle, conf, FVRID,
 696                     DLADM_TYPE_UINT64, &u64);
 697                 if (status != DLADM_STATUS_OK)
 698                         goto done;
 699 
 700                 u64 = attrp->va_af;
 701                 status = dladm_set_conf_field(handle, conf, FVRAF,
 702                     DLADM_TYPE_UINT64, &u64);
 703                 if (status != DLADM_STATUS_OK)
 704                         goto done;
 705 
 706                 if (attrp->va_mac_len != ETHERADDRL) {
 707                         u64 = attrp->va_mac_len;
 708                         status = dladm_set_conf_field(handle, conf, FMADDRLEN,
 709                             DLADM_TYPE_UINT64, &u64);
 710                         if (status != DLADM_STATUS_OK)
 711                                 goto done;
 712                 }
 713 
 714                 if (attrp->va_mac_slot != -1) {
 715                         u64 = attrp->va_mac_slot;
 716                         status = dladm_set_conf_field(handle, conf,
 717                             FMADDRSLOT, DLADM_TYPE_UINT64, &u64);
 718                         if (status != DLADM_STATUS_OK)
 719                         goto done;
 720                 }
 721 
 722                 if (attrp->va_mac_prefix_len !=
 723                     sizeof (dladm_vnic_def_prefix)) {
 724                         u64 = attrp->va_mac_prefix_len;
 725                         status = dladm_set_conf_field(handle, conf,
 726                             FMADDRPREFIXLEN, DLADM_TYPE_UINT64, &u64);
 727                         if (status != DLADM_STATUS_OK)
 728                                 goto done;
 729                 }
 730 
 731                 (void) dladm_vnic_macaddr2str(attrp->va_mac_addr, macstr);
 732                 status = dladm_set_conf_field(handle, conf, FMACADDR,
 733                     DLADM_TYPE_STR, macstr);
 734                 if (status != DLADM_STATUS_OK)
 735                         goto done;
 736         }
 737 
 738         if (attrp->va_vid != 0) {
 739                 u64 = attrp->va_vid;
 740                 status = dladm_set_conf_field(handle, conf, FVLANID,
 741                     DLADM_TYPE_UINT64, &u64);
 742                 if (status != DLADM_STATUS_OK)
 743                         goto done;
 744         }
 745 
 746         /*
 747          * Commit the link configuration.
 748          */
 749         status = dladm_write_conf(handle, conf);
 750 
 751 done:
 752         dladm_destroy_conf(handle, conf);
 753         return (status);
 754 }
 755 
 756 typedef struct dladm_vnic_up_arg_s {
 757         uint32_t        flags;
 758         dladm_status_t  status;
 759 } dladm_vnic_up_arg_t;
 760 
 761 static int
 762 i_dladm_vnic_up(dladm_handle_t handle, datalink_id_t linkid, void *arg)
 763 {
 764         dladm_status_t *statusp = &(((dladm_vnic_up_arg_t *)arg)->status);
 765         dladm_vnic_attr_t attr;
 766         dladm_status_t status;
 767         dladm_arg_list_t *proplist;
 768 
 769         bzero(&attr, sizeof (attr));
 770 
 771         status = dladm_vnic_info(handle, linkid, &attr, DLADM_OPT_PERSIST);
 772         if (status != DLADM_STATUS_OK)
 773                 goto done;
 774 
 775         /* Get all properties for this vnic */
 776         status = dladm_link_get_proplist(handle, linkid, &proplist);
 777         if (status != DLADM_STATUS_OK)
 778                 goto done;
 779 
 780         if (proplist != NULL) {
 781                 status = dladm_link_proplist_extract(handle, proplist,
 782                     &attr.va_resource_props, DLADM_OPT_BOOT);
 783         }
 784 
 785         status = i_dladm_vnic_create_sys(handle, &attr);
 786         if (status == DLADM_STATUS_OK) {
 787                 status = dladm_up_datalink_id(handle, linkid);
 788                 if (status != DLADM_STATUS_OK)
 789                         (void) i_dladm_vnic_delete_sys(handle, linkid);
 790         }
 791 
 792 done:
 793         *statusp = status;
 794         return (DLADM_WALK_CONTINUE);
 795 }
 796 
 797 dladm_status_t
 798 dladm_vnic_up(dladm_handle_t handle, datalink_id_t linkid, uint32_t flags)
 799 {
 800         dladm_vnic_up_arg_t vnic_arg;
 801         datalink_class_t class;
 802 
 803         class = ((flags & DLADM_OPT_VLAN) != 0) ? DATALINK_CLASS_VLAN :
 804             (DATALINK_CLASS_VNIC | DATALINK_CLASS_ETHERSTUB);
 805 
 806         if (linkid == DATALINK_ALL_LINKID) {
 807                 (void) dladm_walk_datalink_id(i_dladm_vnic_up, handle,
 808                     &vnic_arg, class, DATALINK_ANY_MEDIATYPE,
 809                     DLADM_OPT_PERSIST);
 810                 return (DLADM_STATUS_OK);
 811         } else {
 812                 (void) i_dladm_vnic_up(handle, linkid, &vnic_arg);
 813                 return (vnic_arg.status);
 814         }
 815 }