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