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