/*
* 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 <errno.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <stropts.h>
#include <strings.h>
#include <libdlpi.h>
#include <libdllink.h>
#include <libinetutil.h>
#include <limits.h>
#include <zone.h>
#include <ipadm_ndpd.h>
#include "libipadm_impl.h"
/*
* Returns B_FALSE if the interface in `ifname' has at least one address that is
* IFF_UP in the addresses in `ifa'.
*/
static boolean_t
{
char *sep;
*sep = '\0';
/*
* If this condition is true, there is at least one
* address that is IFF_UP. So, we need to return B_FALSE.
*/
return (B_FALSE);
}
}
/* We did not find any IFF_UP addresses. */
return (B_TRUE);
}
/*
* Retrieves the information for the interface `ifname' from active
* config if `ifname' is specified and returns the result in the list `if_info'.
* Otherwise, it retrieves the information for all the interfaces in
* the active config and returns the result in the list `if_info'.
*/
static ipadm_status_t
{
int s;
int n;
int numifs;
/*
* Get information for all interfaces.
*/
return (ipadm_errno2status(errno));
/* Skip interfaces with logical num != 0 */
continue;
/*
* Skip the current interface if a specific `ifname' has
* been requested and current interface does not match
* `ifname'.
*/
continue;
/*
* Check if the interface already exists in our list.
* If it already exists, we need to update its flags.
*/
break;
}
goto fail;
}
/* Update the `ifi_next' pointer for this new node */
else
}
/*
* Retrieve the flags for the interface by doing a
* SIOCGLIFFLAGS to populate the `ifi_cflags' field.
*/
continue;
}
return (IPADM_SUCCESS);
fail:
return (status);
}
/*
* Returns the interface information for `ifname' in `if_info' from persistent
* config if `ifname' is non-null. Otherwise, it returns all the interfaces
* from persistent config in `if_info'.
*/
static ipadm_status_t
{
int i = 0, err = 0;
return (ipadm_errno2status(errno));
return (ipadm_errno2status(err));
return (IPADM_SUCCESS);
} else if (err != 0) {
return (ipadm_errno2status(err));
}
break;
}
}
return (status);
}
/*
* Collects information for `ifname' if one is specified from both
* active and persistent config in `if_info'. If no `ifname' is specified,
* this returns all the interfaces in active and persistent config in
* `if_info'.
*/
{
/*
* Retrive the information for the requested `ifname' or all
* interfaces from active configuration.
*/
if (status != IPADM_SUCCESS)
return (status);
/* Get the interface state for each interface in `aifinfo'. */
/* We need all addresses to get the interface state */
LIFC_ALLZONES|LIFC_UNDER_IPMP)) != 0) {
goto fail;
}
/*
* Find the `ifaddrs' structure from `ifa'
* for this interface. We need the IFF_* flags
* to find the interface state.
*/
break;
}
/*
* The interface might have been removed
* from kernel. Retry getting all the active
* interfaces.
*/
goto retry;
}
else
}
}
/*
* Get the persistent interface information in `pifinfo'.
*/
if (status == IPADM_NOTFOUND) {
return (IPADM_SUCCESS);
}
if (status != IPADM_SUCCESS)
goto fail;
/*
* If a persistent interface is also found in `aifinfo', update
* its entry in `aifinfo' with the persistent information from
* `pifinfo'. If an interface is found in `pifinfo', but not in
* `aifinfo', it means that this interface was disabled. We should
* add this interface to `aifinfo' and set it state to IFIF_DISABLED.
*/
break;
}
}
goto fail;
}
else
}
}
return (IPADM_SUCCESS);
fail:
return (status);
}
int
{
return (0);
}
/*
* Sets the output argument `exists' to true or false based on whether
* any persistent configuration is available for `ifname' and returns
* IPADM_SUCCESS as status. If the persistent information cannot be retrieved,
* `exists' is unmodified and an error status is returned.
*/
{
/*
* if IPH_IPMGMTD is set, we know that the caller (ipmgmtd) already
* knows about persistent configuration in the first place, so we
* just return success.
*/
return (IPADM_SUCCESS);
}
if (status == IPADM_SUCCESS) {
} else if (status == IPADM_NOTFOUND) {
}
return (status);
}
/*
* the bottom of the stream for tunneling interfaces.
*/
{
int err;
return (ipadm_errno2status(errno));
/*
* Pop off all undesired modules (note that the user may have
* configured autopush to add modules above udp), and push the
* arp module onto the resulting stream. This is used to make
* IP+ARP be able to atomically track the muxid for the I_PLINKed
* STREAMS, thus it isn't related to ARP running the ARP protocol.
*/
;
return (IPADM_SUCCESS);
return (ipadm_errno2status(err));
}
/*
* i_ipadm_create_ipmp() is called from i_ipadm_create_ipmp_peer() when an
* underlying interface in an ipmp group G is plumbed for an address family,
* but the meta-interface for the other address family `af' does not exist
* yet for the group G. If `af' is IPv6, we need to bring up the
* link-local address.
*/
static ipadm_status_t
{
int sock;
int err;
/* Create the ipmp underlying interface */
return (status);
/*
* To preserve backward-compatibility, always bring up the link-local
* address for implicitly-created IPv6 IPMP interfaces.
*/
/*
* If the caller requested a different group name, issue a
* SIOCSLIFGROUPNAME on the new IPMP interface.
*/
/* Remove the interface we created. */
if (status == IPADM_SUCCESS) {
}
return (ipadm_errno2status(err));
}
}
return (IPADM_SUCCESS);
}
/*
* Checks if `ifname' is plumbed and in an IPMP group on its "other" address
* family. If so, create a matching IPMP group for address family `af'.
*/
static ipadm_status_t
{
int other_af_sock;
/*
* iph is the handle for the interface that we are trying to plumb.
* other_af_sock is the socket for the "other" address family.
*/
return (IPADM_SUCCESS);
return (IPADM_SUCCESS);
/*
* If `ifname' *is* the IPMP group interface, or if the relevant
* address family is already configured, then there's nothing to do.
*/
return (IPADM_SUCCESS);
}
return (status);
}
/*
* Issues the ioctl SIOCSLIFNAME to kernel on the given ARP stream fd.
*/
static ipadm_status_t
{
/*
* Tell ARP the name and unit number for this interface.
* Note that arp has no support for transparent ioctls.
*/
sizeof (lifr)) == -1) {
return (ipadm_errno2status(errno));
}
return (IPADM_SUCCESS);
}
/*
* Issues the ioctl SIOCSLIFNAME to kernel. If IPADM_OPT_GENPPA is set in
* `ipadm_flags', then a ppa will be generated. `newif' will be updated
* with the generated ppa.
*/
static ipadm_status_t
{
int err = 0;
int ppa;
if (ipadm_flags & IPADM_OPT_GENPPA) {
/*
* We'd like to just set lifr_ppa to UINT_MAX and have the
* kernel pick a PPA. Unfortunately, that would mishandle
* two cases:
*
* 1. If the PPA is available but the groupname is taken
* (e.g., the "ipmp2" IP interface name is available
* but the "ipmp2" groupname is taken) then the
* auto-assignment by the kernel will fail.
*
* 2. If we're creating (e.g.) an IPv6-only IPMP
* interface, and there's already an IPv4-only IPMP
* interface, the kernel will allow us to accidentally
* reuse the IPv6 IPMP interface name (since
* SIOCSLIFNAME uniqueness is per-interface-type).
* This will cause administrative confusion.
*
* Thus, we instead take a brute-force approach of checking
* whether the IPv4 or IPv6 name is already in-use before
* attempting the SIOCSLIFNAME. As per (1) above, the
* SIOCSLIFNAME may still fail, in which case we just proceed
* to the next one. If this approach becomes too slow, we
* can add a new SIOC* to handle this case in the kernel.
*/
continue;
continue;
break;
}
if (err == -1) {
} else {
/*
* PPA has been successfully established.
* Update `newif' with the ppa.
*/
return (IPADM_INVALID_ARG);
}
} else {
/* We should have already validated the interface name. */
/*
* Before we call SIOCSLIFNAME, ensure that the IPMP group
* interface for this address family exists. Otherwise, the
* kernel will kick the interface out of the group when we do
* the SIOCSLIFNAME.
*
* Example: suppose bge0 is plumbed for IPv4 and in group "a".
* If we're now plumbing bge0 for IPv6, but the IPMP group
* interface for "a" is not plumbed for IPv6, the SIOCSLIFNAME
* will kick bge0 out of group "a", which is undesired.
*/
else
if (status != IPADM_SUCCESS)
return (status);
}
return (status);
}
/*
* Plumbs the interface `ifname' for the address family `af'. It also persists
* the interface for `af' if IPADM_OPT_PERSIST is set in `ipadm_flags'.
*/
{
int ip_muxid;
char *udp_dev_name;
char *linkname;
int sock;
((ipadm_flags & IPADM_OPT_PERSIST) != 0);
}
/*
* If we're in the global zone and we're plumbing a datalink, make
* sure that the datalink is not assigned to a non-global zone. Note
* that the non-global zones don't need this check, because zoneadm
* has taken care of this when the zones boot.
*/
/* interface is in use by a non-global zone. */
return (IPADM_IF_INUSE);
}
}
/* loopback interfaces are just added as logical interface */
else
return (IPADM_IF_EXISTS);
return (ipadm_errno2status(errno));
/*
* By default, kernel configures 127.0.0.1 on the loopback
* interface. Replace this with 0.0.0.0 to be consistent
* with interface creation on other physical interfaces.
*/
return (ipadm_errno2status(errno));
if (is_persistent) {
if (status != IPADM_SUCCESS) {
}
}
}
return (status);
}
/*
* If IPADM_OPT_IPMP is specified, then this is a request
* pass "ipmpstub0" as devname since an admin *could* have a normal
* vanity-named link named "ipmpstub0" that they'd like to plumb.)
*/
if (ipadm_flags & IPADM_OPT_IPMP) {
linkname = "ipmpstub0";
} else {
/*
* Verify that the user is not creating a persistent
* IP interface on a non-persistent data-link.
*/
return (IPADM_TEMPORARY_OBJ);
}
}
/*
* We use DLPI_NOATTACH because the ip module will do the attach
* itself for DLPI style-2 devices.
*/
return (IPADM_DLPI_FAILURE);
goto done;
}
/*
* to IFF_IPv4, IFF_IPV6, IFF_BROADCAST, IFF_XRESOLV, IFF_NOLINKLOCAL.
*/
ifflags = 0;
/* Set the name string and the IFF_IPV* flag */
} else {
/*
* With the legacy method, the link-local address should be
* configured as part of the interface plumb, using the default
* token. If IPH_LEGACY is not specified, we want to set :: as
* the address and require the admin to explicitly call
* ipadm_create_addr() with the address object type set to
* IPADM_ADDR_IPV6_ADDRCONF to create the link-local address
* as well as the autoconfigured addresses.
*/
}
if (status != IPADM_SUCCESS)
goto done;
/* Get the full set of existing flags for this stream */
if (status != IPADM_SUCCESS)
goto done;
if (status != IPADM_SUCCESS)
goto done;
/* Check if arp is not needed */
/*
* PLINK the interface stream so that the application can exit
* without tearing down the stream.
*/
goto done;
}
/*
* This interface does use ARP, so set up a separate stream
* from the interface to ARP.
*
* We use DLPI_NOATTACH because the arp module will do the attach
* itself for DLPI style-2 devices.
*/
goto done;
}
goto done;
}
if (status != IPADM_SUCCESS)
goto done;
/*
* PLINK the IP and ARP streams so that ifconfig can exit
* without tearing down the stream.
*/
goto done;
}
}
done:
if (mux_fd != -1)
if (status == IPADM_SUCCESS) {
/* copy back new ifname */
/*
* If it is a 6to4 tunnel, create a default
* addrobj name for the default address on the 0'th
* logical interface and set IFF_UP in the interface flags.
*/
if (status != IPADM_SUCCESS)
return (status);
if (status != IPADM_SUCCESS)
return (status);
addr.ipadm_lifnum = 0;
sizeof (lifname));
IFF_UP, 0);
if (status != IPADM_SUCCESS)
return (status);
} else {
/*
* Prevent static IPv6 addresses from triggering
* autoconf. This does not have to be done for
* 6to4 tunnel interfaces, since in.ndpd will
* not autoconfigure those interfaces.
*/
(void) i_ipadm_disable_autoconf(newif);
}
/*
* If IPADM_OPT_PERSIST was set in flags, store the
* interface in persistent DB.
*/
if (is_persistent) {
if (status != IPADM_SUCCESS) {
}
}
}
if (status == IPADM_EXISTS)
return (status);
}
/*
* Unplumbs the interface in `ifname' of family `af'.
*/
{
char *udp_dev_name;
int save_errno;
int sock;
/* Just do SIOCLIFREMOVEIF on loopback interfaces */
if (i_ipadm_is_loopback(ifname) ||
return (ipadm_errno2status(errno));
}
return (IPADM_SUCCESS);
}
/*
* the same now for PUNLINK also.
*/
if (v6) {
} else {
}
goto done;
}
if (ret != IPADM_SUCCESS)
goto done;
goto done;
}
/*
* There are two reasons the I_PUNLINK can fail with EBUSY:
* (1) if IP interfaces are in the group, or (2) if IPMP data
* addresses are administratively up. For case (1), we fail
* here with a specific error message. For case (2), we bring
* down the addresses prior to doing the I_PUNLINK. If the
* I_PUNLINK still fails with EBUSY then the configuration
* must have changed after our checks, in which case we branch
* back up to `again' and rerun this logic. The net effect is
* that unplumbing an IPMP interface will only fail with EBUSY
* if IP interfaces are in the group.
*/
goto done;
}
goto done;
}
goto done;
}
/*
* The kernel will fail the I_PUNLINK if the IPMP interface
* has administratively up addresses; bring them down.
*/
0, &ifaddrs) == -1) {
goto done;
}
continue;
goto done;
}
if (ret != IPADM_SUCCESS) {
goto done;
}
goto done;
}
}
}
}
goto done;
}
/*
* We don't have a good way of knowing whether the arp stream is
* plumbed. We can't rely on IFF_NOARP because someone could
* have turned it off later using "ifconfig xxx -arp".
*/
if (arp_muxid != 0) {
/*
* See the comment before the SIOCGLIFGROUPNAME call.
*/
goto again;
/*
* Some plumbing utilities set the muxid to
* -1 or some invalid value to signify that
* there is no arp stream. Set the muxid to 0
* before trying to unplumb the IP stream.
* IP does not allow the IP stream to be
* unplumbed if it sees a non-null arp muxid,
* for consistency of IP-ARP streams.
*/
lifr.lifr_arp_muxid = 0;
}
/*
* In case of any other error, we continue with
* the unplumb.
*/
}
}
if (changed_arp_muxid) {
/*
* Some error occurred, and we need to restore
* everything back to what it was.
*/
save_errno = errno;
errno = save_errno;
}
/*
* See the comment before the SIOCGLIFGROUPNAME call.
*/
goto again;
}
done:
if (muxid_fd != -1)
if (mux_fd != -1)
/*
* in.ndpd maintains the phyints in its memory even after
* the interface is plumbed, so that it can be reused when
* the interface gets plumbed again. The default behavior
* of in.ndpd is to start autoconfiguration for an interface
* that gets plumbed. We need to send the
* message IPADM_ENABLE_AUTOCONF to in.ndpd to restore this
* default behavior on replumb.
*/
(void) i_ipadm_enable_autoconf(ifname);
}
return (ret);
}
/*
* Saves the given interface name `ifname' with address family `af' in
* persistent DB.
*/
static ipadm_status_t
{
int err;
return (ipadm_errno2status(err));
}
/*
* Remove the IP interface from active configuration. If IPADM_OPT_PERSIST
* is set in `ipadm_flags', it is also removed from persistent configuration.
*/
{
char *cp;
if (ret != IPADM_SUCCESS)
goto done;
/*
* This is a non-zero logical interface.
* Find the addrobj and remove it from the daemon's memory.
*/
*cp++ = '\0';
sizeof (ipaddr.ipadm_ifname));
if (ret == IPADM_SUCCESS) {
} else if (ret == IPADM_NOTFOUND) {
ret = IPADM_SUCCESS;
}
return (ret);
}
done:
/*
* Even if interface does not exist, remove all its addresses and
* properties from the persistent store. If interface does not
* exist both in kernel and the persistent store, return IPADM_ENXIO.
*/
if (db_status == IPADM_SUCCESS)
ret = IPADM_SUCCESS;
}
return (ret);
}
/*
* Resets all addresses on interface `ifname' with address family `af'
* from ipmgmtd daemon. If is_persistent = B_TRUE, all interface properties
* and address objects of `ifname' for `af' are also removed from the
* persistent DB.
*/
{
int err;
if (is_persistent)
return (ipadm_errno2status(err));
}
/*
* Create the interface by plumbing it for IP.
* This function will check if there is saved configuration information
* for `ifname' and return IPADM_OP_DISABLE_OBJ if the name-space
* for `ifname' is taken.
*/
{
/*
* Return error, if the interface already exists in either the active
* or the persistent configuration.
*/
return (IPADM_IF_EXISTS);
if (status != IPADM_SUCCESS)
return (status);
if (p_exists) {
return (IPADM_OP_DISABLE_OBJ);
else
}
}
}
/*
* Plumbs an interface. Creates both IPv4 and IPv6 interfaces by
* default, unless a value in `af' is specified. The interface may be plumbed
* only if there is no previously saved persistent configuration information
* for the interface (in which case the ipadm_enable_if() function must
* be used to enable the interface).
*
* Returns: IPADM_SUCCESS, IPADM_FAILURE, IPADM_IF_EXISTS,
* IPADM_IF_PERSIST_EXISTS, IPADM_DLPI_FAILURE,
* or appropriate ipadm_status_t corresponding to the errno.
*
* `ifname' must point to memory that can hold upto LIFNAMSIZ chars. It may
* be over-written with the actual interface name when a PPA has to be
* internally generated by the library.
*/
{
/* Check for the required authorization */
if (!ipadm_check_auth())
return (IPADM_EAUTH);
!(flags & IPADM_OPT_ACTIVE)) ||
IPADM_OPT_GENPPA))) {
return (IPADM_INVALID_ARG);
}
if (flags & IPADM_OPT_GENPPA) {
return (IPADM_INVALID_ARG);
} else {
return (IPADM_INVALID_ARG);
}
return (IPADM_INVALID_ARG);
if (status != IPADM_SUCCESS)
return (status);
created_v4 = B_TRUE;
}
if (status != IPADM_SUCCESS) {
if (created_v4) {
}
return (status);
}
}
return (IPADM_SUCCESS);
}
/*
* Deletes the interface in `ifname'. Removes both IPv4 and IPv6 interfaces
* when `af' = AF_UNSPEC.
*/
{
/* Check for the required authorization */
if (!ipadm_check_auth())
return (IPADM_EAUTH);
/* Validate the `ifname' for any logical interface. */
return (IPADM_INVALID_ARG);
/*
* If the family has been uniquely identified, we return the
* associated status, even if that is ENXIO. Calls from ifconfig
* this category.
*/
return (status1);
return (status2);
return (IPADM_INVALID_ARG);
/*
* If af is AF_UNSPEC, then we return the following:
* status1, if status1 == status2
* IPADM_SUCCESS, if either of status1 or status2 is SUCCESS
* and the other status is ENXIO
* IPADM_ENXIO, if both status1 and status2 are ENXIO
* IPADM_FAILURE otherwise.
*/
/* covers the case when both status1 and status2 are ENXIO */
return (status1);
if (status1 == IPADM_SUCCESS)
else
} else {
return (IPADM_FAILURE);
}
}
/*
* Returns information about all interfaces in both active and persistent
* configuration. If `ifname' is not NULL, it returns only the interface
* identified by `ifname'.
*
* Return values:
* On success: IPADM_SUCCESS.
* On error : IPADM_INVALID_ARG, IPADM_ENXIO or IPADM_FAILURE.
*/
{
return (IPADM_INVALID_ARG);
return (IPADM_INVALID_ARG);
}
if (status != IPADM_SUCCESS)
return (status);
return (IPADM_ENXIO);
return (IPADM_SUCCESS);
}
/*
* Frees the linked list allocated by ipadm_if_info().
*/
void
{
}
}
/*
* Re-enable the interface `ifname' based on the saved configuration
* for `ifname'.
*/
{
/* Check for the required authorization */
if (!ipadm_check_auth())
return (IPADM_EAUTH);
/* Check for logical interfaces. */
return (IPADM_INVALID_ARG);
/* Enabling an interface persistently is not supported. */
if (flags & IPADM_OPT_PERSIST)
return (IPADM_NOTSUP);
/*
* Return early by checking if the interface is already enabled.
*/
return (IPADM_IF_EXISTS);
}
/*
* Enable the interface and restore all its interface properties
* and address objects.
*/
if (status != IPADM_SUCCESS)
return (status);
/*
* ipadm_enable_if() does exactly what ipadm_init_ifs() does,
* but only for one interface. We need to set IPH_INIT because
* ipmgmtd daemon does not have to write the interface to persistent
* db. The interface is already available in persistent db
* and we are here to re-enable the persistent configuration.
*/
return (status);
}
/*
* Disable the interface `ifname' by removing it from the active configuration.
* Error code return values follow the model in ipadm_delete_if()
*/
{
/* Check for the required authorization */
if (!ipadm_check_auth())
return (IPADM_EAUTH);
/* Check for logical interfaces. */
return (IPADM_INVALID_ARG);
/* Disabling an interface persistently is not supported. */
if (flags & IPADM_OPT_PERSIST)
return (IPADM_NOTSUP);
if (status1 == IPADM_SUCCESS)
if (status2 == IPADM_SUCCESS)
return (status2);
if (status1 == IPADM_SUCCESS)
else
} else {
return (IPADM_FAILURE);
}
}
/*
* This workaround is until libipadm supports IPMP and is required whenever an
* interface is moved into an IPMP group. Since libipadm doesn't support IPMP
* yet, we will have to update the daemon's in-memory mapping of
* `aobjname' to 'lifnum'.
*
* For `IPMGMT_ACTIVE' case, i_ipadm_delete_ifobj() would only fail if
* door_call(3C) fails. Also, there is no use in returning error because
* `ifname' would have been successfuly moved into IPMP group, by this time.
*/
void
{
}