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