16e91bba0SGirish Moodalbail /*
26e91bba0SGirish Moodalbail  * CDDL HEADER START
36e91bba0SGirish Moodalbail  *
46e91bba0SGirish Moodalbail  * The contents of this file are subject to the terms of the
56e91bba0SGirish Moodalbail  * Common Development and Distribution License (the "License").
66e91bba0SGirish Moodalbail  * You may not use this file except in compliance with the License.
76e91bba0SGirish Moodalbail  *
86e91bba0SGirish Moodalbail  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96e91bba0SGirish Moodalbail  * or http://www.opensolaris.org/os/licensing.
106e91bba0SGirish Moodalbail  * See the License for the specific language governing permissions
116e91bba0SGirish Moodalbail  * and limitations under the License.
126e91bba0SGirish Moodalbail  *
136e91bba0SGirish Moodalbail  * When distributing Covered Code, include this CDDL HEADER in each
146e91bba0SGirish Moodalbail  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156e91bba0SGirish Moodalbail  * If applicable, add the following below this CDDL HEADER, with the
166e91bba0SGirish Moodalbail  * fields enclosed by brackets "[]" replaced with your own identifying
176e91bba0SGirish Moodalbail  * information: Portions Copyright [yyyy] [name of copyright owner]
186e91bba0SGirish Moodalbail  *
196e91bba0SGirish Moodalbail  * CDDL HEADER END
206e91bba0SGirish Moodalbail  */
21a73be61aSHans Rosenfeld 
226e91bba0SGirish Moodalbail /*
2336b41818SGirish Moodalbail  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24a73be61aSHans Rosenfeld  * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
256e91bba0SGirish Moodalbail  */
266e91bba0SGirish Moodalbail 
276e91bba0SGirish Moodalbail #include <errno.h>
286e91bba0SGirish Moodalbail #include <sys/sockio.h>
29a73be61aSHans Rosenfeld #include <sys/list.h>
306e91bba0SGirish Moodalbail #include <string.h>
316e91bba0SGirish Moodalbail #include <assert.h>
326e91bba0SGirish Moodalbail #include <unistd.h>
336e91bba0SGirish Moodalbail #include <stropts.h>
346e91bba0SGirish Moodalbail #include <strings.h>
356e91bba0SGirish Moodalbail #include <libdlpi.h>
366e91bba0SGirish Moodalbail #include <libdllink.h>
376e91bba0SGirish Moodalbail #include <libinetutil.h>
386e91bba0SGirish Moodalbail #include <inet/ip.h>
396e91bba0SGirish Moodalbail #include <limits.h>
406e91bba0SGirish Moodalbail #include <zone.h>
416e91bba0SGirish Moodalbail #include <ipadm_ndpd.h>
42a73be61aSHans Rosenfeld #include <ipmp_query.h>
436e91bba0SGirish Moodalbail #include "libipadm_impl.h"
446e91bba0SGirish Moodalbail 
456e91bba0SGirish Moodalbail static ipadm_status_t	i_ipadm_slifname_arp(char *, uint64_t, int);
466e91bba0SGirish Moodalbail static ipadm_status_t	i_ipadm_slifname(ipadm_handle_t, char *, char *,
476e91bba0SGirish Moodalbail 			    uint64_t, int, uint32_t);
486e91bba0SGirish Moodalbail static ipadm_status_t	i_ipadm_create_ipmp_peer(ipadm_handle_t, char *,
496e91bba0SGirish Moodalbail 			    sa_family_t);
506e91bba0SGirish Moodalbail static ipadm_status_t	i_ipadm_persist_if(ipadm_handle_t, const char *,
51a73be61aSHans Rosenfeld 			    sa_family_t, uint32_t);
52a73be61aSHans Rosenfeld static ipadm_status_t   i_ipadm_allocate_ifinfo(ipadm_if_info_t **);
53a73be61aSHans Rosenfeld static ipadm_status_t	i_ipadm_get_db_if(ipadm_handle_t, const char *,
54a73be61aSHans Rosenfeld 			    nvlist_t **);
55a73be61aSHans Rosenfeld static ipadm_status_t i_ipadm_nvl2ifinfo(nvlist_t *, ipadm_if_info_t **);
56a73be61aSHans Rosenfeld static ipadm_status_t i_ipadm_fill_cmembers(char *, ipadm_ipmp_members_t *);
57a73be61aSHans Rosenfeld static ipadm_status_t i_ipadm_fill_pmembers(nvlist_t *, ipadm_ipmp_members_t *);
58a73be61aSHans Rosenfeld static ipadm_status_t i_ipadm_add_persistent_if_info(ipadm_if_info_t *,
59a73be61aSHans Rosenfeld 		    ipadm_if_info_t *);
60a73be61aSHans Rosenfeld static void i_ipadm_free_ipmp_members(ipadm_ipmp_members_t *);
61a73be61aSHans Rosenfeld static ipadm_status_t i_ipadm_persist_update_ipmp(ipadm_handle_t, const char *,
62a73be61aSHans Rosenfeld 	const char *,
63a73be61aSHans Rosenfeld 	ipadm_ipmp_op_t);
64a73be61aSHans Rosenfeld static ipadm_status_t i_ipadm_update_ipmp(ipadm_handle_t, const char *,
65a73be61aSHans Rosenfeld 	const char *, uint32_t,
66a73be61aSHans Rosenfeld 	ipadm_ipmp_op_t);
676e91bba0SGirish Moodalbail 
686e91bba0SGirish Moodalbail /*
696e91bba0SGirish Moodalbail  * Returns B_FALSE if the interface in `ifname' has at least one address that is
706e91bba0SGirish Moodalbail  * IFF_UP in the addresses in `ifa'.
716e91bba0SGirish Moodalbail  */
726e91bba0SGirish Moodalbail static boolean_t
i_ipadm_is_if_down(char * ifname,struct ifaddrs * ifa)736e91bba0SGirish Moodalbail i_ipadm_is_if_down(char *ifname, struct ifaddrs *ifa)
746e91bba0SGirish Moodalbail {
756e91bba0SGirish Moodalbail 	struct ifaddrs	*ifap;
766e91bba0SGirish Moodalbail 	char		cifname[LIFNAMSIZ];
776e91bba0SGirish Moodalbail 	char		*sep;
786e91bba0SGirish Moodalbail 
796e91bba0SGirish Moodalbail 	for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
806e91bba0SGirish Moodalbail 		(void) strlcpy(cifname, ifap->ifa_name, sizeof (cifname));
816e91bba0SGirish Moodalbail 		if ((sep = strrchr(cifname, IPADM_LOGICAL_SEP)) != NULL)
826e91bba0SGirish Moodalbail 			*sep = '\0';
836e91bba0SGirish Moodalbail 		/*
846e91bba0SGirish Moodalbail 		 * If this condition is true, there is at least one
856e91bba0SGirish Moodalbail 		 * address that is IFF_UP. So, we need to return B_FALSE.
866e91bba0SGirish Moodalbail 		 */
876e91bba0SGirish Moodalbail 		if (strcmp(cifname, ifname) == 0 &&
886e91bba0SGirish Moodalbail 		    (ifap->ifa_flags & IFF_UP)) {
896e91bba0SGirish Moodalbail 			return (B_FALSE);
906e91bba0SGirish Moodalbail 		}
916e91bba0SGirish Moodalbail 	}
926e91bba0SGirish Moodalbail 	/* We did not find any IFF_UP addresses. */
936e91bba0SGirish Moodalbail 	return (B_TRUE);
946e91bba0SGirish Moodalbail }
956e91bba0SGirish Moodalbail 
966e91bba0SGirish Moodalbail /*
976e91bba0SGirish Moodalbail  * Retrieves the information for the interface `ifname' from active
986e91bba0SGirish Moodalbail  * config if `ifname' is specified and returns the result in the list `if_info'.
996e91bba0SGirish Moodalbail  * Otherwise, it retrieves the information for all the interfaces in
1006e91bba0SGirish Moodalbail  * the active config and returns the result in the list `if_info'.
1016e91bba0SGirish Moodalbail  */
1026e91bba0SGirish Moodalbail static ipadm_status_t
i_ipadm_active_if_info(ipadm_handle_t iph,const char * ifname,ipadm_if_info_t ** if_info,int64_t lifc_flags)1036e91bba0SGirish Moodalbail i_ipadm_active_if_info(ipadm_handle_t iph, const char *ifname,
104a73be61aSHans Rosenfeld     ipadm_if_info_t **if_info, int64_t lifc_flags)
1056e91bba0SGirish Moodalbail {
1066e91bba0SGirish Moodalbail 	struct lifreq	*buf;
1076e91bba0SGirish Moodalbail 	struct lifreq	*lifrp;
1086e91bba0SGirish Moodalbail 	struct lifreq	lifrl;
109a73be61aSHans Rosenfeld 	ipadm_if_info_t	*last = NULL;
1106e91bba0SGirish Moodalbail 	ipadm_if_info_t	*ifp;
1116e91bba0SGirish Moodalbail 	int		s;
1126e91bba0SGirish Moodalbail 	int		n;
1136e91bba0SGirish Moodalbail 	int		numifs;
114a73be61aSHans Rosenfeld 	ipadm_status_t	status = IPADM_SUCCESS;
1156e91bba0SGirish Moodalbail 
1166e91bba0SGirish Moodalbail 	*if_info = NULL;
1176e91bba0SGirish Moodalbail 	/*
1186e91bba0SGirish Moodalbail 	 * Get information for all interfaces.
1196e91bba0SGirish Moodalbail 	 */
1206e91bba0SGirish Moodalbail 	if (getallifs(iph->iph_sock, 0, &buf, &numifs, lifc_flags) != 0)
1216e91bba0SGirish Moodalbail 		return (ipadm_errno2status(errno));
1226e91bba0SGirish Moodalbail 
1236e91bba0SGirish Moodalbail 	lifrp = buf;
1246e91bba0SGirish Moodalbail 	for (n = 0; n < numifs; n++, lifrp++) {
1256e91bba0SGirish Moodalbail 		/* Skip interfaces with logical num != 0 */
1266e91bba0SGirish Moodalbail 		if (i_ipadm_get_lnum(lifrp->lifr_name) != 0)
1276e91bba0SGirish Moodalbail 			continue;
1286e91bba0SGirish Moodalbail 		/*
1296e91bba0SGirish Moodalbail 		 * Skip the current interface if a specific `ifname' has
1306e91bba0SGirish Moodalbail 		 * been requested and current interface does not match
1316e91bba0SGirish Moodalbail 		 * `ifname'.
1326e91bba0SGirish Moodalbail 		 */
1336e91bba0SGirish Moodalbail 		if (ifname != NULL && strcmp(lifrp->lifr_name, ifname) != 0)
1346e91bba0SGirish Moodalbail 			continue;
1356e91bba0SGirish Moodalbail 		/*
1366e91bba0SGirish Moodalbail 		 * Check if the interface already exists in our list.
1376e91bba0SGirish Moodalbail 		 * If it already exists, we need to update its flags.
1386e91bba0SGirish Moodalbail 		 */
139a73be61aSHans Rosenfeld 		for (ifp = *if_info; ifp != NULL; ifp = ifp->ifi_next) {
1406e91bba0SGirish Moodalbail 			if (strcmp(lifrp->lifr_name, ifp->ifi_name) == 0)
1416e91bba0SGirish Moodalbail 				break;
1426e91bba0SGirish Moodalbail 		}
143a73be61aSHans Rosenfeld 		if (ifp == NULL) {
144a73be61aSHans Rosenfeld 			if ((status =
145a73be61aSHans Rosenfeld 			    i_ipadm_allocate_ifinfo(&ifp)) != IPADM_SUCCESS)
146a73be61aSHans Rosenfeld 					break;
147a73be61aSHans Rosenfeld 
1486e91bba0SGirish Moodalbail 			(void) strlcpy(ifp->ifi_name, lifrp->lifr_name,
1496e91bba0SGirish Moodalbail 			    sizeof (ifp->ifi_name));
150a73be61aSHans Rosenfeld 			/* Update the `ifi_next' pointer for this new node */
1516e91bba0SGirish Moodalbail 			if (*if_info == NULL)
152a73be61aSHans Rosenfeld 				*if_info = ifp;
1536e91bba0SGirish Moodalbail 			else
154a73be61aSHans Rosenfeld 				last->ifi_next = ifp;
155a73be61aSHans Rosenfeld 			last = ifp;
1566e91bba0SGirish Moodalbail 		}
1576e91bba0SGirish Moodalbail 
1586e91bba0SGirish Moodalbail 		/*
1596e91bba0SGirish Moodalbail 		 * Retrieve the flags for the interface by doing a
1606e91bba0SGirish Moodalbail 		 * SIOCGLIFFLAGS to populate the `ifi_cflags' field.
1616e91bba0SGirish Moodalbail 		 */
1626e91bba0SGirish Moodalbail 		(void) strlcpy(lifrl.lifr_name,
1636e91bba0SGirish Moodalbail 		    lifrp->lifr_name, sizeof (lifrl.lifr_name));
1646e91bba0SGirish Moodalbail 		s = (lifrp->lifr_addr.ss_family == AF_INET) ?
1656e91bba0SGirish Moodalbail 		    iph->iph_sock : iph->iph_sock6;
1666e91bba0SGirish Moodalbail 		if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
1676e91bba0SGirish Moodalbail 			continue;
168a73be61aSHans Rosenfeld 
169a73be61aSHans Rosenfeld 		/* a regular interface by default */
170a73be61aSHans Rosenfeld 		ifp->ifi_class = IPADM_IF_CLASS_REGULAR;
171a73be61aSHans Rosenfeld 
1726e91bba0SGirish Moodalbail 		if (lifrl.lifr_flags & IFF_BROADCAST)
1736e91bba0SGirish Moodalbail 			ifp->ifi_cflags |= IFIF_BROADCAST;
1746e91bba0SGirish Moodalbail 		if (lifrl.lifr_flags & IFF_MULTICAST)
1756e91bba0SGirish Moodalbail 			ifp->ifi_cflags |= IFIF_MULTICAST;
1766e91bba0SGirish Moodalbail 		if (lifrl.lifr_flags & IFF_POINTOPOINT)
1776e91bba0SGirish Moodalbail 			ifp->ifi_cflags |= IFIF_POINTOPOINT;
178a73be61aSHans Rosenfeld 		if (lifrl.lifr_flags & IFF_VIRTUAL) {
1796e91bba0SGirish Moodalbail 			ifp->ifi_cflags |= IFIF_VIRTUAL;
180a73be61aSHans Rosenfeld 			ifp->ifi_class = IPADM_IF_CLASS_VIRTUAL;
181a73be61aSHans Rosenfeld 		}
182a73be61aSHans Rosenfeld 		if (lifrl.lifr_flags & IFF_IPMP) {
1836e91bba0SGirish Moodalbail 			ifp->ifi_cflags |= IFIF_IPMP;
184a73be61aSHans Rosenfeld 			ifp->ifi_class = IPADM_IF_CLASS_IPMP;
185a73be61aSHans Rosenfeld 		}
1866e91bba0SGirish Moodalbail 		if (lifrl.lifr_flags & IFF_STANDBY)
1876e91bba0SGirish Moodalbail 			ifp->ifi_cflags |= IFIF_STANDBY;
1886e91bba0SGirish Moodalbail 		if (lifrl.lifr_flags & IFF_INACTIVE)
1896e91bba0SGirish Moodalbail 			ifp->ifi_cflags |= IFIF_INACTIVE;
1906e91bba0SGirish Moodalbail 		if (lifrl.lifr_flags & IFF_VRRP)
1916e91bba0SGirish Moodalbail 			ifp->ifi_cflags |= IFIF_VRRP;
1926e91bba0SGirish Moodalbail 		if (lifrl.lifr_flags & IFF_NOACCEPT)
1936e91bba0SGirish Moodalbail 			ifp->ifi_cflags |= IFIF_NOACCEPT;
1946e91bba0SGirish Moodalbail 		if (lifrl.lifr_flags & IFF_IPV4)
1956e91bba0SGirish Moodalbail 			ifp->ifi_cflags |= IFIF_IPV4;
1966e91bba0SGirish Moodalbail 		if (lifrl.lifr_flags & IFF_IPV6)
1976e91bba0SGirish Moodalbail 			ifp->ifi_cflags |= IFIF_IPV6;
198550b6e40SSowmini Varadhan 		if (lifrl.lifr_flags & IFF_L3PROTECT)
199550b6e40SSowmini Varadhan 			ifp->ifi_cflags |= IFIF_L3PROTECT;
200a73be61aSHans Rosenfeld 
201a73be61aSHans Rosenfeld 		/*
202a73be61aSHans Rosenfeld 		 * Retrieve active IPMP members. This may fail in in.mpathd if
203a73be61aSHans Rosenfeld 		 * the IPMP interface has just been created with no members.
204a73be61aSHans Rosenfeld 		 * Hence, ignore errors, cmembers will just be empty.
205a73be61aSHans Rosenfeld 		 */
206a73be61aSHans Rosenfeld 		if (ifp->ifi_class == IPADM_IF_CLASS_IPMP) {
207a73be61aSHans Rosenfeld 			if (ioctl(s, SIOCGLIFGROUPNAME, (caddr_t)&lifrl) == 0) {
208a73be61aSHans Rosenfeld 				(void) i_ipadm_fill_cmembers(
209a73be61aSHans Rosenfeld 				    lifrl.lifr_groupname,
210a73be61aSHans Rosenfeld 				    &ifp->ifi_ipmp_cmembers);
211a73be61aSHans Rosenfeld 			}
212a73be61aSHans Rosenfeld 		}
2136e91bba0SGirish Moodalbail 	}
2146e91bba0SGirish Moodalbail 	free(buf);
215a73be61aSHans Rosenfeld 	if (status != IPADM_SUCCESS) {
216a73be61aSHans Rosenfeld 		ipadm_free_if_info(*if_info);
217a73be61aSHans Rosenfeld 		*if_info = NULL;
218a73be61aSHans Rosenfeld 	}
2196e91bba0SGirish Moodalbail 	return (status);
2206e91bba0SGirish Moodalbail }
2216e91bba0SGirish Moodalbail 
2226e91bba0SGirish Moodalbail /*
2236e91bba0SGirish Moodalbail  * Returns the interface information for `ifname' in `if_info' from persistent
2246e91bba0SGirish Moodalbail  * config if `ifname' is non-null. Otherwise, it returns all the interfaces
2256e91bba0SGirish Moodalbail  * from persistent config in `if_info'.
2266e91bba0SGirish Moodalbail  */
2276e91bba0SGirish Moodalbail static ipadm_status_t
i_ipadm_persist_if_info(ipadm_handle_t iph,const char * ifname,ipadm_if_info_t ** if_info)2286e91bba0SGirish Moodalbail i_ipadm_persist_if_info(ipadm_handle_t iph, const char *ifname,
229a73be61aSHans Rosenfeld     ipadm_if_info_t **if_info)
2306e91bba0SGirish Moodalbail {
231a73be61aSHans Rosenfeld 	ipadm_status_t	status = IPADM_SUCCESS;
232a73be61aSHans Rosenfeld 	nvlist_t	*ifs_info_nvl;
2336e91bba0SGirish Moodalbail 
2346e91bba0SGirish Moodalbail 	*if_info = NULL;
2356e91bba0SGirish Moodalbail 
236a73be61aSHans Rosenfeld 	if ((status = i_ipadm_get_db_if(iph,
237a73be61aSHans Rosenfeld 	    ifname, &ifs_info_nvl)) != IPADM_SUCCESS)
238a73be61aSHans Rosenfeld 		return (status);
2396e91bba0SGirish Moodalbail 
240a73be61aSHans Rosenfeld 	assert(ifs_info_nvl != NULL);
241a73be61aSHans Rosenfeld 
242a73be61aSHans Rosenfeld 	return (i_ipadm_nvl2ifinfo(ifs_info_nvl, if_info));
243a73be61aSHans Rosenfeld }
244a73be61aSHans Rosenfeld 
245a73be61aSHans Rosenfeld static ipadm_status_t
i_ipadm_nvl2ifinfo(nvlist_t * ifs_info_nvl,ipadm_if_info_t ** if_info)246a73be61aSHans Rosenfeld i_ipadm_nvl2ifinfo(nvlist_t *ifs_info_nvl, ipadm_if_info_t **if_info)
247a73be61aSHans Rosenfeld {
248a73be61aSHans Rosenfeld 	ipadm_if_info_t *ific = NULL, *ifil = NULL;
249a73be61aSHans Rosenfeld 	nvlist_t	*if_info_nvl;
250a73be61aSHans Rosenfeld 	nvpair_t	*nvp;
251a73be61aSHans Rosenfeld 	char		*strval;
252a73be61aSHans Rosenfeld 	ipadm_status_t	status = IPADM_SUCCESS;
253a73be61aSHans Rosenfeld 	uint16_t	*families;
254a73be61aSHans Rosenfeld 	uint_t		nelem = 0;
255a73be61aSHans Rosenfeld 
256a73be61aSHans Rosenfeld 	for (nvp = nvlist_next_nvpair(ifs_info_nvl, NULL); nvp != NULL;
257a73be61aSHans Rosenfeld 	    nvp = nvlist_next_nvpair(ifs_info_nvl, nvp)) {
258a73be61aSHans Rosenfeld 		if (nvpair_value_nvlist(nvp, &if_info_nvl) != 0)
259a73be61aSHans Rosenfeld 			continue;
260a73be61aSHans Rosenfeld 
261a73be61aSHans Rosenfeld 		status = i_ipadm_allocate_ifinfo(&ific);
262a73be61aSHans Rosenfeld 		if (status != IPADM_SUCCESS) {
263a73be61aSHans Rosenfeld 			ipadm_free_if_info(*if_info);
2646e91bba0SGirish Moodalbail 			break;
2656e91bba0SGirish Moodalbail 		}
266a73be61aSHans Rosenfeld 		if (nvlist_lookup_string(if_info_nvl, IPADM_NVP_IFNAME,
267a73be61aSHans Rosenfeld 		    &strval) != 0) {
268a73be61aSHans Rosenfeld 			ipadm_free_if_info(ific);
269a73be61aSHans Rosenfeld 			ific = NULL;
270a73be61aSHans Rosenfeld 			continue;
271a73be61aSHans Rosenfeld 		}
272a73be61aSHans Rosenfeld 		(void) strlcpy(ific->ifi_name, strval,
273a73be61aSHans Rosenfeld 		    sizeof (ific->ifi_name));
274a73be61aSHans Rosenfeld 
275a73be61aSHans Rosenfeld 		if (nvlist_lookup_uint16_array(if_info_nvl,
276a73be61aSHans Rosenfeld 		    IPADM_NVP_FAMILIES, &families, &nelem) == 0) {
277a73be61aSHans Rosenfeld 			while (nelem-- > 0) {
278a73be61aSHans Rosenfeld 				if (families[nelem] == AF_INET)
279a73be61aSHans Rosenfeld 					ific->ifi_pflags |= IFIF_IPV4;
280a73be61aSHans Rosenfeld 				else if (families[nelem] == AF_INET6)
281a73be61aSHans Rosenfeld 					ific->ifi_pflags |= IFIF_IPV6;
282a73be61aSHans Rosenfeld 			}
283a73be61aSHans Rosenfeld 		}
284a73be61aSHans Rosenfeld 
285a73be61aSHans Rosenfeld 		if (nvlist_lookup_string(if_info_nvl,
286a73be61aSHans Rosenfeld 		    IPADM_NVP_IFCLASS, &strval) == 0)
287a73be61aSHans Rosenfeld 			ific->ifi_class = atoi(strval);
288a73be61aSHans Rosenfeld 		else
289a73be61aSHans Rosenfeld 			ific->ifi_class = IPADM_IF_CLASS_REGULAR;
290a73be61aSHans Rosenfeld 
291a73be61aSHans Rosenfeld 		if (ific->ifi_class == IPADM_IF_CLASS_IPMP)
292a73be61aSHans Rosenfeld 			/* do not expect any failures there */
293a73be61aSHans Rosenfeld 			(void) i_ipadm_fill_pmembers(if_info_nvl,
294a73be61aSHans Rosenfeld 			    &ific->ifi_ipmp_pmembers);
295a73be61aSHans Rosenfeld 
296a73be61aSHans Rosenfeld 		if (*if_info == NULL)
297a73be61aSHans Rosenfeld 			*if_info = ific;
298a73be61aSHans Rosenfeld 		else
299a73be61aSHans Rosenfeld 			ifil->ifi_next = ific;
300a73be61aSHans Rosenfeld 		ifil = ific;
3016e91bba0SGirish Moodalbail 	}
302a73be61aSHans Rosenfeld 
303a73be61aSHans Rosenfeld 	nvlist_free(ifs_info_nvl);
3046e91bba0SGirish Moodalbail 	return (status);
3056e91bba0SGirish Moodalbail }
3066e91bba0SGirish Moodalbail 
307a73be61aSHans Rosenfeld /*
308a73be61aSHans Rosenfeld  * Fill the ipadm_if_info_t->ifi_ipmp_pmembers by info from
309a73be61aSHans Rosenfeld  * ipadm DB
310a73be61aSHans Rosenfeld  */
311a73be61aSHans Rosenfeld static ipadm_status_t
i_ipadm_fill_pmembers(nvlist_t * if_info_nvl,ipadm_ipmp_members_t * pmembers)312a73be61aSHans Rosenfeld i_ipadm_fill_pmembers(nvlist_t *if_info_nvl, ipadm_ipmp_members_t *pmembers)
313a73be61aSHans Rosenfeld {
314a73be61aSHans Rosenfeld 	uint_t	nelem = 0;
315a73be61aSHans Rosenfeld 	char	**members;
316a73be61aSHans Rosenfeld 	ipadm_ipmp_member_t *ipmp_member;
317a73be61aSHans Rosenfeld 
318a73be61aSHans Rosenfeld 	if (nvlist_lookup_string_array(if_info_nvl, IPADM_NVP_MIFNAMES,
319a73be61aSHans Rosenfeld 	    &members, &nelem) != 0)
320a73be61aSHans Rosenfeld 		return (IPADM_SUCCESS);
321a73be61aSHans Rosenfeld 
322a73be61aSHans Rosenfeld 	while (nelem-- > 0) {
323a73be61aSHans Rosenfeld 		if ((ipmp_member = calloc(1,
324a73be61aSHans Rosenfeld 		    sizeof (ipadm_ipmp_member_t))) == NULL)
325a73be61aSHans Rosenfeld 			return (ipadm_errno2status(errno));
326a73be61aSHans Rosenfeld 
327a73be61aSHans Rosenfeld 		(void) strlcpy(ipmp_member->if_name, members[nelem],
328a73be61aSHans Rosenfeld 		    sizeof (ipmp_member->if_name));
329a73be61aSHans Rosenfeld 		list_insert_tail(pmembers, ipmp_member);
330a73be61aSHans Rosenfeld 	}
331a73be61aSHans Rosenfeld 	return (IPADM_SUCCESS);
332a73be61aSHans Rosenfeld }
333a73be61aSHans Rosenfeld 
334a73be61aSHans Rosenfeld /*
335a73be61aSHans Rosenfeld  * Fill the ipadm_if_info_t->ifi_ipmp_cmembers by info from
336a73be61aSHans Rosenfeld  * kernel (libipmp is used to retrieve the required info)
337a73be61aSHans Rosenfeld  */
338a73be61aSHans Rosenfeld static ipadm_status_t
i_ipadm_fill_cmembers(char * grname,ipadm_ipmp_members_t * cmembers)339a73be61aSHans Rosenfeld i_ipadm_fill_cmembers(char *grname, ipadm_ipmp_members_t *cmembers)
340a73be61aSHans Rosenfeld {
341a73be61aSHans Rosenfeld 	ipmp_handle_t ipmp_handle;
342a73be61aSHans Rosenfeld 	ipmp_groupinfo_t *grinfo;
343a73be61aSHans Rosenfeld 	ipmp_iflist_t *iflistp;
344a73be61aSHans Rosenfeld 	ipadm_ipmp_member_t *ipmp_member;
345a73be61aSHans Rosenfeld 	ipadm_status_t ipadm_status = IPADM_SUCCESS;
346a73be61aSHans Rosenfeld 	int i;
347a73be61aSHans Rosenfeld 
348a73be61aSHans Rosenfeld 	if (ipmp_open(&ipmp_handle) != IPMP_SUCCESS)
349a73be61aSHans Rosenfeld 		return (IPADM_FAILURE);
350a73be61aSHans Rosenfeld 
351a73be61aSHans Rosenfeld 	if (ipmp_getgroupinfo(ipmp_handle, grname, &grinfo) != IPMP_SUCCESS) {
352a73be61aSHans Rosenfeld 		ipadm_status = IPADM_FAILURE;
353a73be61aSHans Rosenfeld 		goto fail2;
354a73be61aSHans Rosenfeld 	}
355a73be61aSHans Rosenfeld 
356a73be61aSHans Rosenfeld 	iflistp = grinfo->gr_iflistp;
357a73be61aSHans Rosenfeld 	for (i = 0; i < iflistp->il_nif; i++) {
358a73be61aSHans Rosenfeld 		if ((ipmp_member = calloc(1,
359a73be61aSHans Rosenfeld 		    sizeof (ipadm_ipmp_member_t))) == NULL) {
360a73be61aSHans Rosenfeld 			ipadm_status = ipadm_errno2status(errno);
361a73be61aSHans Rosenfeld 			goto fail1;
362a73be61aSHans Rosenfeld 		}
363a73be61aSHans Rosenfeld 		(void) strlcpy(ipmp_member->if_name, iflistp->il_ifs[i],
364a73be61aSHans Rosenfeld 		    sizeof (ipmp_member->if_name));
365a73be61aSHans Rosenfeld 		list_insert_tail(cmembers, ipmp_member);
366a73be61aSHans Rosenfeld 	}
367a73be61aSHans Rosenfeld 
368a73be61aSHans Rosenfeld fail1:
369a73be61aSHans Rosenfeld 	ipmp_freegroupinfo(grinfo);
370a73be61aSHans Rosenfeld fail2:
371a73be61aSHans Rosenfeld 	ipmp_close(ipmp_handle);
372a73be61aSHans Rosenfeld 	return (ipadm_status);
373a73be61aSHans Rosenfeld }
374a73be61aSHans Rosenfeld 
3756e91bba0SGirish Moodalbail /*
3766e91bba0SGirish Moodalbail  * Collects information for `ifname' if one is specified from both
3776e91bba0SGirish Moodalbail  * active and persistent config in `if_info'. If no `ifname' is specified,
3786e91bba0SGirish Moodalbail  * this returns all the interfaces in active and persistent config in
3796e91bba0SGirish Moodalbail  * `if_info'.
3806e91bba0SGirish Moodalbail  */
3816e91bba0SGirish Moodalbail ipadm_status_t
i_ipadm_get_all_if_info(ipadm_handle_t iph,const char * ifname,ipadm_if_info_t ** if_info,int64_t lifc_flags)3826e91bba0SGirish Moodalbail i_ipadm_get_all_if_info(ipadm_handle_t iph, const char *ifname,
383a73be61aSHans Rosenfeld     ipadm_if_info_t **if_info, int64_t lifc_flags)
3846e91bba0SGirish Moodalbail {
3856e91bba0SGirish Moodalbail 	ipadm_status_t	status;
386a73be61aSHans Rosenfeld 	ipadm_if_info_t	*aifinfo = NULL;
387a73be61aSHans Rosenfeld 	ipadm_if_info_t	*pifinfo = NULL;
3886e91bba0SGirish Moodalbail 	ipadm_if_info_t	*aifp;
3896e91bba0SGirish Moodalbail 	ipadm_if_info_t	*pifp;
390a73be61aSHans Rosenfeld 	ipadm_if_info_t	*last = NULL;
3916e91bba0SGirish Moodalbail 	struct ifaddrs	*ifa;
3926e91bba0SGirish Moodalbail 	struct ifaddrs	*ifap;
3936e91bba0SGirish Moodalbail 
3946e91bba0SGirish Moodalbail 	/*
3956e91bba0SGirish Moodalbail 	 * Retrive the information for the requested `ifname' or all
3966e91bba0SGirish Moodalbail 	 * interfaces from active configuration.
3976e91bba0SGirish Moodalbail 	 */
3986e91bba0SGirish Moodalbail retry:
3996e91bba0SGirish Moodalbail 	status = i_ipadm_active_if_info(iph, ifname, &aifinfo, lifc_flags);
4006e91bba0SGirish Moodalbail 	if (status != IPADM_SUCCESS)
4016e91bba0SGirish Moodalbail 		return (status);
4026e91bba0SGirish Moodalbail 	/* Get the interface state for each interface in `aifinfo'. */
4036e91bba0SGirish Moodalbail 	if (aifinfo != NULL) {
4046e91bba0SGirish Moodalbail 		/* We need all addresses to get the interface state */
4056e91bba0SGirish Moodalbail 		if (getallifaddrs(AF_UNSPEC, &ifa, (LIFC_NOXMIT|LIFC_TEMPORARY|
4066e91bba0SGirish Moodalbail 		    LIFC_ALLZONES|LIFC_UNDER_IPMP)) != 0) {
4076e91bba0SGirish Moodalbail 			status = ipadm_errno2status(errno);
4086e91bba0SGirish Moodalbail 			goto fail;
4096e91bba0SGirish Moodalbail 		}
410a73be61aSHans Rosenfeld 		for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
4116e91bba0SGirish Moodalbail 			/*
4126e91bba0SGirish Moodalbail 			 * Find the `ifaddrs' structure from `ifa'
4136e91bba0SGirish Moodalbail 			 * for this interface. We need the IFF_* flags
4146e91bba0SGirish Moodalbail 			 * to find the interface state.
4156e91bba0SGirish Moodalbail 			 */
4166e91bba0SGirish Moodalbail 			for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
417*3ee59242SSebastian Wiedenroth 				if (ifap->ifa_addr->sa_family == AF_LINK)
418*3ee59242SSebastian Wiedenroth 					continue;
4196e91bba0SGirish Moodalbail 				if (strcmp(ifap->ifa_name, aifp->ifi_name) == 0)
4206e91bba0SGirish Moodalbail 					break;
4216e91bba0SGirish Moodalbail 			}
4226e91bba0SGirish Moodalbail 			if (ifap == NULL) {
4236e91bba0SGirish Moodalbail 				/*
4246e91bba0SGirish Moodalbail 				 * The interface might have been removed
4256e91bba0SGirish Moodalbail 				 * from kernel. Retry getting all the active
4266e91bba0SGirish Moodalbail 				 * interfaces.
4276e91bba0SGirish Moodalbail 				 */
4286e91bba0SGirish Moodalbail 				freeifaddrs(ifa);
4296e91bba0SGirish Moodalbail 				ipadm_free_if_info(aifinfo);
4306e91bba0SGirish Moodalbail 				aifinfo = NULL;
4316e91bba0SGirish Moodalbail 				goto retry;
4326e91bba0SGirish Moodalbail 			}
4336e91bba0SGirish Moodalbail 			if (!(ifap->ifa_flags & IFF_RUNNING) ||
4346e91bba0SGirish Moodalbail 			    (ifap->ifa_flags & IFF_FAILED))
4356e91bba0SGirish Moodalbail 				aifp->ifi_state = IFIS_FAILED;
4366e91bba0SGirish Moodalbail 			else if (ifap->ifa_flags & IFF_OFFLINE)
4376e91bba0SGirish Moodalbail 				aifp->ifi_state = IFIS_OFFLINE;
4386e91bba0SGirish Moodalbail 			else if (i_ipadm_is_if_down(aifp->ifi_name, ifa))
4396e91bba0SGirish Moodalbail 				aifp->ifi_state = IFIS_DOWN;
4406e91bba0SGirish Moodalbail 			else
4416e91bba0SGirish Moodalbail 				aifp->ifi_state = IFIS_OK;
442a73be61aSHans Rosenfeld 			if (aifp->ifi_next == NULL)
443a73be61aSHans Rosenfeld 				last = aifp;
4446e91bba0SGirish Moodalbail 		}
4456e91bba0SGirish Moodalbail 		freeifaddrs(ifa);
4466e91bba0SGirish Moodalbail 	}
4476e91bba0SGirish Moodalbail 	/*
4486e91bba0SGirish Moodalbail 	 * Get the persistent interface information in `pifinfo'.
4496e91bba0SGirish Moodalbail 	 */
4506e91bba0SGirish Moodalbail 	status = i_ipadm_persist_if_info(iph, ifname, &pifinfo);
4516e91bba0SGirish Moodalbail 	if (status == IPADM_NOTFOUND) {
4526e91bba0SGirish Moodalbail 		*if_info = aifinfo;
4536e91bba0SGirish Moodalbail 		return (IPADM_SUCCESS);
4546e91bba0SGirish Moodalbail 	}
4556e91bba0SGirish Moodalbail 	if (status != IPADM_SUCCESS)
4566e91bba0SGirish Moodalbail 		goto fail;
457a73be61aSHans Rosenfeld 
4586e91bba0SGirish Moodalbail 	/*
459a73be61aSHans Rosenfeld 	 * Process the persistent interface information.
460a73be61aSHans Rosenfeld 	 *
461a73be61aSHans Rosenfeld 	 * First try to get the persistent "standby" property, as that isn't
462a73be61aSHans Rosenfeld 	 * retrieved by i_ipadm_persist_if_info().
463a73be61aSHans Rosenfeld 	 *
464a73be61aSHans Rosenfeld 	 * Next, if a persistent interface is also found in `aifinfo', update
4656e91bba0SGirish Moodalbail 	 * its entry in `aifinfo' with the persistent information from
4666e91bba0SGirish Moodalbail 	 * `pifinfo'. If an interface is found in `pifinfo', but not in
4676e91bba0SGirish Moodalbail 	 * `aifinfo', it means that this interface was disabled. We should
4686e91bba0SGirish Moodalbail 	 * add this interface to `aifinfo' and set it state to IFIF_DISABLED.
4696e91bba0SGirish Moodalbail 	 */
470a73be61aSHans Rosenfeld 	for (pifp = pifinfo; pifp != NULL; pifp = pifp->ifi_next) {
471a73be61aSHans Rosenfeld 		char buf[10] = "";
472a73be61aSHans Rosenfeld 		uint_t bufsize = sizeof (buf);
473a73be61aSHans Rosenfeld 
474a73be61aSHans Rosenfeld 		status = ipadm_get_ifprop(iph, pifp->ifi_name, "standby", buf,
475a73be61aSHans Rosenfeld 		    &bufsize, MOD_PROTO_IP, IPADM_OPT_PERSIST);
476a73be61aSHans Rosenfeld 
477a73be61aSHans Rosenfeld 		if (status == IPADM_SUCCESS && strcmp(buf, "on") == 0)
478a73be61aSHans Rosenfeld 			pifp->ifi_pflags |= IFIF_STANDBY;
479a73be61aSHans Rosenfeld 
480a73be61aSHans Rosenfeld 		for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
4816e91bba0SGirish Moodalbail 			if (strcmp(aifp->ifi_name, pifp->ifi_name) == 0) {
4826e91bba0SGirish Moodalbail 				break;
4836e91bba0SGirish Moodalbail 			}
4846e91bba0SGirish Moodalbail 		}
485a73be61aSHans Rosenfeld 
486a73be61aSHans Rosenfeld 		if (aifp == NULL) {
487a73be61aSHans Rosenfeld 			if ((status =
488a73be61aSHans Rosenfeld 			    i_ipadm_allocate_ifinfo(&aifp)) != IPADM_SUCCESS)
4896e91bba0SGirish Moodalbail 				goto fail;
490a73be61aSHans Rosenfeld 
491a73be61aSHans Rosenfeld 			(void) strlcpy(aifp->ifi_name, pifp->ifi_name,
492a73be61aSHans Rosenfeld 			    sizeof (aifp->ifi_name));
493a73be61aSHans Rosenfeld 
494a73be61aSHans Rosenfeld 			aifp->ifi_next = NULL;
495a73be61aSHans Rosenfeld 			aifp->ifi_state = IFIS_DISABLED;
4966e91bba0SGirish Moodalbail 			if (last != NULL)
497a73be61aSHans Rosenfeld 				last->ifi_next = aifp;
4986e91bba0SGirish Moodalbail 			else
499a73be61aSHans Rosenfeld 				aifinfo = aifp;
500a73be61aSHans Rosenfeld 			last = aifp;
5016e91bba0SGirish Moodalbail 		}
502a73be61aSHans Rosenfeld 
503a73be61aSHans Rosenfeld 		if ((status = i_ipadm_add_persistent_if_info(aifp,
504a73be61aSHans Rosenfeld 		    pifp)) != IPADM_SUCCESS)
505a73be61aSHans Rosenfeld 			goto fail;
5066e91bba0SGirish Moodalbail 	}
5076e91bba0SGirish Moodalbail 	*if_info = aifinfo;
5086e91bba0SGirish Moodalbail 	ipadm_free_if_info(pifinfo);
5096e91bba0SGirish Moodalbail 	return (IPADM_SUCCESS);
5106e91bba0SGirish Moodalbail fail:
5116e91bba0SGirish Moodalbail 	*if_info = NULL;
5126e91bba0SGirish Moodalbail 	ipadm_free_if_info(aifinfo);
5136e91bba0SGirish Moodalbail 	ipadm_free_if_info(pifinfo);
5146e91bba0SGirish Moodalbail 	return (status);
5156e91bba0SGirish Moodalbail }
5166e91bba0SGirish Moodalbail 
517a73be61aSHans Rosenfeld /*
518a73be61aSHans Rosenfeld  * Updates active if_info by data from persistent if_info
519a73be61aSHans Rosenfeld  */
520a73be61aSHans Rosenfeld static ipadm_status_t
i_ipadm_add_persistent_if_info(ipadm_if_info_t * aifp,ipadm_if_info_t * pifp)521a73be61aSHans Rosenfeld i_ipadm_add_persistent_if_info(ipadm_if_info_t *aifp, ipadm_if_info_t *pifp)
522a73be61aSHans Rosenfeld {
523a73be61aSHans Rosenfeld 	ipadm_ipmp_member_t *pp_ipmp_member, *ap_ipmp_member;
524a73be61aSHans Rosenfeld 
525a73be61aSHans Rosenfeld 	ipadm_ipmp_members_t *apmembers = &aifp->ifi_ipmp_pmembers;
526a73be61aSHans Rosenfeld 	ipadm_ipmp_members_t *ppmembers = &pifp->ifi_ipmp_pmembers;
527a73be61aSHans Rosenfeld 
528a73be61aSHans Rosenfeld 	aifp->ifi_pflags = pifp->ifi_pflags;
529a73be61aSHans Rosenfeld 	aifp->ifi_class = pifp->ifi_class;
530a73be61aSHans Rosenfeld 
531a73be61aSHans Rosenfeld 	for (pp_ipmp_member = list_head(ppmembers); pp_ipmp_member;
532a73be61aSHans Rosenfeld 	    pp_ipmp_member = list_next(ppmembers, pp_ipmp_member)) {
533a73be61aSHans Rosenfeld 		if ((ap_ipmp_member = calloc(1,
534a73be61aSHans Rosenfeld 		    sizeof (ipadm_ipmp_member_t))) == NULL)
535a73be61aSHans Rosenfeld 			return (ipadm_errno2status(errno));
536a73be61aSHans Rosenfeld 
537a73be61aSHans Rosenfeld 		(void) strlcpy(ap_ipmp_member->if_name,
538a73be61aSHans Rosenfeld 		    pp_ipmp_member->if_name,
539a73be61aSHans Rosenfeld 		    sizeof (ap_ipmp_member->if_name));
540a73be61aSHans Rosenfeld 
541a73be61aSHans Rosenfeld 		list_insert_tail(apmembers, ap_ipmp_member);
542a73be61aSHans Rosenfeld 	}
543a73be61aSHans Rosenfeld 	return (IPADM_SUCCESS);
544a73be61aSHans Rosenfeld }
545a73be61aSHans Rosenfeld 
546a73be61aSHans Rosenfeld static ipadm_status_t
i_ipadm_allocate_ifinfo(ipadm_if_info_t ** if_info)547a73be61aSHans Rosenfeld i_ipadm_allocate_ifinfo(ipadm_if_info_t **if_info)
548a73be61aSHans Rosenfeld {
549a73be61aSHans Rosenfeld 	*if_info = calloc(1, sizeof (ipadm_if_info_t));
550a73be61aSHans Rosenfeld 	if (*if_info == NULL)
551a73be61aSHans Rosenfeld 		return (ipadm_errno2status(errno));
552a73be61aSHans Rosenfeld 
553a73be61aSHans Rosenfeld 	/* List of active (current) members */
554a73be61aSHans Rosenfeld 	list_create(&((*if_info)->ifi_ipmp_cmembers),
555a73be61aSHans Rosenfeld 	    sizeof (ipadm_ipmp_member_t),
556a73be61aSHans Rosenfeld 	    offsetof(ipadm_ipmp_member_t, node));
557a73be61aSHans Rosenfeld 
558a73be61aSHans Rosenfeld 	/* List of persistent members */
559a73be61aSHans Rosenfeld 	list_create(&((*if_info)->ifi_ipmp_pmembers),
560a73be61aSHans Rosenfeld 	    sizeof (ipadm_ipmp_member_t),
561a73be61aSHans Rosenfeld 	    offsetof(ipadm_ipmp_member_t, node));
562a73be61aSHans Rosenfeld 
563a73be61aSHans Rosenfeld 	return (IPADM_SUCCESS);
564a73be61aSHans Rosenfeld }
565a73be61aSHans Rosenfeld 
566a73be61aSHans Rosenfeld /*
567a73be61aSHans Rosenfeld  * Reads all the interface lines from the persistent DB into the nvlist `onvl',
568a73be61aSHans Rosenfeld  * when `ifname' is NULL.
569a73be61aSHans Rosenfeld  * If an `ifname' is specified, then the interface line corresponding to
570a73be61aSHans Rosenfeld  * that name will be returned.
571a73be61aSHans Rosenfeld  */
572a73be61aSHans Rosenfeld static ipadm_status_t
i_ipadm_get_db_if(ipadm_handle_t iph,const char * ifname,nvlist_t ** onvl)573a73be61aSHans Rosenfeld i_ipadm_get_db_if(ipadm_handle_t iph, const char *ifname, nvlist_t **onvl)
574a73be61aSHans Rosenfeld {
575a73be61aSHans Rosenfeld 	ipmgmt_getif_arg_t	garg;
576a73be61aSHans Rosenfeld 
577a73be61aSHans Rosenfeld 	/* Populate the door_call argument structure */
578a73be61aSHans Rosenfeld 	bzero(&garg, sizeof (garg));
579a73be61aSHans Rosenfeld 	garg.ia_cmd = IPMGMT_CMD_GETIF;
580a73be61aSHans Rosenfeld 	if (ifname != NULL)
581a73be61aSHans Rosenfeld 		(void) strlcpy(garg.ia_ifname, ifname, sizeof (garg.ia_ifname));
582a73be61aSHans Rosenfeld 
583a73be61aSHans Rosenfeld 	return (i_ipadm_call_ipmgmtd(iph, (void *) &garg, sizeof (garg), onvl));
584a73be61aSHans Rosenfeld }
585a73be61aSHans Rosenfeld 
5866e91bba0SGirish Moodalbail int
i_ipadm_get_lnum(const char * ifname)5876e91bba0SGirish Moodalbail i_ipadm_get_lnum(const char *ifname)
5886e91bba0SGirish Moodalbail {
5896e91bba0SGirish Moodalbail 	char *num = strrchr(ifname, IPADM_LOGICAL_SEP);
5906e91bba0SGirish Moodalbail 
5916e91bba0SGirish Moodalbail 	if (num == NULL)
5926e91bba0SGirish Moodalbail 		return (0);
5936e91bba0SGirish Moodalbail 
5946e91bba0SGirish Moodalbail 	return (atoi(++num));
5956e91bba0SGirish Moodalbail }
5966e91bba0SGirish Moodalbail 
5976e91bba0SGirish Moodalbail /*
5986e91bba0SGirish Moodalbail  * Sets the output argument `exists' to true or false based on whether
5996e91bba0SGirish Moodalbail  * any persistent configuration is available for `ifname' and returns
6006e91bba0SGirish Moodalbail  * IPADM_SUCCESS as status. If the persistent information cannot be retrieved,
6016e91bba0SGirish Moodalbail  * `exists' is unmodified and an error status is returned.
6026e91bba0SGirish Moodalbail  */
6036e91bba0SGirish Moodalbail ipadm_status_t
i_ipadm_if_pexists(ipadm_handle_t iph,const char * ifname,sa_family_t af,boolean_t * exists)6046e91bba0SGirish Moodalbail i_ipadm_if_pexists(ipadm_handle_t iph, const char *ifname, sa_family_t af,
6056e91bba0SGirish Moodalbail     boolean_t *exists)
6066e91bba0SGirish Moodalbail {
607a73be61aSHans Rosenfeld 	ipadm_if_info_t	*ifinfo;
6086e91bba0SGirish Moodalbail 	ipadm_status_t	status;
6096e91bba0SGirish Moodalbail 
610550b6e40SSowmini Varadhan 	/*
611550b6e40SSowmini Varadhan 	 * if IPH_IPMGMTD is set, we know that the caller (ipmgmtd) already
612550b6e40SSowmini Varadhan 	 * knows about persistent configuration in the first place, so we
613550b6e40SSowmini Varadhan 	 * just return success.
614550b6e40SSowmini Varadhan 	 */
615550b6e40SSowmini Varadhan 	if (iph->iph_flags & IPH_IPMGMTD) {
616550b6e40SSowmini Varadhan 		*exists = B_FALSE;
617550b6e40SSowmini Varadhan 		return (IPADM_SUCCESS);
618550b6e40SSowmini Varadhan 	}
6196e91bba0SGirish Moodalbail 	status = i_ipadm_persist_if_info(iph, ifname, &ifinfo);
6206e91bba0SGirish Moodalbail 	if (status == IPADM_SUCCESS) {
6216e91bba0SGirish Moodalbail 		*exists = ((af == AF_INET &&
622a73be61aSHans Rosenfeld 		    (ifinfo->ifi_pflags & IFIF_IPV4)) ||
6236e91bba0SGirish Moodalbail 		    (af == AF_INET6 &&
624a73be61aSHans Rosenfeld 		    (ifinfo->ifi_pflags & IFIF_IPV6)));
625a73be61aSHans Rosenfeld 		ipadm_free_if_info(ifinfo);
6266e91bba0SGirish Moodalbail 	} else if (status == IPADM_NOTFOUND) {
6276e91bba0SGirish Moodalbail 		status = IPADM_SUCCESS;
6286e91bba0SGirish Moodalbail 		*exists = B_FALSE;
6296e91bba0SGirish Moodalbail 	}
6306e91bba0SGirish Moodalbail 	return (status);
6316e91bba0SGirish Moodalbail }
6326e91bba0SGirish Moodalbail 
6336e91bba0SGirish Moodalbail /*
6346e91bba0SGirish Moodalbail  * Open "/dev/udp{,6}" for use as a multiplexor to PLINK the interface stream
6356e91bba0SGirish Moodalbail  * under. We use "/dev/udp" instead of "/dev/ip" since STREAMS will not let
6366e91bba0SGirish Moodalbail  * you PLINK a driver under itself, and "/dev/ip" is typically the driver at
6376e91bba0SGirish Moodalbail  * the bottom of the stream for tunneling interfaces.
6386e91bba0SGirish Moodalbail  */
6396e91bba0SGirish Moodalbail ipadm_status_t
ipadm_open_arp_on_udp(const char * udp_dev_name,int * fd)6406e91bba0SGirish Moodalbail ipadm_open_arp_on_udp(const char *udp_dev_name, int *fd)
6416e91bba0SGirish Moodalbail {
6426e91bba0SGirish Moodalbail 	int err;
6436e91bba0SGirish Moodalbail 
6446e91bba0SGirish Moodalbail 	if ((*fd = open(udp_dev_name, O_RDWR)) == -1)
6456e91bba0SGirish Moodalbail 		return (ipadm_errno2status(errno));
6466e91bba0SGirish Moodalbail 
6476e91bba0SGirish Moodalbail 	/*
6486e91bba0SGirish Moodalbail 	 * Pop off all undesired modules (note that the user may have
6496e91bba0SGirish Moodalbail 	 * configured autopush to add modules above udp), and push the
6506e91bba0SGirish Moodalbail 	 * arp module onto the resulting stream. This is used to make
6516e91bba0SGirish Moodalbail 	 * IP+ARP be able to atomically track the muxid for the I_PLINKed
6526e91bba0SGirish Moodalbail 	 * STREAMS, thus it isn't related to ARP running the ARP protocol.
6536e91bba0SGirish Moodalbail 	 */
6546e91bba0SGirish Moodalbail 	while (ioctl(*fd, I_POP, 0) != -1)
6556e91bba0SGirish Moodalbail 		;
6566e91bba0SGirish Moodalbail 	if (errno == EINVAL && ioctl(*fd, I_PUSH, ARP_MOD_NAME) != -1)
6576e91bba0SGirish Moodalbail 		return (IPADM_SUCCESS);
6586e91bba0SGirish Moodalbail 	err = errno;
6596e91bba0SGirish Moodalbail 	(void) close(*fd);
6606e91bba0SGirish Moodalbail 
6616e91bba0SGirish Moodalbail 	return (ipadm_errno2status(err));
6626e91bba0SGirish Moodalbail }
6636e91bba0SGirish Moodalbail 
6646e91bba0SGirish Moodalbail /*
6656e91bba0SGirish Moodalbail  * i_ipadm_create_ipmp() is called from i_ipadm_create_ipmp_peer() when an
6666e91bba0SGirish Moodalbail  * underlying interface in an ipmp group G is plumbed for an address family,
6676e91bba0SGirish Moodalbail  * but the meta-interface for the other address family `af' does not exist
6686e91bba0SGirish Moodalbail  * yet for the group G. If `af' is IPv6, we need to bring up the
6696e91bba0SGirish Moodalbail  * link-local address.
6706e91bba0SGirish Moodalbail  */
6716e91bba0SGirish Moodalbail static ipadm_status_t
i_ipadm_create_ipmp(ipadm_handle_t iph,char * ifname,sa_family_t af,const char * grname,uint32_t ipadm_flags)6726e91bba0SGirish Moodalbail i_ipadm_create_ipmp(ipadm_handle_t iph, char *ifname, sa_family_t af,
6736e91bba0SGirish Moodalbail     const char *grname, uint32_t ipadm_flags)
6746e91bba0SGirish Moodalbail {
6756e91bba0SGirish Moodalbail 	ipadm_status_t	status;
6766e91bba0SGirish Moodalbail 	struct lifreq	lifr;
6776e91bba0SGirish Moodalbail 	int		sock;
6786e91bba0SGirish Moodalbail 	int		err;
6796e91bba0SGirish Moodalbail 
6806e91bba0SGirish Moodalbail 	assert(ipadm_flags & IPADM_OPT_IPMP);
6816e91bba0SGirish Moodalbail 
6826e91bba0SGirish Moodalbail 	/* Create the ipmp underlying interface */
6836e91bba0SGirish Moodalbail 	status = i_ipadm_create_if(iph, ifname, af, ipadm_flags);
6846e91bba0SGirish Moodalbail 	if (status != IPADM_SUCCESS && status != IPADM_IF_EXISTS)
6856e91bba0SGirish Moodalbail 		return (status);
6866e91bba0SGirish Moodalbail 
6876e91bba0SGirish Moodalbail 	/*
6886e91bba0SGirish Moodalbail 	 * To preserve backward-compatibility, always bring up the link-local
6896e91bba0SGirish Moodalbail 	 * address for implicitly-created IPv6 IPMP interfaces.
6906e91bba0SGirish Moodalbail 	 */
6916e91bba0SGirish Moodalbail 	if (af == AF_INET6)
6926e91bba0SGirish Moodalbail 		(void) i_ipadm_set_flags(iph, ifname, AF_INET6, IFF_UP, 0);
6936e91bba0SGirish Moodalbail 
6946e91bba0SGirish Moodalbail 	sock = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
6956e91bba0SGirish Moodalbail 	/*
6966e91bba0SGirish Moodalbail 	 * If the caller requested a different group name, issue a
6976e91bba0SGirish Moodalbail 	 * SIOCSLIFGROUPNAME on the new IPMP interface.
6986e91bba0SGirish Moodalbail 	 */
6996e91bba0SGirish Moodalbail 	bzero(&lifr, sizeof (lifr));
7006e91bba0SGirish Moodalbail 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
7016e91bba0SGirish Moodalbail 	if (strcmp(lifr.lifr_name, grname) != 0) {
7026e91bba0SGirish Moodalbail 		(void) strlcpy(lifr.lifr_groupname, grname, LIFGRNAMSIZ);
7036e91bba0SGirish Moodalbail 		if (ioctl(sock, SIOCSLIFGROUPNAME, &lifr) == -1) {
7046e91bba0SGirish Moodalbail 			err = errno;
7056e91bba0SGirish Moodalbail 			/* Remove the interface we created. */
7066e91bba0SGirish Moodalbail 			if (status == IPADM_SUCCESS) {
7076e91bba0SGirish Moodalbail 				(void) i_ipadm_delete_if(iph, ifname, af,
7086e91bba0SGirish Moodalbail 				    ipadm_flags);
7096e91bba0SGirish Moodalbail 			}
7106e91bba0SGirish Moodalbail 			return (ipadm_errno2status(err));
7116e91bba0SGirish Moodalbail 		}
7126e91bba0SGirish Moodalbail 	}
7136e91bba0SGirish Moodalbail 
7146e91bba0SGirish Moodalbail 	return (IPADM_SUCCESS);
7156e91bba0SGirish Moodalbail }
7166e91bba0SGirish Moodalbail 
7176e91bba0SGirish Moodalbail /*
7186e91bba0SGirish Moodalbail  * Checks if `ifname' is plumbed and in an IPMP group on its "other" address
7196e91bba0SGirish Moodalbail  * family.  If so, create a matching IPMP group for address family `af'.
7206e91bba0SGirish Moodalbail  */
7216e91bba0SGirish Moodalbail static ipadm_status_t
i_ipadm_create_ipmp_peer(ipadm_handle_t iph,char * ifname,sa_family_t af)7226e91bba0SGirish Moodalbail i_ipadm_create_ipmp_peer(ipadm_handle_t iph, char *ifname, sa_family_t af)
7236e91bba0SGirish Moodalbail {
7246e91bba0SGirish Moodalbail 	lifgroupinfo_t	lifgr;
7256e91bba0SGirish Moodalbail 	ipadm_status_t	status = IPADM_SUCCESS;
7266e91bba0SGirish Moodalbail 	struct lifreq	lifr;
727ed1e9379SHans Rosenfeld 	int		other_af_sock;
7286e91bba0SGirish Moodalbail 
7296e91bba0SGirish Moodalbail 	assert(af == AF_INET || af == AF_INET6);
7306e91bba0SGirish Moodalbail 
7316e91bba0SGirish Moodalbail 	other_af_sock = (af == AF_INET ? iph->iph_sock6 : iph->iph_sock);
7326e91bba0SGirish Moodalbail 
7336e91bba0SGirish Moodalbail 	/*
7346e91bba0SGirish Moodalbail 	 * iph is the handle for the interface that we are trying to plumb.
7356e91bba0SGirish Moodalbail 	 * other_af_sock is the socket for the "other" address family.
7366e91bba0SGirish Moodalbail 	 */
7376e91bba0SGirish Moodalbail 	bzero(&lifr, sizeof (lifr));
7386e91bba0SGirish Moodalbail 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
7396e91bba0SGirish Moodalbail 	if (ioctl(other_af_sock, SIOCGLIFGROUPNAME, &lifr) != 0)
7406e91bba0SGirish Moodalbail 		return (IPADM_SUCCESS);
7416e91bba0SGirish Moodalbail 
7426e91bba0SGirish Moodalbail 	(void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname, LIFGRNAMSIZ);
7436e91bba0SGirish Moodalbail 	if (ioctl(other_af_sock, SIOCGLIFGROUPINFO, &lifgr) != 0)
7446e91bba0SGirish Moodalbail 		return (IPADM_SUCCESS);
7456e91bba0SGirish Moodalbail 
7466e91bba0SGirish Moodalbail 	/*
7476e91bba0SGirish Moodalbail 	 * If `ifname' *is* the IPMP group interface, or if the relevant
7486e91bba0SGirish Moodalbail 	 * address family is already configured, then there's nothing to do.
7496e91bba0SGirish Moodalbail 	 */
7506e91bba0SGirish Moodalbail 	if (strcmp(lifgr.gi_grifname, ifname) == 0 ||
7516e91bba0SGirish Moodalbail 	    (af == AF_INET && lifgr.gi_v4) || (af == AF_INET6 && lifgr.gi_v6)) {
7526e91bba0SGirish Moodalbail 		return (IPADM_SUCCESS);
7536e91bba0SGirish Moodalbail 	}
7546e91bba0SGirish Moodalbail 
7556e91bba0SGirish Moodalbail 	status = i_ipadm_create_ipmp(iph, lifgr.gi_grifname, af,
7566e91bba0SGirish Moodalbail 	    lifgr.gi_grname, IPADM_OPT_ACTIVE|IPADM_OPT_IPMP);
7576e91bba0SGirish Moodalbail 	return (status);
7586e91bba0SGirish Moodalbail }
7596e91bba0SGirish Moodalbail 
7606e91bba0SGirish Moodalbail /*
7616e91bba0SGirish Moodalbail  * Issues the ioctl SIOCSLIFNAME to kernel on the given ARP stream fd.
7626e91bba0SGirish Moodalbail  */
7636e91bba0SGirish Moodalbail static ipadm_status_t
i_ipadm_slifname_arp(char * ifname,uint64_t flags,int fd)7646e91bba0SGirish Moodalbail i_ipadm_slifname_arp(char *ifname, uint64_t flags, int fd)
7656e91bba0SGirish Moodalbail {
7666e91bba0SGirish Moodalbail 	struct lifreq	lifr;
7676e91bba0SGirish Moodalbail 	ifspec_t	ifsp;
7686e91bba0SGirish Moodalbail 
7696e91bba0SGirish Moodalbail 	bzero(&lifr, sizeof (lifr));
7706e91bba0SGirish Moodalbail 	(void) ifparse_ifspec(ifname, &ifsp);
7716e91bba0SGirish Moodalbail 	lifr.lifr_ppa = ifsp.ifsp_ppa;
7726e91bba0SGirish Moodalbail 	lifr.lifr_flags = flags;
7736e91bba0SGirish Moodalbail 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
7746e91bba0SGirish Moodalbail 	/*
7756e91bba0SGirish Moodalbail 	 * Tell ARP the name and unit number for this interface.
7766e91bba0SGirish Moodalbail 	 * Note that arp has no support for transparent ioctls.
7776e91bba0SGirish Moodalbail 	 */
7786e91bba0SGirish Moodalbail 	if (i_ipadm_strioctl(fd, SIOCSLIFNAME, (char *)&lifr,
7796e91bba0SGirish Moodalbail 	    sizeof (lifr)) == -1) {
7806e91bba0SGirish Moodalbail 		return (ipadm_errno2status(errno));
7816e91bba0SGirish Moodalbail 	}
7826e91bba0SGirish Moodalbail 	return (IPADM_SUCCESS);
7836e91bba0SGirish Moodalbail }
7846e91bba0SGirish Moodalbail 
7856e91bba0SGirish Moodalbail /*
7866e91bba0SGirish Moodalbail  * Issues the ioctl SIOCSLIFNAME to kernel. If IPADM_OPT_GENPPA is set in
7876e91bba0SGirish Moodalbail  * `ipadm_flags', then a ppa will be generated. `newif' will be updated
7886e91bba0SGirish Moodalbail  * with the generated ppa.
7896e91bba0SGirish Moodalbail  */
7906e91bba0SGirish Moodalbail static ipadm_status_t
i_ipadm_slifname(ipadm_handle_t iph,char * ifname,char * newif,uint64_t flags,int fd,uint32_t ipadm_flags)7916e91bba0SGirish Moodalbail i_ipadm_slifname(ipadm_handle_t iph, char *ifname, char *newif, uint64_t flags,
7926e91bba0SGirish Moodalbail     int fd, uint32_t ipadm_flags)
7936e91bba0SGirish Moodalbail {
7946e91bba0SGirish Moodalbail 	struct lifreq	lifr;
7956e91bba0SGirish Moodalbail 	ipadm_status_t	status = IPADM_SUCCESS;
7966e91bba0SGirish Moodalbail 	int		err = 0;
7976e91bba0SGirish Moodalbail 	sa_family_t	af;
7986e91bba0SGirish Moodalbail 	int		ppa;
7996e91bba0SGirish Moodalbail 	ifspec_t	ifsp;
8006e91bba0SGirish Moodalbail 	boolean_t	valid_if;
8016e91bba0SGirish Moodalbail 
8026e91bba0SGirish Moodalbail 	bzero(&lifr, sizeof (lifr));
8036e91bba0SGirish Moodalbail 	if (ipadm_flags & IPADM_OPT_GENPPA) {
8046e91bba0SGirish Moodalbail 		/*
8056e91bba0SGirish Moodalbail 		 * We'd like to just set lifr_ppa to UINT_MAX and have the
8066e91bba0SGirish Moodalbail 		 * kernel pick a PPA.  Unfortunately, that would mishandle
8076e91bba0SGirish Moodalbail 		 * two cases:
8086e91bba0SGirish Moodalbail 		 *
8096e91bba0SGirish Moodalbail 		 *	1. If the PPA is available but the groupname is taken
8106e91bba0SGirish Moodalbail 		 *	   (e.g., the "ipmp2" IP interface name is available
8116e91bba0SGirish Moodalbail 		 *	   but the "ipmp2" groupname is taken) then the
8126e91bba0SGirish Moodalbail 		 *	   auto-assignment by the kernel will fail.
8136e91bba0SGirish Moodalbail 		 *
8146e91bba0SGirish Moodalbail 		 *	2. If we're creating (e.g.) an IPv6-only IPMP
8156e91bba0SGirish Moodalbail 		 *	   interface, and there's already an IPv4-only IPMP
8166e91bba0SGirish Moodalbail 		 *	   interface, the kernel will allow us to accidentally
8176e91bba0SGirish Moodalbail 		 *	   reuse the IPv6 IPMP interface name (since
8186e91bba0SGirish Moodalbail 		 *	   SIOCSLIFNAME uniqueness is per-interface-type).
8196e91bba0SGirish Moodalbail 		 *	   This will cause administrative confusion.
8206e91bba0SGirish Moodalbail 		 *
8216e91bba0SGirish Moodalbail 		 * Thus, we instead take a brute-force approach of checking
8226e91bba0SGirish Moodalbail 		 * whether the IPv4 or IPv6 name is already in-use before
8236e91bba0SGirish Moodalbail 		 * attempting the SIOCSLIFNAME.  As per (1) above, the
8246e91bba0SGirish Moodalbail 		 * SIOCSLIFNAME may still fail, in which case we just proceed
8256e91bba0SGirish Moodalbail 		 * to the next one.  If this approach becomes too slow, we
8266e91bba0SGirish Moodalbail 		 * can add a new SIOC* to handle this case in the kernel.
8276e91bba0SGirish Moodalbail 		 */
8286e91bba0SGirish Moodalbail 		for (ppa = 0; ppa < UINT_MAX; ppa++) {
8296e91bba0SGirish Moodalbail 			(void) snprintf(lifr.lifr_name, LIFNAMSIZ, "%s%d",
8306e91bba0SGirish Moodalbail 			    ifname, ppa);
8316e91bba0SGirish Moodalbail 
8326e91bba0SGirish Moodalbail 			if (ioctl(iph->iph_sock, SIOCGLIFFLAGS, &lifr) != -1 ||
8336e91bba0SGirish Moodalbail 			    errno != ENXIO)
8346e91bba0SGirish Moodalbail 				continue;
8356e91bba0SGirish Moodalbail 
8366e91bba0SGirish Moodalbail 			if (ioctl(iph->iph_sock6, SIOCGLIFFLAGS, &lifr) != -1 ||
8376e91bba0SGirish Moodalbail 			    errno != ENXIO)
8386e91bba0SGirish Moodalbail 				continue;
8396e91bba0SGirish Moodalbail 
8406e91bba0SGirish Moodalbail 			lifr.lifr_ppa = ppa;
8416e91bba0SGirish Moodalbail 			lifr.lifr_flags = flags;
8426e91bba0SGirish Moodalbail 
8436e91bba0SGirish Moodalbail 			err = ioctl(fd, SIOCSLIFNAME, &lifr);
8446e91bba0SGirish Moodalbail 			if (err != -1 || errno != EEXIST)
8456e91bba0SGirish Moodalbail 				break;
8466e91bba0SGirish Moodalbail 		}
8476e91bba0SGirish Moodalbail 		if (err == -1) {
8486e91bba0SGirish Moodalbail 			status = ipadm_errno2status(errno);
8496e91bba0SGirish Moodalbail 		} else {
8506e91bba0SGirish Moodalbail 			/*
8516e91bba0SGirish Moodalbail 			 * PPA has been successfully established.
8526e91bba0SGirish Moodalbail 			 * Update `newif' with the ppa.
8536e91bba0SGirish Moodalbail 			 */
8546e91bba0SGirish Moodalbail 			assert(newif != NULL);
8556e91bba0SGirish Moodalbail 			if (snprintf(newif, LIFNAMSIZ, "%s%d", ifname,
8566e91bba0SGirish Moodalbail 			    ppa) >= LIFNAMSIZ)
8576e91bba0SGirish Moodalbail 				return (IPADM_INVALID_ARG);
8586e91bba0SGirish Moodalbail 		}
8596e91bba0SGirish Moodalbail 	} else {
8606e91bba0SGirish Moodalbail 		/* We should have already validated the interface name. */
8616e91bba0SGirish Moodalbail 		valid_if = ifparse_ifspec(ifname, &ifsp);
8626e91bba0SGirish Moodalbail 		assert(valid_if);
8636e91bba0SGirish Moodalbail 
8646e91bba0SGirish Moodalbail 		/*
8656e91bba0SGirish Moodalbail 		 * Before we call SIOCSLIFNAME, ensure that the IPMP group
8666e91bba0SGirish Moodalbail 		 * interface for this address family exists.  Otherwise, the
8676e91bba0SGirish Moodalbail 		 * kernel will kick the interface out of the group when we do
8686e91bba0SGirish Moodalbail 		 * the SIOCSLIFNAME.
8696e91bba0SGirish Moodalbail 		 *
8706e91bba0SGirish Moodalbail 		 * Example: suppose bge0 is plumbed for IPv4 and in group "a".
8716e91bba0SGirish Moodalbail 		 * If we're now plumbing bge0 for IPv6, but the IPMP group
8726e91bba0SGirish Moodalbail 		 * interface for "a" is not plumbed for IPv6, the SIOCSLIFNAME
8736e91bba0SGirish Moodalbail 		 * will kick bge0 out of group "a", which is undesired.
8746e91bba0SGirish Moodalbail 		 */
8756e91bba0SGirish Moodalbail 		if (flags & IFF_IPV4)
8766e91bba0SGirish Moodalbail 			af = AF_INET;
8776e91bba0SGirish Moodalbail 		else
8786e91bba0SGirish Moodalbail 			af = AF_INET6;
8796e91bba0SGirish Moodalbail 		status = i_ipadm_create_ipmp_peer(iph, ifname, af);
8806e91bba0SGirish Moodalbail 		if (status != IPADM_SUCCESS)
8816e91bba0SGirish Moodalbail 			return (status);
8826e91bba0SGirish Moodalbail 		lifr.lifr_ppa = ifsp.ifsp_ppa;
8836e91bba0SGirish Moodalbail 		lifr.lifr_flags = flags;
8846e91bba0SGirish Moodalbail 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
8856e91bba0SGirish Moodalbail 		if (ioctl(fd, SIOCSLIFNAME, &lifr) == -1)
8866e91bba0SGirish Moodalbail 			status = ipadm_errno2status(errno);
8876e91bba0SGirish Moodalbail 	}
8886e91bba0SGirish Moodalbail 
8896e91bba0SGirish Moodalbail 	return (status);
8906e91bba0SGirish Moodalbail }
8916e91bba0SGirish Moodalbail 
8926e91bba0SGirish Moodalbail /*
8936e91bba0SGirish Moodalbail  * Plumbs the interface `ifname' for the address family `af'. It also persists
8946e91bba0SGirish Moodalbail  * the interface for `af' if IPADM_OPT_PERSIST is set in `ipadm_flags'.
8956e91bba0SGirish Moodalbail  */
8966e91bba0SGirish Moodalbail ipadm_status_t
i_ipadm_plumb_if(ipadm_handle_t iph,char * ifname,sa_family_t af,uint32_t ipadm_flags)8976e91bba0SGirish Moodalbail i_ipadm_plumb_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
8986e91bba0SGirish Moodalbail     uint32_t ipadm_flags)
8996e91bba0SGirish Moodalbail {
9006e91bba0SGirish Moodalbail 	int		ip_muxid;
9016e91bba0SGirish Moodalbail 	int		mux_fd = -1, ip_fd, arp_fd;
9026e91bba0SGirish Moodalbail 	char		*udp_dev_name;
9036e91bba0SGirish Moodalbail 	dlpi_handle_t	dh_arp = NULL, dh_ip;
9046e91bba0SGirish Moodalbail 	uint64_t	ifflags;
9056e91bba0SGirish Moodalbail 	struct lifreq	lifr;
9066e91bba0SGirish Moodalbail 	uint_t		dlpi_flags;
9076e91bba0SGirish Moodalbail 	ipadm_status_t	status = IPADM_SUCCESS;
9086e91bba0SGirish Moodalbail 	char		*linkname;
9096e91bba0SGirish Moodalbail 	boolean_t	legacy = (iph->iph_flags & IPH_LEGACY);
9106e91bba0SGirish Moodalbail 	zoneid_t	zoneid;
9116e91bba0SGirish Moodalbail 	char		newif[LIFNAMSIZ];
9126e91bba0SGirish Moodalbail 	char		lifname[LIFNAMSIZ];
9136e91bba0SGirish Moodalbail 	datalink_id_t	linkid;
9146e91bba0SGirish Moodalbail 	int		sock;
9156e91bba0SGirish Moodalbail 	boolean_t	islo;
9166e91bba0SGirish Moodalbail 	boolean_t	is_persistent =
9176e91bba0SGirish Moodalbail 	    ((ipadm_flags & IPADM_OPT_PERSIST) != 0);
9186e91bba0SGirish Moodalbail 	uint32_t	dlflags;
9196e91bba0SGirish Moodalbail 	dladm_status_t	dlstatus;
9206e91bba0SGirish Moodalbail 
9216e91bba0SGirish Moodalbail 	if (iph->iph_dlh != NULL) {
9226e91bba0SGirish Moodalbail 		dlstatus = dladm_name2info(iph->iph_dlh, ifname, &linkid,
9236e91bba0SGirish Moodalbail 		    &dlflags, NULL, NULL);
9246e91bba0SGirish Moodalbail 	}
9256e91bba0SGirish Moodalbail 	/*
9266e91bba0SGirish Moodalbail 	 * If we're in the global zone and we're plumbing a datalink, make
9276e91bba0SGirish Moodalbail 	 * sure that the datalink is not assigned to a non-global zone.  Note
9286e91bba0SGirish Moodalbail 	 * that the non-global zones don't need this check, because zoneadm
9296e91bba0SGirish Moodalbail 	 * has taken care of this when the zones boot.
9306e91bba0SGirish Moodalbail 	 */
931550b6e40SSowmini Varadhan 	if (iph->iph_zoneid == GLOBAL_ZONEID && dlstatus == DLADM_STATUS_OK) {
9326e91bba0SGirish Moodalbail 		zoneid = ALL_ZONES;
9336e91bba0SGirish Moodalbail 		if (zone_check_datalink(&zoneid, linkid) == 0) {
9346e91bba0SGirish Moodalbail 			/* interface is in use by a non-global zone. */
9356e91bba0SGirish Moodalbail 			return (IPADM_IF_INUSE);
9366e91bba0SGirish Moodalbail 		}
9376e91bba0SGirish Moodalbail 	}
9386e91bba0SGirish Moodalbail 
9396e91bba0SGirish Moodalbail 	/* loopback interfaces are just added as logical interface */
9406e91bba0SGirish Moodalbail 	bzero(&lifr, sizeof (lifr));
9416e91bba0SGirish Moodalbail 	islo = i_ipadm_is_loopback(ifname);
9426e91bba0SGirish Moodalbail 	if (islo || i_ipadm_get_lnum(ifname) != 0) {
9436e91bba0SGirish Moodalbail 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
9446e91bba0SGirish Moodalbail 		if (af == AF_INET)
9456e91bba0SGirish Moodalbail 			sock = iph->iph_sock;
9466e91bba0SGirish Moodalbail 		else
9476e91bba0SGirish Moodalbail 			sock = iph->iph_sock6;
9486e91bba0SGirish Moodalbail 		if (islo && ioctl(sock, SIOCGLIFADDR, (caddr_t)&lifr) >= 0)
9496e91bba0SGirish Moodalbail 			return (IPADM_IF_EXISTS);
9506e91bba0SGirish Moodalbail 		if (ioctl(sock, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
9516e91bba0SGirish Moodalbail 			return (ipadm_errno2status(errno));
9526e91bba0SGirish Moodalbail 
9536e91bba0SGirish Moodalbail 		/*
9546e91bba0SGirish Moodalbail 		 * By default, kernel configures 127.0.0.1 on the loopback
9556e91bba0SGirish Moodalbail 		 * interface. Replace this with 0.0.0.0 to be consistent
9566e91bba0SGirish Moodalbail 		 * with interface creation on other physical interfaces.
9576e91bba0SGirish Moodalbail 		 */
9586e91bba0SGirish Moodalbail 		if (islo && !legacy) {
9596e91bba0SGirish Moodalbail 			bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
9606e91bba0SGirish Moodalbail 			lifr.lifr_addr.ss_family = af;
9616e91bba0SGirish Moodalbail 			if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
9626e91bba0SGirish Moodalbail 				return (ipadm_errno2status(errno));
9636e91bba0SGirish Moodalbail 			if (is_persistent) {
964a73be61aSHans Rosenfeld 				status = i_ipadm_persist_if(iph,
965a73be61aSHans Rosenfeld 				    ifname, af, ipadm_flags);
9666e91bba0SGirish Moodalbail 				if (status != IPADM_SUCCESS) {
9676e91bba0SGirish Moodalbail 					(void) i_ipadm_delete_if(iph, ifname,
9686e91bba0SGirish Moodalbail 					    af, IPADM_OPT_ACTIVE);
9696e91bba0SGirish Moodalbail 				}
9706e91bba0SGirish Moodalbail 			}
9716e91bba0SGirish Moodalbail 		}
9726e91bba0SGirish Moodalbail 		return (status);
9736e91bba0SGirish Moodalbail 	}
9746e91bba0SGirish Moodalbail 
9756e91bba0SGirish Moodalbail 	dlpi_flags = DLPI_NOATTACH;
9766e91bba0SGirish Moodalbail 
9776e91bba0SGirish Moodalbail 	/*
9786e91bba0SGirish Moodalbail 	 * If IPADM_OPT_IPMP is specified, then this is a request
9796e91bba0SGirish Moodalbail 	 * to create an IPMP interface atop /dev/ipmpstub0.  (We can't simply
9806e91bba0SGirish Moodalbail 	 * pass "ipmpstub0" as devname since an admin *could* have a normal
9816e91bba0SGirish Moodalbail 	 * vanity-named link named "ipmpstub0" that they'd like to plumb.)
9826e91bba0SGirish Moodalbail 	 */
9836e91bba0SGirish Moodalbail 	if (ipadm_flags & IPADM_OPT_IPMP) {
9846e91bba0SGirish Moodalbail 		dlpi_flags |= DLPI_DEVONLY;
9856e91bba0SGirish Moodalbail 		linkname = "ipmpstub0";
9866e91bba0SGirish Moodalbail 	} else {
9876e91bba0SGirish Moodalbail 		/*
9886e91bba0SGirish Moodalbail 		 * Verify that the user is not creating a persistent
9896e91bba0SGirish Moodalbail 		 * IP interface on a non-persistent data-link.
9906e91bba0SGirish Moodalbail 		 */
9916e91bba0SGirish Moodalbail 		if (!i_ipadm_is_vni(ifname) && dlstatus == DLADM_STATUS_OK &&
9926e91bba0SGirish Moodalbail 		    is_persistent && !(dlflags & DLADM_OPT_PERSIST)) {
9936e91bba0SGirish Moodalbail 				return (IPADM_TEMPORARY_OBJ);
9946e91bba0SGirish Moodalbail 		}
9956e91bba0SGirish Moodalbail 		linkname = ifname;
9966e91bba0SGirish Moodalbail 	}
9976e91bba0SGirish Moodalbail 
9986e91bba0SGirish Moodalbail 	/*
9996e91bba0SGirish Moodalbail 	 * We use DLPI_NOATTACH because the ip module will do the attach
10006e91bba0SGirish Moodalbail 	 * itself for DLPI style-2 devices.
10016e91bba0SGirish Moodalbail 	 */
10026e91bba0SGirish Moodalbail 	if (dlpi_open(linkname, &dh_ip, dlpi_flags) != DLPI_SUCCESS)
10036e91bba0SGirish Moodalbail 		return (IPADM_DLPI_FAILURE);
10046e91bba0SGirish Moodalbail 	ip_fd = dlpi_fd(dh_ip);
10056e91bba0SGirish Moodalbail 	if (ioctl(ip_fd, I_PUSH, IP_MOD_NAME) == -1) {
10066e91bba0SGirish Moodalbail 		status = ipadm_errno2status(errno);
10076e91bba0SGirish Moodalbail 		goto done;
10086e91bba0SGirish Moodalbail 	}
10096e91bba0SGirish Moodalbail 
10106e91bba0SGirish Moodalbail 	/*
10116e91bba0SGirish Moodalbail 	 * Set IFF_IPV4/IFF_IPV6 flags. The kernel only allows modifications
10126e91bba0SGirish Moodalbail 	 * to IFF_IPv4, IFF_IPV6, IFF_BROADCAST, IFF_XRESOLV, IFF_NOLINKLOCAL.
10136e91bba0SGirish Moodalbail 	 */
10146e91bba0SGirish Moodalbail 	ifflags = 0;
10156e91bba0SGirish Moodalbail 
10166e91bba0SGirish Moodalbail 	/* Set the name string and the IFF_IPV* flag */
10176e91bba0SGirish Moodalbail 	if (af == AF_INET) {
10186e91bba0SGirish Moodalbail 		ifflags = IFF_IPV4;
10196e91bba0SGirish Moodalbail 	} else {
10206e91bba0SGirish Moodalbail 		ifflags = IFF_IPV6;
10216e91bba0SGirish Moodalbail 		/*
10226e91bba0SGirish Moodalbail 		 * With the legacy method, the link-local address should be
10236e91bba0SGirish Moodalbail 		 * configured as part of the interface plumb, using the default
10246e91bba0SGirish Moodalbail 		 * token. If IPH_LEGACY is not specified, we want to set :: as
10256e91bba0SGirish Moodalbail 		 * the address and require the admin to explicitly call
10266e91bba0SGirish Moodalbail 		 * ipadm_create_addr() with the address object type set to
10276e91bba0SGirish Moodalbail 		 * IPADM_ADDR_IPV6_ADDRCONF to create the link-local address
10286e91bba0SGirish Moodalbail 		 * as well as the autoconfigured addresses.
10296e91bba0SGirish Moodalbail 		 */
10306e91bba0SGirish Moodalbail 		if (!legacy && !i_ipadm_is_6to4(iph, ifname))
10316e91bba0SGirish Moodalbail 			ifflags |= IFF_NOLINKLOCAL;
10326e91bba0SGirish Moodalbail 	}
10336e91bba0SGirish Moodalbail 	(void) strlcpy(newif, ifname, sizeof (newif));
10346e91bba0SGirish Moodalbail 	status = i_ipadm_slifname(iph, ifname, newif, ifflags, ip_fd,
10356e91bba0SGirish Moodalbail 	    ipadm_flags);
10366e91bba0SGirish Moodalbail 	if (status != IPADM_SUCCESS)
10376e91bba0SGirish Moodalbail 		goto done;
10386e91bba0SGirish Moodalbail 
10396e91bba0SGirish Moodalbail 	/* Get the full set of existing flags for this stream */
10406e91bba0SGirish Moodalbail 	status = i_ipadm_get_flags(iph, newif, af, &ifflags);
10416e91bba0SGirish Moodalbail 	if (status != IPADM_SUCCESS)
10426e91bba0SGirish Moodalbail 		goto done;
10436e91bba0SGirish Moodalbail 
10446e91bba0SGirish Moodalbail 	udp_dev_name = (af == AF_INET6 ? UDP6_DEV_NAME : UDP_DEV_NAME);
10456e91bba0SGirish Moodalbail 	status = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd);
10466e91bba0SGirish Moodalbail 	if (status != IPADM_SUCCESS)
10476e91bba0SGirish Moodalbail 		goto done;
10486e91bba0SGirish Moodalbail 
10496e91bba0SGirish Moodalbail 	/* Check if arp is not needed */
10506e91bba0SGirish Moodalbail 	if (ifflags & (IFF_NOARP|IFF_IPV6)) {
10516e91bba0SGirish Moodalbail 		/*
10526e91bba0SGirish Moodalbail 		 * PLINK the interface stream so that the application can exit
10536e91bba0SGirish Moodalbail 		 * without tearing down the stream.
10546e91bba0SGirish Moodalbail 		 */
10556e91bba0SGirish Moodalbail 		if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1)
10566e91bba0SGirish Moodalbail 			status = ipadm_errno2status(errno);
10576e91bba0SGirish Moodalbail 		goto done;
10586e91bba0SGirish Moodalbail 	}
10596e91bba0SGirish Moodalbail 
10606e91bba0SGirish Moodalbail 	/*
10616e91bba0SGirish Moodalbail 	 * This interface does use ARP, so set up a separate stream
10626e91bba0SGirish Moodalbail 	 * from the interface to ARP.
10636e91bba0SGirish Moodalbail 	 *
10646e91bba0SGirish Moodalbail 	 * We use DLPI_NOATTACH because the arp module will do the attach
10656e91bba0SGirish Moodalbail 	 * itself for DLPI style-2 devices.
10666e91bba0SGirish Moodalbail 	 */
10676e91bba0SGirish Moodalbail 	if (dlpi_open(linkname, &dh_arp, dlpi_flags) != DLPI_SUCCESS) {
10686e91bba0SGirish Moodalbail 		status = IPADM_DLPI_FAILURE;
10696e91bba0SGirish Moodalbail 		goto done;
10706e91bba0SGirish Moodalbail 	}
10716e91bba0SGirish Moodalbail 
10726e91bba0SGirish Moodalbail 	arp_fd = dlpi_fd(dh_arp);
10736e91bba0SGirish Moodalbail 	if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1) {
10746e91bba0SGirish Moodalbail 		status = ipadm_errno2status(errno);
10756e91bba0SGirish Moodalbail 		goto done;
10766e91bba0SGirish Moodalbail 	}
10776e91bba0SGirish Moodalbail 
10786e91bba0SGirish Moodalbail 	status = i_ipadm_slifname_arp(newif, ifflags, arp_fd);
10796e91bba0SGirish Moodalbail 	if (status != IPADM_SUCCESS)
10806e91bba0SGirish Moodalbail 		goto done;
10816e91bba0SGirish Moodalbail 	/*
10826e91bba0SGirish Moodalbail 	 * PLINK the IP and ARP streams so that ifconfig can exit
10836e91bba0SGirish Moodalbail 	 * without tearing down the stream.
10846e91bba0SGirish Moodalbail 	 */
10856e91bba0SGirish Moodalbail 	if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) {
10866e91bba0SGirish Moodalbail 		status = ipadm_errno2status(errno);
10876e91bba0SGirish Moodalbail 		goto done;
10886e91bba0SGirish Moodalbail 	}
10896e91bba0SGirish Moodalbail 
10906e91bba0SGirish Moodalbail 	if (ioctl(mux_fd, I_PLINK, arp_fd) < 0) {
10916e91bba0SGirish Moodalbail 		status = ipadm_errno2status(errno);
10926e91bba0SGirish Moodalbail 		(void) ioctl(mux_fd, I_PUNLINK, ip_muxid);
10936e91bba0SGirish Moodalbail 	}
10946e91bba0SGirish Moodalbail 
10956e91bba0SGirish Moodalbail done:
10966e91bba0SGirish Moodalbail 	dlpi_close(dh_ip);
10976e91bba0SGirish Moodalbail 	if (dh_arp != NULL)
10986e91bba0SGirish Moodalbail 		dlpi_close(dh_arp);
10996e91bba0SGirish Moodalbail 
11006e91bba0SGirish Moodalbail 	if (mux_fd != -1)
11016e91bba0SGirish Moodalbail 		(void) close(mux_fd);
11026e91bba0SGirish Moodalbail 
11036e91bba0SGirish Moodalbail 	if (status == IPADM_SUCCESS) {
11046e91bba0SGirish Moodalbail 		/* copy back new ifname */
11056e91bba0SGirish Moodalbail 		(void) strlcpy(ifname, newif, LIFNAMSIZ);
11066e91bba0SGirish Moodalbail 		/*
11076e91bba0SGirish Moodalbail 		 * If it is a 6to4 tunnel, create a default
11086e91bba0SGirish Moodalbail 		 * addrobj name for the default address on the 0'th
11096e91bba0SGirish Moodalbail 		 * logical interface and set IFF_UP in the interface flags.
11106e91bba0SGirish Moodalbail 		 */
11116e91bba0SGirish Moodalbail 		if (i_ipadm_is_6to4(iph, ifname)) {
11126e91bba0SGirish Moodalbail 			struct ipadm_addrobj_s addr;
11136e91bba0SGirish Moodalbail 
11146e91bba0SGirish Moodalbail 			i_ipadm_init_addr(&addr, ifname, "", IPADM_ADDR_STATIC);
11156e91bba0SGirish Moodalbail 			addr.ipadm_af = af;
11166e91bba0SGirish Moodalbail 			status = i_ipadm_lookupadd_addrobj(iph, &addr);
11176e91bba0SGirish Moodalbail 			if (status != IPADM_SUCCESS)
11186e91bba0SGirish Moodalbail 				return (status);
11196e91bba0SGirish Moodalbail 			status = ipadm_add_aobjname(iph, ifname,
11206e91bba0SGirish Moodalbail 			    af, addr.ipadm_aobjname, IPADM_ADDR_STATIC, 0);
11216e91bba0SGirish Moodalbail 			if (status != IPADM_SUCCESS)
11226e91bba0SGirish Moodalbail 				return (status);
11236e91bba0SGirish Moodalbail 			addr.ipadm_lifnum = 0;
11246e91bba0SGirish Moodalbail 			i_ipadm_addrobj2lifname(&addr, lifname,
11256e91bba0SGirish Moodalbail 			    sizeof (lifname));
11266e91bba0SGirish Moodalbail 			status = i_ipadm_set_flags(iph, lifname, af,
11276e91bba0SGirish Moodalbail 			    IFF_UP, 0);
11286e91bba0SGirish Moodalbail 			if (status != IPADM_SUCCESS)
11296e91bba0SGirish Moodalbail 				return (status);
11306e91bba0SGirish Moodalbail 		} else {
11316e91bba0SGirish Moodalbail 			/*
11326e91bba0SGirish Moodalbail 			 * Prevent static IPv6 addresses from triggering
11336e91bba0SGirish Moodalbail 			 * autoconf. This does not have to be done for
11346e91bba0SGirish Moodalbail 			 * 6to4 tunnel interfaces, since in.ndpd will
11356e91bba0SGirish Moodalbail 			 * not autoconfigure those interfaces.
11366e91bba0SGirish Moodalbail 			 */
11376e91bba0SGirish Moodalbail 			if (af == AF_INET6 && !legacy)
11386e91bba0SGirish Moodalbail 				(void) i_ipadm_disable_autoconf(newif);
11396e91bba0SGirish Moodalbail 		}
11406e91bba0SGirish Moodalbail 
11416e91bba0SGirish Moodalbail 		/*
11426e91bba0SGirish Moodalbail 		 * If IPADM_OPT_PERSIST was set in flags, store the
11436e91bba0SGirish Moodalbail 		 * interface in persistent DB.
11446e91bba0SGirish Moodalbail 		 */
11456e91bba0SGirish Moodalbail 		if (is_persistent) {
1146a73be61aSHans Rosenfeld 			status = i_ipadm_persist_if(iph,
1147a73be61aSHans Rosenfeld 			    newif, af, ipadm_flags);
11486e91bba0SGirish Moodalbail 			if (status != IPADM_SUCCESS) {
11496e91bba0SGirish Moodalbail 				(void) i_ipadm_delete_if(iph, newif, af,
11506e91bba0SGirish Moodalbail 				    IPADM_OPT_ACTIVE);
11516e91bba0SGirish Moodalbail 			}
11526e91bba0SGirish Moodalbail 		}
11536e91bba0SGirish Moodalbail 	}
11546e91bba0SGirish Moodalbail 	if (status == IPADM_EXISTS)
11556e91bba0SGirish Moodalbail 		status = IPADM_IF_EXISTS;
11566e91bba0SGirish Moodalbail 	return (status);
11576e91bba0SGirish Moodalbail }
11586e91bba0SGirish Moodalbail 
11596e91bba0SGirish Moodalbail /*
11606e91bba0SGirish Moodalbail  * Unplumbs the interface in `ifname' of family `af'.
11616e91bba0SGirish Moodalbail  */
11626e91bba0SGirish Moodalbail ipadm_status_t
i_ipadm_unplumb_if(ipadm_handle_t iph,const char * ifname,sa_family_t af)11636e91bba0SGirish Moodalbail i_ipadm_unplumb_if(ipadm_handle_t iph, const char *ifname, sa_family_t af)
11646e91bba0SGirish Moodalbail {
11656e91bba0SGirish Moodalbail 	int		ip_muxid, arp_muxid;
11666e91bba0SGirish Moodalbail 	int		mux_fd = -1;
11676e91bba0SGirish Moodalbail 	int		muxid_fd = -1;
11686e91bba0SGirish Moodalbail 	char		*udp_dev_name;
11696e91bba0SGirish Moodalbail 	uint64_t	flags;
11706e91bba0SGirish Moodalbail 	boolean_t	changed_arp_muxid = B_FALSE;
11716e91bba0SGirish Moodalbail 	int		save_errno;
11726e91bba0SGirish Moodalbail 	struct lifreq	lifr;
11736e91bba0SGirish Moodalbail 	ipadm_status_t	ret = IPADM_SUCCESS;
11746e91bba0SGirish Moodalbail 	int		sock;
11756e91bba0SGirish Moodalbail 	lifgroupinfo_t	lifgr;
11766e91bba0SGirish Moodalbail 	ifaddrlistx_t	*ifaddrs, *ifaddrp;
11776e91bba0SGirish Moodalbail 	boolean_t	v6 = (af == AF_INET6);
11786e91bba0SGirish Moodalbail 
11796e91bba0SGirish Moodalbail 	/* Just do SIOCLIFREMOVEIF on loopback interfaces */
11806e91bba0SGirish Moodalbail 	bzero(&lifr, sizeof (lifr));
11816e91bba0SGirish Moodalbail 	if (i_ipadm_is_loopback(ifname) ||
11826e91bba0SGirish Moodalbail 	    (i_ipadm_get_lnum(ifname) != 0 && (iph->iph_flags & IPH_LEGACY))) {
11836e91bba0SGirish Moodalbail 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
11846e91bba0SGirish Moodalbail 		if (ioctl((af == AF_INET) ? iph->iph_sock : iph->iph_sock6,
11856e91bba0SGirish Moodalbail 		    SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) {
11866e91bba0SGirish Moodalbail 			return (ipadm_errno2status(errno));
11876e91bba0SGirish Moodalbail 		}
11886e91bba0SGirish Moodalbail 		return (IPADM_SUCCESS);
11896e91bba0SGirish Moodalbail 	}
11906e91bba0SGirish Moodalbail 
11916e91bba0SGirish Moodalbail 	/*
11926e91bba0SGirish Moodalbail 	 * We used /dev/udp or udp6 to set up the mux. So we have to use
11936e91bba0SGirish Moodalbail 	 * the same now for PUNLINK also.
11946e91bba0SGirish Moodalbail 	 */
11956e91bba0SGirish Moodalbail 	if (v6) {
11966e91bba0SGirish Moodalbail 		udp_dev_name = UDP6_DEV_NAME;
11976e91bba0SGirish Moodalbail 		sock = iph->iph_sock6;
11986e91bba0SGirish Moodalbail 	} else {
11996e91bba0SGirish Moodalbail 		udp_dev_name = UDP_DEV_NAME;
12006e91bba0SGirish Moodalbail 		sock = iph->iph_sock;
12016e91bba0SGirish Moodalbail 	}
12026e91bba0SGirish Moodalbail 	if ((muxid_fd = open(udp_dev_name, O_RDWR)) == -1) {
12036e91bba0SGirish Moodalbail 		ret = ipadm_errno2status(errno);
12046e91bba0SGirish Moodalbail 		goto done;
12056e91bba0SGirish Moodalbail 	}
12066e91bba0SGirish Moodalbail 	ret = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd);
12076e91bba0SGirish Moodalbail 	if (ret != IPADM_SUCCESS)
12086e91bba0SGirish Moodalbail 		goto done;
12096e91bba0SGirish Moodalbail 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
12106e91bba0SGirish Moodalbail 	if (ioctl(muxid_fd, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
12116e91bba0SGirish Moodalbail 		ret = ipadm_errno2status(errno);
12126e91bba0SGirish Moodalbail 		goto done;
12136e91bba0SGirish Moodalbail 	}
12146e91bba0SGirish Moodalbail 	flags = lifr.lifr_flags;
12156e91bba0SGirish Moodalbail again:
12166e91bba0SGirish Moodalbail 	if (flags & IFF_IPMP) {
12176e91bba0SGirish Moodalbail 		/*
12186e91bba0SGirish Moodalbail 		 * There are two reasons the I_PUNLINK can fail with EBUSY:
12196e91bba0SGirish Moodalbail 		 * (1) if IP interfaces are in the group, or (2) if IPMP data
12206e91bba0SGirish Moodalbail 		 * addresses are administratively up.  For case (1), we fail
12216e91bba0SGirish Moodalbail 		 * here with a specific error message.  For case (2), we bring
12226e91bba0SGirish Moodalbail 		 * down the addresses prior to doing the I_PUNLINK.  If the
12236e91bba0SGirish Moodalbail 		 * I_PUNLINK still fails with EBUSY then the configuration
12246e91bba0SGirish Moodalbail 		 * must have changed after our checks, in which case we branch
12256e91bba0SGirish Moodalbail 		 * back up to `again' and rerun this logic.  The net effect is
12266e91bba0SGirish Moodalbail 		 * that unplumbing an IPMP interface will only fail with EBUSY
12276e91bba0SGirish Moodalbail 		 * if IP interfaces are in the group.
12286e91bba0SGirish Moodalbail 		 */
12296e91bba0SGirish Moodalbail 		if (ioctl(sock, SIOCGLIFGROUPNAME, &lifr) == -1) {
12306e91bba0SGirish Moodalbail 			ret = ipadm_errno2status(errno);
12316e91bba0SGirish Moodalbail 			goto done;
12326e91bba0SGirish Moodalbail 		}
12336e91bba0SGirish Moodalbail 		(void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname,
12346e91bba0SGirish Moodalbail 		    LIFGRNAMSIZ);
12356e91bba0SGirish Moodalbail 		if (ioctl(sock, SIOCGLIFGROUPINFO, &lifgr) == -1) {
12366e91bba0SGirish Moodalbail 			ret = ipadm_errno2status(errno);
12376e91bba0SGirish Moodalbail 			goto done;
12386e91bba0SGirish Moodalbail 		}
12396e91bba0SGirish Moodalbail 		if ((v6 && lifgr.gi_nv6 != 0) || (!v6 && lifgr.gi_nv4 != 0)) {
12406e91bba0SGirish Moodalbail 			ret = IPADM_GRP_NOTEMPTY;
12416e91bba0SGirish Moodalbail 			goto done;
12426e91bba0SGirish Moodalbail 		}
12436e91bba0SGirish Moodalbail 
12446e91bba0SGirish Moodalbail 		/*
12456e91bba0SGirish Moodalbail 		 * The kernel will fail the I_PUNLINK if the IPMP interface
12466e91bba0SGirish Moodalbail 		 * has administratively up addresses; bring them down.
12476e91bba0SGirish Moodalbail 		 */
12486e91bba0SGirish Moodalbail 		if (ifaddrlistx(ifname, IFF_UP|IFF_DUPLICATE,
12496e91bba0SGirish Moodalbail 		    0, &ifaddrs) == -1) {
12506e91bba0SGirish Moodalbail 			ret = ipadm_errno2status(errno);
12516e91bba0SGirish Moodalbail 			goto done;
12526e91bba0SGirish Moodalbail 		}
12536e91bba0SGirish Moodalbail 		ifaddrp = ifaddrs;
12546e91bba0SGirish Moodalbail 		for (; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
12556e91bba0SGirish Moodalbail 			int sock = (ifaddrp->ia_flags & IFF_IPV4) ?
12566e91bba0SGirish Moodalbail 			    iph->iph_sock : iph->iph_sock6;
12576e91bba0SGirish Moodalbail 			struct lifreq lifrl;
12586e91bba0SGirish Moodalbail 
12596e91bba0SGirish Moodalbail 			if (((ifaddrp->ia_flags & IFF_IPV6) && !v6) ||
12606e91bba0SGirish Moodalbail 			    (!(ifaddrp->ia_flags & IFF_IPV6) && v6))
12616e91bba0SGirish Moodalbail 				continue;
12626e91bba0SGirish Moodalbail 
12636e91bba0SGirish Moodalbail 			bzero(&lifrl, sizeof (lifrl));
12646e91bba0SGirish Moodalbail 			(void) strlcpy(lifrl.lifr_name, ifaddrp->ia_name,
12656e91bba0SGirish Moodalbail 			    sizeof (lifrl.lifr_name));
12666e91bba0SGirish Moodalbail 			if (ioctl(sock, SIOCGLIFFLAGS, &lifrl) < 0) {
12676e91bba0SGirish Moodalbail 				ret = ipadm_errno2status(errno);
12686e91bba0SGirish Moodalbail 				ifaddrlistx_free(ifaddrs);
12696e91bba0SGirish Moodalbail 				goto done;
12706e91bba0SGirish Moodalbail 			}
12716e91bba0SGirish Moodalbail 			if (lifrl.lifr_flags & IFF_UP) {
12726e91bba0SGirish Moodalbail 				ret = i_ipadm_set_flags(iph, lifrl.lifr_name,
12736e91bba0SGirish Moodalbail 				    ((lifrl.lifr_flags & IFF_IPV4) ? AF_INET :
12746e91bba0SGirish Moodalbail 				    AF_INET6), 0, IFF_UP);
12756e91bba0SGirish Moodalbail 				if (ret != IPADM_SUCCESS) {
12766e91bba0SGirish Moodalbail 					ifaddrlistx_free(ifaddrs);
12776e91bba0SGirish Moodalbail 					goto done;
12786e91bba0SGirish Moodalbail 				}
12796e91bba0SGirish Moodalbail 			} else if (lifrl.lifr_flags & IFF_DUPLICATE) {
12806e91bba0SGirish Moodalbail 				if (ioctl(sock, SIOCGLIFADDR, &lifrl) < 0 ||
12816e91bba0SGirish Moodalbail 				    ioctl(sock, SIOCSLIFADDR, &lifrl) < 0) {
12826e91bba0SGirish Moodalbail 					ret = ipadm_errno2status(errno);
12836e91bba0SGirish Moodalbail 					ifaddrlistx_free(ifaddrs);
12846e91bba0SGirish Moodalbail 					goto done;
12856e91bba0SGirish Moodalbail 				}
12866e91bba0SGirish Moodalbail 			}
12876e91bba0SGirish Moodalbail 		}
12886e91bba0SGirish Moodalbail 		ifaddrlistx_free(ifaddrs);
12896e91bba0SGirish Moodalbail 	}
12906e91bba0SGirish Moodalbail 
12916e91bba0SGirish Moodalbail 	if (ioctl(muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
12926e91bba0SGirish Moodalbail 		ret = ipadm_errno2status(errno);
12936e91bba0SGirish Moodalbail 		goto done;
12946e91bba0SGirish Moodalbail 	}
12956e91bba0SGirish Moodalbail 	arp_muxid = lifr.lifr_arp_muxid;
12966e91bba0SGirish Moodalbail 	ip_muxid = lifr.lifr_ip_muxid;
12976e91bba0SGirish Moodalbail 
12986e91bba0SGirish Moodalbail 	/*
12996e91bba0SGirish Moodalbail 	 * We don't have a good way of knowing whether the arp stream is
13006e91bba0SGirish Moodalbail 	 * plumbed. We can't rely on IFF_NOARP because someone could
13016e91bba0SGirish Moodalbail 	 * have turned it off later using "ifconfig xxx -arp".
13026e91bba0SGirish Moodalbail 	 */
13036e91bba0SGirish Moodalbail 	if (arp_muxid != 0) {
13046e91bba0SGirish Moodalbail 		if (ioctl(mux_fd, I_PUNLINK, arp_muxid) < 0) {
13056e91bba0SGirish Moodalbail 			/*
13066e91bba0SGirish Moodalbail 			 * See the comment before the SIOCGLIFGROUPNAME call.
13076e91bba0SGirish Moodalbail 			 */
13086e91bba0SGirish Moodalbail 			if (errno == EBUSY && (flags & IFF_IPMP))
13096e91bba0SGirish Moodalbail 				goto again;
13106e91bba0SGirish Moodalbail 
13116e91bba0SGirish Moodalbail 			if ((errno == EINVAL) &&
13126e91bba0SGirish Moodalbail 			    (flags & (IFF_NOARP | IFF_IPV6))) {
13136e91bba0SGirish Moodalbail 				/*
13146e91bba0SGirish Moodalbail 				 * Some plumbing utilities set the muxid to
13156e91bba0SGirish Moodalbail 				 * -1 or some invalid value to signify that
13166e91bba0SGirish Moodalbail 				 * there is no arp stream. Set the muxid to 0
13176e91bba0SGirish Moodalbail 				 * before trying to unplumb the IP stream.
13186e91bba0SGirish Moodalbail 				 * IP does not allow the IP stream to be
13196e91bba0SGirish Moodalbail 				 * unplumbed if it sees a non-null arp muxid,
13206e91bba0SGirish Moodalbail 				 * for consistency of IP-ARP streams.
13216e91bba0SGirish Moodalbail 				 */
13226e91bba0SGirish Moodalbail 				lifr.lifr_arp_muxid = 0;
13236e91bba0SGirish Moodalbail 				(void) ioctl(muxid_fd, SIOCSLIFMUXID,
13246e91bba0SGirish Moodalbail 				    (caddr_t)&lifr);
13256e91bba0SGirish Moodalbail 				changed_arp_muxid = B_TRUE;
13266e91bba0SGirish Moodalbail 			}
13276e91bba0SGirish Moodalbail 			/*
13286e91bba0SGirish Moodalbail 			 * In case of any other error, we continue with
13296e91bba0SGirish Moodalbail 			 * the unplumb.
13306e91bba0SGirish Moodalbail 			 */
13316e91bba0SGirish Moodalbail 		}
13326e91bba0SGirish Moodalbail 	}
13336e91bba0SGirish Moodalbail 
13346e91bba0SGirish Moodalbail 	if (ioctl(mux_fd, I_PUNLINK, ip_muxid) < 0) {
13356e91bba0SGirish Moodalbail 		if (changed_arp_muxid) {
13366e91bba0SGirish Moodalbail 			/*
13376e91bba0SGirish Moodalbail 			 * Some error occurred, and we need to restore
13386e91bba0SGirish Moodalbail 			 * everything back to what it was.
13396e91bba0SGirish Moodalbail 			 */
13406e91bba0SGirish Moodalbail 			save_errno = errno;
13416e91bba0SGirish Moodalbail 			lifr.lifr_arp_muxid = arp_muxid;
13426e91bba0SGirish Moodalbail 			lifr.lifr_ip_muxid = ip_muxid;
13436e91bba0SGirish Moodalbail 			(void) ioctl(muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
13446e91bba0SGirish Moodalbail 			errno = save_errno;
13456e91bba0SGirish Moodalbail 		}
13466e91bba0SGirish Moodalbail 		/*
13476e91bba0SGirish Moodalbail 		 * See the comment before the SIOCGLIFGROUPNAME call.
13486e91bba0SGirish Moodalbail 		 */
13496e91bba0SGirish Moodalbail 		if (errno == EBUSY && (flags & IFF_IPMP))
13506e91bba0SGirish Moodalbail 			goto again;
13516e91bba0SGirish Moodalbail 
13526e91bba0SGirish Moodalbail 		ret = ipadm_errno2status(errno);
13536e91bba0SGirish Moodalbail 	}
13546e91bba0SGirish Moodalbail done:
13556e91bba0SGirish Moodalbail 	if (muxid_fd != -1)
13566e91bba0SGirish Moodalbail 		(void) close(muxid_fd);
13576e91bba0SGirish Moodalbail 	if (mux_fd != -1)
13586e91bba0SGirish Moodalbail 		(void) close(mux_fd);
13596e91bba0SGirish Moodalbail 
13606e91bba0SGirish Moodalbail 	if (af == AF_INET6 && ret == IPADM_SUCCESS) {
13616e91bba0SGirish Moodalbail 		/*
13626e91bba0SGirish Moodalbail 		 * in.ndpd maintains the phyints in its memory even after
13636e91bba0SGirish Moodalbail 		 * the interface is plumbed, so that it can be reused when
13646e91bba0SGirish Moodalbail 		 * the interface gets plumbed again. The default behavior
13656e91bba0SGirish Moodalbail 		 * of in.ndpd is to start autoconfiguration for an interface
13666e91bba0SGirish Moodalbail 		 * that gets plumbed. We need to send the
13676e91bba0SGirish Moodalbail 		 * message IPADM_ENABLE_AUTOCONF to in.ndpd to restore this
13686e91bba0SGirish Moodalbail 		 * default behavior on replumb.
13696e91bba0SGirish Moodalbail 		 */
13706e91bba0SGirish Moodalbail 		(void) i_ipadm_enable_autoconf(ifname);
13716e91bba0SGirish Moodalbail 	}
13726e91bba0SGirish Moodalbail 	return (ret);
13736e91bba0SGirish Moodalbail }
13746e91bba0SGirish Moodalbail 
13756e91bba0SGirish Moodalbail /*
13766e91bba0SGirish Moodalbail  * Saves the given interface name `ifname' with address family `af' in
13776e91bba0SGirish Moodalbail  * persistent DB.
13786e91bba0SGirish Moodalbail  */
13796e91bba0SGirish Moodalbail static ipadm_status_t
i_ipadm_persist_if(ipadm_handle_t iph,const char * ifname,sa_family_t af,uint32_t ipadm_flags)1380a73be61aSHans Rosenfeld i_ipadm_persist_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1381a73be61aSHans Rosenfeld     uint32_t ipadm_flags)
13826e91bba0SGirish Moodalbail {
13836e91bba0SGirish Moodalbail 	ipmgmt_if_arg_t		ifarg;
13846e91bba0SGirish Moodalbail 	int			err;
13856e91bba0SGirish Moodalbail 
13866e91bba0SGirish Moodalbail 	(void) strlcpy(ifarg.ia_ifname, ifname, sizeof (ifarg.ia_ifname));
13876e91bba0SGirish Moodalbail 	ifarg.ia_family = af;
1388a73be61aSHans Rosenfeld 	if (ipadm_flags & IPADM_OPT_IPMP)
1389a73be61aSHans Rosenfeld 		ifarg.ia_ifclass = IPADM_IF_CLASS_IPMP;
1390a73be61aSHans Rosenfeld 	else
1391a73be61aSHans Rosenfeld 		ifarg.ia_ifclass = IPADM_IF_CLASS_REGULAR;
1392a73be61aSHans Rosenfeld 
13936e91bba0SGirish Moodalbail 	ifarg.ia_cmd = IPMGMT_CMD_SETIF;
13946e91bba0SGirish Moodalbail 	ifarg.ia_flags = IPMGMT_PERSIST;
13956e91bba0SGirish Moodalbail 	err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
13966e91bba0SGirish Moodalbail 	return (ipadm_errno2status(err));
13976e91bba0SGirish Moodalbail }
13986e91bba0SGirish Moodalbail 
13996e91bba0SGirish Moodalbail /*
14006e91bba0SGirish Moodalbail  * Remove the IP interface from active configuration. If IPADM_OPT_PERSIST
14016e91bba0SGirish Moodalbail  * is set in `ipadm_flags', it is also removed from persistent configuration.
14026e91bba0SGirish Moodalbail  */
14036e91bba0SGirish Moodalbail ipadm_status_t
i_ipadm_delete_if(ipadm_handle_t iph,const char * ifname,sa_family_t af,uint32_t ipadm_flags)14046e91bba0SGirish Moodalbail i_ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
14056e91bba0SGirish Moodalbail     uint32_t ipadm_flags)
14066e91bba0SGirish Moodalbail {
14076e91bba0SGirish Moodalbail 	ipadm_status_t		ret = IPADM_SUCCESS;
14086e91bba0SGirish Moodalbail 	ipadm_status_t		db_status;
14096e91bba0SGirish Moodalbail 	char			tmp_ifname[LIFNAMSIZ];
14106e91bba0SGirish Moodalbail 	char			*cp;
14116e91bba0SGirish Moodalbail 	struct ipadm_addrobj_s	ipaddr;
14126e91bba0SGirish Moodalbail 	boolean_t		is_persistent =
14136e91bba0SGirish Moodalbail 	    (ipadm_flags & IPADM_OPT_PERSIST);
14146e91bba0SGirish Moodalbail 
14156e91bba0SGirish Moodalbail 	ret = i_ipadm_unplumb_if(iph, ifname, af);
14166e91bba0SGirish Moodalbail 	if (ret != IPADM_SUCCESS)
14176e91bba0SGirish Moodalbail 		goto done;
14186e91bba0SGirish Moodalbail 
14196e91bba0SGirish Moodalbail 	cp = strrchr(ifname, IPADM_LOGICAL_SEP);
14206e91bba0SGirish Moodalbail 	if (cp != NULL) {
14216e91bba0SGirish Moodalbail 		assert(iph->iph_flags & IPH_LEGACY);
14226e91bba0SGirish Moodalbail 		/*
14236e91bba0SGirish Moodalbail 		 * This is a non-zero logical interface.
14246e91bba0SGirish Moodalbail 		 * Find the addrobj and remove it from the daemon's memory.
14256e91bba0SGirish Moodalbail 		 */
14266e91bba0SGirish Moodalbail 		(void) strlcpy(tmp_ifname, ifname, sizeof (tmp_ifname));
14276e91bba0SGirish Moodalbail 		tmp_ifname[cp - ifname] = '\0';
14286e91bba0SGirish Moodalbail 		*cp++ = '\0';
14296e91bba0SGirish Moodalbail 		ipaddr.ipadm_lifnum = atoi(cp);
14306e91bba0SGirish Moodalbail 		(void) strlcpy(ipaddr.ipadm_ifname, tmp_ifname,
14316e91bba0SGirish Moodalbail 		    sizeof (ipaddr.ipadm_ifname));
14326e91bba0SGirish Moodalbail 		ipaddr.ipadm_af = af;
14336e91bba0SGirish Moodalbail 		ret = i_ipadm_get_lif2addrobj(iph, &ipaddr);
14346e91bba0SGirish Moodalbail 		if (ret == IPADM_SUCCESS) {
14356e91bba0SGirish Moodalbail 			ret = i_ipadm_delete_addrobj(iph, &ipaddr,
14366e91bba0SGirish Moodalbail 			    IPADM_OPT_ACTIVE);
14376e91bba0SGirish Moodalbail 		} else if (ret == IPADM_NOTFOUND) {
14386e91bba0SGirish Moodalbail 			ret = IPADM_SUCCESS;
14396e91bba0SGirish Moodalbail 		}
14406e91bba0SGirish Moodalbail 		return (ret);
14416e91bba0SGirish Moodalbail 	}
14426e91bba0SGirish Moodalbail done:
14436e91bba0SGirish Moodalbail 	/*
14446e91bba0SGirish Moodalbail 	 * Even if interface does not exist, remove all its addresses and
14456e91bba0SGirish Moodalbail 	 * properties from the persistent store. If interface does not
14466e91bba0SGirish Moodalbail 	 * exist both in kernel and the persistent store, return IPADM_ENXIO.
14476e91bba0SGirish Moodalbail 	 */
14486e91bba0SGirish Moodalbail 	if ((ret == IPADM_ENXIO && is_persistent) || ret == IPADM_SUCCESS) {
14496e91bba0SGirish Moodalbail 		db_status = i_ipadm_delete_ifobj(iph, ifname, af,
14506e91bba0SGirish Moodalbail 		    is_persistent);
14516e91bba0SGirish Moodalbail 		if (db_status == IPADM_SUCCESS)
14526e91bba0SGirish Moodalbail 			ret = IPADM_SUCCESS;
14536e91bba0SGirish Moodalbail 	}
14546e91bba0SGirish Moodalbail 
14556e91bba0SGirish Moodalbail 	return (ret);
14566e91bba0SGirish Moodalbail }
14576e91bba0SGirish Moodalbail 
14586e91bba0SGirish Moodalbail /*
14596e91bba0SGirish Moodalbail  * Resets all addresses on interface `ifname' with address family `af'
14606e91bba0SGirish Moodalbail  * from ipmgmtd daemon. If is_persistent = B_TRUE, all interface properties
14616e91bba0SGirish Moodalbail  * and address objects of `ifname' for `af' are also removed from the
14626e91bba0SGirish Moodalbail  * persistent DB.
14636e91bba0SGirish Moodalbail  */
14646e91bba0SGirish Moodalbail ipadm_status_t
i_ipadm_delete_ifobj(ipadm_handle_t iph,const char * ifname,sa_family_t af,boolean_t is_persistent)14656e91bba0SGirish Moodalbail i_ipadm_delete_ifobj(ipadm_handle_t iph, const char *ifname, sa_family_t af,
14666e91bba0SGirish Moodalbail     boolean_t is_persistent)
14676e91bba0SGirish Moodalbail {
14686e91bba0SGirish Moodalbail 	ipmgmt_if_arg_t		ifarg;
14696e91bba0SGirish Moodalbail 	int			err;
14706e91bba0SGirish Moodalbail 
14716e91bba0SGirish Moodalbail 	ifarg.ia_cmd = IPMGMT_CMD_RESETIF;
14726e91bba0SGirish Moodalbail 	ifarg.ia_flags = IPMGMT_ACTIVE;
14736e91bba0SGirish Moodalbail 	if (is_persistent)
14746e91bba0SGirish Moodalbail 		ifarg.ia_flags |= IPMGMT_PERSIST;
14756e91bba0SGirish Moodalbail 	ifarg.ia_family = af;
14766e91bba0SGirish Moodalbail 	(void) strlcpy(ifarg.ia_ifname, ifname, LIFNAMSIZ);
14776e91bba0SGirish Moodalbail 
14786e91bba0SGirish Moodalbail 	err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
14796e91bba0SGirish Moodalbail 	return (ipadm_errno2status(err));
14806e91bba0SGirish Moodalbail }
14816e91bba0SGirish Moodalbail 
14826e91bba0SGirish Moodalbail /*
14836e91bba0SGirish Moodalbail  * Create the interface by plumbing it for IP.
14846e91bba0SGirish Moodalbail  * This function will check if there is saved configuration information
14856e91bba0SGirish Moodalbail  * for `ifname' and return IPADM_OP_DISABLE_OBJ if the name-space
14866e91bba0SGirish Moodalbail  * for `ifname' is taken.
14876e91bba0SGirish Moodalbail  */
14886e91bba0SGirish Moodalbail ipadm_status_t
i_ipadm_create_if(ipadm_handle_t iph,char * ifname,sa_family_t af,uint32_t ipadm_flags)14896e91bba0SGirish Moodalbail i_ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
14906e91bba0SGirish Moodalbail     uint32_t ipadm_flags)
14916e91bba0SGirish Moodalbail {
14926e91bba0SGirish Moodalbail 	ipadm_status_t	status;
14936e91bba0SGirish Moodalbail 	boolean_t	p_exists;
14946e91bba0SGirish Moodalbail 	sa_family_t	other_af;
14956e91bba0SGirish Moodalbail 
14966e91bba0SGirish Moodalbail 	/*
14976e91bba0SGirish Moodalbail 	 * Return error, if the interface already exists in either the active
14986e91bba0SGirish Moodalbail 	 * or the persistent configuration.
14996e91bba0SGirish Moodalbail 	 */
15006e91bba0SGirish Moodalbail 	if (ipadm_if_enabled(iph, ifname, af))
15016e91bba0SGirish Moodalbail 		return (IPADM_IF_EXISTS);
15026e91bba0SGirish Moodalbail 
1503913a9028SVasumathi Sundaram 	if (!(iph->iph_flags & IPH_LEGACY)) {
1504913a9028SVasumathi Sundaram 		status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
1505913a9028SVasumathi Sundaram 		if (status != IPADM_SUCCESS)
1506913a9028SVasumathi Sundaram 			return (status);
1507913a9028SVasumathi Sundaram 		other_af = (af == AF_INET ? AF_INET6 : AF_INET);
1508913a9028SVasumathi Sundaram 		if (p_exists) {
1509913a9028SVasumathi Sundaram 			if (!ipadm_if_enabled(iph, ifname, other_af))
1510913a9028SVasumathi Sundaram 				return (IPADM_OP_DISABLE_OBJ);
1511913a9028SVasumathi Sundaram 			else
1512913a9028SVasumathi Sundaram 				ipadm_flags &= ~IPADM_OPT_PERSIST;
1513913a9028SVasumathi Sundaram 		}
15146e91bba0SGirish Moodalbail 	}
15156e91bba0SGirish Moodalbail 
15166e91bba0SGirish Moodalbail 	return (i_ipadm_plumb_if(iph, ifname, af, ipadm_flags));
15176e91bba0SGirish Moodalbail }
15186e91bba0SGirish Moodalbail 
15196e91bba0SGirish Moodalbail /*
15206e91bba0SGirish Moodalbail  * Plumbs an interface. Creates both IPv4 and IPv6 interfaces by
15216e91bba0SGirish Moodalbail  * default, unless a value in `af' is specified. The interface may be plumbed
15226e91bba0SGirish Moodalbail  * only if there is no previously saved persistent configuration information
15236e91bba0SGirish Moodalbail  * for the interface (in which case the ipadm_enable_if() function must
15246e91bba0SGirish Moodalbail  * be used to enable the interface).
15256e91bba0SGirish Moodalbail  *
15266e91bba0SGirish Moodalbail  * Returns: IPADM_SUCCESS, IPADM_FAILURE, IPADM_IF_EXISTS,
15276e91bba0SGirish Moodalbail  * IPADM_IF_PERSIST_EXISTS, IPADM_DLPI_FAILURE,
15286e91bba0SGirish Moodalbail  * or appropriate ipadm_status_t corresponding to the errno.
15296e91bba0SGirish Moodalbail  *
15306e91bba0SGirish Moodalbail  * `ifname' must point to memory that can hold upto LIFNAMSIZ chars. It may
15316e91bba0SGirish Moodalbail  * be over-written with the actual interface name when a PPA has to be
15326e91bba0SGirish Moodalbail  * internally generated by the library.
15336e91bba0SGirish Moodalbail  */
15346e91bba0SGirish Moodalbail ipadm_status_t
ipadm_create_if(ipadm_handle_t iph,char * ifname,sa_family_t af,uint32_t flags)15356e91bba0SGirish Moodalbail ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
15366e91bba0SGirish Moodalbail     uint32_t flags)
15376e91bba0SGirish Moodalbail {
15386e91bba0SGirish Moodalbail 	ipadm_status_t	status;
15396e91bba0SGirish Moodalbail 	boolean_t	created_v4 = B_FALSE;
15406e91bba0SGirish Moodalbail 	char		newifname[LIFNAMSIZ];
15416e91bba0SGirish Moodalbail 
15426e91bba0SGirish Moodalbail 	/* Check for the required authorization */
15436e91bba0SGirish Moodalbail 	if (!ipadm_check_auth())
15446e91bba0SGirish Moodalbail 		return (IPADM_EAUTH);
15456e91bba0SGirish Moodalbail 
15466e91bba0SGirish Moodalbail 	if (flags == 0 || ((flags & IPADM_OPT_PERSIST) &&
15476e91bba0SGirish Moodalbail 	    !(flags & IPADM_OPT_ACTIVE)) ||
15486e91bba0SGirish Moodalbail 	    (flags & ~(IPADM_COMMON_OPT_MASK | IPADM_OPT_IPMP |
15496e91bba0SGirish Moodalbail 	    IPADM_OPT_GENPPA))) {
15506e91bba0SGirish Moodalbail 		return (IPADM_INVALID_ARG);
15516e91bba0SGirish Moodalbail 	}
15526e91bba0SGirish Moodalbail 	if (flags & IPADM_OPT_GENPPA) {
15536e91bba0SGirish Moodalbail 		if (snprintf(newifname, LIFNAMSIZ, "%s0", ifname) >=
15546e91bba0SGirish Moodalbail 		    LIFNAMSIZ)
15556e91bba0SGirish Moodalbail 			return (IPADM_INVALID_ARG);
15566e91bba0SGirish Moodalbail 	} else {
15576e91bba0SGirish Moodalbail 		if (strlcpy(newifname, ifname, LIFNAMSIZ) >= LIFNAMSIZ)
15586e91bba0SGirish Moodalbail 			return (IPADM_INVALID_ARG);
15596e91bba0SGirish Moodalbail 	}
15606e91bba0SGirish Moodalbail 
15616e91bba0SGirish Moodalbail 	if (!i_ipadm_validate_ifname(iph, newifname))
15626e91bba0SGirish Moodalbail 		return (IPADM_INVALID_ARG);
15636e91bba0SGirish Moodalbail 
15646e91bba0SGirish Moodalbail 	if ((af == AF_INET || af == AF_UNSPEC) &&
15656e91bba0SGirish Moodalbail 	    !i_ipadm_is_6to4(iph, ifname)) {
15666e91bba0SGirish Moodalbail 		status = i_ipadm_create_if(iph, ifname, AF_INET, flags);
15676e91bba0SGirish Moodalbail 		if (status != IPADM_SUCCESS)
15686e91bba0SGirish Moodalbail 			return (status);
15696e91bba0SGirish Moodalbail 		created_v4 = B_TRUE;
15706e91bba0SGirish Moodalbail 	}
15716e91bba0SGirish Moodalbail 	if (af == AF_INET6 || af == AF_UNSPEC) {
15726e91bba0SGirish Moodalbail 		status = i_ipadm_create_if(iph, ifname, AF_INET6, flags);
15736e91bba0SGirish Moodalbail 		if (status != IPADM_SUCCESS) {
15746e91bba0SGirish Moodalbail 			if (created_v4) {
15756e91bba0SGirish Moodalbail 				(void) i_ipadm_delete_if(iph, ifname, AF_INET,
15766e91bba0SGirish Moodalbail 				    IPADM_OPT_ACTIVE);
15776e91bba0SGirish Moodalbail 			}
15786e91bba0SGirish Moodalbail 			return (status);
15796e91bba0SGirish Moodalbail 		}
15806e91bba0SGirish Moodalbail 	}
15816e91bba0SGirish Moodalbail 
15826e91bba0SGirish Moodalbail 	return (IPADM_SUCCESS);
15836e91bba0SGirish Moodalbail }
15846e91bba0SGirish Moodalbail 
1585a73be61aSHans Rosenfeld ipadm_status_t
ipadm_add_ipmp_member(ipadm_handle_t iph,const char * gifname,const char * mifname,uint32_t ipadm_flags)1586a73be61aSHans Rosenfeld ipadm_add_ipmp_member(ipadm_handle_t iph, const char *gifname,
1587a73be61aSHans Rosenfeld     const char *mifname, uint32_t ipadm_flags)
1588a73be61aSHans Rosenfeld {
1589a73be61aSHans Rosenfeld 	return (i_ipadm_update_ipmp(iph, gifname, mifname,
1590a73be61aSHans Rosenfeld 	    ipadm_flags, IPADM_ADD_IPMP));
1591a73be61aSHans Rosenfeld }
1592a73be61aSHans Rosenfeld 
1593a73be61aSHans Rosenfeld ipadm_status_t
ipadm_remove_ipmp_member(ipadm_handle_t iph,const char * gifname,const char * mifname,uint32_t ipadm_flags)1594a73be61aSHans Rosenfeld ipadm_remove_ipmp_member(ipadm_handle_t iph, const char *gifname,
1595a73be61aSHans Rosenfeld     const char *mifname, uint32_t ipadm_flags)
1596a73be61aSHans Rosenfeld {
1597a73be61aSHans Rosenfeld 	return (i_ipadm_update_ipmp(iph, gifname, mifname,
1598a73be61aSHans Rosenfeld 	    ipadm_flags, IPADM_REMOVE_IPMP));
1599a73be61aSHans Rosenfeld }
1600a73be61aSHans Rosenfeld 
1601a73be61aSHans Rosenfeld /*
1602a73be61aSHans Rosenfeld  * Updates active IPMP configuration according to the specified
1603a73be61aSHans Rosenfeld  * command. It also persists the configuration if IPADM_OPT_PERSIST
1604a73be61aSHans Rosenfeld  * is set in `ipadm_flags'.
1605a73be61aSHans Rosenfeld  */
1606a73be61aSHans Rosenfeld static ipadm_status_t
i_ipadm_update_ipmp(ipadm_handle_t iph,const char * gifname,const char * mifname,uint32_t ipadm_flags,ipadm_ipmp_op_t op)1607a73be61aSHans Rosenfeld i_ipadm_update_ipmp(ipadm_handle_t iph, const char *gifname,
1608a73be61aSHans Rosenfeld     const char *mifname, uint32_t ipadm_flags, ipadm_ipmp_op_t op)
1609a73be61aSHans Rosenfeld {
1610a73be61aSHans Rosenfeld 	ipadm_status_t status;
1611a73be61aSHans Rosenfeld 	char	groupname1[LIFGRNAMSIZ];
1612a73be61aSHans Rosenfeld 	char	groupname2[LIFGRNAMSIZ];
1613a73be61aSHans Rosenfeld 
1614a73be61aSHans Rosenfeld 	/* Check for the required authorization */
1615a73be61aSHans Rosenfeld 	if (!ipadm_check_auth())
1616a73be61aSHans Rosenfeld 		return (IPADM_EAUTH);
1617a73be61aSHans Rosenfeld 
1618a73be61aSHans Rosenfeld 	if (!(ipadm_flags & IPADM_OPT_ACTIVE) ||
1619a73be61aSHans Rosenfeld 	    gifname == NULL || mifname == NULL)
1620a73be61aSHans Rosenfeld 		return (IPADM_INVALID_ARG);
1621a73be61aSHans Rosenfeld 
1622a73be61aSHans Rosenfeld 	if (!ipadm_if_enabled(iph, gifname, AF_UNSPEC) ||
1623a73be61aSHans Rosenfeld 	    !ipadm_if_enabled(iph, mifname, AF_UNSPEC))
1624a73be61aSHans Rosenfeld 		return (IPADM_OP_DISABLE_OBJ);
1625a73be61aSHans Rosenfeld 
1626a73be61aSHans Rosenfeld 	if (!i_ipadm_is_ipmp(iph, gifname))
1627a73be61aSHans Rosenfeld 		return (IPADM_INVALID_ARG);
1628a73be61aSHans Rosenfeld 
1629a73be61aSHans Rosenfeld 	if (op == IPADM_ADD_IPMP && i_ipadm_is_under_ipmp(iph, mifname))
1630a73be61aSHans Rosenfeld 		return (IPADM_IF_INUSE);
1631a73be61aSHans Rosenfeld 
1632a73be61aSHans Rosenfeld 	if ((status = i_ipadm_get_groupname_active(iph, gifname,
1633a73be61aSHans Rosenfeld 	    groupname2, sizeof (groupname2))) != IPADM_SUCCESS)
1634a73be61aSHans Rosenfeld 		return (status);
1635a73be61aSHans Rosenfeld 
1636a73be61aSHans Rosenfeld 	if (op == IPADM_REMOVE_IPMP) {
1637a73be61aSHans Rosenfeld 		if ((status = i_ipadm_get_groupname_active(iph, mifname,
1638a73be61aSHans Rosenfeld 		    groupname1, sizeof (groupname1))) != IPADM_SUCCESS)
1639a73be61aSHans Rosenfeld 			return (status);
1640a73be61aSHans Rosenfeld 
1641a73be61aSHans Rosenfeld 		if (groupname1[0] == '\0' ||
1642a73be61aSHans Rosenfeld 		    strcmp(groupname1, groupname2) != 0)
1643a73be61aSHans Rosenfeld 			return (IPADM_INVALID_ARG);
1644a73be61aSHans Rosenfeld 
1645a73be61aSHans Rosenfeld 		groupname2[0] = '\0';
1646a73be61aSHans Rosenfeld 	}
1647a73be61aSHans Rosenfeld 
1648a73be61aSHans Rosenfeld 	if ((ipadm_flags & IPADM_OPT_PERSIST) &&
1649a73be61aSHans Rosenfeld 	    (status = i_ipadm_persist_update_ipmp(iph, gifname,
1650a73be61aSHans Rosenfeld 	    mifname, op)) != IPADM_SUCCESS)
1651a73be61aSHans Rosenfeld 		return (status);
1652a73be61aSHans Rosenfeld 
1653a73be61aSHans Rosenfeld 	return (i_ipadm_set_groupname_active(iph, mifname, groupname2));
1654a73be61aSHans Rosenfeld }
1655a73be61aSHans Rosenfeld 
1656a73be61aSHans Rosenfeld /*
1657a73be61aSHans Rosenfeld  * Call the ipmgmtd to update the IPMP configuration in ipadm DB.
1658a73be61aSHans Rosenfeld  * After this call the DB will know that mifname is under gifname and
1659a73be61aSHans Rosenfeld  * gifname has a member, which name is mifname.
1660a73be61aSHans Rosenfeld  */
1661a73be61aSHans Rosenfeld static ipadm_status_t
i_ipadm_persist_update_ipmp(ipadm_handle_t iph,const char * gifname,const char * mifname,ipadm_ipmp_op_t op)1662a73be61aSHans Rosenfeld i_ipadm_persist_update_ipmp(ipadm_handle_t iph, const char *gifname,
1663a73be61aSHans Rosenfeld     const char *mifname, ipadm_ipmp_op_t op)
1664a73be61aSHans Rosenfeld {
1665a73be61aSHans Rosenfeld 	ipmgmt_ipmp_update_arg_t args;
1666a73be61aSHans Rosenfeld 	int err;
1667a73be61aSHans Rosenfeld 
1668a73be61aSHans Rosenfeld 	assert(op == IPADM_ADD_IPMP || op == IPADM_REMOVE_IPMP);
1669a73be61aSHans Rosenfeld 
1670a73be61aSHans Rosenfeld 	bzero(&args, sizeof (ipmgmt_ipmp_update_arg_t));
1671a73be61aSHans Rosenfeld 
1672a73be61aSHans Rosenfeld 	args.ia_cmd = IPMGMT_CMD_IPMP_UPDATE;
1673a73be61aSHans Rosenfeld 
1674a73be61aSHans Rosenfeld 	(void) strlcpy(args.ia_gifname, gifname, sizeof (args.ia_gifname));
1675a73be61aSHans Rosenfeld 	(void) strlcpy(args.ia_mifname, mifname, sizeof (args.ia_mifname));
1676a73be61aSHans Rosenfeld 
1677a73be61aSHans Rosenfeld 	if (op == IPADM_ADD_IPMP)
1678a73be61aSHans Rosenfeld 		args.ia_flags = IPMGMT_APPEND;
1679a73be61aSHans Rosenfeld 	else
1680a73be61aSHans Rosenfeld 		args.ia_flags = IPMGMT_REMOVE;
1681a73be61aSHans Rosenfeld 
1682a73be61aSHans Rosenfeld 	args.ia_flags |= IPMGMT_PERSIST;
1683a73be61aSHans Rosenfeld 
1684a73be61aSHans Rosenfeld 	err = ipadm_door_call(iph, &args, sizeof (args), NULL, 0, B_FALSE);
1685a73be61aSHans Rosenfeld 	return (ipadm_errno2status(err));
1686a73be61aSHans Rosenfeld }
1687a73be61aSHans Rosenfeld 
16886e91bba0SGirish Moodalbail /*
16896e91bba0SGirish Moodalbail  * Deletes the interface in `ifname'. Removes both IPv4 and IPv6 interfaces
16906e91bba0SGirish Moodalbail  * when `af' = AF_UNSPEC.
16916e91bba0SGirish Moodalbail  */
16926e91bba0SGirish Moodalbail ipadm_status_t
ipadm_delete_if(ipadm_handle_t iph,const char * ifname,sa_family_t af,uint32_t flags)16936e91bba0SGirish Moodalbail ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
16946e91bba0SGirish Moodalbail     uint32_t flags)
16956e91bba0SGirish Moodalbail {
16966e91bba0SGirish Moodalbail 	ipadm_status_t status1 = IPADM_SUCCESS;
16976e91bba0SGirish Moodalbail 	ipadm_status_t status2 = IPADM_SUCCESS;
16986e91bba0SGirish Moodalbail 	ipadm_status_t other;
16996e91bba0SGirish Moodalbail 
17006e91bba0SGirish Moodalbail 	/* Check for the required authorization */
17016e91bba0SGirish Moodalbail 	if (!ipadm_check_auth())
17026e91bba0SGirish Moodalbail 		return (IPADM_EAUTH);
17036e91bba0SGirish Moodalbail 
17046e91bba0SGirish Moodalbail 	/* Validate the `ifname' for any logical interface. */
17056e91bba0SGirish Moodalbail 	if (flags == 0 || (flags & ~(IPADM_COMMON_OPT_MASK)) ||
17066e91bba0SGirish Moodalbail 	    !i_ipadm_validate_ifname(iph, ifname))
17076e91bba0SGirish Moodalbail 		return (IPADM_INVALID_ARG);
17086e91bba0SGirish Moodalbail 
17096e91bba0SGirish Moodalbail 	if (af == AF_INET || af == AF_UNSPEC)
17106e91bba0SGirish Moodalbail 		status1 = i_ipadm_delete_if(iph, ifname, AF_INET, flags);
17116e91bba0SGirish Moodalbail 	if (af == AF_INET6 || af == AF_UNSPEC)
17126e91bba0SGirish Moodalbail 		status2 = i_ipadm_delete_if(iph, ifname, AF_INET6, flags);
17136e91bba0SGirish Moodalbail 	/*
17146e91bba0SGirish Moodalbail 	 * If the family has been uniquely identified, we return the
17156e91bba0SGirish Moodalbail 	 * associated status, even if that is ENXIO. Calls from ifconfig
17166e91bba0SGirish Moodalbail 	 * which can only unplumb one of IPv4/IPv6 at any time fall under
17176e91bba0SGirish Moodalbail 	 * this category.
17186e91bba0SGirish Moodalbail 	 */
17196e91bba0SGirish Moodalbail 	if (af == AF_INET)
17206e91bba0SGirish Moodalbail 		return (status1);
17216e91bba0SGirish Moodalbail 	else if (af == AF_INET6)
17226e91bba0SGirish Moodalbail 		return (status2);
17236e91bba0SGirish Moodalbail 	else if (af != AF_UNSPEC)
17246e91bba0SGirish Moodalbail 		return (IPADM_INVALID_ARG);
17256e91bba0SGirish Moodalbail 
17266e91bba0SGirish Moodalbail 	/*
17276e91bba0SGirish Moodalbail 	 * If af is AF_UNSPEC, then we return the following:
17286e91bba0SGirish Moodalbail 	 * status1,		if status1 == status2
17296e91bba0SGirish Moodalbail 	 * IPADM_SUCCESS,	if either of status1 or status2 is SUCCESS
1730ed1e9379SHans Rosenfeld 	 *			and the other status is ENXIO
17316e91bba0SGirish Moodalbail 	 * IPADM_ENXIO,		if both status1 and status2 are ENXIO
17326e91bba0SGirish Moodalbail 	 * IPADM_FAILURE	otherwise.
17336e91bba0SGirish Moodalbail 	 */
17346e91bba0SGirish Moodalbail 	if (status1 == status2) {
17356e91bba0SGirish Moodalbail 		/* covers the case when both status1 and status2 are ENXIO */
17366e91bba0SGirish Moodalbail 		return (status1);
17376e91bba0SGirish Moodalbail 	} else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
17386e91bba0SGirish Moodalbail 		if (status1 == IPADM_SUCCESS)
17396e91bba0SGirish Moodalbail 			other = status2;
17406e91bba0SGirish Moodalbail 		else
17416e91bba0SGirish Moodalbail 			other = status1;
17426e91bba0SGirish Moodalbail 		return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
17436e91bba0SGirish Moodalbail 	} else {
17446e91bba0SGirish Moodalbail 		return (IPADM_FAILURE);
17456e91bba0SGirish Moodalbail 	}
17466e91bba0SGirish Moodalbail }
17476e91bba0SGirish Moodalbail 
17486e91bba0SGirish Moodalbail /*
17496e91bba0SGirish Moodalbail  * Returns information about all interfaces in both active and persistent
17506e91bba0SGirish Moodalbail  * configuration. If `ifname' is not NULL, it returns only the interface
17516e91bba0SGirish Moodalbail  * identified by `ifname'.
17526e91bba0SGirish Moodalbail  *
17536e91bba0SGirish Moodalbail  * Return values:
1754ed1e9379SHans Rosenfeld  *	On success: IPADM_SUCCESS.
1755ed1e9379SHans Rosenfeld  *	On error  : IPADM_INVALID_ARG, IPADM_ENXIO or IPADM_FAILURE.
17566e91bba0SGirish Moodalbail  */
17576e91bba0SGirish Moodalbail ipadm_status_t
ipadm_if_info(ipadm_handle_t iph,const char * ifname,ipadm_if_info_t ** if_info,uint32_t flags,int64_t lifc_flags)17586e91bba0SGirish Moodalbail ipadm_if_info(ipadm_handle_t iph, const char *ifname,
1759a73be61aSHans Rosenfeld     ipadm_if_info_t **if_info, uint32_t flags, int64_t lifc_flags)
17606e91bba0SGirish Moodalbail {
17616e91bba0SGirish Moodalbail 	ipadm_status_t	status;
17626e91bba0SGirish Moodalbail 	ifspec_t	ifsp;
17636e91bba0SGirish Moodalbail 
17646e91bba0SGirish Moodalbail 	if (if_info == NULL || iph == NULL || flags != 0)
17656e91bba0SGirish Moodalbail 		return (IPADM_INVALID_ARG);
17666e91bba0SGirish Moodalbail 
17676e91bba0SGirish Moodalbail 	if (ifname != NULL &&
17686e91bba0SGirish Moodalbail 	    (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)) {
17696e91bba0SGirish Moodalbail 		return (IPADM_INVALID_ARG);
17706e91bba0SGirish Moodalbail 	}
17716e91bba0SGirish Moodalbail 
17726e91bba0SGirish Moodalbail 	status = i_ipadm_get_all_if_info(iph, ifname, if_info, lifc_flags);
17736e91bba0SGirish Moodalbail 	if (status != IPADM_SUCCESS)
17746e91bba0SGirish Moodalbail 		return (status);
17756e91bba0SGirish Moodalbail 	if (ifname != NULL && *if_info == NULL)
17766e91bba0SGirish Moodalbail 		return (IPADM_ENXIO);
17776e91bba0SGirish Moodalbail 
17786e91bba0SGirish Moodalbail 	return (IPADM_SUCCESS);
17796e91bba0SGirish Moodalbail }
17806e91bba0SGirish Moodalbail 
17816e91bba0SGirish Moodalbail /*
17826e91bba0SGirish Moodalbail  * Frees the linked list allocated by ipadm_if_info().
17836e91bba0SGirish Moodalbail  */
17846e91bba0SGirish Moodalbail void
ipadm_free_if_info(ipadm_if_info_t * ifinfo)1785a73be61aSHans Rosenfeld ipadm_free_if_info(ipadm_if_info_t *ifinfo)
17866e91bba0SGirish Moodalbail {
1787a73be61aSHans Rosenfeld 	ipadm_if_info_t	*ifinfo_next;
17886e91bba0SGirish Moodalbail 
17896e91bba0SGirish Moodalbail 	for (; ifinfo != NULL; ifinfo = ifinfo_next) {
1790a73be61aSHans Rosenfeld 		ifinfo_next = ifinfo->ifi_next;
1791a73be61aSHans Rosenfeld 		i_ipadm_free_ipmp_members(&ifinfo->ifi_ipmp_cmembers);
1792a73be61aSHans Rosenfeld 		i_ipadm_free_ipmp_members(&ifinfo->ifi_ipmp_pmembers);
17936e91bba0SGirish Moodalbail 		free(ifinfo);
17946e91bba0SGirish Moodalbail 	}
17956e91bba0SGirish Moodalbail }
17966e91bba0SGirish Moodalbail 
1797a73be61aSHans Rosenfeld static void
i_ipadm_free_ipmp_members(ipadm_ipmp_members_t * ipmp_members)1798a73be61aSHans Rosenfeld i_ipadm_free_ipmp_members(ipadm_ipmp_members_t *ipmp_members)
1799a73be61aSHans Rosenfeld {
1800a73be61aSHans Rosenfeld 	ipadm_ipmp_member_t *ipmp_member;
1801a73be61aSHans Rosenfeld 
1802a73be61aSHans Rosenfeld 	while ((ipmp_member = list_remove_head(ipmp_members)) != NULL)
1803a73be61aSHans Rosenfeld 		free(ipmp_member);
1804a73be61aSHans Rosenfeld 
1805a73be61aSHans Rosenfeld 	list_destroy(ipmp_members);
1806a73be61aSHans Rosenfeld }
1807a73be61aSHans Rosenfeld 
18086e91bba0SGirish Moodalbail /*
18096e91bba0SGirish Moodalbail  * Re-enable the interface `ifname' based on the saved configuration
18106e91bba0SGirish Moodalbail  * for `ifname'.
18116e91bba0SGirish Moodalbail  */
18126e91bba0SGirish Moodalbail ipadm_status_t
ipadm_enable_if(ipadm_handle_t iph,const char * ifname,uint32_t flags)18136e91bba0SGirish Moodalbail ipadm_enable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
18146e91bba0SGirish Moodalbail {
1815a73be61aSHans Rosenfeld 	boolean_t	set_init = B_FALSE;
18166e91bba0SGirish Moodalbail 	nvlist_t	*ifnvl;
18176e91bba0SGirish Moodalbail 	ipadm_status_t	status;
18186e91bba0SGirish Moodalbail 	ifspec_t	ifsp;
18196e91bba0SGirish Moodalbail 
18206e91bba0SGirish Moodalbail 	/* Check for the required authorization */
18216e91bba0SGirish Moodalbail 	if (!ipadm_check_auth())
18226e91bba0SGirish Moodalbail 		return (IPADM_EAUTH);
18236e91bba0SGirish Moodalbail 
18246e91bba0SGirish Moodalbail 	/* Check for logical interfaces. */
18256e91bba0SGirish Moodalbail 	if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
18266e91bba0SGirish Moodalbail 		return (IPADM_INVALID_ARG);
18276e91bba0SGirish Moodalbail 
18286e91bba0SGirish Moodalbail 	/* Enabling an interface persistently is not supported. */
18296e91bba0SGirish Moodalbail 	if (flags & IPADM_OPT_PERSIST)
18306e91bba0SGirish Moodalbail 		return (IPADM_NOTSUP);
18316e91bba0SGirish Moodalbail 
18326e91bba0SGirish Moodalbail 	/*
18336e91bba0SGirish Moodalbail 	 * Return early by checking if the interface is already enabled.
18346e91bba0SGirish Moodalbail 	 */
18356e91bba0SGirish Moodalbail 	if (ipadm_if_enabled(iph, ifname, AF_INET) &&
1836a73be61aSHans Rosenfeld 	    ipadm_if_enabled(iph, ifname, AF_INET6))
18376e91bba0SGirish Moodalbail 		return (IPADM_IF_EXISTS);
1838a73be61aSHans Rosenfeld 
18396e91bba0SGirish Moodalbail 	/*
18406e91bba0SGirish Moodalbail 	 * Enable the interface and restore all its interface properties
18416e91bba0SGirish Moodalbail 	 * and address objects.
18426e91bba0SGirish Moodalbail 	 */
18436e91bba0SGirish Moodalbail 	status = i_ipadm_init_ifs(iph, ifname, &ifnvl);
18446e91bba0SGirish Moodalbail 	if (status != IPADM_SUCCESS)
18456e91bba0SGirish Moodalbail 		return (status);
18466e91bba0SGirish Moodalbail 
18476e91bba0SGirish Moodalbail 	assert(ifnvl != NULL);
18486e91bba0SGirish Moodalbail 	/*
18496e91bba0SGirish Moodalbail 	 * ipadm_enable_if() does exactly what ipadm_init_ifs() does,
18506e91bba0SGirish Moodalbail 	 * but only for one interface. We need to set IPH_INIT because
1851a73be61aSHans Rosenfeld 	 * ipmgmtd daemon does not have to write the interface to the
1852a73be61aSHans Rosenfeld 	 * persistent db. The interface is already available in the
1853a73be61aSHans Rosenfeld 	 * persistent db and we are here to re-enable the persistent
1854a73be61aSHans Rosenfeld 	 * configuration.
1855a73be61aSHans Rosenfeld 	 *
1856a73be61aSHans Rosenfeld 	 * But we need to make sure we're not accidentally clearing an
1857a73be61aSHans Rosenfeld 	 * IPH_INIT flag that was already set when we were called.
18586e91bba0SGirish Moodalbail 	 */
1859a73be61aSHans Rosenfeld 	if ((iph->iph_flags & IPH_INIT) == 0) {
1860a73be61aSHans Rosenfeld 		iph->iph_flags |= IPH_INIT;
1861a73be61aSHans Rosenfeld 		set_init = B_TRUE;
1862a73be61aSHans Rosenfeld 	}
1863a73be61aSHans Rosenfeld 
18646e91bba0SGirish Moodalbail 	status = i_ipadm_init_ifobj(iph, ifname, ifnvl);
1865a73be61aSHans Rosenfeld 
1866a73be61aSHans Rosenfeld 	if (set_init)
1867a73be61aSHans Rosenfeld 		iph->iph_flags &= ~IPH_INIT;
1868cc6b3039SYuri Pankov 
1869cc6b3039SYuri Pankov 	nvlist_free(ifnvl);
18706e91bba0SGirish Moodalbail 	return (status);
18716e91bba0SGirish Moodalbail }
18726e91bba0SGirish Moodalbail 
18736e91bba0SGirish Moodalbail /*
18746e91bba0SGirish Moodalbail  * Disable the interface `ifname' by removing it from the active configuration.
18756e91bba0SGirish Moodalbail  * Error code return values follow the model in ipadm_delete_if()
18766e91bba0SGirish Moodalbail  */
18776e91bba0SGirish Moodalbail ipadm_status_t
ipadm_disable_if(ipadm_handle_t iph,const char * ifname,uint32_t flags)18786e91bba0SGirish Moodalbail ipadm_disable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
18796e91bba0SGirish Moodalbail {
18806e91bba0SGirish Moodalbail 	ipadm_status_t	status1, status2, other;
18816e91bba0SGirish Moodalbail 	ifspec_t	ifsp;
18826e91bba0SGirish Moodalbail 
18836e91bba0SGirish Moodalbail 	/* Check for the required authorization */
18846e91bba0SGirish Moodalbail 	if (!ipadm_check_auth())
18856e91bba0SGirish Moodalbail 		return (IPADM_EAUTH);
18866e91bba0SGirish Moodalbail 
18876e91bba0SGirish Moodalbail 	/* Check for logical interfaces. */
18886e91bba0SGirish Moodalbail 	if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
18896e91bba0SGirish Moodalbail 		return (IPADM_INVALID_ARG);
18906e91bba0SGirish Moodalbail 
18916e91bba0SGirish Moodalbail 	/* Disabling an interface persistently is not supported. */
18926e91bba0SGirish Moodalbail 	if (flags & IPADM_OPT_PERSIST)
18936e91bba0SGirish Moodalbail 		return (IPADM_NOTSUP);
18946e91bba0SGirish Moodalbail 
18956e91bba0SGirish Moodalbail 	status1 = i_ipadm_unplumb_if(iph, ifname, AF_INET6);
18966e91bba0SGirish Moodalbail 	if (status1 == IPADM_SUCCESS)
18976e91bba0SGirish Moodalbail 		status1 = i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
18986e91bba0SGirish Moodalbail 	status2 = i_ipadm_unplumb_if(iph, ifname, AF_INET);
18996e91bba0SGirish Moodalbail 	if (status2 == IPADM_SUCCESS)
19006e91bba0SGirish Moodalbail 		status2 = i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
19016e91bba0SGirish Moodalbail 	if (status1 == status2) {
19026e91bba0SGirish Moodalbail 		return (status2);
19036e91bba0SGirish Moodalbail 	} else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
19046e91bba0SGirish Moodalbail 		if (status1 == IPADM_SUCCESS)
19056e91bba0SGirish Moodalbail 			other = status2;
19066e91bba0SGirish Moodalbail 		else
19076e91bba0SGirish Moodalbail 			other = status1;
19086e91bba0SGirish Moodalbail 		return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
19096e91bba0SGirish Moodalbail 	} else {
19106e91bba0SGirish Moodalbail 		return (IPADM_FAILURE);
19116e91bba0SGirish Moodalbail 	}
19126e91bba0SGirish Moodalbail }
191336b41818SGirish Moodalbail 
191436b41818SGirish Moodalbail /*
1915bbf21555SRichard Lowe  * FIXME Remove this when ifconfig(8) is updated to use IPMP support
1916a73be61aSHans Rosenfeld  * in libipadm.
1917a73be61aSHans Rosenfeld  */
1918a73be61aSHans Rosenfeld /*
1919bbf21555SRichard Lowe  * This workaround is required by ifconfig(8) whenever an
1920a73be61aSHans Rosenfeld  * interface is moved into an IPMP group to update the daemon's
1921a73be61aSHans Rosenfeld  * in-memory mapping of `aobjname' to 'lifnum'.
192236b41818SGirish Moodalbail  *
192336b41818SGirish Moodalbail  * For `IPMGMT_ACTIVE' case, i_ipadm_delete_ifobj() would only fail if
192436b41818SGirish Moodalbail  * door_call(3C) fails. Also, there is no use in returning error because
192536b41818SGirish Moodalbail  * `ifname' would have been successfuly moved into IPMP group, by this time.
192636b41818SGirish Moodalbail  */
192736b41818SGirish Moodalbail void
ipadm_if_move(ipadm_handle_t iph,const char * ifname)192836b41818SGirish Moodalbail ipadm_if_move(ipadm_handle_t iph, const char *ifname)
192936b41818SGirish Moodalbail {
193036b41818SGirish Moodalbail 	(void) i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
193136b41818SGirish Moodalbail 	(void) i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
193236b41818SGirish Moodalbail }
1933a73be61aSHans Rosenfeld 
1934a73be61aSHans Rosenfeld ipadm_status_t
i_ipadm_set_groupname_active(ipadm_handle_t iph,const char * ifname,const char * groupname)1935a73be61aSHans Rosenfeld i_ipadm_set_groupname_active(ipadm_handle_t iph, const char *ifname,
1936a73be61aSHans Rosenfeld     const char *groupname)
1937a73be61aSHans Rosenfeld {
1938a73be61aSHans Rosenfeld 	struct lifreq   lifr;
1939a73be61aSHans Rosenfeld 	ipadm_addr_info_t *addrinfo, *ia;
1940a73be61aSHans Rosenfeld 	ipadm_status_t	status = IPADM_SUCCESS;
1941a73be61aSHans Rosenfeld 
1942a73be61aSHans Rosenfeld 	(void) memset(&lifr, 0, sizeof (lifr));
1943a73be61aSHans Rosenfeld 
1944a73be61aSHans Rosenfeld 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
1945a73be61aSHans Rosenfeld 	(void) strlcpy(lifr.lifr_groupname, groupname,
1946a73be61aSHans Rosenfeld 	    sizeof (lifr.lifr_groupname));
1947a73be61aSHans Rosenfeld 
1948a73be61aSHans Rosenfeld 	/* Disable all addresses on the interface */
1949a73be61aSHans Rosenfeld 	(void) i_ipadm_active_addr_info(iph, ifname, &addrinfo,
1950a73be61aSHans Rosenfeld 	    IPADM_OPT_ACTIVE | IPADM_OPT_ZEROADDR, IFF_UP | IFF_DUPLICATE);
1951a73be61aSHans Rosenfeld 
1952a73be61aSHans Rosenfeld 	for (ia = addrinfo; ia != NULL; ia = IA_NEXT(ia)) {
1953a73be61aSHans Rosenfeld 		if (strlen(ia->ia_aobjname) > 0) {
1954a73be61aSHans Rosenfeld 			(void) ipadm_disable_addr(iph, ia->ia_aobjname, 0);
1955a73be61aSHans Rosenfeld 		} else {
1956a73be61aSHans Rosenfeld 			/*
1957a73be61aSHans Rosenfeld 			 * There's an address on this interfaces with no
1958a73be61aSHans Rosenfeld 			 * corresponding addrobj. Just clear IFF_UP.
1959a73be61aSHans Rosenfeld 			 */
1960a73be61aSHans Rosenfeld 			(void) i_ipadm_set_flags(iph, ifname,
1961a73be61aSHans Rosenfeld 			    addrinfo->ia_ifa.ifa_addr->sa_family, 0, IFF_UP);
1962a73be61aSHans Rosenfeld 		}
1963a73be61aSHans Rosenfeld 	}
1964a73be61aSHans Rosenfeld 
1965a73be61aSHans Rosenfeld 	if (ioctl(iph->iph_sock, SIOCSLIFGROUPNAME, (caddr_t)&lifr) == -1 &&
1966a73be61aSHans Rosenfeld 	    ioctl(iph->iph_sock6, SIOCSLIFGROUPNAME, (caddr_t)&lifr) == -1)
1967a73be61aSHans Rosenfeld 		status = ipadm_errno2status(errno);
1968a73be61aSHans Rosenfeld 
1969a73be61aSHans Rosenfeld 	/* Enable all addresses on the interface */
1970a73be61aSHans Rosenfeld 	for (ia = addrinfo; ia != NULL; ia = IA_NEXT(ia)) {
1971a73be61aSHans Rosenfeld 		if (strlen(ia->ia_aobjname) > 0) {
1972a73be61aSHans Rosenfeld 			(void) ipadm_enable_addr(iph, ia->ia_aobjname, 0);
1973a73be61aSHans Rosenfeld 		} else {
1974a73be61aSHans Rosenfeld 			/*
1975a73be61aSHans Rosenfeld 			 * There's an address on this interfaces with no
1976a73be61aSHans Rosenfeld 			 * corresponding addrobj. Just set IFF_UP.
1977a73be61aSHans Rosenfeld 			 */
1978a73be61aSHans Rosenfeld 			(void) i_ipadm_set_flags(iph, ifname,
1979a73be61aSHans Rosenfeld 			    addrinfo->ia_ifa.ifa_addr->sa_family, IFF_UP, 0);
1980a73be61aSHans Rosenfeld 		}
1981a73be61aSHans Rosenfeld 	}
1982a73be61aSHans Rosenfeld 
1983a73be61aSHans Rosenfeld 	if (status == IPADM_SUCCESS) {
1984a73be61aSHans Rosenfeld 		if (groupname[0] == '\0') {
1985a73be61aSHans Rosenfeld 			/*
1986a73be61aSHans Rosenfeld 			 * If interface was removed from IPMP group, unset the
1987a73be61aSHans Rosenfeld 			 * DEPRECATED and NOFAILOVER flags.
1988a73be61aSHans Rosenfeld 			 */
1989a73be61aSHans Rosenfeld 			(void) i_ipadm_set_flags(iph, ifname, AF_INET, 0,
1990a73be61aSHans Rosenfeld 			    IFF_DEPRECATED | IFF_NOFAILOVER);
1991a73be61aSHans Rosenfeld 			(void) i_ipadm_set_flags(iph, ifname, AF_INET6, 0,
1992a73be61aSHans Rosenfeld 			    IFF_DEPRECATED | IFF_NOFAILOVER);
1993a73be61aSHans Rosenfeld 		} else if (addrinfo == NULL) {
1994a73be61aSHans Rosenfeld 			/*
1995a73be61aSHans Rosenfeld 			 * If interface was added to IPMP group and there are no
1996a73be61aSHans Rosenfeld 			 * active addresses, explicitly bring it up to be used
1997a73be61aSHans Rosenfeld 			 * for link-based IPMP configuration.
1998a73be61aSHans Rosenfeld 			 */
1999a73be61aSHans Rosenfeld 			(void) i_ipadm_set_flags(iph, ifname, AF_INET,
2000a73be61aSHans Rosenfeld 			    IFF_UP, 0);
2001a73be61aSHans Rosenfeld 			(void) i_ipadm_set_flags(iph, ifname, AF_INET6,
2002a73be61aSHans Rosenfeld 			    IFF_UP, 0);
2003a73be61aSHans Rosenfeld 		}
2004a73be61aSHans Rosenfeld 	}
2005a73be61aSHans Rosenfeld 
2006a73be61aSHans Rosenfeld 	ipadm_free_addr_info(addrinfo);
2007a73be61aSHans Rosenfeld 
2008a73be61aSHans Rosenfeld 	return (status);
2009a73be61aSHans Rosenfeld }
2010a73be61aSHans Rosenfeld 
2011a73be61aSHans Rosenfeld ipadm_status_t
i_ipadm_get_groupname_active(ipadm_handle_t iph,const char * ifname,char * groupname,size_t size)2012a73be61aSHans Rosenfeld i_ipadm_get_groupname_active(ipadm_handle_t iph, const char *ifname,
2013a73be61aSHans Rosenfeld     char *groupname, size_t size)
2014a73be61aSHans Rosenfeld {
2015a73be61aSHans Rosenfeld 	struct lifreq   lifr;
2016a73be61aSHans Rosenfeld 
2017a73be61aSHans Rosenfeld 	(void) memset(&lifr, 0, sizeof (lifr));
2018a73be61aSHans Rosenfeld 
2019a73be61aSHans Rosenfeld 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
2020a73be61aSHans Rosenfeld 
2021a73be61aSHans Rosenfeld 	if (ioctl(iph->iph_sock, SIOCGLIFGROUPNAME, (caddr_t)&lifr) == -1 &&
2022a73be61aSHans Rosenfeld 	    ioctl(iph->iph_sock6, SIOCGLIFGROUPNAME, (caddr_t)&lifr) == -1)
2023a73be61aSHans Rosenfeld 		return (ipadm_errno2status(errno));
2024a73be61aSHans Rosenfeld 
2025a73be61aSHans Rosenfeld 	(void) strlcpy(groupname, lifr.lifr_groupname, size);
2026a73be61aSHans Rosenfeld 
2027a73be61aSHans Rosenfeld 	return (IPADM_SUCCESS);
2028a73be61aSHans Rosenfeld }
2029a73be61aSHans Rosenfeld 
2030a73be61aSHans Rosenfeld /*
2031a73be61aSHans Rosenfeld  * Returns B_TRUE if `ifname' represents an IPMP underlying interface.
2032a73be61aSHans Rosenfeld  */
2033a73be61aSHans Rosenfeld boolean_t
i_ipadm_is_under_ipmp(ipadm_handle_t iph,const char * ifname)2034a73be61aSHans Rosenfeld i_ipadm_is_under_ipmp(ipadm_handle_t iph, const char *ifname)
2035a73be61aSHans Rosenfeld {
2036a73be61aSHans Rosenfeld 
2037a73be61aSHans Rosenfeld 	char	groupname[LIFGRNAMSIZ];
2038a73be61aSHans Rosenfeld 
2039a73be61aSHans Rosenfeld 	if (i_ipadm_get_groupname_active(iph, ifname, groupname,
2040a73be61aSHans Rosenfeld 	    sizeof (groupname)) != IPADM_SUCCESS ||
2041a73be61aSHans Rosenfeld 	    groupname[0] == '\0' ||
2042a73be61aSHans Rosenfeld 	    strcmp(ifname, groupname) == 0)
2043a73be61aSHans Rosenfeld 		return (B_FALSE);
2044a73be61aSHans Rosenfeld 
2045a73be61aSHans Rosenfeld 	return (B_TRUE);
2046a73be61aSHans Rosenfeld }
2047a73be61aSHans Rosenfeld 
2048a73be61aSHans Rosenfeld /*
2049a73be61aSHans Rosenfeld  * Returns B_TRUE if `ifname' represents an IPMP group interface.
2050a73be61aSHans Rosenfeld  */
2051a73be61aSHans Rosenfeld boolean_t
i_ipadm_is_ipmp(ipadm_handle_t iph,const char * ifname)2052a73be61aSHans Rosenfeld i_ipadm_is_ipmp(ipadm_handle_t iph, const char *ifname)
2053a73be61aSHans Rosenfeld {
2054a73be61aSHans Rosenfeld 	uint64_t flags;
2055a73be61aSHans Rosenfeld 
2056a73be61aSHans Rosenfeld 	if (i_ipadm_get_flags(iph, ifname, AF_INET, &flags) != IPADM_SUCCESS &&
2057a73be61aSHans Rosenfeld 	    i_ipadm_get_flags(iph, ifname, AF_INET6, &flags) != IPADM_SUCCESS)
2058a73be61aSHans Rosenfeld 		return (B_FALSE);
2059a73be61aSHans Rosenfeld 
2060a73be61aSHans Rosenfeld 	return ((flags & IFF_IPMP) != 0);
2061a73be61aSHans Rosenfeld }
2062