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