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  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 /*
  26  * This file contains routines to read/write formatted entries from/to
  27  * libipadm data store /etc/ipadm/ipadm.conf. Each entry in the DB is a
  28  * series of IPADM_NVPAIR_SEP separated (name, value) pairs, as shown
  29  * below:
  30  *              name=value[;...]
  31  *
  32  * The 'name' determines how to interpret 'value'. The supported names are:
  33  *
  34  *  IPADM_NVP_IPV6ADDR - value holds local and remote IPv6 addresses and when
  35  *             converted to nvlist, will contain nvpairs for local and remote
  36  *             addresses. These nvpairs are of type DATA_TYPE_STRING
  37  *
  38  *  IPADM_NVP_IPV4ADDR - value holds local and remote IPv4 addresses and when
  39  *             converted to nvlist, will contain nvpairs for local and remote
  40  *             addresses. These nvpairs are of type DATA_TYPE_STRING
  41  *
  42  *  IPADM_NVP_INTFID - value holds token, prefixlen, stateless and stateful
  43  *             info and when converted to nvlist, will contain following nvpairs
  44  *                      interface_id: DATA_TYPE_UINT8_ARRAY
  45  *                      prefixlen: DATA_TYPE_UINT32
  46  *                      stateless: DATA_TYPE_STRING
  47  *                      stateful: DATA_TYPE_STRING
  48  *
  49  *  IPADM_NVP_DHCP - value holds wait time and primary info and when converted
  50  *             to nvlist, will contain following nvpairs
  51  *                      wait:   DATA_TYPE_INT32
  52  *                      primary: DATA_TYPE_BOOLEAN
  53  *
  54  *  default  - value is a single entity and when converted to nvlist, will
  55  *             contain nvpair of type DATA_TYPE_STRING. nvpairs private to
  56  *             ipadm are of this type. Further the property name and property
  57  *             values are stored as nvpairs of this type.
  58  *
  59  * The syntax for each line is described above the respective functions below.
  60  */
  61 
  62 #include <stdlib.h>
  63 #include <strings.h>
  64 #include <errno.h>
  65 #include <ctype.h>
  66 #include <sys/types.h>
  67 #include <sys/stat.h>
  68 #include <sys/dld.h>
  69 #include <fcntl.h>
  70 #include <dirent.h>
  71 #include <unistd.h>
  72 #include <assert.h>
  73 #include <sys/socket.h>
  74 #include <netinet/in.h>
  75 #include <arpa/inet.h>
  76 #include <sys/sockio.h>
  77 #include "libipadm_impl.h"
  78 
  79 #define MAXLINELEN              1024
  80 #define IPADM_NVPAIR_SEP        ";"
  81 #define IPADM_NAME_SEP          ","
  82 
  83 static char ipadm_rootdir[MAXPATHLEN] = "/";
  84 
  85 static int ipadm_process_db_line(db_wfunc_t *, void *, FILE *fp, FILE *nfp,
  86     ipadm_db_op_t);
  87 
  88 /*
  89  * convert nvpair to a "name=value" string for writing to the DB.
  90  */
  91 typedef size_t  ipadm_wfunc_t(nvpair_t *, char *, size_t);
  92 
  93 /*
  94  * ipadm_rfunc_t takes (`name', `value') and adds the appropriately typed
  95  * nvpair to the nvlist.
  96  */
  97 typedef void  ipadm_rfunc_t(nvlist_t *, char *name, char *value);
  98 
  99 static ipadm_rfunc_t    i_ipadm_str_dbline2nvl, i_ipadm_ip4_dbline2nvl,
 100                         i_ipadm_ip6_dbline2nvl, i_ipadm_intfid_dbline2nvl,
 101                         i_ipadm_dhcp_dbline2nvl;
 102 
 103 static ipadm_wfunc_t    i_ipadm_str_nvp2dbline, i_ipadm_ip4_nvp2dbline,
 104                         i_ipadm_ip6_nvp2dbline, i_ipadm_intfid_nvp2dbline,
 105                         i_ipadm_dhcp_nvp2dbline;
 106 
 107 /*
 108  * table of function pointers to read/write formatted entries from/to
 109  * ipadm.conf.
 110  */
 111 typedef struct ipadm_conf_ent_s {
 112         const char              *ipent_type_name;
 113         ipadm_wfunc_t           *ipent_wfunc;
 114         ipadm_rfunc_t           *ipent_rfunc;
 115 } ipadm_conf_ent_t;
 116 
 117 static ipadm_conf_ent_t ipadm_conf_ent[] = {
 118         { IPADM_NVP_IPV6ADDR, i_ipadm_ip6_nvp2dbline, i_ipadm_ip6_dbline2nvl },
 119         { IPADM_NVP_IPV4ADDR, i_ipadm_ip4_nvp2dbline, i_ipadm_ip4_dbline2nvl },
 120         { IPADM_NVP_INTFID, i_ipadm_intfid_nvp2dbline,
 121             i_ipadm_intfid_dbline2nvl },
 122         { IPADM_NVP_DHCP, i_ipadm_dhcp_nvp2dbline, i_ipadm_dhcp_dbline2nvl },
 123         { NULL, i_ipadm_str_nvp2dbline, i_ipadm_str_dbline2nvl }
 124 };
 125 
 126 static ipadm_conf_ent_t *
 127 i_ipadm_find_conf_type(const char *type)
 128 {
 129         int     i;
 130 
 131         for (i = 0; ipadm_conf_ent[i].ipent_type_name != NULL; i++)
 132                 if (strcmp(type, ipadm_conf_ent[i].ipent_type_name) == 0)
 133                         break;
 134         return (&ipadm_conf_ent[i]);
 135 }
 136 
 137 /*
 138  * Extracts the hostnames IPADM_NVP_IPADDRHNAME and IPADM_NVP_IPDADDRHNAME from
 139  * the given nvlist `nvl' and adds the strings to `buf'.
 140  */
 141 size_t
 142 i_ipadm_ip_addhostname2dbline(nvlist_t *nvl, char *buf, size_t buflen)
 143 {
 144         char    *cp;
 145         char    tmpbuf[IPADM_STRSIZE];
 146 
 147         /* Add the local hostname */
 148         if (nvlist_lookup_string(nvl, IPADM_NVP_IPADDRHNAME, &cp) != 0)
 149                 return (0);
 150         (void) strlcat(buf, cp, buflen); /* local hostname */
 151 
 152         /* Add the dst hostname */
 153         if (nvlist_lookup_string(nvl, IPADM_NVP_IPDADDRHNAME, &cp) != 0) {
 154                 /* no dst addr. just add a NULL character */
 155                 (void) snprintf(tmpbuf, sizeof (tmpbuf), ",");
 156         } else {
 157                 (void) snprintf(tmpbuf, sizeof (tmpbuf), ",%s", cp);
 158         }
 159         return (strlcat(buf, tmpbuf, buflen));
 160 }
 161 
 162 /*
 163  * Converts IPADM_NVP_IPV4ADDR nvpair to a string representation for writing to
 164  * the DB. The converted string format:
 165  *      ipv4addr=<local numeric IP string or hostname,remote numeric IP
 166  *          string or hostname>
 167  */
 168 static size_t
 169 i_ipadm_ip4_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
 170 {
 171         nvlist_t        *v;
 172         int             nbytes;
 173 
 174         assert(nvpair_type(nvp) == DATA_TYPE_NVLIST &&
 175             strcmp(nvpair_name(nvp), IPADM_NVP_IPV4ADDR) == 0);
 176 
 177         (void) snprintf(buf, buflen, "%s=", IPADM_NVP_IPV4ADDR);
 178         if (nvpair_value_nvlist(nvp, &v) != 0)
 179                 goto fail;
 180         nbytes = i_ipadm_ip_addhostname2dbline(v, buf, buflen);
 181         if (nbytes != 0)
 182                 return (nbytes);
 183 fail:
 184         buf[0] = '\0';
 185         return (0);
 186 }
 187 
 188 /*
 189  * Converts IPADM_NVP_IPV6ADDR nvpair to a string representation for writing to
 190  * the DB. The converted string format:
 191  *      ipv6addr=<local numeric IP string or hostname,remote numeric IP
 192  *          string or hostname>
 193  */
 194 static size_t
 195 i_ipadm_ip6_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
 196 {
 197         nvlist_t        *v;
 198         int             nbytes;
 199 
 200         assert(nvpair_type(nvp) == DATA_TYPE_NVLIST &&
 201             strcmp(nvpair_name(nvp), IPADM_NVP_IPV6ADDR) == 0);
 202 
 203         (void) snprintf(buf, buflen, "%s=", IPADM_NVP_IPV6ADDR);
 204         if (nvpair_value_nvlist(nvp, &v) != 0)
 205                 goto fail;
 206         nbytes = i_ipadm_ip_addhostname2dbline(v, buf, buflen);
 207         if (nbytes != 0)
 208                 return (nbytes);
 209 fail:
 210         buf[0] = '\0';
 211         return (0);
 212 }
 213 
 214 /*
 215  * Converts IPADM_NVP_INTFID nvpair to a string representation for writing to
 216  * the DB. The converted string format:
 217  *      IPADM_NVP_INTFID=<intfid/prefixlen>,{yes|no},{yes|no}
 218  */
 219 static size_t
 220 i_ipadm_intfid_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
 221 {
 222         char            addrbuf[IPADM_STRSIZE];
 223         nvlist_t        *v;
 224         uint32_t        prefixlen;
 225         struct in6_addr in6addr;
 226         char            *stateless;
 227         char            *stateful;
 228 
 229         assert(nvpair_type(nvp) == DATA_TYPE_NVLIST &&
 230             strcmp(nvpair_name(nvp), IPADM_NVP_INTFID) == 0);
 231 
 232         (void) snprintf(buf, buflen, "%s=", IPADM_NVP_INTFID);
 233         if (nvpair_value_nvlist(nvp, &v) != 0)
 234                 goto fail;
 235         if (i_ipadm_nvl2in6_addr(v, IPADM_NVP_IPNUMADDR, &in6addr) !=
 236             IPADM_SUCCESS)
 237                 goto fail;
 238         (void) inet_ntop(AF_INET6, &in6addr, addrbuf,
 239             sizeof (addrbuf));
 240         (void) strlcat(buf, addrbuf, buflen);
 241         if (nvlist_lookup_uint32(v, IPADM_NVP_PREFIXLEN, &prefixlen) != 0 ||
 242             nvlist_lookup_string(v, IPADM_NVP_STATELESS, &stateless) != 0 ||
 243             nvlist_lookup_string(v, IPADM_NVP_STATEFUL, &stateful) != 0)
 244                 goto fail;
 245         (void) snprintf(addrbuf, sizeof (addrbuf), "/%d,%s,%s",
 246             prefixlen, stateless, stateful);
 247         return (strlcat(buf, addrbuf, buflen));
 248 fail:
 249         buf[0] = '\0';
 250         return (0);
 251 }
 252 
 253 /*
 254  * Converts IPADM_NVP_DHCP nvpair to a string representation for writing to the
 255  * DB. The converted string format:
 256  *      IPADM_NVP_DHCP=<wait_time>,{yes|no}
 257  */
 258 static size_t
 259 i_ipadm_dhcp_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
 260 {
 261         char            addrbuf[IPADM_STRSIZE];
 262         int32_t         wait;
 263         boolean_t       primary;
 264         nvlist_t        *v;
 265 
 266         assert(nvpair_type(nvp) == DATA_TYPE_NVLIST &&
 267             strcmp(nvpair_name(nvp), IPADM_NVP_DHCP) == 0);
 268 
 269         if (nvpair_value_nvlist(nvp, &v) != 0 ||
 270             nvlist_lookup_int32(v, IPADM_NVP_WAIT, &wait) != 0 ||
 271             nvlist_lookup_boolean_value(v, IPADM_NVP_PRIMARY, &primary) != 0) {
 272                 return (0);
 273         }
 274         (void) snprintf(buf, buflen, "%s=", IPADM_NVP_DHCP);
 275         (void) snprintf(addrbuf, sizeof (addrbuf), "%d,%s", wait,
 276             (primary ? "yes" : "no"));
 277         return (strlcat(buf, addrbuf, buflen));
 278 }
 279 
 280 /*
 281  * Constructs a "<name>=<value>" string from the nvpair, whose type must
 282  * be STRING.
 283  */
 284 static size_t
 285 i_ipadm_str_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
 286 {
 287         char    *str = NULL;
 288 
 289         assert(nvpair_type(nvp) == DATA_TYPE_STRING);
 290         if (nvpair_value_string(nvp, &str) != 0)
 291                 return (0);
 292         return (snprintf(buf, buflen, "%s=%s", nvpair_name(nvp), str));
 293 }
 294 
 295 /*
 296  * Converts a nvlist to string of the form:
 297  *  <prop0>=<val0>,...,<valn>;...;<propn>=<val0>,...,<valn>;
 298  */
 299 size_t
 300 ipadm_nvlist2str(nvlist_t *nvl, char *buf, size_t buflen)
 301 {
 302         nvpair_t        *nvp = NULL;
 303         uint_t          nbytes = 0, tbytes = 0;
 304         ipadm_conf_ent_t *ipent;
 305         size_t          bufsize = buflen;
 306 
 307         for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
 308             nvp = nvlist_next_nvpair(nvl, nvp)) {
 309                 ipent = i_ipadm_find_conf_type(nvpair_name(nvp));
 310                 nbytes = (*ipent->ipent_wfunc)(nvp, buf, buflen);
 311                 /* add nvpair separator */
 312                 nbytes += snprintf(buf + nbytes, buflen - nbytes, "%s",
 313                     IPADM_NVPAIR_SEP);
 314                 buflen -= nbytes;
 315                 buf += nbytes;
 316                 tbytes += nbytes;
 317                 if (tbytes >= bufsize)       /* buffer overflow */
 318                         return (0);
 319         }
 320         nbytes = snprintf(buf, buflen, "%c%c", '\n', '\0');
 321         tbytes += nbytes;
 322         if (tbytes >= bufsize)
 323                 return (0);
 324         return (tbytes);
 325 }
 326 
 327 /*
 328  * Adds a nvpair, using the `name' and `value', to the nvlist in `nvl'.
 329  * The value will be interpreted as explained at the top of this file.
 330  */
 331 static void
 332 i_ipadm_add_nvpair(nvlist_t *nvl, char *name, char *value)
 333 {
 334         ipadm_conf_ent_t        *ipent;
 335 
 336         ipent = i_ipadm_find_conf_type(name);
 337         (*ipent->ipent_rfunc)(nvl, name, value);
 338 }
 339 
 340 /*
 341  * Adds an nvpair for IPv4 addr to the nvlist. The "name" is the string in
 342  * IPADM_NVP_IPV4ADDR. The "value" for IPADM_NVP_IPV4ADDR is another nvlist.
 343  * Allocate the value nvlist for IPADM_NVP_IPV4ADDR if necessary, and add
 344  * the address and hostnames from the address object `ipaddr' to it.
 345  * Then add the allocated nvlist to `nvl'.
 346  */
 347 ipadm_status_t
 348 i_ipadm_add_ipaddr2nvl(nvlist_t *nvl, ipadm_addrobj_t ipaddr)
 349 {
 350         nvlist_t                *nvl_addr = NULL;
 351         int                     err;
 352         char                    *name;
 353         sa_family_t             af = ipaddr->ipadm_af;
 354 
 355         if (af == AF_INET) {
 356                 name = IPADM_NVP_IPV4ADDR;
 357         } else {
 358                 assert(af == AF_INET6);
 359                 name = IPADM_NVP_IPV6ADDR;
 360         }
 361 
 362         if (!nvlist_exists(nvl, name)) {
 363                 if ((err = nvlist_alloc(&nvl_addr, NV_UNIQUE_NAME, 0)) != 0)
 364                         return (ipadm_errno2status(err));
 365                 if ((err = nvlist_add_nvlist(nvl, name, nvl_addr)) != 0) {
 366                         nvlist_free(nvl_addr);
 367                         return (ipadm_errno2status(err));
 368                 }
 369                 nvlist_free(nvl_addr);
 370         }
 371         if ((err = nvlist_lookup_nvlist(nvl, name, &nvl_addr)) != 0 ||
 372             (err = nvlist_add_string(nvl_addr, IPADM_NVP_IPADDRHNAME,
 373             ipaddr->ipadm_static_aname)) != 0)
 374                 return (ipadm_errno2status(err));
 375         if (ipaddr->ipadm_static_dname[0] != '\0') {
 376                 if ((err = nvlist_add_string(nvl_addr, IPADM_NVP_IPDADDRHNAME,
 377                     ipaddr->ipadm_static_dname)) != 0)
 378                         return (ipadm_errno2status(err));
 379         }
 380 
 381         return (IPADM_SUCCESS);
 382 }
 383 
 384 /*
 385  * Adds an nvpair for IPv6 interface id to the nvlist. The "name" is
 386  * the string in IPADM_NVP_INTFID. The "value" for IPADM_NVP_INTFID is another
 387  * nvlist. Allocate the value nvlist for IPADM_NVP_INTFID if necessary, and add
 388  * the interface id and its prefixlen from the address object `ipaddr' to it.
 389  * Then add the allocated nvlist to `nvl'.
 390  */
 391 ipadm_status_t
 392 i_ipadm_add_intfid2nvl(nvlist_t *nvl, ipadm_addrobj_t addr)
 393 {
 394         nvlist_t        *nvl_addr = NULL;
 395         struct in6_addr addr6;
 396         int             err;
 397 
 398         if (!nvlist_exists(nvl, IPADM_NVP_INTFID)) {
 399                 if ((err = nvlist_alloc(&nvl_addr, NV_UNIQUE_NAME, 0)) != 0)
 400                         return (ipadm_errno2status(err));
 401                 if ((err = nvlist_add_nvlist(nvl, IPADM_NVP_INTFID,
 402                     nvl_addr)) != 0) {
 403                         nvlist_free(nvl_addr);
 404                         return (ipadm_errno2status(err));
 405                 }
 406                 nvlist_free(nvl_addr);
 407         }
 408         if ((err = nvlist_lookup_nvlist(nvl, IPADM_NVP_INTFID,
 409             &nvl_addr)) != 0 || (err = nvlist_add_uint32(nvl_addr,
 410             IPADM_NVP_PREFIXLEN, addr->ipadm_intfidlen)) != 0) {
 411                 return (ipadm_errno2status(err));
 412         }
 413         addr6 = addr->ipadm_intfid.sin6_addr;
 414         if ((err = nvlist_add_uint8_array(nvl_addr, IPADM_NVP_IPNUMADDR,
 415             addr6.s6_addr, 16)) != 0) {
 416                 return (ipadm_errno2status(err));
 417         }
 418         if (addr->ipadm_stateless)
 419                 err = nvlist_add_string(nvl_addr, IPADM_NVP_STATELESS, "yes");
 420         else
 421                 err = nvlist_add_string(nvl_addr, IPADM_NVP_STATELESS, "no");
 422         if (err != 0)
 423                 return (ipadm_errno2status(err));
 424         if (addr->ipadm_stateful)
 425                 err = nvlist_add_string(nvl_addr, IPADM_NVP_STATEFUL, "yes");
 426         else
 427                 err = nvlist_add_string(nvl_addr, IPADM_NVP_STATEFUL, "no");
 428         if (err != 0)
 429                 return (ipadm_errno2status(err));
 430 
 431         return (IPADM_SUCCESS);
 432 }
 433 
 434 /*
 435  * Adds an nvpair for a dhcp address object to the nvlist. The "name" is
 436  * the string in IPADM_NVP_DHCP. The "value" for IPADM_NVP_DHCP is another
 437  * nvlist. Allocate the value nvlist for IPADM_NVP_DHCP if necessary, and add
 438  * the parameters from the arguments `primary' and `wait'.
 439  * Then add the allocated nvlist to `nvl'.
 440  */
 441 ipadm_status_t
 442 i_ipadm_add_dhcp2nvl(nvlist_t *nvl, boolean_t primary, int32_t wait)
 443 {
 444         nvlist_t        *nvl_dhcp = NULL;
 445         int             err;
 446 
 447         if (!nvlist_exists(nvl, IPADM_NVP_DHCP)) {
 448                 if ((err = nvlist_alloc(&nvl_dhcp, NV_UNIQUE_NAME, 0)) != 0)
 449                         return (ipadm_errno2status(err));
 450                 if ((err = nvlist_add_nvlist(nvl, IPADM_NVP_DHCP,
 451                     nvl_dhcp)) != 0) {
 452                         nvlist_free(nvl_dhcp);
 453                         return (ipadm_errno2status(err));
 454                 }
 455                 nvlist_free(nvl_dhcp);
 456         }
 457         if ((err = nvlist_lookup_nvlist(nvl, IPADM_NVP_DHCP, &nvl_dhcp)) != 0 ||
 458             (err = nvlist_add_int32(nvl_dhcp, IPADM_NVP_WAIT, wait)) != 0 ||
 459             (err = nvlist_add_boolean_value(nvl_dhcp, IPADM_NVP_PRIMARY,
 460             primary)) != 0) {
 461                 return (ipadm_errno2status(err));
 462         }
 463 
 464         return (IPADM_SUCCESS);
 465 }
 466 
 467 /*
 468  * Add (name, value) as an nvpair of type DATA_TYPE_STRING to nvlist.
 469  */
 470 static void
 471 i_ipadm_str_dbline2nvl(nvlist_t *nvl, char *name, char *value)
 472 {
 473         /* if value is NULL create an empty node */
 474         if (value == NULL)
 475                 (void) nvlist_add_string(nvl, name, "");
 476         else
 477                 (void) nvlist_add_string(nvl, name, value);
 478 }
 479 
 480 /*
 481  * `name' = IPADM_NVP_IPV4ADDR and
 482  * `value' = <local numeric IP string or hostname,remote numeric IP string or
 483  *     hostname>
 484  * This function will add an nvlist with the hostname information in
 485  * nvpairs to the nvlist in `nvl'.
 486  */
 487 static void
 488 i_ipadm_ip4_dbline2nvl(nvlist_t *nvl, char *name, char *value)
 489 {
 490         char                    *cp, *hname;
 491         struct ipadm_addrobj_s  ipaddr;
 492 
 493         assert(strcmp(name, IPADM_NVP_IPV4ADDR) == 0 && value != NULL);
 494 
 495         bzero(&ipaddr, sizeof (ipaddr));
 496         ipaddr.ipadm_af = AF_INET;
 497 
 498         hname = value; /* local hostname */
 499         cp = strchr(hname, ',');
 500         assert(cp != NULL);
 501         *cp++ = '\0';
 502         (void) strlcpy(ipaddr.ipadm_static_aname, hname,
 503             sizeof (ipaddr.ipadm_static_aname));
 504 
 505         if (*cp != '\0') {
 506                 /* we have a dst hostname */
 507                 (void) strlcpy(ipaddr.ipadm_static_dname, cp,
 508                     sizeof (ipaddr.ipadm_static_dname));
 509         }
 510         (void) i_ipadm_add_ipaddr2nvl(nvl, &ipaddr);
 511 }
 512 
 513 /*
 514  * `name' = IPADM_NVP_IPV6ADDR and
 515  * `value' = <local numeric IP string or hostname,remote numeric IP string or
 516  *     hostname>
 517  * This function will add an nvlist with the hostname information in
 518  * nvpairs to the nvlist in `nvl'.
 519  */
 520 static void
 521 i_ipadm_ip6_dbline2nvl(nvlist_t *nvl, char *name, char *value)
 522 {
 523         char                    *cp, *hname;
 524         struct ipadm_addrobj_s  ipaddr;
 525 
 526         assert(strcmp(name, IPADM_NVP_IPV6ADDR) == 0 && value != NULL);
 527 
 528         bzero(&ipaddr, sizeof (ipaddr));
 529         ipaddr.ipadm_af = AF_INET6;
 530 
 531         hname = value; /* local hostname */
 532         cp = strchr(hname, ',');
 533         assert(cp != NULL);
 534         *cp++ = '\0';
 535         (void) strlcpy(ipaddr.ipadm_static_aname, hname,
 536             sizeof (ipaddr.ipadm_static_aname));
 537 
 538         if (*cp != '\0') {
 539                 /* we have a dst hostname */
 540                 (void) strlcpy(ipaddr.ipadm_static_dname, cp,
 541                     sizeof (ipaddr.ipadm_static_dname));
 542         }
 543         (void) i_ipadm_add_ipaddr2nvl(nvl, &ipaddr);
 544 }
 545 
 546 /*
 547  * `name' = IPADM_NVP_INTFID and `value' = <intfid/prefixlen>,{yes,no},{yes|no}
 548  * This function will add an nvlist with the address object information in
 549  * nvpairs to the nvlist in `nvl'.
 550  */
 551 static void
 552 i_ipadm_intfid_dbline2nvl(nvlist_t *nvl, char *name, char *value)
 553 {
 554         char                    *cp;
 555         struct ipadm_addrobj_s  ipaddr;
 556         char                    *endp;
 557         char                    *prefixlen;
 558         char                    *stateless;
 559         char                    *stateful;
 560 
 561         assert(strcmp(name, IPADM_NVP_INTFID) == 0 && value != NULL);
 562 
 563         bzero(&ipaddr, sizeof (ipaddr));
 564 
 565         cp = strchr(value, '/');
 566         assert(cp != NULL);
 567 
 568         *cp++ = '\0';
 569         ipaddr.ipadm_intfid.sin6_family = AF_INET6;
 570         (void) inet_pton(AF_INET6, value, &ipaddr.ipadm_intfid.sin6_addr);
 571 
 572         prefixlen = cp;
 573         cp = strchr(cp, ',');
 574         assert(cp != NULL);
 575         *cp++ = '\0';
 576 
 577         errno = 0;
 578         ipaddr.ipadm_intfidlen = (uint32_t)strtoul(prefixlen, &endp, 10);
 579         if (*endp != '\0' || errno != 0)
 580                 return;
 581 
 582         stateless = cp;
 583         stateful = strchr(stateless, ',');
 584         assert(stateful != NULL);
 585         *stateful++ = '\0';
 586         ipaddr.ipadm_stateless = (strcmp(stateless, "yes") == 0);
 587         ipaddr.ipadm_stateful = (strcmp(stateful, "yes") == 0);
 588 
 589         /* Add all of it to the given nvlist */
 590         (void) i_ipadm_add_intfid2nvl(nvl, &ipaddr);
 591 }
 592 
 593 /*
 594  * `name' = IPADM_NVP_DHCP and `value' = <wait_time>,{yes|no}
 595  * This function will add an nvlist with the dhcp address object information in
 596  * nvpairs to the nvlist in `nvl'.
 597  */
 598 static void
 599 i_ipadm_dhcp_dbline2nvl(nvlist_t *nvl, char *name, char *value)
 600 {
 601         char            *cp;
 602         char            *endp;
 603         long            wait_time;
 604         boolean_t       primary;
 605 
 606         assert(strcmp(name, IPADM_NVP_DHCP) == 0 && value != NULL);
 607         cp = strchr(value, ',');
 608         assert(cp != NULL);
 609         *cp++ = '\0';
 610         errno = 0;
 611         wait_time = strtol(value, &endp, 10);
 612         if (*endp != '\0' || errno != 0)
 613                 return;
 614         primary = (strcmp(cp, "yes") == 0);
 615         (void) i_ipadm_add_dhcp2nvl(nvl, primary, (int32_t)wait_time);
 616 }
 617 
 618 /*
 619  * Parses the buffer, for name-value pairs and creates nvlist. The value
 620  * is always considered to be a string.
 621  */
 622 int
 623 ipadm_str2nvlist(const char *inbuf, nvlist_t **ipnvl, uint_t flags)
 624 {
 625         char    *nv, *name, *val, *buf, *cp, *sep;
 626         int     err;
 627 
 628         if (inbuf == NULL || inbuf[0] == '\0' || ipnvl == NULL)
 629                 return (EINVAL);
 630         *ipnvl = NULL;
 631 
 632         /*
 633          * If IPADM_NORVAL is set, then `inbuf' should be comma delimited values
 634          */
 635         if ((flags & IPADM_NORVAL) && strchr(inbuf, '=') != NULL)
 636                 return (EINVAL);
 637 
 638         if ((cp = buf = strdup(inbuf)) == NULL)
 639                 return (errno);
 640 
 641         while (isspace(*buf))
 642                 buf++;
 643 
 644         if (*buf == '\0') {
 645                 err = EINVAL;
 646                 goto fail;
 647         }
 648 
 649         nv = buf;
 650         /*
 651          * work on one nvpair at a time and extract the name and value
 652          */
 653         sep = ((flags & IPADM_NORVAL) ? IPADM_NAME_SEP : IPADM_NVPAIR_SEP);
 654         while ((nv = strsep(&buf, sep)) != NULL) {
 655                 if (*nv == '\n')
 656                         continue;
 657                 name = nv;
 658                 if ((val = strchr(nv, '=')) != NULL)
 659                         *val++ = '\0';
 660                 if (*ipnvl == NULL &&
 661                     (err = nvlist_alloc(ipnvl, NV_UNIQUE_NAME, 0)) != 0)
 662                         goto fail;
 663                 if (nvlist_exists(*ipnvl, name)) {
 664                         err = EEXIST;
 665                         goto fail;
 666                 }
 667                 /* Add the extracted nvpair to the nvlist `ipnvl'. */
 668                 (void) i_ipadm_add_nvpair(*ipnvl, name, val);
 669         }
 670         free(cp);
 671         return (0);
 672 fail:
 673         free(cp);
 674         nvlist_free(*ipnvl);
 675         *ipnvl = NULL;
 676         return (err);
 677 }
 678 
 679 /*
 680  * Opens the data store for read/write operation. For write operation we open
 681  * another file and scribble the changes to it and copy the new file back to
 682  * old file.
 683  */
 684 int
 685 ipadm_rw_db(db_wfunc_t *db_walk_func, void *arg, const char *db_file,
 686     mode_t db_perms, ipadm_db_op_t db_op)
 687 {
 688         FILE            *fp, *nfp = NULL;
 689         char            file[MAXPATHLEN];
 690         char            newfile[MAXPATHLEN];
 691         int             nfd;
 692         boolean_t       writeop;
 693         int             err = 0;
 694 
 695         writeop = (db_op != IPADM_DB_READ);
 696 
 697         (void) snprintf(file, MAXPATHLEN, "%s/%s", ipadm_rootdir, db_file);
 698 
 699         /* open the data store */
 700         if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL)
 701                 return (errno);
 702 
 703         if (writeop) {
 704                 (void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
 705                     ipadm_rootdir, db_file);
 706                 if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
 707                     db_perms)) < 0) {
 708                         err = errno;
 709                         (void) fclose(fp);
 710                         return (err);
 711                 }
 712 
 713                 if ((nfp = fdopen(nfd, "w")) == NULL) {
 714                         err = errno;
 715                         (void) close(nfd);
 716                         (void) fclose(fp);
 717                         (void) unlink(newfile);
 718                         return (err);
 719                 }
 720         }
 721         err = ipadm_process_db_line(db_walk_func, arg, fp, nfp, db_op);
 722         if (!writeop)
 723                 goto done;
 724         if (err != 0 && err != ENOENT)
 725                 goto done;
 726 
 727         if (fflush(nfp) == EOF) {
 728                 err = errno;
 729                 goto done;
 730         }
 731         (void) fclose(fp);
 732         (void) fclose(nfp);
 733 
 734         if (rename(newfile, file) < 0) {
 735                 err = errno;
 736                 (void) unlink(newfile);
 737         }
 738         return (err);
 739 done:
 740         if (nfp != NULL) {
 741                 (void) fclose(nfp);
 742                 if (err != 0)
 743                         (void) unlink(newfile);
 744         }
 745         (void) fclose(fp);
 746         return (err);
 747 }
 748 
 749 /*
 750  * Processes each line of the configuration file, skipping lines with
 751  * leading spaces, blank lines and comments. The line form the DB
 752  * is converted to nvlist and the callback function is called to process
 753  * the list. The buf could be modified by the callback function and
 754  * if this is a write operation and buf is not truncated, buf will
 755  * be written to disk.
 756  *
 757  * Further if cont is set to B_FALSE,  the remainder of the file will
 758  * continue to be read (however callback function will not be called) and,
 759  * if necessary, written to disk as well.
 760  */
 761 static int
 762 ipadm_process_db_line(db_wfunc_t *db_walk_func, void *arg, FILE *fp, FILE *nfp,
 763     ipadm_db_op_t db_op)
 764 {
 765         int             err = 0;
 766         char            buf[MAXLINELEN];
 767         boolean_t       cont = B_TRUE;
 768         int             i, len;
 769         nvlist_t        *db_nvl = NULL;
 770         boolean_t       line_deleted = B_FALSE;
 771 
 772         while (fgets(buf, MAXLINELEN, fp) != NULL) {
 773                 /*
 774                  * Skip leading spaces, blank lines, and comments.
 775                  */
 776                 len = strnlen(buf, MAXLINELEN);
 777                 for (i = 0; i < len; i++) {
 778                         if (!isspace(buf[i]))
 779                                 break;
 780                 }
 781 
 782                 if (i != len && buf[i] != '#' && cont) {
 783                         if (ipadm_str2nvlist(buf, &db_nvl, 0) == 0) {
 784                                 cont = db_walk_func(arg, db_nvl, buf,
 785                                     MAXLINELEN, &err);
 786                         } else {
 787                                 /* Delete corrupted line. */
 788                                 buf[0] = '\0';
 789                         }
 790                         nvlist_free(db_nvl);
 791                         db_nvl = NULL;
 792                 }
 793                 if (err != 0)
 794                         break;
 795                 if (nfp != NULL && buf[0] == '\0')
 796                         line_deleted = B_TRUE;
 797                 if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) {
 798                         err = errno;
 799                         break;
 800                 }
 801         }
 802 
 803         if (err != 0 || !cont)
 804                 return (err);
 805 
 806         if (db_op == IPADM_DB_WRITE) {
 807                 nvlist_t        *nvl;
 808 
 809                 /*
 810                  * `arg' will be NULL when we are doing in-line update of
 811                  * entries.
 812                  */
 813                 if (arg != NULL) {
 814                         nvl = ((ipadm_dbwrite_cbarg_t *)arg)->dbw_nvl;
 815                         /*
 816                          * If the specified entry is not found above, we add
 817                          * the entry to the configuration file, here.
 818                          */
 819                         (void) memset(buf, 0, MAXLINELEN);
 820                         if (ipadm_nvlist2str(nvl, buf, MAXLINELEN) == 0)
 821                                 err = ENOBUFS;
 822                         else if (fputs(buf, nfp) == EOF)
 823                                 err = errno;
 824                 }
 825                 return (err);
 826         }
 827 
 828         if (db_op == IPADM_DB_DELETE && line_deleted)
 829                 return (0);
 830 
 831         /* if we have come this far, then we didn't find any match */
 832         return (ENOENT);
 833 }