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