]> err.no Git - linux-2.6/blobdiff - net/ipv6/route.c
Merge ../linux-2.6
[linux-2.6] / net / ipv6 / route.c
index 0f30ee3d94eacd3010fbe40ebd442412675f2de3..8a777932786d7d4c0975fd6941489039abad3d06 100644 (file)
@@ -280,10 +280,13 @@ static int inline rt6_check_neigh(struct rt6_info *rt)
 {
        struct neighbour *neigh = rt->rt6i_nexthop;
        int m = 0;
-       if (neigh) {
+       if (rt->rt6i_flags & RTF_NONEXTHOP ||
+           !(rt->rt6i_flags & RTF_GATEWAY))
+               m = 1;
+       else if (neigh) {
                read_lock_bh(&neigh->lock);
                if (neigh->nud_state & NUD_VALID)
-                       m = 1;
+                       m = 2;
                read_unlock_bh(&neigh->lock);
        }
        return m;
@@ -292,15 +295,18 @@ static int inline rt6_check_neigh(struct rt6_info *rt)
 static int rt6_score_route(struct rt6_info *rt, int oif,
                           int strict)
 {
-       int m = rt6_check_dev(rt, oif);
+       int m, n;
+               
+       m = rt6_check_dev(rt, oif);
        if (!m && (strict & RT6_SELECT_F_IFACE))
                return -1;
 #ifdef CONFIG_IPV6_ROUTER_PREF
        m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
 #endif
-       if (rt6_check_neigh(rt))
+       n = rt6_check_neigh(rt);
+       if (n > 1)
                m |= 16;
-       else if (strict & RT6_SELECT_F_REACHABLE)
+       else if (!n && strict & RT6_SELECT_F_REACHABLE)
                return -1;
        return m;
 }
@@ -317,7 +323,7 @@ static struct rt6_info *rt6_select(struct rt6_info **head, int oif,
                  __FUNCTION__, head, head ? *head : NULL, oif);
 
        for (rt = rt0, metric = rt0->rt6i_metric;
-            rt && rt->rt6i_metric == metric;
+            rt && rt->rt6i_metric == metric && (!last || rt != rt0);
             rt = rt->u.next) {
                int m;
 
@@ -343,9 +349,12 @@ static struct rt6_info *rt6_select(struct rt6_info **head, int oif,
            (strict & RT6_SELECT_F_REACHABLE) &&
            last && last != rt0) {
                /* no entries matched; do round-robin */
+               static spinlock_t lock = SPIN_LOCK_UNLOCKED;
+               spin_lock(&lock);
                *head = rt0->u.next;
                rt0->u.next = last->u.next;
                last->u.next = rt0;
+               spin_unlock(&lock);
        }
 
        RT6_TRACE("%s() => %p, score=%d\n",
@@ -1144,59 +1153,63 @@ static int ip6_route_del(struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh, void *_r
 void rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr,
                  struct neighbour *neigh, u8 *lladdr, int on_link)
 {
-       struct rt6_info *rt, *nrt;
-
-       /* Locate old route to this destination. */
-       rt = rt6_lookup(dest, NULL, neigh->dev->ifindex, 1);
-
-       if (rt == NULL)
-               return;
-
-       if (neigh->dev != rt->rt6i_dev)
-               goto out;
+       struct rt6_info *rt, *nrt = NULL;
+       int strict;
+       struct fib6_node *fn;
 
        /*
-        * Current route is on-link; redirect is always invalid.
-        * 
-        * Seems, previous statement is not true. It could
-        * be node, which looks for us as on-link (f.e. proxy ndisc)
-        * But then router serving it might decide, that we should
-        * know truth 8)8) --ANK (980726).
+        * Get the "current" route for this destination and
+        * check if the redirect has come from approriate router.
+        *
+        * RFC 2461 specifies that redirects should only be
+        * accepted if they come from the nexthop to the target.
+        * Due to the way the routes are chosen, this notion
+        * is a bit fuzzy and one might need to check all possible
+        * routes.
         */
-       if (!(rt->rt6i_flags&RTF_GATEWAY))
-               goto out;
+       strict = ipv6_addr_type(dest) & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL);
 
-       /*
-        *      RFC 2461 specifies that redirects should only be
-        *      accepted if they come from the nexthop to the target.
-        *      Due to the way default routers are chosen, this notion
-        *      is a bit fuzzy and one might need to check all default
-        *      routers.
-        */
-       if (!ipv6_addr_equal(saddr, &rt->rt6i_gateway)) {
-               if (rt->rt6i_flags & RTF_DEFAULT) {
-                       struct rt6_info *rt1;
-
-                       read_lock(&rt6_lock);
-                       for (rt1 = ip6_routing_table.leaf; rt1; rt1 = rt1->u.next) {
-                               if (ipv6_addr_equal(saddr, &rt1->rt6i_gateway)) {
-                                       dst_hold(&rt1->u.dst);
-                                       dst_release(&rt->u.dst);
-                                       read_unlock(&rt6_lock);
-                                       rt = rt1;
-                                       goto source_ok;
-                               }
-                       }
-                       read_unlock(&rt6_lock);
+       read_lock_bh(&rt6_lock);
+       fn = fib6_lookup(&ip6_routing_table, dest, NULL);
+restart:
+       for (rt = fn->leaf; rt; rt = rt->u.next) {
+               /*
+                * Current route is on-link; redirect is always invalid.
+                *
+                * Seems, previous statement is not true. It could
+                * be node, which looks for us as on-link (f.e. proxy ndisc)
+                * But then router serving it might decide, that we should
+                * know truth 8)8) --ANK (980726).
+                */
+               if (rt6_check_expired(rt))
+                       continue;
+               if (!(rt->rt6i_flags & RTF_GATEWAY))
+                       continue;
+               if (neigh->dev != rt->rt6i_dev)
+                       continue;
+               if (!ipv6_addr_equal(saddr, &rt->rt6i_gateway))
+                       continue;
+               break;
+       }
+       if (rt)
+               dst_hold(&rt->u.dst);
+       else if (strict) {
+               while ((fn = fn->parent) != NULL) {
+                       if (fn->fn_flags & RTN_ROOT)
+                               break;
+                       if (fn->fn_flags & RTN_RTINFO)
+                               goto restart;
                }
+       }
+       read_unlock_bh(&rt6_lock);
+
+       if (!rt) {
                if (net_ratelimit())
                        printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
                               "for redirect target\n");
-               goto out;
+               return;
        }
 
-source_ok:
-
        /*
         *      We have finally decided to accept it.
         */
@@ -1403,6 +1416,9 @@ static struct rt6_info *rt6_add_route_info(struct in6_addr *prefix, int prefixle
        ipv6_addr_copy(&rtmsg.rtmsg_gateway, gwaddr);
        rtmsg.rtmsg_metric = 1024;
        rtmsg.rtmsg_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | RTF_UP | RTF_PREF(pref);
+       /* We should treat it as a default route if prefix length is 0. */
+       if (!prefixlen)
+               rtmsg.rtmsg_flags |= RTF_DEFAULT;
        rtmsg.rtmsg_ifindex = ifindex;
 
        ip6_route_add(&rtmsg, NULL, NULL, NULL);
@@ -1870,11 +1886,10 @@ int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
                /*
                 * 2. allocate and initialize walker.
                 */
-               w = kmalloc(sizeof(*w), GFP_ATOMIC);
+               w = kzalloc(sizeof(*w), GFP_ATOMIC);
                if (w == NULL)
                        return -ENOMEM;
                RT6_TRACE("dump<%p", w);
-               memset(w, 0, sizeof(*w));
                w->root = &ip6_routing_table;
                w->func = fib6_dump_node;
                w->args = &arg;