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  */
  26 
  27 #include <errno.h>
  28 #include <sys/sockio.h>
  29 #include <sys/list.h>
  30 #include <string.h>
  31 #include <assert.h>
  32 #include <unistd.h>
  33 #include <stropts.h>
  34 #include <strings.h>
  35 #include <libdlpi.h>
  36 #include <libdllink.h>
  37 #include <libinetutil.h>
  38 #include <inet/ip.h>
  39 #include <limits.h>
  40 #include <zone.h>
  41 #include <ipadm_ndpd.h>
  42 #include <ipmp_query.h>
  43 #include "libipadm_impl.h"
  44 
  45 static ipadm_status_t   i_ipadm_slifname_arp(char *, uint64_t, int);
  46 static ipadm_status_t   i_ipadm_slifname(ipadm_handle_t, char *, char *,
  47                             uint64_t, int, uint32_t);
  48 static ipadm_status_t   i_ipadm_create_ipmp_peer(ipadm_handle_t, char *,
  49                             sa_family_t);
  50 static ipadm_status_t   i_ipadm_persist_if(ipadm_handle_t, const char *,
  51                             sa_family_t, uint32_t);
  52 static ipadm_status_t   i_ipadm_allocate_ifinfo(ipadm_if_info_t **);
  53 static ipadm_status_t   i_ipadm_get_db_if(ipadm_handle_t, const char *,
  54                             nvlist_t **);
  55 static ipadm_status_t i_ipadm_nvl2ifinfo(nvlist_t *, ipadm_if_info_t **);
  56 static ipadm_status_t i_ipadm_fill_cmembers(char *, ipadm_ipmp_members_t *);
  57 static ipadm_status_t i_ipadm_fill_pmembers(nvlist_t *, ipadm_ipmp_members_t *);
  58 static ipadm_status_t i_ipadm_add_persistent_if_info(ipadm_if_info_t *,
  59                     ipadm_if_info_t *);
  60 static void i_ipadm_free_ipmp_members(ipadm_ipmp_members_t *);
  61 static ipadm_status_t i_ipadm_persist_update_ipmp(ipadm_handle_t, const char *,
  62         const char *,
  63         ipadm_ipmp_op_t);
  64 static ipadm_status_t i_ipadm_update_ipmp(ipadm_handle_t, const char *,
  65         const char *, uint32_t,
  66         ipadm_ipmp_op_t);
  67 
  68 /*
  69  * Returns B_FALSE if the interface in `ifname' has at least one address that is
  70  * IFF_UP in the addresses in `ifa'.
  71  */
  72 static boolean_t
  73 i_ipadm_is_if_down(char *ifname, struct ifaddrs *ifa)
  74 {
  75         struct ifaddrs  *ifap;
  76         char            cifname[LIFNAMSIZ];
  77         char            *sep;
  78 
  79         for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
  80                 (void) strlcpy(cifname, ifap->ifa_name, sizeof (cifname));
  81                 if ((sep = strrchr(cifname, IPADM_LOGICAL_SEP)) != NULL)
  82                         *sep = '\0';
  83                 /*
  84                  * If this condition is true, there is at least one
  85                  * address that is IFF_UP. So, we need to return B_FALSE.
  86                  */
  87                 if (strcmp(cifname, ifname) == 0 &&
  88                     (ifap->ifa_flags & IFF_UP)) {
  89                         return (B_FALSE);
  90                 }
  91         }
  92         /* We did not find any IFF_UP addresses. */
  93         return (B_TRUE);
  94 }
  95 
  96 /*
  97  * Retrieves the information for the interface `ifname' from active
  98  * config if `ifname' is specified and returns the result in the list `if_info'.
  99  * Otherwise, it retrieves the information for all the interfaces in
 100  * the active config and returns the result in the list `if_info'.
 101  */
 102 static ipadm_status_t
 103 i_ipadm_active_if_info(ipadm_handle_t iph, const char *ifname,
 104     ipadm_if_info_t **if_info, int64_t lifc_flags)
 105 {
 106         struct lifreq   *buf;
 107         struct lifreq   *lifrp;
 108         struct lifreq   lifrl;
 109         ipadm_if_info_t *last = NULL;
 110         ipadm_if_info_t *ifp;
 111         int             s;
 112         int             n;
 113         int             numifs;
 114         ipadm_status_t  status;
 115 
 116         *if_info = NULL;
 117         /*
 118          * Get information for all interfaces.
 119          */
 120         if (getallifs(iph->iph_sock, 0, &buf, &numifs, lifc_flags) != 0)
 121                 return (ipadm_errno2status(errno));
 122 
 123         lifrp = buf;
 124         for (n = 0; n < numifs; n++, lifrp++) {
 125                 /* Skip interfaces with logical num != 0 */
 126                 if (i_ipadm_get_lnum(lifrp->lifr_name) != 0)
 127                         continue;
 128                 /*
 129                  * Skip the current interface if a specific `ifname' has
 130                  * been requested and current interface does not match
 131                  * `ifname'.
 132                  */
 133                 if (ifname != NULL && strcmp(lifrp->lifr_name, ifname) != 0)
 134                         continue;
 135                 /*
 136                  * Check if the interface already exists in our list.
 137                  * If it already exists, we need to update its flags.
 138                  */
 139                 for (ifp = *if_info; ifp != NULL; ifp = ifp->ifi_next) {
 140                         if (strcmp(lifrp->lifr_name, ifp->ifi_name) == 0)
 141                                 break;
 142                 }
 143                 if (ifp == NULL) {
 144                         if ((status =
 145                             i_ipadm_allocate_ifinfo(&ifp)) != IPADM_SUCCESS)
 146                                         break;
 147 
 148                         (void) strlcpy(ifp->ifi_name, lifrp->lifr_name,
 149                             sizeof (ifp->ifi_name));
 150                         /* Update the `ifi_next' pointer for this new node */
 151                         if (*if_info == NULL)
 152                                 *if_info = ifp;
 153                         else
 154                                 last->ifi_next = ifp;
 155                         last = ifp;
 156                 }
 157 
 158                 /*
 159                  * Retrieve the flags for the interface by doing a
 160                  * SIOCGLIFFLAGS to populate the `ifi_cflags' field.
 161                  */
 162                 (void) strlcpy(lifrl.lifr_name,
 163                     lifrp->lifr_name, sizeof (lifrl.lifr_name));
 164                 s = (lifrp->lifr_addr.ss_family == AF_INET) ?
 165                     iph->iph_sock : iph->iph_sock6;
 166                 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
 167                         continue;
 168 
 169                 /* a regular interface by default */
 170                 ifp->ifi_class = IPADM_IF_CLASS_REGULAR;
 171 
 172                 if (lifrl.lifr_flags & IFF_BROADCAST)
 173                         ifp->ifi_cflags |= IFIF_BROADCAST;
 174                 if (lifrl.lifr_flags & IFF_MULTICAST)
 175                         ifp->ifi_cflags |= IFIF_MULTICAST;
 176                 if (lifrl.lifr_flags & IFF_POINTOPOINT)
 177                         ifp->ifi_cflags |= IFIF_POINTOPOINT;
 178                 if (lifrl.lifr_flags & IFF_VIRTUAL) {
 179                         ifp->ifi_cflags |= IFIF_VIRTUAL;
 180                         ifp->ifi_class = IPADM_IF_CLASS_VIRTUAL;
 181                 }
 182                 if (lifrl.lifr_flags & IFF_IPMP) {
 183                         ifp->ifi_cflags |= IFIF_IPMP;
 184                         ifp->ifi_class = IPADM_IF_CLASS_IPMP;
 185                 }
 186                 if (lifrl.lifr_flags & IFF_STANDBY)
 187                         ifp->ifi_cflags |= IFIF_STANDBY;
 188                 if (lifrl.lifr_flags & IFF_INACTIVE)
 189                         ifp->ifi_cflags |= IFIF_INACTIVE;
 190                 if (lifrl.lifr_flags & IFF_VRRP)
 191                         ifp->ifi_cflags |= IFIF_VRRP;
 192                 if (lifrl.lifr_flags & IFF_NOACCEPT)
 193                         ifp->ifi_cflags |= IFIF_NOACCEPT;
 194                 if (lifrl.lifr_flags & IFF_IPV4)
 195                         ifp->ifi_cflags |= IFIF_IPV4;
 196                 if (lifrl.lifr_flags & IFF_IPV6)
 197                         ifp->ifi_cflags |= IFIF_IPV6;
 198                 if (lifrl.lifr_flags & IFF_L3PROTECT)
 199                         ifp->ifi_cflags |= IFIF_L3PROTECT;
 200 
 201         /* Retrieve active IPMP members */
 202         if (ifp->ifi_class == IPADM_IF_CLASS_IPMP) {
 203                 if (ioctl(s, SIOCGLIFGROUPNAME,
 204                     (caddr_t)&lifrl) < 0) {
 205                         status = ipadm_errno2status(errno);
 206                         break;
 207                 }
 208 
 209                 if ((status = i_ipadm_fill_cmembers(
 210                     lifrl.lifr_groupname,
 211                     &ifp->ifi_ipmp_cmembers)) != IPADM_SUCCESS)
 212                         break;
 213                 }
 214         }
 215         free(buf);
 216         if (status != IPADM_SUCCESS) {
 217                 ipadm_free_if_info(*if_info);
 218                 *if_info = NULL;
 219         }
 220         return (status);
 221 }
 222 
 223 /*
 224  * Returns the interface information for `ifname' in `if_info' from persistent
 225  * config if `ifname' is non-null. Otherwise, it returns all the interfaces
 226  * from persistent config in `if_info'.
 227  */
 228 static ipadm_status_t
 229 i_ipadm_persist_if_info(ipadm_handle_t iph, const char *ifname,
 230     ipadm_if_info_t **if_info)
 231 {
 232         ipadm_status_t  status = IPADM_SUCCESS;
 233         nvlist_t        *ifs_info_nvl;
 234 
 235         *if_info = NULL;
 236 
 237         if ((status = i_ipadm_get_db_if(iph,
 238             ifname, &ifs_info_nvl)) != IPADM_SUCCESS)
 239                 return (status);
 240 
 241         assert(ifs_info_nvl != NULL);
 242 
 243         return (i_ipadm_nvl2ifinfo(ifs_info_nvl, if_info));
 244 }
 245 
 246 static ipadm_status_t
 247 i_ipadm_nvl2ifinfo(nvlist_t *ifs_info_nvl, ipadm_if_info_t **if_info)
 248 {
 249         ipadm_if_info_t *ific = NULL, *ifil = NULL;
 250         nvlist_t        *if_info_nvl;
 251         nvpair_t        *nvp;
 252         char            *strval;
 253         ipadm_status_t  status = IPADM_SUCCESS;
 254         uint16_t        *families;
 255         uint_t          nelem = 0;
 256 
 257         for (nvp = nvlist_next_nvpair(ifs_info_nvl, NULL); nvp != NULL;
 258             nvp = nvlist_next_nvpair(ifs_info_nvl, nvp)) {
 259                 if (nvpair_value_nvlist(nvp, &if_info_nvl) != 0)
 260                         continue;
 261 
 262                 status = i_ipadm_allocate_ifinfo(&ific);
 263                 if (status != IPADM_SUCCESS) {
 264                         ipadm_free_if_info(*if_info);
 265                         break;
 266                 }
 267                 if (nvlist_lookup_string(if_info_nvl, IPADM_NVP_IFNAME,
 268                     &strval) != 0) {
 269                         ipadm_free_if_info(ific);
 270                         ific = NULL;
 271                         continue;
 272                 }
 273                 (void) strlcpy(ific->ifi_name, strval,
 274                     sizeof (ific->ifi_name));
 275 
 276                 if (nvlist_lookup_uint16_array(if_info_nvl,
 277                     IPADM_NVP_FAMILIES, &families, &nelem) == 0) {
 278                         while (nelem-- > 0) {
 279                                 if (families[nelem] == AF_INET)
 280                                         ific->ifi_pflags |= IFIF_IPV4;
 281                                 else if (families[nelem] == AF_INET6)
 282                                         ific->ifi_pflags |= IFIF_IPV6;
 283                         }
 284                 }
 285 
 286                 if (nvlist_lookup_string(if_info_nvl,
 287                     IPADM_NVP_IFCLASS, &strval) == 0)
 288                         ific->ifi_class = atoi(strval);
 289                 else
 290                         ific->ifi_class = IPADM_IF_CLASS_REGULAR;
 291 
 292                 if (ific->ifi_class == IPADM_IF_CLASS_IPMP)
 293                         /* do not expect any failures there */
 294                         (void) i_ipadm_fill_pmembers(if_info_nvl,
 295                             &ific->ifi_ipmp_pmembers);
 296 
 297                 if (*if_info == NULL)
 298                         *if_info = ific;
 299                 else
 300                         ifil->ifi_next = ific;
 301                 ifil = ific;
 302         }
 303 
 304         nvlist_free(ifs_info_nvl);
 305         return (status);
 306 }
 307 
 308 /*
 309  * Fill the ipadm_if_info_t->ifi_ipmp_pmembers by info from
 310  * ipadm DB
 311  */
 312 static ipadm_status_t
 313 i_ipadm_fill_pmembers(nvlist_t *if_info_nvl, ipadm_ipmp_members_t *pmembers)
 314 {
 315         uint_t  nelem = 0;
 316         char    **members;
 317         ipadm_ipmp_member_t *ipmp_member;
 318 
 319         if (nvlist_lookup_string_array(if_info_nvl, IPADM_NVP_MIFNAMES,
 320             &members, &nelem) != 0)
 321                 return (IPADM_SUCCESS);
 322 
 323         while (nelem-- > 0) {
 324                 if ((ipmp_member = calloc(1,
 325                     sizeof (ipadm_ipmp_member_t))) == NULL)
 326                         return (ipadm_errno2status(errno));
 327 
 328                 (void) strlcpy(ipmp_member->if_name, members[nelem],
 329                     sizeof (ipmp_member->if_name));
 330                 list_insert_tail(pmembers, ipmp_member);
 331         }
 332         return (IPADM_SUCCESS);
 333 }
 334 
 335 /*
 336  * Fill the ipadm_if_info_t->ifi_ipmp_cmembers by info from
 337  * kernel (libipmp is used to retrieve the required info)
 338  */
 339 static ipadm_status_t
 340 i_ipadm_fill_cmembers(char *grname, ipadm_ipmp_members_t *cmembers)
 341 {
 342         ipmp_handle_t ipmp_handle;
 343         ipmp_groupinfo_t *grinfo;
 344         ipmp_iflist_t *iflistp;
 345         ipadm_ipmp_member_t *ipmp_member;
 346         ipadm_status_t ipadm_status = IPADM_SUCCESS;
 347         int i;
 348 
 349         if (ipmp_open(&ipmp_handle) != IPMP_SUCCESS)
 350                 return (IPADM_FAILURE);
 351 
 352         if (ipmp_getgroupinfo(ipmp_handle, grname, &grinfo) != IPMP_SUCCESS) {
 353                 ipadm_status = IPADM_FAILURE;
 354                 goto fail2;
 355         }
 356 
 357         iflistp = grinfo->gr_iflistp;
 358         for (i = 0; i < iflistp->il_nif; i++) {
 359                 if ((ipmp_member = calloc(1,
 360                     sizeof (ipadm_ipmp_member_t))) == NULL) {
 361                         ipadm_status = ipadm_errno2status(errno);
 362                         goto fail1;
 363                 }
 364                 (void) strlcpy(ipmp_member->if_name, iflistp->il_ifs[i],
 365                     sizeof (ipmp_member->if_name));
 366                 list_insert_tail(cmembers, ipmp_member);
 367         }
 368 
 369 fail1:
 370         ipmp_freegroupinfo(grinfo);
 371 fail2:
 372         ipmp_close(ipmp_handle);
 373         return (ipadm_status);
 374 }
 375 
 376 /*
 377  * Collects information for `ifname' if one is specified from both
 378  * active and persistent config in `if_info'. If no `ifname' is specified,
 379  * this returns all the interfaces in active and persistent config in
 380  * `if_info'.
 381  */
 382 ipadm_status_t
 383 i_ipadm_get_all_if_info(ipadm_handle_t iph, const char *ifname,
 384     ipadm_if_info_t **if_info, int64_t lifc_flags)
 385 {
 386         ipadm_status_t  status;
 387         ipadm_if_info_t *aifinfo = NULL;
 388         ipadm_if_info_t *pifinfo = NULL;
 389         ipadm_if_info_t *aifp;
 390         ipadm_if_info_t *pifp;
 391         ipadm_if_info_t *last = NULL;
 392         struct ifaddrs  *ifa;
 393         struct ifaddrs  *ifap;
 394 
 395         /*
 396          * Retrive the information for the requested `ifname' or all
 397          * interfaces from active configuration.
 398          */
 399 retry:
 400         status = i_ipadm_active_if_info(iph, ifname, &aifinfo, lifc_flags);
 401         if (status != IPADM_SUCCESS)
 402                 return (status);
 403         /* Get the interface state for each interface in `aifinfo'. */
 404         if (aifinfo != NULL) {
 405                 /* We need all addresses to get the interface state */
 406                 if (getallifaddrs(AF_UNSPEC, &ifa, (LIFC_NOXMIT|LIFC_TEMPORARY|
 407                     LIFC_ALLZONES|LIFC_UNDER_IPMP)) != 0) {
 408                         status = ipadm_errno2status(errno);
 409                         goto fail;
 410                 }
 411                 for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
 412                         /*
 413                          * Find the `ifaddrs' structure from `ifa'
 414                          * for this interface. We need the IFF_* flags
 415                          * to find the interface state.
 416                          */
 417                         for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
 418                                 if (strcmp(ifap->ifa_name, aifp->ifi_name) == 0)
 419                                         break;
 420                         }
 421                         if (ifap == NULL) {
 422                                 /*
 423                                  * The interface might have been removed
 424                                  * from kernel. Retry getting all the active
 425                                  * interfaces.
 426                                  */
 427                                 freeifaddrs(ifa);
 428                                 ipadm_free_if_info(aifinfo);
 429                                 aifinfo = NULL;
 430                                 goto retry;
 431                         }
 432                         if (!(ifap->ifa_flags & IFF_RUNNING) ||
 433                             (ifap->ifa_flags & IFF_FAILED))
 434                                 aifp->ifi_state = IFIS_FAILED;
 435                         else if (ifap->ifa_flags & IFF_OFFLINE)
 436                                 aifp->ifi_state = IFIS_OFFLINE;
 437                         else if (i_ipadm_is_if_down(aifp->ifi_name, ifa))
 438                                 aifp->ifi_state = IFIS_DOWN;
 439                         else
 440                                 aifp->ifi_state = IFIS_OK;
 441                         if (aifp->ifi_next == NULL)
 442                                 last = aifp;
 443                 }
 444                 freeifaddrs(ifa);
 445         }
 446         /*
 447          * Get the persistent interface information in `pifinfo'.
 448          */
 449         status = i_ipadm_persist_if_info(iph, ifname, &pifinfo);
 450         if (status == IPADM_NOTFOUND) {
 451                 *if_info = aifinfo;
 452                 return (IPADM_SUCCESS);
 453         }
 454         if (status != IPADM_SUCCESS)
 455                 goto fail;
 456         /*
 457          * If a persistent interface is also found in `aifinfo', update
 458          * its entry in `aifinfo' with the persistent information from
 459          * `pifinfo'. If an interface is found in `pifinfo', but not in
 460          * `aifinfo', it means that this interface was disabled. We should
 461          * add this interface to `aifinfo' and set it state to IFIF_DISABLED.
 462          */
 463         for (pifp = pifinfo; pifp != NULL; pifp = pifp->ifi_next) {
 464                 for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
 465                         if (strcmp(aifp->ifi_name, pifp->ifi_name) == 0) {
 466                                 break;
 467                         }
 468                 }
 469 
 470                 if (aifp == NULL) {
 471                         if ((status =
 472                             i_ipadm_allocate_ifinfo(&aifp)) != IPADM_SUCCESS)
 473                                 goto fail;
 474 
 475                         (void) strlcpy(aifp->ifi_name, pifp->ifi_name,
 476                             sizeof (aifp->ifi_name));
 477 
 478                         aifp->ifi_next = NULL;
 479                         aifp->ifi_state = IFIS_DISABLED;
 480                         if (last != NULL)
 481                                 last->ifi_next = aifp;
 482                         else
 483                                 aifinfo = aifp;
 484                         last = aifp;
 485                 }
 486 
 487                 if ((status = i_ipadm_add_persistent_if_info(aifp,
 488                     pifp)) != IPADM_SUCCESS)
 489                         goto fail;
 490         }
 491         *if_info = aifinfo;
 492         ipadm_free_if_info(pifinfo);
 493         return (IPADM_SUCCESS);
 494 fail:
 495         *if_info = NULL;
 496         ipadm_free_if_info(aifinfo);
 497         ipadm_free_if_info(pifinfo);
 498         return (status);
 499 }
 500 
 501 /*
 502  * Updates active if_info by data from persistent if_info
 503  */
 504 static ipadm_status_t
 505 i_ipadm_add_persistent_if_info(ipadm_if_info_t *aifp, ipadm_if_info_t *pifp)
 506 {
 507         ipadm_ipmp_member_t *pp_ipmp_member, *ap_ipmp_member;
 508 
 509         ipadm_ipmp_members_t *apmembers = &aifp->ifi_ipmp_pmembers;
 510         ipadm_ipmp_members_t *ppmembers = &pifp->ifi_ipmp_pmembers;
 511 
 512         aifp->ifi_pflags = pifp->ifi_pflags;
 513         aifp->ifi_class = pifp->ifi_class;
 514 
 515         for (pp_ipmp_member = list_head(ppmembers); pp_ipmp_member;
 516             pp_ipmp_member = list_next(ppmembers, pp_ipmp_member)) {
 517                 if ((ap_ipmp_member = calloc(1,
 518                     sizeof (ipadm_ipmp_member_t))) == NULL)
 519                         return (ipadm_errno2status(errno));
 520 
 521                 (void) strlcpy(ap_ipmp_member->if_name,
 522                     pp_ipmp_member->if_name,
 523                     sizeof (ap_ipmp_member->if_name));
 524 
 525                 list_insert_tail(apmembers, ap_ipmp_member);
 526         }
 527         return (IPADM_SUCCESS);
 528 }
 529 
 530 static ipadm_status_t
 531 i_ipadm_allocate_ifinfo(ipadm_if_info_t **if_info)
 532 {
 533         *if_info = calloc(1, sizeof (ipadm_if_info_t));
 534         if (*if_info == NULL)
 535                 return (ipadm_errno2status(errno));
 536 
 537         /* List of active (current) members */
 538         list_create(&((*if_info)->ifi_ipmp_cmembers),
 539             sizeof (ipadm_ipmp_member_t),
 540             offsetof(ipadm_ipmp_member_t, node));
 541 
 542         /* List of persistent members */
 543         list_create(&((*if_info)->ifi_ipmp_pmembers),
 544             sizeof (ipadm_ipmp_member_t),
 545             offsetof(ipadm_ipmp_member_t, node));
 546 
 547         return (IPADM_SUCCESS);
 548 }
 549 
 550 /*
 551  * Reads all the interface lines from the persistent DB into the nvlist `onvl',
 552  * when `ifname' is NULL.
 553  * If an `ifname' is specified, then the interface line corresponding to
 554  * that name will be returned.
 555  */
 556 static ipadm_status_t
 557 i_ipadm_get_db_if(ipadm_handle_t iph, const char *ifname, nvlist_t **onvl)
 558 {
 559         ipmgmt_getif_arg_t      garg;
 560 
 561         /* Populate the door_call argument structure */
 562         bzero(&garg, sizeof (garg));
 563         garg.ia_cmd = IPMGMT_CMD_GETIF;
 564         if (ifname != NULL)
 565                 (void) strlcpy(garg.ia_ifname, ifname, sizeof (garg.ia_ifname));
 566 
 567         return (i_ipadm_call_ipmgmtd(iph, (void *) &garg, sizeof (garg), onvl));
 568 }
 569 
 570 int
 571 i_ipadm_get_lnum(const char *ifname)
 572 {
 573         char *num = strrchr(ifname, IPADM_LOGICAL_SEP);
 574 
 575         if (num == NULL)
 576                 return (0);
 577 
 578         return (atoi(++num));
 579 }
 580 
 581 /*
 582  * Sets the output argument `exists' to true or false based on whether
 583  * any persistent configuration is available for `ifname' and returns
 584  * IPADM_SUCCESS as status. If the persistent information cannot be retrieved,
 585  * `exists' is unmodified and an error status is returned.
 586  */
 587 ipadm_status_t
 588 i_ipadm_if_pexists(ipadm_handle_t iph, const char *ifname, sa_family_t af,
 589     boolean_t *exists)
 590 {
 591         ipadm_if_info_t *ifinfo;
 592         ipadm_status_t  status;
 593 
 594         /*
 595          * if IPH_IPMGMTD is set, we know that the caller (ipmgmtd) already
 596          * knows about persistent configuration in the first place, so we
 597          * just return success.
 598          */
 599         if (iph->iph_flags & IPH_IPMGMTD) {
 600                 *exists = B_FALSE;
 601                 return (IPADM_SUCCESS);
 602         }
 603         status = i_ipadm_persist_if_info(iph, ifname, &ifinfo);
 604         if (status == IPADM_SUCCESS) {
 605                 *exists = ((af == AF_INET &&
 606                     (ifinfo->ifi_pflags & IFIF_IPV4)) ||
 607                     (af == AF_INET6 &&
 608                     (ifinfo->ifi_pflags & IFIF_IPV6)));
 609                 ipadm_free_if_info(ifinfo);
 610         } else if (status == IPADM_NOTFOUND) {
 611                 status = IPADM_SUCCESS;
 612                 *exists = B_FALSE;
 613         }
 614         return (status);
 615 }
 616 
 617 /*
 618  * Open "/dev/udp{,6}" for use as a multiplexor to PLINK the interface stream
 619  * under. We use "/dev/udp" instead of "/dev/ip" since STREAMS will not let
 620  * you PLINK a driver under itself, and "/dev/ip" is typically the driver at
 621  * the bottom of the stream for tunneling interfaces.
 622  */
 623 ipadm_status_t
 624 ipadm_open_arp_on_udp(const char *udp_dev_name, int *fd)
 625 {
 626         int err;
 627 
 628         if ((*fd = open(udp_dev_name, O_RDWR)) == -1)
 629                 return (ipadm_errno2status(errno));
 630 
 631         /*
 632          * Pop off all undesired modules (note that the user may have
 633          * configured autopush to add modules above udp), and push the
 634          * arp module onto the resulting stream. This is used to make
 635          * IP+ARP be able to atomically track the muxid for the I_PLINKed
 636          * STREAMS, thus it isn't related to ARP running the ARP protocol.
 637          */
 638         while (ioctl(*fd, I_POP, 0) != -1)
 639                 ;
 640         if (errno == EINVAL && ioctl(*fd, I_PUSH, ARP_MOD_NAME) != -1)
 641                 return (IPADM_SUCCESS);
 642         err = errno;
 643         (void) close(*fd);
 644 
 645         return (ipadm_errno2status(err));
 646 }
 647 
 648 /*
 649  * i_ipadm_create_ipmp() is called from i_ipadm_create_ipmp_peer() when an
 650  * underlying interface in an ipmp group G is plumbed for an address family,
 651  * but the meta-interface for the other address family `af' does not exist
 652  * yet for the group G. If `af' is IPv6, we need to bring up the
 653  * link-local address.
 654  */
 655 static ipadm_status_t
 656 i_ipadm_create_ipmp(ipadm_handle_t iph, char *ifname, sa_family_t af,
 657     const char *grname, uint32_t ipadm_flags)
 658 {
 659         ipadm_status_t  status;
 660         struct lifreq   lifr;
 661         int             sock;
 662         int             err;
 663 
 664         assert(ipadm_flags & IPADM_OPT_IPMP);
 665 
 666         /* Create the ipmp underlying interface */
 667         status = i_ipadm_create_if(iph, ifname, af, ipadm_flags);
 668         if (status != IPADM_SUCCESS && status != IPADM_IF_EXISTS)
 669                 return (status);
 670 
 671         /*
 672          * To preserve backward-compatibility, always bring up the link-local
 673          * address for implicitly-created IPv6 IPMP interfaces.
 674          */
 675         if (af == AF_INET6)
 676                 (void) i_ipadm_set_flags(iph, ifname, AF_INET6, IFF_UP, 0);
 677 
 678         sock = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
 679         /*
 680          * If the caller requested a different group name, issue a
 681          * SIOCSLIFGROUPNAME on the new IPMP interface.
 682          */
 683         bzero(&lifr, sizeof (lifr));
 684         (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
 685         if (strcmp(lifr.lifr_name, grname) != 0) {
 686                 (void) strlcpy(lifr.lifr_groupname, grname, LIFGRNAMSIZ);
 687                 if (ioctl(sock, SIOCSLIFGROUPNAME, &lifr) == -1) {
 688                         err = errno;
 689                         /* Remove the interface we created. */
 690                         if (status == IPADM_SUCCESS) {
 691                                 (void) i_ipadm_delete_if(iph, ifname, af,
 692                                     ipadm_flags);
 693                         }
 694                         return (ipadm_errno2status(err));
 695                 }
 696         }
 697 
 698         return (IPADM_SUCCESS);
 699 }
 700 
 701 /*
 702  * Checks if `ifname' is plumbed and in an IPMP group on its "other" address
 703  * family.  If so, create a matching IPMP group for address family `af'.
 704  */
 705 static ipadm_status_t
 706 i_ipadm_create_ipmp_peer(ipadm_handle_t iph, char *ifname, sa_family_t af)
 707 {
 708         lifgroupinfo_t  lifgr;
 709         ipadm_status_t  status = IPADM_SUCCESS;
 710         struct lifreq   lifr;
 711         int             other_af_sock;
 712 
 713         assert(af == AF_INET || af == AF_INET6);
 714 
 715         other_af_sock = (af == AF_INET ? iph->iph_sock6 : iph->iph_sock);
 716 
 717         /*
 718          * iph is the handle for the interface that we are trying to plumb.
 719          * other_af_sock is the socket for the "other" address family.
 720          */
 721         bzero(&lifr, sizeof (lifr));
 722         (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
 723         if (ioctl(other_af_sock, SIOCGLIFGROUPNAME, &lifr) != 0)
 724                 return (IPADM_SUCCESS);
 725 
 726         (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname, LIFGRNAMSIZ);
 727         if (ioctl(other_af_sock, SIOCGLIFGROUPINFO, &lifgr) != 0)
 728                 return (IPADM_SUCCESS);
 729 
 730         /*
 731          * If `ifname' *is* the IPMP group interface, or if the relevant
 732          * address family is already configured, then there's nothing to do.
 733          */
 734         if (strcmp(lifgr.gi_grifname, ifname) == 0 ||
 735             (af == AF_INET && lifgr.gi_v4) || (af == AF_INET6 && lifgr.gi_v6)) {
 736                 return (IPADM_SUCCESS);
 737         }
 738 
 739         status = i_ipadm_create_ipmp(iph, lifgr.gi_grifname, af,
 740             lifgr.gi_grname, IPADM_OPT_ACTIVE|IPADM_OPT_IPMP);
 741         return (status);
 742 }
 743 
 744 /*
 745  * Issues the ioctl SIOCSLIFNAME to kernel on the given ARP stream fd.
 746  */
 747 static ipadm_status_t
 748 i_ipadm_slifname_arp(char *ifname, uint64_t flags, int fd)
 749 {
 750         struct lifreq   lifr;
 751         ifspec_t        ifsp;
 752 
 753         bzero(&lifr, sizeof (lifr));
 754         (void) ifparse_ifspec(ifname, &ifsp);
 755         lifr.lifr_ppa = ifsp.ifsp_ppa;
 756         lifr.lifr_flags = flags;
 757         (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
 758         /*
 759          * Tell ARP the name and unit number for this interface.
 760          * Note that arp has no support for transparent ioctls.
 761          */
 762         if (i_ipadm_strioctl(fd, SIOCSLIFNAME, (char *)&lifr,
 763             sizeof (lifr)) == -1) {
 764                 return (ipadm_errno2status(errno));
 765         }
 766         return (IPADM_SUCCESS);
 767 }
 768 
 769 /*
 770  * Issues the ioctl SIOCSLIFNAME to kernel. If IPADM_OPT_GENPPA is set in
 771  * `ipadm_flags', then a ppa will be generated. `newif' will be updated
 772  * with the generated ppa.
 773  */
 774 static ipadm_status_t
 775 i_ipadm_slifname(ipadm_handle_t iph, char *ifname, char *newif, uint64_t flags,
 776     int fd, uint32_t ipadm_flags)
 777 {
 778         struct lifreq   lifr;
 779         ipadm_status_t  status = IPADM_SUCCESS;
 780         int             err = 0;
 781         sa_family_t     af;
 782         int             ppa;
 783         ifspec_t        ifsp;
 784         boolean_t       valid_if;
 785 
 786         bzero(&lifr, sizeof (lifr));
 787         if (ipadm_flags & IPADM_OPT_GENPPA) {
 788                 /*
 789                  * We'd like to just set lifr_ppa to UINT_MAX and have the
 790                  * kernel pick a PPA.  Unfortunately, that would mishandle
 791                  * two cases:
 792                  *
 793                  *      1. If the PPA is available but the groupname is taken
 794                  *         (e.g., the "ipmp2" IP interface name is available
 795                  *         but the "ipmp2" groupname is taken) then the
 796                  *         auto-assignment by the kernel will fail.
 797                  *
 798                  *      2. If we're creating (e.g.) an IPv6-only IPMP
 799                  *         interface, and there's already an IPv4-only IPMP
 800                  *         interface, the kernel will allow us to accidentally
 801                  *         reuse the IPv6 IPMP interface name (since
 802                  *         SIOCSLIFNAME uniqueness is per-interface-type).
 803                  *         This will cause administrative confusion.
 804                  *
 805                  * Thus, we instead take a brute-force approach of checking
 806                  * whether the IPv4 or IPv6 name is already in-use before
 807                  * attempting the SIOCSLIFNAME.  As per (1) above, the
 808                  * SIOCSLIFNAME may still fail, in which case we just proceed
 809                  * to the next one.  If this approach becomes too slow, we
 810                  * can add a new SIOC* to handle this case in the kernel.
 811                  */
 812                 for (ppa = 0; ppa < UINT_MAX; ppa++) {
 813                         (void) snprintf(lifr.lifr_name, LIFNAMSIZ, "%s%d",
 814                             ifname, ppa);
 815 
 816                         if (ioctl(iph->iph_sock, SIOCGLIFFLAGS, &lifr) != -1 ||
 817                             errno != ENXIO)
 818                                 continue;
 819 
 820                         if (ioctl(iph->iph_sock6, SIOCGLIFFLAGS, &lifr) != -1 ||
 821                             errno != ENXIO)
 822                                 continue;
 823 
 824                         lifr.lifr_ppa = ppa;
 825                         lifr.lifr_flags = flags;
 826 
 827                         err = ioctl(fd, SIOCSLIFNAME, &lifr);
 828                         if (err != -1 || errno != EEXIST)
 829                                 break;
 830                 }
 831                 if (err == -1) {
 832                         status = ipadm_errno2status(errno);
 833                 } else {
 834                         /*
 835                          * PPA has been successfully established.
 836                          * Update `newif' with the ppa.
 837                          */
 838                         assert(newif != NULL);
 839                         if (snprintf(newif, LIFNAMSIZ, "%s%d", ifname,
 840                             ppa) >= LIFNAMSIZ)
 841                                 return (IPADM_INVALID_ARG);
 842                 }
 843         } else {
 844                 /* We should have already validated the interface name. */
 845                 valid_if = ifparse_ifspec(ifname, &ifsp);
 846                 assert(valid_if);
 847 
 848                 /*
 849                  * Before we call SIOCSLIFNAME, ensure that the IPMP group
 850                  * interface for this address family exists.  Otherwise, the
 851                  * kernel will kick the interface out of the group when we do
 852                  * the SIOCSLIFNAME.
 853                  *
 854                  * Example: suppose bge0 is plumbed for IPv4 and in group "a".
 855                  * If we're now plumbing bge0 for IPv6, but the IPMP group
 856                  * interface for "a" is not plumbed for IPv6, the SIOCSLIFNAME
 857                  * will kick bge0 out of group "a", which is undesired.
 858                  */
 859                 if (flags & IFF_IPV4)
 860                         af = AF_INET;
 861                 else
 862                         af = AF_INET6;
 863                 status = i_ipadm_create_ipmp_peer(iph, ifname, af);
 864                 if (status != IPADM_SUCCESS)
 865                         return (status);
 866                 lifr.lifr_ppa = ifsp.ifsp_ppa;
 867                 lifr.lifr_flags = flags;
 868                 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
 869                 if (ioctl(fd, SIOCSLIFNAME, &lifr) == -1)
 870                         status = ipadm_errno2status(errno);
 871         }
 872 
 873         return (status);
 874 }
 875 
 876 /*
 877  * Plumbs the interface `ifname' for the address family `af'. It also persists
 878  * the interface for `af' if IPADM_OPT_PERSIST is set in `ipadm_flags'.
 879  */
 880 ipadm_status_t
 881 i_ipadm_plumb_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
 882     uint32_t ipadm_flags)
 883 {
 884         int             ip_muxid;
 885         int             mux_fd = -1, ip_fd, arp_fd;
 886         char            *udp_dev_name;
 887         dlpi_handle_t   dh_arp = NULL, dh_ip;
 888         uint64_t        ifflags;
 889         struct lifreq   lifr;
 890         uint_t          dlpi_flags;
 891         ipadm_status_t  status = IPADM_SUCCESS;
 892         char            *linkname;
 893         boolean_t       legacy = (iph->iph_flags & IPH_LEGACY);
 894         zoneid_t        zoneid;
 895         char            newif[LIFNAMSIZ];
 896         char            lifname[LIFNAMSIZ];
 897         datalink_id_t   linkid;
 898         int             sock;
 899         boolean_t       islo;
 900         boolean_t       is_persistent =
 901             ((ipadm_flags & IPADM_OPT_PERSIST) != 0);
 902         uint32_t        dlflags;
 903         dladm_status_t  dlstatus;
 904 
 905         if (iph->iph_dlh != NULL) {
 906                 dlstatus = dladm_name2info(iph->iph_dlh, ifname, &linkid,
 907                     &dlflags, NULL, NULL);
 908         }
 909         /*
 910          * If we're in the global zone and we're plumbing a datalink, make
 911          * sure that the datalink is not assigned to a non-global zone.  Note
 912          * that the non-global zones don't need this check, because zoneadm
 913          * has taken care of this when the zones boot.
 914          */
 915         if (iph->iph_zoneid == GLOBAL_ZONEID && dlstatus == DLADM_STATUS_OK) {
 916                 zoneid = ALL_ZONES;
 917                 if (zone_check_datalink(&zoneid, linkid) == 0) {
 918                         /* interface is in use by a non-global zone. */
 919                         return (IPADM_IF_INUSE);
 920                 }
 921         }
 922 
 923         /* loopback interfaces are just added as logical interface */
 924         bzero(&lifr, sizeof (lifr));
 925         islo = i_ipadm_is_loopback(ifname);
 926         if (islo || i_ipadm_get_lnum(ifname) != 0) {
 927                 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
 928                 if (af == AF_INET)
 929                         sock = iph->iph_sock;
 930                 else
 931                         sock = iph->iph_sock6;
 932                 if (islo && ioctl(sock, SIOCGLIFADDR, (caddr_t)&lifr) >= 0)
 933                         return (IPADM_IF_EXISTS);
 934                 if (ioctl(sock, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
 935                         return (ipadm_errno2status(errno));
 936 
 937                 /*
 938                  * By default, kernel configures 127.0.0.1 on the loopback
 939                  * interface. Replace this with 0.0.0.0 to be consistent
 940                  * with interface creation on other physical interfaces.
 941                  */
 942                 if (islo && !legacy) {
 943                         bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
 944                         lifr.lifr_addr.ss_family = af;
 945                         if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
 946                                 return (ipadm_errno2status(errno));
 947                         if (is_persistent) {
 948                                 status = i_ipadm_persist_if(iph,
 949                                     ifname, af, ipadm_flags);
 950                                 if (status != IPADM_SUCCESS) {
 951                                         (void) i_ipadm_delete_if(iph, ifname,
 952                                             af, IPADM_OPT_ACTIVE);
 953                                 }
 954                         }
 955                 }
 956                 return (status);
 957         }
 958 
 959         dlpi_flags = DLPI_NOATTACH;
 960 
 961         /*
 962          * If IPADM_OPT_IPMP is specified, then this is a request
 963          * to create an IPMP interface atop /dev/ipmpstub0.  (We can't simply
 964          * pass "ipmpstub0" as devname since an admin *could* have a normal
 965          * vanity-named link named "ipmpstub0" that they'd like to plumb.)
 966          */
 967         if (ipadm_flags & IPADM_OPT_IPMP) {
 968                 dlpi_flags |= DLPI_DEVONLY;
 969                 linkname = "ipmpstub0";
 970         } else {
 971                 /*
 972                  * Verify that the user is not creating a persistent
 973                  * IP interface on a non-persistent data-link.
 974                  */
 975                 if (!i_ipadm_is_vni(ifname) && dlstatus == DLADM_STATUS_OK &&
 976                     is_persistent && !(dlflags & DLADM_OPT_PERSIST)) {
 977                                 return (IPADM_TEMPORARY_OBJ);
 978                 }
 979                 linkname = ifname;
 980         }
 981 
 982         /*
 983          * We use DLPI_NOATTACH because the ip module will do the attach
 984          * itself for DLPI style-2 devices.
 985          */
 986         if (dlpi_open(linkname, &dh_ip, dlpi_flags) != DLPI_SUCCESS)
 987                 return (IPADM_DLPI_FAILURE);
 988         ip_fd = dlpi_fd(dh_ip);
 989         if (ioctl(ip_fd, I_PUSH, IP_MOD_NAME) == -1) {
 990                 status = ipadm_errno2status(errno);
 991                 goto done;
 992         }
 993 
 994         /*
 995          * Set IFF_IPV4/IFF_IPV6 flags. The kernel only allows modifications
 996          * to IFF_IPv4, IFF_IPV6, IFF_BROADCAST, IFF_XRESOLV, IFF_NOLINKLOCAL.
 997          */
 998         ifflags = 0;
 999 
1000         /* Set the name string and the IFF_IPV* flag */
1001         if (af == AF_INET) {
1002                 ifflags = IFF_IPV4;
1003         } else {
1004                 ifflags = IFF_IPV6;
1005                 /*
1006                  * With the legacy method, the link-local address should be
1007                  * configured as part of the interface plumb, using the default
1008                  * token. If IPH_LEGACY is not specified, we want to set :: as
1009                  * the address and require the admin to explicitly call
1010                  * ipadm_create_addr() with the address object type set to
1011                  * IPADM_ADDR_IPV6_ADDRCONF to create the link-local address
1012                  * as well as the autoconfigured addresses.
1013                  */
1014                 if (!legacy && !i_ipadm_is_6to4(iph, ifname))
1015                         ifflags |= IFF_NOLINKLOCAL;
1016         }
1017         (void) strlcpy(newif, ifname, sizeof (newif));
1018         status = i_ipadm_slifname(iph, ifname, newif, ifflags, ip_fd,
1019             ipadm_flags);
1020         if (status != IPADM_SUCCESS)
1021                 goto done;
1022 
1023         /* Get the full set of existing flags for this stream */
1024         status = i_ipadm_get_flags(iph, newif, af, &ifflags);
1025         if (status != IPADM_SUCCESS)
1026                 goto done;
1027 
1028         udp_dev_name = (af == AF_INET6 ? UDP6_DEV_NAME : UDP_DEV_NAME);
1029         status = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd);
1030         if (status != IPADM_SUCCESS)
1031                 goto done;
1032 
1033         /* Check if arp is not needed */
1034         if (ifflags & (IFF_NOARP|IFF_IPV6)) {
1035                 /*
1036                  * PLINK the interface stream so that the application can exit
1037                  * without tearing down the stream.
1038                  */
1039                 if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1)
1040                         status = ipadm_errno2status(errno);
1041                 goto done;
1042         }
1043 
1044         /*
1045          * This interface does use ARP, so set up a separate stream
1046          * from the interface to ARP.
1047          *
1048          * We use DLPI_NOATTACH because the arp module will do the attach
1049          * itself for DLPI style-2 devices.
1050          */
1051         if (dlpi_open(linkname, &dh_arp, dlpi_flags) != DLPI_SUCCESS) {
1052                 status = IPADM_DLPI_FAILURE;
1053                 goto done;
1054         }
1055 
1056         arp_fd = dlpi_fd(dh_arp);
1057         if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1) {
1058                 status = ipadm_errno2status(errno);
1059                 goto done;
1060         }
1061 
1062         status = i_ipadm_slifname_arp(newif, ifflags, arp_fd);
1063         if (status != IPADM_SUCCESS)
1064                 goto done;
1065         /*
1066          * PLINK the IP and ARP streams so that ifconfig can exit
1067          * without tearing down the stream.
1068          */
1069         if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) {
1070                 status = ipadm_errno2status(errno);
1071                 goto done;
1072         }
1073 
1074         if (ioctl(mux_fd, I_PLINK, arp_fd) < 0) {
1075                 status = ipadm_errno2status(errno);
1076                 (void) ioctl(mux_fd, I_PUNLINK, ip_muxid);
1077         }
1078 
1079 done:
1080         dlpi_close(dh_ip);
1081         if (dh_arp != NULL)
1082                 dlpi_close(dh_arp);
1083 
1084         if (mux_fd != -1)
1085                 (void) close(mux_fd);
1086 
1087         if (status == IPADM_SUCCESS) {
1088                 /* copy back new ifname */
1089                 (void) strlcpy(ifname, newif, LIFNAMSIZ);
1090                 /*
1091                  * If it is a 6to4 tunnel, create a default
1092                  * addrobj name for the default address on the 0'th
1093                  * logical interface and set IFF_UP in the interface flags.
1094                  */
1095                 if (i_ipadm_is_6to4(iph, ifname)) {
1096                         struct ipadm_addrobj_s addr;
1097 
1098                         i_ipadm_init_addr(&addr, ifname, "", IPADM_ADDR_STATIC);
1099                         addr.ipadm_af = af;
1100                         status = i_ipadm_lookupadd_addrobj(iph, &addr);
1101                         if (status != IPADM_SUCCESS)
1102                                 return (status);
1103                         status = ipadm_add_aobjname(iph, ifname,
1104                             af, addr.ipadm_aobjname, IPADM_ADDR_STATIC, 0);
1105                         if (status != IPADM_SUCCESS)
1106                                 return (status);
1107                         addr.ipadm_lifnum = 0;
1108                         i_ipadm_addrobj2lifname(&addr, lifname,
1109                             sizeof (lifname));
1110                         status = i_ipadm_set_flags(iph, lifname, af,
1111                             IFF_UP, 0);
1112                         if (status != IPADM_SUCCESS)
1113                                 return (status);
1114                 } else {
1115                         /*
1116                          * Prevent static IPv6 addresses from triggering
1117                          * autoconf. This does not have to be done for
1118                          * 6to4 tunnel interfaces, since in.ndpd will
1119                          * not autoconfigure those interfaces.
1120                          */
1121                         if (af == AF_INET6 && !legacy)
1122                                 (void) i_ipadm_disable_autoconf(newif);
1123                 }
1124 
1125                 /*
1126                  * If IPADM_OPT_PERSIST was set in flags, store the
1127                  * interface in persistent DB.
1128                  */
1129                 if (is_persistent) {
1130                         status = i_ipadm_persist_if(iph,
1131                             newif, af, ipadm_flags);
1132                         if (status != IPADM_SUCCESS) {
1133                                 (void) i_ipadm_delete_if(iph, newif, af,
1134                                     IPADM_OPT_ACTIVE);
1135                         }
1136                 }
1137         }
1138         if (status == IPADM_EXISTS)
1139                 status = IPADM_IF_EXISTS;
1140         return (status);
1141 }
1142 
1143 /*
1144  * Unplumbs the interface in `ifname' of family `af'.
1145  */
1146 ipadm_status_t
1147 i_ipadm_unplumb_if(ipadm_handle_t iph, const char *ifname, sa_family_t af)
1148 {
1149         int             ip_muxid, arp_muxid;
1150         int             mux_fd = -1;
1151         int             muxid_fd = -1;
1152         char            *udp_dev_name;
1153         uint64_t        flags;
1154         boolean_t       changed_arp_muxid = B_FALSE;
1155         int             save_errno;
1156         struct lifreq   lifr;
1157         ipadm_status_t  ret = IPADM_SUCCESS;
1158         int             sock;
1159         lifgroupinfo_t  lifgr;
1160         ifaddrlistx_t   *ifaddrs, *ifaddrp;
1161         boolean_t       v6 = (af == AF_INET6);
1162 
1163         /* Just do SIOCLIFREMOVEIF on loopback interfaces */
1164         bzero(&lifr, sizeof (lifr));
1165         if (i_ipadm_is_loopback(ifname) ||
1166             (i_ipadm_get_lnum(ifname) != 0 && (iph->iph_flags & IPH_LEGACY))) {
1167                 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
1168                 if (ioctl((af == AF_INET) ? iph->iph_sock : iph->iph_sock6,
1169                     SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) {
1170                         return (ipadm_errno2status(errno));
1171                 }
1172                 return (IPADM_SUCCESS);
1173         }
1174 
1175         /*
1176          * We used /dev/udp or udp6 to set up the mux. So we have to use
1177          * the same now for PUNLINK also.
1178          */
1179         if (v6) {
1180                 udp_dev_name = UDP6_DEV_NAME;
1181                 sock = iph->iph_sock6;
1182         } else {
1183                 udp_dev_name = UDP_DEV_NAME;
1184                 sock = iph->iph_sock;
1185         }
1186         if ((muxid_fd = open(udp_dev_name, O_RDWR)) == -1) {
1187                 ret = ipadm_errno2status(errno);
1188                 goto done;
1189         }
1190         ret = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd);
1191         if (ret != IPADM_SUCCESS)
1192                 goto done;
1193         (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
1194         if (ioctl(muxid_fd, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
1195                 ret = ipadm_errno2status(errno);
1196                 goto done;
1197         }
1198         flags = lifr.lifr_flags;
1199 again:
1200         if (flags & IFF_IPMP) {
1201                 /*
1202                  * There are two reasons the I_PUNLINK can fail with EBUSY:
1203                  * (1) if IP interfaces are in the group, or (2) if IPMP data
1204                  * addresses are administratively up.  For case (1), we fail
1205                  * here with a specific error message.  For case (2), we bring
1206                  * down the addresses prior to doing the I_PUNLINK.  If the
1207                  * I_PUNLINK still fails with EBUSY then the configuration
1208                  * must have changed after our checks, in which case we branch
1209                  * back up to `again' and rerun this logic.  The net effect is
1210                  * that unplumbing an IPMP interface will only fail with EBUSY
1211                  * if IP interfaces are in the group.
1212                  */
1213                 if (ioctl(sock, SIOCGLIFGROUPNAME, &lifr) == -1) {
1214                         ret = ipadm_errno2status(errno);
1215                         goto done;
1216                 }
1217                 (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname,
1218                     LIFGRNAMSIZ);
1219                 if (ioctl(sock, SIOCGLIFGROUPINFO, &lifgr) == -1) {
1220                         ret = ipadm_errno2status(errno);
1221                         goto done;
1222                 }
1223                 if ((v6 && lifgr.gi_nv6 != 0) || (!v6 && lifgr.gi_nv4 != 0)) {
1224                         ret = IPADM_GRP_NOTEMPTY;
1225                         goto done;
1226                 }
1227 
1228                 /*
1229                  * The kernel will fail the I_PUNLINK if the IPMP interface
1230                  * has administratively up addresses; bring them down.
1231                  */
1232                 if (ifaddrlistx(ifname, IFF_UP|IFF_DUPLICATE,
1233                     0, &ifaddrs) == -1) {
1234                         ret = ipadm_errno2status(errno);
1235                         goto done;
1236                 }
1237                 ifaddrp = ifaddrs;
1238                 for (; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
1239                         int sock = (ifaddrp->ia_flags & IFF_IPV4) ?
1240                             iph->iph_sock : iph->iph_sock6;
1241                         struct lifreq lifrl;
1242 
1243                         if (((ifaddrp->ia_flags & IFF_IPV6) && !v6) ||
1244                             (!(ifaddrp->ia_flags & IFF_IPV6) && v6))
1245                                 continue;
1246 
1247                         bzero(&lifrl, sizeof (lifrl));
1248                         (void) strlcpy(lifrl.lifr_name, ifaddrp->ia_name,
1249                             sizeof (lifrl.lifr_name));
1250                         if (ioctl(sock, SIOCGLIFFLAGS, &lifrl) < 0) {
1251                                 ret = ipadm_errno2status(errno);
1252                                 ifaddrlistx_free(ifaddrs);
1253                                 goto done;
1254                         }
1255                         if (lifrl.lifr_flags & IFF_UP) {
1256                                 ret = i_ipadm_set_flags(iph, lifrl.lifr_name,
1257                                     ((lifrl.lifr_flags & IFF_IPV4) ? AF_INET :
1258                                     AF_INET6), 0, IFF_UP);
1259                                 if (ret != IPADM_SUCCESS) {
1260                                         ifaddrlistx_free(ifaddrs);
1261                                         goto done;
1262                                 }
1263                         } else if (lifrl.lifr_flags & IFF_DUPLICATE) {
1264                                 if (ioctl(sock, SIOCGLIFADDR, &lifrl) < 0 ||
1265                                     ioctl(sock, SIOCSLIFADDR, &lifrl) < 0) {
1266                                         ret = ipadm_errno2status(errno);
1267                                         ifaddrlistx_free(ifaddrs);
1268                                         goto done;
1269                                 }
1270                         }
1271                 }
1272                 ifaddrlistx_free(ifaddrs);
1273         }
1274 
1275         if (ioctl(muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
1276                 ret = ipadm_errno2status(errno);
1277                 goto done;
1278         }
1279         arp_muxid = lifr.lifr_arp_muxid;
1280         ip_muxid = lifr.lifr_ip_muxid;
1281 
1282         /*
1283          * We don't have a good way of knowing whether the arp stream is
1284          * plumbed. We can't rely on IFF_NOARP because someone could
1285          * have turned it off later using "ifconfig xxx -arp".
1286          */
1287         if (arp_muxid != 0) {
1288                 if (ioctl(mux_fd, I_PUNLINK, arp_muxid) < 0) {
1289                         /*
1290                          * See the comment before the SIOCGLIFGROUPNAME call.
1291                          */
1292                         if (errno == EBUSY && (flags & IFF_IPMP))
1293                                 goto again;
1294 
1295                         if ((errno == EINVAL) &&
1296                             (flags & (IFF_NOARP | IFF_IPV6))) {
1297                                 /*
1298                                  * Some plumbing utilities set the muxid to
1299                                  * -1 or some invalid value to signify that
1300                                  * there is no arp stream. Set the muxid to 0
1301                                  * before trying to unplumb the IP stream.
1302                                  * IP does not allow the IP stream to be
1303                                  * unplumbed if it sees a non-null arp muxid,
1304                                  * for consistency of IP-ARP streams.
1305                                  */
1306                                 lifr.lifr_arp_muxid = 0;
1307                                 (void) ioctl(muxid_fd, SIOCSLIFMUXID,
1308                                     (caddr_t)&lifr);
1309                                 changed_arp_muxid = B_TRUE;
1310                         }
1311                         /*
1312                          * In case of any other error, we continue with
1313                          * the unplumb.
1314                          */
1315                 }
1316         }
1317 
1318         if (ioctl(mux_fd, I_PUNLINK, ip_muxid) < 0) {
1319                 if (changed_arp_muxid) {
1320                         /*
1321                          * Some error occurred, and we need to restore
1322                          * everything back to what it was.
1323                          */
1324                         save_errno = errno;
1325                         lifr.lifr_arp_muxid = arp_muxid;
1326                         lifr.lifr_ip_muxid = ip_muxid;
1327                         (void) ioctl(muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
1328                         errno = save_errno;
1329                 }
1330                 /*
1331                  * See the comment before the SIOCGLIFGROUPNAME call.
1332                  */
1333                 if (errno == EBUSY && (flags & IFF_IPMP))
1334                         goto again;
1335 
1336                 ret = ipadm_errno2status(errno);
1337         }
1338 done:
1339         if (muxid_fd != -1)
1340                 (void) close(muxid_fd);
1341         if (mux_fd != -1)
1342                 (void) close(mux_fd);
1343 
1344         if (af == AF_INET6 && ret == IPADM_SUCCESS) {
1345                 /*
1346                  * in.ndpd maintains the phyints in its memory even after
1347                  * the interface is plumbed, so that it can be reused when
1348                  * the interface gets plumbed again. The default behavior
1349                  * of in.ndpd is to start autoconfiguration for an interface
1350                  * that gets plumbed. We need to send the
1351                  * message IPADM_ENABLE_AUTOCONF to in.ndpd to restore this
1352                  * default behavior on replumb.
1353                  */
1354                 (void) i_ipadm_enable_autoconf(ifname);
1355         }
1356         return (ret);
1357 }
1358 
1359 /*
1360  * Saves the given interface name `ifname' with address family `af' in
1361  * persistent DB.
1362  */
1363 static ipadm_status_t
1364 i_ipadm_persist_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1365     uint32_t ipadm_flags)
1366 {
1367         ipmgmt_if_arg_t         ifarg;
1368         int                     err;
1369 
1370         (void) strlcpy(ifarg.ia_ifname, ifname, sizeof (ifarg.ia_ifname));
1371         ifarg.ia_family = af;
1372         if (ipadm_flags & IPADM_OPT_IPMP)
1373                 ifarg.ia_ifclass = IPADM_IF_CLASS_IPMP;
1374         else
1375                 ifarg.ia_ifclass = IPADM_IF_CLASS_REGULAR;
1376 
1377         ifarg.ia_cmd = IPMGMT_CMD_SETIF;
1378         ifarg.ia_flags = IPMGMT_PERSIST;
1379         err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
1380         return (ipadm_errno2status(err));
1381 }
1382 
1383 /*
1384  * Remove the IP interface from active configuration. If IPADM_OPT_PERSIST
1385  * is set in `ipadm_flags', it is also removed from persistent configuration.
1386  */
1387 ipadm_status_t
1388 i_ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1389     uint32_t ipadm_flags)
1390 {
1391         ipadm_status_t          ret = IPADM_SUCCESS;
1392         ipadm_status_t          db_status;
1393         char                    tmp_ifname[LIFNAMSIZ];
1394         char                    *cp;
1395         struct ipadm_addrobj_s  ipaddr;
1396         boolean_t               is_persistent =
1397             (ipadm_flags & IPADM_OPT_PERSIST);
1398 
1399         ret = i_ipadm_unplumb_if(iph, ifname, af);
1400         if (ret != IPADM_SUCCESS)
1401                 goto done;
1402 
1403         cp = strrchr(ifname, IPADM_LOGICAL_SEP);
1404         if (cp != NULL) {
1405                 assert(iph->iph_flags & IPH_LEGACY);
1406                 /*
1407                  * This is a non-zero logical interface.
1408                  * Find the addrobj and remove it from the daemon's memory.
1409                  */
1410                 (void) strlcpy(tmp_ifname, ifname, sizeof (tmp_ifname));
1411                 tmp_ifname[cp - ifname] = '\0';
1412                 *cp++ = '\0';
1413                 ipaddr.ipadm_lifnum = atoi(cp);
1414                 (void) strlcpy(ipaddr.ipadm_ifname, tmp_ifname,
1415                     sizeof (ipaddr.ipadm_ifname));
1416                 ipaddr.ipadm_af = af;
1417                 ret = i_ipadm_get_lif2addrobj(iph, &ipaddr);
1418                 if (ret == IPADM_SUCCESS) {
1419                         ret = i_ipadm_delete_addrobj(iph, &ipaddr,
1420                             IPADM_OPT_ACTIVE);
1421                 } else if (ret == IPADM_NOTFOUND) {
1422                         ret = IPADM_SUCCESS;
1423                 }
1424                 return (ret);
1425         }
1426 done:
1427         /*
1428          * Even if interface does not exist, remove all its addresses and
1429          * properties from the persistent store. If interface does not
1430          * exist both in kernel and the persistent store, return IPADM_ENXIO.
1431          */
1432         if ((ret == IPADM_ENXIO && is_persistent) || ret == IPADM_SUCCESS) {
1433                 db_status = i_ipadm_delete_ifobj(iph, ifname, af,
1434                     is_persistent);
1435                 if (db_status == IPADM_SUCCESS)
1436                         ret = IPADM_SUCCESS;
1437         }
1438 
1439         return (ret);
1440 }
1441 
1442 /*
1443  * Resets all addresses on interface `ifname' with address family `af'
1444  * from ipmgmtd daemon. If is_persistent = B_TRUE, all interface properties
1445  * and address objects of `ifname' for `af' are also removed from the
1446  * persistent DB.
1447  */
1448 ipadm_status_t
1449 i_ipadm_delete_ifobj(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1450     boolean_t is_persistent)
1451 {
1452         ipmgmt_if_arg_t         ifarg;
1453         int                     err;
1454 
1455         ifarg.ia_cmd = IPMGMT_CMD_RESETIF;
1456         ifarg.ia_flags = IPMGMT_ACTIVE;
1457         if (is_persistent)
1458                 ifarg.ia_flags |= IPMGMT_PERSIST;
1459         ifarg.ia_family = af;
1460         (void) strlcpy(ifarg.ia_ifname, ifname, LIFNAMSIZ);
1461 
1462         err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
1463         return (ipadm_errno2status(err));
1464 }
1465 
1466 /*
1467  * Create the interface by plumbing it for IP.
1468  * This function will check if there is saved configuration information
1469  * for `ifname' and return IPADM_OP_DISABLE_OBJ if the name-space
1470  * for `ifname' is taken.
1471  */
1472 ipadm_status_t
1473 i_ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
1474     uint32_t ipadm_flags)
1475 {
1476         ipadm_status_t  status;
1477         boolean_t       p_exists;
1478         sa_family_t     other_af;
1479 
1480         /*
1481          * Return error, if the interface already exists in either the active
1482          * or the persistent configuration.
1483          */
1484         if (ipadm_if_enabled(iph, ifname, af))
1485                 return (IPADM_IF_EXISTS);
1486 
1487         if (!(iph->iph_flags & IPH_LEGACY)) {
1488                 status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
1489                 if (status != IPADM_SUCCESS)
1490                         return (status);
1491                 other_af = (af == AF_INET ? AF_INET6 : AF_INET);
1492                 if (p_exists) {
1493                         if (!ipadm_if_enabled(iph, ifname, other_af))
1494                                 return (IPADM_OP_DISABLE_OBJ);
1495                         else
1496                                 ipadm_flags &= ~IPADM_OPT_PERSIST;
1497                 }
1498         }
1499 
1500         return (i_ipadm_plumb_if(iph, ifname, af, ipadm_flags));
1501 }
1502 
1503 /*
1504  * Plumbs an interface. Creates both IPv4 and IPv6 interfaces by
1505  * default, unless a value in `af' is specified. The interface may be plumbed
1506  * only if there is no previously saved persistent configuration information
1507  * for the interface (in which case the ipadm_enable_if() function must
1508  * be used to enable the interface).
1509  *
1510  * Returns: IPADM_SUCCESS, IPADM_FAILURE, IPADM_IF_EXISTS,
1511  * IPADM_IF_PERSIST_EXISTS, IPADM_DLPI_FAILURE,
1512  * or appropriate ipadm_status_t corresponding to the errno.
1513  *
1514  * `ifname' must point to memory that can hold upto LIFNAMSIZ chars. It may
1515  * be over-written with the actual interface name when a PPA has to be
1516  * internally generated by the library.
1517  */
1518 ipadm_status_t
1519 ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
1520     uint32_t flags)
1521 {
1522         ipadm_status_t  status;
1523         boolean_t       created_v4 = B_FALSE;
1524         char            newifname[LIFNAMSIZ];
1525 
1526         /* Check for the required authorization */
1527         if (!ipadm_check_auth())
1528                 return (IPADM_EAUTH);
1529 
1530         if (flags == 0 || ((flags & IPADM_OPT_PERSIST) &&
1531             !(flags & IPADM_OPT_ACTIVE)) ||
1532             (flags & ~(IPADM_COMMON_OPT_MASK | IPADM_OPT_IPMP |
1533             IPADM_OPT_GENPPA))) {
1534                 return (IPADM_INVALID_ARG);
1535         }
1536         if (flags & IPADM_OPT_GENPPA) {
1537                 if (snprintf(newifname, LIFNAMSIZ, "%s0", ifname) >=
1538                     LIFNAMSIZ)
1539                         return (IPADM_INVALID_ARG);
1540         } else {
1541                 if (strlcpy(newifname, ifname, LIFNAMSIZ) >= LIFNAMSIZ)
1542                         return (IPADM_INVALID_ARG);
1543         }
1544 
1545         if (!i_ipadm_validate_ifname(iph, newifname))
1546                 return (IPADM_INVALID_ARG);
1547 
1548         if ((af == AF_INET || af == AF_UNSPEC) &&
1549             !i_ipadm_is_6to4(iph, ifname)) {
1550                 status = i_ipadm_create_if(iph, ifname, AF_INET, flags);
1551                 if (status != IPADM_SUCCESS)
1552                         return (status);
1553                 created_v4 = B_TRUE;
1554         }
1555         if (af == AF_INET6 || af == AF_UNSPEC) {
1556                 status = i_ipadm_create_if(iph, ifname, AF_INET6, flags);
1557                 if (status != IPADM_SUCCESS) {
1558                         if (created_v4) {
1559                                 (void) i_ipadm_delete_if(iph, ifname, AF_INET,
1560                                     IPADM_OPT_ACTIVE);
1561                         }
1562                         return (status);
1563                 }
1564         }
1565 
1566         return (IPADM_SUCCESS);
1567 }
1568 
1569 ipadm_status_t
1570 ipadm_add_ipmp_member(ipadm_handle_t iph, const char *gifname,
1571     const char *mifname, uint32_t ipadm_flags)
1572 {
1573         return (i_ipadm_update_ipmp(iph, gifname, mifname,
1574             ipadm_flags, IPADM_ADD_IPMP));
1575 }
1576 
1577 ipadm_status_t
1578 ipadm_remove_ipmp_member(ipadm_handle_t iph, const char *gifname,
1579     const char *mifname, uint32_t ipadm_flags)
1580 {
1581         return (i_ipadm_update_ipmp(iph, gifname, mifname,
1582             ipadm_flags, IPADM_REMOVE_IPMP));
1583 }
1584 
1585 /*
1586  * Updates active IPMP configuration according to the specified
1587  * command. It also persists the configuration if IPADM_OPT_PERSIST
1588  * is set in `ipadm_flags'.
1589  */
1590 static ipadm_status_t
1591 i_ipadm_update_ipmp(ipadm_handle_t iph, const char *gifname,
1592     const char *mifname, uint32_t ipadm_flags, ipadm_ipmp_op_t op)
1593 {
1594         ipadm_status_t status;
1595         char    groupname1[LIFGRNAMSIZ];
1596         char    groupname2[LIFGRNAMSIZ];
1597 
1598         /* Check for the required authorization */
1599         if (!ipadm_check_auth())
1600                 return (IPADM_EAUTH);
1601 
1602         if (!(ipadm_flags & IPADM_OPT_ACTIVE) ||
1603             gifname == NULL || mifname == NULL)
1604                 return (IPADM_INVALID_ARG);
1605 
1606         if (!ipadm_if_enabled(iph, gifname, AF_UNSPEC) ||
1607             !ipadm_if_enabled(iph, mifname, AF_UNSPEC))
1608                 return (IPADM_OP_DISABLE_OBJ);
1609 
1610         if (!i_ipadm_is_ipmp(iph, gifname))
1611                 return (IPADM_INVALID_ARG);
1612 
1613         if (op == IPADM_ADD_IPMP && i_ipadm_is_under_ipmp(iph, mifname))
1614                 return (IPADM_IF_INUSE);
1615 
1616         if ((status = i_ipadm_get_groupname_active(iph, gifname,
1617             groupname2, sizeof (groupname2))) != IPADM_SUCCESS)
1618                 return (status);
1619 
1620         if (op == IPADM_REMOVE_IPMP) {
1621                 if ((status = i_ipadm_get_groupname_active(iph, mifname,
1622                     groupname1, sizeof (groupname1))) != IPADM_SUCCESS)
1623                         return (status);
1624 
1625                 if (groupname1[0] == '\0' ||
1626                     strcmp(groupname1, groupname2) != 0)
1627                         return (IPADM_INVALID_ARG);
1628 
1629                 groupname2[0] = '\0';
1630         }
1631 
1632         if ((ipadm_flags & IPADM_OPT_PERSIST) &&
1633             (status = i_ipadm_persist_update_ipmp(iph, gifname,
1634             mifname, op)) != IPADM_SUCCESS)
1635                 return (status);
1636 
1637         return (i_ipadm_set_groupname_active(iph, mifname, groupname2));
1638 }
1639 
1640 /*
1641  * Call the ipmgmtd to update the IPMP configuration in ipadm DB.
1642  * After this call the DB will know that mifname is under gifname and
1643  * gifname has a member, which name is mifname.
1644  */
1645 static ipadm_status_t
1646 i_ipadm_persist_update_ipmp(ipadm_handle_t iph, const char *gifname,
1647     const char *mifname, ipadm_ipmp_op_t op)
1648 {
1649         ipmgmt_ipmp_update_arg_t args;
1650         int err;
1651 
1652         assert(op == IPADM_ADD_IPMP || op == IPADM_REMOVE_IPMP);
1653 
1654         bzero(&args, sizeof (ipmgmt_ipmp_update_arg_t));
1655 
1656         args.ia_cmd = IPMGMT_CMD_IPMP_UPDATE;
1657 
1658         (void) strlcpy(args.ia_gifname, gifname, sizeof (args.ia_gifname));
1659         (void) strlcpy(args.ia_mifname, mifname, sizeof (args.ia_mifname));
1660 
1661         if (op == IPADM_ADD_IPMP)
1662                 args.ia_flags = IPMGMT_APPEND;
1663         else
1664                 args.ia_flags = IPMGMT_REMOVE;
1665 
1666         args.ia_flags |= IPMGMT_PERSIST;
1667 
1668         err = ipadm_door_call(iph, &args, sizeof (args), NULL, 0, B_FALSE);
1669         return (ipadm_errno2status(err));
1670 }
1671 
1672 /*
1673  * Deletes the interface in `ifname'. Removes both IPv4 and IPv6 interfaces
1674  * when `af' = AF_UNSPEC.
1675  */
1676 ipadm_status_t
1677 ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1678     uint32_t flags)
1679 {
1680         ipadm_status_t status1 = IPADM_SUCCESS;
1681         ipadm_status_t status2 = IPADM_SUCCESS;
1682         ipadm_status_t other;
1683 
1684         /* Check for the required authorization */
1685         if (!ipadm_check_auth())
1686                 return (IPADM_EAUTH);
1687 
1688         /* Validate the `ifname' for any logical interface. */
1689         if (flags == 0 || (flags & ~(IPADM_COMMON_OPT_MASK)) ||
1690             !i_ipadm_validate_ifname(iph, ifname))
1691                 return (IPADM_INVALID_ARG);
1692 
1693         if (af == AF_INET || af == AF_UNSPEC)
1694                 status1 = i_ipadm_delete_if(iph, ifname, AF_INET, flags);
1695         if (af == AF_INET6 || af == AF_UNSPEC)
1696                 status2 = i_ipadm_delete_if(iph, ifname, AF_INET6, flags);
1697         /*
1698          * If the family has been uniquely identified, we return the
1699          * associated status, even if that is ENXIO. Calls from ifconfig
1700          * which can only unplumb one of IPv4/IPv6 at any time fall under
1701          * this category.
1702          */
1703         if (af == AF_INET)
1704                 return (status1);
1705         else if (af == AF_INET6)
1706                 return (status2);
1707         else if (af != AF_UNSPEC)
1708                 return (IPADM_INVALID_ARG);
1709 
1710         /*
1711          * If af is AF_UNSPEC, then we return the following:
1712          * status1,             if status1 == status2
1713          * IPADM_SUCCESS,       if either of status1 or status2 is SUCCESS
1714          *                      and the other status is ENXIO
1715          * IPADM_ENXIO,         if both status1 and status2 are ENXIO
1716          * IPADM_FAILURE        otherwise.
1717          */
1718         if (status1 == status2) {
1719                 /* covers the case when both status1 and status2 are ENXIO */
1720                 return (status1);
1721         } else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
1722                 if (status1 == IPADM_SUCCESS)
1723                         other = status2;
1724                 else
1725                         other = status1;
1726                 return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
1727         } else {
1728                 return (IPADM_FAILURE);
1729         }
1730 }
1731 
1732 /*
1733  * Returns information about all interfaces in both active and persistent
1734  * configuration. If `ifname' is not NULL, it returns only the interface
1735  * identified by `ifname'.
1736  *
1737  * Return values:
1738  *      On success: IPADM_SUCCESS.
1739  *      On error  : IPADM_INVALID_ARG, IPADM_ENXIO or IPADM_FAILURE.
1740  */
1741 ipadm_status_t
1742 ipadm_if_info(ipadm_handle_t iph, const char *ifname,
1743     ipadm_if_info_t **if_info, uint32_t flags, int64_t lifc_flags)
1744 {
1745         ipadm_status_t  status;
1746         ifspec_t        ifsp;
1747 
1748         if (if_info == NULL || iph == NULL || flags != 0)
1749                 return (IPADM_INVALID_ARG);
1750 
1751         if (ifname != NULL &&
1752             (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)) {
1753                 return (IPADM_INVALID_ARG);
1754         }
1755 
1756         status = i_ipadm_get_all_if_info(iph, ifname, if_info, lifc_flags);
1757         if (status != IPADM_SUCCESS)
1758                 return (status);
1759         if (ifname != NULL && *if_info == NULL)
1760                 return (IPADM_ENXIO);
1761 
1762         return (IPADM_SUCCESS);
1763 }
1764 
1765 /*
1766  * Frees the linked list allocated by ipadm_if_info().
1767  */
1768 void
1769 ipadm_free_if_info(ipadm_if_info_t *ifinfo)
1770 {
1771         ipadm_if_info_t *ifinfo_next;
1772 
1773         for (; ifinfo != NULL; ifinfo = ifinfo_next) {
1774                 ifinfo_next = ifinfo->ifi_next;
1775                 i_ipadm_free_ipmp_members(&ifinfo->ifi_ipmp_cmembers);
1776                 i_ipadm_free_ipmp_members(&ifinfo->ifi_ipmp_pmembers);
1777                 free(ifinfo);
1778         }
1779 }
1780 
1781 static void
1782 i_ipadm_free_ipmp_members(ipadm_ipmp_members_t *ipmp_members)
1783 {
1784         ipadm_ipmp_member_t *ipmp_member;
1785 
1786         while ((ipmp_member = list_remove_head(ipmp_members)) != NULL)
1787                 free(ipmp_member);
1788 
1789         list_destroy(ipmp_members);
1790 }
1791 
1792 /*
1793  * Re-enable the interface `ifname' based on the saved configuration
1794  * for `ifname'.
1795  */
1796 ipadm_status_t
1797 ipadm_enable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
1798 {
1799         nvlist_t        *ifnvl;
1800         ipadm_status_t  status;
1801         ifspec_t        ifsp;
1802 
1803         /* Check for the required authorization */
1804         if (!ipadm_check_auth())
1805                 return (IPADM_EAUTH);
1806 
1807         /* Check for logical interfaces. */
1808         if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
1809                 return (IPADM_INVALID_ARG);
1810 
1811         /* Enabling an interface persistently is not supported. */
1812         if (flags & IPADM_OPT_PERSIST)
1813                 return (IPADM_NOTSUP);
1814 
1815         /*
1816          * Return early by checking if the interface is already enabled.
1817          */
1818         if (ipadm_if_enabled(iph, ifname, AF_INET) &&
1819             ipadm_if_enabled(iph, ifname, AF_INET6))
1820                 return (IPADM_IF_EXISTS);
1821 
1822         /*
1823          * Enable the interface and restore all its interface properties
1824          * and address objects.
1825          */
1826         status = i_ipadm_init_ifs(iph, ifname, &ifnvl);
1827         if (status != IPADM_SUCCESS)
1828                 return (status);
1829 
1830         assert(ifnvl != NULL);
1831         /*
1832          * ipadm_enable_if() does exactly what ipadm_init_ifs() does,
1833          * but only for one interface. We need to set IPH_INIT because
1834          * ipmgmtd daemon does not have to write the interface to persistent
1835          * db. The interface is already available in persistent db
1836          * and we are here to re-enable the persistent configuration.
1837          */
1838         iph->iph_flags |= IPH_INIT;
1839         status = i_ipadm_init_ifobj(iph, ifname, ifnvl);
1840         iph->iph_flags &= ~IPH_INIT;
1841 
1842         nvlist_free(ifnvl);
1843         return (status);
1844 }
1845 
1846 /*
1847  * Disable the interface `ifname' by removing it from the active configuration.
1848  * Error code return values follow the model in ipadm_delete_if()
1849  */
1850 ipadm_status_t
1851 ipadm_disable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
1852 {
1853         ipadm_status_t  status1, status2, other;
1854         ifspec_t        ifsp;
1855 
1856         /* Check for the required authorization */
1857         if (!ipadm_check_auth())
1858                 return (IPADM_EAUTH);
1859 
1860         /* Check for logical interfaces. */
1861         if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
1862                 return (IPADM_INVALID_ARG);
1863 
1864         /* Disabling an interface persistently is not supported. */
1865         if (flags & IPADM_OPT_PERSIST)
1866                 return (IPADM_NOTSUP);
1867 
1868         status1 = i_ipadm_unplumb_if(iph, ifname, AF_INET6);
1869         if (status1 == IPADM_SUCCESS)
1870                 status1 = i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
1871         status2 = i_ipadm_unplumb_if(iph, ifname, AF_INET);
1872         if (status2 == IPADM_SUCCESS)
1873                 status2 = i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
1874         if (status1 == status2) {
1875                 return (status2);
1876         } else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
1877                 if (status1 == IPADM_SUCCESS)
1878                         other = status2;
1879                 else
1880                         other = status1;
1881                 return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
1882         } else {
1883                 return (IPADM_FAILURE);
1884         }
1885 }
1886 
1887 /*
1888  * FIXME Remove this when ifconfig(1M) is updated to use IPMP support
1889  * in libipadm.
1890  */
1891 /*
1892  * This workaround is required by ifconfig(1M) whenever an
1893  * interface is moved into an IPMP group to update the daemon's
1894  * in-memory mapping of `aobjname' to 'lifnum'.
1895  *
1896  * For `IPMGMT_ACTIVE' case, i_ipadm_delete_ifobj() would only fail if
1897  * door_call(3C) fails. Also, there is no use in returning error because
1898  * `ifname' would have been successfuly moved into IPMP group, by this time.
1899  */
1900 void
1901 ipadm_if_move(ipadm_handle_t iph, const char *ifname)
1902 {
1903         (void) i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
1904         (void) i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
1905 }
1906 
1907 ipadm_status_t
1908 i_ipadm_set_groupname_active(ipadm_handle_t iph, const char *ifname,
1909     const char *groupname)
1910 {
1911         struct lifreq   lifr;
1912         ipadm_addr_info_t *addrinfo, *ia;
1913         ipadm_status_t  status = IPADM_SUCCESS;
1914 
1915         (void) memset(&lifr, 0, sizeof (lifr));
1916 
1917         (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
1918         (void) strlcpy(lifr.lifr_groupname, groupname,
1919             sizeof (lifr.lifr_groupname));
1920 
1921         /* Disable all addresses on the interface */
1922         (void) i_ipadm_active_addr_info(iph, ifname, &addrinfo,
1923             IPADM_OPT_ACTIVE, IFF_UP | IFF_DUPLICATE);
1924         for (ia = addrinfo; ia != NULL; ia = IA_NEXT(ia))
1925                 (void) ipadm_disable_addr(iph, ia->ia_aobjname, 0);
1926 
1927         if (ioctl(iph->iph_sock, SIOCSLIFGROUPNAME, (caddr_t)&lifr) == -1 &&
1928             ioctl(iph->iph_sock6, SIOCSLIFGROUPNAME, (caddr_t)&lifr) == -1)
1929                 status = ipadm_errno2status(errno);
1930 
1931         /* Enable all addresses on the interface */
1932         for (ia = addrinfo; ia != NULL; ia = IA_NEXT(ia))
1933                 (void) ipadm_enable_addr(iph, ia->ia_aobjname, 0);
1934 
1935         if (status == IPADM_SUCCESS) {
1936                 if (groupname[0] == '\0') {
1937                         /*
1938                          * If interface was removed from IPMP group, unset the
1939                          * DEPRECATED and NOFAILOVER flags.
1940                          */
1941                         (void) i_ipadm_set_flags(iph, ifname, AF_INET, 0,
1942                             IFF_DEPRECATED | IFF_NOFAILOVER);
1943                         (void) i_ipadm_set_flags(iph, ifname, AF_INET6, 0,
1944                             IFF_DEPRECATED | IFF_NOFAILOVER);
1945                 } else if (addrinfo == NULL) {
1946                         /*
1947                          * If interface was added to IPMP group and there are no
1948                          * active addresses, explicitly bring it up to be used
1949                          * for link-based IPMP configuration.
1950                          */
1951                         (void) i_ipadm_set_flags(iph, ifname, AF_INET,
1952                             IFF_UP, 0);
1953                         (void) i_ipadm_set_flags(iph, ifname, AF_INET6,
1954                             IFF_UP, 0);
1955                 }
1956         }
1957 
1958         ipadm_free_addr_info(addrinfo);
1959 
1960         return (status);
1961 }
1962 
1963 ipadm_status_t
1964 i_ipadm_get_groupname_active(ipadm_handle_t iph, const char *ifname,
1965     char *groupname, size_t size)
1966 {
1967         struct lifreq   lifr;
1968 
1969         (void) memset(&lifr, 0, sizeof (lifr));
1970 
1971         (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
1972 
1973         if (ioctl(iph->iph_sock, SIOCGLIFGROUPNAME, (caddr_t)&lifr) == -1 &&
1974             ioctl(iph->iph_sock6, SIOCGLIFGROUPNAME, (caddr_t)&lifr) == -1)
1975                 return (ipadm_errno2status(errno));
1976 
1977         (void) strlcpy(groupname, lifr.lifr_groupname, size);
1978 
1979         return (IPADM_SUCCESS);
1980 }
1981 
1982 /*
1983  * Returns B_TRUE if `ifname' represents an IPMP underlying interface.
1984  */
1985 boolean_t
1986 i_ipadm_is_under_ipmp(ipadm_handle_t iph, const char *ifname)
1987 {
1988 
1989         char    groupname[LIFGRNAMSIZ];
1990 
1991         if (i_ipadm_get_groupname_active(iph, ifname, groupname,
1992             sizeof (groupname)) != IPADM_SUCCESS ||
1993             groupname[0] == '\0' ||
1994             strcmp(ifname, groupname) == 0)
1995                 return (B_FALSE);
1996 
1997         return (B_TRUE);
1998 }
1999 
2000 /*
2001  * Returns B_TRUE if `ifname' represents an IPMP group interface.
2002  */
2003 boolean_t
2004 i_ipadm_is_ipmp(ipadm_handle_t iph, const char *ifname)
2005 {
2006         uint64_t flags, flags6;
2007 
2008         if (i_ipadm_get_flags(iph, ifname, AF_INET, &flags) != IPADM_SUCCESS &&
2009             i_ipadm_get_flags(iph, ifname, AF_INET6, &flags6) != IPADM_SUCCESS)
2010                 return (B_FALSE);
2011 
2012         return ((flags & IFF_IPMP) != 0 || (flags6 & IFF_IPMP) != 0);
2013 }