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