Print this page
Commit IPMP changes

@@ -18,14 +18,16 @@
  *
  * CDDL HEADER END
  */
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
  */
 
 #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 +36,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_operation_t);
+static ipadm_status_t i_ipadm_update_ipmp(ipadm_handle_t, const char *,
+        const char *, uint32_t,
+        ipadm_ipmp_operation_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 +138,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 +162,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 +194,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;
+
+        /* Retrive 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 +227,157 @@
 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--) {
+                                if (families[nelem] == AF_INET)
+                                        ific->ifi_pflags |= IFIF_IPV4;
+                                else if (families[nelem] == AF_INET6)
+                                        ific->ifi_pflags |= IFIF_IPV6;
+                        }
+                } else {
+                        ipadm_free_if_info(ific);
+                        ific = NULL;
+                        continue;
+                }
+                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)
+                        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--) {
+                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 retrive the required info)
+ */
+static ipadm_status_t
+i_ipadm_fill_cmembers(char *gropname, 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 ipmp_status;
+        uint_t  i;
+
+        if ((ipmp_status = ipmp_open(&ipmp_handle)) != IPMP_SUCCESS)
+                return (IPADM_FAILURE);
+
+        if ((ipmp_status = ipmp_getgroupinfo(ipmp_handle,
+            gropname,
+            &grinfo)) != IPMP_SUCCESS) {
+                ipadm_status = IPADM_FAILURE;
+                goto fail;
         }
 
-        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 fail;
                 }
-                (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);
+
+fail:
+        ipmp_freegroupinfo(grinfo);
+        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 +466,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 +501,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 +609,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 +948,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 +1130,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 +1364,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));
 }

@@ -1261,10 +1487,11 @@
          * or the persistent configuration.
          */
         if (ipadm_if_enabled(iph, ifname, af))
                 return (IPADM_IF_EXISTS);
 
+#if 0
         if (!(iph->iph_flags & IPH_LEGACY)) {
                 status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
                 if (status != IPADM_SUCCESS)
                         return (status);
                 other_af = (af == AF_INET ? AF_INET6 : AF_INET);

@@ -1273,11 +1500,11 @@
                                 return (IPADM_OP_DISABLE_OBJ);
                         else
                                 ipadm_flags &= ~IPADM_OPT_PERSIST;
                 }
         }
-
+#endif
         return (i_ipadm_plumb_if(iph, ifname, af, ipadm_flags));
 }
 
 /*
  * Plumbs an interface. Creates both IPv4 and IPv6 interfaces by

@@ -1343,11 +1570,140 @@
         }
 
         return (IPADM_SUCCESS);
 }
 
+ipadm_status_t
+ipadm_add_ipmp_member(ipadm_handle_t iph, const char *gifname,
+        const char *mifname, uint32_t flags)
+{
+        return (i_ipadm_update_ipmp(iph, gifname, mifname,
+            flags, IPADM_ADD_IPMP_MEMBER));
+}
+
+ipadm_status_t
+ipadm_remove_ipmp_member(ipadm_handle_t iph, const char *gifname,
+        const char *mifname, uint32_t flags)
+{
+        return (i_ipadm_update_ipmp(iph, gifname, mifname,
+            flags, IPADM_REMOVE_IPMP_MEMBER));
+}
+
 /*
+ * Update IPMP configuration according to requested operation,
+ * that can be
+ *
+ * IPADM_ADD_IPMP_MEMBER
+ *
+ * IPADM_REMOVE_IPMP_MEMBER
+ *
+ * At first it update the active config and if IPADM_OPT_PERSIST is set,
+ * then we also update persistent ipadm DB
+ */
+static ipadm_status_t
+i_ipadm_update_ipmp(ipadm_handle_t iph,
+        const char *gifname, const char *mifname,
+        uint32_t flags, ipadm_ipmp_operation_t operation)
+{
+        ipadm_status_t status;
+        char    group_name1[LIFGRNAMSIZ];
+        char    group_name2[LIFGRNAMSIZ];
+
+        /* Check for the required authorization */
+        if (!ipadm_check_auth())
+                return (IPADM_EAUTH);
+
+        if (!(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 1
+        if (!i_ipadm_is_ipmp(iph, gifname)) {
+                return (IPADM_INVALID_ARG);
+        }
+#endif
+        if (operation == IPADM_ADD_IPMP_MEMBER &&
+            i_ipadm_is_under_ipmp(iph, mifname))
+                return (IPADM_IF_INUSE);
+
+        if ((status = i_ipadm_get_groupname_active(iph, gifname,
+            group_name2, LIFGRNAMSIZ)) != IPADM_SUCCESS)
+                return (status);
+
+        if (operation == IPADM_REMOVE_IPMP_MEMBER) {
+                if ((status = i_ipadm_get_groupname_active(iph, mifname,
+                    group_name1, LIFGRNAMSIZ)) != IPADM_SUCCESS)
+                        return (status);
+
+                /* FIXME: Need to return something another */
+                if (group_name1[0] == '\0')
+                        return (IPADM_INVALID_ARG);
+
+                /* FIXME: Need to return something another */
+                if (strcmp(group_name1, group_name2) != 0)
+                        return (IPADM_INVALID_ARG);
+
+                group_name2[0] = '\0';
+        }
+
+        if ((status = i_ipadm_set_groupname_active(iph, mifname,
+            group_name2)) != IPADM_SUCCESS) {
+                return (status);
+        }
+        if (flags & IPADM_OPT_PERSIST) {
+                if ((status = i_ipadm_persist_update_ipmp(iph, gifname,
+                    mifname, operation)) != IPADM_SUCCESS) {
+                        /* Need to revert the active configuration */
+                        if (operation == IPADM_ADD_IPMP_MEMBER) {
+                                group_name2[0] = '\0';
+                                (void) i_ipadm_set_groupname_active(iph,
+                                    mifname, group_name2);
+                        }
+                }
+        }
+
+        return (status);
+}
+
+/*
+ * 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_operation_t operation)
+{
+        ipmgmt_ipmp_update_arg_t args;
+        int err;
+
+        assert(operation == IPADM_ADD_IPMP_MEMBER ||
+            operation == IPADM_REMOVE_IPMP_MEMBER);
+
+        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 (operation == IPADM_ADD_IPMP_MEMBER)
+                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 +1802,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

@@ -1558,6 +1927,83 @@
 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;
+
+        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));
+
+        if (ioctl(iph->iph_sock, SIOCSLIFGROUPNAME, (caddr_t)&lifr) < 0 &&
+            ioctl(iph->iph_sock6, SIOCSLIFGROUPNAME, (caddr_t)&lifr) < 0) {
+                return (ipadm_errno2status(errno));
+        }
+
+        return (IPADM_SUCCESS);
+}
+
+ipadm_status_t
+i_ipadm_get_groupname_active(ipadm_handle_t iph, const char *ifname,
+        char *groupname, size_t size)
+{
+        struct lifreq   lifr;
+
+        memset(&lifr, 0, sizeof (lifr));
+
+        (void) strlcpy(lifr.lifr_name, ifname,
+            sizeof (lifr.lifr_name));
+
+        if (ioctl(iph->iph_sock, SIOCGLIFGROUPNAME, (caddr_t)&lifr) < 0 &&
+            ioctl(iph->iph_sock6, SIOCGLIFGROUPNAME, (caddr_t)&lifr) < 0)
+                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,
+            LIFGRNAMSIZ) != IPADM_SUCCESS ||
+            groupname[0] == '\0')
+                return (B_FALSE);
+
+        if (strcmp(ifname, groupname) == 0)
+                return (B_FALSE);
+
+        return (B_TRUE);
+}
+
+/*
+ * Returns B_TRUE if `ifname' represents an IPMP meta-interface.
+ */
+boolean_t
+i_ipadm_is_ipmp(ipadm_handle_t iph, const char *ifname)
+{
+        uint64_t flags;
+
+        if (i_ipadm_get_flags(iph, ifname, AF_INET, &flags) != IPADM_SUCCESS &&
+            i_ipadm_get_flags(iph, ifname, AF_INET6, &flags) != IPADM_SUCCESS)
+                return (B_FALSE);
+
+        return ((flags & IFF_IPMP) != 0);
 }