Index: sys/netinet6/nd6_rtr.c =================================================================== --- sys/netinet6/nd6_rtr.c +++ sys/netinet6/nd6_rtr.c @@ -68,6 +68,7 @@ #include #include #include +#include #include #include #include @@ -103,6 +104,28 @@ SYSCTL_DECL(_net_inet6_icmp6); +/* + * Global limit and per-VNET count for the maximum amount of default routers + * we accept in the list and equally for prefixes. + * The sysctls are not adjustable per-VNET to keep things simple and avoid + * VNETs allowing themselves to be a DoS target exhausting memory. Otherwise + * we need to make this a PROC as well and handle a global max cap per-vnet. + */ +static int nd6_defrouter_max = 16; +SYSCTL_INT(_net_inet6_icmp6, OID_AUTO, nd6_defrouter_max, + CTLFLAG_RW, &nd6_defrouter_max, 0, + "Maximum number of IPv6 default routers. 0 accept unlimited."); +VNET_DEFINE_STATIC(int, nd6_defrouter_cur); +#define V_nd6_defrouter_cur VNET(nd6_defrouter_cur) + +static int nd6_prefixes_max = 64; +SYSCTL_INT(_net_inet6_icmp6, OID_AUTO, nd6_prefixes_max, + CTLFLAG_RW, &nd6_prefixes_max, 0, + "Maximum number of IPv6 prefix list entries. 0 accept unlimited."); +VNET_DEFINE_STATIC(int, nd6_prefixes_cur); +#define V_nd6_prefixes_cur VNET(nd6_prefixes_cur) + + /* RTPREF_MEDIUM has to be 0! */ #define RTPREF_HIGH 1 #define RTPREF_MEDIUM 0 @@ -136,6 +159,7 @@ ND6_WLOCK_ASSERT(); TAILQ_REMOVE(&V_nd6_defrouter, dr, dr_entry); + V_nd6_defrouter_cur--; V_nd6_list_genid++; if (drq != NULL) TAILQ_INSERT_TAIL(drq, dr, dr_entry); @@ -1102,10 +1126,20 @@ /* * The preferred router may have changed, so relocate this * router. + * We are removing the dr from the list only to re-add it + * later, so we skip any default router list limit checks here. + * This way we make sure we do not lose a (hopefully) valid + * and working default router in case we are under attack. */ TAILQ_REMOVE(&V_nd6_defrouter, dr, dr_entry); n = dr; } else { + if (nd6_defrouter_max > 0 && + V_nd6_defrouter_cur >= nd6_defrouter_max) { + ND6_WUNLOCK(); + ICMP6STAT_INC(icp6s_overflowdefrtr); + return (NULL); + } n = malloc(sizeof(*n), M_IP6NDP, M_NOWAIT | M_ZERO); if (n == NULL) { ND6_WUNLOCK(); @@ -1114,6 +1148,7 @@ memcpy(n, new, sizeof(*n)); /* Initialize with an extra reference for the caller. */ refcount_init(&n->refcnt, 2); + V_nd6_defrouter_cur++; } /* @@ -1369,7 +1404,14 @@ IN6_MASK_ADDR(&new->ndpr_prefix.sin6_addr, &new->ndpr_mask); ND6_WLOCK(); + if (nd6_prefixes_max > 0 && V_nd6_prefixes_cur >= nd6_prefixes_max) { + ND6_WUNLOCK(); + ICMP6STAT_INC(icp6s_overflowprfx); + free(new, M_IP6NDP); + return (ENOBUFS); + } LIST_INSERT_HEAD(&V_nd_prefix, new, ndpr_entry); + V_nd6_prefixes_cur++; V_nd6_list_genid++; ND6_WUNLOCK(); @@ -1410,6 +1452,7 @@ ND6_WLOCK_ASSERT(); LIST_REMOVE(pr, ndpr_entry); + V_nd6_prefixes_cur--; V_nd6_list_genid++; if (list != NULL) LIST_INSERT_HEAD(list, pr, ndpr_entry);