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 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 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 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 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 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 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 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 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 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 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 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 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 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