xref: /illumos-gate/usr/src/lib/libipadm/common/ipadm_if.c (revision 1b863af9bc9a5c76ed773e129889fc19dbf9e41c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
25  */
26 
27 #include <errno.h>
28 #include <sys/sockio.h>
29 #include <sys/list.h>
30 #include <string.h>
31 #include <assert.h>
32 #include <unistd.h>
33 #include <stropts.h>
34 #include <strings.h>
35 #include <libdlpi.h>
36 #include <libdllink.h>
37 #include <libinetutil.h>
38 #include <inet/ip.h>
39 #include <limits.h>
40 #include <zone.h>
41 #include <ipadm_ndpd.h>
42 #include <ipmp_query.h>
43 #include "libipadm_impl.h"
44 
45 static ipadm_status_t	i_ipadm_slifname_arp(char *, uint64_t, int);
46 static ipadm_status_t	i_ipadm_slifname(ipadm_handle_t, char *, char *,
47 			    uint64_t, int, uint32_t);
48 static ipadm_status_t	i_ipadm_create_ipmp_peer(ipadm_handle_t, char *,
49 			    sa_family_t);
50 static ipadm_status_t	i_ipadm_persist_if(ipadm_handle_t, const char *,
51 			    sa_family_t, uint32_t);
52 static ipadm_status_t   i_ipadm_allocate_ifinfo(ipadm_if_info_t **);
53 static ipadm_status_t	i_ipadm_get_db_if(ipadm_handle_t, const char *,
54 			    nvlist_t **);
55 static ipadm_status_t i_ipadm_nvl2ifinfo(nvlist_t *, ipadm_if_info_t **);
56 static ipadm_status_t i_ipadm_fill_cmembers(char *, ipadm_ipmp_members_t *);
57 static ipadm_status_t i_ipadm_fill_pmembers(nvlist_t *, ipadm_ipmp_members_t *);
58 static ipadm_status_t i_ipadm_add_persistent_if_info(ipadm_if_info_t *,
59 		    ipadm_if_info_t *);
60 static void i_ipadm_free_ipmp_members(ipadm_ipmp_members_t *);
61 static ipadm_status_t i_ipadm_persist_update_ipmp(ipadm_handle_t, const char *,
62 	const char *,
63 	ipadm_ipmp_op_t);
64 static ipadm_status_t i_ipadm_update_ipmp(ipadm_handle_t, const char *,
65 	const char *, uint32_t,
66 	ipadm_ipmp_op_t);
67 
68 /*
69  * Returns B_FALSE if the interface in `ifname' has at least one address that is
70  * IFF_UP in the addresses in `ifa'.
71  */
72 static boolean_t
73 i_ipadm_is_if_down(char *ifname, struct ifaddrs *ifa)
74 {
75 	struct ifaddrs	*ifap;
76 	char		cifname[LIFNAMSIZ];
77 	char		*sep;
78 
79 	for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
80 		(void) strlcpy(cifname, ifap->ifa_name, sizeof (cifname));
81 		if ((sep = strrchr(cifname, IPADM_LOGICAL_SEP)) != NULL)
82 			*sep = '\0';
83 		/*
84 		 * If this condition is true, there is at least one
85 		 * address that is IFF_UP. So, we need to return B_FALSE.
86 		 */
87 		if (strcmp(cifname, ifname) == 0 &&
88 		    (ifap->ifa_flags & IFF_UP)) {
89 			return (B_FALSE);
90 		}
91 	}
92 	/* We did not find any IFF_UP addresses. */
93 	return (B_TRUE);
94 }
95 
96 /*
97  * Retrieves the information for the interface `ifname' from active
98  * config if `ifname' is specified and returns the result in the list `if_info'.
99  * Otherwise, it retrieves the information for all the interfaces in
100  * the active config and returns the result in the list `if_info'.
101  */
102 static ipadm_status_t
103 i_ipadm_active_if_info(ipadm_handle_t iph, const char *ifname,
104     ipadm_if_info_t **if_info, int64_t lifc_flags)
105 {
106 	struct lifreq	*buf;
107 	struct lifreq	*lifrp;
108 	struct lifreq	lifrl;
109 	ipadm_if_info_t	*last = NULL;
110 	ipadm_if_info_t	*ifp;
111 	int		s;
112 	int		n;
113 	int		numifs;
114 	ipadm_status_t	status = IPADM_SUCCESS;
115 
116 	*if_info = NULL;
117 	/*
118 	 * Get information for all interfaces.
119 	 */
120 	if (getallifs(iph->iph_sock, 0, &buf, &numifs, lifc_flags) != 0)
121 		return (ipadm_errno2status(errno));
122 
123 	lifrp = buf;
124 	for (n = 0; n < numifs; n++, lifrp++) {
125 		/* Skip interfaces with logical num != 0 */
126 		if (i_ipadm_get_lnum(lifrp->lifr_name) != 0)
127 			continue;
128 		/*
129 		 * Skip the current interface if a specific `ifname' has
130 		 * been requested and current interface does not match
131 		 * `ifname'.
132 		 */
133 		if (ifname != NULL && strcmp(lifrp->lifr_name, ifname) != 0)
134 			continue;
135 		/*
136 		 * Check if the interface already exists in our list.
137 		 * If it already exists, we need to update its flags.
138 		 */
139 		for (ifp = *if_info; ifp != NULL; ifp = ifp->ifi_next) {
140 			if (strcmp(lifrp->lifr_name, ifp->ifi_name) == 0)
141 				break;
142 		}
143 		if (ifp == NULL) {
144 			if ((status =
145 			    i_ipadm_allocate_ifinfo(&ifp)) != IPADM_SUCCESS)
146 					break;
147 
148 			(void) strlcpy(ifp->ifi_name, lifrp->lifr_name,
149 			    sizeof (ifp->ifi_name));
150 			/* Update the `ifi_next' pointer for this new node */
151 			if (*if_info == NULL)
152 				*if_info = ifp;
153 			else
154 				last->ifi_next = ifp;
155 			last = ifp;
156 		}
157 
158 		/*
159 		 * Retrieve the flags for the interface by doing a
160 		 * SIOCGLIFFLAGS to populate the `ifi_cflags' field.
161 		 */
162 		(void) strlcpy(lifrl.lifr_name,
163 		    lifrp->lifr_name, sizeof (lifrl.lifr_name));
164 		s = (lifrp->lifr_addr.ss_family == AF_INET) ?
165 		    iph->iph_sock : iph->iph_sock6;
166 		if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
167 			continue;
168 
169 		/* a regular interface by default */
170 		ifp->ifi_class = IPADM_IF_CLASS_REGULAR;
171 
172 		if (lifrl.lifr_flags & IFF_BROADCAST)
173 			ifp->ifi_cflags |= IFIF_BROADCAST;
174 		if (lifrl.lifr_flags & IFF_MULTICAST)
175 			ifp->ifi_cflags |= IFIF_MULTICAST;
176 		if (lifrl.lifr_flags & IFF_POINTOPOINT)
177 			ifp->ifi_cflags |= IFIF_POINTOPOINT;
178 		if (lifrl.lifr_flags & IFF_VIRTUAL) {
179 			ifp->ifi_cflags |= IFIF_VIRTUAL;
180 			ifp->ifi_class = IPADM_IF_CLASS_VIRTUAL;
181 		}
182 		if (lifrl.lifr_flags & IFF_IPMP) {
183 			ifp->ifi_cflags |= IFIF_IPMP;
184 			ifp->ifi_class = IPADM_IF_CLASS_IPMP;
185 		}
186 		if (lifrl.lifr_flags & IFF_STANDBY)
187 			ifp->ifi_cflags |= IFIF_STANDBY;
188 		if (lifrl.lifr_flags & IFF_INACTIVE)
189 			ifp->ifi_cflags |= IFIF_INACTIVE;
190 		if (lifrl.lifr_flags & IFF_VRRP)
191 			ifp->ifi_cflags |= IFIF_VRRP;
192 		if (lifrl.lifr_flags & IFF_NOACCEPT)
193 			ifp->ifi_cflags |= IFIF_NOACCEPT;
194 		if (lifrl.lifr_flags & IFF_IPV4)
195 			ifp->ifi_cflags |= IFIF_IPV4;
196 		if (lifrl.lifr_flags & IFF_IPV6)
197 			ifp->ifi_cflags |= IFIF_IPV6;
198 		if (lifrl.lifr_flags & IFF_L3PROTECT)
199 			ifp->ifi_cflags |= IFIF_L3PROTECT;
200 
201 		/*
202 		 * Retrieve active IPMP members. This may fail in in.mpathd if
203 		 * the IPMP interface has just been created with no members.
204 		 * Hence, ignore errors, cmembers will just be empty.
205 		 */
206 		if (ifp->ifi_class == IPADM_IF_CLASS_IPMP) {
207 			if (ioctl(s, SIOCGLIFGROUPNAME, (caddr_t)&lifrl) == 0) {
208 				(void) i_ipadm_fill_cmembers(
209 				    lifrl.lifr_groupname,
210 				    &ifp->ifi_ipmp_cmembers);
211 			}
212 		}
213 	}
214 	free(buf);
215 	if (status != IPADM_SUCCESS) {
216 		ipadm_free_if_info(*if_info);
217 		*if_info = NULL;
218 	}
219 	return (status);
220 }
221 
222 /*
223  * Returns the interface information for `ifname' in `if_info' from persistent
224  * config if `ifname' is non-null. Otherwise, it returns all the interfaces
225  * from persistent config in `if_info'.
226  */
227 static ipadm_status_t
228 i_ipadm_persist_if_info(ipadm_handle_t iph, const char *ifname,
229     ipadm_if_info_t **if_info)
230 {
231 	ipadm_status_t	status = IPADM_SUCCESS;
232 	nvlist_t	*ifs_info_nvl;
233 
234 	*if_info = NULL;
235 
236 	if ((status = i_ipadm_get_db_if(iph,
237 	    ifname, &ifs_info_nvl)) != IPADM_SUCCESS)
238 		return (status);
239 
240 	assert(ifs_info_nvl != NULL);
241 
242 	return (i_ipadm_nvl2ifinfo(ifs_info_nvl, if_info));
243 }
244 
245 static ipadm_status_t
246 i_ipadm_nvl2ifinfo(nvlist_t *ifs_info_nvl, ipadm_if_info_t **if_info)
247 {
248 	ipadm_if_info_t *ific = NULL, *ifil = NULL;
249 	nvlist_t	*if_info_nvl;
250 	nvpair_t	*nvp;
251 	char		*strval;
252 	ipadm_status_t	status = IPADM_SUCCESS;
253 	uint16_t	*families;
254 	uint_t		nelem = 0;
255 
256 	for (nvp = nvlist_next_nvpair(ifs_info_nvl, NULL); nvp != NULL;
257 	    nvp = nvlist_next_nvpair(ifs_info_nvl, nvp)) {
258 		if (nvpair_value_nvlist(nvp, &if_info_nvl) != 0)
259 			continue;
260 
261 		status = i_ipadm_allocate_ifinfo(&ific);
262 		if (status != IPADM_SUCCESS) {
263 			ipadm_free_if_info(*if_info);
264 			break;
265 		}
266 		if (nvlist_lookup_string(if_info_nvl, IPADM_NVP_IFNAME,
267 		    &strval) != 0) {
268 			ipadm_free_if_info(ific);
269 			ific = NULL;
270 			continue;
271 		}
272 		(void) strlcpy(ific->ifi_name, strval,
273 		    sizeof (ific->ifi_name));
274 
275 		if (nvlist_lookup_uint16_array(if_info_nvl,
276 		    IPADM_NVP_FAMILIES, &families, &nelem) == 0) {
277 			while (nelem-- > 0) {
278 				if (families[nelem] == AF_INET)
279 					ific->ifi_pflags |= IFIF_IPV4;
280 				else if (families[nelem] == AF_INET6)
281 					ific->ifi_pflags |= IFIF_IPV6;
282 			}
283 		}
284 
285 		if (nvlist_lookup_string(if_info_nvl,
286 		    IPADM_NVP_IFCLASS, &strval) == 0)
287 			ific->ifi_class = atoi(strval);
288 		else
289 			ific->ifi_class = IPADM_IF_CLASS_REGULAR;
290 
291 		if (ific->ifi_class == IPADM_IF_CLASS_IPMP)
292 			/* do not expect any failures there */
293 			(void) i_ipadm_fill_pmembers(if_info_nvl,
294 			    &ific->ifi_ipmp_pmembers);
295 
296 		if (*if_info == NULL)
297 			*if_info = ific;
298 		else
299 			ifil->ifi_next = ific;
300 		ifil = ific;
301 	}
302 
303 	nvlist_free(ifs_info_nvl);
304 	return (status);
305 }
306 
307 /*
308  * Fill the ipadm_if_info_t->ifi_ipmp_pmembers by info from
309  * ipadm DB
310  */
311 static ipadm_status_t
312 i_ipadm_fill_pmembers(nvlist_t *if_info_nvl, ipadm_ipmp_members_t *pmembers)
313 {
314 	uint_t	nelem = 0;
315 	char	**members;
316 	ipadm_ipmp_member_t *ipmp_member;
317 
318 	if (nvlist_lookup_string_array(if_info_nvl, IPADM_NVP_MIFNAMES,
319 	    &members, &nelem) != 0)
320 		return (IPADM_SUCCESS);
321 
322 	while (nelem-- > 0) {
323 		if ((ipmp_member = calloc(1,
324 		    sizeof (ipadm_ipmp_member_t))) == NULL)
325 			return (ipadm_errno2status(errno));
326 
327 		(void) strlcpy(ipmp_member->if_name, members[nelem],
328 		    sizeof (ipmp_member->if_name));
329 		list_insert_tail(pmembers, ipmp_member);
330 	}
331 	return (IPADM_SUCCESS);
332 }
333 
334 /*
335  * Fill the ipadm_if_info_t->ifi_ipmp_cmembers by info from
336  * kernel (libipmp is used to retrieve the required info)
337  */
338 static ipadm_status_t
339 i_ipadm_fill_cmembers(char *grname, ipadm_ipmp_members_t *cmembers)
340 {
341 	ipmp_handle_t ipmp_handle;
342 	ipmp_groupinfo_t *grinfo;
343 	ipmp_iflist_t *iflistp;
344 	ipadm_ipmp_member_t *ipmp_member;
345 	ipadm_status_t ipadm_status = IPADM_SUCCESS;
346 	int i;
347 
348 	if (ipmp_open(&ipmp_handle) != IPMP_SUCCESS)
349 		return (IPADM_FAILURE);
350 
351 	if (ipmp_getgroupinfo(ipmp_handle, grname, &grinfo) != IPMP_SUCCESS) {
352 		ipadm_status = IPADM_FAILURE;
353 		goto fail2;
354 	}
355 
356 	iflistp = grinfo->gr_iflistp;
357 	for (i = 0; i < iflistp->il_nif; i++) {
358 		if ((ipmp_member = calloc(1,
359 		    sizeof (ipadm_ipmp_member_t))) == NULL) {
360 			ipadm_status = ipadm_errno2status(errno);
361 			goto fail1;
362 		}
363 		(void) strlcpy(ipmp_member->if_name, iflistp->il_ifs[i],
364 		    sizeof (ipmp_member->if_name));
365 		list_insert_tail(cmembers, ipmp_member);
366 	}
367 
368 fail1:
369 	ipmp_freegroupinfo(grinfo);
370 fail2:
371 	ipmp_close(ipmp_handle);
372 	return (ipadm_status);
373 }
374 
375 /*
376  * Collects information for `ifname' if one is specified from both
377  * active and persistent config in `if_info'. If no `ifname' is specified,
378  * this returns all the interfaces in active and persistent config in
379  * `if_info'.
380  */
381 ipadm_status_t
382 i_ipadm_get_all_if_info(ipadm_handle_t iph, const char *ifname,
383     ipadm_if_info_t **if_info, int64_t lifc_flags)
384 {
385 	ipadm_status_t	status;
386 	ipadm_if_info_t	*aifinfo = NULL;
387 	ipadm_if_info_t	*pifinfo = NULL;
388 	ipadm_if_info_t	*aifp;
389 	ipadm_if_info_t	*pifp;
390 	ipadm_if_info_t	*last = NULL;
391 	struct ifaddrs	*ifa;
392 	struct ifaddrs	*ifap;
393 
394 	/*
395 	 * Retrive the information for the requested `ifname' or all
396 	 * interfaces from active configuration.
397 	 */
398 retry:
399 	status = i_ipadm_active_if_info(iph, ifname, &aifinfo, lifc_flags);
400 	if (status != IPADM_SUCCESS)
401 		return (status);
402 	/* Get the interface state for each interface in `aifinfo'. */
403 	if (aifinfo != NULL) {
404 		/* We need all addresses to get the interface state */
405 		if (getallifaddrs(AF_UNSPEC, &ifa, (LIFC_NOXMIT|LIFC_TEMPORARY|
406 		    LIFC_ALLZONES|LIFC_UNDER_IPMP)) != 0) {
407 			status = ipadm_errno2status(errno);
408 			goto fail;
409 		}
410 		for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
411 			/*
412 			 * Find the `ifaddrs' structure from `ifa'
413 			 * for this interface. We need the IFF_* flags
414 			 * to find the interface state.
415 			 */
416 			for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
417 				if (strcmp(ifap->ifa_name, aifp->ifi_name) == 0)
418 					break;
419 			}
420 			if (ifap == NULL) {
421 				/*
422 				 * The interface might have been removed
423 				 * from kernel. Retry getting all the active
424 				 * interfaces.
425 				 */
426 				freeifaddrs(ifa);
427 				ipadm_free_if_info(aifinfo);
428 				aifinfo = NULL;
429 				goto retry;
430 			}
431 			if (!(ifap->ifa_flags & IFF_RUNNING) ||
432 			    (ifap->ifa_flags & IFF_FAILED))
433 				aifp->ifi_state = IFIS_FAILED;
434 			else if (ifap->ifa_flags & IFF_OFFLINE)
435 				aifp->ifi_state = IFIS_OFFLINE;
436 			else if (i_ipadm_is_if_down(aifp->ifi_name, ifa))
437 				aifp->ifi_state = IFIS_DOWN;
438 			else
439 				aifp->ifi_state = IFIS_OK;
440 			if (aifp->ifi_next == NULL)
441 				last = aifp;
442 		}
443 		freeifaddrs(ifa);
444 	}
445 	/*
446 	 * Get the persistent interface information in `pifinfo'.
447 	 */
448 	status = i_ipadm_persist_if_info(iph, ifname, &pifinfo);
449 	if (status == IPADM_NOTFOUND) {
450 		*if_info = aifinfo;
451 		return (IPADM_SUCCESS);
452 	}
453 	if (status != IPADM_SUCCESS)
454 		goto fail;
455 
456 	/*
457 	 * Process the persistent interface information.
458 	 *
459 	 * First try to get the persistent "standby" property, as that isn't
460 	 * retrieved by i_ipadm_persist_if_info().
461 	 *
462 	 * Next, if a persistent interface is also found in `aifinfo', update
463 	 * its entry in `aifinfo' with the persistent information from
464 	 * `pifinfo'. If an interface is found in `pifinfo', but not in
465 	 * `aifinfo', it means that this interface was disabled. We should
466 	 * add this interface to `aifinfo' and set it state to IFIF_DISABLED.
467 	 */
468 	for (pifp = pifinfo; pifp != NULL; pifp = pifp->ifi_next) {
469 		char buf[10] = "";
470 		uint_t bufsize = sizeof (buf);
471 
472 		status = ipadm_get_ifprop(iph, pifp->ifi_name, "standby", buf,
473 		    &bufsize, MOD_PROTO_IP, IPADM_OPT_PERSIST);
474 
475 		if (status == IPADM_SUCCESS && strcmp(buf, "on") == 0)
476 			pifp->ifi_pflags |= IFIF_STANDBY;
477 
478 		for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
479 			if (strcmp(aifp->ifi_name, pifp->ifi_name) == 0) {
480 				break;
481 			}
482 		}
483 
484 		if (aifp == NULL) {
485 			if ((status =
486 			    i_ipadm_allocate_ifinfo(&aifp)) != IPADM_SUCCESS)
487 				goto fail;
488 
489 			(void) strlcpy(aifp->ifi_name, pifp->ifi_name,
490 			    sizeof (aifp->ifi_name));
491 
492 			aifp->ifi_next = NULL;
493 			aifp->ifi_state = IFIS_DISABLED;
494 			if (last != NULL)
495 				last->ifi_next = aifp;
496 			else
497 				aifinfo = aifp;
498 			last = aifp;
499 		}
500 
501 		if ((status = i_ipadm_add_persistent_if_info(aifp,
502 		    pifp)) != IPADM_SUCCESS)
503 			goto fail;
504 	}
505 	*if_info = aifinfo;
506 	ipadm_free_if_info(pifinfo);
507 	return (IPADM_SUCCESS);
508 fail:
509 	*if_info = NULL;
510 	ipadm_free_if_info(aifinfo);
511 	ipadm_free_if_info(pifinfo);
512 	return (status);
513 }
514 
515 /*
516  * Updates active if_info by data from persistent if_info
517  */
518 static ipadm_status_t
519 i_ipadm_add_persistent_if_info(ipadm_if_info_t *aifp, ipadm_if_info_t *pifp)
520 {
521 	ipadm_ipmp_member_t *pp_ipmp_member, *ap_ipmp_member;
522 
523 	ipadm_ipmp_members_t *apmembers = &aifp->ifi_ipmp_pmembers;
524 	ipadm_ipmp_members_t *ppmembers = &pifp->ifi_ipmp_pmembers;
525 
526 	aifp->ifi_pflags = pifp->ifi_pflags;
527 	aifp->ifi_class = pifp->ifi_class;
528 
529 	for (pp_ipmp_member = list_head(ppmembers); pp_ipmp_member;
530 	    pp_ipmp_member = list_next(ppmembers, pp_ipmp_member)) {
531 		if ((ap_ipmp_member = calloc(1,
532 		    sizeof (ipadm_ipmp_member_t))) == NULL)
533 			return (ipadm_errno2status(errno));
534 
535 		(void) strlcpy(ap_ipmp_member->if_name,
536 		    pp_ipmp_member->if_name,
537 		    sizeof (ap_ipmp_member->if_name));
538 
539 		list_insert_tail(apmembers, ap_ipmp_member);
540 	}
541 	return (IPADM_SUCCESS);
542 }
543 
544 static ipadm_status_t
545 i_ipadm_allocate_ifinfo(ipadm_if_info_t **if_info)
546 {
547 	*if_info = calloc(1, sizeof (ipadm_if_info_t));
548 	if (*if_info == NULL)
549 		return (ipadm_errno2status(errno));
550 
551 	/* List of active (current) members */
552 	list_create(&((*if_info)->ifi_ipmp_cmembers),
553 	    sizeof (ipadm_ipmp_member_t),
554 	    offsetof(ipadm_ipmp_member_t, node));
555 
556 	/* List of persistent members */
557 	list_create(&((*if_info)->ifi_ipmp_pmembers),
558 	    sizeof (ipadm_ipmp_member_t),
559 	    offsetof(ipadm_ipmp_member_t, node));
560 
561 	return (IPADM_SUCCESS);
562 }
563 
564 /*
565  * Reads all the interface lines from the persistent DB into the nvlist `onvl',
566  * when `ifname' is NULL.
567  * If an `ifname' is specified, then the interface line corresponding to
568  * that name will be returned.
569  */
570 static ipadm_status_t
571 i_ipadm_get_db_if(ipadm_handle_t iph, const char *ifname, nvlist_t **onvl)
572 {
573 	ipmgmt_getif_arg_t	garg;
574 
575 	/* Populate the door_call argument structure */
576 	bzero(&garg, sizeof (garg));
577 	garg.ia_cmd = IPMGMT_CMD_GETIF;
578 	if (ifname != NULL)
579 		(void) strlcpy(garg.ia_ifname, ifname, sizeof (garg.ia_ifname));
580 
581 	return (i_ipadm_call_ipmgmtd(iph, (void *) &garg, sizeof (garg), onvl));
582 }
583 
584 int
585 i_ipadm_get_lnum(const char *ifname)
586 {
587 	char *num = strrchr(ifname, IPADM_LOGICAL_SEP);
588 
589 	if (num == NULL)
590 		return (0);
591 
592 	return (atoi(++num));
593 }
594 
595 /*
596  * Sets the output argument `exists' to true or false based on whether
597  * any persistent configuration is available for `ifname' and returns
598  * IPADM_SUCCESS as status. If the persistent information cannot be retrieved,
599  * `exists' is unmodified and an error status is returned.
600  */
601 ipadm_status_t
602 i_ipadm_if_pexists(ipadm_handle_t iph, const char *ifname, sa_family_t af,
603     boolean_t *exists)
604 {
605 	ipadm_if_info_t	*ifinfo;
606 	ipadm_status_t	status;
607 
608 	/*
609 	 * if IPH_IPMGMTD is set, we know that the caller (ipmgmtd) already
610 	 * knows about persistent configuration in the first place, so we
611 	 * just return success.
612 	 */
613 	if (iph->iph_flags & IPH_IPMGMTD) {
614 		*exists = B_FALSE;
615 		return (IPADM_SUCCESS);
616 	}
617 	status = i_ipadm_persist_if_info(iph, ifname, &ifinfo);
618 	if (status == IPADM_SUCCESS) {
619 		*exists = ((af == AF_INET &&
620 		    (ifinfo->ifi_pflags & IFIF_IPV4)) ||
621 		    (af == AF_INET6 &&
622 		    (ifinfo->ifi_pflags & IFIF_IPV6)));
623 		ipadm_free_if_info(ifinfo);
624 	} else if (status == IPADM_NOTFOUND) {
625 		status = IPADM_SUCCESS;
626 		*exists = B_FALSE;
627 	}
628 	return (status);
629 }
630 
631 /*
632  * Open "/dev/udp{,6}" for use as a multiplexor to PLINK the interface stream
633  * under. We use "/dev/udp" instead of "/dev/ip" since STREAMS will not let
634  * you PLINK a driver under itself, and "/dev/ip" is typically the driver at
635  * the bottom of the stream for tunneling interfaces.
636  */
637 ipadm_status_t
638 ipadm_open_arp_on_udp(const char *udp_dev_name, int *fd)
639 {
640 	int err;
641 
642 	if ((*fd = open(udp_dev_name, O_RDWR)) == -1)
643 		return (ipadm_errno2status(errno));
644 
645 	/*
646 	 * Pop off all undesired modules (note that the user may have
647 	 * configured autopush to add modules above udp), and push the
648 	 * arp module onto the resulting stream. This is used to make
649 	 * IP+ARP be able to atomically track the muxid for the I_PLINKed
650 	 * STREAMS, thus it isn't related to ARP running the ARP protocol.
651 	 */
652 	while (ioctl(*fd, I_POP, 0) != -1)
653 		;
654 	if (errno == EINVAL && ioctl(*fd, I_PUSH, ARP_MOD_NAME) != -1)
655 		return (IPADM_SUCCESS);
656 	err = errno;
657 	(void) close(*fd);
658 
659 	return (ipadm_errno2status(err));
660 }
661 
662 /*
663  * i_ipadm_create_ipmp() is called from i_ipadm_create_ipmp_peer() when an
664  * underlying interface in an ipmp group G is plumbed for an address family,
665  * but the meta-interface for the other address family `af' does not exist
666  * yet for the group G. If `af' is IPv6, we need to bring up the
667  * link-local address.
668  */
669 static ipadm_status_t
670 i_ipadm_create_ipmp(ipadm_handle_t iph, char *ifname, sa_family_t af,
671     const char *grname, uint32_t ipadm_flags)
672 {
673 	ipadm_status_t	status;
674 	struct lifreq	lifr;
675 	int		sock;
676 	int		err;
677 
678 	assert(ipadm_flags & IPADM_OPT_IPMP);
679 
680 	/* Create the ipmp underlying interface */
681 	status = i_ipadm_create_if(iph, ifname, af, ipadm_flags);
682 	if (status != IPADM_SUCCESS && status != IPADM_IF_EXISTS)
683 		return (status);
684 
685 	/*
686 	 * To preserve backward-compatibility, always bring up the link-local
687 	 * address for implicitly-created IPv6 IPMP interfaces.
688 	 */
689 	if (af == AF_INET6)
690 		(void) i_ipadm_set_flags(iph, ifname, AF_INET6, IFF_UP, 0);
691 
692 	sock = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
693 	/*
694 	 * If the caller requested a different group name, issue a
695 	 * SIOCSLIFGROUPNAME on the new IPMP interface.
696 	 */
697 	bzero(&lifr, sizeof (lifr));
698 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
699 	if (strcmp(lifr.lifr_name, grname) != 0) {
700 		(void) strlcpy(lifr.lifr_groupname, grname, LIFGRNAMSIZ);
701 		if (ioctl(sock, SIOCSLIFGROUPNAME, &lifr) == -1) {
702 			err = errno;
703 			/* Remove the interface we created. */
704 			if (status == IPADM_SUCCESS) {
705 				(void) i_ipadm_delete_if(iph, ifname, af,
706 				    ipadm_flags);
707 			}
708 			return (ipadm_errno2status(err));
709 		}
710 	}
711 
712 	return (IPADM_SUCCESS);
713 }
714 
715 /*
716  * Checks if `ifname' is plumbed and in an IPMP group on its "other" address
717  * family.  If so, create a matching IPMP group for address family `af'.
718  */
719 static ipadm_status_t
720 i_ipadm_create_ipmp_peer(ipadm_handle_t iph, char *ifname, sa_family_t af)
721 {
722 	lifgroupinfo_t	lifgr;
723 	ipadm_status_t	status = IPADM_SUCCESS;
724 	struct lifreq	lifr;
725 	int		other_af_sock;
726 
727 	assert(af == AF_INET || af == AF_INET6);
728 
729 	other_af_sock = (af == AF_INET ? iph->iph_sock6 : iph->iph_sock);
730 
731 	/*
732 	 * iph is the handle for the interface that we are trying to plumb.
733 	 * other_af_sock is the socket for the "other" address family.
734 	 */
735 	bzero(&lifr, sizeof (lifr));
736 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
737 	if (ioctl(other_af_sock, SIOCGLIFGROUPNAME, &lifr) != 0)
738 		return (IPADM_SUCCESS);
739 
740 	(void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname, LIFGRNAMSIZ);
741 	if (ioctl(other_af_sock, SIOCGLIFGROUPINFO, &lifgr) != 0)
742 		return (IPADM_SUCCESS);
743 
744 	/*
745 	 * If `ifname' *is* the IPMP group interface, or if the relevant
746 	 * address family is already configured, then there's nothing to do.
747 	 */
748 	if (strcmp(lifgr.gi_grifname, ifname) == 0 ||
749 	    (af == AF_INET && lifgr.gi_v4) || (af == AF_INET6 && lifgr.gi_v6)) {
750 		return (IPADM_SUCCESS);
751 	}
752 
753 	status = i_ipadm_create_ipmp(iph, lifgr.gi_grifname, af,
754 	    lifgr.gi_grname, IPADM_OPT_ACTIVE|IPADM_OPT_IPMP);
755 	return (status);
756 }
757 
758 /*
759  * Issues the ioctl SIOCSLIFNAME to kernel on the given ARP stream fd.
760  */
761 static ipadm_status_t
762 i_ipadm_slifname_arp(char *ifname, uint64_t flags, int fd)
763 {
764 	struct lifreq	lifr;
765 	ifspec_t	ifsp;
766 
767 	bzero(&lifr, sizeof (lifr));
768 	(void) ifparse_ifspec(ifname, &ifsp);
769 	lifr.lifr_ppa = ifsp.ifsp_ppa;
770 	lifr.lifr_flags = flags;
771 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
772 	/*
773 	 * Tell ARP the name and unit number for this interface.
774 	 * Note that arp has no support for transparent ioctls.
775 	 */
776 	if (i_ipadm_strioctl(fd, SIOCSLIFNAME, (char *)&lifr,
777 	    sizeof (lifr)) == -1) {
778 		return (ipadm_errno2status(errno));
779 	}
780 	return (IPADM_SUCCESS);
781 }
782 
783 /*
784  * Issues the ioctl SIOCSLIFNAME to kernel. If IPADM_OPT_GENPPA is set in
785  * `ipadm_flags', then a ppa will be generated. `newif' will be updated
786  * with the generated ppa.
787  */
788 static ipadm_status_t
789 i_ipadm_slifname(ipadm_handle_t iph, char *ifname, char *newif, uint64_t flags,
790     int fd, uint32_t ipadm_flags)
791 {
792 	struct lifreq	lifr;
793 	ipadm_status_t	status = IPADM_SUCCESS;
794 	int		err = 0;
795 	sa_family_t	af;
796 	int		ppa;
797 	ifspec_t	ifsp;
798 	boolean_t	valid_if;
799 
800 	bzero(&lifr, sizeof (lifr));
801 	if (ipadm_flags & IPADM_OPT_GENPPA) {
802 		/*
803 		 * We'd like to just set lifr_ppa to UINT_MAX and have the
804 		 * kernel pick a PPA.  Unfortunately, that would mishandle
805 		 * two cases:
806 		 *
807 		 *	1. If the PPA is available but the groupname is taken
808 		 *	   (e.g., the "ipmp2" IP interface name is available
809 		 *	   but the "ipmp2" groupname is taken) then the
810 		 *	   auto-assignment by the kernel will fail.
811 		 *
812 		 *	2. If we're creating (e.g.) an IPv6-only IPMP
813 		 *	   interface, and there's already an IPv4-only IPMP
814 		 *	   interface, the kernel will allow us to accidentally
815 		 *	   reuse the IPv6 IPMP interface name (since
816 		 *	   SIOCSLIFNAME uniqueness is per-interface-type).
817 		 *	   This will cause administrative confusion.
818 		 *
819 		 * Thus, we instead take a brute-force approach of checking
820 		 * whether the IPv4 or IPv6 name is already in-use before
821 		 * attempting the SIOCSLIFNAME.  As per (1) above, the
822 		 * SIOCSLIFNAME may still fail, in which case we just proceed
823 		 * to the next one.  If this approach becomes too slow, we
824 		 * can add a new SIOC* to handle this case in the kernel.
825 		 */
826 		for (ppa = 0; ppa < UINT_MAX; ppa++) {
827 			(void) snprintf(lifr.lifr_name, LIFNAMSIZ, "%s%d",
828 			    ifname, ppa);
829 
830 			if (ioctl(iph->iph_sock, SIOCGLIFFLAGS, &lifr) != -1 ||
831 			    errno != ENXIO)
832 				continue;
833 
834 			if (ioctl(iph->iph_sock6, SIOCGLIFFLAGS, &lifr) != -1 ||
835 			    errno != ENXIO)
836 				continue;
837 
838 			lifr.lifr_ppa = ppa;
839 			lifr.lifr_flags = flags;
840 
841 			err = ioctl(fd, SIOCSLIFNAME, &lifr);
842 			if (err != -1 || errno != EEXIST)
843 				break;
844 		}
845 		if (err == -1) {
846 			status = ipadm_errno2status(errno);
847 		} else {
848 			/*
849 			 * PPA has been successfully established.
850 			 * Update `newif' with the ppa.
851 			 */
852 			assert(newif != NULL);
853 			if (snprintf(newif, LIFNAMSIZ, "%s%d", ifname,
854 			    ppa) >= LIFNAMSIZ)
855 				return (IPADM_INVALID_ARG);
856 		}
857 	} else {
858 		/* We should have already validated the interface name. */
859 		valid_if = ifparse_ifspec(ifname, &ifsp);
860 		assert(valid_if);
861 
862 		/*
863 		 * Before we call SIOCSLIFNAME, ensure that the IPMP group
864 		 * interface for this address family exists.  Otherwise, the
865 		 * kernel will kick the interface out of the group when we do
866 		 * the SIOCSLIFNAME.
867 		 *
868 		 * Example: suppose bge0 is plumbed for IPv4 and in group "a".
869 		 * If we're now plumbing bge0 for IPv6, but the IPMP group
870 		 * interface for "a" is not plumbed for IPv6, the SIOCSLIFNAME
871 		 * will kick bge0 out of group "a", which is undesired.
872 		 */
873 		if (flags & IFF_IPV4)
874 			af = AF_INET;
875 		else
876 			af = AF_INET6;
877 		status = i_ipadm_create_ipmp_peer(iph, ifname, af);
878 		if (status != IPADM_SUCCESS)
879 			return (status);
880 		lifr.lifr_ppa = ifsp.ifsp_ppa;
881 		lifr.lifr_flags = flags;
882 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
883 		if (ioctl(fd, SIOCSLIFNAME, &lifr) == -1)
884 			status = ipadm_errno2status(errno);
885 	}
886 
887 	return (status);
888 }
889 
890 /*
891  * Plumbs the interface `ifname' for the address family `af'. It also persists
892  * the interface for `af' if IPADM_OPT_PERSIST is set in `ipadm_flags'.
893  */
894 ipadm_status_t
895 i_ipadm_plumb_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
896     uint32_t ipadm_flags)
897 {
898 	int		ip_muxid;
899 	int		mux_fd = -1, ip_fd, arp_fd;
900 	char		*udp_dev_name;
901 	dlpi_handle_t	dh_arp = NULL, dh_ip;
902 	uint64_t	ifflags;
903 	struct lifreq	lifr;
904 	uint_t		dlpi_flags;
905 	ipadm_status_t	status = IPADM_SUCCESS;
906 	char		*linkname;
907 	boolean_t	legacy = (iph->iph_flags & IPH_LEGACY);
908 	zoneid_t	zoneid;
909 	char		newif[LIFNAMSIZ];
910 	char		lifname[LIFNAMSIZ];
911 	datalink_id_t	linkid;
912 	int		sock;
913 	boolean_t	islo;
914 	boolean_t	is_persistent =
915 	    ((ipadm_flags & IPADM_OPT_PERSIST) != 0);
916 	uint32_t	dlflags;
917 	dladm_status_t	dlstatus;
918 
919 	if (iph->iph_dlh != NULL) {
920 		dlstatus = dladm_name2info(iph->iph_dlh, ifname, &linkid,
921 		    &dlflags, NULL, NULL);
922 	}
923 	/*
924 	 * If we're in the global zone and we're plumbing a datalink, make
925 	 * sure that the datalink is not assigned to a non-global zone.  Note
926 	 * that the non-global zones don't need this check, because zoneadm
927 	 * has taken care of this when the zones boot.
928 	 */
929 	if (iph->iph_zoneid == GLOBAL_ZONEID && dlstatus == DLADM_STATUS_OK) {
930 		zoneid = ALL_ZONES;
931 		if (zone_check_datalink(&zoneid, linkid) == 0) {
932 			/* interface is in use by a non-global zone. */
933 			return (IPADM_IF_INUSE);
934 		}
935 	}
936 
937 	/* loopback interfaces are just added as logical interface */
938 	bzero(&lifr, sizeof (lifr));
939 	islo = i_ipadm_is_loopback(ifname);
940 	if (islo || i_ipadm_get_lnum(ifname) != 0) {
941 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
942 		if (af == AF_INET)
943 			sock = iph->iph_sock;
944 		else
945 			sock = iph->iph_sock6;
946 		if (islo && ioctl(sock, SIOCGLIFADDR, (caddr_t)&lifr) >= 0)
947 			return (IPADM_IF_EXISTS);
948 		if (ioctl(sock, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
949 			return (ipadm_errno2status(errno));
950 
951 		/*
952 		 * By default, kernel configures 127.0.0.1 on the loopback
953 		 * interface. Replace this with 0.0.0.0 to be consistent
954 		 * with interface creation on other physical interfaces.
955 		 */
956 		if (islo && !legacy) {
957 			bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
958 			lifr.lifr_addr.ss_family = af;
959 			if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
960 				return (ipadm_errno2status(errno));
961 			if (is_persistent) {
962 				status = i_ipadm_persist_if(iph,
963 				    ifname, af, ipadm_flags);
964 				if (status != IPADM_SUCCESS) {
965 					(void) i_ipadm_delete_if(iph, ifname,
966 					    af, IPADM_OPT_ACTIVE);
967 				}
968 			}
969 		}
970 		return (status);
971 	}
972 
973 	dlpi_flags = DLPI_NOATTACH;
974 
975 	/*
976 	 * If IPADM_OPT_IPMP is specified, then this is a request
977 	 * to create an IPMP interface atop /dev/ipmpstub0.  (We can't simply
978 	 * pass "ipmpstub0" as devname since an admin *could* have a normal
979 	 * vanity-named link named "ipmpstub0" that they'd like to plumb.)
980 	 */
981 	if (ipadm_flags & IPADM_OPT_IPMP) {
982 		dlpi_flags |= DLPI_DEVONLY;
983 		linkname = "ipmpstub0";
984 	} else {
985 		/*
986 		 * Verify that the user is not creating a persistent
987 		 * IP interface on a non-persistent data-link.
988 		 */
989 		if (!i_ipadm_is_vni(ifname) && dlstatus == DLADM_STATUS_OK &&
990 		    is_persistent && !(dlflags & DLADM_OPT_PERSIST)) {
991 				return (IPADM_TEMPORARY_OBJ);
992 		}
993 		linkname = ifname;
994 	}
995 
996 	/*
997 	 * We use DLPI_NOATTACH because the ip module will do the attach
998 	 * itself for DLPI style-2 devices.
999 	 */
1000 	if (dlpi_open(linkname, &dh_ip, dlpi_flags) != DLPI_SUCCESS)
1001 		return (IPADM_DLPI_FAILURE);
1002 	ip_fd = dlpi_fd(dh_ip);
1003 	if (ioctl(ip_fd, I_PUSH, IP_MOD_NAME) == -1) {
1004 		status = ipadm_errno2status(errno);
1005 		goto done;
1006 	}
1007 
1008 	/*
1009 	 * Set IFF_IPV4/IFF_IPV6 flags. The kernel only allows modifications
1010 	 * to IFF_IPv4, IFF_IPV6, IFF_BROADCAST, IFF_XRESOLV, IFF_NOLINKLOCAL.
1011 	 */
1012 	ifflags = 0;
1013 
1014 	/* Set the name string and the IFF_IPV* flag */
1015 	if (af == AF_INET) {
1016 		ifflags = IFF_IPV4;
1017 	} else {
1018 		ifflags = IFF_IPV6;
1019 		/*
1020 		 * With the legacy method, the link-local address should be
1021 		 * configured as part of the interface plumb, using the default
1022 		 * token. If IPH_LEGACY is not specified, we want to set :: as
1023 		 * the address and require the admin to explicitly call
1024 		 * ipadm_create_addr() with the address object type set to
1025 		 * IPADM_ADDR_IPV6_ADDRCONF to create the link-local address
1026 		 * as well as the autoconfigured addresses.
1027 		 */
1028 		if (!legacy && !i_ipadm_is_6to4(iph, ifname))
1029 			ifflags |= IFF_NOLINKLOCAL;
1030 	}
1031 	(void) strlcpy(newif, ifname, sizeof (newif));
1032 	status = i_ipadm_slifname(iph, ifname, newif, ifflags, ip_fd,
1033 	    ipadm_flags);
1034 	if (status != IPADM_SUCCESS)
1035 		goto done;
1036 
1037 	/* Get the full set of existing flags for this stream */
1038 	status = i_ipadm_get_flags(iph, newif, af, &ifflags);
1039 	if (status != IPADM_SUCCESS)
1040 		goto done;
1041 
1042 	udp_dev_name = (af == AF_INET6 ? UDP6_DEV_NAME : UDP_DEV_NAME);
1043 	status = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd);
1044 	if (status != IPADM_SUCCESS)
1045 		goto done;
1046 
1047 	/* Check if arp is not needed */
1048 	if (ifflags & (IFF_NOARP|IFF_IPV6)) {
1049 		/*
1050 		 * PLINK the interface stream so that the application can exit
1051 		 * without tearing down the stream.
1052 		 */
1053 		if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1)
1054 			status = ipadm_errno2status(errno);
1055 		goto done;
1056 	}
1057 
1058 	/*
1059 	 * This interface does use ARP, so set up a separate stream
1060 	 * from the interface to ARP.
1061 	 *
1062 	 * We use DLPI_NOATTACH because the arp module will do the attach
1063 	 * itself for DLPI style-2 devices.
1064 	 */
1065 	if (dlpi_open(linkname, &dh_arp, dlpi_flags) != DLPI_SUCCESS) {
1066 		status = IPADM_DLPI_FAILURE;
1067 		goto done;
1068 	}
1069 
1070 	arp_fd = dlpi_fd(dh_arp);
1071 	if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1) {
1072 		status = ipadm_errno2status(errno);
1073 		goto done;
1074 	}
1075 
1076 	status = i_ipadm_slifname_arp(newif, ifflags, arp_fd);
1077 	if (status != IPADM_SUCCESS)
1078 		goto done;
1079 	/*
1080 	 * PLINK the IP and ARP streams so that ifconfig can exit
1081 	 * without tearing down the stream.
1082 	 */
1083 	if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) {
1084 		status = ipadm_errno2status(errno);
1085 		goto done;
1086 	}
1087 
1088 	if (ioctl(mux_fd, I_PLINK, arp_fd) < 0) {
1089 		status = ipadm_errno2status(errno);
1090 		(void) ioctl(mux_fd, I_PUNLINK, ip_muxid);
1091 	}
1092 
1093 done:
1094 	dlpi_close(dh_ip);
1095 	if (dh_arp != NULL)
1096 		dlpi_close(dh_arp);
1097 
1098 	if (mux_fd != -1)
1099 		(void) close(mux_fd);
1100 
1101 	if (status == IPADM_SUCCESS) {
1102 		/* copy back new ifname */
1103 		(void) strlcpy(ifname, newif, LIFNAMSIZ);
1104 		/*
1105 		 * If it is a 6to4 tunnel, create a default
1106 		 * addrobj name for the default address on the 0'th
1107 		 * logical interface and set IFF_UP in the interface flags.
1108 		 */
1109 		if (i_ipadm_is_6to4(iph, ifname)) {
1110 			struct ipadm_addrobj_s addr;
1111 
1112 			i_ipadm_init_addr(&addr, ifname, "", IPADM_ADDR_STATIC);
1113 			addr.ipadm_af = af;
1114 			status = i_ipadm_lookupadd_addrobj(iph, &addr);
1115 			if (status != IPADM_SUCCESS)
1116 				return (status);
1117 			status = ipadm_add_aobjname(iph, ifname,
1118 			    af, addr.ipadm_aobjname, IPADM_ADDR_STATIC, 0);
1119 			if (status != IPADM_SUCCESS)
1120 				return (status);
1121 			addr.ipadm_lifnum = 0;
1122 			i_ipadm_addrobj2lifname(&addr, lifname,
1123 			    sizeof (lifname));
1124 			status = i_ipadm_set_flags(iph, lifname, af,
1125 			    IFF_UP, 0);
1126 			if (status != IPADM_SUCCESS)
1127 				return (status);
1128 		} else {
1129 			/*
1130 			 * Prevent static IPv6 addresses from triggering
1131 			 * autoconf. This does not have to be done for
1132 			 * 6to4 tunnel interfaces, since in.ndpd will
1133 			 * not autoconfigure those interfaces.
1134 			 */
1135 			if (af == AF_INET6 && !legacy)
1136 				(void) i_ipadm_disable_autoconf(newif);
1137 		}
1138 
1139 		/*
1140 		 * If IPADM_OPT_PERSIST was set in flags, store the
1141 		 * interface in persistent DB.
1142 		 */
1143 		if (is_persistent) {
1144 			status = i_ipadm_persist_if(iph,
1145 			    newif, af, ipadm_flags);
1146 			if (status != IPADM_SUCCESS) {
1147 				(void) i_ipadm_delete_if(iph, newif, af,
1148 				    IPADM_OPT_ACTIVE);
1149 			}
1150 		}
1151 	}
1152 	if (status == IPADM_EXISTS)
1153 		status = IPADM_IF_EXISTS;
1154 	return (status);
1155 }
1156 
1157 /*
1158  * Unplumbs the interface in `ifname' of family `af'.
1159  */
1160 ipadm_status_t
1161 i_ipadm_unplumb_if(ipadm_handle_t iph, const char *ifname, sa_family_t af)
1162 {
1163 	int		ip_muxid, arp_muxid;
1164 	int		mux_fd = -1;
1165 	int		muxid_fd = -1;
1166 	char		*udp_dev_name;
1167 	uint64_t	flags;
1168 	boolean_t	changed_arp_muxid = B_FALSE;
1169 	int		save_errno;
1170 	struct lifreq	lifr;
1171 	ipadm_status_t	ret = IPADM_SUCCESS;
1172 	int		sock;
1173 	lifgroupinfo_t	lifgr;
1174 	ifaddrlistx_t	*ifaddrs, *ifaddrp;
1175 	boolean_t	v6 = (af == AF_INET6);
1176 
1177 	/* Just do SIOCLIFREMOVEIF on loopback interfaces */
1178 	bzero(&lifr, sizeof (lifr));
1179 	if (i_ipadm_is_loopback(ifname) ||
1180 	    (i_ipadm_get_lnum(ifname) != 0 && (iph->iph_flags & IPH_LEGACY))) {
1181 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
1182 		if (ioctl((af == AF_INET) ? iph->iph_sock : iph->iph_sock6,
1183 		    SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) {
1184 			return (ipadm_errno2status(errno));
1185 		}
1186 		return (IPADM_SUCCESS);
1187 	}
1188 
1189 	/*
1190 	 * We used /dev/udp or udp6 to set up the mux. So we have to use
1191 	 * the same now for PUNLINK also.
1192 	 */
1193 	if (v6) {
1194 		udp_dev_name = UDP6_DEV_NAME;
1195 		sock = iph->iph_sock6;
1196 	} else {
1197 		udp_dev_name = UDP_DEV_NAME;
1198 		sock = iph->iph_sock;
1199 	}
1200 	if ((muxid_fd = open(udp_dev_name, O_RDWR)) == -1) {
1201 		ret = ipadm_errno2status(errno);
1202 		goto done;
1203 	}
1204 	ret = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd);
1205 	if (ret != IPADM_SUCCESS)
1206 		goto done;
1207 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
1208 	if (ioctl(muxid_fd, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
1209 		ret = ipadm_errno2status(errno);
1210 		goto done;
1211 	}
1212 	flags = lifr.lifr_flags;
1213 again:
1214 	if (flags & IFF_IPMP) {
1215 		/*
1216 		 * There are two reasons the I_PUNLINK can fail with EBUSY:
1217 		 * (1) if IP interfaces are in the group, or (2) if IPMP data
1218 		 * addresses are administratively up.  For case (1), we fail
1219 		 * here with a specific error message.  For case (2), we bring
1220 		 * down the addresses prior to doing the I_PUNLINK.  If the
1221 		 * I_PUNLINK still fails with EBUSY then the configuration
1222 		 * must have changed after our checks, in which case we branch
1223 		 * back up to `again' and rerun this logic.  The net effect is
1224 		 * that unplumbing an IPMP interface will only fail with EBUSY
1225 		 * if IP interfaces are in the group.
1226 		 */
1227 		if (ioctl(sock, SIOCGLIFGROUPNAME, &lifr) == -1) {
1228 			ret = ipadm_errno2status(errno);
1229 			goto done;
1230 		}
1231 		(void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname,
1232 		    LIFGRNAMSIZ);
1233 		if (ioctl(sock, SIOCGLIFGROUPINFO, &lifgr) == -1) {
1234 			ret = ipadm_errno2status(errno);
1235 			goto done;
1236 		}
1237 		if ((v6 && lifgr.gi_nv6 != 0) || (!v6 && lifgr.gi_nv4 != 0)) {
1238 			ret = IPADM_GRP_NOTEMPTY;
1239 			goto done;
1240 		}
1241 
1242 		/*
1243 		 * The kernel will fail the I_PUNLINK if the IPMP interface
1244 		 * has administratively up addresses; bring them down.
1245 		 */
1246 		if (ifaddrlistx(ifname, IFF_UP|IFF_DUPLICATE,
1247 		    0, &ifaddrs) == -1) {
1248 			ret = ipadm_errno2status(errno);
1249 			goto done;
1250 		}
1251 		ifaddrp = ifaddrs;
1252 		for (; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
1253 			int sock = (ifaddrp->ia_flags & IFF_IPV4) ?
1254 			    iph->iph_sock : iph->iph_sock6;
1255 			struct lifreq lifrl;
1256 
1257 			if (((ifaddrp->ia_flags & IFF_IPV6) && !v6) ||
1258 			    (!(ifaddrp->ia_flags & IFF_IPV6) && v6))
1259 				continue;
1260 
1261 			bzero(&lifrl, sizeof (lifrl));
1262 			(void) strlcpy(lifrl.lifr_name, ifaddrp->ia_name,
1263 			    sizeof (lifrl.lifr_name));
1264 			if (ioctl(sock, SIOCGLIFFLAGS, &lifrl) < 0) {
1265 				ret = ipadm_errno2status(errno);
1266 				ifaddrlistx_free(ifaddrs);
1267 				goto done;
1268 			}
1269 			if (lifrl.lifr_flags & IFF_UP) {
1270 				ret = i_ipadm_set_flags(iph, lifrl.lifr_name,
1271 				    ((lifrl.lifr_flags & IFF_IPV4) ? AF_INET :
1272 				    AF_INET6), 0, IFF_UP);
1273 				if (ret != IPADM_SUCCESS) {
1274 					ifaddrlistx_free(ifaddrs);
1275 					goto done;
1276 				}
1277 			} else if (lifrl.lifr_flags & IFF_DUPLICATE) {
1278 				if (ioctl(sock, SIOCGLIFADDR, &lifrl) < 0 ||
1279 				    ioctl(sock, SIOCSLIFADDR, &lifrl) < 0) {
1280 					ret = ipadm_errno2status(errno);
1281 					ifaddrlistx_free(ifaddrs);
1282 					goto done;
1283 				}
1284 			}
1285 		}
1286 		ifaddrlistx_free(ifaddrs);
1287 	}
1288 
1289 	if (ioctl(muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
1290 		ret = ipadm_errno2status(errno);
1291 		goto done;
1292 	}
1293 	arp_muxid = lifr.lifr_arp_muxid;
1294 	ip_muxid = lifr.lifr_ip_muxid;
1295 
1296 	/*
1297 	 * We don't have a good way of knowing whether the arp stream is
1298 	 * plumbed. We can't rely on IFF_NOARP because someone could
1299 	 * have turned it off later using "ifconfig xxx -arp".
1300 	 */
1301 	if (arp_muxid != 0) {
1302 		if (ioctl(mux_fd, I_PUNLINK, arp_muxid) < 0) {
1303 			/*
1304 			 * See the comment before the SIOCGLIFGROUPNAME call.
1305 			 */
1306 			if (errno == EBUSY && (flags & IFF_IPMP))
1307 				goto again;
1308 
1309 			if ((errno == EINVAL) &&
1310 			    (flags & (IFF_NOARP | IFF_IPV6))) {
1311 				/*
1312 				 * Some plumbing utilities set the muxid to
1313 				 * -1 or some invalid value to signify that
1314 				 * there is no arp stream. Set the muxid to 0
1315 				 * before trying to unplumb the IP stream.
1316 				 * IP does not allow the IP stream to be
1317 				 * unplumbed if it sees a non-null arp muxid,
1318 				 * for consistency of IP-ARP streams.
1319 				 */
1320 				lifr.lifr_arp_muxid = 0;
1321 				(void) ioctl(muxid_fd, SIOCSLIFMUXID,
1322 				    (caddr_t)&lifr);
1323 				changed_arp_muxid = B_TRUE;
1324 			}
1325 			/*
1326 			 * In case of any other error, we continue with
1327 			 * the unplumb.
1328 			 */
1329 		}
1330 	}
1331 
1332 	if (ioctl(mux_fd, I_PUNLINK, ip_muxid) < 0) {
1333 		if (changed_arp_muxid) {
1334 			/*
1335 			 * Some error occurred, and we need to restore
1336 			 * everything back to what it was.
1337 			 */
1338 			save_errno = errno;
1339 			lifr.lifr_arp_muxid = arp_muxid;
1340 			lifr.lifr_ip_muxid = ip_muxid;
1341 			(void) ioctl(muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
1342 			errno = save_errno;
1343 		}
1344 		/*
1345 		 * See the comment before the SIOCGLIFGROUPNAME call.
1346 		 */
1347 		if (errno == EBUSY && (flags & IFF_IPMP))
1348 			goto again;
1349 
1350 		ret = ipadm_errno2status(errno);
1351 	}
1352 done:
1353 	if (muxid_fd != -1)
1354 		(void) close(muxid_fd);
1355 	if (mux_fd != -1)
1356 		(void) close(mux_fd);
1357 
1358 	if (af == AF_INET6 && ret == IPADM_SUCCESS) {
1359 		/*
1360 		 * in.ndpd maintains the phyints in its memory even after
1361 		 * the interface is plumbed, so that it can be reused when
1362 		 * the interface gets plumbed again. The default behavior
1363 		 * of in.ndpd is to start autoconfiguration for an interface
1364 		 * that gets plumbed. We need to send the
1365 		 * message IPADM_ENABLE_AUTOCONF to in.ndpd to restore this
1366 		 * default behavior on replumb.
1367 		 */
1368 		(void) i_ipadm_enable_autoconf(ifname);
1369 	}
1370 	return (ret);
1371 }
1372 
1373 /*
1374  * Saves the given interface name `ifname' with address family `af' in
1375  * persistent DB.
1376  */
1377 static ipadm_status_t
1378 i_ipadm_persist_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1379     uint32_t ipadm_flags)
1380 {
1381 	ipmgmt_if_arg_t		ifarg;
1382 	int			err;
1383 
1384 	(void) strlcpy(ifarg.ia_ifname, ifname, sizeof (ifarg.ia_ifname));
1385 	ifarg.ia_family = af;
1386 	if (ipadm_flags & IPADM_OPT_IPMP)
1387 		ifarg.ia_ifclass = IPADM_IF_CLASS_IPMP;
1388 	else
1389 		ifarg.ia_ifclass = IPADM_IF_CLASS_REGULAR;
1390 
1391 	ifarg.ia_cmd = IPMGMT_CMD_SETIF;
1392 	ifarg.ia_flags = IPMGMT_PERSIST;
1393 	err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
1394 	return (ipadm_errno2status(err));
1395 }
1396 
1397 /*
1398  * Remove the IP interface from active configuration. If IPADM_OPT_PERSIST
1399  * is set in `ipadm_flags', it is also removed from persistent configuration.
1400  */
1401 ipadm_status_t
1402 i_ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1403     uint32_t ipadm_flags)
1404 {
1405 	ipadm_status_t		ret = IPADM_SUCCESS;
1406 	ipadm_status_t		db_status;
1407 	char			tmp_ifname[LIFNAMSIZ];
1408 	char			*cp;
1409 	struct ipadm_addrobj_s	ipaddr;
1410 	boolean_t		is_persistent =
1411 	    (ipadm_flags & IPADM_OPT_PERSIST);
1412 
1413 	ret = i_ipadm_unplumb_if(iph, ifname, af);
1414 	if (ret != IPADM_SUCCESS)
1415 		goto done;
1416 
1417 	cp = strrchr(ifname, IPADM_LOGICAL_SEP);
1418 	if (cp != NULL) {
1419 		assert(iph->iph_flags & IPH_LEGACY);
1420 		/*
1421 		 * This is a non-zero logical interface.
1422 		 * Find the addrobj and remove it from the daemon's memory.
1423 		 */
1424 		(void) strlcpy(tmp_ifname, ifname, sizeof (tmp_ifname));
1425 		tmp_ifname[cp - ifname] = '\0';
1426 		*cp++ = '\0';
1427 		ipaddr.ipadm_lifnum = atoi(cp);
1428 		(void) strlcpy(ipaddr.ipadm_ifname, tmp_ifname,
1429 		    sizeof (ipaddr.ipadm_ifname));
1430 		ipaddr.ipadm_af = af;
1431 		ret = i_ipadm_get_lif2addrobj(iph, &ipaddr);
1432 		if (ret == IPADM_SUCCESS) {
1433 			ret = i_ipadm_delete_addrobj(iph, &ipaddr,
1434 			    IPADM_OPT_ACTIVE);
1435 		} else if (ret == IPADM_NOTFOUND) {
1436 			ret = IPADM_SUCCESS;
1437 		}
1438 		return (ret);
1439 	}
1440 done:
1441 	/*
1442 	 * Even if interface does not exist, remove all its addresses and
1443 	 * properties from the persistent store. If interface does not
1444 	 * exist both in kernel and the persistent store, return IPADM_ENXIO.
1445 	 */
1446 	if ((ret == IPADM_ENXIO && is_persistent) || ret == IPADM_SUCCESS) {
1447 		db_status = i_ipadm_delete_ifobj(iph, ifname, af,
1448 		    is_persistent);
1449 		if (db_status == IPADM_SUCCESS)
1450 			ret = IPADM_SUCCESS;
1451 	}
1452 
1453 	return (ret);
1454 }
1455 
1456 /*
1457  * Resets all addresses on interface `ifname' with address family `af'
1458  * from ipmgmtd daemon. If is_persistent = B_TRUE, all interface properties
1459  * and address objects of `ifname' for `af' are also removed from the
1460  * persistent DB.
1461  */
1462 ipadm_status_t
1463 i_ipadm_delete_ifobj(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1464     boolean_t is_persistent)
1465 {
1466 	ipmgmt_if_arg_t		ifarg;
1467 	int			err;
1468 
1469 	ifarg.ia_cmd = IPMGMT_CMD_RESETIF;
1470 	ifarg.ia_flags = IPMGMT_ACTIVE;
1471 	if (is_persistent)
1472 		ifarg.ia_flags |= IPMGMT_PERSIST;
1473 	ifarg.ia_family = af;
1474 	(void) strlcpy(ifarg.ia_ifname, ifname, LIFNAMSIZ);
1475 
1476 	err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
1477 	return (ipadm_errno2status(err));
1478 }
1479 
1480 /*
1481  * Create the interface by plumbing it for IP.
1482  * This function will check if there is saved configuration information
1483  * for `ifname' and return IPADM_OP_DISABLE_OBJ if the name-space
1484  * for `ifname' is taken.
1485  */
1486 ipadm_status_t
1487 i_ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
1488     uint32_t ipadm_flags)
1489 {
1490 	ipadm_status_t	status;
1491 	boolean_t	p_exists;
1492 	sa_family_t	other_af;
1493 
1494 	/*
1495 	 * Return error, if the interface already exists in either the active
1496 	 * or the persistent configuration.
1497 	 */
1498 	if (ipadm_if_enabled(iph, ifname, af))
1499 		return (IPADM_IF_EXISTS);
1500 
1501 	if (!(iph->iph_flags & IPH_LEGACY)) {
1502 		status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
1503 		if (status != IPADM_SUCCESS)
1504 			return (status);
1505 		other_af = (af == AF_INET ? AF_INET6 : AF_INET);
1506 		if (p_exists) {
1507 			if (!ipadm_if_enabled(iph, ifname, other_af))
1508 				return (IPADM_OP_DISABLE_OBJ);
1509 			else
1510 				ipadm_flags &= ~IPADM_OPT_PERSIST;
1511 		}
1512 	}
1513 
1514 	return (i_ipadm_plumb_if(iph, ifname, af, ipadm_flags));
1515 }
1516 
1517 /*
1518  * Plumbs an interface. Creates both IPv4 and IPv6 interfaces by
1519  * default, unless a value in `af' is specified. The interface may be plumbed
1520  * only if there is no previously saved persistent configuration information
1521  * for the interface (in which case the ipadm_enable_if() function must
1522  * be used to enable the interface).
1523  *
1524  * Returns: IPADM_SUCCESS, IPADM_FAILURE, IPADM_IF_EXISTS,
1525  * IPADM_IF_PERSIST_EXISTS, IPADM_DLPI_FAILURE,
1526  * or appropriate ipadm_status_t corresponding to the errno.
1527  *
1528  * `ifname' must point to memory that can hold upto LIFNAMSIZ chars. It may
1529  * be over-written with the actual interface name when a PPA has to be
1530  * internally generated by the library.
1531  */
1532 ipadm_status_t
1533 ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
1534     uint32_t flags)
1535 {
1536 	ipadm_status_t	status;
1537 	boolean_t	created_v4 = B_FALSE;
1538 	char		newifname[LIFNAMSIZ];
1539 
1540 	/* Check for the required authorization */
1541 	if (!ipadm_check_auth())
1542 		return (IPADM_EAUTH);
1543 
1544 	if (flags == 0 || ((flags & IPADM_OPT_PERSIST) &&
1545 	    !(flags & IPADM_OPT_ACTIVE)) ||
1546 	    (flags & ~(IPADM_COMMON_OPT_MASK | IPADM_OPT_IPMP |
1547 	    IPADM_OPT_GENPPA))) {
1548 		return (IPADM_INVALID_ARG);
1549 	}
1550 	if (flags & IPADM_OPT_GENPPA) {
1551 		if (snprintf(newifname, LIFNAMSIZ, "%s0", ifname) >=
1552 		    LIFNAMSIZ)
1553 			return (IPADM_INVALID_ARG);
1554 	} else {
1555 		if (strlcpy(newifname, ifname, LIFNAMSIZ) >= LIFNAMSIZ)
1556 			return (IPADM_INVALID_ARG);
1557 	}
1558 
1559 	if (!i_ipadm_validate_ifname(iph, newifname))
1560 		return (IPADM_INVALID_ARG);
1561 
1562 	if ((af == AF_INET || af == AF_UNSPEC) &&
1563 	    !i_ipadm_is_6to4(iph, ifname)) {
1564 		status = i_ipadm_create_if(iph, ifname, AF_INET, flags);
1565 		if (status != IPADM_SUCCESS)
1566 			return (status);
1567 		created_v4 = B_TRUE;
1568 	}
1569 	if (af == AF_INET6 || af == AF_UNSPEC) {
1570 		status = i_ipadm_create_if(iph, ifname, AF_INET6, flags);
1571 		if (status != IPADM_SUCCESS) {
1572 			if (created_v4) {
1573 				(void) i_ipadm_delete_if(iph, ifname, AF_INET,
1574 				    IPADM_OPT_ACTIVE);
1575 			}
1576 			return (status);
1577 		}
1578 	}
1579 
1580 	return (IPADM_SUCCESS);
1581 }
1582 
1583 ipadm_status_t
1584 ipadm_add_ipmp_member(ipadm_handle_t iph, const char *gifname,
1585     const char *mifname, uint32_t ipadm_flags)
1586 {
1587 	return (i_ipadm_update_ipmp(iph, gifname, mifname,
1588 	    ipadm_flags, IPADM_ADD_IPMP));
1589 }
1590 
1591 ipadm_status_t
1592 ipadm_remove_ipmp_member(ipadm_handle_t iph, const char *gifname,
1593     const char *mifname, uint32_t ipadm_flags)
1594 {
1595 	return (i_ipadm_update_ipmp(iph, gifname, mifname,
1596 	    ipadm_flags, IPADM_REMOVE_IPMP));
1597 }
1598 
1599 /*
1600  * Updates active IPMP configuration according to the specified
1601  * command. It also persists the configuration if IPADM_OPT_PERSIST
1602  * is set in `ipadm_flags'.
1603  */
1604 static ipadm_status_t
1605 i_ipadm_update_ipmp(ipadm_handle_t iph, const char *gifname,
1606     const char *mifname, uint32_t ipadm_flags, ipadm_ipmp_op_t op)
1607 {
1608 	ipadm_status_t status;
1609 	char	groupname1[LIFGRNAMSIZ];
1610 	char	groupname2[LIFGRNAMSIZ];
1611 
1612 	/* Check for the required authorization */
1613 	if (!ipadm_check_auth())
1614 		return (IPADM_EAUTH);
1615 
1616 	if (!(ipadm_flags & IPADM_OPT_ACTIVE) ||
1617 	    gifname == NULL || mifname == NULL)
1618 		return (IPADM_INVALID_ARG);
1619 
1620 	if (!ipadm_if_enabled(iph, gifname, AF_UNSPEC) ||
1621 	    !ipadm_if_enabled(iph, mifname, AF_UNSPEC))
1622 		return (IPADM_OP_DISABLE_OBJ);
1623 
1624 	if (!i_ipadm_is_ipmp(iph, gifname))
1625 		return (IPADM_INVALID_ARG);
1626 
1627 	if (op == IPADM_ADD_IPMP && i_ipadm_is_under_ipmp(iph, mifname))
1628 		return (IPADM_IF_INUSE);
1629 
1630 	if ((status = i_ipadm_get_groupname_active(iph, gifname,
1631 	    groupname2, sizeof (groupname2))) != IPADM_SUCCESS)
1632 		return (status);
1633 
1634 	if (op == IPADM_REMOVE_IPMP) {
1635 		if ((status = i_ipadm_get_groupname_active(iph, mifname,
1636 		    groupname1, sizeof (groupname1))) != IPADM_SUCCESS)
1637 			return (status);
1638 
1639 		if (groupname1[0] == '\0' ||
1640 		    strcmp(groupname1, groupname2) != 0)
1641 			return (IPADM_INVALID_ARG);
1642 
1643 		groupname2[0] = '\0';
1644 	}
1645 
1646 	if ((ipadm_flags & IPADM_OPT_PERSIST) &&
1647 	    (status = i_ipadm_persist_update_ipmp(iph, gifname,
1648 	    mifname, op)) != IPADM_SUCCESS)
1649 		return (status);
1650 
1651 	return (i_ipadm_set_groupname_active(iph, mifname, groupname2));
1652 }
1653 
1654 /*
1655  * Call the ipmgmtd to update the IPMP configuration in ipadm DB.
1656  * After this call the DB will know that mifname is under gifname and
1657  * gifname has a member, which name is mifname.
1658  */
1659 static ipadm_status_t
1660 i_ipadm_persist_update_ipmp(ipadm_handle_t iph, const char *gifname,
1661     const char *mifname, ipadm_ipmp_op_t op)
1662 {
1663 	ipmgmt_ipmp_update_arg_t args;
1664 	int err;
1665 
1666 	assert(op == IPADM_ADD_IPMP || op == IPADM_REMOVE_IPMP);
1667 
1668 	bzero(&args, sizeof (ipmgmt_ipmp_update_arg_t));
1669 
1670 	args.ia_cmd = IPMGMT_CMD_IPMP_UPDATE;
1671 
1672 	(void) strlcpy(args.ia_gifname, gifname, sizeof (args.ia_gifname));
1673 	(void) strlcpy(args.ia_mifname, mifname, sizeof (args.ia_mifname));
1674 
1675 	if (op == IPADM_ADD_IPMP)
1676 		args.ia_flags = IPMGMT_APPEND;
1677 	else
1678 		args.ia_flags = IPMGMT_REMOVE;
1679 
1680 	args.ia_flags |= IPMGMT_PERSIST;
1681 
1682 	err = ipadm_door_call(iph, &args, sizeof (args), NULL, 0, B_FALSE);
1683 	return (ipadm_errno2status(err));
1684 }
1685 
1686 /*
1687  * Deletes the interface in `ifname'. Removes both IPv4 and IPv6 interfaces
1688  * when `af' = AF_UNSPEC.
1689  */
1690 ipadm_status_t
1691 ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1692     uint32_t flags)
1693 {
1694 	ipadm_status_t status1 = IPADM_SUCCESS;
1695 	ipadm_status_t status2 = IPADM_SUCCESS;
1696 	ipadm_status_t other;
1697 
1698 	/* Check for the required authorization */
1699 	if (!ipadm_check_auth())
1700 		return (IPADM_EAUTH);
1701 
1702 	/* Validate the `ifname' for any logical interface. */
1703 	if (flags == 0 || (flags & ~(IPADM_COMMON_OPT_MASK)) ||
1704 	    !i_ipadm_validate_ifname(iph, ifname))
1705 		return (IPADM_INVALID_ARG);
1706 
1707 	if (af == AF_INET || af == AF_UNSPEC)
1708 		status1 = i_ipadm_delete_if(iph, ifname, AF_INET, flags);
1709 	if (af == AF_INET6 || af == AF_UNSPEC)
1710 		status2 = i_ipadm_delete_if(iph, ifname, AF_INET6, flags);
1711 	/*
1712 	 * If the family has been uniquely identified, we return the
1713 	 * associated status, even if that is ENXIO. Calls from ifconfig
1714 	 * which can only unplumb one of IPv4/IPv6 at any time fall under
1715 	 * this category.
1716 	 */
1717 	if (af == AF_INET)
1718 		return (status1);
1719 	else if (af == AF_INET6)
1720 		return (status2);
1721 	else if (af != AF_UNSPEC)
1722 		return (IPADM_INVALID_ARG);
1723 
1724 	/*
1725 	 * If af is AF_UNSPEC, then we return the following:
1726 	 * status1,		if status1 == status2
1727 	 * IPADM_SUCCESS,	if either of status1 or status2 is SUCCESS
1728 	 *			and the other status is ENXIO
1729 	 * IPADM_ENXIO,		if both status1 and status2 are ENXIO
1730 	 * IPADM_FAILURE	otherwise.
1731 	 */
1732 	if (status1 == status2) {
1733 		/* covers the case when both status1 and status2 are ENXIO */
1734 		return (status1);
1735 	} else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
1736 		if (status1 == IPADM_SUCCESS)
1737 			other = status2;
1738 		else
1739 			other = status1;
1740 		return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
1741 	} else {
1742 		return (IPADM_FAILURE);
1743 	}
1744 }
1745 
1746 /*
1747  * Returns information about all interfaces in both active and persistent
1748  * configuration. If `ifname' is not NULL, it returns only the interface
1749  * identified by `ifname'.
1750  *
1751  * Return values:
1752  *	On success: IPADM_SUCCESS.
1753  *	On error  : IPADM_INVALID_ARG, IPADM_ENXIO or IPADM_FAILURE.
1754  */
1755 ipadm_status_t
1756 ipadm_if_info(ipadm_handle_t iph, const char *ifname,
1757     ipadm_if_info_t **if_info, uint32_t flags, int64_t lifc_flags)
1758 {
1759 	ipadm_status_t	status;
1760 	ifspec_t	ifsp;
1761 
1762 	if (if_info == NULL || iph == NULL || flags != 0)
1763 		return (IPADM_INVALID_ARG);
1764 
1765 	if (ifname != NULL &&
1766 	    (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)) {
1767 		return (IPADM_INVALID_ARG);
1768 	}
1769 
1770 	status = i_ipadm_get_all_if_info(iph, ifname, if_info, lifc_flags);
1771 	if (status != IPADM_SUCCESS)
1772 		return (status);
1773 	if (ifname != NULL && *if_info == NULL)
1774 		return (IPADM_ENXIO);
1775 
1776 	return (IPADM_SUCCESS);
1777 }
1778 
1779 /*
1780  * Frees the linked list allocated by ipadm_if_info().
1781  */
1782 void
1783 ipadm_free_if_info(ipadm_if_info_t *ifinfo)
1784 {
1785 	ipadm_if_info_t	*ifinfo_next;
1786 
1787 	for (; ifinfo != NULL; ifinfo = ifinfo_next) {
1788 		ifinfo_next = ifinfo->ifi_next;
1789 		i_ipadm_free_ipmp_members(&ifinfo->ifi_ipmp_cmembers);
1790 		i_ipadm_free_ipmp_members(&ifinfo->ifi_ipmp_pmembers);
1791 		free(ifinfo);
1792 	}
1793 }
1794 
1795 static void
1796 i_ipadm_free_ipmp_members(ipadm_ipmp_members_t *ipmp_members)
1797 {
1798 	ipadm_ipmp_member_t *ipmp_member;
1799 
1800 	while ((ipmp_member = list_remove_head(ipmp_members)) != NULL)
1801 		free(ipmp_member);
1802 
1803 	list_destroy(ipmp_members);
1804 }
1805 
1806 /*
1807  * Re-enable the interface `ifname' based on the saved configuration
1808  * for `ifname'.
1809  */
1810 ipadm_status_t
1811 ipadm_enable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
1812 {
1813 	boolean_t	set_init = B_FALSE;
1814 	nvlist_t	*ifnvl;
1815 	ipadm_status_t	status;
1816 	ifspec_t	ifsp;
1817 
1818 	/* Check for the required authorization */
1819 	if (!ipadm_check_auth())
1820 		return (IPADM_EAUTH);
1821 
1822 	/* Check for logical interfaces. */
1823 	if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
1824 		return (IPADM_INVALID_ARG);
1825 
1826 	/* Enabling an interface persistently is not supported. */
1827 	if (flags & IPADM_OPT_PERSIST)
1828 		return (IPADM_NOTSUP);
1829 
1830 	/*
1831 	 * Return early by checking if the interface is already enabled.
1832 	 */
1833 	if (ipadm_if_enabled(iph, ifname, AF_INET) &&
1834 	    ipadm_if_enabled(iph, ifname, AF_INET6))
1835 		return (IPADM_IF_EXISTS);
1836 
1837 	/*
1838 	 * Enable the interface and restore all its interface properties
1839 	 * and address objects.
1840 	 */
1841 	status = i_ipadm_init_ifs(iph, ifname, &ifnvl);
1842 	if (status != IPADM_SUCCESS)
1843 		return (status);
1844 
1845 	assert(ifnvl != NULL);
1846 	/*
1847 	 * ipadm_enable_if() does exactly what ipadm_init_ifs() does,
1848 	 * but only for one interface. We need to set IPH_INIT because
1849 	 * ipmgmtd daemon does not have to write the interface to the
1850 	 * persistent db. The interface is already available in the
1851 	 * persistent db and we are here to re-enable the persistent
1852 	 * configuration.
1853 	 *
1854 	 * But we need to make sure we're not accidentally clearing an
1855 	 * IPH_INIT flag that was already set when we were called.
1856 	 */
1857 	if ((iph->iph_flags & IPH_INIT) == 0) {
1858 		iph->iph_flags |= IPH_INIT;
1859 		set_init = B_TRUE;
1860 	}
1861 
1862 	status = i_ipadm_init_ifobj(iph, ifname, ifnvl);
1863 
1864 	if (set_init)
1865 		iph->iph_flags &= ~IPH_INIT;
1866 
1867 	nvlist_free(ifnvl);
1868 	return (status);
1869 }
1870 
1871 /*
1872  * Disable the interface `ifname' by removing it from the active configuration.
1873  * Error code return values follow the model in ipadm_delete_if()
1874  */
1875 ipadm_status_t
1876 ipadm_disable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
1877 {
1878 	ipadm_status_t	status1, status2, other;
1879 	ifspec_t	ifsp;
1880 
1881 	/* Check for the required authorization */
1882 	if (!ipadm_check_auth())
1883 		return (IPADM_EAUTH);
1884 
1885 	/* Check for logical interfaces. */
1886 	if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
1887 		return (IPADM_INVALID_ARG);
1888 
1889 	/* Disabling an interface persistently is not supported. */
1890 	if (flags & IPADM_OPT_PERSIST)
1891 		return (IPADM_NOTSUP);
1892 
1893 	status1 = i_ipadm_unplumb_if(iph, ifname, AF_INET6);
1894 	if (status1 == IPADM_SUCCESS)
1895 		status1 = i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
1896 	status2 = i_ipadm_unplumb_if(iph, ifname, AF_INET);
1897 	if (status2 == IPADM_SUCCESS)
1898 		status2 = i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
1899 	if (status1 == status2) {
1900 		return (status2);
1901 	} else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
1902 		if (status1 == IPADM_SUCCESS)
1903 			other = status2;
1904 		else
1905 			other = status1;
1906 		return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
1907 	} else {
1908 		return (IPADM_FAILURE);
1909 	}
1910 }
1911 
1912 /*
1913  * FIXME Remove this when ifconfig(1M) is updated to use IPMP support
1914  * in libipadm.
1915  */
1916 /*
1917  * This workaround is required by ifconfig(1M) whenever an
1918  * interface is moved into an IPMP group to update the daemon's
1919  * in-memory mapping of `aobjname' to 'lifnum'.
1920  *
1921  * For `IPMGMT_ACTIVE' case, i_ipadm_delete_ifobj() would only fail if
1922  * door_call(3C) fails. Also, there is no use in returning error because
1923  * `ifname' would have been successfuly moved into IPMP group, by this time.
1924  */
1925 void
1926 ipadm_if_move(ipadm_handle_t iph, const char *ifname)
1927 {
1928 	(void) i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
1929 	(void) i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
1930 }
1931 
1932 ipadm_status_t
1933 i_ipadm_set_groupname_active(ipadm_handle_t iph, const char *ifname,
1934     const char *groupname)
1935 {
1936 	struct lifreq   lifr;
1937 	ipadm_addr_info_t *addrinfo, *ia;
1938 	ipadm_status_t	status = IPADM_SUCCESS;
1939 
1940 	(void) memset(&lifr, 0, sizeof (lifr));
1941 
1942 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
1943 	(void) strlcpy(lifr.lifr_groupname, groupname,
1944 	    sizeof (lifr.lifr_groupname));
1945 
1946 	/* Disable all addresses on the interface */
1947 	(void) i_ipadm_active_addr_info(iph, ifname, &addrinfo,
1948 	    IPADM_OPT_ACTIVE | IPADM_OPT_ZEROADDR, IFF_UP | IFF_DUPLICATE);
1949 
1950 	for (ia = addrinfo; ia != NULL; ia = IA_NEXT(ia)) {
1951 		if (strlen(ia->ia_aobjname) > 0) {
1952 			(void) ipadm_disable_addr(iph, ia->ia_aobjname, 0);
1953 		} else {
1954 			/*
1955 			 * There's an address on this interfaces with no
1956 			 * corresponding addrobj. Just clear IFF_UP.
1957 			 */
1958 			(void) i_ipadm_set_flags(iph, ifname,
1959 			    addrinfo->ia_ifa.ifa_addr->sa_family, 0, IFF_UP);
1960 		}
1961 	}
1962 
1963 	if (ioctl(iph->iph_sock, SIOCSLIFGROUPNAME, (caddr_t)&lifr) == -1 &&
1964 	    ioctl(iph->iph_sock6, SIOCSLIFGROUPNAME, (caddr_t)&lifr) == -1)
1965 		status = ipadm_errno2status(errno);
1966 
1967 	/* Enable all addresses on the interface */
1968 	for (ia = addrinfo; ia != NULL; ia = IA_NEXT(ia)) {
1969 		if (strlen(ia->ia_aobjname) > 0) {
1970 			(void) ipadm_enable_addr(iph, ia->ia_aobjname, 0);
1971 		} else {
1972 			/*
1973 			 * There's an address on this interfaces with no
1974 			 * corresponding addrobj. Just set IFF_UP.
1975 			 */
1976 			(void) i_ipadm_set_flags(iph, ifname,
1977 			    addrinfo->ia_ifa.ifa_addr->sa_family, IFF_UP, 0);
1978 		}
1979 	}
1980 
1981 	if (status == IPADM_SUCCESS) {
1982 		if (groupname[0] == '\0') {
1983 			/*
1984 			 * If interface was removed from IPMP group, unset the
1985 			 * DEPRECATED and NOFAILOVER flags.
1986 			 */
1987 			(void) i_ipadm_set_flags(iph, ifname, AF_INET, 0,
1988 			    IFF_DEPRECATED | IFF_NOFAILOVER);
1989 			(void) i_ipadm_set_flags(iph, ifname, AF_INET6, 0,
1990 			    IFF_DEPRECATED | IFF_NOFAILOVER);
1991 		} else if (addrinfo == NULL) {
1992 			/*
1993 			 * If interface was added to IPMP group and there are no
1994 			 * active addresses, explicitly bring it up to be used
1995 			 * for link-based IPMP configuration.
1996 			 */
1997 			(void) i_ipadm_set_flags(iph, ifname, AF_INET,
1998 			    IFF_UP, 0);
1999 			(void) i_ipadm_set_flags(iph, ifname, AF_INET6,
2000 			    IFF_UP, 0);
2001 		}
2002 	}
2003 
2004 	ipadm_free_addr_info(addrinfo);
2005 
2006 	return (status);
2007 }
2008 
2009 ipadm_status_t
2010 i_ipadm_get_groupname_active(ipadm_handle_t iph, const char *ifname,
2011     char *groupname, size_t size)
2012 {
2013 	struct lifreq   lifr;
2014 
2015 	(void) memset(&lifr, 0, sizeof (lifr));
2016 
2017 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
2018 
2019 	if (ioctl(iph->iph_sock, SIOCGLIFGROUPNAME, (caddr_t)&lifr) == -1 &&
2020 	    ioctl(iph->iph_sock6, SIOCGLIFGROUPNAME, (caddr_t)&lifr) == -1)
2021 		return (ipadm_errno2status(errno));
2022 
2023 	(void) strlcpy(groupname, lifr.lifr_groupname, size);
2024 
2025 	return (IPADM_SUCCESS);
2026 }
2027 
2028 /*
2029  * Returns B_TRUE if `ifname' represents an IPMP underlying interface.
2030  */
2031 boolean_t
2032 i_ipadm_is_under_ipmp(ipadm_handle_t iph, const char *ifname)
2033 {
2034 
2035 	char	groupname[LIFGRNAMSIZ];
2036 
2037 	if (i_ipadm_get_groupname_active(iph, ifname, groupname,
2038 	    sizeof (groupname)) != IPADM_SUCCESS ||
2039 	    groupname[0] == '\0' ||
2040 	    strcmp(ifname, groupname) == 0)
2041 		return (B_FALSE);
2042 
2043 	return (B_TRUE);
2044 }
2045 
2046 /*
2047  * Returns B_TRUE if `ifname' represents an IPMP group interface.
2048  */
2049 boolean_t
2050 i_ipadm_is_ipmp(ipadm_handle_t iph, const char *ifname)
2051 {
2052 	uint64_t flags;
2053 
2054 	if (i_ipadm_get_flags(iph, ifname, AF_INET, &flags) != IPADM_SUCCESS &&
2055 	    i_ipadm_get_flags(iph, ifname, AF_INET6, &flags) != IPADM_SUCCESS)
2056 		return (B_FALSE);
2057 
2058 	return ((flags & IFF_IPMP) != 0);
2059 }
2060