16e91bba0SGirish Moodalbail /* 26e91bba0SGirish Moodalbail * CDDL HEADER START 36e91bba0SGirish Moodalbail * 46e91bba0SGirish Moodalbail * The contents of this file are subject to the terms of the 56e91bba0SGirish Moodalbail * Common Development and Distribution License (the "License"). 66e91bba0SGirish Moodalbail * You may not use this file except in compliance with the License. 76e91bba0SGirish Moodalbail * 86e91bba0SGirish Moodalbail * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 96e91bba0SGirish Moodalbail * or http://www.opensolaris.org/os/licensing. 106e91bba0SGirish Moodalbail * See the License for the specific language governing permissions 116e91bba0SGirish Moodalbail * and limitations under the License. 126e91bba0SGirish Moodalbail * 136e91bba0SGirish Moodalbail * When distributing Covered Code, include this CDDL HEADER in each 146e91bba0SGirish Moodalbail * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 156e91bba0SGirish Moodalbail * If applicable, add the following below this CDDL HEADER, with the 166e91bba0SGirish Moodalbail * fields enclosed by brackets "[]" replaced with your own identifying 176e91bba0SGirish Moodalbail * information: Portions Copyright [yyyy] [name of copyright owner] 186e91bba0SGirish Moodalbail * 196e91bba0SGirish Moodalbail * CDDL HEADER END 206e91bba0SGirish Moodalbail */ 216e91bba0SGirish Moodalbail /* 2236b41818SGirish Moodalbail * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 236e91bba0SGirish Moodalbail */ 246e91bba0SGirish Moodalbail 256e91bba0SGirish Moodalbail #include <errno.h> 266e91bba0SGirish Moodalbail #include <sys/sockio.h> 276e91bba0SGirish Moodalbail #include <string.h> 286e91bba0SGirish Moodalbail #include <assert.h> 296e91bba0SGirish Moodalbail #include <unistd.h> 306e91bba0SGirish Moodalbail #include <stropts.h> 316e91bba0SGirish Moodalbail #include <strings.h> 326e91bba0SGirish Moodalbail #include <libdlpi.h> 336e91bba0SGirish Moodalbail #include <libdllink.h> 346e91bba0SGirish Moodalbail #include <libinetutil.h> 356e91bba0SGirish Moodalbail #include <inet/ip.h> 366e91bba0SGirish Moodalbail #include <limits.h> 376e91bba0SGirish Moodalbail #include <zone.h> 386e91bba0SGirish Moodalbail #include <ipadm_ndpd.h> 396e91bba0SGirish Moodalbail #include "libipadm_impl.h" 406e91bba0SGirish Moodalbail 416e91bba0SGirish Moodalbail static ipadm_status_t i_ipadm_slifname_arp(char *, uint64_t, int); 426e91bba0SGirish Moodalbail static ipadm_status_t i_ipadm_slifname(ipadm_handle_t, char *, char *, 436e91bba0SGirish Moodalbail uint64_t, int, uint32_t); 446e91bba0SGirish Moodalbail static ipadm_status_t i_ipadm_create_ipmp_peer(ipadm_handle_t, char *, 456e91bba0SGirish Moodalbail sa_family_t); 466e91bba0SGirish Moodalbail static ipadm_status_t i_ipadm_persist_if(ipadm_handle_t, const char *, 476e91bba0SGirish Moodalbail sa_family_t); 486e91bba0SGirish Moodalbail 496e91bba0SGirish Moodalbail /* 506e91bba0SGirish Moodalbail * Returns B_FALSE if the interface in `ifname' has at least one address that is 516e91bba0SGirish Moodalbail * IFF_UP in the addresses in `ifa'. 526e91bba0SGirish Moodalbail */ 536e91bba0SGirish Moodalbail static boolean_t 546e91bba0SGirish Moodalbail i_ipadm_is_if_down(char *ifname, struct ifaddrs *ifa) 556e91bba0SGirish Moodalbail { 566e91bba0SGirish Moodalbail struct ifaddrs *ifap; 576e91bba0SGirish Moodalbail char cifname[LIFNAMSIZ]; 586e91bba0SGirish Moodalbail char *sep; 596e91bba0SGirish Moodalbail 606e91bba0SGirish Moodalbail for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) { 616e91bba0SGirish Moodalbail (void) strlcpy(cifname, ifap->ifa_name, sizeof (cifname)); 626e91bba0SGirish Moodalbail if ((sep = strrchr(cifname, IPADM_LOGICAL_SEP)) != NULL) 636e91bba0SGirish Moodalbail *sep = '\0'; 646e91bba0SGirish Moodalbail /* 656e91bba0SGirish Moodalbail * If this condition is true, there is at least one 666e91bba0SGirish Moodalbail * address that is IFF_UP. So, we need to return B_FALSE. 676e91bba0SGirish Moodalbail */ 686e91bba0SGirish Moodalbail if (strcmp(cifname, ifname) == 0 && 696e91bba0SGirish Moodalbail (ifap->ifa_flags & IFF_UP)) { 706e91bba0SGirish Moodalbail return (B_FALSE); 716e91bba0SGirish Moodalbail } 726e91bba0SGirish Moodalbail } 736e91bba0SGirish Moodalbail /* We did not find any IFF_UP addresses. */ 746e91bba0SGirish Moodalbail return (B_TRUE); 756e91bba0SGirish Moodalbail } 766e91bba0SGirish Moodalbail 776e91bba0SGirish Moodalbail /* 786e91bba0SGirish Moodalbail * Retrieves the information for the interface `ifname' from active 796e91bba0SGirish Moodalbail * config if `ifname' is specified and returns the result in the list `if_info'. 806e91bba0SGirish Moodalbail * Otherwise, it retrieves the information for all the interfaces in 816e91bba0SGirish Moodalbail * the active config and returns the result in the list `if_info'. 826e91bba0SGirish Moodalbail */ 836e91bba0SGirish Moodalbail static ipadm_status_t 846e91bba0SGirish Moodalbail i_ipadm_active_if_info(ipadm_handle_t iph, const char *ifname, 856e91bba0SGirish Moodalbail ipadm_if_info_t **if_info, int64_t lifc_flags) 866e91bba0SGirish Moodalbail { 876e91bba0SGirish Moodalbail struct lifreq *buf; 886e91bba0SGirish Moodalbail struct lifreq *lifrp; 896e91bba0SGirish Moodalbail struct lifreq lifrl; 906e91bba0SGirish Moodalbail ipadm_if_info_t *last = NULL; 916e91bba0SGirish Moodalbail ipadm_if_info_t *ifp; 926e91bba0SGirish Moodalbail int s; 936e91bba0SGirish Moodalbail int n; 946e91bba0SGirish Moodalbail int numifs; 956e91bba0SGirish Moodalbail ipadm_status_t status; 966e91bba0SGirish Moodalbail 976e91bba0SGirish Moodalbail *if_info = NULL; 986e91bba0SGirish Moodalbail /* 996e91bba0SGirish Moodalbail * Get information for all interfaces. 1006e91bba0SGirish Moodalbail */ 1016e91bba0SGirish Moodalbail if (getallifs(iph->iph_sock, 0, &buf, &numifs, lifc_flags) != 0) 1026e91bba0SGirish Moodalbail return (ipadm_errno2status(errno)); 1036e91bba0SGirish Moodalbail 1046e91bba0SGirish Moodalbail lifrp = buf; 1056e91bba0SGirish Moodalbail for (n = 0; n < numifs; n++, lifrp++) { 1066e91bba0SGirish Moodalbail /* Skip interfaces with logical num != 0 */ 1076e91bba0SGirish Moodalbail if (i_ipadm_get_lnum(lifrp->lifr_name) != 0) 1086e91bba0SGirish Moodalbail continue; 1096e91bba0SGirish Moodalbail /* 1106e91bba0SGirish Moodalbail * Skip the current interface if a specific `ifname' has 1116e91bba0SGirish Moodalbail * been requested and current interface does not match 1126e91bba0SGirish Moodalbail * `ifname'. 1136e91bba0SGirish Moodalbail */ 1146e91bba0SGirish Moodalbail if (ifname != NULL && strcmp(lifrp->lifr_name, ifname) != 0) 1156e91bba0SGirish Moodalbail continue; 1166e91bba0SGirish Moodalbail /* 1176e91bba0SGirish Moodalbail * Check if the interface already exists in our list. 1186e91bba0SGirish Moodalbail * If it already exists, we need to update its flags. 1196e91bba0SGirish Moodalbail */ 1206e91bba0SGirish Moodalbail for (ifp = *if_info; ifp != NULL; ifp = ifp->ifi_next) { 1216e91bba0SGirish Moodalbail if (strcmp(lifrp->lifr_name, ifp->ifi_name) == 0) 1226e91bba0SGirish Moodalbail break; 1236e91bba0SGirish Moodalbail } 1246e91bba0SGirish Moodalbail if (ifp == NULL) { 1256e91bba0SGirish Moodalbail ifp = calloc(1, sizeof (ipadm_if_info_t)); 1266e91bba0SGirish Moodalbail if (ifp == NULL) { 1276e91bba0SGirish Moodalbail status = ipadm_errno2status(errno); 1286e91bba0SGirish Moodalbail goto fail; 1296e91bba0SGirish Moodalbail } 1306e91bba0SGirish Moodalbail (void) strlcpy(ifp->ifi_name, lifrp->lifr_name, 1316e91bba0SGirish Moodalbail sizeof (ifp->ifi_name)); 1326e91bba0SGirish Moodalbail /* Update the `ifi_next' pointer for this new node */ 1336e91bba0SGirish Moodalbail if (*if_info == NULL) 1346e91bba0SGirish Moodalbail *if_info = ifp; 1356e91bba0SGirish Moodalbail else 1366e91bba0SGirish Moodalbail last->ifi_next = ifp; 1376e91bba0SGirish Moodalbail last = ifp; 1386e91bba0SGirish Moodalbail } 1396e91bba0SGirish Moodalbail 1406e91bba0SGirish Moodalbail /* 1416e91bba0SGirish Moodalbail * Retrieve the flags for the interface by doing a 1426e91bba0SGirish Moodalbail * SIOCGLIFFLAGS to populate the `ifi_cflags' field. 1436e91bba0SGirish Moodalbail */ 1446e91bba0SGirish Moodalbail (void) strlcpy(lifrl.lifr_name, 1456e91bba0SGirish Moodalbail lifrp->lifr_name, sizeof (lifrl.lifr_name)); 1466e91bba0SGirish Moodalbail s = (lifrp->lifr_addr.ss_family == AF_INET) ? 1476e91bba0SGirish Moodalbail iph->iph_sock : iph->iph_sock6; 1486e91bba0SGirish Moodalbail if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0) 1496e91bba0SGirish Moodalbail continue; 1506e91bba0SGirish Moodalbail if (lifrl.lifr_flags & IFF_BROADCAST) 1516e91bba0SGirish Moodalbail ifp->ifi_cflags |= IFIF_BROADCAST; 1526e91bba0SGirish Moodalbail if (lifrl.lifr_flags & IFF_MULTICAST) 1536e91bba0SGirish Moodalbail ifp->ifi_cflags |= IFIF_MULTICAST; 1546e91bba0SGirish Moodalbail if (lifrl.lifr_flags & IFF_POINTOPOINT) 1556e91bba0SGirish Moodalbail ifp->ifi_cflags |= IFIF_POINTOPOINT; 1566e91bba0SGirish Moodalbail if (lifrl.lifr_flags & IFF_VIRTUAL) 1576e91bba0SGirish Moodalbail ifp->ifi_cflags |= IFIF_VIRTUAL; 1586e91bba0SGirish Moodalbail if (lifrl.lifr_flags & IFF_IPMP) 1596e91bba0SGirish Moodalbail ifp->ifi_cflags |= IFIF_IPMP; 1606e91bba0SGirish Moodalbail if (lifrl.lifr_flags & IFF_STANDBY) 1616e91bba0SGirish Moodalbail ifp->ifi_cflags |= IFIF_STANDBY; 1626e91bba0SGirish Moodalbail if (lifrl.lifr_flags & IFF_INACTIVE) 1636e91bba0SGirish Moodalbail ifp->ifi_cflags |= IFIF_INACTIVE; 1646e91bba0SGirish Moodalbail if (lifrl.lifr_flags & IFF_VRRP) 1656e91bba0SGirish Moodalbail ifp->ifi_cflags |= IFIF_VRRP; 1666e91bba0SGirish Moodalbail if (lifrl.lifr_flags & IFF_NOACCEPT) 1676e91bba0SGirish Moodalbail ifp->ifi_cflags |= IFIF_NOACCEPT; 1686e91bba0SGirish Moodalbail if (lifrl.lifr_flags & IFF_IPV4) 1696e91bba0SGirish Moodalbail ifp->ifi_cflags |= IFIF_IPV4; 1706e91bba0SGirish Moodalbail if (lifrl.lifr_flags & IFF_IPV6) 1716e91bba0SGirish Moodalbail ifp->ifi_cflags |= IFIF_IPV6; 172*550b6e40SSowmini Varadhan if (lifrl.lifr_flags & IFF_L3PROTECT) 173*550b6e40SSowmini Varadhan ifp->ifi_cflags |= IFIF_L3PROTECT; 1746e91bba0SGirish Moodalbail } 1756e91bba0SGirish Moodalbail free(buf); 1766e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 1776e91bba0SGirish Moodalbail fail: 1786e91bba0SGirish Moodalbail free(buf); 1796e91bba0SGirish Moodalbail ipadm_free_if_info(*if_info); 1806e91bba0SGirish Moodalbail *if_info = NULL; 1816e91bba0SGirish Moodalbail return (status); 1826e91bba0SGirish Moodalbail } 1836e91bba0SGirish Moodalbail 1846e91bba0SGirish Moodalbail /* 1856e91bba0SGirish Moodalbail * Returns the interface information for `ifname' in `if_info' from persistent 1866e91bba0SGirish Moodalbail * config if `ifname' is non-null. Otherwise, it returns all the interfaces 1876e91bba0SGirish Moodalbail * from persistent config in `if_info'. 1886e91bba0SGirish Moodalbail */ 1896e91bba0SGirish Moodalbail static ipadm_status_t 1906e91bba0SGirish Moodalbail i_ipadm_persist_if_info(ipadm_handle_t iph, const char *ifname, 1916e91bba0SGirish Moodalbail ipadm_if_info_t **if_info) 1926e91bba0SGirish Moodalbail { 1936e91bba0SGirish Moodalbail ipadm_status_t status = IPADM_SUCCESS; 1946e91bba0SGirish Moodalbail ipmgmt_getif_arg_t getif; 1956e91bba0SGirish Moodalbail ipmgmt_getif_rval_t *rvalp; 1966e91bba0SGirish Moodalbail ipadm_if_info_t *ifp, *curr, *prev = NULL; 1976e91bba0SGirish Moodalbail int i = 0, err = 0; 1986e91bba0SGirish Moodalbail 1996e91bba0SGirish Moodalbail bzero(&getif, sizeof (getif)); 2006e91bba0SGirish Moodalbail if (ifname != NULL) 2016e91bba0SGirish Moodalbail (void) strlcpy(getif.ia_ifname, ifname, LIFNAMSIZ); 2026e91bba0SGirish Moodalbail getif.ia_cmd = IPMGMT_CMD_GETIF; 2036e91bba0SGirish Moodalbail 2046e91bba0SGirish Moodalbail *if_info = NULL; 2056e91bba0SGirish Moodalbail 2066e91bba0SGirish Moodalbail if ((rvalp = malloc(sizeof (ipmgmt_getif_rval_t))) == NULL) 2076e91bba0SGirish Moodalbail return (ipadm_errno2status(errno)); 2086e91bba0SGirish Moodalbail err = ipadm_door_call(iph, &getif, sizeof (getif), (void **)&rvalp, 2096e91bba0SGirish Moodalbail sizeof (*rvalp), B_TRUE); 2106e91bba0SGirish Moodalbail if (err == ENOENT) { 2116e91bba0SGirish Moodalbail free(rvalp); 2126e91bba0SGirish Moodalbail if (ifname != NULL) 2136e91bba0SGirish Moodalbail return (ipadm_errno2status(err)); 2146e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 2156e91bba0SGirish Moodalbail } else if (err != 0) { 2166e91bba0SGirish Moodalbail free(rvalp); 2176e91bba0SGirish Moodalbail return (ipadm_errno2status(err)); 2186e91bba0SGirish Moodalbail } 2196e91bba0SGirish Moodalbail 2206e91bba0SGirish Moodalbail ifp = rvalp->ir_ifinfo; 2216e91bba0SGirish Moodalbail for (i = 0; i < rvalp->ir_ifcnt; i++) { 2226e91bba0SGirish Moodalbail ifp = rvalp->ir_ifinfo + i; 2236e91bba0SGirish Moodalbail if ((curr = malloc(sizeof (*curr))) == NULL) { 2246e91bba0SGirish Moodalbail status = ipadm_errno2status(errno); 2256e91bba0SGirish Moodalbail ipadm_free_if_info(prev); 2266e91bba0SGirish Moodalbail break; 2276e91bba0SGirish Moodalbail } 2286e91bba0SGirish Moodalbail (void) bcopy(ifp, curr, sizeof (*curr)); 2296e91bba0SGirish Moodalbail curr->ifi_next = prev; 2306e91bba0SGirish Moodalbail prev = curr; 2316e91bba0SGirish Moodalbail } 2326e91bba0SGirish Moodalbail *if_info = curr; 2336e91bba0SGirish Moodalbail free(rvalp); 2346e91bba0SGirish Moodalbail return (status); 2356e91bba0SGirish Moodalbail } 2366e91bba0SGirish Moodalbail 2376e91bba0SGirish Moodalbail /* 2386e91bba0SGirish Moodalbail * Collects information for `ifname' if one is specified from both 2396e91bba0SGirish Moodalbail * active and persistent config in `if_info'. If no `ifname' is specified, 2406e91bba0SGirish Moodalbail * this returns all the interfaces in active and persistent config in 2416e91bba0SGirish Moodalbail * `if_info'. 2426e91bba0SGirish Moodalbail */ 2436e91bba0SGirish Moodalbail ipadm_status_t 2446e91bba0SGirish Moodalbail i_ipadm_get_all_if_info(ipadm_handle_t iph, const char *ifname, 2456e91bba0SGirish Moodalbail ipadm_if_info_t **if_info, int64_t lifc_flags) 2466e91bba0SGirish Moodalbail { 2476e91bba0SGirish Moodalbail ipadm_status_t status; 2486e91bba0SGirish Moodalbail ipadm_if_info_t *aifinfo = NULL; 2496e91bba0SGirish Moodalbail ipadm_if_info_t *pifinfo = NULL; 2506e91bba0SGirish Moodalbail ipadm_if_info_t *aifp; 2516e91bba0SGirish Moodalbail ipadm_if_info_t *pifp; 2526e91bba0SGirish Moodalbail ipadm_if_info_t *last = NULL; 2536e91bba0SGirish Moodalbail struct ifaddrs *ifa; 2546e91bba0SGirish Moodalbail struct ifaddrs *ifap; 2556e91bba0SGirish Moodalbail 2566e91bba0SGirish Moodalbail /* 2576e91bba0SGirish Moodalbail * Retrive the information for the requested `ifname' or all 2586e91bba0SGirish Moodalbail * interfaces from active configuration. 2596e91bba0SGirish Moodalbail */ 2606e91bba0SGirish Moodalbail retry: 2616e91bba0SGirish Moodalbail status = i_ipadm_active_if_info(iph, ifname, &aifinfo, lifc_flags); 2626e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 2636e91bba0SGirish Moodalbail return (status); 2646e91bba0SGirish Moodalbail /* Get the interface state for each interface in `aifinfo'. */ 2656e91bba0SGirish Moodalbail if (aifinfo != NULL) { 2666e91bba0SGirish Moodalbail /* We need all addresses to get the interface state */ 2676e91bba0SGirish Moodalbail if (getallifaddrs(AF_UNSPEC, &ifa, (LIFC_NOXMIT|LIFC_TEMPORARY| 2686e91bba0SGirish Moodalbail LIFC_ALLZONES|LIFC_UNDER_IPMP)) != 0) { 2696e91bba0SGirish Moodalbail status = ipadm_errno2status(errno); 2706e91bba0SGirish Moodalbail goto fail; 2716e91bba0SGirish Moodalbail } 2726e91bba0SGirish Moodalbail for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) { 2736e91bba0SGirish Moodalbail /* 2746e91bba0SGirish Moodalbail * Find the `ifaddrs' structure from `ifa' 2756e91bba0SGirish Moodalbail * for this interface. We need the IFF_* flags 2766e91bba0SGirish Moodalbail * to find the interface state. 2776e91bba0SGirish Moodalbail */ 2786e91bba0SGirish Moodalbail for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) { 2796e91bba0SGirish Moodalbail if (strcmp(ifap->ifa_name, aifp->ifi_name) == 0) 2806e91bba0SGirish Moodalbail break; 2816e91bba0SGirish Moodalbail } 2826e91bba0SGirish Moodalbail if (ifap == NULL) { 2836e91bba0SGirish Moodalbail /* 2846e91bba0SGirish Moodalbail * The interface might have been removed 2856e91bba0SGirish Moodalbail * from kernel. Retry getting all the active 2866e91bba0SGirish Moodalbail * interfaces. 2876e91bba0SGirish Moodalbail */ 2886e91bba0SGirish Moodalbail freeifaddrs(ifa); 2896e91bba0SGirish Moodalbail ipadm_free_if_info(aifinfo); 2906e91bba0SGirish Moodalbail aifinfo = NULL; 2916e91bba0SGirish Moodalbail goto retry; 2926e91bba0SGirish Moodalbail } 2936e91bba0SGirish Moodalbail if (!(ifap->ifa_flags & IFF_RUNNING) || 2946e91bba0SGirish Moodalbail (ifap->ifa_flags & IFF_FAILED)) 2956e91bba0SGirish Moodalbail aifp->ifi_state = IFIS_FAILED; 2966e91bba0SGirish Moodalbail else if (ifap->ifa_flags & IFF_OFFLINE) 2976e91bba0SGirish Moodalbail aifp->ifi_state = IFIS_OFFLINE; 2986e91bba0SGirish Moodalbail else if (i_ipadm_is_if_down(aifp->ifi_name, ifa)) 2996e91bba0SGirish Moodalbail aifp->ifi_state = IFIS_DOWN; 3006e91bba0SGirish Moodalbail else 3016e91bba0SGirish Moodalbail aifp->ifi_state = IFIS_OK; 3026e91bba0SGirish Moodalbail if (aifp->ifi_next == NULL) 3036e91bba0SGirish Moodalbail last = aifp; 3046e91bba0SGirish Moodalbail } 3056e91bba0SGirish Moodalbail freeifaddrs(ifa); 3066e91bba0SGirish Moodalbail } 3076e91bba0SGirish Moodalbail /* 3086e91bba0SGirish Moodalbail * Get the persistent interface information in `pifinfo'. 3096e91bba0SGirish Moodalbail */ 3106e91bba0SGirish Moodalbail status = i_ipadm_persist_if_info(iph, ifname, &pifinfo); 3116e91bba0SGirish Moodalbail if (status == IPADM_NOTFOUND) { 3126e91bba0SGirish Moodalbail *if_info = aifinfo; 3136e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 3146e91bba0SGirish Moodalbail } 3156e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 3166e91bba0SGirish Moodalbail goto fail; 3176e91bba0SGirish Moodalbail /* 3186e91bba0SGirish Moodalbail * If a persistent interface is also found in `aifinfo', update 3196e91bba0SGirish Moodalbail * its entry in `aifinfo' with the persistent information from 3206e91bba0SGirish Moodalbail * `pifinfo'. If an interface is found in `pifinfo', but not in 3216e91bba0SGirish Moodalbail * `aifinfo', it means that this interface was disabled. We should 3226e91bba0SGirish Moodalbail * add this interface to `aifinfo' and set it state to IFIF_DISABLED. 3236e91bba0SGirish Moodalbail */ 3246e91bba0SGirish Moodalbail for (pifp = pifinfo; pifp != NULL; pifp = pifp->ifi_next) { 3256e91bba0SGirish Moodalbail for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) { 3266e91bba0SGirish Moodalbail if (strcmp(aifp->ifi_name, pifp->ifi_name) == 0) { 3276e91bba0SGirish Moodalbail aifp->ifi_pflags = pifp->ifi_pflags; 3286e91bba0SGirish Moodalbail break; 3296e91bba0SGirish Moodalbail } 3306e91bba0SGirish Moodalbail } 3316e91bba0SGirish Moodalbail if (aifp == NULL) { 3326e91bba0SGirish Moodalbail aifp = malloc(sizeof (ipadm_if_info_t)); 3336e91bba0SGirish Moodalbail if (aifp == NULL) { 3346e91bba0SGirish Moodalbail status = ipadm_errno2status(errno); 3356e91bba0SGirish Moodalbail goto fail; 3366e91bba0SGirish Moodalbail } 3376e91bba0SGirish Moodalbail *aifp = *pifp; 3386e91bba0SGirish Moodalbail aifp->ifi_next = NULL; 3396e91bba0SGirish Moodalbail aifp->ifi_state = IFIS_DISABLED; 3406e91bba0SGirish Moodalbail if (last != NULL) 3416e91bba0SGirish Moodalbail last->ifi_next = aifp; 3426e91bba0SGirish Moodalbail else 3436e91bba0SGirish Moodalbail aifinfo = aifp; 3446e91bba0SGirish Moodalbail last = aifp; 3456e91bba0SGirish Moodalbail } 3466e91bba0SGirish Moodalbail } 3476e91bba0SGirish Moodalbail *if_info = aifinfo; 3486e91bba0SGirish Moodalbail ipadm_free_if_info(pifinfo); 3496e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 3506e91bba0SGirish Moodalbail fail: 3516e91bba0SGirish Moodalbail *if_info = NULL; 3526e91bba0SGirish Moodalbail ipadm_free_if_info(aifinfo); 3536e91bba0SGirish Moodalbail ipadm_free_if_info(pifinfo); 3546e91bba0SGirish Moodalbail return (status); 3556e91bba0SGirish Moodalbail } 3566e91bba0SGirish Moodalbail 3576e91bba0SGirish Moodalbail int 3586e91bba0SGirish Moodalbail i_ipadm_get_lnum(const char *ifname) 3596e91bba0SGirish Moodalbail { 3606e91bba0SGirish Moodalbail char *num = strrchr(ifname, IPADM_LOGICAL_SEP); 3616e91bba0SGirish Moodalbail 3626e91bba0SGirish Moodalbail if (num == NULL) 3636e91bba0SGirish Moodalbail return (0); 3646e91bba0SGirish Moodalbail 3656e91bba0SGirish Moodalbail return (atoi(++num)); 3666e91bba0SGirish Moodalbail } 3676e91bba0SGirish Moodalbail 3686e91bba0SGirish Moodalbail /* 3696e91bba0SGirish Moodalbail * Sets the output argument `exists' to true or false based on whether 3706e91bba0SGirish Moodalbail * any persistent configuration is available for `ifname' and returns 3716e91bba0SGirish Moodalbail * IPADM_SUCCESS as status. If the persistent information cannot be retrieved, 3726e91bba0SGirish Moodalbail * `exists' is unmodified and an error status is returned. 3736e91bba0SGirish Moodalbail */ 3746e91bba0SGirish Moodalbail ipadm_status_t 3756e91bba0SGirish Moodalbail i_ipadm_if_pexists(ipadm_handle_t iph, const char *ifname, sa_family_t af, 3766e91bba0SGirish Moodalbail boolean_t *exists) 3776e91bba0SGirish Moodalbail { 3786e91bba0SGirish Moodalbail ipadm_if_info_t *ifinfo; 3796e91bba0SGirish Moodalbail ipadm_status_t status; 3806e91bba0SGirish Moodalbail 381*550b6e40SSowmini Varadhan /* 382*550b6e40SSowmini Varadhan * if IPH_IPMGMTD is set, we know that the caller (ipmgmtd) already 383*550b6e40SSowmini Varadhan * knows about persistent configuration in the first place, so we 384*550b6e40SSowmini Varadhan * just return success. 385*550b6e40SSowmini Varadhan */ 386*550b6e40SSowmini Varadhan if (iph->iph_flags & IPH_IPMGMTD) { 387*550b6e40SSowmini Varadhan *exists = B_FALSE; 388*550b6e40SSowmini Varadhan return (IPADM_SUCCESS); 389*550b6e40SSowmini Varadhan } 3906e91bba0SGirish Moodalbail status = i_ipadm_persist_if_info(iph, ifname, &ifinfo); 3916e91bba0SGirish Moodalbail if (status == IPADM_SUCCESS) { 3926e91bba0SGirish Moodalbail *exists = ((af == AF_INET && 3936e91bba0SGirish Moodalbail (ifinfo->ifi_pflags & IFIF_IPV4)) || 3946e91bba0SGirish Moodalbail (af == AF_INET6 && 3956e91bba0SGirish Moodalbail (ifinfo->ifi_pflags & IFIF_IPV6))); 3966e91bba0SGirish Moodalbail free(ifinfo); 3976e91bba0SGirish Moodalbail } else if (status == IPADM_NOTFOUND) { 3986e91bba0SGirish Moodalbail status = IPADM_SUCCESS; 3996e91bba0SGirish Moodalbail *exists = B_FALSE; 4006e91bba0SGirish Moodalbail } 4016e91bba0SGirish Moodalbail return (status); 4026e91bba0SGirish Moodalbail } 4036e91bba0SGirish Moodalbail 4046e91bba0SGirish Moodalbail /* 4056e91bba0SGirish Moodalbail * Open "/dev/udp{,6}" for use as a multiplexor to PLINK the interface stream 4066e91bba0SGirish Moodalbail * under. We use "/dev/udp" instead of "/dev/ip" since STREAMS will not let 4076e91bba0SGirish Moodalbail * you PLINK a driver under itself, and "/dev/ip" is typically the driver at 4086e91bba0SGirish Moodalbail * the bottom of the stream for tunneling interfaces. 4096e91bba0SGirish Moodalbail */ 4106e91bba0SGirish Moodalbail ipadm_status_t 4116e91bba0SGirish Moodalbail ipadm_open_arp_on_udp(const char *udp_dev_name, int *fd) 4126e91bba0SGirish Moodalbail { 4136e91bba0SGirish Moodalbail int err; 4146e91bba0SGirish Moodalbail 4156e91bba0SGirish Moodalbail if ((*fd = open(udp_dev_name, O_RDWR)) == -1) 4166e91bba0SGirish Moodalbail return (ipadm_errno2status(errno)); 4176e91bba0SGirish Moodalbail 4186e91bba0SGirish Moodalbail /* 4196e91bba0SGirish Moodalbail * Pop off all undesired modules (note that the user may have 4206e91bba0SGirish Moodalbail * configured autopush to add modules above udp), and push the 4216e91bba0SGirish Moodalbail * arp module onto the resulting stream. This is used to make 4226e91bba0SGirish Moodalbail * IP+ARP be able to atomically track the muxid for the I_PLINKed 4236e91bba0SGirish Moodalbail * STREAMS, thus it isn't related to ARP running the ARP protocol. 4246e91bba0SGirish Moodalbail */ 4256e91bba0SGirish Moodalbail while (ioctl(*fd, I_POP, 0) != -1) 4266e91bba0SGirish Moodalbail ; 4276e91bba0SGirish Moodalbail if (errno == EINVAL && ioctl(*fd, I_PUSH, ARP_MOD_NAME) != -1) 4286e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 4296e91bba0SGirish Moodalbail err = errno; 4306e91bba0SGirish Moodalbail (void) close(*fd); 4316e91bba0SGirish Moodalbail 4326e91bba0SGirish Moodalbail return (ipadm_errno2status(err)); 4336e91bba0SGirish Moodalbail } 4346e91bba0SGirish Moodalbail 4356e91bba0SGirish Moodalbail /* 4366e91bba0SGirish Moodalbail * i_ipadm_create_ipmp() is called from i_ipadm_create_ipmp_peer() when an 4376e91bba0SGirish Moodalbail * underlying interface in an ipmp group G is plumbed for an address family, 4386e91bba0SGirish Moodalbail * but the meta-interface for the other address family `af' does not exist 4396e91bba0SGirish Moodalbail * yet for the group G. If `af' is IPv6, we need to bring up the 4406e91bba0SGirish Moodalbail * link-local address. 4416e91bba0SGirish Moodalbail */ 4426e91bba0SGirish Moodalbail static ipadm_status_t 4436e91bba0SGirish Moodalbail i_ipadm_create_ipmp(ipadm_handle_t iph, char *ifname, sa_family_t af, 4446e91bba0SGirish Moodalbail const char *grname, uint32_t ipadm_flags) 4456e91bba0SGirish Moodalbail { 4466e91bba0SGirish Moodalbail ipadm_status_t status; 4476e91bba0SGirish Moodalbail struct lifreq lifr; 4486e91bba0SGirish Moodalbail int sock; 4496e91bba0SGirish Moodalbail int err; 4506e91bba0SGirish Moodalbail 4516e91bba0SGirish Moodalbail assert(ipadm_flags & IPADM_OPT_IPMP); 4526e91bba0SGirish Moodalbail 4536e91bba0SGirish Moodalbail /* Create the ipmp underlying interface */ 4546e91bba0SGirish Moodalbail status = i_ipadm_create_if(iph, ifname, af, ipadm_flags); 4556e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS && status != IPADM_IF_EXISTS) 4566e91bba0SGirish Moodalbail return (status); 4576e91bba0SGirish Moodalbail 4586e91bba0SGirish Moodalbail /* 4596e91bba0SGirish Moodalbail * To preserve backward-compatibility, always bring up the link-local 4606e91bba0SGirish Moodalbail * address for implicitly-created IPv6 IPMP interfaces. 4616e91bba0SGirish Moodalbail */ 4626e91bba0SGirish Moodalbail if (af == AF_INET6) 4636e91bba0SGirish Moodalbail (void) i_ipadm_set_flags(iph, ifname, AF_INET6, IFF_UP, 0); 4646e91bba0SGirish Moodalbail 4656e91bba0SGirish Moodalbail sock = (af == AF_INET ? iph->iph_sock : iph->iph_sock6); 4666e91bba0SGirish Moodalbail /* 4676e91bba0SGirish Moodalbail * If the caller requested a different group name, issue a 4686e91bba0SGirish Moodalbail * SIOCSLIFGROUPNAME on the new IPMP interface. 4696e91bba0SGirish Moodalbail */ 4706e91bba0SGirish Moodalbail bzero(&lifr, sizeof (lifr)); 4716e91bba0SGirish Moodalbail (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); 4726e91bba0SGirish Moodalbail if (strcmp(lifr.lifr_name, grname) != 0) { 4736e91bba0SGirish Moodalbail (void) strlcpy(lifr.lifr_groupname, grname, LIFGRNAMSIZ); 4746e91bba0SGirish Moodalbail if (ioctl(sock, SIOCSLIFGROUPNAME, &lifr) == -1) { 4756e91bba0SGirish Moodalbail err = errno; 4766e91bba0SGirish Moodalbail /* Remove the interface we created. */ 4776e91bba0SGirish Moodalbail if (status == IPADM_SUCCESS) { 4786e91bba0SGirish Moodalbail (void) i_ipadm_delete_if(iph, ifname, af, 4796e91bba0SGirish Moodalbail ipadm_flags); 4806e91bba0SGirish Moodalbail } 4816e91bba0SGirish Moodalbail return (ipadm_errno2status(err)); 4826e91bba0SGirish Moodalbail } 4836e91bba0SGirish Moodalbail } 4846e91bba0SGirish Moodalbail 4856e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 4866e91bba0SGirish Moodalbail } 4876e91bba0SGirish Moodalbail 4886e91bba0SGirish Moodalbail /* 4896e91bba0SGirish Moodalbail * Checks if `ifname' is plumbed and in an IPMP group on its "other" address 4906e91bba0SGirish Moodalbail * family. If so, create a matching IPMP group for address family `af'. 4916e91bba0SGirish Moodalbail */ 4926e91bba0SGirish Moodalbail static ipadm_status_t 4936e91bba0SGirish Moodalbail i_ipadm_create_ipmp_peer(ipadm_handle_t iph, char *ifname, sa_family_t af) 4946e91bba0SGirish Moodalbail { 4956e91bba0SGirish Moodalbail lifgroupinfo_t lifgr; 4966e91bba0SGirish Moodalbail ipadm_status_t status = IPADM_SUCCESS; 4976e91bba0SGirish Moodalbail struct lifreq lifr; 4986e91bba0SGirish Moodalbail int other_af_sock; 4996e91bba0SGirish Moodalbail 5006e91bba0SGirish Moodalbail assert(af == AF_INET || af == AF_INET6); 5016e91bba0SGirish Moodalbail 5026e91bba0SGirish Moodalbail other_af_sock = (af == AF_INET ? iph->iph_sock6 : iph->iph_sock); 5036e91bba0SGirish Moodalbail 5046e91bba0SGirish Moodalbail /* 5056e91bba0SGirish Moodalbail * iph is the handle for the interface that we are trying to plumb. 5066e91bba0SGirish Moodalbail * other_af_sock is the socket for the "other" address family. 5076e91bba0SGirish Moodalbail */ 5086e91bba0SGirish Moodalbail bzero(&lifr, sizeof (lifr)); 5096e91bba0SGirish Moodalbail (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); 5106e91bba0SGirish Moodalbail if (ioctl(other_af_sock, SIOCGLIFGROUPNAME, &lifr) != 0) 5116e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 5126e91bba0SGirish Moodalbail 5136e91bba0SGirish Moodalbail (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname, LIFGRNAMSIZ); 5146e91bba0SGirish Moodalbail if (ioctl(other_af_sock, SIOCGLIFGROUPINFO, &lifgr) != 0) 5156e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 5166e91bba0SGirish Moodalbail 5176e91bba0SGirish Moodalbail /* 5186e91bba0SGirish Moodalbail * If `ifname' *is* the IPMP group interface, or if the relevant 5196e91bba0SGirish Moodalbail * address family is already configured, then there's nothing to do. 5206e91bba0SGirish Moodalbail */ 5216e91bba0SGirish Moodalbail if (strcmp(lifgr.gi_grifname, ifname) == 0 || 5226e91bba0SGirish Moodalbail (af == AF_INET && lifgr.gi_v4) || (af == AF_INET6 && lifgr.gi_v6)) { 5236e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 5246e91bba0SGirish Moodalbail } 5256e91bba0SGirish Moodalbail 5266e91bba0SGirish Moodalbail status = i_ipadm_create_ipmp(iph, lifgr.gi_grifname, af, 5276e91bba0SGirish Moodalbail lifgr.gi_grname, IPADM_OPT_ACTIVE|IPADM_OPT_IPMP); 5286e91bba0SGirish Moodalbail return (status); 5296e91bba0SGirish Moodalbail } 5306e91bba0SGirish Moodalbail 5316e91bba0SGirish Moodalbail /* 5326e91bba0SGirish Moodalbail * Issues the ioctl SIOCSLIFNAME to kernel on the given ARP stream fd. 5336e91bba0SGirish Moodalbail */ 5346e91bba0SGirish Moodalbail static ipadm_status_t 5356e91bba0SGirish Moodalbail i_ipadm_slifname_arp(char *ifname, uint64_t flags, int fd) 5366e91bba0SGirish Moodalbail { 5376e91bba0SGirish Moodalbail struct lifreq lifr; 5386e91bba0SGirish Moodalbail ifspec_t ifsp; 5396e91bba0SGirish Moodalbail 5406e91bba0SGirish Moodalbail bzero(&lifr, sizeof (lifr)); 5416e91bba0SGirish Moodalbail (void) ifparse_ifspec(ifname, &ifsp); 5426e91bba0SGirish Moodalbail lifr.lifr_ppa = ifsp.ifsp_ppa; 5436e91bba0SGirish Moodalbail lifr.lifr_flags = flags; 5446e91bba0SGirish Moodalbail (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); 5456e91bba0SGirish Moodalbail /* 5466e91bba0SGirish Moodalbail * Tell ARP the name and unit number for this interface. 5476e91bba0SGirish Moodalbail * Note that arp has no support for transparent ioctls. 5486e91bba0SGirish Moodalbail */ 5496e91bba0SGirish Moodalbail if (i_ipadm_strioctl(fd, SIOCSLIFNAME, (char *)&lifr, 5506e91bba0SGirish Moodalbail sizeof (lifr)) == -1) { 5516e91bba0SGirish Moodalbail return (ipadm_errno2status(errno)); 5526e91bba0SGirish Moodalbail } 5536e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 5546e91bba0SGirish Moodalbail } 5556e91bba0SGirish Moodalbail 5566e91bba0SGirish Moodalbail /* 5576e91bba0SGirish Moodalbail * Issues the ioctl SIOCSLIFNAME to kernel. If IPADM_OPT_GENPPA is set in 5586e91bba0SGirish Moodalbail * `ipadm_flags', then a ppa will be generated. `newif' will be updated 5596e91bba0SGirish Moodalbail * with the generated ppa. 5606e91bba0SGirish Moodalbail */ 5616e91bba0SGirish Moodalbail static ipadm_status_t 5626e91bba0SGirish Moodalbail i_ipadm_slifname(ipadm_handle_t iph, char *ifname, char *newif, uint64_t flags, 5636e91bba0SGirish Moodalbail int fd, uint32_t ipadm_flags) 5646e91bba0SGirish Moodalbail { 5656e91bba0SGirish Moodalbail struct lifreq lifr; 5666e91bba0SGirish Moodalbail ipadm_status_t status = IPADM_SUCCESS; 5676e91bba0SGirish Moodalbail int err = 0; 5686e91bba0SGirish Moodalbail sa_family_t af; 5696e91bba0SGirish Moodalbail int ppa; 5706e91bba0SGirish Moodalbail ifspec_t ifsp; 5716e91bba0SGirish Moodalbail boolean_t valid_if; 5726e91bba0SGirish Moodalbail 5736e91bba0SGirish Moodalbail bzero(&lifr, sizeof (lifr)); 5746e91bba0SGirish Moodalbail if (ipadm_flags & IPADM_OPT_GENPPA) { 5756e91bba0SGirish Moodalbail /* 5766e91bba0SGirish Moodalbail * We'd like to just set lifr_ppa to UINT_MAX and have the 5776e91bba0SGirish Moodalbail * kernel pick a PPA. Unfortunately, that would mishandle 5786e91bba0SGirish Moodalbail * two cases: 5796e91bba0SGirish Moodalbail * 5806e91bba0SGirish Moodalbail * 1. If the PPA is available but the groupname is taken 5816e91bba0SGirish Moodalbail * (e.g., the "ipmp2" IP interface name is available 5826e91bba0SGirish Moodalbail * but the "ipmp2" groupname is taken) then the 5836e91bba0SGirish Moodalbail * auto-assignment by the kernel will fail. 5846e91bba0SGirish Moodalbail * 5856e91bba0SGirish Moodalbail * 2. If we're creating (e.g.) an IPv6-only IPMP 5866e91bba0SGirish Moodalbail * interface, and there's already an IPv4-only IPMP 5876e91bba0SGirish Moodalbail * interface, the kernel will allow us to accidentally 5886e91bba0SGirish Moodalbail * reuse the IPv6 IPMP interface name (since 5896e91bba0SGirish Moodalbail * SIOCSLIFNAME uniqueness is per-interface-type). 5906e91bba0SGirish Moodalbail * This will cause administrative confusion. 5916e91bba0SGirish Moodalbail * 5926e91bba0SGirish Moodalbail * Thus, we instead take a brute-force approach of checking 5936e91bba0SGirish Moodalbail * whether the IPv4 or IPv6 name is already in-use before 5946e91bba0SGirish Moodalbail * attempting the SIOCSLIFNAME. As per (1) above, the 5956e91bba0SGirish Moodalbail * SIOCSLIFNAME may still fail, in which case we just proceed 5966e91bba0SGirish Moodalbail * to the next one. If this approach becomes too slow, we 5976e91bba0SGirish Moodalbail * can add a new SIOC* to handle this case in the kernel. 5986e91bba0SGirish Moodalbail */ 5996e91bba0SGirish Moodalbail for (ppa = 0; ppa < UINT_MAX; ppa++) { 6006e91bba0SGirish Moodalbail (void) snprintf(lifr.lifr_name, LIFNAMSIZ, "%s%d", 6016e91bba0SGirish Moodalbail ifname, ppa); 6026e91bba0SGirish Moodalbail 6036e91bba0SGirish Moodalbail if (ioctl(iph->iph_sock, SIOCGLIFFLAGS, &lifr) != -1 || 6046e91bba0SGirish Moodalbail errno != ENXIO) 6056e91bba0SGirish Moodalbail continue; 6066e91bba0SGirish Moodalbail 6076e91bba0SGirish Moodalbail if (ioctl(iph->iph_sock6, SIOCGLIFFLAGS, &lifr) != -1 || 6086e91bba0SGirish Moodalbail errno != ENXIO) 6096e91bba0SGirish Moodalbail continue; 6106e91bba0SGirish Moodalbail 6116e91bba0SGirish Moodalbail lifr.lifr_ppa = ppa; 6126e91bba0SGirish Moodalbail lifr.lifr_flags = flags; 6136e91bba0SGirish Moodalbail 6146e91bba0SGirish Moodalbail err = ioctl(fd, SIOCSLIFNAME, &lifr); 6156e91bba0SGirish Moodalbail if (err != -1 || errno != EEXIST) 6166e91bba0SGirish Moodalbail break; 6176e91bba0SGirish Moodalbail } 6186e91bba0SGirish Moodalbail if (err == -1) { 6196e91bba0SGirish Moodalbail status = ipadm_errno2status(errno); 6206e91bba0SGirish Moodalbail } else { 6216e91bba0SGirish Moodalbail /* 6226e91bba0SGirish Moodalbail * PPA has been successfully established. 6236e91bba0SGirish Moodalbail * Update `newif' with the ppa. 6246e91bba0SGirish Moodalbail */ 6256e91bba0SGirish Moodalbail assert(newif != NULL); 6266e91bba0SGirish Moodalbail if (snprintf(newif, LIFNAMSIZ, "%s%d", ifname, 6276e91bba0SGirish Moodalbail ppa) >= LIFNAMSIZ) 6286e91bba0SGirish Moodalbail return (IPADM_INVALID_ARG); 6296e91bba0SGirish Moodalbail } 6306e91bba0SGirish Moodalbail } else { 6316e91bba0SGirish Moodalbail /* We should have already validated the interface name. */ 6326e91bba0SGirish Moodalbail valid_if = ifparse_ifspec(ifname, &ifsp); 6336e91bba0SGirish Moodalbail assert(valid_if); 6346e91bba0SGirish Moodalbail 6356e91bba0SGirish Moodalbail /* 6366e91bba0SGirish Moodalbail * Before we call SIOCSLIFNAME, ensure that the IPMP group 6376e91bba0SGirish Moodalbail * interface for this address family exists. Otherwise, the 6386e91bba0SGirish Moodalbail * kernel will kick the interface out of the group when we do 6396e91bba0SGirish Moodalbail * the SIOCSLIFNAME. 6406e91bba0SGirish Moodalbail * 6416e91bba0SGirish Moodalbail * Example: suppose bge0 is plumbed for IPv4 and in group "a". 6426e91bba0SGirish Moodalbail * If we're now plumbing bge0 for IPv6, but the IPMP group 6436e91bba0SGirish Moodalbail * interface for "a" is not plumbed for IPv6, the SIOCSLIFNAME 6446e91bba0SGirish Moodalbail * will kick bge0 out of group "a", which is undesired. 6456e91bba0SGirish Moodalbail */ 6466e91bba0SGirish Moodalbail if (flags & IFF_IPV4) 6476e91bba0SGirish Moodalbail af = AF_INET; 6486e91bba0SGirish Moodalbail else 6496e91bba0SGirish Moodalbail af = AF_INET6; 6506e91bba0SGirish Moodalbail status = i_ipadm_create_ipmp_peer(iph, ifname, af); 6516e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 6526e91bba0SGirish Moodalbail return (status); 6536e91bba0SGirish Moodalbail lifr.lifr_ppa = ifsp.ifsp_ppa; 6546e91bba0SGirish Moodalbail lifr.lifr_flags = flags; 6556e91bba0SGirish Moodalbail (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); 6566e91bba0SGirish Moodalbail if (ioctl(fd, SIOCSLIFNAME, &lifr) == -1) 6576e91bba0SGirish Moodalbail status = ipadm_errno2status(errno); 6586e91bba0SGirish Moodalbail } 6596e91bba0SGirish Moodalbail 6606e91bba0SGirish Moodalbail return (status); 6616e91bba0SGirish Moodalbail } 6626e91bba0SGirish Moodalbail 6636e91bba0SGirish Moodalbail /* 6646e91bba0SGirish Moodalbail * Plumbs the interface `ifname' for the address family `af'. It also persists 6656e91bba0SGirish Moodalbail * the interface for `af' if IPADM_OPT_PERSIST is set in `ipadm_flags'. 6666e91bba0SGirish Moodalbail */ 6676e91bba0SGirish Moodalbail ipadm_status_t 6686e91bba0SGirish Moodalbail i_ipadm_plumb_if(ipadm_handle_t iph, char *ifname, sa_family_t af, 6696e91bba0SGirish Moodalbail uint32_t ipadm_flags) 6706e91bba0SGirish Moodalbail { 6716e91bba0SGirish Moodalbail int ip_muxid; 6726e91bba0SGirish Moodalbail int mux_fd = -1, ip_fd, arp_fd; 6736e91bba0SGirish Moodalbail char *udp_dev_name; 6746e91bba0SGirish Moodalbail dlpi_handle_t dh_arp = NULL, dh_ip; 6756e91bba0SGirish Moodalbail uint64_t ifflags; 6766e91bba0SGirish Moodalbail struct lifreq lifr; 6776e91bba0SGirish Moodalbail uint_t dlpi_flags; 6786e91bba0SGirish Moodalbail ipadm_status_t status = IPADM_SUCCESS; 6796e91bba0SGirish Moodalbail char *linkname; 6806e91bba0SGirish Moodalbail boolean_t legacy = (iph->iph_flags & IPH_LEGACY); 6816e91bba0SGirish Moodalbail zoneid_t zoneid; 6826e91bba0SGirish Moodalbail char newif[LIFNAMSIZ]; 6836e91bba0SGirish Moodalbail char lifname[LIFNAMSIZ]; 6846e91bba0SGirish Moodalbail datalink_id_t linkid; 6856e91bba0SGirish Moodalbail int sock; 6866e91bba0SGirish Moodalbail boolean_t islo; 6876e91bba0SGirish Moodalbail boolean_t is_persistent = 6886e91bba0SGirish Moodalbail ((ipadm_flags & IPADM_OPT_PERSIST) != 0); 6896e91bba0SGirish Moodalbail uint32_t dlflags; 6906e91bba0SGirish Moodalbail dladm_status_t dlstatus; 6916e91bba0SGirish Moodalbail 6926e91bba0SGirish Moodalbail if (iph->iph_dlh != NULL) { 6936e91bba0SGirish Moodalbail dlstatus = dladm_name2info(iph->iph_dlh, ifname, &linkid, 6946e91bba0SGirish Moodalbail &dlflags, NULL, NULL); 6956e91bba0SGirish Moodalbail } 6966e91bba0SGirish Moodalbail /* 6976e91bba0SGirish Moodalbail * If we're in the global zone and we're plumbing a datalink, make 6986e91bba0SGirish Moodalbail * sure that the datalink is not assigned to a non-global zone. Note 6996e91bba0SGirish Moodalbail * that the non-global zones don't need this check, because zoneadm 7006e91bba0SGirish Moodalbail * has taken care of this when the zones boot. 7016e91bba0SGirish Moodalbail */ 702*550b6e40SSowmini Varadhan if (iph->iph_zoneid == GLOBAL_ZONEID && dlstatus == DLADM_STATUS_OK) { 7036e91bba0SGirish Moodalbail zoneid = ALL_ZONES; 7046e91bba0SGirish Moodalbail if (zone_check_datalink(&zoneid, linkid) == 0) { 7056e91bba0SGirish Moodalbail /* interface is in use by a non-global zone. */ 7066e91bba0SGirish Moodalbail return (IPADM_IF_INUSE); 7076e91bba0SGirish Moodalbail } 7086e91bba0SGirish Moodalbail } 7096e91bba0SGirish Moodalbail 7106e91bba0SGirish Moodalbail /* loopback interfaces are just added as logical interface */ 7116e91bba0SGirish Moodalbail bzero(&lifr, sizeof (lifr)); 7126e91bba0SGirish Moodalbail islo = i_ipadm_is_loopback(ifname); 7136e91bba0SGirish Moodalbail if (islo || i_ipadm_get_lnum(ifname) != 0) { 7146e91bba0SGirish Moodalbail (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); 7156e91bba0SGirish Moodalbail if (af == AF_INET) 7166e91bba0SGirish Moodalbail sock = iph->iph_sock; 7176e91bba0SGirish Moodalbail else 7186e91bba0SGirish Moodalbail sock = iph->iph_sock6; 7196e91bba0SGirish Moodalbail if (islo && ioctl(sock, SIOCGLIFADDR, (caddr_t)&lifr) >= 0) 7206e91bba0SGirish Moodalbail return (IPADM_IF_EXISTS); 7216e91bba0SGirish Moodalbail if (ioctl(sock, SIOCLIFADDIF, (caddr_t)&lifr) < 0) 7226e91bba0SGirish Moodalbail return (ipadm_errno2status(errno)); 7236e91bba0SGirish Moodalbail 7246e91bba0SGirish Moodalbail /* 7256e91bba0SGirish Moodalbail * By default, kernel configures 127.0.0.1 on the loopback 7266e91bba0SGirish Moodalbail * interface. Replace this with 0.0.0.0 to be consistent 7276e91bba0SGirish Moodalbail * with interface creation on other physical interfaces. 7286e91bba0SGirish Moodalbail */ 7296e91bba0SGirish Moodalbail if (islo && !legacy) { 7306e91bba0SGirish Moodalbail bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr)); 7316e91bba0SGirish Moodalbail lifr.lifr_addr.ss_family = af; 7326e91bba0SGirish Moodalbail if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0) 7336e91bba0SGirish Moodalbail return (ipadm_errno2status(errno)); 7346e91bba0SGirish Moodalbail if (is_persistent) { 7356e91bba0SGirish Moodalbail status = i_ipadm_persist_if(iph, ifname, af); 7366e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) { 7376e91bba0SGirish Moodalbail (void) i_ipadm_delete_if(iph, ifname, 7386e91bba0SGirish Moodalbail af, IPADM_OPT_ACTIVE); 7396e91bba0SGirish Moodalbail } 7406e91bba0SGirish Moodalbail } 7416e91bba0SGirish Moodalbail } 7426e91bba0SGirish Moodalbail return (status); 7436e91bba0SGirish Moodalbail } 7446e91bba0SGirish Moodalbail 7456e91bba0SGirish Moodalbail dlpi_flags = DLPI_NOATTACH; 7466e91bba0SGirish Moodalbail 7476e91bba0SGirish Moodalbail /* 7486e91bba0SGirish Moodalbail * If IPADM_OPT_IPMP is specified, then this is a request 7496e91bba0SGirish Moodalbail * to create an IPMP interface atop /dev/ipmpstub0. (We can't simply 7506e91bba0SGirish Moodalbail * pass "ipmpstub0" as devname since an admin *could* have a normal 7516e91bba0SGirish Moodalbail * vanity-named link named "ipmpstub0" that they'd like to plumb.) 7526e91bba0SGirish Moodalbail */ 7536e91bba0SGirish Moodalbail if (ipadm_flags & IPADM_OPT_IPMP) { 7546e91bba0SGirish Moodalbail dlpi_flags |= DLPI_DEVONLY; 7556e91bba0SGirish Moodalbail linkname = "ipmpstub0"; 7566e91bba0SGirish Moodalbail } else { 7576e91bba0SGirish Moodalbail /* 7586e91bba0SGirish Moodalbail * Verify that the user is not creating a persistent 7596e91bba0SGirish Moodalbail * IP interface on a non-persistent data-link. 7606e91bba0SGirish Moodalbail */ 7616e91bba0SGirish Moodalbail if (!i_ipadm_is_vni(ifname) && dlstatus == DLADM_STATUS_OK && 7626e91bba0SGirish Moodalbail is_persistent && !(dlflags & DLADM_OPT_PERSIST)) { 7636e91bba0SGirish Moodalbail return (IPADM_TEMPORARY_OBJ); 7646e91bba0SGirish Moodalbail } 7656e91bba0SGirish Moodalbail linkname = ifname; 7666e91bba0SGirish Moodalbail } 7676e91bba0SGirish Moodalbail 7686e91bba0SGirish Moodalbail /* 7696e91bba0SGirish Moodalbail * We use DLPI_NOATTACH because the ip module will do the attach 7706e91bba0SGirish Moodalbail * itself for DLPI style-2 devices. 7716e91bba0SGirish Moodalbail */ 7726e91bba0SGirish Moodalbail if (dlpi_open(linkname, &dh_ip, dlpi_flags) != DLPI_SUCCESS) 7736e91bba0SGirish Moodalbail return (IPADM_DLPI_FAILURE); 7746e91bba0SGirish Moodalbail ip_fd = dlpi_fd(dh_ip); 7756e91bba0SGirish Moodalbail if (ioctl(ip_fd, I_PUSH, IP_MOD_NAME) == -1) { 7766e91bba0SGirish Moodalbail status = ipadm_errno2status(errno); 7776e91bba0SGirish Moodalbail goto done; 7786e91bba0SGirish Moodalbail } 7796e91bba0SGirish Moodalbail 7806e91bba0SGirish Moodalbail /* 7816e91bba0SGirish Moodalbail * Set IFF_IPV4/IFF_IPV6 flags. The kernel only allows modifications 7826e91bba0SGirish Moodalbail * to IFF_IPv4, IFF_IPV6, IFF_BROADCAST, IFF_XRESOLV, IFF_NOLINKLOCAL. 7836e91bba0SGirish Moodalbail */ 7846e91bba0SGirish Moodalbail ifflags = 0; 7856e91bba0SGirish Moodalbail 7866e91bba0SGirish Moodalbail /* Set the name string and the IFF_IPV* flag */ 7876e91bba0SGirish Moodalbail if (af == AF_INET) { 7886e91bba0SGirish Moodalbail ifflags = IFF_IPV4; 7896e91bba0SGirish Moodalbail } else { 7906e91bba0SGirish Moodalbail ifflags = IFF_IPV6; 7916e91bba0SGirish Moodalbail /* 7926e91bba0SGirish Moodalbail * With the legacy method, the link-local address should be 7936e91bba0SGirish Moodalbail * configured as part of the interface plumb, using the default 7946e91bba0SGirish Moodalbail * token. If IPH_LEGACY is not specified, we want to set :: as 7956e91bba0SGirish Moodalbail * the address and require the admin to explicitly call 7966e91bba0SGirish Moodalbail * ipadm_create_addr() with the address object type set to 7976e91bba0SGirish Moodalbail * IPADM_ADDR_IPV6_ADDRCONF to create the link-local address 7986e91bba0SGirish Moodalbail * as well as the autoconfigured addresses. 7996e91bba0SGirish Moodalbail */ 8006e91bba0SGirish Moodalbail if (!legacy && !i_ipadm_is_6to4(iph, ifname)) 8016e91bba0SGirish Moodalbail ifflags |= IFF_NOLINKLOCAL; 8026e91bba0SGirish Moodalbail } 8036e91bba0SGirish Moodalbail (void) strlcpy(newif, ifname, sizeof (newif)); 8046e91bba0SGirish Moodalbail status = i_ipadm_slifname(iph, ifname, newif, ifflags, ip_fd, 8056e91bba0SGirish Moodalbail ipadm_flags); 8066e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 8076e91bba0SGirish Moodalbail goto done; 8086e91bba0SGirish Moodalbail 8096e91bba0SGirish Moodalbail /* Get the full set of existing flags for this stream */ 8106e91bba0SGirish Moodalbail status = i_ipadm_get_flags(iph, newif, af, &ifflags); 8116e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 8126e91bba0SGirish Moodalbail goto done; 8136e91bba0SGirish Moodalbail 8146e91bba0SGirish Moodalbail udp_dev_name = (af == AF_INET6 ? UDP6_DEV_NAME : UDP_DEV_NAME); 8156e91bba0SGirish Moodalbail status = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd); 8166e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 8176e91bba0SGirish Moodalbail goto done; 8186e91bba0SGirish Moodalbail 8196e91bba0SGirish Moodalbail /* Check if arp is not needed */ 8206e91bba0SGirish Moodalbail if (ifflags & (IFF_NOARP|IFF_IPV6)) { 8216e91bba0SGirish Moodalbail /* 8226e91bba0SGirish Moodalbail * PLINK the interface stream so that the application can exit 8236e91bba0SGirish Moodalbail * without tearing down the stream. 8246e91bba0SGirish Moodalbail */ 8256e91bba0SGirish Moodalbail if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) 8266e91bba0SGirish Moodalbail status = ipadm_errno2status(errno); 8276e91bba0SGirish Moodalbail goto done; 8286e91bba0SGirish Moodalbail } 8296e91bba0SGirish Moodalbail 8306e91bba0SGirish Moodalbail /* 8316e91bba0SGirish Moodalbail * This interface does use ARP, so set up a separate stream 8326e91bba0SGirish Moodalbail * from the interface to ARP. 8336e91bba0SGirish Moodalbail * 8346e91bba0SGirish Moodalbail * We use DLPI_NOATTACH because the arp module will do the attach 8356e91bba0SGirish Moodalbail * itself for DLPI style-2 devices. 8366e91bba0SGirish Moodalbail */ 8376e91bba0SGirish Moodalbail if (dlpi_open(linkname, &dh_arp, dlpi_flags) != DLPI_SUCCESS) { 8386e91bba0SGirish Moodalbail status = IPADM_DLPI_FAILURE; 8396e91bba0SGirish Moodalbail goto done; 8406e91bba0SGirish Moodalbail } 8416e91bba0SGirish Moodalbail 8426e91bba0SGirish Moodalbail arp_fd = dlpi_fd(dh_arp); 8436e91bba0SGirish Moodalbail if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1) { 8446e91bba0SGirish Moodalbail status = ipadm_errno2status(errno); 8456e91bba0SGirish Moodalbail goto done; 8466e91bba0SGirish Moodalbail } 8476e91bba0SGirish Moodalbail 8486e91bba0SGirish Moodalbail status = i_ipadm_slifname_arp(newif, ifflags, arp_fd); 8496e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 8506e91bba0SGirish Moodalbail goto done; 8516e91bba0SGirish Moodalbail /* 8526e91bba0SGirish Moodalbail * PLINK the IP and ARP streams so that ifconfig can exit 8536e91bba0SGirish Moodalbail * without tearing down the stream. 8546e91bba0SGirish Moodalbail */ 8556e91bba0SGirish Moodalbail if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) { 8566e91bba0SGirish Moodalbail status = ipadm_errno2status(errno); 8576e91bba0SGirish Moodalbail goto done; 8586e91bba0SGirish Moodalbail } 8596e91bba0SGirish Moodalbail 8606e91bba0SGirish Moodalbail if (ioctl(mux_fd, I_PLINK, arp_fd) < 0) { 8616e91bba0SGirish Moodalbail status = ipadm_errno2status(errno); 8626e91bba0SGirish Moodalbail (void) ioctl(mux_fd, I_PUNLINK, ip_muxid); 8636e91bba0SGirish Moodalbail } 8646e91bba0SGirish Moodalbail 8656e91bba0SGirish Moodalbail done: 8666e91bba0SGirish Moodalbail dlpi_close(dh_ip); 8676e91bba0SGirish Moodalbail if (dh_arp != NULL) 8686e91bba0SGirish Moodalbail dlpi_close(dh_arp); 8696e91bba0SGirish Moodalbail 8706e91bba0SGirish Moodalbail if (mux_fd != -1) 8716e91bba0SGirish Moodalbail (void) close(mux_fd); 8726e91bba0SGirish Moodalbail 8736e91bba0SGirish Moodalbail if (status == IPADM_SUCCESS) { 8746e91bba0SGirish Moodalbail /* copy back new ifname */ 8756e91bba0SGirish Moodalbail (void) strlcpy(ifname, newif, LIFNAMSIZ); 8766e91bba0SGirish Moodalbail /* 8776e91bba0SGirish Moodalbail * If it is a 6to4 tunnel, create a default 8786e91bba0SGirish Moodalbail * addrobj name for the default address on the 0'th 8796e91bba0SGirish Moodalbail * logical interface and set IFF_UP in the interface flags. 8806e91bba0SGirish Moodalbail */ 8816e91bba0SGirish Moodalbail if (i_ipadm_is_6to4(iph, ifname)) { 8826e91bba0SGirish Moodalbail struct ipadm_addrobj_s addr; 8836e91bba0SGirish Moodalbail 8846e91bba0SGirish Moodalbail i_ipadm_init_addr(&addr, ifname, "", IPADM_ADDR_STATIC); 8856e91bba0SGirish Moodalbail addr.ipadm_af = af; 8866e91bba0SGirish Moodalbail status = i_ipadm_lookupadd_addrobj(iph, &addr); 8876e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 8886e91bba0SGirish Moodalbail return (status); 8896e91bba0SGirish Moodalbail status = ipadm_add_aobjname(iph, ifname, 8906e91bba0SGirish Moodalbail af, addr.ipadm_aobjname, IPADM_ADDR_STATIC, 0); 8916e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 8926e91bba0SGirish Moodalbail return (status); 8936e91bba0SGirish Moodalbail addr.ipadm_lifnum = 0; 8946e91bba0SGirish Moodalbail i_ipadm_addrobj2lifname(&addr, lifname, 8956e91bba0SGirish Moodalbail sizeof (lifname)); 8966e91bba0SGirish Moodalbail status = i_ipadm_set_flags(iph, lifname, af, 8976e91bba0SGirish Moodalbail IFF_UP, 0); 8986e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 8996e91bba0SGirish Moodalbail return (status); 9006e91bba0SGirish Moodalbail } else { 9016e91bba0SGirish Moodalbail /* 9026e91bba0SGirish Moodalbail * Prevent static IPv6 addresses from triggering 9036e91bba0SGirish Moodalbail * autoconf. This does not have to be done for 9046e91bba0SGirish Moodalbail * 6to4 tunnel interfaces, since in.ndpd will 9056e91bba0SGirish Moodalbail * not autoconfigure those interfaces. 9066e91bba0SGirish Moodalbail */ 9076e91bba0SGirish Moodalbail if (af == AF_INET6 && !legacy) 9086e91bba0SGirish Moodalbail (void) i_ipadm_disable_autoconf(newif); 9096e91bba0SGirish Moodalbail } 9106e91bba0SGirish Moodalbail 9116e91bba0SGirish Moodalbail /* 9126e91bba0SGirish Moodalbail * If IPADM_OPT_PERSIST was set in flags, store the 9136e91bba0SGirish Moodalbail * interface in persistent DB. 9146e91bba0SGirish Moodalbail */ 9156e91bba0SGirish Moodalbail if (is_persistent) { 9166e91bba0SGirish Moodalbail status = i_ipadm_persist_if(iph, newif, af); 9176e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) { 9186e91bba0SGirish Moodalbail (void) i_ipadm_delete_if(iph, newif, af, 9196e91bba0SGirish Moodalbail IPADM_OPT_ACTIVE); 9206e91bba0SGirish Moodalbail } 9216e91bba0SGirish Moodalbail } 9226e91bba0SGirish Moodalbail } 9236e91bba0SGirish Moodalbail if (status == IPADM_EXISTS) 9246e91bba0SGirish Moodalbail status = IPADM_IF_EXISTS; 9256e91bba0SGirish Moodalbail return (status); 9266e91bba0SGirish Moodalbail } 9276e91bba0SGirish Moodalbail 9286e91bba0SGirish Moodalbail /* 9296e91bba0SGirish Moodalbail * Unplumbs the interface in `ifname' of family `af'. 9306e91bba0SGirish Moodalbail */ 9316e91bba0SGirish Moodalbail ipadm_status_t 9326e91bba0SGirish Moodalbail i_ipadm_unplumb_if(ipadm_handle_t iph, const char *ifname, sa_family_t af) 9336e91bba0SGirish Moodalbail { 9346e91bba0SGirish Moodalbail int ip_muxid, arp_muxid; 9356e91bba0SGirish Moodalbail int mux_fd = -1; 9366e91bba0SGirish Moodalbail int muxid_fd = -1; 9376e91bba0SGirish Moodalbail char *udp_dev_name; 9386e91bba0SGirish Moodalbail uint64_t flags; 9396e91bba0SGirish Moodalbail boolean_t changed_arp_muxid = B_FALSE; 9406e91bba0SGirish Moodalbail int save_errno; 9416e91bba0SGirish Moodalbail struct lifreq lifr; 9426e91bba0SGirish Moodalbail ipadm_status_t ret = IPADM_SUCCESS; 9436e91bba0SGirish Moodalbail int sock; 9446e91bba0SGirish Moodalbail lifgroupinfo_t lifgr; 9456e91bba0SGirish Moodalbail ifaddrlistx_t *ifaddrs, *ifaddrp; 9466e91bba0SGirish Moodalbail boolean_t v6 = (af == AF_INET6); 9476e91bba0SGirish Moodalbail 9486e91bba0SGirish Moodalbail /* Just do SIOCLIFREMOVEIF on loopback interfaces */ 9496e91bba0SGirish Moodalbail bzero(&lifr, sizeof (lifr)); 9506e91bba0SGirish Moodalbail if (i_ipadm_is_loopback(ifname) || 9516e91bba0SGirish Moodalbail (i_ipadm_get_lnum(ifname) != 0 && (iph->iph_flags & IPH_LEGACY))) { 9526e91bba0SGirish Moodalbail (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); 9536e91bba0SGirish Moodalbail if (ioctl((af == AF_INET) ? iph->iph_sock : iph->iph_sock6, 9546e91bba0SGirish Moodalbail SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) { 9556e91bba0SGirish Moodalbail return (ipadm_errno2status(errno)); 9566e91bba0SGirish Moodalbail } 9576e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 9586e91bba0SGirish Moodalbail } 9596e91bba0SGirish Moodalbail 9606e91bba0SGirish Moodalbail /* 9616e91bba0SGirish Moodalbail * We used /dev/udp or udp6 to set up the mux. So we have to use 9626e91bba0SGirish Moodalbail * the same now for PUNLINK also. 9636e91bba0SGirish Moodalbail */ 9646e91bba0SGirish Moodalbail if (v6) { 9656e91bba0SGirish Moodalbail udp_dev_name = UDP6_DEV_NAME; 9666e91bba0SGirish Moodalbail sock = iph->iph_sock6; 9676e91bba0SGirish Moodalbail } else { 9686e91bba0SGirish Moodalbail udp_dev_name = UDP_DEV_NAME; 9696e91bba0SGirish Moodalbail sock = iph->iph_sock; 9706e91bba0SGirish Moodalbail } 9716e91bba0SGirish Moodalbail if ((muxid_fd = open(udp_dev_name, O_RDWR)) == -1) { 9726e91bba0SGirish Moodalbail ret = ipadm_errno2status(errno); 9736e91bba0SGirish Moodalbail goto done; 9746e91bba0SGirish Moodalbail } 9756e91bba0SGirish Moodalbail ret = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd); 9766e91bba0SGirish Moodalbail if (ret != IPADM_SUCCESS) 9776e91bba0SGirish Moodalbail goto done; 9786e91bba0SGirish Moodalbail (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); 9796e91bba0SGirish Moodalbail if (ioctl(muxid_fd, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) { 9806e91bba0SGirish Moodalbail ret = ipadm_errno2status(errno); 9816e91bba0SGirish Moodalbail goto done; 9826e91bba0SGirish Moodalbail } 9836e91bba0SGirish Moodalbail flags = lifr.lifr_flags; 9846e91bba0SGirish Moodalbail again: 9856e91bba0SGirish Moodalbail if (flags & IFF_IPMP) { 9866e91bba0SGirish Moodalbail /* 9876e91bba0SGirish Moodalbail * There are two reasons the I_PUNLINK can fail with EBUSY: 9886e91bba0SGirish Moodalbail * (1) if IP interfaces are in the group, or (2) if IPMP data 9896e91bba0SGirish Moodalbail * addresses are administratively up. For case (1), we fail 9906e91bba0SGirish Moodalbail * here with a specific error message. For case (2), we bring 9916e91bba0SGirish Moodalbail * down the addresses prior to doing the I_PUNLINK. If the 9926e91bba0SGirish Moodalbail * I_PUNLINK still fails with EBUSY then the configuration 9936e91bba0SGirish Moodalbail * must have changed after our checks, in which case we branch 9946e91bba0SGirish Moodalbail * back up to `again' and rerun this logic. The net effect is 9956e91bba0SGirish Moodalbail * that unplumbing an IPMP interface will only fail with EBUSY 9966e91bba0SGirish Moodalbail * if IP interfaces are in the group. 9976e91bba0SGirish Moodalbail */ 9986e91bba0SGirish Moodalbail if (ioctl(sock, SIOCGLIFGROUPNAME, &lifr) == -1) { 9996e91bba0SGirish Moodalbail ret = ipadm_errno2status(errno); 10006e91bba0SGirish Moodalbail goto done; 10016e91bba0SGirish Moodalbail } 10026e91bba0SGirish Moodalbail (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname, 10036e91bba0SGirish Moodalbail LIFGRNAMSIZ); 10046e91bba0SGirish Moodalbail if (ioctl(sock, SIOCGLIFGROUPINFO, &lifgr) == -1) { 10056e91bba0SGirish Moodalbail ret = ipadm_errno2status(errno); 10066e91bba0SGirish Moodalbail goto done; 10076e91bba0SGirish Moodalbail } 10086e91bba0SGirish Moodalbail if ((v6 && lifgr.gi_nv6 != 0) || (!v6 && lifgr.gi_nv4 != 0)) { 10096e91bba0SGirish Moodalbail ret = IPADM_GRP_NOTEMPTY; 10106e91bba0SGirish Moodalbail goto done; 10116e91bba0SGirish Moodalbail } 10126e91bba0SGirish Moodalbail 10136e91bba0SGirish Moodalbail /* 10146e91bba0SGirish Moodalbail * The kernel will fail the I_PUNLINK if the IPMP interface 10156e91bba0SGirish Moodalbail * has administratively up addresses; bring them down. 10166e91bba0SGirish Moodalbail */ 10176e91bba0SGirish Moodalbail if (ifaddrlistx(ifname, IFF_UP|IFF_DUPLICATE, 10186e91bba0SGirish Moodalbail 0, &ifaddrs) == -1) { 10196e91bba0SGirish Moodalbail ret = ipadm_errno2status(errno); 10206e91bba0SGirish Moodalbail goto done; 10216e91bba0SGirish Moodalbail } 10226e91bba0SGirish Moodalbail ifaddrp = ifaddrs; 10236e91bba0SGirish Moodalbail for (; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) { 10246e91bba0SGirish Moodalbail int sock = (ifaddrp->ia_flags & IFF_IPV4) ? 10256e91bba0SGirish Moodalbail iph->iph_sock : iph->iph_sock6; 10266e91bba0SGirish Moodalbail struct lifreq lifrl; 10276e91bba0SGirish Moodalbail 10286e91bba0SGirish Moodalbail if (((ifaddrp->ia_flags & IFF_IPV6) && !v6) || 10296e91bba0SGirish Moodalbail (!(ifaddrp->ia_flags & IFF_IPV6) && v6)) 10306e91bba0SGirish Moodalbail continue; 10316e91bba0SGirish Moodalbail 10326e91bba0SGirish Moodalbail bzero(&lifrl, sizeof (lifrl)); 10336e91bba0SGirish Moodalbail (void) strlcpy(lifrl.lifr_name, ifaddrp->ia_name, 10346e91bba0SGirish Moodalbail sizeof (lifrl.lifr_name)); 10356e91bba0SGirish Moodalbail if (ioctl(sock, SIOCGLIFFLAGS, &lifrl) < 0) { 10366e91bba0SGirish Moodalbail ret = ipadm_errno2status(errno); 10376e91bba0SGirish Moodalbail ifaddrlistx_free(ifaddrs); 10386e91bba0SGirish Moodalbail goto done; 10396e91bba0SGirish Moodalbail } 10406e91bba0SGirish Moodalbail if (lifrl.lifr_flags & IFF_UP) { 10416e91bba0SGirish Moodalbail ret = i_ipadm_set_flags(iph, lifrl.lifr_name, 10426e91bba0SGirish Moodalbail ((lifrl.lifr_flags & IFF_IPV4) ? AF_INET : 10436e91bba0SGirish Moodalbail AF_INET6), 0, IFF_UP); 10446e91bba0SGirish Moodalbail if (ret != IPADM_SUCCESS) { 10456e91bba0SGirish Moodalbail ifaddrlistx_free(ifaddrs); 10466e91bba0SGirish Moodalbail goto done; 10476e91bba0SGirish Moodalbail } 10486e91bba0SGirish Moodalbail } else if (lifrl.lifr_flags & IFF_DUPLICATE) { 10496e91bba0SGirish Moodalbail if (ioctl(sock, SIOCGLIFADDR, &lifrl) < 0 || 10506e91bba0SGirish Moodalbail ioctl(sock, SIOCSLIFADDR, &lifrl) < 0) { 10516e91bba0SGirish Moodalbail ret = ipadm_errno2status(errno); 10526e91bba0SGirish Moodalbail ifaddrlistx_free(ifaddrs); 10536e91bba0SGirish Moodalbail goto done; 10546e91bba0SGirish Moodalbail } 10556e91bba0SGirish Moodalbail } 10566e91bba0SGirish Moodalbail } 10576e91bba0SGirish Moodalbail ifaddrlistx_free(ifaddrs); 10586e91bba0SGirish Moodalbail } 10596e91bba0SGirish Moodalbail 10606e91bba0SGirish Moodalbail if (ioctl(muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) { 10616e91bba0SGirish Moodalbail ret = ipadm_errno2status(errno); 10626e91bba0SGirish Moodalbail goto done; 10636e91bba0SGirish Moodalbail } 10646e91bba0SGirish Moodalbail arp_muxid = lifr.lifr_arp_muxid; 10656e91bba0SGirish Moodalbail ip_muxid = lifr.lifr_ip_muxid; 10666e91bba0SGirish Moodalbail 10676e91bba0SGirish Moodalbail /* 10686e91bba0SGirish Moodalbail * We don't have a good way of knowing whether the arp stream is 10696e91bba0SGirish Moodalbail * plumbed. We can't rely on IFF_NOARP because someone could 10706e91bba0SGirish Moodalbail * have turned it off later using "ifconfig xxx -arp". 10716e91bba0SGirish Moodalbail */ 10726e91bba0SGirish Moodalbail if (arp_muxid != 0) { 10736e91bba0SGirish Moodalbail if (ioctl(mux_fd, I_PUNLINK, arp_muxid) < 0) { 10746e91bba0SGirish Moodalbail /* 10756e91bba0SGirish Moodalbail * See the comment before the SIOCGLIFGROUPNAME call. 10766e91bba0SGirish Moodalbail */ 10776e91bba0SGirish Moodalbail if (errno == EBUSY && (flags & IFF_IPMP)) 10786e91bba0SGirish Moodalbail goto again; 10796e91bba0SGirish Moodalbail 10806e91bba0SGirish Moodalbail if ((errno == EINVAL) && 10816e91bba0SGirish Moodalbail (flags & (IFF_NOARP | IFF_IPV6))) { 10826e91bba0SGirish Moodalbail /* 10836e91bba0SGirish Moodalbail * Some plumbing utilities set the muxid to 10846e91bba0SGirish Moodalbail * -1 or some invalid value to signify that 10856e91bba0SGirish Moodalbail * there is no arp stream. Set the muxid to 0 10866e91bba0SGirish Moodalbail * before trying to unplumb the IP stream. 10876e91bba0SGirish Moodalbail * IP does not allow the IP stream to be 10886e91bba0SGirish Moodalbail * unplumbed if it sees a non-null arp muxid, 10896e91bba0SGirish Moodalbail * for consistency of IP-ARP streams. 10906e91bba0SGirish Moodalbail */ 10916e91bba0SGirish Moodalbail lifr.lifr_arp_muxid = 0; 10926e91bba0SGirish Moodalbail (void) ioctl(muxid_fd, SIOCSLIFMUXID, 10936e91bba0SGirish Moodalbail (caddr_t)&lifr); 10946e91bba0SGirish Moodalbail changed_arp_muxid = B_TRUE; 10956e91bba0SGirish Moodalbail } 10966e91bba0SGirish Moodalbail /* 10976e91bba0SGirish Moodalbail * In case of any other error, we continue with 10986e91bba0SGirish Moodalbail * the unplumb. 10996e91bba0SGirish Moodalbail */ 11006e91bba0SGirish Moodalbail } 11016e91bba0SGirish Moodalbail } 11026e91bba0SGirish Moodalbail 11036e91bba0SGirish Moodalbail if (ioctl(mux_fd, I_PUNLINK, ip_muxid) < 0) { 11046e91bba0SGirish Moodalbail if (changed_arp_muxid) { 11056e91bba0SGirish Moodalbail /* 11066e91bba0SGirish Moodalbail * Some error occurred, and we need to restore 11076e91bba0SGirish Moodalbail * everything back to what it was. 11086e91bba0SGirish Moodalbail */ 11096e91bba0SGirish Moodalbail save_errno = errno; 11106e91bba0SGirish Moodalbail lifr.lifr_arp_muxid = arp_muxid; 11116e91bba0SGirish Moodalbail lifr.lifr_ip_muxid = ip_muxid; 11126e91bba0SGirish Moodalbail (void) ioctl(muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr); 11136e91bba0SGirish Moodalbail errno = save_errno; 11146e91bba0SGirish Moodalbail } 11156e91bba0SGirish Moodalbail /* 11166e91bba0SGirish Moodalbail * See the comment before the SIOCGLIFGROUPNAME call. 11176e91bba0SGirish Moodalbail */ 11186e91bba0SGirish Moodalbail if (errno == EBUSY && (flags & IFF_IPMP)) 11196e91bba0SGirish Moodalbail goto again; 11206e91bba0SGirish Moodalbail 11216e91bba0SGirish Moodalbail ret = ipadm_errno2status(errno); 11226e91bba0SGirish Moodalbail } 11236e91bba0SGirish Moodalbail done: 11246e91bba0SGirish Moodalbail if (muxid_fd != -1) 11256e91bba0SGirish Moodalbail (void) close(muxid_fd); 11266e91bba0SGirish Moodalbail if (mux_fd != -1) 11276e91bba0SGirish Moodalbail (void) close(mux_fd); 11286e91bba0SGirish Moodalbail 11296e91bba0SGirish Moodalbail if (af == AF_INET6 && ret == IPADM_SUCCESS) { 11306e91bba0SGirish Moodalbail /* 11316e91bba0SGirish Moodalbail * in.ndpd maintains the phyints in its memory even after 11326e91bba0SGirish Moodalbail * the interface is plumbed, so that it can be reused when 11336e91bba0SGirish Moodalbail * the interface gets plumbed again. The default behavior 11346e91bba0SGirish Moodalbail * of in.ndpd is to start autoconfiguration for an interface 11356e91bba0SGirish Moodalbail * that gets plumbed. We need to send the 11366e91bba0SGirish Moodalbail * message IPADM_ENABLE_AUTOCONF to in.ndpd to restore this 11376e91bba0SGirish Moodalbail * default behavior on replumb. 11386e91bba0SGirish Moodalbail */ 11396e91bba0SGirish Moodalbail (void) i_ipadm_enable_autoconf(ifname); 11406e91bba0SGirish Moodalbail } 11416e91bba0SGirish Moodalbail return (ret); 11426e91bba0SGirish Moodalbail } 11436e91bba0SGirish Moodalbail 11446e91bba0SGirish Moodalbail /* 11456e91bba0SGirish Moodalbail * Saves the given interface name `ifname' with address family `af' in 11466e91bba0SGirish Moodalbail * persistent DB. 11476e91bba0SGirish Moodalbail */ 11486e91bba0SGirish Moodalbail static ipadm_status_t 11496e91bba0SGirish Moodalbail i_ipadm_persist_if(ipadm_handle_t iph, const char *ifname, sa_family_t af) 11506e91bba0SGirish Moodalbail { 11516e91bba0SGirish Moodalbail ipmgmt_if_arg_t ifarg; 11526e91bba0SGirish Moodalbail int err; 11536e91bba0SGirish Moodalbail 11546e91bba0SGirish Moodalbail (void) strlcpy(ifarg.ia_ifname, ifname, sizeof (ifarg.ia_ifname)); 11556e91bba0SGirish Moodalbail ifarg.ia_family = af; 11566e91bba0SGirish Moodalbail ifarg.ia_cmd = IPMGMT_CMD_SETIF; 11576e91bba0SGirish Moodalbail ifarg.ia_flags = IPMGMT_PERSIST; 11586e91bba0SGirish Moodalbail err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE); 11596e91bba0SGirish Moodalbail return (ipadm_errno2status(err)); 11606e91bba0SGirish Moodalbail } 11616e91bba0SGirish Moodalbail 11626e91bba0SGirish Moodalbail /* 11636e91bba0SGirish Moodalbail * Remove the IP interface from active configuration. If IPADM_OPT_PERSIST 11646e91bba0SGirish Moodalbail * is set in `ipadm_flags', it is also removed from persistent configuration. 11656e91bba0SGirish Moodalbail */ 11666e91bba0SGirish Moodalbail ipadm_status_t 11676e91bba0SGirish Moodalbail i_ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af, 11686e91bba0SGirish Moodalbail uint32_t ipadm_flags) 11696e91bba0SGirish Moodalbail { 11706e91bba0SGirish Moodalbail ipadm_status_t ret = IPADM_SUCCESS; 11716e91bba0SGirish Moodalbail ipadm_status_t db_status; 11726e91bba0SGirish Moodalbail char tmp_ifname[LIFNAMSIZ]; 11736e91bba0SGirish Moodalbail char *cp; 11746e91bba0SGirish Moodalbail struct ipadm_addrobj_s ipaddr; 11756e91bba0SGirish Moodalbail boolean_t is_persistent = 11766e91bba0SGirish Moodalbail (ipadm_flags & IPADM_OPT_PERSIST); 11776e91bba0SGirish Moodalbail 11786e91bba0SGirish Moodalbail ret = i_ipadm_unplumb_if(iph, ifname, af); 11796e91bba0SGirish Moodalbail if (ret != IPADM_SUCCESS) 11806e91bba0SGirish Moodalbail goto done; 11816e91bba0SGirish Moodalbail 11826e91bba0SGirish Moodalbail cp = strrchr(ifname, IPADM_LOGICAL_SEP); 11836e91bba0SGirish Moodalbail if (cp != NULL) { 11846e91bba0SGirish Moodalbail assert(iph->iph_flags & IPH_LEGACY); 11856e91bba0SGirish Moodalbail /* 11866e91bba0SGirish Moodalbail * This is a non-zero logical interface. 11876e91bba0SGirish Moodalbail * Find the addrobj and remove it from the daemon's memory. 11886e91bba0SGirish Moodalbail */ 11896e91bba0SGirish Moodalbail (void) strlcpy(tmp_ifname, ifname, sizeof (tmp_ifname)); 11906e91bba0SGirish Moodalbail tmp_ifname[cp - ifname] = '\0'; 11916e91bba0SGirish Moodalbail *cp++ = '\0'; 11926e91bba0SGirish Moodalbail ipaddr.ipadm_lifnum = atoi(cp); 11936e91bba0SGirish Moodalbail (void) strlcpy(ipaddr.ipadm_ifname, tmp_ifname, 11946e91bba0SGirish Moodalbail sizeof (ipaddr.ipadm_ifname)); 11956e91bba0SGirish Moodalbail ipaddr.ipadm_af = af; 11966e91bba0SGirish Moodalbail ret = i_ipadm_get_lif2addrobj(iph, &ipaddr); 11976e91bba0SGirish Moodalbail if (ret == IPADM_SUCCESS) { 11986e91bba0SGirish Moodalbail ret = i_ipadm_delete_addrobj(iph, &ipaddr, 11996e91bba0SGirish Moodalbail IPADM_OPT_ACTIVE); 12006e91bba0SGirish Moodalbail } else if (ret == IPADM_NOTFOUND) { 12016e91bba0SGirish Moodalbail ret = IPADM_SUCCESS; 12026e91bba0SGirish Moodalbail } 12036e91bba0SGirish Moodalbail return (ret); 12046e91bba0SGirish Moodalbail } 12056e91bba0SGirish Moodalbail done: 12066e91bba0SGirish Moodalbail /* 12076e91bba0SGirish Moodalbail * Even if interface does not exist, remove all its addresses and 12086e91bba0SGirish Moodalbail * properties from the persistent store. If interface does not 12096e91bba0SGirish Moodalbail * exist both in kernel and the persistent store, return IPADM_ENXIO. 12106e91bba0SGirish Moodalbail */ 12116e91bba0SGirish Moodalbail if ((ret == IPADM_ENXIO && is_persistent) || ret == IPADM_SUCCESS) { 12126e91bba0SGirish Moodalbail db_status = i_ipadm_delete_ifobj(iph, ifname, af, 12136e91bba0SGirish Moodalbail is_persistent); 12146e91bba0SGirish Moodalbail if (db_status == IPADM_SUCCESS) 12156e91bba0SGirish Moodalbail ret = IPADM_SUCCESS; 12166e91bba0SGirish Moodalbail } 12176e91bba0SGirish Moodalbail 12186e91bba0SGirish Moodalbail return (ret); 12196e91bba0SGirish Moodalbail } 12206e91bba0SGirish Moodalbail 12216e91bba0SGirish Moodalbail /* 12226e91bba0SGirish Moodalbail * Resets all addresses on interface `ifname' with address family `af' 12236e91bba0SGirish Moodalbail * from ipmgmtd daemon. If is_persistent = B_TRUE, all interface properties 12246e91bba0SGirish Moodalbail * and address objects of `ifname' for `af' are also removed from the 12256e91bba0SGirish Moodalbail * persistent DB. 12266e91bba0SGirish Moodalbail */ 12276e91bba0SGirish Moodalbail ipadm_status_t 12286e91bba0SGirish Moodalbail i_ipadm_delete_ifobj(ipadm_handle_t iph, const char *ifname, sa_family_t af, 12296e91bba0SGirish Moodalbail boolean_t is_persistent) 12306e91bba0SGirish Moodalbail { 12316e91bba0SGirish Moodalbail ipmgmt_if_arg_t ifarg; 12326e91bba0SGirish Moodalbail int err; 12336e91bba0SGirish Moodalbail 12346e91bba0SGirish Moodalbail ifarg.ia_cmd = IPMGMT_CMD_RESETIF; 12356e91bba0SGirish Moodalbail ifarg.ia_flags = IPMGMT_ACTIVE; 12366e91bba0SGirish Moodalbail if (is_persistent) 12376e91bba0SGirish Moodalbail ifarg.ia_flags |= IPMGMT_PERSIST; 12386e91bba0SGirish Moodalbail ifarg.ia_family = af; 12396e91bba0SGirish Moodalbail (void) strlcpy(ifarg.ia_ifname, ifname, LIFNAMSIZ); 12406e91bba0SGirish Moodalbail 12416e91bba0SGirish Moodalbail err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE); 12426e91bba0SGirish Moodalbail return (ipadm_errno2status(err)); 12436e91bba0SGirish Moodalbail } 12446e91bba0SGirish Moodalbail 12456e91bba0SGirish Moodalbail /* 12466e91bba0SGirish Moodalbail * Create the interface by plumbing it for IP. 12476e91bba0SGirish Moodalbail * This function will check if there is saved configuration information 12486e91bba0SGirish Moodalbail * for `ifname' and return IPADM_OP_DISABLE_OBJ if the name-space 12496e91bba0SGirish Moodalbail * for `ifname' is taken. 12506e91bba0SGirish Moodalbail */ 12516e91bba0SGirish Moodalbail ipadm_status_t 12526e91bba0SGirish Moodalbail i_ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af, 12536e91bba0SGirish Moodalbail uint32_t ipadm_flags) 12546e91bba0SGirish Moodalbail { 12556e91bba0SGirish Moodalbail ipadm_status_t status; 12566e91bba0SGirish Moodalbail boolean_t p_exists; 12576e91bba0SGirish Moodalbail sa_family_t other_af; 12586e91bba0SGirish Moodalbail 12596e91bba0SGirish Moodalbail /* 12606e91bba0SGirish Moodalbail * Return error, if the interface already exists in either the active 12616e91bba0SGirish Moodalbail * or the persistent configuration. 12626e91bba0SGirish Moodalbail */ 12636e91bba0SGirish Moodalbail if (ipadm_if_enabled(iph, ifname, af)) 12646e91bba0SGirish Moodalbail return (IPADM_IF_EXISTS); 12656e91bba0SGirish Moodalbail 1266913a9028SVasumathi Sundaram if (!(iph->iph_flags & IPH_LEGACY)) { 12676e91bba0SGirish Moodalbail status = i_ipadm_if_pexists(iph, ifname, af, &p_exists); 12686e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 12696e91bba0SGirish Moodalbail return (status); 12706e91bba0SGirish Moodalbail other_af = (af == AF_INET ? AF_INET6 : AF_INET); 12716e91bba0SGirish Moodalbail if (p_exists) { 12726e91bba0SGirish Moodalbail if (!ipadm_if_enabled(iph, ifname, other_af)) 12736e91bba0SGirish Moodalbail return (IPADM_OP_DISABLE_OBJ); 12746e91bba0SGirish Moodalbail else 12756e91bba0SGirish Moodalbail ipadm_flags &= ~IPADM_OPT_PERSIST; 12766e91bba0SGirish Moodalbail } 1277913a9028SVasumathi Sundaram } 12786e91bba0SGirish Moodalbail 12796e91bba0SGirish Moodalbail return (i_ipadm_plumb_if(iph, ifname, af, ipadm_flags)); 12806e91bba0SGirish Moodalbail } 12816e91bba0SGirish Moodalbail 12826e91bba0SGirish Moodalbail /* 12836e91bba0SGirish Moodalbail * Plumbs an interface. Creates both IPv4 and IPv6 interfaces by 12846e91bba0SGirish Moodalbail * default, unless a value in `af' is specified. The interface may be plumbed 12856e91bba0SGirish Moodalbail * only if there is no previously saved persistent configuration information 12866e91bba0SGirish Moodalbail * for the interface (in which case the ipadm_enable_if() function must 12876e91bba0SGirish Moodalbail * be used to enable the interface). 12886e91bba0SGirish Moodalbail * 12896e91bba0SGirish Moodalbail * Returns: IPADM_SUCCESS, IPADM_FAILURE, IPADM_IF_EXISTS, 12906e91bba0SGirish Moodalbail * IPADM_IF_PERSIST_EXISTS, IPADM_DLPI_FAILURE, 12916e91bba0SGirish Moodalbail * or appropriate ipadm_status_t corresponding to the errno. 12926e91bba0SGirish Moodalbail * 12936e91bba0SGirish Moodalbail * `ifname' must point to memory that can hold upto LIFNAMSIZ chars. It may 12946e91bba0SGirish Moodalbail * be over-written with the actual interface name when a PPA has to be 12956e91bba0SGirish Moodalbail * internally generated by the library. 12966e91bba0SGirish Moodalbail */ 12976e91bba0SGirish Moodalbail ipadm_status_t 12986e91bba0SGirish Moodalbail ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af, 12996e91bba0SGirish Moodalbail uint32_t flags) 13006e91bba0SGirish Moodalbail { 13016e91bba0SGirish Moodalbail ipadm_status_t status; 13026e91bba0SGirish Moodalbail boolean_t created_v4 = B_FALSE; 13036e91bba0SGirish Moodalbail char newifname[LIFNAMSIZ]; 13046e91bba0SGirish Moodalbail 13056e91bba0SGirish Moodalbail /* Check for the required authorization */ 13066e91bba0SGirish Moodalbail if (!ipadm_check_auth()) 13076e91bba0SGirish Moodalbail return (IPADM_EAUTH); 13086e91bba0SGirish Moodalbail 13096e91bba0SGirish Moodalbail if (flags == 0 || ((flags & IPADM_OPT_PERSIST) && 13106e91bba0SGirish Moodalbail !(flags & IPADM_OPT_ACTIVE)) || 13116e91bba0SGirish Moodalbail (flags & ~(IPADM_COMMON_OPT_MASK | IPADM_OPT_IPMP | 13126e91bba0SGirish Moodalbail IPADM_OPT_GENPPA))) { 13136e91bba0SGirish Moodalbail return (IPADM_INVALID_ARG); 13146e91bba0SGirish Moodalbail } 13156e91bba0SGirish Moodalbail if (flags & IPADM_OPT_GENPPA) { 13166e91bba0SGirish Moodalbail if (snprintf(newifname, LIFNAMSIZ, "%s0", ifname) >= 13176e91bba0SGirish Moodalbail LIFNAMSIZ) 13186e91bba0SGirish Moodalbail return (IPADM_INVALID_ARG); 13196e91bba0SGirish Moodalbail } else { 13206e91bba0SGirish Moodalbail if (strlcpy(newifname, ifname, LIFNAMSIZ) >= LIFNAMSIZ) 13216e91bba0SGirish Moodalbail return (IPADM_INVALID_ARG); 13226e91bba0SGirish Moodalbail } 13236e91bba0SGirish Moodalbail 13246e91bba0SGirish Moodalbail if (!i_ipadm_validate_ifname(iph, newifname)) 13256e91bba0SGirish Moodalbail return (IPADM_INVALID_ARG); 13266e91bba0SGirish Moodalbail 13276e91bba0SGirish Moodalbail if ((af == AF_INET || af == AF_UNSPEC) && 13286e91bba0SGirish Moodalbail !i_ipadm_is_6to4(iph, ifname)) { 13296e91bba0SGirish Moodalbail status = i_ipadm_create_if(iph, ifname, AF_INET, flags); 13306e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 13316e91bba0SGirish Moodalbail return (status); 13326e91bba0SGirish Moodalbail created_v4 = B_TRUE; 13336e91bba0SGirish Moodalbail } 13346e91bba0SGirish Moodalbail if (af == AF_INET6 || af == AF_UNSPEC) { 13356e91bba0SGirish Moodalbail status = i_ipadm_create_if(iph, ifname, AF_INET6, flags); 13366e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) { 13376e91bba0SGirish Moodalbail if (created_v4) { 13386e91bba0SGirish Moodalbail (void) i_ipadm_delete_if(iph, ifname, AF_INET, 13396e91bba0SGirish Moodalbail IPADM_OPT_ACTIVE); 13406e91bba0SGirish Moodalbail } 13416e91bba0SGirish Moodalbail return (status); 13426e91bba0SGirish Moodalbail } 13436e91bba0SGirish Moodalbail } 13446e91bba0SGirish Moodalbail 13456e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 13466e91bba0SGirish Moodalbail } 13476e91bba0SGirish Moodalbail 13486e91bba0SGirish Moodalbail /* 13496e91bba0SGirish Moodalbail * Deletes the interface in `ifname'. Removes both IPv4 and IPv6 interfaces 13506e91bba0SGirish Moodalbail * when `af' = AF_UNSPEC. 13516e91bba0SGirish Moodalbail */ 13526e91bba0SGirish Moodalbail ipadm_status_t 13536e91bba0SGirish Moodalbail ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af, 13546e91bba0SGirish Moodalbail uint32_t flags) 13556e91bba0SGirish Moodalbail { 13566e91bba0SGirish Moodalbail ipadm_status_t status1 = IPADM_SUCCESS; 13576e91bba0SGirish Moodalbail ipadm_status_t status2 = IPADM_SUCCESS; 13586e91bba0SGirish Moodalbail ipadm_status_t other; 13596e91bba0SGirish Moodalbail 13606e91bba0SGirish Moodalbail /* Check for the required authorization */ 13616e91bba0SGirish Moodalbail if (!ipadm_check_auth()) 13626e91bba0SGirish Moodalbail return (IPADM_EAUTH); 13636e91bba0SGirish Moodalbail 13646e91bba0SGirish Moodalbail /* Validate the `ifname' for any logical interface. */ 13656e91bba0SGirish Moodalbail if (flags == 0 || (flags & ~(IPADM_COMMON_OPT_MASK)) || 13666e91bba0SGirish Moodalbail !i_ipadm_validate_ifname(iph, ifname)) 13676e91bba0SGirish Moodalbail return (IPADM_INVALID_ARG); 13686e91bba0SGirish Moodalbail 13696e91bba0SGirish Moodalbail if (af == AF_INET || af == AF_UNSPEC) 13706e91bba0SGirish Moodalbail status1 = i_ipadm_delete_if(iph, ifname, AF_INET, flags); 13716e91bba0SGirish Moodalbail if (af == AF_INET6 || af == AF_UNSPEC) 13726e91bba0SGirish Moodalbail status2 = i_ipadm_delete_if(iph, ifname, AF_INET6, flags); 13736e91bba0SGirish Moodalbail /* 13746e91bba0SGirish Moodalbail * If the family has been uniquely identified, we return the 13756e91bba0SGirish Moodalbail * associated status, even if that is ENXIO. Calls from ifconfig 13766e91bba0SGirish Moodalbail * which can only unplumb one of IPv4/IPv6 at any time fall under 13776e91bba0SGirish Moodalbail * this category. 13786e91bba0SGirish Moodalbail */ 13796e91bba0SGirish Moodalbail if (af == AF_INET) 13806e91bba0SGirish Moodalbail return (status1); 13816e91bba0SGirish Moodalbail else if (af == AF_INET6) 13826e91bba0SGirish Moodalbail return (status2); 13836e91bba0SGirish Moodalbail else if (af != AF_UNSPEC) 13846e91bba0SGirish Moodalbail return (IPADM_INVALID_ARG); 13856e91bba0SGirish Moodalbail 13866e91bba0SGirish Moodalbail /* 13876e91bba0SGirish Moodalbail * If af is AF_UNSPEC, then we return the following: 13886e91bba0SGirish Moodalbail * status1, if status1 == status2 13896e91bba0SGirish Moodalbail * IPADM_SUCCESS, if either of status1 or status2 is SUCCESS 13906e91bba0SGirish Moodalbail * and the other status is ENXIO 13916e91bba0SGirish Moodalbail * IPADM_ENXIO, if both status1 and status2 are ENXIO 13926e91bba0SGirish Moodalbail * IPADM_FAILURE otherwise. 13936e91bba0SGirish Moodalbail */ 13946e91bba0SGirish Moodalbail if (status1 == status2) { 13956e91bba0SGirish Moodalbail /* covers the case when both status1 and status2 are ENXIO */ 13966e91bba0SGirish Moodalbail return (status1); 13976e91bba0SGirish Moodalbail } else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) { 13986e91bba0SGirish Moodalbail if (status1 == IPADM_SUCCESS) 13996e91bba0SGirish Moodalbail other = status2; 14006e91bba0SGirish Moodalbail else 14016e91bba0SGirish Moodalbail other = status1; 14026e91bba0SGirish Moodalbail return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE); 14036e91bba0SGirish Moodalbail } else { 14046e91bba0SGirish Moodalbail return (IPADM_FAILURE); 14056e91bba0SGirish Moodalbail } 14066e91bba0SGirish Moodalbail } 14076e91bba0SGirish Moodalbail 14086e91bba0SGirish Moodalbail /* 14096e91bba0SGirish Moodalbail * Returns information about all interfaces in both active and persistent 14106e91bba0SGirish Moodalbail * configuration. If `ifname' is not NULL, it returns only the interface 14116e91bba0SGirish Moodalbail * identified by `ifname'. 14126e91bba0SGirish Moodalbail * 14136e91bba0SGirish Moodalbail * Return values: 14146e91bba0SGirish Moodalbail * On success: IPADM_SUCCESS. 14156e91bba0SGirish Moodalbail * On error : IPADM_INVALID_ARG, IPADM_ENXIO or IPADM_FAILURE. 14166e91bba0SGirish Moodalbail */ 14176e91bba0SGirish Moodalbail ipadm_status_t 14186e91bba0SGirish Moodalbail ipadm_if_info(ipadm_handle_t iph, const char *ifname, 14196e91bba0SGirish Moodalbail ipadm_if_info_t **if_info, uint32_t flags, int64_t lifc_flags) 14206e91bba0SGirish Moodalbail { 14216e91bba0SGirish Moodalbail ipadm_status_t status; 14226e91bba0SGirish Moodalbail ifspec_t ifsp; 14236e91bba0SGirish Moodalbail 14246e91bba0SGirish Moodalbail if (if_info == NULL || iph == NULL || flags != 0) 14256e91bba0SGirish Moodalbail return (IPADM_INVALID_ARG); 14266e91bba0SGirish Moodalbail 14276e91bba0SGirish Moodalbail if (ifname != NULL && 14286e91bba0SGirish Moodalbail (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)) { 14296e91bba0SGirish Moodalbail return (IPADM_INVALID_ARG); 14306e91bba0SGirish Moodalbail } 14316e91bba0SGirish Moodalbail 14326e91bba0SGirish Moodalbail status = i_ipadm_get_all_if_info(iph, ifname, if_info, lifc_flags); 14336e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 14346e91bba0SGirish Moodalbail return (status); 14356e91bba0SGirish Moodalbail if (ifname != NULL && *if_info == NULL) 14366e91bba0SGirish Moodalbail return (IPADM_ENXIO); 14376e91bba0SGirish Moodalbail 14386e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 14396e91bba0SGirish Moodalbail } 14406e91bba0SGirish Moodalbail 14416e91bba0SGirish Moodalbail /* 14426e91bba0SGirish Moodalbail * Frees the linked list allocated by ipadm_if_info(). 14436e91bba0SGirish Moodalbail */ 14446e91bba0SGirish Moodalbail void 14456e91bba0SGirish Moodalbail ipadm_free_if_info(ipadm_if_info_t *ifinfo) 14466e91bba0SGirish Moodalbail { 14476e91bba0SGirish Moodalbail ipadm_if_info_t *ifinfo_next; 14486e91bba0SGirish Moodalbail 14496e91bba0SGirish Moodalbail for (; ifinfo != NULL; ifinfo = ifinfo_next) { 14506e91bba0SGirish Moodalbail ifinfo_next = ifinfo->ifi_next; 14516e91bba0SGirish Moodalbail free(ifinfo); 14526e91bba0SGirish Moodalbail } 14536e91bba0SGirish Moodalbail } 14546e91bba0SGirish Moodalbail 14556e91bba0SGirish Moodalbail /* 14566e91bba0SGirish Moodalbail * Re-enable the interface `ifname' based on the saved configuration 14576e91bba0SGirish Moodalbail * for `ifname'. 14586e91bba0SGirish Moodalbail */ 14596e91bba0SGirish Moodalbail ipadm_status_t 14606e91bba0SGirish Moodalbail ipadm_enable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags) 14616e91bba0SGirish Moodalbail { 14626e91bba0SGirish Moodalbail nvlist_t *ifnvl; 14636e91bba0SGirish Moodalbail ipadm_status_t status; 14646e91bba0SGirish Moodalbail ifspec_t ifsp; 14656e91bba0SGirish Moodalbail 14666e91bba0SGirish Moodalbail /* Check for the required authorization */ 14676e91bba0SGirish Moodalbail if (!ipadm_check_auth()) 14686e91bba0SGirish Moodalbail return (IPADM_EAUTH); 14696e91bba0SGirish Moodalbail 14706e91bba0SGirish Moodalbail /* Check for logical interfaces. */ 14716e91bba0SGirish Moodalbail if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid) 14726e91bba0SGirish Moodalbail return (IPADM_INVALID_ARG); 14736e91bba0SGirish Moodalbail 14746e91bba0SGirish Moodalbail /* Enabling an interface persistently is not supported. */ 14756e91bba0SGirish Moodalbail if (flags & IPADM_OPT_PERSIST) 14766e91bba0SGirish Moodalbail return (IPADM_NOTSUP); 14776e91bba0SGirish Moodalbail 14786e91bba0SGirish Moodalbail /* 14796e91bba0SGirish Moodalbail * Return early by checking if the interface is already enabled. 14806e91bba0SGirish Moodalbail */ 14816e91bba0SGirish Moodalbail if (ipadm_if_enabled(iph, ifname, AF_INET) && 14826e91bba0SGirish Moodalbail ipadm_if_enabled(iph, ifname, AF_INET6)) { 14836e91bba0SGirish Moodalbail return (IPADM_IF_EXISTS); 14846e91bba0SGirish Moodalbail } 14856e91bba0SGirish Moodalbail /* 14866e91bba0SGirish Moodalbail * Enable the interface and restore all its interface properties 14876e91bba0SGirish Moodalbail * and address objects. 14886e91bba0SGirish Moodalbail */ 14896e91bba0SGirish Moodalbail status = i_ipadm_init_ifs(iph, ifname, &ifnvl); 14906e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 14916e91bba0SGirish Moodalbail return (status); 14926e91bba0SGirish Moodalbail 14936e91bba0SGirish Moodalbail assert(ifnvl != NULL); 14946e91bba0SGirish Moodalbail /* 14956e91bba0SGirish Moodalbail * ipadm_enable_if() does exactly what ipadm_init_ifs() does, 14966e91bba0SGirish Moodalbail * but only for one interface. We need to set IPH_INIT because 14976e91bba0SGirish Moodalbail * ipmgmtd daemon does not have to write the interface to persistent 14986e91bba0SGirish Moodalbail * db. The interface is already available in persistent db 14996e91bba0SGirish Moodalbail * and we are here to re-enable the persistent configuration. 15006e91bba0SGirish Moodalbail */ 15016e91bba0SGirish Moodalbail iph->iph_flags |= IPH_INIT; 15026e91bba0SGirish Moodalbail status = i_ipadm_init_ifobj(iph, ifname, ifnvl); 15036e91bba0SGirish Moodalbail iph->iph_flags &= ~IPH_INIT; 15046e91bba0SGirish Moodalbail return (status); 15056e91bba0SGirish Moodalbail } 15066e91bba0SGirish Moodalbail 15076e91bba0SGirish Moodalbail /* 15086e91bba0SGirish Moodalbail * Disable the interface `ifname' by removing it from the active configuration. 15096e91bba0SGirish Moodalbail * Error code return values follow the model in ipadm_delete_if() 15106e91bba0SGirish Moodalbail */ 15116e91bba0SGirish Moodalbail ipadm_status_t 15126e91bba0SGirish Moodalbail ipadm_disable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags) 15136e91bba0SGirish Moodalbail { 15146e91bba0SGirish Moodalbail ipadm_status_t status1, status2, other; 15156e91bba0SGirish Moodalbail ifspec_t ifsp; 15166e91bba0SGirish Moodalbail 15176e91bba0SGirish Moodalbail /* Check for the required authorization */ 15186e91bba0SGirish Moodalbail if (!ipadm_check_auth()) 15196e91bba0SGirish Moodalbail return (IPADM_EAUTH); 15206e91bba0SGirish Moodalbail 15216e91bba0SGirish Moodalbail /* Check for logical interfaces. */ 15226e91bba0SGirish Moodalbail if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid) 15236e91bba0SGirish Moodalbail return (IPADM_INVALID_ARG); 15246e91bba0SGirish Moodalbail 15256e91bba0SGirish Moodalbail /* Disabling an interface persistently is not supported. */ 15266e91bba0SGirish Moodalbail if (flags & IPADM_OPT_PERSIST) 15276e91bba0SGirish Moodalbail return (IPADM_NOTSUP); 15286e91bba0SGirish Moodalbail 15296e91bba0SGirish Moodalbail status1 = i_ipadm_unplumb_if(iph, ifname, AF_INET6); 15306e91bba0SGirish Moodalbail if (status1 == IPADM_SUCCESS) 15316e91bba0SGirish Moodalbail status1 = i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE); 15326e91bba0SGirish Moodalbail status2 = i_ipadm_unplumb_if(iph, ifname, AF_INET); 15336e91bba0SGirish Moodalbail if (status2 == IPADM_SUCCESS) 15346e91bba0SGirish Moodalbail status2 = i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE); 15356e91bba0SGirish Moodalbail if (status1 == status2) { 15366e91bba0SGirish Moodalbail return (status2); 15376e91bba0SGirish Moodalbail } else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) { 15386e91bba0SGirish Moodalbail if (status1 == IPADM_SUCCESS) 15396e91bba0SGirish Moodalbail other = status2; 15406e91bba0SGirish Moodalbail else 15416e91bba0SGirish Moodalbail other = status1; 15426e91bba0SGirish Moodalbail return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE); 15436e91bba0SGirish Moodalbail } else { 15446e91bba0SGirish Moodalbail return (IPADM_FAILURE); 15456e91bba0SGirish Moodalbail } 15466e91bba0SGirish Moodalbail } 154736b41818SGirish Moodalbail 154836b41818SGirish Moodalbail /* 154936b41818SGirish Moodalbail * This workaround is until libipadm supports IPMP and is required whenever an 155036b41818SGirish Moodalbail * interface is moved into an IPMP group. Since libipadm doesn't support IPMP 155136b41818SGirish Moodalbail * yet, we will have to update the daemon's in-memory mapping of 155236b41818SGirish Moodalbail * `aobjname' to 'lifnum'. 155336b41818SGirish Moodalbail * 155436b41818SGirish Moodalbail * For `IPMGMT_ACTIVE' case, i_ipadm_delete_ifobj() would only fail if 155536b41818SGirish Moodalbail * door_call(3C) fails. Also, there is no use in returning error because 155636b41818SGirish Moodalbail * `ifname' would have been successfuly moved into IPMP group, by this time. 155736b41818SGirish Moodalbail */ 155836b41818SGirish Moodalbail void 155936b41818SGirish Moodalbail ipadm_if_move(ipadm_handle_t iph, const char *ifname) 156036b41818SGirish Moodalbail { 156136b41818SGirish Moodalbail (void) i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE); 156236b41818SGirish Moodalbail (void) i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE); 156336b41818SGirish Moodalbail } 1564