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