]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/ipv6/route.c
[IPV6]: Always copy rt->u.dst.error when copying a rt6_info.
[linux-2.6-omap-h63xx.git] / net / ipv6 / route.c
index 5d6e9083ca2cfbc659ceb2156b9674cf0f4e0d73..aa96be860e969aa223b22a1704c9f69b73af0dee 100644 (file)
@@ -22,6 +22,8 @@
  *             routers in REACHABLE, STALE, DELAY or PROBE states).
  *             - always select the same router if it is (probably)
  *             reachable.  otherwise, round-robin the list.
+ *     Ville Nuorvala
+ *             Fixed routing subtrees.
  */
 
 #include <linux/capability.h>
@@ -74,9 +76,6 @@
 
 #define CLONE_OFFLINK_ROUTE 0
 
-#define RT6_SELECT_F_IFACE     0x1
-#define RT6_SELECT_F_REACHABLE 0x2
-
 static int ip6_rt_max_size = 4096;
 static int ip6_rt_gc_min_interval = HZ / 2;
 static int ip6_rt_gc_timeout = 60*HZ;
@@ -338,7 +337,7 @@ static int rt6_score_route(struct rt6_info *rt, int oif,
        int m, n;
                
        m = rt6_check_dev(rt, oif);
-       if (!m && (strict & RT6_SELECT_F_IFACE))
+       if (!m && (strict & RT6_LOOKUP_F_IFACE))
                return -1;
 #ifdef CONFIG_IPV6_ROUTER_PREF
        m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
@@ -346,7 +345,7 @@ static int rt6_score_route(struct rt6_info *rt, int oif,
        n = rt6_check_neigh(rt);
        if (n > 1)
                m |= 16;
-       else if (!n && strict & RT6_SELECT_F_REACHABLE)
+       else if (!n && strict & RT6_LOOKUP_F_REACHABLE)
                return -1;
        return m;
 }
@@ -386,7 +385,7 @@ static struct rt6_info *rt6_select(struct rt6_info **head, int oif,
        }
 
        if (!match &&
-           (strict & RT6_SELECT_F_REACHABLE) &&
+           (strict & RT6_LOOKUP_F_REACHABLE) &&
            last && last != rt0) {
                /* no entries matched; do round-robin */
                static DEFINE_SPINLOCK(lock);
@@ -481,17 +480,23 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
 }
 #endif
 
-#define BACKTRACK() \
-if (rt == &ip6_null_entry && flags & RT6_F_STRICT) { \
-       while ((fn = fn->parent) != NULL) { \
-               if (fn->fn_flags & RTN_TL_ROOT) { \
-                       dst_hold(&rt->u.dst); \
-                       goto out; \
+#define BACKTRACK(saddr) \
+do { \
+       if (rt == &ip6_null_entry) { \
+               struct fib6_node *pn; \
+               while (1) { \
+                       if (fn->fn_flags & RTN_TL_ROOT) \
+                               goto out; \
+                       pn = fn->parent; \
+                       if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
+                               fn = fib6_lookup(pn->subtree, NULL, saddr); \
+                       else \
+                               fn = pn; \
+                       if (fn->fn_flags & RTN_RTINFO) \
+                               goto restart; \
                } \
-               if (fn->fn_flags & RTN_RTINFO) \
-                       goto restart; \
        } \
-}
+} while(0)
 
 static struct rt6_info *ip6_pol_route_lookup(struct fib6_table *table,
                                             struct flowi *fl, int flags)
@@ -503,10 +508,10 @@ static struct rt6_info *ip6_pol_route_lookup(struct fib6_table *table,
        fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
 restart:
        rt = fn->leaf;
-       rt = rt6_device_match(rt, fl->oif, flags & RT6_F_STRICT);
-       BACKTRACK();
-       dst_hold(&rt->u.dst);
+       rt = rt6_device_match(rt, fl->oif, flags);
+       BACKTRACK(&fl->fl6_src);
 out:
+       dst_hold(&rt->u.dst);
        read_unlock_bh(&table->tb6_lock);
 
        rt->u.dst.lastuse = jiffies;
@@ -524,12 +529,16 @@ struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr,
                .nl_u = {
                        .ip6_u = {
                                .daddr = *daddr,
-                               /* TODO: saddr */
                        },
                },
        };
        struct dst_entry *dst;
-       int flags = strict ? RT6_F_STRICT : 0;
+       int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
+
+       if (saddr) {
+               memcpy(&fl.fl6_src, saddr, sizeof(*saddr));
+               flags |= RT6_LOOKUP_F_HAS_SADDR;
+       }
 
        dst = fib6_rule_lookup(&fl, flags, ip6_pol_route_lookup);
        if (dst->error == 0)
@@ -609,8 +618,6 @@ static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, struct in6_addr *d
                ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
                rt->rt6i_dst.plen = 128;
                rt->rt6i_flags |= RTF_CACHE;
-               if (rt->rt6i_flags & RTF_REJECT)
-                       rt->u.dst.error = ort->u.dst.error;
                rt->u.dst.flags |= DST_HOST;
                rt->rt6i_nexthop = neigh_clone(ort->rt6i_nexthop);
        }
@@ -625,10 +632,9 @@ static struct rt6_info *ip6_pol_route_input(struct fib6_table *table,
        int strict = 0;
        int attempts = 3;
        int err;
-       int reachable = RT6_SELECT_F_REACHABLE;
+       int reachable = RT6_LOOKUP_F_REACHABLE;
 
-       if (flags & RT6_F_STRICT)
-               strict = RT6_SELECT_F_IFACE;
+       strict |= flags & RT6_LOOKUP_F_IFACE;
 
 relookup:
        read_lock_bh(&table->tb6_lock);
@@ -638,7 +644,7 @@ restart_2:
 
 restart:
        rt = rt6_select(&fn->leaf, fl->iif, strict | reachable);
-       BACKTRACK();
+       BACKTRACK(&fl->fl6_src);
        if (rt == &ip6_null_entry ||
            rt->rt6i_flags & RTF_CACHE)
                goto out;
@@ -693,21 +699,24 @@ out2:
 void ip6_route_input(struct sk_buff *skb)
 {
        struct ipv6hdr *iph = skb->nh.ipv6h;
+       int flags = RT6_LOOKUP_F_HAS_SADDR;
        struct flowi fl = {
                .iif = skb->dev->ifindex,
                .nl_u = {
                        .ip6_u = {
                                .daddr = iph->daddr,
                                .saddr = iph->saddr,
+#ifdef CONFIG_IPV6_ROUTE_FWMARK
+                               .fwmark = skb->nfmark,
+#endif
                                .flowlabel = (* (u32 *) iph)&IPV6_FLOWINFO_MASK,
                        },
                },
                .proto = iph->nexthdr,
        };
-       int flags = 0;
 
        if (rt6_need_strict(&iph->daddr))
-               flags |= RT6_F_STRICT;
+               flags |= RT6_LOOKUP_F_IFACE;
 
        skb->dst = fib6_rule_lookup(&fl, flags, ip6_pol_route_input);
 }
@@ -720,10 +729,9 @@ static struct rt6_info *ip6_pol_route_output(struct fib6_table *table,
        int strict = 0;
        int attempts = 3;
        int err;
-       int reachable = RT6_SELECT_F_REACHABLE;
+       int reachable = RT6_LOOKUP_F_REACHABLE;
 
-       if (flags & RT6_F_STRICT)
-               strict = RT6_SELECT_F_IFACE;
+       strict |= flags & RT6_LOOKUP_F_IFACE;
 
 relookup:
        read_lock_bh(&table->tb6_lock);
@@ -733,7 +741,7 @@ restart_2:
 
 restart:
        rt = rt6_select(&fn->leaf, fl->oif, strict | reachable);
-       BACKTRACK();
+       BACKTRACK(&fl->fl6_src);
        if (rt == &ip6_null_entry ||
            rt->rt6i_flags & RTF_CACHE)
                goto out;
@@ -789,7 +797,10 @@ struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl)
        int flags = 0;
 
        if (rt6_need_strict(&fl->fl6_dst))
-               flags |= RT6_F_STRICT;
+               flags |= RT6_LOOKUP_F_IFACE;
+
+       if (!ipv6_addr_any(&fl->fl6_src))
+               flags |= RT6_LOOKUP_F_HAS_SADDR;
 
        return fib6_rule_lookup(fl, flags, ip6_pol_route_output);
 }
@@ -1279,18 +1290,18 @@ static int ip6_route_del(struct fib6_config *cfg)
 /*
  *     Handle redirects
  */
-void rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr,
-                 struct neighbour *neigh, u8 *lladdr, int on_link)
+struct ip6rd_flowi {
+       struct flowi fl;
+       struct in6_addr gateway;
+};
+
+static struct rt6_info *__ip6_route_redirect(struct fib6_table *table,
+                                            struct flowi *fl,
+                                            int flags)
 {
-       struct rt6_info *rt, *nrt = NULL;
+       struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl;
+       struct rt6_info *rt;
        struct fib6_node *fn;
-       struct fib6_table *table;
-       struct netevent_redirect netevent;
-
-       /* TODO: Very lazy, might need to check all tables */
-       table = fib6_get_table(RT6_TABLE_MAIN);
-       if (table == NULL)
-               return;
 
        /*
         * Get the "current" route for this destination and
@@ -1304,7 +1315,7 @@ void rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr,
         */
 
        read_lock_bh(&table->tb6_lock);
-       fn = fib6_lookup(&table->tb6_root, dest, NULL);
+       fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
 restart:
        for (rt = fn->leaf; rt; rt = rt->u.next) {
                /*
@@ -1319,29 +1330,63 @@ restart:
                        continue;
                if (!(rt->rt6i_flags & RTF_GATEWAY))
                        continue;
-               if (neigh->dev != rt->rt6i_dev)
+               if (fl->oif != rt->rt6i_dev->ifindex)
                        continue;
-               if (!ipv6_addr_equal(saddr, &rt->rt6i_gateway))
+               if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
                        continue;
                break;
        }
-       if (rt)
-               dst_hold(&rt->u.dst);
-       else if (rt6_need_strict(dest)) {
-               while ((fn = fn->parent) != NULL) {
-                       if (fn->fn_flags & RTN_ROOT)
-                               break;
-                       if (fn->fn_flags & RTN_RTINFO)
-                               goto restart;
-               }
-       }
+
+       if (!rt)
+               rt = &ip6_null_entry;
+       BACKTRACK(&fl->fl6_src);
+out:
+       dst_hold(&rt->u.dst);
+
        read_unlock_bh(&table->tb6_lock);
 
-       if (!rt) {
+       return rt;
+};
+
+static struct rt6_info *ip6_route_redirect(struct in6_addr *dest,
+                                          struct in6_addr *src,
+                                          struct in6_addr *gateway,
+                                          struct net_device *dev)
+{
+       int flags = RT6_LOOKUP_F_HAS_SADDR;
+       struct ip6rd_flowi rdfl = {
+               .fl = {
+                       .oif = dev->ifindex,
+                       .nl_u = {
+                               .ip6_u = {
+                                       .daddr = *dest,
+                                       .saddr = *src,
+                               },
+                       },
+               },
+               .gateway = *gateway,
+       };
+
+       if (rt6_need_strict(dest))
+               flags |= RT6_LOOKUP_F_IFACE;
+
+       return (struct rt6_info *)fib6_rule_lookup((struct flowi *)&rdfl, flags, __ip6_route_redirect);
+}
+
+void rt6_redirect(struct in6_addr *dest, struct in6_addr *src,
+                 struct in6_addr *saddr,
+                 struct neighbour *neigh, u8 *lladdr, int on_link)
+{
+       struct rt6_info *rt, *nrt = NULL;
+       struct netevent_redirect netevent;
+
+       rt = ip6_route_redirect(dest, src, saddr, neigh->dev);
+
+       if (rt == &ip6_null_entry) {
                if (net_ratelimit())
                        printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
                               "for redirect target\n");
-               return;
+               goto out;
        }
 
        /*
@@ -1493,6 +1538,7 @@ static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
                rt->u.dst.output = ort->u.dst.output;
 
                memcpy(rt->u.dst.metrics, ort->u.dst.metrics, RTAX_MAX*sizeof(u32));
+               rt->u.dst.error = ort->u.dst.error;
                rt->u.dst.dev = ort->u.dst.dev;
                if (rt->u.dst.dev)
                        dev_hold(rt->u.dst.dev);
@@ -1831,7 +1877,7 @@ void rt6_mtu_change(struct net_device *dev, unsigned mtu)
 }
 
 static struct nla_policy rtm_ipv6_policy[RTA_MAX+1] __read_mostly = {
-       [RTA_GATEWAY]           = { .minlen = sizeof(struct in6_addr) },
+       [RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
        [RTA_OIF]               = { .type = NLA_U32 },
        [RTA_IIF]               = { .type = NLA_U32 },
        [RTA_PRIORITY]          = { .type = NLA_U32 },
@@ -2385,13 +2431,9 @@ void __init ip6_route_init(void)
 {
        struct proc_dir_entry *p;
 
-       ip6_dst_ops.kmem_cachep = kmem_cache_create("ip6_dst_cache",
-                                                    sizeof(struct rt6_info),
-                                                    0, SLAB_HWCACHE_ALIGN,
-                                                    NULL, NULL);
-       if (!ip6_dst_ops.kmem_cachep)
-               panic("cannot create ip6_dst_cache");
-
+       ip6_dst_ops.kmem_cachep =
+               kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
+                                 SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
        fib6_init();
 #ifdef         CONFIG_PROC_FS
        p = proc_net_create("ipv6_route", 0, rt6_proc_info);