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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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