inetcfg.c revision 6ba597c56d749c61b4f783157f63196d7b2445f0
/*
* 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
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <stropts.h>
#include <libintl.h>
#include <libdladm.h>
#include <libdllink.h>
#include <libdlpi.h>
#include <libinetutil.h>
#include <zone.h>
#include <inetcfg.h>
#define ICFG_SOCKADDR_LEN(protocol) \
(socklen_t)sizeof (struct sockaddr_in) : \
(socklen_t)sizeof (struct sockaddr_in6)
#define ICFG_LOGICAL_SEP ':'
#define LOOPBACK_IF "lo0"
#define ARP_MOD_NAME "arp"
/*
* Maximum amount of time (in milliseconds) to wait for Duplicate Address
* Detection to complete in the kernel.
*/
#define DAD_WAIT_TIME 5000
/* error codes and text descriiption */
static struct icfg_error_info {
const char *error_desc;
} icfg_errors[] = {
{ ICFG_SUCCESS, "No error occurred" },
{ ICFG_FAILURE, "Generic failure" },
{ ICFG_NO_MEMORY, "Insufficient memory" },
{ ICFG_NOT_TUNNEL, "Tunnel operation attempted on non-tunnel" },
{ ICFG_NOT_SET, "Could not return non-existent value" },
{ ICFG_BAD_ADDR, "Invalid address" },
{ ICFG_BAD_PROTOCOL, "Wrong protocol family for operation" },
{ ICFG_DAD_FAILED, "Duplicate address detection failure" },
{ ICFG_DAD_FOUND, "Duplicate address detected" },
{ ICFG_IF_UP, "Interface is up" },
{ ICFG_EXISTS, "Interface already exists" },
{ ICFG_NO_EXIST, "Interface does not exist" },
{ ICFG_INVALID_ARG, "Invalid argument" },
{ ICFG_INVALID_NAME, "Invalid name" },
{ ICFG_DLPI_INVALID_LINK, "Link does not exist" },
{ ICFG_DLPI_FAILURE, "DLPI error" },
{ ICFG_NO_PLUMB_IP, "Could not plumb IP stream" },
{ ICFG_NO_PLUMB_ARP, "Could not plumb ARP stream" },
{ ICFG_NO_UNPLUMB_IP, "Could not unplumb IP stream" },
{ ICFG_NO_UNPLUMB_ARP, "Could not unplumb ARP stream" },
{ ICFG_NO_IP_MUX, "No IP mux set" },
{ 0, NULL }
};
/* convert libdlpi error to libinetcfg error */
{
switch (err) {
case DLPI_SUCCESS:
return (ICFG_SUCCESS);
case DLPI_ELINKNAMEINVAL:
return (ICFG_INVALID_NAME);
case DLPI_ENOLINK:
case DLPI_EBADLINK:
return (ICFG_DLPI_INVALID_LINK);
case DLPI_EINVAL:
case DLPI_ENOTSTYLE2:
case DLPI_EBADMSG:
case DLPI_EINHANDLE:
case DLPI_EVERNOTSUP:
case DLPI_EMODENOTSUP:
return (ICFG_INVALID_ARG);
case DL_BADADDR:
return (ICFG_BAD_ADDR);
case DL_SYSERR:
switch (errno) {
case ENOMEM:
return (ICFG_NO_MEMORY);
case EINVAL:
return (ICFG_INVALID_ARG);
}
/* FALLTHROUGH */
case DLPI_FAILURE:
default:
return (ICFG_DLPI_FAILURE);
}
}
/*
* 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
{
return (ICFG_FAILURE);
}
while (prefixlen > 0) {
if (prefixlen >= 8) {
*mask++ = 0xFF;
prefixlen -= 8;
continue;
}
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
{
return (ICFG_FAILURE);
}
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
{
return (ICFG_FAILURE);
}
return (ICFG_SUCCESS);
}
/*
* Return the appropriate error message for a given ICFG error.
*/
const char *
icfg_errmsg(int errcode)
{
int i;
return (dgettext(TEXT_DOMAIN,
icfg_errors[i].error_desc));
}
}
/*
* 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, ICFG_NO_MEMORY or ICFG_FAILURE.
*/
int
{
int sock;
int syserr;
/*
* Make sure that a valid protocol family was specified.
*/
return (ICFG_FAILURE);
}
return (ICFG_NO_MEMORY);
}
return (ICFG_FAILURE);
}
*handle = loc_handle;
return (ICFG_SUCCESS);
}
/*
* Closes the interface opened by icfg_open() and releases all resources
* associated with the handle.
*/
void
{
}
/*
* Retrieves the interface name associated with the handle passed in.
*/
const char *
{
}
/*
* Retrieves the protocol associated with the handle passed in.
*/
static int
{
}
/*
* 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
{
union {
char buf[1024];
} msg;
int index;
int retv;
return (retv);
for (;;) {
if (now >= DAD_WAIT_TIME)
break;
break;
break;
continue;
/* Note that ifm_index is just 16 bits */
return (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
{
int ret;
int rtsock = -1;
int aware = RTAW_UNDER_IPMP;
return (ret);
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 (rtsock != -1) {
sizeof (aware));
}
}
if (rtsock != -1)
return (ICFG_FAILURE);
}
if (rtsock == -1) {
return (ICFG_SUCCESS);
} else {
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
{
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
{
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
{
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_PROTOCOL or ICFG_FAILURE.
*/
int
{
int ret;
return (ICFG_BAD_PROTOCOL);
}
return (ret);
}
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_PROTOCOL or ICFG_FAILURE.
*/
int
{
int ret;
return (ICFG_BAD_PROTOCOL);
}
return (ret);
}
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
{
struct sockaddr_in6 *sin6;
int ret;
return (ret);
}
} else {
struct sockaddr_in *sin;
int ret;
return (ret);
}
}
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
{
int ret;
int rtsock = -1;
int aware = RTAW_UNDER_IPMP;
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.
*/
return (ret);
if (rtsock != -1) {
sizeof (aware));
}
}
if (rtsock != -1)
return (ICFG_FAILURE);
}
if (rtsock == -1) {
return (ICFG_SUCCESS);
} else {
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_PROTOCOL or ICFG_FAILURE.
*/
int
int prefixlen)
{
int ret;
return (ICFG_BAD_PROTOCOL);
}
return (ret);
}
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
{
int ret;
return (ret);
}
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
{
int ret;
return (ret);
}
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
{
int ret;
} else {
return (ICFG_FAILURE);
}
}
return (ret);
}
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_PROTOCOL or ICFG_FAILURE.
*/
int
{
return (ICFG_BAD_PROTOCOL);
}
} else {
return (ICFG_FAILURE);
}
}
}
/*
* 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
{
int ret;
} else {
return (ICFG_FAILURE);
}
}
return (ret);
}
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_PROTOCOL or ICFG_FAILURE.
*/
int
{
return (ICFG_BAD_PROTOCOL);
}
if (errno != EADDRNOTAVAIL) {
return (ICFG_FAILURE);
}
}
}
/*
* 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_PROTOCOL or ICFG_FAILURE.
*/
int
{
return (ICFG_BAD_PROTOCOL);
}
if (errno != EADDRNOTAVAIL) {
return (ICFG_FAILURE);
}
}
}
/*
* 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
{
if (errno != EADDRNOTAVAIL) {
return (ICFG_FAILURE);
}
/* No destination address set yet */
sizeof (lifr.lifr_dstaddr));
}
}
/*
* 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
{
return (ICFG_FAILURE);
}
} else {
return (ICFG_NOT_SET);
}
return (ICFG_SUCCESS);
}
/*
* Returns the groupinfo, if any, associated with the group identified in
* the gi_grname field of the passed-in lifgr structure. Upon successful
* return, the lifgr structure will be populated with the associated
* group info.
*
* Returns: ICFG_SUCCESS or ICFG_FAILURE.
*/
static int
{
return (ICFG_FAILURE);
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, ICFG_NO_EXIST or ICFG_FAILURE.
*/
int
{
return (ICFG_INVALID_ARG);
return (ICFG_NO_EXIST);
else
return (ICFG_FAILURE);
}
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
{
return (ICFG_INVALID_ARG);
return (ICFG_FAILURE);
}
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
{
return (ICFG_INVALID_ARG);
return (ICFG_FAILURE);
}
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
{
return (ICFG_INVALID_ARG);
return (ICFG_FAILURE);
}
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
{
int len;
int i;
int ret;
if (ret != ICFG_SUCCESS) {
return (ret);
}
for (i = 0; i < len; i++) {
break;
}
}
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_if_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, ICFG_BAD_PROTOCOL, ICFG_NO_MEMORY or ICFG_FAILURE.
*/
static int
int sock;
char *buf;
unsigned bufsize;
int num;
int lifc_flags = LIFC_NOXMIT;
int syserr;
int i;
/*
* Validate the protocol family.
*/
return (ICFG_BAD_PROTOCOL);
}
lifc_family = proto;
/*
* Open a socket. Note that the AF_INET domain seems to
* support both IPv4 and IPv6.
*/
return (ICFG_FAILURE);
}
/*
* Get the number of interfaces and allocate a buffer
* large enough to allow the interfaces to be enumerated.
*/
return (ICFG_FAILURE);
}
return (ICFG_NO_MEMORY);
}
/*
* Obtain a list of the interfaces.
*/
return (ICFG_FAILURE);
}
return (ICFG_NO_MEMORY);
}
} else {
}
}
return (ICFG_SUCCESS);
}
typedef struct linklist {
char ll_name[DLPI_LINKNAME_MAX];
} linklist_t;
typedef struct linkwalk {
int lw_num;
int lw_err;
} linkwalk_t;
static boolean_t
{
return (B_TRUE);
}
else
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, ICFG_NO_MEMORY or ICFG_FAILURE.
*/
static int
int save_errno = 0;
int ret = ICFG_FAILURE;
goto done;
}
/* no links found, nothing else to do */
*numif = 0;
return (ICFG_SUCCESS);
}
goto done;
}
list++;
}
ret = ICFG_SUCCESS;
done:
save_errno = errno;
}
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_if_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
{
return (ICFG_INVALID_ARG);
*numif = 0;
if (type == ICFG_PLUMBED) {
} else if (type == ICFG_INSTALLED) {
} else {
return (ICFG_FAILURE);
}
}
/*
* Frees the memory allocated by icfg_get_list().
*/
void
{
}
/*
* 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.
*/
{
!= NULL);
}
/*
* Determines whether or not an interface name represents a loopback
* interface or not.
*
* Returns: B_TRUE if loopback, B_FALSE if not.
*/
static boolean_t
{
}
/*
* 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
{
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
const char *str;
int ret = ICFG_FAILURE;
len);
} else {
return (ICFG_FAILURE);
}
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
{
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
int ret;
int err;
return (ICFG_FAILURE);
}
return (ICFG_FAILURE);
}
} else {
return (ICFG_FAILURE);
}
if (err == 0) {
ret = ICFG_BAD_ADDR;
} else if (err == 1) {
ret = ICFG_SUCCESS;
} else {
ret = ICFG_FAILURE;
}
return (ret);
}
/*
* Adds the IP address contained in the 'addr' argument to the physical
* interface represented by the handle passed in. At present,
* additional IP addresses assigned to a physical interface are
* represented as logical interfaces.
*
* If the 'handle' argument is a handle to a physical interface, a logical
* interface will be created, named by the next unused logical unit number
* for that physical interface.
*
* If the 'handle' argument is a handle to an logical interface, then that
* logical interface is the one that will be created. If the logical
* interface model is abandoned in the future, passing in a logical
* interface name should result in ICFG_UNSUPPORTED being returned.
*
* If the 'new_handle' argument is not NULL, then a handle is created for the
* new IP address alias and returned to the caller via 'new_handle'.
* At present this handle refers to a logical interface, but in the future
* it may represent an IP address alias, and be used for setting/retrieving
* address-related information only.
*
*
* Returns: ICFG_SUCCESS, ICFG_BAD_ADDR, ICFG_DAD_FOUND, ICFG_EXISTS
* or ICFG_FAILURE.
*/
int
{
int ret = ICFG_SUCCESS;
return (ICFG_BAD_ADDR);
case AF_INET:
addrsize = sizeof (struct sockaddr_in);
break;
case AF_INET6:
addrsize = sizeof (struct sockaddr_in6);
break;
default:
return (ICFG_BAD_ADDR);
}
return (ICFG_FAILURE);
}
/*
* See comments in ifconfig.c as to why this dance is necessary.
*/
return (ICFG_EXISTS);
else
return (ICFG_FAILURE);
}
/* Create the handle for the new interface name. */
if (ret != ICFG_SUCCESS) {
return (ret);
}
if (new_handle != NULL)
*new_handle = loc_handle;
else
return (ret);
}
/*
* Removes specified IP address alias from physical interface. If the
* If the 'handle' argument is a handle to a physical interface, then
* the address alias removed must be specified by 'addr'. If the
* 'handle' argument is a handle for an IP address alias (currently
* represented as a logical interface), then that address alias
* (logical interface) is removed and the 'addr' argument is ignored.
*
* Under the logical interface model, an interface may only be removed
* if the interface is 'down'.
*
* Returns: ICFG_SUCCESS, ICFG_BAD_ADDR, ICFG_IF_UP, ICFG_NO_EXIST,
* or ICFG_FAILURE.
*/
int
{
switch (icfg_if_protocol(handle)) {
case AF_INET:
addrsize = sizeof (struct sockaddr_in);
break;
case AF_INET6:
addrsize = sizeof (struct sockaddr_in6);
break;
default:
return (ICFG_BAD_ADDR);
}
return (ICFG_FAILURE);
}
} else {
}
return (ICFG_FAILURE);
return (ICFG_SUCCESS);
}
/*
* Wrapper for sending a nontransparent I_STR ioctl().
* Returns: Result from ioctl().
*
*/
static int
{
}
/*
* the user may have configured autopush to add modules above
* udp), and push the arp module onto the resulting stream.
* This is used to make IP+ARP be able to atomically track the muxid
* for the I_PLINKed STREAMS, thus it isn't related to ARP running the ARP
* protocol.
*
*/
static int
open_arp_on_udp(char *udp_dev_name)
{
int fd;
return (-1);
errno = 0;
;
return (fd);
return (-1);
}
/*
* We need to plink both the arp-device stream and the arp-ip-device stream.
* However the muxid is stored only in IP. Plumbing 2 streams individually
* is not atomic, and if ifconfig is killed, the resulting plumbing can
* be inconsistent. For eg. if only the arp stream is plumbed, we have lost
* the muxid, and the half-baked plumbing can neither be unplumbed nor
* replumbed, thus requiring a reboot. To avoid the above the following
* scheme is used.
*
* We ask IP to enforce atomicity of plumbing the arp and IP streams.
* extra information in the I_PLINK and I_PUNLINK ioctls to let IP know
* that the plumbing/unplumbing has to be done atomically. Ifconfig plumbs
* the IP stream first, and unplumbs it last. The kernel (IP) does not
* allow IP stream to be unplumbed without unplumbing arp stream. Similarly
* it does not allow arp stream to be plumbed before IP stream is plumbed.
* There is no need to use SIOCSLIFMUXID, since the whole operation is atomic,
* and IP uses the info in the I_PLINK message to get the muxid.
*
* b. SIOCGLIFMUXID returns the muxid corresponding to the V4 or V6 stream
* c. We need to push ARP in order to get the required kernel support for
* atomic plumbings. The actual work done by ARP is explained in arp.c
* it is not atomic, and is supported by the kernel for backward
* compatibility for other utilities like atmifconfig etc. In this case
* the utility must use SIOCSLIFMUXID.
*
* Returns: ICFG_SUCCESS, ICFG_EXISTS, ICFG_BAD_ADDR, ICFG_FAILURE,
* ICFG_DLPI_*, ICFG_NO_PLUMB_IP, ICFG_NO_PLUMB_ARP,
* ICFG_NO_UNPLUMB_ARP
*/
int
{
int ip_muxid;
char *udp_dev_name;
char provider[DLPI_LINKNAME_MAX];
int saved_errno; /* to set errno after close() */
int dh_arp_ret; /* to track if dh_arp was successfully opened */
/* Logical and loopback interfaces are just added */
/*
* If we're running in the global zone, we need to
* make sure this link is actually assigned to us.
*
* This is not an issue if we are not in the global
* zone, as we simply can't see links we don't own.
*/
if (zoneid == GLOBAL_ZONEID) {
return (ICFG_FAILURE);
if (status != DLADM_STATUS_OK)
return (ICFG_INVALID_ARG);
return (ICFG_INVALID_ARG);
}
/*
* We use DLPI_NOATTACH because the ip module will do the attach
* itself for DLPI style-2 devices.
*/
DLPI_NOATTACH)) != DLPI_SUCCESS) {
return (dlpi_error_to_icfg_error(dlpi_ret));
}
&ppa)) != DLPI_SUCCESS) {
goto done;
}
goto done;
}
/*
* Push the ARP module onto the interface stream. IP uses
* this to send resolution requests up to ARP. We need to
* do this before the SLIFNAME ioctl is sent down because
* the interface becomes publicly known as soon as the SLIFNAME
* ioctl completes. Thus some other process trying to bring up
* the interface after SLIFNAME but before we have pushed ARP
* could hang. We pop the module again later if it is not needed.
*/
goto done;
}
/*
* Set appropriate IFF flags. The kernel only allows us to
* modify IFF_IPv[46], IFF_BROADCAST, and IFF_XRESOLV in the
* SIOCSLIFNAME ioctl call; so we only need to set the ones
* from that set that we care about.
*/
else
/* record the device and module names as interface name */
/* set the interface name */
ret = ICFG_EXISTS;
else
goto done;
}
/* Get the full set of existing flags for this stream */
ret = ICFG_NO_EXIST;
else
ret = ICFG_FAILURE;
goto done;
}
/* Check if arp is not actually needed */
goto done;
}
}
/*
* since STREAMS will not let you PLINK a driver under itself,
* the stream for tunneling interfaces.
*/
else
goto done;
}
/* Check if arp is not needed */
/*
* PLINK the interface stream so that ifconfig can exit
* without tearing down the stream.
*/
goto done;
}
return (ICFG_SUCCESS);
}
/*
* This interface does use ARP, so set up a separate stream
* from the interface to ARP.
*
* Note: modules specified by the user are pushed
* only on the interface stream, not on the ARP stream.
*
* We use DLPI_NOATTACH because the arp module will do the attach
* itself for DLPI style-2 devices.
*/
DLPI_NOATTACH)) != DLPI_SUCCESS) {
goto done;
}
goto done;
}
/*
* Tell ARP the name and unit number for this interface.
* Note that arp has no support for transparent ioctls.
*/
sizeof (lifr)) == -1) {
goto done;
}
/*
* PLINK the IP and ARP streams so that ifconfig can exit
* without tearing down the stream.
*/
goto done;
}
}
done:
/* dlpi_close() may change errno, so save it */
saved_errno = errno;
if (dh_arp_ret == DLPI_SUCCESS)
if (mux_fd != -1)
if (ret != ICFG_SUCCESS)
errno = saved_errno;
return (ret);
}
static boolean_t
{
return (B_FALSE);
return (B_FALSE);
goto done;
}
/*
* Make sure that DAD activity (observable by IFF_DUPLICATE)
* has also been stopped. If we were successful in downing
* the address, the get_flags will fail, as the addr will no
* longer exist.
*/
addrflags & IFF_DUPLICATE) {
struct sockaddr_storage ss;
int plen;
/*
* we've already turned off IFF_UP, DAD will remain
* disabled.
*/
B_FALSE) != ICFG_SUCCESS) ||
!= ICFG_SUCCESS)) {
goto done;
}
}
done:
return (ret);
}
/*
* If this is a physical interface then remove it.
* If it is a logical interface name use SIOCLIFREMOVEIF to
* remove it. In both cases fail if it doesn't exist.
*
* Returns: ICFG_SUCCESS, ICFG_EXISTS, ICFG_NO_EXIST, ICFG_BAD_ADDR,
* ICFG_FAILURE, ICFG_NO_UNPLUMB_IP, ICFG_NO_UNPLUMB_ARP,
* ICFG_INVALID_ARG, ICFG_NO_IP_MUX
*
*/
int
{
int mux_fd;
int muxid_fd;
char *udp_dev_name;
int save_errno;
int ret = ICFG_SUCCESS;
/* Make sure interface exists to start with */
return (ret);
}
/* Can't unplumb logical interface zero */
return (ICFG_INVALID_ARG);
}
/*
* the same now for PUNLINK also.
*/
if (v6)
else
return (ICFG_NO_UNPLUMB_ARP);
goto done;
}
ret = ICFG_FAILURE;
goto done;
}
/*
* libinetcfg's only current consumer is nwamd; and we expect it to
* be replaced before any other consumers come along. NWAM does not
* currently support creation of IPMP groups, so icfg_plumb(), for
* example, does not do any IPMP-specific handling. However, it's
* possible nwamd might need to unplumb an IPMP group, so we include
* IPMP group handling here.
*/
/*
* There are two reasons the I_PUNLINK can fail with EBUSY:
* (1) if IP interfaces are in the group, or (2) if IPMP data
* addresses are administratively up. For case (1), we fail
* here with a specific error message. For case (2), we bring
* down the addresses prior to doing the I_PUNLINK. If the
* I_PUNLINK still fails with EBUSY then the configuration
* must have changed after our checks, in which case we branch
* back up to `again' and rerun this logic. The net effect is
* that unplumbing an IPMP interface will only fail with EBUSY
* if IP interfaces are in the group.
*/
if (ret != ICFG_SUCCESS)
return (ret);
if (ret != ICFG_SUCCESS)
return (ret);
/* make sure the group is empty */
return (ICFG_INVALID_ARG);
/*
* The kernel will fail the I_PUNLINK if the IPMP interface
* has administratively up addresses; bring 'em down.
*/
0, &ifaddrs) == -1)
return (ICFG_FAILURE);
continue;
if (!ifaddr_down(ifaddrp)) {
return (ICFG_FAILURE);
}
}
}
goto done;
}
/*
* We don't have a good way of knowing whether the arp stream is
* plumbed. We can't rely on IFF_NOARP because someone could
* have turned it off later using "ifconfig xxx -arp".
*/
if (arp_muxid != 0) {
/*
* See the comment before the icfg_get_groupname() call.
*/
goto again;
/*
* Some plumbing utilities set the muxid to
* -1 or some invalid value to signify that
* there is no arp stream. Set the muxid to 0
* before trying to unplumb the IP stream.
* IP does not allow the IP stream to be
* unplumbed if it sees a non-null arp muxid,
* for consistency of IP-ARP streams.
*/
lifr.lifr_arp_muxid = 0;
} else {
}
}
}
if (changed_arp_muxid) {
/*
* Some error occurred, and we need to restore
* everything back to what it was.
*/
save_errno = errno;
errno = save_errno;
}
/*
* See the comment before the icfg_get_groupname() call.
*/
goto again;
}
done:
/* close() may change errno, so save it */
save_errno = errno;
if (mux_fd != -1)
if (ret != ICFG_SUCCESS)
errno = save_errno;
return (ret);
}