1*5895Syz147064 /*
2*5895Syz147064  * CDDL HEADER START
3*5895Syz147064  *
4*5895Syz147064  * The contents of this file are subject to the terms of the
5*5895Syz147064  * Common Development and Distribution License (the "License").
6*5895Syz147064  * You may not use this file except in compliance with the License.
7*5895Syz147064  *
8*5895Syz147064  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5895Syz147064  * or http://www.opensolaris.org/os/licensing.
10*5895Syz147064  * See the License for the specific language governing permissions
11*5895Syz147064  * and limitations under the License.
12*5895Syz147064  *
13*5895Syz147064  * When distributing Covered Code, include this CDDL HEADER in each
14*5895Syz147064  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5895Syz147064  * If applicable, add the following below this CDDL HEADER, with the
16*5895Syz147064  * fields enclosed by brackets "[]" replaced with your own identifying
17*5895Syz147064  * information: Portions Copyright [yyyy] [name of copyright owner]
18*5895Syz147064  *
19*5895Syz147064  * CDDL HEADER END
20*5895Syz147064  */
21*5895Syz147064 /*
22*5895Syz147064  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23*5895Syz147064  * Use is subject to license terms.
24*5895Syz147064  */
25*5895Syz147064 
26*5895Syz147064 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*5895Syz147064 
28*5895Syz147064 #include <sys/types.h>
29*5895Syz147064 #include <sys/stat.h>
30*5895Syz147064 #include <fcntl.h>
31*5895Syz147064 #include <unistd.h>
32*5895Syz147064 #include <errno.h>
33*5895Syz147064 #include <assert.h>
34*5895Syz147064 #include <sys/dld.h>
35*5895Syz147064 #include <libdladm_impl.h>
36*5895Syz147064 #include <libdllink.h>
37*5895Syz147064 #include <libdlvlan.h>
38*5895Syz147064 
39*5895Syz147064 /*
40*5895Syz147064  * VLAN Administration Library.
41*5895Syz147064  *
42*5895Syz147064  * This library is used by administration tools such as dladm(1M) to
43*5895Syz147064  * configure VLANs.
44*5895Syz147064  */
45*5895Syz147064 
46*5895Syz147064 /*
47*5895Syz147064  * Returns the current attributes of the specified VLAN.
48*5895Syz147064  */
49*5895Syz147064 static dladm_status_t
50*5895Syz147064 i_dladm_vlan_info_active(datalink_id_t vlanid, dladm_vlan_attr_t *dvap)
51*5895Syz147064 {
52*5895Syz147064 	int			fd;
53*5895Syz147064 	dld_ioc_vlan_attr_t	div;
54*5895Syz147064 	dladm_status_t		status = DLADM_STATUS_OK;
55*5895Syz147064 
56*5895Syz147064 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
57*5895Syz147064 		return (dladm_errno2status(errno));
58*5895Syz147064 
59*5895Syz147064 	div.div_vlanid = vlanid;
60*5895Syz147064 
61*5895Syz147064 	if (i_dladm_ioctl(fd, DLDIOC_VLAN_ATTR, &div, sizeof (div)) < 0)
62*5895Syz147064 		status = dladm_errno2status(errno);
63*5895Syz147064 
64*5895Syz147064 	dvap->dv_vid = div.div_vid;
65*5895Syz147064 	dvap->dv_linkid = div.div_linkid;
66*5895Syz147064 	dvap->dv_force = div.div_force;
67*5895Syz147064 	dvap->dv_implicit = div.div_implicit;
68*5895Syz147064 done:
69*5895Syz147064 	(void) close(fd);
70*5895Syz147064 	return (status);
71*5895Syz147064 }
72*5895Syz147064 
73*5895Syz147064 /*
74*5895Syz147064  * Returns the persistent attributes of the specified VLAN.
75*5895Syz147064  */
76*5895Syz147064 static dladm_status_t
77*5895Syz147064 i_dladm_vlan_info_persist(datalink_id_t vlanid, dladm_vlan_attr_t *dvap)
78*5895Syz147064 {
79*5895Syz147064 	dladm_conf_t	conf = DLADM_INVALID_CONF;
80*5895Syz147064 	dladm_status_t	status;
81*5895Syz147064 	uint64_t	u64;
82*5895Syz147064 
83*5895Syz147064 	if ((status = dladm_read_conf(vlanid, &conf)) != DLADM_STATUS_OK)
84*5895Syz147064 		return (status);
85*5895Syz147064 
86*5895Syz147064 	status = dladm_get_conf_field(conf, FLINKOVER, &u64, sizeof (u64));
87*5895Syz147064 	if (status != DLADM_STATUS_OK)
88*5895Syz147064 		goto done;
89*5895Syz147064 	dvap->dv_linkid = (datalink_id_t)u64;
90*5895Syz147064 
91*5895Syz147064 	status = dladm_get_conf_field(conf, FFORCE, &dvap->dv_force,
92*5895Syz147064 	    sizeof (boolean_t));
93*5895Syz147064 	if (status != DLADM_STATUS_OK)
94*5895Syz147064 		goto done;
95*5895Syz147064 
96*5895Syz147064 	dvap->dv_implicit = B_FALSE;
97*5895Syz147064 
98*5895Syz147064 	status = dladm_get_conf_field(conf, FVLANID, &u64, sizeof (u64));
99*5895Syz147064 	if (status != DLADM_STATUS_OK)
100*5895Syz147064 		goto done;
101*5895Syz147064 	dvap->dv_vid = (uint16_t)u64;
102*5895Syz147064 
103*5895Syz147064 done:
104*5895Syz147064 	dladm_destroy_conf(conf);
105*5895Syz147064 	return (status);
106*5895Syz147064 }
107*5895Syz147064 
108*5895Syz147064 dladm_status_t
109*5895Syz147064 dladm_vlan_info(datalink_id_t vlanid, dladm_vlan_attr_t *dvap, uint32_t flags)
110*5895Syz147064 {
111*5895Syz147064 	assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST);
112*5895Syz147064 	if (flags == DLADM_OPT_ACTIVE)
113*5895Syz147064 		return (i_dladm_vlan_info_active(vlanid, dvap));
114*5895Syz147064 	else
115*5895Syz147064 		return (i_dladm_vlan_info_persist(vlanid, dvap));
116*5895Syz147064 }
117*5895Syz147064 
118*5895Syz147064 static dladm_status_t
119*5895Syz147064 dladm_persist_vlan_conf(const char *vlan, datalink_id_t vlanid,
120*5895Syz147064     boolean_t force, datalink_id_t linkid, uint16_t vid)
121*5895Syz147064 {
122*5895Syz147064 	dladm_conf_t	conf = DLADM_INVALID_CONF;
123*5895Syz147064 	dladm_status_t	status;
124*5895Syz147064 	uint64_t	u64;
125*5895Syz147064 
126*5895Syz147064 	if ((status = dladm_create_conf(vlan, vlanid, DATALINK_CLASS_VLAN,
127*5895Syz147064 	    DL_ETHER, &conf)) != DLADM_STATUS_OK) {
128*5895Syz147064 		return (status);
129*5895Syz147064 	}
130*5895Syz147064 
131*5895Syz147064 	u64 = linkid;
132*5895Syz147064 	status = dladm_set_conf_field(conf, FLINKOVER, DLADM_TYPE_UINT64, &u64);
133*5895Syz147064 	if (status != DLADM_STATUS_OK)
134*5895Syz147064 		goto done;
135*5895Syz147064 
136*5895Syz147064 	status = dladm_set_conf_field(conf, FFORCE, DLADM_TYPE_BOOLEAN, &force);
137*5895Syz147064 	if (status != DLADM_STATUS_OK)
138*5895Syz147064 		goto done;
139*5895Syz147064 
140*5895Syz147064 	u64 = vid;
141*5895Syz147064 	status = dladm_set_conf_field(conf, FVLANID, DLADM_TYPE_UINT64, &u64);
142*5895Syz147064 	if (status != DLADM_STATUS_OK)
143*5895Syz147064 		goto done;
144*5895Syz147064 
145*5895Syz147064 	status = dladm_write_conf(conf);
146*5895Syz147064 
147*5895Syz147064 done:
148*5895Syz147064 	dladm_destroy_conf(conf);
149*5895Syz147064 	return (status);
150*5895Syz147064 }
151*5895Syz147064 
152*5895Syz147064 /*
153*5895Syz147064  * Create a VLAN on given link.
154*5895Syz147064  */
155*5895Syz147064 dladm_status_t
156*5895Syz147064 dladm_vlan_create(const char *vlan, datalink_id_t linkid, uint16_t vid,
157*5895Syz147064     uint32_t flags)
158*5895Syz147064 {
159*5895Syz147064 	dld_ioc_create_vlan_t	dic;
160*5895Syz147064 	int			fd;
161*5895Syz147064 	datalink_id_t		vlanid = DATALINK_INVALID_LINKID;
162*5895Syz147064 	uint_t			media;
163*5895Syz147064 	datalink_class_t	class;
164*5895Syz147064 	dladm_status_t		status;
165*5895Syz147064 
166*5895Syz147064 	if (vid < 1 || vid > 4094)
167*5895Syz147064 		return (DLADM_STATUS_VIDINVAL);
168*5895Syz147064 
169*5895Syz147064 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
170*5895Syz147064 		return (dladm_errno2status(errno));
171*5895Syz147064 
172*5895Syz147064 	status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0);
173*5895Syz147064 	if (status != DLADM_STATUS_OK || media != DL_ETHER ||
174*5895Syz147064 	    class == DATALINK_CLASS_VLAN) {
175*5895Syz147064 		return (DLADM_STATUS_BADARG);
176*5895Syz147064 	}
177*5895Syz147064 
178*5895Syz147064 	status = dladm_create_datalink_id(vlan, DATALINK_CLASS_VLAN, DL_ETHER,
179*5895Syz147064 	    flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST), &vlanid);
180*5895Syz147064 	if (status != DLADM_STATUS_OK)
181*5895Syz147064 		goto fail;
182*5895Syz147064 
183*5895Syz147064 	if (flags & DLADM_OPT_PERSIST) {
184*5895Syz147064 		status = dladm_persist_vlan_conf(vlan, vlanid,
185*5895Syz147064 		    (flags & DLADM_OPT_FORCE) != 0, linkid, vid);
186*5895Syz147064 		if (status != DLADM_STATUS_OK)
187*5895Syz147064 			goto fail;
188*5895Syz147064 	}
189*5895Syz147064 
190*5895Syz147064 	if (flags & DLADM_OPT_ACTIVE) {
191*5895Syz147064 		dic.dic_vlanid = vlanid;
192*5895Syz147064 		dic.dic_linkid = linkid;
193*5895Syz147064 		dic.dic_vid = vid;
194*5895Syz147064 		dic.dic_force = (flags & DLADM_OPT_FORCE) != 0;
195*5895Syz147064 
196*5895Syz147064 		if (i_dladm_ioctl(fd, DLDIOC_CREATE_VLAN, &dic,
197*5895Syz147064 		    sizeof (dic)) < 0) {
198*5895Syz147064 			status = dladm_errno2status(errno);
199*5895Syz147064 			if (flags & DLADM_OPT_PERSIST)
200*5895Syz147064 				(void) dladm_remove_conf(vlanid);
201*5895Syz147064 			goto fail;
202*5895Syz147064 		}
203*5895Syz147064 	}
204*5895Syz147064 
205*5895Syz147064 	(void) close(fd);
206*5895Syz147064 	return (DLADM_STATUS_OK);
207*5895Syz147064 
208*5895Syz147064 fail:
209*5895Syz147064 	if (vlanid != DATALINK_INVALID_LINKID) {
210*5895Syz147064 		(void) dladm_destroy_datalink_id(vlanid,
211*5895Syz147064 		    flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST));
212*5895Syz147064 	}
213*5895Syz147064 	(void) close(fd);
214*5895Syz147064 	return (status);
215*5895Syz147064 }
216*5895Syz147064 
217*5895Syz147064 /*
218*5895Syz147064  * Delete a given VLAN.
219*5895Syz147064  */
220*5895Syz147064 dladm_status_t
221*5895Syz147064 dladm_vlan_delete(datalink_id_t vlanid, uint32_t flags)
222*5895Syz147064 {
223*5895Syz147064 	dld_ioc_delete_vlan_t	did;
224*5895Syz147064 	int			fd;
225*5895Syz147064 	datalink_class_t	class;
226*5895Syz147064 	dladm_status_t		status = DLADM_STATUS_OK;
227*5895Syz147064 
228*5895Syz147064 	if ((dladm_datalink_id2info(vlanid, NULL, &class, NULL, NULL, 0) !=
229*5895Syz147064 	    DLADM_STATUS_OK) || (class != DATALINK_CLASS_VLAN)) {
230*5895Syz147064 		return (DLADM_STATUS_BADARG);
231*5895Syz147064 	}
232*5895Syz147064 
233*5895Syz147064 	if (flags & DLADM_OPT_ACTIVE) {
234*5895Syz147064 		if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
235*5895Syz147064 			return (dladm_errno2status(errno));
236*5895Syz147064 
237*5895Syz147064 		did.did_linkid = vlanid;
238*5895Syz147064 		if ((i_dladm_ioctl(fd, DLDIOC_DELETE_VLAN, &did,
239*5895Syz147064 		    sizeof (did)) < 0) &&
240*5895Syz147064 		    ((errno != ENOENT) || !(flags & DLADM_OPT_PERSIST))) {
241*5895Syz147064 			(void) close(fd);
242*5895Syz147064 			return (dladm_errno2status(errno));
243*5895Syz147064 		}
244*5895Syz147064 		(void) close(fd);
245*5895Syz147064 
246*5895Syz147064 		/*
247*5895Syz147064 		 * Delete active linkprop before this active link is deleted.
248*5895Syz147064 		 */
249*5895Syz147064 		(void) dladm_set_linkprop(vlanid, NULL, NULL, 0,
250*5895Syz147064 		    DLADM_OPT_ACTIVE);
251*5895Syz147064 	}
252*5895Syz147064 
253*5895Syz147064 	(void) dladm_destroy_datalink_id(vlanid,
254*5895Syz147064 	    flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST));
255*5895Syz147064 
256*5895Syz147064 	if (flags & DLADM_OPT_PERSIST)
257*5895Syz147064 		(void) dladm_remove_conf(vlanid);
258*5895Syz147064 
259*5895Syz147064 	return (status);
260*5895Syz147064 }
261*5895Syz147064 
262*5895Syz147064 /*
263*5895Syz147064  * Callback used by dladm_vlan_up()
264*5895Syz147064  */
265*5895Syz147064 static int
266*5895Syz147064 i_dladm_vlan_up(datalink_id_t vlanid, void *arg)
267*5895Syz147064 {
268*5895Syz147064 	dladm_vlan_attr_t	dva;
269*5895Syz147064 	dld_ioc_create_vlan_t	dic;
270*5895Syz147064 	dladm_status_t		*statusp = arg;
271*5895Syz147064 	uint32_t		flags;
272*5895Syz147064 	int			fd;
273*5895Syz147064 	dladm_status_t		status;
274*5895Syz147064 
275*5895Syz147064 	status = dladm_vlan_info(vlanid, &dva, DLADM_OPT_PERSIST);
276*5895Syz147064 	if (status != DLADM_STATUS_OK)
277*5895Syz147064 		goto done;
278*5895Syz147064 
279*5895Syz147064 	/*
280*5895Syz147064 	 * Validate (and delete) the link associated with this VLAN, see if
281*5895Syz147064 	 * the specific hardware has been removed during system shutdown.
282*5895Syz147064 	 */
283*5895Syz147064 	if ((status = dladm_datalink_id2info(dva.dv_linkid, &flags, NULL,
284*5895Syz147064 	    NULL, NULL, 0)) != DLADM_STATUS_OK) {
285*5895Syz147064 		goto done;
286*5895Syz147064 	}
287*5895Syz147064 
288*5895Syz147064 	if (!(flags & DLADM_OPT_ACTIVE)) {
289*5895Syz147064 		status = DLADM_STATUS_BADARG;
290*5895Syz147064 		goto done;
291*5895Syz147064 	}
292*5895Syz147064 
293*5895Syz147064 	dic.dic_linkid = dva.dv_linkid;
294*5895Syz147064 	dic.dic_force = dva.dv_force;
295*5895Syz147064 	dic.dic_vid = dva.dv_vid;
296*5895Syz147064 
297*5895Syz147064 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) {
298*5895Syz147064 		status = dladm_errno2status(errno);
299*5895Syz147064 		goto done;
300*5895Syz147064 	}
301*5895Syz147064 
302*5895Syz147064 	dic.dic_vlanid = vlanid;
303*5895Syz147064 	if (i_dladm_ioctl(fd, DLDIOC_CREATE_VLAN, &dic, sizeof (dic)) < 0) {
304*5895Syz147064 		status = dladm_errno2status(errno);
305*5895Syz147064 		goto done;
306*5895Syz147064 	}
307*5895Syz147064 
308*5895Syz147064 	if ((status = dladm_up_datalink_id(vlanid)) != DLADM_STATUS_OK) {
309*5895Syz147064 		dld_ioc_delete_vlan_t did;
310*5895Syz147064 
311*5895Syz147064 		did.did_linkid = vlanid;
312*5895Syz147064 		(void) i_dladm_ioctl(fd, DLDIOC_DELETE_VLAN, &did,
313*5895Syz147064 		    sizeof (did));
314*5895Syz147064 	} else {
315*5895Syz147064 		/*
316*5895Syz147064 		 * Reset the active linkprop of this specific link.
317*5895Syz147064 		 */
318*5895Syz147064 		(void) dladm_init_linkprop(vlanid);
319*5895Syz147064 	}
320*5895Syz147064 
321*5895Syz147064 	(void) close(fd);
322*5895Syz147064 done:
323*5895Syz147064 	*statusp = status;
324*5895Syz147064 	return (DLADM_WALK_CONTINUE);
325*5895Syz147064 }
326*5895Syz147064 
327*5895Syz147064 /*
328*5895Syz147064  * Bring up one VLAN, or all persistent VLANs.  In the latter case, the
329*5895Syz147064  * walk may terminate early if bringup of a VLAN fails.
330*5895Syz147064  */
331*5895Syz147064 dladm_status_t
332*5895Syz147064 dladm_vlan_up(datalink_id_t linkid)
333*5895Syz147064 {
334*5895Syz147064 	dladm_status_t	status;
335*5895Syz147064 
336*5895Syz147064 	if (linkid == DATALINK_ALL_LINKID) {
337*5895Syz147064 		(void) dladm_walk_datalink_id(i_dladm_vlan_up, &status,
338*5895Syz147064 		    DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE,
339*5895Syz147064 		    DLADM_OPT_PERSIST);
340*5895Syz147064 		return (DLADM_STATUS_OK);
341*5895Syz147064 	} else {
342*5895Syz147064 		(void) i_dladm_vlan_up(linkid, &status);
343*5895Syz147064 		return (status);
344*5895Syz147064 	}
345*5895Syz147064 }
346