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