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