Create a noisy dgram test
authorMatt Caswell <matt@openssl.org>
Thu, 14 Sep 2023 09:59:47 +0000 (10:59 +0100)
committerMatt Caswell <matt@openssl.org>
Fri, 22 Sep 2023 12:56:43 +0000 (13:56 +0100)
Create a noisy dgram test that can drop/duplicate/reorder UDP packets and
ensure that the QUIC connection is tolerant of this. At this stage we just
create the outline of the test. Adding in the noise will come in future
commits.

Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/22157)

test/build.info
test/helpers/noisydgrambio.c [new file with mode: 0644]
test/helpers/quictestlib.c
test/helpers/quictestlib.h
test/quicapitest.c

index d897f726a86fef67b46264a59c109fb793e901b2..4736e28c8b0bb10059bf08d83eadb515a2e62368 100644 (file)
@@ -339,7 +339,7 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[quic_client_test]=../include ../apps/include
   DEPEND[quic_client_test]=../libcrypto.a ../libssl.a libtestutil.a
 
-  SOURCE[quic_multistream_test]=quic_multistream_test.c helpers/ssltestlib.c helpers/quictestlib.c
+  SOURCE[quic_multistream_test]=quic_multistream_test.c helpers/ssltestlib.c helpers/quictestlib.c helpers/noisydgrambio.c
   INCLUDE[quic_multistream_test]=../include ../apps/include
   DEPEND[quic_multistream_test]=../libcrypto.a ../libssl.a libtestutil.a
 
@@ -818,15 +818,15 @@ IF[{- !$disabled{tests} -}]
       INCLUDE[event_queue_test]=../include ../apps/include
       DEPEND[event_queue_test]=../libcrypto ../libssl.a libtestutil.a
 
-      SOURCE[quicfaultstest]=quicfaultstest.c helpers/ssltestlib.c helpers/quictestlib.c
+      SOURCE[quicfaultstest]=quicfaultstest.c helpers/ssltestlib.c helpers/quictestlib.c helpers/noisydgrambio.c
       INCLUDE[quicfaultstest]=../include ../apps/include ..
       DEPEND[quicfaultstest]=../libcrypto.a ../libssl.a libtestutil.a
 
-      SOURCE[quicapitest]=quicapitest.c helpers/ssltestlib.c helpers/quictestlib.c
+      SOURCE[quicapitest]=quicapitest.c helpers/ssltestlib.c helpers/quictestlib.c helpers/noisydgrambio.c
       INCLUDE[quicapitest]=../include ../apps/include
       DEPEND[quicapitest]=../libcrypto.a ../libssl.a libtestutil.a
 
-      SOURCE[quic_newcid_test]=quic_newcid_test.c helpers/ssltestlib.c helpers/quictestlib.c
+      SOURCE[quic_newcid_test]=quic_newcid_test.c helpers/ssltestlib.c helpers/quictestlib.c helpers/noisydgrambio.c
       INCLUDE[quic_newcid_test]=../include ../apps/include ..
       DEPEND[quic_newcid_test]=../libcrypto.a ../libssl.a libtestutil.a
     ENDIF
diff --git a/test/helpers/noisydgrambio.c b/test/helpers/noisydgrambio.c
new file mode 100644 (file)
index 0000000..890ff79
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/bio.h>
+#include "quictestlib.h"
+
+static int noisy_dgram_read(BIO *bio, char *out, int outl)
+{
+    /* We don't support this - not needed anyway */
+    return -1;
+}
+
+static int noisy_dgram_write(BIO *bio, const char *in, int inl)
+{
+    /* We don't support this - not needed anyway */
+    return -1;
+}
+
+static long noisy_dgram_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+    long ret;
+    BIO *next = BIO_next(bio);
+
+    if (next == NULL)
+        return 0;
+
+    switch (cmd) {
+    case BIO_CTRL_DUP:
+        ret = 0L;
+        break;
+    default:
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    }
+    return ret;
+}
+
+static int noisy_dgram_gets(BIO *bio, char *buf, int size)
+{
+    /* We don't support this - not needed anyway */
+    return -1;
+}
+
+static int noisy_dgram_puts(BIO *bio, const char *str)
+{
+    /* We don't support this - not needed anyway */
+    return -1;
+}
+
+static int noisy_dgram_sendmmsg(BIO *bio, BIO_MSG *msg, size_t stride,
+                                size_t num_msg, uint64_t flags,
+                                size_t *msgs_processed)
+{
+    BIO *next = BIO_next(bio);
+
+    if (next == NULL)
+        return 0;
+
+    /*
+     * We only introduce noise when receiving messages. We just pass this on
+     * to the underlying BIO.
+     */
+    return BIO_sendmmsg(next, msg, stride, num_msg, flags, msgs_processed);
+}
+
+static int noisy_dgram_recvmmsg(BIO *bio, BIO_MSG *msg, size_t stride,
+                                size_t num_msg, uint64_t flags,
+                                size_t *msgs_processed)
+{
+    BIO *next = BIO_next(bio);
+
+    if (next == NULL)
+        return 0;
+
+    /*
+     * We will introduce noise here. None implemented yet.
+     */
+    return BIO_recvmmsg(next, msg, stride, num_msg, flags, msgs_processed);
+}
+
+static int noisy_dgram_new(BIO *bio)
+{
+    BIO_set_init(bio, 1);
+
+    return 1;
+}
+
+static int noisy_dgram_free(BIO *bio)
+{
+    BIO_set_init(bio, 0);
+
+    return 1;
+}
+
+/* Choose a sufficiently large type likely to be unused for this custom BIO */
+#define BIO_TYPE_NOISY_DGRAM_FILTER  (0x80 | BIO_TYPE_FILTER)
+
+static BIO_METHOD *method_noisy_dgram = NULL;
+
+/* Note: Not thread safe! */
+const BIO_METHOD *bio_f_noisy_dgram_filter(void)
+{
+    if (method_noisy_dgram == NULL) {
+        method_noisy_dgram = BIO_meth_new(BIO_TYPE_NOISY_DGRAM_FILTER,
+                                          "Nosiy datagram filter");
+        if (method_noisy_dgram == NULL
+            || !BIO_meth_set_write(method_noisy_dgram, noisy_dgram_write)
+            || !BIO_meth_set_read(method_noisy_dgram, noisy_dgram_read)
+            || !BIO_meth_set_puts(method_noisy_dgram, noisy_dgram_puts)
+            || !BIO_meth_set_gets(method_noisy_dgram, noisy_dgram_gets)
+            || !BIO_meth_set_ctrl(method_noisy_dgram, noisy_dgram_ctrl)
+            || !BIO_meth_set_sendmmsg(method_noisy_dgram, noisy_dgram_sendmmsg)
+            || !BIO_meth_set_recvmmsg(method_noisy_dgram, noisy_dgram_recvmmsg)
+            || !BIO_meth_set_create(method_noisy_dgram, noisy_dgram_new)
+            || !BIO_meth_set_destroy(method_noisy_dgram, noisy_dgram_free))
+            return NULL;
+    }
+    return method_noisy_dgram;
+}
+
+void bio_f_noisy_dgram_filter_free(void)
+{
+    BIO_meth_free(method_noisy_dgram);
+}
index f0955559dcac251fdd172771b1f5699502fbea9e..bc135eec95bd1dc435bad9a5a27687a150c857de 100644 (file)
@@ -140,6 +140,14 @@ int qtest_create_quic_objects(OSSL_LIB_CTX *libctx, SSL_CTX *clientctx,
             goto err;
     }
 
+    if ((flags & QTEST_FLAG_NOISE) != 0) {
+        BIO *noisebio = BIO_new(bio_f_noisy_dgram_filter());
+
+        if (!TEST_ptr(noisebio))
+            goto err;
+        cbio = BIO_push(noisebio, cbio);
+    }
+
     SSL_set_bio(*cssl, cbio, cbio);
 
     if (!TEST_true(SSL_set_blocking_mode(*cssl,
index 45f6ebec7918e231bb9f40bef4e85f2c53481d1d..7a72e352d9a867a6f91b56f52aae6c6e2d2e78ff 100644 (file)
@@ -27,9 +27,11 @@ typedef struct qtest_fault_encrypted_extensions {
 /* Flags for use with qtest_create_quic_objects() */
 
 /* Indicates whether we are using blocking mode or not */
-#define QTEST_FLAG_BLOCK        1
+#define QTEST_FLAG_BLOCK        (1 << 0)
 /* Use fake time rather than real time */
-#define QTEST_FLAG_FAKE_TIME    2
+#define QTEST_FLAG_FAKE_TIME    (1 << 1)
+/* Introduce noise in the BIO */
+#define QTEST_FLAG_NOISE        (1 << 2)
 
 /*
  * Given an SSL_CTX for the client and filenames for the server certificate and
@@ -230,3 +232,9 @@ int qtest_fault_set_datagram_listener(QTEST_FAULT *fault,
  * exceeds the over allocation.
  */
 int qtest_fault_resize_datagram(QTEST_FAULT *fault, size_t newlen);
+
+/* BIO filter for simulating a noisy UDP socket */
+const BIO_METHOD *bio_f_noisy_dgram_filter(void);
+
+/* Free the BIO filter method object */
+void bio_f_noisy_dgram_filter_free(void);
\ No newline at end of file
index c471495aa21bcf1f7fc5e2bbf9a7e563973826ef..ed11b73ba24cd91bda8658220ee4bd187caa4fd3 100644 (file)
@@ -1250,6 +1250,34 @@ static int test_alpn(int idx)
     return testresult;
 }
 
+static int test_noisy_dgram(void)
+{
+    SSL_CTX *cctx = SSL_CTX_new_ex(libctx, NULL, OSSL_QUIC_client_method());
+    SSL *clientquic = NULL;
+    QUIC_TSERVER *qtserv = NULL;
+    int testresult = 0;
+
+    if (!TEST_ptr(cctx)
+            || !TEST_true(qtest_create_quic_objects(libctx, cctx, NULL, cert,
+                                                    privkey,
+                                                    QTEST_FLAG_NOISE,
+                                                    &qtserv,
+                                                    &clientquic, NULL)))
+        goto err;
+
+    if (!TEST_true(qtest_create_quic_connection(qtserv, clientquic)))
+            goto err;
+
+    testresult = 1;
+ err:
+    ossl_quic_tserver_free(qtserv);
+    SSL_free(clientquic);
+    SSL_CTX_free(cctx);
+
+    return testresult;
+}
+
+
 OPT_TEST_DECLARE_USAGE("provider config certsdir datadir\n")
 
 int setup_tests(void)
@@ -1323,6 +1351,8 @@ int setup_tests(void)
     ADD_ALL_TESTS(test_non_io_retry, 2);
     ADD_TEST(test_quic_psk);
     ADD_ALL_TESTS(test_alpn, 2);
+    ADD_TEST(test_noisy_dgram);
+
     return 1;
  err:
     cleanup_tests();
@@ -1331,6 +1361,7 @@ int setup_tests(void)
 
 void cleanup_tests(void)
 {
+    bio_f_noisy_dgram_filter_free();
     OPENSSL_free(cert);
     OPENSSL_free(privkey);
     OSSL_PROVIDER_unload(defctxnull);