Author: vangyzen
Date: Mon Sep 14 19:17:25 2015
New Revision: 287798
URL: https://svnweb.freebsd.org/changeset/base/287798

Log:
  Fix the handling of IPv6 On-Link Redirects.
  
  On receipt of a redirect message, install an interface route for the
  redirected destination.  On removal of the corresponding Neighbor Cache
  entry, remove the interface route.
  
  This requires changes in rtredirect_fib() to cope with an AF_LINK
  address for the gateway and with the absence of RTF_GATEWAY.
  
  This fixes the "Redirected On-Link" test cases in the Tahi IPv6 Ready Logo
  Phase 2 test suite.
  
  Unrelated to the above, fix a recursion on the radix node head lock
  triggered by the Tahi Redirected to Alternate Router test cases.
  
  When I first wrote this patch in October 2012, all Section 2
  (Neighbor Discovery) test cases passed on 10-CURRENT, 9-STABLE,
  and 8-STABLE.  cem@ recently rebased the 10.x patch onto head and reported
  that it passes Tahi.  (Thanks!)
  
  These other test cases also passed in 2012:
  
  * the RTF_MODIFIED case, with IPv4 and IPv6 (using a
    RTF_HOST|RTF_GATEWAY route for the destination)
  
  * the redirected-to-self case, with IPv4 and IPv6
  
  * a valid IPv4 redirect
  
  All testing in 2012 was done with WITNESS and INVARIANTS.
  
  Tested by:    EMC / Isilon Storage Division via Conrad Meyer (cem) in 2015,
                Mark Kelley <[email protected]> in 2012,
                TC Telkamp <[email protected]> in 2012
  PR:           152791
  Reviewed by:  melifaro (current rev), bz (earlier rev)
  Approved by:  kib (mentor)
  MFC after:    1 month
  Relnotes:     yes
  Sponsored by: Dell Inc.
  Differential Revision: https://reviews.freebsd.org/D3602

Modified:
  head/sys/net/if_llatbl.h
  head/sys/net/route.c
  head/sys/netinet6/icmp6.c
  head/sys/netinet6/nd6.c
  head/sys/netinet6/nd6_rtr.c

Modified: head/sys/net/if_llatbl.h
==============================================================================
--- head/sys/net/if_llatbl.h    Mon Sep 14 18:59:01 2015        (r287797)
+++ head/sys/net/if_llatbl.h    Mon Sep 14 19:17:25 2015        (r287798)
@@ -183,6 +183,7 @@ MALLOC_DECLARE(M_LLTABLE);
 #define        LLE_STATIC      0x0002  /* entry is static */
 #define        LLE_IFADDR      0x0004  /* entry is interface addr */
 #define        LLE_VALID       0x0008  /* ll_addr is valid */
+#define        LLE_REDIRECT    0x0010  /* installed by redirect; has host 
rtentry */
 #define        LLE_PUB         0x0020  /* publish entry ??? */
 #define        LLE_LINKED      0x0040  /* linked to lookup structure */
 /* LLE request flags */

Modified: head/sys/net/route.c
==============================================================================
--- head/sys/net/route.c        Mon Sep 14 18:59:01 2015        (r287797)
+++ head/sys/net/route.c        Mon Sep 14 19:17:25 2015        (r287798)
@@ -584,13 +584,20 @@ rtredirect_fib(struct sockaddr *dst,
         * 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
@@ -613,7 +620,7 @@ rtredirect_fib(struct sockaddr *dst,
                        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;
@@ -640,6 +647,8 @@ rtredirect_fib(struct sockaddr *dst,
                         * 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;
@@ -653,7 +662,8 @@ rtredirect_fib(struct sockaddr *dst,
                        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;

Modified: head/sys/netinet6/icmp6.c
==============================================================================
--- head/sys/netinet6/icmp6.c   Mon Sep 14 18:59:01 2015        (r287797)
+++ head/sys/netinet6/icmp6.c   Mon Sep 14 19:17:25 2015        (r287798)
@@ -2434,27 +2434,39 @@ icmp6_redirect_input(struct mbuf *m, int
        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 */
     {

Modified: head/sys/netinet6/nd6.c
==============================================================================
--- head/sys/netinet6/nd6.c     Mon Sep 14 18:59:01 2015        (r287797)
+++ head/sys/netinet6/nd6.c     Mon Sep 14 19:17:25 2015        (r287798)
@@ -132,6 +132,7 @@ static void nd6_setmtu0(struct ifnet *, 
 static void nd6_slowtimo(void *);
 static int regen_tmpaddr(struct in6_ifaddr *);
 static struct llentry *nd6_free(struct llentry *, int);
+static void nd6_free_redirect(const struct llentry *);
 static void nd6_llinfo_timer(void *);
 static void clear_llinfo_pqueue(struct llentry *);
 static void nd6_rtrequest(int, struct rtentry *, struct rt_addrinfo *);
@@ -1223,6 +1224,13 @@ nd6_free(struct llentry *ln, int gc)
                        defrouter_select();
                }
 
+               /*
+                * If this entry was added by an on-link redirect, remove the
+                * corresponding host route.
+                */
+               if (ln->la_flags & LLE_REDIRECT)
+                       nd6_free_redirect(ln);
+
                if (ln->ln_router || dr)
                        LLE_WLOCK(ln);
        }
@@ -1256,6 +1264,36 @@ nd6_free(struct llentry *ln, int gc)
 }
 
 /*
+ * Remove the rtentry for the given llentry,
+ * both of which were installed by a redirect.
+ */
+static void
+nd6_free_redirect(const struct llentry *ln)
+{
+       int fibnum;
+       struct rtentry *rt;
+       struct radix_node_head *rnh;
+       struct sockaddr_in6 sin6;
+
+       lltable_fill_sa_entry(ln, (struct sockaddr *)&sin6);
+       for (fibnum = 0; fibnum < rt_numfibs; fibnum++) {
+               rnh = rt_tables_get_rnh(fibnum, AF_INET6);
+               if (rnh == NULL)
+                       continue;
+
+               RADIX_NODE_HEAD_LOCK(rnh);
+               rt = in6_rtalloc1((struct sockaddr *)&sin6, 0,
+                   RTF_RNH_LOCKED, fibnum);
+               if (rt) {
+                       if (rt->rt_flags == (RTF_UP | RTF_HOST | RTF_DYNAMIC))
+                               rt_expunge(rnh, rt);
+                       RTFREE_LOCKED(rt);
+               }
+               RADIX_NODE_HEAD_UNLOCK(rnh);
+       }
+}
+
+/*
  * Upper-layer reachability hint for Neighbor Unreachability Detection.
  *
  * XXX cost-effective methods?
@@ -1746,8 +1784,11 @@ nd6_cache_lladdr(struct ifnet *ifp, stru
                 */
                if (code == ND_REDIRECT_ROUTER)
                        ln->ln_router = 1;
-               else if (is_newentry) /* (6-7) */
-                       ln->ln_router = 0;
+               else {
+                       if (is_newentry) /* (6-7) */
+                               ln->ln_router = 0;
+                       ln->la_flags |= LLE_REDIRECT;
+               }
                break;
        case ND_ROUTER_SOLICIT:
                /*

Modified: head/sys/netinet6/nd6_rtr.c
==============================================================================
--- head/sys/netinet6/nd6_rtr.c Mon Sep 14 18:59:01 2015        (r287797)
+++ head/sys/netinet6/nd6_rtr.c Mon Sep 14 19:17:25 2015        (r287798)
@@ -2105,7 +2105,7 @@ rt6_deleteroute(struct rtentry *rt, void
                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
 }
 
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to