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