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