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