Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux...
[sfrench/cifs-2.6.git] / net / xfrm / xfrm_state.c
index 3003503d0c94937d570795fcbabf8ce155155130..58f1f9347b54515e0bdfe534940adf3263d40755 100644 (file)
@@ -216,10 +216,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 +235,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 +254,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 +281,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 +388,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 +495,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);
@@ -523,6 +525,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;
@@ -796,7 +800,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 ||
@@ -1645,7 +1649,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 +1670,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 +1948,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 +1967,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)