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