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