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