netns xfrm: fix "ip xfrm state|policy count" misreport
[safe/jmp/linux-2.6] / net / xfrm / xfrm_policy.c
index 38822b3..d2c8cb5 100644 (file)
@@ -34,8 +34,6 @@
 
 #include "xfrm_hash.h"
 
-int sysctl_xfrm_larval_drop __read_mostly = 1;
-
 DEFINE_MUTEX(xfrm_cfg_mutex);
 EXPORT_SYMBOL(xfrm_cfg_mutex);
 
@@ -53,6 +51,9 @@ static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family);
 static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo);
 static void xfrm_init_pmtu(struct dst_entry *dst);
 
+static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
+                                               int dir);
+
 static inline int
 __xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl)
 {
@@ -468,16 +469,16 @@ static inline int xfrm_byidx_should_resize(struct net *net, int total)
        return 0;
 }
 
-void xfrm_spd_getinfo(struct xfrmk_spdinfo *si)
+void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si)
 {
        read_lock_bh(&xfrm_policy_lock);
-       si->incnt = init_net.xfrm.policy_count[XFRM_POLICY_IN];
-       si->outcnt = init_net.xfrm.policy_count[XFRM_POLICY_OUT];
-       si->fwdcnt = init_net.xfrm.policy_count[XFRM_POLICY_FWD];
-       si->inscnt = init_net.xfrm.policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX];
-       si->outscnt = init_net.xfrm.policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX];
-       si->fwdscnt = init_net.xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX];
-       si->spdhcnt = init_net.xfrm.policy_idx_hmask;
+       si->incnt = net->xfrm.policy_count[XFRM_POLICY_IN];
+       si->outcnt = net->xfrm.policy_count[XFRM_POLICY_OUT];
+       si->fwdcnt = net->xfrm.policy_count[XFRM_POLICY_FWD];
+       si->inscnt = net->xfrm.policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX];
+       si->outscnt = net->xfrm.policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX];
+       si->fwdscnt = net->xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX];
+       si->spdhcnt = net->xfrm.policy_idx_hmask;
        si->spdhmcnt = xfrm_policy_hashmax;
        read_unlock_bh(&xfrm_policy_lock);
 }
@@ -586,12 +587,8 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
        xfrm_pol_hold(policy);
        net->xfrm.policy_count[dir]++;
        atomic_inc(&flow_cache_genid);
-       if (delpol) {
-               hlist_del(&delpol->bydst);
-               hlist_del(&delpol->byidx);
-               list_del(&delpol->walk.all);
-               net->xfrm.policy_count[dir]--;
-       }
+       if (delpol)
+               __xfrm_policy_unlink(delpol, dir);
        policy->index = delpol ? delpol->index : xfrm_gen_index(net, dir);
        hlist_add_head(&policy->byidx, net->xfrm.policy_byidx+idx_hash(net, policy->index));
        policy->curlft.add_time = get_seconds();
@@ -663,10 +660,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u8 type, int dir,
                                        write_unlock_bh(&xfrm_policy_lock);
                                        return pol;
                                }
-                               hlist_del(&pol->bydst);
-                               hlist_del(&pol->byidx);
-                               list_del(&pol->walk.all);
-                               net->xfrm.policy_count[dir]--;
+                               __xfrm_policy_unlink(pol, dir);
                        }
                        ret = pol;
                        break;
@@ -707,10 +701,7 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net, u8 type, int dir, u32 id,
                                        write_unlock_bh(&xfrm_policy_lock);
                                        return pol;
                                }
-                               hlist_del(&pol->bydst);
-                               hlist_del(&pol->byidx);
-                               list_del(&pol->walk.all);
-                               net->xfrm.policy_count[dir]--;
+                               __xfrm_policy_unlink(pol, dir);
                        }
                        ret = pol;
                        break;
@@ -791,16 +782,14 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
        for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
                struct xfrm_policy *pol;
                struct hlist_node *entry;
-               int i, killed;
+               int i;
 
-               killed = 0;
        again1:
                hlist_for_each_entry(pol, entry,
                                     &net->xfrm.policy_inexact[dir], bydst) {
                        if (pol->type != type)
                                continue;
-                       hlist_del(&pol->bydst);
-                       hlist_del(&pol->byidx);
+                       __xfrm_policy_unlink(pol, dir);
                        write_unlock_bh(&xfrm_policy_lock);
 
                        xfrm_audit_policy_delete(pol, 1, audit_info->loginuid,
@@ -808,7 +797,6 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
                                                 audit_info->secid);
 
                        xfrm_policy_kill(pol);
-                       killed++;
 
                        write_lock_bh(&xfrm_policy_lock);
                        goto again1;
@@ -821,9 +809,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
                                             bydst) {
                                if (pol->type != type)
                                        continue;
-                               hlist_del(&pol->bydst);
-                               hlist_del(&pol->byidx);
-                               list_del(&pol->walk.all);
+                               __xfrm_policy_unlink(pol, dir);
                                write_unlock_bh(&xfrm_policy_lock);
 
                                xfrm_audit_policy_delete(pol, 1,
@@ -831,14 +817,12 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
                                                         audit_info->sessionid,
                                                         audit_info->secid);
                                xfrm_policy_kill(pol);
-                               killed++;
 
                                write_lock_bh(&xfrm_policy_lock);
                                goto again2;
                        }
                }
 
-               net->xfrm.policy_count[dir] -= killed;
        }
        atomic_inc(&flow_cache_genid);
 out:
@@ -1461,7 +1445,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
        if (!dev)
                goto free_dst;
 
-       /* Copy neighbout for reachability confirmation */
+       /* Copy neighbour for reachability confirmation */
        dst0->neighbour = neigh_clone(dst->neighbour);
 
        xfrm_init_path((struct xfrm_dst *)dst0, dst, nfheader_len);
@@ -1671,7 +1655,7 @@ restart:
 
                if (unlikely(nx<0)) {
                        err = nx;
-                       if (err == -EAGAIN && sysctl_xfrm_larval_drop) {
+                       if (err == -EAGAIN && net->xfrm.sysctl_larval_drop) {
                                /* EREMOTE tells the caller to generate
                                 * a one-shot blackhole route.
                                 */
@@ -2043,6 +2027,8 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
 {
        struct net *net = dev_net(skb->dev);
        struct flowi fl;
+       struct dst_entry *dst;
+       int res;
 
        if (xfrm_decode_session(skb, &fl, family) < 0) {
                /* XXX: we should have something like FWDHDRERROR here. */
@@ -2050,7 +2036,11 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
                return 0;
        }
 
-       return xfrm_lookup(net, &skb->dst, &fl, NULL, 0) == 0;
+       dst = skb_dst(skb);
+
+       res = xfrm_lookup(net, &dst, &fl, NULL, 0) == 0;
+       skb_dst_set(skb, dst);
+       return res;
 }
 EXPORT_SYMBOL(__xfrm_route_forward);
 
@@ -2504,8 +2494,13 @@ static int __net_init xfrm_net_init(struct net *net)
        rv = xfrm_policy_init(net);
        if (rv < 0)
                goto out_policy;
+       rv = xfrm_sysctl_init(net);
+       if (rv < 0)
+               goto out_sysctl;
        return 0;
 
+out_sysctl:
+       xfrm_policy_fini(net);
 out_policy:
        xfrm_state_fini(net);
 out_state:
@@ -2516,6 +2511,7 @@ out_statistics:
 
 static void __net_exit xfrm_net_exit(struct net *net)
 {
+       xfrm_sysctl_fini(net);
        xfrm_policy_fini(net);
        xfrm_state_fini(net);
        xfrm_statistics_fini(net);