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