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