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 /* 22*5895Syz147064 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 235084Sjohnlev * Use is subject to license terms. 245084Sjohnlev */ 255084Sjohnlev 265084Sjohnlev #pragma ident "%Z%%M% %I% %E% SMI" 275084Sjohnlev 285084Sjohnlev #include <stdio.h> 295084Sjohnlev #include <sys/types.h> 305084Sjohnlev #include <sys/stat.h> 315084Sjohnlev #include <string.h> 325084Sjohnlev #include <fcntl.h> 335084Sjohnlev #include <unistd.h> 345084Sjohnlev #include <stropts.h> 355084Sjohnlev #include <stdlib.h> 365084Sjohnlev #include <errno.h> 375084Sjohnlev #include <strings.h> 385084Sjohnlev #include <libintl.h> 395084Sjohnlev #include <net/if_types.h> 405084Sjohnlev #include <net/if_dl.h> 415084Sjohnlev #include <libdladm_impl.h> 42*5895Syz147064 #include <libdllink.h> 435084Sjohnlev #include <libdlvnic.h> 445084Sjohnlev 455084Sjohnlev /* 465084Sjohnlev * VNIC administration library. 475084Sjohnlev */ 485084Sjohnlev 495084Sjohnlev #define VNIC_DEV "/devices/pseudo/vnic@0:" VNIC_CTL_NODE_NAME 505084Sjohnlev 515084Sjohnlev /* Limits on buffer size for VNIC_IOC_INFO request */ 525084Sjohnlev #define MIN_INFO_SIZE (4*1024) 535084Sjohnlev #define MAX_INFO_SIZE (128*1024) 545084Sjohnlev 555084Sjohnlev /* configuration database entry */ 565084Sjohnlev typedef struct dladm_vnic_attr_db { 57*5895Syz147064 datalink_id_t vt_vnic_id; 58*5895Syz147064 datalink_id_t vt_link_id; 595084Sjohnlev vnic_mac_addr_type_t vt_mac_addr_type; 605084Sjohnlev uint_t vt_mac_len; 615084Sjohnlev uchar_t vt_mac_addr[MAXMACADDRLEN]; 625084Sjohnlev } dladm_vnic_attr_db_t; 635084Sjohnlev 645084Sjohnlev typedef struct dladm_vnic_modify_attr { 655084Sjohnlev vnic_mac_addr_type_t vm_mac_addr_type; 665084Sjohnlev int vm_mac_len; 675084Sjohnlev uchar_t vm_mac_addr[MAXMACADDRLEN]; 685084Sjohnlev } dladm_vnic_modify_attr_t; 695084Sjohnlev 705084Sjohnlev /* 715084Sjohnlev * Send a create command to the VNIC driver. 725084Sjohnlev */ 735084Sjohnlev static dladm_status_t 745084Sjohnlev i_dladm_vnic_create_sys(int fd, dladm_vnic_attr_db_t *attr) 755084Sjohnlev { 765084Sjohnlev int rc; 775084Sjohnlev vnic_ioc_create_t ioc; 785084Sjohnlev 795084Sjohnlev ioc.vc_vnic_id = attr->vt_vnic_id; 80*5895Syz147064 ioc.vc_link_id = attr->vt_link_id; 815084Sjohnlev ioc.vc_mac_addr_type = attr->vt_mac_addr_type; 825084Sjohnlev ioc.vc_mac_len = attr->vt_mac_len; 835084Sjohnlev bcopy(attr->vt_mac_addr, ioc.vc_mac_addr, attr->vt_mac_len); 845084Sjohnlev 855084Sjohnlev rc = i_dladm_ioctl(fd, VNIC_IOC_CREATE, &ioc, sizeof (ioc)); 865084Sjohnlev 875084Sjohnlev if (rc < 0) 885084Sjohnlev return (dladm_errno2status(errno)); 895084Sjohnlev 905084Sjohnlev return (DLADM_STATUS_OK); 915084Sjohnlev } 925084Sjohnlev 935084Sjohnlev /* 945084Sjohnlev * Send a modify command to the VNIC driver. 955084Sjohnlev */ 965084Sjohnlev static dladm_status_t 97*5895Syz147064 i_dladm_vnic_modify_sys(datalink_id_t vnic_id, uint32_t modify_mask, 985084Sjohnlev dladm_vnic_modify_attr_t *attr) 995084Sjohnlev { 1005084Sjohnlev int rc; 1015084Sjohnlev int fd; 1025084Sjohnlev vnic_ioc_modify_t ioc; 1035084Sjohnlev 1045084Sjohnlev ioc.vm_vnic_id = vnic_id; 1055084Sjohnlev 1065084Sjohnlev ioc.vm_modify_mask = 0; 1075084Sjohnlev if (modify_mask & DLADM_VNIC_MODIFY_ADDR) 1085084Sjohnlev ioc.vm_modify_mask |= VNIC_IOC_MODIFY_ADDR; 1095084Sjohnlev 1105084Sjohnlev ioc.vm_mac_addr_type = attr->vm_mac_addr_type; 1115084Sjohnlev ioc.vm_mac_len = attr->vm_mac_len; 1125084Sjohnlev bcopy(attr->vm_mac_addr, ioc.vm_mac_addr, MAXMACADDRLEN); 1135084Sjohnlev 1145084Sjohnlev if ((fd = open(VNIC_DEV, O_RDWR)) < 0) 1155084Sjohnlev return (dladm_errno2status(errno)); 1165084Sjohnlev 1175084Sjohnlev rc = i_dladm_ioctl(fd, VNIC_IOC_MODIFY, &ioc, sizeof (ioc)); 1185084Sjohnlev 1195084Sjohnlev (void) close(fd); 1205084Sjohnlev 1215084Sjohnlev if (rc < 0) 1225084Sjohnlev return (dladm_errno2status(errno)); 1235084Sjohnlev 1245084Sjohnlev return (DLADM_STATUS_OK); 1255084Sjohnlev } 1265084Sjohnlev 1275084Sjohnlev /* 128*5895Syz147064 * Get the configuration information of the given VNIC. 1295084Sjohnlev */ 1305084Sjohnlev dladm_status_t 131*5895Syz147064 dladm_vnic_info(datalink_id_t vnic_id, dladm_vnic_attr_sys_t *attrp, 132*5895Syz147064 uint32_t flags) 1335084Sjohnlev { 1345084Sjohnlev vnic_ioc_info_t *ioc; 1355084Sjohnlev vnic_ioc_info_vnic_t *vnic; 136*5895Syz147064 int rc, bufsize, fd; 1375084Sjohnlev dladm_status_t status = DLADM_STATUS_OK; 1385084Sjohnlev 139*5895Syz147064 /* for now, only temporary creations are supported */ 140*5895Syz147064 if (flags & DLADM_OPT_PERSIST) 141*5895Syz147064 return (dladm_errno2status(ENOTSUP)); 142*5895Syz147064 1435084Sjohnlev if ((fd = open(VNIC_DEV, O_RDWR)) == -1) 1445084Sjohnlev return (dladm_errno2status(errno)); 1455084Sjohnlev 146*5895Syz147064 bufsize = sizeof (vnic_ioc_info_t) + sizeof (vnic_ioc_info_vnic_t); 1475084Sjohnlev ioc = (vnic_ioc_info_t *)calloc(1, bufsize); 1485084Sjohnlev if (ioc == NULL) { 1495084Sjohnlev (void) close(fd); 1505084Sjohnlev return (dladm_errno2status(ENOMEM)); 1515084Sjohnlev } 1525084Sjohnlev 153*5895Syz147064 ioc->vi_vnic_id = vnic_id; 1545084Sjohnlev rc = i_dladm_ioctl(fd, VNIC_IOC_INFO, ioc, bufsize); 1555084Sjohnlev if (rc != 0) { 1565084Sjohnlev status = dladm_errno2status(errno); 1575084Sjohnlev goto bail; 1585084Sjohnlev } 1595084Sjohnlev 160*5895Syz147064 vnic = (vnic_ioc_info_vnic_t *)(ioc + 1); 1615084Sjohnlev 162*5895Syz147064 attrp->va_vnic_id = vnic->vn_vnic_id; 163*5895Syz147064 attrp->va_link_id = vnic->vn_link_id; 164*5895Syz147064 attrp->va_mac_addr_type = vnic->vn_mac_addr_type; 165*5895Syz147064 bcopy(vnic->vn_mac_addr, attrp->va_mac_addr, ETHERADDRL); 166*5895Syz147064 attrp->va_mac_len = vnic->vn_mac_len; 1675084Sjohnlev 1685084Sjohnlev bail: 1695084Sjohnlev free(ioc); 1705084Sjohnlev (void) close(fd); 1715084Sjohnlev return (status); 1725084Sjohnlev } 1735084Sjohnlev 1745084Sjohnlev /* 1755084Sjohnlev * Remove a VNIC from the kernel. 1765084Sjohnlev */ 1775084Sjohnlev static dladm_status_t 1785084Sjohnlev i_dladm_vnic_delete_sys(int fd, dladm_vnic_attr_sys_t *attr) 1795084Sjohnlev { 1805084Sjohnlev vnic_ioc_delete_t ioc; 1815084Sjohnlev int rc; 1825084Sjohnlev 1835084Sjohnlev ioc.vd_vnic_id = attr->va_vnic_id; 1845084Sjohnlev 1855084Sjohnlev rc = i_dladm_ioctl(fd, VNIC_IOC_DELETE, &ioc, sizeof (ioc)); 1865084Sjohnlev 1875084Sjohnlev if (rc < 0) 1885084Sjohnlev return (dladm_errno2status(errno)); 1895084Sjohnlev 1905084Sjohnlev return (DLADM_STATUS_OK); 1915084Sjohnlev } 1925084Sjohnlev 1935084Sjohnlev /* 1945084Sjohnlev * Convert between MAC address types and their string representations. 1955084Sjohnlev */ 1965084Sjohnlev 1975084Sjohnlev typedef struct dladm_vnic_addr_type_s { 1985084Sjohnlev char *va_str; 1995084Sjohnlev vnic_mac_addr_type_t va_type; 2005084Sjohnlev } dladm_vnic_addr_type_t; 2015084Sjohnlev 2025084Sjohnlev static dladm_vnic_addr_type_t addr_types[] = { 2035084Sjohnlev {"fixed", VNIC_MAC_ADDR_TYPE_FIXED}, 2045084Sjohnlev }; 2055084Sjohnlev 2065084Sjohnlev #define NADDR_TYPES (sizeof (addr_types) / sizeof (dladm_vnic_addr_type_t)) 2075084Sjohnlev 208*5895Syz147064 /* 209*5895Syz147064 * Return DLADM_STATUS_OK if a matching type was found, 210*5895Syz147064 * DLADM_STATUS_BADARG otherwise 211*5895Syz147064 */ 212*5895Syz147064 dladm_status_t 213*5895Syz147064 dladm_vnic_str2macaddrtype(const char *str, vnic_mac_addr_type_t *val) 2145084Sjohnlev { 2155084Sjohnlev int i; 2165084Sjohnlev dladm_vnic_addr_type_t *type; 2175084Sjohnlev 2185084Sjohnlev for (i = 0; i < NADDR_TYPES; i++) { 2195084Sjohnlev type = &addr_types[i]; 2205084Sjohnlev if (strncmp(str, type->va_str, strlen(type->va_str)) == 0) { 2215084Sjohnlev *val = type->va_type; 222*5895Syz147064 return (DLADM_STATUS_OK); 2235084Sjohnlev } 2245084Sjohnlev } 2255084Sjohnlev 226*5895Syz147064 return (DLADM_STATUS_BADARG); 2275084Sjohnlev } 2285084Sjohnlev 2295084Sjohnlev /* 2305084Sjohnlev * Create a new VNIC. Update the configuration file and bring it up. 2315084Sjohnlev */ 2325084Sjohnlev dladm_status_t 233*5895Syz147064 dladm_vnic_create(const char *vnic, datalink_id_t linkid, 2345084Sjohnlev vnic_mac_addr_type_t mac_addr_type, uchar_t *mac_addr, int mac_len, 235*5895Syz147064 datalink_id_t *vnic_id_out, uint32_t flags) 2365084Sjohnlev { 2375084Sjohnlev dladm_vnic_attr_db_t attr; 238*5895Syz147064 int i, fd; 239*5895Syz147064 datalink_id_t vnic_id; 240*5895Syz147064 datalink_class_t class; 241*5895Syz147064 uint32_t media; 242*5895Syz147064 char *name = (char *)vnic; 2435084Sjohnlev dladm_status_t status; 2445084Sjohnlev 2455084Sjohnlev /* 2465084Sjohnlev * Sanity test arguments. 2475084Sjohnlev */ 248*5895Syz147064 if (flags & DLADM_OPT_PERSIST) 249*5895Syz147064 return (dladm_errno2status(ENOTSUP)); 2505084Sjohnlev 2515084Sjohnlev if (mac_len > MAXMACADDRLEN) 2525084Sjohnlev return (DLADM_STATUS_INVALIDMACADDRLEN); 2535084Sjohnlev 2545084Sjohnlev for (i = 0; i < NADDR_TYPES; i++) { 2555084Sjohnlev if (mac_addr_type == addr_types[i].va_type) 2565084Sjohnlev break; 2575084Sjohnlev } 2585084Sjohnlev if (i == NADDR_TYPES) 2595084Sjohnlev return (DLADM_STATUS_INVALIDMACADDRTYPE); 2605084Sjohnlev 261*5895Syz147064 if ((status = dladm_datalink_id2info(linkid, NULL, &class, &media, 262*5895Syz147064 NULL, 0)) != DLADM_STATUS_OK) { 263*5895Syz147064 return (status); 264*5895Syz147064 } 265*5895Syz147064 266*5895Syz147064 if (class == DATALINK_CLASS_VNIC) 267*5895Syz147064 return (DLADM_STATUS_BADARG); 2685084Sjohnlev 269*5895Syz147064 if (vnic == NULL) { 270*5895Syz147064 flags |= DLADM_OPT_PREFIX; 271*5895Syz147064 name = "vnic"; 272*5895Syz147064 } 273*5895Syz147064 274*5895Syz147064 if ((status = dladm_create_datalink_id(name, DATALINK_CLASS_VNIC, 275*5895Syz147064 media, flags, &vnic_id)) != DLADM_STATUS_OK) { 276*5895Syz147064 return (status); 2775084Sjohnlev } 2785084Sjohnlev 2795084Sjohnlev bzero(&attr, sizeof (attr)); 2805084Sjohnlev attr.vt_vnic_id = vnic_id; 281*5895Syz147064 attr.vt_link_id = linkid; 2825084Sjohnlev attr.vt_mac_addr_type = mac_addr_type; 2835084Sjohnlev attr.vt_mac_len = mac_len; 2845084Sjohnlev bcopy(mac_addr, attr.vt_mac_addr, mac_len); 2855084Sjohnlev 286*5895Syz147064 if ((fd = open(VNIC_DEV, O_RDWR)) < 0) { 287*5895Syz147064 status = dladm_errno2status(errno); 288*5895Syz147064 goto done; 289*5895Syz147064 } 290*5895Syz147064 291*5895Syz147064 status = i_dladm_vnic_create_sys(fd, &attr); 292*5895Syz147064 (void) close(fd); 2935084Sjohnlev 294*5895Syz147064 done: 295*5895Syz147064 if (status != DLADM_STATUS_OK) { 296*5895Syz147064 (void) dladm_destroy_datalink_id(vnic_id, 297*5895Syz147064 flags & ~DLADM_OPT_PREFIX); 298*5895Syz147064 } else { 299*5895Syz147064 *vnic_id_out = vnic_id; 300*5895Syz147064 } 3015084Sjohnlev 3025084Sjohnlev return (status); 3035084Sjohnlev } 3045084Sjohnlev 3055084Sjohnlev /* 3065084Sjohnlev * Modify the properties of a VNIC. 3075084Sjohnlev */ 3085084Sjohnlev dladm_status_t 309*5895Syz147064 dladm_vnic_modify(datalink_id_t vnic_id, uint32_t modify_mask, 3105084Sjohnlev vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr, 3115084Sjohnlev uint32_t flags) 3125084Sjohnlev { 3135084Sjohnlev dladm_vnic_modify_attr_t new_attr; 3145084Sjohnlev 3155084Sjohnlev /* for now, only temporary creations are supported */ 316*5895Syz147064 if (flags & DLADM_OPT_PERSIST) 3175084Sjohnlev return (dladm_errno2status(ENOTSUP)); 3185084Sjohnlev 3195084Sjohnlev bzero(&new_attr, sizeof (new_attr)); 3205084Sjohnlev 3215084Sjohnlev if (modify_mask & DLADM_VNIC_MODIFY_ADDR) { 3225084Sjohnlev new_attr.vm_mac_addr_type = mac_addr_type; 3235084Sjohnlev new_attr.vm_mac_len = mac_len; 3245084Sjohnlev bcopy(mac_addr, new_attr.vm_mac_addr, MAXMACADDRLEN); 3255084Sjohnlev } 3265084Sjohnlev 3275084Sjohnlev /* update the properties of the existing VNIC */ 3285084Sjohnlev return (i_dladm_vnic_modify_sys(vnic_id, modify_mask, &new_attr)); 3295084Sjohnlev } 3305084Sjohnlev 3315084Sjohnlev /* 3325084Sjohnlev * Delete a VNIC. 3335084Sjohnlev */ 3345084Sjohnlev dladm_status_t 335*5895Syz147064 dladm_vnic_delete(datalink_id_t vnic_id, uint32_t flags) 3365084Sjohnlev { 337*5895Syz147064 dladm_status_t status; 3385084Sjohnlev dladm_vnic_attr_sys_t sys_attr; 339*5895Syz147064 int fd; 3405084Sjohnlev 3415084Sjohnlev /* for now, only temporary deletes are supported */ 342*5895Syz147064 if (flags & DLADM_OPT_PERSIST) 3435084Sjohnlev return (dladm_errno2status(ENOTSUP)); 3445084Sjohnlev 345*5895Syz147064 if ((fd = open(VNIC_DEV, O_RDWR)) < 0) 346*5895Syz147064 return (dladm_errno2status(errno)); 347*5895Syz147064 3485084Sjohnlev sys_attr.va_vnic_id = vnic_id; 349*5895Syz147064 status = i_dladm_vnic_delete_sys(fd, &sys_attr); 350*5895Syz147064 (void) close(fd); 351*5895Syz147064 352*5895Syz147064 if (status != DLADM_STATUS_OK) 353*5895Syz147064 return (status); 354*5895Syz147064 355*5895Syz147064 (void) dladm_destroy_datalink_id(vnic_id, flags); 356*5895Syz147064 return (status); 3575084Sjohnlev } 358