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