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