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