xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2018 Joyent, Inc.
25  * Copyright 2016 Argo Technologie SA.
26  * Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
27  * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
28  */
29 
30 /*
31  * Contains DB walker functions, which are of type `db_wfunc_t';
32  *
33  * typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf,
34  *				size_t bufsize, int *errp);
35  *
36  * ipadm_rw_db() walks through the data store, one line at a time and calls
37  * these call back functions with:
38  *	`cbarg'  - callback argument
39  *	`db_nvl' - representing a line from DB in nvlist_t form
40  *	`buf'	 - character buffer to hold modified line
41  *	`bufsize'- size of the buffer
42  *	`errp' - captures any error inside the walker function.
43  *
44  * All the 'write' callback functions modify `db_nvl' based on `cbarg' and
45  * copy string representation of `db_nvl' (using ipadm_nvlist2str()) into `buf'.
46  * To delete a line from the DB, buf[0] is set to `\0'. Inside ipadm_rw_db(),
47  * the modified `buf' is written back into DB.
48  *
49  * All the 'read' callback functions, retrieve the information from the DB, by
50  * reading `db_nvl' and then populate the `cbarg'.
51  */
52 
53 #include <stdlib.h>
54 #include <strings.h>
55 #include <errno.h>
56 #include <assert.h>
57 #include <sys/types.h>
58 #include <sys/socket.h>
59 #include <netinet/in.h>
60 #include <arpa/inet.h>
61 #include <unistd.h>
62 #include "ipmgmt_impl.h"
63 
64 /* SCF related property group names and property names */
65 #define	IPMGMTD_APP_PG		"ipmgmtd"
66 #define	IPMGMTD_PROP_FBD	"first_boot_done"
67 #define	IPMGMTD_PROP_DBVER	"datastore_version"
68 #define	IPMGMTD_TRUESTR		"true"
69 
70 #define	ATYPE	"_atype"		/* name of the address type nvpair */
71 #define	FLAGS	"_flags"		/* name of the flags nvpair */
72 
73 /*
74  * flag used by ipmgmt_persist_aobjmap() to indicate address type is
75  * IPADM_ADDR_IPV6_ADDRCONF.
76  */
77 #define	IPMGMT_ATYPE_V6ACONF	0x1
78 
79 extern pthread_rwlock_t ipmgmt_dbconf_lock;
80 
81 /* signifies whether volatile copy of data store is in use */
82 static boolean_t ipmgmt_rdonly_root = B_FALSE;
83 
84 typedef int ipmgmt_if_updater_func_t(nvlist_t *, nvpair_t *, uint_t);
85 
86 static ipmgmt_if_updater_func_t ipmgmt_if_family_updater;
87 static ipmgmt_if_updater_func_t ipmgmt_if_groupmembers_updater;
88 
89 static int ipmgmt_get_ifinfo_nvl(const char *ifname, nvlist_t **if_info_nvl);
90 
91 typedef struct {
92 	const char	*name;
93 	ipmgmt_if_updater_func_t	*func;
94 } ipmgmt_if_updater_ent_t;
95 
96 static ipmgmt_if_updater_ent_t ipmgmt_if_updater_ent[] = {
97 	{IPADM_NVP_FAMILIES, ipmgmt_if_family_updater},
98 	{IPADM_NVP_MIFNAMES, ipmgmt_if_groupmembers_updater},
99 	{NULL, NULL}
100 };
101 
102 static ipmgmt_if_updater_ent_t *
103 ipmgmt_find_if_field_updater(const char *field_name)
104 {
105 	int i;
106 
107 	for (i = 0; ipmgmt_if_updater_ent[i].name != NULL; i++) {
108 		if (strcmp(field_name, ipmgmt_if_updater_ent[i].name) == 0) {
109 			break;
110 		}
111 	}
112 
113 	return (&ipmgmt_if_updater_ent[i]);
114 }
115 
116 static int
117 ipmgmt_if_groupmembers_updater(nvlist_t *db_nvl, nvpair_t *member_nvp,
118     uint_t flags)
119 {
120 	char	**members;
121 	char	*member;
122 	char	**out_members;
123 	uint_t  nelem = 0, cnt = 0;
124 	int	err;
125 
126 	if ((err = nvpair_value_string(member_nvp, &member)) != 0)
127 		return (err);
128 
129 	err = nvlist_lookup_string_array(db_nvl, IPADM_NVP_MIFNAMES,
130 	    &members, &nelem);
131 
132 	if (err != 0 && (flags & IPMGMT_REMOVE))
133 		return (ENOENT);
134 
135 	/*
136 	 * Reserve one extra slot for IPMGMT_APPEND.
137 	 * Probably not worth conditionalizing.
138 	 */
139 	out_members = calloc(nelem + 1, sizeof (char *));
140 	if (out_members == NULL)
141 		return (ENOMEM);
142 
143 	while (nelem-- > 0) {
144 		if ((flags & IPMGMT_REMOVE) &&
145 		    (strcmp(member, members[nelem]) == 0))
146 			continue;
147 
148 		if ((out_members[cnt] = strdup(members[nelem])) == NULL) {
149 			err = ENOMEM;
150 			goto fail;
151 		}
152 
153 		cnt++;
154 	}
155 
156 	if (flags & IPMGMT_APPEND) {
157 		if ((out_members[cnt] = strdup(member)) == NULL) {
158 			err = ENOMEM;
159 			goto fail;
160 		}
161 		cnt++;
162 	}
163 
164 	if (cnt == 0) {
165 		err = nvlist_remove(db_nvl, IPADM_NVP_MIFNAMES,
166 		    DATA_TYPE_STRING_ARRAY);
167 	} else {
168 		err = nvlist_add_string_array(db_nvl, IPADM_NVP_MIFNAMES,
169 		    out_members, cnt);
170 	}
171 
172 fail:
173 	while (cnt--)
174 		free(out_members[cnt]);
175 
176 	free(out_members);
177 
178 	return (err);
179 }
180 
181 static int
182 ipmgmt_if_family_updater(nvlist_t *db_nvl, nvpair_t *families_nvp, uint_t flags)
183 {
184 	uint16_t *families;
185 	uint_t  nelem = 0;
186 	int	err;
187 
188 	if ((err = nvpair_value_uint16_array(families_nvp, &families,
189 	    &nelem)) != 0)
190 		return (err);
191 
192 	return (ipmgmt_update_family_nvp(db_nvl, families[0], flags));
193 }
194 
195 int
196 ipmgmt_update_family_nvp(nvlist_t *nvl, sa_family_t af, uint_t flags)
197 {
198 	uint16_t	*families = NULL;
199 	uint16_t	out_families[2];
200 	uint_t	nelem = 0, cnt;
201 	int	err;
202 
203 	err = nvlist_lookup_uint16_array(nvl, IPADM_NVP_FAMILIES,
204 	    &families, &nelem);
205 	if (err != 0 && (flags & IPMGMT_REMOVE)) {
206 		return (ENOENT);
207 	}
208 
209 	if (flags & IPMGMT_APPEND) {
210 		if (families != NULL) {
211 			if (nelem == 2 || families[0] == af) {
212 				return (EEXIST);
213 			}
214 			out_families[0] = families[0];
215 			out_families[1] = af;
216 			cnt = 2;
217 		} else {
218 			out_families[0] = af;
219 			cnt = 1;
220 		}
221 	} else {
222 		assert(nelem == 1 || nelem == 2);
223 		cnt = 0;
224 		while (nelem-- > 0) {
225 			if (families[nelem] != af) {
226 				out_families[cnt] = families[nelem];
227 				cnt++;
228 			}
229 		}
230 	}
231 
232 	if (cnt != 0) {
233 		return (nvlist_add_uint16_array(nvl, IPADM_NVP_FAMILIES,
234 		    out_families, cnt));
235 	}
236 	return (nvlist_remove(nvl, IPADM_NVP_FAMILIES, DATA_TYPE_UINT16_ARRAY));
237 }
238 
239 /*
240  * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed
241  * in private nvpairs `proto', `ifname' & `aobjname'.
242  */
243 static boolean_t
244 ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname,
245     const char *aobjname)
246 {
247 	char		*db_proto = NULL, *db_ifname = NULL;
248 	char		*db_aobjname = NULL;
249 	nvpair_t	*nvp;
250 	char		*name;
251 
252 	/* walk through db_nvl and retrieve all its private nvpairs */
253 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
254 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
255 		name = nvpair_name(nvp);
256 		if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
257 			(void) nvpair_value_string(nvp, &db_proto);
258 		else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
259 			(void) nvpair_value_string(nvp, &db_ifname);
260 		else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
261 			(void) nvpair_value_string(nvp, &db_aobjname);
262 	}
263 
264 	if (proto != NULL && proto[0] == '\0')
265 		proto = NULL;
266 	if (ifname != NULL && ifname[0] == '\0')
267 		ifname = NULL;
268 	if (aobjname != NULL && aobjname[0] == '\0')
269 		aobjname = NULL;
270 
271 	if ((proto == NULL && db_proto != NULL) ||
272 	    (proto != NULL && db_proto == NULL) ||
273 	    (proto != NULL && db_proto != NULL &&
274 	    strcmp(proto, db_proto) != 0)) {
275 		/* no intersection - different protocols. */
276 		return (B_FALSE);
277 	}
278 	if ((ifname == NULL && db_ifname != NULL) ||
279 	    (ifname != NULL && db_ifname == NULL) ||
280 	    (ifname != NULL && db_ifname != NULL &&
281 	    strcmp(ifname, db_ifname) != 0)) {
282 		/* no intersection - different interfaces. */
283 		return (B_FALSE);
284 	}
285 	if ((aobjname == NULL && db_aobjname != NULL) ||
286 	    (aobjname != NULL && db_aobjname == NULL) ||
287 	    (aobjname != NULL && db_aobjname != NULL &&
288 	    strcmp(aobjname, db_aobjname) != 0)) {
289 		/* no intersection - different address objects */
290 		return (B_FALSE);
291 	}
292 
293 	return (B_TRUE);
294 }
295 
296 /*
297  * Checks if the database nvl, `db_nvl' and the input nvl, `in_nvl' intersects.
298  */
299 static boolean_t
300 ipmgmt_nvlist_intersects(nvlist_t *db_nvl, nvlist_t *in_nvl)
301 {
302 	nvpair_t	*nvp;
303 	char		*name;
304 	char		*proto = NULL, *ifname = NULL, *aobjname = NULL;
305 
306 	/* walk through in_nvl and retrieve all its private nvpairs */
307 	for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
308 	    nvp = nvlist_next_nvpair(in_nvl, nvp)) {
309 		name = nvpair_name(nvp);
310 		if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
311 			(void) nvpair_value_string(nvp, &proto);
312 		else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
313 			(void) nvpair_value_string(nvp, &ifname);
314 		else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
315 			(void) nvpair_value_string(nvp, &aobjname);
316 	}
317 
318 	return (ipmgmt_nvlist_match(db_nvl, proto, ifname, aobjname));
319 }
320 
321 /*
322  * Checks if the database nvl, `db_nvl', contains and matches ANY of the passed
323  * in private nvpairs `proto', `ifname' & `aobjname'.
324  */
325 static boolean_t
326 ipmgmt_nvlist_contains(nvlist_t *db_nvl, const char *proto,
327     const char *ifname, char *aobjname)
328 {
329 	char		*db_ifname = NULL, *db_proto = NULL;
330 	char		*db_aobjname = NULL;
331 	nvpair_t	*nvp;
332 	char		*name;
333 
334 	/* walk through db_nvl and retrieve all private nvpairs */
335 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
336 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
337 		name = nvpair_name(nvp);
338 		if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
339 			(void) nvpair_value_string(nvp, &db_proto);
340 		else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
341 			(void) nvpair_value_string(nvp, &db_ifname);
342 		else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
343 			(void) nvpair_value_string(nvp, &db_aobjname);
344 	}
345 
346 	if (proto != NULL && proto[0] != '\0') {
347 		if ((db_proto == NULL || strcmp(proto, db_proto) != 0))
348 			return (B_FALSE);
349 	}
350 	if (ifname != NULL && ifname[0] != '\0') {
351 		if ((db_ifname == NULL || strcmp(ifname, db_ifname) != 0))
352 			return (B_FALSE);
353 	}
354 	if (aobjname != NULL && aobjname[0] != '\0') {
355 		if ((db_aobjname == NULL || strcmp(aobjname, db_aobjname) != 0))
356 			return (B_FALSE);
357 	}
358 
359 	return (B_TRUE);
360 }
361 
362 /*
363  * Retrieves the property value from the DB. The property whose value is to be
364  * retrieved is in `pargp->ia_pname'.
365  */
366 /* ARGSUSED */
367 boolean_t
368 ipmgmt_db_getprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
369     int *errp)
370 {
371 	ipmgmt_prop_arg_t	*pargp = arg;
372 	boolean_t		cont = B_TRUE;
373 	char			*pval;
374 	int			err = 0;
375 
376 	*errp = 0;
377 
378 	if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
379 	    pargp->ia_ifname, pargp->ia_aobjname))
380 		return (B_TRUE);
381 
382 	if ((err = nvlist_lookup_string(db_nvl, pargp->ia_pname,
383 	    &pval)) == 0) {
384 		(void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval));
385 		/*
386 		 * We have retrieved what we are looking for.
387 		 * Stop the walker.
388 		 */
389 		cont = B_FALSE;
390 	} else {
391 		if (err == ENOENT)
392 			err = 0;
393 		*errp = err;
394 	}
395 
396 	return (cont);
397 }
398 
399 /*
400  * Removes the property value from the DB. The property whose value is to be
401  * removed is in `pargp->ia_pname'.
402  */
403 /* ARGSUSED */
404 boolean_t
405 ipmgmt_db_resetprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
406     int *errp)
407 {
408 	ipmgmt_prop_arg_t	*pargp = arg;
409 
410 	*errp = 0;
411 	if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
412 	    pargp->ia_ifname, pargp->ia_aobjname))
413 		return (B_TRUE);
414 
415 	if (!nvlist_exists(db_nvl, pargp->ia_pname))
416 		return (B_TRUE);
417 
418 	/*
419 	 * We found the property in the DB. If IPMGMT_REMOVE is not set then
420 	 * delete the entry from the db. If it is set, then the property is a
421 	 * multi-valued property so just remove the specified values from DB.
422 	 */
423 	if (pargp->ia_flags & IPMGMT_REMOVE) {
424 		char	*dbpval = NULL;
425 		char	*inpval = pargp->ia_pval;
426 		char	pval[MAXPROPVALLEN];
427 		char	*val, *lasts;
428 
429 		*errp = nvlist_lookup_string(db_nvl, pargp->ia_pname, &dbpval);
430 		if (*errp != 0)
431 			return (B_FALSE);
432 
433 		/*
434 		 * multi-valued properties are represented as comma separated
435 		 * values. Use string tokenizer functions to split them and
436 		 * search for the value to be removed.
437 		 */
438 		bzero(pval, sizeof (pval));
439 		if ((val = strtok_r(dbpval, ",", &lasts)) != NULL) {
440 			if (strcmp(val, inpval) != 0)
441 				(void) strlcat(pval, val, MAXPROPVALLEN);
442 			while ((val = strtok_r(NULL, ",", &lasts)) != NULL) {
443 				if (strcmp(val, inpval) != 0) {
444 					if (pval[0] != '\0')
445 						(void) strlcat(pval, ",",
446 						    MAXPROPVALLEN);
447 					(void) strlcat(pval, val,
448 					    MAXPROPVALLEN);
449 				}
450 			}
451 		} else {
452 			if (strcmp(dbpval, inpval) != 0)
453 				*errp = ENOENT;
454 			else
455 				buf[0] =  '\0';
456 			return (B_FALSE);
457 		}
458 		*errp = nvlist_add_string(db_nvl, pargp->ia_pname, pval);
459 		if (*errp != 0)
460 			return (B_FALSE);
461 
462 		(void) memset(buf, 0, buflen);
463 		if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
464 			/* buffer overflow */
465 			*errp = ENOBUFS;
466 		}
467 	} else {
468 		buf[0] = '\0';
469 	}
470 
471 	/* stop the search */
472 	return (B_FALSE);
473 }
474 
475 /*
476  * Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is
477  * found, when one of the following occurs first.
478  * - the input aobjname matches the db aobjname. Return the db address.
479  * - the input interface matches the db interface. Return all the
480  *   matching db lines with addresses.
481  */
482 /* ARGSUSED */
483 boolean_t
484 ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
485     int *errp)
486 {
487 	ipmgmt_get_cbarg_t	*cbarg = arg;
488 	char		*db_aobjname = NULL;
489 	char		*db_ifname = NULL;
490 	nvlist_t	*db_addr = NULL;
491 	char		name[IPMGMT_STRSIZE];
492 	nvpair_t	*nvp;
493 	boolean_t	add_nvl = B_FALSE;
494 
495 	/* Parse db nvlist */
496 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
497 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
498 		if (nvpair_type(nvp) == DATA_TYPE_NVLIST)
499 			(void) nvpair_value_nvlist(nvp, &db_addr);
500 		else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0)
501 			(void) nvpair_value_string(nvp, &db_ifname);
502 		else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0)
503 			(void) nvpair_value_string(nvp, &db_aobjname);
504 	}
505 
506 	if (db_aobjname == NULL) /* Not an address */
507 		return (B_TRUE);
508 
509 	/* Check for a match between the aobjnames or the interface name */
510 	if (cbarg->cb_aobjname[0] != '\0') {
511 		if (strcmp(cbarg->cb_aobjname, db_aobjname) == 0)
512 			add_nvl = B_TRUE;
513 	} else if (cbarg->cb_ifname[0] != '\0') {
514 		if (strcmp(cbarg->cb_ifname, db_ifname) == 0)
515 			add_nvl = B_TRUE;
516 	} else {
517 		add_nvl = B_TRUE;
518 	}
519 
520 	if (add_nvl) {
521 		(void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
522 		    cbarg->cb_ocnt);
523 		*errp = nvlist_add_nvlist(cbarg->cb_onvl, name, db_nvl);
524 		if (*errp == 0)
525 			cbarg->cb_ocnt++;
526 	}
527 	return (B_TRUE);
528 }
529 
530 /*
531  * This function only gets called if a volatile filesystem version
532  * of the configuration file has been created. This only happens in the
533  * extremely rare case that a request has been made to update the configuration
534  * file at boottime while the root filesystem was read-only. This is
535  * really a rare occurrence now that we don't support UFS root filesystems
536  * any longer. This function will periodically attempt to write the
537  * configuration back to its location on the root filesystem. Success
538  * will indicate that the filesystem is no longer read-only.
539  */
540 /* ARGSUSED */
541 static void *
542 ipmgmt_db_restore_thread(void *arg)
543 {
544 	int err;
545 
546 	for (;;) {
547 		(void) sleep(5);
548 		(void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
549 		if (!ipmgmt_rdonly_root)
550 			break;
551 		err = ipmgmt_cpfile(IPADM_VOL_DB_FILE, IPADM_DB_FILE, B_FALSE);
552 		if (err == 0) {
553 			ipmgmt_rdonly_root = B_FALSE;
554 			break;
555 		}
556 		(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
557 	}
558 	(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
559 	return (NULL);
560 }
561 
562 /*
563  * This function takes the appropriate lock, read or write, based on the
564  * `db_op' and then calls DB walker ipadm_rw_db(). The code is complicated
565  * by the fact that we are not always guaranteed to have a writable root
566  * filesystem since it is possible that we are reading or writing during
567  * bootime while the root filesystem is still read-only. This is, by far,
568  * the exception case. Normally, this function will be called when the
569  * root filesystem is writable. In the unusual case where this is not
570  * true, the configuration file is copied to the volatile file system
571  * and is updated there until the root filesystem becomes writable. At
572  * that time the file will be moved back to its proper location by
573  * ipmgmt_db_restore_thread().
574  */
575 extern int
576 ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op)
577 {
578 	int		err;
579 	boolean_t	writeop;
580 	mode_t		mode;
581 	pthread_t	tid;
582 	pthread_attr_t	attr;
583 
584 	writeop = (db_op != IPADM_DB_READ);
585 	if (writeop) {
586 		(void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
587 		mode = IPADM_FILE_MODE;
588 	} else {
589 		(void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock);
590 		mode = 0;
591 	}
592 
593 	/*
594 	 * Did a previous write attempt fail? If so, don't even try to
595 	 * read/write to IPADM_DB_FILE.
596 	 */
597 	if (!ipmgmt_rdonly_root) {
598 		err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE,
599 		    mode, db_op);
600 		if (err != EROFS)
601 			goto done;
602 	}
603 
604 	/*
605 	 * If we haven't already copied the file to the volatile
606 	 * file system, do so. This should only happen on a failed
607 	 * writeop(i.e., we have acquired the write lock above).
608 	 */
609 	if (access(IPADM_VOL_DB_FILE, F_OK) != 0) {
610 		assert(writeop);
611 		err = ipmgmt_cpfile(IPADM_DB_FILE, IPADM_VOL_DB_FILE, B_TRUE);
612 		if (err != 0)
613 			goto done;
614 		(void) pthread_attr_init(&attr);
615 		(void) pthread_attr_setdetachstate(&attr,
616 		    PTHREAD_CREATE_DETACHED);
617 		(void) pthread_attr_setname_np(&attr, "db_restore");
618 		err = pthread_create(&tid, &attr, ipmgmt_db_restore_thread,
619 		    NULL);
620 		(void) pthread_attr_destroy(&attr);
621 		if (err != 0) {
622 			(void) unlink(IPADM_VOL_DB_FILE);
623 			goto done;
624 		}
625 		ipmgmt_rdonly_root = B_TRUE;
626 	}
627 
628 	/*
629 	 * Read/write from the volatile copy.
630 	 */
631 	err = ipadm_rw_db(db_walk_func, db_warg, IPADM_VOL_DB_FILE,
632 	    mode, db_op);
633 done:
634 	(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
635 	return (err);
636 }
637 
638 /*
639  * Used to add an entry towards the end of DB. It just returns B_TRUE for
640  * every line of the DB. When we reach the end, ipadm_rw_db() adds the
641  * line at the end.
642  */
643 /* ARGSUSED */
644 boolean_t
645 ipmgmt_db_add(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp)
646 {
647 	return (B_TRUE);
648 }
649 
650 /*
651  * This function is used to update or create an entry in DB. The nvlist_t,
652  * `in_nvl', represents the line we are looking for. Once we ensure the right
653  * line from DB, we update that entry.
654  */
655 boolean_t
656 ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
657     int *errp)
658 {
659 	ipadm_dbwrite_cbarg_t	*cb = arg;
660 	uint_t			flags = cb->dbw_flags;
661 	nvlist_t		*in_nvl = cb->dbw_nvl;
662 	nvpair_t		*nvp;
663 	char			*name, *instrval = NULL, *dbstrval = NULL;
664 	char			pval[MAXPROPVALLEN];
665 
666 	*errp = 0;
667 	if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
668 		return (B_TRUE);
669 
670 	for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
671 	    nvp = nvlist_next_nvpair(in_nvl, nvp)) {
672 		name = nvpair_name(nvp);
673 		if (!IPADM_PRIV_NVP(name) && nvlist_exists(db_nvl, name))
674 			break;
675 	}
676 
677 	if (nvp == NULL)
678 		return (B_TRUE);
679 
680 	assert(nvpair_type(nvp) == DATA_TYPE_STRING);
681 
682 	if ((*errp = nvpair_value_string(nvp, &instrval)) != 0)
683 		return (B_FALSE);
684 
685 	/*
686 	 * If IPMGMT_APPEND is set then we are dealing with multi-valued
687 	 * properties. We append to the entry from the db, with the new value.
688 	 */
689 	if (flags & IPMGMT_APPEND) {
690 		if ((*errp = nvlist_lookup_string(db_nvl, name,
691 		    &dbstrval)) != 0)
692 			return (B_FALSE);
693 		(void) snprintf(pval, MAXPROPVALLEN, "%s,%s", dbstrval,
694 		    instrval);
695 		if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0)
696 			return (B_FALSE);
697 	} else {
698 		/* case	of in-line update of a db entry */
699 		if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0)
700 			return (B_FALSE);
701 	}
702 
703 	(void) memset(buf, 0, buflen);
704 	if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
705 		/* buffer overflow */
706 		*errp = ENOBUFS;
707 	}
708 
709 	/* we updated the DB entry, so do not continue */
710 	return (B_FALSE);
711 }
712 
713 /*
714  * This function is used to update a DB line that describes
715  * an interface, its family and group interface
716  *
717  */
718 boolean_t
719 ipmgmt_db_update_if(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
720     int *errp)
721 {
722 	ipadm_dbwrite_cbarg_t *cb = arg;
723 	ipmgmt_if_updater_ent_t *updater;
724 	nvlist_t	*in_nvl = cb->dbw_nvl;
725 	uint_t		flags = cb->dbw_flags;
726 	nvpair_t	*nvp;
727 	char		*name;
728 	char		*db_ifname;
729 	char		*gifname = NULL;
730 	char		*mifname = NULL;
731 
732 	*errp = 0;
733 
734 	/* Only one flag */
735 	if ((flags & (IPMGMT_APPEND | IPMGMT_REMOVE)) == 0 ||
736 	    ((flags & IPMGMT_APPEND) && (flags & IPMGMT_REMOVE))) {
737 		*errp = EINVAL;
738 		return (B_FALSE);
739 	}
740 
741 	if (!nvlist_exists(db_nvl, IPADM_NVP_FAMILIES))
742 		return (B_TRUE);
743 
744 	if (nvlist_exists(db_nvl, IPADM_NVP_IFCLASS) &&
745 	    nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 &&
746 	    nvlist_lookup_string(in_nvl, IPADM_NVP_GIFNAME, &gifname) == 0 &&
747 	    nvlist_lookup_string(in_nvl, IPADM_NVP_MIFNAMES, &mifname) == 0 &&
748 	    strcmp(db_ifname, mifname) == 0) {
749 		if (flags & IPMGMT_APPEND) {
750 			if ((*errp = nvlist_add_string(db_nvl,
751 			    IPADM_NVP_GIFNAME, gifname)) != 0)
752 				return (B_FALSE);
753 		} else {
754 			if ((*errp = nvlist_remove(db_nvl, IPADM_NVP_GIFNAME,
755 			    DATA_TYPE_STRING)) != 0)
756 				return (B_FALSE);
757 		}
758 		cb->dbw_flags &= ~IPMGMT_UPDATE_IPMP;
759 		goto done;
760 	}
761 
762 	if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
763 		return (B_TRUE);
764 
765 	for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
766 	    nvp = nvlist_next_nvpair(in_nvl, nvp)) {
767 		name = nvpair_name(nvp);
768 		if (strcmp(name, IPADM_NVP_FAMILIES) != 0 &&
769 		    strcmp(name, IPADM_NVP_MIFNAMES) != 0)
770 			continue;
771 
772 		updater = ipmgmt_find_if_field_updater(name);
773 		assert(updater != NULL);
774 		*errp = (*updater->func)(db_nvl, nvp, flags);
775 		if (*errp != 0)
776 			return (B_FALSE);
777 	}
778 
779 	cb->dbw_flags &= ~IPMGMT_UPDATE_IF;
780 
781 done:
782 	(void) memset(buf, 0, buflen);
783 	if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
784 		*errp = EOVERFLOW;
785 		return (B_FALSE);
786 	}
787 
788 	/* we finished all operations, so do not continue */
789 	if ((cb->dbw_flags & (IPMGMT_UPDATE_IF | IPMGMT_UPDATE_IPMP)) == 0)
790 		return (B_FALSE);
791 
792 	return (B_TRUE);
793 }
794 
795 /*
796  * For the given `cbarg->cb_ifname' interface, retrieve the nvlist that
797  * represents the persistent interface information.
798  * The nvlist contains:
799  *      IPADM_NVP_IFNAME
800  *      IPADM_NVP_FAMILIES
801  *      IPADM_NVP_IF_CLASS
802  *
803  * (used in 'ipadm show-if')
804  */
805 /* ARGSUSED */
806 boolean_t
807 ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
808     int *errp)
809 {
810 	ipmgmt_get_cbarg_t *cbarg = arg;
811 	char		*ifname = cbarg->cb_ifname;
812 	nvpair_t	*nvp;
813 	char		*db_ifname = NULL;
814 	boolean_t	families = B_FALSE;
815 
816 	/* Parse db nvlist */
817 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
818 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
819 		if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0) {
820 			(void) nvpair_value_string(nvp, &db_ifname);
821 		} else if (strcmp(nvpair_name(nvp), IPADM_NVP_FAMILIES) == 0) {
822 			families = B_TRUE;
823 		}
824 	}
825 
826 	if (db_ifname == NULL || !families)
827 		return (B_TRUE);
828 
829 	if (ifname != NULL && ifname[0] != '\0' &&
830 	    strcmp(ifname, db_ifname) != 0)
831 		return (B_TRUE);
832 
833 	*errp = nvlist_add_nvlist(cbarg->cb_onvl, db_ifname, db_nvl);
834 	if (*errp == 0)
835 		cbarg->cb_ocnt++;
836 
837 	if (ifname != NULL && ifname[0] != '\0')
838 		return (B_FALSE);
839 
840 	return (B_TRUE);
841 }
842 
843 /*
844  * Deletes those entries from the database for which interface name
845  * matches with the given `cbarg->cb_ifname'
846  */
847 /* ARGSUSED */
848 boolean_t
849 ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
850     int *errp)
851 {
852 	ipmgmt_if_cbarg_t *cbarg = arg;
853 	boolean_t	isv6 = (cbarg->cb_family == AF_INET6);
854 	char		*ifname = cbarg->cb_ifname;
855 	char		*modstr = NULL;
856 	char		*aobjname;
857 	uint_t		proto;
858 	ipmgmt_aobjmap_t *head;
859 	boolean_t	aobjfound = B_FALSE;
860 
861 	*errp = 0;
862 
863 	if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL))
864 		return (B_TRUE);
865 
866 	if (nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) {
867 
868 		if ((*errp = ipmgmt_update_family_nvp(db_nvl, cbarg->cb_family,
869 		    IPMGMT_REMOVE)) != 0) {
870 			return (B_FALSE);
871 		}
872 
873 		if (cbarg->cb_family == AF_INET) {
874 			cbarg->cb_ipv4exists = B_FALSE;
875 		} else {
876 			assert(cbarg->cb_family == AF_INET6);
877 			cbarg->cb_ipv6exists = B_FALSE;
878 		}
879 		if (!nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) {
880 			cbarg->cb_ipv4exists = B_FALSE;
881 			cbarg->cb_ipv6exists = B_FALSE;
882 			goto delete;
883 		}
884 		/* Otherwise need to reconstruct this string */
885 		(void) memset(buf, 0, buflen);
886 		if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
887 			/* buffer overflow */
888 			*errp = EOVERFLOW;
889 			return (B_FALSE);
890 		}
891 		return (B_TRUE);
892 	}
893 
894 	/* Reset all the interface configurations for 'ifname' */
895 	if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) ||
896 	    nvlist_exists(db_nvl, IPADM_NVP_INTFID))) {
897 		goto delete;
898 	}
899 	if (!isv6 &&
900 	    (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
901 	    nvlist_exists(db_nvl, IPADM_NVP_DHCP))) {
902 		goto delete;
903 	}
904 
905 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) {
906 		/*
907 		 * This must be an address property. Delete this
908 		 * line if there is a match in the address family.
909 		 */
910 		head = aobjmap.aobjmap_head;
911 		while (head != NULL) {
912 			if (strcmp(head->am_aobjname, aobjname) == 0) {
913 				aobjfound = B_TRUE;
914 				if (head->am_family == cbarg->cb_family)
915 					goto delete;
916 			}
917 			head = head->am_next;
918 		}
919 		/*
920 		 * If aobjfound = B_FALSE, then this address is not
921 		 * available in active configuration. We should go ahead
922 		 * and delete it.
923 		 */
924 		if (!aobjfound)
925 			goto delete;
926 	}
927 
928 	/*
929 	 * If we are removing both v4 and v6 interface, then we get rid of
930 	 * all the properties for that interface. On the other hand, if we
931 	 * are deleting only v4 instance of an interface, then we delete v4
932 	 * properties only.
933 	 */
934 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) {
935 		proto = ipadm_str2proto(modstr);
936 		switch (proto) {
937 		case MOD_PROTO_IPV6:
938 			if (isv6)
939 				goto delete;
940 			break;
941 		case MOD_PROTO_IPV4:
942 			if (!isv6)
943 				goto delete;
944 			break;
945 		case MOD_PROTO_IP:
946 			if (!cbarg->cb_ipv4exists && !cbarg->cb_ipv6exists)
947 				goto delete;
948 			break;
949 		}
950 	}
951 	/* Not found a match yet. Continue processing the db */
952 	return (B_TRUE);
953 delete:
954 	/* delete the line from the db */
955 	buf[0] = '\0';
956 	return (B_TRUE);
957 }
958 
959 /*
960  * Deletes those entries from the database for which address object name
961  * matches with the given `cbarg->cb_aobjname'
962  */
963 /* ARGSUSED */
964 boolean_t
965 ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
966     int *errp)
967 {
968 	ipmgmt_resetaddr_cbarg_t *cbarg = arg;
969 	char		*aobjname = cbarg->cb_aobjname;
970 
971 	*errp = 0;
972 	if (!ipmgmt_nvlist_contains(db_nvl, NULL, NULL, aobjname))
973 		return (B_TRUE);
974 
975 	/* delete the line from the db */
976 	buf[0] = '\0';
977 	return (B_TRUE);
978 }
979 
980 /*
981  * Retrieves all interface props, including addresses, for given interface(s).
982  * `invl' contains the list of interfaces, for which information need to be
983  * retrieved.
984  */
985 /* ARGSUSED */
986 boolean_t
987 ipmgmt_db_initif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
988     int *errp)
989 {
990 	ipmgmt_initif_cbarg_t	*cbarg = arg;
991 	nvlist_t		*onvl = cbarg->cb_onvl;
992 	nvlist_t		*invl = cbarg->cb_invl;
993 	sa_family_t		in_af = cbarg->cb_family;
994 	char			*db_ifname;
995 
996 	*errp = 0;
997 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 &&
998 	    nvlist_exists(invl, db_ifname)) {
999 		char		name[IPMGMT_STRSIZE];
1000 		sa_family_t	db_af = in_af;
1001 		uint_t		proto;
1002 		char		*pstr;
1003 
1004 		if (in_af != AF_UNSPEC) {
1005 			if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME,
1006 			    &pstr) == 0) {
1007 				proto = ipadm_str2proto(pstr);
1008 				if (proto == MOD_PROTO_IPV4)
1009 					db_af = AF_INET;
1010 				else if (proto == MOD_PROTO_IPV6)
1011 					db_af = AF_INET6;
1012 				else
1013 					db_af = in_af;
1014 			} else {
1015 				if (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
1016 				    nvlist_exists(db_nvl, IPADM_NVP_DHCP))
1017 					db_af = AF_INET;
1018 				else
1019 					db_af = AF_INET6;
1020 			}
1021 		}
1022 		if (in_af == db_af) {
1023 			(void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
1024 			    cbarg->cb_ocnt);
1025 			*errp = nvlist_add_nvlist(onvl, name, db_nvl);
1026 			if (*errp == 0)
1027 				cbarg->cb_ocnt++;
1028 		}
1029 	}
1030 	return (B_TRUE);
1031 }
1032 
1033 /*
1034  * helper function for ipmgmt_aobjmap_op(). Adds the node pointed by `nodep'
1035  * into `aobjmap' structure.
1036  */
1037 static int
1038 i_ipmgmt_add_amnode(ipmgmt_aobjmap_t *nodep)
1039 {
1040 	ipmgmt_aobjmap_t	*new, *head;
1041 
1042 	head = aobjmap.aobjmap_head;
1043 	if ((new = malloc(sizeof (ipmgmt_aobjmap_t))) == NULL)
1044 		return (ENOMEM);
1045 	*new = *nodep;
1046 	new->am_next = NULL;
1047 
1048 	/* Add the node at the beginning of the list */
1049 	if (head == NULL) {
1050 		aobjmap.aobjmap_head = new;
1051 	} else {
1052 		new->am_next = aobjmap.aobjmap_head;
1053 		aobjmap.aobjmap_head = new;
1054 	}
1055 	return (0);
1056 }
1057 
1058 /*
1059  * A recursive function to generate alphabetized number given a decimal number.
1060  * Decimal 0 to 25 maps to 'a' to 'z' and then the counting continues with 'aa',
1061  * 'ab', 'ac', et al.
1062  */
1063 static void
1064 i_ipmgmt_num2priv_aobjname(uint32_t num, char **cp, char *endp)
1065 {
1066 	if (num >= 26)
1067 		i_ipmgmt_num2priv_aobjname(num / 26 - 1, cp, endp);
1068 	if (*cp != endp) {
1069 		*cp[0] = 'a' + (num % 26);
1070 		(*cp)++;
1071 	}
1072 }
1073 
1074 /*
1075  * This function generates an `aobjname', when required, and then does
1076  * lookup-add. If `nodep->am_aobjname' is not an empty string, then it walks
1077  * through the `aobjmap' to check if an address object with the same
1078  * `nodep->am_aobjname' exists. If it exists, EEXIST is returned as duplicate
1079  * `aobjname's are not allowed.
1080  *
1081  * If `nodep->am_aobjname' is an empty string then the daemon generates an
1082  * `aobjname' using the `am_nextnum', which contains the next number to be
1083  * used to generate `aobjname'. `am_nextnum' is converted to base26 using
1084  * `a-z' alphabets in i_ipmgmt_num2priv_aobjname().
1085  *
1086  * `am_nextnum' will be 0 to begin with. Every time an address object that
1087  * needs `aobjname' is added it's incremented by 1. So for the first address
1088  * object on net0 the `am_aobjname' will be net0/_a and `am_nextnum' will be 1.
1089  * For the second address object on that interface `am_aobjname' will be net0/_b
1090  * and  `am_nextnum' will incremented to 2.
1091  */
1092 static int
1093 i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t *nodep)
1094 {
1095 	ipmgmt_aobjmap_t	*head;
1096 	uint32_t		nextnum;
1097 
1098 	for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next)
1099 		if (strcmp(head->am_ifname, nodep->am_ifname) == 0)
1100 			break;
1101 	nextnum = (head == NULL ? 0 : head->am_nextnum);
1102 
1103 	/*
1104 	 * if `aobjname' is empty, then the daemon has to generate the
1105 	 * next `aobjname' for the given interface and family.
1106 	 */
1107 	if (nodep->am_aobjname[0] == '\0') {
1108 		char tmpstr[IPADM_AOBJ_USTRSIZ - 1];  /* 1 for leading  '_' */
1109 		char *cp = tmpstr;
1110 		char *endp = tmpstr + sizeof (tmpstr);
1111 
1112 		i_ipmgmt_num2priv_aobjname(nextnum, &cp, endp);
1113 
1114 		if (cp == endp)
1115 			return (EINVAL);
1116 		cp[0] = '\0';
1117 
1118 		if (snprintf(nodep->am_aobjname, IPADM_AOBJSIZ, "%s/_%s",
1119 		    nodep->am_ifname, tmpstr) >= IPADM_AOBJSIZ) {
1120 			return (EINVAL);
1121 		}
1122 		nodep->am_nextnum = ++nextnum;
1123 	} else {
1124 		for (head = aobjmap.aobjmap_head; head != NULL;
1125 		    head = head->am_next) {
1126 			if (strcmp(head->am_aobjname, nodep->am_aobjname) == 0)
1127 				return (EEXIST);
1128 		}
1129 		nodep->am_nextnum = nextnum;
1130 	}
1131 	return (i_ipmgmt_add_amnode(nodep));
1132 }
1133 
1134 /*
1135  * Performs following operations on the global `aobjmap' linked list.
1136  * (a) ADDROBJ_ADD: add or update address object in `aobjmap'
1137  * (b) ADDROBJ_DELETE: delete address object from `aobjmap'
1138  * (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap'
1139  * (d) ADDROBJ_SETLIFNUM: Sets the lifnum for an address object in `aobjmap'
1140  */
1141 int
1142 ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op)
1143 {
1144 	ipmgmt_aobjmap_t	*head, *prev, *matched = NULL;
1145 	boolean_t		update = B_TRUE;
1146 	int			err = 0;
1147 	ipadm_db_op_t		db_op = IPADM_DB_READ;
1148 
1149 	(void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock);
1150 
1151 	head = aobjmap.aobjmap_head;
1152 	switch (op) {
1153 	case ADDROBJ_ADD:
1154 		/*
1155 		 * check for stub nodes (added by ADDROBJ_LOOKUPADD) and
1156 		 * update, else add the new node.
1157 		 */
1158 		for (; head != NULL; head = head->am_next) {
1159 			/*
1160 			 * For IPv6, we need to distinguish between the
1161 			 * linklocal and non-linklocal nodes
1162 			 */
1163 			if (strcmp(head->am_aobjname,
1164 			    nodep->am_aobjname) == 0 &&
1165 			    (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF ||
1166 			    head->ipmgmt_am_linklocal ==
1167 			    nodep->ipmgmt_am_linklocal))
1168 				break;
1169 		}
1170 
1171 		if (head != NULL) {
1172 			/* update the node */
1173 			(void) strlcpy(head->am_ifname, nodep->am_ifname,
1174 			    sizeof (head->am_ifname));
1175 			head->am_lnum = nodep->am_lnum;
1176 			head->am_family = nodep->am_family;
1177 			head->am_flags = nodep->am_flags;
1178 			head->am_atype = nodep->am_atype;
1179 			head->am_atype_cache = nodep->am_atype_cache;
1180 		} else {
1181 			for (head = aobjmap.aobjmap_head; head != NULL;
1182 			    head = head->am_next) {
1183 				if (strcmp(head->am_ifname,
1184 				    nodep->am_ifname) == 0)
1185 					break;
1186 			}
1187 			nodep->am_nextnum = (head == NULL ? 0 :
1188 			    head->am_nextnum);
1189 			err = i_ipmgmt_add_amnode(nodep);
1190 		}
1191 		db_op = IPADM_DB_WRITE;
1192 		break;
1193 	case ADDROBJ_DELETE:
1194 		prev = head;
1195 		while (head != NULL) {
1196 			if (strcmp(head->am_aobjname,
1197 			    nodep->am_aobjname) == 0) {
1198 				nodep->am_atype = head->am_atype;
1199 				/*
1200 				 * There could be multiple IPV6_ADDRCONF nodes,
1201 				 * with same address object name, so check for
1202 				 * logical number also.
1203 				 */
1204 				if (head->am_atype !=
1205 				    IPADM_ADDR_IPV6_ADDRCONF ||
1206 				    nodep->am_lnum == head->am_lnum)
1207 					break;
1208 			}
1209 			prev = head;
1210 			head = head->am_next;
1211 		}
1212 		if (head != NULL) {
1213 			/*
1214 			 * If the address object is in both active and
1215 			 * persistent configuration and the user is deleting it
1216 			 * only from active configuration then mark this node
1217 			 * for deletion by reseting IPMGMT_ACTIVE bit.
1218 			 * With this the same address object name cannot
1219 			 * be reused until it is permanently removed.
1220 			 */
1221 			if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) &&
1222 			    nodep->am_flags == IPMGMT_ACTIVE) {
1223 				/* Update flags in the in-memory map. */
1224 				head->am_flags &= ~IPMGMT_ACTIVE;
1225 				head->am_lnum = -1;
1226 
1227 				/* Update info in file. */
1228 				db_op = IPADM_DB_WRITE;
1229 				*nodep = *head;
1230 			} else {
1231 				(void) strlcpy(nodep->am_ifname,
1232 				    head->am_ifname,
1233 				    sizeof (nodep->am_ifname));
1234 				/* otherwise delete the node */
1235 				if (head == aobjmap.aobjmap_head)
1236 					aobjmap.aobjmap_head = head->am_next;
1237 				else
1238 					prev->am_next = head->am_next;
1239 				free(head);
1240 				db_op = IPADM_DB_DELETE;
1241 			}
1242 		} else {
1243 			err = ENOENT;
1244 		}
1245 		break;
1246 	case ADDROBJ_LOOKUPADD:
1247 		err = i_ipmgmt_lookupadd_amnode(nodep);
1248 		update = B_FALSE;
1249 		break;
1250 	case ADDROBJ_SETLIFNUM:
1251 		update = B_FALSE;
1252 		for (; head != NULL; head = head->am_next) {
1253 			if (strcmp(head->am_ifname,
1254 			    nodep->am_ifname) == 0 &&
1255 			    head->am_family == nodep->am_family &&
1256 			    head->am_lnum == nodep->am_lnum) {
1257 				err = EEXIST;
1258 				break;
1259 			}
1260 			if (strcmp(head->am_aobjname,
1261 			    nodep->am_aobjname) == 0) {
1262 				matched = head;
1263 			}
1264 		}
1265 		if (err == EEXIST)
1266 			break;
1267 		if (matched != NULL) {
1268 			/* update the lifnum */
1269 			matched->am_lnum = nodep->am_lnum;
1270 		} else {
1271 			err = ENOENT;
1272 		}
1273 		break;
1274 	default:
1275 		assert(0);
1276 	}
1277 
1278 	if (err == 0 && update)
1279 		err = ipmgmt_persist_aobjmap(nodep, db_op);
1280 
1281 	(void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
1282 
1283 	return (err);
1284 }
1285 
1286 /*
1287  * Given a node in `aobjmap', this function converts it into nvlist_t structure.
1288  * The content to be written to DB must be represented as nvlist_t.
1289  */
1290 static int
1291 i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np)
1292 {
1293 	int	err;
1294 	char	strval[IPMGMT_STRSIZE];
1295 
1296 	*nvl = NULL;
1297 	if ((err = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0)) != 0)
1298 		goto fail;
1299 
1300 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_AOBJNAME,
1301 	    np->am_aobjname)) != 0)
1302 		goto fail;
1303 
1304 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_IFNAME,
1305 	    np->am_ifname)) != 0)
1306 		goto fail;
1307 
1308 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_lnum);
1309 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_LIFNUM, strval)) != 0)
1310 		goto fail;
1311 
1312 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_family);
1313 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_FAMILY, strval)) != 0)
1314 		goto fail;
1315 
1316 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_flags);
1317 	if ((err = nvlist_add_string(*nvl, FLAGS, strval)) != 0)
1318 		goto fail;
1319 
1320 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_atype);
1321 	if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0)
1322 		goto fail;
1323 
1324 	switch (np->am_atype) {
1325 		case IPADM_ADDR_IPV6_ADDRCONF: {
1326 			struct sockaddr_in6	*in6;
1327 
1328 			in6 = &np->ipmgmt_am_ifid;
1329 			if (np->ipmgmt_am_linklocal &&
1330 			    IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) {
1331 				if ((err = nvlist_add_string(*nvl,
1332 				    IPADM_NVP_IPNUMADDR, "default")) != 0) {
1333 					goto fail;
1334 				}
1335 			} else {
1336 				if (inet_ntop(AF_INET6, &in6->sin6_addr, strval,
1337 				    IPMGMT_STRSIZE) == NULL) {
1338 					err = errno;
1339 					goto fail;
1340 				}
1341 				if ((err = nvlist_add_string(*nvl,
1342 				    IPADM_NVP_IPNUMADDR, strval)) != 0) {
1343 					goto fail;
1344 				}
1345 			}
1346 		}
1347 			break;
1348 		case IPADM_ADDR_DHCP: {
1349 			if (np->ipmgmt_am_reqhost &&
1350 			    *np->ipmgmt_am_reqhost != '\0' &&
1351 			    (err = nvlist_add_string(*nvl, IPADM_NVP_REQHOST,
1352 			    np->ipmgmt_am_reqhost)) != 0)
1353 				goto fail;
1354 		}
1355 			/* FALLTHRU */
1356 		default:
1357 			if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
1358 			    "")) != 0)
1359 				goto fail;
1360 			break;
1361 	}
1362 	return (err);
1363 fail:
1364 	nvlist_free(*nvl);
1365 	return (err);
1366 }
1367 
1368 /*
1369  * Read the aobjmap data store and build the in-memory representation
1370  * of the aobjmap. We don't need to hold any locks while building this as
1371  * we do this in very early stage of daemon coming up, even before the door
1372  * is opened.
1373  */
1374 /* ARGSUSED */
1375 extern boolean_t
1376 ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
1377     int *errp)
1378 {
1379 	nvpair_t		*nvp = NULL;
1380 	char			*name, *strval = NULL;
1381 	ipmgmt_aobjmap_t	node;
1382 	struct sockaddr_in6	*in6;
1383 
1384 	*errp = 0;
1385 	node.am_next = NULL;
1386 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1387 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1388 		name = nvpair_name(nvp);
1389 
1390 		if ((*errp = nvpair_value_string(nvp, &strval)) != 0)
1391 			return (B_TRUE);
1392 		if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) {
1393 			(void) strlcpy(node.am_aobjname, strval,
1394 			    sizeof (node.am_aobjname));
1395 		} else if (strcmp(IPADM_NVP_IFNAME, name) == 0) {
1396 			(void) strlcpy(node.am_ifname, strval,
1397 			    sizeof (node.am_ifname));
1398 		} else if (strcmp(IPADM_NVP_LIFNUM, name) == 0) {
1399 			node.am_lnum = atoi(strval);
1400 		} else if (strcmp(IPADM_NVP_FAMILY, name) == 0) {
1401 			node.am_family = (sa_family_t)atoi(strval);
1402 		} else if (strcmp(FLAGS, name) == 0) {
1403 			node.am_flags = atoi(strval);
1404 		} else if (strcmp(ATYPE, name) == 0) {
1405 			node.am_atype = (ipadm_addr_type_t)atoi(strval);
1406 		} else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) {
1407 			if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1408 				in6 = &node.ipmgmt_am_ifid;
1409 				if (strcmp(strval, "default") == 0) {
1410 					bzero(in6,
1411 					    sizeof (node.ipmgmt_am_ifid));
1412 					node.ipmgmt_am_linklocal = B_TRUE;
1413 				} else {
1414 					(void) inet_pton(AF_INET6, strval,
1415 					    &in6->sin6_addr);
1416 					if (IN6_IS_ADDR_UNSPECIFIED(
1417 					    &in6->sin6_addr))
1418 						node.ipmgmt_am_linklocal =
1419 						    B_TRUE;
1420 				}
1421 			}
1422 		}
1423 	}
1424 
1425 	/* we have all the information we need, add the node */
1426 	*errp = i_ipmgmt_add_amnode(&node);
1427 
1428 	return (B_TRUE);
1429 }
1430 
1431 /*
1432  * Updates an entry from the temporary cache file, which matches the given
1433  * address object name.
1434  */
1435 /* ARGSUSED */
1436 static boolean_t
1437 ipmgmt_update_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1438     size_t buflen, int *errp)
1439 {
1440 	ipadm_dbwrite_cbarg_t	*cb = arg;
1441 	nvlist_t		*in_nvl = cb->dbw_nvl;
1442 	uint32_t		flags = cb->dbw_flags;
1443 	char			*db_lifnumstr = NULL, *in_lifnumstr = NULL;
1444 
1445 	*errp = 0;
1446 	if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
1447 		return (B_TRUE);
1448 
1449 	if (flags & IPMGMT_ATYPE_V6ACONF) {
1450 		if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1451 		    &db_lifnumstr) != 0 ||
1452 		    nvlist_lookup_string(in_nvl, IPADM_NVP_LIFNUM,
1453 		    &in_lifnumstr) != 0 ||
1454 		    (atoi(db_lifnumstr) != -1 && atoi(in_lifnumstr) != -1 &&
1455 		    strcmp(db_lifnumstr, in_lifnumstr) != 0))
1456 			return (B_TRUE);
1457 	}
1458 
1459 	/* we found the match */
1460 	(void) memset(buf, 0, buflen);
1461 	if (ipadm_nvlist2str(in_nvl, buf, buflen) == 0) {
1462 		/* buffer overflow */
1463 		*errp = ENOBUFS;
1464 	}
1465 
1466 	/* stop the walker */
1467 	return (B_FALSE);
1468 }
1469 
1470 /*
1471  * Deletes an entry from the temporary cache file, which matches the given
1472  * address object name.
1473  */
1474 /* ARGSUSED */
1475 static boolean_t
1476 ipmgmt_delete_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1477     size_t buflen, int *errp)
1478 {
1479 	ipmgmt_aobjmap_t	*nodep = arg;
1480 	char			*db_lifnumstr = NULL;
1481 
1482 	*errp = 0;
1483 	if (!ipmgmt_nvlist_match(db_nvl, NULL, nodep->am_ifname,
1484 	    nodep->am_aobjname))
1485 		return (B_TRUE);
1486 
1487 	if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1488 		if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1489 		    &db_lifnumstr) != 0 || atoi(db_lifnumstr) != nodep->am_lnum)
1490 			return (B_TRUE);
1491 	}
1492 
1493 	/* we found the match, delete the line from the db */
1494 	buf[0] = '\0';
1495 
1496 	/* stop the walker */
1497 	return (B_FALSE);
1498 }
1499 
1500 /*
1501  * Adds or deletes aobjmap node information into a temporary cache file.
1502  */
1503 extern int
1504 ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op)
1505 {
1506 	int			err;
1507 	ipadm_dbwrite_cbarg_t	cb;
1508 	nvlist_t		*nvl = NULL;
1509 
1510 	if (op == IPADM_DB_WRITE) {
1511 		if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0)
1512 			return (err);
1513 		cb.dbw_nvl = nvl;
1514 		if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF)
1515 			cb.dbw_flags = IPMGMT_ATYPE_V6ACONF;
1516 		else
1517 			cb.dbw_flags = 0;
1518 
1519 		err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb,
1520 		    ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE);
1521 		nvlist_free(nvl);
1522 	} else {
1523 		assert(op == IPADM_DB_DELETE);
1524 
1525 		err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep,
1526 		    ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE);
1527 	}
1528 	return (err);
1529 }
1530 
1531 /*
1532  * upgrades the ipadm data-store. It renames all the old private protocol
1533  * property names which start with leading protocol names to begin with
1534  * IPADM_PRIV_PROP_PREFIX.
1535  */
1536 /* ARGSUSED */
1537 boolean_t
1538 ipmgmt_db_upgrade(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
1539     int *errp)
1540 {
1541 	nvpair_t	*nvp;
1542 	char		*name, *pname = NULL, *protostr = NULL, *pval = NULL;
1543 	uint_t		proto, nproto;
1544 	char		nname[IPMGMT_STRSIZE], tmpstr[IPMGMT_STRSIZE];
1545 
1546 	*errp = 0;
1547 	/*
1548 	 * We are interested in lines which contain protocol properties. We
1549 	 * walk through other lines in the DB.
1550 	 */
1551 	if (nvlist_exists(db_nvl, IPADM_NVP_IFNAME) ||
1552 	    nvlist_exists(db_nvl, IPADM_NVP_AOBJNAME)) {
1553 		return (B_TRUE);
1554 	}
1555 	assert(nvlist_exists(db_nvl, IPADM_NVP_PROTONAME));
1556 
1557 	/*
1558 	 * extract the propname from the `db_nvl' and also extract the
1559 	 * protocol from the `db_nvl'.
1560 	 */
1561 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1562 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1563 		name = nvpair_name(nvp);
1564 		if (strcmp(name, IPADM_NVP_PROTONAME) == 0) {
1565 			if (nvpair_value_string(nvp, &protostr) != 0)
1566 				return (B_TRUE);
1567 		} else {
1568 			assert(!IPADM_PRIV_NVP(name));
1569 			pname = name;
1570 			if (nvpair_value_string(nvp, &pval) != 0)
1571 				return (B_TRUE);
1572 		}
1573 	}
1574 
1575 	/* if the private property is in the right format return */
1576 	if (strncmp(pname, IPADM_PERSIST_PRIVPROP_PREFIX,
1577 	    strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
1578 		return (B_TRUE);
1579 	}
1580 	/* if it's a public property move onto the next property */
1581 	nproto = proto = ipadm_str2proto(protostr);
1582 	if (ipadm_legacy2new_propname(pname, nname, sizeof (nname),
1583 	    &nproto) != 0) {
1584 		return (B_TRUE);
1585 	}
1586 
1587 	/* replace the old protocol with new protocol, if required */
1588 	if (nproto != proto) {
1589 		protostr = ipadm_proto2str(nproto);
1590 		if (nvlist_add_string(db_nvl, IPADM_NVP_PROTONAME,
1591 		    protostr) != 0) {
1592 			return (B_TRUE);
1593 		}
1594 	}
1595 
1596 	/* replace the old property name with new property name, if required */
1597 	/* add the prefix to property name */
1598 	(void) snprintf(tmpstr, sizeof (tmpstr), "_%s", nname);
1599 	if (nvlist_add_string(db_nvl, tmpstr, pval) != 0 ||
1600 	    nvlist_remove(db_nvl, pname, DATA_TYPE_STRING) != 0) {
1601 		return (B_TRUE);
1602 	}
1603 	(void) memset(buf, 0, buflen);
1604 	if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
1605 		/* buffer overflow */
1606 		*errp = ENOBUFS;
1607 	}
1608 	return (B_TRUE);
1609 }
1610 
1611 /*
1612  * Called during boot.
1613  *
1614  * Walk through the DB and apply all the global module properties. We plow
1615  * through the DB even if we fail to apply property.
1616  */
1617 /* ARGSUSED */
1618 static boolean_t
1619 ipmgmt_db_init(void *cbarg, nvlist_t *db_nvl, char *buf, size_t buflen,
1620     int *errp)
1621 {
1622 	ipadm_handle_t	iph = cbarg;
1623 	nvpair_t	*nvp, *pnvp = NULL;
1624 	char		*strval = NULL, *name, *mod = NULL, *pname;
1625 	char		tmpstr[IPMGMT_STRSIZE];
1626 	uint_t		proto;
1627 
1628 	/*
1629 	 * We could have used nvl_exists() directly, however we need several
1630 	 * calls to it and each call traverses the list. Since this codepath
1631 	 * is exercised during boot, let's traverse the list ourselves and do
1632 	 * the necessary checks.
1633 	 */
1634 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1635 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1636 		name = nvpair_name(nvp);
1637 		if (IPADM_PRIV_NVP(name)) {
1638 			if (strcmp(name, IPADM_NVP_IFNAME) == 0 ||
1639 			    strcmp(name, IPADM_NVP_AOBJNAME) == 0)
1640 				return (B_TRUE);
1641 			else if (strcmp(name, IPADM_NVP_PROTONAME) == 0 &&
1642 			    nvpair_value_string(nvp, &mod) != 0)
1643 				return (B_TRUE);
1644 		} else {
1645 			/* possible a property */
1646 			pnvp = nvp;
1647 		}
1648 	}
1649 
1650 	/* If we are here then we have found a global property */
1651 	assert(mod != NULL);
1652 	assert(nvpair_type(pnvp) == DATA_TYPE_STRING);
1653 
1654 	proto = ipadm_str2proto(mod);
1655 	name = nvpair_name(pnvp);
1656 	if (nvpair_value_string(pnvp, &strval) == 0) {
1657 		if (strncmp(name, IPADM_PERSIST_PRIVPROP_PREFIX,
1658 		    strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
1659 			/* private protocol property */
1660 			pname = &name[1];
1661 		} else if (ipadm_legacy2new_propname(name, tmpstr,
1662 		    sizeof (tmpstr), &proto) == 0) {
1663 			pname = tmpstr;
1664 		} else {
1665 			pname = name;
1666 		}
1667 		if (ipadm_set_prop(iph, pname, strval, proto,
1668 		    IPADM_OPT_ACTIVE) != IPADM_SUCCESS) {
1669 			ipmgmt_log(LOG_WARNING, "Failed to reapply property %s",
1670 			    pname);
1671 		}
1672 	}
1673 
1674 	return (B_TRUE);
1675 }
1676 
1677 /* initialize global module properties */
1678 void
1679 ipmgmt_init_prop()
1680 {
1681 	ipadm_handle_t	iph = NULL;
1682 
1683 	if (ipadm_open(&iph, IPH_INIT) != IPADM_SUCCESS) {
1684 		ipmgmt_log(LOG_WARNING, "Could not reapply any of the "
1685 		    "persisted protocol properties");
1686 		return;
1687 	}
1688 	/* ipmgmt_db_init() logs warnings if there are any issues */
1689 	(void) ipmgmt_db_walk(ipmgmt_db_init, iph, IPADM_DB_READ);
1690 	ipadm_close(iph);
1691 }
1692 
1693 void
1694 ipmgmt_release_scf_resources(scf_resources_t *res)
1695 {
1696 	scf_entry_destroy(res->sr_ent);
1697 	scf_transaction_destroy(res->sr_tx);
1698 	scf_value_destroy(res->sr_val);
1699 	scf_property_destroy(res->sr_prop);
1700 	scf_pg_destroy(res->sr_pg);
1701 	scf_instance_destroy(res->sr_inst);
1702 	(void) scf_handle_unbind(res->sr_handle);
1703 	scf_handle_destroy(res->sr_handle);
1704 }
1705 
1706 /*
1707  * It creates the necessary SCF handles and binds the given `fmri' to an
1708  * instance. These resources are required for retrieving property value,
1709  * creating property groups and modifying property values.
1710  */
1711 int
1712 ipmgmt_create_scf_resources(const char *fmri, scf_resources_t *res)
1713 {
1714 	res->sr_tx = NULL;
1715 	res->sr_ent = NULL;
1716 	res->sr_inst = NULL;
1717 	res->sr_pg = NULL;
1718 	res->sr_prop = NULL;
1719 	res->sr_val = NULL;
1720 
1721 	if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL)
1722 		return (-1);
1723 
1724 	if (scf_handle_bind(res->sr_handle) != 0) {
1725 		scf_handle_destroy(res->sr_handle);
1726 		return (-1);
1727 	}
1728 	if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL)
1729 		goto failure;
1730 	if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL,
1731 	    res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
1732 		goto failure;
1733 	}
1734 	/* we will create the rest of the resources on demand */
1735 	return (0);
1736 
1737 failure:
1738 	ipmgmt_log(LOG_WARNING, "failed to create scf resources: %s",
1739 	    scf_strerror(scf_error()));
1740 	ipmgmt_release_scf_resources(res);
1741 	return (-1);
1742 }
1743 
1744 /*
1745  * persists the `pval' for a given property `pname' in SCF. The only supported
1746  * SCF property types are INTEGER and ASTRING.
1747  */
1748 static int
1749 ipmgmt_set_scfprop_value(scf_resources_t *res, const char *pname, void *pval,
1750     scf_type_t ptype)
1751 {
1752 	int result = -1;
1753 	boolean_t new;
1754 
1755 	if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL)
1756 		goto failure;
1757 	switch (ptype) {
1758 	case SCF_TYPE_INTEGER:
1759 		scf_value_set_integer(res->sr_val, *(int64_t *)pval);
1760 		break;
1761 	case SCF_TYPE_ASTRING:
1762 		if (scf_value_set_astring(res->sr_val, (char *)pval) != 0) {
1763 			ipmgmt_log(LOG_WARNING, "Error setting string value %s "
1764 			    "for property %s: %s", pval, pname,
1765 			    scf_strerror(scf_error()));
1766 			goto failure;
1767 		}
1768 		break;
1769 	default:
1770 		goto failure;
1771 	}
1772 
1773 	if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL)
1774 		goto failure;
1775 	if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL)
1776 		goto failure;
1777 	if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL)
1778 		goto failure;
1779 
1780 retry:
1781 	new = (scf_pg_get_property(res->sr_pg, pname, res->sr_prop) != 0);
1782 	if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1)
1783 		goto failure;
1784 	if (new) {
1785 		if (scf_transaction_property_new(res->sr_tx, res->sr_ent,
1786 		    pname, ptype) == -1) {
1787 			goto failure;
1788 		}
1789 	} else {
1790 		if (scf_transaction_property_change(res->sr_tx, res->sr_ent,
1791 		    pname, ptype) == -1) {
1792 			goto failure;
1793 		}
1794 	}
1795 
1796 	if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0)
1797 		goto failure;
1798 
1799 	result = scf_transaction_commit(res->sr_tx);
1800 	if (result == 0) {
1801 		scf_transaction_reset(res->sr_tx);
1802 		if (scf_pg_update(res->sr_pg) == -1) {
1803 			goto failure;
1804 		}
1805 		goto retry;
1806 	}
1807 	if (result == -1)
1808 		goto failure;
1809 	return (0);
1810 
1811 failure:
1812 	ipmgmt_log(LOG_WARNING, "failed to save the data in SCF: %s",
1813 	    scf_strerror(scf_error()));
1814 	return (-1);
1815 }
1816 
1817 /*
1818  * Given a `pgname'/`pname', it retrieves the value based on `ptype' and
1819  * places it in `pval'.
1820  */
1821 static int
1822 ipmgmt_get_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1823     void *pval, scf_type_t ptype)
1824 {
1825 	ssize_t		numvals;
1826 	scf_simple_prop_t *prop;
1827 
1828 	prop = scf_simple_prop_get(res->sr_handle, IPMGMTD_FMRI, pgname, pname);
1829 	numvals = scf_simple_prop_numvalues(prop);
1830 	if (numvals <= 0)
1831 		goto ret;
1832 	switch (ptype) {
1833 	case SCF_TYPE_INTEGER:
1834 		*(int64_t **)pval = scf_simple_prop_next_integer(prop);
1835 		break;
1836 	case SCF_TYPE_ASTRING:
1837 		*(char **)pval = scf_simple_prop_next_astring(prop);
1838 		break;
1839 	default:
1840 		break;
1841 	}
1842 ret:
1843 	scf_simple_prop_free(prop);
1844 	return (numvals);
1845 }
1846 
1847 /*
1848  * It stores the `pval' for given `pgname'/`pname' property group in SCF.
1849  */
1850 static int
1851 ipmgmt_set_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1852     void *pval, scf_type_t ptype)
1853 {
1854 	scf_error_t		err;
1855 
1856 	if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
1857 		ipmgmt_log(LOG_WARNING, "failed to create property group: %s",
1858 		    scf_strerror(scf_error()));
1859 		return (-1);
1860 	}
1861 
1862 	if (scf_instance_add_pg(res->sr_inst, pgname, SCF_GROUP_APPLICATION,
1863 	    0, res->sr_pg) != 0) {
1864 		if ((err = scf_error()) != SCF_ERROR_EXISTS) {
1865 			ipmgmt_log(LOG_WARNING,
1866 			    "Error adding property group '%s/%s': %s",
1867 			    pgname, pname, scf_strerror(err));
1868 			return (-1);
1869 		}
1870 		/*
1871 		 * if the property group already exists, then we get the
1872 		 * composed view of the property group for the given instance.
1873 		 */
1874 		if (scf_instance_get_pg_composed(res->sr_inst, NULL, pgname,
1875 		    res->sr_pg) != 0) {
1876 			ipmgmt_log(LOG_WARNING, "Error getting composed view "
1877 			    "of the property group '%s/%s': %s", pgname, pname,
1878 			    scf_strerror(scf_error()));
1879 			return (-1);
1880 		}
1881 	}
1882 
1883 	return (ipmgmt_set_scfprop_value(res, pname, pval, ptype));
1884 }
1885 
1886 /*
1887  * Returns B_TRUE, if the non-global zone is being booted for the first time
1888  * after being installed. This is required to setup the ipadm data-store for
1889  * the first boot of the non-global zone. Please see, PSARC 2010/166,
1890  * for more info.
1891  *
1892  * Note that, this API cannot be used to determine first boot post image-update.
1893  * 'pkg image-update' clones the current BE and the existing value of
1894  * ipmgmtd/first_boot_done will be carried forward and obviously it will be set
1895  * to B_TRUE.
1896  */
1897 boolean_t
1898 ipmgmt_ngz_firstboot_postinstall()
1899 {
1900 	scf_resources_t	res;
1901 	boolean_t	bval = B_TRUE;
1902 	char		*strval;
1903 
1904 	/* we always err on the side of caution */
1905 	if (ipmgmt_create_scf_resources(IPMGMTD_FMRI, &res) != 0)
1906 		return (bval);
1907 
1908 	if (ipmgmt_get_scfprop(&res, IPMGMTD_APP_PG, IPMGMTD_PROP_FBD, &strval,
1909 	    SCF_TYPE_ASTRING) > 0) {
1910 		bval = (strcmp(strval, IPMGMTD_TRUESTR) == 0 ?
1911 		    B_FALSE : B_TRUE);
1912 	} else {
1913 		/*
1914 		 * IPMGMTD_PROP_FBD does not exist in the SCF. Lets create it.
1915 		 * Since we err on the side of caution, we ignore the return
1916 		 * error and return B_TRUE.
1917 		 */
1918 		(void) ipmgmt_set_scfprop(&res, IPMGMTD_APP_PG,
1919 		    IPMGMTD_PROP_FBD, IPMGMTD_TRUESTR, SCF_TYPE_ASTRING);
1920 	}
1921 	ipmgmt_release_scf_resources(&res);
1922 	return (bval);
1923 }
1924 
1925 /*
1926  * Returns B_TRUE, if the data-store needs upgrade otherwise returns B_FALSE.
1927  * Today we have to take care of, one case of, upgrading from version 0 to
1928  * version 1, so we will use boolean_t as means to decide if upgrade is needed
1929  * or not. Further, the upcoming projects might completely move the flatfile
1930  * data-store into SCF and hence we shall keep this interface simple.
1931  */
1932 boolean_t
1933 ipmgmt_needs_upgrade(scf_resources_t *res)
1934 {
1935 	boolean_t	bval = B_TRUE;
1936 	int64_t		*verp;
1937 
1938 	if (ipmgmt_get_scfprop(res, IPMGMTD_APP_PG, IPMGMTD_PROP_DBVER,
1939 	    &verp, SCF_TYPE_INTEGER) > 0) {
1940 		if (*verp == IPADM_DB_VERSION)
1941 			bval = B_FALSE;
1942 	}
1943 	/*
1944 	 * 'datastore_version' doesn't exist. Which means that we need to
1945 	 * upgrade the datastore. We will create 'datastore_version' and set
1946 	 * the version value to IPADM_DB_VERSION, after we upgrade the file.
1947 	 */
1948 	return (bval);
1949 }
1950 
1951 /*
1952  * This is called after the successful upgrade of the local data-store. With
1953  * the data-store upgraded to recent version we don't have to do anything on
1954  * subsequent reboots.
1955  */
1956 void
1957 ipmgmt_update_dbver(scf_resources_t *res)
1958 {
1959 	int64_t		version = IPADM_DB_VERSION;
1960 
1961 	(void) ipmgmt_set_scfprop(res, IPMGMTD_APP_PG,
1962 	    IPMGMTD_PROP_DBVER, &version, SCF_TYPE_INTEGER);
1963 }
1964 
1965 /*
1966  * Return TRUE if `ifname' has persistent configuration for the `af' address
1967  * family in the datastore.
1968  * It is possible to call the function with af == AF_UNSPEC, so in this case
1969  * the function returns TRUE if either AF_INET or AF_INET6 interface exists
1970  */
1971 boolean_t
1972 ipmgmt_persist_if_exists(const char *ifname, sa_family_t af)
1973 {
1974 	boolean_t exists = B_FALSE;
1975 	nvlist_t    *if_info_nvl;
1976 	uint16_t    *families = NULL;
1977 	sa_family_t af_db;
1978 	uint_t	nelem = 0;
1979 
1980 	if (ipmgmt_get_ifinfo_nvl(ifname, &if_info_nvl) != 0)
1981 		goto done;
1982 
1983 	if (nvlist_lookup_uint16_array(if_info_nvl, IPADM_NVP_FAMILIES,
1984 	    &families, &nelem) != 0)
1985 		goto done;
1986 
1987 	while (nelem-- > 0) {
1988 		af_db = families[nelem];
1989 		if (af_db == af || (af == AF_UNSPEC &&
1990 		    (af_db == AF_INET || af_db == AF_INET6))) {
1991 			exists = B_TRUE;
1992 			break;
1993 		}
1994 	}
1995 
1996 done:
1997 	if (if_info_nvl != NULL)
1998 		nvlist_free(if_info_nvl);
1999 
2000 	return (exists);
2001 }
2002 
2003 /*
2004  * Retrieves the membership information for the requested mif_name
2005  * if mif_name is a member of a IPMP group, then gif_name will contain
2006  * the name of IPMP group interface, otherwise the variable will be empty
2007  */
2008 void
2009 ipmgmt_get_group_interface(const char *mif_name, char *gif_name, size_t size)
2010 {
2011 	char	*gif_name_from_nvl;
2012 	nvlist_t	*if_info_nvl;
2013 
2014 	gif_name[0] = '\0';
2015 
2016 	if (ipmgmt_get_ifinfo_nvl(mif_name, &if_info_nvl) != 0)
2017 		goto done;
2018 
2019 	if (nvlist_lookup_string(if_info_nvl, IPADM_NVP_GIFNAME,
2020 	    &gif_name_from_nvl) != 0)
2021 		goto done;
2022 
2023 	(void) strlcpy(gif_name, gif_name_from_nvl, size);
2024 
2025 done:
2026 	if (if_info_nvl != NULL)
2027 		nvlist_free(if_info_nvl);
2028 }
2029 
2030 static int
2031 ipmgmt_get_ifinfo_nvl(const char *ifname, nvlist_t **if_info_nvl)
2032 {
2033 	ipmgmt_get_cbarg_t cbarg;
2034 	nvpair_t    *nvp;
2035 	nvlist_t    *nvl;
2036 	int	err;
2037 
2038 	cbarg.cb_ifname = NULL;
2039 	cbarg.cb_aobjname = NULL;
2040 	cbarg.cb_ocnt = 0;
2041 
2042 	*if_info_nvl = NULL;
2043 
2044 	if ((err = nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0)) != 0)
2045 		goto done;
2046 
2047 	err = ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ);
2048 	if (err == ENOENT && cbarg.cb_ocnt > 0)
2049 		err = 0;
2050 
2051 	if (err != 0)
2052 		goto done;
2053 
2054 	for (nvp = nvlist_next_nvpair(cbarg.cb_onvl, NULL); nvp != NULL;
2055 	    nvp = nvlist_next_nvpair(cbarg.cb_onvl, nvp)) {
2056 
2057 		if (strcmp(nvpair_name(nvp), ifname) != 0)
2058 			continue;
2059 
2060 		if ((err = nvpair_value_nvlist(nvp, &nvl)) != 0 ||
2061 		    (err = nvlist_dup(nvl, if_info_nvl, NV_UNIQUE_NAME)) != 0)
2062 			*if_info_nvl = NULL;
2063 
2064 		break;
2065 	}
2066 
2067 done:
2068 	nvlist_free(cbarg.cb_onvl);
2069 
2070 	return (err);
2071 }
2072