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