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 /* 22f6da83d4SAnurag S. Maskey * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 236e91bba0SGirish Moodalbail */ 246e91bba0SGirish Moodalbail 256e91bba0SGirish Moodalbail #include <stdio.h> 266e91bba0SGirish Moodalbail #include <stdlib.h> 276e91bba0SGirish Moodalbail #include <string.h> 286e91bba0SGirish Moodalbail #include <errno.h> 296e91bba0SGirish Moodalbail #include <fcntl.h> 306e91bba0SGirish Moodalbail #include <unistd.h> 316e91bba0SGirish Moodalbail #include <stropts.h> 326e91bba0SGirish Moodalbail #include <sys/sockio.h> 336e91bba0SGirish Moodalbail #include <sys/types.h> 346e91bba0SGirish Moodalbail #include <sys/stat.h> 356e91bba0SGirish Moodalbail #include <sys/socket.h> 366e91bba0SGirish Moodalbail #include <net/route.h> 376e91bba0SGirish Moodalbail #include <netinet/in.h> 386e91bba0SGirish Moodalbail #include <inet/ip.h> 396e91bba0SGirish Moodalbail #include <arpa/inet.h> 406e91bba0SGirish Moodalbail #include <libintl.h> 416e91bba0SGirish Moodalbail #include <libdlpi.h> 426e91bba0SGirish Moodalbail #include <libinetutil.h> 436e91bba0SGirish Moodalbail #include <libdladm.h> 446e91bba0SGirish Moodalbail #include <libdllink.h> 456e91bba0SGirish Moodalbail #include <libdliptun.h> 466e91bba0SGirish Moodalbail #include <strings.h> 476e91bba0SGirish Moodalbail #include <zone.h> 486e91bba0SGirish Moodalbail #include <ctype.h> 496e91bba0SGirish Moodalbail #include <limits.h> 506e91bba0SGirish Moodalbail #include <assert.h> 516e91bba0SGirish Moodalbail #include <netdb.h> 526e91bba0SGirish Moodalbail #include <pwd.h> 536e91bba0SGirish Moodalbail #include <auth_attr.h> 546e91bba0SGirish Moodalbail #include <secdb.h> 556e91bba0SGirish Moodalbail #include <nss_dbdefs.h> 566e91bba0SGirish Moodalbail #include "libipadm_impl.h" 576e91bba0SGirish Moodalbail 586e91bba0SGirish Moodalbail /* error codes and text description */ 596e91bba0SGirish Moodalbail static struct ipadm_error_info { 606e91bba0SGirish Moodalbail ipadm_status_t error_code; 616e91bba0SGirish Moodalbail const char *error_desc; 626e91bba0SGirish Moodalbail } ipadm_errors[] = { 636e91bba0SGirish Moodalbail { IPADM_SUCCESS, "Operation succeeded" }, 646e91bba0SGirish Moodalbail { IPADM_FAILURE, "Operation failed" }, 656e91bba0SGirish Moodalbail { IPADM_EAUTH, "Insufficient user authorizations" }, 666e91bba0SGirish Moodalbail { IPADM_EPERM, "Permission denied" }, 676e91bba0SGirish Moodalbail { IPADM_NO_BUFS, "No buffer space available" }, 686e91bba0SGirish Moodalbail { IPADM_NO_MEMORY, "Insufficient memory" }, 696e91bba0SGirish Moodalbail { IPADM_BAD_ADDR, "Invalid address" }, 706e91bba0SGirish Moodalbail { IPADM_BAD_PROTOCOL, "Incorrect protocol family for operation" }, 716e91bba0SGirish Moodalbail { IPADM_DAD_FOUND, "Duplicate address detected" }, 726e91bba0SGirish Moodalbail { IPADM_EXISTS, "Already exists" }, 736e91bba0SGirish Moodalbail { IPADM_IF_EXISTS, "Interface already exists" }, 746e91bba0SGirish Moodalbail { IPADM_ADDROBJ_EXISTS, "Address object already exists" }, 756e91bba0SGirish Moodalbail { IPADM_ADDRCONF_EXISTS, "Addrconf already in progress" }, 766e91bba0SGirish Moodalbail { IPADM_ENXIO, "Interface does not exist" }, 776e91bba0SGirish Moodalbail { IPADM_GRP_NOTEMPTY, "IPMP group is not empty" }, 786e91bba0SGirish Moodalbail { IPADM_INVALID_ARG, "Invalid argument provided" }, 796e91bba0SGirish Moodalbail { IPADM_INVALID_NAME, "Invalid name" }, 806e91bba0SGirish Moodalbail { IPADM_DLPI_FAILURE, "Could not open DLPI link" }, 816e91bba0SGirish Moodalbail { IPADM_DLADM_FAILURE, "Datalink does not exist" }, 826e91bba0SGirish Moodalbail { IPADM_PROP_UNKNOWN, "Unknown property" }, 836e91bba0SGirish Moodalbail { IPADM_ERANGE, "Value is outside the allowed range" }, 846e91bba0SGirish Moodalbail { IPADM_ESRCH, "Value does not exist" }, 856e91bba0SGirish Moodalbail { IPADM_EOVERFLOW, "Number of values exceeds the allowed limit" }, 866e91bba0SGirish Moodalbail { IPADM_NOTFOUND, "Object not found" }, 876e91bba0SGirish Moodalbail { IPADM_IF_INUSE, "Interface already in use" }, 886e91bba0SGirish Moodalbail { IPADM_ADDR_INUSE, "Address already in use" }, 896e91bba0SGirish Moodalbail { IPADM_BAD_HOSTNAME, "Hostname maps to multiple IP addresses" }, 906e91bba0SGirish Moodalbail { IPADM_ADDR_NOTAVAIL, "Can't assign requested address" }, 916e91bba0SGirish Moodalbail { IPADM_ALL_ADDRS_NOT_ENABLED, "All addresses could not be enabled" }, 926e91bba0SGirish Moodalbail { IPADM_NDPD_NOT_RUNNING, "IPv6 autoconf daemon in.ndpd not running" }, 936e91bba0SGirish Moodalbail { IPADM_DHCP_START_ERROR, "Could not start dhcpagent" }, 946e91bba0SGirish Moodalbail { IPADM_DHCP_IPC_ERROR, "Could not communicate with dhcpagent" }, 956e91bba0SGirish Moodalbail { IPADM_DHCP_IPC_TIMEOUT, "Communication with dhcpagent timed out" }, 966e91bba0SGirish Moodalbail { IPADM_TEMPORARY_OBJ, "Persistent operation on temporary object" }, 976e91bba0SGirish Moodalbail { IPADM_IPC_ERROR, "Could not communicate with ipmgmtd" }, 986e91bba0SGirish Moodalbail { IPADM_NOTSUP, "Operation not supported" }, 996e91bba0SGirish Moodalbail { IPADM_OP_DISABLE_OBJ, "Operation not supported on disabled object" }, 100*550b6e40SSowmini Varadhan { IPADM_EBADE, "Invalid data exchange with daemon" }, 101*550b6e40SSowmini Varadhan { IPADM_GZ_PERM, "Operation not permitted on from-gz interface"} 1026e91bba0SGirish Moodalbail }; 1036e91bba0SGirish Moodalbail 1046e91bba0SGirish Moodalbail #define IPADM_NUM_ERRORS (sizeof (ipadm_errors) / sizeof (*ipadm_errors)) 1056e91bba0SGirish Moodalbail 1066e91bba0SGirish Moodalbail ipadm_status_t 1076e91bba0SGirish Moodalbail ipadm_errno2status(int error) 1086e91bba0SGirish Moodalbail { 1096e91bba0SGirish Moodalbail switch (error) { 1106e91bba0SGirish Moodalbail case 0: 1116e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 1126e91bba0SGirish Moodalbail case ENXIO: 1136e91bba0SGirish Moodalbail return (IPADM_ENXIO); 1146e91bba0SGirish Moodalbail case ENOMEM: 1156e91bba0SGirish Moodalbail return (IPADM_NO_MEMORY); 1166e91bba0SGirish Moodalbail case ENOBUFS: 1176e91bba0SGirish Moodalbail return (IPADM_NO_BUFS); 1186e91bba0SGirish Moodalbail case EINVAL: 1196e91bba0SGirish Moodalbail return (IPADM_INVALID_ARG); 1206e91bba0SGirish Moodalbail case EBUSY: 1216e91bba0SGirish Moodalbail return (IPADM_IF_INUSE); 1226e91bba0SGirish Moodalbail case EEXIST: 1236e91bba0SGirish Moodalbail return (IPADM_EXISTS); 1246e91bba0SGirish Moodalbail case EADDRNOTAVAIL: 1256e91bba0SGirish Moodalbail return (IPADM_ADDR_NOTAVAIL); 1266e91bba0SGirish Moodalbail case EADDRINUSE: 1276e91bba0SGirish Moodalbail return (IPADM_ADDR_INUSE); 1286e91bba0SGirish Moodalbail case ENOENT: 1296e91bba0SGirish Moodalbail return (IPADM_NOTFOUND); 1306e91bba0SGirish Moodalbail case ERANGE: 1316e91bba0SGirish Moodalbail return (IPADM_ERANGE); 1326e91bba0SGirish Moodalbail case EPERM: 1336e91bba0SGirish Moodalbail return (IPADM_EPERM); 1346e91bba0SGirish Moodalbail case ENOTSUP: 1356e91bba0SGirish Moodalbail case EOPNOTSUPP: 1366e91bba0SGirish Moodalbail return (IPADM_NOTSUP); 1376e91bba0SGirish Moodalbail case EBADF: 1386e91bba0SGirish Moodalbail return (IPADM_IPC_ERROR); 1396e91bba0SGirish Moodalbail case EBADE: 1406e91bba0SGirish Moodalbail return (IPADM_EBADE); 1416e91bba0SGirish Moodalbail case ESRCH: 1426e91bba0SGirish Moodalbail return (IPADM_ESRCH); 1436e91bba0SGirish Moodalbail case EOVERFLOW: 1446e91bba0SGirish Moodalbail return (IPADM_EOVERFLOW); 1456e91bba0SGirish Moodalbail default: 1466e91bba0SGirish Moodalbail return (IPADM_FAILURE); 1476e91bba0SGirish Moodalbail } 1486e91bba0SGirish Moodalbail } 1496e91bba0SGirish Moodalbail 1506e91bba0SGirish Moodalbail /* 1516e91bba0SGirish Moodalbail * Returns a message string for the given libipadm error status. 1526e91bba0SGirish Moodalbail */ 1536e91bba0SGirish Moodalbail const char * 1546e91bba0SGirish Moodalbail ipadm_status2str(ipadm_status_t status) 1556e91bba0SGirish Moodalbail { 1566e91bba0SGirish Moodalbail int i; 1576e91bba0SGirish Moodalbail 1586e91bba0SGirish Moodalbail for (i = 0; i < IPADM_NUM_ERRORS; i++) { 1596e91bba0SGirish Moodalbail if (status == ipadm_errors[i].error_code) 1606e91bba0SGirish Moodalbail return (dgettext(TEXT_DOMAIN, 1616e91bba0SGirish Moodalbail ipadm_errors[i].error_desc)); 1626e91bba0SGirish Moodalbail } 1636e91bba0SGirish Moodalbail 1646e91bba0SGirish Moodalbail return (dgettext(TEXT_DOMAIN, "<unknown error>")); 1656e91bba0SGirish Moodalbail } 1666e91bba0SGirish Moodalbail 1676e91bba0SGirish Moodalbail /* 1686e91bba0SGirish Moodalbail * Opens a handle to libipadm. 1696e91bba0SGirish Moodalbail * Possible values for flags: 1706e91bba0SGirish Moodalbail * IPH_VRRP: Used by VRRP daemon to set the socket option SO_VRRP. 1716e91bba0SGirish Moodalbail * IPH_LEGACY: This is used whenever an application needs to provide a 1726e91bba0SGirish Moodalbail * logical interface name while creating or deleting 1736e91bba0SGirish Moodalbail * interfaces and static addresses. 1746e91bba0SGirish Moodalbail * IPH_INIT: Used by ipadm_init_prop(), to initialize protocol properties 1756e91bba0SGirish Moodalbail * on reboot. 1766e91bba0SGirish Moodalbail */ 1776e91bba0SGirish Moodalbail ipadm_status_t 1786e91bba0SGirish Moodalbail ipadm_open(ipadm_handle_t *handle, uint32_t flags) 1796e91bba0SGirish Moodalbail { 1806e91bba0SGirish Moodalbail ipadm_handle_t iph; 1816e91bba0SGirish Moodalbail ipadm_status_t status = IPADM_SUCCESS; 1826e91bba0SGirish Moodalbail zoneid_t zoneid; 1836e91bba0SGirish Moodalbail ushort_t zflags; 1846e91bba0SGirish Moodalbail int on = B_TRUE; 1856e91bba0SGirish Moodalbail 1866e91bba0SGirish Moodalbail if (handle == NULL) 1876e91bba0SGirish Moodalbail return (IPADM_INVALID_ARG); 1886e91bba0SGirish Moodalbail *handle = NULL; 1896e91bba0SGirish Moodalbail 190*550b6e40SSowmini Varadhan if (flags & ~(IPH_VRRP|IPH_LEGACY|IPH_INIT|IPH_IPMGMTD)) 1916e91bba0SGirish Moodalbail return (IPADM_INVALID_ARG); 1926e91bba0SGirish Moodalbail 1936e91bba0SGirish Moodalbail if ((iph = calloc(1, sizeof (struct ipadm_handle))) == NULL) 1946e91bba0SGirish Moodalbail return (IPADM_NO_MEMORY); 1956e91bba0SGirish Moodalbail iph->iph_sock = -1; 1966e91bba0SGirish Moodalbail iph->iph_sock6 = -1; 1976e91bba0SGirish Moodalbail iph->iph_door_fd = -1; 198*550b6e40SSowmini Varadhan iph->iph_rtsock = -1; 1996e91bba0SGirish Moodalbail iph->iph_flags = flags; 2006e91bba0SGirish Moodalbail (void) pthread_mutex_init(&iph->iph_lock, NULL); 2016e91bba0SGirish Moodalbail 2026e91bba0SGirish Moodalbail if ((iph->iph_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0 || 2036e91bba0SGirish Moodalbail (iph->iph_sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 2046e91bba0SGirish Moodalbail goto errnofail; 2056e91bba0SGirish Moodalbail } 2066e91bba0SGirish Moodalbail 2076e91bba0SGirish Moodalbail /* 2086e91bba0SGirish Moodalbail * We open a handle to libdladm here, to facilitate some daemons (like 2096e91bba0SGirish Moodalbail * nwamd) which opens handle to libipadm before devfsadmd installs the 2106e91bba0SGirish Moodalbail * right device permissions into the kernel and requires "all" 2116e91bba0SGirish Moodalbail * privileges to open DLD_CONTROL_DEV. 2126e91bba0SGirish Moodalbail * 2136e91bba0SGirish Moodalbail * In a non-global shared-ip zone there will be no DLD_CONTROL_DEV node 2146e91bba0SGirish Moodalbail * and dladm_open() will fail. So, we avoid this by not calling 2156e91bba0SGirish Moodalbail * dladm_open() for such zones. 2166e91bba0SGirish Moodalbail */ 2176e91bba0SGirish Moodalbail zoneid = getzoneid(); 218*550b6e40SSowmini Varadhan iph->iph_zoneid = zoneid; 2196e91bba0SGirish Moodalbail if (zoneid != GLOBAL_ZONEID) { 2206e91bba0SGirish Moodalbail if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &zflags, 2216e91bba0SGirish Moodalbail sizeof (zflags)) < 0) { 2226e91bba0SGirish Moodalbail goto errnofail; 2236e91bba0SGirish Moodalbail } 2246e91bba0SGirish Moodalbail } 2256e91bba0SGirish Moodalbail if ((zoneid == GLOBAL_ZONEID) || (zflags & ZF_NET_EXCL)) { 2266e91bba0SGirish Moodalbail if (dladm_open(&iph->iph_dlh) != DLADM_STATUS_OK) { 2276e91bba0SGirish Moodalbail ipadm_close(iph); 2286e91bba0SGirish Moodalbail return (IPADM_DLADM_FAILURE); 2296e91bba0SGirish Moodalbail } 230*550b6e40SSowmini Varadhan if (zoneid != GLOBAL_ZONEID) { 231*550b6e40SSowmini Varadhan iph->iph_rtsock = socket(PF_ROUTE, SOCK_RAW, 0); 232*550b6e40SSowmini Varadhan /* 233*550b6e40SSowmini Varadhan * Failure to open rtsock is ignored as this is 234*550b6e40SSowmini Varadhan * only used in non-global zones to initialize 235*550b6e40SSowmini Varadhan * routing socket information. 236*550b6e40SSowmini Varadhan */ 237*550b6e40SSowmini Varadhan } 2386e91bba0SGirish Moodalbail } else { 2396e91bba0SGirish Moodalbail assert(zoneid != GLOBAL_ZONEID); 2406e91bba0SGirish Moodalbail iph->iph_dlh = NULL; 2416e91bba0SGirish Moodalbail } 2426e91bba0SGirish Moodalbail if (flags & IPH_VRRP) { 2436e91bba0SGirish Moodalbail if (setsockopt(iph->iph_sock6, SOL_SOCKET, SO_VRRP, &on, 2446e91bba0SGirish Moodalbail sizeof (on)) < 0 || setsockopt(iph->iph_sock, SOL_SOCKET, 2456e91bba0SGirish Moodalbail SO_VRRP, &on, sizeof (on)) < 0) { 2466e91bba0SGirish Moodalbail goto errnofail; 2476e91bba0SGirish Moodalbail } 2486e91bba0SGirish Moodalbail } 2496e91bba0SGirish Moodalbail *handle = iph; 2506e91bba0SGirish Moodalbail return (status); 2516e91bba0SGirish Moodalbail 2526e91bba0SGirish Moodalbail errnofail: 2536e91bba0SGirish Moodalbail status = ipadm_errno2status(errno); 2546e91bba0SGirish Moodalbail ipadm_close(iph); 2556e91bba0SGirish Moodalbail return (status); 2566e91bba0SGirish Moodalbail } 2576e91bba0SGirish Moodalbail 2586e91bba0SGirish Moodalbail /* 2596e91bba0SGirish Moodalbail * Closes and frees the libipadm handle. 2606e91bba0SGirish Moodalbail */ 2616e91bba0SGirish Moodalbail void 2626e91bba0SGirish Moodalbail ipadm_close(ipadm_handle_t iph) 2636e91bba0SGirish Moodalbail { 2646e91bba0SGirish Moodalbail if (iph == NULL) 2656e91bba0SGirish Moodalbail return; 2666e91bba0SGirish Moodalbail if (iph->iph_sock != -1) 2676e91bba0SGirish Moodalbail (void) close(iph->iph_sock); 2686e91bba0SGirish Moodalbail if (iph->iph_sock6 != -1) 2696e91bba0SGirish Moodalbail (void) close(iph->iph_sock6); 270*550b6e40SSowmini Varadhan if (iph->iph_rtsock != -1) 271*550b6e40SSowmini Varadhan (void) close(iph->iph_rtsock); 2726e91bba0SGirish Moodalbail if (iph->iph_door_fd != -1) 2736e91bba0SGirish Moodalbail (void) close(iph->iph_door_fd); 2746e91bba0SGirish Moodalbail dladm_close(iph->iph_dlh); 2756e91bba0SGirish Moodalbail (void) pthread_mutex_destroy(&iph->iph_lock); 2766e91bba0SGirish Moodalbail free(iph); 2776e91bba0SGirish Moodalbail } 2786e91bba0SGirish Moodalbail 2796e91bba0SGirish Moodalbail /* 2806e91bba0SGirish Moodalbail * Checks if the caller has the authorization to configure network 2816e91bba0SGirish Moodalbail * interfaces. 2826e91bba0SGirish Moodalbail */ 2836e91bba0SGirish Moodalbail boolean_t 2846e91bba0SGirish Moodalbail ipadm_check_auth(void) 2856e91bba0SGirish Moodalbail { 2866e91bba0SGirish Moodalbail struct passwd pwd; 2876e91bba0SGirish Moodalbail char buf[NSS_BUFLEN_PASSWD]; 2886e91bba0SGirish Moodalbail 2896e91bba0SGirish Moodalbail /* get the password entry for the given user ID */ 2906e91bba0SGirish Moodalbail if (getpwuid_r(getuid(), &pwd, buf, sizeof (buf)) == NULL) 2916e91bba0SGirish Moodalbail return (B_FALSE); 2926e91bba0SGirish Moodalbail 2936e91bba0SGirish Moodalbail /* check for presence of given authorization */ 2946e91bba0SGirish Moodalbail return (chkauthattr(NETWORK_INTERFACE_CONFIG_AUTH, pwd.pw_name) != 0); 2956e91bba0SGirish Moodalbail } 2966e91bba0SGirish Moodalbail 2976e91bba0SGirish Moodalbail /* 2986e91bba0SGirish Moodalbail * Stores the index value of the interface in `ifname' for the address 2996e91bba0SGirish Moodalbail * family `af' into the buffer pointed to by `index'. 3006e91bba0SGirish Moodalbail */ 3016e91bba0SGirish Moodalbail static ipadm_status_t 3026e91bba0SGirish Moodalbail i_ipadm_get_index(ipadm_handle_t iph, const char *ifname, sa_family_t af, 3036e91bba0SGirish Moodalbail int *index) 3046e91bba0SGirish Moodalbail { 3056e91bba0SGirish Moodalbail struct lifreq lifr; 3066e91bba0SGirish Moodalbail int sock; 3076e91bba0SGirish Moodalbail 3086e91bba0SGirish Moodalbail bzero(&lifr, sizeof (lifr)); 3096e91bba0SGirish Moodalbail (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); 3106e91bba0SGirish Moodalbail if (af == AF_INET) 3116e91bba0SGirish Moodalbail sock = iph->iph_sock; 3126e91bba0SGirish Moodalbail else 3136e91bba0SGirish Moodalbail sock = iph->iph_sock6; 3146e91bba0SGirish Moodalbail 3156e91bba0SGirish Moodalbail if (ioctl(sock, SIOCGLIFINDEX, (caddr_t)&lifr) < 0) 3166e91bba0SGirish Moodalbail return (ipadm_errno2status(errno)); 3176e91bba0SGirish Moodalbail *index = lifr.lifr_index; 3186e91bba0SGirish Moodalbail 3196e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 3206e91bba0SGirish Moodalbail } 3216e91bba0SGirish Moodalbail 3226e91bba0SGirish Moodalbail /* 3236e91bba0SGirish Moodalbail * Maximum amount of time (in milliseconds) to wait for Duplicate Address 3246e91bba0SGirish Moodalbail * Detection to complete in the kernel. 3256e91bba0SGirish Moodalbail */ 3266e91bba0SGirish Moodalbail #define DAD_WAIT_TIME 1000 3276e91bba0SGirish Moodalbail 3286e91bba0SGirish Moodalbail /* 3296e91bba0SGirish Moodalbail * Any time that flags are changed on an interface where either the new or the 3306e91bba0SGirish Moodalbail * existing flags have IFF_UP set, we'll get a RTM_NEWADDR message to 3316e91bba0SGirish Moodalbail * announce the new address added and its flag status. 3326e91bba0SGirish Moodalbail * We wait here for that message and look for IFF_UP. 3336e91bba0SGirish Moodalbail * If something's amiss with the kernel, though, we don't wait forever. 3346e91bba0SGirish Moodalbail * (Note that IFF_DUPLICATE is a high-order bit, and we cannot see 3356e91bba0SGirish Moodalbail * it in the routing socket messages.) 3366e91bba0SGirish Moodalbail */ 3376e91bba0SGirish Moodalbail static ipadm_status_t 3386e91bba0SGirish Moodalbail i_ipadm_dad_wait(ipadm_handle_t handle, const char *lifname, sa_family_t af, 3396e91bba0SGirish Moodalbail int rtsock) 3406e91bba0SGirish Moodalbail { 3416e91bba0SGirish Moodalbail struct pollfd fds[1]; 3426e91bba0SGirish Moodalbail union { 3436e91bba0SGirish Moodalbail struct if_msghdr ifm; 3446e91bba0SGirish Moodalbail char buf[1024]; 3456e91bba0SGirish Moodalbail } msg; 3466e91bba0SGirish Moodalbail int index; 3476e91bba0SGirish Moodalbail ipadm_status_t retv; 3486e91bba0SGirish Moodalbail uint64_t flags; 3496e91bba0SGirish Moodalbail hrtime_t starttime, now; 3506e91bba0SGirish Moodalbail 3516e91bba0SGirish Moodalbail fds[0].fd = rtsock; 3526e91bba0SGirish Moodalbail fds[0].events = POLLIN; 3536e91bba0SGirish Moodalbail fds[0].revents = 0; 3546e91bba0SGirish Moodalbail 3556e91bba0SGirish Moodalbail retv = i_ipadm_get_index(handle, lifname, af, &index); 3566e91bba0SGirish Moodalbail if (retv != IPADM_SUCCESS) 3576e91bba0SGirish Moodalbail return (retv); 3586e91bba0SGirish Moodalbail 3596e91bba0SGirish Moodalbail starttime = gethrtime(); 3606e91bba0SGirish Moodalbail for (;;) { 3616e91bba0SGirish Moodalbail now = gethrtime(); 3626e91bba0SGirish Moodalbail now = (now - starttime) / 1000000; 3636e91bba0SGirish Moodalbail if (now >= DAD_WAIT_TIME) 3646e91bba0SGirish Moodalbail break; 3656e91bba0SGirish Moodalbail if (poll(fds, 1, DAD_WAIT_TIME - (int)now) <= 0) 3666e91bba0SGirish Moodalbail break; 3676e91bba0SGirish Moodalbail if (read(rtsock, &msg, sizeof (msg)) <= 0) 3686e91bba0SGirish Moodalbail break; 3696e91bba0SGirish Moodalbail if (msg.ifm.ifm_type != RTM_NEWADDR) 3706e91bba0SGirish Moodalbail continue; 3716e91bba0SGirish Moodalbail /* Note that ifm_index is just 16 bits */ 3726e91bba0SGirish Moodalbail if (index == msg.ifm.ifm_index && (msg.ifm.ifm_flags & IFF_UP)) 3736e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 3746e91bba0SGirish Moodalbail } 3756e91bba0SGirish Moodalbail 3766e91bba0SGirish Moodalbail retv = i_ipadm_get_flags(handle, lifname, af, &flags); 3776e91bba0SGirish Moodalbail if (retv != IPADM_SUCCESS) 3786e91bba0SGirish Moodalbail return (retv); 3796e91bba0SGirish Moodalbail if (flags & IFF_DUPLICATE) 3806e91bba0SGirish Moodalbail return (IPADM_DAD_FOUND); 3816e91bba0SGirish Moodalbail 3826e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 3836e91bba0SGirish Moodalbail } 3846e91bba0SGirish Moodalbail 3856e91bba0SGirish Moodalbail /* 3866e91bba0SGirish Moodalbail * Sets the flags `on_flags' and resets the flags `off_flags' for the logical 3876e91bba0SGirish Moodalbail * interface in `lifname'. 3886e91bba0SGirish Moodalbail * 3896e91bba0SGirish Moodalbail * If the new flags value will transition the interface from "down" to "up" 3906e91bba0SGirish Moodalbail * then duplicate address detection is performed by the kernel. This routine 3916e91bba0SGirish Moodalbail * waits to get the outcome of that test. 3926e91bba0SGirish Moodalbail */ 3936e91bba0SGirish Moodalbail ipadm_status_t 3946e91bba0SGirish Moodalbail i_ipadm_set_flags(ipadm_handle_t iph, const char *lifname, sa_family_t af, 3956e91bba0SGirish Moodalbail uint64_t on_flags, uint64_t off_flags) 3966e91bba0SGirish Moodalbail { 3976e91bba0SGirish Moodalbail struct lifreq lifr; 3986e91bba0SGirish Moodalbail uint64_t oflags; 3996e91bba0SGirish Moodalbail ipadm_status_t ret; 4006e91bba0SGirish Moodalbail int rtsock = -1; 4016e91bba0SGirish Moodalbail int sock, err; 4026e91bba0SGirish Moodalbail 4036e91bba0SGirish Moodalbail ret = i_ipadm_get_flags(iph, lifname, af, &oflags); 4046e91bba0SGirish Moodalbail if (ret != IPADM_SUCCESS) 4056e91bba0SGirish Moodalbail return (ret); 4066e91bba0SGirish Moodalbail 4076e91bba0SGirish Moodalbail sock = (af == AF_INET ? iph->iph_sock : iph->iph_sock6); 4086e91bba0SGirish Moodalbail 4096e91bba0SGirish Moodalbail /* 4106e91bba0SGirish Moodalbail * Any time flags are changed on an interface that has IFF_UP set, 4116e91bba0SGirish Moodalbail * we get a routing socket message. We care about the status, 4126e91bba0SGirish Moodalbail * though, only when the new flags are marked "up." 4136e91bba0SGirish Moodalbail */ 4146e91bba0SGirish Moodalbail if (!(oflags & IFF_UP) && (on_flags & IFF_UP)) 4156e91bba0SGirish Moodalbail rtsock = socket(PF_ROUTE, SOCK_RAW, af); 4166e91bba0SGirish Moodalbail 4176e91bba0SGirish Moodalbail oflags |= on_flags; 4186e91bba0SGirish Moodalbail oflags &= ~off_flags; 4196e91bba0SGirish Moodalbail bzero(&lifr, sizeof (lifr)); 4206e91bba0SGirish Moodalbail (void) strlcpy(lifr.lifr_name, lifname, sizeof (lifr.lifr_name)); 4216e91bba0SGirish Moodalbail lifr.lifr_flags = oflags; 4226e91bba0SGirish Moodalbail if (ioctl(sock, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) { 4236e91bba0SGirish Moodalbail err = errno; 4246e91bba0SGirish Moodalbail if (rtsock != -1) 4256e91bba0SGirish Moodalbail (void) close(rtsock); 4266e91bba0SGirish Moodalbail return (ipadm_errno2status(err)); 4276e91bba0SGirish Moodalbail } 4286e91bba0SGirish Moodalbail if (rtsock == -1) { 4296e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 4306e91bba0SGirish Moodalbail } else { 4316e91bba0SGirish Moodalbail /* Wait for DAD to complete. */ 4326e91bba0SGirish Moodalbail ret = i_ipadm_dad_wait(iph, lifname, af, rtsock); 4336e91bba0SGirish Moodalbail (void) close(rtsock); 4346e91bba0SGirish Moodalbail return (ret); 4356e91bba0SGirish Moodalbail } 4366e91bba0SGirish Moodalbail } 4376e91bba0SGirish Moodalbail 4386e91bba0SGirish Moodalbail /* 4396e91bba0SGirish Moodalbail * Returns the flags value for the logical interface in `lifname' 4406e91bba0SGirish Moodalbail * in the buffer pointed to by `flags'. 4416e91bba0SGirish Moodalbail */ 4426e91bba0SGirish Moodalbail ipadm_status_t 4436e91bba0SGirish Moodalbail i_ipadm_get_flags(ipadm_handle_t iph, const char *lifname, sa_family_t af, 4446e91bba0SGirish Moodalbail uint64_t *flags) 4456e91bba0SGirish Moodalbail { 4466e91bba0SGirish Moodalbail struct lifreq lifr; 4476e91bba0SGirish Moodalbail int sock; 4486e91bba0SGirish Moodalbail 4496e91bba0SGirish Moodalbail bzero(&lifr, sizeof (lifr)); 4506e91bba0SGirish Moodalbail (void) strlcpy(lifr.lifr_name, lifname, sizeof (lifr.lifr_name)); 4516e91bba0SGirish Moodalbail if (af == AF_INET) 4526e91bba0SGirish Moodalbail sock = iph->iph_sock; 4536e91bba0SGirish Moodalbail else 4546e91bba0SGirish Moodalbail sock = iph->iph_sock6; 4556e91bba0SGirish Moodalbail 4566e91bba0SGirish Moodalbail if (ioctl(sock, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) { 4576e91bba0SGirish Moodalbail return (ipadm_errno2status(errno)); 4586e91bba0SGirish Moodalbail } 4596e91bba0SGirish Moodalbail *flags = lifr.lifr_flags; 4606e91bba0SGirish Moodalbail 4616e91bba0SGirish Moodalbail return (IPADM_SUCCESS); 4626e91bba0SGirish Moodalbail } 4636e91bba0SGirish Moodalbail 4646e91bba0SGirish Moodalbail /* 4656e91bba0SGirish Moodalbail * Determines whether or not an interface name represents a loopback 4666e91bba0SGirish Moodalbail * interface, before the interface has been plumbed. 4676e91bba0SGirish Moodalbail * It is assumed that the interface name in `ifname' is of correct format 4686e91bba0SGirish Moodalbail * as verified by ifparse_ifspec(). 4696e91bba0SGirish Moodalbail * 4706e91bba0SGirish Moodalbail * Returns: B_TRUE if loopback, B_FALSE if not. 4716e91bba0SGirish Moodalbail */ 4726e91bba0SGirish Moodalbail boolean_t 4736e91bba0SGirish Moodalbail i_ipadm_is_loopback(const char *ifname) 4746e91bba0SGirish Moodalbail { 4756e91bba0SGirish Moodalbail int len = strlen(LOOPBACK_IF); 4766e91bba0SGirish Moodalbail 4776e91bba0SGirish Moodalbail return (strncmp(ifname, LOOPBACK_IF, len) == 0 && 4786e91bba0SGirish Moodalbail (ifname[len] == '\0' || ifname[len] == IPADM_LOGICAL_SEP)); 4796e91bba0SGirish Moodalbail } 4806e91bba0SGirish Moodalbail 4816e91bba0SGirish Moodalbail /* 4826e91bba0SGirish Moodalbail * Determines whether or not an interface name represents a vni 4836e91bba0SGirish Moodalbail * interface, before the interface has been plumbed. 4846e91bba0SGirish Moodalbail * It is assumed that the interface name in `ifname' is of correct format 4856e91bba0SGirish Moodalbail * as verified by ifparse_ifspec(). 4866e91bba0SGirish Moodalbail * 4876e91bba0SGirish Moodalbail * Returns: B_TRUE if vni, B_FALSE if not. 4886e91bba0SGirish Moodalbail */ 4896e91bba0SGirish Moodalbail boolean_t 4906e91bba0SGirish Moodalbail i_ipadm_is_vni(const char *ifname) 4916e91bba0SGirish Moodalbail { 4926e91bba0SGirish Moodalbail ifspec_t ifsp; 4936e91bba0SGirish Moodalbail 4946e91bba0SGirish Moodalbail return (ifparse_ifspec(ifname, &ifsp) && 4956e91bba0SGirish Moodalbail strcmp(ifsp.ifsp_devnm, "vni") == 0); 4966e91bba0SGirish Moodalbail } 4976e91bba0SGirish Moodalbail 4986e91bba0SGirish Moodalbail /* 4996e91bba0SGirish Moodalbail * Returns B_TRUE if `ifname' is an IP interface on a 6to4 tunnel. 5006e91bba0SGirish Moodalbail */ 5016e91bba0SGirish Moodalbail boolean_t 5026e91bba0SGirish Moodalbail i_ipadm_is_6to4(ipadm_handle_t iph, char *ifname) 5036e91bba0SGirish Moodalbail { 5046e91bba0SGirish Moodalbail dladm_status_t dlstatus; 5056e91bba0SGirish Moodalbail datalink_class_t class; 5066e91bba0SGirish Moodalbail iptun_params_t params; 5076e91bba0SGirish Moodalbail datalink_id_t linkid; 5086e91bba0SGirish Moodalbail 5096e91bba0SGirish Moodalbail if (iph->iph_dlh == NULL) { 510*550b6e40SSowmini Varadhan assert(iph->iph_zoneid != GLOBAL_ZONEID); 5116e91bba0SGirish Moodalbail return (B_FALSE); 5126e91bba0SGirish Moodalbail } 5136e91bba0SGirish Moodalbail dlstatus = dladm_name2info(iph->iph_dlh, ifname, &linkid, NULL, 5146e91bba0SGirish Moodalbail &class, NULL); 5156e91bba0SGirish Moodalbail if (dlstatus == DLADM_STATUS_OK && class == DATALINK_CLASS_IPTUN) { 5166e91bba0SGirish Moodalbail params.iptun_param_linkid = linkid; 5176e91bba0SGirish Moodalbail dlstatus = dladm_iptun_getparams(iph->iph_dlh, ¶ms, 5186e91bba0SGirish Moodalbail DLADM_OPT_ACTIVE); 5196e91bba0SGirish Moodalbail if (dlstatus == DLADM_STATUS_OK && 5206e91bba0SGirish Moodalbail params.iptun_param_type == IPTUN_TYPE_6TO4) { 5216e91bba0SGirish Moodalbail return (B_TRUE); 5226e91bba0SGirish Moodalbail } 5236e91bba0SGirish Moodalbail } 5246e91bba0SGirish Moodalbail return (B_FALSE); 5256e91bba0SGirish Moodalbail } 5266e91bba0SGirish Moodalbail 5276e91bba0SGirish Moodalbail /* 5286e91bba0SGirish Moodalbail * Returns B_TRUE if `ifname' represents an IPMP underlying interface. 5296e91bba0SGirish Moodalbail */ 5306e91bba0SGirish Moodalbail boolean_t 5316e91bba0SGirish Moodalbail i_ipadm_is_under_ipmp(ipadm_handle_t iph, const char *ifname) 5326e91bba0SGirish Moodalbail { 5336e91bba0SGirish Moodalbail struct lifreq lifr; 5346e91bba0SGirish Moodalbail 5356e91bba0SGirish Moodalbail (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); 5366e91bba0SGirish Moodalbail if (ioctl(iph->iph_sock, SIOCGLIFGROUPNAME, (caddr_t)&lifr) < 0) { 5376e91bba0SGirish Moodalbail if (ioctl(iph->iph_sock6, SIOCGLIFGROUPNAME, 5386e91bba0SGirish Moodalbail (caddr_t)&lifr) < 0) { 5396e91bba0SGirish Moodalbail return (B_FALSE); 5406e91bba0SGirish Moodalbail } 5416e91bba0SGirish Moodalbail } 5426e91bba0SGirish Moodalbail return (lifr.lifr_groupname[0] != '\0'); 5436e91bba0SGirish Moodalbail } 5446e91bba0SGirish Moodalbail 5456e91bba0SGirish Moodalbail /* 5466e91bba0SGirish Moodalbail * Returns B_TRUE if `ifname' represents an IPMP meta-interface. 5476e91bba0SGirish Moodalbail */ 5486e91bba0SGirish Moodalbail boolean_t 5496e91bba0SGirish Moodalbail i_ipadm_is_ipmp(ipadm_handle_t iph, const char *ifname) 5506e91bba0SGirish Moodalbail { 5516e91bba0SGirish Moodalbail uint64_t flags; 5526e91bba0SGirish Moodalbail 5536e91bba0SGirish Moodalbail if (i_ipadm_get_flags(iph, ifname, AF_INET, &flags) != IPADM_SUCCESS && 5546e91bba0SGirish Moodalbail i_ipadm_get_flags(iph, ifname, AF_INET6, &flags) != IPADM_SUCCESS) 5556e91bba0SGirish Moodalbail return (B_FALSE); 5566e91bba0SGirish Moodalbail 5576e91bba0SGirish Moodalbail return ((flags & IFF_IPMP) != 0); 5586e91bba0SGirish Moodalbail } 5596e91bba0SGirish Moodalbail 5606e91bba0SGirish Moodalbail /* 5616e91bba0SGirish Moodalbail * For a given interface name, ipadm_if_enabled() checks if v4 5626e91bba0SGirish Moodalbail * or v6 or both IP interfaces exist in the active configuration. 5636e91bba0SGirish Moodalbail */ 5646e91bba0SGirish Moodalbail boolean_t 5656e91bba0SGirish Moodalbail ipadm_if_enabled(ipadm_handle_t iph, const char *ifname, sa_family_t af) 5666e91bba0SGirish Moodalbail { 5676e91bba0SGirish Moodalbail struct lifreq lifr; 5686e91bba0SGirish Moodalbail int s4 = iph->iph_sock; 5696e91bba0SGirish Moodalbail int s6 = iph->iph_sock6; 5706e91bba0SGirish Moodalbail 5716e91bba0SGirish Moodalbail bzero(&lifr, sizeof (lifr)); 5726e91bba0SGirish Moodalbail (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); 5736e91bba0SGirish Moodalbail switch (af) { 5746e91bba0SGirish Moodalbail case AF_INET: 5756e91bba0SGirish Moodalbail if (ioctl(s4, SIOCGLIFFLAGS, (caddr_t)&lifr) == 0) 5766e91bba0SGirish Moodalbail return (B_TRUE); 5776e91bba0SGirish Moodalbail break; 5786e91bba0SGirish Moodalbail case AF_INET6: 5796e91bba0SGirish Moodalbail if (ioctl(s6, SIOCGLIFFLAGS, (caddr_t)&lifr) == 0) 5806e91bba0SGirish Moodalbail return (B_TRUE); 5816e91bba0SGirish Moodalbail break; 5826e91bba0SGirish Moodalbail case AF_UNSPEC: 5836e91bba0SGirish Moodalbail if (ioctl(s4, SIOCGLIFFLAGS, (caddr_t)&lifr) == 0 || 5846e91bba0SGirish Moodalbail ioctl(s6, SIOCGLIFFLAGS, (caddr_t)&lifr) == 0) { 5856e91bba0SGirish Moodalbail return (B_TRUE); 5866e91bba0SGirish Moodalbail } 5876e91bba0SGirish Moodalbail } 5886e91bba0SGirish Moodalbail return (B_FALSE); 5896e91bba0SGirish Moodalbail } 5906e91bba0SGirish Moodalbail 5916e91bba0SGirish Moodalbail /* 5926e91bba0SGirish Moodalbail * Apply the interface property by retrieving information from nvl. 5936e91bba0SGirish Moodalbail */ 5946e91bba0SGirish Moodalbail static ipadm_status_t 5956e91bba0SGirish Moodalbail i_ipadm_init_ifprop(ipadm_handle_t iph, nvlist_t *nvl) 5966e91bba0SGirish Moodalbail { 5976e91bba0SGirish Moodalbail nvpair_t *nvp; 5986e91bba0SGirish Moodalbail char *name, *pname = NULL; 5996e91bba0SGirish Moodalbail char *protostr = NULL, *ifname = NULL, *pval = NULL; 6006e91bba0SGirish Moodalbail uint_t proto; 6016e91bba0SGirish Moodalbail int err = 0; 6026e91bba0SGirish Moodalbail 6036e91bba0SGirish Moodalbail for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; 6046e91bba0SGirish Moodalbail nvp = nvlist_next_nvpair(nvl, nvp)) { 6056e91bba0SGirish Moodalbail name = nvpair_name(nvp); 6066e91bba0SGirish Moodalbail if (strcmp(name, IPADM_NVP_IFNAME) == 0) { 6076e91bba0SGirish Moodalbail if ((err = nvpair_value_string(nvp, &ifname)) != 0) 6086e91bba0SGirish Moodalbail break; 6096e91bba0SGirish Moodalbail } else if (strcmp(name, IPADM_NVP_PROTONAME) == 0) { 6106e91bba0SGirish Moodalbail if ((err = nvpair_value_string(nvp, &protostr)) != 0) 6116e91bba0SGirish Moodalbail break; 6126e91bba0SGirish Moodalbail } else { 6136e91bba0SGirish Moodalbail assert(!IPADM_PRIV_NVP(name)); 6146e91bba0SGirish Moodalbail pname = name; 6156e91bba0SGirish Moodalbail if ((err = nvpair_value_string(nvp, &pval)) != 0) 6166e91bba0SGirish Moodalbail break; 6176e91bba0SGirish Moodalbail } 6186e91bba0SGirish Moodalbail } 6196e91bba0SGirish Moodalbail if (err != 0) 6206e91bba0SGirish Moodalbail return (ipadm_errno2status(err)); 6216e91bba0SGirish Moodalbail proto = ipadm_str2proto(protostr); 6226e91bba0SGirish Moodalbail return (ipadm_set_ifprop(iph, ifname, pname, pval, proto, 6236e91bba0SGirish Moodalbail IPADM_OPT_ACTIVE)); 6246e91bba0SGirish Moodalbail } 6256e91bba0SGirish Moodalbail 6266e91bba0SGirish Moodalbail /* 6276e91bba0SGirish Moodalbail * Instantiate the address object or set the address object property by 6286e91bba0SGirish Moodalbail * retrieving the configuration from the nvlist `nvl'. 6296e91bba0SGirish Moodalbail */ 6306e91bba0SGirish Moodalbail ipadm_status_t 6316e91bba0SGirish Moodalbail i_ipadm_init_addrobj(ipadm_handle_t iph, nvlist_t *nvl) 6326e91bba0SGirish Moodalbail { 6336e91bba0SGirish Moodalbail nvpair_t *nvp; 6346e91bba0SGirish Moodalbail char *name; 6356e91bba0SGirish Moodalbail char *aobjname = NULL, *pval = NULL, *ifname = NULL; 6366e91bba0SGirish Moodalbail sa_family_t af = AF_UNSPEC; 6376e91bba0SGirish Moodalbail ipadm_addr_type_t atype = IPADM_ADDR_NONE; 6386e91bba0SGirish Moodalbail int err = 0; 6396e91bba0SGirish Moodalbail ipadm_status_t status = IPADM_SUCCESS; 6406e91bba0SGirish Moodalbail 6416e91bba0SGirish Moodalbail for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; 6426e91bba0SGirish Moodalbail nvp = nvlist_next_nvpair(nvl, nvp)) { 6436e91bba0SGirish Moodalbail name = nvpair_name(nvp); 6446e91bba0SGirish Moodalbail if (strcmp(name, IPADM_NVP_IFNAME) == 0) { 6456e91bba0SGirish Moodalbail if ((err = nvpair_value_string(nvp, &ifname)) != 0) 6466e91bba0SGirish Moodalbail break; 6476e91bba0SGirish Moodalbail } else if (strcmp(name, IPADM_NVP_AOBJNAME) == 0) { 6486e91bba0SGirish Moodalbail if ((err = nvpair_value_string(nvp, &aobjname)) != 0) 6496e91bba0SGirish Moodalbail break; 6506e91bba0SGirish Moodalbail } else if (i_ipadm_name2atype(name, &af, &atype)) { 6516e91bba0SGirish Moodalbail break; 6526e91bba0SGirish Moodalbail } else { 6536e91bba0SGirish Moodalbail assert(!IPADM_PRIV_NVP(name)); 6546e91bba0SGirish Moodalbail err = nvpair_value_string(nvp, &pval); 6556e91bba0SGirish Moodalbail break; 6566e91bba0SGirish Moodalbail } 6576e91bba0SGirish Moodalbail } 6586e91bba0SGirish Moodalbail if (err != 0) 6596e91bba0SGirish Moodalbail return (ipadm_errno2status(err)); 6606e91bba0SGirish Moodalbail 6616e91bba0SGirish Moodalbail switch (atype) { 6626e91bba0SGirish Moodalbail case IPADM_ADDR_STATIC: 6636e91bba0SGirish Moodalbail status = i_ipadm_enable_static(iph, ifname, nvl, af); 6646e91bba0SGirish Moodalbail break; 6656e91bba0SGirish Moodalbail case IPADM_ADDR_DHCP: 6666e91bba0SGirish Moodalbail status = i_ipadm_enable_dhcp(iph, ifname, nvl); 6676e91bba0SGirish Moodalbail if (status == IPADM_DHCP_IPC_TIMEOUT) 6686e91bba0SGirish Moodalbail status = IPADM_SUCCESS; 6696e91bba0SGirish Moodalbail break; 6706e91bba0SGirish Moodalbail case IPADM_ADDR_IPV6_ADDRCONF: 6716e91bba0SGirish Moodalbail status = i_ipadm_enable_addrconf(iph, ifname, nvl); 6726e91bba0SGirish Moodalbail break; 6736e91bba0SGirish Moodalbail case IPADM_ADDR_NONE: 6746e91bba0SGirish Moodalbail status = ipadm_set_addrprop(iph, name, pval, aobjname, 6756e91bba0SGirish Moodalbail IPADM_OPT_ACTIVE); 6766e91bba0SGirish Moodalbail break; 6776e91bba0SGirish Moodalbail } 6786e91bba0SGirish Moodalbail 6796e91bba0SGirish Moodalbail return (status); 6806e91bba0SGirish Moodalbail } 6816e91bba0SGirish Moodalbail 6826e91bba0SGirish Moodalbail /* 6836e91bba0SGirish Moodalbail * Instantiate the interface object by retrieving the configuration from 6846e91bba0SGirish Moodalbail * `ifnvl'. The nvlist `ifnvl' contains all the persistent configuration 6856e91bba0SGirish Moodalbail * (interface properties and address objects on that interface) for the 6866e91bba0SGirish Moodalbail * given `ifname'. 6876e91bba0SGirish Moodalbail */ 6886e91bba0SGirish Moodalbail ipadm_status_t 6896e91bba0SGirish Moodalbail i_ipadm_init_ifobj(ipadm_handle_t iph, const char *ifname, nvlist_t *ifnvl) 6906e91bba0SGirish Moodalbail { 6916e91bba0SGirish Moodalbail nvlist_t *nvl = NULL; 6926e91bba0SGirish Moodalbail nvpair_t *nvp; 6936e91bba0SGirish Moodalbail char *afstr; 6946e91bba0SGirish Moodalbail ipadm_status_t status; 6956e91bba0SGirish Moodalbail ipadm_status_t ret_status = IPADM_SUCCESS; 6966e91bba0SGirish Moodalbail char newifname[LIFNAMSIZ]; 6976e91bba0SGirish Moodalbail char *aobjstr; 698*550b6e40SSowmini Varadhan sa_family_t af = AF_UNSPEC; 699*550b6e40SSowmini Varadhan boolean_t is_ngz = (iph->iph_zoneid != GLOBAL_ZONEID); 7006e91bba0SGirish Moodalbail 7016e91bba0SGirish Moodalbail (void) strlcpy(newifname, ifname, sizeof (newifname)); 7026e91bba0SGirish Moodalbail /* 7036e91bba0SGirish Moodalbail * First plumb the given interface and then apply all the persistent 7046e91bba0SGirish Moodalbail * interface properties and then instantiate any persistent addresses 7056e91bba0SGirish Moodalbail * objects on that interface. 7066e91bba0SGirish Moodalbail */ 7076e91bba0SGirish Moodalbail for (nvp = nvlist_next_nvpair(ifnvl, NULL); nvp != NULL; 7086e91bba0SGirish Moodalbail nvp = nvlist_next_nvpair(ifnvl, nvp)) { 7096e91bba0SGirish Moodalbail if (nvpair_value_nvlist(nvp, &nvl) != 0) 7106e91bba0SGirish Moodalbail continue; 7116e91bba0SGirish Moodalbail 7126e91bba0SGirish Moodalbail if (nvlist_lookup_string(nvl, IPADM_NVP_FAMILY, &afstr) == 0) { 7136e91bba0SGirish Moodalbail status = i_ipadm_plumb_if(iph, newifname, atoi(afstr), 7146e91bba0SGirish Moodalbail IPADM_OPT_ACTIVE); 7156e91bba0SGirish Moodalbail /* 7166e91bba0SGirish Moodalbail * If the interface is already plumbed, we should 7176e91bba0SGirish Moodalbail * ignore this error because there might be address 7186e91bba0SGirish Moodalbail * address objects on that interface that needs to 7196e91bba0SGirish Moodalbail * be enabled again. 7206e91bba0SGirish Moodalbail */ 7216e91bba0SGirish Moodalbail if (status == IPADM_IF_EXISTS) 7226e91bba0SGirish Moodalbail status = IPADM_SUCCESS; 723*550b6e40SSowmini Varadhan 724*550b6e40SSowmini Varadhan if (is_ngz) 725*550b6e40SSowmini Varadhan af = atoi(afstr); 7266e91bba0SGirish Moodalbail } else if (nvlist_lookup_string(nvl, IPADM_NVP_AOBJNAME, 7276e91bba0SGirish Moodalbail &aobjstr) == 0) { 7286e91bba0SGirish Moodalbail /* 7296e91bba0SGirish Moodalbail * For a static address, we need to search for 7306e91bba0SGirish Moodalbail * the prefixlen in the nvlist `ifnvl'. 7316e91bba0SGirish Moodalbail */ 7326e91bba0SGirish Moodalbail if (nvlist_exists(nvl, IPADM_NVP_IPV4ADDR) || 7336e91bba0SGirish Moodalbail nvlist_exists(nvl, IPADM_NVP_IPV6ADDR)) { 7346e91bba0SGirish Moodalbail status = i_ipadm_merge_prefixlen_from_nvl(ifnvl, 7356e91bba0SGirish Moodalbail nvl, aobjstr); 7366e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 7376e91bba0SGirish Moodalbail continue; 7386e91bba0SGirish Moodalbail } 7396e91bba0SGirish Moodalbail status = i_ipadm_init_addrobj(iph, nvl); 7406e91bba0SGirish Moodalbail /* 7416e91bba0SGirish Moodalbail * If this address is in use on some other interface, 7426e91bba0SGirish Moodalbail * we want to record an error to be returned as 7436e91bba0SGirish Moodalbail * a soft error and continue processing the rest of 7446e91bba0SGirish Moodalbail * the addresses. 7456e91bba0SGirish Moodalbail */ 7466e91bba0SGirish Moodalbail if (status == IPADM_ADDR_NOTAVAIL) { 7476e91bba0SGirish Moodalbail ret_status = IPADM_ALL_ADDRS_NOT_ENABLED; 7486e91bba0SGirish Moodalbail status = IPADM_SUCCESS; 7496e91bba0SGirish Moodalbail } 7506e91bba0SGirish Moodalbail } else { 7516e91bba0SGirish Moodalbail assert(nvlist_exists(nvl, IPADM_NVP_PROTONAME)); 7526e91bba0SGirish Moodalbail status = i_ipadm_init_ifprop(iph, nvl); 7536e91bba0SGirish Moodalbail } 7546e91bba0SGirish Moodalbail if (status != IPADM_SUCCESS) 7556e91bba0SGirish Moodalbail return (status); 7566e91bba0SGirish Moodalbail } 757*550b6e40SSowmini Varadhan 758*550b6e40SSowmini Varadhan if (is_ngz && af != AF_UNSPEC) 759*550b6e40SSowmini Varadhan ret_status = ipadm_init_net_from_gz(iph, newifname, NULL); 7606e91bba0SGirish Moodalbail return (ret_status); 7616e91bba0SGirish Moodalbail } 7626e91bba0SGirish Moodalbail 7636e91bba0SGirish Moodalbail /* 7646e91bba0SGirish Moodalbail * Retrieves the persistent configuration for the given interface(s) in `ifs' 7656e91bba0SGirish Moodalbail * by contacting the daemon and dumps the information in `allifs'. 7666e91bba0SGirish Moodalbail */ 7676e91bba0SGirish Moodalbail ipadm_status_t 7686e91bba0SGirish Moodalbail i_ipadm_init_ifs(ipadm_handle_t iph, const char *ifs, nvlist_t **allifs) 7696e91bba0SGirish Moodalbail { 7706e91bba0SGirish Moodalbail nvlist_t *nvl = NULL; 7716e91bba0SGirish Moodalbail size_t nvlsize, bufsize; 7726e91bba0SGirish Moodalbail ipmgmt_initif_arg_t *iargp; 7736e91bba0SGirish Moodalbail char *buf = NULL, *nvlbuf = NULL; 7746e91bba0SGirish Moodalbail ipmgmt_get_rval_t *rvalp = NULL; 7756e91bba0SGirish Moodalbail int err; 7766e91bba0SGirish Moodalbail ipadm_status_t status = IPADM_SUCCESS; 7776e91bba0SGirish Moodalbail 7786e91bba0SGirish Moodalbail if ((err = ipadm_str2nvlist(ifs, &nvl, IPADM_NORVAL)) != 0) 7796e91bba0SGirish Moodalbail return (ipadm_errno2status(err)); 7806e91bba0SGirish Moodalbail 7816e91bba0SGirish Moodalbail err = nvlist_pack(nvl, &nvlbuf, &nvlsize, NV_ENCODE_NATIVE, 0); 7826e91bba0SGirish Moodalbail if (err != 0) { 7836e91bba0SGirish Moodalbail status = ipadm_errno2status(err); 7846e91bba0SGirish Moodalbail goto done; 7856e91bba0SGirish Moodalbail } 7866e91bba0SGirish Moodalbail bufsize = sizeof (*iargp) + nvlsize; 7876e91bba0SGirish Moodalbail if ((buf = malloc(bufsize)) == NULL) { 7886e91bba0SGirish Moodalbail status = ipadm_errno2status(errno); 7896e91bba0SGirish Moodalbail goto done; 7906e91bba0SGirish Moodalbail } 7916e91bba0SGirish Moodalbail 7926e91bba0SGirish Moodalbail /* populate the door_call argument structure */ 7936e91bba0SGirish Moodalbail iargp = (void *)buf; 7946e91bba0SGirish Moodalbail iargp->ia_cmd = IPMGMT_CMD_INITIF; 7956e91bba0SGirish Moodalbail iargp->ia_flags = 0; 7966e91bba0SGirish Moodalbail iargp->ia_family = AF_UNSPEC; 7976e91bba0SGirish Moodalbail iargp->ia_nvlsize = nvlsize; 7986e91bba0SGirish Moodalbail (void) bcopy(nvlbuf, buf + sizeof (*iargp), nvlsize); 7996e91bba0SGirish Moodalbail 8006e91bba0SGirish Moodalbail if ((rvalp = malloc(sizeof (ipmgmt_get_rval_t))) == NULL) { 8016e91bba0SGirish Moodalbail status = ipadm_errno2status(errno); 8026e91bba0SGirish Moodalbail goto done; 8036e91bba0SGirish Moodalbail } 8046e91bba0SGirish Moodalbail if ((err = ipadm_door_call(iph, iargp, bufsize, (void **)&rvalp, 8056e91bba0SGirish Moodalbail sizeof (*rvalp), B_TRUE)) != 0) { 8066e91bba0SGirish Moodalbail status = ipadm_errno2status(err); 8076e91bba0SGirish Moodalbail goto done; 8086e91bba0SGirish Moodalbail } 8096e91bba0SGirish Moodalbail nvlsize = rvalp->ir_nvlsize; 8106e91bba0SGirish Moodalbail nvlbuf = (char *)rvalp + sizeof (ipmgmt_get_rval_t); 8116e91bba0SGirish Moodalbail 8126e91bba0SGirish Moodalbail /* 8136e91bba0SGirish Moodalbail * nvlbuf contains a list of nvlists, each of which represents 8146e91bba0SGirish Moodalbail * configuration information for the given interface(s) 8156e91bba0SGirish Moodalbail */ 8166e91bba0SGirish Moodalbail err = nvlist_unpack(nvlbuf, nvlsize, allifs, NV_ENCODE_NATIVE); 8176e91bba0SGirish Moodalbail if (err != 0) 8186e91bba0SGirish Moodalbail status = ipadm_errno2status(err); 8196e91bba0SGirish Moodalbail done: 8206e91bba0SGirish Moodalbail nvlist_free(nvl); 8216e91bba0SGirish Moodalbail free(buf); 8226e91bba0SGirish Moodalbail free(nvlbuf); 8236e91bba0SGirish Moodalbail free(rvalp); 8246e91bba0SGirish Moodalbail return (status); 8256e91bba0SGirish Moodalbail } 8266e91bba0SGirish Moodalbail 8276e91bba0SGirish Moodalbail /* 8286e91bba0SGirish Moodalbail * Returns B_FALSE if 8296e91bba0SGirish Moodalbail * (1) `ifname' is NULL or has no string or has a string of invalid length 8306e91bba0SGirish Moodalbail * (2) ifname is a logical interface and IPH_LEGACY is not set, or 8316e91bba0SGirish Moodalbail */ 8326e91bba0SGirish Moodalbail boolean_t 8336e91bba0SGirish Moodalbail i_ipadm_validate_ifname(ipadm_handle_t iph, const char *ifname) 8346e91bba0SGirish Moodalbail { 8356e91bba0SGirish Moodalbail ifspec_t ifsp; 8366e91bba0SGirish Moodalbail 8376e91bba0SGirish Moodalbail if (ifname == NULL || ifname[0] == '\0' || 8386e91bba0SGirish Moodalbail !ifparse_ifspec(ifname, &ifsp)) 8396e91bba0SGirish Moodalbail return (B_FALSE); 8406e91bba0SGirish Moodalbail if (ifsp.ifsp_lunvalid) 8416e91bba0SGirish Moodalbail return (ifsp.ifsp_lun > 0 && (iph->iph_flags & IPH_LEGACY)); 8426e91bba0SGirish Moodalbail return (B_TRUE); 8436e91bba0SGirish Moodalbail } 8446e91bba0SGirish Moodalbail 8456e91bba0SGirish Moodalbail /* 8466e91bba0SGirish Moodalbail * Wrapper for sending a non-transparent I_STR ioctl(). 8476e91bba0SGirish Moodalbail * Returns: Result from ioctl(). 8486e91bba0SGirish Moodalbail */ 8496e91bba0SGirish Moodalbail int 8506e91bba0SGirish Moodalbail i_ipadm_strioctl(int s, int cmd, char *buf, int buflen) 8516e91bba0SGirish Moodalbail { 8526e91bba0SGirish Moodalbail struct strioctl ioc; 8536e91bba0SGirish Moodalbail 8546e91bba0SGirish Moodalbail (void) memset(&ioc, 0, sizeof (ioc)); 8556e91bba0SGirish Moodalbail ioc.ic_cmd = cmd; 8566e91bba0SGirish Moodalbail ioc.ic_timout = 0; 8576e91bba0SGirish Moodalbail ioc.ic_len = buflen; 8586e91bba0SGirish Moodalbail ioc.ic_dp = buf; 8596e91bba0SGirish Moodalbail 8606e91bba0SGirish Moodalbail return (ioctl(s, I_STR, (char *)&ioc)); 8616e91bba0SGirish Moodalbail } 8626e91bba0SGirish Moodalbail 8636e91bba0SGirish Moodalbail /* 8646e91bba0SGirish Moodalbail * Make a door call to the server and checks if the door call succeeded or not. 8656e91bba0SGirish Moodalbail * `is_varsize' specifies that the data returned by ipmgmtd daemon is of 8666e91bba0SGirish Moodalbail * variable size and door will allocate buffer using mmap(). In such cases 8676e91bba0SGirish Moodalbail * we re-allocate the required memory,n assign it to `rbufp', copy the data to 8686e91bba0SGirish Moodalbail * `rbufp' and then call munmap() (see below). 8696e91bba0SGirish Moodalbail * 8706e91bba0SGirish Moodalbail * It also checks to see if the server side procedure ran successfully by 8716e91bba0SGirish Moodalbail * checking for ir_err. Therefore, for some callers who just care about the 8726e91bba0SGirish Moodalbail * return status can set `rbufp' to NULL and set `rsize' to 0. 8736e91bba0SGirish Moodalbail */ 8746e91bba0SGirish Moodalbail int 8756e91bba0SGirish Moodalbail ipadm_door_call(ipadm_handle_t iph, void *arg, size_t asize, void **rbufp, 8766e91bba0SGirish Moodalbail size_t rsize, boolean_t is_varsize) 8776e91bba0SGirish Moodalbail { 8786e91bba0SGirish Moodalbail door_arg_t darg; 8796e91bba0SGirish Moodalbail int err; 8806e91bba0SGirish Moodalbail ipmgmt_retval_t rval, *rvalp; 881f6da83d4SAnurag S. Maskey boolean_t reopen = B_FALSE; 8826e91bba0SGirish Moodalbail 8836e91bba0SGirish Moodalbail if (rbufp == NULL) { 8846e91bba0SGirish Moodalbail rvalp = &rval; 8856e91bba0SGirish Moodalbail rbufp = (void **)&rvalp; 8866e91bba0SGirish Moodalbail rsize = sizeof (rval); 8876e91bba0SGirish Moodalbail } 8886e91bba0SGirish Moodalbail 8896e91bba0SGirish Moodalbail darg.data_ptr = arg; 8906e91bba0SGirish Moodalbail darg.data_size = asize; 8916e91bba0SGirish Moodalbail darg.desc_ptr = NULL; 8926e91bba0SGirish Moodalbail darg.desc_num = 0; 8936e91bba0SGirish Moodalbail darg.rbuf = *rbufp; 8946e91bba0SGirish Moodalbail darg.rsize = rsize; 8956e91bba0SGirish Moodalbail 896f6da83d4SAnurag S. Maskey reopen: 8976e91bba0SGirish Moodalbail (void) pthread_mutex_lock(&iph->iph_lock); 8986e91bba0SGirish Moodalbail /* The door descriptor is opened if it isn't already */ 8996e91bba0SGirish Moodalbail if (iph->iph_door_fd == -1) { 9006e91bba0SGirish Moodalbail if ((iph->iph_door_fd = open(IPMGMT_DOOR, O_RDONLY)) < 0) { 9016e91bba0SGirish Moodalbail err = errno; 9026e91bba0SGirish Moodalbail (void) pthread_mutex_unlock(&iph->iph_lock); 9036e91bba0SGirish Moodalbail return (err); 9046e91bba0SGirish Moodalbail } 9056e91bba0SGirish Moodalbail } 9066e91bba0SGirish Moodalbail (void) pthread_mutex_unlock(&iph->iph_lock); 9076e91bba0SGirish Moodalbail 908f6da83d4SAnurag S. Maskey if (door_call(iph->iph_door_fd, &darg) == -1) { 909f6da83d4SAnurag S. Maskey /* 910f6da83d4SAnurag S. Maskey * Stale door descriptor is possible if ipmgmtd was restarted 911f6da83d4SAnurag S. Maskey * since last iph_door_fd was opened, so try re-opening door 912f6da83d4SAnurag S. Maskey * descriptor. 913f6da83d4SAnurag S. Maskey */ 914f6da83d4SAnurag S. Maskey if (!reopen && errno == EBADF) { 915f6da83d4SAnurag S. Maskey (void) close(iph->iph_door_fd); 916f6da83d4SAnurag S. Maskey iph->iph_door_fd = -1; 917f6da83d4SAnurag S. Maskey reopen = B_TRUE; 918f6da83d4SAnurag S. Maskey goto reopen; 919f6da83d4SAnurag S. Maskey } 9206e91bba0SGirish Moodalbail return (errno); 921f6da83d4SAnurag S. Maskey } 9226e91bba0SGirish Moodalbail err = ((ipmgmt_retval_t *)(void *)(darg.rbuf))->ir_err; 9236e91bba0SGirish Moodalbail if (darg.rbuf != *rbufp) { 9246e91bba0SGirish Moodalbail /* 9256e91bba0SGirish Moodalbail * if the caller is expecting the result to fit in specified 9266e91bba0SGirish Moodalbail * buffer then return failure. 9276e91bba0SGirish Moodalbail */ 9286e91bba0SGirish Moodalbail if (!is_varsize) 9296e91bba0SGirish Moodalbail err = EBADE; 9306e91bba0SGirish Moodalbail /* 9316e91bba0SGirish Moodalbail * The size of the buffer `*rbufp' was not big enough 9326e91bba0SGirish Moodalbail * and the door itself allocated buffer, for us. We will 9336e91bba0SGirish Moodalbail * hit this, on several occasion as for some cases 9346e91bba0SGirish Moodalbail * we cannot predict the size of the return structure. 9356e91bba0SGirish Moodalbail * Reallocate the buffer `*rbufp' and memcpy() the contents 9366e91bba0SGirish Moodalbail * to new buffer. 9376e91bba0SGirish Moodalbail */ 9386e91bba0SGirish Moodalbail if (err == 0) { 9396e91bba0SGirish Moodalbail void *newp; 9406e91bba0SGirish Moodalbail 9416e91bba0SGirish Moodalbail /* allocated memory will be freed by the caller */ 9426e91bba0SGirish Moodalbail if ((newp = realloc(*rbufp, darg.rsize)) == NULL) { 9436e91bba0SGirish Moodalbail err = ENOMEM; 9446e91bba0SGirish Moodalbail } else { 9456e91bba0SGirish Moodalbail *rbufp = newp; 9466e91bba0SGirish Moodalbail (void) memcpy(*rbufp, darg.rbuf, darg.rsize); 9476e91bba0SGirish Moodalbail } 9486e91bba0SGirish Moodalbail } 9496e91bba0SGirish Moodalbail /* munmap() the door buffer */ 9506e91bba0SGirish Moodalbail (void) munmap(darg.rbuf, darg.rsize); 9516e91bba0SGirish Moodalbail } else { 9526e91bba0SGirish Moodalbail if (darg.rsize != rsize) 9536e91bba0SGirish Moodalbail err = EBADE; 9546e91bba0SGirish Moodalbail } 9556e91bba0SGirish Moodalbail return (err); 9566e91bba0SGirish Moodalbail } 957