1*e7be843bSPierre Pronchery /*
2*e7be843bSPierre Pronchery * Copyright 2023-2025 The OpenSSL Project Authors. All Rights Reserved.
3*e7be843bSPierre Pronchery *
4*e7be843bSPierre Pronchery * Licensed under the Apache License 2.0 (the "License"). You may not use
5*e7be843bSPierre Pronchery * this file except in compliance with the License. You can obtain a copy
6*e7be843bSPierre Pronchery * in the file LICENSE in the source distribution or at
7*e7be843bSPierre Pronchery * https://www.openssl.org/source/license.html
8*e7be843bSPierre Pronchery */
9*e7be843bSPierre Pronchery
10*e7be843bSPierre Pronchery #include <openssl/bio.h>
11*e7be843bSPierre Pronchery #include "quictestlib.h"
12*e7be843bSPierre Pronchery #include "../testutil.h"
13*e7be843bSPierre Pronchery
pkt_split_dgram_ctrl(BIO * bio,int cmd,long num,void * ptr)14*e7be843bSPierre Pronchery static long pkt_split_dgram_ctrl(BIO *bio, int cmd, long num, void *ptr)
15*e7be843bSPierre Pronchery {
16*e7be843bSPierre Pronchery long ret;
17*e7be843bSPierre Pronchery BIO *next = BIO_next(bio);
18*e7be843bSPierre Pronchery
19*e7be843bSPierre Pronchery if (next == NULL)
20*e7be843bSPierre Pronchery return 0;
21*e7be843bSPierre Pronchery
22*e7be843bSPierre Pronchery switch (cmd) {
23*e7be843bSPierre Pronchery case BIO_CTRL_DUP:
24*e7be843bSPierre Pronchery ret = 0L;
25*e7be843bSPierre Pronchery break;
26*e7be843bSPierre Pronchery default:
27*e7be843bSPierre Pronchery ret = BIO_ctrl(next, cmd, num, ptr);
28*e7be843bSPierre Pronchery break;
29*e7be843bSPierre Pronchery }
30*e7be843bSPierre Pronchery return ret;
31*e7be843bSPierre Pronchery }
32*e7be843bSPierre Pronchery
pkt_split_dgram_sendmmsg(BIO * bio,BIO_MSG * msg,size_t stride,size_t num_msg,uint64_t flags,size_t * msgs_processed)33*e7be843bSPierre Pronchery static int pkt_split_dgram_sendmmsg(BIO *bio, BIO_MSG *msg, size_t stride,
34*e7be843bSPierre Pronchery size_t num_msg, uint64_t flags,
35*e7be843bSPierre Pronchery size_t *msgs_processed)
36*e7be843bSPierre Pronchery {
37*e7be843bSPierre Pronchery BIO *next = BIO_next(bio);
38*e7be843bSPierre Pronchery
39*e7be843bSPierre Pronchery if (next == NULL)
40*e7be843bSPierre Pronchery return 0;
41*e7be843bSPierre Pronchery
42*e7be843bSPierre Pronchery /*
43*e7be843bSPierre Pronchery * We only introduce noise when receiving messages. We just pass this on
44*e7be843bSPierre Pronchery * to the underlying BIO.
45*e7be843bSPierre Pronchery */
46*e7be843bSPierre Pronchery return BIO_sendmmsg(next, msg, stride, num_msg, flags, msgs_processed);
47*e7be843bSPierre Pronchery }
48*e7be843bSPierre Pronchery
pkt_split_dgram_recvmmsg(BIO * bio,BIO_MSG * msg,size_t stride,size_t num_msg,uint64_t flags,size_t * msgs_processed)49*e7be843bSPierre Pronchery static int pkt_split_dgram_recvmmsg(BIO *bio, BIO_MSG *msg, size_t stride,
50*e7be843bSPierre Pronchery size_t num_msg, uint64_t flags,
51*e7be843bSPierre Pronchery size_t *msgs_processed)
52*e7be843bSPierre Pronchery {
53*e7be843bSPierre Pronchery BIO *next = BIO_next(bio);
54*e7be843bSPierre Pronchery size_t i, j, data_len = 0, msg_cnt = 0;
55*e7be843bSPierre Pronchery BIO_MSG *thismsg;
56*e7be843bSPierre Pronchery QTEST_DATA *bdata = BIO_get_data(bio);
57*e7be843bSPierre Pronchery
58*e7be843bSPierre Pronchery if (!TEST_ptr(next) || !TEST_ptr(bdata))
59*e7be843bSPierre Pronchery return 0;
60*e7be843bSPierre Pronchery
61*e7be843bSPierre Pronchery /*
62*e7be843bSPierre Pronchery * For simplicity we assume that all elements in the msg array have the
63*e7be843bSPierre Pronchery * same data_len. They are not required to by the API, but it would be quite
64*e7be843bSPierre Pronchery * strange for that not to be the case - and our code that calls
65*e7be843bSPierre Pronchery * BIO_recvmmsg does do this (which is all that is important for this test
66*e7be843bSPierre Pronchery * code). We test the invariant here.
67*e7be843bSPierre Pronchery */
68*e7be843bSPierre Pronchery for (i = 0; i < num_msg; i++) {
69*e7be843bSPierre Pronchery if (i == 0)
70*e7be843bSPierre Pronchery data_len = msg[i].data_len;
71*e7be843bSPierre Pronchery else if (!TEST_size_t_eq(msg[i].data_len, data_len))
72*e7be843bSPierre Pronchery return 0;
73*e7be843bSPierre Pronchery }
74*e7be843bSPierre Pronchery
75*e7be843bSPierre Pronchery if (!BIO_recvmmsg(next, msg, stride, num_msg, flags, msgs_processed))
76*e7be843bSPierre Pronchery return 0;
77*e7be843bSPierre Pronchery
78*e7be843bSPierre Pronchery msg_cnt = *msgs_processed;
79*e7be843bSPierre Pronchery if (msg_cnt == num_msg)
80*e7be843bSPierre Pronchery return 1; /* We've used all our slots and can't split any more */
81*e7be843bSPierre Pronchery assert(msg_cnt < num_msg);
82*e7be843bSPierre Pronchery
83*e7be843bSPierre Pronchery for (i = 0, thismsg = msg; i < msg_cnt; i++, thismsg++) {
84*e7be843bSPierre Pronchery QUIC_PKT_HDR hdr;
85*e7be843bSPierre Pronchery PACKET pkt;
86*e7be843bSPierre Pronchery size_t remain;
87*e7be843bSPierre Pronchery
88*e7be843bSPierre Pronchery if (!PACKET_buf_init(&pkt, thismsg->data, thismsg->data_len))
89*e7be843bSPierre Pronchery return 0;
90*e7be843bSPierre Pronchery
91*e7be843bSPierre Pronchery /* Decode the packet header */
92*e7be843bSPierre Pronchery if (ossl_quic_wire_decode_pkt_hdr(&pkt, bdata->short_conn_id_len,
93*e7be843bSPierre Pronchery 0, 0, &hdr, NULL, NULL) != 1)
94*e7be843bSPierre Pronchery return 0;
95*e7be843bSPierre Pronchery remain = PACKET_remaining(&pkt);
96*e7be843bSPierre Pronchery if (remain > 0) {
97*e7be843bSPierre Pronchery for (j = msg_cnt; j > i; j--) {
98*e7be843bSPierre Pronchery if (!bio_msg_copy(&msg[j], &msg[j - 1]))
99*e7be843bSPierre Pronchery return 0;
100*e7be843bSPierre Pronchery }
101*e7be843bSPierre Pronchery thismsg->data_len -= remain;
102*e7be843bSPierre Pronchery msg[i + 1].data_len = remain;
103*e7be843bSPierre Pronchery memmove(msg[i + 1].data,
104*e7be843bSPierre Pronchery (unsigned char *)msg[i + 1].data + thismsg->data_len,
105*e7be843bSPierre Pronchery remain);
106*e7be843bSPierre Pronchery msg_cnt++;
107*e7be843bSPierre Pronchery }
108*e7be843bSPierre Pronchery }
109*e7be843bSPierre Pronchery
110*e7be843bSPierre Pronchery *msgs_processed = msg_cnt;
111*e7be843bSPierre Pronchery return 1;
112*e7be843bSPierre Pronchery }
113*e7be843bSPierre Pronchery
114*e7be843bSPierre Pronchery /* Choose a sufficiently large type likely to be unused for this custom BIO */
115*e7be843bSPierre Pronchery #define BIO_TYPE_PKT_SPLIT_DGRAM_FILTER (0x81 | BIO_TYPE_FILTER)
116*e7be843bSPierre Pronchery
117*e7be843bSPierre Pronchery static BIO_METHOD *method_pkt_split_dgram = NULL;
118*e7be843bSPierre Pronchery
119*e7be843bSPierre Pronchery /* Note: Not thread safe! */
bio_f_pkt_split_dgram_filter(void)120*e7be843bSPierre Pronchery const BIO_METHOD *bio_f_pkt_split_dgram_filter(void)
121*e7be843bSPierre Pronchery {
122*e7be843bSPierre Pronchery if (method_pkt_split_dgram == NULL) {
123*e7be843bSPierre Pronchery method_pkt_split_dgram = BIO_meth_new(BIO_TYPE_PKT_SPLIT_DGRAM_FILTER,
124*e7be843bSPierre Pronchery "Packet splitting datagram filter");
125*e7be843bSPierre Pronchery if (method_pkt_split_dgram == NULL
126*e7be843bSPierre Pronchery || !BIO_meth_set_ctrl(method_pkt_split_dgram, pkt_split_dgram_ctrl)
127*e7be843bSPierre Pronchery || !BIO_meth_set_sendmmsg(method_pkt_split_dgram,
128*e7be843bSPierre Pronchery pkt_split_dgram_sendmmsg)
129*e7be843bSPierre Pronchery || !BIO_meth_set_recvmmsg(method_pkt_split_dgram,
130*e7be843bSPierre Pronchery pkt_split_dgram_recvmmsg))
131*e7be843bSPierre Pronchery return NULL;
132*e7be843bSPierre Pronchery }
133*e7be843bSPierre Pronchery return method_pkt_split_dgram;
134*e7be843bSPierre Pronchery }
135*e7be843bSPierre Pronchery
bio_f_pkt_split_dgram_filter_free(void)136*e7be843bSPierre Pronchery void bio_f_pkt_split_dgram_filter_free(void)
137*e7be843bSPierre Pronchery {
138*e7be843bSPierre Pronchery BIO_meth_free(method_pkt_split_dgram);
139*e7be843bSPierre Pronchery }
140