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,16 +16,19 @@
  * 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,19 +37,35 @@
 #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);
+                            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,15 +139,14 @@
                 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;
-                        }
+                        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,20 +163,28 @@
                     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)
+                if (lifrl.lifr_flags & IFF_VIRTUAL) {
                         ifp->ifi_cflags |= IFIF_VIRTUAL;
-                if (lifrl.lifr_flags & IFF_IPMP)
+                        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,17 +195,30 @@
                         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);
-        return (IPADM_SUCCESS);
-fail:
-        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,51 +228,151 @@
 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;
+        nvlist_t        *ifs_info_nvl;
 
-        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)
+        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));
-        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));
+
+                (void) strlcpy(ipmp_member->if_name, members[nelem],
+                    sizeof (ipmp_member->if_name));
+                list_insert_tail(pmembers, ipmp_member);
+        }
                 return (IPADM_SUCCESS);
-        } else if (err != 0) {
-                free(rvalp);
-                return (ipadm_errno2status(err));
+}
+
+/*
+ * 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;
         }
 
-        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;
+        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) bcopy(ifp, curr, sizeof (*curr));
-                curr->ifi_next = prev;
-                prev = curr;
+                (void) strlcpy(ipmp_member->if_name, iflistp->il_ifs[i],
+                    sizeof (ipmp_member->if_name));
+                list_insert_tail(cmembers, ipmp_member);
         }
-        *if_info = curr;
-        free(rvalp);
-        return (status);
+
+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,29 +461,34 @@
          * 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);
+                        if ((status =
+                            i_ipadm_allocate_ifinfo(&aifp)) != IPADM_SUCCESS)
                                 goto fail;
-                        }
-                        *aifp = *pifp;
+
+                        (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,10 +496,79 @@
         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,11 +604,11 @@
         if (status == IPADM_SUCCESS) {
                 *exists = ((af == AF_INET &&
                     (ifinfo->ifi_pflags & IFIF_IPV4)) ||
                     (af == AF_INET6 &&
                     (ifinfo->ifi_pflags & IFIF_IPV6)));
-                free(ifinfo);
+                ipadm_free_if_info(ifinfo);
         } else if (status == IPADM_NOTFOUND) {
                 status = IPADM_SUCCESS;
                 *exists = B_FALSE;
         }
         return (status);
@@ -730,11 +943,12 @@
                         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);
+                                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,11 +1125,12 @@
                 /*
                  * 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);
+                        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,17 +1359,23 @@
 /*
  * 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)
+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,11 +1564,114 @@
         }
 
         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,14 +1770,27 @@
 {
         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,13 +1814,13 @@
 
         /*
          * 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)) {
+            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,14 +1883,17 @@
                 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'.
+ * 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,6 +1900,114 @@
 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);
 }