/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ICFG_FAMILY(handle) handle->ifh_interface.if_protocol #define ICFG_TUNNEL_PROTOCOL(protocol) \ (protocol == IFTAP_IPV6) ? AF_INET6 : AF_INET #define ICFG_SOCKADDR_LEN(protocol) \ (protocol == AF_INET) ? \ (socklen_t)sizeof (struct sockaddr_in) : \ (socklen_t)sizeof (struct sockaddr_in6) #define ICFG_LOGICAL_SEP ':' /* * Maximum amount of time (in milliseconds) to wait for Duplicate Address * Detection to complete in the kernel. */ #define DAD_WAIT_TIME 5000 /* * Note: must be kept in sync with error codes in */ static char *errmsgs[ICFG_NERR] = { /* 0 ICFG_SUCCESS */ "Success", /* 1 ICFG_FAILURE */ "Failure", /* 2 ICFG_NOT_TUNNEL */ "Tunnel operation attempted on non-tunnel", /* 3 ICFG_NOT_SET */ "Could not return non-existent value", /* 4 ICFG_BAD_ADDR */ "Invalid Address", /* 5 ICFG_BAD_PROT */ "Wrong protocol family for operation", /* 6 ICFG_DAD_FAILED */ "Duplicate address detection failure", /* 7 ICFG_DAD_FOUND */ "Duplicate address detected" }; /* * Convert a prefix length to a netmask. Note that the mask array * should zero'ed by the caller. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ static int prefixlen_to_mask(int prefixlen, int maxlen, uchar_t *mask) { if ((prefixlen < 0) || (prefixlen > maxlen)) { errno = EINVAL; return (ICFG_FAILURE); } while (prefixlen > 0) { if (prefixlen >= 8) { *mask++ = 0xFF; prefixlen -= 8; continue; } *mask |= 1 << (8 - prefixlen); prefixlen--; } return (ICFG_SUCCESS); } /* * Copies an an IPv4 or IPv6 address from a sockaddr_storage * structure into the appropriate sockaddr structure for the * address family (sockaddr_in for AF_INET or sockaddr_in6 for * AF_INET6) and verifies that the structure size is large enough * for the copy. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ static int to_sockaddr(sa_family_t af, struct sockaddr *addr, socklen_t *addrlen, const struct sockaddr_storage *ssaddr) { socklen_t len; assert((af == AF_INET) || (af == AF_INET6)); len = ICFG_SOCKADDR_LEN(af); if (*addrlen < len) { errno = ENOSPC; return (ICFG_FAILURE); } (void) memcpy(addr, ssaddr, len); *addrlen = len; return (ICFG_SUCCESS); } /* * Copies an an IPv4 or IPv6 address frrom its sockaddr structure * into a sockaddr_storage structure and does a simple size of * structure verification. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ static int to_sockaddr_storage(sa_family_t af, const struct sockaddr *addr, socklen_t addrlen, struct sockaddr_storage *ssaddr) { socklen_t len; assert((af == AF_INET) || (af == AF_INET6)); len = ICFG_SOCKADDR_LEN(af); if (addrlen < len) { errno = EINVAL; return (ICFG_FAILURE); } (void) memcpy(ssaddr, addr, len); return (ICFG_SUCCESS); } /* * Ensures that the tunnel parameter data for the tunnel associated with * the handle is cached. If the 'force_update' argument is TRUE, then the * cache should be updated. * * Returns: ICFG_SUCCESS, ICFG_NOT_TUNNEL or ICFG_FAILURE. */ static int get_tunnel_params(icfg_handle_t handle, boolean_t force_update) { struct iftun_req *params; if ((handle->ifh_tunnel_params != NULL) && (!force_update)) { return (ICFG_SUCCESS); } if (strchr(handle->ifh_interface.if_name, ICFG_LOGICAL_SEP) != NULL) { return (ICFG_NOT_TUNNEL); } if ((params = calloc(1, sizeof (struct iftun_req))) == NULL) { return (ICFG_FAILURE); } (void) strlcpy(params->ifta_lifr_name, handle->ifh_interface.if_name, sizeof (params->ifta_lifr_name)); if (ioctl(handle->ifh_sock, SIOCGTUNPARAM, (caddr_t)params) < 0) { free(params); if ((errno == EOPNOTSUPP) || (errno == EINVAL)) { return (ICFG_NOT_TUNNEL); } return (ICFG_FAILURE); } /* * We assert that the iftun_req version is the right one * and that the lower and upper protocols are set to either * IPv4 or IPv6. Otherwise, some of our APIs are buggy. */ assert((params->ifta_vers == IFTUN_VERSION) && ((params->ifta_lower == IFTAP_IPV4) || (params->ifta_lower == IFTAP_IPV6)) && ((params->ifta_upper == IFTAP_IPV4) || (params->ifta_upper == IFTAP_IPV6))); if (handle->ifh_tunnel_params != NULL) { free(handle->ifh_tunnel_params); } handle->ifh_tunnel_params = params; return (ICFG_SUCCESS); } /* * Sets a tunnel destination or source address (depending upon 'type') on * a tunnel interface. * * Returns: ICFG_SUCCESS, ICFG_NOT_TUNNEL or ICFG_FAILURE. */ static int set_tunnel_address(icfg_handle_t handle, const struct sockaddr *addr, socklen_t addrlen, int type) { struct sockaddr_storage laddr; sa_family_t lower_family; struct iftun_req *params; int ret; assert((type == IFTUN_SRC) || (type == IFTUN_DST)); if ((ret = get_tunnel_params(handle, B_TRUE)) != ICFG_SUCCESS) { return (ret); } params = handle->ifh_tunnel_params; if (params->ifta_lower == IFTAP_IPV4) { lower_family = AF_INET; } else { lower_family = AF_INET6; } ret = to_sockaddr_storage(lower_family, addr, addrlen, &laddr); if (ret != ICFG_SUCCESS) { return (ret); } if (type == IFTUN_SRC) { params->ifta_saddr = laddr; } else { params->ifta_daddr = laddr; } (void) strlcpy(params->ifta_lifr_name, handle->ifh_interface.if_name, sizeof (params->ifta_lifr_name)); params->ifta_flags |= type; if (ioctl(handle->ifh_sock, SIOCSTUNPARAM, (caddr_t)params) < 0) { return (ICFG_FAILURE); } return (ICFG_SUCCESS); } /* * Return the appropriate error message for a given ICFG error. */ const char * icfg_errmsg(int errcode) { if ((errcode < ICFG_SUCCESS) || (errcode >= ICFG_NERR)) return (dgettext(TEXT_DOMAIN, "")); return (dgettext(TEXT_DOMAIN, errmsgs[errcode])); } /* * Opens the an interface as defined by the interface argument and returns * a handle to the interface via the 'handle' argument. The caller is * responsible for freeing resources allocated by this API by calling the * icfg_close() API. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_open(icfg_handle_t *handle, const icfg_if_t *interface) { icfg_handle_t loc_handle; int sock; sa_family_t family; int syserr; /* * Make sure that a valid protocol family was specified. */ if ((interface->if_protocol != AF_INET) && (interface->if_protocol != AF_INET6)) { errno = EINVAL; return (ICFG_FAILURE); } family = interface->if_protocol; if ((loc_handle = calloc(1, sizeof (struct icfg_handle))) == NULL) { return (ICFG_FAILURE); } if ((sock = socket(family, SOCK_DGRAM, 0)) < 0) { syserr = errno; free(loc_handle); errno = syserr; return (ICFG_FAILURE); } loc_handle->ifh_sock = sock; loc_handle->ifh_interface = *interface; loc_handle->ifh_tunnel_params = NULL; *handle = loc_handle; return (ICFG_SUCCESS); } /* * Closes the interface opened by icfg_open() and releases all resources * associated with the handle. */ void icfg_close(icfg_handle_t handle) { (void) close(handle->ifh_sock); if (handle->ifh_tunnel_params != NULL) { free(handle->ifh_tunnel_params); } free(handle); } /* * Refreshes the tunnel parameter data cache associated with the interface * represented by the handle. Tunnel parameter data is cached by the * libinetcfg library by the first call to to any of the tunnel related APIs. * Since there is no synchronization between consumers of the library and * non-users of this library, the cache may contain stale data. Users may * wish to use this API to refresh the cache before subsequent calls to the * other tunnel related APIs. * * Returns: ICFG_SUCCESS, ICFG_NOT_TUNNEL or ICFG_FAILURE. */ int icfg_refresh_tunnel_cache(icfg_handle_t handle) { return (get_tunnel_params(handle, B_TRUE)); } /* * Sets the destination address for the tunnel interface represented * by 'handle'. * * The 'addr' argument points to either a sockaddr_in structure * (for IPv4) or a sockaddr_in6 structure (for IPv6) that holds * the IP address. The 'addrlen' argument gives the length of the * 'addr' structure. * * This API will always result in an update of the tunnel parameter * data cache. * * Returns: ICFG_SUCCESS, ICFG_NOT_TUNNEL or ICFG_FAILURE. */ int icfg_set_tunnel_dest(icfg_handle_t handle, const struct sockaddr *addr, socklen_t addrlen) { return (set_tunnel_address(handle, addr, addrlen, IFTUN_DST)); } /* * Sets the source address for the tunnel interface represented * by 'handle'. * * The 'addr' argument points to either a sockaddr_in structure * (for IPv4) or a sockaddr_in6 structure (for IPv6) that holds * the IP address. The 'addrlen' argument gives the length of the * 'addr' structure. * * This API will always result in an update of the tunnel parameter * data cache. * * Returns: ICFG_SUCCESS, ICFG_NOT_TUNNEL or ICFG_FAILURE. */ int icfg_set_tunnel_src(icfg_handle_t handle, const struct sockaddr *addr, socklen_t addrlen) { return (set_tunnel_address(handle, addr, addrlen, IFTUN_SRC)); } /* * Sets the hop limit for the tunnel interface represented by * the handle to the value contained in the 'limit' argument. * * This API will always result in an update of the tunnel parameter data cache. * * Returns: ICFG_SUCCESS, ICFG_NOT_TUNNEL or ICFG_FAILURE. */ int icfg_set_tunnel_hoplimit(icfg_handle_t handle, uint8_t limit) { struct iftun_req *params; int ret; if ((ret = get_tunnel_params(handle, B_TRUE)) != ICFG_SUCCESS) { return (ret); } params = handle->ifh_tunnel_params; (void) strlcpy(params->ifta_lifr_name, handle->ifh_interface.if_name, sizeof (params->ifta_lifr_name)); params->ifta_hop_limit = limit; params->ifta_flags |= IFTUN_HOPLIMIT; if (ioctl(handle->ifh_sock, SIOCSTUNPARAM, (caddr_t)params) < 0) { return (ICFG_FAILURE); } return (ICFG_SUCCESS); } /* * Sets the encapsulation limit for the tunnel interface represented by * the handle to the value contained in the 'limit' argument. If the * value of the limit is negative, then the encapsulation limit is disabled. * * This API will always result in an update of the tunnel parameter data cache. * * Returns: ICFG_SUCCESS, ICFG_NOT_TUNNEL or ICFG_FAILURE. */ int icfg_set_tunnel_encaplimit(icfg_handle_t handle, int16_t limit) { struct iftun_req *params; int ret; if ((ret = get_tunnel_params(handle, B_TRUE)) != ICFG_SUCCESS) { return (ret); } params = handle->ifh_tunnel_params; (void) strlcpy(params->ifta_lifr_name, handle->ifh_interface.if_name, sizeof (params->ifta_lifr_name)); params->ifta_encap_lim = limit; params->ifta_flags |= IFTUN_ENCAP; if (ioctl(handle->ifh_sock, SIOCSTUNPARAM, (caddr_t)params) < 0) { return (ICFG_FAILURE); } return (ICFG_SUCCESS); } /* * Returns the source address for the tunnel interface represented * by 'handle'. * * The 'addr' argument is a result parameter that is filled in with * the requested address. The format of the 'addr' parameter is * determined by the address family of the interface. * * The 'addrlen' argument is a value-result parameter. Initially, * it contains the amount of space pointed to by 'addr'; on return * it contains the length in bytes of the address returned. * * Note that if 'addrlen' is not large enough for the returned * address value, then ICFG_FAILURE will be returned and errno * will be set to ENOSPC. * * This API will retrieve the tunnel source value from the tunnel * parameter data cache and will only update the cache if no data has * yet been cached for this tunnel. * * Returns: ICFG_SUCCESS, ICFG_NOT_TUNNEL, ICFG_NOT_SET or * ICFG_FAILURE. */ int icfg_get_tunnel_src(icfg_handle_t handle, struct sockaddr *addr, socklen_t *addrlen) { struct iftun_req *params; int ret; if ((ret = get_tunnel_params(handle, B_FALSE)) != ICFG_SUCCESS) { return (ret); } params = handle->ifh_tunnel_params; if (!(params->ifta_flags & IFTUN_SRC)) { return (ICFG_NOT_SET); } if (params->ifta_lower == IFTAP_IPV4) { assert(params->ifta_saddr.ss_family == AF_INET); } else { assert(params->ifta_saddr.ss_family == AF_INET6); } return (to_sockaddr(params->ifta_saddr.ss_family, addr, addrlen, ¶ms->ifta_saddr)); } /* * Returns the destination address for the tunnel interface * represented by 'handle'. * * The 'addr' argument is a result parameter that is filled in * with the requested address. The format of the 'addr' parameter * is determined by the address family of the interface. * * The 'addrlen' argument is a value-result parameter. Initially, it * contains the amount of space pointed to by 'addr'; on return it * contains the length in bytes of the address returned. * * Note that if 'addrlen' is not large enough for the returned address * value, then ICFG_FAILURE will be returned and errno will be set * to ENOSPC. * * This API will retrieve the tunnel destination value from the tunnel * parameter data cache and will only update the cache if no data has yet * been cached for this tunnel. * * Returns: ICFG_SUCCESS, ICFG_NOT_TUNNEL, ICFG_NOT_SET or * ICFG_FAILURE. */ int icfg_get_tunnel_dest(icfg_handle_t handle, struct sockaddr *addr, socklen_t *addrlen) { struct iftun_req *params; int ret; if ((ret = get_tunnel_params(handle, B_FALSE)) != ICFG_SUCCESS) { return (ret); } params = handle->ifh_tunnel_params; if (!(params->ifta_flags & IFTUN_DST)) { return (ICFG_NOT_SET); } if (params->ifta_lower == IFTAP_IPV4) { assert(params->ifta_daddr.ss_family == AF_INET); } else if (params->ifta_lower == IFTAP_IPV6) { assert(params->ifta_daddr.ss_family == AF_INET6); } return (to_sockaddr(params->ifta_daddr.ss_family, addr, addrlen, ¶ms->ifta_daddr)); } /* * Returns the tunnel hop limit (if any). The value of the limit * will be copied into the buffer supplied by the 'limit' argument. * * This API will retrieve the hoplimit value from the tunnel parameter data * cache and will only update the cache if no data has yet been cached for * this tunnel. * * Returns: ICFG_SUCCESS, ICFG_NOT_TUNNEL, ICFG_NOT_SET or * ICFG_FAILURE. */ int icfg_get_tunnel_hoplimit(icfg_handle_t handle, uint8_t *limit) { struct iftun_req *params; int ret; if ((ret = get_tunnel_params(handle, B_FALSE)) != ICFG_SUCCESS) { return (ret); } params = handle->ifh_tunnel_params; if (!(params->ifta_flags & IFTUN_HOPLIMIT)) { return (ICFG_NOT_SET); } *limit = params->ifta_hop_limit; return (ICFG_SUCCESS); } /* * Returns the tunnel encapsulation limit (if any). The value of the limit * will be copied into the buffer supplied by the 'limit' argument. * * This API will retrieve the encapsulation limit value from the tunnel * parameter data cache and will only update the cache if no data has yet * been cached for this tunnel. * * Returns: ICFG_SUCCESS, ICFG_NOT_TUNNEL, ICFG_NOT_SET or * ICFG_FAILURE. */ int icfg_get_tunnel_encaplimit(icfg_handle_t handle, int16_t *limit) { struct iftun_req *params; int ret; if ((ret = get_tunnel_params(handle, B_FALSE)) != ICFG_SUCCESS) { return (ret); } params = handle->ifh_tunnel_params; if (!(params->ifta_flags & IFTUN_ENCAP)) { return (ICFG_NOT_SET); } *limit = params->ifta_encap_lim; return (ICFG_SUCCESS); } /* * Returns the protocol family (AF_INET or AF_INET6) of the protocol * actually being used to tunnel the data. The value of the protocol family * will be copied into the buffer supplied by the 'protocol' argument. * * This API will retrieve the protocol value from the tunnel parameter data * cache and will only update the cache if no data has yet been cached for * this tunnel. * * Returns: ICFG_SUCCESS, ICFG_NOT_TUNNEL or ICFG_FAILURE. */ int icfg_get_tunnel_lower(icfg_handle_t handle, int *protocol) { struct iftun_req *params; int ret; if ((ret = get_tunnel_params(handle, B_FALSE)) != ICFG_SUCCESS) { return (ret); } params = handle->ifh_tunnel_params; *protocol = ICFG_TUNNEL_PROTOCOL(params->ifta_lower); return (ICFG_SUCCESS); } /* * Returns the protocol family (AF_INET or AF_INET6) of the protocol * actually being tunneled. The value of the protocol family will be copied * into the buffer supplied by the 'protocol' argument. * * This API will retrieve the protocolvalue from the tunnel parameter data * cache and will only update the cache if no data has yet been cached for * this tunnel. * * Returns: ICFG_SUCCESS, ICFG_NOT_TUNNEL or ICFG_FAILURE. */ int icfg_get_tunnel_upper(icfg_handle_t handle, int *protocol) { struct iftun_req *params; int ret; if ((ret = get_tunnel_params(handle, B_FALSE)) != ICFG_SUCCESS) { return (ret); } params = handle->ifh_tunnel_params; *protocol = ICFG_TUNNEL_PROTOCOL(params->ifta_upper); return (ICFG_SUCCESS); } /* * Any time that flags are changed on an interface where either the new or the * existing flags have IFF_UP set, we'll get at least one RTM_IFINFO message to * announce the flag status. Typically, there are two such messages: one * saying that the interface is going down, and another saying that it's coming * back up. * * We wait here for that second message, which can take one of two forms: * either IFF_UP or IFF_DUPLICATE. If something's amiss with the kernel, * though, we don't wait forever. (Note that IFF_DUPLICATE is a high-order * bit, and we can't see it in the routing socket messages.) */ static int dad_wait(icfg_handle_t handle, int rtsock) { struct pollfd fds[1]; union { struct if_msghdr ifm; char buf[1024]; } msg; int index; int retv; uint64_t flags; hrtime_t starttime, now; fds[0].fd = rtsock; fds[0].events = POLLIN; fds[0].revents = 0; if ((retv = icfg_get_index(handle, &index)) != ICFG_SUCCESS) return (retv); starttime = gethrtime(); for (;;) { now = gethrtime(); now = (now - starttime) / 1000000; if (now >= DAD_WAIT_TIME) break; if (poll(fds, 1, DAD_WAIT_TIME - (int)now) <= 0) break; if (read(rtsock, &msg, sizeof (msg)) <= 0) break; if (msg.ifm.ifm_type != RTM_IFINFO) continue; /* Note that ifm_index is just 16 bits */ if (index == msg.ifm.ifm_index && (msg.ifm.ifm_flags & IFF_UP)) return (ICFG_SUCCESS); if ((retv = icfg_get_flags(handle, &flags)) != ICFG_SUCCESS) return (retv); if (flags & IFF_DUPLICATE) return (ICFG_DAD_FOUND); } return (ICFG_DAD_FAILED); } /* * Sets the flags for the interface represented by the 'handle' * argument to the value contained in the 'flags' argument. * * If the new flags value will transition the interface from "down" to "up," * then duplicate address detection is performed by the kernel. This routine * waits to get the outcome of that test. * * Returns: ICFG_SUCCESS, ICFG_DAD_FOUND, ICFG_DAD_FAILED or ICFG_FAILURE. */ int icfg_set_flags(icfg_handle_t handle, uint64_t flags) { struct lifreq lifr; uint64_t oflags; int ret; int rtsock = -1; int aware = RTAW_UNDER_IPMP; (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if ((ret = icfg_get_flags(handle, &oflags)) != ICFG_SUCCESS) return (ret); if (oflags == flags) return (ICFG_SUCCESS); /* * Any time flags are changed on an interface that has IFF_UP set, * you'll get a routing socket message. We care about the status, * though, only when the new flags are marked "up." Since we may be * changing an IPMP test address, we enable RTAW_UNDER_IPMP. */ if (flags & IFF_UP) { rtsock = socket(PF_ROUTE, SOCK_RAW, ICFG_FAMILY(handle)); if (rtsock != -1) { (void) setsockopt(rtsock, SOL_ROUTE, RT_AWARE, &aware, sizeof (aware)); } } lifr.lifr_flags = flags; if (ioctl(handle->ifh_sock, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) { if (rtsock != -1) (void) close(rtsock); return (ICFG_FAILURE); } if (rtsock == -1) { return (ICFG_SUCCESS); } else { ret = dad_wait(handle, rtsock); (void) close(rtsock); return (ret); } } /* * Sets the metric value for the interface represented by the * 'handle' argument to the value contained in the 'metric' * argument. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_set_metric(icfg_handle_t handle, int metric) { struct lifreq lifr; (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); lifr.lifr_metric = metric; if (ioctl(handle->ifh_sock, SIOCSLIFMETRIC, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } return (ICFG_SUCCESS); } /* * Sets the mtu value for the interface represented by the * 'handle' argument to the value contained in the 'mtu' * argument. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_set_mtu(icfg_handle_t handle, uint_t mtu) { struct lifreq lifr; (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); lifr.lifr_mtu = mtu; if (ioctl(handle->ifh_sock, SIOCSLIFMTU, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } return (ICFG_SUCCESS); } /* * Sets the index value for the interface represented by the * 'handle' argument to the value contained in the 'index' * argument. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_set_index(icfg_handle_t handle, int index) { struct lifreq lifr; (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); lifr.lifr_index = index; if (ioctl(handle->ifh_sock, SIOCSLIFINDEX, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } return (ICFG_SUCCESS); } /* * Sets the netmask address for the interface represented by * 'handle'. * * The handle must represent an IPv4 interface. * * The address will be set to the value pointed to by 'addr'. * * Returns: ICFG_SUCCESS, ICFG_BAD_PROT or ICFG_FAILURE. */ int icfg_set_netmask(icfg_handle_t handle, const struct sockaddr_in *addr) { struct lifreq lifr; int ret; if (ICFG_FAMILY(handle) != AF_INET) { return (ICFG_BAD_PROT); } (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); if ((ret = to_sockaddr_storage(ICFG_FAMILY(handle), (struct sockaddr *)addr, sizeof (*addr), &lifr.lifr_addr)) != ICFG_SUCCESS) { return (ret); } (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = AF_INET; if (ioctl(handle->ifh_sock, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } return (ICFG_SUCCESS); } /* * Sets the broadcast address for the interface represented by * 'handle'. * * The handle must represent an IPv4 interface. * * The address will be set to the value pointed to by 'addr'. * * Returns: ICFG_SUCCESS, ICFG_BAD_PROT or ICFG_FAILURE. */ int icfg_set_broadcast(icfg_handle_t handle, const struct sockaddr_in *addr) { struct lifreq lifr; int ret; if (ICFG_FAMILY(handle) != AF_INET) { return (ICFG_BAD_PROT); } (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); if ((ret = to_sockaddr_storage(ICFG_FAMILY(handle), (struct sockaddr *)addr, sizeof (*addr), &lifr.lifr_addr)) != ICFG_SUCCESS) { return (ret); } (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = AF_INET; if (ioctl(handle->ifh_sock, SIOCSLIFBRDADDR, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } return (ICFG_SUCCESS); } /* * Sets the prefixlen value for the interface represented by the handle * argument to the value contained in the 'prefixlen' argument. The * prefixlen is actually stored internally as a netmask value and the API * will convert the value contained in 'prefixlen' into the correct netmask * value according to the protocol family of the interface. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_set_prefixlen(icfg_handle_t handle, int prefixlen) { struct lifreq lifr; (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if (ICFG_FAMILY(handle) == AF_INET6) { struct sockaddr_in6 *sin6; int ret; sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; if ((ret = prefixlen_to_mask(prefixlen, IPV6_ABITS, (uchar_t *)&sin6->sin6_addr)) != ICFG_SUCCESS) { return (ret); } } else { struct sockaddr_in *sin; int ret; sin = (struct sockaddr_in *)&lifr.lifr_addr; if ((ret = prefixlen_to_mask(prefixlen, IP_ABITS, (uchar_t *)&sin->sin_addr)) != ICFG_SUCCESS) { return (ret); } } if (ioctl(handle->ifh_sock, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } return (ICFG_SUCCESS); } /* * Sets the address for the interface represented by 'handle'. * * The 'addr' argument points to either a sockaddr_in structure * (for IPv4) or a sockaddr_in6 structure (for IPv6) that holds * the IP address. The 'addrlen' argument gives the length of the * 'addr' structure. * * If the interface is an IPv6 interface and the interface is * already in the "up" state, then duplicate address detection * is performed before the address is set and is set only if no * duplicate address is detected. * * Returns: ICFG_SUCCESS, ICFG_FAILURE, ICFG_DAD_FOUND, ICFG_DAD_FAILED * or ICFG_FAILURE. */ int icfg_set_addr(icfg_handle_t handle, const struct sockaddr *addr, socklen_t addrlen) { struct lifreq lifr; uint64_t flags; int ret; int rtsock = -1; int aware = RTAW_UNDER_IPMP; (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); if ((ret = to_sockaddr_storage(ICFG_FAMILY(handle), addr, addrlen, &lifr.lifr_addr)) != ICFG_SUCCESS) { return (ret); } /* * Need to check duplicate address detection results if the address is * up. Since this may be an IPMP test address, enable RTAW_UNDER_IPMP. */ if ((ret = icfg_get_flags(handle, &flags)) != ICFG_SUCCESS) return (ret); if (flags & IFF_UP) { rtsock = socket(PF_ROUTE, SOCK_RAW, ICFG_FAMILY(handle)); if (rtsock != -1) { (void) setsockopt(rtsock, SOL_ROUTE, RT_AWARE, &aware, sizeof (aware)); } } (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if (ioctl(handle->ifh_sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0) { if (rtsock != -1) (void) close(rtsock); return (ICFG_FAILURE); } if (rtsock == -1) { return (ICFG_SUCCESS); } else { ret = dad_wait(handle, rtsock); (void) close(rtsock); return (ret); } } /* * Sets the token for the interface represented by 'handle'. * * The handle must represent an IPv6 interface. * * The token will be set to the value contained in 'addr' and * its associated prefixlen will be set to 'prefixlen'. * * Returns: ICFG_SUCCESS, ICFG_BAD_PROT or ICFG_FAILURE. */ int icfg_set_token(icfg_handle_t handle, const struct sockaddr_in6 *addr, int prefixlen) { struct lifreq lifr; int ret; if (ICFG_FAMILY(handle) != AF_INET6) { return (ICFG_BAD_PROT); } (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); if ((ret = to_sockaddr_storage(ICFG_FAMILY(handle), (struct sockaddr *)addr, sizeof (*addr), &lifr.lifr_addr)) != ICFG_SUCCESS) { return (ret); } (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); lifr.lifr_addrlen = prefixlen; if (ioctl(handle->ifh_sock, SIOCSLIFTOKEN, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } return (ICFG_SUCCESS); } /* * Sets the subnet address for the interface represented by 'handle'. * * The 'addr' argument points to either a sockaddr_in structure * (for IPv4) or a sockaddr_in6 structure (for IPv6) that holds * the IP address. The 'addrlen' argument gives the length of the * 'addr' structure. * * The prefixlen of the subnet address will be set to 'prefixlen'. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_set_subnet(icfg_handle_t handle, const struct sockaddr *addr, socklen_t addrlen, int prefixlen) { struct lifreq lifr; int ret; (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if ((ret = to_sockaddr_storage(ICFG_FAMILY(handle), addr, addrlen, &lifr.lifr_addr)) != ICFG_SUCCESS) { return (ret); } lifr.lifr_addrlen = prefixlen; if (ioctl(handle->ifh_sock, SIOCSLIFSUBNET, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } return (ICFG_SUCCESS); } /* * Sets the destination address for the interface represented by * 'handle'. * * The 'addr' argument points to either a sockaddr_in structure * (for IPv4) or a sockaddr_in6 structure (for IPv6) that holds * the IP address. The 'addrlen' argument gives the length of the * 'addr' structure. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_set_dest_addr(icfg_handle_t handle, const struct sockaddr *addr, socklen_t addrlen) { struct lifreq lifr; int ret; (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if ((ret = to_sockaddr_storage(ICFG_FAMILY(handle), addr, addrlen, &lifr.lifr_addr)) != ICFG_SUCCESS) { return (ret); } if (ioctl(handle->ifh_sock, SIOCSLIFDSTADDR, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } return (ICFG_SUCCESS); } /* * Returns the address and prefixlen of the interface represented * by 'handle'. * * The 'addr' argument is a result parameter that is filled in with * the requested address. The format of the 'addr' parameter is * determined by the address family of the interface. * * The 'addrlen' argument is a value-result parameter. Initially, it * contains the amount of space pointed to by 'addr'; on return it * contains the length in bytes of the address returned. * * Note that if 'addrlen' is not large enough for the returned address * value, then ICFG_FAILURE will be returned and errno will be set to ENOSPC. * * If the 'force' argument is set to B_TRUE, then non-critical errors in * obtaining the address will be ignored and the address will be set to * all 0's. Non-critical errors consist of EADDRNOTAVAIL, EAFNOSUPPORT, * and ENXIO. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_get_addr(icfg_handle_t handle, struct sockaddr *addr, socklen_t *addrlen, int *prefixlen, boolean_t force) { struct lifreq lifr; int ret; (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if (ioctl(handle->ifh_sock, SIOCGLIFADDR, (caddr_t)&lifr) < 0) { if (force && ((errno == EADDRNOTAVAIL) || (errno == EAFNOSUPPORT) || (errno == ENXIO))) { (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } else { return (ICFG_FAILURE); } } if ((ret = to_sockaddr(ICFG_FAMILY(handle), addr, addrlen, &lifr.lifr_addr)) != ICFG_SUCCESS) { return (ret); } *prefixlen = lifr.lifr_addrlen; return (ICFG_SUCCESS); } /* * Returns the token address and the token prefixlen of the * interface represented by 'handle'. * * The 'addr' argument is a result parameter that is filled in * with the requested address. * * The 'prefixlen' argument is a result paramter that is filled * in with the token prefixlen. * * If the 'force' argument is set to B_TRUE, then non-critical errors in * obtaining the token address will be ignored and the address will be set * to all 0's. Non-critical errors consist of EADDRNOTAVAIL and EINVAL. * * Returns: ICFG_SUCCESS, ICFG_BAD_PROT or ICFG_FAILURE. */ int icfg_get_token(icfg_handle_t handle, struct sockaddr_in6 *addr, int *prefixlen, boolean_t force) { struct lifreq lifr; socklen_t addrlen = sizeof (*addr); if (ICFG_FAMILY(handle) != AF_INET6) { return (ICFG_BAD_PROT); } (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if (ioctl(handle->ifh_sock, SIOCGLIFTOKEN, (caddr_t)&lifr) < 0) { if (force && ((errno == EADDRNOTAVAIL) || (errno == EINVAL))) { (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } else { return (ICFG_FAILURE); } } *prefixlen = lifr.lifr_addrlen; return (to_sockaddr(ICFG_FAMILY(handle), (struct sockaddr *)addr, &addrlen, &lifr.lifr_addr)); } /* * Returns the subnet address and the subnet prefixlen of the interface * represented by 'handle'. * * The 'addr' argument is a result parameter that is filled in with * the requested address. The format of the 'addr' parameter is * determined by the address family of the interface. * * The 'addrlen' argument is a value-result parameter. Initially, it * contains the amount of space pointed to by 'addr'; on return it * contains the length in bytes of the address returned. * * Note that if 'addrlen' is not large enough for the returned address * value, then ICFG_FAILURE will be returned and errno will be set to ENOSPC. * * If the 'force' argument is set to B_TRUE, then non-critical errors in * obtaining the address will be ignored and the address will be set to all * 0's. Non-critical errors consist of EADDRNOTAVAIL, EAFNOSUPPORT,and ENXIO. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_get_subnet(icfg_handle_t handle, struct sockaddr *addr, socklen_t *addrlen, int *prefixlen, boolean_t force) { struct lifreq lifr; int ret; (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if (ioctl(handle->ifh_sock, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) { if (force && ((errno == EADDRNOTAVAIL) || (errno == EAFNOSUPPORT) || (errno == ENXIO))) { (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } else { return (ICFG_FAILURE); } } if ((ret = to_sockaddr(ICFG_FAMILY(handle), addr, addrlen, &lifr.lifr_addr)) != ICFG_SUCCESS) { return (ret); } *prefixlen = lifr.lifr_addrlen; return (ICFG_SUCCESS); } /* * Returns the netmask address of the interface represented by 'handle'. * * The handle must represent an IPv4 interface. * * The 'addr' argument is a result parameter that is filled in with * the requested address. * * If no netmask address has been set for the interface, an address of * all 0's will be returned. * * Returns: ICFG_SUCCESS, ICFG_BAD_PROT or ICFG_FAILURE. */ int icfg_get_netmask(icfg_handle_t handle, struct sockaddr_in *addr) { struct lifreq lifr; socklen_t addrlen = sizeof (*addr); if (ICFG_FAMILY(handle) != AF_INET) { return (ICFG_BAD_PROT); } (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if (ioctl(handle->ifh_sock, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0) { if (errno != EADDRNOTAVAIL) { return (ICFG_FAILURE); } (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } return (to_sockaddr(ICFG_FAMILY(handle), (struct sockaddr *)addr, &addrlen, &lifr.lifr_addr)); } /* * Returns the broadcast address of the interface represented by 'handle'. * * The handle must represent an IPv4 interface. * * The 'addr' argument is a result parameter that is filled in with * the requested address. * * If no broadcast address has been set for the interface, an address * of all 0's will be returned. * * Returns: ICFG_SUCCESS, ICFG_BAD_PROT or ICFG_FAILURE. */ int icfg_get_broadcast(icfg_handle_t handle, struct sockaddr_in *addr) { struct lifreq lifr; socklen_t addrlen = sizeof (*addr); if (ICFG_FAMILY(handle) != AF_INET) { return (ICFG_BAD_PROT); } (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if (ioctl(handle->ifh_sock, SIOCGLIFBRDADDR, (caddr_t)&lifr) < 0) { if (errno != EADDRNOTAVAIL) { return (ICFG_FAILURE); } (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } return (to_sockaddr(ICFG_FAMILY(handle), (struct sockaddr *)addr, &addrlen, &lifr.lifr_addr)); } /* * Returns the destination address of the interface represented * by 'handle'. * * The 'addr' argument is a result parameter that is filled in with * the requested address. The format of the 'addr' parameter is * determined by the address family of the interface. * * The 'addrlen' argument is a value-result parameter. Initially, it * contains the amount of space pointed to by 'addr'; on return it * contains the length in bytes of the address returned. * * Note that if 'addrlen' is not large enough for the returned address * value, then ICFG_FAILURE will be returned and errno will be set to * ENOSPC. * * If no destination address has been set for the interface, an address * of all 0's will be returned. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_get_dest_addr(icfg_handle_t handle, struct sockaddr *addr, socklen_t *addrlen) { struct lifreq lifr; (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if (ioctl(handle->ifh_sock, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) { if (errno != EADDRNOTAVAIL) { return (ICFG_FAILURE); } /* No destination address set yet */ (void) memset(&lifr.lifr_dstaddr, 0, sizeof (lifr.lifr_dstaddr)); } return (to_sockaddr(ICFG_FAMILY(handle), addr, addrlen, &lifr.lifr_addr)); } /* * Returns the groupname, if any, of the interface represented by the handle * argument into the buffer pointed to by the 'groupname' argument. The size * of the groupname buffer is expected to be of 'len' bytes in length and * should be large enough to receive the groupname of the interface * (i.e., LIFNAMSIZ). * * Returns: ICFG_SUCCESS, ICFG_NOT_SET or ICFG_FAILURE. */ int icfg_get_groupname(icfg_handle_t handle, char *groupname, size_t len) { struct lifreq lifr; (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); (void) memset(lifr.lifr_groupname, 0, sizeof (lifr.lifr_groupname)); if (ioctl(handle->ifh_sock, SIOCGLIFGROUPNAME, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } if (strlen(lifr.lifr_groupname) > 0) { (void) strlcpy(groupname, lifr.lifr_groupname, len); } else { return (ICFG_NOT_SET); } return (ICFG_SUCCESS); } /* * Returns the link info of the interface represented by the handle * argument into the buffer pointed to by the 'info' argument. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_get_linkinfo(icfg_handle_t handle, lif_ifinfo_req_t *info) { struct lifreq lifr; char *cp; (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); if ((cp = strchr(lifr.lifr_name, ICFG_LOGICAL_SEP)) != NULL) { *cp = '\0'; } lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if (ioctl(handle->ifh_sock, SIOCGLIFLNKINFO, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } *info = lifr.lifr_ifinfo; return (ICFG_SUCCESS); } /* * Returns the flags value of the interface represented by the handle * argument into the buffer pointed to by the 'flags' argument. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_get_flags(icfg_handle_t handle, uint64_t *flags) { struct lifreq lifr; (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if (ioctl(handle->ifh_sock, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } *flags = lifr.lifr_flags; return (ICFG_SUCCESS); } /* * Returns the metric value of the interface represented by the handle * argument into the buffer pointed to by the 'metric' argument. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_get_metric(icfg_handle_t handle, int *metric) { struct lifreq lifr; (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if (ioctl(handle->ifh_sock, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } *metric = lifr.lifr_metric; return (ICFG_SUCCESS); } /* * Returns the mtu value of the interface represented by the handle * argument into the buffer pointed to by the 'mtu' argument. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_get_mtu(icfg_handle_t handle, uint_t *mtu) { struct lifreq lifr; (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if (ioctl(handle->ifh_sock, SIOCGLIFMTU, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } *mtu = lifr.lifr_mtu; return (ICFG_SUCCESS); } /* * Returns the index value of the interface represented by the handle * argument into the buffer pointed to by the 'index' argument. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_get_index(icfg_handle_t handle, int *index) { struct lifreq lifr; (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name, sizeof (lifr.lifr_name)); lifr.lifr_addr.ss_family = ICFG_FAMILY(handle); if (ioctl(handle->ifh_sock, SIOCGLIFINDEX, (caddr_t)&lifr) < 0) { return (ICFG_FAILURE); } *index = lifr.lifr_index; return (ICFG_SUCCESS); } /* * Walks a list of interfaces and for each interface found, the * caller-supplied function 'callback()' is invoked. The iteration will be * interrupted if the caller-supplied function does not return ICFG_SUCCESS. * * The 'proto' argument is used by the caller to define which interfaces are * to be walked by the API. The possible values for 'proto' are AF_INET, * AF_INET6, and AF_UNSPEC. * * The 'arg' argument is a pointer to caller-specific data. * * Returns: ICFG_SUCCESS, ICFG_FAILURE or error code returned by callback(). */ int icfg_iterate_if(int proto, int type, void *arg, int (*callback)(icfg_if_t *interface, void *arg)) { icfg_if_t *if_ids; int len; int i; int ret; ret = icfg_get_if_list(&if_ids, &len, proto, type); if (ret != ICFG_SUCCESS) { return (ret); } for (i = 0; i < len; i++) { if ((ret = callback(&if_ids[i], arg)) != ICFG_SUCCESS) { break; } } icfg_free_if_list(if_ids); return (ret); } /* * Returns a list of currently plumbed interfaces. The list of interfaces is * returned as an array of icfg_if_t structures. The number of interfaces in * the array will be returned via the 'numif' argument. Since the array of * interfaces is allocated by this API, the caller is responsible for freeing * the memory associated with this array by calling icfg_free_list(). * * The 'proto' argument is used by the caller to define which interfaces are * to be listed by the API. The possible values for proto are AF_INET, * AF_INET6, and AF_UNSPEC. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ static int get_plumbed_if_list(icfg_if_t **list, int *numif, int proto) { int sock; struct lifconf lifc; struct lifnum lifn; struct lifreq *lifrp; char *buf; unsigned bufsize; icfg_if_t *loc_list; int num; sa_family_t lifc_family; int lifc_flags = LIFC_NOXMIT; int syserr; int i; /* * Validate the protocol family. */ if ((proto != AF_UNSPEC) && (proto != AF_INET) && (proto != AF_INET6)) { errno = EINVAL; return (ICFG_FAILURE); } lifc_family = proto; /* * Open a socket. Note that the AF_INET domain seems to * support both IPv4 and IPv6. */ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { return (ICFG_FAILURE); } /* * Get the number of interfaces and allocate a buffer * large enough to allow the interfaces to be enumerated. */ lifn.lifn_family = lifc_family; lifn.lifn_flags = lifc_flags; if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) { syserr = errno; (void) close(sock); errno = syserr; return (ICFG_FAILURE); } num = lifn.lifn_count; bufsize = num * sizeof (struct lifreq); buf = malloc(bufsize); if (buf == NULL) { syserr = errno; (void) close(sock); errno = syserr; return (ICFG_FAILURE); } /* * Obtain a list of the interfaces. */ lifc.lifc_family = lifc_family; lifc.lifc_flags = lifc_flags; lifc.lifc_len = bufsize; lifc.lifc_buf = buf; if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) { syserr = errno; (void) close(sock); free(buf); errno = syserr; return (ICFG_FAILURE); } (void) close(sock); bufsize = num * sizeof (icfg_if_t); loc_list = malloc(bufsize); if (loc_list == NULL) { syserr = errno; free(buf); errno = syserr; return (ICFG_FAILURE); } lifrp = lifc.lifc_req; for (i = 0; i < num; i++, lifrp++) { (void) strlcpy(loc_list[i].if_name, lifrp->lifr_name, sizeof (loc_list[i].if_name)); if (lifrp->lifr_addr.ss_family == AF_INET) { loc_list[i].if_protocol = AF_INET; } else { loc_list[i].if_protocol = AF_INET6; } } *list = loc_list; *numif = num; free(buf); return (ICFG_SUCCESS); } typedef struct linklist { struct linklist *ll_next; char ll_name[DLPI_LINKNAME_MAX]; } linklist_t; typedef struct linkwalk { linklist_t *lw_list; int lw_num; int lw_err; } linkwalk_t; static boolean_t add_link_list(const char *link, void *arg) { linkwalk_t *lwp = (linkwalk_t *)arg; linklist_t *entry = NULL; if ((entry = calloc(1, sizeof (linklist_t))) == NULL) { lwp->lw_err = ENOMEM; return (B_TRUE); } (void) strlcpy(entry->ll_name, link, DLPI_LINKNAME_MAX); if (lwp->lw_list == NULL) lwp->lw_list = entry; else lwp->lw_list->ll_next = entry; lwp->lw_num++; return (B_FALSE); } /* * Returns a list of data links that can be plumbed. The list of interfaces is * returned as an array of icfg_if_t structures. The number of interfaces in * the array will be returned via the 'numif' argument. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ static int get_link_list(icfg_if_t **listp, int *numif) { linkwalk_t lw = {NULL, 0, 0}; linklist_t *entry, *next; icfg_if_t *list; int save_errno = 0; int ret = ICFG_FAILURE; dlpi_walk(add_link_list, &lw, 0); if (lw.lw_err != 0) { errno = lw.lw_err; goto done; } list = calloc(lw.lw_num, sizeof (icfg_if_t)); if (list == NULL) goto done; *listp = list; for (entry = lw.lw_list; entry != NULL; entry = entry->ll_next) { (void) strlcpy(list->if_name, entry->ll_name, sizeof (list->if_name)); list->if_protocol = AF_UNSPEC; list++; } *numif = lw.lw_num; ret = ICFG_SUCCESS; done: save_errno = errno; for (entry = lw.lw_list; entry != NULL; entry = next) { next = entry->ll_next; free(entry); } errno = save_errno; return (ret); } /* * Returns a list of network interfaces. The list of * interfaces is returned as an array of icfg_if_t structures. * The number of interfaces in the array will be returned via * the 'numif' argument. Since the array of interfaces is * allocated by this API, the caller is responsible for freeing * the memory associated with this array by calling * icfg_free_list(). * * The 'proto' argument is used by the caller to define which * interfaces are to be listed by the API. The possible values * for 'proto' are AF_INET, AF_INET6, and AF_UNSPEC. * * The 'type' argument is used by the caller specify whether * to enumerate installed network interfaces or plumbed * network interfaces. The value for 'type' can be ICFG_PLUMBED * or ICFG_INSTALLED. */ int icfg_get_if_list(icfg_if_t **list, int *numif, int proto, int type) { *list = NULL; *numif = 0; if (type == ICFG_PLUMBED) { return (get_plumbed_if_list(list, numif, proto)); } else if (type == ICFG_INSTALLED) { return (get_link_list(list, numif)); } else { errno = EINVAL; return (ICFG_FAILURE); } } /* * Frees the memory allocated by icfg_get_list(). */ void icfg_free_if_list(icfg_if_t *list) { free(list); list = NULL; } /* * Determines whether or not an interface name represents * a logical interface or not. * * Returns: B_TRUE if logical, B_FALSE if not. * * Note: this API can be vastly improved once interface naming * is resolved in the future. This will do for now. */ boolean_t icfg_is_logical(icfg_handle_t handle) { return (strchr(handle->ifh_interface.if_name, ICFG_LOGICAL_SEP) != NULL); } /* * Given a sockaddr representation of an IPv4 or IPv6 address returns the * string representation. Note that 'sockaddr' should point at the correct * sockaddr structure for the address family (sockaddr_in for AF_INET or * sockaddr_in6 for AF_INET6) or alternatively at a sockaddr_storage * structure. * * Returns: ICFG_SUCCESS or ICFG_FAILURE. */ int icfg_sockaddr_to_str(sa_family_t af, const struct sockaddr *sockaddr, char *straddr, size_t len) { const void *addr = sockaddr; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; const char *str; int ret = ICFG_FAILURE; if (af == AF_INET) { sin = (struct sockaddr_in *)addr; str = inet_ntop(AF_INET, (void *)&sin->sin_addr, straddr, len); } else if (af == AF_INET6) { sin6 = (struct sockaddr_in6 *)addr; str = inet_ntop(AF_INET6, (void *)&sin6->sin6_addr, straddr, len); } else { errno = EINVAL; return (ICFG_FAILURE); } if (str != NULL) { ret = ICFG_SUCCESS; } return (ret); } /* * Given a string representation of an IPv4 or IPv6 address returns the * sockaddr representation. Note that 'sockaddr' should point at the correct * sockaddr structure for the address family (sockaddr_in for AF_INET or * sockaddr_in6 for AF_INET6) or alternatively at a sockaddr_storage * structure. * * Returns: ICFG_SUCCESS, ICFG_BAD_ADDR or ICFG_FAILURE. */ int icfg_str_to_sockaddr(sa_family_t af, const char *straddr, struct sockaddr *sockaddr, socklen_t *addrlen) { void *addr = sockaddr; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; int ret; int err; if (af == AF_INET) { if (*addrlen < sizeof (*sin)) { errno = ENOSPC; return (ICFG_FAILURE); } *addrlen = sizeof (*sin); sin = (struct sockaddr_in *)addr; sin->sin_family = AF_INET; err = inet_pton(AF_INET, straddr, &sin->sin_addr); } else if (af == AF_INET6) { if (*addrlen < sizeof (*sin6)) { errno = ENOSPC; return (ICFG_FAILURE); } *addrlen = sizeof (*sin6); sin6 = (struct sockaddr_in6 *)addr; sin6->sin6_family = AF_INET6; err = inet_pton(AF_INET6, straddr, &sin6->sin6_addr); } else { errno = EINVAL; return (ICFG_FAILURE); } if (err == 0) { ret = ICFG_BAD_ADDR; } else if (err == 1) { ret = ICFG_SUCCESS; } else { ret = ICFG_FAILURE; } return (ret); }