xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c (revision 78f5fe539528ce4afb4d8137ae7f8ff44765b467)
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 (c) 2016-2017, Chris Fraire <cfraire@me.com>.
25  * Copyright 2021, Tintri by DDN. All rights reserved.
26  */
27 
28 /*
29  * Main door handler functions used by ipmgmtd to process the different door
30  * call requests, issued by the library libipadm.so.
31  */
32 
33 #include <alloca.h>
34 #include <pwd.h>
35 #include <auth_attr.h>
36 #include <secdb.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <strings.h>
41 #include <errno.h>
42 #include <assert.h>
43 #include <libnvpair.h>
44 #include "ipmgmt_impl.h"
45 
46 /* Handler declaration for each door command */
47 typedef void ipmgmt_door_handler_t(void *argp);
48 
49 static ipmgmt_door_handler_t	ipmgmt_getaddr_handler,
50 				ipmgmt_getprop_handler,
51 				ipmgmt_getif_handler,
52 				ipmgmt_initif_handler,
53 				ipmgmt_aobjop_handler,
54 				ipmgmt_resetaddr_handler,
55 				ipmgmt_setif_handler,
56 				ipmgmt_resetif_handler,
57 				ipmgmt_resetprop_handler,
58 				ipmgmt_setaddr_handler,
59 				ipmgmt_setprop_handler;
60 
61 typedef struct ipmgmt_door_info_s {
62 	uint_t			idi_cmd;
63 	boolean_t		idi_set;
64 	ipmgmt_door_handler_t	*idi_handler;
65 } ipmgmt_door_info_t;
66 
67 /* maps door commands to door handler functions */
68 static ipmgmt_door_info_t i_ipmgmt_door_info_tbl[] = {
69 	{ IPMGMT_CMD_SETPROP,		B_TRUE,  ipmgmt_setprop_handler },
70 	{ IPMGMT_CMD_SETIF,		B_TRUE,  ipmgmt_setif_handler },
71 	{ IPMGMT_CMD_SETADDR,		B_TRUE,  ipmgmt_setaddr_handler },
72 	{ IPMGMT_CMD_GETPROP,		B_FALSE, ipmgmt_getprop_handler },
73 	{ IPMGMT_CMD_GETIF,		B_FALSE, ipmgmt_getif_handler },
74 	{ IPMGMT_CMD_GETADDR,		B_FALSE, ipmgmt_getaddr_handler },
75 	{ IPMGMT_CMD_RESETIF,		B_TRUE,  ipmgmt_resetif_handler },
76 	{ IPMGMT_CMD_RESETADDR,		B_TRUE,  ipmgmt_resetaddr_handler },
77 	{ IPMGMT_CMD_RESETPROP,		B_TRUE,  ipmgmt_resetprop_handler },
78 	{ IPMGMT_CMD_INITIF,		B_TRUE,  ipmgmt_initif_handler },
79 	{ IPMGMT_CMD_ADDROBJ_LOOKUPADD,	B_TRUE,  ipmgmt_aobjop_handler },
80 	{ IPMGMT_CMD_ADDROBJ_SETLIFNUM,	B_TRUE,  ipmgmt_aobjop_handler },
81 	{ IPMGMT_CMD_ADDROBJ_ADD,	B_TRUE,  ipmgmt_aobjop_handler },
82 	{ IPMGMT_CMD_AOBJNAME2ADDROBJ,	B_FALSE, ipmgmt_aobjop_handler },
83 	{ IPMGMT_CMD_LIF2ADDROBJ,	B_FALSE, ipmgmt_aobjop_handler },
84 	{ 0, 0, NULL },
85 };
86 
87 /*
88  * The main server procedure function that gets invoked for any of the incoming
89  * door commands. Inside this function we identify the incoming command and
90  * invoke the right door handler function.
91  */
92 /* ARGSUSED */
93 void
94 ipmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp,
95     uint_t n_desc)
96 {
97 	ipmgmt_door_info_t	*infop = NULL;
98 	ipmgmt_retval_t		retval;
99 	int			i;
100 	uint_t			err;
101 	ucred_t			*cred = NULL;
102 
103 	for (i = 0; i_ipmgmt_door_info_tbl[i].idi_cmd != 0; i++) {
104 		if (i_ipmgmt_door_info_tbl[i].idi_cmd ==
105 		    ((ipmgmt_arg_t *)(void *)argp)->ia_cmd) {
106 			infop = &i_ipmgmt_door_info_tbl[i];
107 			break;
108 		}
109 	}
110 
111 	if (infop == NULL) {
112 		ipmgmt_log(LOG_ERR, "Invalid door command specified");
113 		err = EINVAL;
114 		goto fail;
115 	}
116 
117 	/* check for solaris.network.interface.config authorization */
118 	if (infop->idi_set) {
119 		uid_t		uid;
120 		struct passwd	pwd;
121 		char		buf[1024];
122 
123 		if (door_ucred(&cred) != 0) {
124 			err = errno;
125 			ipmgmt_log(LOG_ERR, "Could not get user credentials.");
126 			goto fail;
127 		}
128 		uid = ucred_getruid(cred);
129 		if ((int)uid < 0) {
130 			err = errno;
131 			ipmgmt_log(LOG_ERR, "Could not get user id.");
132 			goto fail;
133 		}
134 		if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) ==
135 		    NULL) {
136 			err = errno;
137 			ipmgmt_log(LOG_ERR, "Could not get password entry.");
138 			goto fail;
139 		}
140 		if (chkauthattr(NETWORK_INTERFACE_CONFIG_AUTH,
141 		    pwd.pw_name) != 1) {
142 			err = EPERM;
143 			ipmgmt_log(LOG_ERR, "Not authorized for operation.");
144 			goto fail;
145 		}
146 		ucred_free(cred);
147 	}
148 
149 	/* individual handlers take care of calling door_return */
150 	infop->idi_handler((void *)argp);
151 	return;
152 fail:
153 	ucred_free(cred);
154 	retval.ir_err = err;
155 	(void) door_return((char *)&retval, sizeof (retval), NULL, 0);
156 }
157 
158 /*
159  * Handles the door command IPMGMT_CMD_GETPROP. It retrieves the persisted
160  * property value for the given property.
161  */
162 static void
163 ipmgmt_getprop_handler(void *argp)
164 {
165 	ipmgmt_prop_arg_t	*pargp = argp;
166 	ipmgmt_getprop_rval_t	rval, *rvalp = &rval;
167 
168 	assert(pargp->ia_cmd == IPMGMT_CMD_GETPROP);
169 
170 	rvalp->ir_err = ipmgmt_db_walk(ipmgmt_db_getprop, pargp, IPADM_DB_READ);
171 	if (rvalp->ir_err == 0)
172 		(void) strlcpy(rvalp->ir_pval, pargp->ia_pval,
173 		    sizeof (rvalp->ir_pval));
174 	(void) door_return((char *)rvalp, sizeof (*rvalp), NULL, 0);
175 }
176 
177 /*
178  * Handles the door command IPMGMT_CMD_SETPROP. It persists the property value
179  * for the given property in the DB.
180  */
181 static void
182 ipmgmt_setprop_handler(void *argp)
183 {
184 	ipmgmt_prop_arg_t	*pargp = argp;
185 	ipmgmt_retval_t		rval;
186 	ipadm_dbwrite_cbarg_t	cb;
187 	nvlist_t		*nvl = NULL;
188 	int			err;
189 
190 	assert(pargp->ia_cmd == IPMGMT_CMD_SETPROP);
191 
192 	if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0)
193 		goto fail;
194 	if (pargp->ia_module[0] != '\0' &&
195 	    (err = nvlist_add_string(nvl, IPADM_NVP_PROTONAME,
196 	    pargp->ia_module)) != 0) {
197 		goto fail;
198 	}
199 	if (pargp->ia_ifname[0] != '\0' &&
200 	    (err = nvlist_add_string(nvl, IPADM_NVP_IFNAME,
201 	    pargp->ia_ifname)) != 0)
202 		goto fail;
203 	if (pargp->ia_aobjname[0] != '\0' &&
204 	    (err = nvlist_add_string(nvl, IPADM_NVP_AOBJNAME,
205 	    pargp->ia_aobjname)) != 0)
206 		goto fail;
207 	if ((err = nvlist_add_string(nvl, pargp->ia_pname,
208 	    pargp->ia_pval)) != 0)
209 		goto fail;
210 
211 	cb.dbw_nvl = nvl;
212 	cb.dbw_flags = pargp->ia_flags;
213 	err = ipmgmt_db_walk(ipmgmt_db_update, &cb, IPADM_DB_WRITE);
214 fail:
215 	nvlist_free(nvl);
216 	rval.ir_err = err;
217 	(void) door_return((char *)&rval, sizeof (rval), NULL, 0);
218 }
219 
220 /*
221  * Helper function for ipmgmt_setaddr_handler().
222  * It converts the nvlist_t, `nvl', to aobjmap node `nodep'.
223  */
224 static int
225 i_ipmgmt_nvl2aobjnode(nvlist_t *nvl, ipmgmt_aobjmap_t *nodep)
226 {
227 	char			*aobjname = NULL, *ifname = NULL;
228 	int32_t			lnum;
229 	nvlist_t		*nvladdr;
230 	sa_family_t		af = AF_UNSPEC;
231 	ipadm_addr_type_t	addrtype = IPADM_ADDR_NONE;
232 	int			err = 0;
233 
234 	/*
235 	 * Retrieve all the information needed to build '*nodep' from
236 	 * nvlist_t nvl.
237 	 */
238 	if ((err = nvlist_lookup_string(nvl, IPADM_NVP_AOBJNAME,
239 	    &aobjname)) != 0 ||
240 	    (err = nvlist_lookup_string(nvl, IPADM_NVP_IFNAME, &ifname)) != 0 ||
241 	    (err = nvlist_lookup_int32(nvl, IPADM_NVP_LIFNUM, &lnum)) != 0) {
242 		return (err);
243 	}
244 	if (nvlist_exists(nvl, IPADM_NVP_IPV4ADDR)) {
245 		af = AF_INET;
246 		addrtype = IPADM_ADDR_STATIC;
247 	} else if (nvlist_lookup_nvlist(nvl, IPADM_NVP_DHCP, &nvladdr) == 0) {
248 		char	*reqhost;
249 
250 		af = AF_INET;
251 		addrtype = IPADM_ADDR_DHCP;
252 
253 		/*
254 		 * ipmgmt_am_reqhost comes through in `nvl' for purposes of
255 		 * updating the cached representation, but it is persisted as
256 		 * a stand-alone DB line; so remove it after copying it.
257 		 */
258 		if (!nvlist_exists(nvl, IPADM_NVP_REQHOST)) {
259 			*nodep->ipmgmt_am_reqhost = '\0';
260 		} else {
261 			if ((err = nvlist_lookup_string(nvl, IPADM_NVP_REQHOST,
262 			    &reqhost)) != 0)
263 				return (err);
264 
265 			(void) strlcpy(nodep->ipmgmt_am_reqhost, reqhost,
266 			    sizeof (nodep->ipmgmt_am_reqhost));
267 			(void) nvlist_remove(nvl, IPADM_NVP_REQHOST,
268 			    DATA_TYPE_STRING);
269 		}
270 	} else if (nvlist_exists(nvl, IPADM_NVP_IPV6ADDR)) {
271 		af = AF_INET6;
272 		addrtype = IPADM_ADDR_STATIC;
273 	} else if (nvlist_lookup_nvlist(nvl, IPADM_NVP_INTFID, &nvladdr) == 0) {
274 		struct sockaddr_in6		sin6 = {0};
275 		uint8_t	*addr6;
276 		uint32_t plen;
277 		uint_t	n;
278 
279 		af = AF_INET6;
280 		addrtype = IPADM_ADDR_IPV6_ADDRCONF;
281 		if (nvlist_lookup_uint32(nvladdr, IPADM_NVP_PREFIXLEN,
282 		    &plen) != 0)
283 			return (EINVAL);
284 		if (plen != 0) {
285 			if (nvlist_lookup_uint8_array(nvladdr,
286 			    IPADM_NVP_IPNUMADDR, &addr6, &n) != 0)
287 				return (EINVAL);
288 			bcopy(addr6, &sin6.sin6_addr, n);
289 		}
290 
291 		nodep->ipmgmt_am_linklocal = B_TRUE;
292 		nodep->ipmgmt_am_ifid = sin6;
293 	}
294 
295 	/*
296 	 * populate the non-addrtype-specific `*nodep' with retrieved values.
297 	 */
298 	(void) strlcpy(nodep->am_ifname, ifname, sizeof (nodep->am_ifname));
299 	(void) strlcpy(nodep->am_aobjname, aobjname,
300 	    sizeof (nodep->am_aobjname));
301 	nodep->am_lnum = lnum;
302 	nodep->am_family = af;
303 	nodep->am_atype = addrtype;
304 	nodep->am_next = NULL;
305 
306 	/*
307 	 * Do not store logical interface number in persistent store as it
308 	 * takes different value on reboot. So remove it from `nvl'.
309 	 */
310 	if (nvlist_exists(nvl, IPADM_NVP_LIFNUM))
311 		(void) nvlist_remove(nvl, IPADM_NVP_LIFNUM, DATA_TYPE_INT32);
312 
313 	return (0);
314 }
315 
316 /*
317  * Handles the door command IPMGMT_CMD_SETADDR. It adds a new address object
318  * node to the list `aobjmap' and optionally persists the address
319  * information in the DB.
320  */
321 static void
322 ipmgmt_setaddr_handler(void *argp)
323 {
324 	ipmgmt_setaddr_arg_t	*sargp = argp;
325 	ipmgmt_retval_t		rval;
326 	ipmgmt_aobjmap_t	node = {0};
327 	nvlist_t		*nvl = NULL;
328 	char			*nvlbuf;
329 	size_t			nvlsize = sargp->ia_nvlsize;
330 	uint32_t		flags = sargp->ia_flags;
331 	int			err = 0;
332 
333 	nvlbuf = (char *)argp + sizeof (ipmgmt_setaddr_arg_t);
334 	if ((err = nvlist_unpack(nvlbuf, nvlsize, &nvl, 0)) != 0)
335 		goto ret;
336 	if (flags & (IPMGMT_ACTIVE|IPMGMT_INIT)) {
337 		if ((err = i_ipmgmt_nvl2aobjnode(nvl, &node)) != 0)
338 			goto ret;
339 		if (flags & IPMGMT_INIT)
340 			node.am_flags = (IPMGMT_ACTIVE|IPMGMT_PERSIST);
341 		else
342 			node.am_flags = flags & ~IPMGMT_PROPS_ONLY;
343 		if ((err = ipmgmt_aobjmap_op(&node, ADDROBJ_ADD)) != 0)
344 			goto ret;
345 	}
346 	if ((flags & IPMGMT_PERSIST) && !(flags & IPMGMT_PROPS_ONLY)) {
347 		ipadm_dbwrite_cbarg_t	cb;
348 
349 		cb.dbw_nvl = nvl;
350 		cb.dbw_flags = 0;
351 		err = ipmgmt_db_walk(ipmgmt_db_add, &cb, IPADM_DB_WRITE);
352 	}
353 ret:
354 	nvlist_free(nvl);
355 	rval.ir_err = err;
356 	(void) door_return((char *)&rval, sizeof (rval), NULL, 0);
357 }
358 
359 /*
360  * Handles the door commands that read or modify the `aobjmap' structure.
361  *
362  * IPMGMT_CMD_ADDROBJ_LOOKUPADD - places a stub address object in `aobjmap'
363  *	after ensuring that the namespace is not taken. If required, also
364  *	generates an `aobjname' for address object for the library to use.
365  * IPMGMT_CMD_ADDROBJ_ADD - add/update address object in `aobjmap'
366  * IPMGMT_CMD_LIF2ADDROBJ - given a logical interface, return address object
367  *	associated with that logical interface.
368  * IPMGMT_CMD_AOBJNAME2ADDROBJ - given an address object name return logical
369  *	interface associated with that address object.
370  */
371 static void
372 ipmgmt_aobjop_handler(void *argp)
373 {
374 	ipmgmt_aobjop_arg_t	*largp = argp;
375 	ipmgmt_retval_t		rval;
376 	ipmgmt_aobjop_rval_t	aobjrval;
377 	void			*rvalp;
378 	size_t			rsize;
379 	ipmgmt_aobjmap_t	node;
380 	int			err = 0;
381 	char			*ifname = largp->ia_ifname;
382 	char			*aobjname = largp->ia_aobjname;
383 	int32_t			lnum = largp->ia_lnum;
384 	sa_family_t		af = largp->ia_family;
385 	ipadm_addr_type_t	atype = largp->ia_atype;
386 	ipmgmt_aobjmap_t	*head;
387 
388 	switch (largp->ia_cmd) {
389 	case IPMGMT_CMD_ADDROBJ_LOOKUPADD:
390 		rsize = sizeof (ipmgmt_aobjop_rval_t);
391 		rvalp = &aobjrval;
392 		bzero(&node, sizeof (node));
393 		(void) strlcpy(node.am_aobjname, aobjname,
394 		    sizeof (node.am_aobjname));
395 		(void) strlcpy(node.am_ifname, ifname,
396 		    sizeof (node.am_ifname));
397 		node.am_family = af;
398 		node.am_atype = atype;
399 		/* no logical number is associated with this addrobj yet */
400 		node.am_lnum = -1;
401 		/* The address object is not persisted yet. */
402 		node.am_flags = IPMGMT_ACTIVE;
403 		err = ipmgmt_aobjmap_op(&node, ADDROBJ_LOOKUPADD);
404 		if (err == 0) {
405 			(void) strlcpy(aobjrval.ir_aobjname, node.am_aobjname,
406 			    sizeof (aobjrval.ir_aobjname));
407 		}
408 		break;
409 	case IPMGMT_CMD_ADDROBJ_SETLIFNUM:
410 		rsize = sizeof (ipmgmt_retval_t);
411 		rvalp = &rval;
412 		bzero(&node, sizeof (node));
413 		(void) strlcpy(node.am_aobjname, aobjname,
414 		    sizeof (node.am_aobjname));
415 		(void) strlcpy(node.am_ifname, ifname,
416 		    sizeof (node.am_ifname));
417 		node.am_family = af;
418 		node.am_lnum = lnum;
419 		err = ipmgmt_aobjmap_op(&node, ADDROBJ_SETLIFNUM);
420 		break;
421 	case IPMGMT_CMD_ADDROBJ_ADD:
422 		rsize = sizeof (ipmgmt_retval_t);
423 		rvalp = &rval;
424 		if (aobjname[0] == '\0' || ifname[0] == '\0' || lnum == -1 ||
425 		    af == AF_UNSPEC) {
426 			err = EINVAL;
427 			break;
428 		}
429 		bzero(&node, sizeof (node));
430 		(void) strlcpy(node.am_aobjname, aobjname,
431 		    sizeof (node.am_aobjname));
432 		(void) strlcpy(node.am_ifname, ifname,
433 		    sizeof (node.am_ifname));
434 		node.am_atype = atype;
435 		node.am_lnum = lnum;
436 		node.am_family = af;
437 		/* The address object is not persisted. */
438 		node.am_flags = IPMGMT_ACTIVE;
439 		err = ipmgmt_aobjmap_op(&node, ADDROBJ_ADD);
440 		break;
441 	case IPMGMT_CMD_AOBJNAME2ADDROBJ:
442 		rsize = sizeof (ipmgmt_aobjop_rval_t);
443 		rvalp = &aobjrval;
444 		bzero(&aobjrval, sizeof (aobjrval));
445 		if (aobjname[0] == '\0') {
446 			err = EINVAL;
447 			break;
448 		}
449 		(void) pthread_rwlock_rdlock(&aobjmap.aobjmap_rwlock);
450 		head = aobjmap.aobjmap_head;
451 		for (; head; head = head->am_next) {
452 			if (strcmp(head->am_aobjname, aobjname) != 0)
453 				continue;
454 			/*
455 			 * For an auto-configured interface, return
456 			 * the lifnum that has the link-local on it.
457 			 * Other logical interfaces were created for
458 			 * prefixes and dhcpv6 addresses and do not
459 			 * have am_ifid set.
460 			 */
461 			if (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF ||
462 			    head->ipmgmt_am_linklocal) {
463 				break;
464 			}
465 		}
466 		if (head == NULL) {
467 			err = ENOENT;
468 			(void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
469 			break;
470 		}
471 		(void) strlcpy(aobjrval.ir_ifname, head->am_ifname,
472 		    sizeof (aobjrval.ir_ifname));
473 		aobjrval.ir_lnum = head->am_lnum;
474 		aobjrval.ir_family = head->am_family;
475 		aobjrval.ir_flags = head->am_flags;
476 		aobjrval.ir_atype = head->am_atype;
477 		aobjrval.ir_atype_cache = head->am_atype_cache;
478 		(void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
479 		break;
480 	case IPMGMT_CMD_LIF2ADDROBJ:
481 		rsize = sizeof (ipmgmt_aobjop_rval_t);
482 		rvalp = &aobjrval;
483 		bzero(&aobjrval, sizeof (aobjrval));
484 		if (ifname[0] == '\0') {
485 			err = EINVAL;
486 			break;
487 		}
488 		(void) pthread_rwlock_rdlock(&aobjmap.aobjmap_rwlock);
489 		head = aobjmap.aobjmap_head;
490 		for (; head; head = head->am_next) {
491 			if (strcmp(head->am_ifname, ifname) == 0 &&
492 			    head->am_lnum == lnum &&
493 			    head->am_family == af) {
494 				break;
495 			}
496 		}
497 		if (head == NULL) {
498 			err = ENOENT;
499 			(void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
500 			break;
501 		}
502 		(void) strlcpy(aobjrval.ir_aobjname, head->am_aobjname,
503 		    sizeof (aobjrval.ir_aobjname));
504 		aobjrval.ir_atype = head->am_atype;
505 		aobjrval.ir_flags = head->am_flags;
506 		aobjrval.ir_atype_cache = head->am_atype_cache;
507 		(void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
508 		break;
509 	default:
510 		rsize = sizeof (ipmgmt_retval_t);
511 		rvalp = &rval;
512 		err = EINVAL;
513 	}
514 	((ipmgmt_retval_t *)rvalp)->ir_err = err;
515 	(void) door_return((char *)rvalp, rsize, NULL, 0);
516 }
517 
518 /*
519  * Given an interface name and family, deletes all the address objects
520  * associated with it.
521  */
522 void
523 i_ipmgmt_delif_aobjs(char *ifname, sa_family_t af, uint32_t flags)
524 {
525 	ipmgmt_aobjmap_t	*head, *next, *prev;
526 	ipadm_db_op_t		db_op;
527 
528 	prev = NULL;
529 
530 	(void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock);
531 	head = aobjmap.aobjmap_head;
532 	for (; head; head = next) {
533 		next = head->am_next;
534 		if (strcmp(head->am_ifname, ifname) != 0 ||
535 		    head->am_family != af) {
536 			prev = head;
537 			continue;
538 		}
539 
540 		if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) &&
541 		    flags == IPMGMT_ACTIVE) {
542 			/*
543 			 * If the addres is present in both active and
544 			 * persistent store, and if we are performing
545 			 * a temporary delete, we update the node to
546 			 * indicate that the address is only present in
547 			 * persistent store and we proceed. Otherwise
548 			 * we always delete the node from aobjmap.
549 			 */
550 			head->am_flags &= ~IPMGMT_ACTIVE;
551 			head->am_lnum = -1;
552 			db_op = IPADM_DB_WRITE;
553 		} else {
554 			db_op = IPADM_DB_DELETE;
555 			if (prev == NULL)
556 				aobjmap.aobjmap_head = next;
557 			else
558 				prev->am_next = next;
559 		}
560 		(void) ipmgmt_persist_aobjmap(head, db_op);
561 		if (db_op == IPADM_DB_DELETE)
562 			free(head);
563 	}
564 	(void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
565 }
566 
567 /*
568  * Handles the door command IPMGMT_CMD_SETIF. It persists the interface
569  * information in the DB.
570  */
571 static void
572 ipmgmt_setif_handler(void *argp)
573 {
574 	ipmgmt_retval_t		rval;
575 
576 	rval.ir_err = ipmgmt_persist_if(argp);
577 	(void) door_return((char *)&rval, sizeof (rval), NULL, 0);
578 }
579 
580 /*
581  * Handles the door command IPMGMT_CMD_RESETIF. For the given interface,
582  * deletes all the persisted interface configuration. It also deletes, from
583  * `aobjmap', all the address objects configured on the given interface.
584  */
585 static void
586 ipmgmt_resetif_handler(void *argp)
587 {
588 	ipmgmt_if_arg_t		*rargp = argp;
589 	ipmgmt_retval_t		rval;
590 	ipmgmt_if_cbarg_t	cbarg;
591 	uint32_t		flags = rargp->ia_flags;
592 	int			err = 0;
593 
594 	cbarg.cb_family = rargp->ia_family;
595 	cbarg.cb_ifname = rargp->ia_ifname;
596 	if (flags & IPMGMT_PERSIST)
597 		err = ipmgmt_db_walk(ipmgmt_db_resetif, &cbarg,
598 		    IPADM_DB_DELETE);
599 
600 	if (flags & IPMGMT_ACTIVE)
601 		i_ipmgmt_delif_aobjs(rargp->ia_ifname, rargp->ia_family,
602 		    flags);
603 
604 	rval.ir_err = err;
605 	(void) door_return((char *)&rval, sizeof (rval), NULL, 0);
606 }
607 
608 /*
609  * Handles the door command IPMGMT_CMD_RESETADDR. For the given addrobj
610  * deletes all the persisted addrobj configuration. It also deletes the
611  * corresponding node, from `aobjmap'.
612  */
613 static void
614 ipmgmt_resetaddr_handler(void *argp)
615 {
616 	ipmgmt_addr_arg_t	*rargp = argp;
617 	ipmgmt_retval_t		rval;
618 	ipmgmt_aobjmap_t	node;
619 	uint32_t		flags = rargp->ia_flags;
620 	int			err = 0;
621 	ipmgmt_resetaddr_cbarg_t cbarg;
622 
623 	cbarg.cb_aobjname = rargp->ia_aobjname;
624 
625 	if (flags & IPMGMT_PERSIST)
626 		err = ipmgmt_db_walk(ipmgmt_db_resetaddr, &cbarg,
627 		    IPADM_DB_DELETE);
628 
629 	if (flags & IPMGMT_ACTIVE) {
630 		bzero(&node, sizeof (node));
631 		(void) strlcpy(node.am_aobjname, rargp->ia_aobjname,
632 		    sizeof (node.am_aobjname));
633 
634 		/*
635 		 * am_lnum is used only for IPv6 autoconf case, since there
636 		 * can be multiple nodes with the same aobjname.
637 		 */
638 		node.am_lnum = rargp->ia_lnum;
639 		node.am_flags = flags;
640 		(void) ipmgmt_aobjmap_op(&node, ADDROBJ_DELETE);
641 	}
642 
643 	rval.ir_err = err;
644 	(void) door_return((char *)&rval, sizeof (rval), NULL, 0);
645 }
646 
647 /*
648  * Handles the door command IPMGMT_CMD_GETADDR. It retrieves the persisted
649  * address for a given `gargp->ia_aobjname'. If it is not defined then it
650  * retrieves all the addresses configured on `gargp->ia_ifname'. The
651  * "ipadm show-addr addrobj" or "ipadm show-addr <ifname>/\*" will call this
652  * handler through library.
653  */
654 static void
655 ipmgmt_getaddr_handler(void *argp)
656 {
657 	size_t			buflen, onvlsize;
658 	char			*buf, *onvlbuf;
659 	ipmgmt_getaddr_arg_t	*gargp = argp;
660 	ipmgmt_getaddr_cbarg_t	cbarg;
661 	ipmgmt_get_rval_t	rval, *rvalp = &rval;
662 	int			err = 0;
663 
664 	cbarg.cb_ifname = gargp->ia_ifname;
665 	cbarg.cb_aobjname = gargp->ia_aobjname;
666 	cbarg.cb_ocnt = 0;
667 	if (nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0) != 0)
668 		goto fail;
669 	err = ipmgmt_db_walk(ipmgmt_db_getaddr, &cbarg, IPADM_DB_READ);
670 	if (err == ENOENT && cbarg.cb_ocnt > 0) {
671 		/*
672 		 * If there is atleast one entry in the nvlist,
673 		 * do not return error.
674 		 */
675 		err = 0;
676 	}
677 	if (err != 0)
678 		goto fail;
679 
680 	if ((err = nvlist_size(cbarg.cb_onvl, &onvlsize,
681 	    NV_ENCODE_NATIVE)) != 0) {
682 		goto fail;
683 	}
684 
685 	if (onvlsize > (UINT32_MAX - sizeof (ipmgmt_get_rval_t)))
686 		goto fail;
687 
688 	buflen = onvlsize + sizeof (ipmgmt_get_rval_t);
689 	/*
690 	 * We cannot use malloc() here because door_return never returns, and
691 	 * memory allocated by malloc() would get leaked. Use alloca() instead.
692 	 */
693 	buf = alloca(buflen);
694 	onvlbuf = buf + sizeof (ipmgmt_get_rval_t);
695 	if ((err = nvlist_pack(cbarg.cb_onvl, &onvlbuf, &onvlsize,
696 	    NV_ENCODE_NATIVE, 0)) != 0) {
697 		goto fail;
698 	}
699 	nvlist_free(cbarg.cb_onvl);
700 	rvalp = (ipmgmt_get_rval_t *)(void *)buf;
701 	rvalp->ir_err = 0;
702 	rvalp->ir_nvlsize = onvlsize;
703 
704 	(void) door_return(buf, buflen, NULL, 0);
705 	return;
706 fail:
707 	nvlist_free(cbarg.cb_onvl);
708 	rvalp->ir_err = err;
709 	(void) door_return((char *)rvalp, sizeof (*rvalp), NULL, 0);
710 }
711 
712 /*
713  * Handles the door command IPMGMT_CMD_RESETPROP. It deletes the property line
714  * from the DB.
715  */
716 static void
717 ipmgmt_resetprop_handler(void *argp)
718 {
719 	ipmgmt_prop_arg_t	*pargp = argp;
720 	ipmgmt_retval_t		rval;
721 
722 	assert(pargp->ia_cmd == IPMGMT_CMD_RESETPROP);
723 
724 	rval.ir_err = ipmgmt_db_walk(ipmgmt_db_resetprop, pargp,
725 	    IPADM_DB_DELETE);
726 	(void) door_return((char *)&rval, sizeof (rval), NULL, 0);
727 }
728 
729 /*
730  * Handles the door command IPMGMT_CMD_GETIF. It retrieves the name of all the
731  * persisted interfaces and the IP protocols (IPv4 or IPv6) they support.
732  */
733 static void
734 ipmgmt_getif_handler(void *argp)
735 {
736 	ipmgmt_getif_arg_t	*getif = argp;
737 	ipmgmt_getif_rval_t	*rvalp;
738 	ipmgmt_retval_t		rval;
739 	ipmgmt_getif_cbarg_t	cbarg;
740 	ipadm_if_info_list_t	*ifl, *curifl;
741 	ipadm_if_info_t		*ifp, *rifp;
742 	int			i, err = 0, count = 0;
743 	size_t			rbufsize;
744 
745 	assert(getif->ia_cmd == IPMGMT_CMD_GETIF);
746 
747 	bzero(&cbarg, sizeof (cbarg));
748 	cbarg.cb_ifname = getif->ia_ifname;
749 	err = ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ);
750 	if (err == ENOENT && cbarg.cb_ifinfo) {
751 		/*
752 		 * If there is atleast one entry in the nvlist,
753 		 * do not return error.
754 		 */
755 		err = 0;
756 	}
757 	if (err != 0) {
758 		rval.ir_err = err;
759 		(void) door_return((char *)&rval, sizeof (rval), NULL, 0);
760 		return;
761 	}
762 
763 	/* allocate sufficient buffer to return the interface info */
764 	for (ifl = cbarg.cb_ifinfo; ifl != NULL; ifl = ifl->ifil_next)
765 		++count;
766 	rbufsize = sizeof (*rvalp) + count * sizeof (*ifp);
767 	rvalp = alloca(rbufsize);
768 	bzero(rvalp, rbufsize);
769 
770 	rvalp->ir_ifcnt = count;
771 	rifp = rvalp->ir_ifinfo;
772 	ifl = cbarg.cb_ifinfo;
773 
774 	/*
775 	 * copy the interface info to buffer allocated on stack. The reason
776 	 * we do this is to avoid memory leak, as door_return() would never
777 	 * return
778 	 */
779 	for (i = 0; i < count; i++) {
780 		ifp = &ifl->ifil_ifi;
781 		rifp = rvalp->ir_ifinfo + i;
782 		(void) bcopy(ifp, rifp, sizeof (*rifp));
783 		curifl = ifl->ifil_next;
784 		free(ifl);
785 		ifl = curifl;
786 	}
787 	rvalp->ir_err = err;
788 	(void) door_return((char *)rvalp, rbufsize, NULL, 0);
789 }
790 
791 /*
792  * Handles the door command IPMGMT_CMD_INITIF. It retrieves all the persisted
793  * interface configuration (interface properties and addresses), for all those
794  * interfaces that need to be initialized.
795  */
796 static void
797 ipmgmt_initif_handler(void *argp)
798 {
799 	ipmgmt_initif_arg_t	*initif = argp;
800 	size_t			buflen, nvlsize;
801 	char			*buf = NULL, *onvlbuf, *invlbuf;
802 	ipmgmt_get_rval_t	rval, *rvalp = &rval;
803 	ipmgmt_initif_cbarg_t	cbarg;
804 	int			err;
805 
806 	assert(initif->ia_cmd == IPMGMT_CMD_INITIF);
807 
808 	bzero(&cbarg, sizeof (cbarg));
809 	invlbuf = (char *)argp + sizeof (ipmgmt_initif_arg_t);
810 	nvlsize = initif->ia_nvlsize;
811 	err = nvlist_unpack(invlbuf, nvlsize, &cbarg.cb_invl, 0);
812 	if (err != 0)
813 		goto fail;
814 
815 	cbarg.cb_family = initif->ia_family;
816 	if (nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0) != 0)
817 		goto fail;
818 
819 	err = ipmgmt_db_walk(ipmgmt_db_initif, &cbarg, IPADM_DB_READ);
820 	if (err == ENOENT && cbarg.cb_ocnt > 0) {
821 		/*
822 		 * If there is atleast one entry in the nvlist,
823 		 * do not return error.
824 		 */
825 		err = 0;
826 	}
827 	if (err != 0)
828 		goto fail;
829 
830 	if ((err = nvlist_size(cbarg.cb_onvl, &nvlsize, NV_ENCODE_NATIVE)) != 0)
831 		goto fail;
832 
833 	if (nvlsize > (UINT32_MAX - sizeof (ipmgmt_get_rval_t)))
834 		goto fail;
835 
836 	buflen = nvlsize + sizeof (ipmgmt_get_rval_t);
837 	/*
838 	 * We cannot use malloc() here because door_return never returns, and
839 	 * memory allocated by malloc() would get leaked. Use alloca() instead.
840 	 */
841 	buf = alloca(buflen);
842 	onvlbuf = buf + sizeof (ipmgmt_get_rval_t);
843 	if ((err = nvlist_pack(cbarg.cb_onvl, &onvlbuf, &nvlsize,
844 	    NV_ENCODE_NATIVE, 0)) != 0) {
845 		goto fail;
846 	}
847 	nvlist_free(cbarg.cb_invl);
848 	nvlist_free(cbarg.cb_onvl);
849 	rvalp = (ipmgmt_get_rval_t *)(void *)buf;
850 	rvalp->ir_err = 0;
851 	rvalp->ir_nvlsize = nvlsize;
852 
853 	(void) door_return(buf, buflen, NULL, 0);
854 	return;
855 fail:
856 	nvlist_free(cbarg.cb_invl);
857 	nvlist_free(cbarg.cb_onvl);
858 	rvalp->ir_err = err;
859 	(void) door_return((char *)rvalp, sizeof (*rvalp), NULL, 0);
860 }
861 
862 int
863 ipmgmt_persist_if(ipmgmt_if_arg_t *sargp)
864 {
865 	ipadm_dbwrite_cbarg_t	cb;
866 	uint32_t		flags = sargp->ia_flags;
867 	nvlist_t		*nvl = NULL;
868 	int			err = 0;
869 	char			strval[IPMGMT_STRSIZE];
870 
871 	if (!(flags & IPMGMT_PERSIST) || sargp->ia_family == AF_UNSPEC ||
872 	    sargp->ia_ifname[0] == '\0') {
873 		err = EINVAL;
874 		goto ret;
875 	}
876 	if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0)
877 		goto ret;
878 	if ((err = nvlist_add_string(nvl, IPADM_NVP_IFNAME,
879 	    sargp->ia_ifname)) != 0)
880 		goto ret;
881 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", sargp->ia_family);
882 	if ((err = nvlist_add_string(nvl, IPADM_NVP_FAMILY, strval)) != 0)
883 		goto ret;
884 	cb.dbw_nvl = nvl;
885 	cb.dbw_flags = 0;
886 	err = ipmgmt_db_walk(ipmgmt_db_add, &cb, IPADM_DB_WRITE);
887 ret:
888 	nvlist_free(nvl);
889 	return (err);
890 }
891