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