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