]> git.hungrycats.org Git - linux/commitdiff
[IPV6]: Fix leaks of ndisc DST entries.
authorDavid S. Miller <davem@nuts.ninka.net>
Thu, 10 Jul 2003 19:17:39 +0000 (12:17 -0700)
committerDavid S. Miller <davem@nuts.ninka.net>
Thu, 10 Jul 2003 19:17:39 +0000 (12:17 -0700)
include/net/ip6_route.h
net/ipv6/ip6_fib.c
net/ipv6/ndisc.c
net/ipv6/route.c

index 752b31601ee460c4c99d8839a5b205b198c7a7d8..c421186227f7cd79d0bfd4b00dfffa3f4362a0d7 100644 (file)
@@ -61,7 +61,11 @@ extern struct rt6_info               *rt6_lookup(struct in6_addr *daddr,
                                            struct in6_addr *saddr,
                                            int oif, int flags);
 
-extern struct rt6_info         *ip6_dst_alloc(void);
+extern struct dst_entry *ndisc_dst_alloc(struct net_device *dev,
+                                        struct neighbour *neigh,
+                                        int (*output)(struct sk_buff *));
+extern int ndisc_dst_gc(int *more);
+extern void fib6_force_start_gc(void);
 
 /*
  *     support functions for ND
index e5edda7d7a181c63a2962c72eff5b1454bde82e7..9956944a50bfaf1eadaa2e51c2e641b1fe268818 100644 (file)
@@ -496,6 +496,12 @@ static __inline__ void fib6_start_gc(struct rt6_info *rt)
                mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval);
 }
 
+void fib6_force_start_gc(void)
+{
+       if (ip6_fib_timer.expires == 0)
+               mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval);
+}
+
 /*
  *     Add routing information to the routing tree.
  *     <destination addr>/<source addr>
@@ -1214,6 +1220,7 @@ void fib6_run_gc(unsigned long dummy)
 
 
        write_lock_bh(&rt6_lock);
+       ndisc_dst_gc(&gc_args.more);
        fib6_clean_tree(&ip6_routing_table, fib6_age, 0, NULL);
        write_unlock_bh(&rt6_lock);
 
index d75fa9ce723d5ab23ff31a009c48d61910d35a68..75a9eeaa05d05a32746adaf5576bc14130b62e85 100644 (file)
@@ -402,25 +402,6 @@ static int ndisc_output(struct sk_buff *skb)
        return -EINVAL;
 }
 
-static inline struct dst_entry *ndisc_dst_alloc(struct net_device *dev, 
-                                               struct neighbour *neigh)
-{
-       struct rt6_info *rt = ip6_dst_alloc();
-
-       if (unlikely(rt == NULL))
-               goto out;
-
-       rt->rt6i_dev      = dev;
-       rt->rt6i_nexthop  = neigh;
-       rt->rt6i_expires  = 0;
-       rt->rt6i_flags    = RTF_LOCAL;
-       rt->rt6i_metric   = 0;
-       rt->u.dst.metrics[RTAX_HOPLIMIT-1] = 255;
-       rt->u.dst.output  = ndisc_output;
-out:
-       return (struct dst_entry *)rt;
-}
-
 static inline void ndisc_flow_init(struct flowi *fl, u8 type,
                            struct in6_addr *saddr, struct in6_addr *daddr)
 {
@@ -463,13 +444,13 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
 
        ndisc_flow_init(&fl, NDISC_NEIGHBOUR_ADVERTISEMENT, src_addr, daddr);
 
-       dst = ndisc_dst_alloc(dev, neigh);      
+       dst = ndisc_dst_alloc(dev, neigh, ndisc_output);
        if (!dst)
                return;
 
        err = xfrm_lookup(&dst, &fl, NULL, 0);
        if (err < 0) {
-               dst_free(dst);
+               dst_release(dst);
                return;
        }
 
@@ -485,7 +466,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
 
        if (skb == NULL) {
                ND_PRINTK1("send_na: alloc skb failed\n");
-               dst_free(dst);
+               dst_release(dst);
                return;
        }
 
@@ -515,7 +496,6 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
                                                 csum_partial((__u8 *) msg, 
                                                              len, 0));
 
-       dst_clone(dst);
        skb->dst = dst;
        idev = in6_dev_get(dst->dev);
        dst_output(skb);
@@ -550,10 +530,9 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 
        ndisc_flow_init(&fl, NDISC_NEIGHBOUR_SOLICITATION, saddr, daddr);
 
-       dst = ndisc_dst_alloc(dev, neigh);
+       dst = ndisc_dst_alloc(dev, neigh, ndisc_output);
        if (!dst)
                return;
-       dst_clone(dst);
 
        err = xfrm_lookup(&dst, &fl, NULL, 0);
        if (err < 0) {
@@ -570,6 +549,7 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
                                  1, &err);
        if (skb == NULL) {
                ND_PRINTK1("send_ns: alloc skb failed\n");
+               dst_release(dst);
                return;
        }
 
@@ -595,7 +575,6 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
                                                 csum_partial((__u8 *) msg, 
                                                              len, 0));
        /* send it! */
-       dst_clone(dst);
        skb->dst = dst;
        idev = in6_dev_get(dst->dev);
        dst_output(skb);
@@ -622,10 +601,9 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 
        ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr);
 
-       dst = ndisc_dst_alloc(dev, NULL);
+       dst = ndisc_dst_alloc(dev, NULL, ndisc_output);
        if (!dst)
                return;
-       dst_clone(dst);
 
        err = xfrm_lookup(&dst, &fl, NULL, 0);
        if (err < 0) {
@@ -664,7 +642,6 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
                                           csum_partial((__u8 *) hdr, len, 0));
 
        /* send it! */
-       dst_clone(dst);
        skb->dst = dst;
        idev = in6_dev_get(dst->dev);
        dst_output(skb);
index ff37d74152fbf235e58286282526776bd927f1e4..1b9b06e5fc511000c6a2eb643e4c20360e187976 100644 (file)
@@ -131,16 +131,11 @@ rwlock_t rt6_lock = RW_LOCK_UNLOCKED;
 
 
 /* allocate dst with ip6_dst_ops */
-static __inline__ struct rt6_info *__ip6_dst_alloc(void)
+static __inline__ struct rt6_info *ip6_dst_alloc(void)
 {
        return dst_alloc(&ip6_dst_ops);
 }
 
-struct rt6_info *ip6_dst_alloc(void)
-{
-       return __ip6_dst_alloc();
-}
-
 /*
  *     Route lookup. Any rt6_lock is implied.
  */
@@ -560,6 +555,60 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
        }
 }
 
+/* Protected by rt6_lock.  */
+static struct dst_entry *ndisc_dst_gc_list;
+
+struct dst_entry *ndisc_dst_alloc(struct net_device *dev, 
+                                 struct neighbour *neigh,
+                                 int (*output)(struct sk_buff *))
+{
+       struct rt6_info *rt = ip6_dst_alloc();
+
+       if (unlikely(rt == NULL))
+               goto out;
+
+       rt->rt6i_dev      = dev;
+       rt->rt6i_nexthop  = neigh;
+       rt->rt6i_expires  = 0;
+       rt->rt6i_flags    = RTF_LOCAL;
+       rt->rt6i_metric   = 0;
+       atomic_set(&rt->u.dst.__refcnt, 1);
+       rt->u.dst.metrics[RTAX_HOPLIMIT-1] = 255;
+       rt->u.dst.output  = output;
+
+       write_lock_bh(&rt6_lock);
+       rt->u.dst.next = ndisc_dst_gc_list;
+       ndisc_dst_gc_list = &rt->u.dst;
+       write_unlock_bh(&rt6_lock);
+
+       fib6_force_start_gc();
+
+out:
+       return (struct dst_entry *)rt;
+}
+
+int ndisc_dst_gc(int *more)
+{
+       struct dst_entry *dst, *next, **pprev;
+       int freed;
+
+       next = NULL;
+       pprev = &ndisc_dst_gc_list;
+       freed = 0;
+       while ((dst = *pprev) != NULL) {
+               if (!atomic_read(&dst->__refcnt)) {
+                       *pprev = dst->next;
+                       dst_free(dst);
+                       freed++;
+               } else {
+                       pprev = &dst->next;
+                       (*more)++;
+               }
+       }
+
+       return freed;
+}
+
 static int ip6_dst_gc(void)
 {
        static unsigned expire = 30*HZ;
@@ -655,7 +704,7 @@ int ip6_route_add(struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh, void *_rtattr)
        if (rtmsg->rtmsg_metric == 0)
                rtmsg->rtmsg_metric = IP6_RT_PRIO_USER;
 
-       rt = __ip6_dst_alloc();
+       rt = ip6_dst_alloc();
 
        if (rt == NULL)
                return -ENOMEM;
@@ -1066,7 +1115,7 @@ out:
 
 static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
 {
-       struct rt6_info *rt = __ip6_dst_alloc();
+       struct rt6_info *rt = ip6_dst_alloc();
 
        if (rt) {
                rt->u.dst.input = ort->u.dst.input;
@@ -1209,7 +1258,7 @@ int ip6_pkt_discard(struct sk_buff *skb)
 
 int ip6_rt_addr_add(struct in6_addr *addr, struct net_device *dev)
 {
-       struct rt6_info *rt = __ip6_dst_alloc();
+       struct rt6_info *rt = ip6_dst_alloc();
 
        if (rt == NULL)
                return -ENOMEM;