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 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2018 Joyent, Inc. 25 * Copyright 2016 Argo Technologie SA. 26 * Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>. 27 * Copyright 2021 Tintri by DDN, Inc. All rights reserved. 28 */ 29 30 /* 31 * Contains DB walker functions, which are of type `db_wfunc_t'; 32 * 33 * typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf, 34 * size_t bufsize, int *errp); 35 * 36 * ipadm_rw_db() walks through the data store, one line at a time and calls 37 * these call back functions with: 38 * `cbarg' - callback argument 39 * `db_nvl' - representing a line from DB in nvlist_t form 40 * `buf' - character buffer to hold modified line 41 * `bufsize'- size of the buffer 42 * `errp' - captures any error inside the walker function. 43 * 44 * All the 'write' callback functions modify `db_nvl' based on `cbarg' and 45 * copy string representation of `db_nvl' (using ipadm_nvlist2str()) into `buf'. 46 * To delete a line from the DB, buf[0] is set to `\0'. Inside ipadm_rw_db(), 47 * the modified `buf' is written back into DB. 48 * 49 * All the 'read' callback functions, retrieve the information from the DB, by 50 * reading `db_nvl' and then populate the `cbarg'. 51 */ 52 53 #include <stdlib.h> 54 #include <strings.h> 55 #include <errno.h> 56 #include <assert.h> 57 #include <sys/types.h> 58 #include <sys/socket.h> 59 #include <netinet/in.h> 60 #include <arpa/inet.h> 61 #include <unistd.h> 62 #include "ipmgmt_impl.h" 63 64 /* SCF related property group names and property names */ 65 #define IPMGMTD_APP_PG "ipmgmtd" 66 #define IPMGMTD_PROP_FBD "first_boot_done" 67 #define IPMGMTD_PROP_DBVER "datastore_version" 68 #define IPMGMTD_TRUESTR "true" 69 70 #define ATYPE "_atype" /* name of the address type nvpair */ 71 #define FLAGS "_flags" /* name of the flags nvpair */ 72 73 /* 74 * flag used by ipmgmt_persist_aobjmap() to indicate address type is 75 * IPADM_ADDR_IPV6_ADDRCONF. 76 */ 77 #define IPMGMT_ATYPE_V6ACONF 0x1 78 79 extern pthread_rwlock_t ipmgmt_dbconf_lock; 80 81 /* signifies whether volatile copy of data store is in use */ 82 static boolean_t ipmgmt_rdonly_root = B_FALSE; 83 84 typedef int ipmgmt_if_updater_func_t(nvlist_t *, nvpair_t *, uint_t); 85 86 static ipmgmt_if_updater_func_t ipmgmt_if_family_updater; 87 static ipmgmt_if_updater_func_t ipmgmt_if_groupmembers_updater; 88 89 static int ipmgmt_get_ifinfo_nvl(const char *ifname, nvlist_t **if_info_nvl); 90 91 typedef struct { 92 const char *name; 93 ipmgmt_if_updater_func_t *func; 94 } ipmgmt_if_updater_ent_t; 95 96 static ipmgmt_if_updater_ent_t ipmgmt_if_updater_ent[] = { 97 {IPADM_NVP_FAMILIES, ipmgmt_if_family_updater}, 98 {IPADM_NVP_MIFNAMES, ipmgmt_if_groupmembers_updater}, 99 {NULL, NULL} 100 }; 101 102 static ipmgmt_if_updater_ent_t * 103 ipmgmt_find_if_field_updater(const char *field_name) 104 { 105 int i; 106 107 for (i = 0; ipmgmt_if_updater_ent[i].name != NULL; i++) { 108 if (strcmp(field_name, ipmgmt_if_updater_ent[i].name) == 0) { 109 break; 110 } 111 } 112 113 return (&ipmgmt_if_updater_ent[i]); 114 } 115 116 static int 117 ipmgmt_if_groupmembers_updater(nvlist_t *db_nvl, nvpair_t *member_nvp, 118 uint_t flags) 119 { 120 char **members; 121 char *member; 122 char **out_members; 123 uint_t nelem = 0, cnt = 0; 124 int err; 125 126 if ((err = nvpair_value_string(member_nvp, &member)) != 0) 127 return (err); 128 129 err = nvlist_lookup_string_array(db_nvl, IPADM_NVP_MIFNAMES, 130 &members, &nelem); 131 132 if (err != 0 && (flags & IPMGMT_REMOVE)) 133 return (ENOENT); 134 135 /* 136 * Reserve one extra slot for IPMGMT_APPEND. 137 * Probably not worth conditionalizing. 138 */ 139 out_members = calloc(nelem + 1, sizeof (char *)); 140 if (out_members == NULL) 141 return (ENOMEM); 142 143 while (nelem-- > 0) { 144 if ((flags & IPMGMT_REMOVE) && 145 (strcmp(member, members[nelem]) == 0)) 146 continue; 147 148 if ((out_members[cnt] = strdup(members[nelem])) == NULL) { 149 err = ENOMEM; 150 goto fail; 151 } 152 153 cnt++; 154 } 155 156 if (flags & IPMGMT_APPEND) { 157 if ((out_members[cnt] = strdup(member)) == NULL) { 158 err = ENOMEM; 159 goto fail; 160 } 161 cnt++; 162 } 163 164 if (cnt == 0) { 165 err = nvlist_remove(db_nvl, IPADM_NVP_MIFNAMES, 166 DATA_TYPE_STRING_ARRAY); 167 } else { 168 err = nvlist_add_string_array(db_nvl, IPADM_NVP_MIFNAMES, 169 out_members, cnt); 170 } 171 172 fail: 173 while (cnt--) 174 free(out_members[cnt]); 175 176 free(out_members); 177 178 return (err); 179 } 180 181 static int 182 ipmgmt_if_family_updater(nvlist_t *db_nvl, nvpair_t *families_nvp, uint_t flags) 183 { 184 uint16_t *families; 185 uint_t nelem = 0; 186 int err; 187 188 if ((err = nvpair_value_uint16_array(families_nvp, &families, 189 &nelem)) != 0) 190 return (err); 191 192 return (ipmgmt_update_family_nvp(db_nvl, families[0], flags)); 193 } 194 195 int 196 ipmgmt_update_family_nvp(nvlist_t *nvl, sa_family_t af, uint_t flags) 197 { 198 uint16_t *families = NULL; 199 uint16_t out_families[2]; 200 uint_t nelem = 0, cnt; 201 int err; 202 203 err = nvlist_lookup_uint16_array(nvl, IPADM_NVP_FAMILIES, 204 &families, &nelem); 205 if (err != 0 && (flags & IPMGMT_REMOVE)) { 206 return (ENOENT); 207 } 208 209 if (flags & IPMGMT_APPEND) { 210 if (families != NULL) { 211 if (nelem == 2 || families[0] == af) { 212 return (EEXIST); 213 } 214 out_families[0] = families[0]; 215 out_families[1] = af; 216 cnt = 2; 217 } else { 218 out_families[0] = af; 219 cnt = 1; 220 } 221 } else { 222 assert(nelem == 1 || nelem == 2); 223 cnt = 0; 224 while (nelem-- > 0) { 225 if (families[nelem] != af) { 226 out_families[cnt] = families[nelem]; 227 cnt++; 228 } 229 } 230 } 231 232 if (cnt != 0) { 233 return (nvlist_add_uint16_array(nvl, IPADM_NVP_FAMILIES, 234 out_families, cnt)); 235 } 236 return (nvlist_remove(nvl, IPADM_NVP_FAMILIES, DATA_TYPE_UINT16_ARRAY)); 237 } 238 239 /* 240 * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed 241 * in private nvpairs `proto', `ifname' & `aobjname'. 242 */ 243 static boolean_t 244 ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname, 245 const char *aobjname) 246 { 247 char *db_proto = NULL, *db_ifname = NULL; 248 char *db_aobjname = NULL; 249 nvpair_t *nvp; 250 char *name; 251 252 /* walk through db_nvl and retrieve all its private nvpairs */ 253 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 254 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 255 name = nvpair_name(nvp); 256 if (strcmp(IPADM_NVP_PROTONAME, name) == 0) 257 (void) nvpair_value_string(nvp, &db_proto); 258 else if (strcmp(IPADM_NVP_IFNAME, name) == 0) 259 (void) nvpair_value_string(nvp, &db_ifname); 260 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) 261 (void) nvpair_value_string(nvp, &db_aobjname); 262 } 263 264 if (proto != NULL && proto[0] == '\0') 265 proto = NULL; 266 if (ifname != NULL && ifname[0] == '\0') 267 ifname = NULL; 268 if (aobjname != NULL && aobjname[0] == '\0') 269 aobjname = NULL; 270 271 if ((proto == NULL && db_proto != NULL) || 272 (proto != NULL && db_proto == NULL) || 273 (proto != NULL && db_proto != NULL && 274 strcmp(proto, db_proto) != 0)) { 275 /* no intersection - different protocols. */ 276 return (B_FALSE); 277 } 278 if ((ifname == NULL && db_ifname != NULL) || 279 (ifname != NULL && db_ifname == NULL) || 280 (ifname != NULL && db_ifname != NULL && 281 strcmp(ifname, db_ifname) != 0)) { 282 /* no intersection - different interfaces. */ 283 return (B_FALSE); 284 } 285 if ((aobjname == NULL && db_aobjname != NULL) || 286 (aobjname != NULL && db_aobjname == NULL) || 287 (aobjname != NULL && db_aobjname != NULL && 288 strcmp(aobjname, db_aobjname) != 0)) { 289 /* no intersection - different address objects */ 290 return (B_FALSE); 291 } 292 293 return (B_TRUE); 294 } 295 296 /* 297 * Checks if the database nvl, `db_nvl' and the input nvl, `in_nvl' intersects. 298 */ 299 static boolean_t 300 ipmgmt_nvlist_intersects(nvlist_t *db_nvl, nvlist_t *in_nvl) 301 { 302 nvpair_t *nvp; 303 char *name; 304 char *proto = NULL, *ifname = NULL, *aobjname = NULL; 305 306 /* walk through in_nvl and retrieve all its private nvpairs */ 307 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL; 308 nvp = nvlist_next_nvpair(in_nvl, nvp)) { 309 name = nvpair_name(nvp); 310 if (strcmp(IPADM_NVP_PROTONAME, name) == 0) 311 (void) nvpair_value_string(nvp, &proto); 312 else if (strcmp(IPADM_NVP_IFNAME, name) == 0) 313 (void) nvpair_value_string(nvp, &ifname); 314 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) 315 (void) nvpair_value_string(nvp, &aobjname); 316 } 317 318 return (ipmgmt_nvlist_match(db_nvl, proto, ifname, aobjname)); 319 } 320 321 /* 322 * Checks if the database nvl, `db_nvl', contains and matches ANY of the passed 323 * in private nvpairs `proto', `ifname' & `aobjname'. 324 */ 325 static boolean_t 326 ipmgmt_nvlist_contains(nvlist_t *db_nvl, const char *proto, 327 const char *ifname, char *aobjname) 328 { 329 char *db_ifname = NULL, *db_proto = NULL; 330 char *db_aobjname = NULL; 331 nvpair_t *nvp; 332 char *name; 333 334 /* walk through db_nvl and retrieve all private nvpairs */ 335 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 336 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 337 name = nvpair_name(nvp); 338 if (strcmp(IPADM_NVP_PROTONAME, name) == 0) 339 (void) nvpair_value_string(nvp, &db_proto); 340 else if (strcmp(IPADM_NVP_IFNAME, name) == 0) 341 (void) nvpair_value_string(nvp, &db_ifname); 342 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) 343 (void) nvpair_value_string(nvp, &db_aobjname); 344 } 345 346 if (proto != NULL && proto[0] != '\0') { 347 if ((db_proto == NULL || strcmp(proto, db_proto) != 0)) 348 return (B_FALSE); 349 } 350 if (ifname != NULL && ifname[0] != '\0') { 351 if ((db_ifname == NULL || strcmp(ifname, db_ifname) != 0)) 352 return (B_FALSE); 353 } 354 if (aobjname != NULL && aobjname[0] != '\0') { 355 if ((db_aobjname == NULL || strcmp(aobjname, db_aobjname) != 0)) 356 return (B_FALSE); 357 } 358 359 return (B_TRUE); 360 } 361 362 /* 363 * Retrieves the property value from the DB. The property whose value is to be 364 * retrieved is in `pargp->ia_pname'. 365 */ 366 /* ARGSUSED */ 367 boolean_t 368 ipmgmt_db_getprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 369 int *errp) 370 { 371 ipmgmt_prop_arg_t *pargp = arg; 372 boolean_t cont = B_TRUE; 373 char *pval; 374 int err = 0; 375 376 *errp = 0; 377 378 if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module, 379 pargp->ia_ifname, pargp->ia_aobjname)) 380 return (B_TRUE); 381 382 if ((err = nvlist_lookup_string(db_nvl, pargp->ia_pname, 383 &pval)) == 0) { 384 (void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval)); 385 /* 386 * We have retrieved what we are looking for. 387 * Stop the walker. 388 */ 389 cont = B_FALSE; 390 } else { 391 if (err == ENOENT) 392 err = 0; 393 *errp = err; 394 } 395 396 return (cont); 397 } 398 399 /* 400 * Removes the property value from the DB. The property whose value is to be 401 * removed is in `pargp->ia_pname'. 402 */ 403 /* ARGSUSED */ 404 boolean_t 405 ipmgmt_db_resetprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 406 int *errp) 407 { 408 ipmgmt_prop_arg_t *pargp = arg; 409 410 *errp = 0; 411 if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module, 412 pargp->ia_ifname, pargp->ia_aobjname)) 413 return (B_TRUE); 414 415 if (!nvlist_exists(db_nvl, pargp->ia_pname)) 416 return (B_TRUE); 417 418 /* 419 * We found the property in the DB. If IPMGMT_REMOVE is not set then 420 * delete the entry from the db. If it is set, then the property is a 421 * multi-valued property so just remove the specified values from DB. 422 */ 423 if (pargp->ia_flags & IPMGMT_REMOVE) { 424 char *dbpval = NULL; 425 char *inpval = pargp->ia_pval; 426 char pval[MAXPROPVALLEN]; 427 char *val, *lasts; 428 429 *errp = nvlist_lookup_string(db_nvl, pargp->ia_pname, &dbpval); 430 if (*errp != 0) 431 return (B_FALSE); 432 433 /* 434 * multi-valued properties are represented as comma separated 435 * values. Use string tokenizer functions to split them and 436 * search for the value to be removed. 437 */ 438 bzero(pval, sizeof (pval)); 439 if ((val = strtok_r(dbpval, ",", &lasts)) != NULL) { 440 if (strcmp(val, inpval) != 0) 441 (void) strlcat(pval, val, MAXPROPVALLEN); 442 while ((val = strtok_r(NULL, ",", &lasts)) != NULL) { 443 if (strcmp(val, inpval) != 0) { 444 if (pval[0] != '\0') 445 (void) strlcat(pval, ",", 446 MAXPROPVALLEN); 447 (void) strlcat(pval, val, 448 MAXPROPVALLEN); 449 } 450 } 451 } else { 452 if (strcmp(dbpval, inpval) != 0) 453 *errp = ENOENT; 454 else 455 buf[0] = '\0'; 456 return (B_FALSE); 457 } 458 *errp = nvlist_add_string(db_nvl, pargp->ia_pname, pval); 459 if (*errp != 0) 460 return (B_FALSE); 461 462 (void) memset(buf, 0, buflen); 463 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { 464 /* buffer overflow */ 465 *errp = ENOBUFS; 466 } 467 } else { 468 buf[0] = '\0'; 469 } 470 471 /* stop the search */ 472 return (B_FALSE); 473 } 474 475 /* 476 * Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is 477 * found, when one of the following occurs first. 478 * - the input aobjname matches the db aobjname. Return the db address. 479 * - the input interface matches the db interface. Return all the 480 * matching db lines with addresses. 481 */ 482 /* ARGSUSED */ 483 boolean_t 484 ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 485 int *errp) 486 { 487 ipmgmt_get_cbarg_t *cbarg = arg; 488 char *db_aobjname = NULL; 489 char *db_ifname = NULL; 490 nvlist_t *db_addr = NULL; 491 char name[IPMGMT_STRSIZE]; 492 nvpair_t *nvp; 493 boolean_t add_nvl = B_FALSE; 494 495 /* Parse db nvlist */ 496 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 497 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 498 if (nvpair_type(nvp) == DATA_TYPE_NVLIST) 499 (void) nvpair_value_nvlist(nvp, &db_addr); 500 else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0) 501 (void) nvpair_value_string(nvp, &db_ifname); 502 else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0) 503 (void) nvpair_value_string(nvp, &db_aobjname); 504 } 505 506 if (db_aobjname == NULL) /* Not an address */ 507 return (B_TRUE); 508 509 /* Check for a match between the aobjnames or the interface name */ 510 if (cbarg->cb_aobjname[0] != '\0') { 511 if (strcmp(cbarg->cb_aobjname, db_aobjname) == 0) 512 add_nvl = B_TRUE; 513 } else if (cbarg->cb_ifname[0] != '\0') { 514 if (strcmp(cbarg->cb_ifname, db_ifname) == 0) 515 add_nvl = B_TRUE; 516 } else { 517 add_nvl = B_TRUE; 518 } 519 520 if (add_nvl) { 521 (void) snprintf(name, sizeof (name), "%s_%d", db_ifname, 522 cbarg->cb_ocnt); 523 *errp = nvlist_add_nvlist(cbarg->cb_onvl, name, db_nvl); 524 if (*errp == 0) 525 cbarg->cb_ocnt++; 526 } 527 return (B_TRUE); 528 } 529 530 /* 531 * This function only gets called if a volatile filesystem version 532 * of the configuration file has been created. This only happens in the 533 * extremely rare case that a request has been made to update the configuration 534 * file at boottime while the root filesystem was read-only. This is 535 * really a rare occurrence now that we don't support UFS root filesystems 536 * any longer. This function will periodically attempt to write the 537 * configuration back to its location on the root filesystem. Success 538 * will indicate that the filesystem is no longer read-only. 539 */ 540 /* ARGSUSED */ 541 static void * 542 ipmgmt_db_restore_thread(void *arg) 543 { 544 int err; 545 546 for (;;) { 547 (void) sleep(5); 548 (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock); 549 if (!ipmgmt_rdonly_root) 550 break; 551 err = ipmgmt_cpfile(IPADM_VOL_DB_FILE, IPADM_DB_FILE, B_FALSE); 552 if (err == 0) { 553 ipmgmt_rdonly_root = B_FALSE; 554 break; 555 } 556 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock); 557 } 558 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock); 559 return (NULL); 560 } 561 562 /* 563 * This function takes the appropriate lock, read or write, based on the 564 * `db_op' and then calls DB walker ipadm_rw_db(). The code is complicated 565 * by the fact that we are not always guaranteed to have a writable root 566 * filesystem since it is possible that we are reading or writing during 567 * bootime while the root filesystem is still read-only. This is, by far, 568 * the exception case. Normally, this function will be called when the 569 * root filesystem is writable. In the unusual case where this is not 570 * true, the configuration file is copied to the volatile file system 571 * and is updated there until the root filesystem becomes writable. At 572 * that time the file will be moved back to its proper location by 573 * ipmgmt_db_restore_thread(). 574 */ 575 extern int 576 ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op) 577 { 578 int err; 579 boolean_t writeop; 580 mode_t mode; 581 pthread_t tid; 582 pthread_attr_t attr; 583 584 writeop = (db_op != IPADM_DB_READ); 585 if (writeop) { 586 (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock); 587 mode = IPADM_FILE_MODE; 588 } else { 589 (void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock); 590 mode = 0; 591 } 592 593 /* 594 * Did a previous write attempt fail? If so, don't even try to 595 * read/write to IPADM_DB_FILE. 596 */ 597 if (!ipmgmt_rdonly_root) { 598 err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE, 599 mode, db_op); 600 if (err != EROFS) 601 goto done; 602 } 603 604 /* 605 * If we haven't already copied the file to the volatile 606 * file system, do so. This should only happen on a failed 607 * writeop(i.e., we have acquired the write lock above). 608 */ 609 if (access(IPADM_VOL_DB_FILE, F_OK) != 0) { 610 assert(writeop); 611 err = ipmgmt_cpfile(IPADM_DB_FILE, IPADM_VOL_DB_FILE, B_TRUE); 612 if (err != 0) 613 goto done; 614 (void) pthread_attr_init(&attr); 615 (void) pthread_attr_setdetachstate(&attr, 616 PTHREAD_CREATE_DETACHED); 617 (void) pthread_attr_setname_np(&attr, "db_restore"); 618 err = pthread_create(&tid, &attr, ipmgmt_db_restore_thread, 619 NULL); 620 (void) pthread_attr_destroy(&attr); 621 if (err != 0) { 622 (void) unlink(IPADM_VOL_DB_FILE); 623 goto done; 624 } 625 ipmgmt_rdonly_root = B_TRUE; 626 } 627 628 /* 629 * Read/write from the volatile copy. 630 */ 631 err = ipadm_rw_db(db_walk_func, db_warg, IPADM_VOL_DB_FILE, 632 mode, db_op); 633 done: 634 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock); 635 return (err); 636 } 637 638 /* 639 * Used to add an entry towards the end of DB. It just returns B_TRUE for 640 * every line of the DB. When we reach the end, ipadm_rw_db() adds the 641 * line at the end. 642 */ 643 /* ARGSUSED */ 644 boolean_t 645 ipmgmt_db_add(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp) 646 { 647 return (B_TRUE); 648 } 649 650 /* 651 * This function is used to update or create an entry in DB. The nvlist_t, 652 * `in_nvl', represents the line we are looking for. Once we ensure the right 653 * line from DB, we update that entry. 654 */ 655 boolean_t 656 ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 657 int *errp) 658 { 659 ipadm_dbwrite_cbarg_t *cb = arg; 660 uint_t flags = cb->dbw_flags; 661 nvlist_t *in_nvl = cb->dbw_nvl; 662 nvpair_t *nvp; 663 char *name, *instrval = NULL, *dbstrval = NULL; 664 char pval[MAXPROPVALLEN]; 665 666 *errp = 0; 667 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl)) 668 return (B_TRUE); 669 670 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL; 671 nvp = nvlist_next_nvpair(in_nvl, nvp)) { 672 name = nvpair_name(nvp); 673 if (!IPADM_PRIV_NVP(name) && nvlist_exists(db_nvl, name)) 674 break; 675 } 676 677 if (nvp == NULL) 678 return (B_TRUE); 679 680 assert(nvpair_type(nvp) == DATA_TYPE_STRING); 681 682 if ((*errp = nvpair_value_string(nvp, &instrval)) != 0) 683 return (B_FALSE); 684 685 /* 686 * If IPMGMT_APPEND is set then we are dealing with multi-valued 687 * properties. We append to the entry from the db, with the new value. 688 */ 689 if (flags & IPMGMT_APPEND) { 690 if ((*errp = nvlist_lookup_string(db_nvl, name, 691 &dbstrval)) != 0) 692 return (B_FALSE); 693 (void) snprintf(pval, MAXPROPVALLEN, "%s,%s", dbstrval, 694 instrval); 695 if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0) 696 return (B_FALSE); 697 } else { 698 /* case of in-line update of a db entry */ 699 if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0) 700 return (B_FALSE); 701 } 702 703 (void) memset(buf, 0, buflen); 704 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { 705 /* buffer overflow */ 706 *errp = ENOBUFS; 707 } 708 709 /* we updated the DB entry, so do not continue */ 710 return (B_FALSE); 711 } 712 713 /* 714 * This function is used to update a DB line that describes 715 * an interface, its family and group interface 716 * 717 */ 718 boolean_t 719 ipmgmt_db_update_if(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 720 int *errp) 721 { 722 ipadm_dbwrite_cbarg_t *cb = arg; 723 ipmgmt_if_updater_ent_t *updater; 724 nvlist_t *in_nvl = cb->dbw_nvl; 725 uint_t flags = cb->dbw_flags; 726 nvpair_t *nvp; 727 char *name; 728 char *db_ifname; 729 char *gifname = NULL; 730 char *mifname = NULL; 731 732 *errp = 0; 733 734 /* Only one flag */ 735 if ((flags & (IPMGMT_APPEND | IPMGMT_REMOVE)) == 0 || 736 ((flags & IPMGMT_APPEND) && (flags & IPMGMT_REMOVE))) { 737 *errp = EINVAL; 738 return (B_FALSE); 739 } 740 741 if (!nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) 742 return (B_TRUE); 743 744 if (nvlist_exists(db_nvl, IPADM_NVP_IFCLASS) && 745 nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 && 746 nvlist_lookup_string(in_nvl, IPADM_NVP_GIFNAME, &gifname) == 0 && 747 nvlist_lookup_string(in_nvl, IPADM_NVP_MIFNAMES, &mifname) == 0 && 748 strcmp(db_ifname, mifname) == 0) { 749 if (flags & IPMGMT_APPEND) { 750 if ((*errp = nvlist_add_string(db_nvl, 751 IPADM_NVP_GIFNAME, gifname)) != 0) 752 return (B_FALSE); 753 } else { 754 if ((*errp = nvlist_remove(db_nvl, IPADM_NVP_GIFNAME, 755 DATA_TYPE_STRING)) != 0) 756 return (B_FALSE); 757 } 758 cb->dbw_flags &= ~IPMGMT_UPDATE_IPMP; 759 goto done; 760 } 761 762 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl)) 763 return (B_TRUE); 764 765 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL; 766 nvp = nvlist_next_nvpair(in_nvl, nvp)) { 767 name = nvpair_name(nvp); 768 if (strcmp(name, IPADM_NVP_FAMILIES) != 0 && 769 strcmp(name, IPADM_NVP_MIFNAMES) != 0) 770 continue; 771 772 updater = ipmgmt_find_if_field_updater(name); 773 assert(updater != NULL); 774 *errp = (*updater->func)(db_nvl, nvp, flags); 775 if (*errp != 0) 776 return (B_FALSE); 777 } 778 779 cb->dbw_flags &= ~IPMGMT_UPDATE_IF; 780 781 done: 782 (void) memset(buf, 0, buflen); 783 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { 784 *errp = EOVERFLOW; 785 return (B_FALSE); 786 } 787 788 /* we finished all operations, so do not continue */ 789 if ((cb->dbw_flags & (IPMGMT_UPDATE_IF | IPMGMT_UPDATE_IPMP)) == 0) 790 return (B_FALSE); 791 792 return (B_TRUE); 793 } 794 795 /* 796 * For the given `cbarg->cb_ifname' interface, retrieve the nvlist that 797 * represents the persistent interface information. 798 * The nvlist contains: 799 * IPADM_NVP_IFNAME 800 * IPADM_NVP_FAMILIES 801 * IPADM_NVP_IF_CLASS 802 * 803 * (used in 'ipadm show-if') 804 */ 805 /* ARGSUSED */ 806 boolean_t 807 ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 808 int *errp) 809 { 810 ipmgmt_get_cbarg_t *cbarg = arg; 811 char *ifname = cbarg->cb_ifname; 812 nvpair_t *nvp; 813 char *db_ifname = NULL; 814 boolean_t families = B_FALSE; 815 816 /* Parse db nvlist */ 817 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 818 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 819 if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0) { 820 (void) nvpair_value_string(nvp, &db_ifname); 821 } else if (strcmp(nvpair_name(nvp), IPADM_NVP_FAMILIES) == 0) { 822 families = B_TRUE; 823 } 824 } 825 826 if (db_ifname == NULL || !families) 827 return (B_TRUE); 828 829 if (ifname != NULL && ifname[0] != '\0' && 830 strcmp(ifname, db_ifname) != 0) 831 return (B_TRUE); 832 833 *errp = nvlist_add_nvlist(cbarg->cb_onvl, db_ifname, db_nvl); 834 if (*errp == 0) 835 cbarg->cb_ocnt++; 836 837 if (ifname != NULL && ifname[0] != '\0') 838 return (B_FALSE); 839 840 return (B_TRUE); 841 } 842 843 /* 844 * Deletes those entries from the database for which interface name 845 * matches with the given `cbarg->cb_ifname' 846 */ 847 /* ARGSUSED */ 848 boolean_t 849 ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 850 int *errp) 851 { 852 ipmgmt_if_cbarg_t *cbarg = arg; 853 boolean_t isv6 = (cbarg->cb_family == AF_INET6); 854 char *ifname = cbarg->cb_ifname; 855 char *modstr = NULL; 856 char *aobjname; 857 uint_t proto; 858 ipmgmt_aobjmap_t *head; 859 boolean_t aobjfound = B_FALSE; 860 861 *errp = 0; 862 863 if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL)) 864 return (B_TRUE); 865 866 if (nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) { 867 868 if ((*errp = ipmgmt_update_family_nvp(db_nvl, cbarg->cb_family, 869 IPMGMT_REMOVE)) != 0) { 870 return (B_FALSE); 871 } 872 873 if (cbarg->cb_family == AF_INET) { 874 cbarg->cb_ipv4exists = B_FALSE; 875 } else { 876 assert(cbarg->cb_family == AF_INET6); 877 cbarg->cb_ipv6exists = B_FALSE; 878 } 879 if (!nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) { 880 cbarg->cb_ipv4exists = B_FALSE; 881 cbarg->cb_ipv6exists = B_FALSE; 882 goto delete; 883 } 884 /* Otherwise need to reconstruct this string */ 885 (void) memset(buf, 0, buflen); 886 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { 887 /* buffer overflow */ 888 *errp = EOVERFLOW; 889 return (B_FALSE); 890 } 891 return (B_TRUE); 892 } 893 894 /* Reset all the interface configurations for 'ifname' */ 895 if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) || 896 nvlist_exists(db_nvl, IPADM_NVP_INTFID))) { 897 goto delete; 898 } 899 if (!isv6 && 900 (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) || 901 nvlist_exists(db_nvl, IPADM_NVP_DHCP))) { 902 goto delete; 903 } 904 905 if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) { 906 /* 907 * This must be an address property. Delete this 908 * line if there is a match in the address family. 909 */ 910 head = aobjmap.aobjmap_head; 911 while (head != NULL) { 912 if (strcmp(head->am_aobjname, aobjname) == 0) { 913 aobjfound = B_TRUE; 914 if (head->am_family == cbarg->cb_family) 915 goto delete; 916 } 917 head = head->am_next; 918 } 919 /* 920 * If aobjfound = B_FALSE, then this address is not 921 * available in active configuration. We should go ahead 922 * and delete it. 923 */ 924 if (!aobjfound) 925 goto delete; 926 } 927 928 /* 929 * If we are removing both v4 and v6 interface, then we get rid of 930 * all the properties for that interface. On the other hand, if we 931 * are deleting only v4 instance of an interface, then we delete v4 932 * properties only. 933 */ 934 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) { 935 proto = ipadm_str2proto(modstr); 936 switch (proto) { 937 case MOD_PROTO_IPV6: 938 if (isv6) 939 goto delete; 940 break; 941 case MOD_PROTO_IPV4: 942 if (!isv6) 943 goto delete; 944 break; 945 case MOD_PROTO_IP: 946 if (!cbarg->cb_ipv4exists && !cbarg->cb_ipv6exists) 947 goto delete; 948 break; 949 } 950 } 951 /* Not found a match yet. Continue processing the db */ 952 return (B_TRUE); 953 delete: 954 /* delete the line from the db */ 955 buf[0] = '\0'; 956 return (B_TRUE); 957 } 958 959 /* 960 * Deletes those entries from the database for which address object name 961 * matches with the given `cbarg->cb_aobjname' 962 */ 963 /* ARGSUSED */ 964 boolean_t 965 ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 966 int *errp) 967 { 968 ipmgmt_resetaddr_cbarg_t *cbarg = arg; 969 char *aobjname = cbarg->cb_aobjname; 970 971 *errp = 0; 972 if (!ipmgmt_nvlist_contains(db_nvl, NULL, NULL, aobjname)) 973 return (B_TRUE); 974 975 /* delete the line from the db */ 976 buf[0] = '\0'; 977 return (B_TRUE); 978 } 979 980 /* 981 * Retrieves all interface props, including addresses, for given interface(s). 982 * `invl' contains the list of interfaces, for which information need to be 983 * retrieved. 984 */ 985 /* ARGSUSED */ 986 boolean_t 987 ipmgmt_db_initif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 988 int *errp) 989 { 990 ipmgmt_initif_cbarg_t *cbarg = arg; 991 nvlist_t *onvl = cbarg->cb_onvl; 992 nvlist_t *invl = cbarg->cb_invl; 993 sa_family_t in_af = cbarg->cb_family; 994 char *db_ifname; 995 996 *errp = 0; 997 if (nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 && 998 nvlist_exists(invl, db_ifname)) { 999 char name[IPMGMT_STRSIZE]; 1000 sa_family_t db_af = in_af; 1001 uint_t proto; 1002 char *pstr; 1003 1004 if (in_af != AF_UNSPEC) { 1005 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, 1006 &pstr) == 0) { 1007 proto = ipadm_str2proto(pstr); 1008 if (proto == MOD_PROTO_IPV4) 1009 db_af = AF_INET; 1010 else if (proto == MOD_PROTO_IPV6) 1011 db_af = AF_INET6; 1012 else 1013 db_af = in_af; 1014 } else { 1015 if (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) || 1016 nvlist_exists(db_nvl, IPADM_NVP_DHCP)) 1017 db_af = AF_INET; 1018 else 1019 db_af = AF_INET6; 1020 } 1021 } 1022 if (in_af == db_af) { 1023 (void) snprintf(name, sizeof (name), "%s_%d", db_ifname, 1024 cbarg->cb_ocnt); 1025 *errp = nvlist_add_nvlist(onvl, name, db_nvl); 1026 if (*errp == 0) 1027 cbarg->cb_ocnt++; 1028 } 1029 } 1030 return (B_TRUE); 1031 } 1032 1033 /* 1034 * helper function for ipmgmt_aobjmap_op(). Adds the node pointed by `nodep' 1035 * into `aobjmap' structure. 1036 */ 1037 static int 1038 i_ipmgmt_add_amnode(ipmgmt_aobjmap_t *nodep) 1039 { 1040 ipmgmt_aobjmap_t *new, *head; 1041 1042 head = aobjmap.aobjmap_head; 1043 if ((new = malloc(sizeof (ipmgmt_aobjmap_t))) == NULL) 1044 return (ENOMEM); 1045 *new = *nodep; 1046 new->am_next = NULL; 1047 1048 /* Add the node at the beginning of the list */ 1049 if (head == NULL) { 1050 aobjmap.aobjmap_head = new; 1051 } else { 1052 new->am_next = aobjmap.aobjmap_head; 1053 aobjmap.aobjmap_head = new; 1054 } 1055 return (0); 1056 } 1057 1058 /* 1059 * A recursive function to generate alphabetized number given a decimal number. 1060 * Decimal 0 to 25 maps to 'a' to 'z' and then the counting continues with 'aa', 1061 * 'ab', 'ac', et al. 1062 */ 1063 static void 1064 i_ipmgmt_num2priv_aobjname(uint32_t num, char **cp, char *endp) 1065 { 1066 if (num >= 26) 1067 i_ipmgmt_num2priv_aobjname(num / 26 - 1, cp, endp); 1068 if (*cp != endp) { 1069 *cp[0] = 'a' + (num % 26); 1070 (*cp)++; 1071 } 1072 } 1073 1074 /* 1075 * This function generates an `aobjname', when required, and then does 1076 * lookup-add. If `nodep->am_aobjname' is not an empty string, then it walks 1077 * through the `aobjmap' to check if an address object with the same 1078 * `nodep->am_aobjname' exists. If it exists, EEXIST is returned as duplicate 1079 * `aobjname's are not allowed. 1080 * 1081 * If `nodep->am_aobjname' is an empty string then the daemon generates an 1082 * `aobjname' using the `am_nextnum', which contains the next number to be 1083 * used to generate `aobjname'. `am_nextnum' is converted to base26 using 1084 * `a-z' alphabets in i_ipmgmt_num2priv_aobjname(). 1085 * 1086 * `am_nextnum' will be 0 to begin with. Every time an address object that 1087 * needs `aobjname' is added it's incremented by 1. So for the first address 1088 * object on net0 the `am_aobjname' will be net0/_a and `am_nextnum' will be 1. 1089 * For the second address object on that interface `am_aobjname' will be net0/_b 1090 * and `am_nextnum' will incremented to 2. 1091 */ 1092 static int 1093 i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t *nodep) 1094 { 1095 ipmgmt_aobjmap_t *head; 1096 uint32_t nextnum; 1097 1098 for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next) 1099 if (strcmp(head->am_ifname, nodep->am_ifname) == 0) 1100 break; 1101 nextnum = (head == NULL ? 0 : head->am_nextnum); 1102 1103 /* 1104 * if `aobjname' is empty, then the daemon has to generate the 1105 * next `aobjname' for the given interface and family. 1106 */ 1107 if (nodep->am_aobjname[0] == '\0') { 1108 char tmpstr[IPADM_AOBJ_USTRSIZ - 1]; /* 1 for leading '_' */ 1109 char *cp = tmpstr; 1110 char *endp = tmpstr + sizeof (tmpstr); 1111 1112 i_ipmgmt_num2priv_aobjname(nextnum, &cp, endp); 1113 1114 if (cp == endp) 1115 return (EINVAL); 1116 cp[0] = '\0'; 1117 1118 if (snprintf(nodep->am_aobjname, IPADM_AOBJSIZ, "%s/_%s", 1119 nodep->am_ifname, tmpstr) >= IPADM_AOBJSIZ) { 1120 return (EINVAL); 1121 } 1122 nodep->am_nextnum = ++nextnum; 1123 } else { 1124 for (head = aobjmap.aobjmap_head; head != NULL; 1125 head = head->am_next) { 1126 if (strcmp(head->am_aobjname, nodep->am_aobjname) == 0) 1127 return (EEXIST); 1128 } 1129 nodep->am_nextnum = nextnum; 1130 } 1131 return (i_ipmgmt_add_amnode(nodep)); 1132 } 1133 1134 /* 1135 * Performs following operations on the global `aobjmap' linked list. 1136 * (a) ADDROBJ_ADD: add or update address object in `aobjmap' 1137 * (b) ADDROBJ_DELETE: delete address object from `aobjmap' 1138 * (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap' 1139 * (d) ADDROBJ_SETLIFNUM: Sets the lifnum for an address object in `aobjmap' 1140 */ 1141 int 1142 ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op) 1143 { 1144 ipmgmt_aobjmap_t *head, *prev, *matched = NULL; 1145 boolean_t update = B_TRUE; 1146 int err = 0; 1147 ipadm_db_op_t db_op = IPADM_DB_READ; 1148 1149 (void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock); 1150 1151 head = aobjmap.aobjmap_head; 1152 switch (op) { 1153 case ADDROBJ_ADD: 1154 /* 1155 * check for stub nodes (added by ADDROBJ_LOOKUPADD) and 1156 * update, else add the new node. 1157 */ 1158 for (; head != NULL; head = head->am_next) { 1159 /* 1160 * For IPv6, we need to distinguish between the 1161 * linklocal and non-linklocal nodes 1162 */ 1163 if (strcmp(head->am_aobjname, 1164 nodep->am_aobjname) == 0 && 1165 (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF || 1166 head->ipmgmt_am_linklocal == 1167 nodep->ipmgmt_am_linklocal)) 1168 break; 1169 } 1170 1171 if (head != NULL) { 1172 /* update the node */ 1173 (void) strlcpy(head->am_ifname, nodep->am_ifname, 1174 sizeof (head->am_ifname)); 1175 head->am_lnum = nodep->am_lnum; 1176 head->am_family = nodep->am_family; 1177 head->am_flags = nodep->am_flags; 1178 head->am_atype = nodep->am_atype; 1179 head->am_atype_cache = nodep->am_atype_cache; 1180 } else { 1181 for (head = aobjmap.aobjmap_head; head != NULL; 1182 head = head->am_next) { 1183 if (strcmp(head->am_ifname, 1184 nodep->am_ifname) == 0) 1185 break; 1186 } 1187 nodep->am_nextnum = (head == NULL ? 0 : 1188 head->am_nextnum); 1189 err = i_ipmgmt_add_amnode(nodep); 1190 } 1191 db_op = IPADM_DB_WRITE; 1192 break; 1193 case ADDROBJ_DELETE: 1194 prev = head; 1195 while (head != NULL) { 1196 if (strcmp(head->am_aobjname, 1197 nodep->am_aobjname) == 0) { 1198 nodep->am_atype = head->am_atype; 1199 /* 1200 * There could be multiple IPV6_ADDRCONF nodes, 1201 * with same address object name, so check for 1202 * logical number also. 1203 */ 1204 if (head->am_atype != 1205 IPADM_ADDR_IPV6_ADDRCONF || 1206 nodep->am_lnum == head->am_lnum) 1207 break; 1208 } 1209 prev = head; 1210 head = head->am_next; 1211 } 1212 if (head != NULL) { 1213 /* 1214 * If the address object is in both active and 1215 * persistent configuration and the user is deleting it 1216 * only from active configuration then mark this node 1217 * for deletion by reseting IPMGMT_ACTIVE bit. 1218 * With this the same address object name cannot 1219 * be reused until it is permanently removed. 1220 */ 1221 if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) && 1222 nodep->am_flags == IPMGMT_ACTIVE) { 1223 /* Update flags in the in-memory map. */ 1224 head->am_flags &= ~IPMGMT_ACTIVE; 1225 head->am_lnum = -1; 1226 1227 /* Update info in file. */ 1228 db_op = IPADM_DB_WRITE; 1229 *nodep = *head; 1230 } else { 1231 (void) strlcpy(nodep->am_ifname, 1232 head->am_ifname, 1233 sizeof (nodep->am_ifname)); 1234 /* otherwise delete the node */ 1235 if (head == aobjmap.aobjmap_head) 1236 aobjmap.aobjmap_head = head->am_next; 1237 else 1238 prev->am_next = head->am_next; 1239 free(head); 1240 db_op = IPADM_DB_DELETE; 1241 } 1242 } else { 1243 err = ENOENT; 1244 } 1245 break; 1246 case ADDROBJ_LOOKUPADD: 1247 err = i_ipmgmt_lookupadd_amnode(nodep); 1248 update = B_FALSE; 1249 break; 1250 case ADDROBJ_SETLIFNUM: 1251 update = B_FALSE; 1252 for (; head != NULL; head = head->am_next) { 1253 if (strcmp(head->am_ifname, 1254 nodep->am_ifname) == 0 && 1255 head->am_family == nodep->am_family && 1256 head->am_lnum == nodep->am_lnum) { 1257 err = EEXIST; 1258 break; 1259 } 1260 if (strcmp(head->am_aobjname, 1261 nodep->am_aobjname) == 0) { 1262 matched = head; 1263 } 1264 } 1265 if (err == EEXIST) 1266 break; 1267 if (matched != NULL) { 1268 /* update the lifnum */ 1269 matched->am_lnum = nodep->am_lnum; 1270 } else { 1271 err = ENOENT; 1272 } 1273 break; 1274 default: 1275 assert(0); 1276 } 1277 1278 if (err == 0 && update) 1279 err = ipmgmt_persist_aobjmap(nodep, db_op); 1280 1281 (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock); 1282 1283 return (err); 1284 } 1285 1286 /* 1287 * Given a node in `aobjmap', this function converts it into nvlist_t structure. 1288 * The content to be written to DB must be represented as nvlist_t. 1289 */ 1290 static int 1291 i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np) 1292 { 1293 int err; 1294 char strval[IPMGMT_STRSIZE]; 1295 1296 *nvl = NULL; 1297 if ((err = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0)) != 0) 1298 goto fail; 1299 1300 if ((err = nvlist_add_string(*nvl, IPADM_NVP_AOBJNAME, 1301 np->am_aobjname)) != 0) 1302 goto fail; 1303 1304 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IFNAME, 1305 np->am_ifname)) != 0) 1306 goto fail; 1307 1308 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_lnum); 1309 if ((err = nvlist_add_string(*nvl, IPADM_NVP_LIFNUM, strval)) != 0) 1310 goto fail; 1311 1312 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_family); 1313 if ((err = nvlist_add_string(*nvl, IPADM_NVP_FAMILY, strval)) != 0) 1314 goto fail; 1315 1316 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_flags); 1317 if ((err = nvlist_add_string(*nvl, FLAGS, strval)) != 0) 1318 goto fail; 1319 1320 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_atype); 1321 if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0) 1322 goto fail; 1323 1324 switch (np->am_atype) { 1325 case IPADM_ADDR_IPV6_ADDRCONF: { 1326 struct sockaddr_in6 *in6; 1327 1328 in6 = &np->ipmgmt_am_ifid; 1329 if (np->ipmgmt_am_linklocal && 1330 IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) { 1331 if ((err = nvlist_add_string(*nvl, 1332 IPADM_NVP_IPNUMADDR, "default")) != 0) { 1333 goto fail; 1334 } 1335 } else { 1336 if (inet_ntop(AF_INET6, &in6->sin6_addr, strval, 1337 IPMGMT_STRSIZE) == NULL) { 1338 err = errno; 1339 goto fail; 1340 } 1341 if ((err = nvlist_add_string(*nvl, 1342 IPADM_NVP_IPNUMADDR, strval)) != 0) { 1343 goto fail; 1344 } 1345 } 1346 } 1347 break; 1348 case IPADM_ADDR_DHCP: { 1349 if (np->ipmgmt_am_reqhost && 1350 *np->ipmgmt_am_reqhost != '\0' && 1351 (err = nvlist_add_string(*nvl, IPADM_NVP_REQHOST, 1352 np->ipmgmt_am_reqhost)) != 0) 1353 goto fail; 1354 } 1355 /* FALLTHRU */ 1356 default: 1357 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR, 1358 "")) != 0) 1359 goto fail; 1360 break; 1361 } 1362 return (err); 1363 fail: 1364 nvlist_free(*nvl); 1365 return (err); 1366 } 1367 1368 /* 1369 * Read the aobjmap data store and build the in-memory representation 1370 * of the aobjmap. We don't need to hold any locks while building this as 1371 * we do this in very early stage of daemon coming up, even before the door 1372 * is opened. 1373 */ 1374 /* ARGSUSED */ 1375 extern boolean_t 1376 ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 1377 int *errp) 1378 { 1379 nvpair_t *nvp = NULL; 1380 char *name, *strval = NULL; 1381 ipmgmt_aobjmap_t node; 1382 struct sockaddr_in6 *in6; 1383 1384 *errp = 0; 1385 node.am_next = NULL; 1386 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 1387 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 1388 name = nvpair_name(nvp); 1389 1390 if ((*errp = nvpair_value_string(nvp, &strval)) != 0) 1391 return (B_TRUE); 1392 if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) { 1393 (void) strlcpy(node.am_aobjname, strval, 1394 sizeof (node.am_aobjname)); 1395 } else if (strcmp(IPADM_NVP_IFNAME, name) == 0) { 1396 (void) strlcpy(node.am_ifname, strval, 1397 sizeof (node.am_ifname)); 1398 } else if (strcmp(IPADM_NVP_LIFNUM, name) == 0) { 1399 node.am_lnum = atoi(strval); 1400 } else if (strcmp(IPADM_NVP_FAMILY, name) == 0) { 1401 node.am_family = (sa_family_t)atoi(strval); 1402 } else if (strcmp(FLAGS, name) == 0) { 1403 node.am_flags = atoi(strval); 1404 } else if (strcmp(ATYPE, name) == 0) { 1405 node.am_atype = (ipadm_addr_type_t)atoi(strval); 1406 } else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) { 1407 if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) { 1408 in6 = &node.ipmgmt_am_ifid; 1409 if (strcmp(strval, "default") == 0) { 1410 bzero(in6, 1411 sizeof (node.ipmgmt_am_ifid)); 1412 node.ipmgmt_am_linklocal = B_TRUE; 1413 } else { 1414 (void) inet_pton(AF_INET6, strval, 1415 &in6->sin6_addr); 1416 if (IN6_IS_ADDR_UNSPECIFIED( 1417 &in6->sin6_addr)) 1418 node.ipmgmt_am_linklocal = 1419 B_TRUE; 1420 } 1421 } 1422 } 1423 } 1424 1425 /* we have all the information we need, add the node */ 1426 *errp = i_ipmgmt_add_amnode(&node); 1427 1428 return (B_TRUE); 1429 } 1430 1431 /* 1432 * Updates an entry from the temporary cache file, which matches the given 1433 * address object name. 1434 */ 1435 /* ARGSUSED */ 1436 static boolean_t 1437 ipmgmt_update_aobjmap(void *arg, nvlist_t *db_nvl, char *buf, 1438 size_t buflen, int *errp) 1439 { 1440 ipadm_dbwrite_cbarg_t *cb = arg; 1441 nvlist_t *in_nvl = cb->dbw_nvl; 1442 uint32_t flags = cb->dbw_flags; 1443 char *db_lifnumstr = NULL, *in_lifnumstr = NULL; 1444 1445 *errp = 0; 1446 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl)) 1447 return (B_TRUE); 1448 1449 if (flags & IPMGMT_ATYPE_V6ACONF) { 1450 if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM, 1451 &db_lifnumstr) != 0 || 1452 nvlist_lookup_string(in_nvl, IPADM_NVP_LIFNUM, 1453 &in_lifnumstr) != 0 || 1454 (atoi(db_lifnumstr) != -1 && atoi(in_lifnumstr) != -1 && 1455 strcmp(db_lifnumstr, in_lifnumstr) != 0)) 1456 return (B_TRUE); 1457 } 1458 1459 /* we found the match */ 1460 (void) memset(buf, 0, buflen); 1461 if (ipadm_nvlist2str(in_nvl, buf, buflen) == 0) { 1462 /* buffer overflow */ 1463 *errp = ENOBUFS; 1464 } 1465 1466 /* stop the walker */ 1467 return (B_FALSE); 1468 } 1469 1470 /* 1471 * Deletes an entry from the temporary cache file, which matches the given 1472 * address object name. 1473 */ 1474 /* ARGSUSED */ 1475 static boolean_t 1476 ipmgmt_delete_aobjmap(void *arg, nvlist_t *db_nvl, char *buf, 1477 size_t buflen, int *errp) 1478 { 1479 ipmgmt_aobjmap_t *nodep = arg; 1480 char *db_lifnumstr = NULL; 1481 1482 *errp = 0; 1483 if (!ipmgmt_nvlist_match(db_nvl, NULL, nodep->am_ifname, 1484 nodep->am_aobjname)) 1485 return (B_TRUE); 1486 1487 if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) { 1488 if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM, 1489 &db_lifnumstr) != 0 || atoi(db_lifnumstr) != nodep->am_lnum) 1490 return (B_TRUE); 1491 } 1492 1493 /* we found the match, delete the line from the db */ 1494 buf[0] = '\0'; 1495 1496 /* stop the walker */ 1497 return (B_FALSE); 1498 } 1499 1500 /* 1501 * Adds or deletes aobjmap node information into a temporary cache file. 1502 */ 1503 extern int 1504 ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op) 1505 { 1506 int err; 1507 ipadm_dbwrite_cbarg_t cb; 1508 nvlist_t *nvl = NULL; 1509 1510 if (op == IPADM_DB_WRITE) { 1511 if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0) 1512 return (err); 1513 cb.dbw_nvl = nvl; 1514 if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) 1515 cb.dbw_flags = IPMGMT_ATYPE_V6ACONF; 1516 else 1517 cb.dbw_flags = 0; 1518 1519 err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb, 1520 ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE); 1521 nvlist_free(nvl); 1522 } else { 1523 assert(op == IPADM_DB_DELETE); 1524 1525 err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep, 1526 ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE); 1527 } 1528 return (err); 1529 } 1530 1531 /* 1532 * upgrades the ipadm data-store. It renames all the old private protocol 1533 * property names which start with leading protocol names to begin with 1534 * IPADM_PRIV_PROP_PREFIX. 1535 */ 1536 /* ARGSUSED */ 1537 boolean_t 1538 ipmgmt_db_upgrade(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 1539 int *errp) 1540 { 1541 nvpair_t *nvp; 1542 char *name, *pname = NULL, *protostr = NULL, *pval = NULL; 1543 uint_t proto, nproto; 1544 char nname[IPMGMT_STRSIZE], tmpstr[IPMGMT_STRSIZE]; 1545 1546 *errp = 0; 1547 /* 1548 * We are interested in lines which contain protocol properties. We 1549 * walk through other lines in the DB. 1550 */ 1551 if (nvlist_exists(db_nvl, IPADM_NVP_IFNAME) || 1552 nvlist_exists(db_nvl, IPADM_NVP_AOBJNAME)) { 1553 return (B_TRUE); 1554 } 1555 assert(nvlist_exists(db_nvl, IPADM_NVP_PROTONAME)); 1556 1557 /* 1558 * extract the propname from the `db_nvl' and also extract the 1559 * protocol from the `db_nvl'. 1560 */ 1561 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 1562 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 1563 name = nvpair_name(nvp); 1564 if (strcmp(name, IPADM_NVP_PROTONAME) == 0) { 1565 if (nvpair_value_string(nvp, &protostr) != 0) 1566 return (B_TRUE); 1567 } else { 1568 assert(!IPADM_PRIV_NVP(name)); 1569 pname = name; 1570 if (nvpair_value_string(nvp, &pval) != 0) 1571 return (B_TRUE); 1572 } 1573 } 1574 1575 /* if the private property is in the right format return */ 1576 if (strncmp(pname, IPADM_PERSIST_PRIVPROP_PREFIX, 1577 strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) { 1578 return (B_TRUE); 1579 } 1580 /* if it's a public property move onto the next property */ 1581 nproto = proto = ipadm_str2proto(protostr); 1582 if (ipadm_legacy2new_propname(pname, nname, sizeof (nname), 1583 &nproto) != 0) { 1584 return (B_TRUE); 1585 } 1586 1587 /* replace the old protocol with new protocol, if required */ 1588 if (nproto != proto) { 1589 protostr = ipadm_proto2str(nproto); 1590 if (nvlist_add_string(db_nvl, IPADM_NVP_PROTONAME, 1591 protostr) != 0) { 1592 return (B_TRUE); 1593 } 1594 } 1595 1596 /* replace the old property name with new property name, if required */ 1597 /* add the prefix to property name */ 1598 (void) snprintf(tmpstr, sizeof (tmpstr), "_%s", nname); 1599 if (nvlist_add_string(db_nvl, tmpstr, pval) != 0 || 1600 nvlist_remove(db_nvl, pname, DATA_TYPE_STRING) != 0) { 1601 return (B_TRUE); 1602 } 1603 (void) memset(buf, 0, buflen); 1604 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { 1605 /* buffer overflow */ 1606 *errp = ENOBUFS; 1607 } 1608 return (B_TRUE); 1609 } 1610 1611 /* 1612 * Called during boot. 1613 * 1614 * Walk through the DB and apply all the global module properties. We plow 1615 * through the DB even if we fail to apply property. 1616 */ 1617 /* ARGSUSED */ 1618 static boolean_t 1619 ipmgmt_db_init(void *cbarg, nvlist_t *db_nvl, char *buf, size_t buflen, 1620 int *errp) 1621 { 1622 ipadm_handle_t iph = cbarg; 1623 nvpair_t *nvp, *pnvp = NULL; 1624 char *strval = NULL, *name, *mod = NULL, *pname; 1625 char tmpstr[IPMGMT_STRSIZE]; 1626 uint_t proto; 1627 1628 /* 1629 * We could have used nvl_exists() directly, however we need several 1630 * calls to it and each call traverses the list. Since this codepath 1631 * is exercised during boot, let's traverse the list ourselves and do 1632 * the necessary checks. 1633 */ 1634 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 1635 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 1636 name = nvpair_name(nvp); 1637 if (IPADM_PRIV_NVP(name)) { 1638 if (strcmp(name, IPADM_NVP_IFNAME) == 0 || 1639 strcmp(name, IPADM_NVP_AOBJNAME) == 0) 1640 return (B_TRUE); 1641 else if (strcmp(name, IPADM_NVP_PROTONAME) == 0 && 1642 nvpair_value_string(nvp, &mod) != 0) 1643 return (B_TRUE); 1644 } else { 1645 /* possible a property */ 1646 pnvp = nvp; 1647 } 1648 } 1649 1650 /* If we are here then we have found a global property */ 1651 assert(mod != NULL); 1652 assert(nvpair_type(pnvp) == DATA_TYPE_STRING); 1653 1654 proto = ipadm_str2proto(mod); 1655 name = nvpair_name(pnvp); 1656 if (nvpair_value_string(pnvp, &strval) == 0) { 1657 if (strncmp(name, IPADM_PERSIST_PRIVPROP_PREFIX, 1658 strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) { 1659 /* private protocol property */ 1660 pname = &name[1]; 1661 } else if (ipadm_legacy2new_propname(name, tmpstr, 1662 sizeof (tmpstr), &proto) == 0) { 1663 pname = tmpstr; 1664 } else { 1665 pname = name; 1666 } 1667 if (ipadm_set_prop(iph, pname, strval, proto, 1668 IPADM_OPT_ACTIVE) != IPADM_SUCCESS) { 1669 ipmgmt_log(LOG_WARNING, "Failed to reapply property %s", 1670 pname); 1671 } 1672 } 1673 1674 return (B_TRUE); 1675 } 1676 1677 /* initialize global module properties */ 1678 void 1679 ipmgmt_init_prop() 1680 { 1681 ipadm_handle_t iph = NULL; 1682 1683 if (ipadm_open(&iph, IPH_INIT) != IPADM_SUCCESS) { 1684 ipmgmt_log(LOG_WARNING, "Could not reapply any of the " 1685 "persisted protocol properties"); 1686 return; 1687 } 1688 /* ipmgmt_db_init() logs warnings if there are any issues */ 1689 (void) ipmgmt_db_walk(ipmgmt_db_init, iph, IPADM_DB_READ); 1690 ipadm_close(iph); 1691 } 1692 1693 void 1694 ipmgmt_release_scf_resources(scf_resources_t *res) 1695 { 1696 scf_entry_destroy(res->sr_ent); 1697 scf_transaction_destroy(res->sr_tx); 1698 scf_value_destroy(res->sr_val); 1699 scf_property_destroy(res->sr_prop); 1700 scf_pg_destroy(res->sr_pg); 1701 scf_instance_destroy(res->sr_inst); 1702 (void) scf_handle_unbind(res->sr_handle); 1703 scf_handle_destroy(res->sr_handle); 1704 } 1705 1706 /* 1707 * It creates the necessary SCF handles and binds the given `fmri' to an 1708 * instance. These resources are required for retrieving property value, 1709 * creating property groups and modifying property values. 1710 */ 1711 int 1712 ipmgmt_create_scf_resources(const char *fmri, scf_resources_t *res) 1713 { 1714 res->sr_tx = NULL; 1715 res->sr_ent = NULL; 1716 res->sr_inst = NULL; 1717 res->sr_pg = NULL; 1718 res->sr_prop = NULL; 1719 res->sr_val = NULL; 1720 1721 if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) 1722 return (-1); 1723 1724 if (scf_handle_bind(res->sr_handle) != 0) { 1725 scf_handle_destroy(res->sr_handle); 1726 return (-1); 1727 } 1728 if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) 1729 goto failure; 1730 if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL, 1731 res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) { 1732 goto failure; 1733 } 1734 /* we will create the rest of the resources on demand */ 1735 return (0); 1736 1737 failure: 1738 ipmgmt_log(LOG_WARNING, "failed to create scf resources: %s", 1739 scf_strerror(scf_error())); 1740 ipmgmt_release_scf_resources(res); 1741 return (-1); 1742 } 1743 1744 /* 1745 * persists the `pval' for a given property `pname' in SCF. The only supported 1746 * SCF property types are INTEGER and ASTRING. 1747 */ 1748 static int 1749 ipmgmt_set_scfprop_value(scf_resources_t *res, const char *pname, void *pval, 1750 scf_type_t ptype) 1751 { 1752 int result = -1; 1753 boolean_t new; 1754 1755 if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) 1756 goto failure; 1757 switch (ptype) { 1758 case SCF_TYPE_INTEGER: 1759 scf_value_set_integer(res->sr_val, *(int64_t *)pval); 1760 break; 1761 case SCF_TYPE_ASTRING: 1762 if (scf_value_set_astring(res->sr_val, (char *)pval) != 0) { 1763 ipmgmt_log(LOG_WARNING, "Error setting string value %s " 1764 "for property %s: %s", pval, pname, 1765 scf_strerror(scf_error())); 1766 goto failure; 1767 } 1768 break; 1769 default: 1770 goto failure; 1771 } 1772 1773 if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL) 1774 goto failure; 1775 if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL) 1776 goto failure; 1777 if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) 1778 goto failure; 1779 1780 retry: 1781 new = (scf_pg_get_property(res->sr_pg, pname, res->sr_prop) != 0); 1782 if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1) 1783 goto failure; 1784 if (new) { 1785 if (scf_transaction_property_new(res->sr_tx, res->sr_ent, 1786 pname, ptype) == -1) { 1787 goto failure; 1788 } 1789 } else { 1790 if (scf_transaction_property_change(res->sr_tx, res->sr_ent, 1791 pname, ptype) == -1) { 1792 goto failure; 1793 } 1794 } 1795 1796 if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0) 1797 goto failure; 1798 1799 result = scf_transaction_commit(res->sr_tx); 1800 if (result == 0) { 1801 scf_transaction_reset(res->sr_tx); 1802 if (scf_pg_update(res->sr_pg) == -1) { 1803 goto failure; 1804 } 1805 goto retry; 1806 } 1807 if (result == -1) 1808 goto failure; 1809 return (0); 1810 1811 failure: 1812 ipmgmt_log(LOG_WARNING, "failed to save the data in SCF: %s", 1813 scf_strerror(scf_error())); 1814 return (-1); 1815 } 1816 1817 /* 1818 * Given a `pgname'/`pname', it retrieves the value based on `ptype' and 1819 * places it in `pval'. 1820 */ 1821 static int 1822 ipmgmt_get_scfprop(scf_resources_t *res, const char *pgname, const char *pname, 1823 void *pval, scf_type_t ptype) 1824 { 1825 ssize_t numvals; 1826 scf_simple_prop_t *prop; 1827 1828 prop = scf_simple_prop_get(res->sr_handle, IPMGMTD_FMRI, pgname, pname); 1829 numvals = scf_simple_prop_numvalues(prop); 1830 if (numvals <= 0) 1831 goto ret; 1832 switch (ptype) { 1833 case SCF_TYPE_INTEGER: 1834 *(int64_t **)pval = scf_simple_prop_next_integer(prop); 1835 break; 1836 case SCF_TYPE_ASTRING: 1837 *(char **)pval = scf_simple_prop_next_astring(prop); 1838 break; 1839 default: 1840 break; 1841 } 1842 ret: 1843 scf_simple_prop_free(prop); 1844 return (numvals); 1845 } 1846 1847 /* 1848 * It stores the `pval' for given `pgname'/`pname' property group in SCF. 1849 */ 1850 static int 1851 ipmgmt_set_scfprop(scf_resources_t *res, const char *pgname, const char *pname, 1852 void *pval, scf_type_t ptype) 1853 { 1854 scf_error_t err; 1855 1856 if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) { 1857 ipmgmt_log(LOG_WARNING, "failed to create property group: %s", 1858 scf_strerror(scf_error())); 1859 return (-1); 1860 } 1861 1862 if (scf_instance_add_pg(res->sr_inst, pgname, SCF_GROUP_APPLICATION, 1863 0, res->sr_pg) != 0) { 1864 if ((err = scf_error()) != SCF_ERROR_EXISTS) { 1865 ipmgmt_log(LOG_WARNING, 1866 "Error adding property group '%s/%s': %s", 1867 pgname, pname, scf_strerror(err)); 1868 return (-1); 1869 } 1870 /* 1871 * if the property group already exists, then we get the 1872 * composed view of the property group for the given instance. 1873 */ 1874 if (scf_instance_get_pg_composed(res->sr_inst, NULL, pgname, 1875 res->sr_pg) != 0) { 1876 ipmgmt_log(LOG_WARNING, "Error getting composed view " 1877 "of the property group '%s/%s': %s", pgname, pname, 1878 scf_strerror(scf_error())); 1879 return (-1); 1880 } 1881 } 1882 1883 return (ipmgmt_set_scfprop_value(res, pname, pval, ptype)); 1884 } 1885 1886 /* 1887 * Returns B_TRUE, if the non-global zone is being booted for the first time 1888 * after being installed. This is required to setup the ipadm data-store for 1889 * the first boot of the non-global zone. Please see, PSARC 2010/166, 1890 * for more info. 1891 * 1892 * Note that, this API cannot be used to determine first boot post image-update. 1893 * 'pkg image-update' clones the current BE and the existing value of 1894 * ipmgmtd/first_boot_done will be carried forward and obviously it will be set 1895 * to B_TRUE. 1896 */ 1897 boolean_t 1898 ipmgmt_ngz_firstboot_postinstall() 1899 { 1900 scf_resources_t res; 1901 boolean_t bval = B_TRUE; 1902 char *strval; 1903 1904 /* we always err on the side of caution */ 1905 if (ipmgmt_create_scf_resources(IPMGMTD_FMRI, &res) != 0) 1906 return (bval); 1907 1908 if (ipmgmt_get_scfprop(&res, IPMGMTD_APP_PG, IPMGMTD_PROP_FBD, &strval, 1909 SCF_TYPE_ASTRING) > 0) { 1910 bval = (strcmp(strval, IPMGMTD_TRUESTR) == 0 ? 1911 B_FALSE : B_TRUE); 1912 } else { 1913 /* 1914 * IPMGMTD_PROP_FBD does not exist in the SCF. Lets create it. 1915 * Since we err on the side of caution, we ignore the return 1916 * error and return B_TRUE. 1917 */ 1918 (void) ipmgmt_set_scfprop(&res, IPMGMTD_APP_PG, 1919 IPMGMTD_PROP_FBD, IPMGMTD_TRUESTR, SCF_TYPE_ASTRING); 1920 } 1921 ipmgmt_release_scf_resources(&res); 1922 return (bval); 1923 } 1924 1925 /* 1926 * Returns B_TRUE, if the data-store needs upgrade otherwise returns B_FALSE. 1927 * Today we have to take care of, one case of, upgrading from version 0 to 1928 * version 1, so we will use boolean_t as means to decide if upgrade is needed 1929 * or not. Further, the upcoming projects might completely move the flatfile 1930 * data-store into SCF and hence we shall keep this interface simple. 1931 */ 1932 boolean_t 1933 ipmgmt_needs_upgrade(scf_resources_t *res) 1934 { 1935 boolean_t bval = B_TRUE; 1936 int64_t *verp; 1937 1938 if (ipmgmt_get_scfprop(res, IPMGMTD_APP_PG, IPMGMTD_PROP_DBVER, 1939 &verp, SCF_TYPE_INTEGER) > 0) { 1940 if (*verp == IPADM_DB_VERSION) 1941 bval = B_FALSE; 1942 } 1943 /* 1944 * 'datastore_version' doesn't exist. Which means that we need to 1945 * upgrade the datastore. We will create 'datastore_version' and set 1946 * the version value to IPADM_DB_VERSION, after we upgrade the file. 1947 */ 1948 return (bval); 1949 } 1950 1951 /* 1952 * This is called after the successful upgrade of the local data-store. With 1953 * the data-store upgraded to recent version we don't have to do anything on 1954 * subsequent reboots. 1955 */ 1956 void 1957 ipmgmt_update_dbver(scf_resources_t *res) 1958 { 1959 int64_t version = IPADM_DB_VERSION; 1960 1961 (void) ipmgmt_set_scfprop(res, IPMGMTD_APP_PG, 1962 IPMGMTD_PROP_DBVER, &version, SCF_TYPE_INTEGER); 1963 } 1964 1965 /* 1966 * Return TRUE if `ifname' has persistent configuration for the `af' address 1967 * family in the datastore. 1968 * It is possible to call the function with af == AF_UNSPEC, so in this case 1969 * the function returns TRUE if either AF_INET or AF_INET6 interface exists 1970 */ 1971 boolean_t 1972 ipmgmt_persist_if_exists(const char *ifname, sa_family_t af) 1973 { 1974 boolean_t exists = B_FALSE; 1975 nvlist_t *if_info_nvl; 1976 uint16_t *families = NULL; 1977 sa_family_t af_db; 1978 uint_t nelem = 0; 1979 1980 if (ipmgmt_get_ifinfo_nvl(ifname, &if_info_nvl) != 0) 1981 goto done; 1982 1983 if (nvlist_lookup_uint16_array(if_info_nvl, IPADM_NVP_FAMILIES, 1984 &families, &nelem) != 0) 1985 goto done; 1986 1987 while (nelem-- > 0) { 1988 af_db = families[nelem]; 1989 if (af_db == af || (af == AF_UNSPEC && 1990 (af_db == AF_INET || af_db == AF_INET6))) { 1991 exists = B_TRUE; 1992 break; 1993 } 1994 } 1995 1996 done: 1997 if (if_info_nvl != NULL) 1998 nvlist_free(if_info_nvl); 1999 2000 return (exists); 2001 } 2002 2003 /* 2004 * Retrieves the membership information for the requested mif_name 2005 * if mif_name is a member of a IPMP group, then gif_name will contain 2006 * the name of IPMP group interface, otherwise the variable will be empty 2007 */ 2008 void 2009 ipmgmt_get_group_interface(const char *mif_name, char *gif_name, size_t size) 2010 { 2011 char *gif_name_from_nvl; 2012 nvlist_t *if_info_nvl; 2013 2014 gif_name[0] = '\0'; 2015 2016 if (ipmgmt_get_ifinfo_nvl(mif_name, &if_info_nvl) != 0) 2017 goto done; 2018 2019 if (nvlist_lookup_string(if_info_nvl, IPADM_NVP_GIFNAME, 2020 &gif_name_from_nvl) != 0) 2021 goto done; 2022 2023 (void) strlcpy(gif_name, gif_name_from_nvl, size); 2024 2025 done: 2026 if (if_info_nvl != NULL) 2027 nvlist_free(if_info_nvl); 2028 } 2029 2030 static int 2031 ipmgmt_get_ifinfo_nvl(const char *ifname, nvlist_t **if_info_nvl) 2032 { 2033 ipmgmt_get_cbarg_t cbarg; 2034 nvpair_t *nvp; 2035 nvlist_t *nvl; 2036 int err; 2037 2038 cbarg.cb_ifname = NULL; 2039 cbarg.cb_aobjname = NULL; 2040 cbarg.cb_ocnt = 0; 2041 2042 *if_info_nvl = NULL; 2043 2044 if ((err = nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0)) != 0) 2045 goto done; 2046 2047 err = ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ); 2048 if (err == ENOENT && cbarg.cb_ocnt > 0) 2049 err = 0; 2050 2051 if (err != 0) 2052 goto done; 2053 2054 for (nvp = nvlist_next_nvpair(cbarg.cb_onvl, NULL); nvp != NULL; 2055 nvp = nvlist_next_nvpair(cbarg.cb_onvl, nvp)) { 2056 2057 if (strcmp(nvpair_name(nvp), ifname) != 0) 2058 continue; 2059 2060 if ((err = nvpair_value_nvlist(nvp, &nvl)) != 0 || 2061 (err = nvlist_dup(nvl, if_info_nvl, NV_UNIQUE_NAME)) != 0) 2062 *if_info_nvl = NULL; 2063 2064 break; 2065 } 2066 2067 done: 2068 nvlist_free(cbarg.cb_onvl); 2069 2070 return (err); 2071 } 2072