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