/*
* 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
*/
/*
*/
#include <assert.h>
#include <auth_attr.h>
#include <errno.h>
#include <libdladm.h>
#include <libdliptun.h>
#include <libdllink.h>
#include <libintl.h>
#include <libipadm.h>
#include <libnetcfg.h>
#include <libnwam.h>
#include <libnwam_priv.h>
#include <nss_dbdefs.h>
#include <pwd.h>
#include <secdb.h>
#include <strings.h>
#include <unistd.h>
#include <zone.h>
#include "ipadm_ipmgmt.h"
#include "libipadm_impl.h"
char *, size_t);
/* 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_INSUFF_AUTH, "Insufficient user authorizations" },
{ IPADM_PERM_DENIED, "Permission denied" },
{ IPADM_NO_BUFS, "No buffer space available" },
{ IPADM_NO_MEMORY, "Insufficient memory" },
{ IPADM_BAD_ADDR, "Invalid address" },
{ IPADM_INVALID_IFID, "Invalid interface id" },
{ IPADM_BAD_PROTOCOL, "Incorrect protocol family for operation" },
{ IPADM_DAD_FOUND, "Duplicate address detected" },
{ IPADM_OBJ_EXISTS, "Object already exists" },
{ IPADM_IF_EXISTS, "Interface already exists" },
{ IPADM_ADDROBJ_EXISTS, "Address object already exists" },
{ IPADM_ADDRCONF_EXISTS, "Addrconf already in progress" },
{ IPADM_NOSUCH_IF, "No such interface" },
{ IPADM_GRP_NOTEMPTY, "IPMP group is not empty" },
{ IPADM_INVALID_ARG, "Invalid argument provided" },
{ IPADM_INVALID_IFNAME, "Invalid interface name" },
{ IPADM_DLPI_FAILURE, "Could not open DLPI link" },
{ IPADM_DLPI_NOLINK, "Datalink does not exist" },
{ IPADM_DLADM_FAILURE, "Datalink does not exist" },
{ IPADM_PROP_UNKNOWN, "Unknown property" },
{ IPADM_VAL_OUT_OF_RANGE, "Value is outside the allowed range" },
{ IPADM_VAL_NOTEXISTS, "Value does not exist" },
{ IPADM_VAL_OVERFLOW, "Number of values exceeds the allowed limit" },
{ IPADM_OBJ_NOTFOUND, "Object not found" },
{ IPADM_IF_INUSE, "Interface already in use" },
{ IPADM_ADDR_INUSE, "Address already in use" },
"Hostname maps to multiple IP addresses" },
{ IPADM_CANNOT_ASSIGN_ADDR, "Can't assign requested address" },
{ IPADM_NDPD_NOT_RUNNING, "IPv6 autoconf daemon in.ndpd not running" },
{ IPADM_NDPD_TIMEOUT, "Communication with in.ndpd timed out" },
{ IPADM_NDPD_IO_FAILURE, "I/O error with in.ndpd" },
{ IPADM_CANNOT_START_DHCP, "Could not start dhcpagent" },
{ IPADM_DHCP_IPC_FAILURE, "Communication with dhcpagent failed" },
{ IPADM_DHCP_INVALID_IF, "DHCP client could not run on the interface" },
{ IPADM_DHCP_IPC_TIMEOUT, "Communication with dhcpagent timed out" },
{ IPADM_TEMPORARY_OBJ, "Persistent operation on temporary object" },
{ IPADM_DAEMON_IPC_FAILURE, "Communication with daemon failed" },
{ IPADM_OP_DISABLE_OBJ, "Operation not supported on disabled object" },
{ IPADM_OP_NOTSUP, "Operation not supported" },
{ IPADM_INVALID_EXCH, "Invalid data exchange with daemon" },
{ IPADM_NOT_IPMPIF, "Not an IPMP interface" },
{ IPADM_NOSUCH_IPMPIF, "No such IPMP interface" },
{ IPADM_NOSUCH_UNDERIF, "No such underlying interface" },
{ IPADM_ALREADY_IN_GRP, "Already in an IPMP group" },
{ IPADM_NOT_IN_GROUP, "Interface not in given IPMP group" },
{ IPADM_IPMPIF_NOT_ENABLED, "IPMP interface is not enabled" },
{ IPADM_IPMPIF_DHCP_NOT_ENABLED, "DHCP data address is not enabled" },
{ IPADM_IF_NOT_FULLY_ENABLED, "Interface could not be fully enabled" },
"Underlying interface has addresses managed by external "
"applications" },
"Underlying interface has addresses managed by dhcpagent(1M)" },
"Underlying interface has addresses managed by in.ndpd(1M)" },
"Underlying interface has addresses marked up" },
"IPMP interface missing address families configured on "
"underlying interface" },
"Address objects could not be created for all of the migrated "
"addresses" },
{ IPADM_ROUTE_NOTFOUND, "Route not found" },
{ IPADM_ROUTE_EXISTS, "Route already exists" },
{ IPADM_ROUTE_FAILURE, "Failed to modify or delete a route" },
"Failed to create a global zone assigned default route" }
};
{
switch (error) {
case 0:
return (IPADM_SUCCESS);
case ENXIO:
return (IPADM_NOSUCH_IF);
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_OBJ_EXISTS);
case EADDRNOTAVAIL:
return (IPADM_CANNOT_ASSIGN_ADDR);
case EADDRINUSE:
return (IPADM_ADDR_INUSE);
case ENOENT:
return (IPADM_OBJ_NOTFOUND);
case ERANGE:
return (IPADM_VAL_OUT_OF_RANGE);
case EACCES:
case EPERM:
return (IPADM_PERM_DENIED);
case ENOTSUP:
case EOPNOTSUPP:
return (IPADM_OP_NOTSUP);
case EBADF:
return (IPADM_DAEMON_IPC_FAILURE);
case EBADE:
return (IPADM_INVALID_EXCH);
case ESRCH:
return (IPADM_VAL_NOTEXISTS);
case EOVERFLOW:
return (IPADM_VAL_OVERFLOW);
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));
}
}
const char *
{
switch (class) {
case IPADMIF_CLASS_IP:
return (IPADMIF_CLASS_IP_STR);
case IPADMIF_CLASS_IPMP:
return (IPADMIF_CLASS_IPMP_STR);
case IPADMIF_CLASS_LOOPBACK:
return (IPADMIF_CLASS_LOOPBACK_STR);
case IPADMIF_CLASS_VNI:
return (IPADMIF_CLASS_VNI_STR);
}
return ("unknown");
}
static ipadm_status_t
{
switch (dlstatus) {
case DLADM_STATUS_DENIED:
return (IPADM_PERM_DENIED);
case DLADM_STATUS_NOMEM:
return (IPADM_NO_MEMORY);
default:
return (IPADM_DLADM_FAILURE);
}
}
/*
* Opens a handle to libipadm.
* Possible values for flags:
* IH_VRRP: Used by VRRP daemon to set the socket option SO_VRRP.
* IH_LEGACY: This is used whenever an application needs to provide a
* logical interface name while creating or deleting
* interfaces and static addresses.
* IH_INIT: Used by ipadm_init_prop(), to initialize protocol properties
* on reboot.
*/
const char *owner)
{
return (IPADM_INVALID_ARG);
return (IPADM_INVALID_ARG);
if (!i_ipadm_is_owner_valid(owner))
return (IPADM_INVALID_ARG);
return (IPADM_NO_MEMORY);
!= NETCFG_SUCCESS) {
return (IPADM_INVALID_ARG);
}
} else {
}
return (IPADM_INVALID_ARG);
}
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;
}
}
!= DLADM_STATUS_OK) {
return (dladm2ipadm_status(dlstatus));
}
} 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 */
}
/*
* Determines whether or not the given interface name represents a loopback
* interface.
*
* Returns: B_TRUE if `ifname' is a loopback interface, B_FALSE if not.
*/
{
}
/*
* Determines whether or not an interface name represents a vni interface.
*
* Returns: B_TRUE if vni, B_FALSE if not.
*/
{
}
/*
* Determines if `ifname' is an interface of class IPADMIF_CLASS_IP.
*/
{
return (B_FALSE);
return (B_TRUE);
}
/*
* 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;
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 in `set' and clears the flags in `clear' 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 (status != IPADM_SUCCESS)
return (status);
/*
* 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) {
/*
* If the address is brought down, ensure that the DAD
* activity is stopped.
*/
return (ipadm_errno2status(err));
}
return (IPADM_SUCCESS);
} else {
/*
* We don't wait for DAD to complete for migrated IPMP
* data addresses.
*/
if (!(oflags & IFF_NOFAILOVER)) {
return (IPADM_SUCCESS);
}
}
/* Wait for DAD to complete. */
return (status);
}
}
/*
* Returns the flags value for the logical interface in `lifname'
* in the buffer pointed to by `flags'.
*/
{
int sock;
return (ipadm_errno2status(errno));
return (IPADM_SUCCESS);
}
/*
* 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);
}
{
return (ipadm_errno2status(errno));
return (IPADM_SUCCESS);
}
/*
* Returns B_TRUE if `ifname' represents an IPMP underlying interface.
*/
{
return (B_FALSE);
LIFGRNAMSIZ) == IPADM_SUCCESS)
return (grname[0] != '\0');
return (B_FALSE);
}
/*
* Returns B_TRUE if `ifname' represents an IPMP meta-interface.
*/
{
return (B_FALSE);
}
/*
* Checks if the IPMP interface in `ifname' is available in the persistent
* config.
*/
{
}
return (exists);
}
/*
* 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);
}
{
}
/*
* Returns the address family for which `ifname' exists in the active
* configuration. AF_INET is returned if the interface is configured for
* both IPv4 and IPv6. AF_UNSPEC is returned if the interface is not
* configured at all.
*/
{
return (AF_INET);
return (AF_INET6);
return (AF_UNSPEC);
}
/*
* Apply the interface property by retrieving information from nvl.
*/
static ipadm_status_t
{
int err = 0;
/* Retrieve the ifname and protocol from nvl first */
!= 0)
goto fail;
/* Loop through and set each property */
continue;
break;
if (status != IPADM_SUCCESS)
break;
}
fail:
if (err != 0)
return (status);
}
/*
* Instantiate the address object or set the address object property by
* retrieving the configuration from the nvlist `nvl'.
*/
{
char *name;
int err = 0;
/*
* Walk the aobj nvlist to extract the basics: ifname, aobjname,
* and addrtype. There may also be properties, and there will be
* more than one pair for details related to the addrtype; just
* skip those for now.
*/
break;
break;
} else if (atype == IPADM_ADDR_NONE) {
}
}
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;
}
if (status != IPADM_SUCCESS)
return (status);
/* Now look for (non-private) properties, and set those */
char *pval;
/*
* skip private properties, and others that were handled
* earlier when the address was set.
*/
continue;
}
return (ipadm_errno2status(err));
return (status);
}
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'.
*/
/* ARGSUSED */
{
char **afstrs;
char *aobjstr;
char *db_if;
/* First plumb the given interface */
continue;
continue;
&afcnt) == 0) {
/*
* If `ifname' is an underlying interface, look for
* the IPMP interface now. It is better done now
* than later after many addresses have been added,
* to avoid migrating the data addresses.
*/
if (status == IPADM_OBJ_NOTFOUND)
ipmpif[0] = '\0';
else if (status != IPADM_SUCCESS)
return (status);
for (i = 0; i < afcnt; i++) {
return (status);
}
break;
}
}
/*
* Then apply all the persistent interface properties
* and instantiate any persistent address objects.
*/
continue;
continue;
&aobjstr) == 0) {
/*
* 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_CANNOT_ASSIGN_ADDR) {
} else if (status == IPADM_DHCP_INVALID_IF) {
}
}
if (status != IPADM_SUCCESS)
return (status);
}
if (ipmpif[0] != '\0') {
if (status != IPADM_SUCCESS)
return (status);
}
return (ret_status);
}
/*
* Recreates the interface in `ifname' with the information provided in the
* nvlist `nvl'. If an IPMP interface is provided in `ipmpif', `ifname' is
* added to the IPMP group.
*/
{
int err;
int sock;
if (err != 0)
return (ipadm_errno2status(err));
/*
* If the interface is already plumbed, we should
* ignore this error because there might be address
* objects on that interface that need to be enabled again.
*/
if (status == IPADM_IF_EXISTS)
if (status != IPADM_SUCCESS)
return (status);
/*
* Put it in the IPMP group, if an IPMP interface is found in the
* persistent config.
*/
if (ipmpif[0] != '\0') {
/*
* The interface is an underlying interface for the
* IPMP interface in `ipmpif'.
*/
return (IPADM_IPMPIF_NOT_ENABLED);
return (ipadm_errno2status(errno));
if (status != IPADM_SUCCESS)
return (status);
}
return (IPADM_SUCCESS);
}
/*
* Look up the IPMP interface information from `invl' and copy it to `ipmpif'.
* IPADM_OBJ_NOTFOUND is returned when one is not found.
*/
static ipadm_status_t
{
int err, i;
/*
* check for `underif' in the list of
* unders for this interface.
*/
for (i = 0; i < ucnt; i++) {
break;
}
if (i == ucnt) {
/* no match */
continue;
}
/* found `underif'; get this entry's ifname */
&db_ifname);
if (err != 0)
return (ipadm_errno2status(err));
return (IPADM_SUCCESS);
}
}
return (IPADM_OBJ_NOTFOUND);
}
/*
* 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 IH_LEGACY is not set, or
*/
{
return (B_FALSE);
if (ifsp.ifsp_lunvalid)
return (B_TRUE);
}
{
/* Check for the required authorization */
if (!ipadm_check_auth())
return (IPADM_INSUFF_AUTH);
return (IPADM_INVALID_ARG);
return (IPADM_INVALID_ARG);
if (flags & IPADM_OPT_GENPPA) {
/*
* Return error if the interface name already has a ppa.
* If no ppa is provided, make an interface name with
* a dummy ppa to be fed to i_ipadm_validate_ifname()
* later in this function, to avoid failure from
* ifparse_spec().
*/
return (IPADM_INVALID_ARG);
return (IPADM_INVALID_ARG);
}
return (IPADM_INVALID_IFNAME);
return (IPADM_SUCCESS);
}
/*
* Returns B_TRUE if the given interface is IPADMIF_CLASS_IP.
*/
{
return (B_FALSE);
}
/*
* Get the persistent flags for the interface `ifname'.
*/
{
if (status == IPADM_SUCCESS) {
}
return (pflags);
}
/*
* 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 and 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, should set `rbufp' to NULL and set `rsize' to 0.
*/
static 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 ih_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);
}
/*
* Makes a door call to the server and `rbufp' is not re-allocated. If
* the data returned from the server can't be accomodated in `rbufp'
* that is of size `rsize' then an error will be returned.
*/
int
{
}
/*
* Makes a door call to the server and `rbufp' always points to a
* re-allocated memory and should be freed by the caller. This should be
* used by callers who are not sure of the size of the data returned by
* the server.
*/
int
{
B_TRUE));
}
/*
* Create a fixed profile with the provided name.
*/
int
{
int err;
name)) != NETCFG_SUCCESS) {
goto cleanup;
}
NWAM_MGMT_TYPE_FIXED)) != 0) {
goto cleanup;
}
return (err);
}
{
sizeof (rval)));
}
{
sizeof (rval)));
}