Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 1 Jul 2013 13:30:42 -0700
From:      Loganaden Velvindron <logan@elandsys.com>
To:        freebsd-net@freebsd.org
Cc:        bz@freebsd.org
Subject:   Re: kern/157410: [ip6] IPv6 Router Advertisements Cause Excessive CPU Use
Message-ID:  <20130701203042.GA13730@mx.elandsys.com>
In-Reply-To: <20130701195823.GA207@mx.elandsys.com>
References:  <20130701195823.GA207@mx.elandsys.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On Mon, Jul 01, 2013 at 12:58:23PM -0700, Loganaden Velvindron wrote:
> Hi I came across this old PR. It appears that it's not fixed in -current.
> 
> I attempted to port the diff to our FreeBSD 9.1 release machines which
> have IPv6 connectivity and are affected by RA flooding.
> 
> I can report that it mitigates RA_flooding.
> 
> Feedback welcomed. I'd be happy to polish it so that it can
> make it to 9.2 and 10.0 :-)
> 
> I broke down the diffs into separate ones.
> 
> 

The last diff (nd6_rtr.diff) was garbled and another diff was missing. 
I'm resending the whole patchset.

--- in6.c.orig	2013-06-30 23:07:46.000000000 +0400
+++ in6.c	2013-07-01 19:20:15.000000000 +0400
@@ -2694,6 +2694,8 @@ in6_domifattach(struct ifnet *ifp)
 	ext->nd_ifinfo = nd6_ifattach(ifp);
 	ext->scope6_id = scope6_ifattach(ifp);
 	ext->lltable = lltable_init(ifp, AF_INET6);
+	ext->nprefixes = 0;
+	ext->ndefrouters = 0;
 	if (ext->lltable != NULL) {
 		ext->lltable->llt_free = in6_lltable_free;
 		ext->lltable->llt_prefix_free = in6_lltable_prefix_free;

--- in6_proto.c.orig	2013-06-30 23:07:58.000000000 +0400
+++ in6_proto.c	2013-07-01 21:05:08.000000000 +0400
@@ -413,7 +413,8 @@ VNET_DEFINE(int, ip6_rr_prune) = 5;	/* r
 					 * walk list every 5 sec. */
 VNET_DEFINE(int, ip6_mcast_pmtu) = 0;	/* enable pMTU discovery for multicast? */
 VNET_DEFINE(int, ip6_v6only) = 1;
-
+VNET_DEFINE(int, ip6_maxifprefixes) = 16;
+VNET_DEFINE(int, ip6_maxifdefrouters) = 16;
 VNET_DEFINE(int, ip6_keepfaith) = 0;
 VNET_DEFINE(time_t, ip6_log_time) = (time_t)0L;
 #ifdef IPSTEALTH
@@ -524,6 +525,10 @@ SYSCTL_VNET_STRUCT(_net_inet6_ip6, IPV6C
 	&VNET_NAME(ip6stat), ip6stat, "");
 SYSCTL_VNET_INT(_net_inet6_ip6, IPV6CTL_MAXFRAGPACKETS, maxfragpackets,
 	CTLFLAG_RW, &VNET_NAME(ip6_maxfragpackets), 0, "");
+SYSCTL_VNET_INT(_net_inet6_ip6, IPV6CTL_MAXIFPREFIXES, maxifprefixes,
+        CTLFLAG_RW, &VNET_NAME(ip6_maxifprefixes), 0, "");
+SYSCTL_VNET_INT(_net_inet6_ip6, IPV6CTL_MAXIFDEFROUTERS, maxifdefrouters,
+        CTLFLAG_RW, &VNET_NAME(ip6_maxifdefrouters), 0, "");
 SYSCTL_VNET_INT(_net_inet6_ip6, IPV6CTL_ACCEPT_RTADV, accept_rtadv,
 	CTLFLAG_RW, &VNET_NAME(ip6_accept_rtadv), 0,
 	"Default value of per-interface flag for accepting ICMPv6 Router"

--- in6_var.h.orig	2013-06-30 23:08:28.000000000 +0400
+++ in6_var.h	2013-07-01 22:38:03.000000000 +0400
@@ -104,6 +104,8 @@ struct in6_ifextra {
 	struct scope6_id *scope6_id;
 	struct lltable *lltable;
 	struct mld_ifinfo *mld_ifinfo;
+	int nprefixes;
+	int ndefrouters;
 };
 
 #define	LLTABLE6(ifp)	(((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->lltable)

--- ip6_var.h.orig	2013-06-30 23:09:22.000000000 +0400
+++ ip6_var.h	2013-07-01 20:28:30.000000000 +0400
@@ -315,6 +315,8 @@ VNET_DECLARE(int, ip6_maxfragpackets);	/
 					 * queue */
 VNET_DECLARE(int, ip6_maxfrags);	/* Maximum fragments in reassembly
 					 * queue */
+VNET_DECLARE(int, ip6_maxifprefixes);
+VNET_DECLARE(int, ip6_maxifdefrouters);
 VNET_DECLARE(int, ip6_accept_rtadv);	/* Acts as a host not a router */
 VNET_DECLARE(int, ip6_no_radr);		/* No defroute from RA */
 VNET_DECLARE(int, ip6_norbit_raif);	/* Disable R-bit in NA on RA

--- nd6.h.orig	2013-06-30 23:09:42.000000000 +0400
+++ nd6.h	2013-07-01 22:16:09.000000000 +0400
@@ -277,6 +277,7 @@ struct nd_prefix {
 	u_char	ndpr_plen;
 	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

--- in6.h.orig	2013-07-02 00:24:36.000000000 +0400
+++ in6.h	2013-07-01 21:58:04.000000000 +0400
@@ -607,7 +607,6 @@ struct ip6_mtuinfo {
 #define IPV6CTL_ISATAPRTR	43	/* isatap router */
 #endif
 #define IPV6CTL_MCAST_PMTU	44	/* enable pMTU discovery for multicast? */
-
 /* New entries should be added here from current IPV6CTL_MAXID value. */
 /* to define items, should talk with KAME guys first, for *BSD compatibility */
 #define IPV6CTL_STEALTH		45
@@ -619,6 +618,9 @@ struct ip6_mtuinfo {
 #define	IPV6CTL_RFC6204W3	50	/* Accept defroute even when forwarding
 					   enabled */
 #define	IPV6CTL_MAXID		51
+#define IPV6CTL_MAXIFPREFIXES   52
+#define IPV6CTL_MAXIFDEFROUTERS 53
+
 #endif /* __BSD_VISIBLE */
 
 /*

--- nd6_rtr.c.orig	2013-06-30 23:10:24.000000000 +0400
+++ nd6_rtr.c	2013-07-01 22:40:29.000000000 +0400
@@ -83,6 +83,7 @@ static void nd6_rtmsg(int, struct rtentr
 static int in6_init_prefix_ltimes(struct nd_prefix *);
 static void in6_init_address_ltimes __P((struct nd_prefix *,
 	struct in6_addrlifetime *));
+static void purge_detached(struct ifnet *);
 
 static int nd6_prefix_onlink(struct nd_prefix *);
 static int nd6_prefix_offlink(struct nd_prefix *);
@@ -565,6 +566,7 @@ defrtrlist_del(struct nd_defrouter *dr)
 {
 	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
@@ -597,6 +599,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);
 }
 
@@ -734,6 +742,7 @@ static struct nd_defrouter *
 defrtrlist_update(struct nd_defrouter *new)
 {
 	struct nd_defrouter *dr, *n;
+	struct in6_ifextra *ext = new->ifp->if_afdata[AF_INET6];
 	int s = splnet();
 
 	if ((dr = defrouter_lookup(&new->rtaddr, new->ifp)) != NULL) {
@@ -775,6 +784,12 @@ defrtrlist_update(struct nd_defrouter *n
 		splx(s);
 		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) {
@@ -810,6 +825,8 @@ insert:
 
 	defrouter_select();
 
+	ext->ndefrouters++;
+
 	splx(s);
 
 	return (n);
@@ -868,6 +885,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)
@@ -876,6 +931,14 @@ nd6_prelist_add(struct nd_prefixctl *pr,
 	int error = 0;
 	int i, s;
 	char ip6buf[INET6_ADDRSTRLEN];
+	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;
+	}
 
 	new = (struct nd_prefix *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT);
 	if (new == NULL)
@@ -923,7 +986,7 @@ nd6_prelist_add(struct nd_prefixctl *pr,
 
 	if (dr)
 		pfxrtr_add(new, dr);
-
+	ext->nprefixes++;
 	return 0;
 }
 
@@ -933,6 +996,7 @@ prelist_remove(struct nd_prefix *pr)
 	struct nd_pfxrouter *pfr, *next;
 	int e, s;
 	char ip6buf[INET6_ADDRSTRLEN];
+	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;
@@ -965,6 +1029,12 @@ prelist_remove(struct nd_prefix *pr)
 	LIST_FOREACH_SAFE(pfr, &pr->ndpr_advrtrs, pfr_entry, next) {
 		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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20130701203042.GA13730>