The branch main has been updated by pouria: URL: https://cgit.FreeBSD.org/src/commit/?id=0616521adc35cb252cb399f1147f103284f0f188
commit 0616521adc35cb252cb399f1147f103284f0f188 Author: Pouria Mousavizadeh Tehrani <[email protected]> AuthorDate: 2026-03-05 18:01:30 +0000 Commit: Pouria Mousavizadeh Tehrani <[email protected]> CommitDate: 2026-03-05 18:04:32 +0000 ndp: Simplify and breakdown nd6_ra_input() `nd6_ra_input()` is simplied to make it easier to add additional options. Reviewed by: glebius Differential Revision: https://reviews.freebsd.org/D55267 --- sys/netinet6/nd6.c | 8 +- sys/netinet6/nd6_rtr.c | 300 +++++++++++++++++++++++++------------------------ 2 files changed, 158 insertions(+), 150 deletions(-) diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index f4e48eb57329..5b93c7f115e9 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -435,11 +435,11 @@ nd6_option(union nd_opts *ndopts) } olen = nd_opt->nd_opt_len << 3; + /* + * RFC 4861 section 6.1.2: All included options + * must have a length that is greater than zero. + */ if (olen == 0) { - /* - * Message validation requires that all included - * options have a length that is greater than zero. - */ bzero(ndopts, sizeof(*ndopts)); return NULL; } diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index ba2f025b7db1..6f415408daae 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -79,7 +79,7 @@ MALLOC_DEFINE(M_IP6NDP, "ip6ndp", "IPv6 Neighbor Discovery"); static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *); static int prelist_update(struct nd_prefixctl *, struct nd_defrouter *, - struct mbuf *, int); + bool, int); static int nd6_prefix_onlink(struct nd_prefix *); static int in6_get_tmp_ifid(struct in6_aliasreq *); @@ -180,7 +180,7 @@ nd6_rs_input(struct mbuf *m, int off, int icmp6len) if (!V_ip6_forwarding || ifp->if_inet6->nd_flags & ND6_IFF_ACCEPT_RTADV) goto freeit; - /* RFC 6980: Nodes MUST silently ignore fragments */ + /* RFC 6980: Nodes MUST silently ignore fragments */ if(m->m_flags & M_FRAGMENTED) goto freeit; @@ -355,6 +355,127 @@ nd6_ifnet_link_event(void *arg __unused, struct ifnet *ifp, int linkstate) #endif } +static void +nd6_ra_opt_pi(struct nd_opt_hdr *pt, struct ifnet *ifp, + struct nd_router_advert *nd_ra, struct nd_defrouter *dr, + bool auth, bool mcast) +{ + struct nd_opt_prefix_info *pi = NULL; + struct nd_prefixctl pr; + char ip6bufs[INET6_ADDRSTRLEN]; + + if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION) + return; + + pi = (struct nd_opt_prefix_info *)pt; + if (pi->nd_opt_pi_len != 4) { + nd6log((LOG_INFO, + "%s: invalid option len %d for prefix " + "information option, ignored\n", __func__, + pi->nd_opt_pi_len)); + return; + } + + if (pi->nd_opt_pi_prefix_len > 128) { + nd6log((LOG_INFO, + "%s: invalid prefix len %d for prefix " + "information option, ignored\n", __func__, + pi->nd_opt_pi_prefix_len)); + return; + } + + if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) + || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) { + nd6log((LOG_INFO, + "%s: invalid prefix %s, ignored\n", + __func__, ip6_sprintf(ip6bufs, + &pi->nd_opt_pi_prefix))); + return; + } + + bzero(&pr, sizeof(pr)); + pr.ndpr_prefix.sin6_family = AF_INET6; + pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix); + pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix; + pr.ndpr_ifp = ifp; + + pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved & + ND_OPT_PI_FLAG_ONLINK) ? 1 : 0; + pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved & + ND_OPT_PI_FLAG_AUTO) ? 1 : 0; + pr.ndpr_plen = pi->nd_opt_pi_prefix_len; + pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time); + pr.ndpr_pltime = ntohl(pi->nd_opt_pi_preferred_time); + (void)prelist_update(&pr, dr, auth, mcast); +} + +static void +nd6_ra_opt_mtu(struct nd_opt_mtu *optmtu, struct ifnet *ifp, + struct in6_addr saddr6) +{ + struct in6_ifextra *ndi; + char ip6bufs[INET6_ADDRSTRLEN]; + uint32_t mtu, maxmtu; + + ndi = ifp->if_inet6; + + if (optmtu->nd_opt_mtu_len != 1) + return; + mtu = (uint32_t)ntohl(optmtu->nd_opt_mtu_mtu); + /* lower bound */ + if (mtu < IPV6_MMTU) { + nd6log((LOG_INFO, "%s: bogus mtu option mtu=%u sent from %s, " + "ignoring\n", __func__, mtu, ip6_sprintf(ip6bufs, &saddr6))); + return; + } + + /* upper bound */ + maxmtu = (ndi->nd_maxmtu && ndi->nd_maxmtu < ifp->if_mtu) + ? ndi->nd_maxmtu : ifp->if_mtu; + if (mtu <= maxmtu) { + if (ndi->nd_linkmtu != mtu) { + ndi->nd_linkmtu = mtu; + rt_updatemtu(ifp); + } + } else { + nd6log((LOG_INFO, "%s: bogus mtu=%u sent from %s; " + "exceeds maxmtu %u, ignoring\n", __func__, + mtu, ip6_sprintf(ip6bufs, &saddr6), maxmtu)); + } +} + +static int +nd6_ra_opt_src_lladdr(struct nd_opt_hdr *opthdr, struct ifnet *ifp, + struct in6_addr saddr6) +{ + char ip6bufs[INET6_ADDRSTRLEN]; + char *lladdr = NULL; + int lladdrlen = 0; + + if (opthdr != NULL) { + lladdr = (char *)(opthdr + 1); + lladdrlen = opthdr->nd_opt_len << 3; + } + + if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { + nd6log((LOG_INFO, + "%s: lladdrlen mismatch for %s (if %d, RA packet %d)\n", + __func__, ip6_sprintf(ip6bufs, &saddr6), + ifp->if_addrlen, lladdrlen - 2)); + return (-1); + } + + nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0); + + /* + * Installing a link-layer address might change the state of the + * router's neighbor cache, which might also affect our on-link + * detection of adveritsed prefixes. + */ + pfxlist_onlink_check(); + return (0); +} + /* * Receive Router Advertisement Message. * @@ -369,11 +490,13 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) struct in6_ifextra *ndi; struct ip6_hdr *ip6; struct nd_router_advert *nd_ra; + struct nd_opt_hdr *pt; struct in6_addr saddr6; - struct nd_defrouter *dr; + struct nd_defrouter dr0, *dr; union nd_opts ndopts; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; - int mcast; + uint32_t advreachable; + bool mcast, auth; /* * We only accept RAs only when the per-interface flag @@ -389,6 +512,7 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) goto freeit; ip6 = mtod(m, struct ip6_hdr *); + /* RFC 4861 section 6.1.2: hlim must be 255 */ if (__predict_false(ip6->ip6_hlim != 255)) { ICMP6STAT_INC(icp6s_invlhlim); nd6log((LOG_ERR, @@ -399,6 +523,7 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) } saddr6 = ip6->ip6_src; + /* RFC 4861 section 6.1.2: source address must be link-local */ if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) { nd6log((LOG_ERR, "%s: src %s is not link-local\n", __func__, @@ -413,7 +538,6 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) return; } } - ip6 = mtod(m, struct ip6_hdr *); nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off); icmp6len -= sizeof(*nd_ra); @@ -425,15 +549,11 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) goto freeit; } - mcast = 0; dr = NULL; - { - struct nd_defrouter dr0; - u_int32_t advreachable = nd_ra->nd_ra_reachable; - + mcast = false; /* remember if this is a multicasted advertisement */ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) - mcast = 1; + mcast = true; bzero(&dr0, sizeof(dr0)); dr0.rtaddr = saddr6; @@ -443,17 +563,20 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) * ND6_IFF_NO_RADR enabled on the receiving interface or * (ip6.forwarding == 1 && ip6.rfc6204w3 != 1). */ - if (ndi->nd_flags & ND6_IFF_NO_RADR) - dr0.rtlifetime = 0; - else if (V_ip6_forwarding && !V_ip6_rfc6204w3) + if ((ndi->nd_flags & ND6_IFF_NO_RADR) || + (V_ip6_forwarding && !V_ip6_rfc6204w3)) dr0.rtlifetime = 0; else dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime); dr0.expire = time_uptime + dr0.rtlifetime; dr0.ifp = ifp; - /* unspecified or not? (RFC 2461 6.3.4) */ - if (advreachable) { - advreachable = ntohl(advreachable); + /* + * RFC 4861 6.3.4: RA fields such as Cur Hop Limit, + * Reachable Time, and Retrans Timer may be unspecified. + * In such cases, the parameter should be ignored. + */ + if (nd_ra->nd_ra_reachable) { + advreachable = ntohl(nd_ra->nd_ra_reachable); if (advreachable <= MAX_REACHABLE_TIME && ndi->nd_basereachable != advreachable) { ndi->nd_basereachable = advreachable; @@ -479,63 +602,18 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) #ifdef EXPERIMENTAL defrtr_ipv6_only_ifp(ifp); #endif - } - - /* - * prefix - */ - if (ndopts.nd_opts_pi) { - struct nd_opt_hdr *pt; - struct nd_opt_prefix_info *pi = NULL; - struct nd_prefixctl pr; - + /* Prefix Information */ + if (ndopts.nd_opts_pi != NULL) { + /* + * Authenticity for NA consists authentication for + * both IP header and IP datagrams, doesn't it ? + */ + auth = ((m->m_flags & M_AUTHIPHDR) && (m->m_flags & M_AUTHIPDGM)); for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi; pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end; pt = (struct nd_opt_hdr *)((caddr_t)pt + (pt->nd_opt_len << 3))) { - if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION) - continue; - pi = (struct nd_opt_prefix_info *)pt; - - if (pi->nd_opt_pi_len != 4) { - nd6log((LOG_INFO, - "%s: invalid option len %d for prefix " - "information option, ignored\n", __func__, - pi->nd_opt_pi_len)); - continue; - } - - if (128 < pi->nd_opt_pi_prefix_len) { - nd6log((LOG_INFO, - "%s: invalid prefix len %d for prefix " - "information option, ignored\n", __func__, - pi->nd_opt_pi_prefix_len)); - continue; - } - - if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) - || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) { - nd6log((LOG_INFO, - "%s: invalid prefix %s, ignored\n", - __func__, ip6_sprintf(ip6bufs, - &pi->nd_opt_pi_prefix))); - continue; - } - - bzero(&pr, sizeof(pr)); - pr.ndpr_prefix.sin6_family = AF_INET6; - pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix); - pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix; - pr.ndpr_ifp = (struct ifnet *)m->m_pkthdr.rcvif; - - pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved & - ND_OPT_PI_FLAG_ONLINK) ? 1 : 0; - pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved & - ND_OPT_PI_FLAG_AUTO) ? 1 : 0; - pr.ndpr_plen = pi->nd_opt_pi_prefix_len; - pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time); - pr.ndpr_pltime = ntohl(pi->nd_opt_pi_preferred_time); - (void)prelist_update(&pr, dr, m, mcast); + nd6_ra_opt_pi(pt, ifp, nd_ra, dr, auth, mcast); } } if (dr != NULL) { @@ -543,70 +621,13 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) dr = NULL; } - /* - * MTU - */ - if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) { - u_long mtu; - u_long maxmtu; - - mtu = (u_long)ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); - - /* lower bound */ - if (mtu < IPV6_MMTU) { - nd6log((LOG_INFO, "%s: bogus mtu option mtu=%lu sent " - "from %s, ignoring\n", __func__, - mtu, ip6_sprintf(ip6bufs, &ip6->ip6_src))); - goto skip; - } - - /* upper bound */ - maxmtu = (ndi->nd_maxmtu && ndi->nd_maxmtu < ifp->if_mtu) - ? ndi->nd_maxmtu : ifp->if_mtu; - if (mtu <= maxmtu) { - if (ndi->nd_linkmtu != mtu) { - ndi->nd_linkmtu = mtu; - rt_updatemtu(ifp); - } - } else { - nd6log((LOG_INFO, "%s: bogus mtu=%lu sent from %s; " - "exceeds maxmtu %lu, ignoring\n", __func__, - mtu, ip6_sprintf(ip6bufs, &ip6->ip6_src), maxmtu)); - } - } - - skip: + /* MTU */ + if (ndopts.nd_opts_mtu != NULL) + nd6_ra_opt_mtu(ndopts.nd_opts_mtu, ifp, saddr6); - /* - * Source link layer address - */ - { - char *lladdr = NULL; - int lladdrlen = 0; - - if (ndopts.nd_opts_src_lladdr) { - lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); - lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; - } - - if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - nd6log((LOG_INFO, - "%s: lladdrlen mismatch for %s (if %d, RA packet %d)\n", - __func__, ip6_sprintf(ip6bufs, &saddr6), - ifp->if_addrlen, lladdrlen - 2)); + /* Source link layer address */ + if (nd6_ra_opt_src_lladdr(ndopts.nd_opts_src_lladdr, ifp, saddr6) != 0) goto bad; - } - - nd6_cache_lladdr(ifp, &saddr6, lladdr, - lladdrlen, ND_ROUTER_ADVERT, 0); - - /* - * Installing a link-layer address might change the state of the - * router's neighbor cache, which might also affect our on-link - * detection of adveritsed prefixes. - */ - pfxlist_onlink_check(); - } freeit: m_freem(m); @@ -1478,32 +1499,19 @@ nd6_prefix_del(struct nd_prefix *pr) static int prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr, - struct mbuf *m, int mcast) + bool auth, int mcast) { struct in6_ifaddr *ia6 = NULL, *ia6_match = NULL; struct ifaddr *ifa; struct ifnet *ifp = new->ndpr_ifp; struct nd_prefix *pr; int error = 0; - int auth; struct in6_addrlifetime lt6_tmp; char ip6buf[INET6_ADDRSTRLEN]; bool has_temporary = false; NET_EPOCH_ASSERT(); - auth = 0; - if (m) { - /* - * Authenticity for NA consists authentication for - * both IP header and IP datagrams, doesn't it ? - */ -#if defined(M_AUTHIPHDR) && defined(M_AUTHIPDGM) - auth = ((m->m_flags & M_AUTHIPHDR) && - (m->m_flags & M_AUTHIPDGM)); -#endif - } - if ((pr = nd6_prefix_lookup(new)) != NULL) { /* * nd6_prefix_lookup() ensures that pr and new have the same
