--- old/usr/src/lib/libipadm/common/ipadm_if.c	Sat Jul  5 09:39:28 2014
+++ new/usr/src/lib/libipadm/common/ipadm_if.c	Sat Jul  5 09:39:28 2014
@@ -20,10 +20,12 @@
  */
 /*
  * 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>
@@ -36,6 +38,7 @@
 #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);
@@ -44,7 +47,22 @@
 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
@@ -122,11 +140,10 @@
 				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 */
@@ -147,6 +164,10 @@
 		    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)
@@ -153,10 +174,14 @@
 			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)
@@ -171,13 +196,26 @@
 			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);
-	ipadm_free_if_info(*if_info);
-	*if_info = NULL;
+	if (status != IPADM_SUCCESS) {
+		ipadm_free_if_info(*if_info);
+		*if_info = NULL;
+	}
 	return (status);
 }
 
@@ -188,53 +226,159 @@
  */
 static ipadm_status_t
 i_ipadm_persist_if_info(ipadm_handle_t iph, const char *ifname,
-    ipadm_if_info_t **if_info)
+	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;
+	ipadm_status_t	status = IPADM_SUCCESS;
+	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)
-		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));
-	}
+	if ((status = i_ipadm_get_db_if(iph,
+	    ifname, &ifs_info_nvl)) != IPADM_SUCCESS)
+		return (status);
 
-	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);
+	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;
 		}
-		(void) bcopy(ifp, curr, sizeof (*curr));
-		curr->ifi_next = prev;
-		prev = curr;
+		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;
 	}
-	*if_info = curr;
-	free(rvalp);
+
+	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));
+
+		(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 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;
+	}
+
+	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) strlcpy(ipmp_member->if_name, iflistp->il_ifs[i],
+		    sizeof (ipmp_member->if_name));
+		list_insert_tail(cmembers, ipmp_member);
+	}
+
+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,
  * this returns all the interfaces in active and persistent config in
@@ -324,17 +468,18 @@
 	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)
@@ -343,6 +488,10 @@
 				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);
@@ -354,6 +503,75 @@
 	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)
 {
@@ -393,7 +611,7 @@
 		    (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;
@@ -732,7 +950,8 @@
 			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);
@@ -913,7 +1132,8 @@
 		 * 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);
@@ -1146,7 +1366,8 @@
  * 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;
@@ -1153,6 +1374,11 @@
 
 	(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);
@@ -1263,6 +1489,7 @@
 	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)
@@ -1275,7 +1502,7 @@
 				ipadm_flags &= ~IPADM_OPT_PERSIST;
 		}
 	}
-
+#endif
 	return (i_ipadm_plumb_if(iph, ifname, af, ipadm_flags));
 }
 
@@ -1345,7 +1572,136 @@
 	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.
  */
@@ -1448,10 +1804,23 @@
 
 	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'.
@@ -1560,4 +1929,81 @@
 {
 	(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);
 }
