From: Roy Marples Date: Sun, 26 Apr 2020 16:22:42 +0000 (+0100) Subject: DHCP6: Implement DECLINE support for duplicated addresses X-Git-Tag: v9.1.0~110 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=62094f1bd162e2a4cbaf21409c73904c6524927c;p=thirdparty%2Fdhcpcd.git 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. --- 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 #include @@ -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); @@ -1431,6 +1451,13 @@ dhcp6_sendconfirm(void *arg) dhcp6_sendmessage(arg, dhcp6_sendconfirm); } +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; @@ -1898,6 +1942,36 @@ dhcp6_startexpire(void *arg) dhcp6_fail(ifp); } +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) { @@ -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);