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 /*
22*5895Syz147064  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
235084Sjohnlev  * Use is subject to license terms.
245084Sjohnlev  */
255084Sjohnlev 
265084Sjohnlev #pragma ident	"%Z%%M%	%I%	%E% SMI"
275084Sjohnlev 
285084Sjohnlev #include <stdio.h>
295084Sjohnlev #include <sys/types.h>
305084Sjohnlev #include <sys/stat.h>
315084Sjohnlev #include <string.h>
325084Sjohnlev #include <fcntl.h>
335084Sjohnlev #include <unistd.h>
345084Sjohnlev #include <stropts.h>
355084Sjohnlev #include <stdlib.h>
365084Sjohnlev #include <errno.h>
375084Sjohnlev #include <strings.h>
385084Sjohnlev #include <libintl.h>
395084Sjohnlev #include <net/if_types.h>
405084Sjohnlev #include <net/if_dl.h>
415084Sjohnlev #include <libdladm_impl.h>
42*5895Syz147064 #include <libdllink.h>
435084Sjohnlev #include <libdlvnic.h>
445084Sjohnlev 
455084Sjohnlev /*
465084Sjohnlev  * VNIC administration library.
475084Sjohnlev  */
485084Sjohnlev 
495084Sjohnlev #define	VNIC_DEV	"/devices/pseudo/vnic@0:" VNIC_CTL_NODE_NAME
505084Sjohnlev 
515084Sjohnlev /* Limits on buffer size for VNIC_IOC_INFO request */
525084Sjohnlev #define	MIN_INFO_SIZE (4*1024)
535084Sjohnlev #define	MAX_INFO_SIZE (128*1024)
545084Sjohnlev 
555084Sjohnlev /* configuration database entry */
565084Sjohnlev typedef struct dladm_vnic_attr_db {
57*5895Syz147064 	datalink_id_t	vt_vnic_id;
58*5895Syz147064 	datalink_id_t	vt_link_id;
595084Sjohnlev 	vnic_mac_addr_type_t vt_mac_addr_type;
605084Sjohnlev 	uint_t		vt_mac_len;
615084Sjohnlev 	uchar_t		vt_mac_addr[MAXMACADDRLEN];
625084Sjohnlev } dladm_vnic_attr_db_t;
635084Sjohnlev 
645084Sjohnlev typedef struct dladm_vnic_modify_attr {
655084Sjohnlev 	vnic_mac_addr_type_t	vm_mac_addr_type;
665084Sjohnlev 	int			vm_mac_len;
675084Sjohnlev 	uchar_t			vm_mac_addr[MAXMACADDRLEN];
685084Sjohnlev } dladm_vnic_modify_attr_t;
695084Sjohnlev 
705084Sjohnlev /*
715084Sjohnlev  * Send a create command to the VNIC driver.
725084Sjohnlev  */
735084Sjohnlev static dladm_status_t
745084Sjohnlev i_dladm_vnic_create_sys(int fd, dladm_vnic_attr_db_t *attr)
755084Sjohnlev {
765084Sjohnlev 	int rc;
775084Sjohnlev 	vnic_ioc_create_t ioc;
785084Sjohnlev 
795084Sjohnlev 	ioc.vc_vnic_id = attr->vt_vnic_id;
80*5895Syz147064 	ioc.vc_link_id = attr->vt_link_id;
815084Sjohnlev 	ioc.vc_mac_addr_type = attr->vt_mac_addr_type;
825084Sjohnlev 	ioc.vc_mac_len = attr->vt_mac_len;
835084Sjohnlev 	bcopy(attr->vt_mac_addr, ioc.vc_mac_addr, attr->vt_mac_len);
845084Sjohnlev 
855084Sjohnlev 	rc = i_dladm_ioctl(fd, VNIC_IOC_CREATE, &ioc, sizeof (ioc));
865084Sjohnlev 
875084Sjohnlev 	if (rc < 0)
885084Sjohnlev 		return (dladm_errno2status(errno));
895084Sjohnlev 
905084Sjohnlev 	return (DLADM_STATUS_OK);
915084Sjohnlev }
925084Sjohnlev 
935084Sjohnlev /*
945084Sjohnlev  * Send a modify command to the VNIC driver.
955084Sjohnlev  */
965084Sjohnlev static dladm_status_t
97*5895Syz147064 i_dladm_vnic_modify_sys(datalink_id_t vnic_id, uint32_t modify_mask,
985084Sjohnlev     dladm_vnic_modify_attr_t *attr)
995084Sjohnlev {
1005084Sjohnlev 	int rc;
1015084Sjohnlev 	int fd;
1025084Sjohnlev 	vnic_ioc_modify_t ioc;
1035084Sjohnlev 
1045084Sjohnlev 	ioc.vm_vnic_id = vnic_id;
1055084Sjohnlev 
1065084Sjohnlev 	ioc.vm_modify_mask = 0;
1075084Sjohnlev 	if (modify_mask & DLADM_VNIC_MODIFY_ADDR)
1085084Sjohnlev 		ioc.vm_modify_mask |= VNIC_IOC_MODIFY_ADDR;
1095084Sjohnlev 
1105084Sjohnlev 	ioc.vm_mac_addr_type = attr->vm_mac_addr_type;
1115084Sjohnlev 	ioc.vm_mac_len = attr->vm_mac_len;
1125084Sjohnlev 	bcopy(attr->vm_mac_addr, ioc.vm_mac_addr, MAXMACADDRLEN);
1135084Sjohnlev 
1145084Sjohnlev 	if ((fd = open(VNIC_DEV, O_RDWR)) < 0)
1155084Sjohnlev 		return (dladm_errno2status(errno));
1165084Sjohnlev 
1175084Sjohnlev 	rc = i_dladm_ioctl(fd, VNIC_IOC_MODIFY, &ioc, sizeof (ioc));
1185084Sjohnlev 
1195084Sjohnlev 	(void) close(fd);
1205084Sjohnlev 
1215084Sjohnlev 	if (rc < 0)
1225084Sjohnlev 		return (dladm_errno2status(errno));
1235084Sjohnlev 
1245084Sjohnlev 	return (DLADM_STATUS_OK);
1255084Sjohnlev }
1265084Sjohnlev 
1275084Sjohnlev /*
128*5895Syz147064  * Get the configuration information of the given VNIC.
1295084Sjohnlev  */
1305084Sjohnlev dladm_status_t
131*5895Syz147064 dladm_vnic_info(datalink_id_t vnic_id, dladm_vnic_attr_sys_t *attrp,
132*5895Syz147064     uint32_t flags)
1335084Sjohnlev {
1345084Sjohnlev 	vnic_ioc_info_t *ioc;
1355084Sjohnlev 	vnic_ioc_info_vnic_t *vnic;
136*5895Syz147064 	int rc, bufsize, fd;
1375084Sjohnlev 	dladm_status_t status = DLADM_STATUS_OK;
1385084Sjohnlev 
139*5895Syz147064 	/* for now, only temporary creations are supported */
140*5895Syz147064 	if (flags & DLADM_OPT_PERSIST)
141*5895Syz147064 		return (dladm_errno2status(ENOTSUP));
142*5895Syz147064 
1435084Sjohnlev 	if ((fd = open(VNIC_DEV, O_RDWR)) == -1)
1445084Sjohnlev 		return (dladm_errno2status(errno));
1455084Sjohnlev 
146*5895Syz147064 	bufsize = sizeof (vnic_ioc_info_t) + sizeof (vnic_ioc_info_vnic_t);
1475084Sjohnlev 	ioc = (vnic_ioc_info_t *)calloc(1, bufsize);
1485084Sjohnlev 	if (ioc == NULL) {
1495084Sjohnlev 		(void) close(fd);
1505084Sjohnlev 		return (dladm_errno2status(ENOMEM));
1515084Sjohnlev 	}
1525084Sjohnlev 
153*5895Syz147064 	ioc->vi_vnic_id = vnic_id;
1545084Sjohnlev 	rc = i_dladm_ioctl(fd, VNIC_IOC_INFO, ioc, bufsize);
1555084Sjohnlev 	if (rc != 0) {
1565084Sjohnlev 		status = dladm_errno2status(errno);
1575084Sjohnlev 		goto bail;
1585084Sjohnlev 	}
1595084Sjohnlev 
160*5895Syz147064 	vnic = (vnic_ioc_info_vnic_t *)(ioc + 1);
1615084Sjohnlev 
162*5895Syz147064 	attrp->va_vnic_id = vnic->vn_vnic_id;
163*5895Syz147064 	attrp->va_link_id = vnic->vn_link_id;
164*5895Syz147064 	attrp->va_mac_addr_type = vnic->vn_mac_addr_type;
165*5895Syz147064 	bcopy(vnic->vn_mac_addr, attrp->va_mac_addr, ETHERADDRL);
166*5895Syz147064 	attrp->va_mac_len = vnic->vn_mac_len;
1675084Sjohnlev 
1685084Sjohnlev bail:
1695084Sjohnlev 	free(ioc);
1705084Sjohnlev 	(void) close(fd);
1715084Sjohnlev 	return (status);
1725084Sjohnlev }
1735084Sjohnlev 
1745084Sjohnlev /*
1755084Sjohnlev  * Remove a VNIC from the kernel.
1765084Sjohnlev  */
1775084Sjohnlev static dladm_status_t
1785084Sjohnlev i_dladm_vnic_delete_sys(int fd, dladm_vnic_attr_sys_t *attr)
1795084Sjohnlev {
1805084Sjohnlev 	vnic_ioc_delete_t ioc;
1815084Sjohnlev 	int rc;
1825084Sjohnlev 
1835084Sjohnlev 	ioc.vd_vnic_id = attr->va_vnic_id;
1845084Sjohnlev 
1855084Sjohnlev 	rc = i_dladm_ioctl(fd, VNIC_IOC_DELETE, &ioc, sizeof (ioc));
1865084Sjohnlev 
1875084Sjohnlev 	if (rc < 0)
1885084Sjohnlev 		return (dladm_errno2status(errno));
1895084Sjohnlev 
1905084Sjohnlev 	return (DLADM_STATUS_OK);
1915084Sjohnlev }
1925084Sjohnlev 
1935084Sjohnlev /*
1945084Sjohnlev  * Convert between MAC address types and their string representations.
1955084Sjohnlev  */
1965084Sjohnlev 
1975084Sjohnlev typedef struct dladm_vnic_addr_type_s {
1985084Sjohnlev 	char *va_str;
1995084Sjohnlev 	vnic_mac_addr_type_t va_type;
2005084Sjohnlev } dladm_vnic_addr_type_t;
2015084Sjohnlev 
2025084Sjohnlev static dladm_vnic_addr_type_t addr_types[] = {
2035084Sjohnlev 	{"fixed", VNIC_MAC_ADDR_TYPE_FIXED},
2045084Sjohnlev };
2055084Sjohnlev 
2065084Sjohnlev #define	NADDR_TYPES (sizeof (addr_types) / sizeof (dladm_vnic_addr_type_t))
2075084Sjohnlev 
208*5895Syz147064 /*
209*5895Syz147064  * Return DLADM_STATUS_OK if a matching type was found,
210*5895Syz147064  * DLADM_STATUS_BADARG otherwise
211*5895Syz147064  */
212*5895Syz147064 dladm_status_t
213*5895Syz147064 dladm_vnic_str2macaddrtype(const char *str, vnic_mac_addr_type_t *val)
2145084Sjohnlev {
2155084Sjohnlev 	int i;
2165084Sjohnlev 	dladm_vnic_addr_type_t *type;
2175084Sjohnlev 
2185084Sjohnlev 	for (i = 0; i < NADDR_TYPES; i++) {
2195084Sjohnlev 		type = &addr_types[i];
2205084Sjohnlev 		if (strncmp(str, type->va_str, strlen(type->va_str)) == 0) {
2215084Sjohnlev 			*val = type->va_type;
222*5895Syz147064 			return (DLADM_STATUS_OK);
2235084Sjohnlev 		}
2245084Sjohnlev 	}
2255084Sjohnlev 
226*5895Syz147064 	return (DLADM_STATUS_BADARG);
2275084Sjohnlev }
2285084Sjohnlev 
2295084Sjohnlev /*
2305084Sjohnlev  * Create a new VNIC. Update the configuration file and bring it up.
2315084Sjohnlev  */
2325084Sjohnlev dladm_status_t
233*5895Syz147064 dladm_vnic_create(const char *vnic, datalink_id_t linkid,
2345084Sjohnlev     vnic_mac_addr_type_t mac_addr_type, uchar_t *mac_addr, int mac_len,
235*5895Syz147064     datalink_id_t *vnic_id_out, uint32_t flags)
2365084Sjohnlev {
2375084Sjohnlev 	dladm_vnic_attr_db_t attr;
238*5895Syz147064 	int i, fd;
239*5895Syz147064 	datalink_id_t vnic_id;
240*5895Syz147064 	datalink_class_t class;
241*5895Syz147064 	uint32_t media;
242*5895Syz147064 	char *name = (char *)vnic;
2435084Sjohnlev 	dladm_status_t status;
2445084Sjohnlev 
2455084Sjohnlev 	/*
2465084Sjohnlev 	 * Sanity test arguments.
2475084Sjohnlev 	 */
248*5895Syz147064 	if (flags & DLADM_OPT_PERSIST)
249*5895Syz147064 		return (dladm_errno2status(ENOTSUP));
2505084Sjohnlev 
2515084Sjohnlev 	if (mac_len > MAXMACADDRLEN)
2525084Sjohnlev 		return (DLADM_STATUS_INVALIDMACADDRLEN);
2535084Sjohnlev 
2545084Sjohnlev 	for (i = 0; i < NADDR_TYPES; i++) {
2555084Sjohnlev 		if (mac_addr_type == addr_types[i].va_type)
2565084Sjohnlev 			break;
2575084Sjohnlev 	}
2585084Sjohnlev 	if (i == NADDR_TYPES)
2595084Sjohnlev 		return (DLADM_STATUS_INVALIDMACADDRTYPE);
2605084Sjohnlev 
261*5895Syz147064 	if ((status = dladm_datalink_id2info(linkid, NULL, &class, &media,
262*5895Syz147064 	    NULL, 0)) != DLADM_STATUS_OK) {
263*5895Syz147064 		return (status);
264*5895Syz147064 	}
265*5895Syz147064 
266*5895Syz147064 	if (class == DATALINK_CLASS_VNIC)
267*5895Syz147064 		return (DLADM_STATUS_BADARG);
2685084Sjohnlev 
269*5895Syz147064 	if (vnic == NULL) {
270*5895Syz147064 		flags |= DLADM_OPT_PREFIX;
271*5895Syz147064 		name = "vnic";
272*5895Syz147064 	}
273*5895Syz147064 
274*5895Syz147064 	if ((status = dladm_create_datalink_id(name, DATALINK_CLASS_VNIC,
275*5895Syz147064 	    media, flags, &vnic_id)) != DLADM_STATUS_OK) {
276*5895Syz147064 		return (status);
2775084Sjohnlev 	}
2785084Sjohnlev 
2795084Sjohnlev 	bzero(&attr, sizeof (attr));
2805084Sjohnlev 	attr.vt_vnic_id = vnic_id;
281*5895Syz147064 	attr.vt_link_id = linkid;
2825084Sjohnlev 	attr.vt_mac_addr_type = mac_addr_type;
2835084Sjohnlev 	attr.vt_mac_len = mac_len;
2845084Sjohnlev 	bcopy(mac_addr, attr.vt_mac_addr, mac_len);
2855084Sjohnlev 
286*5895Syz147064 	if ((fd = open(VNIC_DEV, O_RDWR)) < 0) {
287*5895Syz147064 		status = dladm_errno2status(errno);
288*5895Syz147064 		goto done;
289*5895Syz147064 	}
290*5895Syz147064 
291*5895Syz147064 	status = i_dladm_vnic_create_sys(fd, &attr);
292*5895Syz147064 	(void) close(fd);
2935084Sjohnlev 
294*5895Syz147064 done:
295*5895Syz147064 	if (status != DLADM_STATUS_OK) {
296*5895Syz147064 		(void) dladm_destroy_datalink_id(vnic_id,
297*5895Syz147064 		    flags & ~DLADM_OPT_PREFIX);
298*5895Syz147064 	} else {
299*5895Syz147064 		*vnic_id_out = vnic_id;
300*5895Syz147064 	}
3015084Sjohnlev 
3025084Sjohnlev 	return (status);
3035084Sjohnlev }
3045084Sjohnlev 
3055084Sjohnlev /*
3065084Sjohnlev  * Modify the properties of a VNIC.
3075084Sjohnlev  */
3085084Sjohnlev dladm_status_t
309*5895Syz147064 dladm_vnic_modify(datalink_id_t vnic_id, uint32_t modify_mask,
3105084Sjohnlev     vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr,
3115084Sjohnlev     uint32_t flags)
3125084Sjohnlev {
3135084Sjohnlev 	dladm_vnic_modify_attr_t new_attr;
3145084Sjohnlev 
3155084Sjohnlev 	/* for now, only temporary creations are supported */
316*5895Syz147064 	if (flags & DLADM_OPT_PERSIST)
3175084Sjohnlev 		return (dladm_errno2status(ENOTSUP));
3185084Sjohnlev 
3195084Sjohnlev 	bzero(&new_attr, sizeof (new_attr));
3205084Sjohnlev 
3215084Sjohnlev 	if (modify_mask & DLADM_VNIC_MODIFY_ADDR) {
3225084Sjohnlev 		new_attr.vm_mac_addr_type = mac_addr_type;
3235084Sjohnlev 		new_attr.vm_mac_len = mac_len;
3245084Sjohnlev 		bcopy(mac_addr, new_attr.vm_mac_addr, MAXMACADDRLEN);
3255084Sjohnlev 	}
3265084Sjohnlev 
3275084Sjohnlev 	/* update the properties of the existing VNIC */
3285084Sjohnlev 	return (i_dladm_vnic_modify_sys(vnic_id, modify_mask, &new_attr));
3295084Sjohnlev }
3305084Sjohnlev 
3315084Sjohnlev /*
3325084Sjohnlev  * Delete a VNIC.
3335084Sjohnlev  */
3345084Sjohnlev dladm_status_t
335*5895Syz147064 dladm_vnic_delete(datalink_id_t vnic_id, uint32_t flags)
3365084Sjohnlev {
337*5895Syz147064 	dladm_status_t status;
3385084Sjohnlev 	dladm_vnic_attr_sys_t sys_attr;
339*5895Syz147064 	int fd;
3405084Sjohnlev 
3415084Sjohnlev 	/* for now, only temporary deletes are supported */
342*5895Syz147064 	if (flags & DLADM_OPT_PERSIST)
3435084Sjohnlev 		return (dladm_errno2status(ENOTSUP));
3445084Sjohnlev 
345*5895Syz147064 	if ((fd = open(VNIC_DEV, O_RDWR)) < 0)
346*5895Syz147064 		return (dladm_errno2status(errno));
347*5895Syz147064 
3485084Sjohnlev 	sys_attr.va_vnic_id = vnic_id;
349*5895Syz147064 	status = i_dladm_vnic_delete_sys(fd, &sys_attr);
350*5895Syz147064 	(void) close(fd);
351*5895Syz147064 
352*5895Syz147064 	if (status != DLADM_STATUS_OK)
353*5895Syz147064 		return (status);
354*5895Syz147064 
355*5895Syz147064 	(void) dladm_destroy_datalink_id(vnic_id, flags);
356*5895Syz147064 	return (status);
3575084Sjohnlev }
358