view usr/src/lib/libipadm/common/ipadm_persist.c @ 12016:0248e987199b

PSARC 2009/306 Brussels II - ipadm and libipadm PSARC 2010/080 Brussels II addendum 6827318 Brussels Phase II aka ipadm(1m) 6731945 need BSD getifaddrs() API 6909065 explicitly disallow non-contiguous netmasks in the next minor release 6853922 ifconfig dumps core when ether address is non-hexadecimal. 6815806 ipReasmTimeout value should be variable 6567083 nd_getset has some dead and confusing code. 6884466 remove unused tcp/sctp ndd tunables 6928813 Comments at odds with default value of tcp_time_wait_interval 6236982 ifconfig usesrc lets adapter use itself as source address 6936855 modifying the ip6_strict_src_multihoming to non-zero value will unbind V4 IREs
author Girish Moodalbail <Girish.Moodalbail@Sun.COM>
date Fri, 26 Mar 2010 17:53:11 -0400
parents
children 9b5ab8584c21
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * This file contains routines to read/write formatted entries from/to
 * libipadm data store /etc/ipadm/ipadm.conf. Each entry in the DB is a
 * series of IPADM_NVPAIR_SEP separated (name, value) pairs, as shown
 * below:
 *		name=value[;...]
 *
 * The 'name' determines how to interpret 'value'. The supported names are:
 *
 *  IPADM_NVP_IPV6ADDR - value holds local and remote IPv6 addresses and when
 *	       converted to nvlist, will contain nvpairs for local and remote
 *	       addresses. These nvpairs are of type DATA_TYPE_STRING
 *
 *  IPADM_NVP_IPV4ADDR - value holds local and remote IPv4 addresses and when
 *	       converted to nvlist, will contain nvpairs for local and remote
 *	       addresses. These nvpairs are of type DATA_TYPE_STRING
 *
 *  IPADM_NVP_INTFID - value holds token, prefixlen, stateless and stateful
 *	       info and when converted to nvlist, will contain following nvpairs
 *			interface_id: DATA_TYPE_UINT8_ARRAY
 *			prefixlen: DATA_TYPE_UINT32
 *			stateless: DATA_TYPE_STRING
 *			stateful: DATA_TYPE_STRING
 *
 *  IPADM_NVP_DHCP - value holds wait time and primary info and when converted
 *	       to nvlist, will contain following nvpairs
 *			wait:	DATA_TYPE_INT32
 *			primary: DATA_TYPE_BOOLEAN
 *
 *  default  - value is a single entity and when converted to nvlist, will
 *	       contain nvpair of type DATA_TYPE_STRING. nvpairs private to
 *	       ipadm are of this type. Further the property name and property
 *	       values are stored as nvpairs of this type.
 *
 * The syntax for each line is described above the respective functions below.
 */

#include <stdlib.h>
#include <strings.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dld.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/sockio.h>
#include "libipadm_impl.h"

#define	MAXLINELEN		1024
#define	IPADM_NVPAIR_SEP	";"
#define	IPADM_NAME_SEP		","

static char ipadm_rootdir[MAXPATHLEN] = "/";

static int ipadm_process_db_line(db_wfunc_t *, void *, FILE *fp, FILE *nfp,
    ipadm_db_op_t);

/*
 * convert nvpair to a "name=value" string for writing to the DB.
 */
typedef size_t  ipadm_wfunc_t(nvpair_t *, char *, size_t);

/*
 * ipadm_rfunc_t takes (`name', `value') and adds the appropriately typed
 * nvpair to the nvlist.
 */
typedef void  ipadm_rfunc_t(nvlist_t *, char *name, char *value);

static ipadm_rfunc_t	i_ipadm_str_dbline2nvl, i_ipadm_ip4_dbline2nvl,
			i_ipadm_ip6_dbline2nvl, i_ipadm_intfid_dbline2nvl,
			i_ipadm_dhcp_dbline2nvl;

static ipadm_wfunc_t	i_ipadm_str_nvp2dbline, i_ipadm_ip4_nvp2dbline,
			i_ipadm_ip6_nvp2dbline, i_ipadm_intfid_nvp2dbline,
			i_ipadm_dhcp_nvp2dbline;

/*
 * table of function pointers to read/write formatted entries from/to
 * ipadm.conf.
 */
typedef struct ipadm_conf_ent_s {
	const char		*ipent_type_name;
	ipadm_wfunc_t		*ipent_wfunc;
	ipadm_rfunc_t		*ipent_rfunc;
} ipadm_conf_ent_t;

static ipadm_conf_ent_t ipadm_conf_ent[] = {
	{ IPADM_NVP_IPV6ADDR, i_ipadm_ip6_nvp2dbline, i_ipadm_ip6_dbline2nvl },
	{ IPADM_NVP_IPV4ADDR, i_ipadm_ip4_nvp2dbline, i_ipadm_ip4_dbline2nvl },
	{ IPADM_NVP_INTFID, i_ipadm_intfid_nvp2dbline,
	    i_ipadm_intfid_dbline2nvl },
	{ IPADM_NVP_DHCP, i_ipadm_dhcp_nvp2dbline, i_ipadm_dhcp_dbline2nvl },
	{ NULL,	i_ipadm_str_nvp2dbline,	i_ipadm_str_dbline2nvl }
};

static ipadm_conf_ent_t *
i_ipadm_find_conf_type(const char *type)
{
	int	i;

	for (i = 0; ipadm_conf_ent[i].ipent_type_name != NULL; i++)
		if (strcmp(type, ipadm_conf_ent[i].ipent_type_name) == 0)
			break;
	return (&ipadm_conf_ent[i]);
}

/*
 * Extracts the hostnames IPADM_NVP_IPADDRHNAME and IPADM_NVP_IPDADDRHNAME from
 * the given nvlist `nvl' and adds the strings to `buf'.
 */
size_t
i_ipadm_ip_addhostname2dbline(nvlist_t *nvl, char *buf, size_t buflen)
{
	char	*cp;
	char	tmpbuf[IPADM_STRSIZE];

	/* Add the local hostname */
	if (nvlist_lookup_string(nvl, IPADM_NVP_IPADDRHNAME, &cp) != 0)
		return (0);
	(void) strlcat(buf, cp, buflen); /* local hostname */

	/* Add the dst hostname */
	if (nvlist_lookup_string(nvl, IPADM_NVP_IPDADDRHNAME, &cp) != 0) {
		/* no dst addr. just add a NULL character */
		(void) snprintf(tmpbuf, sizeof (tmpbuf), ",");
	} else {
		(void) snprintf(tmpbuf, sizeof (tmpbuf), ",%s", cp);
	}
	return (strlcat(buf, tmpbuf, buflen));
}

/*
 * Converts IPADM_NVP_IPV4ADDR nvpair to a string representation for writing to
 * the DB. The converted string format:
 *	ipv4addr=<local numeric IP string or hostname,remote numeric IP
 *          string or hostname>
 */
static size_t
i_ipadm_ip4_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
{
	nvlist_t	*v;
	int		nbytes;

	assert(nvpair_type(nvp) == DATA_TYPE_NVLIST &&
	    strcmp(nvpair_name(nvp), IPADM_NVP_IPV4ADDR) == 0);

	(void) snprintf(buf, buflen, "%s=", IPADM_NVP_IPV4ADDR);
	if (nvpair_value_nvlist(nvp, &v) != 0)
		goto fail;
	nbytes = i_ipadm_ip_addhostname2dbline(v, buf, buflen);
	if (nbytes != 0)
		return (nbytes);
fail:
	buf[0] = '\0';
	return (0);
}

/*
 * Converts IPADM_NVP_IPV6ADDR nvpair to a string representation for writing to
 * the DB. The converted string format:
 *	ipv6addr=<local numeric IP string or hostname,remote numeric IP
 *          string or hostname>
 */
static size_t
i_ipadm_ip6_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
{
	nvlist_t	*v;
	int		nbytes;

	assert(nvpair_type(nvp) == DATA_TYPE_NVLIST &&
	    strcmp(nvpair_name(nvp), IPADM_NVP_IPV6ADDR) == 0);

	(void) snprintf(buf, buflen, "%s=", IPADM_NVP_IPV6ADDR);
	if (nvpair_value_nvlist(nvp, &v) != 0)
		goto fail;
	nbytes = i_ipadm_ip_addhostname2dbline(v, buf, buflen);
	if (nbytes != 0)
		return (nbytes);
fail:
	buf[0] = '\0';
	return (0);
}

/*
 * Converts IPADM_NVP_INTFID nvpair to a string representation for writing to
 * the DB. The converted string format:
 *	IPADM_NVP_INTFID=<intfid/prefixlen>,{yes|no},{yes|no}
 */
static size_t
i_ipadm_intfid_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
{
	char		addrbuf[IPADM_STRSIZE];
	nvlist_t	*v;
	uint32_t	prefixlen;
	struct in6_addr	in6addr;
	char		*stateless;
	char		*stateful;

	assert(nvpair_type(nvp) == DATA_TYPE_NVLIST &&
	    strcmp(nvpair_name(nvp), IPADM_NVP_INTFID) == 0);

	(void) snprintf(buf, buflen, "%s=", IPADM_NVP_INTFID);
	if (nvpair_value_nvlist(nvp, &v) != 0)
		goto fail;
	if (i_ipadm_nvl2in6_addr(v, IPADM_NVP_IPNUMADDR, &in6addr) !=
	    IPADM_SUCCESS)
		goto fail;
	(void) inet_ntop(AF_INET6, &in6addr, addrbuf,
	    sizeof (addrbuf));
	(void) strlcat(buf, addrbuf, buflen);
	if (nvlist_lookup_uint32(v, IPADM_NVP_PREFIXLEN, &prefixlen) != 0 ||
	    nvlist_lookup_string(v, IPADM_NVP_STATELESS, &stateless) != 0 ||
	    nvlist_lookup_string(v, IPADM_NVP_STATEFUL, &stateful) != 0)
		goto fail;
	(void) snprintf(addrbuf, sizeof (addrbuf), "/%d,%s,%s",
	    prefixlen, stateless, stateful);
	return (strlcat(buf, addrbuf, buflen));
fail:
	buf[0] = '\0';
	return (0);
}

/*
 * Converts IPADM_NVP_DHCP nvpair to a string representation for writing to the
 * DB. The converted string format:
 *	IPADM_NVP_DHCP=<wait_time>,{yes|no}
 */
static size_t
i_ipadm_dhcp_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
{
	char		addrbuf[IPADM_STRSIZE];
	int32_t 	wait;
	boolean_t	primary;
	nvlist_t	*v;

	assert(nvpair_type(nvp) == DATA_TYPE_NVLIST &&
	    strcmp(nvpair_name(nvp), IPADM_NVP_DHCP) == 0);

	if (nvpair_value_nvlist(nvp, &v) != 0 ||
	    nvlist_lookup_int32(v, IPADM_NVP_WAIT, &wait) != 0 ||
	    nvlist_lookup_boolean_value(v, IPADM_NVP_PRIMARY, &primary) != 0) {
		return (0);
	}
	(void) snprintf(buf, buflen, "%s=", IPADM_NVP_DHCP);
	(void) snprintf(addrbuf, sizeof (addrbuf), "%d,%s", wait,
	    (primary ? "yes" : "no"));
	return (strlcat(buf, addrbuf, buflen));
}

/*
 * Constructs a "<name>=<value>" string from the nvpair, whose type must
 * be STRING.
 */
static size_t
i_ipadm_str_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
{
	char	*str = NULL;

	assert(nvpair_type(nvp) == DATA_TYPE_STRING);
	if (nvpair_value_string(nvp, &str) != 0)
		return (0);
	return (snprintf(buf, buflen, "%s=%s", nvpair_name(nvp), str));
}

/*
 * Converts a nvlist to string of the form:
 *  <prop0>=<val0>,...,<valn>;...;<propn>=<val0>,...,<valn>;
 */
size_t
ipadm_nvlist2str(nvlist_t *nvl, char *buf, size_t buflen)
{
	nvpair_t	*nvp = NULL;
	uint_t		nbytes = 0, tbytes = 0;
	ipadm_conf_ent_t *ipent;
	size_t		bufsize = buflen;

	for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
	    nvp = nvlist_next_nvpair(nvl, nvp)) {
		ipent = i_ipadm_find_conf_type(nvpair_name(nvp));
		nbytes = (*ipent->ipent_wfunc)(nvp, buf, buflen);
		/* add nvpair separator */
		nbytes += snprintf(buf + nbytes, buflen - nbytes, "%s",
		    IPADM_NVPAIR_SEP);
		buflen -= nbytes;
		buf += nbytes;
		tbytes += nbytes;
		if (tbytes >= bufsize)	/* buffer overflow */
			return (0);
	}
	nbytes = snprintf(buf, buflen, "%c%c", '\n', '\0');
	tbytes += nbytes;
	if (tbytes >= bufsize)
		return (0);
	return (tbytes);
}

/*
 * Adds a nvpair, using the `name' and `value', to the nvlist in `nvl'.
 * The value will be interpreted as explained at the top of this file.
 */
static void
i_ipadm_add_nvpair(nvlist_t *nvl, char *name, char *value)
{
	ipadm_conf_ent_t	*ipent;

	ipent = i_ipadm_find_conf_type(name);
	(*ipent->ipent_rfunc)(nvl, name, value);
}

/*
 * Adds an nvpair for IPv4 addr to the nvlist. The "name" is the string in
 * IPADM_NVP_IPV4ADDR. The "value" for IPADM_NVP_IPV4ADDR is another nvlist.
 * Allocate the value nvlist for IPADM_NVP_IPV4ADDR if necessary, and add
 * the address and hostnames from the address object `ipaddr' to it.
 * Then add the allocated nvlist to `nvl'.
 */
ipadm_status_t
i_ipadm_add_ipaddr2nvl(nvlist_t *nvl, ipadm_addrobj_t ipaddr)
{
	nvlist_t		*nvl_addr = NULL;
	int			err;
	char			*name;
	sa_family_t		af = ipaddr->ipadm_af;

	if (af == AF_INET) {
		name = IPADM_NVP_IPV4ADDR;
	} else {
		assert(af == AF_INET6);
		name = IPADM_NVP_IPV6ADDR;
	}

	if (!nvlist_exists(nvl, name)) {
		if ((err = nvlist_alloc(&nvl_addr, NV_UNIQUE_NAME, 0)) != 0)
			return (ipadm_errno2status(err));
		if ((err = nvlist_add_nvlist(nvl, name, nvl_addr)) != 0) {
			nvlist_free(nvl_addr);
			return (ipadm_errno2status(err));
		}
		nvlist_free(nvl_addr);
	}
	if ((err = nvlist_lookup_nvlist(nvl, name, &nvl_addr)) != 0 ||
	    (err = nvlist_add_string(nvl_addr, IPADM_NVP_IPADDRHNAME,
	    ipaddr->ipadm_static_aname)) != 0)
		return (ipadm_errno2status(err));
	if (!sockaddrunspec(&ipaddr->ipadm_static_dst_addr)) {
		if ((err = nvlist_add_string(nvl_addr, IPADM_NVP_IPDADDRHNAME,
		    ipaddr->ipadm_static_dname)) != 0)
			return (ipadm_errno2status(err));
	}

	return (IPADM_SUCCESS);
}

/*
 * Adds an nvpair for IPv6 interface id to the nvlist. The "name" is
 * the string in IPADM_NVP_INTFID. The "value" for IPADM_NVP_INTFID is another
 * nvlist. Allocate the value nvlist for IPADM_NVP_INTFID if necessary, and add
 * the interface id and its prefixlen from the address object `ipaddr' to it.
 * Then add the allocated nvlist to `nvl'.
 */
ipadm_status_t
i_ipadm_add_intfid2nvl(nvlist_t *nvl, ipadm_addrobj_t addr)
{
	nvlist_t	*nvl_addr = NULL;
	struct in6_addr	addr6;
	int		err;

	if (!nvlist_exists(nvl, IPADM_NVP_INTFID)) {
		if ((err = nvlist_alloc(&nvl_addr, NV_UNIQUE_NAME, 0)) != 0)
			return (ipadm_errno2status(err));
		if ((err = nvlist_add_nvlist(nvl, IPADM_NVP_INTFID,
		    nvl_addr)) != 0) {
			nvlist_free(nvl_addr);
			return (ipadm_errno2status(err));
		}
		nvlist_free(nvl_addr);
	}
	if ((err = nvlist_lookup_nvlist(nvl, IPADM_NVP_INTFID,
	    &nvl_addr)) != 0 || (err = nvlist_add_uint32(nvl_addr,
	    IPADM_NVP_PREFIXLEN, addr->ipadm_intfidlen)) != 0) {
		return (ipadm_errno2status(err));
	}
	addr6 = addr->ipadm_intfid.sin6_addr;
	if ((err = nvlist_add_uint8_array(nvl_addr, IPADM_NVP_IPNUMADDR,
	    addr6.s6_addr, 16)) != 0) {
		return (ipadm_errno2status(err));
	}
	if (addr->ipadm_stateless)
		err = nvlist_add_string(nvl_addr, IPADM_NVP_STATELESS, "yes");
	else
		err = nvlist_add_string(nvl_addr, IPADM_NVP_STATELESS, "no");
	if (err != 0)
		return (ipadm_errno2status(err));
	if (addr->ipadm_stateful)
		err = nvlist_add_string(nvl_addr, IPADM_NVP_STATEFUL, "yes");
	else
		err = nvlist_add_string(nvl_addr, IPADM_NVP_STATEFUL, "no");
	if (err != 0)
		return (ipadm_errno2status(err));

	return (IPADM_SUCCESS);
}

/*
 * Adds an nvpair for a dhcp address object to the nvlist. The "name" is
 * the string in IPADM_NVP_DHCP. The "value" for IPADM_NVP_DHCP is another
 * nvlist. Allocate the value nvlist for IPADM_NVP_DHCP if necessary, and add
 * the parameters from the arguments `primary' and `wait'.
 * Then add the allocated nvlist to `nvl'.
 */
ipadm_status_t
i_ipadm_add_dhcp2nvl(nvlist_t *nvl, boolean_t primary, int32_t wait)
{
	nvlist_t	*nvl_dhcp = NULL;
	int		err;

	if (!nvlist_exists(nvl, IPADM_NVP_DHCP)) {
		if ((err = nvlist_alloc(&nvl_dhcp, NV_UNIQUE_NAME, 0)) != 0)
			return (ipadm_errno2status(err));
		if ((err = nvlist_add_nvlist(nvl, IPADM_NVP_DHCP,
		    nvl_dhcp)) != 0) {
			nvlist_free(nvl_dhcp);
			return (ipadm_errno2status(err));
		}
		nvlist_free(nvl_dhcp);
	}
	if ((err = nvlist_lookup_nvlist(nvl, IPADM_NVP_DHCP, &nvl_dhcp)) != 0 ||
	    (err = nvlist_add_int32(nvl_dhcp, IPADM_NVP_WAIT, wait)) != 0 ||
	    (err = nvlist_add_boolean_value(nvl_dhcp, IPADM_NVP_PRIMARY,
	    primary)) != 0) {
		return (ipadm_errno2status(err));
	}

	return (IPADM_SUCCESS);
}

/*
 * Add (name, value) as an nvpair of type DATA_TYPE_STRING to nvlist.
 */
static void
i_ipadm_str_dbline2nvl(nvlist_t *nvl, char *name, char *value)
{
	/* if value is NULL create an empty node */
	if (value == NULL)
		(void) nvlist_add_string(nvl, name, "");
	else
		(void) nvlist_add_string(nvl, name, value);
}

/*
 * `name' = IPADM_NVP_IPV4ADDR and
 * `value' = <local numeric IP string or hostname,remote numeric IP string or
 *     hostname>
 * This function will add an nvlist with the hostname information in
 * nvpairs to the nvlist in `nvl'.
 */
static void
i_ipadm_ip4_dbline2nvl(nvlist_t *nvl, char *name, char *value)
{
	char			*cp, *hname;
	struct ipadm_addrobj_s	ipaddr;

	assert(strcmp(name, IPADM_NVP_IPV4ADDR) == 0 && value != NULL);

	bzero(&ipaddr, sizeof (ipaddr));
	ipaddr.ipadm_af = AF_INET;

	hname = value; /* local hostname */
	cp = strchr(hname, ',');
	assert(cp != NULL);
	*cp++ = '\0';
	(void) strlcpy(ipaddr.ipadm_static_aname, hname,
	    sizeof (ipaddr.ipadm_static_aname));

	if (*cp != '\0') {
		/* we have a dst hostname */
		(void) strlcpy(ipaddr.ipadm_static_dname, cp,
		    sizeof (ipaddr.ipadm_static_dname));
	}
	(void) i_ipadm_add_ipaddr2nvl(nvl, &ipaddr);
}

/*
 * `name' = IPADM_NVP_IPV6ADDR and
 * `value' = <local numeric IP string or hostname,remote numeric IP string or
 *     hostname>
 * This function will add an nvlist with the hostname information in
 * nvpairs to the nvlist in `nvl'.
 */
static void
i_ipadm_ip6_dbline2nvl(nvlist_t *nvl, char *name, char *value)
{
	char			*cp, *hname;
	struct ipadm_addrobj_s	ipaddr;

	assert(strcmp(name, IPADM_NVP_IPV6ADDR) == 0 && value != NULL);

	bzero(&ipaddr, sizeof (ipaddr));
	ipaddr.ipadm_af = AF_INET6;

	hname = value; /* local hostname */
	cp = strchr(hname, ',');
	assert(cp != NULL);
	*cp++ = '\0';
	(void) strlcpy(ipaddr.ipadm_static_aname, hname,
	    sizeof (ipaddr.ipadm_static_aname));

	if (*cp != '\0') {
		/* we have a dst hostname */
		(void) strlcpy(ipaddr.ipadm_static_dname, cp,
		    sizeof (ipaddr.ipadm_static_dname));
	}
	(void) i_ipadm_add_ipaddr2nvl(nvl, &ipaddr);
}

/*
 * `name' = IPADM_NVP_INTFID and `value' = <intfid/prefixlen>,{yes,no},{yes|no}
 * This function will add an nvlist with the address object information in
 * nvpairs to the nvlist in `nvl'.
 */
static void
i_ipadm_intfid_dbline2nvl(nvlist_t *nvl, char *name, char *value)
{
	char			*cp;
	struct ipadm_addrobj_s	ipaddr;
	char			*endp;
	char			*prefixlen;
	char			*stateless;
	char			*stateful;

	assert(strcmp(name, IPADM_NVP_INTFID) == 0 && value != NULL);

	bzero(&ipaddr, sizeof (ipaddr));

	cp = strchr(value, '/');
	assert(cp != NULL);

	*cp++ = '\0';
	ipaddr.ipadm_intfid.sin6_family = AF_INET6;
	(void) inet_pton(AF_INET6, value, &ipaddr.ipadm_intfid.sin6_addr);

	prefixlen = cp;
	cp = strchr(cp, ',');
	assert(cp != NULL);
	*cp++ = '\0';

	errno = 0;
	ipaddr.ipadm_intfidlen = (uint32_t)strtoul(prefixlen, &endp, 10);
	if (*endp != '\0' || errno != 0)
		return;

	stateless = cp;
	stateful = strchr(stateless, ',');
	assert(stateful != NULL);
	*stateful++ = '\0';
	ipaddr.ipadm_stateless = (strcmp(stateless, "yes") == 0);
	ipaddr.ipadm_stateful = (strcmp(stateful, "yes") == 0);

	/* Add all of it to the given nvlist */
	(void) i_ipadm_add_intfid2nvl(nvl, &ipaddr);
}

/*
 * `name' = IPADM_NVP_DHCP and `value' = <wait_time>,{yes|no}
 * This function will add an nvlist with the dhcp address object information in
 * nvpairs to the nvlist in `nvl'.
 */
static void
i_ipadm_dhcp_dbline2nvl(nvlist_t *nvl, char *name, char *value)
{
	char		*cp;
	char		*endp;
	long		wait_time;
	boolean_t	primary;

	assert(strcmp(name, IPADM_NVP_DHCP) == 0 && value != NULL);
	cp = strchr(value, ',');
	assert(cp != NULL);
	*cp++ = '\0';
	errno = 0;
	wait_time = strtol(value, &endp, 10);
	if (*endp != '\0' || errno != 0)
		return;
	primary = (strcmp(cp, "yes") == 0);
	(void) i_ipadm_add_dhcp2nvl(nvl, primary, (int32_t)wait_time);
}

/*
 * Parses the buffer, for name-value pairs and creates nvlist. The value
 * is always considered to be a string.
 */
int
ipadm_str2nvlist(const char *inbuf, nvlist_t **ipnvl, uint_t flags)
{
	char	*nv, *name, *val, *buf, *cp, *sep;
	int	err;

	if (inbuf == NULL || inbuf[0] == '\0' || ipnvl == NULL)
		return (EINVAL);
	*ipnvl = NULL;

	/*
	 * If IPADM_NORVAL is set, then `inbuf' should be comma delimited values
	 */
	if ((flags & IPADM_NORVAL) && strchr(inbuf, '=') != NULL)
		return (EINVAL);

	if ((cp = buf = strdup(inbuf)) == NULL)
		return (errno);

	while (isspace(*buf))
		buf++;

	if (*buf == '\0') {
		err = EINVAL;
		goto fail;
	}

	nv = buf;
	/*
	 * work on one nvpair at a time and extract the name and value
	 */
	sep = ((flags & IPADM_NORVAL) ? IPADM_NAME_SEP : IPADM_NVPAIR_SEP);
	while ((nv = strsep(&buf, sep)) != NULL) {
		if (*nv == '\n')
			continue;
		name = nv;
		if ((val = strchr(nv, '=')) != NULL)
			*val++ = '\0';
		if (*ipnvl == NULL &&
		    (err = nvlist_alloc(ipnvl, NV_UNIQUE_NAME, 0)) != 0)
			goto fail;
		if (nvlist_exists(*ipnvl, name)) {
			err = EEXIST;
			goto fail;
		}
		/* Add the extracted nvpair to the nvlist `ipnvl'. */
		(void) i_ipadm_add_nvpair(*ipnvl, name, val);
	}
	free(cp);
	return (0);
fail:
	free(cp);
	nvlist_free(*ipnvl);
	*ipnvl = NULL;
	return (err);
}

/*
 * Opens the data store for read/write operation. For write operation we open
 * another file and scribble the changes to it and copy the new file back to
 * old file.
 */
int
ipadm_rw_db(db_wfunc_t *db_walk_func, void *arg, const char *db_file,
    mode_t db_perms, ipadm_db_op_t db_op)
{
	FILE		*fp, *nfp = NULL;
	char		file[MAXPATHLEN];
	char		newfile[MAXPATHLEN];
	int		nfd;
	boolean_t	writeop;
	int		err = 0;

	writeop = (db_op != IPADM_DB_READ);

	(void) snprintf(file, MAXPATHLEN, "%s/%s", ipadm_rootdir, db_file);

	/* open the data store */
	if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL)
		return (errno);

	if (writeop) {
		(void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
		    ipadm_rootdir, db_file);
		if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
		    db_perms)) < 0) {
			err = errno;
			(void) fclose(fp);
			return (err);
		}

		if ((nfp = fdopen(nfd, "w")) == NULL) {
			err = errno;
			(void) close(nfd);
			(void) fclose(fp);
			(void) unlink(newfile);
			return (err);
		}
	}
	err = ipadm_process_db_line(db_walk_func, arg, fp, nfp, db_op);
	if (!writeop)
		goto done;
	if (err != 0 && err != ENOENT)
		goto done;

	if (fflush(nfp) == EOF) {
		err = errno;
		goto done;
	}
	(void) fclose(fp);
	(void) fclose(nfp);

	if (rename(newfile, file) < 0) {
		err = errno;
		(void) unlink(newfile);
	}
	return (err);
done:
	if (nfp != NULL) {
		(void) fclose(nfp);
		if (err != 0)
			(void) unlink(newfile);
	}
	(void) fclose(fp);
	return (err);
}

/*
 * Processes each line of the configuration file, skipping lines with
 * leading spaces, blank lines and comments. The line form the DB
 * is converted to nvlist and the callback function is called to process
 * the list. The buf could be modified by the callback function and
 * if this is a write operation and buf is not truncated, buf will
 * be written to disk.
 *
 * Further if cont is set to B_FALSE,  the remainder of the file will
 * continue to be read (however callback function will not be called) and,
 * if necessary, written to disk as well.
 */
static int
ipadm_process_db_line(db_wfunc_t *db_walk_func, void *arg, FILE *fp, FILE *nfp,
    ipadm_db_op_t db_op)
{
	int		err = 0;
	char		buf[MAXLINELEN];
	boolean_t	cont = B_TRUE;
	int		i, len;
	nvlist_t	*db_nvl = NULL;
	boolean_t	line_deleted = B_FALSE;

	while (fgets(buf, MAXLINELEN, fp) != NULL) {
		/*
		 * Skip leading spaces, blank lines, and comments.
		 */
		len = strnlen(buf, MAXLINELEN);
		for (i = 0; i < len; i++) {
			if (!isspace(buf[i]))
				break;
		}

		if (i != len && buf[i] != '#' && cont) {
			if (ipadm_str2nvlist(buf, &db_nvl, 0) == 0) {
				cont = db_walk_func(arg, db_nvl, buf,
				    MAXLINELEN, &err);
			} else {
				/* Delete corrupted line. */
				buf[0] = '\0';
			}
			nvlist_free(db_nvl);
			db_nvl = NULL;
		}
		if (err != 0)
			break;
		if (nfp != NULL && buf[0] == '\0')
			line_deleted = B_TRUE;
		if (nfp != NULL	&& buf[0] != '\0' && fputs(buf, nfp) == EOF) {
			err = errno;
			break;
		}
	}

	if (err != 0 || !cont)
		return (err);

	if (db_op == IPADM_DB_WRITE) {
		ipadm_dbwrite_cbarg_t	*cb = arg;
		nvlist_t		*nvl = cb->dbw_nvl;

		/*
		 * If the specified entry is not found above, we add
		 * the entry to the configuration file, here.
		 */
		(void) memset(buf, 0, MAXLINELEN);
		if (ipadm_nvlist2str(nvl, buf, MAXLINELEN) == 0)
			err = ENOBUFS;
		else if (fputs(buf, nfp) == EOF)
			err = errno;
		return (err);
	}

	if (db_op == IPADM_DB_DELETE && line_deleted)
		return (0);

	/* if we have come this far, then we didn't find any match */
	return (ENOENT);
}