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