From 54175cef3f7cfb8497d1d0169f5866430740c13d Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Fri, 24 May 2024 11:52:56 +0100 Subject: [PATCH] Import dhcpcd-10.0.7 with the following changes: * DHCP: use request_time, fallback_time and ipv4ll_time rather than reboot timeout * DHCP6: Wait for IRT to elapse before requesting advertisments * DHCPv6: Don't re-INFORM if the RA changes * privsep: Reduce fd use * dhcpcd: Add support for arp persist defence * Move dhcp(v4) packet size check earlier * Define the Azure Endpoint and other site-specific options * add RFC4191 support by @goertzenator in #297 * dhcpcd: Respect IPV6_PREFERRED_ONLY flag regardless of state * Fix time_offset to be int to match RFC-2132 * hooks/30-hostname: Exit with 0 if setting hostname is not needed --- contrib/dhcpcd/hooks/10-wpa_supplicant | 6 +- contrib/dhcpcd/hooks/30-hostname | 2 +- contrib/dhcpcd/src/arp.c | 14 +++- contrib/dhcpcd/src/defs.h | 2 +- contrib/dhcpcd/src/dhcp.c | 39 ++++++---- contrib/dhcpcd/src/dhcp6.c | 65 ++++++++++++---- contrib/dhcpcd/src/dhcpcd-embedded.c | 31 +++++++- contrib/dhcpcd/src/dhcpcd-embedded.h | 2 +- contrib/dhcpcd/src/dhcpcd.c | 80 +++++++++++++------ contrib/dhcpcd/src/dhcpcd.conf.5 | 18 ++++- contrib/dhcpcd/src/if-bsd.c | 26 ++++++- contrib/dhcpcd/src/if-options.c | 45 ++++++++++- contrib/dhcpcd/src/if-options.h | 11 +++ contrib/dhcpcd/src/if.c | 27 ++++--- contrib/dhcpcd/src/ipv6.c | 29 ++++++- contrib/dhcpcd/src/ipv6nd.c | 135 ++++++++++++++++++++++++++++++--- contrib/dhcpcd/src/ipv6nd.h | 19 ++++- contrib/dhcpcd/src/logerr.c | 2 + contrib/dhcpcd/src/privsep-bpf.c | 5 ++ contrib/dhcpcd/src/privsep-control.c | 27 ++++--- contrib/dhcpcd/src/privsep-inet.c | 2 + contrib/dhcpcd/src/privsep-root.c | 41 ++++++---- contrib/dhcpcd/src/privsep-root.h | 1 + contrib/dhcpcd/src/privsep.c | 25 +++--- contrib/dhcpcd/src/sa.c | 46 +++++++---- contrib/dhcpcd/src/sa.h | 1 + 26 files changed, 560 insertions(+), 141 deletions(-) diff --git a/contrib/dhcpcd/hooks/10-wpa_supplicant b/contrib/dhcpcd/hooks/10-wpa_supplicant index 1da0108dc6..e27ea170c5 100644 --- a/contrib/dhcpcd/hooks/10-wpa_supplicant +++ b/contrib/dhcpcd/hooks/10-wpa_supplicant @@ -106,8 +106,8 @@ if [ "$ifwireless" = "1" ] && \ command -v wpa_cli >/dev/null 2>&1 then case "$reason" in - PREINIT) wpa_supplicant_start;; - RECONFIGURE) wpa_supplicant_reconfigure;; - DEPARTED) wpa_supplicant_stop;; + PREINIT) wpa_supplicant_start;; + RECONFIGURE) wpa_supplicant_reconfigure;; + DEPARTED|STOPPED) wpa_supplicant_stop;; esac fi diff --git a/contrib/dhcpcd/hooks/30-hostname b/contrib/dhcpcd/hooks/30-hostname index cd776771fb..6ac3933c34 100644 --- a/contrib/dhcpcd/hooks/30-hostname +++ b/contrib/dhcpcd/hooks/30-hostname @@ -118,7 +118,7 @@ set_hostname() *) hshort=true;; esac - need_hostname || return + need_hostname || return 0 if [ -n "$new_fqdn" ]; then if ${hfqdn} || ! ${hshort}; then diff --git a/contrib/dhcpcd/src/arp.c b/contrib/dhcpcd/src/arp.c index 453ab2df60..4b894e2739 100644 --- a/contrib/dhcpcd/src/arp.c +++ b/contrib/dhcpcd/src/arp.c @@ -173,12 +173,24 @@ arp_found(struct arp_state *astate, const struct arp_msg *amsg) * the other IPv4LL client will receieve two ARP * messages. * If another conflict happens within DEFEND_INTERVAL - * then we must drop our address and negotiate a new one. */ + * then we must drop our address and negotiate a new one. + * If DHCPCD_ARP_PERSISTDEFENCE is set, that enables + * RFC5227 section 2.4.c behaviour. Upon conflict + * detection, the host records the time that the + * conflicting ARP packet was received, and then + * broadcasts one single ARP Announcement. The host then + * continues to use the address normally. All further + * conflict notifications within the DEFEND_INTERVAL are + * ignored. */ clock_gettime(CLOCK_MONOTONIC, &now); if (timespecisset(&astate->defend) && eloop_timespec_diff(&now, &astate->defend, NULL) < DEFEND_INTERVAL) + { logwarnx("%s: %d second defence failed for %s", ifp->name, DEFEND_INTERVAL, inet_ntoa(astate->addr)); + if (ifp->options->options & DHCPCD_ARP_PERSISTDEFENCE) + return; + } else if (arp_request(astate, &astate->addr) == -1) logerr(__func__); else { diff --git a/contrib/dhcpcd/src/defs.h b/contrib/dhcpcd/src/defs.h index c46b2e54a6..5513950094 100644 --- a/contrib/dhcpcd/src/defs.h +++ b/contrib/dhcpcd/src/defs.h @@ -29,7 +29,7 @@ #define DEFS_H #define PACKAGE "dhcpcd" -#define VERSION "10.0.6" +#define VERSION "10.0.7" #ifndef PRIVSEP_USER # define PRIVSEP_USER "_" PACKAGE diff --git a/contrib/dhcpcd/src/dhcp.c b/contrib/dhcpcd/src/dhcp.c index 014ce6cfd5..6e6b49a2ff 100644 --- a/contrib/dhcpcd/src/dhcp.c +++ b/contrib/dhcpcd/src/dhcp.c @@ -1877,13 +1877,13 @@ dhcp_discover(void *arg) dhcp_new_xid(ifp); eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); if (!(state->added & STATE_EXPIRED)) { - if (ifo->fallback) + if (ifo->fallback && ifo->fallback_time) eloop_timeout_add_sec(ifp->ctx->eloop, - ifo->reboot, dhcp_fallback, ifp); + ifo->fallback_time, dhcp_fallback, ifp); #ifdef IPV4LL else if (ifo->options & DHCPCD_IPV4LL) eloop_timeout_add_sec(ifp->ctx->eloop, - ifo->reboot, ipv4ll_start, ifp); + ifo->ipv4ll_time, ipv4ll_start, ifp); #endif } if (ifo->options & DHCPCD_REQUEST) @@ -1914,11 +1914,13 @@ dhcp_request(void *arg) { struct interface *ifp = arg; struct dhcp_state *state = D_STATE(ifp); + struct if_options *ifo = ifp->options; state->state = DHS_REQUEST; // Handle the server being silent to our request. - eloop_timeout_add_sec(ifp->ctx->eloop, ifp->options->reboot, - dhcp_requestfailed, ifp); + if (ifo->request_time != 0) + eloop_timeout_add_sec(ifp->ctx->eloop, ifo->request_time, + dhcp_requestfailed, ifp); send_request(ifp); } @@ -1944,7 +1946,11 @@ dhcp_expire(void *arg) static void dhcp_decline(struct interface *ifp) { + struct dhcp_state *state = D_STATE(ifp); + // Set the expired state so we send over BPF as this could be + // an address defence failure. + state->added |= STATE_EXPIRED; send_message(ifp, DHCP_DECLINE, NULL); } #endif @@ -2098,8 +2104,12 @@ static void dhcp_arp_defend_failed(struct arp_state *astate) { struct interface *ifp = astate->iface; + struct dhcp_state *state = D_STATE(ifp); + if (!(ifp->options->options & (DHCPCD_INFORM | DHCPCD_STATIC))) + dhcp_decline(ifp); dhcp_drop(ifp, "EXPIRED"); + dhcp_unlink(ifp->ctx, state->leasefile); dhcp_start1(ifp); } #endif @@ -2740,7 +2750,7 @@ dhcp_reboot(struct interface *ifp) /* Need to add this before dhcp_expire and friends. */ if (!ifo->fallback && ifo->options & DHCPCD_IPV4LL) eloop_timeout_add_sec(ifp->ctx->eloop, - ifo->reboot, ipv4ll_start, ifp); + ifo->ipv4ll_time, ipv4ll_start, ifp); #endif if (ifo->options & DHCPCD_LASTLEASE && state->lease.frominfo) @@ -3199,8 +3209,8 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len, 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 || state->state == DHS_REBOOT)) + DHO_IPV6_PREFERRED_ONLY) == 0 && (state->state == DHS_DISCOVER || + state->state == DHS_REBOOT || state->state == DHS_NONE)) { char v6msg[128]; @@ -3524,12 +3534,6 @@ dhcp_handlebootp(struct interface *ifp, struct bootp *bootp, size_t len, { size_t v; - if (len < offsetof(struct bootp, vend)) { - logerrx("%s: truncated packet (%zu) from %s", - ifp->name, len, inet_ntoa(*from)); - return; - } - /* Unlikely, but appeases sanitizers. */ if (len > FRAMELEN_MAX) { logerrx("%s: packet exceeded frame length (%zu) from %s", @@ -3662,6 +3666,13 @@ dhcp_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg) logerr(__func__); return; } + + if (iov->iov_len < offsetof(struct bootp, vend)) { + logerrx("%s: truncated packet (%zu) from %s", + ifp->name, iov->iov_len, inet_ntoa(from->sin_addr)); + return; + } + state = D_CSTATE(ifp); if (state == NULL) { /* Try re-directing it to another interface. */ diff --git a/contrib/dhcpcd/src/dhcp6.c b/contrib/dhcpcd/src/dhcp6.c index bdc3664e26..7cc51f99c7 100644 --- a/contrib/dhcpcd/src/dhcp6.c +++ b/contrib/dhcpcd/src/dhcp6.c @@ -181,6 +181,7 @@ static void dhcp6_bind(struct interface *, const char *, const char *); static void dhcp6_failinform(void *); static void dhcp6_recvaddr(void *, unsigned short); static void dhcp6_startdecline(struct interface *); +static void dhcp6_startrequest(struct interface *); #ifdef SMALL #define dhcp6_hasprefixdelegation(a) (0) @@ -1428,10 +1429,37 @@ dhcp6_sendinform(void *arg) } static void +dhcp6_senddiscover2(void *arg) +{ + + dhcp6_sendmessage(arg, dhcp6_senddiscover2); +} + +static void +dhcp6_senddiscover1(void *arg) +{ + /* + * So the initial RT has elapsed. + * If we have any ADVERTs we can now REQUEST them. + * RFC 8415 15 and 18.2.1 + */ + struct interface *ifp = arg; + struct dhcp6_state *state = D6_STATE(ifp); + + if (state->recv == NULL || state->recv->type != DHCP6_ADVERTISE) + dhcp6_sendmessage(arg, dhcp6_senddiscover2); + else + dhcp6_startrequest(ifp); +} + +static void dhcp6_senddiscover(void *arg) { + struct interface *ifp = arg; + struct dhcp6_state *state = D6_STATE(ifp); - dhcp6_sendmessage(arg, dhcp6_senddiscover); + dhcp6_sendmessage(arg, + state->IMD != 0 ? dhcp6_senddiscover : dhcp6_senddiscover1); } static void @@ -1890,7 +1918,6 @@ dhcp6_startrebind(void *arg) #endif } - static void dhcp6_startrequest(struct interface *ifp) { @@ -3461,6 +3488,16 @@ dhcp6_recvif(struct interface *ifp, const char *sfrom, valid_op = false; break; } + 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 ADVERTISMENT from %s", + ifp->name, sfrom); + return; + } /* RFC7083 */ o = dhcp6_findmoption(r, len, D6_OPTION_SOL_MAX_RT, &ol); if (o && ol == sizeof(uint32_t)) { @@ -3586,7 +3623,7 @@ dhcp6_recvif(struct interface *ifp, const char *sfrom, else loginfox("%s: ADV %s from %s", ifp->name, ia->saddr, sfrom); - dhcp6_startrequest(ifp); + // We will request when the IRT elapses return; } @@ -3791,7 +3828,7 @@ dhcp6_openraw(void) { int fd, v; - fd = socket(PF_INET6, SOCK_RAW | SOCK_CXNB, IPPROTO_UDP); + fd = xsocket(PF_INET6, SOCK_RAW | SOCK_CXNB, IPPROTO_UDP); if (fd == -1) return -1; @@ -3965,20 +4002,16 @@ dhcp6_start(struct interface *ifp, enum DH6S init_state) case DH6S_INIT: goto gogogo; case DH6S_INFORM: + /* RFC 8415 21.23 + * If D6_OPTION_INFO_REFRESH_TIME does not exist + * then we MUST refresh by IRT_DEFAULT seconds + * and should not be influenced by only the + * pl/vl time of the RA changing. */ if (state->state == DH6S_INIT || - state->state == DH6S_INFORMED || (state->state == DH6S_DISCOVER && !(ifp->options->options & DHCPCD_IA_FORCED) && !ipv6nd_hasradhcp(ifp, true))) - { - /* We don't want log spam when the RA - * has just adjusted it's prefix times. */ - if (state->state != DH6S_INFORMED) { - state->new_start = true; - state->failed = false; - } dhcp6_startinform(ifp); - } break; case DH6S_REQUEST: if (ifp->options->options & DHCPCD_DHCP6 && @@ -4254,6 +4287,7 @@ dhcp6_env(FILE *fp, const char *prefix, const struct interface *ifp, #ifndef SMALL const struct dhcp6_state *state; const struct ipv6_addr *ap; + bool first; #endif if (m == NULL) @@ -4355,10 +4389,13 @@ delegated: return 1; if (fprintf(fp, "%s_delegated_dhcp6_prefix=", prefix) == -1) return -1; + first = true; TAILQ_FOREACH(ap, &state->addrs, next) { if (ap->delegating_prefix == NULL) continue; - if (ap != TAILQ_FIRST(&state->addrs)) { + if (first) + first = false; + else { if (fputc(' ', fp) == EOF) return -1; } diff --git a/contrib/dhcpcd/src/dhcpcd-embedded.c b/contrib/dhcpcd/src/dhcpcd-embedded.c index 320db1b831..a8b72b1353 100644 --- a/contrib/dhcpcd/src/dhcpcd-embedded.c +++ b/contrib/dhcpcd/src/dhcpcd-embedded.c @@ -118,7 +118,7 @@ const char dhcpcd_embedded_conf[] = #else "define 1 request ipaddress subnet_mask\n" "define 121 rfc3442 classless_static_routes\n" -"define 2 uint32 time_offset\n" +"define 2 int32 time_offset\n" "define 3 request array ipaddress routers\n" "define 4 array ipaddress time_servers\n" "define 5 array ipaddress ien116_name_servers\n" @@ -299,8 +299,37 @@ const char dhcpcd_embedded_conf[] = "encap 0 string nvt\n" "encap 1 binhex vpn_id\n" "encap 255 flag global\n" +"define 245 ipaddress azureendpoint\n" "define 249 rfc3442 ms_classless_static_routes\n" "define 252 uri wpad_url\n" +"define 224 binhex site_specific_224\n" +"define 225 binhex site_specific_225\n" +"define 226 binhex site_specific_226\n" +"define 227 binhex site_specific_227\n" +"define 228 binhex site_specific_228\n" +"define 229 binhex site_specific_229\n" +"define 230 binhex site_specific_230\n" +"define 231 binhex site_specific_231\n" +"define 232 binhex site_specific_232\n" +"define 233 binhex site_specific_233\n" +"define 234 binhex site_specific_234\n" +"define 235 binhex site_specific_235\n" +"define 236 binhex site_specific_236\n" +"define 237 binhex site_specific_237\n" +"define 238 binhex site_specific_238\n" +"define 239 binhex site_specific_239\n" +"define 240 binhex site_specific_240\n" +"define 241 binhex site_specific_241\n" +"define 242 binhex site_specific_242\n" +"define 243 binhex site_specific_243\n" +"define 244 binhex site_specific_244\n" +"define 246 binhex site_specific_246\n" +"define 247 binhex site_specific_247\n" +"define 248 binhex site_specific_248\n" +"define 250 binhex site_specific_250\n" +"define 251 binhex site_specific_251\n" +"define 253 binhex site_specific_253\n" +"define 254 binhex site_specific_254\n" "definend 1 binhex source_address\n" "definend 2 binhex target_address\n" "definend 3 index embed prefix_information\n" diff --git a/contrib/dhcpcd/src/dhcpcd-embedded.h b/contrib/dhcpcd/src/dhcpcd-embedded.h index dc4df3d365..255c71410d 100644 --- a/contrib/dhcpcd/src/dhcpcd-embedded.h +++ b/contrib/dhcpcd/src/dhcpcd-embedded.h @@ -30,7 +30,7 @@ #define INITDEFINENDS 6 #define INITDEFINE6S 14 #else -#define INITDEFINES 128 +#define INITDEFINES 157 #define INITDEFINENDS 7 #define INITDEFINE6S 74 #endif diff --git a/contrib/dhcpcd/src/dhcpcd.c b/contrib/dhcpcd/src/dhcpcd.c index 3571aae0c5..ccc8fbcf99 100644 --- a/contrib/dhcpcd/src/dhcpcd.c +++ b/contrib/dhcpcd/src/dhcpcd.c @@ -401,31 +401,44 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx) } static void -dhcpcd_drop(struct interface *ifp, int stop) +dhcpcd_drop_af(struct interface *ifp, int stop, int af) { + if (af == AF_UNSPEC || af == AF_INET6) { #ifdef DHCP6 - dhcp6_drop(ifp, stop ? NULL : "EXPIRE6"); + dhcp6_drop(ifp, stop ? NULL : "EXPIRE6"); #endif #ifdef INET6 - ipv6nd_drop(ifp); - ipv6_drop(ifp); + ipv6nd_drop(ifp); + ipv6_drop(ifp); #endif + } + + if (af == AF_UNSPEC || af == AF_INET) { #ifdef IPV4LL - ipv4ll_drop(ifp); + ipv4ll_drop(ifp); #endif #ifdef INET - dhcp_drop(ifp, stop ? "STOP" : "EXPIRE"); + dhcp_drop(ifp, stop ? "STOP" : "EXPIRE"); #endif #ifdef ARP - arp_drop(ifp); + arp_drop(ifp); + } #endif + #if !defined(DHCP6) && !defined(DHCP) UNUSED(stop); #endif } static void +dhcpcd_drop(struct interface *ifp, int stop) +{ + + dhcpcd_drop_af(ifp, stop, AF_UNSPEC); +} + +static void stop_interface(struct interface *ifp, const char *reason) { struct dhcpcd_ctx *ctx; @@ -1512,7 +1525,8 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc, char **argv) { struct interface *ifp; - unsigned long long opts; + struct if_options *ifo; + unsigned long long opts, orig_opts; int opt, oi, oifind, do_reboot, do_renew, af = AF_UNSPEC; size_t len, l, nifaces; char *tmp, *p; @@ -1641,20 +1655,40 @@ dumperr: } if (opts & (DHCPCD_EXITING | DHCPCD_RELEASE)) { - if (oifind == argc) { + if (oifind == argc && af == AF_UNSPEC) { stop_all_interfaces(ctx, opts); eloop_exit(ctx->eloop, EXIT_SUCCESS); return 0; } - for (oi = oifind; oi < argc; oi++) { - if ((ifp = if_find(ctx->ifaces, argv[oi])) == NULL) - continue; + + TAILQ_FOREACH(ifp, ctx->ifaces, next) { if (!ifp->active) continue; - ifp->options->options |= opts; + for (oi = oifind; oi < argc; oi++) { + if (strcmp(ifp->name, argv[oi]) == 0) + break; + } + if (oi == argc) + continue; + + ifo = ifp->options; + orig_opts = ifo->options; + ifo->options |= opts; if (opts & DHCPCD_RELEASE) - ifp->options->options &= ~DHCPCD_PERSISTENT; - stop_interface(ifp, NULL); + ifo->options &= ~DHCPCD_PERSISTENT; + switch (af) { + case AF_INET: + ifo->options &= ~DHCPCD_IPV4; + break; + case AF_INET6: + ifo->options &= ~DHCPCD_IPV6; + break; + } + if (af != AF_UNSPEC) + dhcpcd_drop_af(ifp, 1, af); + else + stop_interface(ifp, NULL); + ifo->options = orig_opts; } return 0; } @@ -1947,6 +1981,7 @@ main(int argc, char **argv, char **envp) } memset(&ctx, 0, sizeof(ctx)); + closefrom(STDERR_FILENO + 1); ifo = NULL; ctx.cffile = CONFIG; @@ -1976,7 +2011,7 @@ main(int argc, char **argv, char **envp) ctx.dhcp6_wfd = -1; #endif #ifdef PRIVSEP - ctx.ps_log_fd = -1; + ctx.ps_log_fd = ctx.ps_log_root_fd = -1; TAILQ_INIT(&ctx.ps_processes); #endif @@ -2173,11 +2208,11 @@ printpidfile: ctx.options |= DHCPCD_MANAGER; /* - * If we are given any interfaces, we + * If we are given any interfaces or a family, we * cannot send a signal as that would impact * other interfaces. */ - if (optind != argc) + if (optind != argc || family != AF_UNSPEC) sig = 0; } if (ctx.options & DHCPCD_PRINT_PIDFILE) { @@ -2427,9 +2462,14 @@ printpidfile: goto run_loop; } +#ifdef DEBUG_FD + loginfox("forkfd %d", ctx.fork_fd); +#endif + /* We have now forked, setsid, forked once more. * From this point on, we are the controlling daemon. */ logdebugx("spawned manager process on PID %d", getpid()); + start_manager: ctx.options |= DHCPCD_STARTED; if ((pid = pidfile_lock(ctx.pidfile)) != 0) { @@ -2647,10 +2687,6 @@ exit1: free(ctx.script_env); rt_dispose(&ctx); free(ctx.duid); - if (ctx.link_fd != -1) { - eloop_event_delete(ctx.eloop, ctx.link_fd); - close(ctx.link_fd); - } if_closesockets(&ctx); free_globals(&ctx); #ifdef INET6 diff --git a/contrib/dhcpcd/src/dhcpcd.conf.5 b/contrib/dhcpcd/src/dhcpcd.conf.5 index e8db233745..7ac3b92cf9 100644 --- a/contrib/dhcpcd/src/dhcpcd.conf.5 +++ b/contrib/dhcpcd/src/dhcpcd.conf.5 @@ -24,7 +24,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd December 18, 2023 +.Dd May 24, 2024 .Dt DHCPCD.CONF 5 .Os .Sh NAME @@ -305,6 +305,10 @@ You can use this option to stop this from happening. .It Ic fallback Ar profile Fall back to using this profile if DHCP fails. This allows you to configure a static profile instead of using ZeroConf. +.It Ic fallback_time Ar seconds +Start fallback after +.Ar seconds . +The default is 5 seconds. .It Ic hostname Ar name Sends the hostname .Ar name @@ -442,6 +446,11 @@ encodes the FQDN hostname as specified in .It Ic interface Ar interface Subsequent options are only parsed for this .Ar interface . +.It Ic ipv4ll_time Ar seconds +Wait for +.Ar seconds +before starting IPv4LL. +The default is 5 seconds. .It Ic ipv6ra_autoconf Generate SLAAC addresses for each Prefix advertised by an IPv6 Router Advertisement message with the Auto flag set. @@ -510,6 +519,8 @@ adding a new IPv4 address. .It Ic noarp Don't send any ARP requests. This also disables IPv4LL. +.It Ic arp_persistdefence +Keep the IP address even if defence fails upon IP Address conflict. .It Ic noauthrequired Don't require authentication even though we requested it. Also allows FORCERENEW and RECONFIGURE messages without authentication. @@ -647,6 +658,11 @@ Use .Ar script instead of the default .Pa /usr/libexec/dhcpcd-run-hooks . +.It Ic request_time Ar seconds +Request the lease for +.Ar seconds +before going back to DISCOVER. +The default is 180 seconds. .It Ic ssid Ar ssid Subsequent options are only parsed for this wireless .Ar ssid . diff --git a/contrib/dhcpcd/src/if-bsd.c b/contrib/dhcpcd/src/if-bsd.c index bab33c2ca6..9ebe36a88f 100644 --- a/contrib/dhcpcd/src/if-bsd.c +++ b/contrib/dhcpcd/src/if-bsd.c @@ -197,6 +197,18 @@ if_opensockets_os(struct dhcpcd_ctx *ctx) &n, sizeof(n)) == -1) logerr("%s: SO_USELOOPBACK", __func__); +#ifdef PRIVSEP + if (ctx->options & DHCPCD_PRIVSEPROOT) { + /* We only want to write to this socket, so set + * a small as possible buffer size. */ + socklen_t smallbuf = 1; + + if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_RCVBUF, + &smallbuf, (socklen_t)sizeof(smallbuf)) == -1) + logerr("%s: setsockopt(SO_RCVBUF)", __func__); + } +#endif + #if defined(RO_MSGFILTER) if (setsockopt(ctx->link_fd, PF_ROUTE, RO_MSGFILTER, &msgfilter, sizeof(msgfilter)) == -1) @@ -220,9 +232,8 @@ if_opensockets_os(struct dhcpcd_ctx *ctx) ps_rights_limit_fd_sockopt(ctx->link_fd); #endif - #if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */ - priv->pf_link_fd = socket(PF_LINK, SOCK_DGRAM, 0); + priv->pf_link_fd = xsocket(PF_LINK, SOCK_DGRAM, 0); if (priv->pf_link_fd == -1) logerr("%s: socket(PF_LINK)", __func__); #endif @@ -235,13 +246,20 @@ if_closesockets_os(struct dhcpcd_ctx *ctx) struct priv *priv; priv = (struct priv *)ctx->priv; + if (priv == NULL) + return; + #ifdef INET6 - if (priv->pf_inet6_fd != -1) + if (priv->pf_inet6_fd != -1) { close(priv->pf_inet6_fd); + priv->pf_inet6_fd = -1; + } #endif #if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */ - if (priv->pf_link_fd != -1) + if (priv->pf_link_fd != -1) { close(priv->pf_link_fd); + priv->pf_link_fd = -1; + } #endif free(priv); ctx->priv = NULL; diff --git a/contrib/dhcpcd/src/if-options.c b/contrib/dhcpcd/src/if-options.c index b28e4ee261..ee8644d20c 100644 --- a/contrib/dhcpcd/src/if-options.c +++ b/contrib/dhcpcd/src/if-options.c @@ -168,6 +168,10 @@ const struct option cf_options[] = { {"link_rcvbuf", required_argument, NULL, O_LINK_RCVBUF}, {"configure", no_argument, NULL, O_CONFIGURE}, {"noconfigure", no_argument, NULL, O_NOCONFIGURE}, + {"arp_persistdefence", no_argument, NULL, O_ARP_PERSISTDEFENCE}, + {"request_time", required_argument, NULL, O_REQUEST_TIME}, + {"fallback_time", required_argument, NULL, O_FALLBACK_TIME}, + {"ipv4ll_time", required_argument, NULL, O_IPV4LL_TIME}, {NULL, 0, NULL, '\0'} }; @@ -2337,6 +2341,38 @@ invalid_token: case O_NOCONFIGURE: ifo->options &= ~DHCPCD_CONFIGURE; break; + case O_ARP_PERSISTDEFENCE: + ifo->options |= DHCPCD_ARP_PERSISTDEFENCE; + break; + case O_REQUEST_TIME: + ARG_REQUIRED; + ifo->request_time = + (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e); + if (e) { + logerrx("invalid request time: %s", arg); + return -1; + } + break; +#ifdef INET + case O_FALLBACK_TIME: + ARG_REQUIRED; + ifo->request_time = + (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e); + if (e) { + logerrx("invalid fallback time: %s", arg); + return -1; + } + break; + case O_IPV4LL_TIME: + ARG_REQUIRED; + ifo->ipv4ll_time = + (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e); + if (e) { + logerrx("invalid ipv4ll time: %s", arg); + return -1; + } + break; +#endif default: return 0; } @@ -2420,6 +2456,11 @@ default_config(struct dhcpcd_ctx *ctx) ifo->options |= DHCPCD_IF_UP | DHCPCD_LINK | DHCPCD_INITIAL_DELAY; ifo->timeout = DEFAULT_TIMEOUT; ifo->reboot = DEFAULT_REBOOT; + ifo->request_time = DEFAULT_REQUEST; +#ifdef INET + ifo->fallback_time = DEFAULT_FALLBACK; + ifo->ipv4ll_time = DEFAULT_IPV4LL; +#endif ifo->metric = -1; ifo->auth.options |= DHCPCD_AUTH_REQUIRE; rb_tree_init(&ifo->routes, &rt_compare_list_ops); @@ -2461,7 +2502,7 @@ read_config(struct dhcpcd_ctx *ctx, default_options |= DHCPCD_CONFIGURE | DHCPCD_DAEMONISE | DHCPCD_GATEWAY; #ifdef INET - skip = socket(PF_INET, SOCK_DGRAM, 0); + skip = xsocket(PF_INET, SOCK_DGRAM, 0); if (skip != -1) { close(skip); default_options |= DHCPCD_IPV4 | DHCPCD_ARP | @@ -2469,7 +2510,7 @@ read_config(struct dhcpcd_ctx *ctx, } #endif #ifdef INET6 - skip = socket(PF_INET6, SOCK_DGRAM, 0); + skip = xsocket(PF_INET6, SOCK_DGRAM, 0); if (skip != -1) { close(skip); default_options |= DHCPCD_IPV6 | DHCPCD_IPV6RS | diff --git a/contrib/dhcpcd/src/if-options.h b/contrib/dhcpcd/src/if-options.h index 53914655df..14c698fe17 100644 --- a/contrib/dhcpcd/src/if-options.h +++ b/contrib/dhcpcd/src/if-options.h @@ -49,6 +49,9 @@ #define DEFAULT_TIMEOUT 30 #define DEFAULT_REBOOT 5 +#define DEFAULT_REQUEST 180 /* secs to request, mirror DHCP6 */ +#define DEFAULT_FALLBACK 5 /* secs until fallback */ +#define DEFAULT_IPV4LL 5 /* secs until ipv4ll */ #ifndef HOSTNAME_MAX_LEN #define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */ @@ -65,6 +68,7 @@ #define DHCPCD_GATEWAY (1ULL << 3) #define DHCPCD_STATIC (1ULL << 4) #define DHCPCD_DEBUG (1ULL << 5) +#define DHCPCD_ARP_PERSISTDEFENCE (1ULL << 6) #define DHCPCD_LASTLEASE (1ULL << 7) #define DHCPCD_INFORM (1ULL << 8) #define DHCPCD_REQUEST (1ULL << 9) @@ -183,6 +187,10 @@ #define O_CONFIGURE O_BASE + 50 #define O_NOCONFIGURE O_BASE + 51 #define O_RANDOMISE_HWADDR O_BASE + 52 +#define O_ARP_PERSISTDEFENCE O_BASE + 53 +#define O_REQUEST_TIME O_BASE + 54 +#define O_FALLBACK_TIME O_BASE + 55 +#define O_IPV4LL_TIME O_BASE + 56 extern const struct option cf_options[]; @@ -234,6 +242,9 @@ struct if_options { uint32_t leasetime; uint32_t timeout; uint32_t reboot; + uint32_t request_time; + uint32_t fallback_time; + uint32_t ipv4ll_time; unsigned long long options; bool randomise_hwaddr; diff --git a/contrib/dhcpcd/src/if.c b/contrib/dhcpcd/src/if.c index 3446a9f786..3ed9f6b38e 100644 --- a/contrib/dhcpcd/src/if.c +++ b/contrib/dhcpcd/src/if.c @@ -132,17 +132,18 @@ void if_closesockets(struct dhcpcd_ctx *ctx) { - if (ctx->pf_inet_fd != -1) - close(ctx->pf_inet_fd); -#ifdef PF_LINK - if (ctx->pf_link_fd != -1) - close(ctx->pf_link_fd); -#endif + if (ctx->link_fd != -1) { + eloop_event_delete(ctx->eloop, ctx->link_fd); + close(ctx->link_fd); + ctx->link_fd = -1; + } - if (ctx->priv) { - if_closesockets_os(ctx); - free(ctx->priv); + if (ctx->pf_inet_fd != -1) { + close(ctx->pf_inet_fd); + ctx->pf_inet_fd = -1; } + + if_closesockets_os(ctx); } int @@ -982,6 +983,10 @@ xsocket(int domain, int type, int protocol) if ((s = socket(domain, type, protocol)) == -1) return -1; +#ifdef DEBUG_FD + logerrx("pid %d fd=%d domain=%d type=%d protocol=%d", + getpid(), s, domain, type, protocol); +#endif #ifndef HAVE_SOCK_CLOEXEC if ((xtype & SOCK_CLOEXEC) && ((xflags = fcntl(s, F_GETFD)) == -1 || @@ -1023,6 +1028,10 @@ xsocketpair(int domain, int type, int protocol, int fd[2]) if ((s = socketpair(domain, type, protocol, fd)) == -1) return -1; +#ifdef DEBUG_FD + logerrx("pid %d fd[0]=%d fd[1]=%d", getpid(), fd[0], fd[1]); +#endif + #ifndef HAVE_SOCK_CLOEXEC if ((xtype & SOCK_CLOEXEC) && ((xflags = fcntl(fd[0], F_GETFD)) == -1 || fcntl(fd[0], F_SETFD, xflags | FD_CLOEXEC) == -1)) diff --git a/contrib/dhcpcd/src/ipv6.c b/contrib/dhcpcd/src/ipv6.c index eb8c617a00..ce985d4ec5 100644 --- a/contrib/dhcpcd/src/ipv6.c +++ b/contrib/dhcpcd/src/ipv6.c @@ -2318,7 +2318,9 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) { struct rt *rt; struct ra *rap; + const struct routeinfo *rinfo; const struct ipv6_addr *addr; + struct in6_addr netmask; if (ctx->ra_routers == NULL) return 0; @@ -2326,6 +2328,27 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) TAILQ_FOREACH(rap, ctx->ra_routers, next) { if (rap->expired) continue; + + /* add rfc4191 route information routes */ + TAILQ_FOREACH (rinfo, &rap->rinfos, next) { + if(rinfo->lifetime == 0) + continue; + if ((rt = inet6_makeroute(rap->iface, rap)) == NULL) + continue; + + in6_addr_fromprefix(&netmask, rinfo->prefix_len); + + sa_in6_init(&rt->rt_dest, &rinfo->prefix); + sa_in6_init(&rt->rt_netmask, &netmask); + sa_in6_init(&rt->rt_gateway, &rap->from); +#ifdef HAVE_ROUTE_PREF + rt->rt_pref = ipv6nd_rtpref(rinfo->flags); +#endif + + rt_proto_add(routes, rt); + } + + /* add subnet routes */ TAILQ_FOREACH(addr, &rap->addrs, next) { if (addr->prefix_vltime == 0) continue; @@ -2333,11 +2356,13 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) if (rt) { rt->rt_dflags |= RTDF_RA; #ifdef HAVE_ROUTE_PREF - rt->rt_pref = ipv6nd_rtpref(rap); + rt->rt_pref = ipv6nd_rtpref(rap->flags); #endif rt_proto_add(routes, rt); } } + + /* add default route */ if (rap->lifetime == 0) continue; if (ipv6_anyglobal(rap->iface) == NULL) @@ -2347,7 +2372,7 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) continue; rt->rt_dflags |= RTDF_RA; #ifdef HAVE_ROUTE_PREF - rt->rt_pref = ipv6nd_rtpref(rap); + rt->rt_pref = ipv6nd_rtpref(rap->flags); #endif rt_proto_add(routes, rt); } diff --git a/contrib/dhcpcd/src/ipv6nd.c b/contrib/dhcpcd/src/ipv6nd.c index 9bf7c5dff4..9264dce730 100644 --- a/contrib/dhcpcd/src/ipv6nd.c +++ b/contrib/dhcpcd/src/ipv6nd.c @@ -71,6 +71,20 @@ #define ND_OPT_PI_FLAG_ROUTER 0x20 /* Router flag in PI */ #endif +#ifndef ND_OPT_RI +#define ND_OPT_RI 24 +struct nd_opt_ri { /* Route Information option RFC4191 */ + uint8_t nd_opt_ri_type; + uint8_t nd_opt_ri_len; + uint8_t nd_opt_ri_prefixlen; + uint8_t nd_opt_ri_flags_reserved; + uint32_t nd_opt_ri_lifetime; + struct in6_addr nd_opt_ri_prefix; +}; +__CTASSERT(sizeof(struct nd_opt_ri) == 24); +#define OPT_RI_FLAG_PREFERENCE(flags) ((flags & 0x18) >> 3) +#endif + #ifndef ND_OPT_RDNSS #define ND_OPT_RDNSS 25 struct nd_opt_rdnss { /* RDNSS option RFC 6106 */ @@ -132,6 +146,8 @@ __CTASSERT(sizeof(struct nd_opt_dnssl) == 8); // static void ipv6nd_handledata(void *, unsigned short); +static struct routeinfo *routeinfo_findalloc(struct ra *, const struct in6_addr *, uint8_t); +static void routeinfohead_free(struct routeinfohead *); /* * Android ships buggy ICMP6 filter headers. @@ -612,10 +628,10 @@ ipv6nd_startexpire(struct interface *ifp) } int -ipv6nd_rtpref(struct ra *rap) +ipv6nd_rtpref(uint8_t flags) { - switch (rap->flags & ND_RA_FLAG_RTPREF_MASK) { + switch (flags & ND_RA_FLAG_RTPREF_MASK) { case ND_RA_FLAG_RTPREF_HIGH: return RTPREF_HIGH; case ND_RA_FLAG_RTPREF_MEDIUM: @@ -624,7 +640,7 @@ ipv6nd_rtpref(struct ra *rap) case ND_RA_FLAG_RTPREF_LOW: return RTPREF_LOW; default: - logerrx("%s: impossible RA flag %x", __func__, rap->flags); + logerrx("%s: impossible RA flag %x", __func__, flags); return RTPREF_INVALID; } /* NOTREACHED */ @@ -649,7 +665,7 @@ ipv6nd_sortrouters(struct dhcpcd_ctx *ctx) continue; if (!ra1->isreachable && ra2->reachable) continue; - if (ipv6nd_rtpref(ra1) <= ipv6nd_rtpref(ra2)) + if (ipv6nd_rtpref(ra1->flags) <= ipv6nd_rtpref(ra2->flags)) continue; /* All things being equal, prefer older routers. */ /* We don't need to check time, becase newer @@ -827,6 +843,7 @@ ipv6nd_removefreedrop_ra(struct ra *rap, int remove_ra, int drop_ra) if (remove_ra) TAILQ_REMOVE(rap->iface->ctx->ra_routers, rap, next); ipv6_freedrop_addrs(&rap->addrs, drop_ra, NULL); + routeinfohead_free(&rap->rinfos); free(rap->data); free(rap); } @@ -1105,6 +1122,8 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct nd_opt_prefix_info pi; struct nd_opt_mtu mtu; struct nd_opt_rdnss rdnss; + struct nd_opt_ri ri; + struct routeinfo *rinfo; uint8_t *p; struct ra *rap; struct in6_addr pi_prefix; @@ -1206,6 +1225,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, rap->from = from->sin6_addr; strlcpy(rap->sfrom, sfrom, sizeof(rap->sfrom)); TAILQ_INIT(&rap->addrs); + TAILQ_INIT(&rap->rinfos); new_rap = true; rap->isreachable = true; } else @@ -1237,9 +1257,6 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, rap->flags = nd_ra->nd_ra_flags_reserved; old_lifetime = rap->lifetime; rap->lifetime = ntohs(nd_ra->nd_ra_router_lifetime); - if (!new_rap && rap->lifetime == 0 && old_lifetime != 0) - logwarnx("%s: %s: no longer a default router (lifetime = 0)", - ifp->name, rap->sfrom); if (nd_ra->nd_ra_curhoplimit != 0) rap->hoplimit = nd_ra->nd_ra_curhoplimit; else @@ -1502,6 +1519,46 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, rdnss.nd_opt_rdnss_len > 1) rap->hasdns = 1; break; + case ND_OPT_RI: + if (ndo.nd_opt_len > 3) { + logmessage(loglevel, "%s: invalid route info option", + ifp->name); + break; + } + memset(&ri, 0, sizeof(ri)); + memcpy(&ri, p, olen); /* may be smaller than sizeof(ri), pad with zero */ + if(ri.nd_opt_ri_prefixlen > 128) { + logmessage(loglevel, "%s: invalid route info prefix length", + ifp->name); + break; + } + + /* rfc4191 3.1 - RI for ::/0 applies to default route */ + if(ri.nd_opt_ri_prefixlen == 0) { + rap->lifetime = ntohl(ri.nd_opt_ri_lifetime); + + /* Update preference leaving other flags intact */ + rap->flags = ((rap->flags & (~ (unsigned int)ND_RA_FLAG_RTPREF_MASK)) + | ri.nd_opt_ri_flags_reserved) & 0xff; + + break; + } + + /* Update existing route info instead of rebuilding all routes so that + previously announced but now absent routes can stay alive. To kill a + route early, an RI with lifetime=0 needs to be received (rfc4191 3.1)*/ + rinfo = routeinfo_findalloc(rap, &ri.nd_opt_ri_prefix, ri.nd_opt_ri_prefixlen); + if(rinfo == NULL) { + logerr(__func__); + break; + } + + /* Update/initialize other route info params */ + rinfo->flags = ri.nd_opt_ri_flags_reserved; + rinfo->lifetime = ntohl(ri.nd_opt_ri_lifetime); + rinfo->acquired = rap->acquired; + + break; default: continue; } @@ -1537,6 +1594,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, ia->prefix_pltime = 0; } + if (!new_rap && rap->lifetime == 0 && old_lifetime != 0) + logwarnx("%s: %s: no longer a default router (lifetime = 0)", + ifp->name, rap->sfrom); + if (new_data && !has_address && rap->lifetime && !ipv6_anyglobal(ifp)) logwarnx("%s: no global addresses for default route", ifp->name); @@ -1699,7 +1760,7 @@ ipv6nd_env(FILE *fp, const struct interface *ifp) return -1; if (efprintf(fp, "%s_hoplimit=%u", ndprefix, rap->hoplimit) == -1) return -1; - pref = ipv6nd_rtpref(rap); + pref = ipv6nd_rtpref(rap->flags); if (efprintf(fp, "%s_flags=%s%s%s%s%s", ndprefix, rap->flags & ND_RA_FLAG_MANAGED ? "M" : "", rap->flags & ND_RA_FLAG_OTHER ? "O" : "", @@ -1804,6 +1865,7 @@ ipv6nd_expirera(void *arg) uint32_t elapsed; bool expired, valid; struct ipv6_addr *ia; + struct routeinfo *rinfo, *rinfob; size_t len, olen; uint8_t *p; struct nd_opt_hdr ndo; @@ -1823,7 +1885,8 @@ ipv6nd_expirera(void *arg) if (rap->iface != ifp || rap->expired) continue; valid = false; - if (rap->lifetime) { + /* 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) { @@ -1879,6 +1942,20 @@ 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) { + logwarnx("%s: expired route %s", + rap->iface->name, rinfo->sprefix); + TAILQ_REMOVE(&rap->rinfos, rinfo, next); + } + } + /* Work out expiry for ND options */ elapsed = (uint32_t)eloop_timespec_diff(&now, &rap->acquired, NULL); @@ -2135,3 +2212,43 @@ ipv6nd_startrs(struct interface *ifp) eloop_timeout_add_msec(ifp->ctx->eloop, delay, ipv6nd_startrs1, ifp); return; } + +static struct routeinfo *routeinfo_findalloc(struct ra *rap, const struct in6_addr *prefix, uint8_t prefix_len) +{ + struct routeinfo *ri; + char buf[INET6_ADDRSTRLEN]; + const char *p; + + TAILQ_FOREACH(ri, &rap->rinfos, next) { + if (ri->prefix_len == prefix_len && + IN6_ARE_ADDR_EQUAL(&ri->prefix, prefix)) + return ri; + } + + ri = malloc(sizeof(struct routeinfo)); + if (ri == NULL) + return NULL; + + memcpy(&ri->prefix, prefix, sizeof(ri->prefix)); + ri->prefix_len = prefix_len; + p = inet_ntop(AF_INET6, prefix, buf, sizeof(buf)); + if (p) + snprintf(ri->sprefix, + sizeof(ri->sprefix), + "%s/%d", + p, prefix_len); + else + ri->sprefix[0] = '\0'; + TAILQ_INSERT_TAIL(&rap->rinfos, ri, next); + return ri; +} + +static void routeinfohead_free(struct routeinfohead *head) +{ + struct routeinfo *ri; + + while ((ri = TAILQ_FIRST(head))) { + TAILQ_REMOVE(head, ri, next); + free(ri); + } +} diff --git a/contrib/dhcpcd/src/ipv6nd.h b/contrib/dhcpcd/src/ipv6nd.h index b702c3bd9f..837b7d0f1e 100644 --- a/contrib/dhcpcd/src/ipv6nd.h +++ b/contrib/dhcpcd/src/ipv6nd.h @@ -37,6 +37,20 @@ #include "dhcpcd.h" #include "ipv6.h" +/* rfc4191 */ +struct routeinfo { + TAILQ_ENTRY(routeinfo) next; + struct in6_addr prefix; + uint8_t prefix_len; + uint32_t lifetime; + uint8_t flags; + struct timespec acquired; + char sprefix[INET6_ADDRSTRLEN]; +}; + +TAILQ_HEAD(routeinfohead, routeinfo); + + struct ra { TAILQ_ENTRY(ra) next; struct interface *iface; @@ -45,13 +59,14 @@ struct ra { uint8_t *data; size_t data_len; struct timespec acquired; - unsigned char flags; + uint8_t flags; uint32_t lifetime; uint32_t reachable; uint32_t retrans; uint32_t mtu; uint8_t hoplimit; struct ipv6_addrhead addrs; + struct routeinfohead rinfos; bool hasdns; bool expired; bool willexpire; @@ -105,7 +120,7 @@ int ipv6nd_open(bool); int ipv6nd_openif(struct interface *); #endif void ipv6nd_recvmsg(struct dhcpcd_ctx *, struct msghdr *); -int ipv6nd_rtpref(struct ra *); +int ipv6nd_rtpref(uint8_t); void ipv6nd_printoptions(const struct dhcpcd_ctx *, const struct dhcp_opt *, size_t); void ipv6nd_startrs(struct interface *); diff --git a/contrib/dhcpcd/src/logerr.c b/contrib/dhcpcd/src/logerr.c index 883b2b19a6..319d6f48e6 100644 --- a/contrib/dhcpcd/src/logerr.c +++ b/contrib/dhcpcd/src/logerr.c @@ -376,6 +376,8 @@ logsetfd(int fd) struct logctx *ctx = &_logctx; ctx->log_fd = fd; + if (fd != -1) + closelog(); #ifndef SMALL if (fd != -1 && ctx->log_file != NULL) { fclose(ctx->log_file); diff --git a/contrib/dhcpcd/src/privsep-bpf.c b/contrib/dhcpcd/src/privsep-bpf.c index 989400902d..742865351a 100644 --- a/contrib/dhcpcd/src/privsep-bpf.c +++ b/contrib/dhcpcd/src/privsep-bpf.c @@ -53,6 +53,8 @@ #include "logerr.h" #include "privsep.h" +/* We expect to have open 3 SEQPACKET and one RAW fd */ + static void ps_bpf_recvbpf(void *arg, unsigned short events) { @@ -160,6 +162,9 @@ ps_bpf_start_bpf(struct ps_process *psp) ps_freeprocesses(ctx, psp); psp->psp_bpf = bpf_open(&psp->psp_ifp, psp->psp_filter, ia); +#ifdef DEBUG_FD + logdebugx("pid %d bpf_fd=%d", getpid(), psp->psp_bpf->bpf_fd); +#endif if (psp->psp_bpf == NULL) logerr("%s: bpf_open",__func__); #ifdef PRIVSEP_RIGHTS diff --git a/contrib/dhcpcd/src/privsep-control.c b/contrib/dhcpcd/src/privsep-control.c index 40bfb16469..5bce41746d 100644 --- a/contrib/dhcpcd/src/privsep-control.c +++ b/contrib/dhcpcd/src/privsep-control.c @@ -36,6 +36,8 @@ #include "logerr.h" #include "privsep.h" +/* We expect to have open 2 SEQPACKET, 2 STREAM and 2 file STREAM fds */ + static int ps_ctl_startcb(struct ps_process *psp) { @@ -217,14 +219,16 @@ ps_ctl_start(struct dhcpcd_ctx *ctx) .psi_cmd = PS_CTL, }; struct ps_process *psp; - int data_fd[2], listen_fd[2]; + int work_fd[2], listen_fd[2]; pid_t pid; - if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, data_fd) == -1 || + if_closesockets(ctx); + + if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, work_fd) == -1 || xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, listen_fd) == -1) return -1; #ifdef PRIVSEP_RIGHTS - if (ps_rights_limit_fdpair(data_fd) == -1 || + if (ps_rights_limit_fdpair(work_fd) == -1 || ps_rights_limit_fdpair(listen_fd) == -1) return -1; #endif @@ -237,24 +241,25 @@ ps_ctl_start(struct dhcpcd_ctx *ctx) if (pid == -1) return -1; else if (pid != 0) { - psp->psp_work_fd = data_fd[1]; - close(data_fd[0]); + psp->psp_work_fd = work_fd[0]; + close(work_fd[1]); + close(listen_fd[1]); ctx->ps_control = control_new(ctx, - listen_fd[1], FD_SENDLEN | FD_LISTEN); + listen_fd[0], FD_SENDLEN | FD_LISTEN); if (ctx->ps_control == NULL) return -1; - close(listen_fd[0]); return pid; } - psp->psp_work_fd = data_fd[0]; - close(data_fd[1]); + close(work_fd[0]); + close(listen_fd[0]); + + psp->psp_work_fd = work_fd[1]; if (eloop_event_add(ctx->eloop, psp->psp_work_fd, ELE_READ, ps_ctl_recv, ctx) == -1) return -1; - ctx->ps_control = control_new(ctx, listen_fd[0], 0); - close(listen_fd[1]); + ctx->ps_control = control_new(ctx, listen_fd[1], 0); if (ctx->ps_control == NULL) return -1; if (eloop_event_add(ctx->eloop, ctx->ps_control->fd, ELE_READ, diff --git a/contrib/dhcpcd/src/privsep-inet.c b/contrib/dhcpcd/src/privsep-inet.c index 5895a5730c..919fe9540b 100644 --- a/contrib/dhcpcd/src/privsep-inet.c +++ b/contrib/dhcpcd/src/privsep-inet.c @@ -47,6 +47,8 @@ #include "logerr.h" #include "privsep.h" +/* We expect to have open 2 SEQPACKET, 1 udp, 1 udp6 and 1 raw6 fds */ + #ifdef INET static void ps_inet_recvbootp(void *arg, unsigned short events) diff --git a/contrib/dhcpcd/src/privsep-root.c b/contrib/dhcpcd/src/privsep-root.c index eb2b3eef68..ed4bb69976 100644 --- a/contrib/dhcpcd/src/privsep-root.c +++ b/contrib/dhcpcd/src/privsep-root.c @@ -259,7 +259,7 @@ ps_root_doioctl(unsigned long req, void *data, size_t len) return -1; } - s = socket(PF_INET, SOCK_DGRAM, 0); + s = xsocket(PF_INET, SOCK_DGRAM, 0); if (s != -1) #ifdef IOCTL_REQUEST_TYPE { @@ -685,17 +685,6 @@ ps_root_startcb(struct ps_process *psp) if (if_opensockets(ctx) == -1) logerr("%s: if_opensockets", __func__); -#ifdef BSD - else { - /* We only want to write to this socket, so set - * a small as possible buffer size. */ - socklen_t smallbuf = 1; - - if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_RCVBUF, - &smallbuf, (socklen_t)sizeof(smallbuf)) == -1) - logerr("%s: setsockopt(SO_RCVBUF)", __func__); - } -#endif /* Open network sockets for sending. * This is a small bit wasteful for non sandboxed OS's @@ -899,7 +888,7 @@ ps_root_start(struct dhcpcd_ctx *ctx) return -1; #endif - if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, datafd) == -1) + if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, datafd) == -1) return -1; if (ps_setbuf_fdpair(datafd) == -1) return -1; @@ -937,6 +926,32 @@ ps_root_start(struct dhcpcd_ctx *ctx) return pid; } +void +ps_root_close(struct dhcpcd_ctx *ctx) +{ + + if_closesockets(ctx); + +#ifdef INET + if (ctx->udp_wfd != -1) { + close(ctx->udp_wfd); + ctx->udp_wfd = -1; + } +#endif +#ifdef INET6 + if (ctx->nd_fd != -1) { + close(ctx->nd_fd); + ctx->nd_fd = -1; + } +#endif +#ifdef DHCP6 + if (ctx->dhcp6_wfd != -1) { + close(ctx->dhcp6_wfd); + ctx->dhcp6_wfd = -1; + } +#endif +} + int ps_root_stop(struct dhcpcd_ctx *ctx) { diff --git a/contrib/dhcpcd/src/privsep-root.h b/contrib/dhcpcd/src/privsep-root.h index ccb2f48582..ce824db976 100644 --- a/contrib/dhcpcd/src/privsep-root.h +++ b/contrib/dhcpcd/src/privsep-root.h @@ -36,6 +36,7 @@ #endif pid_t ps_root_start(struct dhcpcd_ctx *ctx); +void ps_root_close(struct dhcpcd_ctx *ctx); int ps_root_stop(struct dhcpcd_ctx *ctx); void ps_root_signalcb(int, void *); diff --git a/contrib/dhcpcd/src/privsep.c b/contrib/dhcpcd/src/privsep.c index 4cca12ee37..4cccfbdb2b 100644 --- a/contrib/dhcpcd/src/privsep.c +++ b/contrib/dhcpcd/src/privsep.c @@ -408,15 +408,23 @@ ps_startprocess(struct ps_process *psp, return pid; } + /* If we are not the root process, close un-needed stuff. */ + if (ctx->ps_root != psp) { + ps_root_close(ctx); #ifdef PLUGIN_DEV - /* If we are not the root process, stop listening to devices. */ - if (ctx->ps_root != psp) dev_stop(ctx); #endif + } ctx->options |= DHCPCD_FORKED; if (ctx->ps_log_fd != -1) logsetfd(ctx->ps_log_fd); + +#ifdef DEBUG_FD + logerrx("pid %d log_fd=%d data_fd=%d psp_fd=%d", + getpid(), ctx->ps_log_fd, ctx->ps_data_fd, psp->psp_fd); +#endif + eloop_clear(ctx->eloop, -1); eloop_forked(ctx->eloop); eloop_signal_set_cb(ctx->eloop, @@ -459,18 +467,6 @@ ps_startprocess(struct ps_process *psp, #endif } - if (ctx->ps_inet != psp) - ctx->ps_inet = NULL; - if (ctx->ps_ctl != psp) - ctx->ps_ctl = NULL; - -#if 0 - char buf[1024]; - errno = 0; - ssize_t xx = recv(psp->psp_fd, buf, sizeof(buf), MSG_PEEK); - logerr("pid %d test fd %d recv peek %zd", getpid(), psp->psp_fd, xx); -#endif - if (eloop_event_add(ctx->eloop, psp->psp_fd, ELE_READ, recv_msg, psp) == -1) { @@ -1215,6 +1211,7 @@ ps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid) return NULL; psp->psp_ctx = ctx; memcpy(&psp->psp_id, psid, sizeof(psp->psp_id)); + psp->psp_fd = -1; psp->psp_work_fd = -1; #ifdef HAVE_CAPSICUM psp->psp_pfd = -1; diff --git a/contrib/dhcpcd/src/sa.c b/contrib/dhcpcd/src/sa.c index f1e2e16e2e..05009d3bae 100644 --- a/contrib/dhcpcd/src/sa.c +++ b/contrib/dhcpcd/src/sa.c @@ -300,11 +300,39 @@ sa_toprefix(const struct sockaddr *sa) return prefix; } +static void +ipbytes_fromprefix(uint8_t *ap, int prefix, int max_prefix) +{ + int bytes, bits, i; + + bytes = prefix / NBBY; + bits = prefix % NBBY; + + for (i = 0; i < bytes; i++) + *ap++ = 0xff; + if (bits) { + uint8_t a; + + a = 0xff; + a = (uint8_t)(a << (8 - bits)); + *ap++ = a; + } + bytes = (max_prefix - prefix) / NBBY; + for (i = 0; i < bytes; i++) + *ap++ = 0x00; +} + +void +in6_addr_fromprefix(struct in6_addr *addr, int prefix) +{ + ipbytes_fromprefix((uint8_t *)addr, prefix, 128); +} + int sa_fromprefix(struct sockaddr *sa, int prefix) { uint8_t *ap; - int max_prefix, bytes, bits, i; + int max_prefix; switch (sa->sa_family) { #ifdef INET @@ -328,22 +356,8 @@ sa_fromprefix(struct sockaddr *sa, int prefix) return -1; } - bytes = prefix / NBBY; - bits = prefix % NBBY; - ap = (uint8_t *)sa + sa_addroffset(sa); - for (i = 0; i < bytes; i++) - *ap++ = 0xff; - if (bits) { - uint8_t a; - - a = 0xff; - a = (uint8_t)(a << (8 - bits)); - *ap++ = a; - } - bytes = (max_prefix - prefix) / NBBY; - for (i = 0; i < bytes; i++) - *ap++ = 0x00; + ipbytes_fromprefix(ap, prefix, max_prefix); #ifndef NDEBUG /* Ensure the calculation is correct */ diff --git a/contrib/dhcpcd/src/sa.h b/contrib/dhcpcd/src/sa.h index a848defd05..902229afbe 100644 --- a/contrib/dhcpcd/src/sa.h +++ b/contrib/dhcpcd/src/sa.h @@ -67,6 +67,7 @@ bool sa_is_loopback(const struct sockaddr *); void *sa_toaddr(struct sockaddr *); int sa_toprefix(const struct sockaddr *); int sa_fromprefix(struct sockaddr *, int); +void in6_addr_fromprefix(struct in6_addr *, int); const char *sa_addrtop(const struct sockaddr *, char *, socklen_t); int sa_cmp(const struct sockaddr *, const struct sockaddr *); void sa_in_init(struct sockaddr *, const struct in_addr *); -- 2.11.4.GIT