Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 6 Jun 2011 02:14:23 +0000 (UTC)
From:      Hiroki Sato <hrs@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r222728 - in head: sbin/ifconfig sys/netinet6
Message-ID:  <201106060214.p562EN5G007040@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hrs
Date: Mon Jun  6 02:14:23 2011
New Revision: 222728
URL: http://svn.freebsd.org/changeset/base/222728

Log:
  - Accept Router Advertisement messages even when net.inet6.ip6.forwarding=1.
  
  - A new per-interface knob IFF_ND6_NO_RADR and sysctl IPV6CTL_NO_RADR.
    This controls if accepting a route in an RA message as the default route.
    The default value for each interface can be set by net.inet6.ip6.no_radr.
    The system wide default value is 0.
  
  - A new sysctl: net.inet6.ip6.norbit_raif.  This controls if setting R-bit in
    NA on RA accepting interfaces.  The default is 0 (R-bit is set based on
    net.inet6.ip6.forwarding).
  
  Background:
  
   IPv6 host/router model suggests a router sends an RA and a host accepts it for
   router discovery.  Because of that, KAME implementation does not allow
   accepting RAs when net.inet6.ip6.forwarding=1.  Accepting RAs on a router can
   make the routing table confused since it can change the default router
   unintentionally.
  
   However, in practice there are cases where we cannot distinguish a host from
   a router clearly.  For example, a customer edge router often works as a host
   against the ISP, and as a router against the LAN at the same time.  Another
   example is a complex network configurations like an L2TP tunnel for IPv6
   connection to Internet over an Ethernet link with another native IPv6 subnet.
   In this case, the physical interface for the native IPv6 subnet works as a
   host, and the pseudo-interface for L2TP works as the default IP forwarding
   route.
  
  Problem:
  
   Disabling processing RA messages when net.inet6.ip6.forwarding=1 and
   accepting them when net.inet6.ip6.forward=0 cause the following practical
   issues:
  
   - A router cannot perform SLAAC.  It becomes a problem if a box has
     multiple interfaces and you want to use SLAAC on some of them, for
     example.  A customer edge router for IPv6 Internet access service
     using an IPv6-over-IPv6 tunnel sometimes needs SLAAC on the
     physical interface for administration purpose; updating firmware
     and so on (link-local addresses can be used there, but GUAs by
     SLAAC are often used for scalability).
  
   - When a host has multiple IPv6 interfaces and it receives multiple RAs on
     them, controlling the default route is difficult.  Router preferences
     defined in RFC 4191 works only when the routers on the links are
     under your control.
  
  Details of Implementation Changes:
  
   Router Advertisement messages will be accepted even when
   net.inet6.ip6.forwarding=1.  More precisely, the conditions are as
   follow:
  
   (ACCEPT_RTADV && !NO_RADR && !ip6.forwarding)
  	=> Normal RA processing on that interface. (as IPv6 host)
  
   (ACCEPT_RTADV && (NO_RADR || ip6.forwarding))
  	=> Accept RA but add the router to the defroute list with
  	   rtlifetime=0 unconditionally.  This effectively prevents
  	   from setting the received router address as the box's
  	   default route.
  
   (!ACCEPT_RTADV)
  	=> No RA processing on that interface.
  
   ACCEPT_RTADV and NO_RADR are per-interface knob.  In short, all interface
   are classified as "RA-accepting" or not.  An RA-accepting interface always
   processes RA messages regardless of ip6.forwarding.  The difference caused by
   NO_RADR or ip6.forwarding is whether the RA source address is considered as
   the default router or not.
  
   R-bit in NA on the RA accepting interfaces is set based on
   net.inet6.ip6.forwarding.  While RFC 6204 W-1 rule (for CPE case) suggests
   a router should disable the R-bit completely even when the box has
   net.inet6.ip6.forwarding=1, I believe there is no technical reason with
   doing so.  This behavior can be set by a new sysctl net.inet6.ip6.norbit_raif
   (the default is 0).
  
  Usage:
  
   # ifconfig fxp0 inet6 accept_rtadv
  	=> accept RA on fxp0
   # ifconfig fxp0 inet6 accept_rtadv no_radr
  	=> accept RA on fxp0 but ignore default route information in it.
   # sysctl net.inet6.ip6.norbit_no_radr=1
  	=> R-bit in NAs on RA accepting interfaces will always be set to 0.

Modified:
  head/sbin/ifconfig/af_inet6.c
  head/sbin/ifconfig/af_nd6.c
  head/sys/netinet6/in6.h
  head/sys/netinet6/in6_proto.c
  head/sys/netinet6/ip6_var.h
  head/sys/netinet6/nd6.c
  head/sys/netinet6/nd6.h
  head/sys/netinet6/nd6_nbr.c
  head/sys/netinet6/nd6_rtr.c

Modified: head/sbin/ifconfig/af_inet6.c
==============================================================================
--- head/sbin/ifconfig/af_inet6.c	Mon Jun  6 01:53:31 2011	(r222727)
+++ head/sbin/ifconfig/af_inet6.c	Mon Jun  6 02:14:23 2011	(r222728)
@@ -499,6 +499,8 @@ static struct cmd inet6_cmds[] = {
 	DEF_CMD("-autoconf",	-IN6_IFF_AUTOCONF,	setip6flags),
 	DEF_CMD("accept_rtadv",	ND6_IFF_ACCEPT_RTADV,	setnd6flags),
 	DEF_CMD("-accept_rtadv",-ND6_IFF_ACCEPT_RTADV,	setnd6flags),
+	DEF_CMD("no_radr",	ND6_IFF_NO_RADR,	setnd6flags),
+	DEF_CMD("-no_radr",	-ND6_IFF_NO_RADR,	setnd6flags),
 	DEF_CMD("defaultif",	1,			setnd6defif),
 	DEF_CMD("-defaultif",	-1,			setnd6defif),
 	DEF_CMD("ifdisabled",	ND6_IFF_IFDISABLED,	setnd6flags),

Modified: head/sbin/ifconfig/af_nd6.c
==============================================================================
--- head/sbin/ifconfig/af_nd6.c	Mon Jun  6 01:53:31 2011	(r222727)
+++ head/sbin/ifconfig/af_nd6.c	Mon Jun  6 02:14:23 2011	(r222728)
@@ -58,7 +58,7 @@ static const char rcsid[] =
 #define	MAX_SYSCTL_TRY	5
 #define	ND6BITS	"\020\001PERFORMNUD\002ACCEPT_RTADV\003PREFER_SOURCE" \
 		"\004IFDISABLED\005DONT_SET_IFROUTE\006AUTO_LINKLOCAL" \
-		"\020DEFAULTIF"
+		"\007NO_RADR\020DEFAULTIF"
 
 static int isnd6defif(int);
 void setnd6flags(const char *, int, int, const struct afswtch *);

Modified: head/sys/netinet6/in6.h
==============================================================================
--- head/sys/netinet6/in6.h	Mon Jun  6 01:53:31 2011	(r222727)
+++ head/sys/netinet6/in6.h	Mon Jun  6 02:14:23 2011	(r222728)
@@ -611,7 +611,10 @@ struct ip6_mtuinfo {
 #define IPV6CTL_STEALTH		45
 
 #define	ICMPV6CTL_ND6_ONLINKNSRFC4861	47
-#define IPV6CTL_MAXID		48
+#define	IPV6CTL_NO_RADR		48	/* No defroute from RA */
+#define	IPV6CTL_NORBIT_RAIF	49	/* Disable R-bit in NA on RA
+					 * receiving IF. */
+#define	IPV6CTL_MAXID		50
 #endif /* __BSD_VISIBLE */
 
 /*

Modified: head/sys/netinet6/in6_proto.c
==============================================================================
--- head/sys/netinet6/in6_proto.c	Mon Jun  6 01:53:31 2011	(r222727)
+++ head/sys/netinet6/in6_proto.c	Mon Jun  6 02:14:23 2011	(r222728)
@@ -409,6 +409,8 @@ VNET_DEFINE(int, ip6_sendredirects) = IP
 VNET_DEFINE(int, ip6_defhlim) = IPV6_DEFHLIM;
 VNET_DEFINE(int, ip6_defmcasthlim) = IPV6_DEFAULT_MULTICAST_HOPS;
 VNET_DEFINE(int, ip6_accept_rtadv) = 0;
+VNET_DEFINE(int, ip6_no_radr) = 0;
+VNET_DEFINE(int, ip6_norbit_raif) = 0;
 VNET_DEFINE(int, ip6_maxfragpackets);	/* initialized in frag6.c:frag6_init() */
 VNET_DEFINE(int, ip6_maxfrags);		/* initialized in frag6.c:frag6_init() */
 VNET_DEFINE(int, ip6_log_interval) = 5;
@@ -537,6 +539,15 @@ SYSCTL_VNET_INT(_net_inet6_ip6, IPV6CTL_
 	CTLFLAG_RW, &VNET_NAME(ip6_accept_rtadv), 0,
 	"Default value of per-interface flag for accepting ICMPv6 Router"
 	"Advertisement messages");
+SYSCTL_VNET_INT(_net_inet6_ip6, IPV6CTL_NO_RADR, no_radr,
+	CTLFLAG_RW, &VNET_NAME(ip6_no_radr), 0,
+	"Default value of per-interface flag to control whether routers "
+	"sending ICMPv6 RA messages on that interface are added into the "
+	"default router list.");
+SYSCTL_VNET_INT(_net_inet6_ip6, IPV6CTL_NORBIT_RAIF, norbit_raif, CTLFLAG_RW,
+	&VNET_NAME(ip6_norbit_raif), 0,
+	"Always set 0 to R flag in ICMPv6 NA messages when accepting RA"
+	" on the interface.");
 SYSCTL_VNET_INT(_net_inet6_ip6, IPV6CTL_KEEPFAITH, keepfaith, CTLFLAG_RW,
 	&VNET_NAME(ip6_keepfaith), 0, "");
 SYSCTL_VNET_INT(_net_inet6_ip6, IPV6CTL_LOG_INTERVAL, log_interval,

Modified: head/sys/netinet6/ip6_var.h
==============================================================================
--- head/sys/netinet6/ip6_var.h	Mon Jun  6 01:53:31 2011	(r222727)
+++ head/sys/netinet6/ip6_var.h	Mon Jun  6 02:14:23 2011	(r222728)
@@ -316,6 +316,9 @@ VNET_DECLARE(int, ip6_maxfragpackets);	/
 VNET_DECLARE(int, ip6_maxfrags);	/* Maximum fragments in reassembly
 					 * queue */
 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
+					 * receiving IF. */
 VNET_DECLARE(int, ip6_keepfaith);	/* Firewall Aided Internet Translator */
 VNET_DECLARE(int, ip6_log_interval);
 VNET_DECLARE(time_t, ip6_log_time);
@@ -327,6 +330,8 @@ VNET_DECLARE(int, ip6_dad_count);	/* Dup
 #define	V_ip6_maxfragpackets		VNET(ip6_maxfragpackets)
 #define	V_ip6_maxfrags			VNET(ip6_maxfrags)
 #define	V_ip6_accept_rtadv		VNET(ip6_accept_rtadv)
+#define	V_ip6_no_radr			VNET(ip6_no_radr)
+#define	V_ip6_norbit_raif		VNET(ip6_norbit_raif)
 #define	V_ip6_keepfaith			VNET(ip6_keepfaith)
 #define	V_ip6_log_interval		VNET(ip6_log_interval)
 #define	V_ip6_log_time			VNET(ip6_log_time)

Modified: head/sys/netinet6/nd6.c
==============================================================================
--- head/sys/netinet6/nd6.c	Mon Jun  6 01:53:31 2011	(r222727)
+++ head/sys/netinet6/nd6.c	Mon Jun  6 02:14:23 2011	(r222728)
@@ -193,6 +193,8 @@ nd6_ifattach(struct ifnet *ifp)
 	/* A loopback interface does not need to accept RTADV. */
 	if (V_ip6_accept_rtadv && !(ifp->if_flags & IFF_LOOPBACK))
 		nd->flags |= ND6_IFF_ACCEPT_RTADV;
+	if (V_ip6_no_radr && !(ifp->if_flags & IFF_LOOPBACK))
+		nd->flags |= ND6_IFF_NO_RADR;
 
 	/* XXX: we cannot call nd6_setmtu since ifp is not fully initialized */
 	nd6_setmtu0(ifp, nd);
@@ -825,7 +827,7 @@ nd6_purge(struct ifnet *ifp)
 	if (V_nd6_defifindex == ifp->if_index)
 		nd6_setdefaultiface(0);
 
-	if (!V_ip6_forwarding && ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) {
+	if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) {
 		/* Refresh default router list. */
 		defrouter_select();
 	}
@@ -958,10 +960,9 @@ nd6_is_new_addr_neighbor(struct sockaddr
 	/*
 	 * If the default router list is empty, all addresses are regarded
 	 * as on-link, and thus, as a neighbor.
-	 * XXX: we restrict the condition to hosts, because routers usually do
-	 * not have the "default router list".
 	 */
-	if (!V_ip6_forwarding && TAILQ_FIRST(&V_nd_defrouter) == NULL &&
+	if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV &&
+	    TAILQ_FIRST(&V_nd_defrouter) == NULL &&
 	    V_nd6_defifindex == ifp->if_index) {
 		return (1);
 	}
@@ -1022,8 +1023,7 @@ nd6_free(struct llentry *ln, int gc)
 
 	ifp = ln->lle_tbl->llt_ifp;
 
-	if (!V_ip6_forwarding) {
-
+	if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) {
 		dr = defrouter_lookup(&L3_ADDR_SIN6(ln)->sin6_addr, ifp);
 
 		if (dr != NULL && dr->expire &&
@@ -1340,7 +1340,7 @@ nd6_ioctl(u_long cmd, caddr_t data, stru
 					continue;
 				ia = (struct in6_ifaddr *)ifa;
 				if ((ia->ia6_flags & IN6_IFF_DUPLICATED) &&
-				    IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr)) {
+				    IN6_IS_ADDR_LINKLOCAL(IA6_IN6(ia))) {
 					duplicated_linklocal = 1;
 					break;
 				}
@@ -1718,7 +1718,7 @@ nd6_cache_lladdr(struct ifnet *ifp, stru
 	 * for those are not autoconfigured hosts, we explicitly avoid such
 	 * cases for safety.
 	 */
-	if (do_update && router && !V_ip6_forwarding &&
+	if (do_update && router &&
 	    ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) {
 		/*
 		 * guaranteed recursion

Modified: head/sys/netinet6/nd6.h
==============================================================================
--- head/sys/netinet6/nd6.h	Mon Jun  6 01:53:31 2011	(r222727)
+++ head/sys/netinet6/nd6.h	Mon Jun  6 02:14:23 2011	(r222728)
@@ -85,6 +85,7 @@ struct nd_ifinfo {
 				     */
 #define ND6_IFF_DONT_SET_IFROUTE	0x10
 #define ND6_IFF_AUTO_LINKLOCAL	0x20
+#define	ND6_IFF_NO_RADR		0x40
 
 #define	ND6_CREATE		LLE_CREATE
 #define	ND6_EXCLUSIVE		LLE_EXCLUSIVE

Modified: head/sys/netinet6/nd6_nbr.c
==============================================================================
--- head/sys/netinet6/nd6_nbr.c	Mon Jun  6 01:53:31 2011	(r222727)
+++ head/sys/netinet6/nd6_nbr.c	Mon Jun  6 02:14:23 2011	(r222728)
@@ -112,10 +112,14 @@ nd6_ns_input(struct mbuf *m, int off, in
 	int lladdrlen = 0;
 	int anycast = 0, proxy = 0, tentative = 0;
 	int tlladdr;
+	int rflag;
 	union nd_opts ndopts;
 	struct sockaddr_dl proxydl;
 	char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
 
+	rflag = (V_ip6_forwarding) ? ND_NA_FLAG_ROUTER : 0;
+	if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV && V_ip6_norbit_raif)
+		rflag = 0;
 #ifndef PULLDOWN_TEST
 	IP6_EXTHDR_CHECK(m, off, icmp6len,);
 	nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off);
@@ -339,8 +343,7 @@ nd6_ns_input(struct mbuf *m, int off, in
 			goto bad;
 		nd6_na_output(ifp, &in6_all, &taddr6,
 		    ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
-		    (V_ip6_forwarding ? ND_NA_FLAG_ROUTER : 0),
-		    tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL);
+		    rflag, tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL);
 		goto freeit;
 	}
 
@@ -349,8 +352,8 @@ nd6_ns_input(struct mbuf *m, int off, in
 
 	nd6_na_output(ifp, &saddr6, &taddr6,
 	    ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
-	    (V_ip6_forwarding ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED,
-	    tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL);
+	    rflag | ND_NA_FLAG_SOLICITED, tlladdr,
+	    proxy ? (struct sockaddr *)&proxydl : NULL);
  freeit:
 	if (ifa != NULL)
 		ifa_free(ifa);
@@ -862,7 +865,8 @@ nd6_na_input(struct mbuf *m, int off, in
 			dr = defrouter_lookup(in6, ln->lle_tbl->llt_ifp);
 			if (dr)
 				defrtrlist_del(dr);
-			else if (!V_ip6_forwarding) {
+			else if (ND_IFINFO(ln->lle_tbl->llt_ifp)->flags &
+			    ND6_IFF_ACCEPT_RTADV) {
 				/*
 				 * Even if the neighbor is not in the default
 				 * router list, the neighbor may be used

Modified: head/sys/netinet6/nd6_rtr.c
==============================================================================
--- head/sys/netinet6/nd6_rtr.c	Mon Jun  6 01:53:31 2011	(r222727)
+++ head/sys/netinet6/nd6_rtr.c	Mon Jun  6 02:14:23 2011	(r222728)
@@ -127,8 +127,11 @@ nd6_rs_input(struct mbuf *m, int off, in
 	union nd_opts ndopts;
 	char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
 
-	/* If I'm not a router, ignore it. */
-	if (!V_ip6_forwarding)
+	/*
+	 * Accept RS only when V_ip6_forwarding=1 and the interface has
+	 * no ND6_IFF_ACCEPT_RTADV.
+	 */
+	if (!V_ip6_forwarding || ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV)
 		goto freeit;
 
 	/* Sanity checks */
@@ -213,11 +216,10 @@ nd6_ra_input(struct mbuf *m, int off, in
 	char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
 
 	/*
-	 * We only accept RAs only when
-	 * the node is not a router and
-	 * per-interface variable allows RAs on the receiving interface.
+	 * We only accept RAs only when the per-interface flag
+	 * ND6_IFF_ACCEPT_RTADV is on the receiving interface.
 	 */
-	if (V_ip6_forwarding || !(ndi->flags & ND6_IFF_ACCEPT_RTADV))
+	if (!(ndi->flags & ND6_IFF_ACCEPT_RTADV))
 		goto freeit;
 
 	if (ip6->ip6_hlim != 255) {
@@ -266,7 +268,15 @@ nd6_ra_input(struct mbuf *m, int off, in
 	bzero(&dr0, sizeof(dr0));
 	dr0.rtaddr = saddr6;
 	dr0.flags  = nd_ra->nd_ra_flags_reserved;
-	dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime);
+	/*
+	 * Effectively-disable the route in the RA packet
+	 * when ND6_IFF_NO_RADR on the receiving interface or
+	 * ip6.forwarding=1.
+	 */
+	if (ndi->flags & ND6_IFF_NO_RADR || V_ip6_forwarding)
+		dr0.rtlifetime = 0;
+	else
+		dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime);
 	dr0.expire = time_second + dr0.rtlifetime;
 	dr0.ifp = ifp;
 	/* unspecified or not? (RFC 2461 6.3.4) */
@@ -557,7 +567,7 @@ defrtrlist_del(struct nd_defrouter *dr)
 	 * Flush all the routing table entries that use the router
 	 * as a next hop.
 	 */
-	if (!V_ip6_forwarding)
+	if (ND_IFINFO(dr->ifp)->flags & ND6_IFF_ACCEPT_RTADV)
 		rt6_flush(&dr->rtaddr, dr->ifp);
 
 	if (dr->installed) {
@@ -616,20 +626,6 @@ defrouter_select(void)
 	struct llentry *ln = NULL;
 
 	/*
-	 * This function should be called only when acting as an autoconfigured
-	 * host.  Although the remaining part of this function is not effective
-	 * if the node is not an autoconfigured host, we explicitly exclude
-	 * such cases here for safety.
-	 */
-	if (V_ip6_forwarding) {
-		nd6log((LOG_WARNING,
-		    "defrouter_select: called unexpectedly (forwarding=%d)\n",
-		    V_ip6_forwarding));
-		splx(s);
-		return;
-	}
-
-	/*
 	 * Let's handle easy case (3) first:
 	 * If default router list is empty, there's nothing to be done.
 	 */



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