Module Name: src Committed By: roy Date: Wed Feb 12 19:23:13 UTC 2025
Modified Files: src/external/bsd/dhcpcd/dist/src: bpf.c dhcp.c dhcp6.c dhcpcd.c if-bsd.c if-options.c ipv6.c ipv6.h ipv6nd.c logerr.c privsep.c script.c Log Message: Sync with dhcpcd-10.2.0 To generate a diff of this commit: cvs rdiff -u -r1.22 -r1.23 src/external/bsd/dhcpcd/dist/src/bpf.c cvs rdiff -u -r1.51 -r1.52 src/external/bsd/dhcpcd/dist/src/dhcp.c cvs rdiff -u -r1.34 -r1.35 src/external/bsd/dhcpcd/dist/src/dhcp6.c cvs rdiff -u -r1.56 -r1.57 src/external/bsd/dhcpcd/dist/src/dhcpcd.c cvs rdiff -u -r1.31 -r1.32 src/external/bsd/dhcpcd/dist/src/if-bsd.c \ src/external/bsd/dhcpcd/dist/src/ipv6nd.c cvs rdiff -u -r1.37 -r1.38 src/external/bsd/dhcpcd/dist/src/if-options.c cvs rdiff -u -r1.19 -r1.20 src/external/bsd/dhcpcd/dist/src/ipv6.c \ src/external/bsd/dhcpcd/dist/src/privsep.c cvs rdiff -u -r1.14 -r1.15 src/external/bsd/dhcpcd/dist/src/ipv6.h \ src/external/bsd/dhcpcd/dist/src/logerr.c cvs rdiff -u -r1.17 -r1.18 src/external/bsd/dhcpcd/dist/src/script.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/external/bsd/dhcpcd/dist/src/bpf.c diff -u src/external/bsd/dhcpcd/dist/src/bpf.c:1.22 src/external/bsd/dhcpcd/dist/src/bpf.c:1.23 --- src/external/bsd/dhcpcd/dist/src/bpf.c:1.22 Mon Dec 18 16:03:26 2023 +++ src/external/bsd/dhcpcd/dist/src/bpf.c Wed Feb 12 19:23:13 2025 @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd: BPF arp and bootp filtering - * Copyright (c) 2006-2023 Roy Marples <r...@marples.name> + * Copyright (c) 2006-2024 Roy Marples <r...@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without Index: src/external/bsd/dhcpcd/dist/src/dhcp.c diff -u src/external/bsd/dhcpcd/dist/src/dhcp.c:1.51 src/external/bsd/dhcpcd/dist/src/dhcp.c:1.52 --- src/external/bsd/dhcpcd/dist/src/dhcp.c:1.51 Fri May 24 11:30:29 2024 +++ src/external/bsd/dhcpcd/dist/src/dhcp.c Wed Feb 12 19:23:13 2025 @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2023 Roy Marples <r...@marples.name> + * Copyright (c) 2006-2024 Roy Marples <r...@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -49,6 +49,7 @@ #include <errno.h> #include <fcntl.h> #include <inttypes.h> +#include <stdalign.h> #include <stdbool.h> #include <stddef.h> #include <stdio.h> @@ -97,6 +98,8 @@ __CTASSERT(sizeof(struct ip) == 20); __CTASSERT(sizeof(struct udphdr) == 8); __CTASSERT(sizeof(struct bootp) == 300); +#define IP_UDP_SIZE sizeof(struct ip) + sizeof(struct udphdr) +#define BOOTP_MIN_MTU IP_UDP_SIZE + sizeof(struct bootp) struct dhcp_op { uint8_t value; @@ -302,7 +305,7 @@ get_option_uint32(struct dhcpcd_ctx *ctx uint32_t d; p = get_option(ctx, bootp, bootp_len, option, &len); - if (!p || len < (ssize_t)sizeof(d)) + if (!p || len != (ssize_t)sizeof(d)) return -1; memcpy(&d, p, sizeof(d)); if (i) @@ -319,7 +322,7 @@ get_option_uint16(struct dhcpcd_ctx *ctx uint16_t d; p = get_option(ctx, bootp, bootp_len, option, &len); - if (!p || len < (ssize_t)sizeof(d)) + if (!p || len != (ssize_t)sizeof(d)) return -1; memcpy(&d, p, sizeof(d)); if (i) @@ -335,7 +338,7 @@ get_option_uint8(struct dhcpcd_ctx *ctx, size_t len; p = get_option(ctx, bootp, bootp_len, option, &len); - if (!p || len < (ssize_t)sizeof(*p)) + if (!p || len != (ssize_t)sizeof(*p)) return -1; if (i) *i = *(p); @@ -676,6 +679,8 @@ dhcp_get_mtu(const struct interface *ifp get_option_uint16(ifp->ctx, &mtu, state->new, state->new_len, DHO_MTU) == -1) return 0; + if (mtu < IPV4_MMTU) + return IPV4_MMTU; return mtu; } @@ -719,6 +724,77 @@ dhcp_message_add_addr(struct bootp *boot return 0; } +#ifndef SMALL +struct rfc3396_ctx { + uint8_t code; + uint8_t *len; + uint8_t **buf; + size_t buflen; +}; + +/* Encode data as a DHCP Long Option, RFC 3396. */ +/* NOTE: Wireshark does not decode this correctly + * when the option overflows the boundary and another option + * is created to hold the resta of the data. + * Tested against Wireshark-4.4.1 */ +#define RFC3396_BOUNDARY 255UL +static ssize_t +rfc3396_write(struct rfc3396_ctx *ctx, void *data, size_t len) +{ + uint8_t *datap = data; + size_t wlen, left, r = 0; + + while (len != 0) { + if (ctx->len == NULL || *ctx->len == RFC3396_BOUNDARY) { + if (ctx->buflen < 2) { + errno = ENOMEM; + return -1; + } + *(*ctx->buf)++ = ctx->code; + ctx->len = (*ctx->buf)++; + *ctx->len = 0; + ctx->buflen -= 2; + r += 2; + } + + wlen = len < RFC3396_BOUNDARY ? len : RFC3396_BOUNDARY; + left = RFC3396_BOUNDARY - *ctx->len; + if (left < wlen) + wlen = left; + if (ctx->buflen < wlen) { + errno = ENOMEM; + return -1; + } + + memcpy(*ctx->buf, datap, wlen); + datap += wlen; + *ctx->buf += wlen; + ctx->buflen -= wlen; + *ctx->len = (uint8_t)(*ctx->len + wlen); + len -= wlen; + r += wlen; + } + + return (ssize_t)r; +} + +static ssize_t +rfc3396_write_byte(struct rfc3396_ctx *ctx, uint8_t byte) +{ + + return rfc3396_write(ctx, &byte, sizeof(byte)); +} + +static uint8_t * +rfc3396_zero(struct rfc3396_ctx *ctx) { + uint8_t *zerop = *ctx->buf, zero = 0; + + if (rfc3396_write(ctx, &zero, sizeof(zero)) == -1) + return NULL; + return zerop; +} +#endif + static ssize_t make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type) { @@ -734,25 +810,29 @@ make_message(struct bootp **bootpm, cons const struct dhcp_lease *lease = &state->lease; char hbuf[HOSTNAME_MAX_LEN + 1]; const char *hostname; - const struct vivco *vivco; int mtu; #ifdef AUTH uint8_t *auth, auth_len; #endif - if ((mtu = if_getmtu(ifp)) == -1) + /* We could take the DHCPv6 approach and work out the + * message length up front rather than this big hammer approach. */ + if ((mtu = if_getmtu(ifp)) == -1) { logerr("%s: if_getmtu", ifp->name); - else if (mtu < MTU_MIN) { - if (if_setmtu(ifp, MTU_MIN) == -1) - logerr("%s: if_setmtu", ifp->name); - mtu = MTU_MIN; + return -1; + } + if ((size_t)mtu < BOOTP_MIN_MTU) { + logerr("%s: interface mtu is too small (%d<%zu)", + ifp->name, mtu, BOOTP_MIN_MTU); + return -1; } - if (ifo->options & DHCPCD_BOOTP) - bootp = calloc(1, sizeof (*bootp)); - else + if (ifo->options & DHCPCD_BOOTP) { + bootp = calloc(1, sizeof(*bootp)); + } else { /* Make the maximal message we could send */ - bootp = calloc(1, (size_t)(mtu - IP_UDP_SIZE)); + bootp = calloc(1, (size_t)mtu - IP_UDP_SIZE); + } if (bootp == NULL) return -1; @@ -774,7 +854,7 @@ make_message(struct bootp **bootpm, cons } if (ifo->options & DHCPCD_BROADCAST && - bootp->ciaddr == 0 && + bootp->ciaddr == INADDR_ANY && type != DHCP_DECLINE && type != DHCP_RELEASE) bootp->flags = htons(BROADCAST_FLAG); @@ -797,7 +877,7 @@ make_message(struct bootp **bootpm, cons return sizeof(*bootp); p = bootp->vend; - e = (uint8_t *)bootp + (mtu - IP_UDP_SIZE) - 1; /* -1 for DHO_END */ + e = (uint8_t *)bootp + ((size_t)mtu - IP_UDP_SIZE - 1/* DHO_END */); ul = htonl(MAGIC_COOKIE); memcpy(p, &ul, sizeof(ul)); @@ -924,7 +1004,7 @@ make_message(struct bootp **bootpm, cons AREA_CHECK(2); *p++ = DHO_MAXMESSAGESIZE; *p++ = 2; - sz = htons((uint16_t)(mtu - IP_UDP_SIZE)); + sz = htons((uint16_t)((size_t)mtu - IP_UDP_SIZE)); memcpy(p, &sz, 2); p += 2; } @@ -1058,33 +1138,77 @@ make_message(struct bootp **bootpm, cons p += ifo->mudurl[0] + 1; } +#ifndef SMALL if (ifo->vivco_len && !has_option_mask(ifo->nomask, DHO_VIVCO)) { - AREA_CHECK(sizeof(ul)); - *p++ = DHO_VIVCO; - lp = p++; - *lp = sizeof(ul); - ul = htonl(ifo->vivco_en); - memcpy(p, &ul, sizeof(ul)); - p += sizeof(ul); - for (i = 0, vivco = ifo->vivco; - i < ifo->vivco_len; - i++, vivco++) - { - AREA_FIT(vivco->len); - if (vivco->len + 2 + *lp > 255) { - logerrx("%s: VIVCO option too big", - ifp->name); - free(bootp); - return -1; - } - *p++ = (uint8_t)vivco->len; - memcpy(p, vivco->data, vivco->len); - p += vivco->len; + struct vivco *vivco = ifo->vivco; + size_t vlen = ifo->vivco_len; + struct rfc3396_ctx rctx = { + .code = DHO_VIVCO, + .buf = &p, + .buflen = AREA_LEFT, + }; + + for (; vlen > 0; vivco++, vlen--) { + ul = htonl(vivco->en); + if (rfc3396_write(&rctx, &ul, sizeof(ul)) == -1) + goto toobig; + lp = rfc3396_zero(&rctx); + if (lp == NULL) + goto toobig; + if (rfc3396_write_byte(&rctx, + (uint8_t)vivco->len) == -1) + goto toobig; + if (rfc3396_write(&rctx, + vivco->data, vivco->len) == -1) + goto toobig; *lp = (uint8_t)(*lp + vivco->len + 1); } } + + if (ifo->vsio_len && + !has_option_mask(ifo->nomask, DHO_VIVSO)) + { + struct vsio *vso = ifo->vsio; + size_t vlen = ifo->vsio_len; + struct vsio_so *so; + size_t slen; + struct rfc3396_ctx rctx = { + .code = DHO_VIVSO, + .buf = &p, + .buflen = AREA_LEFT, + }; + + for (; vlen > 0; vso++, vlen--) { + if (vso->so_len == 0) + continue; + + so = vso->so; + slen = vso->so_len; + + ul = htonl(vso->en); + if (rfc3396_write(&rctx, &ul, sizeof(ul)) == -1) + goto toobig; + lp = rfc3396_zero(&rctx); + if (lp == NULL) + goto toobig; + + for (; slen > 0; so++, slen--) { + if (rfc3396_write_byte(&rctx, + (uint8_t)so->opt) == -1) + goto toobig; + if (rfc3396_write_byte(&rctx, + (uint8_t)so->len) == -1) + goto toobig; + if (rfc3396_write(&rctx, + so->data, so->len) == -1) + goto toobig; + *lp = (uint8_t)(*lp + so->len + 2); + } + } + } +#endif #ifdef AUTH if ((ifo->auth.options & DHCPCD_AUTH_SENDREQUIRE) != @@ -1409,9 +1533,15 @@ get_lease(struct interface *ifp, const struct ipv4_addr *ia; ia = ipv4_iffindaddr(ifp, &lease->addr, NULL); - assert(ia != NULL); - lease->mask = ia->mask; - lease->brd = ia->brd; + if (ia == NULL) { + lease->mask.s_addr = + ipv4_getnetmask(lease->addr.s_addr); + lease->brd.s_addr = + lease->addr.s_addr | ~lease->mask.s_addr; + } else { + lease->mask = ia->mask; + lease->brd = ia->brd; + } } } else { if (get_option_addr(ctx, &lease->mask, bootp, len, @@ -1645,7 +1775,7 @@ dhcp_makeudppacket(size_t *sz, const uin ip->ip_p = IPPROTO_UDP; ip->ip_src.s_addr = source.s_addr; - if (dest.s_addr == 0) + if (dest.s_addr == INADDR_ANY) ip->ip_dst.s_addr = INADDR_BROADCAST; else ip->ip_dst.s_addr = dest.s_addr; @@ -2233,6 +2363,46 @@ dhcp_arp_found(struct arp_state *astate, #endif /* ARP */ +static void +dhcp_bound(struct interface *ifp, uint8_t old_state) +{ + struct dhcpcd_ctx *ctx = ifp->ctx; + struct dhcp_state *state = D_STATE(ifp); + + /* Close the BPF filter as we can now receive DHCP messages + * on a UDP socket. */ + dhcp_closebpf(ifp); + + /* If not in manager mode, open an address specific socket. */ + if (ctx->options & DHCPCD_MANAGER || + ifp->options->options & DHCPCD_STATIC || + (state->old != NULL && + state->old->yiaddr == state->new->yiaddr) || + (old_state & STATE_ADDED && !(old_state & STATE_FAKE))) + return; + + dhcp_closeinet(ifp); +#ifdef PRIVSEP + if (IN_PRIVSEP_SE(ctx)) { + if (ps_inet_openbootp(state->addr) == -1) + logerr(__func__); + return; + } +#endif + + state->udp_rfd = dhcp_openudp(&state->addr->addr); + if (state->udp_rfd == -1) { + logerr(__func__); + /* We still need to work, so re-open BPF. */ + dhcp_openbpf(ifp); + return; + } + + if (eloop_event_add(ctx->eloop, state->udp_rfd, ELE_READ, + dhcp_handleifudp, ifp) == -1) + logerr("%s: eloop_event_add", __func__); +} + void dhcp_bind(struct interface *ifp) { @@ -2359,7 +2529,18 @@ dhcp_bind(struct interface *ifp) old_state = state->added; - if (!(ifo->options & DHCPCD_CONFIGURE)) { + if (ifo->options & DHCPCD_CONFIGURE) { + /* Add the address */ + if (ipv4_applyaddr(ifp) == NULL) { + /* There was an error adding the address. + * If we are in oneshot, exit with a failure. */ + if (ctx->options & DHCPCD_ONESHOT) { + loginfox("exiting due to oneshot"); + eloop_exit(ctx->eloop, EXIT_FAILURE); + } + return; + } + } else { struct ipv4_addr *ia; script_runreason(ifp, state->reason); @@ -2368,61 +2549,13 @@ dhcp_bind(struct interface *ifp) /* We we are not configuring the address, we need to keep * the BPF socket open if the address does not exist. */ ia = ipv4_iffindaddr(ifp, &state->lease.addr, NULL); - if (ia != NULL) { - state->addr = ia; - state->added = STATE_ADDED; - dhcp_closebpf(ifp); - goto openudp; - } - return; - } - - /* Add the address */ - if (ipv4_applyaddr(ifp) == NULL) { - /* There was an error adding the address. - * If we are in oneshot, exit with a failure. */ - if (ctx->options & DHCPCD_ONESHOT) { - loginfox("exiting due to oneshot"); - eloop_exit(ctx->eloop, EXIT_FAILURE); - } - return; + if (ia == NULL) + return; + state->addr = ia; + state->added = STATE_ADDED; } - /* Close the BPF filter as we can now receive DHCP messages - * on a UDP socket. */ - dhcp_closebpf(ifp); - -openudp: - /* If not in manager mode, open an address specific socket. */ - if (ctx->options & DHCPCD_MANAGER || - ifo->options & DHCPCD_STATIC || - (state->old != NULL && - state->old->yiaddr == state->new->yiaddr && - old_state & STATE_ADDED && !(old_state & STATE_FAKE))) - return; - - dhcp_closeinet(ifp); -#ifdef PRIVSEP - if (IN_PRIVSEP_SE(ctx)) { - if (ps_inet_openbootp(state->addr) == -1) - logerr(__func__); - return; - } -#endif - - state->udp_rfd = dhcp_openudp(&state->addr->addr); - if (state->udp_rfd == -1) { - logerr(__func__); - /* Address sharing without manager mode is not supported. - * It's also possible another DHCP client could be running, - * which is even worse. - * We still need to work, so re-open BPF. */ - dhcp_openbpf(ifp); - return; - } - if (eloop_event_add(ctx->eloop, state->udp_rfd, ELE_READ, - dhcp_handleifudp, ifp) == -1) - logerr("%s: eloop_event_add", __func__); + dhcp_bound(ifp, old_state); } static size_t @@ -2655,43 +2788,11 @@ dhcp_reboot_newopts(struct interface *if } } -#ifdef ARP -static int -dhcp_activeaddr(const struct interface *ifp, const struct in_addr *addr) -{ - const struct interface *ifp1; - const struct dhcp_state *state; - - TAILQ_FOREACH(ifp1, ifp->ctx->ifaces, next) { - if (ifp1 == ifp) - continue; - if ((state = D_CSTATE(ifp1)) == NULL) - continue; - switch(state->state) { - case DHS_REBOOT: - case DHS_RENEW: - case DHS_REBIND: - case DHS_BOUND: - case DHS_INFORM: - break; - default: - continue; - } - if (state->lease.addr.s_addr == addr->s_addr) - return 1; - } - return 0; -} -#endif - static void dhcp_reboot(struct interface *ifp) { struct if_options *ifo; struct dhcp_state *state = D_STATE(ifp); -#ifdef ARP - struct ipv4_addr *ia; -#endif if (state == NULL || state->state == DHS_NONE) return; @@ -2723,25 +2824,11 @@ dhcp_reboot(struct interface *ifp) loginfox("%s: rebinding lease of %s", ifp->name, inet_ntoa(state->lease.addr)); -#ifdef ARP -#ifndef KERNEL_RFC5227 +#if defined(ARP) && !defined(KERNEL_RFC5227) /* Create the DHCP ARP state so we can defend it. */ (void)dhcp_arp_new(ifp, &state->lease.addr); #endif - /* If the address exists on the interface and no other interface - * is currently using it then announce it to ensure this - * interface gets the reply. */ - ia = ipv4_iffindaddr(ifp, &state->lease.addr, NULL); - if (ia != NULL && - !(ifp->ctx->options & DHCPCD_TEST) && -#ifdef IN_IFF_NOTUSEABLE - !(ia->addr_flags & IN_IFF_NOTUSEABLE) && -#endif - dhcp_activeaddr(ifp, &state->lease.addr) == 0) - arp_ifannounceaddr(ifp, &state->lease.addr); -#endif - dhcp_new_xid(ifp); state->lease.server.s_addr = INADDR_ANY; eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); @@ -2769,6 +2856,7 @@ void dhcp_drop(struct interface *ifp, const char *reason) { struct dhcp_state *state = D_STATE(ifp); + struct if_options *ifo = ifp->options; /* dhcp_start may just have been called and we don't yet have a state * but we do have a timeout, so punt it. */ @@ -2785,9 +2873,7 @@ dhcp_drop(struct interface *ifp, const c state->arping_index = -1; #endif - if (ifp->options->options & DHCPCD_RELEASE && - !(ifp->options->options & DHCPCD_INFORM)) - { + if (ifo->options & DHCPCD_RELEASE && !(ifo->options & DHCPCD_INFORM)) { /* Failure to send the release may cause this function to * re-enter so guard by setting the state. */ if (state->state == DHS_RELEASE) @@ -2833,7 +2919,7 @@ dhcp_drop(struct interface *ifp, const c state->new = NULL; state->new_len = 0; state->reason = reason; - if (ifp->options->options & DHCPCD_CONFIGURE) + if (ifo->options & DHCPCD_CONFIGURE) ipv4_applyaddr(ifp); else { state->addr = NULL; @@ -2844,8 +2930,7 @@ dhcp_drop(struct interface *ifp, const c state->old = NULL; state->old_len = 0; state->lease.addr.s_addr = 0; - ifp->options->options &= ~(DHCPCD_CSR_WARNED | - DHCPCD_ROUTER_HOST_ROUTE_WARNED); + ifo->options &= ~(DHCPCD_CSR_WARNED | DHCPCD_ROUTER_HOST_ROUTE_WARNED); /* Close DHCP ports so a changed interface family is picked * up by a new BPF state. */ @@ -2989,7 +3074,7 @@ dhcp_handledhcp(struct interface *ifp, s char *msg; bool bootp_copied; uint32_t v6only_time = 0; - bool use_v6only = false; + bool use_v6only = false, has_auto_conf = false; #ifdef AUTH const uint8_t *auth; size_t auth_len; @@ -3143,8 +3228,7 @@ dhcp_handledhcp(struct interface *ifp, s /* Ensure that no reject options are present */ for (i = 1; i < 255; i++) { if (has_option_mask(ifo->rejectmask, i) && - get_option_uint8(ifp->ctx, &tmp, - bootp, bootp_len, (uint8_t)i) == 0) + get_option(ifp->ctx, bootp, bootp_len, (uint8_t)i, NULL)) { LOGDHCP(LOG_WARNING, "reject DHCP"); return; @@ -3192,8 +3276,7 @@ dhcp_handledhcp(struct interface *ifp, s /* Ensure that all required options are present */ for (i = 1; i < 255; i++) { if (has_option_mask(ifo->requiremask, i) && - get_option_uint8(ifp->ctx, &tmp, - bootp, bootp_len, (uint8_t)i) != 0) + !get_option(ifp->ctx, bootp, bootp_len, (uint8_t)i, NULL)) { /* If we are BOOTP, then ignore the need for serverid. * To ignore BOOTP, require dhcp_message_type. @@ -3209,7 +3292,8 @@ dhcp_handledhcp(struct interface *ifp, s if (has_option_mask(ifo->requestmask, DHO_IPV6_PREFERRED_ONLY)) { if (get_option_uint32(ifp->ctx, &v6only_time, bootp, bootp_len, - DHO_IPV6_PREFERRED_ONLY) == 0 && (state->state == DHS_DISCOVER || + DHO_IPV6_PREFERRED_ONLY) == 0 && + (state->state == DHS_DISCOVER || state->state == DHS_REBOOT || state->state == DHS_NONE)) { char v6msg[128]; @@ -3225,8 +3309,8 @@ dhcp_handledhcp(struct interface *ifp, s } /* DHCP Auto-Configure, RFC 2563 */ - if (type == DHCP_OFFER && bootp->yiaddr == 0) { - LOGDHCP(LOG_WARNING, "no address given"); + if (type == DHCP_OFFER && bootp->yiaddr == INADDR_ANY) { + LOGDHCP(LOG_WARNING, "no address offered"); if ((msg = get_option_string(ifp->ctx, bootp, bootp_len, DHO_MESSAGE))) { @@ -3238,6 +3322,7 @@ dhcp_handledhcp(struct interface *ifp, s get_option_uint8(ifp->ctx, &tmp, bootp, bootp_len, DHO_AUTOCONFIGURE) == 0) { + has_auto_conf = true; switch (tmp) { case 0: LOGDHCP(LOG_WARNING, "IPv4LL disabled from"); @@ -3256,24 +3341,27 @@ dhcp_handledhcp(struct interface *ifp, s ifp->name, tmp); break; } - eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); - eloop_timeout_add_sec(ifp->ctx->eloop, - use_v6only ? v6only_time : DHCP_MAX, - dhcp_discover, ifp); } #endif - return; } if (use_v6only) { dhcp_drop(ifp, "EXPIRE"); dhcp_unlink(ifp->ctx, state->leasefile); + } + if (use_v6only || has_auto_conf) { eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); - eloop_timeout_add_sec(ifp->ctx->eloop, v6only_time, + eloop_timeout_add_sec(ifp->ctx->eloop, + use_v6only ? v6only_time : DHCP_MAX, dhcp_discover, ifp); return; } + /* No hints as what to do with no address? + * All we can do is continue. */ + if (type == DHCP_OFFER && bootp->yiaddr == INADDR_ANY) + return; + /* Ensure that the address offered is valid */ if ((type == 0 || type == DHCP_OFFER || type == DHCP_ACK) && (bootp->ciaddr == INADDR_ANY || bootp->ciaddr == INADDR_BROADCAST) @@ -3471,61 +3559,63 @@ is_packet_udp_bootp(void *packet, size_t return true; } +/* IPv4 pseudo header used for computing TCP and UDP checksums. */ +struct ip_pseudo { + struct in_addr ipp_src; + struct in_addr ipp_dst; + uint8_t ipp_pad; /* must be zero */ + uint8_t ipp_p; + uint16_t ipp_len; +}; + /* Lengths have already been checked. */ static bool -checksums_valid(void *packet, +checksums_valid(const void *packet, struct in_addr *from, unsigned int flags) { - struct ip *ip = packet; - union pip { - struct ip ip; - uint16_t w[sizeof(struct ip) / 2]; - } pip = { - .ip = { - .ip_p = IPPROTO_UDP, - .ip_src = ip->ip_src, - .ip_dst = ip->ip_dst, - } - }; + const struct ip *ip = packet; size_t ip_hlen; struct udphdr udp; - char *udpp, *uh_sump; + const char *udpp; uint32_t csum; + struct ip_pseudo ip_pseudo; + /* We create a buffer to copy ip_pseudo into and send that to + * in_cksum() to avoid memory issues. */ + uint8_t ip_pseudo_buf[sizeof(struct ip_pseudo)]; if (from != NULL) from->s_addr = ip->ip_src.s_addr; ip_hlen = (size_t)ip->ip_hl * 4; + /* RFC 1071 states that the check of the checksum is equal to 0. */ if (in_cksum(ip, ip_hlen, NULL) != 0) return false; if (flags & BPF_PARTIALCSUM) return true; - udpp = (char *)ip + ip_hlen; + udpp = (const char *)ip + ip_hlen; memcpy(&udp, udpp, sizeof(udp)); + /* RFC 768 states that zero means no checksum to verify. */ if (udp.uh_sum == 0) return true; /* UDP checksum is based on a pseudo IP header alongside * the UDP header and payload. */ - pip.ip.ip_len = udp.uh_ulen; - csum = 0; - - /* Need to zero the UDP sum in the packet for the checksum to work. */ - uh_sump = udpp + offsetof(struct udphdr, uh_sum); - memset(uh_sump, 0, sizeof(udp.uh_sum)); + ip_pseudo.ipp_src = ip->ip_src; + ip_pseudo.ipp_dst = ip->ip_dst; + ip_pseudo.ipp_pad = 0; + ip_pseudo.ipp_p = ip->ip_p; + ip_pseudo.ipp_len = udp.uh_ulen; + memcpy(ip_pseudo_buf, &ip_pseudo, sizeof(ip_pseudo_buf)); /* Checksum pseudo header and then UDP + payload. */ - in_cksum(pip.w, sizeof(pip.w), &csum); + csum = 0; + in_cksum(ip_pseudo_buf, sizeof(ip_pseudo_buf), &csum); csum = in_cksum(udpp, ntohs(udp.uh_ulen), &csum); -#if 0 /* Not needed, just here for completeness. */ - /* Put the checksum back. */ - memcpy(uh_sump, &udp.uh_sum, sizeof(udp.uh_sum)); -#endif - - return csum == udp.uh_sum; + /* RFC 1071 states that the check of the checksum is equal to 0. */ + return csum == 0; } static void @@ -3626,7 +3716,8 @@ static void dhcp_readbpf(void *arg, unsigned short events) { struct interface *ifp = arg; - uint8_t buf[FRAMELEN_MAX]; + /* Sparc64 needs this buffer aligned */ + alignas(sizeof(struct ip *)) uint8_t buf[FRAMELEN_MAX]; ssize_t bytes; struct dhcp_state *state = D_STATE(ifp); struct bpf *bpf = state->bpf; @@ -4213,13 +4304,8 @@ dhcp_abort(struct interface *ifp) eloop_timeout_delete(ifp->ctx->eloop, dhcp_start1, ifp); - if (state != NULL && state->added) { + if (state != NULL && state->added) rt_build(ifp->ctx, AF_INET); -#ifdef ARP - if (ifp->options->options & DHCPCD_ARP) - arp_announceaddr(ifp->ctx, &state->addr->addr); -#endif - } } struct ipv4_addr * @@ -4261,18 +4347,15 @@ dhcp_handleifa(int cmd, struct ipv4_addr ifo = ifp->options; -#ifdef PRIVSEP - if (IN_PRIVSEP_SE(ifp->ctx) && - !(ifp->ctx->options & (DHCPCD_MANAGER | DHCPCD_CONFIGURE)) && + if (!(ifp->ctx->options & (DHCPCD_MANAGER | DHCPCD_CONFIGURE)) && IN_ARE_ADDR_EQUAL(&state->lease.addr, &ia->addr)) { + uint8_t old_state = state->added; + state->addr = ia; state->added = STATE_ADDED; - dhcp_closebpf(ifp); - if (ps_inet_openbootp(ia) == -1) - logerr(__func__); + dhcp_bound(ifp, old_state); } -#endif /* If we have requested a specific address, return now. * The below code is only for when inform or static has been Index: src/external/bsd/dhcpcd/dist/src/dhcp6.c diff -u src/external/bsd/dhcpcd/dist/src/dhcp6.c:1.34 src/external/bsd/dhcpcd/dist/src/dhcp6.c:1.35 --- src/external/bsd/dhcpcd/dist/src/dhcp6.c:1.34 Fri May 24 16:09:09 2024 +++ src/external/bsd/dhcpcd/dist/src/dhcp6.c Wed Feb 12 19:23:13 2025 @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2023 Roy Marples <r...@marples.name> + * Copyright (c) 2006-2024 Roy Marples <r...@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -112,8 +112,9 @@ struct dhcp6_ia_addr { }; __CTASSERT(sizeof(struct dhcp6_ia_addr) == 16 + 8); -/* XXX FIXME: This is the only packed structure and it does not align. - * Maybe manually decode it? */ +/* Some compilers do not support packed structures. + * We manually decode this. */ +#if 0 struct dhcp6_pd_addr { uint32_t pltime; uint32_t vltime; @@ -121,6 +122,13 @@ struct dhcp6_pd_addr { struct in6_addr prefix; } __packed; __CTASSERT(sizeof(struct dhcp6_pd_addr) == 8 + 1 + 16); +#endif + +#define DHCP6_PD_ADDR_SIZE (8 + 1 + 16) +#define DHCP6_PD_ADDR_PLTIME 0 +#define DHCP6_PD_ADDR_VLTIME 4 +#define DHCP6_PD_ADDR_PLEN 8 +#define DHCP6_PD_ADDR_PREFIX 9 struct dhcp6_op { uint16_t type; @@ -179,6 +187,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_startrebind(void *arg); static void dhcp6_recvaddr(void *, unsigned short); static void dhcp6_startdecline(struct interface *); static void dhcp6_startrequest(struct interface *); @@ -195,6 +204,11 @@ static int dhcp6_hasprefixdelegation(str !((ia)->flags & IPV6_AF_STALE) && \ (ia)->prefix_vltime != 0) + +/* Gets a pointer to the length part of the option to fill it + * in later. */ +#define NEXTLEN(p) ((p) + offsetof(struct dhcp6_option, len)) + void dhcp6_printoptions(const struct dhcpcd_ctx *ctx, const struct dhcp_opt *opts, size_t opts_len) @@ -262,29 +276,28 @@ dhcp6_makeuser(void *data, const struct return sizeof(o) + olen; } +#ifndef SMALL +/* DHCPv6 Option 16 (Vendor Class Option) */ static size_t dhcp6_makevendor(void *data, const struct interface *ifp) { const struct if_options *ifo; - size_t len, vlen, i; + size_t len = 0, optlen, vlen, i; uint8_t *p; const struct vivco *vivco; struct dhcp6_option o; ifo = ifp->options; - len = sizeof(uint32_t); /* IANA PEN */ - if (ifo->vivco_en) { - vlen = 0; + if (ifo->vivco_len > 0) { for (i = 0, vivco = ifo->vivco; i < ifo->vivco_len; i++, vivco++) - vlen += sizeof(uint16_t) + vivco->len; - len += vlen; + len += sizeof(o) + sizeof(uint32_t) + sizeof(uint16_t) + vivco->len; } else if (ifo->vendorclassid[0] != '\0') { /* dhcpcd owns DHCPCD_IANA_PEN. * If you need your own string, get your own IANA PEN. */ vlen = strlen(ifp->ctx->vendor); - len += sizeof(uint16_t) + vlen; + len += sizeof(o) + sizeof(uint32_t) + sizeof(uint16_t) + vlen; } else return 0; @@ -298,19 +311,19 @@ dhcp6_makevendor(void *data, const struc uint16_t hvlen; p = data; - o.code = htons(D6_OPTION_VENDOR_CLASS); - o.len = htons((uint16_t)len); - memcpy(p, &o, sizeof(o)); - p += sizeof(o); - pen = htonl(ifo->vivco_en ? ifo->vivco_en : DHCPCD_IANA_PEN); - memcpy(p, &pen, sizeof(pen)); - p += sizeof(pen); - if (ifo->vivco_en) { + if (ifo->vivco_len > 0) { for (i = 0, vivco = ifo->vivco; i < ifo->vivco_len; - i++, vivco++) - { + i++, vivco++) { + optlen = sizeof(uint32_t) + sizeof(uint16_t) + vivco->len; + o.code = htons(D6_OPTION_VENDOR_CLASS); + o.len = htons((uint16_t)optlen); + memcpy(p, &o, sizeof(o)); + p += sizeof(o); + pen = htonl(vivco->en); + memcpy(p, &pen, sizeof(pen)); + p += sizeof(pen); hvlen = htons((uint16_t)vivco->len); memcpy(p, &hvlen, sizeof(hvlen)); p += sizeof(hvlen); @@ -318,15 +331,88 @@ dhcp6_makevendor(void *data, const struc p += vivco->len; } } else if (ifo->vendorclassid[0] != '\0') { + o.code = htons(D6_OPTION_VENDOR_CLASS); + o.len = htons((uint16_t)len); + memcpy(p, &o, sizeof(o)); + p += sizeof(o); + pen = htonl(DHCPCD_IANA_PEN); + memcpy(p, &pen, sizeof(pen)); + p += sizeof(pen); hvlen = htons((uint16_t)vlen); memcpy(p, &hvlen, sizeof(hvlen)); p += sizeof(hvlen); memcpy(p, ifp->ctx->vendor, vlen); } } + return len; +} - return sizeof(o) + len; +/* DHCPv6 Option 17 (Vendor-Specific Information Option) */ +static size_t +dhcp6_makevendoropts(void *data, const struct interface *ifp) +{ + uint8_t *p = data, *olenp; + const struct if_options *ifo = ifp->options; + size_t len = 0, olen; + const struct vsio *vsio, *vsio_endp = ifo->vsio6 + ifo->vsio6_len; + const struct vsio_so *so, *so_endp; + struct dhcp6_option o; + uint32_t en; + uint16_t opt, slen; + + for (vsio = ifo->vsio6; vsio != vsio_endp; ++vsio) { + if (vsio->so_len == 0) + continue; + + if (p != NULL) { + olenp = NEXTLEN(p); + o.code = htons(D6_OPTION_VENDOR_OPTS); + o.len = 0; + memcpy(p, &o, sizeof(o)); + p += sizeof(o); + + en = htonl(vsio->en); + memcpy(p, &en, sizeof(en)); + p += sizeof(en); + } else + olenp = NULL; + + olen = sizeof(en); + + so_endp = vsio->so + vsio->so_len; + for (so = vsio->so; so != so_endp; so++) { + if (olen + sizeof(opt) + sizeof(slen) + + so->len > UINT16_MAX) + { + logerrx("%s: option too big", __func__); + break; + } + + if (p != NULL) { + opt = htons(so->opt); + memcpy(p, &opt, sizeof(opt)); + p += sizeof(opt); + slen = htons(so->len); + memcpy(p, &slen, sizeof(slen)); + p += sizeof(slen); + memcpy(p, so->data, so->len); + p += so->len; + } + + olen += sizeof(opt) + sizeof(slen) + so->len; + } + + if (olenp != NULL) { + slen = htons((uint16_t)olen); + memcpy(olenp, &slen, sizeof(slen)); + } + + len += sizeof(o) + olen; + } + + return len; } +#endif static void * dhcp6_findoption(void *data, size_t data_len, uint16_t code, uint16_t *len) @@ -793,8 +879,13 @@ dhcp6_makemessage(struct interface *ifp) if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS)) len += dhcp6_makeuser(NULL, ifp); + +#ifndef SMALL if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS)) len += dhcp6_makevendor(NULL, ifp); + if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_OPTS)) + len += dhcp6_makevendoropts(NULL, ifp); +#endif /* IA */ m = NULL; @@ -838,7 +929,7 @@ dhcp6_makemessage(struct interface *ifp) continue; if (ap->ia_type == D6_OPTION_IA_PD) { #ifndef SMALL - len += sizeof(o) + sizeof(struct dhcp6_pd_addr); + len += sizeof(o) + DHCP6_PD_ADDR_SIZE; if (ap->prefix_exclude_len) len += sizeof(o) + 1 + (uint8_t)((ap->prefix_exclude_len - @@ -941,7 +1032,6 @@ dhcp6_makemessage(struct interface *ifp) p += (_len); \ } \ } while (0 /* CONSTCOND */) -#define NEXTLEN (p + offsetof(struct dhcp6_option, len)) /* Options are listed in numerical order as per RFC 7844 Section 4.1 * XXX: They should be randomised. */ @@ -959,7 +1049,7 @@ dhcp6_makemessage(struct interface *ifp) for (l = 0; IA && l < ifo->ia_len; l++) { ifia = &ifo->ia[l]; - o_lenp = NEXTLEN; + o_lenp = NEXTLEN(p); /* TA structure is the same as the others, * it just lacks the T1 and T2 timers. * These happen to be at the end of the struct, @@ -989,19 +1079,14 @@ dhcp6_makemessage(struct interface *ifp) continue; if (ap->ia_type == D6_OPTION_IA_PD) { #ifndef SMALL - struct dhcp6_pd_addr pdp = { - .prefix_len = ap->prefix_len, - /* - * RFC 8415 21.22 states that the - * valid and preferred lifetimes sent by - * the client SHOULD be zero and MUST - * be ignored by the server. - */ - }; + uint8_t pdp[DHCP6_PD_ADDR_SIZE]; + + memset(pdp, 0, DHCP6_PD_ADDR_PLEN); + pdp[DHCP6_PD_ADDR_PLEN] = (uint8_t)ap->prefix_len; + memcpy(pdp + DHCP6_PD_ADDR_PREFIX, &ap->prefix, + DHCP6_PD_ADDR_SIZE - DHCP6_PD_ADDR_PREFIX); + COPYIN(D6_OPTION_IAPREFIX, pdp, sizeof(pdp)); - /* pdp.prefix is not aligned, so copy it in. */ - memcpy(&pdp.prefix, &ap->prefix, sizeof(pdp.prefix)); - COPYIN(D6_OPTION_IAPREFIX, &pdp, sizeof(pdp)); ia_na_len = (uint16_t) (ia_na_len + sizeof(o) + sizeof(pdp)); @@ -1060,7 +1145,7 @@ dhcp6_makemessage(struct interface *ifp) state->send->type != DHCP6_DECLINE && n_options) { - o_lenp = NEXTLEN; + o_lenp = NEXTLEN(p); o.len = 0; COPYIN1(D6_OPTION_ORO, 0); for (l = 0, opt = ifp->ctx->dhcp6_opts; @@ -1118,14 +1203,19 @@ dhcp6_makemessage(struct interface *ifp) if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS)) p += dhcp6_makeuser(p, ifp); + +#ifndef SMALL if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS)) p += dhcp6_makevendor(p, ifp); + if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_OPTS)) + p += dhcp6_makevendoropts(p, ifp); +#endif if (state->send->type != DHCP6_RELEASE && state->send->type != DHCP6_DECLINE) { if (fqdn != FQDN_DISABLE) { - o_lenp = NEXTLEN; + o_lenp = NEXTLEN(p); COPYIN1(D6_OPTION_FQDN, 0); if (hl == 0) *p = D6_FQDN_NONE; @@ -1187,14 +1277,14 @@ dhcp6_get_op(uint16_t type) } static void -dhcp6_freedrop_addrs(struct interface *ifp, int drop, +dhcp6_freedrop_addrs(struct interface *ifp, int drop, unsigned int notflags, const struct interface *ifd) { struct dhcp6_state *state; state = D6_STATE(ifp); if (state) { - ipv6_freedrop_addrs(&state->addrs, drop, ifd); + ipv6_freedrop_addrs(&state->addrs, drop, notflags, ifd); if (drop) rt_build(ifp->ctx, AF_INET6); } @@ -1208,7 +1298,7 @@ static void dhcp6_delete_delegates(struc if (ifp->ctx->ifaces) { TAILQ_FOREACH(ifp0, ifp->ctx->ifaces, next) { if (ifp0 != ifp) - dhcp6_freedrop_addrs(ifp0, 1, ifp); + dhcp6_freedrop_addrs(ifp0, 1, 0, ifp); } } } @@ -1408,7 +1498,7 @@ sent: state->RT = RT * 2; if (state->RT < RT) /* Check overflow */ state->RT = RT; - if (state->MRC == 0 || state->RTC < state->MRC) + if (state->MRC == 0 || state->RTC <= state->MRC) eloop_timeout_add_msec(ctx->eloop, RT, callback, ifp); else if (state->MRC != 0 && state->MRCcallback) @@ -1570,10 +1660,6 @@ dhcp6_dadcallback(void *arg) if (ia->addr_flags & IN6_IFF_DUPLICATED) logwarnx("%s: DAD detected %s", ia->iface->name, ia->saddr); -#ifdef ND6_ADVERTISE - else - ipv6nd_advertise(ia); -#endif if (completed) return; @@ -1656,16 +1742,22 @@ static void dhcp6_startdiscover(void *arg) { struct interface *ifp; + struct if_options *ifo; struct dhcp6_state *state; int llevel; struct ipv6_addr *ia; ifp = arg; state = D6_STATE(ifp); + ifo = ifp->options; #ifndef SMALL if (state->reason == NULL || strcmp(state->reason, "TIMEOUT6") != 0) dhcp6_delete_delegates(ifp); #endif + /* Ensure we never request INFO_REFRESH_TIME, + * this only belongs in Information-Request messages */ + del_option_mask(ifo->requestmask6, D6_OPTION_INFO_REFRESH_TIME); + if (state->new == NULL && !state->failed) llevel = LOG_INFO; else @@ -1678,11 +1770,6 @@ dhcp6_startdiscover(void *arg) state->MRT = state->sol_max_rt; state->MRC = SOL_MAX_RC; - eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); - free(state->new); - state->new = NULL; - state->new_len = 0; - /* If we fail to renew or confirm, our requested addreses will * be marked as stale. To re-request them, just mark them as not stale. */ @@ -1703,9 +1790,11 @@ dhcp6_startinform(void *arg) struct interface *ifp; struct dhcp6_state *state; int llevel; + struct if_options *ifo; ifp = arg; state = D6_STATE(ifp); + ifo = ifp->options; llevel = state->failed ? LOG_DEBUG : LOG_INFO; logmessage(llevel, "%s: requesting DHCPv6 information", ifp->name); state->state = DH6S_INFORM; @@ -1715,7 +1804,9 @@ dhcp6_startinform(void *arg) state->MRT = state->inf_max_rt; state->MRC = 0; - eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); + /* Ensure we always request INFO_REFRESH_TIME as per rfc8415 */ + add_option_mask(ifo->requestmask6, D6_OPTION_INFO_REFRESH_TIME); + if (dhcp6_makemessage(ifp) == -1) { logerr("%s: %s", __func__, ifp->name); return; @@ -1745,41 +1836,15 @@ dhcp6_startdiscoinform(struct interface } static void -dhcp6_leaseextend(struct interface *ifp) -{ - struct dhcp6_state *state = D6_STATE(ifp); - struct ipv6_addr *ia; - - logwarnx("%s: extending DHCPv6 lease", ifp->name); - TAILQ_FOREACH(ia, &state->addrs, next) { - ia->flags |= IPV6_AF_EXTENDED; - /* Set infinite lifetimes. */ - ia->prefix_pltime = ND6_INFINITE_LIFETIME; - ia->prefix_vltime = ND6_INFINITE_LIFETIME; - } -} - -static void -dhcp6_fail(struct interface *ifp) +dhcp6_fail(struct interface *ifp, bool drop) { struct dhcp6_state *state = D6_STATE(ifp); state->failed = true; - /* RFC3315 18.1.2 says that prior addresses SHOULD be used on failure. - * RFC2131 3.2.3 says that MAY chose to use the prior address. - * Because dhcpcd was written first for RFC2131, we have the LASTLEASE - * option which defaults to off as that makes the most sense for - * mobile clients. - * dhcpcd also has LASTLEASE_EXTEND to extend this lease past it's - * expiry, but this is strictly not RFC compliant in any way or form. */ - if (state->new != NULL && - ifp->options->options & DHCPCD_LASTLEASE_EXTEND) - { - dhcp6_leaseextend(ifp); - dhcp6_bind(ifp, NULL, NULL); - } else { - dhcp6_freedrop_addrs(ifp, 1, NULL); + if (drop) { + dhcp6_freedrop_addrs(ifp, 1, + IPV6_AF_DELEGATED | IPV6_AF_PFXDELEGATION, NULL); #ifndef SMALL dhcp6_delete_delegates(ifp); #endif @@ -1792,12 +1857,21 @@ dhcp6_fail(struct interface *ifp) script_runreason(ifp, "EXPIRE6"); dhcp_unlink(ifp->ctx, state->leasefile); dhcp6_addrequestedaddrs(ifp); + eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); + } else if ((state->state == DH6S_CONFIRM || state->state == DH6S_REBIND) && + ifp->options->options & DHCPCD_LASTLEASE) { + dhcp6_bind(ifp, NULL, NULL); + state->state = DH6S_REBIND; + dhcp6_startrebind(ifp); + return; + } else if (state->new) { + script_runreason(ifp, "TIMEOUT6"); + // We need to keep the expire timeout alive } if (!dhcp6_startdiscoinform(ifp)) { logwarnx("%s: no advertising IPv6 router wants DHCP",ifp->name); state->state = DH6S_INIT; - eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); } } @@ -1817,7 +1891,10 @@ dhcp6_failconfirm(void *arg) logmessage(llevel, "%s: failed to confirm prior DHCPv6 address", ifp->name); - dhcp6_fail(ifp); + eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendconfirm, ifp); + + /* RFC8415 18.2.3 says that prior addresses SHOULD be used on failure. */ + dhcp6_fail(ifp, false); } static void @@ -1827,7 +1904,7 @@ dhcp6_failrequest(void *arg) int llevel = dhcp6_failloglevel(ifp); logmessage(llevel, "%s: failed to request DHCPv6 address", ifp->name); - dhcp6_fail(ifp); + dhcp6_fail(ifp, true); } static void @@ -1838,17 +1915,21 @@ dhcp6_failinform(void *arg) logmessage(llevel, "%s: failed to request DHCPv6 information", ifp->name); - dhcp6_fail(ifp); + dhcp6_fail(ifp, true); } #ifndef SMALL static void -dhcp6_failrebind(void *arg) +dhcp6_failrebindpd(void *arg) { struct interface *ifp = arg; logerrx("%s: failed to rebind prior DHCPv6 delegation", ifp->name); - dhcp6_fail(ifp); + eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrebind, ifp); + + /* RFC8415 18.2.3 says that prior addresses SHOULD be used on failure. + * 18.2 says REBIND rather than CONFIRM with PD but use CONFIRM timings. */ + dhcp6_fail(ifp, false); } static int @@ -1875,47 +1956,39 @@ dhcp6_startrebind(void *arg) { struct interface *ifp; struct dhcp6_state *state; -#ifndef SMALL - int pd; -#endif ifp = arg; eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrenew, ifp); state = D6_STATE(ifp); + + state->IMD = REB_MAX_DELAY; + state->IRT = REB_TIMEOUT; + state->MRT = REB_MAX_RT; + state->RTC = 0; + state->MRC = 0; + if (state->state == DH6S_RENEW) logwarnx("%s: failed to renew DHCPv6, rebinding", ifp->name); - else + else { loginfox("%s: rebinding prior DHCPv6 lease", ifp->name); - state->state = DH6S_REBIND; - state->RTC = 0; - state->MRC = 0; #ifndef SMALL - /* RFC 3633 section 12.1 */ - pd = dhcp6_hasprefixdelegation(ifp); - if (pd) { - state->IMD = CNF_MAX_DELAY; - state->IRT = CNF_TIMEOUT; - state->MRT = CNF_MAX_RT; - } else + /* RFC 8415 18.2.5 */ + if (dhcp6_hasprefixdelegation(ifp)) { + state->IMD = CNF_MAX_DELAY; + state->IRT = CNF_TIMEOUT; + state->MRT = CNF_MAX_RT; + eloop_timeout_add_sec(ifp->ctx->eloop, + CNF_MAX_RD, dhcp6_failrebindpd, ifp); + } #endif - { - state->IMD = REB_MAX_DELAY; - state->IRT = REB_TIMEOUT; - state->MRT = REB_MAX_RT; } + state->state = DH6S_REBIND; if (dhcp6_makemessage(ifp) == -1) logerr("%s: %s", __func__, ifp->name); else dhcp6_sendrebind(ifp); - -#ifndef SMALL - /* RFC 3633 section 12.1 */ - if (pd) - eloop_timeout_add_sec(ifp->ctx->eloop, - CNF_MAX_RD, dhcp6_failrebind, ifp); -#endif } static void @@ -1984,7 +2057,7 @@ dhcp6_startexpire(void *arg) eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrebind, ifp); logerrx("%s: DHCPv6 lease expired", ifp->name); - dhcp6_fail(ifp); + dhcp6_fail(ifp, true); } static void @@ -1993,7 +2066,7 @@ dhcp6_faildecline(void *arg) struct interface *ifp = arg; logerrx("%s: failed to decline duplicated DHCPv6 addresses", ifp->name); - dhcp6_fail(ifp); + dhcp6_fail(ifp, true); } static void @@ -2129,6 +2202,12 @@ dhcp6_checkstatusok(const struct interfa state->lerror = code; errno = 0; + /* RFC 8415 18.2.10 */ + if (code == D6_STATUS_USEMULTICAST) { + logdebugx("%s: server sent USEMULTICAST", ifp->name); + state->unicast = in6addr_any; + } + /* code cannot be D6_STATUS_OK, so there is a failure */ if (ifp->ctx->options & DHCPCD_TEST) eloop_exit(ifp->ctx->eloop, EXIT_FAILURE); @@ -2255,7 +2334,8 @@ dhcp6_findpd(struct interface *ifp, cons int i; uint8_t nb, *pw; uint16_t ol; - struct dhcp6_pd_addr pdp; + uint32_t pdp_vltime, pdp_pltime; + uint8_t pdp_plen; struct in6_addr pdp_prefix; i = 0; @@ -2265,37 +2345,43 @@ dhcp6_findpd(struct interface *ifp, cons nd = o + ol; l -= (size_t)(nd - d); d = nd; - if (ol < sizeof(pdp)) { + if (ol < DHCP6_PD_ADDR_SIZE) { errno = EINVAL; logerrx("%s: IA Prefix option truncated", ifp->name); continue; } - memcpy(&pdp, o, sizeof(pdp)); - pdp.pltime = ntohl(pdp.pltime); - pdp.vltime = ntohl(pdp.vltime); + memcpy(&pdp_pltime, o, sizeof(pdp_pltime)); + o += sizeof(pdp_pltime); + memcpy(&pdp_vltime, o, sizeof(pdp_vltime)); + o += sizeof(pdp_vltime); + memcpy(&pdp_plen, o, sizeof(pdp_plen)); + o += sizeof(pdp_plen); + + pdp_pltime = ntohl(pdp_pltime); + pdp_vltime = ntohl(pdp_vltime); /* RFC 3315 22.6 */ - if (pdp.pltime > pdp.vltime) { + if (pdp_pltime > pdp_vltime) { errno = EINVAL; logerrx("%s: IA Prefix pltime %"PRIu32 " > vltime %"PRIu32, - ifp->name, pdp.pltime, pdp.vltime); + ifp->name, pdp_pltime, pdp_vltime); continue; } - o += sizeof(pdp); - ol = (uint16_t)(ol - sizeof(pdp)); + memcpy(&pdp_prefix, o, sizeof(pdp_prefix)); + o += sizeof(pdp_prefix); + ol = (uint16_t)(ol - sizeof(pdp_pltime) - sizeof(pdp_vltime) - + sizeof(pdp_plen) - sizeof(pdp_prefix)); - /* pdp.prefix is not aligned so copy it out. */ - memcpy(&pdp_prefix, &pdp.prefix, sizeof(pdp_prefix)); TAILQ_FOREACH(a, &state->addrs, next) { if (IN6_ARE_ADDR_EQUAL(&a->prefix, &pdp_prefix)) break; } if (a == NULL) { - a = ipv6_newaddr(ifp, &pdp_prefix, pdp.prefix_len, - IPV6_AF_DELEGATEDPFX); + a = ipv6_newaddr(ifp, &pdp_prefix, pdp_plen, + IPV6_AF_PFXDELEGATION); if (a == NULL) break; a->created = *acquired; @@ -2304,16 +2390,16 @@ dhcp6_findpd(struct interface *ifp, cons memcpy(a->iaid, iaid, sizeof(a->iaid)); TAILQ_INSERT_TAIL(&state->addrs, a, next); } else { - if (!(a->flags & IPV6_AF_DELEGATEDPFX)) - a->flags |= IPV6_AF_NEW | IPV6_AF_DELEGATEDPFX; + if (!(a->flags & IPV6_AF_PFXDELEGATION)) + a->flags |= IPV6_AF_NEW | IPV6_AF_PFXDELEGATION; a->flags &= ~(IPV6_AF_STALE | IPV6_AF_EXTENDED); - if (a->prefix_vltime != pdp.vltime) + if (a->prefix_vltime != pdp_vltime) a->flags |= IPV6_AF_NEW; } a->acquired = *acquired; - a->prefix_pltime = pdp.pltime; - a->prefix_vltime = pdp.vltime; + a->prefix_pltime = pdp_pltime; + a->prefix_vltime = pdp_vltime; if (a->prefix_pltime && a->prefix_pltime < state->lowpl) state->lowpl = a->prefix_pltime; @@ -2394,6 +2480,8 @@ dhcp6_findia(struct interface *ifp, stru i = e = 0; state = D6_STATE(ifp); TAILQ_FOREACH(ap, &state->addrs, next) { + /* Anything not from a lease for this interface should be + * marked as stale. */ if (!(ap->flags & IPV6_AF_DELEGATED)) ap->flags |= IPV6_AF_STALE; } @@ -2571,7 +2659,7 @@ dhcp6_deprecateaddrs(struct ipv6_addrhea #ifndef SMALL /* If we delegated from this prefix, deprecate or remove * the delegations. */ - if (ia->flags & IPV6_AF_DELEGATEDPFX) + if (ia->flags & IPV6_AF_PFXDELEGATION) dhcp6_deprecatedele(ia); #endif @@ -2582,7 +2670,7 @@ dhcp6_deprecateaddrs(struct ipv6_addrhea continue; } TAILQ_REMOVE(addrs, ia, next); - if (ia->flags & IPV6_AF_EXTENDED) + if (!(ia->flags & IPV6_AF_EXTENDED)) ipv6_deleteaddr(ia); ipv6_freeaddr(ia); } @@ -2686,8 +2774,7 @@ dhcp6_readlease(struct interface *ifp, i } if (state->expire != ND6_INFINITE_LIFETIME && - (time_t)state->expire < now - mtime && - !(ifp->options->options & DHCPCD_LASTLEASE_EXTEND)) + (time_t)state->expire < now - mtime) { logdebugx("%s: discarding expired lease", ifp->name); bytes = 0; @@ -2732,7 +2819,7 @@ out: return bytes; ex: - dhcp6_freedrop_addrs(ifp, 0, NULL); + dhcp6_freedrop_addrs(ifp, 0, IPV6_AF_DELEGATED, NULL); dhcp_unlink(ifp->ctx, state->leasefile); free(state->new); state->new = NULL; @@ -2745,19 +2832,20 @@ static void dhcp6_startinit(struct interface *ifp) { struct dhcp6_state *state; + struct if_options *ifo; ssize_t r; uint8_t has_ta, has_non_ta; size_t i; state = D6_STATE(ifp); - state->state = DH6S_INIT; + ifo = ifp->options; state->expire = ND6_INFINITE_LIFETIME; state->lowpl = ND6_INFINITE_LIFETIME; dhcp6_addrequestedaddrs(ifp); has_ta = has_non_ta = 0; - for (i = 0; i < ifp->options->ia_len; i++) { - switch (ifp->options->ia[i].ia_type) { + for (i = 0; i < ifo->ia_len; i++) { + switch (ifo->ia[i].ia_type) { case D6_OPTION_IA_TA: has_ta = 1; break; @@ -2768,18 +2856,19 @@ dhcp6_startinit(struct interface *ifp) if (!(ifp->ctx->options & DHCPCD_TEST) && !(has_ta && !has_non_ta) && - ifp->options->reboot != 0) + ifo->reboot != 0) { r = dhcp6_readlease(ifp, 1); if (r == -1) { if (errno != ENOENT && errno != ESRCH) logerr("%s: %s", __func__, state->leasefile); } else if (r != 0 && - !(ifp->options->options & DHCPCD_ANONYMOUS)) + !(ifo->options & DHCPCD_ANONYMOUS)) { /* RFC 3633 section 12.1 */ #ifndef SMALL - if (dhcp6_hasprefixdelegation(ifp)) + if (state->state == DH6S_MANUALREBIND || + dhcp6_hasprefixdelegation(ifp)) dhcp6_startrebind(ifp); else #endif @@ -2947,7 +3036,7 @@ dhcp6_delegate_prefix(struct interface * k = 0; carrier_warned = false; TAILQ_FOREACH(ap, &state->addrs, next) { - if (!(ap->flags & IPV6_AF_DELEGATEDPFX)) + if (!(ap->flags & IPV6_AF_PFXDELEGATION)) continue; if (!(ap->flags & IPV6_AF_DELEGATEDLOG)) { int loglevel; @@ -3048,7 +3137,7 @@ dhcp6_find_delegates(struct interface *i if (state == NULL || state->state != DH6S_BOUND) continue; TAILQ_FOREACH(ap, &state->addrs, next) { - if (!(ap->flags & IPV6_AF_DELEGATEDPFX)) + if (!(ap->flags & IPV6_AF_PFXDELEGATION)) continue; for (i = 0; i < ifo->ia_len; i++) { ia = &ifo->ia[i]; @@ -3082,7 +3171,6 @@ dhcp6_find_delegates(struct interface *i if (k) { loginfox("%s: adding delegated prefixes", ifp->name); state = D6_STATE(ifp); - state->state = DH6S_DELEGATED; ipv6_addaddrs(&state->addrs); rt_build(ifp->ctx, AF_INET6); dhcp6_script_try_run(ifp, 1); @@ -3139,7 +3227,7 @@ dhcp6_bind(struct interface *ifp, const if (state->reason == NULL) state->reason = "INFORM6"; - o = dhcp6_findmoption(state->new, state->new_len, + o = dhcp6_findmoption(state->recv, state->recv_len, D6_OPTION_INFO_REFRESH_TIME, &ol); if (o == NULL || ol != sizeof(uint32_t)) state->renew = IRT_DEFAULT; @@ -3319,6 +3407,48 @@ dhcp6_bind(struct interface *ifp, const } static void +dhcp6_adjust_max_rt(struct interface *ifp, + struct dhcp6_message *r, size_t len) +{ + struct dhcp6_state *state = D6_STATE(ifp); + uint8_t *o; + uint16_t ol; + + /* RFC 8415 */ + o = dhcp6_findmoption(r, len, D6_OPTION_SOL_MAX_RT, &ol); + if (o != NULL && ol == sizeof(uint32_t)) { + uint32_t max_rt; + + memcpy(&max_rt, o, sizeof(max_rt)); + max_rt = ntohl(max_rt); + if (max_rt >= 60 && max_rt <= 86400) { + logdebugx("%s: SOL_MAX_RT %llu -> %u", + ifp->name, + (unsigned long long)state->sol_max_rt, + max_rt); + state->sol_max_rt = max_rt; + } else + logerrx("%s: invalid SOL_MAX_RT %u", ifp->name, max_rt); + } + + o = dhcp6_findmoption(r, len, D6_OPTION_INF_MAX_RT, &ol); + if (o != NULL && ol == sizeof(uint32_t)) { + uint32_t max_rt; + + memcpy(&max_rt, o, sizeof(max_rt)); + max_rt = ntohl(max_rt); + if (max_rt >= 60 && max_rt <= 86400) { + logdebugx("%s: INF_MAX_RT %llu -> %u", + ifp->name, + (unsigned long long)state->inf_max_rt, + max_rt); + state->inf_max_rt = max_rt; + } else + logerrx("%s: invalid INF_MAX_RT %u", ifp->name, max_rt); + } +} + +static void dhcp6_recvif(struct interface *ifp, const char *sfrom, struct dhcp6_message *r, size_t len) { @@ -3326,7 +3456,7 @@ dhcp6_recvif(struct interface *ifp, cons size_t i; const char *op; struct dhcp6_state *state; - uint8_t *o; + uint8_t *o, preference = 0; uint16_t ol; const struct dhcp_opt *opt; const struct if_options *ifo; @@ -3438,6 +3568,7 @@ dhcp6_recvif(struct interface *ifp, cons case DH6S_REQUEST: /* FALLTHROUGH */ case DH6S_RENEW: /* FALLTHROUGH */ case DH6S_REBIND: + dhcp6_adjust_max_rt(ifp, r, len); if (dhcp6_validatelease(ifp, r, len, sfrom, NULL) == -1) { /* @@ -3476,7 +3607,7 @@ dhcp6_recvif(struct interface *ifp, cons * acknowledgement of one. */ loginfox("%s: %s acknowledged DECLINE6", ifp->name, sfrom); - dhcp6_fail(ifp); + dhcp6_fail(ifp, true); return; default: valid_op = false; @@ -3488,49 +3619,25 @@ dhcp6_recvif(struct interface *ifp, cons valid_op = false; break; } + + o = dhcp6_findmoption(r, len, D6_OPTION_PREFERENCE, &ol); + if (o && ol == sizeof(uint8_t)) + preference = *o; + + /* If we already have an advertisement check that this one + * has a higher preference value. */ if (state->recv_len && state->recv->type == DHCP6_ADVERTISE) { - /* We already have an advertismemnt. - * RFC 8415 says we have to wait for the IRT to elapse. - * To keep the same behaviour we won't do anything with - * this. In the future we should make a lists of - * ADVERTS and pick the "best" one. */ - logdebugx("%s: discarding ADVERTISEMENT from %s", - ifp->name, sfrom); - return; - } - /* RFC7083 */ - o = dhcp6_findmoption(r, len, D6_OPTION_SOL_MAX_RT, &ol); - if (o && ol == sizeof(uint32_t)) { - uint32_t max_rt; - - memcpy(&max_rt, o, sizeof(max_rt)); - max_rt = ntohl(max_rt); - if (max_rt >= 60 && max_rt <= 86400) { - logdebugx("%s: SOL_MAX_RT %llu -> %u", - ifp->name, - (unsigned long long)state->sol_max_rt, - max_rt); - state->sol_max_rt = max_rt; - } else - logerr("%s: invalid SOL_MAX_RT %u", - ifp->name, max_rt); - } - o = dhcp6_findmoption(r, len, D6_OPTION_INF_MAX_RT, &ol); - if (o && ol == sizeof(uint32_t)) { - uint32_t max_rt; - - memcpy(&max_rt, o, sizeof(max_rt)); - max_rt = ntohl(max_rt); - if (max_rt >= 60 && max_rt <= 86400) { - logdebugx("%s: INF_MAX_RT %llu -> %u", - ifp->name, - (unsigned long long)state->inf_max_rt, - max_rt); - state->inf_max_rt = max_rt; - } else - logerrx("%s: invalid INF_MAX_RT %u", - ifp->name, max_rt); + o = dhcp6_findmoption(state->recv, state->recv_len, + D6_OPTION_PREFERENCE, &ol); + if (o && ol == sizeof(uint8_t) && *o >= preference) { + logdebugx( + "%s: discarding ADVERTISEMENT from %s (%u)", + ifp->name, sfrom, preference); + return; + } } + + dhcp6_adjust_max_rt(ifp, r, len); if (dhcp6_validatelease(ifp, r, len, sfrom, NULL) == -1) return; break; @@ -3609,8 +3716,6 @@ dhcp6_recvif(struct interface *ifp, cons if (r->type == DHCP6_ADVERTISE) { struct ipv6_addr *ia; - if (state->state == DH6S_REQUEST) /* rapid commit */ - goto bind; TAILQ_FOREACH(ia, &state->addrs, next) { if (!(ia->flags & (IPV6_AF_STALE | IPV6_AF_REQUEST))) break; @@ -3618,16 +3723,22 @@ dhcp6_recvif(struct interface *ifp, cons if (ia == NULL) ia = TAILQ_FIRST(&state->addrs); if (ia == NULL) - loginfox("%s: ADV (no address) from %s", - ifp->name, sfrom); + loginfox("%s: ADV (no address) from %s (%u)", + ifp->name, sfrom, preference); else - loginfox("%s: ADV %s from %s", - ifp->name, ia->saddr, sfrom); - // We will request when the IRT elapses + loginfox("%s: ADV %s from %s (%u)", + ifp->name, ia->saddr, sfrom, preference); + + /* + * RFC 8415 18.2.1 says we must collect until ADVERTISEMENTs + * until we get one with a preference of 255 or + * the initial RT has elpased. + */ + if (preference == 255 || state->RTC > 1) + dhcp6_startrequest(ifp); return; } -bind: dhcp6_bind(ifp, op, sfrom); } @@ -3978,13 +4089,10 @@ dhcp6_start1(void *arg) del_option_mask(ifo->requestmask6, D6_OPTION_RAPID_COMMIT); #endif - if (state->state == DH6S_INFORM) { - add_option_mask(ifo->requestmask6, D6_OPTION_INFO_REFRESH_TIME); + if (state->state == DH6S_INFORM) dhcp6_startinform(ifp); - } else { - del_option_mask(ifo->requestmask6, D6_OPTION_INFO_REFRESH_TIME); + else dhcp6_startinit(ifp); - } #ifndef SMALL dhcp6_activateinterfaces(ifp); @@ -4026,7 +4134,17 @@ dhcp6_start(struct interface *ifp, enum } break; case DH6S_CONFIRM: - init_state = DH6S_INIT; + /* + * CONFIRM a prior lease from a RA. + * This could be triggered by a roaming interface. + * We could also get here if we are delegated to. + * Now that we don't remove delegated addresses when + * reading the lease file this is the safe path. + */ + if (state->state == DH6S_MANUALREBIND) + init_state = DH6S_MANUALREBIND; + else + init_state = DH6S_INIT; goto gogogo; default: /* Not possible, but sushes some compiler warnings. */ @@ -4060,8 +4178,8 @@ dhcp6_start(struct interface *ifp, enum TAILQ_INIT(&state->addrs); gogogo: - state->new_start = true; state->state = init_state; + state->new_start = true; state->lerror = 0; state->failed = false; dhcp_set_leasefile(state->leasefile, sizeof(state->leasefile), @@ -4085,15 +4203,17 @@ dhcp6_reboot(struct interface *ifp) if (state == NULL) return; - state->lerror = 0; switch (state->state) { - case DH6S_BOUND: - dhcp6_startrebind(ifp); + case DH6S_RENEW: /* FALLTHROUGH */ + case DH6S_BOUND: /* FALLTHROUGH */ + case DH6S_REBIND: + state->state = DH6S_MANUALREBIND; break; - default: - dhcp6_startdiscoinform(ifp); + default: /* Appease compilers */ break; } + + /* Do nothing. On confirming the next lease we will REBIND instead. */ } static void @@ -4153,7 +4273,7 @@ dhcp6_freedrop(struct interface *ifp, in } #endif - dhcp6_freedrop_addrs(ifp, drop, NULL); + dhcp6_freedrop_addrs(ifp, drop, 0, NULL); free(state->old); state->old = state->new; state->old_len = state->new_len; @@ -4207,21 +4327,12 @@ void dhcp6_abort(struct interface *ifp) { struct dhcp6_state *state; -#ifdef ND6_ADVERTISE - struct ipv6_addr *ia; -#endif eloop_timeout_delete(ifp->ctx->eloop, dhcp6_start1, ifp); state = D6_STATE(ifp); if (state == NULL) return; -#ifdef ND6_ADVERTISE - TAILQ_FOREACH(ia, &state->addrs, next) { - ipv6nd_advertise(ia); - } -#endif - eloop_timeout_delete(ifp->ctx->eloop, dhcp6_startdiscover, ifp); eloop_timeout_delete(ifp->ctx->eloop, dhcp6_senddiscover, ifp); eloop_timeout_delete(ifp->ctx->eloop, dhcp6_startinform, ifp); Index: src/external/bsd/dhcpcd/dist/src/dhcpcd.c diff -u src/external/bsd/dhcpcd/dist/src/dhcpcd.c:1.56 src/external/bsd/dhcpcd/dist/src/dhcpcd.c:1.57 --- src/external/bsd/dhcpcd/dist/src/dhcpcd.c:1.56 Fri May 24 16:09:09 2024 +++ src/external/bsd/dhcpcd/dist/src/dhcpcd.c Wed Feb 12 19:23:13 2025 @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2023 Roy Marples <r...@marples.name> + * Copyright (c) 2006-2024 Roy Marples <r...@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -26,10 +26,9 @@ * SUCH DAMAGE. */ -static const char dhcpcd_copyright[] = "Copyright (c) 2006-2023 Roy Marples"; +static const char dhcpcd_copyright[] = "Copyright (c) 2006-2024 Roy Marples"; #include <sys/file.h> -#include <sys/ioctl.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/time.h> @@ -115,7 +114,7 @@ printf("usage: "PACKAGE"\t[-146ABbDdEGgH "\t\t[-O, --nooption option] [-o, --option option]\n" "\t\t[-Q, --require option] [-r, --request address]\n" "\t\t[-S, --static value]\n" - "\t\t[-s, --inform address[/cidr[/broadcast_address]]]\n [--inform6]" + "\t\t[-s, --inform address[/cidr[/broadcast_address]]] [--inform6]\n" "\t\t[-t, --timeout seconds] [-u, --userclass class]\n" "\t\t[-v, --vendor code, value] [-W, --whitelist address[/cidr]] [-w]\n" "\t\t[--waitip [4 | 6]] [-y, --reboot seconds]\n" @@ -724,6 +723,9 @@ dhcpcd_nocarrier_roaming(struct interfac #ifdef INET dhcp_abort(ifp); #endif +#ifdef INET6 + ipv6nd_abort(ifp); +#endif #ifdef DHCP6 dhcp6_abort(ifp); #endif @@ -963,6 +965,7 @@ dhcpcd_startinterface(void *arg) else if (ifo->options & DHCPCD_INFORM6) d6_state = DH6S_INFORM; else + /* CONFIRM lease triggered from RA */ d6_state = DH6S_CONFIRM; if (dhcp6_start(ifp, d6_state) == -1) logerr("%s: dhcp6_start", ifp->name); @@ -2278,8 +2281,7 @@ printpidfile: #ifndef SMALL if (ctx.options & DHCPCD_DUMPLEASE && - ioctl(fileno(stdin), FIONREAD, &i, sizeof(i)) == 0 && - i > 0) + ctx.ifc == 1 && ctx.ifv[0][0] == '-' && ctx.ifv[0][1] == '\0') { ctx.options |= DHCPCD_FORKED; /* pretend child process */ #ifdef PRIVSEP @@ -2720,6 +2722,7 @@ exit1: eloop_free(ctx.eloop); logclose(); free(ctx.logfile); + fflush(stdout); free(ctx.ctl_buf); #ifdef SETPROCTITLE_H setproctitle_fini(); Index: src/external/bsd/dhcpcd/dist/src/if-bsd.c diff -u src/external/bsd/dhcpcd/dist/src/if-bsd.c:1.31 src/external/bsd/dhcpcd/dist/src/if-bsd.c:1.32 --- src/external/bsd/dhcpcd/dist/src/if-bsd.c:1.31 Fri May 24 11:30:29 2024 +++ src/external/bsd/dhcpcd/dist/src/if-bsd.c Wed Feb 12 19:23:13 2025 @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* * BSD interface driver for dhcpcd - * Copyright (c) 2006-2023 Roy Marples <r...@marples.name> + * Copyright (c) 2006-2024 Roy Marples <r...@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -742,15 +742,12 @@ if_route(unsigned char cmd, const struct { rtm->rtm_index = (unsigned short)rt->rt_ifp->index; /* - * OpenBSD rejects the message for on-link routes. - * FreeBSD-12 kernel apparently panics. - * I can't replicate the panic, but better safe than sorry! - * https://roy.marples.name/archives/dhcpcd-discuss/0002286.html - * - * Neither OS currently allows IPv6 address sharing anyway, so let's - * try to encourage someone to fix that by logging a waring during compile. + * OpenBSD rejects this for on-link routes when there is no default route + * OpenBSD does not allow the same IPv6 address on different + * interfaces on the same network, so let's try to encourage someone to + * fix that by logging a waring during compile. */ -#if defined(__FreeBSD__) || defined(__OpenBSD__) +#ifdef __OpenBSD__ #warning kernel does not allow IPv6 address sharing if (!gateway_unspec || rt->rt_dest.sa_family!=AF_INET6) #endif @@ -1355,8 +1352,18 @@ if_ifa(struct dhcpcd_ctx *ctx, const str /* All BSD's set IFF_UP on the interface when adding an address. * But not all BSD's emit this via RTM_IFINFO when they do this ... */ - if (ifam->ifam_type == RTM_NEWADDR && !(ifp->flags & IFF_UP)) - dhcpcd_handlecarrier(ifp, ifp->carrier, ifp->flags | IFF_UP); + if (ifam->ifam_type == RTM_NEWADDR && !(ifp->flags & IFF_UP)) { + struct ifreq ifr = { .ifr_flags = 0 }; + + /* Don't blindly assume the interface is up though. + * We might get the address via a state change. */ + strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); + if (ioctl(ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr) == -1) + return -1; + if (ifr.ifr_flags & IFF_UP) + dhcpcd_handlecarrier(ifp, ifp->carrier, + ifp->flags | IFF_UP); + } switch (rti_info[RTAX_IFA]->sa_family) { case AF_LINK: @@ -1671,8 +1678,7 @@ if_machinearch(char *str, size_t len) } #ifdef INET6 -#if (defined(IPV6CTL_ACCEPT_RTADV) && !defined(ND6_IFF_ACCEPT_RTADV)) || \ - defined(IPV6CTL_FORWARDING) +#if (defined(IPV6CTL_ACCEPT_RTADV) && !defined(ND6_IFF_ACCEPT_RTADV)) #define get_inet6_sysctl(code) inet6_sysctl(code, 0, 0) #define set_inet6_sysctl(code, val) inet6_sysctl(code, val, 1) static int @@ -1744,39 +1750,6 @@ if_applyra(const struct ra *rap) #endif } -#ifndef IPV6CTL_FORWARDING -#define get_inet6_sysctlbyname(code) inet6_sysctlbyname(code, 0, 0) -#define set_inet6_sysctlbyname(code, val) inet6_sysctlbyname(code, val, 1) -static int -inet6_sysctlbyname(const char *name, int val, int action) -{ - size_t size; - - size = sizeof(val); - if (action) { - if (sysctlbyname(name, NULL, 0, &val, size) == -1) - return -1; - return 0; - } - if (sysctlbyname(name, &val, &size, NULL, 0) == -1) - return -1; - return val; -} -#endif - -int -ip6_forwarding(__unused const char *ifname) -{ - int val; - -#ifdef IPV6CTL_FORWARDING - val = get_inet6_sysctl(IPV6CTL_FORWARDING); -#else - val = get_inet6_sysctlbyname("net.inet6.ip6.forwarding"); -#endif - return val < 0 ? 0 : val; -} - #ifdef SIOCIFAFATTACH static int if_af_attach(const struct interface *ifp, int af) Index: src/external/bsd/dhcpcd/dist/src/ipv6nd.c diff -u src/external/bsd/dhcpcd/dist/src/ipv6nd.c:1.31 src/external/bsd/dhcpcd/dist/src/ipv6nd.c:1.32 --- src/external/bsd/dhcpcd/dist/src/ipv6nd.c:1.31 Fri May 24 11:30:29 2024 +++ src/external/bsd/dhcpcd/dist/src/ipv6nd.c Wed Feb 12 19:23:13 2025 @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - IPv6 ND handling - * Copyright (c) 2006-2023 Roy Marples <r...@marples.name> + * Copyright (c) 2006-2024 Roy Marples <r...@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -434,167 +434,6 @@ sent: logwarnx("%s: no IPv6 Routers available", ifp->name); } -#ifdef ND6_ADVERTISE -static void -ipv6nd_sendadvertisement(void *arg) -{ - struct ipv6_addr *ia = arg; - struct interface *ifp = ia->iface; - 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 }; - union { - struct cmsghdr hdr; - uint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; - } cmsgbuf = { .buf = { 0 } }; - struct msghdr msg = { - .msg_name = &dst, .msg_namelen = sizeof(dst), - .msg_iov = &iov, .msg_iovlen = 1, - .msg_control = cmsgbuf.buf, .msg_controllen = sizeof(cmsgbuf.buf), - }; - struct cmsghdr *cm; - struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index }; - const struct rs_state *state = RS_CSTATE(ifp); - int s; - - if (state == NULL || !if_is_link_up(ifp)) - goto freeit; - -#ifdef SIN6_LEN - dst.sin6_len = sizeof(dst); -#endif - - /* Set the outbound interface. */ - cm = CMSG_FIRSTHDR(&msg); - assert(cm != NULL); - cm->cmsg_level = IPPROTO_IPV6; - 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); - -#ifdef PRIVSEP - if (IN_PRIVSEP(ifp->ctx)) { - if (ps_inet_sendnd(ifp, &msg) == -1) - logerr(__func__); - goto sent; - } -#endif -#ifdef __sun - s = state->nd_fd; -#else - s = ctx->nd_fd; -#endif - if (sendmsg(s, &msg, 0) == -1) - logerr(__func__); - -#ifdef PRIVSEP -sent: -#endif - if (++ia->na_count < MAX_NEIGHBOR_ADVERTISEMENT) { - eloop_timeout_add_sec(ctx->eloop, - state->retrans / 1000, ipv6nd_sendadvertisement, ia); - return; - } - -freeit: - free(ia->na); - ia->na = NULL; - ia->na_count = 0; -} - -void -ipv6nd_advertise(struct ipv6_addr *ia) -{ - struct dhcpcd_ctx *ctx; - struct interface *ifp; - struct ipv6_state *state; - struct ipv6_addr *iap, *iaf; - struct nd_neighbor_advert *na; - - if (IN6_IS_ADDR_MULTICAST(&ia->addr)) - return; - -#ifdef __sun - if (!(ia->flags & IPV6_AF_AUTOCONF) && ia->flags & IPV6_AF_RAPFX) - return; -#endif - - ctx = ia->iface->ctx; - /* Find the most preferred address to advertise. */ - iaf = NULL; - TAILQ_FOREACH(ifp, ctx->ifaces, next) { - state = IPV6_STATE(ifp); - if (state == NULL || !if_is_link_up(ifp)) - continue; - - TAILQ_FOREACH(iap, &state->addrs, next) { - if (!IN6_ARE_ADDR_EQUAL(&iap->addr, &ia->addr)) - continue; - - /* Cancel any current advertisement. */ - eloop_timeout_delete(ctx->eloop, - ipv6nd_sendadvertisement, iap); - - /* Don't advertise what we can't use. */ - if (iap->prefix_vltime == 0 || - iap->addr_flags & IN6_IFF_NOTUSEABLE) - continue; - - if (iaf == NULL || - iaf->iface->metric > iap->iface->metric) - iaf = iap; - } - } - if (iaf == NULL) - return; - - /* Make the packet. */ - ifp = iaf->iface; - iaf->na_len = sizeof(*na); - if (ifp->hwlen != 0) - iaf->na_len += (size_t)ROUNDUP8(ifp->hwlen + 2); - na = calloc(1, iaf->na_len); - if (na == NULL) { - logerr(__func__); - return; - } - - na->nd_na_type = ND_NEIGHBOR_ADVERT; - na->nd_na_flags_reserved = ND_NA_FLAG_OVERRIDE; -#if defined(PRIVSEP) && (defined(__linux__) || defined(HAVE_PLEDGE)) - if (IN_PRIVSEP(ctx)) { - if (ps_root_ip6forwarding(ctx, ifp->name) != 0) - na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER; - } else -#endif - if (ip6_forwarding(ifp->name) != 0) - na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER; - na->nd_na_target = ia->addr; - - if (ifp->hwlen != 0) { - struct nd_opt_hdr *opt; - - opt = (struct nd_opt_hdr *)(na + 1); - opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; - opt->nd_opt_len = (uint8_t)((ROUNDUP8(ifp->hwlen + 2)) >> 3); - memcpy(opt + 1, ifp->hwaddr, ifp->hwlen); - } - - iaf->na_count = 0; - free(iaf->na); - iaf->na = na; - eloop_timeout_delete(ctx->eloop, ipv6nd_sendadvertisement, iaf); - ipv6nd_sendadvertisement(iaf); -} -#elif !defined(SMALL) -#warning kernel does not support userland sending ND6 advertisements -#endif /* ND6_ADVERTISE */ - static void ipv6nd_expire(void *arg) { @@ -842,7 +681,7 @@ ipv6nd_removefreedrop_ra(struct ra *rap, eloop_timeout_delete(rap->iface->ctx->eloop, NULL, rap); if (remove_ra) TAILQ_REMOVE(rap->iface->ctx->ra_routers, rap, next); - ipv6_freedrop_addrs(&rap->addrs, drop_ra, NULL); + ipv6_freedrop_addrs(&rap->addrs, drop_ra, 0, NULL); routeinfohead_free(&rap->rinfos); free(rap->data); free(rap); @@ -1065,9 +904,6 @@ try_script: ipv6nd_scriptrun(rap); } } -#ifdef ND6_ADVERTISE - ipv6nd_advertise(ia); -#endif } } @@ -1138,6 +974,12 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, bool new_ia; #endif +#define FREE_RAP(rap) \ + if (new_rap) \ + ipv6nd_removefreedrop_ra(rap, 0, 0); \ + else \ + ipv6nd_free_ra(rap); \ + if (ifp == NULL || RS_STATE(ifp) == NULL) { #ifdef DEBUG_RS logdebugx("RA for unexpected interface from %s", sfrom); @@ -1294,8 +1136,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, memcpy(&ndo, p, sizeof(ndo)); olen = (size_t)ndo.nd_opt_len * 8; if (olen == 0) { + /* RFC4681 4.6 says we MUST discard this ND packet. */ logerrx("%s: zero length option", ifp->name); - break; + FREE_RAP(rap); + return; } if (olen > len) { logerrx("%s: option length exceeds message", @@ -1319,10 +1163,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, else logwarnx("%s: reject RA (option %d) from %s", ifp->name, ndo.nd_opt_type, rap->sfrom); - if (new_rap) - ipv6nd_removefreedrop_ra(rap, 0, 0); - else - ipv6nd_free_ra(rap); + FREE_RAP(rap); return; } @@ -1573,10 +1414,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, { logwarnx("%s: reject RA (no option %s) from %s", ifp->name, dho->var, rap->sfrom); - if (new_rap) - ipv6nd_removefreedrop_ra(rap, 0, 0); - else - ipv6nd_free_ra(rap); + FREE_RAP(rap); return; } } @@ -1598,7 +1436,8 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, logwarnx("%s: %s: no longer a default router (lifetime = 0)", ifp->name, rap->sfrom); - if (new_data && !has_address && rap->lifetime && !ipv6_anyglobal(ifp)) + if (new_data && !has_address && rap->lifetime && + ifp->options->options & DHCPCD_GATEWAY && !ipv6_anyglobal(ifp)) logwarnx("%s: no global addresses for default route", ifp->name); @@ -1657,6 +1496,7 @@ nodhcp6: /* Expire should be called last as the rap object could be destroyed */ ipv6nd_expirera(ifp); +#undef FREE_RAP } bool @@ -1862,7 +1702,6 @@ ipv6nd_expirera(void *arg) struct interface *ifp; struct ra *rap, *ran; struct timespec now; - uint32_t elapsed; bool expired, valid; struct ipv6_addr *ia; struct routeinfo *rinfo, *rinfob; @@ -1874,11 +1713,11 @@ ipv6nd_expirera(void *arg) #endif struct nd_opt_dnssl dnssl; struct nd_opt_rdnss rdnss; - unsigned int next = 0, ltime; + uint32_t next = 0, ltime, elapsed; size_t nexpired = 0; ifp = arg; - clock_gettime(CLOCK_MONOTONIC, &now); + timespecclear(&now); expired = false; TAILQ_FOREACH_SAFE(rap, ifp->ctx->ra_routers, next, ran) { @@ -1886,10 +1725,10 @@ ipv6nd_expirera(void *arg) continue; valid = false; /* lifetime may be set to infinite by rfc4191 route information */ - if (rap->lifetime && rap->lifetime != ND6_INFINITE_LIFETIME) { - elapsed = (uint32_t)eloop_timespec_diff(&now, - &rap->acquired, NULL); - if (elapsed >= rap->lifetime || rap->doexpire) { + if (rap->lifetime) { + ltime = lifetime_left(rap->lifetime, + &rap->acquired, &now); + if (ltime == 0 || rap->doexpire) { if (!rap->expired) { logwarnx("%s: %s: router expired", ifp->name, rap->sfrom); @@ -1898,7 +1737,6 @@ ipv6nd_expirera(void *arg) } } else { valid = true; - ltime = rap->lifetime - elapsed; if (next == 0 || ltime < next) next = ltime; } @@ -1910,15 +1748,9 @@ ipv6nd_expirera(void *arg) TAILQ_FOREACH(ia, &rap->addrs, next) { if (ia->prefix_vltime == 0) continue; - if (ia->prefix_vltime == ND6_INFINITE_LIFETIME && - !rap->doexpire) - { - valid = true; - continue; - } - elapsed = (uint32_t)eloop_timespec_diff(&now, - &ia->acquired, NULL); - if (elapsed >= ia->prefix_vltime || rap->doexpire) { + ltime = lifetime_left(ia->prefix_vltime, + &ia->acquired, &now); + if (ltime == 0 || rap->doexpire) { if (ia->flags & IPV6_AF_ADDED) { logwarnx("%s: expired %s %s", ia->iface->name, @@ -1936,7 +1768,6 @@ ipv6nd_expirera(void *arg) expired = true; } else { valid = true; - ltime = ia->prefix_vltime - elapsed; if (next == 0 || ltime < next) next = ltime; } @@ -1944,12 +1775,9 @@ ipv6nd_expirera(void *arg) /* Expire route information */ TAILQ_FOREACH_SAFE(rinfo, &rap->rinfos, next, rinfob) { - if (rinfo->lifetime == ND6_INFINITE_LIFETIME && - !rap->doexpire) - continue; - elapsed = (uint32_t)eloop_timespec_diff(&now, - &rinfo->acquired, NULL); - if (elapsed >= rinfo->lifetime || rap->doexpire) { + ltime = lifetime_left(rinfo->lifetime, + &rinfo->acquired, &now); + if (ltime == 0 || rap->doexpire) { logwarnx("%s: expired route %s", rap->iface->name, rinfo->sprefix); TAILQ_REMOVE(&rap->rinfos, rinfo, next); @@ -2163,7 +1991,7 @@ ipv6nd_handledata(void *arg, unsigned sh } static void -ipv6nd_startrs1(void *arg) +ipv6nd_startrs2(void *arg) { struct interface *ifp = arg; struct rs_state *state; @@ -2195,22 +2023,43 @@ ipv6nd_startrs1(void *arg) ipv6nd_sendrsprobe(ifp); } -void -ipv6nd_startrs(struct interface *ifp) +static void +ipv6nd_startrs1(void *arg) { + struct interface *ifp = arg; unsigned int delay; - eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); if (!(ifp->options->options & DHCPCD_INITIAL_DELAY)) { - ipv6nd_startrs1(ifp); + ipv6nd_startrs2(ifp); return; } delay = arc4random_uniform(MAX_RTR_SOLICITATION_DELAY * MSEC_PER_SEC); logdebugx("%s: delaying IPv6 router solicitation for %0.1f seconds", ifp->name, (float)delay / MSEC_PER_SEC); - eloop_timeout_add_msec(ifp->ctx->eloop, delay, ipv6nd_startrs1, ifp); - return; + eloop_timeout_add_msec(ifp->ctx->eloop, delay, ipv6nd_startrs2, ifp); +} + +void +ipv6nd_startrs(struct interface *ifp) +{ + + if (ipv6_linklocal(ifp) == NULL) { + logdebugx("%s: " + "delaying IPv6 Router Solicitation for LL address", + ifp->name); + ipv6_addlinklocalcallback(ifp, ipv6nd_startrs1, ifp); + } else + ipv6nd_startrs1(ifp); +} + +void +ipv6nd_abort(struct interface *ifp) +{ + + eloop_timeout_delete(ifp->ctx->eloop, ipv6nd_startrs1, ifp); + eloop_timeout_delete(ifp->ctx->eloop, ipv6nd_startrs2, ifp); + eloop_timeout_delete(ifp->ctx->eloop, ipv6nd_sendrsprobe, ifp); } static struct routeinfo *routeinfo_findalloc(struct ra *rap, const struct in6_addr *prefix, uint8_t prefix_len) Index: src/external/bsd/dhcpcd/dist/src/if-options.c diff -u src/external/bsd/dhcpcd/dist/src/if-options.c:1.37 src/external/bsd/dhcpcd/dist/src/if-options.c:1.38 --- src/external/bsd/dhcpcd/dist/src/if-options.c:1.37 Fri May 24 11:30:29 2024 +++ src/external/bsd/dhcpcd/dist/src/if-options.c Wed Feb 12 19:23:13 2025 @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2023 Roy Marples <r...@marples.name> + * Copyright (c) 2006-2024 Roy Marples <r...@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -87,6 +87,8 @@ const struct option cf_options[] = { #ifndef SMALL {"msuserclass", required_argument, NULL, O_MSUSERCLASS}, #endif + {"vsio", required_argument, NULL, O_VSIO}, + {"vsio6", required_argument, NULL, O_VSIO6}, {"vendor", required_argument, NULL, 'v'}, {"waitip", optional_argument, NULL, 'w'}, {"exit", no_argument, NULL, 'x'}, @@ -654,6 +656,7 @@ parse_option(struct dhcpcd_ctx *ctx, con struct dhcp_opt **dop, *ndop; size_t *dop_len, dl, odl; struct vivco *vivco; + const struct vivco *vivco_endp = ifo->vivco + ifo->vivco_len; struct group *grp; #ifdef AUTH struct token *token; @@ -666,7 +669,11 @@ parse_option(struct dhcpcd_ctx *ctx, con struct if_ia *ia; uint8_t iaid[4]; #ifndef SMALL + struct in6_addr in6addr; struct if_sla *sla, *slap; + struct vsio **vsiop = NULL, *vsio; + size_t *vsio_lenp = NULL, opt_max, opt_header; + struct vsio_so *vsio_so; #endif #endif @@ -755,7 +762,7 @@ parse_option(struct dhcpcd_ctx *ctx, con case 'i': if (arg) s = parse_string((char *)ifo->vendorclassid + 1, - VENDORCLASSID_MAX_LEN, arg); + sizeof(ifo->vendorclassid) - 1, arg); else s = 0; if (s == -1) { @@ -897,6 +904,139 @@ parse_option(struct dhcpcd_ctx *ctx, con ifo->userclass[0] = (uint8_t)s; break; #endif + + case O_VSIO: +#ifndef SMALL + vsiop = &ifo->vsio; + vsio_lenp = &ifo->vsio_len; + opt_max = UINT8_MAX; + opt_header = sizeof(uint8_t) + sizeof(uint8_t); +#endif + /* FALLTHROUGH */ + case O_VSIO6: +#ifndef SMALL + if (vsiop == NULL) { + vsiop = &ifo->vsio6; + vsio_lenp = &ifo->vsio6_len; + opt_max = UINT16_MAX; + opt_header = sizeof(uint16_t) + sizeof(uint16_t); + } +#endif + ARG_REQUIRED; +#ifdef SMALL + logwarnx("%s: vendor options not compiled in", ifname); + return -1; +#else + fp = strwhite(arg); + if (fp) + *fp++ = '\0'; + u = (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e); + if (e) { + logerrx("invalid code: %s", arg); + return -1; + } + + fp = strskipwhite(fp); + p = strchr(fp, ','); + if (!p || !p[1]) { + logerrx("invalid vendor format: %s", arg); + return -1; + } + + /* Strip and preserve the comma */ + *p = '\0'; + i = (int)strtoi(fp, NULL, 0, 1, (intmax_t)opt_max, &e); + *p = ','; + if (e) { + logerrx("vendor option should be between" + " 1 and %zu inclusive", opt_max); + return -1; + } + + fp = p + 1; + + if (fp) { + if (inet_pton(AF_INET, fp, &addr) == 1) { + s = sizeof(addr.s_addr); + dl = (size_t)s; + np = malloc(dl); + if (np == NULL) { + logerr(__func__); + return -1; + } + memcpy(np, &addr.s_addr, dl); + } else if (inet_pton(AF_INET6, fp, &in6addr) == 1) { + s = sizeof(in6addr.s6_addr); + dl = (size_t)s; + np = malloc(dl); + if (np == NULL) { + logerr(__func__); + return -1; + } + memcpy(np, &in6addr.s6_addr, dl); + } else { + s = parse_string(NULL, 0, fp); + if (s == -1) { + logerr(__func__); + return -1; + } + dl = (size_t)s; + np = malloc(dl); + if (np == NULL) { + logerr(__func__); + return -1; + } + parse_string(np, dl, fp); + } + } else { + dl = 0; + np = NULL; + } + + for (sl = 0, vsio = *vsiop; sl < *vsio_lenp; sl++, vsio++) { + if (vsio->en == (uint32_t)u) + break; + } + if (sl == *vsio_lenp) { + vsio = reallocarray(*vsiop, *vsio_lenp + 1, + sizeof(**vsiop)); + if (vsio == NULL) { + logerr("%s: reallocarray vsio", __func__); + free(np); + return -1; + } + *vsiop = vsio; + vsio = &(*vsiop)[(*vsio_lenp)++]; + vsio->en = (uint32_t)u; + vsio->so = NULL; + vsio->so_len = 0; + } + + for (sl = 0, vsio_so = vsio->so; + sl < vsio->so_len; + sl++, vsio_so++) + opt_max -= opt_header + vsio_so->len; + if (opt_header + dl > opt_max) { + logerrx("vsio is too big: %s", fp); + free(np); + return -1; + } + + vsio_so = reallocarray(vsio->so, vsio->so_len + 1, + sizeof(*vsio_so)); + if (vsio_so == NULL) { + logerr("%s: reallocarray vsio_so", __func__); + free(np); + return -1; + } + + vsio->so = vsio_so; + vsio_so = &vsio->so[vsio->so_len++]; + vsio_so->opt = (uint16_t)i; + vsio_so->len = (uint16_t)dl; + vsio_so->data = np; + break; +#endif case 'v': ARG_REQUIRED; p = strchr(arg, ','); @@ -909,7 +1049,7 @@ parse_option(struct dhcpcd_ctx *ctx, con if (p == arg) { arg++; s = parse_string((char *)ifo->vendor + 1, - VENDOR_MAX_LEN, arg); + sizeof(ifo->vendor) - 1, arg); if (s == -1) { logerr("vendor"); return -1; @@ -936,7 +1076,7 @@ parse_option(struct dhcpcd_ctx *ctx, con } arg = p + 1; - s = VENDOR_MAX_LEN - ifo->vendor[0] - 2; + s = sizeof(ifo->vendor) - 1 - ifo->vendor[0] - 2; if (inet_aton(arg, &addr) == 1) { if (s < 6) { s = -1; @@ -1063,11 +1203,13 @@ parse_option(struct dhcpcd_ctx *ctx, con ifo->options |= DHCPCD_XID_HWADDR; break; case 'I': - /* Strings have a type of 0 */; - ifo->clientid[1] = 0; if (arg) + /* If parse_hwaddr cannot decoded arg as a + * hardware address then the first byte + * in the clientid will be zero to indicate + * a string value. */ s = parse_hwaddr((char *)ifo->clientid + 1, - CLIENTID_MAX_LEN, arg); + sizeof(ifo->clientid) - 1, arg); else s = 0; if (s == -1) { @@ -1218,7 +1360,7 @@ parse_option(struct dhcpcd_ctx *ctx, con if (p == NULL) break; ifo->mtu = (unsigned int)strtou(p, NULL, 0, - MTU_MIN, MTU_MAX, &e); + IPV4_MMTU, UINT_MAX, &e); if (e) { logerrx("invalid MTU %s", p); return -1; @@ -1972,6 +2114,10 @@ err_sla: break; case O_VENDCLASS: ARG_REQUIRED; +#ifdef SMALL + logwarnx("%s: vendor options not compiled in", ifname); + return -1; +#else fp = strwhite(arg); if (fp) *fp++ = '\0'; @@ -1980,6 +2126,12 @@ err_sla: logerrx("invalid code: %s", arg); return -1; } + for (vivco = ifo->vivco; vivco != vivco_endp; vivco++) { + if (vivco->en == (uint32_t)u) { + logerrx("vendor class option for enterprise number %u already defined", vivco->en); + return -1; + } + } fp = strskipwhite(fp); if (fp) { s = parse_string(NULL, 0, fp); @@ -2010,11 +2162,12 @@ err_sla: return -1; } ifo->vivco = vivco; - ifo->vivco_en = (uint32_t)u; vivco = &ifo->vivco[ifo->vivco_len++]; + vivco->en = (uint32_t)u; vivco->len = dl; vivco->data = (uint8_t *)np; break; +#endif case O_AUTHPROTOCOL: ARG_REQUIRED; #ifdef AUTH @@ -2318,7 +2471,8 @@ invalid_token: break; case O_MUDURL: ARG_REQUIRED; - s = parse_string((char *)ifo->mudurl + 1, MUDURL_MAX_LEN, arg); + s = parse_string((char *)ifo->mudurl + 1, + sizeof(ifo->mudurl) - 1, arg); if (s == -1) { logerr("mudurl"); return -1; @@ -2801,6 +2955,10 @@ free_options(struct dhcpcd_ctx *ctx, str #ifdef AUTH struct token *token; #endif +#ifndef SMALL + struct vsio *vsio; + struct vsio_so *vsio_so; +#endif if (ifo == NULL) return; @@ -2851,11 +3009,35 @@ free_options(struct dhcpcd_ctx *ctx, str opt++, ifo->dhcp6_override_len--) free_dhcp_opt_embenc(opt); free(ifo->dhcp6_override); +#ifndef SMALL for (vo = ifo->vivco; ifo->vivco_len > 0; vo++, ifo->vivco_len--) free(vo->data); free(ifo->vivco); + for (vsio = ifo->vsio; + ifo->vsio_len > 0; + vsio++, ifo->vsio_len--) + { + for (vsio_so = vsio->so; + vsio->so_len > 0; + vsio_so++, vsio->so_len--) + free(vsio_so->data); + free(vsio->so); + } + free(ifo->vsio); + for (vsio = ifo->vsio6; + ifo->vsio6_len > 0; + vsio++, ifo->vsio6_len--) + { + for (vsio_so = vsio->so; + vsio->so_len > 0; + vsio_so++, vsio->so_len--) + free(vsio_so->data); + free(vsio->so); + } + free(ifo->vsio6); +#endif for (opt = ifo->vivso_override; ifo->vivso_override_len > 0; opt++, ifo->vivso_override_len--) Index: src/external/bsd/dhcpcd/dist/src/ipv6.c diff -u src/external/bsd/dhcpcd/dist/src/ipv6.c:1.19 src/external/bsd/dhcpcd/dist/src/ipv6.c:1.20 --- src/external/bsd/dhcpcd/dist/src/ipv6.c:1.19 Fri May 24 11:30:29 2024 +++ src/external/bsd/dhcpcd/dist/src/ipv6.c Wed Feb 12 19:23:13 2025 @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2023 Roy Marples <r...@marples.name> + * Copyright (c) 2006-2024 Roy Marples <r...@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -89,7 +89,6 @@ #ifdef IPV6_POLLADDRFLAG # warning kernel does not report IPv6 address flag changes -# warning polling tentative address flags periodically #endif /* Hackery at it's finest. */ @@ -615,9 +614,8 @@ ipv6_deletedaddr(struct ipv6_addr *ia) #ifdef PRIVSEP if (!(ia->iface->ctx->options & DHCPCD_MANAGER)) ps_inet_closedhcp6(ia); -#elif defined(SMALL) - UNUSED(ia); -#else +#endif +#ifndef SMALL /* NOREJECT is set if we delegated exactly the prefix to another * address. * This can only be one address, so just clear the flag. @@ -625,8 +623,10 @@ ipv6_deletedaddr(struct ipv6_addr *ia) if (ia->delegating_prefix != NULL) ia->delegating_prefix->flags &= ~IPV6_AF_NOREJECT; #endif -#else - UNUSED(ia); +#endif + +#if !defined(DHCP6) || (!defined(PRIVSEP) && defined(SMALL)) + UNUSED(ia) #endif } @@ -652,45 +652,52 @@ ipv6_deleteaddr(struct ipv6_addr *ia) break; } } +} -#ifdef ND6_ADVERTISE - /* Advertise the address if it exists on another interface. */ - ipv6nd_advertise(ia); -#endif +static struct ipv6_state * +ipv6_getstate(struct interface *ifp) +{ + struct ipv6_state *state; + + state = IPV6_STATE(ifp); + if (state == NULL) { + ifp->if_data[IF_DATA_IPV6] = calloc(1, sizeof(*state)); + state = IPV6_STATE(ifp); + if (state == NULL) { + logerr(__func__); + return NULL; + } + TAILQ_INIT(&state->addrs); + TAILQ_INIT(&state->ll_callbacks); + } + return state; } static int -ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now) +ipv6_addaddr1(struct ipv6_addr *ia, struct timespec *now) { struct interface *ifp; uint32_t pltime, vltime; int loglevel; -#ifdef ND6_ADVERTISE - bool vltime_was_zero = ia->prefix_vltime == 0; -#endif -#ifdef __sun - struct ipv6_state *state; - struct ipv6_addr *ia2; + struct ipv6_addr *iaf; +#ifdef __sun /* If we re-add then address on Solaris then the prefix * route will be scrubbed and re-added. Something might * be using it, so let's avoid it. */ if (ia->flags & IPV6_AF_DADCOMPLETED) { logdebugx("%s: IP address %s already exists", ia->iface->name, ia->saddr); -#ifdef ND6_ADVERTISE - goto advertise; -#else return 0; -#endif } #endif /* Remember the interface of the address. */ ifp = ia->iface; - if (!(ia->flags & IPV6_AF_DADCOMPLETED) && - ipv6_iffindaddr(ifp, &ia->addr, IN6_IFF_NOTUSEABLE)) + /* Find any existing address. */ + iaf = ipv6_iffindaddr(ifp, &ia->addr, 0); + if (iaf != NULL && !(iaf->addr_flags & IN6_IFF_NOTUSEABLE)) ia->flags |= IPV6_AF_DADCOMPLETED; /* Adjust plftime and vltime based on acquired time */ @@ -704,31 +711,11 @@ ipv6_addaddr1(struct ipv6_addr *ia, cons ia->prefix_vltime = ia->prefix_pltime = ND6_INFINITE_LIFETIME; } - if (timespecisset(&ia->acquired) && - (ia->prefix_pltime != ND6_INFINITE_LIFETIME || - ia->prefix_vltime != ND6_INFINITE_LIFETIME)) - { - uint32_t elapsed; - struct timespec n; - - if (now == NULL) { - clock_gettime(CLOCK_MONOTONIC, &n); - now = &n; - } - elapsed = (uint32_t)eloop_timespec_diff(now, &ia->acquired, - NULL); - if (ia->prefix_pltime != ND6_INFINITE_LIFETIME) { - if (elapsed > ia->prefix_pltime) - ia->prefix_pltime = 0; - else - ia->prefix_pltime -= elapsed; - } - if (ia->prefix_vltime != ND6_INFINITE_LIFETIME) { - if (elapsed > ia->prefix_vltime) - ia->prefix_vltime = 0; - else - ia->prefix_vltime -= elapsed; - } + if (timespecisset(&ia->acquired)) { + ia->prefix_pltime = lifetime_left(ia->prefix_pltime, + &ia->acquired, now); + ia->prefix_vltime = lifetime_left(ia->prefix_vltime, + &ia->acquired, now); } loglevel = ia->flags & IPV6_AF_NEW ? LOG_INFO : LOG_DEBUG; @@ -793,35 +780,19 @@ ipv6_addaddr1(struct ipv6_addr *ia, cons } #endif -#ifdef __sun - /* Solaris does not announce new addresses which need DaD - * so we need to take a copy and add it to our list. - * Otherwise aliasing gets confused if we add another - * address during DaD. */ + /* Take a copy of the address and add it to our state if + * it does not exist. + * This is important if route overflow loses the message. */ + if (iaf == NULL) { + struct ipv6_state *state = ipv6_getstate(ifp); - state = IPV6_STATE(ifp); - TAILQ_FOREACH(ia2, &state->addrs, next) { - if (IN6_ARE_ADDR_EQUAL(&ia2->addr, &ia->addr)) - break; - } - if (ia2 == NULL) { - if ((ia2 = malloc(sizeof(*ia2))) == NULL) { + if ((iaf = malloc(sizeof(*iaf))) == NULL) { logerr(__func__); return 0; /* Well, we did add the address */ } - memcpy(ia2, ia, sizeof(*ia2)); - TAILQ_INSERT_TAIL(&state->addrs, ia2, next); + memcpy(iaf, ia, sizeof(*iaf)); + TAILQ_INSERT_TAIL(&state->addrs, iaf, next); } -#endif - -#ifdef ND6_ADVERTISE -#ifdef __sun -advertise: -#endif - /* Re-advertise the preferred address to be safe. */ - if (!vltime_was_zero) - ipv6nd_advertise(ia); -#endif return 0; } @@ -889,7 +860,7 @@ find_unit: #endif int -ipv6_addaddr(struct ipv6_addr *ia, const struct timespec *now) +ipv6_addaddr(struct ipv6_addr *ia, struct timespec *now) { int r; #ifdef ALIAS_ADDR @@ -965,7 +936,7 @@ ipv6_doaddr(struct ipv6_addr *ia, struct { /* A delegated prefix is not an address. */ - if (ia->flags & IPV6_AF_DELEGATEDPFX) + if (ia->flags & IPV6_AF_PFXDELEGATION) return 0; if (ia->prefix_vltime == 0) { @@ -984,8 +955,6 @@ ipv6_doaddr(struct ipv6_addr *ia, struct IN6_IS_ADDR_UNSPECIFIED(&ia->addr)) return 0; - if (!timespecisset(now)) - clock_gettime(CLOCK_MONOTONIC, now); ipv6_addaddr(ia, now); return ia->flags & IPV6_AF_NEW ? 1 : 0; } @@ -1019,7 +988,7 @@ ipv6_freeaddr(struct ipv6_addr *ia) struct ipv6_addr *iad; /* Forget the reference */ - if (ia->flags & IPV6_AF_DELEGATEDPFX) { + if (ia->flags & IPV6_AF_PFXDELEGATION) { TAILQ_FOREACH(iad, &ia->pd_pfxs, pd_next) { iad->delegating_prefix = NULL; } @@ -1040,7 +1009,7 @@ ipv6_freeaddr(struct ipv6_addr *ia) void ipv6_freedrop_addrs(struct ipv6_addrhead *addrs, int drop, - const struct interface *ifd) + unsigned int notflags, const struct interface *ifd) { struct ipv6_addr *ap, *apn, *apf; struct timespec now; @@ -1050,6 +1019,8 @@ ipv6_freedrop_addrs(struct ipv6_addrhead #endif timespecclear(&now); TAILQ_FOREACH_SAFE(ap, addrs, next, apn) { + if (ap->flags & notflags) + continue; #ifndef SMALL if (ifd != NULL && (ap->delegating_prefix == NULL || @@ -1077,12 +1048,7 @@ ipv6_freedrop_addrs(struct ipv6_addrhead ipv6_deleteaddr(ap); if (!(ap->iface->options->options & DHCPCD_EXITING) && apf) - { - if (!timespecisset(&now)) - clock_gettime(CLOCK_MONOTONIC, - &now); ipv6_addaddr(apf, &now); - } if (drop == 2) ipv6_freeaddr(ap); } @@ -1092,61 +1058,51 @@ ipv6_freedrop_addrs(struct ipv6_addrhead } } -static struct ipv6_state * -ipv6_getstate(struct interface *ifp) +static struct ipv6_addr * +ipv6_ifanyglobal(struct interface *ifp) { struct ipv6_state *state; + struct ipv6_addr *ia; state = IPV6_STATE(ifp); - if (state == NULL) { - ifp->if_data[IF_DATA_IPV6] = calloc(1, sizeof(*state)); - state = IPV6_STATE(ifp); - if (state == NULL) { - logerr(__func__); - return NULL; - } - TAILQ_INIT(&state->addrs); - TAILQ_INIT(&state->ll_callbacks); + if (state == NULL) + return NULL; + + TAILQ_FOREACH(ia, &state->addrs, next) { + if (IN6_IS_ADDR_LINKLOCAL(&ia->addr) || + IN6_IS_ADDR_LOOPBACK(&ia->addr)) + continue; + /* Let's be optimistic. + * Any decent OS won't forward or accept traffic + * from/to tentative or detached addresses. */ + if (!(ia->addr_flags & IN6_IFF_DUPLICATED)) + return ia; } - return state; + + return NULL; } struct ipv6_addr * ipv6_anyglobal(struct interface *sifp) { - struct interface *ifp; - struct ipv6_state *state; struct ipv6_addr *ia; - bool forwarding; - - /* BSD forwarding is either on or off. - * Linux forwarding is technically the same as it's - * configured by the "all" interface. - * Per interface only affects IsRouter of NA messages. */ -#if defined(PRIVSEP) && (defined(HAVE_PLEDGE) || defined(__linux__)) - if (IN_PRIVSEP(sifp->ctx)) - forwarding = ps_root_ip6forwarding(sifp->ctx, NULL) != 0; - else -#endif - forwarding = ip6_forwarding(NULL) != 0; + struct interface *ifp; + /* + * IPv6 source address selection will prefer the outgoing interface, + * but will also use any other interface if it things the address is + * a better fit for the destination. + * This logic is pretty much baked into all kernels and you + * don't need to be a router either. + * We only have this logic to work around badly configured IPv6 + * setups where there is a default router, but you're not handed + * a reachable address. This results in network timeouts which we + * want to actively avoid. + */ TAILQ_FOREACH(ifp, sifp->ctx->ifaces, next) { - if (ifp != sifp && !forwarding) - continue; - - state = IPV6_STATE(ifp); - if (state == NULL) - continue; - - TAILQ_FOREACH(ia, &state->addrs, next) { - if (IN6_IS_ADDR_LINKLOCAL(&ia->addr)) - continue; - /* Let's be optimistic. - * Any decent OS won't forward or accept traffic - * from/to tentative or detached addresses. */ - if (!(ia->addr_flags & IN6_IFF_DUPLICATED)) - return ia; - } + ia = ipv6_ifanyglobal(ifp); + if (ia != NULL) + return ia; } return NULL; } @@ -1198,22 +1154,20 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx, anyglobal = ipv6_anyglobal(ifp) != NULL; TAILQ_FOREACH(ia, &state->addrs, next) { - if (IN6_ARE_ADDR_EQUAL(&ia->addr, addr)) + if (IN6_ARE_ADDR_EQUAL(&ia->addr, addr)) { + ia->addr_flags = addrflags; break; + } } switch (cmd) { case RTM_DELADDR: if (ia != NULL) { TAILQ_REMOVE(&state->addrs, ia, next); -#ifdef ND6_ADVERTISE - /* Advertise the address if it exists on - * another interface. */ - ipv6nd_advertise(ia); -#endif /* We'll free it at the end of the function. */ } break; + case RTM_NEWADDR: if (ia == NULL) { ia = ipv6_newaddr(ifp, addr, prefix_len, 0); @@ -1244,49 +1198,52 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx, * generate a new temporary address on * restart. */ ia->acquired = ia->created; + ia->addr_flags = addrflags; TAILQ_INSERT_TAIL(&state->addrs, ia, next); } - ia->addr_flags = addrflags; ia->flags &= ~IPV6_AF_STALE; #ifdef IPV6_MANAGETEMPADDR if (ia->addr_flags & IN6_IFF_TEMPORARY) ia->flags |= IPV6_AF_TEMPORARY; #endif - if (IN6_IS_ADDR_LINKLOCAL(&ia->addr) || ia->dadcallback) { + #ifdef IPV6_POLLADDRFLAG - if (ia->addr_flags & IN6_IFF_TENTATIVE) { - eloop_timeout_add_msec( - ia->iface->ctx->eloop, - RETRANS_TIMER / 2, ipv6_checkaddrflags, ia); - break; - } + if ((IN6_IS_ADDR_LINKLOCAL(&ia->addr) || ia->dadcallback) && + ia->addr_flags & IN6_IFF_TENTATIVE) + { + eloop_timeout_add_msec( + ia->iface->ctx->eloop, + RETRANS_TIMER / 2, ipv6_checkaddrflags, ia); + } #endif - if (ia->dadcallback) - ia->dadcallback(ia); - - if (IN6_IS_ADDR_LINKLOCAL(&ia->addr) && - !(ia->addr_flags & IN6_IFF_NOTUSEABLE)) - { - /* Now run any callbacks. - * Typically IPv6RS or DHCPv6 */ - while ((cb = - TAILQ_FIRST(&state->ll_callbacks))) - { - TAILQ_REMOVE( - &state->ll_callbacks, - cb, next); - cb->callback(cb->arg); - free(cb); - } - } - } break; + + default: + return; } if (ia == NULL) return; + if (ia->dadcallback && ((ia->addr_flags & + (IN6_IFF_DETACHED | IN6_IFF_TENTATIVE)) == 0 || + ia->addr_flags & IN6_IFF_DUPLICATED)) + ia->dadcallback(ia); + + if (IN6_IS_ADDR_LINKLOCAL(&ia->addr) && + !(ia->addr_flags & IN6_IFF_NOTUSEABLE)) + { + /* Now run any callbacks. + * Typically IPv6RS or DHCPv6 */ + while ((cb = TAILQ_FIRST(&state->ll_callbacks))) + { + TAILQ_REMOVE(&state->ll_callbacks, cb, next); + cb->callback(cb->arg); + free(cb); + } + } + ctx->options &= ~DHCPCD_RTBUILD; ipv6nd_handleifa(cmd, ia, pid); #ifdef DHCP6 @@ -1332,17 +1289,19 @@ ipv6_iffindaddr(struct interface *ifp, c struct ipv6_addr *ap; state = IPV6_STATE(ifp); - if (state) { - TAILQ_FOREACH(ap, &state->addrs, next) { - if (addr == NULL) { - if (IN6_IS_ADDR_LINKLOCAL(&ap->addr) && - (!revflags || !(ap->addr_flags & revflags))) - return ap; - } else { - if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr) && - (!revflags || !(ap->addr_flags & revflags))) - return ap; - } + if (state == NULL) + return NULL; + + TAILQ_FOREACH(ap, &state->addrs, next) { + if (addr == NULL) { + if (IN6_IS_ADDR_LINKLOCAL(&ap->addr) && + (!revflags || !(ap->addr_flags & revflags))) + return ap; + } else if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr)) { + /* This is our address so we will return now */ + if (!revflags || !(ap->addr_flags & revflags)) + return ap; + return NULL; } } return NULL; @@ -1391,18 +1350,17 @@ ipv6_addlinklocalcallback(struct interfa state = ipv6_getstate(ifp); TAILQ_FOREACH(cb, &state->ll_callbacks, next) { if (cb->callback == callback && cb->arg == arg) - break; + return 0; } + + cb = malloc(sizeof(*cb)); if (cb == NULL) { - cb = malloc(sizeof(*cb)); - if (cb == NULL) { - logerr(__func__); - return -1; - } - cb->callback = callback; - cb->arg = arg; - TAILQ_INSERT_TAIL(&state->ll_callbacks, cb, next); + logerr(__func__); + return -1; } + cb->callback = callback; + cb->arg = arg; + TAILQ_INSERT_TAIL(&state->ll_callbacks, cb, next); return 0; } @@ -1609,38 +1567,13 @@ ipv6_newaddr(struct interface *ifp, cons struct ipv6_addr *ia, *iaf; char buf[INET6_ADDRSTRLEN]; const char *cbp; - bool tempaddr; - int addr_flags; - -#ifdef IPV6_AF_TEMPORARY - tempaddr = flags & IPV6_AF_TEMPORARY; -#else - tempaddr = false; -#endif - - /* If adding a new DHCP / RA derived address, check current flags - * from an existing address. */ - if (tempaddr) - iaf = NULL; - else if (flags & IPV6_AF_AUTOCONF) - iaf = ipv6nd_iffindprefix(ifp, addr, prefix_len); - else - iaf = ipv6_iffindaddr(ifp, addr, 0); - if (iaf != NULL) { - addr_flags = iaf->addr_flags; - flags |= IPV6_AF_ADDED; - } else - addr_flags = IN6_IFF_TENTATIVE; ia = calloc(1, sizeof(*ia)); if (ia == NULL) goto err; ia->iface = ifp; - ia->addr_flags = addr_flags; ia->flags = IPV6_AF_NEW | flags; - if (!(ia->addr_flags & IN6_IFF_NOTUSEABLE)) - ia->flags |= IPV6_AF_DADCOMPLETED; ia->prefix_len = prefix_len; ia->dhcp6_fd = -1; @@ -1652,6 +1585,7 @@ ipv6_newaddr(struct interface *ifp, cons goto makepfx; else if (ia->flags & IPV6_AF_AUTOCONF) { ia->prefix = *addr; + iaf = ipv6nd_iffindprefix(ifp, addr, prefix_len); if (iaf != NULL) memcpy(&ia->addr, &iaf->addr, sizeof(ia->addr)); else { @@ -1669,9 +1603,9 @@ ipv6_newaddr(struct interface *ifp, cons cbp = inet_ntop(AF_INET6, &ia->addr, buf, sizeof(buf)); goto paddr; #else - return ia; + goto flags; #endif - } else if (ia->flags & (IPV6_AF_REQUEST | IPV6_AF_DELEGATEDPFX)) { + } else if (ia->flags & (IPV6_AF_REQUEST | IPV6_AF_PFXDELEGATION)) { ia->prefix = *addr; cbp = inet_ntop(AF_INET6, &ia->prefix, buf, sizeof(buf)); goto paddr; @@ -1689,6 +1623,21 @@ paddr: goto err; snprintf(ia->saddr, sizeof(ia->saddr), "%s/%d", cbp, ia->prefix_len); +#ifndef __sun +flags: +#endif + /* If adding a new DHCP / RA derived address, check current flags + * from an existing address. */ + iaf = ipv6_iffindaddr(ifp, &ia->addr, 0); + if (iaf != NULL) { + ia->addr_flags = iaf->addr_flags; + ia->flags |= IPV6_AF_ADDED; + } else + ia->addr_flags |= IN6_IFF_TENTATIVE; + + if (!(ia->addr_flags & IN6_IFF_NOTUSEABLE)) + ia->flags |= IPV6_AF_DADCOMPLETED; + return ia; err: @@ -1857,7 +1806,7 @@ ipv6_freedrop(struct interface *ifp, int free(cb); } - ipv6_freedrop_addrs(&state->addrs, drop ? 2 : 0, NULL); + ipv6_freedrop_addrs(&state->addrs, drop ? 2 : 0, 0, NULL); if (drop) { if (ifp->ctx->ra_routers != NULL) rt_build(ifp->ctx, AF_INET6); @@ -1883,10 +1832,13 @@ ipv6_handleifa_addrs(int cmd, struct ipv6_addrhead *addrs, const struct ipv6_addr *addr, pid_t pid) { struct ipv6_addr *ia, *ian; - uint8_t found, alldadcompleted; + int found = 0, alldadcompleted = 1; + + if (cmd != RTM_NEWADDR && cmd != RTM_DELADDR) { + errno = EINVAL; + return -1; + } - alldadcompleted = 1; - found = 0; TAILQ_FOREACH_SAFE(ia, addrs, next, ian) { if (!IN6_ARE_ADDR_EQUAL(&addr->addr, &ia->addr)) { if (ia->flags & IPV6_AF_ADDED && @@ -1894,34 +1846,35 @@ ipv6_handleifa_addrs(int cmd, alldadcompleted = 0; continue; } - switch (cmd) { - case RTM_DELADDR: - if (ia->flags & IPV6_AF_ADDED) { - logwarnx("%s: pid %d deleted address %s", - ia->iface->name, pid, ia->saddr); - ia->flags &= ~IPV6_AF_ADDED; - } + + ia->addr_flags = addr->addr_flags; + + if (cmd == RTM_DELADDR && ia->flags & IPV6_AF_ADDED) + logwarnx("%s: pid %d deleted address %s", + ia->iface->name, pid, ia->saddr); + + /* Check DAD. + * On Linux we can get IN6_IFF_DUPLICATED via RTM_DELADDR. */ + if (((ia->addr_flags & + (IN6_IFF_DETACHED | IN6_IFF_TENTATIVE)) == 0 || + ia->addr_flags & IN6_IFF_DUPLICATED) && + (ia->flags & IPV6_AF_DADCOMPLETED) == 0) + { + found++; + if (ia->dadcallback) + ia->dadcallback(ia); + /* We need to set this here in-case the + * dadcallback function checks it */ + ia->flags |= IPV6_AF_DADCOMPLETED; + } + + if (cmd == RTM_DELADDR) { + ia->flags &= ~IPV6_AF_ADDED; ipv6_deletedaddr(ia); if (ia->flags & IPV6_AF_DELEGATED) { TAILQ_REMOVE(addrs, ia, next); ipv6_freeaddr(ia); } - break; - case RTM_NEWADDR: - ia->addr_flags = addr->addr_flags; - /* Safety - ignore tentative announcements */ - if (ia->addr_flags & - (IN6_IFF_DETACHED | IN6_IFF_TENTATIVE)) - break; - if ((ia->flags & IPV6_AF_DADCOMPLETED) == 0) { - found++; - if (ia->dadcallback) - ia->dadcallback(ia); - /* We need to set this here in-case the - * dadcallback function checks it */ - ia->flags |= IPV6_AF_DADCOMPLETED; - } - break; } } @@ -2093,7 +2046,7 @@ valid: } void -ipv6_addtempaddrs(struct interface *ifp, const struct timespec *now) +ipv6_addtempaddrs(struct interface *ifp, struct timespec *now) { struct ipv6_state *state; struct ipv6_addr *ia; @@ -2238,7 +2191,7 @@ inet6_makeprefix(struct interface *ifp, /* This address is the delegated prefix, so add a reject route for * it via the loopback interface. */ - if (addr->flags & IPV6_AF_DELEGATEDPFX) { + if (addr->flags & IPV6_AF_PFXDELEGATION) { struct interface *lo0; TAILQ_FOREACH(lo0, ifp->ctx->ifaces, next) { @@ -2258,7 +2211,7 @@ inet6_makeprefix(struct interface *ifp, sa_in6_init(&rt->rt_dest, &addr->prefix); ipv6_mask(&netmask, addr->prefix_len); sa_in6_init(&rt->rt_netmask, &netmask); - if (addr->flags & IPV6_AF_DELEGATEDPFX) { + if (addr->flags & IPV6_AF_PFXDELEGATION) { rt->rt_flags |= RTF_REJECT; /* Linux does not like a gateway for a reject route. */ #ifndef __linux__ @@ -2321,10 +2274,13 @@ inet6_raroutes(rb_tree_t *routes, struct const struct routeinfo *rinfo; const struct ipv6_addr *addr; struct in6_addr netmask; + struct timespec now; if (ctx->ra_routers == NULL) return 0; + timespecclear(&now); + TAILQ_FOREACH(rap, ctx->ra_routers, next) { if (rap->expired) continue; @@ -2341,10 +2297,14 @@ inet6_raroutes(rb_tree_t *routes, struct sa_in6_init(&rt->rt_dest, &rinfo->prefix); sa_in6_init(&rt->rt_netmask, &netmask); sa_in6_init(&rt->rt_gateway, &rap->from); + rt->rt_dflags |= RTDF_RA; #ifdef HAVE_ROUTE_PREF rt->rt_pref = ipv6nd_rtpref(rinfo->flags); #endif - +#ifdef HAVE_ROUTE_LIFETIME + rt->rt_lifetime = lifetime_left(rinfo->lifetime, + &rinfo->acquired, &now); +#endif rt_proto_add(routes, rt); } @@ -2358,6 +2318,12 @@ inet6_raroutes(rb_tree_t *routes, struct #ifdef HAVE_ROUTE_PREF rt->rt_pref = ipv6nd_rtpref(rap->flags); #endif +#ifdef HAVE_ROUTE_LIFETIME + rt->rt_lifetime = + lifetime_left(addr->prefix_vltime, + &addr->acquired, &now); +#endif + rt_proto_add(routes, rt); } } @@ -2365,6 +2331,21 @@ inet6_raroutes(rb_tree_t *routes, struct /* add default route */ if (rap->lifetime == 0) continue; + /* + * We only want to install a default route if we have + * an address that we can use over it. + * If we don't have any global addresses then the link-local + * address would be used instead and we wouldn't reach + * our destination and even if we could, they wouldn't + * be able to reply back to us. + * This avoids timeouts on badly configured IPv6 setups + * where there is a default router but it or a DHCPv6 server + * doesn't hand out an address. + * If an address appears from anywhere, dhcpcd will spot this + * and then add the default like. + * Likewise, if all global addresses are removed then dhcpcd + * will remove the default route. + */ if (ipv6_anyglobal(rap->iface) == NULL) continue; rt = inet6_makerouter(rap); @@ -2374,6 +2355,11 @@ inet6_raroutes(rb_tree_t *routes, struct #ifdef HAVE_ROUTE_PREF rt->rt_pref = ipv6nd_rtpref(rap->flags); #endif +#ifdef HAVE_ROUTE_LIFETIME + rt->rt_lifetime = lifetime_left(rap->lifetime, + &rap->acquired, &now); +#endif + rt_proto_add(routes, rt); } return 0; @@ -2386,19 +2372,30 @@ inet6_dhcproutes(rb_tree_t *routes, stru { struct interface *ifp; const struct dhcp6_state *d6_state; - const struct ipv6_addr *addr; + const struct ipv6_addr *ia; struct rt *rt; TAILQ_FOREACH(ifp, ctx->ifaces, next) { d6_state = D6_CSTATE(ifp); - if (d6_state && d6_state->state == dstate) { - TAILQ_FOREACH(addr, &d6_state->addrs, next) { - rt = inet6_makeprefix(ifp, NULL, addr); - if (rt == NULL) + if (d6_state == NULL) + continue; + + // Don't test the actual state as we could + // be between states with still valid routes + + TAILQ_FOREACH(ia, &d6_state->addrs, next) { + if (dstate == DH6S_DELEGATED) { + // Reject route won't have IPV6_AF_ADDED + if (!(ia->flags & IPV6_AF_PFXDELEGATION)) continue; - rt->rt_dflags |= RTDF_DHCP; - rt_proto_add(routes, rt); - } + } else if (!(ia->flags & IPV6_AF_ADDED)) + continue; + + rt = inet6_makeprefix(ifp, NULL, ia); + if (rt == NULL) + continue; + rt->rt_dflags |= RTDF_DHCP; + rt_proto_add(routes, rt); } } return 0; Index: src/external/bsd/dhcpcd/dist/src/privsep.c diff -u src/external/bsd/dhcpcd/dist/src/privsep.c:1.19 src/external/bsd/dhcpcd/dist/src/privsep.c:1.20 --- src/external/bsd/dhcpcd/dist/src/privsep.c:1.19 Fri May 24 11:30:29 2024 +++ src/external/bsd/dhcpcd/dist/src/privsep.c Wed Feb 12 19:23:13 2025 @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* * Privilege Separation for dhcpcd - * Copyright (c) 2006-2023 Roy Marples <r...@marples.name> + * Copyright (c) 2006-2024 Roy Marples <r...@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -125,12 +125,20 @@ static int ps_dropprivs(struct dhcpcd_ctx *ctx) { struct passwd *pw = ctx->ps_user; + int fd_out = ctx->options & DHCPCD_DUMPLEASE ? + STDOUT_FILENO : STDERR_FILENO; if (ctx->options & DHCPCD_LAUNCHER) +#ifdef ASAN + logwarnx("not chrooting as compiled for ASAN"); +#else logdebugx("chrooting as %s to %s", pw->pw_name, pw->pw_dir); + if (chroot(pw->pw_dir) == -1 && (errno != EPERM || ctx->options & DHCPCD_FORKED)) logerr("%s: chroot: %s", __func__, pw->pw_dir); +#endif + if (chdir("/") == -1) logerr("%s: chdir: /", __func__); @@ -172,7 +180,7 @@ ps_dropprivs(struct dhcpcd_ctx *ctx) * Obviously this won't work if we are using a logfile * or redirecting stderr to a file. */ if ((ctx->options & DHC_NOCHKIO) == DHC_NOCHKIO || - (ctx->logfile == NULL && isatty(STDERR_FILENO) == 1)) + (ctx->logfile == NULL && isatty(fd_out) == 1)) { if (setrlimit(RLIMIT_FSIZE, &rzero) == -1) logerr("setrlimit RLIMIT_FSIZE"); @@ -1131,7 +1139,7 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int len = read(fd, &psm, sizeof(psm)); #ifdef PRIVSEP_DEBUG - logdebugx("%s: %zd", __func__, len); + logdebugx("%s: fd=%d %zd", __func__, fd, len); #endif if (len == -1 || len == 0) Index: src/external/bsd/dhcpcd/dist/src/ipv6.h diff -u src/external/bsd/dhcpcd/dist/src/ipv6.h:1.14 src/external/bsd/dhcpcd/dist/src/ipv6.h:1.15 --- src/external/bsd/dhcpcd/dist/src/ipv6.h:1.14 Fri Apr 21 16:54:26 2023 +++ src/external/bsd/dhcpcd/dist/src/ipv6.h Wed Feb 12 19:23:13 2025 @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2023 Roy Marples <r...@marples.name> + * Copyright (c) 2006-2024 Roy Marples <r...@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -158,19 +158,6 @@ # define IN6_IFF_DETACHED 0 #endif -/* - * ND6 Advertising is only used for IP address sharing to prefer - * the address on a specific interface. - * This just fails to work on OpenBSD and causes erroneous duplicate - * address messages on BSD's other then DragonFly and NetBSD. - */ -#if !defined(SMALL) && \ - ((defined(__DragonFly_version) && __DragonFly_version >= 500703) || \ - (defined(__NetBSD_Version__) && __NetBSD_Version__ >= 899002800) || \ - defined(__linux__) || defined(__sun)) -# define ND6_ADVERTISE -#endif - #ifdef INET6 TAILQ_HEAD(ipv6_addrhead, ipv6_addr); struct ipv6_addr { @@ -217,8 +204,8 @@ struct ipv6_addr { #define IPV6_AF_ADDED (1U << 3) #define IPV6_AF_AUTOCONF (1U << 4) #define IPV6_AF_DADCOMPLETED (1U << 5) -#define IPV6_AF_DELEGATED (1U << 6) -#define IPV6_AF_DELEGATEDPFX (1U << 7) +#define IPV6_AF_PFXDELEGATION (1U << 6) +#define IPV6_AF_DELEGATED (1U << 7) #define IPV6_AF_NOREJECT (1U << 8) #define IPV6_AF_REQUEST (1U << 9) #define IPV6_AF_STATIC (1U << 10) @@ -227,8 +214,9 @@ struct ipv6_addr { #define IPV6_AF_EXTENDED (1U << 13) #define IPV6_AF_REGEN (1U << 14) #define IPV6_AF_ROUTER (1U << 15) +#define IPV6_AF_ADVERTISED (1U << 16) #ifdef IPV6_MANAGETEMPADDR -#define IPV6_AF_TEMPORARY (1U << 16) +#define IPV6_AF_TEMPORARY (1U << 17) #endif struct ll_callback { @@ -266,11 +254,11 @@ int ipv6_userprefix( const struct in6_ad void ipv6_checkaddrflags(void *); void ipv6_markaddrsstale(struct interface *, unsigned int); void ipv6_deletestaleaddrs(struct interface *); -int ipv6_addaddr(struct ipv6_addr *, const struct timespec *); +int ipv6_addaddr(struct ipv6_addr *, struct timespec *); int ipv6_doaddr(struct ipv6_addr *, struct timespec *); ssize_t ipv6_addaddrs(struct ipv6_addrhead *addrs); void ipv6_deleteaddr(struct ipv6_addr *); -void ipv6_freedrop_addrs(struct ipv6_addrhead *, int, +void ipv6_freedrop_addrs(struct ipv6_addrhead *, int, unsigned int, const struct interface *); void ipv6_handleifa(struct dhcpcd_ctx *ctx, int, struct if_head *, const char *, const struct in6_addr *, uint8_t, int, pid_t); @@ -301,7 +289,7 @@ void ipv6_freedrop(struct interface *, i struct ipv6_addr *ipv6_createtempaddr(struct ipv6_addr *, const struct timespec *); struct ipv6_addr *ipv6_settemptime(struct ipv6_addr *, int); -void ipv6_addtempaddrs(struct interface *, const struct timespec *); +void ipv6_addtempaddrs(struct interface *, struct timespec *); void ipv6_regentempaddrs(void *); #endif Index: src/external/bsd/dhcpcd/dist/src/logerr.c diff -u src/external/bsd/dhcpcd/dist/src/logerr.c:1.14 src/external/bsd/dhcpcd/dist/src/logerr.c:1.15 --- src/external/bsd/dhcpcd/dist/src/logerr.c:1.14 Fri May 24 11:30:29 2024 +++ src/external/bsd/dhcpcd/dist/src/logerr.c Wed Feb 12 19:23:13 2025 @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* * logerr: errx with logging - * Copyright (c) 2006-2023 Roy Marples <r...@marples.name> + * Copyright (c) 2006-2024 Roy Marples <r...@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -447,7 +447,7 @@ int logopen(const char *path) { struct logctx *ctx = &_logctx; - int opts = 0; + int opts = LOG_NDELAY; /* Ensure openlog gets a fd */ /* Cache timezone */ tzset(); Index: src/external/bsd/dhcpcd/dist/src/script.c diff -u src/external/bsd/dhcpcd/dist/src/script.c:1.17 src/external/bsd/dhcpcd/dist/src/script.c:1.18 --- src/external/bsd/dhcpcd/dist/src/script.c:1.17 Thu Oct 19 11:26:52 2023 +++ src/external/bsd/dhcpcd/dist/src/script.c Wed Feb 12 19:23:13 2025 @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2023 Roy Marples <r...@marples.name> + * Copyright (c) 2006-2024 Roy Marples <r...@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -738,6 +738,7 @@ script_dump(const char *env, size_t len) env += 4; printf("%s\n", env); } + fflush(stdout); return 0; }