xref: /illumos-gate/usr/src/lib/libdladm/common/libdlsim.c (revision cd3e933325e68e23516a196a8fea7f49b1e497c3)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <string.h>
28 #include <strings.h>
29 #include <sys/mac.h>
30 #include <sys/dls_mgmt.h>
31 #include <sys/dlpi.h>
32 #include <net/simnet.h>
33 #include <errno.h>
34 #include <unistd.h>
35 
36 #include <libdladm_impl.h>
37 #include <libdllink.h>
38 #include <libdlaggr.h>
39 #include <libdlsim.h>
40 
41 static dladm_status_t dladm_simnet_persist_conf(dladm_handle_t, const char *,
42     dladm_simnet_attr_t *);
43 
44 /* New simnet instance creation */
45 static dladm_status_t
46 i_dladm_create_simnet(dladm_handle_t handle, dladm_simnet_attr_t *attrp)
47 {
48 	int rc;
49 	dladm_status_t status = DLADM_STATUS_OK;
50 	simnet_ioc_create_t ioc;
51 
52 	bzero(&ioc, sizeof (ioc));
53 	ioc.sic_link_id = attrp->sna_link_id;
54 	ioc.sic_type = attrp->sna_type;
55 	if (attrp->sna_mac_len > 0 && attrp->sna_mac_len <= MAXMACADDRLEN) {
56 		ioc.sic_mac_len = attrp->sna_mac_len;
57 		bcopy(attrp->sna_mac_addr, ioc.sic_mac_addr, ioc.sic_mac_len);
58 	}
59 
60 	rc = ioctl(dladm_dld_fd(handle), SIMNET_IOC_CREATE, &ioc);
61 	if (rc < 0)
62 		status = dladm_errno2status(errno);
63 
64 	if (status != DLADM_STATUS_OK)
65 		return (status);
66 
67 	bcopy(ioc.sic_mac_addr, attrp->sna_mac_addr, MAXMACADDRLEN);
68 	attrp->sna_mac_len = ioc.sic_mac_len;
69 	return (status);
70 }
71 
72 /* Modify existing simnet instance */
73 static dladm_status_t
74 i_dladm_modify_simnet(dladm_handle_t handle, dladm_simnet_attr_t *attrp)
75 {
76 	int rc;
77 	dladm_status_t status = DLADM_STATUS_OK;
78 	simnet_ioc_modify_t ioc;
79 
80 	bzero(&ioc, sizeof (ioc));
81 	ioc.sim_link_id = attrp->sna_link_id;
82 	ioc.sim_peer_link_id = attrp->sna_peer_link_id;
83 
84 	rc = ioctl(dladm_dld_fd(handle), SIMNET_IOC_MODIFY, &ioc);
85 	if (rc < 0)
86 		status = dladm_errno2status(errno);
87 
88 	return (status);
89 }
90 
91 /* Delete simnet instance */
92 static dladm_status_t
93 i_dladm_delete_simnet(dladm_handle_t handle, dladm_simnet_attr_t *attrp)
94 {
95 	int rc;
96 	dladm_status_t status = DLADM_STATUS_OK;
97 	simnet_ioc_delete_t ioc;
98 
99 	bzero(&ioc, sizeof (ioc));
100 	ioc.sid_link_id = attrp->sna_link_id;
101 
102 	rc = ioctl(dladm_dld_fd(handle), SIMNET_IOC_DELETE, &ioc);
103 	if (rc < 0)
104 		status = dladm_errno2status(errno);
105 
106 	return (status);
107 }
108 
109 /* Retrieve simnet instance information */
110 static dladm_status_t
111 i_dladm_get_simnet_info(dladm_handle_t handle, dladm_simnet_attr_t *attrp)
112 {
113 	int rc;
114 	dladm_status_t status = DLADM_STATUS_OK;
115 	simnet_ioc_info_t ioc;
116 
117 	bzero(&ioc, sizeof (ioc));
118 	ioc.sii_link_id = attrp->sna_link_id;
119 
120 	rc = ioctl(dladm_dld_fd(handle), SIMNET_IOC_INFO, &ioc);
121 	if (rc < 0) {
122 		status = dladm_errno2status(errno);
123 		return (status);
124 	}
125 
126 	bcopy(ioc.sii_mac_addr, attrp->sna_mac_addr, MAXMACADDRLEN);
127 	attrp->sna_mac_len = ioc.sii_mac_len;
128 	attrp->sna_peer_link_id = ioc.sii_peer_link_id;
129 	attrp->sna_type = ioc.sii_type;
130 	return (status);
131 }
132 
133 /* Retrieve simnet configuratin */
134 static dladm_status_t
135 i_dladm_get_simnet_info_persist(dladm_handle_t handle,
136     dladm_simnet_attr_t *attrp)
137 {
138 	dladm_conf_t conf;
139 	dladm_status_t status;
140 	char macstr[ETHERADDRL * 3];
141 	char simnetpeer[MAXLINKNAMELEN];
142 	uint64_t u64;
143 	boolean_t mac_fixed;
144 
145 	if ((status = dladm_read_conf(handle, attrp->sna_link_id, &conf)) !=
146 	    DLADM_STATUS_OK)
147 		return (status);
148 
149 	status = dladm_get_conf_field(handle, conf, FSIMNETTYPE, &u64,
150 	    sizeof (u64));
151 	if (status != DLADM_STATUS_OK)
152 		goto done;
153 	attrp->sna_type = (uint_t)u64;
154 
155 	status = dladm_get_conf_field(handle, conf, FMADDRLEN, &u64,
156 	    sizeof (u64));
157 	if (status != DLADM_STATUS_OK)
158 		goto done;
159 	attrp->sna_mac_len = (uint_t)u64;
160 
161 	status = dladm_get_conf_field(handle, conf, FMACADDR, macstr,
162 	    sizeof (macstr));
163 	if (status != DLADM_STATUS_OK)
164 		goto done;
165 	(void) dladm_aggr_str2macaddr(macstr, &mac_fixed, attrp->sna_mac_addr);
166 
167 	/* Peer field is optional and only set when peer is attached */
168 	if (dladm_get_conf_field(handle, conf, FSIMNETPEER, simnetpeer,
169 	    sizeof (simnetpeer)) == DLADM_STATUS_OK) {
170 		status = dladm_name2info(handle, simnetpeer,
171 		    &attrp->sna_peer_link_id, NULL, NULL, NULL);
172 	} else {
173 		attrp->sna_peer_link_id = DATALINK_INVALID_LINKID;
174 	}
175 done:
176 	dladm_destroy_conf(handle, conf);
177 	return (status);
178 }
179 
180 dladm_status_t
181 dladm_simnet_create(dladm_handle_t handle, const char *simnetname,
182     uint_t media, uint32_t flags)
183 {
184 	datalink_id_t simnet_id;
185 	dladm_status_t status;
186 	dladm_simnet_attr_t attr;
187 
188 	if (!(flags & DLADM_OPT_ACTIVE))
189 		return (DLADM_STATUS_NOTSUP);
190 
191 	flags &= (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
192 	if ((status = dladm_create_datalink_id(handle, simnetname,
193 	    DATALINK_CLASS_SIMNET, media, flags,
194 	    &simnet_id)) != DLADM_STATUS_OK)
195 		return (status);
196 
197 	bzero(&attr, sizeof (attr));
198 	attr.sna_link_id = simnet_id;
199 	attr.sna_type = media;
200 	status = i_dladm_create_simnet(handle, &attr);
201 	if (status != DLADM_STATUS_OK)
202 		goto done;
203 
204 	if (!(flags & DLADM_OPT_PERSIST))
205 		goto done;
206 
207 	status = dladm_simnet_persist_conf(handle, simnetname, &attr);
208 	if (status != DLADM_STATUS_OK) {
209 		(void) i_dladm_delete_simnet(handle, &attr);
210 		goto done;
211 	}
212 
213 	(void) dladm_set_linkprop(handle, simnet_id, NULL, NULL, 0, flags);
214 
215 done:
216 	if (status != DLADM_STATUS_OK) {
217 		(void) dladm_destroy_datalink_id(handle, simnet_id, flags);
218 	}
219 	return (status);
220 }
221 
222 /* Update existing simnet configuration */
223 static dladm_status_t
224 i_dladm_simnet_update_conf(dladm_handle_t handle, datalink_id_t simnet_id,
225     datalink_id_t peer_simnet_id)
226 {
227 	dladm_status_t status;
228 	dladm_conf_t conf;
229 	char simnetpeer[MAXLINKNAMELEN];
230 
231 	status = dladm_read_conf(handle, simnet_id, &conf);
232 	if (status != DLADM_STATUS_OK)
233 		return (status);
234 
235 	/* First clear previous peer if any in configuration */
236 	(void) dladm_unset_conf_field(handle, conf, FSIMNETPEER);
237 	if (peer_simnet_id != DATALINK_INVALID_LINKID) {
238 		if ((status = dladm_datalink_id2info(handle,
239 		    peer_simnet_id, NULL, NULL, NULL, simnetpeer,
240 		    sizeof (simnetpeer))) == DLADM_STATUS_OK) {
241 			status = dladm_set_conf_field(handle, conf,
242 			    FSIMNETPEER, DLADM_TYPE_STR, simnetpeer);
243 		}
244 		if (status != DLADM_STATUS_OK)
245 			goto fail;
246 	}
247 
248 	status = dladm_write_conf(handle, conf);
249 fail:
250 	dladm_destroy_conf(handle, conf);
251 	return (status);
252 }
253 
254 /* Modify attached simnet peer */
255 dladm_status_t
256 dladm_simnet_modify(dladm_handle_t handle, datalink_id_t simnet_id,
257     datalink_id_t peer_simnet_id, uint32_t flags)
258 {
259 	dladm_simnet_attr_t attr;
260 	dladm_simnet_attr_t prevattr;
261 	dladm_status_t status;
262 	datalink_class_t class;
263 	uint32_t linkflags;
264 	uint32_t peerlinkflags;
265 
266 	if (!(flags & DLADM_OPT_ACTIVE))
267 		return (DLADM_STATUS_NOTSUP);
268 
269 	if ((dladm_datalink_id2info(handle, simnet_id, &linkflags, &class,
270 	    NULL, NULL, 0) != DLADM_STATUS_OK))
271 		return (DLADM_STATUS_BADARG);
272 	if (class != DATALINK_CLASS_SIMNET)
273 		return (DLADM_STATUS_BADARG);
274 
275 	if (peer_simnet_id != DATALINK_INVALID_LINKID) {
276 		if (dladm_datalink_id2info(handle, peer_simnet_id,
277 		    &peerlinkflags, &class, NULL, NULL, 0) != DLADM_STATUS_OK)
278 			return (DLADM_STATUS_BADARG);
279 		if (class != DATALINK_CLASS_SIMNET)
280 			return (DLADM_STATUS_BADARG);
281 		/* Check to ensure the peer link has identical flags */
282 		if (peerlinkflags != linkflags)
283 			return (DLADM_STATUS_BADARG);
284 	}
285 
286 	/* Retrieve previous attrs before modification */
287 	bzero(&prevattr, sizeof (prevattr));
288 	if ((status = dladm_simnet_info(handle, simnet_id, &prevattr,
289 	    flags)) != DLADM_STATUS_OK)
290 		return (status);
291 
292 	bzero(&attr, sizeof (attr));
293 	attr.sna_link_id = simnet_id;
294 	attr.sna_peer_link_id = peer_simnet_id;
295 	status = i_dladm_modify_simnet(handle, &attr);
296 	if ((status != DLADM_STATUS_OK) || !(flags & DLADM_OPT_PERSIST))
297 		return (status);
298 
299 	/* First we clear link's existing peer field in config */
300 	status = i_dladm_simnet_update_conf(handle, simnet_id,
301 	    DATALINK_INVALID_LINKID);
302 	if (status != DLADM_STATUS_OK)
303 		return (status);
304 
305 	/* Clear the previous peer link's existing peer field in config */
306 	if (prevattr.sna_peer_link_id != DATALINK_INVALID_LINKID) {
307 		status = i_dladm_simnet_update_conf(handle,
308 		    prevattr.sna_peer_link_id, DATALINK_INVALID_LINKID);
309 		if (status != DLADM_STATUS_OK)
310 			return (status);
311 	}
312 
313 	/* Update the configuration in both simnets with any new peer link */
314 	if (peer_simnet_id != DATALINK_INVALID_LINKID) {
315 		status = i_dladm_simnet_update_conf(handle, simnet_id,
316 		    peer_simnet_id);
317 		if (status == DLADM_STATUS_OK)
318 			status = i_dladm_simnet_update_conf(handle,
319 			    peer_simnet_id, simnet_id);
320 	}
321 
322 	return (status);
323 }
324 
325 dladm_status_t
326 dladm_simnet_delete(dladm_handle_t handle, datalink_id_t simnet_id,
327     uint32_t flags)
328 {
329 	dladm_simnet_attr_t attr;
330 	dladm_simnet_attr_t prevattr;
331 	dladm_status_t status;
332 	datalink_class_t class;
333 
334 	if ((dladm_datalink_id2info(handle, simnet_id, NULL, &class,
335 	    NULL, NULL, 0) != DLADM_STATUS_OK))
336 		return (DLADM_STATUS_BADARG);
337 
338 	if (class != DATALINK_CLASS_SIMNET)
339 		return (DLADM_STATUS_BADARG);
340 
341 	/* Check current simnet attributes before deletion */
342 	flags &= (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
343 	bzero(&prevattr, sizeof (prevattr));
344 	if ((status = dladm_simnet_info(handle, simnet_id, &prevattr,
345 	    flags)) != DLADM_STATUS_OK)
346 		return (status);
347 
348 	bzero(&attr, sizeof (attr));
349 	attr.sna_link_id = simnet_id;
350 	if (flags & DLADM_OPT_ACTIVE) {
351 		status = i_dladm_delete_simnet(handle, &attr);
352 		if (status == DLADM_STATUS_OK) {
353 			(void) dladm_set_linkprop(handle, simnet_id, NULL,
354 			    NULL, 0, DLADM_OPT_ACTIVE);
355 			(void) dladm_destroy_datalink_id(handle, simnet_id,
356 			    DLADM_OPT_ACTIVE);
357 		} else if (status != DLADM_STATUS_NOTFOUND) {
358 			return (status);
359 		}
360 	}
361 
362 	if (flags & DLADM_OPT_PERSIST) {
363 		(void) dladm_remove_conf(handle, simnet_id);
364 		(void) dladm_destroy_datalink_id(handle, simnet_id,
365 		    DLADM_OPT_PERSIST);
366 
367 		/* Update any attached peer configuration */
368 		if (prevattr.sna_peer_link_id != DATALINK_INVALID_LINKID)
369 			status = i_dladm_simnet_update_conf(handle,
370 			    prevattr.sna_peer_link_id, DATALINK_INVALID_LINKID);
371 	}
372 	return (status);
373 }
374 
375 /* Retrieve simnet information either active or from configuration */
376 dladm_status_t
377 dladm_simnet_info(dladm_handle_t handle, datalink_id_t simnet_id,
378     dladm_simnet_attr_t *attrp, uint32_t flags)
379 {
380 	datalink_class_t class;
381 	dladm_status_t status;
382 
383 	if ((dladm_datalink_id2info(handle, simnet_id, NULL, &class,
384 	    NULL, NULL, 0) != DLADM_STATUS_OK))
385 		return (DLADM_STATUS_BADARG);
386 
387 	if (class != DATALINK_CLASS_SIMNET)
388 		return (DLADM_STATUS_BADARG);
389 
390 	bzero(attrp, sizeof (attrp));
391 	attrp->sna_link_id = simnet_id;
392 
393 	if (flags & DLADM_OPT_ACTIVE) {
394 		status = i_dladm_get_simnet_info(handle, attrp);
395 		/*
396 		 * If no active simnet found then return any simnet
397 		 * from stored config if requested.
398 		 */
399 		if (status == DLADM_STATUS_NOTFOUND &&
400 		    (flags & DLADM_OPT_PERSIST))
401 			return (i_dladm_get_simnet_info_persist(handle, attrp));
402 		return (status);
403 	} else if (flags & DLADM_OPT_PERSIST) {
404 		return (i_dladm_get_simnet_info_persist(handle, attrp));
405 	} else {
406 		return (DLADM_STATUS_BADARG);
407 	}
408 }
409 
410 /* Bring up simnet from stored configuration */
411 static int
412 i_dladm_simnet_up(dladm_handle_t handle, datalink_id_t simnet_id, void *arg)
413 {
414 	dladm_status_t *statusp = arg;
415 	dladm_status_t status;
416 	dladm_simnet_attr_t attr;
417 	dladm_simnet_attr_t peer_attr;
418 
419 	bzero(&attr, sizeof (attr));
420 	attr.sna_link_id = simnet_id;
421 	status = dladm_simnet_info(handle, simnet_id, &attr,
422 	    DLADM_OPT_PERSIST);
423 	if (status != DLADM_STATUS_OK)
424 		goto done;
425 
426 	status = i_dladm_create_simnet(handle, &attr);
427 	if (status != DLADM_STATUS_OK)
428 		goto done;
429 
430 	/*
431 	 * When bringing up check if the peer link is available, if it
432 	 * is then modify the simnet and attach the peer link.
433 	 */
434 	if ((attr.sna_peer_link_id != DATALINK_INVALID_LINKID) &&
435 	    (dladm_simnet_info(handle, attr.sna_peer_link_id, &peer_attr,
436 	    DLADM_OPT_ACTIVE) == DLADM_STATUS_OK)) {
437 		status = i_dladm_modify_simnet(handle, &attr);
438 		if (status != DLADM_STATUS_OK)
439 			goto done;
440 	}
441 
442 	if ((status = dladm_up_datalink_id(handle, simnet_id)) !=
443 	    DLADM_STATUS_OK) {
444 		(void) dladm_simnet_delete(handle, simnet_id,
445 		    DLADM_OPT_PERSIST);
446 		goto done;
447 	}
448 done:
449 	*statusp = status;
450 	return (DLADM_WALK_CONTINUE);
451 }
452 
453 /* Bring up simnet instance(s) from configuration */
454 /* ARGSUSED */
455 dladm_status_t
456 dladm_simnet_up(dladm_handle_t handle, datalink_id_t simnet_id,
457     uint32_t flags)
458 {
459 	dladm_status_t status;
460 
461 	if (simnet_id == DATALINK_ALL_LINKID) {
462 		(void) dladm_walk_datalink_id(i_dladm_simnet_up, handle,
463 		    &status, DATALINK_CLASS_SIMNET, DATALINK_ANY_MEDIATYPE,
464 		    DLADM_OPT_PERSIST);
465 		return (DLADM_STATUS_OK);
466 	} else {
467 		(void) i_dladm_simnet_up(handle, simnet_id, &status);
468 		return (status);
469 	}
470 }
471 
472 /* Store simnet configuration */
473 static dladm_status_t
474 dladm_simnet_persist_conf(dladm_handle_t handle, const char *name,
475     dladm_simnet_attr_t *attrp)
476 {
477 	dladm_conf_t conf = DLADM_INVALID_CONF;
478 	dladm_status_t status;
479 	char mstr[ETHERADDRL * 3];
480 	uint64_t u64;
481 
482 	if ((status = dladm_create_conf(handle, name, attrp->sna_link_id,
483 	    DATALINK_CLASS_SIMNET, attrp->sna_type, &conf)) != DLADM_STATUS_OK)
484 		return (status);
485 
486 	status = dladm_set_conf_field(handle, conf, FMACADDR,
487 	    DLADM_TYPE_STR, dladm_aggr_macaddr2str(attrp->sna_mac_addr, mstr));
488 	if (status != DLADM_STATUS_OK)
489 		goto done;
490 
491 	u64 = attrp->sna_type;
492 	status = dladm_set_conf_field(handle, conf, FSIMNETTYPE,
493 	    DLADM_TYPE_UINT64, &u64);
494 	if (status != DLADM_STATUS_OK)
495 		goto done;
496 
497 	u64 = attrp->sna_mac_len;
498 	status = dladm_set_conf_field(handle, conf, FMADDRLEN,
499 	    DLADM_TYPE_UINT64, &u64);
500 	if (status != DLADM_STATUS_OK)
501 		goto done;
502 
503 	status = dladm_write_conf(handle, conf);
504 done:
505 	dladm_destroy_conf(handle, conf);
506 	return (status);
507 }
508