interface.c revision d04ccbb3f3163ae5962a8b7465d9796bff6ca434
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <search.h>
#include <libdevinfo.h>
#include <netinet/if_ether.h>
#include <dhcpmsg.h>
#include <dhcp_inittab.h>
#include "agent.h"
#include "interface.h"
#include "util.h"
#include "dlpi_io.h"
#include "packet.h"
#include "states.h"
/*
* Interface flags to watch: things that should be under our direct control.
*/
static void clear_lif_dhcp(dhcp_lif_t *);
/*
* insert_pif(): creates a new physical interface structure and chains it on
* the list. Initializes state that remains consistent across
* all use of the physical interface entry.
*
* input: const char *: the name of the physical interface
* boolean_t: if B_TRUE, this is DHCPv6
* int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
* error code with the reason why
* output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure
*/
{
"%s", pname);
return (NULL);
}
pname);
goto failure;
}
/* We do not use DLPI with DHCPv6 */
if (!isv6) {
/*
* Do the allocations necessary for IPv4 DHCP.
*
* 1. open the interface using DLPI
* 2. get the interface max SDU
* 3. get the interface hardware type and hardware length
* 4. get the interface hardware address
* 5. get the interface hardware broadcast address
*/
/* step 1 */
goto failure;
}
/* step 2 */
"large enough maximum SDU to support DHCP "
goto failure;
}
/* step 3 */
/* step 4 */
"pif_hwaddr for %s", pname);
goto failure;
}
}
/*
* depending on the DLPI device, the sap and hardware addresses
* can be in either order within the dlsap address; find the
* location of the hardware address using dl_sap_length. see
* the DLPI specification for more on this braindamage.
*/
if (dlia->dl_sap_length > 0) {
}
/* step 5 */
"pif_daddr for %s", pname);
goto failure;
}
/* Close the DLPI stream until actually needed */
}
/*
* This is a bit gross, but IP has a confused interface. We must
* assume that the zeroth LIF is plumbed, and must query there to get
* the interface index number.
*/
else
*error = DHCP_IPC_E_INT;
goto failure;
}
return (pif);
return (NULL);
}
/*
* hold_pif(): acquire a hold on a physical interface structure.
*
* input: dhcp_pif_t *: a pointer to the PIF structure
* output: none
*/
void
{
pif->pif_hold_count++;
}
/*
* release_pif(): release a hold on a physical interface structure; will
* destroy the structure on the last hold removed.
*
* input: dhcp_pif_t *: a pointer to the PIF structure
* output: none
*/
void
{
if (pif->pif_hold_count == 0) {
return;
}
if (--pif->pif_hold_count == 0) {
} else {
}
}
/*
* lookup_pif_by_index(): Looks up PIF entries given regular ifIndex.
*
* input: uint_t: the interface index
* boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
* output: dhcp_pif_t *: the matching PIF, or NULL if not found
*/
{
break;
}
return (pif);
}
/*
* lookup_pif_by_uindex(): Looks up PIF entries given truncated index and
* previous PIF pointer (or NULL for list start).
* Caller is expected to iterate through all
* potential matches to find interface of interest.
*
* input: uint16_t: the interface index (truncated)
* dhcp_pif_t *: the previous PIF, or NULL for list start
* boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
* output: dhcp_pif_t *: the next matching PIF, or NULL if not found
* note: This operates using the 'truncated' (16-bit) ifindex as seen by
* routing socket clients. The value stored in pif_index is the
* 32-bit ifindex from the ioctl interface.
*/
{
else
break;
}
return (pif);
}
/*
* lookup_pif_by_name(): Looks up a physical interface entry given a name.
*
* input: const char *: the physical interface name
* boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
* output: dhcp_pif_t *: the matching PIF, or NULL if not found
*/
{
break;
}
return (pif);
}
/*
* open_dlpi_pif(): register the use of DLPI I/O by a LIF on a PIF, opening
* the connection if necessary.
*
* input: dhcp_pif_t *: the physical interface on which to use DLPI
* output: boolean_t: B_TRUE on success, B_FALSE on failure.
*/
{
return (B_FALSE);
return (B_FALSE);
}
}
pif->pif_dlpi_count++;
return (B_TRUE);
}
/*
* close_dlpi_pif(): unregister the use of DLPI I/O by a LIF on a PIF, closing
* the connection if this was the last user.
*
* input: dhcp_pif_t *: the physical interface on which we're using DLPI
* output: none
*/
void
{
pif->pif_dlpi_count--;
return;
}
pif->pif_dlpi_count = 0;
}
}
}
/*
*
* input: dhcp_pif_t *: the physical interface on which we're using DLPI
* boolean_t: B_TRUE if the interface is going up
* output: none
*/
void
{
if (isup)
else
}
}
}
/* Helper for insert_lif: extract addresses as defined */
} else { \
}
/*
* insert_lif(): Creates a new logical interface structure and chains it on
* the list for a given physical interface. Initializes state
* that remains consistent across all use of the logical
* interface entry. Caller's PIF hold is transferred to the
* LIF on success, and is dropped on failure.
*
* input: dhcp_pif_t *: pointer to the physical interface for this LIF
* const char *: the name of the logical interface
* int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
* error code with the reason why
* output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure
*/
{
int fd;
"%s", lname);
return (NULL);
}
lname);
goto failure;
}
else
else
*error = DHCP_IPC_E_INT;
goto failure;
}
else
*error = DHCP_IPC_E_INT;
goto failure;
}
*error = DHCP_IPC_E_INT;
goto failure;
}
/*
* If we've just detected the interface going up or down, then signal
* an appropriate action. There may be other state machines here.
*/
}
*error = DHCP_IPC_E_INT;
lname);
goto failure;
}
*error = DHCP_IPC_E_INT;
lname);
goto failure;
}
lif->lif_broadcast =
}
cached_v6_max_mtu = 0;
else
cached_v4_max_mtu = 0;
return (lif);
return (NULL);
}
/*
* hold_lif(): acquire a hold on a logical interface structure.
*
* input: dhcp_lif_t *: a pointer to the LIF structure
* output: none
*/
void
{
lif->lif_hold_count++;
}
/*
* release_lif(): release a hold on a logical interface structure; will
* destroy the structure on the last hold removed.
*
* input: dhcp_lif_t *: a pointer to the LIF structure
* output: none
*/
void
{
if (lif->lif_hold_count == 0) {
return;
}
return;
}
if (--lif->lif_hold_count == 0) {
"release_lif: still holding lease at last hold!");
cached_v6_max_mtu = 0;
else
cached_v4_max_mtu = 0;
} else {
}
}
/*
* remove_lif(): remove a logical interface from its PIF and lease (if any) and
* the lease's hold on the LIF. Assumes that we did not plumb
* the interface.
*
* input: dhcp_lif_t *: a pointer to the LIF structure
* output: none
*/
void
{
if (lif->lif_plumbed) {
return;
}
if (lif->lif_removed) {
} else {
}
/* Remove from PIF list */
/* If we were part of a lease, then remove ourselves */
}
}
}
}
/*
* lookup_lif_by_name(): Looks up a logical interface entry given a name and
* a physical interface.
*
* input: const char *: the logical interface name
* const dhcp_pif_t *: the physical interface
* output: dhcp_lif_t *: the matching LIF, or NULL if not found
*/
{
break;
}
return (lif);
}
/*
* checkaddr(): checks if the given address is still set on the given LIF
*
* input: const dhcp_lif_t *: the LIF to check
* int: the address to look up on the interface (ioctl)
* const in6_addr_t *: the address to compare to
* const char *: name of the address for logging purposes
* output: boolean_t: B_TRUE if the address is still set; B_FALSE if not
*/
static boolean_t
const char *aname)
{
int fd;
return (B_FALSE);
}
"checkaddr: ignoring ioctl error on %s %x: %s",
} else if (isv6) {
struct sockaddr_in6 *sin6 =
char abuf1[INET6_ADDRSTRLEN];
char abuf2[INET6_ADDRSTRLEN];
"checkaddr: expected %s %s on %s, have %s",
return (B_FALSE);
}
} else {
struct sockaddr_in *sinp =
char abuf1[INET_ADDRSTRLEN];
char abuf2[INET_ADDRSTRLEN];
"checkaddr: expected %s %s on %s, have %s",
sizeof (abuf2)));
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* verify_lif(): verifies than a LIF is still valid (i.e., has not been
* explicitly or implicitly dropped or released)
*
* input: const dhcp_lif_t *: the LIF to verify
* output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise
*/
{
int fd;
return (B_FALSE);
}
/*
* If important flags have changed, then abandon the interface.
*/
return (B_FALSE);
}
/*
* Special case: if the interface has gone down as a duplicate, then
* this alone does _not_ mean that we're abandoning it just yet. Allow
* the state machine to handle this normally by trying to get a new
* lease.
*/
return (B_TRUE);
}
/*
* If the user has torn down or started up the interface manually, then
* abandon the lease.
*/
return (B_FALSE);
}
/*
* Check for delete and recreate.
*/
return (B_FALSE);
}
"verify_lif: ifindex on %s changed: %u to %u",
return (B_FALSE);
}
/*
* If the IP address, netmask, or broadcast address have changed, or
* the interface has been unplumbed, then we act like there has been an
* implicit drop. (Note that the netmask is under DHCP control for
* IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast
* addresses.)
*/
return (B_FALSE);
if (isv6) {
/*
* If it's not point-to-point, we're done. If it is, then
* check the peer's address as well.
*/
"peer address"));
} else {
"netmask"))
return (B_FALSE);
}
}
/*
* canonize_lif(): puts the interface in a canonical (zeroed) form. This is
* used only on the "main" LIF for IPv4. All other interfaces
* are under dhcpagent control and are removed using
* unplumb_lif().
*
* input: dhcp_lif_t *: the interface to canonize
* output: none
*/
static void
{
int fd;
/*
* If there's nothing here, then don't touch the interface. This can
* happen when an already-canonized LIF is recanonized.
*/
return;
return;
}
/* Should not happen */
"canonize_lif: cannot clear %s; flags are %llx",
return;
}
/*
* clear the UP flag, but don't clear DHCPRUNNING since
* that should only be done when the interface is removed
* (see clear_lif_dhcp() and remove_lif())
*/
return;
}
if (isv6) {
struct sockaddr_in6 *sin6 =
} else {
struct sockaddr_in *sinv =
}
"canonize_lif: can't clear local address on %s",
}
/* Netmask is under in.ndpd control with IPv6 */
}
"canonize_lif: can't clear remote address on %s",
}
} else if (!isv6) {
"canonize_lif: can't clear broadcast address on %s",
}
}
}
/*
* plumb_lif(): Adds the LIF to the system. This is used for all
* DHCPv6-derived interfaces. The returned LIF has a hold
* on it.
*
* input: dhcp_lif_t *: the interface to unplumb
* output: none
*/
{
char abuf[INET6_ADDRSTRLEN];
struct sockaddr_in6 *sin6;
int error;
"plumb_lif: entry for %s already exists!", abuf);
return (NULL);
}
}
/* First, create a new zero-address logical interface */
return (NULL);
}
/* Next, set the netmask to all ones */
goto failure;
}
/* Now set the interface address */
goto failure;
}
/* Mark the interface up */
goto failure;
}
goto failure;
}
/* Now we can create the internal LIF structure */
goto failure;
return (lif);
}
return (NULL);
}
/*
* unplumb_lif(): Removes the LIF from dhcpagent and the system. This is used
* for all interfaces configured by DHCP (those in leases).
*
* input: dhcp_lif_t *: the interface to unplumb
* output: none
*/
void
{
if (lif->lif_plumbed) {
}
}
/*
* Special case: if we're "unplumbing" the main LIF for DHCPv4, then
* just canonize it and remove it from the lease.
*/
}
} else {
}
}
/*
* attach_lif(): create a new logical interface, creating the physical
* interface as necessary.
*
* input: const char *: the logical interface name
* boolean_t: B_TRUE for IPv6
* int *: set to DHCP_IPC_E_* if creation fails
* output: dhcp_lif_t *: pointer to new entry, or NULL on failure
*/
{
*cp = '\0';
return (NULL);
lname);
return (NULL);
}
/* If LIF creation fails, then insert_lif discards our PIF hold */
}
/*
* set_lif_dhcp(): Set logical interface flags to show that it's managed
* by DHCP.
*
* input: dhcp_lif_t *: the logical interface
* boolean_t: B_TRUE if adopting
* output: int: set to DHCP_IPC_E_* if operation fails
*/
int
{
int fd;
int err;
}
/*
* Check for conflicting sources of address control, and other
* unacceptable configurations.
*/
return (DHCP_IPC_E_INVIF);
}
/*
* if DHCPRUNNING is already set on the interface and we're
* not adopting it, the agent probably crashed and burned.
* note it, but don't let it stop the proceedings. we're
* pretty sure we're not already running, since we wouldn't
* have been able to bind to our IPC port.
*/
if (!is_adopting) {
}
} else {
return (DHCP_IPC_E_INT);
}
}
return (DHCP_IPC_SUCCESS);
}
/*
* clear_lif_dhcp(): Clear logical interface flags to show that it's no longer
* managed by DHCP.
*
* input: dhcp_lif_t *: the logical interface
* output: none
*/
static void
{
int fd;
return;
return;
return;
}
/*
* set_lif_deprecated(): Set the "deprecated" flag to tell users that this
* address will be going away. As the interface is
* going away, we don't care if there are errors.
*
* input: dhcp_lif_t *: the logical interface
* output: none
*/
void
{
int fd;
return;
return;
return;
}
/*
* clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this
* address will not be going away. This happens if we
* get a renewal after preferred lifetime but before
* the valid lifetime.
*
* input: dhcp_lif_t *: the logical interface
* output: boolean_t: B_TRUE on success.
*/
{
int fd;
return (B_FALSE);
}
/*
* Check for conflicting sources of address control, and other
* unacceptable configurations.
*/
return (B_FALSE);
}
return (B_TRUE);
return (B_FALSE);
} else {
return (B_TRUE);
}
}
/*
* open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only).
*
* input: dhcp_lif_t *: the logical interface to operate on
* output: boolean_t: B_TRUE if the socket was opened successfully.
*/
{
return (B_FALSE);
}
return (B_FALSE);
}
return (B_FALSE);
}
"receive IP unicast");
return (B_FALSE);
}
return (B_TRUE);
}
/*
* close_ip_lif(): close an IP socket for I/O on a given LIF.
*
* input: dhcp_lif_t *: the logical interface to operate on
* output: none
*/
void
{
}
}
}
/*
* lif_mark_decline(): mark a LIF as having been declined due to a duplicate
* address or some other conflict. This is used in
* send_declines() to report failure back to the server.
*
* input: dhcp_lif_t *: the logical interface to operate on
* const char *: text string explaining why the address is declined
* output: none
*/
void
{
}
}
/*
* schedule_lif_timer(): schedules the LIF-related timer
*
* input: dhcp_lif_t *: the logical interface to operate on
* dhcp_timer_t *: the timer to schedule
* iu_tq_callback_t *: the callback to call upon firing
* output: boolean_t: B_TRUE if the timer was scheduled successfully
*/
{
/*
* If there's a timer running, cancel it and release its lease
* reference.
*/
if (!cancel_timer(dt))
return (B_FALSE);
}
return (B_TRUE);
} else {
"schedule_lif_timer: cannot schedule timer");
return (B_FALSE);
}
}
/*
* cancel_lif_timer(): cancels a LIF-related timer
*
* input: dhcp_lif_t *: the logical interface to operate on
* dhcp_timer_t *: the timer to cancel
* output: none
*/
static void
{
return;
if (cancel_timer(dt)) {
"cancel_lif_timer: canceled expiry timer on %s",
} else {
"cancel_lif_timer: cannot cancel timer on %s",
}
}
/*
* cancel_lif_timers(): cancels the LIF-related timers
*
* input: dhcp_lif_t *: the logical interface to operate on
* output: none
*/
void
{
}
/*
* get_max_mtu(): find the maximum MTU of all interfaces for I/O on common
* file descriptors (v4_sock_fd and v6_sock_fd).
*
* input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4
* output: none
*/
{
if (*mtup == 0) {
/* Set an arbitrary lower bound */
*mtup = 1024;
}
}
}
}
return (*mtup);
}
/*
* expired_lif_state(): summarize the state of expired LIFs on a given state
* machine.
*
* input: dhcp_smach_t *: the state machine to scan
* output: dhcp_expire_t: overall state
*/
{
if (lif->lif_expired)
numexp++;
}
}
if (numlifs == 0)
return (DHCP_EXP_NOLIFS);
else if (numexp == 0)
return (DHCP_EXP_NOEXP);
return (DHCP_EXP_ALLEXP);
else
return (DHCP_EXP_SOMEEXP);
}
/*
* find_expired_lif(): find the first expired LIF on a given state machine
*
* input: dhcp_smach_t *: the state machine to scan
* output: dhcp_lif_t *: the first expired LIF, or NULL if none.
*/
{
if (lif->lif_expired)
return (lif);
}
}
return (NULL);
}
/*
* remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING. Used
* only for DHCPv6.
*
* input: none
* output: none
*/
void
remove_v6_strays(void)
{
/*
* Get the approximate number of interfaces in the system. It's only
* approximate because the system is dynamic -- interfaces may be
* plumbed or unplumbed at any time. This is also the reason for the
* "+ 10" fudge factor: we're trying to avoid unnecessary looping.
*/
"remove_v6_strays: cannot read number of interfaces");
numifs = 10;
} else {
}
/*
* Get the interface information. We do this in a loop so that we can
* recover from EINVAL from the kernel -- delivered when the buffer is
* too small.
*/
for (;;) {
"remove_v6_strays: cannot allocate memory");
return;
}
errno = 0;
break;
numifs <<= 1;
} else {
return;
}
}
/*
* Get the interface flags; we're interested in the DHCP ones.
*/
continue;
if (!(flags & IFF_DHCPRUNNING))
continue;
/*
* If the interface has a link-local address, then we don't
* control it. Just remove the flag.
*/
continue;
continue;
}
/*
* All others are (or were) under our control. Clean up by
* removing them.
*/
"remove_v6_strays: SIOCLIFREMOVEIF %s",
}
}
}