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