1 1.1 christos /* 2 1.1 christos * Copyright 2023-2025 The OpenSSL Project Authors. All Rights Reserved. 3 1.1 christos * 4 1.1 christos * Licensed under the Apache License 2.0 (the "License"). You may not use 5 1.1 christos * this file except in compliance with the License. You can obtain a copy 6 1.1 christos * in the file LICENSE in the source distribution or at 7 1.1 christos * https://www.openssl.org/source/license.html 8 1.1 christos */ 9 1.1 christos 10 1.1 christos #include <openssl/bio.h> 11 1.1 christos #include "quictestlib.h" 12 1.1 christos #include "../testutil.h" 13 1.1 christos 14 1.1.1.2 christos #define MSG_DATA_LEN_MAX 1472 15 1.1 christos #define SAMPLING_WINDOW_PERIOD 10 /* in milliseconds */ 16 1.1 christos #define MAX_PKTS_PER_WINDOW 1024 17 1.1 christos 18 1.1 christos struct pkt_info_st { 19 1.1 christos size_t size; 20 1.1 christos OSSL_TIME timestamp; 21 1.1 christos }; 22 1.1 christos 23 1.1 christos struct bw_limiter_st { 24 1.1 christos struct pkt_info_st pinfos[MAX_PKTS_PER_WINDOW]; /* ring buffer */ 25 1.1.1.2 christos size_t start, num; /* ring buffer start and number of items */ 26 1.1.1.2 christos size_t size_sum; /* sum of packet sizes in window */ 27 1.1.1.2 christos size_t bw; /* bandwidth in bytes/ms */ 28 1.1 christos }; 29 1.1 christos 30 1.1 christos struct noisy_dgram_st { 31 1.1 christos uint64_t this_dgram; 32 1.1 christos BIO_MSG msg; 33 1.1 christos uint64_t reinject_dgram; 34 1.1 christos int backoff; 35 1.1.1.2 christos int noise_rate; /* 1 in noise_rate packets will get noise */ 36 1.1 christos struct bw_limiter_st recv_limit, send_limit; 37 1.1 christos OSSL_TIME (*now_cb)(void *arg); 38 1.1 christos void *now_cb_arg; 39 1.1 christos }; 40 1.1 christos 41 1.1 christos static long noisy_dgram_ctrl(BIO *bio, int cmd, long num, void *ptr) 42 1.1 christos { 43 1.1 christos long ret; 44 1.1 christos BIO *next = BIO_next(bio); 45 1.1 christos 46 1.1 christos if (next == NULL) 47 1.1 christos return 0; 48 1.1 christos 49 1.1 christos switch (cmd) { 50 1.1 christos case BIO_CTRL_DUP: 51 1.1 christos ret = 0L; 52 1.1 christos break; 53 1.1 christos case BIO_CTRL_NOISE_BACK_OFF: { 54 1.1.1.2 christos struct noisy_dgram_st *data; 55 1.1 christos 56 1.1.1.2 christos data = BIO_get_data(bio); 57 1.1.1.2 christos if (!TEST_ptr(data)) 58 1.1.1.2 christos return 0; 59 1.1.1.2 christos data->backoff = (int)num; 60 1.1.1.2 christos ret = 1; 61 1.1.1.2 christos break; 62 1.1.1.2 christos } 63 1.1 christos case BIO_CTRL_NOISE_RATE: { 64 1.1.1.2 christos struct noisy_dgram_st *data; 65 1.1 christos 66 1.1.1.2 christos data = BIO_get_data(bio); 67 1.1.1.2 christos if (!TEST_ptr(data)) 68 1.1.1.2 christos return 0; 69 1.1.1.2 christos data->noise_rate = (int)num; 70 1.1.1.2 christos ret = 1; 71 1.1.1.2 christos break; 72 1.1.1.2 christos } 73 1.1 christos case BIO_CTRL_NOISE_RECV_BANDWIDTH: { 74 1.1.1.2 christos struct noisy_dgram_st *data; 75 1.1 christos 76 1.1.1.2 christos data = BIO_get_data(bio); 77 1.1.1.2 christos if (!TEST_ptr(data)) 78 1.1.1.2 christos return 0; 79 1.1.1.2 christos data->recv_limit.bw = (size_t)num; 80 1.1.1.2 christos ret = 1; 81 1.1.1.2 christos break; 82 1.1.1.2 christos } 83 1.1 christos case BIO_CTRL_NOISE_SEND_BANDWIDTH: { 84 1.1.1.2 christos struct noisy_dgram_st *data; 85 1.1 christos 86 1.1.1.2 christos data = BIO_get_data(bio); 87 1.1.1.2 christos if (!TEST_ptr(data)) 88 1.1.1.2 christos return 0; 89 1.1.1.2 christos data->send_limit.bw = (size_t)num; 90 1.1.1.2 christos ret = 1; 91 1.1.1.2 christos break; 92 1.1.1.2 christos } 93 1.1 christos case BIO_CTRL_NOISE_SET_NOW_CB: { 94 1.1.1.2 christos struct noisy_dgram_st *data; 95 1.1.1.2 christos struct bio_noise_now_cb_st *now_cb = ptr; 96 1.1 christos 97 1.1.1.2 christos data = BIO_get_data(bio); 98 1.1.1.2 christos if (!TEST_ptr(data)) 99 1.1.1.2 christos return 0; 100 1.1.1.2 christos data->now_cb = now_cb->now_cb; 101 1.1.1.2 christos data->now_cb_arg = now_cb->now_cb_arg; 102 1.1.1.2 christos ret = 1; 103 1.1.1.2 christos break; 104 1.1.1.2 christos } 105 1.1 christos default: 106 1.1 christos ret = BIO_ctrl(next, cmd, num, ptr); 107 1.1 christos break; 108 1.1 christos } 109 1.1 christos return ret; 110 1.1 christos } 111 1.1 christos 112 1.1 christos static size_t bandwidth_limit(struct bw_limiter_st *limit, OSSL_TIME now, 113 1.1.1.2 christos BIO_MSG *msg, size_t num_msg) 114 1.1 christos { 115 1.1 christos size_t i; 116 1.1 christos OSSL_TIME sampling_start 117 1.1 christos = ossl_time_subtract(now, ossl_ms2time(SAMPLING_WINDOW_PERIOD)); 118 1.1 christos 119 1.1 christos if (limit->bw == 0) /* 0 -> no limit */ 120 1.1 christos return num_msg; 121 1.1 christos 122 1.1 christos if (num_msg > MAX_PKTS_PER_WINDOW) 123 1.1 christos num_msg = MAX_PKTS_PER_WINDOW; 124 1.1 christos 125 1.1 christos /* trim the start of the ring buffer */ 126 1.1 christos for (i = 0; i < limit->num; i++) { 127 1.1 christos size_t idx = (limit->start + i) % MAX_PKTS_PER_WINDOW; 128 1.1 christos 129 1.1 christos if (ossl_time_compare(limit->pinfos[idx].timestamp, sampling_start) >= 0) 130 1.1 christos break; 131 1.1 christos limit->size_sum -= limit->pinfos[idx].size; 132 1.1 christos } 133 1.1 christos limit->start = (limit->start + i) % MAX_PKTS_PER_WINDOW; 134 1.1 christos limit->num -= i; 135 1.1 christos 136 1.1 christos for (i = 0; i < num_msg; ++i) { 137 1.1.1.2 christos size_t end; 138 1.1.1.2 christos size_t pktsize = msg[i].data_len; 139 1.1 christos 140 1.1.1.2 christos if ((limit->size_sum + pktsize) / SAMPLING_WINDOW_PERIOD > limit->bw) { 141 1.1.1.2 christos /* 142 1.1.1.2 christos * Throw out all the packets once reaching the limit, 143 1.1.1.2 christos * although some following packets could still fit. 144 1.1.1.2 christos * This is accurate enough. 145 1.1.1.2 christos */ 146 1.1 christos #ifdef OSSL_NOISY_DGRAM_DEBUG 147 1.1.1.2 christos printf("**BW limit applied: now: %llu orig packets %u new packets %u\n", 148 1.1.1.2 christos (unsigned long long)ossl_time2ms(now), 149 1.1.1.2 christos (unsigned int)num_msg, (unsigned int)i); 150 1.1 christos #endif 151 1.1.1.2 christos num_msg = i; 152 1.1.1.2 christos break; 153 1.1.1.2 christos } 154 1.1.1.2 christos 155 1.1.1.2 christos if (limit->num >= MAX_PKTS_PER_WINDOW) { 156 1.1.1.2 christos limit->size_sum -= limit->pinfos[limit->start].size; 157 1.1.1.2 christos limit->start = (limit->start + 1) % MAX_PKTS_PER_WINDOW; 158 1.1.1.2 christos } else { 159 1.1.1.2 christos ++limit->num; 160 1.1.1.2 christos } 161 1.1.1.2 christos end = (limit->start + limit->num) % MAX_PKTS_PER_WINDOW; 162 1.1.1.2 christos limit->pinfos[end].size = pktsize; 163 1.1.1.2 christos limit->pinfos[end].timestamp = now; 164 1.1.1.2 christos limit->size_sum += pktsize; 165 1.1 christos } 166 1.1 christos return num_msg; 167 1.1 christos } 168 1.1 christos 169 1.1 christos static int noisy_dgram_sendmmsg(BIO *bio, BIO_MSG *msg, size_t stride, 170 1.1.1.2 christos size_t num_msg, uint64_t flags, 171 1.1.1.2 christos size_t *msgs_processed) 172 1.1 christos { 173 1.1 christos BIO *next = BIO_next(bio); 174 1.1 christos struct noisy_dgram_st *data; 175 1.1 christos OSSL_TIME now; 176 1.1 christos 177 1.1 christos if (next == NULL) 178 1.1 christos return 0; 179 1.1 christos 180 1.1 christos data = BIO_get_data(bio); 181 1.1 christos if (!TEST_ptr(data)) 182 1.1 christos return 0; 183 1.1 christos 184 1.1 christos now = data->now_cb != NULL ? data->now_cb(data->now_cb_arg) 185 1.1 christos : ossl_time_now(); 186 1.1 christos 187 1.1 christos /* bandwidth limit can be applied on both sides */ 188 1.1 christos num_msg = bandwidth_limit(&data->send_limit, now, msg, num_msg); 189 1.1 christos if (num_msg == 0) { 190 1.1 christos *msgs_processed = 0; 191 1.1 christos ERR_raise(ERR_LIB_BIO, BIO_R_NON_FATAL); 192 1.1 christos return 0; 193 1.1 christos } 194 1.1 christos 195 1.1 christos /* 196 1.1 christos * We only introduce noise when receiving messages. We just pass this on 197 1.1 christos * to the underlying BIO. 198 1.1 christos */ 199 1.1 christos return BIO_sendmmsg(next, msg, stride, num_msg, flags, msgs_processed); 200 1.1 christos } 201 1.1 christos 202 1.1 christos /* Default noise_rate value. With a value of 5 that is 20% packets. */ 203 1.1.1.2 christos #define NOISE_RATE 5 204 1.1 christos 205 1.1 christos /* 206 1.1 christos * We have 3 different types of noise: drop, duplicate and delay 207 1.1 christos * Each of these have equal probability. 208 1.1 christos */ 209 1.1.1.2 christos #define NOISE_TYPE_DROP 0 210 1.1 christos #define NOISE_TYPE_DUPLICATE 1 211 1.1.1.2 christos #define NOISE_TYPE_DELAY 2 212 1.1.1.2 christos #define NOISE_TYPE_BITFLIPS 3 213 1.1.1.2 christos #define NUM_NOISE_TYPES 4 214 1.1 christos 215 1.1 christos /* 216 1.1 christos * When a duplicate occurs we reinject the new datagram after up to 217 1.1 christos * MAX_DGRAM_REINJECT datagrams have been sent. A reinject of 1 means that the 218 1.1 christos * duplicate follows immediately after the original datagram. A reinject of 4 219 1.1 christos * means that original datagram plus 3 other datagrams are sent before the 220 1.1 christos * reinjected datagram is inserted. 221 1.1 christos * This also controls when a delay (not a duplicate) occurs. In that case 222 1.1 christos * we add 1 to the number because there is no point in skipping the current 223 1.1 christos * datagram only to immediately reinject it in the next datagram. 224 1.1 christos */ 225 1.1 christos #define MAX_DGRAM_REINJECT 4 226 1.1 christos 227 1.1 christos static void get_noise(int noise_rate, int long_header, uint64_t *reinject, 228 1.1.1.2 christos int *should_drop, uint16_t *flip, size_t *flip_offset) 229 1.1 christos { 230 1.1 christos uint32_t type; 231 1.1 christos 232 1.1 christos *flip = 0; 233 1.1 christos 234 1.1 christos if (test_random() % noise_rate != 0) { 235 1.1 christos *reinject = 0; 236 1.1 christos *should_drop = 0; 237 1.1 christos return; 238 1.1 christos } 239 1.1 christos 240 1.1 christos type = test_random() % NUM_NOISE_TYPES; 241 1.1 christos 242 1.1 christos /* 243 1.1 christos * Of noisy datagrams, 25% drop, 25% duplicate, 25% delay, 25% flip bits 244 1.1 christos * A duplicated datagram keeps the current datagram and reinjects a new 245 1.1 christos * identical one after up to MAX_DGRAM_DELAY datagrams have been sent. 246 1.1 christos * A delayed datagram is implemented as both a reinject and a drop, i.e. an 247 1.1 christos * identical datagram is reinjected after the given number of datagrams have 248 1.1 christos * been sent and the current datagram is dropped. 249 1.1 christos */ 250 1.1 christos *should_drop = (type == NOISE_TYPE_DROP || type == NOISE_TYPE_DELAY); 251 1.1 christos 252 1.1 christos /* 253 1.1 christos * Where a duplicate occurs we reinject the copy of the datagram up to 254 1.1 christos * MAX_DGRAM_DELAY datagrams later 255 1.1 christos */ 256 1.1 christos *reinject = (type == NOISE_TYPE_DUPLICATE || type == NOISE_TYPE_DELAY) 257 1.1.1.2 christos ? (uint64_t)((test_random() % MAX_DGRAM_REINJECT) + 1) 258 1.1.1.2 christos : 0; 259 1.1 christos 260 1.1 christos /* 261 1.1 christos * No point in reinjecting after 1 datagram if the current datagram is also 262 1.1 christos * dropped (i.e. this is a delay not a duplicate), so we reinject after an 263 1.1 christos * extra datagram in that case 264 1.1 christos */ 265 1.1 christos *reinject += type == NOISE_TYPE_DELAY; 266 1.1 christos 267 1.1 christos /* flip some bits in the header */ 268 1.1 christos if (type == NOISE_TYPE_BITFLIPS) { 269 1.1 christos /* we flip at most 8 bits of the 16 bit value at once */ 270 1.1 christos *flip = (test_random() % 255 + 1) << (test_random() % 8); 271 1.1 christos /* 272 1.1 christos * 25/50 bytes of guesstimated header size (it depends on CID length) 273 1.1 christos * It does not matter much if it is overestimated. 274 1.1 christos */ 275 1.1 christos *flip_offset = test_random() % (25 * (1 + long_header)); 276 1.1 christos } 277 1.1 christos } 278 1.1 christos 279 1.1 christos static void flip_bits(unsigned char *msg, size_t msg_len, uint16_t flip, 280 1.1.1.2 christos size_t flip_offset) 281 1.1 christos { 282 1.1 christos if (flip == 0) 283 1.1 christos return; 284 1.1 christos 285 1.1 christos /* None of these border conditions should happen but check them anyway */ 286 1.1 christos if (msg_len < 2) 287 1.1 christos return; 288 1.1 christos if (msg_len < flip_offset + 2) 289 1.1 christos flip_offset = msg_len - 2; 290 1.1 christos 291 1.1 christos #ifdef OSSL_NOISY_DGRAM_DEBUG 292 1.1 christos printf("**Flipping bits in a datagram at offset %u\n", 293 1.1.1.2 christos (unsigned int)flip_offset); 294 1.1 christos BIO_dump_fp(stdout, msg, msg_len); 295 1.1 christos printf("\n"); 296 1.1 christos #endif 297 1.1 christos 298 1.1 christos msg[flip_offset] ^= flip >> 8; 299 1.1 christos msg[flip_offset + 1] ^= flip & 0xff; 300 1.1 christos } 301 1.1 christos 302 1.1 christos static int noisy_dgram_recvmmsg(BIO *bio, BIO_MSG *msg, size_t stride, 303 1.1.1.2 christos size_t num_msg, uint64_t flags, 304 1.1.1.2 christos size_t *msgs_processed) 305 1.1 christos { 306 1.1 christos BIO *next = BIO_next(bio); 307 1.1 christos size_t i, j, data_len = 0, msg_cnt = 0; 308 1.1 christos BIO_MSG *thismsg; 309 1.1 christos struct noisy_dgram_st *data; 310 1.1 christos OSSL_TIME now; 311 1.1 christos 312 1.1 christos if (!TEST_ptr(next)) 313 1.1 christos return 0; 314 1.1 christos 315 1.1 christos data = BIO_get_data(bio); 316 1.1 christos if (!TEST_ptr(data)) 317 1.1 christos return 0; 318 1.1 christos 319 1.1 christos /* 320 1.1 christos * For simplicity we assume that all elements in the msg array have the 321 1.1 christos * same data_len. They are not required to by the API, but it would be quite 322 1.1 christos * strange for that not to be the case - and our code that calls 323 1.1 christos * BIO_recvmmsg does do this (which is all that is important for this test 324 1.1 christos * code). We test the invariant here. 325 1.1 christos */ 326 1.1 christos for (i = 0; i < num_msg; i++) { 327 1.1 christos if (i == 0) { 328 1.1 christos data_len = msg[i].data_len; 329 1.1 christos if (!TEST_size_t_le(data_len, MSG_DATA_LEN_MAX)) 330 1.1 christos return 0; 331 1.1 christos } else if (!TEST_size_t_eq(msg[i].data_len, data_len)) { 332 1.1 christos return 0; 333 1.1 christos } 334 1.1 christos } 335 1.1 christos 336 1.1 christos if (!BIO_recvmmsg(next, msg, stride, num_msg, flags, msgs_processed)) 337 1.1 christos return 0; 338 1.1 christos 339 1.1 christos #ifdef OSSL_NOISY_DGRAM_DEBUG 340 1.1 christos printf("Pre-filter datagram list:\n"); 341 1.1 christos for (i = 0; i < *msgs_processed; i++) { 342 1.1 christos printf("Pre-filter Datagram:\n"); 343 1.1 christos BIO_dump_fp(stdout, msg[i].data, msg[i].data_len); 344 1.1 christos printf("\n"); 345 1.1 christos } 346 1.1 christos printf("End of pre-filter datagram list\nApplying noise filters:\n"); 347 1.1 christos #endif 348 1.1 christos 349 1.1 christos now = data->now_cb != NULL ? data->now_cb(data->now_cb_arg) 350 1.1 christos : ossl_time_now(); 351 1.1 christos 352 1.1 christos msg_cnt = *msgs_processed; 353 1.1 christos msg_cnt = bandwidth_limit(&data->recv_limit, now, msg, msg_cnt); 354 1.1 christos if (msg_cnt == 0) 355 1.1 christos goto end; 356 1.1 christos 357 1.1 christos if (data->noise_rate == 0) 358 1.1 christos goto end; 359 1.1 christos 360 1.1 christos /* Introduce noise */ 361 1.1 christos for (i = 0, thismsg = msg; 362 1.1.1.2 christos i < msg_cnt; 363 1.1.1.2 christos i++, thismsg++, data->this_dgram++) { 364 1.1 christos uint64_t reinject; 365 1.1 christos int should_drop; 366 1.1 christos uint16_t flip = 0; 367 1.1 christos size_t flip_offset = 0; 368 1.1 christos 369 1.1 christos /* If we have a message to reinject then insert it now */ 370 1.1 christos if (data->reinject_dgram > 0 371 1.1.1.2 christos && data->reinject_dgram == data->this_dgram) { 372 1.1 christos if (msg_cnt < num_msg) { 373 1.1 christos /* Make space for the injected message */ 374 1.1 christos for (j = msg_cnt; j > i; j--) { 375 1.1 christos if (!bio_msg_copy(&msg[j], &msg[j - 1])) 376 1.1 christos return 0; 377 1.1 christos } 378 1.1 christos if (!bio_msg_copy(thismsg, &data->msg)) 379 1.1 christos return 0; 380 1.1 christos msg_cnt++; 381 1.1 christos data->reinject_dgram = 0; 382 1.1 christos #ifdef OSSL_NOISY_DGRAM_DEBUG 383 1.1 christos printf("**Injecting a datagram\n"); 384 1.1 christos BIO_dump_fp(stdout, thismsg->data, thismsg->data_len); 385 1.1 christos printf("\n"); 386 1.1 christos #endif 387 1.1 christos continue; 388 1.1 christos } /* else we have no space for the injection, so just drop it */ 389 1.1 christos data->reinject_dgram = 0; 390 1.1 christos } 391 1.1 christos 392 1.1 christos get_noise(data->noise_rate, 393 1.1.1.2 christos /* long header */ (((uint8_t *)thismsg->data)[0] & 0x80) != 0, 394 1.1.1.2 christos &reinject, &should_drop, &flip, &flip_offset); 395 1.1 christos if (data->backoff) { 396 1.1 christos /* 397 1.1 christos * We might be asked to back off on introducing too much noise if 398 1.1 christos * there is a danger that the connection will fail. In that case 399 1.1 christos * we always ensure that the next datagram does not get dropped so 400 1.1 christos * that the connection always survives. After that we can resume 401 1.1 christos * with normal noise 402 1.1 christos * Note that the backoff value is configurable via BIO ctrl, 403 1.1 christos * allowing for multiframe backoff. 404 1.1 christos */ 405 1.1 christos #ifdef OSSL_NOISY_DGRAM_DEBUG 406 1.1 christos printf("**Back off applied\n"); 407 1.1 christos #endif 408 1.1 christos should_drop = 0; 409 1.1 christos flip = 0; 410 1.1 christos data->backoff--; 411 1.1 christos } 412 1.1 christos 413 1.1 christos flip_bits(thismsg->data, thismsg->data_len, flip, flip_offset); 414 1.1 christos 415 1.1 christos /* 416 1.1 christos * We ignore reinjection if a message is already waiting to be 417 1.1 christos * reinjected 418 1.1 christos */ 419 1.1 christos if (reinject > 0 && data->reinject_dgram == 0) { 420 1.1 christos /* 421 1.1 christos * Both duplicated and delayed datagrams get reintroduced after the 422 1.1 christos * delay period. Datagrams that are delayed only (not duplicated) 423 1.1 christos * will also have the current copy of the datagram dropped (i.e 424 1.1 christos * should_drop below will be true). 425 1.1 christos */ 426 1.1 christos if (!bio_msg_copy(&data->msg, thismsg)) 427 1.1 christos return 0; 428 1.1 christos 429 1.1 christos data->reinject_dgram = data->this_dgram + reinject; 430 1.1 christos 431 1.1 christos #ifdef OSSL_NOISY_DGRAM_DEBUG 432 1.1 christos printf("**Scheduling a reinject after %u messages%s\n", 433 1.1.1.2 christos (unsigned int)reinject, should_drop ? "" : "(duplicating)"); 434 1.1 christos BIO_dump_fp(stdout, thismsg->data, thismsg->data_len); 435 1.1 christos printf("\n"); 436 1.1 christos #endif 437 1.1 christos } 438 1.1 christos 439 1.1 christos if (should_drop) { 440 1.1 christos #ifdef OSSL_NOISY_DGRAM_DEBUG 441 1.1 christos printf("**Dropping a datagram\n"); 442 1.1 christos BIO_dump_fp(stdout, thismsg->data, thismsg->data_len); 443 1.1 christos printf("\n"); 444 1.1 christos #endif 445 1.1 christos for (j = i + 1; j < msg_cnt; j++) { 446 1.1 christos if (!bio_msg_copy(&msg[j - 1], &msg[j])) 447 1.1 christos return 0; 448 1.1 christos } 449 1.1 christos msg_cnt--; 450 1.1 christos } 451 1.1 christos } 452 1.1 christos 453 1.1 christos #ifdef OSSL_NOISY_DGRAM_DEBUG 454 1.1 christos printf("End of noise filters\nPost-filter datagram list:\n"); 455 1.1 christos for (i = 0; i < msg_cnt; i++) { 456 1.1 christos printf("Post-filter Datagram:\n"); 457 1.1 christos BIO_dump_fp(stdout, msg[i].data, msg[i].data_len); 458 1.1 christos printf("\n"); 459 1.1 christos } 460 1.1 christos printf("End of post-filter datagram list\n"); 461 1.1 christos #endif 462 1.1 christos 463 1.1.1.2 christos end: 464 1.1 christos *msgs_processed = msg_cnt; 465 1.1 christos 466 1.1 christos if (msg_cnt == 0) { 467 1.1 christos ERR_raise(ERR_LIB_BIO, BIO_R_NON_FATAL); 468 1.1 christos return 0; 469 1.1 christos } 470 1.1 christos 471 1.1 christos return 1; 472 1.1 christos } 473 1.1 christos 474 1.1 christos static void data_free(struct noisy_dgram_st *data) 475 1.1 christos { 476 1.1 christos if (data == NULL) 477 1.1 christos return; 478 1.1 christos 479 1.1 christos OPENSSL_free(data->msg.data); 480 1.1 christos BIO_ADDR_free(data->msg.peer); 481 1.1 christos BIO_ADDR_free(data->msg.local); 482 1.1 christos OPENSSL_free(data); 483 1.1 christos } 484 1.1 christos 485 1.1 christos static int noisy_dgram_new(BIO *bio) 486 1.1 christos { 487 1.1 christos struct noisy_dgram_st *data = OPENSSL_zalloc(sizeof(*data)); 488 1.1 christos 489 1.1 christos if (!TEST_ptr(data)) 490 1.1 christos return 0; 491 1.1 christos 492 1.1 christos data->noise_rate = NOISE_RATE; 493 1.1 christos data->msg.data = OPENSSL_malloc(MSG_DATA_LEN_MAX); 494 1.1 christos data->msg.peer = BIO_ADDR_new(); 495 1.1 christos data->msg.local = BIO_ADDR_new(); 496 1.1 christos if (data->msg.data == NULL 497 1.1.1.2 christos || data->msg.peer == NULL 498 1.1.1.2 christos || data->msg.local == NULL) { 499 1.1 christos data_free(data); 500 1.1 christos return 0; 501 1.1 christos } 502 1.1 christos 503 1.1 christos BIO_set_data(bio, data); 504 1.1 christos BIO_set_init(bio, 1); 505 1.1 christos 506 1.1 christos return 1; 507 1.1 christos } 508 1.1 christos 509 1.1 christos static int noisy_dgram_free(BIO *bio) 510 1.1 christos { 511 1.1 christos data_free(BIO_get_data(bio)); 512 1.1 christos BIO_set_data(bio, NULL); 513 1.1 christos BIO_set_init(bio, 0); 514 1.1 christos 515 1.1 christos return 1; 516 1.1 christos } 517 1.1 christos 518 1.1 christos /* Choose a sufficiently large type likely to be unused for this custom BIO */ 519 1.1.1.2 christos #define BIO_TYPE_NOISY_DGRAM_FILTER (0x80 | BIO_TYPE_FILTER) 520 1.1 christos 521 1.1 christos static BIO_METHOD *method_noisy_dgram = NULL; 522 1.1 christos 523 1.1 christos /* Note: Not thread safe! */ 524 1.1 christos const BIO_METHOD *bio_f_noisy_dgram_filter(void) 525 1.1 christos { 526 1.1 christos if (method_noisy_dgram == NULL) { 527 1.1 christos method_noisy_dgram = BIO_meth_new(BIO_TYPE_NOISY_DGRAM_FILTER, 528 1.1.1.2 christos "Noisy datagram filter"); 529 1.1 christos if (method_noisy_dgram == NULL 530 1.1 christos || !BIO_meth_set_ctrl(method_noisy_dgram, noisy_dgram_ctrl) 531 1.1 christos || !BIO_meth_set_sendmmsg(method_noisy_dgram, noisy_dgram_sendmmsg) 532 1.1 christos || !BIO_meth_set_recvmmsg(method_noisy_dgram, noisy_dgram_recvmmsg) 533 1.1 christos || !BIO_meth_set_create(method_noisy_dgram, noisy_dgram_new) 534 1.1 christos || !BIO_meth_set_destroy(method_noisy_dgram, noisy_dgram_free)) 535 1.1 christos return NULL; 536 1.1 christos } 537 1.1 christos return method_noisy_dgram; 538 1.1 christos } 539 1.1 christos 540 1.1 christos void bio_f_noisy_dgram_filter_free(void) 541 1.1 christos { 542 1.1 christos BIO_meth_free(method_noisy_dgram); 543 1.1 christos } 544