From: Roy Marples Date: Sat, 20 Jun 2026 20:31:15 +0000 (+0100) Subject: ND: Enforce require and reject policy X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=805fe4ac07751a76978cf567548545ee35964bdc;p=thirdparty%2Fdhcpcd.git ND: Enforce require and reject policy Reported by NVIDIA Project Vanessa --- diff --git a/src/ipv6nd.c b/src/ipv6nd.c index c219bdb9..f2c8c207 100644 --- a/src/ipv6nd.c +++ b/src/ipv6nd.c @@ -949,7 +949,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, const struct sockaddr_in6 *from, const char *sfrom, struct interface *ifp, struct icmp6_hdr *icp, size_t len, int hoplimit) { - size_t i, olen; + size_t i, olen, rlen; struct nd_router_advert *nd_ra; struct nd_opt_hdr ndo; struct nd_opt_prefix_info pi; @@ -957,12 +957,13 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, const struct sockaddr_in6 *from, struct nd_opt_rdnss rdnss; struct nd_opt_ri ri; struct routeinfo *rinfo; + struct if_options *ifo; uint8_t *p; struct ra *rap; struct in6_addr pi_prefix; struct ipv6_addr *ia; struct dhcp_opt *dho; - bool new_rap, new_data, has_address; + bool new_rap, new_data, has_address, has_opt; uint32_t old_lifetime; int ifmtu; int loglevel; @@ -1041,6 +1042,80 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, const struct sockaddr_in6 *from, nd_ra = (struct nd_router_advert *)icp; + /* Validate */ + loglevel = rap == NULL || rap->willexpire || !rap->isreachable ? + LOG_ERR : + LOG_DEBUG; + ifo = ifp->options; + rlen = len; + len -= sizeof(struct nd_router_advert); + p = ((uint8_t *)icp) + sizeof(struct nd_router_advert); + for (; len > 0; p += olen, len -= olen) { + if (len < sizeof(ndo)) { + logmessage(loglevel, "%s: short RA option from %s", + ifp->name, sfrom); + break; + } + memcpy(&ndo, p, sizeof(ndo)); + olen = (size_t)ndo.nd_opt_len * 8; + if (olen == 0) { + /* RFC4681 4.6 says we MUST discard this ND packet. */ + logmessage(loglevel, "%s: zero length RA option %s", + ifp->name, sfrom); + return; + } + if (olen > len) { + logmessage(loglevel, + "%s: RA option length exceeds message from %s", + ifp->name, sfrom); + break; + } + + if (has_option_mask(ifo->rejectmasknd, ndo.nd_opt_type)) { + for (i = 0, dho = ctx->nd_opts; i < ctx->nd_opts_len; + i++, dho++) { + if (dho->option == ndo.nd_opt_type) + break; + } + if (i == ctx->nd_opts_len) + logmessage(loglevel, + "%s: reject RA (option %d) from %s", + ifp->name, ndo.nd_opt_type, sfrom); + else + logmessage(loglevel, + "%s: reject RA (option %s) from %s", + ifp->name, dho->var, sfrom); + return; + } + } + + for (i = 0, dho = ctx->nd_opts; i < ctx->nd_opts_len; i++, dho++) { + if (!has_option_mask(ifo->requiremasknd, dho->option)) + continue; + len = rlen; + len -= sizeof(struct nd_router_advert); + p = ((uint8_t *)icp) + sizeof(struct nd_router_advert); + has_opt = false; + for (; len > 0; p += olen, len -= olen) { + if (len < sizeof(ndo)) + break; + memcpy(&ndo, p, sizeof(ndo)); + olen = (size_t)ndo.nd_opt_len * 8; + if (olen > len) + break; + if (ndo.nd_opt_type == dho->option) { + has_opt = true; + break; + } + } + if (has_opt) + continue; + logmessage(loglevel, "%s: reject RA (missing %s) from %s", + ifp->name, dho->var, sfrom); + return; + } + len = rlen; + /* We don't want to spam the log with the fact we got an RA every * 30 seconds or so, so only spam the log if it's different. */ if (rap == NULL || @@ -1125,39 +1200,12 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, const struct sockaddr_in6 *from, len -= sizeof(struct nd_router_advert); p = ((uint8_t *)icp) + sizeof(struct nd_router_advert); for (; len > 0; p += olen, len -= olen) { - if (len < sizeof(ndo)) { - logerrx("%s: short option", ifp->name); + if (len < sizeof(ndo)) break; - } memcpy(&ndo, p, sizeof(ndo)); olen = (size_t)ndo.nd_opt_len * 8; - if (olen == 0) { - /* RFC4681 4.6 says we MUST discard this ND packet. */ - logerrx("%s: zero length option", ifp->name); - FREE_RAP(rap); - return; - } - if (olen > len) { - logerrx("%s: option length exceeds message", ifp->name); + if (olen > len) break; - } - - if (has_option_mask(ifp->options->rejectmasknd, - ndo.nd_opt_type)) { - for (i = 0, dho = ctx->nd_opts; i < ctx->nd_opts_len; - i++, dho++) { - if (dho->option == ndo.nd_opt_type) - break; - } - if (i == ctx->nd_opts_len) - logwarnx("%s: reject RA (option %d) from %s", - ifp->name, ndo.nd_opt_type, rap->sfrom); - else - logwarnx("%s: reject RA (option %s) from %s", - ifp->name, dho->var, rap->sfrom); - FREE_RAP(rap); - return; - } if (has_option_mask(ifp->options->nomasknd, ndo.nd_opt_type)) continue; @@ -1406,15 +1454,6 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, const struct sockaddr_in6 *from, } } - for (i = 0, dho = ctx->nd_opts; i < ctx->nd_opts_len; i++, dho++) { - if (has_option_mask(ifp->options->requiremasknd, dho->option)) { - logwarnx("%s: reject RA (no option %s) from %s", - ifp->name, dho->var, rap->sfrom); - FREE_RAP(rap); - return; - } - } - TAILQ_FOREACH(ia, &rap->addrs, next) { if (!(ia->flags & IPV6_AF_STALE) || ia->prefix_pltime == 0) continue;