/*
* 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 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stropts.h>
#include <libintl.h>
#include <libdlpi.h>
#include <libinetutil.h>
#include <libdladm.h>
#include <libdllink.h>
#include <libdliptun.h>
#include <strings.h>
#include <zone.h>
#include <ctype.h>
#include <limits.h>
#include <assert.h>
#include <netdb.h>
#include <pwd.h>
#include <auth_attr.h>
#include <secdb.h>
#include <nss_dbdefs.h>
#include "libipadm_impl.h"
/* error codes and text description */
static struct ipadm_error_info {
const char *error_desc;
} ipadm_errors[] = {
{ IPADM_SUCCESS, "Operation succeeded" },
{ IPADM_FAILURE, "Operation failed" },
{ IPADM_EAUTH, "Insufficient user authorizations" },
{ IPADM_EPERM, "Permission denied" },
{ IPADM_NO_BUFS, "No buffer space available" },
{ IPADM_NO_MEMORY, "Insufficient memory" },
{ IPADM_BAD_ADDR, "Invalid address" },
{ IPADM_BAD_PROTOCOL, "Incorrect protocol family for operation" },
{ IPADM_DAD_FOUND, "Duplicate address detected" },
{ IPADM_EXISTS, "Already exists" },
{ IPADM_IF_EXISTS, "Interface already exists" },
{ IPADM_ADDROBJ_EXISTS, "Address object already exists" },
{ IPADM_ADDRCONF_EXISTS, "Addrconf already in progress" },
{ IPADM_ENXIO, "Interface does not exist" },
{ IPADM_GRP_NOTEMPTY, "IPMP group is not empty" },
{ IPADM_INVALID_ARG, "Invalid argument provided" },
{ IPADM_INVALID_NAME, "Invalid name" },
{ IPADM_DLPI_FAILURE, "Could not open DLPI link" },
{ IPADM_DLADM_FAILURE, "Datalink does not exist" },
{ IPADM_PROP_UNKNOWN, "Unknown property" },
{ IPADM_ERANGE, "Value is outside the allowed range" },
{ IPADM_ESRCH, "Value does not exist" },
{ IPADM_EOVERFLOW, "Number of values exceeds the allowed limit" },
{ IPADM_NOTFOUND, "Object not found" },
{ IPADM_IF_INUSE, "Interface already in use" },
{ IPADM_ADDR_INUSE, "Address already in use" },
{ IPADM_BAD_HOSTNAME, "Hostname maps to multiple IP addresses" },
{ IPADM_ADDR_NOTAVAIL, "Can't assign requested address" },
{ IPADM_ALL_ADDRS_NOT_ENABLED, "All addresses could not be enabled" },
{ IPADM_NDPD_NOT_RUNNING, "IPv6 autoconf daemon in.ndpd not running" },
{ IPADM_DHCP_START_ERROR, "Could not start dhcpagent" },
{ IPADM_DHCP_IPC_ERROR, "Could not communicate with dhcpagent" },
{ IPADM_DHCP_IPC_TIMEOUT, "Communication with dhcpagent timed out" },
{ IPADM_TEMPORARY_OBJ, "Persistent operation on temporary object" },
{ IPADM_IPC_ERROR, "Could not communicate with ipmgmtd" },
{ IPADM_NOTSUP, "Operation not supported" },
{ IPADM_OP_DISABLE_OBJ, "Operation not supported on disabled object" },
{ IPADM_EBADE, "Invalid data exchange with daemon" },
{ IPADM_GZ_PERM, "Operation not permitted on from-gz interface"}
};
{
switch (error) {
case 0:
return (IPADM_SUCCESS);
case ENXIO:
return (IPADM_ENXIO);
case ENOMEM:
return (IPADM_NO_MEMORY);
case ENOBUFS:
return (IPADM_NO_BUFS);
case EINVAL:
return (IPADM_INVALID_ARG);
case EBUSY:
return (IPADM_IF_INUSE);
case EEXIST:
return (IPADM_EXISTS);
case EADDRNOTAVAIL:
return (IPADM_ADDR_NOTAVAIL);
case EADDRINUSE:
return (IPADM_ADDR_INUSE);
case ENOENT:
return (IPADM_NOTFOUND);
case ERANGE:
return (IPADM_ERANGE);
case EPERM:
return (IPADM_EPERM);
case ENOTSUP:
case EOPNOTSUPP:
return (IPADM_NOTSUP);
case EBADF:
return (IPADM_IPC_ERROR);
case EBADE:
return (IPADM_EBADE);
case ESRCH:
return (IPADM_ESRCH);
case EOVERFLOW:
return (IPADM_EOVERFLOW);
default:
return (IPADM_FAILURE);
}
}
/*
* Returns a message string for the given libipadm error status.
*/
const char *
{
int i;
for (i = 0; i < IPADM_NUM_ERRORS; i++) {
return (dgettext(TEXT_DOMAIN,
ipadm_errors[i].error_desc));
}
}
/*
* Opens a handle to libipadm.
* Possible values for flags:
* IPH_VRRP: Used by VRRP daemon to set the socket option SO_VRRP.
* IPH_LEGACY: This is used whenever an application needs to provide a
* logical interface name while creating or deleting
* interfaces and static addresses.
* IPH_INIT: Used by ipadm_init_prop(), to initialize protocol properties
* on reboot.
*/
{
return (IPADM_INVALID_ARG);
return (IPADM_INVALID_ARG);
return (IPADM_NO_MEMORY);
goto errnofail;
}
/*
* We open a handle to libdladm here, to facilitate some daemons (like
* nwamd) which opens handle to libipadm before devfsadmd installs the
* right device permissions into the kernel and requires "all"
* privileges to open DLD_CONTROL_DEV.
*
* In a non-global shared-ip zone there will be no DLD_CONTROL_DEV node
* and dladm_open() will fail. So, we avoid this by not calling
* dladm_open() for such zones.
*/
if (zoneid != GLOBAL_ZONEID) {
sizeof (zflags)) < 0) {
goto errnofail;
}
}
return (IPADM_DLADM_FAILURE);
}
if (zoneid != GLOBAL_ZONEID) {
/*
* Failure to open rtsock is ignored as this is
* only used in non-global zones to initialize
* routing socket information.
*/
}
} else {
}
goto errnofail;
}
}
return (status);
return (status);
}
/*
* Closes and frees the libipadm handle.
*/
void
{
return;
}
/*
* Checks if the caller has the authorization to configure network
* interfaces.
*/
ipadm_check_auth(void)
{
/* get the password entry for the given user ID */
return (B_FALSE);
/* check for presence of given authorization */
}
/*
* Stores the index value of the interface in `ifname' for the address
* family `af' into the buffer pointed to by `index'.
*/
static ipadm_status_t
int *index)
{
int sock;
else
return (ipadm_errno2status(errno));
return (IPADM_SUCCESS);
}
/*
* Maximum amount of time (in milliseconds) to wait for Duplicate Address
* Detection to complete in the kernel.
*/
/*
* Any time that flags are changed on an interface where either the new or the
* existing flags have IFF_UP set, we'll get a RTM_NEWADDR message to
* announce the new address added and its flag status.
* We wait here for that message and look for IFF_UP.
* If something's amiss with the kernel, though, we don't wait forever.
* (Note that IFF_DUPLICATE is a high-order bit, and we cannot see
* it in the routing socket messages.)
*/
static ipadm_status_t
int rtsock)
{
union {
} msg;
int index;
if (retv != IPADM_SUCCESS)
return (retv);
for (;;) {
if (now >= DAD_WAIT_TIME)
break;
break;
break;
continue;
/* Note that ifm_index is just 16 bits */
return (IPADM_SUCCESS);
}
if (retv != IPADM_SUCCESS)
return (retv);
if (flags & IFF_DUPLICATE)
return (IPADM_DAD_FOUND);
return (IPADM_SUCCESS);
}
/*
* Sets the flags `on_flags' and resets the flags `off_flags' for the logical
* interface in `lifname'.
*
* 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.
*/
{
if (ret != IPADM_SUCCESS)
return (ret);
/*
* Any time flags are changed on an interface that has IFF_UP set,
* we get a routing socket message. We care about the status,
* though, only when the new flags are marked "up."
*/
if (rtsock != -1)
return (ipadm_errno2status(err));
}
if (rtsock == -1) {
return (IPADM_SUCCESS);
} else {
/* Wait for DAD to complete. */
return (ret);
}
}
/*
* Returns the flags value for the logical interface in `lifname'
* in the buffer pointed to by `flags'.
*/
{
int sock;
else
return (ipadm_errno2status(errno));
}
return (IPADM_SUCCESS);
}
/*
* Determines whether or not an interface name represents a loopback
* interface, before the interface has been plumbed.
* It is assumed that the interface name in `ifname' is of correct format
* as verified by ifparse_ifspec().
*
* Returns: B_TRUE if loopback, B_FALSE if not.
*/
{
}
/*
* Determines whether or not an interface name represents a vni
* interface, before the interface has been plumbed.
* It is assumed that the interface name in `ifname' is of correct format
* as verified by ifparse_ifspec().
*
* Returns: B_TRUE if vni, B_FALSE if not.
*/
{
}
/*
* Returns B_TRUE if `ifname' is an IP interface on a 6to4 tunnel.
*/
{
return (B_FALSE);
}
if (dlstatus == DLADM_STATUS_OK &&
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* Returns B_TRUE if `ifname' represents an IPMP underlying interface.
*/
{
return (B_FALSE);
}
}
}
/*
* Returns B_TRUE if `ifname' represents an IPMP meta-interface.
*/
{
return (B_FALSE);
}
/*
* For a given interface name, ipadm_if_enabled() checks if v4
* or v6 or both IP interfaces exist in the active configuration.
*/
{
switch (af) {
case AF_INET:
return (B_TRUE);
break;
case AF_INET6:
return (B_TRUE);
break;
case AF_UNSPEC:
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* Apply the interface property by retrieving information from nvl.
*/
static ipadm_status_t
{
int err = 0;
break;
break;
} else {
break;
}
}
if (err != 0)
return (ipadm_errno2status(err));
}
/*
* Instantiate the address object or set the address object property by
* retrieving the configuration from the nvlist `nvl'.
*/
{
char *name;
int err = 0;
break;
break;
break;
} else {
break;
}
}
if (err != 0)
return (ipadm_errno2status(err));
switch (atype) {
case IPADM_ADDR_STATIC:
break;
case IPADM_ADDR_DHCP:
if (status == IPADM_DHCP_IPC_TIMEOUT)
break;
case IPADM_ADDR_IPV6_ADDRCONF:
break;
case IPADM_ADDR_NONE:
break;
}
return (status);
}
/*
* Instantiate the interface object by retrieving the configuration from
* `ifnvl'. The nvlist `ifnvl' contains all the persistent configuration
* (interface properties and address objects on that interface) for the
* given `ifname'.
*/
{
char *afstr;
char *aobjstr;
/*
* First plumb the given interface and then apply all the persistent
* interface properties and then instantiate any persistent addresses
* objects on that interface.
*/
continue;
/*
* If the interface is already plumbed, we should
* ignore this error because there might be address
* address objects on that interface that needs to
* be enabled again.
*/
if (status == IPADM_IF_EXISTS)
if (is_ngz)
&aobjstr) == 0) {
/*
* For a static address, we need to search for
* the prefixlen in the nvlist `ifnvl'.
*/
if (status != IPADM_SUCCESS)
continue;
}
/*
* If this address is in use on some other interface,
* we want to record an error to be returned as
* a soft error and continue processing the rest of
* the addresses.
*/
if (status == IPADM_ADDR_NOTAVAIL) {
}
} else {
}
if (status != IPADM_SUCCESS)
return (status);
}
return (ret_status);
}
/*
* Retrieves the persistent configuration for the given interface(s) in `ifs'
* by contacting the daemon and dumps the information in `allifs'.
*/
{
int err;
return (ipadm_errno2status(err));
if (err != 0) {
goto done;
}
goto done;
}
/* populate the door_call argument structure */
goto done;
}
goto done;
}
/*
* Daemon reply pointed to by rvalp contains ipmgmt_get_rval_t structure
* followed by a list of packed nvlists, each of which represents
* configuration information for the given interface(s).
*/
if (err != 0)
done:
return (status);
}
/*
* Returns B_FALSE if
* (1) `ifname' is NULL or has no string or has a string of invalid length
* (2) ifname is a logical interface and IPH_LEGACY is not set, or
*/
{
return (B_FALSE);
if (ifsp.ifsp_lunvalid)
return (B_TRUE);
}
/*
* Wrapper for sending a non-transparent I_STR ioctl().
* Returns: Result from ioctl().
*/
int
{
}
/*
* Make a door call to the server and checks if the door call succeeded or not.
* `is_varsize' specifies that the data returned by ipmgmtd daemon is of
* variable size and door will allocate buffer using mmap(). In such cases
* we re-allocate the required memory,n assign it to `rbufp', copy the data to
* `rbufp' and then call munmap() (see below).
*
* It also checks to see if the server side procedure ran successfully by
* checking for ir_err. Therefore, for some callers who just care about the
* return status can set `rbufp' to NULL and set `rsize' to 0.
*/
int
{
int err;
}
/* The door descriptor is opened if it isn't already */
return (err);
}
}
/*
* Stale door descriptor is possible if ipmgmtd was restarted
* since last iph_door_fd was opened, so try re-opening door
* descriptor.
*/
goto reopen;
}
return (errno);
}
/*
* if the caller is expecting the result to fit in specified
* buffer then return failure.
*/
if (!is_varsize)
/*
* The size of the buffer `*rbufp' was not big enough
* and the door itself allocated buffer, for us. We will
* hit this, on several occasion as for some cases
* we cannot predict the size of the return structure.
* Reallocate the buffer `*rbufp' and memcpy() the contents
* to new buffer.
*/
if (err == 0) {
void *newp;
/* allocated memory will be freed by the caller */
} else {
}
}
/* munmap() the door buffer */
} else {
}
return (err);
}