BSD systems where this has been fixed are:
NetBSD-5.0
+Some BSD systems announce IPv6 addresses to userland when the address has
+been added, not when it's actually ready to use. This is important because
+no kernel allows to send from the unspecified address which means userland
+cannot be RFC conformation when creating DAD messages so we need to rely on
+the kernel implementation.
+You can find the discussion here:
+ http://mail-index.netbsd.org/tech-net/2013/03/15/msg004019.html
+BSD systems where this will be fixed are:
+ NetBSD-7.0
+
We try and detect how dhcpcd should interact with system services during the
configure stage. If we cannot auto-detect how do to this, or it is wrong then
you can change this by passing shell commands to --service-exists,
dhcpcd-5.0 is only fully command line compatible with dhcpcd-4.0
For compatibility with older versions, use dhcpcd-4.0
-dhcpcd no longer sends a default ClientID for ethernet interfaces.
-This is so we can re-use the address the kernel DHCP client found.
-To retain the old behaviour of sending a default ClientID based on the
-hardware address for interface, simply add the keyword clientid to dhcpcd.conf.
-
ChangeLog
---------
#define CONFIG_H
#define PACKAGE "dhcpcd"
-#define VERSION "5.99.5"
+#define VERSION "5.99.6"
#ifndef CONFIG
# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"
if (k) {
syslog(LOG_INFO, "%s: adding delegated prefixes", ifp->name);
state = D6_STATE(ifp);
+ state->state = DH6S_DELEGATED;
ipv6ns_probeaddrs(&state->addrs);
ipv6_buildroutes();
}
struct dhcp6_message *r;
struct dhcp6_state *state;
const struct dhcp6_option *o;
- const char *reason;
const struct dhcp_opt *opt;
const struct if_options *ifo;
const struct ipv6_addr *ap;
r->xid[2] != state->send->xid[2])
{
syslog(LOG_ERR,
- "%s: wrong xid 0x%02x%02x%02x (expecting 0x%02x%02x%02x) from %s",
+ "%s: wrong xid 0x%02x%02x%02x"
+ " (expecting 0x%02x%02x%02x) from %s",
ifp->name,
r->xid[0], r->xid[1], r->xid[2],
state->send->xid[0], state->send->xid[1],
recv:
syslog(LOG_INFO, "%s: %s received from %s", ifp->name, op, sfrom);
- reason = NULL;
+ state->reason = NULL;
eloop_timeout_delete(NULL, ifp);
switch(state->state) {
case DH6S_INFORM:
state->rebind = 0;
state->expire = ~0U;
state->lowpl = ~0U;
- reason = "INFORM6";
+ state->reason = "INFORM6";
break;
case DH6S_REQUEST:
- if (reason == NULL)
- reason = "BOUND6";
+ if (state->reason == NULL)
+ state->reason = "BOUND6";
/* FALLTHROUGH */
case DH6S_RENEW:
- if (reason == NULL)
- reason = "RENEW6";
+ if (state->reason == NULL)
+ state->reason = "RENEW6";
/* FALLTHROUGH */
case DH6S_REBIND:
- if (reason == NULL)
- reason = "REBIND6";
+ if (state->reason == NULL)
+ state->reason = "REBIND6";
case DH6S_CONFIRM:
- if (reason == NULL)
- reason = "REBOOT6";
+ if (state->reason == NULL)
+ state->reason = "REBOOT6";
if (state->renew == 0) {
if (state->expire == ~0U)
state->renew = ~0U;
}
break;
default:
- reason = "UNKNOWN6";
+ state->reason = "UNKNOWN6";
break;
}
state->recv_len = 0;
}
- if (!(options & DHCPCD_TEST)) {
+ if (options & DHCPCD_TEST)
+ script_runreason(ifp, "TEST");
+ else {
+ if (state->state == DH6S_INFORM)
+ script_runreason(ifp, state->reason);
state->state = DH6S_BOUND;
if (state->renew)
eloop_timeout_add_sec(state->renew,
ifp->name, state->renew, state->rebind);
ipv6_buildroutes();
dhcp6_writelease(ifp);
+
+ len = 1;
+ /* If all addresses have completed DAD run the script */
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ if (ap->dadcompleted == 0) {
+ len = 0;
+ break;
+ }
+ }
+ if (len) {
+ script_runreason(ifp, state->reason);
+ daemonise();
+ } else
+ syslog(LOG_DEBUG, "%s: waiting for RA DAD to complete",
+ ifp->name);
}
- script_runreason(ifp, options & DHCPCD_TEST ? "TEST" : reason);
if (options & DHCPCD_TEST ||
(ifp->options->options & DHCPCD_INFORM &&
!(options & DHCPCD_MASTER)))
#endif
exit(EXIT_SUCCESS);
}
- daemonise();
}
static int
return -1;
TAILQ_INIT(&state->addrs);
- if (dhcp6_find_delegates(ifp)) {
- state->state = DH6S_DELEGATED;
+ if (dhcp6_find_delegates(ifp))
return 0;
- }
syslog(LOG_INFO, "%s: %s", ifp->name,
manage ? "soliciting DHCPv6 address" :
dhcp6_freedrop(ifp, 0, NULL);
}
+void
+dhcp6_handleifa(int cmd, const char *ifname, const struct in6_addr *addr)
+{
+ struct interface *ifp;
+ struct dhcp6_state *state;
+ int found;
+
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ state = D6_STATE(ifp);
+ if (state == NULL || strcmp(ifp->name, ifname))
+ continue;
+ found = ipv6_handleifa_addrs(cmd, &state->addrs, addr);
+ if (found && state->state == DH6S_BOUND) {
+ syslog(LOG_DEBUG, "%s: DHCPv6 DAD completed",
+ ifp->name);
+ script_runreason(ifp, state->reason);
+ daemonise();
+ }
+ }
+}
+
ssize_t
dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
const struct dhcp6_message *m, ssize_t mlen)
-/*
+/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
* All rights reserved
struct ipv6_addrhead addrs;
uint32_t lowpl;
char leasefile[PATH_MAX];
+ const char *reason;
};
#define D6_STATE(ifp) \
ssize_t dhcp6_env(char **, const char *, const struct interface *,
const struct dhcp6_message *, ssize_t);
void dhcp6_free(struct interface *);
+void dhcp6_handleifa(int, const char *, const struct in6_addr *addr);
void dhcp6_drop(struct interface *, const char *);
#else
#define dhcp6_printoptions()
configure_interface(ifp, argc, argv);
ifo = ifp->options;
- if (ifo->options & DHCPCD_LINK && linkfd == -1) {
+ /* RTM_NEWADDR goes through the link socket as well which we
+ * need for IPv6 DAD, so we check for DHCPCD_LINK in handle_carrier
+ * instead */
+ if (linkfd == -1) {
linkfd = open_link_socket();
if (linkfd == -1) {
syslog(LOG_ERR, "open_link_socket: %m");
#define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len))
#endif
-/* FIXME: Why do we need to check for sa_family 255 */
#define COPYOUT(sin, sa) \
sin.s_addr = ((sa) != NULL) ? \
(((struct sockaddr_in *)(void *)sa)->sin_addr).s_addr : 0
+#define COPYOUT6(sin, sa) \
+ sin.s6_addr = ((sa) != NULL) ? \
+ (((struct sockaddr_in6 *)(void *)sa)->sin6_addr).s6_addr : 0
+
static int r_fd = -1;
static char *link_buf;
static ssize_t link_buflen;
struct sockaddr_dl sdl;
unsigned char *hwaddr;
#endif
+#ifdef INET6
+ struct in6_addr ia6;
+ struct sockaddr_in6 *sin6;
+#endif
for (;;) {
if (ioctl(fd, FIONREAD, &len) == -1)
ipv4_handleifa(rtm->rtm_type, ifname,
&rt.dest, &rt.net, &rt.gate);
break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6*)
+ rti_info[RTAX_IFA];
+ memcpy(ia6.s6_addr,
+ sin6->sin6_addr.s6_addr,
+ sizeof(ia6.s6_addr));
+ ipv6_handleifa(rtm->rtm_type, ifname,
+ &ia6);
+ break;
#endif
}
break;
#include <sys/types.h>
#include <sys/socket.h>
+#include <net/route.h>
#include <netinet/in.h>
+#ifdef __linux__
+# include <asm/types.h> /* for systems with broken headers */
+# include <linux/rtnetlink.h>
+#endif
+
#include <errno.h>
#include <ifaddrs.h>
#include <stdlib.h>
return i;
}
+void
+ipv6_handleifa(int cmd, const char *ifname, const struct in6_addr *addr)
+{
+
+ ipv6rs_handleifa(cmd, ifname, addr);
+ dhcp6_handleifa(cmd, ifname, addr);
+}
+
+int
+ipv6_handleifa_addrs(int cmd,
+ struct ipv6_addrhead *addrs, const struct in6_addr *addr)
+{
+ struct ipv6_addr *ap, *apn;
+ uint8_t found, alldadcompleted;
+
+ alldadcompleted = 1;
+ found = 0;
+ TAILQ_FOREACH_SAFE(ap, addrs, next, apn) {
+ if (memcmp(addr->s6_addr, ap->addr.s6_addr,
+ sizeof(addr->s6_addr)))
+ {
+ if (ap->dadcompleted == 0)
+ alldadcompleted = 0;
+ continue;
+ }
+ switch (cmd) {
+ case RTM_DELADDR:
+ syslog(LOG_INFO, "%s: deleted address %s",
+ ap->iface->name, ap->saddr);
+ TAILQ_REMOVE(addrs, ap, next);
+ free(ap);
+ break;
+ case RTM_NEWADDR:
+ if (!ap->dadcompleted) {
+ found++;
+ ap->dadcompleted = 1;
+ }
+ }
+ }
+
+ return alldadcompleted ? found : 0;
+}
+
static struct rt6 *
find_route6(struct rt6head *rts, const struct rt6 *r)
{
int ipv6_prefixlen(const struct in6_addr *);
int ipv6_addaddr(struct ipv6_addr *);
ssize_t ipv6_addaddrs(struct ipv6_addrhead *);
+void ipv6_handleifa(int, const char *, const struct in6_addr *);
+int ipv6_handleifa_addrs(int, struct ipv6_addrhead *, const struct in6_addr *);
int ipv6_removesubnet(const struct interface *, struct ipv6_addr *);
void ipv6_buildroutes(void);
void ipv6_drop(struct interface *);
#include "ipv6ns.h"
#include "script.h"
-#define MIN_RANDOM_FACTOR 500 /* milliseconds */
-#define MAX_RANDOM_FACTOR 1500 /* milliseconds */
+#define MIN_RANDOM_FACTOR 500 /* millisecs */
+#define MAX_RANDOM_FACTOR 1500 /* millisecs */
#define MIN_RANDOM_FACTOR_U MIN_RANDOM_FACTOR * 1000 /* usecs */
#define MAX_RANDOM_FACTOR_U MAX_RANDOM_FACTOR * 1000 /* usecs */
#endif
}
+static void
+ipv6rs_scriptrun(const struct ra *rap)
+{
+ int alldadcomplete, hasdns;
+ const struct ipv6_addr *ap;
+ const struct ra_opt *rao;
+
+ /* If all addresses have completed DAD run the script */
+ alldadcomplete = 1;
+ TAILQ_FOREACH(ap, &rap->addrs, next) {
+ if (ap->dadcompleted == 0)
+ return;
+ }
+
+ /* If we don't require RDNSS then set hasdns = 1 so we fork */
+ if (!(rap->iface->options->options & DHCPCD_IPV6RA_REQRDNSS))
+ hasdns = 1;
+ else {
+ hasdns = 0;
+ TAILQ_FOREACH(rao, &rap->options, next) {
+ if (rao->type == ND_OPT_RDNSS &&
+ rao->option &&
+ timerisset(&rao->expire))
+ {
+ hasdns = 1;
+ break;
+ }
+ }
+ }
+
+ script_runreason(rap->iface, "ROUTERADVERT");
+ if (hasdns)
+ daemonise();
+#if 0
+ else if (options & DHCPCD_DAEMONISE &&
+ !(options & DHCPCD_DAEMONISED) && new_data)
+ syslog(LOG_WARNING,
+ "%s: did not fork due to an absent"
+ " RDNSS option in the RA",
+ ifp->name);
+}
+#endif
+}
+
/* ARGSUSED */
static void
ipv6rs_handledata(__unused void *arg)
struct ipv6_addr *ap;
char *opt, *tmp;
struct timeval expire;
- uint8_t has_dns, new_rap, new_data;
+ uint8_t new_rap, new_data;
len = recvmsg(sock, &rcvhdr, 0);
if (len == -1) {
p = ((uint8_t *)icp) + sizeof(struct nd_router_advert);
olen = 0;
lifetime = ~0U;
- has_dns = 0;
for (olen = 0; len > 0; p += olen, len -= olen) {
if ((size_t)len < sizeof(struct nd_opt_hdr)) {
syslog(LOG_ERR, "%s: Short option", ifp->name);
l -= (m + 1);
tmp += m;
*tmp++ = ' ';
- if (lifetime > 0)
- has_dns = 1;
}
}
if (tmp != opt)
if (options & DHCPCD_IPV6RA_OWN)
ipv6ns_probeaddrs(&rap->addrs);
ipv6_buildroutes();
+
/* We will get run by the expire function */
if (rap->lifetime)
- script_runreason(ifp, "ROUTERADVERT");
-
- /* If we don't require RDNSS then set has_dns = 1 so we fork */
- if (!(ifp->options->options & DHCPCD_IPV6RA_REQRDNSS))
- has_dns = 1;
+ ipv6rs_scriptrun(rap);
eloop_timeout_delete(NULL, ifp);
eloop_timeout_delete(NULL, rap); /* reachable timer */
- if (has_dns)
- daemonise();
- else if (options & DHCPCD_DAEMONISE &&
- !(options & DHCPCD_DAEMONISED) && new_data)
- syslog(LOG_WARNING,
- "%s: did not fork due to an absent RDNSS option in the RA",
- ifp->name);
/* If we're owning the RA then we need to try and ensure the
* router is actually reachable */
return NULL;
}
+void
+ipv6rs_handleifa(int cmd, const char *ifname, const struct in6_addr *addr)
+{
+ struct ra *rap;
+ int found;
+
+ TAILQ_FOREACH(rap, &ipv6_routers, next) {
+ if (strcmp(rap->iface->name, ifname))
+ continue;
+ found = ipv6_handleifa_addrs(cmd, &rap->addrs, addr);
+ if (found && rap->lifetime) {
+ syslog(LOG_DEBUG,
+ "%s: IPv6 Router Advertisement DAD completed",
+ rap->iface->name);
+ ipv6rs_scriptrun(rap);
+ }
+ }
+}
+
void
ipv6rs_expire(void *arg)
{
ssize_t ipv6rs_free(struct interface *);
void ipv6rs_expire(void *arg);
int ipv6rs_has_ra(const struct interface *);
+void ipv6rs_handleifa(int, const char *, const struct in6_addr *);
void ipv6rs_drop(struct interface *);
#else
#define ipv6rs_start(a) {}