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