Skip to content

Commit

Permalink
Add support for bandwidth limitation in noisydgram BIO filter
Browse files Browse the repository at this point in the history
Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: Neil Horman <nhorman@openssl.org>
(Merged from #23588)
  • Loading branch information
t8m committed Mar 15, 2024
1 parent 45d16a4 commit 37ffd4a
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 6 deletions.
157 changes: 152 additions & 5 deletions test/helpers/noisydgrambio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -42,30 +60,146 @@ 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;
}
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.
*/
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

/*
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
/*
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
Expand Down
11 changes: 10 additions & 1 deletion test/helpers/quictestlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 37ffd4a

Please sign in to comment.