summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2020-04-26 17:22:42 +0100
committerRoy Marples <roy@marples.name>2020-04-26 17:22:42 +0100
commit9c817dc5a5567ca735a713a76f5b62f8bb954ea9 (patch)
treee3fa39936e43669cbae76ca833532468f17ef29f
parentd62cfeae018b7bec80234d8868317527945d4fa1 (diff)
downloaddhcpcd-9c817dc5a5567ca735a713a76f5b62f8bb954ea9.tar.xz
DHCP6: Implement DECLINE support for duplicated addresses
This is the final piece of DHCP6 to implement! Part of this change drops the use of the IPV6_AF_DUPLICATED flag and we just use IN6_IFF_DUPLICATED now.
-rw-r--r--src/dhcp6.c167
-rw-r--r--src/dhcp6.h3
-rw-r--r--src/ipv6.c11
-rw-r--r--src/ipv6.h21
-rw-r--r--src/ipv6nd.c2
5 files changed, 141 insertions, 63 deletions
diff --git a/src/dhcp6.c b/src/dhcp6.c
index db3c7e2e..2956f683 100644
--- a/src/dhcp6.c
+++ b/src/dhcp6.c
@@ -26,8 +26,6 @@
* SUCH DAMAGE.
*/
-/* TODO: We should decline dupliate addresses detected */
-
#include <sys/stat.h>
#include <sys/utsname.h>
@@ -139,6 +137,7 @@ static const struct dhcp6_op dhcp6_ops[] = {
{ DHCP6_INFORMATION_REQ, "INFORM6" },
{ DHCP6_RELEASE, "RELEASE6" },
{ DHCP6_RECONFIGURE, "RECONFIGURE6" },
+ { DHCP6_DECLINE, "DECLINE6" },
{ 0, NULL }
};
@@ -174,6 +173,7 @@ static const char * const dhcp6_statuses[] = {
static void dhcp6_bind(struct interface *, const char *, const char *);
static void dhcp6_failinform(void *);
static void dhcp6_recvaddr(void *);
+static void dhcp6_startdecline(struct interface *);
#ifdef SMALL
#define dhcp6_hasprefixdelegation(a) (0)
@@ -181,6 +181,12 @@ static void dhcp6_recvaddr(void *);
static int dhcp6_hasprefixdelegation(struct interface *);
#endif
+#define DECLINE_IA(ia) \
+ ((ia)->addr_flags & IN6_IFF_DUPLICATED && \
+ (ia)->ia_type != 0 && (ia)->ia_type != D6_OPTION_IA_PD && \
+ !((ia)->flags & IPV6_AF_STALE) && \
+ (ia)->prefix_vltime != 0)
+
void
dhcp6_printoptions(const struct dhcpcd_ctx *ctx,
const struct dhcp_opt *opts, size_t opts_len)
@@ -672,7 +678,7 @@ dhcp6_makemessage(struct interface *ifp)
len = 0;
si = NULL;
hl = 0; /* Appease gcc */
- if (state->state != DH6S_RELEASE) {
+ if (state->state != DH6S_RELEASE && state->state != DH6S_DECLINE) {
for (l = 0, opt = ifp->ctx->dhcp6_opts;
l < ifp->ctx->dhcp6_opts_len;
l++, opt++)
@@ -750,6 +756,8 @@ dhcp6_makemessage(struct interface *ifp)
m = state->recv;
ml = state->recv_len;
/* FALLTHROUGH */
+ case DH6S_DECLINE:
+ /* FALLTHROUGH */
case DH6S_RELEASE:
/* FALLTHROUGH */
case DH6S_RENEW:
@@ -778,6 +786,8 @@ dhcp6_makemessage(struct interface *ifp)
(ap->prefix_vltime == 0 ||
state->state == DH6S_DISCOVER))
continue;
+ if (DECLINE_IA(ap) && state->state != DH6S_DECLINE)
+ continue;
if (ap->ia_type == D6_OPTION_IA_PD) {
#ifndef SMALL
len += sizeof(o) + sizeof(struct dhcp6_pd_addr);
@@ -836,6 +846,9 @@ dhcp6_makemessage(struct interface *ifp)
case DH6S_RELEASE:
type = DHCP6_RELEASE;
break;
+ case DH6S_DECLINE:
+ type = DHCP6_DECLINE;
+ break;
default:
errno = EINVAL;
return -1;
@@ -949,6 +962,8 @@ dhcp6_makemessage(struct interface *ifp)
(ap->prefix_vltime == 0 ||
state->state == DH6S_DISCOVER))
continue;
+ if (DECLINE_IA(ap) && state->state != DH6S_DECLINE)
+ continue;
if (ap->ia_type != ifia->ia_type)
continue;
if (memcmp(ap->iaid, ifia->iaid, sizeof(ap->iaid)))
@@ -1010,7 +1025,10 @@ dhcp6_makemessage(struct interface *ifp)
memcpy(o_lenp, &ia_na_len, sizeof(ia_na_len));
}
- if (state->send->type != DHCP6_RELEASE && n_options) {
+ if (state->send->type != DHCP6_RELEASE &&
+ state->send->type != DHCP6_DECLINE &&
+ n_options)
+ {
o_lenp = NEXTLEN;
o.len = 0;
COPYIN1(D6_OPTION_ORO, 0);
@@ -1072,7 +1090,9 @@ dhcp6_makemessage(struct interface *ifp)
if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
p += dhcp6_makevendor(p, ifp);
- if (state->send->type != DHCP6_RELEASE) {
+ if (state->send->type != DHCP6_RELEASE &&
+ state->send->type != DHCP6_DECLINE)
+ {
if (fqdn != FQDN_DISABLE) {
o_lenp = NEXTLEN;
COPYIN1(D6_OPTION_FQDN, 0);
@@ -1432,6 +1452,13 @@ dhcp6_sendconfirm(void *arg)
}
static void
+dhcp6_senddecline(void *arg)
+{
+
+ dhcp6_sendmessage(arg, dhcp6_senddecline);
+}
+
+static void
dhcp6_sendrelease(void *arg)
{
@@ -1496,54 +1523,60 @@ dhcp6_dadcallback(void *arg)
struct ipv6_addr *ia = arg;
struct interface *ifp;
struct dhcp6_state *state;
- int wascompleted, valid;
+ struct ipv6_addr *ia2;
+ bool completed, valid, oneduplicated;
- wascompleted = (ia->flags & IPV6_AF_DADCOMPLETED);
+ completed = (ia->flags & IPV6_AF_DADCOMPLETED);
ia->flags |= IPV6_AF_DADCOMPLETED;
- if (ia->flags & IPV6_AF_DUPLICATED) {
- /* XXX FIXME
- * We should decline the address */
+ if (ia->addr_flags & IN6_IFF_DUPLICATED)
logwarnx("%s: DAD detected %s", ia->iface->name, ia->saddr);
- }
- if (!wascompleted) {
- ifp = ia->iface;
+#ifdef ND6_ADVERTISE
+ else
+ ipv6nd_advertise(ia);
+#endif
+ if (completed)
+ return;
- state = D6_STATE(ifp);
- if (state->state == DH6S_BOUND ||
- state->state == DH6S_DELEGATED)
- {
- struct ipv6_addr *ia2;
+ ifp = ia->iface;
+ state = D6_STATE(ifp);
+ if (state->state != DH6S_BOUND && state->state != DH6S_DELEGATED)
+ return;
#ifdef SMALL
- valid = true;
+ valid = true;
#else
- valid = (ia->delegating_prefix == NULL);
-#endif
- TAILQ_FOREACH(ia2, &state->addrs, next) {
- if (ia2->flags & IPV6_AF_ADDED &&
- !(ia2->flags & IPV6_AF_DADCOMPLETED))
- {
- wascompleted = 1;
- break;
- }
- }
- if (!wascompleted) {
- logdebugx("%s: DHCPv6 DAD completed",
- ifp->name);
- script_runreason(ifp,
-#ifndef SMALL
- ia->delegating_prefix ? "DELEGATED6" :
-#endif
- state->reason);
- if (valid)
- dhcpcd_daemonise(ifp->ctx);
- }
-#ifdef ND6_ADVERTISE
- ipv6nd_advertise(ia);
+ valid = (ia->delegating_prefix == NULL);
#endif
+ completed = true;
+ oneduplicated = false;
+ TAILQ_FOREACH(ia2, &state->addrs, next) {
+ if (ia2->flags & IPV6_AF_ADDED &&
+ !(ia2->flags & IPV6_AF_DADCOMPLETED))
+ {
+ completed = false;
+ break;
}
+ if (DECLINE_IA(ia))
+ oneduplicated = true;
}
+ if (!completed)
+ return;
+
+ logdebugx("%s: DHCPv6 DAD completed", ifp->name);
+
+ if (oneduplicated && state->state == DH6S_BOUND) {
+ dhcp6_startdecline(ifp);
+ return;
+ }
+
+ script_runreason(ifp,
+#ifndef SMALL
+ ia->delegating_prefix ? "DELEGATED6" :
+#endif
+ state->reason);
+ if (valid)
+ dhcpcd_daemonise(ifp->ctx);
}
static void
@@ -1682,7 +1715,7 @@ dhcp6_leaseextend(struct interface *ifp)
}
static void
-dhcp6_fail(struct interface* ifp)
+dhcp6_fail(struct interface *ifp)
{
struct dhcp6_state *state = D6_STATE(ifp);
@@ -1867,8 +1900,18 @@ static void
dhcp6_startconfirm(struct interface *ifp)
{
struct dhcp6_state *state;
+ struct ipv6_addr *ia;
state = D6_STATE(ifp);
+
+ TAILQ_FOREACH(ia, &state->addrs, next) {
+ if (!DECLINE_IA(ia))
+ continue;
+ logerrx("%s: prior DHCPv6 has a duplicated address", ifp->name);
+ dhcp6_startdecline(ifp);
+ return;
+ }
+
state->state = DH6S_CONFIRM;
state->RTC = 0;
state->IMD = CNF_MAX_DELAY;
@@ -1877,6 +1920,7 @@ dhcp6_startconfirm(struct interface *ifp)
state->MRC = CNF_MAX_RC;
loginfox("%s: confirming prior DHCPv6 lease", ifp->name);
+
if (dhcp6_makemessage(ifp) == -1) {
logerr("%s: %s", __func__, ifp->name);
return;
@@ -1899,6 +1943,36 @@ dhcp6_startexpire(void *arg)
}
static void
+dhcp6_faildecline(void *arg)
+{
+ struct interface *ifp = arg;
+
+ logerrx("%s: failed to decline duplicated DHCPv6 addresses", ifp->name);
+ dhcp6_fail(ifp);
+}
+
+static void
+dhcp6_startdecline(struct interface *ifp)
+{
+ struct dhcp6_state *state;
+
+ state = D6_STATE(ifp);
+ loginfox("%s: declining failed DHCPv6 addresses", ifp->name);
+ state->state = DH6S_DECLINE;
+ state->RTC = 0;
+ state->IMD = 0;
+ state->IRT = DEC_TIMEOUT;
+ state->MRT = 0;
+ state->MRC = DEC_MAX_RC;
+ state->MRCcallback = dhcp6_faildecline;
+
+ if (dhcp6_makemessage(ifp) == -1)
+ logerr("%s: %s", __func__, ifp->name);
+ else
+ dhcp6_senddecline(ifp);
+}
+
+static void
dhcp6_finishrelease(void *arg)
{
struct interface *ifp;
@@ -3350,6 +3424,13 @@ dhcp6_recvif(struct interface *ifp, const char *sfrom,
if (state->state == DH6S_DISCOVER)
state->state = DH6S_REQUEST;
break;
+ case DH6S_DECLINE:
+ /* This isnt really a failure, but an
+ * acknowledgement of one. */
+ loginfox("%s: %s acknowledged DECLINE6",
+ ifp->name, sfrom);
+ dhcp6_fail(ifp);
+ return;
default:
valid_op = false;
break;
diff --git a/src/dhcp6.h b/src/dhcp6.h
index 73341cde..d0998375 100644
--- a/src/dhcp6.h
+++ b/src/dhcp6.h
@@ -168,9 +168,10 @@ enum DH6S {
DH6S_INFORMED,
DH6S_RENEW_REQUESTED,
DH6S_PROBE,
+ DH6S_DECLINE,
DH6S_DELEGATED,
DH6S_RELEASE,
- DH6S_RELEASED
+ DH6S_RELEASED,
};
struct dhcp6_state {
diff --git a/src/ipv6.c b/src/ipv6.c
index f6734e93..c039e7e6 100644
--- a/src/ipv6.c
+++ b/src/ipv6.c
@@ -1635,7 +1635,7 @@ ipv6_staticdadcallback(void *arg)
wascompleted = (ia->flags & IPV6_AF_DADCOMPLETED);
ia->flags |= IPV6_AF_DADCOMPLETED;
- if (ia->flags & IPV6_AF_DUPLICATED)
+ if (ia->addr_flags & IN6_IFF_DUPLICATED)
logwarnx("%s: DAD detected %s", ia->iface->name,
ia->saddr);
else if (!wascompleted) {
@@ -1838,16 +1838,13 @@ ipv6_handleifa_addrs(int cmd,
}
break;
case RTM_NEWADDR:
+ ia->addr_flags = addr->addr_flags;
/* Safety - ignore tentative announcements */
- if (addr->addr_flags &
+ if (ia->addr_flags &
(IN6_IFF_DETACHED | IN6_IFF_TENTATIVE))
break;
if ((ia->flags & IPV6_AF_DADCOMPLETED) == 0) {
found++;
- if (addr->addr_flags & IN6_IFF_DUPLICATED)
- ia->flags |= IPV6_AF_DUPLICATED;
- else
- ia->flags &= ~IPV6_AF_DUPLICATED;
if (ia->dadcallback)
ia->dadcallback(ia);
/* We need to set this here in-case the
@@ -1890,7 +1887,7 @@ ipv6_tempdadcallback(void *arg)
{
struct ipv6_addr *ia = arg;
- if (ia->flags & IPV6_AF_DUPLICATED) {
+ if (ia->addr_flags & IN6_IFF_DUPLICATED) {
struct ipv6_addr *ia1;
struct timespec tv;
diff --git a/src/ipv6.h b/src/ipv6.h
index d311b374..005735d7 100644
--- a/src/ipv6.h
+++ b/src/ipv6.h
@@ -216,18 +216,17 @@ struct ipv6_addr {
#define IPV6_AF_STALE (1U << 2)
#define IPV6_AF_ADDED (1U << 3)
#define IPV6_AF_AUTOCONF (1U << 4)
-#define IPV6_AF_DUPLICATED (1U << 5)
-#define IPV6_AF_DADCOMPLETED (1U << 6)
-#define IPV6_AF_DELEGATED (1U << 7)
-#define IPV6_AF_DELEGATEDPFX (1U << 8)
-#define IPV6_AF_NOREJECT (1U << 9)
-#define IPV6_AF_REQUEST (1U << 10)
-#define IPV6_AF_STATIC (1U << 11)
-#define IPV6_AF_DELEGATEDLOG (1U << 12)
-#define IPV6_AF_RAPFX (1U << 13)
-#define IPV6_AF_EXTENDED (1U << 14)
+#define IPV6_AF_DADCOMPLETED (1U << 5)
+#define IPV6_AF_DELEGATED (1U << 6)
+#define IPV6_AF_DELEGATEDPFX (1U << 7)
+#define IPV6_AF_NOREJECT (1U << 8)
+#define IPV6_AF_REQUEST (1U << 9)
+#define IPV6_AF_STATIC (1U << 10)
+#define IPV6_AF_DELEGATEDLOG (1U << 11)
+#define IPV6_AF_RAPFX (1U << 12)
+#define IPV6_AF_EXTENDED (1U << 13)
#ifdef IPV6_MANAGETEMPADDR
-#define IPV6_AF_TEMPORARY (1U << 15)
+#define IPV6_AF_TEMPORARY (1U << 14)
#endif
struct ll_callback {
diff --git a/src/ipv6nd.c b/src/ipv6nd.c
index f1f83d5f..faf31561 100644
--- a/src/ipv6nd.c
+++ b/src/ipv6nd.c
@@ -927,7 +927,7 @@ ipv6nd_dadcallback(void *arg)
ifp = ia->iface;
wascompleted = (ia->flags & IPV6_AF_DADCOMPLETED);
ia->flags |= IPV6_AF_DADCOMPLETED;
- if (ia->flags & IPV6_AF_DUPLICATED) {
+ if (ia->addr_flags & IN6_IFF_DUPLICATED) {
ia->dadcounter++;
logwarnx("%s: DAD detected %s", ifp->name, ia->saddr);