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);

Reply via email to