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