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