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