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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 #include <string.h> 32 #include <fcntl.h> 33 #include <unistd.h> 34 #include <stropts.h> 35 #include <stdlib.h> 36 #include <errno.h> 37 #include <strings.h> 38 #include <libintl.h> 39 #include <net/if_types.h> 40 #include <net/if_dl.h> 41 #include <libdladm_impl.h> 42 #include <libdllink.h> 43 #include <libdlvnic.h> 44 45 /* 46 * VNIC administration library. 47 */ 48 49 #define VNIC_DEV "/devices/pseudo/vnic@0:" VNIC_CTL_NODE_NAME 50 51 /* Limits on buffer size for VNIC_IOC_INFO request */ 52 #define MIN_INFO_SIZE (4*1024) 53 #define MAX_INFO_SIZE (128*1024) 54 55 /* configuration database entry */ 56 typedef struct dladm_vnic_attr_db { 57 datalink_id_t vt_vnic_id; 58 datalink_id_t vt_link_id; 59 vnic_mac_addr_type_t vt_mac_addr_type; 60 uint_t vt_mac_len; 61 uchar_t vt_mac_addr[MAXMACADDRLEN]; 62 } dladm_vnic_attr_db_t; 63 64 typedef struct dladm_vnic_modify_attr { 65 vnic_mac_addr_type_t vm_mac_addr_type; 66 int vm_mac_len; 67 uchar_t vm_mac_addr[MAXMACADDRLEN]; 68 } dladm_vnic_modify_attr_t; 69 70 /* 71 * Send a create command to the VNIC driver. 72 */ 73 static dladm_status_t 74 i_dladm_vnic_create_sys(int fd, dladm_vnic_attr_db_t *attr) 75 { 76 int rc; 77 vnic_ioc_create_t ioc; 78 79 ioc.vc_vnic_id = attr->vt_vnic_id; 80 ioc.vc_link_id = attr->vt_link_id; 81 ioc.vc_mac_addr_type = attr->vt_mac_addr_type; 82 ioc.vc_mac_len = attr->vt_mac_len; 83 bcopy(attr->vt_mac_addr, ioc.vc_mac_addr, attr->vt_mac_len); 84 85 rc = i_dladm_ioctl(fd, VNIC_IOC_CREATE, &ioc, sizeof (ioc)); 86 87 if (rc < 0) 88 return (dladm_errno2status(errno)); 89 90 return (DLADM_STATUS_OK); 91 } 92 93 /* 94 * Send a modify command to the VNIC driver. 95 */ 96 static dladm_status_t 97 i_dladm_vnic_modify_sys(datalink_id_t vnic_id, uint32_t modify_mask, 98 dladm_vnic_modify_attr_t *attr) 99 { 100 int rc; 101 int fd; 102 vnic_ioc_modify_t ioc; 103 104 ioc.vm_vnic_id = vnic_id; 105 106 ioc.vm_modify_mask = 0; 107 if (modify_mask & DLADM_VNIC_MODIFY_ADDR) 108 ioc.vm_modify_mask |= VNIC_IOC_MODIFY_ADDR; 109 110 ioc.vm_mac_addr_type = attr->vm_mac_addr_type; 111 ioc.vm_mac_len = attr->vm_mac_len; 112 bcopy(attr->vm_mac_addr, ioc.vm_mac_addr, MAXMACADDRLEN); 113 114 if ((fd = open(VNIC_DEV, O_RDWR)) < 0) 115 return (dladm_errno2status(errno)); 116 117 rc = i_dladm_ioctl(fd, VNIC_IOC_MODIFY, &ioc, sizeof (ioc)); 118 119 (void) close(fd); 120 121 if (rc < 0) 122 return (dladm_errno2status(errno)); 123 124 return (DLADM_STATUS_OK); 125 } 126 127 /* 128 * Get the configuration information of the given VNIC. 129 */ 130 dladm_status_t 131 dladm_vnic_info(datalink_id_t vnic_id, dladm_vnic_attr_sys_t *attrp, 132 uint32_t flags) 133 { 134 vnic_ioc_info_t *ioc; 135 vnic_ioc_info_vnic_t *vnic; 136 int rc, bufsize, fd; 137 dladm_status_t status = DLADM_STATUS_OK; 138 139 /* for now, only temporary creations are supported */ 140 if (flags & DLADM_OPT_PERSIST) 141 return (dladm_errno2status(ENOTSUP)); 142 143 if ((fd = open(VNIC_DEV, O_RDWR)) == -1) 144 return (dladm_errno2status(errno)); 145 146 bufsize = sizeof (vnic_ioc_info_t) + sizeof (vnic_ioc_info_vnic_t); 147 ioc = (vnic_ioc_info_t *)calloc(1, bufsize); 148 if (ioc == NULL) { 149 (void) close(fd); 150 return (dladm_errno2status(ENOMEM)); 151 } 152 153 ioc->vi_vnic_id = vnic_id; 154 rc = i_dladm_ioctl(fd, VNIC_IOC_INFO, ioc, bufsize); 155 if (rc != 0) { 156 status = dladm_errno2status(errno); 157 goto bail; 158 } 159 160 vnic = (vnic_ioc_info_vnic_t *)(ioc + 1); 161 162 attrp->va_vnic_id = vnic->vn_vnic_id; 163 attrp->va_link_id = vnic->vn_link_id; 164 attrp->va_mac_addr_type = vnic->vn_mac_addr_type; 165 bcopy(vnic->vn_mac_addr, attrp->va_mac_addr, ETHERADDRL); 166 attrp->va_mac_len = vnic->vn_mac_len; 167 168 bail: 169 free(ioc); 170 (void) close(fd); 171 return (status); 172 } 173 174 /* 175 * Remove a VNIC from the kernel. 176 */ 177 static dladm_status_t 178 i_dladm_vnic_delete_sys(int fd, dladm_vnic_attr_sys_t *attr) 179 { 180 vnic_ioc_delete_t ioc; 181 int rc; 182 183 ioc.vd_vnic_id = attr->va_vnic_id; 184 185 rc = i_dladm_ioctl(fd, VNIC_IOC_DELETE, &ioc, sizeof (ioc)); 186 187 if (rc < 0) 188 return (dladm_errno2status(errno)); 189 190 return (DLADM_STATUS_OK); 191 } 192 193 /* 194 * Convert between MAC address types and their string representations. 195 */ 196 197 typedef struct dladm_vnic_addr_type_s { 198 char *va_str; 199 vnic_mac_addr_type_t va_type; 200 } dladm_vnic_addr_type_t; 201 202 static dladm_vnic_addr_type_t addr_types[] = { 203 {"fixed", VNIC_MAC_ADDR_TYPE_FIXED}, 204 }; 205 206 #define NADDR_TYPES (sizeof (addr_types) / sizeof (dladm_vnic_addr_type_t)) 207 208 /* 209 * Return DLADM_STATUS_OK if a matching type was found, 210 * DLADM_STATUS_BADARG otherwise 211 */ 212 dladm_status_t 213 dladm_vnic_str2macaddrtype(const char *str, vnic_mac_addr_type_t *val) 214 { 215 int i; 216 dladm_vnic_addr_type_t *type; 217 218 for (i = 0; i < NADDR_TYPES; i++) { 219 type = &addr_types[i]; 220 if (strncmp(str, type->va_str, strlen(type->va_str)) == 0) { 221 *val = type->va_type; 222 return (DLADM_STATUS_OK); 223 } 224 } 225 226 return (DLADM_STATUS_BADARG); 227 } 228 229 /* 230 * Create a new VNIC. Update the configuration file and bring it up. 231 */ 232 dladm_status_t 233 dladm_vnic_create(const char *vnic, datalink_id_t linkid, 234 vnic_mac_addr_type_t mac_addr_type, uchar_t *mac_addr, int mac_len, 235 datalink_id_t *vnic_id_out, uint32_t flags) 236 { 237 dladm_vnic_attr_db_t attr; 238 int i, fd; 239 datalink_id_t vnic_id; 240 datalink_class_t class; 241 uint32_t media; 242 char *name = (char *)vnic; 243 dladm_status_t status; 244 245 /* 246 * Sanity test arguments. 247 */ 248 if (flags & DLADM_OPT_PERSIST) 249 return (dladm_errno2status(ENOTSUP)); 250 251 if (mac_len > MAXMACADDRLEN) 252 return (DLADM_STATUS_INVALIDMACADDRLEN); 253 254 for (i = 0; i < NADDR_TYPES; i++) { 255 if (mac_addr_type == addr_types[i].va_type) 256 break; 257 } 258 if (i == NADDR_TYPES) 259 return (DLADM_STATUS_INVALIDMACADDRTYPE); 260 261 if ((status = dladm_datalink_id2info(linkid, NULL, &class, &media, 262 NULL, 0)) != DLADM_STATUS_OK) { 263 return (status); 264 } 265 266 if (class == DATALINK_CLASS_VNIC) 267 return (DLADM_STATUS_BADARG); 268 269 if (vnic == NULL) { 270 flags |= DLADM_OPT_PREFIX; 271 name = "vnic"; 272 } 273 274 if ((status = dladm_create_datalink_id(name, DATALINK_CLASS_VNIC, 275 media, flags, &vnic_id)) != DLADM_STATUS_OK) { 276 return (status); 277 } 278 279 bzero(&attr, sizeof (attr)); 280 attr.vt_vnic_id = vnic_id; 281 attr.vt_link_id = linkid; 282 attr.vt_mac_addr_type = mac_addr_type; 283 attr.vt_mac_len = mac_len; 284 bcopy(mac_addr, attr.vt_mac_addr, mac_len); 285 286 if ((fd = open(VNIC_DEV, O_RDWR)) < 0) { 287 status = dladm_errno2status(errno); 288 goto done; 289 } 290 291 status = i_dladm_vnic_create_sys(fd, &attr); 292 (void) close(fd); 293 294 done: 295 if (status != DLADM_STATUS_OK) { 296 (void) dladm_destroy_datalink_id(vnic_id, 297 flags & ~DLADM_OPT_PREFIX); 298 } else { 299 *vnic_id_out = vnic_id; 300 } 301 302 return (status); 303 } 304 305 /* 306 * Modify the properties of a VNIC. 307 */ 308 dladm_status_t 309 dladm_vnic_modify(datalink_id_t vnic_id, uint32_t modify_mask, 310 vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr, 311 uint32_t flags) 312 { 313 dladm_vnic_modify_attr_t new_attr; 314 315 /* for now, only temporary creations are supported */ 316 if (flags & DLADM_OPT_PERSIST) 317 return (dladm_errno2status(ENOTSUP)); 318 319 bzero(&new_attr, sizeof (new_attr)); 320 321 if (modify_mask & DLADM_VNIC_MODIFY_ADDR) { 322 new_attr.vm_mac_addr_type = mac_addr_type; 323 new_attr.vm_mac_len = mac_len; 324 bcopy(mac_addr, new_attr.vm_mac_addr, MAXMACADDRLEN); 325 } 326 327 /* update the properties of the existing VNIC */ 328 return (i_dladm_vnic_modify_sys(vnic_id, modify_mask, &new_attr)); 329 } 330 331 /* 332 * Delete a VNIC. 333 */ 334 dladm_status_t 335 dladm_vnic_delete(datalink_id_t vnic_id, uint32_t flags) 336 { 337 dladm_status_t status; 338 dladm_vnic_attr_sys_t sys_attr; 339 int fd; 340 341 /* for now, only temporary deletes are supported */ 342 if (flags & DLADM_OPT_PERSIST) 343 return (dladm_errno2status(ENOTSUP)); 344 345 if ((fd = open(VNIC_DEV, O_RDWR)) < 0) 346 return (dladm_errno2status(errno)); 347 348 sys_attr.va_vnic_id = vnic_id; 349 status = i_dladm_vnic_delete_sys(fd, &sys_attr); 350 (void) close(fd); 351 352 if (status != DLADM_STATUS_OK) 353 return (status); 354 355 (void) dladm_destroy_datalink_id(vnic_id, flags); 356 return (status); 357 } 358