]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/xfrm/xfrm_policy.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6-omap-h63xx.git] / net / xfrm / xfrm_policy.c
index 32ddb7b12e7f01af481c2da07f7ec6f310c067a7..9fc4c315f6cd9702a223b5758d3f61e26c89808b 100644 (file)
@@ -24,6 +24,7 @@
 #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>
@@ -220,7 +221,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);
 
@@ -232,7 +233,7 @@ void __xfrm_policy_destroy(struct xfrm_policy *policy)
        security_xfrm_policy_free(policy);
        kfree(policy);
 }
-EXPORT_SYMBOL(__xfrm_policy_destroy);
+EXPORT_SYMBOL(xfrm_policy_destroy);
 
 static void xfrm_policy_gc_kill(struct xfrm_policy *policy)
 {
@@ -330,15 +331,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;
        }
 }
 
@@ -1494,8 +1511,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) {
@@ -1507,8 +1526,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)
@@ -1529,6 +1550,7 @@ restart:
        default:
        case XFRM_POLICY_BLOCK:
                /* Prohibit the flow */
+               XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLBLOCK);
                err = -EPERM;
                goto error;
 
@@ -1548,6 +1570,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;
                }
@@ -1562,10 +1585,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;
                                }
@@ -1596,6 +1621,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;
                        }
@@ -1611,6 +1637,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;
                                }
@@ -1621,8 +1648,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. */
@@ -1632,8 +1661,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);
@@ -1652,6 +1683,10 @@ restart:
                        if (dst)
                                dst_free(dst);
 
+                       if (pol_dead)
+                               XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLDEAD);
+                       else
+                               XFRM_INC_STATS(LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
                        err = -EHOSTUNREACH;
                        goto error;
                }
@@ -1664,6 +1699,7 @@ restart:
                        write_unlock_bh(&policy->lock);
                        if (dst)
                                dst_free(dst);
+                       XFRM_INC_STATS(LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
                        goto error;
                }
 
@@ -1817,8 +1853,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. */
@@ -1827,28 +1866,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;
@@ -1864,8 +1910,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 ++;
                }
@@ -1886,10 +1934,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];
                }
@@ -1911,16 +1963,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);
@@ -1934,8 +1990,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;
 }
@@ -2324,8 +2383,8 @@ void __init xfrm_init(void)
 }
 
 #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;
@@ -2362,35 +2421,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,
+                          u32 auid, 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, 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,
+                             u32 auid, 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, secid, audit_buf);
+       audit_log_format(audit_buf, " res=%u", result);
        xfrm_audit_common_policyinfo(xp, audit_buf);
        audit_log_end(audit_buf);
 }