/*
* 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
*/
/*
*/
/*
* This file contains functions for address management such as creating
* an address, deleting an address, enabling an address, disabling an
* on an address object and listing address information
* for all addresses in active as well as persistent configuration.
*/
#include <assert.h>
#include <ctype.h>
#include <dhcp_inittab.h>
#include <dhcpagent_ipc.h>
#include <dhcpagent_util.h>
#include <errno.h>
#include <ipadm_ndpd.h>
#include <libdlpi.h>
#include <libnetcfg.h>
#include <libnwam.h>
#include <string.h>
#include <strings.h>
#include <stropts.h>
#include <unistd.h>
#include <zone.h>
#include "libipadm_impl.h"
uint32_t);
uint32_t);
const char *, nvlist_t **);
int *, dhcp_ipc_reply_t **);
uint32_t *);
ipadm_addrobj_t, boolean_t *);
static boolean_t i_ipadm_is_user_aobjname_valid(const char *);
size_t);
size_t, char **);
/*
* Callback functions to retrieve property values from the kernel. These
* functions, when required, translate the values from the kernel to a format
* suitable for printing. They also retrieve DEFAULT, PERM and POSSIBLE values
* for a given property.
*/
/*
* Callback functions to set property values. These functions translate the
* values to a format suitable for kernel consumption, allocate the necessary
* ioctl buffers and then invoke ioctl().
*/
/* address properties description table */
};
/*
* Helper function that initializes the `ipadm_ifname', `ipadm_aobjname', and
* `ipadm_atype' fields of the given `ipaddr'.
*/
void
{
sizeof (ipaddr->ipadm_ifname));
sizeof (ipaddr->ipadm_aobjname));
}
/*
* Determine the permission of the property depending on whether it has a
*/
{
return (IPADM_SUCCESS);
}
/*
* Retrieves the address object information from persistent db for the
* address object name in `ipaddr->ipadm_aobjname' and fills the type, flags,
* and address family fields of `ipaddr'.
*/
static ipadm_status_t
{
/*
* Get the address line in the nvlist `onvl' from ipmgmtd daemon.
*/
if (status != IPADM_SUCCESS)
return (status);
/*
* Fill the relevant fields of `ipaddr' using info from the nvlist.
*/
goto fail;
} else {
goto fail;
}
return (IPADM_SUCCESS);
fail:
return (IPADM_OBJ_NOTFOUND);
}
/*
* Given an addrobj with `ipadm_aobjname' filled in, i_ipadm_get_addrobj()
* retrieves the information necessary for any operation on the object,
* such as delete-addr, enable-addr, disable-addr, up-addr, down-addr,
* refresh-addr, get-addrprop or set-addrprop. The information include
* the logical interface number, address type, address family,
* the interface id (if the address type is IPADM_ADDR_IPV6_ADDRCONF) and
* the ipadm_flags that indicate if the address is present in
* active configuration or persistent configuration or both. If the address
* is not found, IPADM_OP_NOTSUP is returned.
*/
{
int err = 0;
/* populate the door_call argument structure */
sizeof (larg.ia_aobjname));
if (err == 0) {
sizeof (ipaddr->ipadm_ifname));
sizeof (ipaddr->ipadm_intfid));
}
/*
* We will get ENOENT, if the addrobj was created persistently
* but is disabled and is also not available in the aobjmap
* of ipmgmtd daemon. For this case, we attempt to retrieve it
* from persistent db.
*/
if (status != IPADM_SUCCESS)
return (status);
} else {
return (ipadm_errno2status(err));
}
return (IPADM_SUCCESS);
}
/*
* Retrieves the static address (IPv4 or IPv6) for the given address object
* in `ipaddr' from persistent DB.
*/
static ipadm_status_t
{
char *up_str;
int err;
/*
* Get the address line in the nvlist `onvl' from ipmgmtd daemon.
*/
if (status != IPADM_SUCCESS)
return (status);
/*
* onvl should contain exactly one pair, an nvlist which represents
* this aobjname's entry in the db. Verify that that nvlist contains
* static address properties.
*/
continue;
break;
break;
}
}
goto done;
}
goto done;
}
goto done;
&dname) == 0) {
if (status != IPADM_SUCCESS)
goto done;
}
if (err != 0) {
goto done;
}
}
done:
return (status);
}
/*
* Retrieves the addrconf address object for the address object name
* in `ipaddr' from persistent DB.
*/
static ipadm_status_t
{
/* Get the db entry for this addrobj */
if (status != IPADM_SUCCESS)
return (status);
/* Extract the INTFID-related properties, if present */
if (status != IPADM_SUCCESS)
goto done;
done:
return (status);
}
/*
* For the given `addrobj->ipadm_lifnum' and `addrobj->ipadm_af', this function
* fills in the address objname, the address type and the ipadm_flags.
*/
{
int err;
if (err != 0)
return (ipadm_errno2status(err));
sizeof (addrobj->ipadm_ifname));
sizeof (addrobj->ipadm_aobjname));
return (IPADM_SUCCESS);
}
/*
* Adds an addrobj to ipmgmtd daemon's aobjmap (active configuration).
* with the given name and logical interface number.
* This API is called by in.ndpd to add addrobjs when new prefixes or
* dhcpv6 addresses are configured.
*/
{
int err;
return (ipadm_errno2status(err));
}
/*
* Deletes an address object with given name and logical number from ipmgmtd
* daemon's aobjmap (active configuration). This API is called by in.ndpd to
* remove addrobjs when auto-configured prefixes or dhcpv6 addresses are
* removed.
*/
{
}
/*
* Gets all the addresses from active configuration and populates the
* address information in `addrinfo'.
*/
static ipadm_status_t
{
int sock;
/* Get all the configured addresses */
return (ipadm_errno2status(errno));
/* Return if there is nothing to process. */
return (IPADM_SUCCESS);
continue;
if (!(ipadm_flags & IPADM_OPT_ZEROADDR) &&
continue;
/* Allocate and populate the current node in the list. */
goto fail;
}
/* Link to the list in `addrinfo'. */
else
goto fail;
}
goto fail;
}
sizeof (struct sockaddr_storage));
goto fail;
}
sizeof (struct sockaddr_storage));
sizeof (struct sockaddr_storage));
goto fail;
}
sizeof (struct sockaddr_storage));
sizeof (struct sockaddr_storage));
goto fail;
}
sizeof (struct sockaddr_storage));
}
/* Get the addrobj name stored for this logical interface. */
/*
* Find address type from ifa_flags, if we could not get it
* from daemon.
*/
sizeof (struct sockaddr_in6));
if (status == IPADM_SUCCESS) {
sizeof (curr->ia_aobjname));
} else {
sizeof (ipaddr.ipadm_ifname));
if ((flags & IFF_DHCPRUNNING) &&
} else if (flags & IFF_ADDRCONF) {
} else {
}
}
/*
* Populate the flags for the active configuration from the
* `ifa_flags'.
*/
if (flags & IFF_DUPLICATE)
else
} else {
if (flags & IFF_RUNNING) {
goto retry;
}
goto fail;
}
else
} else {
}
}
if (flags & IFF_UNNUMBERED)
if (flags & IFF_PRIVATE)
if (flags & IFF_TEMPORARY)
if (flags & IFF_DEPRECATED)
(flags & IFF_DHCPRUNNING) &&
if (status != IPADM_SUCCESS)
goto fail;
}
}
return (IPADM_SUCCESS);
fail:
/* On error, cleanup everything and return. */
return (status);
}
/*
* From the given `name', i_ipadm_name2atype() deduces the address type
* and address family. If the `name' implies an address, it returns B_TRUE.
* Else, returns B_FALSE and leaves the output parameters unchanged.
*/
{
*type = IPADM_ADDR_DHCP;
} else {
}
return (is_addr);
}
/*
* properties. `ainfo' is an input as well as output parameter. When an
* address or an address property is found, `ainfo' is updated with the
* information found. Some of the fields may be already filled in by the
* calling function.
*
* `ia_sname' and `ia_dname'. Values for `ia_pflags' are obtained if the `nvl'
* contains an address property. `ia_sname', `ia_dname', and `ia_pflags' are
* obtained if `nvl' contains an address.
*/
static ipadm_status_t
{
int err;
found_addr = B_TRUE;
continue;
} else if (IPADM_PRIV_NVP(name)) {
continue;
} else {
found_prop = B_TRUE;
}
}
if (found_addr) {
/*
* We got an address from the nvlist `nvl'.
* Extract relevant information from `nvl' and populate `ainfo'.
*/
switch (atype) {
case IPADM_ADDR_STATIC:
}
&snamestr)) != 0)
return (ipadm_errno2status(err));
&dnamestr)) == 0)
break;
case IPADM_ADDR_DHCP:
case IPADM_ADDR_IPV6_ADDRCONF:
/*
* dhcp and addrconf address objects are always
* marked up when re-enabled.
*/
break;
default:
return (IPADM_FAILURE);
}
}
if (found_prop) {
/*
* We got an address property from `nvl'. Parse the
* name and the property value. Update the `ainfo->ia_pflags'
* for the flags.
*/
== 0) {
}
}
}
return (IPADM_SUCCESS);
}
/*
* Parses the given nvlist `nvl' for an address or an address property.
* The input nvlist must contain either an address or an address property.
* `ainfo' is an input as well as output parameter. When an address or an
* address property is found, `ainfo' is updated with the information found.
* Some of the fields may be already filled in by the calling function,
* because of previous calls to i_ipadm_nvl2ainfo_active().
*
* Since the address object in `nvl' is also in the active configuration, the
* `ia_sname' and `ia_dname'.
*
* If this function returns an error, the calling function will take
* care of freeing the fields in `ainfo'.
*/
static ipadm_status_t
{
}
/*
* The input nvlist must contain either an address, an address property, or
* both. `ainfo' is an input as well as output parameter. When an address
* or an address property is found, `ainfo' is updated with the information
* found.
*
* Some of the fields may be already filled in by the calling function,
* because of previous calls to i_ipadm_nvl2ainfo_persist().
*
* All the relevant fields in `ainfo' will be filled by this function based
* on what we find in `nvl'.
*
* If this function returns an error, the calling function will take
* care of freeing any fields in `ainfo' that it allocated.
*/
static ipadm_status_t
{
char *name;
int err = 0;
if (status != IPADM_SUCCESS)
return (status);
found_addr = B_TRUE;
found_plen = B_TRUE;
if (err != 0)
return (ipadm_errno2status(err));
}
sizeof (ainfo->ia_aobjname));
return (IPADM_NO_MEMORY);
if (found_addr) {
char *addrstr;
/*
* We got an address from the nvlist `nvl'.
* Parse `nvl' and populate `ifa->ifa_addr'.
*/
return (IPADM_NO_MEMORY);
switch (atype) {
case IPADM_ADDR_STATIC:
break;
case IPADM_ADDR_DHCP:
break;
case IPADM_ADDR_IPV6_ADDRCONF:
IPADM_NVP_INTFID, &addrstr)) != 0) {
return (ipadm_errno2status(err));
}
return (IPADM_NO_MEMORY);
if (err != 0)
return (ipadm_errno2status(err));
sizeof (dstdata));
}
break;
default:
return (IPADM_FAILURE);
}
}
if (found_plen) {
char *plen;
&plen)) != 0)
return (ipadm_errno2status(err));
return (IPADM_NO_MEMORY);
return (ipadm_errno2status(err));
}
return (IPADM_SUCCESS);
}
/*
* Retrieves all addresses from active config and appends to it the
* addresses that are found only in persistent config. In addition,
* it updates the persistent fields for each address from information
* found in persistent config. The output parameter `addrinfo' contains
* complete information regarding all addresses in active as well as
* persistent config.
*/
static ipadm_status_t
{
/*
* Only get all addresses from the active config if the profile in the
* handle is the currently active profile.
*/
!= NETCFG_SUCCESS)
return (IPADM_INVALID_ARG);
if (status != IPADM_SUCCESS)
goto fail;
}
/* Get all addresses from persistent config. */
/*
* If no address was found in persistent config, just
* return what we found in active config.
*/
if (status == IPADM_OBJ_NOTFOUND) {
/*
* No address was found in active nor persistent config.
* If a specific interface was not provided in `ifname',
* we return IPADM_SUCCESS and let the caller loop through
* `addrinfo'. Else, we check if the interface is configured
* in active config before returning IPADM_ENXIO.
* This is to avoid throwing an error when an interface is
* plumbed with no addresses configured on it.
*/
return (IPADM_NOSUCH_IF);
return (IPADM_SUCCESS);
}
/* In case of any other error, cleanup and return. */
if (status != IPADM_SUCCESS)
goto fail;
/* we append to make sure, loopback addresses are first */
;
}
/*
* `onvl' will contain all the address lines from the db. Each line
* contains the address itself and any address properties.
*
* If an address A was found in active, we will already have `ainfo',
* and it is present in persistent configuration as well, we need to
* update `ainfo' with persistent information (`ia_pflags).
* For each address B found only in persistent configuration,
* append the address to the list with the address info for B from
* `onvl'.
*/
continue;
&aobjname) != 0)
continue;
break;
}
/*
* We did not find this address object in `ainfo'.
* This means that the address object exists only
* in the persistent configuration. Get its
* details and append to `ainfo'.
*/
goto fail;
else
}
/*
* Fill relevant fields of `curr' from the persistent info
* in `nvladdr'. Call the appropriate function based on the
* `ia_state' value.
*/
else
if (status != IPADM_SUCCESS)
goto fail;
}
return (status);
fail:
/* On error, cleanup and return. */
return (status);
}
/*
* Callback function that sets the property `prefixlen' on the address
* object in `arg' to the value in `pval'.
*/
/* ARGSUSED */
static ipadm_status_t
{
int err, s;
char *end;
return (IPADM_OP_NOTSUP);
errno = 0;
return (IPADM_INVALID_ARG);
return (IPADM_INVALID_ARG);
return (ipadm_errno2status(err));
return (ipadm_errno2status(errno));
/* now, change the broadcast address to reflect the prefixlen */
/*
* get the interface address and set it, this should reset
* the broadcast address.
*/
}
return (IPADM_SUCCESS);
}
/*
* Callback function that sets the given value `pval' to one of the
* properties among `deprecated', `private', and `transmit' as defined in
* `pdp', on the address object in `arg'.
*/
/* ARGSUSED */
static ipadm_status_t
{
return (IPADM_OP_NOTSUP);
else
return (IPADM_INVALID_ARG);
if (on)
else
if (on)
else
if (on)
else
} else {
return (IPADM_PROP_UNKNOWN);
}
}
/*
* Callback function that sets the property `zone' on the address
* object in `arg' to the value in `pval'.
*/
/* ARGSUSED */
static ipadm_status_t
{
int s;
/*
* To modify the zone assignment such that it persists across
* reboots, zonecfg(1M) must be used. Trusted Extentions
* "all-zones" property value is an exception to that rule because
* it doesn't depend on the existence of any non-global zone; hence
* persisting all-zones property (and reverting it back to global
* zone default value) are allowed.
*/
if (flags & IPADM_OPT_PERSIST) {
if (!is_system_labeled())
return (IPADM_OP_NOTSUP);
else
return (IPADM_OP_NOTSUP);
} else if (flags & IPADM_OPT_ACTIVE) {
/* put logical interface into all zones */
if (is_system_labeled())
else
return (IPADM_OP_NOTSUP);
} else {
/* zone must be ready or running */
return (ipadm_errno2status(errno));
}
} else {
return (IPADM_INVALID_ARG);
}
return (ipadm_errno2status(errno));
return (IPADM_SUCCESS);
}
/*
* Callback function that gets the property `broadcast' for the address
* object in `arg'.
*/
/* ARGSUSED */
static ipadm_status_t
{
if (status != IPADM_SUCCESS)
return (status);
if (!(ifflags & IFF_BROADCAST)) {
buf[0] = '\0';
*bufsize = 1;
return (IPADM_SUCCESS);
}
}
switch (valtype) {
case MOD_PROP_DEFAULT: {
int err;
/*
* Since the address is unknown we cannot
* obtain default prefixlen
*/
buf[0] = '\0';
*bufsize = 1;
return (IPADM_SUCCESS);
}
/*
* For the static address, we get the address from the
* persistent db.
*/
if (status != IPADM_SUCCESS)
return (status);
} else {
/*
* If the address object is active, we retrieve the
* address from kernel.
*/
return (ipadm_errno2status(errno));
}
/*
* For default broadcast address, get the address and the
* default prefixlen for that address and then compute the
* broadcast address.
*/
if (status != IPADM_SUCCESS)
return (status);
return (ipadm_errno2status(err));
break;
}
case MOD_PROP_ACTIVE:
return (ipadm_errno2status(errno));
} else {
}
break;
default:
return (IPADM_INVALID_ARG);
}
return (IPADM_SUCCESS);
}
/*
* Callback function that retrieves the value of the property `prefixlen'
* for the address object in `arg'.
*/
/* ARGSUSED */
static ipadm_status_t
{
int s;
if (status != IPADM_SUCCESS) {
return (status);
} else if (lifflags & IFF_POINTOPOINT) {
buf[0] = '\0';
*bufsize = 1;
return (status);
}
}
switch (valtype) {
case MOD_PROP_POSSIBLE:
else
break;
case MOD_PROP_DEFAULT:
/*
* For static addresses, we retrieve the address
* from kernel if it is active.
*/
return (ipadm_errno2status(errno));
if (status != IPADM_SUCCESS)
return (status);
/*
* Since the address is unknown we cannot
* obtain default prefixlen
*/
buf[0] = '\0';
*bufsize = 1;
return (IPADM_SUCCESS);
} else {
/*
* If not in active config, we use the address
* from persistent store.
*/
if (status != IPADM_SUCCESS)
return (status);
if (status != IPADM_SUCCESS)
return (status);
}
break;
case MOD_PROP_ACTIVE:
return (ipadm_errno2status(errno));
break;
default:
return (IPADM_INVALID_ARG);
}
return (IPADM_SUCCESS);
}
/*
* Callback function that retrieves the value of one of the properties
* among `deprecated', `private', and `transmit' for the address object
* in `arg'.
*/
/* ARGSUSED */
static ipadm_status_t
{
switch (valtype) {
case MOD_PROP_DEFAULT:
} else {
return (IPADM_PROP_UNKNOWN);
}
break;
case MOD_PROP_ACTIVE:
/*
* If the address is present in active configuration, we
* retrieve it from kernel to get the property value.
* Else, there is no value to return.
*/
if (status != IPADM_SUCCESS)
return (status);
break;
default:
return (IPADM_INVALID_ARG);
}
return (status);
}
/*
* Callback function that retrieves the value of the property
* `reqhost' for the address object in `arg'.
*/
/* ARGSUSED */
static ipadm_status_t
{
int dherror;
buf[0] = '\0';
switch (valtype) {
case MOD_PROP_DEFAULT:
/*
* By default, no hostname is requested.
*/
*bufsize = 1;
break;
case MOD_PROP_ACTIVE:
/*
* The property only applies to DHCPv4 address object types.
*/
*bufsize = 1;
return (IPADM_SUCCESS);
}
/*
* If a hostname was requested, then it can be retrieved
* from the dhcpagent via the DHCP_STATUS IPC.
*/
&dhreply);
if (status != IPADM_SUCCESS) {
*bufsize = 1;
return (IPADM_SUCCESS);
}
if (reply_size < DHCP_STATUS_VER3_SIZE) {
*bufsize = 1;
return (IPADM_SUCCESS);
}
break;
default:
return (IPADM_INVALID_ARG);
}
return (IPADM_SUCCESS);
}
/*
* Callback function that retrieves the value of the property `zone'
* for the address object in `arg'.
*/
/* ARGSUSED */
static ipadm_status_t
{
int s;
buf[0] = '\0';
*bufsize = 1;
return (IPADM_SUCCESS);
}
/*
* we are in global zone. See if the lifname is assigned to shared-ip
* zone or global zone.
*/
switch (valtype) {
case MOD_PROP_DEFAULT:
sizeof (zone_name)) > 0)
else
return (ipadm_errno2status(errno));
break;
case MOD_PROP_ACTIVE:
return (ipadm_errno2status(errno));
sizeof (zone_name)) < 0) {
return (ipadm_errno2status(errno));
} else {
}
break;
default:
return (IPADM_INVALID_ARG);
}
return (IPADM_SUCCESS);
}
static ipadm_prop_desc_t *
{
int i;
return (&ipadm_addrprop_table[i]);
}
return (NULL);
}
/*
* Gets the value of the given address property `pname' for the address
* object with name `aobjname'.
*
* `valtype' determines the type of value that will be retrieved.
* IPADM_OPT_ACTIVE - current value of the property (active config)
* IPADM_OPT_PERSIST - value of the property from persistent store
* IPADM_OPT_DEFAULT - default hard coded value (boot-time value)
* IPADM_OPT_POSSIBLE - range of values
*
* The framework checks to see if the given `buf' of size `bufsize' is big
* enough to fit the result. All the callback functions return IPADM_SUCCESS
* regardless of whether it could fit everything into the buffer it was
* provided. In cases where the buffer was insufficient, the callback
* functions return the required size to fit the result via `psize'.
*/
{
/*
* Validate the arguments of the function. Allow `buf' to be NULL
* only when `*bufsize' is zero as it can be used by the callers to
* determine the actual buffer size required.
*/
return (IPADM_INVALID_ARG);
}
/* find the property in the property description table */
return (IPADM_PROP_UNKNOWN);
/*
* For the given aobjname, get the addrobj it represents and
* retrieve the property value for that object.
*/
return (status);
return (IPADM_OP_NOTSUP);
/*
* Call the appropriate callback function to based on the field
* that was asked for.
*/
switch (valtype) {
case IPADM_OPT_PERM:
break;
case IPADM_OPT_ACTIVE:
buf[0] = '\0';
psize = 1;
} else {
}
break;
case IPADM_OPT_DEFAULT:
break;
case IPADM_OPT_POSSIBLE:
break;
}
buf[0] = '\0';
psize = 1;
break;
case IPADM_OPT_PERSIST:
&ipaddr);
break;
default:
return (IPADM_INVALID_ARG);
}
/*
* Check whether the provided buffer was of sufficient size to
* hold the property value.
*/
}
return (status);
}
/*
* Sets the value of the given address property `pname' to `pval' for the
* address object with name `aobjname'.
*/
{
/* Check for solaris.network.interface.config authorization */
if (!ipadm_check_auth())
return (IPADM_INSUFF_AUTH);
pflags == IPADM_OPT_PERSIST ||
return (IPADM_INVALID_ARG);
}
/* find the property in the property description table */
return (IPADM_PROP_UNKNOWN);
return (IPADM_OP_NOTSUP);
return (IPADM_INVALID_ARG);
}
/*
* For the given aobjname, get the addrobj it represents and
* set the property value for that object.
*/
return (status);
return (IPADM_OP_DISABLE_OBJ);
/* Persistent operation not allowed on a temporary object. */
if ((pflags & IPADM_OPT_PERSIST) &&
return (IPADM_TEMPORARY_OBJ);
/*
* Currently, setting an address property on an address object of type
* IPADM_ADDR_IPV6_ADDRCONF is not supported. Supporting it involves
* in.ndpd retrieving the address properties from ipmgmtd for given
* address object and then setting them on auto-configured addresses,
* whenever in.ndpd gets a new prefix. This will be supported in
* future releases.
*/
return (IPADM_OP_NOTSUP);
/*
* Setting an address property on an address object that is
* not present in active configuration is not supported.
*/
return (IPADM_OP_NOTSUP);
if (reset) {
/*
* If we were asked to reset the value, we need to fetch
* the default value and set the default value.
*/
if (status != IPADM_SUCCESS)
return (status);
}
/* set the user provided or default property value */
if (status != IPADM_SUCCESS)
return (status);
/*
* If IPADM_OPT_PERSIST was set in `flags', we need to store
* property and its value in persistent DB.
*/
if (pflags & IPADM_OPT_PERSIST) {
pflags);
}
return (status);
}
/*
* This function is called after deleting a test address. It makes sure that
* the interface is brought up, if the deleted address was the last address on
* the interface, to make it usable in the IPMP group.
*/
{
if (addr->ipadm_lifnum == 0) {
if (status != IPADM_SUCCESS)
return (status);
}
return (ipadm_errno2status(errno));
}
return (status);
}
/*
* Remove the address specified by the address object in `addr'
* from kernel. If the address is on a non-zero logical interface, we do a
* SIOCLIFREMOVEIF, otherwise we set the address to INADDR_ANY for IPv4 or
* :: for IPv6.
*/
{
int sock;
if (addr->ipadm_lifnum == 0) {
/*
* Fake the deletion of the 0'th address by
* clearing IFF_UP and setting it to as 0.0.0.0 or ::.
* Note that setting the address to 0.0.0.0 or ::
* is not allowed for addresses created by ifconfig
* and will result in EADDRNOTAVAIL. We ignore this
* error and drive on.
*/
if (status != IPADM_SUCCESS)
return (status);
errno != EADDRNOTAVAIL)
return (ipadm_errno2status(errno));
/*
* We do not zero the destination addresses for addrconf
* addresses, because we might not be able to bring them
* up again (in the case where default destination tokens
* are being used). Not zeroing out the address has no
* adverse effect.
*/
return (ipadm_errno2status(errno));
}
return (ipadm_errno2status(errno));
}
/*
* If it happens to be an underlying interface and there
* is no IFF_UP address left on it, bring up the interface
* to make it usable in the group.
*/
if (!i_ipadm_is_legacy(iph) &&
return (IPADM_SUCCESS);
}
/*
* Extracts the IPv6 address from the nvlist in `nvl'.
*/
{
uint_t n;
return (IPADM_OBJ_NOTFOUND);
assert(n == 16);
return (IPADM_SUCCESS);
}
/*
* A common function that is used to validate that a name conforms to
* the rules of an aobjname. This function will be used to validate
* both address objects names and address object owner names. The names
* cannot exceed `len` in length. The names should start with an
* alphabetic character and can only contain alphanumeric characters.
*/
static boolean_t
{
const char *cp;
return (B_FALSE);
}
;
return (*cp == '\0');
}
/*
* Used to validate the given addrobj name string.
*/
static boolean_t
{
return (i_ipadm_is_aobjname_valid_common(aobjname,
}
/*
* Used to validate the given owner string.
*/
{
return (B_TRUE);
}
/*
* Computes the prefixlen for the given `addr' based on the netmask found using
* the order specified in /etc/nsswitch.conf. If not found, then the
* prefixlen is computed using the Classful subnetting semantics defined
* in RFC 791 for IPv4 and RFC 4291 for IPv6.
*/
static ipadm_status_t
{
switch (af) {
case AF_INET:
m->sin_family = AF_INET;
break;
case AF_INET6:
prefixlen = 10;
else
prefixlen = 64;
break;
default:
return (IPADM_INVALID_ARG);
}
return (IPADM_SUCCESS);
}
/*
* Resolves name using af as an address family filter, and stores the result
* in sa. We assume that the storage used for sa is at least sizeof
* (struct sockaddr_storage).
*/
{
int rc;
/* maps to more than one hostname */
return (IPADM_HOSTNAME_TO_MULTADDR);
}
return (IPADM_SUCCESS);
}
{
void *addr;
return (IPADM_INVALID_ARG);
case AF_INET:
sa_len = sizeof (struct sockaddr_in);
break;
case AF_INET6:
sa_len = sizeof (struct sockaddr_in6);
break;
}
return (ipadm_errno2status(errno));
return (IPADM_SUCCESS);
}
/*
* This takes a static address string <addr>[/<mask>] or a hostname
* and maps it to a single numeric IP address, consulting DNS if
* hostname was provided. If a specific address family was requested,
* an error is returned if the given hostname does not map to an address
* of the given family. Note that this function returns failure
* if the name maps to more than one IP address.
*/
{
char *prefixlenstr;
char *endp;
/*
* We use (NI_MAXHOST + 5) because the longest possible
* astr will have (NI_MAXHOST + '/' + {a maximum of 32 for IPv4
* or a maximum of 128 for IPv6 + '\0') chars
*/
*prefixlenstr++ = '\0';
errno = 0;
return (IPADM_INVALID_ARG);
}
if (status != IPADM_SUCCESS)
return (status);
if (status == IPADM_SUCCESS) {
sizeof (ipaddr->ipadm_static_aname));
}
return (status);
}
/*
* Gets the static source address from the address object in `ipaddr'.
* Memory for `addr' should be already allocated by the caller.
*/
{
return (IPADM_INVALID_ARG);
}
return (IPADM_SUCCESS);
}
/*
* Set up tunnel destination address in ipaddr by contacting DNS.
* The function works similar to ipadm_set_addr().
* The dst_addr must resolve to exactly one address. IPADM_BAD_ADDR is returned
* if dst_addr resolves to more than one address. The caller has to verify
* that ipadm_static_addr and ipadm_static_dst_addr have the same ss_family
*/
{
/* mask lengths are not meaningful for point-to-point interfaces. */
return (IPADM_BAD_ADDR);
if (status == IPADM_SUCCESS) {
sizeof (ipaddr->ipadm_static_dname));
}
return (status);
}
/*
* Private interface used by ifconfig(1M) to retrieve the interface id
* for the specified aobjname.
*/
{
/* validate input */
if (!(flags & IPADM_OPT_ACTIVE))
return (IPADM_INVALID_ARG);
return (IPADM_INVALID_ARG);
/* Retrieve the address object information from ipmgmtd. */
if (status != IPADM_SUCCESS)
return (status);
return (IPADM_OBJ_NOTFOUND);
return (IPADM_OP_NOTSUP);
return (IPADM_SUCCESS);
}
/*
* Performs validation of the input flags and address object for
* ipadm_update_addr(). It also retrieves the address object for the
* input address object name in `mod_ipaddr' and if found, returns it
* in `ipaddr'.
*/
static ipadm_status_t
{
/* check for solaris.network.interface.config authorization */
if (!ipadm_check_auth())
return (IPADM_INSUFF_AUTH);
if (!i_ipadm_is_legacy(iph))
return (IPADM_OP_NOTSUP);
/* validate input */
if ((!(flags & IPADM_OPT_ACTIVE)) ||
return (IPADM_INVALID_ARG);
/* Get the addrobj for this aobjname */
*ipaddr = *mod_ipaddr;
if (mod_ipaddr->ipadm_lifnum > 0) {
} else {
}
ipaddr);
if (status != IPADM_SUCCESS)
return (IPADM_INVALID_ARG);
/* Retrieve the address object information from ipmgmtd */
if (status != IPADM_SUCCESS)
return (status);
return (IPADM_OP_NOTSUP);
if ((flags & IPADM_OPT_PERSIST) &&
return (IPADM_TEMPORARY_OBJ);
return (IPADM_SUCCESS);
}
/*
* Private interface used by ifconfig(1M) to modify the local and remote
* address for the specified address object.
*/
{
int sock;
if (status != IPADM_SUCCESS)
return (status);
if (lcl_set) {
/*
* i_ipadm_create_addr() sets the local address
* and the destination address if one is available.
* We mask off the IPADM_OPT_PERSIST flag because we
* will handle persistence later in this function.
*/
if (status != IPADM_SUCCESS)
return (status);
} else if (dst_set) {
/*
* In this case, the local address is not being modified.
* We don't call i_ipadm_create_addr() because it will
* over-write the local address.
*/
return (ipadm_errno2status(errno));
} else {
return (IPADM_INVALID_ARG);
}
/*
* If the change to the address object is persistent, we retrieve
* the persistent address object, delete it fron the persistent
* store and persist it again with the updated address.
* Since there is no door call available to update a persistent
* address object in place, we are forced to do the above.
*/
if (flags & IPADM_OPT_PERSIST) {
if (status != IPADM_SUCCESS)
return (status);
if (status != IPADM_SUCCESS)
return (status);
if (lcl_set) {
sizeof (ipaddr.ipadm_static_aname));
}
if (dst_set) {
sizeof (ipaddr.ipadm_static_dname));
}
if (status != IPADM_SUCCESS)
return (status);
if (is_up)
flags |= IPADM_OPT_UP;
}
return (IPADM_SUCCESS);
}
/*
* Private interface used by ifconfig(1M) to modify the interface id(s)
* for the specified aobjname.
*/
{
if (status != IPADM_SUCCESS)
return (status);
/* Update the address object with updated ids */
if (mod_ipaddr->ipadm_intfidlen != 0) {
}
if (status != IPADM_SUCCESS)
return (status);
return (status);
/*
* If the change to the interface ID is persistent, we retrieve
* the persistent address object, delete it from the persistent
* store and persist it again with the updated interface ID.
* Since there is no door call available to update a persistent
* address object in place, we are forced to do the above.
*/
if (flags & IPADM_OPT_PERSIST) {
if (status != IPADM_SUCCESS)
return (status);
if (status != IPADM_SUCCESS)
return (status);
if (mod_ipaddr->ipadm_intfidlen != 0) {
}
}
/*
* The IPADM_OPT_MODIFY flag lets ipmgmtd know that it
* should only update the active configuration even though
* the addr is persistent.
*/
}
/*
* Remove any prefix from the interface id.
*/
static boolean_t
{
int i = 0;
/*
* The only valid prefix is the linklocal prefix (or none at all).
*/
if (IN6_IS_ADDR_LINKLOCAL(intfid)) {
for (i = 0; i < TOKEN_PREFIXLEN/8; i++) {
}
} else {
for (i = 0; i < TOKEN_PREFIXLEN/8; i++) {
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* Sets the interface ID in the address object `ipaddr' with the address
* in the string `interface_id'. This interface ID will be used when
* ipadm_create_addr() is called with `ipaddr' with address type
* set to IPADM_ADDR_IPV6_ADDRCONF.
*/
{
char *end;
char *cp;
return (IPADM_INVALID_ARG);
/*
* Interface ids should always have perfixlen of 64 bits. If provided,
* then verify it. Otherwise, default it.
*/
*cp++ = '\0';
errno = 0;
return (IPADM_INVALID_IFID);
} else {
}
return (IPADM_SUCCESS);
}
return (IPADM_INVALID_IFID);
}
/*
* Sets the destination interface ID in the address object `ipaddr' with the
* address in the string `interface_id'.
*/
{
return (IPADM_INVALID_ARG);
/* mask lengths are not meaningful for point-to-point interfaces */
return (IPADM_BAD_ADDR);
return (IPADM_SUCCESS);
}
return (IPADM_INVALID_IFID);
}
/*
* Sets the value for the field `ipadm_stateless' in address object `ipaddr'.
*/
{
return (IPADM_INVALID_ARG);
return (IPADM_SUCCESS);
}
/*
* Sets the value for the field `ipadm_stateful' in address object `ipaddr'.
*/
{
return (IPADM_INVALID_ARG);
return (IPADM_SUCCESS);
}
/*
* Sets the dhcp parameter `ipadm_primary' in the address object `ipaddr'.
* The field is used during the address creation with address
* type IPADM_ADDR_DHCP. It specifies if the interface should be set
* as a primary interface for getting dhcp global options from the DHCP server.
*/
{
return (IPADM_INVALID_ARG);
return (IPADM_SUCCESS);
}
/*
* Sets the dhcp parameter `ipadm_reqhost' in the address object `ipaddr'.
* This field is used during the address creation with address type
* IPADM_ADDR_DHCP. It specifies the DNS hostname that the client is
* requesting to be updated with the IP address acquired by the dhcpagent.
*/
{
int i;
return (IPADM_INVALID_ARG);
for (i = 0; hostname[i] != '\0'; i++) {
(i > 0)))
continue;
return (IPADM_INVALID_ARG);
}
if (i == 0 || i >= sizeof (ipaddr->ipadm_reqhost))
return (IPADM_INVALID_ARG);
sizeof (ipaddr->ipadm_reqhost));
return (IPADM_SUCCESS);
}
/*
* Sets the dhcp parameter `ipadm_wait' in the address object `ipaddr'.
* This field is used during the address creation with address type
* IPADM_ADDR_DHCP. It specifies how long the API ipadm_create_addr()
* should wait before returning while the dhcp address is being acquired
* by the dhcpagent.
* Possible values:
* - IPADM_DHCP_WAIT_FOREVER : Do not return until dhcpagent returns.
* - IPADM_DHCP_WAIT_DEFAULT : Wait a default amount of time before returning.
* - <integer> : Wait the specified number of seconds before returning.
*/
{
return (IPADM_INVALID_ARG);
return (IPADM_SUCCESS);
}
/*
* Creates a placeholder for the `ipadm_aobjname' in the ipmgmtd `aobjmap'.
* If the `aobjname' already exists in the daemon's `aobjmap' then
* IPADM_ADDROBJ_EXISTS will be returned.
*
* If the libipadm consumer set `ipaddr.ipadm_aobjname[0]' to `\0', then the
* daemon will generate an `aobjname' for the given `ipaddr'.
*/
{
int err;
sizeof (larg.ia_aobjname));
/* copy the daemon generated `aobjname' into `ipadddr' */
sizeof (ipaddr->ipadm_aobjname));
}
return (IPADM_ADDROBJ_EXISTS);
return (ipadm_errno2status(err));
}
/*
* Sets the logical interface number in the ipmgmtd's memory map for the
* address object `ipaddr'. If another address object has the same
* logical interface number, IPADM_ADDROBJ_EXISTS is returned.
*/
{
int err;
return (IPADM_SUCCESS);
sizeof (larg.ia_aobjname));
return (IPADM_ADDROBJ_EXISTS);
return (ipadm_errno2status(err));
}
/*
* Creates the IPv4 or IPv6 address in the nvlist `nvl' on the interface
* `ifname'. If a hostname is present, it is resolved before the address
* is created.
*/
{
int err = 0;
/* retrieve the address information */
}
if (err != 0)
return (ipadm_errno2status(err));
}
flags |= IPADM_OPT_UP;
/* build the address object from the above information */
return (IPADM_NO_MEMORY);
} else {
}
if (status != IPADM_SUCCESS)
return (status);
if (status != IPADM_SUCCESS)
return (status);
}
}
/*
* Creates a dhcp address on the interface `ifname' based on the
* IPADM_ADDR_DHCP address object parameters from the nvlist `nvl'.
*/
{
char *aobjname;
char *ewait;
int err = 0;
/* Extract the dhcp parameters */
IPADM_NVP_AOBJNAME, &aobjname)) != 0) {
return (ipadm_errno2status(err));
}
/* There might also be an optional reqhost parameter */
/* Build the address object */
sizeof (ipaddr.ipadm_reqhost));
/*
* When enabling the interface as part of the
* to be overridden by the environmental variable.
*/
wait = 0;
}
}
/*
* Parses an address object of type IPADM_ADDR_IPV6_ADDRCONF from input
* nvlist `nvl' and stores it in `ipaddr'.
*/
static ipadm_status_t
{
char *aobjname;
int err = 0;
!= 0 ||
&addrstr)) != 0 ||
&plenstr)) != 0 ||
&stateless)) != 0 ||
&stateful)) != 0) {
return (ipadm_errno2status(err));
}
/* the remote interface id is optional */
!= 0) {
return (ipadm_errno2status(err));
}
}
}
sizeof (ipaddr->ipadm_aobjname));
return (IPADM_SUCCESS);
}
/*
* Creates auto-configured addresses on the interface `ifname' based on
* the IPADM_ADDR_IPV6_ADDRCONF address object parameters from the nvlist `nvl'.
*/
{
if (status != IPADM_SUCCESS)
return (status);
}
/*
* Allocates `ipadm_addrobj_t' and populates the relevant member fields based on
* the provided `type'. `aobjname' represents the address object name, which
* is of the form `<ifname>/<addressname>'.
*
* The caller has to minimally provide <ifname>. If <addressname> is not
* provided, then a default one will be generated by the API.
*/
{
return (IPADM_INVALID_ARG);
return (IPADM_INVALID_ARG);
return (IPADM_INVALID_ARG);
*aname++ = '\0';
/* Check if the interface name is valid. */
return (IPADM_INVALID_ARG);
/* Check if the given addrobj name is valid. */
return (IPADM_INVALID_ARG);
return (IPADM_NO_MEMORY);
/*
* If the ifname has logical interface number, extract it and assign
* it to `ipadm_lifnum'. Only applications with IH_LEGACY set will do
* this today. We will check for the validity later in
* i_ipadm_validate_create_addr().
*/
if (ifsp.ifsp_lunvalid) {
*cp = '\0';
}
sizeof (newaddr->ipadm_ifname));
}
switch (type) {
case IPADM_ADDR_IPV6_ADDRCONF:
newaddr->ipadm_intfidlen = 0;
break;
case IPADM_ADDR_DHCP:
break;
case IPADM_ADDR_STATIC:
break;
default:
goto fail;
}
return (IPADM_SUCCESS);
fail:
return (status);
}
/*
* Returns `aobjname' from the address object in `ipaddr'.
*/
{
return (IPADM_INVALID_ARG);
return (IPADM_INVALID_ARG);
return (IPADM_SUCCESS);
}
/*
* Frees the address object in `ipaddr'.
*/
void
{
}
/*
* Retrieves the logical interface name from `ipaddr' and stores the
* string in `lifname'.
*/
void
{
if (ipaddr->ipadm_lifnum != 0) {
} else {
}
}
/*
* Checks if a non-zero static address is present on the 0th logical interface
* of the given IPv4 or IPv6 physical interface. For an IPv4 interface, it
* also checks if the interface is under DHCP control. If the condition is true,
* the output argument `exists' will be set to B_TRUE. Otherwise, `exists'
* is set to B_FALSE.
*
* Note that *exists will not be initialized if an error is encountered.
*/
static ipadm_status_t
{
int sock;
/* For IH_LEGACY, a new logical interface will never be added. */
if (i_ipadm_is_legacy(iph)) {
return (IPADM_SUCCESS);
}
return (ipadm_errno2status(errno));
return (IPADM_SUCCESS);
}
}
return (ipadm_errno2status(errno));
return (IPADM_SUCCESS);
}
/*
* Adds a new logical interface in the kernel for interface
* `addr->ipadm_ifname', if there is a non-zero address on the 0th
* logical interface or if the 0th logical interface is under DHCP
* control. On success, it sets the lifnum in the address object `addr'.
*/
{
int sock;
addr->ipadm_lifnum = 0;
if (status != IPADM_SUCCESS) {
return (status);
}
if (addif) {
/*
* If there is an address on 0th logical interface,
* add a new logical interface.
*/
return (ipadm_errno2status(errno));
}
return (IPADM_SUCCESS);
}
/*
* Reads all the address lines from the persistent DB into the nvlist `onvl',
* when both `ifname' and `aobjname' are NULL. If an `ifname' is provided,
* it returns all the addresses for the given interface `ifname'.
* If an `aobjname' is specified, then the address line corresponding to
* that name will be returned.
*/
static ipadm_status_t
{
int err;
char *nvlbuf;
/* Populate the door_call argument structure */
sizeof (garg.ia_aobjname));
sizeof (*rvalp));
if (err == 0) {
}
return (ipadm_errno2status(err));
}
/*
* Adds the IP address contained in the 'ipaddr' argument to the physical
* interface represented by 'ifname' after doing the required validation.
* If the interface does not exist, IPADM_ENXIO is returned, unless it
* is the loopback interface.
*
* If IH_LEGACY is set in ih_flags, flags has to be IPADM_OPT_ACTIVE
* and a default addrobj name will be generated. Input `addr->ipadm_aobjname',
* if provided, will be ignored and replaced with the newly generated name.
* The interface name provided has to be a logical interface name that
* already exists. No new logical interface will be added in this function.
*/
{
/* check for solaris.network.interface.config authorization */
if (!ipadm_check_auth())
return (IPADM_INSUFF_AUTH);
/*
* If the profile in the handle is not the active profile, or if
* the request was to persist *only*, just persist the request.
*/
!= NETCFG_SUCCESS)
return (IPADM_INVALID_ARG);
if (!(flags & IPADM_OPT_ACTIVE) ||
if (flags & IPADM_OPT_PERSIST) {
/*
* If a loopback address is created, also create the
* interface. This is also done when a loopback
* address is created on the active profile.
*/
if (ipadm_is_loopback(ifname)) {
if (status != IPADM_SUCCESS)
return (status);
}
return (status);
} else {
return (IPADM_INVALID_ARG);
}
}
/* Validate the addrobj. This also fills in addr->ipadm_ifname. */
if (status != IPADM_SUCCESS)
return (status);
/*
* For Legacy case, check if an addrobj already exists for the
* given logical interface name. If one does not exist,
* a default name will be generated and added to the daemon's
* aobjmap.
*/
if (legacy) {
if (addr->ipadm_lifnum > 0) {
} else {
}
&ipaddr);
if (status == IPADM_SUCCESS) {
/*
* With IH_LEGACY, modifying an address that is not
* a static address will return with an error.
*/
return (IPADM_OP_NOTSUP);
/*
* we found the addrobj in daemon, copy over the
* aobjname to `addr'.
*/
} else if (status != IPADM_OBJ_NOTFOUND) {
return (status);
}
}
/*
* Create a placeholder for this address object in the daemon.
* Skip this step if we are booting a zone (and therefore being called
* from ipmgmtd itself), and, for IH_LEGACY case if the
* addrobj already exists.
*
* Note that the placeholder is not needed in the NGZ boot case,
* when zoneadmd has itself applied the "allowed-ips" property to clamp
* down any interface configuration, so the namespace for the interface
* is fully controlled by the GZ.
*/
if (!is_boot && create_aobj) {
if (status != IPADM_SUCCESS)
return (status);
}
if (ipadm_is_vni(ifname))
else if (ipadm_is_loopback(ifname))
else
/* Plumb the interface if necessary */
} else {
if (class == IPADMIF_CLASS_LOOPBACK) {
}
}
goto fail;
if (status == IPADM_SUCCESS)
created_af = B_TRUE;
}
/*
* Some input validation based on the interface flags:
* 1. in non-global zones, make sure that we are not persistently
* creating addresses on interfaces that are acquiring
* address from the global zone.
* 2. Validate static addresses for IFF_POINTOPOINT interfaces.
*/
if (status != IPADM_SUCCESS)
goto fail;
}
if (ifflags & IFF_POINTOPOINT) {
if (is_6to4) {
goto fail;
}
} else {
goto fail;
}
/* Check for a valid dst address. */
if (!legacy && sockaddrunspec(
(struct sockaddr *)
&addr->ipadm_static_dst_addr)) {
goto fail;
}
}
} else {
/*
* Disallow setting of dstaddr when the link is not
* a point-to-point link.
*/
goto fail;
}
}
/*
* Disallow setting of dstaddr when the link is not
* a point-to-point link.
*/
return (IPADM_INVALID_ARG);
}
/* Create the address. */
switch (type) {
case IPADM_ADDR_STATIC:
break;
case IPADM_ADDR_DHCP:
break;
case IPADM_ADDR_IPV6_ADDRCONF:
break;
default:
break;
}
/*
* If address was not created successfully, remove the
* addrobj created by the ipmgmtd daemon as a placeholder.
* If IH_LEGACY is set, then remove the addrobj only if it was
* created in this function.
*/
fail:
if (status != IPADM_DHCP_IPC_TIMEOUT &&
status != IPADM_SUCCESS) {
if (created_af)
else if (create_aobj)
}
return (status);
}
/*
* Creates the static address in `ipaddr' in kernel. After successfully
* creating it, it updates the ipmgmtd daemon's aobjmap with the logical
* interface information.
*/
static ipadm_status_t
{
int sock;
/* If prefixlen was not provided, get default prefixlen */
if (ipaddr->ipadm_static_prefixlen == 0) {
/* prefixlen was not provided, get default prefixlen */
if (status != IPADM_SUCCESS)
return (status);
}
/*
* Create a new logical interface if needed; otherwise, just
* use the 0th logical interface.
*/
if (!i_ipadm_is_legacy(iph)) {
if (status != IPADM_SUCCESS)
return (status);
/*
* If the address is to be created on an underlying
* interface and it happens to be on the 0th logical
* interface, make sure we bring the address down first
* to avoid the address migration onto the IPMP interface.
*/
if (status != IPADM_SUCCESS)
return (status);
}
/*
* We don't have to set the lifnum for IH_INIT case, because
* there is no placeholder created for the address object in
* this case. For IH_LEGACY, we don't do this because the
* lifnum is given by the caller and it will be set in the
* end while we call the i_ipadm_addr_persist().
*/
if (status == IPADM_ADDROBJ_EXISTS)
goto retry;
if (status != IPADM_SUCCESS)
return (status);
}
}
goto ret;
}
goto ret;
}
/* Set the destination address, if one is given. */
goto ret;
}
}
/*
* If this is an underlying interface in an IPMP group,
* make this a test address.
*/
if (status != IPADM_SUCCESS)
goto ret;
}
if (flags & IPADM_OPT_UP) {
/*
* IPADM_DAD_FOUND is a soft-error for create-addr.
* No need to tear down the address.
*/
if (status == IPADM_DAD_FOUND)
}
/*
* For IH_LEGACY, we might be modifying the address on
* an address object that already exists e.g. by doing
* "ifconfig bge0:1 <addr>; ifconfig bge0:1 <newaddr>"
* So, we need to store the object only if it does not
* already exist in ipmgmtd.
*/
if (legacy) {
sizeof (legacy_addr.ipadm_aobjname));
if (status == IPADM_SUCCESS &&
legacy_addr.ipadm_lifnum >= 0) {
return (status);
}
}
flags);
}
ret:
if (status != IPADM_SUCCESS) {
if (!legacy)
if (bringup) {
IFF_UP, 0);
}
}
return (status);
}
/*
* Removes the address object identified by `aobjname' from both active and
* persistent configuration. The address object will be removed from only
* active configuration if IH_LEGACY is set in `iph->ih_flags'.
*
* If the address type is IPADM_ADDR_STATIC or IPADM_ADDR_DHCP, the address
* in the address object will be removed from the physical interface.
*
* If the address type is IPADM_ADDR_DHCP or IPADM_ADDR_IPV6_ADDRCONF, the
* IPADM_OPT_RELEASE flag specifies whether the lease should be released.
* If IPADM_OPT_RELEASE is not specified, the lease will be dropped. This
* option is not supported for IPADM_ADDR_STATIC address types.
*
* If the address type is IPADM_ADDR_IPV6_ADDRCONF, the link-local address and
* all the autoconfigured addresses will be removed.
* Finally, the address object is also removed from ipmgmtd's aobjmap and from
* the persistent DB.
*/
{
/* check for solaris.network.interface.config authorization */
if (!ipadm_check_auth())
return (IPADM_INSUFF_AUTH);
/* validate input */
!(flags & IPADM_OPT_ACTIVE)) ||
return (IPADM_INVALID_ARG);
}
IPADM_AOBJSIZ) >= IPADM_AOBJSIZ) {
return (IPADM_INVALID_ARG);
}
/* Retrieve the address object information from ipmgmtd. */
if (status != IPADM_SUCCESS)
return (status);
return (IPADM_OP_NOTSUP);
/*
* If requested to delete just from active config but the address
* is not in active config, return error.
*/
return (IPADM_OBJ_NOTFOUND);
}
/*
* If address is present in active config, remove it from
* kernel.
*/
switch (ipaddr.ipadm_atype) {
case IPADM_ADDR_STATIC:
break;
case IPADM_ADDR_DHCP:
break;
case IPADM_ADDR_IPV6_ADDRCONF:
release);
break;
default:
/*
* This is the case of address object name residing in
* daemon's aobjmap (added by ADDROBJ_LOOKUPADD). Fall
* through and delete that address object.
*/
break;
}
/*
* If the address was previously deleted from the active
* config, we will get an ENXIO from kernel.
* We will still proceed and purge the address information
* in the DB.
*/
if (status == IPADM_NOSUCH_IF)
else if (status != IPADM_SUCCESS)
return (status);
}
(flags & IPADM_OPT_PERSIST)) {
flags &= ~IPADM_OPT_PERSIST;
}
if (status == IPADM_OBJ_NOTFOUND)
return (status);
return (IPADM_SUCCESS);
}
/*
* Starts the dhcpagent and sends it the message DHCP_START to start
* configuring a dhcp address on the given interface in `addr'.
* After making the dhcpagent request, it also updates the
* address object information in ipmgmtd's aobjmap and creates an
* entry in persistent DB if IPADM_OPT_PERSIST is set in `flags'.
*/
static ipadm_status_t
{
return (IPADM_CANNOT_START_DHCP);
/*
* Create a new logical interface if needed; otherwise, just
* use the 0th logical interface.
*/
if (!legacy) {
if (status != IPADM_SUCCESS)
return (status);
if (status == IPADM_ADDROBJ_EXISTS)
goto retry;
if (status != IPADM_SUCCESS)
return (status);
}
}
/* Send DHCP_START to the dhcpagent. */
/*
* We do not undo the create-addr operation for IPADM_DHCP_IPC_TIMEOUT
* since it is only a soft error to indicate the caller that the lease
* might be required after the function returns.
*/
goto fail;
/* Persist the address object information in ipmgmtd. */
if (status != IPADM_SUCCESS)
goto fail;
return (dh_status);
fail:
/* In case of error, delete the dhcp address */
return (status);
}
/*
* object `addr'. If `release' is set to B_FALSE, the lease will be dropped.
*/
static ipadm_status_t
{
int dherr;
/* Send DHCP_RELEASE or DHCP_DROP to the dhcpagent */
if (release) {
/*
* If no lease was obtained on the object, we should
* drop the dhcp control on the interface.
*/
} else {
}
if (status != IPADM_SUCCESS)
return (status);
if (!i_ipadm_is_legacy(iph)) {
/*
* Delete the logical interface. Bring it down, if it is the 0th
* logical Interface.
*/
if (addr->ipadm_lifnum != 0) {
return (ipadm_errno2status(errno));
} else {
if (status != IPADM_SUCCESS)
return (status);
}
/*
* For an underlying interface, we need to clear the
* IFF_NOFAILOVER and IFF_DEPRECATED flags that were set
* by the dhcpagent. Also, check if the interface needs
* to be brought up to make it usable in the group.
*/
}
return (IPADM_SUCCESS);
}
/*
* Communicates with the dhcpagent to send a dhcp message of type `type'.
* It returns the dhcp error in `dhcperror' if a non-null pointer is provided
* in `dhcperror'. If `type' is DHCP_STATUS, the function returns the
* dhcp reply ipc structure in `dhreply', which should be freed by the caller.
* Note that since this function may be used to get dhcp status for
* interfaces not configured by the library, there is no guarantee that
* addr->ipadm_aobjname will not be a NULL string.
*/
static ipadm_status_t
{
int error;
int dhcp_timeout;
/* Construct a message to the dhcpagent. */
if (type == DHCP_START) {
if (buffer_size != 0) {
}
if (addr->ipadm_primary)
type |= DHCP_PRIMARY;
}
return (IPADM_NO_MEMORY);
else
/* Send the message to dhcpagent. */
if (error == 0) {
else
}
if (error != 0) {
if (error != DHCP_IPC_E_TIMEOUT) {
if (error == DHCP_IPC_E_INVIF)
return (IPADM_DHCP_INVALID_IF);
if (error == DHCP_IPC_E_RUNNING)
return (IPADM_OBJ_EXISTS);
return (IPADM_DHCP_IPC_FAILURE);
} else if (dhcp_timeout != 0) {
return (IPADM_DHCP_IPC_TIMEOUT);
}
}
return (IPADM_SUCCESS);
}
/*
* Used to obtain dhcp information that includes the lease details, the
* Client ID and its type, for the given `addr' object. Note that since
* this function must support obtaining information for addresses not
* created by the library, 'addr' is only populated to the extent necessary
* for the request being made. Specifically, this means that ipadm_aobjname
* can be a NULL string. ipadm_ifname, ipadm_lnum and ipadm_af will, at
* minimum, be populated.
*/
{
int dherror;
char *typestr;
char *sep;
if (isv6) {
addr->ipadm_lifnum = 0;
}
if (status != IPADM_SUCCESS) {
goto ret;
}
if (reply_size < DHCP_STATUS_VER2_SIZE) {
goto ret;
}
/*
* If no Client ID is returned by dhcpagent, this means that
* the MAC address of the interface is used as the default Client ID.
* When that happens, we retrieve the MAC address to construct the
* Client ID and set the Client ID type to -1 to indicate "default".
*/
goto ret;
}
goto ret;
}
dlpi_close(dh);
goto ret;
}
dlpi_close(dh);
if (dlinfo.di_physaddrlen > 0) {
goto ret;
}
hwaddr[0] = 1;
if (status == IPADM_SUCCESS)
}
goto ret;
}
ITAB_CONS_INFO, "clientid");
goto ret;
}
/*
* Now we parse the Client ID returned by the dhcpagent. It could be
* of type DSYM_OCTET (only for IPv4) or DSYM_DUID (for both IPv4
* and IPv6).
*/
/*
* Check to see if this is a RFC 4361 client identifier.
*/
if (n_octets <= 5) {
goto ret;
}
cp += 5;
n_octets -= 5;
/*
* A DUID was not found. This should be a simple
* hex string that can be parsed by using
* i_ipadm_parse_legacy_cid().
*/
if (status == IPADM_OBJ_NOTFOUND)
legacy_cid = B_TRUE;
else if (status != IPADM_SUCCESS)
goto ret;
} else {
legacy_cid = B_TRUE;
}
/*
* The client identifier is a simple hex string.
*/
if (legacy_cid) {
&ainfo->ia_clientid);
if (status != IPADM_SUCCESS)
goto ret;
}
if (status != IPADM_SUCCESS)
goto ret;
}
/*
* Get the Client ID type. If it is a DUID, the type is the first
* set of digits followed by the ',' separator. If not, we set it
* to 0 to indicate "other".
*/
goto ret;
switch (type) {
case DHCPV6_DUID_LLT:
break;
case DHCPV6_DUID_LL:
break;
case DHCPV6_DUID_EN:
break;
default:
break;
}
}
ret:
if (isv6)
return (status);
}
/*
* From the given hex input, parse a legacy (RFC 2132) client identifier
* which is a simple hex string.
*/
static ipadm_status_t
{
return (IPADM_NO_MEMORY);
while (n_octets-- > 0) {
}
return (IPADM_SUCCESS);
}
/*
* From the given hex input, parse a RFC3315 DUID. If no DUID is found,
* return IPADM_OBJ_NOTFOUND.
* The DUID is of the format
* decimal,data......
* where the value of the `decimal' ranges from 0-65536 and the `data'
* interpreted based on the `decimal' value, by inittab_decode() function.
*/
static ipadm_status_t
char **duid)
{
char *value;
/*
* For DUID-LL type of DUID, libdhcpagent returns a duid_llt_t type
* after adding a timestamp with the current time value.
* We need to convert it into type duid_ll_t before passing to
* inittab_decode() to print it in DUID-LL format.
*/
if (duidtype == DHCPV6_DUID_LL) {
/* LINTED E_BAD_PTR_CAST_ALIGN */
return (IPADM_NO_MEMORY);
} else {
}
return (IPADM_OBJ_NOTFOUND);
return (IPADM_NO_MEMORY);
}
return (IPADM_SUCCESS);
}
/*
* Returns the IP addresses of the specified interface in both the
* active and the persistent configuration. If no
* interface is specified, it returns all non-zero IP addresses
* configured on all interfaces in active and persistent
* configurations.
* `addrinfo' will contain addresses that are
* (1) in both active and persistent configuration (created persistently)
* (2) only in active configuration (created temporarily)
* (3) only in persistent configuration (disabled addresses)
*
* Address list that is returned by this function must be freed
* using the ipadm_freeaddr_info() function.
*/
{
return (IPADM_INVALID_ARG);
return (IPADM_INVALID_ARG);
}
flags, lifc_flags));
}
/*
* Frees the structure allocated by ipadm_addr_info().
*/
void
{
return;
free(a->ia_clientid);
}
/*
* Makes a door call to ipmgmtd to update its `aobjmap' with the address
* object in `ipaddr'. This door call also updates the persistent DB to
* remember address object to be recreated on next reboot or on an
* ipadm_enable_addr()/ipadm_enable_if() call.
*/
{
int err = 0;
/*
* Construct the nvl to send to the door.
*/
return (IPADM_NO_MEMORY);
ipaddr->ipadm_ifname)) != 0 ||
ipaddr->ipadm_lifnum)) != 0) {
goto ret;
}
switch (ipaddr->ipadm_atype) {
case IPADM_ADDR_STATIC:
!= 0) {
goto ret;
}
if (daddrstr[0] != '\0' &&
!= 0) {
goto ret;
}
if (!default_prefixlen) {
IPADM_NVP_PREFIXLEN, pval)) != 0) {
goto ret;
}
}
if (flags & IPADM_OPT_UP)
else
break;
case IPADM_ADDR_DHCP:
B_TRUE)) != 0 ||
ipaddr->ipadm_primary)) != 0 ||
ipaddr->ipadm_wait)) != 0) {
goto ret;
}
break;
case IPADM_ADDR_IPV6_ADDRCONF:
addrstr)) != 0 ||
pval)) != 0) {
goto ret;
}
IPADM_NVP_DINTFID, addrstr)) != 0) {
goto ret;
}
}
if (ipaddr->ipadm_stateless) {
IPADM_NVP_STATELESS, "yes");
} else {
IPADM_NVP_STATELESS, "no");
}
if (err != 0) {
goto ret;
}
if (ipaddr->ipadm_stateful) {
IPADM_NVP_STATEFUL, "yes");
} else {
IPADM_NVP_STATEFUL, "no");
}
break;
}
if (status != IPADM_SUCCESS)
goto ret;
/*
* IPMGMT_MODIFY tells the ipmgmtd to set both IPMGMT_ACTIVE
* and IPMGMT_PERSIST on the address object in its `aobjmap'.
* For the callers ipadm_enable_if() and ipadm_enable_addr(),
* IPADM_OPT_PERSIST is not set in their flags. They send
* IH_INIT in ih_flags, so that the address object will be
* set as both IPMGMT_ACTIVE and IPMGMT_PERSIST.
*/
pflags |= IPMGMT_MODIFY;
} else {
if (flags & IPADM_OPT_ACTIVE)
pflags |= IPMGMT_ACTIVE;
if (flags & IPADM_OPT_MODIFY)
pflags |= IPMGMT_MODIFY;
if (flags & IPADM_OPT_PERSIST)
pflags |= IPMGMT_PERSIST;
}
pflags);
ret:
return (status);
}
/*
* Makes the door call to ipmgmtd to store the address object in the
* nvlist `nvl'.
*/
static ipadm_status_t
{
int err;
if (err != 0)
return (ipadm_errno2status(err));
/*
* If the door call to write the configuration persistently is
* successful, send a door call to nwamd to read in this newly created
* configuration.
*/
B_TRUE);
}
return (ipadm_errno2status(err));
}
/*
* Makes a door call to ipmgmtd to remove the address object in `ipaddr'
* from its `aobjmap'. This door call also removes the address object and all
* its properties from the persistent DB if IPADM_OPT_PERSIST is set in
* `flags', so that the object will not be recreated on next reboot or on an
* ipadm_enable_addr()/ipadm_enable_if() call.
*/
{
int err;
if (flags & IPADM_OPT_ACTIVE)
if (flags & IPADM_OPT_PERSIST)
sizeof (arg.ia_aobjname));
/*
* If the door call to remove the configuration persistently is
* successful, send a door call to nwamd to read in this
* configuration. Since the interface still exists, nwamd has to read
* in the configuration rather than remove it.
*/
}
return (ipadm_errno2status(err));
}
/*
* Retrieves the address object corresponding to `aobjname' from ipmgmtd
* and retrieves the address flags for that object from kernel.
* The arguments `ipaddr' and `ifflags' must be allocated by the caller.
*/
static ipadm_status_t
{
/* check for solaris.network.interface.config authorization */
if (!ipadm_check_auth())
return (IPADM_INSUFF_AUTH);
/* validate input */
IPADM_AOBJSIZ) >= IPADM_AOBJSIZ) {
return (IPADM_INVALID_ARG);
}
/* Retrieve the address object information. */
if (status != IPADM_SUCCESS)
return (status);
return (IPADM_OP_DISABLE_OBJ);
if ((ipadm_flags & IPADM_OPT_PERSIST) &&
return (IPADM_TEMPORARY_OBJ);
(ipadm_flags & IPADM_OPT_PERSIST)))
return (IPADM_OP_NOTSUP);
}
/*
* Marks the address in the address object `aobjname' up. This operation is
* not supported for an address object of type IPADM_ADDR_IPV6_ADDRCONF.
* For an address object of type IPADM_ADDR_DHCP, this operation can
* only be temporary and no updates will be made to the persistent DB.
*/
{
&ifflags);
if (status != IPADM_SUCCESS)
return (status);
/*
* If the address is already a duplicate, then refresh-addr
* should be used to mark it up. For the legacy case, we should
* go ahead and try to mark it up to keep the backward
* compatibility.
*/
return (IPADM_DAD_FOUND);
goto persist;
} else {
IFF_UP, 0);
}
if (status != IPADM_SUCCESS)
return (status);
/* Update persistent DB. */
if (ipadm_flags & IPADM_OPT_PERSIST) {
&ipaddr, 0);
}
return (status);
}
/*
* Function called when an address on an underlying interface
* is being brought up.
*
* If the address is a data address, this function handles the migration
* of the data address from the underlying interface to the IPMP interface
* that might happen in the kernel. It involves removing the address object
* on the underif and recreating a new address object for the migrated address
* on the IPMP interface.
*/
static ipadm_status_t
{
int sock;
if (status != IPADM_SUCCESS)
return (status);
/*
* If we are bringing up the IPv6 interface, we might need to
* bring up the IPv6 part of the IPMP interface.
*/
if (status != IPADM_SUCCESS)
return (status);
IFF_UP, 0);
if (status != IPADM_SUCCESS)
return (status);
}
}
/*
* Get the data address to help find the new logical interface that
* will be created by kernel on the IPMP interface during migration.
*/
if (!(ifflags & IFF_NOFAILOVER)) {
return (ipadm_errno2status(errno));
}
}
/* Bring up the address */
return (status);
if (migrate) {
sizeof (ptr->im_aobjname));
return (IPADM_ADDROBJ_NOT_CREATED);
}
return (IPADM_SUCCESS);
}
/*
* Marks the address in the address object `aobjname' down. This operation is
* not supported for an address object of type IPADM_ADDR_IPV6_ADDRCONF.
* For an address object of type IPADM_ADDR_DHCP, this operation can
* only be temporary and no updates will be made to the persistent DB.
*/
{
&flags);
if (status != IPADM_SUCCESS)
return (status);
if (status != IPADM_SUCCESS)
return (status);
} else if (flags & IFF_DUPLICATE) {
/*
* Clear the IFF_DUPLICATE flag.
*/
return (ipadm_errno2status(errno));
return (ipadm_errno2status(errno));
}
/* Update persistent DB */
if (ipadm_flags & IPADM_OPT_PERSIST) {
"no", &ipaddr, 0);
}
return (status);
}
/*
* Refreshes the address in the address object `aobjname'. If the address object
* is of type IPADM_ADDR_STATIC, DAD is re-initiated on the address. If
* `ipadm_flags' has IPADM_OPT_INFORM set, a DHCP_INFORM message is sent to the
* dhcpagent for this static address. If the address object is of type
* IPADM_ADDR_DHCP, a DHCP_EXTEND message is sent to the dhcpagent.
* If a dhcp address has not yet been acquired, a DHCP_START is sent to the
* dhcpagent. This operation is not supported for an address object of
* type IPADM_ADDR_IPV6_ADDRCONF.
*/
{
((ipadm_flags & IPADM_OPT_INFORM) != 0);
int dherr;
/* check for solaris.network.interface.config authorization */
if (!ipadm_check_auth())
return (IPADM_INSUFF_AUTH);
/* validate input */
IPADM_AOBJSIZ) >= IPADM_AOBJSIZ) {
return (IPADM_INVALID_ARG);
}
/* Retrieve the address object information. */
if (status != IPADM_SUCCESS)
return (status);
return (IPADM_OP_DISABLE_OBJ);
return (IPADM_OP_NOTSUP);
return (IPADM_INVALID_ARG);
if (status != IPADM_SUCCESS)
return (status);
if (inform) {
return (IPADM_CANNOT_START_DHCP);
NULL));
}
if (!(flags & IFF_DUPLICATE))
return (IPADM_SUCCESS);
/*
* Restart the dhcp address negotiation with server if no
* address has been acquired yet.
*/
NULL);
}
} else {
}
return (status);
}
/*
* This is called from ipadm_create_addr() to validate the address parameters.
* It does the following steps:
* 1. Validates the interface name.
* 2. In case of a persistent operation, verifies that the interface
* is persistent. Returns error if interface is not enabled but
* is in persistent config.
* 3. Verifies that the destination address is not set for a non-POINTOPOINT
* interface.
* 4. Verifies that the address type is not DHCP or ADDRCONF when the
* interface is a vni or a loopback interface.
* 5. Verifies that the address type is not DHCP or ADDRCONF when the interface
* has IFF_VRRP interface flag set.
*/
static ipadm_status_t
{
char *ifname;
return (IPADM_INVALID_ARG);
return (IPADM_BAD_ADDR);
if (!legacy) {
if (ipaddr->ipadm_lifnum != 0)
return (IPADM_INVALID_ARG);
} else {
return (IPADM_OP_NOTSUP);
}
/*
* For legacy case, interfaces are not implicitly plumbed. We need to
* check if the interface exists in the active configuration.
*/
return (IPADM_NOSUCH_IF);
/*
* Check if one of the v4 or the v6 interfaces exists in the
* active configuration. An interface is considered disabled only
* if both v4 and v6 are not active.
*/
/* Check if interface exists in the persistent configuration. */
if (!a_exists) {
if (p_exists)
return (IPADM_OP_DISABLE_OBJ);
if (!islo)
return (IPADM_NOSUCH_IF);
} else {
/*
* If address has to be created persistently,
* and the interface does not exist in the persistent
* store but in active config, fail.
*/
return (IPADM_TEMPORARY_OBJ);
}
}
if (!islo) {
if (af_exists) {
} else {
&ifflags);
}
if (status != IPADM_SUCCESS)
return (status);
}
/* Perform validation steps (4) and (5) */
switch (ipaddr->ipadm_atype) {
case IPADM_ADDR_STATIC:
return (IPADM_INVALID_ARG);
/* Check for a valid src address */
if (!legacy && sockaddrunspec(
return (IPADM_BAD_ADDR);
break;
case IPADM_ADDR_DHCP:
return (IPADM_OP_NOTSUP);
break;
case IPADM_ADDR_IPV6_ADDRCONF:
return (IPADM_OP_NOTSUP);
}
break;
default:
return (IPADM_INVALID_ARG);
}
return (IPADM_SUCCESS);
}
/*
* Re-enables the address object `aobjname' based on the saved
* configuration for `aobjname'.
*/
{
int err;
/* check for solaris.network.interface.config authorization */
if (!ipadm_check_auth())
return (IPADM_INSUFF_AUTH);
/* validate input */
if (flags & IPADM_OPT_PERSIST)
return (IPADM_OP_NOTSUP);
IPADM_AOBJSIZ) >= IPADM_AOBJSIZ) {
return (IPADM_INVALID_ARG);
}
/* Retrieve the address object information. */
if (status != IPADM_SUCCESS)
return (status);
return (IPADM_ADDROBJ_EXISTS);
if (status != IPADM_SUCCESS)
return (status);
return (IPADM_OBJ_NOTFOUND);
}
if (err != 0) {
return (ipadm_errno2status(err));
}
return (status);
}
/*
* Disables the address object in `aobjname' from the active configuration.
* Error code return values follow the model in ipadm_delete_addr().
*/
{
/* validate input */
if (flags & IPADM_OPT_PERSIST)
return (IPADM_OP_NOTSUP);
}
/*
* Function used to migrate the list of address objects in `maddrs' from
* `underif' to `ipmpif'. This is called when an interface is added to an
* IPMP interface in legacy mode and the underif happens to have data
* addresses configured.
*/
const char *ipmpif)
{
int err;
if (list_is_empty(maddrs))
return (IPADM_SUCCESS);
return (ipadm_errno2status(err));
goto fail;
goto fail;
}
} else {
}
if (err == 0)
if (err != 0) {
goto fail;
}
}
if (nvlist_empty(nvl) ||
return (IPADM_NO_MEMORY);
}
goto fail;
fail:
return (ipadm_errno2status(err));
}
/*
* Gets the IPMP interface name for the given underlying interface `ifname'.
*/
{
if (status != IPADM_SUCCESS)
return (status);
return (ipadm_errno2status(errno));
return (IPADM_SUCCESS);
}