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> 395084Sjohnlev #include <libdladm_impl.h> 405895Syz147064 #include <libdllink.h> 415084Sjohnlev #include <libdlvnic.h> 425084Sjohnlev 435084Sjohnlev /* 445084Sjohnlev * VNIC administration library. 455084Sjohnlev */ 465084Sjohnlev 475084Sjohnlev /* Limits on buffer size for VNIC_IOC_INFO request */ 485084Sjohnlev #define MIN_INFO_SIZE (4*1024) 495084Sjohnlev #define MAX_INFO_SIZE (128*1024) 505084Sjohnlev 515084Sjohnlev /* configuration database entry */ 525084Sjohnlev typedef struct dladm_vnic_attr_db { 535895Syz147064 datalink_id_t vt_vnic_id; 545895Syz147064 datalink_id_t vt_link_id; 555084Sjohnlev vnic_mac_addr_type_t vt_mac_addr_type; 565084Sjohnlev uint_t vt_mac_len; 575084Sjohnlev uchar_t vt_mac_addr[MAXMACADDRLEN]; 585084Sjohnlev } dladm_vnic_attr_db_t; 595084Sjohnlev 605084Sjohnlev typedef struct dladm_vnic_modify_attr { 615084Sjohnlev vnic_mac_addr_type_t vm_mac_addr_type; 625084Sjohnlev int vm_mac_len; 635084Sjohnlev uchar_t vm_mac_addr[MAXMACADDRLEN]; 645084Sjohnlev } dladm_vnic_modify_attr_t; 655084Sjohnlev 665084Sjohnlev /* 675084Sjohnlev * Send a create command to the VNIC driver. 685084Sjohnlev */ 695084Sjohnlev static dladm_status_t 705084Sjohnlev i_dladm_vnic_create_sys(int fd, dladm_vnic_attr_db_t *attr) 715084Sjohnlev { 725084Sjohnlev vnic_ioc_create_t ioc; 735084Sjohnlev 745084Sjohnlev ioc.vc_vnic_id = attr->vt_vnic_id; 755895Syz147064 ioc.vc_link_id = attr->vt_link_id; 765084Sjohnlev ioc.vc_mac_addr_type = attr->vt_mac_addr_type; 775084Sjohnlev ioc.vc_mac_len = attr->vt_mac_len; 785084Sjohnlev bcopy(attr->vt_mac_addr, ioc.vc_mac_addr, attr->vt_mac_len); 795084Sjohnlev 80*7408SSebastien.Roy@Sun.COM if (ioctl(fd, VNIC_IOC_CREATE, &ioc) < 0) 815084Sjohnlev return (dladm_errno2status(errno)); 825084Sjohnlev 835084Sjohnlev return (DLADM_STATUS_OK); 845084Sjohnlev } 855084Sjohnlev 865084Sjohnlev /* 875084Sjohnlev * Send a modify command to the VNIC driver. 885084Sjohnlev */ 895084Sjohnlev static dladm_status_t 905895Syz147064 i_dladm_vnic_modify_sys(datalink_id_t vnic_id, uint32_t modify_mask, 915084Sjohnlev dladm_vnic_modify_attr_t *attr) 925084Sjohnlev { 93*7408SSebastien.Roy@Sun.COM dladm_status_t status = DLADM_STATUS_OK; 945084Sjohnlev int fd; 955084Sjohnlev vnic_ioc_modify_t ioc; 965084Sjohnlev 975084Sjohnlev ioc.vm_vnic_id = vnic_id; 985084Sjohnlev 995084Sjohnlev ioc.vm_modify_mask = 0; 1005084Sjohnlev if (modify_mask & DLADM_VNIC_MODIFY_ADDR) 1015084Sjohnlev ioc.vm_modify_mask |= VNIC_IOC_MODIFY_ADDR; 1025084Sjohnlev 1035084Sjohnlev ioc.vm_mac_addr_type = attr->vm_mac_addr_type; 1045084Sjohnlev ioc.vm_mac_len = attr->vm_mac_len; 1055084Sjohnlev bcopy(attr->vm_mac_addr, ioc.vm_mac_addr, MAXMACADDRLEN); 1065084Sjohnlev 107*7408SSebastien.Roy@Sun.COM if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 1085084Sjohnlev return (dladm_errno2status(errno)); 1095084Sjohnlev 110*7408SSebastien.Roy@Sun.COM if (ioctl(fd, VNIC_IOC_MODIFY, &ioc) < 0) 111*7408SSebastien.Roy@Sun.COM status = dladm_errno2status(errno); 1125084Sjohnlev 1135084Sjohnlev (void) close(fd); 114*7408SSebastien.Roy@Sun.COM return (status); 1155084Sjohnlev } 1165084Sjohnlev 1175084Sjohnlev /* 1185895Syz147064 * Get the configuration information of the given VNIC. 1195084Sjohnlev */ 1205084Sjohnlev dladm_status_t 1215895Syz147064 dladm_vnic_info(datalink_id_t vnic_id, dladm_vnic_attr_sys_t *attrp, 1225895Syz147064 uint32_t flags) 1235084Sjohnlev { 1245084Sjohnlev vnic_ioc_info_t *ioc; 1255084Sjohnlev vnic_ioc_info_vnic_t *vnic; 126*7408SSebastien.Roy@Sun.COM int bufsize, fd; 1275084Sjohnlev dladm_status_t status = DLADM_STATUS_OK; 1285084Sjohnlev 1295895Syz147064 /* for now, only temporary creations are supported */ 1305895Syz147064 if (flags & DLADM_OPT_PERSIST) 1315895Syz147064 return (dladm_errno2status(ENOTSUP)); 1325895Syz147064 133*7408SSebastien.Roy@Sun.COM if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) == -1) 1345084Sjohnlev return (dladm_errno2status(errno)); 1355084Sjohnlev 1365895Syz147064 bufsize = sizeof (vnic_ioc_info_t) + sizeof (vnic_ioc_info_vnic_t); 1375084Sjohnlev ioc = (vnic_ioc_info_t *)calloc(1, bufsize); 1385084Sjohnlev if (ioc == NULL) { 1395084Sjohnlev (void) close(fd); 1405084Sjohnlev return (dladm_errno2status(ENOMEM)); 1415084Sjohnlev } 1425084Sjohnlev 1435895Syz147064 ioc->vi_vnic_id = vnic_id; 144*7408SSebastien.Roy@Sun.COM ioc->vi_size = bufsize - sizeof (vnic_ioc_info_t); 145*7408SSebastien.Roy@Sun.COM if (ioctl(fd, VNIC_IOC_INFO, ioc) != 0) { 1465084Sjohnlev status = dladm_errno2status(errno); 1475084Sjohnlev goto bail; 1485084Sjohnlev } 1495084Sjohnlev 1505895Syz147064 vnic = (vnic_ioc_info_vnic_t *)(ioc + 1); 1515084Sjohnlev 1525895Syz147064 attrp->va_vnic_id = vnic->vn_vnic_id; 1535895Syz147064 attrp->va_link_id = vnic->vn_link_id; 1545895Syz147064 attrp->va_mac_addr_type = vnic->vn_mac_addr_type; 1555895Syz147064 bcopy(vnic->vn_mac_addr, attrp->va_mac_addr, ETHERADDRL); 1565895Syz147064 attrp->va_mac_len = vnic->vn_mac_len; 1575084Sjohnlev 1585084Sjohnlev bail: 1595084Sjohnlev free(ioc); 1605084Sjohnlev (void) close(fd); 1615084Sjohnlev return (status); 1625084Sjohnlev } 1635084Sjohnlev 1645084Sjohnlev /* 1655084Sjohnlev * Remove a VNIC from the kernel. 1665084Sjohnlev */ 1675084Sjohnlev static dladm_status_t 1685084Sjohnlev i_dladm_vnic_delete_sys(int fd, dladm_vnic_attr_sys_t *attr) 1695084Sjohnlev { 1705084Sjohnlev vnic_ioc_delete_t ioc; 1715084Sjohnlev 1725084Sjohnlev ioc.vd_vnic_id = attr->va_vnic_id; 1735084Sjohnlev 174*7408SSebastien.Roy@Sun.COM if (ioctl(fd, VNIC_IOC_DELETE, &ioc) < 0) 1755084Sjohnlev return (dladm_errno2status(errno)); 1765084Sjohnlev 1775084Sjohnlev return (DLADM_STATUS_OK); 1785084Sjohnlev } 1795084Sjohnlev 1805084Sjohnlev /* 1815084Sjohnlev * Convert between MAC address types and their string representations. 1825084Sjohnlev */ 1835084Sjohnlev 1845084Sjohnlev typedef struct dladm_vnic_addr_type_s { 1855084Sjohnlev char *va_str; 1865084Sjohnlev vnic_mac_addr_type_t va_type; 1875084Sjohnlev } dladm_vnic_addr_type_t; 1885084Sjohnlev 1895084Sjohnlev static dladm_vnic_addr_type_t addr_types[] = { 1905084Sjohnlev {"fixed", VNIC_MAC_ADDR_TYPE_FIXED}, 1915084Sjohnlev }; 1925084Sjohnlev 1935084Sjohnlev #define NADDR_TYPES (sizeof (addr_types) / sizeof (dladm_vnic_addr_type_t)) 1945084Sjohnlev 1955895Syz147064 /* 1965895Syz147064 * Return DLADM_STATUS_OK if a matching type was found, 1975895Syz147064 * DLADM_STATUS_BADARG otherwise 1985895Syz147064 */ 1995895Syz147064 dladm_status_t 2005895Syz147064 dladm_vnic_str2macaddrtype(const char *str, vnic_mac_addr_type_t *val) 2015084Sjohnlev { 2025084Sjohnlev int i; 2035084Sjohnlev dladm_vnic_addr_type_t *type; 2045084Sjohnlev 2055084Sjohnlev for (i = 0; i < NADDR_TYPES; i++) { 2065084Sjohnlev type = &addr_types[i]; 2075084Sjohnlev if (strncmp(str, type->va_str, strlen(type->va_str)) == 0) { 2085084Sjohnlev *val = type->va_type; 2095895Syz147064 return (DLADM_STATUS_OK); 2105084Sjohnlev } 2115084Sjohnlev } 2125084Sjohnlev 2135895Syz147064 return (DLADM_STATUS_BADARG); 2145084Sjohnlev } 2155084Sjohnlev 2165084Sjohnlev /* 2175084Sjohnlev * Create a new VNIC. Update the configuration file and bring it up. 2185084Sjohnlev */ 2195084Sjohnlev dladm_status_t 2205895Syz147064 dladm_vnic_create(const char *vnic, datalink_id_t linkid, 2215084Sjohnlev vnic_mac_addr_type_t mac_addr_type, uchar_t *mac_addr, int mac_len, 2225895Syz147064 datalink_id_t *vnic_id_out, uint32_t flags) 2235084Sjohnlev { 2245084Sjohnlev dladm_vnic_attr_db_t attr; 2255895Syz147064 int i, fd; 2265895Syz147064 datalink_id_t vnic_id; 2275895Syz147064 datalink_class_t class; 2285895Syz147064 uint32_t media; 2295895Syz147064 char *name = (char *)vnic; 2305084Sjohnlev dladm_status_t status; 2315084Sjohnlev 2325084Sjohnlev /* 2335084Sjohnlev * Sanity test arguments. 2345084Sjohnlev */ 2355895Syz147064 if (flags & DLADM_OPT_PERSIST) 2365895Syz147064 return (dladm_errno2status(ENOTSUP)); 2375084Sjohnlev 2385084Sjohnlev if (mac_len > MAXMACADDRLEN) 2395084Sjohnlev return (DLADM_STATUS_INVALIDMACADDRLEN); 2405084Sjohnlev 2415084Sjohnlev for (i = 0; i < NADDR_TYPES; i++) { 2425084Sjohnlev if (mac_addr_type == addr_types[i].va_type) 2435084Sjohnlev break; 2445084Sjohnlev } 2455084Sjohnlev if (i == NADDR_TYPES) 2465084Sjohnlev return (DLADM_STATUS_INVALIDMACADDRTYPE); 2475084Sjohnlev 2485895Syz147064 if ((status = dladm_datalink_id2info(linkid, NULL, &class, &media, 2495895Syz147064 NULL, 0)) != DLADM_STATUS_OK) { 2505895Syz147064 return (status); 2515895Syz147064 } 2525895Syz147064 2535895Syz147064 if (class == DATALINK_CLASS_VNIC) 2545895Syz147064 return (DLADM_STATUS_BADARG); 2555084Sjohnlev 2565895Syz147064 if (vnic == NULL) { 2575895Syz147064 flags |= DLADM_OPT_PREFIX; 2585895Syz147064 name = "vnic"; 2595895Syz147064 } 2605895Syz147064 2615895Syz147064 if ((status = dladm_create_datalink_id(name, DATALINK_CLASS_VNIC, 2625895Syz147064 media, flags, &vnic_id)) != DLADM_STATUS_OK) { 2635895Syz147064 return (status); 2645084Sjohnlev } 2655084Sjohnlev 2665084Sjohnlev bzero(&attr, sizeof (attr)); 2675084Sjohnlev attr.vt_vnic_id = vnic_id; 2685895Syz147064 attr.vt_link_id = linkid; 2695084Sjohnlev attr.vt_mac_addr_type = mac_addr_type; 2705084Sjohnlev attr.vt_mac_len = mac_len; 2715084Sjohnlev bcopy(mac_addr, attr.vt_mac_addr, mac_len); 2725084Sjohnlev 273*7408SSebastien.Roy@Sun.COM if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) { 2745895Syz147064 status = dladm_errno2status(errno); 2755895Syz147064 goto done; 2765895Syz147064 } 2775895Syz147064 2785895Syz147064 status = i_dladm_vnic_create_sys(fd, &attr); 2795895Syz147064 (void) close(fd); 2805084Sjohnlev 2815895Syz147064 done: 2825895Syz147064 if (status != DLADM_STATUS_OK) { 2835895Syz147064 (void) dladm_destroy_datalink_id(vnic_id, 2845895Syz147064 flags & ~DLADM_OPT_PREFIX); 2855895Syz147064 } else { 2865895Syz147064 *vnic_id_out = vnic_id; 2875895Syz147064 } 2885084Sjohnlev 2895084Sjohnlev return (status); 2905084Sjohnlev } 2915084Sjohnlev 2925084Sjohnlev /* 2935084Sjohnlev * Modify the properties of a VNIC. 2945084Sjohnlev */ 2955084Sjohnlev dladm_status_t 2965895Syz147064 dladm_vnic_modify(datalink_id_t vnic_id, uint32_t modify_mask, 2975084Sjohnlev vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr, 2985084Sjohnlev uint32_t flags) 2995084Sjohnlev { 3005084Sjohnlev dladm_vnic_modify_attr_t new_attr; 3015084Sjohnlev 3025084Sjohnlev /* for now, only temporary creations are supported */ 3035895Syz147064 if (flags & DLADM_OPT_PERSIST) 3045084Sjohnlev return (dladm_errno2status(ENOTSUP)); 3055084Sjohnlev 3065084Sjohnlev bzero(&new_attr, sizeof (new_attr)); 3075084Sjohnlev 3085084Sjohnlev if (modify_mask & DLADM_VNIC_MODIFY_ADDR) { 3095084Sjohnlev new_attr.vm_mac_addr_type = mac_addr_type; 3105084Sjohnlev new_attr.vm_mac_len = mac_len; 3115084Sjohnlev bcopy(mac_addr, new_attr.vm_mac_addr, MAXMACADDRLEN); 3125084Sjohnlev } 3135084Sjohnlev 3145084Sjohnlev /* update the properties of the existing VNIC */ 3155084Sjohnlev return (i_dladm_vnic_modify_sys(vnic_id, modify_mask, &new_attr)); 3165084Sjohnlev } 3175084Sjohnlev 3185084Sjohnlev /* 3195084Sjohnlev * Delete a VNIC. 3205084Sjohnlev */ 3215084Sjohnlev dladm_status_t 3225895Syz147064 dladm_vnic_delete(datalink_id_t vnic_id, uint32_t flags) 3235084Sjohnlev { 3245895Syz147064 dladm_status_t status; 3255084Sjohnlev dladm_vnic_attr_sys_t sys_attr; 3265895Syz147064 int fd; 3275084Sjohnlev 3285084Sjohnlev /* for now, only temporary deletes are supported */ 3295895Syz147064 if (flags & DLADM_OPT_PERSIST) 3305084Sjohnlev return (dladm_errno2status(ENOTSUP)); 3315084Sjohnlev 332*7408SSebastien.Roy@Sun.COM if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 3335895Syz147064 return (dladm_errno2status(errno)); 3345895Syz147064 3355084Sjohnlev sys_attr.va_vnic_id = vnic_id; 3365895Syz147064 status = i_dladm_vnic_delete_sys(fd, &sys_attr); 3375895Syz147064 (void) close(fd); 3385895Syz147064 3395895Syz147064 if (status != DLADM_STATUS_OK) 3405895Syz147064 return (status); 3415895Syz147064 3425895Syz147064 (void) dladm_destroy_datalink_id(vnic_id, flags); 3435895Syz147064 return (status); 3445084Sjohnlev } 345