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