Module Name:    src
Committed By:   jdc
Date:           Mon Jul  8 07:40:07 UTC 2013

Modified Files:
        src/share/man/man7 [netbsd-6]: sysctl.7
        src/sys/netinet6 [netbsd-6]: 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:
Pull up revisions:
  src/share/man/man7/sysctl.7 revision 1.73 via patch
  src/sys/netinet6/icmp6.c revision 1.161 via patch
  src/sys/netinet6/in6.c revision 1.161 via patch
  src/sys/netinet6/in6_proto.c revision 1.97 via patch
  src/sys/netinet6/in6_var.h revision 1.65 via patch
  src/sys/netinet6/ip6_input.c revision 1.139 via patch
  src/sys/netinet6/ip6_var.h revision 1.59 via patch
  src/sys/netinet6/nd6.c revision 1.143 via patch
  src/sys/netinet6/nd6.h revision 1.57 via patch
  src/sys/netinet6/nd6_rtr.c revision 1.83 via patch
(requested by christos in ticket #905).
Patch by Loganaden Velvindron.

  4 new sysctls to avoid ipv6 DoS attacks from OpenBSD


To generate a diff of this commit:
cvs rdiff -u -r1.68 -r1.68.2.1 src/share/man/man7/sysctl.7
cvs rdiff -u -r1.159 -r1.159.2.1 src/sys/netinet6/icmp6.c
cvs rdiff -u -r1.159 -r1.159.4.1 src/sys/netinet6/in6.c
cvs rdiff -u -r1.95 -r1.95.2.1 src/sys/netinet6/in6_proto.c
cvs rdiff -u -r1.64.20.1 -r1.64.20.2 src/sys/netinet6/in6_var.h
cvs rdiff -u -r1.136 -r1.136.2.1 src/sys/netinet6/ip6_input.c
cvs rdiff -u -r1.58 -r1.58.2.1 src/sys/netinet6/ip6_var.h
cvs rdiff -u -r1.141 -r1.141.2.1 src/sys/netinet6/nd6.c
cvs rdiff -u -r1.56 -r1.56.4.1 src/sys/netinet6/nd6.h
cvs rdiff -u -r1.82 -r1.82.4.1 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/share/man/man7/sysctl.7
diff -u src/share/man/man7/sysctl.7:1.68 src/share/man/man7/sysctl.7:1.68.2.1
--- src/share/man/man7/sysctl.7:1.68	Thu Nov  3 00:29:00 2011
+++ src/share/man/man7/sysctl.7	Mon Jul  8 07:40:07 2013
@@ -1,4 +1,4 @@
-.\"	$NetBSD: sysctl.7,v 1.68 2011/11/03 00:29:00 jym Exp $
+.\"	$NetBSD: sysctl.7,v 1.68.2.1 2013/07/08 07:40:07 jdc Exp $
 .\"
 .\" Copyright (c) 1993
 .\"	The Regents of the University of California.  All rights reserved.
@@ -29,7 +29,7 @@
 .\"
 .\"	@(#)sysctl.3	8.4 (Berkeley) 5/9/95
 .\"
-.Dd September 24, 2011
+.Dd June 22, 2012
 .Dt SYSCTL 7
 .Os
 .Sh NAME
@@ -1212,8 +1212,12 @@ The currently defined protocols and name
 .It ip	hostzerobroadcast	integer	yes
 .It ip	lowportmin	integer	yes
 .It ip	lowportmax	integer	yes
+.It ip6	maxdynroutes	integer	yes
+.It ip6	maxifprefixes	integer	yes
+.It ip6	maxifdefrouters	integer	yes
 .It ip	maxflows	integer	yes
 .It ip	maxfragpackets	integer	yes
+.It ip6	neighborgcthresh	integer	yes
 .It ip	mtudisc	integer	yes
 .It ip	mtudisctimeout	integer	yes
 .It ip	random_id	integer	yes
@@ -1687,6 +1691,18 @@ The lowest port number to use for TCP an
 This cannot be set to less than 0 or greater than 1024, and must
 be smaller than
 .Li ip6.lowportmax .
+.It Li ip6.maxdynroutes
+Maximum number of routes created by redirect.
+Set it to negative to disable.
+The default value is 4096.
+.It Li ip6.maxifprefixes
+Maximum number of prefixes created by route advertisements per interface.
+Set it to negative to disable.
+The default value is 16.
+.It Li ip6.maxifdefrouters 16
+Maximum number of default routers created by route advertisements per interface.
+Set it to negative to disable.
+The default value is 16.
 .It Li ip6.maxflows
 IPv6 Fast Forwarding is enabled by default.
 If set to 0, IPv6 Fast Forwarding is disabled.
@@ -1703,6 +1719,10 @@ The maximum number of fragments the node
 0 means that the node will not accept any fragments.
 \-1 means that the node will accept as many fragments as it receives.
 The flag is provided basically for avoiding possible DoS attacks.
+.It Li ip6.neighborgcthresh
+Maximum number of entries in neighbor cache.
+Set to negative to disable.
+The default value is 2048.
 .It Li ip6.redirect
 If set to 1, ICMPv6 redirects may be sent by the node.
 This option is ignored unless the node is routing IP packets,

Index: src/sys/netinet6/icmp6.c
diff -u src/sys/netinet6/icmp6.c:1.159 src/sys/netinet6/icmp6.c:1.159.2.1
--- src/sys/netinet6/icmp6.c:1.159	Sat Dec 31 20:41:59 2011
+++ src/sys/netinet6/icmp6.c	Mon Jul  8 07:40:07 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: icmp6.c,v 1.159 2011/12/31 20:41:59 christos Exp $	*/
+/*	$NetBSD: icmp6.c,v 1.159.2.1 2013/07/08 07:40:07 jdc 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.159 2011/12/31 20:41:59 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: icmp6.c,v 1.159.2.1 2013/07/08 07:40:07 jdc Exp $");
 
 #include "opt_inet.h"
 #include "opt_ipsec.h"
@@ -2284,6 +2284,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.159 src/sys/netinet6/in6.c:1.159.4.1
--- src/sys/netinet6/in6.c:1.159	Sat Nov 19 22:51:26 2011
+++ src/sys/netinet6/in6.c	Mon Jul  8 07:40:07 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: in6.c,v 1.159 2011/11/19 22:51:26 tls Exp $	*/
+/*	$NetBSD: in6.c,v 1.159.4.1 2013/07/08 07:40:07 jdc 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.159 2011/11/19 22:51:26 tls Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.159.4.1 2013/07/08 07:40:07 jdc Exp $");
 
 #include "opt_inet.h"
 #include "opt_pfil_hooks.h"
@@ -2281,6 +2281,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.95 src/sys/netinet6/in6_proto.c:1.95.2.1
--- src/sys/netinet6/in6_proto.c:1.95	Sat Dec 31 20:41:59 2011
+++ src/sys/netinet6/in6_proto.c	Mon Jul  8 07:40:07 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: in6_proto.c,v 1.95 2011/12/31 20:41:59 christos Exp $	*/
+/*	$NetBSD: in6_proto.c,v 1.95.2.1 2013/07/08 07:40:07 jdc 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.95 2011/12/31 20:41:59 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6_proto.c,v 1.95.2.1 2013/07/08 07:40:07 jdc Exp $");
 
 #include "opt_gateway.h"
 #include "opt_inet.h"
@@ -475,6 +475,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.20.1 src/sys/netinet6/in6_var.h:1.64.20.2
--- src/sys/netinet6/in6_var.h:1.64.20.1	Wed Oct 31 16:07:46 2012
+++ src/sys/netinet6/in6_var.h	Mon Jul  8 07:40:07 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: in6_var.h,v 1.64.20.1 2012/10/31 16:07:46 riz Exp $	*/
+/*	$NetBSD: in6_var.h,v 1.64.20.2 2013/07/08 07:40:07 jdc 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.136 src/sys/netinet6/ip6_input.c:1.136.2.1
--- src/sys/netinet6/ip6_input.c:1.136	Tue Jan 10 20:01:56 2012
+++ src/sys/netinet6/ip6_input.c	Mon Jul  8 07:40:07 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip6_input.c,v 1.136 2012/01/10 20:01:56 drochner Exp $	*/
+/*	$NetBSD: ip6_input.c,v 1.136.2.1 2013/07/08 07:40:07 jdc 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.136 2012/01/10 20:01:56 drochner Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip6_input.c,v 1.136.2.1 2013/07/08 07:40:07 jdc Exp $");
 
 #include "opt_gateway.h"
 #include "opt_inet.h"
@@ -1989,6 +1989,38 @@ sysctl_net_inet6_ip6_setup(struct sysctl
 			CTL_NET, PF_INET6, IPPROTO_IPV6,
 			CTL_CREATE, CTL_EOL);
 #endif
+	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.58.2.1
--- src/sys/netinet6/ip6_var.h:1.58	Thu Jan 19 13:19:34 2012
+++ src/sys/netinet6/ip6_var.h	Mon Jul  8 07:40:07 2013
@@ -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.58.2.1 2013/07/08 07:40:07 jdc Exp $	*/
 /*	$KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $	*/
 
 /*
@@ -272,6 +272,10 @@ 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.141 src/sys/netinet6/nd6.c:1.141.2.1
--- src/sys/netinet6/nd6.c:1.141	Fri Feb  3 03:32:45 2012
+++ src/sys/netinet6/nd6.c	Mon Jul  8 07:40:07 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: nd6.c,v 1.141 2012/02/03 03:32:45 christos Exp $	*/
+/*	$NetBSD: nd6.c,v 1.141.2.1 2013/07/08 07:40:07 jdc 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.141 2012/02/03 03:32:45 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.141.2.1 2013/07/08 07:40:07 jdc Exp $");
 
 #include "opt_ipsec.h"
 
@@ -135,6 +135,17 @@ 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)
 {
@@ -476,7 +487,7 @@ nd6_llinfo_timer(void *arg)
 			nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
 		}
 		break;
-
+	case ND6_LLINFO_PURGE:
 	case ND6_LLINFO_STALE:
 		/* Garbage Collection(RFC 2461 5.3) */
 		if (!ND6_LLINFO_PERMANENT(ln)) {
@@ -1336,6 +1347,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
@@ -2047,6 +2087,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.56.4.1
--- src/sys/netinet6/nd6.h:1.56	Sat Nov 19 22:51:29 2011
+++ src/sys/netinet6/nd6.h	Mon Jul  8 07:40:07 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: nd6.h,v 1.56 2011/11/19 22:51:29 tls Exp $	*/
+/*	$NetBSD: nd6.h,v 1.56.4.1 2013/07/08 07:40:07 jdc 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.82.4.1
--- src/sys/netinet6/nd6_rtr.c:1.82	Sat Nov 19 22:51:29 2011
+++ src/sys/netinet6/nd6_rtr.c	Mon Jul  8 07:40:07 2013
@@ -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.82.4.1 2013/07/08 07:40:07 jdc 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.82.4.1 2013/07/08 07:40:07 jdc 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,44 @@ 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 +937,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);
@@ -929,6 +992,7 @@ 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);

Reply via email to