]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/xfrm/xfrm_policy.c
[IPSEC]: Make xfrm_lookup flags argument a bit-field
[linux-2.6-omap-h63xx.git] / net / xfrm / xfrm_policy.c
index af27c193697c5ceede169a36588ed36cbd4e4abb..3d516d57b5b2dc70538a9b74b969662b95670c7b 100644 (file)
@@ -13,6 +13,7 @@
  *
  */
 
+#include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/kmod.h>
 #include <linux/list.h>
@@ -23,6 +24,7 @@
 #include <linux/netfilter.h>
 #include <linux/module.h>
 #include <linux/cache.h>
+#include <net/dst.h>
 #include <net/xfrm.h>
 #include <net/ip.h>
 
@@ -49,8 +51,7 @@ static DEFINE_SPINLOCK(xfrm_policy_gc_lock);
 
 static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family);
 static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo);
-static struct xfrm_policy_afinfo *xfrm_policy_lock_afinfo(unsigned int family);
-static void xfrm_policy_unlock_afinfo(struct xfrm_policy_afinfo *afinfo);
+static void xfrm_init_pmtu(struct dst_entry *dst);
 
 static inline int
 __xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl)
@@ -86,176 +87,26 @@ int xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl,
        return 0;
 }
 
-int xfrm_register_type(struct xfrm_type *type, unsigned short family)
-{
-       struct xfrm_policy_afinfo *afinfo = xfrm_policy_lock_afinfo(family);
-       struct xfrm_type **typemap;
-       int err = 0;
-
-       if (unlikely(afinfo == NULL))
-               return -EAFNOSUPPORT;
-       typemap = afinfo->type_map;
-
-       if (likely(typemap[type->proto] == NULL))
-               typemap[type->proto] = type;
-       else
-               err = -EEXIST;
-       xfrm_policy_unlock_afinfo(afinfo);
-       return err;
-}
-EXPORT_SYMBOL(xfrm_register_type);
-
-int xfrm_unregister_type(struct xfrm_type *type, unsigned short family)
-{
-       struct xfrm_policy_afinfo *afinfo = xfrm_policy_lock_afinfo(family);
-       struct xfrm_type **typemap;
-       int err = 0;
-
-       if (unlikely(afinfo == NULL))
-               return -EAFNOSUPPORT;
-       typemap = afinfo->type_map;
-
-       if (unlikely(typemap[type->proto] != type))
-               err = -ENOENT;
-       else
-               typemap[type->proto] = NULL;
-       xfrm_policy_unlock_afinfo(afinfo);
-       return err;
-}
-EXPORT_SYMBOL(xfrm_unregister_type);
-
-struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
-{
-       struct xfrm_policy_afinfo *afinfo;
-       struct xfrm_type **typemap;
-       struct xfrm_type *type;
-       int modload_attempted = 0;
-
-retry:
-       afinfo = xfrm_policy_get_afinfo(family);
-       if (unlikely(afinfo == NULL))
-               return NULL;
-       typemap = afinfo->type_map;
-
-       type = typemap[proto];
-       if (unlikely(type && !try_module_get(type->owner)))
-               type = NULL;
-       if (!type && !modload_attempted) {
-               xfrm_policy_put_afinfo(afinfo);
-               request_module("xfrm-type-%d-%d",
-                              (int) family, (int) proto);
-               modload_attempted = 1;
-               goto retry;
-       }
-
-       xfrm_policy_put_afinfo(afinfo);
-       return type;
-}
-
-int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl,
-                   unsigned short family)
-{
-       struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
-       int err = 0;
-
-       if (unlikely(afinfo == NULL))
-               return -EAFNOSUPPORT;
-
-       if (likely(afinfo->dst_lookup != NULL))
-               err = afinfo->dst_lookup(dst, fl);
-       else
-               err = -EINVAL;
-       xfrm_policy_put_afinfo(afinfo);
-       return err;
-}
-EXPORT_SYMBOL(xfrm_dst_lookup);
-
-void xfrm_put_type(struct xfrm_type *type)
-{
-       module_put(type->owner);
-}
-
-int xfrm_register_mode(struct xfrm_mode *mode, int family)
-{
-       struct xfrm_policy_afinfo *afinfo;
-       struct xfrm_mode **modemap;
-       int err;
-
-       if (unlikely(mode->encap >= XFRM_MODE_MAX))
-               return -EINVAL;
-
-       afinfo = xfrm_policy_lock_afinfo(family);
-       if (unlikely(afinfo == NULL))
-               return -EAFNOSUPPORT;
-
-       err = -EEXIST;
-       modemap = afinfo->mode_map;
-       if (likely(modemap[mode->encap] == NULL)) {
-               modemap[mode->encap] = mode;
-               err = 0;
-       }
-
-       xfrm_policy_unlock_afinfo(afinfo);
-       return err;
-}
-EXPORT_SYMBOL(xfrm_register_mode);
-
-int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
-{
-       struct xfrm_policy_afinfo *afinfo;
-       struct xfrm_mode **modemap;
-       int err;
-
-       if (unlikely(mode->encap >= XFRM_MODE_MAX))
-               return -EINVAL;
-
-       afinfo = xfrm_policy_lock_afinfo(family);
-       if (unlikely(afinfo == NULL))
-               return -EAFNOSUPPORT;
-
-       err = -ENOENT;
-       modemap = afinfo->mode_map;
-       if (likely(modemap[mode->encap] == mode)) {
-               modemap[mode->encap] = NULL;
-               err = 0;
-       }
-
-       xfrm_policy_unlock_afinfo(afinfo);
-       return err;
-}
-EXPORT_SYMBOL(xfrm_unregister_mode);
-
-struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
+static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos,
+                                               int family)
 {
+       xfrm_address_t *saddr = &x->props.saddr;
+       xfrm_address_t *daddr = &x->id.daddr;
        struct xfrm_policy_afinfo *afinfo;
-       struct xfrm_mode *mode;
-       int modload_attempted = 0;
+       struct dst_entry *dst;
 
-       if (unlikely(encap >= XFRM_MODE_MAX))
-               return NULL;
+       if (x->type->flags & XFRM_TYPE_LOCAL_COADDR)
+               saddr = x->coaddr;
+       if (x->type->flags & XFRM_TYPE_REMOTE_COADDR)
+               daddr = x->coaddr;
 
-retry:
        afinfo = xfrm_policy_get_afinfo(family);
        if (unlikely(afinfo == NULL))
-               return NULL;
-
-       mode = afinfo->mode_map[encap];
-       if (unlikely(mode && !try_module_get(mode->owner)))
-               mode = NULL;
-       if (!mode && !modload_attempted) {
-               xfrm_policy_put_afinfo(afinfo);
-               request_module("xfrm-mode-%d-%d", family, encap);
-               modload_attempted = 1;
-               goto retry;
-       }
+               return ERR_PTR(-EAFNOSUPPORT);
 
+       dst = afinfo->dst_lookup(tos, saddr, daddr);
        xfrm_policy_put_afinfo(afinfo);
-       return mode;
-}
-
-void xfrm_put_mode(struct xfrm_mode *mode)
-{
-       module_put(mode->owner);
+       return dst;
 }
 
 static inline unsigned long make_jiffies(long secs)
@@ -352,9 +203,8 @@ struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp)
                INIT_HLIST_NODE(&policy->byidx);
                rwlock_init(&policy->lock);
                atomic_set(&policy->refcnt, 1);
-               init_timer(&policy->timer);
-               policy->timer.data = (unsigned long)policy;
-               policy->timer.function = xfrm_policy_timer;
+               setup_timer(&policy->timer, xfrm_policy_timer,
+                               (unsigned long)policy);
        }
        return policy;
 }
@@ -1386,24 +1236,164 @@ xfrm_find_bundle(struct flowi *fl, struct xfrm_policy *policy, unsigned short fa
        return x;
 }
 
-/* Allocate chain of dst_entry's, attach known xfrm's, calculate
- * all the metrics... Shortly, bundle a bundle.
- */
+static inline int xfrm_get_tos(struct flowi *fl, int family)
+{
+       struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+       int tos;
 
-static int
-xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
-                  struct flowi *fl, struct dst_entry **dst_p,
-                  unsigned short family)
+       if (!afinfo)
+               return -EINVAL;
+
+       tos = afinfo->get_tos(fl);
+
+       xfrm_policy_put_afinfo(afinfo);
+
+       return tos;
+}
+
+static inline struct xfrm_dst *xfrm_alloc_dst(int family)
 {
-       int err;
        struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
-       if (unlikely(afinfo == NULL))
+       struct xfrm_dst *xdst;
+
+       if (!afinfo)
+               return ERR_PTR(-EINVAL);
+
+       xdst = dst_alloc(afinfo->dst_ops) ?: ERR_PTR(-ENOBUFS);
+
+       xfrm_policy_put_afinfo(afinfo);
+
+       return xdst;
+}
+
+static inline int xfrm_fill_dst(struct xfrm_dst *xdst, struct net_device *dev)
+{
+       struct xfrm_policy_afinfo *afinfo =
+               xfrm_policy_get_afinfo(xdst->u.dst.ops->family);
+       int err;
+
+       if (!afinfo)
                return -EINVAL;
-       err = afinfo->bundle_create(policy, xfrm, nx, fl, dst_p);
+
+       err = afinfo->fill_dst(xdst, dev);
+
        xfrm_policy_put_afinfo(afinfo);
+
        return err;
 }
 
+/* Allocate chain of dst_entry's, attach known xfrm's, calculate
+ * all the metrics... Shortly, bundle a bundle.
+ */
+
+static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
+                                           struct xfrm_state **xfrm, int nx,
+                                           struct flowi *fl,
+                                           struct dst_entry *dst)
+{
+       unsigned long now = jiffies;
+       struct net_device *dev;
+       struct dst_entry *dst_prev = NULL;
+       struct dst_entry *dst0 = NULL;
+       int i = 0;
+       int err;
+       int header_len = 0;
+       int trailer_len = 0;
+       int tos;
+       int family = policy->selector.family;
+
+       tos = xfrm_get_tos(fl, family);
+       err = tos;
+       if (tos < 0)
+               goto put_states;
+
+       dst_hold(dst);
+
+       for (; i < nx; i++) {
+               struct xfrm_dst *xdst = xfrm_alloc_dst(family);
+               struct dst_entry *dst1 = &xdst->u.dst;
+
+               err = PTR_ERR(xdst);
+               if (IS_ERR(xdst)) {
+                       dst_release(dst);
+                       goto put_states;
+               }
+
+               if (!dst_prev)
+                       dst0 = dst1;
+               else {
+                       dst_prev->child = dst_clone(dst1);
+                       dst1->flags |= DST_NOHASH;
+               }
+
+               xdst->route = dst;
+               memcpy(&dst1->metrics, &dst->metrics, sizeof(dst->metrics));
+
+               if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
+                       family = xfrm[i]->props.family;
+                       dst = xfrm_dst_lookup(xfrm[i], tos, family);
+                       err = PTR_ERR(dst);
+                       if (IS_ERR(dst))
+                               goto put_states;
+               } else
+                       dst_hold(dst);
+
+               dst1->xfrm = xfrm[i];
+               xdst->genid = xfrm[i]->genid;
+
+               dst1->obsolete = -1;
+               dst1->flags |= DST_HOST;
+               dst1->lastuse = now;
+
+               dst1->input = dst_discard;
+               dst1->output = xfrm[i]->outer_mode->afinfo->output;
+
+               dst1->next = dst_prev;
+               dst_prev = dst1;
+
+               header_len += xfrm[i]->props.header_len;
+               trailer_len += xfrm[i]->props.trailer_len;
+       }
+
+       dst_prev->child = dst;
+       dst0->path = dst;
+
+       err = -ENODEV;
+       dev = dst->dev;
+       if (!dev)
+               goto free_dst;
+
+       /* Copy neighbout for reachability confirmation */
+       dst0->neighbour = neigh_clone(dst->neighbour);
+
+       xfrm_init_pmtu(dst_prev);
+
+       for (dst_prev = dst0; dst_prev != dst; dst_prev = dst_prev->child) {
+               struct xfrm_dst *xdst = (struct xfrm_dst *)dst_prev;
+
+               err = xfrm_fill_dst(xdst, dev);
+               if (err)
+                       goto free_dst;
+
+               dst_prev->header_len = header_len;
+               dst_prev->trailer_len = trailer_len;
+               header_len -= xdst->u.dst.xfrm->props.header_len;
+               trailer_len -= xdst->u.dst.xfrm->props.trailer_len;
+       }
+
+out:
+       return dst0;
+
+put_states:
+       for (; i < nx; i++)
+               xfrm_state_put(xfrm[i]);
+free_dst:
+       if (dst0)
+               dst_free(dst0);
+       dst0 = ERR_PTR(err);
+       goto out;
+}
+
 static int inline
 xfrm_dst_alloc_copy(void **target, void *src, int size)
 {
@@ -1474,8 +1464,9 @@ 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))
-                       return PTR_ERR(policy);
+                       goto dropdst;
        }
 
        if (!policy) {
@@ -1486,8 +1477,9 @@ restart:
 
                policy = flow_cache_lookup(fl, dst_orig->ops->family,
                                           dir, xfrm_policy_lookup);
+               err = PTR_ERR(policy);
                if (IS_ERR(policy))
-                       return PTR_ERR(policy);
+                       goto dropdst;
        }
 
        if (!policy)
@@ -1500,6 +1492,7 @@ restart:
        xfrm_nr += pols[0]->xfrm_nr;
 
        switch (policy->action) {
+       default:
        case XFRM_POLICY_BLOCK:
                /* Prohibit the flow */
                err = -EPERM;
@@ -1572,7 +1565,7 @@ restart:
                                xfrm_pol_put(policy);
                                return -EREMOTE;
                        }
-                       if (err == -EAGAIN && flags) {
+                       if (err == -EAGAIN && (flags & XFRM_LOOKUP_WAIT)) {
                                DECLARE_WAITQUEUE(wait, current);
 
                                add_wait_queue(&km_waitq, &wait);
@@ -1603,15 +1596,10 @@ restart:
                        return 0;
                }
 
-               dst = dst_orig;
-               err = xfrm_bundle_create(policy, xfrm, nx, fl, &dst, family);
-
-               if (unlikely(err)) {
-                       int i;
-                       for (i=0; i<nx; i++)
-                               xfrm_state_put(xfrm[i]);
+               dst = xfrm_bundle_create(policy, xfrm, nx, fl, dst_orig);
+               err = PTR_ERR(dst);
+               if (IS_ERR(dst))
                        goto error;
-               }
 
                for (pi = 0; pi < npols; pi++) {
                        read_lock_bh(&pols[pi]->lock);
@@ -1656,8 +1644,9 @@ restart:
        return 0;
 
 error:
-       dst_release(dst_orig);
        xfrm_pols_put(pols, npols);
+dropdst:
+       dst_release(dst_orig);
        *dst_p = NULL;
        return err;
 }
@@ -1945,7 +1934,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 = init_net.loopback_dev;
+               dst->dev = dev->nd_net->loopback_dev;
                dev_hold(dst->dev);
                dev_put(dev);
        }
@@ -2034,7 +2023,7 @@ static int xfrm_flush_bundles(void)
        return 0;
 }
 
-void xfrm_init_pmtu(struct dst_entry *dst)
+static void xfrm_init_pmtu(struct dst_entry *dst)
 {
        do {
                struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
@@ -2055,8 +2044,6 @@ void xfrm_init_pmtu(struct dst_entry *dst)
        } while ((dst = dst->next));
 }
 
-EXPORT_SYMBOL(xfrm_init_pmtu);
-
 /* Check that the bundle accepts the flow and its components are
  * still valid.
  */
@@ -2096,7 +2083,8 @@ int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
                if (xdst->genid != dst->xfrm->genid)
                        return 0;
 
-               if (strict && fl && dst->xfrm->props.mode != XFRM_MODE_TUNNEL &&
+               if (strict && fl &&
+                   !(dst->xfrm->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
                    !xfrm_state_addr_flow_check(dst->xfrm, fl, family))
                        return 0;
 
@@ -2213,23 +2201,6 @@ static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo)
        read_unlock(&xfrm_policy_afinfo_lock);
 }
 
-static struct xfrm_policy_afinfo *xfrm_policy_lock_afinfo(unsigned int family)
-{
-       struct xfrm_policy_afinfo *afinfo;
-       if (unlikely(family >= NPROTO))
-               return NULL;
-       write_lock_bh(&xfrm_policy_afinfo_lock);
-       afinfo = xfrm_policy_afinfo[family];
-       if (unlikely(!afinfo))
-               write_unlock_bh(&xfrm_policy_afinfo_lock);
-       return afinfo;
-}
-
-static void xfrm_policy_unlock_afinfo(struct xfrm_policy_afinfo *afinfo)
-{
-       write_unlock_bh(&xfrm_policy_afinfo_lock);
-}
-
 static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
        struct net_device *dev = ptr;
@@ -2295,29 +2266,37 @@ void __init xfrm_init(void)
 static inline void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,
                                                struct audit_buffer *audit_buf)
 {
-       if (xp->security)
+       struct xfrm_sec_ctx *ctx = xp->security;
+       struct xfrm_selector *sel = &xp->selector;
+
+       if (ctx)
                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);
+                                ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
 
-       switch(xp->selector.family) {
+       switch(sel->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));
+               audit_log_format(audit_buf, " src=" NIPQUAD_FMT,
+                                NIPQUAD(sel->saddr.a4));
+               if (sel->prefixlen_s != 32)
+                       audit_log_format(audit_buf, " src_prefixlen=%d",
+                                        sel->prefixlen_s);
+               audit_log_format(audit_buf, " dst=" NIPQUAD_FMT,
+                                NIPQUAD(sel->daddr.a4));
+               if (sel->prefixlen_d != 32)
+                       audit_log_format(audit_buf, " dst_prefixlen=%d",
+                                        sel->prefixlen_d);
                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));
-               }
+               audit_log_format(audit_buf, " src=" NIP6_FMT,
+                                NIP6(*(struct in6_addr *)sel->saddr.a6));
+               if (sel->prefixlen_s != 128)
+                       audit_log_format(audit_buf, " src_prefixlen=%d",
+                                        sel->prefixlen_s);
+               audit_log_format(audit_buf, " dst=" NIP6_FMT,
+                                NIP6(*(struct in6_addr *)sel->daddr.a6));
+               if (sel->prefixlen_d != 128)
+                       audit_log_format(audit_buf, " dst_prefixlen=%d",
+                                        sel->prefixlen_d);
                break;
        }
 }
@@ -2330,7 +2309,7 @@ xfrm_audit_policy_add(struct xfrm_policy *xp, int result, u32 auid, u32 sid)
 
        if (audit_enabled == 0)
                return;
-       audit_buf = xfrm_audit_start(sid, auid);
+       audit_buf = xfrm_audit_start(auid, sid);
        if (audit_buf == NULL)
                return;
        audit_log_format(audit_buf, " op=SPD-add res=%u", result);
@@ -2347,7 +2326,7 @@ xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, u32 auid, u32 sid)
 
        if (audit_enabled == 0)
                return;
-       audit_buf = xfrm_audit_start(sid, auid);
+       audit_buf = xfrm_audit_start(auid, sid);
        if (audit_buf == NULL)
                return;
        audit_log_format(audit_buf, " op=SPD-delete res=%u", result);
@@ -2464,7 +2443,8 @@ static int xfrm_policy_migrate(struct xfrm_policy *pol,
                        if (!migrate_tmpl_match(mp, &pol->xfrm_vec[i]))
                                continue;
                        n++;
-                       if (pol->xfrm_vec[i].mode != XFRM_MODE_TUNNEL)
+                       if (pol->xfrm_vec[i].mode != XFRM_MODE_TUNNEL &&
+                           pol->xfrm_vec[i].mode != XFRM_MODE_BEET)
                                continue;
                        /* update endpoints */
                        memcpy(&pol->xfrm_vec[i].id.daddr, &mp->new_daddr,