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