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