Hi,

with the Linux 2.6.28 kernel the first time xfrm_user.c:xfrm_add_sa()
is called with either "rfc4106(gcm(aes))" or "rfc4309(ccm(aes))",
an EEXIST error is returned and the installation of the IPsec SA fails.
A detailed analysis of the function calls (see trace added below) shows
that aead.c:crypto_lookup_aead() first loads

name         : rfc4106(gcm(aes))
driver       : rfc4106(gcm_base(ctr(aes-generic)))
module       : kernel
priority     : 100
refcnt       : 1
selftest     : passed
type         : nivaead
async        : yes
blocksize    : 1
ivsize       : 8
maxauthsize  : 16
geniv        : seqiv

Because of type=nivaead

    if (alg->cra_type == &crypto_aead_type)
        return alg;

crypto_lookup_aead() does not return the algorithm and falls through
to the statement

    return ERR_PTR(crypto_nivaead_default(alg, type, mask));

which makes another call to algapi.c;__crypto_register_alg().
Because the driver has already been installed

    ret = -EEXIST;
    ...

        if (crypto_is_larval(q)) {
            if (!strcmp(alg->cra_driver_name, q->cra_driver_name))
                goto err;
            continue;
        }

__crypto_register_alg() exits with the EEXIST error code.

The second time around the type=aead algorithm is additionally loaded

name         : rfc4106(gcm(aes))
driver       : rfc4106(gcm_base(ctr(aes-generic)))
module       : kernel
priority     : 100
refcnt       : 1
selftest     : passed
type         : aead
async        : yes
blocksize    : 1
ivsize       : 8
maxauthsize  : 16
geniv        : seqiv

name         : rfc4106(gcm(aes))
driver       : rfc4106(gcm_base(ctr(aes-generic)))
module       : kernel
priority     : 100
refcnt       : 1
selftest     : passed
type         : nivaead
async        : yes
blocksize    : 1
ivsize       : 8
maxauthsize  : 16
geniv        : seqiv

and the IPsec SAs can be set up successfully.

With the Linux 2.6.27.10 and earlier kernels the aead type is
loaded right at the beginning and no problems occur:

name         : rfc4106(gcm(aes))
driver       : rfc4106(gcm_base(ctr(aes-asm)))
module       : kernel
priority     : 200
refcnt       : 1
type         : aead
async        : yes
blocksize    : 1
ivsize       : 8
maxauthsize  : 16
geniv        : seqiv

name         : rfc4106(gcm(aes))
driver       : rfc4106(gcm_base(ctr(aes-asm)))
module       : kernel
priority     : 200
refcnt       : 1
type         : nivaead
async        : yes
blocksize    : 1
ivsize       : 8
maxauthsize  : 16
geniv        : seqiv

Best regards

Andreas

Trace of function calls returning EEXIST:

-----------------------------------------------------------------------------
net/xfrm/xfrm_user.c:xfrm_add_sa()
{
        x = xfrm_state_construct(p, attrs, &err);
        if (!x)
                return err;
}

-----------------------------------------------------------------------------
net/xfrm/xfrm_user.c:xfrm_state_construct()
{
        err = xfrm_init_state(x);
        if (err)
                goto error;
    ...
error:
        x->km.state = XFRM_STATE_DEAD;
        xfrm_state_put(x);
error_no_put:
        *errp = err;
        return NULL;
}

-----------------------------------------------------------------------------
net/xfrm/xfrm_state.c:xfrm_init_state()
{
        x->type = xfrm_get_type(x->id.proto, family);
        if (x->type == NULL)
                goto error;

        err = x->type->init_state(x);
        if (err)
                goto error;
    ...
error:
        return err;
}

-----------------------------------------------------------------------------
linux/net/xfrm.h:struct xfrm_type
{
        char                    *description;
        struct module           *owner;
        __u8                    proto;
        __u8                    flags;
#define XFRM_TYPE_NON_FRAGMENT  1
#define XFRM_TYPE_REPLAY_PROT   2
#define XFRM_TYPE_LOCAL_COADDR  4
#define XFRM_TYPE_REMOTE_COADDR 8

        int                     (*init_state)(struct xfrm_state *x);
        void                    (*destructor)(struct xfrm_state *);
        int                     (*input)(struct xfrm_state *, struct sk_buff 
*skb);
        int                     (*output)(struct xfrm_state *, struct sk_buff 
*pskb);
        int                     (*reject)(struct xfrm_state *, struct sk_buff 
*, struct flowi *);
        int                     (*hdr_offset)(struct xfrm_state *, struct 
sk_buff *, u8 **);
        /* Estimate maximal size of result of transformation of a dgram */
        u32                     (*get_mtu)(struct xfrm_state *, int size);
};

-----------------------------------------------------------------------------
net/ipv4/esp4.c:esp_init_state()
{
        if (x->aead)
                err = esp_init_aead(x);
        }
        else
                err = esp_init_authenc(x);

        if (err)
                goto error;

}

-----------------------------------------------------------------------------
net/ipv4/esp4.c:esp_init_aead()
{
        struct esp_data *esp = x->data;
        struct crypto_aead *aead;
        int err;

        aead = crypto_alloc_aead(x->aead->alg_name, 0, 0);
        err = PTR_ERR(aead);
        if (IS_ERR(aead))
                goto error;

error:
        return err;
}

-----------------------------------------------------------------------------
crypto/aead.c:crypto_alloc_aead()
{
                alg = crypto_lookup_aead(alg_name, type, mask);
                if (IS_ERR(alg)) {
                        err = PTR_ERR(alg);
                        goto err;
                }

err:
                if (err != -EAGAIN)
                        break;
                if (signal_pending(current)) {
                        err = -EINTR;
                        break;
                }
        }

        return ERR_PTR(err);
}

-----------------------------------------------------------------------------
crypto/aead.c:crypto_lookup_aead()
{
        struct crypto_alg *alg;

        alg = crypto_alg_mod_lookup(name, type, mask);
        if (IS_ERR(alg))
                return alg;

    /* after the first call alg->cra_type is &crypto_nivaead_type */

        if (alg->cra_type == &crypto_aead_type)
                return alg;

        if (!alg->cra_aead.ivsize)
                return alg;

        return ERR_PTR(crypto_nivaead_default(alg, type, mask));
}

-----------------------------------------------------------------------------
crypto/aead.c:crypto_nivaead_default()
{
        if ((err = crypto_register_instance(tmpl, inst))) {
                tmpl->free(inst);
                goto put_tmpl;
        }

        /* Redo the lookup to use the instance we just registered. */
        err = -EAGAIN;

put_tmpl:
        crypto_tmpl_put(tmpl);
kill_larval:
        crypto_larval_kill(larval);
drop_larval:
        crypto_mod_put(larval);
out:
        crypto_mod_put(alg);
        return err;
}

-----------------------------------------------------------------------------
crypto/algapi.c:crypto_register_instance()
{
        larval = __crypto_register_alg(&inst->alg);
        if (IS_ERR(larval))
                goto unlock;

        hlist_add_head(&inst->list, &tmpl->instances);
        inst->tmpl = tmpl;

unlock:
        up_write(&crypto_alg_sem);

        err = PTR_ERR(larval);
        if (IS_ERR(larval))
                goto err;

        crypto_wait_for_test(larval);
        err = 0;

err:
        return err;
}

-----------------------------------------------------------------------------
crypto/algapi.c:__crypto_register_alg()
{
        ret = -EEXIST;
    ...

                if (crypto_is_larval(q)) {
                        if (!strcmp(alg->cra_driver_name, q->cra_driver_name))
                                goto err;
                        continue;
                }
}

Uff!

======================================================================
Andreas Steffen                         andreas.stef...@strongswan.org
strongSwan - the Linux VPN Solution!                www.strongswan.org
              Institute for Internet Technologies and Applications
University of Applied Sciences Rapperswil
CH-8640 Rapperswil (Switzerland)
===========================================================[ITA-HSR]==

--
To unsubscribe from this list: send the line "unsubscribe linux-crypto" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to