DragonFlyBSD Kernel Audit
DF-0418 / fix.diff
← back to finding ↓ download raw
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;