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