/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "defs.h"
#include "tables.h"
#include <time.h>
#include <assert.h>
int num_of_phyints = 0;
int bits);
/* 1 week in ms */
struct phyint *
{
break;
}
return (pi);
}
struct phyint *
{
break;
}
return (pi);
}
struct phyint *
{
int i;
return (NULL);
}
/*
* Copy the defaults from the defaults array.
* Do not copy the cf_notdefault fields since these have not
* been explicitly set for the phyint.
*/
for (i = 0; i < I_IFSIZE; i++)
/*
* TmpDesyncFactor is used to desynchronize temporary token
* generation among systems; the actual preferred lifetime value
* of a temporary address will be (TmpPreferredLifetime -
* TmpDesyncFactor). It's a random value, with a user-configurable
* maximum value. The value is constant throughout the lifetime
* of the in.ndpd process, but can change if the daemon is restarted,
* per RFC3041.
*/
if (pi->pi_TmpMaxDesyncFactor != 0) {
/* we actually want [1,max], not [0,(max-1)] */
pi->pi_TmpDesyncFactor++;
}
return (NULL);
}
return (NULL);
}
}
return (pi);
}
/* Insert in linked list */
static void
{
/* Insert in list */
if (phyints)
}
/*
* Initialize both the phyint data structure and the pi_sock for
* sending and receving on the interface.
* Extract information from the kernel (if present) and set pi_kernel_state.
*/
int
{
int fd;
int save_errno;
return (-1);
}
} else {
}
if (newsock) {
}
}
return (0);
}
goto error;
}
/*
* Interface has been re-plumbed, lets open a new socket.
* quite frequently.
*/
goto start_over;
}
goto error;
}
/*
* If the link local interface is not up yet or it's IFF_UP and the
* IFF_NOLOCAL flag is set, then ignore the interface.
*/
if (newsock) {
}
}
return (0);
}
goto error;
}
goto error;
}
goto error;
}
/* Ignore interface if the token is all zeros */
goto error;
}
}
/*
* Guess a remote token for POINTOPOINT by looking at
* the link-local destination address.
*/
goto error;
}
} else {
/* Clear link-local prefix (first 10 bits) */
}
} else {
}
if (newsock) {
/* Set default values */
pi->pi_CurHopLimit = 0;
/* Setup socket for transmission and reception */
"IPV6_BOUND_IF");
goto error;
}
ttl = IPV6_MAX_HOPS;
"IPV6_UNICAST_HOPS");
goto error;
}
"IPV6_MULTICAST_HOPS");
goto error;
}
/*
* One benign reason IPV6_JOIN_GROUP could fail is
* when `pi' has been placed into an IPMP group and we
* haven't yet processed the routing socket message
* informing us of its disappearance. As such, if
* it's now in a group, don't print an error.
*/
save_errno = errno;
errno = save_errno;
"setsockopt IPV6_JOIN_GROUP");
}
goto error;
}
/*
* Filter out so that we only receive router advertisements and
* router solicitations.
*/
"ICMP6_FILTER");
goto error;
}
/* Enable receipt of ancillary data */
"IPV6_RECVHOPLIMIT");
goto error;
}
"IPV6_RECVRTHDR");
goto error;
}
}
if (pi->pi_AdvSendAdvertisements &&
/*
* See IPV6_JOIN_GROUP comment above.
*/
save_errno = errno;
errno = save_errno;
"setsockopt IPV6_JOIN_GROUP");
}
goto error;
}
}
/*
* If not already set, set the IFF_ROUTER interface flag based on
* AdvSendAdvertisements. Note that this will also enable IPv6
* forwarding on the interface. We don't clear IFF_ROUTER if we're
* not advertising on an interface, because we could still be
* forwarding on those interfaces.
*/
goto error;
}
goto error;
}
}
/* Set linkinfo parameters */
/* Setting maxmtu to 0 means that we're leaving the MTU alone */
goto error;
}
}
return (0);
/* Pretend the interface does not exist in the kernel */
if (newsock) {
}
return (-1);
}
/*
* Delete (unlink and free).
* Handles delete of things that have not yet been inserted in the list.
*/
void
{
assert(num_of_phyints > 0);
while (pi->pi_router_list)
while (pi->pi_prefix_list) {
}
while (pi->pi_adv_prefix_list)
}
}
} else {
}
}
/*
* Called with the number of milliseconds elapsed since the last call.
* Determines if any timeout event has occurred and
* returns the number of milliseconds until the next timeout event
* for the phyint itself (excluding prefixes and routers).
* Returns TIMER_INFINITY for "never".
*/
{
if (pi->pi_AdvSendAdvertisements) {
}
"state %d -> %d\n",
(int)pi->pi_adv_state);
}
}
} else {
}
"state %d -> %d\n",
(int)pi->pi_sol_state);
}
}
}
/*
* If the phyint has been unplumbed, we don't want to call
* phyint_reach_random. We will be in the NO_ADV or NO_SOLICIT state.
*/
(!pi->pi_AdvSendAdvertisements &&
}
return (next);
}
static void
{
"num routers %d\n",
if (pi->pi_TmpAddrsEnabled) {
"maxdesync %d desync %d regen %d\n",
}
}
"BaseReachableTime %d\n\tReachableTime %d RetransTimer %d\n",
if (!pi->pi_AdvSendAdvertisements) {
/* Solicit state */
} else {
/* Advertise state */
"since last %d\n",
}
}
}
/*
* Store the LLA for the phyint `pi' `lifrp'. Returns 0 on success, or
* -1 on failure.
*
* Note that we do not cache the hardware address since there's no reliable
* mechanism to determine when it's become stale.
*/
int
{
/* If this phyint doesn't have a link-layer address, bail */
return (-1);
}
/*
* For IPMP interfaces, don't report ESRCH errors since that
* merely indicates that there are no active interfaces in the
* IPMP group (and thus there's no working hardware address),
* and the packet will thus never make it out anyway.
*/
return (-1);
}
return (0);
}
/*
* Randomize pi->pi_ReachableTime.
* Done periodically when there are no RAs and at a maximum frequency when
* RA's arrive.
* Assumes that caller has determined that it is time to generate
* a new random ReachableTime.
*/
void
{
if (set_needed) {
"phyint_reach_random: SIOCSLIFLNKINFO");
return;
}
}
pi->pi_reach_time_since_random = 0;
}
/*
* Validate a temporary token against a list of known bad values.
* Currently assumes that token is 8 bytes long! Current known
* bad values include 0, reserved anycast tokens (RFC 2526), tokens
* used by ISATAP (draft-ietf-ngtrans-isatap-N), any token already
* assigned to this interface, or any token for which the global
* bit is set.
*
* Called by tmptoken_create().
*
* Return _B_TRUE if token is valid (no match), _B_FALSE if not.
*/
static boolean_t
{
0, 0, 0x5e, 0xfe, 0, 0, 0, 0 };
0, 0, 0, 0, \
0xfd, 0xff, 0xff, 0xff, \
0xff, 0xff, 0xff, 0x80 };
if (IN6_IS_ADDR_UNSPECIFIED(token))
return (_B_FALSE);
return (_B_FALSE);
return (_B_FALSE);
return (_B_FALSE);
return (_B_FALSE);
}
/* none of our tests failed, must be a good one! */
return (_B_TRUE);
}
/*
* Generate a temporary token and set up its timer
*
* Called from incoming_prefix_addrconf_process() (when token is first
* needed) and from tmptoken_timer() (when current token expires).
*
* Returns _B_TRUE if a token was successfully generated, _B_FALSE if not.
*/
{
goto no_token;
}
do {
goto no_token;
}
/*
* Assume EUI-64 formatting, and thus 64-bit
* token len; need to clear global bit.
*/
i++;
if (i == max_tries) {
"token; disabling temporary addresses on %s\n",
pi->pi_TmpAddrsEnabled = 0;
return (_B_FALSE);
}
if (pi->pi_TmpRegenCountdown != 0)
return (_B_TRUE);
}
/*
* Delete a temporary token. This is outside the normal timeout process,
* so mark any existing addresses based on this token DEPRECATED and set
* their preferred lifetime to 0. Don't tamper with valid lifetime, that
* will be used to eventually remove the address. Also reset the current
* pi_tmp_token value to 0.
*
* Called from incoming_prefix_addrconf_process() if DAD fails on a temp
* addr.
*/
void
{
TMP_TOKEN_BITS))) {
continue;
}
pr->pr_PreferredLifetime = 0;
}
}
/*
* Called from run_timeouts() with the number of milliseconds elapsed
* since the last call. Determines if any timeout event has occurred
* and returns the number of milliseconds until the next timeout event
* for the tmp token. Returns TIMER_INFINITY for "never".
*/
{
}
if (!pi->pi_TmpAddrsEnabled ||
return (TIMER_INFINITY);
return (pi->pi_TmpRegenCountdown);
}
/*
* Tmp token timer has expired. Start by generating a new token.
* If we can't get a new token, tmp addrs are disabled on this
* interface, so there's no need to continue, or to set a timer.
*/
if (!tmptoken_create(pi))
return (TIMER_INFINITY);
/*
* Now that we have a new token, walk the list of prefixes to
* find which ones need a corresponding tmp addr generated.
*/
continue;
sizeof (pbuf));
sizeof (tbuf));
continue;
}
/*
* We want to use incoming_prefix_*_process() functions to
* set up the new tmp addr, so cobble together a prefix
* info option struct based on the existing prefix to pass
* in. The lifetimes will be based on the current time
* remaining.
*
* The "from" param is only used for messages; pass in
* ::0 for that.
*/
sizeof (pbuf));
sizeof (tbuf));
continue;
}
}
}
/*
* appropriate timers were scheduled when
* the token and addresses were created.
*/
return (TIMER_INFINITY);
}
/*
* tlen specifies the token length in bits. Compares the lower
* tlen bits of the two addresses provided and returns _B_TRUE if
* they match, _B_FALSE if not. Also returns _B_FALSE for invalid
* values of tlen.
*/
{
return (_B_FALSE);
return (_B_FALSE);
if (tbits == 0)
return (_B_TRUE);
/* We only care about the tbits rightmost bits */
return (_B_FALSE);
return (_B_TRUE);
}
/*
* Lookup prefix structure that matches the prefix and prefix length.
* Assumes that the bits after prefixlen might not be zero.
*/
static struct prefix *
{
}
return (pr);
}
return (NULL);
}
/*
* Compare two prefixes that have the same prefix length.
* Fails if the prefix length is unreasonable.
*/
{
return (_B_FALSE);
for (j = 0; j < pbytes; j++)
return (_B_FALSE);
if (pbits == 0)
return (_B_TRUE);
/* Make the N leftmost bits one */
return (_B_FALSE);
return (_B_TRUE);
}
/*
* Set a prefix from an address and a prefix length.
* Force all the bits after the prefix length to be zero.
*/
void
{
int j;
return;
/* Make the N leftmost bits one */
}
/*
* Lookup a prefix based on the kernel's interface name.
*/
struct prefix *
{
}
if (name[0] == '\0')
return (NULL);
return (pr);
}
return (NULL);
}
/*
* Search the phyints list to make sure that this new prefix does
* not already exist in any other physical interfaces that have
* the same address as this one
*/
struct prefix *
{
int prefixlen;
}
continue;
&otherpr->pr_address))
return (otherpr);
}
return (NULL);
}
/*
* Initialize a new prefix without setting lifetimes etc.
*/
struct prefix *
{
}
return (NULL);
}
/*
* The prefix might have non-zero bits after the prefix len bits.
* Force them to be zero.
*/
pr->pr_kernel_state = 0;
return (pr);
}
/*
* Create a new named prefix. Caller should use prefix_init_from_k
* to initialize the content.
*/
struct prefix *
{
}
return (NULL);
}
return (pr);
}
/* Insert in linked list */
static void
{
}
/*
* Initialize the prefix from the content of the kernel.
* If IFF_ADDRCONF is set we treat it as PR_AUTO (i.e. an addrconf
* prefix). However, we cannot derive the lifetime from
* the kernel, thus it is set to 1 week.
* Ignore the prefix if the interface is not IFF_UP.
* If it's from DHCPv6, then we set the netmask.
*/
int
{
goto error;
}
goto error;
}
goto error;
}
/*
* If this is a DHCPv6 interface, then we control the netmask.
*/
"ignoring DHCP %s not ready\n",
return (0);
}
/*
* Examine any non-static (autoconfigured) prefixes as
* well as existing DHCP-controlled prefixes for valid
* prefix length information.
*/
pr2->pr_prefix_len)) {
break;
}
}
"saved mask for DHCP %s; need to "
} else {
"%s mask for DHCP %s\n",
}
/*
* If this interface was created using ipadm, store the
* addrobj for the DHCPv6 interface in ipmgmtd daemon's
* in-memory aobjmap.
*/
} else {
"prefix_init_from_k: ioctl (get subnet)");
goto error;
}
"ignoring interface %s: not AF_INET6\n",
goto error;
}
/*
* Guard against the prefix having non-zero bits after the
* prefix len bits.
*/
"be configured with an invalid interface id "
"(%s/%u)\n",
goto error;
}
}
pr->pr_kernel_state = 0;
/* Prevent ndpd from stepping on this prefix */
}
/* Adjust pr_prefix_len based if PR_AUTO is set */
pr->pr_prefix_len =
}
/* Can't extract lifetimes from the kernel - use 1 week */
/*
* If this is a temp addr, the creation time needs to be set.
* Though it won't be entirely accurate, the current time is
* an okay approximation.
*/
if (pr->pr_kernel_state == 0)
return (0);
/* Pretend that the prefix does not exist in the kernel */
pr->pr_kernel_state = 0;
return (-1);
}
/*
* Delete (unlink and free) and remove from kernel if the prefix
* was added by in.ndpd (i.e. PR_STATIC is not set).
* Handles delete of things that have not yet been inserted in the list
* i.e. pr_physical is NULL.
* Removes the ipadm addrobj created for the prefix.
*/
void
{
}
/* Remove non-static prefixes from the kernel. */
} else {
}
}
/*
* Toggle one or more IFF_ flags for a prefix. Turn on 'onflags' and
* turn off 'offflags'.
*/
static int
{
"flags %llx on %llx off %llx\n",
}
/* Assumes that only the PR_STATIC link-local matches the pi_name */
"name matches interface name\n",
return (-1);
}
}
return (-1);
}
" new 0x%llx on 0x%llx off 0x%llx\n",
}
return (-1);
}
return (0);
}
/*
* Update the subnet mask for this interface under DHCPv6 control.
*/
void
{
/*
* Ignore ENXIO, as the dhcpagent process is responsible for plumbing
* and unplumbing these.
*/
}
/*
* Make the kernel state match what is in the prefix structure.
* This includes creating the prefix (allocating a new interface name)
* as well as setting the local address and on-link subnet prefix
* and controlling the IFF_ADDRCONF and IFF_DEPRECATED flags.
*/
void
{
sizeof (buf1)),
}
return; /* No changes */
/* Skip static prefixes */
return;
if (pr->pr_kernel_state == 0) {
/*
* Create a new logical interface name and store in pr_name.
* Set IFF_ADDRCONF. Do not set an address (yet).
*/
/* Name already set! */
"from %s to %s name is already allocated\n",
sizeof (buf1)),
sizeof (buf2)));
return;
}
return;
}
}
/*
* The IFF_TEMPORARY flag might have already been set; if
* so, it needs to be or'd into the flags we're turning on.
* But be careful, we might be re-creating a manually
* removed interface, in which case we don't want to try
* to set *all* the flags we might have in our copy of the
* flags yet.
*/
onflags |= IFF_TEMPORARY;
return;
}
/* Remove the interface */
return;
}
/*
* Assumes that only the PR_STATIC link-local matches
* the pi_name
*/
return;
}
/* Remove logical interface based on pr_name */
}
pr->pr_kernel_state = 0;
return;
}
/*
* Set local address and set the prefix length to 128.
* Turn off IFF_NOLOCAL in case it was set.
* Turn on IFF_UP.
*/
"for PR_AUTO on\n",
}
return;
}
/*
* If this interface was created using ipadm, store the
* addrobj for the prefix in ipmgmtd daemon's aobjmap.
*/
} else {
}
}
return;
}
/*
* For ptp interfaces, create a destination based on
* prefix and prefix len together with the remote token
* extracted from the remote pt-pt address. This is used by
* ip to choose a proper source for outgoing packets.
*/
int i;
for (i = 0; i < 16; i++) {
}
"set dstaddr %s for PR_AUTO on\n",
}
(char *)&lifr) < 0) {
"prefix_update_k: SIOCSLIFDSTADDR");
return;
}
}
return;
else
}
/* Turn on IFF_NOLOCAL and set the local address to all zero */
return;
}
return;
}
}
/* Only applies if PR_AUTO */
return;
}
return;
}
/* Set the subnet and set IFF_UP */
}
return;
}
/*
* If we've previously marked the interface "up" while
* processing the PR_AUTO flag -- via incoming_prefix_addrconf
* -- then there's no need to set it "up" again. We're done;
* just set PR_ONLINK to indicate that we've set the subnet.
*/
return;
}
/* Set the prefixlen to 128 */
}
return;
}
}
}
/*
* Called with the number of millseconds elapsed since the last call.
* Determines if any timeout event has occurred and
* returns the number of milliseconds until the next timeout event.
* Returns TIMER_INFINITY for "never".
*/
{
"valid %d pref %d onlink %d\n",
}
/* Exclude static prefixes */
return (next);
if (pr->pr_AutonomousFlag &&
pr->pr_PreferredLifetime = 0;
} else {
}
}
if (pr->pr_AutonomousFlag &&
pr->pr_ValidLifetime = 0;
} else {
}
}
if (pr->pr_OnLinkFlag &&
pr->pr_OnLinkLifetime = 0;
} else {
}
}
}
/* Might cause prefix to be deleted! */
/* Log a message when an addrconf prefix goes away */
"Address removed due to timeout %s\n",
}
}
return (next);
}
static char *
{
char *cp;
cp[0] = '\0';
return (buf);
}
return (buf);
}
if (state & PR_DEPRECATED) {
return (buf);
}
return (buf);
}
return (buf);
}
static void
{
}
/*
* Lookup advertisement prefix structure that matches the prefix and
* prefix length.
* Assumes that the bits after prefixlen might not be zero.
*/
struct adv_prefix *
{
}
return (adv_pr);
}
return (NULL);
}
/*
* Initialize a new advertisement prefix.
*/
struct adv_prefix *
{
}
return (NULL);
}
/*
* The prefix might have non-zero bits after the prefix len bits.
* Force them to be zero.
*/
return (adv_pr);
}
/* Insert in linked list */
static void
{
}
/*
* Delete (unlink and free) from our tables. There should be
* a corresponding "struct prefix *" which will clean up the kernel
* if necessary. adv_prefix is just used for sending out advertisements.
*/
static void
{
}
} else {
}
}
/*
* Called with the number of millseconds elapsed since the last call.
* Determines if any timeout event has occurred and
* returns the number of milliseconds until the next timeout event.
* Returns TIMER_INFINITY for "never".
*/
{
elapsed);
}
/* Decrement Expire time left for real-time lifetimes */
if (adv_pr->adv_pr_AdvValidRealTime) {
else
}
if (adv_pr->adv_pr_AdvPreferredRealTime) {
} else {
}
}
return (TIMER_INFINITY);
}
static void
{
}
/* Lookup router on its link-local IPv6 address */
struct router *
{
}
sizeof (addr)) == 0)
return (dr);
}
return (NULL);
}
/*
* Create a default router entry.
* The lifetime parameter is in seconds.
*/
struct router *
{
}
return (NULL);
}
if (dr->dr_lifetime != 0)
return (dr);
}
/* Insert in linked list */
static void
{
}
/*
* Delete (unlink and free).
* Handles delete of things that have not yet been inserted in the list
* i.e. dr_physical is NULL.
*/
static void
{
}
} else {
}
}
/*
* Update the kernel to match dr_lifetime
*/
void
{
}
/* Log a message when last router goes away */
"Last default router (%s) removed on %s\n",
}
}
/*
* Called with the number of millseconds elapsed since the last call.
* Determines if any timeout event has occurred and
* returns the number of milliseconds until the next timeout event.
* Returns TIMER_INFINITY for "never".
*/
{
}
dr->dr_lifetime = 0;
} else {
}
if (dr->dr_lifetime == 0) {
/* Log a message when last router goes away */
"Last default router (%s) timed out on %s\n",
}
}
return (next);
}
/*
* Add a default route to the kernel (unless the lifetime is zero)
* Handles onlink default routes.
*/
static void
{
int rlen;
}
return;
}
if (rlen < 0) {
return;
}
return;
}
pi->pi_num_k_routers++;
}
/*
* Delete a route from the kernel.
* Handles onlink default routes.
*/
static void
{
int rlen;
}
return;
}
if (rlen < 0) {
}
}
pi->pi_num_k_routers--;
}
static void
{
}
void
phyint_print_all(void)
{
}
}
void
{
pi->pi_kernel_state = 0;
if (pi->pi_AdvSendAdvertisements) {
} else {
}
while (pi->pi_router_list)
}
/*
*/
void
{
int lnum = 0;
char *cp;
/*
* If ipadm was used to autoconfigure this interface,
* pi_ipadm_aobjname will contain the address object name
* that is used to identify the addresses. Use the same
* address object name for this prefix.
*/
return;
}
return;
}
if (add) {
} else {
}
/* Ignore the error if the ipmgmtd daemon is not running */
}
}