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