1 /*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2021 Tatsuhiro Tsujikawa
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25 #include "shrpx_quic_connection_handler.h"
26
27 #include <openssl/rand.h>
28
29 #include <ngtcp2/ngtcp2.h>
30 #include <ngtcp2/ngtcp2_crypto.h>
31
32 #include "shrpx_worker.h"
33 #include "shrpx_client_handler.h"
34 #include "shrpx_log.h"
35 #include "shrpx_http3_upstream.h"
36 #include "shrpx_connection_handler.h"
37 #include "ssl_compat.h"
38
39 namespace shrpx {
40
41 namespace {
stateless_reset_bucket_regen_timercb(struct ev_loop * loop,ev_timer * w,int revents)42 void stateless_reset_bucket_regen_timercb(struct ev_loop *loop, ev_timer *w,
43 int revents) {
44 auto quic_conn_handler = static_cast<QUICConnectionHandler *>(w->data);
45
46 quic_conn_handler->on_stateless_reset_bucket_regen();
47 }
48 } // namespace
49
QUICConnectionHandler(Worker * worker)50 QUICConnectionHandler::QUICConnectionHandler(Worker *worker)
51 : worker_{worker},
52 stateless_reset_bucket_{SHRPX_QUIC_STATELESS_RESET_BURST} {
53 ev_timer_init(&stateless_reset_bucket_regen_timer_,
54 stateless_reset_bucket_regen_timercb, 0., 1.);
55 stateless_reset_bucket_regen_timer_.data = this;
56 }
57
~QUICConnectionHandler()58 QUICConnectionHandler::~QUICConnectionHandler() {
59 ev_timer_stop(worker_->get_loop(), &stateless_reset_bucket_regen_timer_);
60 }
61
handle_packet(const UpstreamAddr * faddr,const Address & remote_addr,const Address & local_addr,const ngtcp2_pkt_info & pi,std::span<const uint8_t> data)62 int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr,
63 const Address &remote_addr,
64 const Address &local_addr,
65 const ngtcp2_pkt_info &pi,
66 std::span<const uint8_t> data) {
67 int rv;
68 ngtcp2_version_cid vc;
69
70 rv = ngtcp2_pkt_decode_version_cid(&vc, data.data(), data.size(),
71 SHRPX_QUIC_SCIDLEN);
72 switch (rv) {
73 case 0:
74 break;
75 case NGTCP2_ERR_VERSION_NEGOTIATION:
76 send_version_negotiation(faddr, vc.version, {vc.dcid, vc.dcidlen},
77 {vc.scid, vc.scidlen}, remote_addr, local_addr);
78
79 return 0;
80 default:
81 return 0;
82 }
83
84 auto config = get_config();
85
86 ngtcp2_cid dcid_key;
87 ngtcp2_cid_init(&dcid_key, vc.dcid, vc.dcidlen);
88
89 auto conn_handler = worker_->get_connection_handler();
90
91 ClientHandler *handler;
92
93 auto &quicconf = config->quic;
94
95 auto it = connections_.find(dcid_key);
96 if (it == std::end(connections_)) {
97 auto cwit = close_waits_.find(dcid_key);
98 if (cwit != std::end(close_waits_)) {
99 auto cw = (*cwit).second;
100
101 cw->handle_packet(faddr, remote_addr, local_addr, pi, data);
102
103 return 0;
104 }
105
106 if (data[0] & 0x80) {
107 if (generate_quic_hashed_connection_id(dcid_key, remote_addr, local_addr,
108 dcid_key) != 0) {
109 return 0;
110 }
111
112 it = connections_.find(dcid_key);
113 if (it == std::end(connections_)) {
114 auto cwit = close_waits_.find(dcid_key);
115 if (cwit != std::end(close_waits_)) {
116 auto cw = (*cwit).second;
117
118 cw->handle_packet(faddr, remote_addr, local_addr, pi, data);
119
120 return 0;
121 }
122 }
123 }
124 }
125
126 if (it == std::end(connections_)) {
127 ConnectionID decrypted_dcid;
128
129 auto &qkms = conn_handler->get_quic_keying_materials();
130 const QUICKeyingMaterial *qkm = nullptr;
131
132 if (vc.dcidlen == SHRPX_QUIC_SCIDLEN) {
133 qkm = select_quic_keying_material(
134 *qkms.get(), vc.dcid[0] & SHRPX_QUIC_DCID_KM_ID_MASK);
135
136 if (decrypt_quic_connection_id(decrypted_dcid,
137 vc.dcid + SHRPX_QUIC_CID_WORKER_ID_OFFSET,
138 qkm->cid_decryption_ctx) != 0) {
139 return 0;
140 }
141
142 if (qkm != &qkms->keying_materials.front() ||
143 decrypted_dcid.worker != worker_->get_worker_id()) {
144 auto quic_lwp =
145 conn_handler->match_quic_lingering_worker_process_worker_id(
146 decrypted_dcid.worker);
147 if (quic_lwp) {
148 if (conn_handler->forward_quic_packet_to_lingering_worker_process(
149 quic_lwp, remote_addr, local_addr, pi, data) == 0) {
150 return 0;
151 }
152
153 return 0;
154 }
155 }
156 }
157
158 // new connection
159
160 auto &upstreamconf = config->conn.upstream;
161 if (worker_->get_worker_stat()->num_connections >=
162 upstreamconf.worker_connections) {
163 if (LOG_ENABLED(INFO)) {
164 LOG(INFO) << "Too many connections >="
165 << upstreamconf.worker_connections;
166 }
167
168 return 0;
169 }
170
171 ngtcp2_pkt_hd hd;
172 ngtcp2_cid odcid, *podcid = nullptr;
173 std::span<const uint8_t> token;
174 ngtcp2_token_type token_type = NGTCP2_TOKEN_TYPE_UNKNOWN;
175
176 switch (ngtcp2_accept(&hd, data.data(), data.size())) {
177 case 0: {
178 // If we get Initial and it has the Worker ID of this worker, it
179 // is likely that client is intentionally use the prefix. Just
180 // drop it.
181 if (vc.dcidlen == SHRPX_QUIC_SCIDLEN) {
182 if (qkm != &qkms->keying_materials.front()) {
183 qkm = &qkms->keying_materials.front();
184
185 if (decrypt_quic_connection_id(
186 decrypted_dcid, vc.dcid + SHRPX_QUIC_CID_WORKER_ID_OFFSET,
187 qkm->cid_decryption_ctx) != 0) {
188 return 0;
189 }
190 }
191
192 if (decrypted_dcid.worker == worker_->get_worker_id()) {
193 return 0;
194 }
195 }
196
197 if (worker_->get_graceful_shutdown()) {
198 send_connection_close(faddr, hd.version, hd.dcid, hd.scid, remote_addr,
199 local_addr, NGTCP2_CONNECTION_REFUSED,
200 data.size() * 3);
201 return 0;
202 }
203
204 if (hd.tokenlen == 0) {
205 if (quicconf.upstream.require_token) {
206 send_retry(faddr, vc.version, {vc.dcid, vc.dcidlen},
207 {vc.scid, vc.scidlen}, remote_addr, local_addr,
208 data.size() * 3);
209
210 return 0;
211 }
212
213 break;
214 }
215
216 switch (hd.token[0]) {
217 case NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY: {
218 if (vc.dcidlen != SHRPX_QUIC_SCIDLEN) {
219 // Initial packets with Retry token must have DCID chosen by
220 // server.
221 return 0;
222 }
223
224 auto qkm = select_quic_keying_material(
225 *qkms.get(), vc.dcid[0] & SHRPX_QUIC_DCID_KM_ID_MASK);
226
227 if (verify_retry_token(odcid, {hd.token, hd.tokenlen}, hd.version,
228 hd.dcid, &remote_addr.su.sa, remote_addr.len,
229 qkm->secret) != 0) {
230 if (LOG_ENABLED(INFO)) {
231 LOG(INFO) << "Failed to validate Retry token from remote="
232 << util::to_numeric_addr(&remote_addr);
233 }
234
235 // 2nd Retry packet is not allowed, so send CONNECTION_CLOSE
236 // with INVALID_TOKEN.
237 send_connection_close(faddr, hd.version, hd.dcid, hd.scid,
238 remote_addr, local_addr, NGTCP2_INVALID_TOKEN,
239 data.size() * 3);
240 return 0;
241 }
242
243 if (LOG_ENABLED(INFO)) {
244 LOG(INFO) << "Successfully validated Retry token from remote="
245 << util::to_numeric_addr(&remote_addr);
246 }
247
248 podcid = &odcid;
249 token = {hd.token, hd.tokenlen};
250 token_type = NGTCP2_TOKEN_TYPE_RETRY;
251
252 break;
253 }
254 case NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR: {
255 // If a token is a regular token, it must be at least
256 // NGTCP2_MIN_INITIAL_DCIDLEN bytes long.
257 if (vc.dcidlen < NGTCP2_MIN_INITIAL_DCIDLEN) {
258 return 0;
259 }
260
261 if (hd.tokenlen != NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN + 1) {
262 if (LOG_ENABLED(INFO)) {
263 LOG(INFO) << "Failed to validate token from remote="
264 << util::to_numeric_addr(&remote_addr);
265 }
266
267 if (quicconf.upstream.require_token) {
268 send_retry(faddr, vc.version, {vc.dcid, vc.dcidlen},
269 {vc.scid, vc.scidlen}, remote_addr, local_addr,
270 data.size() * 3);
271
272 return 0;
273 }
274
275 break;
276 }
277
278 auto qkm = select_quic_keying_material(
279 *qkms.get(), hd.token[NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN]);
280
281 if (verify_token({hd.token, hd.tokenlen}, &remote_addr.su.sa,
282 remote_addr.len, qkm->secret) != 0) {
283 if (LOG_ENABLED(INFO)) {
284 LOG(INFO) << "Failed to validate token from remote="
285 << util::to_numeric_addr(&remote_addr);
286 }
287
288 if (quicconf.upstream.require_token) {
289 send_retry(faddr, vc.version, {vc.dcid, vc.dcidlen},
290 {vc.scid, vc.scidlen}, remote_addr, local_addr,
291 data.size() * 3);
292
293 return 0;
294 }
295
296 break;
297 }
298
299 if (LOG_ENABLED(INFO)) {
300 LOG(INFO) << "Successfully validated token from remote="
301 << util::to_numeric_addr(&remote_addr);
302 }
303
304 token = {hd.token, hd.tokenlen};
305 token_type = NGTCP2_TOKEN_TYPE_NEW_TOKEN;
306
307 break;
308 }
309 default:
310 if (quicconf.upstream.require_token) {
311 send_retry(faddr, vc.version, {vc.dcid, vc.dcidlen},
312 {vc.scid, vc.scidlen}, remote_addr, local_addr,
313 data.size() * 3);
314
315 return 0;
316 }
317
318 break;
319 }
320
321 break;
322 }
323 default:
324 if (!(data[0] & 0x80) && vc.dcidlen == SHRPX_QUIC_SCIDLEN &&
325 decrypted_dcid.worker != worker_->get_worker_id()) {
326 if (!config->single_thread && conn_handler->forward_quic_packet(
327 faddr, remote_addr, local_addr, pi,
328 decrypted_dcid.worker, data) == 0) {
329 return 0;
330 }
331
332 if (data.size() >= SHRPX_QUIC_SCIDLEN + 22) {
333 send_stateless_reset(faddr, data.size(), {vc.dcid, vc.dcidlen},
334 remote_addr, local_addr);
335 }
336 }
337
338 return 0;
339 }
340
341 handler = handle_new_connection(faddr, remote_addr, local_addr, hd, podcid,
342 token, token_type);
343 if (handler == nullptr) {
344 return 0;
345 }
346 } else {
347 handler = (*it).second;
348 }
349
350 if (handler->read_quic(faddr, remote_addr, local_addr, pi, data) != 0) {
351 delete handler;
352 return 0;
353 }
354
355 handler->signal_write();
356
357 return 0;
358 }
359
handle_new_connection(const UpstreamAddr * faddr,const Address & remote_addr,const Address & local_addr,const ngtcp2_pkt_hd & hd,const ngtcp2_cid * odcid,std::span<const uint8_t> token,ngtcp2_token_type token_type)360 ClientHandler *QUICConnectionHandler::handle_new_connection(
361 const UpstreamAddr *faddr, const Address &remote_addr,
362 const Address &local_addr, const ngtcp2_pkt_hd &hd, const ngtcp2_cid *odcid,
363 std::span<const uint8_t> token, ngtcp2_token_type token_type) {
364 std::array<char, NI_MAXHOST> host;
365 std::array<char, NI_MAXSERV> service;
366 int rv;
367
368 rv = getnameinfo(&remote_addr.su.sa, remote_addr.len, host.data(),
369 host.size(), service.data(), service.size(),
370 NI_NUMERICHOST | NI_NUMERICSERV);
371 if (rv != 0) {
372 LOG(ERROR) << "getnameinfo() failed: " << gai_strerror(rv);
373
374 return nullptr;
375 }
376
377 auto ssl_ctx = worker_->get_quic_sv_ssl_ctx();
378
379 assert(ssl_ctx);
380
381 auto ssl = tls::create_ssl(ssl_ctx);
382 if (ssl == nullptr) {
383 return nullptr;
384 }
385
386 #ifdef NGHTTP2_GENUINE_OPENSSL
387 assert(SSL_is_quic(ssl));
388 #endif // NGHTTP2_GENUINE_OPENSSL
389
390 SSL_set_accept_state(ssl);
391
392 auto config = get_config();
393 auto &quicconf = config->quic;
394
395 if (quicconf.upstream.early_data) {
396 #ifdef NGHTTP2_GENUINE_OPENSSL
397 SSL_set_quic_early_data_enabled(ssl, 1);
398 #elif defined(NGHTTP2_OPENSSL_IS_BORINGSSL)
399 SSL_set_early_data_enabled(ssl, 1);
400 #endif // NGHTTP2_OPENSSL_IS_BORINGSSL
401 }
402
403 // Disable TLS session ticket if we don't have working ticket
404 // keys.
405 if (!worker_->get_ticket_keys()) {
406 SSL_set_options(ssl, SSL_OP_NO_TICKET);
407 }
408
409 auto handler = std::make_unique<ClientHandler>(
410 worker_, faddr->fd, ssl, StringRef{host.data()},
411 StringRef{service.data()}, remote_addr.su.sa.sa_family, faddr);
412
413 auto upstream = std::make_unique<Http3Upstream>(handler.get());
414 if (upstream->init(faddr, remote_addr, local_addr, hd, odcid, token,
415 token_type) != 0) {
416 return nullptr;
417 }
418
419 handler->setup_http3_upstream(std::move(upstream));
420
421 return handler.release();
422 }
423
424 namespace {
generate_reserved_version(const Address & addr,uint32_t version)425 uint32_t generate_reserved_version(const Address &addr, uint32_t version) {
426 uint32_t h = 0x811C9DC5u;
427 const uint8_t *p = reinterpret_cast<const uint8_t *>(&addr.su.sa);
428 const uint8_t *ep = p + addr.len;
429
430 for (; p != ep; ++p) {
431 h ^= *p;
432 h *= 0x01000193u;
433 }
434
435 version = htonl(version);
436 p = (const uint8_t *)&version;
437 ep = p + sizeof(version);
438
439 for (; p != ep; ++p) {
440 h ^= *p;
441 h *= 0x01000193u;
442 }
443
444 h &= 0xf0f0f0f0u;
445 h |= 0x0a0a0a0au;
446
447 return h;
448 }
449 } // namespace
450
send_retry(const UpstreamAddr * faddr,uint32_t version,std::span<const uint8_t> ini_dcid,std::span<const uint8_t> ini_scid,const Address & remote_addr,const Address & local_addr,size_t max_pktlen)451 int QUICConnectionHandler::send_retry(
452 const UpstreamAddr *faddr, uint32_t version,
453 std::span<const uint8_t> ini_dcid, std::span<const uint8_t> ini_scid,
454 const Address &remote_addr, const Address &local_addr, size_t max_pktlen) {
455 std::array<char, NI_MAXHOST> host;
456 std::array<char, NI_MAXSERV> port;
457
458 if (getnameinfo(&remote_addr.su.sa, remote_addr.len, host.data(), host.size(),
459 port.data(), port.size(),
460 NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
461 return -1;
462 }
463
464 auto config = get_config();
465 auto &quicconf = config->quic;
466
467 auto conn_handler = worker_->get_connection_handler();
468 auto &qkms = conn_handler->get_quic_keying_materials();
469 auto &qkm = qkms->keying_materials.front();
470
471 ngtcp2_cid retry_scid;
472
473 if (generate_quic_retry_connection_id(retry_scid, quicconf.server_id, qkm.id,
474 qkm.cid_encryption_ctx) != 0) {
475 return -1;
476 }
477
478 ngtcp2_cid idcid, iscid;
479 ngtcp2_cid_init(&idcid, ini_dcid.data(), ini_dcid.size());
480 ngtcp2_cid_init(&iscid, ini_scid.data(), ini_scid.size());
481
482 std::array<uint8_t, NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN> tokenbuf;
483
484 auto token =
485 generate_retry_token(tokenbuf, version, &remote_addr.su.sa,
486 remote_addr.len, retry_scid, idcid, qkm.secret);
487 if (!token) {
488 return -1;
489 }
490
491 std::vector<uint8_t> buf;
492 buf.resize(std::min(max_pktlen, static_cast<size_t>(256)));
493
494 auto nwrite = ngtcp2_crypto_write_retry(buf.data(), buf.size(), version,
495 &iscid, &retry_scid, &idcid,
496 token->data(), token->size());
497 if (nwrite < 0) {
498 LOG(ERROR) << "ngtcp2_crypto_write_retry: " << ngtcp2_strerror(nwrite);
499 return -1;
500 }
501
502 buf.resize(nwrite);
503
504 quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len,
505 &local_addr.su.sa, local_addr.len, ngtcp2_pkt_info{}, buf,
506 buf.size());
507
508 if (generate_quic_hashed_connection_id(idcid, remote_addr, local_addr,
509 idcid) != 0) {
510 return -1;
511 }
512
513 auto d =
514 static_cast<ev_tstamp>(NGTCP2_DEFAULT_INITIAL_RTT * 3) / NGTCP2_SECONDS;
515
516 if (LOG_ENABLED(INFO)) {
517 LOG(INFO) << "Enter close-wait period " << d << "s with " << buf.size()
518 << " bytes sentinel packet";
519 }
520
521 auto cw = std::make_unique<CloseWait>(worker_, std::vector<ngtcp2_cid>{idcid},
522 std::move(buf), d);
523
524 add_close_wait(cw.release());
525
526 return 0;
527 }
528
send_version_negotiation(const UpstreamAddr * faddr,uint32_t version,std::span<const uint8_t> ini_dcid,std::span<const uint8_t> ini_scid,const Address & remote_addr,const Address & local_addr)529 int QUICConnectionHandler::send_version_negotiation(
530 const UpstreamAddr *faddr, uint32_t version,
531 std::span<const uint8_t> ini_dcid, std::span<const uint8_t> ini_scid,
532 const Address &remote_addr, const Address &local_addr) {
533 auto sv = std::to_array({
534 generate_reserved_version(remote_addr, version),
535 NGTCP2_PROTO_VER_V1,
536 });
537
538 std::array<uint8_t, NGTCP2_MAX_UDP_PAYLOAD_SIZE> buf;
539
540 uint8_t rand_byte;
541 util::random_bytes(&rand_byte, &rand_byte + 1, worker_->get_randgen());
542
543 auto nwrite = ngtcp2_pkt_write_version_negotiation(
544 buf.data(), buf.size(), rand_byte, ini_scid.data(), ini_scid.size(),
545 ini_dcid.data(), ini_dcid.size(), sv.data(), sv.size());
546 if (nwrite < 0) {
547 LOG(ERROR) << "ngtcp2_pkt_write_version_negotiation: "
548 << ngtcp2_strerror(nwrite);
549 return -1;
550 }
551
552 auto pkt = std::span{std::begin(buf), static_cast<size_t>(nwrite)};
553 return quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len,
554 &local_addr.su.sa, local_addr.len, ngtcp2_pkt_info{},
555 pkt, pkt.size());
556 }
557
send_stateless_reset(const UpstreamAddr * faddr,size_t pktlen,std::span<const uint8_t> dcid,const Address & remote_addr,const Address & local_addr)558 int QUICConnectionHandler::send_stateless_reset(const UpstreamAddr *faddr,
559 size_t pktlen,
560 std::span<const uint8_t> dcid,
561 const Address &remote_addr,
562 const Address &local_addr) {
563 if (stateless_reset_bucket_ == 0) {
564 if (LOG_ENABLED(INFO)) {
565 LOG(INFO) << "Stateless Reset bucket has been depleted";
566 }
567
568 return 0;
569 }
570
571 --stateless_reset_bucket_;
572
573 if (!ev_is_active(&stateless_reset_bucket_regen_timer_)) {
574 ev_timer_again(worker_->get_loop(), &stateless_reset_bucket_regen_timer_);
575 }
576
577 std::array<uint8_t, NGTCP2_STATELESS_RESET_TOKENLEN> token;
578 ngtcp2_cid cid;
579
580 ngtcp2_cid_init(&cid, dcid.data(), dcid.size());
581
582 auto conn_handler = worker_->get_connection_handler();
583 auto &qkms = conn_handler->get_quic_keying_materials();
584 auto &qkm = qkms->keying_materials.front();
585
586 if (auto rv = generate_quic_stateless_reset_token(
587 token.data(), cid, qkm.secret.data(), qkm.secret.size());
588 rv != 0) {
589 return -1;
590 }
591
592 // SCID + minimum expansion - NGTCP2_STATELESS_RESET_TOKENLEN
593 constexpr size_t max_rand_byteslen =
594 NGTCP2_MAX_CIDLEN + 22 - NGTCP2_STATELESS_RESET_TOKENLEN;
595
596 size_t rand_byteslen;
597
598 if (pktlen <= 43) {
599 // As per
600 // https://datatracker.ietf.org/doc/html/rfc9000#section-10.3
601 rand_byteslen = pktlen - NGTCP2_STATELESS_RESET_TOKENLEN - 1;
602 } else {
603 rand_byteslen = max_rand_byteslen;
604 }
605
606 std::array<uint8_t, max_rand_byteslen> rand_bytes;
607
608 if (RAND_bytes(rand_bytes.data(), rand_byteslen) != 1) {
609 return -1;
610 }
611
612 std::array<uint8_t, NGTCP2_MAX_UDP_PAYLOAD_SIZE> buf;
613
614 auto nwrite = ngtcp2_pkt_write_stateless_reset(
615 buf.data(), buf.size(), token.data(), rand_bytes.data(), rand_byteslen);
616 if (nwrite < 0) {
617 LOG(ERROR) << "ngtcp2_pkt_write_stateless_reset: "
618 << ngtcp2_strerror(nwrite);
619 return -1;
620 }
621
622 if (LOG_ENABLED(INFO)) {
623 LOG(INFO) << "Send stateless_reset to remote="
624 << util::to_numeric_addr(&remote_addr)
625 << " dcid=" << util::format_hex(dcid);
626 }
627
628 auto pkt = std::span{std::begin(buf), static_cast<size_t>(nwrite)};
629 return quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len,
630 &local_addr.su.sa, local_addr.len, ngtcp2_pkt_info{},
631 pkt, pkt.size());
632 }
633
send_connection_close(const UpstreamAddr * faddr,uint32_t version,const ngtcp2_cid & ini_dcid,const ngtcp2_cid & ini_scid,const Address & remote_addr,const Address & local_addr,uint64_t error_code,size_t max_pktlen)634 int QUICConnectionHandler::send_connection_close(
635 const UpstreamAddr *faddr, uint32_t version, const ngtcp2_cid &ini_dcid,
636 const ngtcp2_cid &ini_scid, const Address &remote_addr,
637 const Address &local_addr, uint64_t error_code, size_t max_pktlen) {
638 std::array<uint8_t, NGTCP2_MAX_UDP_PAYLOAD_SIZE> buf;
639
640 max_pktlen = std::min(max_pktlen, buf.size());
641
642 auto nwrite = ngtcp2_crypto_write_connection_close(
643 buf.data(), max_pktlen, version, &ini_scid, &ini_dcid, error_code,
644 nullptr, 0);
645 if (nwrite < 0) {
646 LOG(ERROR) << "ngtcp2_crypto_write_connection_close failed";
647 return -1;
648 }
649
650 if (LOG_ENABLED(INFO)) {
651 LOG(INFO) << "Send Initial CONNECTION_CLOSE with error_code=" << log::hex
652 << error_code << log::dec
653 << " to remote=" << util::to_numeric_addr(&remote_addr)
654 << " dcid="
655 << util::format_hex(std::span{ini_scid.data, ini_scid.datalen})
656 << " scid="
657 << util::format_hex(std::span{ini_dcid.data, ini_dcid.datalen});
658 }
659
660 auto pkt = std::span{std::begin(buf), static_cast<size_t>(nwrite)};
661 return quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len,
662 &local_addr.su.sa, local_addr.len, ngtcp2_pkt_info{},
663 pkt, pkt.size());
664 }
665
add_connection_id(const ngtcp2_cid & cid,ClientHandler * handler)666 void QUICConnectionHandler::add_connection_id(const ngtcp2_cid &cid,
667 ClientHandler *handler) {
668 connections_.emplace(cid, handler);
669 }
670
remove_connection_id(const ngtcp2_cid & cid)671 void QUICConnectionHandler::remove_connection_id(const ngtcp2_cid &cid) {
672 connections_.erase(cid);
673 }
674
add_close_wait(CloseWait * cw)675 void QUICConnectionHandler::add_close_wait(CloseWait *cw) {
676 for (auto &cid : cw->scids) {
677 close_waits_.emplace(cid, cw);
678 }
679 }
680
remove_close_wait(const CloseWait * cw)681 void QUICConnectionHandler::remove_close_wait(const CloseWait *cw) {
682 for (auto &cid : cw->scids) {
683 close_waits_.erase(cid);
684 }
685 }
686
on_stateless_reset_bucket_regen()687 void QUICConnectionHandler::on_stateless_reset_bucket_regen() {
688 assert(stateless_reset_bucket_ < SHRPX_QUIC_STATELESS_RESET_BURST);
689
690 if (++stateless_reset_bucket_ == SHRPX_QUIC_STATELESS_RESET_BURST) {
691 ev_timer_stop(worker_->get_loop(), &stateless_reset_bucket_regen_timer_);
692 }
693 }
694
close_wait_timeoutcb(struct ev_loop * loop,ev_timer * w,int revents)695 static void close_wait_timeoutcb(struct ev_loop *loop, ev_timer *w,
696 int revents) {
697 auto cw = static_cast<CloseWait *>(w->data);
698
699 if (LOG_ENABLED(INFO)) {
700 LOG(INFO) << "close-wait period finished";
701 }
702
703 auto quic_conn_handler = cw->worker->get_quic_connection_handler();
704 quic_conn_handler->remove_close_wait(cw);
705
706 delete cw;
707 }
708
CloseWait(Worker * worker,std::vector<ngtcp2_cid> scids,std::vector<uint8_t> pkt,ev_tstamp period)709 CloseWait::CloseWait(Worker *worker, std::vector<ngtcp2_cid> scids,
710 std::vector<uint8_t> pkt, ev_tstamp period)
711 : worker{worker},
712 scids{std::move(scids)},
713 pkt{std::move(pkt)},
714 bytes_recv{0},
715 bytes_sent{0},
716 num_pkts_recv{0},
717 next_pkts_recv{1} {
718 ++worker->get_worker_stat()->num_close_waits;
719
720 ev_timer_init(&timer, close_wait_timeoutcb, period, 0.);
721 timer.data = this;
722
723 ev_timer_start(worker->get_loop(), &timer);
724 }
725
~CloseWait()726 CloseWait::~CloseWait() {
727 auto loop = worker->get_loop();
728
729 ev_timer_stop(loop, &timer);
730
731 auto worker_stat = worker->get_worker_stat();
732 --worker_stat->num_close_waits;
733
734 if (worker->get_graceful_shutdown() && worker_stat->num_connections == 0 &&
735 worker_stat->num_close_waits == 0) {
736 ev_break(loop);
737 }
738 }
739
handle_packet(const UpstreamAddr * faddr,const Address & remote_addr,const Address & local_addr,const ngtcp2_pkt_info & pi,std::span<const uint8_t> data)740 int CloseWait::handle_packet(const UpstreamAddr *faddr,
741 const Address &remote_addr,
742 const Address &local_addr,
743 const ngtcp2_pkt_info &pi,
744 std::span<const uint8_t> data) {
745 if (pkt.empty()) {
746 return 0;
747 }
748
749 ++num_pkts_recv;
750 bytes_recv += data.size();
751
752 if (bytes_sent + pkt.size() > 3 * bytes_recv ||
753 next_pkts_recv > num_pkts_recv) {
754 return 0;
755 }
756
757 auto rv = quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len,
758 &local_addr.su.sa, local_addr.len,
759 ngtcp2_pkt_info{}, pkt, pkt.size());
760 if (rv != 0) {
761 return -1;
762 }
763
764 next_pkts_recv *= 2;
765 bytes_sent += pkt.size();
766
767 return 0;
768 }
769
770 } // namespace shrpx
771