/src/openssl/include/internal/quic_reactor.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2022-2024 The OpenSSL Project Authors. All Rights Reserved. |
3 | | * |
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 |
8 | | */ |
9 | | #ifndef OSSL_QUIC_REACTOR_H |
10 | | # define OSSL_QUIC_REACTOR_H |
11 | | |
12 | | # include "internal/time.h" |
13 | | # include "internal/sockets.h" |
14 | | # include "internal/quic_predef.h" |
15 | | # include "internal/thread_arch.h" |
16 | | # include <openssl/bio.h> |
17 | | |
18 | | # ifndef OPENSSL_NO_QUIC |
19 | | |
20 | | /* |
21 | | * Core I/O Reactor Framework |
22 | | * ========================== |
23 | | * |
24 | | * Manages use of async network I/O which the QUIC stack is built on. The core |
25 | | * mechanic looks like this: |
26 | | * |
27 | | * - There is a pollable FD for both the read and write side respectively. |
28 | | * Readability and writeability of these FDs respectively determines when |
29 | | * network I/O is available. |
30 | | * |
31 | | * - The reactor can export these FDs to the user, as well as flags indicating |
32 | | * whether the user should listen for readability, writeability, or neither. |
33 | | * |
34 | | * - The reactor can export a timeout indication to the user, indicating when |
35 | | * the reactor should be called (via libssl APIs) regardless of whether |
36 | | * the network socket has become ready. |
37 | | * |
38 | | * The reactor is based around a tick callback which is essentially the mutator |
39 | | * function. The mutator attempts to do whatever it can, attempting to perform |
40 | | * network I/O to the extent currently feasible. When done, the mutator returns |
41 | | * information to the reactor indicating when it should be woken up again: |
42 | | * |
43 | | * - Should it be woken up when network RX is possible? |
44 | | * - Should it be woken up when network TX is possible? |
45 | | * - Should it be woken up no later than some deadline X? |
46 | | * |
47 | | * The intention is that ALL I/O-related SSL_* functions with side effects (e.g. |
48 | | * SSL_read/SSL_write) consist of three phases: |
49 | | * |
50 | | * - Optionally mutate the QUIC machine's state. |
51 | | * - Optionally tick the QUIC reactor. |
52 | | * - Optionally mutate the QUIC machine's state. |
53 | | * |
54 | | * For example, SSL_write is a mutation (appending to a stream buffer) followed |
55 | | * by an optional tick (generally expected as we may want to send the data |
56 | | * immediately, though not strictly needed if transmission is being deferred due |
57 | | * to Nagle's algorithm, etc.). |
58 | | * |
59 | | * SSL_read is also a mutation and in principle does not need to tick the |
60 | | * reactor, but it generally will anyway to ensure that the reactor is regularly |
61 | | * ticked by an application which is only reading and not writing. |
62 | | * |
63 | | * If the SSL object is being used in blocking mode, SSL_read may need to block |
64 | | * if no data is available yet, and SSL_write may need to block if buffers |
65 | | * are full. |
66 | | * |
67 | | * The internals of the QUIC I/O engine always use asynchronous I/O. If the |
68 | | * application desires blocking semantics, we handle this by adding a blocking |
69 | | * adaptation layer on top of our internal asynchronous I/O API as exposed by |
70 | | * the reactor interface. |
71 | | */ |
72 | | struct quic_tick_result_st { |
73 | | char net_read_desired; |
74 | | char net_write_desired; |
75 | | OSSL_TIME tick_deadline; |
76 | | }; |
77 | | |
78 | | static ossl_inline ossl_unused void |
79 | | ossl_quic_tick_result_merge_into(QUIC_TICK_RESULT *r, |
80 | | const QUIC_TICK_RESULT *src) |
81 | 0 | { |
82 | 0 | r->net_read_desired = r->net_read_desired || src->net_read_desired; |
83 | 0 | r->net_write_desired = r->net_write_desired || src->net_write_desired; |
84 | 0 | r->tick_deadline = ossl_time_min(r->tick_deadline, src->tick_deadline); |
85 | 0 | } Unexecuted instantiation: methods.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: s3_lib.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: s3_msg.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: ssl_cert.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: ssl_ciph.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: ssl_init.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: ssl_lib.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: ssl_mcnf.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: ssl_sess.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: t1_lib.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: tls13_enc.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: tls_depr.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: tls_srp.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_impl.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_method.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_port.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_reactor.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_record_rx.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_record_shared.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_record_tx.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_record_util.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_thread_assist.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: rec_layer_d1.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: rec_layer_s3.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: dtls_meth.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: tls1_meth.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: tls_common.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: tls_multib.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: tlsany_meth.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: extensions.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: extensions_clnt.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: extensions_cust.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: extensions_srvr.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: statem.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: statem_clnt.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: statem_dtls.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: statem_lib.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: statem_srvr.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: d1_lib.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: d1_msg.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: d1_srtp.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: pqueue.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: s3_enc.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: ssl_asn1.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: ssl_conf.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: ssl_rsa.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: t1_enc.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_channel.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_engine.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_rx_depack.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_tls.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: ssl3_meth.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: tls13_meth.c:ossl_quic_tick_result_merge_into |
86 | | |
87 | | struct quic_reactor_st { |
88 | | /* |
89 | | * BIO poll descriptors which can be polled. poll_r is a poll descriptor |
90 | | * which becomes readable when the QUIC state machine can potentially do |
91 | | * work, and poll_w is a poll descriptor which becomes writable when the |
92 | | * QUIC state machine can potentially do work. Generally, either of these |
93 | | * conditions means that SSL_tick() should be called, or another SSL |
94 | | * function which implicitly calls SSL_tick() (e.g. SSL_read/SSL_write()). |
95 | | */ |
96 | | BIO_POLL_DESCRIPTOR poll_r, poll_w; |
97 | | OSSL_TIME tick_deadline; /* ossl_time_infinite() if none currently applicable */ |
98 | | |
99 | | void (*tick_cb)(QUIC_TICK_RESULT *res, void *arg, uint32_t flags); |
100 | | void *tick_cb_arg; |
101 | | |
102 | | /* |
103 | | * These are true if we would like to know when we can read or write from |
104 | | * the network respectively. |
105 | | */ |
106 | | unsigned int net_read_desired : 1; |
107 | | unsigned int net_write_desired : 1; |
108 | | |
109 | | /* |
110 | | * Are the read and write poll descriptors we are currently configured with |
111 | | * things we can actually poll? |
112 | | */ |
113 | | unsigned int can_poll_r : 1; |
114 | | unsigned int can_poll_w : 1; |
115 | | }; |
116 | | |
117 | | void ossl_quic_reactor_init(QUIC_REACTOR *rtor, |
118 | | void (*tick_cb)(QUIC_TICK_RESULT *res, void *arg, |
119 | | uint32_t flags), |
120 | | void *tick_cb_arg, |
121 | | OSSL_TIME initial_tick_deadline); |
122 | | |
123 | | void ossl_quic_reactor_set_poll_r(QUIC_REACTOR *rtor, |
124 | | const BIO_POLL_DESCRIPTOR *r); |
125 | | |
126 | | void ossl_quic_reactor_set_poll_w(QUIC_REACTOR *rtor, |
127 | | const BIO_POLL_DESCRIPTOR *w); |
128 | | |
129 | | const BIO_POLL_DESCRIPTOR *ossl_quic_reactor_get_poll_r(const QUIC_REACTOR *rtor); |
130 | | const BIO_POLL_DESCRIPTOR *ossl_quic_reactor_get_poll_w(const QUIC_REACTOR *rtor); |
131 | | |
132 | | int ossl_quic_reactor_can_poll_r(const QUIC_REACTOR *rtor); |
133 | | int ossl_quic_reactor_can_poll_w(const QUIC_REACTOR *rtor); |
134 | | |
135 | | int ossl_quic_reactor_can_support_poll_descriptor(const QUIC_REACTOR *rtor, |
136 | | const BIO_POLL_DESCRIPTOR *d); |
137 | | |
138 | | int ossl_quic_reactor_net_read_desired(QUIC_REACTOR *rtor); |
139 | | int ossl_quic_reactor_net_write_desired(QUIC_REACTOR *rtor); |
140 | | |
141 | | OSSL_TIME ossl_quic_reactor_get_tick_deadline(QUIC_REACTOR *rtor); |
142 | | |
143 | | /* |
144 | | * Do whatever work can be done, and as much work as can be done. This involves |
145 | | * e.g. seeing if we can read anything from the network (if we want to), seeing |
146 | | * if we can write anything to the network (if we want to), etc. |
147 | | * |
148 | | * If the CHANNEL_ONLY flag is set, this indicates that we should only |
149 | | * touch state which is synchronised by the channel mutex. |
150 | | */ |
151 | 0 | #define QUIC_REACTOR_TICK_FLAG_CHANNEL_ONLY (1U << 0) |
152 | | |
153 | | int ossl_quic_reactor_tick(QUIC_REACTOR *rtor, uint32_t flags); |
154 | | |
155 | | /* |
156 | | * Blocking I/O Adaptation Layer |
157 | | * ============================= |
158 | | * |
159 | | * The blocking I/O adaptation layer implements blocking I/O on top of our |
160 | | * asynchronous core. |
161 | | * |
162 | | * The core mechanism is block_until_pred(), which does not return until pred() |
163 | | * returns a value other than 0. The blocker uses OS I/O synchronisation |
164 | | * primitives (e.g. poll(2)) and ticks the reactor until the predicate is |
165 | | * satisfied. The blocker is not required to call pred() more than once between |
166 | | * tick calls. |
167 | | * |
168 | | * When pred returns a non-zero value, that value is returned by this function. |
169 | | * This can be used to allow pred() to indicate error conditions and short |
170 | | * circuit the blocking process. |
171 | | * |
172 | | * A return value of -1 is reserved for network polling errors. Therefore this |
173 | | * return value should not be used by pred() if ambiguity is not desired. Note |
174 | | * that the predicate function can always arrange its own output mechanism, for |
175 | | * example by passing a structure of its own as the argument. |
176 | | * |
177 | | * If the SKIP_FIRST_TICK flag is set, the first call to reactor_tick() before |
178 | | * the first call to pred() is skipped. This is useful if it is known that |
179 | | * ticking the reactor again will not be useful (e.g. because it has already |
180 | | * been done). |
181 | | * |
182 | | * This function assumes a write lock is held for the entire QUIC_CHANNEL. If |
183 | | * mutex is non-NULL, it must be a lock currently held for write; it will be |
184 | | * unlocked during any sleep, and then relocked for write afterwards. |
185 | | * |
186 | | * Precondition: mutex is NULL or is held for write (unchecked) |
187 | | * Postcondition: mutex is NULL or is held for write (unless |
188 | | * CRYPTO_THREAD_write_lock fails) |
189 | | */ |
190 | 0 | #define SKIP_FIRST_TICK (1U << 0) |
191 | | |
192 | | int ossl_quic_reactor_block_until_pred(QUIC_REACTOR *rtor, |
193 | | int (*pred)(void *arg), void *pred_arg, |
194 | | uint32_t flags, |
195 | | CRYPTO_MUTEX *mutex); |
196 | | |
197 | | # endif |
198 | | |
199 | | #endif |