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

Reply via email to