[NET]: Make the loopback device per network namespace.
[deliverable/linux.git] / net / xfrm / xfrm_policy.c
index d0882e53b6fced18c5a4f2ef4b87b38c2a0716c7..76f172f13f90536a729ef420cfc0ecba53301de5 100644 (file)
 #include <linux/cache.h>
 #include <net/xfrm.h>
 #include <net/ip.h>
-#include <linux/audit.h>
 
 #include "xfrm_hash.h"
 
+int sysctl_xfrm_larval_drop __read_mostly;
+
 DEFINE_MUTEX(xfrm_cfg_mutex);
 EXPORT_SYMBOL(xfrm_cfg_mutex);
 
@@ -831,11 +832,65 @@ struct xfrm_policy *xfrm_policy_byid(u8 type, int dir, u32 id, int delete,
 }
 EXPORT_SYMBOL(xfrm_policy_byid);
 
-void xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info)
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+static inline int
+xfrm_policy_flush_secctx_check(u8 type, struct xfrm_audit *audit_info)
 {
-       int dir;
+       int dir, err = 0;
+
+       for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
+               struct xfrm_policy *pol;
+               struct hlist_node *entry;
+               int i;
+
+               hlist_for_each_entry(pol, entry,
+                                    &xfrm_policy_inexact[dir], bydst) {
+                       if (pol->type != type)
+                               continue;
+                       err = security_xfrm_policy_delete(pol);
+                       if (err) {
+                               xfrm_audit_policy_delete(pol, 0,
+                                                        audit_info->loginuid,
+                                                        audit_info->secid);
+                               return err;
+                       }
+               }
+               for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
+                       hlist_for_each_entry(pol, entry,
+                                            xfrm_policy_bydst[dir].table + i,
+                                            bydst) {
+                               if (pol->type != type)
+                                       continue;
+                               err = security_xfrm_policy_delete(pol);
+                               if (err) {
+                                       xfrm_audit_policy_delete(pol, 0,
+                                                       audit_info->loginuid,
+                                                       audit_info->secid);
+                                       return err;
+                               }
+                       }
+               }
+       }
+       return err;
+}
+#else
+static inline int
+xfrm_policy_flush_secctx_check(u8 type, struct xfrm_audit *audit_info)
+{
+       return 0;
+}
+#endif
+
+int xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info)
+{
+       int dir, err = 0;
 
        write_lock_bh(&xfrm_policy_lock);
+
+       err = xfrm_policy_flush_secctx_check(type, audit_info);
+       if (err)
+               goto out;
+
        for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
                struct xfrm_policy *pol;
                struct hlist_node *entry;
@@ -851,8 +906,8 @@ void xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info)
                        hlist_del(&pol->byidx);
                        write_unlock_bh(&xfrm_policy_lock);
 
-                       xfrm_audit_log(audit_info->loginuid, audit_info->secid,
-                                      AUDIT_MAC_IPSEC_DELSPD, 1, pol, NULL);
+                       xfrm_audit_policy_delete(pol, 1, audit_info->loginuid,
+                                                audit_info->secid);
 
                        xfrm_policy_kill(pol);
                        killed++;
@@ -872,11 +927,9 @@ void xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info)
                                hlist_del(&pol->byidx);
                                write_unlock_bh(&xfrm_policy_lock);
 
-                               xfrm_audit_log(audit_info->loginuid,
-                                              audit_info->secid,
-                                              AUDIT_MAC_IPSEC_DELSPD, 1,
-                                              pol, NULL);
-
+                               xfrm_audit_policy_delete(pol, 1,
+                                                        audit_info->loginuid,
+                                                        audit_info->secid);
                                xfrm_policy_kill(pol);
                                killed++;
 
@@ -888,7 +941,9 @@ void xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info)
                xfrm_policy_count[dir] -= killed;
        }
        atomic_inc(&flow_cache_genid);
+out:
        write_unlock_bh(&xfrm_policy_lock);
+       return err;
 }
 EXPORT_SYMBOL(xfrm_policy_flush);
 
@@ -1238,7 +1293,8 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl,
                xfrm_address_t *local  = saddr;
                struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i];
 
-               if (tmpl->mode == XFRM_MODE_TUNNEL) {
+               if (tmpl->mode == XFRM_MODE_TUNNEL ||
+                   tmpl->mode == XFRM_MODE_BEET) {
                        remote = &tmpl->id.daddr;
                        local = &tmpl->saddr;
                        family = tmpl->encap_family;
@@ -1390,8 +1446,8 @@ static int stale_bundle(struct dst_entry *dst);
  * At the moment we eat a raw IP route. Mostly to speed up lookups
  * on interfaces with disabled IPsec.
  */
-int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
-               struct sock *sk, int flags)
+int __xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
+                 struct sock *sk, int flags)
 {
        struct xfrm_policy *policy;
        struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
@@ -1416,7 +1472,7 @@ restart:
        pol_dead = 0;
        xfrm_nr = 0;
 
-       if (sk && sk->sk_policy[1]) {
+       if (sk && sk->sk_policy[XFRM_POLICY_OUT]) {
                policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
                if (IS_ERR(policy))
                        return PTR_ERR(policy);
@@ -1509,6 +1565,13 @@ restart:
 
                if (unlikely(nx<0)) {
                        err = nx;
+                       if (err == -EAGAIN && sysctl_xfrm_larval_drop) {
+                               /* EREMOTE tells the caller to generate
+                                * a one-shot blackhole route.
+                                */
+                               xfrm_pol_put(policy);
+                               return -EREMOTE;
+                       }
                        if (err == -EAGAIN && flags) {
                                DECLARE_WAITQUEUE(wait, current);
 
@@ -1598,6 +1661,21 @@ error:
        *dst_p = NULL;
        return err;
 }
+EXPORT_SYMBOL(__xfrm_lookup);
+
+int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
+               struct sock *sk, int flags)
+{
+       int err = __xfrm_lookup(dst_p, fl, sk, flags);
+
+       if (err == -EREMOTE) {
+               dst_release(*dst_p);
+               *dst_p = NULL;
+               err = -EAGAIN;
+       }
+
+       return err;
+}
 EXPORT_SYMBOL(xfrm_lookup);
 
 static inline int
@@ -1871,8 +1949,8 @@ 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 = &loopback_dev;
-               dev_hold(&loopback_dev);
+               dst->dev = init_net.loopback_dev;
+               dev_hold(dst->dev);
                dev_put(dev);
        }
 }
@@ -2058,7 +2136,7 @@ int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
                if (last == first)
                        break;
 
-               last = last->u.next;
+               last = (struct xfrm_dst *)last->u.dst.next;
                last->child_mtu_cached = mtu;
        }
 
@@ -2067,122 +2145,6 @@ int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
 
 EXPORT_SYMBOL(xfrm_bundle_ok);
 
-#ifdef CONFIG_AUDITSYSCALL
-/* Audit addition and deletion of SAs and ipsec policy */
-
-void xfrm_audit_log(uid_t auid, u32 sid, int type, int result,
-                   struct xfrm_policy *xp, struct xfrm_state *x)
-{
-
-       char *secctx;
-       u32 secctx_len;
-       struct xfrm_sec_ctx *sctx = NULL;
-       struct audit_buffer *audit_buf;
-       int family;
-       extern int audit_enabled;
-
-       if (audit_enabled == 0)
-               return;
-
-       BUG_ON((type == AUDIT_MAC_IPSEC_ADDSA ||
-               type == AUDIT_MAC_IPSEC_DELSA) && !x);
-       BUG_ON((type == AUDIT_MAC_IPSEC_ADDSPD ||
-               type == AUDIT_MAC_IPSEC_DELSPD) && !xp);
-
-       audit_buf = audit_log_start(current->audit_context, GFP_ATOMIC, type);
-       if (audit_buf == NULL)
-               return;
-
-       switch(type) {
-       case AUDIT_MAC_IPSEC_ADDSA:
-               audit_log_format(audit_buf, "SAD add: auid=%u", auid);
-               break;
-       case AUDIT_MAC_IPSEC_DELSA:
-               audit_log_format(audit_buf, "SAD delete: auid=%u", auid);
-               break;
-       case AUDIT_MAC_IPSEC_ADDSPD:
-               audit_log_format(audit_buf, "SPD add: auid=%u", auid);
-               break;
-       case AUDIT_MAC_IPSEC_DELSPD:
-               audit_log_format(audit_buf, "SPD delete: auid=%u", auid);
-               break;
-       default:
-               return;
-       }
-
-       if (sid != 0 &&
-               security_secid_to_secctx(sid, &secctx, &secctx_len) == 0)
-               audit_log_format(audit_buf, " subj=%s", secctx);
-       else
-               audit_log_task_context(audit_buf);
-
-       if (xp) {
-               family = xp->selector.family;
-               if (xp->security)
-                       sctx = xp->security;
-       } else {
-               family = x->props.family;
-               if (x->security)
-                       sctx = x->security;
-       }
-
-       if (sctx)
-               audit_log_format(audit_buf,
-                               " sec_alg=%u sec_doi=%u sec_obj=%s",
-                               sctx->ctx_alg, sctx->ctx_doi, sctx->ctx_str);
-
-       switch(family) {
-       case AF_INET:
-               {
-                       struct in_addr saddr, daddr;
-                       if (xp) {
-                               saddr.s_addr = xp->selector.saddr.a4;
-                               daddr.s_addr = xp->selector.daddr.a4;
-                       } else {
-                               saddr.s_addr = x->props.saddr.a4;
-                               daddr.s_addr = x->id.daddr.a4;
-                       }
-                       audit_log_format(audit_buf,
-                                        " src=%u.%u.%u.%u dst=%u.%u.%u.%u",
-                                        NIPQUAD(saddr), NIPQUAD(daddr));
-               }
-                       break;
-       case AF_INET6:
-               {
-                       struct in6_addr saddr6, daddr6;
-                       if (xp) {
-                               memcpy(&saddr6, xp->selector.saddr.a6,
-                                       sizeof(struct in6_addr));
-                               memcpy(&daddr6, xp->selector.daddr.a6,
-                                       sizeof(struct in6_addr));
-                       } else {
-                               memcpy(&saddr6, x->props.saddr.a6,
-                                       sizeof(struct in6_addr));
-                               memcpy(&daddr6, x->id.daddr.a6,
-                                       sizeof(struct in6_addr));
-                       }
-                       audit_log_format(audit_buf,
-                                        " src=" NIP6_FMT " dst=" NIP6_FMT,
-                                        NIP6(saddr6), NIP6(daddr6));
-               }
-               break;
-       }
-
-       if (x)
-               audit_log_format(audit_buf, " spi=%lu(0x%lx) protocol=%s",
-                               (unsigned long)ntohl(x->id.spi),
-                               (unsigned long)ntohl(x->id.spi),
-                               x->id.proto == IPPROTO_AH ? "AH" :
-                               (x->id.proto == IPPROTO_ESP ?
-                               "ESP" : "IPCOMP"));
-
-       audit_log_format(audit_buf, " res=%u", result);
-       audit_log_end(audit_buf);
-}
-
-EXPORT_SYMBOL(xfrm_audit_log);
-#endif /* CONFIG_AUDITSYSCALL */
-
 int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
 {
        int err = 0;
@@ -2274,6 +2236,11 @@ static void xfrm_policy_unlock_afinfo(struct xfrm_policy_afinfo *afinfo)
 
 static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
+       struct net_device *dev = ptr;
+
+       if (dev->nd_net != &init_net)
+               return NOTIFY_DONE;
+
        switch (event) {
        case NETDEV_DOWN:
                xfrm_flush_bundles();
@@ -2295,7 +2262,7 @@ static void __init xfrm_policy_init(void)
        xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
                                           sizeof(struct xfrm_dst),
                                           0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
-                                          NULL, NULL);
+                                          NULL);
 
        hmask = 8 - 1;
        sz = (hmask+1) * sizeof(struct hlist_head);
@@ -2328,6 +2295,72 @@ void __init xfrm_init(void)
        xfrm_input_init();
 }
 
+#ifdef CONFIG_AUDITSYSCALL
+static inline void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,
+                                               struct audit_buffer *audit_buf)
+{
+       if (xp->security)
+               audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
+                                xp->security->ctx_alg, xp->security->ctx_doi,
+                                xp->security->ctx_str);
+
+       switch(xp->selector.family) {
+       case AF_INET:
+               audit_log_format(audit_buf, " src=%u.%u.%u.%u dst=%u.%u.%u.%u",
+                                NIPQUAD(xp->selector.saddr.a4),
+                                NIPQUAD(xp->selector.daddr.a4));
+               break;
+       case AF_INET6:
+               {
+                       struct in6_addr saddr6, daddr6;
+
+                       memcpy(&saddr6, xp->selector.saddr.a6,
+                               sizeof(struct in6_addr));
+                       memcpy(&daddr6, xp->selector.daddr.a6,
+                               sizeof(struct in6_addr));
+                       audit_log_format(audit_buf,
+                               " src=" NIP6_FMT " dst=" NIP6_FMT,
+                               NIP6(saddr6), NIP6(daddr6));
+               }
+               break;
+       }
+}
+
+void
+xfrm_audit_policy_add(struct xfrm_policy *xp, int result, u32 auid, u32 sid)
+{
+       struct audit_buffer *audit_buf;
+       extern int audit_enabled;
+
+       if (audit_enabled == 0)
+               return;
+       audit_buf = xfrm_audit_start(sid, auid);
+       if (audit_buf == NULL)
+               return;
+       audit_log_format(audit_buf, " op=SPD-add 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)
+{
+       struct audit_buffer *audit_buf;
+       extern int audit_enabled;
+
+       if (audit_enabled == 0)
+               return;
+       audit_buf = xfrm_audit_start(sid, auid);
+       if (audit_buf == NULL)
+               return;
+       audit_log_format(audit_buf, " op=SPD-delete res=%u", result);
+       xfrm_audit_common_policyinfo(xp, audit_buf);
+       audit_log_end(audit_buf);
+}
+EXPORT_SYMBOL_GPL(xfrm_audit_policy_delete);
+#endif
+
 #ifdef CONFIG_XFRM_MIGRATE
 static int xfrm_migrate_selector_match(struct xfrm_selector *sel_cmp,
                                       struct xfrm_selector *sel_tgt)
@@ -2558,4 +2591,3 @@ restore_state:
 }
 EXPORT_SYMBOL(xfrm_migrate);
 #endif
-
This page took 0.029496 seconds and 5 git commands to generate.