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