2 * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
10 #include <openssl/bio.h>
11 #include "quictestlib.h"
12 #include "../testutil.h"
14 #define MSG_DATA_LEN_MAX 1472
16 struct noisy_dgram_st {
19 uint64_t delayed_dgram;
22 static int noisy_dgram_read(BIO *bio, char *out, int outl)
24 /* We don't support this - not needed anyway */
28 static int noisy_dgram_write(BIO *bio, const char *in, int inl)
30 /* We don't support this - not needed anyway */
34 static long noisy_dgram_ctrl(BIO *bio, int cmd, long num, void *ptr)
37 BIO *next = BIO_next(bio);
47 ret = BIO_ctrl(next, cmd, num, ptr);
53 static int noisy_dgram_gets(BIO *bio, char *buf, int size)
55 /* We don't support this - not needed anyway */
59 static int noisy_dgram_puts(BIO *bio, const char *str)
61 /* We don't support this - not needed anyway */
65 static int noisy_dgram_sendmmsg(BIO *bio, BIO_MSG *msg, size_t stride,
66 size_t num_msg, uint64_t flags,
67 size_t *msgs_processed)
69 BIO *next = BIO_next(bio);
75 * We only introduce noise when receiving messages. We just pass this on
76 * to the underlying BIO.
78 return BIO_sendmmsg(next, msg, stride, num_msg, flags, msgs_processed);
81 static void get_noise(uint64_t *delay, int *should_drop)
85 /* 20% of all datagrams should be noisy */
86 if (test_random() % 5 != 0) {
92 type = test_random() % 3;
94 /* Of noisy datagrams, 33% drop only, 33% delay only, 33% drop and delay */
96 *should_drop = (type == 0 || type == 1);
98 /* Where a delay occurs we delay by 1 - 4 datagrams */
99 *delay = (type == 0) ? 0 : (uint64_t)((test_random() % 4) + 1);
102 * No point in delaying by 1 datagram if we are also dropping, so we delay
103 * by an extra datagram in that case
105 *delay += (uint64_t)(*should_drop);
108 static int noisy_dgram_recvmmsg(BIO *bio, BIO_MSG *msg, size_t stride,
109 size_t num_msg, uint64_t flags,
110 size_t *msgs_processed)
112 BIO *next = BIO_next(bio);
113 size_t i, j, data_len = 0, msg_cnt = 0;
115 struct noisy_dgram_st *data;
120 data = BIO_get_data(bio);
125 * For simplicity we assume that all elements in the msg array have the
126 * same data_len. They are not required to by the API, but it would be quite
127 * strange for that not to be the case - and our code that calls
128 * BIO_recvmmsg does do this (which is all that is important for this test
129 * code). We test the invariant here.
131 for (i = 0; i < num_msg; i++) {
133 data_len = msg[i].data_len;
134 if (!TEST_size_t_le(data_len, MSG_DATA_LEN_MAX))
136 } else if (!TEST_size_t_eq(msg[i].data_len, data_len)) {
141 if (!BIO_recvmmsg(next, msg, stride, num_msg, flags, msgs_processed))
144 #ifdef OSSL_NOISY_DGRAM_DEBUG
145 printf("Pre-filter datagram list:\n");
146 for (i = 0; i < *msgs_processed; i++) {
147 printf("Pre-filter Datagram:\n");
148 BIO_dump_fp(stdout, msg[i].data, msg[i].data_len);
151 printf("End of pre-filter datagram list\nApplying noise filters:\n");
154 msg_cnt = *msgs_processed;
156 /* Introduce noise */
157 for (i = 0, thismsg = msg;
159 i++, thismsg++, data->this_dgram++) {
163 /* If we have a delayed message ready insert it now */
164 if (data->delayed_dgram > 0
165 && data->delayed_dgram == data->this_dgram) {
166 if (msg_cnt < num_msg) {
167 /* Make space for the inserted message */
168 for (j = msg_cnt; j > i; j--) {
169 if (!bio_msg_copy(&msg[j], &msg[j - 1]))
172 if (!bio_msg_copy(thismsg, &data->msg))
175 data->delayed_dgram = 0;
176 #ifdef OSSL_NOISY_DGRAM_DEBUG
177 printf("**Inserting a delayed datagram\n");
178 BIO_dump_fp(stdout, thismsg->data, thismsg->data_len);
182 } /* else we have no space for the insertion, so just drop it */
183 data->delayed_dgram = 0;
186 get_noise(&delay, &should_drop);
188 /* We ignore delay if a message is already delayed */
189 if (delay > 0 && data->delayed_dgram == 0) {
191 * Note that a message may be delayed *and* dropped, or delayed
193 * Delayed and dropped means the message will not be sent now and
194 * will only be sent after the delay.
195 * Delayed and not dropped means the message will be sent now and
196 * a duplicate will also be sent after the delay.
199 if (!bio_msg_copy(&data->msg, thismsg))
202 data->delayed_dgram = data->this_dgram + delay;
204 #ifdef OSSL_NOISY_DGRAM_DEBUG
205 printf("**Delaying a datagram for %u messages%s\n",
206 (unsigned int)delay, should_drop ? "" : "(duplicating)");
207 BIO_dump_fp(stdout, thismsg->data, thismsg->data_len);
213 #ifdef OSSL_NOISY_DGRAM_DEBUG
214 printf("**Dropping a datagram\n");
215 BIO_dump_fp(stdout, thismsg->data, thismsg->data_len);
218 for (j = i + 1; j < msg_cnt; j++) {
219 if (!bio_msg_copy(&msg[j - 1], &msg[j]))
226 #ifdef OSSL_NOISY_DGRAM_DEBUG
227 printf("End of noise filters\nPost-filter datagram list:\n");
228 for (i = 0; i < msg_cnt; i++) {
229 printf("Post-filter Datagram:\n");
230 BIO_dump_fp(stdout, msg[i].data, msg[i].data_len);
233 printf("End of post-filter datagram list\n");
236 *msgs_processed = msg_cnt;
239 ERR_raise(ERR_LIB_BIO, BIO_R_NON_FATAL);
246 static void data_free(struct noisy_dgram_st *data)
251 OPENSSL_free(data->msg.data);
252 BIO_ADDR_free(data->msg.peer);
253 BIO_ADDR_free(data->msg.local);
257 static int noisy_dgram_new(BIO *bio)
259 struct noisy_dgram_st *data = OPENSSL_zalloc(sizeof(*data));
264 data->msg.data = OPENSSL_malloc(MSG_DATA_LEN_MAX);
265 data->msg.peer = BIO_ADDR_new();
266 data->msg.local = BIO_ADDR_new();
267 if (data->msg.data == NULL
268 || data->msg.peer == NULL
269 || data->msg.local == NULL) {
274 BIO_set_data(bio, data);
275 BIO_set_init(bio, 1);
280 static int noisy_dgram_free(BIO *bio)
282 data_free(BIO_get_data(bio));
283 BIO_set_data(bio, NULL);
284 BIO_set_init(bio, 0);
289 /* Choose a sufficiently large type likely to be unused for this custom BIO */
290 #define BIO_TYPE_NOISY_DGRAM_FILTER (0x80 | BIO_TYPE_FILTER)
292 static BIO_METHOD *method_noisy_dgram = NULL;
294 /* Note: Not thread safe! */
295 const BIO_METHOD *bio_f_noisy_dgram_filter(void)
297 if (method_noisy_dgram == NULL) {
298 method_noisy_dgram = BIO_meth_new(BIO_TYPE_NOISY_DGRAM_FILTER,
299 "Nosiy datagram filter");
300 if (method_noisy_dgram == NULL
301 || !BIO_meth_set_write(method_noisy_dgram, noisy_dgram_write)
302 || !BIO_meth_set_read(method_noisy_dgram, noisy_dgram_read)
303 || !BIO_meth_set_puts(method_noisy_dgram, noisy_dgram_puts)
304 || !BIO_meth_set_gets(method_noisy_dgram, noisy_dgram_gets)
305 || !BIO_meth_set_ctrl(method_noisy_dgram, noisy_dgram_ctrl)
306 || !BIO_meth_set_sendmmsg(method_noisy_dgram, noisy_dgram_sendmmsg)
307 || !BIO_meth_set_recvmmsg(method_noisy_dgram, noisy_dgram_recvmmsg)
308 || !BIO_meth_set_create(method_noisy_dgram, noisy_dgram_new)
309 || !BIO_meth_set_destroy(method_noisy_dgram, noisy_dgram_free))
312 return method_noisy_dgram;
315 void bio_f_noisy_dgram_filter_free(void)
317 BIO_meth_free(method_noisy_dgram);