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