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