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 /* There isn't a public function to do BIO_ADDR_copy() so we create one */
109 static int bio_addr_copy(BIO_ADDR *dst, BIO_ADDR *src)
116 if (src == NULL || dst == NULL)
119 family = BIO_ADDR_family(src);
120 if (family == AF_UNSPEC) {
125 if (!BIO_ADDR_rawaddress(src, NULL, &len))
129 data = OPENSSL_malloc(len);
134 if (!BIO_ADDR_rawaddress(src, data, &len))
137 if (!BIO_ADDR_rawmake(src, family, data, len, BIO_ADDR_rawport(src)))
146 static int bio_msg_copy(BIO_MSG *dst, BIO_MSG *src)
149 * Note it is assumed that the originally allocated data sizes for dst and
152 memcpy(dst->data, src->data, src->data_len);
153 dst->data_len = src->data_len;
154 dst->flags = src->flags;
155 if (dst->local != NULL) {
156 if (src->local != NULL) {
157 if (!TEST_true(bio_addr_copy(dst->local, src->local)))
160 BIO_ADDR_clear(dst->local);
163 if (!TEST_true(bio_addr_copy(dst->peer, src->peer)))
169 static int noisy_dgram_recvmmsg(BIO *bio, BIO_MSG *msg, size_t stride,
170 size_t num_msg, uint64_t flags,
171 size_t *msgs_processed)
173 BIO *next = BIO_next(bio);
174 size_t i, j, data_len = 0, msg_cnt = 0;
176 struct noisy_dgram_st *data;
181 data = BIO_get_data(bio);
186 * For simplicity we assume that all elements in the msg array have the
187 * same data_len. They are not required to by the API, but it would be quite
188 * strange for that not to be the case - and our code that calls
189 * BIO_recvmmsg does do this (which is all that is important for this test
190 * code). We test the invariant here.
192 for (i = 0; i < num_msg; i++) {
194 data_len = msg[i].data_len;
195 if (!TEST_size_t_le(data_len, MSG_DATA_LEN_MAX))
197 } else if (!TEST_size_t_eq(msg[i].data_len, data_len)) {
202 if (!BIO_recvmmsg(next, msg, stride, num_msg, flags, msgs_processed))
205 #ifdef OSSL_NOISY_DGRAM_DEBUG
206 printf("Pre-filter datagram list:\n");
207 for (i = 0; i < *msgs_processed; i++) {
208 printf("Pre-filter Datagram:\n");
209 BIO_dump_fp(stdout, msg[i].data, msg[i].data_len);
212 printf("End of pre-filter datagram list\nApplying noise filters:\n");
215 msg_cnt = *msgs_processed;
217 /* Introduce noise */
218 for (i = 0, thismsg = msg;
220 i++, thismsg++, data->this_dgram++) {
224 /* If we have a delayed message ready insert it now */
225 if (data->delayed_dgram > 0
226 && data->delayed_dgram == data->this_dgram) {
227 if (msg_cnt < num_msg) {
228 /* Make space for the inserted message */
229 for (j = msg_cnt; j > i; j--) {
230 if (!bio_msg_copy(&msg[j], &msg[j - 1]))
233 if (!bio_msg_copy(thismsg, &data->msg))
236 data->delayed_dgram = 0;
237 #ifdef OSSL_NOISY_DGRAM_DEBUG
238 printf("**Inserting a delayed datagram\n");
239 BIO_dump_fp(stdout, thismsg->data, thismsg->data_len);
243 } /* else we have no space for the insertion, so just drop it */
244 data->delayed_dgram = 0;
247 get_noise(&delay, &should_drop);
249 /* We ignore delay if a message is already delayed */
250 if (delay > 0 && data->delayed_dgram == 0) {
252 * Note that a message may be delayed *and* dropped, or delayed
254 * Delayed and dropped means the message will not be sent now and
255 * will only be sent after the delay.
256 * Delayed and not dropped means the message will be sent now and
257 * a duplicate will also be sent after the delay.
260 if (!bio_msg_copy(&data->msg, thismsg))
263 data->delayed_dgram = data->this_dgram + delay;
265 #ifdef OSSL_NOISY_DGRAM_DEBUG
266 printf("**Delaying a datagram for %u messages%s\n",
267 (unsigned int)delay, should_drop ? "" : "(duplicating)");
268 BIO_dump_fp(stdout, thismsg->data, thismsg->data_len);
274 #ifdef OSSL_NOISY_DGRAM_DEBUG
275 printf("**Dropping a datagram\n");
276 BIO_dump_fp(stdout, thismsg->data, thismsg->data_len);
279 for (j = i + 1; j < msg_cnt; j++) {
280 if (!bio_msg_copy(&msg[j - 1], &msg[j]))
287 #ifdef OSSL_NOISY_DGRAM_DEBUG
288 printf("End of noise filters\nPost-filter datagram list:\n");
289 for (i = 0; i < msg_cnt; i++) {
290 printf("Post-filter Datagram:\n");
291 BIO_dump_fp(stdout, msg[i].data, msg[i].data_len);
294 printf("End of post-filter datagram list\n");
297 *msgs_processed = msg_cnt;
300 ERR_raise(ERR_LIB_BIO, BIO_R_NON_FATAL);
307 static void data_free(struct noisy_dgram_st *data)
312 OPENSSL_free(data->msg.data);
313 BIO_ADDR_free(data->msg.peer);
314 BIO_ADDR_free(data->msg.local);
318 static int noisy_dgram_new(BIO *bio)
320 struct noisy_dgram_st *data = OPENSSL_zalloc(sizeof(*data));
325 data->msg.data = OPENSSL_malloc(MSG_DATA_LEN_MAX);
326 data->msg.peer = BIO_ADDR_new();
327 data->msg.local = BIO_ADDR_new();
328 if (data->msg.data == NULL
329 || data->msg.peer == NULL
330 || data->msg.local == NULL) {
335 BIO_set_data(bio, data);
336 BIO_set_init(bio, 1);
341 static int noisy_dgram_free(BIO *bio)
343 data_free(BIO_get_data(bio));
344 BIO_set_data(bio, NULL);
345 BIO_set_init(bio, 0);
350 /* Choose a sufficiently large type likely to be unused for this custom BIO */
351 #define BIO_TYPE_NOISY_DGRAM_FILTER (0x80 | BIO_TYPE_FILTER)
353 static BIO_METHOD *method_noisy_dgram = NULL;
355 /* Note: Not thread safe! */
356 const BIO_METHOD *bio_f_noisy_dgram_filter(void)
358 if (method_noisy_dgram == NULL) {
359 method_noisy_dgram = BIO_meth_new(BIO_TYPE_NOISY_DGRAM_FILTER,
360 "Nosiy datagram filter");
361 if (method_noisy_dgram == NULL
362 || !BIO_meth_set_write(method_noisy_dgram, noisy_dgram_write)
363 || !BIO_meth_set_read(method_noisy_dgram, noisy_dgram_read)
364 || !BIO_meth_set_puts(method_noisy_dgram, noisy_dgram_puts)
365 || !BIO_meth_set_gets(method_noisy_dgram, noisy_dgram_gets)
366 || !BIO_meth_set_ctrl(method_noisy_dgram, noisy_dgram_ctrl)
367 || !BIO_meth_set_sendmmsg(method_noisy_dgram, noisy_dgram_sendmmsg)
368 || !BIO_meth_set_recvmmsg(method_noisy_dgram, noisy_dgram_recvmmsg)
369 || !BIO_meth_set_create(method_noisy_dgram, noisy_dgram_new)
370 || !BIO_meth_set_destroy(method_noisy_dgram, noisy_dgram_free))
373 return method_noisy_dgram;
376 void bio_f_noisy_dgram_filter_free(void)
378 BIO_meth_free(method_noisy_dgram);