diff --git a/BUILDING.md b/BUILDING.md index f672724a..8b4e1cea 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -148,3 +148,11 @@ copied to `$(libexecdir)/dhcpcd-hooks` for use. The configure program attempts to find hooks for systems you have installed. To add more simply `./configure -with-hook=ntp.conf` + +If using resolvconf, the `20-resolv.conf` hook now requires a version with the +`-C` and `-c` options to deprecate and activate interfaces to support wireless +roaming (Linux) or carrier just drops (NetBSD). +If your resolvconf does not support this then you will see a warning +about an illegal option when the carrier changes, but things should still work. +In this instance the DNS information cannot be Deprecated and may not +be optimal for multi-homed hosts. diff --git a/hooks/20-resolv.conf b/hooks/20-resolv.conf index 73d33386..504a6c53 100644 --- a/hooks/20-resolv.conf +++ b/hooks/20-resolv.conf @@ -7,9 +7,15 @@ # or dnsmasq. This is important as the libc resolver isn't that powerful. resolv_conf_dir="$state_dir/resolv.conf" +nocarrier_roaming_dir="$state_dir/roaming" NL=" " : ${resolvconf:=resolvconf} +if type "$resolvconf" >/dev/null 2>&1; then + have_resolvconf=true +else + have_resolvconf=false +fi build_resolv_conf() { @@ -164,7 +170,7 @@ add_resolv_conf() for x in ${new_domain_name_servers}; do conf="${conf}nameserver $x$NL" done - if type "$resolvconf" >/dev/null 2>&1; then + if $have_resolvconf; then [ -n "$ifmetric" ] && export IF_METRIC="$ifmetric" printf %s "$conf" | "$resolvconf" -a "$ifname" return $? @@ -180,7 +186,7 @@ add_resolv_conf() remove_resolv_conf() { - if type "$resolvconf" >/dev/null 2>&1; then + if $have_resolvconf; then "$resolvconf" -d "$ifname" -f else if [ -e "$resolv_conf_dir/$ifname" ]; then @@ -199,7 +205,18 @@ BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6) esac if $if_configured; then - if $if_up || [ "$reason" = ROUTERADVERT ]; then + if $have_resolvconf && [ "$reason" = NOCARRIER_ROAMING ]; then + # avoid calling resolvconf -c on CARRIER unless we roam + mkdir -p "$nocarrier_roaming_dir" + echo " " >"$nocarrier_roaming_dir/$interface" + "$resolvconf" -C "$interface.*" + elif $have_resolvconf && [ "$reason" = CARRIER ]; then + # Not all resolvconf implementations support -c + if [ -e "$nocarrier_roaming_dir/$interface" ]; then + rm -f "$nocarrier_roaming_dir/$interface" + "$resolvconf" -c "$interface.*" + fi + elif $if_up || [ "$reason" = ROUTERADVERT ]; then add_resolv_conf elif $if_down; then remove_resolv_conf diff --git a/hooks/dhcpcd-run-hooks.8.in b/hooks/dhcpcd-run-hooks.8.in index 49915767..9b5483a4 100644 --- a/hooks/dhcpcd-run-hooks.8.in +++ b/hooks/dhcpcd-run-hooks.8.in @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 24, 2020 +.Dd December 27, 2020 .Dt DHCPCD-RUN-HOOKS 8 .Os .Sh NAME @@ -92,6 +92,9 @@ This is generally just a notification and no action need be taken. .It Dv NOCARRIER dhcpcd lost the carrier. The cable may have been unplugged or association to the wireless point lost. +.It Dv NOCARRIER_ROAMING +dhcpcd lost the carrier but the interface configuration is persisted. +The OS has to support wireless roaming or IP Persistance for this to happen. .It Dv INFORM | Dv INFORM6 dhcpcd informed a DHCP server about its address and obtained other configuration details. @@ -147,10 +150,6 @@ will clear the environment variables aside from The following variables will then be set, along with any protocol supplied ones. .Bl -tag -width xnew_delegated_dhcp6_prefix -.It Ev $chroot -the directory where -.Nm dhcpcd -is chrooted. .It Ev $interface the name of the interface. .It Ev $protocol @@ -193,12 +192,14 @@ if the .Ev interface is up, otherwise .Dv false . +This is more than IFF_UP and may not be equal. .It Ev $if_down .Dv true if the .Ev interface is down, otherwise .Dv false . +This is more than IFF_UP and may not be equal. .It Ev $af_waiting Address family waiting for, as defined in .Xr dhcpcd.conf 5 . diff --git a/src/defs.h b/src/defs.h index 151dc62f..39f14a87 100644 --- a/src/defs.h +++ b/src/defs.h @@ -29,7 +29,7 @@ #define CONFIG_H #define PACKAGE "dhcpcd" -#define VERSION "9.3.3" +#define VERSION "9.4.0" #ifndef PRIVSEP_USER # define PRIVSEP_USER "_" PACKAGE diff --git a/src/dhcp.c b/src/dhcp.c index e2e9accd..e13c63e5 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -2364,13 +2364,21 @@ dhcp_bind(struct interface *ifp) 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; + } + /* Close the BPF filter as we can now receive DHCP messages * on a UDP socket. */ dhcp_closebpf(ifp); - /* Add the address */ - ipv4_applyaddr(ifp); - openudp: /* If not in master mode, open an address specific socket. */ if (ctx->options & DHCPCD_MASTER || @@ -3878,20 +3886,9 @@ dhcp_init(struct interface *ifp) state->clientid = NULL; if (ifo->options & DHCPCD_ANONYMOUS) { - uint8_t duid[DUID_LEN]; - uint8_t duid_len; - - duid_len = (uint8_t)duid_make(duid, ifp, DUID_LL); - if (duid_len != 0) { - state->clientid = malloc((size_t)duid_len + 6); - if (state->clientid == NULL) - goto eexit; - state->clientid[0] =(uint8_t)(duid_len + 5); - state->clientid[1] = 255; /* RFC 4361 */ - memcpy(state->clientid + 2, ifo->iaid, 4); - memset(state->clientid + 2, 0, 4); /* IAID */ - memcpy(state->clientid + 6, duid, duid_len); - } + /* Removing the option could show that we want anonymous. + * As such keep it as it's already in the hwaddr field. */ + goto make_clientid; } else if (*ifo->clientid) { state->clientid = malloc((size_t)(ifo->clientid[0] + 1)); if (state->clientid == NULL) @@ -3909,6 +3906,7 @@ dhcp_init(struct interface *ifp) memcpy(state->clientid + 6, ifp->ctx->duid, ifp->ctx->duid_len); } else { +make_clientid: len = (uint8_t)(ifp->hwlen + 1); state->clientid = malloc((size_t)len + 1); if (state->clientid == NULL) diff --git a/src/dhcp6.c b/src/dhcp6.c index ec6d7738..176a12e4 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -2064,6 +2064,11 @@ dhcp6_checkstatusok(const struct interface *ifp, free(sbuf); state->lerror = code; errno = 0; + + /* code cannot be D6_STATUS_OK, so there is a failure */ + if (ifp->ctx->options & DHCPCD_TEST) + eloop_exit(ifp->ctx->eloop, EXIT_FAILURE); + return (int)code; } diff --git a/src/dhcpcd.8.in b/src/dhcpcd.8.in index bfd03cdc..7533f30b 100644 --- a/src/dhcpcd.8.in +++ b/src/dhcpcd.8.in @@ -24,7 +24,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd November 3, 2020 +.Dd November 25, 2020 .Dt DHCPCD 8 .Os .Sh NAME @@ -264,18 +264,29 @@ Use this .Ar script instead of the default .Pa @SCRIPT@ . -.It Fl D , Fl Fl duid +.It Fl D , Fl Fl duid Op Ar ll | lt | uuid | value Use a DHCP Unique Identifier. If a system UUID is available, that will be used to create a DUID-UUID, otheriwse if persistent storage is available then a DUID-LLT (link local address + time) is generated, otherwise DUID-LL is generated (link local address). +The DUID type can be hinted as an optional parameter if the file +.Pa @DBDIR@/duid +does not exist. +If not +.Va ll , +.Va lt +or +.Va uuid +then +.Va value +will be converted from 00:11:22:33 format. This, plus the IAID will be used as the .Fl I , Fl Fl clientid . The DUID generated will be held in .Pa @DBDIR@/duid and should not be copied to other hosts. -This file also takes precedence over the above rules. +This file also takes precedence over the above rules except for setting a value. .It Fl d , Fl Fl debug Echo debug messages to the stderr and syslog. .It Fl E , Fl Fl lastlease @@ -747,7 +758,7 @@ This is the default behaviour and sets .Ev if_configured=true . .It Fl Fl noconfigure .Nm -will not configure the system add all. +will not configure the system at all. This is only of use if the .Fl Fl script that diff --git a/src/dhcpcd.c b/src/dhcpcd.c index 5892a28b..97417e25 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -695,36 +695,55 @@ dhcpcd_reportssid(struct interface *ifp) loginfox("%s: connected to Access Point: %s", ifp->name, pssid); } +static void +dhcpcd_nocarrier_roaming(struct interface *ifp) +{ + + loginfox("%s: carrier lost - roaming", ifp->name); + +#ifdef ARP + arp_drop(ifp); +#endif +#ifdef INET + dhcp_abort(ifp); +#endif +#ifdef DHCP6 + dhcp6_abort(ifp); +#endif + + rt_build(ifp->ctx, AF_UNSPEC); + script_runreason(ifp, "NOCARRIER_ROAMING"); +} + void dhcpcd_handlecarrier(struct interface *ifp, int carrier, unsigned int flags) { bool was_link_up = if_is_link_up(ifp); + bool was_roaming = if_roaming(ifp); ifp->carrier = carrier; ifp->flags = flags; if (!if_is_link_up(ifp)) { - if (!was_link_up || !ifp->active) + if (!ifp->active || (!was_link_up && !was_roaming)) + return; + + /* + * If the interface is roaming (generally on wireless) + * then while we are not up, we are not down either. + * Preserve the network state until we either disconnect + * or re-connect. + */ + if (!ifp->options->randomise_hwaddr && if_roaming(ifp)) { + dhcpcd_nocarrier_roaming(ifp); return; + } + loginfox("%s: carrier lost", ifp->name); script_runreason(ifp, "NOCARRIER"); -#ifdef NOCARRIER_PRESERVE_IP - if (ifp->flags & IFF_UP && - !(ifp->options->options & DHCPCD_ANONYMOUS)) - { -#ifdef ARP - arp_drop(ifp); -#endif -#ifdef INET - dhcp_abort(ifp); -#endif -#ifdef DHCP6 - dhcp6_abort(ifp); -#endif - } else -#endif - dhcpcd_drop(ifp, 0); - if (ifp->options->options & DHCPCD_ANONYMOUS) { + dhcpcd_drop(ifp, 0); + + if (ifp->options->randomise_hwaddr) { bool is_up = ifp->flags & IFF_UP; if (is_up) @@ -734,6 +753,7 @@ dhcpcd_handlecarrier(struct interface *ifp, int carrier, unsigned int flags) if (is_up) if_up(ifp); } + return; } @@ -774,9 +794,7 @@ dhcpcd_handlecarrier(struct interface *ifp, int carrier, unsigned int flags) memcmp(ifp->ssid, ossid, ifp->ssid_len)) && ifp->active) { dhcpcd_reportssid(ifp); -#ifdef NOCARRIER_PRESERVE_IP dhcpcd_drop(ifp, 0); -#endif #ifdef IPV4LL ipv4ll_reset(ifp); #endif @@ -788,17 +806,17 @@ dhcpcd_handlecarrier(struct interface *ifp, int carrier, unsigned int flags) dhcpcd_initstate(ifp, 0); script_runreason(ifp, "CARRIER"); + #ifdef INET6 -#ifdef NOCARRIER_PRESERVE_IP /* Set any IPv6 Routers we remembered to expire faster than they * would normally as we maybe on a new network. */ ipv6nd_startexpire(ifp); -#endif #ifdef IPV6_MANAGETEMPADDR /* RFC4941 Section 3.5 */ ipv6_regentempaddrs(ifp); #endif #endif + dhcpcd_startinterface(ifp); } @@ -841,13 +859,17 @@ dhcpcd_initduid(struct dhcpcd_ctx *ctx, struct interface *ifp) { char buf[DUID_LEN * 3]; - if (ctx->duid != NULL) + if (ctx->duid != NULL) { + if (ifp == NULL) + goto log; return; + } duid_init(ctx, ifp); if (ctx->duid == NULL) return; +log: loginfox("DUID %s", hwaddr_ntoa(ctx->duid, ctx->duid_len, buf, sizeof(buf))); } @@ -947,22 +969,22 @@ dhcpcd_prestartinterface(void *arg) { struct interface *ifp = arg; struct dhcpcd_ctx *ctx = ifp->ctx; - bool anondown; + bool randmac_down; if (ifp->carrier <= LINK_DOWN && - ifp->options->options & DHCPCD_ANONYMOUS && + ifp->options->randomise_hwaddr && ifp->flags & IFF_UP) { if_down(ifp); - anondown = true; + randmac_down = true; } else - anondown = false; + randmac_down = false; if ((!(ctx->options & DHCPCD_MASTER) || - ifp->options->options & DHCPCD_IF_UP || anondown) && + ifp->options->options & DHCPCD_IF_UP || randmac_down) && !(ifp->flags & IFF_UP)) { - if (ifp->options->options & DHCPCD_ANONYMOUS && + if (ifp->options->randomise_hwaddr && if_randomisemac(ifp) == -1) logerr(__func__); if (if_up(ifp) == -1) @@ -991,17 +1013,20 @@ void dhcpcd_activateinterface(struct interface *ifp, unsigned long long options) { - if (!ifp->active) { - ifp->active = IF_ACTIVE; - dhcpcd_initstate2(ifp, options); - /* It's possible we might not have been able to load - * a config. */ - if (ifp->active) { - configure_interface1(ifp); - run_preinit(ifp); - dhcpcd_prestartinterface(ifp); - } - } + if (ifp->active) + return; + + ifp->active = IF_ACTIVE; + dhcpcd_initstate2(ifp, options); + + /* It's possible we might not have been able to load + * a config. */ + if (!ifp->active) + return; + + configure_interface1(ifp); + run_preinit(ifp); + dhcpcd_prestartinterface(ifp); } int @@ -1154,8 +1179,10 @@ dhcpcd_linkoverflow(struct dhcpcd_ctx *ctx) socklen = sizeof(rcvbuflen); if (getsockopt(ctx->link_fd, SOL_SOCKET, - SO_RCVBUF, &rcvbuflen, &socklen) == -1) + SO_RCVBUF, &rcvbuflen, &socklen) == -1) { + logerr("%s: getsockopt", __func__); rcvbuflen = 0; + } #ifdef __linux__ else rcvbuflen /= 2; @@ -1232,8 +1259,9 @@ dhcpcd_handlehwaddr(struct interface *ifp, } if (ifp->hwtype != hwtype) { - loginfox("%s: hardware address type changed from %d to %d", - ifp->name, ifp->hwtype, hwtype); + if (ifp->active) + loginfox("%s: hardware address type changed" + " from %d to %d", ifp->name, ifp->hwtype, hwtype); ifp->hwtype = hwtype; } @@ -1241,8 +1269,12 @@ dhcpcd_handlehwaddr(struct interface *ifp, (hwlen == 0 || memcmp(ifp->hwaddr, hwaddr, hwlen) == 0)) return; - loginfox("%s: new hardware address: %s", ifp->name, - hwaddr_ntoa(hwaddr, hwlen, buf, sizeof(buf))); + if (ifp->active) { + loginfox("%s: old hardware address: %s", ifp->name, + hwaddr_ntoa(ifp->hwaddr, ifp->hwlen, buf, sizeof(buf))); + loginfox("%s: new hardware address: %s", ifp->name, + hwaddr_ntoa(hwaddr, hwlen, buf, sizeof(buf))); + } ifp->hwlen = hwlen; if (hwaddr != NULL) memcpy(ifp->hwaddr, hwaddr, hwlen); @@ -1880,6 +1912,7 @@ main(int argc, char **argv, char **envp) logopts |= LOGERR_ERR; i = 0; + while ((opt = getopt_long(argc, argv, ctx.options & DHCPCD_PRINT_PIDFILE ? NOERR_IF_OPTS : IF_OPTS, cf_options, &oi)) != -1) @@ -1954,6 +1987,9 @@ main(int argc, char **argv, char **envp) } } + if (optind != argc - 1) + ctx.options |= DHCPCD_MASTER; + logsetopts(logopts); logopen(ctx.logfile); @@ -1970,6 +2006,7 @@ main(int argc, char **argv, char **envp) goto printpidfile; goto exit_failure; } + opt = add_options(&ctx, NULL, ifo, argc, argv); if (opt != 1) { if (ctx.options & DHCPCD_PRINT_PIDFILE) @@ -2010,6 +2047,7 @@ main(int argc, char **argv, char **envp) goto exit_success; } ctx.options |= ifo->options; + if (i == 1 || i == 3) { if (i == 1) ctx.options |= DHCPCD_TEST; @@ -2244,10 +2282,10 @@ printpidfile: if (ctx.stdin_valid && freopen(_PATH_DEVNULL, "w", stdin) == NULL) logwarn("freopen stdin"); +#if defined(USE_SIGNALS) && !defined(THERE_IS_NO_FORK) if (!(ctx.options & DHCPCD_DAEMONISE)) goto start_master; -#if defined(USE_SIGNALS) && !defined(THERE_IS_NO_FORK) if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fork_fd) == -1 || (ctx.stderr_valid && xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, stderr_fd) == -1)) @@ -2527,7 +2565,7 @@ exit_failure: i = EXIT_FAILURE; exit1: - if (control_stop(&ctx) == -1) + if (!(ctx.options & DHCPCD_TEST) && control_stop(&ctx) == -1) logerr("%s: control_stop", __func__); if (ifaddrs != NULL) { #ifdef PRIVSEP_GETIFADDRS diff --git a/src/dhcpcd.conf.5.in b/src/dhcpcd.conf.5.in index 1e7a8631..fb89580d 100644 --- a/src/dhcpcd.conf.5.in +++ b/src/dhcpcd.conf.5.in @@ -24,7 +24,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd October 25, 2020 +.Dd December 27, 2020 .Dt DHCPCD.CONF 5 .Os .Sh NAME @@ -61,9 +61,7 @@ which is a space or comma separated list of patterns passed to .Xr fnmatch 3 . .It Ic anonymous Enables Anonymity Profiles for DHCP, RFC 7844. -This implementation forces a hardware address randomisaton when -the interface link is down and that ClientID's are only LL. -Any DUID is ignored. +Any DUID is ignored and ClientID is set to LL only. All non essential options are then masked at this point, but they could be unmasked by explicitly requesting the option .Sy after @@ -79,6 +77,10 @@ send something which could identify you. .Nm dhcpcd will not try and reboot an old lease, it will go straight into DISCOVER/SOLICIT. +.It Ic randomise_hwaddr +Forces a hardware address randomisation when the interface is brought up +or when the carrier is lost. +This is generally used in tandem with the anonymous option. .It Ic arping Ar address Op address .Nm dhcpcd will arping each address in order before attempting DHCP. @@ -211,7 +213,7 @@ is an empty string then sends a default .Ar clientid of the hardware family and the hardware address. -.It Ic duid Op ll | lt | uuid +.It Ic duid Op ll | lt | uuid | value Use a DHCP Unique Identifier. If a system UUID is available, that will be used to create a DUID-UUID, otheriwse if persistent storage is available then a DUID-LLT @@ -220,12 +222,20 @@ otherwise DUID-LL is generated (link local address). The DUID type can be hinted as an optional parameter if the file .Pa @DBDIR@/duid does not exist. +If not +.Va ll , +.Va lt +or +.Va uuid +then +.Va value +will be converted from 00:11:22:33 format. This, plus the IAID will be used as the .Ic clientid . The DUID generated will be held in .Pa @DBDIR@/duid and should not be copied to other hosts. -This file also takes precedence over the above rules. +This file also takes precedence over the above rules except for setting a value. .It Ic iaid Ar iaid Set the Interface Association Identifier to .Ar iaid . diff --git a/src/if-bsd.c b/src/if-bsd.c index 94f58b31..8fe7d264 100644 --- a/src/if-bsd.c +++ b/src/if-bsd.c @@ -410,6 +410,21 @@ if_carrier(struct interface *ifp, const void *ifadata) return LINK_DOWN; } +bool +if_roaming(struct interface *ifp) +{ + +/* Check for NetBSD as a safety measure. + * If other BSD's gain IN_IFF_TENTATIVE check they re-do DAD + * when the carrier comes up again. */ +#if defined(IN_IFF_TENTATIVE) && defined(__NetBSD__) + return ifp->flags & IFF_UP && ifp->carrier == LINK_DOWN; +#else + UNUSED(ifp); + return false; +#endif +} + static void if_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp) { diff --git a/src/if-linux.c b/src/if-linux.c index eaa5a4d6..da143e7f 100644 --- a/src/if-linux.c +++ b/src/if-linux.c @@ -167,10 +167,19 @@ static int if_addressexists(struct interface *, struct in_addr *); #define SYS_LAYER2 "/sys/class/net/%s/device/layer2" #define SYS_TUNTAP "/sys/class/net/%s/tun_flags" +#if defined(__aarch64__) +static const char *mproc = "AArch64"; +int +if_machinearch(char *str, size_t len) +{ + + return snprintf(str, len, "%s", mproc); +} +#else static const char *mproc = #if defined(__alpha__) "system type" -#elif defined(__arm__) || defined(__aarch64__) +#elif defined(__arm__) "Hardware" #elif defined(__avr32__) "cpu family" @@ -232,6 +241,7 @@ if_machinearch(char *str, size_t len) errno = ESRCH; return -1; } +#endif static int check_proc_int(struct dhcpcd_ctx *ctx, const char *path) @@ -515,6 +525,21 @@ if_carrier(struct interface *ifp, __unused const void *ifadata) return ifp->flags & IFF_RUNNING ? LINK_UP : LINK_DOWN; } +bool +if_roaming(struct interface *ifp) +{ + +#ifdef IFF_LOWER_UP + if (!ifp->wireless || + ifp->flags & IFF_RUNNING || + (ifp->flags & (IFF_UP | IFF_LOWER_UP)) != (IFF_UP | IFF_LOWER_UP)) + return false; + return true; +#else + return false; +#endif +} + int if_getnetlink(struct dhcpcd_ctx *ctx, struct iovec *iov, int fd, int flags, int (*cb)(struct dhcpcd_ctx *, void *, struct nlmsghdr *), void *cbarg) diff --git a/src/if-options.c b/src/if-options.c index 9a261c07..881591a6 100644 --- a/src/if-options.c +++ b/src/if-options.c @@ -120,6 +120,7 @@ const struct option cf_options[] = { {"ipv4only", no_argument, NULL, '4'}, {"ipv6only", no_argument, NULL, '6'}, {"anonymous", no_argument, NULL, O_ANONYMOUS}, + {"randomise_hwaddr",no_argument, NULL, O_RANDOMISE_HWADDR}, {"arping", required_argument, NULL, O_ARPING}, {"destination", required_argument, NULL, O_DESTINATION}, {"fallback", required_argument, NULL, O_FALLBACK}, @@ -134,9 +135,9 @@ const struct option cf_options[] = { {"noipv6", no_argument, NULL, O_NOIPV6}, {"noalias", no_argument, NULL, O_NOALIAS}, {"iaid", required_argument, NULL, O_IAID}, - {"ia_na", no_argument, NULL, O_IA_NA}, - {"ia_ta", no_argument, NULL, O_IA_TA}, - {"ia_pd", no_argument, NULL, O_IA_PD}, + {"ia_na", optional_argument, NULL, O_IA_NA}, + {"ia_ta", optional_argument, NULL, O_IA_TA}, + {"ia_pd", optional_argument, NULL, O_IA_PD}, {"hostname_short", no_argument, NULL, O_HOSTNAME_SHORT}, {"dev", required_argument, NULL, O_DEV}, {"nodev", no_argument, NULL, O_NODEV}, @@ -1002,8 +1003,16 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, else if (strcmp(arg, "uuid") == 0) ctx->duid_type = DUID_UUID; else { - logwarnx("%s: invalid duid type", arg); - ctx->duid_type = DUID_DEFAULT; + dl = hwaddr_aton(NULL, arg); + if (dl != 0) { + no = realloc(ctx->duid, dl); + if (no == NULL) + logerrx(__func__); + else { + ctx->duid = no; + ctx->duid_len = hwaddr_aton(no, arg); + } + } } break; case 'E': @@ -1295,6 +1304,9 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, del_option_mask(ifo->nomask6, D6_OPTION_INF_MAX_RT); #endif + break; + case O_RANDOMISE_HWADDR: + ifo->randomise_hwaddr = true; break; #ifdef INET case O_ARPING: @@ -1344,7 +1356,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, #endif case O_IAID: ARG_REQUIRED; - if (!IN_CONFIG_BLOCK(ifo)) { + if (ctx->options & DHCPCD_MASTER && !IN_CONFIG_BLOCK(ifo)) { logerrx("IAID must belong in an interface block"); return -1; } @@ -1386,7 +1398,9 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, logwarnx("%s: IA_PD not compiled in", ifname); return -1; #else - if (!IN_CONFIG_BLOCK(ifo)) { + if (ctx->options & DHCPCD_MASTER && + !IN_CONFIG_BLOCK(ifo)) + { logerrx("IA PD must belong in an " "interface block"); return -1; @@ -1394,7 +1408,9 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, i = D6_OPTION_IA_PD; #endif } - if (!IN_CONFIG_BLOCK(ifo) && arg) { + if (ctx->options & DHCPCD_MASTER && + !IN_CONFIG_BLOCK(ifo) && arg) + { logerrx("IA with IAID must belong in an " "interface block"); return -1; @@ -2343,6 +2359,8 @@ default_config(struct dhcpcd_ctx *ctx) #endif /* Inherit some global defaults */ + if (ctx->options & DHCPCD_CONFIGURE) + ifo->options |= DHCPCD_CONFIGURE; if (ctx->options & DHCPCD_PERSISTENT) ifo->options |= DHCPCD_PERSISTENT; if (ctx->options & DHCPCD_SLAACPRIVATE) @@ -2371,8 +2389,8 @@ read_config(struct dhcpcd_ctx *ctx, if ((ifo = default_config(ctx)) == NULL) return NULL; if (default_options == 0) { - default_options |= DHCPCD_DAEMONISE | - DHCPCD_CONFIGURE | DHCPCD_GATEWAY; + default_options |= DHCPCD_CONFIGURE | DHCPCD_DAEMONISE | + DHCPCD_GATEWAY; #ifdef INET skip = socket(PF_INET, SOCK_DGRAM, 0); if (skip != -1) { diff --git a/src/if-options.h b/src/if-options.h index 689d9344..e4e39e28 100644 --- a/src/if-options.h +++ b/src/if-options.h @@ -182,6 +182,7 @@ #define O_MSUSERCLASS O_BASE + 49 #define O_CONFIGURE O_BASE + 50 #define O_NOCONFIGURE O_BASE + 51 +#define O_RANDOMISE_HWADDR O_BASE + 52 extern const struct option cf_options[]; @@ -234,6 +235,7 @@ struct if_options { uint32_t timeout; uint32_t reboot; unsigned long long options; + bool randomise_hwaddr; struct in_addr req_addr; struct in_addr req_mask; diff --git a/src/if-sun.c b/src/if-sun.c index 51c0cfd5..019076e7 100644 --- a/src/if-sun.c +++ b/src/if-sun.c @@ -245,6 +245,13 @@ err: return LINK_UNKNOWN; } +bool +if_roaming(__unused struct interface *ifp) +{ + + return false; +} + int if_mtu_os(const struct interface *ifp) { diff --git a/src/if.c b/src/if.c index deb5280b..d3852f3d 100644 --- a/src/if.c +++ b/src/if.c @@ -697,12 +697,11 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs, ifp->metric = (unsigned int)ifr.ifr_metric; if_getssid(ifp); #else - /* We reserve the 100 range for virtual interfaces, if and when - * we can work them out. */ - ifp->metric = 200 + ifp->index; + /* Leave a low portion for user config */ + ifp->metric = RTMETRIC_BASE + ifp->index; if (if_getssid(ifp) != -1) { ifp->wireless = true; - ifp->metric += 100; + ifp->metric += RTMETRIC_WIRELESS; } #endif diff --git a/src/if.h b/src/if.h index 8474deb7..43d13307 100644 --- a/src/if.h +++ b/src/if.h @@ -42,14 +42,6 @@ * dhcpcd can poll it for the relevant flags periodically */ #define IF_POLL_UP 100 /* milliseconds */ -/* Some systems have in-built IPv4 DAD. - * However, we need them to do DAD at carrier up as well. */ -#ifdef IN_IFF_TENTATIVE -# ifdef __NetBSD__ -# define NOCARRIER_PRESERVE_IP -# endif -#endif - /* * Systems which handle 1 address per alias. * Currenly this is just Solaris. @@ -161,6 +153,7 @@ int if_domtu(const struct interface *, short int); #define if_getmtu(ifp) if_domtu((ifp), 0) #define if_setmtu(ifp, mtu) if_domtu((ifp), (mtu)) int if_carrier(struct interface *, const void *); +bool if_roaming(struct interface *); #ifdef ALIAS_ADDR int if_makealias(char *, size_t, const char *, int); diff --git a/src/ipv4.c b/src/ipv4.c index 92ecdbd0..220b3330 100644 --- a/src/ipv4.c +++ b/src/ipv4.c @@ -728,7 +728,7 @@ ipv4_daddaddr(struct interface *ifp, const struct dhcp_lease *lease) return 0; } -void +struct ipv4_addr * ipv4_applyaddr(void *arg) { struct interface *ifp = arg; @@ -738,7 +738,7 @@ ipv4_applyaddr(void *arg) struct ipv4_addr *ia; if (state == NULL) - return; + return NULL; lease = &state->lease; if (state->new == NULL) { @@ -757,7 +757,7 @@ ipv4_applyaddr(void *arg) script_runreason(ifp, state->reason); } else rt_build(ifp->ctx, AF_INET); - return; + return NULL; } ia = ipv4_iffindaddr(ifp, &lease->addr, NULL); @@ -783,22 +783,22 @@ ipv4_applyaddr(void *arg) #endif #ifndef IP_LIFETIME if (ipv4_daddaddr(ifp, lease) == -1 && errno != EEXIST) - return; + return NULL; #endif } #ifdef IP_LIFETIME if (ipv4_daddaddr(ifp, lease) == -1 && errno != EEXIST) - return; + return NULL; #endif ia = ipv4_iffindaddr(ifp, &lease->addr, NULL); if (ia == NULL) { logerrx("%s: added address vanished", ifp->name); - return; + return NULL; } #if defined(ARP) && defined(IN_IFF_NOTUSEABLE) if (ia->addr_flags & IN_IFF_NOTUSEABLE) - return; + return NULL; #endif /* Delete the old address if different */ @@ -820,6 +820,7 @@ ipv4_applyaddr(void *arg) script_runreason(ifp, state->reason); dhcpcd_daemonise(ifp->ctx); } + return ia; } void diff --git a/src/ipv4.h b/src/ipv4.h index bb3d5b2c..c72418e3 100644 --- a/src/ipv4.h +++ b/src/ipv4.h @@ -135,7 +135,7 @@ int ipv4_deladdr(struct ipv4_addr *, int); struct ipv4_addr *ipv4_addaddr(struct interface *, const struct in_addr *, const struct in_addr *, const struct in_addr *, uint32_t, uint32_t); -void ipv4_applyaddr(void *); +struct ipv4_addr *ipv4_applyaddr(void *); struct ipv4_addr *ipv4_iffindaddr(struct interface *, const struct in_addr *, const struct in_addr *); diff --git a/src/ipv4ll.c b/src/ipv4ll.c index 93898109..6f9cd73d 100644 --- a/src/ipv4ll.c +++ b/src/ipv4ll.c @@ -137,7 +137,7 @@ ipv4ll_defaultroute(rb_tree_t *routes, struct interface *ifp) sa_in_init(&rt->rt_ifa, &state->addr->addr); rt->rt_dflags |= RTDF_IPV4LL; #ifdef HAVE_ROUTE_METRIC - rt->rt_metric += 10000; + rt->rt_metric += RTMETRIC_IPV4LL; #endif return rt_proto_add(routes, rt) ? 1 : 0; } diff --git a/src/ipv6nd.c b/src/ipv6nd.c index f0a79d51..b9ba57f4 100644 --- a/src/ipv6nd.c +++ b/src/ipv6nd.c @@ -1155,7 +1155,6 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, return; } -#ifdef NOCARRIER_PRESERVE_IP /* * Because we preserve RA's and expire them quickly after * carrier up, it's important to reset the kernels notion of @@ -1168,7 +1167,6 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, } if (rap != NULL && rap->willexpire) ipv6nd_applyra(ifp); -#endif TAILQ_FOREACH(rap, ctx->ra_routers, next) { if (ifp == rap->iface && diff --git a/src/privsep-bpf.c b/src/privsep-bpf.c index 23da9a07..79c4696d 100644 --- a/src/privsep-bpf.c +++ b/src/privsep-bpf.c @@ -73,7 +73,8 @@ ps_bpf_recvbpf(void *arg) if (len == -1) { int error = errno; - logerr("%s: %s", psp->psp_ifname, __func__); + if (errno != ENETDOWN) + logerr("%s: %s", psp->psp_ifname, __func__); if (error != ENXIO) break; /* If the interface has departed, close the BPF diff --git a/src/privsep-linux.c b/src/privsep-linux.c index e588ecd7..d31d720d 100644 --- a/src/privsep-linux.c +++ b/src/privsep-linux.c @@ -28,10 +28,13 @@ #include #include +#include #include +#include /* For TCGETS */ #include #include +#include #include #include @@ -251,6 +254,11 @@ static struct sock_filter ps_seccomp_filter[] = { #ifdef __NR_getpid SECCOMP_ALLOW(__NR_getpid), #endif +#ifdef __NR_getsockopt + /* For route socket overflow */ + SECCOMP_ALLOW_ARG(__NR_getsockopt, 1, SOL_SOCKET), + SECCOMP_ALLOW_ARG(__NR_getsockopt, 2, SO_RCVBUF), +#endif #ifdef __NR_ioctl SECCOMP_ALLOW_ARG(__NR_ioctl, 1, SIOCGIFFLAGS), SECCOMP_ALLOW_ARG(__NR_ioctl, 1, SIOCGIFHWADDR), @@ -304,6 +312,23 @@ static struct sock_filter ps_seccomp_filter[] = { #ifdef __NR_sendto SECCOMP_ALLOW(__NR_sendto), #endif +#ifdef __NR_socketcall + /* i386 needs this and demonstrates why SECCOMP + * is poor compared to OpenBSD pledge(2) and FreeBSD capsicum(4) + * as this is soooo tied to the kernel API which changes per arch + * and likely libc as well. */ + SECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_ACCEPT), + SECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_ACCEPT4), + SECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_LISTEN), + SECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_GETSOCKOPT), /* overflow */ + SECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_RECV), + SECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_RECVFROM), + SECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_RECVMSG), + SECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_SEND), + SECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_SENDMSG), + SECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_SENDTO), + SECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_SHUTDOWN), +#endif #ifdef __NR_shutdown SECCOMP_ALLOW(__NR_shutdown), #endif diff --git a/src/privsep.c b/src/privsep.c index 55bb3c42..d5c7c562 100644 --- a/src/privsep.c +++ b/src/privsep.c @@ -581,7 +581,9 @@ ps_mastersandbox(struct dhcpcd_ctx *ctx, const char *_pledge) } logerr("%s: %s", __func__, sandbox); return -1; - } else if (ctx->options & DHCPCD_LAUNCHER) + } else if (ctx->options & DHCPCD_LAUNCHER || + ((!(ctx->options & DHCPCD_DAEMONISE)) && + ctx->options & DHCPCD_MASTER)) logdebugx("sandbox: %s", sandbox); return 0; } diff --git a/src/route.c b/src/route.c index 80c23850..89d0c132 100644 --- a/src/route.c +++ b/src/route.c @@ -168,6 +168,15 @@ rt_compare_proto(void *context, const void *node1, const void *node2) if (c != 0) return -c; + /* Prefer roaming over non roaming if both carriers are down. */ + if (ifp1->carrier == LINK_DOWN && ifp2->carrier == LINK_DOWN) { + bool roam1 = if_roaming(ifp1); + bool roam2 = if_roaming(ifp2); + + if (roam1 != roam2) + return roam1 ? 1 : -1; + } + #ifdef INET /* IPv4LL routes always come last */ if (rt1->rt_dflags & RTDF_IPV4LL && !(rt2->rt_dflags & RTDF_IPV4LL)) @@ -374,6 +383,8 @@ rt_setif(struct rt *rt, struct interface *ifp) rt->rt_ifp = ifp; #ifdef HAVE_ROUTE_METRIC rt->rt_metric = ifp->metric; + if (if_roaming(ifp)) + rt->rt_metric += RTMETRIC_ROAM; #endif } @@ -398,6 +409,7 @@ rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx) return rt; rt_free(rt); + errno = EEXIST; return NULL; } @@ -713,7 +725,10 @@ rt_build(struct dhcpcd_ctx *ctx, int af) #endif RB_TREE_FOREACH_SAFE(rt, &routes, rtn) { - if (!(rt->rt_ifp->options->options & DHCPCD_CONFIGURE)) + if (rt->rt_ifp->active) { + if (!(rt->rt_ifp->options->options & DHCPCD_CONFIGURE)) + continue; + } else if (!(ctx->options & DHCPCD_CONFIGURE)) continue; #ifdef BSD if (rt_is_default(rt) && diff --git a/src/route.h b/src/route.h index e9ac121a..cb935c83 100644 --- a/src/route.h +++ b/src/route.h @@ -93,6 +93,15 @@ struct rt { #ifdef HAVE_ROUTE_METRIC unsigned int rt_metric; #endif +/* Maximum interface index is generally USHORT_MAX or 65535. + * Add some padding for other stuff and we get offsets for the + * below that should work automatically. + * This is only an issue if the user defines higher metrics in + * their configuration, but then they might wish to override also. */ +#define RTMETRIC_BASE 1000U +#define RTMETRIC_WIRELESS 2000U +#define RTMETRIC_IPV4LL 1000000U +#define RTMETRIC_ROAM 2000000U #ifdef HAVE_ROUTE_PREF int rt_pref; #endif diff --git a/src/script.c b/src/script.c index 0260845e..622bab87 100644 --- a/src/script.c +++ b/src/script.c @@ -74,6 +74,9 @@ static const char * const if_params[] = { NULL }; +static const char * true_str = "true"; +static const char * false_str = "false"; + void if_printoptions(void) { @@ -228,6 +231,10 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp, const struct if_options *ifo; const struct interface *ifp2; int af; + bool is_stdin = ifp->name[0] == '\0'; + const char *if_up, *if_down; + rb_tree_t ifaces; + struct rt *rt; #ifdef INET const struct dhcp_state *state; #ifdef IPV4LL @@ -237,7 +244,6 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp, #ifdef DHCP6 const struct dhcp6_state *d6_state; #endif - bool is_stdin = ifp->name[0] == '\0'; #ifdef HAVE_OPEN_MEMSTREAM if (ctx->script_fp == NULL) { @@ -276,6 +282,7 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp, if (efprintf(fp, "pid=%d", getpid()) == -1) goto eexit; } + if (!is_stdin) { if (efprintf(fp, "reason=%s", reason) == -1) goto eexit; @@ -326,6 +333,7 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp, else if (strcmp(reason, "PREINIT") == 0 || strcmp(reason, "CARRIER") == 0 || strcmp(reason, "NOCARRIER") == 0 || + strcmp(reason, "NOCARRIER_ROAMING") == 0 || strcmp(reason, "UNKNOWN") == 0 || strcmp(reason, "DEPARTED") == 0 || strcmp(reason, "STOPPED") == 0) @@ -382,34 +390,46 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp, if (ifp->ctx->options & DHCPCD_DUMPLEASE) goto dumplease; + ifp->ctx->rt_order = 0; + rb_tree_init(&ifaces, &rt_compare_proto_ops); + TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { + if (!ifp2->active) + continue; + rt = rt_new(UNCONST(ifp2)); + if (rt == NULL) + goto eexit; + if (rt_proto_add(&ifaces, rt) != rt) + goto eexit; + } if (fprintf(fp, "interface_order=") == -1) goto eexit; - TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { - if (ifp2 != TAILQ_FIRST(ifp->ctx->ifaces)) { - if (fputc(' ', fp) == EOF) - return -1; - } - if (fprintf(fp, "%s", ifp2->name) == -1) - return -1; + RB_TREE_FOREACH(rt, &ifaces) { + if (rt != RB_TREE_MIN(&ifaces) && + fprintf(fp, "%s", " ") == -1) + goto eexit; + if (fprintf(fp, "%s", rt->rt_ifp->name) == -1) + goto eexit; } + rt_headclear(&ifaces, AF_UNSPEC); if (fputc('\0', fp) == EOF) - return -1; + goto eexit; if (strcmp(reason, "STOPPED") == 0) { - if (efprintf(fp, "if_up=false") == -1) - goto eexit; - if (efprintf(fp, "if_down=%s", - ifo->options & DHCPCD_RELEASE ? "true" : "false") == -1) - goto eexit; + if_up = false_str; + if_down = ifo->options & DHCPCD_RELEASE ? true_str : false_str; } else if (strcmp(reason, "TEST") == 0 || strcmp(reason, "PREINIT") == 0 || strcmp(reason, "CARRIER") == 0 || strcmp(reason, "UNKNOWN") == 0) { - if (efprintf(fp, "if_up=false") == -1) - goto eexit; - if (efprintf(fp, "if_down=false") == -1) - goto eexit; + if_up = false_str; + if_down = false_str; + } else if (strcmp(reason, "NOCARRIER") == 0) { + if_up = false_str; + if_down = true_str; + } else if (strcmp(reason, "NOCARRIER_ROAMING") == 0) { + if_up = true_str; + if_down = false_str; } else if (1 == 2 /* appease ifdefs */ #ifdef INET || (protocol == PROTO_DHCP && state && state->new) @@ -426,16 +446,17 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp, #endif ) { - if (efprintf(fp, "if_up=true") == -1) - goto eexit; - if (efprintf(fp, "if_down=false") == -1) - goto eexit; + if_up = true_str; + if_down = false_str; } else { - if (efprintf(fp, "if_up=false") == -1) - goto eexit; - if (efprintf(fp, "if_down=true") == -1) - goto eexit; + if_up = false_str; + if_down = true_str; } + if (efprintf(fp, "if_up=%s", if_up) == -1) + goto eexit; + if (efprintf(fp, "if_down=%s", if_down) == -1) + goto eexit; + if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) { if (efprintf(fp, "if_afwaiting=%d", af) == -1) goto eexit;