Module Name: src
Committed By: christos
Date: Sat Jun 23 03:14:04 UTC 2012
Modified Files:
src/sys/netinet6: icmp6.c in6.c in6_proto.c in6_var.h ip6_input.c
ip6_var.h nd6.c nd6.h nd6_rtr.c
Log Message:
4 new sysctls to avoid ipv6 DoS attacks from OpenBSD
To generate a diff of this commit:
cvs rdiff -u -r1.160 -r1.161 src/sys/netinet6/icmp6.c src/sys/netinet6/in6.c
cvs rdiff -u -r1.96 -r1.97 src/sys/netinet6/in6_proto.c
cvs rdiff -u -r1.64 -r1.65 src/sys/netinet6/in6_var.h
cvs rdiff -u -r1.138 -r1.139 src/sys/netinet6/ip6_input.c
cvs rdiff -u -r1.58 -r1.59 src/sys/netinet6/ip6_var.h
cvs rdiff -u -r1.142 -r1.143 src/sys/netinet6/nd6.c
cvs rdiff -u -r1.56 -r1.57 src/sys/netinet6/nd6.h
cvs rdiff -u -r1.82 -r1.83 src/sys/netinet6/nd6_rtr.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/netinet6/icmp6.c
diff -u src/sys/netinet6/icmp6.c:1.160 src/sys/netinet6/icmp6.c:1.161
--- src/sys/netinet6/icmp6.c:1.160 Thu Mar 22 16:34:40 2012
+++ src/sys/netinet6/icmp6.c Fri Jun 22 23:14:03 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: icmp6.c,v 1.160 2012/03/22 20:34:40 drochner Exp $ */
+/* $NetBSD: icmp6.c,v 1.161 2012/06/23 03:14:03 christos Exp $ */
/* $KAME: icmp6.c,v 1.217 2001/06/20 15:03:29 jinmei Exp $ */
/*
@@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: icmp6.c,v 1.160 2012/03/22 20:34:40 drochner Exp $");
+__KERNEL_RCSID(0, "$NetBSD: icmp6.c,v 1.161 2012/06/23 03:14:03 christos Exp $");
#include "opt_inet.h"
#include "opt_ipsec.h"
@@ -2279,6 +2279,8 @@ icmp6_redirect_input(struct mbuf *m, int
* (there will be additional hops, though).
*/
rtcount = rt_timer_count(icmp6_redirect_timeout_q);
+ if (0 <= ip6_maxdynroutes && rtcount >= ip6_maxdynroutes)
+ goto freeit;
if (0 <= icmp6_redirect_hiwat && rtcount > icmp6_redirect_hiwat)
return;
else if (0 <= icmp6_redirect_lowat &&
Index: src/sys/netinet6/in6.c
diff -u src/sys/netinet6/in6.c:1.160 src/sys/netinet6/in6.c:1.161
--- src/sys/netinet6/in6.c:1.160 Tue Mar 13 14:40:59 2012
+++ src/sys/netinet6/in6.c Fri Jun 22 23:14:03 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: in6.c,v 1.160 2012/03/13 18:40:59 elad Exp $ */
+/* $NetBSD: in6.c,v 1.161 2012/06/23 03:14:03 christos Exp $ */
/* $KAME: in6.c,v 1.198 2001/07/18 09:12:38 itojun Exp $ */
/*
@@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.160 2012/03/13 18:40:59 elad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.161 2012/06/23 03:14:03 christos Exp $");
#include "opt_inet.h"
#include "opt_pfil_hooks.h"
@@ -2283,6 +2283,8 @@ in6_domifattach(struct ifnet *ifp)
ext->nd_ifinfo = nd6_ifattach(ifp);
ext->scope6_id = scope6_ifattach(ifp);
+ ext->nprefixes = 0;
+ ext->ndefrouters = 0;
return ext;
}
Index: src/sys/netinet6/in6_proto.c
diff -u src/sys/netinet6/in6_proto.c:1.96 src/sys/netinet6/in6_proto.c:1.97
--- src/sys/netinet6/in6_proto.c:1.96 Thu Mar 22 16:34:40 2012
+++ src/sys/netinet6/in6_proto.c Fri Jun 22 23:14:03 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: in6_proto.c,v 1.96 2012/03/22 20:34:40 drochner Exp $ */
+/* $NetBSD: in6_proto.c,v 1.97 2012/06/23 03:14:03 christos Exp $ */
/* $KAME: in6_proto.c,v 1.66 2000/10/10 15:35:47 itojun Exp $ */
/*
@@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6_proto.c,v 1.96 2012/03/22 20:34:40 drochner Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6_proto.c,v 1.97 2012/06/23 03:14:03 christos Exp $");
#include "opt_gateway.h"
#include "opt_inet.h"
@@ -439,6 +439,10 @@ int ip6_rr_prune = 5; /* router renumber
* walk list every 5 sec. */
int ip6_mcast_pmtu = 0; /* enable pMTU discovery for multicast? */
int ip6_v6only = 1;
+int ip6_neighborgcthresh = 2048; /* Threshold # of NDP entries for GC */
+int ip6_maxifprefixes = 16; /* Max acceptable prefixes via RA per IF */
+int ip6_maxifdefrouters = 16; /* Max acceptable def routers via RA */
+int ip6_maxdynroutes = 4096; /* Max # of routes created via redirect */
int ip6_keepfaith = 0;
time_t ip6_log_time = 0;
Index: src/sys/netinet6/in6_var.h
diff -u src/sys/netinet6/in6_var.h:1.64 src/sys/netinet6/in6_var.h:1.65
--- src/sys/netinet6/in6_var.h:1.64 Thu Jan 15 18:22:15 2009
+++ src/sys/netinet6/in6_var.h Fri Jun 22 23:14:03 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: in6_var.h,v 1.64 2009/01/15 23:22:15 christos Exp $ */
+/* $NetBSD: in6_var.h,v 1.65 2012/06/23 03:14:03 christos Exp $ */
/* $KAME: in6_var.h,v 1.81 2002/06/08 11:16:51 itojun Exp $ */
/*
@@ -94,6 +94,8 @@ struct in6_ifextra {
struct icmp6_ifstat *icmp6_ifstat;
struct nd_ifinfo *nd_ifinfo;
struct scope6_id *scope6_id;
+ int nprefixes;
+ int ndefrouters;
};
struct in6_ifaddr {
Index: src/sys/netinet6/ip6_input.c
diff -u src/sys/netinet6/ip6_input.c:1.138 src/sys/netinet6/ip6_input.c:1.139
--- src/sys/netinet6/ip6_input.c:1.138 Fri Jun 22 10:54:35 2012
+++ src/sys/netinet6/ip6_input.c Fri Jun 22 23:14:04 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: ip6_input.c,v 1.138 2012/06/22 14:54:35 christos Exp $ */
+/* $NetBSD: ip6_input.c,v 1.139 2012/06/23 03:14:04 christos Exp $ */
/* $KAME: ip6_input.c,v 1.188 2001/03/29 05:34:31 itojun Exp $ */
/*
@@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ip6_input.c,v 1.138 2012/06/22 14:54:35 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip6_input.c,v 1.139 2012/06/23 03:14:04 christos Exp $");
#include "opt_gateway.h"
#include "opt_inet.h"
@@ -1977,6 +1977,38 @@ sysctl_net_inet6_ip6_setup(struct sysctl
SYSCTL_DESCR("selected algorithm"),
sysctl_rfc6056_selected6, 0, NULL, RFC6056_MAXLEN,
CTL_CREATE, CTL_EOL);
+ sysctl_createv(clog, 0, NULL, NULL,
+ CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+ CTLTYPE_INT, "neighborgcthresh",
+ SYSCTL_DESCR("Maximum number of entries in neighbor"
+ " cache"),
+ NULL, 1, &ip6_neighborgcthresh, 0,
+ CTL_NET, PF_INET6, IPPROTO_IPV6,
+ CTL_CREATE, CTL_EOL);
+ sysctl_createv(clog, 0, NULL, NULL,
+ CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+ CTLTYPE_INT, "maxifprefixes",
+ SYSCTL_DESCR("Maximum number of prefixes created by"
+ " route advertisement per interface"),
+ NULL, 1, &ip6_maxifprefixes, 0,
+ CTL_NET, PF_INET6, IPPROTO_IPV6,
+ CTL_CREATE, CTL_EOL);
+ sysctl_createv(clog, 0, NULL, NULL,
+ CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+ CTLTYPE_INT, "maxifdefrouters",
+ SYSCTL_DESCR("Maximum number of default routers created"
+ " by route advertisement per interface"),
+ NULL, 1, &ip6_maxifdefrouters, 0,
+ CTL_NET, PF_INET6, IPPROTO_IPV6,
+ CTL_CREATE, CTL_EOL);
+ sysctl_createv(clog, 0, NULL, NULL,
+ CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+ CTLTYPE_INT, "maxdynroutes",
+ SYSCTL_DESCR("Maximum number of routes created via"
+ " redirect"),
+ NULL, 1, &ip6_maxdynroutes, 0,
+ CTL_NET, PF_INET6, IPPROTO_IPV6,
+ CTL_CREATE, CTL_EOL);
}
void
Index: src/sys/netinet6/ip6_var.h
diff -u src/sys/netinet6/ip6_var.h:1.58 src/sys/netinet6/ip6_var.h:1.59
--- src/sys/netinet6/ip6_var.h:1.58 Thu Jan 19 08:19:34 2012
+++ src/sys/netinet6/ip6_var.h Fri Jun 22 23:14:04 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: ip6_var.h,v 1.58 2012/01/19 13:19:34 liamjfoy Exp $ */
+/* $NetBSD: ip6_var.h,v 1.59 2012/06/23 03:14:04 christos Exp $ */
/* $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $ */
/*
@@ -272,6 +272,11 @@ extern int ip6_rr_prune; /* router renu
* walk list every 5 sec. */
extern int ip6_mcast_pmtu; /* enable pMTU discovery for multicast? */
extern int ip6_v6only;
+extern int ip6_neighborgcthresh; /* Threshold # of NDP entries for GC */
+extern int ip6_maxifprefixes; /* Max acceptable prefixes via RA per IF */
+extern int ip6_maxifdefrouters; /* Max acceptable def routers via RA */
+extern int ip6_maxdynroutes; /* Max # of routes created via redirect */
+
extern struct socket *ip6_mrouter; /* multicast routing daemon */
extern int ip6_sendredirects; /* send IP redirects when forwarding? */
Index: src/sys/netinet6/nd6.c
diff -u src/sys/netinet6/nd6.c:1.142 src/sys/netinet6/nd6.c:1.143
--- src/sys/netinet6/nd6.c:1.142 Thu Mar 22 16:34:41 2012
+++ src/sys/netinet6/nd6.c Fri Jun 22 23:14:04 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: nd6.c,v 1.142 2012/03/22 20:34:41 drochner Exp $ */
+/* $NetBSD: nd6.c,v 1.143 2012/06/23 03:14:04 christos Exp $ */
/* $KAME: nd6.c,v 1.279 2002/06/08 11:16:51 itojun Exp $ */
/*
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.142 2012/03/22 20:34:41 drochner Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.143 2012/06/23 03:14:04 christos Exp $");
#include "opt_ipsec.h"
@@ -131,6 +131,16 @@ static int fill_prlist(void *, size_t *,
MALLOC_DEFINE(M_IP6NDP, "NDP", "IPv6 Neighbour Discovery");
+#define LN_DEQUEUE(ln) do { \
+ (ln)->ln_next->ln_prev = (ln)->ln_prev; \
+ (ln)->ln_prev->ln_next = (ln)->ln_next; \
+ } while (/*CONSTCOND*/0)
+#define LN_INSERTHEAD(ln) do { \
+ (ln)->ln_next = llinfo_nd6.ln_next; \
+ llinfo_nd6.ln_next = (ln); \
+ (ln)->ln_prev = &llinfo_nd6; \
+ (ln)->ln_next->ln_prev = (ln); \
+ } while (/*CONSTCOND*/0)
void
nd6_init(void)
{
@@ -473,6 +483,7 @@ nd6_llinfo_timer(void *arg)
}
break;
+ case ND6_LLINFO_PURGE:
case ND6_LLINFO_STALE:
/* Garbage Collection(RFC 2461 5.3) */
if (!ND6_LLINFO_PERMANENT(ln)) {
@@ -1332,6 +1343,35 @@ nd6_rtrequest(int req, struct rtentry *r
ln->ln_prev = &llinfo_nd6;
ln->ln_next->ln_prev = ln;
+ /*
+ * If we have too many cache entries, initiate immediate
+ * purging for some "less recently used" entries. Note that
+ * we cannot directly call nd6_free() here because it would
+ * cause re-entering rtable related routines triggering an LOR
+ * problem for FreeBSD.
+ */
+ if (ip6_neighborgcthresh >= 0 &&
+ nd6_inuse >= ip6_neighborgcthresh) {
+ int i;
+
+ for (i = 0; i < 10 && llinfo_nd6.ln_prev != ln; i++) {
+ struct llinfo_nd6 *ln_end = llinfo_nd6.ln_prev;
+
+ /* Move this entry to the head */
+ LN_DEQUEUE(ln_end);
+ LN_INSERTHEAD(ln_end);
+
+ if (ND6_LLINFO_PERMANENT(ln_end))
+ continue;
+
+ if (ln_end->ln_state > ND6_LLINFO_INCOMPLETE)
+ ln_end->ln_state = ND6_LLINFO_STALE;
+ else
+ ln_end->ln_state = ND6_LLINFO_PURGE;
+ nd6_llinfo_settimer(ln_end, 0);
+ }
+ }
+
RT_DPRINTF("rt->_rt_key = %p\n", (void *)rt->_rt_key);
/*
* check if rt_getkey(rt) is an address assigned
@@ -2043,6 +2083,14 @@ nd6_output(struct ifnet *ifp, struct ifn
goto sendpkt; /* send anyway */
}
+ /*
+ * Move this entry to the head of the queue so that it is less likely
+ * for this entry to be a target of forced garbage collection (see
+ * nd6_rtrequest()).
+ */
+ LN_DEQUEUE(ln);
+ LN_INSERTHEAD(ln);
+
/* We don't have to do link-layer address resolution on a p2p link. */
if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
ln->ln_state < ND6_LLINFO_REACHABLE) {
Index: src/sys/netinet6/nd6.h
diff -u src/sys/netinet6/nd6.h:1.56 src/sys/netinet6/nd6.h:1.57
--- src/sys/netinet6/nd6.h:1.56 Sat Nov 19 17:51:29 2011
+++ src/sys/netinet6/nd6.h Fri Jun 22 23:14:04 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: nd6.h,v 1.56 2011/11/19 22:51:29 tls Exp $ */
+/* $NetBSD: nd6.h,v 1.57 2012/06/23 03:14:04 christos Exp $ */
/* $KAME: nd6.h,v 1.95 2002/06/08 11:31:06 itojun Exp $ */
/*
@@ -51,6 +51,7 @@ struct llinfo_nd6 {
struct callout ln_timer_ch;
};
+#define ND6_LLINFO_PURGE -3
#define ND6_LLINFO_NOSTATE -2
/*
* We don't need the WAITDELETE state any more, but we keep the definition
@@ -303,6 +304,8 @@ struct nd_prefix {
int ndpr_refcnt; /* reference couter from addresses */
};
+#define ndpr_next ndpr_entry.le_next
+
#define ndpr_raf ndpr_flags
#define ndpr_raf_onlink ndpr_flags.onlink
#define ndpr_raf_auto ndpr_flags.autonomous
Index: src/sys/netinet6/nd6_rtr.c
diff -u src/sys/netinet6/nd6_rtr.c:1.82 src/sys/netinet6/nd6_rtr.c:1.83
--- src/sys/netinet6/nd6_rtr.c:1.82 Sat Nov 19 17:51:29 2011
+++ src/sys/netinet6/nd6_rtr.c Fri Jun 22 23:14:04 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: nd6_rtr.c,v 1.82 2011/11/19 22:51:29 tls Exp $ */
+/* $NetBSD: nd6_rtr.c,v 1.83 2012/06/23 03:14:04 christos Exp $ */
/* $KAME: nd6_rtr.c,v 1.95 2001/02/07 08:09:47 itojun Exp $ */
/*
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nd6_rtr.c,v 1.82 2011/11/19 22:51:29 tls Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6_rtr.c,v 1.83 2012/06/23 03:14:04 christos Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -79,8 +79,9 @@ static void defrouter_delreq(struct nd_d
static void nd6_rtmsg(int, struct rtentry *);
static int in6_init_prefix_ltimes(struct nd_prefix *);
-static void in6_init_address_ltimes(struct nd_prefix *ndpr,
- struct in6_addrlifetime *lt6);
+static void in6_init_address_ltimes(struct nd_prefix *,
+ struct in6_addrlifetime *);
+static void purge_detached(struct ifnet *);
static int rt6_deleteroute(struct rtentry *, void *);
@@ -488,6 +489,7 @@ defrtrlist_del(struct nd_defrouter *dr)
struct nd_ifinfo *ndi = ND_IFINFO(dr->ifp);
struct nd_defrouter *deldr = NULL;
struct nd_prefix *pr;
+ struct in6_ifextra *ext = dr->ifp->if_afdata[AF_INET6];
/*
* Flush all the routing table entries that use the router
@@ -521,6 +523,12 @@ defrtrlist_del(struct nd_defrouter *dr)
if (deldr)
defrouter_select();
+ ext->ndefrouters--;
+ if (ext->ndefrouters < 0) {
+ log(LOG_WARNING, "defrtrlist_del: negative count on %s\n",
+ dr->ifp->if_xname);
+ }
+
free(dr, M_IP6NDP);
}
@@ -777,6 +785,13 @@ defrtrlist_update(struct nd_defrouter *n
return (dr);
}
+ struct in6_ifextra *ext = new->ifp->if_afdata[AF_INET6];
+ if (ip6_maxifdefrouters >= 0 &&
+ ext->ndefrouters >= ip6_maxifdefrouters) {
+ splx(s);
+ return (NULL);
+ }
+
/* entry does not exist */
if (new->rtlifetime == 0) {
splx(s);
@@ -818,6 +833,8 @@ insert:
defrouter_select();
+ ext->ndefrouters++;
+
splx(s);
return (n);
@@ -875,6 +892,43 @@ nd6_prefix_lookup(struct nd_prefixctl *k
return (search);
}
+static void
+purge_detached(struct ifnet *ifp)
+{
+ struct nd_prefix *pr, *pr_next;
+ struct in6_ifaddr *ia;
+ struct ifaddr *ifa, *ifa_next;
+
+ for (pr = nd_prefix.lh_first; pr; pr = pr_next) {
+ pr_next = pr->ndpr_next;
+
+ /*
+ * This function is called when we need to make more room for
+ * new prefixes rather than keeping old, possibly stale ones.
+ * Detached prefixes would be a good candidate; if all routers
+ * that advertised the prefix expired, the prefix is also
+ * probably stale.
+ */
+ if (pr->ndpr_ifp != ifp ||
+ IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) ||
+ ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 &&
+ !LIST_EMPTY(&pr->ndpr_advrtrs)))
+ continue;
+
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa_next) {
+ ifa_next = ifa->ifa_list.tqe_next;
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ ia = (struct in6_ifaddr *)ifa;
+ if ((ia->ia6_flags & IN6_IFF_AUTOCONF) ==
+ IN6_IFF_AUTOCONF && ia->ia6_ndpr == pr) {
+ in6_purgeaddr(ifa);
+ }
+ }
+ if (pr->ndpr_refcnt == 0)
+ prelist_remove(pr);
+ }
+}
int
nd6_prelist_add(struct nd_prefixctl *pr, struct nd_defrouter *dr,
struct nd_prefix **newp)
@@ -882,6 +936,14 @@ nd6_prelist_add(struct nd_prefixctl *pr,
struct nd_prefix *new = NULL;
int i, s;
int error;
+ struct in6_ifextra *ext = pr->ndpr_ifp->if_afdata[AF_INET6];
+
+ if (ip6_maxifprefixes >= 0) {
+ if (ext->nprefixes >= ip6_maxifprefixes / 2)
+ purge_detached(pr->ndpr_ifp);
+ if (ext->nprefixes >= ip6_maxifprefixes)
+ return ENOMEM;
+ }
error = 0;
new = malloc(sizeof(*new), M_IP6NDP, M_NOWAIT|M_ZERO);
@@ -930,6 +992,8 @@ nd6_prelist_add(struct nd_prefixctl *pr,
if (dr)
pfxrtr_add(new, dr);
+ ext->nprefixes++;
+
return 0;
}
@@ -938,6 +1002,7 @@ prelist_remove(struct nd_prefix *pr)
{
struct nd_pfxrouter *pfr, *next;
int e, s;
+ struct in6_ifextra *ext = pr->ndpr_ifp->if_afdata[AF_INET6];
/* make sure to invalidate the prefix until it is really freed. */
pr->ndpr_vltime = 0;
@@ -972,6 +1037,12 @@ prelist_remove(struct nd_prefix *pr)
free(pfr, M_IP6NDP);
}
+
+ ext->nprefixes--;
+ if (ext->nprefixes < 0) {
+ log(LOG_WARNING, "prelist_remove: negative count on %s\n",
+ pr->ndpr_ifp->if_xname);
+ }
splx(s);
free(pr, M_IP6NDP);