xref: /titanic_51/usr/src/lib/libipadm/common/ipadm_if.c (revision cfb7311ca709ca6f4d930977c7498455556b5312)
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  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <errno.h>
26 #include <sys/sockio.h>
27 #include <string.h>
28 #include <assert.h>
29 #include <unistd.h>
30 #include <stropts.h>
31 #include <strings.h>
32 #include <libdlpi.h>
33 #include <libdllink.h>
34 #include <libinetutil.h>
35 #include <inet/ip.h>
36 #include <limits.h>
37 #include <zone.h>
38 #include <ipadm_ndpd.h>
39 #include "libipadm_impl.h"
40 
41 static ipadm_status_t	i_ipadm_slifname_arp(char *, uint64_t, int);
42 static ipadm_status_t	i_ipadm_slifname(ipadm_handle_t, char *, char *,
43 			    uint64_t, int, uint32_t);
44 static ipadm_status_t	i_ipadm_create_ipmp_peer(ipadm_handle_t, char *,
45 			    sa_family_t);
46 static ipadm_status_t	i_ipadm_persist_if(ipadm_handle_t, const char *,
47 			    sa_family_t);
48 
49 /*
50  * Returns B_FALSE if the interface in `ifname' has at least one address that is
51  * IFF_UP in the addresses in `ifa'.
52  */
53 static boolean_t
54 i_ipadm_is_if_down(char *ifname, struct ifaddrs *ifa)
55 {
56 	struct ifaddrs	*ifap;
57 	char		cifname[LIFNAMSIZ];
58 	char		*sep;
59 
60 	for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
61 		(void) strlcpy(cifname, ifap->ifa_name, sizeof (cifname));
62 		if ((sep = strrchr(cifname, IPADM_LOGICAL_SEP)) != NULL)
63 			*sep = '\0';
64 		/*
65 		 * If this condition is true, there is at least one
66 		 * address that is IFF_UP. So, we need to return B_FALSE.
67 		 */
68 		if (strcmp(cifname, ifname) == 0 &&
69 		    (ifap->ifa_flags & IFF_UP)) {
70 			return (B_FALSE);
71 		}
72 	}
73 	/* We did not find any IFF_UP addresses. */
74 	return (B_TRUE);
75 }
76 
77 /*
78  * Retrieves the information for the interface `ifname' from active
79  * config if `ifname' is specified and returns the result in the list `if_info'.
80  * Otherwise, it retrieves the information for all the interfaces in
81  * the active config and returns the result in the list `if_info'.
82  */
83 static ipadm_status_t
84 i_ipadm_active_if_info(ipadm_handle_t iph, const char *ifname,
85     ipadm_if_info_t **if_info, int64_t lifc_flags)
86 {
87 	struct lifreq	*buf;
88 	struct lifreq	*lifrp;
89 	struct lifreq	lifrl;
90 	ipadm_if_info_t	*last = NULL;
91 	ipadm_if_info_t	*ifp;
92 	int		s;
93 	int		n;
94 	int		numifs;
95 	ipadm_status_t	status;
96 
97 	*if_info = NULL;
98 	/*
99 	 * Get information for all interfaces.
100 	 */
101 	if (getallifs(iph->iph_sock, 0, &buf, &numifs, lifc_flags) != 0)
102 		return (ipadm_errno2status(errno));
103 
104 	lifrp = buf;
105 	for (n = 0; n < numifs; n++, lifrp++) {
106 		/* Skip interfaces with logical num != 0 */
107 		if (i_ipadm_get_lnum(lifrp->lifr_name) != 0)
108 			continue;
109 		/*
110 		 * Skip the current interface if a specific `ifname' has
111 		 * been requested and current interface does not match
112 		 * `ifname'.
113 		 */
114 		if (ifname != NULL && strcmp(lifrp->lifr_name, ifname) != 0)
115 			continue;
116 		/*
117 		 * Check if the interface already exists in our list.
118 		 * If it already exists, we need to update its flags.
119 		 */
120 		for (ifp = *if_info; ifp != NULL; ifp = ifp->ifi_next) {
121 			if (strcmp(lifrp->lifr_name, ifp->ifi_name) == 0)
122 				break;
123 		}
124 		if (ifp == NULL) {
125 			ifp = calloc(1, sizeof (ipadm_if_info_t));
126 			if (ifp == NULL) {
127 				status = ipadm_errno2status(errno);
128 				goto fail;
129 			}
130 			(void) strlcpy(ifp->ifi_name, lifrp->lifr_name,
131 			    sizeof (ifp->ifi_name));
132 			/* Update the `ifi_next' pointer for this new node */
133 			if (*if_info == NULL)
134 				*if_info = ifp;
135 			else
136 				last->ifi_next = ifp;
137 			last = ifp;
138 		}
139 
140 		/*
141 		 * Retrieve the flags for the interface by doing a
142 		 * SIOCGLIFFLAGS to populate the `ifi_cflags' field.
143 		 */
144 		(void) strlcpy(lifrl.lifr_name,
145 		    lifrp->lifr_name, sizeof (lifrl.lifr_name));
146 		s = (lifrp->lifr_addr.ss_family == AF_INET) ?
147 		    iph->iph_sock : iph->iph_sock6;
148 		if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
149 			continue;
150 		if (lifrl.lifr_flags & IFF_BROADCAST)
151 			ifp->ifi_cflags |= IFIF_BROADCAST;
152 		if (lifrl.lifr_flags & IFF_MULTICAST)
153 			ifp->ifi_cflags |= IFIF_MULTICAST;
154 		if (lifrl.lifr_flags & IFF_POINTOPOINT)
155 			ifp->ifi_cflags |= IFIF_POINTOPOINT;
156 		if (lifrl.lifr_flags & IFF_VIRTUAL)
157 			ifp->ifi_cflags |= IFIF_VIRTUAL;
158 		if (lifrl.lifr_flags & IFF_IPMP)
159 			ifp->ifi_cflags |= IFIF_IPMP;
160 		if (lifrl.lifr_flags & IFF_STANDBY)
161 			ifp->ifi_cflags |= IFIF_STANDBY;
162 		if (lifrl.lifr_flags & IFF_INACTIVE)
163 			ifp->ifi_cflags |= IFIF_INACTIVE;
164 		if (lifrl.lifr_flags & IFF_VRRP)
165 			ifp->ifi_cflags |= IFIF_VRRP;
166 		if (lifrl.lifr_flags & IFF_NOACCEPT)
167 			ifp->ifi_cflags |= IFIF_NOACCEPT;
168 		if (lifrl.lifr_flags & IFF_IPV4)
169 			ifp->ifi_cflags |= IFIF_IPV4;
170 		if (lifrl.lifr_flags & IFF_IPV6)
171 			ifp->ifi_cflags |= IFIF_IPV6;
172 	}
173 	free(buf);
174 	return (IPADM_SUCCESS);
175 fail:
176 	free(buf);
177 	ipadm_free_if_info(*if_info);
178 	*if_info = NULL;
179 	return (status);
180 }
181 
182 /*
183  * Returns the interface information for `ifname' in `if_info' from persistent
184  * config if `ifname' is non-null. Otherwise, it returns all the interfaces
185  * from persistent config in `if_info'.
186  */
187 static ipadm_status_t
188 i_ipadm_persist_if_info(ipadm_handle_t iph, const char *ifname,
189     ipadm_if_info_t **if_info)
190 {
191 	ipadm_status_t		status = IPADM_SUCCESS;
192 	ipmgmt_getif_arg_t	getif;
193 	ipmgmt_getif_rval_t	*rvalp;
194 	ipadm_if_info_t		*ifp, *curr, *prev = NULL;
195 	int			i = 0, err = 0;
196 
197 	bzero(&getif, sizeof (getif));
198 	if (ifname != NULL)
199 		(void) strlcpy(getif.ia_ifname, ifname, LIFNAMSIZ);
200 	getif.ia_cmd = IPMGMT_CMD_GETIF;
201 
202 	*if_info = NULL;
203 
204 	if ((rvalp = malloc(sizeof (ipmgmt_getif_rval_t))) == NULL)
205 		return (ipadm_errno2status(errno));
206 	err = ipadm_door_call(iph, &getif, sizeof (getif), (void **)&rvalp,
207 	    sizeof (*rvalp), B_TRUE);
208 	if (err == ENOENT) {
209 		free(rvalp);
210 		if (ifname != NULL)
211 			return (ipadm_errno2status(err));
212 		return (IPADM_SUCCESS);
213 	} else if (err != 0) {
214 		free(rvalp);
215 		return (ipadm_errno2status(err));
216 	}
217 
218 	ifp = rvalp->ir_ifinfo;
219 	for (i = 0; i < rvalp->ir_ifcnt; i++) {
220 		ifp = rvalp->ir_ifinfo + i;
221 		if ((curr = malloc(sizeof (*curr))) == NULL) {
222 			status = ipadm_errno2status(errno);
223 			ipadm_free_if_info(prev);
224 			break;
225 		}
226 		(void) bcopy(ifp, curr, sizeof (*curr));
227 		curr->ifi_next = prev;
228 		prev = curr;
229 	}
230 	*if_info = curr;
231 	free(rvalp);
232 	return (status);
233 }
234 
235 /*
236  * Collects information for `ifname' if one is specified from both
237  * active and persistent config in `if_info'. If no `ifname' is specified,
238  * this returns all the interfaces in active and persistent config in
239  * `if_info'.
240  */
241 ipadm_status_t
242 i_ipadm_get_all_if_info(ipadm_handle_t iph, const char *ifname,
243     ipadm_if_info_t **if_info, int64_t lifc_flags)
244 {
245 	ipadm_status_t	status;
246 	ipadm_if_info_t	*aifinfo = NULL;
247 	ipadm_if_info_t	*pifinfo = NULL;
248 	ipadm_if_info_t	*aifp;
249 	ipadm_if_info_t	*pifp;
250 	ipadm_if_info_t	*last = NULL;
251 	struct ifaddrs	*ifa;
252 	struct ifaddrs	*ifap;
253 
254 	/*
255 	 * Retrive the information for the requested `ifname' or all
256 	 * interfaces from active configuration.
257 	 */
258 retry:
259 	status = i_ipadm_active_if_info(iph, ifname, &aifinfo, lifc_flags);
260 	if (status != IPADM_SUCCESS)
261 		return (status);
262 	/* Get the interface state for each interface in `aifinfo'. */
263 	if (aifinfo != NULL) {
264 		/* We need all addresses to get the interface state */
265 		if (getallifaddrs(AF_UNSPEC, &ifa, (LIFC_NOXMIT|LIFC_TEMPORARY|
266 		    LIFC_ALLZONES|LIFC_UNDER_IPMP)) != 0) {
267 			status = ipadm_errno2status(errno);
268 			goto fail;
269 		}
270 		for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
271 			/*
272 			 * Find the `ifaddrs' structure from `ifa'
273 			 * for this interface. We need the IFF_* flags
274 			 * to find the interface state.
275 			 */
276 			for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
277 				if (strcmp(ifap->ifa_name, aifp->ifi_name) == 0)
278 					break;
279 			}
280 			if (ifap == NULL) {
281 				/*
282 				 * The interface might have been removed
283 				 * from kernel. Retry getting all the active
284 				 * interfaces.
285 				 */
286 				freeifaddrs(ifa);
287 				ipadm_free_if_info(aifinfo);
288 				aifinfo = NULL;
289 				goto retry;
290 			}
291 			if (!(ifap->ifa_flags & IFF_RUNNING) ||
292 			    (ifap->ifa_flags & IFF_FAILED))
293 				aifp->ifi_state = IFIS_FAILED;
294 			else if (ifap->ifa_flags & IFF_OFFLINE)
295 				aifp->ifi_state = IFIS_OFFLINE;
296 			else if (i_ipadm_is_if_down(aifp->ifi_name, ifa))
297 				aifp->ifi_state = IFIS_DOWN;
298 			else
299 				aifp->ifi_state = IFIS_OK;
300 			if (aifp->ifi_next == NULL)
301 				last = aifp;
302 		}
303 		freeifaddrs(ifa);
304 	}
305 	/*
306 	 * Get the persistent interface information in `pifinfo'.
307 	 */
308 	status = i_ipadm_persist_if_info(iph, ifname, &pifinfo);
309 	if (status == IPADM_NOTFOUND) {
310 		*if_info = aifinfo;
311 		return (IPADM_SUCCESS);
312 	}
313 	if (status != IPADM_SUCCESS)
314 		goto fail;
315 	/*
316 	 * If a persistent interface is also found in `aifinfo', update
317 	 * its entry in `aifinfo' with the persistent information from
318 	 * `pifinfo'. If an interface is found in `pifinfo', but not in
319 	 * `aifinfo', it means that this interface was disabled. We should
320 	 * add this interface to `aifinfo' and set it state to IFIF_DISABLED.
321 	 */
322 	for (pifp = pifinfo; pifp != NULL; pifp = pifp->ifi_next) {
323 		for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
324 			if (strcmp(aifp->ifi_name, pifp->ifi_name) == 0) {
325 				aifp->ifi_pflags = pifp->ifi_pflags;
326 				break;
327 			}
328 		}
329 		if (aifp == NULL) {
330 			aifp = malloc(sizeof (ipadm_if_info_t));
331 			if (aifp == NULL) {
332 				status = ipadm_errno2status(errno);
333 				goto fail;
334 			}
335 			*aifp = *pifp;
336 			aifp->ifi_next = NULL;
337 			aifp->ifi_state = IFIS_DISABLED;
338 			if (last != NULL)
339 				last->ifi_next = aifp;
340 			else
341 				aifinfo = aifp;
342 			last = aifp;
343 		}
344 	}
345 	*if_info = aifinfo;
346 	ipadm_free_if_info(pifinfo);
347 	return (IPADM_SUCCESS);
348 fail:
349 	*if_info = NULL;
350 	ipadm_free_if_info(aifinfo);
351 	ipadm_free_if_info(pifinfo);
352 	return (status);
353 }
354 
355 int
356 i_ipadm_get_lnum(const char *ifname)
357 {
358 	char *num = strrchr(ifname, IPADM_LOGICAL_SEP);
359 
360 	if (num == NULL)
361 		return (0);
362 
363 	return (atoi(++num));
364 }
365 
366 /*
367  * Sets the output argument `exists' to true or false based on whether
368  * any persistent configuration is available for `ifname' and returns
369  * IPADM_SUCCESS as status. If the persistent information cannot be retrieved,
370  * `exists' is unmodified and an error status is returned.
371  */
372 ipadm_status_t
373 i_ipadm_if_pexists(ipadm_handle_t iph, const char *ifname, sa_family_t af,
374     boolean_t *exists)
375 {
376 	ipadm_if_info_t	*ifinfo;
377 	ipadm_status_t	status;
378 
379 	status = i_ipadm_persist_if_info(iph, ifname, &ifinfo);
380 	if (status == IPADM_SUCCESS) {
381 		*exists = ((af == AF_INET &&
382 		    (ifinfo->ifi_pflags & IFIF_IPV4)) ||
383 		    (af == AF_INET6 &&
384 		    (ifinfo->ifi_pflags & IFIF_IPV6)));
385 		free(ifinfo);
386 	} else if (status == IPADM_NOTFOUND) {
387 		status = IPADM_SUCCESS;
388 		*exists = B_FALSE;
389 	}
390 	return (status);
391 }
392 
393 /*
394  * Open "/dev/udp{,6}" for use as a multiplexor to PLINK the interface stream
395  * under. We use "/dev/udp" instead of "/dev/ip" since STREAMS will not let
396  * you PLINK a driver under itself, and "/dev/ip" is typically the driver at
397  * the bottom of the stream for tunneling interfaces.
398  */
399 ipadm_status_t
400 ipadm_open_arp_on_udp(const char *udp_dev_name, int *fd)
401 {
402 	int err;
403 
404 	if ((*fd = open(udp_dev_name, O_RDWR)) == -1)
405 		return (ipadm_errno2status(errno));
406 
407 	/*
408 	 * Pop off all undesired modules (note that the user may have
409 	 * configured autopush to add modules above udp), and push the
410 	 * arp module onto the resulting stream. This is used to make
411 	 * IP+ARP be able to atomically track the muxid for the I_PLINKed
412 	 * STREAMS, thus it isn't related to ARP running the ARP protocol.
413 	 */
414 	while (ioctl(*fd, I_POP, 0) != -1)
415 		;
416 	if (errno == EINVAL && ioctl(*fd, I_PUSH, ARP_MOD_NAME) != -1)
417 		return (IPADM_SUCCESS);
418 	err = errno;
419 	(void) close(*fd);
420 
421 	return (ipadm_errno2status(err));
422 }
423 
424 /*
425  * i_ipadm_create_ipmp() is called from i_ipadm_create_ipmp_peer() when an
426  * underlying interface in an ipmp group G is plumbed for an address family,
427  * but the meta-interface for the other address family `af' does not exist
428  * yet for the group G. If `af' is IPv6, we need to bring up the
429  * link-local address.
430  */
431 static ipadm_status_t
432 i_ipadm_create_ipmp(ipadm_handle_t iph, char *ifname, sa_family_t af,
433     const char *grname, uint32_t ipadm_flags)
434 {
435 	ipadm_status_t	status;
436 	struct lifreq	lifr;
437 	int		sock;
438 	int		err;
439 
440 	assert(ipadm_flags & IPADM_OPT_IPMP);
441 
442 	/* Create the ipmp underlying interface */
443 	status = i_ipadm_create_if(iph, ifname, af, ipadm_flags);
444 	if (status != IPADM_SUCCESS && status != IPADM_IF_EXISTS)
445 		return (status);
446 
447 	/*
448 	 * To preserve backward-compatibility, always bring up the link-local
449 	 * address for implicitly-created IPv6 IPMP interfaces.
450 	 */
451 	if (af == AF_INET6)
452 		(void) i_ipadm_set_flags(iph, ifname, AF_INET6, IFF_UP, 0);
453 
454 	sock = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
455 	/*
456 	 * If the caller requested a different group name, issue a
457 	 * SIOCSLIFGROUPNAME on the new IPMP interface.
458 	 */
459 	bzero(&lifr, sizeof (lifr));
460 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
461 	if (strcmp(lifr.lifr_name, grname) != 0) {
462 		(void) strlcpy(lifr.lifr_groupname, grname, LIFGRNAMSIZ);
463 		if (ioctl(sock, SIOCSLIFGROUPNAME, &lifr) == -1) {
464 			err = errno;
465 			/* Remove the interface we created. */
466 			if (status == IPADM_SUCCESS) {
467 				(void) i_ipadm_delete_if(iph, ifname, af,
468 				    ipadm_flags);
469 			}
470 			return (ipadm_errno2status(err));
471 		}
472 	}
473 
474 	return (IPADM_SUCCESS);
475 }
476 
477 /*
478  * Checks if `ifname' is plumbed and in an IPMP group on its "other" address
479  * family.  If so, create a matching IPMP group for address family `af'.
480  */
481 static ipadm_status_t
482 i_ipadm_create_ipmp_peer(ipadm_handle_t iph, char *ifname, sa_family_t af)
483 {
484 	lifgroupinfo_t	lifgr;
485 	ipadm_status_t	status = IPADM_SUCCESS;
486 	struct lifreq	lifr;
487 	int 		other_af_sock;
488 
489 	assert(af == AF_INET || af == AF_INET6);
490 
491 	other_af_sock = (af == AF_INET ? iph->iph_sock6 : iph->iph_sock);
492 
493 	/*
494 	 * iph is the handle for the interface that we are trying to plumb.
495 	 * other_af_sock is the socket for the "other" address family.
496 	 */
497 	bzero(&lifr, sizeof (lifr));
498 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
499 	if (ioctl(other_af_sock, SIOCGLIFGROUPNAME, &lifr) != 0)
500 		return (IPADM_SUCCESS);
501 
502 	(void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname, LIFGRNAMSIZ);
503 	if (ioctl(other_af_sock, SIOCGLIFGROUPINFO, &lifgr) != 0)
504 		return (IPADM_SUCCESS);
505 
506 	/*
507 	 * If `ifname' *is* the IPMP group interface, or if the relevant
508 	 * address family is already configured, then there's nothing to do.
509 	 */
510 	if (strcmp(lifgr.gi_grifname, ifname) == 0 ||
511 	    (af == AF_INET && lifgr.gi_v4) || (af == AF_INET6 && lifgr.gi_v6)) {
512 		return (IPADM_SUCCESS);
513 	}
514 
515 	status = i_ipadm_create_ipmp(iph, lifgr.gi_grifname, af,
516 	    lifgr.gi_grname, IPADM_OPT_ACTIVE|IPADM_OPT_IPMP);
517 	return (status);
518 }
519 
520 /*
521  * Issues the ioctl SIOCSLIFNAME to kernel on the given ARP stream fd.
522  */
523 static ipadm_status_t
524 i_ipadm_slifname_arp(char *ifname, uint64_t flags, int fd)
525 {
526 	struct lifreq	lifr;
527 	ifspec_t	ifsp;
528 
529 	bzero(&lifr, sizeof (lifr));
530 	(void) ifparse_ifspec(ifname, &ifsp);
531 	lifr.lifr_ppa = ifsp.ifsp_ppa;
532 	lifr.lifr_flags = flags;
533 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
534 	/*
535 	 * Tell ARP the name and unit number for this interface.
536 	 * Note that arp has no support for transparent ioctls.
537 	 */
538 	if (i_ipadm_strioctl(fd, SIOCSLIFNAME, (char *)&lifr,
539 	    sizeof (lifr)) == -1) {
540 		return (ipadm_errno2status(errno));
541 	}
542 	return (IPADM_SUCCESS);
543 }
544 
545 /*
546  * Issues the ioctl SIOCSLIFNAME to kernel. If IPADM_OPT_GENPPA is set in
547  * `ipadm_flags', then a ppa will be generated. `newif' will be updated
548  * with the generated ppa.
549  */
550 static ipadm_status_t
551 i_ipadm_slifname(ipadm_handle_t iph, char *ifname, char *newif, uint64_t flags,
552     int fd, uint32_t ipadm_flags)
553 {
554 	struct lifreq	lifr;
555 	ipadm_status_t	status = IPADM_SUCCESS;
556 	int		err = 0;
557 	sa_family_t	af;
558 	int		ppa;
559 	ifspec_t	ifsp;
560 	boolean_t	valid_if;
561 
562 	bzero(&lifr, sizeof (lifr));
563 	if (ipadm_flags & IPADM_OPT_GENPPA) {
564 		/*
565 		 * We'd like to just set lifr_ppa to UINT_MAX and have the
566 		 * kernel pick a PPA.  Unfortunately, that would mishandle
567 		 * two cases:
568 		 *
569 		 *	1. If the PPA is available but the groupname is taken
570 		 *	   (e.g., the "ipmp2" IP interface name is available
571 		 *	   but the "ipmp2" groupname is taken) then the
572 		 *	   auto-assignment by the kernel will fail.
573 		 *
574 		 *	2. If we're creating (e.g.) an IPv6-only IPMP
575 		 *	   interface, and there's already an IPv4-only IPMP
576 		 *	   interface, the kernel will allow us to accidentally
577 		 *	   reuse the IPv6 IPMP interface name (since
578 		 *	   SIOCSLIFNAME uniqueness is per-interface-type).
579 		 *	   This will cause administrative confusion.
580 		 *
581 		 * Thus, we instead take a brute-force approach of checking
582 		 * whether the IPv4 or IPv6 name is already in-use before
583 		 * attempting the SIOCSLIFNAME.  As per (1) above, the
584 		 * SIOCSLIFNAME may still fail, in which case we just proceed
585 		 * to the next one.  If this approach becomes too slow, we
586 		 * can add a new SIOC* to handle this case in the kernel.
587 		 */
588 		for (ppa = 0; ppa < UINT_MAX; ppa++) {
589 			(void) snprintf(lifr.lifr_name, LIFNAMSIZ, "%s%d",
590 			    ifname, ppa);
591 
592 			if (ioctl(iph->iph_sock, SIOCGLIFFLAGS, &lifr) != -1 ||
593 			    errno != ENXIO)
594 				continue;
595 
596 			if (ioctl(iph->iph_sock6, SIOCGLIFFLAGS, &lifr) != -1 ||
597 			    errno != ENXIO)
598 				continue;
599 
600 			lifr.lifr_ppa = ppa;
601 			lifr.lifr_flags = flags;
602 
603 			err = ioctl(fd, SIOCSLIFNAME, &lifr);
604 			if (err != -1 || errno != EEXIST)
605 				break;
606 		}
607 		if (err == -1) {
608 			status = ipadm_errno2status(errno);
609 		} else {
610 			/*
611 			 * PPA has been successfully established.
612 			 * Update `newif' with the ppa.
613 			 */
614 			assert(newif != NULL);
615 			if (snprintf(newif, LIFNAMSIZ, "%s%d", ifname,
616 			    ppa) >= LIFNAMSIZ)
617 				return (IPADM_INVALID_ARG);
618 		}
619 	} else {
620 		/* We should have already validated the interface name. */
621 		valid_if = ifparse_ifspec(ifname, &ifsp);
622 		assert(valid_if);
623 
624 		/*
625 		 * Before we call SIOCSLIFNAME, ensure that the IPMP group
626 		 * interface for this address family exists.  Otherwise, the
627 		 * kernel will kick the interface out of the group when we do
628 		 * the SIOCSLIFNAME.
629 		 *
630 		 * Example: suppose bge0 is plumbed for IPv4 and in group "a".
631 		 * If we're now plumbing bge0 for IPv6, but the IPMP group
632 		 * interface for "a" is not plumbed for IPv6, the SIOCSLIFNAME
633 		 * will kick bge0 out of group "a", which is undesired.
634 		 */
635 		if (flags & IFF_IPV4)
636 			af = AF_INET;
637 		else
638 			af = AF_INET6;
639 		status = i_ipadm_create_ipmp_peer(iph, ifname, af);
640 		if (status != IPADM_SUCCESS)
641 			return (status);
642 		lifr.lifr_ppa = ifsp.ifsp_ppa;
643 		lifr.lifr_flags = flags;
644 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
645 		if (ioctl(fd, SIOCSLIFNAME, &lifr) == -1)
646 			status = ipadm_errno2status(errno);
647 	}
648 
649 	return (status);
650 }
651 
652 /*
653  * Plumbs the interface `ifname' for the address family `af'. It also persists
654  * the interface for `af' if IPADM_OPT_PERSIST is set in `ipadm_flags'.
655  */
656 ipadm_status_t
657 i_ipadm_plumb_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
658     uint32_t ipadm_flags)
659 {
660 	int		ip_muxid;
661 	int		mux_fd = -1, ip_fd, arp_fd;
662 	char		*udp_dev_name;
663 	dlpi_handle_t	dh_arp = NULL, dh_ip;
664 	uint64_t	ifflags;
665 	struct lifreq	lifr;
666 	uint_t		dlpi_flags;
667 	ipadm_status_t	status = IPADM_SUCCESS;
668 	char		*linkname;
669 	boolean_t	legacy = (iph->iph_flags & IPH_LEGACY);
670 	zoneid_t	zoneid;
671 	char		newif[LIFNAMSIZ];
672 	char		lifname[LIFNAMSIZ];
673 	datalink_id_t	linkid;
674 	int		sock;
675 	boolean_t	islo;
676 	boolean_t	is_persistent =
677 	    ((ipadm_flags & IPADM_OPT_PERSIST) != 0);
678 	uint32_t	dlflags;
679 	dladm_status_t	dlstatus;
680 
681 	if (iph->iph_dlh != NULL) {
682 		dlstatus = dladm_name2info(iph->iph_dlh, ifname, &linkid,
683 		    &dlflags, NULL, NULL);
684 	}
685 	/*
686 	 * If we're in the global zone and we're plumbing a datalink, make
687 	 * sure that the datalink is not assigned to a non-global zone.  Note
688 	 * that the non-global zones don't need this check, because zoneadm
689 	 * has taken care of this when the zones boot.
690 	 */
691 	if (getzoneid() == GLOBAL_ZONEID && dlstatus == DLADM_STATUS_OK) {
692 		zoneid = ALL_ZONES;
693 		if (zone_check_datalink(&zoneid, linkid) == 0) {
694 			/* interface is in use by a non-global zone. */
695 			return (IPADM_IF_INUSE);
696 		}
697 	}
698 
699 	/* loopback interfaces are just added as logical interface */
700 	bzero(&lifr, sizeof (lifr));
701 	islo = i_ipadm_is_loopback(ifname);
702 	if (islo || i_ipadm_get_lnum(ifname) != 0) {
703 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
704 		if (af == AF_INET)
705 			sock = iph->iph_sock;
706 		else
707 			sock = iph->iph_sock6;
708 		if (islo && ioctl(sock, SIOCGLIFADDR, (caddr_t)&lifr) >= 0)
709 			return (IPADM_IF_EXISTS);
710 		if (ioctl(sock, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
711 			return (ipadm_errno2status(errno));
712 
713 		/*
714 		 * By default, kernel configures 127.0.0.1 on the loopback
715 		 * interface. Replace this with 0.0.0.0 to be consistent
716 		 * with interface creation on other physical interfaces.
717 		 */
718 		if (islo && !legacy) {
719 			bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
720 			lifr.lifr_addr.ss_family = af;
721 			if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
722 				return (ipadm_errno2status(errno));
723 			if (is_persistent) {
724 				status = i_ipadm_persist_if(iph, ifname, af);
725 				if (status != IPADM_SUCCESS) {
726 					(void) i_ipadm_delete_if(iph, ifname,
727 					    af, IPADM_OPT_ACTIVE);
728 				}
729 			}
730 		}
731 		return (status);
732 	}
733 
734 	dlpi_flags = DLPI_NOATTACH;
735 
736 	/*
737 	 * If IPADM_OPT_IPMP is specified, then this is a request
738 	 * to create an IPMP interface atop /dev/ipmpstub0.  (We can't simply
739 	 * pass "ipmpstub0" as devname since an admin *could* have a normal
740 	 * vanity-named link named "ipmpstub0" that they'd like to plumb.)
741 	 */
742 	if (ipadm_flags & IPADM_OPT_IPMP) {
743 		dlpi_flags |= DLPI_DEVONLY;
744 		linkname = "ipmpstub0";
745 	} else {
746 		/*
747 		 * Verify that the user is not creating a persistent
748 		 * IP interface on a non-persistent data-link.
749 		 */
750 		if (!i_ipadm_is_vni(ifname) && dlstatus == DLADM_STATUS_OK &&
751 		    is_persistent && !(dlflags & DLADM_OPT_PERSIST)) {
752 				return (IPADM_TEMPORARY_OBJ);
753 		}
754 		linkname = ifname;
755 	}
756 
757 	/*
758 	 * We use DLPI_NOATTACH because the ip module will do the attach
759 	 * itself for DLPI style-2 devices.
760 	 */
761 	if (dlpi_open(linkname, &dh_ip, dlpi_flags) != DLPI_SUCCESS)
762 		return (IPADM_DLPI_FAILURE);
763 	ip_fd = dlpi_fd(dh_ip);
764 	if (ioctl(ip_fd, I_PUSH, IP_MOD_NAME) == -1) {
765 		status = ipadm_errno2status(errno);
766 		goto done;
767 	}
768 
769 	/*
770 	 * Set IFF_IPV4/IFF_IPV6 flags. The kernel only allows modifications
771 	 * to IFF_IPv4, IFF_IPV6, IFF_BROADCAST, IFF_XRESOLV, IFF_NOLINKLOCAL.
772 	 */
773 	ifflags = 0;
774 
775 	/* Set the name string and the IFF_IPV* flag */
776 	if (af == AF_INET) {
777 		ifflags = IFF_IPV4;
778 	} else {
779 		ifflags = IFF_IPV6;
780 		/*
781 		 * With the legacy method, the link-local address should be
782 		 * configured as part of the interface plumb, using the default
783 		 * token. If IPH_LEGACY is not specified, we want to set :: as
784 		 * the address and require the admin to explicitly call
785 		 * ipadm_create_addr() with the address object type set to
786 		 * IPADM_ADDR_IPV6_ADDRCONF to create the link-local address
787 		 * as well as the autoconfigured addresses.
788 		 */
789 		if (!legacy && !i_ipadm_is_6to4(iph, ifname))
790 			ifflags |= IFF_NOLINKLOCAL;
791 	}
792 	(void) strlcpy(newif, ifname, sizeof (newif));
793 	status = i_ipadm_slifname(iph, ifname, newif, ifflags, ip_fd,
794 	    ipadm_flags);
795 	if (status != IPADM_SUCCESS)
796 		goto done;
797 
798 	/* Get the full set of existing flags for this stream */
799 	status = i_ipadm_get_flags(iph, newif, af, &ifflags);
800 	if (status != IPADM_SUCCESS)
801 		goto done;
802 
803 	udp_dev_name = (af == AF_INET6 ? UDP6_DEV_NAME : UDP_DEV_NAME);
804 	status = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd);
805 	if (status != IPADM_SUCCESS)
806 		goto done;
807 
808 	/* Check if arp is not needed */
809 	if (ifflags & (IFF_NOARP|IFF_IPV6)) {
810 		/*
811 		 * PLINK the interface stream so that the application can exit
812 		 * without tearing down the stream.
813 		 */
814 		if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1)
815 			status = ipadm_errno2status(errno);
816 		goto done;
817 	}
818 
819 	/*
820 	 * This interface does use ARP, so set up a separate stream
821 	 * from the interface to ARP.
822 	 *
823 	 * We use DLPI_NOATTACH because the arp module will do the attach
824 	 * itself for DLPI style-2 devices.
825 	 */
826 	if (dlpi_open(linkname, &dh_arp, dlpi_flags) != DLPI_SUCCESS) {
827 		status = IPADM_DLPI_FAILURE;
828 		goto done;
829 	}
830 
831 	arp_fd = dlpi_fd(dh_arp);
832 	if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1) {
833 		status = ipadm_errno2status(errno);
834 		goto done;
835 	}
836 
837 	status = i_ipadm_slifname_arp(newif, ifflags, arp_fd);
838 	if (status != IPADM_SUCCESS)
839 		goto done;
840 	/*
841 	 * PLINK the IP and ARP streams so that ifconfig can exit
842 	 * without tearing down the stream.
843 	 */
844 	if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) {
845 		status = ipadm_errno2status(errno);
846 		goto done;
847 	}
848 
849 	if (ioctl(mux_fd, I_PLINK, arp_fd) < 0) {
850 		status = ipadm_errno2status(errno);
851 		(void) ioctl(mux_fd, I_PUNLINK, ip_muxid);
852 	}
853 
854 done:
855 	dlpi_close(dh_ip);
856 	if (dh_arp != NULL)
857 		dlpi_close(dh_arp);
858 
859 	if (mux_fd != -1)
860 		(void) close(mux_fd);
861 
862 	if (status == IPADM_SUCCESS) {
863 		/* copy back new ifname */
864 		(void) strlcpy(ifname, newif, LIFNAMSIZ);
865 		/*
866 		 * If it is a 6to4 tunnel, create a default
867 		 * addrobj name for the default address on the 0'th
868 		 * logical interface and set IFF_UP in the interface flags.
869 		 */
870 		if (i_ipadm_is_6to4(iph, ifname)) {
871 			struct ipadm_addrobj_s addr;
872 
873 			i_ipadm_init_addr(&addr, ifname, "", IPADM_ADDR_STATIC);
874 			addr.ipadm_af = af;
875 			status = i_ipadm_lookupadd_addrobj(iph, &addr);
876 			if (status != IPADM_SUCCESS)
877 				return (status);
878 			status = ipadm_add_aobjname(iph, ifname,
879 			    af, addr.ipadm_aobjname, IPADM_ADDR_STATIC, 0);
880 			if (status != IPADM_SUCCESS)
881 				return (status);
882 			addr.ipadm_lifnum = 0;
883 			i_ipadm_addrobj2lifname(&addr, lifname,
884 			    sizeof (lifname));
885 			status = i_ipadm_set_flags(iph, lifname, af,
886 			    IFF_UP, 0);
887 			if (status != IPADM_SUCCESS)
888 				return (status);
889 		} else {
890 			/*
891 			 * Prevent static IPv6 addresses from triggering
892 			 * autoconf. This does not have to be done for
893 			 * 6to4 tunnel interfaces, since in.ndpd will
894 			 * not autoconfigure those interfaces.
895 			 */
896 			if (af == AF_INET6 && !legacy)
897 				(void) i_ipadm_disable_autoconf(newif);
898 		}
899 
900 		/*
901 		 * If IPADM_OPT_PERSIST was set in flags, store the
902 		 * interface in persistent DB.
903 		 */
904 		if (is_persistent) {
905 			status = i_ipadm_persist_if(iph, newif, af);
906 			if (status != IPADM_SUCCESS) {
907 				(void) i_ipadm_delete_if(iph, newif, af,
908 				    IPADM_OPT_ACTIVE);
909 			}
910 		}
911 	}
912 	if (status == IPADM_EXISTS)
913 		status = IPADM_IF_EXISTS;
914 	return (status);
915 }
916 
917 /*
918  * Unplumbs the interface in `ifname' of family `af'.
919  */
920 ipadm_status_t
921 i_ipadm_unplumb_if(ipadm_handle_t iph, const char *ifname, sa_family_t af)
922 {
923 	int		ip_muxid, arp_muxid;
924 	int		mux_fd = -1;
925 	int		muxid_fd = -1;
926 	char		*udp_dev_name;
927 	uint64_t	flags;
928 	boolean_t	changed_arp_muxid = B_FALSE;
929 	int		save_errno;
930 	struct lifreq	lifr;
931 	ipadm_status_t	ret = IPADM_SUCCESS;
932 	int		sock;
933 	lifgroupinfo_t	lifgr;
934 	ifaddrlistx_t	*ifaddrs, *ifaddrp;
935 	boolean_t	v6 = (af == AF_INET6);
936 
937 	/* Just do SIOCLIFREMOVEIF on loopback interfaces */
938 	bzero(&lifr, sizeof (lifr));
939 	if (i_ipadm_is_loopback(ifname) ||
940 	    (i_ipadm_get_lnum(ifname) != 0 && (iph->iph_flags & IPH_LEGACY))) {
941 		(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
942 		if (ioctl((af == AF_INET) ? iph->iph_sock : iph->iph_sock6,
943 		    SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) {
944 			return (ipadm_errno2status(errno));
945 		}
946 		return (IPADM_SUCCESS);
947 	}
948 
949 	/*
950 	 * We used /dev/udp or udp6 to set up the mux. So we have to use
951 	 * the same now for PUNLINK also.
952 	 */
953 	if (v6) {
954 		udp_dev_name = UDP6_DEV_NAME;
955 		sock = iph->iph_sock6;
956 	} else {
957 		udp_dev_name = UDP_DEV_NAME;
958 		sock = iph->iph_sock;
959 	}
960 	if ((muxid_fd = open(udp_dev_name, O_RDWR)) == -1) {
961 		ret = ipadm_errno2status(errno);
962 		goto done;
963 	}
964 	ret = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd);
965 	if (ret != IPADM_SUCCESS)
966 		goto done;
967 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
968 	if (ioctl(muxid_fd, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
969 		ret = ipadm_errno2status(errno);
970 		goto done;
971 	}
972 	flags = lifr.lifr_flags;
973 again:
974 	if (flags & IFF_IPMP) {
975 		/*
976 		 * There are two reasons the I_PUNLINK can fail with EBUSY:
977 		 * (1) if IP interfaces are in the group, or (2) if IPMP data
978 		 * addresses are administratively up.  For case (1), we fail
979 		 * here with a specific error message.  For case (2), we bring
980 		 * down the addresses prior to doing the I_PUNLINK.  If the
981 		 * I_PUNLINK still fails with EBUSY then the configuration
982 		 * must have changed after our checks, in which case we branch
983 		 * back up to `again' and rerun this logic.  The net effect is
984 		 * that unplumbing an IPMP interface will only fail with EBUSY
985 		 * if IP interfaces are in the group.
986 		 */
987 		if (ioctl(sock, SIOCGLIFGROUPNAME, &lifr) == -1) {
988 			ret = ipadm_errno2status(errno);
989 			goto done;
990 		}
991 		(void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname,
992 		    LIFGRNAMSIZ);
993 		if (ioctl(sock, SIOCGLIFGROUPINFO, &lifgr) == -1) {
994 			ret = ipadm_errno2status(errno);
995 			goto done;
996 		}
997 		if ((v6 && lifgr.gi_nv6 != 0) || (!v6 && lifgr.gi_nv4 != 0)) {
998 			ret = IPADM_GRP_NOTEMPTY;
999 			goto done;
1000 		}
1001 
1002 		/*
1003 		 * The kernel will fail the I_PUNLINK if the IPMP interface
1004 		 * has administratively up addresses; bring them down.
1005 		 */
1006 		if (ifaddrlistx(ifname, IFF_UP|IFF_DUPLICATE,
1007 		    0, &ifaddrs) == -1) {
1008 			ret = ipadm_errno2status(errno);
1009 			goto done;
1010 		}
1011 		ifaddrp = ifaddrs;
1012 		for (; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
1013 			int sock = (ifaddrp->ia_flags & IFF_IPV4) ?
1014 			    iph->iph_sock : iph->iph_sock6;
1015 			struct lifreq lifrl;
1016 
1017 			if (((ifaddrp->ia_flags & IFF_IPV6) && !v6) ||
1018 			    (!(ifaddrp->ia_flags & IFF_IPV6) && v6))
1019 				continue;
1020 
1021 			bzero(&lifrl, sizeof (lifrl));
1022 			(void) strlcpy(lifrl.lifr_name, ifaddrp->ia_name,
1023 			    sizeof (lifrl.lifr_name));
1024 			if (ioctl(sock, SIOCGLIFFLAGS, &lifrl) < 0) {
1025 				ret = ipadm_errno2status(errno);
1026 				ifaddrlistx_free(ifaddrs);
1027 				goto done;
1028 			}
1029 			if (lifrl.lifr_flags & IFF_UP) {
1030 				ret = i_ipadm_set_flags(iph, lifrl.lifr_name,
1031 				    ((lifrl.lifr_flags & IFF_IPV4) ? AF_INET :
1032 				    AF_INET6), 0, IFF_UP);
1033 				if (ret != IPADM_SUCCESS) {
1034 					ifaddrlistx_free(ifaddrs);
1035 					goto done;
1036 				}
1037 			} else if (lifrl.lifr_flags & IFF_DUPLICATE) {
1038 				if (ioctl(sock, SIOCGLIFADDR, &lifrl) < 0 ||
1039 				    ioctl(sock, SIOCSLIFADDR, &lifrl) < 0) {
1040 					ret = ipadm_errno2status(errno);
1041 					ifaddrlistx_free(ifaddrs);
1042 					goto done;
1043 				}
1044 			}
1045 		}
1046 		ifaddrlistx_free(ifaddrs);
1047 	}
1048 
1049 	if (ioctl(muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
1050 		ret = ipadm_errno2status(errno);
1051 		goto done;
1052 	}
1053 	arp_muxid = lifr.lifr_arp_muxid;
1054 	ip_muxid = lifr.lifr_ip_muxid;
1055 
1056 	/*
1057 	 * We don't have a good way of knowing whether the arp stream is
1058 	 * plumbed. We can't rely on IFF_NOARP because someone could
1059 	 * have turned it off later using "ifconfig xxx -arp".
1060 	 */
1061 	if (arp_muxid != 0) {
1062 		if (ioctl(mux_fd, I_PUNLINK, arp_muxid) < 0) {
1063 			/*
1064 			 * See the comment before the SIOCGLIFGROUPNAME call.
1065 			 */
1066 			if (errno == EBUSY && (flags & IFF_IPMP))
1067 				goto again;
1068 
1069 			if ((errno == EINVAL) &&
1070 			    (flags & (IFF_NOARP | IFF_IPV6))) {
1071 				/*
1072 				 * Some plumbing utilities set the muxid to
1073 				 * -1 or some invalid value to signify that
1074 				 * there is no arp stream. Set the muxid to 0
1075 				 * before trying to unplumb the IP stream.
1076 				 * IP does not allow the IP stream to be
1077 				 * unplumbed if it sees a non-null arp muxid,
1078 				 * for consistency of IP-ARP streams.
1079 				 */
1080 				lifr.lifr_arp_muxid = 0;
1081 				(void) ioctl(muxid_fd, SIOCSLIFMUXID,
1082 				    (caddr_t)&lifr);
1083 				changed_arp_muxid = B_TRUE;
1084 			}
1085 			/*
1086 			 * In case of any other error, we continue with
1087 			 * the unplumb.
1088 			 */
1089 		}
1090 	}
1091 
1092 	if (ioctl(mux_fd, I_PUNLINK, ip_muxid) < 0) {
1093 		if (changed_arp_muxid) {
1094 			/*
1095 			 * Some error occurred, and we need to restore
1096 			 * everything back to what it was.
1097 			 */
1098 			save_errno = errno;
1099 			lifr.lifr_arp_muxid = arp_muxid;
1100 			lifr.lifr_ip_muxid = ip_muxid;
1101 			(void) ioctl(muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
1102 			errno = save_errno;
1103 		}
1104 		/*
1105 		 * See the comment before the SIOCGLIFGROUPNAME call.
1106 		 */
1107 		if (errno == EBUSY && (flags & IFF_IPMP))
1108 			goto again;
1109 
1110 		ret = ipadm_errno2status(errno);
1111 	}
1112 done:
1113 	if (muxid_fd != -1)
1114 		(void) close(muxid_fd);
1115 	if (mux_fd != -1)
1116 		(void) close(mux_fd);
1117 
1118 	if (af == AF_INET6 && ret == IPADM_SUCCESS) {
1119 		/*
1120 		 * in.ndpd maintains the phyints in its memory even after
1121 		 * the interface is plumbed, so that it can be reused when
1122 		 * the interface gets plumbed again. The default behavior
1123 		 * of in.ndpd is to start autoconfiguration for an interface
1124 		 * that gets plumbed. We need to send the
1125 		 * message IPADM_ENABLE_AUTOCONF to in.ndpd to restore this
1126 		 * default behavior on replumb.
1127 		 */
1128 		(void) i_ipadm_enable_autoconf(ifname);
1129 	}
1130 	return (ret);
1131 }
1132 
1133 /*
1134  * Saves the given interface name `ifname' with address family `af' in
1135  * persistent DB.
1136  */
1137 static ipadm_status_t
1138 i_ipadm_persist_if(ipadm_handle_t iph, const char *ifname, sa_family_t af)
1139 {
1140 	ipmgmt_if_arg_t		ifarg;
1141 	int			err;
1142 
1143 	(void) strlcpy(ifarg.ia_ifname, ifname, sizeof (ifarg.ia_ifname));
1144 	ifarg.ia_family = af;
1145 	ifarg.ia_cmd = IPMGMT_CMD_SETIF;
1146 	ifarg.ia_flags = IPMGMT_PERSIST;
1147 	err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
1148 	return (ipadm_errno2status(err));
1149 }
1150 
1151 /*
1152  * Remove the IP interface from active configuration. If IPADM_OPT_PERSIST
1153  * is set in `ipadm_flags', it is also removed from persistent configuration.
1154  */
1155 ipadm_status_t
1156 i_ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1157     uint32_t ipadm_flags)
1158 {
1159 	ipadm_status_t		ret = IPADM_SUCCESS;
1160 	ipadm_status_t		db_status;
1161 	char			tmp_ifname[LIFNAMSIZ];
1162 	char			*cp;
1163 	struct ipadm_addrobj_s	ipaddr;
1164 	boolean_t		is_persistent =
1165 	    (ipadm_flags & IPADM_OPT_PERSIST);
1166 
1167 	ret = i_ipadm_unplumb_if(iph, ifname, af);
1168 	if (ret != IPADM_SUCCESS)
1169 		goto done;
1170 
1171 	cp = strrchr(ifname, IPADM_LOGICAL_SEP);
1172 	if (cp != NULL) {
1173 		assert(iph->iph_flags & IPH_LEGACY);
1174 		/*
1175 		 * This is a non-zero logical interface.
1176 		 * Find the addrobj and remove it from the daemon's memory.
1177 		 */
1178 		(void) strlcpy(tmp_ifname, ifname, sizeof (tmp_ifname));
1179 		tmp_ifname[cp - ifname] = '\0';
1180 		*cp++ = '\0';
1181 		ipaddr.ipadm_lifnum = atoi(cp);
1182 		(void) strlcpy(ipaddr.ipadm_ifname, tmp_ifname,
1183 		    sizeof (ipaddr.ipadm_ifname));
1184 		ipaddr.ipadm_af = af;
1185 		ret = i_ipadm_get_lif2addrobj(iph, &ipaddr);
1186 		if (ret == IPADM_SUCCESS) {
1187 			ret = i_ipadm_delete_addrobj(iph, &ipaddr,
1188 			    IPADM_OPT_ACTIVE);
1189 		} else if (ret == IPADM_NOTFOUND) {
1190 			ret = IPADM_SUCCESS;
1191 		}
1192 		return (ret);
1193 	}
1194 done:
1195 	/*
1196 	 * Even if interface does not exist, remove all its addresses and
1197 	 * properties from the persistent store. If interface does not
1198 	 * exist both in kernel and the persistent store, return IPADM_ENXIO.
1199 	 */
1200 	if ((ret == IPADM_ENXIO && is_persistent) || ret == IPADM_SUCCESS) {
1201 		db_status = i_ipadm_delete_ifobj(iph, ifname, af,
1202 		    is_persistent);
1203 		if (db_status == IPADM_SUCCESS)
1204 			ret = IPADM_SUCCESS;
1205 	}
1206 
1207 	return (ret);
1208 }
1209 
1210 /*
1211  * Resets all addresses on interface `ifname' with address family `af'
1212  * from ipmgmtd daemon. If is_persistent = B_TRUE, all interface properties
1213  * and address objects of `ifname' for `af' are also removed from the
1214  * persistent DB.
1215  */
1216 ipadm_status_t
1217 i_ipadm_delete_ifobj(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1218     boolean_t is_persistent)
1219 {
1220 	ipmgmt_if_arg_t		ifarg;
1221 	int			err;
1222 
1223 	ifarg.ia_cmd = IPMGMT_CMD_RESETIF;
1224 	ifarg.ia_flags = IPMGMT_ACTIVE;
1225 	if (is_persistent)
1226 		ifarg.ia_flags |= IPMGMT_PERSIST;
1227 	ifarg.ia_family = af;
1228 	(void) strlcpy(ifarg.ia_ifname, ifname, LIFNAMSIZ);
1229 
1230 	err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
1231 	return (ipadm_errno2status(err));
1232 }
1233 
1234 /*
1235  * Create the interface by plumbing it for IP.
1236  * This function will check if there is saved configuration information
1237  * for `ifname' and return IPADM_OP_DISABLE_OBJ if the name-space
1238  * for `ifname' is taken.
1239  */
1240 ipadm_status_t
1241 i_ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
1242     uint32_t ipadm_flags)
1243 {
1244 	ipadm_status_t	status;
1245 	boolean_t	p_exists;
1246 	sa_family_t	other_af;
1247 
1248 	/*
1249 	 * Return error, if the interface already exists in either the active
1250 	 * or the persistent configuration.
1251 	 */
1252 	if (ipadm_if_enabled(iph, ifname, af))
1253 		return (IPADM_IF_EXISTS);
1254 
1255 	status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
1256 	if (status != IPADM_SUCCESS)
1257 		return (status);
1258 	other_af = (af == AF_INET ? AF_INET6 : AF_INET);
1259 	if (p_exists) {
1260 		if (!ipadm_if_enabled(iph, ifname, other_af))
1261 			return (IPADM_OP_DISABLE_OBJ);
1262 		else
1263 			ipadm_flags &= ~IPADM_OPT_PERSIST;
1264 	}
1265 
1266 	return (i_ipadm_plumb_if(iph, ifname, af, ipadm_flags));
1267 }
1268 
1269 /*
1270  * Plumbs an interface. Creates both IPv4 and IPv6 interfaces by
1271  * default, unless a value in `af' is specified. The interface may be plumbed
1272  * only if there is no previously saved persistent configuration information
1273  * for the interface (in which case the ipadm_enable_if() function must
1274  * be used to enable the interface).
1275  *
1276  * Returns: IPADM_SUCCESS, IPADM_FAILURE, IPADM_IF_EXISTS,
1277  * IPADM_IF_PERSIST_EXISTS, IPADM_DLPI_FAILURE,
1278  * or appropriate ipadm_status_t corresponding to the errno.
1279  *
1280  * `ifname' must point to memory that can hold upto LIFNAMSIZ chars. It may
1281  * be over-written with the actual interface name when a PPA has to be
1282  * internally generated by the library.
1283  */
1284 ipadm_status_t
1285 ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
1286     uint32_t flags)
1287 {
1288 	ipadm_status_t	status;
1289 	boolean_t	created_v4 = B_FALSE;
1290 	char		newifname[LIFNAMSIZ];
1291 
1292 	/* Check for the required authorization */
1293 	if (!ipadm_check_auth())
1294 		return (IPADM_EAUTH);
1295 
1296 	if (flags == 0 || ((flags & IPADM_OPT_PERSIST) &&
1297 	    !(flags & IPADM_OPT_ACTIVE)) ||
1298 	    (flags & ~(IPADM_COMMON_OPT_MASK | IPADM_OPT_IPMP |
1299 	    IPADM_OPT_GENPPA))) {
1300 		return (IPADM_INVALID_ARG);
1301 	}
1302 	if (flags & IPADM_OPT_GENPPA) {
1303 		if (snprintf(newifname, LIFNAMSIZ, "%s0", ifname) >=
1304 		    LIFNAMSIZ)
1305 			return (IPADM_INVALID_ARG);
1306 	} else {
1307 		if (strlcpy(newifname, ifname, LIFNAMSIZ) >= LIFNAMSIZ)
1308 			return (IPADM_INVALID_ARG);
1309 	}
1310 
1311 	if (!i_ipadm_validate_ifname(iph, newifname))
1312 		return (IPADM_INVALID_ARG);
1313 
1314 	if ((af == AF_INET || af == AF_UNSPEC) &&
1315 	    !i_ipadm_is_6to4(iph, ifname)) {
1316 		status = i_ipadm_create_if(iph, ifname, AF_INET, flags);
1317 		if (status != IPADM_SUCCESS)
1318 			return (status);
1319 		created_v4 = B_TRUE;
1320 	}
1321 	if (af == AF_INET6 || af == AF_UNSPEC) {
1322 		status = i_ipadm_create_if(iph, ifname, AF_INET6, flags);
1323 		if (status != IPADM_SUCCESS) {
1324 			if (created_v4) {
1325 				(void) i_ipadm_delete_if(iph, ifname, AF_INET,
1326 				    IPADM_OPT_ACTIVE);
1327 			}
1328 			return (status);
1329 		}
1330 	}
1331 
1332 	return (IPADM_SUCCESS);
1333 }
1334 
1335 /*
1336  * Deletes the interface in `ifname'. Removes both IPv4 and IPv6 interfaces
1337  * when `af' = AF_UNSPEC.
1338  */
1339 ipadm_status_t
1340 ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1341     uint32_t flags)
1342 {
1343 	ipadm_status_t status1 = IPADM_SUCCESS;
1344 	ipadm_status_t status2 = IPADM_SUCCESS;
1345 	ipadm_status_t other;
1346 
1347 	/* Check for the required authorization */
1348 	if (!ipadm_check_auth())
1349 		return (IPADM_EAUTH);
1350 
1351 	/* Validate the `ifname' for any logical interface. */
1352 	if (flags == 0 || (flags & ~(IPADM_COMMON_OPT_MASK)) ||
1353 	    !i_ipadm_validate_ifname(iph, ifname))
1354 		return (IPADM_INVALID_ARG);
1355 
1356 	if (af == AF_INET || af == AF_UNSPEC)
1357 		status1 = i_ipadm_delete_if(iph, ifname, AF_INET, flags);
1358 	if (af == AF_INET6 || af == AF_UNSPEC)
1359 		status2 = i_ipadm_delete_if(iph, ifname, AF_INET6, flags);
1360 	/*
1361 	 * If the family has been uniquely identified, we return the
1362 	 * associated status, even if that is ENXIO. Calls from ifconfig
1363 	 * which can only unplumb one of IPv4/IPv6 at any time fall under
1364 	 * this category.
1365 	 */
1366 	if (af == AF_INET)
1367 		return (status1);
1368 	else if (af == AF_INET6)
1369 		return (status2);
1370 	else if (af != AF_UNSPEC)
1371 		return (IPADM_INVALID_ARG);
1372 
1373 	/*
1374 	 * If af is AF_UNSPEC, then we return the following:
1375 	 * status1,		if status1 == status2
1376 	 * IPADM_SUCCESS,	if either of status1 or status2 is SUCCESS
1377 	 * 			and the other status is ENXIO
1378 	 * IPADM_ENXIO,		if both status1 and status2 are ENXIO
1379 	 * IPADM_FAILURE	otherwise.
1380 	 */
1381 	if (status1 == status2) {
1382 		/* covers the case when both status1 and status2 are ENXIO */
1383 		return (status1);
1384 	} else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
1385 		if (status1 == IPADM_SUCCESS)
1386 			other = status2;
1387 		else
1388 			other = status1;
1389 		return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
1390 	} else {
1391 		return (IPADM_FAILURE);
1392 	}
1393 }
1394 
1395 /*
1396  * Returns information about all interfaces in both active and persistent
1397  * configuration. If `ifname' is not NULL, it returns only the interface
1398  * identified by `ifname'.
1399  *
1400  * Return values:
1401  * 	On success: IPADM_SUCCESS.
1402  * 	On error  : IPADM_INVALID_ARG, IPADM_ENXIO or IPADM_FAILURE.
1403  */
1404 ipadm_status_t
1405 ipadm_if_info(ipadm_handle_t iph, const char *ifname,
1406     ipadm_if_info_t **if_info, uint32_t flags, int64_t lifc_flags)
1407 {
1408 	ipadm_status_t	status;
1409 	ifspec_t	ifsp;
1410 
1411 	if (if_info == NULL || iph == NULL || flags != 0)
1412 		return (IPADM_INVALID_ARG);
1413 
1414 	if (ifname != NULL &&
1415 	    (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)) {
1416 		return (IPADM_INVALID_ARG);
1417 	}
1418 
1419 	status = i_ipadm_get_all_if_info(iph, ifname, if_info, lifc_flags);
1420 	if (status != IPADM_SUCCESS)
1421 		return (status);
1422 	if (ifname != NULL && *if_info == NULL)
1423 		return (IPADM_ENXIO);
1424 
1425 	return (IPADM_SUCCESS);
1426 }
1427 
1428 /*
1429  * Frees the linked list allocated by ipadm_if_info().
1430  */
1431 void
1432 ipadm_free_if_info(ipadm_if_info_t *ifinfo)
1433 {
1434 	ipadm_if_info_t	*ifinfo_next;
1435 
1436 	for (; ifinfo != NULL; ifinfo = ifinfo_next) {
1437 		ifinfo_next = ifinfo->ifi_next;
1438 		free(ifinfo);
1439 	}
1440 }
1441 
1442 /*
1443  * Re-enable the interface `ifname' based on the saved configuration
1444  * for `ifname'.
1445  */
1446 ipadm_status_t
1447 ipadm_enable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
1448 {
1449 	nvlist_t	*ifnvl;
1450 	ipadm_status_t	status;
1451 	ifspec_t	ifsp;
1452 
1453 	/* Check for the required authorization */
1454 	if (!ipadm_check_auth())
1455 		return (IPADM_EAUTH);
1456 
1457 	/* Check for logical interfaces. */
1458 	if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
1459 		return (IPADM_INVALID_ARG);
1460 
1461 	/* Enabling an interface persistently is not supported. */
1462 	if (flags & IPADM_OPT_PERSIST)
1463 		return (IPADM_NOTSUP);
1464 
1465 	/*
1466 	 * Return early by checking if the interface is already enabled.
1467 	 */
1468 	if (ipadm_if_enabled(iph, ifname, AF_INET) &&
1469 	    ipadm_if_enabled(iph, ifname, AF_INET6)) {
1470 		return (IPADM_IF_EXISTS);
1471 	}
1472 	/*
1473 	 * Enable the interface and restore all its interface properties
1474 	 * and address objects.
1475 	 */
1476 	status = i_ipadm_init_ifs(iph, ifname, &ifnvl);
1477 	if (status != IPADM_SUCCESS)
1478 		return (status);
1479 
1480 	assert(ifnvl != NULL);
1481 	/*
1482 	 * ipadm_enable_if() does exactly what ipadm_init_ifs() does,
1483 	 * but only for one interface. We need to set IPH_INIT because
1484 	 * ipmgmtd daemon does not have to write the interface to persistent
1485 	 * db. The interface is already available in persistent db
1486 	 * and we are here to re-enable the persistent configuration.
1487 	 */
1488 	iph->iph_flags |= IPH_INIT;
1489 	status = i_ipadm_init_ifobj(iph, ifname, ifnvl);
1490 	iph->iph_flags &= ~IPH_INIT;
1491 	return (status);
1492 }
1493 
1494 /*
1495  * Disable the interface `ifname' by removing it from the active configuration.
1496  * Error code return values follow the model in ipadm_delete_if()
1497  */
1498 ipadm_status_t
1499 ipadm_disable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
1500 {
1501 	ipadm_status_t	status1, status2, other;
1502 	ifspec_t	ifsp;
1503 
1504 	/* Check for the required authorization */
1505 	if (!ipadm_check_auth())
1506 		return (IPADM_EAUTH);
1507 
1508 	/* Check for logical interfaces. */
1509 	if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
1510 		return (IPADM_INVALID_ARG);
1511 
1512 	/* Disabling an interface persistently is not supported. */
1513 	if (flags & IPADM_OPT_PERSIST)
1514 		return (IPADM_NOTSUP);
1515 
1516 	status1 = i_ipadm_unplumb_if(iph, ifname, AF_INET6);
1517 	if (status1 == IPADM_SUCCESS)
1518 		status1 = i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
1519 	status2 = i_ipadm_unplumb_if(iph, ifname, AF_INET);
1520 	if (status2 == IPADM_SUCCESS)
1521 		status2 = i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
1522 	if (status1 == status2) {
1523 		return (status2);
1524 	} else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
1525 		if (status1 == IPADM_SUCCESS)
1526 			other = status2;
1527 		else
1528 			other = status1;
1529 		return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
1530 	} else {
1531 		return (IPADM_FAILURE);
1532 	}
1533 }
1534 
1535 /*
1536  * This workaround is until libipadm supports IPMP and is required whenever an
1537  * interface is moved into an IPMP group. Since libipadm doesn't support IPMP
1538  * yet, we will have to update the daemon's in-memory mapping of
1539  * `aobjname' to 'lifnum'.
1540  *
1541  * For `IPMGMT_ACTIVE' case, i_ipadm_delete_ifobj() would only fail if
1542  * door_call(3C) fails. Also, there is no use in returning error because
1543  * `ifname' would have been successfuly moved into IPMP group, by this time.
1544  */
1545 void
1546 ipadm_if_move(ipadm_handle_t iph, const char *ifname)
1547 {
1548 	(void) i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
1549 	(void) i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
1550 }
1551