--- old/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c	Sat Jul  5 09:39:26 2014
+++ new/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c	Sat Jul  5 09:39:26 2014
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
@@ -41,6 +42,9 @@
 #include <libnvpair.h>
 #include "ipmgmt_impl.h"
 
+
+static void ipmgmt_common_handler(char *, char *, db_wfunc_t *);
+
 /* Handler declaration for each door command */
 typedef void ipmgmt_door_handler_t(void *argp);
 
@@ -54,7 +58,8 @@
 				ipmgmt_resetif_handler,
 				ipmgmt_resetprop_handler,
 				ipmgmt_setaddr_handler,
-				ipmgmt_setprop_handler;
+				ipmgmt_setprop_handler,
+				ipmgmt_ipmp_update_handler;
 
 typedef struct ipmgmt_door_info_s {
 	uint_t			idi_cmd;
@@ -79,6 +84,7 @@
 	{ IPMGMT_CMD_ADDROBJ_ADD,	B_TRUE,  ipmgmt_aobjop_handler },
 	{ IPMGMT_CMD_AOBJNAME2ADDROBJ,	B_FALSE, ipmgmt_aobjop_handler },
 	{ IPMGMT_CMD_LIF2ADDROBJ,	B_FALSE, ipmgmt_aobjop_handler },
+	{ IPMGMT_CMD_IPMP_UPDATE,	B_FALSE, ipmgmt_ipmp_update_handler},
 	{ 0, 0, NULL },
 };
 
@@ -576,6 +582,10 @@
 
 	cbarg.cb_family = rargp->ia_family;
 	cbarg.cb_ifname = rargp->ia_ifname;
+
+	cbarg.cb_ipv4exists = B_TRUE;
+	cbarg.cb_ipv6exists = B_TRUE;
+
 	if (flags & IPMGMT_PERSIST)
 		err = ipmgmt_db_walk(ipmgmt_db_resetif, &cbarg,
 		    IPADM_DB_DELETE);
@@ -637,55 +647,10 @@
 static void
 ipmgmt_getaddr_handler(void *argp)
 {
-	size_t			buflen, onvlsize;
-	char			*buf, *onvlbuf;
-	ipmgmt_getaddr_arg_t	*gargp = argp;
-	ipmgmt_getaddr_cbarg_t	cbarg;
-	ipmgmt_get_rval_t 	rval, *rvalp = &rval;
-	int			err = 0;
+	ipmgmt_getaddr_arg_t    *gargp = argp;
 
-	cbarg.cb_ifname = gargp->ia_ifname;
-	cbarg.cb_aobjname = gargp->ia_aobjname;
-	cbarg.cb_ocnt = 0;
-	if (nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0) != 0)
-		goto fail;
-	err = ipmgmt_db_walk(ipmgmt_db_getaddr, &cbarg, IPADM_DB_READ);
-	if (err == ENOENT && cbarg.cb_ocnt > 0) {
-		/*
-		 * If there is atleast one entry in the nvlist,
-		 * do not return error.
-		 */
-		err = 0;
-	}
-	if (err != 0)
-		goto fail;
-
-	if ((err = nvlist_size(cbarg.cb_onvl, &onvlsize,
-	    NV_ENCODE_NATIVE)) != 0) {
-		goto fail;
-	}
-	buflen = onvlsize + sizeof (ipmgmt_get_rval_t);
-	/*
-	 * We cannot use malloc() here because door_return never returns, and
-	 * memory allocated by malloc() would get leaked. Use alloca() instead.
-	 */
-	buf = alloca(buflen);
-	onvlbuf = buf + sizeof (ipmgmt_get_rval_t);
-	if ((err = nvlist_pack(cbarg.cb_onvl, &onvlbuf, &onvlsize,
-	    NV_ENCODE_NATIVE, 0)) != 0) {
-		goto fail;
-	}
-	nvlist_free(cbarg.cb_onvl);
-	rvalp = (ipmgmt_get_rval_t *)(void *)buf;
-	rvalp->ir_err = 0;
-	rvalp->ir_nvlsize = onvlsize;
-
-	(void) door_return(buf, buflen, NULL, 0);
-	return;
-fail:
-	nvlist_free(cbarg.cb_onvl);
-	rvalp->ir_err = err;
-	(void) door_return((char *)rvalp, sizeof (*rvalp), NULL, 0);
+	ipmgmt_common_handler(gargp->ia_ifname, gargp->ia_aobjname,
+	    ipmgmt_db_getaddr);
 }
 
 /*
@@ -707,63 +672,18 @@
 
 /*
  * Handles the door command IPMGMT_CMD_GETIF. It retrieves the name of all the
- * persisted interfaces and the IP protocols (IPv4 or IPv6) they support.
+ * persisted interfaces and the IP protocols (IPv4 or IPv6) they support and
+ * returns the info as a nvlist
  */
 static void
 ipmgmt_getif_handler(void *argp)
 {
-	ipmgmt_getif_arg_t	*getif = argp;
-	ipmgmt_getif_rval_t	*rvalp;
-	ipmgmt_retval_t		rval;
-	ipmgmt_getif_cbarg_t	cbarg;
-	ipadm_if_info_t		*ifp, *rifp, *curifp;
-	int			i, err = 0, count = 0;
-	size_t			rbufsize;
+	ipmgmt_getif_arg_t  *getif = argp;
 
 	assert(getif->ia_cmd == IPMGMT_CMD_GETIF);
 
-	bzero(&cbarg, sizeof (cbarg));
-	cbarg.cb_ifname = getif->ia_ifname;
-	err = ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ);
-	if (err == ENOENT && cbarg.cb_ifinfo) {
-		/*
-		 * If there is atleast one entry in the nvlist,
-		 * do not return error.
-		 */
-		err = 0;
-	}
-	if (err != 0) {
-		rval.ir_err = err;
-		(void) door_return((char *)&rval, sizeof (rval), NULL, 0);
-		return;
-	}
-
-	/* allocate sufficient buffer to return the interface info */
-	for (ifp = cbarg.cb_ifinfo; ifp != NULL; ifp = ifp->ifi_next)
-		++count;
-	rbufsize = sizeof (*rvalp) + count * sizeof (*ifp);
-	rvalp = alloca(rbufsize);
-	bzero(rvalp, rbufsize);
-
-	rvalp->ir_ifcnt = count;
-	rifp = rvalp->ir_ifinfo;
-	ifp = cbarg.cb_ifinfo;
-
-	/*
-	 * copy the interface info to buffer allocated on stack. The reason
-	 * we do this is to avoid memory leak, as door_return() would never
-	 * return
-	 */
-	for (i = 0; i < count; i++) {
-		rifp = rvalp->ir_ifinfo + i;
-		(void) bcopy(ifp, rifp, sizeof (*rifp));
-		rifp->ifi_next = NULL;
-		curifp = ifp->ifi_next;
-		free(ifp);
-		ifp = curifp;
-	}
-	rvalp->ir_err = err;
-	(void) door_return((char *)rvalp, rbufsize, NULL, 0);
+	ipmgmt_common_handler(getif->ia_ifname, NULL,
+	    ipmgmt_db_getif);
 }
 
 /*
@@ -797,7 +717,7 @@
 	err = ipmgmt_db_walk(ipmgmt_db_initif, &cbarg, IPADM_DB_READ);
 	if (err == ENOENT && cbarg.cb_ocnt > 0) {
 		/*
-		 * If there is atleast one entry in the nvlist,
+		 * If there is at least one entry in the nvlist,
 		 * do not return error.
 		 */
 		err = 0;
@@ -837,10 +757,10 @@
 ipmgmt_persist_if(ipmgmt_if_arg_t *sargp)
 {
 	ipadm_dbwrite_cbarg_t	cb;
-	uint32_t		flags = sargp->ia_flags;
-	nvlist_t		*nvl = NULL;
-	int			err = 0;
-	char			strval[IPMGMT_STRSIZE];
+	uint32_t	flags = sargp->ia_flags;
+	nvlist_t	*nvl = NULL;
+	char	strval[IPMGMT_STRSIZE];
+	int	err = 0;
 
 	if (!(flags & IPMGMT_PERSIST) || sargp->ia_family == AF_UNSPEC ||
 	    sargp->ia_ifname[0] == '\0') {
@@ -849,16 +769,159 @@
 	}
 	if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0)
 		goto ret;
+
 	if ((err = nvlist_add_string(nvl, IPADM_NVP_IFNAME,
 	    sargp->ia_ifname)) != 0)
 		goto ret;
-	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", sargp->ia_family);
-	if ((err = nvlist_add_string(nvl, IPADM_NVP_FAMILY, strval)) != 0)
+
+	if ((err = ipmgmt_update_family_nvp(nvl, sargp->ia_family,
+	    IPMGMT_APPEND)) != 0)
 		goto ret;
+
+	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", sargp->ia_ifclass);
+	if ((err = nvlist_add_string(nvl, IPADM_NVP_IFCLASS, strval)) != 0)
+		goto ret;
+
 	cb.dbw_nvl = nvl;
-	cb.dbw_flags = 0;
-	err = ipmgmt_db_walk(ipmgmt_db_add, &cb, IPADM_DB_WRITE);
+	cb.dbw_flags = IPMGMT_APPEND | IPMGMT_UPDATE_IF;
+	err = ipmgmt_db_walk(ipmgmt_db_update_if, &cb, IPADM_DB_WRITE);
 ret:
 	nvlist_free(nvl);
 	return (err);
+}
+
+/*
+ * The helper for ipmgmt_getif_handler and ipmgmt_getaddr_handler
+ */
+static void
+ipmgmt_common_handler(char *if_name, char *aobj_name, db_wfunc_t worker)
+{
+	ipmgmt_get_rval_t   rval, *rvalp = &rval;
+	ipmgmt_get_cbarg_t    cbarg;
+	int	err = 0;
+	size_t	buflen, onvlsize;
+	char	*buf, *onvlbuf;
+
+	cbarg.cb_ifname = if_name;
+	cbarg.cb_aobjname = aobj_name;
+	cbarg.cb_ocnt = 0;
+
+	if (nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0) != 0)
+		goto fail;
+
+	err = ipmgmt_db_walk(worker, &cbarg, IPADM_DB_READ);
+	if (err == ENOENT && cbarg.cb_ocnt > 0) {
+		/*
+		 * If there is atleast one entry in the nvlist,
+		 * do not return error.
+		 */
+		err = 0;
+	}
+	if (err != 0)
+		goto fail;
+
+	if ((err = nvlist_size(cbarg.cb_onvl, &onvlsize,
+	    NV_ENCODE_NATIVE)) != 0)
+		goto fail;
+
+	buflen = onvlsize + sizeof (ipmgmt_get_rval_t);
+	/* We cannot use malloc() here because door_return never returns */
+	buf = alloca(buflen);
+	onvlbuf = buf + sizeof (ipmgmt_get_rval_t);
+	if ((err = nvlist_pack(cbarg.cb_onvl, &onvlbuf,
+	    &onvlsize, NV_ENCODE_NATIVE, 0)) != 0)
+		goto fail;
+
+	nvlist_free(cbarg.cb_onvl);
+	rvalp = (ipmgmt_get_rval_t *)(void *)buf;
+	rvalp->ir_err = 0;
+	rvalp->ir_nvlsize = onvlsize;
+
+	(void) door_return(buf, buflen, NULL, 0);
+	return;
+
+fail:
+	nvlist_free(cbarg.cb_onvl);
+	rvalp->ir_err = err;
+	(void) door_return((char *)rvalp, sizeof (*rvalp), NULL, 0);
+}
+
+/*
+ * Handles the door command IPMGMT_CMD_IPMP_UPDATE
+ */
+static void
+ipmgmt_ipmp_update_handler(void *argp)
+{
+	ipmgmt_ipmp_update_arg_t *uargp = argp;
+	ipmgmt_retval_t	rval;
+	ipadm_dbwrite_cbarg_t	cb;
+
+	boolean_t	gif_exists;
+	char		gifname[LIFNAMSIZ];
+	nvlist_t	*nvl = NULL;
+	uint32_t	flags = uargp->ia_flags;
+	int		err = 0;
+
+	assert(uargp->ia_cmd == IPMGMT_CMD_IPMP_UPDATE);
+
+	gif_exists = ipmgmt_persist_if_exists(uargp->ia_gifname,
+	    AF_UNSPEC);
+
+	if (!ipmgmt_persist_if_exists(uargp->ia_mifname, AF_UNSPEC)) {
+		err = EINVAL;
+		goto ret;
+	}
+
+	ipmgmt_get_group_interface(uargp->ia_mifname, gifname, LIFNAMSIZ);
+
+	if (flags & IPMGMT_APPEND) {
+		/* group interface should be available in the DB */
+		if (!gif_exists) {
+			err = ENOENT;
+			goto ret;
+		}
+
+		if (gifname[0] != '\0') {
+			err = EEXIST;
+			goto ret;
+		}
+	}
+
+	if (flags & IPMGMT_REMOVE) {
+		/* We cannot remove something that does not exist */
+		if (!gif_exists || gifname[0] == '\0') {
+			err = ENOENT;
+			goto ret;
+		}
+		if (strcmp(uargp->ia_gifname, gifname) != 0) {
+			err = EINVAL;
+			goto ret;
+		}
+	}
+
+	if (flags & IPMGMT_PERSIST) {
+
+		if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0)
+			goto ret;
+
+		if ((err = nvlist_add_string(nvl, IPADM_NVP_IFNAME,
+		    uargp->ia_gifname)) != 0)
+			goto ret;
+
+		if ((err = nvlist_add_string(nvl, IPADM_NVP_MIFNAMES,
+		    uargp->ia_mifname)) != 0)
+			goto ret;
+
+		if ((err = nvlist_add_string(nvl, IPADM_NVP_GIFNAME,
+		    uargp->ia_gifname)) != 0)
+			goto ret;
+
+		cb.dbw_nvl = nvl;
+		cb.dbw_flags = flags | IPMGMT_UPDATE_IF | IPMGMT_UPDATE_IPMP;
+		err = ipmgmt_db_walk(ipmgmt_db_update_if, &cb, IPADM_DB_WRITE);
+	}
+ret:
+	nvlist_free(nvl);
+	rval.ir_err = err;
+	(void) door_return((char *)&rval, sizeof (rval), NULL, 0);
 }
--- old/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h	Sat Jul  5 09:39:26 2014
+++ new/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h	Sat Jul  5 09:39:26 2014
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
  */
 
 #ifndef	_IPMGMT_IMPL_H
@@ -49,6 +50,8 @@
 extern int	ipmgmt_cpfile(const char *, const char *, boolean_t);
 
 /* ipmgmt_persist.c */
+extern boolean_t ipmgmt_persist_if_exists(const char *, sa_family_t);
+extern void ipmgmt_get_group_interface(const char *, char *, size_t);
 
 /*
  * following are the list of DB walker callback functions and the callback
@@ -58,25 +61,22 @@
 extern db_wfunc_t	ipmgmt_db_getprop, ipmgmt_db_resetprop;
 
 /* following functions take ipadm_dbwrite_cbarg_t as callback argument */
-extern db_wfunc_t	ipmgmt_db_add, ipmgmt_db_update;
+extern db_wfunc_t	ipmgmt_db_add, ipmgmt_db_update, ipmgmt_db_update_if;
 
 typedef struct {
-	char		*cb_ifname;
-	ipadm_if_info_t	*cb_ifinfo;
-} ipmgmt_getif_cbarg_t;
-extern db_wfunc_t	ipmgmt_db_getif;
-
-typedef struct {
-	char		*cb_aobjname;
-	char		*cb_ifname;
+	char	*cb_ifname;
+	char	*cb_aobjname;
 	nvlist_t	*cb_onvl;
-	int		cb_ocnt;
-} ipmgmt_getaddr_cbarg_t;
+	int	cb_ocnt;
+} ipmgmt_get_cbarg_t;
+extern db_wfunc_t	ipmgmt_db_getif;
 extern db_wfunc_t	ipmgmt_db_getaddr;
 
 typedef struct {
 	sa_family_t	cb_family;
 	char		*cb_ifname;
+    boolean_t   cb_ipv4exists;
+    boolean_t   cb_ipv6exists;
 } ipmgmt_if_cbarg_t;
 extern db_wfunc_t	ipmgmt_db_setif, ipmgmt_db_resetif;
 
@@ -171,6 +171,8 @@
 	scf_transaction_entry_t	*sr_ent;
 } scf_resources_t;
 
+extern int		ipmgmt_update_family_nvp(nvlist_t *,
+			    sa_family_t, uint_t);
 extern int		ipmgmt_db_walk(db_wfunc_t *, void *, ipadm_db_op_t);
 extern int		ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *, uint32_t);
 extern boolean_t	ipmgmt_aobjmap_init(void *, nvlist_t *, char *,
--- old/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c	Sat Jul  5 09:39:26 2014
+++ new/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c	Sat Jul  5 09:39:26 2014
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
@@ -520,30 +521,6 @@
 	return (EXIT_FAILURE);
 }
 
-/*
- * Return TRUE if `ifname' has persistent configuration for the `af' address
- * family in the datastore
- */
-static boolean_t
-ipmgmt_persist_if_exists(char *ifname, sa_family_t af)
-{
-	ipmgmt_getif_cbarg_t cbarg;
-	boolean_t exists = B_FALSE;
-	ipadm_if_info_t *ifp;
-
-	bzero(&cbarg, sizeof (cbarg));
-	cbarg.cb_ifname = ifname;
-	(void) ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ);
-	if ((ifp = cbarg.cb_ifinfo) != NULL) {
-		if ((af == AF_INET && (ifp->ifi_pflags & IFIF_IPV4)) ||
-		    (af == AF_INET6 && (ifp->ifi_pflags & IFIF_IPV6))) {
-			exists = B_TRUE;
-		}
-	}
-	free(ifp);
-	return (exists);
-}
-
 /*
  * Persist any NGZ interfaces assigned to us from the global zone if they do
  * not already exist in the persistent db. We need to
--- old/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c	Sat Jul  5 09:39:26 2014
+++ new/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c	Sat Jul  5 09:39:26 2014
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
@@ -77,6 +78,151 @@
 /* signifies whether volatile copy of data store is in use */
 static boolean_t ipmgmt_rdonly_root = B_FALSE;
 
+typedef int ipmgmt_if_updater_func_t(nvlist_t *, nvpair_t *, uint_t);
+
+static ipmgmt_if_updater_func_t ipmgmt_if_family_updater;
+static ipmgmt_if_updater_func_t ipmgmt_if_groupmembers_updater;
+
+static int ipmgmt_get_ifinfo_nvl(const char *ifname, nvlist_t **if_info_nvl);
+
+typedef struct {
+	const char	*name;
+	ipmgmt_if_updater_func_t	*func;
+} ipmgmt_if_updater_ent_t;
+
+static ipmgmt_if_updater_ent_t ipmgmt_if_updater_ent[] = {
+	{IPADM_NVP_FAMILIES, ipmgmt_if_family_updater},
+	{IPADM_NVP_MIFNAMES, ipmgmt_if_groupmembers_updater},
+	{NULL, NULL}
+};
+
+static ipmgmt_if_updater_ent_t *
+ipmgmt_find_if_field_updater(const char *field_name)
+{
+	int i;
+
+	for (i = 0; ipmgmt_if_updater_ent[i].name != NULL; i++) {
+		if (strcmp(field_name, ipmgmt_if_updater_ent[i].name) == 0) {
+			break;
+		}
+	}
+
+	return (&ipmgmt_if_updater_ent[i]);
+}
+
+static int
+ipmgmt_if_groupmembers_updater(nvlist_t *db_nvl, nvpair_t *member_nvp,
+	uint_t flags)
+{
+	char	**members;
+	char	*member;
+	char	*out_memebers[256];
+	uint_t  nelem = 0, cnt = 0;
+	int	err;
+
+	if ((err = nvpair_value_string(member_nvp, &member)) != 0)
+		return (err);
+
+	err = nvlist_lookup_string_array(db_nvl, IPADM_NVP_MIFNAMES,
+	    &members, &nelem);
+
+	if (err != 0 && (flags & IPMGMT_REMOVE))
+		return (ENOENT);
+
+	while (nelem--) {
+		if ((flags & IPMGMT_REMOVE) &&
+		    (strcmp(member, members[nelem]) == 0))
+			continue;
+
+		if ((out_memebers[cnt] = strdup(members[nelem])) == NULL) {
+			err = ENOMEM;
+			goto fail;
+		}
+
+		cnt++;
+	}
+
+	if (flags & IPMGMT_APPEND) {
+		if ((out_memebers[cnt] = strdup(member)) == NULL) {
+			err = ENOMEM;
+			goto fail;
+		}
+		cnt++;
+	}
+
+	if (cnt == 0) {
+		err = nvlist_remove(db_nvl, IPADM_NVP_MIFNAMES,
+		    DATA_TYPE_STRING_ARRAY);
+	} else {
+		err = nvlist_add_string_array(db_nvl, IPADM_NVP_MIFNAMES,
+		    out_memebers, cnt);
+	}
+
+fail:
+	while (cnt--)
+		free(out_memebers[cnt]);
+
+	return (err);
+}
+
+static int
+ipmgmt_if_family_updater(nvlist_t *db_nvl, nvpair_t *families_nvp, uint_t flags)
+{
+	uint16_t *families;
+	uint_t  nelem = 0;
+	int	err;
+
+	if ((err = nvpair_value_uint16_array(families_nvp, &families,
+	    &nelem)) != 0)
+		return (err);
+
+	return (ipmgmt_update_family_nvp(db_nvl, families[0], flags));
+}
+
+int
+ipmgmt_update_family_nvp(nvlist_t *nvl, sa_family_t af, uint_t flags)
+{
+	uint16_t	*families = NULL;
+	uint16_t	out_families[2];
+	uint_t	nelem = 0, cnt;
+	int	err;
+
+	err = nvlist_lookup_uint16_array(nvl, IPADM_NVP_FAMILIES,
+	    &families, &nelem);
+	if (err != 0 && (flags & IPMGMT_REMOVE)) {
+		return (ENOENT);
+	}
+
+	if (flags & IPMGMT_APPEND) {
+		if (families != NULL) {
+			if (nelem == 2 || families[0] == af) {
+				return (EEXIST);
+			}
+			out_families[0] = families[0];
+			out_families[1] = af;
+			cnt = 2;
+		} else {
+			out_families[0] = af;
+			cnt = 1;
+		}
+	} else {
+		assert(nelem == 1 || nelem == 2);
+		cnt = 0;
+		while (nelem--) {
+			if (families[nelem] != af) {
+				out_families[cnt] = families[nelem];
+				cnt++;
+			}
+		}
+	}
+
+	if (cnt != 0) {
+		return (nvlist_add_uint16_array(nvl, IPADM_NVP_FAMILIES,
+		    out_families, cnt));
+	}
+	return (nvlist_remove(nvl, IPADM_NVP_FAMILIES, DATA_TYPE_UINT16_ARRAY));
+}
+
 /*
  * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed
  * in private nvpairs `proto', `ifname' & `aobjname'.
@@ -322,7 +468,7 @@
 ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
     int *errp)
 {
-	ipmgmt_getaddr_cbarg_t	*cbarg = arg;
+	ipmgmt_get_cbarg_t	*cbarg = arg;
 	char		*db_aobjname = NULL;
 	char		*db_ifname = NULL;
 	nvlist_t	*db_addr = NULL;
@@ -548,55 +694,135 @@
 }
 
 /*
- * For the given `cbarg->cb_ifname' interface, retrieves any persistent
- * interface information (used in 'ipadm show-if')
+ * This function is used to update a DB line that describes
+ * an interface, its family and group interface
+ *
  */
-/* ARGSUSED */
 boolean_t
-ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
-    int *errp)
+ipmgmt_db_update_if(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
+		int *errp)
 {
-	ipmgmt_getif_cbarg_t	*cbarg = arg;
-	char			*ifname = cbarg->cb_ifname;
-	char			*intf = NULL;
-	ipadm_if_info_t		*ifp = NULL;
-	sa_family_t		af;
-	char			*afstr;
+	ipadm_dbwrite_cbarg_t	*cb = arg;
+	nvlist_t	*in_nvl = cb->dbw_nvl;
+	uint_t	flags = cb->dbw_flags;
+	nvpair_t	*nvp;
+	char	*name;
+	ipmgmt_if_updater_ent_t *updater;
+	char	*member = NULL;
+	char	*gifname = NULL;
+	char	*db_line_if_name;
 
+
 	*errp = 0;
-	if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) != 0 ||
-	    nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &intf) != 0 ||
-	    (ifname[0] != '\0' && strcmp(ifname, intf) != 0)) {
+
+	/* Only one flag */
+	if ((flags & (IPMGMT_APPEND | IPMGMT_REMOVE)) == 0 ||
+	    ((flags & IPMGMT_APPEND) && (flags & IPMGMT_REMOVE))) {
+		*errp = EINVAL;
+		return (B_FALSE);
+	}
+
+	if (!nvlist_exists(db_nvl, IPADM_NVP_FAMILIES))
 		return (B_TRUE);
+
+	if (nvlist_lookup_string(db_nvl,
+	    IPADM_NVP_IFNAME, &db_line_if_name) == 0 &&
+	    nvlist_lookup_string(in_nvl, IPADM_NVP_GIFNAME, &gifname) == 0 &&
+	    nvlist_lookup_string(in_nvl, IPADM_NVP_MIFNAMES, &member) == 0 &&
+	    strcmp(db_line_if_name, member) == 0) {
+		if (flags & IPMGMT_APPEND) {
+			if ((*errp = nvlist_add_string(db_nvl,
+			    IPADM_NVP_GIFNAME, gifname)) != 0)
+				return (B_FALSE);
+		} else {
+			if ((*errp = nvlist_remove(db_nvl, IPADM_NVP_GIFNAME,
+			    DATA_TYPE_STRING)) != 0)
+				return (B_FALSE);
+		}
+		cb->dbw_flags &= ~IPMGMT_UPDATE_IPMP;
+		goto done;
 	}
-	af = atoi(afstr);
-	for (ifp = cbarg->cb_ifinfo; ifp != NULL; ifp = ifp->ifi_next) {
-		if (strcmp(ifp->ifi_name, intf) == 0)
-			break;
+
+	if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
+		return (B_TRUE);
+
+
+	for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
+	    nvp = nvlist_next_nvpair(in_nvl, nvp)) {
+
+		name = nvpair_name(nvp);
+		if (strcmp(name, IPADM_NVP_FAMILIES) != 0 &&
+		    strcmp(name, IPADM_NVP_MIFNAMES) != 0)
+			continue;
+
+		updater = ipmgmt_find_if_field_updater(name);
+		assert(updater != NULL);
+		*errp = (*updater->func)(db_nvl, nvp, flags);
+		if (*errp != 0)
+			return (B_FALSE);
 	}
-	if (ifp == NULL) {
-		ipadm_if_info_t *new;
 
-		if ((new = calloc(1, sizeof (*new))) == NULL) {
-			*errp = ENOMEM;
-			return (B_FALSE); /* don't continue the walk */
-		}
-		new->ifi_next = cbarg->cb_ifinfo;
-		cbarg->cb_ifinfo = new;
-		ifp = new;
-		(void) strlcpy(ifp->ifi_name, intf, sizeof (ifp->ifi_name));
+	cb->dbw_flags &= ~IPMGMT_UPDATE_IF;
+
+done:
+	(void) memset(buf, 0, buflen);
+	if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
+		*errp = ENOBUFS;
+		return (B_FALSE);
 	}
 
-	if (af == AF_INET) {
-		ifp->ifi_pflags |= IFIF_IPV4;
-	} else {
-		assert(af == AF_INET6);
-		ifp->ifi_pflags |= IFIF_IPV6;
+	/* we finished all operations ???, so do not continue */
+	if ((cb->dbw_flags & (IPMGMT_UPDATE_IF | IPMGMT_UPDATE_IPMP)) == 0)
+		return (B_FALSE);
+
+	return (B_TRUE);
+}
+
+/*
+ * For the given `cbarg->cb_ifname' interface retrieves
+ * the nvlist that represents the persistent interface information
+ * The nvlist contains:
+ *      IPADM_NVP_IFNAME
+ *      IPADM_NVP_FAMILIES
+ *      IPADM_NVP_IF_CLASS
+ *
+ * (used in 'ipadm show-if')
+ */
+/* ARGSUSED */
+boolean_t
+ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
+    int *errp)
+{
+	ipmgmt_get_cbarg_t	*cbarg = arg;
+	char	*ifname = cbarg->cb_ifname;
+	nvpair_t	*nvp;
+	char	*db_ifname = NULL;
+	uint16_t	*db_families = NULL;
+	uint_t	nelem = 0;
+
+	/* Parse db nvlist */
+	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
+	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
+
+		if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0)
+			(void) nvpair_value_string(nvp, &db_ifname);
+		else if (strcmp(nvpair_name(nvp), IPADM_NVP_FAMILIES) == 0)
+			(void) nvpair_value_uint16_array(nvp,
+			    &db_families, &nelem);
 	}
 
-	/* Terminate the walk if we found both v4 and v6 interfaces. */
-	if (ifname[0] != '\0' && (ifp->ifi_pflags & IFIF_IPV4) &&
-	    (ifp->ifi_pflags & IFIF_IPV6))
+	if (db_ifname == NULL || db_families == NULL)
+		return (B_TRUE);
+
+	if (ifname != NULL && ifname[0] != '\0' &&
+	    strcmp(ifname, db_ifname) != 0)
+		return (B_TRUE);
+
+	*errp = nvlist_add_nvlist(cbarg->cb_onvl, db_ifname, db_nvl);
+	if (*errp == 0)
+		cbarg->cb_ocnt++;
+
+	if (ifname != NULL && ifname[0] != '\0')
 		return (B_FALSE);
 
 	return (B_TRUE);
@@ -615,7 +841,6 @@
 	boolean_t	isv6 = (cbarg->cb_family == AF_INET6);
 	char		*ifname = cbarg->cb_ifname;
 	char		*modstr = NULL;
-	char		*afstr;
 	char		*aobjname;
 	uint_t		proto;
 	ipmgmt_aobjmap_t *head;
@@ -626,9 +851,31 @@
 	if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL))
 		return (B_TRUE);
 
-	if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) == 0) {
-		if (atoi(afstr) == cbarg->cb_family)
+	if (nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) {
+
+		if ((*errp = ipmgmt_update_family_nvp(db_nvl, cbarg->cb_family,
+		    IPMGMT_REMOVE)) != 0) {
+			return (B_FALSE);
+		}
+
+		if (cbarg->cb_family == AF_INET) {
+			cbarg->cb_ipv4exists = B_FALSE;
+		} else {
+			assert(cbarg->cb_family == AF_INET6);
+			cbarg->cb_ipv6exists = B_FALSE;
+		}
+		if (!nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) {
+			cbarg->cb_ipv4exists = B_FALSE;
+			cbarg->cb_ipv6exists = B_FALSE;
 			goto delete;
+		}
+		/* Otherwise need to reconstruct this string */
+		(void) memset(buf, 0, buflen);
+		if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
+			/* buffer overflow */
+			*errp = ENOBUFS;
+			return (B_FALSE);
+		}
 		return (B_TRUE);
 	}
 
@@ -684,8 +931,8 @@
 				goto delete;
 			break;
 		case MOD_PROTO_IP:
-			/* this should never be the case, today */
-			assert(0);
+		if (!cbarg->cb_ipv4exists && !cbarg->cb_ipv6exists)
+		goto delete;
 			break;
 		}
 	}
@@ -1685,4 +1932,110 @@
 
 	(void) ipmgmt_set_scfprop(res, IPMGMTD_APP_PG,
 	    IPMGMTD_PROP_DBVER, &version, SCF_TYPE_INTEGER);
+}
+
+/*
+ * Return TRUE if `ifname' has persistent configuration for the `af' address
+ * family in the datastore.
+ * It is possible to call the function with af == AF_UNSPEC, so in this case
+ * the function returns TRUE if either AF_INET or AF_INET6 interface exists
+ */
+boolean_t
+ipmgmt_persist_if_exists(const char *ifname, sa_family_t af)
+{
+	boolean_t exists = B_FALSE;
+	nvlist_t    *if_info_nvl;
+	uint16_t    *families = NULL;
+	sa_family_t af_db;
+	uint_t	nelem = 0;
+
+	if (ipmgmt_get_ifinfo_nvl(ifname, &if_info_nvl) != 0)
+		goto done;
+
+	if (nvlist_lookup_uint16_array(if_info_nvl, IPADM_NVP_FAMILIES,
+	    &families, &nelem) != 0)
+		goto done;
+
+	while (nelem--) {
+		af_db = families[nelem];
+		if (af_db == af || (af == AF_UNSPEC &&
+		    (af_db == AF_INET || af_db == AF_INET6))) {
+			exists = B_TRUE;
+			break;
+		}
+	}
+
+done:
+	if (if_info_nvl != NULL)
+		nvlist_free(if_info_nvl);
+
+	return (exists);
+}
+
+/*
+ * Retrieves the membership information for the requested mif_name
+ * if mif_name is a memeber of a IPMP group, then gif_name will contain
+ * the name of IPMP group interface, otherwise the variable will be empty
+ */
+void
+ipmgmt_get_group_interface(const char *mif_name, char *gif_name, size_t size)
+{
+	char	*gif_name_from_nvl;
+	nvlist_t	*if_info_nvl;
+
+	gif_name[0] = '\0';
+
+	if (ipmgmt_get_ifinfo_nvl(mif_name, &if_info_nvl) != 0)
+		goto done;
+
+	if (nvlist_lookup_string(if_info_nvl, IPADM_NVP_GIFNAME,
+	    &gif_name_from_nvl) != 0)
+		goto done;
+
+	(void) strlcpy(gif_name, gif_name_from_nvl, size);
+
+done:
+	if (if_info_nvl != NULL)
+		nvlist_free(if_info_nvl);
+}
+
+static int
+ipmgmt_get_ifinfo_nvl(const char *ifname, nvlist_t **if_info_nvl)
+{
+	ipmgmt_get_cbarg_t cbarg;
+	nvpair_t    *nvp;
+	nvlist_t    *nvl;
+	int	err;
+
+	cbarg.cb_ifname = NULL;
+	cbarg.cb_aobjname = NULL;
+	cbarg.cb_ocnt = 0;
+
+	if ((err = nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0)) != 0)
+		goto done;
+
+	err = ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ);
+	if (err == ENOENT && cbarg.cb_ocnt > 0)
+		err = 0;
+
+	if (err != 0)
+		goto done;
+
+	for (nvp = nvlist_next_nvpair(cbarg.cb_onvl, NULL); nvp != NULL;
+	    nvp = nvlist_next_nvpair(cbarg.cb_onvl, nvp)) {
+
+		if (strcmp(nvpair_name(nvp), ifname) != 0)
+			continue;
+
+		if ((err = nvpair_value_nvlist(nvp, &nvl)) != 0 ||
+		    (err = nvlist_dup(nvl, if_info_nvl, NV_UNIQUE_NAME)) != 0)
+			*if_info_nvl = NULL;
+
+		break;
+	}
+
+done:
+	nvlist_free(cbarg.cb_onvl);
+
+	return (err);
 }
--- old/usr/src/cmd/cmd-inet/usr.sbin/ipadm/Makefile	Sat Jul  5 09:39:27 2014
+++ new/usr/src/cmd/cmd-inet/usr.sbin/ipadm/Makefile	Sat Jul  5 09:39:27 2014
@@ -22,12 +22,14 @@
 # Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
 #
+#
 
 PROG =		ipadm
 ROOTFS_PROG =	$(PROG)
 LOCALOBJS=	ipadm.o
-COMMONOBJS=	
+COMMONOBJS=
 OBJS=		$(LOCALOBJS) $(COMMONOBJS)
 
 include ../../../Makefile.cmd
@@ -41,7 +43,7 @@
 CPPFLAGS +=	-I$(CMDINETCOMMONDIR)
 CERRWARN +=	-_gcc=-Wno-unused-label
 CERRWARN +=	-_gcc=-Wno-uninitialized
-LDLIBS +=       -linetutil -lipadm -lnvpair
+LDLIBS +=       -linetutil -lipadm -lnvpair -lipmp -lcmdutils
 LINTFLAGS +=	-m
 
 ROOTUSRSBINLINKS = $(PROG:%=$(ROOTUSRSBIN)/%)
--- old/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c	Sat Jul  5 09:39:27 2014
+++ new/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c	Sat Jul  5 09:39:27 2014
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
  */
 #include <arpa/inet.h>
 #include <errno.h>
@@ -33,6 +33,8 @@
 #include <libdllink.h>
 #include <libinetutil.h>
 #include <libipadm.h>
+#include <ipmp.h>
+#include <ipmp_admin.h>
 #include <locale.h>
 #include <netdb.h>
 #include <netinet/in.h>
@@ -46,12 +48,17 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <zone.h>
+#include <sys/list.h>
+#include <stddef.h>
 
 #define	STR_UNKNOWN_VAL	"?"
 #define	LIFC_DEFAULT	(LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES |\
 			LIFC_UNDER_IPMP)
 
+static void do_create_if_common(int, char **, const char *, uint32_t);
+
 typedef void cmdfunc_t(int, char **, const char *);
+static cmdfunc_t do_create_ipmp, do_add_ipmp, do_remove_ipmp;
 static cmdfunc_t do_create_if, do_delete_if, do_enable_if, do_disable_if;
 static cmdfunc_t do_show_if;
 static cmdfunc_t do_set_prop, do_show_prop, do_set_ifprop;
@@ -69,6 +76,14 @@
 
 static cmd_t	cmds[] = {
 	/* interface management related sub-commands */
+	{ "create-ipmp", do_create_ipmp, "\tcreate-ipmp\t[-t] <ipmp-group>"},
+	{ "delete-ipmp", do_delete_if, "\tdelete-ipmp\t[-t] <ipmp-group>"},
+	{ "add-ipmp", do_add_ipmp, "\tadd-ipmp\t[-t] -i"
+	    " <ipmp-member-interface> "
+	    "[-i <ipmp-member-interface>] <ipmp-group-interface>"},
+	{ "remove-ipmp", do_remove_ipmp, "\tremove-ipmp\t[-t] -i"
+	    " <ipmp-member-interface> "
+	    "[-i <ipmp-member-interface>] <ipmp-group-interface>"},
 	{ "create-if",	do_create_if,	"\tcreate-if\t[-t] <interface>"	},
 	{ "disable-if",	do_disable_if,	"\tdisable-if\t-t <interface>"	},
 	{ "enable-if",	do_enable_if,	"\tenable-if\t-t <interface>"	},
@@ -284,6 +299,7 @@
 
 typedef enum {
 	SI_IFNAME,
+	SI_IFCLASS,
 	SI_STATE,
 	SI_CURRENT,
 	SI_PERSISTENT
@@ -303,6 +319,7 @@
 static ofmt_field_t show_if_fields[] = {
 /* name,	field width,	id,		callback */
 { "IFNAME",	11,		SI_IFNAME,	print_si_cb},
+{ "CLASS",	10,		SI_IFCLASS,	print_si_cb},
 { "STATE",	9,		SI_STATE,	print_si_cb},
 { "CURRENT",	13,		SI_CURRENT,	print_si_cb},
 { "PERSISTENT",	11,		SI_PERSISTENT,	print_si_cb},
@@ -316,6 +333,11 @@
 	uint64_t	mask;
 } fmask_t;
 
+typedef enum {
+    IPMP_ADD_MEMBER,
+    IPMP_REMOVE_MEMBER
+} ipmp_action_t;
+
 /*
  * Handle to libipadm. Opened in main() before the sub-command specific
  * function is called and is closed before the program exits.
@@ -336,6 +358,7 @@
 static void 	ipadm_check_propstr(const char *, boolean_t, const char *);
 static void 	process_misc_addrargs(int, char **, const char *, int *,
 		    uint32_t *);
+static void	do_action_ipmp(int, char **, const char *, ipmp_action_t);
 
 static void
 usage(void)
@@ -398,19 +421,17 @@
 }
 
 /*
- * Create an IP interface for which no saved configuration exists in the
- * persistent store.
+ * Create regular IP interface or IPMP group interface
  */
 static void
-do_create_if(int argc, char *argv[], const char *use)
+do_create_if_common(int argc, char *argv[], const char *use, uint32_t flags)
 {
 	ipadm_status_t	status;
 	int		option;
-	uint32_t	flags = IPADM_OPT_PERSIST|IPADM_OPT_ACTIVE;
 
 	opterr = 0;
-	while ((option = getopt_long(argc, argv, ":t", if_longopts,
-	    NULL)) != -1) {
+	while ((option = getopt_long(argc, argv,
+	    ":t", if_longopts, NULL)) != -1) {
 		switch (option) {
 		case 't':
 			/*
@@ -433,6 +454,124 @@
 }
 
 /*
+ * Create an IPMP group interface for which no saved configuration
+ * exists in the persistent store.
+ */
+static void
+do_create_ipmp(int argc, char *argv[], const char *use)
+{
+	ipmp_handle_t ipmp_handle;
+	int retval;
+	uint32_t flags = IPADM_OPT_PERSIST | IPADM_OPT_ACTIVE | IPADM_OPT_IPMP;
+
+	retval = ipmp_open(&ipmp_handle);
+	if (retval != IPMP_SUCCESS) {
+		die("Could not create IPMP handle: %s",
+		    ipadm_status2str(retval));
+	}
+
+	retval = ipmp_ping_daemon(ipmp_handle);
+	ipmp_close(ipmp_handle);
+
+	if (retval != IPMP_SUCCESS) {
+		die("Cannot ping in.mpathd: %s", ipmp_errmsg(retval));
+	}
+
+	do_create_if_common(argc, argv, use, flags);
+}
+
+static void
+do_add_ipmp(int argc, char *argv[], const char *use)
+{
+	do_action_ipmp(argc, argv, use, IPMP_ADD_MEMBER);
+}
+
+static void
+do_remove_ipmp(int argc, char *argv[], const char *use)
+{
+	do_action_ipmp(argc, argv, use, IPMP_REMOVE_MEMBER);
+}
+
+static void
+do_action_ipmp(int argc, char *argv[], const char *use,
+	ipmp_action_t action)
+{
+	int	option;
+	ipadm_status_t  status;
+	ipadm_ipmp_members_t members;
+	ipadm_ipmp_member_t *ipmp_member;
+	uint32_t flags = IPADM_OPT_PERSIST | IPADM_OPT_ACTIVE;
+
+	list_create(&members, sizeof (ipadm_ipmp_member_t),
+	    offsetof(ipadm_ipmp_member_t, node));
+
+	opterr = 0;
+	while ((option = getopt_long(argc, argv,
+	    ":ti:", if_longopts, NULL)) != -1) {
+		switch (option) {
+		case 't':
+			flags &= ~IPADM_OPT_PERSIST;
+			break;
+		case 'i':
+			if ((ipmp_member = calloc(1,
+			    sizeof (ipadm_ipmp_member_t))) == NULL)
+				die("insufficient memory");
+
+			if (strlcpy(ipmp_member->if_name,
+			    optarg, sizeof (ipmp_member->if_name)) >= LIFNAMSIZ)
+				die("Incorrect length of interface"
+				    "name: %s", optarg);
+
+			list_insert_tail(&members, ipmp_member);
+			break;
+		default:
+			die_opterr(optopt, option, use);
+		}
+	}
+
+	if (optind != (argc - 1))
+		die("Usage: %s", use);
+
+	while ((ipmp_member = list_remove_head(&members)) != NULL) {
+		switch (action) {
+			case IPMP_ADD_MEMBER:
+				if ((status = ipadm_add_ipmp_member(iph,
+				    argv[optind], ipmp_member->if_name, flags))
+				    != IPADM_SUCCESS)
+					die("Cannot add '%s' interface to"
+					    "'%s': %s",
+					    ipmp_member->if_name, argv[optind],
+					    ipadm_status2str(status));
+				break;
+			case IPMP_REMOVE_MEMBER:
+				if ((status = ipadm_remove_ipmp_member(iph,
+				    argv[optind], ipmp_member->if_name, flags))
+				    != IPADM_SUCCESS)
+					die("Cannot remove '%s' interface from "
+					    "'%s': %s",
+					    ipmp_member->if_name, argv[optind],
+					    ipadm_status2str(status));
+				break;
+		}
+
+		free(ipmp_member);
+	}
+
+	list_destroy(&members);
+}
+
+/*
+ * Create an IP interface for which no saved configuration exists in the
+ * persistent store.
+ */
+static void
+do_create_if(int argc, char *argv[], const char *use)
+{
+	do_create_if_common(argc, argv, use,
+	    IPADM_OPT_PERSIST | IPADM_OPT_ACTIVE);
+}
+
+/*
  * Enable an IP interface based on the persistent configuration for
  * that interface.
  */
@@ -1908,6 +2047,13 @@
 		{ "6",	IFIF_IPV6,		IFIF_IPV6	},
 		{ NULL,	0,			0		}
 	};
+	fmask_t intf_class[] = {
+		{ "IP",		IPADM_IF_CLASS_REGULAR, IPADM_ALL_BITS},
+		{ "IPMP",	IPADM_IF_CLASS_IPMP,    IPADM_ALL_BITS},
+		{ "VIRTUAL",	IPADM_IF_CLASS_VIRTUAL, IPADM_ALL_BITS},
+		{ "UNKNOWN",	IPADM_IF_CLASS_UNKNOWN, IPADM_ALL_BITS},
+		{ NULL,	0,	0}
+	};
 
 	buf[0] = '\0';
 	switch (ofarg->ofmt_id) {
@@ -1914,6 +2060,10 @@
 	case SI_IFNAME:
 		(void) snprintf(buf, bufsize, "%s", ifname);
 		break;
+	case SI_IFCLASS:
+		flags2str(ifinfo->ifi_class, intf_class, _B_FALSE,
+		    buf, bufsize);
+		break;
 	case SI_STATE:
 		flags2str(ifinfo->ifi_state, intf_state, _B_FALSE,
 		    buf, bufsize);
--- old/usr/src/lib/libipadm/Makefile.com	Sat Jul  5 09:39:27 2014
+++ new/usr/src/lib/libipadm/Makefile.com	Sat Jul  5 09:39:27 2014
@@ -34,8 +34,8 @@
 include ../../Makefile.rootfs
 
 LIBS =		$(DYNLIB) $(LINTLIB)
-LDLIBS +=	-lc -lnsl -linetutil -lsocket -ldlpi -lnvpair -ldhcpagent \
-	        -ldladm -lsecdb
+LDLIBS +=	-lnsl -linetutil -lsocket -ldlpi -lnvpair -ldhcpagent \
+	        -ldladm -lsecdb -lipmp -lcmdutils -lc
 
 SRCDIR =	../common
 $(LINTLIB) :=	SRCS = $(SRCDIR)/$(LINTSRC)
--- old/usr/src/lib/libipadm/common/ipadm_addr.c	Sat Jul  5 09:39:27 2014
+++ new/usr/src/lib/libipadm/common/ipadm_addr.c	Sat Jul  5 09:39:27 2014
@@ -230,8 +230,9 @@
 	 * Get the address line in the nvlist `onvl' from ipmgmtd daemon.
 	 */
 	status = i_ipadm_get_db_addr(iph, NULL, aobjname, &onvl);
-	if (status != IPADM_SUCCESS)
+	if (status != IPADM_SUCCESS) {
 		return (status);
+	}
 	/*
 	 * Walk through the nvlist `onvl' to extract the IPADM_NVP_IPV4ADDR
 	 * or the IPADM_NVP_IPV6ADDR name-value pair.
@@ -259,9 +260,11 @@
 	}
 	assert(af != AF_UNSPEC);
 	if (nvpair_value_nvlist(nvp, &nvladdr) != 0 ||
-	    nvlist_lookup_string(nvladdr, IPADM_NVP_IPADDRHNAME, &sname) != 0 ||
-	    ipadm_set_addr(ipaddr, sname, af) != IPADM_SUCCESS) {
+	    nvlist_lookup_string(nvladdr, IPADM_NVP_IPADDRHNAME, &sname) != 0)
 		goto fail;
+
+	if (ipadm_set_addr(ipaddr, sname, af) != IPADM_SUCCESS) {
+		goto fail;
 	}
 	nvlist_free(onvl);
 	return (IPADM_SUCCESS);
@@ -1513,7 +1516,6 @@
 	if ((pflags & IPADM_OPT_PERSIST) &&
 	    !(ipaddr.ipadm_flags & IPMGMT_PERSIST))
 		return (IPADM_TEMPORARY_OBJ);
-
 	/*
 	 * Currently, setting an address property on an address object of type
 	 * IPADM_ADDR_IPV6_ADDRCONF is not supported. Supporting it involves
@@ -2032,8 +2034,8 @@
 	} else {
 		status = ipadm_set_addr(&ipaddr, sname, af);
 	}
-	if (status != IPADM_SUCCESS)
-		return (status);
+//	if (status != IPADM_SUCCESS)
+//		return (status);
 
 	if (dname != NULL) {
 		status = ipadm_set_dst_addr(&ipaddr, dname, af);
@@ -2178,13 +2180,13 @@
 		*aname++ = '\0';
 
 	/* Check if the interface name is valid. */
-	if (!ifparse_ifspec(ifname, &ifsp))
+	if (!ifparse_ifspec(ifname, &ifsp)) {
 		return (IPADM_INVALID_ARG);
-
+	}
 	/* Check if the given addrobj name is valid. */
-	if (aname != NULL && !i_ipadm_is_user_aobjname_valid(aname))
+	if (aname != NULL && !i_ipadm_is_user_aobjname_valid(aname)) {
 		return (IPADM_INVALID_ARG);
-
+	}
 	if ((newaddr = calloc(1, sizeof (struct ipadm_addrobj_s))) == NULL)
 		return (IPADM_NO_MEMORY);
 
@@ -2366,10 +2368,6 @@
     const char *aobjname, nvlist_t **onvl)
 {
 	ipmgmt_getaddr_arg_t	garg;
-	ipmgmt_get_rval_t	*rvalp;
-	int			err;
-	size_t			nvlsize;
-	char			*nvlbuf;
 
 	/* Populate the door_call argument structure */
 	bzero(&garg, sizeof (garg));
@@ -2380,16 +2378,7 @@
 	if (ifname != NULL)
 		(void) strlcpy(garg.ia_ifname, ifname, sizeof (garg.ia_ifname));
 
-	rvalp = malloc(sizeof (ipmgmt_get_rval_t));
-	err = ipadm_door_call(iph, &garg, sizeof (garg), (void **)&rvalp,
-	    sizeof (*rvalp), B_TRUE);
-	if (err == 0) {
-		nvlsize = rvalp->ir_nvlsize;
-		nvlbuf = (char *)rvalp + sizeof (ipmgmt_get_rval_t);
-		err = nvlist_unpack(nvlbuf, nvlsize, onvl, NV_ENCODE_NATIVE);
-	}
-	free(rvalp);
-	return (ipadm_errno2status(err));
+	return (i_ipadm_call_ipmgmtd(iph, (void *) &garg, sizeof (garg), onvl));
 }
 
 /*
@@ -2432,9 +2421,9 @@
 
 	/* Validate the addrobj. This also fills in addr->ipadm_ifname. */
 	status = i_ipadm_validate_create_addr(iph, addr, flags);
-	if (status != IPADM_SUCCESS)
+	if (status != IPADM_SUCCESS) {
 		return (status);
-
+	}
 	/*
 	 * For Legacy case, check if an addrobj already exists for the
 	 * given logical interface name. If one does not exist,
@@ -2481,8 +2470,9 @@
 	 */
 	if (!is_boot && (!legacy || !aobjfound)) {
 		status = i_ipadm_lookupadd_addrobj(iph, addr);
-		if (status != IPADM_SUCCESS)
+		if (status != IPADM_SUCCESS) {
 			return (status);
+		}
 	}
 
 	is_6to4 = i_ipadm_is_6to4(iph, ifname);
@@ -2631,6 +2621,7 @@
 	struct sockaddr_storage		m, *mask = &m;
 	const struct sockaddr_storage	*addr = &ipaddr->ipadm_static_addr;
 	const struct sockaddr_storage	*daddr = &ipaddr->ipadm_static_dst_addr;
+	uint32_t			iff_flags = IFF_UP;
 	sa_family_t			af;
 	boolean_t			legacy = (iph->iph_flags & IPH_LEGACY);
 	struct ipadm_addrobj_s		legacy_addr;
@@ -2700,7 +2691,10 @@
 	}
 
 	if (flags & IPADM_OPT_UP) {
-		status = i_ipadm_set_flags(iph, lifr.lifr_name, af, IFF_UP, 0);
+		if (i_ipadm_is_under_ipmp(iph, lifr.lifr_name)) {
+			iff_flags |= IFF_NOFAILOVER;
+		}
+		status = i_ipadm_set_flags(iph, lifr.lifr_name, af, iff_flags, 0);
 
 		/*
 		 * IPADM_DAD_FOUND is a soft-error for create-addr.
@@ -3203,9 +3197,11 @@
 
 	if (!(ipaddr->ipadm_flags & IPMGMT_ACTIVE))
 		return (IPADM_OP_DISABLE_OBJ);
+
 	if ((ipadm_flags & IPADM_OPT_PERSIST) &&
 	    !(ipaddr->ipadm_flags & IPMGMT_PERSIST))
 		return (IPADM_TEMPORARY_OBJ);
+
 	if (ipaddr->ipadm_atype == IPADM_ADDR_IPV6_ADDRCONF ||
 	    (ipaddr->ipadm_atype == IPADM_ADDR_DHCP &&
 	    (ipadm_flags & IPADM_OPT_PERSIST)))
@@ -3212,6 +3208,7 @@
 		return (IPADM_NOTSUP);
 
 	i_ipadm_addrobj2lifname(ipaddr, lifname, sizeof (lifname));
+
 	return (i_ipadm_get_flags(iph, lifname, ipaddr->ipadm_af, ifflags));
 }
 
@@ -3423,8 +3420,13 @@
 
 	ifname = ipaddr->ipadm_ifname;
 
-	if (i_ipadm_is_ipmp(iph, ifname) || i_ipadm_is_under_ipmp(iph, ifname))
-		return (IPADM_NOTSUP);
+	/*
+	 * do not go furhter when we are under ipmp. 
+	 * The interface is plumbed up and we are going to add
+	 * NOFAILOVER address to make in.mpathd happy.
+	 */ 
+	if (i_ipadm_is_under_ipmp(iph, ifname))
+		return (IPADM_SUCCESS);
 
 	af = ipaddr->ipadm_af;
 	af_exists = ipadm_if_enabled(iph, ifname, af);
@@ -3448,16 +3450,10 @@
 	status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
 	if (status != IPADM_SUCCESS)
 		return (status);
+
 	if (!a_exists && p_exists)
 		return (IPADM_OP_DISABLE_OBJ);
-	if ((flags & IPADM_OPT_PERSIST) && a_exists && !p_exists) {
-		/*
-		 * If address has to be created persistently,
-		 * and the interface does not exist in the persistent
-		 * store but in active config, fail.
-		 */
-		return (IPADM_TEMPORARY_OBJ);
-	}
+
 	if (af_exists) {
 		status = i_ipadm_get_flags(iph, ifname, af, &ifflags);
 		if (status != IPADM_SUCCESS)
--- 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);
 }
--- old/usr/src/lib/libipadm/common/ipadm_ipmgmt.h	Sat Jul  5 09:39:28 2014
+++ new/usr/src/lib/libipadm/common/ipadm_ipmgmt.h	Sat Jul  5 09:39:28 2014
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  */
 
 #ifndef _IPADM_IPMGMT_H
@@ -56,6 +57,10 @@
  */
 #define	IPADM_NVP_PROTONAME	"_protocol"	/* protocol name */
 #define	IPADM_NVP_IFNAME	"_ifname"	/* interface name */
+#define	IPADM_NVP_IFCLASS   "_ifclass"  /* interface class */
+#define	IPADM_NVP_FAMILIES	"_families"	/* interface families */
+#define	IPADM_NVP_GIFNAME   "_gifname" /* IPMP group interface name */
+#define	IPADM_NVP_MIFNAMES  "_mifnames" /* IPMP group members */
 #define	IPADM_NVP_AOBJNAME	"_aobjname"	/* addrobj name */
 #define	IPADM_NVP_FAMILY	"_family"	/* address family */
 #define	IPADM_NVP_IPV4ADDR	"_ipv4addr"	/* name of IPv4 addr nvlist */
@@ -106,6 +111,34 @@
 #define	IPMGMT_DOOR		"/etc/svc/volatile/ipadm/ipmgmt_door"
 #define	MAXPROTONAMELEN		32
 
+/*
+ * ia_flags used inside the arguments for interface/address commands
+ *
+ *	- APPEND updates the multi-valued ipadm DB entry with a new value
+ *	- REMOVE updates the multi-valued ipadm DB entry by removing a value
+ *	- ACTIVE updates the running configuration
+ *	- PERSIST updates the permanent data store
+ *	- INIT  indicates that operation being performed is under init
+ *	        context
+ *
+ * These two flags are used by ipmgmt_db_update_if function,
+ * because it can be used to update more that one DB line
+ * and we need to be sure that we finished all operations,
+ * after the operation has finished the related flag is cleared
+ *
+ *  - UPDATE_IF - used when we need to update IPADM_NVP_FAMILIES and
+ *                IPADM_NVP_MIFNAMES fields
+ *  - UPDATE_IPMP - used when we need to update IPADM_NVP_GIFNAME
+ */
+#define	IPMGMT_APPEND	0x00000001
+#define	IPMGMT_REMOVE	0x00000002
+#define	IPMGMT_ACTIVE	0x00000004
+#define	IPMGMT_PERSIST	0x00000008
+#define	IPMGMT_INIT	0x00000010
+#define	IPMGMT_UPDATE_IF    0x00000020
+#define	IPMGMT_UPDATE_IPMP  0x00000040
+
+
 /* door call command type */
 typedef enum {
 	IPMGMT_CMD_SETPROP = 1,		/* persist property */
@@ -122,7 +155,8 @@
 	IPMGMT_CMD_ADDROBJ_SETLIFNUM,	/* set lifnum on the addrobj */
 	IPMGMT_CMD_ADDROBJ_ADD,		/* add addr. object to addrobj map */
 	IPMGMT_CMD_LIF2ADDROBJ,		/* lifname to addrobj mapping */
-	IPMGMT_CMD_AOBJNAME2ADDROBJ	/* aobjname to addrobj mapping */
+	IPMGMT_CMD_AOBJNAME2ADDROBJ,	/* aobjname to addrobj mapping */
+	IPMGMT_CMD_IPMP_UPDATE  /* update IPMP group members */
 } ipmgmt_door_cmd_type_t;
 
 /*
@@ -144,13 +178,6 @@
 	char			ia_pname[MAXPROPNAMELEN];
 	char			ia_pval[MAXPROPVALLEN];
 } ipmgmt_prop_arg_t;
-/*
- * ia_flags used in ipmgmt_prop_arg_t.
- *	- APPEND updates the multi-valued property entry with a new value
- *	- REDUCE updates the multi-valued property entry by removing a value
- */
-#define	IPMGMT_APPEND	0x00000001
-#define	IPMGMT_REMOVE	0x00000002
 
 /* IPMGMT_CMD_GETIF door_call argument structure */
 typedef struct ipmgmt_getif_arg_s {
@@ -165,6 +192,7 @@
 	uint32_t		ia_flags;
 	char			ia_ifname[LIFNAMSIZ];
 	sa_family_t		ia_family;
+    ipadm_if_class_t    ia_ifclass;
 } ipmgmt_if_arg_t;
 
 /* IPMGMT_CMD_INITIF door_call argument structure */
@@ -176,6 +204,14 @@
 	/* packed nvl follows */
 } ipmgmt_initif_arg_t;
 
+/* IPMGMT_CMD_IPMP_UPDATE door_call argument structure */
+typedef struct ipmgmt_ipmp_update_arg_s {
+	ipmgmt_door_cmd_type_t	ia_cmd;
+	uint32_t	ia_flags;
+	char		ia_gifname[LIFNAMSIZ]; /* group interface name */
+	char	ia_mifname[LIFNAMSIZ]; /* group's member interface name */
+} ipmgmt_ipmp_update_arg_t;
+
 /* IPMGMT_CMD_SETADDR door_call argument */
 typedef struct ipmgmt_setaddr_arg_s {
 	ipmgmt_door_cmd_type_t	ia_cmd;
@@ -215,17 +251,6 @@
 	ipadm_addr_type_t	ia_atype;
 } ipmgmt_aobjop_arg_t;
 
-/*
- * ia_flags used inside the arguments for interface/address commands
- *	- ACTIVE updates the running configuration
- *	- PERSIST updates the permanent data store
- *	- INIT	indicates that operation being performed is under init
- *		    context
- */
-#define	IPMGMT_ACTIVE		0x00000001
-#define	IPMGMT_PERSIST		0x00000002
-#define	IPMGMT_INIT		0x00000004
-
 /* door call return value */
 typedef struct ipmgmt_retval_s {
 	int32_t	ir_err;
--- old/usr/src/lib/libipadm/common/ipadm_persist.c	Sat Jul  5 09:39:29 2014
+++ new/usr/src/lib/libipadm/common/ipadm_persist.c	Sat Jul  5 09:39:29 2014
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
@@ -51,6 +52,12 @@
  *			wait:	DATA_TYPE_INT32
  *			primary: DATA_TYPE_BOOLEAN
  *
+ *	IPADM_NVP_FAMILIES - value holds interface families and when converted
+ *	        to nvlist, will be a DATA_TYPE_UINT16_ARRAY
+ *
+ *  IPADM_NVP_MIFNAMES - value holds IPMP group members and when converted
+ *          to nvlist, will be a DATA_TYPE_STRING_ARRAY
+ *
  *  default  - value is a single entity and when converted to nvlist, will
  *	       contain nvpair of type DATA_TYPE_STRING. nvpairs private to
  *	       ipadm are of this type. Further the property name and property
@@ -98,11 +105,13 @@
 
 static ipadm_rfunc_t	i_ipadm_str_dbline2nvl, i_ipadm_ip4_dbline2nvl,
 			i_ipadm_ip6_dbline2nvl, i_ipadm_intfid_dbline2nvl,
-			i_ipadm_dhcp_dbline2nvl;
+			i_ipadm_dhcp_dbline2nvl, i_ipadm_families_dbline2nvl,
+			i_ipadm_groupmembers_dbline2nvl;
 
 static ipadm_wfunc_t	i_ipadm_str_nvp2dbline, i_ipadm_ip4_nvp2dbline,
 			i_ipadm_ip6_nvp2dbline, i_ipadm_intfid_nvp2dbline,
-			i_ipadm_dhcp_nvp2dbline;
+			i_ipadm_dhcp_nvp2dbline, i_ipadm_families_nvp2dbline,
+			i_ipadm_groupmembers_nvp2dbline;
 
 /*
  * table of function pointers to read/write formatted entries from/to
@@ -120,6 +129,10 @@
 	{ IPADM_NVP_INTFID, i_ipadm_intfid_nvp2dbline,
 	    i_ipadm_intfid_dbline2nvl },
 	{ IPADM_NVP_DHCP, i_ipadm_dhcp_nvp2dbline, i_ipadm_dhcp_dbline2nvl },
+	{ IPADM_NVP_FAMILIES, i_ipadm_families_nvp2dbline,
+	    i_ipadm_families_dbline2nvl },
+	{ IPADM_NVP_MIFNAMES, i_ipadm_groupmembers_nvp2dbline,
+	    i_ipadm_groupmembers_dbline2nvl},
 	{ NULL,	i_ipadm_str_nvp2dbline,	i_ipadm_str_dbline2nvl }
 };
 
@@ -615,6 +628,141 @@
 	(void) i_ipadm_add_dhcp2nvl(nvl, primary, (int32_t)wait_time);
 }
 
+/*
+ * Input 'nvp': name = IPADM_NVP_FAMILIES and value = array of 'uint16_t'
+ *
+ *
+ */
+static size_t
+i_ipadm_families_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
+{
+	uint_t nelem = 0;
+	uint16_t *elem;
+
+	assert(nvpair_type(nvp) == DATA_TYPE_UINT16_ARRAY);
+
+	if (nvpair_value_uint16_array(nvp,
+	    &elem, &nelem) != 0) {
+		buf[0] = '\0';
+		return (0);
+	}
+
+	assert(nelem != 0 || nelem > 2);
+
+	if (nelem == 1) {
+		return (snprintf(buf, buflen, "%s=%d",
+		    nvpair_name(nvp), elem[0]));
+	} else {
+		return (snprintf(buf, buflen, "%s=%d,%d",
+		    nvpair_name(nvp), elem[0], elem[1]));
+	}
+}
+
+/*
+ * name = IPADM_NVP_FAMILIES and value = <FAMILY>[,FAMILY]
+ *
+ * output nvp: name = IPADM_NVP_FAMILIES and value = array of 'uint16_t'
+ *
+ */
+static void
+i_ipadm_families_dbline2nvl(nvlist_t *nvl, char *name, char *value)
+{
+	uint16_t	families[2];
+	uint_t	nelem = 0;
+	char	*val, *lasts;
+
+	if ((val = strtok_r(value,
+	    ",", &lasts)) != NULL) {
+		families[0] = atoi(val);
+		nelem++;
+		if ((val = strtok_r(NULL,
+		    ",", &lasts)) != NULL) {
+			families[1] = atoi(val);
+			nelem++;
+		}
+		nvlist_add_uint16_array(nvl,
+		    IPADM_NVP_FAMILIES, families, nelem);
+	}
+}
+
+/*
+ * input nvp: name = IPADM_NVP_MIFNAMES and value = array of 'char *'
+ *
+ *
+ */
+static size_t
+i_ipadm_groupmembers_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
+{
+	uint_t nelem = 0;
+	char **elem;
+	size_t n;
+
+	assert(nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY);
+
+	if (nvpair_value_string_array(nvp,
+	    &elem, &nelem) != 0) {
+		buf[0] = '\0';
+		return (0);
+	}
+
+	assert(nelem != 0);
+
+	n = snprintf(buf, buflen, "%s=", IPADM_NVP_MIFNAMES);
+	if (n >= buflen)
+		return (n);
+
+	while (nelem--) {
+		n = strlcat(buf, elem[nelem], buflen);
+		if (nelem > 0)
+			n = strlcat(buf, ",", buflen);
+
+		if (n > buflen)
+			return (n);
+	}
+
+	return (n);
+}
+
+/*
+ * name = IPADM_NVP_MIFNAMES and value = <if_name>[,if_name]
+ *
+ * output nvp: name = IPADM_NVP_MIFNAMES and value = array of 'char *'
+ */
+static void
+i_ipadm_groupmembers_dbline2nvl(nvlist_t *nvl, char *name, char *value)
+{
+	char	*members[256];
+	char	*member;
+	char	*val, *lasts;
+	uint_t	m_cnt = 0;
+
+	assert(strcmp(name, IPADM_NVP_MIFNAMES) == 0 && value != NULL);
+
+	if ((val = strtok_r(value, ",", &lasts)) != NULL) {
+		if ((member = calloc(1, LIFNAMSIZ)) == NULL)
+			return;
+
+		strlcpy(member, val, LIFNAMSIZ);
+		members[m_cnt++] = member;
+
+		while ((val = strtok_r(NULL, ",", &lasts)) != NULL) {
+			if ((member = calloc(1, LIFNAMSIZ)) == NULL)
+				goto fail;
+
+			strlcpy(member, val, LIFNAMSIZ);
+			members[m_cnt++] = member;
+		}
+
+		nvlist_add_string_array(nvl, IPADM_NVP_MIFNAMES,
+		    members, m_cnt);
+	}
+
+fail:
+	while (m_cnt--) {
+		free(members[m_cnt]);
+	}
+}
+
 /*
  * Parses the buffer, for name-value pairs and creates nvlist. The value
  * is always considered to be a string.
--- old/usr/src/lib/libipadm/common/ipadm_prop.c	Sat Jul  5 09:39:29 2014
+++ new/usr/src/lib/libipadm/common/ipadm_prop.c	Sat Jul  5 09:39:29 2014
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright (c) 2014 Nexenta Systems, Inc. All rights reserved
  */
 
 /*
@@ -145,6 +146,11 @@
 	    i_ipadm_set_hostmodel, i_ipadm_get_hostmodel,
 	    i_ipadm_get_hostmodel },
 
+	{ "standby", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IP, 0,
+	    i_ipadm_set_ifprop_flags, i_ipadm_get_onoff,
+	    i_ipadm_get_ifprop_flags },
+
+
 	{ NULL, NULL, 0, 0, 0, NULL, NULL, NULL }
 };
 
@@ -595,7 +601,8 @@
 		    strcmp(pdp->ipd_name, "arp") == 0 ||
 		    strcmp(pdp->ipd_name, "nud") == 0) {
 			pval = IPADM_ONSTR;
-		} else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
+		} else if (strcmp(pdp->ipd_name, "forwarding") == 0 ||
+		    strcmp(pdp->ipd_name, "standby") == 0) {
 			pval = IPADM_OFFSTR;
 		} else {
 			return (IPADM_PROP_UNKNOWN);
@@ -629,6 +636,11 @@
 			on_flags = IFF_ROUTER;
 		else
 			off_flags = IFF_ROUTER;
+	} else if (strcmp(pdp->ipd_name, "standby") == 0) {
+		if (on)
+			on_flags = IFF_STANDBY;
+		else
+			off_flags = IFF_STANDBY;
 	}
 
 	if (on_flags || off_flags)  {
@@ -997,7 +1009,8 @@
 		    strcmp(pdp->ipd_name, "arp") == 0 ||
 		    strcmp(pdp->ipd_name, "nud") == 0) {
 			val = IPADM_ONSTR;
-		} else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
+		} else if (strcmp(pdp->ipd_name, "forwarding") == 0 ||
+		    strcmp(pdp->ipd_name, "standby") == 0) {
 			val = IPADM_OFFSTR;
 		} else {
 			return (IPADM_PROP_UNKNOWN);
@@ -1023,6 +1036,9 @@
 		} else if (strcmp(pdp->ipd_name, "nud") == 0) {
 			if (!(intf_flags & IFF_NONUD))
 				val = IPADM_ONSTR;
+		} else if (strcmp(pdp->ipd_name, "standby") == 0) {
+			if (!(intf_flags & IFF_STANDBY))
+				val = IPADM_ONSTR;
 		}
 		nbytes = snprintf(buf, *bufsize, "%s", val);
 		break;
@@ -1698,8 +1714,7 @@
  * This is called from ipadm_set_ifprop() to validate the set operation.
  * It does the following steps:
  * 1. Validates the interface name.
- * 2. Fails if it is an IPMP meta-interface or an underlying interface.
- * 3. In case of a persistent operation, verifies that the
+ * 2. In case of a persistent operation, verifies that the
  *	interface is persistent.
  */
 static ipadm_status_t
@@ -1716,12 +1731,6 @@
 		return (IPADM_INVALID_ARG);
 
 	af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET);
-	/*
-	 * Setting properties on an IPMP meta-interface or underlying
-	 * interface is not supported.
-	 */
-	if (i_ipadm_is_ipmp(iph, ifname) || i_ipadm_is_under_ipmp(iph, ifname))
-		return (IPADM_NOTSUP);
 
 	/* Check if interface exists in the persistent configuration. */
 	status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
--- old/usr/src/lib/libipadm/common/libipadm.c	Sat Jul  5 09:39:29 2014
+++ new/usr/src/lib/libipadm/common/libipadm.c	Sat Jul  5 09:39:29 2014
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  */
 
 #include <stdio.h>
@@ -525,39 +526,6 @@
 }
 
 /*
- * Returns B_TRUE if `ifname' represents an IPMP underlying interface.
- */
-boolean_t
-i_ipadm_is_under_ipmp(ipadm_handle_t iph, const char *ifname)
-{
-	struct lifreq	lifr;
-
-	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
-	if (ioctl(iph->iph_sock, SIOCGLIFGROUPNAME, (caddr_t)&lifr) < 0) {
-		if (ioctl(iph->iph_sock6, SIOCGLIFGROUPNAME,
-		    (caddr_t)&lifr) < 0) {
-			return (B_FALSE);
-		}
-	}
-	return (lifr.lifr_groupname[0] != '\0');
-}
-
-/*
- * 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);
-}
-
-/*
  * For a given interface name, ipadm_if_enabled() checks if v4
  * or v6 or both IP interfaces exist in the active configuration.
  */
@@ -690,13 +658,17 @@
 {
 	nvlist_t	*nvl = NULL;
 	nvpair_t	*nvp;
-	char		*afstr;
 	ipadm_status_t	status;
 	ipadm_status_t	ret_status = IPADM_SUCCESS;
 	char		newifname[LIFNAMSIZ];
 	char		*aobjstr;
-	sa_family_t	af = AF_UNSPEC;
+	char	*ifclass_str, *gif_name;
+	uint16_t	*families;
+	uint_t	nelem = 0;
+	char **members;
+	uint32_t	ipadm_flags;
 	boolean_t	is_ngz = (iph->iph_zoneid != GLOBAL_ZONEID);
+	boolean_t   init_from_gz = B_FALSE;
 
 	(void) strlcpy(newifname, ifname, sizeof (newifname));
 	/*
@@ -706,24 +678,48 @@
 	 */
 	for (nvp = nvlist_next_nvpair(ifnvl, NULL); nvp != NULL;
 	    nvp = nvlist_next_nvpair(ifnvl, nvp)) {
+
 		if (nvpair_value_nvlist(nvp, &nvl) != 0)
 			continue;
 
-		if (nvlist_lookup_string(nvl, IPADM_NVP_FAMILY, &afstr) == 0) {
-			status = i_ipadm_plumb_if(iph, newifname, atoi(afstr),
-			    IPADM_OPT_ACTIVE);
-			/*
-			 * If the interface is already plumbed, we should
-			 * ignore this error because there might be address
-			 * address objects on that interface that needs to
-			 * be enabled again.
-			 */
-			if (status == IPADM_IF_EXISTS)
-				status = IPADM_SUCCESS;
 
+		if (nvlist_lookup_uint16_array(nvl, IPADM_NVP_FAMILIES,
+		    &families, &nelem) == 0) {
+
+			ipadm_flags = IPADM_OPT_ACTIVE;
+
+			if (nvlist_lookup_string(nvl, IPADM_NVP_IFCLASS, &ifclass_str) == 0 &&
+			    atoi(ifclass_str) == IPADM_IF_CLASS_IPMP)
+				ipadm_flags |= IPADM_OPT_IPMP;
+
+			while (nelem--) {
+				assert(families[nelem] == AF_INET ||
+			    	    families[nelem] == AF_INET6);
+
+				status = i_ipadm_plumb_if(iph, newifname, families[nelem],
+		    		    ipadm_flags);
+
+				if (status == IPADM_IF_EXISTS)
+					status = IPADM_SUCCESS;
+
+				/* plumbing can fail for ipmp, this is expected */
+				if (status != IPADM_SUCCESS && !(ipadm_flags & IPADM_OPT_IPMP))
+					break;
+			}
+			/* does this interface belong to ipmp ? */
+			if (nvlist_lookup_string(nvl, IPADM_NVP_GIFNAME, &gif_name) == 0) {
+				(void) ipadm_create_if(iph, gif_name, AF_INET, IPADM_OPT_IPMP |
+		  		    IPADM_OPT_ACTIVE);
+				(void) ipadm_create_if(iph, gif_name, AF_INET6, IPADM_OPT_IPMP |
+				   IPADM_OPT_ACTIVE);
+				/** add itself to the group */
+				status = ipadm_add_ipmp_member(iph, gif_name, newifname, IPADM_OPT_ACTIVE);
+				if (status != IPADM_SUCCESS && status != IPADM_IF_EXISTS)
+					break;
+			}
 			if (is_ngz)
-				af = atoi(afstr);
-		} else if (nvlist_lookup_string(nvl, IPADM_NVP_AOBJNAME,
+				init_from_gz = B_TRUE;
+		 } else if (nvlist_lookup_string(nvl, IPADM_NVP_AOBJNAME,
 		    &aobjstr) == 0) {
 			/*
 			 * For a static address, we need to search for
@@ -733,6 +729,7 @@
 			    nvlist_exists(nvl, IPADM_NVP_IPV6ADDR)) {
 				status = i_ipadm_merge_prefixlen_from_nvl(ifnvl,
 				    nvl, aobjstr);
+
 				if (status != IPADM_SUCCESS)
 					continue;
 			}
@@ -754,9 +751,9 @@
 		if (status != IPADM_SUCCESS)
 			return (status);
 	}
-
-	if (is_ngz && af != AF_UNSPEC)
+	if (init_from_gz)
 		ret_status = ipadm_init_net_from_gz(iph, newifname, NULL);
+
 	return (ret_status);
 }
 
@@ -953,4 +950,31 @@
 			err = EBADE;
 	}
 	return (err);
+}
+
+/*
+ * A helper that is used by i_ipadm_get_db_addr and i_ipadm_get_db_if
+ * to do a door_call to ipmgmtd, that should return persistent information
+ * about interfaces or/and addresses from ipadm DB
+ */
+ipadm_status_t
+i_ipadm_call_ipmgmtd(ipadm_handle_t iph, void *garg,
+	size_t garg_size, nvlist_t **onvl)
+{
+	ipmgmt_get_rval_t	*rvalp;
+	int			err;
+	size_t			nvlsize;
+	char			*nvlbuf;
+
+	rvalp = malloc(sizeof (ipmgmt_get_rval_t));
+	err = ipadm_door_call(iph, garg, garg_size, (void **)&rvalp,
+	    sizeof (*rvalp), B_TRUE);
+	if (err == 0) {
+		nvlsize = rvalp->ir_nvlsize;
+		nvlbuf = (char *)rvalp + sizeof (ipmgmt_get_rval_t);
+		err = nvlist_unpack(nvlbuf, nvlsize, onvl, NV_ENCODE_NATIVE);
+	}
+	free(rvalp);
+
+	return (ipadm_errno2status(err));
 }
--- old/usr/src/lib/libipadm/common/libipadm.h	Sat Jul  5 09:39:29 2014
+++ new/usr/src/lib/libipadm/common/libipadm.h	Sat Jul  5 09:39:29 2014
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  */
 #ifndef _LIBIPADM_H
 #define	_LIBIPADM_H
@@ -36,6 +37,8 @@
 #include <libnvpair.h>
 #include <netinet/tcp.h>
 #include <sys/stropts.h>
+#include <sys/list.h>
+#include <stddef.h>
 
 #define	IPADM_AOBJ_USTRSIZ	32
 #define	IPADM_AOBJSIZ		(LIFNAMSIZ + IPADM_AOBJ_USTRSIZ)
@@ -191,12 +194,29 @@
 	IFIS_DISABLED		/* Interface has been disabled. */
 } ipadm_if_state_t;
 
+typedef list_t ipadm_ipmp_members_t;
+
+typedef struct {
+    list_node_t node;
+    char if_name[LIFNAMSIZ];
+} ipadm_ipmp_member_t;
+
+typedef enum {
+    IPADM_IF_CLASS_REGULAR,
+    IPADM_IF_CLASS_IPMP,
+    IPADM_IF_CLASS_VIRTUAL,
+    IPADM_IF_CLASS_UNKNOWN
+} ipadm_if_class_t;
+
 typedef struct ipadm_if_info_s {
 	struct ipadm_if_info_s	*ifi_next;
 	char			ifi_name[LIFNAMSIZ];	/* interface name */
+	ipadm_if_class_t ifi_class; /* interface class */
 	ipadm_if_state_t	ifi_state;		/* see above */
 	uint_t			ifi_cflags;		/* current flags */
 	uint_t			ifi_pflags;		/* persistent flags */
+	ipadm_ipmp_members_t    ifi_ipmp_cmembers; /* current IPMP group members */
+	ipadm_ipmp_members_t    ifi_ipmp_pmembers; /* persistent IPMP grp members */
 } ipadm_if_info_t;
 
 /* ipadm_if_info_t flags */
@@ -270,6 +290,10 @@
 extern ipadm_status_t	ipadm_delete_if(ipadm_handle_t, const char *,
 			    sa_family_t, uint32_t);
 extern void		ipadm_if_move(ipadm_handle_t, const char *);
+extern ipadm_status_t ipadm_add_ipmp_member(ipadm_handle_t, const char *,
+	    const char *, uint32_t);
+extern ipadm_status_t ipadm_remove_ipmp_member(ipadm_handle_t, const char *,
+	    const char *, uint32_t);
 
 /*
  * Address management functions
--- old/usr/src/lib/libipadm/common/libipadm_impl.h	Sat Jul  5 09:39:30 2014
+++ new/usr/src/lib/libipadm/common/libipadm_impl.h	Sat Jul  5 09:39:30 2014
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright (c) 2014 Nexenta Systems, Inc. All rights reserved.
  */
 
 #ifndef _LIBIPADM_IMPL_H
@@ -31,6 +32,7 @@
 #endif
 
 #include <sys/socket.h>
+#include <sys/list.h>
 #include <net/if.h>
 #include <libipadm.h>
 #include <libdladm.h>
@@ -51,6 +53,11 @@
 /* mask for flags accepted by libipadm functions */
 #define	IPADM_COMMON_OPT_MASK	(IPADM_OPT_ACTIVE | IPADM_OPT_PERSIST)
 
+typedef enum {
+    IPADM_ADD_IPMP_MEMBER,
+    IPADM_REMOVE_IPMP_MEMBER
+} ipadm_ipmp_operation_t;
+
 /* Opaque library handle */
 struct ipadm_handle {
 	int		iph_sock;	/* socket to interface */
@@ -150,8 +157,6 @@
 extern int		i_ipadm_strioctl(int, int, char *, int);
 extern boolean_t	i_ipadm_is_loopback(const char *);
 extern boolean_t	i_ipadm_is_vni(const char *);
-extern boolean_t	i_ipadm_is_ipmp(ipadm_handle_t, const char *);
-extern boolean_t	i_ipadm_is_under_ipmp(ipadm_handle_t, const char *);
 extern boolean_t	i_ipadm_is_6to4(ipadm_handle_t, char *);
 extern boolean_t	i_ipadm_validate_ifname(ipadm_handle_t, const char *);
 extern ipadm_status_t	ipadm_errno2status(int);
@@ -159,6 +164,8 @@
 			    size_t, boolean_t);
 extern boolean_t 	ipadm_if_enabled(ipadm_handle_t, const char *,
 			    sa_family_t);
+extern ipadm_status_t i_ipadm_call_ipmgmtd(ipadm_handle_t, void *,
+	    size_t, nvlist_t **);
 
 /* ipadm_ndpd.c */
 extern	ipadm_status_t	i_ipadm_create_ipv6addrs(ipadm_handle_t,
@@ -226,6 +233,12 @@
 			    sa_family_t, boolean_t);
 extern int		i_ipadm_get_lnum(const char *);
 
+extern ipadm_status_t i_ipadm_set_groupname_active(ipadm_handle_t,
+	    const char *, const char *);
+extern ipadm_status_t i_ipadm_get_groupname_active(ipadm_handle_t,
+	    const char *, char *, size_t);
+extern boolean_t i_ipadm_is_under_ipmp(ipadm_handle_t, const char *);
+extern boolean_t i_ipadm_is_ipmp(ipadm_handle_t, const char *);
 #ifdef	__cplusplus
 }
 #endif
--- old/usr/src/lib/libipadm/common/mapfile-vers	Sat Jul  5 09:39:30 2014
+++ new/usr/src/lib/libipadm/common/mapfile-vers	Sat Jul  5 09:39:30 2014
@@ -20,6 +20,7 @@
 #
 #
 # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
 #
 
 #
@@ -40,6 +41,7 @@
 
 SYMBOL_VERSION SUNWprivate_1.1 {
     global:
+	ipadm_add_ipmp_member;
 	ipadm_add_aobjname;
 	ipadm_addr_info;
 	ipadm_check_auth;
@@ -76,6 +78,7 @@
 	ipadm_open_arp_on_udp;
 	ipadm_proto2str;
 	ipadm_refresh_addr;
+	ipadm_remove_ipmp_member;
 	ipadm_rw_db;
 	ipadm_set_addr;
 	ipadm_set_addrprop;
