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
83static char ipadm_rootdir[MAXPATHLEN] = "/";
84
85static 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 */
91typedef 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 */
97typedef void  ipadm_rfunc_t(nvlist_t *, char *name, char *value);
98
99static 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
103static 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 */
111typedef 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
117static 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
126static ipadm_conf_ent_t *
127i_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 */
141size_t
142i_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 */
168static size_t
169i_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);
183fail:
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 */
194static size_t
195i_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);
209fail:
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 */
219static size_t
220i_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));
248fail:
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 */
258static size_t
259i_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 */
284static size_t
285i_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 */
299size_t
300ipadm_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 */
331static void
332i_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 */
347ipadm_status_t
348i_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 */
391ipadm_status_t
392i_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 */
441ipadm_status_t
442i_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 */
470static void
471i_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 */
487static void
488i_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 */
520static void
521i_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 */
551static void
552i_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 */
598static void
599i_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 */
622int
623ipadm_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);
672fail:
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 */
684int
685ipadm_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);
739done:
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 */
761static int
762ipadm_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