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