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