xref: /freebsd/crypto/openssl/ssl/quic/quic_engine.c (revision e7be843b4a162e68651d3911f0357ed464915629)
1 /*
2  * Copyright 2023-2025 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 
10 #include "internal/quic_engine.h"
11 #include "internal/quic_port.h"
12 #include "quic_engine_local.h"
13 #include "quic_port_local.h"
14 #include "../ssl_local.h"
15 
16 /*
17  * QUIC Engine
18  * ===========
19  */
20 static int qeng_init(QUIC_ENGINE *qeng, uint64_t reactor_flags);
21 static void qeng_cleanup(QUIC_ENGINE *qeng);
22 static void qeng_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags);
23 
24 DEFINE_LIST_OF_IMPL(port, QUIC_PORT);
25 
ossl_quic_engine_new(const QUIC_ENGINE_ARGS * args)26 QUIC_ENGINE *ossl_quic_engine_new(const QUIC_ENGINE_ARGS *args)
27 {
28     QUIC_ENGINE *qeng;
29 
30     if ((qeng = OPENSSL_zalloc(sizeof(QUIC_ENGINE))) == NULL)
31         return NULL;
32 
33     qeng->libctx            = args->libctx;
34     qeng->propq             = args->propq;
35     qeng->mutex             = args->mutex;
36 
37     if (!qeng_init(qeng, args->reactor_flags)) {
38         OPENSSL_free(qeng);
39         return NULL;
40     }
41 
42     return qeng;
43 }
44 
ossl_quic_engine_free(QUIC_ENGINE * qeng)45 void ossl_quic_engine_free(QUIC_ENGINE *qeng)
46 {
47     if (qeng == NULL)
48         return;
49 
50     qeng_cleanup(qeng);
51     OPENSSL_free(qeng);
52 }
53 
qeng_init(QUIC_ENGINE * qeng,uint64_t reactor_flags)54 static int qeng_init(QUIC_ENGINE *qeng, uint64_t reactor_flags)
55 {
56     return ossl_quic_reactor_init(&qeng->rtor, qeng_tick, qeng,
57                                   qeng->mutex,
58                                   ossl_time_zero(), reactor_flags);
59 }
60 
qeng_cleanup(QUIC_ENGINE * qeng)61 static void qeng_cleanup(QUIC_ENGINE *qeng)
62 {
63     assert(ossl_list_port_num(&qeng->port_list) == 0);
64     ossl_quic_reactor_cleanup(&qeng->rtor);
65 }
66 
ossl_quic_engine_get0_reactor(QUIC_ENGINE * qeng)67 QUIC_REACTOR *ossl_quic_engine_get0_reactor(QUIC_ENGINE *qeng)
68 {
69     return &qeng->rtor;
70 }
71 
ossl_quic_engine_get0_mutex(QUIC_ENGINE * qeng)72 CRYPTO_MUTEX *ossl_quic_engine_get0_mutex(QUIC_ENGINE *qeng)
73 {
74     return qeng->mutex;
75 }
76 
ossl_quic_engine_get_time(QUIC_ENGINE * qeng)77 OSSL_TIME ossl_quic_engine_get_time(QUIC_ENGINE *qeng)
78 {
79     if (qeng->now_cb == NULL)
80         return ossl_time_now();
81 
82     return qeng->now_cb(qeng->now_cb_arg);
83 }
84 
ossl_quic_engine_make_real_time(QUIC_ENGINE * qeng,OSSL_TIME tm)85 OSSL_TIME ossl_quic_engine_make_real_time(QUIC_ENGINE *qeng, OSSL_TIME tm)
86 {
87     OSSL_TIME offset;
88 
89     if (qeng->now_cb != NULL
90             && !ossl_time_is_zero(tm)
91             && !ossl_time_is_infinite(tm)) {
92 
93         offset = qeng->now_cb(qeng->now_cb_arg);
94 
95         /* If tm is earlier than offset then tm will end up as "now" */
96         tm = ossl_time_add(ossl_time_subtract(tm, offset), ossl_time_now());
97     }
98 
99     return tm;
100 }
101 
ossl_quic_engine_set_time_cb(QUIC_ENGINE * qeng,OSSL_TIME (* now_cb)(void * arg),void * now_cb_arg)102 void ossl_quic_engine_set_time_cb(QUIC_ENGINE *qeng,
103                                   OSSL_TIME (*now_cb)(void *arg),
104                                   void *now_cb_arg)
105 {
106     qeng->now_cb = now_cb;
107     qeng->now_cb_arg = now_cb_arg;
108 }
109 
ossl_quic_engine_set_inhibit_tick(QUIC_ENGINE * qeng,int inhibit)110 void ossl_quic_engine_set_inhibit_tick(QUIC_ENGINE *qeng, int inhibit)
111 {
112     qeng->inhibit_tick = (inhibit != 0);
113 }
114 
ossl_quic_engine_get0_libctx(QUIC_ENGINE * qeng)115 OSSL_LIB_CTX *ossl_quic_engine_get0_libctx(QUIC_ENGINE *qeng)
116 {
117     return qeng->libctx;
118 }
119 
ossl_quic_engine_get0_propq(QUIC_ENGINE * qeng)120 const char *ossl_quic_engine_get0_propq(QUIC_ENGINE *qeng)
121 {
122     return qeng->propq;
123 }
124 
ossl_quic_engine_update_poll_descriptors(QUIC_ENGINE * qeng,int force)125 void ossl_quic_engine_update_poll_descriptors(QUIC_ENGINE *qeng, int force)
126 {
127     QUIC_PORT *port;
128 
129     /*
130      * TODO(QUIC MULTIPORT): The implementation of
131      * ossl_quic_port_update_poll_descriptors assumes an engine only ever has a
132      * single port for now due to reactor limitations. This limitation will be
133      * removed in future.
134      *
135      * TODO(QUIC MULTIPORT): Consider only iterating the port list when dirty at
136      * the engine level in future when we can have multiple ports. This is not
137      * important currently as the port list has a single entry.
138      */
139     OSSL_LIST_FOREACH(port, port, &qeng->port_list)
140         ossl_quic_port_update_poll_descriptors(port, force);
141 }
142 
143 /*
144  * QUIC Engine: Child Object Lifecycle Management
145  * ==============================================
146  */
147 
ossl_quic_engine_create_port(QUIC_ENGINE * qeng,const QUIC_PORT_ARGS * args)148 QUIC_PORT *ossl_quic_engine_create_port(QUIC_ENGINE *qeng,
149                                         const QUIC_PORT_ARGS *args)
150 {
151     QUIC_PORT_ARGS largs = *args;
152 
153     if (ossl_list_port_num(&qeng->port_list) > 0)
154         /* TODO(QUIC MULTIPORT): We currently support only one port. */
155         return NULL;
156 
157     if (largs.engine != NULL)
158         return NULL;
159 
160     largs.engine = qeng;
161     return ossl_quic_port_new(&largs);
162 }
163 
164 /*
165  * QUIC Engine: Ticker-Mutator
166  * ===========================
167  */
168 
169 /*
170  * The central ticker function called by the reactor. This does everything, or
171  * at least everything network I/O related. Best effort - not allowed to fail
172  * "loudly".
173  */
qeng_tick(QUIC_TICK_RESULT * res,void * arg,uint32_t flags)174 static void qeng_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags)
175 {
176     QUIC_ENGINE *qeng = arg;
177     QUIC_PORT *port;
178 
179     res->net_read_desired     = 0;
180     res->net_write_desired    = 0;
181     res->notify_other_threads = 0;
182     res->tick_deadline        = ossl_time_infinite();
183 
184     if (qeng->inhibit_tick)
185         return;
186 
187     /* Iterate through all ports and service them. */
188     OSSL_LIST_FOREACH(port, port, &qeng->port_list) {
189         QUIC_TICK_RESULT subr = {0};
190 
191         ossl_quic_port_subtick(port, &subr, flags);
192         ossl_quic_tick_result_merge_into(res, &subr);
193     }
194 }
195