Mercurial > hg > dhcpcd
changeset 4511:a4f492ca06a6 draft
Merge branch 'master' into rbtree
| author | Roy Marples <roy@marples.name> |
|---|---|
| date | Sat, 04 May 2019 11:05:17 +0100 |
| parents | 6fd556af42a4 (current diff) 6bf601e44f37 (diff) |
| children | 011f49d4e4b8 |
| files | configure src/dhcp.c src/dhcp6.c src/dhcpcd.c src/dhcpcd.conf.5.in src/dhcpcd.h src/if-bsd.c src/if-linux.c src/if-options.c src/if-sun.c src/if.c src/if.h src/ipv4.c src/ipv4.h src/ipv4ll.c src/ipv4ll.h src/ipv6.c src/ipv6.h src/ipv6nd.c |
| diffstat | 28 files changed, 1217 insertions(+), 867 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/consttime_memequal.h Sat May 04 11:05:17 2019 +0100 @@ -0,0 +1,28 @@ +/* + * Written by Matthias Drochner <drochner@NetBSD.org>. + * Public domain. + */ + +#ifndef CONSTTIME_MEMEQUAL_H +#define CONSTTIME_MEMEQUAL_H +inline static int +consttime_memequal(const void *b1, const void *b2, size_t len) +{ + const unsigned char *c1 = b1, *c2 = b2; + unsigned int res = 0; + + while (len--) + res |= *c1++ ^ *c2++; + + /* + * Map 0 to 1 and [1, 256) to 0 using only constant-time + * arithmetic. + * + * This is not simply `!res' because although many CPUs support + * branchless conditional moves and many compilers will take + * advantage of them, certain compilers generate branches on + * certain CPUs for `!res'. + */ + return (1 & ((res - 1) >> 8)); +} +#endif /* CONSTTIME_MEMEQUAL_H */
--- a/configure Wed Apr 17 22:18:39 2019 +0000 +++ b/configure Sat May 04 11:05:17 2019 +0100 @@ -14,6 +14,7 @@ ARC4RANDOM= CLOSEFROM= RBTREE= +CONSTTIME_MEMEQUAL= STRLCPY= UDEV= OS= @@ -848,6 +849,27 @@ echo "#include \"compat/strtoi.h\"" >>$CONFIG_H fi +if [ -z "$CONSTTIME_MEMEQUAL" ]; then + printf "Testing for consttime_memequal ... " + cat <<EOF >_consttime_memequal.c +#include <string.h> +int main(void) { + return consttime_memequal("deadbeef", "deadbeef", 8); +} +EOF + if $XCC _consttime_memequal.c -o _consttime_memequal 2>&3; then + CONSTTIME_MEMEQUAL=yes + else + CONSTTIME_MEMEQUAL=no + fi + echo "$CONSTTIME_MEMEQUAL" + rm -f _consttime_memequal.c _consttime_memequal +fi +if [ "$CONSTTIME_MEMEQUAL" = no ]; then + echo "#include \"compat/consttime_memequal.h\"" \ + >>$CONFIG_H +fi + if [ -z "$DPRINTF" ]; then printf "Testing for dprintf ... " cat <<EOF >_dprintf.c
--- a/src/arp.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/arp.c Sat May 04 11:05:17 2019 +0100 @@ -62,8 +62,9 @@ /* Assert the correct structure size for on wire */ __CTASSERT(sizeof(struct arphdr) == 8); -ssize_t -arp_request(const struct interface *ifp, in_addr_t sip, in_addr_t tip) +static ssize_t +arp_request(const struct interface *ifp, + const struct in_addr *sip, const struct in_addr *tip) { uint8_t arp_buffer[ARP_LEN]; struct arphdr ar; @@ -74,7 +75,7 @@ ar.ar_hrd = htons(ifp->family); ar.ar_pro = htons(ETHERTYPE_IP); ar.ar_hln = ifp->hwlen; - ar.ar_pln = sizeof(sip); + ar.ar_pln = sizeof(tip->s_addr); ar.ar_op = htons(ARPOP_REQUEST); p = arp_buffer; @@ -93,9 +94,12 @@ APPEND(&ar, sizeof(ar)); APPEND(ifp->hwaddr, ifp->hwlen); - APPEND(&sip, sizeof(sip)); + if (sip != NULL) + APPEND(&sip->s_addr, sizeof(sip->s_addr)); + else + ZERO(sizeof(tip->s_addr)); ZERO(ifp->hwlen); - APPEND(&tip, sizeof(tip)); + APPEND(&tip->s_addr, sizeof(tip->s_addr)); state = ARP_CSTATE(ifp); return bpf_send(ifp, state->bpf_fd, ETHERTYPE_ARP, arp_buffer, len); @@ -106,6 +110,77 @@ } static void +arp_report_conflicted(const struct arp_state *astate, + const struct arp_msg *amsg) +{ + char buf[HWADDR_LEN * 3]; + + if (amsg == NULL) { + logerrx("%s: DAD detected %s", + astate->iface->name, inet_ntoa(astate->addr)); + return; + } + + logerrx("%s: hardware address %s claims %s", + astate->iface->name, + hwaddr_ntoa(amsg->sha, astate->iface->hwlen, buf, sizeof(buf)), + inet_ntoa(astate->addr)); +} + + +static void +arp_found(struct arp_state *astate, const struct arp_msg *amsg) +{ + struct interface *ifp; + struct ivp4_addr *ia; +#ifndef KERNEL_RFC5227 + struct timespec now, defend; +#endif + + arp_report_conflicted(astate, amsg); + ifp = astate->iface; + +#pragma GCC diagnostic push /* GCC is clearly wrong about this warning. */ +#pragma GCC diagnostic ignored "-Wincompatible-pointer-types" + /* If we haven't added the address we're doing a probe. */ + ia = ipv4_iffindaddr(ifp, &astate->addr, NULL); +#pragma GCC diagnostic pop + if (ia == NULL) { + if (astate->found_cb != NULL) + astate->found_cb(astate, amsg); + return; + } + +#ifndef KERNEL_RFC5227 + /* RFC 3927 Section 2.5 says a defence should + * broadcast an ARP announcement. + * Because the kernel will also unicast a reply to the + * hardware address which requested the IP address + * the other IPv4LL client will receieve two ARP + * messages. + * If another conflict happens within DEFEND_INTERVAL + * then we must drop our address and negotiate a new one. */ + defend.tv_sec = astate->defend.tv_sec + DEFEND_INTERVAL; + defend.tv_nsec = astate->defend.tv_nsec; + clock_gettime(CLOCK_MONOTONIC, &now); + if (timespeccmp(&defend, &now, >)) + logwarnx("%s: %d second defence failed for %s", + ifp->name, DEFEND_INTERVAL, inet_ntoa(astate->addr)); + else if (arp_request(ifp, &astate->addr, &astate->addr) == -1) + logerr(__func__); + else { + logdebugx("%s: defended address %s", + ifp->name, inet_ntoa(astate->addr)); + astate->defend = now; + return; + } +#endif + + if (astate->defend_failed_cb != NULL) + astate->defend_failed_cb(astate); +} + +static void arp_packet(struct interface *ifp, uint8_t *data, size_t len) { const struct interface *ifn; @@ -164,14 +239,15 @@ memcpy(&arm.tha, hw_t, ar.ar_hln); memcpy(&arm.tip.s_addr, hw_t + ar.ar_hln, ar.ar_pln); - /* Run the conflicts */ + /* Match the ARP probe to our states. + * Ignore Unicast Poll, RFC1122. */ state = ARP_CSTATE(ifp); TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, astaten) { - if (arm.sip.s_addr != astate->addr.s_addr && - arm.tip.s_addr != astate->addr.s_addr) - continue; - if (astate->conflicted_cb) - astate->conflicted_cb(astate, &arm); + if (IN_ARE_ADDR_EQUAL(&arm.sip, &astate->addr) || + (IN_IS_ADDR_UNSPECIFIED(&arm.sip) && + IN_ARE_ADDR_EQUAL(&arm.tip, &astate->addr) && + state->bpf_flags & BPF_BCAST)) + arp_found(astate, &arm); } } @@ -243,7 +319,7 @@ } } -int +static int arp_open(struct interface *ifp) { struct iarp_state *state; @@ -265,7 +341,8 @@ { struct arp_state *astate = arg; - astate->probed_cb(astate); + timespecclear(&astate->defend); + astate->not_found_cb(astate); } static void @@ -290,7 +367,7 @@ ifp->name, inet_ntoa(astate->addr), astate->probes ? astate->probes : PROBE_NUM, PROBE_NUM, timespec_to_double(&tv)); - if (arp_request(ifp, 0, astate->addr.s_addr) == -1) + if (arp_request(ifp, NULL, &astate->addr) == -1) logerr(__func__); } @@ -314,6 +391,23 @@ } #endif /* ARP */ +static struct arp_state * +arp_find(struct interface *ifp, const struct in_addr *addr) +{ + struct iarp_state *state; + struct arp_state *astate; + + if ((state = ARP_STATE(ifp)) == NULL) + goto out; + TAILQ_FOREACH(astate, &state->arp_states, next) { + if (astate->addr.s_addr == addr->s_addr && astate->iface == ifp) + return astate; + } +out: + errno = ESRCH; + return NULL; +} + static void arp_announced(void *arg) { @@ -342,7 +436,7 @@ logdebugx("%s: ARP announcing %s (%d of %d)", ifp->name, inet_ntoa(astate->addr), astate->claims, ANNOUNCE_NUM); - if (arp_request(ifp, astate->addr.s_addr, astate->addr.s_addr) == -1) + if (arp_request(ifp, &astate->addr, &astate->addr) == -1) logerr(__func__); eloop_timeout_add_sec(ifp->ctx->eloop, ANNOUNCE_WAIT, astate->claims < ANNOUNCE_NUM ? arp_announce1 : arp_announced, @@ -401,11 +495,25 @@ } void +arp_ifannounceaddr(struct interface *ifp, const struct in_addr *ia) +{ + struct arp_state *astate; + + astate = arp_find(ifp, ia); + if (astate == NULL) { + astate = arp_new(ifp, ia); + if (astate == NULL) + return; + astate->announced_cb = arp_free; + } + arp_announce(astate); +} + +void arp_announceaddr(struct dhcpcd_ctx *ctx, const struct in_addr *ia) { struct interface *ifp; struct ipv4_addr *iaf; - struct arp_state *astate; TAILQ_FOREACH(ifp, ctx->ifaces, next) { iaf = ipv4_iffindaddr(ifp, ia, NULL); @@ -419,54 +527,7 @@ if (ifp == NULL) return; - astate = arp_find(ifp, ia); - if (astate != NULL) - arp_announce(astate); -} - -void -arp_ifannounceaddr(struct interface *ifp, const struct in_addr *ia) -{ - struct arp_state *astate; - - astate = arp_new(ifp, ia); - if (astate != NULL) - arp_announce(astate); -} - -void -arp_report_conflicted(const struct arp_state *astate, - const struct arp_msg *amsg) -{ - - if (amsg != NULL) { - char buf[HWADDR_LEN * 3]; - - logerrx("%s: hardware address %s claims %s", - astate->iface->name, - hwaddr_ntoa(amsg->sha, astate->iface->hwlen, - buf, sizeof(buf)), - inet_ntoa(astate->failed)); - } else - logerrx("%s: DAD detected %s", - astate->iface->name, inet_ntoa(astate->failed)); -} - -struct arp_state * -arp_find(struct interface *ifp, const struct in_addr *addr) -{ - struct iarp_state *state; - struct arp_state *astate; - - if ((state = ARP_STATE(ifp)) == NULL) - goto out; - TAILQ_FOREACH(astate, &state->arp_states, next) { - if (astate->addr.s_addr == addr->s_addr && astate->iface == ifp) - return astate; - } -out: - errno = ESRCH; - return NULL; + arp_ifannounceaddr(ifp, ia); } struct arp_state * @@ -532,61 +593,28 @@ arp_tryfree(ifp); } -static void -arp_free_but1(struct interface *ifp, struct arp_state *astate) +void +arp_freeaddr(struct interface *ifp, const struct in_addr *ia) { - struct iarp_state *state; - - if ((state = ARP_STATE(ifp)) != NULL) { - struct arp_state *p, *n; + struct arp_state *astate; - TAILQ_FOREACH_SAFE(p, &state->arp_states, next, n) { - if (p != astate) - arp_free(p); - } - } -} - -void -arp_free_but(struct arp_state *astate) -{ - - arp_free_but1(astate->iface, astate); + astate = arp_find(ifp, ia); + arp_free(astate); } void arp_drop(struct interface *ifp) { - - arp_free_but1(ifp, NULL); - arp_close(ifp); -} + struct iarp_state *state; + struct arp_state *astate; -void -arp_handleifa(int cmd, struct ipv4_addr *addr) -{ - struct iarp_state *state; - struct arp_state *astate, *asn; - - state = ARP_STATE(addr->iface); + state = ARP_STATE(ifp); if (state == NULL) return; - TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, asn) { - if (astate->addr.s_addr != addr->addr.s_addr) - continue; - if (cmd == RTM_DELADDR) - arp_free(astate); -#ifdef IN_IFF_DUPLICATED - if (cmd != RTM_NEWADDR) - continue; - if (addr->addr_flags & IN_IFF_DUPLICATED) { - if (astate->conflicted_cb) - astate->conflicted_cb(astate, NULL); - } else if (!(addr->addr_flags & IN_IFF_NOTUSEABLE)) { - if (astate->probed_cb) - astate->probed_cb(astate); - } -#endif + while ((astate = TAILQ_FIRST(&state->arp_states)) != NULL) { + arp_free(astate); } + + /* No need to close because the last free will close */ }
--- a/src/arp.h Wed Apr 17 22:18:39 2019 +0000 +++ b/src/arp.h Sat May 04 11:05:17 2019 +0100 @@ -63,15 +63,16 @@ TAILQ_ENTRY(arp_state) next; struct interface *iface; - void (*probed_cb)(struct arp_state *); + void (*found_cb)(struct arp_state *, const struct arp_msg *); + void (*not_found_cb)(struct arp_state *); void (*announced_cb)(struct arp_state *); - void (*conflicted_cb)(struct arp_state *, const struct arp_msg *); + void (*defend_failed_cb)(struct arp_state *); void (*free_cb)(struct arp_state *); struct in_addr addr; int probes; int claims; - struct in_addr failed; + struct timespec defend; }; TAILQ_HEAD(arp_statehead, arp_state); @@ -87,20 +88,14 @@ ((const struct iarp_state *)(ifp)->if_data[IF_DATA_ARP]) #ifdef ARP -int arp_open(struct interface *); -ssize_t arp_request(const struct interface *, in_addr_t, in_addr_t); +struct arp_state *arp_new(struct interface *, const struct in_addr *); void arp_probe(struct arp_state *); -void arp_report_conflicted(const struct arp_state *, const struct arp_msg *); -struct arp_state *arp_new(struct interface *, const struct in_addr *); -struct arp_state *arp_find(struct interface *, const struct in_addr *); void arp_announce(struct arp_state *); void arp_announceaddr(struct dhcpcd_ctx *, const struct in_addr *); void arp_ifannounceaddr(struct interface *, const struct in_addr *); void arp_cancel(struct arp_state *); void arp_free(struct arp_state *); -void arp_free_but(struct arp_state *); +void arp_freeaddr(struct interface *, const struct in_addr *); void arp_drop(struct interface *); - -void arp_handleifa(int, struct ipv4_addr *); #endif /* ARP */ #endif /* ARP_H */
--- a/src/auth.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/auth.c Sat May 04 11:05:17 2019 +0100 @@ -117,7 +117,11 @@ m = vm; data = vdata; - /* Ensure that d is inside m which *may* not be the case for DHPCPv4 */ + /* Ensure that d is inside m which *may* not be the case for DHCPv4. + * This can occur if the authentication option is split using + * DHCP long option from RFC 3399. Section 9 which does infact note that + * implementations should take this into account. + * Fixing this would be problematic, patches welcome. */ if (data < m || data > m + mlen || data + dlen > m + mlen) { errno = ERANGE; return NULL; @@ -354,7 +358,7 @@ } free(mm); - if (memcmp(d, &hmac_code, dlen)) { + if (!consttime_memequal(d, &hmac_code, dlen)) { errno = EPERM; return NULL; }
--- a/src/bpf.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/bpf.c Sat May 04 11:05:17 2019 +0100 @@ -84,7 +84,7 @@ bpf_frame_header_len(const struct interface *ifp) { - switch(ifp->family) { + switch (ifp->family) { case ARPHRD_ETHER: return sizeof(struct ether_header); default: @@ -92,6 +92,23 @@ } } +static const uint8_t etherbroadcastaddr[] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +int +bpf_frame_bcast(const struct interface *ifp, const char *frame) +{ + + switch (ifp->family) { + case ARPHRD_ETHER: + return memcmp(frame + + offsetof(struct ether_header, ether_dhost), + etherbroadcastaddr, sizeof(etherbroadcastaddr)); + default: + return -1; + } +} + #ifndef __linux__ /* Linux is a special snowflake for opening, attaching and reading BPF. * See if-linux.c for the Linux specific BPF functions. */ @@ -227,8 +244,12 @@ if (state->buffer_pos + packet.bh_caplen + packet.bh_hdrlen > state->buffer_len) goto next; /* Packet beyond buffer, drop. */ - payload = state->buffer + state->buffer_pos + - packet.bh_hdrlen + fl; + payload = state->buffer + state->buffer_pos + packet.bh_hdrlen; + if (bpf_frame_bcast(ifp, payload) == 0) + *flags |= BPF_BCAST; + else + *flags &= ~BPF_BCAST; + payload += fl; bytes = (ssize_t)packet.bh_caplen - fl; if ((size_t)bytes > len) bytes = (ssize_t)len; @@ -301,6 +322,7 @@ /* Normally this is needed by bootp. * Once that uses this again, the ARP guard here can be removed. */ #ifdef ARP +#define BPF_CMP_HWADDR_LEN ((((HWADDR_LEN / 4) + 2) * 2) + 1) static unsigned int bpf_cmp_hwaddr(struct bpf_insn *bpf, size_t bpf_len, size_t off, bool equal, uint8_t *hwaddr, size_t hwaddr_len) @@ -414,7 +436,7 @@ sizeof(((struct ether_arp *)0)->arp_sha), 1, 0), BPF_STMT(BPF_RET + BPF_K, 0), }; -#define bpf_arp_ether_len __arraycount(bpf_arp_ether) +#define BPF_ARP_ETHER_LEN __arraycount(bpf_arp_ether) static const struct bpf_insn bpf_arp_filter [] = { /* Make sure this is for IP. */ @@ -425,21 +447,25 @@ BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_op)), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0), /* or ARP REPLY. */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 1), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), BPF_STMT(BPF_RET + BPF_K, 0), /* Make sure the protocol length matches. */ BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_pln)), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(in_addr_t), 1, 0), BPF_STMT(BPF_RET + BPF_K, 0), }; -#define bpf_arp_filter_len __arraycount(bpf_arp_filter) -#define bpf_arp_extra ((((ARP_ADDRS_MAX + 1) * 2) * 2) + 2) -#define bpf_arp_hw ((((HWADDR_LEN / 4) + 2) * 2) + 1) +#define BPF_ARP_FILTER_LEN __arraycount(bpf_arp_filter) + +#define BPF_ARP_ADDRS_LEN 1 + (ARP_ADDRS_MAX * 2) + 3 + \ + (ARP_ADDRS_MAX * 2) + 1 + +#define BPF_ARP_LEN BPF_ARP_ETHER_LEN + BPF_ARP_FILTER_LEN + \ + BPF_CMP_HWADDR_LEN + BPF_ARP_ADDRS_LEN int bpf_arp(struct interface *ifp, int fd) { - struct bpf_insn bpf[3+ bpf_arp_filter_len + bpf_arp_hw + bpf_arp_extra]; + struct bpf_insn bpf[BPF_ARP_LEN]; struct bpf_insn *bp; struct iarp_state *state; uint16_t arp_len; @@ -452,7 +478,7 @@ switch(ifp->family) { case ARPHRD_ETHER: memcpy(bp, bpf_arp_ether, sizeof(bpf_arp_ether)); - bp += bpf_arp_ether_len; + bp += BPF_ARP_ETHER_LEN; arp_len = sizeof(struct ether_header)+sizeof(struct ether_arp); break; default: @@ -462,10 +488,10 @@ /* Copy in the main filter. */ memcpy(bp, bpf_arp_filter, sizeof(bpf_arp_filter)); - bp += bpf_arp_filter_len; + bp += BPF_ARP_FILTER_LEN; /* Ensure it's not from us. */ - bp += bpf_cmp_hwaddr(bp, bpf_arp_hw, sizeof(struct arphdr), + bp += bpf_cmp_hwaddr(bp, BPF_CMP_HWADDR_LEN, sizeof(struct arphdr), false, ifp->hwaddr, ifp->hwlen); state = ARP_STATE(ifp);
--- a/src/bpf.h Wed Apr 17 22:18:39 2019 +0000 +++ b/src/bpf.h Sat May 04 11:05:17 2019 +0100 @@ -31,11 +31,13 @@ #define BPF_READING (1U << 0) #define BPF_EOF (1U << 1) #define BPF_PARTIALCSUM (1U << 2) +#define BPF_BCAST (1U << 3) #include "dhcpcd.h" extern const char *bpf_name; size_t bpf_frame_header_len(const struct interface *); +int bpf_frame_bcast(const struct interface *, const char *frame); int bpf_open(struct interface *, int (*)(struct interface *, int)); int bpf_close(struct interface *, int); int bpf_attach(int, void *, unsigned int);
--- a/src/control.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/control.c Sat May 04 11:05:17 2019 +0100 @@ -318,7 +318,7 @@ if ((fd = make_sock(&sa, ifname, 0)) != -1) { socklen_t len; - + len = (socklen_t)SUN_LEN(&sa); if (connect(fd, (struct sockaddr *)&sa, len) == -1) { close(fd);
--- a/src/defs.h Wed Apr 17 22:18:39 2019 +0000 +++ b/src/defs.h Sat May 04 11:05:17 2019 +0100 @@ -28,7 +28,7 @@ #define CONFIG_H #define PACKAGE "dhcpcd" -#define VERSION "7.1.1" +#define VERSION "7.99.0" #ifndef CONFIG # define CONFIG SYSCONFDIR "/" PACKAGE ".conf"
--- a/src/dhcp.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/dhcp.c Sat May 04 11:05:17 2019 +0100 @@ -124,8 +124,9 @@ }; static int dhcp_openbpf(struct interface *); +static void dhcp_start1(void *); #ifdef ARP -static void dhcp_arp_conflicted(struct arp_state *, const struct arp_msg *); +static void dhcp_arp_found(struct arp_state *, const struct arp_msg *); #endif static void dhcp_handledhcp(struct interface *, struct bootp *, size_t, const struct in_addr *); @@ -215,6 +216,12 @@ } l = *p++; + /* Check we can read the option data, if present */ + if (p + l > e) { + errno = EINVAL; + return NULL; + } + if (o == DHO_OPTSOVERLOADED) { /* Ensure we only get this option once by setting * the last bit as well as the value. @@ -249,10 +256,6 @@ bp += ol; } ol = l; - if (p + ol >= e) { - errno = EINVAL; - return NULL; - } op = p; bl += ol; } @@ -1925,35 +1928,6 @@ send_request(ifp); } -static int -dhcp_leaseextend(struct interface *ifp) -{ - -#ifdef ARP - if (ifp->options->options & DHCPCD_ARP) { - const struct dhcp_state *state; - struct arp_state *astate; - - state = D_CSTATE(ifp); - if ((astate = arp_new(ifp, &state->lease.addr)) == NULL) - return -1; - astate->conflicted_cb = dhcp_arp_conflicted; - -#ifndef KERNEL_RFC5227 - if (arp_open(ifp) == -1) - return -1; -#endif - - logwarnx("%s: extending lease until DaD failure or DHCP", - ifp->name); - return 0; - } -#endif - - logwarnx("%s: extending lease", ifp->name); - return 0; -} - static void dhcp_expire1(struct interface *ifp) { @@ -1972,12 +1946,12 @@ { struct interface *ifp = arg; + if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) { + logwarnx("%s: DHCP lease expired, extending lease", ifp->name); + return; + } + logerrx("%s: DHCP lease expired", ifp->name); - if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) { - if (dhcp_leaseextend(ifp) == 0) - return; - logerr(__func__); - } dhcp_expire1(ifp); } @@ -2043,9 +2017,78 @@ send_rebind(ifp); } +static void +dhcp_finish_dad(struct interface *ifp, struct in_addr *ia) +{ + struct dhcp_state *state = D_STATE(ifp); + + if (state->state != DHS_PROBE) + return; + if (state->offer == NULL || state->offer->yiaddr != ia->s_addr) + return; + + logdebugx("%s: DAD completed for %s", ifp->name, inet_ntoa(*ia)); + if (!(ifp->options->options & DHCPCD_INFORM)) + dhcp_bind(ifp); +#ifndef IN_IFF_DUPLICATED + else { + struct bootp *bootp; + size_t len; + + bootp = state->new; + len = state->new_len; + state->new = state->offer; + state->new_len = state->offer_len; + get_lease(ifp, &state->lease, state->new, state->new_len); + ipv4_applyaddr(ifp); + state->new = bootp; + state->new_len = len; + } +#endif + + /* If we forked, stop here. */ + if (ifp->ctx->options & DHCPCD_FORKED) + return; + +#ifdef IPV4LL + /* Stop IPv4LL now we have a working DHCP address */ + ipv4ll_drop(ifp); +#endif + + if (ifp->options->options & DHCPCD_INFORM) + dhcp_inform(ifp); +} + + +static void +dhcp_addr_duplicated(struct interface *ifp, struct in_addr *ia) +{ + struct dhcp_state *state = D_STATE(ifp); +#ifdef IN_IFF_DUPLICATED + struct ipv4_addr *iap; +#endif + + if ((state->offer == NULL || state->offer->yiaddr != ia->s_addr) && + !IN_ARE_ADDR_EQUAL(ia, &state->lease.addr)) + return; + + /* RFC 2131 3.1.5, Client-server interaction */ + logerrx("%s: DAD detected %s", ifp->name, inet_ntoa(*ia)); + unlink(state->leasefile); + if (!(ifp->options->options & DHCPCD_STATIC) && !state->lease.frominfo) + dhcp_decline(ifp); +#ifdef IN_IFF_DUPLICATED + if ((iap = ipv4_iffindaddr(ifp, ia, NULL)) != NULL) + ipv4_deladdr(iap, 0); +#endif + eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); + eloop_timeout_add_sec(ifp->ctx->eloop, + DHCP_RAND_MAX, dhcp_discover, ifp); +} + #ifdef ARP static void -dhcp_arp_probed(struct arp_state *astate) +dhcp_arp_not_found(struct arp_state *astate) { struct interface *ifp; struct dhcp_state *state; @@ -2071,56 +2114,21 @@ } #endif - /* Already bound so DAD has worked */ - if (state->state == DHS_BOUND) - return; - - logdebugx("%s: DAD completed for %s", - ifp->name, inet_ntoa(astate->addr)); - if (!(ifo->options & DHCPCD_INFORM)) - dhcp_bind(ifp); -#ifndef IN_IFF_TENTATIVE - else { - struct bootp *bootp; - size_t len; - - bootp = state->new; - len = state->new_len; - state->new = state->offer; - state->new_len = state->offer_len; - get_lease(ifp, &state->lease, state->new, state->new_len); - ipv4_applyaddr(astate->iface); - state->new = bootp; - state->new_len = len; - } -#endif - - /* If we forked, stop here. */ - if (ifp->ctx->options & DHCPCD_FORKED) - return; - -#ifdef IPV4LL - /* Stop IPv4LL now we have a working DHCP address */ - ipv4ll_drop(ifp); -#endif - - if (ifo->options & DHCPCD_INFORM) - dhcp_inform(ifp); + dhcp_finish_dad(ifp, &astate->addr); } static void -dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg) +dhcp_arp_found(struct arp_state *astate, const struct arp_msg *amsg) { + struct in_addr addr; +#ifdef ARPING struct interface *ifp; struct dhcp_state *state; -#ifdef ARPING struct if_options *ifo; -#endif ifp = astate->iface; state = D_STATE(ifp); -#ifdef ARPING ifo = ifp->options; if (state->arping_index != -1 && state->arping_index < ifo->arping_len && @@ -2129,17 +2137,14 @@ { char buf[HWADDR_LEN * 3]; - astate->failed.s_addr = ifo->arping[state->arping_index]; - arp_report_conflicted(astate, amsg); hwaddr_ntoa(amsg->sha, ifp->hwlen, buf, sizeof(buf)); if (dhcpcd_selectprofile(ifp, buf) == -1 && - dhcpcd_selectprofile(ifp, - inet_ntoa(astate->failed)) == -1) + dhcpcd_selectprofile(ifp, inet_ntoa(amsg->sip)) == -1) { /* We didn't find a profile for this * address or hwaddr, so move to the next * arping profile */ - dhcp_arp_probed(astate); + dhcp_arp_not_found(astate); return; } arp_free(astate); @@ -2149,65 +2154,20 @@ } #endif - /* RFC 2131 3.1.5, Client-server interaction - * NULL amsg means IN_IFF_DUPLICATED */ - if (amsg == NULL || (state->offer && - (amsg->sip.s_addr == state->offer->yiaddr || - (amsg->sip.s_addr == 0 && - amsg->tip.s_addr == state->offer->yiaddr)))) - { -#ifdef IN_IFF_DUPLICATED - struct ipv4_addr *ia; -#endif - - if (amsg) - astate->failed.s_addr = state->offer->yiaddr; - else - astate->failed = astate->addr; - arp_report_conflicted(astate, amsg); - unlink(state->leasefile); -#ifdef ARP - if (!(ifp->options->options & DHCPCD_STATIC) && - !state->lease.frominfo) - dhcp_decline(ifp); -#endif -#ifdef IN_IFF_DUPLICATED - if ((ia = ipv4_iffindaddr(ifp, &astate->addr, NULL)) != NULL) - ipv4_deladdr(ia, 1); -#endif - arp_free(astate); - eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); - eloop_timeout_add_sec(ifp->ctx->eloop, - DHCP_RAND_MAX, dhcp_discover, ifp); - return; - } - - /* Bound address */ - if (amsg && state->addr && - amsg->sip.s_addr == state->addr->addr.s_addr) - { - astate->failed = state->addr->addr; - arp_report_conflicted(astate, amsg); - if (state->state == DHS_BOUND) { - /* For now, just report the duplicated address */ - } else { - arp_free(astate); - dhcp_expire1(ifp); - } - return; - } + addr = astate->addr; + arp_free(astate); + dhcp_addr_duplicated(astate->iface, &addr); } +#ifdef KERNEL_RFC5227 static void dhcp_arp_announced(struct arp_state *state) { -// TODO: DHCP addresses handle ACD? -//#ifdef KERNEL_RFC5227 arp_free(state); -//#endif } -#endif +#endif /* KERNEL_RFC5227 */ +#endif /* ARP */ void dhcp_bind(struct interface *ifp) @@ -2361,12 +2321,6 @@ if (ifp->ctx->options & DHCPCD_FORKED) return; state->interval = 0; - if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND && - dhcp_leaseextend(ifp) == -1) - { - logerr("%s: %s", ifp->name, __func__); - dhcp_expire(ifp); - } dhcp_discover(ifp); } @@ -2399,17 +2353,32 @@ } #ifdef ARP +#ifndef KERNEL_RFC5227 +static void +dhcp_arp_defend_failed(struct arp_state *astate) +{ + + dhcp_drop(astate->iface, "EXPIRED"); + dhcp_start1(astate->iface); +} +#endif + static struct arp_state * dhcp_arp_new(struct interface *ifp, struct in_addr *addr) { struct arp_state *astate; + astate = arp_new(ifp, addr); if (astate == NULL) return NULL; - astate->probed_cb = dhcp_arp_probed; - astate->conflicted_cb = dhcp_arp_conflicted; + astate->found_cb = dhcp_arp_found; + astate->not_found_cb = dhcp_arp_not_found; +#ifdef KERNEL_RFC5227 astate->announced_cb = dhcp_arp_announced; +#else + astate->defend_failed_cb = dhcp_arp_defend_failed; +#endif return astate; } @@ -2419,7 +2388,6 @@ struct dhcp_state *state; struct in_addr addr; struct ipv4_addr *ia; - struct arp_state *astate; eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); @@ -2429,11 +2397,7 @@ /* If the interface already has the address configured * then we can't ARP for duplicate detection. */ ia = ipv4_iffindaddr(ifp, &addr, NULL); - astate = dhcp_arp_new(ifp, &addr); - if (astate == NULL) - return -1; - -#ifdef IN_IFF_TENTATIVE +#ifdef IN_IFF_NOTUSEABLE if (ia == NULL || ia->addr_flags & IN_IFF_NOTUSEABLE) { state->state = DHS_PROBE; if (ia == NULL) { @@ -2449,8 +2413,13 @@ } #else if (ifp->options->options & DHCPCD_ARP && ia == NULL) { + struct arp_state *astate; struct dhcp_lease l; + astate = dhcp_arp_new(ifp, &addr); + if (astate == NULL) + return -1; + state->state = DHS_PROBE; get_lease(ifp, &l, state->offer, state->offer_len); loginfox("%s: probing address %s/%d", @@ -2707,9 +2676,14 @@ return; } +#ifdef ARP + if (state->addr != NULL) + arp_freeaddr(ifp, &state->addr->addr); +#endif #ifdef ARPING state->arping_index = -1; #endif + if (ifp->options->options & DHCPCD_RELEASE && !(ifp->options->options & DHCPCD_INFORM)) { @@ -3501,9 +3475,14 @@ logerr(__func__); return; } + if (D_CSTATE(ifp) == NULL) { + logdebugx("%s: received BOOTP for inactive interface", + ifp->name); + return; + } } - dhcp_handlebootp(ifp, (struct bootp *)buf, (size_t)bytes, + dhcp_handlebootp(ifp, (struct bootp *)(void *)buf, (size_t)bytes, &from.sin_addr); #endif } @@ -3762,7 +3741,7 @@ astate = dhcp_arp_new(ifp, NULL); if (astate) - dhcp_arp_probed(astate); + dhcp_arp_not_found(astate); return; } #endif @@ -4014,8 +3993,10 @@ return; #ifdef IN_IFF_NOTUSEABLE - if (ia->addr_flags & IN_IFF_NOTUSEABLE) - return; + if (!(ia->addr_flags & IN_IFF_NOTUSEABLE)) + dhcp_finish_dad(ifp, &ia->addr); + else if (ia->addr_flags & IN_IFF_DUPLICATED) + dhcp_addr_duplicated(ifp, &ia->addr); #endif ifo = ifp->options;
--- a/src/dhcp6.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/dhcp6.c Sat May 04 11:05:17 2019 +0100 @@ -2029,12 +2029,12 @@ nd = o + ol; l -= (size_t)(nd - d); d = nd; - if (ol < 24) { + if (ol < sizeof(ia)) { errno = EINVAL; logerrx("%s: IA Address option truncated", ifp->name); continue; } - memcpy(&ia, o, ol); + memcpy(&ia, o, sizeof(ia)); ia.pltime = ntohl(ia.pltime); ia.vltime = ntohl(ia.vltime); /* RFC 3315 22.6 */ @@ -2166,40 +2166,38 @@ state->expire = a->prefix_vltime; i++; - o = dhcp6_findoption(o, ol, D6_OPTION_PD_EXCLUDE, &ol); a->prefix_exclude_len = 0; memset(&a->prefix_exclude, 0, sizeof(a->prefix_exclude)); -#if 0 - if (ex == NULL) { - struct dhcp6_option *w; - uint8_t *wp; - - w = calloc(1, 128); - w->len = htons(2); - wp = D6_OPTION_DATA(w); - *wp++ = 64; - *wp++ = 0x78; - ex = w; - } -#endif + o = dhcp6_findoption(o, ol, D6_OPTION_PD_EXCLUDE, &ol); if (o == NULL) continue; - if (ol < 2) { - logerrx("%s: truncated PD Exclude", ifp->name); + + /* RFC 6603 4.2 says option length MUST be between 2 and 17. + * This allows 1 octet for prefix length and 16 for the + * subnet ID. */ + if (ol < 2 || ol > 17) { + logerrx("%s: invalid PD Exclude option", ifp->name); + continue; + } + + /* RFC 6603 4.2 says prefix length MUST be between the + * length of the IAPREFIX prefix length + 1 and 128. */ + if (*o < a->prefix_len + 1 || *o > 128) { + logerrx("%s: invalid PD Exclude length", ifp->name); + continue; + } + + ol--; + /* Check option length matches prefix length. */ + if (((*o - a->prefix_len - 1) / NBBY) + 1 != ol) { + logerrx("%s: PD Exclude length mismatch", ifp->name); continue; } a->prefix_exclude_len = *o++; - ol--; - if (((a->prefix_exclude_len - a->prefix_len - 1) / NBBY) + 1 - != ol) - { - logerrx("%s: PD Exclude length mismatch", ifp->name); - a->prefix_exclude_len = 0; - continue; - } - nb = a->prefix_len % NBBY; + memcpy(&a->prefix_exclude, &a->prefix, sizeof(a->prefix_exclude)); + nb = a->prefix_len % NBBY; if (nb) ol--; pw = a->prefix_exclude.s6_addr + @@ -3019,7 +3017,7 @@ * unless those values in those fields are 0. */ logwarnx("%s: ignoring T1 %"PRIu32 - " to due address expiry", + " due to address expiry", ifp->name, state->renew); state->renew = state->rebind = 0; }
--- a/src/dhcpcd.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/dhcpcd.c Sat May 04 11:05:17 2019 +0100 @@ -84,6 +84,9 @@ const size_t dhcpcd_signals_len = __arraycount(dhcpcd_signals); #endif +#define IF_UPANDRUNNING(a) \ + (((a)->flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) + static void usage(void) { @@ -653,25 +656,6 @@ } static void -dhcpcd_pollup(void *arg) -{ - struct interface *ifp = arg; - int carrier; - - carrier = if_carrier(ifp); /* will set ifp->flags */ - if (carrier == LINK_UP && !(ifp->flags & IFF_UP)) { - struct timespec tv; - - tv.tv_sec = 0; - tv.tv_nsec = IF_POLL_UP * NSEC_PER_MSEC; - eloop_timeout_add_tv(ifp->ctx->eloop, &tv, dhcpcd_pollup, ifp); - return; - } - - dhcpcd_handlecarrier(ifp->ctx, carrier, ifp->flags, ifp->name); -} - -static void dhcpcd_initstate2(struct interface *ifp, unsigned long long options) { struct if_options *ifo; @@ -724,35 +708,18 @@ !ifp->active) return; - switch(carrier) { - case LINK_UNKNOWN: - carrier = if_carrier(ifp); /* will set ifp->flags */ - break; - case LINK_UP: - /* we have a carrier! Still need to check for IFF_UP */ - if (flags & IFF_UP) + if (carrier == LINK_UNKNOWN) { + if (ifp->wireless) { + carrier = LINK_DOWN; ifp->flags = flags; - else { - /* So we need to poll for IFF_UP as there is no - * kernel notification when it's set. */ - dhcpcd_pollup(ifp); - return; - } - break; - default: + } else + carrier = if_carrier(ifp); + } else ifp->flags = flags; - } + if (carrier == LINK_UNKNOWN) + carrier = IF_UPANDRUNNING(ifp) ? LINK_UP : LINK_DOWN; - /* If we here, we don't need to poll for IFF_UP any longer - * if generated by a kernel event. */ - eloop_timeout_delete(ifp->ctx->eloop, dhcpcd_pollup, ifp); - - if (carrier == LINK_UNKNOWN) { - if (errno != ENOTTY && errno != ENXIO) { - /* Don't log an error if interface departed */ - logerr("%s: %s", ifp->name, __func__); - } - } else if (carrier == LINK_DOWN || (ifp->flags & IFF_UP) == 0) { + if (carrier == LINK_DOWN || (ifp->flags & IFF_UP) == 0) { if (ifp->carrier != LINK_DOWN) { if (ifp->carrier == LINK_UP) loginfox("%s: carrier lost", ifp->name); @@ -867,7 +834,6 @@ struct if_options *ifo = ifp->options; char buf[DUID_LEN * 3]; int carrier; - struct timespec tv; if (ifo->options & DHCPCD_LINK) { switch (ifp->carrier) { @@ -879,14 +845,22 @@ case LINK_UNKNOWN: /* No media state available. * Loop until both IFF_UP and IFF_RUNNING are set */ - if ((carrier = if_carrier(ifp)) == LINK_UNKNOWN) { - tv.tv_sec = 0; - tv.tv_nsec = IF_POLL_UP * NSEC_PER_MSEC; - eloop_timeout_add_tv(ifp->ctx->eloop, - &tv, dhcpcd_startinterface, ifp); - } else - dhcpcd_handlecarrier(ifp->ctx, carrier, - ifp->flags, ifp->name); + carrier = if_carrier(ifp); + if (carrier == LINK_UNKNOWN) { + if (IF_UPANDRUNNING(ifp)) + carrier = LINK_UP; + else { + struct timespec tv; + + tv.tv_sec = 0; + tv.tv_nsec = IF_POLL_UP * NSEC_PER_MSEC; + eloop_timeout_add_tv(ifp->ctx->eloop, + &tv, dhcpcd_startinterface, ifp); + return; + } + } + dhcpcd_handlecarrier(ifp->ctx, carrier, + ifp->flags, ifp->name); return; } } @@ -987,20 +961,6 @@ ) logerr("%s: %s", __func__, ifp->name); - if (ifp->options->options & DHCPCD_LINK && - ifp->carrier == LINK_UNKNOWN) - { - int carrier; - - if ((carrier = if_carrier(ifp)) != LINK_UNKNOWN) { - dhcpcd_handlecarrier(ifp->ctx, carrier, - ifp->flags, ifp->name); - return; - } - loginfox("%s: unknown carrier, waiting for interface flags", - ifp->name); - } - dhcpcd_startinterface(ifp); } @@ -1121,7 +1081,8 @@ dhcpcd_linkoverflow(ctx); return; } - logerr(__func__); + if (errno != ENOTSUP) + logerr(__func__); } }
--- a/src/dhcpcd.conf.5.in Wed Apr 17 22:18:39 2019 +0000 +++ b/src/dhcpcd.conf.5.in Sat May 04 11:05:17 2019 +0100 @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd March 8, 2019 +.Dd April 24, 2019 .Dt DHCPCD.CONF 5 .Os .Sh NAME @@ -376,8 +376,7 @@ noipv6rs # disable routing solicitation denyinterfaces eth2 # Don't touch eth2 at all interface eth0 - ipv6rs # enable routing solicitation get the - # default IPv6 route + ipv6rs # enable routing solicitation for eth0 ia_na 1 # request an IPv6 address ia_pd 2 eth1/0 # request a PD and assign it to eth1 ia_pd 3 eth2/1 eth3/2 # req a PD and assign it to eth2 and eth3
--- a/src/dhcpcd.h Wed Apr 17 22:18:39 2019 +0000 +++ b/src/dhcpcd.h Sat May 04 11:05:17 2019 +0100 @@ -85,7 +85,6 @@ unsigned short vlanid; unsigned int metric; int carrier; - bool media_valid; bool wireless; uint8_t ssid[IF_SSIDLEN]; unsigned int ssid_len; @@ -184,7 +183,9 @@ uint8_t *secret; size_t secret_len; +#ifndef __sun int nd_fd; +#endif struct ra_head *ra_routers; int dhcp6_fd;
--- a/src/if-bsd.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/if-bsd.c Sat May 04 11:05:17 2019 +0100 @@ -203,6 +203,19 @@ close(priv->pf_inet6_fd); } +int +if_carrier(struct interface *ifp) +{ + struct ifmediareq ifmr = { .ifm_status = 0 }; + + strlcpy(ifmr.ifm_name, ifp->name, sizeof(ifmr.ifm_name)); + if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFMEDIA, &ifmr) == -1 || + !(ifmr.ifm_status & IFM_AVALID)) + return LINK_UNKNOWN; + + return (ifmr.ifm_status & IFM_ACTIVE) ? LINK_UP : LINK_DOWN; +} + static void if_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp) { @@ -347,20 +360,28 @@ #endif } -static void -get_addrs(int type, const void *data, const struct sockaddr **sa) +static int +get_addrs(int type, const void *data, size_t data_len, + const struct sockaddr **sa) { - const char *cp; + const char *cp, *ep; int i; cp = data; + ep = cp + data_len; for (i = 0; i < RTAX_MAX; i++) { if (type & (1 << i)) { + if (cp >= ep) { + errno = EINVAL; + return -1; + } sa[i] = (const struct sockaddr *)cp; RT_ADVANCE(cp, sa[i]); } else sa[i] = NULL; } + + return 0; } static struct interface * @@ -636,7 +657,12 @@ } #endif - get_addrs(rtm->rtm_addrs, rtm + 1, rti_info); + /* We have already checked that at least one address must be + * present after the rtm structure. */ + /* coverity[ptr_arith] */ + if (get_addrs(rtm->rtm_addrs, rtm + 1, + rtm->rtm_msglen - sizeof(*rtm), rti_info) == -1) + return -1; memset(rt, 0, sizeof(*rt)); rt->rt_flags = (unsigned int)rtm->rtm_flags; @@ -700,6 +726,10 @@ end = buf + needed; for (p = buf; p < end; p += rtm->rtm_msglen) { rtm = (void *)p; + if (p + rtm->rtm_msglen >= end) { + errno = EINVAL; + break; + } if (if_copyrt(ctx, &rt, rtm) != 0) continue; if ((rtn = rt_new(rt.rt_ifp)) == NULL) { @@ -711,7 +741,7 @@ rt_free(rtn); } free(buf); - return 0; + return p == end ? 0 : -1; } #ifdef INET @@ -948,10 +978,15 @@ priv = (struct priv *)ia->iface->ctx->priv; if (ioctl(priv->pf_inet6_fd, SIOCGIFALIFETIME_IN6, &ifr6) == -1) return -1; + clock_gettime(CLOCK_MONOTONIC, &ia->created); +#if defined(__FreeBSD__) || defined(__DragonFly__) + t = ia->created.tv_sec; +#else t = time(NULL); +#endif + lifetime = &ifr6.ifr_ifru.ifru_lifetime; - if (lifetime->ia6t_preferred) ia->prefix_pltime = (uint32_t)(lifetime->ia6t_preferred - MIN(t, lifetime->ia6t_preferred)); @@ -961,7 +996,6 @@ ia->prefix_vltime = (uint32_t)(lifetime->ia6t_expire - MIN(t, lifetime->ia6t_expire)); /* Calculate the created time */ - clock_gettime(CLOCK_MONOTONIC, &ia->created); ia->created.tv_sec -= lifetime->ia6t_vltime - ia->prefix_vltime; } else ia->prefix_vltime = ND6_INFINITE_LIFETIME; @@ -969,40 +1003,45 @@ } #endif -static void +static int if_announce(struct dhcpcd_ctx *ctx, const struct if_announcemsghdr *ifan) { + if (ifan->ifan_msglen < sizeof(*ifan)) { + errno = EINVAL; + return -1; + } + switch(ifan->ifan_what) { case IFAN_ARRIVAL: - dhcpcd_handleinterface(ctx, 1, ifan->ifan_name); - break; + return dhcpcd_handleinterface(ctx, 1, ifan->ifan_name); case IFAN_DEPARTURE: - dhcpcd_handleinterface(ctx, -1, ifan->ifan_name); - break; + return dhcpcd_handleinterface(ctx, -1, ifan->ifan_name); } + + return 0; } -static void +static int if_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm) { struct interface *ifp; int link_state; + if (ifm->ifm_msglen < sizeof(*ifm)) { + errno = EINVAL; + return -1; + } + if ((ifp = if_findindex(ctx->ifaces, ifm->ifm_index)) == NULL) - return; + return 0; switch (ifm->ifm_data.ifi_link_state) { case LINK_STATE_UNKNOWN: - if (ifp->media_valid) { - link_state = LINK_DOWN; - break; - } - /* Interface does not report media state, so we have - * to rely on IFF_UP. */ - /* FALLTHROUGH */ + link_state = LINK_UNKNOWN; + break; case LINK_STATE_UP: - link_state = ifm->ifm_flags & IFF_UP ? LINK_UP : LINK_DOWN; + link_state = LINK_UP; break; default: link_state = LINK_DOWN; @@ -1011,19 +1050,25 @@ dhcpcd_handlecarrier(ctx, link_state, (unsigned int)ifm->ifm_flags, ifp->name); + return 0; } -static void +static int if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm) { struct rt rt; + if (rtm->rtm_msglen < sizeof(*rtm)) { + errno = EINVAL; + return -1; + } + /* Ignore errors. */ if (rtm->rtm_errno != 0) - return; + return 0; if (if_copyrt(ctx, &rt, rtm) == -1) - return; + return -1; #ifdef INET6 /* @@ -1047,9 +1092,10 @@ #endif rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid); + return 0; } -static void +static int if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam) { struct interface *ifp; @@ -1057,11 +1103,21 @@ int addrflags; pid_t pid; + if (ifam->ifam_msglen < sizeof(*ifam)) { + errno = EINVAL; + return -1; + } + if (~ifam->ifam_addrs & RTA_IFA) + return 0; if ((ifp = if_findindex(ctx->ifaces, ifam->ifam_index)) == NULL) - return; - get_addrs(ifam->ifam_addrs, ifam + 1, rti_info); - if (rti_info[RTAX_IFA] == NULL) - return; + return 0; + + /* We have already checked that at least one address must be + * present after the ifam structure. */ + /* coverity[ptr_arith] */ + if (get_addrs(ifam->ifam_addrs, ifam + 1, + ifam->ifam_msglen - sizeof(*ifam), rti_info) == -1) + return -1; #ifdef HAVE_IFAM_PID pid = ifam->ifam_pid; @@ -1161,7 +1217,7 @@ } freeifaddrs(ifaddrs); if (ifa != NULL) - return; + return 0; #endif } @@ -1224,42 +1280,41 @@ } #endif } + + return 0; } -static void +static int if_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm) { if (rtm->rtm_version != RTM_VERSION) - return; + return 0; switch(rtm->rtm_type) { #ifdef RTM_IFANNOUNCE case RTM_IFANNOUNCE: - if_announce(ctx, (const void *)rtm); - break; + return if_announce(ctx, (const void *)rtm); #endif case RTM_IFINFO: - if_ifinfo(ctx, (const void *)rtm); - break; + return if_ifinfo(ctx, (const void *)rtm); case RTM_ADD: /* FALLTHROUGH */ case RTM_CHANGE: /* FALLTHROUGH */ case RTM_DELETE: - if_rtm(ctx, (const void *)rtm); - break; + return if_rtm(ctx, (const void *)rtm); #ifdef RTM_CHGADDR case RTM_CHGADDR: /* FALLTHROUGH */ #endif case RTM_DELADDR: /* FALLTHROUGH */ case RTM_NEWADDR: - if_ifa(ctx, (const void *)rtm); - break; + return if_ifa(ctx, (const void *)rtm); #ifdef RTM_DESYNC case RTM_DESYNC: - dhcpcd_linkoverflow(ctx); - break; + return dhcpcd_linkoverflow(ctx); #endif } + + return 0; } int @@ -1273,9 +1328,13 @@ len = recvmsg(ctx->link_fd, &msg, 0); if (len == -1) return -1; - if (len != 0) - if_dispatch(ctx, &rtm.hdr); - return 0; + if (len == 0) + return 0; + if (len < rtm.hdr.rtm_msglen) { + errno = EINVAL; + return -1; + } + return if_dispatch(ctx, &rtm.hdr); } #ifndef SYS_NMLN /* OSX */
--- a/src/if-linux.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/if-linux.c Sat May 04 11:05:17 2019 +0100 @@ -349,6 +349,15 @@ } } +int +if_carrier(struct interface *ifp) +{ + + if (if_getflags(ifp) == -1) + return LINK_UNKNOWN; + return ifp->flags & IFF_RUNNING ? LINK_UP : LINK_DOWN; +} + static int get_netlink(struct dhcpcd_ctx *ctx, struct iovec *iov, void *arg, int fd, int flags, @@ -1418,6 +1427,10 @@ if (bytes) { ssize_t fl = (ssize_t)bpf_frame_header_len(ifp); + if (bpf_frame_bcast(ifp, state->buffer) == 0) + *flags |= BPF_BCAST; + else + *flags &= ~BPF_BCAST; bytes -= fl; if ((size_t)bytes > len) bytes = (ssize_t)len;
--- a/src/if-options.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/if-options.c Sat May 04 11:05:17 2019 +0100 @@ -1323,7 +1323,7 @@ return -1; #else if (ifname == NULL) { - logerr("IA PD must belong in an " + logerrx("IA PD must belong in an " "interface block"); return -1; }
--- a/src/if-sun.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/if-sun.c Sat May 04 11:05:17 2019 +0100 @@ -74,9 +74,9 @@ #endif #ifndef RT_ROUNDUP -#define RT_ROUNDUP(a) \ - ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) -#define RT_ADVANCE(x, n) (x += RT_ROUNDUP(salen(n))) +#define RT_ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(int32_t) - 1))) : sizeof(int32_t)) +#define RT_ADVANCE(x, n) ((x) += RT_ROUNDUP(salen((n)))) #endif #define COPYOUT(sin, sa) do { \ @@ -171,13 +171,16 @@ } int -if_carrier_os(struct interface *ifp) +if_carrier(struct interface *ifp) { kstat_ctl_t *kcp; kstat_t *ksp; kstat_named_t *knp; link_state_t linkstate; + if (if_getflags(ifp) == -1) + return LINK_UNKNOWN; + kcp = kstat_open(); if (kcp == NULL) goto err; @@ -268,6 +271,7 @@ if_newaddr(const char *ifname, void *arg) { struct linkwalk *lw = arg; + int error; struct ifaddrs *ifa; dlpi_handle_t dh; dlpi_info_t dlinfo; @@ -276,7 +280,10 @@ struct sockaddr_dl *sdl; ifa = NULL; - if (dlpi_open(ifname, &dh, 0) != DLPI_SUCCESS) + error = dlpi_open(ifname, &dh, 0); + if (error == DLPI_ENOLINK) /* Just vanished or in global zone */ + return B_FALSE; + if (error != DLPI_SUCCESS) goto failed1; if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS) goto failed; @@ -317,7 +324,7 @@ ifa->ifa_next = lw->lw_ifa; lw->lw_ifa = ifa; dlpi_close(dh); - return (B_FALSE); + return B_FALSE; failed: dlpi_close(dh); @@ -328,7 +335,7 @@ } failed1: lw->lw_error = errno; - return (B_TRUE); + return B_TRUE; } /* Creates an empty sockaddr_dl for lo0. */ @@ -419,19 +426,26 @@ } static int -get_addrs(int type, const void *data, const struct sockaddr **sa) +get_addrs(int type, const void *data, size_t data_len, + const struct sockaddr **sa) { - const char *cp; + const char *cp, *ep; int i; cp = data; + ep = cp + data_len; for (i = 0; i < RTAX_MAX; i++) { if (type & (1 << i)) { + if (cp >= ep) { + errno = EINVAL; + return -1; + } sa[i] = (const struct sockaddr *)cp; RT_ADVANCE(cp, sa[i]); } else sa[i] = NULL; } + return 0; } @@ -520,6 +534,7 @@ struct rt_msghdr *rtm; char *bp = rtmsg->buffer; socklen_t sl; + bool gateway_unspec; /* WARNING: Solaris will not allow you to delete RTF_KERNEL routes. * This includes subnet/prefix routes. */ @@ -538,25 +553,28 @@ rtm->rtm_flags = rt->rt_flags; rtm->rtm_addrs = RTA_DST | RTA_GATEWAY; + gateway_unspec = sa_is_unspecified(&rt->rt_gateway); + if (cmd == RTM_ADD || cmd == RTM_CHANGE) { bool netmask_bcast = sa_is_allones(&rt->rt_netmask); rtm->rtm_flags |= RTF_UP; if (!(rtm->rtm_flags & RTF_REJECT) && - !sa_is_loopback(&rt->rt_gateway) && - /* Solaris doesn't like interfaces on default routes. */ - !sa_is_unspecified(&rt->rt_dest)) + !sa_is_loopback(&rt->rt_gateway)) { rtm->rtm_addrs |= RTA_IFP; -#if 0 + /* RTA_IFA is currently ignored by the kernel. + * RTA_SRC and RTF_SETSRC look like what we want, + * but they don't work with RTF_GATEWAY. + * We set RTA_IFA just in the hope that the + * kernel will one day support this. */ if (!sa_is_unspecified(&rt->rt_ifa)) rtm->rtm_addrs |= RTA_IFA; -#endif } if (netmask_bcast) rtm->rtm_flags |= RTF_HOST; - else + else if (!gateway_unspec) rtm->rtm_flags |= RTF_GATEWAY; /* Emulate the kernel by marking address generated @@ -575,7 +593,7 @@ ADDSA(&rt->rt_dest); - if (sa_is_unspecified(&rt->rt_gateway)) + if (gateway_unspec) ADDSA(&rt->rt_ifa); else ADDSA(&rt->rt_gateway); @@ -590,14 +608,13 @@ ADDSA((struct sockaddr *)&sdl); } - if (rtm->rtm_addrs & RTA_IFA) { + if (rtm->rtm_addrs & RTA_IFA) ADDSA(&rt->rt_ifa); - rtm->rtm_addrs |= RTA_SRC; - } + +#if 0 if (rtm->rtm_addrs & RTA_SRC) ADDSA(&rt->rt_ifa); - -#undef ADDSA +#endif rtm->rtm_msglen = (unsigned short)(bp - (char *)rtm); } @@ -620,10 +637,16 @@ { const struct sockaddr *rti_info[RTAX_MAX]; - if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY)) + if (~rtm->rtm_addrs & RTA_DST) return -1; - get_addrs(rtm->rtm_addrs, rtm + 1, rti_info); + /* We have already checked that at least one address must be + * present after the rtm structure. */ + /* coverity[ptr_arith] */ + if (get_addrs(rtm->rtm_addrs, rtm + 1, + rtm->rtm_msglen - sizeof(*rtm), rti_info) == -1) + return -1; + memset(rt, 0, sizeof(*rt)); rt->rt_flags = (unsigned int)rtm->rtm_flags; @@ -631,7 +654,7 @@ if (rtm->rtm_addrs & RTA_NETMASK) COPYSA(&rt->rt_netmask, rti_info[RTAX_NETMASK]); /* dhcpcd likes an unspecified gateway to indicate via the link. */ - if (rt->rt_flags & RTF_GATEWAY && + if (rtm->rtm_addrs & RTA_GATEWAY && rti_info[RTAX_GATEWAY]->sa_family != AF_LINK) COPYSA(&rt->rt_gateway, rti_info[RTAX_GATEWAY]); if (rtm->rtm_addrs & RTA_SRC) @@ -687,7 +710,7 @@ return rt; } -static void +static int if_finishrt(struct dhcpcd_ctx *ctx, struct rt *rt) { int mtu; @@ -731,10 +754,8 @@ if (rt->rt_ifp == NULL) { if (if_route_get(ctx, rt) == NULL) { rt->rt_ifp = if_loopback(ctx); - if (rt->rt_ifp == NULL) { - logerr(__func__); - return; - } + if (rt->rt_ifp == NULL) + return - 1; } } @@ -743,29 +764,42 @@ * This confuses dhcpcd as it expects MTU to be 0 * when no explicit MTU has been set. */ mtu = if_getmtu(rt->rt_ifp); + if (mtu == -1) + return -1; if (rt->rt_mtu == (unsigned int)mtu) rt->rt_mtu = 0; + + return 0; } -static int -if_addrflags0(int fd, const char *ifname) +static uint64_t +if_addrflags0(int fd, const char *ifname, const struct sockaddr *sa) { struct lifreq lifr; memset(&lifr, 0, sizeof(lifr)); strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) - return -1; + return 0; + if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1) + return 0; + if (sa_cmp(sa, (struct sockaddr *)&lifr.lifr_addr) != 0) + return 0; return lifr.lifr_flags; } -static void +static int if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm) { const struct sockaddr *sa; struct rt rt; + if (rtm->rtm_msglen < sizeof(*rtm) + sizeof(*sa)) { + errno = EINVAL; + return -1; + } + sa = (const void *)(rtm + 1); switch (sa->sa_family) { #ifdef INET6 @@ -783,7 +817,9 @@ struct in6_addr dst6; struct sockaddr_dl sdl; - get_addrs(rtm->rtm_addrs, sa, rti_info); + if (get_addrs(rtm->rtm_addrs, sa, + rtm->rtm_msglen - sizeof(*rtm), rti_info) == -1) + return -1; COPYOUT6(dst6, rti_info[RTAX_DST]); if (rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) memcpy(&sdl, rti_info[RTAX_GATEWAY], @@ -798,29 +834,85 @@ } #endif - if (if_copyrt(ctx, &rt, rtm) == 0) { - if_finishrt(ctx, &rt); - rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid); - } + if (if_copyrt(ctx, &rt, rtm) == -1 && errno != ESRCH) + return -1; + if (if_finishrt(ctx, &rt) == -1) + return -1; + rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid); + return 0; } -static void +static bool +if_getalias(struct interface *ifp, const struct sockaddr *sa, char *alias) +{ + struct ifaddrs *ifaddrs, *ifa; + struct interface *ifpx; + bool found; + + ifaddrs = NULL; + if (getallifaddrs(sa->sa_family, &ifaddrs, 0) == -1) + return false; + found = false; + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + if (sa_cmp(sa, ifa->ifa_addr) != 0) + continue; + /* Check it's for the right interace. */ + ifpx = if_find(ifp->ctx->ifaces, ifa->ifa_name); + if (ifp == ifpx) { + strlcpy(alias, ifa->ifa_name, IF_NAMESIZE); + found = true; + break; + } + } + freeifaddrs(ifaddrs); + return found; +} + +static int +if_getbrdaddr(struct dhcpcd_ctx *ctx, const char *ifname, struct in_addr *brd) +{ + struct lifreq lifr = { 0 }; + int r; + + memset(&lifr, 0, sizeof(lifr)); + strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name)); + errno = 0; + r = ioctl(ctx->pf_inet_fd, SIOCGLIFBRDADDR, &lifr, sizeof(lifr)); + if (r != -1) + COPYOUT(*brd, (struct sockaddr *)&lifr.lifr_broadaddr); + return r; +} + +static int if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam) { struct interface *ifp; const struct sockaddr *sa, *rti_info[RTAX_MAX]; int flags; - const char *ifalias; + char ifalias[IF_NAMESIZE]; + + if (ifam->ifam_msglen < sizeof(*ifam)) { + errno = EINVAL; + return -1; + } + if (~ifam->ifam_addrs & RTA_IFA) + return 0; + + /* We have already checked that at least one address must be + * present after the ifam structure. */ + /* coverity[ptr_arith] */ + if (get_addrs(ifam->ifam_addrs, ifam + 1, + ifam->ifam_msglen - sizeof(*ifam), rti_info) == -1) + return -1; + sa = rti_info[RTAX_IFA]; /* XXX We have no way of knowing who generated these * messages wich truely sucks because we want to * avoid listening to our own delete messages. */ if ((ifp = if_findindex(ctx->ifaces, ifam->ifam_index)) == NULL) - return; - sa = (const void *)(ifam + 1); - get_addrs(ifam->ifam_addrs, sa, rti_info); - if ((sa = rti_info[RTAX_IFA]) == NULL) - return; + return 0; /* * ifa_msghdr does not supply the alias, just the interface index. @@ -835,31 +927,8 @@ * ifam_alias * ifam_pid */ - - ifalias = ifp->name; - if (ifam->ifam_type != RTM_DELADDR && sa->sa_family != AF_LINK) { - struct ifaddrs *ifaddrs, *ifa; - - ifaddrs = NULL; - if (getallifaddrs(sa->sa_family, &ifaddrs, 0) == -1) - return; - for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { - if (ifa->ifa_addr != NULL) { - if (sa_cmp(sa, ifa->ifa_addr) == 0) { - /* Check it's for the right interace. */ - struct interface *ifpx; - - ifpx = if_find(ctx->ifaces, - ifa->ifa_name); - if (ifp == ifpx) { - ifalias = ifa->ifa_name; - break; - } - } - } - } - freeifaddrs(ifaddrs); - } + if (ifam->ifam_type != RTM_DELADDR && !if_getalias(ifp, sa, ifalias)) + return 0; switch (sa->sa_family) { case AF_LINK: @@ -882,12 +951,25 @@ COPYOUT(mask, rti_info[RTAX_NETMASK]); COPYOUT(bcast, rti_info[RTAX_BRD]); - if (ifam->ifam_type != RTM_DELADDR) { - flags = if_addrflags0(ctx->pf_inet_fd, ifalias); - if (flags == -1) - break; - } else - flags = 0; + if (ifam->ifam_type == RTM_DELADDR) { + struct ipv4_addr *ia; + + ia = ipv4_iffindaddr(ifp, &addr, &mask); + if (ia == NULL) + return 0; + strlcpy(ifalias, ia->alias, sizeof(ifalias)); + } else if (bcast.s_addr == INADDR_ANY) { + /* Work around a bug where broadcast + * address is not correctly reported. */ + if (if_getbrdaddr(ctx, ifalias, &bcast) == -1) + return 0; + } + flags = if_addrflags(ifp, &addr, ifalias); + if (ifam->ifam_type == RTM_DELADDR) { + if (flags != -1) + return 0; + } else if (flags == -1) + return 0; ipv4_handleifa(ctx, ifam->ifam_type == RTM_CHGADDR ? @@ -907,15 +989,20 @@ sin6 = (const void *)rti_info[RTAX_NETMASK]; mask6 = sin6->sin6_addr; - if (ifam->ifam_type != RTM_DELADDR) { - const struct priv *priv; + if (ifam->ifam_type == RTM_DELADDR) { + struct ipv6_addr *ia; - priv = (struct priv *)ctx->priv; - flags = if_addrflags0(priv->pf_inet6_fd, ifalias); - if (flags == -1) - break; - } else - flags = 0; + ia = ipv6_iffindaddr(ifp, &addr6, 0); + if (ia == NULL) + return 0; + strlcpy(ifalias, ia->alias, sizeof(ifalias)); + } + flags = if_addrflags6(ifp, &addr6, ifalias); + if (ifam->ifam_type == RTM_DELADDR) { + if (flags != -1) + return 0; + } else if (flags == -1) + return 0; ipv6_handleifa(ctx, ifam->ifam_type == RTM_CHGADDR ? @@ -925,17 +1012,24 @@ } #endif } + + return 0; } -static void +static int if_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm) { struct interface *ifp; int state; unsigned int flags; + if (ifm->ifm_msglen < sizeof(*ifm)) { + errno = EINVAL; + return -1; + } + if ((ifp = if_findindex(ctx->ifaces, ifm->ifm_index)) == NULL) - return; + return 0; flags = (unsigned int)ifm->ifm_flags; if (ifm->ifm_flags & IFF_OFFLINE) state = LINK_DOWN; @@ -944,30 +1038,30 @@ flags |= IFF_UP; } dhcpcd_handlecarrier(ctx, state, flags, ifp->name); + return 0; } -static void +static int if_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm) { if (rtm->rtm_version != RTM_VERSION) - return; + return 0; switch(rtm->rtm_type) { case RTM_IFINFO: - if_ifinfo(ctx, (const void *)rtm); - break; + return if_ifinfo(ctx, (const void *)rtm); case RTM_ADD: /* FALLTHROUGH */ case RTM_CHANGE: /* FALLTHROUGH */ case RTM_DELETE: - if_rtm(ctx, (const void *)rtm); - break; + return if_rtm(ctx, (const void *)rtm); case RTM_CHGADDR: /* FALLTHROUGH */ case RTM_DELADDR: /* FALLTHROUGH */ case RTM_NEWADDR: - if_ifa(ctx, (const void *)rtm); - break; + return if_ifa(ctx, (const void *)rtm); } + + return 0; } int @@ -980,9 +1074,15 @@ if ((len = recvmsg(ctx->link_fd, &msg, 0)) == -1) return -1; - if (len != 0) - if_dispatch(ctx, &rtm.hdr); - return 0; + if (len == -1) + return -1; + if (len == 0) + return 0; + if (len < rtm.hdr.rtm_msglen) { + errno = EINVAL; + return -1; + } + return if_dispatch(ctx, &rtm.hdr); } static void @@ -1003,7 +1103,8 @@ static int if_addaddr(int fd, const char *ifname, - struct sockaddr_storage *addr, struct sockaddr_storage *mask) + struct sockaddr_storage *addr, struct sockaddr_storage *mask, + struct sockaddr_storage *brd) { struct lifreq lifr; @@ -1020,6 +1121,13 @@ if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) return -1; + /* Then assign the broadcast address. */ + if (brd != NULL) { + lifr.lifr_broadaddr = *brd; + if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1) + return -1; + } + /* Now bring it up. */ if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) return -1; @@ -1173,15 +1281,11 @@ static int if_unplumbif(const struct dhcpcd_ctx *ctx, int af, const char *ifname) { - struct sockaddr_storage addr, mask; + struct sockaddr_storage addr = { .ss_family = af }; int fd; /* For the time being, don't unplumb the interface, just * set the address to zero. */ - memset(&addr, 0, sizeof(addr)); - addr.ss_family = af; - memset(&mask, 0, sizeof(mask)); - mask.ss_family = af; switch (af) { #ifdef INET case AF_INET: @@ -1202,7 +1306,8 @@ errno = EAFNOSUPPORT; return -1; } - return if_addaddr(fd, ifname, &addr, &mask); + return if_addaddr(fd, ifname, &addr, &addr, + af == AF_INET ? &addr : NULL); } static int @@ -1262,7 +1367,10 @@ rt.rt_mtu = re->ipRouteInfo.re_max_frag; if_octetstr(ifname, &re->ipRouteIfIndex, sizeof(ifname)); rt.rt_ifp = if_find(ctx->ifaces, ifname); - if_finishrt(ctx, &rt); + if (if_finishrt(ctx, &rt) == -1) { + logerr(__func__); + continue; + } if ((rtn = rt_new(rt.rt_ifp)) == NULL) { logerr(__func__); break; @@ -1310,10 +1418,14 @@ ipv6_mask(&in6, re->ipv6RoutePfxLength); sa_in6_init(&rt.rt_netmask, &in6); sa_in6_init(&rt.rt_gateway, &re->ipv6RouteNextHop); + sa_in6_init(&rt.rt_ifa, &re->ipv6RouteInfo.re_src_addr); rt.rt_mtu = re->ipv6RouteInfo.re_max_frag; if_octetstr(ifname, &re->ipv6RouteIfIndex, sizeof(ifname)); rt.rt_ifp = if_find(ctx->ifaces, ifname); - if_finishrt(ctx, &rt); + if (if_finishrt(ctx, &rt) == -1) { + logerr(__func__); + continue; + } if ((rtn = rt_new(rt.rt_ifp)) == NULL) { logerr(__func__); break; @@ -1466,8 +1578,10 @@ int if_address(unsigned char cmd, const struct ipv4_addr *ia) { - struct sockaddr_storage ss_addr, ss_mask; - struct sockaddr_in *sin_addr, *sin_mask; + union { + struct sockaddr sa; + struct sockaddr_storage ss; + } addr, mask, brd; /* Either remove the alias or ensure it exists. */ if (if_plumb(cmd, ia->iface->ctx, AF_INET, ia->alias) == -1) @@ -1484,24 +1598,24 @@ /* We need to update the index now */ ia->iface->index = if_nametoindex(ia->alias); - sin_addr = (struct sockaddr_in *)&ss_addr; - sin_addr->sin_family = AF_INET; - sin_addr->sin_addr = ia->addr; - sin_mask = (struct sockaddr_in *)&ss_mask; - sin_mask->sin_family = AF_INET; - sin_mask->sin_addr = ia->mask; - return if_addaddr(ia->iface->ctx->pf_inet_fd, - ia->alias, &ss_addr, &ss_mask); + sa_in_init(&addr.sa, &ia->addr); + sa_in_init(&mask.sa, &ia->mask); + sa_in_init(&brd.sa, &ia->brd); + return if_addaddr(ia->iface->ctx->pf_inet_fd, ia->alias, + &addr.ss, &mask.ss, &brd.ss); } int -if_addrflags(const struct interface *ifp, __unused const struct in_addr *addr, +if_addrflags(const struct interface *ifp, const struct in_addr *addr, const char *alias) { - int flags, aflags; + union sa_ss ss; + uint64_t aflags; + int flags; - aflags = if_addrflags0(ifp->ctx->pf_inet_fd, alias); - if (aflags == -1) + sa_in_init(&ss.sa, addr); + aflags = if_addrflags0(ifp->ctx->pf_inet_fd, alias, &ss.sa); + if (aflags == 0) return -1; flags = 0; if (aflags & IFF_DUPLICATE) @@ -1515,9 +1629,12 @@ int if_address6(unsigned char cmd, const struct ipv6_addr *ia) { - struct sockaddr_storage ss_addr, ss_mask; - struct sockaddr_in6 *sin6_addr, *sin6_mask; - struct priv *priv; + union { + struct sockaddr sa; + struct sockaddr_in6 sin6; + struct sockaddr_storage ss; + } addr, mask; + const struct priv *priv; int r; /* Either remove the alias or ensure it exists. */ @@ -1532,29 +1649,30 @@ return -1; } - priv = (struct priv *)ia->iface->ctx->priv; - sin6_addr = (struct sockaddr_in6 *)&ss_addr; - sin6_addr->sin6_family = AF_INET6; - sin6_addr->sin6_addr = ia->addr; - sin6_mask = (struct sockaddr_in6 *)&ss_mask; - sin6_mask->sin6_family = AF_INET6; - ipv6_mask(&sin6_mask->sin6_addr, ia->prefix_len); - r = if_addaddr(priv->pf_inet6_fd, - ia->alias, &ss_addr, &ss_mask); + sa_in6_init(&addr.sa, &ia->addr); + mask.sin6.sin6_family = AF_INET6; + ipv6_mask(&mask.sin6.sin6_addr, ia->prefix_len); + priv = (const struct priv *)ia->iface->ctx->priv; + r = if_addaddr(priv->pf_inet6_fd, ia->alias, &addr.ss, &mask.ss, NULL); if (r == -1 && errno == EEXIST) return 0; return r; } int -if_addrflags6(const struct interface *ifp, __unused const struct in6_addr *addr, +if_addrflags6(const struct interface *ifp, const struct in6_addr *addr, const char *alias) { struct priv *priv; - int aflags, flags; + union sa_ss ss; + uint64_t aflags; + int flags; priv = (struct priv *)ifp->ctx->priv; - aflags = if_addrflags0(priv->pf_inet6_fd, alias); + sa_in6_init(&ss.sa, addr); + aflags = if_addrflags0(priv->pf_inet6_fd, alias, &ss.sa); + if (aflags == 0) + return -1; flags = 0; if (aflags & IFF_DUPLICATE) flags |= IN6_IFF_DUPLICATED;
--- a/src/if.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/if.c Sat May 04 11:05:17 2019 +0100 @@ -126,65 +126,37 @@ } int -if_carrier(struct interface *ifp) +if_getflags(struct interface *ifp) { - int r; - struct ifreq ifr; -#ifdef SIOCGIFMEDIA - struct ifmediareq ifmr; -#endif - - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); - r = ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr); - if (r != -1) - ifp->flags = (unsigned int)ifr.ifr_flags; + struct ifreq ifr = { .ifr_flags = 0 }; -#ifdef __sun - return if_carrier_os(ifp); -#else - if (r == -1) - return LINK_UNKNOWN; - -#ifdef SIOCGIFMEDIA - memset(&ifmr, 0, sizeof(ifmr)); - strlcpy(ifmr.ifm_name, ifp->name, sizeof(ifmr.ifm_name)); - if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFMEDIA, &ifmr) != -1 && - ifmr.ifm_status & IFM_AVALID) - { - ifp->media_valid = true; - r = (ifmr.ifm_status & IFM_ACTIVE) ? LINK_UP : LINK_DOWN; - } else { - ifp->media_valid = false; - r = ifr.ifr_flags & IFF_RUNNING ? LINK_UP : LINK_UNKNOWN; - } -#else - r = ifr.ifr_flags & IFF_RUNNING ? LINK_UP : LINK_DOWN; -#endif -#endif /* __sun */ - return r; + strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); + if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr) == -1) + return -1; + ifp->flags = (unsigned int)ifr.ifr_flags; + return 0; } int if_setflag(struct interface *ifp, short flag) { - struct ifreq ifr; - int r; + struct ifreq ifr = { .ifr_flags = 0 }; + short f; - memset(&ifr, 0, sizeof(ifr)); + if (if_getflags(ifp) == -1) + return -1; + + f = (short)ifp->flags; + if ((f & flag) == flag) + return 0; + strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); - r = -1; - if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr) == 0) { - if (flag == 0 || (ifr.ifr_flags & flag) == flag) - r = 0; - else { - ifr.ifr_flags |= flag; - if (ioctl(ifp->ctx->pf_inet_fd, SIOCSIFFLAGS, &ifr) ==0) - r = 0; - } - ifp->flags = (unsigned int)ifr.ifr_flags; - } - return r; + ifr.ifr_flags = f | flag; + if (ioctl(ifp->ctx->pf_inet_fd, SIOCSIFFLAGS, &ifr) == -1) + return -1; + + ifp->flags = (unsigned int)ifr.ifr_flags; + return 0; } static int
--- a/src/if.h Wed Apr 17 22:18:39 2019 +0000 +++ b/src/if.h Sat May 04 11:05:17 2019 +0100 @@ -111,6 +111,7 @@ #define getifaddrs if_getifaddrs #endif +int if_getflags(struct interface *ifp); int if_setflag(struct interface *ifp, short flag); #define if_up(ifp) if_setflag((ifp), (IFF_UP | IFF_RUNNING)) bool if_valid_hwaddr(const uint8_t *, size_t); @@ -132,7 +133,6 @@ int if_makealias(char *, size_t, const char *, int); #endif -int if_carrier_os(struct interface *); int if_mtu_os(const struct interface *); /*
--- a/src/ipv4.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/ipv4.c Sat May 04 11:05:17 2019 +0100 @@ -482,11 +482,6 @@ int r; struct ipv4_state *state; struct ipv4_addr *ap; -#ifdef ARP - struct arp_state *astate; -#else - UNUSED(keeparp); -#endif logdebugx("%s: deleting IP address %s", addr->iface->name, addr->saddr); @@ -498,8 +493,8 @@ logerr("%s: %s", addr->iface->name, __func__); #ifdef ARP - if (!keeparp && (astate = arp_find(addr->iface, &addr->addr)) != NULL) - arp_free(astate); + if (!keeparp) + arp_freeaddr(addr->iface, &addr->addr); #endif state = IPV4_STATE(addr->iface); @@ -537,6 +532,7 @@ ifo->options & DHCPCD_INFORM || (ifo->options & DHCPCD_STATIC && ifo->req_addr.s_addr == 0)) return 0; + arp_freeaddr(ifp, &state->addr->addr); r = ipv4_deladdr(state->addr, 0); return r; } @@ -907,10 +903,10 @@ } if (addr->s_addr != INADDR_ANY && addr->s_addr != INADDR_BROADCAST) { -#ifdef ARP - arp_handleifa(cmd, ia); + dhcp_handleifa(cmd, ia, pid); +#ifdef IPV4LL + ipv4ll_handleifa(cmd, ia, pid); #endif - dhcp_handleifa(cmd, ia, pid); } if (cmd == RTM_DELADDR)
--- a/src/ipv4.h Wed Apr 17 22:18:39 2019 +0000 +++ b/src/ipv4.h Sat May 04 11:05:17 2019 +0100 @@ -62,9 +62,8 @@ * While it supports DaD, to seems to only expose IFF_DUPLICATE * so we have no way of knowing if it's tentative or not. * I don't even know if Solaris has any special treatment for tentative. */ -# define IN_IFF_TENTATIVE 0 # define IN_IFF_DUPLICATED 0x02 -# define IN_IFF_DETACHED 0 +# define IN_IFF_NOTUSEABLE IN_IFF_DUPLICATED #endif #ifdef IN_IFF_TENTATIVE @@ -72,6 +71,9 @@ (IN_IFF_TENTATIVE | IN_IFF_DUPLICATED | IN_IFF_DETACHED) #endif +#define IN_ARE_ADDR_EQUAL(a, b) ((a)->s_addr == (b)->s_addr) +#define IN_IS_ADDR_UNSPECIFIED(a) ((a)->s_addr == INADDR_ANY) + struct ipv4_addr { TAILQ_ENTRY(ipv4_addr) next; struct in_addr addr;
--- a/src/ipv4ll.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/ipv4ll.c Sat May 04 11:05:17 2019 +0100 @@ -47,7 +47,6 @@ #include "sa.h" #include "script.h" -#ifdef IPV4LL static const struct in_addr inaddr_llmask = { .s_addr = HTONL(LINKLOCAL_MASK) }; @@ -55,18 +54,21 @@ .s_addr = HTONL(LINKLOCAL_BCAST) }; +static void ipv4ll_start1(struct interface *, struct arp_state *); + static in_addr_t -ipv4ll_pickaddr(struct arp_state *astate) +ipv4ll_pickaddr(struct interface *ifp) { struct in_addr addr; - struct ipv4ll_state *istate; + struct ipv4ll_state *state; - istate = IPV4LL_STATE(astate->iface); - setstate(istate->randomstate); + state = IPV4LL_STATE(ifp); + setstate(state->randomstate); do { long r; +again: /* RFC 3927 Section 2.1 states that the first 256 and * last 256 addresses are reserved for future use. * See ipv4ll_start for why we don't use arc4random. */ @@ -76,13 +78,13 @@ ((uint32_t)(r % 0xFD00) + 0x0100)); /* No point using a failed address */ - if (addr.s_addr == astate->failed.s_addr) - continue; + if (IN_ARE_ADDR_EQUAL(&addr, &state->pickedaddr)) + goto again; /* Ensure we don't have the address on another interface */ - } while (ipv4_findaddr(astate->iface->ctx, &addr) != NULL); + } while (ipv4_findaddr(ifp->ctx, &addr) != NULL); /* Restore the original random state */ - setstate(istate->arp->iface->ctx->randomstate); + setstate(ifp->ctx->randomstate); return addr.s_addr; } @@ -177,159 +179,14 @@ } static void -ipv4ll_probed(struct arp_state *astate) -{ - struct interface *ifp; - struct ipv4ll_state *state; - struct ipv4_addr *ia; - - assert(astate != NULL); - assert(astate->iface != NULL); - - ifp = astate->iface; - state = IPV4LL_STATE(ifp); - assert(state != NULL); - - ia = ipv4_iffindaddr(ifp, &astate->addr, &inaddr_llmask); -#ifdef IN_IFF_NOTREADY - if (ia == NULL || ia->addr_flags & IN_IFF_NOTREADY) -#endif - loginfox("%s: using IPv4LL address %s", - ifp->name, inet_ntoa(astate->addr)); - if (ia == NULL) { - if (ifp->ctx->options & DHCPCD_TEST) - goto test; - ia = ipv4_addaddr(ifp, &astate->addr, - &inaddr_llmask, &inaddr_llbcast); - } - if (ia == NULL) - return; -#ifdef IN_IFF_NOTREADY - if (ia->addr_flags & IN_IFF_NOTREADY) - return; - logdebugx("%s: DAD completed for %s", - ifp->name, inet_ntoa(astate->addr)); -#endif -test: - state->addr = ia; - if (ifp->ctx->options & DHCPCD_TEST) { - script_runreason(ifp, "TEST"); - eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS); - return; - } - timespecclear(&state->defend); - rt_build(ifp->ctx, AF_INET); - arp_announce(astate); - script_runreason(ifp, "IPV4LL"); - dhcpcd_daemonise(ifp->ctx); -} - -static void -ipv4ll_announced(struct arp_state *astate) +ipv4ll_announced_arp(struct arp_state *astate) { struct ipv4ll_state *state = IPV4LL_STATE(astate->iface); state->conflicts = 0; - /* Need to keep the arp state so we can defend our IP. */ -} - -static void -ipv4ll_probe(void *arg) -{ - -#ifdef IN_IFF_TENTATIVE - ipv4ll_probed(arg); -#else - arp_probe(arg); -#endif -} - -static void -ipv4ll_conflicted(struct arp_state *astate, const struct arp_msg *amsg) -{ - struct interface *ifp; - struct ipv4ll_state *state; -#ifdef IN_IFF_DUPLICATED - struct ipv4_addr *ia; +#ifdef KERNEL_RFC5227 + arp_free(astate); #endif - - assert(astate != NULL); - assert(astate->iface != NULL); - ifp = astate->iface; - state = IPV4LL_STATE(ifp); - assert(state != NULL); - - /* - * NULL amsg means kernel detected DAD. - * We always fail on matching sip. - * We only fail on matching tip and we haven't added that address yet. - */ - if (amsg == NULL || - amsg->sip.s_addr == astate->addr.s_addr || - (amsg->sip.s_addr == 0 && amsg->tip.s_addr == astate->addr.s_addr - && ipv4_iffindaddr(ifp, &amsg->tip, NULL) == NULL)) - astate->failed = astate->addr; - else - return; - - arp_report_conflicted(astate, amsg); - - if (state->addr != NULL && - astate->failed.s_addr == state->addr->addr.s_addr) - { -#ifdef KERNEL_RFC5227 - logwarnx("%s: IPv4LL defence failed for %s", - ifp->name, state->addr->saddr); -#else - struct timespec now, defend; - - /* RFC 3927 Section 2.5 says a defence should - * broadcast an ARP announcement. - * Because the kernel will also unicast a reply to the - * hardware address which requested the IP address - * the other IPv4LL client will receieve two ARP - * messages. - * If another conflict happens within DEFEND_INTERVAL - * then we must drop our address and negotiate a new one. */ - defend.tv_sec = state->defend.tv_sec + DEFEND_INTERVAL; - defend.tv_nsec = state->defend.tv_nsec; - clock_gettime(CLOCK_MONOTONIC, &now); - if (timespeccmp(&defend, &now, >)) - logwarnx("%s: IPv4LL %d second defence failed for %s", - ifp->name, DEFEND_INTERVAL, state->addr->saddr); - else if (arp_request(ifp, - state->addr->addr.s_addr, state->addr->addr.s_addr) == -1) - logerr(__func__); - else { - logdebugx("%s: defended IPv4LL address %s", - ifp->name, state->addr->saddr); - state->defend = now; - return; - } -#endif - ipv4_deladdr(state->addr, 1); - state->down = 1; - state->addr = NULL; - rt_build(ifp->ctx, AF_INET); - script_runreason(ifp, "IPV4LL"); - } - -#ifdef IN_IFF_DUPLICATED - ia = ipv4_iffindaddr(ifp, &astate->addr, NULL); - if (ia != NULL && ia->addr_flags & IN_IFF_DUPLICATED) - ipv4_deladdr(ia, 1); -#endif - - arp_cancel(astate); - if (++state->conflicts == MAX_CONFLICTS) - logerr("%s: failed to acquire an IPv4LL address", - ifp->name); - state->pickedaddr.s_addr = ipv4ll_pickaddr(astate); - astate->addr = state->pickedaddr; - eloop_timeout_add_sec(ifp->ctx->eloop, - state->conflicts >= MAX_CONFLICTS ? - RATE_LIMIT_INTERVAL : PROBE_WAIT, - ipv4ll_probe, astate); } static void @@ -342,16 +199,144 @@ state->arp = NULL; } -void -ipv4ll_start(void *arg) +static void +ipv4ll_not_found(struct interface *ifp) +{ + struct ipv4ll_state *state; + struct ipv4_addr *ia; +#ifdef KERNEL_RFC5227 + struct arp_state *astate; +#endif + + state = IPV4LL_STATE(ifp); + assert(state != NULL); + + ia = ipv4_iffindaddr(ifp, &state->pickedaddr, &inaddr_llmask); +#ifdef IN_IFF_NOTREADY + if (ia == NULL || ia->addr_flags & IN_IFF_NOTREADY) +#endif + loginfox("%s: using IPv4LL address %s", + ifp->name, inet_ntoa(state->pickedaddr)); + if (ia == NULL) { + if (ifp->ctx->options & DHCPCD_TEST) + goto test; + ia = ipv4_addaddr(ifp, &state->pickedaddr, + &inaddr_llmask, &inaddr_llbcast); + } + if (ia == NULL) + return; +#ifdef IN_IFF_NOTREADY + if (ia->addr_flags & IN_IFF_NOTREADY) + return; + logdebugx("%s: DAD completed for %s", ifp->name, ia->saddr); +#endif +test: + state->addr = ia; + state->down = false; + if (ifp->ctx->options & DHCPCD_TEST) { + script_runreason(ifp, "TEST"); + eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS); + return; + } + rt_build(ifp->ctx, AF_INET); +#ifdef KERNEL_RFC5227 + astate = arp_new(ifp, &ia->addr); + if (astate != NULL) { + astate->announced_cb = ipv4ll_announced_arp; + astate->free_cb = ipv4ll_arpfree; + arp_announce(astate); + } +#else + arp_announce(state->arp); +#endif + script_runreason(ifp, "IPV4LL"); + dhcpcd_daemonise(ifp->ctx); +} + +static void +ipv4ll_startifp(void *arg) +{ + struct interface *ifp = arg; + struct ipv4ll_state *state; + + state = IPV4LL_STATE(ifp); + ipv4ll_start1(ifp, state->arp); +} + +static void +ipv4ll_found(struct interface *ifp) +{ + struct ipv4ll_state *state = IPV4LL_STATE(ifp); + + arp_cancel(state->arp); + if (++state->conflicts == MAX_CONFLICTS) + logerr("%s: failed to acquire an IPv4LL address", + ifp->name); + eloop_timeout_add_sec(ifp->ctx->eloop, + state->conflicts >= MAX_CONFLICTS ? + RATE_LIMIT_INTERVAL : PROBE_WAIT, + ipv4ll_startifp, ifp); +} + +static void +ipv4ll_defend_failed(struct interface *ifp) +{ + struct ipv4ll_state *state = IPV4LL_STATE(ifp); + + ipv4_deladdr(state->addr, 1); + state->down = true; + state->addr = NULL; + if_initrt(ifp->ctx, AF_INET); + rt_build(ifp->ctx, AF_INET); + script_runreason(ifp, "IPV4LL"); + ipv4ll_start1(ifp, state->arp); +} + +#ifndef KERNEL_RFC5227 +static void +ipv4ll_not_found_arp(struct arp_state *astate) { struct interface *ifp; struct ipv4ll_state *state; - struct arp_state *astate; - struct ipv4_addr *ia; + + assert(astate != NULL); + assert(astate->iface != NULL); + + ifp = astate->iface; + state = IPV4LL_STATE(ifp); + assert(state != NULL); + assert(state->arp == astate); + ipv4ll_not_found_arp(astate); +} + +static void +ipv4ll_found_arp(struct arp_state *astate, __unused const struct arp_msg *amsg) +{ + struct interface *ifp = astate->iface; + struct ipv4ll_state *state = IPV4LL_STATE(ifp); - assert(arg != NULL); - ifp = arg; + assert(state->arp == astate); + ipv4ll_found(ifp); +} + +static void +ipv4ll_defend_failed_arp(struct arp_state *astate) +{ + struct ipv4ll_state *state = IPV4LL_STATE(astate->iface); + + assert(state->arp == astate); + ipv4ll_defend_failed(astate->iface); +} +#endif + +static void +ipv4ll_start1(struct interface *ifp, struct arp_state *astate) +{ + struct ipv4ll_state *state; + struct ipv4_addr *ia; + bool repick; + + assert(ifp != NULL); if ((state = IPV4LL_STATE(ifp)) == NULL) { ifp->if_data[IF_DATA_IPV4LL] = calloc(1, sizeof(*state)); if ((state = IPV4LL_STATE(ifp)) == NULL) { @@ -387,16 +372,25 @@ state->seeded = true; } - if (state->arp != NULL) - return; - if ((astate = arp_new(ifp, NULL)) == NULL) - return; +#ifndef KERNEL_RFC5227 + if (astate == NULL) { + if (state->arp != NULL) + return; + if ((astate = arp_new(ifp, NULL)) == NULL) + return; + astate->found_cb = ipv4ll_found_arp; + astate->not_found_cb = ipv4ll_not_found_arp; + astate->announced_cb = ipv4ll_announced_arp; + astate->defend_failed_cb = ipv4ll_defend_failed_arp; + astate->free_cb = ipv4ll_arpfree; + state->arp = astate; + } else + assert(state->arp == astate); +#else + UNUSED(astate); +#endif - state->arp = astate; - astate->probed_cb = ipv4ll_probed; - astate->announced_cb = ipv4ll_announced; - astate->conflicted_cb = ipv4ll_conflicted; - astate->free_cb = ipv4ll_arpfree; + state->down = true; /* Find the previosuly used address. */ if (state->pickedaddr.s_addr != INADDR_ANY) @@ -408,38 +402,56 @@ if (ia == NULL) ia = ipv4_iffindlladdr(ifp); -#ifdef IN_IFF_TENTATIVE + repick = false; +#ifdef IN_IFF_DUPLICATED if (ia != NULL && ia->addr_flags & IN_IFF_DUPLICATED) { + state->pickedaddr = ia->addr; /* So it's not picked again. */ + repick = true; ipv4_deladdr(ia, 0); ia = NULL; } #endif + state->addr = ia; if (ia != NULL) { - state->pickedaddr = astate->addr = ia->addr; + state->pickedaddr = ia->addr; +#ifndef KERNEL_RFC5227 + astate->addr = ia->addr; +#endif #ifdef IN_IFF_TENTATIVE if (ia->addr_flags & (IN_IFF_TENTATIVE | IN_IFF_DETACHED)) { loginfox("%s: waiting for DAD to complete on %s", ifp->name, inet_ntoa(ia->addr)); return; } +#endif +#ifdef IN_IFF_DUPLICATED loginfox("%s: using IPv4LL address %s", ifp->name, ia->saddr); #endif - ipv4ll_probed(astate); + ipv4ll_not_found(ifp); return; } loginfox("%s: probing for an IPv4LL address", ifp->name); - if (state->pickedaddr.s_addr == INADDR_ANY) - state->pickedaddr.s_addr = ipv4ll_pickaddr(astate); + if (repick || state->pickedaddr.s_addr == INADDR_ANY) + state->pickedaddr.s_addr = ipv4ll_pickaddr(ifp); +#ifndef KERNEL_RFC5227 astate->addr = state->pickedaddr; -#ifdef IN_IFF_TENTATIVE - ipv4ll_probed(astate); +#endif +#ifdef IN_IFF_DUPLICATED + ipv4ll_not_found(ifp); #else arp_probe(astate); #endif } +void +ipv4ll_start(void *arg) +{ + + ipv4ll_start1(arg, NULL); +} + static void ipv4ll_freearp(struct interface *ifp) { @@ -451,7 +463,6 @@ eloop_timeout_delete(ifp->ctx->eloop, NULL, state->arp); arp_free(state->arp); - state->arp = NULL; } void @@ -465,9 +476,7 @@ ipv4ll_freearp(ifp); -#ifndef IN_IFF_TENATIVE if ((ifp->options->options & DHCPCD_NODROP) == DHCPCD_NODROP) -#endif return; state = IPV4LL_STATE(ifp); @@ -542,4 +551,34 @@ return 0; } #endif + +void +ipv4ll_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid) +{ + struct interface *ifp; + struct ipv4ll_state *state; + + ifp = ia->iface; + state = IPV4LL_STATE(ifp); + if (state == NULL || state->addr == NULL || + !IN_ARE_ADDR_EQUAL(&state->addr->addr, &ia->addr)) + return; + + if (cmd == RTM_DELADDR) { + loginfox("%s: pid %d deleted IP address %s", + ifp->name, pid, ia->saddr); + ipv4ll_defend_failed(ifp); + } + +#ifdef IN_IFF_DUPLICATED + if (cmd != RTM_NEWADDR) + return; + if (!(ia->addr_flags & IN_IFF_NOTUSEABLE)) + ipv4ll_not_found(ifp); + else if (ia->addr_flags & IN_IFF_DUPLICATED) { + logerrx("%s: DAD detected %s", ifp->name, ia->saddr); + ipv4_deladdr(state->addr, 1); + ipv4ll_found(ifp); + } #endif +}
--- a/src/ipv4ll.h Wed Apr 17 22:18:39 2019 +0000 +++ b/src/ipv4ll.h Sat May 04 11:05:17 2019 +0100 @@ -43,11 +43,10 @@ struct in_addr pickedaddr; struct ipv4_addr *addr; struct arp_state *arp; - unsigned int conflicts; - struct timespec defend; char randomstate[128]; bool seeded; - uint8_t down; + bool down; + size_t conflicts; }; #define IPV4LL_STATE(ifp) \ @@ -64,6 +63,7 @@ void ipv4ll_start(void *); void ipv4ll_claimed(void *); void ipv4ll_handle_failure(void *); +void ipv4ll_handleifa(int, struct ipv4_addr *, pid_t pid); #ifdef HAVE_ROUTE_METRIC int ipv4ll_recvrt(int, const struct rt *); #endif
--- a/src/ipv6.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/ipv6.c Sat May 04 11:05:17 2019 +0100 @@ -137,7 +137,9 @@ return -1; TAILQ_INIT(ctx->ra_routers); +#ifndef __sun ctx->nd_fd = -1; +#endif ctx->dhcp6_fd = -1; return 0; }
--- a/src/ipv6.h Wed Apr 17 22:18:39 2019 +0000 +++ b/src/ipv6.h Sat May 04 11:05:17 2019 +0100 @@ -44,9 +44,6 @@ # endif #endif -#define ALLNODES "ff02::1" -#define ALLROUTERS "ff02::2" - #define EUI64_GBIT 0x01 #define EUI64_UBIT 0x02 #define EUI64_TO_IFID(in6) do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (0) @@ -77,6 +74,17 @@ (((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 ) #endif +#ifndef IN6ADDR_LINKLOCAL_ALLNODES_INIT +#define IN6ADDR_LINKLOCAL_ALLNODES_INIT \ + {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} +#endif +#ifndef IN6ADDR_LINKLOCAL_ALLROUTERS_INIT +#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \ + {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}} +#endif + /* * BSD kernels don't inform userland of DAD results. * See the discussion here:
--- a/src/ipv6nd.c Wed Apr 17 22:18:39 2019 +0000 +++ b/src/ipv6nd.c Sat May 04 11:05:17 2019 +0100 @@ -190,54 +190,106 @@ } static int -ipv6nd_open(struct dhcpcd_ctx *ctx) +ipv6nd_open0(void) { - int on; + int s, on; struct icmp6_filter filt; - if (ctx->nd_fd != -1) - return ctx->nd_fd; #define SOCK_FLAGS SOCK_CLOEXEC | SOCK_NONBLOCK - ctx->nd_fd = xsocket(PF_INET6, SOCK_RAW | SOCK_FLAGS, IPPROTO_ICMPV6); + s = xsocket(PF_INET6, SOCK_RAW | SOCK_FLAGS, IPPROTO_ICMPV6); #undef SOCK_FLAGS - if (ctx->nd_fd == -1) + if (s == -1) return -1; /* RFC4861 4.1 */ on = 255; - if (setsockopt(ctx->nd_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &on, sizeof(on)) == -1) goto eexit; on = 1; - if (setsockopt(ctx->nd_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, - &on, sizeof(on)) == -1) - goto eexit; - - on = 1; - if (setsockopt(ctx->nd_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)) == -1) goto eexit; ICMP6_FILTER_SETBLOCKALL(&filt); ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filt); ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); - if (setsockopt(ctx->nd_fd, IPPROTO_ICMPV6, ICMP6_FILTER, + if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, sizeof(filt)) == -1) goto eexit; - eloop_event_add(ctx->eloop, ctx->nd_fd, ipv6nd_handledata, ctx); - return ctx->nd_fd; + return s; eexit: - if (ctx->nd_fd != -1) { - eloop_event_delete(ctx->eloop, ctx->nd_fd); - close(ctx->nd_fd); - ctx->nd_fd = -1; - } + close(s); return -1; } +#ifdef __sun +static int +ipv6nd_open(struct interface *ifp) +{ + int s; + struct ipv6_mreq mreq = { + .ipv6mr_multiaddr = IN6ADDR_LINKLOCAL_ALLNODES_INIT, + .ipv6mr_interface = ifp->index + }; + struct rs_state *state = RS_STATE(ifp); + uint_t ifindex = ifp->index; + + if (state->nd_fd != -1) + return state->nd_fd; + + s = ipv6nd_open0(); + if (s == -1) + return -1; + + if (setsockopt(s, IPPROTO_IPV6, IPV6_BOUND_IF, + &ifindex, sizeof(ifindex)) == -1) + { + close(s); + return -1; + } + + if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq, sizeof(mreq)) == -1) + { + close(s); + return -1; + } + + state->nd_fd = s; + eloop_event_add(ifp->ctx->eloop, s, ipv6nd_handledata, ifp); + return s; +} +#else +static int +ipv6nd_open(struct dhcpcd_ctx *ctx) +{ + int s, on; + + if (ctx->nd_fd != -1) + return ctx->nd_fd; + + s = ipv6nd_open0(); + if (s == -1) + return -1; + + on = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, + &on, sizeof(on)) == -1) + { + close(s); + return -1; + } + + ctx->nd_fd = s; + eloop_event_add(ctx->eloop, s, ipv6nd_handledata, ctx); + return s; +} +#endif + static int ipv6nd_makersprobe(struct interface *ifp) { @@ -273,9 +325,12 @@ ipv6nd_sendrsprobe(void *arg) { struct interface *ifp = arg; - struct dhcpcd_ctx *ctx; struct rs_state *state = RS_STATE(ifp); - struct sockaddr_in6 dst = { .sin6_family = AF_INET6 }; + struct sockaddr_in6 dst = { + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT, + .sin6_scope_id = ifp->index, + }; struct iovec iov = { .iov_base = state->rs, .iov_len = state->rslen }; unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 }; struct msghdr msg = { @@ -285,6 +340,7 @@ }; struct cmsghdr *cm; struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index }; + int s; if (ipv6_linklocal(ifp) == NULL) { logdebugx("%s: delaying Router Solicitation for LL address", @@ -296,13 +352,6 @@ #ifdef HAVE_SA_LEN dst.sin6_len = sizeof(dst); #endif - dst.sin6_scope_id = ifp->index; - if (inet_pton(AF_INET6, ALLROUTERS, &dst.sin6_addr) != 1) { - logerr(__func__); - return; - } - - ctx = ifp->ctx; /* Set the outbound interface */ cm = CMSG_FIRSTHDR(&msg); @@ -314,7 +363,12 @@ memcpy(CMSG_DATA(cm), &pi, sizeof(pi)); logdebugx("%s: sending Router Solicitation", ifp->name); - if (sendmsg(ctx->nd_fd, &msg, 0) == -1) { +#ifdef __sun + s = state->nd_fd; +#else + s = ifp->ctx->nd_fd; +#endif + if (sendmsg(s, &msg, 0) == -1) { logerr(__func__); /* Allow IPv6ND to continue .... at most a few errors * would be logged. @@ -342,6 +396,7 @@ struct dhcpcd_ctx *ctx = ifp->ctx; struct sockaddr_in6 dst = { .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LINKLOCAL_ALLNODES_INIT, .sin6_scope_id = ifp->index, }; struct iovec iov = { .iov_base = ia->na, .iov_len = ia->na_len }; @@ -354,6 +409,7 @@ struct cmsghdr *cm; struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index }; const struct rs_state *state = RS_CSTATE(ifp); + int s; if (state == NULL || ifp->carrier <= LINK_DOWN) goto freeit; @@ -361,10 +417,6 @@ #ifdef SIN6_LEN dst.sin6_len = sizeof(dst); #endif - if (inet_pton(AF_INET6, ALLNODES, &dst.sin6_addr) != 1) { - logerr(__func__); - return; - } /* Set the outbound interface. */ cm = CMSG_FIRSTHDR(&msg); @@ -373,10 +425,20 @@ cm->cmsg_type = IPV6_PKTINFO; cm->cmsg_len = CMSG_LEN(sizeof(pi)); memcpy(CMSG_DATA(cm), &pi, sizeof(pi)); - logdebugx("%s: sending NA for %s", ifp->name, ia->saddr); - if (sendmsg(ctx->nd_fd, &msg, 0) == -1) +#ifdef __sun + s = state->nd_fd; +#else + s = ctx->nd_fd; +#endif + if (sendmsg(s, &msg, 0) == -1) +#ifdef __OpenBSD__ +/* This isn't too critical as they don't support IPv6 address sharing */ +#warning Cannot send NA messages on OpenBSD + logdebug(__func__); +#else logerr(__func__); +#endif if (++ia->na_count < MAX_NEIGHBOR_ADVERTISEMENT) { eloop_timeout_add_sec(ctx->eloop, @@ -619,6 +681,11 @@ if (state == NULL) return 0; + ctx = ifp->ctx; +#ifdef __sun + eloop_event_delete(ctx->eloop, state->nd_fd); + close(state->nd_fd); +#endif free(state->rs); free(state); ifp->if_data[IF_DATA_IPV6ND] = NULL; @@ -630,9 +697,9 @@ } } +#ifndef __sun /* If we don't have any more IPv6 enabled interfaces, * close the global socket and release resources */ - ctx = ifp->ctx; TAILQ_FOREACH(ifp, ctx->ifaces, next) { if (RS_STATE(ifp)) break; @@ -644,6 +711,7 @@ ctx->nd_fd = -1; } } +#endif return n; } @@ -1654,6 +1722,7 @@ ipv6nd_handledata(void *arg) { struct dhcpcd_ctx *ctx; + int s; struct sockaddr_in6 from; unsigned char buf[64 * 1024]; /* Maximum ICMPv6 size */ struct iovec iov = { @@ -1672,8 +1741,18 @@ struct icmp6_hdr *icp; struct interface *ifp; +#ifdef __sun + struct rs_state *state; + + ifp = arg; + state = RS_STATE(ifp); + ctx = ifp->ctx; + s = state->nd_fd; +#else ctx = arg; - len = recvmsg(ctx->nd_fd, &msg, 0); + s = ctx->nd_fd; +#endif + len = recvmsg(s, &msg, 0); if (len == -1) { logerr(__func__); return; @@ -1684,11 +1763,15 @@ return; } +#ifdef __sun + if_findifpfromcmsg(ctx, &msg, &hoplimit); +#else ifp = if_findifpfromcmsg(ctx, &msg, &hoplimit); if (ifp == NULL) { logerr(__func__); return; } +#endif /* Don't do anything if the user hasn't configured it. */ if (ifp->active != IF_ACTIVE_USER || @@ -1720,11 +1803,6 @@ struct rs_state *state; loginfox("%s: soliciting an IPv6 router", ifp->name); - if (ipv6nd_open(ifp->ctx) == -1) { - logerr(__func__); - return; - } - state = RS_STATE(ifp); if (state == NULL) { ifp->if_data[IF_DATA_IPV6ND] = calloc(1, sizeof(*state)); @@ -1733,8 +1811,23 @@ logerr(__func__); return; } +#ifdef __sun + state->nd_fd = -1; +#endif } +#ifdef __sun + if (ipv6nd_open(ifp) == -1) { + logerr(__func__); + return; + } +#else + if (ipv6nd_open(ifp->ctx) == -1) { + logerr(__func__); + return; + } +#endif + /* Always make a new probe as the underlying hardware * address could have changed. */ ipv6nd_makersprobe(ifp);
