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