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