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>
39*8275SEric Cheng #include <sys/dld.h>
405084Sjohnlev #include <libdladm_impl.h>
415895Syz147064 #include <libdllink.h>
425084Sjohnlev #include <libdlvnic.h>
435084Sjohnlev 
445084Sjohnlev /*
455084Sjohnlev  * VNIC administration library.
465084Sjohnlev  */
475084Sjohnlev 
48*8275SEric Cheng /*
49*8275SEric Cheng  * Default random MAC address prefix (locally administered).
50*8275SEric Cheng  */
51*8275SEric Cheng static char dladm_vnic_def_prefix[] = {0x02, 0x08, 0x20};
52*8275SEric Cheng 
53*8275SEric Cheng static dladm_status_t	dladm_vnic_persist_conf(const char *name,
54*8275SEric Cheng 			    dladm_vnic_attr_t *, datalink_class_t);
55*8275SEric Cheng static const char	*dladm_vnic_macaddr2str(const uchar_t *, char *);
56*8275SEric Cheng static dladm_status_t	dladm_vnic_str2macaddr(const char *, uchar_t *);
575084Sjohnlev 
58*8275SEric Cheng /*
59*8275SEric Cheng  * Convert a diagnostic returned by the kernel into a dladm_status_t.
60*8275SEric Cheng  */
61*8275SEric Cheng static dladm_status_t
62*8275SEric Cheng dladm_vnic_diag2status(vnic_ioc_diag_t ioc_diag)
63*8275SEric Cheng {
64*8275SEric Cheng 	switch (ioc_diag) {
65*8275SEric Cheng 	case VNIC_IOC_DIAG_MACADDR_INVALID:
66*8275SEric Cheng 		return (DLADM_STATUS_INVALIDMACADDR);
67*8275SEric Cheng 	case VNIC_IOC_DIAG_MACADDRLEN_INVALID:
68*8275SEric Cheng 		return (DLADM_STATUS_INVALIDMACADDRLEN);
69*8275SEric Cheng 	case VNIC_IOC_DIAG_MACADDR_NIC:
70*8275SEric Cheng 		return (DLADM_STATUS_INVALIDMACADDRNIC);
71*8275SEric Cheng 	case VNIC_IOC_DIAG_MACADDR_INUSE:
72*8275SEric Cheng 		return (DLADM_STATUS_INVALIDMACADDRINUSE);
73*8275SEric Cheng 	case VNIC_IOC_DIAG_MACFACTORYSLOTINVALID:
74*8275SEric Cheng 		return (DLADM_STATUS_MACFACTORYSLOTINVALID);
75*8275SEric Cheng 	case VNIC_IOC_DIAG_MACFACTORYSLOTUSED:
76*8275SEric Cheng 		return (DLADM_STATUS_MACFACTORYSLOTUSED);
77*8275SEric Cheng 	case VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED:
78*8275SEric Cheng 		return (DLADM_STATUS_MACFACTORYSLOTALLUSED);
79*8275SEric Cheng 	case VNIC_IOC_DIAG_MACFACTORYNOTSUP:
80*8275SEric Cheng 		return (DLADM_STATUS_MACFACTORYNOTSUP);
81*8275SEric Cheng 	case VNIC_IOC_DIAG_MACPREFIX_INVALID:
82*8275SEric Cheng 		return (DLADM_STATUS_INVALIDMACPREFIX);
83*8275SEric Cheng 	case VNIC_IOC_DIAG_MACPREFIXLEN_INVALID:
84*8275SEric Cheng 		return (DLADM_STATUS_INVALIDMACPREFIXLEN);
85*8275SEric Cheng 	case VNIC_IOC_DIAG_MACMARGIN_INVALID:
86*8275SEric Cheng 		return (DLADM_STATUS_INVALID_MACMARGIN);
87*8275SEric Cheng 	case VNIC_IOC_DIAG_NO_HWRINGS:
88*8275SEric Cheng 		return (DLADM_STATUS_NO_HWRINGS);
89*8275SEric Cheng 	}
90*8275SEric Cheng 	return (DLADM_STATUS_OK);
91*8275SEric Cheng }
925084Sjohnlev 
935084Sjohnlev /*
945084Sjohnlev  * Send a create command to the VNIC driver.
955084Sjohnlev  */
96*8275SEric Cheng dladm_status_t
97*8275SEric Cheng i_dladm_vnic_create_sys(dladm_vnic_attr_t *attr)
985084Sjohnlev {
99*8275SEric Cheng 	int rc, fd;
1005084Sjohnlev 	vnic_ioc_create_t ioc;
101*8275SEric Cheng 	dladm_status_t status = DLADM_STATUS_OK;
1025084Sjohnlev 
103*8275SEric Cheng 	bzero(&ioc, sizeof (ioc));
104*8275SEric Cheng 	ioc.vc_vnic_id = attr->va_vnic_id;
105*8275SEric Cheng 	ioc.vc_link_id = attr->va_link_id;
106*8275SEric Cheng 	ioc.vc_mac_addr_type = attr->va_mac_addr_type;
107*8275SEric Cheng 	ioc.vc_mac_len = attr->va_mac_len;
108*8275SEric Cheng 	ioc.vc_mac_slot = attr->va_mac_slot;
109*8275SEric Cheng 	ioc.vc_mac_prefix_len = attr->va_mac_prefix_len;
110*8275SEric Cheng 	ioc.vc_vid = attr->va_vid;
111*8275SEric Cheng 	ioc.vc_flags = attr->va_force ? VNIC_IOC_CREATE_FORCE : 0;
112*8275SEric Cheng 	ioc.vc_flags |= attr->va_hwrings ? VNIC_IOC_CREATE_REQ_HWRINGS : 0;
1135084Sjohnlev 
114*8275SEric Cheng 	if (attr->va_mac_len > 0 || ioc.vc_mac_prefix_len > 0)
115*8275SEric Cheng 		bcopy(attr->va_mac_addr, ioc.vc_mac_addr, MAXMACADDRLEN);
116*8275SEric Cheng 	bcopy(&attr->va_resource_props, &ioc.vc_resource_props,
117*8275SEric Cheng 	    sizeof (mac_resource_props_t));
118*8275SEric Cheng 	if (attr->va_link_id == DATALINK_INVALID_LINKID)
119*8275SEric Cheng 		ioc.vc_flags |= VNIC_IOC_CREATE_ANCHOR;
1205084Sjohnlev 
1217408SSebastien.Roy@Sun.COM 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
1225084Sjohnlev 		return (dladm_errno2status(errno));
1235084Sjohnlev 
124*8275SEric Cheng 	rc = ioctl(fd, VNIC_IOC_CREATE, &ioc);
125*8275SEric Cheng 	if (rc < 0)
126*8275SEric Cheng 		status = dladm_errno2status(errno);
127*8275SEric Cheng 
128*8275SEric Cheng 	(void) close(fd);
129*8275SEric Cheng 	if (status != DLADM_STATUS_OK) {
130*8275SEric Cheng 		if (ioc.vc_diag != VNIC_IOC_DIAG_NONE)
131*8275SEric Cheng 			status = dladm_vnic_diag2status(ioc.vc_diag);
132*8275SEric Cheng 	}
133*8275SEric Cheng 	if (status != DLADM_STATUS_OK)
134*8275SEric Cheng 		return (status);
135*8275SEric Cheng 
136*8275SEric Cheng 	attr->va_mac_addr_type = ioc.vc_mac_addr_type;
137*8275SEric Cheng 	switch (ioc.vc_mac_addr_type) {
138*8275SEric Cheng 	case VNIC_MAC_ADDR_TYPE_FACTORY:
139*8275SEric Cheng 		attr->va_mac_slot = ioc.vc_mac_slot;
140*8275SEric Cheng 		break;
141*8275SEric Cheng 	case VNIC_MAC_ADDR_TYPE_RANDOM:
142*8275SEric Cheng 		bcopy(ioc.vc_mac_addr, attr->va_mac_addr, MAXMACADDRLEN);
143*8275SEric Cheng 		attr->va_mac_len = ioc.vc_mac_len;
144*8275SEric Cheng 		break;
145*8275SEric Cheng 	}
146*8275SEric Cheng 	return (status);
147*8275SEric Cheng }
148*8275SEric Cheng 
149*8275SEric Cheng /*
150*8275SEric Cheng  * Get the configuration information of the given VNIC.
151*8275SEric Cheng  */
152*8275SEric Cheng static dladm_status_t
153*8275SEric Cheng i_dladm_vnic_info_active(datalink_id_t linkid, dladm_vnic_attr_t *attrp)
154*8275SEric Cheng {
155*8275SEric Cheng 	vnic_ioc_info_t ioc;
156*8275SEric Cheng 	vnic_info_t *vnic;
157*8275SEric Cheng 	int rc, fd;
158*8275SEric Cheng 	dladm_status_t status = DLADM_STATUS_OK;
159*8275SEric Cheng 
160*8275SEric Cheng 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) == -1)
161*8275SEric Cheng 		return (dladm_errno2status(errno));
162*8275SEric Cheng 
163*8275SEric Cheng 	bzero(&ioc, sizeof (ioc));
164*8275SEric Cheng 	vnic = &ioc.vi_info;
165*8275SEric Cheng 	vnic->vn_vnic_id = linkid;
166*8275SEric Cheng 
167*8275SEric Cheng 	rc = ioctl(fd, VNIC_IOC_INFO, &ioc);
168*8275SEric Cheng 	if (rc != 0) {
169*8275SEric Cheng 		status = dladm_errno2status(errno);
170*8275SEric Cheng 		goto bail;
171*8275SEric Cheng 	}
172*8275SEric Cheng 
173*8275SEric Cheng 	attrp->va_vnic_id = vnic->vn_vnic_id;
174*8275SEric Cheng 	attrp->va_link_id = vnic->vn_link_id;
175*8275SEric Cheng 	attrp->va_mac_addr_type = vnic->vn_mac_addr_type;
176*8275SEric Cheng 	bcopy(vnic->vn_mac_addr, attrp->va_mac_addr, MAXMACADDRLEN);
177*8275SEric Cheng 	attrp->va_mac_len = vnic->vn_mac_len;
178*8275SEric Cheng 	attrp->va_mac_slot = vnic->vn_mac_slot;
179*8275SEric Cheng 	attrp->va_mac_prefix_len = vnic->vn_mac_prefix_len;
180*8275SEric Cheng 	attrp->va_vid = vnic->vn_vid;
181*8275SEric Cheng 	attrp->va_force = vnic->vn_force;
182*8275SEric Cheng 
183*8275SEric Cheng bail:
184*8275SEric Cheng 	(void) close(fd);
185*8275SEric Cheng 	return (status);
186*8275SEric Cheng }
187*8275SEric Cheng 
188*8275SEric Cheng static dladm_status_t
189*8275SEric Cheng i_dladm_vnic_info_persist(datalink_id_t linkid, dladm_vnic_attr_t *attrp)
190*8275SEric Cheng {
191*8275SEric Cheng 	dladm_conf_t conf;
192*8275SEric Cheng 	dladm_status_t status;
193*8275SEric Cheng 	char macstr[ETHERADDRL * 3];
194*8275SEric Cheng 	uint64_t u64;
195*8275SEric Cheng 	datalink_class_t class;
196*8275SEric Cheng 
197*8275SEric Cheng 	attrp->va_vnic_id = linkid;
198*8275SEric Cheng 	if ((status = dladm_read_conf(linkid, &conf)) != DLADM_STATUS_OK)
199*8275SEric Cheng 		return (status);
200*8275SEric Cheng 
201*8275SEric Cheng 	status = dladm_get_conf_field(conf, FLINKOVER, &u64, sizeof (u64));
202*8275SEric Cheng 	attrp->va_link_id = ((status == DLADM_STATUS_OK) ?
203*8275SEric Cheng 	    (datalink_id_t)u64 : DATALINK_INVALID_LINKID);
204*8275SEric Cheng 
205*8275SEric Cheng 	status = dladm_get_conf_field(conf, FHWRINGS, &attrp->va_hwrings,
206*8275SEric Cheng 	    sizeof (boolean_t));
207*8275SEric Cheng 
208*8275SEric Cheng 	if (status != DLADM_STATUS_OK && status != DLADM_STATUS_NOTFOUND)
209*8275SEric Cheng 		goto done;
210*8275SEric Cheng 	if (status == DLADM_STATUS_NOTFOUND)
211*8275SEric Cheng 		attrp->va_hwrings = B_FALSE;
212*8275SEric Cheng 
213*8275SEric Cheng 	if ((status = dladm_datalink_id2info(linkid, NULL, &class,
214*8275SEric Cheng 	    NULL, NULL, 0)) != DLADM_STATUS_OK)
215*8275SEric Cheng 		goto done;
216*8275SEric Cheng 
217*8275SEric Cheng 	if (class == DATALINK_CLASS_VLAN) {
218*8275SEric Cheng 		if (attrp->va_link_id == DATALINK_INVALID_LINKID) {
219*8275SEric Cheng 			status = DLADM_STATUS_BADARG;
220*8275SEric Cheng 			goto done;
221*8275SEric Cheng 		}
222*8275SEric Cheng 		attrp->va_mac_addr_type = VNIC_MAC_ADDR_TYPE_PRIMARY;
223*8275SEric Cheng 		attrp->va_mac_len = 0;
224*8275SEric Cheng 	} else {
225*8275SEric Cheng 		status = dladm_get_conf_field(conf, FMADDRTYPE, &u64,
226*8275SEric Cheng 		    sizeof (u64));
227*8275SEric Cheng 		if (status != DLADM_STATUS_OK)
228*8275SEric Cheng 			goto done;
229*8275SEric Cheng 
230*8275SEric Cheng 		attrp->va_mac_addr_type = (vnic_mac_addr_type_t)u64;
231*8275SEric Cheng 
232*8275SEric Cheng 		status = dladm_get_conf_field(conf, FMADDRLEN, &u64,
233*8275SEric Cheng 		    sizeof (u64));
234*8275SEric Cheng 		attrp->va_mac_len = ((status == DLADM_STATUS_OK) ?
235*8275SEric Cheng 		    (uint_t)u64 : ETHERADDRL);
236*8275SEric Cheng 
237*8275SEric Cheng 		status = dladm_get_conf_field(conf, FMADDRSLOT, &u64,
238*8275SEric Cheng 		    sizeof (u64));
239*8275SEric Cheng 		attrp->va_mac_slot = ((status == DLADM_STATUS_OK) ?
240*8275SEric Cheng 		    (int)u64 : -1);
241*8275SEric Cheng 
242*8275SEric Cheng 		status = dladm_get_conf_field(conf, FMADDRPREFIXLEN, &u64,
243*8275SEric Cheng 		    sizeof (u64));
244*8275SEric Cheng 		attrp->va_mac_prefix_len = ((status == DLADM_STATUS_OK) ?
245*8275SEric Cheng 		    (uint_t)u64 : sizeof (dladm_vnic_def_prefix));
246*8275SEric Cheng 
247*8275SEric Cheng 		status = dladm_get_conf_field(conf, FMACADDR, macstr,
248*8275SEric Cheng 		    sizeof (macstr));
249*8275SEric Cheng 		if (status != DLADM_STATUS_OK)
250*8275SEric Cheng 			goto done;
251*8275SEric Cheng 
252*8275SEric Cheng 		status = dladm_vnic_str2macaddr(macstr, attrp->va_mac_addr);
253*8275SEric Cheng 		if (status != DLADM_STATUS_OK)
254*8275SEric Cheng 			goto done;
255*8275SEric Cheng 	}
256*8275SEric Cheng 
257*8275SEric Cheng 	status = dladm_get_conf_field(conf, FVLANID, &u64, sizeof (u64));
258*8275SEric Cheng 	attrp->va_vid = ((status == DLADM_STATUS_OK) ?  (uint16_t)u64 : 0);
259*8275SEric Cheng 
260*8275SEric Cheng 
261*8275SEric Cheng 	status = DLADM_STATUS_OK;
262*8275SEric Cheng done:
263*8275SEric Cheng 	dladm_destroy_conf(conf);
264*8275SEric Cheng 	return (status);
265*8275SEric Cheng }
266*8275SEric Cheng 
267*8275SEric Cheng dladm_status_t
268*8275SEric Cheng dladm_vnic_info(datalink_id_t linkid, dladm_vnic_attr_t *attrp,
269*8275SEric Cheng     uint32_t flags)
270*8275SEric Cheng {
271*8275SEric Cheng 	if (flags == DLADM_OPT_ACTIVE)
272*8275SEric Cheng 		return (i_dladm_vnic_info_active(linkid, attrp));
273*8275SEric Cheng 	else if (flags == DLADM_OPT_PERSIST)
274*8275SEric Cheng 		return (i_dladm_vnic_info_persist(linkid, attrp));
275*8275SEric Cheng 	else
276*8275SEric Cheng 		return (DLADM_STATUS_BADARG);
277*8275SEric Cheng }
278*8275SEric Cheng 
279*8275SEric Cheng /*
280*8275SEric Cheng  * Remove a VNIC from the kernel.
281*8275SEric Cheng  */
282*8275SEric Cheng dladm_status_t
283*8275SEric Cheng i_dladm_vnic_delete_sys(datalink_id_t linkid)
284*8275SEric Cheng {
285*8275SEric Cheng 	vnic_ioc_delete_t ioc;
286*8275SEric Cheng 	dladm_status_t status = DLADM_STATUS_OK;
287*8275SEric Cheng 	int rc, fd;
288*8275SEric Cheng 
289*8275SEric Cheng 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
290*8275SEric Cheng 		return (dladm_errno2status(errno));
291*8275SEric Cheng 
292*8275SEric Cheng 	ioc.vd_vnic_id = linkid;
293*8275SEric Cheng 
294*8275SEric Cheng 	rc = ioctl(fd, VNIC_IOC_DELETE, &ioc);
295*8275SEric Cheng 	if (rc < 0)
2967408SSebastien.Roy@Sun.COM 		status = dladm_errno2status(errno);
2975084Sjohnlev 
2985084Sjohnlev 	(void) close(fd);
2997408SSebastien.Roy@Sun.COM 	return (status);
3005084Sjohnlev }
3015084Sjohnlev 
3025084Sjohnlev /*
3035084Sjohnlev  * Convert between MAC address types and their string representations.
3045084Sjohnlev  */
3055084Sjohnlev 
3065084Sjohnlev typedef struct dladm_vnic_addr_type_s {
307*8275SEric Cheng 	const char		*va_str;
308*8275SEric Cheng 	vnic_mac_addr_type_t	va_type;
3095084Sjohnlev } dladm_vnic_addr_type_t;
3105084Sjohnlev 
3115084Sjohnlev static dladm_vnic_addr_type_t addr_types[] = {
3125084Sjohnlev 	{"fixed", VNIC_MAC_ADDR_TYPE_FIXED},
313*8275SEric Cheng 	{"random", VNIC_MAC_ADDR_TYPE_RANDOM},
314*8275SEric Cheng 	{"factory", VNIC_MAC_ADDR_TYPE_FACTORY},
315*8275SEric Cheng 	{"auto", VNIC_MAC_ADDR_TYPE_AUTO},
316*8275SEric Cheng 	{"fixed", VNIC_MAC_ADDR_TYPE_PRIMARY}
3175084Sjohnlev };
3185084Sjohnlev 
3195084Sjohnlev #define	NADDR_TYPES (sizeof (addr_types) / sizeof (dladm_vnic_addr_type_t))
3205084Sjohnlev 
321*8275SEric Cheng static const char *
322*8275SEric Cheng dladm_vnic_macaddrtype2str(vnic_mac_addr_type_t type)
323*8275SEric Cheng {
324*8275SEric Cheng 	int i;
325*8275SEric Cheng 
326*8275SEric Cheng 	for (i = 0; i < NADDR_TYPES; i++) {
327*8275SEric Cheng 		if (type == addr_types[i].va_type)
328*8275SEric Cheng 			return (addr_types[i].va_str);
329*8275SEric Cheng 	}
330*8275SEric Cheng 	return (NULL);
331*8275SEric Cheng }
332*8275SEric Cheng 
3335895Syz147064 dladm_status_t
3345895Syz147064 dladm_vnic_str2macaddrtype(const char *str, vnic_mac_addr_type_t *val)
3355084Sjohnlev {
3365084Sjohnlev 	int i;
3375084Sjohnlev 	dladm_vnic_addr_type_t *type;
3385084Sjohnlev 
3395084Sjohnlev 	for (i = 0; i < NADDR_TYPES; i++) {
3405084Sjohnlev 		type = &addr_types[i];
3415084Sjohnlev 		if (strncmp(str, type->va_str, strlen(type->va_str)) == 0) {
3425084Sjohnlev 			*val = type->va_type;
3435895Syz147064 			return (DLADM_STATUS_OK);
3445084Sjohnlev 		}
3455084Sjohnlev 	}
3465895Syz147064 	return (DLADM_STATUS_BADARG);
3475084Sjohnlev }
3485084Sjohnlev 
349*8275SEric Cheng 
350*8275SEric Cheng 
3515084Sjohnlev /*
352*8275SEric Cheng  * Create a new VNIC / VLAN. Update the configuration file and bring it up.
3535084Sjohnlev  */
3545084Sjohnlev dladm_status_t
3555895Syz147064 dladm_vnic_create(const char *vnic, datalink_id_t linkid,
3565084Sjohnlev     vnic_mac_addr_type_t mac_addr_type, uchar_t *mac_addr, int mac_len,
357*8275SEric Cheng     int *mac_slot, uint_t mac_prefix_len, uint16_t vid,
358*8275SEric Cheng     datalink_id_t *vnic_id_out, dladm_arg_list_t *proplist, uint32_t flags)
3595084Sjohnlev {
360*8275SEric Cheng 	dladm_vnic_attr_t attr;
3615895Syz147064 	datalink_id_t vnic_id;
3625895Syz147064 	datalink_class_t class;
363*8275SEric Cheng 	uint32_t media = DL_ETHER;
364*8275SEric Cheng 	char name[MAXLINKNAMELEN];
365*8275SEric Cheng 	uchar_t tmp_addr[MAXMACADDRLEN];
3665084Sjohnlev 	dladm_status_t status;
367*8275SEric Cheng 	boolean_t is_vlan;
368*8275SEric Cheng 	boolean_t is_etherstub;
369*8275SEric Cheng 	int i;
3705084Sjohnlev 
3715084Sjohnlev 	/*
3725084Sjohnlev 	 * Sanity test arguments.
3735084Sjohnlev 	 */
374*8275SEric Cheng 	if ((flags & DLADM_OPT_ACTIVE) == 0)
375*8275SEric Cheng 		return (DLADM_STATUS_NOTSUP);
376*8275SEric Cheng 
377*8275SEric Cheng 	is_vlan = ((flags & DLADM_OPT_VLAN) != 0);
378*8275SEric Cheng 	if (is_vlan && ((vid < 1 || vid > 4094)))
379*8275SEric Cheng 		return (DLADM_STATUS_VIDINVAL);
380*8275SEric Cheng 
381*8275SEric Cheng 	is_etherstub = (linkid == DATALINK_INVALID_LINKID);
3825084Sjohnlev 
3835084Sjohnlev 	if (mac_len > MAXMACADDRLEN)
3845084Sjohnlev 		return (DLADM_STATUS_INVALIDMACADDRLEN);
3855084Sjohnlev 
386*8275SEric Cheng 	if (!dladm_vnic_macaddrtype2str(mac_addr_type))
3875084Sjohnlev 		return (DLADM_STATUS_INVALIDMACADDRTYPE);
3885084Sjohnlev 
389*8275SEric Cheng 	/*
390*8275SEric Cheng 	 * If a random address might be generated, but no prefix
391*8275SEric Cheng 	 * was specified by the caller, use the default MAC address
392*8275SEric Cheng 	 * prefix.
393*8275SEric Cheng 	 */
394*8275SEric Cheng 	if ((mac_addr_type == VNIC_MAC_ADDR_TYPE_RANDOM ||
395*8275SEric Cheng 	    mac_addr_type == VNIC_MAC_ADDR_TYPE_AUTO) &&
396*8275SEric Cheng 	    mac_prefix_len == 0) {
397*8275SEric Cheng 		mac_prefix_len = sizeof (dladm_vnic_def_prefix);
398*8275SEric Cheng 		mac_addr = tmp_addr;
399*8275SEric Cheng 		bcopy(dladm_vnic_def_prefix, mac_addr, mac_prefix_len);
4005895Syz147064 	}
4015895Syz147064 
402*8275SEric Cheng 	if ((flags & DLADM_OPT_ANCHOR) == 0) {
403*8275SEric Cheng 		if ((status = dladm_datalink_id2info(linkid, NULL, &class,
404*8275SEric Cheng 		    &media, NULL, 0)) != DLADM_STATUS_OK)
405*8275SEric Cheng 			return (status);
406*8275SEric Cheng 
407*8275SEric Cheng 		if (class == DATALINK_CLASS_VNIC ||
408*8275SEric Cheng 		    class == DATALINK_CLASS_VLAN)
409*8275SEric Cheng 			return (DLADM_STATUS_BADARG);
410*8275SEric Cheng 	} else {
411*8275SEric Cheng 		/* it's an anchor VNIC */
412*8275SEric Cheng 		if (linkid != DATALINK_INVALID_LINKID || vid != 0)
413*8275SEric Cheng 			return (DLADM_STATUS_BADARG);
414*8275SEric Cheng 	}
4155084Sjohnlev 
4165895Syz147064 	if (vnic == NULL) {
4175895Syz147064 		flags |= DLADM_OPT_PREFIX;
418*8275SEric Cheng 		(void) strlcpy(name, "vnic", sizeof (name));
419*8275SEric Cheng 	} else {
420*8275SEric Cheng 		(void) strlcpy(name, vnic, sizeof (name));
4215895Syz147064 	}
4225895Syz147064 
423*8275SEric Cheng 	class = is_vlan ? DATALINK_CLASS_VLAN :
424*8275SEric Cheng 	    (is_etherstub ? DATALINK_CLASS_ETHERSTUB : DATALINK_CLASS_VNIC);
425*8275SEric Cheng 	if ((status = dladm_create_datalink_id(name, class,
426*8275SEric Cheng 	    media, flags, &vnic_id)) != DLADM_STATUS_OK)
4275895Syz147064 		return (status);
428*8275SEric Cheng 
429*8275SEric Cheng 	if ((flags & DLADM_OPT_PREFIX) != 0) {
430*8275SEric Cheng 		(void) snprintf(name + 4, sizeof (name), "%llu", vnic_id);
431*8275SEric Cheng 		flags &= ~DLADM_OPT_PREFIX;
4325084Sjohnlev 	}
4335084Sjohnlev 
4345084Sjohnlev 	bzero(&attr, sizeof (attr));
435*8275SEric Cheng 
436*8275SEric Cheng 	/* Extract resource_ctl and cpu_list from proplist */
437*8275SEric Cheng 	if (proplist != NULL) {
438*8275SEric Cheng 		status = dladm_link_proplist_extract(proplist,
439*8275SEric Cheng 		    &attr.va_resource_props);
440*8275SEric Cheng 		if (status != DLADM_STATUS_OK)
441*8275SEric Cheng 			goto done;
442*8275SEric Cheng 	}
4435084Sjohnlev 
444*8275SEric Cheng 	attr.va_vnic_id = vnic_id;
445*8275SEric Cheng 	attr.va_link_id = linkid;
446*8275SEric Cheng 	attr.va_mac_addr_type = mac_addr_type;
447*8275SEric Cheng 	attr.va_mac_len = mac_len;
448*8275SEric Cheng 	if (mac_slot != NULL)
449*8275SEric Cheng 		attr.va_mac_slot = *mac_slot;
450*8275SEric Cheng 	if (mac_len > 0)
451*8275SEric Cheng 		bcopy(mac_addr, attr.va_mac_addr, mac_len);
452*8275SEric Cheng 	else if (mac_prefix_len > 0)
453*8275SEric Cheng 		bcopy(mac_addr, attr.va_mac_addr, mac_prefix_len);
454*8275SEric Cheng 	attr.va_mac_prefix_len = mac_prefix_len;
455*8275SEric Cheng 	attr.va_vid = vid;
456*8275SEric Cheng 	attr.va_force = (flags & DLADM_OPT_FORCE) != 0;
457*8275SEric Cheng 	attr.va_hwrings = (flags & DLADM_OPT_HWRINGS) != 0;
458*8275SEric Cheng 
459*8275SEric Cheng 	status = i_dladm_vnic_create_sys(&attr);
460*8275SEric Cheng 	if (status != DLADM_STATUS_OK)
461*8275SEric Cheng 		goto done;
462*8275SEric Cheng 
463*8275SEric Cheng 	/* Save vnic configuration and its properties */
464*8275SEric Cheng 	if (!(flags & DLADM_OPT_PERSIST))
465*8275SEric Cheng 		goto done;
466*8275SEric Cheng 
467*8275SEric Cheng 	status = dladm_vnic_persist_conf(name, &attr, class);
468*8275SEric Cheng 	if (status != DLADM_STATUS_OK) {
469*8275SEric Cheng 		(void) i_dladm_vnic_delete_sys(vnic_id);
4705895Syz147064 		goto done;
4715895Syz147064 	}
4725895Syz147064 
473*8275SEric Cheng 	if (proplist != NULL) {
474*8275SEric Cheng 		for (i = 0; i < proplist->al_count; i++) {
475*8275SEric Cheng 			dladm_arg_info_t	*aip = &proplist->al_info[i];
476*8275SEric Cheng 
477*8275SEric Cheng 			status = dladm_set_linkprop(vnic_id, aip->ai_name,
478*8275SEric Cheng 			    aip->ai_val, aip->ai_count, DLADM_OPT_PERSIST);
479*8275SEric Cheng 			if (status != DLADM_STATUS_OK)
480*8275SEric Cheng 				break;
481*8275SEric Cheng 		}
482*8275SEric Cheng 
483*8275SEric Cheng 		if (status != DLADM_STATUS_OK) {
484*8275SEric Cheng 			(void) dladm_remove_conf(vnic_id);
485*8275SEric Cheng 			(void) i_dladm_vnic_delete_sys(vnic_id);
486*8275SEric Cheng 		}
487*8275SEric Cheng 	}
4885084Sjohnlev 
4895895Syz147064 done:
4905895Syz147064 	if (status != DLADM_STATUS_OK) {
491*8275SEric Cheng 		(void) dladm_destroy_datalink_id(vnic_id, flags);
4925895Syz147064 	} else {
493*8275SEric Cheng 		if (vnic_id_out != NULL)
494*8275SEric Cheng 			*vnic_id_out = vnic_id;
495*8275SEric Cheng 		if (mac_slot != NULL)
496*8275SEric Cheng 			*mac_slot = attr.va_mac_slot;
4975895Syz147064 	}
4985084Sjohnlev 	return (status);
4995084Sjohnlev }
5005084Sjohnlev 
5015084Sjohnlev /*
502*8275SEric Cheng  * Delete a VNIC / VLAN.
5035084Sjohnlev  */
5045084Sjohnlev dladm_status_t
505*8275SEric Cheng dladm_vnic_delete(datalink_id_t linkid, uint32_t flags)
5065084Sjohnlev {
507*8275SEric Cheng 	dladm_status_t status;
508*8275SEric Cheng 	datalink_class_t class;
509*8275SEric Cheng 
510*8275SEric Cheng 	if (flags == 0)
511*8275SEric Cheng 		return (DLADM_STATUS_BADARG);
5125084Sjohnlev 
513*8275SEric Cheng 	if ((dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0) !=
514*8275SEric Cheng 	    DLADM_STATUS_OK))
515*8275SEric Cheng 		return (DLADM_STATUS_BADARG);
5165084Sjohnlev 
517*8275SEric Cheng 	if ((flags & DLADM_OPT_VLAN) != 0) {
518*8275SEric Cheng 		if (class != DATALINK_CLASS_VLAN)
519*8275SEric Cheng 			return (DLADM_STATUS_BADARG);
520*8275SEric Cheng 	} else {
521*8275SEric Cheng 		if (class != DATALINK_CLASS_VNIC &&
522*8275SEric Cheng 		    class != DATALINK_CLASS_ETHERSTUB)
523*8275SEric Cheng 			return (DLADM_STATUS_BADARG);
5245084Sjohnlev 	}
5255084Sjohnlev 
526*8275SEric Cheng 	if ((flags & DLADM_OPT_ACTIVE) != 0) {
527*8275SEric Cheng 		status = i_dladm_vnic_delete_sys(linkid);
528*8275SEric Cheng 		if (status == DLADM_STATUS_OK) {
529*8275SEric Cheng 			(void) dladm_set_linkprop(linkid, NULL, NULL, 0,
530*8275SEric Cheng 			    DLADM_OPT_ACTIVE);
531*8275SEric Cheng 			(void) dladm_destroy_datalink_id(linkid,
532*8275SEric Cheng 			    DLADM_OPT_ACTIVE);
533*8275SEric Cheng 		} else if (status != DLADM_STATUS_NOTFOUND ||
534*8275SEric Cheng 		    !(flags & DLADM_OPT_PERSIST)) {
535*8275SEric Cheng 			return (status);
536*8275SEric Cheng 		}
537*8275SEric Cheng 	}
538*8275SEric Cheng 	if ((flags & DLADM_OPT_PERSIST) != 0) {
539*8275SEric Cheng 		(void) dladm_destroy_datalink_id(linkid, DLADM_OPT_PERSIST);
540*8275SEric Cheng 		(void) dladm_remove_conf(linkid);
541*8275SEric Cheng 	}
542*8275SEric Cheng 	return (DLADM_STATUS_OK);
543*8275SEric Cheng }
544*8275SEric Cheng 
545*8275SEric Cheng static const char *
546*8275SEric Cheng dladm_vnic_macaddr2str(const uchar_t *mac, char *buf)
547*8275SEric Cheng {
548*8275SEric Cheng 	static char unknown_mac[] = {0, 0, 0, 0, 0, 0};
549*8275SEric Cheng 
550*8275SEric Cheng 	if (buf == NULL)
551*8275SEric Cheng 		return (NULL);
552*8275SEric Cheng 
553*8275SEric Cheng 	if (bcmp(unknown_mac, mac, ETHERADDRL) == 0)
554*8275SEric Cheng 		(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
555*8275SEric Cheng 	else
556*8275SEric Cheng 		return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER));
557*8275SEric Cheng 
558*8275SEric Cheng 	return (buf);
559*8275SEric Cheng }
560*8275SEric Cheng 
561*8275SEric Cheng static dladm_status_t
562*8275SEric Cheng dladm_vnic_str2macaddr(const char *str, uchar_t *buf)
563*8275SEric Cheng {
564*8275SEric Cheng 	int len = 0;
565*8275SEric Cheng 	uchar_t *b = _link_aton(str, &len);
566*8275SEric Cheng 
567*8275SEric Cheng 	if (b == NULL || len >= MAXMACADDRLEN)
568*8275SEric Cheng 		return (DLADM_STATUS_BADARG);
569*8275SEric Cheng 
570*8275SEric Cheng 	bcopy(b, buf, len);
571*8275SEric Cheng 	free(b);
572*8275SEric Cheng 	return (DLADM_STATUS_OK);
5735084Sjohnlev }
5745084Sjohnlev 
5755084Sjohnlev 
576*8275SEric Cheng static dladm_status_t
577*8275SEric Cheng dladm_vnic_persist_conf(const char *name, dladm_vnic_attr_t *attrp,
578*8275SEric Cheng     datalink_class_t class)
579*8275SEric Cheng {
580*8275SEric Cheng 	dladm_conf_t conf = DLADM_INVALID_CONF;
581*8275SEric Cheng 	dladm_status_t status;
582*8275SEric Cheng 	char macstr[ETHERADDRL * 3];
583*8275SEric Cheng 	uint64_t u64;
5845084Sjohnlev 
585*8275SEric Cheng 	if ((status = dladm_create_conf(name, attrp->va_vnic_id,
586*8275SEric Cheng 	    class, DL_ETHER, &conf)) != DLADM_STATUS_OK)
5875895Syz147064 		return (status);
5885895Syz147064 
589*8275SEric Cheng 	if (attrp->va_link_id != DATALINK_INVALID_LINKID) {
590*8275SEric Cheng 		u64 = attrp->va_link_id;
591*8275SEric Cheng 		status = dladm_set_conf_field(conf, FLINKOVER,
592*8275SEric Cheng 		    DLADM_TYPE_UINT64, &u64);
593*8275SEric Cheng 		if (status != DLADM_STATUS_OK)
594*8275SEric Cheng 			goto done;
595*8275SEric Cheng 	}
596*8275SEric Cheng 
597*8275SEric Cheng 	if (class != DATALINK_CLASS_VLAN) {
598*8275SEric Cheng 		u64 = attrp->va_mac_addr_type;
599*8275SEric Cheng 		status = dladm_set_conf_field(conf, FMADDRTYPE,
600*8275SEric Cheng 		    DLADM_TYPE_UINT64, &u64);
601*8275SEric Cheng 		if (status != DLADM_STATUS_OK)
602*8275SEric Cheng 			goto done;
603*8275SEric Cheng 
604*8275SEric Cheng 		if (attrp->va_mac_len != ETHERADDRL) {
605*8275SEric Cheng 			u64 = attrp->va_mac_len;
606*8275SEric Cheng 			status = dladm_set_conf_field(conf, FMADDRLEN,
607*8275SEric Cheng 			    DLADM_TYPE_UINT64, &u64);
608*8275SEric Cheng 			if (status != DLADM_STATUS_OK)
609*8275SEric Cheng 				goto done;
610*8275SEric Cheng 		}
611*8275SEric Cheng 	}
612*8275SEric Cheng 
613*8275SEric Cheng 	if (attrp->va_hwrings) {
614*8275SEric Cheng 		boolean_t hwrings = attrp->va_hwrings;
615*8275SEric Cheng 		status = dladm_set_conf_field(conf, FHWRINGS,
616*8275SEric Cheng 		    DLADM_TYPE_BOOLEAN, &hwrings);
617*8275SEric Cheng 		if (status != DLADM_STATUS_OK)
618*8275SEric Cheng 			goto done;
619*8275SEric Cheng 	}
620*8275SEric Cheng 
621*8275SEric Cheng 	if (class != DATALINK_CLASS_VLAN) {
622*8275SEric Cheng 		if (attrp->va_mac_slot != -1) {
623*8275SEric Cheng 			u64 = attrp->va_mac_slot;
624*8275SEric Cheng 			status = dladm_set_conf_field(conf, FMADDRSLOT,
625*8275SEric Cheng 			    DLADM_TYPE_UINT64, &u64);
626*8275SEric Cheng 			if (status != DLADM_STATUS_OK)
627*8275SEric Cheng 			goto done;
628*8275SEric Cheng 		}
629*8275SEric Cheng 
630*8275SEric Cheng 		if (attrp->va_mac_prefix_len !=
631*8275SEric Cheng 		    sizeof (dladm_vnic_def_prefix)) {
632*8275SEric Cheng 			u64 = attrp->va_mac_prefix_len;
633*8275SEric Cheng 			status = dladm_set_conf_field(conf, FMADDRPREFIXLEN,
634*8275SEric Cheng 			    DLADM_TYPE_UINT64, &u64);
635*8275SEric Cheng 			if (status != DLADM_STATUS_OK)
636*8275SEric Cheng 				goto done;
637*8275SEric Cheng 		}
638*8275SEric Cheng 
639*8275SEric Cheng 		(void) dladm_vnic_macaddr2str(attrp->va_mac_addr, macstr);
640*8275SEric Cheng 		status = dladm_set_conf_field(conf, FMACADDR, DLADM_TYPE_STR,
641*8275SEric Cheng 		    macstr);
642*8275SEric Cheng 		if (status != DLADM_STATUS_OK)
643*8275SEric Cheng 			goto done;
644*8275SEric Cheng 	}
645*8275SEric Cheng 
646*8275SEric Cheng 	if (attrp->va_vid != 0) {
647*8275SEric Cheng 		u64 = attrp->va_vid;
648*8275SEric Cheng 		status = dladm_set_conf_field(conf, FVLANID,
649*8275SEric Cheng 		    DLADM_TYPE_UINT64, &u64);
650*8275SEric Cheng 		if (status != DLADM_STATUS_OK)
651*8275SEric Cheng 			goto done;
652*8275SEric Cheng 	}
653*8275SEric Cheng 
654*8275SEric Cheng 	/*
655*8275SEric Cheng 	 * Commit the link configuration.
656*8275SEric Cheng 	 */
657*8275SEric Cheng 	status = dladm_write_conf(conf);
658*8275SEric Cheng 
659*8275SEric Cheng done:
660*8275SEric Cheng 	dladm_destroy_conf(conf);
6615895Syz147064 	return (status);
6625084Sjohnlev }
663*8275SEric Cheng 
664*8275SEric Cheng typedef struct dladm_vnic_up_arg_s {
665*8275SEric Cheng 	uint32_t	flags;
666*8275SEric Cheng 	dladm_status_t	status;
667*8275SEric Cheng } dladm_vnic_up_arg_t;
668*8275SEric Cheng 
669*8275SEric Cheng #define	DLADM_VNIC_UP_FIRST_WALK	0x1
670*8275SEric Cheng #define	DLADM_VNIC_UP_SECOND_WALK	0x2
671*8275SEric Cheng 
672*8275SEric Cheng static int
673*8275SEric Cheng i_dladm_vnic_up(datalink_id_t linkid, void *arg)
674*8275SEric Cheng {
675*8275SEric Cheng 	dladm_status_t *statusp = &(((dladm_vnic_up_arg_t *)arg)->status);
676*8275SEric Cheng 	dladm_vnic_attr_t attr;
677*8275SEric Cheng 	dladm_status_t status;
678*8275SEric Cheng 	dladm_arg_list_t *proplist;
679*8275SEric Cheng 	uint32_t flags = ((dladm_vnic_up_arg_t *)arg)->flags;
680*8275SEric Cheng 
681*8275SEric Cheng 	bzero(&attr, sizeof (attr));
682*8275SEric Cheng 
683*8275SEric Cheng 	status = dladm_vnic_info(linkid, &attr, DLADM_OPT_PERSIST);
684*8275SEric Cheng 	if (status != DLADM_STATUS_OK)
685*8275SEric Cheng 		goto done;
686*8275SEric Cheng 
687*8275SEric Cheng 	/*
688*8275SEric Cheng 	 * Create the vnics that request hardware group first
689*8275SEric Cheng 	 * Create the vnics that don't request hardware group in the second walk
690*8275SEric Cheng 	 */
691*8275SEric Cheng 	if ((flags == DLADM_VNIC_UP_FIRST_WALK && !attr.va_hwrings) ||
692*8275SEric Cheng 	    (flags == DLADM_VNIC_UP_SECOND_WALK && attr.va_hwrings))
693*8275SEric Cheng 			goto done;
694*8275SEric Cheng 
695*8275SEric Cheng 	/* Get all properties for this vnic */
696*8275SEric Cheng 	status = dladm_link_get_proplist(linkid, &proplist);
697*8275SEric Cheng 	if (status != DLADM_STATUS_OK)
698*8275SEric Cheng 		goto done;
699*8275SEric Cheng 
700*8275SEric Cheng 	if (proplist != NULL) {
701*8275SEric Cheng 		status = dladm_link_proplist_extract(proplist,
702*8275SEric Cheng 		    &attr.va_resource_props);
703*8275SEric Cheng 	}
704*8275SEric Cheng 
705*8275SEric Cheng 	status = i_dladm_vnic_create_sys(&attr);
706*8275SEric Cheng 	if (status != DLADM_STATUS_OK)
707*8275SEric Cheng 		goto done;
708*8275SEric Cheng 
709*8275SEric Cheng 	if ((status = dladm_up_datalink_id(linkid)) != DLADM_STATUS_OK) {
710*8275SEric Cheng 		(void) i_dladm_vnic_delete_sys(linkid);
711*8275SEric Cheng 		goto done;
712*8275SEric Cheng 	}
713*8275SEric Cheng done:
714*8275SEric Cheng 	*statusp = status;
715*8275SEric Cheng 	return (DLADM_WALK_CONTINUE);
716*8275SEric Cheng }
717*8275SEric Cheng 
718*8275SEric Cheng dladm_status_t
719*8275SEric Cheng dladm_vnic_up(datalink_id_t linkid, uint32_t flags)
720*8275SEric Cheng {
721*8275SEric Cheng 	dladm_vnic_up_arg_t vnic_arg;
722*8275SEric Cheng 	datalink_class_t class;
723*8275SEric Cheng 
724*8275SEric Cheng 	class = ((flags & DLADM_OPT_VLAN) != 0) ? DATALINK_CLASS_VLAN :
725*8275SEric Cheng 	    (DATALINK_CLASS_VNIC | DATALINK_CLASS_ETHERSTUB);
726*8275SEric Cheng 
727*8275SEric Cheng 	if (linkid == DATALINK_ALL_LINKID) {
728*8275SEric Cheng 		vnic_arg.flags = DLADM_VNIC_UP_FIRST_WALK;
729*8275SEric Cheng 		(void) dladm_walk_datalink_id(i_dladm_vnic_up, &vnic_arg,
730*8275SEric Cheng 		    class, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
731*8275SEric Cheng 		vnic_arg.flags = DLADM_VNIC_UP_SECOND_WALK;
732*8275SEric Cheng 		(void) dladm_walk_datalink_id(i_dladm_vnic_up, &vnic_arg,
733*8275SEric Cheng 		    class, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
734*8275SEric Cheng 		return (DLADM_STATUS_OK);
735*8275SEric Cheng 	} else {
736*8275SEric Cheng 		(void) i_dladm_vnic_up(linkid, &vnic_arg);
737*8275SEric Cheng 		return (vnic_arg.status);
738*8275SEric Cheng 	}
739*8275SEric Cheng }
740