/*
* 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.
*/
/*
* Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
*/
#include "defs.h"
#include "tables.h"
#include <sys/sysmacros.h>
#include <dhcpagent_ipc.h>
#include <dhcpagent_util.h>
struct sockaddr_in6 *from);
struct nd_router_advert *ra,
char *frombuf);
char *frombuf);
/*
* Return a pointer to the specified option buffer.
* If not found return NULL.
*/
static void *
{
}
}
return (NULL);
}
void
{
int len;
const char *msgbuf;
return;
}
if (len == 0)
return;
msgbuf = "Unspecified Router";
else
/* Ignore packets > 64k or control buffers that don't fit */
}
return;
}
if (len < ICMP6_MINLEN) {
"from %s on %s\n",
return;
}
/* Unknown hoplimit - must drop */
return;
}
/* Can't allow routing headers in ND messages */
"from %s on %s\n",
return;
}
switch (icmp->icmp6_type) {
case ND_ROUTER_SOLICIT:
if (!pi->pi_AdvSendAdvertisements)
return;
"on %s (no route exchange on interface)\n",
}
return;
}
/*
* Assumes that the kernel has verified the AH (if present)
* and the ICMP checksum.
*/
if (hoplimit != IPV6_MAX_HOPS) {
return;
}
if (icmp->icmp6_code != 0) {
return;
}
if (len < sizeof (struct nd_router_solicit)) {
"from %s on %s\n",
return;
}
if (len > sizeof (struct nd_router_solicit)) {
return;
}
}
break;
case ND_ROUTER_ADVERT:
/*
* Router advt. must have address!
* Logging the news and returning.
*/
"Router's address unspecified in advertisement\n");
return;
}
"on %s (no route exchange on interface)\n",
}
return;
}
/*
* Assumes that the kernel has verified the AH (if present)
* and the ICMP checksum.
*/
return;
}
if (hoplimit != IPV6_MAX_HOPS) {
return;
}
if (icmp->icmp6_code != 0) {
return;
}
if (len < sizeof (struct nd_router_advert)) {
"from %s on %s\n",
return;
}
if (len > sizeof (struct nd_router_advert)) {
return;
}
}
if (pi->pi_AdvSendAdvertisements)
else
break;
}
}
/*
* Process a received router solicitation.
* Check for source link-layer address option and check if it
* is time to advertise.
*/
static void
struct sockaddr_in6 *from)
{
int optlen;
/* Process any options */
len -= sizeof (struct nd_router_solicit);
while (len >= sizeof (struct nd_opt_hdr)) {
switch (opt->nd_opt_type) {
case ND_OPT_SOURCE_LINKADDR:
break;
default:
break;
}
}
/* Simple algorithm: treat unicast and multicast RSs the same */
}
/*
* Function that sends commands to dhcpagent daemon.
*/
int
{
int error;
/* make sure we try again next time there's a chance */
if (type != DHCP_RELEASE) {
pi->pi_ra_flags &=
}
return (DHCP_IPC_E_MEMORY);
}
if (error != 0) {
return (error);
}
return (error);
}
/*
* Start up DHCPv6 on a given physical interface. Does not wait for
* a message to be returned from the daemon.
*/
void
{
int error;
int type;
/* make sure we try again next time there's a chance */
return;
}
type = DHCP_START;
else
type = DHCP_INFORM;
/*
* Timeout is considered to be "success" because we don't wait for DHCP
* to do its exchange.
*/
error != DHCP_IPC_E_TIMEOUT) {
}
}
/*
* Release the acquired DHCPv6 lease on a given physical interface.
* Does not wait for a message to be returned from the daemon.
*/
void
{
int error;
int type;
type = DHCP_RELEASE;
error != DHCP_IPC_E_TIMEOUT) {
/*
* Drop the dhcp control if we cannot release it.
*/
goto retry;
}
}
}
/*
* Globals to check if we're seeing unusual hop counts in Router
* Advertisements (RAs). We record the hopcounts in the kernel using
* SIOCSLIFLNKINFO, but the kernel ignores these when actually setting IPv6
* hop counts for packets.
*
* RFC 3756 does mention the possibility of an adversary throttling down
* hopcounts using unsolicited RAs. These variables can be tuned with 'mdb -p'
*/
/* Really a boolean... if set, also log the offending sending address. */
int bad_hopcount_record_addr = 0;
/* Anything less triggers a warning. Set to 0 to disable. */
/* Number of packets received below the threshhold. */
/*
* Process a received router advertisement.
* Called both when packets arrive as well as when we send RAs.
* In the latter case 'loopback' is set.
*/
void
{
int optlen;
if (no_loopback && loopback)
return;
"Low hopcount %d received on %s%s%s\n",
INET6_ADDRSTRLEN) : "");
}
}
if (reachable != 0 &&
}
}
if (retrans != 0 &&
}
if (set_needed) {
return;
}
}
/*
* If the "managed" flag is set, then just assume that the "other" flag
* is set as well. It's not legal to get addresses alone without
* getting other data.
*/
/*
* If either the "managed" or "other" bits have turned on, then it's
* now time to invoke DHCP. If only the "other" bit is set, then don't
* get addresses via DHCP; only "other" data. If "managed" is set,
* then we must always get both addresses and "other" data.
*/
(ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER))) {
"incoming_ra: trigger dhcp %s on %s\n",
}
start_dhcp(pi);
}
/* Skip default router code if sent from ourselves */
if (!loopback) {
/* Find and update or add default router in list */
if (router_lifetime != 0) {
}
} else {
if (dr->dr_lifetime != 0)
}
}
/* Process any options */
len -= sizeof (struct nd_router_advert);
while (len >= sizeof (struct nd_opt_hdr)) {
switch (opt->nd_opt_type) {
loopback);
break;
case ND_OPT_MTU:
break;
case ND_OPT_SOURCE_LINKADDR:
/* skip lla option if sent from ourselves! */
if (!loopback) {
}
break;
default:
break;
}
}
if (!loopback && !slla_opt_present)
/* Stop sending solicitations */
}
/*
* Process a received prefix option.
* Unless addrconf is turned off we process both the addrconf and the
* onlink aspects of the prefix option.
*
* Note that when a flag (onlink or auto) is turned off we do nothing -
* the prefix will time out.
*/
static void
{
"(%d bytes)\n",
return;
}
"- ignored\n",
return;
}
}
good_prefix) {
}
}
/*
* Process prefix options with the onlink flag set.
*
* If there are no routers ndpd will add an onlink
* default route which will allow communication
* between neighbors.
*
* This function needs to loop to find the same prefix multiple times
* as if a failover happened earlier, the addresses belonging to
* a different interface may be found here on this interface.
*/
static void
{
int plen;
/* Exclude static prefixes */
continue;
}
}
/*
* If we have found a matching prefix already or validtime
* is zero, we have nothing to do.
*/
return;
return;
}
void
{
if (validtime != 0)
else
/*
* Convert from seconds to milliseconds avoiding overflow.
* If the lifetime in the packet is e.g. PREFIX_INFINITY - 1
* (4 billion seconds - about 130 years) we will in fact time
* out the prefix after 4 billion milliseconds - 46 days).
* Thus the longest lifetime (apart from infinity) is 46 days.
* Note that this ensures that PREFIX_INFINITY still means "forever".
*/
} else {
else
}
"onlink %u state 0x%x, kstate 0x%x\n",
}
}
if (pr->pr_OnLinkLifetime != 0)
}
/*
* Process all prefix options by locating the DHCPv6-configured interfaces, and
* applying the netmasks as needed.
*/
static void
{
/* Make sure it's a valid prefix. */
"prefix with no valid time\n");
return;
}
po->nd_opt_pi_prefix_len)) {
"incoming_prefix_stateful:"
" set mask on DHCP %s\n",
}
}
}
}
/*
* If there's no matching DHCPv6 prefix present, then create an empty
* one so that we'll be able to configure it later.
*/
if (!foundpref) {
"incoming_prefix_stateful: created dummy "
"prefix for later\n");
}
}
}
/*
* Process prefix options with the autonomous flag set.
* Returns false if this prefix results in a bad address (duplicate)
* This function needs to loop to find the same prefix multiple times
* as if a failover happened earlier, the addresses belonging to
* a different interface may be found here on this interface.
*/
static boolean_t
{
int plen;
/* Sanity checks */
(void *)&po->nd_opt_pi_prefix,
"valid %u < pref %u ignored\n",
return (_B_FALSE);
}
/* Exclude static prefixes and DHCP */
continue;
/*
* If this address is deprecated and its token
* doesn't match the current tmp token, we want
* to create a new address with the current
* token. So don't count this addr as a match.
*/
} else {
}
}
}
/*
* If we have found a matching prefix (for public and, if temp addrs
* are enabled, for temporary) already or validtime is zero, we have
* nothing to do.
*/
if (validtime == 0 ||
return (_B_TRUE);
if (!found_pub) {
return (_B_TRUE);
}
/*
* if processing of the public address failed,
* don't bother with the temporary address.
*/
return (_B_FALSE);
return (_B_TRUE);
}
return (ret);
}
{
int plen;
if (!new_prefix) {
/*
* Check 2 hour rule on valid lifetime.
* Follows: RFC 2462
* If we advertised this prefix ourselves we skip
* these checks. They are also skipped if we did not
* previously do addrconf on this prefix.
*/
validtime >= MIN_VALID_LIFETIME ||
/* LINTED - statement has no consequent */
validtime >= recorded_validtime) {
/* OK */
} else if (recorded_validtime < MIN_VALID_LIFETIME &&
/* Ignore the prefix */
(void *)&po->nd_opt_pi_prefix,
"too short valid lifetime %u stored %u "
"- ignored\n",
return (_B_TRUE);
} else {
/*
* If the router clock runs slower than the
* host by 1 second over 2 hours then this
* test will set the lifetime back to 2 hours
* once i.e. a lifetime decrementing in
* realtime might cause the prefix to live an
* extra 2 hours on the host.
*/
(void *)&po->nd_opt_pi_prefix,
"valid time %u stored %u rounded up "
"to %u\n",
}
}
/*
* For RFC3041 addresses, need to take token lifetime
* into account, too.
*/
if (new_prefix) {
} else {
validtime = 0;
/*
* If this is an existing address which was deprecated
* because of a bad token, we don't want to update its
* preferred lifetime!
*/
if ((pr->pr_PreferredLifetime == 0) &&
preftime = 0;
preftime = 0;
}
(void *)&po->nd_opt_pi_prefix,
"preferred lifetime(%d) <= TmpRegenAdvance(%d)\n",
if (new_prefix) {
}
return (_B_TRUE);
}
}
int i, tokenlen;
/*
* Form a new local address if the lengths match.
*/
if (!tmptoken_create(pi)) {
return (_B_TRUE);
}
}
} else {
}
(void *)&po->nd_opt_pi_prefix,
"mismatched length %d token length %d\n",
return (_B_TRUE);
}
for (i = 0; i < 16; i++) {
/*
* prefix_create ensures that pr_prefix has all-zero
* bits after prefixlen.
*/
}
/*
* Check if any other physical interface has the same
* address configured already
*/
/*
* Delete this prefix structure as kernel
* does not allow duplicated addresses
*/
"Duplicate prefix %s received on interface %s\n",
"Prefix already exists in interface %s\n",
if (new_prefix) {
return (_B_FALSE);
}
/* Ignore for addrconf purposes */
}
"created tmp addr(%s v %d p %d)\n",
}
}
if (validtime != 0)
else
else
/*
* Convert from seconds to milliseconds avoiding overflow.
* If the lifetime in the packet is e.g. PREFIX_INFINITY - 1
* (4 billion seconds - about 130 years) we will in fact time
* out the prefix after 4 billion milliseconds - 46 days).
* Thus the longest lifetime (apart from infinity) is 46 days.
* Note that this ensures that PREFIX_INFINITY still means "forever".
*/
else
else
"valid %u pref %u\n",
}
/* Take the min of the two timeouts by calling it twice */
if (pr->pr_ValidLifetime != 0)
if (pr->pr_PreferredLifetime != 0)
}
/* Log a message when an addrconf prefix goes away */
"valid lifetime %s\n",
}
}
return (_B_TRUE);
}
/*
* Process an MTU option received in a router advertisement.
*/
static void
struct sockaddr_in6 *from)
{
"(%d bytes)\n",
return;
}
return; /* No change */
/* Can't exceed physical MTU */
return;
}
if (mtu < IPV6_MIN_MTU) {
return;
}
return;
}
}
/*
* Process a source link-layer address option received in a router
* advertisement or solicitation.
*/
static void
{
int max_content_len;
/*
* Get our link-layer address length. We may not have one, in which
* case we can just bail.
*/
return;
/*
* Can't remove padding since it is link type specific.
* However, we check against the length of our link-layer address.
* Note: assumes that all links have a fixed length address.
*/
(max_content_len >= 8 &&
return;
}
/*
* Set IsRouter flag if RA; clear if RS.
*/
return;
}
}
/*
* Verify the content of the received router advertisement against our
* own configuration as specified in RFC 2461.
*/
static void
struct sockaddr_in6 *from)
{
int optlen;
if (ra->nd_ra_curhoplimit != 0 &&
pi->pi_AdvCurHopLimit != 0 &&
"limit:\n\treceived %d configuration %d\n",
}
"time:\n\treceived %d configuration %d\n",
}
"timer:\n\treceived %d configuration %d\n",
}
"flag:\n\treceived %s configuration %s\n",
}
"flag:\n\treceived %s configuration %s\n",
}
/* Process any options */
len -= sizeof (struct nd_router_advert);
while (len >= sizeof (struct nd_opt_hdr)) {
switch (opt->nd_opt_type) {
break;
case ND_OPT_MTU:
break;
default:
break;
}
}
}
/*
* with our settings.
*/
static void
{
int plen;
"(%d bytes)\n",
return;
}
"prefix - ignored\n",
return;
}
return;
/* Ignore prefixes which we do not advertise */
return;
"RA from %s on %s inconsistent autonomous flag for \n\t"
"prefix %s/%u: received %s configuration %s\n",
}
"for \n\tprefix %s/%u: received %s configuration %s\n",
}
/*
* Take into account variation for lifetimes decrementing
* in real time. Allow +/- 10 percent and +/- 10 seconds.
*/
if (adv_pr->adv_pr_AdvValidRealTime) {
if (adv_pr->adv_pr_AdvValidExpiration > 0 &&
(validtime <
"lifetime for\n\tprefix %s/%u: received %d "
"configuration %d\n",
}
} else {
"lifetime for\n\tprefix %s/%u: received %d "
"configuration %d\n",
}
}
if (adv_pr->adv_pr_AdvPreferredRealTime) {
if (adv_pr->adv_pr_AdvPreferredExpiration > 0 &&
(preftime <
preftime >
"preferred lifetime for\n\tprefix %s/%u: "
"received %d configuration %d\n",
}
} else {
"preferred lifetime for\n\tprefix %s/%u: "
"received %d configuration %d\n",
}
}
}
/*
* Verify the received MTU against our own configuration.
*/
static void
{
"(%d bytes)\n",
return;
}
if (pi->pi_AdvLinkMTU != 0 &&
"received %d configuration %d\n",
}
}
/*
* Verify that all options have a non-zero length and that
* the options fit within the total length of the packet (optlen).
*/
static boolean_t
{
while (optlen > 0) {
if (opt->nd_opt_len == 0) {
"from %s on %s\n",
return (_B_FALSE);
}
if (optlen < 0) {
"from %s on %s\n",
return (_B_FALSE);
}
}
return (_B_TRUE);
}
/*
* Update IsRouter Flag for Host turning into a router or vice-versa.
*/
static void
int isrouter)
{
/* check if valid flag is being set */
if ((isrouter != NDF_ISROUTER_ON) &&
(isrouter != NDF_ISROUTER_OFF)) {
"flag %d\n", isrouter);
return;
}
"update_ra_flag: SIOCLIFGETND: nce doesn't exist, not setting IFF_ROUTER\n");
}
} else {
}
} else {
/*
* The lif_nd_req structure has three state values to be used
* lnr_state_create, lnr_state_same_lla, and lnr_state_diff_lla.
*
* In this case, we're updating an nce, without changing lla;
* so we set lnr_state_same_lla to ND_UNCHANGED, indicating that
* nce's state should not be affected by our flag change.
*
* The kernel implementation also expects the lnr_state_create
* field be always set, before processing ioctl request for NCE
* update.
* We use the state as STALE, while addressing the possibility
* of NCE deletion when ioctl with SIOCLIFGETND argument
* in earlier step is returned - further in such case we don't
* want to re-create the entry in the reachable state.
*/
} else {
"updated for %s\n", abuf);
}
}
}