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