Merge branch 'topic/core-cleanup' into for-linus
[safe/jmp/linux-2.6] / net / ipv6 / route.c
index 9da1ece..05ebd78 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/nsproxy.h>
+#include <linux/slab.h>
 #include <net/net_namespace.h>
 #include <net/snmp.h>
 #include <net/ipv6.h>
@@ -98,7 +99,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,
@@ -117,7 +118,7 @@ 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,
@@ -137,6 +138,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),
 };
@@ -159,6 +161,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),
 };
@@ -176,6 +179,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),
 };
@@ -478,7 +482,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
 
        pref = rinfo->route_pref;
        if (pref == ICMPV6_ROUTER_PREF_INVALID)
-               pref = ICMPV6_ROUTER_PREF_MEDIUM;
+               return -EINVAL;
 
        lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
 
@@ -627,6 +631,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))
@@ -646,7 +653,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;
 
        }
 
@@ -763,10 +798,10 @@ 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);
+       skb_dst_set(skb, fib6_rule_lookup(net, &fl, flags, ip6_pol_route_input));
 }
 
 static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table,
@@ -780,20 +815,13 @@ struct dst_entry * ip6_route_output(struct net *net, struct sock *sk,
 {
        int flags = 0;
 
-       if (rt6_need_strict(&fl->fl6_dst))
+       if (fl->oif || rt6_need_strict(&fl->fl6_dst))
                flags |= RT6_LOOKUP_F_IFACE;
 
        if (!ipv6_addr_any(&fl->fl6_src))
                flags |= RT6_LOOKUP_F_HAS_SADDR;
-       else if (sk) {
-               unsigned int prefs = inet6_sk(sk)->srcprefs;
-               if (prefs & IPV6_PREFER_SRC_TMP)
-                       flags |= RT6_LOOKUP_F_SRCPREF_TMP;
-               if (prefs & IPV6_PREFER_SRC_PUBLIC)
-                       flags |= RT6_LOOKUP_F_SRCPREF_PUBLIC;
-               if (prefs & IPV6_PREFER_SRC_COA)
-                       flags |= RT6_LOOKUP_F_SRCPREF_COA;
-       }
+       else if (sk)
+               flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
 
        return fib6_rule_lookup(net, fl, flags, ip6_pol_route_output);
 }
@@ -852,7 +880,7 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
 
        rt = (struct rt6_info *) dst;
 
-       if (rt && rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
+       if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
                return dst;
 
        return NULL;
@@ -863,21 +891,26 @@ static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
        struct rt6_info *rt = (struct rt6_info *) dst;
 
        if (rt) {
-               if (rt->rt6i_flags & RTF_CACHE)
-                       ip6_del_rt(rt);
-               else
+               if (rt->rt6i_flags & RTF_CACHE) {
+                       if (rt6_check_expired(rt)) {
+                               ip6_del_rt(rt);
+                               dst = NULL;
+                       }
+               } else {
                        dst_release(dst);
+                       dst = NULL;
+               }
        }
-       return NULL;
+       return dst;
 }
 
 static void ip6_link_failure(struct sk_buff *skb)
 {
        struct rt6_info *rt;
 
-       icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev);
+       icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
 
-       rt = (struct rt6_info *) skb->dst;
+       rt = (struct rt6_info *) skb_dst(skb);
        if (rt) {
                if (rt->rt6i_flags&RTF_CACHE) {
                        dst_set_expires(&rt->u.dst, 0);
@@ -936,7 +969,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
        if (unlikely(idev == NULL))
                return NULL;
 
-       rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
+       rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops);
        if (unlikely(rt == NULL)) {
                in6_dev_put(idev);
                goto out;
@@ -945,8 +978,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;
@@ -1023,7 +1059,7 @@ static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
 static int ip6_dst_gc(struct dst_ops *ops)
 {
        unsigned long now = jiffies;
-       struct net *net = ops->dst_net;
+       struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
        int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
        int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
        int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
@@ -1117,7 +1153,7 @@ int ip6_route_add(struct fib6_config *cfg)
                goto out;
        }
 
-       rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
+       rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops);
 
        if (rt == NULL) {
                err = -ENOMEM;
@@ -1434,9 +1470,10 @@ static struct rt6_info *ip6_route_redirect(struct in6_addr *dest,
                                },
                        },
                },
-               .gateway = *gateway,
        };
 
+       ipv6_addr_copy(&rdfl.gateway, gateway);
+
        if (rt6_need_strict(dest))
                flags |= RT6_LOOKUP_F_IFACE;
 
@@ -1606,7 +1643,7 @@ out:
 static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
 {
        struct net *net = dev_net(ort->rt6i_dev);
-       struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
+       struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops);
 
        if (rt) {
                rt->u.dst.input = ort->u.dst.input;
@@ -1828,14 +1865,14 @@ int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
  *     Drop the packet on the floor
  */
 
-static int ip6_pkt_drop(struct sk_buff *skb, int code, int ipstats_mib_noroutes)
+static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
 {
        int type;
-       struct dst_entry *dst = skb->dst;
+       struct dst_entry *dst = skb_dst(skb);
        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) {
+               if (type == IPV6_ADDR_ANY) {
                        IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
                                      IPSTATS_MIB_INADDRERRORS);
                        break;
@@ -1846,7 +1883,7 @@ static int ip6_pkt_drop(struct sk_buff *skb, int code, int ipstats_mib_noroutes)
                              ipstats_mib_noroutes);
                break;
        }
-       icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0, skb->dev);
+       icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
        kfree_skb(skb);
        return 0;
 }
@@ -1858,7 +1895,7 @@ static int ip6_pkt_discard(struct sk_buff *skb)
 
 static int ip6_pkt_discard_out(struct sk_buff *skb)
 {
-       skb->dev = skb->dst->dev;
+       skb->dev = skb_dst(skb)->dev;
        return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
 }
 
@@ -1871,7 +1908,7 @@ static int ip6_pkt_prohibit(struct sk_buff *skb)
 
 static int ip6_pkt_prohibit_out(struct sk_buff *skb)
 {
-       skb->dev = skb->dst->dev;
+       skb->dev = skb_dst(skb)->dev;
        return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
 }
 
@@ -1886,7 +1923,8 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
                                    int anycast)
 {
        struct net *net = dev_net(idev->dev);
-       struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
+       struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops);
+       struct neighbour *neigh;
 
        if (rt == NULL)
                return ERR_PTR(-ENOMEM);
@@ -1909,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;
@@ -2194,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)
@@ -2321,7 +2366,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void
        skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
 
        rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl);
-       skb->dst = &rt->u.dst;
+       skb_dst_set(skb, &rt->u.dst);
 
        err = rt6_fill_node(net, skb, rt, &fl.fl6_dst, &fl.fl6_src, iif,
                            RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
@@ -2358,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);
@@ -2455,7 +2501,7 @@ static int rt6_stats_seq_show(struct seq_file *seq, void *v)
                   net->ipv6.rt6_stats->fib_rt_alloc,
                   net->ipv6.rt6_stats->fib_rt_entries,
                   net->ipv6.rt6_stats->fib_rt_cache,
-                  atomic_read(&net->ipv6.ip6_dst_ops->entries),
+                  atomic_read(&net->ipv6.ip6_dst_ops.entries),
                   net->ipv6.rt6_stats->fib_discarded_routes);
 
        return 0;
@@ -2478,13 +2524,13 @@ static const struct file_operations rt6_stats_seq_fops = {
 #ifdef CONFIG_SYSCTL
 
 static
-int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, struct file * filp,
+int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
                              void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net = current->nsproxy->net_ns;
        int delay = net->ipv6.sysctl.flush_delay;
        if (write) {
-               proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
+               proc_dointvec(ctl, write, buffer, lenp, ppos);
                fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
                return 0;
        } else
@@ -2500,7 +2546,6 @@ ctl_table ipv6_route_table_template[] = {
                .proc_handler   =       ipv6_sysctl_rtcache_flush
        },
        {
-               .ctl_name       =       NET_IPV6_ROUTE_GC_THRESH,
                .procname       =       "gc_thresh",
                .data           =       &ip6_dst_ops_template.gc_thresh,
                .maxlen         =       sizeof(int),
@@ -2508,7 +2553,6 @@ ctl_table ipv6_route_table_template[] = {
                .proc_handler   =       proc_dointvec,
        },
        {
-               .ctl_name       =       NET_IPV6_ROUTE_MAX_SIZE,
                .procname       =       "max_size",
                .data           =       &init_net.ipv6.sysctl.ip6_rt_max_size,
                .maxlen         =       sizeof(int),
@@ -2516,72 +2560,58 @@ ctl_table ipv6_route_table_template[] = {
                .proc_handler   =       proc_dointvec,
        },
        {
-               .ctl_name       =       NET_IPV6_ROUTE_GC_MIN_INTERVAL,
                .procname       =       "gc_min_interval",
                .data           =       &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
                .proc_handler   =       proc_dointvec_jiffies,
-               .strategy       =       sysctl_jiffies,
        },
        {
-               .ctl_name       =       NET_IPV6_ROUTE_GC_TIMEOUT,
                .procname       =       "gc_timeout",
                .data           =       &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
                .proc_handler   =       proc_dointvec_jiffies,
-               .strategy       =       sysctl_jiffies,
        },
        {
-               .ctl_name       =       NET_IPV6_ROUTE_GC_INTERVAL,
                .procname       =       "gc_interval",
                .data           =       &init_net.ipv6.sysctl.ip6_rt_gc_interval,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
                .proc_handler   =       proc_dointvec_jiffies,
-               .strategy       =       sysctl_jiffies,
        },
        {
-               .ctl_name       =       NET_IPV6_ROUTE_GC_ELASTICITY,
                .procname       =       "gc_elasticity",
                .data           =       &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
                .proc_handler   =       proc_dointvec_jiffies,
-               .strategy       =       sysctl_jiffies,
        },
        {
-               .ctl_name       =       NET_IPV6_ROUTE_MTU_EXPIRES,
                .procname       =       "mtu_expires",
                .data           =       &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
                .proc_handler   =       proc_dointvec_jiffies,
-               .strategy       =       sysctl_jiffies,
        },
        {
-               .ctl_name       =       NET_IPV6_ROUTE_MIN_ADVMSS,
                .procname       =       "min_adv_mss",
                .data           =       &init_net.ipv6.sysctl.ip6_rt_min_advmss,
                .maxlen         =       sizeof(int),
                .mode           =       0644,
                .proc_handler   =       proc_dointvec_jiffies,
-               .strategy       =       sysctl_jiffies,
        },
        {
-               .ctl_name       =       NET_IPV6_ROUTE_GC_MIN_INTERVAL_MS,
                .procname       =       "gc_min_interval_ms",
                .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,
        },
-       { .ctl_name = 0 }
+       { }
 };
 
-struct ctl_table *ipv6_route_sysctl_init(struct net *net)
+struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
 {
        struct ctl_table *table;
 
@@ -2591,7 +2621,7 @@ struct ctl_table *ipv6_route_sysctl_init(struct net *net)
 
        if (table) {
                table[0].data = &net->ipv6.sysctl.flush_delay;
-               table[1].data = &net->ipv6.ip6_dst_ops->gc_thresh;
+               table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
                table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
                table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
                table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
@@ -2599,22 +2629,19 @@ struct ctl_table *ipv6_route_sysctl_init(struct net *net)
                table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
                table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
                table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
+               table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
        }
 
        return table;
 }
 #endif
 
-static int ip6_route_net_init(struct net *net)
+static int __net_init ip6_route_net_init(struct net *net)
 {
        int ret = -ENOMEM;
 
-       net->ipv6.ip6_dst_ops = kmemdup(&ip6_dst_ops_template,
-                                       sizeof(*net->ipv6.ip6_dst_ops),
-                                       GFP_KERNEL);
-       if (!net->ipv6.ip6_dst_ops)
-               goto out;
-       net->ipv6.ip6_dst_ops->dst_net = hold_net(net);
+       memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
+              sizeof(net->ipv6.ip6_dst_ops));
 
        net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
                                           sizeof(*net->ipv6.ip6_null_entry),
@@ -2623,7 +2650,7 @@ static int ip6_route_net_init(struct net *net)
                goto out_ip6_dst_ops;
        net->ipv6.ip6_null_entry->u.dst.path =
                (struct dst_entry *)net->ipv6.ip6_null_entry;
-       net->ipv6.ip6_null_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
+       net->ipv6.ip6_null_entry->u.dst.ops = &net->ipv6.ip6_dst_ops;
 
 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
        net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
@@ -2633,7 +2660,7 @@ static int ip6_route_net_init(struct net *net)
                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;
+       net->ipv6.ip6_prohibit_entry->u.dst.ops = &net->ipv6.ip6_dst_ops;
 
        net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
                                               sizeof(*net->ipv6.ip6_blk_hole_entry),
@@ -2642,7 +2669,7 @@ static int ip6_route_net_init(struct net *net)
                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;
+       net->ipv6.ip6_blk_hole_entry->u.dst.ops = &net->ipv6.ip6_dst_ops;
 #endif
 
        net->ipv6.sysctl.flush_delay = 0;
@@ -2671,12 +2698,10 @@ 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);
        goto out;
 }
 
-static void ip6_route_net_exit(struct net *net)
+static void __net_exit ip6_route_net_exit(struct net *net)
 {
 #ifdef CONFIG_PROC_FS
        proc_net_remove(net, "ipv6_route");
@@ -2687,8 +2712,6 @@ static void ip6_route_net_exit(struct net *net)
        kfree(net->ipv6.ip6_prohibit_entry);
        kfree(net->ipv6.ip6_blk_hole_entry);
 #endif
-       release_net(net->ipv6.ip6_dst_ops->dst_net);
-       kfree(net->ipv6.ip6_dst_ops);
 }
 
 static struct pernet_operations ip6_route_net_ops = {
@@ -2710,7 +2733,7 @@ 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)