lsquic_pr_queue.c revision 72bbf1fb
1/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */ 2/* 3 * lsquic_pr_queue.c -- packet request queue. 4 */ 5 6#include <assert.h> 7#include <errno.h> 8#include <inttypes.h> 9#include <netinet/in.h> 10#include <stdlib.h> 11#include <string.h> 12#include <sys/queue.h> 13#include <sys/socket.h> 14 15#include <openssl/aead.h> 16#include <openssl/rand.h> 17 18#include "lsquic.h" 19#include "lsquic_types.h" 20#include "lsquic_int_types.h" 21#include "lsquic_packet_common.h" 22#include "lsquic_packet_gquic.h" 23#include "lsquic_packet_out.h" 24#include "lsquic_packet_in.h" 25#include "lsquic_hash.h" 26#include "lsquic_conn.h" 27#include "lsquic_parse.h" 28#include "lsquic_malo.h" 29#include "lsquic_pr_queue.h" 30#include "lsquic_parse_common.h" 31#include "lsquic_tokgen.h" 32#include "lsquic_version.h" 33#include "lsquic_mm.h" 34#include "lsquic_engine_public.h" 35#include "lsquic_sizes.h" 36#include "lsquic_handshake.h" 37#include "lsquic_xxhash.h" 38 39#define LSQUIC_LOGGER_MODULE LSQLM_PRQ 40#include "lsquic_logger.h" 41 42#define MAX(a, b) ((a) > (b) ? (a) : (b)) 43#define MIN(a, b) ((a) < (b) ? (a) : (b)) 44 45 46static const struct conn_iface evanescent_conn_iface; 47 48 49struct packet_req 50{ 51 struct lsquic_hash_elem pr_hash_el; 52 lsquic_cid_t pr_scid; 53 lsquic_cid_t pr_dcid; 54 enum packet_req_type pr_type; 55 enum pr_flags { 56 PR_GQUIC = 1 << 0, 57 } pr_flags; 58 enum lsquic_version pr_version; 59 unsigned pr_rst_sz; 60 struct network_path pr_path; 61}; 62 63 64struct evanescent_conn 65{ 66 struct lsquic_conn evc_conn; 67 struct packet_req *evc_req; 68 struct pr_queue *evc_queue; 69 struct lsquic_packet_out evc_packet_out; 70 struct conn_cid_elem evc_cces[1]; 71 enum { 72 EVC_DROP = 1 << 0, 73 } evc_flags; 74 unsigned char evc_buf[0]; 75}; 76 77 78/* [draft-ietf-quic-transport-22], Section 17.2.1 */ 79#define IQUIC_VERNEG_SIZE (1 /* Type */ + 4 /* Version (zero tag) */ \ 80 + 1 /* DCIL */ + MAX_CID_LEN + 1 /* SCIL */ + MAX_CID_LEN + \ 81 4 * N_LSQVER) 82 83 84struct pr_queue 85{ 86 TAILQ_HEAD(, lsquic_conn) prq_free_conns, 87 prq_returned_conns; 88 struct malo *prq_reqs_pool; 89 const struct lsquic_engine_public 90 *prq_enpub; 91 struct lsquic_hash *prq_reqs_hash; 92 unsigned prq_max_reqs; 93 unsigned prq_nreqs; 94 unsigned prq_max_conns; 95 unsigned prq_nconns; 96 unsigned prq_verneg_g_sz; /* Size of prq_verneg_g_buf */ 97 unsigned prq_pubres_g_sz; /* Size of prq_pubres_g_buf */ 98 99 /* GQUIC version negotiation and stateless reset packets are generated 100 * once, when the Packet Request Queue is created. For each request, 101 * these buffers are simply copied and the connection ID is replaced. 102 * 103 * Since IETF QUIC uses variable-length connections IDs, we have to 104 * generate packets every time. 105 */ 106 unsigned char prq_pubres_g_buf[GQUIC_RESET_SZ]; 107 unsigned char prq_verneg_g_buf[1 + GQUIC_CID_LEN 108 + N_LSQVER * 4]; 109 /* We generate random nybbles in batches */ 110#define NYBBLE_COUNT_BITS 4 111#define NYBBLE_COUNT (1 << NYBBLE_COUNT_BITS) 112#define NYBBLE_MASK (NYBBLE_COUNT - 1) 113 unsigned prq_rand_nybble_off; 114 uint8_t prq_rand_nybble_buf[NYBBLE_COUNT * 2]; 115}; 116 117 118static uint8_t 119get_rand_byte (struct pr_queue *); 120 121 122static int 123comp_reqs (const void *s1, const void *s2, size_t n) 124{ 125 const struct packet_req *a, *b; 126 127 a = s1; 128 b = s2; 129 if (a->pr_type == b->pr_type && LSQUIC_CIDS_EQ(&a->pr_dcid, &b->pr_dcid)) 130 return 0; 131 else 132 return -1; 133} 134 135 136static unsigned 137hash_req (const void *p, size_t len, unsigned seed) 138{ 139 const struct packet_req *req; 140 141 req = p; 142 return XXH32(req->pr_dcid.idbuf, req->pr_dcid.len, seed); 143} 144 145 146struct pr_queue * 147prq_create (unsigned max_elems, unsigned max_conns, 148 const struct lsquic_engine_public *enpub) 149{ 150 const struct parse_funcs *pf; 151 struct pr_queue *prq; 152 struct malo *malo; 153 struct lsquic_hash *hash; 154 unsigned verneg_g_sz; 155 ssize_t prst_g_sz; 156 int len; 157 158 malo = lsquic_malo_create(sizeof(struct packet_req)); 159 if (!malo) 160 { 161 LSQ_WARN("malo_create failed: %s", strerror(errno)); 162 goto err0; 163 } 164 165 166 hash = lsquic_hash_create_ext(comp_reqs, hash_req); 167 if (!hash) 168 { 169 LSQ_WARN("cannot create hash"); 170 goto err1; 171 } 172 173 prq = malloc(sizeof(*prq)); 174 if (!prq) 175 { 176 LSQ_WARN("malloc failed: %s", strerror(errno)); 177 goto err2; 178 } 179 180 const lsquic_cid_t cid = { .len = 8, }; 181 pf = select_pf_by_ver(LSQVER_039); 182 len = lsquic_gquic_gen_ver_nego_pkt(prq->prq_verneg_g_buf, 183 sizeof(prq->prq_verneg_g_buf), &cid, 184 enpub->enp_settings.es_versions); 185 assert(len > 0); 186 if (len <= 0) 187 { 188 LSQ_ERROR("cannot generate version negotiation packet"); 189 goto err3; 190 } 191 verneg_g_sz = (unsigned) len; 192 193 prst_g_sz = pf->pf_generate_simple_prst(0 /* This is just placeholder */, 194 prq->prq_pubres_g_buf, sizeof(prq->prq_pubres_g_buf)); 195 if (prst_g_sz < 0) 196 { 197 LSQ_ERROR("cannot generate public reset packet"); 198 goto err3; 199 } 200 201 TAILQ_INIT(&prq->prq_free_conns); 202 TAILQ_INIT(&prq->prq_returned_conns); 203 prq->prq_reqs_hash = hash; 204 prq->prq_reqs_pool = malo; 205 prq->prq_max_reqs = max_elems; 206 prq->prq_nreqs = 0; 207 prq->prq_max_conns = max_conns; 208 prq->prq_nconns = 0; 209 prq->prq_verneg_g_sz = verneg_g_sz; 210 prq->prq_pubres_g_sz = (unsigned) prst_g_sz; 211 prq->prq_enpub = enpub; 212 prq->prq_rand_nybble_off = 0; 213 214 LSQ_INFO("initialized queue of size %d", max_elems); 215 216 return prq; 217 218 err3: 219 free(prq); 220 err2: 221 lsquic_hash_destroy(hash); 222 err1: 223 lsquic_malo_destroy(malo); 224 err0: 225 return NULL; 226} 227 228 229void 230prq_destroy (struct pr_queue *prq) 231{ 232 struct lsquic_conn *conn; 233 234 LSQ_INFO("destroy"); 235 while ((conn = TAILQ_FIRST(&prq->prq_free_conns))) 236 { 237 TAILQ_REMOVE(&prq->prq_free_conns, conn, cn_next_pr); 238 free(conn); 239 } 240 lsquic_hash_destroy(prq->prq_reqs_hash); 241 lsquic_malo_destroy(prq->prq_reqs_pool); 242 free(prq); 243} 244 245 246static struct packet_req * 247get_req (struct pr_queue *prq) 248{ 249 struct packet_req *req; 250 if (prq->prq_nreqs < prq->prq_max_reqs) 251 { 252 req = lsquic_malo_get(prq->prq_reqs_pool); 253 if (req) 254 ++prq->prq_nreqs; 255 else 256 LSQ_WARN("malo_get failed: %s", strerror(errno)); 257 return req; 258 } 259 else 260 return NULL; 261} 262 263 264static void 265put_req (struct pr_queue *prq, struct packet_req *req) 266{ 267 lsquic_malo_put(req); 268 --prq->prq_nreqs; 269} 270 271 272int 273lsquic_prq_new_req (struct pr_queue *prq, enum packet_req_type type, 274 unsigned flags, enum lsquic_version version, unsigned short data_sz, 275 const lsquic_cid_t *dcid, const lsquic_cid_t *scid, void *peer_ctx, 276 const struct sockaddr *local_addr, const struct sockaddr *peer_addr) 277{ 278 struct packet_req *req; 279 unsigned max, size, rand; 280 281 if (type == PACKET_REQ_PUBRES && !(flags & PR_GQUIC)) 282 { 283 if (data_sz <= IQUIC_MIN_SRST_SIZE) 284 { 285 LSQ_DEBUGC("not scheduling public reset: incoming packet for CID " 286 "%"CID_FMT" too small: %hu bytes", CID_BITS(dcid), data_sz); 287 return -1; 288 } 289 /* Use a random stateless reset size */ 290 max = MIN(IQUIC_MAX_SRST_SIZE, data_sz - 1u); 291 if (max > IQUIC_MIN_SRST_SIZE) 292 { 293 rand = get_rand_byte(prq); 294 size = IQUIC_MIN_SRST_SIZE + rand % (max - IQUIC_MIN_SRST_SIZE); 295 } 296 else 297 size = IQUIC_MIN_SRST_SIZE; 298 LSQ_DEBUGC("selected %u-byte reset size for CID %"CID_FMT 299 " (range is [%u, %u])", size, CID_BITS(dcid), 300 IQUIC_MIN_SRST_SIZE, max); 301 } 302 else 303 size = 0; 304 305 req = get_req(prq); 306 if (!req) 307 { 308 LSQ_DEBUG("out of reqs: cannot allocated another one"); 309 return -1; 310 } 311 312 req->pr_type = type; 313 req->pr_dcid = *dcid; 314 if (lsquic_hash_find(prq->prq_reqs_hash, req, sizeof(*req))) 315 { 316 LSQ_DEBUG("request for this DCID and type already exists"); 317 put_req(prq, req); 318 return -1; 319 } 320 321 req->pr_hash_el.qhe_flags = 0; 322 if (!lsquic_hash_insert(prq->prq_reqs_hash, req, sizeof(*req), 323 req, &req->pr_hash_el)) 324 { 325 LSQ_DEBUG("could not insert req into hash"); 326 put_req(prq, req); 327 return -1; 328 } 329 330 req->pr_flags = flags; 331 req->pr_rst_sz = size; 332 req->pr_version = version; 333 req->pr_scid = *scid; 334 req->pr_path.np_peer_ctx = peer_ctx; 335 memcpy(req->pr_path.np_local_addr, local_addr, 336 sizeof(req->pr_path.np_local_addr)); 337 memcpy(NP_PEER_SA(&req->pr_path), peer_addr, 338 sizeof(req->pr_path.np_peer_addr)); 339 340 LSQ_DEBUGC("scheduled %s packet for connection %"CID_FMT, 341 lsquic_preqt2str[type], CID_BITS(&req->pr_dcid)); 342 return 0; 343} 344 345 346int 347prq_new_req (struct pr_queue *prq, enum packet_req_type type, 348 const struct lsquic_packet_in *packet_in, void *peer_ctx, 349 const struct sockaddr *local_addr, const struct sockaddr *peer_addr) 350{ 351 lsquic_ver_tag_t ver_tag; 352 enum lsquic_version version; 353 enum pr_flags flags; 354 lsquic_cid_t scid; 355 356 if (packet_in->pi_flags & PI_GQUIC) 357 flags = PR_GQUIC; 358 else 359 flags = 0; 360 361 if (packet_in->pi_quic_ver) 362 { 363 memcpy(&ver_tag, packet_in->pi_data + packet_in->pi_quic_ver, 364 sizeof(ver_tag)); 365 version = lsquic_tag2ver(ver_tag); 366 } 367 else /* Got to set it to something sensible... */ 368 version = LSQVER_ID24; 369 370 lsquic_scid_from_packet_in(packet_in, &scid); 371 return lsquic_prq_new_req(prq, type, flags, version, packet_in->pi_data_sz, 372 &packet_in->pi_dcid, &scid, peer_ctx, local_addr, peer_addr); 373} 374 375 376static size_t 377max_bufsz (const struct pr_queue *prq) 378{ 379 return MAX(MAX(MAX(IQUIC_VERNEG_SIZE, 380 IQUIC_MIN_SRST_SIZE), 381 sizeof(prq->prq_verneg_g_buf)), 382 sizeof(prq->prq_pubres_g_buf)); 383} 384 385 386static struct evanescent_conn * 387get_evconn (struct pr_queue *prq) 388{ 389 struct evanescent_conn *evconn; 390 struct lsquic_conn *lconn; 391 struct lsquic_packet_out *packet_out; 392 size_t bufsz; 393 394 if (prq->prq_nconns >= prq->prq_max_conns) 395 { /* This deserves a warning */ 396 LSQ_WARN("tried to get connection past limit of %u", prq->prq_max_conns); 397 return NULL; 398 } 399 400 lconn = TAILQ_FIRST(&prq->prq_free_conns); 401 if (lconn) 402 { 403 TAILQ_REMOVE(&prq->prq_free_conns, lconn, cn_next_pr); 404 evconn = (struct evanescent_conn *) lconn; 405 evconn->evc_flags = 0; 406 return evconn; 407 } 408 409 bufsz = max_bufsz(prq); 410 evconn = calloc(1, sizeof(*evconn) + bufsz); 411 if (!evconn) 412 { 413 LSQ_WARN("calloc failed: %s", strerror(errno)); 414 return NULL; 415 } 416 417 /* These values stay the same between connection usages: */ 418 evconn->evc_queue = prq; 419 lconn = &evconn->evc_conn; 420 lconn->cn_cces = evconn->evc_cces; 421 lconn->cn_cces_mask = 1; 422 lconn->cn_n_cces = sizeof(evconn->evc_cces) / sizeof(evconn->evc_cces[0]); 423 lconn->cn_if = &evanescent_conn_iface; 424 lconn->cn_flags = LSCONN_EVANESCENT; 425 packet_out = &evconn->evc_packet_out; 426 packet_out->po_flags = PO_NOENCRYPT; 427 packet_out->po_data = evconn->evc_buf; 428 429 return evconn; 430} 431 432 433static uint8_t 434get_rand_nybble (struct pr_queue *prq) 435{ 436 uint8_t byte; 437 438 if (prq->prq_rand_nybble_off == 0) 439 RAND_bytes(prq->prq_rand_nybble_buf, sizeof(prq->prq_rand_nybble_buf)); 440 441 byte = prq->prq_rand_nybble_buf[prq->prq_rand_nybble_off / 2]; 442 if (prq->prq_rand_nybble_off & 1) 443 byte >>= 4; 444 else 445 byte &= 0xF; 446 prq->prq_rand_nybble_off = (prq->prq_rand_nybble_off + 1) & NYBBLE_MASK; 447 return byte; 448} 449 450 451static uint8_t 452get_rand_byte (struct pr_queue *prq) 453{ 454 return (get_rand_nybble(prq) << 4) | get_rand_nybble(prq); 455} 456 457 458struct lsquic_conn * 459prq_next_conn (struct pr_queue *prq) 460{ 461 struct evanescent_conn *evconn; 462 struct lsquic_conn *lconn; 463 struct lsquic_hash_elem *el; 464 struct packet_req *req; 465 struct lsquic_packet_out *packet_out; 466 int (*gen_verneg) (unsigned char *, size_t, const lsquic_cid_t *, 467 const lsquic_cid_t *, unsigned, uint8_t); 468 int len; 469 470 lconn = TAILQ_FIRST(&prq->prq_returned_conns); 471 if (lconn) 472 { 473 TAILQ_REMOVE(&prq->prq_returned_conns, lconn, cn_next_pr); 474 return lconn; 475 } 476 477 el = lsquic_hash_first(prq->prq_reqs_hash); 478 if (!el) /* Nothing is queued */ 479 return NULL; 480 481 evconn = get_evconn(prq); 482 if (!evconn) /* Reached limit or malloc failed */ 483 return NULL; 484 485 req = lsquic_hashelem_getdata(el); 486 packet_out = &evconn->evc_packet_out; 487 switch ((req->pr_type << 29) | req->pr_flags) 488 { 489 case (PACKET_REQ_VERNEG << 29) | PR_GQUIC: 490 packet_out->po_data_sz = prq->prq_verneg_g_sz; 491 packet_out->po_flags |= PO_VERNEG; 492 memcpy(packet_out->po_data, prq->prq_verneg_g_buf, 493 prq->prq_verneg_g_sz); 494 memcpy(packet_out->po_data + 1, req->pr_dcid.idbuf, GQUIC_CID_LEN); 495 break; 496 case (PACKET_REQ_PUBRES << 29) | PR_GQUIC: 497 packet_out->po_flags &= ~PO_VERNEG; 498 packet_out->po_data_sz = prq->prq_pubres_g_sz; 499 memcpy(packet_out->po_data, prq->prq_pubres_g_buf, 500 prq->prq_pubres_g_sz); 501 memcpy(packet_out->po_data + 1, req->pr_dcid.idbuf, GQUIC_CID_LEN); 502 break; 503 case (PACKET_REQ_VERNEG << 29) | 0: 504 packet_out->po_flags |= PO_VERNEG; 505 if (req->pr_version == LSQVER_046) 506 gen_verneg = lsquic_Q046_gen_ver_nego_pkt; 507 else 508 gen_verneg = lsquic_ietf_v1_gen_ver_nego_pkt; 509 len = gen_verneg(packet_out->po_data, max_bufsz(prq), 510 /* Flip SCID/DCID here: */ &req->pr_dcid, &req->pr_scid, 511 prq->prq_enpub->enp_settings.es_versions, 512 get_rand_byte(prq)); 513 if (len > 0) 514 packet_out->po_data_sz = len; 515 else 516 packet_out->po_data_sz = 0; 517 break; 518 default: 519 packet_out->po_flags &= ~PO_VERNEG; 520 packet_out->po_data_sz = req->pr_rst_sz; 521 RAND_bytes(packet_out->po_data, req->pr_rst_sz - IQUIC_SRESET_TOKEN_SZ); 522 packet_out->po_data[0] &= ~0x80; 523 packet_out->po_data[0] |= 0x40; 524 lsquic_tg_generate_sreset(prq->prq_enpub->enp_tokgen, &req->pr_dcid, 525 packet_out->po_data + req->pr_rst_sz - IQUIC_SRESET_TOKEN_SZ); 526 break; 527 } 528 529 lsquic_hash_erase(prq->prq_reqs_hash, el); 530 evconn->evc_req = req; 531 532 lconn= &evconn->evc_conn; 533 evconn->evc_cces[0].cce_cid = req->pr_dcid; 534 packet_out->po_path = &req->pr_path; 535 536 ++prq->prq_nconns; 537 return lconn; 538} 539 540 541int 542prq_have_pending (const struct pr_queue *prq) 543{ 544 return lsquic_hash_count(prq->prq_reqs_hash) > 0; 545} 546 547 548static struct lsquic_packet_out * 549evanescent_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size) 550{ 551 struct evanescent_conn *const evconn = (struct evanescent_conn *) lconn; 552 assert(size == 0); 553 return &evconn->evc_packet_out; 554} 555 556 557static void 558prq_free_conn (struct pr_queue *prq, struct lsquic_conn *lconn) 559{ 560 struct evanescent_conn *const evconn = (struct evanescent_conn *) lconn; 561 562 TAILQ_INSERT_HEAD(&prq->prq_free_conns, lconn, cn_next_pr); 563 put_req(prq, evconn->evc_req); 564 --prq->prq_nconns; 565} 566 567 568static void 569evanescent_conn_ci_packet_sent (struct lsquic_conn *lconn, 570 struct lsquic_packet_out *packet_out) 571{ 572 struct evanescent_conn *const evconn = (struct evanescent_conn *) lconn; 573 struct pr_queue *const prq = evconn->evc_queue; 574 575 assert(packet_out == &evconn->evc_packet_out); 576 assert(prq->prq_nconns > 0); 577 578 LSQ_DEBUGC("sent %s packet for connection %"CID_FMT"; free resources", 579 lsquic_preqt2str[ evconn->evc_req->pr_type ], 580 CID_BITS(&evconn->evc_req->pr_dcid)); 581 prq_free_conn(prq, lconn); 582} 583 584 585static void 586evanescent_conn_ci_packet_not_sent (struct lsquic_conn *lconn, 587 struct lsquic_packet_out *packet_out) 588{ 589 struct evanescent_conn *const evconn = (struct evanescent_conn *) lconn; 590 struct pr_queue *const prq = evconn->evc_queue; 591 592 assert(packet_out == &evconn->evc_packet_out); 593 assert(prq->prq_nconns > 0); 594 595 if (evconn->evc_flags & EVC_DROP) 596 { 597 LSQ_DEBUGC("packet not sent; drop connection %"CID_FMT, 598 CID_BITS(&evconn->evc_req->pr_dcid)); 599 prq_free_conn(prq, lconn); 600 } 601 else 602 { 603 LSQ_DEBUG("packet not sent; put connection onto used list"); 604 TAILQ_INSERT_HEAD(&prq->prq_returned_conns, lconn, cn_next_pr); 605 } 606} 607 608 609static enum tick_st 610evanescent_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now) 611{ 612 assert(0); 613 return TICK_CLOSE; 614} 615 616 617static void 618evanescent_conn_ci_destroy (struct lsquic_conn *lconn) 619{ 620 assert(0); 621} 622 623 624static struct lsquic_engine * 625evanescent_conn_ci_get_engine (struct lsquic_conn *lconn) 626{ 627 assert(0); 628 return NULL; 629} 630 631 632static void 633evanescent_conn_ci_hsk_done (struct lsquic_conn *lconn, 634 enum lsquic_hsk_status status) 635{ 636 assert(0); 637} 638 639 640static void 641evanescent_conn_ci_packet_in (struct lsquic_conn *lconn, 642 struct lsquic_packet_in *packet_in) 643{ 644 assert(0); 645} 646 647 648static void 649evanescent_conn_ci_client_call_on_new (struct lsquic_conn *lconn) 650{ 651 assert(0); 652} 653 654 655static struct network_path * 656evanescent_conn_ci_get_path (struct lsquic_conn *lconn, 657 const struct sockaddr *sa) 658{ 659 struct evanescent_conn *const evconn = (struct evanescent_conn *) lconn; 660 661 return &evconn->evc_req->pr_path; 662} 663 664 665static unsigned char 666evanescent_conn_ci_record_addrs (struct lsquic_conn *lconn, void *peer_ctx, 667 const struct sockaddr *local_sa, const struct sockaddr *peer_sa) 668{ 669 assert(0); 670 return 0; 671} 672 673 674static const struct conn_iface evanescent_conn_iface = { 675 .ci_client_call_on_new = evanescent_conn_ci_client_call_on_new, 676 .ci_destroy = evanescent_conn_ci_destroy, 677 .ci_get_engine = evanescent_conn_ci_get_engine, 678 .ci_get_path = evanescent_conn_ci_get_path, 679 .ci_hsk_done = evanescent_conn_ci_hsk_done, 680 .ci_next_packet_to_send = evanescent_conn_ci_next_packet_to_send, 681 .ci_packet_in = evanescent_conn_ci_packet_in, 682 .ci_packet_not_sent = evanescent_conn_ci_packet_not_sent, 683 .ci_packet_sent = evanescent_conn_ci_packet_sent, 684 .ci_record_addrs = evanescent_conn_ci_record_addrs, 685 .ci_tick = evanescent_conn_ci_tick, 686}; 687 688 689const char *const lsquic_preqt2str[] = 690{ 691 [PACKET_REQ_VERNEG] = "version negotiation", 692 [PACKET_REQ_PUBRES] = "stateless reset", 693}; 694 695 696void 697lsquic_prq_drop (struct lsquic_conn *lconn) 698{ 699 struct evanescent_conn *const evconn = (void *) lconn; 700 701 evconn->evc_flags |= EVC_DROP; 702 LSQ_DEBUGC("mark for connection %"CID_FMT" for dropping", 703 CID_BITS(&evconn->evc_req->pr_dcid)); 704} 705