diff --git a/src/common.c b/src/common.c index 03bb33c1..0b56e9f4 100644 --- a/src/common.c +++ b/src/common.c @@ -217,13 +217,21 @@ is_root_local(void) } uint32_t -lifetime_left(uint32_t lifetime, const struct timespec *acquired, const struct timespec *now) +lifetime_left(uint32_t lifetime, const struct timespec *acquired, struct timespec *now) { uint32_t elapsed; + struct timespec n; if (lifetime == INFINITE_LIFETIME) return lifetime; + if (now == NULL) { + timespecclear(&n); + now = &n; + } + if (!timespecisset(now)) + clock_gettime(CLOCK_MONOTONIC, now); + elapsed = (uint32_t)eloop_timespec_diff(now, acquired, NULL); if (elapsed > lifetime) return 0; diff --git a/src/common.h b/src/common.h index 096f6bd4..a2ab05ea 100644 --- a/src/common.h +++ b/src/common.h @@ -150,5 +150,5 @@ ssize_t writefile(const char *, mode_t, const void *, size_t); int filemtime(const char *, time_t *); char *get_line(char ** __restrict, ssize_t * __restrict); int is_root_local(void); -uint32_t lifetime_left(uint32_t, const struct timespec *, const struct timespec *); +uint32_t lifetime_left(uint32_t, const struct timespec *, struct timespec *); #endif diff --git a/src/dhcp.c b/src/dhcp.c index 9e23ab62..bc87c760 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -809,7 +809,6 @@ make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type) 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; @@ -1138,35 +1137,35 @@ make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type) 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); } } - -#ifndef SMALL + if (ifo->vsio_len && !has_option_mask(ifo->nomask, DHO_VIVSO)) { diff --git a/src/dhcp6.c b/src/dhcp6.c index ca076e4e..ea0bff53 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -276,29 +276,28 @@ dhcp6_makeuser(void *data, const struct interface *ifp) 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; @@ -312,19 +311,19 @@ dhcp6_makevendor(void *data, const struct interface *ifp) 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); @@ -332,17 +331,22 @@ dhcp6_makevendor(void *data, const struct interface *ifp) 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 sizeof(o) + len; + return len; } -#ifndef SMALL /* DHCPv6 Option 17 (Vendor-Specific Information Option) */ static size_t dhcp6_makevendoropts(void *data, const struct interface *ifp) @@ -875,10 +879,10 @@ dhcp6_makemessage(struct interface *ifp) if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS)) len += dhcp6_makeuser(NULL, ifp); - if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS)) - len += dhcp6_makevendor(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 @@ -1199,10 +1203,10 @@ dhcp6_makemessage(struct interface *ifp) if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS)) p += dhcp6_makeuser(p, ifp); - if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS)) - p += dhcp6_makevendor(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 diff --git a/src/eloop.c b/src/eloop.c index 523455d7..d602fdcf 100644 --- a/src/eloop.c +++ b/src/eloop.c @@ -464,7 +464,7 @@ eloop_timespec_diff(const struct timespec *tsp, const struct timespec *usp, unsigned long long tsecs, usecs, secs; long nsecs; - if (tsp->tv_sec < 0) /* time wreapped */ + if (tsp->tv_sec < 0) /* time wrapped */ tsecs = UTIME_MAX - (unsigned long long)(-tsp->tv_sec); else tsecs = (unsigned long long)tsp->tv_sec; diff --git a/src/if-bsd.c b/src/if-bsd.c index 68303e82..5b4c43a0 100644 --- a/src/if-bsd.c +++ b/src/if-bsd.c @@ -742,15 +742,12 @@ if_route(unsigned char cmd, const struct rt *rt) { 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 diff --git a/src/if-options.c b/src/if-options.c index c50f65a1..54ae590b 100644 --- a/src/if-options.c +++ b/src/if-options.c @@ -656,6 +656,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, 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; @@ -2111,6 +2112,10 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, 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'; @@ -2119,6 +2124,12 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, 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); @@ -2149,11 +2160,12 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, 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 @@ -2994,12 +3006,12 @@ free_options(struct dhcpcd_ctx *ctx, struct if_options *ifo) 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); -#ifndef SMALL for (vsio = ifo->vsio; ifo->vsio_len > 0; vsio++, ifo->vsio_len--) diff --git a/src/if-options.h b/src/if-options.h index f3b9c17a..c892e75d 100644 --- a/src/if-options.h +++ b/src/if-options.h @@ -61,7 +61,6 @@ #define USERCLASS_MAX_LEN 255 #define VENDOR_MAX_LEN 255 #define MUDURL_MAX_LEN 255 -#define ENTERPRISE_NUMS_MAX_LEN 255 #define DHCPCD_ARP (1ULL << 0) #define DHCPCD_RELEASE (1ULL << 1) @@ -220,12 +219,12 @@ struct if_ia { #endif }; +#ifndef SMALL struct vivco { + uint32_t en; size_t len; uint8_t *data; }; - -#ifndef SMALL struct vsio_so { uint16_t opt; uint16_t len; @@ -303,13 +302,12 @@ struct if_options { size_t nd_override_len; struct dhcp_opt *dhcp6_override; size_t dhcp6_override_len; - uint32_t vivco_en; - struct vivco *vivco; - size_t vivco_len; struct dhcp_opt *vivso_override; size_t vivso_override_len; #ifndef SMALL + size_t vivco_len; + struct vivco *vivco; size_t vsio_len; struct vsio *vsio; size_t vsio6_len; diff --git a/src/ipv6.c b/src/ipv6.c index 156b24c6..a0be7c04 100644 --- a/src/ipv6.c +++ b/src/ipv6.c @@ -674,7 +674,7 @@ ipv6_getstate(struct interface *ifp) } 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; @@ -711,31 +711,11 @@ ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now) 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; @@ -880,7 +860,7 @@ ipv6_aliasaddr(struct ipv6_addr *ia, struct ipv6_addr **repl) #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 @@ -975,8 +955,6 @@ ipv6_doaddr(struct ipv6_addr *ia, struct timespec *now) 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; } @@ -1070,12 +1048,7 @@ ipv6_freedrop_addrs(struct ipv6_addrhead *addrs, int drop, 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); } @@ -2073,7 +2046,7 @@ ipv6_settemptime(struct ipv6_addr *ia, int flags) } 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; @@ -2306,7 +2279,7 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) if (ctx->ra_routers == NULL) return 0; - clock_gettime(CLOCK_MONOTONIC, &now); + timespecclear(&now); TAILQ_FOREACH(rap, ctx->ra_routers, next) { if (rap->expired) @@ -2328,7 +2301,8 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) #ifdef HAVE_ROUTE_PREF rt->rt_pref = ipv6nd_rtpref(rinfo->flags); #endif - rt->rt_expires = lifetime_left(rinfo->lifetime, &rinfo->acquired, &now); + rt->rt_expires = lifetime_left(rinfo->lifetime, + &rinfo->acquired, &now); rt->rt_updated = rinfo->acquired; rt_proto_add(routes, rt); @@ -2344,7 +2318,9 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) #ifdef HAVE_ROUTE_PREF rt->rt_pref = ipv6nd_rtpref(rap->flags); #endif - rt->rt_expires = lifetime_left(addr->prefix_vltime, &addr->acquired, &now); + rt->rt_expires = + lifetime_left(addr->prefix_vltime, + &addr->acquired, &now); rt->rt_updated = addr->acquired; rt_proto_add(routes, rt); @@ -2378,7 +2354,8 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) #ifdef HAVE_ROUTE_PREF rt->rt_pref = ipv6nd_rtpref(rap->flags); #endif - rt->rt_expires = lifetime_left(rap->lifetime, &rap->acquired, &now); + rt->rt_expires = lifetime_left(rap->lifetime, + &rap->acquired, &now); rt->rt_updated = rap->acquired; rt_proto_add(routes, rt); diff --git a/src/ipv6.h b/src/ipv6.h index 6351cd4c..2304d95c 100644 --- a/src/ipv6.h +++ b/src/ipv6.h @@ -254,7 +254,7 @@ int ipv6_userprefix( const struct in6_addr *, short prefix_len, 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 *); @@ -289,7 +289,7 @@ void ipv6_freedrop(struct interface *, int); 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 diff --git a/src/ipv6nd.c b/src/ipv6nd.c index bfaf9274..b1bdfecb 100644 --- a/src/ipv6nd.c +++ b/src/ipv6nd.c @@ -1702,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; @@ -1714,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) { @@ -1726,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); @@ -1738,7 +1737,6 @@ ipv6nd_expirera(void *arg) } } else { valid = true; - ltime = rap->lifetime - elapsed; if (next == 0 || ltime < next) next = ltime; } @@ -1750,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, @@ -1776,7 +1768,6 @@ ipv6nd_expirera(void *arg) expired = true; } else { valid = true; - ltime = ia->prefix_vltime - elapsed; if (next == 0 || ltime < next) next = ltime; } @@ -1784,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);