15084Sjohnlev /* 25084Sjohnlev * CDDL HEADER START 35084Sjohnlev * 45084Sjohnlev * The contents of this file are subject to the terms of the 55084Sjohnlev * Common Development and Distribution License (the "License"). 65084Sjohnlev * You may not use this file except in compliance with the License. 75084Sjohnlev * 85084Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 95084Sjohnlev * or http://www.opensolaris.org/os/licensing. 105084Sjohnlev * See the License for the specific language governing permissions 115084Sjohnlev * and limitations under the License. 125084Sjohnlev * 135084Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each 145084Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 155084Sjohnlev * If applicable, add the following below this CDDL HEADER, with the 165084Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying 175084Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner] 185084Sjohnlev * 195084Sjohnlev * CDDL HEADER END 205084Sjohnlev */ 215084Sjohnlev /* 225895Syz147064 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 235084Sjohnlev * Use is subject to license terms. 245084Sjohnlev */ 255084Sjohnlev 265084Sjohnlev #include <stdio.h> 275084Sjohnlev #include <sys/types.h> 285084Sjohnlev #include <sys/stat.h> 295084Sjohnlev #include <string.h> 305084Sjohnlev #include <fcntl.h> 315084Sjohnlev #include <unistd.h> 325084Sjohnlev #include <stropts.h> 335084Sjohnlev #include <stdlib.h> 345084Sjohnlev #include <errno.h> 355084Sjohnlev #include <strings.h> 365084Sjohnlev #include <libintl.h> 375084Sjohnlev #include <net/if_types.h> 385084Sjohnlev #include <net/if_dl.h> 39*8275SEric Cheng #include <sys/dld.h> 405084Sjohnlev #include <libdladm_impl.h> 415895Syz147064 #include <libdllink.h> 425084Sjohnlev #include <libdlvnic.h> 435084Sjohnlev 445084Sjohnlev /* 455084Sjohnlev * VNIC administration library. 465084Sjohnlev */ 475084Sjohnlev 48*8275SEric Cheng /* 49*8275SEric Cheng * Default random MAC address prefix (locally administered). 50*8275SEric Cheng */ 51*8275SEric Cheng static char dladm_vnic_def_prefix[] = {0x02, 0x08, 0x20}; 52*8275SEric Cheng 53*8275SEric Cheng static dladm_status_t dladm_vnic_persist_conf(const char *name, 54*8275SEric Cheng dladm_vnic_attr_t *, datalink_class_t); 55*8275SEric Cheng static const char *dladm_vnic_macaddr2str(const uchar_t *, char *); 56*8275SEric Cheng static dladm_status_t dladm_vnic_str2macaddr(const char *, uchar_t *); 575084Sjohnlev 58*8275SEric Cheng /* 59*8275SEric Cheng * Convert a diagnostic returned by the kernel into a dladm_status_t. 60*8275SEric Cheng */ 61*8275SEric Cheng static dladm_status_t 62*8275SEric Cheng dladm_vnic_diag2status(vnic_ioc_diag_t ioc_diag) 63*8275SEric Cheng { 64*8275SEric Cheng switch (ioc_diag) { 65*8275SEric Cheng case VNIC_IOC_DIAG_MACADDR_INVALID: 66*8275SEric Cheng return (DLADM_STATUS_INVALIDMACADDR); 67*8275SEric Cheng case VNIC_IOC_DIAG_MACADDRLEN_INVALID: 68*8275SEric Cheng return (DLADM_STATUS_INVALIDMACADDRLEN); 69*8275SEric Cheng case VNIC_IOC_DIAG_MACADDR_NIC: 70*8275SEric Cheng return (DLADM_STATUS_INVALIDMACADDRNIC); 71*8275SEric Cheng case VNIC_IOC_DIAG_MACADDR_INUSE: 72*8275SEric Cheng return (DLADM_STATUS_INVALIDMACADDRINUSE); 73*8275SEric Cheng case VNIC_IOC_DIAG_MACFACTORYSLOTINVALID: 74*8275SEric Cheng return (DLADM_STATUS_MACFACTORYSLOTINVALID); 75*8275SEric Cheng case VNIC_IOC_DIAG_MACFACTORYSLOTUSED: 76*8275SEric Cheng return (DLADM_STATUS_MACFACTORYSLOTUSED); 77*8275SEric Cheng case VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED: 78*8275SEric Cheng return (DLADM_STATUS_MACFACTORYSLOTALLUSED); 79*8275SEric Cheng case VNIC_IOC_DIAG_MACFACTORYNOTSUP: 80*8275SEric Cheng return (DLADM_STATUS_MACFACTORYNOTSUP); 81*8275SEric Cheng case VNIC_IOC_DIAG_MACPREFIX_INVALID: 82*8275SEric Cheng return (DLADM_STATUS_INVALIDMACPREFIX); 83*8275SEric Cheng case VNIC_IOC_DIAG_MACPREFIXLEN_INVALID: 84*8275SEric Cheng return (DLADM_STATUS_INVALIDMACPREFIXLEN); 85*8275SEric Cheng case VNIC_IOC_DIAG_MACMARGIN_INVALID: 86*8275SEric Cheng return (DLADM_STATUS_INVALID_MACMARGIN); 87*8275SEric Cheng case VNIC_IOC_DIAG_NO_HWRINGS: 88*8275SEric Cheng return (DLADM_STATUS_NO_HWRINGS); 89*8275SEric Cheng } 90*8275SEric Cheng return (DLADM_STATUS_OK); 91*8275SEric Cheng } 925084Sjohnlev 935084Sjohnlev /* 945084Sjohnlev * Send a create command to the VNIC driver. 955084Sjohnlev */ 96*8275SEric Cheng dladm_status_t 97*8275SEric Cheng i_dladm_vnic_create_sys(dladm_vnic_attr_t *attr) 985084Sjohnlev { 99*8275SEric Cheng int rc, fd; 1005084Sjohnlev vnic_ioc_create_t ioc; 101*8275SEric Cheng dladm_status_t status = DLADM_STATUS_OK; 1025084Sjohnlev 103*8275SEric Cheng bzero(&ioc, sizeof (ioc)); 104*8275SEric Cheng ioc.vc_vnic_id = attr->va_vnic_id; 105*8275SEric Cheng ioc.vc_link_id = attr->va_link_id; 106*8275SEric Cheng ioc.vc_mac_addr_type = attr->va_mac_addr_type; 107*8275SEric Cheng ioc.vc_mac_len = attr->va_mac_len; 108*8275SEric Cheng ioc.vc_mac_slot = attr->va_mac_slot; 109*8275SEric Cheng ioc.vc_mac_prefix_len = attr->va_mac_prefix_len; 110*8275SEric Cheng ioc.vc_vid = attr->va_vid; 111*8275SEric Cheng ioc.vc_flags = attr->va_force ? VNIC_IOC_CREATE_FORCE : 0; 112*8275SEric Cheng ioc.vc_flags |= attr->va_hwrings ? VNIC_IOC_CREATE_REQ_HWRINGS : 0; 1135084Sjohnlev 114*8275SEric Cheng if (attr->va_mac_len > 0 || ioc.vc_mac_prefix_len > 0) 115*8275SEric Cheng bcopy(attr->va_mac_addr, ioc.vc_mac_addr, MAXMACADDRLEN); 116*8275SEric Cheng bcopy(&attr->va_resource_props, &ioc.vc_resource_props, 117*8275SEric Cheng sizeof (mac_resource_props_t)); 118*8275SEric Cheng if (attr->va_link_id == DATALINK_INVALID_LINKID) 119*8275SEric Cheng ioc.vc_flags |= VNIC_IOC_CREATE_ANCHOR; 1205084Sjohnlev 1217408SSebastien.Roy@Sun.COM if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 1225084Sjohnlev return (dladm_errno2status(errno)); 1235084Sjohnlev 124*8275SEric Cheng rc = ioctl(fd, VNIC_IOC_CREATE, &ioc); 125*8275SEric Cheng if (rc < 0) 126*8275SEric Cheng status = dladm_errno2status(errno); 127*8275SEric Cheng 128*8275SEric Cheng (void) close(fd); 129*8275SEric Cheng if (status != DLADM_STATUS_OK) { 130*8275SEric Cheng if (ioc.vc_diag != VNIC_IOC_DIAG_NONE) 131*8275SEric Cheng status = dladm_vnic_diag2status(ioc.vc_diag); 132*8275SEric Cheng } 133*8275SEric Cheng if (status != DLADM_STATUS_OK) 134*8275SEric Cheng return (status); 135*8275SEric Cheng 136*8275SEric Cheng attr->va_mac_addr_type = ioc.vc_mac_addr_type; 137*8275SEric Cheng switch (ioc.vc_mac_addr_type) { 138*8275SEric Cheng case VNIC_MAC_ADDR_TYPE_FACTORY: 139*8275SEric Cheng attr->va_mac_slot = ioc.vc_mac_slot; 140*8275SEric Cheng break; 141*8275SEric Cheng case VNIC_MAC_ADDR_TYPE_RANDOM: 142*8275SEric Cheng bcopy(ioc.vc_mac_addr, attr->va_mac_addr, MAXMACADDRLEN); 143*8275SEric Cheng attr->va_mac_len = ioc.vc_mac_len; 144*8275SEric Cheng break; 145*8275SEric Cheng } 146*8275SEric Cheng return (status); 147*8275SEric Cheng } 148*8275SEric Cheng 149*8275SEric Cheng /* 150*8275SEric Cheng * Get the configuration information of the given VNIC. 151*8275SEric Cheng */ 152*8275SEric Cheng static dladm_status_t 153*8275SEric Cheng i_dladm_vnic_info_active(datalink_id_t linkid, dladm_vnic_attr_t *attrp) 154*8275SEric Cheng { 155*8275SEric Cheng vnic_ioc_info_t ioc; 156*8275SEric Cheng vnic_info_t *vnic; 157*8275SEric Cheng int rc, fd; 158*8275SEric Cheng dladm_status_t status = DLADM_STATUS_OK; 159*8275SEric Cheng 160*8275SEric Cheng if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) == -1) 161*8275SEric Cheng return (dladm_errno2status(errno)); 162*8275SEric Cheng 163*8275SEric Cheng bzero(&ioc, sizeof (ioc)); 164*8275SEric Cheng vnic = &ioc.vi_info; 165*8275SEric Cheng vnic->vn_vnic_id = linkid; 166*8275SEric Cheng 167*8275SEric Cheng rc = ioctl(fd, VNIC_IOC_INFO, &ioc); 168*8275SEric Cheng if (rc != 0) { 169*8275SEric Cheng status = dladm_errno2status(errno); 170*8275SEric Cheng goto bail; 171*8275SEric Cheng } 172*8275SEric Cheng 173*8275SEric Cheng attrp->va_vnic_id = vnic->vn_vnic_id; 174*8275SEric Cheng attrp->va_link_id = vnic->vn_link_id; 175*8275SEric Cheng attrp->va_mac_addr_type = vnic->vn_mac_addr_type; 176*8275SEric Cheng bcopy(vnic->vn_mac_addr, attrp->va_mac_addr, MAXMACADDRLEN); 177*8275SEric Cheng attrp->va_mac_len = vnic->vn_mac_len; 178*8275SEric Cheng attrp->va_mac_slot = vnic->vn_mac_slot; 179*8275SEric Cheng attrp->va_mac_prefix_len = vnic->vn_mac_prefix_len; 180*8275SEric Cheng attrp->va_vid = vnic->vn_vid; 181*8275SEric Cheng attrp->va_force = vnic->vn_force; 182*8275SEric Cheng 183*8275SEric Cheng bail: 184*8275SEric Cheng (void) close(fd); 185*8275SEric Cheng return (status); 186*8275SEric Cheng } 187*8275SEric Cheng 188*8275SEric Cheng static dladm_status_t 189*8275SEric Cheng i_dladm_vnic_info_persist(datalink_id_t linkid, dladm_vnic_attr_t *attrp) 190*8275SEric Cheng { 191*8275SEric Cheng dladm_conf_t conf; 192*8275SEric Cheng dladm_status_t status; 193*8275SEric Cheng char macstr[ETHERADDRL * 3]; 194*8275SEric Cheng uint64_t u64; 195*8275SEric Cheng datalink_class_t class; 196*8275SEric Cheng 197*8275SEric Cheng attrp->va_vnic_id = linkid; 198*8275SEric Cheng if ((status = dladm_read_conf(linkid, &conf)) != DLADM_STATUS_OK) 199*8275SEric Cheng return (status); 200*8275SEric Cheng 201*8275SEric Cheng status = dladm_get_conf_field(conf, FLINKOVER, &u64, sizeof (u64)); 202*8275SEric Cheng attrp->va_link_id = ((status == DLADM_STATUS_OK) ? 203*8275SEric Cheng (datalink_id_t)u64 : DATALINK_INVALID_LINKID); 204*8275SEric Cheng 205*8275SEric Cheng status = dladm_get_conf_field(conf, FHWRINGS, &attrp->va_hwrings, 206*8275SEric Cheng sizeof (boolean_t)); 207*8275SEric Cheng 208*8275SEric Cheng if (status != DLADM_STATUS_OK && status != DLADM_STATUS_NOTFOUND) 209*8275SEric Cheng goto done; 210*8275SEric Cheng if (status == DLADM_STATUS_NOTFOUND) 211*8275SEric Cheng attrp->va_hwrings = B_FALSE; 212*8275SEric Cheng 213*8275SEric Cheng if ((status = dladm_datalink_id2info(linkid, NULL, &class, 214*8275SEric Cheng NULL, NULL, 0)) != DLADM_STATUS_OK) 215*8275SEric Cheng goto done; 216*8275SEric Cheng 217*8275SEric Cheng if (class == DATALINK_CLASS_VLAN) { 218*8275SEric Cheng if (attrp->va_link_id == DATALINK_INVALID_LINKID) { 219*8275SEric Cheng status = DLADM_STATUS_BADARG; 220*8275SEric Cheng goto done; 221*8275SEric Cheng } 222*8275SEric Cheng attrp->va_mac_addr_type = VNIC_MAC_ADDR_TYPE_PRIMARY; 223*8275SEric Cheng attrp->va_mac_len = 0; 224*8275SEric Cheng } else { 225*8275SEric Cheng status = dladm_get_conf_field(conf, FMADDRTYPE, &u64, 226*8275SEric Cheng sizeof (u64)); 227*8275SEric Cheng if (status != DLADM_STATUS_OK) 228*8275SEric Cheng goto done; 229*8275SEric Cheng 230*8275SEric Cheng attrp->va_mac_addr_type = (vnic_mac_addr_type_t)u64; 231*8275SEric Cheng 232*8275SEric Cheng status = dladm_get_conf_field(conf, FMADDRLEN, &u64, 233*8275SEric Cheng sizeof (u64)); 234*8275SEric Cheng attrp->va_mac_len = ((status == DLADM_STATUS_OK) ? 235*8275SEric Cheng (uint_t)u64 : ETHERADDRL); 236*8275SEric Cheng 237*8275SEric Cheng status = dladm_get_conf_field(conf, FMADDRSLOT, &u64, 238*8275SEric Cheng sizeof (u64)); 239*8275SEric Cheng attrp->va_mac_slot = ((status == DLADM_STATUS_OK) ? 240*8275SEric Cheng (int)u64 : -1); 241*8275SEric Cheng 242*8275SEric Cheng status = dladm_get_conf_field(conf, FMADDRPREFIXLEN, &u64, 243*8275SEric Cheng sizeof (u64)); 244*8275SEric Cheng attrp->va_mac_prefix_len = ((status == DLADM_STATUS_OK) ? 245*8275SEric Cheng (uint_t)u64 : sizeof (dladm_vnic_def_prefix)); 246*8275SEric Cheng 247*8275SEric Cheng status = dladm_get_conf_field(conf, FMACADDR, macstr, 248*8275SEric Cheng sizeof (macstr)); 249*8275SEric Cheng if (status != DLADM_STATUS_OK) 250*8275SEric Cheng goto done; 251*8275SEric Cheng 252*8275SEric Cheng status = dladm_vnic_str2macaddr(macstr, attrp->va_mac_addr); 253*8275SEric Cheng if (status != DLADM_STATUS_OK) 254*8275SEric Cheng goto done; 255*8275SEric Cheng } 256*8275SEric Cheng 257*8275SEric Cheng status = dladm_get_conf_field(conf, FVLANID, &u64, sizeof (u64)); 258*8275SEric Cheng attrp->va_vid = ((status == DLADM_STATUS_OK) ? (uint16_t)u64 : 0); 259*8275SEric Cheng 260*8275SEric Cheng 261*8275SEric Cheng status = DLADM_STATUS_OK; 262*8275SEric Cheng done: 263*8275SEric Cheng dladm_destroy_conf(conf); 264*8275SEric Cheng return (status); 265*8275SEric Cheng } 266*8275SEric Cheng 267*8275SEric Cheng dladm_status_t 268*8275SEric Cheng dladm_vnic_info(datalink_id_t linkid, dladm_vnic_attr_t *attrp, 269*8275SEric Cheng uint32_t flags) 270*8275SEric Cheng { 271*8275SEric Cheng if (flags == DLADM_OPT_ACTIVE) 272*8275SEric Cheng return (i_dladm_vnic_info_active(linkid, attrp)); 273*8275SEric Cheng else if (flags == DLADM_OPT_PERSIST) 274*8275SEric Cheng return (i_dladm_vnic_info_persist(linkid, attrp)); 275*8275SEric Cheng else 276*8275SEric Cheng return (DLADM_STATUS_BADARG); 277*8275SEric Cheng } 278*8275SEric Cheng 279*8275SEric Cheng /* 280*8275SEric Cheng * Remove a VNIC from the kernel. 281*8275SEric Cheng */ 282*8275SEric Cheng dladm_status_t 283*8275SEric Cheng i_dladm_vnic_delete_sys(datalink_id_t linkid) 284*8275SEric Cheng { 285*8275SEric Cheng vnic_ioc_delete_t ioc; 286*8275SEric Cheng dladm_status_t status = DLADM_STATUS_OK; 287*8275SEric Cheng int rc, fd; 288*8275SEric Cheng 289*8275SEric Cheng if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 290*8275SEric Cheng return (dladm_errno2status(errno)); 291*8275SEric Cheng 292*8275SEric Cheng ioc.vd_vnic_id = linkid; 293*8275SEric Cheng 294*8275SEric Cheng rc = ioctl(fd, VNIC_IOC_DELETE, &ioc); 295*8275SEric Cheng if (rc < 0) 2967408SSebastien.Roy@Sun.COM status = dladm_errno2status(errno); 2975084Sjohnlev 2985084Sjohnlev (void) close(fd); 2997408SSebastien.Roy@Sun.COM return (status); 3005084Sjohnlev } 3015084Sjohnlev 3025084Sjohnlev /* 3035084Sjohnlev * Convert between MAC address types and their string representations. 3045084Sjohnlev */ 3055084Sjohnlev 3065084Sjohnlev typedef struct dladm_vnic_addr_type_s { 307*8275SEric Cheng const char *va_str; 308*8275SEric Cheng vnic_mac_addr_type_t va_type; 3095084Sjohnlev } dladm_vnic_addr_type_t; 3105084Sjohnlev 3115084Sjohnlev static dladm_vnic_addr_type_t addr_types[] = { 3125084Sjohnlev {"fixed", VNIC_MAC_ADDR_TYPE_FIXED}, 313*8275SEric Cheng {"random", VNIC_MAC_ADDR_TYPE_RANDOM}, 314*8275SEric Cheng {"factory", VNIC_MAC_ADDR_TYPE_FACTORY}, 315*8275SEric Cheng {"auto", VNIC_MAC_ADDR_TYPE_AUTO}, 316*8275SEric Cheng {"fixed", VNIC_MAC_ADDR_TYPE_PRIMARY} 3175084Sjohnlev }; 3185084Sjohnlev 3195084Sjohnlev #define NADDR_TYPES (sizeof (addr_types) / sizeof (dladm_vnic_addr_type_t)) 3205084Sjohnlev 321*8275SEric Cheng static const char * 322*8275SEric Cheng dladm_vnic_macaddrtype2str(vnic_mac_addr_type_t type) 323*8275SEric Cheng { 324*8275SEric Cheng int i; 325*8275SEric Cheng 326*8275SEric Cheng for (i = 0; i < NADDR_TYPES; i++) { 327*8275SEric Cheng if (type == addr_types[i].va_type) 328*8275SEric Cheng return (addr_types[i].va_str); 329*8275SEric Cheng } 330*8275SEric Cheng return (NULL); 331*8275SEric Cheng } 332*8275SEric Cheng 3335895Syz147064 dladm_status_t 3345895Syz147064 dladm_vnic_str2macaddrtype(const char *str, vnic_mac_addr_type_t *val) 3355084Sjohnlev { 3365084Sjohnlev int i; 3375084Sjohnlev dladm_vnic_addr_type_t *type; 3385084Sjohnlev 3395084Sjohnlev for (i = 0; i < NADDR_TYPES; i++) { 3405084Sjohnlev type = &addr_types[i]; 3415084Sjohnlev if (strncmp(str, type->va_str, strlen(type->va_str)) == 0) { 3425084Sjohnlev *val = type->va_type; 3435895Syz147064 return (DLADM_STATUS_OK); 3445084Sjohnlev } 3455084Sjohnlev } 3465895Syz147064 return (DLADM_STATUS_BADARG); 3475084Sjohnlev } 3485084Sjohnlev 349*8275SEric Cheng 350*8275SEric Cheng 3515084Sjohnlev /* 352*8275SEric Cheng * Create a new VNIC / VLAN. Update the configuration file and bring it up. 3535084Sjohnlev */ 3545084Sjohnlev dladm_status_t 3555895Syz147064 dladm_vnic_create(const char *vnic, datalink_id_t linkid, 3565084Sjohnlev vnic_mac_addr_type_t mac_addr_type, uchar_t *mac_addr, int mac_len, 357*8275SEric Cheng int *mac_slot, uint_t mac_prefix_len, uint16_t vid, 358*8275SEric Cheng datalink_id_t *vnic_id_out, dladm_arg_list_t *proplist, uint32_t flags) 3595084Sjohnlev { 360*8275SEric Cheng dladm_vnic_attr_t attr; 3615895Syz147064 datalink_id_t vnic_id; 3625895Syz147064 datalink_class_t class; 363*8275SEric Cheng uint32_t media = DL_ETHER; 364*8275SEric Cheng char name[MAXLINKNAMELEN]; 365*8275SEric Cheng uchar_t tmp_addr[MAXMACADDRLEN]; 3665084Sjohnlev dladm_status_t status; 367*8275SEric Cheng boolean_t is_vlan; 368*8275SEric Cheng boolean_t is_etherstub; 369*8275SEric Cheng int i; 3705084Sjohnlev 3715084Sjohnlev /* 3725084Sjohnlev * Sanity test arguments. 3735084Sjohnlev */ 374*8275SEric Cheng if ((flags & DLADM_OPT_ACTIVE) == 0) 375*8275SEric Cheng return (DLADM_STATUS_NOTSUP); 376*8275SEric Cheng 377*8275SEric Cheng is_vlan = ((flags & DLADM_OPT_VLAN) != 0); 378*8275SEric Cheng if (is_vlan && ((vid < 1 || vid > 4094))) 379*8275SEric Cheng return (DLADM_STATUS_VIDINVAL); 380*8275SEric Cheng 381*8275SEric Cheng is_etherstub = (linkid == DATALINK_INVALID_LINKID); 3825084Sjohnlev 3835084Sjohnlev if (mac_len > MAXMACADDRLEN) 3845084Sjohnlev return (DLADM_STATUS_INVALIDMACADDRLEN); 3855084Sjohnlev 386*8275SEric Cheng if (!dladm_vnic_macaddrtype2str(mac_addr_type)) 3875084Sjohnlev return (DLADM_STATUS_INVALIDMACADDRTYPE); 3885084Sjohnlev 389*8275SEric Cheng /* 390*8275SEric Cheng * If a random address might be generated, but no prefix 391*8275SEric Cheng * was specified by the caller, use the default MAC address 392*8275SEric Cheng * prefix. 393*8275SEric Cheng */ 394*8275SEric Cheng if ((mac_addr_type == VNIC_MAC_ADDR_TYPE_RANDOM || 395*8275SEric Cheng mac_addr_type == VNIC_MAC_ADDR_TYPE_AUTO) && 396*8275SEric Cheng mac_prefix_len == 0) { 397*8275SEric Cheng mac_prefix_len = sizeof (dladm_vnic_def_prefix); 398*8275SEric Cheng mac_addr = tmp_addr; 399*8275SEric Cheng bcopy(dladm_vnic_def_prefix, mac_addr, mac_prefix_len); 4005895Syz147064 } 4015895Syz147064 402*8275SEric Cheng if ((flags & DLADM_OPT_ANCHOR) == 0) { 403*8275SEric Cheng if ((status = dladm_datalink_id2info(linkid, NULL, &class, 404*8275SEric Cheng &media, NULL, 0)) != DLADM_STATUS_OK) 405*8275SEric Cheng return (status); 406*8275SEric Cheng 407*8275SEric Cheng if (class == DATALINK_CLASS_VNIC || 408*8275SEric Cheng class == DATALINK_CLASS_VLAN) 409*8275SEric Cheng return (DLADM_STATUS_BADARG); 410*8275SEric Cheng } else { 411*8275SEric Cheng /* it's an anchor VNIC */ 412*8275SEric Cheng if (linkid != DATALINK_INVALID_LINKID || vid != 0) 413*8275SEric Cheng return (DLADM_STATUS_BADARG); 414*8275SEric Cheng } 4155084Sjohnlev 4165895Syz147064 if (vnic == NULL) { 4175895Syz147064 flags |= DLADM_OPT_PREFIX; 418*8275SEric Cheng (void) strlcpy(name, "vnic", sizeof (name)); 419*8275SEric Cheng } else { 420*8275SEric Cheng (void) strlcpy(name, vnic, sizeof (name)); 4215895Syz147064 } 4225895Syz147064 423*8275SEric Cheng class = is_vlan ? DATALINK_CLASS_VLAN : 424*8275SEric Cheng (is_etherstub ? DATALINK_CLASS_ETHERSTUB : DATALINK_CLASS_VNIC); 425*8275SEric Cheng if ((status = dladm_create_datalink_id(name, class, 426*8275SEric Cheng media, flags, &vnic_id)) != DLADM_STATUS_OK) 4275895Syz147064 return (status); 428*8275SEric Cheng 429*8275SEric Cheng if ((flags & DLADM_OPT_PREFIX) != 0) { 430*8275SEric Cheng (void) snprintf(name + 4, sizeof (name), "%llu", vnic_id); 431*8275SEric Cheng flags &= ~DLADM_OPT_PREFIX; 4325084Sjohnlev } 4335084Sjohnlev 4345084Sjohnlev bzero(&attr, sizeof (attr)); 435*8275SEric Cheng 436*8275SEric Cheng /* Extract resource_ctl and cpu_list from proplist */ 437*8275SEric Cheng if (proplist != NULL) { 438*8275SEric Cheng status = dladm_link_proplist_extract(proplist, 439*8275SEric Cheng &attr.va_resource_props); 440*8275SEric Cheng if (status != DLADM_STATUS_OK) 441*8275SEric Cheng goto done; 442*8275SEric Cheng } 4435084Sjohnlev 444*8275SEric Cheng attr.va_vnic_id = vnic_id; 445*8275SEric Cheng attr.va_link_id = linkid; 446*8275SEric Cheng attr.va_mac_addr_type = mac_addr_type; 447*8275SEric Cheng attr.va_mac_len = mac_len; 448*8275SEric Cheng if (mac_slot != NULL) 449*8275SEric Cheng attr.va_mac_slot = *mac_slot; 450*8275SEric Cheng if (mac_len > 0) 451*8275SEric Cheng bcopy(mac_addr, attr.va_mac_addr, mac_len); 452*8275SEric Cheng else if (mac_prefix_len > 0) 453*8275SEric Cheng bcopy(mac_addr, attr.va_mac_addr, mac_prefix_len); 454*8275SEric Cheng attr.va_mac_prefix_len = mac_prefix_len; 455*8275SEric Cheng attr.va_vid = vid; 456*8275SEric Cheng attr.va_force = (flags & DLADM_OPT_FORCE) != 0; 457*8275SEric Cheng attr.va_hwrings = (flags & DLADM_OPT_HWRINGS) != 0; 458*8275SEric Cheng 459*8275SEric Cheng status = i_dladm_vnic_create_sys(&attr); 460*8275SEric Cheng if (status != DLADM_STATUS_OK) 461*8275SEric Cheng goto done; 462*8275SEric Cheng 463*8275SEric Cheng /* Save vnic configuration and its properties */ 464*8275SEric Cheng if (!(flags & DLADM_OPT_PERSIST)) 465*8275SEric Cheng goto done; 466*8275SEric Cheng 467*8275SEric Cheng status = dladm_vnic_persist_conf(name, &attr, class); 468*8275SEric Cheng if (status != DLADM_STATUS_OK) { 469*8275SEric Cheng (void) i_dladm_vnic_delete_sys(vnic_id); 4705895Syz147064 goto done; 4715895Syz147064 } 4725895Syz147064 473*8275SEric Cheng if (proplist != NULL) { 474*8275SEric Cheng for (i = 0; i < proplist->al_count; i++) { 475*8275SEric Cheng dladm_arg_info_t *aip = &proplist->al_info[i]; 476*8275SEric Cheng 477*8275SEric Cheng status = dladm_set_linkprop(vnic_id, aip->ai_name, 478*8275SEric Cheng aip->ai_val, aip->ai_count, DLADM_OPT_PERSIST); 479*8275SEric Cheng if (status != DLADM_STATUS_OK) 480*8275SEric Cheng break; 481*8275SEric Cheng } 482*8275SEric Cheng 483*8275SEric Cheng if (status != DLADM_STATUS_OK) { 484*8275SEric Cheng (void) dladm_remove_conf(vnic_id); 485*8275SEric Cheng (void) i_dladm_vnic_delete_sys(vnic_id); 486*8275SEric Cheng } 487*8275SEric Cheng } 4885084Sjohnlev 4895895Syz147064 done: 4905895Syz147064 if (status != DLADM_STATUS_OK) { 491*8275SEric Cheng (void) dladm_destroy_datalink_id(vnic_id, flags); 4925895Syz147064 } else { 493*8275SEric Cheng if (vnic_id_out != NULL) 494*8275SEric Cheng *vnic_id_out = vnic_id; 495*8275SEric Cheng if (mac_slot != NULL) 496*8275SEric Cheng *mac_slot = attr.va_mac_slot; 4975895Syz147064 } 4985084Sjohnlev return (status); 4995084Sjohnlev } 5005084Sjohnlev 5015084Sjohnlev /* 502*8275SEric Cheng * Delete a VNIC / VLAN. 5035084Sjohnlev */ 5045084Sjohnlev dladm_status_t 505*8275SEric Cheng dladm_vnic_delete(datalink_id_t linkid, uint32_t flags) 5065084Sjohnlev { 507*8275SEric Cheng dladm_status_t status; 508*8275SEric Cheng datalink_class_t class; 509*8275SEric Cheng 510*8275SEric Cheng if (flags == 0) 511*8275SEric Cheng return (DLADM_STATUS_BADARG); 5125084Sjohnlev 513*8275SEric Cheng if ((dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0) != 514*8275SEric Cheng DLADM_STATUS_OK)) 515*8275SEric Cheng return (DLADM_STATUS_BADARG); 5165084Sjohnlev 517*8275SEric Cheng if ((flags & DLADM_OPT_VLAN) != 0) { 518*8275SEric Cheng if (class != DATALINK_CLASS_VLAN) 519*8275SEric Cheng return (DLADM_STATUS_BADARG); 520*8275SEric Cheng } else { 521*8275SEric Cheng if (class != DATALINK_CLASS_VNIC && 522*8275SEric Cheng class != DATALINK_CLASS_ETHERSTUB) 523*8275SEric Cheng return (DLADM_STATUS_BADARG); 5245084Sjohnlev } 5255084Sjohnlev 526*8275SEric Cheng if ((flags & DLADM_OPT_ACTIVE) != 0) { 527*8275SEric Cheng status = i_dladm_vnic_delete_sys(linkid); 528*8275SEric Cheng if (status == DLADM_STATUS_OK) { 529*8275SEric Cheng (void) dladm_set_linkprop(linkid, NULL, NULL, 0, 530*8275SEric Cheng DLADM_OPT_ACTIVE); 531*8275SEric Cheng (void) dladm_destroy_datalink_id(linkid, 532*8275SEric Cheng DLADM_OPT_ACTIVE); 533*8275SEric Cheng } else if (status != DLADM_STATUS_NOTFOUND || 534*8275SEric Cheng !(flags & DLADM_OPT_PERSIST)) { 535*8275SEric Cheng return (status); 536*8275SEric Cheng } 537*8275SEric Cheng } 538*8275SEric Cheng if ((flags & DLADM_OPT_PERSIST) != 0) { 539*8275SEric Cheng (void) dladm_destroy_datalink_id(linkid, DLADM_OPT_PERSIST); 540*8275SEric Cheng (void) dladm_remove_conf(linkid); 541*8275SEric Cheng } 542*8275SEric Cheng return (DLADM_STATUS_OK); 543*8275SEric Cheng } 544*8275SEric Cheng 545*8275SEric Cheng static const char * 546*8275SEric Cheng dladm_vnic_macaddr2str(const uchar_t *mac, char *buf) 547*8275SEric Cheng { 548*8275SEric Cheng static char unknown_mac[] = {0, 0, 0, 0, 0, 0}; 549*8275SEric Cheng 550*8275SEric Cheng if (buf == NULL) 551*8275SEric Cheng return (NULL); 552*8275SEric Cheng 553*8275SEric Cheng if (bcmp(unknown_mac, mac, ETHERADDRL) == 0) 554*8275SEric Cheng (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 555*8275SEric Cheng else 556*8275SEric Cheng return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER)); 557*8275SEric Cheng 558*8275SEric Cheng return (buf); 559*8275SEric Cheng } 560*8275SEric Cheng 561*8275SEric Cheng static dladm_status_t 562*8275SEric Cheng dladm_vnic_str2macaddr(const char *str, uchar_t *buf) 563*8275SEric Cheng { 564*8275SEric Cheng int len = 0; 565*8275SEric Cheng uchar_t *b = _link_aton(str, &len); 566*8275SEric Cheng 567*8275SEric Cheng if (b == NULL || len >= MAXMACADDRLEN) 568*8275SEric Cheng return (DLADM_STATUS_BADARG); 569*8275SEric Cheng 570*8275SEric Cheng bcopy(b, buf, len); 571*8275SEric Cheng free(b); 572*8275SEric Cheng return (DLADM_STATUS_OK); 5735084Sjohnlev } 5745084Sjohnlev 5755084Sjohnlev 576*8275SEric Cheng static dladm_status_t 577*8275SEric Cheng dladm_vnic_persist_conf(const char *name, dladm_vnic_attr_t *attrp, 578*8275SEric Cheng datalink_class_t class) 579*8275SEric Cheng { 580*8275SEric Cheng dladm_conf_t conf = DLADM_INVALID_CONF; 581*8275SEric Cheng dladm_status_t status; 582*8275SEric Cheng char macstr[ETHERADDRL * 3]; 583*8275SEric Cheng uint64_t u64; 5845084Sjohnlev 585*8275SEric Cheng if ((status = dladm_create_conf(name, attrp->va_vnic_id, 586*8275SEric Cheng class, DL_ETHER, &conf)) != DLADM_STATUS_OK) 5875895Syz147064 return (status); 5885895Syz147064 589*8275SEric Cheng if (attrp->va_link_id != DATALINK_INVALID_LINKID) { 590*8275SEric Cheng u64 = attrp->va_link_id; 591*8275SEric Cheng status = dladm_set_conf_field(conf, FLINKOVER, 592*8275SEric Cheng DLADM_TYPE_UINT64, &u64); 593*8275SEric Cheng if (status != DLADM_STATUS_OK) 594*8275SEric Cheng goto done; 595*8275SEric Cheng } 596*8275SEric Cheng 597*8275SEric Cheng if (class != DATALINK_CLASS_VLAN) { 598*8275SEric Cheng u64 = attrp->va_mac_addr_type; 599*8275SEric Cheng status = dladm_set_conf_field(conf, FMADDRTYPE, 600*8275SEric Cheng DLADM_TYPE_UINT64, &u64); 601*8275SEric Cheng if (status != DLADM_STATUS_OK) 602*8275SEric Cheng goto done; 603*8275SEric Cheng 604*8275SEric Cheng if (attrp->va_mac_len != ETHERADDRL) { 605*8275SEric Cheng u64 = attrp->va_mac_len; 606*8275SEric Cheng status = dladm_set_conf_field(conf, FMADDRLEN, 607*8275SEric Cheng DLADM_TYPE_UINT64, &u64); 608*8275SEric Cheng if (status != DLADM_STATUS_OK) 609*8275SEric Cheng goto done; 610*8275SEric Cheng } 611*8275SEric Cheng } 612*8275SEric Cheng 613*8275SEric Cheng if (attrp->va_hwrings) { 614*8275SEric Cheng boolean_t hwrings = attrp->va_hwrings; 615*8275SEric Cheng status = dladm_set_conf_field(conf, FHWRINGS, 616*8275SEric Cheng DLADM_TYPE_BOOLEAN, &hwrings); 617*8275SEric Cheng if (status != DLADM_STATUS_OK) 618*8275SEric Cheng goto done; 619*8275SEric Cheng } 620*8275SEric Cheng 621*8275SEric Cheng if (class != DATALINK_CLASS_VLAN) { 622*8275SEric Cheng if (attrp->va_mac_slot != -1) { 623*8275SEric Cheng u64 = attrp->va_mac_slot; 624*8275SEric Cheng status = dladm_set_conf_field(conf, FMADDRSLOT, 625*8275SEric Cheng DLADM_TYPE_UINT64, &u64); 626*8275SEric Cheng if (status != DLADM_STATUS_OK) 627*8275SEric Cheng goto done; 628*8275SEric Cheng } 629*8275SEric Cheng 630*8275SEric Cheng if (attrp->va_mac_prefix_len != 631*8275SEric Cheng sizeof (dladm_vnic_def_prefix)) { 632*8275SEric Cheng u64 = attrp->va_mac_prefix_len; 633*8275SEric Cheng status = dladm_set_conf_field(conf, FMADDRPREFIXLEN, 634*8275SEric Cheng DLADM_TYPE_UINT64, &u64); 635*8275SEric Cheng if (status != DLADM_STATUS_OK) 636*8275SEric Cheng goto done; 637*8275SEric Cheng } 638*8275SEric Cheng 639*8275SEric Cheng (void) dladm_vnic_macaddr2str(attrp->va_mac_addr, macstr); 640*8275SEric Cheng status = dladm_set_conf_field(conf, FMACADDR, DLADM_TYPE_STR, 641*8275SEric Cheng macstr); 642*8275SEric Cheng if (status != DLADM_STATUS_OK) 643*8275SEric Cheng goto done; 644*8275SEric Cheng } 645*8275SEric Cheng 646*8275SEric Cheng if (attrp->va_vid != 0) { 647*8275SEric Cheng u64 = attrp->va_vid; 648*8275SEric Cheng status = dladm_set_conf_field(conf, FVLANID, 649*8275SEric Cheng DLADM_TYPE_UINT64, &u64); 650*8275SEric Cheng if (status != DLADM_STATUS_OK) 651*8275SEric Cheng goto done; 652*8275SEric Cheng } 653*8275SEric Cheng 654*8275SEric Cheng /* 655*8275SEric Cheng * Commit the link configuration. 656*8275SEric Cheng */ 657*8275SEric Cheng status = dladm_write_conf(conf); 658*8275SEric Cheng 659*8275SEric Cheng done: 660*8275SEric Cheng dladm_destroy_conf(conf); 6615895Syz147064 return (status); 6625084Sjohnlev } 663*8275SEric Cheng 664*8275SEric Cheng typedef struct dladm_vnic_up_arg_s { 665*8275SEric Cheng uint32_t flags; 666*8275SEric Cheng dladm_status_t status; 667*8275SEric Cheng } dladm_vnic_up_arg_t; 668*8275SEric Cheng 669*8275SEric Cheng #define DLADM_VNIC_UP_FIRST_WALK 0x1 670*8275SEric Cheng #define DLADM_VNIC_UP_SECOND_WALK 0x2 671*8275SEric Cheng 672*8275SEric Cheng static int 673*8275SEric Cheng i_dladm_vnic_up(datalink_id_t linkid, void *arg) 674*8275SEric Cheng { 675*8275SEric Cheng dladm_status_t *statusp = &(((dladm_vnic_up_arg_t *)arg)->status); 676*8275SEric Cheng dladm_vnic_attr_t attr; 677*8275SEric Cheng dladm_status_t status; 678*8275SEric Cheng dladm_arg_list_t *proplist; 679*8275SEric Cheng uint32_t flags = ((dladm_vnic_up_arg_t *)arg)->flags; 680*8275SEric Cheng 681*8275SEric Cheng bzero(&attr, sizeof (attr)); 682*8275SEric Cheng 683*8275SEric Cheng status = dladm_vnic_info(linkid, &attr, DLADM_OPT_PERSIST); 684*8275SEric Cheng if (status != DLADM_STATUS_OK) 685*8275SEric Cheng goto done; 686*8275SEric Cheng 687*8275SEric Cheng /* 688*8275SEric Cheng * Create the vnics that request hardware group first 689*8275SEric Cheng * Create the vnics that don't request hardware group in the second walk 690*8275SEric Cheng */ 691*8275SEric Cheng if ((flags == DLADM_VNIC_UP_FIRST_WALK && !attr.va_hwrings) || 692*8275SEric Cheng (flags == DLADM_VNIC_UP_SECOND_WALK && attr.va_hwrings)) 693*8275SEric Cheng goto done; 694*8275SEric Cheng 695*8275SEric Cheng /* Get all properties for this vnic */ 696*8275SEric Cheng status = dladm_link_get_proplist(linkid, &proplist); 697*8275SEric Cheng if (status != DLADM_STATUS_OK) 698*8275SEric Cheng goto done; 699*8275SEric Cheng 700*8275SEric Cheng if (proplist != NULL) { 701*8275SEric Cheng status = dladm_link_proplist_extract(proplist, 702*8275SEric Cheng &attr.va_resource_props); 703*8275SEric Cheng } 704*8275SEric Cheng 705*8275SEric Cheng status = i_dladm_vnic_create_sys(&attr); 706*8275SEric Cheng if (status != DLADM_STATUS_OK) 707*8275SEric Cheng goto done; 708*8275SEric Cheng 709*8275SEric Cheng if ((status = dladm_up_datalink_id(linkid)) != DLADM_STATUS_OK) { 710*8275SEric Cheng (void) i_dladm_vnic_delete_sys(linkid); 711*8275SEric Cheng goto done; 712*8275SEric Cheng } 713*8275SEric Cheng done: 714*8275SEric Cheng *statusp = status; 715*8275SEric Cheng return (DLADM_WALK_CONTINUE); 716*8275SEric Cheng } 717*8275SEric Cheng 718*8275SEric Cheng dladm_status_t 719*8275SEric Cheng dladm_vnic_up(datalink_id_t linkid, uint32_t flags) 720*8275SEric Cheng { 721*8275SEric Cheng dladm_vnic_up_arg_t vnic_arg; 722*8275SEric Cheng datalink_class_t class; 723*8275SEric Cheng 724*8275SEric Cheng class = ((flags & DLADM_OPT_VLAN) != 0) ? DATALINK_CLASS_VLAN : 725*8275SEric Cheng (DATALINK_CLASS_VNIC | DATALINK_CLASS_ETHERSTUB); 726*8275SEric Cheng 727*8275SEric Cheng if (linkid == DATALINK_ALL_LINKID) { 728*8275SEric Cheng vnic_arg.flags = DLADM_VNIC_UP_FIRST_WALK; 729*8275SEric Cheng (void) dladm_walk_datalink_id(i_dladm_vnic_up, &vnic_arg, 730*8275SEric Cheng class, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 731*8275SEric Cheng vnic_arg.flags = DLADM_VNIC_UP_SECOND_WALK; 732*8275SEric Cheng (void) dladm_walk_datalink_id(i_dladm_vnic_up, &vnic_arg, 733*8275SEric Cheng class, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 734*8275SEric Cheng return (DLADM_STATUS_OK); 735*8275SEric Cheng } else { 736*8275SEric Cheng (void) i_dladm_vnic_up(linkid, &vnic_arg); 737*8275SEric Cheng return (vnic_arg.status); 738*8275SEric Cheng } 739*8275SEric Cheng } 740