From nobody@FreeBSD.org  Thu Dec  2 20:34:32 2010
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 7003F1065672
	for <freebsd-gnats-submit@FreeBSD.org>; Thu,  2 Dec 2010 20:34:32 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from red.freebsd.org (unknown [IPv6:2001:4f8:fff6::22])
	by mx1.freebsd.org (Postfix) with ESMTP id 43E968FC24
	for <freebsd-gnats-submit@FreeBSD.org>; Thu,  2 Dec 2010 20:34:32 +0000 (UTC)
Received: from red.freebsd.org (localhost [127.0.0.1])
	by red.freebsd.org (8.14.4/8.14.4) with ESMTP id oB2KYWXQ020918
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 2 Dec 2010 20:34:32 GMT
	(envelope-from nobody@red.freebsd.org)
Received: (from nobody@localhost)
	by red.freebsd.org (8.14.4/8.14.4/Submit) id oB2KYWrU020917;
	Thu, 2 Dec 2010 20:34:32 GMT
	(envelope-from nobody)
Message-Id: <201012022034.oB2KYWrU020917@red.freebsd.org>
Date: Thu, 2 Dec 2010 20:34:32 GMT
From: Ted Sun <tsun@datatekcorp.com>
To: freebsd-gnats-submit@FreeBSD.org
Subject: ND, ICMPv6 Redirect vs Destination Cache failed in release 8.0
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         152791
>Category:       kern
>Synopsis:       [icmp] ND, ICMPv6 Redirect vs Destination Cache failed in release 8.0
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    gnn
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Dec 02 20:40:07 UTC 2010
>Closed-Date:    
>Last-Modified:  Sun May 18 05:00:27 UTC 2014
>Originator:     Ted Sun
>Release:        FreeBSD 8.0-RELEASE i386
>Organization:
Datatek
>Environment:
FreeBSD fosters.datatekcorp.com 8.0-RELEASE FreeBSD 8.0-RELEASE #0: Thu Sep 9  14:52:35 EDT 2010 root@fosters.datatekcorp.com:/usr/obj/usr/src/sys/MYKERNEL i386
>Description:
The running OS is a 8.0-RELEASE FreeBSD with the IP-SEC support on.
Output of command "diff -U5 GENERIC MYKERNEL" at /usr/src/sys/i386/conf shows
---------------------------------------------------------------------
--- GENERIC     2009-11-09 18:48:01.000000000 -0500
+++ MYKERNEL    2010-09-09 13:15:51.000000000 -0400
@@ -330,5 +330,9 @@
 #device        sbp             # SCSI over FireWire (Requires scbus and da)
 device         fwe             # Ethernet over FireWire (non-standard!)
 device         fwip            # IP over FireWire (RFC 2734,3146)
 device         dcons           # Dumb console driver
 device         dcons_crom      # Configuration ROM for dcons
+
+#arndt added- IP-SEC support
+device         crypto
+options        IPSEC
---------------------------------------------------------------------

After receiving an ICMPv6 redirect package with Option ICMPv6_TLL which contains TargetAddress == DestinationAddress == 3ffe:501:fff:109:200:ff:fe00:1c5.

The "ndp -a" will output a corresponding entry as
"3ffe:501:fff:109:200:ff:fe00:1c5  0:0:0:0:a9:a9    vr0 23h58m38s S"

But the FreeBSD will continue to echo reply to the old MAC address 0:0:0:0:a0:a0 for IPv6 destination 3ffe:501:fff:109:200:ff:fe00:1c5

The correct echo reply should use the redirected MAC address 0:0:0:0:a9:a9.
>How-To-Repeat:
Needed test tool are the "IPv6 Conformance Test Tool v6eval-3.0.12.tar.gz or newer one" and the "ct-2.1.1.tar.gz" from "http://www.tahi.org/release".

Set up the TAHI "IPv6 conformance Test For Neighbor discovery" on a BSD machine.
Set up a IP-SEC enabled BSD 8.0 release PC as the NUT (node under test).
Cross connect the TAHI and the NUT with ethernet cable.

1. Start the TAHI test by "make test". Test will fail at item 66 "Redirect vs Destination Cache; Redirect to a host" should fail. or ..
2. Start the TAHI test of item 66 only by "make AROPT='-s 66 -e 66' test".
>Fix:


>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->bz 
Responsible-Changed-By: bz 
Responsible-Changed-When: Fri Dec 3 09:24:12 UTC 2010 
Responsible-Changed-Why:  
Temporarly claim. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=152791 
State-Changed-From-To: open->feedback 
State-Changed-By: bz 
State-Changed-When: Fri Dec 3 09:27:25 UTC 2010 
State-Changed-Why:  
Folow-up sent to see if it still exists or is a side effect 
of another features; I'll need to re-setup TAHI in the lab 
otherwise. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=152791 

From: "Bjoern A. Zeeb" <bz@FreeBSD.org>
To: bug-followup@FreeBSD.org, tsun@datatekcorp.com
Cc:  
Subject: Re: kern/152791: ND, ICMPv6 Redirect vs Destination Cache failed in
 release 8.0
Date: Fri, 3 Dec 2010 09:27:09 +0000 (UTC)

 Hi,
 
 have you tried on stable/8 rather than 8.0-RELEASE?  A lot of bugs
 have been fixed since.  I am not saying this one was but it would be
 good if you could confirm it still exists.  Would you be able to do
 so?
 
 And as a second thing; can you remove FLOWTABLE from your kernel and
 try again to see if it makes a difference (even on 8.0-R)?
 
 /bz
 
 -- 
 Bjoern A. Zeeb                              Welcome a new stage of life.
          <ks> Going to jail sucks -- <bz> All my daemons like it!
    http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/jails.html

From: "Ted Sun" <tsun@datatekcorp.com>
To: <bug-followup@FreeBSD.org>,
	<tsun@datatekcorp.com>
Cc:  
Subject: Re: kern/152791: ND, ICMPv6 Redirect vs Destination Cache failed in release 8.0
Date: Fri, 3 Dec 2010 10:19:50 -0500

 This is a multi-part message in MIME format.
 
 ------=_NextPart_000_004E_01CB92D3.9E9C76C0
 Content-Type: text/plain;
 	charset="us-ascii"
 Content-Transfer-Encoding: 7bit
 
 Hi Bjoern,
 
 Thank you for the kindly reply.
 
 I have not tried the stable/8 yet. 
 
 I will try the 8.0-RELEASE without FLOWTABLE first then the stable/8 with
 and without FLOWTABLE next.
 
 Best regards,
 
 Ted
 
 ------=_NextPart_000_004E_01CB92D3.9E9C76C0--

From: "Ted Sun" <tsun@datatekcorp.com>
To: "'Bjoern A. Zeeb'" <bz@FreeBSD.org>,
	<bug-followup@FreeBSD.org>
Subject: RE: kern/152791: ND, ICMPv6 Redirect vs Destination Cache failed in release 8.0
Date: Mon, 20 Dec 2010 09:37:23 -0500
MIME-Version: 1.0
Content-Type: multipart/alternative;
	boundary="----=_NextPart_000_0005_01CBA029.818CC220"
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.5994

This is a multi-part message in MIME format.

------=_NextPart_000_0005_01CBA029.818CC220
Content-Type: text/plain;
	charset="us-ascii"
Content-Transfer-Encoding: 7bit

Hi Bjoern,

The following results are tested by a FreeBSD 8.0 release PC with TAHI
v6eval-3.3.1 and ct-2.1.1.

I tried the FreeBSD-8.1-STABLE-201011-i386-dvd1.iso with and without the
FLOWTABLE kernel option.

I also tried the FreeBSD 8.0 release without the FLOWTABLE kernel option.

This problem still exists.

I can see the related MAC address show in the output of "ndp -a".

I can not find the related IP address show in the output of "netstat -rn".

Best regards,

Ted

No virus found in this message.
Checked by AVG - www.avg.com
Version: 10.0.1170 / Virus Database: 426/3293 - Release Date: 12/02/10

------=_NextPart_000_0005_01CBA029.818CC220--
State-Changed-From-To: feedback->open 
State-Changed-By: bz 
State-Changed-When: Sun Dec 26 13:21:24 UTC 2010 
State-Changed-Why:  
Feedback received.  Thanks a lot for doing the testing! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=152791 

From: "Ted Sun" <tsun@datatekcorp.com>
To: <bug-followup@FreeBSD.org>,
	<tsun@datatekcorp.com>
Cc:  
Subject: Re: kern/152791: [icmp] ND, ICMPv6 Redirect vs Destination Cache failed in release 8.0
Date: Wed, 15 Jun 2011 16:02:54 -0400

 In=A0reading the FreeBSD release 8.0 code that handles=A0Neighbor =
 Discovery
 (ND), I have difficulty locating the data structure which represents the
 =93Destination Cache=94=A0discussed in RFC4861.
 A Destination Cache-related sysctl =93net.inet6.icmp6.redirtimeout=94 is
 initialized in in6_proto.c=A0to a value of 600 seconds, but it is never
 referenced by any code.
 After receiving a ND-redirect for an on-link host,=A0BSD only creates a
 llentry for the target address for the ND package.
 Neither rtentry nor the prefix was created for it. Therefore, the =
 redirect
 will not change BSD's routing preference when the selectroute is called =
 and
 future data packets will be transmitted with the wrong MAC address and =
 will
 also=A0fail the TAHI ND test 66.
 I am totally lost here.
 Your kind help will be=A0greatly appreciated.
 Thanks,
 Ted
 
 

From: Eric van Gyzen <eric_van_gyzen@dell.com>
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/152791: [icmp] ND, ICMPv6 Redirect vs Destination Cache
 failed in release 8.0
Date: Thu, 20 Sep 2012 16:52:31 -0500

 I just ran into this problem.  Is this fixed in head?  Is anyone working 
 on a fix?  If not, I'll work on it.  I just don't want to duplicate effort.
 
 Thanks,
 
 Eric

From: Eric van Gyzen <eric_van_gyzen@dell.com>
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/152791: [icmp] ND, ICMPv6 Redirect vs Destination Cache
 failed in release 8.0
Date: Mon, 22 Oct 2012 11:41:30 -0500

 FYI:  I just sent patches to freebsd-net@freebsd.org for review.
 
 Eric

From: Eric van Gyzen <eric_van_gyzen@dell.com>
To: bug-followup@FreeBSD.org, tsun@datatekcorp.com, 
 "Bjoern A. Zeeb" <bz@FreeBSD.org>
Cc:  
Subject: Re: kern/152791: [icmp] ND, ICMPv6 Redirect vs Destination Cache
 failed in release 8.0
Date: Thu, 9 May 2013 10:08:07 -0500

 --------------000407050907080508090402
 Content-Type: multipart/alternative;
 	boundary="------------060001080000050809000203"
 
 --------------060001080000050809000203
 Content-Type: text/plain; charset="ISO-8859-1"
 Content-Transfer-Encoding: 7bit
 
 I'm attaching the proposed patches to the PR so they don't get lost.
 
 Eric
 
 -- 
 *Eric van Gyzen*
 Software Development Engineer
 *Dell* | Compellent
 *office* +1 952 562 3197
 Cube J-732, 7615 Smetana Lane
 Eden Prairie, MN 55344
 eric_van_gyzen@dell.com <mailto:eric_van_gyzen@dell.com>
 
 --------------060001080000050809000203
 Content-Type: text/html; charset="ISO-8859-1"
 Content-Transfer-Encoding: 7bit
 
 <html>
   <head>
 
     <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
   </head>
   <body bgcolor="#FFFFFF" text="#000000">
     I'm attaching the proposed patches to the PR so they don't get lost.<br>
     <br>
     Eric<br>
     <br>
     <div class="moz-signature">-- <br>
       <div style="font-size:small">
         <b>Eric van Gyzen</b><br>
         Software Development Engineer<br>
         <span style="color:#0085C3"><b>Dell</b></span> | Compellent<br>
         <b>office</b> +1 952 562 3197<br>
         Cube J-732, 7615 Smetana Lane<br>
         Eden Prairie, MN 55344<br>
         <a href="mailto:eric_van_gyzen@dell.com">eric_van_gyzen@dell.com</a>
       </div>
     </div>
   </body>
 </html>
 
 --------------060001080000050809000203--
 
 --------------000407050907080508090402
 Content-Type: text/x-diff; name="redir_onlink_10_rev1.diff"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment; filename="redir_onlink_10_rev1.diff"
 
 diff --git a/sys/net/route.c b/sys/net/route.c
 index e69ce48..77f4b9c 100644
 --- a/sys/net/route.c
 +++ b/sys/net/route.c
 @@ -547,27 +547,34 @@ rtredirect_fib(struct sockaddr *dst,
  		error = ENETUNREACH;
  		goto out;
  	}
  	rt = rtalloc1_fib(dst, 0, 0UL, fibnum);	/* NB: rt is locked */
  	/*
  	 * If the redirect isn't from our current router for this dst,
  	 * it's either old or wrong.  If it redirects us to ourselves,
  	 * we have a routing loop, perhaps as a result of an interface
  	 * going down recently.
  	 */
 -	if (!(flags & RTF_DONE) && rt &&
 -	     (!sa_equal(src, rt->rt_gateway) || rt->rt_ifa != ifa))
 -		error = EINVAL;
 -	else if (ifa_ifwithaddr_check(gateway))
 +	if (!(flags & RTF_DONE) && rt) {
 +		if (!sa_equal(src, rt->rt_gateway)) {
 +			error = EINVAL;
 +			goto done;
 +		}
 +		if (rt->rt_ifa != ifa && ifa->ifa_addr->sa_family != AF_LINK) {
 +			error = EINVAL;
 +			goto done;
 +		}
 +	}
 +	if ((flags & RTF_GATEWAY) && ifa_ifwithaddr_check(gateway)) {
  		error = EHOSTUNREACH;
 -	if (error)
  		goto done;
 +	}
  	/*
  	 * Create a new entry if we just got back a wildcard entry
  	 * or the lookup failed.  This is necessary for hosts
  	 * which use routing redirects generated by smart gateways
  	 * to dynamically build the routing tables.
  	 */
  	if (rt == NULL || (rt_mask(rt) && rt_mask(rt)->sa_len < 2))
  		goto create;
  	/*
  	 * Don't listen to the redirect if it's
 @@ -576,21 +583,21 @@ rtredirect_fib(struct sockaddr *dst,
  	if (rt->rt_flags & RTF_GATEWAY) {
  		if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) {
  			/*
  			 * Changing from route to net => route to host.
  			 * Create new route, rather than smashing route to net.
  			 */
  		create:
  			rt0 = rt;
  			rt = NULL;
  		
 -			flags |=  RTF_GATEWAY | RTF_DYNAMIC;
 +			flags |= RTF_DYNAMIC;
  			bzero((caddr_t)&info, sizeof(info));
  			info.rti_info[RTAX_DST] = dst;
  			info.rti_info[RTAX_GATEWAY] = gateway;
  			info.rti_info[RTAX_NETMASK] = netmask;
  			info.rti_ifa = ifa;
  			info.rti_flags = flags;
  			if (rt0 != NULL)
  				RT_UNLOCK(rt0);	/* drop lock to avoid LOR with RNH */
  			error = rtrequest1_fib(RTM_ADD, &info, &rt, fibnum);
  			if (rt != NULL) {
 @@ -603,34 +610,37 @@ rtredirect_fib(struct sockaddr *dst,
  				RTFREE(rt0);
  			
  			stat = &V_rtstat.rts_dynamic;
  		} else {
  			struct rtentry *gwrt;
  
  			/*
  			 * Smash the current notion of the gateway to
  			 * this destination.  Should check about netmask!!!
  			 */
 +			if ((flags & RTF_GATEWAY) == 0)
 +				rt->rt_flags &= ~RTF_GATEWAY;
  			rt->rt_flags |= RTF_MODIFIED;
  			flags |= RTF_MODIFIED;
  			stat = &V_rtstat.rts_newgateway;
  			/*
  			 * add the key and gateway (in one malloc'd chunk).
  			 */
  			RT_UNLOCK(rt);
  			RADIX_NODE_HEAD_LOCK(rnh);
  			RT_LOCK(rt);
  			rt_setgate(rt, rt_key(rt), gateway);
  			gwrt = rtalloc1(gateway, 1, RTF_RNH_LOCKED);
  			RADIX_NODE_HEAD_UNLOCK(rnh);
  			EVENTHANDLER_INVOKE(route_redirect_event, rt, gwrt, dst);
 -			RTFREE_LOCKED(gwrt);
 +			if (gwrt)
 +				RTFREE_LOCKED(gwrt);
  		}
  	} else
  		error = EHOSTUNREACH;
  done:
  	if (rt)
  		RTFREE_LOCKED(rt);
  out:
  	if (error)
  		V_rtstat.rts_badredirect++;
  	else if (stat != NULL)
 diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c
 index 202dc05..de6b8bd 100644
 --- a/sys/netinet6/icmp6.c
 +++ b/sys/netinet6/icmp6.c
 @@ -2456,21 +2456,20 @@ icmp6_redirect_input(struct mbuf *m, int off)
  		is_router = 1;	/* router case */
  	if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0)
  		is_onlink = 1;	/* on-link destination case */
  	if (!is_router && !is_onlink) {
  		nd6log((LOG_ERR,
  		    "ICMP6 redirect rejected; "
  		    "neither router case nor onlink case: %s\n",
  		    icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
  		goto bad;
  	}
 -	/* validation passed */
  
  	icmp6len -= sizeof(*nd_rd);
  	nd6_option_init(nd_rd + 1, icmp6len, &ndopts);
  	if (nd6_options(&ndopts) < 0) {
  		nd6log((LOG_INFO, "%s: invalid ND option, rejected: %s\n",
  		    __func__, icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
  		/* nd6_options have incremented stats */
  		goto freeit;
  	}
  
 @@ -2481,47 +2480,62 @@ icmp6_redirect_input(struct mbuf *m, int off)
  
  	if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
  		nd6log((LOG_INFO, "%s: lladdrlen mismatch for %s "
  		    "(if %d, icmp6 packet %d): %s\n",
  		    __func__, ip6_sprintf(ip6buf, &redtgt6),
  		    ifp->if_addrlen, lladdrlen - 2,
  		    icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
  		goto bad;
  	}
  
 +	/* Validation passed. */
 +
  	/* RFC 2461 8.3 */
  	nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
  	    is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER);
  
 -	if (!is_onlink) {	/* better router case.  perform rtredirect. */
 -		/* perform rtredirect */
 +	/*
 +	 * Install a gateway route in the better-router case
 +	 * or an interface route in the on-link-destination case.
 +	 */
 +	{
  		struct sockaddr_in6 sdst;
  		struct sockaddr_in6 sgw;
  		struct sockaddr_in6 ssrc;
 +		struct sockaddr *gw;
 +		int rt_flags;
  		u_int fibnum;
  
  		bzero(&sdst, sizeof(sdst));
 -		bzero(&sgw, sizeof(sgw));
  		bzero(&ssrc, sizeof(ssrc));
 -		sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6;
 -		sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len =
 -			sizeof(struct sockaddr_in6);
 -		bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr));
 +		sdst.sin6_family = ssrc.sin6_family = AF_INET6;
 +		sdst.sin6_len = ssrc.sin6_len = sizeof(struct sockaddr_in6);
  		bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
  		bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr));
 +		rt_flags = RTF_HOST;
 +		if (is_router) {
 +			bzero(&sgw, sizeof(sgw));
 +			sgw.sin6_family = AF_INET6;
 +			sgw.sin6_len = sizeof(struct sockaddr_in6);
 +			bcopy(&redtgt6, &sgw.sin6_addr,
 +				sizeof(struct in6_addr));
 +			gw = (struct sockaddr *)&sgw;
 +			rt_flags |= RTF_GATEWAY;
 +		} else
 +			gw = ifp->if_addr->ifa_addr;
  		for (fibnum = 0; fibnum < rt_numfibs; fibnum++)
 -			in6_rtredirect((struct sockaddr *)&sdst,
 -			    (struct sockaddr *)&sgw, (struct sockaddr *)NULL,
 -			    RTF_GATEWAY | RTF_HOST, (struct sockaddr *)&ssrc,
 -			    fibnum);
 +			in6_rtredirect((struct sockaddr *)&sdst, gw,
 +			    (struct sockaddr *)NULL, rt_flags,
 +			    (struct sockaddr *)&ssrc, fibnum);
  	}
 -	/* finally update cached route in each socket via pfctlinput */
 +
 +	/* Finally, update the cached route in each socket via pfctlinput. */
      {
  	struct sockaddr_in6 sdst;
  
  	bzero(&sdst, sizeof(sdst));
  	sdst.sin6_family = AF_INET6;
  	sdst.sin6_len = sizeof(struct sockaddr_in6);
  	bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
  	pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst);
  #ifdef IPSEC
  	key_sa_routechange((struct sockaddr *)&sdst);
 diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
 index 592eb90..e70d49e 100644
 --- a/sys/netinet6/nd6.c
 +++ b/sys/netinet6/nd6.c
 @@ -992,20 +992,22 @@ nd6_is_addr_neighbor(struct sockaddr_in6 *addr, struct ifnet *ifp)
   * Since the function would cause significant changes in the kernel, DO NOT
   * make it global, unless you have a strong reason for the change, and are sure
   * that the change is safe.
   */
  static struct llentry *
  nd6_free(struct llentry *ln, int gc)
  {
          struct llentry *next;
  	struct nd_defrouter *dr;
  	struct ifnet *ifp;
 +	struct radix_node_head *rnh;
 +	int fibnum;
  
  	LLE_WLOCK_ASSERT(ln);
  
  	/*
  	 * we used to have pfctlinput(PRC_HOSTDEAD) here.
  	 * even though it is not harmful, it was not really necessary.
  	 */
  
  	/* cancel timer */
  	nd6_llinfo_settimer_locked(ln, -1);
 @@ -1085,20 +1087,42 @@ nd6_free(struct llentry *ln, int gc)
  			 * the check now.
  			 */
  			pfxlist_onlink_check();
  
  			/*
  			 * Refresh default router list.
  			 */
  			defrouter_select();
  		}
  
 +		/*
 +		 * If this entry was added by an on-link redirect,
 +		 * remove the corresponding host route.
 +		 */
 +		for (fibnum = 0; fibnum < rt_numfibs; fibnum++) {
 +			rnh = rt_tables_get_rnh(fibnum, AF_INET6);
 +			if (rnh) {
 +				struct rtentry *rt;
 +
 +				RADIX_NODE_HEAD_LOCK(rnh);
 +				rt = in6_rtalloc1(L3_ADDR(ln), 0,
 +					RTF_RNH_LOCKED, fibnum);
 +				if (rt) {
 +					if (rt->rt_flags ==
 +					    (RTF_UP|RTF_HOST|RTF_DYNAMIC))
 +						rtexpunge(rt);
 +					RTFREE_LOCKED(rt);
 +				}
 +				RADIX_NODE_HEAD_UNLOCK(rnh);
 +			}
 +		}
 +
  		if (ln->ln_router || dr)
  			LLE_WLOCK(ln);
  	}
  
  	/*
  	 * Before deleting the entry, remember the next entry as the
  	 * return value.  We need this because pfxlist_onlink_check() above
  	 * might have freed other entries (particularly the old next entry) as
  	 * a side effect (XXX).
  	 */
 diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
 index 792b5c9..f19beeb 100644
 --- a/sys/netinet6/nd6_rtr.c
 +++ b/sys/netinet6/nd6_rtr.c
 @@ -2156,21 +2156,21 @@ rt6_deleteroute(struct radix_node *rn, void *arg)
  		return (0);
  
  	/*
  	 * We delete only host route. This means, in particular, we don't
  	 * delete default route.
  	 */
  	if ((rt->rt_flags & RTF_HOST) == 0)
  		return (0);
  
  	return (in6_rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway,
 -	    rt_mask(rt), rt->rt_flags, NULL, rt->rt_fibnum));
 +	    rt_mask(rt), rt->rt_flags | RTF_RNH_LOCKED, NULL, rt->rt_fibnum));
  #undef SIN6
  }
  
  int
  nd6_setdefaultiface(int ifindex)
  {
  	int error = 0;
  
  	if (ifindex < 0 || V_if_index < ifindex)
  		return (EINVAL);
 
 --------------000407050907080508090402
 Content-Type: text/x-diff; name="redir_onlink_8_rev1.diff"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment; filename="redir_onlink_8_rev1.diff"
 
 diff --git a/sys/net/route.c b/sys/net/route.c
 index 4df1819..e1aedad 100644
 --- a/sys/net/route.c
 +++ b/sys/net/route.c
 @@ -547,27 +547,34 @@ rtredirect_fib(struct sockaddr *dst,
  		error = ENETUNREACH;
  		goto out;
  	}
  	rt = rtalloc1_fib(dst, 0, 0UL, fibnum);	/* NB: rt is locked */
  	/*
  	 * If the redirect isn't from our current router for this dst,
  	 * it's either old or wrong.  If it redirects us to ourselves,
  	 * we have a routing loop, perhaps as a result of an interface
  	 * going down recently.
  	 */
 -	if (!(flags & RTF_DONE) && rt &&
 -	     (!sa_equal(src, rt->rt_gateway) || rt->rt_ifa != ifa))
 -		error = EINVAL;
 -	else if (ifa_ifwithaddr_check(gateway))
 +	if (!(flags & RTF_DONE) && rt) {
 +		if (!sa_equal(src, rt->rt_gateway)) {
 +			error = EINVAL;
 +			goto done;
 +		}
 +		if (rt->rt_ifa != ifa && ifa->ifa_addr->sa_family != AF_LINK) {
 +			error = EINVAL;
 +			goto done;
 +		}
 +	}
 +	if ((flags & RTF_GATEWAY) && ifa_ifwithaddr_check(gateway)) {
  		error = EHOSTUNREACH;
 -	if (error)
  		goto done;
 +	}
  	/*
  	 * Create a new entry if we just got back a wildcard entry
  	 * or the lookup failed.  This is necessary for hosts
  	 * which use routing redirects generated by smart gateways
  	 * to dynamically build the routing tables.
  	 */
  	if (rt == NULL || (rt_mask(rt) && rt_mask(rt)->sa_len < 2))
  		goto create;
  	/*
  	 * Don't listen to the redirect if it's
 @@ -576,21 +583,21 @@ rtredirect_fib(struct sockaddr *dst,
  	if (rt->rt_flags & RTF_GATEWAY) {
  		if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) {
  			/*
  			 * Changing from route to net => route to host.
  			 * Create new route, rather than smashing route to net.
  			 */
  		create:
  			rt0 = rt;
  			rt = NULL;
  		
 -			flags |=  RTF_GATEWAY | RTF_DYNAMIC;
 +			flags |= RTF_DYNAMIC;
  			bzero((caddr_t)&info, sizeof(info));
  			info.rti_info[RTAX_DST] = dst;
  			info.rti_info[RTAX_GATEWAY] = gateway;
  			info.rti_info[RTAX_NETMASK] = netmask;
  			info.rti_ifa = ifa;
  			info.rti_flags = flags;
  			if (rt0 != NULL)
  				RT_UNLOCK(rt0);	/* drop lock to avoid LOR with RNH */
  			error = rtrequest1_fib(RTM_ADD, &info, &rt, fibnum);
  			if (rt != NULL) {
 @@ -603,34 +610,37 @@ rtredirect_fib(struct sockaddr *dst,
  				RTFREE(rt0);
  			
  			stat = &V_rtstat.rts_dynamic;
  		} else {
  			struct rtentry *gwrt;
  
  			/*
  			 * Smash the current notion of the gateway to
  			 * this destination.  Should check about netmask!!!
  			 */
 +			if ((flags & RTF_GATEWAY) == 0)
 +				rt->rt_flags &= ~RTF_GATEWAY;
  			rt->rt_flags |= RTF_MODIFIED;
  			flags |= RTF_MODIFIED;
  			stat = &V_rtstat.rts_newgateway;
  			/*
  			 * add the key and gateway (in one malloc'd chunk).
  			 */
  			RT_UNLOCK(rt);
  			RADIX_NODE_HEAD_LOCK(rnh);
  			RT_LOCK(rt);
  			rt_setgate(rt, rt_key(rt), gateway);
  			gwrt = rtalloc1(gateway, 1, RTF_RNH_LOCKED);
  			RADIX_NODE_HEAD_UNLOCK(rnh);
  			EVENTHANDLER_INVOKE(route_redirect_event, rt, gwrt, dst);
 -			RTFREE_LOCKED(gwrt);
 +			if (gwrt)
 +				RTFREE_LOCKED(gwrt);
  		}
  	} else
  		error = EHOSTUNREACH;
  done:
  	if (rt)
  		RTFREE_LOCKED(rt);
  out:
  	if (error)
  		V_rtstat.rts_badredirect++;
  	else if (stat != NULL)
 diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c
 index 7185537..9ae260c 100644
 --- a/sys/netinet6/icmp6.c
 +++ b/sys/netinet6/icmp6.c
 @@ -2378,21 +2378,20 @@ icmp6_redirect_input(struct mbuf *m, int off)
  		is_router = 1;	/* router case */
  	if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0)
  		is_onlink = 1;	/* on-link destination case */
  	if (!is_router && !is_onlink) {
  		nd6log((LOG_ERR,
  		    "ICMP6 redirect rejected; "
  		    "neither router case nor onlink case: %s\n",
  		    icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
  		goto bad;
  	}
 -	/* validation passed */
  
  	icmp6len -= sizeof(*nd_rd);
  	nd6_option_init(nd_rd + 1, icmp6len, &ndopts);
  	if (nd6_options(&ndopts) < 0) {
  		nd6log((LOG_INFO, "icmp6_redirect_input: "
  		    "invalid ND option, rejected: %s\n",
  		    icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
  		/* nd6_options have incremented stats */
  		goto freeit;
  	}
 @@ -2405,47 +2404,62 @@ icmp6_redirect_input(struct mbuf *m, int off)
  	if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
  		nd6log((LOG_INFO,
  		    "icmp6_redirect_input: lladdrlen mismatch for %s "
  		    "(if %d, icmp6 packet %d): %s\n",
  		    ip6_sprintf(ip6buf, &redtgt6),
  		    ifp->if_addrlen, lladdrlen - 2,
  		    icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
  		goto bad;
  	}
  
 +	/* Validation passed. */
 +
  	/* RFC 2461 8.3 */
  	nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
  	    is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER);
  
 -	if (!is_onlink) {	/* better router case.  perform rtredirect. */
 -		/* perform rtredirect */
 +	/*
 +	 * Install a gateway route in the better-router case
 +	 * or an interface route in the on-link-destination case.
 +	 */
 +	{
  		struct sockaddr_in6 sdst;
  		struct sockaddr_in6 sgw;
  		struct sockaddr_in6 ssrc;
 +		struct sockaddr *gw;
 +		int rt_flags;
  		u_int fibnum;
  
  		bzero(&sdst, sizeof(sdst));
 -		bzero(&sgw, sizeof(sgw));
  		bzero(&ssrc, sizeof(ssrc));
 -		sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6;
 -		sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len =
 -			sizeof(struct sockaddr_in6);
 -		bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr));
 +		sdst.sin6_family = ssrc.sin6_family = AF_INET6;
 +		sdst.sin6_len = ssrc.sin6_len = sizeof(struct sockaddr_in6);
  		bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
  		bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr));
 +		rt_flags = RTF_HOST;
 +		if (is_router) {
 +			bzero(&sgw, sizeof(sgw));
 +			sgw.sin6_family = AF_INET6;
 +			sgw.sin6_len = sizeof(struct sockaddr_in6);
 +			bcopy(&redtgt6, &sgw.sin6_addr,
 +				sizeof(struct in6_addr));
 +			gw = (struct sockaddr *)&sgw;
 +			rt_flags |= RTF_GATEWAY;
 +		} else
 +			gw = ifp->if_addr->ifa_addr;
  		for (fibnum = 0; fibnum < rt_numfibs; fibnum++)
 -			in6_rtredirect((struct sockaddr *)&sdst,
 -			    (struct sockaddr *)&sgw, (struct sockaddr *)NULL,
 -			    RTF_GATEWAY | RTF_HOST, (struct sockaddr *)&ssrc,
 -			    fibnum);
 +			in6_rtredirect((struct sockaddr *)&sdst, gw,
 +			    (struct sockaddr *)NULL, rt_flags,
 +			    (struct sockaddr *)&ssrc, fibnum);
  	}
 -	/* finally update cached route in each socket via pfctlinput */
 +
 +	/* Finally, update the cached route in each socket via pfctlinput. */
      {
  	struct sockaddr_in6 sdst;
  
  	bzero(&sdst, sizeof(sdst));
  	sdst.sin6_family = AF_INET6;
  	sdst.sin6_len = sizeof(struct sockaddr_in6);
  	bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
  	pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst);
  #ifdef IPSEC
  	key_sa_routechange((struct sockaddr *)&sdst);
 diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
 index 64fbf16..b75909c 100644
 --- a/sys/netinet6/nd6.c
 +++ b/sys/netinet6/nd6.c
 @@ -991,20 +991,22 @@ nd6_is_addr_neighbor(struct sockaddr_in6 *addr, struct ifnet *ifp)
   * Since the function would cause significant changes in the kernel, DO NOT
   * make it global, unless you have a strong reason for the change, and are sure
   * that the change is safe.
   */
  static struct llentry *
  nd6_free(struct llentry *ln, int gc)
  {
          struct llentry *next;
  	struct nd_defrouter *dr;
  	struct ifnet *ifp;
 +	struct radix_node_head *rnh;
 +	int fibnum;
  
  	LLE_WLOCK_ASSERT(ln);
  
  	/*
  	 * we used to have pfctlinput(PRC_HOSTDEAD) here.
  	 * even though it is not harmful, it was not really necessary.
  	 */
  
  	/* cancel timer */
  	nd6_llinfo_settimer_locked(ln, -1);
 @@ -1085,20 +1087,42 @@ nd6_free(struct llentry *ln, int gc)
  			 * the check now.
  			 */
  			pfxlist_onlink_check();
  
  			/*
  			 * Refresh default router list.
  			 */
  			defrouter_select();
  		}
  
 +		/*
 +		 * If this entry was added by an on-link redirect,
 +		 * remove the corresponding host route.
 +		 */
 +		for (fibnum = 0; fibnum < rt_numfibs; fibnum++) {
 +			rnh = rt_tables_get_rnh(fibnum, AF_INET6);
 +			if (rnh) {
 +				struct rtentry *rt;
 +
 +				RADIX_NODE_HEAD_LOCK(rnh);
 +				rt = in6_rtalloc1(L3_ADDR(ln), 0,
 +					RTF_RNH_LOCKED, fibnum);
 +				if (rt) {
 +					if (rt->rt_flags ==
 +					    (RTF_UP|RTF_HOST|RTF_DYNAMIC))
 +						rtexpunge(rt);
 +					RTFREE_LOCKED(rt);
 +				}
 +				RADIX_NODE_HEAD_UNLOCK(rnh);
 +			}
 +		}
 +
  		if (ln->ln_router || dr)
  			LLE_WLOCK(ln);
  	}
  
  	/*
  	 * Before deleting the entry, remember the next entry as the
  	 * return value.  We need this because pfxlist_onlink_check() above
  	 * might have freed other entries (particularly the old next entry) as
  	 * a side effect (XXX).
  	 */
 diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
 index b05ee80..0d591b5 100644
 --- a/sys/netinet6/nd6_rtr.c
 +++ b/sys/netinet6/nd6_rtr.c
 @@ -2160,21 +2160,21 @@ rt6_deleteroute(struct radix_node *rn, void *arg)
  		return (0);
  
  	/*
  	 * We delete only host route. This means, in particular, we don't
  	 * delete default route.
  	 */
  	if ((rt->rt_flags & RTF_HOST) == 0)
  		return (0);
  
  	return (in6_rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway,
 -	    rt_mask(rt), rt->rt_flags, NULL, rt->rt_fibnum));
 +	    rt_mask(rt), rt->rt_flags | RTF_RNH_LOCKED, NULL, rt->rt_fibnum));
  #undef SIN6
  }
  
  int
  nd6_setdefaultiface(int ifindex)
  {
  	int error = 0;
  
  	if (ifindex < 0 || V_if_index < ifindex)
  		return (EINVAL);
 
 --------------000407050907080508090402
 Content-Type: text/x-diff; name="redir_onlink_9_rev1.diff"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment; filename="redir_onlink_9_rev1.diff"
 
 diff --git a/sys/net/route.c b/sys/net/route.c
 index a629c73..97c631b 100644
 --- a/sys/net/route.c
 +++ b/sys/net/route.c
 @@ -547,27 +547,34 @@ rtredirect_fib(struct sockaddr *dst,
  		error = ENETUNREACH;
  		goto out;
  	}
  	rt = rtalloc1_fib(dst, 0, 0UL, fibnum);	/* NB: rt is locked */
  	/*
  	 * If the redirect isn't from our current router for this dst,
  	 * it's either old or wrong.  If it redirects us to ourselves,
  	 * we have a routing loop, perhaps as a result of an interface
  	 * going down recently.
  	 */
 -	if (!(flags & RTF_DONE) && rt &&
 -	     (!sa_equal(src, rt->rt_gateway) || rt->rt_ifa != ifa))
 -		error = EINVAL;
 -	else if (ifa_ifwithaddr_check(gateway))
 +	if (!(flags & RTF_DONE) && rt) {
 +		if (!sa_equal(src, rt->rt_gateway)) {
 +			error = EINVAL;
 +			goto done;
 +		}
 +		if (rt->rt_ifa != ifa && ifa->ifa_addr->sa_family != AF_LINK) {
 +			error = EINVAL;
 +			goto done;
 +		}
 +	}
 +	if ((flags & RTF_GATEWAY) && ifa_ifwithaddr_check(gateway)) {
  		error = EHOSTUNREACH;
 -	if (error)
  		goto done;
 +	}
  	/*
  	 * Create a new entry if we just got back a wildcard entry
  	 * or the lookup failed.  This is necessary for hosts
  	 * which use routing redirects generated by smart gateways
  	 * to dynamically build the routing tables.
  	 */
  	if (rt == NULL || (rt_mask(rt) && rt_mask(rt)->sa_len < 2))
  		goto create;
  	/*
  	 * Don't listen to the redirect if it's
 @@ -576,21 +583,21 @@ rtredirect_fib(struct sockaddr *dst,
  	if (rt->rt_flags & RTF_GATEWAY) {
  		if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) {
  			/*
  			 * Changing from route to net => route to host.
  			 * Create new route, rather than smashing route to net.
  			 */
  		create:
  			rt0 = rt;
  			rt = NULL;
  		
 -			flags |=  RTF_GATEWAY | RTF_DYNAMIC;
 +			flags |= RTF_DYNAMIC;
  			bzero((caddr_t)&info, sizeof(info));
  			info.rti_info[RTAX_DST] = dst;
  			info.rti_info[RTAX_GATEWAY] = gateway;
  			info.rti_info[RTAX_NETMASK] = netmask;
  			info.rti_ifa = ifa;
  			info.rti_flags = flags;
  			if (rt0 != NULL)
  				RT_UNLOCK(rt0);	/* drop lock to avoid LOR with RNH */
  			error = rtrequest1_fib(RTM_ADD, &info, &rt, fibnum);
  			if (rt != NULL) {
 @@ -603,34 +610,37 @@ rtredirect_fib(struct sockaddr *dst,
  				RTFREE(rt0);
  			
  			stat = &V_rtstat.rts_dynamic;
  		} else {
  			struct rtentry *gwrt;
  
  			/*
  			 * Smash the current notion of the gateway to
  			 * this destination.  Should check about netmask!!!
  			 */
 +			if ((flags & RTF_GATEWAY) == 0)
 +				rt->rt_flags &= ~RTF_GATEWAY;
  			rt->rt_flags |= RTF_MODIFIED;
  			flags |= RTF_MODIFIED;
  			stat = &V_rtstat.rts_newgateway;
  			/*
  			 * add the key and gateway (in one malloc'd chunk).
  			 */
  			RT_UNLOCK(rt);
  			RADIX_NODE_HEAD_LOCK(rnh);
  			RT_LOCK(rt);
  			rt_setgate(rt, rt_key(rt), gateway);
  			gwrt = rtalloc1(gateway, 1, RTF_RNH_LOCKED);
  			RADIX_NODE_HEAD_UNLOCK(rnh);
  			EVENTHANDLER_INVOKE(route_redirect_event, rt, gwrt, dst);
 -			RTFREE_LOCKED(gwrt);
 +			if (gwrt)
 +				RTFREE_LOCKED(gwrt);
  		}
  	} else
  		error = EHOSTUNREACH;
  done:
  	if (rt)
  		RTFREE_LOCKED(rt);
  out:
  	if (error)
  		V_rtstat.rts_badredirect++;
  	else if (stat != NULL)
 diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c
 index a1e19d9..1c9a06e 100644
 --- a/sys/netinet6/icmp6.c
 +++ b/sys/netinet6/icmp6.c
 @@ -2460,21 +2460,20 @@ icmp6_redirect_input(struct mbuf *m, int off)
  		is_router = 1;	/* router case */
  	if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0)
  		is_onlink = 1;	/* on-link destination case */
  	if (!is_router && !is_onlink) {
  		nd6log((LOG_ERR,
  		    "ICMP6 redirect rejected; "
  		    "neither router case nor onlink case: %s\n",
  		    icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
  		goto bad;
  	}
 -	/* validation passed */
  
  	icmp6len -= sizeof(*nd_rd);
  	nd6_option_init(nd_rd + 1, icmp6len, &ndopts);
  	if (nd6_options(&ndopts) < 0) {
  		nd6log((LOG_INFO, "%s: invalid ND option, rejected: %s\n",
  		    __func__, icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
  		/* nd6_options have incremented stats */
  		goto freeit;
  	}
  
 @@ -2485,47 +2484,62 @@ icmp6_redirect_input(struct mbuf *m, int off)
  
  	if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
  		nd6log((LOG_INFO, "%s: lladdrlen mismatch for %s "
  		    "(if %d, icmp6 packet %d): %s\n",
  		    __func__, ip6_sprintf(ip6buf, &redtgt6),
  		    ifp->if_addrlen, lladdrlen - 2,
  		    icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
  		goto bad;
  	}
  
 +	/* Validation passed. */
 +
  	/* RFC 2461 8.3 */
  	nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
  	    is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER);
  
 -	if (!is_onlink) {	/* better router case.  perform rtredirect. */
 -		/* perform rtredirect */
 +	/*
 +	 * Install a gateway route in the better-router case
 +	 * or an interface route in the on-link-destination case.
 +	 */
 +	{
  		struct sockaddr_in6 sdst;
  		struct sockaddr_in6 sgw;
  		struct sockaddr_in6 ssrc;
 +		struct sockaddr *gw;
 +		int rt_flags;
  		u_int fibnum;
  
  		bzero(&sdst, sizeof(sdst));
 -		bzero(&sgw, sizeof(sgw));
  		bzero(&ssrc, sizeof(ssrc));
 -		sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6;
 -		sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len =
 -			sizeof(struct sockaddr_in6);
 -		bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr));
 +		sdst.sin6_family = ssrc.sin6_family = AF_INET6;
 +		sdst.sin6_len = ssrc.sin6_len = sizeof(struct sockaddr_in6);
  		bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
  		bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr));
 +		rt_flags = RTF_HOST;
 +		if (is_router) {
 +			bzero(&sgw, sizeof(sgw));
 +			sgw.sin6_family = AF_INET6;
 +			sgw.sin6_len = sizeof(struct sockaddr_in6);
 +			bcopy(&redtgt6, &sgw.sin6_addr,
 +				sizeof(struct in6_addr));
 +			gw = (struct sockaddr *)&sgw;
 +			rt_flags |= RTF_GATEWAY;
 +		} else
 +			gw = ifp->if_addr->ifa_addr;
  		for (fibnum = 0; fibnum < rt_numfibs; fibnum++)
 -			in6_rtredirect((struct sockaddr *)&sdst,
 -			    (struct sockaddr *)&sgw, (struct sockaddr *)NULL,
 -			    RTF_GATEWAY | RTF_HOST, (struct sockaddr *)&ssrc,
 -			    fibnum);
 +			in6_rtredirect((struct sockaddr *)&sdst, gw,
 +			    (struct sockaddr *)NULL, rt_flags,
 +			    (struct sockaddr *)&ssrc, fibnum);
  	}
 -	/* finally update cached route in each socket via pfctlinput */
 +
 +	/* Finally, update the cached route in each socket via pfctlinput. */
      {
  	struct sockaddr_in6 sdst;
  
  	bzero(&sdst, sizeof(sdst));
  	sdst.sin6_family = AF_INET6;
  	sdst.sin6_len = sizeof(struct sockaddr_in6);
  	bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
  	pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst);
  #ifdef IPSEC
  	key_sa_routechange((struct sockaddr *)&sdst);
 diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
 index 9fe9ec8..3523acf 100644
 --- a/sys/netinet6/nd6.c
 +++ b/sys/netinet6/nd6.c
 @@ -992,20 +992,22 @@ nd6_is_addr_neighbor(struct sockaddr_in6 *addr, struct ifnet *ifp)
   * Since the function would cause significant changes in the kernel, DO NOT
   * make it global, unless you have a strong reason for the change, and are sure
   * that the change is safe.
   */
  static struct llentry *
  nd6_free(struct llentry *ln, int gc)
  {
          struct llentry *next;
  	struct nd_defrouter *dr;
  	struct ifnet *ifp;
 +	struct radix_node_head *rnh;
 +	int fibnum;
  
  	LLE_WLOCK_ASSERT(ln);
  
  	/*
  	 * we used to have pfctlinput(PRC_HOSTDEAD) here.
  	 * even though it is not harmful, it was not really necessary.
  	 */
  
  	/* cancel timer */
  	nd6_llinfo_settimer_locked(ln, -1);
 @@ -1085,20 +1087,42 @@ nd6_free(struct llentry *ln, int gc)
  			 * the check now.
  			 */
  			pfxlist_onlink_check();
  
  			/*
  			 * Refresh default router list.
  			 */
  			defrouter_select();
  		}
  
 +		/*
 +		 * If this entry was added by an on-link redirect,
 +		 * remove the corresponding host route.
 +		 */
 +		for (fibnum = 0; fibnum < rt_numfibs; fibnum++) {
 +			rnh = rt_tables_get_rnh(fibnum, AF_INET6);
 +			if (rnh) {
 +				struct rtentry *rt;
 +
 +				RADIX_NODE_HEAD_LOCK(rnh);
 +				rt = in6_rtalloc1(L3_ADDR(ln), 0,
 +					RTF_RNH_LOCKED, fibnum);
 +				if (rt) {
 +					if (rt->rt_flags ==
 +					    (RTF_UP|RTF_HOST|RTF_DYNAMIC))
 +						rtexpunge(rt);
 +					RTFREE_LOCKED(rt);
 +				}
 +				RADIX_NODE_HEAD_UNLOCK(rnh);
 +			}
 +		}
 +
  		if (ln->ln_router || dr)
  			LLE_WLOCK(ln);
  	}
  
  	/*
  	 * Before deleting the entry, remember the next entry as the
  	 * return value.  We need this because pfxlist_onlink_check() above
  	 * might have freed other entries (particularly the old next entry) as
  	 * a side effect (XXX).
  	 */
 diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
 index 792b5c9..f19beeb 100644
 --- a/sys/netinet6/nd6_rtr.c
 +++ b/sys/netinet6/nd6_rtr.c
 @@ -2156,21 +2156,21 @@ rt6_deleteroute(struct radix_node *rn, void *arg)
  		return (0);
  
  	/*
  	 * We delete only host route. This means, in particular, we don't
  	 * delete default route.
  	 */
  	if ((rt->rt_flags & RTF_HOST) == 0)
  		return (0);
  
  	return (in6_rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway,
 -	    rt_mask(rt), rt->rt_flags, NULL, rt->rt_fibnum));
 +	    rt_mask(rt), rt->rt_flags | RTF_RNH_LOCKED, NULL, rt->rt_fibnum));
  #undef SIN6
  }
  
  int
  nd6_setdefaultiface(int ifindex)
  {
  	int error = 0;
  
  	if (ifindex < 0 || V_if_index < ifindex)
  		return (EINVAL);
 
 --------------000407050907080508090402--
Responsible-Changed-From-To: bz->gnn 
Responsible-Changed-By: bz 
Responsible-Changed-When: Sun May 18 05:00:20 UTC 2014 
Responsible-Changed-Why:  
I shall not use bugzilla (at least until we will have a CLI). 

http://www.freebsd.org/cgi/query-pr.cgi?pr=152791 
>Unformatted:
