1843e1988Sjohnlev /* 2843e1988Sjohnlev * CDDL HEADER START 3843e1988Sjohnlev * 4843e1988Sjohnlev * The contents of this file are subject to the terms of the 5843e1988Sjohnlev * Common Development and Distribution License (the "License"). 6843e1988Sjohnlev * You may not use this file except in compliance with the License. 7843e1988Sjohnlev * 8843e1988Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9843e1988Sjohnlev * or http://www.opensolaris.org/os/licensing. 10843e1988Sjohnlev * See the License for the specific language governing permissions 11843e1988Sjohnlev * and limitations under the License. 12843e1988Sjohnlev * 13843e1988Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each 14843e1988Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15843e1988Sjohnlev * If applicable, add the following below this CDDL HEADER, with the 16843e1988Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying 17843e1988Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner] 18843e1988Sjohnlev * 19843e1988Sjohnlev * CDDL HEADER END 20843e1988Sjohnlev */ 21843e1988Sjohnlev /* 22*d62bc4baSyz147064 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23843e1988Sjohnlev * Use is subject to license terms. 24843e1988Sjohnlev */ 25843e1988Sjohnlev 26843e1988Sjohnlev #pragma ident "%Z%%M% %I% %E% SMI" 27843e1988Sjohnlev 28843e1988Sjohnlev #include <stdio.h> 29843e1988Sjohnlev #include <sys/types.h> 30843e1988Sjohnlev #include <sys/stat.h> 31843e1988Sjohnlev #include <string.h> 32843e1988Sjohnlev #include <fcntl.h> 33843e1988Sjohnlev #include <unistd.h> 34843e1988Sjohnlev #include <stropts.h> 35843e1988Sjohnlev #include <stdlib.h> 36843e1988Sjohnlev #include <errno.h> 37843e1988Sjohnlev #include <strings.h> 38843e1988Sjohnlev #include <libintl.h> 39843e1988Sjohnlev #include <net/if_types.h> 40843e1988Sjohnlev #include <net/if_dl.h> 41843e1988Sjohnlev #include <libdladm_impl.h> 42*d62bc4baSyz147064 #include <libdllink.h> 43843e1988Sjohnlev #include <libdlvnic.h> 44843e1988Sjohnlev 45843e1988Sjohnlev /* 46843e1988Sjohnlev * VNIC administration library. 47843e1988Sjohnlev */ 48843e1988Sjohnlev 49843e1988Sjohnlev #define VNIC_DEV "/devices/pseudo/vnic@0:" VNIC_CTL_NODE_NAME 50843e1988Sjohnlev 51843e1988Sjohnlev /* Limits on buffer size for VNIC_IOC_INFO request */ 52843e1988Sjohnlev #define MIN_INFO_SIZE (4*1024) 53843e1988Sjohnlev #define MAX_INFO_SIZE (128*1024) 54843e1988Sjohnlev 55843e1988Sjohnlev /* configuration database entry */ 56843e1988Sjohnlev typedef struct dladm_vnic_attr_db { 57*d62bc4baSyz147064 datalink_id_t vt_vnic_id; 58*d62bc4baSyz147064 datalink_id_t vt_link_id; 59843e1988Sjohnlev vnic_mac_addr_type_t vt_mac_addr_type; 60843e1988Sjohnlev uint_t vt_mac_len; 61843e1988Sjohnlev uchar_t vt_mac_addr[MAXMACADDRLEN]; 62843e1988Sjohnlev } dladm_vnic_attr_db_t; 63843e1988Sjohnlev 64843e1988Sjohnlev typedef struct dladm_vnic_modify_attr { 65843e1988Sjohnlev vnic_mac_addr_type_t vm_mac_addr_type; 66843e1988Sjohnlev int vm_mac_len; 67843e1988Sjohnlev uchar_t vm_mac_addr[MAXMACADDRLEN]; 68843e1988Sjohnlev } dladm_vnic_modify_attr_t; 69843e1988Sjohnlev 70843e1988Sjohnlev /* 71843e1988Sjohnlev * Send a create command to the VNIC driver. 72843e1988Sjohnlev */ 73843e1988Sjohnlev static dladm_status_t 74843e1988Sjohnlev i_dladm_vnic_create_sys(int fd, dladm_vnic_attr_db_t *attr) 75843e1988Sjohnlev { 76843e1988Sjohnlev int rc; 77843e1988Sjohnlev vnic_ioc_create_t ioc; 78843e1988Sjohnlev 79843e1988Sjohnlev ioc.vc_vnic_id = attr->vt_vnic_id; 80*d62bc4baSyz147064 ioc.vc_link_id = attr->vt_link_id; 81843e1988Sjohnlev ioc.vc_mac_addr_type = attr->vt_mac_addr_type; 82843e1988Sjohnlev ioc.vc_mac_len = attr->vt_mac_len; 83843e1988Sjohnlev bcopy(attr->vt_mac_addr, ioc.vc_mac_addr, attr->vt_mac_len); 84843e1988Sjohnlev 85843e1988Sjohnlev rc = i_dladm_ioctl(fd, VNIC_IOC_CREATE, &ioc, sizeof (ioc)); 86843e1988Sjohnlev 87843e1988Sjohnlev if (rc < 0) 88843e1988Sjohnlev return (dladm_errno2status(errno)); 89843e1988Sjohnlev 90843e1988Sjohnlev return (DLADM_STATUS_OK); 91843e1988Sjohnlev } 92843e1988Sjohnlev 93843e1988Sjohnlev /* 94843e1988Sjohnlev * Send a modify command to the VNIC driver. 95843e1988Sjohnlev */ 96843e1988Sjohnlev static dladm_status_t 97*d62bc4baSyz147064 i_dladm_vnic_modify_sys(datalink_id_t vnic_id, uint32_t modify_mask, 98843e1988Sjohnlev dladm_vnic_modify_attr_t *attr) 99843e1988Sjohnlev { 100843e1988Sjohnlev int rc; 101843e1988Sjohnlev int fd; 102843e1988Sjohnlev vnic_ioc_modify_t ioc; 103843e1988Sjohnlev 104843e1988Sjohnlev ioc.vm_vnic_id = vnic_id; 105843e1988Sjohnlev 106843e1988Sjohnlev ioc.vm_modify_mask = 0; 107843e1988Sjohnlev if (modify_mask & DLADM_VNIC_MODIFY_ADDR) 108843e1988Sjohnlev ioc.vm_modify_mask |= VNIC_IOC_MODIFY_ADDR; 109843e1988Sjohnlev 110843e1988Sjohnlev ioc.vm_mac_addr_type = attr->vm_mac_addr_type; 111843e1988Sjohnlev ioc.vm_mac_len = attr->vm_mac_len; 112843e1988Sjohnlev bcopy(attr->vm_mac_addr, ioc.vm_mac_addr, MAXMACADDRLEN); 113843e1988Sjohnlev 114843e1988Sjohnlev if ((fd = open(VNIC_DEV, O_RDWR)) < 0) 115843e1988Sjohnlev return (dladm_errno2status(errno)); 116843e1988Sjohnlev 117843e1988Sjohnlev rc = i_dladm_ioctl(fd, VNIC_IOC_MODIFY, &ioc, sizeof (ioc)); 118843e1988Sjohnlev 119843e1988Sjohnlev (void) close(fd); 120843e1988Sjohnlev 121843e1988Sjohnlev if (rc < 0) 122843e1988Sjohnlev return (dladm_errno2status(errno)); 123843e1988Sjohnlev 124843e1988Sjohnlev return (DLADM_STATUS_OK); 125843e1988Sjohnlev } 126843e1988Sjohnlev 127843e1988Sjohnlev /* 128*d62bc4baSyz147064 * Get the configuration information of the given VNIC. 129843e1988Sjohnlev */ 130843e1988Sjohnlev dladm_status_t 131*d62bc4baSyz147064 dladm_vnic_info(datalink_id_t vnic_id, dladm_vnic_attr_sys_t *attrp, 132*d62bc4baSyz147064 uint32_t flags) 133843e1988Sjohnlev { 134843e1988Sjohnlev vnic_ioc_info_t *ioc; 135843e1988Sjohnlev vnic_ioc_info_vnic_t *vnic; 136*d62bc4baSyz147064 int rc, bufsize, fd; 137843e1988Sjohnlev dladm_status_t status = DLADM_STATUS_OK; 138843e1988Sjohnlev 139*d62bc4baSyz147064 /* for now, only temporary creations are supported */ 140*d62bc4baSyz147064 if (flags & DLADM_OPT_PERSIST) 141*d62bc4baSyz147064 return (dladm_errno2status(ENOTSUP)); 142*d62bc4baSyz147064 143843e1988Sjohnlev if ((fd = open(VNIC_DEV, O_RDWR)) == -1) 144843e1988Sjohnlev return (dladm_errno2status(errno)); 145843e1988Sjohnlev 146*d62bc4baSyz147064 bufsize = sizeof (vnic_ioc_info_t) + sizeof (vnic_ioc_info_vnic_t); 147843e1988Sjohnlev ioc = (vnic_ioc_info_t *)calloc(1, bufsize); 148843e1988Sjohnlev if (ioc == NULL) { 149843e1988Sjohnlev (void) close(fd); 150843e1988Sjohnlev return (dladm_errno2status(ENOMEM)); 151843e1988Sjohnlev } 152843e1988Sjohnlev 153*d62bc4baSyz147064 ioc->vi_vnic_id = vnic_id; 154843e1988Sjohnlev rc = i_dladm_ioctl(fd, VNIC_IOC_INFO, ioc, bufsize); 155843e1988Sjohnlev if (rc != 0) { 156843e1988Sjohnlev status = dladm_errno2status(errno); 157843e1988Sjohnlev goto bail; 158843e1988Sjohnlev } 159843e1988Sjohnlev 160*d62bc4baSyz147064 vnic = (vnic_ioc_info_vnic_t *)(ioc + 1); 161843e1988Sjohnlev 162*d62bc4baSyz147064 attrp->va_vnic_id = vnic->vn_vnic_id; 163*d62bc4baSyz147064 attrp->va_link_id = vnic->vn_link_id; 164*d62bc4baSyz147064 attrp->va_mac_addr_type = vnic->vn_mac_addr_type; 165*d62bc4baSyz147064 bcopy(vnic->vn_mac_addr, attrp->va_mac_addr, ETHERADDRL); 166*d62bc4baSyz147064 attrp->va_mac_len = vnic->vn_mac_len; 167843e1988Sjohnlev 168843e1988Sjohnlev bail: 169843e1988Sjohnlev free(ioc); 170843e1988Sjohnlev (void) close(fd); 171843e1988Sjohnlev return (status); 172843e1988Sjohnlev } 173843e1988Sjohnlev 174843e1988Sjohnlev /* 175843e1988Sjohnlev * Remove a VNIC from the kernel. 176843e1988Sjohnlev */ 177843e1988Sjohnlev static dladm_status_t 178843e1988Sjohnlev i_dladm_vnic_delete_sys(int fd, dladm_vnic_attr_sys_t *attr) 179843e1988Sjohnlev { 180843e1988Sjohnlev vnic_ioc_delete_t ioc; 181843e1988Sjohnlev int rc; 182843e1988Sjohnlev 183843e1988Sjohnlev ioc.vd_vnic_id = attr->va_vnic_id; 184843e1988Sjohnlev 185843e1988Sjohnlev rc = i_dladm_ioctl(fd, VNIC_IOC_DELETE, &ioc, sizeof (ioc)); 186843e1988Sjohnlev 187843e1988Sjohnlev if (rc < 0) 188843e1988Sjohnlev return (dladm_errno2status(errno)); 189843e1988Sjohnlev 190843e1988Sjohnlev return (DLADM_STATUS_OK); 191843e1988Sjohnlev } 192843e1988Sjohnlev 193843e1988Sjohnlev /* 194843e1988Sjohnlev * Convert between MAC address types and their string representations. 195843e1988Sjohnlev */ 196843e1988Sjohnlev 197843e1988Sjohnlev typedef struct dladm_vnic_addr_type_s { 198843e1988Sjohnlev char *va_str; 199843e1988Sjohnlev vnic_mac_addr_type_t va_type; 200843e1988Sjohnlev } dladm_vnic_addr_type_t; 201843e1988Sjohnlev 202843e1988Sjohnlev static dladm_vnic_addr_type_t addr_types[] = { 203843e1988Sjohnlev {"fixed", VNIC_MAC_ADDR_TYPE_FIXED}, 204843e1988Sjohnlev }; 205843e1988Sjohnlev 206843e1988Sjohnlev #define NADDR_TYPES (sizeof (addr_types) / sizeof (dladm_vnic_addr_type_t)) 207843e1988Sjohnlev 208*d62bc4baSyz147064 /* 209*d62bc4baSyz147064 * Return DLADM_STATUS_OK if a matching type was found, 210*d62bc4baSyz147064 * DLADM_STATUS_BADARG otherwise 211*d62bc4baSyz147064 */ 212*d62bc4baSyz147064 dladm_status_t 213*d62bc4baSyz147064 dladm_vnic_str2macaddrtype(const char *str, vnic_mac_addr_type_t *val) 214843e1988Sjohnlev { 215843e1988Sjohnlev int i; 216843e1988Sjohnlev dladm_vnic_addr_type_t *type; 217843e1988Sjohnlev 218843e1988Sjohnlev for (i = 0; i < NADDR_TYPES; i++) { 219843e1988Sjohnlev type = &addr_types[i]; 220843e1988Sjohnlev if (strncmp(str, type->va_str, strlen(type->va_str)) == 0) { 221843e1988Sjohnlev *val = type->va_type; 222843e1988Sjohnlev return (DLADM_STATUS_OK); 223843e1988Sjohnlev } 224843e1988Sjohnlev } 225843e1988Sjohnlev 226*d62bc4baSyz147064 return (DLADM_STATUS_BADARG); 227843e1988Sjohnlev } 228843e1988Sjohnlev 229843e1988Sjohnlev /* 230843e1988Sjohnlev * Create a new VNIC. Update the configuration file and bring it up. 231843e1988Sjohnlev */ 232843e1988Sjohnlev dladm_status_t 233*d62bc4baSyz147064 dladm_vnic_create(const char *vnic, datalink_id_t linkid, 234843e1988Sjohnlev vnic_mac_addr_type_t mac_addr_type, uchar_t *mac_addr, int mac_len, 235*d62bc4baSyz147064 datalink_id_t *vnic_id_out, uint32_t flags) 236843e1988Sjohnlev { 237843e1988Sjohnlev dladm_vnic_attr_db_t attr; 238*d62bc4baSyz147064 int i, fd; 239*d62bc4baSyz147064 datalink_id_t vnic_id; 240*d62bc4baSyz147064 datalink_class_t class; 241*d62bc4baSyz147064 uint32_t media; 242*d62bc4baSyz147064 char *name = (char *)vnic; 243843e1988Sjohnlev dladm_status_t status; 244843e1988Sjohnlev 245843e1988Sjohnlev /* 246843e1988Sjohnlev * Sanity test arguments. 247843e1988Sjohnlev */ 248*d62bc4baSyz147064 if (flags & DLADM_OPT_PERSIST) 249*d62bc4baSyz147064 return (dladm_errno2status(ENOTSUP)); 250843e1988Sjohnlev 251843e1988Sjohnlev if (mac_len > MAXMACADDRLEN) 252843e1988Sjohnlev return (DLADM_STATUS_INVALIDMACADDRLEN); 253843e1988Sjohnlev 254843e1988Sjohnlev for (i = 0; i < NADDR_TYPES; i++) { 255843e1988Sjohnlev if (mac_addr_type == addr_types[i].va_type) 256843e1988Sjohnlev break; 257843e1988Sjohnlev } 258843e1988Sjohnlev if (i == NADDR_TYPES) 259843e1988Sjohnlev return (DLADM_STATUS_INVALIDMACADDRTYPE); 260843e1988Sjohnlev 261*d62bc4baSyz147064 if ((status = dladm_datalink_id2info(linkid, NULL, &class, &media, 262*d62bc4baSyz147064 NULL, 0)) != DLADM_STATUS_OK) { 263843e1988Sjohnlev return (status); 264*d62bc4baSyz147064 } 265*d62bc4baSyz147064 266*d62bc4baSyz147064 if (class == DATALINK_CLASS_VNIC) 267*d62bc4baSyz147064 return (DLADM_STATUS_BADARG); 268*d62bc4baSyz147064 269*d62bc4baSyz147064 if (vnic == NULL) { 270*d62bc4baSyz147064 flags |= DLADM_OPT_PREFIX; 271*d62bc4baSyz147064 name = "vnic"; 272*d62bc4baSyz147064 } 273*d62bc4baSyz147064 274*d62bc4baSyz147064 if ((status = dladm_create_datalink_id(name, DATALINK_CLASS_VNIC, 275*d62bc4baSyz147064 media, flags, &vnic_id)) != DLADM_STATUS_OK) { 276*d62bc4baSyz147064 return (status); 277843e1988Sjohnlev } 278843e1988Sjohnlev 279843e1988Sjohnlev bzero(&attr, sizeof (attr)); 280843e1988Sjohnlev attr.vt_vnic_id = vnic_id; 281*d62bc4baSyz147064 attr.vt_link_id = linkid; 282843e1988Sjohnlev attr.vt_mac_addr_type = mac_addr_type; 283843e1988Sjohnlev attr.vt_mac_len = mac_len; 284843e1988Sjohnlev bcopy(mac_addr, attr.vt_mac_addr, mac_len); 285843e1988Sjohnlev 286*d62bc4baSyz147064 if ((fd = open(VNIC_DEV, O_RDWR)) < 0) { 287*d62bc4baSyz147064 status = dladm_errno2status(errno); 288*d62bc4baSyz147064 goto done; 289*d62bc4baSyz147064 } 290843e1988Sjohnlev 291*d62bc4baSyz147064 status = i_dladm_vnic_create_sys(fd, &attr); 292*d62bc4baSyz147064 (void) close(fd); 293843e1988Sjohnlev 294*d62bc4baSyz147064 done: 295*d62bc4baSyz147064 if (status != DLADM_STATUS_OK) { 296*d62bc4baSyz147064 (void) dladm_destroy_datalink_id(vnic_id, 297*d62bc4baSyz147064 flags & ~DLADM_OPT_PREFIX); 298*d62bc4baSyz147064 } else { 299*d62bc4baSyz147064 *vnic_id_out = vnic_id; 300*d62bc4baSyz147064 } 301843e1988Sjohnlev 302843e1988Sjohnlev return (status); 303843e1988Sjohnlev } 304843e1988Sjohnlev 305843e1988Sjohnlev /* 306843e1988Sjohnlev * Modify the properties of a VNIC. 307843e1988Sjohnlev */ 308843e1988Sjohnlev dladm_status_t 309*d62bc4baSyz147064 dladm_vnic_modify(datalink_id_t vnic_id, uint32_t modify_mask, 310843e1988Sjohnlev vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr, 311843e1988Sjohnlev uint32_t flags) 312843e1988Sjohnlev { 313843e1988Sjohnlev dladm_vnic_modify_attr_t new_attr; 314843e1988Sjohnlev 315843e1988Sjohnlev /* for now, only temporary creations are supported */ 316*d62bc4baSyz147064 if (flags & DLADM_OPT_PERSIST) 317843e1988Sjohnlev return (dladm_errno2status(ENOTSUP)); 318843e1988Sjohnlev 319843e1988Sjohnlev bzero(&new_attr, sizeof (new_attr)); 320843e1988Sjohnlev 321843e1988Sjohnlev if (modify_mask & DLADM_VNIC_MODIFY_ADDR) { 322843e1988Sjohnlev new_attr.vm_mac_addr_type = mac_addr_type; 323843e1988Sjohnlev new_attr.vm_mac_len = mac_len; 324843e1988Sjohnlev bcopy(mac_addr, new_attr.vm_mac_addr, MAXMACADDRLEN); 325843e1988Sjohnlev } 326843e1988Sjohnlev 327843e1988Sjohnlev /* update the properties of the existing VNIC */ 328843e1988Sjohnlev return (i_dladm_vnic_modify_sys(vnic_id, modify_mask, &new_attr)); 329843e1988Sjohnlev } 330843e1988Sjohnlev 331843e1988Sjohnlev /* 332843e1988Sjohnlev * Delete a VNIC. 333843e1988Sjohnlev */ 334843e1988Sjohnlev dladm_status_t 335*d62bc4baSyz147064 dladm_vnic_delete(datalink_id_t vnic_id, uint32_t flags) 336843e1988Sjohnlev { 337*d62bc4baSyz147064 dladm_status_t status; 338843e1988Sjohnlev dladm_vnic_attr_sys_t sys_attr; 339*d62bc4baSyz147064 int fd; 340843e1988Sjohnlev 341843e1988Sjohnlev /* for now, only temporary deletes are supported */ 342*d62bc4baSyz147064 if (flags & DLADM_OPT_PERSIST) 343843e1988Sjohnlev return (dladm_errno2status(ENOTSUP)); 344843e1988Sjohnlev 345*d62bc4baSyz147064 if ((fd = open(VNIC_DEV, O_RDWR)) < 0) 346*d62bc4baSyz147064 return (dladm_errno2status(errno)); 347*d62bc4baSyz147064 348843e1988Sjohnlev sys_attr.va_vnic_id = vnic_id; 349*d62bc4baSyz147064 status = i_dladm_vnic_delete_sys(fd, &sys_attr); 350*d62bc4baSyz147064 (void) close(fd); 351*d62bc4baSyz147064 352*d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 353*d62bc4baSyz147064 return (status); 354*d62bc4baSyz147064 355*d62bc4baSyz147064 (void) dladm_destroy_datalink_id(vnic_id, flags); 356*d62bc4baSyz147064 return (status); 357843e1988Sjohnlev } 358