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