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