warn: Turn the netdev timeout WARN_ON() into a WARN()
[safe/jmp/linux-2.6] / net / xfrm / xfrm_policy.c
index 521cb6e..b7754b1 100644 (file)
 #include <linux/netfilter.h>
 #include <linux/module.h>
 #include <linux/cache.h>
+#include <linux/audit.h>
 #include <net/dst.h>
 #include <net/xfrm.h>
 #include <net/ip.h>
+#ifdef CONFIG_XFRM_STATISTICS
+#include <net/snmp.h>
+#endif
 
 #include "xfrm_hash.h"
 
 int sysctl_xfrm_larval_drop __read_mostly;
 
+#ifdef CONFIG_XFRM_STATISTICS
+DEFINE_SNMP_STAT(struct linux_xfrm_mib, xfrm_statistics) __read_mostly;
+EXPORT_SYMBOL(xfrm_statistics);
+#endif
+
 DEFINE_MUTEX(xfrm_cfg_mutex);
 EXPORT_SYMBOL(xfrm_cfg_mutex);
 
 static DEFINE_RWLOCK(xfrm_policy_lock);
 
+static struct list_head xfrm_policy_bytype[XFRM_POLICY_TYPE_MAX];
 unsigned int xfrm_policy_count[XFRM_POLICY_MAX*2];
 EXPORT_SYMBOL(xfrm_policy_count);
 
@@ -87,25 +97,52 @@ int xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl,
        return 0;
 }
 
+static inline struct dst_entry *__xfrm_dst_lookup(int tos,
+                                                 xfrm_address_t *saddr,
+                                                 xfrm_address_t *daddr,
+                                                 int family)
+{
+       struct xfrm_policy_afinfo *afinfo;
+       struct dst_entry *dst;
+
+       afinfo = xfrm_policy_get_afinfo(family);
+       if (unlikely(afinfo == NULL))
+               return ERR_PTR(-EAFNOSUPPORT);
+
+       dst = afinfo->dst_lookup(tos, saddr, daddr);
+
+       xfrm_policy_put_afinfo(afinfo);
+
+       return dst;
+}
+
 static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos,
+                                               xfrm_address_t *prev_saddr,
+                                               xfrm_address_t *prev_daddr,
                                                int family)
 {
        xfrm_address_t *saddr = &x->props.saddr;
        xfrm_address_t *daddr = &x->id.daddr;
-       struct xfrm_policy_afinfo *afinfo;
        struct dst_entry *dst;
 
-       if (x->type->flags & XFRM_TYPE_LOCAL_COADDR)
+       if (x->type->flags & XFRM_TYPE_LOCAL_COADDR) {
                saddr = x->coaddr;
-       if (x->type->flags & XFRM_TYPE_REMOTE_COADDR)
+               daddr = prev_daddr;
+       }
+       if (x->type->flags & XFRM_TYPE_REMOTE_COADDR) {
+               saddr = prev_saddr;
                daddr = x->coaddr;
+       }
 
-       afinfo = xfrm_policy_get_afinfo(family);
-       if (unlikely(afinfo == NULL))
-               return ERR_PTR(-EAFNOSUPPORT);
+       dst = __xfrm_dst_lookup(tos, saddr, daddr, family);
+
+       if (!IS_ERR(dst)) {
+               if (prev_saddr != saddr)
+                       memcpy(prev_saddr, saddr,  sizeof(*prev_saddr));
+               if (prev_daddr != daddr)
+                       memcpy(prev_daddr, daddr,  sizeof(*prev_daddr));
+       }
 
-       dst = afinfo->dst_lookup(tos, saddr, daddr);
-       xfrm_policy_put_afinfo(afinfo);
        return dst;
 }
 
@@ -199,6 +236,7 @@ struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp)
        policy = kzalloc(sizeof(struct xfrm_policy), gfp);
 
        if (policy) {
+               INIT_LIST_HEAD(&policy->bytype);
                INIT_HLIST_NODE(&policy->bydst);
                INIT_HLIST_NODE(&policy->byidx);
                rwlock_init(&policy->lock);
@@ -212,7 +250,7 @@ EXPORT_SYMBOL(xfrm_policy_alloc);
 
 /* Destroy xfrm_policy: descendant resources must be released to this moment. */
 
-void __xfrm_policy_destroy(struct xfrm_policy *policy)
+void xfrm_policy_destroy(struct xfrm_policy *policy)
 {
        BUG_ON(!policy->dead);
 
@@ -221,10 +259,14 @@ void __xfrm_policy_destroy(struct xfrm_policy *policy)
        if (del_timer(&policy->timer))
                BUG();
 
-       security_xfrm_policy_free(policy);
+       write_lock_bh(&xfrm_policy_lock);
+       list_del(&policy->bytype);
+       write_unlock_bh(&xfrm_policy_lock);
+
+       security_xfrm_policy_free(policy->security);
        kfree(policy);
 }
-EXPORT_SYMBOL(__xfrm_policy_destroy);
+EXPORT_SYMBOL(xfrm_policy_destroy);
 
 static void xfrm_policy_gc_kill(struct xfrm_policy *policy)
 {
@@ -322,15 +364,31 @@ static void xfrm_dst_hash_transfer(struct hlist_head *list,
                                   struct hlist_head *ndsttable,
                                   unsigned int nhashmask)
 {
-       struct hlist_node *entry, *tmp;
+       struct hlist_node *entry, *tmp, *entry0 = NULL;
        struct xfrm_policy *pol;
+       unsigned int h0 = 0;
 
+redo:
        hlist_for_each_entry_safe(pol, entry, tmp, list, bydst) {
                unsigned int h;
 
                h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr,
                                pol->family, nhashmask);
-               hlist_add_head(&pol->bydst, ndsttable+h);
+               if (!entry0) {
+                       hlist_del(entry);
+                       hlist_add_head(&pol->bydst, ndsttable+h);
+                       h0 = h;
+               } else {
+                       if (h != h0)
+                               continue;
+                       hlist_del(entry);
+                       hlist_add_after(entry0, &pol->bydst);
+               }
+               entry0 = entry;
+       }
+       if (!hlist_empty(list)) {
+               entry0 = NULL;
+               goto redo;
        }
 }
 
@@ -559,6 +617,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
        policy->curlft.use_time = 0;
        if (!mod_timer(&policy->timer, jiffies + HZ))
                xfrm_pol_hold(policy);
+       list_add_tail(&policy->bytype, &xfrm_policy_bytype[policy->type]);
        write_unlock_bh(&xfrm_policy_lock);
 
        if (delpol)
@@ -617,7 +676,8 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
                    xfrm_sec_ctx_match(ctx, pol->security)) {
                        xfrm_pol_hold(pol);
                        if (delete) {
-                               *err = security_xfrm_policy_delete(pol);
+                               *err = security_xfrm_policy_delete(
+                                                               pol->security);
                                if (*err) {
                                        write_unlock_bh(&xfrm_policy_lock);
                                        return pol;
@@ -659,7 +719,8 @@ struct xfrm_policy *xfrm_policy_byid(u8 type, int dir, u32 id, int delete,
                if (pol->type == type && pol->index == id) {
                        xfrm_pol_hold(pol);
                        if (delete) {
-                               *err = security_xfrm_policy_delete(pol);
+                               *err = security_xfrm_policy_delete(
+                                                               pol->security);
                                if (*err) {
                                        write_unlock_bh(&xfrm_policy_lock);
                                        return pol;
@@ -697,10 +758,11 @@ xfrm_policy_flush_secctx_check(u8 type, struct xfrm_audit *audit_info)
                                     &xfrm_policy_inexact[dir], bydst) {
                        if (pol->type != type)
                                continue;
-                       err = security_xfrm_policy_delete(pol);
+                       err = security_xfrm_policy_delete(pol->security);
                        if (err) {
                                xfrm_audit_policy_delete(pol, 0,
                                                         audit_info->loginuid,
+                                                        audit_info->sessionid,
                                                         audit_info->secid);
                                return err;
                        }
@@ -711,10 +773,12 @@ xfrm_policy_flush_secctx_check(u8 type, struct xfrm_audit *audit_info)
                                             bydst) {
                                if (pol->type != type)
                                        continue;
-                               err = security_xfrm_policy_delete(pol);
+                               err = security_xfrm_policy_delete(
+                                                               pol->security);
                                if (err) {
                                        xfrm_audit_policy_delete(pol, 0,
                                                        audit_info->loginuid,
+                                                       audit_info->sessionid,
                                                        audit_info->secid);
                                        return err;
                                }
@@ -757,6 +821,7 @@ int xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info)
                        write_unlock_bh(&xfrm_policy_lock);
 
                        xfrm_audit_policy_delete(pol, 1, audit_info->loginuid,
+                                                audit_info->sessionid,
                                                 audit_info->secid);
 
                        xfrm_policy_kill(pol);
@@ -779,6 +844,7 @@ int xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info)
 
                                xfrm_audit_policy_delete(pol, 1,
                                                         audit_info->loginuid,
+                                                        audit_info->sessionid,
                                                         audit_info->secid);
                                xfrm_policy_kill(pol);
                                killed++;
@@ -797,57 +863,60 @@ out:
 }
 EXPORT_SYMBOL(xfrm_policy_flush);
 
-int xfrm_policy_walk(u8 type, int (*func)(struct xfrm_policy *, int, int, void*),
+int xfrm_policy_walk(struct xfrm_policy_walk *walk,
+                    int (*func)(struct xfrm_policy *, int, int, void*),
                     void *data)
 {
-       struct xfrm_policy *pol, *last = NULL;
-       struct hlist_node *entry;
-       int dir, last_dir = 0, count, error;
+       struct xfrm_policy *old, *pol, *last = NULL;
+       int error = 0;
+
+       if (walk->type >= XFRM_POLICY_TYPE_MAX &&
+           walk->type != XFRM_POLICY_TYPE_ANY)
+               return -EINVAL;
 
+       if (walk->policy == NULL && walk->count != 0)
+               return 0;
+
+       old = pol = walk->policy;
+       walk->policy = NULL;
        read_lock_bh(&xfrm_policy_lock);
-       count = 0;
 
-       for (dir = 0; dir < 2*XFRM_POLICY_MAX; dir++) {
-               struct hlist_head *table = xfrm_policy_bydst[dir].table;
-               int i;
+       for (; walk->cur_type < XFRM_POLICY_TYPE_MAX; walk->cur_type++) {
+               if (walk->type != walk->cur_type &&
+                   walk->type != XFRM_POLICY_TYPE_ANY)
+                       continue;
 
-               hlist_for_each_entry(pol, entry,
-                                    &xfrm_policy_inexact[dir], bydst) {
-                       if (pol->type != type)
+               if (pol == NULL) {
+                       pol = list_first_entry(&xfrm_policy_bytype[walk->cur_type],
+                                              struct xfrm_policy, bytype);
+               }
+               list_for_each_entry_from(pol, &xfrm_policy_bytype[walk->cur_type], bytype) {
+                       if (pol->dead)
                                continue;
                        if (last) {
-                               error = func(last, last_dir % XFRM_POLICY_MAX,
-                                            count, data);
-                               if (error)
+                               error = func(last, xfrm_policy_id2dir(last->index),
+                                            walk->count, data);
+                               if (error) {
+                                       xfrm_pol_hold(last);
+                                       walk->policy = last;
                                        goto out;
-                       }
-                       last = pol;
-                       last_dir = dir;
-                       count++;
-               }
-               for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
-                       hlist_for_each_entry(pol, entry, table + i, bydst) {
-                               if (pol->type != type)
-                                       continue;
-                               if (last) {
-                                       error = func(last, last_dir % XFRM_POLICY_MAX,
-                                                    count, data);
-                                       if (error)
-                                               goto out;
                                }
-                               last = pol;
-                               last_dir = dir;
-                               count++;
                        }
+                       last = pol;
+                       walk->count++;
                }
+               pol = NULL;
        }
-       if (count == 0) {
+       if (walk->count == 0) {
                error = -ENOENT;
                goto out;
        }
-       error = func(last, last_dir % XFRM_POLICY_MAX, 0, data);
+       if (last)
+               error = func(last, xfrm_policy_id2dir(last->index), 0, data);
 out:
        read_unlock_bh(&xfrm_policy_lock);
+       if (old != NULL)
+               xfrm_pol_put(old);
        return error;
 }
 EXPORT_SYMBOL(xfrm_policy_walk);
@@ -869,7 +938,8 @@ static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl,
 
        match = xfrm_selector_match(sel, fl, family);
        if (match)
-               ret = security_xfrm_policy_lookup(pol, fl->secid, dir);
+               ret = security_xfrm_policy_lookup(pol->security, fl->secid,
+                                                 dir);
 
        return ret;
 }
@@ -986,8 +1056,9 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struc
                int err = 0;
 
                if (match) {
-                       err = security_xfrm_policy_lookup(pol, fl->secid,
-                                       policy_to_flow_dir(dir));
+                       err = security_xfrm_policy_lookup(pol->security,
+                                                     fl->secid,
+                                                     policy_to_flow_dir(dir));
                        if (!err)
                                xfrm_pol_hold(pol);
                        else if (err == -ESRCH)
@@ -1006,6 +1077,7 @@ static void __xfrm_policy_link(struct xfrm_policy *pol, int dir)
        struct hlist_head *chain = policy_hash_bysel(&pol->selector,
                                                     pol->family, dir);
 
+       list_add_tail(&pol->bytype, &xfrm_policy_bytype[pol->type]);
        hlist_add_head(&pol->bydst, chain);
        hlist_add_head(&pol->byidx, xfrm_policy_byidx+idx_hash(pol->index));
        xfrm_policy_count[dir]++;
@@ -1076,7 +1148,8 @@ static struct xfrm_policy *clone_policy(struct xfrm_policy *old, int dir)
 
        if (newp) {
                newp->selector = old->selector;
-               if (security_xfrm_policy_clone(old, newp)) {
+               if (security_xfrm_policy_clone(old->security,
+                                              &newp->security)) {
                        kfree(newp);
                        return NULL;  /* ENOMEM */
                }
@@ -1319,6 +1392,9 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
        int trailer_len = 0;
        int tos;
        int family = policy->selector.family;
+       xfrm_address_t saddr, daddr;
+
+       xfrm_flowi_addr_get(fl, &saddr, &daddr, family);
 
        tos = xfrm_get_tos(fl, family);
        err = tos;
@@ -1349,7 +1425,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
 
                if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
                        family = xfrm[i]->props.family;
-                       dst = xfrm_dst_lookup(xfrm[i], tos, family);
+                       dst = xfrm_dst_lookup(xfrm[i], tos, &saddr, &daddr,
+                                             family);
                        err = PTR_ERR(dst);
                        if (IS_ERR(dst))
                                goto put_states;
@@ -1486,8 +1563,10 @@ restart:
        if (sk && sk->sk_policy[XFRM_POLICY_OUT]) {
                policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
                err = PTR_ERR(policy);
-               if (IS_ERR(policy))
+               if (IS_ERR(policy)) {
+                       XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLERROR);
                        goto dropdst;
+               }
        }
 
        if (!policy) {
@@ -1499,8 +1578,10 @@ restart:
                policy = flow_cache_lookup(fl, dst_orig->ops->family,
                                           dir, xfrm_policy_lookup);
                err = PTR_ERR(policy);
-               if (IS_ERR(policy))
+               if (IS_ERR(policy)) {
+                       XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLERROR);
                        goto dropdst;
+               }
        }
 
        if (!policy)
@@ -1521,6 +1602,7 @@ restart:
        default:
        case XFRM_POLICY_BLOCK:
                /* Prohibit the flow */
+               XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLBLOCK);
                err = -EPERM;
                goto error;
 
@@ -1540,6 +1622,7 @@ restart:
                 */
                dst = xfrm_find_bundle(fl, policy, family);
                if (IS_ERR(dst)) {
+                       XFRM_INC_STATS(LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
                        err = PTR_ERR(dst);
                        goto error;
                }
@@ -1554,10 +1637,12 @@ restart:
                                                            XFRM_POLICY_OUT);
                        if (pols[1]) {
                                if (IS_ERR(pols[1])) {
+                                       XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLERROR);
                                        err = PTR_ERR(pols[1]);
                                        goto error;
                                }
                                if (pols[1]->action == XFRM_POLICY_BLOCK) {
+                                       XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLBLOCK);
                                        err = -EPERM;
                                        goto error;
                                }
@@ -1588,6 +1673,7 @@ restart:
                                /* EREMOTE tells the caller to generate
                                 * a one-shot blackhole route.
                                 */
+                               XFRM_INC_STATS(LINUX_MIB_XFRMOUTNOSTATES);
                                xfrm_pol_put(policy);
                                return -EREMOTE;
                        }
@@ -1603,6 +1689,7 @@ restart:
                                nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family);
 
                                if (nx == -EAGAIN && signal_pending(current)) {
+                                       XFRM_INC_STATS(LINUX_MIB_XFRMOUTNOSTATES);
                                        err = -ERESTART;
                                        goto error;
                                }
@@ -1613,8 +1700,10 @@ restart:
                                }
                                err = nx;
                        }
-                       if (err < 0)
+                       if (err < 0) {
+                               XFRM_INC_STATS(LINUX_MIB_XFRMOUTNOSTATES);
                                goto error;
+                       }
                }
                if (nx == 0) {
                        /* Flow passes not transformed. */
@@ -1624,8 +1713,10 @@ restart:
 
                dst = xfrm_bundle_create(policy, xfrm, nx, fl, dst_orig);
                err = PTR_ERR(dst);
-               if (IS_ERR(dst))
+               if (IS_ERR(dst)) {
+                       XFRM_INC_STATS(LINUX_MIB_XFRMOUTBUNDLEGENERROR);
                        goto error;
+               }
 
                for (pi = 0; pi < npols; pi++) {
                        read_lock_bh(&pols[pi]->lock);
@@ -1641,9 +1732,12 @@ restart:
                         * We can't enlist stable bundles either.
                         */
                        write_unlock_bh(&policy->lock);
-                       if (dst)
-                               dst_free(dst);
+                       dst_free(dst);
 
+                       if (pol_dead)
+                               XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLDEAD);
+                       else
+                               XFRM_INC_STATS(LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
                        err = -EHOSTUNREACH;
                        goto error;
                }
@@ -1654,8 +1748,8 @@ restart:
                        err = xfrm_dst_update_origin(dst, fl);
                if (unlikely(err)) {
                        write_unlock_bh(&policy->lock);
-                       if (dst)
-                               dst_free(dst);
+                       dst_free(dst);
+                       XFRM_INC_STATS(LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
                        goto error;
                }
 
@@ -1728,7 +1822,7 @@ xfrm_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x,
                (x->id.spi == tmpl->id.spi || !tmpl->id.spi) &&
                (x->props.reqid == tmpl->reqid || !tmpl->reqid) &&
                x->props.mode == tmpl->mode &&
-               ((tmpl->aalgos & (1<<x->props.aalgo)) ||
+               (tmpl->allalgs || (tmpl->aalgos & (1<<x->props.aalgo)) ||
                 !(xfrm_id_proto_match(tmpl->id.proto, IPSEC_PROTO_ANY))) &&
                !(x->props.mode != XFRM_MODE_TRANSPORT &&
                  xfrm_state_addr_cmp(tmpl, x, family));
@@ -1809,8 +1903,11 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
        dir &= XFRM_POLICY_MASK;
        fl_dir = policy_to_flow_dir(dir);
 
-       if (__xfrm_decode_session(skb, &fl, family, reverse) < 0)
+       if (__xfrm_decode_session(skb, &fl, family, reverse) < 0) {
+               XFRM_INC_STATS(LINUX_MIB_XFRMINHDRERROR);
                return 0;
+       }
+
        nf_nat_decode_session(skb, &fl, family);
 
        /* First, check used SA against their selectors. */
@@ -1819,28 +1916,35 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
 
                for (i=skb->sp->len-1; i>=0; i--) {
                        struct xfrm_state *x = skb->sp->xvec[i];
-                       if (!xfrm_selector_match(&x->sel, &fl, family))
+                       if (!xfrm_selector_match(&x->sel, &fl, family)) {
+                               XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEMISMATCH);
                                return 0;
+                       }
                }
        }
 
        pol = NULL;
        if (sk && sk->sk_policy[dir]) {
                pol = xfrm_sk_policy_lookup(sk, dir, &fl);
-               if (IS_ERR(pol))
+               if (IS_ERR(pol)) {
+                       XFRM_INC_STATS(LINUX_MIB_XFRMINPOLERROR);
                        return 0;
+               }
        }
 
        if (!pol)
                pol = flow_cache_lookup(&fl, family, fl_dir,
                                        xfrm_policy_lookup);
 
-       if (IS_ERR(pol))
+       if (IS_ERR(pol)) {
+               XFRM_INC_STATS(LINUX_MIB_XFRMINPOLERROR);
                return 0;
+       }
 
        if (!pol) {
                if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) {
                        xfrm_secpath_reject(xerr_idx, skb, &fl);
+                       XFRM_INC_STATS(LINUX_MIB_XFRMINNOPOLS);
                        return 0;
                }
                return 1;
@@ -1856,8 +1960,10 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
                                                    &fl, family,
                                                    XFRM_POLICY_IN);
                if (pols[1]) {
-                       if (IS_ERR(pols[1]))
+                       if (IS_ERR(pols[1])) {
+                               XFRM_INC_STATS(LINUX_MIB_XFRMINPOLERROR);
                                return 0;
+                       }
                        pols[1]->curlft.use_time = get_seconds();
                        npols ++;
                }
@@ -1878,10 +1984,14 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
 
                for (pi = 0; pi < npols; pi++) {
                        if (pols[pi] != pol &&
-                           pols[pi]->action != XFRM_POLICY_ALLOW)
+                           pols[pi]->action != XFRM_POLICY_ALLOW) {
+                               XFRM_INC_STATS(LINUX_MIB_XFRMINPOLBLOCK);
                                goto reject;
-                       if (ti + pols[pi]->xfrm_nr >= XFRM_MAX_DEPTH)
+                       }
+                       if (ti + pols[pi]->xfrm_nr >= XFRM_MAX_DEPTH) {
+                               XFRM_INC_STATS(LINUX_MIB_XFRMINBUFFERERROR);
                                goto reject_error;
+                       }
                        for (i = 0; i < pols[pi]->xfrm_nr; i++)
                                tpp[ti++] = &pols[pi]->xfrm_vec[i];
                }
@@ -1903,16 +2013,20 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
                                if (k < -1)
                                        /* "-2 - errored_index" returned */
                                        xerr_idx = -(2+k);
+                               XFRM_INC_STATS(LINUX_MIB_XFRMINTMPLMISMATCH);
                                goto reject;
                        }
                }
 
-               if (secpath_has_nontransport(sp, k, &xerr_idx))
+               if (secpath_has_nontransport(sp, k, &xerr_idx)) {
+                       XFRM_INC_STATS(LINUX_MIB_XFRMINTMPLMISMATCH);
                        goto reject;
+               }
 
                xfrm_pols_put(pols, npols);
                return 1;
        }
+       XFRM_INC_STATS(LINUX_MIB_XFRMINPOLBLOCK);
 
 reject:
        xfrm_secpath_reject(xerr_idx, skb, &fl);
@@ -1926,8 +2040,11 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
 {
        struct flowi fl;
 
-       if (xfrm_decode_session(skb, &fl, family) < 0)
+       if (xfrm_decode_session(skb, &fl, family) < 0) {
+               /* XXX: we should have something like FWDHDRERROR here. */
+               XFRM_INC_STATS(LINUX_MIB_XFRMINHDRERROR);
                return 0;
+       }
 
        return xfrm_lookup(&skb->dst, &fl, NULL, 0) == 0;
 }
@@ -1971,7 +2088,7 @@ static int stale_bundle(struct dst_entry *dst)
 void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev)
 {
        while ((dst = dst->child) && dst->xfrm && dst->dev == dev) {
-               dst->dev = dev->nd_net->loopback_dev;
+               dst->dev = dev_net(dev)->loopback_dev;
                dev_hold(dst->dev);
                dev_put(dev);
        }
@@ -2242,7 +2359,7 @@ static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void
 {
        struct net_device *dev = ptr;
 
-       if (dev->nd_net != &init_net)
+       if (!net_eq(dev_net(dev), &init_net))
                return NOTIFY_DONE;
 
        switch (event) {
@@ -2258,6 +2375,16 @@ static struct notifier_block xfrm_dev_notifier = {
        0
 };
 
+#ifdef CONFIG_XFRM_STATISTICS
+static int __init xfrm_statistics_init(void)
+{
+       if (snmp_mib_init((void **)xfrm_statistics,
+                         sizeof(struct linux_xfrm_mib)) < 0)
+               return -ENOMEM;
+       return 0;
+}
+#endif
+
 static void __init xfrm_policy_init(void)
 {
        unsigned int hmask, sz;
@@ -2288,20 +2415,29 @@ static void __init xfrm_policy_init(void)
                        panic("XFRM: failed to allocate bydst hash\n");
        }
 
+       for (dir = 0; dir < XFRM_POLICY_TYPE_MAX; dir++)
+               INIT_LIST_HEAD(&xfrm_policy_bytype[dir]);
+
        INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task);
        register_netdevice_notifier(&xfrm_dev_notifier);
 }
 
 void __init xfrm_init(void)
 {
+#ifdef CONFIG_XFRM_STATISTICS
+       xfrm_statistics_init();
+#endif
        xfrm_state_init();
        xfrm_policy_init();
        xfrm_input_init();
+#ifdef CONFIG_XFRM_STATISTICS
+       xfrm_proc_init();
+#endif
 }
 
 #ifdef CONFIG_AUDITSYSCALL
-static inline void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,
-                                               struct audit_buffer *audit_buf)
+static void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,
+                                        struct audit_buffer *audit_buf)
 {
        struct xfrm_sec_ctx *ctx = xp->security;
        struct xfrm_selector *sel = &xp->selector;
@@ -2338,35 +2474,31 @@ static inline void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,
        }
 }
 
-void
-xfrm_audit_policy_add(struct xfrm_policy *xp, int result, u32 auid, u32 sid)
+void xfrm_audit_policy_add(struct xfrm_policy *xp, int result,
+                          uid_t auid, u32 sessionid, u32 secid)
 {
        struct audit_buffer *audit_buf;
-       extern int audit_enabled;
 
-       if (audit_enabled == 0)
-               return;
-       audit_buf = xfrm_audit_start(auid, sid);
+       audit_buf = xfrm_audit_start("SPD-add");
        if (audit_buf == NULL)
                return;
-       audit_log_format(audit_buf, " op=SPD-add res=%u", result);
+       xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
+       audit_log_format(audit_buf, " res=%u", result);
        xfrm_audit_common_policyinfo(xp, audit_buf);
        audit_log_end(audit_buf);
 }
 EXPORT_SYMBOL_GPL(xfrm_audit_policy_add);
 
-void
-xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, u32 auid, u32 sid)
+void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result,
+                             uid_t auid, u32 sessionid, u32 secid)
 {
        struct audit_buffer *audit_buf;
-       extern int audit_enabled;
 
-       if (audit_enabled == 0)
-               return;
-       audit_buf = xfrm_audit_start(auid, sid);
+       audit_buf = xfrm_audit_start("SPD-delete");
        if (audit_buf == NULL)
                return;
-       audit_log_format(audit_buf, " op=SPD-delete res=%u", result);
+       xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
+       audit_log_format(audit_buf, " res=%u", result);
        xfrm_audit_common_policyinfo(xp, audit_buf);
        audit_log_end(audit_buf);
 }