summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomas Mraz <tomas@openssl.org>2024-02-05 16:03:15 +0100
committerTomas Mraz <tomas@openssl.org>2024-03-15 10:19:19 +0100
commit37ffd4a1fa02b37919b5c7a8059ba57d3c49b1f0 (patch)
tree58c9085f392592be6b4de802c58f4a896a34b1ad
parent45d16a44eb64df3c7df918520b828be4d41b35cb (diff)
Add support for bandwidth limitation in noisydgram BIO filter
Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Neil Horman <nhorman@openssl.org> (Merged from https://github.com/openssl/openssl/pull/23588)
-rw-r--r--test/helpers/noisydgrambio.c157
-rw-r--r--test/helpers/quictestlib.h11
2 files changed, 162 insertions, 6 deletions
diff --git a/test/helpers/noisydgrambio.c b/test/helpers/noisydgrambio.c
index 0dab50762a..4660250e9c 100644
--- a/test/helpers/noisydgrambio.c
+++ b/test/helpers/noisydgrambio.c
@@ -12,12 +12,30 @@
#include "../testutil.h"
#define MSG_DATA_LEN_MAX 1472
+#define SAMPLING_WINDOW_PERIOD 10 /* in milliseconds */
+#define MAX_PKTS_PER_WINDOW 1024
+
+struct pkt_info_st {
+ size_t size;
+ OSSL_TIME timestamp;
+};
+
+struct bw_limiter_st {
+ struct pkt_info_st pinfos[MAX_PKTS_PER_WINDOW]; /* ring buffer */
+ size_t start, num; /* ring buffer start and number of items */
+ size_t size_sum; /* sum of packet sizes in window */
+ size_t bw; /* bandwidth in bytes/ms */
+};
struct noisy_dgram_st {
uint64_t this_dgram;
BIO_MSG msg;
uint64_t reinject_dgram;
int backoff;
+ int noise_rate; /* 1 in noise_rate packets will get noise */
+ struct bw_limiter_st recv_limit, send_limit;
+ OSSL_TIME (*now_cb)(void *arg);
+ void *now_cb_arg;
};
static long noisy_dgram_ctrl(BIO *bio, int cmd, long num, void *ptr)
@@ -42,6 +60,48 @@ static long noisy_dgram_ctrl(BIO *bio, int cmd, long num, void *ptr)
ret = 1;
break;
}
+ case BIO_CTRL_NOISE_RATE: {
+ struct noisy_dgram_st *data;
+
+ data = BIO_get_data(bio);
+ if (!TEST_ptr(data))
+ return 0;
+ data->noise_rate = (int)num;
+ ret = 1;
+ break;
+ }
+ case BIO_CTRL_NOISE_RECV_BANDWIDTH: {
+ struct noisy_dgram_st *data;
+
+ data = BIO_get_data(bio);
+ if (!TEST_ptr(data))
+ return 0;
+ data->recv_limit.bw = (size_t)num;
+ ret = 1;
+ break;
+ }
+ case BIO_CTRL_NOISE_SEND_BANDWIDTH: {
+ struct noisy_dgram_st *data;
+
+ data = BIO_get_data(bio);
+ if (!TEST_ptr(data))
+ return 0;
+ data->send_limit.bw = (size_t)num;
+ ret = 1;
+ break;
+ }
+ case BIO_CTRL_NOISE_SET_NOW_CB: {
+ struct noisy_dgram_st *data;
+ struct bio_noise_now_cb_st *now_cb = ptr;
+
+ data = BIO_get_data(bio);
+ if (!TEST_ptr(data))
+ return 0;
+ data->now_cb = now_cb->now_cb;
+ data->now_cb_arg = now_cb->now_cb_arg;
+ ret = 1;
+ break;
+ }
default:
ret = BIO_ctrl(next, cmd, num, ptr);
break;
@@ -49,15 +109,89 @@ static long noisy_dgram_ctrl(BIO *bio, int cmd, long num, void *ptr)
return ret;
}
+static size_t bandwidth_limit(struct bw_limiter_st *limit, OSSL_TIME now,
+ BIO_MSG *msg, size_t num_msg)
+{
+ size_t i;
+ OSSL_TIME sampling_start
+ = ossl_time_subtract(now, ossl_ms2time(SAMPLING_WINDOW_PERIOD));
+
+ if (limit->bw == 0) /* 0 -> no limit */
+ return num_msg;
+
+ if (num_msg > MAX_PKTS_PER_WINDOW)
+ num_msg = MAX_PKTS_PER_WINDOW;
+
+ /* trim the start of the ring buffer */
+ for (i = 0; i < limit->num; i++) {
+ size_t idx = (limit->start + i) % MAX_PKTS_PER_WINDOW;
+
+ if (ossl_time_compare(limit->pinfos[idx].timestamp, sampling_start) >= 0)
+ break;
+ limit->size_sum -= limit->pinfos[idx].size;
+ }
+ limit->start = (limit->start + i) % MAX_PKTS_PER_WINDOW;
+ limit->num -= i;
+
+ for (i = 0; i < num_msg; ++i) {
+ size_t end;
+ size_t pktsize = msg[i].data_len;
+
+ if ((limit->size_sum + pktsize) / SAMPLING_WINDOW_PERIOD > limit->bw) {
+ /*
+ * Throw out all the packets once reaching the limit,
+ * although some following packets could still fit.
+ * This is accurate enough.
+ */
+#ifdef OSSL_NOISY_DGRAM_DEBUG
+ printf("**BW limit applied: now: %llu orig packets %u new packets %u\n",
+ (unsigned long long)ossl_time2ms(now),
+ (unsigned int)num_msg, (unsigned int) i);
+#endif
+ num_msg = i;
+ break;
+ }
+
+ if (limit->num >= MAX_PKTS_PER_WINDOW) {
+ limit->size_sum -= limit->pinfos[limit->start].size;
+ limit->start = (limit->start + 1) % MAX_PKTS_PER_WINDOW;
+ } else {
+ ++limit->num;
+ }
+ end = (limit->start + limit->num) % MAX_PKTS_PER_WINDOW;
+ limit->pinfos[end].size = pktsize;
+ limit->pinfos[end].timestamp = now;
+ limit->size_sum += pktsize;
+ }
+ return num_msg;
+}
+
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);
+ struct noisy_dgram_st *data;
+ OSSL_TIME now;
if (next == NULL)
return 0;
+ data = BIO_get_data(bio);
+ if (!TEST_ptr(data))
+ return 0;
+
+ now = data->now_cb != NULL ? data->now_cb(data->now_cb_arg)
+ : ossl_time_now();
+
+ /* bandwidth limit can be applied on both sides */
+ num_msg = bandwidth_limit(&data->send_limit, now, msg, num_msg);
+ if (num_msg == 0) {
+ *msgs_processed = 0;
+ ERR_raise(ERR_LIB_BIO, BIO_R_NON_FATAL);
+ return 0;
+ }
+
/*
* We only introduce noise when receiving messages. We just pass this on
* to the underlying BIO.
@@ -65,7 +199,7 @@ static int noisy_dgram_sendmmsg(BIO *bio, BIO_MSG *msg, size_t stride,
return BIO_sendmmsg(next, msg, stride, num_msg, flags, msgs_processed);
}
-/* 1 in NOISE_RATE datagrams will be noisy. With a value of 5 that is 20% */
+/* Default noise_rate value. With a value of 5 that is 20% packets. */
#define NOISE_RATE 5
/*
@@ -90,14 +224,14 @@ static int noisy_dgram_sendmmsg(BIO *bio, BIO_MSG *msg, size_t stride,
*/
#define MAX_DGRAM_REINJECT 4
-static void get_noise(int long_header, uint64_t *reinject, int *should_drop,
- uint16_t *flip, size_t *flip_offset)
+static void get_noise(int noise_rate, int long_header, uint64_t *reinject,
+ int *should_drop, uint16_t *flip, size_t *flip_offset)
{
uint32_t type;
*flip = 0;
- if (test_random() % NOISE_RATE != 0) {
+ if (test_random() % noise_rate != 0) {
*reinject = 0;
*should_drop = 0;
return;
@@ -173,6 +307,7 @@ static int noisy_dgram_recvmmsg(BIO *bio, BIO_MSG *msg, size_t stride,
size_t i, j, data_len = 0, msg_cnt = 0;
BIO_MSG *thismsg;
struct noisy_dgram_st *data;
+ OSSL_TIME now;
if (!TEST_ptr(next))
return 0;
@@ -211,7 +346,16 @@ static int noisy_dgram_recvmmsg(BIO *bio, BIO_MSG *msg, size_t stride,
printf("End of pre-filter datagram list\nApplying noise filters:\n");
#endif
+ now = data->now_cb != NULL ? data->now_cb(data->now_cb_arg)
+ : ossl_time_now();
+
msg_cnt = *msgs_processed;
+ msg_cnt = bandwidth_limit(&data->recv_limit, now, msg, msg_cnt);
+ if (msg_cnt == 0)
+ goto end;
+
+ if (data->noise_rate == 0)
+ goto end;
/* Introduce noise */
for (i = 0, thismsg = msg;
@@ -245,7 +389,8 @@ static int noisy_dgram_recvmmsg(BIO *bio, BIO_MSG *msg, size_t stride,
data->reinject_dgram = 0;
}
- get_noise(/* long header */ (((uint8_t *)thismsg->data)[0] & 0x80) != 0,
+ get_noise(data->noise_rate,
+ /* long header */ (((uint8_t *)thismsg->data)[0] & 0x80) != 0,
&reinject, &should_drop, &flip, &flip_offset);
if (data->backoff) {
/*
@@ -313,6 +458,7 @@ static int noisy_dgram_recvmmsg(BIO *bio, BIO_MSG *msg, size_t stride,
printf("End of post-filter datagram list\n");
#endif
+ end:
*msgs_processed = msg_cnt;
if (msg_cnt == 0) {
@@ -341,6 +487,7 @@ static int noisy_dgram_new(BIO *bio)
if (!TEST_ptr(data))
return 0;
+ data->noise_rate = NOISE_RATE;
data->msg.data = OPENSSL_malloc(MSG_DATA_LEN_MAX);
data->msg.peer = BIO_ADDR_new();
data->msg.local = BIO_ADDR_new();
diff --git a/test/helpers/quictestlib.h b/test/helpers/quictestlib.h
index 79de17d153..228d396f41 100644
--- a/test/helpers/quictestlib.h
+++ b/test/helpers/quictestlib.h
@@ -247,7 +247,16 @@ int qtest_fault_resize_datagram(QTEST_FAULT *fault, size_t newlen);
/* Copy a BIO_MSG */
int bio_msg_copy(BIO_MSG *dst, BIO_MSG *src);
-#define BIO_CTRL_NOISE_BACK_OFF 1001
+#define BIO_CTRL_NOISE_BACK_OFF 1001
+#define BIO_CTRL_NOISE_RATE 1002
+#define BIO_CTRL_NOISE_RECV_BANDWIDTH 1003
+#define BIO_CTRL_NOISE_SEND_BANDWIDTH 1004
+#define BIO_CTRL_NOISE_SET_NOW_CB 1005
+
+struct bio_noise_now_cb_st {
+ OSSL_TIME (*now_cb)(void *);
+ void *now_cb_arg;
+};
/* BIO filter for simulating a noisy UDP socket */
const BIO_METHOD *bio_f_noisy_dgram_filter(void);