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