diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -127,6 +127,15 @@ /* preventing too many loops in ND option parsing */ int nd6_maxndopt = 10; /* max # of ND options allowed */ +/* + * Per-interface caps on the Default Router List and the Prefix List, + * to prevent unbounded kernel memory growth from a flood of spoofed + * Router Advertisements (RFC 6104/6105). A host legitimately learns a + * small number of routers/prefixes; values above are refused and logged. + */ +int nd6_maxdefrouters = 16; /* max # of default routers per interface */ +int nd6_maxprefixes = 16; /* max # of on-link prefixes per interface */ + int nd6_maxnudhint = 0; /* max # of subsequent upper layer hints */ #ifdef ND6_DEBUG diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -686,6 +686,25 @@ return (NULL); } + /* DF-0418: cap per-interface default-router list to bound memory */ + { + struct nd_defrouter *drtmp; + int nrt = 0; + + TAILQ_FOREACH(drtmp, &nd_defrouter, dr_entry) { + if (drtmp->ifp == new->ifp) + nrt++; + } + if (nrt >= nd6_maxdefrouters) { + nd6log((LOG_INFO, "nd6_rtr: default router list for %s " + "full (%d), ignoring RA from %s\n", + if_name(new->ifp), nrt, + ip6_sprintf(&new->rtaddr))); + mtx_unlock(&nd6_mtx); + return (NULL); + } + } + n = (struct nd_defrouter *)kmalloc(sizeof(*n), M_IP6NDP, M_NOWAIT | M_ZERO); if (n == NULL) { @@ -762,6 +781,24 @@ struct nd_prefix *new = NULL; int i; + /* DF-0418: cap per-interface prefix list to bound memory */ + { + struct nd_prefix *ptmp; + int npfx = 0; + + for (ptmp = nd_prefix.lh_first; ptmp; ptmp = ptmp->ndpr_next) { + if (ptmp->ndpr_ifp == pr->ndpr_ifp) + npfx++; + } + if (npfx >= nd6_maxprefixes) { + nd6log((LOG_INFO, "nd6_rtr: prefix list for %s full (%d), " + "ignoring prefix %s/%d\n", if_name(pr->ndpr_ifp), npfx, + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen)); + if (newp != NULL) + *newp = NULL; + return (ENOSPC); + } + } new = kmalloc(sizeof(*new), M_IP6NDP, M_INTWAIT); *new = *pr; if (newp != NULL) diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -319,6 +319,8 @@ extern struct llinfo_nd6 llinfo_nd6; extern struct nd_ifinfo *nd_ifinfo; extern struct nd_drhead nd_defrouter; +extern int nd6_maxdefrouters; +extern int nd6_maxprefixes; extern struct nd_prhead nd_prefix; extern struct mtx nd6_mtx; extern int nd6_debug;