diff mbox series

[12/22] tests/qtest/migration: Split TLS tests from migration-test.c

Message ID 20241105180837.5990-13-farosas@suse.de (mailing list archive)
State New
Headers show
Series tests/qtest: migration-test refactoring | expand

Commit Message

Fabiano Rosas Nov. 5, 2024, 6:08 p.m. UTC
The migration-test.c file has become unwieldy large. It's quite
confusing to navigate with all the test definitions mixed with hook
definitions. The TLS tests make this worse with ifdef'ery.

Since we're planning on having a smaller set of tests to run as smoke
testing on all architectures, I'm taking the time to split some tests
into their own file.

Move the TLS tests into a file of their own.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 tests/qtest/meson.build                  |   8 +-
 tests/qtest/migration-test.c             | 788 +---------------------
 tests/qtest/migration/migration-common.h |   6 +
 tests/qtest/migration/tls-tests.c        | 790 +++++++++++++++++++++++
 4 files changed, 803 insertions(+), 789 deletions(-)
 create mode 100644 tests/qtest/migration/tls-tests.c

Comments

Daniel P. Berrangé Nov. 6, 2024, 11:15 a.m. UTC | #1
On Tue, Nov 05, 2024 at 03:08:27PM -0300, Fabiano Rosas wrote:
> The migration-test.c file has become unwieldy large. It's quite
> confusing to navigate with all the test definitions mixed with hook
> definitions. The TLS tests make this worse with ifdef'ery.
> 
> Since we're planning on having a smaller set of tests to run as smoke
> testing on all architectures, I'm taking the time to split some tests
> into their own file.
> 
> Move the TLS tests into a file of their own.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  tests/qtest/meson.build                  |   8 +-
>  tests/qtest/migration-test.c             | 788 +---------------------
>  tests/qtest/migration/migration-common.h |   6 +
>  tests/qtest/migration/tls-tests.c        | 790 +++++++++++++++++++++++
>  4 files changed, 803 insertions(+), 789 deletions(-)
>  create mode 100644 tests/qtest/migration/tls-tests.c


> diff --git a/tests/qtest/migration/migration-common.h b/tests/qtest/migration/migration-common.h
> index 8d0081c698..c546e92259 100644
> --- a/tests/qtest/migration/tls-tests.c
> +++ b/tests/qtest/migration/tls-tests.c


> +
> +void migration_test_add_tls(MigrationTestEnv *env)
> +{
> +    tmpfs = env->tmpfs;
> +
> +    migration_test_add("/migration/precopy/unix/tls/psk",
> +                       test_precopy_unix_tls_psk);
> +

...snip...

> +}

Looking at this, and considering the later patch which introduces
'make qtest-<subsystem>' support, I wonder if we actually need to
have a single 'migration-test' binary. Why not just add a main()
method to this  test-tests.c, and have a 'migration-test-tls'
binary ?

"make qtest-migration" would provoide a way to run the same level
of functionality seen when everything was in one 'migration-test'
binary.

With regards,
Daniel
Fabiano Rosas Nov. 6, 2024, 1:05 p.m. UTC | #2
Daniel P. Berrangé <berrange@redhat.com> writes:

> On Tue, Nov 05, 2024 at 03:08:27PM -0300, Fabiano Rosas wrote:
>> The migration-test.c file has become unwieldy large. It's quite
>> confusing to navigate with all the test definitions mixed with hook
>> definitions. The TLS tests make this worse with ifdef'ery.
>> 
>> Since we're planning on having a smaller set of tests to run as smoke
>> testing on all architectures, I'm taking the time to split some tests
>> into their own file.
>> 
>> Move the TLS tests into a file of their own.
>> 
>> Signed-off-by: Fabiano Rosas <farosas@suse.de>
>> ---
>>  tests/qtest/meson.build                  |   8 +-
>>  tests/qtest/migration-test.c             | 788 +---------------------
>>  tests/qtest/migration/migration-common.h |   6 +
>>  tests/qtest/migration/tls-tests.c        | 790 +++++++++++++++++++++++
>>  4 files changed, 803 insertions(+), 789 deletions(-)
>>  create mode 100644 tests/qtest/migration/tls-tests.c
>
>
>> diff --git a/tests/qtest/migration/migration-common.h b/tests/qtest/migration/migration-common.h
>> index 8d0081c698..c546e92259 100644
>> --- a/tests/qtest/migration/tls-tests.c
>> +++ b/tests/qtest/migration/tls-tests.c
>
>
>> +
>> +void migration_test_add_tls(MigrationTestEnv *env)
>> +{
>> +    tmpfs = env->tmpfs;
>> +
>> +    migration_test_add("/migration/precopy/unix/tls/psk",
>> +                       test_precopy_unix_tls_psk);
>> +
>
> ...snip...
>
>> +}
>
> Looking at this, and considering the later patch which introduces
> 'make qtest-<subsystem>' support, I wonder if we actually need to
> have a single 'migration-test' binary. Why not just add a main()
> method to this  test-tests.c, and have a 'migration-test-tls'
> binary ?

I did that initially, but then I realised it duplicates the -qmp, -util
and -common helpers into every new test binary. With the current split,
that would be 7x.

Another point is that we can't then implement the smoke tests like in
this series, we'd have to make every migration-foo-test.c choose between
smoke or full tests individually. Which is doable, but it seemed against
your and Alex's suggestions of having 2 separate binaries.

If we're fine with the duplication in the build, I could go back to that
approach. Each main() function would need to look like this:
      
    if (g_test_thorough() || env->has_kvm) {
        /* add all tests */
        migration_test_add();
        migration_test_add();
        ...
    } else {
        /* only the smoke suite */
        migration_test_add_smoke();
    }

>
> "make qtest-migration" would provoide a way to run the same level
> of functionality seen when everything was in one 'migration-test'
> binary.

Yes... but not quite, because running all tests with gdb or valgrind
attached is something that I do frequently, e.g.:

PYTHON=$(which python3.11) \
QTEST_QEMU_BINARY_SRC=\'valgrind -q --leak-check=full --show-leak-kinds=definite,indirect \
--sim-hints=lax-ioctls --suppressions=${BASEDIR}/valgrind-suppressions ./qemu-system-${ARCH}\' \
QTEST_QEMU_BINARY=./qemu-system-${ARCH} ./tests/qtest/migration-test -r /${ARCH}/migration || exit 1

QTEST_QEMU_BINARY=./qemu-system-x86_64 QTEST_QEMU_BINARY_SRC='gdb -q
--ex "set pagination off" --ex "set print thread-events off" --ex
"handle SIGUSR1 noprint" --ex "handle SIGPIPE noprint" --ex "run" --ex
"quit \$_exitcode" --args ./qemu-system-x86_64'
./tests/qtest/migration-test

This could probably be done with meson directly, if that happens to fit
their opinionated view of the world, but I'd rather not delve into that.
On the other hand, maybe it's not a big deal and I can live with only
running a group of tests with the -r or -p flag instead of all of them.

>
> With regards,
> Daniel
Daniel P. Berrangé Nov. 6, 2024, 1:13 p.m. UTC | #3
On Wed, Nov 06, 2024 at 10:05:59AM -0300, Fabiano Rosas wrote:
> Daniel P. Berrangé <berrange@redhat.com> writes:
> 
> > On Tue, Nov 05, 2024 at 03:08:27PM -0300, Fabiano Rosas wrote:
> >> The migration-test.c file has become unwieldy large. It's quite
> >> confusing to navigate with all the test definitions mixed with hook
> >> definitions. The TLS tests make this worse with ifdef'ery.
> >> 
> >> Since we're planning on having a smaller set of tests to run as smoke
> >> testing on all architectures, I'm taking the time to split some tests
> >> into their own file.
> >> 
> >> Move the TLS tests into a file of their own.
> >> 
> >> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> >> ---
> >>  tests/qtest/meson.build                  |   8 +-
> >>  tests/qtest/migration-test.c             | 788 +---------------------
> >>  tests/qtest/migration/migration-common.h |   6 +
> >>  tests/qtest/migration/tls-tests.c        | 790 +++++++++++++++++++++++
> >>  4 files changed, 803 insertions(+), 789 deletions(-)
> >>  create mode 100644 tests/qtest/migration/tls-tests.c
> >
> >
> >> diff --git a/tests/qtest/migration/migration-common.h b/tests/qtest/migration/migration-common.h
> >> index 8d0081c698..c546e92259 100644
> >> --- a/tests/qtest/migration/tls-tests.c
> >> +++ b/tests/qtest/migration/tls-tests.c
> >
> >
> >> +
> >> +void migration_test_add_tls(MigrationTestEnv *env)
> >> +{
> >> +    tmpfs = env->tmpfs;
> >> +
> >> +    migration_test_add("/migration/precopy/unix/tls/psk",
> >> +                       test_precopy_unix_tls_psk);
> >> +
> >
> > ...snip...
> >
> >> +}
> >
> > Looking at this, and considering the later patch which introduces
> > 'make qtest-<subsystem>' support, I wonder if we actually need to
> > have a single 'migration-test' binary. Why not just add a main()
> > method to this  test-tests.c, and have a 'migration-test-tls'
> > binary ?
> 
> I did that initially, but then I realised it duplicates the -qmp, -util
> and -common helpers into every new test binary. With the current split,
> that would be 7x.
> 
> Another point is that we can't then implement the smoke tests like in
> this series, we'd have to make every migration-foo-test.c choose between
> smoke or full tests individually. Which is doable, but it seemed against
> your and Alex's suggestions of having 2 separate binaries.
> 
> If we're fine with the duplication in the build, I could go back to that
> approach. Each main() function would need to look like this:
>       
>     if (g_test_thorough() || env->has_kvm) {
>         /* add all tests */
>         migration_test_add();
>         migration_test_add();
>         ...
>     } else {
>         /* only the smoke suite */
>         migration_test_add_smoke();
>     }
> 
> >
> > "make qtest-migration" would provoide a way to run the same level
> > of functionality seen when everything was in one 'migration-test'
> > binary.
> 
> Yes... but not quite, because running all tests with gdb or valgrind
> attached is something that I do frequently, e.g.:
> 
> PYTHON=$(which python3.11) \
> QTEST_QEMU_BINARY_SRC=\'valgrind -q --leak-check=full --show-leak-kinds=definite,indirect \
> --sim-hints=lax-ioctls --suppressions=${BASEDIR}/valgrind-suppressions ./qemu-system-${ARCH}\' \
> QTEST_QEMU_BINARY=./qemu-system-${ARCH} ./tests/qtest/migration-test -r /${ARCH}/migration || exit 1
> 
> QTEST_QEMU_BINARY=./qemu-system-x86_64 QTEST_QEMU_BINARY_SRC='gdb -q
> --ex "set pagination off" --ex "set print thread-events off" --ex
> "handle SIGUSR1 noprint" --ex "handle SIGPIPE noprint" --ex "run" --ex
> "quit \$_exitcode" --args ./qemu-system-x86_64'
> ./tests/qtest/migration-test
> 
> This could probably be done with meson directly, if that happens to fit
> their opinionated view of the world, but I'd rather not delve into that.
> On the other hand, maybe it's not a big deal and I can live with only
> running a group of tests with the -r or -p flag instead of all of them.

Ok, what you have proposed here is a clear improvement of the status
quo, and doesn't block any of the suggestions I made. So lets just
not complicate this patch series further.

With regards,
Daniel
diff mbox series

Patch

diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index e3dfc74bb8..d83f3db758 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -335,11 +335,13 @@  migration_files = [files(
   'migration/migration-util.c',
 )]
 
+migration_tls_files = []
 if gnutls.found()
-  migration_files += [files('../unit/crypto-tls-psk-helpers.c'), gnutls]
+  migration_tls_files = [files('migration/tls-tests.c'),
+                      files('../unit/crypto-tls-psk-helpers.c'), gnutls]
 
   if tasn1.found()
-    migration_files += [files('../unit/crypto-tls-x509-helpers.c'), tasn1]
+    migration_tls_files += [files('../unit/crypto-tls-x509-helpers.c'), tasn1]
   endif
 endif
 
@@ -349,7 +351,7 @@  qtests = {
   'dbus-vmstate-test': files('migration/migration-qmp.c', 'migration/migration-util.c') + dbus_vmstate1,
   'erst-test': files('erst-test.c'),
   'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'],
-  'migration-test': migration_files,
+  'migration-test': migration_files + migration_tls_files,
   'pxe-test': files('boot-sector.c'),
   'qos-test': [chardev, io, qos_test_ss.apply({}).sources()],
   'tpm-crb-swtpm-test': [io, tpmemu_files],
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 2edede5278..9d5f3be63c 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -25,12 +25,6 @@ 
 #include "migration/bootfile.h"
 #include "migration/migration-common.h"
 #include "migration/migration-util.h"
-#ifdef CONFIG_GNUTLS
-# include "tests/unit/crypto-tls-psk-helpers.h"
-# ifdef CONFIG_TASN1
-#  include "tests/unit/crypto-tls-x509-helpers.h"
-# endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
 
 /*
  * Dirtylimit stop working if dirty page rate error
@@ -48,345 +42,6 @@ 
 
 static char *tmpfs;
 
-#ifdef CONFIG_GNUTLS
-struct TestMigrateTLSPSKData {
-    char *workdir;
-    char *workdiralt;
-    char *pskfile;
-    char *pskfilealt;
-};
-
-static void *
-migrate_tls_psk_start_common(QTestState *from,
-                             QTestState *to,
-                             bool mismatch)
-{
-    struct TestMigrateTLSPSKData *data =
-        g_new0(struct TestMigrateTLSPSKData, 1);
-
-    data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs);
-    data->pskfile = g_strdup_printf("%s/%s", data->workdir,
-                                    QCRYPTO_TLS_CREDS_PSKFILE);
-    g_mkdir_with_parents(data->workdir, 0700);
-    test_tls_psk_init(data->pskfile);
-
-    if (mismatch) {
-        data->workdiralt = g_strdup_printf("%s/tlscredspskalt0", tmpfs);
-        data->pskfilealt = g_strdup_printf("%s/%s", data->workdiralt,
-                                           QCRYPTO_TLS_CREDS_PSKFILE);
-        g_mkdir_with_parents(data->workdiralt, 0700);
-        test_tls_psk_init_alt(data->pskfilealt);
-    }
-
-    qtest_qmp_assert_success(from,
-                             "{ 'execute': 'object-add',"
-                             "  'arguments': { 'qom-type': 'tls-creds-psk',"
-                             "                 'id': 'tlscredspsk0',"
-                             "                 'endpoint': 'client',"
-                             "                 'dir': %s,"
-                             "                 'username': 'qemu'} }",
-                             data->workdir);
-
-    qtest_qmp_assert_success(to,
-                             "{ 'execute': 'object-add',"
-                             "  'arguments': { 'qom-type': 'tls-creds-psk',"
-                             "                 'id': 'tlscredspsk0',"
-                             "                 'endpoint': 'server',"
-                             "                 'dir': %s } }",
-                             mismatch ? data->workdiralt : data->workdir);
-
-    migrate_set_parameter_str(from, "tls-creds", "tlscredspsk0");
-    migrate_set_parameter_str(to, "tls-creds", "tlscredspsk0");
-
-    return data;
-}
-
-static void *
-migrate_tls_psk_match_start(QTestState *from,
-                            QTestState *to)
-{
-    return migrate_tls_psk_start_common(from, to, false);
-}
-
-static void *
-migrate_tls_psk_mismatch_start(QTestState *from,
-                               QTestState *to)
-{
-    return migrate_tls_psk_start_common(from, to, true);
-}
-
-static void
-migrate_tls_psk_end(QTestState *from,
-                    QTestState *to,
-                    void *opaque)
-{
-    struct TestMigrateTLSPSKData *data = opaque;
-
-    test_tls_psk_cleanup(data->pskfile);
-    if (data->pskfilealt) {
-        test_tls_psk_cleanup(data->pskfilealt);
-    }
-    rmdir(data->workdir);
-    if (data->workdiralt) {
-        rmdir(data->workdiralt);
-    }
-
-    g_free(data->workdiralt);
-    g_free(data->pskfilealt);
-    g_free(data->workdir);
-    g_free(data->pskfile);
-    g_free(data);
-}
-
-#ifdef CONFIG_TASN1
-typedef struct {
-    char *workdir;
-    char *keyfile;
-    char *cacert;
-    char *servercert;
-    char *serverkey;
-    char *clientcert;
-    char *clientkey;
-} TestMigrateTLSX509Data;
-
-typedef struct {
-    bool verifyclient;
-    bool clientcert;
-    bool hostileclient;
-    bool authzclient;
-    const char *certhostname;
-    const char *certipaddr;
-} TestMigrateTLSX509;
-
-static void *
-migrate_tls_x509_start_common(QTestState *from,
-                              QTestState *to,
-                              TestMigrateTLSX509 *args)
-{
-    TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1);
-
-    data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs);
-    data->keyfile = g_strdup_printf("%s/key.pem", data->workdir);
-
-    data->cacert = g_strdup_printf("%s/ca-cert.pem", data->workdir);
-    data->serverkey = g_strdup_printf("%s/server-key.pem", data->workdir);
-    data->servercert = g_strdup_printf("%s/server-cert.pem", data->workdir);
-    if (args->clientcert) {
-        data->clientkey = g_strdup_printf("%s/client-key.pem", data->workdir);
-        data->clientcert = g_strdup_printf("%s/client-cert.pem", data->workdir);
-    }
-
-    g_mkdir_with_parents(data->workdir, 0700);
-
-    test_tls_init(data->keyfile);
-#ifndef _WIN32
-    g_assert(link(data->keyfile, data->serverkey) == 0);
-#else
-    g_assert(CreateHardLink(data->serverkey, data->keyfile, NULL) != 0);
-#endif
-    if (args->clientcert) {
-#ifndef _WIN32
-        g_assert(link(data->keyfile, data->clientkey) == 0);
-#else
-        g_assert(CreateHardLink(data->clientkey, data->keyfile, NULL) != 0);
-#endif
-    }
-
-    TLS_ROOT_REQ_SIMPLE(cacertreq, data->cacert);
-    if (args->clientcert) {
-        TLS_CERT_REQ_SIMPLE_CLIENT(servercertreq, cacertreq,
-                                   args->hostileclient ?
-                                   QCRYPTO_TLS_TEST_CLIENT_HOSTILE_NAME :
-                                   QCRYPTO_TLS_TEST_CLIENT_NAME,
-                                   data->clientcert);
-        test_tls_deinit_cert(&servercertreq);
-    }
-
-    TLS_CERT_REQ_SIMPLE_SERVER(clientcertreq, cacertreq,
-                               data->servercert,
-                               args->certhostname,
-                               args->certipaddr);
-    test_tls_deinit_cert(&clientcertreq);
-    test_tls_deinit_cert(&cacertreq);
-
-    qtest_qmp_assert_success(from,
-                             "{ 'execute': 'object-add',"
-                             "  'arguments': { 'qom-type': 'tls-creds-x509',"
-                             "                 'id': 'tlscredsx509client0',"
-                             "                 'endpoint': 'client',"
-                             "                 'dir': %s,"
-                             "                 'sanity-check': true,"
-                             "                 'verify-peer': true} }",
-                             data->workdir);
-    migrate_set_parameter_str(from, "tls-creds", "tlscredsx509client0");
-    if (args->certhostname) {
-        migrate_set_parameter_str(from, "tls-hostname", args->certhostname);
-    }
-
-    qtest_qmp_assert_success(to,
-                             "{ 'execute': 'object-add',"
-                             "  'arguments': { 'qom-type': 'tls-creds-x509',"
-                             "                 'id': 'tlscredsx509server0',"
-                             "                 'endpoint': 'server',"
-                             "                 'dir': %s,"
-                             "                 'sanity-check': true,"
-                             "                 'verify-peer': %i} }",
-                             data->workdir, args->verifyclient);
-    migrate_set_parameter_str(to, "tls-creds", "tlscredsx509server0");
-
-    if (args->authzclient) {
-        qtest_qmp_assert_success(to,
-                                 "{ 'execute': 'object-add',"
-                                 "  'arguments': { 'qom-type': 'authz-simple',"
-                                 "                 'id': 'tlsauthz0',"
-                                 "                 'identity': %s} }",
-                                 "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME);
-        migrate_set_parameter_str(to, "tls-authz", "tlsauthz0");
-    }
-
-    return data;
-}
-
-/*
- * The normal case: match server's cert hostname against
- * whatever host we were telling QEMU to connect to (if any)
- */
-static void *
-migrate_tls_x509_default_host_start(QTestState *from,
-                                    QTestState *to)
-{
-    TestMigrateTLSX509 args = {
-        .verifyclient = true,
-        .clientcert = true,
-        .certipaddr = "127.0.0.1"
-    };
-    return migrate_tls_x509_start_common(from, to, &args);
-}
-
-/*
- * The unusual case: the server's cert is different from
- * the address we're telling QEMU to connect to (if any),
- * so we must give QEMU an explicit hostname to validate
- */
-static void *
-migrate_tls_x509_override_host_start(QTestState *from,
-                                     QTestState *to)
-{
-    TestMigrateTLSX509 args = {
-        .verifyclient = true,
-        .clientcert = true,
-        .certhostname = "qemu.org",
-    };
-    return migrate_tls_x509_start_common(from, to, &args);
-}
-
-/*
- * The unusual case: the server's cert is different from
- * the address we're telling QEMU to connect to, and so we
- * expect the client to reject the server
- */
-static void *
-migrate_tls_x509_mismatch_host_start(QTestState *from,
-                                     QTestState *to)
-{
-    TestMigrateTLSX509 args = {
-        .verifyclient = true,
-        .clientcert = true,
-        .certipaddr = "10.0.0.1",
-    };
-    return migrate_tls_x509_start_common(from, to, &args);
-}
-
-static void *
-migrate_tls_x509_friendly_client_start(QTestState *from,
-                                       QTestState *to)
-{
-    TestMigrateTLSX509 args = {
-        .verifyclient = true,
-        .clientcert = true,
-        .authzclient = true,
-        .certipaddr = "127.0.0.1",
-    };
-    return migrate_tls_x509_start_common(from, to, &args);
-}
-
-static void *
-migrate_tls_x509_hostile_client_start(QTestState *from,
-                                      QTestState *to)
-{
-    TestMigrateTLSX509 args = {
-        .verifyclient = true,
-        .clientcert = true,
-        .hostileclient = true,
-        .authzclient = true,
-        .certipaddr = "127.0.0.1",
-    };
-    return migrate_tls_x509_start_common(from, to, &args);
-}
-
-/*
- * The case with no client certificate presented,
- * and no server verification
- */
-static void *
-migrate_tls_x509_allow_anon_client_start(QTestState *from,
-                                         QTestState *to)
-{
-    TestMigrateTLSX509 args = {
-        .certipaddr = "127.0.0.1",
-    };
-    return migrate_tls_x509_start_common(from, to, &args);
-}
-
-/*
- * The case with no client certificate presented,
- * and server verification rejecting
- */
-static void *
-migrate_tls_x509_reject_anon_client_start(QTestState *from,
-                                          QTestState *to)
-{
-    TestMigrateTLSX509 args = {
-        .verifyclient = true,
-        .certipaddr = "127.0.0.1",
-    };
-    return migrate_tls_x509_start_common(from, to, &args);
-}
-
-static void
-migrate_tls_x509_end(QTestState *from,
-                     QTestState *to,
-                     void *opaque)
-{
-    TestMigrateTLSX509Data *data = opaque;
-
-    test_tls_cleanup(data->keyfile);
-    g_free(data->keyfile);
-
-    unlink(data->cacert);
-    g_free(data->cacert);
-    unlink(data->servercert);
-    g_free(data->servercert);
-    unlink(data->serverkey);
-    g_free(data->serverkey);
-
-    if (data->clientcert) {
-        unlink(data->clientcert);
-        g_free(data->clientcert);
-    }
-    if (data->clientkey) {
-        unlink(data->clientkey);
-        g_free(data->clientkey);
-    }
-
-    rmdir(data->workdir);
-    g_free(data->workdir);
-
-    g_free(data);
-}
-#endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
-
 static void test_postcopy(void)
 {
     MigrateCommon args = { };
@@ -412,29 +67,6 @@  static void test_postcopy_preempt(void)
     test_postcopy_common(&args);
 }
 
-#ifdef CONFIG_GNUTLS
-static void test_postcopy_tls_psk(void)
-{
-    MigrateCommon args = {
-        .start_hook = migrate_tls_psk_match_start,
-        .end_hook = migrate_tls_psk_end,
-    };
-
-    test_postcopy_common(&args);
-}
-
-static void test_postcopy_preempt_tls_psk(void)
-{
-    MigrateCommon args = {
-        .postcopy_preempt = true,
-        .start_hook = migrate_tls_psk_match_start,
-        .end_hook = migrate_tls_psk_end,
-    };
-
-    test_postcopy_common(&args);
-}
-#endif
-
 static void test_postcopy_recovery(void)
 {
     MigrateCommon args = { };
@@ -460,18 +92,6 @@  static void test_postcopy_recovery_fail_reconnect(void)
     test_postcopy_recovery_common(&args);
 }
 
-#ifdef CONFIG_GNUTLS
-static void test_postcopy_recovery_tls_psk(void)
-{
-    MigrateCommon args = {
-        .start_hook = migrate_tls_psk_match_start,
-        .end_hook = migrate_tls_psk_end,
-    };
-
-    test_postcopy_recovery_common(&args);
-}
-#endif
-
 static void test_postcopy_preempt_recovery(void)
 {
     MigrateCommon args = {
@@ -481,21 +101,6 @@  static void test_postcopy_preempt_recovery(void)
     test_postcopy_recovery_common(&args);
 }
 
-#ifdef CONFIG_GNUTLS
-/* This contains preempt+recovery+tls test altogether */
-static void test_postcopy_preempt_all(void)
-{
-    MigrateCommon args = {
-        .postcopy_preempt = true,
-        .start_hook = migrate_tls_psk_match_start,
-        .end_hook = migrate_tls_psk_end,
-    };
-
-    test_postcopy_recovery_common(&args);
-}
-
-#endif
-
 static void test_baddest(void)
 {
     MigrateStart args = {
@@ -630,53 +235,6 @@  static void test_precopy_unix_dirty_ring(void)
     test_precopy_common(&args);
 }
 
-#ifdef CONFIG_GNUTLS
-static void test_precopy_unix_tls_psk(void)
-{
-    g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
-    MigrateCommon args = {
-        .connect_uri = uri,
-        .listen_uri = uri,
-        .start_hook = migrate_tls_psk_match_start,
-        .end_hook = migrate_tls_psk_end,
-    };
-
-    test_precopy_common(&args);
-}
-
-#ifdef CONFIG_TASN1
-static void test_precopy_unix_tls_x509_default_host(void)
-{
-    g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
-    MigrateCommon args = {
-        .start = {
-            .hide_stderr = true,
-        },
-        .connect_uri = uri,
-        .listen_uri = uri,
-        .start_hook = migrate_tls_x509_default_host_start,
-        .end_hook = migrate_tls_x509_end,
-        .result = MIG_TEST_FAIL_DEST_QUIT_ERR,
-    };
-
-    test_precopy_common(&args);
-}
-
-static void test_precopy_unix_tls_x509_override_host(void)
-{
-    g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
-    MigrateCommon args = {
-        .connect_uri = uri,
-        .listen_uri = uri,
-        .start_hook = migrate_tls_x509_override_host_start,
-        .end_hook = migrate_tls_x509_end,
-    };
-
-    test_precopy_common(&args);
-}
-#endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
-
 #if 0
 /* Currently upset on aarch64 TCG */
 static void test_ignore_shared(void)
@@ -1079,125 +637,6 @@  static void test_precopy_tcp_switchover_ack(void)
     test_precopy_common(&args);
 }
 
-#ifdef CONFIG_GNUTLS
-static void test_precopy_tcp_tls_psk_match(void)
-{
-    MigrateCommon args = {
-        .listen_uri = "tcp:127.0.0.1:0",
-        .start_hook = migrate_tls_psk_match_start,
-        .end_hook = migrate_tls_psk_end,
-    };
-
-    test_precopy_common(&args);
-}
-
-static void test_precopy_tcp_tls_psk_mismatch(void)
-{
-    MigrateCommon args = {
-        .start = {
-            .hide_stderr = true,
-        },
-        .listen_uri = "tcp:127.0.0.1:0",
-        .start_hook = migrate_tls_psk_mismatch_start,
-        .end_hook = migrate_tls_psk_end,
-        .result = MIG_TEST_FAIL,
-    };
-
-    test_precopy_common(&args);
-}
-
-#ifdef CONFIG_TASN1
-static void test_precopy_tcp_tls_x509_default_host(void)
-{
-    MigrateCommon args = {
-        .listen_uri = "tcp:127.0.0.1:0",
-        .start_hook = migrate_tls_x509_default_host_start,
-        .end_hook = migrate_tls_x509_end,
-    };
-
-    test_precopy_common(&args);
-}
-
-static void test_precopy_tcp_tls_x509_override_host(void)
-{
-    MigrateCommon args = {
-        .listen_uri = "tcp:127.0.0.1:0",
-        .start_hook = migrate_tls_x509_override_host_start,
-        .end_hook = migrate_tls_x509_end,
-    };
-
-    test_precopy_common(&args);
-}
-
-static void test_precopy_tcp_tls_x509_mismatch_host(void)
-{
-    MigrateCommon args = {
-        .start = {
-            .hide_stderr = true,
-        },
-        .listen_uri = "tcp:127.0.0.1:0",
-        .start_hook = migrate_tls_x509_mismatch_host_start,
-        .end_hook = migrate_tls_x509_end,
-        .result = MIG_TEST_FAIL_DEST_QUIT_ERR,
-    };
-
-    test_precopy_common(&args);
-}
-
-static void test_precopy_tcp_tls_x509_friendly_client(void)
-{
-    MigrateCommon args = {
-        .listen_uri = "tcp:127.0.0.1:0",
-        .start_hook = migrate_tls_x509_friendly_client_start,
-        .end_hook = migrate_tls_x509_end,
-    };
-
-    test_precopy_common(&args);
-}
-
-static void test_precopy_tcp_tls_x509_hostile_client(void)
-{
-    MigrateCommon args = {
-        .start = {
-            .hide_stderr = true,
-        },
-        .listen_uri = "tcp:127.0.0.1:0",
-        .start_hook = migrate_tls_x509_hostile_client_start,
-        .end_hook = migrate_tls_x509_end,
-        .result = MIG_TEST_FAIL,
-    };
-
-    test_precopy_common(&args);
-}
-
-static void test_precopy_tcp_tls_x509_allow_anon_client(void)
-{
-    MigrateCommon args = {
-        .listen_uri = "tcp:127.0.0.1:0",
-        .start_hook = migrate_tls_x509_allow_anon_client_start,
-        .end_hook = migrate_tls_x509_end,
-    };
-
-    test_precopy_common(&args);
-}
-
-static void test_precopy_tcp_tls_x509_reject_anon_client(void)
-{
-    MigrateCommon args = {
-        .start = {
-            .hide_stderr = true,
-        },
-        .listen_uri = "tcp:127.0.0.1:0",
-        .start_hook = migrate_tls_x509_reject_anon_client_start,
-        .end_hook = migrate_tls_x509_end,
-        .result = MIG_TEST_FAIL,
-    };
-
-    test_precopy_common(&args);
-}
-#endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
-
 #ifndef _WIN32
 static void *migrate_fd_start(QTestState *from,
                               QTestState *to)
@@ -1738,163 +1177,6 @@  static void test_multifd_tcp_uadk(void)
 }
 #endif
 
-#ifdef CONFIG_GNUTLS
-static void *
-migrate_multifd_tcp_tls_psk_match_start(QTestState *from,
-                                        QTestState *to)
-{
-    migrate_precopy_tcp_multifd_start_common(from, to, "none");
-    return migrate_tls_psk_match_start(from, to);
-}
-
-static void *
-migrate_multifd_tcp_tls_psk_mismatch_start(QTestState *from,
-                                           QTestState *to)
-{
-    migrate_precopy_tcp_multifd_start_common(from, to, "none");
-    return migrate_tls_psk_mismatch_start(from, to);
-}
-
-#ifdef CONFIG_TASN1
-static void *
-migrate_multifd_tls_x509_default_host_start(QTestState *from,
-                                            QTestState *to)
-{
-    migrate_precopy_tcp_multifd_start_common(from, to, "none");
-    return migrate_tls_x509_default_host_start(from, to);
-}
-
-static void *
-migrate_multifd_tls_x509_override_host_start(QTestState *from,
-                                             QTestState *to)
-{
-    migrate_precopy_tcp_multifd_start_common(from, to, "none");
-    return migrate_tls_x509_override_host_start(from, to);
-}
-
-static void *
-migrate_multifd_tls_x509_mismatch_host_start(QTestState *from,
-                                             QTestState *to)
-{
-    migrate_precopy_tcp_multifd_start_common(from, to, "none");
-    return migrate_tls_x509_mismatch_host_start(from, to);
-}
-
-static void *
-migrate_multifd_tls_x509_allow_anon_client_start(QTestState *from,
-                                                 QTestState *to)
-{
-    migrate_precopy_tcp_multifd_start_common(from, to, "none");
-    return migrate_tls_x509_allow_anon_client_start(from, to);
-}
-
-static void *
-migrate_multifd_tls_x509_reject_anon_client_start(QTestState *from,
-                                                  QTestState *to)
-{
-    migrate_precopy_tcp_multifd_start_common(from, to, "none");
-    return migrate_tls_x509_reject_anon_client_start(from, to);
-}
-#endif /* CONFIG_TASN1 */
-
-static void test_multifd_tcp_tls_psk_match(void)
-{
-    MigrateCommon args = {
-        .listen_uri = "defer",
-        .start_hook = migrate_multifd_tcp_tls_psk_match_start,
-        .end_hook = migrate_tls_psk_end,
-    };
-    test_precopy_common(&args);
-}
-
-static void test_multifd_tcp_tls_psk_mismatch(void)
-{
-    MigrateCommon args = {
-        .start = {
-            .hide_stderr = true,
-        },
-        .listen_uri = "defer",
-        .start_hook = migrate_multifd_tcp_tls_psk_mismatch_start,
-        .end_hook = migrate_tls_psk_end,
-        .result = MIG_TEST_FAIL,
-    };
-    test_precopy_common(&args);
-}
-
-#ifdef CONFIG_TASN1
-static void test_multifd_tcp_tls_x509_default_host(void)
-{
-    MigrateCommon args = {
-        .listen_uri = "defer",
-        .start_hook = migrate_multifd_tls_x509_default_host_start,
-        .end_hook = migrate_tls_x509_end,
-    };
-    test_precopy_common(&args);
-}
-
-static void test_multifd_tcp_tls_x509_override_host(void)
-{
-    MigrateCommon args = {
-        .listen_uri = "defer",
-        .start_hook = migrate_multifd_tls_x509_override_host_start,
-        .end_hook = migrate_tls_x509_end,
-    };
-    test_precopy_common(&args);
-}
-
-static void test_multifd_tcp_tls_x509_mismatch_host(void)
-{
-    /*
-     * This has different behaviour to the non-multifd case.
-     *
-     * In non-multifd case when client aborts due to mismatched
-     * cert host, the server has already started trying to load
-     * migration state, and so it exits with I/O failure.
-     *
-     * In multifd case when client aborts due to mismatched
-     * cert host, the server is still waiting for the other
-     * multifd connections to arrive so hasn't started trying
-     * to load migration state, and thus just aborts the migration
-     * without exiting.
-     */
-    MigrateCommon args = {
-        .start = {
-            .hide_stderr = true,
-        },
-        .listen_uri = "defer",
-        .start_hook = migrate_multifd_tls_x509_mismatch_host_start,
-        .end_hook = migrate_tls_x509_end,
-        .result = MIG_TEST_FAIL,
-    };
-    test_precopy_common(&args);
-}
-
-static void test_multifd_tcp_tls_x509_allow_anon_client(void)
-{
-    MigrateCommon args = {
-        .listen_uri = "defer",
-        .start_hook = migrate_multifd_tls_x509_allow_anon_client_start,
-        .end_hook = migrate_tls_x509_end,
-    };
-    test_precopy_common(&args);
-}
-
-static void test_multifd_tcp_tls_x509_reject_anon_client(void)
-{
-    MigrateCommon args = {
-        .start = {
-            .hide_stderr = true,
-        },
-        .listen_uri = "defer",
-        .start_hook = migrate_multifd_tls_x509_reject_anon_client_start,
-        .end_hook = migrate_tls_x509_end,
-        .result = MIG_TEST_FAIL,
-    };
-    test_precopy_common(&args);
-}
-#endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
-
 /*
  * This test does:
  *  source               target
@@ -2402,6 +1684,8 @@  int main(int argc, char **argv)
 
     tmpfs = env->tmpfs;
 
+    migration_test_add_tls(env);
+
     migration_test_add("/migration/bad_dest", test_baddest);
 #ifndef _WIN32
     migration_test_add("/migration/analyze-script", test_analyze_script);
@@ -2477,61 +1761,11 @@  int main(int argc, char **argv)
                        test_multifd_file_mapped_ram_fdset_dio);
 #endif
 
-#ifdef CONFIG_GNUTLS
-    migration_test_add("/migration/precopy/unix/tls/psk",
-                       test_precopy_unix_tls_psk);
-
-    if (env->has_uffd) {
-        /*
-         * NOTE: psk test is enough for postcopy, as other types of TLS
-         * channels are tested under precopy.  Here what we want to test is the
-         * general postcopy path that has TLS channel enabled.
-         */
-        migration_test_add("/migration/postcopy/tls/psk",
-                           test_postcopy_tls_psk);
-        migration_test_add("/migration/postcopy/recovery/tls/psk",
-                           test_postcopy_recovery_tls_psk);
-        migration_test_add("/migration/postcopy/preempt/tls/psk",
-                           test_postcopy_preempt_tls_psk);
-        migration_test_add("/migration/postcopy/preempt/recovery/tls/psk",
-                           test_postcopy_preempt_all);
-    }
-#ifdef CONFIG_TASN1
-    migration_test_add("/migration/precopy/unix/tls/x509/default-host",
-                       test_precopy_unix_tls_x509_default_host);
-    migration_test_add("/migration/precopy/unix/tls/x509/override-host",
-                       test_precopy_unix_tls_x509_override_host);
-#endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
-
     migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain);
 
     migration_test_add("/migration/precopy/tcp/plain/switchover-ack",
                        test_precopy_tcp_switchover_ack);
 
-#ifdef CONFIG_GNUTLS
-    migration_test_add("/migration/precopy/tcp/tls/psk/match",
-                       test_precopy_tcp_tls_psk_match);
-    migration_test_add("/migration/precopy/tcp/tls/psk/mismatch",
-                       test_precopy_tcp_tls_psk_mismatch);
-#ifdef CONFIG_TASN1
-    migration_test_add("/migration/precopy/tcp/tls/x509/default-host",
-                       test_precopy_tcp_tls_x509_default_host);
-    migration_test_add("/migration/precopy/tcp/tls/x509/override-host",
-                       test_precopy_tcp_tls_x509_override_host);
-    migration_test_add("/migration/precopy/tcp/tls/x509/mismatch-host",
-                       test_precopy_tcp_tls_x509_mismatch_host);
-    migration_test_add("/migration/precopy/tcp/tls/x509/friendly-client",
-                       test_precopy_tcp_tls_x509_friendly_client);
-    migration_test_add("/migration/precopy/tcp/tls/x509/hostile-client",
-                       test_precopy_tcp_tls_x509_hostile_client);
-    migration_test_add("/migration/precopy/tcp/tls/x509/allow-anon-client",
-                       test_precopy_tcp_tls_x509_allow_anon_client);
-    migration_test_add("/migration/precopy/tcp/tls/x509/reject-anon-client",
-                       test_precopy_tcp_tls_x509_reject_anon_client);
-#endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
-
     /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */
 #ifndef _WIN32
     migration_test_add("/migration/precopy/fd/tcp",
@@ -2590,24 +1824,6 @@  int main(int argc, char **argv)
     migration_test_add("/migration/multifd/tcp/plain/uadk",
                        test_multifd_tcp_uadk);
 #endif
-#ifdef CONFIG_GNUTLS
-    migration_test_add("/migration/multifd/tcp/tls/psk/match",
-                       test_multifd_tcp_tls_psk_match);
-    migration_test_add("/migration/multifd/tcp/tls/psk/mismatch",
-                       test_multifd_tcp_tls_psk_mismatch);
-#ifdef CONFIG_TASN1
-    migration_test_add("/migration/multifd/tcp/tls/x509/default-host",
-                       test_multifd_tcp_tls_x509_default_host);
-    migration_test_add("/migration/multifd/tcp/tls/x509/override-host",
-                       test_multifd_tcp_tls_x509_override_host);
-    migration_test_add("/migration/multifd/tcp/tls/x509/mismatch-host",
-                       test_multifd_tcp_tls_x509_mismatch_host);
-    migration_test_add("/migration/multifd/tcp/tls/x509/allow-anon-client",
-                       test_multifd_tcp_tls_x509_allow_anon_client);
-    migration_test_add("/migration/multifd/tcp/tls/x509/reject-anon-client",
-                       test_multifd_tcp_tls_x509_reject_anon_client);
-#endif /* CONFIG_TASN1 */
-#endif /* CONFIG_GNUTLS */
 
     if (g_str_equal(env->arch, "x86_64") && env->has_kvm && env->has_dirty_ring) {
         migration_test_add("/migration/dirty_ring",
diff --git a/tests/qtest/migration/migration-common.h b/tests/qtest/migration/migration-common.h
index 8d0081c698..c546e92259 100644
--- a/tests/qtest/migration/migration-common.h
+++ b/tests/qtest/migration/migration-common.h
@@ -211,4 +211,10 @@  QTestMigrationState *get_src(void);
 MigrationTestEnv *migration_get_env(void);
 int migration_env_clean(MigrationTestEnv *env);
 
+#ifdef CONFIG_GNUTLS
+void migration_test_add_tls(MigrationTestEnv *env);
+#else
+static inline void migration_test_add_tls(MigrationTestEnv *env) {};
+#endif
+
 #endif /* MIGRATION_COMMON_H */
diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c
new file mode 100644
index 0000000000..07d8d376f2
--- /dev/null
+++ b/tests/qtest/migration/tls-tests.c
@@ -0,0 +1,790 @@ 
+/*
+ * QTest testcases for TLS migration
+ *
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+ *   based on the vhost-user-test.c that is:
+ *      Copyright (c) 2014 Virtual Open Systems Sarl.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/tlscredspsk.h"
+#include "libqtest.h"
+#include "migration/migration-common.h"
+#include "migration/migration-util.h"
+
+#include "tests/unit/crypto-tls-psk-helpers.h"
+#ifdef CONFIG_TASN1
+# include "tests/unit/crypto-tls-x509-helpers.h"
+#endif /* CONFIG_TASN1 */
+
+
+struct TestMigrateTLSPSKData {
+    char *workdir;
+    char *workdiralt;
+    char *pskfile;
+    char *pskfilealt;
+};
+
+static char *tmpfs;
+
+static void *
+migrate_tls_psk_start_common(QTestState *from,
+                             QTestState *to,
+                             bool mismatch)
+{
+    struct TestMigrateTLSPSKData *data =
+        g_new0(struct TestMigrateTLSPSKData, 1);
+
+    data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs);
+    data->pskfile = g_strdup_printf("%s/%s", data->workdir,
+                                    QCRYPTO_TLS_CREDS_PSKFILE);
+    g_mkdir_with_parents(data->workdir, 0700);
+    test_tls_psk_init(data->pskfile);
+
+    if (mismatch) {
+        data->workdiralt = g_strdup_printf("%s/tlscredspskalt0", tmpfs);
+        data->pskfilealt = g_strdup_printf("%s/%s", data->workdiralt,
+                                           QCRYPTO_TLS_CREDS_PSKFILE);
+        g_mkdir_with_parents(data->workdiralt, 0700);
+        test_tls_psk_init_alt(data->pskfilealt);
+    }
+
+    qtest_qmp_assert_success(from,
+                             "{ 'execute': 'object-add',"
+                             "  'arguments': { 'qom-type': 'tls-creds-psk',"
+                             "                 'id': 'tlscredspsk0',"
+                             "                 'endpoint': 'client',"
+                             "                 'dir': %s,"
+                             "                 'username': 'qemu'} }",
+                             data->workdir);
+
+    qtest_qmp_assert_success(to,
+                             "{ 'execute': 'object-add',"
+                             "  'arguments': { 'qom-type': 'tls-creds-psk',"
+                             "                 'id': 'tlscredspsk0',"
+                             "                 'endpoint': 'server',"
+                             "                 'dir': %s } }",
+                             mismatch ? data->workdiralt : data->workdir);
+
+    migrate_set_parameter_str(from, "tls-creds", "tlscredspsk0");
+    migrate_set_parameter_str(to, "tls-creds", "tlscredspsk0");
+
+    return data;
+}
+
+static void *
+migrate_tls_psk_match_start(QTestState *from,
+                            QTestState *to)
+{
+    return migrate_tls_psk_start_common(from, to, false);
+}
+
+static void *
+migrate_tls_psk_mismatch_start(QTestState *from,
+                               QTestState *to)
+{
+    return migrate_tls_psk_start_common(from, to, true);
+}
+
+static void
+migrate_tls_psk_end(QTestState *from,
+                    QTestState *to,
+                    void *opaque)
+{
+    struct TestMigrateTLSPSKData *data = opaque;
+
+    test_tls_psk_cleanup(data->pskfile);
+    if (data->pskfilealt) {
+        test_tls_psk_cleanup(data->pskfilealt);
+    }
+    rmdir(data->workdir);
+    if (data->workdiralt) {
+        rmdir(data->workdiralt);
+    }
+
+    g_free(data->workdiralt);
+    g_free(data->pskfilealt);
+    g_free(data->workdir);
+    g_free(data->pskfile);
+    g_free(data);
+}
+
+#ifdef CONFIG_TASN1
+typedef struct {
+    char *workdir;
+    char *keyfile;
+    char *cacert;
+    char *servercert;
+    char *serverkey;
+    char *clientcert;
+    char *clientkey;
+} TestMigrateTLSX509Data;
+
+typedef struct {
+    bool verifyclient;
+    bool clientcert;
+    bool hostileclient;
+    bool authzclient;
+    const char *certhostname;
+    const char *certipaddr;
+} TestMigrateTLSX509;
+
+static void *
+migrate_tls_x509_start_common(QTestState *from,
+                              QTestState *to,
+                              TestMigrateTLSX509 *args)
+{
+    TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1);
+
+    data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs);
+    data->keyfile = g_strdup_printf("%s/key.pem", data->workdir);
+
+    data->cacert = g_strdup_printf("%s/ca-cert.pem", data->workdir);
+    data->serverkey = g_strdup_printf("%s/server-key.pem", data->workdir);
+    data->servercert = g_strdup_printf("%s/server-cert.pem", data->workdir);
+    if (args->clientcert) {
+        data->clientkey = g_strdup_printf("%s/client-key.pem", data->workdir);
+        data->clientcert = g_strdup_printf("%s/client-cert.pem", data->workdir);
+    }
+
+    g_mkdir_with_parents(data->workdir, 0700);
+
+    test_tls_init(data->keyfile);
+#ifndef _WIN32
+    g_assert(link(data->keyfile, data->serverkey) == 0);
+#else
+    g_assert(CreateHardLink(data->serverkey, data->keyfile, NULL) != 0);
+#endif
+    if (args->clientcert) {
+#ifndef _WIN32
+        g_assert(link(data->keyfile, data->clientkey) == 0);
+#else
+        g_assert(CreateHardLink(data->clientkey, data->keyfile, NULL) != 0);
+#endif
+    }
+
+    TLS_ROOT_REQ_SIMPLE(cacertreq, data->cacert);
+    if (args->clientcert) {
+        TLS_CERT_REQ_SIMPLE_CLIENT(servercertreq, cacertreq,
+                                   args->hostileclient ?
+                                   QCRYPTO_TLS_TEST_CLIENT_HOSTILE_NAME :
+                                   QCRYPTO_TLS_TEST_CLIENT_NAME,
+                                   data->clientcert);
+        test_tls_deinit_cert(&servercertreq);
+    }
+
+    TLS_CERT_REQ_SIMPLE_SERVER(clientcertreq, cacertreq,
+                               data->servercert,
+                               args->certhostname,
+                               args->certipaddr);
+    test_tls_deinit_cert(&clientcertreq);
+    test_tls_deinit_cert(&cacertreq);
+
+    qtest_qmp_assert_success(from,
+                             "{ 'execute': 'object-add',"
+                             "  'arguments': { 'qom-type': 'tls-creds-x509',"
+                             "                 'id': 'tlscredsx509client0',"
+                             "                 'endpoint': 'client',"
+                             "                 'dir': %s,"
+                             "                 'sanity-check': true,"
+                             "                 'verify-peer': true} }",
+                             data->workdir);
+    migrate_set_parameter_str(from, "tls-creds", "tlscredsx509client0");
+    if (args->certhostname) {
+        migrate_set_parameter_str(from, "tls-hostname", args->certhostname);
+    }
+
+    qtest_qmp_assert_success(to,
+                             "{ 'execute': 'object-add',"
+                             "  'arguments': { 'qom-type': 'tls-creds-x509',"
+                             "                 'id': 'tlscredsx509server0',"
+                             "                 'endpoint': 'server',"
+                             "                 'dir': %s,"
+                             "                 'sanity-check': true,"
+                             "                 'verify-peer': %i} }",
+                             data->workdir, args->verifyclient);
+    migrate_set_parameter_str(to, "tls-creds", "tlscredsx509server0");
+
+    if (args->authzclient) {
+        qtest_qmp_assert_success(to,
+                                 "{ 'execute': 'object-add',"
+                                 "  'arguments': { 'qom-type': 'authz-simple',"
+                                 "                 'id': 'tlsauthz0',"
+                                 "                 'identity': %s} }",
+                                 "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME);
+        migrate_set_parameter_str(to, "tls-authz", "tlsauthz0");
+    }
+
+    return data;
+}
+
+/*
+ * The normal case: match server's cert hostname against
+ * whatever host we were telling QEMU to connect to (if any)
+ */
+static void *
+migrate_tls_x509_default_host_start(QTestState *from,
+                                    QTestState *to)
+{
+    TestMigrateTLSX509 args = {
+        .verifyclient = true,
+        .clientcert = true,
+        .certipaddr = "127.0.0.1"
+    };
+    return migrate_tls_x509_start_common(from, to, &args);
+}
+
+/*
+ * The unusual case: the server's cert is different from
+ * the address we're telling QEMU to connect to (if any),
+ * so we must give QEMU an explicit hostname to validate
+ */
+static void *
+migrate_tls_x509_override_host_start(QTestState *from,
+                                     QTestState *to)
+{
+    TestMigrateTLSX509 args = {
+        .verifyclient = true,
+        .clientcert = true,
+        .certhostname = "qemu.org",
+    };
+    return migrate_tls_x509_start_common(from, to, &args);
+}
+
+/*
+ * The unusual case: the server's cert is different from
+ * the address we're telling QEMU to connect to, and so we
+ * expect the client to reject the server
+ */
+static void *
+migrate_tls_x509_mismatch_host_start(QTestState *from,
+                                     QTestState *to)
+{
+    TestMigrateTLSX509 args = {
+        .verifyclient = true,
+        .clientcert = true,
+        .certipaddr = "10.0.0.1",
+    };
+    return migrate_tls_x509_start_common(from, to, &args);
+}
+
+static void *
+migrate_tls_x509_friendly_client_start(QTestState *from,
+                                       QTestState *to)
+{
+    TestMigrateTLSX509 args = {
+        .verifyclient = true,
+        .clientcert = true,
+        .authzclient = true,
+        .certipaddr = "127.0.0.1",
+    };
+    return migrate_tls_x509_start_common(from, to, &args);
+}
+
+static void *
+migrate_tls_x509_hostile_client_start(QTestState *from,
+                                      QTestState *to)
+{
+    TestMigrateTLSX509 args = {
+        .verifyclient = true,
+        .clientcert = true,
+        .hostileclient = true,
+        .authzclient = true,
+        .certipaddr = "127.0.0.1",
+    };
+    return migrate_tls_x509_start_common(from, to, &args);
+}
+
+/*
+ * The case with no client certificate presented,
+ * and no server verification
+ */
+static void *
+migrate_tls_x509_allow_anon_client_start(QTestState *from,
+                                         QTestState *to)
+{
+    TestMigrateTLSX509 args = {
+        .certipaddr = "127.0.0.1",
+    };
+    return migrate_tls_x509_start_common(from, to, &args);
+}
+
+/*
+ * The case with no client certificate presented,
+ * and server verification rejecting
+ */
+static void *
+migrate_tls_x509_reject_anon_client_start(QTestState *from,
+                                          QTestState *to)
+{
+    TestMigrateTLSX509 args = {
+        .verifyclient = true,
+        .certipaddr = "127.0.0.1",
+    };
+    return migrate_tls_x509_start_common(from, to, &args);
+}
+
+static void
+migrate_tls_x509_end(QTestState *from,
+                     QTestState *to,
+                     void *opaque)
+{
+    TestMigrateTLSX509Data *data = opaque;
+
+    test_tls_cleanup(data->keyfile);
+    g_free(data->keyfile);
+
+    unlink(data->cacert);
+    g_free(data->cacert);
+    unlink(data->servercert);
+    g_free(data->servercert);
+    unlink(data->serverkey);
+    g_free(data->serverkey);
+
+    if (data->clientcert) {
+        unlink(data->clientcert);
+        g_free(data->clientcert);
+    }
+    if (data->clientkey) {
+        unlink(data->clientkey);
+        g_free(data->clientkey);
+    }
+
+    rmdir(data->workdir);
+    g_free(data->workdir);
+
+    g_free(data);
+}
+#endif /* CONFIG_TASN1 */
+
+static void test_postcopy_tls_psk(void)
+{
+    MigrateCommon args = {
+        .start_hook = migrate_tls_psk_match_start,
+        .end_hook = migrate_tls_psk_end,
+    };
+
+    test_postcopy_common(&args);
+}
+
+static void test_postcopy_preempt_tls_psk(void)
+{
+    MigrateCommon args = {
+        .postcopy_preempt = true,
+        .start_hook = migrate_tls_psk_match_start,
+        .end_hook = migrate_tls_psk_end,
+    };
+
+    test_postcopy_common(&args);
+}
+
+static void test_postcopy_recovery_tls_psk(void)
+{
+    MigrateCommon args = {
+        .start_hook = migrate_tls_psk_match_start,
+        .end_hook = migrate_tls_psk_end,
+    };
+
+    test_postcopy_recovery_common(&args);
+}
+
+/* This contains preempt+recovery+tls test altogether */
+static void test_postcopy_preempt_all(void)
+{
+    MigrateCommon args = {
+        .postcopy_preempt = true,
+        .start_hook = migrate_tls_psk_match_start,
+        .end_hook = migrate_tls_psk_end,
+    };
+
+    test_postcopy_recovery_common(&args);
+}
+
+static void test_precopy_unix_tls_psk(void)
+{
+    g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+    MigrateCommon args = {
+        .connect_uri = uri,
+        .listen_uri = uri,
+        .start_hook = migrate_tls_psk_match_start,
+        .end_hook = migrate_tls_psk_end,
+    };
+
+    test_precopy_common(&args);
+}
+
+#ifdef CONFIG_TASN1
+static void test_precopy_unix_tls_x509_default_host(void)
+{
+    g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+    MigrateCommon args = {
+        .start = {
+            .hide_stderr = true,
+        },
+        .connect_uri = uri,
+        .listen_uri = uri,
+        .start_hook = migrate_tls_x509_default_host_start,
+        .end_hook = migrate_tls_x509_end,
+        .result = MIG_TEST_FAIL_DEST_QUIT_ERR,
+    };
+
+    test_precopy_common(&args);
+}
+
+static void test_precopy_unix_tls_x509_override_host(void)
+{
+    g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+    MigrateCommon args = {
+        .connect_uri = uri,
+        .listen_uri = uri,
+        .start_hook = migrate_tls_x509_override_host_start,
+        .end_hook = migrate_tls_x509_end,
+    };
+
+    test_precopy_common(&args);
+}
+#endif /* CONFIG_TASN1 */
+
+static void test_precopy_tcp_tls_psk_match(void)
+{
+    MigrateCommon args = {
+        .listen_uri = "tcp:127.0.0.1:0",
+        .start_hook = migrate_tls_psk_match_start,
+        .end_hook = migrate_tls_psk_end,
+    };
+
+    test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_tls_psk_mismatch(void)
+{
+    MigrateCommon args = {
+        .start = {
+            .hide_stderr = true,
+        },
+        .listen_uri = "tcp:127.0.0.1:0",
+        .start_hook = migrate_tls_psk_mismatch_start,
+        .end_hook = migrate_tls_psk_end,
+        .result = MIG_TEST_FAIL,
+    };
+
+    test_precopy_common(&args);
+}
+
+#ifdef CONFIG_TASN1
+static void test_precopy_tcp_tls_x509_default_host(void)
+{
+    MigrateCommon args = {
+        .listen_uri = "tcp:127.0.0.1:0",
+        .start_hook = migrate_tls_x509_default_host_start,
+        .end_hook = migrate_tls_x509_end,
+    };
+
+    test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_tls_x509_override_host(void)
+{
+    MigrateCommon args = {
+        .listen_uri = "tcp:127.0.0.1:0",
+        .start_hook = migrate_tls_x509_override_host_start,
+        .end_hook = migrate_tls_x509_end,
+    };
+
+    test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_tls_x509_mismatch_host(void)
+{
+    MigrateCommon args = {
+        .start = {
+            .hide_stderr = true,
+        },
+        .listen_uri = "tcp:127.0.0.1:0",
+        .start_hook = migrate_tls_x509_mismatch_host_start,
+        .end_hook = migrate_tls_x509_end,
+        .result = MIG_TEST_FAIL_DEST_QUIT_ERR,
+    };
+
+    test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_tls_x509_friendly_client(void)
+{
+    MigrateCommon args = {
+        .listen_uri = "tcp:127.0.0.1:0",
+        .start_hook = migrate_tls_x509_friendly_client_start,
+        .end_hook = migrate_tls_x509_end,
+    };
+
+    test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_tls_x509_hostile_client(void)
+{
+    MigrateCommon args = {
+        .start = {
+            .hide_stderr = true,
+        },
+        .listen_uri = "tcp:127.0.0.1:0",
+        .start_hook = migrate_tls_x509_hostile_client_start,
+        .end_hook = migrate_tls_x509_end,
+        .result = MIG_TEST_FAIL,
+    };
+
+    test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_tls_x509_allow_anon_client(void)
+{
+    MigrateCommon args = {
+        .listen_uri = "tcp:127.0.0.1:0",
+        .start_hook = migrate_tls_x509_allow_anon_client_start,
+        .end_hook = migrate_tls_x509_end,
+    };
+
+    test_precopy_common(&args);
+}
+
+static void test_precopy_tcp_tls_x509_reject_anon_client(void)
+{
+    MigrateCommon args = {
+        .start = {
+            .hide_stderr = true,
+        },
+        .listen_uri = "tcp:127.0.0.1:0",
+        .start_hook = migrate_tls_x509_reject_anon_client_start,
+        .end_hook = migrate_tls_x509_end,
+        .result = MIG_TEST_FAIL,
+    };
+
+    test_precopy_common(&args);
+}
+#endif /* CONFIG_TASN1 */
+
+static void *
+migrate_multifd_tcp_tls_psk_match_start(QTestState *from,
+                                        QTestState *to)
+{
+    migrate_precopy_tcp_multifd_start_common(from, to, "none");
+    return migrate_tls_psk_match_start(from, to);
+}
+
+static void *
+migrate_multifd_tcp_tls_psk_mismatch_start(QTestState *from,
+                                           QTestState *to)
+{
+    migrate_precopy_tcp_multifd_start_common(from, to, "none");
+    return migrate_tls_psk_mismatch_start(from, to);
+}
+
+#ifdef CONFIG_TASN1
+static void *
+migrate_multifd_tls_x509_default_host_start(QTestState *from,
+                                            QTestState *to)
+{
+    migrate_precopy_tcp_multifd_start_common(from, to, "none");
+    return migrate_tls_x509_default_host_start(from, to);
+}
+
+static void *
+migrate_multifd_tls_x509_override_host_start(QTestState *from,
+                                             QTestState *to)
+{
+    migrate_precopy_tcp_multifd_start_common(from, to, "none");
+    return migrate_tls_x509_override_host_start(from, to);
+}
+
+static void *
+migrate_multifd_tls_x509_mismatch_host_start(QTestState *from,
+                                             QTestState *to)
+{
+    migrate_precopy_tcp_multifd_start_common(from, to, "none");
+    return migrate_tls_x509_mismatch_host_start(from, to);
+}
+
+static void *
+migrate_multifd_tls_x509_allow_anon_client_start(QTestState *from,
+                                                 QTestState *to)
+{
+    migrate_precopy_tcp_multifd_start_common(from, to, "none");
+    return migrate_tls_x509_allow_anon_client_start(from, to);
+}
+
+static void *
+migrate_multifd_tls_x509_reject_anon_client_start(QTestState *from,
+                                                  QTestState *to)
+{
+    migrate_precopy_tcp_multifd_start_common(from, to, "none");
+    return migrate_tls_x509_reject_anon_client_start(from, to);
+}
+#endif /* CONFIG_TASN1 */
+
+static void test_multifd_tcp_tls_psk_match(void)
+{
+    MigrateCommon args = {
+        .listen_uri = "defer",
+        .start_hook = migrate_multifd_tcp_tls_psk_match_start,
+        .end_hook = migrate_tls_psk_end,
+    };
+    test_precopy_common(&args);
+}
+
+static void test_multifd_tcp_tls_psk_mismatch(void)
+{
+    MigrateCommon args = {
+        .start = {
+            .hide_stderr = true,
+        },
+        .listen_uri = "defer",
+        .start_hook = migrate_multifd_tcp_tls_psk_mismatch_start,
+        .end_hook = migrate_tls_psk_end,
+        .result = MIG_TEST_FAIL,
+    };
+    test_precopy_common(&args);
+}
+
+#ifdef CONFIG_TASN1
+static void test_multifd_tcp_tls_x509_default_host(void)
+{
+    MigrateCommon args = {
+        .listen_uri = "defer",
+        .start_hook = migrate_multifd_tls_x509_default_host_start,
+        .end_hook = migrate_tls_x509_end,
+    };
+    test_precopy_common(&args);
+}
+
+static void test_multifd_tcp_tls_x509_override_host(void)
+{
+    MigrateCommon args = {
+        .listen_uri = "defer",
+        .start_hook = migrate_multifd_tls_x509_override_host_start,
+        .end_hook = migrate_tls_x509_end,
+    };
+    test_precopy_common(&args);
+}
+
+static void test_multifd_tcp_tls_x509_mismatch_host(void)
+{
+    /*
+     * This has different behaviour to the non-multifd case.
+     *
+     * In non-multifd case when client aborts due to mismatched
+     * cert host, the server has already started trying to load
+     * migration state, and so it exits with I/O failure.
+     *
+     * In multifd case when client aborts due to mismatched
+     * cert host, the server is still waiting for the other
+     * multifd connections to arrive so hasn't started trying
+     * to load migration state, and thus just aborts the migration
+     * without exiting.
+     */
+    MigrateCommon args = {
+        .start = {
+            .hide_stderr = true,
+        },
+        .listen_uri = "defer",
+        .start_hook = migrate_multifd_tls_x509_mismatch_host_start,
+        .end_hook = migrate_tls_x509_end,
+        .result = MIG_TEST_FAIL,
+    };
+    test_precopy_common(&args);
+}
+
+static void test_multifd_tcp_tls_x509_allow_anon_client(void)
+{
+    MigrateCommon args = {
+        .listen_uri = "defer",
+        .start_hook = migrate_multifd_tls_x509_allow_anon_client_start,
+        .end_hook = migrate_tls_x509_end,
+    };
+    test_precopy_common(&args);
+}
+
+static void test_multifd_tcp_tls_x509_reject_anon_client(void)
+{
+    MigrateCommon args = {
+        .start = {
+            .hide_stderr = true,
+        },
+        .listen_uri = "defer",
+        .start_hook = migrate_multifd_tls_x509_reject_anon_client_start,
+        .end_hook = migrate_tls_x509_end,
+        .result = MIG_TEST_FAIL,
+    };
+    test_precopy_common(&args);
+}
+#endif /* CONFIG_TASN1 */
+
+void migration_test_add_tls(MigrationTestEnv *env)
+{
+    tmpfs = env->tmpfs;
+
+    migration_test_add("/migration/precopy/unix/tls/psk",
+                       test_precopy_unix_tls_psk);
+
+    if (env->has_uffd) {
+        /*
+         * NOTE: psk test is enough for postcopy, as other types of TLS
+         * channels are tested under precopy.  Here what we want to test is the
+         * general postcopy path that has TLS channel enabled.
+         */
+        migration_test_add("/migration/postcopy/tls/psk",
+                           test_postcopy_tls_psk);
+        migration_test_add("/migration/postcopy/recovery/tls/psk",
+                           test_postcopy_recovery_tls_psk);
+        migration_test_add("/migration/postcopy/preempt/tls/psk",
+                           test_postcopy_preempt_tls_psk);
+        migration_test_add("/migration/postcopy/preempt/recovery/tls/psk",
+                           test_postcopy_preempt_all);
+    }
+#ifdef CONFIG_TASN1
+    migration_test_add("/migration/precopy/unix/tls/x509/default-host",
+                       test_precopy_unix_tls_x509_default_host);
+    migration_test_add("/migration/precopy/unix/tls/x509/override-host",
+                       test_precopy_unix_tls_x509_override_host);
+#endif /* CONFIG_TASN1 */
+
+    migration_test_add("/migration/precopy/tcp/tls/psk/match",
+                       test_precopy_tcp_tls_psk_match);
+    migration_test_add("/migration/precopy/tcp/tls/psk/mismatch",
+                       test_precopy_tcp_tls_psk_mismatch);
+#ifdef CONFIG_TASN1
+    migration_test_add("/migration/precopy/tcp/tls/x509/default-host",
+                       test_precopy_tcp_tls_x509_default_host);
+    migration_test_add("/migration/precopy/tcp/tls/x509/override-host",
+                       test_precopy_tcp_tls_x509_override_host);
+    migration_test_add("/migration/precopy/tcp/tls/x509/mismatch-host",
+                       test_precopy_tcp_tls_x509_mismatch_host);
+    migration_test_add("/migration/precopy/tcp/tls/x509/friendly-client",
+                       test_precopy_tcp_tls_x509_friendly_client);
+    migration_test_add("/migration/precopy/tcp/tls/x509/hostile-client",
+                       test_precopy_tcp_tls_x509_hostile_client);
+    migration_test_add("/migration/precopy/tcp/tls/x509/allow-anon-client",
+                       test_precopy_tcp_tls_x509_allow_anon_client);
+    migration_test_add("/migration/precopy/tcp/tls/x509/reject-anon-client",
+                       test_precopy_tcp_tls_x509_reject_anon_client);
+#endif /* CONFIG_TASN1 */
+
+    migration_test_add("/migration/multifd/tcp/tls/psk/match",
+                       test_multifd_tcp_tls_psk_match);
+    migration_test_add("/migration/multifd/tcp/tls/psk/mismatch",
+                       test_multifd_tcp_tls_psk_mismatch);
+#ifdef CONFIG_TASN1
+    migration_test_add("/migration/multifd/tcp/tls/x509/default-host",
+                       test_multifd_tcp_tls_x509_default_host);
+    migration_test_add("/migration/multifd/tcp/tls/x509/override-host",
+                       test_multifd_tcp_tls_x509_override_host);
+    migration_test_add("/migration/multifd/tcp/tls/x509/mismatch-host",
+                       test_multifd_tcp_tls_x509_mismatch_host);
+    migration_test_add("/migration/multifd/tcp/tls/x509/allow-anon-client",
+                       test_multifd_tcp_tls_x509_allow_anon_client);
+    migration_test_add("/migration/multifd/tcp/tls/x509/reject-anon-client",
+                       test_multifd_tcp_tls_x509_reject_anon_client);
+#endif /* CONFIG_TASN1 */
+}