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