From 9344101f186b830465259e0a5445cfd52c7da03b Mon Sep 17 00:00:00 2001 From: Masakazu Kitajo Date: Tue, 14 Jul 2020 11:36:02 +0900 Subject: [PATCH] Squashed commit of the following: commit a1952d8a53feb66b0722d0dca252e098c9320f0a Author: Masakazu Kitajo Date: Tue Jul 14 11:14:45 2020 +0900 Comment out an unused const value commit f7893992358606565c156e6f9964fa02f5425e87 Author: Masakazu Kitajo Date: Tue Jul 14 11:06:57 2020 +0900 Don't process write_vio on initialization if there is no data commit d7d00c2b838c46b761ac9dcd72fa5ae391a8ab19 Merge: 00d78836a 89b6b91d4 Author: Masakazu Kitajo Date: Tue Jul 14 10:19:18 2020 +0900 Merge branch 'master' into quic-latest * master: Test and fix connection timeout and retries (#6897) Improve client_vc tracking (#6889) Open UDP ports on traffic_manager if ports are configured for QUIC (#6808) Issue 6847 Fixing documentation for secondary_mode (#6851) Add maxmind acl plugin (#6980) fix leak in early data (#6957) Traffic Dump: dump server-side protocol stack (#6972) Preserve cert name through ssl vc migration (#6977) Adds null check (#6994) hostdb: don't use next_sync_time - now() as TTL (it can be negative) (#6979) commit 00d78836a62346511dd1ffecde4a2d067bf9b196 Author: Masakazu Kitajo Date: Mon Jul 13 16:21:45 2020 +0900 Update tests commit 32fb67f89e8314479b1457e83a278bd46edd0e94 Merge: fbc8d4a67 da888769f Author: Masakazu Kitajo Date: Fri Jul 10 11:27:30 2020 +0900 Merge branch 'master' into quic-latest * master: Fix a build issue with BoringSSL (#6988) Update autest to version 1.8.0. (#6981) Updates path to reflect new location in tree (#6993) Fix memory leak in header_rewrite (#6986) Fix typo in Http2ConnectionState (#6991) Fixes use after free when boringssl is used (#6985) Fix out of source tree builds for QUIC (#6984) remap doc correction (#6974) Use print mode with length in validate_sni debug (#6976) Assert non-zero HdrHeap object size (#6954) AuTest: Properly handle experimental plugins. (#6971) Add new test and fix for float configuration failure in conf_remap (#6967) Cleanup: Break down HpackIndexingTable::lookup() into static table lookup & dynamic table lookup (#6509) Perf: Use LocalBuffer in HTTP/2 (#6536) RateLimiting and Connection Config changes (#6968) Update docs for some DNS config settings (#6969) Log whether client certs were exchanged in TLS handshake (#6699) Fix support for openssl async engine (#6910) Make the log_pipe test more efficient. (#6966) Cleanup: Simplify Http2Stream::update_write_request() (#6962) Assert on valid boundaries for UserArgTable access (#6953) Fix format string for int64_t (#6963) Fixes icap build on macOS (#6958) Metrics for origin close (#6873) Cleanup: Remove unnecessary member from Http2Stream (#6951) Prevent buffer overflow during log filter actions (#6950) Make HostDBInfo class safer to use. (#6858) Remove two unused includes in HdrHeap.h. (#6905) Set the default thread count factor to 1x the number of logical cores (#6949) Build test C/C++ files with Automake. (#6945) remove useless code (#6952) Add CI verbose and debug options for autest (#6947) Remove dup code in QUICMultiCertConfigLoader (#6942) Retry read when fastopen_bread() get non fatal error (#6841) Cleanup: Remove unused arguments (#6943) AuTest: New log and When condition for ATS initialized (#6931) Add IPCAP Plugin (#6484) Load combined file with bogus key path (#6933) plugins: Move to blocklists and allowlists (#6940) Move to blocklists and allowlists (#6941) Prevent use-after-free of TransactionPlugin (#6937) Update buffer-writer.en.rst (wrong header link) (#6934) Fixed bug in the calculation of the header block fragment length (#6923) Disable max_connections_active_in default now that featur works (#6903) Make compress Au test less flakey. (#6915) Enable only squash and merge for GitHub example: Move to blocklists and allowlists Fix dual_cert_select test to run with older openssl binary (#6896) Prevent stale netvc access on SSL Callbacks (#6925) commit fbc8d4a670026d1fbc38c00107d6f06c283d9852 Author: scw00 <616955249@qq.com> Date: Fri Jun 26 19:56:58 2020 +0800 Document qlog_dir configuration (#6935) commit 5d3cecf6b5fc0820f6b3cb608caa2a9d31345a4b Author: Masakazu Kitajo Date: Fri Jun 26 10:22:52 2020 +0900 Update tests commit 8d09233a75eb5d34097c17ccf7ed69be1bea6440 Author: Masakazu Kitajo Date: Mon Jun 22 11:36:09 2020 +0900 clang-format commit 4f54576cbc56e630b6104e1a52d4c3b9a52aa76e Merge: 5c50ff382 763aa8e14 Author: Masakazu Kitajo Date: Mon Jun 22 11:34:21 2020 +0900 Merge branch 'master' into quic-latest * master: Make QPACK.h self-contained build: Require OCSP headers for OCSP-enablement Fix old MIMEHdr handling of HPACK Customize Max IOBuffer Size (#6869) Ensure read_avail is set for the first non-empty block (#6916) Removes SSLNetVConnection::sslContextSet Disable lua_stats autest until we can reliably wait for stats set sni_name with remapped origin name if sni_policy is not the default value (#6898) Make h2spec test more resiliant by extending timeout (#6891) Make Http2ClientSession inactive on EOS event Fix assert when client aborts during backfill (#6809) Traffic Dump: Add server response HTTP version (#6856) AuTest: Pipfile update to use microserver 1.0.5 (#6893) Fix compiler issue with ICC 19.1 change overridable var type for proxy.config.http.server_session_sharing.match from int to string (#6822) Fix the relative path for template_sets_dir to be install directory (#6203) microserver error handling: SSLError check and debug. (#6884) Track thread changes during origin connect and cache open write (#6872) Fix for Ubuntu 16 and Clang 5 AuTest: port selection improvements. (#6888) Revert "Avoid stale client_vc (#6732)" (#6879) Return null when do_io_write called on closed stream (#6826) Handle immediate as inactivity timeout (#6689) Avoid stale client_vc (#6732) Protect against nullptr access during SSL Callback (#6866) avoid dynamic_cast to get Pi-tag for non_internal requests (#6868) Adding HTTP status 451 in apidefs as well (See PR#6789) (#6797) Fix session pool to add and fetch to beginning of hash chain rather than end (#6805) Conflicts: iocore/net/quic/QUICPacketPayloadProtector.cc commit 5c50ff382560a22fab2e6ac65d5dcaa2d528247a Author: scw00 Date: Tue Jun 16 09:17:15 2020 +0800 make compiler happy commit 344ede3200f4de10f6319fc58a49c18abda09d6f Author: scw00 Date: Fri Jun 12 10:53:31 2020 +0800 Chang qlog_file configuration to qlog_dir commit 511ac7ecc1b95b445e432f7c1e0b0c3094217aa3 Author: scw00 Date: Sun Jun 7 18:43:37 2020 +0800 make qlog configurable commit 1a5669a48defbb39a3059021e397c05e9a5dc05f Author: scw00 Date: Fri Jun 5 15:58:23 2020 +0800 add metrics update commit a126b84f4318c52cdb8e6b81d239ed505ade6bce Author: scw00 Date: Thu Jun 4 17:48:32 2020 +0800 remove useless INCLUDE_YAML commit 82b51076ad0f28b1df8a6b367297643cfbc72504 Author: scw00 Date: Thu Jun 4 17:40:32 2020 +0800 make qlog configurable commit da958f1c91f7a1ed7af472eedad4b3da55981f5e Author: scw00 Date: Mon May 25 13:54:49 2020 +0800 QUIC: add qlog support commit a2e788b8b5337a7225a456df56de853fece7e17c Merge: db1a85c5a 02a60b200 Author: Masakazu Kitajo Date: Tue Jun 9 11:44:47 2020 +0900 Merge branch 'master' into quic-latest * master: Fix a crash on TLS resumption Make format specifier for time_t portable Fix pointer overflow in XPACK Add include so BROTLI constants are available to plugin (#6862) We should remove whitespace from fieldname in http response due to RFC7230:3.2.4(#6793) Fix missing virtual destructor in TLSSessionResumptionSupport. (#6812) Generalize KA check logic LGTM: fix hiding a global variable with the same name LGTM: add header guard Removes refcounting from compress and s3_auth plugins Schedule Transform on the same thread as the continuation (#6843) traffic_dump: debug_tag and lock improvements Issue 6838 Fixing the comparison in waited_enough (drain functionality) (#6839) Drastically improve generator.so performance for /nocache (#6834) Slice plugin: recover out of sync slices, better handling of non 206s commit db1a85c5a029260761e65f86917c2c583bd33583 Author: Masakazu Kitajo Date: Thu Jun 4 14:08:07 2020 +0900 Add support for BoringSSL API version 10 (QUIC) commit 3a41eb4286b8955db80a3bcb8ef72a504dbbf558 Author: Masakazu Kitajo Date: Wed Jun 3 10:18:53 2020 +0900 Ignore QUIC Short packets during handshake commit 81d52e4c80dc3caf913527a5d817c6cec8f49fa9 Author: Masakazu Kitajo Date: Wed Jun 3 09:46:52 2020 +0900 Increment ndone with th amount of data actually read ndone was not updated correctly and it caused a problem on setting FIN flag on a QUIC stream. commit a788cc38ca803a0f3c5901b14db5f6a7ce6361e4 Author: Masakazu Kitajo Date: Tue Jun 2 11:33:40 2020 +0900 Cleanup dependency for QUIC module commit 88d4597c257c00859c29f77616c96d2090a699f1 Merge: 9c358d48e 78028cf5b Author: Masakazu Kitajo Date: Mon Jun 1 16:22:08 2020 +0900 Merge branch 'master' into quic-latest * master: Change AM_LDFLAGS to be an addition, not an overwrite, in the plugin makefile. This allows the various libpaths, and specifically the rpath, to be transferred over to plugins when used for tests. Currently separate test applications will build properly against things like a custom openssl installation, however when they run they do not know the correct location unless you have set the LD path on the system to include that directory. Passing this information around allows the ATS library rpath to also be used for the test applications so they can determine at runtime where to look for libraries (#6835) Adding a basic ip_allow test. Add CSV output as an optional format for stats_over_http (#6818) HostDB - change HostResPreferenceOrder type to std::array. cache_range_request: Overhaul and clean up the core autest. lua plugin: fix for incorrectly injecting global plugin stats Clear handling tracked events of Http2Stream Add le32toh and htole32 for macOS (#6807) Make chunked encoding test more resilient (#6827) regex_remap: Adjust regex recursion limit down due to crashes in testing. (#6819) Fix test certs in client_context_dump (#6824) Doc updates for tunnel_route and ip_allow interaction. ip_resolve - Make config variable overridable Fixes remaining memory leaks with nexthop strategy unit tests found by ASAN. This should close issue 6765 Make post_slow_server Au test work in Docker container. Add an optional ramcache setting to volume.config to be able to disable it (#6746) Fixes to hostDB to avoid event and memory leaks (#6686) Add TXN_CLOSE hook to CPPAPI TransactionPlugin (#6800) clang-analyzer: uninitialized va_list (#6798) commit 9c358d48e1791bc86e61e99114775a973b82055c Author: Masakazu Kitajo Date: Mon May 25 14:19:28 2020 +0900 Update tests TP format was changed but binaries in test cases were not updated commit 3444f4108dfb9bb7c41bc9536b26272eb7312736 Author: Masakazu Kitajo Date: Tue May 5 16:25:07 2020 +0900 Add TLSSessionResumptionSupport to QUICNetVC commit 221c5b5f37738c65ba9d1787a047eec6ba200d2a Merge: 7f9338f62 16fb8092e Author: Masakazu Kitajo Date: Wed May 20 08:36:52 2020 +0900 Merge branch 'master' into quic-latest * master: Generalize callbacks for TLS session resumption fixes issue 6765, memleak in unit test mocked Machine class in nexthop_test_stubs.cc Fix HPACK Dynamic Table Cleanup clang-analyzer: code clone in get_proxy_protocol_addr (#6791) clang-analyzer: eliminate identical conditions (#6790) Adding HTTP Status code 451 for Unavailable For Legal Reasons (RFC 7725) (#6789) HPACK: send back an error to the client when the index is invalid Use Proxy-Connection iff parent_is_proxy=true 1. Set a non-zero default value for TLS Client Handshake Timeout (#6781) Update existingh formatting with new clang-format package Updates clang-format to LLVM v10.0.0 Fixed `AddressSanitizer: odr-violation` Weird characters in debug message Add back reading config for handshake timeout (#6773) Lua plugin: add ts.server_response.get_maxage() Ensure inactivity timeout is not set when passed in timeout value is 0 (#6772) clang-analyzer: Fix dead nested assignment issues Do not fail multicert load if line does not create entry (#6760) Accept Handling VIO events SSL (#6764) Accept handling VIO events (#6763) Promote netvc to ProxySession (#6759) Add metrics to track default inactivity timed out connections (#6755) Script to find mutexes/futexes that are contending Enforce Active Connection limits (#6754) Add HttpTransact::get_max_age and TSHttpTxnGetMaxAge Make chunked_encoding test more reliable by killing nc process (#6762) Disable remap-stats test until we can reliably wait for metrics (#6761) Fix set manipulation in dual cert resolution (#6758) Correct `schedule_every_local` to schedule locally ASAN: Fixed one definition rule violation gcc10: fixed clearing an object of non-trivial type for wccp traffic_dump: refactor to make transactions atomically written Cleans up doubled words in documentation Doc: Add example for ipv4/6 on dns nameserver Doc: Remove build warning Fix typos in comments Remove unused index for SSL application specific data SSL: Introduce proxy.config.ssl.server.session_ticket.number Lua Plugin - Extend the crypto API with SHA-256 and HMAC functions. GCC 10: Update traffic_via to use string_view to avoid compile errors. GCC 10 fixes - simple fixes. Update expired test certificates for cert_update Lua plugin: add ts.server_response.is_cacheable() HttpSM cleaning up non-sense pointer indirection (#6721) python: Remove unused imports python: Fix dangerous default argument python: Remove unused variables python: Remove unnecessary comprehension Conflicts: iocore/net/UnixUDPNet.cc commit 7f9338f62e41543243fd3aff6770b25b8996ed6f Author: Masakazu Kitajo Date: Tue May 5 17:49:03 2020 +0900 Enable QUIC 0-RTT with Tatsuhiro's OpenSSL commit 42a0c656067ed117e7c729be30c7f91a744922c8 Merge: 05b7ab887 59ff1d846 Author: Masakazu Kitajo Date: Mon May 4 17:33:54 2020 +0900 Merge branch 'master' into quic-latest * master: Fix ink_endian.h to include an appropriate header file Fix a bug that current_active_client_connections doesn't decrease Extendible asan simple (#6650) Rework stats over http so that it supports both a config file and the original path parameter (#6542) Commenting EventIO methods (#6712) Document ip_allow in sni.yaml (#6723) traffic_dump: add tls information to dump. (#6727) gcc10: fixed warning about returning local variable in int64_to_str() Au test: HTTP/2 client sends POST, server delays 2 minutes, sends 200 KB. Fixup .gitignores to match repo reality put events into local queue when scheduling on the same thread as the scheduler Fix typo in DiagsConfig Updated docs for guaranteed_{min,max}_lifetime Remove tls_versions from host sni policy check remove TSContSchedule, then rename TSContScheduleOnPool to TSContSchedule slice plugin: add --include-regex, --exclude-regex parameters (#6701) Add Access log fields for ProxyProtocol Context Adding logging fields for collapsed forwarding metrics (#6708) Added more of the main sub-trees Fix lost fragments when update object Removes ATS version from gold files traffic_dump: add nullptr check for sni string (#6700) Fix crash when folloing redirect Fix deprecated-copy warning in cache tool Fixed memory leak in header unit test Increase the default max_record_entries to match the original compiled in value (#6697) commit 05b7ab8874379e527712580210b41253601e54e9 Merge: d2232313c 7320d5c53 Author: Masakazu Kitajo Date: Wed Apr 22 16:11:34 2020 +0900 Merge branch 'master' into quic-latest * master: Normalizes function names to match hook names in intercept plugins Added Apache notifications file Test PUSHing an object into the cache and the GETting it with a few variations on the client connection protocol. Fix link issue of test_libhttp2 on FreeBSD 12 with --enable-debug Traffic Dump: Adding an SNI filtering option. (#6645) code and documentation for lua states configuration and stats printing (#6571) Fixes minor memory leak in configure_net Update TSStatFindName to check that sync callback is set on the stat Fixes memory leak during log configuration Removes commented out code from example chdir into src before running git commands Added / renamed config.h -> configs.h, to avoid our .gitignore Added new flag to documentation Cleanup for consistency Add PolicyManager, basic functionality Refactor the cache_promote policies Format to match perferred if/else formatting for sh scripts Adds the 8.1 stuff that's already running and building Skip running autests for ci builds that don't have relevant changed files Skip running autests for ci builds that don't have relevant changed files Added a basic CODEOWNERS remove unnecessary Last-Modified header from tests Bug fixes to h2 buffering Added bonded interface support to system stats plugin (#6668) Fix memory leak of HPACK Fixes memory leak loading certs Check sni against SSL object (#6656) Fix origin scheme selection with partial-blind addition (#6655) traffic_top bug fixes for client connections Run httpbin as an origin server of AuTest Removing whitespace and closing file elsewhere complete change change suggested during discussion close the file after use Augment autest to exercise combined cert and key file Optimize HTTPHdr conversion of HTTP/1.1 to HTTP/2 Convert HTTP/2 regression tests to use Catch Introduce LocalBuffer Add hashname to the configuration definition (#6647) Adds important config notes for TLS v1/1.1 (#6646) Add docs for memory leak detection configs Add more options to session_sharing.match (#6566) Fixes crash loading combined(cert+key) certs traffic_dump: don't dump cookies from the wire (#6586) Adding a log pipe buffer size test. Adds partial_blind_route sni action Make next_cs_id a private member variable. Use default rwlock attributes on initialize Fix migrate use after free (#6578) Add a new log tag % AppVersionInfo.BldNumStr s3_auth_v4: multiple same name fields signing fix Fix ASAN detected crash in test_LogUtils. This is cause by double linking BufferWriterFormat.cc, so don't do that. Fixes a bug where the nexthop markNextHop method to mark a host down is not called when because the wrapper function was not used. SNI: Add support to match/replace captured group from fqdn into tunnel_route. (#6613) Add ready checks for the microserver and ATS test instances. (#6625) Updated ink_rwlock to be a real reader writer lock Patch to catch and invalidate an HTTP asset with negative bytes after cache_seek. Fix missing virtual destructor for PluginUserArgsMixin. Don't be overly aggressive on stream failures and closing commit d2232313ce0d6baa2fb746b82596916256c72a69 Merge: 69713f57f ba98187ef Author: Masakazu Kitajo Date: Tue Apr 7 12:45:14 2020 +0900 Merge branch 'master' into quic-latest * master: Remove configure option --max-api-stats which does not do anything. Should have been removed as part of commit ea1fb0c87261b1fbf375fa6ade26deda1d01995b Add extension ip.test.ext to Au Test, with Test method to allocate extra TCP ports. Optimize HTTPHdr conversion of HTTP/2 to HTTP/1.1 Return TSFetchSM from TSFetchUrl so TSFetchFlagSet can set fetch flags READ_RESPONSE_HDR_HOOK is invoked only when the response is from the Origin Server Fix typo in system stats, change loadavg 10min to be 15min (#6608) Making client session id unique across HTTP/1 and 2 sessions Add support for a simple_server_retry_responses list (#6605) AuTest for server_push_preload plugin fix sed matching Improving the messaging around the use of TSSslSessionGetBuffer Fix PUSH_PROMISE frame payload length Cleanup: fix a inline function style Doc updates to TSContSchedule* API Skip compressible content type check with null strings. Include start line of HTTP messages in xdebug probe output. Convert mgmt/utils unit tests to use Catch. [Doc][DevGuide][TSStatSync] Fixing documentation for SUM and COUNT types Support body factory template suppression for internal requests Fix tunnel crash When using TSContSchedule() and TSContScheduleAPI() set the calling thread as the thread affinity when not already set Ensure TSContSchedule API family are called from an EThread. Make request/response body as an option for AuTest microserver Moved printing the incoming headers for debug before remapping SSL: Always renew TLS Session Tickets iff TLSv1.3 is being used Convert proxy/logging unit tests to use Catch. Convert single regression test in iocore/eventsystem/SocketManager.cc to Catch. Enable logging autests on macOS, clarify why Linux is required In Au tests, poll for excretion of log file rather than waiting fixed delay. (#6506) Fixed build issue with Ubuntu 16 debug Removed some unused declarations in HttpTransact Convert Mime and URL unit tests in proxy/hdrs to Catch. Convert unit tests for sslheaders plugin to Catch. free(map) -> delete map Removes copypasta curl text from tests and removes checks for curl Fixes some tls autests on macOS Add FetchSM support to dechunking in non-streaming mode. Add new TS API TSFetchFlagSet() Doc: Add mention for sleep time variance Allow lo interface in list of system stats (#6531) Remove unused unit test source files in src/tscore. better handling of TSVIO calls and TSVConnAbort (#6239) Convert tscore regression tests to Catch unit tests. Disables "virtual host not used with AWS auth v4" error in s3_auth Created remap_stats au test. This tests 2 remaps, one for a 2xx response and one for a 4xx response and then checks the stats output (#6505) Adds support for configure option --enable-yaml-headers (#6519) Require 1.1.1 as minimum openssl lib version for tls_check_dual_cert_selection Au test. Removes noisy log statement from xdebug Make traffic_ctl limp along for now with large records.snap (#6517) Fixes a bug where getHostStatus() will create a host status record when none is found. A Host status record should only be created when a host is marked up or down when traffic_ctl is used to mark a host up or down. Moves hosting.config finished loading message outside of parsing loop Refactor and generalize the User Arg concept, add global (#6468) Check the exit value of the regression test Fix SDK_API_TSSslServerContextCreate Exponential backoff - Make values used configurable - Add config support for ceiling time and retry cap. - Add small variance when sleeping. Cleanup: check activity of Http2Stream by ActivityCop Rework server side SSL_CTX creation to better handle dual_cert mismatches (#6483) XDebug: Always append to the X- header, rather than prepend Doc: connection.match default value update Charge Proxy Header Regression tests into Catch unit tests. traffic_dump: Make client-request gathered in a global hook (#6500) Remove method that does nothing. (#6501) Doc: traffic_manager - Add documentation for exp backoff and configuration Make all_headers test more resilient to timimng remove pthread_cancel, use atomic flags to ensure cross thread safety, and some other cleanups Add null check to fix error tunnel crash LGTM: Fix comparing int8_t with wider type Fix a compile warning Bikeshedding some code structures for reloadable plugins config (#6488) Issue 3546: Add "overridable" to the configuration variable description. Remove some outdated files. Be explicit about RTLD_LOCAL, defaults varies on platforms (#6485) [CPPAPI] Provide access to TSRemapRequestInfo in RemapPlugins. Conflicts: iocore/net/quic/QUICTLS_boringssl.cc commit 69713f57fc0398a489de9ffddcfa0c51564a3b67 Author: Masakazu Kitajo Date: Mon Mar 2 12:36:27 2020 +0900 Update QUIC TP format commit c6221cf2377cb6fef11e94a63e22d6449d4e37ee Author: Masakazu Kitajo Date: Wed Feb 26 17:14:14 2020 +0900 Update QUIC draft version numbers to 27 commit 4f69d0018064a16bfcd9bec246da227eb52c2dee Author: Masakazu Kitajo Date: Thu Mar 5 17:51:42 2020 +0900 Fix a bug around IPv6 commit 63e8470330050cee29f57647299bb316a72d25c3 Author: Masakazu Kitajo Date: Thu Mar 5 15:15:46 2020 +0900 Following changes for UDPBind interface change commit 93a1e7c453cfba0454a1fe33d73c2e32c7cafd07 Merge: febec04ee ddaf9e5f4 Author: Masakazu Kitajo Date: Thu Mar 5 11:35:37 2020 +0900 Merge branch 'master' into quic-latest * master: traffic_dump: Fixing content:size collection. Delay cleanup when cache write continues after early client response Reduce minimum config files neeed for golden_tests. Removes some things deprecated from older versions (#6471) Fixed logging docs typos Use same filename variable to keep consistency Fix clang-format on SSLUtils.cc Add more flexible error handling when open a config file. Remove --read_core option Remove noisy mutex warning AUTest MakeATSProcess default ports records.config Using dynamic ports in AUtest Issue #6400 - Adds config option to enable/disable dynamic reload feature for plugins => proxy.config.plugin.dynamic_reload_mode - 1 (default) enables the dynamic reload feature, 0 disables it => Adds to and refactors unit-tests for the dynamic plugin reload feature cache_range_requests plugin: detect and handle TSCacheUrlSet failures which poison the cache (#6464) Fix clang-format Make traffic_manager be flexible when opening config files. make sure time is consistent between calculations Remove update to unused variable commit febec04eee0f28cec84a94fe5ea68e1eb9d127e4 Author: Masakazu Kitajo Date: Thu Mar 5 09:13:03 2020 +0900 Update doc commit 62a0465ef36549432f14d2e9f69c016d563ec08f Author: Masakazu Kitajo Date: Tue Mar 3 22:36:16 2020 +0900 Use file descriptor passed by TM for QUIC commit fb0400a72976a0c162732bad69437923d75c22e1 Author: Masakazu Kitajo Date: Tue Mar 3 22:14:59 2020 +0900 Open UDP ports on traffic_manager if ports are configured for QUIC commit 0b776127316ecfc0b1f500fb201f1c8892109b84 Author: Masakazu Kitajo Date: Thu Mar 5 08:52:16 2020 +0900 fix compile error commit 26007a5162e8c68ba80b7ab71b8d4fe830b41f39 Author: Masakazu Kitajo Date: Wed Mar 4 22:07:02 2020 +0900 Fix compile warnings commit 0462bb7e918db3b8a64e5034a9a9fb67d2696b1e Author: Masakazu Kitajo Date: Wed Mar 4 15:12:08 2020 +0900 Add a setting for disable_active_migration Transport Parameter commit e622acc6edcbd32b1a190f0b00b244ee9777ad42 Merge: 7ea257aa0 b63879cbb Author: Masakazu Kitajo Date: Tue Mar 3 09:48:34 2020 +0900 Merge branch 'master' into quic-latest * master: Try to avoid mixing curl headers and body for disjoing-wait-for-cache test Move TestClientAction to SNIConfig class Add mechanism to enforce SNI policy x-remap ignoring age in gold file Adjust consume logic in data frame read Skipping log_retention.test.py because it is flaky in CI Fix code to avoid HostDBContinuation use after free Fix crash when H2 client does not set End-of-data bit Signal VC_EVENT_READ_COMPLETE when ATS received END_STREAM flag if transaction status nonzero, bypass the slice plugin (#6417) Turn on debug for the bash script test_logstats_summary Fix port selection for ssl ipv6 SSLNetVConnection, fixed/removed assert when running debug build traffic_dump post_process.py commit 7ea257aa06a5ed5cfa9d075d558ca1b8813abc15 Author: Masakazu Kitajo Date: Mon Mar 2 18:04:02 2020 +0900 Enable QUIC 0-RTT with BoringSSL commit 0da1abcff2b9ec2b37d79b4b0743dd6b4c6ef232 Author: Masakazu Kitajo Date: Thu Feb 27 17:02:40 2020 +0900 Fix an error with the old OpenSSL commit 68be51aa442d2b5c07de6c4df9c769c62b9f741d Merge: b3aa9226b e2a3e2951 Author: Masakazu Kitajo Date: Wed Feb 26 17:02:26 2020 +0900 Merge branch 'master' into quic-latest * master: SSL: Introduce proxy.config.ssl.server.prioritize_chacha Traffic Dump: fix client request target parsing Remove documentation for removed TSIOBufferReader API functions. Revert "Disable the chunked_encoding test" Avoid a weird name collision between HRW and tscore (#6446) Move log line length configuration to records.config docs. Cleaned up smuggle-client Cleaned up ssl-port Another option to fix potential HTTP/2 vio stall Disable the chunked_encoding test Reset captive_action.cancelled during open read retry to prevent assert Fixed encoding test to work with OpenSSL 1.0.2 Export headers for internal YAML-CPP library. This allows plugins to use the library in the same way and version as the TS core. Replace python with python3 in AuTest Set default encoding UTF-8 for AuTest on Linux Cleanup RamCacheCLFUS Doc: Add link from proxy.config.proxy_binary_opts to traffic_server options. Document the traffic_server -m, -M options, sort the options a bit better. Syntax Error fixed in URI sig Plugin (#6420) URI Sig Null Check for Clang Warning (#6419) commit b3aa9226bcbcd37375e297af252abb4d8f256081 Author: Masakazu Kitajo Date: Wed Feb 26 17:01:40 2020 +0900 Update unit tests commit e4ac336bf4067be6bf8c39c65b253754a93df805 Author: Masakazu Kitajo Date: Wed Feb 26 16:47:36 2020 +0900 traffic_quic: Add an option to specify a server name for SNI commit eeff49abddb0ff5dca8aafd8d5b5f1cfc86536df Author: Masakazu Kitajo Date: Tue Feb 25 14:37:04 2020 +0900 Fix memory leaks arond QUIC packet encryption commit 8bfdc08856719f4bc5ab7501b43b0227d48f63cc Author: scw00 Date: Mon Feb 24 15:58:49 2020 +0800 Using std::string for QUICConnectionId::hex commit bfbea17a5e6060d90c2f4807d126ade76431062a Author: Masakazu Kitajo Date: Thu Feb 20 23:13:50 2020 +0900 Add -r option for stateless reset excersice to traffic_quic commit cc223b0e42cf8530cea23370414a5984a6131669 Author: Masakazu Kitajo Date: Thu Feb 20 23:12:32 2020 +0900 Add QUICConnection::reset_connection() commit 0f09b5967f9bd9b67f653c5785e98038c92bb3c3 Author: Masakazu Kitajo Date: Wed Feb 19 15:05:25 2020 +0900 Handle stateless reset commit 7fea981ee4f305631fc0761028b725c097a02e79 Author: Masakazu Kitajo Date: Thu Feb 13 10:13:26 2020 +0900 Update comments commit 1598518319f71f48dbe6b2460c772c9029d93f92 Author: Masakazu Kitajo Date: Thu Feb 13 08:59:29 2020 +0900 Update files for OpenSSL with BoringSSL APIs to compile those commit 6ae995bccd5348ad535447352b23ea05f82587f4 Author: Masakazu Kitajo Date: Wed Feb 12 23:45:09 2020 +0900 Update configure script and Makefile to use files for OpenSSL with BoringSSL APIs commit 7d04123057499b3ddc0098a15ecd0e318666ccff Author: Masakazu Kitajo Date: Wed Feb 12 23:29:08 2020 +0900 Copy files for BoringSSL as files for OpenSSL with BoringSSL APIs commit 5c181b4a95b7177a29b44df6f7696e3a685e99e4 Author: Masakazu Kitajo Date: Wed Feb 12 23:27:33 2020 +0900 Use _legacy as suffix for files for hacked OpenSSL commit 4c1236efa11e0c133d712cb6999700b042d2548f Author: Masakazu Kitajo Date: Thu Feb 13 10:13:41 2020 +0900 Restore one more tests disabled for BoringSSL commit 292619ed11c13922943374dfe3412b639f5dc96f Author: Masakazu Kitajo Date: Mon Feb 17 10:42:03 2020 +0900 Remove a possible null pointer dereference commit 48cca203f44508a3706cb58fbdde65bfacc4c686 Author: Masakazu Kitajo Date: Thu Feb 13 12:19:16 2020 +0900 Update a test for handshake error commit 00d3f7fb1a586abd4872be6535a9f71c14d9821e Author: Masakazu Kitajo Date: Wed Feb 12 21:45:28 2020 +0900 Add missing AL header commit 40f8a594dd7bbda2fabfda2f3442c39eec266c85 Author: Masakazu Kitajo Date: Wed Feb 12 21:43:46 2020 +0900 Add a length check to TP parser commit b75169ab6bdcee21f8c8af47ab42d42b1b8d0ba8 Author: Masakazu Kitajo Date: Wed Feb 12 21:43:03 2020 +0900 Reenable tests for handshake that didn't pass with BoringSSL commit 01e6daf2c840c797b9723319c49d49d887bd6aaa Author: Masakazu Kitajo Date: Wed Jan 22 11:37:32 2020 +0900 Send and receive Transport Parameters with BoringSSL API commit da6062a45e9d9d1ded78001979a2596903f51a0b Author: Masakazu Kitajo Date: Wed Jan 22 09:53:24 2020 +0900 Update tests commit d86fa83b427ffdc6fafb0b4a2724f30da88b4f65 Author: Masakazu Kitajo Date: Wed Jan 22 00:14:52 2020 +0900 Update tests commit 4cd59ce984fd3e6e49edcde4a8abc57dce37be56 Author: Masakazu Kitajo Date: Tue Jan 21 23:16:23 2020 +0900 Fix compile error with OpenSSL commit 70f86f553c9eee2318f2269910db84a836ba719b Author: Masakazu Kitajo Date: Tue Jan 21 22:57:09 2020 +0900 Handshake with BoringSSL commit c694fd25c3d8cdb719412988bbdd1b00097f8ce7 Author: Masakazu Kitajo Date: Tue Jan 21 20:23:21 2020 +0900 Update OpenSSL impl commit 4d4c07f5b6ec278be314b25fdb12ed81f179c717 Author: Masakazu Kitajo Date: Tue Jan 21 16:38:45 2020 +0900 Read ClientHello with BoringSSL commit 22b7d9f309b3da33de81123e00d05bac0024190c Author: Masakazu Kitajo Date: Mon Jan 20 11:54:20 2020 +0900 fixup commit 7611b5894cf29e25a1052b570e7a42b1d19afb6b Author: Masakazu Kitajo Date: Wed Jan 8 17:05:07 2020 +0900 Use some of BoringSSL APIs commit 715566abd0985380914b21d1003a8b3c65f772e7 Author: Masakazu Kitajo Date: Mon Jan 6 11:19:40 2020 +0900 Make QUIC code work with BoringSSL commit 886bb0910f1feac2d66618d21ede49cbba1c8821 Author: Masakazu Kitajo Date: Fri Feb 14 10:51:21 2020 +0900 Warn if quic is specified to a port configuration on a binary that doesn't support it. commit 4034130ab0d4dca21fff4509e2d8ab9b78b78833 Merge: db258fb55 4bdde5d48 Author: Masakazu Kitajo Date: Thu Feb 13 11:29:16 2020 +0900 Merge branch 'master' into quic-latest * master: Change gold files to be less restrictive since some of the headers include can be in a different order (#6410) Avoid cross-thread mutex conflicts Auto port select slow_post test Remove unnecesary HttpSM handler call with VC_EVENT_ERROR Fix for lua plugin coredump problem during reload Update inactive_timeout_at in Http2Stream::signal_read_event() Fix localstatedir and runtimedir for Debian layout Including stdexcept Change to use throw std::runtime_error instead of removal Removing throw since there is no context and could cause a crash Ensure that extra data beyond the chunked body is not tunneled Free TSMgmtString after using it. Fixed how we handle uknown schemes Change header validation Change default connection match config value from ip to both Remove dependencies on include/tscore Skip unnecessary HostDB update on a fall back to serving stale record Remove trailing white space from json formatter Document the order of the X-Cache header Format config_describe output logs. Pretty format using BufferWritter Add compressible content-types example and 1Kb min Fix rare SSN/TXN Start/Close Hook misorderings (#6364) Add header rewrite test to make sure we are not injecting end of rule char (#6340) Change bitfields to be unsigned explicitly (#6373) commit db258fb551044c6811d0ba56d9f9bf9f7fe9df1e Author: Masakazu Kitajo Date: Wed Feb 5 00:13:27 2020 +0900 Fix a bug that receiving multiple Retry packets makes PN calculation wrong commit f8741de2364916c31c260951a05d1f73300d674a Author: Masakazu Kitajo Date: Tue Feb 4 22:10:50 2020 +0900 Fix debug log commit e93a9a58e28def940a19d53516dbc6bec8577fe0 Author: Masakazu Kitajo Date: Tue Feb 4 20:55:23 2020 +0900 Fix a buffer size for address validation token commit 99a19e8c5cbef73c0a1ac689b4d78d8283158a46 Author: Masakazu Kitajo Date: Mon Feb 3 23:29:50 2020 +0900 Fix zero-length CID commit 550adb320db62739d6dd4676da08fbf1fd03ceb5 Author: Masakazu Kitajo Date: Mon Feb 3 22:34:21 2020 +0900 Fix preferred_address commit e1e06e12fd5603407dbe113f66c9c5dd5c27bd07 Author: Masakazu Kitajo Date: Mon Feb 3 21:47:56 2020 +0900 Fix QUIC Version Negotiation commit 212ae79222e756bb149e718bd7976d963b84fc52 Author: Masakazu Kitajo Date: Mon Feb 3 20:45:04 2020 +0900 Check buffer len while parsing QUIC Packet commit cf7f741f645b1ce933ae4c9b3e2c11068085bf3d Author: Masakazu Kitajo Date: Fri Jan 31 14:13:21 2020 +0900 Fix a stack overflow at read_essential_info() commit 5c6063e97440908748fc999a048441e86c7efd02 Author: Masakazu Kitajo Date: Thu Jan 30 16:20:02 2020 +0900 Add QUICRetryIntegrityTag commit d233b3f415da3f16f86778f230a3bbbfed478be4 Author: Masakazu Kitajo Date: Thu Jan 30 16:08:26 2020 +0900 Fix a bug in Stateless Reset Packet generation commit 5cf73159b56513f13858e09c4b72d486ffc64b97 Merge: 8e1dd61f3 614bbe0a4 Author: Masakazu Kitajo Date: Thu Jan 30 15:40:18 2020 +0900 Merge branch 'master' into quic-latest * master: Change alloca usage to malloc/free Fixing shadowed variables, both global and local: (#6371) Cleanup: Remove unused accessors of HttpVCTableEntry Removing always true/false comparisons (#6363) Change localtime/gmtime usages to use the threadsafe versions with local storage (#6362) Cleanup: Remove empty function Add header guard (#6358) Update git-versions Fix heap-use-after-free on Http2Stream::destroy() Add virtual destructors for Http2TxFrame, Http2FrequencyCounter Perf: Optimize sending HTTP/2 frame Fixes an issue where a debug build of ATS will fail the assertion in HttpTransact::handle_parent_died() when proxy.config.http.no_dns_just_forward_to_parent is enabled and there is no parent.config or strategy rule for the request. Also this provides a log warning for both debug and release builds of this condition. Fixes an issue where NextHopSelectionStrategy did not implement an available nexthop or parent check when proxy.config.http.no_dns_just_forward_to_parent is enabled. Fixes Issue #6321 caused when proxy.config.http.no_dns_just_forward_to_parent is enabled. When this configuration variable is enabled, a parent selection strategies findParent() function is called twice on each transaction resulting in unexpected results such as every other parent is only used in a strict round robin strategy. Fix problems with "Probe" option for X-Debug MIME header field. (#6197) Make compress plugin normalization of Accept-Encoding header compatible with normalization in core TS. Adding max line length configuration documentation. Fixed build issues on macOS after 0-RTT merge Fixes typo in TLS Bridge illustration Fix strict round robin cur_index increment remove dequeue_timed since it is not being used anymore fix doc build error Simple and miscellaneous fixes/additions for lua plugin fix freebsd build error Remove unused variable Improvements on ESI plugin Add new log field to output server name sent by client in TLS handshake. Fixes clang-format issues TLSv1.3 0-RTT support (#5450) Add simple autest and subsequent fixes Clear all pointers in API Hooks clear function. Fix closed flag from #6287 Doc bugs Add tests to exercise H2 and chunked encoding commit 8e1dd61f323596f5d7204ab918884c0b0f32e35e Author: Masakazu Kitajo Date: Thu Jan 30 15:35:39 2020 +0900 Count CIDs used on Initial and PreferredAddress as active CIDs commit bc33fc37c122dfd89140421b8c446328e9e71b1d Author: Masakazu Kitajo Date: Wed Jan 29 23:16:29 2020 +0900 Update RETRY packet commit bf08848e654e75c66d7e47bb6aea89f2f9e04c37 Author: Masakazu Kitajo Date: Tue Jan 28 16:14:53 2020 +0900 Send INVALID_TOKEN error commit 593be74801b18a98b0a137e4bcf95c8e759fa288 Author: Masakazu Kitajo Date: Mon Jan 27 12:04:16 2020 +0900 Use CONNECTION_ID_LIMIT_ERROR commit b7c4f1c8c3668486c1bc975ff08cf6d983b70317 Author: Masakazu Kitajo Date: Mon Jan 27 11:59:06 2020 +0900 Add CONNECTION_ID_LIMIT_ERROR and INVALID_TOKEN commit df36ca7bfb6c14c001ab50e42f1ad77aa18c4b7c Author: Masakazu Kitajo Date: Mon Jan 27 11:45:00 2020 +0900 Update idle_timeout to max_idle_timeout and follow the new behavior commit 0ee4a7adca60d2f9f4bc5f271e1582e7910883da Author: Masakazu Kitajo Date: Thu Jan 23 21:46:10 2020 +0900 Update tests commit 3e191ce71057ecd161e2c434eff6a6f2b8609ff5 Author: Masakazu Kitajo Date: Thu Jan 23 21:24:41 2020 +0900 Support HANDSHAKE_DONE frame commit 137451f24cd7d02a91a79a0e1b9e74e77ca0d578 Author: Masakazu Kitajo Date: Thu Jan 23 21:35:16 2020 +0900 Update QUIC draft version numbers to 25 commit 7b82a93d93ab143edf85bf1765aff1b882c9a10b Author: Masakazu Kitajo Date: Wed Jan 8 17:21:40 2020 +0900 Fix an wrong assert and add a test for it commit 983cd002288f82c6eb36d11d7cec24b961d2355a Author: Masakazu Kitajo Date: Tue Jan 7 16:06:28 2020 +0900 Rename QUICConnection::close to QUICConnection::close_quic_connection There was a name conflict. commit 7e6c192633658bc8a84da34a2ed54e6171686108 Merge: b15cf0991 f63027569 Author: Masakazu Kitajo Date: Tue Jan 7 15:24:37 2020 +0900 Merge branch 'master' into quic-latest * master: Introduce NetEvent to split UnixNetVConnection and NetHandler Reduce process duration of test_Http2FrequencyCounter Make sure shutdown_cont_event isn't holding any garbage references. Add in_destroy to Http2ConnectionState to prevent double delete (similar to Http2ClientSession) Fixes a corner case where the NextHop consistent hash ring may not be searched in it's entirety for an available host due to a premature wrapped ring indication. Adjust debug tag for ssl_sni_whitelist plugin to match plugin Add invalid config warning when cache open write fail and read retry are inconsistent Add links to RWW sections Fix rst errors for collapsed forwarding plugin Set wrap after checking all the parents Perf: replace dynamic_cast with static_cast in this_thread() Fixes a few Sphinx build warnings (#6290) Moving / adding the Roadmap to the release notes (#6257) These features are in 9.0.x release notes (#6286) Removes the remaining references to TSQA LGTM: Fix unused imports Assure no SM survives plugin factory deactivation. Remove remnants of obsolete remap thread. Update the admin-guide hierachical caching and remap.config documentation to include the NextHop strategies feature. For combo_handler plugin, add an optional whitelist of allowed values for Content-Type. Copy the Client SNI Server Name out of the openssl SSL object and ensure it is null-terminated. For per-transaction config override, crossing the const-correctness event horizon. Remove -j from lgtm build (#6274) LGTM: Add header guards LGTM: Fix wrong type of arguments to formatting function Change HTTP/2 error rate log to warning LGTM: fixes a shadowed variable in ParentSelection::PreprocessParents(). LGTM: fix a comparison that is always false. Fixes various crashers loading/reloading parent.config TCL: cleanup in HostLookup.cc, make sure keys are stable. (#6263) Fix TS_USE_DIAGS usage for --disable-diags option Initial revision of .lgtm.yml config file (#6258) auto delete rolled log file fixes remap_stats: restore handling of remap/hostname to remove memory leak Avoid unnecesarry copy on POST request over HTTP/2 Add some stats collections to cache_promote. Some tweaks to reloading-plugins.en.rst (#6251) Fix sni.yaml fqdn to match complete name string Adding verify plugin TS maintenance commands Removing traffic_cop reference in Admin Guide introduction. commit b15cf0991e0bfbf3e8d7a8445169da8d440f294c Author: Masakazu Kitajo Date: Thu Dec 12 14:44:35 2019 +0900 Use individual classes for receiving packet commit e0d4e22324e52fb43ca47412a074b143fd1445a2 Author: Masakazu Kitajo Date: Fri Nov 22 15:03:18 2019 +0900 Update tests and add accessor functions commit a79707fadd990d403cfb352c40555f813d4b28bf Author: Masakazu Kitajo Date: Fri Nov 15 15:15:39 2019 +0900 Use a buffer on stack for sending packet instances commit 6718a5a78026de2ef7feada907b9013ff0355cf1 Author: Masakazu Kitajo Date: Fri Nov 8 22:47:05 2019 +0900 Use individual classes for sending packets commit f2e27565a2c4f01453151c99f5e1fb4d00ec20d6 Author: Masakazu Kitajo Date: Thu Dec 26 11:11:43 2019 +0900 Add debug log about retry token commit 64b76fcd245b58ab827bfb7d14ca8f602de6169b Author: Masakazu Kitajo Date: Fri Dec 20 10:18:55 2019 +0900 Fix a bug that QUICFlowController generates frames regardless of max_frame_size commit c18cd3a4ace2cf7603f86efce24316950966fa94 Author: Masakazu Kitajo Date: Mon Dec 9 17:34:22 2019 +0900 Fix a bug commit 8e7597f04c34216f6d4b896ee3718cd8e526591d Author: Masakazu Kitajo Date: Mon Dec 9 11:13:47 2019 +0900 Don't use the allocator for receiving QUICPacket commit 1ea2ba6757126977e9442b658c895299b33340ba Author: Masakazu Kitajo Date: Mon Dec 9 10:54:21 2019 +0900 Fix compile warnings commit 4bcb41acee6ae3fc26c85c21036abce5b349a5f4 Author: Masakazu Kitajo Date: Fri Dec 6 17:33:21 2019 +0900 Completely ignore the second and following RETRY packets commit 152a42efbf5c516ecc59894952961b6dba4abdcb Merge: ba48f6b66 080889f68 Author: Masakazu Kitajo Date: Thu Dec 5 16:55:53 2019 +0900 Merge branch 'master' into quic-latest * master: Fixes sphinx build warning with the strategies.yaml document in the admin-guide. Adds strategies.yaml to install target cache_range_request plugin: add support for opt X-CRR-IMS reval header Adjust the refcounts to avoid Mutex leak Following catch test naming standards. Review comments. Fixing rolled log cleanup parsing for .log. files. Fixed build issue with clang5 and Extendible Remove never implemented regex descriptions Fix out of bound array access in ssl_session_reuse plugin (#6235) Don't sleep if ProtectedQueue.localQueue is not empty. (#6234) fix stringstream crash during shutdown Correct handle the value return from mgmt socket read and write (#6220) commit ba48f6b66397b31e8c6836037f6667e82c2cc636 Author: scw00 Date: Mon Nov 25 19:12:08 2019 +0800 Fix build error in QUICTypes.cc commit 91d7b743c65c5f3ba8270c52cd4c1f5186c9faa0 Merge: ecab2b84c aba5c2c6f Author: Masakazu Kitajo Date: Fri Nov 22 15:10:31 2019 +0900 Merge branch 'master' into quic-latest * master: Docker builds do not need build numbers in workspace (#6221) Add autest for cache_range_request, add xdebug x-parentselection-key Move logging before session could be freed Do not reenable txnp with TS_EVENT_HTTP_ERROR if received TS_EVENT_HTTP_TXN_CLOSE event (PR #6215) Fixed next hop tests for out of tree builds This fixes next hop unit tests that segfault due to missing (#6216) Add autopep8 & pyflakes in dev-packages for AuTest Set END_STREAM flag when write_vio ntodo is 0 Add 100-continue expectation support on H2 connection Run dos2unix on all files in tree Lua plugin fix: Account for null in output from TSUrlPercentDecode. Cleanup: Remove useless UDPConnection function Check Range header for stale content Fixes spelling in strategies.yaml docs Adds WS check, and some cleanup (#6213) Cleanup trailing whitespaces, and YAML formatting (#6210) ssl_session_reuse optimization to check if diags is on Fix the strategies.yaml documentation. Add a remap @strategy tag and nexthop selection strategies to remap. Add test to catch regressions in sni and override configs (#6170) Upgrade Catch.hpp to v2.11 (#6185) Fix ssl_session_reuse to compile on macOS and FreeBSD Remove header conversion functions for HTTP/0.9 remap_stats: Fix BufferWriter usage error. Limit this check to Linux, where ldd always works (#6191) Fixed issue with macOS Catalina and pcre 8.43 enabling pcre-jit (#6189) Replaces Python -> Python3 in a few utilities (#6187) Fix compile warnings in Catch checks for TextView (#6186) Dockerfile for Debian package managers (#6183) RBTree - fix potential nullptr dereference Doc: Remove local path to MathJax. Removes the ssn_close hook, it can never work (#6175) tslua: Exposes set/get version for server request objects Remove unimplement UDP function Remove HttpTransact::build_upgrade_response Fixes cppcheck issues for cookie_remap plugin Fixed gcc7 issue with yaml-cpp 0.6.3 Updates yaml-cpp to 0.6.3 Remove unused functions of IOBuffer Re-order READ_REQUEST_ HDR_HOOK and PRE_REMAP_HOOK tslua: Exposes set/get method for server request objects Cleans up some of the filenames mess (#6144) Fixed build issues with hwloc 2.x API changes Remove Cache v23 support detect bogus jemalloc version Move websocket upgrade later in state machine Change API to return a TSReturnCode code. Promote server_push_preload plugin. Move TSHttpTxnServerPush to the stable API interface. Only decrement log_stat_log_files_open_stat when the file is closed. Normalize loopback usage in session_match autest tests: Adds autest for WebSocket mappings Fix building Catch on macOS Reverse debug specific check. thread ready check tests: Cleans up local variable is assigned to but never used warnings tests: Cleans up imported but unused imports Rename test to lua_watermark.test.py Add apache header to watermark lua test Add lua watermark test to check for existence of lua watermark upstream API call Internal link needs _ Reduce doc build errors Disable the most expensive "make check" tests by default slice plugin add support for using effective url with remap host. Make MIOBufferWriter unit tests work when compiled without -DDEBUG. Remove including sys/sysctl.h for Linux A couple simple tweaks. Update docs for SSL Handshake stats Remove using namespace std Avoid IOBufferReader::read_avail() call from MIOBuffer::high_water() Remove remnants of http/1.1 pipeline logic. Cleaned up the changelog command for making our release notes Updated to clang-format v9.0.0 Remove never implemented HttpTransact::service_transaction_in_proxy_only_mode Add Metrics to track SSL Handshake attempts Removes the records.config.shadow alternative config file Fixing include synopsis rendering for API docs. Adding an autest for traffic_dump. Issue #4294: Handle return value of SSL_write() properly. Fix null pointer dereference reported by clang-analyzer Add an appropriate warning where behavior has changed Fixes misc. spelling and whitespace For remap_stats, removes configure time dependency on search.h Removes proxy.config.cache.storage_filename set host name in TLS extension for SNI Host check in service side with sni policy verify_with_name_source. Minor fixes. Fixes misc. spelling and whitespace Docs: cachekey: fixed non-ascii table characters Docs: clarify remap plugin inst init params cleanup the eventloop Script used for comparing commits on internal branch and seeing if they are on the remote master or release branch. It uses a combination of cherry-mark and searching the commit message for cherry-pick -x hashes. doc + unittest TSRemap(Init|NewInstance) failures Updating the autest version pin to 1.7.4. Fixing log cleanup candidate selection and adding a test for it. Replace container of HPACK dynamic table from std::vector to std::deque Allow txn handler to be set from ssn on same hook Add basic SystemTap markers support Issue #6072: Make If-Range date compare to be exact match. Cleanup: Ignore checkprograms of remap Issue 4635: Address pipe reuse after configuration reload issues Fix the malformed threads table Disable tests using exceptions in MIOBufferWriter UT. Updated API header and ssl_session_reuse for new TSSslSessionInsert changes Premature DSO unload with "suicidal" continuations support for listening on all the net threads uses epollexclusive flag and soreuseport Fixing session sharing with IP matching. Promote 'Enable_Config_Var' from HttpConnectionCount to HttpConfig. This is so other configuration can use it. Cleanup: Remove use of obsolete class TSConstBuffer from MIME.cc Add example to retrieve request body using Lua Dechunk chunked contents on HttpTunnel if client protocol is HTTP/2 or HTTP/3 Add MIMEHdr Garbage Collection to HPACK Dynamic Table Fix some long lines and reduntant plugin_config SO additions. Updates references to OSX to macOS Fixes help message for traffic_top's sleep switch commit ecab2b84c79b456658a04e3163048b642a63b147 Author: Masakazu Kitajo Date: Fri Nov 22 10:22:43 2019 +0900 Fix typos commit 4cd5ec022e6924b32e9e4708b90770557fcf20d9 Author: Masakazu Kitajo Date: Wed Nov 20 14:39:38 2019 +0900 Fix a crash on reading malformed packet commit 0fd40f358d6865b76bae95a94e9403015baf61e8 Author: Masakazu Kitajo Date: Tue Nov 19 15:54:17 2019 +0900 Ignore dup NCID frames commit ded20f5fa04bc6bd2a608a2d1cd2ec26beaa3e30 Author: Masakazu Kitajo Date: Sun Nov 17 10:45:01 2019 +0900 Fix an issue that early data on the 2nd initial packet is not processed commit 26910cf2284dcbf5a8731296881053b505ae6971 Author: Masakazu Kitajo Date: Fri Nov 15 10:34:32 2019 +0900 Fix tests commit c53e4094c84378870d9642b7ca20563a2d9306ef Author: Masakazu Kitajo Date: Tue Nov 12 14:24:08 2019 +0900 QUIC Connection Close frame is not ack-eliciting commit 500b22ac2c0daa1457224c6995fb10b55c5d28f2 Author: Masakazu Kitajo Date: Tue Nov 12 13:59:48 2019 +0900 Add settings for QUIC quantum readiness test proxy.config.quic.client.quantum_readiness_test_enabled proxy.config.quic.server.quantum_readiness_test_enabled commit 4766128cacf0dd27c17badb71a3b12c7195b0cb0 Author: Masakazu Kitajo Date: Tue Nov 12 13:58:54 2019 +0900 Handle multiple INITIAL packets from a client commit 77a5dd1028850598719be37c184da0265d8c608f Author: Masakazu Kitajo Date: Mon Nov 11 16:18:57 2019 +0900 Allow sending PING at any encryption level This reverts #5859 commit ce0efc2db4d1ff4b49bf318d5a2d5fb13607580f Author: Masakazu Kitajo Date: Mon Nov 11 16:16:29 2019 +0900 Update QUIC draft version numbers to 24 commit af2f854085d00f32f4aa55a8ec669a9989d84e09 Author: Masakazu Kitajo Date: Tue Nov 12 15:06:35 2019 +0900 Don't migrate connection until a client sends non-probing frame commit e892988c32506d7e609a94b99594b3e17cd670aa Author: Masakazu Kitajo Date: Mon Oct 28 17:23:44 2019 +0900 traffic_quic: Support close exercise option on H3 session commit 9e4b0586a51cca83f531752acbf7b024f30bd7e8 Author: Masakazu Kitajo Date: Mon Oct 28 12:15:23 2019 +0900 Fix a bug in stream count limit This fixes #5995. commit 673575fd1542261be1d3f992f3687338746585bb Author: Masakazu Kitajo Date: Mon Oct 28 10:09:18 2019 +0900 Fix a bug in sending QUIC ConnectionClose frame This closes #6057. commit 8d59375108f52b14a90c29f1d41b43fde78bbaa5 Author: scw00 Date: Wed Oct 23 17:14:02 2019 +0800 QUIC: Every two ack-elicting packet one ack frame commit 95717cdcfcd4fe0cf6be7caeeb18922ee97ee52f Merge: cbbc6a5de 9c05e64df Author: scw00 Date: Thu Oct 24 16:00:19 2019 +0800 Merge branch 'master' into quic-latest * master: Add a config for internal HPACK header table size limit Add tests for MIMEHdr::get_host_port_values Clarify relation of nameservers and resolv.conf Ignore MgmtMessageHdr if ProcessManager is not running Remove obsolete pipeline_max variable Add Example URI Signer Python Script Adding connection close headers to regex_remap test. Rewrote remap_stats plugin to use C++ Doc: Fixed the logging mode of pipe to be ascii_pipe Enhance Connection Collapse in ATS core Doc: improve documentation for event loop statistics. Doc: Fix :units: options for statistics. Doc: clean up build errors. Restore the MIOBufferWriter unit tests. Log H2 priority information "Plugin (un)used" post reload notification do not schedule stuff during shutdown Remove obsolete 4-2-0-fixup Cleanup: unifdef TRACK_BUFFER_USER cachekey: allow multiple values for `--key-type` Revert "Optimize: If failed on migrateToCurrentThread, put the server session back to global server session pool" url_sig: fix memory leak with urlParse and pristine flag make proxy.config.http.request_buffer_enabled configurable and bug fix Update TSVConnSslConnectionGet name to match others in the TSVConnSsl* family Minor cleanup of proxy/logging/Log.h . Add QUIC draft-23 support Fix TSHttpTxnReenable to schedule back to original SM thread Fix dynamic update for conntrack (HttpConnectionCount) configuration variables. These are the current settings we're running on docs do not reload configs periodically Mark host as partial when subdmains are more than default table depth Fix our doc build system to handle non-source dir configure locations. Destroy MIMEFieldBlockImpl that doesn't have fields in use clang-analyzer: Add a null check ProxySession cleanup: moving inline functions to .h Cleanup: do not re-define _proxy_ssn variable in local Cache SSL EC explicitly add some documentation to the action class to avoid confusion a general autoconf cleanup Updating the default cipher-suite lists for the 9.x release. Removed hardcoded logging.yaml filename in logs Conflicts: iocore/net/QUICNetVConnection.cc iocore/net/quic/Mock.h iocore/net/quic/QUICContext.cc iocore/net/quic/QUICContext.h iocore/net/quic/QUICPathManager.cc iocore/net/quic/QUICPathManager.h iocore/net/quic/QUICStreamManager.cc iocore/net/quic/QUICStreamManager.h iocore/net/quic/test/test_QUICFrameDispatcher.cc commit cbbc6a5de0e4282097227fef510d911c282ea8d5 Author: scw00 Date: Tue Oct 22 10:30:29 2019 +0800 QUIC: add Context to QUICStreamManager commit 4ef3d3a3084dec2bdfdbc35c292f9f2d5cc2bf0d Author: scw00 Date: Wed Oct 9 10:37:12 2019 +0800 QUIC: sink path manager to stream manager --- configure.ac | 21 +- doc/admin-guide/files/records.config.en.rst | 6 + iocore/net/P_Net.h | 7 - iocore/net/P_QUICNet.h | 3 + iocore/net/P_QUICNetProcessor.h | 2 + iocore/net/P_QUICNetVConnection.h | 57 +- iocore/net/P_QUICPacketHandler.h | 14 +- iocore/net/QUICNet.cc | 3 +- iocore/net/QUICNetProcessor.cc | 12 +- iocore/net/QUICNetVConnection.cc | 407 ++-- iocore/net/QUICPacketHandler.cc | 159 +- iocore/net/quic/Makefile.am | 16 +- iocore/net/quic/Mock.h | 110 +- iocore/net/quic/QUICAckFrameCreator.cc | 9 +- iocore/net/quic/QUICAckFrameCreator.h | 2 +- iocore/net/quic/QUICAltConnectionManager.cc | 46 +- iocore/net/quic/QUICAltConnectionManager.h | 7 +- iocore/net/quic/QUICConfig.cc | 39 +- iocore/net/quic/QUICConfig.h | 16 +- iocore/net/quic/QUICCongestionController.h | 13 + iocore/net/quic/QUICConnection.h | 9 +- iocore/net/quic/QUICContext.cc | 23 +- iocore/net/quic/QUICContext.h | 170 +- iocore/net/quic/QUICDebugNames.cc | 10 +- iocore/net/quic/QUICEvents.h | 1 + iocore/net/quic/QUICFlowController.cc | 46 +- iocore/net/quic/QUICFrame.cc | 180 +- iocore/net/quic/QUICFrame.h | 115 +- iocore/net/quic/QUICFrameDispatcher.cc | 7 +- iocore/net/quic/QUICFrameDispatcher.h | 5 +- iocore/net/quic/QUICHandshake.cc | 111 +- iocore/net/quic/QUICHandshake.h | 14 +- iocore/net/quic/QUICHandshakeProtocol.h | 4 +- iocore/net/quic/QUICIntUtil.cc | 4 +- iocore/net/quic/QUICIntUtil.h | 2 +- iocore/net/quic/QUICKeyGenerator.cc | 8 +- iocore/net/quic/QUICKeyGenerator.h | 12 +- iocore/net/quic/QUICKeyGenerator_boringssl.cc | 21 +- iocore/net/quic/QUICKeyGenerator_legacy.cc | 63 + iocore/net/quic/QUICKeyGenerator_openssl.cc | 27 +- iocore/net/quic/QUICLossDetector.cc | 15 +- iocore/net/quic/QUICLossDetector.h | 14 +- .../quic/QUICNewRenoCongestionController.cc | 8 +- iocore/net/quic/QUICPacket.cc | 2113 ++++++++++++----- iocore/net/quic/QUICPacket.h | 698 +++--- iocore/net/quic/QUICPacketFactory.cc | 397 ++-- iocore/net/quic/QUICPacketFactory.h | 34 +- iocore/net/quic/QUICPacketHeaderProtector.cc | 43 +- .../QUICPacketHeaderProtector_boringssl.cc | 31 +- .../quic/QUICPacketHeaderProtector_legacy.cc | 53 + .../quic/QUICPacketHeaderProtector_openssl.cc | 9 +- iocore/net/quic/QUICPacketPayloadProtector.cc | 124 +- .../QUICPacketPayloadProtector_boringssl.cc | 131 +- .../quic/QUICPacketPayloadProtector_legacy.cc | 136 ++ .../QUICPacketPayloadProtector_openssl.cc | 15 + iocore/net/quic/QUICPacketReceiveQueue.cc | 20 +- iocore/net/quic/QUICPacketReceiveQueue.h | 3 +- iocore/net/quic/QUICPathManager.cc | 10 +- iocore/net/quic/QUICPathManager.h | 20 +- iocore/net/quic/QUICPinger.cc | 36 +- iocore/net/quic/QUICPinger.h | 8 +- iocore/net/quic/QUICResetTokenTable.cc | 53 + iocore/net/quic/QUICResetTokenTable.h | 46 + iocore/net/quic/QUICRetryIntegrityTag.cc | 79 + iocore/net/quic/QUICRetryIntegrityTag.h | 39 + iocore/net/quic/QUICStream.cc | 2 +- iocore/net/quic/QUICStreamManager.cc | 42 +- iocore/net/quic/QUICStreamManager.h | 8 +- iocore/net/quic/QUICTLS.cc | 323 ++- iocore/net/quic/QUICTLS.h | 32 +- iocore/net/quic/QUICTLS_boringssl.cc | 388 ++- iocore/net/quic/QUICTLS_legacy.cc | 445 ++++ iocore/net/quic/QUICTLS_openssl.cc | 587 +---- iocore/net/quic/QUICTransportParameters.cc | 85 +- iocore/net/quic/QUICTransportParameters.h | 5 +- iocore/net/quic/QUICTypes.cc | 80 +- iocore/net/quic/QUICTypes.h | 129 +- iocore/net/quic/QUICVersionNegotiator.cc | 15 +- iocore/net/quic/qlog/QLog.cc | 103 + iocore/net/quic/qlog/QLog.h | 145 ++ iocore/net/quic/qlog/QLogEvent.cc | 317 +++ iocore/net/quic/qlog/QLogEvent.h | 1013 ++++++++ iocore/net/quic/qlog/QLogFrame.cc | 282 +++ iocore/net/quic/qlog/QLogFrame.h | 309 +++ iocore/net/quic/qlog/QLogListener.h | 119 + iocore/net/quic/qlog/QLogUtils.h | 80 + iocore/net/quic/test/main.cc | 2 + .../net/quic/test/test_QUICAckFrameCreator.cc | 23 + iocore/net/quic/test/test_QUICFrame.cc | 39 +- .../net/quic/test/test_QUICFrameDispatcher.cc | 8 +- .../quic/test/test_QUICFrameRetransmitter.cc | 4 +- .../quic/test/test_QUICHandshakeProtocol.cc | 266 +-- iocore/net/quic/test/test_QUICLossDetector.cc | 141 +- iocore/net/quic/test/test_QUICPacket.cc | 751 ++++-- .../net/quic/test/test_QUICPacketFactory.cc | 69 +- .../test/test_QUICPacketHeaderProtector.cc | 77 +- .../net/quic/test/test_QUICPathValidator.cc | 2 +- iocore/net/quic/test/test_QUICPinger.cc | 58 +- iocore/net/quic/test/test_QUICStream.cc | 13 +- .../net/quic/test/test_QUICStreamManager.cc | 192 +- iocore/net/quic/test/test_QUICStreamState.cc | 6 +- .../quic/test/test_QUICTransportParameters.cc | 127 +- .../quic/test/test_QUICVersionNegotiator.cc | 59 +- lib/records/RecHttp.cc | 2 + mgmt/RecordsConfig.cc | 8 + proxy/http/HttpProxyServerMain.cc | 7 +- proxy/http/Makefile.am | 3 +- proxy/http3/Http09App.cc | 1 + proxy/http3/Http3App.cc | 1 + proxy/http3/Http3Frame.cc | 4 +- proxy/http3/Http3HeaderFramer.cc | 2 +- proxy/http3/Http3Session.cc | 1 + proxy/http3/Http3SessionAccept.cc | 1 + proxy/http3/Http3StreamDataVIOAdaptor.cc | 7 + proxy/http3/Http3StreamDataVIOAdaptor.h | 6 +- proxy/http3/Http3Transaction.cc | 23 +- proxy/http3/Http3Transaction.h | 9 +- proxy/http3/Makefile.am | 3 +- proxy/http3/test/test_QPACK.cc | 2 +- src/traffic_quic/Makefile.inc | 2 +- src/traffic_quic/quic_client.cc | 41 +- src/traffic_quic/quic_client.h | 5 +- src/traffic_quic/traffic_quic.cc | 2 + src/traffic_server/traffic_server.cc | 1 + src/tscore/ink_inet.cc | 4 +- 125 files changed, 9161 insertions(+), 3236 deletions(-) create mode 100644 iocore/net/quic/QUICKeyGenerator_legacy.cc create mode 100644 iocore/net/quic/QUICPacketHeaderProtector_legacy.cc create mode 100644 iocore/net/quic/QUICPacketPayloadProtector_legacy.cc create mode 100644 iocore/net/quic/QUICResetTokenTable.cc create mode 100644 iocore/net/quic/QUICResetTokenTable.h create mode 100644 iocore/net/quic/QUICRetryIntegrityTag.cc create mode 100644 iocore/net/quic/QUICRetryIntegrityTag.h create mode 100644 iocore/net/quic/QUICTLS_legacy.cc create mode 100644 iocore/net/quic/qlog/QLog.cc create mode 100644 iocore/net/quic/qlog/QLog.h create mode 100644 iocore/net/quic/qlog/QLogEvent.cc create mode 100644 iocore/net/quic/qlog/QLogEvent.h create mode 100644 iocore/net/quic/qlog/QLogFrame.cc create mode 100644 iocore/net/quic/qlog/QLogFrame.h create mode 100644 iocore/net/quic/qlog/QLogListener.h create mode 100644 iocore/net/quic/qlog/QLogUtils.h diff --git a/configure.ac b/configure.ac index 4d02f4637c8..ea97c898dbd 100644 --- a/configure.ac +++ b/configure.ac @@ -1261,18 +1261,31 @@ enable_quic=no AC_MSG_CHECKING([whether APIs for QUIC are available]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ - #ifdef OPENSSL_IS_BORINGSSL SSL_QUIC_METHOD var; + ]]) + ], + [ + AC_MSG_RESULT([yes]) + enable_quic=yes + _quic_saved_LIBS=$LIBS + TS_ADDTO(LIBS, [$OPENSSL_LIBS]) + AC_CHECK_FUNCS(SSL_set_quic_early_data_enabled) + LIBS=$_quic_saved_LIBS + ], + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[ + #ifdef SSL_MODE_QUIC_HACK #else - #ifndef SSL_MODE_QUIC_HACK # error no hack for quic #endif - #endif ]]) ], - [AC_MSG_RESULT([yes]); enable_quic=yes], + [AC_MSG_RESULT([yes]); enable_quic=yes; enable_quic_old_api=yes], [AC_MSG_RESULT([no])]) + ]) + AM_CONDITIONAL([ENABLE_QUIC], [test "x$enable_quic" = "xyes"]) +AM_CONDITIONAL([ENABLE_QUIC_OLD_API], [test "x$enable_quic_old_api" = "xyes"]) TS_ARG_ENABLE_VAR([use], [quic]) AC_SUBST(use_quic) diff --git a/doc/admin-guide/files/records.config.en.rst b/doc/admin-guide/files/records.config.en.rst index 9c24300c04f..6f17595679d 100644 --- a/doc/admin-guide/files/records.config.en.rst +++ b/doc/admin-guide/files/records.config.en.rst @@ -3897,6 +3897,12 @@ QUIC Configuration All configurations for QUIC are still experimental and may be changed or removed in the future without prior notice. +.. ts:cv:: CONFIG proxy.config.quic.qlog_dir STRING NULL + :reloadable: + + The qlog is enabled when this configuration is not NULL. And will dump + the qlog to this dir. + .. ts:cv:: CONFIG proxy.config.quic.instance_id INT 0 :reloadable: diff --git a/iocore/net/P_Net.h b/iocore/net/P_Net.h index 46a05c3a784..b3027ce155a 100644 --- a/iocore/net/P_Net.h +++ b/iocore/net/P_Net.h @@ -111,13 +111,6 @@ extern RecRawStatBlock *net_rsb; #include "P_SSLNetAccept.h" #include "P_SSLCertLookup.h" -#if TS_USE_QUIC == 1 -#include "P_QUICNetVConnection.h" -#include "P_QUICNetProcessor.h" -#include "P_QUICPacketHandler.h" -#include "P_QUICNet.h" -#endif - static constexpr ts::ModuleVersion NET_SYSTEM_MODULE_INTERNAL_VERSION(NET_SYSTEM_MODULE_PUBLIC_VERSION, ts::ModuleVersion::PRIVATE); // For very verbose iocore debugging. diff --git a/iocore/net/P_QUICNet.h b/iocore/net/P_QUICNet.h index 802bf6ed6e6..a8a59a20199 100644 --- a/iocore/net/P_QUICNet.h +++ b/iocore/net/P_QUICNet.h @@ -29,6 +29,9 @@ #include "tscore/ink_platform.h" #include "P_Net.h" +#include "quic/QUICTypes.h" +#include "P_QUICNetProcessor.h" +#include "P_QUICNetVConnection.h" class NetHandler; typedef int (NetHandler::*NetContHandler)(int, void *); diff --git a/iocore/net/P_QUICNetProcessor.h b/iocore/net/P_QUICNetProcessor.h index bb3e8576b74..68e722814d5 100644 --- a/iocore/net/P_QUICNetProcessor.h +++ b/iocore/net/P_QUICNetProcessor.h @@ -42,6 +42,7 @@ #include "quic/QUICConnectionTable.h" class UnixNetVConnection; +class QUICResetTokenTable; struct NetAccept; ////////////////////////////////////////////////////////////////// @@ -73,6 +74,7 @@ class QUICNetProcessor : public UnixNetProcessor QUICNetProcessor &operator=(const QUICNetProcessor &); QUICConnectionTable *_ctable = nullptr; + QUICResetTokenTable *_rtable = nullptr; }; extern QUICNetProcessor quic_NetProcessor; diff --git a/iocore/net/P_QUICNetVConnection.h b/iocore/net/P_QUICNetVConnection.h index d39824e8074..a7aee81831d 100644 --- a/iocore/net/P_QUICNetVConnection.h +++ b/iocore/net/P_QUICNetVConnection.h @@ -37,18 +37,20 @@ #include "P_UnixNetVConnection.h" #include "P_UnixNet.h" #include "P_UDPNet.h" +#include "P_ALPNSupport.h" +#include "TLSSessionResumptionSupport.h" #include "tscore/ink_apidefs.h" #include "tscore/List.h" #include "quic/QUICConfig.h" #include "quic/QUICConnection.h" #include "quic/QUICConnectionTable.h" +#include "quic/QUICResetTokenTable.h" #include "quic/QUICVersionNegotiator.h" #include "quic/QUICPacket.h" #include "quic/QUICPacketFactory.h" #include "quic/QUICFrame.h" #include "quic/QUICFrameDispatcher.h" -#include "quic/QUICHandshake.h" #include "quic/QUICApplication.h" #include "quic/QUICStream.h" #include "quic/QUICHandshakeProtocol.h" @@ -62,10 +64,12 @@ #include "quic/QUICPathManager.h" #include "quic/QUICApplicationMap.h" #include "quic/QUICPacketReceiveQueue.h" +#include "quic/QUICPacketHeaderProtector.h" #include "quic/QUICAddrVerifyState.h" #include "quic/QUICPacketProtectionKeyInfo.h" #include "quic/QUICContext.h" #include "quic/QUICTokenCreator.h" +#include "quic/qlog/QLogListener.h" // Size of connection ids for debug log : e.g. aaaaaaaa-bbbbbbbb\0 static constexpr size_t MAX_CIDS_SIZE = 8 + 1 + 8 + 1; @@ -80,6 +84,7 @@ static constexpr size_t MAX_CIDS_SIZE = 8 + 1 + 8 + 1; class QUICPacketHandler; class QUICLossDetector; +class QUICHandshake; class SSLNextProtocolSet; @@ -131,16 +136,21 @@ class SSLNextProtocolSet; * WRITE: * Do nothing **/ -class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, public RefCountObj, public ALPNSupport +class QUICNetVConnection : public UnixNetVConnection, + public QUICConnection, + public RefCountObj, + public ALPNSupport, + public TLSSessionResumptionSupport { using super = UnixNetVConnection; ///< Parent type. public: QUICNetVConnection(); ~QUICNetVConnection(); - void init(QUICConnectionId peer_cid, QUICConnectionId original_cid, UDPConnection *, QUICPacketHandler *); + void init(QUICConnectionId peer_cid, QUICConnectionId original_cid, UDPConnection *, QUICPacketHandler *, + QUICResetTokenTable *rtable); void init(QUICConnectionId peer_cid, QUICConnectionId original_cid, QUICConnectionId first_cid, UDPConnection *, - QUICPacketHandler *, QUICConnectionTable *ctable); + QUICPacketHandler *, QUICResetTokenTable *rtable, QUICConnectionTable *ctable); // accept new conn_id int acceptEvent(int event, Event *e); @@ -180,7 +190,8 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub // QUICConnection QUICStreamManager *stream_manager() override; - void close(QUICConnectionErrorUPtr error) override; + void close_quic_connection(QUICConnectionErrorUPtr error) override; + void reset_quic_connection() override; void handle_received_packet(UDPPacket *packet) override; void ping() override; @@ -207,6 +218,9 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub LINK(QUICNetVConnection, closed_link); SLINK(QUICNetVConnection, closed_alink); +protected: + const IpEndpoint &_getLocalEndpoint() override; + private: std::random_device _rnd; @@ -247,6 +261,7 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub QUICCongestionController *_congestion_controller = nullptr; QUICRemoteFlowController *_remote_flow_controller = nullptr; QUICLocalFlowController *_local_flow_controller = nullptr; + QUICResetTokenTable *_rtable = nullptr; QUICConnectionTable *_ctable = nullptr; QUICAltConnectionManager *_alt_con_manager = nullptr; QUICPathValidator *_path_validator = nullptr; @@ -290,22 +305,23 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub Ptr _store_frame(Ptr parent_block, size_t &size_added, uint64_t &max_frame_size, QUICFrame &frame, std::vector &frames); - QUICPacketUPtr _packetize_frames(QUICEncryptionLevel level, uint64_t max_packet_size, std::vector &frames); + QUICPacketUPtr _packetize_frames(uint8_t *packet_buf, QUICEncryptionLevel level, uint64_t max_packet_size, + std::vector &frames); void _packetize_closing_frame(); - QUICPacketUPtr _build_packet(QUICEncryptionLevel level, Ptr parent_block, bool retransmittable, bool probing, - bool crypto); + QUICPacketUPtr _build_packet(uint8_t *packet_buf, QUICEncryptionLevel level, Ptr parent_block, + bool retransmittable, bool probing, bool crypto); - QUICConnectionErrorUPtr _recv_and_ack(const QUICPacket &packet, bool *has_non_probing_frame = nullptr); + QUICConnectionErrorUPtr _recv_and_ack(const QUICPacketR &packet, bool *has_non_probing_frame = nullptr); QUICConnectionErrorUPtr _state_handshake_process_packet(const QUICPacket &packet); - QUICConnectionErrorUPtr _state_handshake_process_version_negotiation_packet(const QUICPacket &packet); - QUICConnectionErrorUPtr _state_handshake_process_initial_packet(const QUICPacket &packet); - QUICConnectionErrorUPtr _state_handshake_process_retry_packet(const QUICPacket &packet); - QUICConnectionErrorUPtr _state_handshake_process_handshake_packet(const QUICPacket &packet); - QUICConnectionErrorUPtr _state_handshake_process_zero_rtt_protected_packet(const QUICPacket &packet); + QUICConnectionErrorUPtr _state_handshake_process_version_negotiation_packet(const QUICVersionNegotiationPacketR &packet); + QUICConnectionErrorUPtr _state_handshake_process_initial_packet(const QUICInitialPacketR &packet); + QUICConnectionErrorUPtr _state_handshake_process_retry_packet(const QUICRetryPacketR &packet); + QUICConnectionErrorUPtr _state_handshake_process_handshake_packet(const QUICHandshakePacketR &packet); + QUICConnectionErrorUPtr _state_handshake_process_zero_rtt_protected_packet(const QUICZeroRttPacketR &packet); QUICConnectionErrorUPtr _state_connection_established_receive_packet(); - QUICConnectionErrorUPtr _state_connection_established_process_protected_packet(const QUICPacket &packet); - QUICConnectionErrorUPtr _state_connection_established_migrate_connection(const QUICPacket &packet); + QUICConnectionErrorUPtr _state_connection_established_process_protected_packet(const QUICShortHeaderPacketR &packet); + QUICConnectionErrorUPtr _state_connection_established_migrate_connection(const QUICPacketR &packet); QUICConnectionErrorUPtr _state_connection_established_initiate_connection_migration(); QUICConnectionErrorUPtr _state_closing_receive_packet(); QUICConnectionErrorUPtr _state_draining_receive_packet(); @@ -318,7 +334,7 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub void _init_flow_control_params(const std::shared_ptr &local_tp, const std::shared_ptr &remote_tp); void _handle_error(QUICConnectionErrorUPtr error); - QUICPacketUPtr _dequeue_recv_packet(QUICPacketCreationResult &result); + QUICPacketUPtr _dequeue_recv_packet(uint8_t *packet_buf, QUICPacketCreationResult &result); void _validate_new_path(const QUICPath &path); int _complete_handshake_if_possible(); @@ -344,6 +360,7 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub QUICHandshakeProtocol *_setup_handshake_protocol(shared_SSL_CTX ctx); QUICPacketUPtr _the_final_packet = QUICPacketFactory::create_null_packet(); + uint8_t _final_packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; QUICStatelessResetToken _reset_token; ats_unique_buf _av_token = {nullptr}; @@ -353,9 +370,11 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub uint32_t _seq_num = 0; // TODO: Source addresses verification through an address validation token - QUICAddrVerifyState _verfied_state; + QUICAddrVerifyState _verified_state; + + std::unique_ptr _context; - std::unique_ptr _context; + std::shared_ptr _qlog; }; typedef int (QUICNetVConnection::*QUICNetVConnHandler)(int, void *); diff --git a/iocore/net/P_QUICPacketHandler.h b/iocore/net/P_QUICPacketHandler.h index 27a5235d911..4df92ece805 100644 --- a/iocore/net/P_QUICPacketHandler.h +++ b/iocore/net/P_QUICPacketHandler.h @@ -28,6 +28,7 @@ #include "P_NetAccept.h" #include "quic/QUICTypes.h" #include "quic/QUICConnectionTable.h" +#include "quic/QUICResetTokenTable.h" class QUICClosedConCollector; class QUICNetVConnection; @@ -37,7 +38,7 @@ class QUICPacketHeaderProtector; class QUICPacketHandler { public: - QUICPacketHandler(); + QUICPacketHandler(QUICResetTokenTable &rtable); ~QUICPacketHandler(); void send_packet(const QUICPacket &packet, QUICNetVConnection *vc, const QUICPacketHeaderProtector &pn_protector); @@ -49,6 +50,7 @@ class QUICPacketHandler void _send_packet(const QUICPacket &packet, UDPConnection *udp_con, IpEndpoint &addr, uint32_t pmtu, const QUICPacketHeaderProtector *ph_protector, int dcil); void _send_packet(UDPConnection *udp_con, IpEndpoint &addr, Ptr udp_payload); + QUICConnection *_check_stateless_reset(const uint8_t *buf, size_t buf_len); // FIXME Remove this // QUICPacketHandler could be a continuation, but NetAccept is a contination too. @@ -58,6 +60,8 @@ class QUICPacketHandler QUICClosedConCollector *_closed_con_collector = nullptr; virtual void _recv_packet(int event, UDPPacket *udpPacket) = 0; + + QUICResetTokenTable &_rtable; }; /* @@ -67,7 +71,7 @@ class QUICPacketHandler class QUICPacketHandlerIn : public NetAccept, public QUICPacketHandler { public: - QUICPacketHandlerIn(const NetProcessor::AcceptOptions &opt, QUICConnectionTable &ctable); + QUICPacketHandlerIn(const NetProcessor::AcceptOptions &opt, QUICConnectionTable &ctable, QUICResetTokenTable &rtable); ~QUICPacketHandlerIn(); // NetAccept @@ -84,6 +88,10 @@ class QUICPacketHandlerIn : public NetAccept, public QUICPacketHandler void _recv_packet(int event, UDPPacket *udp_packet) override; int _stateless_retry(const uint8_t *buf, uint64_t buf_len, UDPConnection *connection, IpEndpoint from, QUICConnectionId dcid, QUICConnectionId scid, QUICConnectionId *original_cid); + bool _send_stateless_reset(QUICConnectionId dcid, uint32_t instance_id, UDPConnection *udp_con, IpEndpoint &addr, + size_t maximum_size); + void _send_invalid_token_error(const uint8_t *initial_packet, uint64_t initial_packet_len, UDPConnection *connection, + IpEndpoint from); QUICConnectionTable &_ctable; }; @@ -95,7 +103,7 @@ class QUICPacketHandlerIn : public NetAccept, public QUICPacketHandler class QUICPacketHandlerOut : public Continuation, public QUICPacketHandler { public: - QUICPacketHandlerOut(); + QUICPacketHandlerOut(QUICResetTokenTable &rtable); ~QUICPacketHandlerOut(){}; void init(QUICNetVConnection *vc); diff --git a/iocore/net/QUICNet.cc b/iocore/net/QUICNet.cc index d3df166be11..34e21ae2272 100644 --- a/iocore/net/QUICNet.cc +++ b/iocore/net/QUICNet.cc @@ -22,6 +22,7 @@ */ #include "P_Net.h" +#include "P_QUICNet.h" #include "quic/QUICEvents.h" ClassAllocator quicPollEventAllocator("quicPollEvent"); @@ -68,7 +69,7 @@ QUICPollCont::_process_long_header_packet(QUICPollEvent *e, NetHandler *nh) uint8_t *buf = (uint8_t *)p->getIOBlockChain()->buf(); QUICPacketType ptype; - QUICPacketLongHeader::type(ptype, buf, 1); + QUICLongHeaderPacketR::type(ptype, buf, 1); if (ptype == QUICPacketType::INITIAL && !vc->read.triggered) { SCOPED_MUTEX_LOCK(lock, vc->mutex, this_ethread()); vc->read.triggered = 1; diff --git a/iocore/net/QUICNetProcessor.cc b/iocore/net/QUICNetProcessor.cc index a0c706a6d34..4dfebae1487 100644 --- a/iocore/net/QUICNetProcessor.cc +++ b/iocore/net/QUICNetProcessor.cc @@ -25,9 +25,13 @@ #include "P_Net.h" #include "records/I_RecHttp.h" +#include "P_QUICNetProcessor.h" +#include "P_QUICNet.h" +#include "P_QUICPacketHandler.h" #include "QUICGlobals.h" #include "QUICConfig.h" #include "QUICMultiCertConfigLoader.h" +#include "QUICResetTokenTable.h" // // Global Data @@ -76,8 +80,9 @@ QUICNetProcessor::createNetAccept(const NetProcessor::AcceptOptions &opt) if (this->_ctable == nullptr) { QUICConfig::scoped_config params; this->_ctable = new QUICConnectionTable(params->connection_table_size()); + this->_rtable = new QUICResetTokenTable(); } - return (NetAccept *)new QUICPacketHandlerIn(opt, *this->_ctable); + return (NetAccept *)new QUICPacketHandlerIn(opt, *this->_ctable, *this->_rtable); } NetVConnection * @@ -125,7 +130,8 @@ QUICNetProcessor::connect_re(Continuation *cont, sockaddr const *remote_addr, Ne UnixUDPConnection *con = new UnixUDPConnection(fd); Debug("quic_ps", "con=%p fd=%d", con, fd); - QUICPacketHandlerOut *packet_handler = new QUICPacketHandlerOut(); + this->_rtable = new QUICResetTokenTable(); + QUICPacketHandlerOut *packet_handler = new QUICPacketHandlerOut(*this->_rtable); if (opt->local_ip.isValid()) { con->setBinding(opt->local_ip, opt->local_port); } @@ -144,7 +150,7 @@ QUICNetProcessor::connect_re(Continuation *cont, sockaddr const *remote_addr, Ne QUICConnectionId client_dst_cid; client_dst_cid.randomize(); // vc->init set handler of vc `QUICNetVConnection::startEvent` - vc->init(client_dst_cid, client_dst_cid, con, packet_handler); + vc->init(client_dst_cid, client_dst_cid, con, packet_handler, this->_rtable); packet_handler->init(vc); // Connection ID will be changed diff --git a/iocore/net/QUICNetVConnection.cc b/iocore/net/QUICNetVConnection.cc index 004867d0bcf..833875337f4 100644 --- a/iocore/net/QUICNetVConnection.cc +++ b/iocore/net/QUICNetVConnection.cc @@ -27,6 +27,8 @@ #include "records/I_RecHttp.h" #include "tscore/Diags.h" +#include "P_QUICNetVConnection.h" +#include "P_QUICPacketHandler.h" #include "P_Net.h" #include "InkAPIInternal.h" // Added to include the quic_hook definitions #include "Log.h" @@ -39,12 +41,16 @@ #include "QUICGlobals.h" #include "QUICDebugNames.h" #include "QUICEvents.h" +#include "QUICHandshake.h" #include "QUICConfig.h" #include "QUICIntUtil.h" using namespace std::literals; static constexpr std::string_view QUIC_DEBUG_TAG = "quic_net"sv; +static constexpr uint16_t QUANTUM_TEST_ID = 3127; +static constexpr uint8_t QUANTUM_TEST_VALUE[1200] = {'Q'}; + #define QUICConDebug(fmt, ...) Debug(QUIC_DEBUG_TAG.data(), "[%s] " fmt, this->cids().data(), ##__VA_ARGS__) #define QUICConVDebug(fmt, ...) Debug("v_quic_net", "[%s] " fmt, this->cids().data(), ##__VA_ARGS__) @@ -188,9 +194,33 @@ class QUICTPConfigQCP : public QUICTPConfig } } + bool + disable_active_migration() const override + { + if (this->_ctx == NET_VCONNECTION_IN) { + return this->_params->disable_active_migration(); + } else { + return false; + } + } + + std::unordered_map> + additional_tp() const override + { + return this->_additional_tp; + } + + void + add_tp(uint16_t id, const uint8_t *value, uint16_t length) + { + this->_additional_tp.emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(value, length)); + } + private: const QUICConfigParams *_params; NetVConnectionContext_t _ctx; + + std::unordered_map> _additional_tp; }; QUICNetVConnection::QUICNetVConnection() : _packet_factory(this->_pp_key_info), _ph_protector(this->_pp_key_info) {} @@ -207,31 +237,28 @@ QUICNetVConnection::~QUICNetVConnection() // Initialize QUICNetVC for out going connection (NET_VCONNECTION_OUT) void QUICNetVConnection::init(QUICConnectionId peer_cid, QUICConnectionId original_cid, UDPConnection *udp_con, - QUICPacketHandler *packet_handler) + QUICPacketHandler *packet_handler, QUICResetTokenTable *rtable) { SET_HANDLER((NetVConnHandler)&QUICNetVConnection::startEvent); this->_udp_con = udp_con; this->_packet_handler = packet_handler; this->_peer_quic_connection_id = peer_cid; this->_original_quic_connection_id = original_cid; + this->_rtable = rtable; this->_quic_connection_id.randomize(); this->_update_cids(); if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { - char dcid_hex_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - char scid_hex_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - this->_peer_quic_connection_id.hex(dcid_hex_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - this->_quic_connection_id.hex(scid_hex_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - - QUICConDebug("dcid=%s scid=%s", dcid_hex_str, scid_hex_str); + QUICConDebug("dcid=%s scid=%s", this->_peer_quic_connection_id.hex().c_str(), this->_quic_connection_id.hex().c_str()); } } // Initialize QUICNetVC for in coming connection (NET_VCONNECTION_IN) void QUICNetVConnection::init(QUICConnectionId peer_cid, QUICConnectionId original_cid, QUICConnectionId first_cid, - UDPConnection *udp_con, QUICPacketHandler *packet_handler, QUICConnectionTable *ctable) + UDPConnection *udp_con, QUICPacketHandler *packet_handler, QUICResetTokenTable *rtable, + QUICConnectionTable *ctable) { SET_HANDLER((NetVConnHandler)&QUICNetVConnection::acceptEvent); this->_udp_con = udp_con; @@ -246,16 +273,12 @@ QUICNetVConnection::init(QUICConnectionId peer_cid, QUICConnectionId original_ci this->_ctable->insert(this->_quic_connection_id, this); this->_ctable->insert(this->_original_quic_connection_id, this); } + this->_rtable = rtable; this->_update_cids(); if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { - char dcid_hex_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - char scid_hex_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - this->_peer_quic_connection_id.hex(dcid_hex_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - this->_quic_connection_id.hex(scid_hex_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - - QUICConDebug("dcid=%s scid=%s", dcid_hex_str, scid_hex_str); + QUICConDebug("dcid=%s scid=%s", this->_peer_quic_connection_id.hex().c_str(), this->_quic_connection_id.hex().c_str()); } } @@ -356,7 +379,17 @@ void QUICNetVConnection::start() { ink_release_assert(this->thread != nullptr); - this->_context = std::make_unique(&this->_rtt_measure, this, &this->_pp_key_info); + this->_path_validator = new QUICPathValidator(*this, [this](bool succeeded) { + if (succeeded) { + this->_alt_con_manager->drop_cid(this->_peer_old_quic_connection_id); + // FIXME This is a kind of workaround for connection migration. + // This PING make peer to send an ACK frame so that ATS can detect packet loss. + // It would be better if QUICLossDetector could detect the loss in another way. + this->ping(); + } + }); + this->_path_manager = new QUICPathManagerImpl(*this, *this->_path_validator); + this->_context = std::make_unique(&this->_rtt_measure, this, &this->_pp_key_info, this->_path_manager); this->_five_tuple.update(this->local_addr, this->remote_addr, SOCK_DGRAM); QUICPath trusted_path = {{}, {}}; // Version 0x00000001 uses stream 0 for cryptographic handshake with TLS 1.3, but newer version may not @@ -374,6 +407,9 @@ QUICNetVConnection::start() } else { trusted_path = {this->local_addr, this->remote_addr}; QUICTPConfigQCP tp_config(this->_quic_config, NET_VCONNECTION_OUT); + if (this->_quic_config->quantum_readiness_test_enabled_out()) { + tp_config.add_tp(QUANTUM_TEST_ID, QUANTUM_TEST_VALUE, sizeof(QUANTUM_TEST_VALUE)); + } this->_pp_key_info.set_context(QUICPacketProtectionKeyInfo::Context::CLIENT); this->_ack_frame_manager.set_ack_delay_exponent(this->_quic_config->ack_delay_exponent_out()); this->_hs_protocol = this->_setup_handshake_protocol(this->_quic_config->client_ssl_ctx()); @@ -383,6 +419,7 @@ QUICNetVConnection::start() this->_ack_frame_manager.set_max_ack_delay(this->_quic_config->max_ack_delay_out()); this->_schedule_ack_manager_periodic(this->_quic_config->max_ack_delay_out()); } + this->_path_manager->set_trusted_path(trusted_path); this->_application_map = new QUICApplicationMap(); @@ -399,19 +436,8 @@ QUICNetVConnection::start() this->_remote_flow_controller = new QUICRemoteConnectionFlowController(UINT64_MAX); this->_local_flow_controller = new QUICLocalConnectionFlowController(&this->_rtt_measure, UINT64_MAX); - this->_path_validator = new QUICPathValidator(*this, [this](bool succeeded) { - if (succeeded) { - this->_alt_con_manager->drop_cid(this->_peer_old_quic_connection_id); - // FIXME This is a kind of workaround for connection migration. - // This PING make peer to send an ACK frame so that ATS can detect packet loss. - // It would be better if QUICLossDetector could detect the loss in another way. - this->ping(); - } - }); - this->_stream_manager = new QUICStreamManager(this, &this->_rtt_measure, this->_application_map); - this->_path_manager = new QUICPathManager(*this, *this->_path_validator); - this->_path_manager->set_trusted_path(trusted_path); - this->_token_creator = new QUICTokenCreator(this->_context.get()); + this->_stream_manager = new QUICStreamManager(this->_context.get(), this->_application_map); + this->_token_creator = new QUICTokenCreator(this->_context.get()); static constexpr int QUIC_STREAM_MANAGER_WEIGHT = QUICFrameGeneratorWeight::AFTER_DATA - 1; static constexpr int QUIC_PINGER_WEIGHT = QUICFrameGeneratorWeight::LATE + 1; @@ -436,6 +462,14 @@ QUICNetVConnection::start() this->_frame_dispatcher->add_handler(this->_stream_manager); this->_frame_dispatcher->add_handler(this->_path_validator); this->_frame_dispatcher->add_handler(this->_handshake_handler); + + // regist qlog + if (this->_context->config()->qlog_dir() != nullptr) { + this->_qlog = std::make_unique(*this->_context, this->_original_quic_connection_id.hex()); + this->_qlog->last_trace().set_vantage_point( + {"ats", QLog::Trace::VantagePointType::server, QLog::Trace::VantagePointType::server}); + this->_context->regist_callback(this->_qlog); + } } void @@ -462,6 +496,7 @@ QUICNetVConnection::free(EThread *t) super::clear(); */ + this->_context->trigger(QUICContext::CallbackEvent::CONNECTION_CLOSE); ALPNSupport::clear(); this->_packet_handler->close_connection(this); } @@ -641,11 +676,11 @@ QUICNetVConnection::handle_received_packet(UDPPacket *packet) void QUICNetVConnection::ping() { - this->_pinger->request(); + this->_pinger->request(QUICEncryptionLevel::ONE_RTT); } void -QUICNetVConnection::close(QUICConnectionErrorUPtr error) +QUICNetVConnection::close_quic_connection(QUICConnectionErrorUPtr error) { if (this->handler == reinterpret_cast(&QUICNetVConnection::state_connection_closed) || this->handler == reinterpret_cast(&QUICNetVConnection::state_connection_closing)) { @@ -655,6 +690,24 @@ QUICNetVConnection::close(QUICConnectionErrorUPtr error) } } +void +QUICNetVConnection::reset_quic_connection() +{ + this->_switch_to_close_state(); + + QUICStatelessResetToken token(this->connection_id(), this->_quic_config->instance_id()); + auto packet = QUICPacketFactory::create_stateless_reset_packet(token, 128); + if (packet) { + Ptr udp_payload(new_IOBufferBlock()); + udp_payload->alloc(iobuffer_size_to_index(128, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(udp_payload->end()); + size_t len = 0; + packet->store(buf, &len); + udp_payload->fill(len); + this->_packet_handler->send_packet(this, udp_payload); + } +} + std::vector QUICNetVConnection::interests() { @@ -745,7 +798,8 @@ QUICNetVConnection::state_handshake(int event, Event *data) QUICPacketCreationResult result; net_activity(this, this_ethread()); do { - QUICPacketUPtr packet = this->_dequeue_recv_packet(result); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet = this->_dequeue_recv_packet(packet_buf, result); if (result == QUICPacketCreationResult::NOT_READY) { error = nullptr; } else if (result == QUICPacketCreationResult::FAILED) { @@ -767,6 +821,9 @@ QUICNetVConnection::state_handshake(int event, Event *data) } while (error == nullptr && (result == QUICPacketCreationResult::SUCCESS || result == QUICPacketCreationResult::IGNORED)); break; } + case QUIC_EVENT_STATELESS_RESET: + this->_switch_to_draining_state(std::make_unique(QUICTransErrorCode::NO_ERROR, "Stateless Reset")); + break; case QUIC_EVENT_ACK_PERIODIC: this->_handle_periodic_ack_event(); break; @@ -810,6 +867,9 @@ QUICNetVConnection::state_connection_established(int event, Event *data) // Reschedule WRITE_READY this->_schedule_packet_write_ready(true); break; + case QUIC_EVENT_STATELESS_RESET: + this->_switch_to_draining_state(std::make_unique(QUICTransErrorCode::NO_ERROR, "Stateless Reset")); + break; case VC_EVENT_INACTIVITY_TIMEOUT: // Start Immediate Close because of Idle Timeout this->_handle_idle_timeout(); @@ -844,6 +904,8 @@ QUICNetVConnection::state_connection_closing(int event, Event *data) this->_close_closing_timeout(data); this->_switch_to_close_state(); break; + case QUIC_EVENT_STATELESS_RESET: + break; case QUIC_EVENT_ACK_PERIODIC: default: QUICConDebug("Unexpected event: %s (%d)", QUICDebugNames::quic_event(event), event); @@ -872,6 +934,8 @@ QUICNetVConnection::state_connection_draining(int event, Event *data) this->_close_closing_timeout(data); this->_switch_to_close_state(); break; + case QUIC_EVENT_STATELESS_RESET: + break; case QUIC_EVENT_ACK_PERIODIC: default: QUICConDebug("Unexpected event: %s (%d)", QUICDebugNames::quic_event(event), event); @@ -1024,28 +1088,28 @@ QUICNetVConnection::_state_handshake_process_packet(const QUICPacket &packet) QUICConnectionErrorUPtr error = nullptr; switch (packet.type()) { case QUICPacketType::VERSION_NEGOTIATION: - error = this->_state_handshake_process_version_negotiation_packet(packet); + error = this->_state_handshake_process_version_negotiation_packet(static_cast(packet)); break; case QUICPacketType::INITIAL: - error = this->_state_handshake_process_initial_packet(packet); + error = this->_state_handshake_process_initial_packet(static_cast(packet)); break; case QUICPacketType::RETRY: - error = this->_state_handshake_process_retry_packet(packet); + error = this->_state_handshake_process_retry_packet(static_cast(packet)); break; case QUICPacketType::HANDSHAKE: - error = this->_state_handshake_process_handshake_packet(packet); + error = this->_state_handshake_process_handshake_packet(static_cast(packet)); if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::INITIAL) && this->netvc_context == NET_VCONNECTION_IN) { this->_pp_key_info.drop_keys(QUICKeyPhase::INITIAL); this->_minimum_encryption_level = QUICEncryptionLevel::HANDSHAKE; } break; case QUICPacketType::ZERO_RTT_PROTECTED: - error = this->_state_handshake_process_zero_rtt_protected_packet(packet); + error = this->_state_handshake_process_zero_rtt_protected_packet(static_cast(packet)); break; case QUICPacketType::PROTECTED: - default: QUICConDebug("Ignore %s(%" PRIu8 ") packet", QUICDebugNames::packet_type(packet.type()), static_cast(packet.type())); - + break; + default: error = std::make_unique(QUICTransErrorCode::INTERNAL_ERROR); break; } @@ -1053,7 +1117,7 @@ QUICNetVConnection::_state_handshake_process_packet(const QUICPacket &packet) } QUICConnectionErrorUPtr -QUICNetVConnection::_state_handshake_process_version_negotiation_packet(const QUICPacket &packet) +QUICNetVConnection::_state_handshake_process_version_negotiation_packet(const QUICVersionNegotiationPacketR &packet) { QUICConnectionErrorUPtr error = nullptr; @@ -1082,7 +1146,7 @@ QUICNetVConnection::_state_handshake_process_version_negotiation_packet(const QU } QUICConnectionErrorUPtr -QUICNetVConnection::_state_handshake_process_initial_packet(const QUICPacket &packet) +QUICNetVConnection::_state_handshake_process_initial_packet(const QUICInitialPacketR &packet) { // QUIC packet could be smaller than MINIMUM_INITIAL_PACKET_SIZE when coalescing packets // if (packet->size() < MINIMUM_INITIAL_PACKET_SIZE) { @@ -1101,28 +1165,31 @@ QUICNetVConnection::_state_handshake_process_initial_packet(const QUICPacket &pa if (!this->_alt_con_manager) { this->_alt_con_manager = - new QUICAltConnectionManager(this, *this->_ctable, this->_peer_quic_connection_id, this->_quic_config->instance_id(), - this->_quic_config->active_cid_limit_in(), this->_quic_config->preferred_address_ipv4(), - this->_quic_config->preferred_address_ipv6()); + new QUICAltConnectionManager(this, *this->_ctable, *this->_rtable, this->_peer_quic_connection_id, + this->_quic_config->instance_id(), this->_quic_config->active_cid_limit_in(), + this->_quic_config->preferred_address_ipv4(), this->_quic_config->preferred_address_ipv6()); this->_frame_generators.add_generator(*this->_alt_con_manager, QUICFrameGeneratorWeight::EARLY); this->_frame_dispatcher->add_handler(this->_alt_con_manager); } QUICTPConfigQCP tp_config(this->_quic_config, NET_VCONNECTION_IN); + if (this->_quic_config->quantum_readiness_test_enabled_in()) { + tp_config.add_tp(QUANTUM_TEST_ID, QUANTUM_TEST_VALUE, sizeof(QUANTUM_TEST_VALUE)); + } error = this->_handshake_handler->start(tp_config, packet, &this->_packet_factory, this->_alt_con_manager->preferred_address()); // If version negotiation was failed and VERSION NEGOTIATION packet was sent, nothing to do. if (this->_handshake_handler->is_version_negotiated()) { error = this->_recv_and_ack(packet); - if (error == nullptr && !this->_handshake_handler->has_remote_tp()) { + if (error == nullptr && this->_handshake_handler->is_completed() && !this->_handshake_handler->has_remote_tp()) { error = std::make_unique(QUICTransErrorCode::TRANSPORT_PARAMETER_ERROR); } } } else { if (!this->_alt_con_manager) { this->_alt_con_manager = - new QUICAltConnectionManager(this, *this->_ctable, this->_peer_quic_connection_id, this->_quic_config->instance_id(), - this->_quic_config->active_cid_limit_out()); + new QUICAltConnectionManager(this, *this->_ctable, *this->_rtable, this->_peer_quic_connection_id, + this->_quic_config->instance_id(), this->_quic_config->active_cid_limit_out()); this->_frame_generators.add_generator(*this->_alt_con_manager, QUICFrameGeneratorWeight::BEFORE_DATA); this->_frame_dispatcher->add_handler(this->_alt_con_manager); } @@ -1138,7 +1205,7 @@ QUICNetVConnection::_state_handshake_process_initial_packet(const QUICPacket &pa This doesn't call this->_recv_and_ack(), because RETRY packet doesn't have any frames. */ QUICConnectionErrorUPtr -QUICNetVConnection::_state_handshake_process_retry_packet(const QUICPacket &packet) +QUICNetVConnection::_state_handshake_process_retry_packet(const QUICRetryPacketR &packet) { ink_assert(this->netvc_context == NET_VCONNECTION_OUT); @@ -1147,10 +1214,19 @@ QUICNetVConnection::_state_handshake_process_retry_packet(const QUICPacket &pack return nullptr; } + // Check Integrity Tag + if (!packet.has_valid_tag(this->_original_quic_connection_id)) { + // Discard the packet + QUICConDebug("Ignore RETRY packet - integrity tag is not valid"); + return nullptr; + } else { + QUICConDebug("Integrity tag is valid"); + } + // TODO: move packet->payload to _av_token - this->_av_token_len = packet.payload_length(); + this->_av_token_len = packet.token().length(); this->_av_token = ats_unique_malloc(this->_av_token_len); - memcpy(this->_av_token.get(), packet.payload(), this->_av_token_len); + memcpy(this->_av_token.get(), packet.token().buf(), this->_av_token_len); this->_padder->set_av_token_len(this->_av_token_len); @@ -1174,18 +1250,18 @@ QUICNetVConnection::_state_handshake_process_retry_packet(const QUICPacket &pack } QUICConnectionErrorUPtr -QUICNetVConnection::_state_handshake_process_handshake_packet(const QUICPacket &packet) +QUICNetVConnection::_state_handshake_process_handshake_packet(const QUICHandshakePacketR &packet) { // Source address is verified by receiving any message from the client encrypted using the // Handshake keys. - if (this->netvc_context == NET_VCONNECTION_IN && !this->_verfied_state.is_verified()) { - this->_verfied_state.set_addr_verifed(); + if (this->netvc_context == NET_VCONNECTION_IN && !this->_verified_state.is_verified()) { + this->_verified_state.set_addr_verifed(); } return this->_recv_and_ack(packet); } QUICConnectionErrorUPtr -QUICNetVConnection::_state_handshake_process_zero_rtt_protected_packet(const QUICPacket &packet) +QUICNetVConnection::_state_handshake_process_zero_rtt_protected_packet(const QUICZeroRttPacketR &packet) { this->_stream_manager->init_flow_control_params(this->_handshake_handler->local_transport_parameters(), this->_handshake_handler->remote_transport_parameters()); @@ -1194,7 +1270,7 @@ QUICNetVConnection::_state_handshake_process_zero_rtt_protected_packet(const QUI } QUICConnectionErrorUPtr -QUICNetVConnection::_state_connection_established_process_protected_packet(const QUICPacket &packet) +QUICNetVConnection::_state_connection_established_process_protected_packet(const QUICShortHeaderPacketR &packet) { QUICConnectionErrorUPtr error = nullptr; bool has_non_probing_frame = false; @@ -1204,16 +1280,10 @@ QUICNetVConnection::_state_connection_established_process_protected_packet(const return error; } - // Migrate connection if required - // FIXME Connection migration will be initiated when a peer sent non-probing frames. - // We need to two or more paths because we need to respond to probing packets on a new path and also need to send other frames - // on the old path until they initiate migration. - // if (packet.destination_cid() == this->_quic_connection_id && has_non_probing_frame) { + // Migrate connection if needed if (this->_alt_con_manager != nullptr) { - if (packet.destination_cid() != this->_quic_connection_id || !ats_ip_addr_port_eq(packet.from(), this->remote_addr)) { - if (!has_non_probing_frame) { - QUICConDebug("FIXME: Connection migration has been initiated without non-probing frames"); - } + if (has_non_probing_frame && + (packet.destination_cid() != this->_quic_connection_id || !ats_ip_addr_port_eq(packet.from(), this->remote_addr))) { error = this->_state_connection_established_migrate_connection(packet); if (error != nullptr) { return error; @@ -1238,7 +1308,8 @@ QUICNetVConnection::_state_connection_established_receive_packet() // Receive a QUIC packet net_activity(this, this_ethread()); do { - QUICPacketUPtr packet = this->_dequeue_recv_packet(result); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet = this->_dequeue_recv_packet(packet_buf, result); if (result == QUICPacketCreationResult::FAILED) { // Don't make this error, and discard the packet. // Because: @@ -1256,13 +1327,13 @@ QUICNetVConnection::_state_connection_established_receive_packet() // Process the packet switch (packet->type()) { case QUICPacketType::PROTECTED: - error = this->_state_connection_established_process_protected_packet(*packet); + error = this->_state_connection_established_process_protected_packet(static_cast(*packet)); break; case QUICPacketType::INITIAL: case QUICPacketType::HANDSHAKE: case QUICPacketType::ZERO_RTT_PROTECTED: // Pass packet to _recv_and_ack to send ack to the packet. Stream data will be discarded by offset mismatch. - error = this->_recv_and_ack(*packet); + error = this->_recv_and_ack(static_cast(*packet)); break; default: QUICConDebug("Unknown packet type: %s(%" PRIu8 ")", QUICDebugNames::packet_type(packet->type()), @@ -1281,14 +1352,15 @@ QUICNetVConnection::_state_closing_receive_packet() { while (this->_packet_recv_queue.size() > 0) { QUICPacketCreationResult result; - QUICPacketUPtr packet = this->_dequeue_recv_packet(result); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet = this->_dequeue_recv_packet(packet_buf, result); if (result == QUICPacketCreationResult::SUCCESS) { switch (packet->type()) { case QUICPacketType::VERSION_NEGOTIATION: // Ignore VN packets on closing state break; default: - this->_recv_and_ack(*packet); + this->_recv_and_ack(static_cast(*packet)); break; } } @@ -1312,9 +1384,10 @@ QUICNetVConnection::_state_draining_receive_packet() { while (this->_packet_recv_queue.size() > 0) { QUICPacketCreationResult result; - QUICPacketUPtr packet = this->_dequeue_recv_packet(result); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet = this->_dequeue_recv_packet(packet_buf, result); if (result == QUICPacketCreationResult::SUCCESS) { - this->_recv_and_ack(*packet); + this->_recv_and_ack(static_cast(*packet)); // Do NOT schedule WRITE_READY event from this point. // An endpoint in the draining state MUST NOT send any packets. } @@ -1348,25 +1421,34 @@ QUICNetVConnection::_state_common_send_packet() uint32_t written = 0; for (int i = static_cast(this->_minimum_encryption_level); i <= static_cast(QUICEncryptionLevel::ONE_RTT); ++i) { + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + auto level = QUIC_ENCRYPTION_LEVELS[i]; if (level == QUICEncryptionLevel::ONE_RTT && !this->_handshake_handler->is_completed()) { continue; } uint32_t max_packet_size = udp_payload_len - written; - if (this->netvc_context == NET_VCONNECTION_IN && !this->_verfied_state.is_verified()) { - max_packet_size = std::min(max_packet_size, this->_verfied_state.windows()); + if (this->netvc_context == NET_VCONNECTION_IN && !this->_verified_state.is_verified()) { + max_packet_size = std::min(max_packet_size, this->_verified_state.windows()); } QUICPacketInfoUPtr packet_info = std::make_unique(); - QUICPacketUPtr packet = this->_packetize_frames(level, max_packet_size, packet_info->frames); + QUICPacketUPtr packet = this->_packetize_frames(packet_buf, level, max_packet_size, packet_info->frames); if (packet) { - packet_info->packet_number = packet->packet_number(); - packet_info->time_sent = Thread::get_hrtime(); - packet_info->ack_eliciting = packet->is_ack_eliciting(); - packet_info->is_crypto_packet = packet->is_crypto_packet(); - packet_info->in_flight = true; + // trigger callback + this->_context->trigger(QUICContext::CallbackEvent::PACKET_SEND, packet.get()); + + packet_info->packet_number = packet->packet_number(); + packet_info->time_sent = Thread::get_hrtime(); + packet_info->ack_eliciting = packet->is_ack_eliciting(); + if (packet->type() == QUICPacketType::PROTECTED) { + packet_info->is_crypto_packet = false; + } else { + packet_info->is_crypto_packet = static_cast(*packet).is_crypto_packet(); + } + packet_info->in_flight = true; if (packet_info->ack_eliciting) { packet_info->sent_bytes = packet->size(); } else { @@ -1375,9 +1457,9 @@ QUICNetVConnection::_state_common_send_packet() packet_info->type = packet->type(); packet_info->pn_space = QUICTypeUtil::pn_space(level); - if (this->netvc_context == NET_VCONNECTION_IN && !this->_verfied_state.is_verified()) { - QUICConDebug("send to unverified window: %u", this->_verfied_state.windows()); - this->_verfied_state.consume(packet->size()); + if (this->netvc_context == NET_VCONNECTION_IN && !this->_verified_state.is_verified()) { + QUICConDebug("send to unverified window: %u", this->_verified_state.windows()); + this->_verified_state.consume(packet->size()); } // TODO: do not write two QUIC Short Header Packets @@ -1415,6 +1497,9 @@ QUICNetVConnection::_state_common_send_packet() } if (packet_count) { + this->_context->trigger(QUICContext::CallbackEvent::METRICS_UPDATE, this->_congestion_controller->congestion_window(), + this->_congestion_controller->bytes_in_flight(), this->_congestion_controller->current_ssthresh()); + QUIC_INCREMENT_DYN_STAT_EX(QUICStats::total_packets_sent_stat, packet_count); net_activity(this, this_ethread()); } @@ -1447,11 +1532,9 @@ QUICNetVConnection::_store_frame(Ptr parent_block, size_t &size_a { Ptr new_block = frame.to_io_buffer_block(max_frame_size); - size_added = 0; - Ptr tmp = new_block; - while (tmp) { + size_added = 0; + for (Ptr tmp = new_block; tmp; tmp = tmp->next) { size_added += tmp->size(); - tmp = tmp->next; } if (parent_block == nullptr) { @@ -1480,7 +1563,8 @@ QUICNetVConnection::_store_frame(Ptr parent_block, size_t &size_a } QUICPacketUPtr -QUICNetVConnection::_packetize_frames(QUICEncryptionLevel level, uint64_t max_packet_size, std::vector &frames) +QUICNetVConnection::_packetize_frames(uint8_t *packet_buf, QUICEncryptionLevel level, uint64_t max_packet_size, + std::vector &frames) { QUICPacketUPtr packet = QUICPacketFactory::create_null_packet(); if (max_packet_size <= MAX_PACKET_OVERHEAD) { @@ -1516,24 +1600,17 @@ QUICNetVConnection::_packetize_frames(QUICEncryptionLevel level, uint64_t max_pa break; } - if (g == this->_stream_manager) { - // Don't send DATA frames if current path is not validated - // FIXME will_generate_frame should receive more parameters so we don't need extra checks - if (auto path = this->_path_manager->get_verified_path(); !path.remote_ep().isValid()) { - break; - } - } - // Common block frame = g->generate_frame(frame_instance_buffer, level, this->_remote_flow_controller->credit(), max_frame_size, len, seq_num); if (frame) { + this->_context->trigger(QUICContext::CallbackEvent::FRAME_PACKETIZE, *frame); // Some frame types must not be sent on Initial and Handshake packets switch (auto t = frame->type(); level) { case QUICEncryptionLevel::INITIAL: case QUICEncryptionLevel::HANDSHAKE: ink_assert(t == QUICFrameType::CRYPTO || t == QUICFrameType::ACK || t == QUICFrameType::PADDING || - t == QUICFrameType::CONNECTION_CLOSE); + t == QUICFrameType::CONNECTION_CLOSE || t == QUICFrameType::PING); break; default: break; @@ -1576,7 +1653,7 @@ QUICNetVConnection::_packetize_frames(QUICEncryptionLevel level, uint64_t max_pa // Schedule a packet if (len != 0) { // Packet is retransmittable if it's not ack only packet - packet = this->_build_packet(level, first_block, ack_eliciting, probing, crypto); + packet = this->_build_packet(packet_buf, level, first_block, ack_eliciting, probing, crypto); } return packet; @@ -1600,22 +1677,30 @@ QUICNetVConnection::_packetize_closing_frame() size_t size_added = 0; uint64_t max_frame_size = static_cast(max_size); std::vector frames; - Ptr parent_block; - parent_block = nullptr; - parent_block = this->_store_frame(parent_block, size_added, max_frame_size, *frame, frames); + Ptr first_block = make_ptr(new_IOBufferBlock()); + Ptr last_block = first_block; + first_block->alloc(iobuffer_size_to_index(0, BUFFER_SIZE_INDEX_32K)); + first_block->fill(0); + last_block = this->_store_frame(last_block, size_added, max_frame_size, *frame, frames); QUICEncryptionLevel level = this->_hs_protocol->current_encryption_level(); ink_assert(level != QUICEncryptionLevel::ZERO_RTT); - this->_the_final_packet = this->_build_packet(level, parent_block, true, false, false); + this->_the_final_packet = this->_build_packet(this->_final_packet_buf, level, first_block, true, false, false); } QUICConnectionErrorUPtr -QUICNetVConnection::_recv_and_ack(const QUICPacket &packet, bool *has_non_probing_frame) +QUICNetVConnection::_recv_and_ack(const QUICPacketR &packet, bool *has_non_probing_frame) { ink_assert(packet.type() != QUICPacketType::RETRY); - const uint8_t *payload = packet.payload(); uint16_t size = packet.payload_length(); + ats_unique_buf payload_ubuf = ats_unique_malloc(size); + uint8_t *payload = payload_ubuf.get(); + size_t copied_len = 0; + for (auto b = packet.payload_block(); b; b = b->next) { + memcpy(payload + copied_len, b->start(), b->size()); + copied_len += b->size(); + } QUICPacketNumber packet_num = packet.packet_number(); QUICEncryptionLevel level = QUICTypeUtil::encryption_level(packet.type()); @@ -1627,8 +1712,10 @@ QUICNetVConnection::_recv_and_ack(const QUICPacket &packet, bool *has_non_probin *has_non_probing_frame = false; } - error = - this->_frame_dispatcher->receive_frames(level, payload, size, ack_only, is_flow_controlled, has_non_probing_frame, &packet); + error = this->_frame_dispatcher->receive_frames(*this->_context, level, payload, size, ack_only, is_flow_controlled, + has_non_probing_frame, static_cast(&packet)); + this->_context->trigger(QUICContext::CallbackEvent::PACKET_RECV, &packet); + if (error != nullptr) { return error; } @@ -1653,21 +1740,15 @@ QUICNetVConnection::_recv_and_ack(const QUICPacket &packet, bool *has_non_probin } QUICPacketUPtr -QUICNetVConnection::_build_packet(QUICEncryptionLevel level, Ptr parent_block, bool ack_eliciting, bool probing, - bool crypto) +QUICNetVConnection::_build_packet(uint8_t *packet_buf, QUICEncryptionLevel level, Ptr parent_block, + bool ack_eliciting, bool probing, bool crypto) { QUICPacketType type = QUICTypeUtil::packet_type(level); QUICPacketUPtr packet = QUICPacketFactory::create_null_packet(); - // FIXME Pass parent_block to create_x_packet - // No need to make a unique buf here - ats_unique_buf buf = ats_unique_malloc(2048); - uint8_t *raw_buf = buf.get(); - size_t len = 0; - while (parent_block) { - memcpy(raw_buf + len, parent_block->start(), parent_block->size()); - len += parent_block->size(); - parent_block = parent_block->next; + size_t len = 0; + for (Ptr tmp = parent_block; tmp; tmp = tmp->next) { + len += tmp->size(); } switch (type) { @@ -1688,26 +1769,26 @@ QUICNetVConnection::_build_packet(QUICEncryptionLevel level, Ptr } packet = this->_packet_factory.create_initial_packet( - dcid, this->_quic_connection_id, this->_largest_acked_packet_number(QUICEncryptionLevel::INITIAL), std::move(buf), len, - ack_eliciting, probing, crypto, std::move(token), token_len); + packet_buf, dcid, this->_quic_connection_id, this->_largest_acked_packet_number(QUICEncryptionLevel::INITIAL), parent_block, + len, ack_eliciting, probing, crypto, std::move(token), token_len); break; } case QUICPacketType::HANDSHAKE: { - packet = this->_packet_factory.create_handshake_packet(this->_peer_quic_connection_id, this->_quic_connection_id, + packet = this->_packet_factory.create_handshake_packet(packet_buf, this->_peer_quic_connection_id, this->_quic_connection_id, this->_largest_acked_packet_number(QUICEncryptionLevel::HANDSHAKE), - std::move(buf), len, ack_eliciting, probing, crypto); + parent_block, len, ack_eliciting, probing, crypto); break; } case QUICPacketType::ZERO_RTT_PROTECTED: { - packet = this->_packet_factory.create_zero_rtt_packet(this->_original_quic_connection_id, this->_quic_connection_id, + packet = this->_packet_factory.create_zero_rtt_packet(packet_buf, this->_original_quic_connection_id, this->_quic_connection_id, this->_largest_acked_packet_number(QUICEncryptionLevel::ZERO_RTT), - std::move(buf), len, ack_eliciting, probing); + parent_block, len, ack_eliciting, probing); break; } case QUICPacketType::PROTECTED: { - packet = this->_packet_factory.create_protected_packet(this->_peer_quic_connection_id, - this->_largest_acked_packet_number(QUICEncryptionLevel::ONE_RTT), - std::move(buf), len, ack_eliciting, probing); + packet = this->_packet_factory.create_short_header_packet(packet_buf, this->_peer_quic_connection_id, + this->_largest_acked_packet_number(QUICEncryptionLevel::ONE_RTT), + parent_block, len, ack_eliciting, probing); break; } default: @@ -1750,29 +1831,31 @@ QUICNetVConnection::_handle_error(QUICConnectionErrorUPtr error) static_cast(error->cls), QUICDebugNames::error_code(error->code), error->code); // Connection Error - this->close(std::move(error)); + this->close_quic_connection(std::move(error)); } QUICPacketUPtr -QUICNetVConnection::_dequeue_recv_packet(QUICPacketCreationResult &result) +QUICNetVConnection::_dequeue_recv_packet(uint8_t *packet_buf, QUICPacketCreationResult &result) { - QUICPacketUPtr packet = this->_packet_recv_queue.dequeue(result); + QUICPacketUPtr packet = this->_packet_recv_queue.dequeue(packet_buf, result); if (result == QUICPacketCreationResult::SUCCESS) { if (this->direction() == NET_VCONNECTION_OUT) { // Reset CID if a server sent back a new CID - // FIXME This should happen only once - QUICConnectionId src_cid = packet->source_cid(); - // FIXME src connection id could be zero ? if so, check packet header type. - if (src_cid != QUICConnectionId::ZERO()) { - if (this->_peer_quic_connection_id != src_cid) { - this->_update_peer_cid(src_cid); + // FIXME This should happen only once - it should probably be controlled by PathManager + if (packet->type() != QUICPacketType::PROTECTED && (packet->type() != QUICPacketType::RETRY || !this->_av_token)) { + QUICConnectionId src_cid = static_cast(*packet).source_cid(); + // FIXME src connection id could be zero ? if so, check packet header type. + if (src_cid != QUICConnectionId::ZERO()) { + if (this->_peer_quic_connection_id != src_cid) { + this->_update_peer_cid(src_cid); + } } } } - if (!this->_verfied_state.is_verified()) { - this->_verfied_state.fill(packet->size()); + if (!this->_verified_state.is_verified()) { + this->_verified_state.fill(packet->size()); } } @@ -1790,11 +1873,15 @@ QUICNetVConnection::_dequeue_recv_packet(QUICPacketCreationResult &result) QUICConDebug("Unsupported version"); break; case QUICPacketCreationResult::SUCCESS: - if (packet->type() == QUICPacketType::VERSION_NEGOTIATION) { + switch (packet->type()) { + case QUICPacketType::VERSION_NEGOTIATION: + case QUICPacketType::RETRY: QUICConDebug("[RX] %s packet size=%u", QUICDebugNames::packet_type(packet->type()), packet->size()); - } else { + break; + default: QUICConDebug("[RX] %s packet #%" PRIu64 " size=%u header_len=%u payload_len=%u", QUICDebugNames::packet_type(packet->type()), packet->packet_number(), packet->size(), packet->header_size(), packet->payload_length()); + break; } break; default: @@ -1926,6 +2013,14 @@ QUICNetVConnection::_complete_handshake_if_possible() this->_handshake_handler->remote_transport_parameters()->getAsUInt(QUICTransportParameterId::ACK_DELAY_EXPONENT); this->_loss_detector->update_ack_delay_exponent(ack_delay_exponent); + const uint8_t *reset_token; + uint16_t reset_token_len; + reset_token = this->_handshake_handler->remote_transport_parameters()->getAsBytes(QUICTransportParameterId::STATELESS_RESET_TOKEN, + reset_token_len); + if (reset_token) { + this->_rtable->insert({reset_token}, this); + } + this->_start_application(); return 0; @@ -1974,12 +2069,16 @@ QUICNetVConnection::_switch_to_established_state() SET_HANDLER((NetVConnHandler)&QUICNetVConnection::state_connection_established); std::shared_ptr remote_tp = this->_handshake_handler->remote_transport_parameters(); + std::shared_ptr local_tp = this->_handshake_handler->local_transport_parameters(); uint64_t active_cid_limit = remote_tp->getAsUInt(QUICTransportParameterId::ACTIVE_CONNECTION_ID_LIMIT); if (active_cid_limit) { this->_alt_con_manager->set_remote_active_cid_limit(active_cid_limit); } + this->set_inactivity_timeout(HRTIME_MSECONDS(std::min(remote_tp->getAsUInt(QUICTransportParameterId::MAX_IDLE_TIMEOUT), + local_tp->getAsUInt(QUICTransportParameterId::MAX_IDLE_TIMEOUT)))); + if (this->direction() == NET_VCONNECTION_OUT) { uint16_t len; const uint8_t *pref_addr_buf = remote_tp->getAsBytes(QUICTransportParameterId::PREFERRED_ADDRESS, len); @@ -2100,12 +2199,7 @@ void QUICNetVConnection::_update_peer_cid(const QUICConnectionId &new_cid) { if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { - char old_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - char new_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - this->_peer_quic_connection_id.hex(old_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - new_cid.hex(new_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - - QUICConDebug("dcid: %s -> %s", old_cid_str, new_cid_str); + QUICConDebug("update peer dcid: %s -> %s", this->_peer_quic_connection_id.hex().c_str(), new_cid.hex().c_str()); } this->_peer_old_quic_connection_id = this->_peer_quic_connection_id; @@ -2117,12 +2211,7 @@ void QUICNetVConnection::_update_local_cid(const QUICConnectionId &new_cid) { if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { - char old_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - char new_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - this->_quic_connection_id.hex(old_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - new_cid.hex(new_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - - QUICConDebug("scid: %s -> %s", old_cid_str, new_cid_str); + QUICConDebug("update local dcid: %s -> %s", this->_quic_connection_id.hex().c_str(), new_cid.hex().c_str()); } this->_quic_connection_id = new_cid; @@ -2136,12 +2225,7 @@ QUICNetVConnection::_rerandomize_original_cid() this->_original_quic_connection_id.randomize(); if (is_debug_tag_set(QUIC_DEBUG_TAG.data())) { - char old_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - char new_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - tmp.hex(old_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - this->_original_quic_connection_id.hex(new_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - - QUICConDebug("original cid: %s -> %s", old_cid_str, new_cid_str); + QUICConDebug("original cid: %s -> %s", tmp.hex().c_str(), this->_original_quic_connection_id.hex().c_str()); } } @@ -2153,12 +2237,13 @@ QUICNetVConnection::_setup_handshake_protocol(shared_SSL_CTX ctx) QUICTLS *tls = new QUICTLS(this->_pp_key_info, ctx.get(), this->direction(), this->options, this->_quic_config->client_session_file(), this->_quic_config->client_keylog_file()); SSL_set_ex_data(tls->ssl_handle(), QUIC::ssl_quic_qc_index, static_cast(this)); + TLSSessionResumptionSupport::bind(tls->ssl_handle(), this); return tls; } QUICConnectionErrorUPtr -QUICNetVConnection::_state_connection_established_migrate_connection(const QUICPacket &p) +QUICNetVConnection::_state_connection_established_migrate_connection(const QUICPacketR &p) { ink_assert(this->_handshake_handler->is_completed()); @@ -2208,9 +2293,7 @@ QUICNetVConnection::_state_connection_established_migrate_connection(const QUICP this->_validate_new_path(new_path); } } else { - char dcid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - dcid.hex(dcid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - QUICConDebug("Connection migration failed cid=%s", dcid_str); + QUICConDebug("Connection migration failed cid=%s", dcid.hex().c_str()); } } @@ -2265,3 +2348,9 @@ QUICNetVConnection::_handle_periodic_ack_event() this->_schedule_packet_write_ready(); } } + +const IpEndpoint & +QUICNetVConnection::_getLocalEndpoint() +{ + return local_addr; +} diff --git a/iocore/net/QUICPacketHandler.cc b/iocore/net/QUICPacketHandler.cc index 9afee71f4a0..a6e214d2b5f 100644 --- a/iocore/net/QUICPacketHandler.cc +++ b/iocore/net/QUICPacketHandler.cc @@ -22,6 +22,9 @@ #include "tscore/ink_config.h" #include "P_Net.h" +#include "P_QUICPacketHandler.h" +#include "P_QUICNetProcessor.h" +#include "P_QUICNet.h" #include "P_QUICClosedConCollector.h" #include "QUICGlobals.h" @@ -29,6 +32,10 @@ #include "QUICPacket.h" #include "QUICDebugNames.h" #include "QUICEvents.h" +#include "QUICResetTokenTable.h" + +#include "QUICMultiCertConfigLoader.h" +#include "QUICTLS.h" static constexpr char debug_tag[] = "quic_sec"; @@ -42,7 +49,7 @@ static constexpr char debug_tag[] = "quic_sec"; // // QUICPacketHandler // -QUICPacketHandler::QUICPacketHandler() +QUICPacketHandler::QUICPacketHandler(QUICResetTokenTable &rtable) : _rtable(rtable) { this->_closed_con_collector = new QUICClosedConCollector; this->_closed_con_collector->mutex = new_ProxyMutex(); @@ -118,11 +125,18 @@ QUICPacketHandler::_send_packet(UDPConnection *udp_con, IpEndpoint &addr, Ptr(udp_con)->ethread)->signalActivity(); } +QUICConnection * +QUICPacketHandler::_check_stateless_reset(const uint8_t *buf, size_t buf_len) +{ + return this->_rtable.lookup({buf + (buf_len - 16)}); +} + // // QUICPacketHandlerIn // -QUICPacketHandlerIn::QUICPacketHandlerIn(const NetProcessor::AcceptOptions &opt, QUICConnectionTable &ctable) - : NetAccept(opt), QUICPacketHandler(), _ctable(ctable) +QUICPacketHandlerIn::QUICPacketHandlerIn(const NetProcessor::AcceptOptions &opt, QUICConnectionTable &ctable, + QUICResetTokenTable &rtable) + : NetAccept(opt), QUICPacketHandler(rtable), _ctable(ctable) { this->mutex = new_ProxyMutex(); // create Connection Table @@ -141,7 +155,7 @@ NetAccept * QUICPacketHandlerIn::clone() const { NetAccept *na; - na = new QUICPacketHandlerIn(opt, this->_ctable); + na = new QUICPacketHandlerIn(opt, this->_ctable, this->_rtable); *na = *this; return na; } @@ -254,7 +268,7 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) } QUICPacketType type = QUICPacketType::UNINITIALIZED; - QUICPacketLongHeader::type(type, buf, buf_len); + QUICLongHeaderPacketR::type(type, buf, buf_len); if (type == QUICPacketType::INITIAL) { // [draft-18] 7.2. // When an Initial packet is sent by a client which has not previously received a Retry packet from the server, it populates @@ -293,22 +307,33 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) // [draft-12] 6.1.2. Server Packet Handling // Servers MUST drop incoming packets under all other circumstances. They SHOULD send a Stateless Reset (Section 6.10.4) if a // connection ID is present in the header. - if ((!vc && !QUICInvariants::is_long_header(buf)) || (vc && vc->in_closed_queue)) { - if (is_debug_tag_set(debug_tag)) { - char dcid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - dcid.hex(dcid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); + if (!vc && !QUICInvariants::is_long_header(buf)) { + auto connection = static_cast(this->_check_stateless_reset(buf, buf_len)); + if (connection) { + QUICDebug("Stateless Reset has been received"); + connection->thread->schedule_imm(connection, QUIC_EVENT_STATELESS_RESET); + return; + } - if (!vc && !QUICInvariants::is_long_header(buf)) { - QUICDebugDS(scid, dcid, "sent Stateless Reset : connection not found, dcid=%s", dcid_str); - } else if (vc && vc->in_closed_queue) { - QUICDebugDS(scid, dcid, "sent Stateless Reset : connection is already closed, dcid=%s", dcid_str); - } + bool sent = + this->_send_stateless_reset(dcid, params->instance_id(), udp_packet->getConnection(), udp_packet->from, buf_len - 1); + udp_packet->free(); + + if (is_debug_tag_set(debug_tag) && sent) { + QUICDebugDS(scid, dcid, "sent Stateless Reset : connection not found, dcid=%s", dcid.hex().c_str()); } - QUICStatelessResetToken token(dcid, params->instance_id()); - auto packet = QUICPacketFactory::create_stateless_reset_packet(dcid, token); - this->_send_packet(*packet, udp_packet->getConnection(), udp_packet->from, 1200, nullptr, 0); + return; + + } else if (vc && vc->in_closed_queue) { + bool sent = + this->_send_stateless_reset(dcid, params->instance_id(), udp_packet->getConnection(), udp_packet->from, buf_len - 1); udp_packet->free(); + + if (is_debug_tag_set(debug_tag) && sent) { + QUICDebugDS(scid, dcid, "sent Stateless Reset : connection is already closed, dcid=%s", dcid.hex().c_str()); + } + return; } @@ -323,13 +348,11 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) QUICConnectionId peer_cid = scid; if (is_debug_tag_set("quic_sec")) { - char client_dcid_hex_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - original_cid.hex(client_dcid_hex_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - QUICDebugDS(peer_cid, original_cid, "client initial dcid=%s", client_dcid_hex_str); + QUICDebugDS(peer_cid, original_cid, "client initial dcid=%s", original_cid.hex().c_str()); } vc = static_cast(getNetProcessor()->allocate_vc(nullptr)); - vc->init(peer_cid, original_cid, cid_in_retry_token, udp_packet->getConnection(), this, &this->_ctable); + vc->init(peer_cid, original_cid, cid_in_retry_token, udp_packet->getConnection(), this, &this->_rtable, &this->_ctable); vc->id = net_next_connection_number(); vc->con.move(con); vc->submit_time = Thread::get_hrtime(); @@ -373,7 +396,7 @@ QUICPacketHandlerIn::_stateless_retry(const uint8_t *buf, uint64_t buf_len, UDPC QUICConnectionId dcid, QUICConnectionId scid, QUICConnectionId *original_cid) { QUICPacketType type = QUICPacketType::UNINITIALIZED; - QUICPacketLongHeader::type(type, buf, buf_len); + QUICPacketR::type(type, buf, buf_len); if (type != QUICPacketType::INITIAL) { return 1; @@ -383,7 +406,7 @@ QUICPacketHandlerIn::_stateless_retry(const uint8_t *buf, uint64_t buf_len, UDPC size_t token_length = 0; uint8_t token_length_field_len = 0; size_t token_length_field_offset = 0; - if (!QUICPacketLongHeader::token_length(token_length, token_length_field_len, token_length_field_offset, buf, buf_len)) { + if (!QUICInitialPacketR::token_length(token_length, token_length_field_len, token_length_field_offset, buf, buf_len)) { return -1; } @@ -391,10 +414,11 @@ QUICPacketHandlerIn::_stateless_retry(const uint8_t *buf, uint64_t buf_len, UDPC QUICRetryToken token(from, dcid); QUICConnectionId local_cid; local_cid.randomize(); - QUICPacketUPtr retry_packet = QUICPacketFactory::create_retry_packet(scid, local_cid, dcid, token); + QUICPacketUPtr retry_packet = QUICPacketFactory::create_retry_packet(scid, local_cid, token); - QUICDebug("[TX] %s packet ODCID=%" PRIx64, QUICDebugNames::packet_type(retry_packet->type()), - static_cast(static_cast(retry_packet->header()).original_dcid())); + QUICDebug("[TX] %s packet ODCID=%" PRIx64 " token_length=%u token=%02x%02x%02x%02x...", + QUICDebugNames::packet_type(retry_packet->type()), static_cast(token.original_dcid()), token.length(), + token.buf()[0], token.buf()[1], token.buf()[2], token.buf()[3]); this->_send_packet(*retry_packet, connection, from, 1200, nullptr, 0); return -2; @@ -405,8 +429,13 @@ QUICPacketHandlerIn::_stateless_retry(const uint8_t *buf, uint64_t buf_len, UDPC QUICRetryToken token(buf + token_offset, token_length); if (token.is_valid(from)) { *original_cid = token.original_dcid(); + QUICDebug("Retry Token is valid. ODCID=%" PRIx64, static_cast(*original_cid)); return 0; } else { + QUICDebug("Retry token is invalid: ODCID=%" PRIx64 "token_length=%u token=%02x%02x%02x%02x...", + static_cast(token.original_dcid()), token.length(), token.buf()[0], token.buf()[1], token.buf()[2], + token.buf()[3]); + this->_send_invalid_token_error(buf, buf_len, connection, from); return -3; } } else { @@ -418,10 +447,61 @@ QUICPacketHandlerIn::_stateless_retry(const uint8_t *buf, uint64_t buf_len, UDPC return 0; } +bool +QUICPacketHandlerIn::_send_stateless_reset(QUICConnectionId dcid, uint32_t instance_id, UDPConnection *udp_con, IpEndpoint &addr, + size_t maximum_size) +{ + QUICStatelessResetToken token(dcid, instance_id); + auto packet = QUICPacketFactory::create_stateless_reset_packet(token, maximum_size); + if (packet) { + this->_send_packet(*packet, udp_con, addr, 1200, nullptr, 0); + return true; + } + return false; +} + +void +QUICPacketHandlerIn::_send_invalid_token_error(const uint8_t *initial_packet, uint64_t initial_packet_len, + UDPConnection *connection, IpEndpoint from) +{ + QUICConnectionId scid_in_initial; + QUICConnectionId dcid_in_initial; + QUICInvariants::scid(scid_in_initial, initial_packet, initial_packet_len); + QUICInvariants::dcid(dcid_in_initial, initial_packet, initial_packet_len); + + // Create CONNECTION_CLOSE frame + auto error = std::make_unique(QUICTransErrorCode::INVALID_TOKEN); + uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE]; + QUICFrame *frame = QUICFrameFactory::create_connection_close_frame(frame_buf, *error); + Ptr block = frame->to_io_buffer_block(1200); + size_t block_len = 0; + for (Ptr tmp = block; tmp; tmp = tmp->next) { + block_len += tmp->size(); + } + frame->~QUICFrame(); + + // Prepare for packet protection + QUICPacketProtectionKeyInfo ppki; + ppki.set_context(QUICPacketProtectionKeyInfo::Context::SERVER); + QUICPacketFactory pf(ppki); + QUICPacketHeaderProtector php(ppki); + QUICCertConfig::scoped_config server_cert; + QUICTLS tls(ppki, server_cert->ssl_default.get(), NET_VCONNECTION_IN, {}, "", ""); + tls.initialize_key_materials(dcid_in_initial); + + // Create INITIAL packet + QUICConnectionId scid; + scid.randomize(); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr cc_packet = pf.create_initial_packet(packet_buf, scid_in_initial, scid, 0, block, block_len, 0, 0, 1); + + this->_send_packet(*cc_packet, connection, from, 0, &php, scid_in_initial); +} + // // QUICPacketHandlerOut // -QUICPacketHandlerOut::QUICPacketHandlerOut() : Continuation(new_ProxyMutex()), QUICPacketHandler() +QUICPacketHandlerOut::QUICPacketHandlerOut(QUICResetTokenTable &rtable) : Continuation(new_ProxyMutex()), QUICPacketHandler(rtable) { SET_HANDLER(&QUICPacketHandlerOut::event_handler); } @@ -466,10 +546,11 @@ QUICPacketHandlerOut::_get_continuation() void QUICPacketHandlerOut::_recv_packet(int event, UDPPacket *udp_packet) { - if (is_debug_tag_set(debug_tag)) { - IOBufferBlock *block = udp_packet->getIOBlockChain(); - const uint8_t *buf = reinterpret_cast(block->buf()); + IOBufferBlock *block = udp_packet->getIOBlockChain(); + const uint8_t *buf = reinterpret_cast(block->buf()); + uint64_t buf_len = block->size(); + if (is_debug_tag_set(debug_tag)) { ip_port_text_buffer ipb_from; ip_port_text_buffer ipb_to; QUICDebugQC(this->_vc, "recv %s packet from %s to %s size=%" PRId64, (QUICInvariants::is_long_header(buf) ? "LH" : "SH"), @@ -477,6 +558,24 @@ QUICPacketHandlerOut::_recv_packet(int event, UDPPacket *udp_packet) ats_ip_nptop(&udp_packet->to.sa, ipb_to, sizeof(ipb_to)), udp_packet->getPktLength()); } + QUICConnectionId dcid; + if (!QUICInvariants::dcid(dcid, buf, buf_len)) { + QUICDebug("Ignore packet - payload is too small"); + udp_packet->free(); + return; + } + + if (!QUICInvariants::is_long_header(buf) && dcid != this->_vc->connection_id()) { + auto connection = static_cast(this->_check_stateless_reset(buf, buf_len)); + if (connection) { + if (connection->connection_id() == this->_vc->connection_id()) { + QUICDebug("Stateless Reset has been received"); + this->_vc->thread->schedule_imm(this->_vc, QUIC_EVENT_STATELESS_RESET); + } + return; + } + } + this->_vc->handle_received_packet(udp_packet); eventProcessor.schedule_imm(this->_vc, ET_CALL, QUIC_EVENT_PACKET_READ_READY, nullptr); } diff --git a/iocore/net/quic/Makefile.am b/iocore/net/quic/Makefile.am index 8acdb53be75..75c4dafa168 100644 --- a/iocore/net/quic/Makefile.am +++ b/iocore/net/quic/Makefile.am @@ -30,7 +30,7 @@ AM_CPPFLAGS += \ -I$(abs_top_srcdir)/mgmt \ -I$(abs_top_srcdir)/mgmt/utils \ $(TS_INCLUDES) \ - @OPENSSL_INCLUDES@ + @OPENSSL_INCLUDES@ @YAMLCPP_INCLUDES@ noinst_LIBRARIES = libquic.a @@ -40,11 +40,20 @@ QUICPPProtector_impl = QUICPacketPayloadProtector_boringssl.cc QUICTLS_impl = QUICTLS_boringssl.cc QUICKeyGenerator_impl = QUICKeyGenerator_boringssl.cc else +if ENABLE_QUIC_OLD_API +QUICPHProtector_impl = QUICPacketHeaderProtector_legacy.cc +QUICPPProtector_impl = QUICPacketPayloadProtector_legacy.cc +QUICTLS_impl = QUICTLS_legacy.cc +QUICKeyGenerator_impl = QUICKeyGenerator_legacy.cc +else QUICPHProtector_impl = QUICPacketHeaderProtector_openssl.cc QUICPPProtector_impl = QUICPacketPayloadProtector_openssl.cc QUICTLS_impl = QUICTLS_openssl.cc QUICKeyGenerator_impl = QUICKeyGenerator_openssl.cc endif +endif + +QLog_impl = qlog/QLogEvent.cc qlog/QLogFrame.cc qlog/QLog.cc libquic_a_SOURCES = \ QUICGlobals.cc \ @@ -86,6 +95,8 @@ libquic_a_SOURCES = \ QUICPathManager.cc \ QUICPathValidator.cc \ QUICPinger.cc \ + QUICRetryIntegrityTag.cc \ + QUICResetTokenTable.cc \ QUICFrameGenerator.cc \ QUICFrameRetransmitter.cc \ QUICAddrVerifyState.cc \ @@ -95,7 +106,8 @@ libquic_a_SOURCES = \ QUICStreamFactory.cc \ QUICPadder.cc \ QUICContext.cc \ - QUICTokenCreator.cc + QUICTokenCreator.cc \ + $(QLog_impl) # # Check Programs diff --git a/iocore/net/quic/Mock.h b/iocore/net/quic/Mock.h index 0c78e30aa0c..68e421a9761 100644 --- a/iocore/net/quic/Mock.h +++ b/iocore/net/quic/Mock.h @@ -29,11 +29,15 @@ #include "QUICStreamManager.h" #include "QUICLossDetector.h" #include "QUICEvents.h" +#include "QUICPacketProtectionKeyInfo.h" +#include "QUICPinger.h" +#include "QUICPadder.h" +#include "QUICHandshakeProtocol.h" class MockQUICContext; using namespace std::literals; -std::string_view negotiated_application_name_sv = "h3-23"sv; +std::string_view negotiated_application_name_sv = "h3-27"sv; class MockQUICLDConfig : public QUICLDConfig { @@ -95,6 +99,35 @@ class MockQUICCCConfig : public QUICCCConfig } }; +class MockQUICPathManager : public QUICPathManager +{ +public: + virtual ~MockQUICPathManager() {} + virtual const QUICPath & + get_current_path() + { + return _path; + } + virtual const QUICPath & + get_verified_path() + { + return _path; + } + virtual void + open_new_path(const QUICPath &path, ink_hrtime timeout_in) + { + return; + } + virtual void + set_trusted_path(const QUICPath &path) + { + return; + } + +private: + QUICPath _path = {{}, {}}; +}; + class MockQUICConnectionInfoProvider : public QUICConnectionInfoProvider { QUICConnectionId @@ -169,7 +202,7 @@ class MockQUICConnectionInfoProvider : public QUICConnectionInfoProvider class MockQUICStreamManager : public QUICStreamManager { public: - MockQUICStreamManager(QUICConnectionInfoProvider *info) : QUICStreamManager(info, nullptr, nullptr) {} + MockQUICStreamManager(QUICContext *context) : QUICStreamManager(context, nullptr) {} // Override virtual QUICConnectionErrorUPtr @@ -349,7 +382,12 @@ class MockQUICConnection : public QUICConnection } void - close(QUICConnectionErrorUPtr error) override + close_quic_connection(QUICConnectionErrorUPtr error) override + { + } + + void + reset_quic_connection() override { } @@ -362,7 +400,7 @@ class MockQUICConnection : public QUICConnection QUICStreamManager * stream_manager() override { - return &_stream_manager; + return nullptr; } bool @@ -397,9 +435,8 @@ class MockQUICConnection : public QUICConnection int _transmit_count = 0; int _retransmit_count = 0; Ptr _mutex; - int _totalFrameCount = 0; - int _frameCount[256] = {0}; - MockQUICStreamManager _stream_manager = {this}; + int _totalFrameCount = 0; + int _frameCount[256] = {0}; QUICTransportParametersInEncryptedExtensions dummy_transport_parameters(); NetVConnectionContext_t _direction; @@ -443,6 +480,21 @@ class MockQUICCongestionController : public QUICCongestionController { return 0; } + virtual uint32_t + bytes_in_flight() const override + { + return 0; + } + virtual uint32_t + congestion_window() const override + { + return 0; + } + virtual uint32_t + current_ssthresh() const override + { + return 0; + } // for Test int @@ -498,15 +550,16 @@ class MockQUICPacketProtectionKeyInfo : public QUICPacketProtectionKeyInfo } }; -class MockQUICContext : public QUICContext, public QUICLDContext, public QUICCCContext +class MockQUICContext : public QUICContext { public: - MockQUICContext() + MockQUICContext() : QUICContext() { - _info = std::make_unique(); - _key_info = std::make_unique(); - _ld_config = std::make_unique(); - _cc_config = std::make_unique(); + _info = std::make_unique(); + _key_info = std::make_unique(); + _path_manager = std::make_unique(); + _ld_config = std::make_unique(); + _cc_config = std::make_unique(); } virtual QUICConnectionInfoProvider * @@ -543,6 +596,12 @@ class MockQUICContext : public QUICContext, public QUICLDContext, public QUICCCC return *_cc_config; } + virtual QUICPathManager * + path_manager() const override + { + return _path_manager.get(); + } + private: QUICConfig::scoped_config _config; QUICRTTMeasure _rtt_measure; @@ -550,6 +609,7 @@ class MockQUICContext : public QUICContext, public QUICLDContext, public QUICCCC std::unique_ptr _key_info; std::unique_ptr _ld_config; std::unique_ptr _cc_config; + std::unique_ptr _path_manager; }; class MockQUICLossDetector : public QUICLossDetector @@ -599,9 +659,29 @@ class MockQUICApplication : public QUICApplication } }; -class MockQUICPacket : public QUICPacket +class MockQUICPacketR : public QUICPacketR { public: + MockQUICPacketR() : QUICPacketR(nullptr, {}, {}) {} + + QUICPacketType + type() const override + { + return QUICPacketType::PROTECTED; + } + + QUICConnectionId + destination_cid() const override + { + return QUICConnectionId::ZERO(); + } + + QUICPacketNumber + packet_number() const override + { + return 0; + } + const IpEndpoint & from() const override { @@ -637,7 +717,7 @@ class MockQUICHandshakeProtocol : public QUICHandshakeProtocol MockQUICHandshakeProtocol(QUICPacketProtectionKeyInfo &pp_key_info) : QUICHandshakeProtocol(pp_key_info) {} int - handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) override + handshake(QUICHandshakeMsgs **out, const QUICHandshakeMsgs *in) override { return true; } diff --git a/iocore/net/quic/QUICAckFrameCreator.cc b/iocore/net/quic/QUICAckFrameCreator.cc index 98ca8983d7a..43167daad16 100644 --- a/iocore/net/quic/QUICAckFrameCreator.cc +++ b/iocore/net/quic/QUICAckFrameCreator.cc @@ -187,11 +187,8 @@ QUICAckFrameManager::QUICAckFrameCreator::push_back(QUICPacketNumber packet_numb this->_should_send = true; } - // every 2 full-packet should send a ack frame like tcp - this->_size_unsend += size; - // FIXME: this size should be fixed with PMTU - if (this->_size_unsend > 2 * 1480) { - this->_size_unsend = 0; + // every 2 ack-eliciting packet should send a ack frame + if (!ack_only && ++this->_ack_eliciting_count % 2 == 0) { this->_should_send = true; } @@ -224,7 +221,7 @@ QUICAckFrameManager::QUICAckFrameCreator::clear() this->_largest_ack_number = 0; this->_largest_ack_received_time = 0; this->_latest_packet_received_time = 0; - this->_size_unsend = 0; + this->_ack_eliciting_count = 0; this->_should_send = false; this->_available = false; } diff --git a/iocore/net/quic/QUICAckFrameCreator.h b/iocore/net/quic/QUICAckFrameCreator.h index 58e28e86868..42a7540bf71 100644 --- a/iocore/net/quic/QUICAckFrameCreator.h +++ b/iocore/net/quic/QUICAckFrameCreator.h @@ -69,7 +69,7 @@ class QUICAckFrameManager : public QUICFrameGenerator bool _available = false; // packet_number has data to sent bool _should_send = false; // ack frame should be sent immediately bool _has_new_data = false; // new data after last sent - size_t _size_unsend = 0; + uint32_t _ack_eliciting_count = 0; // every two ack-eliciting packet should send ack immediatly uint16_t _max_ack_delay = 25; QUICPacketNumber _largest_ack_number = 0; QUICPacketNumber _expect_next = 0; diff --git a/iocore/net/quic/QUICAltConnectionManager.cc b/iocore/net/quic/QUICAltConnectionManager.cc index 4e39e930244..569581caf5e 100644 --- a/iocore/net/quic/QUICAltConnectionManager.cc +++ b/iocore/net/quic/QUICAltConnectionManager.cc @@ -26,25 +26,28 @@ #include "tscore/ink_defs.h" #include "QUICAltConnectionManager.h" #include "QUICConnectionTable.h" +#include "QUICResetTokenTable.h" static constexpr char V_DEBUG_TAG[] = "v_quic_alt_con"; #define QUICACMVDebug(fmt, ...) Debug(V_DEBUG_TAG, "[%s] " fmt, this->_qc->cids().data(), ##__VA_ARGS__) -QUICAltConnectionManager::QUICAltConnectionManager(QUICConnection *qc, QUICConnectionTable &ctable, +QUICAltConnectionManager::QUICAltConnectionManager(QUICConnection *qc, QUICConnectionTable &ctable, QUICResetTokenTable &rtable, const QUICConnectionId &peer_initial_cid, uint32_t instance_id, uint8_t local_active_cid_limit, const IpEndpoint *preferred_endpoint_ipv4, const IpEndpoint *preferred_endpoint_ipv6) - : _qc(qc), _ctable(ctable), _instance_id(instance_id), _local_active_cid_limit(local_active_cid_limit) + : _qc(qc), _ctable(ctable), _rtable(rtable), _instance_id(instance_id), _local_active_cid_limit(local_active_cid_limit) { // Sequence number of the initial CID is 0 this->_alt_quic_connection_ids_remote.push_back({0, peer_initial_cid, {}, {true}}); + this->_alt_quic_connection_ids_local[0].seq_num = 0; + this->_alt_quic_connection_ids_local[0].advertised = true; if ((preferred_endpoint_ipv4 && !ats_ip_addr_port_eq(*preferred_endpoint_ipv4, qc->five_tuple().source())) || (preferred_endpoint_ipv6 && !ats_ip_addr_port_eq(*preferred_endpoint_ipv6, qc->five_tuple().source()))) { - this->_alt_quic_connection_ids_local[0] = this->_generate_next_alt_con_info(); + this->_alt_quic_connection_ids_local[1] = this->_generate_next_alt_con_info(); // This alt cid will be advertised via Transport Parameter, so no need to advertise it via NCID frame - this->_alt_quic_connection_ids_local[0].advertised = true; + this->_alt_quic_connection_ids_local[1].advertised = true; IpEndpoint empty_endpoint_ipv4; IpEndpoint empty_endpoint_ipv6; @@ -59,8 +62,8 @@ QUICAltConnectionManager::QUICAltConnectionManager(QUICConnection *qc, QUICConne // FIXME Check nullptr dereference this->_local_preferred_address = - new QUICPreferredAddress(*preferred_endpoint_ipv4, *preferred_endpoint_ipv6, this->_alt_quic_connection_ids_local[0].id, - this->_alt_quic_connection_ids_local[0].token); + new QUICPreferredAddress(*preferred_endpoint_ipv4, *preferred_endpoint_ipv6, this->_alt_quic_connection_ids_local[1].id, + this->_alt_quic_connection_ids_local[1].token); } } @@ -116,9 +119,7 @@ QUICAltConnectionManager::_generate_next_alt_con_info() } if (is_debug_tag_set(V_DEBUG_TAG)) { - char new_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - conn_id.hex(new_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - QUICACMVDebug("alt-cid=%s", new_cid_str); + QUICACMVDebug("alt-cid=%s", conn_id.hex().c_str()); } return aci; @@ -127,7 +128,7 @@ QUICAltConnectionManager::_generate_next_alt_con_info() void QUICAltConnectionManager::_init_alt_connection_ids() { - for (int i = 0; i < this->_remote_active_cid_limit; ++i) { + for (int i = this->_alt_quic_connection_id_seq_num + 1; i < this->_remote_active_cid_limit; ++i) { this->_alt_quic_connection_ids_local[i] = this->_generate_next_alt_con_info(); } this->_need_advertise = true; @@ -136,16 +137,6 @@ QUICAltConnectionManager::_init_alt_connection_ids() bool QUICAltConnectionManager::_update_alt_connection_id(uint64_t chosen_seq_num) { - // Seq 0 is special so it's not in the array - if (chosen_seq_num == 0) { - return true; - } - - // Seq 1 is for Preferred Address - if (chosen_seq_num == 1) { - return true; - } - for (int i = 0; i < this->_remote_active_cid_limit; ++i) { if (this->_alt_quic_connection_ids_local[i].seq_num == chosen_seq_num) { this->_alt_quic_connection_ids_local[i] = this->_generate_next_alt_con_info(); @@ -166,10 +157,17 @@ QUICAltConnectionManager::_register_remote_connection_id(const QUICNewConnection error = std::make_unique(QUICTransErrorCode::PROTOCOL_VIOLATION, "received zero-length cid", QUICFrameType::NEW_CONNECTION_ID); } else { - int unused = std::count_if(this->_alt_quic_connection_ids_remote.begin(), this->_alt_quic_connection_ids_remote.end(), - [](AltConnectionInfo info) { return info.used == false && info.seq_num != 1; }); + int unused = 0; + for (auto &&x : this->_alt_quic_connection_ids_remote) { + if (x.seq_num == frame.sequence()) { + return error; + } + if (x.used == false && x.seq_num != 1) { + ++unused; + } + } if (unused > this->_local_active_cid_limit) { - error = std::make_unique(QUICTransErrorCode::PROTOCOL_VIOLATION, "received too many alt CIDs", + error = std::make_unique(QUICTransErrorCode::CONNECTION_ID_LIMIT_ERROR, "received too many alt CIDs", QUICFrameType::NEW_CONNECTION_ID); } else { this->_alt_quic_connection_ids_remote.push_back( @@ -215,6 +213,7 @@ QUICAltConnectionManager::migrate_to_alt_cid() continue; } info.used = true; + this->_rtable.insert(info.token, this->_qc); return info.id; } @@ -250,6 +249,7 @@ QUICAltConnectionManager::drop_cid(const QUICConnectionId &cid) if (it->id == cid) { QUICACMVDebug("Dropping advertized CID %" PRIx32 " seq# %" PRIu64, it->id.h32(), it->seq_num); this->_retired_seq_nums.push(it->seq_num); + this->_rtable.erase(it->token); this->_alt_quic_connection_ids_remote.erase(it); return; } diff --git a/iocore/net/quic/QUICAltConnectionManager.h b/iocore/net/quic/QUICAltConnectionManager.h index 09b99ca3d7c..f92e90e036a 100644 --- a/iocore/net/quic/QUICAltConnectionManager.h +++ b/iocore/net/quic/QUICAltConnectionManager.h @@ -31,12 +31,14 @@ #include "QUICConnection.h" class QUICConnectionTable; +class QUICResetTokenTable; class QUICAltConnectionManager : public QUICFrameHandler, public QUICFrameGenerator { public: - QUICAltConnectionManager(QUICConnection *qc, QUICConnectionTable &ctable, const QUICConnectionId &peer_initial_cid, - uint32_t instance_id, uint8_t active_cid_limit, const IpEndpoint *preferred_endpoint_ipv4 = nullptr, + QUICAltConnectionManager(QUICConnection *qc, QUICConnectionTable &ctable, QUICResetTokenTable &rtable, + const QUICConnectionId &peer_initial_cid, uint32_t instance_id, uint8_t active_cid_limit, + const IpEndpoint *preferred_endpoint_ipv4 = nullptr, const IpEndpoint *preferred_endpoint_ipv6 = nullptr); ~QUICAltConnectionManager(); @@ -95,6 +97,7 @@ class QUICAltConnectionManager : public QUICFrameHandler, public QUICFrameGenera QUICConnection *_qc = nullptr; QUICConnectionTable &_ctable; + QUICResetTokenTable &_rtable; AltConnectionInfo _alt_quic_connection_ids_local[8]; // 8 is perhaps enough std::vector _alt_quic_connection_ids_remote; std::queue _retired_seq_nums; diff --git a/iocore/net/quic/QUICConfig.cc b/iocore/net/quic/QUICConfig.cc index 1cfe703fc3f..cc44d007e79 100644 --- a/iocore/net/quic/QUICConfig.cc +++ b/iocore/net/quic/QUICConfig.cc @@ -49,10 +49,6 @@ quic_new_ssl_ctx() SSL_CTX_set_max_early_data(ssl_ctx, UINT32_C(0xFFFFFFFF)); - SSL_CTX_add_custom_ext(ssl_ctx, QUICTransportParametersHandler::TRANSPORT_PARAMETER_ID, - SSL_EXT_TLS_ONLY | SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS, - &QUICTransportParametersHandler::add, &QUICTransportParametersHandler::free, nullptr, - &QUICTransportParametersHandler::parse, nullptr); #else // QUIC Transport Parameters are accesible with SSL_set_quic_transport_params and SSL_get_peer_quic_transport_params #endif @@ -61,6 +57,11 @@ quic_new_ssl_ctx() // tatsuhiro-t's custom OpenSSL for QUIC draft-13 // https://github.com/tatsuhiro-t/openssl/tree/quic-draft-13 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_QUIC_HACK); + SSL_CTX_add_custom_ext(ssl_ctx, QUICTransportParametersHandler::TRANSPORT_PARAMETER_ID, + SSL_EXT_TLS_ONLY | SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS, + &QUICTransportParametersHandler::add, &QUICTransportParametersHandler::free, nullptr, + &QUICTransportParametersHandler::parse, nullptr); + #endif return ssl_ctx; @@ -120,11 +121,16 @@ QUICConfigParams::initialize() REC_EstablishStaticConfigInt32U(this->_stateless_retry, "proxy.config.quic.server.stateless_retry_enabled"); REC_EstablishStaticConfigInt32U(this->_vn_exercise_enabled, "proxy.config.quic.client.vn_exercise_enabled"); REC_EstablishStaticConfigInt32U(this->_cm_exercise_enabled, "proxy.config.quic.client.cm_exercise_enabled"); + REC_EstablishStaticConfigInt32U(this->_quantum_readiness_test_enabled_out, + "proxy.config.quic.client.quantum_readiness_test_enabled"); + REC_EstablishStaticConfigInt32U(this->_quantum_readiness_test_enabled_in, + "proxy.config.quic.server.quantum_readiness_test_enabled"); REC_ReadConfigStringAlloc(this->_server_supported_groups, "proxy.config.quic.server.supported_groups"); REC_ReadConfigStringAlloc(this->_client_supported_groups, "proxy.config.quic.client.supported_groups"); REC_ReadConfigStringAlloc(this->_client_session_file, "proxy.config.quic.client.session_file"); REC_ReadConfigStringAlloc(this->_client_keylog_file, "proxy.config.quic.client.keylog_file"); + REC_ReadConfigStringAlloc(this->_qlog_dir, "proxy.config.quic.qlog_dir"); // Transport Parameters REC_EstablishStaticConfigInt32U(this->_no_activity_timeout_in, "proxy.config.quic.no_activity_timeout_in"); @@ -159,6 +165,7 @@ QUICConfigParams::initialize() REC_EstablishStaticConfigInt32U(this->_max_ack_delay_out, "proxy.config.quic.max_ack_delay_out"); REC_EstablishStaticConfigInt32U(this->_active_cid_limit_in, "proxy.config.quic.active_cid_limit_in"); REC_EstablishStaticConfigInt32U(this->_active_cid_limit_out, "proxy.config.quic.active_cid_limit_out"); + REC_EstablishStaticConfigInt32U(this->_disable_active_migration, "proxy.config.quic.disable_active_migration"); // Loss Detection REC_EstablishStaticConfigInt32U(this->_ld_packet_threshold, "proxy.config.quic.loss_detection.packet_threshold"); @@ -244,6 +251,18 @@ QUICConfigParams::cm_exercise_enabled() const return this->_cm_exercise_enabled; } +uint32_t +QUICConfigParams::quantum_readiness_test_enabled_in() const +{ + return this->_quantum_readiness_test_enabled_in; +} + +uint32_t +QUICConfigParams::quantum_readiness_test_enabled_out() const +{ + return this->_quantum_readiness_test_enabled_out; +} + uint32_t QUICConfigParams::initial_max_data_in() const { @@ -352,6 +371,12 @@ QUICConfigParams::active_cid_limit_out() const return this->_active_cid_limit_out; } +bool +QUICConfigParams::disable_active_migration() const +{ + return this->_disable_active_migration; +} + const char * QUICConfigParams::server_supported_groups() const { @@ -447,6 +472,12 @@ QUICConfigParams::client_keylog_file() const return this->_client_keylog_file; } +const char * +QUICConfigParams::qlog_dir() const +{ + return this->_qlog_dir; +} + // // QUICConfig // diff --git a/iocore/net/quic/QUICConfig.h b/iocore/net/quic/QUICConfig.h index 67106e65ffb..38a655a8c14 100644 --- a/iocore/net/quic/QUICConfig.h +++ b/iocore/net/quic/QUICConfig.h @@ -40,11 +40,14 @@ class QUICConfigParams : public ConfigInfo uint32_t stateless_retry() const; uint32_t vn_exercise_enabled() const; uint32_t cm_exercise_enabled() const; + uint32_t quantum_readiness_test_enabled_in() const; + uint32_t quantum_readiness_test_enabled_out() const; const char *server_supported_groups() const; const char *client_supported_groups() const; const char *client_session_file() const; const char *client_keylog_file() const; + const char *qlog_dir() const; shared_SSL_CTX client_ssl_ctx() const; @@ -71,6 +74,7 @@ class QUICConfigParams : public ConfigInfo uint8_t max_ack_delay_out() const; uint8_t active_cid_limit_in() const; uint8_t active_cid_limit_out() const; + bool disable_active_migration() const; // Loss Detection uint32_t ld_packet_threshold() const; @@ -93,15 +97,18 @@ class QUICConfigParams : public ConfigInfo // TODO: make configurable static const uint8_t _scid_len = 18; //< Length of Source Connection ID - uint32_t _instance_id = 0; - uint32_t _stateless_retry = 0; - uint32_t _vn_exercise_enabled = 0; - uint32_t _cm_exercise_enabled = 0; + uint32_t _instance_id = 0; + uint32_t _stateless_retry = 0; + uint32_t _vn_exercise_enabled = 0; + uint32_t _cm_exercise_enabled = 0; + uint32_t _quantum_readiness_test_enabled_in = 0; + uint32_t _quantum_readiness_test_enabled_out = 0; char *_server_supported_groups = nullptr; char *_client_supported_groups = nullptr; char *_client_session_file = nullptr; char *_client_keylog_file = nullptr; + char *_qlog_dir = nullptr; shared_SSL_CTX _client_ssl_ctx = nullptr; @@ -130,6 +137,7 @@ class QUICConfigParams : public ConfigInfo uint32_t _max_ack_delay_out = 0; uint32_t _active_cid_limit_in = 0; uint32_t _active_cid_limit_out = 0; + uint32_t _disable_active_migration = 0; // [draft-17 recovery] 6.4.1. Constants of interest uint32_t _ld_packet_threshold = 3; diff --git a/iocore/net/quic/QUICCongestionController.h b/iocore/net/quic/QUICCongestionController.h index 46341dc2ba2..5afa8f01685 100644 --- a/iocore/net/quic/QUICCongestionController.h +++ b/iocore/net/quic/QUICCongestionController.h @@ -23,6 +23,8 @@ #pragma once +#include "QUICFrame.h" + struct QUICPacketInfo { // 6.3.1. Sent Packet Fields QUICPacketNumber packet_number; @@ -42,6 +44,13 @@ struct QUICPacketInfo { class QUICCongestionController { public: + enum class State : uint8_t { + RECOVERY, + CONGESTION_AVOIDANCE, + SLOW_START, + APPPLICATION_LIMITED, + }; + virtual ~QUICCongestionController() {} virtual void on_packet_sent(size_t bytes_sent) = 0; virtual void on_packet_acked(const QUICPacketInfo &acked_packet) = 0; @@ -50,4 +59,8 @@ class QUICCongestionController virtual void add_extra_credit() = 0; virtual void reset() = 0; virtual uint32_t credit() const = 0; + // Debug + virtual uint32_t bytes_in_flight() const = 0; + virtual uint32_t congestion_window() const = 0; + virtual uint32_t current_ssthresh() const = 0; }; diff --git a/iocore/net/quic/QUICConnection.h b/iocore/net/quic/QUICConnection.h index 28c5a83ec8c..ed75dc6f628 100644 --- a/iocore/net/quic/QUICConnection.h +++ b/iocore/net/quic/QUICConnection.h @@ -53,8 +53,9 @@ class QUICConnectionInfoProvider class QUICConnection : public QUICFrameHandler, public QUICConnectionInfoProvider { public: - virtual QUICStreamManager *stream_manager() = 0; - virtual void close(QUICConnectionErrorUPtr error) = 0; - virtual void handle_received_packet(UDPPacket *packeet) = 0; - virtual void ping() = 0; + virtual QUICStreamManager *stream_manager() = 0; + virtual void close_quic_connection(QUICConnectionErrorUPtr error) = 0; + virtual void reset_quic_connection() = 0; + virtual void handle_received_packet(UDPPacket *packeet) = 0; + virtual void ping() = 0; }; diff --git a/iocore/net/quic/QUICContext.cc b/iocore/net/quic/QUICContext.cc index 7ccb9e4043f..4d0d5019317 100644 --- a/iocore/net/quic/QUICContext.cc +++ b/iocore/net/quic/QUICContext.cc @@ -100,48 +100,55 @@ class QUICLDConfigQCP : public QUICLDConfig const QUICConfigParams *_params; }; -QUICContextImpl::QUICContextImpl(QUICRTTProvider *rtt, QUICConnectionInfoProvider *info, - QUICPacketProtectionKeyInfoProvider *key_info) +QUICContext::QUICContext(QUICRTTProvider *rtt, QUICConnectionInfoProvider *info, QUICPacketProtectionKeyInfoProvider *key_info, + QUICPathManager *path_manager) : _key_info(key_info), _connection_info(info), _rtt_provider(rtt), + _path_manager(path_manager), _ld_config(std::make_unique(_config)), _cc_config(std::make_unique(_config)) { } QUICConnectionInfoProvider * -QUICContextImpl::connection_info() const +QUICContext::connection_info() const { return _connection_info; } QUICConfig::scoped_config -QUICContextImpl::config() const +QUICContext::config() const { return _config; } QUICPacketProtectionKeyInfoProvider * -QUICContextImpl::key_info() const +QUICContext::key_info() const { return _key_info; } QUICRTTProvider * -QUICContextImpl::rtt_provider() const +QUICContext::rtt_provider() const { return _rtt_provider; } QUICLDConfig & -QUICContextImpl::ld_config() const +QUICContext::ld_config() const { return *_ld_config; } QUICCCConfig & -QUICContextImpl::cc_config() const +QUICContext::cc_config() const { return *_cc_config; } + +QUICPathManager * +QUICContext::path_manager() const +{ + return _path_manager; +} \ No newline at end of file diff --git a/iocore/net/quic/QUICContext.h b/iocore/net/quic/QUICContext.h index 57e6c0b3a3b..31e494cc6d1 100644 --- a/iocore/net/quic/QUICContext.h +++ b/iocore/net/quic/QUICContext.h @@ -25,60 +25,170 @@ #include "QUICConnection.h" #include "QUICConfig.h" +#include "QUICEvents.h" +#include "QUICCongestionController.h" class QUICRTTProvider; class QUICCongestionController; class QUICPacketProtectionKeyInfoProvider; +class QUICPathManager; +class QUICPacketR; +class QUICPacket; class QUICNetVConnection; +struct QUICPacketInfo; -class QUICContext -{ -public: - virtual ~QUICContext(){}; - virtual QUICConnectionInfoProvider *connection_info() const = 0; - virtual QUICConfig::scoped_config config() const = 0; -}; - -class QUICLDContext +// this class is a connection between the callbacks. it should do something +// TODO: it should do something +class QUICCallbackContext { -public: - virtual ~QUICLDContext() {} - virtual QUICConnectionInfoProvider *connection_info() const = 0; - virtual QUICLDConfig &ld_config() const = 0; - virtual QUICPacketProtectionKeyInfoProvider *key_info() const = 0; }; -class QUICCCContext +class QUICCallback { public: - virtual ~QUICCCContext() {} - virtual QUICConnectionInfoProvider *connection_info() const = 0; - virtual QUICCCConfig &cc_config() const = 0; - virtual QUICRTTProvider *rtt_provider() const = 0; + virtual ~QUICCallback() {} + + // callback on connection close event + virtual void connection_close_callback(QUICCallbackContext &){}; + // callback on packet send event + virtual void packet_send_callback(QUICCallbackContext &, const QUICPacket &p){}; + // callback on packet lost event + virtual void packet_lost_callback(QUICCallbackContext &, const QUICPacketInfo &p){}; + // callback on packet receive event + virtual void packet_recv_callback(QUICCallbackContext &, const QUICPacket &p){}; + // callback on packet acked event + virtual void cc_metrics_update_callback(QUICCallbackContext &, uint64_t congestion_window, uint64_t bytes_in_flight, + uint64_t sshresh){}; + // callback on packet receive event + virtual void frame_packetize_callback(QUICCallbackContext &, const QUICFrame &p){}; + // callback on packet receive event + virtual void frame_recv_callback(QUICCallbackContext &, const QUICFrame &p){}; + // callback on packet receive event + virtual void congestion_state_updated_callback(QUICCallbackContext &, QUICCongestionController::State p){}; }; -class QUICContextImpl : public QUICContext, public QUICCCContext, public QUICLDContext +class QUICContext { public: - QUICContextImpl(QUICRTTProvider *rtt, QUICConnectionInfoProvider *info, QUICPacketProtectionKeyInfoProvider *key_info); - - virtual QUICConnectionInfoProvider *connection_info() const override; - virtual QUICConfig::scoped_config config() const override; - virtual QUICRTTProvider *rtt_provider() const override; + QUICContext(QUICRTTProvider *rtt, QUICConnectionInfoProvider *info, QUICPacketProtectionKeyInfoProvider *key_info, + QUICPathManager *path_manager); - // TODO should be more abstract - virtual QUICPacketProtectionKeyInfoProvider *key_info() const override; - - virtual QUICLDConfig &ld_config() const override; - virtual QUICCCConfig &cc_config() const override; + virtual ~QUICContext(){}; + virtual QUICConnectionInfoProvider *connection_info() const; + virtual QUICConfig::scoped_config config() const; + virtual QUICLDConfig &ld_config() const; + virtual QUICPacketProtectionKeyInfoProvider *key_info() const; + virtual QUICCCConfig &cc_config() const; + virtual QUICRTTProvider *rtt_provider() const; + virtual QUICPathManager *path_manager() const; + + // regist a callback which will be called when specifed event happen. + void + regist_callback(std::shared_ptr cbs) + { + this->_callbacks.push_back(cbs); + } + + enum class CallbackEvent : uint8_t { + PACKET_LOST, + PACKET_SEND, + FRAME_PACKETIZE, + PACKET_RECV, + FRAME_RECV, + METRICS_UPDATE, + CONNECTION_CLOSE, + CONGESTION_STATE_CHANGED, + }; + + // FIXME stupid trigger should be fix in more smart way. + void + trigger(CallbackEvent e, const QUICPacket *p = nullptr) + { + QUICCallbackContext ctx; + switch (e) { + case CallbackEvent::PACKET_RECV: + for (auto &&it : this->_callbacks) { + it->packet_recv_callback(ctx, *p); + } + break; + case CallbackEvent::PACKET_SEND: + for (auto &&it : this->_callbacks) { + it->packet_send_callback(ctx, *p); + } + break; + case CallbackEvent::CONNECTION_CLOSE: + for (auto &&it : this->_callbacks) { + it->connection_close_callback(ctx); + } + break; + default: + break; + } + } + + void + trigger(CallbackEvent e, const QUICPacketInfo &p) + { + QUICCallbackContext ctx; + for (auto &&it : this->_callbacks) { + it->packet_lost_callback(ctx, p); + } + } + + void + trigger(CallbackEvent e, uint64_t congestion_window, uint64_t bytes_in_flight, uint64_t sshresh) + { + QUICCallbackContext ctx; + for (auto &&it : this->_callbacks) { + it->cc_metrics_update_callback(ctx, congestion_window, bytes_in_flight, sshresh); + } + } + + void + trigger(CallbackEvent e, QUICCongestionController::State state) + { + QUICCallbackContext ctx; + for (auto &&it : this->_callbacks) { + it->congestion_state_updated_callback(ctx, state); + } + } + + void + trigger(CallbackEvent e, const QUICFrame &frame) + { + QUICCallbackContext ctx; + switch (e) { + case CallbackEvent::FRAME_PACKETIZE: + + for (auto &&it : this->_callbacks) { + it->frame_packetize_callback(ctx, frame); + } + break; + + case CallbackEvent::FRAME_RECV: + for (auto &&it : this->_callbacks) { + it->frame_recv_callback(ctx, frame); + } + break; + default: + break; + } + } + +protected: + // For Mock + QUICContext() {} private: QUICConfig::scoped_config _config; QUICPacketProtectionKeyInfoProvider *_key_info = nullptr; QUICConnectionInfoProvider *_connection_info = nullptr; QUICRTTProvider *_rtt_provider = nullptr; + QUICPathManager *_path_manager = nullptr; std::unique_ptr _ld_config = nullptr; std::unique_ptr _cc_config = nullptr; + + std::vector> _callbacks; }; diff --git a/iocore/net/quic/QUICDebugNames.cc b/iocore/net/quic/QUICDebugNames.cc index bf9d2d8f45a..6a75032cd18 100644 --- a/iocore/net/quic/QUICDebugNames.cc +++ b/iocore/net/quic/QUICDebugNames.cc @@ -90,6 +90,8 @@ QUICDebugNames::frame_type(QUICFrameType type) return "RETIRE_CONNECTION_ID"; case QUICFrameType::NEW_TOKEN: return "NEW_TOKEN"; + case QUICFrameType::HANDSHAKE_DONE: + return "HANDSHAKE_DONE"; case QUICFrameType::UNKNOWN: default: return "UNKNOWN"; @@ -131,8 +133,12 @@ QUICDebugNames::error_code(uint16_t code) return "FRAME_ENCODING_ERROR"; case static_cast(QUICTransErrorCode::TRANSPORT_PARAMETER_ERROR): return "TRANSPORT_PARAMETER_ERROR"; + case static_cast(QUICTransErrorCode::CONNECTION_ID_LIMIT_ERROR): + return "CONNECTION_ID_LIMIT_ERROR"; case static_cast(QUICTransErrorCode::PROTOCOL_VIOLATION): return "PROTOCOL_VIOLATION"; + case static_cast(QUICTransErrorCode::INVALID_TOKEN): + return "INVALID_TOKEN"; case static_cast(QUICTransErrorCode::CRYPTO_BUFFER_EXCEEDED): return "CRYPTO_BUFFER_EXCEEDED"; default: @@ -179,8 +185,8 @@ QUICDebugNames::transport_parameter_id(QUICTransportParameterId id) return "INITIAL_MAX_DATA"; case QUICTransportParameterId::INITIAL_MAX_STREAMS_BIDI: return "INITIAL_MAX_STREAMS_BIDI"; - case QUICTransportParameterId::IDLE_TIMEOUT: - return "IDLE_TIMEOUT"; + case QUICTransportParameterId::MAX_IDLE_TIMEOUT: + return "MAX_IDLE_TIMEOUT"; case QUICTransportParameterId::PREFERRED_ADDRESS: return "PREFERRED_ADDRESS"; case QUICTransportParameterId::MAX_PACKET_SIZE: diff --git a/iocore/net/quic/QUICEvents.h b/iocore/net/quic/QUICEvents.h index c758249ee93..50362e899a8 100644 --- a/iocore/net/quic/QUICEvents.h +++ b/iocore/net/quic/QUICEvents.h @@ -35,4 +35,5 @@ enum { QUIC_EVENT_ACK_PERIODIC, QUIC_EVENT_SHUTDOWN, QUIC_EVENT_LD_SHUTDOWN, + QUIC_EVENT_STATELESS_RESET, }; diff --git a/iocore/net/quic/QUICFlowController.cc b/iocore/net/quic/QUICFlowController.cc index b5275d0818b..60e665c6d1a 100644 --- a/iocore/net/quic/QUICFlowController.cc +++ b/iocore/net/quic/QUICFlowController.cc @@ -120,8 +120,17 @@ QUICFlowController::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint if (this->_should_create_frame) { frame = this->_create_frame(buf); - if (frame && frame->size() <= maximum_frame_size) { - this->_should_create_frame = false; + if (frame) { + if (frame->size() <= maximum_frame_size) { + this->_should_create_frame = false; + QUICFrameInformationUPtr info = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc()); + info->type = frame->type(); + info->level = QUICEncryptionLevel::NONE; + *(reinterpret_cast(info->data)) = this->_limit; + this->_records_frame(frame->id(), std::move(info)); + } else { + frame = nullptr; + } } } @@ -223,48 +232,23 @@ QUICLocalFlowController::_need_to_forward_limit() QUICFrame * QUICRemoteConnectionFlowController::_create_frame(uint8_t *buf) { - auto frame = QUICFrameFactory::create_data_blocked_frame(buf, this->_offset, this->_issue_frame_id(), this); - QUICFrameInformationUPtr info = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc()); - info->type = frame->type(); - info->level = QUICEncryptionLevel::NONE; - *(reinterpret_cast(info->data)) = this->_offset; - this->_records_frame(frame->id(), std::move(info)); - return frame; + return QUICFrameFactory::create_data_blocked_frame(buf, this->_offset, this->_issue_frame_id(), this); } QUICFrame * QUICLocalConnectionFlowController::_create_frame(uint8_t *buf) { - auto frame = QUICFrameFactory::create_max_data_frame(buf, this->_limit, this->_issue_frame_id(), this); - QUICFrameInformationUPtr info = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc()); - info->type = frame->type(); - info->level = QUICEncryptionLevel::NONE; - *(reinterpret_cast(info->data)) = this->_limit; - this->_records_frame(frame->id(), std::move(info)); - return frame; + return QUICFrameFactory::create_max_data_frame(buf, this->_limit, this->_issue_frame_id(), this); } QUICFrame * QUICRemoteStreamFlowController::_create_frame(uint8_t *buf) { - auto frame = - QUICFrameFactory::create_stream_data_blocked_frame(buf, this->_stream_id, this->_offset, this->_issue_frame_id(), this); - QUICFrameInformationUPtr info = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc()); - info->type = frame->type(); - info->level = QUICEncryptionLevel::NONE; - *(reinterpret_cast(info->data)) = this->_offset; - this->_records_frame(frame->id(), std::move(info)); - return frame; + return QUICFrameFactory::create_stream_data_blocked_frame(buf, this->_stream_id, this->_offset, this->_issue_frame_id(), this); } QUICFrame * QUICLocalStreamFlowController::_create_frame(uint8_t *buf) { - auto frame = QUICFrameFactory::create_max_stream_data_frame(buf, this->_stream_id, this->_limit, this->_issue_frame_id(), this); - QUICFrameInformationUPtr info = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc()); - info->type = frame->type(); - info->level = QUICEncryptionLevel::NONE; - *(reinterpret_cast(info->data)) = this->_limit; - this->_records_frame(frame->id(), std::move(info)); - return frame; + return QUICFrameFactory::create_max_stream_data_frame(buf, this->_stream_id, this->_limit, this->_issue_frame_id(), this); } diff --git a/iocore/net/quic/QUICFrame.cc b/iocore/net/quic/QUICFrame.cc index 68f03e300eb..1bffae34f36 100644 --- a/iocore/net/quic/QUICFrame.cc +++ b/iocore/net/quic/QUICFrame.cc @@ -46,7 +46,7 @@ read_varint(uint8_t *&pos, size_t len, uint64_t &field, size_t &field_len) return false; } - field = QUICIntUtil::read_QUICVariableInt(pos); + field = QUICIntUtil::read_QUICVariableInt(pos, len); pos += field_len; return true; } @@ -63,10 +63,10 @@ QUICFrame::ack_eliciting() const { auto type = this->type(); - return type != QUICFrameType::PADDING && type != QUICFrameType::ACK; + return type != QUICFrameType::PADDING && type != QUICFrameType::ACK && type != QUICFrameType::CONNECTION_CLOSE; } -const QUICPacket * +const QUICPacketR * QUICFrame::packet() const { return this->_packet; @@ -112,7 +112,7 @@ QUICFrame::type(const uint8_t *buf) buf[0] < static_cast(QUICFrameType::NEW_CONNECTION_ID)) { return QUICFrameType::STREAMS_BLOCKED; } else if (static_cast(QUICFrameType::CONNECTION_CLOSE) <= buf[0] && - buf[0] < static_cast(QUICFrameType::UNKNOWN)) { + buf[0] < static_cast(QUICFrameType::HANDSHAKE_DONE)) { return QUICFrameType::CONNECTION_CLOSE; } else { return static_cast(buf[0]); @@ -147,7 +147,7 @@ QUICStreamFrame::QUICStreamFrame(Ptr &block, QUICStreamId stream_ { } -QUICStreamFrame::QUICStreamFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICStreamFrame::QUICStreamFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } @@ -164,7 +164,7 @@ QUICStreamFrame::QUICStreamFrame(const QUICStreamFrame &o) } void -QUICStreamFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICStreamFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -392,7 +392,7 @@ QUICCryptoFrame::QUICCryptoFrame(Ptr &block, QUICOffset offset, Q { } -QUICCryptoFrame::QUICCryptoFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICCryptoFrame::QUICCryptoFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } @@ -403,7 +403,7 @@ QUICCryptoFrame::QUICCryptoFrame(const QUICCryptoFrame &o) } void -QUICCryptoFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICCryptoFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -541,13 +541,29 @@ QUICCryptoFrame::data() const // ACK frame // -QUICAckFrame::QUICAckFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +std::set +QUICAckFrame::ranges() const +{ + std::set numbers; + QUICPacketNumber x = this->largest_acknowledged(); + numbers.insert({x, static_cast(x) - this->ack_block_section()->first_ack_block()}); + x -= this->ack_block_section()->first_ack_block() + 1; + for (auto &&block : *(this->ack_block_section())) { + x -= block.gap() + 1; + numbers.insert({x, static_cast(x) - block.length()}); + x -= block.length() + 1; + } + + return numbers; +} + +QUICAckFrame::QUICAckFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICAckFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICAckFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -997,13 +1013,13 @@ QUICRstStreamFrame::QUICRstStreamFrame(QUICStreamId stream_id, QUICAppErrorCode { } -QUICRstStreamFrame::QUICRstStreamFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICRstStreamFrame::QUICRstStreamFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICRstStreamFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICRstStreamFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1128,13 +1144,13 @@ QUICRstStreamFrame::final_offset() const // PING frame // -QUICPingFrame::QUICPingFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICPingFrame::QUICPingFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICPingFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICPingFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { this->_reset(); this->_packet = packet; @@ -1179,13 +1195,13 @@ QUICPingFrame::to_io_buffer_block(size_t limit) const // // PADDING frame // -QUICPaddingFrame::QUICPaddingFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICPaddingFrame::QUICPaddingFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICPaddingFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICPaddingFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1265,7 +1281,7 @@ QUICConnectionCloseFrame::QUICConnectionCloseFrame(uint64_t error_code, uint64_t { } -QUICConnectionCloseFrame::QUICConnectionCloseFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICConnectionCloseFrame::QUICConnectionCloseFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); @@ -1286,7 +1302,7 @@ QUICConnectionCloseFrame::_reset() } void -QUICConnectionCloseFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICConnectionCloseFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1488,13 +1504,13 @@ QUICMaxDataFrame::_reset() this->_size = 0; } -QUICMaxDataFrame::QUICMaxDataFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICMaxDataFrame::QUICMaxDataFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICMaxDataFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICMaxDataFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1588,14 +1604,14 @@ QUICMaxStreamDataFrame::_reset() this->_size = 0; } -QUICMaxStreamDataFrame::QUICMaxStreamDataFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICMaxStreamDataFrame::QUICMaxStreamDataFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICMaxStreamDataFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICMaxStreamDataFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1701,13 +1717,13 @@ QUICMaxStreamsFrame::_reset() this->_size = 0; } -QUICMaxStreamsFrame::QUICMaxStreamsFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICMaxStreamsFrame::QUICMaxStreamsFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICMaxStreamsFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICMaxStreamsFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1775,7 +1791,8 @@ QUICMaxStreamsFrame::maximum_streams() const // // DATA_BLOCKED frame // -QUICDataBlockedFrame::QUICDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICDataBlockedFrame::QUICDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) + : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } @@ -1792,7 +1809,7 @@ QUICDataBlockedFrame::_reset() } void -QUICDataBlockedFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICDataBlockedFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1866,7 +1883,7 @@ QUICDataBlockedFrame::offset() const // // STREAM_DATA_BLOCKED frame // -QUICStreamDataBlockedFrame::QUICStreamDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICStreamDataBlockedFrame::QUICStreamDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); @@ -1885,7 +1902,7 @@ QUICStreamDataBlockedFrame::_reset() } void -QUICStreamDataBlockedFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICStreamDataBlockedFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -1974,7 +1991,7 @@ QUICStreamDataBlockedFrame::offset() const // // STREAMS_BLOCKED frame // -QUICStreamIdBlockedFrame::QUICStreamIdBlockedFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICStreamIdBlockedFrame::QUICStreamIdBlockedFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); @@ -1992,7 +2009,7 @@ QUICStreamIdBlockedFrame::_reset() } void -QUICStreamIdBlockedFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICStreamIdBlockedFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -2060,7 +2077,7 @@ QUICStreamIdBlockedFrame::stream_id() const // // NEW_CONNECTION_ID frame // -QUICNewConnectionIdFrame::QUICNewConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICNewConnectionIdFrame::QUICNewConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); @@ -2080,7 +2097,7 @@ QUICNewConnectionIdFrame::_reset() } void -QUICNewConnectionIdFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICNewConnectionIdFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -2190,11 +2207,10 @@ QUICNewConnectionIdFrame::to_io_buffer_block(size_t limit) const int QUICNewConnectionIdFrame::debug_msg(char *msg, size_t msg_len) const { - char cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; - this->connection_id().hex(cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH); - - return snprintf(msg, msg_len, "NEW_CONNECTION_ID size=%zu seq=%" PRIu64 " rpt=%" PRIu64 " cid=0x%s", this->size(), - this->sequence(), this->retire_prior_to(), cid_str); + return snprintf(msg, msg_len, "NEW_CONNECTION_ID size=%zu seq=%" PRIu64 " rpt=%" PRIu64 " cid=0x%s srt=%02x%02x%02x%02x", + this->size(), this->sequence(), this->retire_prior_to(), this->connection_id().hex().c_str(), + this->stateless_reset_token().buf()[0], this->stateless_reset_token().buf()[1], + this->stateless_reset_token().buf()[2], this->stateless_reset_token().buf()[3]); } uint64_t @@ -2243,13 +2259,14 @@ QUICStopSendingFrame::_reset() this->_size = 0; } -QUICStopSendingFrame::QUICStopSendingFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICStopSendingFrame::QUICStopSendingFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) + : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } void -QUICStopSendingFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICStopSendingFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -2336,7 +2353,7 @@ QUICStopSendingFrame::stream_id() const // // PATH_CHALLENGE frame // -QUICPathChallengeFrame::QUICPathChallengeFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICPathChallengeFrame::QUICPathChallengeFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); @@ -2353,7 +2370,7 @@ QUICPathChallengeFrame::_reset() } void -QUICPathChallengeFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICPathChallengeFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -2435,7 +2452,7 @@ QUICPathChallengeFrame::data() const // // PATH_RESPONSE frame // -QUICPathResponseFrame::QUICPathResponseFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICPathResponseFrame::QUICPathResponseFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); @@ -2478,7 +2495,7 @@ QUICPathResponseFrame::to_io_buffer_block(size_t limit) const } void -QUICPathResponseFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICPathResponseFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -2530,7 +2547,7 @@ QUICPathResponseFrame::data() const // // QUICNewTokenFrame // -QUICNewTokenFrame::QUICNewTokenFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) : QUICFrame(0, nullptr, packet) +QUICNewTokenFrame::QUICNewTokenFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); } @@ -2548,7 +2565,7 @@ QUICNewTokenFrame::_reset() } void -QUICNewTokenFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICNewTokenFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -2632,7 +2649,7 @@ QUICNewTokenFrame::token() const // // RETIRE_CONNECTION_ID frame // -QUICRetireConnectionIdFrame::QUICRetireConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICRetireConnectionIdFrame::QUICRetireConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) : QUICFrame(0, nullptr, packet) { this->parse(buf, len, packet); @@ -2651,7 +2668,7 @@ QUICRetireConnectionIdFrame::_reset() } void -QUICRetireConnectionIdFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICRetireConnectionIdFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { ink_assert(len >= 1); this->_reset(); @@ -2722,6 +2739,59 @@ QUICRetireConnectionIdFrame::seq_num() const return this->_seq_num; } +// +// HANDSHAKE_DONE frame +// + +QUICHandshakeDoneFrame::QUICHandshakeDoneFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet) + : QUICFrame(0, nullptr, packet) +{ + this->parse(buf, len, packet); +} + +void +QUICHandshakeDoneFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) +{ + this->_reset(); + this->_packet = packet; + this->_valid = true; + this->_size = 1; +} + +QUICFrameType +QUICHandshakeDoneFrame::type() const +{ + return QUICFrameType::HANDSHAKE_DONE; +} + +size_t +QUICHandshakeDoneFrame::size() const +{ + return 1; +} + +Ptr +QUICHandshakeDoneFrame::to_io_buffer_block(size_t limit) const +{ + Ptr block; + size_t n = 0; + + if (limit < this->size()) { + return block; + } + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(this->size(), BUFFER_SIZE_INDEX_32K)); + uint8_t *block_start = reinterpret_cast(block->start()); + + // Type + block_start[0] = static_cast(QUICFrameType::HANDSHAKE_DONE); + n += 1; + + block->fill(n); + return block; +} + // // UNKNOWN // @@ -2746,7 +2816,7 @@ QUICUnknownFrame::to_io_buffer_block(size_t limit) const } void -QUICUnknownFrame::parse(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICUnknownFrame::parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) { this->_packet = packet; } @@ -2762,7 +2832,7 @@ QUICUnknownFrame::debug_msg(char *msg, size_t msg_len) const // QUICFrame * -QUICFrameFactory::create(uint8_t *buf, const uint8_t *src, size_t len, const QUICPacket *packet) +QUICFrameFactory::create(uint8_t *buf, const uint8_t *src, size_t len, const QUICPacketR *packet) { switch (QUICFrame::type(src)) { case QUICFrameType::STREAM: @@ -2822,6 +2892,9 @@ QUICFrameFactory::create(uint8_t *buf, const uint8_t *src, size_t len, const QUI case QUICFrameType::RETIRE_CONNECTION_ID: new (buf) QUICRetireConnectionIdFrame(src, len, packet); return reinterpret_cast(buf); + case QUICFrameType::HANDSHAKE_DONE: + new (buf) QUICHandshakeDoneFrame(src, len, packet); + return reinterpret_cast(buf); default: // Unknown frame Debug("quic_frame_factory", "Unknown frame type %x", src[0]); @@ -2830,7 +2903,7 @@ QUICFrameFactory::create(uint8_t *buf, const uint8_t *src, size_t len, const QUI } const QUICFrame & -QUICFrameFactory::fast_create(const uint8_t *buf, size_t len, const QUICPacket *packet) +QUICFrameFactory::fast_create(const uint8_t *buf, size_t len, const QUICPacketR *packet) { if (QUICFrame::type(buf) == QUICFrameType::UNKNOWN) { return this->_unknown_frame; @@ -3024,6 +3097,13 @@ QUICFrameFactory::create_retire_connection_id_frame(uint8_t *buf, uint64_t seq_n return reinterpret_cast(buf); } +QUICHandshakeDoneFrame * +QUICFrameFactory::create_handshake_done_frame(uint8_t *buf, QUICFrameId id, QUICFrameGenerator *owner) +{ + new (buf) QUICHandshakeDoneFrame(id, owner); + return reinterpret_cast(buf); +} + QUICFrameId QUICFrameInfo::id() const { diff --git a/iocore/net/quic/QUICFrame.h b/iocore/net/quic/QUICFrame.h index 8d78510ee10..e933e68f53f 100644 --- a/iocore/net/quic/QUICFrame.h +++ b/iocore/net/quic/QUICFrame.h @@ -30,13 +30,14 @@ #include "I_IOBuffer.h" #include #include +#include #include "QUICTypes.h" class QUICFrame; class QUICStreamFrame; class QUICCryptoFrame; -class QUICPacket; +class QUICPacketR; class QUICFrameGenerator; using QUICFrameId = uint64_t; @@ -57,16 +58,16 @@ class QUICFrame virtual bool is_flow_controlled() const; virtual Ptr to_io_buffer_block(size_t limit) const = 0; virtual int debug_msg(char *msg, size_t msg_len) const; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet){}; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet){}; virtual QUICFrameGenerator *generated_by(); bool valid() const; bool ack_eliciting() const; - const QUICPacket *packet() const; + const QUICPacketR *packet() const; LINK(QUICFrame, link); protected: virtual void _reset(){}; - QUICFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr, const QUICPacket *packet = nullptr) + QUICFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr, const QUICPacketR *packet = nullptr) : _id(id), _owner(owner), _packet(packet) { } @@ -74,7 +75,7 @@ class QUICFrame bool _valid = false; QUICFrameId _id = 0; QUICFrameGenerator *_owner = nullptr; - const QUICPacket *_packet = nullptr; + const QUICPacketR *_packet = nullptr; }; // @@ -85,7 +86,7 @@ class QUICStreamFrame : public QUICFrame { public: QUICStreamFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICStreamFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICStreamFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICStreamFrame(Ptr &block, QUICStreamId streamid, QUICOffset offset, bool last = false, bool has_offset_field = true, bool has_length_field = true, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); @@ -96,7 +97,7 @@ class QUICStreamFrame : public QUICFrame virtual bool is_flow_controlled() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; QUICStreamId stream_id() const; QUICOffset offset() const; @@ -131,7 +132,7 @@ class QUICCryptoFrame : public QUICFrame { public: QUICCryptoFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICCryptoFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICCryptoFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICCryptoFrame(Ptr &block, QUICOffset offset, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); QUICCryptoFrame(const QUICCryptoFrame &o); @@ -139,7 +140,7 @@ class QUICCryptoFrame : public QUICFrame virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; QUICOffset offset() const; uint64_t data_length() const; @@ -265,9 +266,10 @@ class QUICAckFrame : public QUICFrame }; QUICAckFrame(QUICFrameId id = 0) : QUICFrame(id) {} - QUICAckFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICAckFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICAckFrame(QUICPacketNumber largest_acknowledged, uint64_t ack_delay, uint64_t first_ack_block, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); + std::set ranges() const; // There's no reasont restrict copy, but we need to write the copy constructor. Otherwise it will crash on destruct. QUICAckFrame(const QUICAckFrame &) = delete; @@ -276,7 +278,7 @@ class QUICAckFrame : public QUICFrame virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual int debug_msg(char *msg, size_t msg_len) const override; QUICPacketNumber largest_acknowledged() const; @@ -304,7 +306,7 @@ class QUICRstStreamFrame : public QUICFrame { public: QUICRstStreamFrame(QUICFrameId id = 0) : QUICFrame(id) {} - QUICRstStreamFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICRstStreamFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICRstStreamFrame(QUICStreamId stream_id, QUICAppErrorCode error_code, QUICOffset final_offset, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); @@ -312,7 +314,7 @@ class QUICRstStreamFrame : public QUICFrame virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; QUICStreamId stream_id() const; QUICAppErrorCode error_code() const; @@ -334,11 +336,11 @@ class QUICPingFrame : public QUICFrame { public: QUICPingFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICPingFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICPingFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; private: }; @@ -351,12 +353,12 @@ class QUICPaddingFrame : public QUICFrame { public: QUICPaddingFrame(size_t size) : _size(size) {} - QUICPaddingFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICPaddingFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); virtual QUICFrameType type() const override; virtual size_t size() const override; virtual bool is_probing_frame() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; private: // padding frame is a resident of padding frames @@ -372,7 +374,7 @@ class QUICConnectionCloseFrame : public QUICFrame { public: QUICConnectionCloseFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICConnectionCloseFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICConnectionCloseFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); // Constructor for transport error codes QUICConnectionCloseFrame(uint64_t error_code, QUICFrameType frame_type, uint64_t reason_phrase_length, const char *reason_phrase, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); @@ -383,7 +385,7 @@ class QUICConnectionCloseFrame : public QUICFrame virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; uint16_t error_code() const; QUICFrameType frame_type() const; @@ -408,13 +410,13 @@ class QUICMaxDataFrame : public QUICFrame { public: QUICMaxDataFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICMaxDataFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICMaxDataFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICMaxDataFrame(uint64_t maximum_data, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; uint64_t maximum_data() const; @@ -432,12 +434,12 @@ class QUICMaxStreamDataFrame : public QUICFrame { public: QUICMaxStreamDataFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICMaxStreamDataFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICMaxStreamDataFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICMaxStreamDataFrame(QUICStreamId stream_id, uint64_t maximum_stream_data, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); virtual QUICFrameType type() const override; virtual size_t size() const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; @@ -459,12 +461,12 @@ class QUICMaxStreamsFrame : public QUICFrame { public: QUICMaxStreamsFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICMaxStreamsFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICMaxStreamsFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICMaxStreamsFrame(QUICStreamId maximum_streams, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; uint64_t maximum_streams() const; private: @@ -480,13 +482,13 @@ class QUICDataBlockedFrame : public QUICFrame { public: QUICDataBlockedFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICDataBlockedFrame(QUICOffset offset, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _offset(offset){}; virtual QUICFrameType type() const override; virtual size_t size() const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual int debug_msg(char *msg, size_t msg_len) const override; virtual Ptr to_io_buffer_block(size_t limit) const override; @@ -506,14 +508,14 @@ class QUICStreamDataBlockedFrame : public QUICFrame { public: QUICStreamDataBlockedFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICStreamDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICStreamDataBlockedFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICStreamDataBlockedFrame(QUICStreamId s, QUICOffset o, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _stream_id(s), _offset(o){}; virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual int debug_msg(char *msg, size_t msg_len) const override; QUICStreamId stream_id() const; @@ -533,7 +535,7 @@ class QUICStreamIdBlockedFrame : public QUICFrame { public: QUICStreamIdBlockedFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICStreamIdBlockedFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICStreamIdBlockedFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICStreamIdBlockedFrame(QUICStreamId s, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _stream_id(s) { @@ -541,7 +543,7 @@ class QUICStreamIdBlockedFrame : public QUICFrame virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; QUICStreamId stream_id() const; @@ -559,7 +561,7 @@ class QUICNewConnectionIdFrame : public QUICFrame { public: QUICNewConnectionIdFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICNewConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICNewConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICNewConnectionIdFrame(uint64_t seq, uint64_t ret, const QUICConnectionId &cid, QUICStatelessResetToken token, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _sequence(seq), _retire_prior_to(ret), _connection_id(cid), _stateless_reset_token(token){}; @@ -567,7 +569,7 @@ class QUICNewConnectionIdFrame : public QUICFrame virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual int debug_msg(char *msg, size_t msg_len) const override; uint64_t sequence() const; @@ -592,13 +594,13 @@ class QUICStopSendingFrame : public QUICFrame { public: QUICStopSendingFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICStopSendingFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICStopSendingFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICStopSendingFrame(QUICStreamId stream_id, QUICAppErrorCode error_code, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); virtual QUICFrameType type() const override; virtual size_t size() const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual Ptr to_io_buffer_block(size_t limit) const override; QUICStreamId stream_id() const; @@ -620,7 +622,7 @@ class QUICPathChallengeFrame : public QUICFrame public: static constexpr uint8_t DATA_LEN = 8; QUICPathChallengeFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICPathChallengeFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICPathChallengeFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICPathChallengeFrame(ats_unique_buf data, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _data(std::move(data)) { @@ -628,7 +630,7 @@ class QUICPathChallengeFrame : public QUICFrame virtual QUICFrameType type() const override; virtual size_t size() const override; virtual bool is_probing_frame() const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; @@ -649,7 +651,7 @@ class QUICPathResponseFrame : public QUICFrame public: static constexpr uint8_t DATA_LEN = 8; QUICPathResponseFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICPathResponseFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICPathResponseFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICPathResponseFrame(ats_unique_buf data, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _data(std::move(data)) { @@ -657,7 +659,7 @@ class QUICPathResponseFrame : public QUICFrame virtual QUICFrameType type() const override; virtual size_t size() const override; virtual bool is_probing_frame() const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual Ptr to_io_buffer_block(size_t limit) const override; virtual int debug_msg(char *msg, size_t msg_len) const override; @@ -677,7 +679,7 @@ class QUICNewTokenFrame : public QUICFrame { public: QUICNewTokenFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICNewTokenFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICNewTokenFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICNewTokenFrame(ats_unique_buf token, size_t token_length, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _token_length(token_length), _token(std::move(token)) { @@ -685,7 +687,7 @@ class QUICNewTokenFrame : public QUICFrame virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; uint64_t token_length() const; const uint8_t *token() const; @@ -705,7 +707,7 @@ class QUICRetireConnectionIdFrame : public QUICFrame { public: QUICRetireConnectionIdFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} - QUICRetireConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacket *packet = nullptr); + QUICRetireConnectionIdFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); QUICRetireConnectionIdFrame(uint64_t seq_num, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner), _seq_num(seq_num) { @@ -713,7 +715,7 @@ class QUICRetireConnectionIdFrame : public QUICFrame virtual QUICFrameType type() const override; virtual size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - virtual void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; virtual int debug_msg(char *msg, size_t msg_len) const override; uint64_t seq_num() const; @@ -724,16 +726,32 @@ class QUICRetireConnectionIdFrame : public QUICFrame uint64_t _seq_num = 0; }; +// +// HANDSHAKE_DONE +// + +class QUICHandshakeDoneFrame : public QUICFrame +{ +public: + QUICHandshakeDoneFrame(QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr) : QUICFrame(id, owner) {} + QUICHandshakeDoneFrame(const uint8_t *buf, size_t len, const QUICPacketR *packet = nullptr); + virtual QUICFrameType type() const override; + virtual size_t size() const override; + virtual Ptr to_io_buffer_block(size_t limit) const override; + virtual void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; +}; + // // UNKNOWN // class QUICUnknownFrame : public QUICFrame { +public: QUICFrameType type() const override; size_t size() const override; virtual Ptr to_io_buffer_block(size_t limit) const override; - void parse(const uint8_t *buf, size_t len, const QUICPacket *packet) override; + void parse(const uint8_t *buf, size_t len, const QUICPacketR *packet) override; int debug_msg(char *msg, size_t msg_len) const override; }; @@ -746,13 +764,13 @@ class QUICFrameFactory /* * This is used for creating a QUICFrame object based on received data. */ - static QUICFrame *create(uint8_t *buf, const uint8_t *src, size_t len, const QUICPacket *packet); + static QUICFrame *create(uint8_t *buf, const uint8_t *src, size_t len, const QUICPacketR *packet); /* * This works almost the same as create() but it reuses created objects for performance. * If you create a frame object which has the same frame type that you created before, the object will be reset by new data. */ - const QUICFrame &fast_create(const uint8_t *buf, size_t len, const QUICPacket *packet); + const QUICFrame &fast_create(const uint8_t *buf, size_t len, const QUICPacketR *packet); /* * Creates a STREAM frame. @@ -879,6 +897,11 @@ class QUICFrameFactory */ static QUICPaddingFrame *create_padding_frame(uint8_t *buf, size_t size, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); + /* + * Creates a HANDSHAKE_DONE frame + */ + static QUICHandshakeDoneFrame *create_handshake_done_frame(uint8_t *buf, QUICFrameId id = 0, QUICFrameGenerator *owner = nullptr); + private: // FIXME Actual number of frame types is several but some of the values are not sequential. QUICFrame *_reusable_frames[256] = {nullptr}; diff --git a/iocore/net/quic/QUICFrameDispatcher.cc b/iocore/net/quic/QUICFrameDispatcher.cc index b807ff516e0..95099332bc1 100644 --- a/iocore/net/quic/QUICFrameDispatcher.cc +++ b/iocore/net/quic/QUICFrameDispatcher.cc @@ -42,8 +42,9 @@ QUICFrameDispatcher::add_handler(QUICFrameHandler *handler) } QUICConnectionErrorUPtr -QUICFrameDispatcher::receive_frames(QUICEncryptionLevel level, const uint8_t *payload, uint16_t size, bool &ack_only, - bool &is_flow_controlled, bool *has_non_probing_frame, const QUICPacket *packet) +QUICFrameDispatcher::receive_frames(QUICContext &ctx, QUICEncryptionLevel level, const uint8_t *payload, uint16_t size, + bool &ack_only, bool &is_flow_controlled, bool *has_non_probing_frame, + const QUICPacketR *packet) { uint16_t cursor = 0; ack_only = true; @@ -63,6 +64,8 @@ QUICFrameDispatcher::receive_frames(QUICEncryptionLevel level, const uint8_t *pa QUICFrameType type = frame.type(); + ctx.trigger(QUICContext::CallbackEvent::FRAME_RECV, frame); + if (type == QUICFrameType::STREAM) { is_flow_controlled = true; } diff --git a/iocore/net/quic/QUICFrameDispatcher.h b/iocore/net/quic/QUICFrameDispatcher.h index e7ce785b49e..435103badc2 100644 --- a/iocore/net/quic/QUICFrameDispatcher.h +++ b/iocore/net/quic/QUICFrameDispatcher.h @@ -28,15 +28,16 @@ #include "QUICConnection.h" #include "QUICFrame.h" #include "QUICFrameHandler.h" +#include "QUICContext.h" class QUICFrameDispatcher { public: QUICFrameDispatcher(QUICConnectionInfoProvider *info); - QUICConnectionErrorUPtr receive_frames(QUICEncryptionLevel level, const uint8_t *payload, uint16_t size, + QUICConnectionErrorUPtr receive_frames(QUICContext &context, QUICEncryptionLevel level, const uint8_t *payload, uint16_t size, bool &should_send_ackbool, bool &is_flow_controlled, bool *has_non_probing_frame, - const QUICPacket *packet); + const QUICPacketR *packet); void add_handler(QUICFrameHandler *handler); diff --git a/iocore/net/quic/QUICHandshake.cc b/iocore/net/quic/QUICHandshake.cc index c88377f16dd..08c30619640 100644 --- a/iocore/net/quic/QUICHandshake.cc +++ b/iocore/net/quic/QUICHandshake.cc @@ -27,6 +27,7 @@ #include "QUICEvents.h" #include "QUICGlobals.h" +#include "QUICHandshakeProtocol.h" #include "QUICPacketFactory.h" #include "QUICVersionNegotiator.h" #include "QUICConfig.h" @@ -80,8 +81,6 @@ } static constexpr int UDP_MAXIMUM_PAYLOAD_SIZE = 65527; -// TODO: fix size -static constexpr int MAX_HANDSHAKE_MSG_LEN = 65527; QUICHandshake::QUICHandshake(QUICConnection *qc, QUICHandshakeProtocol *hsp) : QUICHandshake(qc, hsp, {}, false) {} @@ -119,7 +118,7 @@ QUICHandshake::start(const QUICTPConfig &tp_config, QUICPacketFactory *packet_fa } QUICConnectionErrorUPtr -QUICHandshake::start(const QUICTPConfig &tp_config, const QUICPacket &initial_packet, QUICPacketFactory *packet_factory, +QUICHandshake::start(const QUICTPConfig &tp_config, const QUICInitialPacketR &initial_packet, QUICPacketFactory *packet_factory, const QUICPreferredAddress *pref_addr) { // Negotiate version @@ -143,7 +142,7 @@ QUICHandshake::start(const QUICTPConfig &tp_config, const QUICPacket &initial_pa } QUICConnectionErrorUPtr -QUICHandshake::negotiate_version(const QUICPacket &vn, QUICPacketFactory *packet_factory) +QUICHandshake::negotiate_version(const QUICVersionNegotiationPacketR &vn, QUICPacketFactory *packet_factory) { // Client side only ink_assert(this->_qc->direction() == NET_VCONNECTION_OUT); @@ -185,6 +184,16 @@ QUICHandshake::is_completed() const return this->_hs_protocol->is_handshake_finished(); } +bool +QUICHandshake::is_confirmed() const +{ + if (this->_qc->direction() == NET_VCONNECTION_IN) { + return this->is_completed(); + } else { + return this->_is_handshake_done_received; + } +} + bool QUICHandshake::is_stateless_retry_enabled() const { @@ -298,6 +307,7 @@ QUICHandshake::interests() { return { QUICFrameType::CRYPTO, + QUICFrameType::HANDSHAKE_DONE, }; } @@ -308,8 +318,15 @@ QUICHandshake::handle_frame(QUICEncryptionLevel level, const QUICFrame &frame) switch (frame.type()) { case QUICFrameType::CRYPTO: error = this->_crypto_streams[static_cast(level)].recv(static_cast(frame)); - if (error != nullptr) { - return error; + if (error == nullptr) { + error = this->do_handshake(); + } + break; + case QUICFrameType::HANDSHAKE_DONE: + if (this->_qc->direction() == NET_VCONNECTION_IN) { + error = std::make_unique(QUICTransErrorCode::PROTOCOL_VIOLATION); + } else { + this->_is_handshake_done_received = true; } break; default: @@ -318,7 +335,7 @@ QUICHandshake::handle_frame(QUICEncryptionLevel level, const QUICFrame &frame) break; } - return this->do_handshake(); + return error; } bool @@ -328,7 +345,8 @@ QUICHandshake::will_generate_frame(QUICEncryptionLevel level, size_t current_pac return false; } - return this->_crypto_streams[static_cast(level)].will_generate_frame(level, current_packet_size, ack_eliciting, seq_num); + return (this->_qc->direction() == NET_VCONNECTION_IN && !this->_is_handshake_done_sent) || + this->_crypto_streams[static_cast(level)].will_generate_frame(level, current_packet_size, ack_eliciting, seq_num); } QUICFrame * @@ -338,8 +356,23 @@ QUICHandshake::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t QUICFrame *frame = nullptr; if (this->_is_level_matched(level)) { + // CRYPTO frame = this->_crypto_streams[static_cast(level)].generate_frame(buf, level, connection_credit, maximum_frame_size, current_packet_size, seq_num); + if (frame) { + return frame; + } + } + + if (level == QUICEncryptionLevel::ONE_RTT) { + // HANDSHAKE_DONE + if (!this->_is_handshake_done_sent && this->is_completed()) { + frame = QUICFrameFactory::create_handshake_done_frame(buf, this->_issue_frame_id(), this); + } + if (frame) { + this->_is_handshake_done_sent = true; + return frame; + } } return frame; @@ -351,7 +384,7 @@ QUICHandshake::_load_local_server_transport_parameters(const QUICTPConfig &tp_co QUICTransportParametersInEncryptedExtensions *tp = new QUICTransportParametersInEncryptedExtensions(); // MUSTs - tp->set(QUICTransportParameterId::IDLE_TIMEOUT, static_cast(tp_config.no_activity_timeout())); + tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, static_cast(tp_config.no_activity_timeout())); if (this->_stateless_retry) { tp->set(QUICTransportParameterId::ORIGINAL_CONNECTION_ID, this->_qc->first_connection_id(), this->_qc->first_connection_id().length()); @@ -376,6 +409,9 @@ QUICHandshake::_load_local_server_transport_parameters(const QUICTPConfig &tp_co if (tp_config.initial_max_stream_data_uni() != 0) { tp->set(QUICTransportParameterId::INITIAL_MAX_STREAM_DATA_UNI, tp_config.initial_max_stream_data_uni()); } + if (tp_config.disable_active_migration()) { + tp->set(QUICTransportParameterId::DISABLE_ACTIVE_MIGRATION, nullptr, 0); + } if (pref_addr != nullptr) { uint8_t pref_addr_buf[QUICPreferredAddress::MAX_LEN]; uint16_t len; @@ -392,6 +428,11 @@ QUICHandshake::_load_local_server_transport_parameters(const QUICTPConfig &tp_co tp->add_version(QUIC_SUPPORTED_VERSIONS[0]); + // Additional parameters + for (auto &¶m : tp_config.additional_tp()) { + tp->set(param.first, param.second.first, param.second.second); + } + this->_local_transport_parameters = std::shared_ptr(tp); this->_hs_protocol->set_local_transport_parameters(this->_local_transport_parameters); } @@ -402,7 +443,7 @@ QUICHandshake::_load_local_client_transport_parameters(const QUICTPConfig &tp_co QUICTransportParametersInClientHello *tp = new QUICTransportParametersInClientHello(); // MUSTs - tp->set(QUICTransportParameterId::IDLE_TIMEOUT, static_cast(tp_config.no_activity_timeout())); + tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, static_cast(tp_config.no_activity_timeout())); // MAYs if (tp_config.initial_max_data() != 0) { @@ -428,6 +469,11 @@ QUICHandshake::_load_local_client_transport_parameters(const QUICTPConfig &tp_co tp->set(QUICTransportParameterId::ACTIVE_CONNECTION_ID_LIMIT, tp_config.active_cid_limit()); } + // Additional parameters + for (auto &¶m : tp_config.additional_tp()) { + tp->set(param.first, param.second.first, param.second.second); + } + this->_local_transport_parameters = std::shared_ptr(tp); this->_hs_protocol->set_local_transport_parameters(std::unique_ptr(tp)); } @@ -452,18 +498,13 @@ QUICHandshake::do_handshake() // TODO: check size if (bytes_avail > 0) { stream->read(in.buf + in.offsets[index], bytes_avail); - in.offsets[index] = bytes_avail; - in.offsets[4] += bytes_avail; } + in.offsets[index + 1] = in.offsets[index] + bytes_avail; } } - QUICHandshakeMsgs out; - uint8_t out_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - out.buf = out_buf; - out.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - int result = this->_hs_protocol->handshake(&out, &in); + QUICHandshakeMsgs *out = nullptr; + int result = this->_hs_protocol->handshake(&out, &in); if (this->_remote_transport_parameters == nullptr) { if (!this->check_remote_transport_parameters()) { result = 0; @@ -471,18 +512,25 @@ QUICHandshake::do_handshake() } if (result == 1) { - for (auto level : QUIC_ENCRYPTION_LEVELS) { - int index = static_cast(level); - QUICCryptoStream *stream = &this->_crypto_streams[index]; - size_t len = out.offsets[index + 1] - out.offsets[index]; - // TODO: check size - if (len > 0) { - stream->write(out.buf + out.offsets[index], len); + if (out) { + for (auto level : QUIC_ENCRYPTION_LEVELS) { + int index = static_cast(level); + QUICCryptoStream *stream = &this->_crypto_streams[index]; + size_t len = out->offsets[index + 1] - out->offsets[index]; + // TODO: check size + if (len > 0) { + stream->write(out->buf + out->offsets[index], len); + } } } - } else if (out.error_code != 0) { + } else { this->_hs_protocol->abort_handshake(); - error = std::make_unique(QUICErrorClass::TRANSPORT, out.error_code); + if (this->_hs_protocol->has_crypto_error()) { + error = std::make_unique(QUICErrorClass::TRANSPORT, this->_hs_protocol->crypto_error()); + } else { + error = std::make_unique(QUICErrorClass::TRANSPORT, + static_cast(QUICTransErrorCode::PROTOCOL_VIOLATION)); + } } return error; @@ -495,7 +543,7 @@ QUICHandshake::_abort_handshake(QUICTransErrorCode code) this->_hs_protocol->abort_handshake(); - this->_qc->close(QUICConnectionErrorUPtr(new QUICConnectionError(code))); + this->_qc->close_quic_connection(QUICConnectionErrorUPtr(new QUICConnectionError(code))); } /* @@ -514,3 +562,10 @@ QUICHandshake::_is_level_matched(QUICEncryptionLevel level) { return true; } + +void +QUICHandshake::_on_frame_lost(QUICFrameInformationUPtr &info) +{ + ink_assert(info->type == QUICFrameType::HANDSHAKE_DONE); + this->_is_handshake_done_sent = false; +} diff --git a/iocore/net/quic/QUICHandshake.h b/iocore/net/quic/QUICHandshake.h index 49ef4379ff2..5af42d8501a 100644 --- a/iocore/net/quic/QUICHandshake.h +++ b/iocore/net/quic/QUICHandshake.h @@ -35,6 +35,7 @@ */ class QUICVersionNegotiator; class QUICPacketFactory; +class QUICHandshakeProtocol; class SSLNextProtocolSet; class QUICHandshake : public QUICFrameHandler, public QUICFrameGenerator @@ -57,12 +58,12 @@ class QUICHandshake : public QUICFrameHandler, public QUICFrameGenerator // for client side QUICConnectionErrorUPtr start(const QUICTPConfig &tp_config, QUICPacketFactory *packet_factory, bool vn_exercise_enabled); - QUICConnectionErrorUPtr negotiate_version(const QUICPacket &packet, QUICPacketFactory *packet_factory); + QUICConnectionErrorUPtr negotiate_version(const QUICVersionNegotiationPacketR &packet, QUICPacketFactory *packet_factory); void reset(); // for server side - QUICConnectionErrorUPtr start(const QUICTPConfig &tp_config, const QUICPacket &initial_packet, QUICPacketFactory *packet_factory, - const QUICPreferredAddress *pref_addr); + QUICConnectionErrorUPtr start(const QUICTPConfig &tp_config, const QUICInitialPacketR &initial_packet, + QUICPacketFactory *packet_factory, const QUICPreferredAddress *pref_addr); QUICConnectionErrorUPtr do_handshake(); @@ -77,6 +78,7 @@ class QUICHandshake : public QUICFrameHandler, public QUICFrameGenerator bool is_version_negotiated() const; bool is_completed() const; + bool is_confirmed() const; bool is_stateless_retry_enabled() const; bool has_remote_tp() const; @@ -102,4 +104,10 @@ class QUICHandshake : public QUICFrameHandler, public QUICFrameGenerator std::shared_ptr _remote_transport_parameters = nullptr; void _abort_handshake(QUICTransErrorCode code); + + bool _is_handshake_done_sent = false; + bool _is_handshake_done_received = false; + + // QUICFrameGenerator + void _on_frame_lost(QUICFrameInformationUPtr &info) override; }; diff --git a/iocore/net/quic/QUICHandshakeProtocol.h b/iocore/net/quic/QUICHandshakeProtocol.h index fd4c68b65c1..2d56e020f09 100644 --- a/iocore/net/quic/QUICHandshakeProtocol.h +++ b/iocore/net/quic/QUICHandshakeProtocol.h @@ -43,7 +43,7 @@ class QUICHandshakeProtocol QUICHandshakeProtocol(QUICPacketProtectionKeyInfo &pp_key_info) : _pp_key_info(pp_key_info) {} virtual ~QUICHandshakeProtocol(){}; - virtual int handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) = 0; + virtual int handshake(QUICHandshakeMsgs **out, const QUICHandshakeMsgs *in) = 0; virtual void reset() = 0; virtual bool is_handshake_finished() const = 0; virtual bool is_ready_to_derive() const = 0; @@ -58,6 +58,8 @@ class QUICHandshakeProtocol virtual QUICEncryptionLevel current_encryption_level() const = 0; virtual void abort_handshake() = 0; + virtual bool has_crypto_error() const = 0; + virtual uint64_t crypto_error() const = 0; protected: QUICPacketProtectionKeyInfo &_pp_key_info; diff --git a/iocore/net/quic/QUICIntUtil.cc b/iocore/net/quic/QUICIntUtil.cc index e9840ca2d61..26159d74770 100644 --- a/iocore/net/quic/QUICIntUtil.cc +++ b/iocore/net/quic/QUICIntUtil.cc @@ -102,11 +102,11 @@ QUICVariableInt::decode(uint64_t &dst, size_t &len, const uint8_t *src, size_t s } uint64_t -QUICIntUtil::read_QUICVariableInt(const uint8_t *buf) +QUICIntUtil::read_QUICVariableInt(const uint8_t *buf, size_t buf_len) { uint64_t dst = 0; size_t len = 0; - QUICVariableInt::decode(dst, len, buf, 8); + QUICVariableInt::decode(dst, len, buf, buf_len); return dst; } diff --git a/iocore/net/quic/QUICIntUtil.h b/iocore/net/quic/QUICIntUtil.h index c259bca682a..5116304643a 100644 --- a/iocore/net/quic/QUICIntUtil.h +++ b/iocore/net/quic/QUICIntUtil.h @@ -38,7 +38,7 @@ class QUICVariableInt class QUICIntUtil { public: - static uint64_t read_QUICVariableInt(const uint8_t *buf); + static uint64_t read_QUICVariableInt(const uint8_t *buf, size_t buf_len); static void write_QUICVariableInt(uint64_t data, uint8_t *buf, size_t *len); static uint64_t read_nbytes_as_uint(const uint8_t *buf, uint8_t n); static void write_uint_as_nbytes(uint64_t value, uint8_t n, uint8_t *buf, size_t *len); diff --git a/iocore/net/quic/QUICKeyGenerator.cc b/iocore/net/quic/QUICKeyGenerator.cc index 98d091d8725..24c2b84628b 100644 --- a/iocore/net/quic/QUICKeyGenerator.cc +++ b/iocore/net/quic/QUICKeyGenerator.cc @@ -45,8 +45,8 @@ constexpr static std::string_view LABEL_FOR_HP("quic hp"sv); void QUICKeyGenerator::generate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t *iv_len, QUICConnectionId cid) { - const QUIC_EVP_CIPHER *cipher = this->_get_cipher_for_initial(); - const EVP_MD *md = EVP_sha256(); + const EVP_CIPHER *cipher = this->_get_cipher_for_initial(); + const EVP_MD *md = EVP_sha256(); uint8_t secret[512]; size_t secret_len = sizeof(secret); QUICHKDF hkdf(md); @@ -79,14 +79,14 @@ QUICKeyGenerator::generate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t void QUICKeyGenerator::regenerate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t *iv_len, const uint8_t *secret, - size_t secret_len, const QUIC_EVP_CIPHER *cipher, QUICHKDF &hkdf) + size_t secret_len, const EVP_CIPHER *cipher, QUICHKDF &hkdf) { this->_generate(hp_key, pp_key, iv, iv_len, hkdf, secret, secret_len, cipher); } int QUICKeyGenerator::_generate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t *iv_len, QUICHKDF &hkdf, const uint8_t *secret, - size_t secret_len, const QUIC_EVP_CIPHER *cipher) + size_t secret_len, const EVP_CIPHER *cipher) { // Generate key, iv, and hp_key // key = HKDF-Expand-Label(S, "quic key", "", key_length) diff --git a/iocore/net/quic/QUICKeyGenerator.h b/iocore/net/quic/QUICKeyGenerator.h index a82ad43a2a3..c2ff89f78fa 100644 --- a/iocore/net/quic/QUICKeyGenerator.h +++ b/iocore/net/quic/QUICKeyGenerator.h @@ -46,7 +46,7 @@ class QUICKeyGenerator void generate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t *iv_len, QUICConnectionId cid); void regenerate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t *iv_len, const uint8_t *secret, size_t secret_len, - const QUIC_EVP_CIPHER *cipher, QUICHKDF &hkdf); + const EVP_CIPHER *cipher, QUICHKDF &hkdf); private: Context _ctx = Context::SERVER; @@ -55,15 +55,15 @@ class QUICKeyGenerator size_t _last_secret_len = 0; int _generate(uint8_t *hp_key, uint8_t *pp_key, uint8_t *iv, size_t *iv_len, QUICHKDF &hkdf, const uint8_t *secret, - size_t secret_len, const QUIC_EVP_CIPHER *cipher); + size_t secret_len, const EVP_CIPHER *cipher); int _generate_initial_secret(uint8_t *out, size_t *out_len, QUICHKDF &hkdf, QUICConnectionId cid, const char *label, size_t label_len, size_t length); int _generate_key(uint8_t *out, size_t *out_len, QUICHKDF &hkdf, const uint8_t *secret, size_t secret_len, size_t key_length) const; int _generate_iv(uint8_t *out, size_t *out_len, QUICHKDF &hkdf, const uint8_t *secret, size_t secret_len, size_t iv_length) const; int _generate_hp(uint8_t *out, size_t *out_len, QUICHKDF &hkdf, const uint8_t *secret, size_t secret_len, size_t hp_length) const; - size_t _get_key_len(const QUIC_EVP_CIPHER *cipher) const; - size_t _get_iv_len(const QUIC_EVP_CIPHER *cipher) const; - const QUIC_EVP_CIPHER *_get_cipher_for_initial() const; - const QUIC_EVP_CIPHER *_get_cipher_for_protected_packet(const SSL *ssl) const; + size_t _get_key_len(const EVP_CIPHER *cipher) const; + size_t _get_iv_len(const EVP_CIPHER *cipher) const; + const EVP_CIPHER *_get_cipher_for_initial() const; + const EVP_CIPHER *_get_cipher_for_protected_packet(const SSL *ssl) const; }; diff --git a/iocore/net/quic/QUICKeyGenerator_boringssl.cc b/iocore/net/quic/QUICKeyGenerator_boringssl.cc index e2204bbd3b0..4406bbd376c 100644 --- a/iocore/net/quic/QUICKeyGenerator_boringssl.cc +++ b/iocore/net/quic/QUICKeyGenerator_boringssl.cc @@ -25,33 +25,34 @@ #include size_t -QUICKeyGenerator::_get_key_len(const QUIC_EVP_CIPHER *cipher) const +QUICKeyGenerator::_get_key_len(const EVP_CIPHER *cipher) const { - return EVP_AEAD_key_length(cipher); + return EVP_CIPHER_key_length(cipher); } size_t -QUICKeyGenerator::_get_iv_len(const QUIC_EVP_CIPHER *cipher) const +QUICKeyGenerator::_get_iv_len(const EVP_CIPHER *cipher) const { - return EVP_AEAD_nonce_length(cipher); + return EVP_CIPHER_iv_length(cipher); } -const QUIC_EVP_CIPHER * +const EVP_CIPHER * QUICKeyGenerator::_get_cipher_for_initial() const { - return EVP_aead_aes_128_gcm(); + return EVP_aes_128_gcm(); } -const QUIC_EVP_CIPHER * +const EVP_CIPHER * QUICKeyGenerator::_get_cipher_for_protected_packet(const SSL *ssl) const { switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { case TLS1_CK_AES_128_GCM_SHA256: - return EVP_aead_aes_128_gcm(); + return EVP_aes_128_gcm(); case TLS1_CK_AES_256_GCM_SHA384: - return EVP_aead_aes_256_gcm(); + return EVP_aes_256_gcm(); case TLS1_CK_CHACHA20_POLY1305_SHA256: - return EVP_aead_chacha20_poly1305(); + return nullptr; + // return EVP_aead_chacha20_poly1305(); default: ink_assert(false); return nullptr; diff --git a/iocore/net/quic/QUICKeyGenerator_legacy.cc b/iocore/net/quic/QUICKeyGenerator_legacy.cc new file mode 100644 index 00000000000..03b46e1cf1e --- /dev/null +++ b/iocore/net/quic/QUICKeyGenerator_legacy.cc @@ -0,0 +1,63 @@ +/** @file + * + * A key generator for QUIC connection (OpenSSL specific parts) + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "tscore/ink_assert.h" +#include "QUICKeyGenerator.h" + +#include + +size_t +QUICKeyGenerator::_get_key_len(const EVP_CIPHER *cipher) const +{ + return EVP_CIPHER_key_length(cipher); +} + +size_t +QUICKeyGenerator::_get_iv_len(const EVP_CIPHER *cipher) const +{ + return EVP_CIPHER_iv_length(cipher); +} + +const EVP_CIPHER * +QUICKeyGenerator::_get_cipher_for_initial() const +{ + return EVP_aes_128_gcm(); +} + +const EVP_CIPHER * +QUICKeyGenerator::_get_cipher_for_protected_packet(const SSL *ssl) const +{ + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_3_CK_AES_128_GCM_SHA256: + return EVP_aes_128_gcm(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return EVP_aes_256_gcm(); + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return EVP_chacha20_poly1305(); + case TLS1_3_CK_AES_128_CCM_SHA256: + case TLS1_3_CK_AES_128_CCM_8_SHA256: + return EVP_aes_128_ccm(); + default: + ink_assert(false); + return nullptr; + } +} diff --git a/iocore/net/quic/QUICKeyGenerator_openssl.cc b/iocore/net/quic/QUICKeyGenerator_openssl.cc index 5d9d0294e0f..e7bb227faf0 100644 --- a/iocore/net/quic/QUICKeyGenerator_openssl.cc +++ b/iocore/net/quic/QUICKeyGenerator_openssl.cc @@ -24,26 +24,25 @@ #include "QUICKeyGenerator.h" #include - size_t -QUICKeyGenerator::_get_key_len(const QUIC_EVP_CIPHER *cipher) const +QUICKeyGenerator::_get_key_len(const EVP_CIPHER *cipher) const { return EVP_CIPHER_key_length(cipher); } size_t -QUICKeyGenerator::_get_iv_len(const QUIC_EVP_CIPHER *cipher) const +QUICKeyGenerator::_get_iv_len(const EVP_CIPHER *cipher) const { return EVP_CIPHER_iv_length(cipher); } -const QUIC_EVP_CIPHER * +const EVP_CIPHER * QUICKeyGenerator::_get_cipher_for_initial() const { return EVP_aes_128_gcm(); } -const QUIC_EVP_CIPHER * +const EVP_CIPHER * QUICKeyGenerator::_get_cipher_for_protected_packet(const SSL *ssl) const { switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { @@ -61,3 +60,21 @@ QUICKeyGenerator::_get_cipher_for_protected_packet(const SSL *ssl) const return nullptr; } } + +// SSL_HANDSHAKE_MAC_SHA256, SSL_HANDSHAKE_MAC_SHA384 are defind in `ssl/internal.h` of BoringSSL +/* +const EVP_MD * +QUICKeyGenerator::get_handshake_digest(const SSL *ssl) +{ + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_CK_AES_128_GCM_SHA256: + case TLS1_CK_CHACHA20_POLY1305_SHA256: + return EVP_sha256(); + case TLS1_CK_AES_256_GCM_SHA384: + return EVP_sha384(); + default: + ink_assert(false); + return nullptr; + } +} +*/ diff --git a/iocore/net/quic/QUICLossDetector.cc b/iocore/net/quic/QUICLossDetector.cc index 4d6397bbfab..6a0138d30a4 100644 --- a/iocore/net/quic/QUICLossDetector.cc +++ b/iocore/net/quic/QUICLossDetector.cc @@ -38,7 +38,7 @@ #define QUICLDVDebug(fmt, ...) \ Debug("v_quic_loss_detector", "[%s] " fmt, this->_context.connection_info()->cids().data(), ##__VA_ARGS__) -QUICLossDetector::QUICLossDetector(QUICLDContext &context, QUICCongestionController *cc, QUICRTTMeasure *rtt_measure, +QUICLossDetector::QUICLossDetector(QUICContext &context, QUICCongestionController *cc, QUICRTTMeasure *rtt_measure, QUICPinger *pinger, QUICPadder *padder) : _rtt_measure(rtt_measure), _pinger(pinger), _padder(padder), _cc(cc), _context(context) { @@ -444,6 +444,7 @@ QUICLossDetector::_detect_lost_packets(QUICPacketNumberSpace pn_space) if (!lost_packets.empty()) { this->_cc->on_packets_lost(lost_packets); for (auto lost_packet : lost_packets) { + this->_context.trigger(QUICContext::CallbackEvent::PACKET_LOST, *lost_packet.second); // -- ADDITIONAL CODE -- // Not sure how we can get feedback from congestion control and when we should retransmit the lost packets but we need to send // them somewhere. @@ -487,7 +488,7 @@ QUICLossDetector::_send_packet(QUICEncryptionLevel level, bool padded) if (padded) { this->_padder->request(level); } else { - this->_pinger->request(); + this->_pinger->request(level); } this->_cc->add_extra_credit(); } @@ -497,23 +498,25 @@ QUICLossDetector::_send_one_or_two_packet() { this->_send_packet(QUICEncryptionLevel::ONE_RTT); this->_send_packet(QUICEncryptionLevel::ONE_RTT); - ink_assert(this->_pinger->count() >= 2); + ink_assert(this->_pinger->count(QUICEncryptionLevel::ONE_RTT) >= 2); QUICLDDebug("[%s] send ping frame %" PRIu64, QUICDebugNames::encryption_level(QUICEncryptionLevel::ONE_RTT), - this->_pinger->count()); + this->_pinger->count(QUICEncryptionLevel::ONE_RTT)); } void QUICLossDetector::_send_one_handshake_packets() { this->_send_packet(QUICEncryptionLevel::HANDSHAKE); - QUICLDDebug("[%s] send handshake packet", QUICDebugNames::encryption_level(QUICEncryptionLevel::HANDSHAKE)); + QUICLDDebug("[%s] send handshake packet: ping count=%" PRIu64, QUICDebugNames::encryption_level(QUICEncryptionLevel::HANDSHAKE), + this->_pinger->count(QUICEncryptionLevel::HANDSHAKE)); } void QUICLossDetector::_send_one_padded_packets() { this->_send_packet(QUICEncryptionLevel::INITIAL, true); - QUICLDDebug("[%s] send PADDING frame", QUICDebugNames::encryption_level(QUICEncryptionLevel::INITIAL)); + QUICLDDebug("[%s] send PADDING frame: ping count=%" PRIu64, QUICDebugNames::encryption_level(QUICEncryptionLevel::INITIAL), + this->_pinger->count(QUICEncryptionLevel::INITIAL)); } // ===== Functions below are helper functions ===== diff --git a/iocore/net/quic/QUICLossDetector.h b/iocore/net/quic/QUICLossDetector.h index 5e4005b8546..42afd81891f 100644 --- a/iocore/net/quic/QUICLossDetector.h +++ b/iocore/net/quic/QUICLossDetector.h @@ -59,7 +59,7 @@ class QUICRTTProvider class QUICNewRenoCongestionController : public QUICCongestionController { public: - QUICNewRenoCongestionController(QUICCCContext &context); + QUICNewRenoCongestionController(QUICContext &context); virtual ~QUICNewRenoCongestionController() {} void on_packet_sent(size_t bytes_sent) override; void on_packet_acked(const QUICPacketInfo &acked_packet) override; @@ -71,9 +71,9 @@ class QUICNewRenoCongestionController : public QUICCongestionController bool is_app_limited(); // Debug - uint32_t bytes_in_flight() const; - uint32_t congestion_window() const; - uint32_t current_ssthresh() const; + uint32_t bytes_in_flight() const override; + uint32_t congestion_window() const override; + uint32_t current_ssthresh() const override; void add_extra_credit() override; @@ -105,13 +105,13 @@ class QUICNewRenoCongestionController : public QUICCongestionController bool _in_congestion_recovery(ink_hrtime sent_time); - QUICCCContext &_context; + QUICContext &_context; }; class QUICLossDetector : public Continuation, public QUICFrameHandler { public: - QUICLossDetector(QUICLDContext &context, QUICCongestionController *cc, QUICRTTMeasure *rtt_measure, QUICPinger *pinger, + QUICLossDetector(QUICContext &context, QUICCongestionController *cc, QUICRTTMeasure *rtt_measure, QUICPinger *pinger, QUICPadder *padder); ~QUICLossDetector(); @@ -185,7 +185,7 @@ class QUICLossDetector : public Continuation, public QUICFrameHandler QUICPadder *_padder = nullptr; QUICCongestionController *_cc = nullptr; - QUICLDContext &_context; + QUICContext &_context; }; class QUICRTTMeasure : public QUICRTTProvider diff --git a/iocore/net/quic/QUICNewRenoCongestionController.cc b/iocore/net/quic/QUICNewRenoCongestionController.cc index 080c910ed4c..db6ec90a149 100644 --- a/iocore/net/quic/QUICNewRenoCongestionController.cc +++ b/iocore/net/quic/QUICNewRenoCongestionController.cc @@ -38,7 +38,7 @@ this->_context.connection_info()->cids().data(), this->_congestion_window, this->_bytes_in_flight, this->_ssthresh, \ this->_extra_packets_count, ##__VA_ARGS__) -QUICNewRenoCongestionController::QUICNewRenoCongestionController(QUICCCContext &context) +QUICNewRenoCongestionController::QUICNewRenoCongestionController(QUICContext &context) : _cc_mutex(new_ProxyMutex()), _context(context) { auto &cc_config = context.cc_config(); @@ -94,10 +94,13 @@ QUICNewRenoCongestionController::on_packet_acked(const QUICPacketInfo &acked_pac if (this->_congestion_window < this->_ssthresh) { // Slow start. + this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED, QUICCongestionController::State::SLOW_START); this->_congestion_window += acked_packet.sent_bytes; QUICCCDebug("slow start window chaged"); } else { // Congestion avoidance. + this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED, + QUICCongestionController::State::CONGESTION_AVOIDANCE); this->_congestion_window += this->_k_max_datagram_size * acked_packet.sent_bytes / this->_congestion_window; QUICCCDebug("Congestion avoidance window changed"); } @@ -116,6 +119,9 @@ QUICNewRenoCongestionController::_congestion_event(ink_hrtime sent_time) this->_congestion_window *= this->_k_loss_reduction_factor; this->_congestion_window = std::max(this->_congestion_window, this->_k_minimum_window); this->_ssthresh = this->_congestion_window; + this->_context.trigger(QUICContext::CallbackEvent::CONGESTION_STATE_CHANGED, QUICCongestionController::State::RECOVERY); + this->_context.trigger(QUICContext::CallbackEvent::METRICS_UPDATE, this->_congestion_window, this->_bytes_in_flight, + this->_ssthresh); } } diff --git a/iocore/net/quic/QUICPacket.cc b/iocore/net/quic/QUICPacket.cc index 534ed2770f7..494dd3249f8 100644 --- a/iocore/net/quic/QUICPacket.cc +++ b/iocore/net/quic/QUICPacket.cc @@ -23,258 +23,438 @@ #include "QUICPacket.h" +#include + #include #include #include "QUICIntUtil.h" #include "QUICDebugNames.h" +#include "QUICRetryIntegrityTag.h" using namespace std::literals; -static constexpr std::string_view tag = "quic_packet"sv; -static constexpr uint64_t aead_tag_len = 16; +static constexpr uint64_t aead_tag_len = 16; +static constexpr int LONG_HDR_OFFSET_CONNECTION_ID = 6; +static constexpr int LONG_HDR_OFFSET_VERSION = 1; #define QUICDebug(dcid, scid, fmt, ...) \ Debug(tag.data(), "[%08" PRIx32 "-%08" PRIx32 "] " fmt, dcid.h32(), scid.h32(), ##__VA_ARGS__); -ClassAllocator quicPacketAllocator("quicPacketAllocator"); -ClassAllocator quicPacketLongHeaderAllocator("quicPacketLongHeaderAllocator"); -ClassAllocator quicPacketShortHeaderAllocator("quicPacketShortHeaderAllocator"); - -static constexpr int LONG_HDR_OFFSET_CONNECTION_ID = 6; -static constexpr int LONG_HDR_OFFSET_VERSION = 1; - // -// QUICPacketHeader +// QUICPacket // -const uint8_t * -QUICPacketHeader::buf() -{ - if (this->_buf) { - return this->_buf.get(); - } else { - // TODO Reuse serialzied data if nothing has changed - this->store(this->_serialized, &this->_buf_len); - if (this->_buf_len > MAX_PACKET_HEADER_LEN) { - ink_assert(!"Serialized packet header is too long"); - } +QUICPacket::QUICPacket() {} - this->_buf_len += this->_payload_length; - return this->_serialized; - } -} +QUICPacket::QUICPacket(bool ack_eliciting, bool probing) : _is_ack_eliciting(ack_eliciting), _is_probing_packet(probing) {} -const IpEndpoint & -QUICPacketHeader::from() const +QUICPacket::~QUICPacket() {} + +QUICKeyPhase +QUICPacket::key_phase() const { - return this->_from; + ink_assert(!"This function should not be called"); + return QUICKeyPhase::INITIAL; } -const IpEndpoint & -QUICPacketHeader::to() const +bool +QUICPacket::is_ack_eliciting() const { - return this->_to; + return this->_is_ack_eliciting; } bool -QUICPacketHeader::is_crypto_packet() const +QUICPacket::is_probing_packet() const { - return false; + return this->_is_probing_packet; } uint16_t -QUICPacketHeader::packet_size() const +QUICPacket::header_size() const { - return this->_buf_len; -} + uint16_t size = 0; -QUICPacketHeaderUPtr -QUICPacketHeader::load(const IpEndpoint from, const IpEndpoint to, ats_unique_buf buf, size_t len, QUICPacketNumber base) -{ - QUICPacketHeaderUPtr header = QUICPacketHeaderUPtr(nullptr, &QUICPacketHeaderDeleter::delete_null_header); - if (QUICInvariants::is_long_header(buf.get())) { - QUICPacketLongHeader *long_header = quicPacketLongHeaderAllocator.alloc(); - new (long_header) QUICPacketLongHeader(from, to, std::move(buf), len, base); - header = QUICPacketHeaderUPtr(long_header, &QUICPacketHeaderDeleter::delete_long_header); - } else { - QUICPacketShortHeader *short_header = quicPacketShortHeaderAllocator.alloc(); - new (short_header) QUICPacketShortHeader(from, to, std::move(buf), len, base); - header = QUICPacketHeaderUPtr(short_header, &QUICPacketHeaderDeleter::delete_short_header); + for (auto b = this->header_block(); b; b = b->next) { + size += b->size(); } - return header; + + return size; } -QUICPacketHeaderUPtr -QUICPacketHeader::build(QUICPacketType type, QUICKeyPhase key_phase, QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, QUICVersion version, bool crypto, - ats_unique_buf payload, size_t len) +uint16_t +QUICPacket::payload_length() const { - QUICPacketLongHeader *long_header = quicPacketLongHeaderAllocator.alloc(); - new (long_header) QUICPacketLongHeader(type, key_phase, destination_cid, source_cid, packet_number, base_packet_number, version, - crypto, std::move(payload), len); - return QUICPacketHeaderUPtr(long_header, &QUICPacketHeaderDeleter::delete_long_header); + uint16_t size = 0; + + for (auto b = this->payload_block(); b; b = b->next) { + size += b->size(); + } + + return size; } -QUICPacketHeaderUPtr -QUICPacketHeader::build(QUICPacketType type, QUICKeyPhase key_phase, QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, QUICVersion version, bool crypto, - ats_unique_buf payload, size_t len, ats_unique_buf token, size_t token_len) +uint16_t +QUICPacket::size() const { - QUICPacketLongHeader *long_header = quicPacketLongHeaderAllocator.alloc(); - new (long_header) QUICPacketLongHeader(type, key_phase, destination_cid, source_cid, packet_number, base_packet_number, version, - crypto, std::move(payload), len, std::move(token), token_len); - return QUICPacketHeaderUPtr(long_header, &QUICPacketHeaderDeleter::delete_long_header); + return this->header_size() + this->payload_length(); } -QUICPacketHeaderUPtr -QUICPacketHeader::build(QUICPacketType type, QUICKeyPhase key_phase, QUICVersion version, QUICConnectionId destination_cid, - QUICConnectionId source_cid, QUICConnectionId original_dcid, ats_unique_buf retry_token, - size_t retry_token_len) +void +QUICPacket::store(uint8_t *buf, size_t *len) const { - QUICPacketLongHeader *long_header = quicPacketLongHeaderAllocator.alloc(); - new (long_header) QUICPacketLongHeader(type, key_phase, version, destination_cid, source_cid, original_dcid, - std::move(retry_token), retry_token_len); - return QUICPacketHeaderUPtr(long_header, &QUICPacketHeaderDeleter::delete_long_header); + size_t written = 0; + Ptr block; + + block = this->header_block(); + while (block) { + memcpy(buf + written, block->start(), block->size()); + written += block->size(); + block = block->next; + } + + block = this->payload_block(); + while (block) { + memcpy(buf + written, block->start(), block->size()); + written += block->size(); + block = block->next; + } + + *len = written; } -QUICPacketHeaderUPtr -QUICPacketHeader::build(QUICPacketType type, QUICKeyPhase key_phase, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len) +uint8_t +QUICPacket::calc_packet_number_len(QUICPacketNumber num, QUICPacketNumber base) { - QUICPacketShortHeader *short_header = quicPacketShortHeaderAllocator.alloc(); - new (short_header) QUICPacketShortHeader(type, key_phase, packet_number, base_packet_number, std::move(payload), len); - return QUICPacketHeaderUPtr(short_header, &QUICPacketHeaderDeleter::delete_short_header); + uint64_t d = (num - base) * 2; + uint8_t len = 0; + + if (d > 0xFFFFFF) { + len = 4; + } else if (d > 0xFFFF) { + len = 3; + } else if (d > 0xFF) { + len = 2; + } else { + len = 1; + } + + return len; } -QUICPacketHeaderUPtr -QUICPacketHeader::build(QUICPacketType type, QUICKeyPhase key_phase, QUICConnectionId connection_id, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len) +bool +QUICPacket::encode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len) { - QUICPacketShortHeader *short_header = quicPacketShortHeaderAllocator.alloc(); - new (short_header) - QUICPacketShortHeader(type, key_phase, connection_id, packet_number, base_packet_number, std::move(payload), len); - return QUICPacketHeaderUPtr(short_header, &QUICPacketHeaderDeleter::delete_short_header); + uint64_t mask = 0; + switch (len) { + case 1: + mask = 0xFF; + break; + case 2: + mask = 0xFFFF; + break; + case 3: + mask = 0xFFFFFF; + break; + case 4: + mask = 0xFFFFFFFF; + break; + default: + ink_assert(!"len must be 1, 2, or 4"); + return false; + } + dst = src & mask; + + return true; } -QUICPacketHeaderUPtr -QUICPacketHeader::clone() const +bool +QUICPacket::decode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len, QUICPacketNumber largest_acked) { - return QUICPacketHeaderUPtr(nullptr, &QUICPacketHeaderDeleter::delete_null_header); + ink_assert(len == 1 || len == 2 || len == 3 || len == 4); + + uint64_t maximum_diff = 0; + switch (len) { + case 1: + maximum_diff = 0x100; + break; + case 2: + maximum_diff = 0x10000; + break; + case 3: + maximum_diff = 0x1000000; + break; + case 4: + maximum_diff = 0x100000000; + break; + default: + ink_assert(!"len must be 1, 2, 3 or 4"); + } + QUICPacketNumber base = largest_acked & (~(maximum_diff - 1)); + QUICPacketNumber candidate1 = base + src; + QUICPacketNumber candidate2 = base + src + maximum_diff; + QUICPacketNumber expected = largest_acked + 1; + + if (((candidate1 > expected) ? (candidate1 - expected) : (expected - candidate1)) < + ((candidate2 > expected) ? (candidate2 - expected) : (expected - candidate2))) { + dst = candidate1; + } else { + dst = candidate2; + } + + return true; } // -// QUICPacketLongHeader +// QUICPacketR // -QUICPacketLongHeader::QUICPacketLongHeader(const IpEndpoint from, const IpEndpoint to, ats_unique_buf buf, size_t len, - QUICPacketNumber base) - : QUICPacketHeader(from, to, std::move(buf), len, base) +QUICPacketR::QUICPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to) : _udp_con(udp_con), _from(from), _to(to) {} + +UDPConnection * +QUICPacketR::udp_con() const { - this->_key_phase = QUICTypeUtil::key_phase(this->type()); - uint8_t *raw_buf = this->_buf.get(); + return this->_udp_con; +} - uint8_t dcil = 0; - uint8_t scil = 0; - QUICPacketLongHeader::dcil(dcil, raw_buf, len); - QUICPacketLongHeader::scil(scil, raw_buf, len); +const IpEndpoint & +QUICPacketR::from() const +{ + return this->_from; +} - size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; - this->_destination_cid = {raw_buf + offset, dcil}; - offset += dcil + 1; - this->_source_cid = {raw_buf + offset, scil}; - offset += scil; +const IpEndpoint & +QUICPacketR::to() const +{ + return this->_to; +} - if (this->type() != QUICPacketType::VERSION_NEGOTIATION) { - if (this->type() == QUICPacketType::RETRY) { - uint8_t odcil = raw_buf[offset]; - offset += 1; +bool +QUICPacketR::type(QUICPacketType &type, const uint8_t *packet, size_t packet_len) +{ + if (packet_len < 1) { + return false; + } - this->_original_dcid = {raw_buf + offset, odcil}; - offset += odcil; - } else { - if (this->type() == QUICPacketType::INITIAL) { - // Token Length Field - this->_token_len = QUICIntUtil::read_QUICVariableInt(raw_buf + offset); - offset += QUICVariableInt::size(raw_buf + offset); - // Token Field - this->_token_offset = offset; - offset += this->_token_len; - } + if (QUICInvariants::is_long_header(packet)) { + return QUICLongHeaderPacketR::type(type, packet, packet_len); + } else { + type = QUICPacketType::PROTECTED; + return true; + } +} + +bool +QUICPacketR::read_essential_info(Ptr block, QUICPacketType &type, QUICVersion &version, QUICConnectionId &dcid, + QUICConnectionId &scid, QUICPacketNumber &packet_number, QUICPacketNumber base_packet_number, + QUICKeyPhase &key_phase) +{ + uint8_t tmp[47 + 64]; + IOBufferReader reader; + reader.block = block; + int64_t len = std::min(static_cast(sizeof(tmp)), reader.read_avail()); - // Length Field - offset += QUICVariableInt::size(raw_buf + offset); + if (len < 10) { + return false; + } - // PN Field - int pn_len = QUICTypeUtil::read_QUICPacketNumberLen(raw_buf); - QUICPacket::decode_packet_number(this->_packet_number, QUICTypeUtil::read_QUICPacketNumber(raw_buf + offset, pn_len), pn_len, - this->_base_packet_number); - offset += pn_len; + reader.memcpy(tmp, 1, 0); + if (QUICInvariants::is_long_header(tmp)) { + reader.memcpy(tmp, len, 0); + type = static_cast((0x30 & tmp[0]) >> 4); + QUICInvariants::version(version, tmp, len); + if (version == 0x00) { + type = QUICPacketType::VERSION_NEGOTIATION; + } + if (!QUICInvariants::dcid(dcid, tmp, len) || !QUICInvariants::scid(scid, tmp, len)) { + return false; + } + if (type != QUICPacketType::RETRY) { + int packet_number_len = QUICTypeUtil::read_QUICPacketNumberLen(tmp); + size_t length_offset = 7 + dcid.length() + scid.length(); + if (length_offset >= static_cast(len)) { + return false; + } + uint64_t value; + size_t field_len; + QUICVariableInt::decode(value, field_len, tmp + length_offset); + switch (type) { + case QUICPacketType::INITIAL: + length_offset += field_len + value; + if (length_offset >= static_cast(len)) { + return false; + } + QUICVariableInt::decode(value, field_len, tmp + length_offset); + if (length_offset + field_len >= static_cast(len)) { + return false; + } + if (length_offset + field_len + packet_number_len > static_cast(len)) { + return false; + } + packet_number = QUICTypeUtil::read_QUICPacketNumber(tmp + length_offset + field_len, packet_number_len); + key_phase = QUICKeyPhase::INITIAL; + break; + case QUICPacketType::ZERO_RTT_PROTECTED: + if (length_offset + field_len + packet_number_len >= static_cast(len)) { + return false; + } + packet_number = QUICTypeUtil::read_QUICPacketNumber(tmp + length_offset + field_len, packet_number_len); + key_phase = QUICKeyPhase::ZERO_RTT; + break; + case QUICPacketType::HANDSHAKE: + if (length_offset + field_len + packet_number_len >= static_cast(len)) { + return false; + } + packet_number = QUICTypeUtil::read_QUICPacketNumber(tmp + length_offset + field_len, packet_number_len); + key_phase = QUICKeyPhase::INITIAL; + break; + case QUICPacketType::VERSION_NEGOTIATION: + break; + default: + break; + } + } else { + packet_number = 0; + } + } else { + len = std::min(static_cast(25), len); + reader.memcpy(tmp, len, 0); + type = QUICPacketType::PROTECTED; + QUICInvariants::dcid(dcid, tmp, len); + int packet_number_len = QUICTypeUtil::read_QUICPacketNumberLen(tmp); + if (tmp[0] & 0x04) { + key_phase = QUICKeyPhase::PHASE_1; + } else { + key_phase = QUICKeyPhase::PHASE_0; } + packet_number = QUICTypeUtil::read_QUICPacketNumber(tmp + 1 + dcid.length(), packet_number_len); } + return true; +} - this->_payload_offset = offset; - this->_payload_length = len - this->_payload_offset; +// +// QUICLongHeaderPacket +// +QUICLongHeaderPacket::QUICLongHeaderPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, bool ack_eliciting, + bool probing, bool crypto) + : QUICPacket(ack_eliciting, probing), _version(version), _dcid(dcid), _scid(scid), _is_crypto_packet(crypto) +{ } -QUICPacketLongHeader::QUICPacketLongHeader(QUICPacketType type, QUICKeyPhase key_phase, const QUICConnectionId &destination_cid, - const QUICConnectionId &source_cid, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, QUICVersion version, bool crypto, - ats_unique_buf buf, size_t len, ats_unique_buf token, size_t token_len) - : QUICPacketHeader(type, packet_number, base_packet_number, true, version, std::move(buf), len, key_phase), - _destination_cid(destination_cid), - _source_cid(source_cid), - _token_len(token_len), - _token(std::move(token)), - _is_crypto_packet(crypto) +QUICConnectionId +QUICLongHeaderPacket::destination_cid() const { - if (this->_type == QUICPacketType::VERSION_NEGOTIATION) { - this->_buf_len = - LONG_HDR_OFFSET_CONNECTION_ID + this->_destination_cid.length() + 1 + this->_source_cid.length() + this->_payload_length; - } else { - this->buf(); - } + return this->_dcid; } -QUICPacketLongHeader::QUICPacketLongHeader(QUICPacketType type, QUICKeyPhase key_phase, QUICVersion version, - const QUICConnectionId &destination_cid, const QUICConnectionId &source_cid, - const QUICConnectionId &original_dcid, ats_unique_buf retry_token, - size_t retry_token_len) - : QUICPacketHeader(type, 0, 0, true, version, std::move(retry_token), retry_token_len, key_phase), - _destination_cid(destination_cid), - _source_cid(source_cid), - _original_dcid(original_dcid) +QUICConnectionId +QUICLongHeaderPacket::source_cid() const +{ + return this->_scid; +} +uint16_t +QUICLongHeaderPacket::payload_length() const { - // this->_buf_len will be set - this->buf(); + return this->_payload_length; } -QUICPacketType -QUICPacketLongHeader::type() const +QUICVersion +QUICLongHeaderPacket::version() const +{ + return this->_version; +} + +size_t +QUICLongHeaderPacket::_write_common_header(uint8_t *buf) const { - if (this->_buf) { - QUICPacketType type = QUICPacketType::UNINITIALIZED; - QUICPacketLongHeader::type(type, this->_buf.get(), this->_buf_len); - return type; + size_t n; + size_t len = 0; + + buf[0] = 0xC0; + buf[0] += static_cast(this->type()) << 4; + len += 1; + + QUICTypeUtil::write_QUICVersion(this->_version, buf + len, &n); + len += n; + + // DICD + if (this->_dcid != QUICConnectionId::ZERO()) { + // Len + buf[len] = this->_dcid.length(); + len += 1; + + // ID + QUICTypeUtil::write_QUICConnectionId(this->_dcid, buf + len, &n); + len += n; + } else { + buf[len] = 0; + len += 1; + } + + // SCID + if (this->_scid != QUICConnectionId::ZERO()) { + // Len + buf[len] = this->_scid.length(); + len += 1; + + // ID + QUICTypeUtil::write_QUICConnectionId(this->_scid, buf + len, &n); + len += n; } else { - return this->_type; + buf[len] = 0; + len += 1; } + + return len; } bool -QUICPacketLongHeader::is_crypto_packet() const +QUICLongHeaderPacket::is_crypto_packet() const { return this->_is_crypto_packet; } +// +// QUICLongHeaderPacketR +// +QUICLongHeaderPacketR::QUICLongHeaderPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks) + : QUICPacketR(udp_con, from, to) +{ + IOBufferReader reader; + uint8_t data[47]; + + reader.block = blocks; + int64_t data_len = reader.read(data, sizeof(data)); + + QUICLongHeaderPacketR::version(this->_version, data, data_len); +} + +QUICVersion +QUICLongHeaderPacketR::version() const +{ + return this->_version; +} + +QUICConnectionId +QUICLongHeaderPacketR::source_cid() const +{ + return this->_scid; +} + +QUICConnectionId +QUICLongHeaderPacketR::destination_cid() const +{ + return this->_dcid; +} + bool -QUICPacketLongHeader::type(QUICPacketType &type, const uint8_t *packet, size_t packet_len) +QUICLongHeaderPacketR::type(QUICPacketType &type, const uint8_t *packet, size_t packet_len) { if (packet_len < 1) { return false; } QUICVersion version; - if (QUICPacketLongHeader::version(version, packet, packet_len) && version == 0x00) { + if (QUICLongHeaderPacketR::version(version, packet, packet_len) && version == 0x00) { type = QUICPacketType::VERSION_NEGOTIATION; } else { uint8_t raw_type = (packet[0] & 0x30) >> 4; @@ -284,7 +464,7 @@ QUICPacketLongHeader::type(QUICPacketType &type, const uint8_t *packet, size_t p } bool -QUICPacketLongHeader::version(QUICVersion &version, const uint8_t *packet, size_t packet_len) +QUICLongHeaderPacketR::version(QUICVersion &version, const uint8_t *packet, size_t packet_len) { if (packet_len < 5) { return false; @@ -295,101 +475,70 @@ QUICPacketLongHeader::version(QUICVersion &version, const uint8_t *packet, size_ } bool -QUICPacketLongHeader::dcil(uint8_t &dcil, const uint8_t *packet, size_t packet_len) -{ - if (QUICInvariants::dcil(dcil, packet, packet_len)) { - return true; - } else { - return false; - } -} - -bool -QUICPacketLongHeader::scil(uint8_t &scil, const uint8_t *packet, size_t packet_len) -{ - if (QUICInvariants::scil(scil, packet, packet_len)) { - return true; - } else { - return false; - } -} - -bool -QUICPacketLongHeader::token_length(size_t &token_length, uint8_t &field_len, size_t &token_length_filed_offset, - const uint8_t *packet, size_t packet_len) +QUICLongHeaderPacketR::key_phase(QUICKeyPhase &phase, const uint8_t *packet, size_t packet_len) { QUICPacketType type = QUICPacketType::UNINITIALIZED; - QUICPacketLongHeader::type(type, packet, packet_len); - - if (type != QUICPacketType::INITIAL) { - token_length = 0; - field_len = 0; - - return true; - } - - uint8_t dcil, scil; - QUICPacketLongHeader::dcil(dcil, packet, packet_len); - QUICPacketLongHeader::scil(scil, packet, packet_len); - - token_length_filed_offset = LONG_HDR_OFFSET_CONNECTION_ID + dcil + 1 + scil; - if (token_length_filed_offset >= packet_len) { - return false; - } - - token_length = QUICIntUtil::read_QUICVariableInt(packet + token_length_filed_offset); - field_len = QUICVariableInt::size(packet + token_length_filed_offset); - + QUICLongHeaderPacketR::type(type, packet, packet_len); + phase = QUICTypeUtil::key_phase(type); return true; } bool -QUICPacketLongHeader::length(size_t &length, uint8_t &length_field_len, size_t &length_field_offset, const uint8_t *packet, - size_t packet_len) +QUICLongHeaderPacketR::length(size_t &length, uint8_t &length_field_len, size_t &length_field_offset, const uint8_t *packet, + size_t packet_len) { + // FIXME This is not great because each packet types have different formats. + // We should remove this function and have length() on each packet type classes instead. + uint8_t dcil; - if (!QUICPacketLongHeader::dcil(dcil, packet, packet_len)) { + if (!QUICInvariants::dcil(dcil, packet, packet_len)) { return false; } uint8_t scil; - if (!QUICPacketLongHeader::scil(scil, packet, packet_len)) { + if (!QUICInvariants::scil(scil, packet, packet_len)) { return false; } - // Token Length (i) + Token (*) (for INITIAL packet) - size_t token_length = 0; - uint8_t token_length_field_len = 0; - size_t token_length_field_offset = 0; - if (!QUICPacketLongHeader::token_length(token_length, token_length_field_len, token_length_field_offset, packet, packet_len)) { - return false; + length_field_offset = LONG_HDR_OFFSET_CONNECTION_ID + dcil + 1 + scil; + + QUICPacketType type = QUICPacketType::UNINITIALIZED; + QUICLongHeaderPacketR::type(type, packet, packet_len); + if (type == QUICPacketType::INITIAL) { + // Token Length (i) + Token (*) (for INITIAL packet) + size_t token_length = 0; + uint8_t token_length_field_len = 0; + size_t token_length_field_offset = 0; + if (!QUICInitialPacketR::token_length(token_length, token_length_field_len, token_length_field_offset, packet, packet_len)) { + return false; + } + length_field_offset += token_length_field_len + token_length; } // Length (i) - length_field_offset = LONG_HDR_OFFSET_CONNECTION_ID + dcil + 1 + scil + token_length_field_len + token_length; if (length_field_offset >= packet_len) { return false; } length_field_len = QUICVariableInt::size(packet + length_field_offset); - length = QUICIntUtil::read_QUICVariableInt(packet + length_field_offset); + length = QUICIntUtil::read_QUICVariableInt(packet + length_field_offset, packet_len - length_field_offset); return true; } bool -QUICPacketLongHeader::packet_number_offset(size_t &pn_offset, const uint8_t *packet, size_t packet_len) +QUICLongHeaderPacketR::packet_length(size_t &packet_len, const uint8_t *buf, size_t buf_len) { size_t length; uint8_t length_field_len; size_t length_field_offset; - if (!QUICPacketLongHeader::length(length, length_field_len, length_field_offset, packet, packet_len)) { + if (!QUICLongHeaderPacketR::length(length, length_field_len, length_field_offset, buf, buf_len)) { return false; } - pn_offset = length_field_offset + length_field_len; + packet_len = length + length_field_offset + length_field_len; - if (pn_offset >= packet_len) { + if (packet_len > buf_len) { return false; } @@ -397,639 +546,1297 @@ QUICPacketLongHeader::packet_number_offset(size_t &pn_offset, const uint8_t *pac } bool -QUICPacketLongHeader::packet_length(size_t &packet_len, const uint8_t *buf, size_t buf_len) +QUICLongHeaderPacketR::packet_number_offset(size_t &pn_offset, const uint8_t *packet, size_t packet_len) { - size_t length; + size_t dummy; uint8_t length_field_len; size_t length_field_offset; - if (!QUICPacketLongHeader::length(length, length_field_len, length_field_offset, buf, buf_len)) { + if (!QUICLongHeaderPacketR::length(dummy, length_field_len, length_field_offset, packet, packet_len)) { return false; } - packet_len = length + length_field_offset + length_field_len; + pn_offset = length_field_offset + length_field_len; - if (packet_len > buf_len) { + if (pn_offset >= packet_len) { return false; } return true; } -bool -QUICPacketLongHeader::key_phase(QUICKeyPhase &phase, const uint8_t *packet, size_t packet_len) +// +// QUICShortHeaderPacket +// +QUICShortHeaderPacket::QUICShortHeaderPacket(QUICConnectionId dcid, QUICPacketNumber packet_number, + QUICPacketNumber base_packet_number, QUICKeyPhase key_phase, bool ack_eliciting, + bool probing) + : QUICPacket(ack_eliciting, probing), _dcid(dcid), _packet_number(packet_number), _key_phase(key_phase) { - QUICPacketType type = QUICPacketType::UNINITIALIZED; - QUICPacketLongHeader::type(type, packet, packet_len); - phase = QUICTypeUtil::key_phase(type); - return true; + this->_packet_number_len = QUICPacket::calc_packet_number_len(packet_number, base_packet_number); } -QUICConnectionId -QUICPacketLongHeader::destination_cid() const +QUICPacketType +QUICShortHeaderPacket::type() const { - return this->_destination_cid; + return QUICPacketType::PROTECTED; } -QUICConnectionId -QUICPacketLongHeader::source_cid() const +QUICKeyPhase +QUICShortHeaderPacket::key_phase() const { - return this->_source_cid; + return this->_key_phase; } QUICConnectionId -QUICPacketLongHeader::original_dcid() const +QUICShortHeaderPacket::destination_cid() const { - return this->_original_dcid; + return this->_dcid; } QUICPacketNumber -QUICPacketLongHeader::packet_number() const +QUICShortHeaderPacket::packet_number() const { return this->_packet_number; } -bool -QUICPacketLongHeader::has_version() const +uint16_t +QUICShortHeaderPacket::payload_length() const { - return true; + return this->_payload_length; } -bool -QUICPacketLongHeader::is_valid() const +Ptr +QUICShortHeaderPacket::header_block() const { - if (this->_buf && this->_buf_len != this->_payload_offset + this->_payload_length) { - QUICDebug(this->_source_cid, this->_destination_cid, - "Invalid packet: packet_size(%zu) should be header_size(%zu) + payload_size(%zu)", this->_buf_len, - this->_payload_offset, this->_payload_length); - Warning("Invalid packet: packet_size(%zu) should be header_size(%zu) + payload_size(%zu)", this->_buf_len, - this->_payload_offset, this->_payload_length); + Ptr block; + size_t written_len = 0; - return false; - } + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(1 + QUICConnectionId::MAX_LENGTH + 4, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); - return true; -} + size_t n; + buf[0] = 0x40; -QUICVersion -QUICPacketLongHeader::version() const -{ - if (this->_buf) { - QUICVersion version = 0; - QUICPacketLongHeader::version(version, this->_buf.get(), this->_buf_len); - return version; - } else { - return this->_version; - } -} + // Type + buf[0] = 0x40; -const uint8_t * -QUICPacketLongHeader::payload() const -{ - if (this->_buf) { - uint8_t *raw = this->_buf.get(); - return raw + this->_payload_offset; - } else { - return this->_payload.get(); + // TODO Spin Bit + + // KeyPhase + if (this->_key_phase == QUICKeyPhase::PHASE_1) { + buf[0] |= 0x04; } -} -uint16_t -QUICPacketHeader::payload_size() const -{ - return this->_payload_length; -} + written_len += 1; -const uint8_t * -QUICPacketLongHeader::token() const -{ - if (this->_buf) { - uint8_t *raw = this->_buf.get(); - return raw + this->_token_offset; - } else { - return this->_token.get(); + // Destination Connection ID + if (this->_dcid != QUICConnectionId::ZERO()) { + QUICTypeUtil::write_QUICConnectionId(this->_dcid, buf + written_len, &n); + written_len += n; } -} -size_t -QUICPacketLongHeader::token_len() const -{ - return this->_token_len; -} + // Packet Number + QUICPacketNumber dst = 0; + size_t dst_len = this->_packet_number_len; + QUICPacket::encode_packet_number(dst, this->_packet_number, dst_len); + QUICTypeUtil::write_QUICPacketNumber(dst, dst_len, buf + written_len, &n); + written_len += n; -QUICKeyPhase -QUICPacketLongHeader::key_phase() const -{ - return this->_key_phase; + // Packet Number Length + QUICTypeUtil::write_QUICPacketNumberLen(n, buf); + + block->fill(written_len); + + return block; } -uint16_t -QUICPacketLongHeader::size() const +Ptr +QUICShortHeaderPacket::payload_block() const { - return this->_buf_len - this->_payload_length; + return this->_payload_block; } void -QUICPacketLongHeader::store(uint8_t *buf, size_t *len) const +QUICShortHeaderPacket::attach_payload(Ptr payload, bool unprotected) +{ + this->_payload_block = payload; + this->_payload_length = 0; + Ptr tmp = payload; + while (tmp) { + this->_payload_length += tmp->size(); + tmp = tmp->next; + } + if (unprotected) { + this->_payload_length += aead_tag_len; + } +} + +// +// QUICShortHeaderPacketR +// +QUICShortHeaderPacketR::QUICShortHeaderPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number) + : QUICPacketR(udp_con, from, to) { - size_t n; - *len = 0; - buf[0] = 0xC0; - buf[0] += static_cast(this->_type) << 4; - if (this->_type == QUICPacketType::VERSION_NEGOTIATION) { - buf[0] |= rand(); + size_t len = 0; + for (auto b = blocks; b; b = b->next) { + len += b->size(); } - *len += 1; - QUICTypeUtil::write_QUICVersion(this->_version, buf + *len, &n); - *len += n; + Ptr concatinated_block = make_ptr(new_IOBufferBlock()); + concatinated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + concatinated_block->fill(len); - // DICD - if (this->_destination_cid != QUICConnectionId::ZERO()) { - // Len - buf[*len] = this->_destination_cid.length(); - *len += 1; + uint8_t *raw_buf = reinterpret_cast(concatinated_block->start()); - // ID - QUICTypeUtil::write_QUICConnectionId(this->_destination_cid, buf + *len, &n); - *len += n; - } else { - buf[*len] = 0; - *len += 1; + size_t copied_len = 0; + for (auto b = blocks; b; b = b->next) { + memcpy(raw_buf + copied_len, b->start(), b->size()); + copied_len += b->size(); } - // SCID - if (this->_source_cid != QUICConnectionId::ZERO()) { - // Len - buf[*len] = this->_source_cid.length(); - *len += 1; - - // ID - QUICTypeUtil::write_QUICConnectionId(this->_source_cid, buf + *len, &n); - *len += n; + if (raw_buf[0] & 0x04) { + this->_key_phase = QUICKeyPhase::PHASE_1; } else { - buf[*len] = 0; - *len += 1; + this->_key_phase = QUICKeyPhase::PHASE_0; } - if (this->_type != QUICPacketType::VERSION_NEGOTIATION) { - if (this->_type == QUICPacketType::RETRY) { - // Original Destination Connection ID - if (this->_original_dcid != QUICConnectionId::ZERO()) { - // Len - buf[*len] = this->_original_dcid.length(); - *len += 1; - - // ID - QUICTypeUtil::write_QUICConnectionId(this->_original_dcid, buf + *len, &n); - *len += n; - } else { - buf[*len] = 0; - *len += 1; - } - } else { - if (this->_type == QUICPacketType::INITIAL) { - // Token Length Field - QUICIntUtil::write_QUICVariableInt(this->_token_len, buf + *len, &n); - *len += n; - - // Token Field - memcpy(buf + *len, this->token(), this->token_len()); - *len += this->token_len(); - } - - QUICPacketNumber pn = 0; - size_t pn_len = 4; - QUICPacket::encode_packet_number(pn, this->_packet_number, pn_len); - - if (pn > 0x7FFFFF) { - pn_len = 4; - } else if (pn > 0x7FFF) { - pn_len = 3; - } else if (pn > 0x7F) { - pn_len = 2; - } else { - pn_len = 1; - } - - if (this->_type != QUICPacketType::RETRY) { - // PN Len field - QUICTypeUtil::write_QUICPacketNumberLen(pn_len, buf); - } - - // Length Field - QUICIntUtil::write_QUICVariableInt(pn_len + this->_payload_length + aead_tag_len, buf + *len, &n); - *len += n; + QUICInvariants::dcid(this->_dcid, raw_buf, len); - // PN Field - QUICTypeUtil::write_QUICPacketNumber(pn, pn_len, buf + *len, &n); - *len += n; - } + int offset = 1 + this->_dcid.length(); + this->_packet_number_len = QUICTypeUtil::read_QUICPacketNumberLen(raw_buf); + QUICPacketNumber src = QUICTypeUtil::read_QUICPacketNumber(raw_buf + offset, this->_packet_number_len); + QUICPacket::decode_packet_number(this->_packet_number, src, this->_packet_number_len, base_packet_number); + offset += this->_packet_number_len; - // Payload will be stored - } + this->_header_block = concatinated_block->clone(); + this->_header_block->_end = this->_header_block->_start + offset; + this->_header_block->next = nullptr; + this->_payload_block = concatinated_block->clone(); + this->_payload_block->_start = this->_payload_block->_start + offset; } -// -// QUICPacketShortHeader -// - -QUICPacketShortHeader::QUICPacketShortHeader(const IpEndpoint from, const IpEndpoint to, ats_unique_buf buf, size_t len, - QUICPacketNumber base) - : QUICPacketHeader(from, to, std::move(buf), len, base) +QUICPacketType +QUICShortHeaderPacketR::type() const { - QUICInvariants::dcid(this->_connection_id, this->_buf.get(), len); - - int offset = 1 + this->_connection_id.length(); - this->_packet_number_len = QUICTypeUtil::read_QUICPacketNumberLen(this->_buf.get()); - QUICPacketNumber src = QUICTypeUtil::read_QUICPacketNumber(this->_buf.get() + offset, this->_packet_number_len); - QUICPacket::decode_packet_number(this->_packet_number, src, this->_packet_number_len, this->_base_packet_number); - this->_payload_length = len - (1 + QUICConnectionId::SCID_LEN + this->_packet_number_len); + return QUICPacketType::PROTECTED; } -QUICPacketShortHeader::QUICPacketShortHeader(QUICPacketType type, QUICKeyPhase key_phase, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, ats_unique_buf buf, size_t len) +QUICKeyPhase +QUICShortHeaderPacketR::key_phase() const { - this->_type = type; - this->_key_phase = key_phase; - this->_packet_number = packet_number; - this->_base_packet_number = base_packet_number; - this->_packet_number_len = QUICPacket::calc_packet_number_len(packet_number, base_packet_number); - this->_payload = std::move(buf); - this->_payload_length = len; + return this->_key_phase; } -QUICPacketShortHeader::QUICPacketShortHeader(QUICPacketType type, QUICKeyPhase key_phase, const QUICConnectionId &connection_id, - QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, - ats_unique_buf buf, size_t len) +QUICPacketNumber +QUICShortHeaderPacketR::packet_number() const { - this->_type = type; - this->_key_phase = key_phase; - this->_connection_id = connection_id; - this->_packet_number = packet_number; - this->_base_packet_number = base_packet_number; - this->_packet_number_len = QUICPacket::calc_packet_number_len(packet_number, base_packet_number); - this->_payload = std::move(buf); - this->_payload_length = len; + return this->_packet_number; } -QUICPacketType -QUICPacketShortHeader::type() const +QUICConnectionId +QUICShortHeaderPacketR::destination_cid() const { - QUICKeyPhase key_phase = this->key_phase(); - - switch (key_phase) { - case QUICKeyPhase::PHASE_0: { - return QUICPacketType::PROTECTED; - } - case QUICKeyPhase::PHASE_1: { - return QUICPacketType::PROTECTED; - } - default: - return QUICPacketType::STATELESS_RESET; - } + return this->_dcid; } -QUICConnectionId -QUICPacketShortHeader::destination_cid() const +Ptr +QUICShortHeaderPacketR::header_block() const { - if (this->_buf) { - QUICConnectionId dcid = QUICConnectionId::ZERO(); - QUICInvariants::dcid(dcid, this->_buf.get(), this->_buf_len); - return dcid; - } else { - return _connection_id; - } + return this->_header_block; } -QUICPacketNumber -QUICPacketShortHeader::packet_number() const +Ptr +QUICShortHeaderPacketR::payload_block() const { - return this->_packet_number; + return this->_payload_block; } -bool -QUICPacketShortHeader::has_version() const +void +QUICShortHeaderPacketR::attach_payload(Ptr payload, bool unprotected) { - return false; + this->_payload_block = payload; } bool -QUICPacketShortHeader::is_valid() const +QUICShortHeaderPacketR::packet_number_offset(size_t &pn_offset, const uint8_t *packet, size_t packet_len, int dcil) { + pn_offset = 1 + dcil; return true; } -QUICVersion -QUICPacketShortHeader::version() const +// +// QUICStatelessResetPacket +// +QUICStatelessResetPacket::QUICStatelessResetPacket(QUICStatelessResetToken token, size_t maximum_size) + : QUICPacket(), _token(token), _maximum_size(maximum_size) { - return 0; } -const uint8_t * -QUICPacketShortHeader::payload() const +QUICPacketType +QUICStatelessResetPacket::type() const { - if (this->_buf) { - return this->_buf.get() + this->size(); - } else { - return this->_payload.get(); - } + return QUICPacketType::STATELESS_RESET; } -QUICKeyPhase -QUICPacketShortHeader::key_phase() const +QUICConnectionId +QUICStatelessResetPacket::destination_cid() const { - if (this->_buf) { - QUICKeyPhase phase = QUICKeyPhase::INITIAL; - QUICPacketShortHeader::key_phase(phase, this->_buf.get(), this->_buf_len); - return phase; - } else { - return this->_key_phase; - } + ink_assert(!"You should not need DCID of Stateless Reset Packet"); + return QUICConnectionId::ZERO(); } -bool -QUICPacketShortHeader::key_phase(QUICKeyPhase &phase, const uint8_t *packet, size_t packet_len) +Ptr +QUICStatelessResetPacket::header_block() const { - if (packet_len < 1) { - return false; + // Required shortest length is 38 bits however less than 41 bytes in total indicates this is stateless reset. + constexpr uint8_t MIN_UNPREDICTABLE_FIELD_LEN = 5 + 20; + + std::random_device rnd; + + Ptr block; + size_t written_len = 0; + + size_t random_extra_length = rnd() & 0x07; // Extra 0 to 7 bytes + + if (MIN_UNPREDICTABLE_FIELD_LEN + random_extra_length > this->_maximum_size) { + return block; } - if (packet[0] & 0x04) { - phase = QUICKeyPhase::PHASE_1; - } else { - phase = QUICKeyPhase::PHASE_0; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(MIN_UNPREDICTABLE_FIELD_LEN + random_extra_length, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); + + // Generate random octets + for (int i = 0; i < MIN_UNPREDICTABLE_FIELD_LEN; ++i) { + buf[i] = static_cast(rnd() & 0xFF); } - return true; + buf[0] = (buf[0] | 0x40) & 0x7f; + written_len += MIN_UNPREDICTABLE_FIELD_LEN; + + block->fill(written_len); + + return block; } -bool -QUICPacketShortHeader::packet_number_offset(size_t &pn_offset, const uint8_t *packet, size_t packet_len, int dcil) +Ptr +QUICStatelessResetPacket::payload_block() const { - pn_offset = 1 + dcil; - return true; + Ptr block; + size_t written_len = 0; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(QUICStatelessResetToken::LEN, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); + + memcpy(buf, this->_token.buf(), QUICStatelessResetToken::LEN); + written_len += QUICStatelessResetToken::LEN; + + block->fill(written_len); + + return block; } -/** - * Header Length (doesn't include payload length) - */ -uint16_t -QUICPacketShortHeader::size() const +QUICPacketNumber +QUICStatelessResetPacket::packet_number() const { - uint16_t len = 1; - if (this->_connection_id != QUICConnectionId::ZERO()) { - len += this->_connection_id.length(); - } - len += this->_packet_number_len; + ink_assert(!"You should not need packet number of Stateless Reset Packet"); + return 0; +} - return len; +QUICStatelessResetToken +QUICStatelessResetPacket::token() const +{ + return this->_token; } -void -QUICPacketShortHeader::store(uint8_t *buf, size_t *len) const +// +// QUICStatelessResetPacketR +// +QUICStatelessResetPacketR::QUICStatelessResetPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, + Ptr blocks) + : QUICPacketR(udp_con, from, to) { - size_t n; - *len = 0; - buf[0] = 0x40; - if (this->_key_phase == QUICKeyPhase::PHASE_1) { - buf[0] |= 0x04; - } - *len += 1; +} - if (this->_connection_id != QUICConnectionId::ZERO()) { - QUICTypeUtil::write_QUICConnectionId(this->_connection_id, buf + *len, &n); - *len += n; - } +QUICPacketType +QUICStatelessResetPacketR::type() const +{ + return QUICPacketType::STATELESS_RESET; +} - QUICPacketNumber dst = 0; - size_t dst_len = this->_packet_number_len; - QUICPacket::encode_packet_number(dst, this->_packet_number, dst_len); - QUICTypeUtil::write_QUICPacketNumber(dst, dst_len, buf + *len, &n); - *len += n; +QUICPacketNumber +QUICStatelessResetPacketR::packet_number() const +{ + ink_assert(!"You should not need packet number of Stateless Reset Packet"); + return 0; +} - QUICTypeUtil::write_QUICPacketNumberLen(n, buf); +QUICConnectionId +QUICStatelessResetPacketR::destination_cid() const +{ + ink_assert(!"You should not need DCID of Stateless Reset Packet"); + return QUICConnectionId::ZERO(); } // -// QUICPacket +// QUICVersionNegotiationPacket // - -QUICPacket::QUICPacket() {} - -QUICPacket::QUICPacket(UDPConnection *udp_con, QUICPacketHeaderUPtr header, ats_unique_buf payload, size_t payload_len) - : _udp_con(udp_con), _header(std::move(header)), _payload(std::move(payload)), _payload_size(payload_len) +QUICVersionNegotiationPacket::QUICVersionNegotiationPacket(QUICConnectionId dcid, QUICConnectionId scid, + const QUICVersion versions[], int nversions) + : QUICLongHeaderPacket(0, dcid, scid, false, false, false), _versions(versions), _nversions(nversions) { } -QUICPacket::QUICPacket(QUICPacketHeaderUPtr header, ats_unique_buf payload, size_t payload_len, bool ack_eliciting, bool probing) - : _header(std::move(header)), - _payload(std::move(payload)), - _payload_size(payload_len), - _is_ack_eliciting(ack_eliciting), - _is_probing_packet(probing) +QUICPacketType +QUICVersionNegotiationPacket::type() const { + return QUICPacketType::VERSION_NEGOTIATION; } -QUICPacket::~QUICPacket() +QUICVersion +QUICVersionNegotiationPacket::version() const { - this->_header = nullptr; + return 0; } -const IpEndpoint & -QUICPacket::from() const +QUICPacketNumber +QUICVersionNegotiationPacket::packet_number() const { - return this->_header->from(); + ink_assert(!"You should not need packet number of Version Negotiation Packet"); + return 0; } -const IpEndpoint & -QUICPacket::to() const +uint16_t +QUICVersionNegotiationPacket::payload_length() const { - return this->_header->to(); + uint16_t size = 0; + + for (auto b = this->payload_block(); b; b = b->next) { + size += b->size(); + } + + return size; } -UDPConnection * -QUICPacket::udp_con() const +Ptr +QUICVersionNegotiationPacket::header_block() const { - return this->_udp_con; + Ptr block; + size_t written_len = 0; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); + + // Common Long Header + written_len += this->_write_common_header(buf + written_len); + + // Overwrite the first byte + buf[0] = 0x80 | rand(); + + block->fill(written_len); + + return block; } -/** - * When packet is "Short Header Packet", QUICPacket::type() will return 1-RTT Protected (key phase 0) - * or 1-RTT Protected (key phase 1) - */ -QUICPacketType -QUICPacket::type() const +Ptr +QUICVersionNegotiationPacket::payload_block() const { - return this->_header->type(); + Ptr block; + uint8_t *buf; + size_t written_len = 0; + size_t n; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(sizeof(QUICVersion) * (this->_nversions + 1), BUFFER_SIZE_INDEX_32K)); + buf = reinterpret_cast(block->start()); + + for (auto i = 0; i < this->_nversions; ++i) { + QUICTypeUtil::write_QUICVersion(*(this->_versions + i), buf + written_len, &n); + written_len += n; + } + + // [draft-18] 6.3. Using Reserved Versions + // To help ensure this, a server SHOULD include a reserved version (see Section 15) while generating a + // Version Negotiation packet. + QUICTypeUtil::write_QUICVersion(QUIC_EXERCISE_VERSION, buf + written_len, &n); + written_len += n; + + block->fill(written_len); + + return block; } -QUICConnectionId -QUICPacket::destination_cid() const +const QUICVersion * +QUICVersionNegotiationPacket::versions() const { - return this->_header->destination_cid(); + return this->_versions; } -QUICConnectionId -QUICPacket::source_cid() const +int +QUICVersionNegotiationPacket::nversions() const { - return this->_header->source_cid(); + return this->_nversions; } -QUICPacketNumber -QUICPacket::packet_number() const +// +// QUICVersionNegotiationPacketR +// +QUICVersionNegotiationPacketR::QUICVersionNegotiationPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, + Ptr blocks) + : QUICLongHeaderPacketR(udp_con, from, to, blocks) { - return this->_header->packet_number(); + size_t len = 0; + for (auto b = blocks; b; b = b->next) { + len += b->size(); + } + + Ptr concatinated_block = make_ptr(new_IOBufferBlock()); + concatinated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + concatinated_block->fill(len); + + uint8_t *raw_buf = reinterpret_cast(concatinated_block->start()); + + size_t copied_len = 0; + for (auto b = blocks; b; b = b->next) { + memcpy(raw_buf + copied_len, b->start(), b->size()); + copied_len += b->size(); + } + + uint8_t dcil = 0; + uint8_t scil = 0; + QUICInvariants::dcil(dcil, raw_buf, len); + QUICInvariants::scil(scil, raw_buf, len); + + size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; + this->_dcid = {raw_buf + offset, dcil}; + offset += dcil + 1; + this->_scid = {raw_buf + offset, scil}; + offset += scil; + + this->_versions = raw_buf + offset; + this->_nversions = (len - offset) / sizeof(QUICVersion); + + this->_header_block = concatinated_block->clone(); + this->_header_block->_end = this->_header_block->_start + offset; + this->_header_block->next = nullptr; + this->_payload_block = concatinated_block->clone(); + this->_payload_block->_start = this->_payload_block->_start + offset; } -bool -QUICPacket::is_crypto_packet() const +QUICPacketType +QUICVersionNegotiationPacketR::type() const { - return this->_header->is_crypto_packet(); + return QUICPacketType::VERSION_NEGOTIATION; } -const QUICPacketHeader & -QUICPacket::header() const +QUICPacketNumber +QUICVersionNegotiationPacketR::packet_number() const { - return *this->_header; + ink_assert(!"You should not need packet number of Version Negotiation Packet"); + return 0; } -const uint8_t * -QUICPacket::payload() const +QUICConnectionId +QUICVersionNegotiationPacketR::destination_cid() const { - return this->_payload.get(); + return this->_dcid; } -QUICVersion -QUICPacket::version() const +Ptr +QUICVersionNegotiationPacketR::header_block() const { - return this->_header->version(); + return this->_header_block; } -bool -QUICPacket::is_ack_eliciting() const +Ptr +QUICVersionNegotiationPacketR::payload_block() const { - return this->_is_ack_eliciting; + return this->_payload_block; } -bool -QUICPacket::is_probing_packet() const +const QUICVersion +QUICVersionNegotiationPacketR::supported_version(uint8_t index) const { - return this->_is_probing_packet; + return QUICTypeUtil::read_QUICVersion(this->_versions + sizeof(QUICVersion) * index); } -uint16_t -QUICPacket::size() const +int +QUICVersionNegotiationPacketR::nversions() const { - // This includes not only header size and payload size but also AEAD tag length - uint16_t size = this->_header->packet_size(); - if (size == 0) { - size = this->header_size() + this->payload_length(); - } - return size; + return this->_nversions; } -uint16_t -QUICPacket::header_size() const +// +// QUICInitialPacket +// +QUICInitialPacket::QUICInitialPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t token_len, + ats_unique_buf token, size_t length, QUICPacketNumber packet_number, bool ack_eliciting, + bool probing, bool crypto) + : QUICLongHeaderPacket(version, dcid, scid, ack_eliciting, probing, crypto), + _token_len(token_len), + _token(std::move(token)), + _packet_number(packet_number) { - return this->_header->size(); } -uint16_t -QUICPacket::payload_length() const +QUICPacketType +QUICInitialPacket::type() const { - return this->_payload_size; + return QUICPacketType::INITIAL; } QUICKeyPhase -QUICPacket::key_phase() const +QUICInitialPacket::key_phase() const { - return this->_header->key_phase(); + return QUICKeyPhase::INITIAL; } -void -QUICPacket::store(uint8_t *buf, size_t *len) const +QUICPacketNumber +QUICInitialPacket::packet_number() const { - memcpy(buf, this->_header->buf(), this->_header->size()); - memcpy(buf + this->_header->size(), this->payload(), this->payload_length()); - *len = this->_header->size() + this->payload_length(); + return this->_packet_number; } -uint8_t -QUICPacket::calc_packet_number_len(QUICPacketNumber num, QUICPacketNumber base) +Ptr +QUICInitialPacket::header_block() const { - uint64_t d = (num - base) * 2; - uint8_t len = 0; + Ptr block; + size_t written_len = 0; + size_t n; - if (d > 0xFFFFFF) { - len = 4; - } else if (d > 0xFFFF) { - len = 3; - } else if (d > 0xFF) { - len = 2; + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); + + // Common Long Header + written_len += this->_write_common_header(buf + written_len); + + // Token Length + QUICIntUtil::write_QUICVariableInt(this->_token_len, buf + written_len, &n); + written_len += n; + + // Token + memcpy(buf + written_len, this->_token.get(), this->_token_len); + written_len += this->_token_len; + + QUICPacketNumber pn = 0; + size_t pn_len = 4; + QUICPacket::encode_packet_number(pn, this->_packet_number, pn_len); + + if (pn > 0x7FFFFF) { + pn_len = 4; + } else if (pn > 0x7FFF) { + pn_len = 3; + } else if (pn > 0x7F) { + pn_len = 2; } else { - len = 1; + pn_len = 1; } - return len; + // PN Len + QUICTypeUtil::write_QUICPacketNumberLen(pn_len, buf); + + // Length + QUICIntUtil::write_QUICVariableInt(pn_len + this->_payload_length, buf + written_len, &n); + written_len += n; + + // PN Field + QUICTypeUtil::write_QUICPacketNumber(pn, pn_len, buf + written_len, &n); + written_len += n; + + block->fill(written_len); + + return block; +} + +Ptr +QUICInitialPacket::payload_block() const +{ + return this->_payload_block; +} + +void +QUICInitialPacket::attach_payload(Ptr payload, bool unprotected) +{ + this->_payload_block = payload; + this->_payload_length = 0; + Ptr tmp = payload; + while (tmp) { + this->_payload_length += tmp->size(); + tmp = tmp->next; + } + if (unprotected) { + this->_payload_length += aead_tag_len; + } +} + +// +// QUICInitialPacketR +// +QUICInitialPacketR::QUICInitialPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number) + : QUICLongHeaderPacketR(udp_con, from, to, blocks) +{ + size_t len = 0; + for (auto b = blocks; b; b = b->next) { + len += b->size(); + } + + Ptr concatinated_block = make_ptr(new_IOBufferBlock()); + concatinated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + concatinated_block->fill(len); + + uint8_t *raw_buf = reinterpret_cast(concatinated_block->start()); + + size_t copied_len = 0; + for (auto b = blocks; b; b = b->next) { + memcpy(raw_buf + copied_len, b->start(), b->size()); + copied_len += b->size(); + } + + uint8_t dcil = 0; + uint8_t scil = 0; + QUICInvariants::dcil(dcil, raw_buf, len); + QUICInvariants::scil(scil, raw_buf, len); + + size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; + this->_dcid = {raw_buf + offset, dcil}; + offset += dcil + 1; + this->_scid = {raw_buf + offset, scil}; + offset += scil; + + // Token Length Field + uint64_t token_len = QUICIntUtil::read_QUICVariableInt(raw_buf + offset, len - offset); + offset += QUICVariableInt::size(raw_buf + offset); + + // Token Field + if (token_len) { + this->_token = new QUICAddressValidationToken(raw_buf + offset, token_len); + offset += token_len; + } else { + this->_token = new QUICAddressValidationToken(nullptr, 0); + } + + // Length Field + offset += QUICVariableInt::size(raw_buf + offset); + + // PN Field + int pn_len = QUICTypeUtil::read_QUICPacketNumberLen(raw_buf); + QUICPacket::decode_packet_number(this->_packet_number, QUICTypeUtil::read_QUICPacketNumber(raw_buf + offset, pn_len), pn_len, + base_packet_number); + offset += pn_len; + + this->_header_block = concatinated_block->clone(); + this->_header_block->_end = this->_header_block->_start + offset; + this->_header_block->next = nullptr; + this->_payload_block = concatinated_block->clone(); + this->_payload_block->_start = this->_payload_block->_start + offset; +} + +QUICInitialPacketR::~QUICInitialPacketR() +{ + delete this->_token; +} + +QUICPacketType +QUICInitialPacketR::type() const +{ + return QUICPacketType::INITIAL; +} + +QUICPacketNumber +QUICInitialPacketR::packet_number() const +{ + return this->_packet_number; +} + +QUICKeyPhase +QUICInitialPacketR::key_phase() const +{ + return QUICKeyPhase::INITIAL; +} + +Ptr +QUICInitialPacketR::header_block() const +{ + return this->_header_block; +} + +Ptr +QUICInitialPacketR::payload_block() const +{ + return this->_payload_block; +} + +void +QUICInitialPacketR::attach_payload(Ptr payload, bool unprotected) +{ + this->_payload_block = payload; +} + +const QUICAddressValidationToken & +QUICInitialPacketR::token() const +{ + return *(this->_token); } bool -QUICPacket::encode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len) +QUICInitialPacketR::token_length(size_t &token_length, uint8_t &field_len, size_t &token_length_filed_offset, const uint8_t *packet, + size_t packet_len) { - uint64_t mask = 0; - switch (len) { - case 1: - mask = 0xFF; - break; - case 2: - mask = 0xFFFF; - break; - case 3: - mask = 0xFFFFFF; - break; - case 4: - mask = 0xFFFFFFFF; - break; - default: - ink_assert(!"len must be 1, 2, or 4"); + QUICPacketType type = QUICPacketType::UNINITIALIZED; + QUICPacketR::type(type, packet, packet_len); + + ink_assert(type == QUICPacketType::INITIAL); + + uint8_t dcil, scil; + QUICInvariants::dcil(dcil, packet, packet_len); + QUICInvariants::scil(scil, packet, packet_len); + + token_length_filed_offset = LONG_HDR_OFFSET_CONNECTION_ID + dcil + 1 + scil; + if (token_length_filed_offset >= packet_len) { return false; } - dst = src & mask; + + token_length = QUICIntUtil::read_QUICVariableInt(packet + token_length_filed_offset, packet_len - token_length_filed_offset); + field_len = QUICVariableInt::size(packet + token_length_filed_offset); return true; } -bool -QUICPacket::decode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len, QUICPacketNumber largest_acked) +// +// QUICZeroRttPacket +// +QUICZeroRttPacket::QUICZeroRttPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t length, + QUICPacketNumber packet_number, bool ack_eliciting, bool probing) + : QUICLongHeaderPacket(version, dcid, scid, ack_eliciting, probing, false), _packet_number(packet_number) { - ink_assert(len == 1 || len == 2 || len == 3 || len == 4); +} - uint64_t maximum_diff = 0; - switch (len) { - case 1: - maximum_diff = 0x100; - break; - case 2: - maximum_diff = 0x10000; - break; - case 3: - maximum_diff = 0x1000000; - break; - case 4: - maximum_diff = 0x100000000; - break; - default: - ink_assert(!"len must be 1, 2, 3 or 4"); +QUICPacketType +QUICZeroRttPacket::type() const +{ + return QUICPacketType::ZERO_RTT_PROTECTED; +} + +QUICKeyPhase +QUICZeroRttPacket::key_phase() const +{ + return QUICKeyPhase::ZERO_RTT; +} + +QUICPacketNumber +QUICZeroRttPacket::packet_number() const +{ + return this->_packet_number; +} + +Ptr +QUICZeroRttPacket::header_block() const +{ + Ptr block; + size_t written_len = 0; + size_t n; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); + + // Common Long Header + written_len += this->_write_common_header(buf + written_len); + + QUICPacketNumber pn = 0; + size_t pn_len = 4; + QUICPacket::encode_packet_number(pn, this->_packet_number, pn_len); + + if (pn > 0x7FFFFF) { + pn_len = 4; + } else if (pn > 0x7FFF) { + pn_len = 3; + } else if (pn > 0x7F) { + pn_len = 2; + } else { + pn_len = 1; } - QUICPacketNumber base = largest_acked & (~(maximum_diff - 1)); - QUICPacketNumber candidate1 = base + src; - QUICPacketNumber candidate2 = base + src + maximum_diff; - QUICPacketNumber expected = largest_acked + 1; - if (((candidate1 > expected) ? (candidate1 - expected) : (expected - candidate1)) < - ((candidate2 > expected) ? (candidate2 - expected) : (expected - candidate2))) { - dst = candidate1; + // PN Len + QUICTypeUtil::write_QUICPacketNumberLen(pn_len, buf); + + // Length + QUICIntUtil::write_QUICVariableInt(pn_len + this->_payload_length, buf + written_len, &n); + written_len += n; + + // PN Field + QUICTypeUtil::write_QUICPacketNumber(pn, pn_len, buf + written_len, &n); + written_len += n; + + block->fill(written_len); + + return block; +} + +Ptr +QUICZeroRttPacket::payload_block() const +{ + return this->_payload_block; +} + +void +QUICZeroRttPacket::attach_payload(Ptr payload, bool unprotected) +{ + this->_payload_block = payload; + this->_payload_length = 0; + Ptr tmp = payload; + while (tmp) { + this->_payload_length += tmp->size(); + tmp = tmp->next; + } + if (unprotected) { + this->_payload_length += aead_tag_len; + } +} + +// +// QUICZeroRttPacketR +// +QUICZeroRttPacketR::QUICZeroRttPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number) + : QUICLongHeaderPacketR(udp_con, from, to, blocks) +{ + size_t len = 0; + for (auto b = blocks; b; b = b->next) { + len += b->size(); + } + + Ptr concatinated_block = make_ptr(new_IOBufferBlock()); + concatinated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + concatinated_block->fill(len); + + uint8_t *raw_buf = reinterpret_cast(concatinated_block->start()); + + size_t copied_len = 0; + for (auto b = blocks; b; b = b->next) { + memcpy(raw_buf + copied_len, b->start(), b->size()); + copied_len += b->size(); + } + + uint8_t dcil = 0; + uint8_t scil = 0; + QUICInvariants::dcil(dcil, raw_buf, len); + QUICInvariants::scil(scil, raw_buf, len); + + size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; + this->_dcid = {raw_buf + offset, dcil}; + offset += dcil + 1; + this->_scid = {raw_buf + offset, scil}; + offset += scil; + + // Length Field + offset += QUICVariableInt::size(raw_buf + offset); + + // PN Field + int pn_len = QUICTypeUtil::read_QUICPacketNumberLen(raw_buf); + QUICPacket::decode_packet_number(this->_packet_number, QUICTypeUtil::read_QUICPacketNumber(raw_buf + offset, pn_len), pn_len, + base_packet_number); + offset += pn_len; + + this->_header_block = concatinated_block->clone(); + this->_header_block->_end = this->_header_block->_start + offset; + this->_header_block->next = nullptr; + this->_payload_block = concatinated_block->clone(); + this->_payload_block->_start = this->_payload_block->_start + offset; +} + +QUICPacketType +QUICZeroRttPacketR::type() const +{ + return QUICPacketType::ZERO_RTT_PROTECTED; +} + +QUICPacketNumber +QUICZeroRttPacketR::packet_number() const +{ + return this->_packet_number; +} + +QUICKeyPhase +QUICZeroRttPacketR::key_phase() const +{ + return QUICKeyPhase::ZERO_RTT; +} + +Ptr +QUICZeroRttPacketR::header_block() const +{ + return this->_header_block; +} + +Ptr +QUICZeroRttPacketR::payload_block() const +{ + return this->_payload_block; +} + +void +QUICZeroRttPacketR::attach_payload(Ptr payload, bool unprotected) +{ + this->_payload_block = payload; +} + +// +// QUICHandshakePacket +// +QUICHandshakePacket::QUICHandshakePacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t length, + QUICPacketNumber packet_number, bool ack_eliciting, bool probing, bool crypto) + : QUICLongHeaderPacket(version, dcid, scid, ack_eliciting, probing, crypto), _packet_number(packet_number) +{ +} + +QUICPacketType +QUICHandshakePacket::type() const +{ + return QUICPacketType::HANDSHAKE; +} + +QUICKeyPhase +QUICHandshakePacket::key_phase() const +{ + return QUICKeyPhase::HANDSHAKE; +} + +QUICPacketNumber +QUICHandshakePacket::packet_number() const +{ + return this->_packet_number; +} + +Ptr +QUICHandshakePacket::header_block() const +{ + Ptr block; + size_t written_len = 0; + size_t n; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); + + // Common Long Header + written_len += this->_write_common_header(buf + written_len); + + QUICPacketNumber pn = 0; + size_t pn_len = 4; + QUICPacket::encode_packet_number(pn, this->_packet_number, pn_len); + + if (pn > 0x7FFFFF) { + pn_len = 4; + } else if (pn > 0x7FFF) { + pn_len = 3; + } else if (pn > 0x7F) { + pn_len = 2; } else { - dst = candidate2; + pn_len = 1; } - return true; + // PN Len + QUICTypeUtil::write_QUICPacketNumberLen(pn_len, buf); + + // Length + QUICIntUtil::write_QUICVariableInt(pn_len + this->_payload_length, buf + written_len, &n); + written_len += n; + + // PN Field + QUICTypeUtil::write_QUICPacketNumber(pn, pn_len, buf + written_len, &n); + written_len += n; + + block->fill(written_len); + + return block; +} + +Ptr +QUICHandshakePacket::payload_block() const +{ + return this->_payload_block; +} + +void +QUICHandshakePacket::attach_payload(Ptr payload, bool unprotected) +{ + this->_payload_block = payload; + this->_payload_length = 0; + Ptr tmp = payload; + while (tmp) { + this->_payload_length += tmp->size(); + tmp = tmp->next; + } + if (unprotected) { + this->_payload_length += aead_tag_len; + } +} + +// +// QUICHandshakePacketR +// +QUICHandshakePacketR::QUICHandshakePacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number) + : QUICLongHeaderPacketR(udp_con, from, to, blocks) +{ + size_t len = 0; + for (auto b = blocks; b; b = b->next) { + len += b->size(); + } + + Ptr concatinated_block = make_ptr(new_IOBufferBlock()); + concatinated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + concatinated_block->fill(len); + + uint8_t *raw_buf = reinterpret_cast(concatinated_block->start()); + + size_t copied_len = 0; + for (auto b = blocks; b; b = b->next) { + memcpy(raw_buf + copied_len, b->start(), b->size()); + copied_len += b->size(); + } + + uint8_t dcil = 0; + uint8_t scil = 0; + QUICInvariants::dcil(dcil, raw_buf, len); + QUICInvariants::scil(scil, raw_buf, len); + + size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; + this->_dcid = {raw_buf + offset, dcil}; + offset += dcil + 1; + this->_scid = {raw_buf + offset, scil}; + offset += scil; + + // Length Field + offset += QUICVariableInt::size(raw_buf + offset); + + // PN Field + int pn_len = QUICTypeUtil::read_QUICPacketNumberLen(raw_buf); + QUICPacket::decode_packet_number(this->_packet_number, QUICTypeUtil::read_QUICPacketNumber(raw_buf + offset, pn_len), pn_len, + base_packet_number); + offset += pn_len; + + this->_header_block = concatinated_block->clone(); + this->_header_block->_end = this->_header_block->_start + offset; + this->_header_block->next = nullptr; + this->_payload_block = concatinated_block->clone(); + this->_payload_block->_start = this->_payload_block->_start + offset; +} + +QUICPacketType +QUICHandshakePacketR::type() const +{ + return QUICPacketType::HANDSHAKE; +} + +QUICKeyPhase +QUICHandshakePacketR::key_phase() const +{ + return QUICKeyPhase::HANDSHAKE; +} + +QUICPacketNumber +QUICHandshakePacketR::packet_number() const +{ + return this->_packet_number; +} + +Ptr +QUICHandshakePacketR::header_block() const +{ + return this->_header_block; +} + +Ptr +QUICHandshakePacketR::payload_block() const +{ + return this->_payload_block; +} + +void +QUICHandshakePacketR::attach_payload(Ptr payload, bool unprotected) +{ + this->_payload_block = payload; +} + +// +// QUICRetryPacket +// +QUICRetryPacket::QUICRetryPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, QUICRetryToken &token) + : QUICLongHeaderPacket(version, dcid, scid, false, false, false), _token(token) +{ +} + +QUICPacketType +QUICRetryPacket::type() const +{ + return QUICPacketType::RETRY; +} + +QUICPacketNumber +QUICRetryPacket::packet_number() const +{ + ink_assert(!"You should not need packet number of Retry Packet"); + return 0; +} + +uint16_t +QUICRetryPacket::payload_length() const +{ + uint16_t size = 0; + + for (auto b = this->payload_block(); b; b = b->next) { + size += b->size(); + } + + return size; +} + +Ptr +QUICRetryPacket::header_block() const +{ + Ptr block; + size_t written_len = 0; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); + uint8_t *buf = reinterpret_cast(block->start()); + + // Common Long Header + written_len += this->_write_common_header(buf + written_len); + + block->fill(written_len); + + return block; +} + +Ptr +QUICRetryPacket::payload_block() const +{ + Ptr block; + uint8_t *buf; + size_t written_len = 0; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(QUICConnectionId::MAX_LENGTH + this->_token.length() + QUICRetryIntegrityTag::LEN, + BUFFER_SIZE_INDEX_32K)); + buf = reinterpret_cast(block->start()); + + // Retry Token + memcpy(buf + written_len, this->_token.buf(), this->_token.length()); + written_len += this->_token.length(); + block->fill(written_len); + + // Retry Integrity Tag + QUICRetryIntegrityTag::compute(buf + written_len, this->_token.original_dcid(), this->header_block(), block); + written_len += QUICRetryIntegrityTag::LEN; + block->fill(QUICRetryIntegrityTag::LEN); + + return block; +} + +const QUICRetryToken & +QUICRetryPacket::token() const +{ + return this->_token; +} + +// +// QUICRetryPacketR +// +QUICRetryPacketR::QUICRetryPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks) + : QUICLongHeaderPacketR(udp_con, from, to, blocks) +{ + size_t len = 0; + for (auto b = blocks; b; b = b->next) { + len += b->size(); + } + + Ptr concatinated_block = make_ptr(new_IOBufferBlock()); + concatinated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + concatinated_block->fill(len); + + uint8_t *raw_buf = reinterpret_cast(concatinated_block->start()); + + size_t copied_len = 0; + for (auto b = blocks; b; b = b->next) { + memcpy(raw_buf + copied_len, b->start(), b->size()); + copied_len += b->size(); + } + + uint8_t dcil = 0; + uint8_t scil = 0; + QUICInvariants::dcil(dcil, raw_buf, len); + QUICInvariants::scil(scil, raw_buf, len); + + size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; + this->_dcid = {raw_buf + offset, dcil}; + offset += dcil + 1; + this->_scid = {raw_buf + offset, scil}; + offset += scil; + + // Retry Token field + this->_token = new QUICRetryToken(raw_buf + offset, len - offset - QUICRetryIntegrityTag::LEN); + offset += this->_token->length(); + + // Retry Integrity Tag + memcpy(this->_integrity_tag, raw_buf + offset, QUICRetryIntegrityTag::LEN); + + this->_header_block = concatinated_block->clone(); + this->_header_block->_end = this->_header_block->_start + offset; + this->_header_block->next = nullptr; + this->_payload_block = concatinated_block->clone(); + this->_payload_block->_start = this->_payload_block->_start + offset; + this->_payload_block_without_tag = this->_payload_block->clone(); + this->_payload_block_without_tag->_end = this->_payload_block_without_tag->_end - QUICRetryIntegrityTag::LEN; +} + +QUICRetryPacketR::~QUICRetryPacketR() +{ + delete this->_token; +} + +Ptr +QUICRetryPacketR::header_block() const +{ + return this->_header_block; +} + +Ptr +QUICRetryPacketR::payload_block() const +{ + return this->_payload_block; +} + +QUICPacketType +QUICRetryPacketR::type() const +{ + return QUICPacketType::RETRY; +} + +QUICPacketNumber +QUICRetryPacketR::packet_number() const +{ + return 0; +} + +const QUICAddressValidationToken & +QUICRetryPacketR::token() const +{ + return *(this->_token); +} + +bool +QUICRetryPacketR::has_valid_tag(QUICConnectionId &odcid) const +{ + uint8_t tag_computed[QUICRetryIntegrityTag::LEN]; + QUICRetryIntegrityTag::compute(tag_computed, odcid, this->_header_block, this->_payload_block_without_tag); + + return memcmp(this->_integrity_tag, tag_computed, QUICRetryIntegrityTag::LEN) == 0; } diff --git a/iocore/net/quic/QUICPacket.h b/iocore/net/quic/QUICPacket.h index 5fc834f45c1..9d20b234fcb 100644 --- a/iocore/net/quic/QUICPacket.h +++ b/iocore/net/quic/QUICPacket.h @@ -27,394 +27,524 @@ #include #include -#include "tscore/List.h" #include "I_IOBuffer.h" #include "QUICTypes.h" -#include "QUICHandshakeProtocol.h" -#include "QUICPacketHeaderProtector.h" -#include "QUICFrame.h" +#include "QUICRetryIntegrityTag.h" #define QUIC_FIELD_OFFSET_CONNECTION_ID 1 #define QUIC_FIELD_OFFSET_PACKET_NUMBER 4 #define QUIC_FIELD_OFFSET_PAYLOAD 5 class UDPConnection; -class QUICPacketHeader; -class QUICPacket; -class QUICPacketLongHeader; -class QUICPacketShortHeader; -extern ClassAllocator quicPacketAllocator; -extern ClassAllocator quicPacketLongHeaderAllocator; -extern ClassAllocator quicPacketShortHeaderAllocator; - -using QUICPacketHeaderDeleterFunc = void (*)(QUICPacketHeader *p); -using QUICPacketHeaderUPtr = std::unique_ptr; - -class QUICPacketHeader +class QUICPacket { public: - QUICPacketHeader(const IpEndpoint from, const IpEndpoint to, ats_unique_buf buf, size_t len, QUICPacketNumber base) - : _from(from), _to(to), _buf(std::move(buf)), _buf_len(len), _base_packet_number(base) - { - } - ~QUICPacketHeader() {} - const uint8_t *buf(); + static constexpr int MAX_INSTANCE_SIZE = 1024; - virtual bool is_crypto_packet() const; + // Token field in Initial packet could be very long. + static constexpr size_t MAX_PACKET_HEADER_LEN = 256; - const IpEndpoint &from() const; - const IpEndpoint &to() const; + /** + * Creates a QUICPacket for sending packets + */ + QUICPacket(bool ack_eliciting, bool probing); - virtual QUICPacketType type() const = 0; + virtual ~QUICPacket(); - /* - * Returns a connection id - */ + virtual QUICPacketType type() const = 0; virtual QUICConnectionId destination_cid() const = 0; - virtual QUICConnectionId source_cid() const = 0; + virtual QUICPacketNumber packet_number() const = 0; + bool is_ack_eliciting() const; + bool is_probing_packet() const; - virtual QUICPacketNumber packet_number() const = 0; - virtual QUICVersion version() const = 0; + // TODO These two should be pure virtual + virtual Ptr + header_block() const + { + return Ptr(); + }; + virtual Ptr + payload_block() const + { + return Ptr(); + }; /* - * Returns a pointer for the payload + * Size of whole QUIC packet (header + payload + integrity check) */ - virtual const uint8_t *payload() const = 0; + virtual uint16_t size() const; /* - * Returns its payload size based on header length and buffer size that is specified to the constructo. + * Size of header */ - uint16_t payload_size() const; + virtual uint16_t header_size() const; /* - * Returns its header size + * Length of payload (payload + integrity check if exists) */ - virtual uint16_t size() const = 0; + virtual uint16_t payload_length() const; - /* - * Returns its packet size + /** + * Key phase */ - uint16_t packet_size() const; + virtual QUICKeyPhase key_phase() const; - /* - * Returns a key phase - */ - virtual QUICKeyPhase key_phase() const = 0; + // FIXME Remove this and use IOBufferBlock instead + void store(uint8_t *buf, size_t *len) const; - /* - * Stores serialized header - * - * The serialized data doesn't contain a payload part even if it was created with a buffer that contains payload data. - */ - virtual void store(uint8_t *buf, size_t *len) const = 0; + /***** STATIC MEMBERS *****/ - QUICPacketHeaderUPtr clone() const; + static uint8_t calc_packet_number_len(QUICPacketNumber num, QUICPacketNumber base); + static bool encode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len); + static bool decode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len, QUICPacketNumber largest_acked); - virtual bool has_version() const = 0; - virtual bool is_valid() const = 0; +protected: + QUICPacket(); - /***** STATIC members *****/ +private: + bool _is_ack_eliciting = false; + bool _is_probing_packet = false; +}; - /* - * Load data from a buffer and create a QUICPacketHeader - * - * This creates either a QUICPacketShortHeader or a QUICPacketLongHeader. +class QUICPacketR : public QUICPacket +{ +public: + /** + * Creates a QUICPacket for receiving packets */ - static QUICPacketHeaderUPtr load(const IpEndpoint from, const IpEndpoint to, ats_unique_buf buf, size_t len, - QUICPacketNumber base); + QUICPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to); - /* - * Build a QUICPacketHeader - * - * This creates a QUICPacketLongHeader. - */ - static QUICPacketHeaderUPtr build(QUICPacketType type, QUICKeyPhase key_phase, QUICConnectionId destination_cid, - QUICConnectionId source_cid, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, QUICVersion version, bool crypto, ats_unique_buf payload, - size_t len); + virtual QUICPacketType type() const override = 0; - /* - * Build a QUICPacketHeader - * - * This creates a QUICPacketLongHeader for INITIAL packet - */ - static QUICPacketHeaderUPtr build(QUICPacketType type, QUICKeyPhase key_phase, QUICConnectionId destination_cid, - QUICConnectionId source_cid, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, QUICVersion version, bool crypto, ats_unique_buf payload, - size_t len, ats_unique_buf token, size_t token_len); + UDPConnection *udp_con() const; + virtual const IpEndpoint &from() const; + virtual const IpEndpoint &to() const; - /* - * Build a QUICPacketHeader - * - * This creates a QUICPacketLongHeader for RETRY packet - */ - static QUICPacketHeaderUPtr build(QUICPacketType type, QUICKeyPhase key_phase, QUICVersion version, - QUICConnectionId destination_cid, QUICConnectionId source_cid, QUICConnectionId original_dcid, - ats_unique_buf retry_token, size_t retry_token_len); + static bool read_essential_info(Ptr block, QUICPacketType &type, QUICVersion &version, QUICConnectionId &dcid, + QUICConnectionId &scid, QUICPacketNumber &packet_number, QUICPacketNumber base_packet_number, + QUICKeyPhase &key_phase); + static bool type(QUICPacketType &type, const uint8_t *packet, size_t packet_len); - /* - * Build a QUICPacketHeader - * - * This creates a QUICPacketShortHeader that contains a ConnectionID. - */ - static QUICPacketHeaderUPtr build(QUICPacketType type, QUICKeyPhase key_phase, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len); +protected: + Ptr _header_block; + Ptr _payload_block; - /* - * Build a QUICPacketHeader - * - * This creates a QUICPacketShortHeader that doesn't contain a ConnectionID (Stateless Reset Packet). +private: + UDPConnection *_udp_con = nullptr; + const IpEndpoint _from = {}; + const IpEndpoint _to = {}; +}; + +using QUICPacketDeleterFunc = void (*)(QUICPacket *p); +using QUICPacketUPtr = std::unique_ptr; + +class QUICPacketDeleter +{ +public: + static void + delete_null_packet(QUICPacket *packet) + { + ink_assert(packet == nullptr); + } + + static void + delete_dont_free(QUICPacket *packet) + { + packet->~QUICPacket(); + } + + static void + delete_packet_new(QUICPacket *packet) + { + delete packet; + } +}; + +class QUICLongHeaderPacket : public QUICPacket +{ +public: + /** + * For sending packet */ - static QUICPacketHeaderUPtr build(QUICPacketType type, QUICKeyPhase key_phase, QUICConnectionId connection_id, - QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, ats_unique_buf payload, - size_t len); + QUICLongHeaderPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, bool ack_eliciting, bool probing, + bool crypto); + + QUICConnectionId source_cid() const; + + QUICConnectionId destination_cid() const override; + uint16_t payload_length() const override; + virtual QUICVersion version() const; + virtual bool is_crypto_packet() const; protected: - QUICPacketHeader(){}; - QUICPacketHeader(QUICPacketType type, QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, bool has_version, - QUICVersion version, ats_unique_buf payload, size_t payload_length, QUICKeyPhase key_phase) - : _payload(std::move(payload)), - _type(type), - _key_phase(key_phase), - _packet_number(packet_number), - _base_packet_number(base_packet_number), - _version(version), - _payload_length(payload_length), - _has_version(has_version){}; - // Token field in Initial packet could be very long. - static constexpr size_t MAX_PACKET_HEADER_LEN = 256; + size_t _write_common_header(uint8_t *buf) const; - const IpEndpoint _from = {}; - const IpEndpoint _to = {}; - - // These two are used only if the instance was created with a buffer - ats_unique_buf _buf = {nullptr}; - size_t _buf_len = 0; - - // These are used only if the instance was created without a buffer - uint8_t _serialized[MAX_PACKET_HEADER_LEN]; - ats_unique_buf _payload = ats_unique_buf(nullptr); - QUICPacketType _type = QUICPacketType::UNINITIALIZED; - QUICKeyPhase _key_phase = QUICKeyPhase::INITIAL; - QUICConnectionId _connection_id = QUICConnectionId::ZERO(); - QUICPacketNumber _packet_number = 0; - QUICPacketNumber _base_packet_number = 0; - QUICVersion _version = 0; - size_t _payload_length = 0; - bool _has_version = false; + Ptr _payload_block; + size_t _payload_length = 0; + +private: + QUICVersion _version; + QUICConnectionId _dcid; + QUICConnectionId _scid; + + bool _is_crypto_packet; }; -class QUICPacketLongHeader : public QUICPacketHeader +class QUICLongHeaderPacketR : public QUICPacketR { public: - QUICPacketLongHeader() : QUICPacketHeader(){}; - virtual ~QUICPacketLongHeader(){}; - QUICPacketLongHeader(const IpEndpoint from, const IpEndpoint to, ats_unique_buf buf, size_t len, QUICPacketNumber base); - QUICPacketLongHeader(QUICPacketType type, QUICKeyPhase key_phase, const QUICConnectionId &destination_cid, - const QUICConnectionId &source_cid, QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, - QUICVersion version, bool crypto, ats_unique_buf buf, size_t len, - ats_unique_buf token = ats_unique_buf(nullptr), size_t token_len = 0); - QUICPacketLongHeader(QUICPacketType type, QUICKeyPhase key_phase, QUICVersion version, const QUICConnectionId &destination_cid, - const QUICConnectionId &source_cid, const QUICConnectionId &original_dcid, ats_unique_buf retry_token, - size_t retry_token_len); + /** + * For receiving packet + */ + QUICLongHeaderPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks); + + virtual ~QUICLongHeaderPacketR(){}; - QUICPacketType type() const override; QUICConnectionId destination_cid() const override; - QUICConnectionId source_cid() const override; - QUICConnectionId original_dcid() const; - QUICPacketNumber packet_number() const override; - bool has_version() const override; - bool is_valid() const override; - bool is_crypto_packet() const override; - QUICVersion version() const override; - const uint8_t *payload() const override; - const uint8_t *token() const; - size_t token_len() const; - QUICKeyPhase key_phase() const override; - uint16_t size() const override; - void store(uint8_t *buf, size_t *len) const override; + QUICConnectionId source_cid() const; + virtual QUICVersion version() const; static bool type(QUICPacketType &type, const uint8_t *packet, size_t packet_len); static bool version(QUICVersion &version, const uint8_t *packet, size_t packet_len); - static bool dcil(uint8_t &dcil, const uint8_t *packet, size_t packet_len); - static bool scil(uint8_t &scil, const uint8_t *packet, size_t packet_len); - static bool token_length(size_t &token_length, uint8_t &field_len, size_t &token_length_filed_offset, const uint8_t *packet, - size_t packet_len); + static bool key_phase(QUICKeyPhase &key_phase, const uint8_t *packet, size_t packet_len); static bool length(size_t &length, uint8_t &length_field_len, size_t &length_field_offset, const uint8_t *packet, size_t packet_len); - static bool key_phase(QUICKeyPhase &key_phase, const uint8_t *packet, size_t packet_len); + static bool packet_length(size_t &packet_len, const uint8_t *buf, size_t buf_len); static bool packet_number_offset(size_t &pn_offset, const uint8_t *packet, size_t packet_len); - static bool packet_length(size_t &length, const uint8_t *buf, size_t buf_len); -private: - QUICConnectionId _destination_cid = QUICConnectionId::ZERO(); - QUICConnectionId _source_cid = QUICConnectionId::ZERO(); - QUICConnectionId _original_dcid = QUICConnectionId::ZERO(); //< RETRY packet only - size_t _token_len = 0; //< INITIAL packet only - size_t _token_offset = 0; //< INITIAL packet only - ats_unique_buf _token = ats_unique_buf(nullptr); //< INITIAL packet only - size_t _payload_offset = 0; - bool _is_crypto_packet = false; +protected: + QUICVersion _version; + QUICConnectionId _scid; + QUICConnectionId _dcid; }; -class QUICPacketShortHeader : public QUICPacketHeader +class QUICShortHeaderPacket : public QUICPacket { public: - QUICPacketShortHeader() : QUICPacketHeader(){}; - virtual ~QUICPacketShortHeader(){}; - QUICPacketShortHeader(const IpEndpoint from, const IpEndpoint to, ats_unique_buf buf, size_t len, QUICPacketNumber base); - QUICPacketShortHeader(QUICPacketType type, QUICKeyPhase key_phase, QUICPacketNumber packet_number, - QUICPacketNumber base_packet_number, ats_unique_buf buf, size_t len); - QUICPacketShortHeader(QUICPacketType type, QUICKeyPhase key_phase, const QUICConnectionId &connection_id, - QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, ats_unique_buf buf, size_t len); + /** + * For sending packet + */ + QUICShortHeaderPacket(QUICConnectionId dcid, QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, + QUICKeyPhase key_phase, bool ack_eliciting, bool probing); + QUICPacketType type() const override; - QUICConnectionId destination_cid() const override; - QUICConnectionId - source_cid() const override - { - return QUICConnectionId::ZERO(); - } + QUICKeyPhase key_phase() const override; QUICPacketNumber packet_number() const override; - bool has_version() const override; - bool is_valid() const override; - QUICVersion version() const override; - const uint8_t *payload() const override; + QUICConnectionId destination_cid() const override; + + uint16_t payload_length() const override; + Ptr header_block() const override; + Ptr payload_block() const override; + + void attach_payload(Ptr payload, bool unprotected); + +private: + QUICConnectionId _dcid; + QUICPacketNumber _packet_number; + QUICKeyPhase _key_phase; + int _packet_number_len; + + Ptr _payload_block; + size_t _payload_length; +}; + +class QUICShortHeaderPacketR : public QUICPacketR +{ +public: + /** + * For receiving packet + */ + QUICShortHeaderPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number); + + void attach_payload(Ptr payload, bool unprotected); + + QUICPacketType type() const override; QUICKeyPhase key_phase() const override; - uint16_t size() const override; - void store(uint8_t *buf, size_t *len) const override; + QUICPacketNumber packet_number() const override; + QUICConnectionId destination_cid() const override; + + Ptr header_block() const override; + Ptr payload_block() const override; - static bool key_phase(QUICKeyPhase &key_phase, const uint8_t *packet, size_t packet_len); static bool packet_number_offset(size_t &pn_offset, const uint8_t *packet, size_t packet_len, int dcil); private: + QUICKeyPhase _key_phase; + QUICPacketNumber _packet_number; int _packet_number_len; + QUICConnectionId _dcid; }; -class QUICPacketHeaderDeleter +class QUICStatelessResetPacket : public QUICPacket { public: - static void - delete_null_header(QUICPacketHeader *header) - { - ink_assert(header == nullptr); - } + /** + * For sending packet + */ + QUICStatelessResetPacket(QUICStatelessResetToken token, size_t maximum_size); - static void - delete_long_header(QUICPacketHeader *header) - { - QUICPacketLongHeader *long_header = dynamic_cast(header); - ink_assert(long_header != nullptr); - long_header->~QUICPacketLongHeader(); - quicPacketLongHeaderAllocator.free(long_header); - } + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + QUICConnectionId destination_cid() const override; - static void - delete_short_header(QUICPacketHeader *header) - { - QUICPacketShortHeader *short_header = dynamic_cast(header); - ink_assert(short_header != nullptr); - short_header->~QUICPacketShortHeader(); - quicPacketShortHeaderAllocator.free(short_header); - } + Ptr header_block() const override; + Ptr payload_block() const override; + + QUICStatelessResetToken token() const; + +private: + QUICStatelessResetToken _token; + size_t _maximum_size; }; -class QUICPacket +class QUICStatelessResetPacketR : public QUICPacketR { public: - QUICPacket(); + /** + * For receiving packet + */ + QUICStatelessResetPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks); - /* - * Creates a QUICPacket with a QUICPacketHeader and a buffer that contains payload - * - * This will be used for receiving packets. Therefore, it is expected that payload is already decrypted. - * However, QUICPacket class itself doesn't care about whether the payload is protected (encrypted) or not. + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + QUICConnectionId destination_cid() const override; +}; + +class QUICVersionNegotiationPacket : public QUICLongHeaderPacket +{ +public: + /** + * For sending packet */ - QUICPacket(UDPConnection *udp_con, QUICPacketHeaderUPtr header, ats_unique_buf payload, size_t payload_len); + QUICVersionNegotiationPacket(QUICConnectionId dcid, QUICConnectionId scid, const QUICVersion versions[], int nversions); - QUICPacket(QUICPacketHeaderUPtr header, ats_unique_buf payload, size_t payload_len, std::vector &frames); + QUICPacketType type() const override; + QUICVersion version() const override; + QUICPacketNumber packet_number() const override; + uint16_t payload_length() const override; - /* - * Creates a QUICPacket with a QUICPacketHeader, a buffer that contains payload and a flag that indicates whether the packet is - * ack_eliciting - * - * This will be used for sending packets. Therefore, it is expected that payload is already encrypted. - * However, QUICPacket class itself doesn't care about whether the payload is protected (encrypted) or not. + Ptr header_block() const override; + Ptr payload_block() const override; + + const QUICVersion *versions() const; + int nversions() const; + +private: + const QUICVersion *_versions; + int _nversions; +}; + +class QUICVersionNegotiationPacketR : public QUICLongHeaderPacketR +{ +public: + /** + * For receiving packet */ - QUICPacket(QUICPacketHeaderUPtr header, ats_unique_buf payload, size_t payload_len, bool ack_eliciting, bool probing); + QUICVersionNegotiationPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks); - QUICPacket(QUICPacketHeaderUPtr header, ats_unique_buf payload, size_t payload_len, bool ack_eliciting, bool probing, - std::vector &frames); + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + QUICConnectionId destination_cid() const override; - virtual ~QUICPacket(); + Ptr header_block() const override; + Ptr payload_block() const override; - UDPConnection *udp_con() const; - virtual const IpEndpoint &from() const; - virtual const IpEndpoint &to() const; - QUICPacketType type() const; - QUICConnectionId destination_cid() const; - QUICConnectionId source_cid() const; - QUICPacketNumber packet_number() const; - QUICVersion version() const; - const QUICPacketHeader &header() const; - const uint8_t *payload() const; - bool is_ack_eliciting() const; - bool is_crypto_packet() const; - bool is_probing_packet() const; + const QUICVersion supported_version(uint8_t index) const; + int nversions() const; - /* - * Size of whole QUIC packet (header + payload + integrity check) +private: + QUICConnectionId _dcid; + uint8_t *_versions; + int _nversions; +}; + +class QUICInitialPacket : public QUICLongHeaderPacket +{ +public: + /** + * For sending packet */ - uint16_t size() const; + QUICInitialPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t token_len, ats_unique_buf token, + size_t length, QUICPacketNumber packet_number, bool ack_eliciting, bool probing, bool crypto); - /* - * Size of header + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + QUICKeyPhase key_phase() const override; + + Ptr header_block() const override; + Ptr payload_block() const override; + void attach_payload(Ptr payload, bool unprotected); + +private: + size_t _token_len = 0; + ats_unique_buf _token = ats_unique_buf(nullptr); + QUICPacketNumber _packet_number; +}; + +class QUICInitialPacketR : public QUICLongHeaderPacketR +{ +public: + /** + * For receiving packet */ - uint16_t header_size() const; + QUICInitialPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number); + ~QUICInitialPacketR(); - /* - * Length of payload + Ptr header_block() const override; + Ptr payload_block() const override; + void attach_payload(Ptr payload, bool unprotected); + + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + QUICKeyPhase key_phase() const override; + + const QUICAddressValidationToken &token() const; + + static bool token_length(size_t &token_length, uint8_t &field_len, size_t &token_length_filed_offset, const uint8_t *packet, + size_t packet_len); + +protected: + Ptr _payload_block; + +private: + QUICPacketNumber _packet_number; + QUICAddressValidationToken *_token = nullptr; + + bool _parse(); +}; + +class QUICZeroRttPacket : public QUICLongHeaderPacket +{ +public: + /** + * For sending packet */ - uint16_t payload_length() const; + QUICZeroRttPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t length, + QUICPacketNumber packet_number, bool ack_eliciting, bool probing); - void store(uint8_t *buf, size_t *len) const; - QUICKeyPhase key_phase() const; + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + QUICKeyPhase key_phase() const override; - /***** STATIC MEMBERS *****/ + Ptr header_block() const override; + Ptr payload_block() const override; + void attach_payload(Ptr payload, bool unprotected); - static uint8_t calc_packet_number_len(QUICPacketNumber num, QUICPacketNumber base); - static bool encode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len); - static bool decode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len, QUICPacketNumber largest_acked); +private: + QUICPacketNumber _packet_number; +}; + +class QUICZeroRttPacketR : public QUICLongHeaderPacketR +{ +public: + /** + * For receiving packet + */ + QUICZeroRttPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number); - LINK(QUICPacket, link); + Ptr header_block() const override; + Ptr payload_block() const override; + void attach_payload(Ptr payload, bool unprotected); + + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + QUICKeyPhase key_phase() const override; private: - UDPConnection *_udp_con = nullptr; - QUICPacketHeaderUPtr _header = QUICPacketHeaderUPtr(nullptr, &QUICPacketHeaderDeleter::delete_null_header); - ats_unique_buf _payload = ats_unique_buf(nullptr); - size_t _payload_size = 0; - bool _is_ack_eliciting = false; - bool _is_probing_packet = false; + QUICPacketNumber _packet_number; }; -using QUICPacketDeleterFunc = void (*)(QUICPacket *p); -using QUICPacketUPtr = std::unique_ptr; +class QUICHandshakePacket : public QUICLongHeaderPacket +{ +public: + /** + * For sending packet + */ + QUICHandshakePacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t length, + QUICPacketNumber packet_number, bool ack_eliciting, bool probing, bool crypto); -class QUICPacketDeleter + QUICPacketType type() const override; + QUICKeyPhase key_phase() const override; + QUICPacketNumber packet_number() const override; + + Ptr header_block() const override; + Ptr payload_block() const override; + void attach_payload(Ptr payload, bool unprotected); + +private: + QUICPacketNumber _packet_number; +}; + +class QUICHandshakePacketR : public QUICLongHeaderPacketR { public: - // TODO Probably these methods should call destructor - static void - delete_null_packet(QUICPacket *packet) - { - ink_assert(packet == nullptr); - } + /** + * For receiving packet + */ + QUICHandshakePacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks, + QUICPacketNumber base_packet_number); - static void - delete_packet(QUICPacket *packet) - { - packet->~QUICPacket(); - quicPacketAllocator.free(packet); - } + Ptr header_block() const override; + Ptr payload_block() const override; + void attach_payload(Ptr payload, bool unprotected); + + QUICPacketType type() const override; + QUICKeyPhase key_phase() const override; + QUICPacketNumber packet_number() const override; + +private: + QUICPacketNumber _packet_number; +}; + +class QUICRetryPacket : public QUICLongHeaderPacket +{ +public: + /** + * For sending packet + */ + QUICRetryPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, QUICRetryToken &token); + + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + uint16_t payload_length() const override; + + Ptr header_block() const override; + Ptr payload_block() const override; + + const QUICRetryToken &token() const; + +private: + QUICRetryToken _token; + + bool _compute_retry_integrity_tag(uint8_t *out, QUICConnectionId odcid, Ptr header, + Ptr payload) const; +}; + +class QUICRetryPacketR : public QUICLongHeaderPacketR +{ +public: + /** + * For receiving packet + */ + QUICRetryPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr blocks); + ~QUICRetryPacketR(); + + Ptr header_block() const override; + Ptr payload_block() const override; + + QUICPacketType type() const override; + QUICPacketNumber packet_number() const override; + + const QUICAddressValidationToken &token() const; + bool has_valid_tag(QUICConnectionId &odcid) const; + +private: + QUICAddressValidationToken *_token = nullptr; + uint8_t _integrity_tag[QUICRetryIntegrityTag::LEN]; + Ptr _payload_block_without_tag; }; diff --git a/iocore/net/quic/QUICPacketFactory.cc b/iocore/net/quic/QUICPacketFactory.cc index ea82f4400c9..436c68306b1 100644 --- a/iocore/net/quic/QUICPacketFactory.cc +++ b/iocore/net/quic/QUICPacketFactory.cc @@ -62,282 +62,255 @@ QUICPacketFactory::create_null_packet() } QUICPacketUPtr -QUICPacketFactory::create(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, ats_unique_buf buf, size_t len, - QUICPacketNumber base_packet_number, QUICPacketCreationResult &result) +QUICPacketFactory::create(uint8_t *packet_buf, UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, ats_unique_buf buf, + size_t len, QUICPacketNumber base_packet_number, QUICPacketCreationResult &result) { - size_t max_plain_txt_len = 2048; - ats_unique_buf plain_txt = ats_unique_malloc(max_plain_txt_len); - size_t plain_txt_len = 0; - - QUICPacketHeaderUPtr header = QUICPacketHeader::load(from, to, std::move(buf), len, base_packet_number); - - QUICConnectionId dcid = header->destination_cid(); - QUICConnectionId scid = header->source_cid(); - QUICVDebug(scid, dcid, "Decrypting %s packet #%" PRIu64 " using %s", QUICDebugNames::packet_type(header->type()), - header->packet_number(), QUICDebugNames::key_phase(header->key_phase())); - - if (header->has_version() && !QUICTypeUtil::is_supported_version(header->version())) { - if (header->type() == QUICPacketType::VERSION_NEGOTIATION) { - // version of VN packet is 0x00000000 - // This packet is unprotected. Just copy the payload - result = QUICPacketCreationResult::SUCCESS; - memcpy(plain_txt.get(), header->payload(), header->payload_size()); - plain_txt_len = header->payload_size(); - } else { - // We can't decrypt packets that have unknown versions - // What we can use is invariant field of Long Header - version, dcid, and scid - result = QUICPacketCreationResult::UNSUPPORTED; - } - } else { - Ptr plain = make_ptr(new_IOBufferBlock()); - Ptr protected_ibb = make_ptr(new_IOBufferBlock()); - protected_ibb->set_internal(reinterpret_cast(const_cast(header->payload())), header->payload_size(), - BUFFER_SIZE_NOT_ALLOCATED); - Ptr header_ibb = make_ptr(new_IOBufferBlock()); - header_ibb->set_internal(reinterpret_cast(const_cast(header->buf())), header->size(), - BUFFER_SIZE_NOT_ALLOCATED); - - switch (header->type()) { - case QUICPacketType::STATELESS_RESET: - case QUICPacketType::RETRY: - // These packets are unprotected. Just copy the payload - memcpy(plain_txt.get(), header->payload(), header->payload_size()); - plain_txt_len = header->payload_size(); - result = QUICPacketCreationResult::SUCCESS; - break; - case QUICPacketType::PROTECTED: - if (this->_pp_key_info.is_decryption_key_available(header->key_phase())) { - plain = this->_pp_protector.unprotect(header_ibb, protected_ibb, header->packet_number(), header->key_phase()); - if (plain != nullptr) { - memcpy(plain_txt.get(), plain->buf(), plain->size()); - plain_txt_len = plain->size(); - result = QUICPacketCreationResult::SUCCESS; - } else { - result = QUICPacketCreationResult::FAILED; - } + QUICPacket *packet = nullptr; + + // FIXME This is temporal. Receive IOBufferBlock from the caller. + Ptr whole_data = make_ptr(new_IOBufferBlock()); + whole_data->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + memcpy(whole_data->start(), buf.get(), len); + whole_data->fill(len); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + + if (QUICPacketR::read_essential_info(whole_data, type, version, dcid, scid, packet_number, base_packet_number, key_phase)) { + QUICVDebug(scid, dcid, "Decrypting %s packet #%" PRIu64 " using %s", QUICDebugNames::packet_type(type), packet_number, + QUICDebugNames::key_phase(key_phase)); + + if (type != QUICPacketType::PROTECTED && !QUICTypeUtil::is_supported_version(version)) { + if (type == QUICPacketType::VERSION_NEGOTIATION) { + packet = new QUICVersionNegotiationPacketR(udp_con, from, to, whole_data); + result = QUICPacketCreationResult::SUCCESS; } else { - result = QUICPacketCreationResult::NOT_READY; + // We can't decrypt packets that have unknown versions + // What we can use is invariant field of Long Header - version, dcid, and scid + result = QUICPacketCreationResult::UNSUPPORTED; } - break; - case QUICPacketType::INITIAL: - if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::INITIAL)) { - if (QUICTypeUtil::is_supported_version(header->version())) { - plain = this->_pp_protector.unprotect(header_ibb, protected_ibb, header->packet_number(), header->key_phase()); + } else { + Ptr plain; + switch (type) { + case QUICPacketType::STATELESS_RESET: + packet = new (packet_buf) QUICStatelessResetPacketR(udp_con, from, to, whole_data); + result = QUICPacketCreationResult::SUCCESS; + break; + case QUICPacketType::RETRY: + packet = new (packet_buf) QUICRetryPacketR(udp_con, from, to, whole_data); + result = QUICPacketCreationResult::SUCCESS; + break; + case QUICPacketType::PROTECTED: + packet = new (packet_buf) QUICShortHeaderPacketR(udp_con, from, to, whole_data, base_packet_number); + if (this->_pp_key_info.is_decryption_key_available(packet->key_phase())) { + plain = this->_pp_protector.unprotect(packet->header_block(), packet->payload_block(), packet->packet_number(), + packet->key_phase()); if (plain != nullptr) { - memcpy(plain_txt.get(), plain->buf(), plain->size()); - plain_txt_len = plain->size(); - result = QUICPacketCreationResult::SUCCESS; + static_cast(packet)->attach_payload(plain, true); + result = QUICPacketCreationResult::SUCCESS; } else { result = QUICPacketCreationResult::FAILED; } } else { - result = QUICPacketCreationResult::SUCCESS; + result = QUICPacketCreationResult::NOT_READY; } - } else { - result = QUICPacketCreationResult::IGNORED; - } - break; - case QUICPacketType::HANDSHAKE: - if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::HANDSHAKE)) { - plain = this->_pp_protector.unprotect(header_ibb, protected_ibb, header->packet_number(), header->key_phase()); - if (plain != nullptr) { - memcpy(plain_txt.get(), plain->buf(), plain->size()); - plain_txt_len = plain->size(); - result = QUICPacketCreationResult::SUCCESS; + break; + case QUICPacketType::INITIAL: + packet = new (packet_buf) QUICInitialPacketR(udp_con, from, to, whole_data, base_packet_number); + if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::INITIAL)) { + plain = this->_pp_protector.unprotect(packet->header_block(), packet->payload_block(), packet->packet_number(), + packet->key_phase()); + if (plain != nullptr) { + static_cast(packet)->attach_payload(plain, true); + result = QUICPacketCreationResult::SUCCESS; + } else { + result = QUICPacketCreationResult::FAILED; + } } else { - result = QUICPacketCreationResult::FAILED; + result = QUICPacketCreationResult::IGNORED; } - } else { - result = QUICPacketCreationResult::IGNORED; - } - break; - case QUICPacketType::ZERO_RTT_PROTECTED: - if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::ZERO_RTT)) { - plain = this->_pp_protector.unprotect(header_ibb, protected_ibb, header->packet_number(), header->key_phase()); - if (plain != nullptr) { - memcpy(plain_txt.get(), plain->buf(), plain->size()); - plain_txt_len = plain->size(); - result = QUICPacketCreationResult::SUCCESS; + break; + case QUICPacketType::HANDSHAKE: + packet = new (packet_buf) QUICHandshakePacketR(udp_con, from, to, whole_data, base_packet_number); + if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::HANDSHAKE)) { + plain = this->_pp_protector.unprotect(packet->header_block(), packet->payload_block(), packet->packet_number(), + packet->key_phase()); + if (plain != nullptr) { + static_cast(packet)->attach_payload(plain, true); + result = QUICPacketCreationResult::SUCCESS; + } else { + result = QUICPacketCreationResult::FAILED; + } } else { result = QUICPacketCreationResult::IGNORED; } - } else { - result = QUICPacketCreationResult::NOT_READY; + break; + case QUICPacketType::ZERO_RTT_PROTECTED: + packet = new (packet_buf) QUICZeroRttPacketR(udp_con, from, to, whole_data, base_packet_number); + if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::ZERO_RTT)) { + plain = this->_pp_protector.unprotect(packet->header_block(), packet->payload_block(), packet->packet_number(), + packet->key_phase()); + if (plain != nullptr) { + static_cast(packet)->attach_payload(plain, true); + result = QUICPacketCreationResult::SUCCESS; + } else { + result = QUICPacketCreationResult::IGNORED; + } + } else { + result = QUICPacketCreationResult::NOT_READY; + } + break; + default: + result = QUICPacketCreationResult::FAILED; + break; } - break; - default: - result = QUICPacketCreationResult::FAILED; - break; } + } else { + Debug(tag.data(), "Failed to read essential field"); + uint8_t *buf = reinterpret_cast(whole_data->start()); + if (len > 16) { + Debug(tag_v.data(), "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], + buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); + } + if (len > 32) { + Debug(tag_v.data(), "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", buf[16 + 0], + buf[16 + 1], buf[16 + 2], buf[16 + 3], buf[16 + 4], buf[16 + 5], buf[16 + 6], buf[16 + 7], buf[16 + 8], buf[16 + 9], + buf[16 + 10], buf[16 + 11], buf[16 + 12], buf[16 + 13], buf[16 + 14], buf[16 + 15]); + } + if (len > 48) { + Debug(tag_v.data(), "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", buf[32 + 0], + buf[32 + 1], buf[32 + 2], buf[32 + 3], buf[32 + 4], buf[32 + 5], buf[32 + 6], buf[32 + 7], buf[32 + 8], buf[32 + 9], + buf[32 + 10], buf[32 + 11], buf[32 + 12], buf[32 + 13], buf[32 + 14], buf[32 + 15]); + } + result = QUICPacketCreationResult::FAILED; } - QUICPacket *packet = nullptr; - if (result == QUICPacketCreationResult::SUCCESS || result == QUICPacketCreationResult::UNSUPPORTED) { - packet = quicPacketAllocator.alloc(); - new (packet) QUICPacket(udp_con, std::move(header), std::move(plain_txt), plain_txt_len); + if (result != QUICPacketCreationResult::SUCCESS && result != QUICPacketCreationResult::UNSUPPORTED) { + packet = nullptr; } - return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_packet); + return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free); } QUICPacketUPtr QUICPacketFactory::create_version_negotiation_packet(QUICConnectionId dcid, QUICConnectionId scid) { - size_t len = sizeof(QUICVersion) * (countof(QUIC_SUPPORTED_VERSIONS) + 1); - ats_unique_buf versions(reinterpret_cast(ats_malloc(len))); - uint8_t *p = versions.get(); - - size_t n; - for (auto v : QUIC_SUPPORTED_VERSIONS) { - QUICTypeUtil::write_QUICVersion(v, p, &n); - p += n; - } - - // [draft-18] 6.3. Using Reserved Versions - // To help ensure this, a server SHOULD include a reserved version (see Section 15) while generating a - // Version Negotiation packet. - QUICTypeUtil::write_QUICVersion(QUIC_EXERCISE_VERSION, p, &n); - p += n; - - ink_assert(len == static_cast(p - versions.get())); - // VN packet dosen't have packet number field and version field is always 0x00000000 - QUICPacketHeaderUPtr header = QUICPacketHeader::build(QUICPacketType::VERSION_NEGOTIATION, QUICKeyPhase::INITIAL, dcid, scid, - 0x00, 0x00, 0x00, false, std::move(versions), len); - - return QUICPacketFactory::_create_unprotected_packet(std::move(header)); + return QUICPacketUPtr(new QUICVersionNegotiationPacket(dcid, scid, QUIC_SUPPORTED_VERSIONS, countof(QUIC_SUPPORTED_VERSIONS)), + &QUICPacketDeleter::delete_packet_new); } QUICPacketUPtr -QUICPacketFactory::create_initial_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len, - bool retransmittable, bool probing, bool crypto, ats_unique_buf token, size_t token_len) +QUICPacketFactory::create_initial_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, + bool ack_eliciting, bool probing, bool crypto, ats_unique_buf token, size_t token_len) { QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::INITIAL); QUICPacketNumber pn = this->_packet_number_generator[static_cast(index)].next(); - QUICPacketHeaderUPtr header = - QUICPacketHeader::build(QUICPacketType::INITIAL, QUICKeyPhase::INITIAL, destination_cid, source_cid, pn, base_packet_number, - this->_version, crypto, std::move(payload), len, std::move(token), token_len); - return this->_create_encrypted_packet(std::move(header), retransmittable, probing); -} -QUICPacketUPtr -QUICPacketFactory::create_retry_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICConnectionId original_dcid, QUICRetryToken &token) -{ - ats_unique_buf payload = ats_unique_malloc(token.length()); - memcpy(payload.get(), token.buf(), token.length()); + QUICInitialPacket *packet = new (packet_buf) QUICInitialPacket(this->_version, destination_cid, source_cid, token_len, + std::move(token), length, pn, ack_eliciting, probing, crypto); - QUICPacketHeaderUPtr header = - QUICPacketHeader::build(QUICPacketType::RETRY, QUICKeyPhase::INITIAL, QUIC_SUPPORTED_VERSIONS[0], destination_cid, source_cid, - original_dcid, std::move(payload), token.length()); - return QUICPacketFactory::_create_unprotected_packet(std::move(header)); -} + packet->attach_payload(payload, true); // Attach a cleartext payload with extra headers + Ptr protected_payload = + this->_pp_protector.protect(packet->header_block(), packet->payload_block(), packet->packet_number(), packet->key_phase()); + if (protected_payload != nullptr) { + packet->attach_payload(protected_payload, false); // Replace its payload with the protected payload + } else { + QUICDebug(destination_cid, source_cid, "Failed to encrypt a packet"); + packet = nullptr; + } -QUICPacketUPtr -QUICPacketFactory::create_handshake_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len, - bool retransmittable, bool probing, bool crypto) -{ - QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::HANDSHAKE); - QUICPacketNumber pn = this->_packet_number_generator[static_cast(index)].next(); - QUICPacketHeaderUPtr header = - QUICPacketHeader::build(QUICPacketType::HANDSHAKE, QUICKeyPhase::HANDSHAKE, destination_cid, source_cid, pn, base_packet_number, - this->_version, crypto, std::move(payload), len); - return this->_create_encrypted_packet(std::move(header), retransmittable, probing); + return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free); } QUICPacketUPtr -QUICPacketFactory::create_zero_rtt_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len, - bool retransmittable, bool probing) +QUICPacketFactory::create_retry_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, QUICRetryToken &token) { - QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::ZERO_RTT); - QUICPacketNumber pn = this->_packet_number_generator[static_cast(index)].next(); - QUICPacketHeaderUPtr header = - QUICPacketHeader::build(QUICPacketType::ZERO_RTT_PROTECTED, QUICKeyPhase::ZERO_RTT, destination_cid, source_cid, pn, - base_packet_number, this->_version, false, std::move(payload), len); - return this->_create_encrypted_packet(std::move(header), retransmittable, probing); + return QUICPacketUPtr(new QUICRetryPacket(QUIC_SUPPORTED_VERSIONS[0], destination_cid, source_cid, token), + &QUICPacketDeleter::delete_packet_new); } QUICPacketUPtr -QUICPacketFactory::create_protected_packet(QUICConnectionId connection_id, QUICPacketNumber base_packet_number, - ats_unique_buf payload, size_t len, bool retransmittable, bool probing) +QUICPacketFactory::create_handshake_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, + bool ack_eliciting, bool probing, bool crypto) { - QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::ONE_RTT); + QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::HANDSHAKE); QUICPacketNumber pn = this->_packet_number_generator[static_cast(index)].next(); - // TODO Key phase should be picked up from QUICHandshakeProtocol, probably - QUICPacketHeaderUPtr header = QUICPacketHeader::build(QUICPacketType::PROTECTED, QUICKeyPhase::PHASE_0, connection_id, pn, - base_packet_number, std::move(payload), len); - return this->_create_encrypted_packet(std::move(header), retransmittable, probing); -} -QUICPacketUPtr -QUICPacketFactory::create_stateless_reset_packet(QUICConnectionId connection_id, QUICStatelessResetToken stateless_reset_token) -{ - constexpr uint8_t MIN_UNPREDICTABLE_FIELD_LEN = 5; - std::random_device rnd; + QUICHandshakePacket *packet = + new (packet_buf) QUICHandshakePacket(this->_version, destination_cid, source_cid, length, pn, ack_eliciting, probing, crypto); - uint8_t random_packet_number = static_cast(rnd() & 0xFF); - size_t payload_len = static_cast((rnd() & 0xFF) | (MIN_UNPREDICTABLE_FIELD_LEN + QUICStatelessResetToken::LEN)); - ats_unique_buf payload = ats_unique_malloc(payload_len); - uint8_t *naked_payload = payload.get(); - - // Generate random octets - for (int i = payload_len - 1; i >= 0; --i) { - naked_payload[i] = static_cast(rnd() & 0xFF); + packet->attach_payload(payload, true); // Attach a cleartext payload with extra headers + Ptr protected_payload = + this->_pp_protector.protect(packet->header_block(), packet->payload_block(), packet->packet_number(), packet->key_phase()); + if (protected_payload != nullptr) { + packet->attach_payload(protected_payload, false); // Replace its payload with the protected payload + } else { + QUICDebug(destination_cid, source_cid, "Failed to encrypt a packet"); + packet = nullptr; } - // Copy stateless reset token into payload - memcpy(naked_payload + payload_len - QUICStatelessResetToken::LEN, stateless_reset_token.buf(), QUICStatelessResetToken::LEN); - // KeyPhase won't be used - QUICPacketHeaderUPtr header = QUICPacketHeader::build(QUICPacketType::STATELESS_RESET, QUICKeyPhase::INITIAL, connection_id, - random_packet_number, 0, std::move(payload), payload_len); - return QUICPacketFactory::_create_unprotected_packet(std::move(header)); + return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free); } QUICPacketUPtr -QUICPacketFactory::_create_unprotected_packet(QUICPacketHeaderUPtr header) +QUICPacketFactory::create_zero_rtt_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, + bool ack_eliciting, bool probing) { - ats_unique_buf cleartext = ats_unique_malloc(2048); - size_t cleartext_len = header->payload_size(); + QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::ZERO_RTT); + QUICPacketNumber pn = this->_packet_number_generator[static_cast(index)].next(); + + QUICZeroRttPacket *packet = + new (packet_buf) QUICZeroRttPacket(this->_version, destination_cid, source_cid, length, pn, ack_eliciting, probing); - memcpy(cleartext.get(), header->payload(), cleartext_len); - QUICPacket *packet = quicPacketAllocator.alloc(); - new (packet) QUICPacket(std::move(header), std::move(cleartext), cleartext_len, false, false); + packet->attach_payload(payload, true); // Attach a cleartext payload with extra headers + Ptr protected_payload = + this->_pp_protector.protect(packet->header_block(), packet->payload_block(), packet->packet_number(), packet->key_phase()); + if (protected_payload != nullptr) { + packet->attach_payload(protected_payload, false); // Replace its payload with the protected payload + } else { + QUICDebug(destination_cid, source_cid, "Failed to encrypt a packet"); + packet = nullptr; + } - return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_packet); + return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free); } QUICPacketUPtr -QUICPacketFactory::_create_encrypted_packet(QUICPacketHeaderUPtr header, bool retransmittable, bool probing) +QUICPacketFactory::create_short_header_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, + bool ack_eliciting, bool probing) { - QUICConnectionId dcid = header->destination_cid(); - QUICConnectionId scid = header->source_cid(); - QUICVDebug(dcid, scid, "Encrypting %s packet #%" PRIu64 " using %s", QUICDebugNames::packet_type(header->type()), - header->packet_number(), QUICDebugNames::key_phase(header->key_phase())); - - QUICPacket *packet = nullptr; - - Ptr payload_ibb = make_ptr(new_IOBufferBlock()); - payload_ibb->set_internal(reinterpret_cast(const_cast(header->payload())), header->payload_size(), - BUFFER_SIZE_NOT_ALLOCATED); + QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::ONE_RTT); + QUICPacketNumber pn = this->_packet_number_generator[static_cast(index)].next(); - Ptr header_ibb = make_ptr(new_IOBufferBlock()); - header_ibb->set_internal(reinterpret_cast(const_cast(header->buf())), header->size(), - BUFFER_SIZE_NOT_ALLOCATED); + // TODO Key phase should be picked up from QUICHandshakeProtocol, probably + QUICShortHeaderPacket *packet = + new (packet_buf) QUICShortHeaderPacket(destination_cid, pn, base_packet_number, QUICKeyPhase::PHASE_0, ack_eliciting, probing); + packet->attach_payload(payload, true); // Attach a cleartext payload with extra headers Ptr protected_payload = - this->_pp_protector.protect(header_ibb, payload_ibb, header->packet_number(), header->key_phase()); + this->_pp_protector.protect(packet->header_block(), packet->payload_block(), packet->packet_number(), packet->key_phase()); if (protected_payload != nullptr) { - ats_unique_buf cipher_txt = ats_unique_malloc(protected_payload->size()); - memcpy(cipher_txt.get(), protected_payload->buf(), protected_payload->size()); - packet = quicPacketAllocator.alloc(); - new (packet) QUICPacket(std::move(header), std::move(cipher_txt), protected_payload->size(), retransmittable, probing); + packet->attach_payload(protected_payload, false); // Replace its payload with the protected payload } else { - QUICDebug(dcid, scid, "Failed to encrypt a packet"); + QUICDebug(destination_cid, QUICConnectionId::ZERO(), "Failed to encrypt a packet"); + packet = nullptr; } - return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_packet); + return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free); +} + +QUICPacketUPtr +QUICPacketFactory::create_stateless_reset_packet(QUICStatelessResetToken stateless_reset_token, size_t maximum_size) +{ + return QUICPacketUPtr(new QUICStatelessResetPacket(stateless_reset_token, maximum_size), &QUICPacketDeleter::delete_packet_new); } void diff --git a/iocore/net/quic/QUICPacketFactory.h b/iocore/net/quic/QUICPacketFactory.h index 732ad35514d..d5316ce02f5 100644 --- a/iocore/net/quic/QUICPacketFactory.h +++ b/iocore/net/quic/QUICPacketFactory.h @@ -45,27 +45,26 @@ class QUICPacketFactory public: static QUICPacketUPtr create_null_packet(); static QUICPacketUPtr create_version_negotiation_packet(QUICConnectionId dcid, QUICConnectionId scid); - static QUICPacketUPtr create_stateless_reset_packet(QUICConnectionId connection_id, - QUICStatelessResetToken stateless_reset_token); - static QUICPacketUPtr create_retry_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICConnectionId original_dcid, QUICRetryToken &token); + static QUICPacketUPtr create_stateless_reset_packet(QUICStatelessResetToken stateless_reset_token, size_t maximum_size); + static QUICPacketUPtr create_retry_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, QUICRetryToken &token); QUICPacketFactory(const QUICPacketProtectionKeyInfo &pp_key_info) : _pp_key_info(pp_key_info), _pp_protector(pp_key_info) {} - QUICPacketUPtr create(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, ats_unique_buf buf, size_t len, + QUICPacketUPtr create(uint8_t *packet_buf, UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, ats_unique_buf buf, size_t len, QUICPacketNumber base_packet_number, QUICPacketCreationResult &result); - QUICPacketUPtr create_initial_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len, bool ack_eliciting, - bool probing, bool crypto, ats_unique_buf token = ats_unique_buf(nullptr), - size_t token_len = 0); - QUICPacketUPtr create_handshake_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len, + QUICPacketUPtr create_initial_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, + bool ack_eliciting, bool probing, bool crypto, + ats_unique_buf token = ats_unique_buf(nullptr), size_t token_len = 0); + QUICPacketUPtr create_handshake_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, bool ack_eliciting, bool probing, bool crypto); - QUICPacketUPtr create_zero_rtt_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, - QUICPacketNumber base_packet_number, ats_unique_buf payload, size_t len, bool ack_eliciting, - bool probing); - QUICPacketUPtr create_protected_packet(QUICConnectionId connection_id, QUICPacketNumber base_packet_number, - ats_unique_buf payload, size_t len, bool ack_eliciting, bool probing); + QUICPacketUPtr create_zero_rtt_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, + bool ack_eliciting, bool probing); + QUICPacketUPtr create_short_header_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, + QUICPacketNumber base_packet_number, Ptr payload, size_t length, + bool ack_eliciting, bool probing); void set_version(QUICVersion negotiated_version); bool is_ready_to_create_protected_packet(); @@ -79,7 +78,4 @@ class QUICPacketFactory // Initial, 0/1-RTT, and Handshake QUICPacketNumberGenerator _packet_number_generator[3]; - - static QUICPacketUPtr _create_unprotected_packet(QUICPacketHeaderUPtr header); - QUICPacketUPtr _create_encrypted_packet(QUICPacketHeaderUPtr header, bool ack_eliciting, bool probing); }; diff --git a/iocore/net/quic/QUICPacketHeaderProtector.cc b/iocore/net/quic/QUICPacketHeaderProtector.cc index 0dbfe8df2e7..f6a71b4da6d 100644 --- a/iocore/net/quic/QUICPacketHeaderProtector.cc +++ b/iocore/net/quic/QUICPacketHeaderProtector.cc @@ -32,19 +32,15 @@ bool QUICPacketHeaderProtector::protect(uint8_t *unprotected_packet, size_t unprotected_packet_len, int dcil) const { // Do nothing if the packet is VN - if (QUICInvariants::is_long_header(unprotected_packet)) { - QUICVersion version; - QUICPacketLongHeader::version(version, unprotected_packet, unprotected_packet_len); - if (version == 0x0) { - return true; - } + QUICPacketType type; + QUICPacketR::type(type, unprotected_packet, unprotected_packet_len); + if (type == QUICPacketType::VERSION_NEGOTIATION) { + return true; } QUICKeyPhase phase; - QUICPacketType type; if (QUICInvariants::is_long_header(unprotected_packet)) { - QUICPacketLongHeader::key_phase(phase, unprotected_packet, unprotected_packet_len); - QUICPacketLongHeader::type(type, unprotected_packet, unprotected_packet_len); + QUICLongHeaderPacketR::key_phase(phase, unprotected_packet, unprotected_packet_len); } else { // This is a kind of hack. For short header we need to use the same key for header protection regardless of the key phase. phase = QUICKeyPhase::PHASE_0; @@ -89,24 +85,15 @@ bool QUICPacketHeaderProtector::unprotect(uint8_t *protected_packet, size_t protected_packet_len) const { // Do nothing if the packet is VN or RETRY - if (QUICInvariants::is_long_header(protected_packet)) { - QUICVersion version; - QUICPacketLongHeader::version(version, protected_packet, protected_packet_len); - if (version == 0x0) { - return true; - } - QUICPacketType type; - QUICPacketLongHeader::type(type, protected_packet, protected_packet_len); - if (type == QUICPacketType::RETRY) { - return true; - } + QUICPacketType type; + QUICPacketR::type(type, protected_packet, protected_packet_len); + if (type == QUICPacketType::VERSION_NEGOTIATION || type == QUICPacketType::RETRY) { + return true; } QUICKeyPhase phase; - QUICPacketType type; if (QUICInvariants::is_long_header(protected_packet)) { - QUICPacketLongHeader::key_phase(phase, protected_packet, protected_packet_len); - QUICPacketLongHeader::type(type, protected_packet, protected_packet_len); + QUICLongHeaderPacketR::key_phase(phase, protected_packet, protected_packet_len); } else { // This is a kind of hack. For short header we need to use the same key for header protection regardless of the key phase. phase = QUICKeyPhase::PHASE_0; @@ -155,7 +142,7 @@ QUICPacketHeaderProtector::_calc_sample_offset(uint8_t *sample_offset, const uin size_t dummy; uint8_t length_len; size_t length_offset; - if (!QUICPacketLongHeader::length(dummy, length_len, length_offset, protected_packet, protected_packet_len)) { + if (!QUICLongHeaderPacketR::length(dummy, length_len, length_offset, protected_packet, protected_packet_len)) { return false; } @@ -175,10 +162,10 @@ QUICPacketHeaderProtector::_unprotect(uint8_t *protected_packet, size_t protecte // Unprotect packet number if (QUICInvariants::is_long_header(protected_packet)) { protected_packet[0] ^= mask[0] & 0x0f; - QUICPacketLongHeader::packet_number_offset(pn_offset, protected_packet, protected_packet_len); + QUICLongHeaderPacketR::packet_number_offset(pn_offset, protected_packet, protected_packet_len); } else { protected_packet[0] ^= mask[0] & 0x1f; - QUICPacketShortHeader::packet_number_offset(pn_offset, protected_packet, protected_packet_len, QUICConnectionId::SCID_LEN); + QUICShortHeaderPacketR::packet_number_offset(pn_offset, protected_packet, protected_packet_len, QUICConnectionId::SCID_LEN); } uint8_t pn_length = QUICTypeUtil::read_QUICPacketNumberLen(protected_packet); @@ -199,10 +186,10 @@ QUICPacketHeaderProtector::_protect(uint8_t *protected_packet, size_t protected_ // Protect packet number if (QUICInvariants::is_long_header(protected_packet)) { protected_packet[0] ^= mask[0] & 0x0f; - QUICPacketLongHeader::packet_number_offset(pn_offset, protected_packet, protected_packet_len); + QUICLongHeaderPacketR::packet_number_offset(pn_offset, protected_packet, protected_packet_len); } else { protected_packet[0] ^= mask[0] & 0x1f; - QUICPacketShortHeader::packet_number_offset(pn_offset, protected_packet, protected_packet_len, dcil); + QUICShortHeaderPacketR::packet_number_offset(pn_offset, protected_packet, protected_packet_len, dcil); } for (int i = 0; i < pn_length; ++i) { diff --git a/iocore/net/quic/QUICPacketHeaderProtector_boringssl.cc b/iocore/net/quic/QUICPacketHeaderProtector_boringssl.cc index 54c539e6c59..63e71bf0105 100644 --- a/iocore/net/quic/QUICPacketHeaderProtector_boringssl.cc +++ b/iocore/net/quic/QUICPacketHeaderProtector_boringssl.cc @@ -23,9 +23,36 @@ #include "QUICPacketHeaderProtector.h" +#include "openssl/chacha.h" + bool QUICPacketHeaderProtector::_generate_mask(uint8_t *mask, const uint8_t *sample, const uint8_t *key, const EVP_CIPHER *cipher) const { - ink_assert(!"not implemented"); - return false; + static constexpr unsigned char FIVE_ZEROS[] = {0x00, 0x00, 0x00, 0x00, 0x00}; + + if (cipher == nullptr) { + uint32_t counter = htole32(*reinterpret_cast(&sample[0])); + CRYPTO_chacha_20(mask, FIVE_ZEROS, sizeof(FIVE_ZEROS), key, &sample[4], counter); + } else { + int len = 0; + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) { + return false; + } + if (!EVP_EncryptInit_ex(ctx, cipher, nullptr, key, sample)) { + EVP_CIPHER_CTX_free(ctx); + return false; + } + if (!EVP_EncryptUpdate(ctx, mask, &len, sample, 16)) { + EVP_CIPHER_CTX_free(ctx); + return false; + } + if (!EVP_EncryptFinal_ex(ctx, mask + len, &len)) { + EVP_CIPHER_CTX_free(ctx); + return false; + } + EVP_CIPHER_CTX_free(ctx); + } + + return true; } diff --git a/iocore/net/quic/QUICPacketHeaderProtector_legacy.cc b/iocore/net/quic/QUICPacketHeaderProtector_legacy.cc new file mode 100644 index 00000000000..43bbba8e901 --- /dev/null +++ b/iocore/net/quic/QUICPacketHeaderProtector_legacy.cc @@ -0,0 +1,53 @@ +/** @file + * + * QUIC Packet Header Protector (OpenSSL specific code) + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QUICPacketHeaderProtector.h" + +bool +QUICPacketHeaderProtector::_generate_mask(uint8_t *mask, const uint8_t *sample, const uint8_t *key, const EVP_CIPHER *cipher) const +{ + static constexpr unsigned char FIVE_ZEROS[] = {0x00, 0x00, 0x00, 0x00, 0x00}; + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + + if (!ctx || !EVP_EncryptInit_ex(ctx, cipher, nullptr, key, sample)) { + return false; + } + + int len = 0; + if (cipher == EVP_chacha20()) { + if (!EVP_EncryptUpdate(ctx, mask, &len, FIVE_ZEROS, sizeof(FIVE_ZEROS))) { + return false; + } + } else { + if (!EVP_EncryptUpdate(ctx, mask, &len, sample, 16)) { + return false; + } + } + if (!EVP_EncryptFinal_ex(ctx, mask + len, &len)) { + return false; + } + + EVP_CIPHER_CTX_free(ctx); + + return true; +} diff --git a/iocore/net/quic/QUICPacketHeaderProtector_openssl.cc b/iocore/net/quic/QUICPacketHeaderProtector_openssl.cc index 43bbba8e901..c7d65a19e11 100644 --- a/iocore/net/quic/QUICPacketHeaderProtector_openssl.cc +++ b/iocore/net/quic/QUICPacketHeaderProtector_openssl.cc @@ -29,21 +29,28 @@ QUICPacketHeaderProtector::_generate_mask(uint8_t *mask, const uint8_t *sample, static constexpr unsigned char FIVE_ZEROS[] = {0x00, 0x00, 0x00, 0x00, 0x00}; EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); - if (!ctx || !EVP_EncryptInit_ex(ctx, cipher, nullptr, key, sample)) { + if (!ctx) { + return false; + } + if (!EVP_EncryptInit_ex(ctx, cipher, nullptr, key, sample)) { + EVP_CIPHER_CTX_free(ctx); return false; } int len = 0; if (cipher == EVP_chacha20()) { if (!EVP_EncryptUpdate(ctx, mask, &len, FIVE_ZEROS, sizeof(FIVE_ZEROS))) { + EVP_CIPHER_CTX_free(ctx); return false; } } else { if (!EVP_EncryptUpdate(ctx, mask, &len, sample, 16)) { + EVP_CIPHER_CTX_free(ctx); return false; } } if (!EVP_EncryptFinal_ex(ctx, mask + len, &len)) { + EVP_CIPHER_CTX_free(ctx); return false; } diff --git a/iocore/net/quic/QUICPacketPayloadProtector.cc b/iocore/net/quic/QUICPacketPayloadProtector.cc index 374480b133e..4b8291c8a39 100644 --- a/iocore/net/quic/QUICPacketPayloadProtector.cc +++ b/iocore/net/quic/QUICPacketPayloadProtector.cc @@ -46,12 +46,16 @@ QUICPacketPayloadProtector::protect(const Ptr unprotected_header, const EVP_CIPHER *cipher = this->_pp_key_info.get_cipher(phase); - protected_payload = make_ptr(new_IOBufferBlock()); - protected_payload->alloc(iobuffer_size_to_index(unprotected_payload->size() + tag_len, BUFFER_SIZE_INDEX_32K)); + protected_payload = make_ptr(new_IOBufferBlock()); + size_t unprotected_payload_len = 0; + for (Ptr tmp = unprotected_payload; tmp; tmp = tmp->next) { + unprotected_payload_len += tmp->size(); + } + protected_payload->alloc(iobuffer_size_to_index(unprotected_payload_len + tag_len, BUFFER_SIZE_INDEX_32K)); size_t written_len = 0; if (!this->_protect(reinterpret_cast(protected_payload->start()), written_len, protected_payload->write_avail(), - unprotected_payload, pkt_num, reinterpret_cast(unprotected_header->buf()), + unprotected_payload, pkt_num, reinterpret_cast(unprotected_header->start()), unprotected_header->size(), key, iv, iv_len, cipher, tag_len)) { Debug(tag, "Failed to encrypt a packet #%" PRIu64 " with keys for %s", pkt_num, QUICDebugNames::key_phase(phase)); protected_payload = nullptr; @@ -84,9 +88,9 @@ QUICPacketPayloadProtector::unprotect(const Ptr unprotected_heade size_t written_len = 0; if (!this->_unprotect(reinterpret_cast(unprotected_payload->start()), written_len, unprotected_payload->write_avail(), - reinterpret_cast(protected_payload->buf()), protected_payload->size(), pkt_num, - reinterpret_cast(unprotected_header->buf()), unprotected_header->size(), key, iv, iv_len, cipher, - tag_len)) { + reinterpret_cast(protected_payload->start()), protected_payload->size(), pkt_num, + reinterpret_cast(unprotected_header->start()), unprotected_header->size(), key, iv, iv_len, + cipher, tag_len)) { Debug(tag, "Failed to decrypt a packet #%" PRIu64, pkt_num); unprotected_payload = nullptr; } else { @@ -122,3 +126,111 @@ QUICPacketPayloadProtector::_gen_nonce(uint8_t *nonce, size_t &nonce_len, uint64 nonce[iv_len - 8 + i] ^= p[i]; } } + +bool +QUICPacketPayloadProtector::_protect(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len, const Ptr plain, + uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, const uint8_t *iv, + size_t iv_len, const EVP_CIPHER *aead, size_t tag_len) const +{ + EVP_CIPHER_CTX *aead_ctx; + int len; + uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; + size_t nonce_len = 0; + + this->_gen_nonce(nonce, nonce_len, pkt_num, iv, iv_len); + + if (!(aead_ctx = EVP_CIPHER_CTX_new())) { + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + return false; + } + if (!EVP_EncryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + return false; + } + + cipher_len = 0; + for (Ptr b = plain; b; b = b->next) { + if (!EVP_EncryptUpdate(aead_ctx, cipher + cipher_len, &len, reinterpret_cast(b->start()), b->size())) { + return false; + } + cipher_len += len; + } + + if (!EVP_EncryptFinal_ex(aead_ctx, cipher + cipher_len, &len)) { + return false; + } + cipher_len += len; + + if (max_cipher_len < cipher_len + tag_len) { + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, cipher + cipher_len)) { + return false; + } + cipher_len += tag_len; + + EVP_CIPHER_CTX_free(aead_ctx); + + return true; +} + +bool +QUICPacketPayloadProtector::_unprotect(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *cipher, + size_t cipher_len, uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, + const uint8_t *iv, size_t iv_len, const EVP_CIPHER *aead, size_t tag_len) const +{ + EVP_CIPHER_CTX *aead_ctx; + int len; + uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; + size_t nonce_len = 0; + + this->_gen_nonce(nonce, nonce_len, pkt_num, iv, iv_len); + + if (!(aead_ctx = EVP_CIPHER_CTX_new())) { + return false; + } + if (!EVP_DecryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + return false; + } + if (!EVP_DecryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + return false; + } + if (!EVP_DecryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + return false; + } + + if (cipher_len < tag_len) { + return false; + } + cipher_len -= tag_len; + if (!EVP_DecryptUpdate(aead_ctx, plain, &len, cipher, cipher_len)) { + return false; + } + plain_len = len; + + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, const_cast(cipher + cipher_len))) { + return false; + } + + int ret = EVP_DecryptFinal_ex(aead_ctx, plain + len, &len); + + EVP_CIPHER_CTX_free(aead_ctx); + + if (ret > 0) { + plain_len += len; + return true; + } else { + Debug(tag, "Failed to decrypt -- the first 4 bytes decrypted are %0x %0x %0x %0x", plain[0], plain[1], plain[2], plain[3]); + return false; + } +} diff --git a/iocore/net/quic/QUICPacketPayloadProtector_boringssl.cc b/iocore/net/quic/QUICPacketPayloadProtector_boringssl.cc index 56e88dd45fd..0352e513d13 100644 --- a/iocore/net/quic/QUICPacketPayloadProtector_boringssl.cc +++ b/iocore/net/quic/QUICPacketPayloadProtector_boringssl.cc @@ -25,24 +25,129 @@ #include "QUICPacketPayloadProtector.h" #include "tscore/Diags.h" -// static constexpr char tag[] = "quic_ppp"; +static constexpr char tag[] = "quic_ppp"; bool -QUICPacketPayloadProtector::_protect(uint8_t *protected_payload, size_t &protected_payload_len, size_t max_protecgted_payload_len, - const Ptr plain, uint64_t pkt_num, const uint8_t *ad, size_t ad_len, - const uint8_t *key, const uint8_t *iv, size_t iv_len, const EVP_CIPHER *cipher, - size_t tag_len) const +QUICPacketPayloadProtector::_protect(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len, const Ptr plain, + uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, const uint8_t *iv, + size_t iv_len, const EVP_CIPHER *aead, size_t tag_len) const { - ink_assert(!"not implemented"); - return false; + EVP_CIPHER_CTX *aead_ctx; + int len; + uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; + size_t nonce_len = 0; + + this->_gen_nonce(nonce, nonce_len, pkt_num, iv, iv_len); + + if (!(aead_ctx = EVP_CIPHER_CTX_new())) { + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + if (!EVP_EncryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + + cipher_len = 0; + Ptr b = plain; + while (b) { + if (!EVP_EncryptUpdate(aead_ctx, cipher + cipher_len, &len, reinterpret_cast(b->buf()), b->size())) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + cipher_len += len; + b = b->next; + } + + if (!EVP_EncryptFinal_ex(aead_ctx, cipher + cipher_len, &len)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + cipher_len += len; + + if (max_cipher_len < cipher_len + tag_len) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, cipher + cipher_len)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + cipher_len += tag_len; + + EVP_CIPHER_CTX_free(aead_ctx); + + return true; } bool -QUICPacketPayloadProtector::_unprotect(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *protected_payload, - size_t protected_payload_len, uint64_t pkt_num, const uint8_t *ad, size_t ad_len, - const uint8_t *key, const uint8_t *iv, size_t iv_len, const EVP_CIPHER *cipher, - size_t tag_len) const +QUICPacketPayloadProtector::_unprotect(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *cipher, + size_t cipher_len, uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, + const uint8_t *iv, size_t iv_len, const EVP_CIPHER *aead, size_t tag_len) const { - ink_assert(!"not implemented"); - return false; + EVP_CIPHER_CTX *aead_ctx; + int len; + uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; + size_t nonce_len = 0; + + this->_gen_nonce(nonce, nonce_len, pkt_num, iv, iv_len); + + if (!(aead_ctx = EVP_CIPHER_CTX_new())) { + return false; + } + if (!EVP_DecryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + if (!EVP_DecryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + if (!EVP_DecryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + + if (cipher_len < tag_len) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + cipher_len -= tag_len; + if (!EVP_DecryptUpdate(aead_ctx, plain, &len, cipher, cipher_len)) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + plain_len = len; + + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, const_cast(cipher + cipher_len))) { + EVP_CIPHER_CTX_free(aead_ctx); + return false; + } + + int ret = EVP_DecryptFinal_ex(aead_ctx, plain + len, &len); + + EVP_CIPHER_CTX_free(aead_ctx); + + if (ret > 0) { + plain_len += len; + return true; + } else { + Debug(tag, "Failed to decrypt -- the first 4 bytes decrypted are %0x %0x %0x %0x", plain[0], plain[1], plain[2], plain[3]); + return false; + } } diff --git a/iocore/net/quic/QUICPacketPayloadProtector_legacy.cc b/iocore/net/quic/QUICPacketPayloadProtector_legacy.cc new file mode 100644 index 00000000000..5fc9e2e6e11 --- /dev/null +++ b/iocore/net/quic/QUICPacketPayloadProtector_legacy.cc @@ -0,0 +1,136 @@ +/** @file + * + * QUIC Packet Payload Protector (OpenSSL specific code) + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QUICPacketProtectionKeyInfo.h" +#include "QUICPacketPayloadProtector.h" +#include "tscore/Diags.h" + +static constexpr char tag[] = "quic_ppp"; + +bool +QUICPacketPayloadProtector::_protect(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len, const Ptr plain, + uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, const uint8_t *iv, + size_t iv_len, const EVP_CIPHER *aead, size_t tag_len) const +{ + EVP_CIPHER_CTX *aead_ctx; + int len; + uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; + size_t nonce_len = 0; + + this->_gen_nonce(nonce, nonce_len, pkt_num, iv, iv_len); + + if (!(aead_ctx = EVP_CIPHER_CTX_new())) { + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + return false; + } + if (!EVP_EncryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + return false; + } + + cipher_len = 0; + for (Ptr b = plain; b; b = b->next) { + if (!EVP_EncryptUpdate(aead_ctx, cipher + cipher_len, &len, reinterpret_cast(b->start()), b->size())) { + return false; + } + cipher_len += len; + } + + if (!EVP_EncryptFinal_ex(aead_ctx, cipher + cipher_len, &len)) { + return false; + } + cipher_len += len; + + if (max_cipher_len < cipher_len + tag_len) { + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, cipher + cipher_len)) { + return false; + } + cipher_len += tag_len; + + EVP_CIPHER_CTX_free(aead_ctx); + + return true; +} + +bool +QUICPacketPayloadProtector::_unprotect(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *cipher, + size_t cipher_len, uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, + const uint8_t *iv, size_t iv_len, const EVP_CIPHER *aead, size_t tag_len) const +{ + EVP_CIPHER_CTX *aead_ctx; + int len; + uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; + size_t nonce_len = 0; + + this->_gen_nonce(nonce, nonce_len, pkt_num, iv, iv_len); + + if (!(aead_ctx = EVP_CIPHER_CTX_new())) { + return false; + } + if (!EVP_DecryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + return false; + } + if (!EVP_DecryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + return false; + } + if (!EVP_DecryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + return false; + } + + if (cipher_len < tag_len) { + return false; + } + cipher_len -= tag_len; + if (!EVP_DecryptUpdate(aead_ctx, plain, &len, cipher, cipher_len)) { + return false; + } + plain_len = len; + + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, const_cast(cipher + cipher_len))) { + return false; + } + + int ret = EVP_DecryptFinal_ex(aead_ctx, plain + len, &len); + + EVP_CIPHER_CTX_free(aead_ctx); + + if (ret > 0) { + plain_len += len; + return true; + } else { + Debug(tag, "Failed to decrypt -- the first 4 bytes decrypted are %0x %0x %0x %0x", plain[0], plain[1], plain[2], plain[3]); + return false; + } +} diff --git a/iocore/net/quic/QUICPacketPayloadProtector_openssl.cc b/iocore/net/quic/QUICPacketPayloadProtector_openssl.cc index b25f099051e..c900718417b 100644 --- a/iocore/net/quic/QUICPacketPayloadProtector_openssl.cc +++ b/iocore/net/quic/QUICPacketPayloadProtector_openssl.cc @@ -43,15 +43,19 @@ QUICPacketPayloadProtector::_protect(uint8_t *cipher, size_t &cipher_len, size_t return false; } if (!EVP_EncryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (!EVP_EncryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (!EVP_EncryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } @@ -59,6 +63,7 @@ QUICPacketPayloadProtector::_protect(uint8_t *cipher, size_t &cipher_len, size_t Ptr b = plain; while (b) { if (!EVP_EncryptUpdate(aead_ctx, cipher + cipher_len, &len, reinterpret_cast(b->buf()), b->size())) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } cipher_len += len; @@ -66,14 +71,17 @@ QUICPacketPayloadProtector::_protect(uint8_t *cipher, size_t &cipher_len, size_t } if (!EVP_EncryptFinal_ex(aead_ctx, cipher + cipher_len, &len)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } cipher_len += len; if (max_cipher_len < cipher_len + tag_len) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, cipher + cipher_len)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } cipher_len += tag_len; @@ -99,28 +107,35 @@ QUICPacketPayloadProtector::_unprotect(uint8_t *plain, size_t &plain_len, size_t return false; } if (!EVP_DecryptInit_ex(aead_ctx, aead, nullptr, nullptr, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (!EVP_DecryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (!EVP_DecryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } if (cipher_len < tag_len) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } cipher_len -= tag_len; if (!EVP_DecryptUpdate(aead_ctx, plain, &len, cipher, cipher_len)) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } plain_len = len; if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, const_cast(cipher + cipher_len))) { + EVP_CIPHER_CTX_free(aead_ctx); return false; } diff --git a/iocore/net/quic/QUICPacketReceiveQueue.cc b/iocore/net/quic/QUICPacketReceiveQueue.cc index c90385bcffc..f19779099f9 100644 --- a/iocore/net/quic/QUICPacketReceiveQueue.cc +++ b/iocore/net/quic/QUICPacketReceiveQueue.cc @@ -22,6 +22,7 @@ */ #include "QUICPacketReceiveQueue.h" +#include "QUICPacketHeaderProtector.h" #include "QUICPacketFactory.h" #include "QUICIntUtil.h" @@ -44,7 +45,7 @@ QUICPacketReceiveQueue::enqueue(UDPPacket *packet) } QUICPacketUPtr -QUICPacketReceiveQueue::dequeue(QUICPacketCreationResult &result) +QUICPacketReceiveQueue::dequeue(uint8_t *packet_buf, QUICPacketCreationResult &result) { QUICPacketUPtr quic_packet = QUICPacketFactory::create_null_packet(); UDPPacket *udp_packet = nullptr; @@ -67,7 +68,7 @@ QUICPacketReceiveQueue::dequeue(QUICPacketCreationResult &result) IOBufferBlock *b = udp_packet->getIOBlockChain(); size_t written = 0; while (b) { - memcpy(this->_payload.get() + written, b->buf(), b->read_avail()); + memcpy(this->_payload.get() + written, b->start(), b->read_avail()); written += b->read_avail(); b = b->next.get(); } @@ -83,7 +84,7 @@ QUICPacketReceiveQueue::dequeue(QUICPacketCreationResult &result) if (QUICInvariants::is_long_header(buf)) { QUICVersion version; - QUICPacketLongHeader::version(version, buf, remaining_len); + QUICLongHeaderPacketR::version(version, buf, remaining_len); if (is_vn(version)) { pkt_len = remaining_len; type = QUICPacketType::VERSION_NEGOTIATION; @@ -91,17 +92,17 @@ QUICPacketReceiveQueue::dequeue(QUICPacketCreationResult &result) result = QUICPacketCreationResult::UNSUPPORTED; pkt_len = remaining_len; } else { - QUICPacketLongHeader::type(type, this->_payload.get() + this->_offset, remaining_len); + QUICLongHeaderPacketR::type(type, this->_payload.get() + this->_offset, remaining_len); if (type == QUICPacketType::RETRY) { pkt_len = remaining_len; } else { - if (!QUICPacketLongHeader::packet_length(pkt_len, this->_payload.get() + this->_offset, remaining_len)) { + if (!QUICLongHeaderPacketR::packet_length(pkt_len, this->_payload.get() + this->_offset, remaining_len)) { + // This should not happen basically. Ignore rest of data on current packet. this->_payload.release(); this->_payload = nullptr; this->_payload_len = 0; this->_offset = 0; - - result = QUICPacketCreationResult::IGNORED; + result = QUICPacketCreationResult::IGNORED; return quic_packet; } @@ -148,7 +149,7 @@ QUICPacketReceiveQueue::dequeue(QUICPacketCreationResult &result) } if (this->_ph_protector.unprotect(pkt.get(), pkt_len)) { - quic_packet = this->_packet_factory.create(this->_udp_con, this->_from, this->_to, std::move(pkt), pkt_len, + quic_packet = this->_packet_factory.create(packet_buf, this->_udp_con, this->_from, this->_to, std::move(pkt), pkt_len, this->_largest_received_packet_number, result); } else { // ZERO_RTT might be rejected @@ -175,7 +176,8 @@ QUICPacketReceiveQueue::dequeue(QUICPacketCreationResult &result) // do nothing - if the packet is unsupported version, we don't know packet number break; default: - if (quic_packet && quic_packet->packet_number() > this->_largest_received_packet_number) { + if (quic_packet && quic_packet->type() != QUICPacketType::VERSION_NEGOTIATION && + quic_packet->packet_number() > this->_largest_received_packet_number) { this->_largest_received_packet_number = quic_packet->packet_number(); } } diff --git a/iocore/net/quic/QUICPacketReceiveQueue.h b/iocore/net/quic/QUICPacketReceiveQueue.h index e4e3e297209..78e5b00a18a 100644 --- a/iocore/net/quic/QUICPacketReceiveQueue.h +++ b/iocore/net/quic/QUICPacketReceiveQueue.h @@ -29,6 +29,7 @@ #include "QUICPacket.h" class QUICPacketFactory; +class QUICPacketHeaderProtector; class QUICPacketReceiveQueue { @@ -36,7 +37,7 @@ class QUICPacketReceiveQueue QUICPacketReceiveQueue(QUICPacketFactory &packet_factory, QUICPacketHeaderProtector &ph_protector); void enqueue(UDPPacket *packet); - QUICPacketUPtr dequeue(QUICPacketCreationResult &result); + QUICPacketUPtr dequeue(uint8_t *packet_buf, QUICPacketCreationResult &result); uint32_t size(); void reset(); diff --git a/iocore/net/quic/QUICPathManager.cc b/iocore/net/quic/QUICPathManager.cc index 613efecbe19..c0adbdecc5b 100644 --- a/iocore/net/quic/QUICPathManager.cc +++ b/iocore/net/quic/QUICPathManager.cc @@ -28,7 +28,7 @@ #define QUICDebug(fmt, ...) Debug("quic_path", "[%s] " fmt, this->_cinfo.cids().data(), ##__VA_ARGS__) void -QUICPathManager::open_new_path(const QUICPath &path, ink_hrtime timeout_in) +QUICPathManagerImpl::open_new_path(const QUICPath &path, ink_hrtime timeout_in) { if (this->_verify_timeout_at == 0) { // Overwrite _previous_path only if _current_path is verified @@ -41,14 +41,14 @@ QUICPathManager::open_new_path(const QUICPath &path, ink_hrtime timeout_in) } void -QUICPathManager::set_trusted_path(const QUICPath &path) +QUICPathManagerImpl::set_trusted_path(const QUICPath &path) { this->_current_path = path; this->_previous_path = path; } void -QUICPathManager::_check_verify_timeout() +QUICPathManagerImpl::_check_verify_timeout() { if (this->_verify_timeout_at != 0) { if (this->_path_validator.is_validated(this->_current_path)) { @@ -66,14 +66,14 @@ QUICPathManager::_check_verify_timeout() } const QUICPath & -QUICPathManager::get_current_path() +QUICPathManagerImpl::get_current_path() { this->_check_verify_timeout(); return this->_current_path; } const QUICPath & -QUICPathManager::get_verified_path() +QUICPathManagerImpl::get_verified_path() { this->_check_verify_timeout(); if (this->_verify_timeout_at != 0) { diff --git a/iocore/net/quic/QUICPathManager.h b/iocore/net/quic/QUICPathManager.h index 9f9eb37b8eb..1d4542a629b 100644 --- a/iocore/net/quic/QUICPathManager.h +++ b/iocore/net/quic/QUICPathManager.h @@ -31,15 +31,25 @@ class QUICPathValidator; class QUICPathManager { public: - QUICPathManager(const QUICConnectionInfoProvider &info, QUICPathValidator &path_validator) + virtual ~QUICPathManager() {} + virtual const QUICPath &get_current_path() = 0; + virtual const QUICPath &get_verified_path() = 0; + virtual void open_new_path(const QUICPath &path, ink_hrtime timeout_in) = 0; + virtual void set_trusted_path(const QUICPath &path) = 0; +}; + +class QUICPathManagerImpl : public QUICPathManager +{ +public: + QUICPathManagerImpl(const QUICConnectionInfoProvider &info, QUICPathValidator &path_validator) : _cinfo(info), _path_validator(path_validator) { } - const QUICPath &get_current_path(); - const QUICPath &get_verified_path(); - void open_new_path(const QUICPath &path, ink_hrtime timeout_in); - void set_trusted_path(const QUICPath &path); + const QUICPath &get_current_path() override; + const QUICPath &get_verified_path() override; + void open_new_path(const QUICPath &path, ink_hrtime timeout_in) override; + void set_trusted_path(const QUICPath &path) override; private: const QUICConnectionInfoProvider &_cinfo; diff --git a/iocore/net/quic/QUICPinger.cc b/iocore/net/quic/QUICPinger.cc index 84454c8eb66..2514c028210 100644 --- a/iocore/net/quic/QUICPinger.cc +++ b/iocore/net/quic/QUICPinger.cc @@ -24,18 +24,20 @@ #include "QUICPinger.h" void -QUICPinger::request() +QUICPinger::request(QUICEncryptionLevel level) { SCOPED_MUTEX_LOCK(lock, this->_mutex, this_ethread()); - ++this->_need_to_fire; + ink_assert(level != QUICEncryptionLevel::NONE); + ++this->_need_to_fire[static_cast(level)]; } void -QUICPinger::cancel() +QUICPinger::cancel(QUICEncryptionLevel level) { SCOPED_MUTEX_LOCK(lock, this->_mutex, this_ethread()); - if (this->_need_to_fire > 0) { - --this->_need_to_fire; + ink_assert(level != QUICEncryptionLevel::NONE); + if (this->_need_to_fire[static_cast(level)] > 0) { + --this->_need_to_fire[static_cast(level)]; } } @@ -44,27 +46,25 @@ QUICPinger::_will_generate_frame(QUICEncryptionLevel level, size_t current_packe { SCOPED_MUTEX_LOCK(lock, this->_mutex, this_ethread()); - if (level != QUICEncryptionLevel::ONE_RTT) { - return false; - } - // PING Frame is meaningless for ack_eliciting packet. Cancel it. if (ack_eliciting) { this->_ack_eliciting_packet_out = true; - this->cancel(); + this->cancel(level); return false; } - if (this->_ack_eliciting_packet_out == false && !ack_eliciting && current_packet_size > 0 && this->_need_to_fire == 0) { + ink_assert(level != QUICEncryptionLevel::NONE); + if (this->_ack_eliciting_packet_out == false && !ack_eliciting && current_packet_size > 0 && + this->_need_to_fire[static_cast(level)] == 0) { // force to send an PING Frame - this->request(); + this->request(level); } // only update `_ack_eliciting_packet_out` when we has something to send. if (current_packet_size) { this->_ack_eliciting_packet_out = ack_eliciting; } - return this->_need_to_fire > 0; + return this->_need_to_fire[static_cast(level)] > 0; } /** @@ -77,10 +77,11 @@ QUICPinger::_generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t /* SCOPED_MUTEX_LOCK(lock, this->_mutex, this_ethread()); QUICFrame *frame = nullptr; - if (level == QUICEncryptionLevel::ONE_RTT && this->_need_to_fire > 0 && maximum_frame_size > 0) { + ink_assert(level != QUICEncryptionLevel::NONE); + if (this->_need_to_fire[static_cast(level)] > 0 && maximum_frame_size > 0) { // don't care ping frame lost or acked frame = QUICFrameFactory::create_ping_frame(buf, 0, nullptr); - --this->_need_to_fire; + --this->_need_to_fire[static_cast(level)]; this->_ack_eliciting_packet_out = true; } @@ -88,8 +89,9 @@ QUICPinger::_generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t /* } uint64_t -QUICPinger::count() +QUICPinger::count(QUICEncryptionLevel level) { SCOPED_MUTEX_LOCK(lock, this->_mutex, this_ethread()); - return this->_need_to_fire; + ink_assert(level != QUICEncryptionLevel::NONE); + return this->_need_to_fire[static_cast(level)]; } diff --git a/iocore/net/quic/QUICPinger.h b/iocore/net/quic/QUICPinger.h index 483cfb2cd0e..0ea9b58f8d2 100644 --- a/iocore/net/quic/QUICPinger.h +++ b/iocore/net/quic/QUICPinger.h @@ -35,9 +35,9 @@ class QUICPinger : public QUICFrameOnceGenerator public: QUICPinger() : _mutex(new_ProxyMutex()) {} - void request(); - void cancel(); - uint64_t count(); + void request(QUICEncryptionLevel level); + void cancel(QUICEncryptionLevel level); + uint64_t count(QUICEncryptionLevel level); private: // QUICFrameGenerator @@ -48,5 +48,5 @@ class QUICPinger : public QUICFrameOnceGenerator bool _ack_eliciting_packet_out = false; Ptr _mutex; - uint64_t _need_to_fire = 0; + uint64_t _need_to_fire[4] = {0}; // Initial, 0RTT, HANDSHAKE and 1RTT }; diff --git a/iocore/net/quic/QUICResetTokenTable.cc b/iocore/net/quic/QUICResetTokenTable.cc new file mode 100644 index 00000000000..0da753f976f --- /dev/null +++ b/iocore/net/quic/QUICResetTokenTable.cc @@ -0,0 +1,53 @@ +/** @file + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include "tscore/Diags.h" +#include "QUICResetTokenTable.h" +#include "QUICConnection.h" + +void +QUICResetTokenTable::insert(const QUICStatelessResetToken token, QUICConnection *connection) +{ + Debug("quic_reset_token_table", "Token:%02x%02x%02x%02x... CID:%08" PRIx32 "...", token.buf()[0], token.buf()[1], token.buf()[2], + token.buf()[3], connection->connection_id().h32()); + this->_map.emplace(token, connection); +} + +QUICConnection * +QUICResetTokenTable::lookup(QUICStatelessResetToken token) +{ + Debug("quic_reset_token_table", "Token:%02x%02x%02x%02x...", token.buf()[0], token.buf()[1], token.buf()[2], token.buf()[3]); + auto result = this->_map.find(token); + if (result != this->_map.end()) { + Debug("quic_reset_token_table", "CID:%08" PRIx32 "...", result->second->connection_id().h32()); + return result->second; + } else { + Debug("quic_reset_token_table", "not fouund"); + return nullptr; + } +} + +void +QUICResetTokenTable::erase(const QUICStatelessResetToken token) +{ + Debug("quic_reset_token_table", "Token:%02x%02x%02x%02x...", token.buf()[0], token.buf()[1], token.buf()[2], token.buf()[3]); + this->_map.erase(token); +} diff --git a/iocore/net/quic/QUICResetTokenTable.h b/iocore/net/quic/QUICResetTokenTable.h new file mode 100644 index 00000000000..559b2647582 --- /dev/null +++ b/iocore/net/quic/QUICResetTokenTable.h @@ -0,0 +1,46 @@ +/** @file + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#pragma once + +#include "QUICTypes.h" + +class QUICConnection; + +class QUICResetTokenTable +{ +public: + void insert(const QUICStatelessResetToken token, QUICConnection *connection); + QUICConnection *lookup(const QUICStatelessResetToken token); + void erase(const QUICStatelessResetToken token); + +private: + class TokenHasher + { + public: + uint64_t + operator()(const QUICStatelessResetToken &key) const + { + return static_cast(key); + } + }; + std::unordered_map _map; +}; diff --git a/iocore/net/quic/QUICRetryIntegrityTag.cc b/iocore/net/quic/QUICRetryIntegrityTag.cc new file mode 100644 index 00000000000..c528656896d --- /dev/null +++ b/iocore/net/quic/QUICRetryIntegrityTag.cc @@ -0,0 +1,79 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QUICRetryIntegrityTag.h" + +bool +QUICRetryIntegrityTag::compute(uint8_t *out, QUICConnectionId odcid, Ptr header, Ptr payload) +{ + EVP_CIPHER_CTX *aead_ctx; + + if (!(aead_ctx = EVP_CIPHER_CTX_new())) { + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) { + return false; + } + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, nullptr)) { + return false; + } + if (!EVP_EncryptInit_ex(aead_ctx, nullptr, nullptr, KEY_FOR_RETRY_INTEGRITY_TAG, NONCE_FOR_RETRY_INTEGRITY_TAG)) { + return false; + } + + // Original Destination Connection ID + size_t n; + int dummy; + uint8_t odcid_buf[1 + QUICConnectionId::MAX_LENGTH]; + // Len + odcid_buf[0] = odcid.length(); + // ID + QUICTypeUtil::write_QUICConnectionId(odcid, odcid_buf + 1, &n); + if (!EVP_EncryptUpdate(aead_ctx, nullptr, &dummy, odcid_buf, 1 + odcid.length())) { + return false; + } + + // Retry Packet + for (Ptr b = header; b; b = b->next) { + if (!EVP_EncryptUpdate(aead_ctx, nullptr, &dummy, reinterpret_cast(b->start()), b->size())) { + return false; + } + } + for (Ptr b = payload; b; b = b->next) { + if (!EVP_EncryptUpdate(aead_ctx, nullptr, &dummy, reinterpret_cast(b->start()), b->size())) { + return false; + } + } + + if (!EVP_EncryptFinal_ex(aead_ctx, nullptr, &dummy)) { + return false; + } + + if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_GET_TAG, LEN, out)) { + return false; + } + + EVP_CIPHER_CTX_free(aead_ctx); + + return true; +} diff --git a/iocore/net/quic/QUICRetryIntegrityTag.h b/iocore/net/quic/QUICRetryIntegrityTag.h new file mode 100644 index 00000000000..3ac9498f567 --- /dev/null +++ b/iocore/net/quic/QUICRetryIntegrityTag.h @@ -0,0 +1,39 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "QUICTypes.h" + +class QUICRetryIntegrityTag +{ +public: + static constexpr int LEN = 16; + static bool compute(uint8_t *out, QUICConnectionId odcid, Ptr header, Ptr payload); + +private: + static constexpr uint8_t KEY_FOR_RETRY_INTEGRITY_TAG[] = {0x4d, 0x32, 0xec, 0xdb, 0x2a, 0x21, 0x33, 0xc8, + 0x41, 0xe4, 0x04, 0x3d, 0xf2, 0x7d, 0x44, 0x30}; + static constexpr uint8_t NONCE_FOR_RETRY_INTEGRITY_TAG[] = {0x4d, 0x16, 0x11, 0xd0, 0x55, 0x13, + 0xa5, 0x52, 0xc5, 0x87, 0xd5, 0x75}; +}; diff --git a/iocore/net/quic/QUICStream.cc b/iocore/net/quic/QUICStream.cc index 334b9f41bca..93ac8f1f733 100644 --- a/iocore/net/quic/QUICStream.cc +++ b/iocore/net/quic/QUICStream.cc @@ -245,7 +245,7 @@ QUICStreamVConnection::_signal_read_event() } MUTEX_TRY_LOCK(lock, this->_read_vio.mutex, this_ethread()); - int event = this->_read_vio.ntodo() ? VC_EVENT_READ_READY : VC_EVENT_READ_COMPLETE; + int event = this->_read_vio.nbytes == INT64_MAX ? VC_EVENT_READ_READY : VC_EVENT_READ_COMPLETE; if (lock.is_locked()) { this->_read_vio.cont->handleEvent(event, &this->_read_vio); diff --git a/iocore/net/quic/QUICStreamManager.cc b/iocore/net/quic/QUICStreamManager.cc index de4ffb9cd18..da66d740822 100644 --- a/iocore/net/quic/QUICStreamManager.cc +++ b/iocore/net/quic/QUICStreamManager.cc @@ -29,10 +29,10 @@ static constexpr char tag[] = "quic_stream_manager"; static constexpr QUICStreamId QUIC_STREAM_TYPES = 4; -QUICStreamManager::QUICStreamManager(QUICConnectionInfoProvider *info, QUICRTTProvider *rtt_provider, QUICApplicationMap *app_map) - : _stream_factory(rtt_provider, info), _info(info), _app_map(app_map) +QUICStreamManager::QUICStreamManager(QUICContext *context, QUICApplicationMap *app_map) + : _stream_factory(context->rtt_provider(), context->connection_info()), _context(context), _app_map(app_map) { - if (this->_info->direction() == NET_VCONNECTION_OUT) { + if (this->_context->connection_info()->direction() == NET_VCONNECTION_OUT) { this->_next_stream_id_bidi = static_cast(QUICStreamType::CLIENT_BIDI); this->_next_stream_id_uni = static_cast(QUICStreamType::CLIENT_UNI); } else { @@ -265,12 +265,13 @@ QUICStreamManager::_find_or_create_stream_vc(QUICStreamId stream_id) uint64_t local_max_stream_data = 0; uint64_t remote_max_stream_data = 0; + uint64_t nth_stream = this->_stream_id_to_nth_stream(stream_id); switch (QUICTypeUtil::detect_stream_type(stream_id)) { case QUICStreamType::CLIENT_BIDI: - if (this->_info->direction() == NET_VCONNECTION_OUT) { + if (this->_context->connection_info()->direction() == NET_VCONNECTION_OUT) { // client - if (this->_remote_max_streams_bidi == 0 || stream_id > this->_remote_max_streams_bidi) { + if (this->_remote_max_streams_bidi == 0 || nth_stream > this->_remote_max_streams_bidi) { return nullptr; } @@ -278,7 +279,7 @@ QUICStreamManager::_find_or_create_stream_vc(QUICStreamId stream_id) remote_max_stream_data = this->_remote_tp->getAsUInt(QUICTransportParameterId::INITIAL_MAX_STREAM_DATA_BIDI_REMOTE); } else { // server - if (this->_local_max_streams_bidi == 0 || stream_id > this->_local_max_streams_bidi) { + if (this->_local_max_streams_bidi == 0 || nth_stream > this->_local_max_streams_bidi) { return nullptr; } @@ -288,14 +289,14 @@ QUICStreamManager::_find_or_create_stream_vc(QUICStreamId stream_id) break; case QUICStreamType::CLIENT_UNI: - if (this->_info->direction() == NET_VCONNECTION_OUT) { + if (this->_context->connection_info()->direction() == NET_VCONNECTION_OUT) { // client - if (this->_remote_max_streams_uni == 0 || stream_id > this->_remote_max_streams_uni) { + if (this->_remote_max_streams_uni == 0 || nth_stream > this->_remote_max_streams_uni) { return nullptr; } } else { // server - if (this->_local_max_streams_uni == 0 || stream_id > this->_local_max_streams_uni) { + if (this->_local_max_streams_uni == 0 || nth_stream > this->_local_max_streams_uni) { return nullptr; } } @@ -305,9 +306,9 @@ QUICStreamManager::_find_or_create_stream_vc(QUICStreamId stream_id) break; case QUICStreamType::SERVER_BIDI: - if (this->_info->direction() == NET_VCONNECTION_OUT) { + if (this->_context->connection_info()->direction() == NET_VCONNECTION_OUT) { // client - if (this->_local_max_streams_bidi == 0 || stream_id > this->_local_max_streams_bidi) { + if (this->_local_max_streams_bidi == 0 || nth_stream > this->_local_max_streams_bidi) { return nullptr; } @@ -315,7 +316,7 @@ QUICStreamManager::_find_or_create_stream_vc(QUICStreamId stream_id) remote_max_stream_data = this->_remote_tp->getAsUInt(QUICTransportParameterId::INITIAL_MAX_STREAM_DATA_BIDI_LOCAL); } else { // server - if (this->_remote_max_streams_bidi == 0 || stream_id > this->_remote_max_streams_bidi) { + if (this->_remote_max_streams_bidi == 0 || nth_stream > this->_remote_max_streams_bidi) { return nullptr; } @@ -324,12 +325,12 @@ QUICStreamManager::_find_or_create_stream_vc(QUICStreamId stream_id) } break; case QUICStreamType::SERVER_UNI: - if (this->_info->direction() == NET_VCONNECTION_OUT) { - if (this->_local_max_streams_uni == 0 || stream_id > this->_local_max_streams_uni) { + if (this->_context->connection_info()->direction() == NET_VCONNECTION_OUT) { + if (this->_local_max_streams_uni == 0 || nth_stream > this->_local_max_streams_uni) { return nullptr; } } else { - if (this->_remote_max_streams_uni == 0 || stream_id > this->_remote_max_streams_uni) { + if (this->_remote_max_streams_uni == 0 || nth_stream > this->_remote_max_streams_uni) { return nullptr; } } @@ -416,6 +417,11 @@ QUICStreamManager::will_generate_frame(QUICEncryptionLevel level, size_t current return false; } + // Don't send DATA frames if current path is not validated + if (!this->_context->path_manager()->get_verified_path().remote_ep().isValid()) { + return false; + } + for (QUICStreamVConnection *s = this->stream_list.head; s; s = s->link.next) { if (s->will_generate_frame(level, current_packet_size, ack_eliciting, seq_num)) { return true; @@ -466,3 +472,9 @@ QUICStreamManager::_is_level_matched(QUICEncryptionLevel level) return false; } + +uint64_t +QUICStreamManager::_stream_id_to_nth_stream(QUICStreamId stream_id) +{ + return (stream_id / 4) + 1; +} diff --git a/iocore/net/quic/QUICStreamManager.h b/iocore/net/quic/QUICStreamManager.h index fb38ecc1309..05da5fc93ab 100644 --- a/iocore/net/quic/QUICStreamManager.h +++ b/iocore/net/quic/QUICStreamManager.h @@ -31,13 +31,15 @@ #include "QUICFrame.h" #include "QUICStreamFactory.h" #include "QUICLossDetector.h" +#include "QUICPathManager.h" +#include "QUICContext.h" class QUICTransportParameters; class QUICStreamManager : public QUICFrameHandler, public QUICFrameGenerator { public: - QUICStreamManager(QUICConnectionInfoProvider *info, QUICRTTProvider *rtt_provider, QUICApplicationMap *app_map); + QUICStreamManager(QUICContext *context, QUICApplicationMap *app_map); void init_flow_control_params(const std::shared_ptr &local_tp, const std::shared_ptr &remote_tp); @@ -82,7 +84,7 @@ class QUICStreamManager : public QUICFrameHandler, public QUICFrameGenerator QUICStreamFactory _stream_factory; - QUICConnectionInfoProvider *_info = nullptr; + QUICContext *_context = nullptr; QUICApplicationMap *_app_map = nullptr; std::shared_ptr _local_tp = nullptr; std::shared_ptr _remote_tp = nullptr; @@ -97,4 +99,6 @@ class QUICStreamManager : public QUICFrameHandler, public QUICFrameGenerator QUICEncryptionLevel::ZERO_RTT, QUICEncryptionLevel::ONE_RTT, }; + + uint64_t _stream_id_to_nth_stream(QUICStreamId stream_id); }; diff --git a/iocore/net/quic/QUICTLS.cc b/iocore/net/quic/QUICTLS.cc index 5736ef46191..b0317f27a5e 100644 --- a/iocore/net/quic/QUICTLS.cc +++ b/iocore/net/quic/QUICTLS.cc @@ -32,6 +32,55 @@ constexpr static char tag[] = "quic_tls"; +static const char * +content_type_str(int type) +{ + switch (type) { + case SSL3_RT_CHANGE_CIPHER_SPEC: + return "CHANGE_CIPHER_SPEC"; + case SSL3_RT_ALERT: + return "ALERT"; + case SSL3_RT_HANDSHAKE: + return "HANDSHAKE"; + case SSL3_RT_APPLICATION_DATA: + return "APPLICATION_DATA"; + case SSL3_RT_HEADER: + // The buf contains the record header bytes only + return "HEADER"; + default: + return "UNKNOWN"; + } +} + +static const char * +hs_type_str(int type) +{ + switch (type) { + case SSL3_MT_CLIENT_HELLO: + return "CLIENT_HELLO"; + case SSL3_MT_SERVER_HELLO: + return "SERVER_HELLO"; + case SSL3_MT_NEWSESSION_TICKET: + return "NEWSESSION_TICKET"; + case SSL3_MT_END_OF_EARLY_DATA: + return "END_OF_EARLY_DATA"; + case SSL3_MT_ENCRYPTED_EXTENSIONS: + return "ENCRYPTED_EXTENSIONS"; + case SSL3_MT_CERTIFICATE: + return "CERTIFICATE"; + case SSL3_MT_CERTIFICATE_VERIFY: + return "CERTIFICATE_VERIFY"; + case SSL3_MT_FINISHED: + return "FINISHED"; + case SSL3_MT_KEY_UPDATE: + return "KEY_UPDATE"; + case SSL3_MT_MESSAGE_HASH: + return "MESSAGE_HASH"; + default: + return "UNKNOWN"; + } +} + SSL * QUICTLS::ssl_handle() { @@ -50,12 +99,6 @@ QUICTLS::remote_transport_parameters() return this->_remote_transport_parameters; } -void -QUICTLS::set_local_transport_parameters(std::shared_ptr tp) -{ - this->_local_transport_parameters = tp; -} - void QUICTLS::set_remote_transport_parameters(std::shared_ptr tp) { @@ -79,16 +122,30 @@ QUICTLS::~QUICTLS() SSL_free(this->_ssl); } +int +QUICTLS::handshake(QUICHandshakeMsgs **out, const QUICHandshakeMsgs *in) +{ + if (this->is_handshake_finished()) { + if (in != nullptr && in->offsets[4] != 0) { + return this->_process_post_handshake_messages(*out, in); + } + + return 0; + } + + return this->_handshake(out, in); +} + void QUICTLS::reset() { SSL_clear(this->_ssl); } -uint16_t +uint64_t QUICTLS::convert_to_quic_trans_error_code(uint8_t alert) { - return 0x100 | alert; + return static_cast(QUICTransErrorCode::CRYPTO_ERROR) + alert; } bool @@ -114,7 +171,9 @@ QUICTLS::initialize_key_materials(QUICConnectionId cid) this->_pp_key_info.set_cipher_for_hp_initial(EVP_aes_128_ecb()); // Generate keys - Debug(tag, "Generating %s keys", QUICDebugNames::key_phase(QUICKeyPhase::INITIAL)); + if (is_debug_tag_set(tag)) { + Debug(tag, "Generating %s keys with cid %s", QUICDebugNames::key_phase(QUICKeyPhase::INITIAL), cid.hex().c_str()); + } uint8_t *client_key_for_hp; uint8_t *client_key; @@ -172,6 +231,127 @@ QUICTLS::initialize_key_materials(QUICConnectionId cid) return 1; } +void +QUICTLS::update_negotiated_cipher() +{ + this->_store_negotiated_cipher(); + this->_store_negotiated_cipher_for_hp(); +} + +void +QUICTLS::update_key_materials_for_read(QUICEncryptionLevel level, const uint8_t *secret, size_t secret_len) +{ + if (this->_state == HandshakeState::ABORTED) { + return; + } + + QUICKeyPhase phase; + const EVP_CIPHER *cipher; + + switch (level) { + case QUICEncryptionLevel::ZERO_RTT: + phase = QUICKeyPhase::ZERO_RTT; + cipher = this->_pp_key_info.get_cipher(phase); + break; + case QUICEncryptionLevel::HANDSHAKE: + this->_update_encryption_level(QUICEncryptionLevel::HANDSHAKE); + phase = QUICKeyPhase::HANDSHAKE; + break; + case QUICEncryptionLevel::ONE_RTT: + this->_update_encryption_level(QUICEncryptionLevel::ONE_RTT); + // TODO Support Key Update + phase = QUICKeyPhase::PHASE_0; + break; + default: + phase = QUICKeyPhase::INITIAL; + break; + } + + QUICHKDF hkdf(this->_get_handshake_digest()); + + uint8_t *key_for_hp; + uint8_t *key; + uint8_t *iv; + size_t key_for_hp_len; + size_t key_len; + size_t *iv_len; + + cipher = this->_pp_key_info.get_cipher(phase); + key_for_hp = this->_pp_key_info.decryption_key_for_hp(phase); + key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(phase); + key = this->_pp_key_info.decryption_key(phase); + key_len = this->_pp_key_info.decryption_key_len(phase); + iv = this->_pp_key_info.decryption_iv(phase); + iv_len = this->_pp_key_info.decryption_iv_len(phase); + + if (this->_netvc_context == NET_VCONNECTION_IN) { + this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); + this->_print_km("update - client", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len, phase); + } else { + this->_keygen_for_server.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); + this->_print_km("update - server", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len, phase); + } + + this->_pp_key_info.set_decryption_key_available(phase); +} + +void +QUICTLS::update_key_materials_for_write(QUICEncryptionLevel level, const uint8_t *secret, size_t secret_len) +{ + if (this->_state == HandshakeState::ABORTED) { + return; + } + + QUICKeyPhase phase; + const EVP_CIPHER *cipher = nullptr; + + switch (level) { + case QUICEncryptionLevel::ZERO_RTT: + phase = QUICKeyPhase::ZERO_RTT; + cipher = this->_pp_key_info.get_cipher(phase); + break; + case QUICEncryptionLevel::HANDSHAKE: + this->_update_encryption_level(QUICEncryptionLevel::HANDSHAKE); + phase = QUICKeyPhase::HANDSHAKE; + cipher = this->_pp_key_info.get_cipher(phase); + break; + case QUICEncryptionLevel::ONE_RTT: + this->_update_encryption_level(QUICEncryptionLevel::ONE_RTT); + phase = QUICKeyPhase::PHASE_0; + cipher = this->_pp_key_info.get_cipher(phase); + break; + default: + phase = QUICKeyPhase::INITIAL; + break; + } + + QUICHKDF hkdf(this->_get_handshake_digest()); + + uint8_t *key_for_hp; + uint8_t *key; + uint8_t *iv; + size_t key_for_hp_len; + size_t key_len; + size_t *iv_len; + + key_for_hp = this->_pp_key_info.encryption_key_for_hp(phase); + key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(phase); + key = this->_pp_key_info.encryption_key(phase); + key_len = this->_pp_key_info.encryption_key_len(phase); + iv = this->_pp_key_info.encryption_iv(phase); + iv_len = this->_pp_key_info.encryption_iv_len(phase); + + if (this->_netvc_context == NET_VCONNECTION_IN) { + this->_keygen_for_server.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); + this->_print_km("update - server", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len, phase); + } else { + this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); + this->_print_km("update - client", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len, phase); + } + + this->_pp_key_info.set_encryption_key_available(phase); +} + const char * QUICTLS::negotiated_cipher_suite() const { @@ -198,6 +378,118 @@ QUICTLS::abort_handshake() return; } +void +QUICTLS::set_ready_for_write() +{ + this->_should_flush = true; +} + +void +QUICTLS::on_handshake_data_generated(QUICEncryptionLevel level, const uint8_t *data, size_t len) +{ + int index = static_cast(level); + int next_index = index + 1; + + size_t offset = this->_out.offsets[next_index]; + size_t next_level_offset = offset + len; + + memcpy(this->_out.buf + offset, data, len); + + for (int i = next_index; i < 5; ++i) { + this->_out.offsets[i] = next_level_offset; + } +} + +void +QUICTLS::on_tls_alert(uint8_t alert) +{ + this->_has_crypto_error = true; + this->_crypto_error = QUICTLS::convert_to_quic_trans_error_code(alert); +} + +bool +QUICTLS::has_crypto_error() const +{ + return this->_has_crypto_error; +} + +uint64_t +QUICTLS::crypto_error() const +{ + return this->_crypto_error; +} + +int +QUICTLS::_handshake(QUICHandshakeMsgs **out, const QUICHandshakeMsgs *in) +{ + ink_assert(this->_ssl != nullptr); + if (this->_state == HandshakeState::ABORTED) { + return 0; + } + + int err = SSL_ERROR_NONE; + ERR_clear_error(); + int ret = 0; + + SSL_set_msg_callback(this->_ssl, QUICTLS::_msg_cb); + + this->_out.offsets[0] = 0; + this->_out.offsets[1] = 0; + this->_out.offsets[2] = 0; + this->_out.offsets[3] = 0; + this->_out.offsets[4] = 0; + + if (in) { + this->_pass_quic_data_to_ssl_impl(*in); + } + + if (this->_netvc_context == NET_VCONNECTION_IN) { + if (!this->_early_data_processed) { + if (auto ret = this->_read_early_data(); ret == 0) { + this->_early_data_processed = true; + } else if (ret < 0) { + return 0; + } else { + // Early data is not arrived yet -- can be multiple initial packets + } + } + + ret = SSL_accept(this->_ssl); + } else { + if (!this->_early_data_processed) { + if (this->_write_early_data()) { + this->_early_data_processed = true; + } + } + + ret = SSL_connect(this->_ssl); + } + + if (ret <= 0) { + err = SSL_get_error(this->_ssl, ret); + + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + break; + default: + char err_buf[256] = {0}; + ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); + Debug(tag, "Handshake: %s", err_buf); + return ret; + } + } + + if (this->_should_flush) { + this->_should_flush = false; + *out = &this->_out; + } else { + *out = nullptr; + } + + return 1; +} + void QUICTLS::_update_encryption_level(QUICEncryptionLevel level) { @@ -210,10 +502,10 @@ QUICTLS::_update_encryption_level(QUICEncryptionLevel level) void QUICTLS::_print_km(const char *header, const uint8_t *key_for_hp, size_t key_for_hp_len, const uint8_t *key, size_t key_len, - const uint8_t *iv, size_t iv_len, const uint8_t *secret, size_t secret_len) + const uint8_t *iv, size_t iv_len, const uint8_t *secret, size_t secret_len, QUICKeyPhase phase) { if (is_debug_tag_set("vv_quic_crypto")) { - Debug("vv_quic_crypto", "%s", header); + Debug("vv_quic_crypto", "%s - %s", header, QUICDebugNames::key_phase(phase)); uint8_t print_buf[128]; if (secret) { QUICDebug::to_hex(print_buf, static_cast(secret), secret_len); @@ -227,3 +519,12 @@ QUICTLS::_print_km(const char *header, const uint8_t *key_for_hp, size_t key_for Debug("vv_quic_crypto", "hp=%s", print_buf); } } + +void +QUICTLS::_print_hs_message(int content_type, const void *buf, size_t len) +{ + if ((content_type == SSL3_RT_HANDSHAKE || content_type == SSL3_RT_ALERT)) { + int msg_type = reinterpret_cast(buf)[0]; + Debug(tag, "%s (%d), %s (%d) len=%zu", content_type_str(content_type), content_type, hs_type_str(msg_type), msg_type, len); + } +} diff --git a/iocore/net/quic/QUICTLS.h b/iocore/net/quic/QUICTLS.h index 7bba45aff38..e60d9206c8f 100644 --- a/iocore/net/quic/QUICTLS.h +++ b/iocore/net/quic/QUICTLS.h @@ -36,6 +36,9 @@ #include "I_NetVConnection.h" #include "QUICHandshakeProtocol.h" +// TODO: fix size +static constexpr int MAX_HANDSHAKE_MSG_LEN = 65527; + class QUICTLS : public QUICHandshakeProtocol { public: @@ -50,7 +53,7 @@ class QUICTLS : public QUICHandshakeProtocol }; static QUICEncryptionLevel get_encryption_level(int msg_type); - static uint16_t convert_to_quic_trans_error_code(uint8_t alert); + static uint64_t convert_to_quic_trans_error_code(uint8_t alert); std::shared_ptr local_transport_parameters() override; std::shared_ptr remote_transport_parameters() override; @@ -64,16 +67,25 @@ class QUICTLS : public QUICHandshakeProtocol SSL *ssl_handle(); // QUICHandshakeProtocol - int handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) override; + int handshake(QUICHandshakeMsgs **out, const QUICHandshakeMsgs *in) override; void reset() override; bool is_handshake_finished() const override; bool is_ready_to_derive() const override; int initialize_key_materials(QUICConnectionId cid) override; - void update_key_materials_on_key_cb(int name, const uint8_t *secret, size_t secret_len); + void update_negotiated_cipher(); + void update_key_materials_for_read(QUICEncryptionLevel level, const uint8_t *secret, size_t secret_len); + void update_key_materials_for_write(QUICEncryptionLevel level, const uint8_t *secret, size_t secret_len); const char *negotiated_cipher_suite() const override; void negotiated_application_name(const uint8_t **name, unsigned int *len) const override; QUICEncryptionLevel current_encryption_level() const override; void abort_handshake() override; + bool has_crypto_error() const override; + uint64_t crypto_error() const override; + + void set_ready_for_write(); + + void on_handshake_data_generated(QUICEncryptionLevel level, const uint8_t *data, size_t len); + void on_tls_alert(uint8_t alert); private: QUICKeyGenerator _keygen_for_client = QUICKeyGenerator(QUICKeyGenerator::Context::CLIENT); @@ -82,7 +94,8 @@ class QUICTLS : public QUICHandshakeProtocol int _read_early_data(); int _write_early_data(); - int _handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in); + void _pass_quic_data_to_ssl_impl(const QUICHandshakeMsgs &in); + int _handshake(QUICHandshakeMsgs **out, const QUICHandshakeMsgs *in); int _process_post_handshake_messages(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in); void _generate_0rtt_key(); void _update_encryption_level(QUICEncryptionLevel level); @@ -91,8 +104,11 @@ class QUICTLS : public QUICHandshakeProtocol void _store_negotiated_cipher_for_hp(); void _print_km(const char *header, const uint8_t *key_for_hp, size_t key_for_hp_len, const uint8_t *key, size_t key_len, - const uint8_t *iv, size_t iv_len, const uint8_t *secret = nullptr, size_t secret_len = 0); + const uint8_t *iv, size_t iv_len, const uint8_t *secret = nullptr, size_t secret_len = 0, + QUICKeyPhase phase = QUICKeyPhase::INITIAL); + static void _print_hs_message(int content_type, const void *buf, size_t len); + static void _msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg); const char *_session_file = nullptr; const char *_keylog_file = nullptr; SSL *_ssl = nullptr; @@ -105,4 +121,10 @@ class QUICTLS : public QUICHandshakeProtocol std::shared_ptr _local_transport_parameters = nullptr; std::shared_ptr _remote_transport_parameters = nullptr; + + uint8_t _out_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; + QUICHandshakeMsgs _out = {_out_buf, MAX_HANDSHAKE_MSG_LEN, {0}, 0}; + bool _should_flush = false; + bool _has_crypto_error = false; + uint64_t _crypto_error = 0; }; diff --git a/iocore/net/quic/QUICTLS_boringssl.cc b/iocore/net/quic/QUICTLS_boringssl.cc index c46b5f256af..46584384801 100644 --- a/iocore/net/quic/QUICTLS_boringssl.cc +++ b/iocore/net/quic/QUICTLS_boringssl.cc @@ -29,7 +29,141 @@ #include #include -// static constexpr char tag[] = "quic_tls"; +#include "QUICGlobals.h" +#include "QUICPacketProtectionKeyInfo.h" + +static constexpr char tag[] = "quic_tls"; + +static QUICEncryptionLevel +convert_level_ats2ssl(enum ssl_encryption_level_t level) +{ + switch (level) { + case ssl_encryption_initial: + return QUICEncryptionLevel::INITIAL; + case ssl_encryption_early_data: + return QUICEncryptionLevel::ZERO_RTT; + case ssl_encryption_handshake: + return QUICEncryptionLevel::HANDSHAKE; + case ssl_encryption_application: + return QUICEncryptionLevel::ONE_RTT; + default: + return QUICEncryptionLevel::NONE; + } +} + +#if BORINGSSL_API_VERSION >= 10 +static int +set_read_secret(SSL *ssl, enum ssl_encryption_level_t level, const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len) +{ + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + + qtls->update_negotiated_cipher(); + + QUICEncryptionLevel ats_level = convert_level_ats2ssl(level); + qtls->update_key_materials_for_read(ats_level, secret, secret_len); + + return 1; +} + +static int +set_write_secret(SSL *ssl, enum ssl_encryption_level_t level, const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len) +{ + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + + qtls->update_negotiated_cipher(); + + QUICEncryptionLevel ats_level = convert_level_ats2ssl(level); + qtls->update_key_materials_for_write(ats_level, secret, secret_len); + + if (ats_level == QUICEncryptionLevel::ONE_RTT) { + // FIXME Where should this be placed? + const uint8_t *tp_buf; + size_t tp_buf_len; + SSL_get_peer_quic_transport_params(ssl, &tp_buf, &tp_buf_len); + if (SSL_is_server(ssl)) { + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + } else { + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + } + } + + return 1; +} +#else +static int +set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level, const uint8_t *read_secret, const uint8_t *write_secret, + size_t secret_len) +{ + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + + qtls->update_negotiated_cipher(); + + QUICEncryptionLevel ats_level = convert_level_ats2ssl(level); + if (read_secret) { + qtls->update_key_materials_for_read(ats_level, read_secret, secret_len); + } + if (write_secret) { + qtls->update_key_materials_for_write(ats_level, write_secret, secret_len); + } + + if (ats_level == QUICEncryptionLevel::ONE_RTT) { + // FIXME Where should this be placed? + const uint8_t *tp_buf; + size_t tp_buf_len; + SSL_get_peer_quic_transport_params(ssl, &tp_buf, &tp_buf_len); + if (SSL_is_server(ssl)) { + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + } else { + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + } + } + + return 1; +} +#endif + +static int +add_handshake_data(SSL *ssl, enum ssl_encryption_level_t level, const uint8_t *data, size_t len) +{ + QUICEncryptionLevel ats_level = convert_level_ats2ssl(level); + + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + qtls->on_handshake_data_generated(ats_level, data, len); + + return 1; +} + +static int +flush_flight(SSL *ssl) +{ + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + qtls->set_ready_for_write(); + + return 1; +} + +static int +send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert) +{ + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + qtls->on_tls_alert(alert); + return 1; +} + +#if BORINGSSL_API_VERSION >= 10 +static const SSL_QUIC_METHOD quic_method = {set_read_secret, set_write_secret, add_handshake_data, flush_flight, send_alert}; +#else +static const SSL_QUIC_METHOD quic_method = {set_encryption_secrets, add_handshake_data, flush_flight, send_alert}; +#endif + +void +QUICTLS::_msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) +{ + // Debug for reading + if (write_p == 0) { + QUICTLS::_print_hs_message(content_type, buf, len); + } +} QUICTLS::QUICTLS(QUICPacketProtectionKeyInfo &pp_key_info, SSL_CTX *ssl_ctx, NetVConnectionContext_t nvc_ctx, const NetVCOptions &netvc_options, const char *session_file, const char *keylog_file) @@ -40,140 +174,188 @@ QUICTLS::QUICTLS(QUICPacketProtectionKeyInfo &pp_key_info, SSL_CTX *ssl_ctx, Net _netvc_context(nvc_ctx) { ink_assert(this->_netvc_context != NET_VCONNECTION_UNSET); + + if (this->_netvc_context == NET_VCONNECTION_OUT) { + SSL_set_connect_state(this->_ssl); + + SSL_set_alpn_protos(this->_ssl, reinterpret_cast(netvc_options.alpn_protos.data()), + netvc_options.alpn_protos.size()); + const ats_scoped_str &tlsext_host_name = netvc_options.sni_hostname ? netvc_options.sni_hostname : netvc_options.sni_servername; + SSL_set_tlsext_host_name(this->_ssl, tlsext_host_name.get()); + } else { + SSL_set_accept_state(this->_ssl); + } + + SSL_set_ex_data(this->_ssl, QUIC::ssl_quic_tls_index, this); + SSL_set_quic_method(this->_ssl, &quic_method); + SSL_set_early_data_enabled(this->_ssl, 1); + + if (session_file && this->_netvc_context == NET_VCONNECTION_OUT) { + auto file = BIO_new_file(session_file, "r"); + if (file == nullptr) { + Debug(tag, "Could not read tls session file %s", session_file); + return; + } + + auto session = PEM_read_bio_SSL_SESSION(file, nullptr, nullptr, nullptr); + if (session == nullptr) { + Debug(tag, "Could not read tls session file %s", session_file); + } else { + if (!SSL_set_session(this->_ssl, session)) { + Debug(tag, "Session resumption failed : %s", session_file); + } else { + Debug(tag, "Session resumption success : %s", session_file); + this->_is_session_reused = true; + } + SSL_SESSION_free(session); + } + + BIO_free(file); + } } -int -QUICTLS::handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) +void +QUICTLS::set_local_transport_parameters(std::shared_ptr tp) { - ink_assert(false); - return 0; + this->_local_transport_parameters = tp; + + uint8_t buf[UINT16_MAX]; + uint16_t len; + this->_local_transport_parameters->store(buf, &len); + SSL_set_quic_transport_params(this->_ssl, buf, len); } int QUICTLS::_process_post_handshake_messages(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) { - ink_assert(false); - return 0; + this->_pass_quic_data_to_ssl_impl(*in); + return SSL_process_quic_post_handshake(this->_ssl); } -int -QUICTLS::_read_early_data() +void +QUICTLS::_store_negotiated_cipher() { - uint8_t early_data[8]; - do { - ERR_clear_error(); - SSL_read(this->_ssl, early_data, sizeof(early_data)); - } while (SSL_in_early_data(this->_ssl)); + ink_assert(this->_ssl); - return 1; -} + const EVP_CIPHER *cipher = nullptr; + size_t tag_len = 0; + const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); -/* -const EVP_AEAD * -QUICTLS::_get_evp_aead(QUICKeyPhase phase) const -{ - if (phase == QUICKeyPhase::INITIAL) { - return EVP_aead_aes_128_gcm(); - } else { - const SSL_CIPHER *cipher = SSL_get_current_cipher(this->_ssl); - if (cipher) { - switch (SSL_CIPHER_get_id(cipher)) { - case TLS1_CK_AES_128_GCM_SHA256: - return EVP_aead_aes_128_gcm(); - case TLS1_CK_AES_256_GCM_SHA384: - return EVP_aead_aes_256_gcm(); - case TLS1_CK_CHACHA20_POLY1305_SHA256: - return EVP_aead_chacha20_poly1305(); - default: - ink_assert(false); - return nullptr; - } - } else { + if (ssl_cipher) { + switch (SSL_CIPHER_get_id(ssl_cipher)) { + case TLS1_CK_AES_128_GCM_SHA256: + cipher = EVP_aes_128_gcm(); + tag_len = EVP_GCM_TLS_TAG_LEN; + break; + case TLS1_CK_AES_256_GCM_SHA384: + cipher = EVP_aes_256_gcm(); + tag_len = EVP_GCM_TLS_TAG_LEN; + break; + case TLS1_CK_CHACHA20_POLY1305_SHA256: + // cipher = EVP_chacha20_poly1305(); + cipher = nullptr; + tag_len = 16; + break; + default: ink_assert(false); - return nullptr; } + } else { + ink_assert(false); } + + this->_pp_key_info.set_cipher(cipher, tag_len); } -size_t -QUICTLS::_get_aead_tag_len(QUICKeyPhase phase) const +void +QUICTLS::_store_negotiated_cipher_for_hp() { - if (phase == QUICKeyPhase::INITIAL) { - return EVP_GCM_TLS_TAG_LEN; - } else { - const SSL_CIPHER *cipher = SSL_get_current_cipher(this->_ssl); - if (cipher) { - switch (SSL_CIPHER_get_id(cipher)) { - case TLS1_CK_AES_128_GCM_SHA256: - case TLS1_CK_AES_256_GCM_SHA384: - return EVP_GCM_TLS_TAG_LEN; - case TLS1_CK_CHACHA20_POLY1305_SHA256: - return 16; - default: - ink_assert(false); - return -1; - } - } else { + ink_assert(this->_ssl); + + const EVP_CIPHER *cipher_for_hp = nullptr; + const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); + + if (ssl_cipher) { + switch (SSL_CIPHER_get_id(ssl_cipher)) { + case TLS1_CK_AES_128_GCM_SHA256: + cipher_for_hp = EVP_aes_128_ecb(); + break; + case TLS1_CK_AES_256_GCM_SHA384: + cipher_for_hp = EVP_aes_256_ecb(); + break; + case TLS1_CK_CHACHA20_POLY1305_SHA256: + // cipher_for_hp = EVP_chacha20(); + cipher_for_hp = nullptr; + break; + default: ink_assert(false); - return -1; + break; } + } else { + ink_assert(false); } + + this->_pp_key_info.set_cipher_for_hp(cipher_for_hp); } -const EVP_MD * -QUICKeyGenerator::_get_handshake_digest() +int +QUICTLS::_read_early_data() { - // TODO not implemented - return nullptr; + // This is for Hacked OpenSSL. Do nothing here. + return 1; } -bool -QUICTLS::_encrypt(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len, const uint8_t *plain, size_t plain_len, - uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const KeyMaterial &km, const EVP_AEAD *aead, - size_t tag_len) const +int +QUICTLS::_write_early_data() { - uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; - size_t nonce_len = 0; - _gen_nonce(nonce, nonce_len, pkt_num, km.iv, km.iv_len); - - EVP_AEAD_CTX *aead_ctx = EVP_AEAD_CTX_new(aead, km.key, km.key_len, tag_len); - if (!aead_ctx) { - Debug(tag, "Failed to create EVP_AEAD_CTX"); - return false; - } - - if (!EVP_AEAD_CTX_seal(aead_ctx, cipher, &cipher_len, max_cipher_len, nonce, nonce_len, plain, plain_len, ad, ad_len)) { - Debug(tag, "Failed to encrypt"); - return false; - } - - EVP_AEAD_CTX_free(aead_ctx); - - return true; + // This is for Hacked OpenSSL. Do nothing here. + return 1; } -bool -QUICTLS::_decrypt(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *cipher, size_t cipher_len, - uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const KeyMaterial &km, const EVP_AEAD *aead, - size_t tag_len) const +void +QUICTLS::_pass_quic_data_to_ssl_impl(const QUICHandshakeMsgs &in) { - uint8_t nonce[EVP_MAX_IV_LENGTH] = {0}; - size_t nonce_len = 0; - _gen_nonce(nonce, nonce_len, pkt_num, km.iv, km.iv_len); - - EVP_AEAD_CTX *aead_ctx = EVP_AEAD_CTX_new(aead, km.key, km.key_len, tag_len); - if (!aead_ctx) { - Debug(tag, "Failed to create EVP_AEAD_CTX"); - return false; + for (auto level : QUIC_ENCRYPTION_LEVELS) { + int index = static_cast(level); + ssl_encryption_level_t ossl_level; + switch (level) { + case QUICEncryptionLevel::INITIAL: + ossl_level = ssl_encryption_initial; + break; + case QUICEncryptionLevel::ZERO_RTT: + ossl_level = ssl_encryption_early_data; + break; + case QUICEncryptionLevel::HANDSHAKE: + ossl_level = ssl_encryption_handshake; + break; + case QUICEncryptionLevel::ONE_RTT: + ossl_level = ssl_encryption_application; + break; + default: + // Should not be happend + ossl_level = ssl_encryption_application; + break; + } + if (in.offsets[index + 1] - in.offsets[index]) { + int start = 0; + for (int i = 0; i < index; ++i) { + start += in.offsets[index]; + } + SSL_provide_quic_data(this->_ssl, ossl_level, in.buf + start, in.offsets[index + 1] - in.offsets[index]); + } } +} - if (!EVP_AEAD_CTX_open(aead_ctx, plain, &plain_len, max_plain_len, nonce, nonce_len, cipher, cipher_len, ad, ad_len)) { - Debug(tag, "Failed to decrypt"); - return false; +const EVP_MD * +QUICTLS::_get_handshake_digest() const +{ + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(this->_ssl))) { + case TLS1_CK_AES_128_GCM_SHA256: + case TLS1_CK_CHACHA20_POLY1305_SHA256: + return EVP_sha256(); + case TLS1_CK_AES_256_GCM_SHA384: + return EVP_sha384(); + default: + ink_assert(false); + return nullptr; } - - EVP_AEAD_CTX_free(aead_ctx); - - return true; } -*/ diff --git a/iocore/net/quic/QUICTLS_legacy.cc b/iocore/net/quic/QUICTLS_legacy.cc new file mode 100644 index 00000000000..3b6e8ea4fca --- /dev/null +++ b/iocore/net/quic/QUICTLS_legacy.cc @@ -0,0 +1,445 @@ +/** @file + * + * QUIC Crypto (TLS to Secure QUIC) using OpenSSL + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "QUICTLS.h" + +#include +#include +#include +#include +#include + +#include "QUICConfig.h" +#include "QUICGlobals.h" +#include "QUICDebugNames.h" +#include "QUICPacketProtectionKeyInfo.h" + +static constexpr char tag[] = "quic_tls"; + +using namespace std::literals; + +static constexpr std::string_view QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_EARLY_TRAFFIC_SECRET"sv); +static constexpr std::string_view QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET"sv); +static constexpr std::string_view QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL("QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET"sv); +// TODO: support key update +static constexpr std::string_view QUIC_CLIENT_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_TRAFFIC_SECRET_0"sv); +static constexpr std::string_view QUIC_SERVER_TRAFFIC_SECRET_LABEL("QUIC_SERVER_TRAFFIC_SECRET_0"sv); + +void +QUICTLS::_msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) +{ + // Debug for reading + if (write_p == 0) { + QUICTLS::_print_hs_message(content_type, buf, len); + return; + } + + if (!write_p || (content_type != SSL3_RT_HANDSHAKE && content_type != SSL3_RT_ALERT)) { + return; + } + + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + const uint8_t *data = reinterpret_cast(buf); + if (content_type == SSL3_RT_HANDSHAKE) { + if (version != TLS1_3_VERSION) { + return; + } + + QUICEncryptionLevel level = QUICTLS::get_encryption_level(data[0]); + qtls->on_handshake_data_generated(level, data, len); + qtls->set_ready_for_write(); + } else if (content_type == SSL3_RT_ALERT && data[0] == SSL3_AL_FATAL && len == 2) { + qtls->on_tls_alert(data[1]); + } + + return; +} + +/** + This is very inspired from writting keylog format of ngtcp2's examples + https://github.com/ngtcp2/ngtcp2/blob/894ed23c970d61eede74f69d9178090af63fdf70/examples/keylog.cc + */ +static void +log_secret(SSL *ssl, int name, const unsigned char *secret, size_t secretlen) +{ + if (auto keylog_cb = SSL_CTX_get_keylog_callback(SSL_get_SSL_CTX(ssl))) { + unsigned char crandom[32]; + if (SSL_get_client_random(ssl, crandom, sizeof(crandom)) != sizeof(crandom)) { + return; + } + uint8_t line[256] = {0}; + size_t len = 0; + switch (name) { + case SSL_KEY_CLIENT_EARLY_TRAFFIC: + memcpy(line, QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.size(); + break; + case SSL_KEY_CLIENT_HANDSHAKE_TRAFFIC: + memcpy(line, QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.size(); + break; + case SSL_KEY_SERVER_HANDSHAKE_TRAFFIC: + memcpy(line, QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.data(), QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.size(); + break; + case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: + memcpy(line, QUIC_CLIENT_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_CLIENT_TRAFFIC_SECRET_LABEL.size(); + break; + case SSL_KEY_SERVER_APPLICATION_TRAFFIC: + memcpy(line, QUIC_SERVER_TRAFFIC_SECRET_LABEL.data(), QUIC_SERVER_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_SERVER_TRAFFIC_SECRET_LABEL.size(); + break; + + default: + return; + } + + line[len] = ' '; + ++len; + QUICDebug::to_hex(line + len, crandom, sizeof(crandom)); + len += sizeof(crandom) * 2; + line[len] = ' '; + ++len; + QUICDebug::to_hex(line + len, secret, secretlen); + + keylog_cb(ssl, reinterpret_cast(line)); + } +} + +static int +key_cb(SSL *ssl, int name, const unsigned char *secret, size_t secret_len, void *arg) +{ + if (arg == nullptr) { + return 0; + } + + QUICTLS *qtls = reinterpret_cast(arg); + + qtls->update_negotiated_cipher(); + + QUICEncryptionLevel level; + switch (name) { + case SSL_KEY_CLIENT_EARLY_TRAFFIC: + Debug("vv_quic_crypto", "%s", QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.data()); + level = QUICEncryptionLevel::ZERO_RTT; + if (SSL_is_server(ssl)) { + qtls->update_key_materials_for_read(level, secret, secret_len); + } else { + qtls->update_key_materials_for_write(level, secret, secret_len); + } + break; + case SSL_KEY_CLIENT_HANDSHAKE_TRAFFIC: + Debug("vv_quic_crypto", "%s", QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.data()); + level = QUICEncryptionLevel::HANDSHAKE; + if (SSL_is_server(ssl)) { + qtls->update_key_materials_for_read(level, secret, secret_len); + } else { + qtls->update_key_materials_for_write(level, secret, secret_len); + } + break; + case SSL_KEY_SERVER_HANDSHAKE_TRAFFIC: + Debug("vv_quic_crypto", "%s", QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.data()); + level = QUICEncryptionLevel::HANDSHAKE; + if (SSL_is_server(ssl)) { + qtls->update_key_materials_for_write(level, secret, secret_len); + } else { + qtls->update_key_materials_for_read(level, secret, secret_len); + } + break; + case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: + Debug("vv_quic_crypto", "%s", QUIC_CLIENT_TRAFFIC_SECRET_LABEL.data()); + level = QUICEncryptionLevel::ONE_RTT; + if (SSL_is_server(ssl)) { + qtls->update_key_materials_for_read(level, secret, secret_len); + } else { + qtls->update_key_materials_for_write(level, secret, secret_len); + } + break; + case SSL_KEY_SERVER_APPLICATION_TRAFFIC: + Debug("vv_quic_crypto", "%s", QUIC_SERVER_TRAFFIC_SECRET_LABEL.data()); + level = QUICEncryptionLevel::ONE_RTT; + if (SSL_is_server(ssl)) { + qtls->update_key_materials_for_write(level, secret, secret_len); + } else { + qtls->update_key_materials_for_read(level, secret, secret_len); + } + break; + default: + level = QUICEncryptionLevel::NONE; + break; + } + + log_secret(ssl, name, secret, secret_len); + + return 1; +} + +QUICTLS::QUICTLS(QUICPacketProtectionKeyInfo &pp_key_info, SSL_CTX *ssl_ctx, NetVConnectionContext_t nvc_ctx, + const NetVCOptions &netvc_options, const char *session_file, const char *keylog_file) + : QUICHandshakeProtocol(pp_key_info), + _session_file(session_file), + _keylog_file(keylog_file), + _ssl(SSL_new(ssl_ctx)), + _netvc_context(nvc_ctx) +{ + ink_assert(this->_netvc_context != NET_VCONNECTION_UNSET); + + if (this->_netvc_context == NET_VCONNECTION_OUT) { + SSL_set_connect_state(this->_ssl); + + SSL_set_alpn_protos(this->_ssl, reinterpret_cast(netvc_options.alpn_protos.data()), + netvc_options.alpn_protos.size()); + const ats_scoped_str &tlsext_host_name = netvc_options.sni_hostname ? netvc_options.sni_hostname : netvc_options.sni_servername; + SSL_set_tlsext_host_name(this->_ssl, tlsext_host_name.get()); + } else { + SSL_set_accept_state(this->_ssl); + } + + SSL_set_ex_data(this->_ssl, QUIC::ssl_quic_tls_index, this); + SSL_set_key_callback(this->_ssl, key_cb, this); + + if (session_file && this->_netvc_context == NET_VCONNECTION_OUT) { + auto file = BIO_new_file(session_file, "r"); + if (file == nullptr) { + Debug(tag, "Could not read tls session file %s", session_file); + return; + } + + auto session = PEM_read_bio_SSL_SESSION(file, nullptr, nullptr, nullptr); + if (session == nullptr) { + Debug(tag, "Could not read tls session file %s", session_file); + } else { + if (!SSL_set_session(this->_ssl, session)) { + Debug(tag, "Session resumption failed : %s", session_file); + } else { + Debug(tag, "Session resumption success : %s", session_file); + this->_is_session_reused = true; + } + SSL_SESSION_free(session); + } + + BIO_free(file); + } +} + +QUICEncryptionLevel +QUICTLS::get_encryption_level(int msg_type) +{ + switch (msg_type) { + case SSL3_MT_CLIENT_HELLO: + case SSL3_MT_SERVER_HELLO: + return QUICEncryptionLevel::INITIAL; + case SSL3_MT_END_OF_EARLY_DATA: + return QUICEncryptionLevel::ZERO_RTT; + case SSL3_MT_ENCRYPTED_EXTENSIONS: + case SSL3_MT_CERTIFICATE_REQUEST: + case SSL3_MT_CERTIFICATE: + case SSL3_MT_CERTIFICATE_VERIFY: + case SSL3_MT_FINISHED: + return QUICEncryptionLevel::HANDSHAKE; + case SSL3_MT_KEY_UPDATE: + case SSL3_MT_NEWSESSION_TICKET: + return QUICEncryptionLevel::ONE_RTT; + default: + return QUICEncryptionLevel::NONE; + } +} + +void +QUICTLS::set_local_transport_parameters(std::shared_ptr tp) +{ + this->_local_transport_parameters = tp; +} + +int +QUICTLS::_process_post_handshake_messages(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) +{ + ink_assert(this->_ssl != nullptr); + + int err = SSL_ERROR_NONE; + ERR_clear_error(); + int ret = 0; + + SSL_set_msg_callback(this->_ssl, QUICTLS::_msg_cb); + SSL_set_msg_callback_arg(this->_ssl, out); + + this->_pass_quic_data_to_ssl_impl(*in); + + uint8_t data[2048]; + size_t l = 0; + ret = SSL_read_ex(this->_ssl, data, 2048, &l); + + if (ret <= 0) { + err = SSL_get_error(this->_ssl, ret); + + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + break; + default: + char err_buf[256] = {0}; + ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); + Debug(tag, "Handshake: %s", err_buf); + return ret; + } + } + + return 1; +} + +void +QUICTLS::_store_negotiated_cipher() +{ + ink_assert(this->_ssl); + + const QUIC_EVP_CIPHER *cipher = nullptr; + size_t tag_len = 0; + const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); + + if (ssl_cipher) { + switch (SSL_CIPHER_get_id(ssl_cipher)) { + case TLS1_3_CK_AES_128_GCM_SHA256: + cipher = EVP_aes_128_gcm(); + tag_len = EVP_GCM_TLS_TAG_LEN; + break; + case TLS1_3_CK_AES_256_GCM_SHA384: + cipher = EVP_aes_256_gcm(); + tag_len = EVP_GCM_TLS_TAG_LEN; + break; + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + cipher = EVP_chacha20_poly1305(); + tag_len = EVP_CHACHAPOLY_TLS_TAG_LEN; + break; + case TLS1_3_CK_AES_128_CCM_SHA256: + cipher = EVP_aes_128_ccm(); + tag_len = EVP_GCM_TLS_TAG_LEN; + break; + case TLS1_3_CK_AES_128_CCM_8_SHA256: + cipher = EVP_aes_128_ccm(); + tag_len = EVP_CCM8_TLS_TAG_LEN; + break; + default: + ink_assert(false); + } + } else { + ink_assert(false); + } + + this->_pp_key_info.set_cipher(cipher, tag_len); +} + +void +QUICTLS::_store_negotiated_cipher_for_hp() +{ + ink_assert(this->_ssl); + + const QUIC_EVP_CIPHER *cipher_for_hp = nullptr; + const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); + + if (ssl_cipher) { + switch (SSL_CIPHER_get_id(ssl_cipher)) { + case TLS1_3_CK_AES_128_GCM_SHA256: + cipher_for_hp = EVP_aes_128_ecb(); + break; + case TLS1_3_CK_AES_256_GCM_SHA384: + cipher_for_hp = EVP_aes_256_ecb(); + break; + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + cipher_for_hp = EVP_chacha20(); + break; + case TLS1_3_CK_AES_128_CCM_SHA256: + case TLS1_3_CK_AES_128_CCM_8_SHA256: + cipher_for_hp = EVP_aes_128_ecb(); + break; + default: + ink_assert(false); + break; + } + } else { + ink_assert(false); + } + + this->_pp_key_info.set_cipher_for_hp(cipher_for_hp); +} + +int +QUICTLS::_read_early_data() +{ + uint8_t early_data[8]; + size_t early_data_len = 0; + + // Early data within the TLS connection MUST NOT be used. As it is for other TLS application data, a server MUST treat receiving + // early data on the TLS connection as a connection error of type PROTOCOL_VIOLATION. + int ret = SSL_read_early_data(this->_ssl, early_data, sizeof(early_data), &early_data_len); + // error or reading empty data return 1, otherwise return 0. + if (early_data_len != 0) { + return -1; + } + if (ret == SSL_READ_EARLY_DATA_FINISH) { + return 0; + } else { + return 1; + } +} + +int +QUICTLS::_write_early_data() +{ + size_t early_data_len = 0; + + // Early data within the TLS connection MUST NOT be used. As it is for other TLS application data, a server MUST treat receiving + // early data on the TLS connection as a connection error of type PROTOCOL_VIOLATION. + SSL_write_early_data(this->_ssl, "", 0, &early_data_len); + // always return 1 + return 1; +} + +void +QUICTLS::_pass_quic_data_to_ssl_impl(const QUICHandshakeMsgs &in) +{ + // TODO: set BIO_METHOD which read from QUICHandshakeMsgs directly + BIO *rbio = BIO_new(BIO_s_mem()); + // TODO: set dummy BIO_METHOD which do nothing + BIO *wbio = BIO_new(BIO_s_mem()); + if (in.offsets[4] != 0) { + BIO_write(rbio, in.buf, in.offsets[4]); + } + SSL_set_bio(this->_ssl, rbio, wbio); +} + +const EVP_MD * +QUICTLS::_get_handshake_digest() const +{ + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(this->_ssl))) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + case TLS1_3_CK_AES_128_CCM_SHA256: + case TLS1_3_CK_AES_128_CCM_8_SHA256: + return EVP_sha256(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return EVP_sha384(); + default: + ink_assert(false); + return nullptr; + } +} diff --git a/iocore/net/quic/QUICTLS_openssl.cc b/iocore/net/quic/QUICTLS_openssl.cc index 75e9842c7db..6b8668c299d 100644 --- a/iocore/net/quic/QUICTLS_openssl.cc +++ b/iocore/net/quic/QUICTLS_openssl.cc @@ -28,366 +28,96 @@ #include #include -#include "QUICConfig.h" #include "QUICGlobals.h" -#include "QUICDebugNames.h" #include "QUICPacketProtectionKeyInfo.h" static constexpr char tag[] = "quic_tls"; -using namespace std::literals; - -static constexpr std::string_view QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_EARLY_TRAFFIC_SECRET"sv); -static constexpr std::string_view QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET"sv); -static constexpr std::string_view QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL("QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET"sv); -// TODO: support key update -static constexpr std::string_view QUIC_CLIENT_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_TRAFFIC_SECRET_0"sv); -static constexpr std::string_view QUIC_SERVER_TRAFFIC_SECRET_LABEL("QUIC_SERVER_TRAFFIC_SECRET_0"sv); - -static const char * -content_type_str(int type) +static QUICEncryptionLevel +convert_level_ats2ssl(enum ssl_encryption_level_t level) { - switch (type) { - case SSL3_RT_CHANGE_CIPHER_SPEC: - return "CHANGE_CIPHER_SPEC"; - case SSL3_RT_ALERT: - return "ALERT"; - case SSL3_RT_HANDSHAKE: - return "HANDSHAKE"; - case SSL3_RT_APPLICATION_DATA: - return "APPLICATION_DATA"; - case SSL3_RT_HEADER: - // The buf contains the record header bytes only - return "HEADER"; - case SSL3_RT_INNER_CONTENT_TYPE: - // Used when an encrypted TLSv1.3 record is sent or received. In encrypted TLSv1.3 records the content type in the record header - // is always SSL3_RT_APPLICATION_DATA. The real content type for the record is contained in an "inner" content type. buf - // contains the encoded "inner" content type byte. - return "INNER_CONTENT_TYPE"; - default: - return "UNKNOWN"; - } -} - -static const char * -hs_type_str(int type) -{ - switch (type) { - case SSL3_MT_CLIENT_HELLO: - return "CLIENT_HELLO"; - case SSL3_MT_SERVER_HELLO: - return "SERVER_HELLO"; - case SSL3_MT_NEWSESSION_TICKET: - return "NEWSESSION_TICKET"; - case SSL3_MT_END_OF_EARLY_DATA: - return "END_OF_EARLY_DATA"; - case SSL3_MT_ENCRYPTED_EXTENSIONS: - return "ENCRYPTED_EXTENSIONS"; - case SSL3_MT_CERTIFICATE: - return "CERTIFICATE"; - case SSL3_MT_CERTIFICATE_VERIFY: - return "CERTIFICATE_VERIFY"; - case SSL3_MT_FINISHED: - return "FINISHED"; - case SSL3_MT_KEY_UPDATE: - return "KEY_UPDATE"; - case SSL3_MT_MESSAGE_HASH: - return "MESSAGE_HASH"; + switch (level) { + case ssl_encryption_initial: + return QUICEncryptionLevel::INITIAL; + case ssl_encryption_early_data: + return QUICEncryptionLevel::ZERO_RTT; + case ssl_encryption_handshake: + return QUICEncryptionLevel::HANDSHAKE; + case ssl_encryption_application: + return QUICEncryptionLevel::ONE_RTT; default: - return "UNKNOWN"; + return QUICEncryptionLevel::NONE; } } -static void -msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) +static int +set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level, const uint8_t *read_secret, const uint8_t *write_secret, + size_t secret_len) { - // Debug for reading - if (write_p == 0 && (content_type == SSL3_RT_HANDSHAKE || content_type == SSL3_RT_ALERT)) { - const uint8_t *tmp = reinterpret_cast(buf); - int msg_type = tmp[0]; + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); - Debug(tag, "%s (%d), %s (%d) len=%zu", content_type_str(content_type), content_type, hs_type_str(msg_type), msg_type, len); - return; - } + qtls->update_negotiated_cipher(); - if (!write_p || !arg || (content_type != SSL3_RT_HANDSHAKE && content_type != SSL3_RT_ALERT)) { - return; + QUICEncryptionLevel ats_level = convert_level_ats2ssl(level); + if (read_secret) { + qtls->update_key_materials_for_read(ats_level, read_secret, secret_len); } - - QUICHandshakeMsgs *msg = reinterpret_cast(arg); - if (msg == nullptr) { - return; + if (write_secret) { + qtls->update_key_materials_for_write(ats_level, write_secret, secret_len); } - const uint8_t *msg_buf = reinterpret_cast(buf); - - if (content_type == SSL3_RT_HANDSHAKE) { - if (version != TLS1_3_VERSION) { - return; - } - - int msg_type = msg_buf[0]; - - QUICEncryptionLevel level = QUICTLS::get_encryption_level(msg_type); - int index = static_cast(level); - int next_index = index + 1; - - size_t offset = msg->offsets[next_index]; - size_t next_level_offset = offset + len; - - memcpy(msg->buf + offset, buf, len); - - for (int i = next_index; i < 5; ++i) { - msg->offsets[i] = next_level_offset; + if (ats_level == QUICEncryptionLevel::ONE_RTT) { + // FIXME Where should this be placed? + const uint8_t *tp_buf; + size_t tp_buf_len; + SSL_get_peer_quic_transport_params(ssl, &tp_buf, &tp_buf_len); + if (SSL_is_server(ssl)) { + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); + } else { + qtls->set_remote_transport_parameters(std::make_shared(tp_buf, tp_buf_len)); } - } else if (content_type == SSL3_RT_ALERT && msg_buf[0] == SSL3_AL_FATAL && len == 2) { - msg->error_code = QUICTLS::convert_to_quic_trans_error_code(msg_buf[1]); } - return; + return 1; } -/** - This is very inspired from writting keylog format of ngtcp2's examples - https://github.com/ngtcp2/ngtcp2/blob/894ed23c970d61eede74f69d9178090af63fdf70/examples/keylog.cc - */ -static void -log_secret(SSL *ssl, int name, const unsigned char *secret, size_t secretlen) +static int +add_handshake_data(SSL *ssl, enum ssl_encryption_level_t level, const uint8_t *data, size_t len) { - if (auto keylog_cb = SSL_CTX_get_keylog_callback(SSL_get_SSL_CTX(ssl))) { - unsigned char crandom[32]; - if (SSL_get_client_random(ssl, crandom, sizeof(crandom)) != sizeof(crandom)) { - return; - } - uint8_t line[256] = {0}; - size_t len = 0; - switch (name) { - case SSL_KEY_CLIENT_EARLY_TRAFFIC: - memcpy(line, QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.size()); - len += QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.size(); - break; - case SSL_KEY_CLIENT_HANDSHAKE_TRAFFIC: - memcpy(line, QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.size()); - len += QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.size(); - break; - case SSL_KEY_SERVER_HANDSHAKE_TRAFFIC: - memcpy(line, QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.data(), QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.size()); - len += QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.size(); - break; - case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: - memcpy(line, QUIC_CLIENT_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_TRAFFIC_SECRET_LABEL.size()); - len += QUIC_CLIENT_TRAFFIC_SECRET_LABEL.size(); - break; - case SSL_KEY_SERVER_APPLICATION_TRAFFIC: - memcpy(line, QUIC_SERVER_TRAFFIC_SECRET_LABEL.data(), QUIC_SERVER_TRAFFIC_SECRET_LABEL.size()); - len += QUIC_SERVER_TRAFFIC_SECRET_LABEL.size(); - break; - - default: - return; - } + QUICEncryptionLevel ats_level = convert_level_ats2ssl(level); - line[len] = ' '; - ++len; - QUICDebug::to_hex(line + len, crandom, sizeof(crandom)); - len += sizeof(crandom) * 2; - line[len] = ' '; - ++len; - QUICDebug::to_hex(line + len, secret, secretlen); + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + qtls->on_handshake_data_generated(ats_level, data, len); - keylog_cb(ssl, reinterpret_cast(line)); - } + return 1; } static int -key_cb(SSL *ssl, int name, const unsigned char *secret, size_t secret_len, void *arg) +flush_flight(SSL *ssl) { - if (arg == nullptr) { - return 0; - } - - QUICTLS *qtls = reinterpret_cast(arg); - qtls->update_key_materials_on_key_cb(name, secret, secret_len); - - log_secret(ssl, name, secret, secret_len); + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + qtls->set_ready_for_write(); return 1; } -void -QUICTLS::update_key_materials_on_key_cb(int name, const uint8_t *secret, size_t secret_len) +static int +send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert) { - if (is_debug_tag_set("vv_quic_crypto")) { - switch (name) { - case SSL_KEY_CLIENT_EARLY_TRAFFIC: - Debug("vv_quic_crypto", "%s", QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.data()); - break; - case SSL_KEY_CLIENT_HANDSHAKE_TRAFFIC: - Debug("vv_quic_crypto", "%s", QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.data()); - break; - case SSL_KEY_SERVER_HANDSHAKE_TRAFFIC: - Debug("vv_quic_crypto", "%s", QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.data()); - break; - case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: - Debug("vv_quic_crypto", "%s", QUIC_CLIENT_TRAFFIC_SECRET_LABEL.data()); - break; - case SSL_KEY_SERVER_APPLICATION_TRAFFIC: - Debug("vv_quic_crypto", "%s", QUIC_SERVER_TRAFFIC_SECRET_LABEL.data()); - break; - default: - break; - } - } + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + qtls->on_tls_alert(alert); + return 1; +} - if (this->_state == HandshakeState::ABORTED) { - return; - } +static const SSL_QUIC_METHOD quic_method = {set_encryption_secrets, add_handshake_data, flush_flight, send_alert}; - QUICKeyPhase phase; - const QUIC_EVP_CIPHER *cipher; - QUICHKDF hkdf(this->_get_handshake_digest()); - - this->_store_negotiated_cipher(); - this->_store_negotiated_cipher_for_hp(); - - uint8_t *key_for_hp; - uint8_t *key; - uint8_t *iv; - size_t key_for_hp_len; - size_t key_len; - size_t *iv_len; - - switch (name) { - case SSL_KEY_CLIENT_EARLY_TRAFFIC: - // this->_update_encryption_level(QUICEncryptionLevel::ZERO_RTT); - phase = QUICKeyPhase::ZERO_RTT; - cipher = this->_pp_key_info.get_cipher(phase); - if (this->_netvc_context == NET_VCONNECTION_IN) { - key_for_hp = this->_pp_key_info.decryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(phase); - key = this->_pp_key_info.decryption_key(phase); - key_len = this->_pp_key_info.decryption_key_len(phase); - iv = this->_pp_key_info.decryption_iv(phase); - iv_len = this->_pp_key_info.decryption_iv_len(phase); - this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_decryption_key_available(phase); - } else { - key_for_hp = this->_pp_key_info.encryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(phase); - key = this->_pp_key_info.encryption_key(phase); - key_len = this->_pp_key_info.encryption_key_len(phase); - iv = this->_pp_key_info.encryption_iv(phase); - iv_len = this->_pp_key_info.encryption_iv_len(phase); - this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_encryption_key_available(phase); - } - this->_print_km("update - client - 0rtt", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len); - break; - case SSL_KEY_CLIENT_HANDSHAKE_TRAFFIC: - this->_update_encryption_level(QUICEncryptionLevel::HANDSHAKE); - phase = QUICKeyPhase::HANDSHAKE; - cipher = this->_pp_key_info.get_cipher(phase); - if (this->_netvc_context == NET_VCONNECTION_IN) { - key_for_hp = this->_pp_key_info.decryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(phase); - key = this->_pp_key_info.decryption_key(phase); - key_len = this->_pp_key_info.decryption_key_len(phase); - iv = this->_pp_key_info.decryption_iv(phase); - iv_len = this->_pp_key_info.decryption_iv_len(phase); - this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_decryption_key_available(phase); - } else { - key_for_hp = this->_pp_key_info.encryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(phase); - key = this->_pp_key_info.encryption_key(phase); - key_len = this->_pp_key_info.encryption_key_len(phase); - iv = this->_pp_key_info.encryption_iv(phase); - iv_len = this->_pp_key_info.encryption_iv_len(phase); - this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_encryption_key_available(phase); - } - this->_print_km("update - client - handshake", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len); - break; - case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: - this->_update_encryption_level(QUICEncryptionLevel::ONE_RTT); - phase = QUICKeyPhase::PHASE_0; - cipher = this->_pp_key_info.get_cipher(phase); - if (this->_netvc_context == NET_VCONNECTION_IN) { - key_for_hp = this->_pp_key_info.decryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(phase); - key = this->_pp_key_info.decryption_key(phase); - key_len = this->_pp_key_info.decryption_key_len(phase); - iv = this->_pp_key_info.decryption_iv(phase); - iv_len = this->_pp_key_info.decryption_iv_len(phase); - this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_decryption_key_available(phase); - } else { - key_for_hp = this->_pp_key_info.encryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(phase); - key = this->_pp_key_info.encryption_key(phase); - key_len = this->_pp_key_info.encryption_key_len(phase); - iv = this->_pp_key_info.encryption_iv(phase); - iv_len = this->_pp_key_info.encryption_iv_len(phase); - this->_keygen_for_client.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_encryption_key_available(phase); - } - this->_print_km("update - client - 1rtt", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len); - break; - case SSL_KEY_SERVER_HANDSHAKE_TRAFFIC: - this->_update_encryption_level(QUICEncryptionLevel::HANDSHAKE); - phase = QUICKeyPhase::HANDSHAKE; - cipher = this->_pp_key_info.get_cipher(phase); - if (this->_netvc_context == NET_VCONNECTION_IN) { - key_for_hp = this->_pp_key_info.encryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(phase); - key = this->_pp_key_info.encryption_key(phase); - key_len = this->_pp_key_info.encryption_key_len(phase); - iv = this->_pp_key_info.encryption_iv(phase); - iv_len = this->_pp_key_info.encryption_iv_len(phase); - this->_keygen_for_server.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_encryption_key_available(phase); - } else { - key_for_hp = this->_pp_key_info.decryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(phase); - key = this->_pp_key_info.decryption_key(phase); - key_len = this->_pp_key_info.decryption_key_len(phase); - iv = this->_pp_key_info.decryption_iv(phase); - iv_len = this->_pp_key_info.decryption_iv_len(phase); - this->_keygen_for_server.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_decryption_key_available(phase); - } - this->_print_km("update - server - handshake", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len); - break; - case SSL_KEY_SERVER_APPLICATION_TRAFFIC: - this->_update_encryption_level(QUICEncryptionLevel::ONE_RTT); - phase = QUICKeyPhase::PHASE_0; - cipher = this->_pp_key_info.get_cipher(phase); - if (this->_netvc_context == NET_VCONNECTION_IN) { - key_for_hp = this->_pp_key_info.encryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.encryption_key_for_hp_len(phase); - key = this->_pp_key_info.encryption_key(phase); - key_len = this->_pp_key_info.encryption_key_len(phase); - iv = this->_pp_key_info.encryption_iv(phase); - iv_len = this->_pp_key_info.encryption_iv_len(phase); - this->_keygen_for_server.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_encryption_key_available(phase); - } else { - key_for_hp = this->_pp_key_info.decryption_key_for_hp(phase); - key_for_hp_len = this->_pp_key_info.decryption_key_for_hp_len(phase); - key = this->_pp_key_info.decryption_key(phase); - key_len = this->_pp_key_info.decryption_key_len(phase); - iv = this->_pp_key_info.decryption_iv(phase); - iv_len = this->_pp_key_info.decryption_iv_len(phase); - this->_keygen_for_server.regenerate(key_for_hp, key, iv, iv_len, secret, secret_len, cipher, hkdf); - this->_pp_key_info.set_decryption_key_available(phase); - } - this->_print_km("update - server - 1rtt", key_for_hp, key_for_hp_len, key, key_len, iv, *iv_len, secret, secret_len); - break; - default: - break; +void +QUICTLS::_msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) +{ + // Debug for reading + if (write_p == 0) { + QUICTLS::_print_hs_message(content_type, buf, len); } - - return; } QUICTLS::QUICTLS(QUICPacketProtectionKeyInfo &pp_key_info, SSL_CTX *ssl_ctx, NetVConnectionContext_t nvc_ctx, @@ -412,7 +142,10 @@ QUICTLS::QUICTLS(QUICPacketProtectionKeyInfo &pp_key_info, SSL_CTX *ssl_ctx, Net } SSL_set_ex_data(this->_ssl, QUIC::ssl_quic_tls_index, this); - SSL_set_key_callback(this->_ssl, key_cb, this); + SSL_set_quic_method(this->_ssl, &quic_method); +#ifdef HAVE_SSL_SET_QUIC_EARLY_DATA_ENABLED + SSL_set_quic_early_data_enabled(this->_ssl, 1); +#endif if (session_file && this->_netvc_context == NET_VCONNECTION_OUT) { auto file = BIO_new_file(session_file, "r"); @@ -438,147 +171,22 @@ QUICTLS::QUICTLS(QUICPacketProtectionKeyInfo &pp_key_info, SSL_CTX *ssl_ctx, Net } } -QUICEncryptionLevel -QUICTLS::get_encryption_level(int msg_type) -{ - switch (msg_type) { - case SSL3_MT_CLIENT_HELLO: - case SSL3_MT_SERVER_HELLO: - return QUICEncryptionLevel::INITIAL; - case SSL3_MT_END_OF_EARLY_DATA: - return QUICEncryptionLevel::ZERO_RTT; - case SSL3_MT_ENCRYPTED_EXTENSIONS: - case SSL3_MT_CERTIFICATE_REQUEST: - case SSL3_MT_CERTIFICATE: - case SSL3_MT_CERTIFICATE_VERIFY: - case SSL3_MT_FINISHED: - return QUICEncryptionLevel::HANDSHAKE; - case SSL3_MT_KEY_UPDATE: - case SSL3_MT_NEWSESSION_TICKET: - return QUICEncryptionLevel::ONE_RTT; - default: - return QUICEncryptionLevel::NONE; - } -} - -int -QUICTLS::handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) -{ - if (this->is_handshake_finished()) { - if (in != nullptr && in->offsets[4] != 0) { - return this->_process_post_handshake_messages(out, in); - } - - return 0; - } - - return this->_handshake(out, in); -} - -int -QUICTLS::_handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) +void +QUICTLS::set_local_transport_parameters(std::shared_ptr tp) { - ink_assert(this->_ssl != nullptr); - if (this->_state == HandshakeState::ABORTED) { - return 0; - } - - int err = SSL_ERROR_NONE; - ERR_clear_error(); - int ret = 0; - - SSL_set_msg_callback(this->_ssl, msg_cb); - SSL_set_msg_callback_arg(this->_ssl, out); - - // TODO: set BIO_METHOD which read from QUICHandshakeMsgs directly - BIO *rbio = BIO_new(BIO_s_mem()); - // TODO: set dummy BIO_METHOD which do nothing - BIO *wbio = BIO_new(BIO_s_mem()); - if (in != nullptr && in->offsets[4] != 0) { - BIO_write(rbio, in->buf, in->offsets[4]); - } - SSL_set_bio(this->_ssl, rbio, wbio); - - if (this->_netvc_context == NET_VCONNECTION_IN) { - if (!this->_early_data_processed) { - if (this->_read_early_data() != 1) { - out->error_code = static_cast(QUICTransErrorCode::PROTOCOL_VIOLATION); - return 0; - } else { - this->_early_data_processed = true; - } - } - - ret = SSL_accept(this->_ssl); - } else { - if (!this->_early_data_processed) { - if (this->_write_early_data()) { - this->_early_data_processed = true; - } - } - - ret = SSL_connect(this->_ssl); - } + this->_local_transport_parameters = tp; - if (ret <= 0) { - err = SSL_get_error(this->_ssl, ret); - - switch (err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - break; - default: - char err_buf[256] = {0}; - ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); - Debug(tag, "Handshake: %s", err_buf); - return ret; - } - } - - return 1; + uint8_t buf[UINT16_MAX]; + uint16_t len; + this->_local_transport_parameters->store(buf, &len); + SSL_set_quic_transport_params(this->_ssl, buf, len); } int QUICTLS::_process_post_handshake_messages(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in) { - ink_assert(this->_ssl != nullptr); - - int err = SSL_ERROR_NONE; - ERR_clear_error(); - int ret = 0; - - SSL_set_msg_callback(this->_ssl, msg_cb); - SSL_set_msg_callback_arg(this->_ssl, out); - - // TODO: set BIO_METHOD which read from QUICHandshakeMsgs directly - BIO *rbio = BIO_new(BIO_s_mem()); - // TODO: set dummy BIO_METHOD which do nothing - BIO *wbio = BIO_new(BIO_s_mem()); - if (in != nullptr && in->offsets[4] != 0) { - BIO_write(rbio, in->buf, in->offsets[4]); - } - SSL_set_bio(this->_ssl, rbio, wbio); - - uint8_t data[2048]; - size_t l = 0; - ret = SSL_read_ex(this->_ssl, data, 2048, &l); - - if (ret <= 0) { - err = SSL_get_error(this->_ssl, ret); - - switch (err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - break; - default: - char err_buf[256] = {0}; - ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); - Debug(tag, "Handshake: %s", err_buf); - return ret; - } - } - - return 1; + this->_pass_quic_data_to_ssl_impl(*in); + return SSL_process_quic_post_handshake(this->_ssl); } void @@ -586,9 +194,9 @@ QUICTLS::_store_negotiated_cipher() { ink_assert(this->_ssl); - const QUIC_EVP_CIPHER *cipher = nullptr; - size_t tag_len = 0; - const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); + const EVP_CIPHER *cipher = nullptr; + size_t tag_len = 0; + const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); if (ssl_cipher) { switch (SSL_CIPHER_get_id(ssl_cipher)) { @@ -627,8 +235,8 @@ QUICTLS::_store_negotiated_cipher_for_hp() { ink_assert(this->_ssl); - const QUIC_EVP_CIPHER *cipher_for_hp = nullptr; - const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); + const EVP_CIPHER *cipher_for_hp = nullptr; + const SSL_CIPHER *ssl_cipher = SSL_get_current_cipher(this->_ssl); if (ssl_cipher) { switch (SSL_CIPHER_get_id(ssl_cipher)) { @@ -659,28 +267,51 @@ QUICTLS::_store_negotiated_cipher_for_hp() int QUICTLS::_read_early_data() { - uint8_t early_data[8]; - size_t early_data_len = 0; - - // Early data within the TLS connection MUST NOT be used. As it is for other TLS application data, a server MUST treat receiving - // early data on the TLS connection as a connection error of type PROTOCOL_VIOLATION. - SSL_read_early_data(this->_ssl, early_data, sizeof(early_data), &early_data_len); - // error or reading empty data return 1, otherwise return 0. - return early_data_len != 0 ? 0 : 1; + // This is for Hacked OpenSSL. Do nothing here. + return 1; } int QUICTLS::_write_early_data() { - size_t early_data_len = 0; - - // Early data within the TLS connection MUST NOT be used. As it is for other TLS application data, a server MUST treat receiving - // early data on the TLS connection as a connection error of type PROTOCOL_VIOLATION. - SSL_write_early_data(this->_ssl, "", 0, &early_data_len); - // always return 1 + // This is for Hacked OpenSSL. Do nothing here. return 1; } +void +QUICTLS::_pass_quic_data_to_ssl_impl(const QUICHandshakeMsgs &in) +{ + for (auto level : QUIC_ENCRYPTION_LEVELS) { + int index = static_cast(level); + ssl_encryption_level_t ossl_level; + switch (level) { + case QUICEncryptionLevel::INITIAL: + ossl_level = ssl_encryption_initial; + break; + case QUICEncryptionLevel::ZERO_RTT: + ossl_level = ssl_encryption_early_data; + break; + case QUICEncryptionLevel::HANDSHAKE: + ossl_level = ssl_encryption_handshake; + break; + case QUICEncryptionLevel::ONE_RTT: + ossl_level = ssl_encryption_application; + break; + default: + // Should not be happend + ossl_level = ssl_encryption_application; + break; + } + if (in.offsets[index + 1] - in.offsets[index]) { + int start = 0; + for (int i = 0; i < index; ++i) { + start += in.offsets[index]; + } + SSL_provide_quic_data(this->_ssl, ossl_level, in.buf + start, in.offsets[index + 1] - in.offsets[index]); + } + } +} + const EVP_MD * QUICTLS::_get_handshake_digest() const { diff --git a/iocore/net/quic/QUICTransportParameters.cc b/iocore/net/quic/QUICTransportParameters.cc index 248cfcb6b9c..cead9700e2d 100644 --- a/iocore/net/quic/QUICTransportParameters.cc +++ b/iocore/net/quic/QUICTransportParameters.cc @@ -27,13 +27,14 @@ #include "QUICIntUtil.h" #include "QUICTransportParameters.h" #include "QUICConnection.h" -#include "QUICHandshake.h" #include "QUICDebugNames.h" #include "QUICTLS.h" #include "QUICTypes.h" static constexpr char tag[] = "quic_handshake"; +static constexpr int TRANSPORT_PARAMETERS_MAXIMUM_SIZE = 65535; + static constexpr uint32_t TP_ERROR_LENGTH = 0x010000; static constexpr uint32_t TP_ERROR_VALUE = 0x020000; // static constexpr uint32_t TP_ERROR_MUST_EXIST = 0x030000; @@ -81,19 +82,16 @@ QUICTransportParameters::_load(const uint8_t *buf, size_t len) { bool has_error = false; const uint8_t *p = buf; - - // Read size of parameters field - uint16_t nbytes = (p[0] << 8) + p[1]; - p += 2; + size_t l; + uint64_t param_id; + uint64_t param_len; // Read parameters - const uint8_t *end = p + nbytes; - while (p < end) { + while (len) { // Read ID - uint16_t id = 0; - if (end - p >= 2) { - id = (p[0] << 8) + p[1]; - p += 2; + if (!QUICVariableInt::decode(param_id, l, p, len)) { + len -= l; + p += l; } else { has_error = true; break; @@ -101,25 +99,25 @@ QUICTransportParameters::_load(const uint8_t *buf, size_t len) // Check duplication // An endpoint MUST treat receipt of duplicate transport parameters as a connection error of type TRANSPORT_PARAMETER_ERROR - if (this->_parameters.find(id) != this->_parameters.end()) { + if (this->_parameters.find(param_id) != this->_parameters.end()) { has_error = true; break; } // Read length of value - uint16_t len = 0; - if (end - p >= 2) { - len = (p[0] << 8) + p[1]; - p += 2; + if (!QUICVariableInt::decode(param_len, l, p, len)) { + len -= l; + p += l; } else { has_error = true; break; } // Store parameter - if (end - p >= len) { - this->_parameters.insert(std::make_pair(id, new Value(p, len))); - p += len; + if (len >= param_len) { + this->_parameters.insert(std::make_pair(param_id, new Value(p, param_len))); + len -= param_len; + p += param_len; } else { has_error = true; break; @@ -158,7 +156,7 @@ QUICTransportParameters::_validate_parameters() const if ((ite = this->_parameters.find(QUICTransportParameterId::INITIAL_MAX_STREAMS_UNI)) != this->_parameters.end()) { } - if ((ite = this->_parameters.find(QUICTransportParameterId::IDLE_TIMEOUT)) != this->_parameters.end()) { + if ((ite = this->_parameters.find(QUICTransportParameterId::MAX_IDLE_TIMEOUT)) != this->_parameters.end()) { } if ((ite = this->_parameters.find(QUICTransportParameterId::MAX_PACKET_SIZE)) != this->_parameters.end()) { @@ -250,33 +248,19 @@ void QUICTransportParameters::store(uint8_t *buf, uint16_t *len) const { uint8_t *p = buf; + size_t l; - // Write QUIC versions - this->_store(p, len); - p += *len; - - // Write parameters - // XXX parameters_size will be written later - uint8_t *parameters_size = p; - p += sizeof(uint16_t); - + *len = 0; for (auto &it : this->_parameters) { // TODO Skip non-MUST parameters that have their default values - p[0] = (it.first & 0xff00) >> 8; - p[1] = it.first & 0xff; - p += 2; - p[0] = (it.second->len() & 0xff00) >> 8; - p[1] = it.second->len() & 0xff; - p += 2; + QUICVariableInt::encode(p, TRANSPORT_PARAMETERS_MAXIMUM_SIZE, l, it.first); + p += l; + QUICVariableInt::encode(p, TRANSPORT_PARAMETERS_MAXIMUM_SIZE, l, it.second->len()); + p += l; memcpy(p, it.second->data(), it.second->len()); p += it.second->len(); } - ptrdiff_t n = p - parameters_size - sizeof(uint16_t); - - parameters_size[0] = (n & 0xff00) >> 8; - parameters_size[1] = n & 0xff; - *len = (p - buf); } @@ -298,18 +282,17 @@ QUICTransportParameters::_print() const Debug(tag, "%s (%" PRIu32 "): %s", QUICDebugNames::transport_parameter_id(p.first), static_cast(p.first), hex_str); } else if (QUICTransportParameterId::PREFERRED_ADDRESS == p.first) { QUICPreferredAddress pref_addr(p.second->data(), p.second->len()); - char cid_hex_str[QUICConnectionId::MAX_HEX_STR_LENGTH]; char token_hex_str[QUICStatelessResetToken::LEN * 2 + 1]; char ep_ipv4_hex_str[512]; char ep_ipv6_hex_str[512]; - pref_addr.cid().hex(cid_hex_str, sizeof(cid_hex_str)); to_hex_str(token_hex_str, sizeof(token_hex_str), pref_addr.token().buf(), QUICStatelessResetToken::LEN); ats_ip_nptop(pref_addr.endpoint_ipv4(), ep_ipv4_hex_str, sizeof(ep_ipv4_hex_str)); ats_ip_nptop(pref_addr.endpoint_ipv6(), ep_ipv6_hex_str, sizeof(ep_ipv6_hex_str)); Debug(tag, "%s: Endpoint(IPv4)=%s, Endpoint(IPv6)=%s, CID=%s, Token=%s", QUICDebugNames::transport_parameter_id(p.first), - ep_ipv4_hex_str, ep_ipv6_hex_str, cid_hex_str, token_hex_str); + ep_ipv4_hex_str, ep_ipv6_hex_str, pref_addr.cid().hex().c_str(), token_hex_str); } else { - Debug(tag, "%s: (long data)", QUICDebugNames::transport_parameter_id(p.first)); + Debug(tag, "%s (%" PRIu32 "): (%u byte data)", QUICDebugNames::transport_parameter_id(p.first), + static_cast(p.first), p.second->len()); } } } @@ -326,12 +309,6 @@ QUICTransportParametersInClientHello::QUICTransportParametersInClientHello(const } } -void -QUICTransportParametersInClientHello::_store(uint8_t *buf, uint16_t *len) const -{ - *len = 0; -} - std::ptrdiff_t QUICTransportParametersInClientHello::_parameters_offset(const uint8_t *) const { @@ -372,12 +349,6 @@ QUICTransportParametersInEncryptedExtensions::QUICTransportParametersInEncrypted } } -void -QUICTransportParametersInEncryptedExtensions::_store(uint8_t *buf, uint16_t *len) const -{ - *len = 0; -} - void QUICTransportParametersInEncryptedExtensions::add_version(QUICVersion version) { @@ -426,8 +397,6 @@ QUICTransportParametersInEncryptedExtensions::_validate_parameters() const #ifndef OPENSSL_IS_BORINGSSL -static constexpr int TRANSPORT_PARAMETERS_MAXIMUM_SIZE = 65535; - // // QUICTransportParametersHandler // diff --git a/iocore/net/quic/QUICTransportParameters.h b/iocore/net/quic/QUICTransportParameters.h index df46d9d3acd..00af57cca44 100644 --- a/iocore/net/quic/QUICTransportParameters.h +++ b/iocore/net/quic/QUICTransportParameters.h @@ -35,7 +35,7 @@ class QUICTransportParameterId public: enum { ORIGINAL_CONNECTION_ID, - IDLE_TIMEOUT, + MAX_IDLE_TIMEOUT, STATELESS_RESET_TOKEN, MAX_PACKET_SIZE, INITIAL_MAX_DATA, @@ -109,7 +109,6 @@ class QUICTransportParameters virtual std::ptrdiff_t _parameters_offset(const uint8_t *buf) const = 0; virtual int _validate_parameters() const; - virtual void _store(uint8_t *buf, uint16_t *len) const = 0; void _print() const; std::map _parameters; @@ -124,7 +123,6 @@ class QUICTransportParametersInClientHello : public QUICTransportParameters protected: std::ptrdiff_t _parameters_offset(const uint8_t *buf) const override; int _validate_parameters() const override; - void _store(uint8_t *buf, uint16_t *len) const override; private: }; @@ -139,7 +137,6 @@ class QUICTransportParametersInEncryptedExtensions : public QUICTransportParamet protected: std::ptrdiff_t _parameters_offset(const uint8_t *buf) const override; int _validate_parameters() const override; - void _store(uint8_t *buf, uint16_t *len) const override; uint8_t _n_versions = 0; QUICVersion _versions[256] = {}; diff --git a/iocore/net/quic/QUICTypes.cc b/iocore/net/quic/QUICTypes.cc index 5c354a9c68d..80a8379d2b3 100644 --- a/iocore/net/quic/QUICTypes.cc +++ b/iocore/net/quic/QUICTypes.cc @@ -22,6 +22,10 @@ */ #include +#include +#include +#include + #include "QUICTypes.h" #include "QUICIntUtil.h" #include "tscore/CryptoHash.h" @@ -190,15 +194,15 @@ QUICTypeUtil::read_QUICVersion(const uint8_t *buf) } QUICStreamId -QUICTypeUtil::read_QUICStreamId(const uint8_t *buf) +QUICTypeUtil::read_QUICStreamId(const uint8_t *buf, size_t buf_len) { - return static_cast(QUICIntUtil::read_QUICVariableInt(buf)); + return static_cast(QUICIntUtil::read_QUICVariableInt(buf, buf_len)); } QUICOffset -QUICTypeUtil::read_QUICOffset(const uint8_t *buf) +QUICTypeUtil::read_QUICOffset(const uint8_t *buf, size_t buf_len) { - return static_cast(QUICIntUtil::read_QUICVariableInt(buf)); + return static_cast(QUICIntUtil::read_QUICVariableInt(buf, buf_len)); } uint16_t @@ -214,9 +218,9 @@ QUICTypeUtil::read_QUICAppErrorCode(const uint8_t *buf) } uint64_t -QUICTypeUtil::read_QUICMaxData(const uint8_t *buf) +QUICTypeUtil::read_QUICMaxData(const uint8_t *buf, size_t buf_len) { - return QUICIntUtil::read_QUICVariableInt(buf); + return QUICIntUtil::read_QUICVariableInt(buf, buf_len); } void @@ -284,6 +288,27 @@ QUICStatelessResetToken::QUICStatelessResetToken(const QUICConnectionId &conn_id QUICIntUtil::write_uint_as_nbytes(_hash.u64[1], 8, _token + 8, &dummy); } +uint64_t +QUICStatelessResetToken::_hashcode() const +{ + return (static_cast(this->_token[0]) << 56) + (static_cast(this->_token[1]) << 48) + + (static_cast(this->_token[2]) << 40) + (static_cast(this->_token[3]) << 32) + + (static_cast(this->_token[4]) << 24) + (static_cast(this->_token[5]) << 16) + + (static_cast(this->_token[6]) << 8) + (static_cast(this->_token[7])); +} + +std::string +QUICStatelessResetToken::hex() const +{ + std::stringstream stream; + stream << "0x"; + for (auto i = 0; i < QUICStatelessResetToken::LEN; i++) { + stream << std::setfill('0') << std::setw(2) << std::hex; + stream << std::hex << static_cast(this->_token[i]); + } + return stream.str(); +} + QUICResumptionToken::QUICResumptionToken(const IpEndpoint &src, QUICConnectionId cid, ink_hrtime expire_time) { // TODO: read cookie secret from file like SSLTicketKeyConfig @@ -577,7 +602,7 @@ QUICConnectionId QUICConnectionId::ZERO() { uint8_t zero[MAX_LENGTH] = {0}; - return QUICConnectionId(zero, sizeof(zero)); + return QUICConnectionId(zero, 0); } QUICConnectionId::QUICConnectionId() @@ -587,7 +612,8 @@ QUICConnectionId::QUICConnectionId() QUICConnectionId::QUICConnectionId(const uint8_t *buf, uint8_t len) : _len(len) { - memcpy(this->_id, buf, len); + ink_assert(len <= QUICConnectionId::MAX_LENGTH); + memcpy(this->_id, buf, std::min(static_cast(len), QUICConnectionId::MAX_LENGTH)); } uint8_t @@ -636,10 +662,16 @@ QUICConnectionId::h32() const return static_cast(QUICIntUtil::read_nbytes_as_uint(this->_id, 4)); } -int -QUICConnectionId::hex(char *buf, size_t len) const +std::string +QUICConnectionId::hex() const { - return to_hex_str(buf, len, this->_id, this->_len); + std::stringstream stream; + stream << "0x"; + for (auto i = 0; i < this->_len; i++) { + stream << std::setfill('0') << std::setw(2) << std::hex; + stream << std::hex << static_cast(this->_id[i]); + } + return stream.str(); } // @@ -692,8 +724,11 @@ QUICInvariants::scil(uint8_t &dst, const uint8_t *buf, uint64_t buf_len) if (!QUICInvariants::dcil(dcil, buf, buf_len)) { return false; } - - dst = buf[QUICInvariants::LH_CIL_OFFSET + 1 + dcil]; + uint64_t scil_offset = QUICInvariants::LH_CIL_OFFSET + 1 + dcil; + if (scil_offset >= buf_len) { + return false; + } + dst = buf[scil_offset]; return true; } @@ -723,6 +758,10 @@ QUICInvariants::dcid(QUICConnectionId &dst, const uint8_t *buf, uint64_t buf_len dcid_offset = QUICInvariants::SH_DCID_OFFSET; } + if (dcid_len > QUICConnectionId::MAX_LENGTH) { + return false; + } + if (dcid_offset + dcid_len > buf_len) { return false; } @@ -769,3 +808,18 @@ QUICInvariants::scid(QUICConnectionId &dst, const uint8_t *buf, uint64_t buf_len return true; } + +namespace QUICBase +{ +std::string +to_hex(const uint8_t *buf, size_t len) +{ + std::stringstream stream; + stream << "0x"; + for (size_t i = 0; i < len; i++) { + stream << std::setfill('0') << std::setw(2) << std::hex; + stream << std::hex << static_cast(buf[i]); + } + return stream.str(); +} +} // namespace QUICBase diff --git a/iocore/net/quic/QUICTypes.h b/iocore/net/quic/QUICTypes.h index 7c3a53796c7..5139b7821f1 100644 --- a/iocore/net/quic/QUICTypes.h +++ b/iocore/net/quic/QUICTypes.h @@ -24,6 +24,7 @@ #pragma once #include +#include #include "tscore/ink_endian.h" #include "tscore/ink_hrtime.h" #include "tscore/Ptr.h" @@ -34,6 +35,7 @@ #include #include #include +#include #include "tscore/ink_memory.h" #include "tscore/ink_inet.h" #include "openssl/evp.h" @@ -51,7 +53,7 @@ static constexpr uint8_t kPacketNumberSpace = 3; // Note: Fix QUIC_ALPN_PROTO_LIST in QUICConfig.cc // Note: Change ExtensionType (QUICTransportParametersHandler::TRANSPORT_PARAMETER_ID) if it's changed constexpr QUICVersion QUIC_SUPPORTED_VERSIONS[] = { - 0xff000017, + 0xff00001b, }; constexpr QUICVersion QUIC_EXERCISE_VERSION = 0x1a2a3a4a; @@ -115,7 +117,8 @@ enum class QUICFrameType : uint8_t { PATH_CHALLENGE, PATH_RESPONSE, CONNECTION_CLOSE, // 0x1c - 0x1d - UNKNOWN = 0x1e, + HANDSHAKE_DONE = 0x1e, + UNKNOWN = 0x1f, }; enum class QUICVersionNegotiationStatus { @@ -158,7 +161,9 @@ enum class QUICTransErrorCode : uint64_t { FINAL_SIZE_ERROR, FRAME_ENCODING_ERROR, TRANSPORT_PARAMETER_ERROR, - PROTOCOL_VIOLATION = 0x0A, + CONNECTION_ID_LIMIT_ERROR, + PROTOCOL_VIOLATION, + INVALID_TOKEN, CRYPTO_BUFFER_EXCEEDED = 0x0D, CRYPTO_ERROR = 0x0100, // 0x100 - 0x1FF }; @@ -224,9 +229,9 @@ class QUICConnectionId public: static uint8_t SCID_LEN; - static const int MIN_LENGTH_FOR_INITIAL = 8; - static const int MAX_LENGTH = 20; - static const size_t MAX_HEX_STR_LENGTH = MAX_LENGTH * 2 + 1; + static constexpr int MIN_LENGTH_FOR_INITIAL = 8; + static constexpr int MAX_LENGTH = 20; + static constexpr size_t MAX_HEX_STR_LENGTH = MAX_LENGTH * 2 + 1; static QUICConnectionId ZERO(); QUICConnectionId(); QUICConnectionId(const uint8_t *buf, uint8_t len); @@ -259,7 +264,7 @@ class QUICConnectionId * This is just for debugging. */ uint32_t h32() const; - int hex(char *buf, size_t len) const; + std::string hex() const; uint8_t length() const; bool is_zero() const; @@ -280,22 +285,36 @@ class QUICStatelessResetToken QUICStatelessResetToken(const QUICConnectionId &conn_id, uint32_t instance_id); QUICStatelessResetToken(const uint8_t *buf) { memcpy(this->_token, buf, QUICStatelessResetToken::LEN); } + /** + * Note that this returns a kind of hash code so we can use a StatelessResetToken as a key for a hashtable. + */ + operator uint64_t() const { return this->_hashcode(); } + bool operator==(const QUICStatelessResetToken &x) const { return memcmp(this->_token, x._token, QUICStatelessResetToken::LEN) == 0; } + bool + operator!=(const QUICStatelessResetToken &x) const + { + return memcmp(this->_token, x._token, QUICStatelessResetToken::LEN) != 0; + } + const uint8_t * buf() const { return _token; } + std::string hex() const; + private: uint8_t _token[LEN] = {0}; void _generate(uint64_t data); + uint64_t _hashcode() const; }; class QUICAddressValidationToken @@ -306,6 +325,8 @@ class QUICAddressValidationToken RETRY, }; + // FIXME Check token length + QUICAddressValidationToken(const uint8_t *buf, size_t len) : _token_len(len) { memcpy(this->_token, buf, len); } virtual ~QUICAddressValidationToken(){}; static Type @@ -315,15 +336,30 @@ class QUICAddressValidationToken return static_cast(buf[0]) == Type::RESUMPTION ? Type::RESUMPTION : Type::RETRY; } - virtual const uint8_t *buf() const = 0; - virtual uint8_t length() const = 0; + virtual const uint8_t * + buf() const + { + return this->_token; + } + + virtual uint8_t + length() const + { + return this->_token_len; + } + +protected: + QUICAddressValidationToken() {} + + // The size should be smaller than maximum size of Retry packet + uint8_t _token[1200] = {0}; + unsigned int _token_len; }; class QUICResumptionToken : public QUICAddressValidationToken { public: - QUICResumptionToken() {} - QUICResumptionToken(const uint8_t *buf, uint8_t len) : _token_len(len) { memcpy(this->_token, buf, len); } + QUICResumptionToken(const uint8_t *buf, uint8_t len) : QUICAddressValidationToken(buf, len) {} QUICResumptionToken(const IpEndpoint &src, QUICConnectionId cid, ink_hrtime expire_time); bool @@ -339,29 +375,12 @@ class QUICResumptionToken : public QUICAddressValidationToken const QUICConnectionId cid() const; const ink_hrtime expire_time() const; - - const uint8_t * - buf() const override - { - return this->_token; - } - - uint8_t - length() const override - { - return this->_token_len; - } - -private: - uint8_t _token[1 + EVP_MAX_MD_SIZE + QUICConnectionId::MAX_LENGTH + 4]; - unsigned int _token_len; }; class QUICRetryToken : public QUICAddressValidationToken { public: - QUICRetryToken() {} - QUICRetryToken(const uint8_t *buf, uint8_t len) : _token_len(len) { memcpy(this->_token, buf, len); } + QUICRetryToken(const uint8_t *buf, size_t len) : QUICAddressValidationToken(buf, len) {} QUICRetryToken(const IpEndpoint &src, QUICConnectionId original_dcid); bool @@ -377,21 +396,7 @@ class QUICRetryToken : public QUICAddressValidationToken const QUICConnectionId original_dcid() const; - const uint8_t * - buf() const override - { - return this->_token; - } - - uint8_t - length() const override - { - return this->_token_len; - } - private: - uint8_t _token[1 + EVP_MAX_MD_SIZE + QUICConnectionId::MAX_LENGTH] = {}; - unsigned int _token_len = 0; QUICConnectionId _original_dcid; }; @@ -515,18 +520,20 @@ class QUICPathValidationData class QUICTPConfig { public: - virtual uint32_t no_activity_timeout() const = 0; - virtual const IpEndpoint *preferred_address_ipv4() const = 0; - virtual const IpEndpoint *preferred_address_ipv6() const = 0; - virtual uint32_t initial_max_data() const = 0; - virtual uint32_t initial_max_stream_data_bidi_local() const = 0; - virtual uint32_t initial_max_stream_data_bidi_remote() const = 0; - virtual uint32_t initial_max_stream_data_uni() const = 0; - virtual uint64_t initial_max_streams_bidi() const = 0; - virtual uint64_t initial_max_streams_uni() const = 0; - virtual uint8_t ack_delay_exponent() const = 0; - virtual uint8_t max_ack_delay() const = 0; - virtual uint8_t active_cid_limit() const = 0; + virtual uint32_t no_activity_timeout() const = 0; + virtual const IpEndpoint *preferred_address_ipv4() const = 0; + virtual const IpEndpoint *preferred_address_ipv6() const = 0; + virtual uint32_t initial_max_data() const = 0; + virtual uint32_t initial_max_stream_data_bidi_local() const = 0; + virtual uint32_t initial_max_stream_data_bidi_remote() const = 0; + virtual uint32_t initial_max_stream_data_uni() const = 0; + virtual uint64_t initial_max_streams_bidi() const = 0; + virtual uint64_t initial_max_streams_uni() const = 0; + virtual uint8_t ack_delay_exponent() const = 0; + virtual uint8_t max_ack_delay() const = 0; + virtual uint8_t active_cid_limit() const = 0; + virtual bool disable_active_migration() const = 0; + virtual std::unordered_map> additional_tp() const = 0; }; class QUICLDConfig @@ -566,11 +573,11 @@ class QUICTypeUtil static int read_QUICPacketNumberLen(const uint8_t *buf); static QUICPacketNumber read_QUICPacketNumber(const uint8_t *buf, int len); static QUICVersion read_QUICVersion(const uint8_t *buf); - static QUICStreamId read_QUICStreamId(const uint8_t *buf); - static QUICOffset read_QUICOffset(const uint8_t *buf); + static QUICStreamId read_QUICStreamId(const uint8_t *buf, size_t buf_len); + static QUICOffset read_QUICOffset(const uint8_t *buf, size_t buf_len); static uint16_t read_QUICTransErrorCode(const uint8_t *buf); static QUICAppErrorCode read_QUICAppErrorCode(const uint8_t *buf); - static uint64_t read_QUICMaxData(const uint8_t *buf); + static uint64_t read_QUICMaxData(const uint8_t *buf, size_t buf_len); static void write_QUICConnectionId(QUICConnectionId connection_id, uint8_t *buf, size_t *len); static void write_QUICPacketNumberLen(int len, uint8_t *buf); @@ -605,3 +612,9 @@ class QUICInvariants }; int to_hex_str(char *dst, size_t dst_len, const uint8_t *src, size_t src_len); + +namespace QUICBase +{ +std::string to_hex(const uint8_t *buf, size_t len); + +} // namespace QUICBase diff --git a/iocore/net/quic/QUICVersionNegotiator.cc b/iocore/net/quic/QUICVersionNegotiator.cc index ec1dc7938d6..e08504ce52c 100644 --- a/iocore/net/quic/QUICVersionNegotiator.cc +++ b/iocore/net/quic/QUICVersionNegotiator.cc @@ -35,20 +35,21 @@ QUICVersionNegotiator::negotiate(const QUICPacket &packet) { switch (packet.type()) { case QUICPacketType::INITIAL: { - if (QUICTypeUtil::is_supported_version(packet.version())) { + const QUICInitialPacketR &initial_packet = static_cast(packet); + if (QUICTypeUtil::is_supported_version(initial_packet.version())) { this->_status = QUICVersionNegotiationStatus::NEGOTIATED; - this->_negotiated_version = packet.version(); + this->_negotiated_version = initial_packet.version(); } break; } case QUICPacketType::VERSION_NEGOTIATION: { - const uint8_t *supported_versions = packet.payload(); - uint16_t supported_versions_len = packet.payload_length(); - uint16_t len = 0; + const QUICVersionNegotiationPacketR &vn_packet = static_cast(packet); + uint16_t n_supported_version = vn_packet.nversions(); + uint16_t len = 0; - while (len < supported_versions_len) { - QUICVersion version = QUICTypeUtil::read_QUICVersion(supported_versions + len); + for (int i = 0; i < n_supported_version; ++i) { + QUICVersion version = vn_packet.supported_version(i); len += sizeof(QUICVersion); if (QUICTypeUtil::is_supported_version(version)) { diff --git a/iocore/net/quic/qlog/QLog.cc b/iocore/net/quic/qlog/QLog.cc new file mode 100644 index 00000000000..f94fb398de1 --- /dev/null +++ b/iocore/net/quic/qlog/QLog.cc @@ -0,0 +1,103 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "QLog.h" + +namespace QLog +{ +void +Trace::encode(YAML::Node &node) +{ + node["title"] = _title; + node["description"] = _desc; + + // common fields + { + YAML::Node cf; + cf["ODCID"] = _odcid; + cf["reference_time"] = std::to_string(this->_reference_time / HRTIME_MSECOND); + node["common_fields"] = cf; + } + + { + node["event_fields"].push_back("relative_time"); + node["event_fields"].push_back("category"); + node["event_fields"].push_back("event"); + node["event_fields"].push_back("data"); + + if (_vp.name != "") { + node["vantage_point"]["name"] = _vp.name; + } + + if (vantage_point_type_name(_vp.type)) { + node["vantage_point"]["type"] = vantage_point_type_name(_vp.type); + } + + if (vantage_point_type_name(_vp.flow)) { + node["vantage_point"]["flow"] = vantage_point_type_name(_vp.flow); + } + } + + // events + for (auto &&it : _events) { + YAML::Node sub(YAML::NodeType::value::Sequence); + sub.push_back((it->get_time() - this->_reference_time) / HRTIME_MSECOND); + sub.push_back(it->category()); + sub.push_back(it->event()); + YAML::Node event; + it->encode(event); + sub.push_back(event); + node["events"].push_back(sub); + } +} + +void +QLog::dump(std::string dir) +{ + YAML::Node root; + root["qlog_version"] = this->_ver; + root["title"] = this->_title; + root["description"] = this->_desc; + for (auto &&it : this->_traces) { + YAML::Node node; + it->encode(node); + root["traces"].push_back(node); + } + + auto file = dir; + if (file.back() != '/') { + file += "/"; + } + file += this->last_trace().odcid() + ".qlog"; + std::ofstream ofs; + ofs.open(file, std::ofstream::in | std::ofstream::trunc); + + YAML::Emitter emitter(ofs); + emitter << YAML::DoubleQuoted << YAML::Flow << root; + ofs << "\n"; + ofs.close(); +} + +} // namespace QLog diff --git a/iocore/net/quic/qlog/QLog.h b/iocore/net/quic/qlog/QLog.h new file mode 100644 index 00000000000..2407d58202d --- /dev/null +++ b/iocore/net/quic/qlog/QLog.h @@ -0,0 +1,145 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "QLogEvent.h" + +namespace QLog +{ +class Trace +{ +public: + enum class VantagePointType : uint8_t { + client, + server, + network, + unknown, + }; + + struct VantagePoint { + std::string name; + VantagePointType type; + VantagePointType flow = VantagePointType::unknown; + }; + + Trace(std::string odcid, std::string title = "", std::string desc = "") : _reference_time(Thread::get_hrtime()), _odcid(odcid) {} + + Trace(const VantagePoint &vp, std::string odcid, std::string title = "", std::string desc = "") : Trace(odcid, title, desc) + { + set_vantage_point(vp); + } + + static const char * + vantage_point_type_name(VantagePointType ty) + { + switch (ty) { + case VantagePointType::client: + return "client"; + case VantagePointType::server: + return "server"; + case VantagePointType::network: + return "network"; + case VantagePointType::unknown: + return "unknown"; + default: + return nullptr; + } + } + + void + set_vantage_point(const VantagePoint vp) + { + this->_vp = vp; + } + + Trace & + push_event(QLogEventUPtr e) + { + this->_events.push_back(std::move(e)); + return *this; + } + + std::string + odcid() const + { + return this->_odcid; + } + + void encode(YAML::Node &node); + +private: + int64_t _reference_time = Thread::get_hrtime(); + std::string _odcid; + std::string _title; + std::string _desc; + + VantagePoint _vp; + + std::vector _events; +}; + +class QLog +{ +public: + static constexpr char QLOG_VERSION[] = "draft-01"; + + QLog(std::string title = "", std::string desc = "", std::string ver = QLOG_VERSION) : _title(title), _desc(desc), _ver(ver) {} + + Trace & + new_trace(Trace::VantagePoint vp, std::string odcid, std::string title = "", std::string desc = "") + { + this->_traces.push_back(std::make_unique(vp, odcid, title, desc)); + return *this->_traces.back().get(); + } + + Trace & + new_trace(std::string odcid, std::string title = "", std::string desc = "") + { + this->_traces.push_back(std::make_unique(odcid, title, desc)); + return *this->_traces.back().get(); + } + + Trace & + last_trace() + { + if (this->_traces.empty()) { + throw std::invalid_argument("traces is empty"); + } + + return *this->_traces.back().get(); + } + + void dump(std::string dir); + +private: + std::string _title; + std::string _desc; + std::string _ver; + std::vector> _traces; +}; + +} // namespace QLog diff --git a/iocore/net/quic/qlog/QLogEvent.cc b/iocore/net/quic/qlog/QLogEvent.cc new file mode 100644 index 00000000000..98bec4ed898 --- /dev/null +++ b/iocore/net/quic/qlog/QLogEvent.cc @@ -0,0 +1,317 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QLogEvent.h" + +namespace QLog +{ +void +check_and_set(YAML::Node &node, std::string key, std::string val) +{ + if (val.length() > 0) { + node[key] = val; + } +} + +void +check_and_set(YAML::Node &node, std::string key, std::vector val) +{ + if (val.size() > 0) { + node[key] = val; + } +} + +template +void +check_and_set(YAML::Node &node, std::string key, T val) +{ + if (val) { + node[key] = val; + } +} + +namespace Connectivity +{ + void + ServerListening::encode(YAML::Node &node) + { + check_and_set(node, "ip_v4", _ip_v4); + check_and_set(node, "ip_v6", _ip_v6); + check_and_set(node, "port_v4", _port_v4); + check_and_set(node, "port_v6", _port_v6); + check_and_set(node, "stateless_reset_required", _port_v6); + check_and_set(node, "quic_version", _quic_version); + check_and_set(node, "alpn_values", _alpn_values); + } + + void + ConnectionStarted::encode(YAML::Node &node) + { + check_and_set(node, "quic_version", _quic_version); + check_and_set(node, "ip_version", _ip_version); + check_and_set(node, "src_ip", _src_ip); + check_and_set(node, "dst_ip", _dst_ip); + check_and_set(node, "protocol", _protocol); + check_and_set(node, "src_port", _src_port); + check_and_set(node, "dst_port", _dst_port); + check_and_set(node, "src_cid", _src_cid); + check_and_set(node, "dst_cid", _dst_cid); + check_and_set(node, "alpn_values", _alpn_values); + } + + void + ConnectionIdUpdated::encode(YAML::Node &node) + { + check_and_set(node, "src_old", _src_old); + check_and_set(node, "src_new", _src_new); + check_and_set(node, "dst_old", _dst_old); + check_and_set(node, "dst_new", _dst_new); + } + + void + SpinBitUpdated::encode(YAML::Node &node) + { + check_and_set(node, "state", _state); + } + + void + ConnectionStateUpdated::encode(YAML::Node &node) + { + check_and_set(node, "new", static_cast(_new)); + check_and_set(node, "old", static_cast(_old)); + check_and_set(node, "trigger", trigger_name(_trigger)); + } + +} // namespace Connectivity + +namespace Security +{ + void + KeyEvent::encode(YAML::Node &node) + { + node["key_type"] = static_cast(_key_type); + node["new"] = _new; + check_and_set(node, "generation", _generation); + check_and_set(node, "old", _old); + check_and_set(node, "trigger", trigger_name(_trigger)); + } + +} // namespace Security + +namespace Transport +{ + void + ParametersSet::encode(YAML::Node &node) + { + node["owner"] = _owner ? "local" : "remote"; + check_and_set(node, "resumption_allowed", _resumption_allowed); + check_and_set(node, "early_data_enabled", _early_data_enabled); + check_and_set(node, "alpn", _alpn); + check_and_set(node, "version", _version); + check_and_set(node, "tls_cipher", _tls_cipher); + check_and_set(node, "original_connection_id", _original_connection_id); + check_and_set(node, "stateless_reset_token", _stateless_reset_token); + check_and_set(node, "disable_active_migration", _disable_active_migration); + check_and_set(node, "max_idle_timeout", _max_idle_timeout); + check_and_set(node, "max_udp_payload_size", _max_udp_payload_size); + check_and_set(node, "ack_delay_exponent", _ack_delay_exponent); + check_and_set(node, "max_ack_delay", _max_ack_delay); + check_and_set(node, "active_connection_id_limit", _active_connection_id_limit); + check_and_set(node, "initial_max_data", _initial_max_data); + check_and_set(node, "initial_max_stream_data_bidi_local", _initial_max_stream_data_bidi_local); + check_and_set(node, "initial_max_stream_data_bidi_remote", _initial_max_stream_data_bidi_remote); + check_and_set(node, "initial_max_stream_data_uni", _initial_max_stream_data_uni); + check_and_set(node, "initial_max_streams_bidi", _initial_max_streams_bidi); + check_and_set(node, "initial_max_streams_uni", _initial_max_streams_uni); + + if (_preferred_address.ip.length() > 0) { + YAML::Node sub; + check_and_set(sub, _preferred_address.ipv4 ? "ip_v4" : "ip_v6", _preferred_address.ip); + check_and_set(sub, _preferred_address.ipv4 ? "port_v4" : "port_v6", _preferred_address.port); + check_and_set(sub, "connection_id", _preferred_address.connection_id); + check_and_set(sub, "stateless_reset_token", _preferred_address.stateless_reset_token); + node["preferred_address"] = sub; + } + } + + void + PacketEvent::encode(YAML::Node &node) + { + node["packet_type"] = _packet_type; + for (auto &&it : this->_frames) { + YAML::Node sub; + it->encode(sub); + node["frames"].push_back(sub); + } + check_and_set(node, "is_coalesced", _is_coalesced); + check_and_set(node, "stateless_reset_token", _stateless_reset_token); + check_and_set(node, "supported_version", _supported_version); + check_and_set(node, "raw_encrypted", _raw_encrypted); + check_and_set(node, "raw_decrypted", _raw_decrypted); + check_and_set(node, "supported_version", _supported_version); + check_and_set(node, "supported_version", trigger_name(_trigger)); + + node["header"]["packet_number"] = _header.packet_number; + node["header"]["packet_size"] = _header.packet_size; + node["header"]["payload_length"] = _header.payload_length; + node["header"]["version"] = _header.version; + node["header"]["scil"] = _header.scil; + node["header"]["dcil"] = _header.dcil; + node["header"]["scid"] = _header.scid; + node["header"]["dcid"] = _header.dcid; + } + + void + PacketDropped::encode(YAML::Node &node) + { + node["packet_type"] = _packet_type; + check_and_set(node, "packet_size", _packet_size); + check_and_set(node, "raw", _raw); + check_and_set(node, "trigger", trigger_name(_trigger)); + } + + void + PacketBuffered::encode(YAML::Node &node) + { + node["packet_type"] = _packet_type; + check_and_set(node, "trigger", trigger_name(_trigger)); + check_and_set(node, "packet_number", trigger_name(_trigger)); + } + + void + DatagramsEvent::encode(YAML::Node &node) + { + check_and_set(node, "count", _count); + check_and_set(node, "byte_length", _byte_length); + } + + void + DatagramsDropped::encode(YAML::Node &node) + { + check_and_set(node, "byte_length", _byte_length); + } + + void + StreamStateUpdated::encode(YAML::Node &node) + { + node["new"] = static_cast(_new); + node["stream_id"] = _stream_id; + // FXIME + // node["stream_type"] = bidi ? "bidirectional" : "unidirectional"; + // node["stream_side"] = "sending"; + } + + void + FrameProcessed::encode(YAML::Node &node) + { + for (auto &&it : _frames) { + YAML::Node sub; + it->encode(sub); + node["frames"].push_back(sub); + } + } + +} // namespace Transport + +namespace Recovery +{ + void + ParametersSet::encode(YAML::Node &node) + { + check_and_set(node, "reordering_threshold", _reordering_threshold); + check_and_set(node, "time_threshold", _time_threshold); + check_and_set(node, "timer_granularity", _timer_granularity); + check_and_set(node, "initial_rtt", _initial_rtt); + check_and_set(node, "max_datagram_size", _max_datagram_size); + check_and_set(node, "initial_congestion_window", _initial_congestion_window); + check_and_set(node, "minimum_congestion_window", _minimum_congestion_window); + check_and_set(node, "loss_reduction_factor", _loss_reduction_factor); + check_and_set(node, "persistent_congestion_threshold", _persistent_congestion_threshold); + } + + void + MetricsUpdated::encode(YAML::Node &node) + { + check_and_set(node, "min_rtt", _min_rtt); + check_and_set(node, "smoothed_rtt", _smoothed_rtt); + check_and_set(node, "latest_rtt", _latest_rtt); + check_and_set(node, "rtt_variance", _rtt_variance); + check_and_set(node, "max_ack_delay", _max_ack_delay); + check_and_set(node, "pto_count", _pto_count); + check_and_set(node, "congestion_window", _congestion_window); + check_and_set(node, "bytes_in_flight", _bytes_in_flight); + check_and_set(node, "ssthresh", _ssthresh); + check_and_set(node, "packets_in_flight", _packets_in_flight); + check_and_set(node, "in_recovery", _in_recovery); + check_and_set(node, "pacing_rate", _pacing_rate); + } + + void + CongestionStateUpdated::encode(YAML::Node &node) + { + node["new"] = state_to_string(_new); + check_and_set(node, "old", state_to_string(_old)); + check_and_set(node, "old", trigger_name(_trigger)); + } + + void + LossTimerUpdated::encode(YAML::Node &node) + { + node["timer_type"] = _timer_type_ack ? "ack" : "pto"; + check_and_set(node, "event_type", event_type_name(_event_type)); + check_and_set(node, "packet_number_space", _packet_number_space); + if (_event_type == EventType::set) { + check_and_set(node, "delta", _delta); + } + } + + void + PacketLost::encode(YAML::Node &node) + { + node["packet_number"] = _packet_number; + node["packet_type"] = _packet_type; + check_and_set(node, "trigger", trigger_name(_trigger)); + YAML::Node sub; + _header.encode(sub); + node["header"] = sub; + + for (auto &&it : _frames) { + YAML::Node sub; + it->encode(sub); + node["frames"].push_back(sub); + } + } + + void + MarkedForRetransmit::encode(YAML::Node &node) + { + for (auto &&it : _frames) { + YAML::Node sub; + it->encode(sub); + node["frames"].push_back(sub); + } + } + +} // namespace Recovery + +} // namespace QLog diff --git a/iocore/net/quic/qlog/QLogEvent.h b/iocore/net/quic/qlog/QLogEvent.h new file mode 100644 index 00000000000..a03f7cc4f32 --- /dev/null +++ b/iocore/net/quic/qlog/QLogEvent.h @@ -0,0 +1,1013 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "QUICTypes.h" +#include "QLogEvent.h" +#include "QLogFrame.h" + +namespace QLog +{ +class QLogEvent +{ +public: + virtual ~QLogEvent() {} + + virtual std::string category() const = 0; + virtual std::string event() const = 0; + virtual void encode(YAML::Node &) = 0; + + virtual ink_hrtime + get_time() const + { + return this->_time; + }; + +protected: + ink_hrtime _time = Thread::get_hrtime(); +}; + +using QLogEventUPtr = std::unique_ptr; + +#define SET(field, type) \ + void set_##field(type v) { this->_node[#field] = v; } + +// enum class PacketType : uint8_t { initial, handshake, zerortt, onertt, retry, version_negotiation, unknown }; +using PacketType = std::string; + +struct PacketHeader { + std::string packet_number; + uint64_t packet_size; + uint64_t payload_length; + + // only if present in the header + // if correctly using NEW_CONNECTION_ID events, + // dcid can be skipped for 1RTT packets + std::string version; + std::string scil; + std::string dcil; + std::string scid; + std::string dcid; + + // Note: short vs long header is implicit through PacketType + void + encode(YAML::Node &node) + { + node["packet_number"] = packet_number; + node["packet_size"] = packet_size; + node["payload_length"] = payload_length; + node["version"] = version; + node["scil"] = scil; + node["dcil"] = dcil; + node["scid"] = scid; + node["dcid"] = dcid; + } +}; + +#define SET_FUNC(cla, field, type) \ +public: \ + cla &set_##field(type v) \ + { \ + this->_##field = v; \ + return *this; \ + } \ + \ +private: \ + type _##field; + +#define APPEND_FUNC(cla, field, type) \ +public: \ + cla &append_##field(type v) \ + { \ + this->_##field.push_back(v); \ + return *this; \ + } \ + \ +private: \ + std::vector _##field; + +#define APPEND_FRAME_FUNC(cla) \ +public: \ + cla &append_frames(QLogFrameUPtr v) \ + { \ + this->_frames.push_back(std::move(v)); \ + return *this; \ + } \ + \ +private: \ + std::vector _frames; + +// +// connectivity +// +namespace Connectivity +{ + class ConnectivityEvent : public QLogEvent + { + public: + std::string + category() const override + { + return "connectivity"; + } + }; + + class ServerListening : public ConnectivityEvent + { + public: + ServerListening(int port, bool v6 = false) + { + if (v6) { + set_port_v6(port); + } else { + set_port_v4(port); + } + } + +#define _SET(a, b) SET_FUNC(ServerListening, a, b) +#define _APPEND(a, b) APPEND_FUNC(ServerListening, a, b) + _SET(port_v4, int) + _SET(port_v6, int) + _SET(ip_v4, std::string) + _SET(ip_v6, std::string) + _SET(stateless_reset_required, bool) + _APPEND(quic_version, std::string) + _APPEND(alpn_values, std::string) + +#undef _SET +#undef _APPEND + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "server_listening"; + } + }; + + class ConnectionStarted : public ConnectivityEvent + { + public: + ConnectionStarted(std::string version, std::string sip, std::string dip, int sport, int dport, std::string protocol = "QUIC") + { + set_ip_version(version); + set_protocol(protocol); + set_src_ip(sip); + set_dst_ip(dip); + set_src_port(sport); + set_dst_port(dport); + } + +#define _SET(a, b) SET_FUNC(ConnectionStarted, a, b) +#define _APPEND(a, b) APPEND_FUNC(ConnectionStarted, a, b) + _SET(quic_version, std::string); + _SET(src_cid, std::string); + _SET(dst_cid, std::string); + _SET(protocol, std::string); + _SET(ip_version, std::string) + _SET(src_ip, std::string) + _SET(dst_ip, std::string) + _SET(src_port, int) + _SET(dst_port, int) + _APPEND(alpn_values, std::string) + +#undef _SET +#undef _APPEND + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "connection_started"; + } + }; + + class ConnectionIdUpdated : public ConnectivityEvent + { + public: + ConnectionIdUpdated(std::string old, std::string n, bool peer = false) + { + if (peer) { + set_dst_old(old); + set_dst_new(n); + } else { + set_src_old(old); + set_src_new(n); + } + } + +#define _SET(a, b) SET_FUNC(ConnectionIdUpdated, a, b) +#define _APPEND(a, b) APPEND_FUNC(ConnectionIdUpdated, a, b) + + _SET(src_old, std::string); + _SET(src_new, std::string); + _SET(dst_old, std::string); + _SET(dst_new, std::string); + +#undef _SET +#undef _APPEND + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "connection_id_updated"; + } + }; + + class SpinBitUpdated : public ConnectivityEvent + { + public: + SpinBitUpdated(bool state) { set_state(state); } + +#define _SET(a, b) SET_FUNC(SpinBitUpdated, a, b) + _SET(state, bool); +#undef _SET + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "spin_bit_updated"; + } + }; + + class ConnectionStateUpdated : public ConnectivityEvent + { + public: + enum class ConnectionState : uint8_t { + attempted, // client initial sent + reset, // stateless reset sent + handshake, // handshake in progress + active, // handshake successful, data exchange + keepalive, // no data for a longer period + draining, // CONNECTION_CLOSE sent + closed // connection actually fully closed, memory freed + }; + + enum class Triggered : uint8_t { + unknown, + error, // when closing because of an unexpected event + clean, // when closing normally + application // e.g., HTTP/3's GOAWAY frame + }; + + ConnectionStateUpdated(ConnectionState n, Triggered tr = Triggered::unknown) + { + set_new(n); + set_trigger(tr); + } + +#define _SET(a, b) SET_FUNC(ConnectionStateUpdated, a, b) + _SET(new, ConnectionState); + _SET(old, ConnectionState); + _SET(trigger, Triggered) + +#undef _SET + + void encode(YAML::Node &) override; + + static const char * + trigger_name(Triggered trigger) + { + switch (trigger) { + case Triggered::error: + return "error"; + case Triggered::clean: + return "clean"; + case Triggered::application: + return "application"; + default: + return nullptr; + } + } + + std::string + event() const override + { + return "connection_state_updated"; + } + }; + +} // namespace Connectivity + +namespace Security +{ + class KeyEvent : public QLogEvent + { + public: + enum class KeyType : uint8_t { + server_initial_secret, + client_initial_secret, + + server_handshake_secret, + client_handshake_secret, + + server_0rtt_secret, + client_0rtt_secret, + + server_1rtt_secret, + client_1rtt_secret + }; + + enum class Triggered : uint8_t { + unknown, + remote_update, + local_update, + tls, + }; + + KeyEvent(KeyType ty, std::string n, int generation, Triggered triggered = Triggered::unknown) + { + set_key_type(ty); + set_new(n); + set_generation(generation); + set_trigger(triggered); + } + +#define _SET(a, b) SET_FUNC(KeyEvent, a, b) + _SET(key_type, KeyType); + _SET(new, std::string) + _SET(old, std::string); + _SET(generation, int) + _SET(trigger, Triggered) +#undef _SET + + void encode(YAML::Node &) override; + + const char * + trigger_name(Triggered triggered) + { + switch (triggered) { + case Triggered::remote_update: + return "remote_update"; + case Triggered::local_update: + return "local_update"; + case Triggered::tls: + return "tls"; + default: + return nullptr; + } + } + + std::string + category() const override + { + return "security"; + } + }; + + class KeyUpdated : public KeyEvent + { + public: + KeyUpdated(KeyType ty, std::string n, int generation, Triggered triggered = KeyEvent::Triggered::unknown) + : KeyEvent(ty, n, generation, triggered) + { + } + + std::string + event() const override + { + return "key_updated"; + } + }; + + class KeyRetired : public KeyEvent + { + public: + KeyRetired(KeyType ty, std::string n, int generation, Triggered triggered = KeyEvent::Triggered::unknown) + : KeyEvent(ty, n, generation, triggered) + { + } + + std::string + event() const override + { + return "key_retired"; + } + }; + +} // namespace Security + +// +// transport event +// +namespace Transport +{ + class TransportEvent : public QLogEvent + { + public: + std::string + category() const override + { + return "transport"; + } + }; + + class ParametersSet : public TransportEvent + { + public: + struct PreferredAddress { + std::string ip; + int port; + std::string connection_id; + std::string stateless_reset_token; + bool ipv4 = true; + }; + + ParametersSet(bool owner) : _owner(owner) {} + + std::string + event() const override + { + return "parameters_set"; + } + +#define _SET(a, b) SET_FUNC(ParametersSet, a, b) + _SET(resumption_allowed, bool); // early data extension was enabled on the TLS layer + _SET(early_data_enabled, bool); // early data extension was enabled on the TLS layer + _SET(alpn, std::string); + _SET(version, std::string); // hex (e.g. 0x); + _SET(tls_cipher, std::string); // (e.g. AES_128_GCM_SHA256); + _SET(original_connection_id, std::string); // hex + _SET(stateless_reset_token, std::string); // hex + _SET(disable_active_migration, bool); + _SET(idle_timeout, int); + _SET(max_packet_size, int); + _SET(ack_delay_exponent, int); + _SET(max_ack_delay, int); + _SET(active_connection_id_limit, int); + _SET(initial_max_data, std::string); + _SET(initial_max_stream_data_bidi_local, std::string); + _SET(initial_max_stream_data_bidi_remote, std::string); + _SET(initial_max_stream_data_uni, std::string); + _SET(initial_max_streams_bidi, std::string); + _SET(initial_max_streams_uni, std::string); + _SET(max_idle_timeout, int64_t) + _SET(max_udp_payload_size, size_t) + _SET(preferred_address, PreferredAddress) +#undef _SET + + void encode(YAML::Node &) override; + + private: + bool _owner = false; + }; + + class PacketEvent : public TransportEvent + { + public: + enum class Triggered : uint8_t { + unknown, + keys_available, // if packet was buffered because it couldn't be decrypted before + retransmit_reordered, // draft-23 5.1.1 + retransmit_timeout, // draft-23 5.1.2 + pto_probe, // draft-23 5.3.1 + retransmit_crypto, // draft-19 6.2 + cc_bandwidth_probe, // needed for some CCs to figure out bandwidth allocations when there are no normal sends + }; + + PacketEvent(PacketType type, PacketHeader h, Triggered tr = Triggered::unknown) + { + set_packet_type(type).set_header(h).set_trigger(tr); + } + +#define _SET(a, b) SET_FUNC(PacketEvent, a, b) +#define _APPEND(a, b) APPEND_FUNC(PacketEvent, a, b) + _SET(packet_type, PacketType) + _SET(header, PacketHeader) + _SET(is_coalesced, bool); + _SET(raw_encrypted, std::string); + _SET(raw_decrypted, std::string); + _SET(stateless_reset_token, std::string); + _SET(trigger, Triggered); + _APPEND(supported_version, std::string); + +#undef _SET +#undef _APPEND + APPEND_FRAME_FUNC(PacketEvent) + + void encode(YAML::Node &) override; + + static const char * + trigger_name(Triggered triggered) + { + switch (triggered) { + case Triggered::retransmit_reordered: + return "retransmit_reordered"; + case Triggered::retransmit_timeout: + return "retransmit_timeout"; + case Triggered::pto_probe: + return "pto_probe"; + case Triggered::retransmit_crypto: + return "retransmit_crypto"; + case Triggered::cc_bandwidth_probe: + return "cc_bandwidth_probe"; + break; + case Triggered::keys_available: + return "keys_available"; + default: + return nullptr; + } + } + }; + + class PacketSent : public PacketEvent + { + public: + PacketSent(PacketType type, PacketHeader h, Triggered tr = Triggered::unknown) : PacketEvent(type, h, tr) {} + std::string + event() const override + { + return "packet_sent"; + } + }; + + class PacketReceived : public PacketEvent + { + public: + PacketReceived(PacketType type, PacketHeader h, Triggered tr = Triggered::unknown) : PacketEvent(type, h, tr) {} + std::string + event() const override + { + return "packet_received"; + } + }; + + class PacketDropped : public TransportEvent + { + public: + enum class Triggered : uint8_t { + unknown, + key_unavailable, + unknown_connection_id, + header_decrypt_error, + payload_decrypt_error, + protocol_violation, + dos_prevention, + unsupported_version, + unexpected_packet, + unexpected_source_connection_id, + unexpected_version, + }; + + PacketDropped(Triggered tr = Triggered::unknown) { set_trigger(tr); } + +#define _SET(a, b) SET_FUNC(PacketDropped, a, b) + _SET(packet_size, int); + _SET(raw, std::string); + _SET(trigger, Triggered); + _SET(packet_type, PacketType) +#undef _SET + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "packet_dropped"; + } + + static const char * + trigger_name(Triggered tr) + { + switch (tr) { + case Triggered::key_unavailable: + return "key_unavailable"; + case Triggered::unknown_connection_id: + return "unknown_connection_id"; + case Triggered::header_decrypt_error: + return "header_decrypt_error"; + case Triggered::payload_decrypt_error: + return "payload_decrypt_error"; + case Triggered::protocol_violation: + return "protocol_violation"; + case Triggered::dos_prevention: + return "dos_prevention"; + case Triggered::unsupported_version: + return "unsupported_version"; + case Triggered::unexpected_packet: + return "unexpected_packet"; + case Triggered::unexpected_source_connection_id: + return "unexpected_source_connection_id"; + case Triggered::unexpected_version: + return "unexpected_version"; + default: + return nullptr; + } + } + }; + + class PacketBuffered : public TransportEvent + { + public: + enum class Triggered : uint8_t { + unknown, + backpressure, + keys_unavailable, + }; + + PacketBuffered(Triggered tr = Triggered::unknown) { set_trigger(tr); } + +#define _SET(a, b) SET_FUNC(PacketBuffered, a, b) + _SET(trigger, Triggered); + _SET(packet_type, PacketType) + _SET(packet_number, std::string) +#undef _SET + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "packet_buffered"; + } + + static const char * + trigger_name(Triggered tr) + { + switch (tr) { + case Triggered::backpressure: + return "backpressure"; + case Triggered::keys_unavailable: + return "keys_unavailable"; + default: + return nullptr; + } + } + }; + + class DatagramsEvent : public TransportEvent + { + public: +#define _SET(a, b) SET_FUNC(DatagramsEvent, a, b) + _SET(count, int); + _SET(byte_length, int); +#undef _SET + void encode(YAML::Node &) override; + }; + + class DatagramsSent : public DatagramsEvent + { + public: + std::string + event() const override + { + return "datagrams_sent"; + } + }; + class DatagramReceived : public DatagramsEvent + { + public: + std::string + event() const override + { + return "datagrams_received"; + } + }; + + class DatagramsDropped : public TransportEvent + { + public: +#define _SET(a, b) SET_FUNC(DatagramsDropped, a, b) + _SET(byte_length, int); +#undef _SET + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "datagrams_dropped"; + } + }; + + class StreamStateUpdated : public TransportEvent + { + enum class StreamState { + // bidirectional stream states, draft-23 3.4. + idle, + open, + half_closed_local, + half_closed_remote, + closed, + + // sending-side stream states, draft-23 3.1. + ready, + send, + data_sent, + reset_sent, + reset_received, + + // receive-side stream states, draft-23 3.2. + receive, + size_known, + data_read, + reset_read, + + // both-side states + data_received, + + // qlog-defined + destroyed // memory actually freed + }; + + StreamStateUpdated(std::string stream_id, StreamState n) { set_new(n).set_stream_id(stream_id); } + + void encode(YAML::Node &) override; + +#define _SET(a, b) SET_FUNC(StreamStateUpdated, a, b) + _SET(new, StreamState); + _SET(old, StreamState); + _SET(stream_id, std::string); + _SET(bidi, bool); +#undef _SET + + std::string + event() const override + { + return "stream_state_updated"; + } + }; + + class FrameProcessed : public TransportEvent + { + public: + APPEND_FRAME_FUNC(FrameProcessed) + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "frame_processed"; + } + }; + +} // namespace Transport + +namespace Recovery +{ + class RecoveryEvent : public QLogEvent + { + public: + std::string + category() const override + { + return "recovery"; + } + }; + + class ParametersSet : public RecoveryEvent + { + public: +#define _SET(a, b) SET_FUNC(ParametersSet, a, b) + _SET(reordering_threshold, int); + _SET(time_threshold, int); + _SET(timer_granularity, int); + _SET(initial_rtt, int); + _SET(max_datagram_size, int); + _SET(initial_congestion_window, int); + _SET(minimum_congestion_window, int); + _SET(loss_reduction_factor, int); + _SET(persistent_congestion_threshold, int); +#undef _SET + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "parameters_set"; + } + }; + + class MetricsUpdated : public RecoveryEvent + { + public: +#define _SET(a, b) SET_FUNC(MetricsUpdated, a, b) + _SET(min_rtt, int); + _SET(smoothed_rtt, int); + _SET(latest_rtt, int); + _SET(rtt_variance, int); + _SET(max_ack_delay, int); + _SET(pto_count, int); + _SET(congestion_window, int); + _SET(bytes_in_flight, int); + _SET(ssthresh, int); + _SET(packets_in_flight, int); + _SET(in_recovery, int); + _SET(pacing_rate, int); +#undef _SET + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "metrics_updated"; + } + }; + + class CongestionStateUpdated : public RecoveryEvent + { + public: + enum class State : uint8_t { + slow_start, + congestion_avoidance, + application_limited, + recovery, + }; + + enum class Triggered : uint8_t { + unknown, + persistent_congestion, + ECN, + }; + + CongestionStateUpdated(State n, Triggered tr = Triggered::unknown) { set_trigger(tr).set_new(n); } + +#define _SET(a, b) SET_FUNC(CongestionStateUpdated, a, b) + _SET(trigger, Triggered) + _SET(new, State) + _SET(old, State) +#undef _SET + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "congestion_state_updated"; + } + + static const char * + trigger_name(Triggered tr) + { + switch (tr) { + case Triggered::persistent_congestion: + return "persistent_congestion"; + case Triggered::ECN: + return "ECN"; + default: + return nullptr; + } + } + + static const char * + state_to_string(State s) + { + switch (s) { + case State::slow_start: + return "slow_start"; + case State::congestion_avoidance: + return "congestion_avoidance"; + case State::application_limited: + return "application_limited"; + case State::recovery: + return "recovery"; + default: + break; + } + + return nullptr; + } + }; + + class LossTimerUpdated : public RecoveryEvent + { + public: + enum class EventType : uint8_t { + set, + expired, + cancelled, + }; + + void + set_timer_type(bool ack) + { + this->_timer_type_ack = ack; + } + + void encode(YAML::Node &) override; + +#define _SET(a, b) SET_FUNC(LossTimerUpdated, a, b) + _SET(event_type, EventType) + _SET(packet_number_space, int); + _SET(delta, int); +#undef _SET + + std::string + event() const override + { + return "loss_timer_updated"; + } + + static const char * + event_type_name(EventType et) + { + switch (et) { + case EventType::set: + return "set"; + case EventType::expired: + return "expired"; + case EventType::cancelled: + return "cancelled"; + default: + break; + } + return nullptr; + } + + private: + bool _timer_type_ack = false; + }; + + class PacketLost : public RecoveryEvent + { + public: + enum class Triggered : uint8_t { + unknown, + reordering_threshold, + time_threshold, + pto_expired, + }; + + PacketLost(PacketType pt, uint64_t pn, Triggered tr = Triggered::unknown) + { + set_trigger(tr).set_packet_type(pt).set_packet_number(pn); + } + +#define _SET(a, b) SET_FUNC(PacketLost, a, b) + _SET(header, PacketHeader) + _SET(packet_number, uint64_t); + _SET(packet_type, PacketType); + _SET(trigger, Triggered) + APPEND_FRAME_FUNC(PacketLost) +#undef _SET + + void encode(YAML::Node &) override; + + std::string + event() const override + { + return "packet_lost"; + } + + static const char * + trigger_name(Triggered tr) + { + switch (tr) { + case Triggered::pto_expired: + return "pto_expired"; + case Triggered::reordering_threshold: + return "reordering_threshold"; + case Triggered::time_threshold: + return "time_threshold"; + default: + return nullptr; + } + } + }; + + class MarkedForRetransmit : public RecoveryEvent + { + public: + APPEND_FRAME_FUNC(MarkedForRetransmit) + void encode(YAML::Node &) override; + std::string + event() const override + { + return "marked_for_retransmit"; + } + }; + +} // namespace Recovery + +} // namespace QLog diff --git a/iocore/net/quic/qlog/QLogFrame.cc b/iocore/net/quic/qlog/QLogFrame.cc new file mode 100644 index 00000000000..bcc46fcf739 --- /dev/null +++ b/iocore/net/quic/qlog/QLogFrame.cc @@ -0,0 +1,282 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QLogFrame.h" + +namespace QLog +{ +template +const Real & +Convert(const QUICFrame *frame) +{ + // FIXME: dangerous + auto tmp = static_cast(frame); +#if defined(DEBUG) + auto ref = dynamic_cast(tmp); + ink_assert(ref != nullptr); + return *ref; +#endif + return *static_cast(tmp); +} + +QLogFrameUPtr +QLogFrameFactory::create(const QUICFrame *frame) +{ + switch (frame->type()) { + case QUICFrameType::ACK: + return std::make_unique(Convert(frame)); + case QUICFrameType::PADDING: + return std::make_unique(Convert(frame)); + case QUICFrameType::PING: + return std::make_unique(Convert(frame)); + case QUICFrameType::RESET_STREAM: + return std::make_unique(Convert(frame)); + case QUICFrameType::STOP_SENDING: + return std::make_unique(Convert(frame)); + case QUICFrameType::CRYPTO: + return std::make_unique(Convert(frame)); + case QUICFrameType::NEW_TOKEN: + return std::make_unique(Convert(frame)); + case QUICFrameType::STREAM: + return std::make_unique(Convert(frame)); + case QUICFrameType::MAX_DATA: + return std::make_unique(Convert(frame)); + case QUICFrameType::MAX_STREAM_DATA: + return std::make_unique(Convert(frame)); + case QUICFrameType::MAX_STREAMS: + return std::make_unique(Convert(frame)); + case QUICFrameType::DATA_BLOCKED: + return std::make_unique(Convert(frame)); + case QUICFrameType::STREAM_DATA_BLOCKED: + return std::make_unique(Convert(frame)); + case QUICFrameType::STREAMS_BLOCKED: + return std::make_unique(Convert(frame)); + case QUICFrameType::NEW_CONNECTION_ID: + return std::make_unique(Convert(frame)); + case QUICFrameType::RETIRE_CONNECTION_ID: + return std::make_unique(Convert(frame)); + case QUICFrameType::PATH_CHALLENGE: + return std::make_unique(Convert(frame)); + case QUICFrameType::PATH_RESPONSE: + return std::make_unique(Convert(frame)); + case QUICFrameType::CONNECTION_CLOSE: + return std::make_unique(Convert(frame)); + case QUICFrameType::HANDSHAKE_DONE: + return std::make_unique(Convert(frame)); + default: + ink_release_assert(0); + return nullptr; + } +} + +namespace Frame +{ + template + std::string + convert_to_string(T a) + { + return std::to_string(static_cast(a)); + } + + void + AckFrame::encode(YAML::Node &node) + { + node["frame_type"] = "ack"; + node["ack_delay"] = std::to_string(ack_delay); + for (auto &it : acked_range) { + YAML::Node sub; + sub.push_back(convert_to_string(it.first())); + sub.push_back(convert_to_string(it.last())); + node["acked_ranges"].push_back(sub); + } + + if (ect1) { + node["ect1"] = ect1; + } + + if (ect1) { + node["ect0"] = ect0; + } + + if (ce) { + node["ce"] = ce; + } + } + + void + StreamFrame::encode(YAML::Node &node) + { + node["frame_type"] = "stream"; + node["stream_id"] = stream_id; + node["offset"] = offset; + node["length"] = length; + node["fin"] = fin; + } + + void + PaddingFrame::encode(YAML::Node &node) + { + node["frame_type"] = "padding"; + } + + void + PingFrame::encode(YAML::Node &node) + { + node["frame_type"] = "ping"; + } + + void + RstStreamFrame::encode(YAML::Node &node) + { + node["frame_type"] = "reset_stream"; + node["stream_id"] = stream_id; + node["error_code"] = error_code; + node["final_size"] = final_size; + } + + void + StopSendingFrame::encode(YAML::Node &node) + { + node["frame_type"] = "stop_sending"; + node["stream_id"] = stream_id; + node["error_code"] = error_code; + } + + void + CryptoFrame::encode(YAML::Node &node) + { + node["frame_type"] = "crypto"; + node["offset"] = offset; + node["length"] = length; + } + + void + NewTokenFrame::encode(YAML::Node &node) + { + node["frame_type"] = "new_token"; + node["token"] = token; + node["length"] = length; + } + + void + MaxDataFrame::encode(YAML::Node &node) + { + node["frame_type"] = "max_data"; + node["maximum"] = maximum; + } + + void + MaxStreamDataFrame::encode(YAML::Node &node) + { + node["frame_type"] = "max_stream_data"; + node["maximum"] = maximum; + node["stream_id"] = stream_id; + } + + void + MaxStreamsFrame::encode(YAML::Node &node) + { + node["frame_type"] = "max_streams"; + node["maximum"] = maximum; + node["stream_type"] = stream_type; + } + + void + DataBlockedFrame::encode(YAML::Node &node) + { + node["frame_type"] = "data_blocked"; + node["limit"] = limit; + } + + void + StreamDataBlockedFrame::encode(YAML::Node &node) + { + node["frame_type"] = "stream_data_blocked"; + node["limit"] = limit; + node["stream_id"] = stream_id; + } + + void + StreamsBlockedFrame::encode(YAML::Node &node) + { + node["frame_type"] = "streams_blocked"; + node["stream_id"] = stream_id; + node["stream_type"] = stream_type; + } + + void + NewConnectionIDFrame::encode(YAML::Node &node) + { + node["frame_type"] = "new_connection_id"; + node["sequence_number"] = sequence_number; + node["retire_prior_to"] = retire_prior_to; + node["stateless_reset_token"] = stateless_reset_token; + node["length"] = length; + } + + void + RetireConnectionIDFrame::encode(YAML::Node &node) + { + node["frame_type"] = "retire_connection_id"; + node["sequence_number"] = sequence_number; + } + + void + PathChallengeFrame::encode(YAML::Node &node) + { + node["frame_type"] = "path_challenge"; + node["data"] = data; + } + + void + PathResponseFrame::encode(YAML::Node &node) + { + node["frame_type"] = "path_response"; + node["data"] = data; + } + + void + ConnectionCloseFrame::encode(YAML::Node &node) + { + node["frame_type"] = "connection_close"; + node["error_space"] = error_space; + node["error_code"] = error_code; + node["raw_error_code"] = raw_error_code; + node["reason"] = reason; + } + + void + HandshakeDoneFrame::encode(YAML::Node &node) + { + node["frame_type"] = "handshake_done"; + } + + void + UnknownFrame::encode(YAML::Node &node) + { + node["frame_type"] = "unknown"; + node["raw_frame_type"] = raw_frame_type; + } + +} // namespace Frame +} // namespace QLog diff --git a/iocore/net/quic/qlog/QLogFrame.h b/iocore/net/quic/qlog/QLogFrame.h new file mode 100644 index 00000000000..be56351a1ee --- /dev/null +++ b/iocore/net/quic/qlog/QLogFrame.h @@ -0,0 +1,309 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "QUICFrame.h" + +namespace QLog +{ +class QLogFrame +{ +public: + QLogFrame(QUICFrameType type) : _type(type) {} + virtual ~QLogFrame() {} + + QUICFrameType + type() const + { + return this->_type; + } + + // encode frame into YAML stype + virtual void encode(YAML::Node &node) = 0; + +protected: + QUICFrameType _type = QUICFrameType::UNKNOWN; +}; + +using QLogFrameUPtr = std::unique_ptr; + +// +// convert QUICFrame to QLogFrame +// +class QLogFrameFactory +{ +public: + // create QLogFrame + static QLogFrameUPtr create(const QUICFrame *frame); +}; + +namespace Frame +{ + struct AckFrame : public QLogFrame { + AckFrame(const QUICAckFrame &frame) : QLogFrame(frame.type()) + { + acked_range = frame.ranges(); + ack_delay = frame.ack_delay(); + if (frame.ecn_section()) { + ect0 = frame.ecn_section()->ect0_count(); + ect1 = frame.ecn_section()->ect1_count(); + ce = frame.ecn_section()->ecn_ce_count(); + } + } + + void encode(YAML::Node &) override; + + std::set acked_range; + uint64_t ect1 = 0; + uint64_t ect0 = 0; + uint64_t ce = 0; + uint64_t ack_delay = 0; + }; + + struct StreamFrame : public QLogFrame { + StreamFrame(const QUICStreamFrame &frame) : QLogFrame(frame.type()) + { + stream_id = std::to_string(static_cast(frame.stream_id())); + offset = std::to_string(static_cast(frame.offset())); + length = frame.data_length(); + fin = frame.has_fin_flag(); + } + + void encode(YAML::Node &) override; + std::string stream_id; + + // These two MUST always be set + // If not present in the Frame type, log their default values + std::string offset; + uint64_t length = 0; + + // this MAY be set any time, but MUST only be set if the value is "true" + // if absent, the value MUST be assumed to be "false" + bool fin = false; + + // FIXME raw + }; + + struct PaddingFrame : public QLogFrame { + PaddingFrame(const QUICPaddingFrame &frame) : QLogFrame(frame.type()) {} + void encode(YAML::Node &) override; + }; + + struct PingFrame : public QLogFrame { + PingFrame(const QUICPingFrame &frame) : QLogFrame(frame.type()) {} + void encode(YAML::Node &) override; + }; + + struct RstStreamFrame : public QLogFrame { + RstStreamFrame(const QUICRstStreamFrame &frame) : QLogFrame(frame.type()) + { + stream_id = std::to_string(static_cast(frame.stream_id())); + error_code = frame.error_code(); + final_size = std::to_string(frame.final_offset()); + } + + void encode(YAML::Node &) override; + std::string stream_id; + // FIXME ApplicationError + uint64_t error_code = 0; + std::string final_size; + }; + + struct StopSendingFrame : public QLogFrame { + StopSendingFrame(const QUICStopSendingFrame &frame) : QLogFrame(frame.type()) + { + stream_id = std::to_string(static_cast(frame.stream_id())); + error_code = frame.error_code(); + } + + void encode(YAML::Node &) override; + std::string stream_id; + // FIXME ApplicationError + uint64_t error_code = 0; + }; + + struct CryptoFrame : public QLogFrame { + CryptoFrame(const QUICCryptoFrame &frame) : QLogFrame(frame.type()) + { + offset = std::to_string(static_cast(frame.offset())); + length = frame.data_length(); + } + + void encode(YAML::Node &) override; + std::string offset; + uint64_t length = 0; + }; + + struct NewTokenFrame : public QLogFrame { + NewTokenFrame(const QUICNewTokenFrame &frame) : QLogFrame(frame.type()) + { + token = QUICBase::to_hex(frame.token(), frame.token_length()); + length = frame.token_length(); + } + + void encode(YAML::Node &) override; + std::string token; + uint64_t length = 0; + }; + + struct MaxDataFrame : public QLogFrame { + MaxDataFrame(const QUICMaxDataFrame &frame) : QLogFrame(frame.type()) { maximum = std::to_string(frame.maximum_data()); } + + void encode(YAML::Node &) override; + std::string maximum; + }; + + struct MaxStreamDataFrame : public QLogFrame { + MaxStreamDataFrame(const QUICMaxStreamDataFrame &frame) : QLogFrame(frame.type()) + { + stream_id = std::to_string(static_cast(frame.stream_id())); + maximum = std::to_string(frame.maximum_stream_data()); + } + + void encode(YAML::Node &) override; + std::string stream_id; + std::string maximum; + }; + + struct MaxStreamsFrame : public QLogFrame { + MaxStreamsFrame(const QUICMaxStreamsFrame &frame) : QLogFrame(frame.type()) + { + maximum = std::to_string(frame.maximum_streams()); + // FIXME + stream_type = "bidirectional"; + } + + void encode(YAML::Node &) override; + std::string stream_type; + std::string maximum; + }; + + struct DataBlockedFrame : public QLogFrame { + DataBlockedFrame(const QUICDataBlockedFrame &frame) : QLogFrame(frame.type()) + { + limit = std::to_string(static_cast(frame.offset())); + } + void encode(YAML::Node &) override; + std::string limit; + }; + + struct StreamDataBlockedFrame : public QLogFrame { + StreamDataBlockedFrame(const QUICStreamDataBlockedFrame &frame) : QLogFrame(frame.type()) + { + limit = std::to_string(static_cast(frame.offset())); + stream_id = std::to_string(static_cast(frame.stream_id())); + } + + void encode(YAML::Node &) override; + std::string stream_id, limit; + }; + + struct StreamsBlockedFrame : public QLogFrame { + StreamsBlockedFrame(const QUICStreamIdBlockedFrame &frame) : QLogFrame(frame.type()) + { + stream_type = "bidirectional"; + stream_id = std::to_string(static_cast(frame.stream_id())); + } + + void encode(YAML::Node &) override; + std::string stream_id, stream_type; + }; + + struct NewConnectionIDFrame : public QLogFrame { + NewConnectionIDFrame(const QUICNewConnectionIdFrame &frame) : QLogFrame(frame.type()) + { + sequence_number = std::to_string(frame.sequence()); + retire_prior_to = std::to_string(frame.retire_prior_to()); + connection_id = frame.connection_id().hex(); + stateless_reset_token = QUICBase::to_hex(frame.stateless_reset_token().buf(), QUICStatelessResetToken::LEN); + length = frame.connection_id().length(); + } + + void encode(YAML::Node &) override; + std::string sequence_number, retire_prior_to, connection_id, stateless_reset_token; + uint8_t length = 0; + }; + + struct RetireConnectionIDFrame : public QLogFrame { + RetireConnectionIDFrame(const QUICRetireConnectionIdFrame &frame) : QLogFrame(frame.type()) + { + sequence_number = std::to_string(frame.seq_num()); + } + void encode(YAML::Node &) override; + std::string sequence_number; + }; + + struct PathChallengeFrame : public QLogFrame { + PathChallengeFrame(const QUICPathChallengeFrame &frame) : QLogFrame(frame.type()) + { + data = QUICBase::to_hex(frame.data(), QUICPathChallengeFrame::DATA_LEN); + } + void encode(YAML::Node &) override; + std::string data; + }; + + struct PathResponseFrame : public QLogFrame { + PathResponseFrame(const QUICPathResponseFrame &frame) : QLogFrame(frame.type()) + { + data = QUICBase::to_hex(frame.data(), QUICPathChallengeFrame::DATA_LEN); + } + void encode(YAML::Node &) override; + std::string data; + }; + + struct ConnectionCloseFrame : public QLogFrame { + ConnectionCloseFrame(const QUICConnectionCloseFrame &frame, bool app = false) : QLogFrame(frame.type()) + { + error_space = app ? "application" : "transport"; + error_code = frame.error_code(); + // FIXME + raw_error_code = error_code; + reason = frame.reason_phrase(); + } + + void encode(YAML::Node &) override; + std::string error_space, reason, trigger_frame_type; + uint64_t error_code, raw_error_code; + }; + + struct HandshakeDoneFrame : public QLogFrame { + HandshakeDoneFrame(const QUICHandshakeDoneFrame &frame) : QLogFrame(frame.type()){}; + void encode(YAML::Node &) override; + }; + + struct UnknownFrame : public QLogFrame { + UnknownFrame(const QUICUnknownFrame &frame) : QLogFrame(frame.type()) + { + // FIXME + raw_frame_type = static_cast(frame.type()); + } + + void encode(YAML::Node &) override; + uint8_t raw_frame_type = 0; + }; +} // namespace Frame +} // namespace QLog diff --git a/iocore/net/quic/qlog/QLogListener.h b/iocore/net/quic/qlog/QLogListener.h new file mode 100644 index 00000000000..7b55af2ceff --- /dev/null +++ b/iocore/net/quic/qlog/QLogListener.h @@ -0,0 +1,119 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "QLog.h" +#include "QLogUtils.h" +#include "QUICPacket.h" +#include "QUICContext.h" + +namespace QLog +{ +class QLogListener : public QUICCallback +{ +public: + QLogListener(QUICContext &ctx, std::string odcid, std::string title = "", std::string desc = "") : _context(ctx) + { + this->_log.new_trace(odcid, title, desc); // initial new trace + } + + void + frame_recv_callback(QUICCallbackContext &, const QUICFrame &frame) override + { + this->_recv_frames.push_back(QLogFrameFactory::create(static_cast(&frame))); + } + + void + frame_packetize_callback(QUICCallbackContext &, const QUICFrame &frame) override + { + this->_send_frames.push_back(QLogFrameFactory::create(static_cast(&frame))); + } + + void + packet_send_callback(QUICCallbackContext &, const QUICPacket &packet) override + { + auto qe = std::make_unique(PacketTypeToName(packet.type()), QUICPacketToLogPacket(packet)); + for (auto &it : this->_send_frames) { + qe->append_frames(std::move(it)); + } + this->_send_frames.clear(); + this->_log.last_trace().push_event(std::move(qe)); + }; + + void + packet_recv_callback(QUICCallbackContext &, const QUICPacket &packet) override + { + auto qe = std::make_unique(PacketTypeToName(packet.type()), QUICPacketToLogPacket(packet)); + for (auto &it : this->_recv_frames) { + qe->append_frames(std::move(it)); + } + this->_recv_frames.clear(); + this->_log.last_trace().push_event(std::move(qe)); + }; + + void + packet_lost_callback(QUICCallbackContext &, const QUICPacketInfo &packet) override + { + auto qe = std::make_unique(PacketTypeToName(packet.type), packet.packet_number); + this->_log.last_trace().push_event(std::move(qe)); + }; + + void + cc_metrics_update_callback(QUICCallbackContext &, uint64_t congestion_window, uint64_t bytes_in_flight, uint64_t sshresh) override + { + auto qe = std::make_unique(); + qe->set_congestion_window(static_cast(congestion_window)).set_bytes_in_flight(bytes_in_flight).set_ssthresh(sshresh); + this->_log.last_trace().push_event(std::move(qe)); + } + + void + congestion_state_updated_callback(QUICCallbackContext &, QUICCongestionController::State state) override + { + if (state != this->_state) { + this->_log.last_trace().push_event(std::make_unique(CongestionStateConvert(state))); + this->_state = state; + } + } + + void + connection_close_callback(QUICCallbackContext &) override + { + this->_log.dump(this->_context.config()->qlog_dir()); + } + + Trace & + last_trace() + { + return this->_log.last_trace(); + } + +private: + QUICCongestionController::State _state = QUICCongestionController::State::SLOW_START; + std::vector _recv_frames; + std::vector _send_frames; + QLog _log; + QUICContext &_context; +}; + +} // namespace QLog diff --git a/iocore/net/quic/qlog/QLogUtils.h b/iocore/net/quic/qlog/QLogUtils.h new file mode 100644 index 00000000000..2242a4fd6e4 --- /dev/null +++ b/iocore/net/quic/qlog/QLogUtils.h @@ -0,0 +1,80 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QLog.h" +#include "QUICPacket.h" + +namespace QLog +{ +inline static const char * +PacketTypeToName(QUICPacketType pt) +{ + switch (pt) { + case QUICPacketType::INITIAL: + return "initial"; + case QUICPacketType::HANDSHAKE: + return "handshake"; + case QUICPacketType::ZERO_RTT_PROTECTED: + return "0rtt"; + case QUICPacketType::PROTECTED: + return "1rtt"; + case QUICPacketType::RETRY: + return "retry"; + case QUICPacketType::VERSION_NEGOTIATION: + return "version_negotiation"; + case QUICPacketType::STATELESS_RESET: + return "stateless_reset"; + default: + return "unknown"; + } +} + +inline static Recovery::CongestionStateUpdated::State +CongestionStateConvert(QUICCongestionController::State state) +{ + switch (state) { + case QUICCongestionController::State::APPPLICATION_LIMITED: + return Recovery::CongestionStateUpdated::State::application_limited; + case QUICCongestionController::State::SLOW_START: + return Recovery::CongestionStateUpdated::State::slow_start; + case QUICCongestionController::State::CONGESTION_AVOIDANCE: + return Recovery::CongestionStateUpdated::State::congestion_avoidance; + case QUICCongestionController::State::RECOVERY: + return Recovery::CongestionStateUpdated::State::recovery; + default: + return Recovery::CongestionStateUpdated::State::slow_start; + } +} + +inline static PacketHeader +QUICPacketToLogPacket(const QUICPacket &packet) +{ + PacketHeader ph; + ph.dcid = packet.destination_cid().hex(); + ph.packet_number = std::to_string(packet.packet_number()); + ph.packet_size = packet.size(); + ph.payload_length = packet.payload_length(); + return ph; +} + +} // namespace QLog diff --git a/iocore/net/quic/test/main.cc b/iocore/net/quic/test/main.cc index eb93ea784b6..4e7ee5adc6f 100644 --- a/iocore/net/quic/test/main.cc +++ b/iocore/net/quic/test/main.cc @@ -30,6 +30,7 @@ #include "tscore/Diags.h" #include "RecordsConfig.h" +#include "QUICGlobals.h" #include "QUICConfig.h" struct EventProcessorListener : Catch::TestEventListenerBase { @@ -49,6 +50,7 @@ struct EventProcessorListener : Catch::TestEventListenerBase { LibRecordsConfigInit(); QUICConfig::startup(); + QUIC::init(); EThread *thread = new EThread(); thread->set_specific(); diff --git a/iocore/net/quic/test/test_QUICAckFrameCreator.cc b/iocore/net/quic/test/test_QUICAckFrameCreator.cc index 39b7bc9db4d..c60ecd9c765 100644 --- a/iocore/net/quic/test/test_QUICAckFrameCreator.cc +++ b/iocore/net/quic/test/test_QUICAckFrameCreator.cc @@ -133,11 +133,34 @@ TEST_CASE("QUICAckFrameManager should send", "[quic]") ack_manager.update(level, 0, 1, false); CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == false); + ack_manager.update(level, 1, 1, true); + CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == false); + + ack_manager.update(level, 3, 1, false); + CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == true); + } + + SECTION("QUIC every two ack eliciting packet", "[quic]") + { + QUICAckFrameManager ack_manager; + uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE]; + + QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; + ack_manager.update(level, 0, 1, false); + CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == false); + ack_manager.update(level, 1, 1, false); + CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == true); + + CHECK(ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0) != nullptr); + + ack_manager.update(level, 2, 1, false); CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == false); ack_manager.update(level, 3, 1, false); CHECK(ack_manager.will_generate_frame(level, 0, true, 0) == true); + + CHECK(ack_manager.generate_frame(frame_buf, level, UINT16_MAX, UINT16_MAX, 0, 0) != nullptr); } SECTION("QUIC delay too much time", "[quic]") diff --git a/iocore/net/quic/test/test_QUICFrame.cc b/iocore/net/quic/test/test_QUICFrame.cc index 4460066ba5b..ec7a4324e00 100644 --- a/iocore/net/quic/test/test_QUICFrame.cc +++ b/iocore/net/quic/test/test_QUICFrame.cc @@ -68,8 +68,10 @@ TEST_CASE("QUICFrame Type", "[quic]") CHECK(QUICFrame::type(reinterpret_cast("\x1c")) == QUICFrameType::CONNECTION_CLOSE); CHECK(QUICFrame::type(reinterpret_cast("\x1d")) == QUICFrameType::CONNECTION_CLOSE); + CHECK(QUICFrame::type(reinterpret_cast("\x1e")) == QUICFrameType::HANDSHAKE_DONE); + // Undefined ragne - CHECK(QUICFrame::type(reinterpret_cast("\x1e")) == QUICFrameType::UNKNOWN); + CHECK(QUICFrame::type(reinterpret_cast("\x1f")) == QUICFrameType::UNKNOWN); CHECK(QUICFrame::type(reinterpret_cast("\xff")) == QUICFrameType::UNKNOWN); } @@ -826,6 +828,41 @@ TEST_CASE("Store Padding Frame", "[quic]") CHECK(memcmp(buf, expected, len) == 0); } +TEST_CASE("Load HandshakeDone Frame", "[quic]") +{ + uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE]; + uint8_t buf[] = { + 0x1e, // Type + }; + const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr); + CHECK(frame->type() == QUICFrameType::HANDSHAKE_DONE); + CHECK(frame->size() == 1); + + const QUICHandshakeDoneFrame *handshake_done_frame = static_cast(frame); + CHECK(handshake_done_frame != nullptr); +} + +TEST_CASE("Store HandshakeDone Frame", "[quic]") +{ + uint8_t buf[16]; + size_t len = 0; + + uint8_t expected[] = { + 0x1e, // Type + }; + + QUICHandshakeDoneFrame frame; + CHECK(frame.size() == 1); + + Ptr ibb = frame.to_io_buffer_block(sizeof(buf)); + for (auto b = ibb; b; b = b->next) { + memcpy(buf + len, b->start(), b->size()); + len += b->size(); + } + CHECK(len == 1); + CHECK(memcmp(buf, expected, len) == 0); +} + TEST_CASE("ConnectionClose Frame", "[quic]") { uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE]; diff --git a/iocore/net/quic/test/test_QUICFrameDispatcher.cc b/iocore/net/quic/test/test_QUICFrameDispatcher.cc index e34322bad80..10e2d318804 100644 --- a/iocore/net/quic/test/test_QUICFrameDispatcher.cc +++ b/iocore/net/quic/test/test_QUICFrameDispatcher.cc @@ -39,7 +39,7 @@ TEST_CASE("QUICFrameHandler", "[quic]") MockQUICContext context; MockQUICConnection connection; - MockQUICStreamManager streamManager = {&connection}; + MockQUICStreamManager streamManager = {&context}; MockQUICConnectionInfoProvider info; MockQUICLossDetector lossDetector(context); @@ -62,7 +62,8 @@ TEST_CASE("QUICFrameHandler", "[quic]") } bool should_send_ack; bool is_flow_controlled; - quicFrameDispatcher.receive_frames(QUICEncryptionLevel::INITIAL, buf, len, should_send_ack, is_flow_controlled, nullptr, nullptr); + quicFrameDispatcher.receive_frames(context, QUICEncryptionLevel::INITIAL, buf, len, should_send_ack, is_flow_controlled, nullptr, + nullptr); CHECK(connection.getTotalFrameCount() == 0); CHECK(streamManager.getTotalFrameCount() == 1); @@ -74,7 +75,8 @@ TEST_CASE("QUICFrameHandler", "[quic]") memcpy(buf + len, b->start(), b->size()); len += b->size(); } - quicFrameDispatcher.receive_frames(QUICEncryptionLevel::INITIAL, buf, len, should_send_ack, is_flow_controlled, nullptr, nullptr); + quicFrameDispatcher.receive_frames(context, QUICEncryptionLevel::INITIAL, buf, len, should_send_ack, is_flow_controlled, nullptr, + nullptr); CHECK(connection.getTotalFrameCount() == 1); CHECK(streamManager.getTotalFrameCount() == 1); } diff --git a/iocore/net/quic/test/test_QUICFrameRetransmitter.cc b/iocore/net/quic/test/test_QUICFrameRetransmitter.cc index c8a91a5a1ae..916c65f5ede 100644 --- a/iocore/net/quic/test/test_QUICFrameRetransmitter.cc +++ b/iocore/net/quic/test/test_QUICFrameRetransmitter.cc @@ -189,7 +189,7 @@ TEST_CASE("QUICFrameRetransmitter successfully split crypto frame", "[quic]") info->level = QUICEncryptionLevel::INITIAL; Ptr block = make_ptr(new_IOBufferBlock()); - block->alloc(); + block->alloc(BUFFER_SIZE_INDEX_32K); memcpy(block->start(), data, sizeof(data)); block->fill(sizeof(data)); @@ -243,7 +243,7 @@ TEST_CASE("QUICFrameRetransmitter successfully split stream frame with fin flag" info->level = QUICEncryptionLevel::INITIAL; Ptr block = make_ptr(new_IOBufferBlock()); - block->alloc(); + block->alloc(BUFFER_SIZE_INDEX_32K); memcpy(block->start(), data, sizeof(data)); block->fill(sizeof(data)); diff --git a/iocore/net/quic/test/test_QUICHandshakeProtocol.cc b/iocore/net/quic/test/test_QUICHandshakeProtocol.cc index f90b10af3fa..4a71a07be96 100644 --- a/iocore/net/quic/test/test_QUICHandshakeProtocol.cc +++ b/iocore/net/quic/test/test_QUICHandshakeProtocol.cc @@ -45,9 +45,6 @@ struct PollCont; #include "P_UnixNet.h" #include "P_UnixNetVConnection.h" -// depends on size of cert -static constexpr uint32_t MAX_HANDSHAKE_MSG_LEN = 8192; - #include "./server_cert.h" static void @@ -115,49 +112,52 @@ TEST_CASE("QUICHandshakeProtocol") QUICPacketPayloadProtector ppp_client(pp_key_info_client); QUICPacketPayloadProtector ppp_server(pp_key_info_server); + auto client_tp = std::make_shared(); + auto server_tp = std::make_shared(); + client_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + server_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + client->set_local_transport_parameters(client_tp); + server->set_local_transport_parameters(server_tp); + CHECK(client->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); // CH - QUICHandshakeMsgs msg1; - uint8_t msg1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg1.buf = msg1_buf; - msg1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(client->handshake(&msg1, nullptr) == 1); + QUICHandshakeMsgs msg0; + msg0.offsets[0] = 0; + msg0.offsets[1] = 0; + msg0.offsets[2] = 0; + msg0.offsets[3] = 0; + msg0.offsets[4] = 0; + + QUICHandshakeMsgs *msg1 = nullptr; + REQUIRE(client->handshake(&msg1, &msg0) == 1); + REQUIRE(msg1); std::cout << "### Messages from client" << std::endl; - print_hex(msg1.buf, msg1.offsets[4]); + print_hex(msg1->buf, msg1->offsets[4]); // SH, EE, CERT, CV, FIN - QUICHandshakeMsgs msg2; - uint8_t msg2_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg2.buf = msg2_buf; - msg2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg2, &msg1) == 1); + QUICHandshakeMsgs *msg2 = nullptr; + REQUIRE(server->handshake(&msg2, msg1) == 1); + REQUIRE(msg2); std::cout << "### Messages from server" << std::endl; - print_hex(msg2.buf, msg2.offsets[4]); + print_hex(msg2->buf, msg2->offsets[4]); // FIN - QUICHandshakeMsgs msg3; - uint8_t msg3_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg3.buf = msg3_buf; - msg3.max_buf_len = MAX_HANDSHAKE_MSG_LEN; + QUICHandshakeMsgs *msg3; -#ifdef SSL_MODE_QUIC_HACK - // -- Hacks for OpenSSL with SSL_MODE_QUIC_HACK -- // SH QUICHandshakeMsgs msg2_1; uint8_t msg2_1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; msg2_1.buf = msg2_1_buf; msg2_1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - memcpy(msg2_1.buf, msg2.buf, msg2.offsets[1]); + memcpy(msg2_1.buf, msg2->buf, msg2->offsets[1]); msg2_1.offsets[0] = 0; - msg2_1.offsets[1] = msg2.offsets[1]; - msg2_1.offsets[2] = msg2.offsets[1]; - msg2_1.offsets[3] = msg2.offsets[1]; - msg2_1.offsets[4] = msg2.offsets[1]; + msg2_1.offsets[1] = msg2->offsets[1]; + msg2_1.offsets[2] = msg2->offsets[1]; + msg2_1.offsets[3] = msg2->offsets[1]; + msg2_1.offsets[4] = msg2->offsets[1]; // EE - FIN QUICHandshakeMsgs msg2_2; @@ -165,8 +165,8 @@ TEST_CASE("QUICHandshakeProtocol") msg2_2.buf = msg2_2_buf; msg2_2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - size_t len = msg2.offsets[3] - msg2.offsets[2]; - memcpy(msg2_2.buf, msg2.buf + msg2.offsets[1], len); + size_t len = msg2->offsets[3] - msg2->offsets[2]; + memcpy(msg2_2.buf, msg2->buf + msg2->offsets[1], len); msg2_2.offsets[0] = 0; msg2_2.offsets[1] = 0; msg2_2.offsets[2] = 0; @@ -175,29 +175,19 @@ TEST_CASE("QUICHandshakeProtocol") REQUIRE(client->handshake(&msg3, &msg2_1) == 1); REQUIRE(client->handshake(&msg3, &msg2_2) == 1); -#else - REQUIRE(client->handshake(&msg3, &msg2) == 1); -#endif std::cout << "### Messages from client" << std::endl; - print_hex(msg3.buf, msg3.offsets[4]); + print_hex(msg3->buf, msg3->offsets[4]); // NS - QUICHandshakeMsgs msg4; - uint8_t msg4_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg4.buf = msg4_buf; - msg4.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg4, &msg3) == 1); + QUICHandshakeMsgs *msg4 = nullptr; + REQUIRE(server->handshake(&msg4, msg3) == 1); + REQUIRE(msg4); std::cout << "### Messages from server" << std::endl; - print_hex(msg4.buf, msg4.offsets[4]); + print_hex(msg4->buf, msg4->offsets[4]); - QUICHandshakeMsgs msg5; - uint8_t msg5_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg5.buf = msg5_buf; - msg5.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - REQUIRE(client->handshake(&msg5, &msg4) == 1); - std::cout << "### Messages from server" << std::endl; - print_hex(msg4.buf, msg4.offsets[4]); + QUICHandshakeMsgs *msg5 = nullptr; + REQUIRE(client->handshake(&msg5, msg4) == 1); + REQUIRE(msg5 == nullptr); // encrypt - decrypt // client (encrypt) - server (decrypt) @@ -247,69 +237,66 @@ TEST_CASE("QUICHandshakeProtocol") QUICPacketPayloadProtector ppp_client(pp_key_info_client); QUICPacketPayloadProtector ppp_server(pp_key_info_server); + auto client_tp = std::make_shared(); + auto server_tp = std::make_shared(); + client_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + server_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + client->set_local_transport_parameters(client_tp); + server->set_local_transport_parameters(server_tp); + CHECK(client->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); // CH - QUICHandshakeMsgs msg1; - uint8_t msg1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg1.buf = msg1_buf; - msg1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(client->handshake(&msg1, nullptr) == 1); + QUICHandshakeMsgs msg0; + msg0.offsets[0] = 0; + msg0.offsets[1] = 0; + msg0.offsets[2] = 0; + msg0.offsets[3] = 0; + msg0.offsets[4] = 0; + + QUICHandshakeMsgs *msg1 = nullptr; + REQUIRE(client->handshake(&msg1, &msg0) == 1); + REQUIRE(msg1); std::cout << "### Messages from client" << std::endl; - print_hex(msg1.buf, msg1.offsets[4]); + print_hex(msg1->buf, msg1->offsets[4]); // HRR - QUICHandshakeMsgs msg2; - uint8_t msg2_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg2.buf = msg2_buf; - msg2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg2, &msg1) == 1); + QUICHandshakeMsgs *msg2 = nullptr; + REQUIRE(server->handshake(&msg2, msg1) == 1); + REQUIRE(msg2); std::cout << "### Messages from server" << std::endl; - print_hex(msg2.buf, msg2.offsets[4]); + print_hex(msg2->buf, msg2->offsets[4]); // CH - QUICHandshakeMsgs msg3; - uint8_t msg3_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg3.buf = msg3_buf; - msg3.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(client->handshake(&msg3, &msg2) == 1); + QUICHandshakeMsgs *msg3 = nullptr; + REQUIRE(client->handshake(&msg3, msg2) == 1); + REQUIRE(msg3); std::cout << "### Messages from client" << std::endl; - print_hex(msg3.buf, msg3.offsets[4]); + print_hex(msg3->buf, msg3->offsets[4]); // SH, EE, CERT, CV, FIN - QUICHandshakeMsgs msg4; - uint8_t msg4_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg4.buf = msg4_buf; - msg4.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg4, &msg3) == 1); + QUICHandshakeMsgs *msg4 = nullptr; + REQUIRE(server->handshake(&msg4, msg3) == 1); + REQUIRE(msg4); std::cout << "### Messages from server" << std::endl; - print_hex(msg4.buf, msg4.offsets[4]); + print_hex(msg4->buf, msg4->offsets[4]); // FIN - QUICHandshakeMsgs msg5; - uint8_t msg5_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg5.buf = msg5_buf; - msg5.max_buf_len = MAX_HANDSHAKE_MSG_LEN; + QUICHandshakeMsgs *msg5 = nullptr; -#ifdef SSL_MODE_QUIC_HACK - // -- Hacks for OpenSSL with SSL_MODE_QUIC_HACK -- // SH QUICHandshakeMsgs msg4_1; uint8_t msg4_1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; msg4_1.buf = msg4_1_buf; msg4_1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - memcpy(msg4_1.buf, msg4.buf, msg4.offsets[1]); + memcpy(msg4_1.buf, msg4->buf, msg4->offsets[1]); msg4_1.offsets[0] = 0; - msg4_1.offsets[1] = msg4.offsets[1]; - msg4_1.offsets[2] = msg4.offsets[1]; - msg4_1.offsets[3] = msg4.offsets[1]; - msg4_1.offsets[4] = msg4.offsets[1]; + msg4_1.offsets[1] = msg4->offsets[1]; + msg4_1.offsets[2] = msg4->offsets[1]; + msg4_1.offsets[3] = msg4->offsets[1]; + msg4_1.offsets[4] = msg4->offsets[1]; // EE - FIN QUICHandshakeMsgs msg4_2; @@ -317,8 +304,8 @@ TEST_CASE("QUICHandshakeProtocol") msg4_2.buf = msg4_2_buf; msg4_2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - size_t len = msg4.offsets[3] - msg4.offsets[2]; - memcpy(msg4_2.buf, msg4.buf + msg4.offsets[1], len); + size_t len = msg4->offsets[3] - msg4->offsets[2]; + memcpy(msg4_2.buf, msg4->buf + msg4->offsets[1], len); msg4_2.offsets[0] = 0; msg4_2.offsets[1] = 0; msg4_2.offsets[2] = 0; @@ -327,21 +314,16 @@ TEST_CASE("QUICHandshakeProtocol") REQUIRE(client->handshake(&msg5, &msg4_1) == 1); REQUIRE(client->handshake(&msg5, &msg4_2) == 1); -#else - REQUIRE(client->handshake(&msg5, &msg4) == 1); -#endif + REQUIRE(msg5); std::cout << "### Messages from client" << std::endl; - print_hex(msg5.buf, msg5.offsets[4]); + print_hex(msg5->buf, msg5->offsets[4]); // NS - QUICHandshakeMsgs msg6; - uint8_t msg6_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg6.buf = msg6_buf; - msg6.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg6, &msg5) == 1); + QUICHandshakeMsgs *msg6 = nullptr; + REQUIRE(server->handshake(&msg6, msg5) == 1); + REQUIRE(msg6); std::cout << "### Messages from server" << std::endl; - print_hex(msg6.buf, msg6.offsets[4]); + print_hex(msg6->buf, msg6->offsets[4]); Ptr original_ibb = make_ptr(new_IOBufferBlock()); original_ibb->set_internal(const_cast(original), sizeof(original), BUFFER_SIZE_NOT_ALLOCATED); @@ -405,13 +387,10 @@ TEST_CASE("QUICHandshakeProtocol") msg1.offsets[3] = msg1_len; msg1.offsets[4] = msg1_len; - QUICHandshakeMsgs msg2; - uint8_t msg2_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg2.buf = msg2_buf; - msg2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - + QUICHandshakeMsgs *msg2 = nullptr; CHECK(server->handshake(&msg2, &msg1) != 1); - CHECK(msg2.error_code == 0x10a); //< 0x100 + unexpected_message(10) + CHECK(server->has_crypto_error()); + CHECK((server->crypto_error() == 0x10a || server->crypto_error() == 0x150)); //< 0x100 + unexpected_message(10) // Teardown delete server; @@ -425,47 +404,50 @@ TEST_CASE("QUICHandshakeProtocol") QUICHandshakeProtocol *client = new QUICTLS(pp_key_info_client, client_ssl_ctx, NET_VCONNECTION_OUT, netvc_options); QUICHandshakeProtocol *server = new QUICTLS(pp_key_info_server, server_ssl_ctx, NET_VCONNECTION_IN, netvc_options); + auto client_tp = std::make_shared(); + auto server_tp = std::make_shared(); + client_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + server_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + client->set_local_transport_parameters(client_tp); + server->set_local_transport_parameters(server_tp); + CHECK(client->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); // # Start Handshake - // CH - QUICHandshakeMsgs msg1; - uint8_t msg1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg1.buf = msg1_buf; - msg1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; + QUICHandshakeMsgs msg0; + msg0.offsets[0] = 0; + msg0.offsets[1] = 0; + msg0.offsets[2] = 0; + msg0.offsets[3] = 0; + msg0.offsets[4] = 0; - REQUIRE(client->handshake(&msg1, nullptr) == 1); + // CH + QUICHandshakeMsgs *msg1 = nullptr; + REQUIRE(client->handshake(&msg1, &msg0) == 1); + REQUIRE(msg1); // SH, EE, CERT, CV, FIN - QUICHandshakeMsgs msg2; - uint8_t msg2_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg2.buf = msg2_buf; - msg2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg2, &msg1) == 1); + QUICHandshakeMsgs *msg2 = nullptr; + REQUIRE(server->handshake(&msg2, msg1) == 1); + REQUIRE(msg2); // FIN - QUICHandshakeMsgs msg3; - uint8_t msg3_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg3.buf = msg3_buf; - msg3.max_buf_len = MAX_HANDSHAKE_MSG_LEN; + QUICHandshakeMsgs *msg3 = nullptr; -#ifdef SSL_MODE_QUIC_HACK - // -- Hacks for OpenSSL with SSL_MODE_QUIC_HACK -- // SH QUICHandshakeMsgs msg2_1; uint8_t msg2_1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; msg2_1.buf = msg2_1_buf; msg2_1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - memcpy(msg2_1.buf, msg2.buf, msg2.offsets[1]); + memcpy(msg2_1.buf, msg2->buf, msg2->offsets[1]); msg2_1.offsets[0] = 0; - msg2_1.offsets[1] = msg2.offsets[1]; - msg2_1.offsets[2] = msg2.offsets[1]; - msg2_1.offsets[3] = msg2.offsets[1]; - msg2_1.offsets[4] = msg2.offsets[1]; + msg2_1.offsets[1] = msg2->offsets[1]; + msg2_1.offsets[2] = msg2->offsets[1]; + msg2_1.offsets[3] = msg2->offsets[1]; + msg2_1.offsets[4] = msg2->offsets[1]; // EE - FIN QUICHandshakeMsgs msg2_2; @@ -473,8 +455,8 @@ TEST_CASE("QUICHandshakeProtocol") msg2_2.buf = msg2_2_buf; msg2_2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - size_t len = msg2.offsets[3] - msg2.offsets[2]; - memcpy(msg2_2.buf, msg2.buf + msg2.offsets[1], len); + size_t len = msg2->offsets[3] - msg2->offsets[2]; + memcpy(msg2_2.buf, msg2->buf + msg2->offsets[1], len); msg2_2.offsets[0] = 0; msg2_2.offsets[1] = 0; msg2_2.offsets[2] = 0; @@ -483,23 +465,15 @@ TEST_CASE("QUICHandshakeProtocol") REQUIRE(client->handshake(&msg3, &msg2_1) == 1); REQUIRE(client->handshake(&msg3, &msg2_2) == 1); -#else - REQUIRE(client->handshake(&msg3, &msg2) == 1); -#endif // NS - QUICHandshakeMsgs msg4; - uint8_t msg4_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg4.buf = msg4_buf; - msg4.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg4, &msg3) == 1); - - QUICHandshakeMsgs msg5; - uint8_t msg5_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg5.buf = msg5_buf; - msg5.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - REQUIRE(client->handshake(&msg5, &msg4) == 1); + QUICHandshakeMsgs *msg4 = nullptr; + REQUIRE(server->handshake(&msg4, msg3) == 1); + REQUIRE(msg4); + + QUICHandshakeMsgs *msg5 = nullptr; + REQUIRE(client->handshake(&msg5, msg4) == 1); + REQUIRE(msg5 == nullptr); // # End Handshake diff --git a/iocore/net/quic/test/test_QUICLossDetector.cc b/iocore/net/quic/test/test_QUICLossDetector.cc index 6c3604ffef3..acabd8252ad 100644 --- a/iocore/net/quic/test/test_QUICLossDetector.cc +++ b/iocore/net/quic/test/test_QUICLossDetector.cc @@ -24,6 +24,8 @@ #include "catch.hpp" #include "QUICLossDetector.h" +#include "QUICPacketFactory.h" +#include "QUICAckFrameCreator.h" #include "QUICEvents.h" #include "Mock.h" #include "tscore/ink_hrtime.h" @@ -43,10 +45,11 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") QUICPadder padder(NetVConnectionContext_t::NET_VCONNECTION_IN); MockQUICCongestionController cc; QUICLossDetector detector(context, &cc, &rtt_measure, &pinger, &padder); - ats_unique_buf payload = ats_unique_malloc(512); - size_t payload_len = 512; - QUICPacketUPtr packet = QUICPacketFactory::create_null_packet(); - QUICAckFrame *frame = nullptr; + Ptr payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(512, BUFFER_SIZE_INDEX_32K)); + size_t payload_len = 512; + QUICPacketUPtr packet = QUICPacketFactory::create_null_packet(); + QUICAckFrame *frame = nullptr; SECTION("Handshake") { @@ -66,21 +69,21 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") CHECK(len < 4); // Send SERVER_CLEARTEXT (Handshake message) - ats_unique_buf payload = ats_unique_malloc(sizeof(raw)); - memcpy(payload.get(), raw, sizeof(raw)); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(sizeof(raw), BUFFER_SIZE_INDEX_32K)); + memcpy(payload->start(), raw, sizeof(raw)); + payload->fill(sizeof(raw)); - QUICPacketHeaderUPtr header = - QUICPacketHeader::build(QUICPacketType::HANDSHAKE, QUICKeyPhase::HANDSHAKE, - {reinterpret_cast("\xff\xdd\xbb\x99\x77\x55\x33\x11"), 8}, - {reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8}, 0x00000001, 0, 0x00112233, - false, std::move(payload), sizeof(raw)); - QUICPacketUPtr packet = QUICPacketUPtr(new QUICPacket(std::move(header), std::move(payload), sizeof(raw), true, false), - [](QUICPacket *p) { delete p; }); + QUICHandshakePacket *handshake_packet = new QUICHandshakePacket( + 0x00112233, {reinterpret_cast("\xff\xdd\xbb\x99\x77\x55\x33\x11"), 8}, + {reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8}, sizeof(raw), 0, true, true, false); + handshake_packet->attach_payload(payload, true); + QUICPacketUPtr packet = QUICPacketUPtr(handshake_packet, [](QUICPacket *p) { delete p; }); detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{ packet->packet_number(), Thread::get_hrtime(), packet->is_ack_eliciting(), - packet->is_crypto_packet(), + static_cast(*packet).is_crypto_packet(), true, packet->size(), packet->type(), @@ -105,37 +108,67 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") // Send packet (1) to (7) QUICPacketNumberSpace pn_space = QUICPacketNumberSpace::ApplicationData; QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet1 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_1_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet1 = pf.create_short_header_packet( + packet_1_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); REQUIRE(packet1 != nullptr); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet2 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet3 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet4 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet5 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet6 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet7 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet8 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet9 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); - payload = ats_unique_malloc(payload_len); - QUICPacketUPtr packet10 = pf.create_protected_packet(connection_id, detector.largest_acked_packet_number(pn_space), - std::move(payload), payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_2_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet2 = pf.create_short_header_packet( + packet_2_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_3_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet3 = pf.create_short_header_packet( + packet_3_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_4_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet4 = pf.create_short_header_packet( + packet_4_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_5_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet5 = pf.create_short_header_packet( + packet_5_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_6_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet6 = pf.create_short_header_packet( + packet_6_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_7_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet7 = pf.create_short_header_packet( + packet_7_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_8_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet8 = pf.create_short_header_packet( + packet_8_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_9_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet9 = pf.create_short_header_packet( + packet_9_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); + payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(payload_len, BUFFER_SIZE_INDEX_32K)); + payload->fill(payload_len); + uint8_t packet_10_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet10 = pf.create_short_header_packet( + packet_10_buf, connection_id, detector.largest_acked_packet_number(pn_space), payload, payload_len, true, false); QUICPacketNumber pn1 = packet1->packet_number(); QUICPacketNumber pn2 = packet2->packet_number(); @@ -152,7 +185,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet1->packet_number(), Thread::get_hrtime(), packet1->is_ack_eliciting(), - packet1->is_crypto_packet(), + false, true, packet1->size(), packet1->type(), @@ -161,7 +194,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet2->packet_number(), Thread::get_hrtime(), packet2->is_ack_eliciting(), - packet2->is_crypto_packet(), + false, true, packet2->size(), packet2->type(), @@ -170,7 +203,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet3->packet_number(), Thread::get_hrtime(), packet3->is_ack_eliciting(), - packet3->is_crypto_packet(), + false, true, packet3->size(), packet3->type(), @@ -179,7 +212,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet4->packet_number(), Thread::get_hrtime(), packet4->is_ack_eliciting(), - packet4->is_crypto_packet(), + false, true, packet4->size(), packet4->type(), @@ -188,7 +221,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet5->packet_number(), Thread::get_hrtime(), packet5->is_ack_eliciting(), - packet5->is_crypto_packet(), + false, true, packet5->size(), packet5->type(), @@ -197,7 +230,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet6->packet_number(), Thread::get_hrtime(), packet6->is_ack_eliciting(), - packet6->is_crypto_packet(), + false, true, packet6->size(), packet6->type(), @@ -206,7 +239,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet7->packet_number(), Thread::get_hrtime(), packet6->is_ack_eliciting(), - packet7->is_crypto_packet(), + false, true, packet7->size(), packet7->type(), @@ -215,7 +248,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet8->packet_number(), Thread::get_hrtime(), packet6->is_ack_eliciting(), - packet8->is_crypto_packet(), + false, true, packet8->size(), packet8->type(), @@ -224,7 +257,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet9->packet_number(), Thread::get_hrtime(), packet6->is_ack_eliciting(), - packet9->is_crypto_packet(), + false, true, packet9->size(), packet9->type(), @@ -233,7 +266,7 @@ TEST_CASE("QUICLossDetector_Loss", "[quic]") detector.on_packet_sent(QUICPacketInfoUPtr(new QUICPacketInfo{packet10->packet_number(), Thread::get_hrtime(), packet10->is_ack_eliciting(), - packet10->is_crypto_packet(), + false, true, packet10->size(), packet10->type(), diff --git a/iocore/net/quic/test/test_QUICPacket.cc b/iocore/net/quic/test/test_QUICPacket.cc index 573e1a89077..5dda30a6016 100644 --- a/iocore/net/quic/test/test_QUICPacket.cc +++ b/iocore/net/quic/test/test_QUICPacket.cc @@ -25,11 +25,18 @@ #include "quic/QUICPacket.h" -TEST_CASE("QUICPacketHeader - Long", "[quic]") +TEST_CASE("Receiving Packet", "[quic]") { - SECTION("Long Header (load) Version Negotiation Packet") + const uint8_t raw_dcid[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // + 0x10, 0x11, // + }; + QUICConnectionId dcid(raw_dcid, sizeof(raw_dcid)); + + SECTION("Version Negotiation Packet") { - const uint8_t input[] = { + uint8_t input[] = { 0xc0, // Long header, Type: NONE 0x00, 0x00, 0x00, 0x00, // Version 0x08, // DCID Len @@ -39,23 +46,20 @@ TEST_CASE("QUICPacketHeader - Long", "[quic]") 0x00, 0x00, 0x00, 0x08, // Supported Version 1 0x00, 0x00, 0x00, 0x09, // Supported Version 1 }; - ats_unique_buf uinput = ats_unique_malloc(sizeof(input)); - memcpy(uinput.get(), input, sizeof(input)); - - QUICPacketHeaderUPtr header = QUICPacketHeader::load({}, {}, std::move(uinput), sizeof(input), 0); - CHECK(header->size() == sizeof(input) - 8); - CHECK(header->packet_size() == sizeof(input)); - CHECK(header->type() == QUICPacketType::VERSION_NEGOTIATION); - CHECK( - (header->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8))); - CHECK((header->source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8))); - CHECK(header->has_version() == true); - CHECK(header->version() == 0x00000000); + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICVersionNegotiationPacketR packet(nullptr, {}, {}, input_ibb); + CHECK(packet.type() == QUICPacketType::VERSION_NEGOTIATION); + CHECK(packet.size() == sizeof(input)); + CHECK(packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8)); + CHECK(packet.source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8)); + CHECK(packet.version() == 0x00000000); } - SECTION("Long Header (load) INITIAL Packet") + SECTION("INITIAL Packet") { - const uint8_t input[] = { + uint8_t input[] = { 0xc3, // Long header, Type: INITIAL 0x11, 0x22, 0x33, 0x44, // Version 0x08, // DCID Len @@ -67,60 +71,54 @@ TEST_CASE("QUICPacketHeader - Long", "[quic]") 0x01, 0x23, 0x45, 0x67, // Packet number 0xff, 0xff, // Payload (dummy) }; - ats_unique_buf uinput = ats_unique_malloc(sizeof(input)); - memcpy(uinput.get(), input, sizeof(input)); - - QUICPacketHeaderUPtr header = QUICPacketHeader::load({}, {}, std::move(uinput), sizeof(input), 0); - CHECK(header->size() == sizeof(input) - 2); // Packet Length - Payload Length - CHECK(header->packet_size() == sizeof(input)); - CHECK(header->type() == QUICPacketType::INITIAL); - CHECK( - (header->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8))); - CHECK((header->source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8))); - CHECK(header->packet_number() == 0x01234567); - CHECK(header->has_version() == true); - CHECK(header->version() == 0x11223344); + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICInitialPacketR packet(nullptr, {}, {}, input_ibb, 0); + CHECK(packet.type() == QUICPacketType::INITIAL); + CHECK(packet.size() == sizeof(input)); // Packet Length - Payload Length + CHECK(packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8)); + CHECK(packet.source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8)); + CHECK(packet.packet_number() == 0x01234567); + CHECK(packet.version() == 0x11223344); } - SECTION("Long Header (load) RETRY Packet") + SECTION("RETRY Packet") { - const uint8_t input[] = { + uint8_t input[] = { 0xf5, // Long header, Type: RETRY 0x11, 0x22, 0x33, 0x44, // Version 0x08, // DCID Len 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID 0x08, // SCID Len 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID - 0x08, // ODCID Len - 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Original Destination Connection ID - 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, // Retry Token - 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, + 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Retry Token + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // + 0x10, 0x11, 0x12, 0x13, 0x14, 0xf0, 0xf1, 0xf2, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Retry Integrity Tag + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; - ats_unique_buf uinput = ats_unique_malloc(sizeof(input)); - memcpy(uinput.get(), input, sizeof(input)); - - const uint8_t retry_token[] = {0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0}; - - QUICPacketHeaderUPtr header = QUICPacketHeader::load({}, {}, std::move(uinput), sizeof(input), 0); - CHECK(header->size() == sizeof(input) - 16); // Packet Length - Payload Length (Retry Token) - CHECK(header->packet_size() == sizeof(input)); - CHECK(header->type() == QUICPacketType::RETRY); - CHECK( - (header->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8))); - CHECK((header->source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8))); - - QUICPacketLongHeader *retry_header = static_cast(header.get()); - CHECK((retry_header->original_dcid() == - QUICConnectionId(reinterpret_cast("\x08\x07\x06\x05\x04\x03\x02\x01"), 8))); - - CHECK(memcmp(header->payload(), retry_token, 16) == 0); - CHECK(header->has_version() == true); - CHECK(header->version() == 0x11223344); + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(input, sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + const uint8_t retry_token[] = { + 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0xf0, 0xf1, 0xf2, + }; + + QUICRetryPacketR packet(nullptr, {}, {}, input_ibb); + CHECK(packet.type() == QUICPacketType::RETRY); + CHECK(packet.size() == sizeof(input)); + CHECK(packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8)); + CHECK(packet.source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8)); + + CHECK(memcmp(packet.token().buf(), retry_token, 24) == 0); + CHECK(packet.version() == 0x11223344); } - SECTION("Long Header (parse) INITIAL Packet") + SECTION("INITIAL Packet") { - const uint8_t buf[] = { + uint8_t input[] = { 0xc3, // Long header, Type: INITIAL 0x11, 0x22, 0x33, 0x44, // Version 0x08, // DCID Len @@ -132,49 +130,90 @@ TEST_CASE("QUICPacketHeader - Long", "[quic]") 0x01, 0x23, 0x45, 0x67, // Packet number 0xff, 0xff, // Payload (dummy) }; + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(input, sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); - QUICPacketType type; - REQUIRE(QUICPacketLongHeader::type(type, buf, sizeof(buf))); - CHECK(type == QUICPacketType::INITIAL); - - QUICVersion version; - REQUIRE(QUICPacketLongHeader::version(version, buf, sizeof(buf))); - CHECK(version == 0x11223344); - - uint8_t dcil; - REQUIRE(QUICPacketLongHeader::dcil(dcil, buf, sizeof(buf))); - CHECK(dcil == 8); + QUICInitialPacketR packet(nullptr, {}, {}, input_ibb, 0); - uint8_t scil; - REQUIRE(QUICPacketLongHeader::scil(scil, buf, sizeof(buf))); - CHECK(dcil == 8); + CHECK(packet.type() == QUICPacketType::INITIAL); + CHECK(packet.size() == sizeof(input)); + CHECK(packet.version() == 0x11223344); + CHECK(packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8)); + CHECK(packet.source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8)); + CHECK(packet.token().length() == 0); size_t token_length; uint8_t token_length_field_len; size_t token_length_field_offset; - REQUIRE(QUICPacketLongHeader::token_length(token_length, token_length_field_len, token_length_field_offset, buf, sizeof(buf))); + CHECK(QUICInitialPacketR::token_length(token_length, token_length_field_len, token_length_field_offset, input, sizeof(input))); CHECK(token_length == 0); CHECK(token_length_field_len == 1); CHECK(token_length_field_offset == 23); + } - size_t length; - uint8_t length_field_len; - size_t length_field_offset; - REQUIRE(QUICPacketLongHeader::length(length, length_field_len, length_field_offset, buf, sizeof(buf))); - CHECK(length == 6); - CHECK(length_field_len == 1); - CHECK(length_field_offset == 24); - - size_t pn_offset; - REQUIRE(QUICPacketLongHeader::packet_number_offset(pn_offset, buf, sizeof(buf))); - CHECK(pn_offset == 25); - - size_t packet_length; - REQUIRE(QUICPacketLongHeader::packet_length(packet_length, buf, sizeof(buf))); - CHECK(packet_length == sizeof(buf)); + SECTION("Short Header Packet") + { + uint8_t input[] = { + 0x43, // Short header with (K=0) + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // + 0x10, 0x11, // + 0x01, 0x23, 0x45, 0x67, // Packet number + 0xff, 0xff, // Payload (dummy) + }; + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICShortHeaderPacketR packet(nullptr, {}, {}, input_ibb, 0); + CHECK(packet.size() == 25); + CHECK(packet.key_phase() == QUICKeyPhase::PHASE_0); + CHECK(packet.destination_cid() == dcid); + CHECK(packet.packet_number() == 0x01234567); } +} - SECTION("Long Header (store) INITIAL Packet") +TEST_CASE("Sending Packet", "[quic]") +{ + const uint8_t raw_dcid[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // + 0x10, 0x11, // + }; + QUICConnectionId dcid(raw_dcid, sizeof(raw_dcid)); + + SECTION("Short Header Packet (store)") + { + uint8_t buf[32] = {0}; + size_t len = 0; + + const uint8_t expected[] = { + 0x43, // Short header with (K=0) + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // + 0x10, 0x11, // + 0x01, 0x23, 0x45, 0x67, // Packet number + 0x11, 0x22, 0x33, 0x44, 0x55, // Protected Payload + }; + size_t payload_len = 5; + Ptr payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(5, BUFFER_SIZE_INDEX_32K)); + payload->fill(5); + memcpy(payload->start(), expected + sizeof(expected) - payload_len, payload_len); + + QUICShortHeaderPacket packet(dcid, 0x1234567, 0, QUICKeyPhase::PHASE_0, true, true); + packet.attach_payload(payload, true); + + CHECK(packet.size() - 16 == 28); + CHECK(packet.key_phase() == QUICKeyPhase::PHASE_0); + CHECK(packet.type() == QUICPacketType::PROTECTED); + CHECK(packet.destination_cid() == dcid); + CHECK(packet.packet_number() == 0x01234567); + + packet.store(buf, &len); + CHECK(len == sizeof(expected)); + CHECK(memcmp(buf, expected, sizeof(expected)) == 0); + } + SECTION("INITIAL Packet (store)") { uint8_t buf[64] = {0}; size_t len = 0; @@ -191,31 +230,30 @@ TEST_CASE("QUICPacketHeader - Long", "[quic]") 0x01, 0x23, 0x45, 0x67, // Packet number 0x11, 0x22, 0x33, 0x44, 0x55, // Payload (dummy) }; - ats_unique_buf payload = ats_unique_malloc(5); - memcpy(payload.get(), expected + 17, 5); - - QUICPacketHeaderUPtr header = QUICPacketHeader::build( - QUICPacketType::INITIAL, QUICKeyPhase::INITIAL, {reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8}, - {reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8}, 0x01234567, 0, 0x11223344, true, - std::move(payload), 5); - - CHECK(header->size() == sizeof(expected) - 5); - CHECK(header->packet_size() == sizeof(expected)); - CHECK(header->type() == QUICPacketType::INITIAL); - CHECK( - (header->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8))); - CHECK((header->source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8))); - CHECK(header->packet_number() == 0x01234567); - CHECK(header->has_version() == true); - CHECK(header->version() == 0x11223344); - CHECK(header->is_crypto_packet()); - - header->store(buf, &len); - CHECK(len == header->size()); - CHECK(memcmp(buf, expected, len) == 0); + Ptr payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(5, BUFFER_SIZE_INDEX_32K)); + payload->fill(5); + memcpy(payload->start(), expected + 17, 5); + + QUICInitialPacket packet(0x11223344, {reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8}, + {reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8}, 0, nullptr, 5, 0x01234567, + true, true, true); + packet.attach_payload(payload, true); + + CHECK(packet.size() == sizeof(expected) + 16); + CHECK(packet.type() == QUICPacketType::INITIAL); + CHECK((packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8))); + CHECK((packet.source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8))); + CHECK(packet.packet_number() == 0x01234567); + CHECK(packet.version() == 0x11223344); + CHECK(packet.is_crypto_packet()); + + packet.store(buf, &len); + CHECK(len == packet.size() - 16); + CHECK(memcmp(buf, expected, len - 16) == 0); } - SECTION("Long Header (store) RETRY Packet") + SECTION("RETRY Packet (store)") { uint8_t buf[64] = {0}; size_t len = 0; @@ -227,104 +265,25 @@ TEST_CASE("QUICPacketHeader - Long", "[quic]") 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID 0x08, // SCID Len 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID - 0x08, // ODCID Len - 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Original Destination Connection ID - 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, // Retry Token - 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, - }; - ats_unique_buf payload = ats_unique_malloc(16); - memcpy(payload.get(), expected + 30, 16); - - QUICPacketHeaderUPtr header = - QUICPacketHeader::build(QUICPacketType::RETRY, QUICKeyPhase::INITIAL, 0x11223344, - {reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8}, - {reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8}, - {reinterpret_cast("\x08\x07\x06\x05\x04\x03\x02\x01"), 8}, std::move(payload), 16); - - CHECK(header->size() == sizeof(expected) - 16); - CHECK(header->packet_size() == sizeof(expected)); - CHECK(header->type() == QUICPacketType::RETRY); - CHECK( - (header->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8))); - CHECK((header->source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8))); - CHECK(header->has_version() == true); - CHECK(header->version() == 0x11223344); - - QUICPacketLongHeader *retry_header = static_cast(header.get()); - CHECK((retry_header->original_dcid() == - QUICConnectionId(reinterpret_cast("\x08\x07\x06\x05\x04\x03\x02\x01"), 8))); - - header->store(buf, &len); - CHECK(len == header->size()); - CHECK(memcmp(buf, expected, 22) == 0); - CHECK(memcmp(buf + 22, expected + 22, 8) == 0); - } -} - -TEST_CASE("QUICPacketHeader - Short", "[quic]") -{ - const uint8_t raw_dcid[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // - 0x10, 0x11, // - }; - QUICConnectionId dcid(raw_dcid, sizeof(raw_dcid)); - - SECTION("Short Header (load)") - { - const uint8_t input[] = { - 0x43, // Short header with (K=0) - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) + 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Retry Token 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // - 0x10, 0x11, // - 0x01, 0x23, 0x45, 0x67, // Packet number - 0xff, 0xff, // Payload (dummy) + 0x10, 0x11, 0x12, 0x13, 0x14, 0xf0, 0xf1, 0xf2, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Retry Integrity Tag + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; - ats_unique_buf uinput = ats_unique_malloc(sizeof(input)); - memcpy(uinput.get(), input, sizeof(input)); - - QUICPacketHeaderUPtr header = QUICPacketHeader::load({}, {}, std::move(uinput), sizeof(input), 0); - CHECK(header->size() == 23); - CHECK(header->packet_size() == 25); - CHECK(header->key_phase() == QUICKeyPhase::PHASE_0); - CHECK(header->destination_cid() == dcid); - CHECK(header->packet_number() == 0x01234567); - CHECK(header->has_version() == false); - } - - SECTION("Short Header (store)") - { - uint8_t buf[32] = {0}; - size_t len = 0; - - const uint8_t expected[] = { - 0x43, // Short header with (K=0) - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // - 0x10, 0x11, // - 0x01, 0x23, 0x45, 0x67, // Packet number - 0x11, 0x22, 0x33, 0x44, 0x55, // Protected Payload - }; - size_t payload_len = 5; - size_t header_len = sizeof(expected) - 5; - - ats_unique_buf payload = ats_unique_malloc(payload_len); - memcpy(payload.get(), expected + header_len, payload_len); - - QUICPacketHeaderUPtr header = - QUICPacketHeader::build(QUICPacketType::PROTECTED, QUICKeyPhase::PHASE_0, dcid, 0x01234567, 0, std::move(payload), 32); - - CHECK(header->size() == 23); - CHECK(header->packet_size() == 0); - CHECK(header->key_phase() == QUICKeyPhase::PHASE_0); - CHECK(header->type() == QUICPacketType::PROTECTED); - CHECK(header->destination_cid() == dcid); - CHECK(header->packet_number() == 0x01234567); - CHECK(header->has_version() == false); - - header->store(buf, &len); - CHECK(len == header_len); - CHECK(memcmp(buf, expected, header_len) == 0); + QUICRetryToken token(expected + 23, 24); + + QUICRetryPacket packet(0x11223344, {reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8}, + {reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8}, token); + CHECK(packet.size() == sizeof(expected)); + CHECK(packet.type() == QUICPacketType::RETRY); + CHECK((packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04\x05\x06\x07\x08"), 8))); + CHECK((packet.source_cid() == QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14\x15\x16\x17\x18"), 8))); + CHECK(packet.version() == 0x11223344); + + packet.store(buf, &len); + CHECK(len == packet.size()); + CHECK(memcmp(buf, expected, sizeof(expected) - 16) == 0); } } @@ -354,3 +313,379 @@ TEST_CASE("Decoding Packet Number 1", "[quic]") QUICPacket::decode_packet_number(dst, src, len, base); CHECK(dst == 0xaa8309b3); } + +TEST_CASE("read_essential_info", "[quic]") +{ + SECTION("Long header packet - INITIAL") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + 0x08, // SCID Len + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID + 0x00, // Token Length (i), Token (*) + 0x06, // Length + 0x01, 0x23, 0x45, 0x67, // Packet number + 0xff, 0xff, // Payload (dummy) + }; + + QUICConnectionId expected_dcid(input + 6, 8); + QUICConnectionId expected_scid(input + 15, 8); + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(result); + CHECK(type == QUICPacketType::INITIAL); + CHECK(version == 0x11223344); + CHECK(dcid == expected_dcid); + CHECK(scid == expected_scid); + CHECK(packet_number == 0x01234567); + } + + SECTION("Long header packet - INITIAL - 0 length CID") + { + uint8_t input[] = { + 0xc2, // Long header, Type: INITIAL + 0xff, 0x00, 0x00, 0x19, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + 0x00, // SCID Len + 0x00, // Token Length (i), Token (*) + 0x42, 0x17, // Length + 0x00, 0x00, 0x00 // Packet number + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(result); + } + + SECTION("Long header packet - RETRY") + { + uint8_t input[] = { + 0xf0, // Long header, Type: RETRY + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + 0x08, // SCID Len + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID + 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Retry Token + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // + 0x10, 0x11, 0x12, 0x13, 0x14, 0xf0, 0xf1, 0xf2, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Retry Integrity Tag + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + QUICConnectionId expected_dcid(input + 6, 8); + QUICConnectionId expected_scid(input + 15, 8); + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(result); + CHECK(type == QUICPacketType::RETRY); + CHECK(version == 0x11223344); + CHECK(dcid == expected_dcid); + CHECK(scid == expected_scid); + } + + SECTION("Long header packet - Version Negotiation") + { + uint8_t input[] = { + 0xd9, // Long header, Type: RETRY + 0x00, 0x00, 0x00, 0x00, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + 0x08, // SCID Len + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID + 0xff, 0x00, 0x00, 0x19, // Supported Version 1 + 0xa1, 0xa2, 0xa3, 0xa4, // Supported Version 2 + }; + + QUICConnectionId expected_dcid(input + 6, 8); + QUICConnectionId expected_scid(input + 15, 8); + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(result); + CHECK(type == QUICPacketType::VERSION_NEGOTIATION); + CHECK(version == 0x00); + CHECK(dcid == expected_dcid); + CHECK(scid == expected_scid); + } + + SECTION("Short header packet") + { + uint8_t input[] = { + 0x43, // Short header with (K=0) + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Destination Connection ID (144) + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // + 0x10, 0x11, // + 0x01, 0x23, 0x45, 0x67, // Packet number + 0xff, 0xff, // Payload (dummy) + }; + + QUICConnectionId expected_dcid(input + 1, 18); + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(result); + CHECK(type == QUICPacketType::PROTECTED); + CHECK(key_phase == QUICKeyPhase::PHASE_0); + CHECK(dcid == expected_dcid); + CHECK(packet_number == 0x01234567); + } + + SECTION("Long header packet - Malformed INITIAL 1") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + + SECTION("Long header packet - Malformed INITIAL 2") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, // Version + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + + SECTION("Long header packet - Malformed INITIAL 3") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + + SECTION("Long header packet - Malformed INITIAL 4") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + + SECTION("Long header packet - Malformed INITIAL 5") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, // Destination Connection ID + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + + SECTION("Long header packet - Malformed INITIAL 6") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + + SECTION("Long header packet - Malformed INITIAL 7") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + 0x08, // SCID Len + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + SECTION("Long header packet - Malformed INITIAL 8") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + 0x08, // SCID Len + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID + 0x80, // Token Length (i), Token (*) + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } + + SECTION("Long header packet - Malformed INITIAL 9") + { + uint8_t input[] = { + 0xc3, // Long header, Type: INITIAL + 0x11, 0x22, 0x33, 0x44, // Version + 0x08, // DCID Len + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Destination Connection ID + 0x08, // SCID Len + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Source Connection ID + 0x00, // Token Length (i), Token (*) + 0x06, // Length + 0x01, 0x23, // Packet number + }; + + Ptr input_ibb = make_ptr(new_IOBufferBlock()); + input_ibb->set_internal(static_cast(input), sizeof(input), BUFFER_SIZE_NOT_ALLOCATED); + + QUICPacketType type; + QUICVersion version; + QUICConnectionId dcid; + QUICConnectionId scid; + QUICPacketNumber packet_number; + QUICKeyPhase key_phase; + bool result = QUICPacketR::read_essential_info(input_ibb, type, version, dcid, scid, packet_number, 0, key_phase); + + REQUIRE(!result); + } +} diff --git a/iocore/net/quic/test/test_QUICPacketFactory.cc b/iocore/net/quic/test/test_QUICPacketFactory.cc index dc017658803..39c2c43e29d 100644 --- a/iocore/net/quic/test/test_QUICPacketFactory.cc +++ b/iocore/net/quic/test/test_QUICPacketFactory.cc @@ -24,6 +24,7 @@ #include "catch.hpp" #include "quic/QUICPacket.h" +#include "quic/QUICPacketFactory.h" #include "quic/Mock.h" TEST_CASE("QUICPacketFactory_Create_VersionNegotiationPacket", "[quic]") @@ -36,15 +37,16 @@ TEST_CASE("QUICPacketFactory_Create_VersionNegotiationPacket", "[quic]") QUICConnectionId dcid(raw_dcid, 8); QUICConnectionId scid(raw_scid, 8); - QUICPacketUPtr vn_packet = factory.create_version_negotiation_packet(scid, dcid); + QUICPacketUPtr packet = factory.create_version_negotiation_packet(scid, dcid); + REQUIRE(packet != nullptr); - REQUIRE(vn_packet != nullptr); - CHECK(vn_packet->type() == QUICPacketType::VERSION_NEGOTIATION); - CHECK(vn_packet->destination_cid() == scid); - CHECK(vn_packet->source_cid() == dcid); - CHECK(vn_packet->version() == 0x00); + QUICVersionNegotiationPacket &vn_packet = static_cast(*packet); + CHECK(vn_packet.type() == QUICPacketType::VERSION_NEGOTIATION); + CHECK(vn_packet.destination_cid() == scid); + CHECK(vn_packet.source_cid() == dcid); + CHECK(vn_packet.version() == 0x00); - QUICVersion supported_version = QUICTypeUtil::read_QUICVersion(vn_packet->payload()); + QUICVersion supported_version = QUICTypeUtil::read_QUICVersion(reinterpret_cast(vn_packet.payload_block()->start())); CHECK(supported_version == QUIC_SUPPORTED_VERSIONS[0]); uint8_t expected[] = { @@ -54,12 +56,12 @@ TEST_CASE("QUICPacketFactory_Create_VersionNegotiationPacket", "[quic]") 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // Destination Connection ID 0x08, // SCID Len 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Source Connection ID - 0xff, 0x00, 0x00, 0x17, // Supported Version + 0xff, 0x00, 0x00, 0x1b, // Supported Version 0x1a, 0x2a, 0x3a, 0x4a, // Excercise Version }; uint8_t buf[1024] = {0}; size_t buf_len; - vn_packet->store(buf, &buf_len); + vn_packet.store(buf, &buf_len); CHECK((buf[0] & 0x80) == 0x80); // Lower 7 bits of the first byte is random CHECK(memcmp(buf + 1, expected + 1, buf_len - 1) == 0); } @@ -75,15 +77,15 @@ TEST_CASE("QUICPacketFactory_Create_Retry", "[quic]") QUICPacketUPtr packet = factory.create_retry_packet(QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4), - QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14"), 4), - QUICConnectionId(reinterpret_cast("\x04\x03\x02\x01"), 4), token); + QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14"), 4), token); REQUIRE(packet != nullptr); - CHECK(packet->type() == QUICPacketType::RETRY); - CHECK((packet->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4))); - CHECK(memcmp(packet->payload(), raw, sizeof(raw)) == 0); - CHECK(packet->packet_number() == 0); - CHECK(packet->version() == QUIC_SUPPORTED_VERSIONS[0]); + + QUICRetryPacket &retry_packet = static_cast(*packet); + CHECK(retry_packet.type() == QUICPacketType::RETRY); + CHECK(retry_packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4)); + CHECK(retry_packet.version() == QUIC_SUPPORTED_VERSIONS[0]); + CHECK(retry_packet.token() == token); } TEST_CASE("QUICPacketFactory_Create_Handshake", "[quic]") @@ -93,20 +95,24 @@ TEST_CASE("QUICPacketFactory_Create_Handshake", "[quic]") QUICPacketFactory factory(pp_key_info); factory.set_version(0x11223344); - uint8_t raw[] = {0xaa, 0xbb, 0xcc, 0xdd}; - ats_unique_buf payload = ats_unique_malloc(sizeof(raw)); - memcpy(payload.get(), raw, sizeof(raw)); + uint8_t raw[] = {0xaa, 0xbb, 0xcc, 0xdd}; + Ptr payload = make_ptr(new_IOBufferBlock()); + payload->alloc(iobuffer_size_to_index(sizeof(raw), BUFFER_SIZE_INDEX_32K)); + payload->fill(sizeof(raw)); + memcpy(payload->start(), raw, sizeof(raw)); - QUICPacketUPtr packet = - factory.create_handshake_packet(QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4), - QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14"), 4), 0, - std::move(payload), sizeof(raw), true, false, true); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet = factory.create_handshake_packet( + packet_buf, QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4), + QUICConnectionId(reinterpret_cast("\x11\x12\x13\x14"), 4), 0, payload, sizeof(raw), true, false, true); REQUIRE(packet != nullptr); - CHECK(packet->type() == QUICPacketType::HANDSHAKE); - CHECK((packet->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4))); - CHECK(memcmp(packet->payload(), raw, sizeof(raw)) != 0); - CHECK(packet->packet_number() <= 0xFFFFFBFF); - CHECK(packet->version() == 0x11223344); + + QUICHandshakePacket &handshake_packet = reinterpret_cast(*packet); + CHECK(handshake_packet.type() == QUICPacketType::HANDSHAKE); + CHECK(handshake_packet.destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4)); + CHECK(memcmp(handshake_packet.payload_block()->start(), raw, sizeof(raw)) != 0); + CHECK(handshake_packet.packet_number() <= 0xFFFFFBFF); + CHECK(handshake_packet.version() == 0x11223344); } TEST_CASE("QUICPacketFactory_Create_StatelessResetPacket", "[quic]") @@ -115,10 +121,11 @@ TEST_CASE("QUICPacketFactory_Create_StatelessResetPacket", "[quic]") QUICPacketFactory factory(pp_key_info); QUICStatelessResetToken token({reinterpret_cast("\x30\x39"), 2}, 67890); - QUICPacketUPtr packet = - factory.create_stateless_reset_packet(QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4), token); + QUICPacketUPtr packet = factory.create_stateless_reset_packet(token, 1200); REQUIRE(packet != nullptr); CHECK(packet->type() == QUICPacketType::STATELESS_RESET); - CHECK((packet->destination_cid() == QUICConnectionId(reinterpret_cast("\x01\x02\x03\x04"), 4))); + + QUICStatelessResetPacket *sr_packet = dynamic_cast(packet.get()); + CHECK(sr_packet->token() == token); } diff --git a/iocore/net/quic/test/test_QUICPacketHeaderProtector.cc b/iocore/net/quic/test/test_QUICPacketHeaderProtector.cc index 5cb69af9efa..031aa51cd37 100644 --- a/iocore/net/quic/test/test_QUICPacketHeaderProtector.cc +++ b/iocore/net/quic/test/test_QUICPacketHeaderProtector.cc @@ -32,9 +32,6 @@ struct PollCont; #include "P_UnixNet.h" #include "P_UnixNetVConnection.h" -// depends on size of cert -static constexpr uint32_t MAX_HANDSHAKE_MSG_LEN = 8192; - #include "./server_cert.h" TEST_CASE("QUICPacketHeaderProtector") @@ -127,6 +124,13 @@ TEST_CASE("QUICPacketHeaderProtector") QUICHandshakeProtocol *client = new QUICTLS(pp_key_info_client, client_ssl_ctx, NET_VCONNECTION_OUT, netvc_options); QUICHandshakeProtocol *server = new QUICTLS(pp_key_info_server, server_ssl_ctx, NET_VCONNECTION_IN, netvc_options); + auto client_tp = std::make_shared(); + auto server_tp = std::make_shared(); + client_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + server_tp->set(QUICTransportParameterId::MAX_IDLE_TIMEOUT, 10); + client->set_local_transport_parameters(client_tp); + server->set_local_transport_parameters(server_tp); + CHECK(client->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); CHECK(server->initialize_key_materials({reinterpret_cast("\x83\x94\xc8\xf0\x3e\x51\x57\x00"), 8})); @@ -135,41 +139,37 @@ TEST_CASE("QUICPacketHeaderProtector") // Handshake // CH - QUICHandshakeMsgs msg1; - uint8_t msg1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg1.buf = msg1_buf; - msg1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; + QUICHandshakeMsgs msg0; + msg0.offsets[0] = 0; + msg0.offsets[1] = 0; + msg0.offsets[2] = 0; + msg0.offsets[3] = 0; + msg0.offsets[4] = 0; - REQUIRE(client->handshake(&msg1, nullptr) == 1); + QUICHandshakeMsgs *msg1 = nullptr; + REQUIRE(client->handshake(&msg1, &msg0) == 1); + REQUIRE(msg1); // SH, EE, CERT, CV, FIN - QUICHandshakeMsgs msg2; - uint8_t msg2_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg2.buf = msg2_buf; - msg2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg2, &msg1) == 1); + QUICHandshakeMsgs *msg2 = nullptr; + REQUIRE(server->handshake(&msg2, msg1) == 1); + REQUIRE(msg2); // FIN - QUICHandshakeMsgs msg3; - uint8_t msg3_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg3.buf = msg3_buf; - msg3.max_buf_len = MAX_HANDSHAKE_MSG_LEN; + QUICHandshakeMsgs *msg3 = nullptr; -#ifdef SSL_MODE_QUIC_HACK - // -- Hacks for OpenSSL with SSL_MODE_QUIC_HACK -- // SH QUICHandshakeMsgs msg2_1; uint8_t msg2_1_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; msg2_1.buf = msg2_1_buf; msg2_1.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - memcpy(msg2_1.buf, msg2.buf, msg2.offsets[1]); + memcpy(msg2_1.buf, msg2->buf, msg2->offsets[1]); msg2_1.offsets[0] = 0; - msg2_1.offsets[1] = msg2.offsets[1]; - msg2_1.offsets[2] = msg2.offsets[1]; - msg2_1.offsets[3] = msg2.offsets[1]; - msg2_1.offsets[4] = msg2.offsets[1]; + msg2_1.offsets[1] = msg2->offsets[1]; + msg2_1.offsets[2] = msg2->offsets[1]; + msg2_1.offsets[3] = msg2->offsets[1]; + msg2_1.offsets[4] = msg2->offsets[1]; // EE - FIN QUICHandshakeMsgs msg2_2; @@ -177,8 +177,8 @@ TEST_CASE("QUICPacketHeaderProtector") msg2_2.buf = msg2_2_buf; msg2_2.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - size_t len = msg2.offsets[3] - msg2.offsets[2]; - memcpy(msg2_2.buf, msg2.buf + msg2.offsets[1], len); + size_t len = msg2->offsets[3] - msg2->offsets[2]; + memcpy(msg2_2.buf, msg2->buf + msg2->offsets[1], len); msg2_2.offsets[0] = 0; msg2_2.offsets[1] = 0; msg2_2.offsets[2] = 0; @@ -187,23 +187,16 @@ TEST_CASE("QUICPacketHeaderProtector") REQUIRE(client->handshake(&msg3, &msg2_1) == 1); REQUIRE(client->handshake(&msg3, &msg2_2) == 1); -#else - REQUIRE(client->handshake(&msg3, &msg2) == 1); -#endif + REQUIRE(msg3); // NS - QUICHandshakeMsgs msg4; - uint8_t msg4_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg4.buf = msg4_buf; - msg4.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - - REQUIRE(server->handshake(&msg4, &msg3) == 1); - - QUICHandshakeMsgs msg5; - uint8_t msg5_buf[MAX_HANDSHAKE_MSG_LEN] = {0}; - msg5.buf = msg5_buf; - msg5.max_buf_len = MAX_HANDSHAKE_MSG_LEN; - REQUIRE(client->handshake(&msg5, &msg4) == 1); + QUICHandshakeMsgs *msg4 = nullptr; + REQUIRE(server->handshake(&msg4, msg3) == 1); + REQUIRE(msg4); + + QUICHandshakeMsgs *msg5 = nullptr; + REQUIRE(client->handshake(&msg5, msg4) == 1); + REQUIRE(msg5 == nullptr); // ## Client -> Server REQUIRE(client_ph_protector.protect(tmp, sizeof(tmp), 18)); diff --git a/iocore/net/quic/test/test_QUICPathValidator.cc b/iocore/net/quic/test/test_QUICPathValidator.cc index 78c51f2f355..cd96ef74a2b 100644 --- a/iocore/net/quic/test/test_QUICPathValidator.cc +++ b/iocore/net/quic/test/test_QUICPathValidator.cc @@ -95,7 +95,7 @@ TEST_CASE("QUICPathValidator", "[quic]") memcpy(buf + len, b->start(), b->size()); len += b->size(); } - MockQUICPacket mock_packet; + MockQUICPacketR mock_packet; auto received_frame = QUICFrameFactory::create(received_frame_buf, buf, len, &mock_packet); mock_packet.set_from(remote); mock_packet.set_to(local); diff --git a/iocore/net/quic/test/test_QUICPinger.cc b/iocore/net/quic/test/test_QUICPinger.cc index fce04c62ce1..bb257026c78 100644 --- a/iocore/net/quic/test/test_QUICPinger.cc +++ b/iocore/net/quic/test/test_QUICPinger.cc @@ -33,69 +33,69 @@ TEST_CASE("QUICPinger", "[quic]") SECTION("request and cancel") { QUICPinger pinger; - pinger.request(); - REQUIRE(pinger.count() == 1); - pinger.request(); - REQUIRE(pinger.count() == 2); - pinger.cancel(); - REQUIRE(pinger.count() == 1); + pinger.request(level); + REQUIRE(pinger.count(level) == 1); + pinger.request(level); + REQUIRE(pinger.count(level) == 2); + pinger.cancel(level); + REQUIRE(pinger.count(level) == 1); REQUIRE(pinger.generate_frame(frame, level, UINT64_MAX, UINT16_MAX, 0, 0) != nullptr); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); } SECTION("generate PING Frame twice") { QUICPinger pinger; - pinger.request(); - REQUIRE(pinger.count() == 1); - pinger.request(); - REQUIRE(pinger.count() == 2); + pinger.request(level); + REQUIRE(pinger.count(level) == 1); + pinger.request(level); + REQUIRE(pinger.count(level) == 2); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, false, 0) == true); - REQUIRE(pinger.count() == 2); + REQUIRE(pinger.count(level) == 2); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, false, 0) == false); - REQUIRE(pinger.count() == 2); + REQUIRE(pinger.count(level) == 2); } SECTION("don't generate frame when packet is ack_elicting") { QUICPinger pinger; - pinger.request(); - REQUIRE(pinger.count() == 1); - pinger.request(); - REQUIRE(pinger.count() == 2); + pinger.request(level); + REQUIRE(pinger.count(level) == 1); + pinger.request(level); + REQUIRE(pinger.count(level) == 2); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, true, 0) == false); - REQUIRE(pinger.count() == 1); + REQUIRE(pinger.count(level) == 1); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, true, 1) == false); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); } SECTION("generating PING Frame for next continuos un-ack-eliciting packets") { QUICPinger pinger; REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, false, 0) == true); - REQUIRE(pinger.count() == 1); + REQUIRE(pinger.count(level) == 1); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, true, 1) == false); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, false, 2) == false); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, false, 3) == true); - REQUIRE(pinger.count() == 1); + REQUIRE(pinger.count(level) == 1); } SECTION("don't send PING Frame for empty packet") { QUICPinger pinger; REQUIRE(pinger.will_generate_frame(level, 0, false, 0) == false); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, false, 1) == true); - REQUIRE(pinger.count() == 1); + REQUIRE(pinger.count(level) == 1); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, true, 2) == false); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); REQUIRE(pinger.will_generate_frame(level, UINT64_MAX, false, 3) == false); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); REQUIRE(pinger.will_generate_frame(level, 0, false, 4) == false); - REQUIRE(pinger.count() == 0); + REQUIRE(pinger.count(level) == 0); REQUIRE(pinger.will_generate_frame(level, 1, false, 5) == true); - REQUIRE(pinger.count() == 1); + REQUIRE(pinger.count(level) == 1); } } diff --git a/iocore/net/quic/test/test_QUICStream.cc b/iocore/net/quic/test/test_QUICStream.cc index 1ed23562515..5527fdd691c 100644 --- a/iocore/net/quic/test/test_QUICStream.cc +++ b/iocore/net/quic/test/test_QUICStream.cc @@ -33,7 +33,7 @@ TEST_CASE("QUICBidiStream", "[quic]") uint8_t payload[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}; uint32_t stream_id = 0x03; Ptr block = make_ptr(new_IOBufferBlock()); - block->alloc(); + block->alloc(BUFFER_SIZE_INDEX_32K); memcpy(block->start(), payload, sizeof(payload)); block->fill(sizeof(payload)); @@ -178,7 +178,7 @@ TEST_CASE("QUICBidiStream", "[quic]") stream->do_io_read(nullptr, INT64_MAX, read_buffer); Ptr block = make_ptr(new_IOBufferBlock()); - block->alloc(); + block->alloc(BUFFER_SIZE_INDEX_32K); block->fill(1024); // Start with 1024 but not 0 so received frames won't be processed @@ -421,7 +421,7 @@ TEST_CASE("QUIC receive only stream", "[quic]") uint8_t payload[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}; uint32_t stream_id = 0x03; Ptr block = make_ptr(new_IOBufferBlock()); - block->alloc(); + block->alloc(BUFFER_SIZE_INDEX_32K); memcpy(block->start(), payload, sizeof(payload)); block->fill(sizeof(payload)); @@ -562,7 +562,7 @@ TEST_CASE("QUIC receive only stream", "[quic]") stream->do_io_read(nullptr, INT64_MAX, read_buffer); Ptr block = make_ptr(new_IOBufferBlock()); - block->alloc(); + block->alloc(BUFFER_SIZE_INDEX_32K); block->fill(1024); // Start with 1024 but not 0 so received frames won't be processed @@ -619,7 +619,7 @@ TEST_CASE("QUIC send only stream", "[quic]") uint8_t payload[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}; uint32_t stream_id = 0x03; Ptr block = make_ptr(new_IOBufferBlock()); - block->alloc(); + block->alloc(BUFFER_SIZE_INDEX_32K); memcpy(block->start(), payload, sizeof(payload)); block->fill(sizeof(payload)); @@ -672,7 +672,6 @@ TEST_CASE("QUIC send only stream", "[quic]") MIOBuffer *write_buffer = new_MIOBuffer(BUFFER_SIZE_INDEX_4K); IOBufferReader *write_buffer_reader = write_buffer->alloc_reader(); - QUICRTTMeasure rtt_provider; MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream(new QUICSendStream(&cinfo_provider, stream_id, 4096)); SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); @@ -766,7 +765,6 @@ TEST_CASE("QUIC send only stream", "[quic]") MIOBuffer *write_buffer = new_MIOBuffer(BUFFER_SIZE_INDEX_8K); IOBufferReader *write_buffer_reader = write_buffer->alloc_reader(); - QUICRTTMeasure rtt_provider; MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream(new QUICSendStream(&cinfo_provider, stream_id, UINT64_MAX)); SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); @@ -814,7 +812,6 @@ TEST_CASE("QUIC send only stream", "[quic]") MIOBuffer *write_buffer = new_MIOBuffer(BUFFER_SIZE_INDEX_8K); IOBufferReader *write_buffer_reader = write_buffer->alloc_reader(); - QUICRTTMeasure rtt_provider; MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream(new QUICSendStream(&cinfo_provider, stream_id, UINT64_MAX)); SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); diff --git a/iocore/net/quic/test/test_QUICStreamManager.cc b/iocore/net/quic/test/test_QUICStreamManager.cc index b875c79aa98..8d7ddf28bfc 100644 --- a/iocore/net/quic/test/test_QUICStreamManager.cc +++ b/iocore/net/quic/test/test_QUICStreamManager.cc @@ -29,6 +29,8 @@ #include "quic/QUICFrame.h" #include "quic/Mock.h" +MockQUICContext context; + TEST_CASE("QUICStreamManager_NewStream", "[quic]") { QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; @@ -37,23 +39,20 @@ TEST_CASE("QUICStreamManager_NewStream", "[quic]") MockQUICApplication mock_app(&connection); app_map.set_default(&mock_app); MockQUICConnectionInfoProvider cinfo_provider; - QUICRTTMeasure rtt_provider; - QUICStreamManager sm(&cinfo_provider, &rtt_provider, &app_map); + QUICStreamManager sm(&context, &app_map); uint8_t local_tp_buf[] = { - 0x00, 0x06, // size of parameters - 0x00, 0x08, // parameter id - initial_max_streams_bidi - 0x00, 0x02, // length of value - 0x40, 0x10 // value + 0x08, // parameter id - initial_max_streams_bidi + 0x02, // length of value + 0x40, 0x10 // value }; std::shared_ptr local_tp = std::make_shared(local_tp_buf, sizeof(local_tp_buf)); uint8_t remote_tp_buf[] = { - 0x00, 0x06, // size of parameters - 0x00, 0x08, // parameter id - initial_max_streams_bidi - 0x00, 0x02, // length of value - 0x40, 0x10 // value + 0x08, // parameter id - initial_max_streams_bidi + 0x02, // length of value + 0x40, 0x10 // value }; std::shared_ptr remote_tp = std::make_shared(remote_tp_buf, sizeof(remote_tp_buf)); @@ -94,13 +93,6 @@ TEST_CASE("QUICStreamManager_NewStream", "[quic]") QUICFrame *stream_blocked_frame = QUICFrameFactory::create_stream_data_blocked_frame(stream_blocked_frame_buf, 0x10, 0); sm.handle_frame(level, *stream_blocked_frame); CHECK(sm.stream_count() == 5); - - // Set local maximum stream id - sm.set_max_streams_bidi(5); - uint8_t stream_blocked_frame_x_buf[QUICFrame::MAX_INSTANCE_SIZE]; - QUICFrame *stream_blocked_frame_x = QUICFrameFactory::create_stream_data_blocked_frame(stream_blocked_frame_x_buf, 0x18, 0); - sm.handle_frame(level, *stream_blocked_frame_x); - CHECK(sm.stream_count() == 5); } TEST_CASE("QUICStreamManager_first_initial_map", "[quic]") @@ -111,8 +103,7 @@ TEST_CASE("QUICStreamManager_first_initial_map", "[quic]") MockQUICApplication mock_app(&connection); app_map.set_default(&mock_app); MockQUICConnectionInfoProvider cinfo_provider; - QUICRTTMeasure rtt_provider; - QUICStreamManager sm(&cinfo_provider, &rtt_provider, &app_map); + QUICStreamManager sm(&context, &app_map); std::shared_ptr local_tp = std::make_shared(); std::shared_ptr remote_tp = std::make_shared(); sm.init_flow_control_params(local_tp, remote_tp); @@ -137,28 +128,25 @@ TEST_CASE("QUICStreamManager_total_offset_received", "[quic]") MockQUICConnection connection; MockQUICApplication mock_app(&connection); app_map.set_default(&mock_app); - QUICRTTMeasure rtt_provider; - QUICStreamManager sm(new MockQUICConnectionInfoProvider(), &rtt_provider, &app_map); + QUICStreamManager sm(&context, &app_map); uint8_t local_tp_buf[] = { - 0x00, 0x0e, // size of parameters - 0x00, 0x08, // parameter id - initial_max_streams_bidi - 0x00, 0x02, // length of value + 0x08, // parameter id - initial_max_streams_bidi + 0x02, // length of value 0x40, 0x10, // value - 0x00, 0x05, // parameter id - initial_max_stream_data_bidi_local - 0x00, 0x04, // length of value + 0x05, // parameter id - initial_max_stream_data_bidi_local + 0x04, // length of value 0xbf, 0xff, 0xff, 0xff // value }; std::shared_ptr local_tp = std::make_shared(local_tp_buf, sizeof(local_tp_buf)); uint8_t remote_tp_buf[] = { - 0x00, 0x0e, // size of parameters - 0x00, 0x08, // parameter id - initial_max_streams_bidi - 0x00, 0x02, // length of value + 0x08, // parameter id - initial_max_streams_bidi + 0x02, // length of value 0x40, 0x10, // value - 0x00, 0x06, // parameter id - initial_max_stream_data_bidi_remote - 0x00, 0x04, // length of value + 0x06, // parameter id - initial_max_stream_data_bidi_remote + 0x04, // length of value 0xbf, 0xff, 0xff, 0xff // value }; std::shared_ptr remote_tp = @@ -195,28 +183,25 @@ TEST_CASE("QUICStreamManager_total_offset_sent", "[quic]") MockQUICConnection connection; MockQUICApplication mock_app(&connection); app_map.set_default(&mock_app); - QUICRTTMeasure rtt_provider; - QUICStreamManager sm(new MockQUICConnectionInfoProvider(), &rtt_provider, &app_map); + QUICStreamManager sm(&context, &app_map); uint8_t local_tp_buf[] = { - 0x00, 0x0e, // size of parameters - 0x00, 0x08, // parameter id - initial_max_streams_bidi - 0x00, 0x02, // length of value + 0x08, // parameter id - initial_max_streams_bidi + 0x02, // length of value 0x40, 0x10, // value - 0x00, 0x05, // parameter id - initial_max_stream_data_bidi_local - 0x00, 0x04, // length of value + 0x05, // parameter id - initial_max_stream_data_bidi_local + 0x04, // length of value 0xbf, 0xff, 0xff, 0xff // value }; std::shared_ptr local_tp = std::make_shared(local_tp_buf, sizeof(local_tp_buf)); uint8_t remote_tp_buf[] = { - 0x00, 0x0e, // size of parameters - 0x00, 0x08, // parameter id - initial_max_streams_bidi - 0x00, 0x02, // length of value + 0x08, // parameter id - initial_max_streams_bidi + 0x02, // length of value 0x40, 0x10, // value - 0x00, 0x06, // parameter id - initial_max_stream_data_bidi_remote - 0x00, 0x04, // length of value + 0x06, // parameter id - initial_max_stream_data_bidi_remote + 0x04, // length of value 0xbf, 0xff, 0xff, 0xff // value }; std::shared_ptr remote_tp = @@ -258,3 +243,124 @@ TEST_CASE("QUICStreamManager_total_offset_sent", "[quic]") // Wait for event processing sleep(2); } + +TEST_CASE("QUICStreamManager_max_streams", "[quic]") +{ + QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; + QUICApplicationMap app_map; + MockQUICConnection connection; + MockQUICApplication mock_app(&connection); + app_map.set_default(&mock_app); + QUICStreamManager sm(&context, &app_map); + + uint8_t local_tp_buf[] = { + 0x08, // parameter id - initial_max_streams_bidi + 0x01, // length of value + 0x03, // value + 0x09, // parameter id - initial_max_streams_uni + 0x01, // length of value + 0x03, // value + }; + std::shared_ptr local_tp = + std::make_shared(local_tp_buf, sizeof(local_tp_buf)); + + uint8_t remote_tp_buf[] = { + 0x08, // parameter id - initial_max_streams_bidi + 0x01, // length of value + 0x03, // value + 0x09, // parameter id - initial_max_streams_uni + 0x01, // length of value + 0x03, // value + }; + std::shared_ptr remote_tp = + std::make_shared(remote_tp_buf, sizeof(remote_tp_buf)); + + sm.init_flow_control_params(local_tp, remote_tp); + + SECTION("local") + { + // RESET_STREAM frames create new streams + + // Bidirectional + uint8_t rst_stream_frame_buf[QUICFrame::MAX_INSTANCE_SIZE]; + QUICFrame *rst_stream_frame = + QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 1, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 1); + + rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 5, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 2); + + rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 9, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 3); + + rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 13, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 3); + + // Unidirectional + rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 3, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 4); + + rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 7, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 5); + + rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 11, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 6); + + rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(rst_stream_frame_buf, 15, static_cast(0x01), 0); + sm.handle_frame(level, *rst_stream_frame); + REQUIRE(sm.stream_count() == 6); + } + + SECTION("remote") + { + // Bidirection + QUICConnectionErrorUPtr error; + QUICStreamId id; + + error = sm.create_bidi_stream(id); + REQUIRE(!error); + REQUIRE(id == 0); + REQUIRE(sm.stream_count() == 1); + + error = sm.create_bidi_stream(id); + REQUIRE(!error); + REQUIRE(id == 4); + REQUIRE(sm.stream_count() == 2); + + error = sm.create_bidi_stream(id); + REQUIRE(!error); + REQUIRE(id == 8); + REQUIRE(sm.stream_count() == 3); + + error = sm.create_bidi_stream(id); + REQUIRE(error); + REQUIRE(sm.stream_count() == 3); + + // Unidirection + error = sm.create_uni_stream(id); + REQUIRE(!error); + REQUIRE(id == 2); + REQUIRE(sm.stream_count() == 4); + + error = sm.create_uni_stream(id); + REQUIRE(!error); + REQUIRE(id == 6); + REQUIRE(sm.stream_count() == 5); + + error = sm.create_uni_stream(id); + REQUIRE(!error); + REQUIRE(id == 10); + REQUIRE(sm.stream_count() == 6); + + error = sm.create_uni_stream(id); + REQUIRE(error); + REQUIRE(sm.stream_count() == 6); + } +} diff --git a/iocore/net/quic/test/test_QUICStreamState.cc b/iocore/net/quic/test/test_QUICStreamState.cc index b5538669d16..9eb11281475 100644 --- a/iocore/net/quic/test/test_QUICStreamState.cc +++ b/iocore/net/quic/test/test_QUICStreamState.cc @@ -33,7 +33,7 @@ TEST_CASE("QUICSendStreamState", "[quic]") { Ptr block_4 = make_ptr(new_IOBufferBlock()); - block_4->alloc(); + block_4->alloc(BUFFER_SIZE_INDEX_32K); block_4->fill(4); CHECK(block_4->read_avail() == 4); @@ -183,7 +183,7 @@ TEST_CASE("QUICSendStreamState", "[quic]") TEST_CASE("QUICReceiveStreamState", "[quic]") { Ptr block_4 = make_ptr(new_IOBufferBlock()); - block_4->alloc(); + block_4->alloc(BUFFER_SIZE_INDEX_32K); block_4->fill(4); CHECK(block_4->read_avail() == 4); @@ -366,7 +366,7 @@ TEST_CASE("QUICReceiveStreamState", "[quic]") TEST_CASE("QUICBidiState", "[quic]") { Ptr block_4 = make_ptr(new_IOBufferBlock()); - block_4->alloc(); + block_4->alloc(BUFFER_SIZE_INDEX_32K); block_4->fill(4); CHECK(block_4->read_avail() == 4); diff --git a/iocore/net/quic/test/test_QUICTransportParameters.cc b/iocore/net/quic/test/test_QUICTransportParameters.cc index 95fb0ae27bd..a225b86fed7 100644 --- a/iocore/net/quic/test/test_QUICTransportParameters.cc +++ b/iocore/net/quic/test/test_QUICTransportParameters.cc @@ -30,18 +30,17 @@ TEST_CASE("QUICTransportParametersInClientHello_read", "[quic]") SECTION("OK") { uint8_t buf[] = { - 0x00, 0x1c, // size of parameters - 0x00, 0x00, // parameter id - 0x00, 0x04, // length of value + 0x00, // parameter id + 0x04, // length of value 0x11, 0x22, 0x33, 0x44, // value - 0x00, 0x01, // parameter id - 0x00, 0x04, // length of value + 0x01, // parameter id + 0x04, // length of value 0x12, 0x34, 0x56, 0x78, // value - 0x00, 0x05, // parameter id - 0x00, 0x02, // length of value + 0x05, // parameter id + 0x02, // length of value 0x0a, 0x0b, // value - 0x00, 0x03, // parameter id - 0x00, 0x02, // length of value + 0x03, // parameter id + 0x02, // length of value 0x05, 0x67, // value }; @@ -55,7 +54,7 @@ TEST_CASE("QUICTransportParametersInClientHello_read", "[quic]") CHECK(len == 4); CHECK(memcmp(data, "\x11\x22\x33\x44", 4) == 0); - data = params_in_ch.getAsBytes(QUICTransportParameterId::IDLE_TIMEOUT, len); + data = params_in_ch.getAsBytes(QUICTransportParameterId::MAX_IDLE_TIMEOUT, len); CHECK(len == 4); CHECK(memcmp(data, "\x12\x34\x56\x78", 4) == 0); @@ -75,12 +74,11 @@ TEST_CASE("QUICTransportParametersInClientHello_read", "[quic]") SECTION("Duplicate parameters") { uint8_t buf[] = { - 0x00, 0x10, // size of parameters - 0x00, 0x00, // parameter id - 0x00, 0x04, // length of value + 0x00, // parameter id + 0x04, // length of value 0x11, 0x22, 0x33, 0x44, // value - 0x00, 0x00, // parameter id - 0x00, 0x04, // length of value + 0x00, // parameter id + 0x04, // length of value 0x12, 0x34, 0x56, 0x78, // value }; @@ -95,16 +93,15 @@ TEST_CASE("QUICTransportParametersInClientHello_write", "[quic]") uint16_t len; uint8_t expected[] = { - 0x00, 0x22, // size of parameters - 0x00, 0x02, // parameter id - 0x00, 0x10, // length of value + 0x02, // parameter id + 0x10, // length of value 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // value 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // value - 0x00, 0x03, // parameter id - 0x00, 0x02, // length of value + 0x03, // parameter id + 0x02, // length of value 0x5b, 0xcd, // value - 0x00, 0x05, // parameter id - 0x00, 0x04, // length of value + 0x05, // parameter id + 0x04, // length of value 0x91, 0x22, 0x33, 0x44, // value }; @@ -121,7 +118,7 @@ TEST_CASE("QUICTransportParametersInClientHello_write", "[quic]") params_in_ch.set(QUICTransportParameterId::STATELESS_RESET_TOKEN, stateless_reset_token, 16); params_in_ch.store(buf, &len); - CHECK(len == 36); + CHECK(len == 28); CHECK(memcmp(buf, expected, len) == 0); } @@ -130,19 +127,19 @@ TEST_CASE("QUICTransportParametersInEncryptedExtensions_read", "[quic]") SECTION("OK case") { uint8_t buf[] = { - 0x00, 0x2a, // size of parameters - 0x00, 0x01, // parameter id - 0x00, 0x02, // length of value - 0x51, 0x23, // value - 0x00, 0x02, // parameter id - 0x00, 0x10, // length of value - 0x00, 0x10, 0x20, 0x30, // value - 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0x00, 0x04, // parameter id - 0x00, 0x04, // length of value - 0x92, 0x34, 0x56, 0x78, // value - 0x00, 0x06, // parameter id - 0x00, 0x04, // length of value - 0x91, 0x22, 0x33, 0x44, // value + 0x01, // parameter id + 0x02, // length of value + 0x51, 0x23, // value + 0x02, // parameter id + 0x10, // length of value + 0x00, 0x10, 0x20, 0x30, // value + 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, + 0x04, // parameter id + 0x04, // length of value + 0x92, 0x34, 0x56, 0x78, // value + 0x06, // parameter id + 0x04, // length of value + 0x91, 0x22, 0x33, 0x44, // value }; QUICTransportParametersInEncryptedExtensions params_in_ee(buf, sizeof(buf)); @@ -159,13 +156,13 @@ TEST_CASE("QUICTransportParametersInEncryptedExtensions_read", "[quic]") CHECK(len == 4); CHECK(memcmp(data, "\x92\x34\x56\x78", 4) == 0); - data = params_in_ee.getAsBytes(QUICTransportParameterId::IDLE_TIMEOUT, len); + data = params_in_ee.getAsBytes(QUICTransportParameterId::MAX_IDLE_TIMEOUT, len); CHECK(len == 2); CHECK(memcmp(data, "\x51\x23", 2) == 0); data = params_in_ee.getAsBytes(QUICTransportParameterId::STATELESS_RESET_TOKEN, len); CHECK(len == 16); - CHECK(memcmp(data, buf + 12, 16) == 0); + CHECK(memcmp(data, buf + 6, 16) == 0); CHECK(!params_in_ee.contains(QUICTransportParameterId::DISABLE_ACTIVE_MIGRATION)); } @@ -173,18 +170,17 @@ TEST_CASE("QUICTransportParametersInEncryptedExtensions_read", "[quic]") SECTION("OK case - zero length value") { uint8_t buf[] = { - 0x00, 0x1a, // size of parameters - 0x00, 0x01, // parameter id - 0x00, 0x02, // length of value + 0x01, // parameter id + 0x02, // length of value 0x51, 0x23, // value - 0x00, 0x04, // parameter id - 0x00, 0x04, // length of value + 0x04, // parameter id + 0x04, // length of value 0xa2, 0x34, 0x56, 0x78, // value - 0x00, 0x06, // parameter id - 0x00, 0x04, // length of value + 0x06, // parameter id + 0x04, // length of value 0xa1, 0x22, 0x33, 0x44, // value - 0x00, 0x0c, // parameter id - 0x00, 0x00, // length of value + 0x0c, // parameter id + 0x00, // length of value }; QUICTransportParametersInEncryptedExtensions params_in_ee(buf, sizeof(buf)); @@ -201,7 +197,7 @@ TEST_CASE("QUICTransportParametersInEncryptedExtensions_read", "[quic]") CHECK(len == 4); CHECK(memcmp(data, "\xa2\x34\x56\x78", 4) == 0); - data = params_in_ee.getAsBytes(QUICTransportParameterId::IDLE_TIMEOUT, len); + data = params_in_ee.getAsBytes(QUICTransportParameterId::MAX_IDLE_TIMEOUT, len); CHECK(len == 2); CHECK(memcmp(data, "\x51\x23", 2) == 0); @@ -211,12 +207,11 @@ TEST_CASE("QUICTransportParametersInEncryptedExtensions_read", "[quic]") SECTION("Duplicate parameters") { uint8_t buf[] = { - 0x00, 0x1e, // size of parameters - 0x00, 0x00, // parameter id - 0x00, 0x04, // length of value + 0x00, // parameter id + 0x04, // length of value 0x01, 0x02, 0x03, 0x04, // value - 0x00, 0x00, // parameter id - 0x00, 0x04, // length of value + 0x00, // parameter id + 0x04, // length of value 0x12, 0x34, 0x56, 0x78, // value }; @@ -233,12 +228,11 @@ TEST_CASE("QUICTransportParametersEncryptedExtensions_write", "[quic]") uint16_t len; uint8_t expected[] = { - 0x00, 0x0e, // size of parameters - 0x00, 0x03, // parameter id - 0x00, 0x02, // length of value + 0x03, // parameter id + 0x02, // length of value 0x5b, 0xcd, // value - 0x00, 0x06, // parameter id - 0x00, 0x04, // length of value + 0x06, // parameter id + 0x04, // length of value 0x91, 0x22, 0x33, 0x44, // value }; @@ -253,7 +247,7 @@ TEST_CASE("QUICTransportParametersEncryptedExtensions_write", "[quic]") params_in_ee.add_version(0x01020304); params_in_ee.add_version(0x05060708); params_in_ee.store(buf, &len); - CHECK(len == 16); + CHECK(len == 10); CHECK(memcmp(buf, expected, len) == 0); } @@ -263,15 +257,14 @@ TEST_CASE("QUICTransportParametersEncryptedExtensions_write", "[quic]") uint16_t len; uint8_t expected[] = { - 0x00, 0x12, // size of parameters - 0x00, 0x03, // parameter id - 0x00, 0x02, // length of value + 0x03, // parameter id + 0x02, // length of value 0x5b, 0xcd, // value - 0x00, 0x06, // parameter id - 0x00, 0x04, // length of value + 0x06, // parameter id + 0x04, // length of value 0x91, 0x22, 0x33, 0x44, // value - 0x00, 0x0c, // parameter id - 0x00, 0x00, // length of value + 0x0c, // parameter id + 0x00, // length of value }; QUICTransportParametersInEncryptedExtensions params_in_ee; @@ -286,7 +279,7 @@ TEST_CASE("QUICTransportParametersEncryptedExtensions_write", "[quic]") params_in_ee.add_version(0x01020304); params_in_ee.add_version(0x05060708); params_in_ee.store(buf, &len); - CHECK(len == 20); + CHECK(len == 12); CHECK(memcmp(buf, expected, len) == 0); } } diff --git a/iocore/net/quic/test/test_QUICVersionNegotiator.cc b/iocore/net/quic/test/test_QUICVersionNegotiator.cc index be76949a89d..a1bcab3f3b0 100644 --- a/iocore/net/quic/test/test_QUICVersionNegotiator.cc +++ b/iocore/net/quic/test/test_QUICVersionNegotiator.cc @@ -25,6 +25,7 @@ #include "quic/QUICVersionNegotiator.h" #include "quic/QUICPacketProtectionKeyInfo.h" +#include "quic/QUICPacketFactory.h" #include "quic/Mock.h" TEST_CASE("QUICVersionNegotiator - Server Side", "[quic]") @@ -34,8 +35,11 @@ TEST_CASE("QUICVersionNegotiator - Server Side", "[quic]") QUICPacketFactory packet_factory(pp_key_info); QUICVersionNegotiator vn; - ats_unique_buf dummy_payload = ats_unique_malloc(128); - size_t dummy_payload_len = 128; + + size_t dummy_payload_len = 128; + Ptr dummy_payload = make_ptr(new_IOBufferBlock()); + dummy_payload->alloc(iobuffer_size_to_index(dummy_payload_len, BUFFER_SIZE_INDEX_32K)); + dummy_payload->fill(dummy_payload_len); SECTION("Normal case") { @@ -44,11 +48,15 @@ TEST_CASE("QUICVersionNegotiator - Server Side", "[quic]") // Negotiate version packet_factory.set_version(QUIC_SUPPORTED_VERSIONS[0]); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; QUICPacketUPtr initial_packet = - packet_factory.create_initial_packet({}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); - + packet_factory.create_initial_packet(packet_buf, {}, {}, 0, dummy_payload, dummy_payload_len, true, false, true); REQUIRE(initial_packet != nullptr); - vn.negotiate(*initial_packet); + + auto blocks = initial_packet->header_block(); + blocks->next = initial_packet->payload_block(); + QUICInitialPacketR received_initial_packet(nullptr, {}, {}, blocks, 0); + vn.negotiate(received_initial_packet); CHECK(vn.status() == QUICVersionNegotiationStatus::NEGOTIATED); } @@ -59,11 +67,15 @@ TEST_CASE("QUICVersionNegotiator - Server Side", "[quic]") // Negotiate version packet_factory.set_version(QUIC_SUPPORTED_VERSIONS[0]); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; QUICPacketUPtr initial_packet = - packet_factory.create_initial_packet({}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); - + packet_factory.create_initial_packet(packet_buf, {}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); REQUIRE(initial_packet != nullptr); - vn.negotiate(*initial_packet); + + auto blocks = initial_packet->header_block(); + blocks->next = initial_packet->payload_block(); + QUICInitialPacketR received_initial_packet(nullptr, {}, {}, blocks, 0); + vn.negotiate(received_initial_packet); CHECK(vn.status() == QUICVersionNegotiationStatus::NEGOTIATED); } @@ -74,11 +86,15 @@ TEST_CASE("QUICVersionNegotiator - Server Side", "[quic]") // Negotiate version packet_factory.set_version(QUIC_EXERCISE_VERSION); + uint8_t packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; QUICPacketUPtr initial_packet = - packet_factory.create_initial_packet({}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); - + packet_factory.create_initial_packet(packet_buf, {}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); REQUIRE(initial_packet != nullptr); - vn.negotiate(*initial_packet); + + auto blocks = initial_packet->header_block(); + blocks->next = initial_packet->payload_block(); + QUICInitialPacketR received_initial_packet(nullptr, {}, {}, blocks, 0); + vn.negotiate(received_initial_packet); CHECK(vn.status() == QUICVersionNegotiationStatus::NOT_NEGOTIATED); } } @@ -90,8 +106,10 @@ TEST_CASE("QUICVersionNegotiator - Client Side", "[quic]") QUICPacketFactory packet_factory(pp_key_info); QUICVersionNegotiator vn; - ats_unique_buf dummy_payload = ats_unique_malloc(128); - size_t dummy_payload_len = 128; + size_t dummy_payload_len = 128; + Ptr dummy_payload = make_ptr(new_IOBufferBlock()); + dummy_payload->alloc(iobuffer_size_to_index(dummy_payload_len, BUFFER_SIZE_INDEX_32K)); + dummy_payload->fill(dummy_payload_len); SECTION("Normal case") { @@ -108,17 +126,22 @@ TEST_CASE("QUICVersionNegotiator - Client Side", "[quic]") // Negotiate version packet_factory.set_version(QUIC_EXERCISE_VERSION); - QUICPacketUPtr initial_packet = - packet_factory.create_initial_packet({}, {}, 0, std::move(dummy_payload), dummy_payload_len, true, false, true); - REQUIRE(initial_packet != nullptr); + uint8_t initial_packet_buf[QUICPacket::MAX_INSTANCE_SIZE]; + QUICPacketUPtr packet = packet_factory.create_initial_packet(initial_packet_buf, {}, {}, 0, std::move(dummy_payload), + dummy_payload_len, true, false, true); + REQUIRE(packet != nullptr); + QUICInitialPacket &initial_packet = static_cast(*packet); // Server send VN packet based on Initial packet QUICPacketUPtr vn_packet = - packet_factory.create_version_negotiation_packet(initial_packet->source_cid(), initial_packet->destination_cid()); + packet_factory.create_version_negotiation_packet(initial_packet.source_cid(), initial_packet.destination_cid()); REQUIRE(vn_packet != nullptr); + auto blocks = vn_packet->header_block(); + blocks->next = vn_packet->payload_block(); + QUICVersionNegotiationPacketR received_vn_packet(nullptr, {}, {}, blocks); // Negotiate version - vn.negotiate(*vn_packet); + vn.negotiate(received_vn_packet); CHECK(vn.status() == QUICVersionNegotiationStatus::NEGOTIATED); CHECK(vn.negotiated_version() == QUIC_SUPPORTED_VERSIONS[0]); } diff --git a/lib/records/RecHttp.cc b/lib/records/RecHttp.cc index 0d2165f43c2..351792db70e 100644 --- a/lib/records/RecHttp.cc +++ b/lib/records/RecHttp.cc @@ -395,8 +395,10 @@ HttpProxyPort::processOptions(const char *opts) af_set_p = true; } else if (0 == strcasecmp(OPT_SSL, item)) { m_type = TRANSPORT_SSL; +#if TS_USE_QUIC == 1 } else if (0 == strcasecmp(OPT_QUIC, item)) { m_type = TRANSPORT_QUIC; +#endif } else if (0 == strcasecmp(OPT_PLUGIN, item)) { m_type = TRANSPORT_PLUGIN; } else if (0 == strcasecmp(OPT_PROXY_PROTO, item)) { diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc index 557c3e67eff..24bc5c8ad09 100644 --- a/mgmt/RecordsConfig.cc +++ b/mgmt/RecordsConfig.cc @@ -1370,6 +1370,10 @@ static const RecordElement RecordsConfig[] = , {RECT_CONFIG, "proxy.config.quic.client.cm_exercise_enabled", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL} , + {RECT_CONFIG, "proxy.config.quic.client.quantum_readiness_test_enabled", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL} + , + {RECT_CONFIG, "proxy.config.quic.server.quantum_readiness_test_enabled", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL} + , {RECT_CONFIG, "proxy.config.quic.server.supported_groups", RECD_STRING, "P-256:X25519:P-384:P-521" , RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL} , {RECT_CONFIG, "proxy.config.quic.client.supported_groups", RECD_STRING, "P-256:X25519:P-384:P-521" , RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL} @@ -1378,6 +1382,8 @@ static const RecordElement RecordsConfig[] = , {RECT_CONFIG, "proxy.config.quic.client.keylog_file", RECD_STRING, nullptr , RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL} , + {RECT_CONFIG, "proxy.config.quic.qlog_dir", RECD_STRING, nullptr , RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL} + , // Transport Parameters {RECT_CONFIG, "proxy.config.quic.no_activity_timeout_in", RECD_INT, "30000", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL} , @@ -1423,6 +1429,8 @@ static const RecordElement RecordsConfig[] = , {RECT_CONFIG, "proxy.config.quic.active_cid_limit_out", RECD_INT, "8", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL} , + {RECT_CONFIG, "proxy.config.quic.disable_active_migration", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_STR, "[0-1]", RECA_NULL} + , // Constants of Loss Detection {RECT_CONFIG, "proxy.config.quic.loss_detection.packet_threshold", RECD_INT, "3", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL} , diff --git a/proxy/http/HttpProxyServerMain.cc b/proxy/http/HttpProxyServerMain.cc index 9c80490ef8d..98941b31169 100644 --- a/proxy/http/HttpProxyServerMain.cc +++ b/proxy/http/HttpProxyServerMain.cc @@ -40,6 +40,7 @@ #include "HttpConnectionCount.h" #include "HttpProxyServerMain.h" #if TS_USE_QUIC == 1 +#include "P_QUICNetProcessor.h" #include "P_QUICNextProtocolAccept.h" #include "http3/Http3SessionAccept.h" #endif @@ -237,12 +238,12 @@ MakeHttpProxyAcceptor(HttpProxyAcceptor &acceptor, HttpProxyPort &port, unsigned quic->enableProtocols(port.m_session_protocol_preference); - // HTTP/3 - quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_3, new Http3SessionAccept(accept_opt)); - // HTTP/0.9 over QUIC (for interop only, will be removed) quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_QUIC, new Http3SessionAccept(accept_opt)); + // HTTP/3 + quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_3, new Http3SessionAccept(accept_opt)); + quic->proxyPort = &port; acceptor._accept = quic; #endif diff --git a/proxy/http/Makefile.am b/proxy/http/Makefile.am index 31a06c1b086..edc10ff5a9b 100644 --- a/proxy/http/Makefile.am +++ b/proxy/http/Makefile.am @@ -33,7 +33,8 @@ AM_CPPFLAGS += \ -I$(abs_top_srcdir)/proxy/logging \ -I$(abs_top_srcdir)/proxy/http2 \ -I$(abs_top_srcdir)/proxy/http3 \ - $(TS_INCLUDES) + $(TS_INCLUDES) \ + @YAMLCPP_INCLUDES@ noinst_HEADERS = HttpProxyServerMain.h noinst_LIBRARIES = libhttp.a diff --git a/proxy/http3/Http09App.cc b/proxy/http3/Http09App.cc index 5635f7594c0..cbde81222ab 100644 --- a/proxy/http3/Http09App.cc +++ b/proxy/http3/Http09App.cc @@ -27,6 +27,7 @@ #include "P_Net.h" #include "P_VConnection.h" +#include "P_QUICNetVConnection.h" #include "QUICDebugNames.h" #include "Http3Session.h" diff --git a/proxy/http3/Http3App.cc b/proxy/http3/Http3App.cc index 38f7fccabda..dddcfea11fd 100644 --- a/proxy/http3/Http3App.cc +++ b/proxy/http3/Http3App.cc @@ -27,6 +27,7 @@ #include "P_Net.h" #include "P_VConnection.h" +#include "P_QUICNetVConnection.h" #include "Http3.h" #include "Http3Config.h" diff --git a/proxy/http3/Http3Frame.cc b/proxy/http3/Http3Frame.cc index b9102dc3afd..db8c98b1adf 100644 --- a/proxy/http3/Http3Frame.cc +++ b/proxy/http3/Http3Frame.cc @@ -241,11 +241,11 @@ Http3SettingsFrame::Http3SettingsFrame(const uint8_t *buf, size_t buf_len, uint3 } size_t id_len = QUICVariableInt::size(buf + len); - uint16_t id = QUICIntUtil::read_QUICVariableInt(buf + len); + uint16_t id = QUICIntUtil::read_QUICVariableInt(buf + len, buf_len - len); len += id_len; size_t value_len = QUICVariableInt::size(buf + len); - uint64_t value = QUICIntUtil::read_QUICVariableInt(buf + len); + uint64_t value = QUICIntUtil::read_QUICVariableInt(buf + len, buf_len - len); len += value_len; // Ignore any SETTINGS identifier it does not understand. diff --git a/proxy/http3/Http3HeaderFramer.cc b/proxy/http3/Http3HeaderFramer.cc index 43157dce5fc..4a86fd4e705 100644 --- a/proxy/http3/Http3HeaderFramer.cc +++ b/proxy/http3/Http3HeaderFramer.cc @@ -92,7 +92,7 @@ Http3HeaderFramer::_generate_header_block() http2_init_pseudo_headers(this->_header); parse_result = this->_header.parse_resp(&this->_http_parser, this->_source_vio->get_reader(), &bytes_used, false); } - this->_source_vio->ndone += this->_header.length_get(); + this->_source_vio->ndone += bytes_used; switch (parse_result) { case PARSE_RESULT_DONE: { diff --git a/proxy/http3/Http3Session.cc b/proxy/http3/Http3Session.cc index db56c3ca498..4d9ceaf066e 100644 --- a/proxy/http3/Http3Session.cc +++ b/proxy/http3/Http3Session.cc @@ -22,6 +22,7 @@ */ #include "Http3Session.h" +#include "P_QUICNetVConnection.h" #include "Http3.h" diff --git a/proxy/http3/Http3SessionAccept.cc b/proxy/http3/Http3SessionAccept.cc index 9763f95c067..f5d1ff66419 100644 --- a/proxy/http3/Http3SessionAccept.cc +++ b/proxy/http3/Http3SessionAccept.cc @@ -22,6 +22,7 @@ */ #include "Http3SessionAccept.h" +#include "P_QUICNetVConnection.h" #include "P_Net.h" #include "I_Machine.h" diff --git a/proxy/http3/Http3StreamDataVIOAdaptor.cc b/proxy/http3/Http3StreamDataVIOAdaptor.cc index e5b6875c209..15712e1c5c8 100644 --- a/proxy/http3/Http3StreamDataVIOAdaptor.cc +++ b/proxy/http3/Http3StreamDataVIOAdaptor.cc @@ -42,6 +42,13 @@ Http3StreamDataVIOAdaptor::handle_frame(std::shared_ptr frame) MIOBuffer *writer = this->_sink_vio->get_writer(); writer->write(dframe->payload(), dframe->payload_length()); + this->_total_data_length += dframe->payload_length(); return Http3ErrorUPtr(new Http3NoError()); } + +void +Http3StreamDataVIOAdaptor::finalize() +{ + this->_sink_vio->nbytes = this->_total_data_length; +} diff --git a/proxy/http3/Http3StreamDataVIOAdaptor.h b/proxy/http3/Http3StreamDataVIOAdaptor.h index f62fd319c64..19ac384320a 100644 --- a/proxy/http3/Http3StreamDataVIOAdaptor.h +++ b/proxy/http3/Http3StreamDataVIOAdaptor.h @@ -36,6 +36,10 @@ class Http3StreamDataVIOAdaptor : public Http3FrameHandler std::vector interests() override; Http3ErrorUPtr handle_frame(std::shared_ptr frame) override; + // Http3StreamDataVIOAdaptor + void finalize(); + private: - VIO *_sink_vio = nullptr; + VIO *_sink_vio = nullptr; + int64_t _total_data_length = 0; }; diff --git a/proxy/http3/Http3Transaction.cc b/proxy/http3/Http3Transaction.cc index 519ef76d39e..fc9d066cdd7 100644 --- a/proxy/http3/Http3Transaction.cc +++ b/proxy/http3/Http3Transaction.cc @@ -22,6 +22,7 @@ */ #include "Http3Transaction.h" +#include "P_QUICNetVConnection.h" #include "QUICDebugNames.h" @@ -156,8 +157,11 @@ HQTransaction::do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *buf, this->_write_vio.vc_server = this; this->_write_vio.op = VIO::WRITE; - this->_process_write_vio(); - this->_send_tracked_event(this->_write_event, VC_EVENT_WRITE_READY, &this->_write_vio); + if (c != nullptr && nbytes > 0) { + // TODO Return nullptr if the stream is not on writable state + this->_process_write_vio(); + this->_send_tracked_event(this->_write_event, VC_EVENT_WRITE_READY, &this->_write_vio); + } return &this->_write_vio; } @@ -280,7 +284,7 @@ HQTransaction::_signal_read_event() if (this->_read_vio.cont == nullptr || this->_read_vio.op == VIO::NONE) { return; } - int event = this->_read_vio.ntodo() ? VC_EVENT_READ_READY : VC_EVENT_READ_COMPLETE; + int event = this->_read_vio.nbytes == INT64_MAX ? VC_EVENT_READ_READY : VC_EVENT_READ_COMPLETE; MUTEX_TRY_LOCK(lock, this->_read_vio.mutex, this_ethread()); if (lock.is_locked()) { @@ -364,17 +368,20 @@ Http3Transaction::state_stream_open(int event, void *edata) switch (event) { case VC_EVENT_READ_READY: - case VC_EVENT_READ_COMPLETE: { Http3TransVDebug("%s (%d)", get_vc_event_name(event), event); - int64_t len = this->_process_read_vio(); // if no progress, don't need to signal - if (len > 0) { + if (this->_process_read_vio() > 0) { this->_signal_read_event(); } this->_stream_io->read_reenable(); - break; - } + case VC_EVENT_READ_COMPLETE: + Http3TransVDebug("%s (%d)", get_vc_event_name(event), event); + this->_process_read_vio(); + this->_data_handler->finalize(); + this->_signal_read_event(); + this->_stream_io->read_reenable(); + break; case VC_EVENT_WRITE_READY: case VC_EVENT_WRITE_COMPLETE: { Http3TransVDebug("%s (%d)", get_vc_event_name(event), event); diff --git a/proxy/http3/Http3Transaction.h b/proxy/http3/Http3Transaction.h index 1bed5b058fd..5eebfbad310 100644 --- a/proxy/http3/Http3Transaction.h +++ b/proxy/http3/Http3Transaction.h @@ -34,6 +34,7 @@ class Http09Session; class Http3Session; class Http3HeaderFramer; class Http3DataFramer; +class Http3StreamDataVIOAdaptor; class HQTransaction : public ProxyTransaction { @@ -115,10 +116,10 @@ class Http3Transaction : public HQTransaction // These are for HTTP/3 Http3FrameDispatcher _frame_dispatcher; Http3FrameCollector _frame_collector; - Http3FrameGenerator *_header_framer = nullptr; - Http3FrameGenerator *_data_framer = nullptr; - Http3FrameHandler *_header_handler = nullptr; - Http3FrameHandler *_data_handler = nullptr; + Http3FrameGenerator *_header_framer = nullptr; + Http3FrameGenerator *_data_framer = nullptr; + Http3FrameHandler *_header_handler = nullptr; + Http3StreamDataVIOAdaptor *_data_handler = nullptr; }; /** diff --git a/proxy/http3/Makefile.am b/proxy/http3/Makefile.am index 7c626094029..979f3a656e9 100644 --- a/proxy/http3/Makefile.am +++ b/proxy/http3/Makefile.am @@ -31,7 +31,8 @@ AM_CPPFLAGS += \ -I$(abs_top_srcdir)/proxy/hdrs \ -I$(abs_top_srcdir)/proxy/shared \ -I$(abs_top_srcdir)/proxy/http/remap \ - $(TS_INCLUDES) + $(TS_INCLUDES) \ + @YAMLCPP_INCLUDES@ noinst_LIBRARIES = libhttp3.a diff --git a/proxy/http3/test/test_QPACK.cc b/proxy/http3/test/test_QPACK.cc index 179cd57848e..211030764e8 100644 --- a/proxy/http3/test/test_QPACK.cc +++ b/proxy/http3/test/test_QPACK.cc @@ -41,7 +41,7 @@ extern char appname[256]; extern char pattern[256]; constexpr int ACK_MODE_IMMEDIATE = 1; -constexpr int ACK_MODE_NONE = 0; +// constexpr int ACK_MODE_NONE = 0; constexpr int MAX_SEQUENCE = 1024; diff --git a/src/traffic_quic/Makefile.inc b/src/traffic_quic/Makefile.inc index a0fbf471f97..c00faf01e8a 100644 --- a/src/traffic_quic/Makefile.inc +++ b/src/traffic_quic/Makefile.inc @@ -33,7 +33,7 @@ traffic_quic_traffic_quic_CPPFLAGS = \ -I$(abs_top_srcdir)/proxy/logging \ -I$(abs_top_srcdir)/proxy/shared \ $(TS_INCLUDES) \ - @OPENSSL_INCLUDES@ + @OPENSSL_INCLUDES@ @YAMLCPP_INCLUDES@ traffic_quic_traffic_quic_LDFLAGS = \ $(AM_LDFLAGS) \ diff --git a/src/traffic_quic/quic_client.cc b/src/traffic_quic/quic_client.cc index c6d15b73ef0..8fc6e373a9b 100644 --- a/src/traffic_quic/quic_client.cc +++ b/src/traffic_quic/quic_client.cc @@ -28,13 +28,14 @@ #include #include "Http3Transaction.h" +#include "P_QUICNetVConnection.h" // OpenSSL protocol-lists format (vector of 8-bit length-prefixed, byte strings) // https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_alpn_protos.html // Should be integrate with IP_PROTO_TAG_HTTP_QUIC in ts/ink_inet.h ? using namespace std::literals; -static constexpr std::string_view HQ_ALPN_PROTO_LIST("\5hq-23"sv); -static constexpr std::string_view H3_ALPN_PROTO_LIST("\5h3-23"sv); +static constexpr std::string_view HQ_ALPN_PROTO_LIST("\5hq-27"sv); +static constexpr std::string_view H3_ALPN_PROTO_LIST("\5h3-27"sv); QUICClient::QUICClient(const QUICClientConfig *config) : Continuation(new_ProxyMutex()), _config(config) { @@ -80,7 +81,11 @@ QUICClient::start(int, void *) opt.socket_recv_bufsize = 1048576; opt.socket_send_bufsize = 1048576; opt.alpn_protos = alpn_protos; - opt.set_sni_servername(this->_config->addr, strnlen(this->_config->addr, 1023)); + if (strlen(this->_config->server_name) == 0) { + opt.set_sni_servername(this->_config->addr, strnlen(this->_config->addr, 1023)); + } else { + opt.set_sni_servername(this->_config->server_name, strnlen(this->_config->server_name, 1023)); + } SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); @@ -224,7 +229,8 @@ Http09ClientApp::main_event_handler(int event, Event *data) if (stream_io->is_read_done() && this->_config->close) { // Connection Close Exercise - this->_qc->close(QUICConnectionErrorUPtr(new QUICConnectionError(QUICTransErrorCode::NO_ERROR, "Close Exercise"))); + this->_qc->close_quic_connection( + QUICConnectionErrorUPtr(new QUICConnectionError(QUICTransErrorCode::NO_ERROR, "Close Exercise"))); } break; @@ -272,7 +278,16 @@ Http3ClientApp::start() this->_resp_buf = new_MIOBuffer(BUFFER_SIZE_INDEX_32K); IOBufferReader *resp_buf_reader = _resp_buf->alloc_reader(); - this->_resp_handler = new RespHandler(this->_config, resp_buf_reader); + this->_resp_handler = new RespHandler(this->_config, resp_buf_reader, [&](void) { + if (this->_config->close) { + // Connection Close Exercise + this->_qc->close_quic_connection( + QUICConnectionErrorUPtr(new QUICConnectionError(QUICTransErrorCode::NO_ERROR, "Close Exercise"))); + } else if (this->_config->reset) { + // Stateless Reset Excercise + this->_qc->reset_quic_connection(); + } + }); super::start(); this->_do_http_request(); @@ -309,7 +324,13 @@ Http3ClientApp::_do_http_request() format = "GET https://%s/%s HTTP/1.1\r\n\r\n"; } - int request_len = snprintf(request, sizeof(request), format.c_str(), this->_config->addr, this->_config->path); + const char *authority; + if (strlen(this->_config->server_name) == 0) { + authority = this->_config->addr; + } else { + authority = this->_config->server_name; + } + int request_len = snprintf(request, sizeof(request), format.c_str(), authority, this->_config->path); Http09ClientAppDebug("\n%s", request); @@ -322,8 +343,8 @@ Http3ClientApp::_do_http_request() // // Response Handler // -RespHandler::RespHandler(const QUICClientConfig *config, IOBufferReader *reader) - : Continuation(new_ProxyMutex()), _config(config), _reader(reader) +RespHandler::RespHandler(const QUICClientConfig *config, IOBufferReader *reader, std::function on_complete) + : Continuation(new_ProxyMutex()), _config(config), _reader(reader), _on_complete(on_complete) { if (this->_config->output[0] != 0x0) { this->_filename = this->_config->output; @@ -372,6 +393,10 @@ RespHandler::main_event_handler(int event, Event *data) std::cout.rdbuf(default_stream); } + if (event == VC_EVENT_READ_COMPLETE) { + this->_on_complete(); + } + break; } case VC_EVENT_WRITE_READY: diff --git a/src/traffic_quic/quic_client.h b/src/traffic_quic/quic_client.h index 9af9f0d3593..fc12dbc436c 100644 --- a/src/traffic_quic/quic_client.h +++ b/src/traffic_quic/quic_client.h @@ -38,8 +38,10 @@ struct QUICClientConfig { char output[1024] = {0}; char port[16] = "4433"; char path[1018] = "/"; + char server_name[128] = ""; char debug_tags[1024] = "quic|vv_quic_crypto|http3|qpack"; int close = false; + int reset = false; int http0_9 = true; int http3 = false; }; @@ -47,7 +49,7 @@ struct QUICClientConfig { class RespHandler : public Continuation { public: - RespHandler(const QUICClientConfig *config, IOBufferReader *reader); + RespHandler(const QUICClientConfig *config, IOBufferReader *reader, std::function on_complete); int main_event_handler(int event, Event *data); void set_read_vio(VIO *vio); @@ -56,6 +58,7 @@ class RespHandler : public Continuation const char *_filename = nullptr; IOBufferReader *_reader = nullptr; VIO *_read_vio = nullptr; + std::function _on_complete; }; class QUICClient : public Continuation diff --git a/src/traffic_quic/traffic_quic.cc b/src/traffic_quic/traffic_quic.cc index 69e23506ee0..00ac53a303a 100644 --- a/src/traffic_quic/traffic_quic.cc +++ b/src/traffic_quic/traffic_quic.cc @@ -61,8 +61,10 @@ main(int argc, const char **argv) {"output", 'o', "Write to FILE instead of stdout", "S1023", config.output, nullptr, nullptr}, {"port", 'p', "Port", "S15", config.port, nullptr, nullptr}, {"path", 'P', "Path", "S1017", config.path, nullptr, nullptr}, + {"server", 's', "Server name", "S127", config.server_name, nullptr, nullptr}, {"debug", 'T', "Vertical-bar-separated Debug Tags", "S1023", config.debug_tags, nullptr, nullptr}, {"close", 'c', "Enable connection close excercise", "F", &config.close, nullptr, nullptr}, + {"reset", 'r', "Enable stateless reset excercise", "F", &config.reset, nullptr, nullptr}, {"http0_9", '-', "Enable HTTP/0.9", "T", &config.http0_9, nullptr, nullptr}, {"http3", '-', "Enable HTTP/3", "F", &config.http3, nullptr, nullptr}, diff --git a/src/traffic_server/traffic_server.cc b/src/traffic_server/traffic_server.cc index ac1d953198c..84df79c1ca6 100644 --- a/src/traffic_server/traffic_server.cc +++ b/src/traffic_server/traffic_server.cc @@ -67,6 +67,7 @@ extern "C" int plock(int); #include "tscore/signals.h" #include "P_EventSystem.h" #include "P_Net.h" +#include "P_QUICNetProcessor.h" #include "P_UDPNet.h" #include "P_DNS.h" #include "P_SplitDNS.h" diff --git a/src/tscore/ink_inet.cc b/src/tscore/ink_inet.cc index 9f36ce9f9a8..5ba9bc3c614 100644 --- a/src/tscore/ink_inet.cc +++ b/src/tscore/ink_inet.cc @@ -50,8 +50,8 @@ const std::string_view IP_PROTO_TAG_HTTP_0_9("http/0.9"sv); const std::string_view IP_PROTO_TAG_HTTP_1_0("http/1.0"sv); const std::string_view IP_PROTO_TAG_HTTP_1_1("http/1.1"sv); const std::string_view IP_PROTO_TAG_HTTP_2_0("h2"sv); // HTTP/2 over TLS -const std::string_view IP_PROTO_TAG_HTTP_QUIC("hq-23"sv); // HTTP/0.9 over QUIC -const std::string_view IP_PROTO_TAG_HTTP_3("h3-23"sv); // HTTP/3 over QUIC +const std::string_view IP_PROTO_TAG_HTTP_QUIC("hq-27"sv); // HTTP/0.9 over QUIC +const std::string_view IP_PROTO_TAG_HTTP_3("h3-27"sv); // HTTP/3 over QUIC const std::string_view UNIX_PROTO_TAG{"unix"sv};