[IPV6]: Don't update ADVMSS on routes where the MTU is not also updated
[pandora-kernel.git] / net / ipv6 / route.c
index ad9b285..55ea80f 100644 (file)
@@ -119,6 +119,19 @@ static struct dst_ops ip6_dst_ops = {
        .entry_size             =       sizeof(struct rt6_info),
 };
 
+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),
+       .destroy                =       ip6_dst_destroy,
+       .check                  =       ip6_dst_check,
+       .update_pmtu            =       ip6_rt_blackhole_update_pmtu,
+       .entry_size             =       sizeof(struct rt6_info),
+};
+
 struct rt6_info ip6_null_entry = {
        .u = {
                .dst = {
@@ -575,6 +588,8 @@ struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr,
        return NULL;
 }
 
+EXPORT_SYMBOL(rt6_lookup);
+
 /* ip6_ins_rt is called with FREE table->tb6_lock.
    It takes new route entry, the addition fails by any reason the
    route is freed. In any case, if caller does not hold it, it may
@@ -724,7 +739,7 @@ out2:
 
 void ip6_route_input(struct sk_buff *skb)
 {
-       struct ipv6hdr *iph = skb->nh.ipv6h;
+       struct ipv6hdr *iph = ipv6_hdr(skb);
        int flags = RT6_LOOKUP_F_HAS_SADDR;
        struct flowi fl = {
                .iif = skb->dev->ifindex,
@@ -829,6 +844,55 @@ struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl)
        return fib6_rule_lookup(fl, flags, ip6_pol_route_output);
 }
 
+EXPORT_SYMBOL(ip6_route_output);
+
+static int ip6_blackhole_output(struct sk_buff *skb)
+{
+       kfree_skb(skb);
+       return 0;
+}
+
+int ip6_dst_blackhole(struct sock *sk, struct dst_entry **dstp, struct flowi *fl)
+{
+       struct rt6_info *ort = (struct rt6_info *) *dstp;
+       struct rt6_info *rt = (struct rt6_info *)
+               dst_alloc(&ip6_dst_blackhole_ops);
+       struct dst_entry *new = NULL;
+
+       if (rt) {
+               new = &rt->u.dst;
+
+               atomic_set(&new->__refcnt, 1);
+               new->__use = 1;
+               new->input = ip6_blackhole_output;
+               new->output = ip6_blackhole_output;
+
+               memcpy(new->metrics, ort->u.dst.metrics, RTAX_MAX*sizeof(u32));
+               new->dev = ort->u.dst.dev;
+               if (new->dev)
+                       dev_hold(new->dev);
+               rt->rt6i_idev = ort->rt6i_idev;
+               if (rt->rt6i_idev)
+                       in6_dev_hold(rt->rt6i_idev);
+               rt->rt6i_expires = 0;
+
+               ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway);
+               rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
+               rt->rt6i_metric = 0;
+
+               memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
+#ifdef CONFIG_IPV6_SUBTREES
+               memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
+#endif
+
+               dst_free(new);
+       }
+
+       dst_release(*dstp);
+       *dstp = new;
+       return (new ? 0 : -ENOMEM);
+}
+EXPORT_SYMBOL_GPL(ip6_dst_blackhole);
 
 /*
  *     Destination cache support functions
@@ -1757,7 +1821,7 @@ int ipv6_route_ioctl(unsigned int cmd, void __user *arg)
                rtnl_unlock();
 
                return err;
-       };
+       }
 
        return -EINVAL;
 }
@@ -1766,13 +1830,22 @@ int ipv6_route_ioctl(unsigned int cmd, void __user *arg)
  *     Drop the packet on the floor
  */
 
-static inline int ip6_pkt_drop(struct sk_buff *skb, int code)
+static inline int ip6_pkt_drop(struct sk_buff *skb, int code,
+                              int ipstats_mib_noroutes)
 {
-       int type = ipv6_addr_type(&skb->nh.ipv6h->daddr);
-       if (type == IPV6_ADDR_ANY || type == IPV6_ADDR_RESERVED)
-               IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INADDRERRORS);
-
-       IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_OUTNOROUTES);
+       int type;
+       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);
+                       break;
+               }
+               /* FALLTHROUGH */
+       case IPSTATS_MIB_OUTNOROUTES:
+               IP6_INC_STATS(ip6_dst_idev(skb->dst), ipstats_mib_noroutes);
+               break;
+       }
        icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0, skb->dev);
        kfree_skb(skb);
        return 0;
@@ -1780,26 +1853,26 @@ static inline int ip6_pkt_drop(struct sk_buff *skb, int code)
 
 static int ip6_pkt_discard(struct sk_buff *skb)
 {
-       return ip6_pkt_drop(skb, ICMPV6_NOROUTE);
+       return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
 }
 
 static int ip6_pkt_discard_out(struct sk_buff *skb)
 {
        skb->dev = skb->dst->dev;
-       return ip6_pkt_discard(skb);
+       return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
 }
 
 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
 
 static int ip6_pkt_prohibit(struct sk_buff *skb)
 {
-       return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED);
+       return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
 }
 
 static int ip6_pkt_prohibit_out(struct sk_buff *skb)
 {
        skb->dev = skb->dst->dev;
-       return ip6_pkt_prohibit(skb);
+       return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
 }
 
 static int ip6_pkt_blk_hole(struct sk_buff *skb)
@@ -1910,9 +1983,10 @@ static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
            !dst_metric_locked(&rt->u.dst, RTAX_MTU) &&
            (dst_mtu(&rt->u.dst) > arg->mtu ||
             (dst_mtu(&rt->u.dst) < arg->mtu &&
-             dst_mtu(&rt->u.dst) == idev->cnf.mtu6)))
+             dst_mtu(&rt->u.dst) == idev->cnf.mtu6))) {
                rt->u.dst.metrics[RTAX_MTU-1] = arg->mtu;
-       rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(arg->mtu);
+               rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(arg->mtu);
+       }
        return 0;
 }
 
@@ -1926,7 +2000,7 @@ void rt6_mtu_change(struct net_device *dev, unsigned mtu)
        fib6_clean_all(rt6_mtu_change_route, 0, &arg);
 }
 
-static struct nla_policy rtm_ipv6_policy[RTA_MAX+1] __read_mostly = {
+static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
        [RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
        [RTA_OIF]               = { .type = NLA_U32 },
        [RTA_IIF]               = { .type = NLA_U32 },
@@ -2003,7 +2077,7 @@ errout:
        return err;
 }
 
-int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
        struct fib6_config cfg;
        int err;
@@ -2015,7 +2089,7 @@ int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
        return ip6_route_del(&cfg);
 }
 
-int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
        struct fib6_config cfg;
        int err;
@@ -2152,7 +2226,7 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg)
                     prefix, NLM_F_MULTI);
 }
 
-int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
+static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
 {
        struct nlattr *tb[RTA_MAX+1];
        struct rt6_info *rt;
@@ -2206,7 +2280,7 @@ int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
        /* Reserve room for dummy headers, this skb can pass
           through good chunk of routing engine.
         */
-       skb->mac.raw = skb->data;
+       skb_reset_mac_header(skb);
        skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
 
        rt = (struct rt6_info*) ip6_route_output(NULL, &fl);
@@ -2477,11 +2551,14 @@ ctl_table ipv6_route_table[] = {
 
 void __init ip6_route_init(void)
 {
+#ifdef         CONFIG_PROC_FS
        struct proc_dir_entry *p;
-
+#endif
        ip6_dst_ops.kmem_cachep =
                kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
-                                 SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+                                 SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
+       ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops.kmem_cachep;
+
        fib6_init();
 #ifdef         CONFIG_PROC_FS
        p = proc_net_create("ipv6_route", 0, rt6_proc_info);
@@ -2496,6 +2573,10 @@ void __init ip6_route_init(void)
 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
        fib6_rules_init();
 #endif
+
+       __rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL);
+       __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL);
+       __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL);
 }
 
 void ip6_route_cleanup(void)