RxRPC: Error handling for rxrpc_alloc_connection()
[safe/jmp/linux-2.6] / net / ipv6 / route.c
index 776871e..032a5ec 100644 (file)
@@ -98,7 +98,7 @@ static struct rt6_info *rt6_get_route_info(struct net *net,
 
 static struct dst_ops ip6_dst_ops_template = {
        .family                 =       AF_INET6,
-       .protocol               =       __constant_htons(ETH_P_IPV6),
+       .protocol               =       cpu_to_be16(ETH_P_IPV6),
        .gc                     =       ip6_dst_gc,
        .gc_thresh              =       1024,
        .check                  =       ip6_dst_check,
@@ -108,7 +108,6 @@ static struct dst_ops ip6_dst_ops_template = {
        .link_failure           =       ip6_link_failure,
        .update_pmtu            =       ip6_rt_update_pmtu,
        .local_out              =       __ip6_local_out,
-       .entry_size             =       sizeof(struct rt6_info),
        .entries                =       ATOMIC_INIT(0),
 };
 
@@ -118,11 +117,10 @@ static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu)
 
 static struct dst_ops ip6_dst_blackhole_ops = {
        .family                 =       AF_INET6,
-       .protocol               =       __constant_htons(ETH_P_IPV6),
+       .protocol               =       cpu_to_be16(ETH_P_IPV6),
        .destroy                =       ip6_dst_destroy,
        .check                  =       ip6_dst_check,
        .update_pmtu            =       ip6_rt_blackhole_update_pmtu,
-       .entry_size             =       sizeof(struct rt6_info),
        .entries                =       ATOMIC_INIT(0),
 };
 
@@ -139,6 +137,7 @@ static struct rt6_info ip6_null_entry_template = {
                }
        },
        .rt6i_flags     = (RTF_REJECT | RTF_NONEXTHOP),
+       .rt6i_protocol  = RTPROT_KERNEL,
        .rt6i_metric    = ~(u32) 0,
        .rt6i_ref       = ATOMIC_INIT(1),
 };
@@ -161,6 +160,7 @@ static struct rt6_info ip6_prohibit_entry_template = {
                }
        },
        .rt6i_flags     = (RTF_REJECT | RTF_NONEXTHOP),
+       .rt6i_protocol  = RTPROT_KERNEL,
        .rt6i_metric    = ~(u32) 0,
        .rt6i_ref       = ATOMIC_INIT(1),
 };
@@ -178,6 +178,7 @@ static struct rt6_info ip6_blk_hole_entry_template = {
                }
        },
        .rt6i_flags     = (RTF_REJECT | RTF_NONEXTHOP),
+       .rt6i_protocol  = RTPROT_KERNEL,
        .rt6i_metric    = ~(u32) 0,
        .rt6i_ref       = ATOMIC_INIT(1),
 };
@@ -629,6 +630,9 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad
        rt = ip6_rt_copy(ort);
 
        if (rt) {
+               struct neighbour *neigh;
+               int attempts = !in_softirq();
+
                if (!(rt->rt6i_flags&RTF_GATEWAY)) {
                        if (rt->rt6i_dst.plen != 128 &&
                            ipv6_addr_equal(&rt->rt6i_dst.addr, daddr))
@@ -648,7 +652,35 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad
                }
 #endif
 
-               rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+       retry:
+               neigh = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+               if (IS_ERR(neigh)) {
+                       struct net *net = dev_net(rt->rt6i_dev);
+                       int saved_rt_min_interval =
+                               net->ipv6.sysctl.ip6_rt_gc_min_interval;
+                       int saved_rt_elasticity =
+                               net->ipv6.sysctl.ip6_rt_gc_elasticity;
+
+                       if (attempts-- > 0) {
+                               net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
+                               net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
+
+                               ip6_dst_gc(net->ipv6.ip6_dst_ops);
+
+                               net->ipv6.sysctl.ip6_rt_gc_elasticity =
+                                       saved_rt_elasticity;
+                               net->ipv6.sysctl.ip6_rt_gc_min_interval =
+                                       saved_rt_min_interval;
+                               goto retry;
+                       }
+
+                       if (net_ratelimit())
+                               printk(KERN_WARNING
+                                      "Neighbour table overflow.\n");
+                       dst_free(&rt->u.dst);
+                       return NULL;
+               }
+               rt->rt6i_nexthop = neigh;
 
        }
 
@@ -765,7 +797,7 @@ void ip6_route_input(struct sk_buff *skb)
                .proto = iph->nexthdr,
        };
 
-       if (rt6_need_strict(&iph->daddr))
+       if (rt6_need_strict(&iph->daddr) && skb->dev->type != ARPHRD_PIMREG)
                flags |= RT6_LOOKUP_F_IFACE;
 
        skb->dst = fib6_rule_lookup(net, &fl, flags, ip6_pol_route_input);
@@ -947,8 +979,11 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
        dev_hold(dev);
        if (neigh)
                neigh_hold(neigh);
-       else
+       else {
                neigh = ndisc_get_neigh(dev, addr);
+               if (IS_ERR(neigh))
+                       neigh = NULL;
+       }
 
        rt->rt6i_dev      = dev;
        rt->rt6i_idev     = idev;
@@ -1833,16 +1868,19 @@ int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 static int ip6_pkt_drop(struct sk_buff *skb, int code, int ipstats_mib_noroutes)
 {
        int type;
+       struct dst_entry *dst = skb->dst;
        switch (ipstats_mib_noroutes) {
        case IPSTATS_MIB_INNOROUTES:
                type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
                if (type == IPV6_ADDR_ANY || type == IPV6_ADDR_RESERVED) {
-                       IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INADDRERRORS);
+                       IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
+                                     IPSTATS_MIB_INADDRERRORS);
                        break;
                }
                /* FALLTHROUGH */
        case IPSTATS_MIB_OUTNOROUTES:
-               IP6_INC_STATS(ip6_dst_idev(skb->dst), ipstats_mib_noroutes);
+               IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
+                             ipstats_mib_noroutes);
                break;
        }
        icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0, skb->dev);
@@ -1886,6 +1924,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
 {
        struct net *net = dev_net(idev->dev);
        struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
+       struct neighbour *neigh;
 
        if (rt == NULL)
                return ERR_PTR(-ENOMEM);
@@ -1908,11 +1947,18 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
                rt->rt6i_flags |= RTF_ANYCAST;
        else
                rt->rt6i_flags |= RTF_LOCAL;
-       rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
-       if (rt->rt6i_nexthop == NULL) {
+       neigh = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+       if (IS_ERR(neigh)) {
                dst_free(&rt->u.dst);
-               return ERR_PTR(-ENOMEM);
+
+               /* We are casting this because that is the return
+                * value type.  But an errno encoded pointer is the
+                * same regardless of the underlying pointer type,
+                * and that's what we are returning.  So this is OK.
+                */
+               return (struct rt6_info *) neigh;
        }
+       rt->rt6i_nexthop = neigh;
 
        ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
        rt->rt6i_dst.plen = 128;
@@ -2193,7 +2239,7 @@ static int rt6_fill_node(struct net *net,
        if (iif) {
 #ifdef CONFIG_IPV6_MROUTE
                if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
-                       int err = ip6mr_get_route(skb, rtm, nowait);
+                       int err = ip6mr_get_route(net, skb, rtm, nowait);
                        if (err <= 0) {
                                if (!nowait) {
                                        if (err == 0)
@@ -2357,8 +2403,9 @@ void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info)
                kfree_skb(skb);
                goto errout;
        }
-       err = rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
-                         info->nlh, gfp_any());
+       rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE,
+                   info->nlh, gfp_any());
+       return;
 errout:
        if (err < 0)
                rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
@@ -2405,19 +2452,16 @@ static int rt6_info_route(struct rt6_info *rt, void *p_arg)
 {
        struct seq_file *m = p_arg;
 
-       seq_printf(m, NIP6_SEQFMT " %02x ", NIP6(rt->rt6i_dst.addr),
-                  rt->rt6i_dst.plen);
+       seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
 
 #ifdef CONFIG_IPV6_SUBTREES
-       seq_printf(m, NIP6_SEQFMT " %02x ", NIP6(rt->rt6i_src.addr),
-                  rt->rt6i_src.plen);
+       seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
 #else
        seq_puts(m, "00000000000000000000000000000000 00 ");
 #endif
 
        if (rt->rt6i_nexthop) {
-               seq_printf(m, NIP6_SEQFMT,
-                          NIP6(*((struct in6_addr *)rt->rt6i_nexthop->primary_key)));
+               seq_printf(m, "%pi6", rt->rt6i_nexthop->primary_key);
        } else {
                seq_puts(m, "00000000000000000000000000000000");
        }
@@ -2499,7 +2543,7 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.flush_delay,
                .maxlen         =       sizeof(int),
                .mode           =       0200,
-               .proc_handler   =       &ipv6_sysctl_rtcache_flush
+               .proc_handler   =       ipv6_sysctl_rtcache_flush
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_GC_THRESH,
@@ -2507,7 +2551,7 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &ip6_dst_ops_template.gc_thresh,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec,
+               .proc_handler   =       proc_dointvec,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_MAX_SIZE,
@@ -2515,7 +2559,7 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_max_size,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec,
+               .proc_handler   =       proc_dointvec,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_GC_MIN_INTERVAL,
@@ -2523,8 +2567,8 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec_jiffies,
-               .strategy       =       &sysctl_jiffies,
+               .proc_handler   =       proc_dointvec_jiffies,
+               .strategy       =       sysctl_jiffies,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_GC_TIMEOUT,
@@ -2532,8 +2576,8 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec_jiffies,
-               .strategy       =       &sysctl_jiffies,
+               .proc_handler   =       proc_dointvec_jiffies,
+               .strategy       =       sysctl_jiffies,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_GC_INTERVAL,
@@ -2541,8 +2585,8 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_gc_interval,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec_jiffies,
-               .strategy       =       &sysctl_jiffies,
+               .proc_handler   =       proc_dointvec_jiffies,
+               .strategy       =       sysctl_jiffies,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_GC_ELASTICITY,
@@ -2550,8 +2594,8 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec_jiffies,
-               .strategy       =       &sysctl_jiffies,
+               .proc_handler   =       proc_dointvec_jiffies,
+               .strategy       =       sysctl_jiffies,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_MTU_EXPIRES,
@@ -2559,8 +2603,8 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec_jiffies,
-               .strategy       =       &sysctl_jiffies,
+               .proc_handler   =       proc_dointvec_jiffies,
+               .strategy       =       sysctl_jiffies,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_MIN_ADVMSS,
@@ -2568,8 +2612,8 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_min_advmss,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec_jiffies,
-               .strategy       =       &sysctl_jiffies,
+               .proc_handler   =       proc_dointvec_jiffies,
+               .strategy       =       sysctl_jiffies,
        },
        {
                .ctl_name       =       NET_IPV6_ROUTE_GC_MIN_INTERVAL_MS,
@@ -2577,8 +2621,8 @@ ctl_table ipv6_route_table_template[] = {
                .data           =       &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
-               .proc_handler   =       &proc_dointvec_ms_jiffies,
-               .strategy       =       &sysctl_ms_jiffies,
+               .proc_handler   =       proc_dointvec_ms_jiffies,
+               .strategy       =       sysctl_ms_jiffies,
        },
        { .ctl_name = 0 }
 };
@@ -2631,10 +2675,8 @@ static int ip6_route_net_init(struct net *net)
        net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
                                               sizeof(*net->ipv6.ip6_prohibit_entry),
                                               GFP_KERNEL);
-       if (!net->ipv6.ip6_prohibit_entry) {
-               kfree(net->ipv6.ip6_null_entry);
-               goto out;
-       }
+       if (!net->ipv6.ip6_prohibit_entry)
+               goto out_ip6_null_entry;
        net->ipv6.ip6_prohibit_entry->u.dst.path =
                (struct dst_entry *)net->ipv6.ip6_prohibit_entry;
        net->ipv6.ip6_prohibit_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
@@ -2642,16 +2684,22 @@ static int ip6_route_net_init(struct net *net)
        net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
                                               sizeof(*net->ipv6.ip6_blk_hole_entry),
                                               GFP_KERNEL);
-       if (!net->ipv6.ip6_blk_hole_entry) {
-               kfree(net->ipv6.ip6_null_entry);
-               kfree(net->ipv6.ip6_prohibit_entry);
-               goto out;
-       }
+       if (!net->ipv6.ip6_blk_hole_entry)
+               goto out_ip6_prohibit_entry;
        net->ipv6.ip6_blk_hole_entry->u.dst.path =
                (struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
        net->ipv6.ip6_blk_hole_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
 #endif
 
+       net->ipv6.sysctl.flush_delay = 0;
+       net->ipv6.sysctl.ip6_rt_max_size = 4096;
+       net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
+       net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
+       net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
+       net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
+       net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
+       net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
+
 #ifdef CONFIG_PROC_FS
        proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops);
        proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);
@@ -2662,6 +2710,12 @@ static int ip6_route_net_init(struct net *net)
 out:
        return ret;
 
+#ifdef CONFIG_IPV6_MULTIPLE_TABLES
+out_ip6_prohibit_entry:
+       kfree(net->ipv6.ip6_prohibit_entry);
+out_ip6_null_entry:
+       kfree(net->ipv6.ip6_null_entry);
+#endif
 out_ip6_dst_ops:
        release_net(net->ipv6.ip6_dst_ops->dst_net);
        kfree(net->ipv6.ip6_dst_ops);
@@ -2702,12 +2756,14 @@ int __init ip6_route_init(void)
                kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
                                  SLAB_HWCACHE_ALIGN, NULL);
        if (!ip6_dst_ops_template.kmem_cachep)
-               goto out;;
+               goto out;
 
        ret = register_pernet_subsys(&ip6_route_net_ops);
        if (ret)
                goto out_kmem_cache;
 
+       ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
+
        /* Registering of the loopback is done before this portion of code,
         * the loopback reference in rt6_info will not be taken, do it
         * manually for init_net */