1*5084Sjohnlev /*
2*5084Sjohnlev  * CDDL HEADER START
3*5084Sjohnlev  *
4*5084Sjohnlev  * The contents of this file are subject to the terms of the
5*5084Sjohnlev  * Common Development and Distribution License (the "License").
6*5084Sjohnlev  * You may not use this file except in compliance with the License.
7*5084Sjohnlev  *
8*5084Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5084Sjohnlev  * or http://www.opensolaris.org/os/licensing.
10*5084Sjohnlev  * See the License for the specific language governing permissions
11*5084Sjohnlev  * and limitations under the License.
12*5084Sjohnlev  *
13*5084Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
14*5084Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5084Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
16*5084Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
17*5084Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
18*5084Sjohnlev  *
19*5084Sjohnlev  * CDDL HEADER END
20*5084Sjohnlev  */
21*5084Sjohnlev /*
22*5084Sjohnlev  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*5084Sjohnlev  * Use is subject to license terms.
24*5084Sjohnlev  */
25*5084Sjohnlev 
26*5084Sjohnlev #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*5084Sjohnlev 
28*5084Sjohnlev #include <stdio.h>
29*5084Sjohnlev #include <sys/types.h>
30*5084Sjohnlev #include <sys/stat.h>
31*5084Sjohnlev #include <string.h>
32*5084Sjohnlev #include <fcntl.h>
33*5084Sjohnlev #include <unistd.h>
34*5084Sjohnlev #include <stropts.h>
35*5084Sjohnlev #include <stdlib.h>
36*5084Sjohnlev #include <errno.h>
37*5084Sjohnlev #include <strings.h>
38*5084Sjohnlev #include <libintl.h>
39*5084Sjohnlev #include <net/if_types.h>
40*5084Sjohnlev #include <net/if_dl.h>
41*5084Sjohnlev #include <libdladm_impl.h>
42*5084Sjohnlev #include <libdlvnic.h>
43*5084Sjohnlev 
44*5084Sjohnlev /*
45*5084Sjohnlev  * VNIC administration library.
46*5084Sjohnlev  */
47*5084Sjohnlev 
48*5084Sjohnlev #define	VNIC_DEV	"/devices/pseudo/vnic@0:" VNIC_CTL_NODE_NAME
49*5084Sjohnlev 
50*5084Sjohnlev /*
51*5084Sjohnlev  * Because by default the id is used as the DLPI device PPA and default
52*5084Sjohnlev  * VLAN PPA's are calculated as ((1000 * vid) + PPA), the largest id
53*5084Sjohnlev  * can't be > 999. We reserve the last 100 VNIC ids for automatic
54*5084Sjohnlev  * VNIC id assignment.
55*5084Sjohnlev  */
56*5084Sjohnlev #define	DLADM_VNIC_MIN_VNIC_ID		1	/* total range */
57*5084Sjohnlev #define	DLADM_VNIC_MAX_VNIC_ID		999
58*5084Sjohnlev #define	DLADM_VNIC_MIN_VNIC_SPEC_ID	1	/* specified by user */
59*5084Sjohnlev #define	DLADM_VNIC_MAX_VNIC_SPEC_ID	899
60*5084Sjohnlev #define	DLADM_VNIC_MIN_VNIC_AUTO_ID	900	/* picked automatically */
61*5084Sjohnlev #define	DLADM_VNIC_MAX_VNIC_AUTO_ID	999
62*5084Sjohnlev 
63*5084Sjohnlev #define	DLADM_VNIC_NUM_VNIC_AUTO_ID	(DLADM_VNIC_MAX_VNIC_AUTO_ID - \
64*5084Sjohnlev     DLADM_VNIC_MIN_VNIC_AUTO_ID + 1)
65*5084Sjohnlev 
66*5084Sjohnlev /* Limits on buffer size for VNIC_IOC_INFO request */
67*5084Sjohnlev #define	MIN_INFO_SIZE (4*1024)
68*5084Sjohnlev #define	MAX_INFO_SIZE (128*1024)
69*5084Sjohnlev 
70*5084Sjohnlev /* configuration database entry */
71*5084Sjohnlev typedef struct dladm_vnic_attr_db {
72*5084Sjohnlev 	uint_t		vt_vnic_id;
73*5084Sjohnlev 	char		vt_dev_name[MAXNAMELEN];
74*5084Sjohnlev 	vnic_mac_addr_type_t vt_mac_addr_type;
75*5084Sjohnlev 	uint_t		vt_mac_len;
76*5084Sjohnlev 	uchar_t		vt_mac_addr[MAXMACADDRLEN];
77*5084Sjohnlev } dladm_vnic_attr_db_t;
78*5084Sjohnlev 
79*5084Sjohnlev typedef struct dladm_vnic_up {
80*5084Sjohnlev 	uint_t		vu_vnic_id;
81*5084Sjohnlev 	boolean_t	vu_found;
82*5084Sjohnlev 	int		vu_fd;
83*5084Sjohnlev } dladm_vnic_up_t;
84*5084Sjohnlev 
85*5084Sjohnlev typedef struct dladm_vnic_down {
86*5084Sjohnlev 	uint32_t	vd_vnic_id;
87*5084Sjohnlev 	boolean_t	vd_found;
88*5084Sjohnlev } dladm_vnic_down_t;
89*5084Sjohnlev 
90*5084Sjohnlev typedef struct dladm_vnic_modify {
91*5084Sjohnlev 	uint32_t	vm_vnic_id;
92*5084Sjohnlev 	boolean_t	vm_found;
93*5084Sjohnlev } dladm_vnic_modify_t;
94*5084Sjohnlev 
95*5084Sjohnlev typedef struct dladm_vnic_modify_attr {
96*5084Sjohnlev 	vnic_mac_addr_type_t	vm_mac_addr_type;
97*5084Sjohnlev 	int			vm_mac_len;
98*5084Sjohnlev 	uchar_t			vm_mac_addr[MAXMACADDRLEN];
99*5084Sjohnlev } dladm_vnic_modify_attr_t;
100*5084Sjohnlev 
101*5084Sjohnlev /*
102*5084Sjohnlev  * Send a create command to the VNIC driver.
103*5084Sjohnlev  */
104*5084Sjohnlev static dladm_status_t
105*5084Sjohnlev i_dladm_vnic_create_sys(int fd, dladm_vnic_attr_db_t *attr)
106*5084Sjohnlev {
107*5084Sjohnlev 	int rc;
108*5084Sjohnlev 	vnic_ioc_create_t ioc;
109*5084Sjohnlev 
110*5084Sjohnlev 	ioc.vc_vnic_id = attr->vt_vnic_id;
111*5084Sjohnlev 	bcopy(attr->vt_dev_name, ioc.vc_dev_name, MAXNAMELEN);
112*5084Sjohnlev 	ioc.vc_mac_addr_type = attr->vt_mac_addr_type;
113*5084Sjohnlev 	ioc.vc_mac_len = attr->vt_mac_len;
114*5084Sjohnlev 	bcopy(attr->vt_mac_addr, ioc.vc_mac_addr, attr->vt_mac_len);
115*5084Sjohnlev 
116*5084Sjohnlev 	rc = i_dladm_ioctl(fd, VNIC_IOC_CREATE, &ioc, sizeof (ioc));
117*5084Sjohnlev 
118*5084Sjohnlev 	if (rc < 0)
119*5084Sjohnlev 		return (dladm_errno2status(errno));
120*5084Sjohnlev 
121*5084Sjohnlev 	return (DLADM_STATUS_OK);
122*5084Sjohnlev }
123*5084Sjohnlev 
124*5084Sjohnlev /*
125*5084Sjohnlev  * Invoked to bring up a VNIC.
126*5084Sjohnlev  */
127*5084Sjohnlev static dladm_status_t
128*5084Sjohnlev i_dladm_vnic_up(void *arg, dladm_vnic_attr_db_t *attr)
129*5084Sjohnlev {
130*5084Sjohnlev 	dladm_vnic_up_t *up = (dladm_vnic_up_t *)arg;
131*5084Sjohnlev 	dladm_status_t status;
132*5084Sjohnlev 
133*5084Sjohnlev 	if (up->vu_vnic_id != 0 && up->vu_vnic_id != attr->vt_vnic_id)
134*5084Sjohnlev 		return (DLADM_STATUS_OK);
135*5084Sjohnlev 
136*5084Sjohnlev 	up->vu_found = B_TRUE;
137*5084Sjohnlev 
138*5084Sjohnlev 	status = i_dladm_vnic_create_sys(up->vu_fd, attr);
139*5084Sjohnlev 	if ((status != DLADM_STATUS_OK) && (up->vu_vnic_id != 0))
140*5084Sjohnlev 		return (status);
141*5084Sjohnlev 
142*5084Sjohnlev 	return (DLADM_STATUS_OK);
143*5084Sjohnlev }
144*5084Sjohnlev 
145*5084Sjohnlev /*
146*5084Sjohnlev  * Send a modify command to the VNIC driver.
147*5084Sjohnlev  */
148*5084Sjohnlev static dladm_status_t
149*5084Sjohnlev i_dladm_vnic_modify_sys(uint_t vnic_id, uint32_t modify_mask,
150*5084Sjohnlev     dladm_vnic_modify_attr_t *attr)
151*5084Sjohnlev {
152*5084Sjohnlev 	int rc;
153*5084Sjohnlev 	int fd;
154*5084Sjohnlev 	vnic_ioc_modify_t ioc;
155*5084Sjohnlev 
156*5084Sjohnlev 	ioc.vm_vnic_id = vnic_id;
157*5084Sjohnlev 
158*5084Sjohnlev 	ioc.vm_modify_mask = 0;
159*5084Sjohnlev 	if (modify_mask & DLADM_VNIC_MODIFY_ADDR)
160*5084Sjohnlev 		ioc.vm_modify_mask |= VNIC_IOC_MODIFY_ADDR;
161*5084Sjohnlev 
162*5084Sjohnlev 	ioc.vm_mac_addr_type = attr->vm_mac_addr_type;
163*5084Sjohnlev 	ioc.vm_mac_len = attr->vm_mac_len;
164*5084Sjohnlev 	bcopy(attr->vm_mac_addr, ioc.vm_mac_addr, MAXMACADDRLEN);
165*5084Sjohnlev 
166*5084Sjohnlev 	if ((fd = open(VNIC_DEV, O_RDWR)) < 0)
167*5084Sjohnlev 		return (dladm_errno2status(errno));
168*5084Sjohnlev 
169*5084Sjohnlev 	rc = i_dladm_ioctl(fd, VNIC_IOC_MODIFY, &ioc, sizeof (ioc));
170*5084Sjohnlev 
171*5084Sjohnlev 	(void) close(fd);
172*5084Sjohnlev 
173*5084Sjohnlev 	if (rc < 0)
174*5084Sjohnlev 		return (dladm_errno2status(errno));
175*5084Sjohnlev 
176*5084Sjohnlev 	return (DLADM_STATUS_OK);
177*5084Sjohnlev }
178*5084Sjohnlev 
179*5084Sjohnlev /*
180*5084Sjohnlev  * Walk through the vnics defined on the system and for each vnic <vnic>,
181*5084Sjohnlev  * invoke <fn>(<arg>, <vnic>);
182*5084Sjohnlev  */
183*5084Sjohnlev dladm_status_t
184*5084Sjohnlev dladm_vnic_walk_sys(dladm_status_t (*fn)(void *, dladm_vnic_attr_sys_t *),
185*5084Sjohnlev     void *arg)
186*5084Sjohnlev {
187*5084Sjohnlev 	vnic_ioc_info_t *ioc;
188*5084Sjohnlev 	vnic_ioc_info_vnic_t *vnic;
189*5084Sjohnlev 	dladm_vnic_attr_sys_t attr;
190*5084Sjohnlev 	int rc, i, bufsize, fd;
191*5084Sjohnlev 	char *where;
192*5084Sjohnlev 	dladm_status_t status = DLADM_STATUS_OK;
193*5084Sjohnlev 
194*5084Sjohnlev 	if ((fd = open(VNIC_DEV, O_RDWR)) == -1)
195*5084Sjohnlev 		return (dladm_errno2status(errno));
196*5084Sjohnlev 
197*5084Sjohnlev 	bufsize = MIN_INFO_SIZE;
198*5084Sjohnlev 	ioc = (vnic_ioc_info_t *)calloc(1, bufsize);
199*5084Sjohnlev 	if (ioc == NULL) {
200*5084Sjohnlev 		(void) close(fd);
201*5084Sjohnlev 		return (dladm_errno2status(ENOMEM));
202*5084Sjohnlev 	}
203*5084Sjohnlev 
204*5084Sjohnlev tryagain:
205*5084Sjohnlev 
206*5084Sjohnlev 	rc = i_dladm_ioctl(fd, VNIC_IOC_INFO, ioc, bufsize);
207*5084Sjohnlev 
208*5084Sjohnlev 	if (rc != 0) {
209*5084Sjohnlev 		if (errno == ENOSPC) {
210*5084Sjohnlev 			bufsize *= 2;
211*5084Sjohnlev 			if (bufsize <= MAX_INFO_SIZE) {
212*5084Sjohnlev 				ioc = (vnic_ioc_info_t *)realloc(ioc, bufsize);
213*5084Sjohnlev 				if (ioc != NULL) {
214*5084Sjohnlev 					bzero(ioc, bufsize);
215*5084Sjohnlev 					goto tryagain;
216*5084Sjohnlev 				}
217*5084Sjohnlev 			}
218*5084Sjohnlev 		}
219*5084Sjohnlev 		status = dladm_errno2status(errno);
220*5084Sjohnlev 		goto bail;
221*5084Sjohnlev 	}
222*5084Sjohnlev 
223*5084Sjohnlev 	/*
224*5084Sjohnlev 	 * Go through each vnic returned by the vnic driver
225*5084Sjohnlev 	 */
226*5084Sjohnlev 	where = (char *)(ioc + 1);
227*5084Sjohnlev 
228*5084Sjohnlev 	for (i = 0; i < ioc->vi_nvnics; i++) {
229*5084Sjohnlev 		/* LINTED E_BAD_PTR_CAST_ALIGN */
230*5084Sjohnlev 		vnic = (vnic_ioc_info_vnic_t *)where;
231*5084Sjohnlev 
232*5084Sjohnlev 		attr.va_vnic_id = vnic->vn_vnic_id;
233*5084Sjohnlev 		bcopy(vnic->vn_dev_name, attr.va_dev_name,
234*5084Sjohnlev 		    MAXNAMELEN);
235*5084Sjohnlev 		attr.va_mac_addr_type = vnic->vn_mac_addr_type;
236*5084Sjohnlev 		bcopy(vnic->vn_mac_addr, attr.va_mac_addr, ETHERADDRL);
237*5084Sjohnlev 		attr.va_mac_len = vnic->vn_mac_len;
238*5084Sjohnlev 		where = (char *)(vnic + 1);
239*5084Sjohnlev 
240*5084Sjohnlev 		status = fn(arg, &attr);
241*5084Sjohnlev 		if (status != DLADM_STATUS_OK)
242*5084Sjohnlev 			goto bail;
243*5084Sjohnlev 	}
244*5084Sjohnlev 
245*5084Sjohnlev bail:
246*5084Sjohnlev 	free(ioc);
247*5084Sjohnlev 	(void) close(fd);
248*5084Sjohnlev 
249*5084Sjohnlev 	return (status);
250*5084Sjohnlev }
251*5084Sjohnlev 
252*5084Sjohnlev /*
253*5084Sjohnlev  * Remove a VNIC from the kernel.
254*5084Sjohnlev  */
255*5084Sjohnlev static dladm_status_t
256*5084Sjohnlev i_dladm_vnic_delete_sys(int fd, dladm_vnic_attr_sys_t *attr)
257*5084Sjohnlev {
258*5084Sjohnlev 	vnic_ioc_delete_t ioc;
259*5084Sjohnlev 	int rc;
260*5084Sjohnlev 
261*5084Sjohnlev 	ioc.vd_vnic_id = attr->va_vnic_id;
262*5084Sjohnlev 
263*5084Sjohnlev 	rc = i_dladm_ioctl(fd, VNIC_IOC_DELETE, &ioc, sizeof (ioc));
264*5084Sjohnlev 
265*5084Sjohnlev 	if (rc < 0)
266*5084Sjohnlev 		return (dladm_errno2status(errno));
267*5084Sjohnlev 
268*5084Sjohnlev 	return (DLADM_STATUS_OK);
269*5084Sjohnlev }
270*5084Sjohnlev 
271*5084Sjohnlev /*
272*5084Sjohnlev  * Invoked to bring down a VNIC.
273*5084Sjohnlev  */
274*5084Sjohnlev static dladm_status_t
275*5084Sjohnlev i_dladm_vnic_down(void *arg, dladm_vnic_attr_sys_t *attr)
276*5084Sjohnlev {
277*5084Sjohnlev 	dladm_vnic_down_t *down = (dladm_vnic_down_t *)arg;
278*5084Sjohnlev 	int fd;
279*5084Sjohnlev 	dladm_status_t status;
280*5084Sjohnlev 
281*5084Sjohnlev 	if (down->vd_vnic_id != 0 && down->vd_vnic_id != attr->va_vnic_id)
282*5084Sjohnlev 		return (DLADM_STATUS_OK);
283*5084Sjohnlev 
284*5084Sjohnlev 	down->vd_found = B_TRUE;
285*5084Sjohnlev 
286*5084Sjohnlev 	if ((fd = open(VNIC_DEV, O_RDWR)) < 0)
287*5084Sjohnlev 		return (dladm_errno2status(errno));
288*5084Sjohnlev 
289*5084Sjohnlev 	status = i_dladm_vnic_delete_sys(fd, attr);
290*5084Sjohnlev 	if ((status != DLADM_STATUS_OK) && (down->vd_vnic_id != 0)) {
291*5084Sjohnlev 		(void) close(fd);
292*5084Sjohnlev 		return (status);
293*5084Sjohnlev 	}
294*5084Sjohnlev 
295*5084Sjohnlev 	(void) close(fd);
296*5084Sjohnlev 	return (DLADM_STATUS_OK);
297*5084Sjohnlev }
298*5084Sjohnlev 
299*5084Sjohnlev /*
300*5084Sjohnlev  * Convert between MAC address types and their string representations.
301*5084Sjohnlev  */
302*5084Sjohnlev 
303*5084Sjohnlev typedef struct dladm_vnic_addr_type_s {
304*5084Sjohnlev 	char *va_str;
305*5084Sjohnlev 	vnic_mac_addr_type_t va_type;
306*5084Sjohnlev } dladm_vnic_addr_type_t;
307*5084Sjohnlev 
308*5084Sjohnlev static dladm_vnic_addr_type_t addr_types[] = {
309*5084Sjohnlev 	{"fixed", VNIC_MAC_ADDR_TYPE_FIXED},
310*5084Sjohnlev };
311*5084Sjohnlev 
312*5084Sjohnlev #define	NADDR_TYPES (sizeof (addr_types) / sizeof (dladm_vnic_addr_type_t))
313*5084Sjohnlev 
314*5084Sjohnlev /* returns B_TRUE if a matching type was found, B_FALSE otherwise */
315*5084Sjohnlev boolean_t
316*5084Sjohnlev dladm_vnic_mac_addr_str_to_type(const char *str, vnic_mac_addr_type_t *val)
317*5084Sjohnlev {
318*5084Sjohnlev 	int i;
319*5084Sjohnlev 	dladm_vnic_addr_type_t *type;
320*5084Sjohnlev 
321*5084Sjohnlev 	for (i = 0; i < NADDR_TYPES; i++) {
322*5084Sjohnlev 		type = &addr_types[i];
323*5084Sjohnlev 		if (strncmp(str, type->va_str, strlen(type->va_str)) == 0) {
324*5084Sjohnlev 			*val = type->va_type;
325*5084Sjohnlev 			return (B_TRUE);
326*5084Sjohnlev 		}
327*5084Sjohnlev 	}
328*5084Sjohnlev 
329*5084Sjohnlev 	return (B_FALSE);
330*5084Sjohnlev }
331*5084Sjohnlev 
332*5084Sjohnlev /*
333*5084Sjohnlev  * Select a VNIC id automatically.
334*5084Sjohnlev  */
335*5084Sjohnlev 
336*5084Sjohnlev typedef struct dladm_vnic_auto_state_s {
337*5084Sjohnlev 	uint_t		as_nslots;
338*5084Sjohnlev 	uint_t		*as_slots;
339*5084Sjohnlev } dladm_vnic_auto_state_t;
340*5084Sjohnlev 
341*5084Sjohnlev static dladm_status_t
342*5084Sjohnlev i_dladm_vnic_create_auto_walker(void *arg, dladm_vnic_attr_sys_t *attr)
343*5084Sjohnlev {
344*5084Sjohnlev 	dladm_vnic_auto_state_t *state = arg;
345*5084Sjohnlev 
346*5084Sjohnlev 	if (attr->va_vnic_id < DLADM_VNIC_MIN_VNIC_AUTO_ID ||
347*5084Sjohnlev 	    attr->va_vnic_id > DLADM_VNIC_MAX_VNIC_AUTO_ID)
348*5084Sjohnlev 		return (DLADM_STATUS_OK);
349*5084Sjohnlev 
350*5084Sjohnlev 	state->as_slots[state->as_nslots++] = attr->va_vnic_id;
351*5084Sjohnlev 
352*5084Sjohnlev 	return (DLADM_STATUS_OK);
353*5084Sjohnlev }
354*5084Sjohnlev 
355*5084Sjohnlev static int
356*5084Sjohnlev i_dladm_vnic_compare(const void *p1, const void *p2)
357*5084Sjohnlev {
358*5084Sjohnlev 	uint_t i = *((uint_t *)p1);
359*5084Sjohnlev 	uint_t j = *((uint_t *)p2);
360*5084Sjohnlev 
361*5084Sjohnlev 	if (i > j)
362*5084Sjohnlev 		return (1);
363*5084Sjohnlev 	if (i < j)
364*5084Sjohnlev 		return (-1);
365*5084Sjohnlev 	return (0);
366*5084Sjohnlev }
367*5084Sjohnlev 
368*5084Sjohnlev /*ARGSUSED*/
369*5084Sjohnlev static dladm_status_t
370*5084Sjohnlev i_dladm_vnic_get_auto_id(dladm_vnic_attr_db_t *attr, uint32_t *vnic_id_out)
371*5084Sjohnlev {
372*5084Sjohnlev 	dladm_vnic_auto_state_t state;
373*5084Sjohnlev 	uint_t vnic_ids[DLADM_VNIC_NUM_VNIC_AUTO_ID];
374*5084Sjohnlev 	int i;
375*5084Sjohnlev 	uint_t last_id, vnic_id;
376*5084Sjohnlev 	dladm_status_t status;
377*5084Sjohnlev 
378*5084Sjohnlev 	/*
379*5084Sjohnlev 	 * Build a sorted array containing the existing VNIC ids in the range
380*5084Sjohnlev 	 * allocated for automatic allocation.
381*5084Sjohnlev 	 */
382*5084Sjohnlev 	state.as_nslots = 0;
383*5084Sjohnlev 	state.as_slots = vnic_ids;
384*5084Sjohnlev 
385*5084Sjohnlev 	status = dladm_vnic_walk_sys(i_dladm_vnic_create_auto_walker, &state);
386*5084Sjohnlev 	if (status != DLADM_STATUS_OK)
387*5084Sjohnlev 		return (status);
388*5084Sjohnlev 
389*5084Sjohnlev 	qsort(vnic_ids, state.as_nslots, sizeof (uint_t),
390*5084Sjohnlev 	    i_dladm_vnic_compare);
391*5084Sjohnlev 
392*5084Sjohnlev 	/*
393*5084Sjohnlev 	 * Find a gap in the sequence of existing VNIC ids.
394*5084Sjohnlev 	 */
395*5084Sjohnlev 	last_id = DLADM_VNIC_MIN_VNIC_AUTO_ID - 1;
396*5084Sjohnlev 	vnic_id = 0;
397*5084Sjohnlev 	for (i = 0; i < state.as_nslots; i++) {
398*5084Sjohnlev 		if (vnic_ids[i] > (last_id + 1)) {
399*5084Sjohnlev 			vnic_id = last_id + 1;
400*5084Sjohnlev 			break;
401*5084Sjohnlev 		}
402*5084Sjohnlev 		last_id = vnic_ids[i];
403*5084Sjohnlev 	}
404*5084Sjohnlev 
405*5084Sjohnlev 	if (vnic_id == 0) {
406*5084Sjohnlev 		/*
407*5084Sjohnlev 		 * Did not find a gap between existing entries, see if we
408*5084Sjohnlev 		 * can add one.
409*5084Sjohnlev 		 */
410*5084Sjohnlev 		if (last_id + 1 > DLADM_VNIC_MAX_VNIC_AUTO_ID)
411*5084Sjohnlev 			return (DLADM_STATUS_AUTOIDNOAVAILABLEID);
412*5084Sjohnlev 
413*5084Sjohnlev 		/* still have room for one more VNIC */
414*5084Sjohnlev 		vnic_id = last_id + 1;
415*5084Sjohnlev 	}
416*5084Sjohnlev 
417*5084Sjohnlev 	*vnic_id_out = vnic_id;
418*5084Sjohnlev 
419*5084Sjohnlev 	return (DLADM_STATUS_OK);
420*5084Sjohnlev }
421*5084Sjohnlev 
422*5084Sjohnlev /*
423*5084Sjohnlev  * Create a new VNIC. Update the configuration file and bring it up.
424*5084Sjohnlev  */
425*5084Sjohnlev dladm_status_t
426*5084Sjohnlev dladm_vnic_create(uint_t vnic_id, char *dev_name,
427*5084Sjohnlev     vnic_mac_addr_type_t mac_addr_type, uchar_t *mac_addr, int mac_len,
428*5084Sjohnlev     uint_t *vnic_id_out, uint32_t flags)
429*5084Sjohnlev {
430*5084Sjohnlev 	dladm_vnic_attr_db_t attr;
431*5084Sjohnlev 	int i;
432*5084Sjohnlev 	boolean_t tempop = ((flags & DLADM_VNIC_OPT_TEMP) != 0);
433*5084Sjohnlev 	boolean_t autoid = ((flags & DLADM_VNIC_OPT_AUTOID) != 0);
434*5084Sjohnlev 	dladm_vnic_up_t up;
435*5084Sjohnlev 	dladm_status_t status;
436*5084Sjohnlev 
437*5084Sjohnlev 	/*
438*5084Sjohnlev 	 * Sanity test arguments.
439*5084Sjohnlev 	 */
440*5084Sjohnlev 	if (autoid && !tempop)
441*5084Sjohnlev 		return (DLADM_STATUS_AUTOIDNOTEMP);
442*5084Sjohnlev 
443*5084Sjohnlev 	if (!autoid && ((vnic_id < DLADM_VNIC_MIN_VNIC_SPEC_ID) ||
444*5084Sjohnlev 	    (vnic_id > DLADM_VNIC_MAX_VNIC_SPEC_ID)))
445*5084Sjohnlev 		return (DLADM_STATUS_INVALIDID);
446*5084Sjohnlev 
447*5084Sjohnlev 	if (mac_len > MAXMACADDRLEN)
448*5084Sjohnlev 		return (DLADM_STATUS_INVALIDMACADDRLEN);
449*5084Sjohnlev 
450*5084Sjohnlev 	for (i = 0; i < NADDR_TYPES; i++) {
451*5084Sjohnlev 		if (mac_addr_type == addr_types[i].va_type)
452*5084Sjohnlev 			break;
453*5084Sjohnlev 	}
454*5084Sjohnlev 	if (i == NADDR_TYPES)
455*5084Sjohnlev 		return (DLADM_STATUS_INVALIDMACADDRTYPE);
456*5084Sjohnlev 
457*5084Sjohnlev 	/* for now, only temporary creations are supported */
458*5084Sjohnlev 	if (!tempop)
459*5084Sjohnlev 		return (dladm_errno2status(ENOTSUP));
460*5084Sjohnlev 
461*5084Sjohnlev auto_again:
462*5084Sjohnlev 	if (autoid) {
463*5084Sjohnlev 		/*
464*5084Sjohnlev 		 * Find an unused VNIC id.
465*5084Sjohnlev 		 */
466*5084Sjohnlev 		status = i_dladm_vnic_get_auto_id(&attr, vnic_id_out);
467*5084Sjohnlev 		if (status != DLADM_STATUS_OK)
468*5084Sjohnlev 			return (status);
469*5084Sjohnlev 		vnic_id = *vnic_id_out;
470*5084Sjohnlev 	}
471*5084Sjohnlev 
472*5084Sjohnlev 	bzero(&attr, sizeof (attr));
473*5084Sjohnlev 	attr.vt_vnic_id = vnic_id;
474*5084Sjohnlev 	(void) strncpy(attr.vt_dev_name, dev_name,
475*5084Sjohnlev 	    sizeof (attr.vt_dev_name) - 1);
476*5084Sjohnlev 	attr.vt_mac_addr_type = mac_addr_type;
477*5084Sjohnlev 	attr.vt_mac_len = mac_len;
478*5084Sjohnlev 	bcopy(mac_addr, attr.vt_mac_addr, mac_len);
479*5084Sjohnlev 
480*5084Sjohnlev 	up.vu_vnic_id = vnic_id;
481*5084Sjohnlev 	up.vu_found = B_FALSE;
482*5084Sjohnlev 	up.vu_fd = open(VNIC_DEV, O_RDWR);
483*5084Sjohnlev 	if (up.vu_fd < 0)
484*5084Sjohnlev 		return (dladm_errno2status(errno));
485*5084Sjohnlev 
486*5084Sjohnlev 	status = i_dladm_vnic_up((void *)&up, &attr);
487*5084Sjohnlev 	(void) close(up.vu_fd);
488*5084Sjohnlev 
489*5084Sjohnlev 	if ((status == DLADM_STATUS_EXIST) && autoid)
490*5084Sjohnlev 		goto auto_again;
491*5084Sjohnlev 
492*5084Sjohnlev 	return (status);
493*5084Sjohnlev }
494*5084Sjohnlev 
495*5084Sjohnlev /*
496*5084Sjohnlev  * Modify the properties of a VNIC.
497*5084Sjohnlev  */
498*5084Sjohnlev dladm_status_t
499*5084Sjohnlev dladm_vnic_modify(uint_t vnic_id, uint32_t modify_mask,
500*5084Sjohnlev     vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr,
501*5084Sjohnlev     uint32_t flags)
502*5084Sjohnlev {
503*5084Sjohnlev 	dladm_vnic_modify_attr_t new_attr;
504*5084Sjohnlev 	boolean_t tempop = ((flags & DLADM_VNIC_OPT_TEMP) != 0);
505*5084Sjohnlev 
506*5084Sjohnlev 	if ((vnic_id < DLADM_VNIC_MIN_VNIC_ID) ||
507*5084Sjohnlev 	    (vnic_id > DLADM_VNIC_MAX_VNIC_ID))
508*5084Sjohnlev 		return (DLADM_STATUS_INVALIDID);
509*5084Sjohnlev 
510*5084Sjohnlev 	/* for now, only temporary creations are supported */
511*5084Sjohnlev 	if (!tempop)
512*5084Sjohnlev 		return (dladm_errno2status(ENOTSUP));
513*5084Sjohnlev 
514*5084Sjohnlev 	bzero(&new_attr, sizeof (new_attr));
515*5084Sjohnlev 
516*5084Sjohnlev 	if (modify_mask & DLADM_VNIC_MODIFY_ADDR) {
517*5084Sjohnlev 		new_attr.vm_mac_addr_type = mac_addr_type;
518*5084Sjohnlev 		new_attr.vm_mac_len = mac_len;
519*5084Sjohnlev 		bcopy(mac_addr, new_attr.vm_mac_addr, MAXMACADDRLEN);
520*5084Sjohnlev 	}
521*5084Sjohnlev 
522*5084Sjohnlev 	/* update the properties of the existing VNIC */
523*5084Sjohnlev 	return (i_dladm_vnic_modify_sys(vnic_id, modify_mask, &new_attr));
524*5084Sjohnlev }
525*5084Sjohnlev 
526*5084Sjohnlev 
527*5084Sjohnlev /*
528*5084Sjohnlev  * Delete a VNIC.
529*5084Sjohnlev  */
530*5084Sjohnlev dladm_status_t
531*5084Sjohnlev dladm_vnic_delete(uint_t vnic_id, uint32_t flags)
532*5084Sjohnlev {
533*5084Sjohnlev 	boolean_t tempop = ((flags & DLADM_VNIC_OPT_TEMP) != 0);
534*5084Sjohnlev 	dladm_vnic_down_t down;
535*5084Sjohnlev 	dladm_vnic_attr_sys_t sys_attr;
536*5084Sjohnlev 
537*5084Sjohnlev 	if ((vnic_id < DLADM_VNIC_MIN_VNIC_ID) ||
538*5084Sjohnlev 	    (vnic_id > DLADM_VNIC_MAX_VNIC_ID))
539*5084Sjohnlev 		return (DLADM_STATUS_INVALIDID);
540*5084Sjohnlev 
541*5084Sjohnlev 	/* for now, only temporary deletes are supported */
542*5084Sjohnlev 	if (!tempop)
543*5084Sjohnlev 		return (dladm_errno2status(ENOTSUP));
544*5084Sjohnlev 
545*5084Sjohnlev 	down.vd_vnic_id = vnic_id;
546*5084Sjohnlev 	down.vd_found = B_FALSE;
547*5084Sjohnlev 	sys_attr.va_vnic_id = vnic_id;
548*5084Sjohnlev 	return (i_dladm_vnic_down((void *)&down, &sys_attr));
549*5084Sjohnlev }
550