Page MenuHomeFreeBSD

D22447.diff
No OneTemporary

D22447.diff

Index: sys/net/if_llatbl.h
===================================================================
--- sys/net/if_llatbl.h
+++ sys/net/if_llatbl.h
@@ -151,8 +151,8 @@
typedef void (llt_free_entry_t)(struct lltable *, struct llentry *);
typedef void (llt_fill_sa_entry_t)(const struct llentry *, struct sockaddr *);
typedef void (llt_free_tbl_t)(struct lltable *);
-typedef void (llt_link_entry_t)(struct lltable *, struct llentry *);
-typedef void (llt_unlink_entry_t)(struct llentry *);
+typedef int (llt_link_entry_t)(struct lltable *, struct llentry *);
+typedef int (llt_unlink_entry_t)(struct llentry *);
typedef void (llt_mark_used_t)(struct llentry *);
typedef int (llt_foreach_cb_t)(struct lltable *, struct llentry *, void *);
@@ -162,6 +162,8 @@
SLIST_ENTRY(lltable) llt_link;
int llt_af;
int llt_hsize;
+ int llt_entries;
+ int llt_spare;
struct llentries *lle_head;
struct ifnet *llt_ifp;
@@ -213,6 +215,9 @@
struct sockaddr *, u_int);
int lltable_sysctl_dumparp(int, struct sysctl_req *);
+int htable_link_entry(struct lltable *, struct llentry *);
+int htable_unlink_entry(struct llentry *);
+
size_t llentry_free(struct llentry *);
/* helper functions */
@@ -230,8 +235,8 @@
void lltable_free_entry(struct lltable *llt, struct llentry *lle);
int lltable_delete_addr(struct lltable *llt, u_int flags,
const struct sockaddr *l3addr);
-void lltable_link_entry(struct lltable *llt, struct llentry *lle);
-void lltable_unlink_entry(struct lltable *llt, struct llentry *lle);
+int lltable_link_entry(struct lltable *llt, struct llentry *lle);
+int lltable_unlink_entry(struct lltable *llt, struct llentry *lle);
void lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa);
struct ifnet *lltable_get_ifp(const struct lltable *llt);
int lltable_get_af(const struct lltable *llt);
Index: sys/net/if_llatbl.c
===================================================================
--- sys/net/if_llatbl.c
+++ sys/net/if_llatbl.c
@@ -153,14 +153,14 @@
return (error);
}
-static void
+int
htable_link_entry(struct lltable *llt, struct llentry *lle)
{
struct llentries *lleh;
uint32_t hashidx;
if ((lle->la_flags & LLE_LINKED) != 0)
- return;
+ return (0);
IF_AFDATA_WLOCK_ASSERT(llt->llt_ifp);
@@ -171,14 +171,16 @@
lle->lle_head = lleh;
lle->la_flags |= LLE_LINKED;
CK_LIST_INSERT_HEAD(lleh, lle, lle_next);
+
+ return (1);
}
-static void
+int
htable_unlink_entry(struct llentry *lle)
{
if ((lle->la_flags & LLE_LINKED) == 0)
- return;
+ return (0);
IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp);
CK_LIST_REMOVE(lle, lle_next);
@@ -187,6 +189,8 @@
lle->lle_tbl = NULL;
lle->lle_head = NULL;
#endif
+
+ return (1);
}
struct prefix_match_data {
@@ -483,6 +487,9 @@
llentry_free(lle);
}
+ KASSERT(llt->llt_entries == 0, ("%s: lltable %p (%s) entires not 0: %d",
+ __func__, llt, llt->llt_ifp->if_xname, llt->llt_entries));
+
llt->llt_free_tbl(llt);
}
@@ -608,18 +615,18 @@
llt->llt_free_entry(llt, lle);
}
-void
+int
lltable_link_entry(struct lltable *llt, struct llentry *lle)
{
- llt->llt_link_entry(llt, lle);
+ return (llt->llt_link_entry(llt, lle));
}
-void
+int
lltable_unlink_entry(struct lltable *llt, struct llentry *lle)
{
- llt->llt_unlink_entry(lle);
+ return (llt->llt_unlink_entry(lle));
}
void
Index: sys/netinet/icmp6.h
===================================================================
--- sys/netinet/icmp6.h
+++ sys/netinet/icmp6.h
@@ -635,6 +635,10 @@
uint64_t icp6s_badrs; /* bad router solicitation */
uint64_t icp6s_badra; /* bad router advertisement */
uint64_t icp6s_badredirect; /* bad redirect message */
+ uint64_t icp6s_overflowdefrtr; /* Too many default routers. */
+ uint64_t icp6s_overflowprfx; /* Too many prefixes. */
+ uint64_t icp6s_overflownndp; /* Too many neighbour entries. */
+ uint64_t icp6s_overflowredirect;/* Too many redirects. */
};
#ifdef _KERNEL
Index: sys/netinet6/in6.c
===================================================================
--- sys/netinet6/in6.c
+++ sys/netinet6/in6.c
@@ -104,6 +104,7 @@
#include <netinet/ip_carp.h>
#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#include <netinet6/mld6_var.h>
@@ -122,6 +123,8 @@
offsetof(struct ifreq, ifr_ifru),
"struct in6_ifreq and struct ifreq are not type punnable");
+static int in6_llt_max_entries;
+
VNET_DECLARE(int, icmp6_nodeinfo_oldmcprefix);
#define V_icmp6_nodeinfo_oldmcprefix VNET(icmp6_nodeinfo_oldmcprefix)
@@ -164,8 +167,20 @@
#define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa))
#define ia62ifa(ia6) (&((ia6)->ia_ifa))
+SYSCTL_DECL(_net_inet6_icmp6);
+SYSCTL_INT(_net_inet6_icmp6, OID_AUTO, in6_llt_max_entries,
+ CTLFLAG_RW, &in6_llt_max_entries, 0,
+ "Maximum number of IPv6 neighbour entries per interface. "
+ "0 accept unlimited.");
void
+in6_init(void)
+{
+
+ in6_llt_max_entries = min(physmem / 256, 16384);
+}
+
+void
in6_newaddrmsg(struct in6_ifaddr *ia, int cmd)
{
struct sockaddr_dl gateway;
@@ -2407,6 +2422,49 @@
return (error);
}
+/*
+ * We are overloading the if_llatbl base functions in order to check limits
+ * of maximum number of table entries.
+ * The functions return: 0 if the entry was (un)linked already and nothing
+ * changed, 1 if the entry was added/removed to/from the table, and -1 on
+ * error (e.g., not being able to add the entry due to limits reached).
+ * While the "unlink" operation should always succeed, callers of
+ * lltable_link_entry() need to check for errors and handle them.
+ */
+static int
+in6_htable_link_entry(struct lltable *llt, struct llentry *lle)
+{
+ int linked;
+
+ if (in6_llt_max_entries > 0 &&
+ llt->llt_entries >= in6_llt_max_entries) {
+ ICMP6STAT_INC(icp6s_overflownndp);
+ return (-1);
+ }
+
+ linked = htable_link_entry(llt, lle);
+ if (linked > 0)
+ llt->llt_entries += linked;
+
+ return (linked);
+}
+
+static int
+in6_htable_unlink_entry(struct llentry *lle)
+{
+ struct lltable *llt;
+ int unlinked;
+
+ llt = lle->lle_tbl;
+ KASSERT(llt->llt_entries > 0, ("%s: lltable %p (%s) entries %d <= 0",
+ __func__, llt, llt->llt_ifp->if_xname, llt->llt_entries));
+ unlinked = htable_unlink_entry(lle);
+ if (unlinked > 0)
+ llt->llt_entries -= unlinked;
+
+ return (unlinked);
+}
+
static struct lltable *
in6_lltattach(struct ifnet *ifp)
{
@@ -2425,6 +2483,9 @@
llt->llt_free_entry = in6_lltable_free_entry;
llt->llt_match_prefix = in6_lltable_match_prefix;
llt->llt_mark_used = in6_lltable_mark_used;
+ llt->llt_link_entry = in6_htable_link_entry;
+ llt->llt_unlink_entry = in6_htable_unlink_entry;
+
lltable_link(llt);
return (llt);
Index: sys/netinet6/nd6.h
===================================================================
--- sys/netinet6/nd6.h
+++ sys/netinet6/nd6.h
@@ -352,6 +352,9 @@
#define nd_opts_last nd_opt_each.last
#define nd_opts_done nd_opt_each.done
+/* in6.c */
+void in6_init(void);
+
/* XXX: need nd6_var.h?? */
/* nd6.c */
void nd6_init(void);
Index: sys/netinet6/nd6.c
===================================================================
--- sys/netinet6/nd6.c
+++ sys/netinet6/nd6.c
@@ -231,6 +231,8 @@
nd6_dad_init();
if (IS_DEFAULT_VNET(curvnet)) {
+ in6_init();
+
lle_event_eh = EVENTHANDLER_REGISTER(lle_event, nd6_lle_event,
NULL, EVENTHANDLER_PRI_ANY);
iflladdr_event_eh = EVENTHANDLER_REGISTER(iflladdr_event,
@@ -1929,7 +1931,7 @@
struct mbuf *chain = NULL;
u_char linkhdr[LLE_MAX_LINKHDR];
size_t linkhdrsize;
- int lladdr_off;
+ int linked, lladdr_off;
NET_EPOCH_ASSERT();
IF_AFDATA_UNLOCK_ASSERT(ifp);
@@ -1976,8 +1978,14 @@
LLE_WLOCK(ln);
/* Prefer any existing lle over newly-created one */
ln_tmp = nd6_lookup(from, LLE_EXCLUSIVE, ifp);
- if (ln_tmp == NULL)
- lltable_link_entry(LLTABLE6(ifp), ln);
+ if (ln_tmp == NULL) {
+ linked = lltable_link_entry(LLTABLE6(ifp), ln);
+ if (linked < 0) {
+ IF_AFDATA_WUNLOCK(ifp);
+ lltable_free_entry(LLTABLE6(ifp), ln);
+ return;
+ }
+ }
IF_AFDATA_WUNLOCK(ifp);
if (ln_tmp == NULL) {
/* No existing lle, mark as new entry (6,7) */
@@ -2285,7 +2293,7 @@
{
struct llentry *lle = NULL, *lle_tmp;
struct in6_addr *psrc, src;
- int send_ns, ll_len;
+ int linked, send_ns, ll_len;
char *lladdr;
NET_EPOCH_ASSERT();
@@ -2320,8 +2328,12 @@
/* Prefer any existing entry over newly-created one */
lle_tmp = nd6_lookup(&dst->sin6_addr, LLE_EXCLUSIVE, ifp);
if (lle_tmp == NULL)
- lltable_link_entry(LLTABLE6(ifp), lle);
+ linked = lltable_link_entry(LLTABLE6(ifp), lle);
IF_AFDATA_WUNLOCK(ifp);
+ if (lle_tmp == NULL && linked < 0) {
+ lltable_free_entry(LLTABLE6(ifp), lle);
+ lle = NULL;
+ }
if (lle_tmp != NULL) {
lltable_free_entry(LLTABLE6(ifp), lle);
lle = lle_tmp;
@@ -2506,6 +2518,7 @@
struct ifnet *ifp;
struct llentry *ln, *ln_tmp;
struct sockaddr *dst;
+ int linked;
ifp = ia->ia_ifa.ifa_ifp;
if (nd6_need_cache(ifp) == 0)
@@ -2521,9 +2534,14 @@
LLE_WLOCK(ln);
/* Unlink any entry if exists */
ln_tmp = lla_lookup(LLTABLE6(ifp), LLE_EXCLUSIVE, dst);
+ linked = lltable_link_entry(LLTABLE6(ifp), ln);
+ if (linked < 0) {
+ IF_AFDATA_WUNLOCK(ifp);
+ lltable_free_entry(LLTABLE6(ifp), ln);
+ return (ENOBUFS);
+ }
if (ln_tmp != NULL)
lltable_unlink_entry(LLTABLE6(ifp), ln_tmp);
- lltable_link_entry(LLTABLE6(ifp), ln);
IF_AFDATA_WUNLOCK(ifp);
if (ln_tmp != NULL)
Index: sys/netinet6/nd6_rtr.c
===================================================================
--- sys/netinet6/nd6_rtr.c
+++ sys/netinet6/nd6_rtr.c
@@ -68,6 +68,7 @@
#include <netinet6/in6_var.h>
#include <netinet6/in6_ifattach.h>
#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#include <netinet/icmp6.h>
@@ -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.");
Index: usr.bin/netstat/inet6.c
===================================================================
--- usr.bin/netstat/inet6.c
+++ usr.bin/netstat/inet6.c
@@ -1055,6 +1055,14 @@
"{N:/bad router advertisement message%s}\n");
p(icp6s_badredirect, "\t{:bad-redirect/%ju} "
"{N:/bad redirect message%s}\n");
+ p_5(icp6s_overflowdefrtr, "\t{:too-many-default-routers/%ju} "
+ "{N:/too many default routers}\n");
+ p_5(icp6s_overflowprfx, "\t{:too-many-prefixes/%ju} "
+ "{N:/too many prefixes}\n");
+ p_5(icp6s_overflownndp, "\t{:too-many-neighbour-entries/%ju} "
+ "{N:/too many neighbour entries}\n");
+ p_5(icp6s_overflowredirect, "\t{:too-many-redirects/%ju} "
+ "{N:/too many redirects}\n");
xo_close_container("errors");
p(icp6s_pmtuchg, "\t{:path-mtu-changes/%ju} {N:/path MTU change%s}\n");
#undef p

File Metadata

Mime Type
text/plain
Expires
Wed, Nov 20, 2:41 AM (19 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14728317
Default Alt Text
D22447.diff (13 KB)

Event Timeline