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