Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102949564
D22447.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D22447.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D22447: Implement NDP resource limits.
Attached
Detach File
Event Timeline
Log In to Comment