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