Print this page
NEX-9226 libipadm`ipadm_enable_if() leaks ifnvl
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
NEX-7823 ipmgmtd can't properly remove interface from the old ipadm.conf format
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Jean McCormack <jean.mccormack@nexenta.com>
NEX-6864 cannot create functional link-based IPMP interface
NEX-7793 unable to add/remove interface with existing address to/from IPMP group
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-3676 Assertion failed when creating address object on IPMP group
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-2460 libfksmbd should not link with libsmb (elfcheck)
NEX-2395: i_ipadm_nvl2ifinfo() shall ignore unknown entries in the per-interface NVL
NEX-2395: new libipadm/ipadm/ipmgmtd shall be backward compatible with old ipadm.conf format
OS-161: Integrate IPMP changes
        
*** 16,31 ****
--- 16,34 ----
   * fields enclosed by brackets "[]" replaced with your own identifying
   * information: Portions Copyright [yyyy] [name of copyright owner]
   *
   * CDDL HEADER END
   */
+ 
  /*
   * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+  * Copyright 2016 Nexenta Systems, Inc.
   */
  
  #include <errno.h>
  #include <sys/sockio.h>
+ #include <sys/list.h>
  #include <string.h>
  #include <assert.h>
  #include <unistd.h>
  #include <stropts.h>
  #include <strings.h>
*** 34,52 ****
  #include <libinetutil.h>
  #include <inet/ip.h>
  #include <limits.h>
  #include <zone.h>
  #include <ipadm_ndpd.h>
  #include "libipadm_impl.h"
  
  static ipadm_status_t   i_ipadm_slifname_arp(char *, uint64_t, int);
  static ipadm_status_t   i_ipadm_slifname(ipadm_handle_t, char *, char *,
                              uint64_t, int, uint32_t);
  static ipadm_status_t   i_ipadm_create_ipmp_peer(ipadm_handle_t, char *,
                              sa_family_t);
  static ipadm_status_t   i_ipadm_persist_if(ipadm_handle_t, const char *,
!                             sa_family_t);
  
  /*
   * Returns B_FALSE if the interface in `ifname' has at least one address that is
   * IFF_UP in the addresses in `ifa'.
   */
--- 37,71 ----
  #include <libinetutil.h>
  #include <inet/ip.h>
  #include <limits.h>
  #include <zone.h>
  #include <ipadm_ndpd.h>
+ #include <ipmp_query.h>
  #include "libipadm_impl.h"
  
  static ipadm_status_t   i_ipadm_slifname_arp(char *, uint64_t, int);
  static ipadm_status_t   i_ipadm_slifname(ipadm_handle_t, char *, char *,
                              uint64_t, int, uint32_t);
  static ipadm_status_t   i_ipadm_create_ipmp_peer(ipadm_handle_t, char *,
                              sa_family_t);
  static ipadm_status_t   i_ipadm_persist_if(ipadm_handle_t, const char *,
!                             sa_family_t, uint32_t);
! static ipadm_status_t   i_ipadm_allocate_ifinfo(ipadm_if_info_t **);
! static ipadm_status_t   i_ipadm_get_db_if(ipadm_handle_t, const char *,
!                             nvlist_t **);
! static ipadm_status_t i_ipadm_nvl2ifinfo(nvlist_t *, ipadm_if_info_t **);
! static ipadm_status_t i_ipadm_fill_cmembers(char *, ipadm_ipmp_members_t *);
! static ipadm_status_t i_ipadm_fill_pmembers(nvlist_t *, ipadm_ipmp_members_t *);
! static ipadm_status_t i_ipadm_add_persistent_if_info(ipadm_if_info_t *,
!                     ipadm_if_info_t *);
! static void i_ipadm_free_ipmp_members(ipadm_ipmp_members_t *);
! static ipadm_status_t i_ipadm_persist_update_ipmp(ipadm_handle_t, const char *,
!         const char *,
!         ipadm_ipmp_op_t);
! static ipadm_status_t i_ipadm_update_ipmp(ipadm_handle_t, const char *,
!         const char *, uint32_t,
!         ipadm_ipmp_op_t);
  
  /*
   * Returns B_FALSE if the interface in `ifname' has at least one address that is
   * IFF_UP in the addresses in `ifa'.
   */
*** 120,134 ****
                  for (ifp = *if_info; ifp != NULL; ifp = ifp->ifi_next) {
                          if (strcmp(lifrp->lifr_name, ifp->ifi_name) == 0)
                                  break;
                  }
                  if (ifp == NULL) {
!                         ifp = calloc(1, sizeof (ipadm_if_info_t));
!                         if (ifp == NULL) {
!                                 status = ipadm_errno2status(errno);
!                                 goto fail;
!                         }
                          (void) strlcpy(ifp->ifi_name, lifrp->lifr_name,
                              sizeof (ifp->ifi_name));
                          /* Update the `ifi_next' pointer for this new node */
                          if (*if_info == NULL)
                                  *if_info = ifp;
--- 139,152 ----
                  for (ifp = *if_info; ifp != NULL; ifp = ifp->ifi_next) {
                          if (strcmp(lifrp->lifr_name, ifp->ifi_name) == 0)
                                  break;
                  }
                  if (ifp == NULL) {
!                         if ((status =
!                             i_ipadm_allocate_ifinfo(&ifp)) != IPADM_SUCCESS)
!                                         break;
! 
                          (void) strlcpy(ifp->ifi_name, lifrp->lifr_name,
                              sizeof (ifp->ifi_name));
                          /* Update the `ifi_next' pointer for this new node */
                          if (*if_info == NULL)
                                  *if_info = ifp;
*** 145,164 ****
                      lifrp->lifr_name, sizeof (lifrl.lifr_name));
                  s = (lifrp->lifr_addr.ss_family == AF_INET) ?
                      iph->iph_sock : iph->iph_sock6;
                  if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
                          continue;
                  if (lifrl.lifr_flags & IFF_BROADCAST)
                          ifp->ifi_cflags |= IFIF_BROADCAST;
                  if (lifrl.lifr_flags & IFF_MULTICAST)
                          ifp->ifi_cflags |= IFIF_MULTICAST;
                  if (lifrl.lifr_flags & IFF_POINTOPOINT)
                          ifp->ifi_cflags |= IFIF_POINTOPOINT;
!                 if (lifrl.lifr_flags & IFF_VIRTUAL)
                          ifp->ifi_cflags |= IFIF_VIRTUAL;
!                 if (lifrl.lifr_flags & IFF_IPMP)
                          ifp->ifi_cflags |= IFIF_IPMP;
                  if (lifrl.lifr_flags & IFF_STANDBY)
                          ifp->ifi_cflags |= IFIF_STANDBY;
                  if (lifrl.lifr_flags & IFF_INACTIVE)
                          ifp->ifi_cflags |= IFIF_INACTIVE;
                  if (lifrl.lifr_flags & IFF_VRRP)
--- 163,190 ----
                      lifrp->lifr_name, sizeof (lifrl.lifr_name));
                  s = (lifrp->lifr_addr.ss_family == AF_INET) ?
                      iph->iph_sock : iph->iph_sock6;
                  if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
                          continue;
+ 
+                 /* a regular interface by default */
+                 ifp->ifi_class = IPADM_IF_CLASS_REGULAR;
+ 
                  if (lifrl.lifr_flags & IFF_BROADCAST)
                          ifp->ifi_cflags |= IFIF_BROADCAST;
                  if (lifrl.lifr_flags & IFF_MULTICAST)
                          ifp->ifi_cflags |= IFIF_MULTICAST;
                  if (lifrl.lifr_flags & IFF_POINTOPOINT)
                          ifp->ifi_cflags |= IFIF_POINTOPOINT;
!                 if (lifrl.lifr_flags & IFF_VIRTUAL) {
                          ifp->ifi_cflags |= IFIF_VIRTUAL;
!                         ifp->ifi_class = IPADM_IF_CLASS_VIRTUAL;
!                 }
!                 if (lifrl.lifr_flags & IFF_IPMP) {
                          ifp->ifi_cflags |= IFIF_IPMP;
+                         ifp->ifi_class = IPADM_IF_CLASS_IPMP;
+                 }
                  if (lifrl.lifr_flags & IFF_STANDBY)
                          ifp->ifi_cflags |= IFIF_STANDBY;
                  if (lifrl.lifr_flags & IFF_INACTIVE)
                          ifp->ifi_cflags |= IFIF_INACTIVE;
                  if (lifrl.lifr_flags & IFF_VRRP)
*** 169,185 ****
                          ifp->ifi_cflags |= IFIF_IPV4;
                  if (lifrl.lifr_flags & IFF_IPV6)
                          ifp->ifi_cflags |= IFIF_IPV6;
                  if (lifrl.lifr_flags & IFF_L3PROTECT)
                          ifp->ifi_cflags |= IFIF_L3PROTECT;
          }
          free(buf);
!         return (IPADM_SUCCESS);
! fail:
!         free(buf);
          ipadm_free_if_info(*if_info);
          *if_info = NULL;
          return (status);
  }
  
  /*
   * Returns the interface information for `ifname' in `if_info' from persistent
--- 195,224 ----
                          ifp->ifi_cflags |= IFIF_IPV4;
                  if (lifrl.lifr_flags & IFF_IPV6)
                          ifp->ifi_cflags |= IFIF_IPV6;
                  if (lifrl.lifr_flags & IFF_L3PROTECT)
                          ifp->ifi_cflags |= IFIF_L3PROTECT;
+ 
+         /* Retrieve active IPMP members */
+         if (ifp->ifi_class == IPADM_IF_CLASS_IPMP) {
+                 if (ioctl(s, SIOCGLIFGROUPNAME,
+                     (caddr_t)&lifrl) < 0) {
+                         status = ipadm_errno2status(errno);
+                         break;
                  }
+ 
+                 if ((status = i_ipadm_fill_cmembers(
+                     lifrl.lifr_groupname,
+                     &ifp->ifi_ipmp_cmembers)) != IPADM_SUCCESS)
+                         break;
+                 }
+         }
          free(buf);
!         if (status != IPADM_SUCCESS) {
                  ipadm_free_if_info(*if_info);
                  *if_info = NULL;
+         }
          return (status);
  }
  
  /*
   * Returns the interface information for `ifname' in `if_info' from persistent
*** 189,239 ****
  static ipadm_status_t
  i_ipadm_persist_if_info(ipadm_handle_t iph, const char *ifname,
      ipadm_if_info_t **if_info)
  {
          ipadm_status_t          status = IPADM_SUCCESS;
!         ipmgmt_getif_arg_t      getif;
!         ipmgmt_getif_rval_t     *rvalp;
!         ipadm_if_info_t         *ifp, *curr, *prev = NULL;
!         int                     i = 0, err = 0;
  
-         bzero(&getif, sizeof (getif));
-         if (ifname != NULL)
-                 (void) strlcpy(getif.ia_ifname, ifname, LIFNAMSIZ);
-         getif.ia_cmd = IPMGMT_CMD_GETIF;
- 
          *if_info = NULL;
  
!         if ((rvalp = malloc(sizeof (ipmgmt_getif_rval_t))) == NULL)
                  return (ipadm_errno2status(errno));
!         err = ipadm_door_call(iph, &getif, sizeof (getif), (void **)&rvalp,
!             sizeof (*rvalp), B_TRUE);
!         if (err == ENOENT) {
!                 free(rvalp);
!                 if (ifname != NULL)
!                         return (ipadm_errno2status(err));
                  return (IPADM_SUCCESS);
!         } else if (err != 0) {
!                 free(rvalp);
!                 return (ipadm_errno2status(err));
          }
  
!         ifp = rvalp->ir_ifinfo;
!         for (i = 0; i < rvalp->ir_ifcnt; i++) {
!                 ifp = rvalp->ir_ifinfo + i;
!                 if ((curr = malloc(sizeof (*curr))) == NULL) {
!                         status = ipadm_errno2status(errno);
!                         ipadm_free_if_info(prev);
!                         break;
                  }
!                 (void) bcopy(ifp, curr, sizeof (*curr));
!                 curr->ifi_next = prev;
!                 prev = curr;
          }
!         *if_info = curr;
!         free(rvalp);
!         return (status);
  }
  
  /*
   * Collects information for `ifname' if one is specified from both
   * active and persistent config in `if_info'. If no `ifname' is specified,
--- 228,378 ----
  static ipadm_status_t
  i_ipadm_persist_if_info(ipadm_handle_t iph, const char *ifname,
      ipadm_if_info_t **if_info)
  {
          ipadm_status_t  status = IPADM_SUCCESS;
!         nvlist_t        *ifs_info_nvl;
  
          *if_info = NULL;
  
!         if ((status = i_ipadm_get_db_if(iph,
!             ifname, &ifs_info_nvl)) != IPADM_SUCCESS)
!                 return (status);
! 
!         assert(ifs_info_nvl != NULL);
! 
!         return (i_ipadm_nvl2ifinfo(ifs_info_nvl, if_info));
! }
! 
! static ipadm_status_t
! i_ipadm_nvl2ifinfo(nvlist_t *ifs_info_nvl, ipadm_if_info_t **if_info)
! {
!         ipadm_if_info_t *ific = NULL, *ifil = NULL;
!         nvlist_t        *if_info_nvl;
!         nvpair_t        *nvp;
!         char            *strval;
!         ipadm_status_t  status = IPADM_SUCCESS;
!         uint16_t        *families;
!         uint_t          nelem = 0;
! 
!         for (nvp = nvlist_next_nvpair(ifs_info_nvl, NULL); nvp != NULL;
!             nvp = nvlist_next_nvpair(ifs_info_nvl, nvp)) {
!                 if (nvpair_value_nvlist(nvp, &if_info_nvl) != 0)
!                         continue;
! 
!                 status = i_ipadm_allocate_ifinfo(&ific);
!                 if (status != IPADM_SUCCESS) {
!                         ipadm_free_if_info(*if_info);
!                         break;
!                 }
!                 if (nvlist_lookup_string(if_info_nvl, IPADM_NVP_IFNAME,
!                     &strval) != 0) {
!                         ipadm_free_if_info(ific);
!                         ific = NULL;
!                         continue;
!                 }
!                 (void) strlcpy(ific->ifi_name, strval,
!                     sizeof (ific->ifi_name));
! 
!                 if (nvlist_lookup_uint16_array(if_info_nvl,
!                     IPADM_NVP_FAMILIES, &families, &nelem) == 0) {
!                         while (nelem-- > 0) {
!                                 if (families[nelem] == AF_INET)
!                                         ific->ifi_pflags |= IFIF_IPV4;
!                                 else if (families[nelem] == AF_INET6)
!                                         ific->ifi_pflags |= IFIF_IPV6;
!                         }
!                 }
! 
!                 if (nvlist_lookup_string(if_info_nvl,
!                     IPADM_NVP_IFCLASS, &strval) == 0)
!                         ific->ifi_class = atoi(strval);
!                 else
!                         ific->ifi_class = IPADM_IF_CLASS_REGULAR;
! 
!                 if (ific->ifi_class == IPADM_IF_CLASS_IPMP)
!                         /* do not expect any failures there */
!                         (void) i_ipadm_fill_pmembers(if_info_nvl,
!                             &ific->ifi_ipmp_pmembers);
! 
!                 if (*if_info == NULL)
!                         *if_info = ific;
!                 else
!                         ifil->ifi_next = ific;
!                 ifil = ific;
!         }
! 
!         nvlist_free(ifs_info_nvl);
!         return (status);
! }
! 
! /*
!  * Fill the ipadm_if_info_t->ifi_ipmp_pmembers by info from
!  * ipadm DB
!  */
! static ipadm_status_t
! i_ipadm_fill_pmembers(nvlist_t *if_info_nvl, ipadm_ipmp_members_t *pmembers)
! {
!         uint_t  nelem = 0;
!         char    **members;
!         ipadm_ipmp_member_t *ipmp_member;
! 
!         if (nvlist_lookup_string_array(if_info_nvl, IPADM_NVP_MIFNAMES,
!             &members, &nelem) != 0)
!                 return (IPADM_SUCCESS);
! 
!         while (nelem-- > 0) {
!                 if ((ipmp_member = calloc(1,
!                     sizeof (ipadm_ipmp_member_t))) == NULL)
                          return (ipadm_errno2status(errno));
! 
!                 (void) strlcpy(ipmp_member->if_name, members[nelem],
!                     sizeof (ipmp_member->if_name));
!                 list_insert_tail(pmembers, ipmp_member);
!         }
          return (IPADM_SUCCESS);
! }
! 
! /*
!  * Fill the ipadm_if_info_t->ifi_ipmp_cmembers by info from
!  * kernel (libipmp is used to retrieve the required info)
!  */
! static ipadm_status_t
! i_ipadm_fill_cmembers(char *grname, ipadm_ipmp_members_t *cmembers)
! {
!         ipmp_handle_t ipmp_handle;
!         ipmp_groupinfo_t *grinfo;
!         ipmp_iflist_t *iflistp;
!         ipadm_ipmp_member_t *ipmp_member;
!         ipadm_status_t ipadm_status = IPADM_SUCCESS;
!         int i;
! 
!         if (ipmp_open(&ipmp_handle) != IPMP_SUCCESS)
!                 return (IPADM_FAILURE);
! 
!         if (ipmp_getgroupinfo(ipmp_handle, grname, &grinfo) != IPMP_SUCCESS) {
!                 ipadm_status = IPADM_FAILURE;
!                 goto fail2;
          }
  
!         iflistp = grinfo->gr_iflistp;
!         for (i = 0; i < iflistp->il_nif; i++) {
!                 if ((ipmp_member = calloc(1,
!                     sizeof (ipadm_ipmp_member_t))) == NULL) {
!                         ipadm_status = ipadm_errno2status(errno);
!                         goto fail1;
                  }
!                 (void) strlcpy(ipmp_member->if_name, iflistp->il_ifs[i],
!                     sizeof (ipmp_member->if_name));
!                 list_insert_tail(cmembers, ipmp_member);
          }
! 
! fail1:
!         ipmp_freegroupinfo(grinfo);
! fail2:
!         ipmp_close(ipmp_handle);
!         return (ipadm_status);
  }
  
  /*
   * Collects information for `ifname' if one is specified from both
   * active and persistent config in `if_info'. If no `ifname' is specified,
*** 322,350 ****
           * add this interface to `aifinfo' and set it state to IFIF_DISABLED.
           */
          for (pifp = pifinfo; pifp != NULL; pifp = pifp->ifi_next) {
                  for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
                          if (strcmp(aifp->ifi_name, pifp->ifi_name) == 0) {
-                                 aifp->ifi_pflags = pifp->ifi_pflags;
                                  break;
                          }
                  }
                  if (aifp == NULL) {
!                         aifp = malloc(sizeof (ipadm_if_info_t));
!                         if (aifp == NULL) {
!                                 status = ipadm_errno2status(errno);
                                  goto fail;
!                         }
!                         *aifp = *pifp;
                          aifp->ifi_next = NULL;
                          aifp->ifi_state = IFIS_DISABLED;
                          if (last != NULL)
                                  last->ifi_next = aifp;
                          else
                                  aifinfo = aifp;
                          last = aifp;
                  }
          }
          *if_info = aifinfo;
          ipadm_free_if_info(pifinfo);
          return (IPADM_SUCCESS);
  fail:
--- 461,494 ----
           * add this interface to `aifinfo' and set it state to IFIF_DISABLED.
           */
          for (pifp = pifinfo; pifp != NULL; pifp = pifp->ifi_next) {
                  for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
                          if (strcmp(aifp->ifi_name, pifp->ifi_name) == 0) {
                                  break;
                          }
                  }
+ 
                  if (aifp == NULL) {
!                         if ((status =
!                             i_ipadm_allocate_ifinfo(&aifp)) != IPADM_SUCCESS)
                                  goto fail;
! 
!                         (void) strlcpy(aifp->ifi_name, pifp->ifi_name,
!                             sizeof (aifp->ifi_name));
! 
                          aifp->ifi_next = NULL;
                          aifp->ifi_state = IFIS_DISABLED;
                          if (last != NULL)
                                  last->ifi_next = aifp;
                          else
                                  aifinfo = aifp;
                          last = aifp;
                  }
+ 
+                 if ((status = i_ipadm_add_persistent_if_info(aifp,
+                     pifp)) != IPADM_SUCCESS)
+                         goto fail;
          }
          *if_info = aifinfo;
          ipadm_free_if_info(pifinfo);
          return (IPADM_SUCCESS);
  fail:
*** 352,361 ****
--- 496,574 ----
          ipadm_free_if_info(aifinfo);
          ipadm_free_if_info(pifinfo);
          return (status);
  }
  
+ /*
+  * Updates active if_info by data from persistent if_info
+  */
+ static ipadm_status_t
+ i_ipadm_add_persistent_if_info(ipadm_if_info_t *aifp, ipadm_if_info_t *pifp)
+ {
+         ipadm_ipmp_member_t *pp_ipmp_member, *ap_ipmp_member;
+ 
+         ipadm_ipmp_members_t *apmembers = &aifp->ifi_ipmp_pmembers;
+         ipadm_ipmp_members_t *ppmembers = &pifp->ifi_ipmp_pmembers;
+ 
+         aifp->ifi_pflags = pifp->ifi_pflags;
+         aifp->ifi_class = pifp->ifi_class;
+ 
+         for (pp_ipmp_member = list_head(ppmembers); pp_ipmp_member;
+             pp_ipmp_member = list_next(ppmembers, pp_ipmp_member)) {
+                 if ((ap_ipmp_member = calloc(1,
+                     sizeof (ipadm_ipmp_member_t))) == NULL)
+                         return (ipadm_errno2status(errno));
+ 
+                 (void) strlcpy(ap_ipmp_member->if_name,
+                     pp_ipmp_member->if_name,
+                     sizeof (ap_ipmp_member->if_name));
+ 
+                 list_insert_tail(apmembers, ap_ipmp_member);
+         }
+         return (IPADM_SUCCESS);
+ }
+ 
+ static ipadm_status_t
+ i_ipadm_allocate_ifinfo(ipadm_if_info_t **if_info)
+ {
+         *if_info = calloc(1, sizeof (ipadm_if_info_t));
+         if (*if_info == NULL)
+                 return (ipadm_errno2status(errno));
+ 
+         /* List of active (current) members */
+         list_create(&((*if_info)->ifi_ipmp_cmembers),
+             sizeof (ipadm_ipmp_member_t),
+             offsetof(ipadm_ipmp_member_t, node));
+ 
+         /* List of persistent members */
+         list_create(&((*if_info)->ifi_ipmp_pmembers),
+             sizeof (ipadm_ipmp_member_t),
+             offsetof(ipadm_ipmp_member_t, node));
+ 
+         return (IPADM_SUCCESS);
+ }
+ 
+ /*
+  * Reads all the interface lines from the persistent DB into the nvlist `onvl',
+  * when `ifname' is NULL.
+  * If an `ifname' is specified, then the interface line corresponding to
+  * that name will be returned.
+  */
+ static ipadm_status_t
+ i_ipadm_get_db_if(ipadm_handle_t iph, const char *ifname, nvlist_t **onvl)
+ {
+         ipmgmt_getif_arg_t      garg;
+ 
+         /* Populate the door_call argument structure */
+         bzero(&garg, sizeof (garg));
+         garg.ia_cmd = IPMGMT_CMD_GETIF;
+         if (ifname != NULL)
+                 (void) strlcpy(garg.ia_ifname, ifname, sizeof (garg.ia_ifname));
+ 
+         return (i_ipadm_call_ipmgmtd(iph, (void *) &garg, sizeof (garg), onvl));
+ }
+ 
  int
  i_ipadm_get_lnum(const char *ifname)
  {
          char *num = strrchr(ifname, IPADM_LOGICAL_SEP);
  
*** 391,401 ****
          if (status == IPADM_SUCCESS) {
                  *exists = ((af == AF_INET &&
                      (ifinfo->ifi_pflags & IFIF_IPV4)) ||
                      (af == AF_INET6 &&
                      (ifinfo->ifi_pflags & IFIF_IPV6)));
!                 free(ifinfo);
          } else if (status == IPADM_NOTFOUND) {
                  status = IPADM_SUCCESS;
                  *exists = B_FALSE;
          }
          return (status);
--- 604,614 ----
          if (status == IPADM_SUCCESS) {
                  *exists = ((af == AF_INET &&
                      (ifinfo->ifi_pflags & IFIF_IPV4)) ||
                      (af == AF_INET6 &&
                      (ifinfo->ifi_pflags & IFIF_IPV6)));
!                 ipadm_free_if_info(ifinfo);
          } else if (status == IPADM_NOTFOUND) {
                  status = IPADM_SUCCESS;
                  *exists = B_FALSE;
          }
          return (status);
*** 730,740 ****
                          bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
                          lifr.lifr_addr.ss_family = af;
                          if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
                                  return (ipadm_errno2status(errno));
                          if (is_persistent) {
!                                 status = i_ipadm_persist_if(iph, ifname, af);
                                  if (status != IPADM_SUCCESS) {
                                          (void) i_ipadm_delete_if(iph, ifname,
                                              af, IPADM_OPT_ACTIVE);
                                  }
                          }
--- 943,954 ----
                          bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
                          lifr.lifr_addr.ss_family = af;
                          if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
                                  return (ipadm_errno2status(errno));
                          if (is_persistent) {
!                                 status = i_ipadm_persist_if(iph,
!                                     ifname, af, ipadm_flags);
                                  if (status != IPADM_SUCCESS) {
                                          (void) i_ipadm_delete_if(iph, ifname,
                                              af, IPADM_OPT_ACTIVE);
                                  }
                          }
*** 911,921 ****
                  /*
                   * If IPADM_OPT_PERSIST was set in flags, store the
                   * interface in persistent DB.
                   */
                  if (is_persistent) {
!                         status = i_ipadm_persist_if(iph, newif, af);
                          if (status != IPADM_SUCCESS) {
                                  (void) i_ipadm_delete_if(iph, newif, af,
                                      IPADM_OPT_ACTIVE);
                          }
                  }
--- 1125,1136 ----
                  /*
                   * If IPADM_OPT_PERSIST was set in flags, store the
                   * interface in persistent DB.
                   */
                  if (is_persistent) {
!                         status = i_ipadm_persist_if(iph,
!                             newif, af, ipadm_flags);
                          if (status != IPADM_SUCCESS) {
                                  (void) i_ipadm_delete_if(iph, newif, af,
                                      IPADM_OPT_ACTIVE);
                          }
                  }
*** 1144,1160 ****
  /*
   * Saves the given interface name `ifname' with address family `af' in
   * persistent DB.
   */
  static ipadm_status_t
! i_ipadm_persist_if(ipadm_handle_t iph, const char *ifname, sa_family_t af)
  {
          ipmgmt_if_arg_t         ifarg;
          int                     err;
  
          (void) strlcpy(ifarg.ia_ifname, ifname, sizeof (ifarg.ia_ifname));
          ifarg.ia_family = af;
          ifarg.ia_cmd = IPMGMT_CMD_SETIF;
          ifarg.ia_flags = IPMGMT_PERSIST;
          err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
          return (ipadm_errno2status(err));
  }
--- 1359,1381 ----
  /*
   * Saves the given interface name `ifname' with address family `af' in
   * persistent DB.
   */
  static ipadm_status_t
! i_ipadm_persist_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
!     uint32_t ipadm_flags)
  {
          ipmgmt_if_arg_t         ifarg;
          int                     err;
  
          (void) strlcpy(ifarg.ia_ifname, ifname, sizeof (ifarg.ia_ifname));
          ifarg.ia_family = af;
+         if (ipadm_flags & IPADM_OPT_IPMP)
+                 ifarg.ia_ifclass = IPADM_IF_CLASS_IPMP;
+         else
+                 ifarg.ia_ifclass = IPADM_IF_CLASS_REGULAR;
+ 
          ifarg.ia_cmd = IPMGMT_CMD_SETIF;
          ifarg.ia_flags = IPMGMT_PERSIST;
          err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
          return (ipadm_errno2status(err));
  }
*** 1343,1353 ****
--- 1564,1677 ----
          }
  
          return (IPADM_SUCCESS);
  }
  
+ ipadm_status_t
+ ipadm_add_ipmp_member(ipadm_handle_t iph, const char *gifname,
+     const char *mifname, uint32_t ipadm_flags)
+ {
+         return (i_ipadm_update_ipmp(iph, gifname, mifname,
+             ipadm_flags, IPADM_ADD_IPMP));
+ }
+ 
+ ipadm_status_t
+ ipadm_remove_ipmp_member(ipadm_handle_t iph, const char *gifname,
+     const char *mifname, uint32_t ipadm_flags)
+ {
+         return (i_ipadm_update_ipmp(iph, gifname, mifname,
+             ipadm_flags, IPADM_REMOVE_IPMP));
+ }
+ 
  /*
+  * Updates active IPMP configuration according to the specified
+  * command. It also persists the configuration if IPADM_OPT_PERSIST
+  * is set in `ipadm_flags'.
+  */
+ static ipadm_status_t
+ i_ipadm_update_ipmp(ipadm_handle_t iph, const char *gifname,
+     const char *mifname, uint32_t ipadm_flags, ipadm_ipmp_op_t op)
+ {
+         ipadm_status_t status;
+         char    groupname1[LIFGRNAMSIZ];
+         char    groupname2[LIFGRNAMSIZ];
+ 
+         /* Check for the required authorization */
+         if (!ipadm_check_auth())
+                 return (IPADM_EAUTH);
+ 
+         if (!(ipadm_flags & IPADM_OPT_ACTIVE) ||
+             gifname == NULL || mifname == NULL)
+                 return (IPADM_INVALID_ARG);
+ 
+         if (!ipadm_if_enabled(iph, gifname, AF_UNSPEC) ||
+             !ipadm_if_enabled(iph, mifname, AF_UNSPEC))
+                 return (IPADM_OP_DISABLE_OBJ);
+ 
+         if (!i_ipadm_is_ipmp(iph, gifname))
+                 return (IPADM_INVALID_ARG);
+ 
+         if (op == IPADM_ADD_IPMP && i_ipadm_is_under_ipmp(iph, mifname))
+                 return (IPADM_IF_INUSE);
+ 
+         if ((status = i_ipadm_get_groupname_active(iph, gifname,
+             groupname2, sizeof (groupname2))) != IPADM_SUCCESS)
+                 return (status);
+ 
+         if (op == IPADM_REMOVE_IPMP) {
+                 if ((status = i_ipadm_get_groupname_active(iph, mifname,
+                     groupname1, sizeof (groupname1))) != IPADM_SUCCESS)
+                         return (status);
+ 
+                 if (groupname1[0] == '\0' ||
+                     strcmp(groupname1, groupname2) != 0)
+                         return (IPADM_INVALID_ARG);
+ 
+                 groupname2[0] = '\0';
+         }
+ 
+         if ((ipadm_flags & IPADM_OPT_PERSIST) &&
+             (status = i_ipadm_persist_update_ipmp(iph, gifname,
+             mifname, op)) != IPADM_SUCCESS)
+                 return (status);
+ 
+         return (i_ipadm_set_groupname_active(iph, mifname, groupname2));
+ }
+ 
+ /*
+  * Call the ipmgmtd to update the IPMP configuration in ipadm DB.
+  * After this call the DB will know that mifname is under gifname and
+  * gifname has a member, which name is mifname.
+  */
+ static ipadm_status_t
+ i_ipadm_persist_update_ipmp(ipadm_handle_t iph, const char *gifname,
+     const char *mifname, ipadm_ipmp_op_t op)
+ {
+         ipmgmt_ipmp_update_arg_t args;
+         int err;
+ 
+         assert(op == IPADM_ADD_IPMP || op == IPADM_REMOVE_IPMP);
+ 
+         bzero(&args, sizeof (ipmgmt_ipmp_update_arg_t));
+ 
+         args.ia_cmd = IPMGMT_CMD_IPMP_UPDATE;
+ 
+         (void) strlcpy(args.ia_gifname, gifname, sizeof (args.ia_gifname));
+         (void) strlcpy(args.ia_mifname, mifname, sizeof (args.ia_mifname));
+ 
+         if (op == IPADM_ADD_IPMP)
+                 args.ia_flags = IPMGMT_APPEND;
+         else
+                 args.ia_flags = IPMGMT_REMOVE;
+ 
+         args.ia_flags |= IPMGMT_PERSIST;
+ 
+         err = ipadm_door_call(iph, &args, sizeof (args), NULL, 0, B_FALSE);
+         return (ipadm_errno2status(err));
+ }
+ 
+ /*
   * Deletes the interface in `ifname'. Removes both IPv4 and IPv6 interfaces
   * when `af' = AF_UNSPEC.
   */
  ipadm_status_t
  ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
*** 1446,1459 ****
--- 1770,1796 ----
  {
          ipadm_if_info_t *ifinfo_next;
  
          for (; ifinfo != NULL; ifinfo = ifinfo_next) {
                  ifinfo_next = ifinfo->ifi_next;
+                 i_ipadm_free_ipmp_members(&ifinfo->ifi_ipmp_cmembers);
+                 i_ipadm_free_ipmp_members(&ifinfo->ifi_ipmp_pmembers);
                  free(ifinfo);
          }
  }
  
+ static void
+ i_ipadm_free_ipmp_members(ipadm_ipmp_members_t *ipmp_members)
+ {
+         ipadm_ipmp_member_t *ipmp_member;
+ 
+         while ((ipmp_member = list_remove_head(ipmp_members)) != NULL)
+                 free(ipmp_member);
+ 
+         list_destroy(ipmp_members);
+ }
+ 
  /*
   * Re-enable the interface `ifname' based on the saved configuration
   * for `ifname'.
   */
  ipadm_status_t
*** 1477,1489 ****
  
          /*
           * Return early by checking if the interface is already enabled.
           */
          if (ipadm_if_enabled(iph, ifname, AF_INET) &&
!             ipadm_if_enabled(iph, ifname, AF_INET6)) {
                  return (IPADM_IF_EXISTS);
!         }
          /*
           * Enable the interface and restore all its interface properties
           * and address objects.
           */
          status = i_ipadm_init_ifs(iph, ifname, &ifnvl);
--- 1814,1826 ----
  
          /*
           * Return early by checking if the interface is already enabled.
           */
          if (ipadm_if_enabled(iph, ifname, AF_INET) &&
!             ipadm_if_enabled(iph, ifname, AF_INET6))
                  return (IPADM_IF_EXISTS);
! 
          /*
           * Enable the interface and restore all its interface properties
           * and address objects.
           */
          status = i_ipadm_init_ifs(iph, ifname, &ifnvl);
*** 1546,1559 ****
                  return (IPADM_FAILURE);
          }
  }
  
  /*
!  * This workaround is until libipadm supports IPMP and is required whenever an
!  * interface is moved into an IPMP group. Since libipadm doesn't support IPMP
!  * yet, we will have to update the daemon's in-memory mapping of
!  * `aobjname' to 'lifnum'.
   *
   * For `IPMGMT_ACTIVE' case, i_ipadm_delete_ifobj() would only fail if
   * door_call(3C) fails. Also, there is no use in returning error because
   * `ifname' would have been successfuly moved into IPMP group, by this time.
   */
--- 1883,1899 ----
                  return (IPADM_FAILURE);
          }
  }
  
  /*
!  * FIXME Remove this when ifconfig(1M) is updated to use IPMP support
!  * in libipadm.
!  */
! /*
!  * This workaround is required by ifconfig(1M) whenever an
!  * interface is moved into an IPMP group to update the daemon's
!  * in-memory mapping of `aobjname' to 'lifnum'.
   *
   * For `IPMGMT_ACTIVE' case, i_ipadm_delete_ifobj() would only fail if
   * door_call(3C) fails. Also, there is no use in returning error because
   * `ifname' would have been successfuly moved into IPMP group, by this time.
   */
*** 1560,1565 ****
--- 1900,2013 ----
  void
  ipadm_if_move(ipadm_handle_t iph, const char *ifname)
  {
          (void) i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
          (void) i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
+ }
+ 
+ ipadm_status_t
+ i_ipadm_set_groupname_active(ipadm_handle_t iph, const char *ifname,
+     const char *groupname)
+ {
+         struct lifreq   lifr;
+         ipadm_addr_info_t *addrinfo, *ia;
+         ipadm_status_t  status = IPADM_SUCCESS;
+ 
+         (void) memset(&lifr, 0, sizeof (lifr));
+ 
+         (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+         (void) strlcpy(lifr.lifr_groupname, groupname,
+             sizeof (lifr.lifr_groupname));
+ 
+         /* Disable all addresses on the interface */
+         (void) i_ipadm_active_addr_info(iph, ifname, &addrinfo,
+             IPADM_OPT_ACTIVE, IFF_UP | IFF_DUPLICATE);
+         for (ia = addrinfo; ia != NULL; ia = IA_NEXT(ia))
+                 (void) ipadm_disable_addr(iph, ia->ia_aobjname, 0);
+ 
+         if (ioctl(iph->iph_sock, SIOCSLIFGROUPNAME, (caddr_t)&lifr) == -1 &&
+             ioctl(iph->iph_sock6, SIOCSLIFGROUPNAME, (caddr_t)&lifr) == -1)
+                 status = ipadm_errno2status(errno);
+ 
+         /* Enable all addresses on the interface */
+         for (ia = addrinfo; ia != NULL; ia = IA_NEXT(ia))
+                 (void) ipadm_enable_addr(iph, ia->ia_aobjname, 0);
+ 
+         if (status == IPADM_SUCCESS) {
+                 if (groupname[0] == '\0') {
+                         /*
+                          * If interface was removed from IPMP group, unset the
+                          * DEPRECATED and NOFAILOVER flags.
+                          */
+                         (void) i_ipadm_set_flags(iph, ifname, AF_INET, 0,
+                             IFF_DEPRECATED | IFF_NOFAILOVER);
+                         (void) i_ipadm_set_flags(iph, ifname, AF_INET6, 0,
+                             IFF_DEPRECATED | IFF_NOFAILOVER);
+                 } else if (addrinfo == NULL) {
+                         /*
+                          * If interface was added to IPMP group and there are no
+                          * active addresses, explicitly bring it up to be used
+                          * for link-based IPMP configuration.
+                          */
+                         (void) i_ipadm_set_flags(iph, ifname, AF_INET,
+                             IFF_UP, 0);
+                         (void) i_ipadm_set_flags(iph, ifname, AF_INET6,
+                             IFF_UP, 0);
+                 }
+         }
+ 
+         ipadm_free_addr_info(addrinfo);
+ 
+         return (status);
+ }
+ 
+ ipadm_status_t
+ i_ipadm_get_groupname_active(ipadm_handle_t iph, const char *ifname,
+     char *groupname, size_t size)
+ {
+         struct lifreq   lifr;
+ 
+         (void) memset(&lifr, 0, sizeof (lifr));
+ 
+         (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ 
+         if (ioctl(iph->iph_sock, SIOCGLIFGROUPNAME, (caddr_t)&lifr) == -1 &&
+             ioctl(iph->iph_sock6, SIOCGLIFGROUPNAME, (caddr_t)&lifr) == -1)
+                 return (ipadm_errno2status(errno));
+ 
+         (void) strlcpy(groupname, lifr.lifr_groupname, size);
+ 
+         return (IPADM_SUCCESS);
+ }
+ 
+ /*
+  * Returns B_TRUE if `ifname' represents an IPMP underlying interface.
+  */
+ boolean_t
+ i_ipadm_is_under_ipmp(ipadm_handle_t iph, const char *ifname)
+ {
+ 
+         char    groupname[LIFGRNAMSIZ];
+ 
+         if (i_ipadm_get_groupname_active(iph, ifname, groupname,
+             sizeof (groupname)) != IPADM_SUCCESS ||
+             groupname[0] == '\0' ||
+             strcmp(ifname, groupname) == 0)
+                 return (B_FALSE);
+ 
+         return (B_TRUE);
+ }
+ 
+ /*
+  * Returns B_TRUE if `ifname' represents an IPMP group interface.
+  */
+ boolean_t
+ i_ipadm_is_ipmp(ipadm_handle_t iph, const char *ifname)
+ {
+         uint64_t flags, flags6;
+ 
+         if (i_ipadm_get_flags(iph, ifname, AF_INET, &flags) != IPADM_SUCCESS &&
+             i_ipadm_get_flags(iph, ifname, AF_INET6, &flags6) != IPADM_SUCCESS)
+                 return (B_FALSE);
+ 
+         return ((flags & IFF_IPMP) != 0 || (flags6 & IFF_IPMP) != 0);
  }