]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/xfrm/xfrm_state.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/roland...
[linux-2.6-omap-h63xx.git] / net / xfrm / xfrm_state.c
index 3003503d0c94937d570795fcbabf8ce155155130..5dcc10b93c861df1e675589947137bf0006d392e 100644 (file)
@@ -50,6 +50,7 @@ static DEFINE_SPINLOCK(xfrm_state_lock);
  * Main use is finding SA after policy selected tunnel or transport mode.
  * Also, it can be used by ah/esp icmp error handler to find offending SA.
  */
+static LIST_HEAD(xfrm_state_all);
 static struct hlist_head *xfrm_state_bydst __read_mostly;
 static struct hlist_head *xfrm_state_bysrc __read_mostly;
 static struct hlist_head *xfrm_state_byspi __read_mostly;
@@ -216,10 +217,10 @@ static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
        write_unlock_bh(&xfrm_state_afinfo_lock);
 }
 
-int xfrm_register_type(struct xfrm_type *type, unsigned short family)
+int xfrm_register_type(const struct xfrm_type *type, unsigned short family)
 {
        struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
-       struct xfrm_type **typemap;
+       const struct xfrm_type **typemap;
        int err = 0;
 
        if (unlikely(afinfo == NULL))
@@ -235,10 +236,10 @@ int xfrm_register_type(struct xfrm_type *type, unsigned short family)
 }
 EXPORT_SYMBOL(xfrm_register_type);
 
-int xfrm_unregister_type(struct xfrm_type *type, unsigned short family)
+int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
 {
        struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
-       struct xfrm_type **typemap;
+       const struct xfrm_type **typemap;
        int err = 0;
 
        if (unlikely(afinfo == NULL))
@@ -254,11 +255,11 @@ int xfrm_unregister_type(struct xfrm_type *type, unsigned short family)
 }
 EXPORT_SYMBOL(xfrm_unregister_type);
 
-static struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
+static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
 {
        struct xfrm_state_afinfo *afinfo;
-       struct xfrm_type **typemap;
-       struct xfrm_type *type;
+       const struct xfrm_type **typemap;
+       const struct xfrm_type *type;
        int modload_attempted = 0;
 
 retry:
@@ -281,7 +282,7 @@ retry:
        return type;
 }
 
-static void xfrm_put_type(struct xfrm_type *type)
+static void xfrm_put_type(const struct xfrm_type *type)
 {
        module_put(type->owner);
 }
@@ -388,6 +389,8 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
        kfree(x->coaddr);
        if (x->inner_mode)
                xfrm_put_mode(x->inner_mode);
+       if (x->inner_mode_iaf)
+               xfrm_put_mode(x->inner_mode_iaf);
        if (x->outer_mode)
                xfrm_put_mode(x->outer_mode);
        if (x->type) {
@@ -493,7 +496,7 @@ expired:
                km_state_expired(x, 1, 0);
 
        xfrm_audit_state_delete(x, err ? 0 : 1,
-                               audit_get_loginuid(current->audit_context), 0);
+                               audit_get_loginuid(current), 0);
 
 out:
        spin_unlock(&x->lock);
@@ -510,6 +513,7 @@ struct xfrm_state *xfrm_state_alloc(void)
        if (x) {
                atomic_set(&x->refcnt, 1);
                atomic_set(&x->tunnel_users, 0);
+               INIT_LIST_HEAD(&x->all);
                INIT_HLIST_NODE(&x->bydst);
                INIT_HLIST_NODE(&x->bysrc);
                INIT_HLIST_NODE(&x->byspi);
@@ -523,6 +527,8 @@ struct xfrm_state *xfrm_state_alloc(void)
                x->lft.hard_packet_limit = XFRM_INF;
                x->replay_maxage = 0;
                x->replay_maxdiff = 0;
+               x->inner_mode = NULL;
+               x->inner_mode_iaf = NULL;
                spin_lock_init(&x->lock);
        }
        return x;
@@ -533,6 +539,10 @@ void __xfrm_state_destroy(struct xfrm_state *x)
 {
        BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
 
+       spin_lock_bh(&xfrm_state_lock);
+       list_del(&x->all);
+       spin_unlock_bh(&xfrm_state_lock);
+
        spin_lock_bh(&xfrm_state_gc_lock);
        hlist_add_head(&x->bydst, &xfrm_state_gc_list);
        spin_unlock_bh(&xfrm_state_gc_lock);
@@ -796,7 +806,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
                              selector.
                         */
                        if (x->km.state == XFRM_STATE_VALID) {
-                               if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
+                               if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
                                    !security_xfrm_state_pol_flow_match(x, pol, fl))
                                        continue;
                                if (!best ||
@@ -909,6 +919,8 @@ static void __xfrm_state_insert(struct xfrm_state *x)
 
        x->genid = ++xfrm_state_genid;
 
+       list_add_tail(&x->all, &xfrm_state_all);
+
        h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
                          x->props.reqid, x->props.family);
        hlist_add_head(&x->bydst, xfrm_state_bydst+h);
@@ -1518,36 +1530,47 @@ unlock:
 }
 EXPORT_SYMBOL(xfrm_alloc_spi);
 
-int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
+int xfrm_state_walk(struct xfrm_state_walk *walk,
+                   int (*func)(struct xfrm_state *, int, void*),
                    void *data)
 {
-       int i;
-       struct xfrm_state *x, *last = NULL;
-       struct hlist_node *entry;
-       int count = 0;
+       struct xfrm_state *old, *x, *last = NULL;
        int err = 0;
 
+       if (walk->state == NULL && walk->count != 0)
+               return 0;
+
+       old = x = walk->state;
+       walk->state = NULL;
        spin_lock_bh(&xfrm_state_lock);
-       for (i = 0; i <= xfrm_state_hmask; i++) {
-               hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
-                       if (!xfrm_id_proto_match(x->id.proto, proto))
-                               continue;
-                       if (last) {
-                               err = func(last, count, data);
-                               if (err)
-                                       goto out;
+       if (x == NULL)
+               x = list_first_entry(&xfrm_state_all, struct xfrm_state, all);
+       list_for_each_entry_from(x, &xfrm_state_all, all) {
+               if (x->km.state == XFRM_STATE_DEAD)
+                       continue;
+               if (!xfrm_id_proto_match(x->id.proto, walk->proto))
+                       continue;
+               if (last) {
+                       err = func(last, walk->count, data);
+                       if (err) {
+                               xfrm_state_hold(last);
+                               walk->state = last;
+                               goto out;
                        }
-                       last = x;
-                       count++;
                }
+               last = x;
+               walk->count++;
        }
-       if (count == 0) {
+       if (walk->count == 0) {
                err = -ENOENT;
                goto out;
        }
-       err = func(last, 0, data);
+       if (last)
+               err = func(last, 0, data);
 out:
        spin_unlock_bh(&xfrm_state_lock);
+       if (old != NULL)
+               xfrm_state_put(old);
        return err;
 }
 EXPORT_SYMBOL(xfrm_state_walk);
@@ -1645,7 +1668,6 @@ err:
        xfrm_audit_state_replay(x, skb, net_seq);
        return -EINVAL;
 }
-EXPORT_SYMBOL(xfrm_replay_check);
 
 void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
 {
@@ -1667,7 +1689,6 @@ void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
        if (xfrm_aevent_is_on())
                xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
 }
-EXPORT_SYMBOL(xfrm_replay_advance);
 
 static LIST_HEAD(xfrm_km_list);
 static DEFINE_RWLOCK(xfrm_km_lock);
@@ -1946,6 +1967,7 @@ int xfrm_state_mtu(struct xfrm_state *x, int mtu)
 int xfrm_init_state(struct xfrm_state *x)
 {
        struct xfrm_state_afinfo *afinfo;
+       struct xfrm_mode *inner_mode;
        int family = x->props.family;
        int err;
 
@@ -1964,13 +1986,48 @@ int xfrm_init_state(struct xfrm_state *x)
                goto error;
 
        err = -EPROTONOSUPPORT;
-       x->inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
-       if (x->inner_mode == NULL)
-               goto error;
 
-       if (!(x->inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
-           family != x->sel.family)
-               goto error;
+       if (x->sel.family != AF_UNSPEC) {
+               inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
+               if (inner_mode == NULL)
+                       goto error;
+
+               if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
+                   family != x->sel.family) {
+                       xfrm_put_mode(inner_mode);
+                       goto error;
+               }
+
+               x->inner_mode = inner_mode;
+       } else {
+               struct xfrm_mode *inner_mode_iaf;
+
+               inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
+               if (inner_mode == NULL)
+                       goto error;
+
+               if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
+                       xfrm_put_mode(inner_mode);
+                       goto error;
+               }
+
+               inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
+               if (inner_mode_iaf == NULL)
+                       goto error;
+
+               if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
+                       xfrm_put_mode(inner_mode_iaf);
+                       goto error;
+               }
+
+               if (x->props.family == AF_INET) {
+                       x->inner_mode = inner_mode;
+                       x->inner_mode_iaf = inner_mode_iaf;
+               } else {
+                       x->inner_mode = inner_mode_iaf;
+                       x->inner_mode_iaf = inner_mode;
+               }
+       }
 
        x->type = xfrm_get_type(x->id.proto, family);
        if (x->type == NULL)