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