lsquic_engine.c revision c44946ec
1/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * lsquic_engine.c - QUIC engine
4 */
5
6#include <assert.h>
7#include <errno.h>
8#include <inttypes.h>
9#include <stdint.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <sys/queue.h>
14#include <time.h>
15#ifndef WIN32
16#include <sys/time.h>
17#include <netinet/in.h>
18#include <sys/types.h>
19#include <sys/stat.h>
20#include <fcntl.h>
21#include <unistd.h>
22#include <netdb.h>
23#endif
24
25
26
27#include "lsquic.h"
28#include "lsquic_types.h"
29#include "lsquic_alarmset.h"
30#include "lsquic_parse.h"
31#include "lsquic_packet_in.h"
32#include "lsquic_packet_out.h"
33#include "lsquic_senhist.h"
34#include "lsquic_rtt.h"
35#include "lsquic_cubic.h"
36#include "lsquic_pacer.h"
37#include "lsquic_send_ctl.h"
38#include "lsquic_set.h"
39#include "lsquic_conn_flow.h"
40#include "lsquic_sfcw.h"
41#include "lsquic_stream.h"
42#include "lsquic_conn.h"
43#include "lsquic_full_conn.h"
44#include "lsquic_util.h"
45#include "lsquic_qtags.h"
46#include "lsquic_str.h"
47#include "lsquic_handshake.h"
48#include "lsquic_mm.h"
49#include "lsquic_conn_hash.h"
50#include "lsquic_engine_public.h"
51#include "lsquic_eng_hist.h"
52#include "lsquic_ev_log.h"
53#include "lsquic_version.h"
54#include "lsquic_hash.h"
55#include "lsquic_attq.h"
56#include "lsquic_min_heap.h"
57
58#define LSQUIC_LOGGER_MODULE LSQLM_ENGINE
59#include "lsquic_logger.h"
60
61
62/* The batch of outgoing packets grows and shrinks dynamically */
63#define MAX_OUT_BATCH_SIZE 1024
64#define MIN_OUT_BATCH_SIZE 256
65#define INITIAL_OUT_BATCH_SIZE 512
66
67struct out_batch
68{
69    lsquic_conn_t           *conns  [MAX_OUT_BATCH_SIZE];
70    lsquic_packet_out_t     *packets[MAX_OUT_BATCH_SIZE];
71    struct lsquic_out_spec   outs   [MAX_OUT_BATCH_SIZE];
72};
73
74typedef struct lsquic_conn * (*conn_iter_f)(struct lsquic_engine *);
75
76static void
77process_connections (struct lsquic_engine *engine, conn_iter_f iter,
78                     lsquic_time_t now);
79
80static void
81engine_incref_conn (lsquic_conn_t *conn, enum lsquic_conn_flags flag);
82
83static lsquic_conn_t *
84engine_decref_conn (lsquic_engine_t *engine, lsquic_conn_t *conn,
85                                        enum lsquic_conn_flags flag);
86
87static void
88force_close_conn (lsquic_engine_t *engine, lsquic_conn_t *conn);
89
90/* Nested calls to LSQUIC are not supported */
91#define ENGINE_IN(e) do {                               \
92    assert(!((e)->pub.enp_flags & ENPUB_PROC));         \
93    (e)->pub.enp_flags |= ENPUB_PROC;                   \
94} while (0)
95
96#define ENGINE_OUT(e) do {                              \
97    assert((e)->pub.enp_flags & ENPUB_PROC);            \
98    (e)->pub.enp_flags &= ~ENPUB_PROC;                  \
99} while (0)
100
101/* A connection can be referenced from one of five places:
102 *
103 *   1. Connection hash: a connection starts its life in one of those.
104 *
105 *   2. Outgoing queue.
106 *
107 *   3. Tickable queue
108 *
109 *   4. Advisory Tick Time queue.
110 *
111 *   5. Closing connections queue.  This is a transient queue -- it only
112 *      exists for the duration of process_connections() function call.
113 *
114 * The idea is to destroy the connection when it is no longer referenced.
115 * For example, a connection tick may return TICK_SEND|TICK_CLOSE.  In
116 * that case, the connection is referenced from two places: (2) and (5).
117 * After its packets are sent, it is only referenced in (5), and at the
118 * end of the function call, when it is removed from (5), reference count
119 * goes to zero and the connection is destroyed.  If not all packets can
120 * be sent, at the end of the function call, the connection is referenced
121 * by (2) and will only be removed once all outgoing packets have been
122 * sent.
123 */
124#define CONN_REF_FLAGS  (LSCONN_HASHED          \
125                        |LSCONN_HAS_OUTGOING    \
126                        |LSCONN_TICKABLE        \
127                        |LSCONN_CLOSING         \
128                        |LSCONN_ATTQ)
129
130
131
132
133struct lsquic_engine
134{
135    struct lsquic_engine_public        pub;
136    enum {
137        ENG_SERVER      = LSENG_SERVER,
138        ENG_HTTP        = LSENG_HTTP,
139        ENG_COOLDOWN    = (1 <<  7),    /* Cooldown: no new connections */
140        ENG_PAST_DEADLINE
141                        = (1 <<  8),    /* Previous call to a processing
142                                         * function went past time threshold.
143                                         */
144#ifndef NDEBUG
145        ENG_DTOR        = (1 << 26),    /* Engine destructor */
146#endif
147    }                                  flags;
148    const struct lsquic_stream_if     *stream_if;
149    void                              *stream_if_ctx;
150    lsquic_packets_out_f               packets_out;
151    void                              *packets_out_ctx;
152    void                              *bad_handshake_ctx;
153    struct conn_hash                   conns_hash;
154    struct min_heap                    conns_tickable;
155    struct min_heap                    conns_out;
156    struct eng_hist                    history;
157    unsigned                           batch_size;
158    struct attq                       *attq;
159    /* Track time last time a packet was sent to give new connections
160     * priority lower than that of existing connections.
161     */
162    lsquic_time_t                      last_sent;
163    unsigned                           n_conns;
164    lsquic_time_t                      deadline;
165    struct out_batch                   out_batch;
166};
167
168
169void
170lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
171                             unsigned flags)
172{
173    memset(settings, 0, sizeof(*settings));
174    settings->es_versions        = LSQUIC_DF_VERSIONS;
175    if (flags & ENG_SERVER)
176    {
177        settings->es_cfcw        = LSQUIC_DF_CFCW_SERVER;
178        settings->es_sfcw        = LSQUIC_DF_SFCW_SERVER;
179        settings->es_support_srej= LSQUIC_DF_SUPPORT_SREJ_SERVER;
180    }
181    else
182    {
183        settings->es_cfcw        = LSQUIC_DF_CFCW_CLIENT;
184        settings->es_sfcw        = LSQUIC_DF_SFCW_CLIENT;
185        settings->es_support_srej= LSQUIC_DF_SUPPORT_SREJ_CLIENT;
186    }
187    settings->es_max_streams_in  = LSQUIC_DF_MAX_STREAMS_IN;
188    settings->es_idle_conn_to    = LSQUIC_DF_IDLE_CONN_TO;
189    settings->es_handshake_to    = LSQUIC_DF_HANDSHAKE_TO;
190    settings->es_silent_close    = LSQUIC_DF_SILENT_CLOSE;
191    settings->es_max_header_list_size
192                                 = LSQUIC_DF_MAX_HEADER_LIST_SIZE;
193    settings->es_ua              = LSQUIC_DF_UA;
194
195    settings->es_pdmd            = QTAG_X509;
196    settings->es_aead            = QTAG_AESG;
197    settings->es_kexs            = QTAG_C255;
198    settings->es_support_push    = LSQUIC_DF_SUPPORT_PUSH;
199    settings->es_support_tcid0   = LSQUIC_DF_SUPPORT_TCID0;
200    settings->es_support_nstp    = LSQUIC_DF_SUPPORT_NSTP;
201    settings->es_honor_prst      = LSQUIC_DF_HONOR_PRST;
202    settings->es_progress_check  = LSQUIC_DF_PROGRESS_CHECK;
203    settings->es_rw_once         = LSQUIC_DF_RW_ONCE;
204    settings->es_proc_time_thresh= LSQUIC_DF_PROC_TIME_THRESH;
205    settings->es_pace_packets    = LSQUIC_DF_PACE_PACKETS;
206}
207
208
209/* Note: if returning an error, err_buf must be valid if non-NULL */
210int
211lsquic_engine_check_settings (const struct lsquic_engine_settings *settings,
212                              unsigned flags,
213                              char *err_buf, size_t err_buf_sz)
214{
215    if (settings->es_cfcw < LSQUIC_MIN_FCW ||
216        settings->es_sfcw < LSQUIC_MIN_FCW)
217    {
218        if (err_buf)
219            snprintf(err_buf, err_buf_sz, "%s",
220                                            "flow control window set too low");
221        return -1;
222    }
223    if (0 == (settings->es_versions & LSQUIC_SUPPORTED_VERSIONS))
224    {
225        if (err_buf)
226            snprintf(err_buf, err_buf_sz, "%s",
227                        "No supported QUIC versions specified");
228        return -1;
229    }
230    if (settings->es_versions & ~LSQUIC_SUPPORTED_VERSIONS)
231    {
232        if (err_buf)
233            snprintf(err_buf, err_buf_sz, "%s",
234                        "one or more unsupported QUIC version is specified");
235        return -1;
236    }
237    return 0;
238}
239
240
241static void
242free_packet (void *ctx, unsigned char *packet_data)
243{
244    free(packet_data);
245}
246
247
248static void *
249malloc_buf (void *ctx, size_t size)
250{
251    return malloc(size);
252}
253
254
255static const struct lsquic_packout_mem_if stock_pmi =
256{
257    malloc_buf, (void(*)(void *, void *)) free_packet,
258};
259
260
261lsquic_engine_t *
262lsquic_engine_new (unsigned flags,
263                   const struct lsquic_engine_api *api)
264{
265    lsquic_engine_t *engine;
266    int tag_buf_len;
267    char err_buf[100];
268
269    if (!api->ea_packets_out)
270    {
271        LSQ_ERROR("packets_out callback is not specified");
272        return NULL;
273    }
274
275    if (api->ea_settings &&
276                0 != lsquic_engine_check_settings(api->ea_settings, flags,
277                                                    err_buf, sizeof(err_buf)))
278    {
279        LSQ_ERROR("cannot create engine: %s", err_buf);
280        return NULL;
281    }
282
283    engine = calloc(1, sizeof(*engine));
284    if (!engine)
285        return NULL;
286    if (0 != lsquic_mm_init(&engine->pub.enp_mm))
287    {
288        free(engine);
289        return NULL;
290    }
291    if (api->ea_settings)
292        engine->pub.enp_settings        = *api->ea_settings;
293    else
294        lsquic_engine_init_settings(&engine->pub.enp_settings, flags);
295    tag_buf_len = gen_ver_tags(engine->pub.enp_ver_tags_buf,
296                                    sizeof(engine->pub.enp_ver_tags_buf),
297                                    engine->pub.enp_settings.es_versions);
298    if (tag_buf_len <= 0)
299    {
300        LSQ_ERROR("cannot generate version tags buffer");
301        free(engine);
302        return NULL;
303    }
304    engine->pub.enp_ver_tags_len = tag_buf_len;
305    engine->pub.enp_flags = ENPUB_CAN_SEND;
306
307    engine->flags           = flags;
308    engine->stream_if       = api->ea_stream_if;
309    engine->stream_if_ctx   = api->ea_stream_if_ctx;
310    engine->packets_out     = api->ea_packets_out;
311    engine->packets_out_ctx = api->ea_packets_out_ctx;
312    if (api->ea_pmi)
313    {
314        engine->pub.enp_pmi      = api->ea_pmi;
315        engine->pub.enp_pmi_ctx  = api->ea_pmi_ctx;
316    }
317    else
318    {
319        engine->pub.enp_pmi      = &stock_pmi;
320        engine->pub.enp_pmi_ctx  = NULL;
321    }
322    engine->pub.enp_engine = engine;
323    conn_hash_init(&engine->conns_hash);
324    engine->attq = attq_create();
325    eng_hist_init(&engine->history);
326    engine->batch_size = INITIAL_OUT_BATCH_SIZE;
327
328
329    LSQ_INFO("instantiated engine");
330    return engine;
331}
332
333
334static void
335grow_batch_size (struct lsquic_engine *engine)
336{
337    engine->batch_size <<= engine->batch_size < MAX_OUT_BATCH_SIZE;
338}
339
340
341static void
342shrink_batch_size (struct lsquic_engine *engine)
343{
344    engine->batch_size >>= engine->batch_size > MIN_OUT_BATCH_SIZE;
345}
346
347
348/* Wrapper to make sure important things occur before the connection is
349 * really destroyed.
350 */
351static void
352destroy_conn (struct lsquic_engine *engine, lsquic_conn_t *conn)
353{
354    --engine->n_conns;
355    conn->cn_flags |= LSCONN_NEVER_TICKABLE;
356    conn->cn_if->ci_destroy(conn);
357}
358
359
360static int
361maybe_grow_conn_heaps (struct lsquic_engine *engine)
362{
363    struct min_heap_elem *els;
364    unsigned count;
365
366    if (engine->n_conns < lsquic_mh_nalloc(&engine->conns_tickable))
367        return 0;   /* Nothing to do */
368
369    if (lsquic_mh_nalloc(&engine->conns_tickable))
370        count = lsquic_mh_nalloc(&engine->conns_tickable) * 2 * 2;
371    else
372        count = 8;
373
374    els = malloc(sizeof(els[0]) * count);
375    if (!els)
376    {
377        LSQ_ERROR("%s: malloc failed", __func__);
378        return -1;
379    }
380
381    LSQ_DEBUG("grew heaps to %u elements", count / 2);
382    memcpy(&els[0], engine->conns_tickable.mh_elems,
383                sizeof(els[0]) * lsquic_mh_count(&engine->conns_tickable));
384    memcpy(&els[count / 2], engine->conns_out.mh_elems,
385                sizeof(els[0]) * lsquic_mh_count(&engine->conns_out));
386    free(engine->conns_tickable.mh_elems);
387    engine->conns_tickable.mh_elems = els;
388    engine->conns_out.mh_elems = &els[count / 2];
389    engine->conns_tickable.mh_nalloc = count / 2;
390    engine->conns_out.mh_nalloc = count / 2;
391    return 0;
392}
393
394
395static lsquic_conn_t *
396new_full_conn_client (lsquic_engine_t *engine, const char *hostname,
397                      unsigned short max_packet_size)
398{
399    lsquic_conn_t *conn;
400    unsigned flags;
401    if (0 != maybe_grow_conn_heaps(engine))
402        return NULL;
403    flags = engine->flags & (ENG_SERVER|ENG_HTTP);
404    conn = full_conn_client_new(&engine->pub, engine->stream_if,
405                    engine->stream_if_ctx, flags, hostname, max_packet_size);
406    if (!conn)
407        return NULL;
408    ++engine->n_conns;
409    if (0 != conn_hash_add(&engine->conns_hash, conn))
410    {
411        LSQ_WARN("cannot add connection %"PRIu64" to hash - destroy",
412            conn->cn_cid);
413        destroy_conn(engine, conn);
414        return NULL;
415    }
416    assert(!(conn->cn_flags &
417        (CONN_REF_FLAGS
418         & ~LSCONN_TICKABLE /* This flag may be set as effect of user
419                                 callbacks */
420                             )));
421    conn->cn_flags |= LSCONN_HASHED;
422    return conn;
423}
424
425
426static lsquic_conn_t *
427find_or_create_conn (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in,
428         struct packin_parse_state *ppstate, const struct sockaddr *sa_peer,
429         void *peer_ctx)
430{
431    lsquic_conn_t *conn;
432
433    if (lsquic_packet_in_is_prst(packet_in)
434                                && !engine->pub.enp_settings.es_honor_prst)
435    {
436        LSQ_DEBUG("public reset packet: discarding");
437        return NULL;
438    }
439
440    if (!(packet_in->pi_flags & PI_CONN_ID))
441    {
442        LSQ_DEBUG("packet header does not have connection ID: discarding");
443        return NULL;
444    }
445
446    conn = conn_hash_find(&engine->conns_hash, packet_in->pi_conn_id);
447    if (conn)
448    {
449        conn->cn_pf->pf_parse_packet_in_finish(packet_in, ppstate);
450        return conn;
451    }
452
453    return conn;
454}
455
456
457#if !defined(NDEBUG) && __GNUC__
458__attribute__((weak))
459#endif
460void
461lsquic_engine_add_conn_to_tickable (struct lsquic_engine_public *enpub,
462                                    lsquic_conn_t *conn)
463{
464    if (0 == (enpub->enp_flags & ENPUB_PROC) &&
465        0 == (conn->cn_flags & (LSCONN_TICKABLE|LSCONN_NEVER_TICKABLE)))
466    {
467        lsquic_engine_t *engine = (lsquic_engine_t *) enpub;
468        lsquic_mh_insert(&engine->conns_tickable, conn, conn->cn_last_ticked);
469        engine_incref_conn(conn, LSCONN_TICKABLE);
470    }
471}
472
473
474void
475lsquic_engine_add_conn_to_attq (struct lsquic_engine_public *enpub,
476                                lsquic_conn_t *conn, lsquic_time_t tick_time)
477{
478    lsquic_engine_t *const engine = (lsquic_engine_t *) enpub;
479    if (conn->cn_flags & LSCONN_TICKABLE)
480    {
481        /* Optimization: no need to add the connection to the Advisory Tick
482         * Time Queue: it is about to be ticked, after which it its next tick
483         * time may be queried again.
484         */;
485    }
486    else if (conn->cn_flags & LSCONN_ATTQ)
487    {
488        if (lsquic_conn_adv_time(conn) != tick_time)
489        {
490            attq_remove(engine->attq, conn);
491            if (0 != attq_add(engine->attq, conn, tick_time))
492                engine_decref_conn(engine, conn, LSCONN_ATTQ);
493        }
494    }
495    else if (0 == attq_add(engine->attq, conn, tick_time))
496        engine_incref_conn(conn, LSCONN_ATTQ);
497}
498
499
500/* Return 0 if packet is being processed by a connections, otherwise return 1 */
501static int
502process_packet_in (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in,
503       struct packin_parse_state *ppstate, const struct sockaddr *sa_local,
504       const struct sockaddr *sa_peer, void *peer_ctx)
505{
506    lsquic_conn_t *conn;
507
508    conn = find_or_create_conn(engine, packet_in, ppstate, sa_peer, peer_ctx);
509    if (!conn)
510    {
511        lsquic_mm_put_packet_in(&engine->pub.enp_mm, packet_in);
512        return 1;
513    }
514
515    if (0 == (conn->cn_flags & LSCONN_TICKABLE))
516    {
517        lsquic_mh_insert(&engine->conns_tickable, conn, conn->cn_last_ticked);
518        engine_incref_conn(conn, LSCONN_TICKABLE);
519    }
520    lsquic_conn_record_sockaddr(conn, sa_local, sa_peer);
521    lsquic_packet_in_upref(packet_in);
522    conn->cn_peer_ctx = peer_ctx;
523    conn->cn_if->ci_packet_in(conn, packet_in);
524    lsquic_packet_in_put(&engine->pub.enp_mm, packet_in);
525    return 0;
526}
527
528
529void
530lsquic_engine_destroy (lsquic_engine_t *engine)
531{
532    lsquic_conn_t *conn;
533
534    LSQ_DEBUG("destroying engine");
535#ifndef NDEBUG
536    engine->flags |= ENG_DTOR;
537#endif
538
539    while ((conn = lsquic_mh_pop(&engine->conns_out)))
540    {
541        assert(conn->cn_flags & LSCONN_HAS_OUTGOING);
542        (void) engine_decref_conn(engine, conn, LSCONN_HAS_OUTGOING);
543    }
544
545    while ((conn = lsquic_mh_pop(&engine->conns_tickable)))
546    {
547        assert(conn->cn_flags & LSCONN_TICKABLE);
548        (void) engine_decref_conn(engine, conn, LSCONN_TICKABLE);
549    }
550
551    for (conn = conn_hash_first(&engine->conns_hash); conn;
552                            conn = conn_hash_next(&engine->conns_hash))
553        force_close_conn(engine, conn);
554    conn_hash_cleanup(&engine->conns_hash);
555
556    assert(0 == engine->n_conns);
557    attq_destroy(engine->attq);
558
559    assert(0 == lsquic_mh_count(&engine->conns_out));
560    assert(0 == lsquic_mh_count(&engine->conns_tickable));
561    free(engine->conns_tickable.mh_elems);
562    free(engine);
563}
564
565
566lsquic_conn_t *
567lsquic_engine_connect (lsquic_engine_t *engine, const struct sockaddr *peer_sa,
568                       void *peer_ctx, lsquic_conn_ctx_t *conn_ctx,
569                       const char *hostname, unsigned short max_packet_size)
570{
571    lsquic_conn_t *conn;
572    ENGINE_IN(engine);
573
574    if (engine->flags & ENG_SERVER)
575    {
576        LSQ_ERROR("`%s' must only be called in client mode", __func__);
577        goto err;
578    }
579
580    if (0 == max_packet_size)
581    {
582        switch (peer_sa->sa_family)
583        {
584        case AF_INET:
585            max_packet_size = QUIC_MAX_IPv4_PACKET_SZ;
586            break;
587        default:
588            max_packet_size = QUIC_MAX_IPv6_PACKET_SZ;
589            break;
590        }
591    }
592
593    conn = new_full_conn_client(engine, hostname, max_packet_size);
594    if (!conn)
595        goto err;
596    lsquic_mh_insert(&engine->conns_tickable, conn, conn->cn_last_ticked);
597    engine_incref_conn(conn, LSCONN_TICKABLE);
598    lsquic_conn_record_peer_sa(conn, peer_sa);
599    conn->cn_peer_ctx = peer_ctx;
600    lsquic_conn_set_ctx(conn, conn_ctx);
601    full_conn_client_call_on_new(conn);
602  end:
603    ENGINE_OUT(engine);
604    return conn;
605  err:
606    conn = NULL;
607    goto end;
608}
609
610
611static void
612remove_conn_from_hash (lsquic_engine_t *engine, lsquic_conn_t *conn)
613{
614    conn_hash_remove(&engine->conns_hash, conn);
615    (void) engine_decref_conn(engine, conn, LSCONN_HASHED);
616}
617
618
619static void
620refflags2str (enum lsquic_conn_flags flags, char s[6])
621{
622    *s = 'C'; s += !!(flags & LSCONN_CLOSING);
623    *s = 'H'; s += !!(flags & LSCONN_HASHED);
624    *s = 'O'; s += !!(flags & LSCONN_HAS_OUTGOING);
625    *s = 'T'; s += !!(flags & LSCONN_TICKABLE);
626    *s = 'A'; s += !!(flags & LSCONN_ATTQ);
627    *s = '\0';
628}
629
630
631static void
632engine_incref_conn (lsquic_conn_t *conn, enum lsquic_conn_flags flag)
633{
634    char str[2][6];
635    assert(flag & CONN_REF_FLAGS);
636    assert(!(conn->cn_flags & flag));
637    conn->cn_flags |= flag;
638    LSQ_DEBUG("incref conn %"PRIu64", '%s' -> '%s'", conn->cn_cid,
639                    (refflags2str(conn->cn_flags & ~flag, str[0]), str[0]),
640                    (refflags2str(conn->cn_flags, str[1]), str[1]));
641}
642
643
644static lsquic_conn_t *
645engine_decref_conn (lsquic_engine_t *engine, lsquic_conn_t *conn,
646                                        enum lsquic_conn_flags flags)
647{
648    char str[2][6];
649    assert(flags & CONN_REF_FLAGS);
650    assert(conn->cn_flags & flags);
651#ifndef NDEBUG
652    if (flags & LSCONN_CLOSING)
653        assert(0 == (conn->cn_flags & LSCONN_HASHED));
654#endif
655    conn->cn_flags &= ~flags;
656    LSQ_DEBUG("decref conn %"PRIu64", '%s' -> '%s'", conn->cn_cid,
657                    (refflags2str(conn->cn_flags | flags, str[0]), str[0]),
658                    (refflags2str(conn->cn_flags, str[1]), str[1]));
659    if (0 == (conn->cn_flags & CONN_REF_FLAGS))
660    {
661        eng_hist_inc(&engine->history, 0, sl_del_full_conns);
662        destroy_conn(engine, conn);
663        return NULL;
664    }
665    else
666        return conn;
667}
668
669
670/* This is not a general-purpose function.  Only call from engine dtor. */
671static void
672force_close_conn (lsquic_engine_t *engine, lsquic_conn_t *conn)
673{
674    assert(engine->flags & ENG_DTOR);
675    const enum lsquic_conn_flags flags = conn->cn_flags;
676    assert(conn->cn_flags & CONN_REF_FLAGS);
677    assert(!(flags & LSCONN_HAS_OUTGOING));  /* Should be removed already */
678    assert(!(flags & LSCONN_TICKABLE));    /* Should be removed already */
679    assert(!(flags & LSCONN_CLOSING));  /* It is in transient queue? */
680    if (flags & LSCONN_ATTQ)
681    {
682        attq_remove(engine->attq, conn);
683        (void) engine_decref_conn(engine, conn, LSCONN_ATTQ);
684    }
685    if (flags & LSCONN_HASHED)
686        remove_conn_from_hash(engine, conn);
687}
688
689
690/* Iterator for tickable connections (those on the Tickable Queue).  Before
691 * a connection is returned, it is removed from the Advisory Tick Time queue
692 * if necessary.
693 */
694static lsquic_conn_t *
695conn_iter_next_tickable (struct lsquic_engine *engine)
696{
697    lsquic_conn_t *conn;
698
699    conn = lsquic_mh_pop(&engine->conns_tickable);
700
701    if (conn)
702        conn = engine_decref_conn(engine, conn, LSCONN_TICKABLE);
703    if (conn && (conn->cn_flags & LSCONN_ATTQ))
704    {
705        attq_remove(engine->attq, conn);
706        conn = engine_decref_conn(engine, conn, LSCONN_ATTQ);
707    }
708
709    return conn;
710}
711
712
713void
714lsquic_engine_process_conns (lsquic_engine_t *engine)
715{
716    lsquic_conn_t *conn;
717    lsquic_time_t now;
718
719    ENGINE_IN(engine);
720
721    now = lsquic_time_now();
722    while ((conn = attq_pop(engine->attq, now)))
723    {
724        conn = engine_decref_conn(engine, conn, LSCONN_ATTQ);
725        if (conn && !(conn->cn_flags & LSCONN_TICKABLE))
726        {
727            lsquic_mh_insert(&engine->conns_tickable, conn, conn->cn_last_ticked);
728            engine_incref_conn(conn, LSCONN_TICKABLE);
729        }
730    }
731
732    process_connections(engine, conn_iter_next_tickable, now);
733    ENGINE_OUT(engine);
734}
735
736
737static int
738generate_header (const lsquic_packet_out_t *packet_out,
739                 const struct parse_funcs *pf, lsquic_cid_t cid,
740                 unsigned char *buf, size_t bufsz)
741{
742    return pf->pf_gen_reg_pkt_header(buf, bufsz,
743        packet_out->po_flags & PO_CONN_ID ? &cid                    : NULL,
744        packet_out->po_flags & PO_VERSION ? &packet_out->po_ver_tag : NULL,
745        packet_out->po_flags & PO_NONCE   ? packet_out->po_nonce    : NULL,
746        packet_out->po_packno, lsquic_packet_out_packno_bits(packet_out));
747}
748
749
750static ssize_t
751really_encrypt_packet (const lsquic_conn_t *conn,
752                       const lsquic_packet_out_t *packet_out,
753                       unsigned char *buf, size_t bufsz)
754{
755    int enc, header_sz, is_hello_packet;
756    size_t packet_sz;
757    unsigned char header_buf[QUIC_MAX_PUBHDR_SZ];
758
759    header_sz = generate_header(packet_out, conn->cn_pf, conn->cn_cid,
760                                            header_buf, sizeof(header_buf));
761    if (header_sz < 0)
762        return -1;
763
764    is_hello_packet = !!(packet_out->po_flags & PO_HELLO);
765    enc = conn->cn_esf->esf_encrypt(conn->cn_enc_session, conn->cn_version, 0,
766                packet_out->po_packno, header_buf, header_sz,
767                packet_out->po_data, packet_out->po_data_sz,
768                buf, bufsz, &packet_sz, is_hello_packet);
769    if (0 == enc)
770    {
771        LSQ_DEBUG("encrypted packet %"PRIu64"; plaintext is %u bytes, "
772            "ciphertext is %zd bytes",
773            packet_out->po_packno,
774            lsquic_po_header_length(packet_out->po_flags) +
775                                                packet_out->po_data_sz,
776            packet_sz);
777        return packet_sz;
778    }
779    else
780        return -1;
781}
782
783
784static enum { ENCPA_OK, ENCPA_NOMEM, ENCPA_BADCRYPT, }
785encrypt_packet (lsquic_engine_t *engine, const lsquic_conn_t *conn,
786                                            lsquic_packet_out_t *packet_out)
787{
788    ssize_t enc_sz;
789    size_t bufsz;
790    unsigned sent_sz;
791    unsigned char *buf;
792
793    bufsz = lsquic_po_header_length(packet_out->po_flags) +
794                                packet_out->po_data_sz + QUIC_PACKET_HASH_SZ;
795    buf = engine->pub.enp_pmi->pmi_allocate(engine->pub.enp_pmi_ctx, bufsz);
796    if (!buf)
797    {
798        LSQ_DEBUG("could not allocate memory for outgoing packet of size %zd",
799                                                                        bufsz);
800        return ENCPA_NOMEM;
801    }
802
803    {
804        enc_sz = really_encrypt_packet(conn, packet_out, buf, bufsz);
805        sent_sz = enc_sz;
806    }
807
808    if (enc_sz < 0)
809    {
810        engine->pub.enp_pmi->pmi_release(engine->pub.enp_pmi_ctx, buf);
811        return ENCPA_BADCRYPT;
812    }
813
814    packet_out->po_enc_data    = buf;
815    packet_out->po_enc_data_sz = enc_sz;
816    packet_out->po_sent_sz     = sent_sz;
817    packet_out->po_flags |= PO_ENCRYPTED|PO_SENT_SZ;
818
819    return ENCPA_OK;
820}
821
822
823STAILQ_HEAD(conns_stailq, lsquic_conn);
824
825
826struct conns_out_iter
827{
828    struct min_heap            *coi_heap;
829    TAILQ_HEAD(, lsquic_conn)   coi_active_list,
830                                coi_inactive_list;
831    lsquic_conn_t              *coi_next;
832#ifndef NDEBUG
833    lsquic_time_t               coi_last_sent;
834#endif
835};
836
837
838static void
839coi_init (struct conns_out_iter *iter, struct lsquic_engine *engine)
840{
841    iter->coi_heap = &engine->conns_out;
842    iter->coi_next = NULL;
843    TAILQ_INIT(&iter->coi_active_list);
844    TAILQ_INIT(&iter->coi_inactive_list);
845#ifndef NDEBUG
846    iter->coi_last_sent = 0;
847#endif
848}
849
850
851static lsquic_conn_t *
852coi_next (struct conns_out_iter *iter)
853{
854    lsquic_conn_t *conn;
855
856    if (lsquic_mh_count(iter->coi_heap) > 0)
857    {
858        conn = lsquic_mh_pop(iter->coi_heap);
859        TAILQ_INSERT_TAIL(&iter->coi_active_list, conn, cn_next_out);
860        conn->cn_flags |= LSCONN_COI_ACTIVE;
861#ifndef NDEBUG
862        if (iter->coi_last_sent)
863            assert(iter->coi_last_sent <= conn->cn_last_sent);
864        iter->coi_last_sent = conn->cn_last_sent;
865#endif
866        return conn;
867    }
868    else if (!TAILQ_EMPTY(&iter->coi_active_list))
869    {
870        conn = iter->coi_next;
871        if (!conn)
872            conn = TAILQ_FIRST(&iter->coi_active_list);
873        if (conn)
874            iter->coi_next = TAILQ_NEXT(conn, cn_next_out);
875        return conn;
876    }
877    else
878        return NULL;
879}
880
881
882static void
883coi_deactivate (struct conns_out_iter *iter, lsquic_conn_t *conn)
884{
885    if (!(conn->cn_flags & LSCONN_EVANESCENT))
886    {
887        assert(!TAILQ_EMPTY(&iter->coi_active_list));
888        TAILQ_REMOVE(&iter->coi_active_list, conn, cn_next_out);
889        conn->cn_flags &= ~LSCONN_COI_ACTIVE;
890        TAILQ_INSERT_TAIL(&iter->coi_inactive_list, conn, cn_next_out);
891        conn->cn_flags |= LSCONN_COI_INACTIVE;
892    }
893}
894
895
896static void
897coi_remove (struct conns_out_iter *iter, lsquic_conn_t *conn)
898{
899    assert(conn->cn_flags & LSCONN_COI_ACTIVE);
900    if (conn->cn_flags & LSCONN_COI_ACTIVE)
901    {
902        TAILQ_REMOVE(&iter->coi_active_list, conn, cn_next_out);
903        conn->cn_flags &= ~LSCONN_COI_ACTIVE;
904    }
905}
906
907
908static void
909coi_reactivate (struct conns_out_iter *iter, lsquic_conn_t *conn)
910{
911    assert(conn->cn_flags & LSCONN_COI_INACTIVE);
912    TAILQ_REMOVE(&iter->coi_inactive_list, conn, cn_next_out);
913    conn->cn_flags &= ~LSCONN_COI_INACTIVE;
914    TAILQ_INSERT_TAIL(&iter->coi_active_list, conn, cn_next_out);
915    conn->cn_flags |= LSCONN_COI_ACTIVE;
916}
917
918
919static void
920coi_reheap (struct conns_out_iter *iter, lsquic_engine_t *engine)
921{
922    lsquic_conn_t *conn;
923    while ((conn = TAILQ_FIRST(&iter->coi_active_list)))
924    {
925        TAILQ_REMOVE(&iter->coi_active_list, conn, cn_next_out);
926        conn->cn_flags &= ~LSCONN_COI_ACTIVE;
927        lsquic_mh_insert(iter->coi_heap, conn, conn->cn_last_sent);
928    }
929    while ((conn = TAILQ_FIRST(&iter->coi_inactive_list)))
930    {
931        TAILQ_REMOVE(&iter->coi_inactive_list, conn, cn_next_out);
932        conn->cn_flags &= ~LSCONN_COI_INACTIVE;
933        (void) engine_decref_conn(engine, conn, LSCONN_HAS_OUTGOING);
934    }
935}
936
937
938static unsigned
939send_batch (lsquic_engine_t *engine, struct conns_out_iter *conns_iter,
940                  struct out_batch *batch, unsigned n_to_send)
941{
942    int n_sent, i;
943    lsquic_time_t now;
944
945    /* Set sent time before the write to avoid underestimating RTT */
946    now = lsquic_time_now();
947    for (i = 0; i < (int) n_to_send; ++i)
948        batch->packets[i]->po_sent = now;
949    n_sent = engine->packets_out(engine->packets_out_ctx, batch->outs,
950                                                                n_to_send);
951    if (n_sent >= 0)
952        LSQ_DEBUG("packets out returned %d (out of %u)", n_sent, n_to_send);
953    else
954    {
955        engine->pub.enp_flags &= ~ENPUB_CAN_SEND;
956        LSQ_DEBUG("packets out returned an error: %s", strerror(errno));
957        EV_LOG_GENERIC_EVENT("cannot send packets");
958        n_sent = 0;
959    }
960    if (n_sent > 0)
961        engine->last_sent = now + n_sent;
962    for (i = 0; i < n_sent; ++i)
963    {
964        eng_hist_inc(&engine->history, now, sl_packets_out);
965        EV_LOG_PACKET_SENT(batch->conns[i]->cn_cid, batch->packets[i]);
966        batch->conns[i]->cn_if->ci_packet_sent(batch->conns[i],
967                                                    batch->packets[i]);
968        /* `i' is added to maintain relative order */
969        batch->conns[i]->cn_last_sent = now + i;
970        /* Release packet out buffer as soon as the packet is sent
971         * successfully.  If not successfully sent, we hold on to
972         * this buffer until the packet sending is attempted again
973         * or until it times out and regenerated.
974         */
975        if (batch->packets[i]->po_flags & PO_ENCRYPTED)
976        {
977            batch->packets[i]->po_flags &= ~PO_ENCRYPTED;
978            engine->pub.enp_pmi->pmi_release(engine->pub.enp_pmi_ctx,
979                                                batch->packets[i]->po_enc_data);
980            batch->packets[i]->po_enc_data = NULL;  /* JIC */
981        }
982    }
983    if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT))
984        for ( ; i < (int) n_to_send; ++i)
985            EV_LOG_PACKET_NOT_SENT(batch->conns[i]->cn_cid, batch->packets[i]);
986    /* Return packets to the connection in reverse order so that the packet
987     * ordering is maintained.
988     */
989    for (i = (int) n_to_send - 1; i >= n_sent; --i)
990    {
991        batch->conns[i]->cn_if->ci_packet_not_sent(batch->conns[i],
992                                                    batch->packets[i]);
993        if (!(batch->conns[i]->cn_flags & (LSCONN_COI_ACTIVE|LSCONN_EVANESCENT)))
994            coi_reactivate(conns_iter, batch->conns[i]);
995    }
996    return n_sent;
997}
998
999
1000/* Return 1 if went past deadline, 0 otherwise */
1001static int
1002check_deadline (lsquic_engine_t *engine)
1003{
1004    if (engine->pub.enp_settings.es_proc_time_thresh &&
1005                                lsquic_time_now() > engine->deadline)
1006    {
1007        LSQ_INFO("went past threshold of %u usec, stop sending",
1008                            engine->pub.enp_settings.es_proc_time_thresh);
1009        engine->flags |= ENG_PAST_DEADLINE;
1010        return 1;
1011    }
1012    else
1013        return 0;
1014}
1015
1016
1017static void
1018send_packets_out (struct lsquic_engine *engine,
1019                  struct conns_stailq *closed_conns)
1020{
1021    unsigned n, w, n_sent, n_batches_sent;
1022    lsquic_packet_out_t *packet_out;
1023    lsquic_conn_t *conn;
1024    struct out_batch *const batch = &engine->out_batch;
1025    struct conns_out_iter conns_iter;
1026    int shrink, deadline_exceeded;
1027
1028    coi_init(&conns_iter, engine);
1029    n_batches_sent = 0;
1030    n_sent = 0, n = 0;
1031    shrink = 0;
1032    deadline_exceeded = 0;
1033
1034    while ((conn = coi_next(&conns_iter)))
1035    {
1036        packet_out = conn->cn_if->ci_next_packet_to_send(conn);
1037        if (!packet_out) {
1038            LSQ_DEBUG("batched all outgoing packets for conn %"PRIu64,
1039                                                            conn->cn_cid);
1040            coi_deactivate(&conns_iter, conn);
1041            continue;
1042        }
1043        if (!(packet_out->po_flags & (PO_ENCRYPTED|PO_NOENCRYPT)))
1044        {
1045            switch (encrypt_packet(engine, conn, packet_out))
1046            {
1047            case ENCPA_NOMEM:
1048                /* Send what we have and wait for a more opportune moment */
1049                conn->cn_if->ci_packet_not_sent(conn, packet_out);
1050                goto end_for;
1051            case ENCPA_BADCRYPT:
1052                /* This is pretty bad: close connection immediately */
1053                conn->cn_if->ci_packet_not_sent(conn, packet_out);
1054                LSQ_INFO("conn %"PRIu64" has unsendable packets", conn->cn_cid);
1055                if (!(conn->cn_flags & LSCONN_EVANESCENT))
1056                {
1057                    if (!(conn->cn_flags & LSCONN_CLOSING))
1058                    {
1059                        STAILQ_INSERT_TAIL(closed_conns, conn, cn_next_closed_conn);
1060                        engine_incref_conn(conn, LSCONN_CLOSING);
1061                        if (conn->cn_flags & LSCONN_HASHED)
1062                            remove_conn_from_hash(engine, conn);
1063                    }
1064                    coi_remove(&conns_iter, conn);
1065                }
1066                continue;
1067            case ENCPA_OK:
1068                break;
1069            }
1070        }
1071        LSQ_DEBUG("batched packet %"PRIu64" for connection %"PRIu64,
1072                                        packet_out->po_packno, conn->cn_cid);
1073        assert(conn->cn_flags & LSCONN_HAS_PEER_SA);
1074        if (packet_out->po_flags & PO_ENCRYPTED)
1075        {
1076            batch->outs[n].buf     = packet_out->po_enc_data;
1077            batch->outs[n].sz      = packet_out->po_enc_data_sz;
1078        }
1079        else
1080        {
1081            batch->outs[n].buf     = packet_out->po_data;
1082            batch->outs[n].sz      = packet_out->po_data_sz;
1083        }
1084        batch->outs   [n].peer_ctx = conn->cn_peer_ctx;
1085        batch->outs   [n].local_sa = (struct sockaddr *) conn->cn_local_addr;
1086        batch->outs   [n].dest_sa  = (struct sockaddr *) conn->cn_peer_addr;
1087        batch->conns  [n]          = conn;
1088        batch->packets[n]          = packet_out;
1089        ++n;
1090        if (n == engine->batch_size)
1091        {
1092            n = 0;
1093            w = send_batch(engine, &conns_iter, batch, engine->batch_size);
1094            ++n_batches_sent;
1095            n_sent += w;
1096            if (w < engine->batch_size)
1097            {
1098                shrink = 1;
1099                break;
1100            }
1101            deadline_exceeded = check_deadline(engine);
1102            if (deadline_exceeded)
1103                break;
1104            grow_batch_size(engine);
1105        }
1106    }
1107  end_for:
1108
1109    if (n > 0) {
1110        w = send_batch(engine, &conns_iter, batch, n);
1111        n_sent += w;
1112        shrink = w < n;
1113        ++n_batches_sent;
1114        deadline_exceeded = check_deadline(engine);
1115    }
1116
1117    if (shrink)
1118        shrink_batch_size(engine);
1119    else if (n_batches_sent > 1 && !deadline_exceeded)
1120        grow_batch_size(engine);
1121
1122    coi_reheap(&conns_iter, engine);
1123
1124    LSQ_DEBUG("%s: sent %u packet%.*s", __func__, n_sent, n_sent != 1, "s");
1125}
1126
1127
1128int
1129lsquic_engine_has_unsent_packets (lsquic_engine_t *engine)
1130{
1131    return lsquic_mh_count(&engine->conns_out) > 0
1132    ;
1133}
1134
1135
1136static void
1137reset_deadline (lsquic_engine_t *engine, lsquic_time_t now)
1138{
1139    engine->deadline = now + engine->pub.enp_settings.es_proc_time_thresh;
1140    engine->flags &= ~ENG_PAST_DEADLINE;
1141}
1142
1143
1144/* TODO: this is a user-facing function, account for load */
1145void
1146lsquic_engine_send_unsent_packets (lsquic_engine_t *engine)
1147{
1148    lsquic_conn_t *conn;
1149    struct conns_stailq closed_conns;
1150
1151    STAILQ_INIT(&closed_conns);
1152    reset_deadline(engine, lsquic_time_now());
1153    if (!(engine->pub.enp_flags & ENPUB_CAN_SEND))
1154    {
1155        LSQ_DEBUG("can send again");
1156        EV_LOG_GENERIC_EVENT("can send again");
1157        engine->pub.enp_flags |= ENPUB_CAN_SEND;
1158    }
1159
1160    send_packets_out(engine, &closed_conns);
1161
1162    while ((conn = STAILQ_FIRST(&closed_conns))) {
1163        STAILQ_REMOVE_HEAD(&closed_conns, cn_next_closed_conn);
1164        (void) engine_decref_conn(engine, conn, LSCONN_CLOSING);
1165    }
1166
1167}
1168
1169
1170static void
1171process_connections (lsquic_engine_t *engine, conn_iter_f next_conn,
1172                     lsquic_time_t now)
1173{
1174    lsquic_conn_t *conn;
1175    enum tick_st tick_st;
1176    unsigned i;
1177    lsquic_time_t next_tick_time;
1178    struct conns_stailq closed_conns, ticked_conns;
1179
1180    eng_hist_tick(&engine->history, now);
1181
1182    STAILQ_INIT(&closed_conns);
1183    STAILQ_INIT(&ticked_conns);
1184    reset_deadline(engine, now);
1185
1186    i = 0;
1187    while ((conn = next_conn(engine))
1188          )
1189    {
1190        tick_st = conn->cn_if->ci_tick(conn, now);
1191        conn->cn_last_ticked = now + i /* Maintain relative order */ ++;
1192        if (tick_st & TICK_SEND)
1193        {
1194            if (!(conn->cn_flags & LSCONN_HAS_OUTGOING))
1195            {
1196                lsquic_mh_insert(&engine->conns_out, conn, conn->cn_last_sent);
1197                engine_incref_conn(conn, LSCONN_HAS_OUTGOING);
1198            }
1199        }
1200        if (tick_st & TICK_CLOSE)
1201        {
1202            STAILQ_INSERT_TAIL(&closed_conns, conn, cn_next_closed_conn);
1203            engine_incref_conn(conn, LSCONN_CLOSING);
1204            if (conn->cn_flags & LSCONN_HASHED)
1205                remove_conn_from_hash(engine, conn);
1206        }
1207        else
1208            STAILQ_INSERT_TAIL(&ticked_conns, conn, cn_next_ticked);
1209    }
1210
1211    if ((engine->pub.enp_flags & ENPUB_CAN_SEND)
1212                        && lsquic_engine_has_unsent_packets(engine))
1213        send_packets_out(engine, &closed_conns);
1214
1215    while ((conn = STAILQ_FIRST(&closed_conns))) {
1216        STAILQ_REMOVE_HEAD(&closed_conns, cn_next_closed_conn);
1217        (void) engine_decref_conn(engine, conn, LSCONN_CLOSING);
1218    }
1219
1220    /* TODO Heapification can be optimized by switching to the Floyd method:
1221     * https://en.wikipedia.org/wiki/Binary_heap#Building_a_heap
1222     */
1223    while ((conn = STAILQ_FIRST(&ticked_conns)))
1224    {
1225        STAILQ_REMOVE_HEAD(&ticked_conns, cn_next_ticked);
1226        if (!(conn->cn_flags & LSCONN_TICKABLE)
1227            && conn->cn_if->ci_is_tickable(conn))
1228        {
1229            lsquic_mh_insert(&engine->conns_tickable, conn, conn->cn_last_ticked);
1230            engine_incref_conn(conn, LSCONN_TICKABLE);
1231        }
1232        else if (!(conn->cn_flags & LSCONN_ATTQ))
1233        {
1234            next_tick_time = conn->cn_if->ci_next_tick_time(conn);
1235            if (next_tick_time)
1236            {
1237                if (0 == attq_add(engine->attq, conn, next_tick_time))
1238                    engine_incref_conn(conn, LSCONN_ATTQ);
1239            }
1240            else
1241                assert(0);
1242        }
1243    }
1244
1245}
1246
1247
1248/* Return 0 if packet is being processed by a real connection, 1 if the
1249 * packet was processed, but not by a connection, and -1 on error.
1250 */
1251int
1252lsquic_engine_packet_in (lsquic_engine_t *engine,
1253    const unsigned char *packet_in_data, size_t packet_in_size,
1254    const struct sockaddr *sa_local, const struct sockaddr *sa_peer,
1255    void *peer_ctx)
1256{
1257    struct packin_parse_state ppstate;
1258    lsquic_packet_in_t *packet_in;
1259
1260    if (packet_in_size > QUIC_MAX_PACKET_SZ)
1261    {
1262        LSQ_DEBUG("Cannot handle packet_in_size(%zd) > %d packet incoming "
1263            "packet's header", packet_in_size, QUIC_MAX_PACKET_SZ);
1264        errno = E2BIG;
1265        return -1;
1266    }
1267
1268    packet_in = lsquic_mm_get_packet_in(&engine->pub.enp_mm);
1269    if (!packet_in)
1270        return -1;
1271
1272    /* Library does not modify packet_in_data, it is not referenced after
1273     * this function returns and subsequent release of pi_data is guarded
1274     * by PI_OWN_DATA flag.
1275     */
1276    packet_in->pi_data = (unsigned char *) packet_in_data;
1277    if (0 != parse_packet_in_begin(packet_in, packet_in_size,
1278                                        engine->flags & ENG_SERVER, &ppstate))
1279    {
1280        LSQ_DEBUG("Cannot parse incoming packet's header");
1281        lsquic_mm_put_packet_in(&engine->pub.enp_mm, packet_in);
1282        errno = EINVAL;
1283        return -1;
1284    }
1285
1286    packet_in->pi_received = lsquic_time_now();
1287    eng_hist_inc(&engine->history, packet_in->pi_received, sl_packets_in);
1288    return process_packet_in(engine, packet_in, &ppstate, sa_local, sa_peer,
1289                                                                    peer_ctx);
1290}
1291
1292
1293#if __GNUC__ && !defined(NDEBUG)
1294__attribute__((weak))
1295#endif
1296unsigned
1297lsquic_engine_quic_versions (const lsquic_engine_t *engine)
1298{
1299    return engine->pub.enp_settings.es_versions;
1300}
1301
1302
1303int
1304lsquic_engine_earliest_adv_tick (lsquic_engine_t *engine, int *diff)
1305{
1306    const lsquic_time_t *next_time;
1307    lsquic_time_t now;
1308
1309    if (((engine->flags & ENG_PAST_DEADLINE)
1310                                    && lsquic_mh_count(&engine->conns_out))
1311        || lsquic_mh_count(&engine->conns_tickable))
1312    {
1313        *diff = 0;
1314        return 1;
1315    }
1316
1317    next_time = attq_next_time(engine->attq);
1318    if (!next_time)
1319        return 0;
1320
1321    now = lsquic_time_now();
1322    *diff = (int) ((int64_t) *next_time - (int64_t) now);
1323    return 1;
1324}
1325
1326
1327unsigned
1328lsquic_engine_count_attq (lsquic_engine_t *engine, int from_now)
1329{
1330    lsquic_time_t now;
1331    now = lsquic_time_now();
1332    if (from_now < 0)
1333        now -= from_now;
1334    else
1335        now += from_now;
1336    return attq_count_before(engine->attq, now);
1337}
1338