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 @@ -81,6 +82,22 @@ VNET_DEFINE_STATIC(struct nd6_drhead, nd6_defrouter); #define V_nd6_defrouter VNET(nd6_defrouter) +/* + * Global limit and per-VNET count for the maximum amount of default routers + * we accept in the list. + */ +static int nd6_defrouter_max = 16; +VNET_DEFINE_STATIC(int, nd6_defrouter_cur); +#define V_nd6_defrouter_cur VNET(nd6_defrouter_cur) + +/* + * Global limit and per-VNET count for the maximum amount of prefixes + * we accept in the list. + */ +static int nd6_prefixes_max = 64; +VNET_DEFINE_STATIC(int, nd6_prefixes_cur); +#define V_nd6_prefixes_cur VNET(nd6_prefixes_cur) + VNET_DECLARE(int, nd6_recalc_reachtm_interval); #define V_nd6_recalc_reachtm_interval VNET(nd6_recalc_reachtm_interval) @@ -135,6 +152,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); @@ -1122,16 +1140,31 @@ } } + /* + * Do this check before possibly removing the router from the list. + * If the sysctl is set to 0 we accept an unrestricted amount of + * default routers. If people do not want default routers, they + * should use the -no_radr interface flags or sysctl. + */ + if (nd6_defrouter_max > 0 && V_nd6_defrouter_cur >= nd6_defrouter_max) { + ND6_WUNLOCK(); + ICMP6STAT_INC(icp6s_overflowdefrtr); + return (NULL); + } + V_nd6_defrouter_cur++; + if (dr != NULL) { /* * The preferred router may have changed, so relocate this * router. */ TAILQ_REMOVE(&V_nd6_defrouter, dr, dr_entry); + V_nd6_defrouter_cur--; n = dr; } else { n = malloc(sizeof(*n), M_IP6NDP, M_NOWAIT | M_ZERO); if (n == NULL) { + V_nd6_defrouter_cur--; ND6_WUNLOCK(); return (NULL); } @@ -1393,7 +1426,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(); @@ -1434,6 +1474,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); @@ -2639,3 +2680,15 @@ CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, nd6_sysctl_drlist, "S,in6_defrouter", "NDP default router list"); + +/* + * These ones 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. + */ +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."); +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.");