/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include "config.h"
#ifdef HAVE_SYS_BITOPS_H
#include <sys/bitops.h>
#else
#include "compat/bitops.h"
#endif
#ifdef BSD
/* Purely for the ND6_IFF_AUTO_LINKLOCAL #define which is solely used
* to generate our CAN_ADD_LLADDR #define. */
# include <netinet6/in6_var.h>
# include <netinet6/nd6.h>
#endif
#include <errno.h>
#include <ifaddrs.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#define ELOOP_QUEUE ELOOP_IPV6
#include "common.h"
#include "if.h"
#include "dhcpcd.h"
#include "dhcp6.h"
#include "eloop.h"
#include "ipv6.h"
#include "ipv6nd.h"
#include "logerr.h"
#include "privsep.h"
#include "sa.h"
#include "script.h"
#ifdef HAVE_MD5_H
# ifndef DEPGEN
# include <md5.h>
# endif
#endif
#ifdef SHA2_H
# include SHA2_H
#endif
#ifndef SHA256_DIGEST_LENGTH
# define SHA256_DIGEST_LENGTH 32
#endif
#ifdef IPV6_POLLADDRFLAG
# warning kernel does not report IPv6 address flag changes
# warning polling tentative address flags periodically
#endif
/* Hackery at it's finest. */
#ifndef s6_addr32
# ifdef __sun
# define s6_addr32 _S6_un._S6_u32
# else
# define s6_addr32 __u6_addr.__u6_addr32
# endif
#endif
#if defined(HAVE_IN6_ADDR_GEN_MODE_NONE) || defined(ND6_IFF_AUTO_LINKLOCAL) || \
defined(IFF_NOLINKLOCAL)
/* Only add the LL address if we have a carrier, so DaD works. */
#define CAN_ADD_LLADDR(ifp) \
(!((ifp)->options->options & DHCPCD_LINK) || (ifp)->carrier != LINK_DOWN)
#ifdef __sun
/* Although we can add our own LL address, we cannot drop it
* without unplumbing the if which is a lot of code.
* So just keep it for the time being. */
#define CAN_DROP_LLADDR(ifp) (0)
#else
#define CAN_DROP_LLADDR(ifp) (1)
#endif
#else
/* We have no control over the OS adding the LLADDR, so just let it do it
* as we cannot force our own view on it. */
#define CAN_ADD_LLADDR(ifp) (0)
#define CAN_DROP_LLADDR(ifp) (0)
#endif
#ifdef IPV6_MANAGETEMPADDR
static void ipv6_regentempaddr(void *);
#endif
int
ipv6_init(struct dhcpcd_ctx *ctx)
{
if (ctx->ra_routers != NULL)
return 0;
ctx->ra_routers = malloc(sizeof(*ctx->ra_routers));
if (ctx->ra_routers == NULL)
return -1;
TAILQ_INIT(ctx->ra_routers);
#ifndef __sun
ctx->nd_fd = -1;
#endif
ctx->dhcp6_fd = -1;
return 0;
}
static ssize_t
ipv6_readsecret(struct dhcpcd_ctx *ctx)
{
FILE *fp;
char line[1024];
unsigned char *p;
size_t len;
uint32_t r;
int x;
if ((ctx->secret_len = read_hwaddr_aton(&ctx->secret, SECRET)) != 0)
return (ssize_t)ctx->secret_len;
if (errno != ENOENT)
logerr("%s: %s", __func__, SECRET);
/* Chaining arc4random should be good enough.
* RFC7217 section 5.1 states the key SHOULD be at least 128 bits.
* To attempt and future proof ourselves, we'll generate a key of
* 512 bits (64 bytes). */
if (ctx->secret_len < 64) {
if ((ctx->secret = malloc(64)) == NULL) {
logerr(__func__);
return -1;
}
ctx->secret_len = 64;
}
p = ctx->secret;
for (len = 0; len < 512 / NBBY; len += sizeof(r)) {
r = arc4random();
memcpy(p, &r, sizeof(r));
p += sizeof(r);
}
/* Ensure that only the dhcpcd user can read the secret.
* Write permission is also denied as changing it would remove
* it's stability. */
if ((fp = fopen(SECRET, "w")) == NULL ||
chmod(SECRET, S_IRUSR) == -1)
goto eexit;
x = fprintf(fp, "%s\n",
hwaddr_ntoa(ctx->secret, ctx->secret_len, line, sizeof(line)));
if (fclose(fp) == EOF)
x = -1;
fp = NULL;
if (x > 0)
return (ssize_t)ctx->secret_len;
eexit:
logerr("%s: %s", __func__, SECRET);
if (fp != NULL)
fclose(fp);
unlink(SECRET);
ctx->secret_len = 0;
return -1;
}
/* http://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xhtml
* RFC5453 */
static const struct reslowhigh {
const uint8_t high[8];
const uint8_t low[8];
} reslowhigh[] = {
/* RFC4291 + RFC6543 */
{ { 0x02, 0x00, 0x5e, 0xff, 0xfe, 0x00, 0x00, 0x00 },
{ 0x02, 0x00, 0x5e, 0xff, 0xfe, 0xff, 0xff, 0xff } },
/* RFC2526 */
{ { 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 },
{ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }
};
static bool
ipv6_reserved(const struct in6_addr *addr)
{
uint64_t id, low, high;
size_t i;
const struct reslowhigh *r;
id = be64dec(addr->s6_addr + sizeof(id));
if (id == 0) /* RFC4291 */
return 1;
for (i = 0; i < __arraycount(reslowhigh); i++) {
r = &reslowhigh[i];
low = be64dec(r->low);
high = be64dec(r->high);
if (id >= low && id <= high)
return true;
}
return false;
}
/* RFC7217 */
static int
ipv6_makestableprivate1(struct dhcpcd_ctx *ctx,
struct in6_addr *addr, const struct in6_addr *prefix, int prefix_len,
const unsigned char *netiface, size_t netiface_len,
const unsigned char *netid, size_t netid_len,
unsigned short vlanid,
uint32_t *dad_counter)
{
unsigned char buf[2048], *p, digest[SHA256_DIGEST_LENGTH];
size_t len, l;
SHA256_CTX sha_ctx;
if (prefix_len < 0 || prefix_len > 120) {
errno = EINVAL;
return -1;
}
if (ctx->secret_len == 0) {
if (ipv6_readsecret(ctx) == -1)
return -1;
}
l = (size_t)(ROUNDUP8(prefix_len) / NBBY);
len = l + netiface_len + netid_len + sizeof(*dad_counter) +
ctx->secret_len;
if (vlanid != 0)
len += sizeof(vlanid);
if (len > sizeof(buf)) {
errno = ENOBUFS;
return -1;
}
for (;; (*dad_counter)++) {
/* Combine all parameters into one buffer */
p = buf;
memcpy(p, prefix, l);
p += l;
memcpy(p, netiface, netiface_len);
p += netiface_len;
memcpy(p, netid, netid_len);
p += netid_len;
/* Don't use a vlanid if not set.
* This ensures prior versions have the same unique address. */
if (vlanid != 0) {
memcpy(p, &vlanid, sizeof(vlanid));
p += sizeof(vlanid);
}
memcpy(p, dad_counter, sizeof(*dad_counter));
p += sizeof(*dad_counter);
memcpy(p, ctx->secret, ctx->secret_len);
/* Make an address using the digest of the above.
* RFC7217 Section 5.1 states that we shouldn't use MD5.
* Pity as we use that for HMAC-MD5 which is still deemed OK.
* SHA-256 is recommended */
SHA256_Init(&sha_ctx);
SHA256_Update(&sha_ctx, buf, len);
SHA256_Final(digest, &sha_ctx);
p = addr->s6_addr;
memcpy(p, prefix, l);
/* RFC7217 section 5.2 says we need to start taking the id from
* the least significant bit */
len = sizeof(addr->s6_addr) - l;
memcpy(p + l, digest + (sizeof(digest) - len), len);
/* Ensure that the Interface ID does not match a reserved one,
* if it does then treat it as a DAD failure.
* RFC7217 section 5.2 */
if (prefix_len != 64)
break;
if (!ipv6_reserved(addr))
break;
}
return 0;
}
int
ipv6_makestableprivate(struct in6_addr *addr,
const struct in6_addr *prefix, int prefix_len,
const struct interface *ifp,
int *dad_counter)
{
uint32_t dad;
int r;
dad = (uint32_t)*dad_counter;
/* For our implementation, we shall set the hardware address
* as the interface identifier */
r = ipv6_makestableprivate1(ifp->ctx, addr, prefix, prefix_len,
ifp->hwaddr, ifp->hwlen,
ifp->ssid, ifp->ssid_len,
ifp->vlanid, &dad);
if (r == 0)
*dad_counter = (int)dad;
return r;
}
#ifdef IPV6_AF_TEMPORARY
static int
ipv6_maketemporaryaddress(struct in6_addr *addr,
const struct in6_addr *prefix, int prefix_len,
const struct interface *ifp)
{
struct in6_addr mask;
struct interface *ifpn;
if (ipv6_mask(&mask, prefix_len) == -1)
return -1;
*addr = *prefix;
again:
addr->s6_addr32[2] |= (arc4random() & ~mask.s6_addr32[2]);
addr->s6_addr32[3] |= (arc4random() & ~mask.s6_addr32[3]);
TAILQ_FOREACH(ifpn, ifp->ctx->ifaces, next) {
if (ipv6_iffindaddr(ifpn, addr, 0) != NULL)
break;
}
if (ifpn != NULL)
goto again;
if (ipv6_reserved(addr))
goto again;
return 0;
}
#endif
int
ipv6_makeaddr(struct in6_addr *addr, struct interface *ifp,
const struct in6_addr *prefix, int prefix_len, unsigned int flags)
{
const struct ipv6_addr *ap;
int dad;
if (prefix_len < 0 || prefix_len > 120) {
errno = EINVAL;
return -1;
}
#ifdef IPV6_AF_TEMPORARY
if (flags & IPV6_AF_TEMPORARY)
return ipv6_maketemporaryaddress(addr, prefix, prefix_len, ifp);
#else
UNUSED(flags);
#endif
if (ifp->options->options & DHCPCD_SLAACPRIVATE) {
dad = 0;
if (ipv6_makestableprivate(addr,
prefix, prefix_len, ifp, &dad) == -1)
return -1;
return dad;
}
if (prefix_len > 64) {
errno = EINVAL;
return -1;
}
if ((ap = ipv6_linklocal(ifp)) == NULL) {
/* We delay a few functions until we get a local-link address
* so this should never be hit. */
errno = ENOENT;
return -1;
}
/* Make the address from the first local-link address */
memcpy(addr, prefix, sizeof(*prefix));
addr->s6_addr32[2] = ap->addr.s6_addr32[2];
addr->s6_addr32[3] = ap->addr.s6_addr32[3];
return 0;
}
static int
ipv6_makeprefix(struct in6_addr *prefix, const struct in6_addr *addr, int len)
{
struct in6_addr mask;
size_t i;
if (ipv6_mask(&mask, len) == -1)
return -1;
*prefix = *addr;
for (i = 0; i < sizeof(prefix->s6_addr); i++)
prefix->s6_addr[i] &= mask.s6_addr[i];
return 0;
}
int
ipv6_mask(struct in6_addr *mask, int len)
{
static const unsigned char masks[NBBY] =
{ 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
int bytes, bits, i;
if (len < 0 || len > 128) {
errno = EINVAL;
return -1;
}
memset(mask, 0, sizeof(*mask));
bytes = len / NBBY;
bits = len % NBBY;
for (i = 0; i < bytes; i++)
mask->s6_addr[i] = 0xff;
if (bits != 0) {
/* Coverify false positive.
* bytelen cannot be 16 if bitlen is non zero */
/* coverity[overrun-local] */
mask->s6_addr[bytes] = masks[bits - 1];
}
return 0;
}
uint8_t
ipv6_prefixlen(const struct in6_addr *mask)
{
int x = 0, y;
const unsigned char *lim, *p;
lim = (const unsigned char *)mask + sizeof(*mask);
for (p = (const unsigned char *)mask; p < lim; x++, p++) {
if (*p != 0xff)
break;
}
y = 0;
if (p < lim) {
for (y = 0; y < NBBY; y++) {
if ((*p & (0x80 >> y)) == 0)
break;
}
}
/*
* when the limit pointer is given, do a stricter check on the
* remaining bits.
*/
if (p < lim) {
if (y != 0 && (*p & (0x00ff >> y)) != 0)
return 0;
for (p = p + 1; p < lim; p++)
if (*p != 0)
return 0;
}
return (uint8_t)(x * NBBY + y);
}
static void
in6_to_h64(uint64_t *vhigh, uint64_t *vlow, const struct in6_addr *addr)
{
*vhigh = be64dec(addr->s6_addr);
*vlow = be64dec(addr->s6_addr + 8);
}
static void
h64_to_in6(struct in6_addr *addr, uint64_t vhigh, uint64_t vlow)
{
be64enc(addr->s6_addr, vhigh);
be64enc(addr->s6_addr + 8, vlow);
}
int
ipv6_userprefix(
const struct in6_addr *prefix, // prefix from router
short prefix_len, // length of prefix received
uint64_t user_number, // "random" number from user
struct in6_addr *result, // resultant prefix
short result_len) // desired prefix length
{
uint64_t vh, vl, user_low, user_high;
if (prefix_len < 1 || prefix_len > 128 ||
result_len < 1 || result_len > 128)
{
errno = EINVAL;
return -1;
}
/* Check that the user_number fits inside result_len less prefix_len */
if (result_len < prefix_len ||
fls64(user_number) > result_len - prefix_len)
{
errno = ERANGE;
return -1;
}
/* If user_number is zero, just copy the prefix into the result. */
if (user_number == 0) {
*result = *prefix;
return 0;
}
/* Shift user_number so it fit's just inside result_len.
* Shifting by 0 or sizeof(user_number) is undefined,
* so we cater for that. */
if (result_len == 128) {
user_high = 0;
user_low = user_number;
} else if (result_len > 64) {
if (prefix_len >= 64)
user_high = 0;
else
user_high = user_number >> (result_len - prefix_len);
user_low = user_number << (128 - result_len);
} else if (result_len == 64) {
user_high = user_number;
user_low = 0;
} else {
user_high = user_number << (64 - result_len);
user_low = 0;
}
/* convert to two 64bit host order values */
in6_to_h64(&vh, &vl, prefix);
vh |= user_high;
vl |= user_low;
/* copy back result */
h64_to_in6(result, vh, vl);
return 0;
}
#ifdef IPV6_POLLADDRFLAG
void
ipv6_checkaddrflags(void *arg)
{
struct ipv6_addr *ia;
int flags;
const char *alias;
ia = arg;
#ifdef ALIAS_ADDR
alias = ia->alias;
#else
alias = NULL;
#endif
if ((flags = if_addrflags6(ia->iface, &ia->addr, alias)) == -1) {
if (errno != EEXIST && errno != EADDRNOTAVAIL)
logerr("%s: if_addrflags6", __func__);
return;
}
if (!(flags & IN6_IFF_TENTATIVE)) {
/* Simulate the kernel announcing the new address. */
ipv6_handleifa(ia->iface->ctx, RTM_NEWADDR,
ia->iface->ctx->ifaces, ia->iface->name,
&ia->addr, ia->prefix_len, flags, 0);
} else {
/* Still tentative? Check again in a bit. */
eloop_timeout_add_msec(ia->iface->ctx->eloop,
RETRANS_TIMER / 2, ipv6_checkaddrflags, ia);
}
}
#endif
static void
ipv6_deletedaddr(struct ipv6_addr *ia)
{
#ifdef PRIVSEP
if (!(ia->iface->ctx->options & DHCPCD_MASTER))
ps_inet_closedhcp6(ia);
#elif defined(SMALL)
UNUSED(ia);
#else
/* NOREJECT is set if we delegated exactly the prefix to another
* address.
* This can only be one address, so just clear the flag.
* This should ensure the reject route will be restored. */
if (ia->delegating_prefix != NULL)
ia->delegating_prefix->flags &= ~IPV6_AF_NOREJECT;
#endif
}
void
ipv6_deleteaddr(struct ipv6_addr *ia)
{
struct ipv6_state *state;
struct ipv6_addr *ap;
loginfox("%s: deleting address %s", ia->iface->name, ia->saddr);
if (if_address6(RTM_DELADDR, ia) == -1 &&
errno != EADDRNOTAVAIL && errno != ESRCH &&
errno != ENXIO && errno != ENODEV)
logerr(__func__);
ipv6_deletedaddr(ia);
state = IPV6_STATE(ia->iface);
TAILQ_FOREACH(ap, &state->addrs, next) {
if (IN6_ARE_ADDR_EQUAL(&ap->addr, &ia->addr)) {
TAILQ_REMOVE(&state->addrs, ap, next);
ipv6_freeaddr(ap);
break;
}
}
#ifdef ND6_ADVERTISE
/* Advertise the address if it exists on another interface. */
ipv6nd_advertise(ia);
#endif
}
static int
ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now)
{
struct interface *ifp;
uint32_t pltime, vltime;
int loglevel;
#ifdef ND6_ADVERTISE
bool vltime_was_zero = ia->prefix_vltime == 0;
#endif
#ifdef __sun
struct ipv6_state *state;
struct ipv6_addr *ia2;
/* If we re-add then address on Solaris then the prefix
* route will be scrubbed and re-added. Something might
* be using it, so let's avoid it. */
if (ia->flags & IPV6_AF_DADCOMPLETED) {
logdebugx("%s: IP address %s already exists",
ia->iface->name, ia->saddr);
#ifdef ND6_ADVERTISE
goto advertise;
#else
return 0;
#endif
}
#endif
/* Remember the interface of the address. */
ifp = ia->iface;
if (!(ia->flags & IPV6_AF_DADCOMPLETED) &&
ipv6_iffindaddr(ifp, &ia->addr, IN6_IFF_NOTUSEABLE))
ia->flags |= IPV6_AF_DADCOMPLETED;
/* Adjust plftime and vltime based on acquired time */
pltime = ia->prefix_pltime;
vltime = ia->prefix_vltime;
if (timespecisset(&ia->acquired) &&
(ia->prefix_pltime != ND6_INFINITE_LIFETIME ||
ia->prefix_vltime != ND6_INFINITE_LIFETIME))
{
uint32_t elapsed;
struct timespec n;
if (now == NULL) {
clock_gettime(CLOCK_MONOTONIC, &n);
now = &n;
}
elapsed = (uint32_t)eloop_timespec_diff(now, &ia->acquired,
NULL);
if (ia->prefix_pltime != ND6_INFINITE_LIFETIME) {
if (elapsed > ia->prefix_pltime)
ia->prefix_pltime = 0;
else
ia->prefix_pltime -= elapsed;
}
if (ia->prefix_vltime != ND6_INFINITE_LIFETIME) {
if (elapsed > ia->prefix_vltime)
ia->prefix_vltime = 0;
else
ia->prefix_vltime -= elapsed;
}
}
loglevel = ia->flags & IPV6_AF_NEW ? LOG_INFO : LOG_DEBUG;
logmessage(loglevel, "%s: adding %saddress %s", ifp->name,
#ifdef IPV6_AF_TEMPORARY
ia->flags & IPV6_AF_TEMPORARY ? "temporary " : "",
#else
"",
#endif
ia->saddr);
if (ia->prefix_pltime == ND6_INFINITE_LIFETIME &&
ia->prefix_vltime == ND6_INFINITE_LIFETIME)
logdebugx("%s: pltime infinity, vltime infinity",
ifp->name);
else if (ia->prefix_pltime == ND6_INFINITE_LIFETIME)
logdebugx("%s: pltime infinity, vltime %"PRIu32" seconds",
ifp->name, ia->prefix_vltime);
else if (ia->prefix_vltime == ND6_INFINITE_LIFETIME)
logdebugx("%s: pltime %"PRIu32"seconds, vltime infinity",
ifp->name, ia->prefix_pltime);
else
logdebugx("%s: pltime %"PRIu32" seconds, vltime %"PRIu32
" seconds",
ifp->name, ia->prefix_pltime, ia->prefix_vltime);
if (if_address6(RTM_NEWADDR, ia) == -1) {
logerr(__func__);
/* Restore real pltime and vltime */
ia->prefix_pltime = pltime;
ia->prefix_vltime = vltime;
return -1;
}
#ifdef IPV6_MANAGETEMPADDR
/* RFC4941 Section 3.4 */
if (ia->flags & IPV6_AF_TEMPORARY &&
ia->prefix_pltime &&
ia->prefix_vltime &&
ifp->options->options & DHCPCD_SLAACTEMP)
eloop_timeout_add_sec(ifp->ctx->eloop,
ia->prefix_pltime - REGEN_ADVANCE,
ipv6_regentempaddr, ia);
#endif
/* Restore real pltime and vltime */
ia->prefix_pltime = pltime;
ia->prefix_vltime = vltime;
ia->flags &= ~IPV6_AF_NEW;
ia->flags |= IPV6_AF_ADDED;
#ifndef SMALL
if (ia->delegating_prefix != NULL)
ia->flags |= IPV6_AF_DELEGATED;
#endif
#ifdef IPV6_POLLADDRFLAG
eloop_timeout_delete(ifp->ctx->eloop,
ipv6_checkaddrflags, ia);
if (!(ia->flags & IPV6_AF_DADCOMPLETED)) {
eloop_timeout_add_msec(ifp->ctx->eloop,
RETRANS_TIMER / 2, ipv6_checkaddrflags, ia);
}
#endif
#ifdef __sun
/* Solaris does not announce new addresses which need DaD
* so we need to take a copy and add it to our list.
* Otherwise aliasing gets confused if we add another
* address during DaD. */
state = IPV6_STATE(ifp);
TAILQ_FOREACH(ia2, &state->addrs, next) {
if (IN6_ARE_ADDR_EQUAL(&ia2->addr, &ia->addr))
break;
}
if (ia2 == NULL) {
if ((ia2 = malloc(sizeof(*ia2))) == NULL) {
logerr(__func__);
return 0; /* Well, we did add the address */
}
memcpy(ia2, ia, sizeof(*ia2));
TAILQ_INSERT_TAIL(&state->addrs, ia2, next);
}
#endif
#ifdef ND6_ADVERTISE
#ifdef __sun
advertise:
#endif
/* Re-advertise the preferred address to be safe. */
if (!vltime_was_zero)
ipv6nd_advertise(ia);
#endif
return 0;
}
#ifdef ALIAS_ADDR
/* Find the next logical alias address we can use. */
static int
ipv6_aliasaddr(struct ipv6_addr *ia, struct ipv6_addr **repl)
{
struct ipv6_state *state;
struct ipv6_addr *iap;
unsigned int lun;
char alias[IF_NAMESIZE];
if (ia->alias[0] != '\0')
return 0;
state = IPV6_STATE(ia->iface);
/* First find an existng address.
* This can happen when dhcpcd restarts as ND and DHCPv6
* maintain their own lists of addresses. */
TAILQ_FOREACH(iap, &state->addrs, next) {
if (iap->alias[0] != '\0' &&
IN6_ARE_ADDR_EQUAL(&iap->addr, &ia->addr))
{
strlcpy(ia->alias, iap->alias, sizeof(ia->alias));
return 0;
}
}
lun = 0;
find_unit:
if (if_makealias(alias, IF_NAMESIZE, ia->iface->name, lun) >=
IF_NAMESIZE)
{
errno = ENOMEM;
return -1;
}
TAILQ_FOREACH(iap, &state->addrs, next) {
if (iap->alias[0] == '\0')
continue;
if (IN6_IS_ADDR_UNSPECIFIED(&iap->addr)) {
/* No address assigned? Lets use it. */
strlcpy(ia->alias, iap->alias, sizeof(ia->alias));
if (repl)
*repl = iap;
return 1;
}
if (strcmp(iap->alias, alias) == 0)
break;
}
if (iap != NULL) {
if (lun == UINT_MAX) {
errno = ERANGE;
return -1;
}
lun++;
goto find_unit;
}
strlcpy(ia->alias, alias, sizeof(ia->alias));
return 0;
}
#endif
int
ipv6_addaddr(struct ipv6_addr *ia, const struct timespec *now)
{
int r;
#ifdef ALIAS_ADDR
int replaced, blank;
struct ipv6_addr *replaced_ia;
blank = (ia->alias[0] == '\0');
if ((replaced = ipv6_aliasaddr(ia, &replaced_ia)) == -1)
return -1;
if (blank)
logdebugx("%s: aliased %s", ia->alias, ia->saddr);
#endif
if ((r = ipv6_addaddr1(ia, now)) == 0) {
#ifdef ALIAS_ADDR
if (replaced) {
struct ipv6_state *state;
state = IPV6_STATE(ia->iface);
TAILQ_REMOVE(&state->addrs, replaced_ia, next);
ipv6_freeaddr(replaced_ia);
}
#endif
}
return r;
}
int
ipv6_findaddrmatch(const struct ipv6_addr *addr, const struct in6_addr *match,
unsigned int flags)
{
if (match == NULL) {
if ((addr->flags &
(IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)) ==
(IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED))
return 1;
} else if (addr->prefix_vltime &&
IN6_ARE_ADDR_EQUAL(&addr->addr, match) &&
(!flags || addr->flags & flags))
return 1;
return 0;
}
struct ipv6_addr *
ipv6_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr, unsigned int flags)
{
struct ipv6_addr *nap;
#ifdef DHCP6
struct ipv6_addr *dap;
#endif
nap = ipv6nd_findaddr(ctx, addr, flags);
#ifdef DHCP6
dap = dhcp6_findaddr(ctx, addr, flags);
if (!dap && !nap)
return NULL;
if (dap && !nap)
return dap;
if (nap && !dap)
return nap;
if (nap->iface->metric < dap->iface->metric)
return nap;
return dap;
#else
return nap;
#endif
}
int
ipv6_doaddr(struct ipv6_addr *ia, struct timespec *now)
{
/* A delegated prefix is not an address. */
if (ia->flags & IPV6_AF_DELEGATEDPFX)
return 0;
if (ia->prefix_vltime == 0) {
if (ia->flags & IPV6_AF_ADDED)
ipv6_deleteaddr(ia);
eloop_q_timeout_delete(ia->iface->ctx->eloop,
ELOOP_QUEUE_ALL, NULL, ia);
if (ia->flags & IPV6_AF_REQUEST) {
ia->flags &= ~IPV6_AF_ADDED;
return 0;
}
return -1;
}
if (ia->flags & IPV6_AF_STALE ||
IN6_IS_ADDR_UNSPECIFIED(&ia->addr))
return 0;
if (!timespecisset(now))
clock_gettime(CLOCK_MONOTONIC, now);
ipv6_addaddr(ia, now);
return ia->flags & IPV6_AF_NEW ? 1 : 0;
}
ssize_t
ipv6_addaddrs(struct ipv6_addrhead *iaddrs)
{
struct timespec now;
struct ipv6_addr *ia, *ian;
ssize_t i, r;
i = 0;
timespecclear(&now);
TAILQ_FOREACH_SAFE(ia, iaddrs, next, ian) {
r = ipv6_doaddr(ia, &now);
if (r != 0)
i++;
if (r == -1) {
TAILQ_REMOVE(iaddrs, ia, next);
ipv6_freeaddr(ia);
}
}
return i;
}
void
ipv6_freeaddr(struct ipv6_addr *ia)
{
struct eloop *eloop = ia->iface->ctx->eloop;
#ifndef SMALL
struct ipv6_addr *iad;
/* Forget the reference */
if (ia->flags & IPV6_AF_DELEGATEDPFX) {
TAILQ_FOREACH(iad, &ia->pd_pfxs, pd_next) {
iad->delegating_prefix = NULL;
}
} else if (ia->delegating_prefix != NULL) {
TAILQ_REMOVE(&ia->delegating_prefix->pd_pfxs, ia, pd_next);
}
#endif
if (ia->dhcp6_fd != -1) {
close(ia->dhcp6_fd);
eloop_event_delete(eloop, ia->dhcp6_fd);
}
eloop_q_timeout_delete(eloop, ELOOP_QUEUE_ALL, NULL, ia);
free(ia->na);
free(ia);
}
void
ipv6_freedrop_addrs(struct ipv6_addrhead *addrs, int drop,
const struct interface *ifd)
{
struct ipv6_addr *ap, *apn, *apf;
struct timespec now;
#ifdef SMALL
UNUSED(ifd);
#endif
timespecclear(&now);
TAILQ_FOREACH_SAFE(ap, addrs, next, apn) {
#ifndef SMALL
if (ifd != NULL &&
(ap->delegating_prefix == NULL ||
ap->delegating_prefix->iface != ifd))
continue;
#endif
if (drop != 2)
TAILQ_REMOVE(addrs, ap, next);
if (drop && ap->flags & IPV6_AF_ADDED &&
(ap->iface->options->options &
(DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
(DHCPCD_EXITING | DHCPCD_PERSISTENT))
{
/* Don't drop link-local addresses. */
if (!IN6_IS_ADDR_LINKLOCAL(&ap->addr) ||
CAN_DROP_LLADDR(ap->iface))
{
if (drop == 2)
TAILQ_REMOVE(addrs, ap, next);
/* Find the same address somewhere else */
apf = ipv6_findaddr(ap->iface->ctx, &ap->addr,
0);
if ((apf == NULL ||
(apf->iface != ap->iface)))
ipv6_deleteaddr(ap);
if (!(ap->iface->options->options &
DHCPCD_EXITING) && apf)
{
if (!timespecisset(&now))
clock_gettime(CLOCK_MONOTONIC,
&now);
ipv6_addaddr(apf, &now);
}
if (drop == 2)
ipv6_freeaddr(ap);
}
}
if (drop != 2)
ipv6_freeaddr(ap);
}
}
static struct ipv6_state *
ipv6_getstate(struct interface *ifp)
{
struct ipv6_state *state;
state = IPV6_STATE(ifp);
if (state == NULL) {
ifp->if_data[IF_DATA_IPV6] = calloc(1, sizeof(*state));
state = IPV6_STATE(ifp);
if (state == NULL) {
logerr(__func__);
return NULL;
}
TAILQ_INIT(&state->addrs);
TAILQ_INIT(&state->ll_callbacks);
}
return state;
}
struct ipv6_addr *
ipv6_anyglobal(struct interface *sifp)
{
struct interface *ifp;
struct ipv6_state *state;
struct ipv6_addr *ia;
#ifdef BSD
bool forwarding;
#ifdef HAVE_PLEDGE
forwarding = ps_root_ip6forwarding(sifp->ctx) == 1;
#else
forwarding = ip6_forwarding(NULL) == 1;
#endif
#endif
TAILQ_FOREACH(ifp, sifp->ctx->ifaces, next) {
#ifdef BSD
if (ifp != sifp && !forwarding)
continue;
#else
if (ifp != sifp && ip6_forwarding(ifp->name) != 1)
continue;
#endif
state = IPV6_STATE(ifp);
if (state == NULL)
continue;
TAILQ_FOREACH(ia, &state->addrs, next) {
if (IN6_IS_ADDR_LINKLOCAL(&ia->addr))
continue;
/* Let's be optimistic.
* Any decent OS won't forward or accept traffic
* from/to tentative or detached addresses. */
if (!(ia->addr_flags & IN6_IFF_DUPLICATED))
return ia;
}
}
return NULL;
}
void
ipv6_handleifa(struct dhcpcd_ctx *ctx,
int cmd, struct if_head *ifs, const char *ifname,
const struct in6_addr *addr, uint8_t prefix_len, int addrflags, pid_t pid)
{
struct interface *ifp;
struct ipv6_state *state;
struct ipv6_addr *ia;
struct ll_callback *cb;
bool anyglobal;
#ifdef __sun
struct sockaddr_in6 subnet;
/* Solaris on-link route is an unspecified address! */
if (IN6_IS_ADDR_UNSPECIFIED(addr)) {
if (if_getsubnet(ctx, ifname, AF_INET6,
&subnet, sizeof(subnet)) == -1)
{
logerr(__func__);
return;
}
addr = &subnet.sin6_addr;
}
#endif
#if 0
char dbuf[INET6_ADDRSTRLEN];
const char *dbp;
dbp = inet_ntop(AF_INET6, &addr->s6_addr,
dbuf, INET6_ADDRSTRLEN);
loginfox("%s: cmd %d addr %s addrflags %d",
ifname, cmd, dbp, addrflags);
#endif
if (ifs == NULL)
ifs = ctx->ifaces;
if (ifs == NULL)
return;
if ((ifp = if_find(ifs, ifname)) == NULL)
return;
if ((state = ipv6_getstate(ifp)) == NULL)
return;
anyglobal = ipv6_anyglobal(ifp) != NULL;
TAILQ_FOREACH(ia, &state->addrs, next) {
if (IN6_ARE_ADDR_EQUAL(&ia->addr, addr))
break;
}
switch (cmd) {
case RTM_DELADDR:
if (ia != NULL) {
TAILQ_REMOVE(&state->addrs, ia, next);
#ifdef ND6_ADVERTISE
/* Advertise the address if it exists on
* another interface. */
ipv6nd_advertise(ia);
#endif
/* We'll free it at the end of the function. */
}
break;
case RTM_NEWADDR:
if (ia == NULL) {
ia = ipv6_newaddr(ifp, addr, prefix_len, 0);
#ifdef ALIAS_ADDR
strlcpy(ia->alias, ifname, sizeof(ia->alias));
#endif
if (if_getlifetime6(ia) == -1) {
/* No support or address vanished.
* Either way, just set a deprecated
* infinite time lifetime and continue.
* This is fine because we only want
* to know this when trying to extend
* temporary addresses.
* As we can't extend infinite, we'll
* create a new temporary address. */
ia->prefix_pltime = 0;
ia->prefix_vltime =
ND6_INFINITE_LIFETIME;
}
/* This is a minor regression against RFC 4941
* because the kernel only knows when the
* lifetimes were last updated, not when the
* address was initially created.
* Provided dhcpcd is not restarted, this
* won't be a problem.
* If we don't like it, we can always
* pretend lifetimes are infinite and always
* generate a new temporary address on
* restart. */
ia->acquired = ia->created;
TAILQ_INSERT_TAIL(&state->addrs, ia, next);
}
ia->addr_flags = addrflags;
ia->flags &= ~IPV6_AF_STALE;
#ifdef IPV6_MANAGETEMPADDR
if (ia->addr_flags & IN6_IFF_TEMPORARY)
ia->flags |= IPV6_AF_TEMPORARY;
#endif
if (IN6_IS_ADDR_LINKLOCAL(&ia->addr) || ia->dadcallback) {
#ifdef IPV6_POLLADDRFLAG
if (ia->addr_flags & IN6_IFF_TENTATIVE) {
eloop_timeout_add_msec(
ia->iface->ctx->eloop,
RETRANS_TIMER / 2, ipv6_checkaddrflags, ia);
break;
}
#endif
if (ia->dadcallback) {
ia->dadcallback(ia);
if (ctx->options & DHCPCD_FORKED)
goto out;
}
if (IN6_IS_ADDR_LINKLOCAL(&ia->addr) &&
!(ia->addr_flags & IN6_IFF_NOTUSEABLE))
{
/* Now run any callbacks.
* Typically IPv6RS or DHCPv6 */
while ((cb =
TAILQ_FIRST(&state->ll_callbacks)))
{
TAILQ_REMOVE(
&state->ll_callbacks,
cb, next);
cb->callback(cb->arg);
free(cb);
if (ctx->options & DHCPCD_FORKED)
goto out;
}
}
}
break;
}
if (ia == NULL)
return;
ctx->options &= ~DHCPCD_RTBUILD;
ipv6nd_handleifa(cmd, ia, pid);
#ifdef DHCP6
dhcp6_handleifa(cmd, ia, pid);
#endif
out:
/* Done with the ia now, so free it. */
if (cmd == RTM_DELADDR)
ipv6_freeaddr(ia);
else if (!(ia->addr_flags & IN6_IFF_NOTUSEABLE))
ia->flags |= IPV6_AF_DADCOMPLETED;
/* If we've not already called rt_build via the IPv6ND
* or DHCP6 handlers and the existance of any useable
* global address on the interface has changed,
* call rt_build to add/remove the default route. */
if (ifp->active &&
((ifp->options != NULL && ifp->options->options & DHCPCD_IPV6) ||
(ifp->options == NULL && ctx->options & DHCPCD_IPV6)) &&
!(ctx->options & DHCPCD_RTBUILD) &&
(ipv6_anyglobal(ifp) != NULL) != anyglobal)
rt_build(ctx, AF_INET6);
}
int
ipv6_hasaddr(const struct interface *ifp)
{
if (ipv6nd_iffindaddr(ifp, NULL, 0) != NULL)
return 1;
#ifdef DHCP6
if (dhcp6_iffindaddr(ifp, NULL, 0) != NULL)
return 1;
#endif
return 0;
}
struct ipv6_addr *
ipv6_iffindaddr(struct interface *ifp, const struct in6_addr *addr,
int revflags)
{
struct ipv6_state *state;
struct ipv6_addr *ap;
state = IPV6_STATE(ifp);
if (state) {
TAILQ_FOREACH(ap, &state->addrs, next) {
if (addr == NULL) {
if (IN6_IS_ADDR_LINKLOCAL(&ap->addr) &&
(!revflags || !(ap->addr_flags & revflags)))
return ap;
} else {
if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr) &&
(!revflags || !(ap->addr_flags & revflags)))
return ap;
}
}
}
return NULL;
}
static struct ipv6_addr *
ipv6_iffindmaskaddr(const struct interface *ifp, const struct in6_addr *addr)
{
struct ipv6_state *state;
struct ipv6_addr *ap;
struct in6_addr mask;
state = IPV6_STATE(ifp);
if (state) {
TAILQ_FOREACH(ap, &state->addrs, next) {
if (ipv6_mask(&mask, ap->prefix_len) == -1)
continue;
if (IN6_ARE_MASKED_ADDR_EQUAL(&ap->addr, addr, &mask))
return ap;
}
}
return NULL;
}
struct ipv6_addr *
ipv6_findmaskaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr)
{
struct interface *ifp;
struct ipv6_addr *ap;
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
ap = ipv6_iffindmaskaddr(ifp, addr);
if (ap != NULL)
return ap;
}
return NULL;
}
int
ipv6_addlinklocalcallback(struct interface *ifp,
void (*callback)(void *), void *arg)
{
struct ipv6_state *state;
struct ll_callback *cb;
state = ipv6_getstate(ifp);
TAILQ_FOREACH(cb, &state->ll_callbacks, next) {
if (cb->callback == callback && cb->arg == arg)
break;
}
if (cb == NULL) {
cb = malloc(sizeof(*cb));
if (cb == NULL) {
logerr(__func__);
return -1;
}
cb->callback = callback;
cb->arg = arg;
TAILQ_INSERT_TAIL(&state->ll_callbacks, cb, next);
}
return 0;
}
static struct ipv6_addr *
ipv6_newlinklocal(struct interface *ifp)
{
struct ipv6_addr *ia;
struct in6_addr in6;
memset(&in6, 0, sizeof(in6));
in6.s6_addr32[0] = htonl(0xfe800000);
ia = ipv6_newaddr(ifp, &in6, 64, 0);
if (ia != NULL) {
ia->prefix_pltime = ND6_INFINITE_LIFETIME;
ia->prefix_vltime = ND6_INFINITE_LIFETIME;
}
return ia;
}
static const uint8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
static const uint8_t allone[8] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
static int
ipv6_addlinklocal(struct interface *ifp)
{
struct ipv6_state *state;
struct ipv6_addr *ap, *ap2;
int dadcounter;
/* Check sanity before malloc */
if (!(ifp->options->options & DHCPCD_SLAACPRIVATE)) {
switch (ifp->hwtype) {
case ARPHRD_ETHER:
/* Check for a valid hardware address */
if (ifp->hwlen != 6 && ifp->hwlen != 8) {
errno = ENOTSUP;
return -1;
}
if (memcmp(ifp->hwaddr, allzero, ifp->hwlen) == 0 ||
memcmp(ifp->hwaddr, allone, ifp->hwlen) == 0)
{
errno = EINVAL;
return -1;
}
break;
default:
errno = ENOTSUP;
return -1;
}
}
state = ipv6_getstate(ifp);
if (state == NULL)
return -1;
ap = ipv6_newlinklocal(ifp);
if (ap == NULL)
return -1;
dadcounter = 0;
if (ifp->options->options & DHCPCD_SLAACPRIVATE) {
nextslaacprivate:
if (ipv6_makestableprivate(&ap->addr,
&ap->prefix, ap->prefix_len, ifp, &dadcounter) == -1)
{
free(ap);
return -1;
}
ap->dadcounter = dadcounter;
} else {
memcpy(ap->addr.s6_addr, ap->prefix.s6_addr, 8);
switch (ifp->hwtype) {
case ARPHRD_ETHER:
if (ifp->hwlen == 6) {
ap->addr.s6_addr[ 8] = ifp->hwaddr[0];
ap->addr.s6_addr[ 9] = ifp->hwaddr[1];
ap->addr.s6_addr[10] = ifp->hwaddr[2];
ap->addr.s6_addr[11] = 0xff;
ap->addr.s6_addr[12] = 0xfe;
ap->addr.s6_addr[13] = ifp->hwaddr[3];
ap->addr.s6_addr[14] = ifp->hwaddr[4];
ap->addr.s6_addr[15] = ifp->hwaddr[5];
} else if (ifp->hwlen == 8)
memcpy(&ap->addr.s6_addr[8], ifp->hwaddr, 8);
else {
free(ap);
errno = ENOTSUP;
return -1;
}
break;
}
/* Sanity check: g bit must not indciate "group" */
if (EUI64_GROUP(&ap->addr)) {
free(ap);
errno = EINVAL;
return -1;
}
EUI64_TO_IFID(&ap->addr);
}
/* Do we already have this address? */
TAILQ_FOREACH(ap2, &state->addrs, next) {
if (IN6_ARE_ADDR_EQUAL(&ap->addr, &ap2->addr)) {
if (ap2->addr_flags & IN6_IFF_DUPLICATED) {
if (ifp->options->options &
DHCPCD_SLAACPRIVATE)
{
dadcounter++;
goto nextslaacprivate;
}
free(ap);
errno = EADDRNOTAVAIL;
return -1;
}
logwarnx("%s: waiting for %s to complete",
ap2->iface->name, ap2->saddr);
free(ap);
errno = EEXIST;
return 0;
}
}
inet_ntop(AF_INET6, &ap->addr, ap->saddr, sizeof(ap->saddr));
TAILQ_INSERT_TAIL(&state->addrs, ap, next);
ipv6_addaddr(ap, NULL);
return 1;
}
static int
ipv6_tryaddlinklocal(struct interface *ifp)
{
struct ipv6_addr *ia;
/* We can't assign a link-locak address to this,
* the ppp process has to. */
if (ifp->flags & IFF_POINTOPOINT)
return 0;
ia = ipv6_iffindaddr(ifp, NULL, IN6_IFF_DUPLICATED);
if (ia != NULL) {
#ifdef IPV6_POLLADDRFLAG
if (ia->addr_flags & IN6_IFF_TENTATIVE) {
eloop_timeout_add_msec(
ia->iface->ctx->eloop,
RETRANS_TIMER / 2, ipv6_checkaddrflags, ia);
}
#endif
return 0;
}
if (!CAN_ADD_LLADDR(ifp))
return 0;
return ipv6_addlinklocal(ifp);
}
void
ipv6_setscope(struct sockaddr_in6 *sin, unsigned int ifindex)
{
#ifdef __KAME__
/* KAME based systems want to store the scope inside the sin6_addr
* for link local addresses */
if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
uint16_t scope = htons((uint16_t)ifindex);
memcpy(&sin->sin6_addr.s6_addr[2], &scope,
sizeof(scope));
}
sin->sin6_scope_id = 0;
#else
if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))
sin->sin6_scope_id = ifindex;
else
sin->sin6_scope_id = 0;
#endif
}
unsigned int
ipv6_getscope(const struct sockaddr_in6 *sin)
{
#ifdef __KAME__
uint16_t scope;
#endif
if (!IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))
return 0;
#ifdef __KAME__
memcpy(&scope, &sin->sin6_addr.s6_addr[2], sizeof(scope));
return (unsigned int)ntohs(scope);
#else
return (unsigned int)sin->sin6_scope_id;
#endif
}
struct ipv6_addr *
ipv6_newaddr(struct interface *ifp, const struct in6_addr *addr,
uint8_t prefix_len, unsigned int flags)
{
struct ipv6_addr *ia, *iaf;
char buf[INET6_ADDRSTRLEN];
const char *cbp;
bool tempaddr;
int addr_flags;
#ifdef IPV6_AF_TEMPORARY
tempaddr = flags & IPV6_AF_TEMPORARY;
#else
tempaddr = false;
#endif
/* If adding a new DHCP / RA derived address, check current flags
* from an existing address. */
if (tempaddr)
iaf = NULL;
else if (flags & IPV6_AF_AUTOCONF)
iaf = ipv6nd_iffindprefix(ifp, addr, prefix_len);
else
iaf = ipv6_iffindaddr(ifp, addr, 0);
if (iaf != NULL) {
addr_flags = iaf->addr_flags;
flags |= IPV6_AF_ADDED;
} else
addr_flags = IN6_IFF_TENTATIVE;
ia = calloc(1, sizeof(*ia));
if (ia == NULL)
goto err;
ia->iface = ifp;
ia->addr_flags = addr_flags;
ia->flags = IPV6_AF_NEW | flags;
if (!(ia->addr_flags & IN6_IFF_NOTUSEABLE))
ia->flags |= IPV6_AF_DADCOMPLETED;
ia->prefix_len = prefix_len;
ia->dhcp6_fd = -1;
#ifndef SMALL
TAILQ_INIT(&ia->pd_pfxs);
#endif
if (prefix_len == 128)
goto makepfx;
else if (ia->flags & IPV6_AF_AUTOCONF) {
ia->prefix = *addr;
if (iaf != NULL)
memcpy(&ia->addr, &iaf->addr, sizeof(ia->addr));
else {
ia->dadcounter = ipv6_makeaddr(&ia->addr, ifp,
&ia->prefix,
ia->prefix_len,
ia->flags);
if (ia->dadcounter == -1)
goto err;
}
} else if (ia->flags & IPV6_AF_RAPFX) {
ia->prefix = *addr;
#ifdef __sun
ia->addr = *addr;
cbp = inet_ntop(AF_INET6, &ia->addr, buf, sizeof(buf));
goto paddr;
#else
return ia;
#endif
} else if (ia->flags & (IPV6_AF_REQUEST | IPV6_AF_DELEGATEDPFX)) {
ia->prefix = *addr;
cbp = inet_ntop(AF_INET6, &ia->prefix, buf, sizeof(buf));
goto paddr;
} else {
makepfx:
ia->addr = *addr;
if (ipv6_makeprefix(&ia->prefix,
&ia->addr, ia->prefix_len) == -1)
goto err;
}
cbp = inet_ntop(AF_INET6, &ia->addr, buf, sizeof(buf));
paddr:
if (cbp == NULL)
goto err;
snprintf(ia->saddr, sizeof(ia->saddr), "%s/%d", cbp, ia->prefix_len);
return ia;
err:
logerr(__func__);
free(ia);
return NULL;
}
static void
ipv6_staticdadcallback(void *arg)
{
struct ipv6_addr *ia = arg;
int wascompleted;
wascompleted = (ia->flags & IPV6_AF_DADCOMPLETED);
ia->flags |= IPV6_AF_DADCOMPLETED;
if (ia->addr_flags & IN6_IFF_DUPLICATED)
logwarnx("%s: DAD detected %s", ia->iface->name,
ia->saddr);
else if (!wascompleted) {
logdebugx("%s: IPv6 static DAD completed",
ia->iface->name);
}
#define FINISHED (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)
if (!wascompleted) {
struct interface *ifp;
struct ipv6_state *state;
ifp = ia->iface;
state = IPV6_STATE(ifp);
TAILQ_FOREACH(ia, &state->addrs, next) {
if (ia->flags & IPV6_AF_STATIC &&
(ia->flags & FINISHED) != FINISHED)
{
wascompleted = 1;
break;
}
}
if (!wascompleted)
script_runreason(ifp, "STATIC6");
}
#undef FINISHED
}
ssize_t
ipv6_env(FILE *fp, const char *prefix, const struct interface *ifp)
{
struct ipv6_addr *ia;
ia = ipv6_iffindaddr(UNCONST(ifp), &ifp->options->req_addr6,
IN6_IFF_NOTUSEABLE);
if (ia == NULL)
return 0;
if (efprintf(fp, "%s_ip6_address=%s", prefix, ia->saddr) == -1)
return -1;
return 1;
}
int
ipv6_staticdadcompleted(const struct interface *ifp)
{
const struct ipv6_state *state;
const struct ipv6_addr *ia;
int n;
if ((state = IPV6_CSTATE(ifp)) == NULL)
return 0;
n = 0;
#define COMPLETED (IPV6_AF_STATIC | IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)
TAILQ_FOREACH(ia, &state->addrs, next) {
if ((ia->flags & COMPLETED) == COMPLETED &&
!(ia->addr_flags & IN6_IFF_NOTUSEABLE))
n++;
}
return n;
}
int
ipv6_startstatic(struct interface *ifp)
{
struct ipv6_addr *ia;
int run_script;
if (IN6_IS_ADDR_UNSPECIFIED(&ifp->options->req_addr6))
return 0;
ia = ipv6_iffindaddr(ifp, &ifp->options->req_addr6, 0);
if (ia != NULL &&
(ia->prefix_len != ifp->options->req_prefix_len ||
ia->addr_flags & IN6_IFF_NOTUSEABLE))
{
ipv6_deleteaddr(ia);
ia = NULL;
}
if (ia == NULL) {
struct ipv6_state *state;
ia = ipv6_newaddr(ifp, &ifp->options->req_addr6,
ifp->options->req_prefix_len, 0);
if (ia == NULL)
return -1;
state = IPV6_STATE(ifp);
TAILQ_INSERT_TAIL(&state->addrs, ia, next);
run_script = 0;
} else
run_script = 1;
ia->flags |= IPV6_AF_STATIC | IPV6_AF_ONLINK;
ia->prefix_vltime = ND6_INFINITE_LIFETIME;
ia->prefix_pltime = ND6_INFINITE_LIFETIME;
ia->dadcallback = ipv6_staticdadcallback;
ipv6_addaddr(ia, NULL);
rt_build(ifp->ctx, AF_INET6);
if (run_script)
script_runreason(ifp, "STATIC6");
return 1;
}
/* Ensure the interface has a link-local address */
int
ipv6_start(struct interface *ifp)
{
#ifdef IPV6_POLLADDRFLAG
struct ipv6_state *state;
/* We need to update the address flags. */
if ((state = IPV6_STATE(ifp)) != NULL) {
struct ipv6_addr *ia;
const char *alias;
int flags;
TAILQ_FOREACH(ia, &state->addrs, next) {
#ifdef ALIAS_ADDR
alias = ia->alias;
#else
alias = NULL;
#endif
flags = if_addrflags6(ia->iface, &ia->addr, alias);
if (flags != -1)
ia->addr_flags = flags;
}
}
#endif
if (ipv6_tryaddlinklocal(ifp) == -1)
return -1;
return 0;
}
void
ipv6_freedrop(struct interface *ifp, int drop)
{
struct ipv6_state *state;
struct ll_callback *cb;
if (ifp == NULL)
return;
if ((state = IPV6_STATE(ifp)) == NULL)
return;
/* If we got here, we can get rid of any LL callbacks. */
while ((cb = TAILQ_FIRST(&state->ll_callbacks))) {
TAILQ_REMOVE(&state->ll_callbacks, cb, next);
free(cb);
}
ipv6_freedrop_addrs(&state->addrs, drop ? 2 : 0, NULL);
if (drop) {
if (ifp->ctx->ra_routers != NULL)
rt_build(ifp->ctx, AF_INET6);
} else {
/* Because we need to cache the addresses we don't control,
* we only free the state on when NOT dropping addresses. */
free(state);
ifp->if_data[IF_DATA_IPV6] = NULL;
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
}
}
void
ipv6_ctxfree(struct dhcpcd_ctx *ctx)
{
free(ctx->ra_routers);
free(ctx->secret);
}
int
ipv6_handleifa_addrs(int cmd,
struct ipv6_addrhead *addrs, const struct ipv6_addr *addr, pid_t pid)
{
struct ipv6_addr *ia, *ian;
uint8_t found, alldadcompleted;
alldadcompleted = 1;
found = 0;
TAILQ_FOREACH_SAFE(ia, addrs, next, ian) {
if (!IN6_ARE_ADDR_EQUAL(&addr->addr, &ia->addr)) {
if (ia->flags & IPV6_AF_ADDED &&
!(ia->flags & IPV6_AF_DADCOMPLETED))
alldadcompleted = 0;
continue;
}
switch (cmd) {
case RTM_DELADDR:
if (ia->flags & IPV6_AF_ADDED) {
logwarnx("%s: pid %d deleted address %s",
ia->iface->name, pid, ia->saddr);
ia->flags &= ~IPV6_AF_ADDED;
}
ipv6_deletedaddr(ia);
if (ia->flags & IPV6_AF_DELEGATED) {
TAILQ_REMOVE(addrs, ia, next);
ipv6_freeaddr(ia);
}
break;
case RTM_NEWADDR:
ia->addr_flags = addr->addr_flags;
/* Safety - ignore tentative announcements */
if (ia->addr_flags &
(IN6_IFF_DETACHED | IN6_IFF_TENTATIVE))
break;
if ((ia->flags & IPV6_AF_DADCOMPLETED) == 0) {
found++;
if (ia->dadcallback)
ia->dadcallback(ia);
/* We need to set this here in-case the
* dadcallback function checks it */
ia->flags |= IPV6_AF_DADCOMPLETED;
}
break;
}
}
return alldadcompleted ? found : 0;
}
#ifdef IPV6_MANAGETEMPADDR
static void
ipv6_regen_desync(struct interface *ifp, bool force)
{
struct ipv6_state *state;
unsigned int max;
state = IPV6_STATE(ifp);
/* RFC4941 Section 5 states that DESYNC_FACTOR must never be
* greater than TEMP_VALID_LIFETIME - REGEN_ADVANCE.
* I believe this is an error and it should be never be greater than
* TEMP_PREFERRED_LIFETIME - REGEN_ADVANCE. */
max = TEMP_PREFERRED_LIFETIME - REGEN_ADVANCE;
if (state->desync_factor && !force && state->desync_factor < max)
return;
if (state->desync_factor == 0)
state->desync_factor =
arc4random_uniform(MIN(MAX_DESYNC_FACTOR, max));
max = TEMP_PREFERRED_LIFETIME - state->desync_factor - REGEN_ADVANCE;
eloop_timeout_add_sec(ifp->ctx->eloop, max, ipv6_regentempaddrs, ifp);
}
/* RFC4941 Section 3.3.7 */
static void
ipv6_tempdadcallback(void *arg)
{
struct ipv6_addr *ia = arg;
if (ia->addr_flags & IN6_IFF_DUPLICATED) {
struct ipv6_addr *ia1;
struct timespec tv;
if (++ia->dadcounter == TEMP_IDGEN_RETRIES) {
logerrx("%s: too many duplicate temporary addresses",
ia->iface->name);
return;
}
clock_gettime(CLOCK_MONOTONIC, &tv);
if ((ia1 = ipv6_createtempaddr(ia, &tv)) == NULL)
logerr(__func__);
else
ia1->dadcounter = ia->dadcounter;
ipv6_deleteaddr(ia);
if (ia1)
ipv6_addaddr(ia1, &ia1->acquired);
}
}
struct ipv6_addr *
ipv6_createtempaddr(struct ipv6_addr *ia0, const struct timespec *now)
{
struct ipv6_state *state;
struct interface *ifp = ia0->iface;
struct ipv6_addr *ia;
ia = ipv6_newaddr(ifp, &ia0->prefix, ia0->prefix_len,
IPV6_AF_AUTOCONF | IPV6_AF_TEMPORARY);
if (ia == NULL)
return NULL;
ia->dadcallback = ipv6_tempdadcallback;
ia->created = ia->acquired = now ? *now : ia0->acquired;
/* Ensure desync is still valid */
ipv6_regen_desync(ifp, false);
/* RFC4941 Section 3.3.4 */
state = IPV6_STATE(ia->iface);
ia->prefix_pltime = MIN(ia0->prefix_pltime,
TEMP_PREFERRED_LIFETIME - state->desync_factor);
ia->prefix_vltime = MIN(ia0->prefix_vltime, TEMP_VALID_LIFETIME);
if (ia->prefix_pltime <= REGEN_ADVANCE ||
ia->prefix_pltime > ia0->prefix_vltime)
{
errno = EINVAL;
free(ia);
return NULL;
}
TAILQ_INSERT_TAIL(&state->addrs, ia, next);
return ia;
}
struct ipv6_addr *
ipv6_settemptime(struct ipv6_addr *ia, int flags)
{
struct ipv6_state *state;
struct ipv6_addr *ap, *first;
state = IPV6_STATE(ia->iface);
first = NULL;
TAILQ_FOREACH_REVERSE(ap, &state->addrs, ipv6_addrhead, next) {
if (ap->flags & IPV6_AF_TEMPORARY &&
ap->prefix_pltime &&
IN6_ARE_ADDR_EQUAL(&ia->prefix, &ap->prefix))
{
unsigned int max, ext;
if (flags == 0) {
if (ap->prefix_pltime -
(uint32_t)(ia->acquired.tv_sec -
ap->acquired.tv_sec)
< REGEN_ADVANCE)
continue;
return ap;
}
if (!(ap->flags & IPV6_AF_ADDED))
ap->flags |= IPV6_AF_NEW | IPV6_AF_AUTOCONF;
ap->flags &= ~IPV6_AF_STALE;
/* RFC4941 Section 3.4
* Deprecated prefix, deprecate the temporary address */
if (ia->prefix_pltime == 0) {
ap->prefix_pltime = 0;
goto valid;
}
/* Ensure desync is still valid */
ipv6_regen_desync(ap->iface, false);
/* RFC4941 Section 3.3.2
* Extend temporary times, but ensure that they
* never last beyond the system limit. */
ext = (unsigned int)ia->acquired.tv_sec
+ ia->prefix_pltime;
max = (unsigned int)(ap->created.tv_sec +
TEMP_PREFERRED_LIFETIME -
state->desync_factor);
if (ext < max)
ap->prefix_pltime = ia->prefix_pltime;
else
ap->prefix_pltime =
(uint32_t)(max - ia->acquired.tv_sec);
valid:
ext = (unsigned int)ia->acquired.tv_sec +
ia->prefix_vltime;
max = (unsigned int)(ap->created.tv_sec +
TEMP_VALID_LIFETIME);
if (ext < max)
ap->prefix_vltime = ia->prefix_vltime;
else
ap->prefix_vltime =
(uint32_t)(max - ia->acquired.tv_sec);
/* Just extend the latest matching prefix */
ap->acquired = ia->acquired;
/* If extending return the last match as
* it's the most current.
* If deprecating, deprecate any other addresses we
* may have, although this should not be needed */
if (ia->prefix_pltime)
return ap;
if (first == NULL)
first = ap;
}
}
return first;
}
void
ipv6_addtempaddrs(struct interface *ifp, const struct timespec *now)
{
struct ipv6_state *state;
struct ipv6_addr *ia;
state = IPV6_STATE(ifp);
TAILQ_FOREACH(ia, &state->addrs, next) {
if (ia->flags & IPV6_AF_TEMPORARY &&
!(ia->flags & IPV6_AF_STALE))
ipv6_addaddr(ia, now);
}
}
static void
ipv6_regentempaddr0(struct ipv6_addr *ia, struct timespec *tv)
{
struct ipv6_addr *ia1;
logdebugx("%s: regen temp addr %s", ia->iface->name, ia->saddr);
ia1 = ipv6_createtempaddr(ia, tv);
if (ia1)
ipv6_addaddr(ia1, tv);
else
logerr(__func__);
}
static void
ipv6_regentempaddr(void *arg)
{
struct timespec tv;
clock_gettime(CLOCK_MONOTONIC, &tv);
ipv6_regentempaddr0(arg, &tv);
}
void
ipv6_regentempaddrs(void *arg)
{
struct interface *ifp = arg;
struct timespec tv;
struct ipv6_state *state;
struct ipv6_addr *ia;
state = IPV6_STATE(ifp);
if (state == NULL)
return;
ipv6_regen_desync(ifp, true);
clock_gettime(CLOCK_MONOTONIC, &tv);
/* Mark addresses for regen so we don't infinite loop. */
TAILQ_FOREACH(ia, &state->addrs, next) {
if (ia->flags & IPV6_AF_TEMPORARY &&
!(ia->flags & IPV6_AF_STALE))
ia->flags |= IPV6_AF_REGEN;
else
ia->flags &= ~IPV6_AF_REGEN;
}
/* Now regen temp addrs */
TAILQ_FOREACH(ia, &state->addrs, next) {
if (ia->flags & IPV6_AF_REGEN) {
ipv6_regentempaddr0(ia, &tv);
ia->flags &= ~IPV6_AF_REGEN;
}
}
}
#endif /* IPV6_MANAGETEMPADDR */
void
ipv6_markaddrsstale(struct interface *ifp, unsigned int flags)
{
struct ipv6_state *state;
struct ipv6_addr *ia;
state = IPV6_STATE(ifp);
if (state == NULL)
return;
TAILQ_FOREACH(ia, &state->addrs, next) {
if (flags == 0 || ia->flags & flags)
ia->flags |= IPV6_AF_STALE;
}
}
void
ipv6_deletestaleaddrs(struct interface *ifp)
{
struct ipv6_state *state;
struct ipv6_addr *ia, *ia1;
state = IPV6_STATE(ifp);
if (state == NULL)
return;
TAILQ_FOREACH_SAFE(ia, &state->addrs, next, ia1) {
if (ia->flags & IPV6_AF_STALE)
ipv6_handleifa(ifp->ctx, RTM_DELADDR,
ifp->ctx->ifaces, ifp->name,
&ia->addr, ia->prefix_len, 0, getpid());
}
}
static struct rt *
inet6_makeroute(struct interface *ifp, const struct ra *rap)
{
struct rt *rt;
if ((rt = rt_new(ifp)) == NULL)
return NULL;
#ifdef HAVE_ROUTE_METRIC
rt->rt_metric = ifp->metric;
#endif
if (rap != NULL)
rt->rt_mtu = rap->mtu;
return rt;
}
static struct rt *
inet6_makeprefix(struct interface *ifp, const struct ra *rap,
const struct ipv6_addr *addr)
{
struct rt *rt;
struct in6_addr netmask;
if (addr == NULL || addr->prefix_len > 128) {
errno = EINVAL;
return NULL;
}
/* There is no point in trying to manage a /128 prefix,
* ones without a lifetime. */
if (addr->prefix_len == 128 || addr->prefix_vltime == 0)
return NULL;
/* Don't install a reject route when not creating bigger prefixes. */
if (addr->flags & IPV6_AF_NOREJECT)
return NULL;
/* This address is the delegated prefix, so add a reject route for
* it via the loopback interface. */
if (addr->flags & IPV6_AF_DELEGATEDPFX) {
struct interface *lo0;
TAILQ_FOREACH(lo0, ifp->ctx->ifaces, next) {
if (lo0->flags & IFF_LOOPBACK)
break;
}
if (lo0 == NULL)
logwarnx("cannot find a loopback interface "
"to reject via");
else
ifp = lo0;
}
if ((rt = inet6_makeroute(ifp, rap)) == NULL)
return NULL;
sa_in6_init(&rt->rt_dest, &addr->prefix);
ipv6_mask(&netmask, addr->prefix_len);
sa_in6_init(&rt->rt_netmask, &netmask);
if (addr->flags & IPV6_AF_DELEGATEDPFX) {
rt->rt_flags |= RTF_REJECT;
/* Linux does not like a gateway for a reject route. */
#ifndef __linux__
sa_in6_init(&rt->rt_gateway, &in6addr_loopback);
#endif
} else if (!(addr->flags & IPV6_AF_ONLINK))
sa_in6_init(&rt->rt_gateway, &rap->from);
else
rt->rt_gateway.sa_family = AF_UNSPEC;
sa_in6_init(&rt->rt_ifa, &addr->addr);
return rt;
}
static struct rt *
inet6_makerouter(struct ra *rap)
{
struct rt *rt;
if ((rt = inet6_makeroute(rap->iface, rap)) == NULL)
return NULL;
sa_in6_init(&rt->rt_dest, &in6addr_any);
sa_in6_init(&rt->rt_netmask, &in6addr_any);
sa_in6_init(&rt->rt_gateway, &rap->from);
return rt;
}
#define RT_IS_DEFAULT(rtp) \
(IN6_ARE_ADDR_EQUAL(&((rtp)->dest), &in6addr_any) && \
IN6_ARE_ADDR_EQUAL(&((rtp)->mask), &in6addr_any))
static int
inet6_staticroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
{
struct interface *ifp;
struct ipv6_state *state;
struct ipv6_addr *ia;
struct rt *rt;
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if ((state = IPV6_STATE(ifp)) == NULL)
continue;
TAILQ_FOREACH(ia, &state->addrs, next) {
if ((ia->flags & (IPV6_AF_ADDED | IPV6_AF_STATIC)) ==
(IPV6_AF_ADDED | IPV6_AF_STATIC))
{
rt = inet6_makeprefix(ifp, NULL, ia);
if (rt)
rt_proto_add(routes, rt);
}
}
}
return 0;
}
static int
inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
{
struct rt *rt;
struct ra *rap;
const struct ipv6_addr *addr;
if (ctx->ra_routers == NULL)
return 0;
TAILQ_FOREACH(rap, ctx->ra_routers, next) {
if (rap->expired)
continue;
TAILQ_FOREACH(addr, &rap->addrs, next) {
if (addr->prefix_vltime == 0)
continue;
rt = inet6_makeprefix(rap->iface, rap, addr);
if (rt) {
rt->rt_dflags |= RTDF_RA;
#ifdef HAVE_ROUTE_PREF
rt->rt_pref = ipv6nd_rtpref(rap);
#endif
rt_proto_add(routes, rt);
}
}
if (rap->lifetime == 0)
continue;
if (ipv6_anyglobal(rap->iface) == NULL)
continue;
rt = inet6_makerouter(rap);
if (rt == NULL)
continue;
rt->rt_dflags |= RTDF_RA;
#ifdef HAVE_ROUTE_PREF
rt->rt_pref = ipv6nd_rtpref(rap);
#endif
rt_proto_add(routes, rt);
}
return 0;
}
#ifdef DHCP6
static int
inet6_dhcproutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx,
enum DH6S dstate)
{
struct interface *ifp;
const struct dhcp6_state *d6_state;
const struct ipv6_addr *addr;
struct rt *rt;
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
d6_state = D6_CSTATE(ifp);
if (d6_state && d6_state->state == dstate) {
TAILQ_FOREACH(addr, &d6_state->addrs, next) {
rt = inet6_makeprefix(ifp, NULL, addr);
if (rt == NULL)
continue;
rt->rt_dflags |= RTDF_DHCP;
rt_proto_add(routes, rt);
}
}
}
return 0;
}
#endif
bool
inet6_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes)
{
/* Should static take priority? */
if (inet6_staticroutes(routes, ctx) == -1)
return false;
/* First add reachable routers and their prefixes */
if (inet6_raroutes(routes, ctx) == -1)
return false;
#ifdef DHCP6
/* We have no way of knowing if prefixes added by DHCP are reachable
* or not, so we have to assume they are.
* Add bound before delegated so we can prefer interfaces better. */
if (inet6_dhcproutes(routes, ctx, DH6S_BOUND) == -1)
return false;
if (inet6_dhcproutes(routes, ctx, DH6S_DELEGATED) == -1)
return false;
#endif
return true;
}