From ab1acaf57ada4b654279a26f663be6988b575a25 Mon Sep 17 00:00:00 2001 From: chenhaibo <495810242@qq.com> Date: Thu, 21 Sep 2023 01:52:40 +0000 Subject: [PATCH 1/3] update srt to v1.5.3 --- trunk/3rdparty/srt-1-fit/CMakeLists.txt | 106 +- trunk/3rdparty/srt-1-fit/configure-data.tcl | 1 + .../srt-1-fit/haicrypt/cryspr-mbedtls.c | 15 +- .../srt-1-fit/haicrypt/cryspr-mbedtls.h | 8 +- .../srt-1-fit/haicrypt/cryspr-openssl-evp.c | 100 +- .../srt-1-fit/haicrypt/cryspr-openssl-evp.h | 4 + .../srt-1-fit/haicrypt/cryspr-openssl.c | 6 +- trunk/3rdparty/srt-1-fit/haicrypt/cryspr.c | 421 ++- trunk/3rdparty/srt-1-fit/haicrypt/cryspr.h | 33 +- trunk/3rdparty/srt-1-fit/haicrypt/haicrypt.h | 8 +- .../srt-1-fit/haicrypt/haicrypt_log.cpp | 4 +- .../srt-1-fit/haicrypt/haicrypt_log.h | 2 +- trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.c | 20 +- trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.h | 15 +- .../3rdparty/srt-1-fit/haicrypt/hcrypt_ctx.h | 3 +- .../srt-1-fit/haicrypt/hcrypt_ctx_rx.c | 60 +- .../srt-1-fit/haicrypt/hcrypt_ctx_tx.c | 30 +- .../3rdparty/srt-1-fit/haicrypt/hcrypt_msg.h | 6 +- trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_rx.c | 5 +- trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_tx.c | 61 +- .../scripts/CheckCXXStdPutTime.cmake | 57 + .../scripts/CheckGCCAtomicIntrinsics.cmake | 2 +- .../srt-1-fit/scripts/ShowProjectConfig.cmake | 1 + .../srt-1-fit/scripts/build-android/README.md | 3 + .../scripts/build-android/build-android | 111 + .../srt-1-fit/scripts/build-android/mkmbedtls | 27 + .../srt-1-fit/scripts/build-android/mksrt | 32 + .../srt-1-fit/scripts/build-android/mkssl | 85 + .../srt-1-fit/scripts/build-windows.ps1 | 2 +- .../scripts/generate-error-types.tcl | 2 +- .../scripts/googletest-download.cmake | 2 +- .../srt-1-fit/scripts/release-notes/README.md | 13 +- trunk/3rdparty/srt-1-fit/scripts/srt-dev.lua | 4 +- trunk/3rdparty/srt-1-fit/scripts/test_vista.c | 10 + .../scripts/win-installer/libsrt.nsi | 2 + .../srt-1-fit/srtcore/access_control.h | 2 +- trunk/3rdparty/srt-1-fit/srtcore/api.cpp | 334 +- trunk/3rdparty/srt-1-fit/srtcore/api.h | 11 + trunk/3rdparty/srt-1-fit/srtcore/buffer.cpp | 2298 ------------ trunk/3rdparty/srt-1-fit/srtcore/buffer.h | 611 ---- .../3rdparty/srt-1-fit/srtcore/buffer_rcv.cpp | 431 ++- trunk/3rdparty/srt-1-fit/srtcore/buffer_rcv.h | 73 +- .../3rdparty/srt-1-fit/srtcore/buffer_snd.cpp | 730 ++++ trunk/3rdparty/srt-1-fit/srtcore/buffer_snd.h | 259 ++ .../srt-1-fit/srtcore/buffer_tools.cpp | 275 ++ .../3rdparty/srt-1-fit/srtcore/buffer_tools.h | 201 ++ trunk/3rdparty/srt-1-fit/srtcore/cache.cpp | 2 +- trunk/3rdparty/srt-1-fit/srtcore/cache.h | 2 +- trunk/3rdparty/srt-1-fit/srtcore/channel.cpp | 168 +- trunk/3rdparty/srt-1-fit/srtcore/channel.h | 133 +- trunk/3rdparty/srt-1-fit/srtcore/common.cpp | 21 +- trunk/3rdparty/srt-1-fit/srtcore/common.h | 16 +- trunk/3rdparty/srt-1-fit/srtcore/congctl.cpp | 2 +- trunk/3rdparty/srt-1-fit/srtcore/congctl.h | 4 +- trunk/3rdparty/srt-1-fit/srtcore/core.cpp | 3132 +++++++++-------- trunk/3rdparty/srt-1-fit/srtcore/core.h | 155 +- trunk/3rdparty/srt-1-fit/srtcore/crypto.cpp | 110 +- trunk/3rdparty/srt-1-fit/srtcore/crypto.h | 33 +- trunk/3rdparty/srt-1-fit/srtcore/epoll.cpp | 21 +- trunk/3rdparty/srt-1-fit/srtcore/epoll.h | 5 +- trunk/3rdparty/srt-1-fit/srtcore/fec.cpp | 59 +- trunk/3rdparty/srt-1-fit/srtcore/fec.h | 6 +- trunk/3rdparty/srt-1-fit/srtcore/filelist.maf | 6 +- trunk/3rdparty/srt-1-fit/srtcore/group.cpp | 642 +--- trunk/3rdparty/srt-1-fit/srtcore/group.h | 37 +- .../3rdparty/srt-1-fit/srtcore/handshake.cpp | 3 +- trunk/3rdparty/srt-1-fit/srtcore/list.cpp | 115 +- trunk/3rdparty/srt-1-fit/srtcore/list.h | 22 +- trunk/3rdparty/srt-1-fit/srtcore/logging.h | 29 +- trunk/3rdparty/srt-1-fit/srtcore/md5.cpp | 374 +- trunk/3rdparty/srt-1-fit/srtcore/md5.h | 32 +- trunk/3rdparty/srt-1-fit/srtcore/packet.cpp | 161 +- trunk/3rdparty/srt-1-fit/srtcore/packet.h | 15 +- .../srt-1-fit/srtcore/packetfilter.cpp | 16 +- .../3rdparty/srt-1-fit/srtcore/packetfilter.h | 4 +- .../srt-1-fit/srtcore/packetfilter_api.h | 2 +- .../3rdparty/srt-1-fit/srtcore/platform_sys.h | 4 - trunk/3rdparty/srt-1-fit/srtcore/queue.cpp | 197 +- trunk/3rdparty/srt-1-fit/srtcore/queue.h | 43 +- .../srt-1-fit/srtcore/socketconfig.cpp | 87 +- .../3rdparty/srt-1-fit/srtcore/socketconfig.h | 43 +- trunk/3rdparty/srt-1-fit/srtcore/srt.h | 26 +- .../srt-1-fit/srtcore/srt_attr_defs.h | 2 +- .../3rdparty/srt-1-fit/srtcore/srt_c_api.cpp | 6 + trunk/3rdparty/srt-1-fit/srtcore/srt_compat.c | 6 +- .../srt-1-fit/srtcore/strerror_defs.cpp | 8 +- trunk/3rdparty/srt-1-fit/srtcore/sync.cpp | 6 +- trunk/3rdparty/srt-1-fit/srtcore/sync.h | 10 +- .../3rdparty/srt-1-fit/srtcore/sync_posix.cpp | 2 +- trunk/3rdparty/srt-1-fit/srtcore/window.cpp | 12 +- 90 files changed, 5982 insertions(+), 6283 deletions(-) create mode 100644 trunk/3rdparty/srt-1-fit/scripts/CheckCXXStdPutTime.cmake create mode 100644 trunk/3rdparty/srt-1-fit/scripts/build-android/README.md create mode 100755 trunk/3rdparty/srt-1-fit/scripts/build-android/build-android create mode 100755 trunk/3rdparty/srt-1-fit/scripts/build-android/mkmbedtls create mode 100755 trunk/3rdparty/srt-1-fit/scripts/build-android/mksrt create mode 100755 trunk/3rdparty/srt-1-fit/scripts/build-android/mkssl create mode 100644 trunk/3rdparty/srt-1-fit/scripts/test_vista.c delete mode 100644 trunk/3rdparty/srt-1-fit/srtcore/buffer.cpp delete mode 100644 trunk/3rdparty/srt-1-fit/srtcore/buffer.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/buffer_snd.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/buffer_snd.h create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/buffer_tools.cpp create mode 100644 trunk/3rdparty/srt-1-fit/srtcore/buffer_tools.h diff --git a/trunk/3rdparty/srt-1-fit/CMakeLists.txt b/trunk/3rdparty/srt-1-fit/CMakeLists.txt index b43b4b63d5..1492306a28 100644 --- a/trunk/3rdparty/srt-1-fit/CMakeLists.txt +++ b/trunk/3rdparty/srt-1-fit/CMakeLists.txt @@ -8,7 +8,7 @@ # cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR) -set (SRT_VERSION 1.5.1) +set (SRT_VERSION 1.5.3) set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/scripts") include(haiUtil) # needed for set_version_variables @@ -117,6 +117,9 @@ if (ENABLE_DEBUG) set(ENABLE_HEAVY_LOGGING_DEFAULT ON) endif() +# Note that the IP_PKTINFO has a limited portability and may not work on some platforms +# (Windows, FreeBSD, ...). +set (ENABLE_PKTINFO_DEFAULT OFF) set(ENABLE_STDCXX_SYNC_DEFAULT OFF) set(ENABLE_MONOTONIC_CLOCK_DEFAULT OFF) @@ -144,10 +147,13 @@ option(ENABLE_HEAVY_LOGGING "Should heavy debug logging be enabled" ${ENABLE_HEA option(ENABLE_HAICRYPT_LOGGING "Should logging in haicrypt be enabled" 0) option(ENABLE_SHARED "Should libsrt be built as a shared library" ON) option(ENABLE_STATIC "Should libsrt be built as a static library" ON) +option(ENABLE_PKTINFO "Enable using IP_PKTINFO to allow the listener extracting the target IP address from incoming packets" ${ENABLE_PKTINFO_DEFAULT}) option(ENABLE_RELATIVE_LIBPATH "Should application contain relative library paths, like ../lib" OFF) option(ENABLE_GETNAMEINFO "In-logs sockaddr-to-string should do rev-dns" OFF) option(ENABLE_UNITTESTS "Enable unit tests" OFF) option(ENABLE_ENCRYPTION "Enable encryption in SRT" ON) +option(ENABLE_AEAD_API_PREVIEW "Enable AEAD API preview in SRT" Off) +option(ENABLE_MAXREXMITBW "Enable SRTO_MAXREXMITBW (v1.6.0 API preview)" Off) option(ENABLE_CXX_DEPS "Extra library dependencies in srt.pc for the CXX libraries useful with C language" ON) option(USE_STATIC_LIBSTDCXX "Should use static rather than shared libstdc++" OFF) option(ENABLE_INET_PTON "Set to OFF to prevent usage of inet_pton when building against modern SDKs while still requiring compatibility with older Windows versions, such as Windows XP, Windows Server 2003 etc." ON) @@ -160,7 +166,6 @@ option(USE_BUSY_WAITING "Enable more accurate sending times at a cost of potenti option(USE_GNUSTL "Get c++ library/headers from the gnustl.pc" OFF) option(ENABLE_SOCK_CLOEXEC "Enable setting SOCK_CLOEXEC on a socket" ON) option(ENABLE_SHOW_PROJECT_CONFIG "Enable show Project Configuration" OFF) -option(ENABLE_NEW_RCVBUFFER "Enable new receiver buffer implementation" ON) option(ENABLE_CLANG_TSA "Enable Clang Thread Safety Analysis" OFF) @@ -200,6 +205,16 @@ else() message(STATUS "USE_BUSY_WAITING: OFF (default)") endif() +# Reduce the frequency of some frequent logs, milliseconds +set(SRT_LOG_SLOWDOWN_FREQ_MS_DEFAULT 1000) # 1s +if (NOT DEFINED SRT_LOG_SLOWDOWN_FREQ_MS) + if (ENABLE_HEAVY_LOGGING) + set(SRT_LOG_SLOWDOWN_FREQ_MS 0) # Just show every log message. + else() + set(SRT_LOG_SLOWDOWN_FREQ_MS ${SRT_LOG_SLOWDOWN_FREQ_MS_DEFAULT}) + endif() +endif() + if ( CYGWIN AND NOT CYGWIN_USE_POSIX ) set(WIN32 1) set(CMAKE_LEGACY_CYGWIN_WIN32 1) @@ -285,7 +300,13 @@ if(WIN32) if(ENABLE_INET_PTON) set(CMAKE_REQUIRED_LIBRARIES ws2_32) check_function_exists(inet_pton HAVE_INET_PTON) - add_definitions(-D_WIN32_WINNT=0x0600) + try_compile(AT_LEAST_VISTA + ${CMAKE_BINARY_DIR} + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/test_vista.c") + if(NOT AT_LEAST_VISTA) + # force targeting Vista + add_definitions(-D_WIN32_WINNT=0x0600) + endif() else() add_definitions(-D_WIN32_WINNT=0x0501) endif() @@ -422,8 +443,21 @@ if (ENABLE_ENCRYPTION) add_definitions(-DSRT_ENABLE_ENCRYPTION) message(STATUS "ENCRYPTION: ENABLED, using: ${SSL_REQUIRED_MODULES}") message (STATUS "SSL libraries: ${SSL_LIBRARIES}") + + if (ENABLE_AEAD_API_PREVIEW) + if ("${USE_ENCLIB}" STREQUAL "openssl-evp") + add_definitions(-DENABLE_AEAD_API_PREVIEW) + message(STATUS "ENCRYPTION AEAD API: ENABLED") + else() + message(FATAL_ERROR "ENABLE_AEAD_API_PREVIEW is only available with USE_ENCLIB=openssl-evp!") + endif() + else() + message(STATUS "ENCRYPTION AEAD API: DISABLED") + endif() + else() message(STATUS "ENCRYPTION: DISABLED") + message(STATUS "ENCRYPTION AEAD API: N/A") endif() if (USE_GNUSTL) @@ -433,8 +467,15 @@ if (USE_GNUSTL) set (SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) endif() +if (ENABLE_MAXREXMITBW) + add_definitions(-DENABLE_MAXREXMITBW) + message(STATUS "MAXREXMITBW API: ENABLED") +else() + message(STATUS "MAXREXMITBW API: DISABLED") +endif() + if (USING_DEFAULT_COMPILER_PREFIX) -# Detect if the compiler is GNU compatable for flags +# Detect if the compiler is GNU compatible for flags if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Intel|Clang|AppleClang") message(STATUS "COMPILER: ${CMAKE_CXX_COMPILER_ID} (${CMAKE_CXX_COMPILER}) - GNU compat") set(HAVE_COMPILER_GNU_COMPAT 1) @@ -478,6 +519,15 @@ CheckGCCAtomicIntrinsics() include(CheckCXXAtomic) CheckCXXAtomic() +# Check for std::put_time(): +# Sets: +# HAVE_CXX_STD_PUT_TIME +include(CheckCXXStdPutTime) +CheckCXXStdPutTime() +if (HAVE_CXX_STD_PUT_TIME) + add_definitions(-DHAVE_CXX_STD_PUT_TIME=1) +endif() + if (DISABLE_CXX11) set (ENABLE_CXX11 0) elseif( DEFINED ENABLE_CXX11 ) @@ -533,14 +583,6 @@ if (ENABLE_SOCK_CLOEXEC) add_definitions(-DENABLE_SOCK_CLOEXEC=1) endif() -if (ENABLE_NEW_RCVBUFFER) - add_definitions(-DENABLE_NEW_RCVBUFFER=1) - message(STATUS "RECEIVER_BUFFER: NEW") -else() - remove_definitions(-DENABLE_NEW_RCVBUFFER) - message(STATUS "RECEIVER_BUFFER: OLD") -endif() - if (CMAKE_MAJOR_VERSION LESS 3) set (FORCE_CXX_STANDARD_GNUONLY 1) endif() @@ -614,6 +656,9 @@ endif() # add extra warning flags for gccish compilers if (HAVE_COMPILER_GNU_COMPAT) set (SRT_GCC_WARN "-Wall -Wextra") + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set (SRT_GCC_WARN "${SRT_GCC_WARN} -Wshadow=local") + endif() else() # cpp debugging on Windows :D #set (SRT_GCC_WARN "/showIncludes") @@ -720,6 +765,15 @@ if (ENABLE_GETNAMEINFO) list(APPEND SRT_EXTRA_CFLAGS "-DENABLE_GETNAMEINFO=1") endif() +if (ENABLE_PKTINFO) + if (WIN32 OR BSD) + message(FATAL_ERROR "PKTINFO is not implemented on Windows or *BSD.") + endif() + + list(APPEND SRT_EXTRA_CFLAGS "-DSRT_ENABLE_PKTINFO=1") +endif() + + # ENABLE_EXPERIMENTAL_BONDING is deprecated. Use ENABLE_BONDING. ENABLE_EXPERIMENTAL_BONDING is be removed in v1.6.0. if (ENABLE_EXPERIMENTAL_BONDING) message(DEPRECATION "ENABLE_EXPERIMENTAL_BONDING is deprecated. Please use ENABLE_BONDING instead.") @@ -1040,6 +1094,8 @@ if (ENABLE_SHARED) target_compile_definitions(srt_virtual PUBLIC -DSRT_DYNAMIC) endif() +target_compile_definitions(srt_virtual PRIVATE -DSRT_LOG_SLOWDOWN_FREQ_MS=${SRT_LOG_SLOWDOWN_FREQ_MS}) + if (srt_libspec_shared) if (MICROSOFT) target_link_libraries(${TARGET_srt}_shared PUBLIC Ws2_32.lib) @@ -1064,6 +1120,11 @@ elseif (HAVE_LIBATOMIC AND HAVE_LIBATOMIC_COMPILES_STATIC) if (srt_libspec_static) target_link_libraries(${TARGET_srt}_static PUBLIC atomic) endif() +elseif (LINUX AND HAVE_LIBATOMIC AND HAVE_LIBATOMIC_COMPILES) + # This is a workaround for some older Linux Toolchains. + if (srt_libspec_static) + target_link_libraries(${TARGET_srt}_static PUBLIC atomic) + endif() endif() # Cygwin installs the *.dll libraries in bin directory and uses PATH. @@ -1122,7 +1183,7 @@ endif() # obtained by `pkg-config --libs`. if(ENABLE_CXX_DEPS) foreach(LIB ${CMAKE_CXX_IMPLICIT_LINK_LIBRARIES}) - if(IS_ABSOLUTE ${LIB} AND EXISTS ${LIB}) + if((IS_ABSOLUTE ${LIB} AND EXISTS ${LIB}) OR (${LIB} MATCHES "^-l")) set(SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} ${LIB}) else() set(SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} "-l${LIB}") @@ -1334,6 +1395,10 @@ if (ENABLE_EXAMPLES) srt_add_example(recvfile.cpp) + srt_add_example(sendmsg.cpp) + + srt_add_example(recvmsg.cpp) + srt_add_example(test-c-client.c) srt_add_example(example-client-nonblock.c) @@ -1363,7 +1428,13 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - find_package(GTest 1.8) + # Version ranges are only supported with CMake 3.19 or later. + # Need GTest v1.10 or higher to support GTEST_SKIP. + if (${CMAKE_VERSION} VERSION_LESS "3.19.0") + find_package(GTest 1.10) + else() + find_package(GTest 1.10...1.12) + endif() if (NOT GTEST_FOUND) message(STATUS "GTEST not found! Fetching from git.") include(googletest) @@ -1395,12 +1466,13 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) NAME test-srt COMMAND ${CMAKE_BINARY_DIR}/test-srt ) + #set_tests_properties(test-srt PROPERTIES RUN_SERIAL TRUE) else() gtest_add_tests( - test-srt - "" - AUTO + TEST_LIST tests_srt + TARGET test-srt ) + set_tests_properties(${tests_srt} PROPERTIES RUN_SERIAL TRUE) endif() enable_testing() diff --git a/trunk/3rdparty/srt-1-fit/configure-data.tcl b/trunk/3rdparty/srt-1-fit/configure-data.tcl index eace99da14..2944c1c77f 100644 --- a/trunk/3rdparty/srt-1-fit/configure-data.tcl +++ b/trunk/3rdparty/srt-1-fit/configure-data.tcl @@ -43,6 +43,7 @@ set cmake_options { enable-logging "Should logging be enabled (default: ON)" enable-heavy-logging "Should heavy debug logging be enabled (default: OFF)" enable-haicrypt-logging "Should logging in haicrypt be enabled (default: OFF)" + enable-pktinfo "Should pktinfo reading and using be enabled (POSIX only) (default: OFF)" enable-shared "Should libsrt be built as a shared library (default: ON)" enable-static "Should libsrt be built as a static library (default: ON)" enable-relative-libpath "Should applications contain relative library paths, like ../lib (default: OFF)" diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-mbedtls.c b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-mbedtls.c index c220162937..a961cca61e 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-mbedtls.c +++ b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-mbedtls.c @@ -16,7 +16,7 @@ written by 2022-05-19 (jdube) CRYSPR2 adaptation 2019-06-27 (jdube) - GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT) + MBedTLS CRYSPR/4SRT (CRYypto Service PRovider for SRT) *****************************************************************************/ #include "hcrypt.h" @@ -32,7 +32,7 @@ written by static mbedtls_ctr_drbg_context crysprMbedtls_ctr_drbg; static mbedtls_entropy_context crysprMbedtls_entropy; -typedef struct tag_crysprGnuTLS_AES_cb { +typedef struct tag_crysprMBedTLS_AES_cb { CRYSPR_cb ccb; /* CRYSPR control block */ /* Add other cryptolib specific data here */ #ifdef CRYSPR2 @@ -75,9 +75,9 @@ int crysprMbedtls_AES_SetKey( // kstr_len is in "bytes" convention (16, 24, 32). if (bEncrypt) { /* Encrypt key */ - ret = mbedtls_aes_setkey_enc(aes_key, kstr, kstr_len*8); + ret = mbedtls_aes_setkey_enc(aes_key, kstr, (unsigned int)kstr_len*8); } else { /* Decrypt key */ - ret = mbedtls_aes_setkey_dec(aes_key, kstr, kstr_len*8); + ret = mbedtls_aes_setkey_dec(aes_key, kstr, (unsigned int)kstr_len*8); } return ret == 0 ? 0 : -1; @@ -91,8 +91,8 @@ int crysprMbedtls_AES_EcbCipher( /* AES Electronic Codebook cipher*/ unsigned char *out_txt, /* dst (cipher text) */ size_t *outlen) /* dst len */ { - int nblk = inlen/CRYSPR_AESBLKSZ; - int nmore = inlen%CRYSPR_AESBLKSZ; + int nblk = (int)(inlen/CRYSPR_AESBLKSZ); + int nmore = (int)(inlen%CRYSPR_AESBLKSZ); int i; if (bEncrypt) { @@ -160,7 +160,6 @@ int crysprMbedtls_AES_CtrCipher( /* AES-CTR128 Encryption */ static CRYSPR_cb *crysprMbedtls_Open(CRYSPR_methods *cryspr, size_t max_len) { crysprMbedtls_cb *aes_data; - CRYSPR_cb *cryspr_cb; aes_data = (crysprMbedtls_cb *)crysprHelper_Open(cryspr, sizeof(crysprMbedtls_cb), max_len); if (NULL == aes_data) { @@ -216,7 +215,7 @@ int crysprMbedtls_KmPbkdf2( ret = mbedtls_pkcs5_pbkdf2_hmac(&mdctx, (unsigned char*)passwd, passwd_len, salt, salt_len, - itr, key_len, out); + itr, (uint32_t)key_len, out); mbedtls_md_free(&mdctx); diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-mbedtls.h b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-mbedtls.h index a83b397c16..f62b32e486 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-mbedtls.h +++ b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-mbedtls.h @@ -13,12 +13,12 @@ written by Haivision Systems Inc. 2019-06-27 (jdube) - GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT) + MBedTLS CRYSPR/4SRT (CRYypto Service PRovider for SRT) *****************************************************************************/ -#ifndef CRYSPR_GNUTLS_H -#define CRYSPR_GNUTLS_H +#ifndef CRYSPR_MBEDTLS_H +#define CRYSPR_MBEDTLS_H #include #include @@ -59,5 +59,5 @@ typedef mbedtls_aes_context CRYSPR_AESCTX; /* CRYpto Service PRovider AES key struct tag_CRYSPR_methods *crysprMbedtls(void); -#endif /* CRYSPR_GNUTLS_H */ +#endif /* CRYSPR_MBEDTLS_H */ diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl-evp.c b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl-evp.c index 85e9f9d493..3e87e9a5ef 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl-evp.c +++ b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl-evp.c @@ -34,9 +34,11 @@ int crysprOpenSSL_EVP_Prng(unsigned char* rn, int len) const EVP_CIPHER* (*Xcipher_fnptr)(void) = EVP_aes_128_ecb; const EVP_CIPHER* (*_crysprOpenSSL_EVP_cipher_fnptr[][3])(void) = { - {NULL, NULL, NULL}, - {EVP_aes_128_ecb, EVP_aes_192_ecb, EVP_aes_256_ecb}, - {EVP_aes_128_ctr, EVP_aes_192_ctr, EVP_aes_256_ctr}, + {NULL, NULL, NULL}, // HCRYPT_CTX_MODE_CLRTXT + {EVP_aes_128_ecb, EVP_aes_192_ecb, EVP_aes_256_ecb}, // HCRYPT_CTX_MODE_AESECB + {EVP_aes_128_ctr, EVP_aes_192_ctr, EVP_aes_256_ctr}, // HCRYPT_CTX_MODE_AESCTR + {NULL, NULL, NULL}, // HCRYPT_CTX_MODE_AESCBC + {EVP_aes_128_gcm, EVP_aes_192_gcm, EVP_aes_256_gcm}, // HCRYPT_CTX_MODE_AESGCM }; int crysprOpenSSL_EVP_AES_SetKey( @@ -47,7 +49,7 @@ int crysprOpenSSL_EVP_AES_SetKey( CRYSPR_AESCTX* aes_key) /* CRYpto Service PRovider AES Key context */ { const EVP_CIPHER* cipher = NULL; - int idxKlen = (kstr_len / 8) - 2; /* key_len index in cipher_fnptr array in [0,1,2] range */ + int idxKlen = (int)((kstr_len / 8) - 2); /* key_len index in cipher_fnptr array in [0,1,2] range */ switch (cipher_type) { @@ -61,6 +63,8 @@ int crysprOpenSSL_EVP_AES_SetKey( cipher_type = HCRYPT_CTX_MODE_AESECB; #endif break; + case HCRYPT_CTX_MODE_AESGCM: + break; default: HCRYPT_LOG(LOG_ERR, "invalid cipher type (%d). Expected: [%d..%d]\n", @@ -139,7 +143,7 @@ int crysprOpenSSL_EVP_AES_EcbCipher(bool bEncrypt, /* true:encry size_t* outlen_p) /* in/out dst len */ { int nmore = inlen % CRYSPR_AESBLKSZ; /* bytes in last incomplete block */ - int nblk = inlen / CRYSPR_AESBLKSZ + (nmore ? 1 : 0); /* blocks including incomplete */ + int nblk = (int)(inlen / CRYSPR_AESBLKSZ + (nmore ? 1 : 0)); /* blocks including incomplete */ size_t outsiz = (outlen_p ? *outlen_p : 0); int c_len = 0, f_len = 0; @@ -156,7 +160,7 @@ int crysprOpenSSL_EVP_AES_EcbCipher(bool bEncrypt, /* true:encry return (-1); /* output buf size must have room for PKCS7 padding */ } /* allows reusing of 'e' for multiple encryption cycles */ - if (!EVP_CipherInit_ex(aes_key, NULL, NULL, NULL, NULL, -1)) + if (!EVP_CipherInit_ex(aes_key, NULL, NULL, NULL, NULL, bEncrypt)) { HCRYPT_LOG(LOG_ERR, "EVP_CipherInit_ex(%p,NULL,...,-1) failed\n", aes_key); return -1; @@ -170,7 +174,7 @@ int crysprOpenSSL_EVP_AES_EcbCipher(bool bEncrypt, /* true:encry /* update ciphertext, c_len is filled with the length of ciphertext generated, * cryptoPtr->cipher_in_len is the size of plain/cipher text in bytes */ - if (!EVP_CipherUpdate(aes_key, out_txt, &c_len, indata, inlen)) + if (!EVP_CipherUpdate(aes_key, out_txt, &c_len, indata, (int)inlen)) { HCRYPT_LOG(LOG_ERR, "EVP_CipherUpdate(%p, out, %d, in, %d) failed\n", aes_key, c_len, inlen); return -1; @@ -223,7 +227,7 @@ int crysprOpenSSL_EVP_AES_CtrCipher(bool bEncrypt, /* true:encry /* update ciphertext, c_len is filled with the length of ciphertext generated, * cryptoPtr->cipher_in_len is the size of plain/cipher text in bytes */ - if (!EVP_CipherUpdate(aes_key, out_txt, &c_len, indata, inlen)) + if (!EVP_CipherUpdate(aes_key, out_txt, &c_len, indata, (int)inlen)) { HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CipherUpdate() failed"); return -1; @@ -247,6 +251,83 @@ int crysprOpenSSL_EVP_AES_CtrCipher(bool bEncrypt, /* true:encry return 0; } +int crysprOpenSSL_EVP_AES_GCMCipher(bool bEncrypt, /* true:encrypt, false:decrypt */ + CRYSPR_AESCTX* aes_key, /* CRYpto Service PRovider AES Key context */ + unsigned char* iv, /* iv */ + const unsigned char* aad, /* associated data */ + size_t aadlen, + const unsigned char* indata, /* src */ + size_t inlen, /* length */ + unsigned char* out_txt, + unsigned char* out_tag) /* auth tag */ +{ + int c_len, f_len; + + /* allows reusing of 'e' for multiple encryption cycles */ + if (!EVP_CipherInit_ex(aes_key, NULL, NULL, NULL, iv, -1)) + { + HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CipherInit_ex() failed"); + return -1; + } + if (!EVP_CIPHER_CTX_set_padding(aes_key, 0)) + { + HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CIPHER_CTX_set_padding() failed"); + return -1; + } + + /* + * Provide any AAD data. This can be called zero or more times as + * required + */ + if (1 != EVP_CipherUpdate(aes_key, NULL, &c_len, aad, (int) aadlen)) + { + ERR_print_errors_fp(stderr); + HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_EncryptUpdate failed"); + return -1; + } + + /* update ciphertext, c_len is filled with the length of ciphertext generated, + * cryptoPtr->cipher_in_len is the size of plain/cipher text in bytes + */ + if (!EVP_CipherUpdate(aes_key, out_txt, &c_len, indata, (int) inlen)) + { + HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CipherUpdate() failed"); + return -1; + } + + if (!bEncrypt && !EVP_CIPHER_CTX_ctrl(aes_key, EVP_CTRL_GCM_SET_TAG, HAICRYPT_AUTHTAG_MAX, out_tag)) { + ERR_print_errors_fp(stderr); + HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_EncryptUpdate failed"); + return -1; + } + + /* update ciphertext with the final remaining bytes */ + /* Useless with pre-padding */ + f_len = 0; + if (0 == EVP_CipherFinal_ex(aes_key, &out_txt[c_len], &f_len)) + { +#if ENABLE_HAICRYPT_LOGGING + char szErrBuf[256]; + HCRYPT_LOG(LOG_ERR, + "EVP_CipherFinal_ex(ctx,&out[%d],%d)) failed: %s\n", + c_len, + f_len, + ERR_error_string(ERR_get_error(), szErrBuf)); +#endif /*ENABLE_HAICRYPT_LOGGING*/ + return -1; + } + + /* Get the tag if we are encrypting */ + if (bEncrypt && !EVP_CIPHER_CTX_ctrl(aes_key, EVP_CTRL_GCM_GET_TAG, HAICRYPT_AUTHTAG_MAX, out_tag)) + { + ERR_print_errors_fp(stderr); + HCRYPT_LOG(LOG_ERR, "%s\n", "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_GET_TAG) failed"); + return -1; + } + + return 0; +} + /* * Password-based Key Derivation Function */ @@ -260,7 +341,7 @@ int crysprOpenSSL_EVP_KmPbkdf2(CRYSPR_cb* cryspr_cb, unsigned char* out) /* derived key */ { (void)cryspr_cb; - int rc = PKCS5_PBKDF2_HMAC_SHA1(passwd, passwd_len, salt, salt_len, itr, key_len, out); + int rc = PKCS5_PBKDF2_HMAC_SHA1(passwd, (int)passwd_len, salt, (int)salt_len, itr, (int)key_len, out); return (rc == 1 ? 0 : -1); } @@ -300,6 +381,7 @@ CRYSPR_methods* crysprOpenSSL_EVP(void) #if CRYSPR_HAS_AESCTR crysprOpenSSL_EVP_methods.aes_ctr_cipher = crysprOpenSSL_EVP_AES_CtrCipher; #endif + crysprOpenSSL_EVP_methods.aes_gcm_cipher = crysprOpenSSL_EVP_AES_GCMCipher; #if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP) /* AES-ECB only required if cryspr has no AES-CTR and no AES KeyWrap */ /* OpenSSL has both AESCTR and AESKWRP and the AESECB wrapper is only used diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl-evp.h b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl-evp.h index 61cb68617b..a1fb0b925f 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl-evp.h +++ b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl-evp.h @@ -34,6 +34,10 @@ written by */ #define CRYSPR_HAS_AESCTR 1 +/* Define CRYSPR_HAS_AESGCM to 1 if this CRYSPR has AES GCM cipher mode. OpenSSL EVP supports GCM. +*/ +#define CRYSPR_HAS_AESGCM 1 + /* Define CRYSPR_HAS_AESKWRAP to 1 if this CRYSPR has AES Key Wrap if not set to 0 to enable default/fallback crysprFallback_AES_WrapKey/crysprFallback_AES_UnwrapKey methods and provide the aes_ecb_cipher method . diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl.c b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl.c index 3af907d242..87eb535ec5 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl.c +++ b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr-openssl.c @@ -48,12 +48,12 @@ int crysprOpenSSL_AES_SetKey( (void)cipher_type; if (bEncrypt) { /* Encrypt key */ - if (AES_set_encrypt_key(kstr, kstr_len * 8, aes_key)) { + if (AES_set_encrypt_key(kstr, (int)(kstr_len * 8), aes_key)) { HCRYPT_LOG(LOG_ERR, "%s", "AES_set_encrypt_key(kek) failed\n"); return(-1); } } else { /* Decrypt key */ - if (AES_set_decrypt_key(kstr, kstr_len * 8, aes_key)) { + if (AES_set_decrypt_key(kstr, (int)(kstr_len * 8), aes_key)) { HCRYPT_LOG(LOG_ERR, "%s", "AES_set_decrypt_key(kek) failed\n"); return(-1); } @@ -163,7 +163,7 @@ int crysprOpenSSL_KmPbkdf2( unsigned char *out) /* derived key */ { (void)cryspr_cb; - int rc = PKCS5_PBKDF2_HMAC_SHA1(passwd,passwd_len,salt,salt_len,itr,key_len,out); + int rc = PKCS5_PBKDF2_HMAC_SHA1(passwd,(int)passwd_len,salt,(int)salt_len,itr,(int)key_len,out); return(rc == 1? 0 : -1); } diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr.c b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr.c index e6aa40219a..2e3a68a599 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr.c +++ b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr.c @@ -14,7 +14,7 @@ written by Haivision Systems Inc. 2019-06-28 (jdube) - CRYSPR/4SRT Initial implementation. + CRYSPR/4SRT Initial implementation. *****************************************************************************/ #include "hcrypt.h" @@ -25,111 +25,135 @@ written by int crysprStub_Prng(unsigned char *rn, int len) { - (void)rn; - (void)len; - return(0); + (void)rn; + (void)len; + return(0); } int crysprStub_AES_SetKey( - int cipher_type, /* One of HCRYPT_CTX_MODE_[CLRTXT|AESECB|AESCTR|AESGDM] */ - bool bEncrypt, /* true Enxcrypt key, false: decrypt */ - const unsigned char *kstr, /* key sttring*/ - size_t kstr_len, /* kstr len in bytes (16, 24, or 32 bytes (for AES128,AES192, or AES256) */ - CRYSPR_AESCTX *aes_key) /* Cryptolib Specific AES key context */ + int cipher_type, /* One of HCRYPT_CTX_MODE_[CLRTXT|AESECB|AESCTR|AESGDM] */ + bool bEncrypt, /* true Enxcrypt key, false: decrypt */ + const unsigned char *kstr, /* key sttring*/ + size_t kstr_len, /* kstr len in bytes (16, 24, or 32 bytes (for AES128,AES192, or AES256) */ + CRYSPR_AESCTX *aes_key) /* Cryptolib Specific AES key context */ { - (void)cipher_type; - (void)bEncrypt; - (void)kstr; - (void)kstr_len; - (void)aes_key; + (void)cipher_type; + (void)bEncrypt; + (void)kstr; + (void)kstr_len; + (void)aes_key; - return(0); + return(0); } int crysprStub_AES_EcbCipher( - bool bEncrypt, /* true:encrypt, false:decrypt */ - CRYSPR_AESCTX *aes_key, /* AES context */ - const unsigned char *indata,/* src (clear text)*/ - size_t inlen, /* length */ - unsigned char *out_txt, /* dst (cipher text) */ - size_t *outlen) /* dst len */ + bool bEncrypt, /* true:encrypt, false:decrypt */ + CRYSPR_AESCTX *aes_key, /* AES context */ + const unsigned char *indata,/* src (clear text)*/ + size_t inlen, /* length */ + unsigned char *out_txt, /* dst (cipher text) */ + size_t *outlen) /* dst len */ { - (void)bEncrypt; - (void)aes_key; - (void)indata; - (void)inlen; - (void)out_txt; - (void)outlen; - - return -1; + (void)bEncrypt; + (void)aes_key; + (void)indata; + (void)inlen; + (void)out_txt; + (void)outlen; + + return -1; } int crysprStub_AES_CtrCipher( - bool bEncrypt, /* true:encrypt, false:decrypt */ - CRYSPR_AESCTX *aes_key, /* AES context */ - unsigned char *iv, /* iv */ - const unsigned char *indata,/* src */ - size_t inlen, /* length */ - unsigned char *out_txt) /* dest */ + bool bEncrypt, /* true:encrypt, false:decrypt */ + CRYSPR_AESCTX *aes_key, /* AES context */ + unsigned char *iv, /* iv */ + const unsigned char *indata,/* src */ + size_t inlen, /* length */ + unsigned char *out_txt) /* dest */ +{ + (void)bEncrypt; + (void)aes_key; + (void)iv; + (void)indata; + (void)inlen; + (void)out_txt; + + return(-1); +} + +int crysprStub_AES_GCMCipher( + bool bEncrypt, /* true:encrypt, false:decrypt */ + CRYSPR_AESCTX *aes_key, /* AES context */ + unsigned char *iv, /* iv */ + const unsigned char *aad, /* associated data */ + size_t aadlen, + const unsigned char * indata, + size_t inlen, + unsigned char *out_txt, + unsigned char* out_tag) { - (void)bEncrypt; - (void)aes_key; - (void)iv; - (void)indata; - (void)inlen; - (void)out_txt; - - return(-1); + (void)bEncrypt; + (void)aes_key; + (void)iv; + (void)aad; + (void)aadlen; + (void)indata; + (void)inlen; + (void)out_txt; + (void)out_tag; + + return(-1); } unsigned char *crysprStub_SHA1_MsgDigest( - const unsigned char *m, /* in: message */ - size_t m_len, /* message length */ - unsigned char *md) /* out: message digest buffer *160 bytes */ + const unsigned char *m, /* in: message */ + size_t m_len, /* message length */ + unsigned char *md) /* out: message digest buffer *160 bytes */ { - (void)m; - (void)m_len; - (void)md; + (void)m; + (void)m_len; + (void)md; - return(NULL);//return md; + return(NULL);//return md; } /* * Password-based Key Derivation Function */ int crysprStub_KmPbkdf2( - CRYSPR_cb *cryspr_cb, - char *passwd, /* passphrase */ - size_t passwd_len, /* passphrase len */ - unsigned char *salt, /* salt */ - size_t salt_len, /* salt_len */ - int itr, /* iterations */ - size_t key_len, /* key_len */ - unsigned char *out) /* derived key */ + CRYSPR_cb *cryspr_cb, + char *passwd, /* passphrase */ + size_t passwd_len, /* passphrase len */ + unsigned char *salt, /* salt */ + size_t salt_len, /* salt_len */ + int itr, /* iterations */ + size_t key_len, /* key_len */ + unsigned char *out) /* derived key */ { - (void)cryspr_cb; - (void)passwd; - (void)passwd_len; - (void)salt; - (void)salt_len; - (void)itr; - (void)key_len; - (void)out; - - /* >>Todo: - * develop PBKDF2 using SHA1 primitive cryspr_cb->cryspr->sha1_msg_digest() for cryptolibs not providing it - */ - return(-1); + (void)cryspr_cb; + (void)passwd; + (void)passwd_len; + (void)salt; + (void)salt_len; + (void)itr; + (void)key_len; + (void)out; + + /* >>Todo: + * develop PBKDF2 using SHA1 primitive cryspr_cb->cryspr->sha1_msg_digest() for cryptolibs not providing it + */ + return(-1); } static int crysprFallback_KmSetKey(CRYSPR_cb *cryspr_cb, bool bWrap, const unsigned char *kek, size_t kek_len) { - CRYSPR_AESCTX *aes_kek = CRYSPR_GETKEK(cryspr_cb); + CRYSPR_AESCTX *aes_kek = CRYSPR_GETKEK(cryspr_cb); - if (cryspr_cb->cryspr->aes_set_key(HCRYPT_CTX_MODE_AESECB, bWrap, kek, kek_len, aes_kek)) { - HCRYPT_LOG(LOG_ERR, "aes_set_%s_key(kek) failed\n", bWrap? "encrypt": "decrypt"); - return(-1); - } + if (cryspr_cb->cryspr->aes_set_key(HCRYPT_CTX_MODE_AESECB, bWrap, kek, kek_len, aes_kek)) { + HCRYPT_LOG(LOG_ERR, "aes_set_%s_key(kek) failed\n", bWrap? "encrypt": "decrypt"); + return(-1); + } return(0); } @@ -144,7 +168,7 @@ static const unsigned char default_iv[] = { int crysprFallback_AES_WrapKey(CRYSPR_cb *cryspr_cb, unsigned char *out, const unsigned char *in, - unsigned int inlen) + unsigned int inlen) { unsigned char *A, B[16], *R; const unsigned char *iv = default_iv; @@ -180,13 +204,13 @@ int crysprFallback_AES_WrapKey(CRYSPR_cb *cryspr_cb, } } memcpy(out, A, 8); - return 0; + return 0; } int crysprFallback_AES_UnwrapKey(CRYSPR_cb *cryspr_cb, unsigned char *out, const unsigned char *in, - unsigned int inlen) + unsigned int inlen) { unsigned char *A, B[16], *R; const unsigned char *iv = default_iv; @@ -225,9 +249,9 @@ int crysprFallback_AES_UnwrapKey(CRYSPR_cb *cryspr_cb, if (memcmp(A, iv, 8)) { memset(out, 0, inlen); - return -1; + return -1; } - return 0; + return 0; } static unsigned char *_crysprFallback_GetOutbuf(CRYSPR_cb *cryspr_cb, size_t pfx_len, size_t out_len) @@ -249,12 +273,12 @@ CRYSPR_cb *crysprHelper_Open(CRYSPR_methods *cryspr, size_t cb_len, size_t max_l unsigned char *membuf; size_t memsiz, padded_len = hcryptMsg_PaddedLen(max_len, 128/8); - if(cb_len < sizeof(*cryspr_cb)) { - HCRYPT_LOG(LOG_ERR, "crysprHelper_Open() cb_len too small (%zd < %zd)n", - cb_len, sizeof(*cryspr_cb)); - return(NULL); - } - memsiz = cb_len + (CRYSPR_OUTMSGMAX * padded_len); + if(cb_len < sizeof(*cryspr_cb)) { + HCRYPT_LOG(LOG_ERR, "crysprHelper_Open() cb_len too small (%zd < %zd)n", + cb_len, sizeof(*cryspr_cb)); + return(NULL); + } + memsiz = cb_len + (CRYSPR_OUTMSGMAX * padded_len); #if !CRYSPR_HAS_AESCTR memsiz += HCRYPT_CTR_STREAM_SZ; #endif /* !CRYSPR_HAS_AESCTR */ @@ -267,8 +291,8 @@ CRYSPR_cb *crysprHelper_Open(CRYSPR_methods *cryspr, size_t cb_len, size_t max_l membuf = (unsigned char *)cryspr_cb; membuf += sizeof(*cryspr_cb); - /*reserve cryspr's private data that caller will initialize */ - membuf += (cb_len-sizeof(CRYSPR_cb)); + /*reserve cryspr's private data that caller will initialize */ + membuf += (cb_len-sizeof(CRYSPR_cb)); #if !CRYSPR_HAS_AESCTR cryspr_cb->ctr_stream = membuf; @@ -289,28 +313,33 @@ CRYSPR_cb *crysprHelper_Open(CRYSPR_methods *cryspr, size_t cb_len, size_t max_l int crysprHelper_Close(CRYSPR_cb *cryspr_cb) { - free(cryspr_cb); - return(0); + free(cryspr_cb); + return(0); } static CRYSPR_cb *crysprFallback_Open(CRYSPR_methods *cryspr, size_t max_len) { - CRYSPR_cb *cryspr_cb; + CRYSPR_cb *cryspr_cb; - cryspr_cb = crysprHelper_Open(cryspr, sizeof(CRYSPR_cb), max_len); - return(cryspr_cb); + cryspr_cb = crysprHelper_Open(cryspr, sizeof(CRYSPR_cb), max_len); + return(cryspr_cb); } static int crysprFallback_Close(CRYSPR_cb *cryspr_cb) { - return(crysprHelper_Close(cryspr_cb)); + return(crysprHelper_Close(cryspr_cb)); } static int crysprFallback_MsSetKey(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, const unsigned char *key, size_t key_len) { CRYSPR_AESCTX *aes_sek = CRYSPR_GETSEK(cryspr_cb, hcryptCtx_GetKeyIndex(ctx)); /* Ctx tells if it's for odd or even key */ - if ((ctx->flags & HCRYPT_CTX_F_ENCRYPT) /* Encrypt key */ + if (ctx->mode == HCRYPT_CTX_MODE_AESGCM) { /* AES GCM mode */ + if (cryspr_cb->cryspr->aes_set_key(HCRYPT_CTX_MODE_AESGCM, (ctx->flags & HCRYPT_CTX_F_ENCRYPT) != 0, key, key_len, aes_sek)) { + HCRYPT_LOG(LOG_ERR, "%s", "CRYSPR->set_encrypt_key(sek) failed\n"); + return(-1); + } + } else if ((ctx->flags & HCRYPT_CTX_F_ENCRYPT) /* Encrypt key */ || (ctx->mode == HCRYPT_CTX_MODE_AESCTR)) { /* CTR mode decrypts using encryption methods */ if (cryspr_cb->cryspr->aes_set_key(HCRYPT_CTX_MODE_AESCTR, true, key, key_len, aes_sek)) { HCRYPT_LOG(LOG_ERR, "%s", "CRYSPR->set_encrypt_key(sek) failed\n"); @@ -395,10 +424,17 @@ static int crysprFallback_MsEncrypt( * to reserve room for unencrypted message header in output buffer */ pfx_len = ctx->msg_info->pfx_len; + /* Extra 16 bytes are needed for an authentication tag in GCM. */ + const int aux_len = (ctx->mode == HCRYPT_CTX_MODE_AESGCM) ? HAICRYPT_AUTHTAG_MAX : 0; - /* Get buffer room from the internal circular output buffer */ - out_msg = _crysprFallback_GetOutbuf(cryspr_cb, pfx_len, in_data[0].len); + /* Auth tag produced by AES GCM. */ + unsigned char tag[HAICRYPT_AUTHTAG_MAX]; + /* + * Get buffer room from the internal circular output buffer. + * Reserve additional 16 bytes for auth tag in AES GCM mode when needed. + */ + out_msg = _crysprFallback_GetOutbuf(cryspr_cb, pfx_len, in_data[0].len + aux_len); if (NULL == out_msg) { /* input data too big */ return(-1); @@ -406,6 +442,7 @@ static int crysprFallback_MsEncrypt( switch(ctx->mode) { case HCRYPT_CTX_MODE_AESCTR: /* Counter mode */ + case HCRYPT_CTX_MODE_AESGCM: { /* Get current key (odd|even) from context */ CRYSPR_AESCTX *aes_key = CRYSPR_GETSEK(cryspr_cb, hcryptCtx_GetKeyIndex(ctx)); /* Ctx tells if it's for odd or even key */ @@ -416,48 +453,64 @@ static int crysprFallback_MsEncrypt( hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1); /* - * Compute the Initial Vector - * IV (128-bit): - * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * | 0s | pki | ctr | - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * XOR - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * | nonce + - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * - * pki (32-bit): packet index - * ctr (16-bit): block counter - * nonce (112-bit): number used once (salt) - */ + * Compute the Initial Vector + * IV (128-bit): + * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | 0s | pki | ctr | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * XOR + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | nonce + + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * pki (32-bit): packet index + * ctr (16-bit): block counter + * nonce (112-bit): number used once (salt) + */ hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv); + if (ctx->mode == HCRYPT_CTX_MODE_AESGCM) + { + const int iret = cryspr_cb->cryspr->aes_gcm_cipher(true, aes_key, iv, in_data[0].pfx, pfx_len, in_data[0].payload, in_data[0].len, + &out_msg[pfx_len], tag); + if (iret) { + return(iret); + } + } + else { #if CRYSPR_HAS_AESCTR - cryspr_cb->cryspr->aes_ctr_cipher(true, aes_key, iv, in_data[0].payload, in_data[0].len, - &out_msg[pfx_len]); + cryspr_cb->cryspr->aes_ctr_cipher(true, aes_key, iv, in_data[0].payload, in_data[0].len, + &out_msg[pfx_len]); #else /*CRYSPR_HAS_AESCTR*/ - /* Create CtrStream. May be longer than in_len (next cryspr block size boundary) */ - int iret = _crysprFallback_AES_SetCtrStream(cryspr_cb, ctx, in_data[0].len, iv); - if (iret) { - return(iret); - } - /* Reserve output buffer for cryspr */ - out_msg = _crysprFallback_GetOutbuf(cryspr_cb, pfx_len, cryspr_cb->ctr_stream_len); - - /* Create KeyStream (encrypt CtrStream) */ - iret = cryspr_cb->cryspr->aes_ecb_cipher(true, aes_key, - cryspr_cb->ctr_stream, cryspr_cb->ctr_stream_len, - &out_msg[pfx_len], &out_len); - if (iret) { - HCRYPT_LOG(LOG_ERR, "%s", "hcOpenSSL_AES_ecb_cipher(encrypt, failed\n"); - return(iret); - } + + /* Create CtrStream. May be longer than in_len (next cryspr block size boundary) */ + int iret = _crysprFallback_AES_SetCtrStream(cryspr_cb, ctx, in_data[0].len, iv); + if (iret) { + return(iret); + } + /* Reserve output buffer for cryspr */ + out_msg = _crysprFallback_GetOutbuf(cryspr_cb, pfx_len, cryspr_cb->ctr_stream_len); + + /* Create KeyStream (encrypt CtrStream) */ + iret = cryspr_cb->cryspr->aes_ecb_cipher(true, aes_key, + cryspr_cb->ctr_stream, cryspr_cb->ctr_stream_len, + &out_msg[pfx_len], &out_len); + if (iret) { + HCRYPT_LOG(LOG_ERR, "%s", "hcOpenSSL_AES_ecb_cipher(encrypt, failed\n"); + return(iret); + } #endif/*CRYSPR_HAS_AESCTR*/ + } /* Prepend packet prefix (clear text) in output buffer */ memcpy(out_msg, in_data[0].pfx, pfx_len); /* CTR mode output length is same as input, no padding */ out_len = in_data[0].len; + if (ctx->mode == HCRYPT_CTX_MODE_AESGCM) + { + memcpy(out_msg + pfx_len + out_len, tag, sizeof(tag)); + out_len += sizeof(tag); + } break; } case HCRYPT_CTX_MODE_CLRTXT: /* Clear text mode (transparent mode for tests) */ @@ -497,8 +550,12 @@ static int crysprFallback_MsEncrypt( memcpy(in_data[0].payload, &out_msg[pfx_len], out_len); } #else /* CRYSPR_HAS_AESCTR */ - /* Copy output data back in input buffer */ - memcpy(in_data[0].payload, &out_msg[pfx_len], out_len); + /* Copy output data back in input buffer */ + memcpy(in_data[0].payload, &out_msg[pfx_len], out_len); + if (ctx->mode == HCRYPT_CTX_MODE_AESGCM) { + // Encoding produced more payload (auth tag). + return (int)out_len; + } #endif /* CRYSPR_HAS_AESCTR */ } else { /* Copy header in output buffer if needed */ @@ -533,8 +590,8 @@ static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, if (NULL != out_txt) { switch(ctx->mode) { case HCRYPT_CTX_MODE_AESCTR: + case HCRYPT_CTX_MODE_AESGCM: { -#if CRYSPR_HAS_AESCTR /* Get current key (odd|even) from context */ CRYSPR_AESCTX *aes_key = CRYSPR_GETSEK(cryspr_cb, hcryptCtx_GetKeyIndex(ctx)); unsigned char iv[CRYSPR_AESBLKSZ]; @@ -560,54 +617,63 @@ static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, */ hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv); - cryspr_cb->cryspr->aes_ctr_cipher(false, aes_key, iv, in_data[0].payload, in_data[0].len, + if (ctx->mode == HCRYPT_CTX_MODE_AESGCM) + { + unsigned char* tag = in_data[0].payload + in_data[0].len - HAICRYPT_AUTHTAG_MAX; + int liret = cryspr_cb->cryspr->aes_gcm_cipher(false, aes_key, iv, in_data[0].pfx, ctx->msg_info->pfx_len, in_data[0].payload, in_data[0].len - HAICRYPT_AUTHTAG_MAX, + out_txt, tag); + if (liret) { + return(liret); + } + out_len = in_data[0].len - HAICRYPT_AUTHTAG_MAX; + } + else { +#if CRYSPR_HAS_AESCTR + cryspr_cb->cryspr->aes_ctr_cipher(false, aes_key, iv, in_data[0].payload, in_data[0].len, out_txt); - out_len = in_data[0].len; + out_len = in_data[0].len; #else /*CRYSPR_HAS_AESCTR*/ - /* Get current key (odd|even) from context */ - CRYSPR_AESCTX *aes_key = CRYSPR_GETSEK(cryspr_cb, hcryptCtx_GetKeyIndex(ctx)); - unsigned char iv[CRYSPR_AESBLKSZ]; - int iret = 0; - - /* Get input packet index (in network order) */ - hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1); - - /* - * Compute the Initial Vector - * IV (128-bit): - * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * | 0s | pki | ctr | - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * XOR - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * | nonce + - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * - * pki (32-bit): packet index - * ctr (16-bit): block counter - * nonce (112-bit): number used once (salt) - */ - hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv); - /* Create CtrStream. May be longer than in_len (next cipher block size boundary) */ - iret = _crysprFallback_AES_SetCtrStream(cryspr_cb, ctx, in_data[0].len, iv); - if (iret) { - return(iret); - } - /* Reserve output buffer for cryspr */ - out_txt = _crysprFallback_GetOutbuf(cryspr_cb, 0, cryspr_cb->ctr_stream_len); - - /* Create KeyStream (encrypt CtrStream) */ - iret = cryspr_cb->cryspr->aes_ecb_cipher(true, aes_key, + /* Get input packet index (in network order) */ + hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1); + + /* + * Compute the Initial Vector + * IV (128-bit): + * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | 0s | pki | ctr | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * XOR + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | nonce + + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * pki (32-bit): packet index + * ctr (16-bit): block counter + * nonce (112-bit): number used once (salt) + */ + hcrypt_SetCtrIV((unsigned char*)&pki, ctx->salt, iv); + + /* Create CtrStream. May be longer than in_len (next cipher block size boundary) */ + int liret = _crysprFallback_AES_SetCtrStream(cryspr_cb, ctx, in_data[0].len, iv); + if (liret) { + return(liret); + } + /* Reserve output buffer for cryspr */ + out_txt = _crysprFallback_GetOutbuf(cryspr_cb, 0, cryspr_cb->ctr_stream_len); + + /* Create KeyStream (encrypt CtrStream) */ + liret = cryspr_cb->cryspr->aes_ecb_cipher(true, aes_key, cryspr_cb->ctr_stream, cryspr_cb->ctr_stream_len, out_txt, &out_len); - if (iret) { - HCRYPT_LOG(LOG_ERR, "%s", "crysprNatural_AES_ecb_cipher(encrypt failed\n"); - return(iret); - } + if (liret) { + HCRYPT_LOG(LOG_ERR, "%s", "crysprNatural_AES_ecb_cipher(encrypt failed\n"); + return(liret); + } #endif /*CRYSPR_HAS_AESCTR*/ + } break; } case HCRYPT_CTX_MODE_CLRTXT: @@ -638,6 +704,7 @@ static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, #else /* CRYSPR_HAS_AESCTR */ /* Copy output data back in input buffer */ memcpy(in_data[0].payload, out_txt, out_len); + in_data->len = out_len; #endif /* CRYSPR_HAS_AESCTR */ } else { /* Copy header in output buffer if needed */ @@ -683,9 +750,9 @@ CRYSPR_methods *crysprInit(CRYSPR_methods *cryspr) cryspr->aes_set_key = crysprStub_AES_SetKey; cryspr->aes_ecb_cipher = crysprStub_AES_EcbCipher; cryspr->aes_ctr_cipher = crysprStub_AES_CtrCipher; + cryspr->aes_gcm_cipher = crysprStub_AES_GCMCipher; cryspr->sha1_msg_digest = crysprStub_SHA1_MsgDigest; - /* Crypto Session API */ cryspr->open = crysprFallback_Open; cryspr->close = crysprFallback_Close; @@ -704,5 +771,5 @@ CRYSPR_methods *crysprInit(CRYSPR_methods *cryspr) HaiCrypt_Cryspr HaiCryptCryspr_Get_Instance(void) { - return((HaiCrypt_Cryspr)cryspr4SRT()); + return((HaiCrypt_Cryspr)cryspr4SRT()); } diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr.h b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr.h index d515c3a4be..42426f4223 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/cryspr.h +++ b/trunk/3rdparty/srt-1-fit/haicrypt/cryspr.h @@ -40,32 +40,32 @@ extern "C" { typedef struct tag_CRYSPR_cb { #ifdef CRYSPR2 - CRYSPR_AESCTX *aes_kek; /* Key Encrypting Key (KEK) */ - CRYSPR_AESCTX *aes_sek[2]; /* even/odd Stream Encrypting Key (SEK) */ + CRYSPR_AESCTX *aes_kek; /* Key Encrypting Key (KEK) */ + CRYSPR_AESCTX *aes_sek[2]; /* even/odd Stream Encrypting Key (SEK) */ #define CRYSPR_GETKEK(cb) ((cb)->aes_kek) #define CRYSPR_GETSEK(cb,kk) ((cb)->aes_sek[kk]) #else /*CRYSPR2*/ - CRYSPR_AESCTX aes_kek; /* Key Encrypting Key (KEK) */ - CRYSPR_AESCTX aes_sek[2]; /* even/odd Stream Encrypting Key (SEK) */ + CRYSPR_AESCTX aes_kek; /* Key Encrypting Key (KEK) */ + CRYSPR_AESCTX aes_sek[2]; /* even/odd Stream Encrypting Key (SEK) */ #define CRYSPR_GETKEK(cb) (&((cb)->aes_kek)) #define CRYSPR_GETSEK(cb,kk) (&((cb)->aes_sek[kk])) #endif /*CRYSPR2*/ - struct tag_CRYSPR_methods *cryspr; + struct tag_CRYSPR_methods *cryspr; #if !CRYSPR_HAS_AESCTR /* Reserve room to build the counter stream ourself */ #define HCRYPT_CTR_BLK_SZ CRYSPR_AESBLKSZ #define HCRYPT_CTR_STREAM_SZ 2048 - unsigned char * ctr_stream; - size_t ctr_stream_len; /* Content size */ - size_t ctr_stream_siz; /* Allocated length */ + unsigned char * ctr_stream; + size_t ctr_stream_len; /* Content size */ + size_t ctr_stream_siz; /* Allocated length */ #endif /* !CRYSPR_HAS_AESCTR */ #define CRYSPR_OUTMSGMAX 6 - uint8_t * outbuf; /* output circle buffer */ - size_t outbuf_ofs; /* write offset in circle buffer */ - size_t outbuf_siz; /* circle buffer size */ + uint8_t * outbuf; /* output circle buffer */ + size_t outbuf_ofs; /* write offset in circle buffer */ + size_t outbuf_siz; /* circle buffer size */ } CRYSPR_cb; typedef struct tag_CRYSPR_methods { @@ -100,6 +100,17 @@ typedef struct tag_CRYSPR_methods { size_t inlen, /* src length */ unsigned char *out_txt);/* dest */ + int (*aes_gcm_cipher)( + bool bEncrypt, /* true:encrypt false:decrypt (don't care with CTR) */ + CRYSPR_AESCTX* aes_key, /* ctx */ + unsigned char* iv, /* iv */ + const unsigned char* aad, /* associated data */ + size_t aadlen, + const unsigned char* indata, /* src (clear text) */ + size_t inlen, /* src length */ + unsigned char* out_txt, /* dest */ + unsigned char* out_tag); + unsigned char *(*sha1_msg_digest)( const unsigned char *m, /* in: message */ size_t m_len, /* message length */ diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt.h b/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt.h index 04eb18b767..b6e83ad7a3 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt.h +++ b/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt.h @@ -36,7 +36,7 @@ HaiCrypt_Cryspr HaiCryptCryspr_Get_Instance (void); /* Return a default crys #define HAICRYPT_PWD_MAX_SZ 80 /* MAX password (for Password-based Key Derivation) */ #define HAICRYPT_KEY_MAX_SZ 32 /* MAX key */ #define HAICRYPT_SECRET_MAX_SZ (HAICRYPT_PWD_MAX_SZ > HAICRYPT_KEY_MAX_SZ ? HAICRYPT_PWD_MAX_SZ : HAICRYPT_KEY_MAX_SZ) - +#define HAICRYPT_AUTHTAG_MAX 16 /* maximum length of the auth tag (e.g. GCM) */ #define HAICRYPT_SALT_SZ 16 @@ -60,6 +60,7 @@ typedef struct { #define HAICRYPT_CFG_F_TX 0x01 /* !TX -> RX */ #define HAICRYPT_CFG_F_CRYPTO 0x02 /* Perform crypto Tx:Encrypt Rx:Decrypt */ #define HAICRYPT_CFG_F_FEC 0x04 /* Do Forward Error Correction */ +#define HAICRYPT_CFG_F_GCM 0x08 /* Use AES-GCM */ unsigned flags; HaiCrypt_Secret secret; /* Security Association */ @@ -108,10 +109,15 @@ int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc, unsigned char *pfx, unsigned char *data, size_t data_len); int HaiCrypt_Rx_Data(HaiCrypt_Handle hhc, unsigned char *pfx, unsigned char *data, size_t data_len); +/// @brief Check if the crypto service provider supports AES GCM. +/// @return returns 1 if AES GCM is supported, 0 otherwise. +int HaiCrypt_IsAESGCM_Supported(void); + /* Status values */ #define HAICRYPT_ERROR -1 #define HAICRYPT_ERROR_WRONG_SECRET -2 +#define HAICRYPT_ERROR_CIPHER -3 #define HAICRYPT_OK 0 diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.cpp b/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.cpp index 2d5353da39..7735071962 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.cpp +++ b/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.cpp @@ -44,10 +44,10 @@ int HaiCrypt_SetLogLevel(int level, int logfa) #define HAICRYPT_DEFINE_LOG_DISPATCHER(LOGLEVEL, dispatcher) \ int HaiCrypt_LogF_##LOGLEVEL ( const char* file, int line, const char* function, const char* format, ...) \ { \ - va_list ap; \ - va_start(ap, format); \ srt_logging::LogDispatcher& lg = hclog.dispatcher; \ if (!lg.CheckEnabled()) return -1; \ + va_list ap; \ + va_start(ap, format); \ lg().setloc(file, line, function).vform(format, ap); \ va_end(ap); \ return 0; \ diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.h b/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.h index 9655f722b6..d788316bf8 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.h +++ b/trunk/3rdparty/srt-1-fit/haicrypt/haicrypt_log.h @@ -21,7 +21,7 @@ HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_EMERG); #define HCRYPT_LOG_INIT() #define HCRYPT_LOG_EXIT() -#define HCRYPT_LOG(lvl, fmt, ...) HaiCrypt_LogF_##lvl (__FILE__, __LINE__, __FUNCTION__, fmt, __VA_ARGS__) +#define HCRYPT_LOG(lvl, ...) HaiCrypt_LogF_##lvl (__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) #if ENABLE_HAICRYPT_LOGGING == 2 #define HCRYPT_DEV 1 diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.c b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.c index a94bd9af75..2568654b1e 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.c +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.c @@ -156,7 +156,7 @@ int HaiCrypt_Create(const HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc) || hcryptCtx_Tx_Init(crypto, &crypto->ctx_pair[1], cfg)) { free(crypto); return(-1); - } + } /* Generate keys for first (default) context */ if (hcryptCtx_Tx_Rekey(crypto, &crypto->ctx_pair[0])) { free(crypto); @@ -196,6 +196,9 @@ int HaiCrypt_ExtractConfig(HaiCrypt_Handle hhcSrc, HaiCrypt_Cfg* pcfg) pcfg->flags = HAICRYPT_CFG_F_CRYPTO; if ((ctx->flags & HCRYPT_CTX_F_ENCRYPT) == HCRYPT_CTX_F_ENCRYPT) pcfg->flags |= HAICRYPT_CFG_F_TX; + + if (ctx->mode == HCRYPT_CTX_MODE_AESGCM) + pcfg->flags |= HAICRYPT_CFG_F_GCM; /* Set this explicitly - this use of this library is SRT only. */ pcfg->xport = HAICRYPT_XPT_SRT; @@ -237,7 +240,8 @@ int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handl if (tx) { HaiCrypt_Cfg crypto_config; - HaiCrypt_ExtractConfig(hhcSrc, &crypto_config); + if (-1 == HaiCrypt_ExtractConfig(hhcSrc, &crypto_config)) + return -1; /* * Just invert the direction written in flags and use the @@ -303,8 +307,7 @@ int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handl return(-1); } - - /* Configure contexts */ + /* Configure contexts. Note that GCM mode has been already copied from the source context. */ if (hcryptCtx_Rx_Init(cryptoClone, &cryptoClone->ctx_pair[0], NULL) || hcryptCtx_Rx_Init(cryptoClone, &cryptoClone->ctx_pair[1], NULL)) { free(cryptoClone); @@ -336,3 +339,12 @@ int HaiCrypt_Close(HaiCrypt_Handle hhc) HCRYPT_LOG_EXIT(); return rc; } + +int HaiCrypt_IsAESGCM_Supported(void) +{ +#if CRYSPR_HAS_AESGCM + return 1; +#else + return 0; +#endif +} diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.h b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.h index c4fbf87f91..34e744e8a9 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.h +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt.h @@ -32,10 +32,6 @@ written by #ifdef _WIN32 #include #include - #if defined(_MSC_VER) - #pragma warning(disable:4267) - #pragma warning(disable:4018) - #endif #else #include #endif @@ -163,7 +159,18 @@ int hcryptCtx_Tx_AsmKM(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *a int hcryptCtx_Tx_ManageKM(hcrypt_Session *crypto); int hcryptCtx_Tx_InjectKM(hcrypt_Session *crypto, void *out_p[], size_t out_len_p[], int maxout); +/// @brief Initialize receiving crypto context. +/// @param crypto library instance handle. +/// @param ctx additional crypto context. +/// @param cfg crypto configuration. +/// @return -1 on error, 0 otherwise. int hcryptCtx_Rx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg); + +/// @brief Parse an incoming message related to cryptography module. +/// @param crypto library instance handle. +/// @param msg a message to parse. +/// @param msg_len length of the message in bytes. +/// @return 0 on success; -3 on cipher mode mismatch; -2 on unmatched shared secret; -1 on other failures. int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *msg, size_t msg_len); #endif /* HCRYPT_H */ diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx.h b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx.h index 9c60c0854c..3a46fd40f7 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx.h +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx.h @@ -45,7 +45,7 @@ typedef struct { typedef struct tag_hcrypt_Ctx { struct tag_hcrypt_Ctx * alt; /* Alternative ctx (even/odd) */ -#define HCRYPT_CTX_F_MSG 0x00FF /* Aligned wiht message header flags */ +#define HCRYPT_CTX_F_MSG 0x00FF /* Aligned with message header flags */ #define HCRYPT_CTX_F_eSEK HCRYPT_MSG_F_eSEK #define HCRYPT_CTX_F_oSEK HCRYPT_MSG_F_oSEK #define HCRYPT_CTX_F_xSEK HCRYPT_MSG_F_xSEK @@ -68,6 +68,7 @@ typedef struct tag_hcrypt_Ctx { #define HCRYPT_CTX_MODE_AESECB 1 /* Electronic Code Book mode */ #define HCRYPT_CTX_MODE_AESCTR 2 /* Counter mode */ #define HCRYPT_CTX_MODE_AESCBC 3 /* Cipher-block chaining mode */ +#define HCRYPT_CTX_MODE_AESGCM 4 /* AES GCM authenticated encryption */ unsigned mode; struct { diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_rx.c b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_rx.c index 9bd8a8e639..9fcba6c306 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_rx.c +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_rx.c @@ -14,9 +14,9 @@ written by Haivision Systems Inc. 2011-06-23 (jdube) - HaiCrypt initial implementation. + HaiCrypt initial implementation. 2014-03-11 (jdube) - Adaptation for SRT. + Adaptation for SRT. *****************************************************************************/ #include /* memcpy */ @@ -24,16 +24,18 @@ written by int hcryptCtx_Rx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg) { - ctx->mode = HCRYPT_CTX_MODE_AESCTR; - ctx->status = HCRYPT_CTX_S_INIT; + if (cfg) { + ctx->mode = (cfg->flags & HAICRYPT_CFG_F_GCM) ? HCRYPT_CTX_MODE_AESGCM : HCRYPT_CTX_MODE_AESCTR; + } + ctx->status = HCRYPT_CTX_S_INIT; - ctx->msg_info = crypto->msg_info; + ctx->msg_info = crypto->msg_info; - if (cfg && hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) { - return(-1); - } - ctx->status = HCRYPT_CTX_S_SARDY; - return(0); + if (cfg && hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) { + return(-1); + } + ctx->status = HCRYPT_CTX_S_SARDY; + return(0); } int hcryptCtx_Rx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *sek, size_t sek_len) @@ -98,9 +100,31 @@ int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *km_msg, size_t m } /* Check options support */ - if ((HCRYPT_CIPHER_AES_CTR != km_msg[HCRYPT_MSG_KM_OFS_CIPHER]) - || (HCRYPT_AUTH_NONE != km_msg[HCRYPT_MSG_KM_OFS_AUTH])) { - HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported option\n"); + if (HCRYPT_CIPHER_AES_CTR != km_msg[HCRYPT_MSG_KM_OFS_CIPHER] + && HCRYPT_CIPHER_AES_GCM != km_msg[HCRYPT_MSG_KM_OFS_CIPHER]) + { + HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported cipher\n"); + return(-1); + } + +#if !CRYSPR_HAS_AESGCM + /* Only OpenSSL EVP crypto provider allows the use of GCM.Add this condition. Reject if GCM is not supported by the CRYSPR. */ + if (HCRYPT_CIPHER_AES_GCM == km_msg[HCRYPT_MSG_KM_OFS_CIPHER]) + { + HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported GCM cipher\n"); + return(-1); + } +#endif + + if (HCRYPT_CIPHER_AES_GCM == km_msg[HCRYPT_MSG_KM_OFS_CIPHER] + && HCRYPT_AUTH_AES_GCM != km_msg[HCRYPT_MSG_KM_OFS_AUTH]) { + HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg GCM auth method was expected.\n"); + return(-1); + } + + if (HCRYPT_CIPHER_AES_CTR == km_msg[HCRYPT_MSG_KM_OFS_CIPHER] + && HCRYPT_AUTH_NONE != km_msg[HCRYPT_MSG_KM_OFS_AUTH]) { + HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported auth method\n"); return(-1); } @@ -144,6 +168,13 @@ int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *km_msg, size_t m do_pbkdf = 1; /* Impact on password derived kek */ } + /* Check cipher mode */ + if (ctx->mode != km_msg[HCRYPT_MSG_KM_OFS_CIPHER]) + { + HCRYPT_LOG(LOG_WARNING, "%s", "cipher mode mismatch\n"); + return(-3); + } + /* * Regenerate KEK if it is password derived * and Salt or SEK length changed @@ -159,7 +190,7 @@ int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *km_msg, size_t m /* Unwrap SEK(s) and set in context */ if (0 > crypto->cryspr->km_unwrap(crypto->cryspr_cb, seks, &km_msg[HCRYPT_MSG_KM_OFS_SALT + salt_len], - (sek_cnt * sek_len) + HAICRYPT_WRAPKEY_SIGN_SZ)) { + (unsigned int)((sek_cnt * sek_len) + HAICRYPT_WRAPKEY_SIGN_SZ))) { HCRYPT_LOG(LOG_WARNING, "%s", "unwrap key failed\n"); return(-2); //Report unmatched shared secret } @@ -184,7 +215,6 @@ int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *km_msg, size_t m alt->salt_len = salt_len; if (kek_len) { /* New or changed KEK */ -// memcpy(&alt->aes_kek, &ctx->aes_kek, sizeof(alt->aes_kek)); alt->status = HCRYPT_CTX_S_SARDY; } diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_tx.c b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_tx.c index e518f51386..9ed5ba36c2 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_tx.c +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_ctx_tx.c @@ -14,18 +14,18 @@ written by Haivision Systems Inc. 2011-06-23 (jdube) - HaiCrypt initial implementation. + HaiCrypt initial implementation. 2014-03-11 (jdube) - Adaptation for SRT. + Adaptation for SRT. *****************************************************************************/ #include /* memcpy */ #ifdef _WIN32 - #include - #include - #include + #include + #include + #include #else - #include + #include #endif #include "hcrypt.h" @@ -33,7 +33,7 @@ int hcryptCtx_Tx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cf { ctx->cfg.key_len = cfg->key_len; - ctx->mode = HCRYPT_CTX_MODE_AESCTR; + ctx->mode = (cfg->flags & HAICRYPT_CFG_F_GCM) ? HCRYPT_CTX_MODE_AESGCM : HCRYPT_CTX_MODE_AESCTR; ctx->status = HCRYPT_CTX_S_INIT; ctx->msg_info = crypto->msg_info; @@ -52,14 +52,14 @@ int hcryptCtx_Tx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx) /* Generate Salt */ ctx->salt_len = HAICRYPT_SALT_SZ; - if (0 > (iret = crypto->cryspr->prng(ctx->salt, ctx->salt_len))) { + if (0 > (iret = crypto->cryspr->prng(ctx->salt, (int)ctx->salt_len))) { HCRYPT_LOG(LOG_ERR, "PRNG(salt[%zd]) failed\n", ctx->salt_len); return(iret); } /* Generate SEK */ ctx->sek_len = ctx->cfg.key_len; - if (0 > (iret = crypto->cryspr->prng(ctx->sek, ctx->sek_len))) { + if (0 > (iret = crypto->cryspr->prng(ctx->sek, (int)ctx->sek_len))) { HCRYPT_LOG(LOG_ERR, "PRNG(sek[%zd] failed\n", ctx->sek_len); return(iret); } @@ -195,7 +195,7 @@ int hcryptCtx_Tx_Refresh(hcrypt_Session *crypto) HCRYPT_LOG(LOG_DEBUG, "refresh/generate SEK. salt_len=%d sek_len=%d\n", (int)new_ctx->salt_len, (int)new_ctx->sek_len); - if (0 > crypto->cryspr->prng(new_ctx->sek, new_ctx->sek_len)) { + if (0 > crypto->cryspr->prng(new_ctx->sek, (int)new_ctx->sek_len)) { HCRYPT_LOG(LOG_ERR, "PRNG(sek[%zd] failed\n", new_ctx->sek_len); return(-1); } @@ -299,9 +299,9 @@ int hcryptCtx_Tx_AsmKM(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *a 2 == sek_cnt ? HCRYPT_MSG_F_xSEK : (ctx->flags & HCRYPT_MSG_F_xSEK)); /* crypto->KMmsg_cache[4..7]: KEKI=0 */ - km_msg[HCRYPT_MSG_KM_OFS_CIPHER] = HCRYPT_CIPHER_AES_CTR; - km_msg[HCRYPT_MSG_KM_OFS_AUTH] = HCRYPT_AUTH_NONE; - km_msg[HCRYPT_MSG_KM_OFS_SE] = crypto->se; + km_msg[HCRYPT_MSG_KM_OFS_CIPHER] = (ctx->mode == HCRYPT_CTX_MODE_AESGCM) ? HCRYPT_CIPHER_AES_GCM : HCRYPT_CIPHER_AES_CTR; + km_msg[HCRYPT_MSG_KM_OFS_AUTH] = (ctx->mode == HCRYPT_CTX_MODE_AESGCM) ? HCRYPT_AUTH_AES_GCM : HCRYPT_AUTH_NONE; + km_msg[HCRYPT_MSG_KM_OFS_SE] = (char) crypto->se; hcryptMsg_KM_SetSaltLen(km_msg, ctx->salt_len); hcryptMsg_KM_SetSekLen(km_msg, ctx->sek_len); @@ -322,7 +322,7 @@ int hcryptCtx_Tx_AsmKM(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *a } if (0 > crypto->cryspr->km_wrap(crypto->cryspr_cb, &km_msg[HCRYPT_MSG_KM_OFS_SALT + ctx->salt_len], - seks, sek_cnt * ctx->sek_len)) { + seks, (unsigned int)(sek_cnt * ctx->sek_len))) { HCRYPT_LOG(LOG_ERR, "%s", "wrap key failed\n"); return(-1); @@ -360,7 +360,7 @@ int hcryptCtx_Tx_ManageKM(hcrypt_Session *crypto) * prepare next SEK for announcement */ hcryptCtx_Tx_Refresh(crypto); - + HCRYPT_LOG(LOG_INFO, "KM[%d] Pre-announced\n", (ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2); diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_msg.h b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_msg.h index a915c2fa02..33a9522f99 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_msg.h +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_msg.h @@ -122,8 +122,10 @@ typedef struct { #define HCRYPT_CIPHER_AES_ECB 1 #define HCRYPT_CIPHER_AES_CTR 2 #define HCRYPT_CIPHER_AES_CBC 3 +#define HCRYPT_CIPHER_AES_GCM 4 #define HCRYPT_AUTH_NONE 0 +#define HCRYPT_AUTH_AES_GCM 1 #define HCRYPT_SE_TSUDP 1 hcrypt_MsgInfo * hcryptMsg_STA_MsgInfo(void); @@ -148,8 +150,8 @@ typedef struct { #define hcryptMsg_KM_GetSaltLen(msg) (size_t)((msg)[HCRYPT_MSG_KM_OFS_SLEN] * 4) #define hcryptMsg_KM_GetSekLen(msg) (size_t)((msg)[HCRYPT_MSG_KM_OFS_KLEN] * 4) -#define hcryptMsg_KM_SetSaltLen(msg,len)do {(msg)[HCRYPT_MSG_KM_OFS_SLEN] = (len)/4;} while(0) -#define hcryptMsg_KM_SetSekLen(msg,len) do {(msg)[HCRYPT_MSG_KM_OFS_KLEN] = (len)/4;} while(0) +#define hcryptMsg_KM_SetSaltLen(msg,len)do {(msg)[HCRYPT_MSG_KM_OFS_SLEN] = (unsigned char)(len)/4;} while(0) +#define hcryptMsg_KM_SetSekLen(msg,len) do {(msg)[HCRYPT_MSG_KM_OFS_KLEN] = (unsigned char)(len)/4;} while(0) #endif /* HCRYPT_MSG_H */ diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_rx.c b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_rx.c index e77c73b355..68cb396f83 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_rx.c +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_rx.c @@ -53,7 +53,7 @@ int HaiCrypt_Rx_Data(HaiCrypt_Handle hhc, if (0 > (nb = crypto->cryspr->ms_decrypt(crypto->cryspr_cb, ctx, &indata, 1, NULL, NULL, NULL))) { HCRYPT_LOG(LOG_ERR, "%s", "ms_decrypt failed\n"); } else { - nb = indata.len; + nb = (int)indata.len; } } else { /* No key received yet */ nb = 0; @@ -124,9 +124,6 @@ int HaiCrypt_Rx_Process(HaiCrypt_Handle hhc, || (0 != memcmp(ctx->KMmsg_cache, in_msg, in_len))) { /* or different */ nbout = hcryptCtx_Rx_ParseKM(crypto, in_msg, in_len); - //-2: unmatched shared secret - //-1: other failures - //0: success } else { nbout = 0; } diff --git a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_tx.c b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_tx.c index 85fc798cbe..3472c625b4 100644 --- a/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_tx.c +++ b/trunk/3rdparty/srt-1-fit/haicrypt/hcrypt_tx.c @@ -14,20 +14,20 @@ written by Haivision Systems Inc. 2011-06-23 (jdube) - HaiCrypt initial implementation. + HaiCrypt initial implementation. 2014-03-11 (jdube) - Adaptation for SRT. + Adaptation for SRT. *****************************************************************************/ #include #include /* NULL */ #include /* memcpy */ #ifdef _WIN32 - #include - #include - #include + #include + #include + #include #else - #include /* htonl */ + #include /* htonl */ #endif #include "hcrypt.h" @@ -52,29 +52,28 @@ int HaiCrypt_Tx_GetBuf(HaiCrypt_Handle hhc, size_t data_len, unsigned char **in_ return(crypto->msg_info->pfx_len); } -int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[], int maxout) +int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[], int maxout) { hcrypt_Session *crypto = (hcrypt_Session *)hhc; - hcrypt_Ctx *ctx = crypto->ctx; int nbout = 0; if ((NULL == crypto) - || (NULL == ctx) + || (NULL == crypto->ctx) || (NULL == out_p) || (NULL == out_len_p)) { - HCRYPT_LOG(LOG_ERR, "ManageKeys: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx); + HCRYPT_LOG(LOG_ERR, "ManageKeys: invalid params: crypto=%p out_p=%p out_len_p=%p\n", + crypto, out_p, out_len_p); return(-1); } /* Manage Key Material (refresh, announce, decommission) */ hcryptCtx_Tx_ManageKM(crypto); - ctx = crypto->ctx; - if (NULL == ctx) { - HCRYPT_LOG(LOG_ERR, "%s", "crypto context not defined\n"); + if (NULL == crypto->ctx) { + HCRYPT_LOG(LOG_ERR, "%s", "crypto context NULL after ManageKM call\n"); return(-1); } - ASSERT(ctx->status == HCRYPT_CTX_S_ACTIVE); + ASSERT(crypto->ctx->status == HCRYPT_CTX_S_ACTIVE); nbout = hcryptCtx_Tx_InjectKM(crypto, out_p, out_len_p, maxout); return(nbout); @@ -83,30 +82,35 @@ int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[ int HaiCrypt_Tx_GetKeyFlags(HaiCrypt_Handle hhc) { hcrypt_Session *crypto = (hcrypt_Session *)hhc; - hcrypt_Ctx *ctx = crypto->ctx; + hcrypt_Ctx *ctx = NULL; if ((NULL == crypto) - || (NULL == ctx)){ + || (NULL == (ctx = crypto->ctx))) { HCRYPT_LOG(LOG_ERR, "GetKeyFlags: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx); return(-1); } - return(hcryptCtx_GetKeyFlags(ctx)); + return(hcryptCtx_GetKeyFlags(crypto->ctx)); } -int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc, - unsigned char *in_pfx, unsigned char *in_data, size_t in_len) +int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc, + unsigned char *in_pfx, unsigned char *in_data, size_t in_len) { hcrypt_Session *crypto = (hcrypt_Session *)hhc; - hcrypt_Ctx *ctx = crypto->ctx; + hcrypt_Ctx *ctx = NULL; int nbout = 0; if ((NULL == crypto) - || (NULL == ctx)){ + || (NULL == (ctx = crypto->ctx))) { HCRYPT_LOG(LOG_ERR, "Tx_Data: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx); return(-1); } /* Get/Set packet index */ - ctx->msg_info->indexMsg(in_pfx, ctx->MSpfx_cache); + ctx->msg_info->indexMsg(in_pfx, ctx->MSpfx_cache); + + if (hcryptMsg_GetKeyIndex(ctx->msg_info, in_pfx) != hcryptCtx_GetKeyIndex(ctx)) + { + HCRYPT_LOG(LOG_ERR, "Tx_Data: Key mismatch!"); + } /* Encrypt */ { @@ -125,16 +129,16 @@ int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc, return(nbout); } -int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc, - unsigned char *in_msg, size_t in_len, +int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc, + unsigned char *in_msg, size_t in_len, void *out_p[], size_t out_len_p[], int maxout) { hcrypt_Session *crypto = (hcrypt_Session *)hhc; - hcrypt_Ctx *ctx = crypto->ctx; + hcrypt_Ctx *ctx = NULL; int nb, nbout = 0; if ((NULL == crypto) - || (NULL == ctx) + || (NULL == (ctx = crypto->ctx)) || (NULL == out_p) || (NULL == out_len_p)) { HCRYPT_LOG(LOG_ERR, "Tx_Process: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx); @@ -144,11 +148,6 @@ int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc, /* Manage Key Material (refresh, announce, decommission) */ hcryptCtx_Tx_ManageKM(crypto); - ctx = crypto->ctx; - if (NULL == ctx) { - HCRYPT_LOG(LOG_ERR, "%s", "crypto context not defined\n"); - return(-1); - } ASSERT(ctx->status == HCRYPT_CTX_S_ACTIVE); nbout += hcryptCtx_Tx_InjectKM(crypto, out_p, out_len_p, maxout); diff --git a/trunk/3rdparty/srt-1-fit/scripts/CheckCXXStdPutTime.cmake b/trunk/3rdparty/srt-1-fit/scripts/CheckCXXStdPutTime.cmake new file mode 100644 index 0000000000..7f15ae09f8 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/CheckCXXStdPutTime.cmake @@ -0,0 +1,57 @@ +# +# SRT - Secure, Reliable, Transport Copyright (c) 2022 Haivision Systems Inc. +# +# This Source Code Form is subject to the terms of the Mozilla Public License, +# v. 2.0. If a copy of the MPL was not distributed with this file, You can +# obtain one at http://mozilla.org/MPL/2.0/. +# + +# Check for C++11 std::put_time(). +# +# Sets: +# HAVE_CXX_STD_PUT_TIME + +include(CheckCSourceCompiles) + +function(CheckCXXStdPutTime) + + unset(HAVE_CXX_STD_PUT_TIME CACHE) + + set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) # CMake 3.6 + + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) + + set(CheckCXXStdPutTime_CODE + " + #include + #include + #include + int main(void) + { + const int result = 0; + std::time_t t = std::time(nullptr); + std::tm tm = *std::localtime(&t); + std::cout + << std::put_time(&tm, \"%FT%T\") + << std::setfill('0') + << std::setw(6) + << std::endl; + return result; + } + " + ) + + # NOTE: Should we set -std or use the current compiler configuration. + # It seems that the top level build does not track the compiler + # in a consistent manner. So Maybe we need this? + set(CMAKE_REQUIRED_FLAGS "-std=c++11") + + # Check that the compiler can build the std::put_time() example: + message(STATUS "Checking for C++ 'std::put_time()':") + check_cxx_source_compiles( + "${CheckCXXStdPutTime_CODE}" + HAVE_CXX_STD_PUT_TIME) + +endfunction(CheckCXXStdPutTime) diff --git a/trunk/3rdparty/srt-1-fit/scripts/CheckGCCAtomicIntrinsics.cmake b/trunk/3rdparty/srt-1-fit/scripts/CheckGCCAtomicIntrinsics.cmake index f47b14d61b..7952dd750c 100644 --- a/trunk/3rdparty/srt-1-fit/scripts/CheckGCCAtomicIntrinsics.cmake +++ b/trunk/3rdparty/srt-1-fit/scripts/CheckGCCAtomicIntrinsics.cmake @@ -36,7 +36,7 @@ function(CheckGCCAtomicIntrinsics) unset(CMAKE_REQUIRED_LIBRARIES) unset(CMAKE_REQUIRED_LINK_OPTIONS) - # Check for existance of libatomic and whether this symbol is present. + # Check for existence of libatomic and whether this symbol is present. check_library_exists(atomic __atomic_fetch_add_8 "" HAVE_LIBATOMIC) set(CheckLibAtomicCompiles_CODE diff --git a/trunk/3rdparty/srt-1-fit/scripts/ShowProjectConfig.cmake b/trunk/3rdparty/srt-1-fit/scripts/ShowProjectConfig.cmake index 9cb959294b..108dde5dbd 100644 --- a/trunk/3rdparty/srt-1-fit/scripts/ShowProjectConfig.cmake +++ b/trunk/3rdparty/srt-1-fit/scripts/ShowProjectConfig.cmake @@ -141,6 +141,7 @@ function(ShowProjectConfig) " HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC: ${HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC}\n" " HAVE_CXX_ATOMIC: ${HAVE_CXX_ATOMIC}\n" " HAVE_CXX_ATOMIC_STATIC: ${HAVE_CXX_ATOMIC_STATIC}\n" + " HAVE_CXX_STD_PUT_TIME: ${HAVE_CXX_STD_PUT_TIME}\n" " Project Configuration:\n" " ENABLE_DEBUG: ${ENABLE_DEBUG}\n" " ENABLE_CXX11: ${ENABLE_CXX11}\n" diff --git a/trunk/3rdparty/srt-1-fit/scripts/build-android/README.md b/trunk/3rdparty/srt-1-fit/scripts/build-android/README.md new file mode 100644 index 0000000000..85277f2a65 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/build-android/README.md @@ -0,0 +1,3 @@ +## Scripts for building SRT for Android + +See [Building SRT for Android](../../docs/build/build-android.md) for the instructions. diff --git a/trunk/3rdparty/srt-1-fit/scripts/build-android/build-android b/trunk/3rdparty/srt-1-fit/scripts/build-android/build-android new file mode 100755 index 0000000000..b1c7363a98 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/build-android/build-android @@ -0,0 +1,111 @@ +#!/bin/sh + +echo_help() +{ + echo "Usage: $0 [options...]" + echo " -n NDK root path for the build" + echo " -a Target API level" + echo " -t Space-separated list of target architectures" + echo " Android supports the following architectures: armeabi-v7a arm64-v8a x86 x86_64" + echo " -e Encryption library to be used. Possible options: openssl (default) mbedtls" + echo " -o OpenSSL version. E.g. 1.1.1l" + echo " -m Mbed TLS version. E.g. v2.26.0" + echo + echo "Example: ./build-android -n /home/username/Android/Sdk/ndk/23.0.7599858 -a 28 -t \"arm64-v8a x86_64\"" + echo +} + +# Init optional command line vars +NDK_ROOT="" +API_LEVEL=28 +BUILD_TARGETS="armeabi-v7a arm64-v8a x86 x86_64" +OPENSSL_VERSION=1.1.1l +ENC_LIB=openssl +MBEDTLS_VERSION=v2.26.0 + +while getopts n:a:t:o:s:e:m: option +do + case "${option}" + in + n) NDK_ROOT=${OPTARG};; + a) API_LEVEL=${OPTARG};; + t) BUILD_TARGETS=${OPTARG};; + o) OPENSSL_VERSION=${OPTARG};; + s) SRT_VERSION=${OPTARG};; + e) ENC_LIB=${OPTARG};; + m) MBEDTLS_VERSION=${OPTARG};; + *) twentytwo=${OPTARG};; + esac +done + +echo_help + +if [ -z "$NDK_ROOT" ] ; then + echo "NDK directory not set." + exit 128 +else + if [ ! -d "$NDK_ROOT" ]; then + echo "NDK directory does not exist: $NDK_ROOT" + exit 128 + fi +fi + +SCRIPT_DIR=$(pwd) +HOST_TAG='unknown' +unamestr=$(uname -s) +if [ "$unamestr" = 'Linux' ]; then + HOST_TAG='linux-x86_64' +elif [ "$unamestr" = 'Darwin' ]; then + if [ $(uname -p) = 'arm' ]; then + echo "NDK does not currently support ARM64" + exit 128 + else + HOST_TAG='darwin-x86_64' + fi +fi + +# Write files relative to current location +BASE_DIR=$(pwd) +case "${BASE_DIR}" in + *\ * ) + echo "Your path contains whitespaces, which is not supported by 'make install'." + exit 128 + ;; +esac +cd "${BASE_DIR}" + +if [ $ENC_LIB = 'openssl' ]; then + echo "Building OpenSSL $OPENSSL_VERSION" + $SCRIPT_DIR/mkssl -n $NDK_ROOT -a $API_LEVEL -t "$BUILD_TARGETS" -o $OPENSSL_VERSION -d $BASE_DIR -h $HOST_TAG +elif [ $ENC_LIB = 'mbedtls' ]; then + if [ ! -d $BASE_DIR/mbedtls ]; then + git clone https://github.com/ARMmbed/mbedtls mbedtls + if [ ! -z "$MBEDTLS_VERSION" ]; then + git -C $BASE_DIR/mbedtls checkout $MBEDTLS_VERSION + fi + fi +else + echo "Unknown encryption library. Possible options: openssl mbedtls" + exit 128 +fi + +# Build working copy of srt repository +REPO_DIR="../.." + +for build_target in $BUILD_TARGETS; do + LIB_DIR=$BASE_DIR/$build_target/lib + JNI_DIR=$BASE_DIR/prebuilt/$build_target + + mkdir -p $JNI_DIR + + if [ $ENC_LIB = 'mbedtls' ]; then + $SCRIPT_DIR/mkmbedtls -n $NDK_ROOT -a $API_LEVEL -t $build_target -s $BASE_DIR/mbedtls -i $BASE_DIR/$build_target + cp $LIB_DIR/libmbedcrypto.so $JNI_DIR/libmbedcrypto.so + cp $LIB_DIR/libmbedtls.so $JNI_DIR/libmbedtls.so + cp $LIB_DIR/libmbedx509.so $JNI_DIR/libmbedx509.so + fi + + git -C $REPO_DIR clean -fd -e scripts + $SCRIPT_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -e $ENC_LIB -s $REPO_DIR -i $BASE_DIR/$build_target + cp $LIB_DIR/libsrt.so $JNI_DIR/libsrt.so +done diff --git a/trunk/3rdparty/srt-1-fit/scripts/build-android/mkmbedtls b/trunk/3rdparty/srt-1-fit/scripts/build-android/mkmbedtls new file mode 100755 index 0000000000..176154184c --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/build-android/mkmbedtls @@ -0,0 +1,27 @@ +#!/bin/sh + +while getopts s:i:t:n:a: option +do + case "${option}" + in + s) SRC_DIR=${OPTARG};; + i) INSTALL_DIR=${OPTARG};; + t) ARCH_ABI=${OPTARG};; + n) NDK_ROOT=${OPTARG};; + a) API_LEVEL=${OPTARG};; + *) twentytwo=${OPTARG};; + esac +done + + +BUILD_DIR=/tmp/mbedtls_android_build +rm -rf $BUILD_DIR +mkdir $BUILD_DIR +cd $BUILD_DIR +cmake -DENABLE_TESTING=Off -DUSE_SHARED_MBEDTLS_LIBRARY=On \ +-DCMAKE_PREFIX_PATH=$INSTALL_DIR -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR -DCMAKE_ANDROID_NDK=$NDK_ROOT \ +-DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=$API_LEVEL -DCMAKE_ANDROID_ARCH_ABI=$ARCH_ABI \ +-DCMAKE_C_FLAGS="-fPIC" -DCMAKE_SHARED_LINKER_FLAGS="-Wl,--build-id" \ +-DCMAKE_BUILD_TYPE=RelWithDebInfo $SRC_DIR +cmake --build . +cmake --install . diff --git a/trunk/3rdparty/srt-1-fit/scripts/build-android/mksrt b/trunk/3rdparty/srt-1-fit/scripts/build-android/mksrt new file mode 100755 index 0000000000..a36900768b --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/build-android/mksrt @@ -0,0 +1,32 @@ +#!/bin/sh + +while getopts s:i:t:n:a:e: option +do + case "${option}" + in + s) SRC_DIR=${OPTARG};; + i) INSTALL_DIR=${OPTARG};; + t) ARCH_ABI=${OPTARG};; + n) NDK_ROOT=${OPTARG};; + a) API_LEVEL=${OPTARG};; + e) ENC_LIB=${OPTARG};; + *) twentytwo=${OPTARG};; + esac +done + + +cd $SRC_DIR +./configure --use-enclib=$ENC_LIB \ +--use-openssl-pc=OFF \ +--OPENSSL_INCLUDE_DIR=$INSTALL_DIR/include \ +--OPENSSL_CRYPTO_LIBRARY=$INSTALL_DIR/lib/libcrypto.a --OPENSSL_SSL_LIBRARY=$INSTALL_DIR/lib/libssl.a \ +--STATIC_MBEDTLS=FALSE \ +--MBEDTLS_INCLUDE_DIR=$INSTALL_DIR/include --MBEDTLS_INCLUDE_DIRS=$INSTALL_DIR/include \ +--MBEDTLS_LIBRARIES=$INSTALL_DIR/lib/libmbedtls.so \ +--CMAKE_PREFIX_PATH=$INSTALL_DIR --CMAKE_INSTALL_PREFIX=$INSTALL_DIR --CMAKE_ANDROID_NDK=$NDK_ROOT \ +--CMAKE_SYSTEM_NAME=Android --CMAKE_SYSTEM_VERSION=$API_LEVEL --CMAKE_ANDROID_ARCH_ABI=$ARCH_ABI \ +--CMAKE_C_FLAGS="-fPIC" --CMAKE_SHARED_LINKER_FLAGS="-Wl,--build-id" \ +--enable-c++11 --enable-stdcxx-sync \ +--enable-debug=2 --enable-logging=0 --enable-heavy-logging=0 --enable-apps=0 +make +make install diff --git a/trunk/3rdparty/srt-1-fit/scripts/build-android/mkssl b/trunk/3rdparty/srt-1-fit/scripts/build-android/mkssl new file mode 100755 index 0000000000..22a4421f0b --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/build-android/mkssl @@ -0,0 +1,85 @@ +#!/bin/sh + +while getopts n:o:a:t:d:h: option +do + case "${option}" + in + n) ANDROID_NDK=${OPTARG};; + o) OPENSSL_VERSION=${OPTARG};; + a) API_LEVEL=${OPTARG};; + t) BUILD_TARGETS=${OPTARG};; + d) OUT_DIR=${OPTARG};; + h) HOST_TAG=${OPTARG};; + *) twentytwo=${OPTARG};; + esac +done + + +BUILD_DIR=/tmp/openssl_android_build + +if [ ! -d openssl-${OPENSSL_VERSION} ] +then + if [ ! -f openssl-${OPENSSL_VERSION}.tar.gz ] + then + wget https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz || exit 128 + fi + tar xzf openssl-${OPENSSL_VERSION}.tar.gz || exit 128 +fi + +cd openssl-${OPENSSL_VERSION} || exit 128 + + +##### export ndk directory. Required by openssl-build-scripts ##### +case ${OPENSSL_VERSION} in + 1.1.1*) + export ANDROID_NDK_HOME=$ANDROID_NDK + ;; + *) + export ANDROID_NDK_ROOT=$ANDROID_NDK + ;; +esac + +export PATH=$ANDROID_NDK/toolchains/llvm/prebuilt/$HOST_TAG/bin:$PATH + +##### build-function ##### +build_the_thing() { + make clean + ./Configure $SSL_TARGET -D__ANDROID_API__=$API_LEVEL && \ + make SHLIB_EXT=.so && \ + make install SHLIB_EXT=.so DESTDIR=$DESTDIR || exit 128 +} + +##### set variables according to build-tagret ##### +for build_target in $BUILD_TARGETS +do + case $build_target in + armeabi-v7a) + DESTDIR="$BUILD_DIR/armeabi-v7a" + SSL_TARGET="android-arm" + ;; + x86) + DESTDIR="$BUILD_DIR/x86" + SSL_TARGET="android-x86" + ;; + x86_64) + DESTDIR="$BUILD_DIR/x86_64" + SSL_TARGET="android-x86_64" + ;; + arm64-v8a) + DESTDIR="$BUILD_DIR/arm64-v8a" + SSL_TARGET="android-arm64" + ;; + esac + + rm -rf $DESTDIR + build_the_thing +#### copy libraries and includes to output-directory ##### + mkdir -p $OUT_DIR/$build_target/include + cp -R $DESTDIR/usr/local/include/* $OUT_DIR/$build_target/include + cp -R $DESTDIR/usr/local/ssl/* $OUT_DIR/$build_target/ + mkdir -p $OUT_DIR/$build_target/lib + cp -R $DESTDIR/usr/local/lib/*.so $OUT_DIR/$build_target/lib + cp -R $DESTDIR/usr/local/lib/*.a $OUT_DIR/$build_target/lib +done + +echo Success diff --git a/trunk/3rdparty/srt-1-fit/scripts/build-windows.ps1 b/trunk/3rdparty/srt-1-fit/scripts/build-windows.ps1 index cd25da12c2..4db3fe6274 100644 --- a/trunk/3rdparty/srt-1-fit/scripts/build-windows.ps1 +++ b/trunk/3rdparty/srt-1-fit/scripts/build-windows.ps1 @@ -101,7 +101,7 @@ if ( $null -eq (Get-Command "cmake.exe" -ErrorAction SilentlyContinue) ) { # get pthreads from nuget if CXX11 is not enabled if ( $CXX11 -eq "OFF" ) { - # get pthreads (this is legacy, and is only availble in nuget for VS2015 and VS2013) + # get pthreads (this is legacy, and is only available in nuget for VS2015 and VS2013) if ( $VS_VERSION -gt 2015 ) { Write-Output "Pthreads is not recommended for use beyond VS2015 and is not supported by this build script - aborting build" throw diff --git a/trunk/3rdparty/srt-1-fit/scripts/generate-error-types.tcl b/trunk/3rdparty/srt-1-fit/scripts/generate-error-types.tcl index b51d60eb17..e4d29c6233 100755 --- a/trunk/3rdparty/srt-1-fit/scripts/generate-error-types.tcl +++ b/trunk/3rdparty/srt-1-fit/scripts/generate-error-types.tcl @@ -114,7 +114,7 @@ set errortypes { SIDINVAL "Invalid socket ID" ISUNBOUND "Cannot do this operation on an UNBOUND socket" NOLISTEN "Socket is not in listening state" - ISRENDEZVOUS "Listen/accept is not supported in rendezous connection setup" + ISRENDEZVOUS "Listen/accept is not supported in rendezvous connection setup" ISRENDUNBOUND "Cannot call connect on UNBOUND socket in rendezvous connection setup" INVALMSGAPI "Incorrect use of Message API (sendmsg/recvmsg)." INVALBUFFERAPI "Incorrect use of Buffer API (send/recv) or File API (sendfile/recvfile)." diff --git a/trunk/3rdparty/srt-1-fit/scripts/googletest-download.cmake b/trunk/3rdparty/srt-1-fit/scripts/googletest-download.cmake index 55f2c5a55e..7469f4f7b1 100644 --- a/trunk/3rdparty/srt-1-fit/scripts/googletest-download.cmake +++ b/trunk/3rdparty/srt-1-fit/scripts/googletest-download.cmake @@ -11,7 +11,7 @@ ExternalProject_Add( BINARY_DIR "@GOOGLETEST_DOWNLOAD_ROOT@/googletest-build" GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.8.1 + GIT_TAG release-1.10.0 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/trunk/3rdparty/srt-1-fit/scripts/release-notes/README.md b/trunk/3rdparty/srt-1-fit/scripts/release-notes/README.md index 1eba3eeb21..d6c4adbfc8 100644 --- a/trunk/3rdparty/srt-1-fit/scripts/release-notes/README.md +++ b/trunk/3rdparty/srt-1-fit/scripts/release-notes/README.md @@ -4,15 +4,24 @@ Script designed to generate release notes template with main sections, contribut In order to obtain the git log file since the previous release (e.g., v1.4.0), use the following command: +```shell +git log --pretty=format:"%h|%s|%an|%ae" v1.4.0...HEAD > commits.csv ``` -git log --pretty=format:"%h|%s|%an|%ae" v1.4.0...HEAD^ > commits.csv + +Use the produced `commits.csv` file as an input to the script: + +```shell +python scripts/release-notes/generate-release-notes.py commits.csv ``` +The script produces `release-notes.md` as an output. + + ## Requirements * Python 3.6+ To install Python libraries use: -``` +```shell pip install -r requirements.txt ``` diff --git a/trunk/3rdparty/srt-1-fit/scripts/srt-dev.lua b/trunk/3rdparty/srt-1-fit/scripts/srt-dev.lua index 92b5fca1a8..a90ac487d9 100644 --- a/trunk/3rdparty/srt-1-fit/scripts/srt-dev.lua +++ b/trunk/3rdparty/srt-1-fit/scripts/srt-dev.lua @@ -115,7 +115,7 @@ fields.tsbpd_delay = ProtoField.uint16("srt_dev.tsbpd_delay", "TsbPd Delay", bas fields.rcv_tsbpd_delay = ProtoField.uint16("srt_dev.rcv_tsbpd_delay", "Receiver TsbPd Delay", base.DEC) fields.snd_tsbpd_delay = ProtoField.uint16("srt_dev.snd_tsbpd_delay", "Sender TsbPd Delay", base.DEC) --- V adn PT status flag +-- V and PT status flag local V_state_select = { [1] = "Initial version" } @@ -252,7 +252,7 @@ function srt_dev.dissector (tvb, pinfo, tree) offset = offset + 4 if UDT_version == 4 then - -- UDT version is 4, packet is diffrent from UDT version 5 + -- UDT version is 4, packet is different from UDT version 5 -- Handle sock type local sock_type = tvb(offset, 4):uint() if sock_type == 1 then diff --git a/trunk/3rdparty/srt-1-fit/scripts/test_vista.c b/trunk/3rdparty/srt-1-fit/scripts/test_vista.c new file mode 100644 index 0000000000..833a4d373b --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/scripts/test_vista.c @@ -0,0 +1,10 @@ +/* Copyright © 2023 Steve Lhomme */ +/* SPDX-License-Identifier: ISC */ +#include +#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600 /* _WIN32_WINNT_VISTA */ +#error NOPE +#endif +int main(void) +{ + return 0; +} diff --git a/trunk/3rdparty/srt-1-fit/scripts/win-installer/libsrt.nsi b/trunk/3rdparty/srt-1-fit/scripts/win-installer/libsrt.nsi index 4628642cc8..1dffc3fd17 100644 --- a/trunk/3rdparty/srt-1-fit/scripts/win-installer/libsrt.nsi +++ b/trunk/3rdparty/srt-1-fit/scripts/win-installer/libsrt.nsi @@ -125,9 +125,11 @@ Section "Install" ; Header files. CreateDirectory "$INSTDIR\include\srt" SetOutPath "$INSTDIR\include\srt" + File "${RepoDir}\srtcore\access_control.h" File "${RepoDir}\srtcore\logging_api.h" File "${RepoDir}\srtcore\platform_sys.h" File "${RepoDir}\srtcore\srt.h" + File "${RepoDir}\srtcore\udt.h" File "${Build64Dir}\version.h" CreateDirectory "$INSTDIR\include\win" diff --git a/trunk/3rdparty/srt-1-fit/srtcore/access_control.h b/trunk/3rdparty/srt-1-fit/srtcore/access_control.h index 97a1104a83..611e1dad8e 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/access_control.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/access_control.h @@ -41,7 +41,7 @@ written by // CODE NOT IN USE 408: unused: no timeout predicted for listener callback #define SRT_REJX_CONFLICT 1409 // The resource being accessed is already locked for modification. This is in case of m=publish and the specified resource is currently read-only. // CODE NOT IN USE 410: unused: treated as a specific case of 404 -// CODE NOT IN USE 411: unused: no reason to include lenght in the protocol +// CODE NOT IN USE 411: unused: no reason to include length in the protocol // CODE NOT IN USE 412: unused: preconditions not predicted in AC // CODE NOT IN USE 413: unused: AC size is already defined as 512 // CODE NOT IN USE 414: unused: AC size is already defined as 512 diff --git a/trunk/3rdparty/srt-1-fit/srtcore/api.cpp b/trunk/3rdparty/srt-1-fit/srtcore/api.cpp index d575cd9681..7ec8ff5708 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/api.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/api.cpp @@ -326,6 +326,7 @@ SRTSOCKET srt::CUDTUnited::generateSocketID(bool for_group) // We have a rollover on the socket value, so // definitely we haven't made the Columbus mistake yet. m_SocketIDGenerator = MAX_SOCKET_VAL; + sockval = MAX_SOCKET_VAL; } // Check all sockets if any of them has this value. @@ -459,10 +460,8 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) // failure and rollback delete ns; ns = NULL; - } - - if (!ns) throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); + } if (pps) *pps = ns; @@ -563,7 +562,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, } catch (const CUDTException&) { - LOGF(cnlog.Fatal, "newConnection: IPE: all sockets occupied? Last gen=%d", m_SocketIDGenerator); + LOGC(cnlog.Fatal, log << "newConnection: IPE: all sockets occupied? Last gen=" << m_SocketIDGenerator); // generateSocketID throws exception, which can be naturally handled // when the call is derived from the API call, but here it's called // internally in response to receiving a handshake. It must be handled @@ -600,7 +599,8 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, // this call causes sending the SRT Handshake through this socket. // Without this mapping the socket cannot be found and therefore // the SRT Handshake message would fail. - HLOGF(cnlog.Debug, "newConnection: incoming %s, mapping socket %d", peer.str().c_str(), ns->m_SocketID); + HLOGC(cnlog.Debug, log << + "newConnection: incoming " << peer.str() << ", mapping socket " << ns->m_SocketID); { ScopedLock cg(m_GlobControlLock); m_Sockets[ns->m_SocketID] = ns; @@ -619,7 +619,14 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, // bind to the same addr of listening socket ns->core().open(); - updateListenerMux(ns, ls); + if (!updateListenerMux(ns, ls)) + { + // This is highly unlikely if not impossible, but there's + // a theoretical runtime chance of failure so it should be + // handled + ns->core().m_RejectReason = SRT_REJ_IPE; + throw false; // let it jump directly into the omni exception handler + } ns->core().acceptAndRespond(ls->m_SelfAddr, peer, hspkt, (w_hs)); } @@ -647,7 +654,8 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, ScopedLock glock(m_GlobControlLock); try { - HLOGF(cnlog.Debug, "newConnection: mapping peer %d to that socket (%d)\n", ns->m_PeerID, ns->m_SocketID); + HLOGC(cnlog.Debug, log << "newConnection: mapping peer " << ns->m_PeerID + << " to that socket (" << ns->m_SocketID << ")"); m_PeerRec[ns->getPeerSpec()].insert(ns->m_SocketID); } catch (...) @@ -666,7 +674,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, // XXX this might require another check of group type. // For redundancy group, at least, update the status in the group CUDTGroup* g = ns->m_GroupOf; - ScopedLock glock(g->m_GroupLock); + ScopedLock grlock(g->m_GroupLock); if (g->m_bClosing) { error = 1; // "INTERNAL REJECTION" @@ -911,6 +919,15 @@ int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) if (s->m_Status != SRTS_INIT) throw CUDTException(MJ_NOTSUP, MN_NONE, 0); + if (s->core().m_config.iIpV6Only == -1 && name.family() == AF_INET6 && name.isany()) + { + // V6ONLY option must be set explicitly if you want to bind to a wildcard address in IPv6 + HLOGP(smlog.Error, + "bind: when binding to :: (IPv6 wildcard), SRTO_IPV6ONLY option must be set explicitly to 0 or 1"); + + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + s->core().open(); updateMux(s, name); s->m_Status = SRTS_OPENED; @@ -1078,7 +1095,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int SRTSOCKET u = CUDT::INVALID_SOCK; bool accepted = false; - // !!only one conection can be set up each time!! + // !!only one connection can be set up each time!! while (!accepted) { UniqueLock accept_lock(ls->m_AcceptLock); @@ -1179,7 +1196,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen) { // Here both srcname and tarname must be specified - if (!srcname || !tarname || size_t(namelen) < sizeof(sockaddr_in)) + if (!srcname || !tarname || namelen < int(sizeof(sockaddr_in))) { LOGC(aclog.Error, log << "connect(with source): invalid call: srcname=" << srcname << " tarname=" << tarname @@ -1224,6 +1241,12 @@ int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockadd int srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) { + if (!name || namelen < int(sizeof(sockaddr_in))) + { + LOGC(aclog.Error, log << "connect(): invalid call: name=" << name << " namelen=" << namelen); + throw CUDTException(MJ_NOTSUP, MN_INVAL); + } + sockaddr_any target_addr(name, namelen); if (target_addr.len == 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -1458,7 +1481,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i ns->m_GroupMemberData = f; ns->m_GroupOf = &g; f->weight = targets[tii].weight; - LOGC(aclog.Note, log << "srt_connect_group: socket @" << sid << " added to group $" << g.m_GroupID); + HLOGC(aclog.Debug, log << "srt_connect_group: socket @" << sid << " added to group $" << g.m_GroupID); } else { @@ -1474,21 +1497,13 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // XXX This should be reenabled later, this should // be probably still in use to exchange information about - // packets assymetrically lost. But for no other purpose. + // packets asymmetrically lost. But for no other purpose. /* ns->core().m_cbPacketArrival.set(ns->m_pUDT, &CUDT::groupPacketArrival); */ int isn = g.currentSchedSequence(); - // Don't synchronize ISN in case of synch on msgno. Every link - // may send their own payloads independently. - if (g.synconmsgno()) - { - HLOGC(aclog.Debug, log << "groupConnect: NOT synchronizing sequence numbers: will sync on msgno"); - isn = -1; - } - // Set it the groupconnect option, as all in-group sockets should have. ns->core().m_config.iGroupConnect = 1; @@ -1867,6 +1882,8 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i */ try { + // record peer address + s->m_PeerAddr = target_addr; s->core().startConnect(target_addr, forced_isn); } catch (const CUDTException&) // Interceptor, just to change the state. @@ -1946,9 +1963,9 @@ void srt::CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) int srt::CUDTUnited::close(CUDTSocket* s) { - HLOGC(smlog.Debug, log << s->core().CONID() << " CLOSE. Acquiring control lock"); + HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSE. Acquiring control lock"); ScopedLock socket_cg(s->m_ControlLock); - HLOGC(smlog.Debug, log << s->core().CONID() << " CLOSING (removing from listening, closing CUDT)"); + HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSING (removing from listening, closing CUDT)"); const bool synch_close_snd = s->core().m_config.bSynSending; @@ -1971,14 +1988,16 @@ int srt::CUDTUnited::close(CUDTSocket* s) // be unable to bind to this port that the about-to-delete listener // is currently occupying (due to blocked slot in the RcvQueue). - HLOGC(smlog.Debug, log << s->core().CONID() << " CLOSING (removing listener immediately)"); + HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSING (removing listener immediately)"); s->core().notListening(); + s->m_Status = SRTS_CLOSING; // broadcast all "accept" waiting CSync::lock_notify_all(s->m_AcceptCond, s->m_AcceptLock); } else { + s->m_Status = SRTS_CLOSING; // Note: this call may be done on a socket that hasn't finished // sending all packets scheduled for sending, which means, this call // may block INDEFINITELY. As long as it's acceptable to block the @@ -1989,7 +2008,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) // synchronize with garbage collection. HLOGC(smlog.Debug, log << "@" << u << "U::close done. GLOBAL CLOSE: " << s->core().CONID() - << ". Acquiring GLOBAL control lock"); + << "Acquiring GLOBAL control lock"); ScopedLock manager_cg(m_GlobControlLock); // since "s" is located before m_GlobControlLock, locate it again in case // it became invalid @@ -2101,6 +2120,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) ... } */ + CSync::notify_one_relaxed(m_GCStopCond); return 0; } @@ -2597,11 +2617,7 @@ void srt::CUDTUnited::checkBrokenSockets() // NOT WHETHER THEY ARE ALSO READY TO PLAY at the time when // this function is called (isRcvDataReady also checks if the // available data is "ready to play"). -#if ENABLE_NEW_RCVBUFFER && s->core().m_pRcvBuffer->hasAvailablePackets()) -#else - && s->core().m_pRcvBuffer->isRcvDataAvailable()) -#endif { const int bc = s->core().m_iBrokenCounter.load(); if (bc > 0) @@ -2615,7 +2631,7 @@ void srt::CUDTUnited::checkBrokenSockets() #if ENABLE_BONDING if (s->m_GroupOf) { - LOGC(smlog.Note, + HLOGC(smlog.Debug, log << "@" << s->m_SocketID << " IS MEMBER OF $" << s->m_GroupOf->id() << " - REMOVING FROM GROUP"); s->removeFromGroup(true); } @@ -2644,7 +2660,7 @@ void srt::CUDTUnited::checkBrokenSockets() for (sockets_t::iterator j = m_ClosedSockets.begin(); j != m_ClosedSockets.end(); ++j) { - // HLOGF(smlog.Debug, "checking CLOSED socket: %d\n", j->first); + // HLOGC(smlog.Debug, log << "checking CLOSED socket: " << j->first); if (!is_zero(j->second->core().m_tsLingerExpiration)) { // asynchronous close: @@ -2671,7 +2687,7 @@ void srt::CUDTUnited::checkBrokenSockets() log << "checkBrokenSockets: @" << j->second->m_SocketID << " closed " << FormatDuration(closed_ago) << " ago and removed from RcvQ - will remove"); - // HLOGF(smlog.Debug, "will unref socket: %d\n", j->first); + // HLOGC(smlog.Debug, log << "will unref socket: " << j->first); tbr.push_back(j->first); } } @@ -2826,12 +2842,41 @@ uint16_t srt::CUDTUnited::installMuxer(CUDTSocket* w_s, CMultiplexer& fw_sm) return sa.hport(); } +bool srt::CUDTUnited::inet6SettingsCompat(const sockaddr_any& muxaddr, const CSrtMuxerConfig& cfgMuxer, + const sockaddr_any& reqaddr, const CSrtMuxerConfig& cfgSocket) +{ + if (muxaddr.family() != AF_INET6) + return true; // Don't check - the family has been checked already + + if (reqaddr.isany()) + { + if (cfgSocket.iIpV6Only == -1) // Treat as "adaptive" + return true; + + // If set explicitly, then it must be equal to the one of found muxer. + return cfgSocket.iIpV6Only == cfgMuxer.iIpV6Only; + } + + // If binding to the certain IPv6 address, then this setting doesn't matter. + return true; +} + bool srt::CUDTUnited::channelSettingsMatch(const CSrtMuxerConfig& cfgMuxer, const CSrtConfig& cfgSocket) { - return cfgMuxer.bReuseAddr && cfgMuxer == cfgSocket; + if (!cfgMuxer.bReuseAddr) + { + HLOGP(smlog.Debug, "channelSettingsMatch: fail: the multiplexer is not reusable"); + return false; + } + + if (cfgMuxer.isCompatWith(cfgSocket)) + return true; + + HLOGP(smlog.Debug, "channelSettingsMatch: fail: some options have different values"); + return false; } -void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* udpsock /*[[nullable]]*/) +void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& reqaddr, const UDPSOCKET* udpsock /*[[nullable]]*/) { ScopedLock cg(m_GlobControlLock); @@ -2844,9 +2889,23 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U { // If not, we need to see if there exist already a multiplexer bound // to the same endpoint. - const int port = addr.hport(); + const int port = reqaddr.hport(); const CSrtConfig& cfgSocket = s->core().m_config; + // This loop is going to check the attempted binding of + // address:port and socket settings against every existing + // multiplexer. Possible results of the check are: + + // 1. MATCH: identical address - reuse it and quit. + // 2. CONFLICT: report error: the binding partially overlaps + // so it neither can be reused nor is free to bind. + // 3. PASS: different and not overlapping - continue searching. + + // In this function the convention is: + // MATCH: do nothing and proceed with binding reusage, THEN break. + // CONFLICT: throw an exception. + // PASS: use 'continue' to pass to the next element. + bool reuse_attempt = false; for (map::iterator i = m_mMultiplexer.begin(); i != m_mMultiplexer.end(); ++i) { @@ -2862,74 +2921,166 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U } // If this is bound to the wildcard address, it can be reused if: - // - addr is also a wildcard + // - reqaddr is also a wildcard // - channel settings match // Otherwise it's a conflict. - sockaddr_any sa; - m.m_pChannel->getSockAddr((sa)); + sockaddr_any mux_addr; + m.m_pChannel->getSockAddr((mux_addr)); HLOGC(smlog.Debug, - log << "bind: Found existing muxer @" << m.m_iID << " : " << sa.str() << " - check against " - << addr.str()); + log << "bind: Found existing muxer @" << m.m_iID << " : " << mux_addr.str() << " - check against " + << reqaddr.str()); - if (sa.isany()) + if (mux_addr.isany()) { - if (!addr.isany()) + if (mux_addr.family() == AF_INET6) { - LOGC(smlog.Error, - log << "bind: Address: " << addr.str() - << " conflicts with existing wildcard binding: " << sa.str()); - throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + // With IPv6 we need to research two possibilities: + // iIpV6Only == 1 -> This means that it binds only :: wildcard, but not 0.0.0.0 + // iIpV6Only == 0 -> This means that it binds both :: and 0.0.0.0. + // iIpV6Only == -1 -> Hard to say what to do, but treat it as a potential conflict in any doubtful case. + + if (m.m_mcfg.iIpV6Only == 1) + { + // PASS IF: candidate is IPv4, no matter the address + // MATCH IF: candidate is IPv6 with only=1 + // CONFLICT IF: candidate is IPv6 with only != 1 or IPv6 non-wildcard. + + if (reqaddr.family() == AF_INET) + { + HLOGC(smlog.Debug, log << "bind: muxer @" << m.m_iID + << " is :: v6only - requested IPv4 ANY is NOT IN THE WAY. Searching on."); + continue; + } + + // Candidate is AF_INET6 + + if (cfgSocket.iIpV6Only != 1 || !reqaddr.isany()) + { + // CONFLICT: + // 1. attempting to make a wildcard IPv4 + IPv6 + // while the multiplexer for wildcard IPv6 exists. + // 2. If binding to a given address, it conflicts with the wildcard + LOGC(smlog.Error, + log << "bind: Address: " << reqaddr.str() + << " conflicts with existing IPv6 wildcard binding: " << mux_addr.str()); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + + // Otherwise, MATCH. + } + else if (m.m_mcfg.iIpV6Only == 0) + { + // Muxer's address is a wildcard for :: and 0.0.0.0 at once. + // This way only IPv6 wildcard with v6only=0 is a perfect match and everything + // else is a conflict. + + if (reqaddr.family() == AF_INET6 && reqaddr.isany() && cfgSocket.iIpV6Only == 0) + { + // MATCH + } + else + { + // CONFLICT: attempting to make a wildcard IPv4 + IPv6 while + // the multiplexer for wildcard IPv6 exists. + LOGC(smlog.Error, + log << "bind: Address: " << reqaddr.str() << " v6only=" << cfgSocket.iIpV6Only + << " conflicts with existing IPv6 + IPv4 wildcard binding: " << mux_addr.str()); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + } + else // Case -1, by unknown reason. Accept only with -1 setting, others are conflict. + { + if (reqaddr.family() == AF_INET6 && reqaddr.isany() && cfgSocket.iIpV6Only == -1) + { + // MATCH + } + else + { + LOGC(smlog.Error, + log << "bind: Address: " << reqaddr.str() << " v6only=" << cfgSocket.iIpV6Only + << " conflicts with existing IPv6 v6only=unknown wildcard binding: " << mux_addr.str()); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + } } - - // Still, for ANY you need either the same family, or open - // for families. - if (m.m_mcfg.iIpV6Only != -1 && m.m_mcfg.iIpV6Only != cfgSocket.iIpV6Only) + else // muxer is IPv4 wildcard { - LOGC(smlog.Error, - log << "bind: Address: " << addr.str() - << " conflicts with existing IPv6 wildcard binding: " << sa.str()); - throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + // Then only IPv4 wildcard is a match and: + // - IPv6 with only=true is PASS (not a conflict) + // - IPv6 with only=false is CONFLICT + // - IPv6 with only=undefined is CONFLICT + // REASON: we need to make a potential conflict a conflict as there will be + // no bind() call to check if this wouldn't be a conflict in result. If you want + // to have a binding to IPv6 that should avoid conflict with IPv4 wildcard binding, + // then SRTO_IPV6ONLY option must be explicitly set before binding. + // Also: + if (reqaddr.family() == AF_INET) + { + if (reqaddr.isany()) + { + // MATCH + } + else + { + LOGC(smlog.Error, + log << "bind: Address: " << reqaddr.str() + << " conflicts with existing IPv4 wildcard binding: " << mux_addr.str()); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + } + else // AF_INET6 + { + if (cfgSocket.iIpV6Only == 1 || !reqaddr.isany()) + { + // PASS + HLOGC(smlog.Debug, log << "bind: muxer @" << m.m_iID + << " is IPv4 wildcard - requested " << reqaddr.str() << " v6only=" << cfgSocket.iIpV6Only + << " is NOT IN THE WAY. Searching on."); + continue; + } + else + { + LOGC(smlog.Error, + log << "bind: Address: " << reqaddr.str() << " v6only=" << cfgSocket.iIpV6Only + << " conflicts with existing IPv4 wildcard binding: " << mux_addr.str()); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + } } - if ((m.m_mcfg.iIpV6Only == 0 || cfgSocket.iIpV6Only == 0) && m.m_iIPversion != addr.family()) - { - LOGC(smlog.Error, - log << "bind: Address: " << addr.str() << " conflicts with IPv6 wildcard binding: " << sa.str() - << " : family " << (m.m_iIPversion == AF_INET ? "IPv4" : "IPv6") << " vs. " - << (addr.family() == AF_INET ? "IPv4" : "IPv6")); - throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); - } reuse_attempt = true; HLOGC(smlog.Debug, log << "bind: wildcard address - multiplexer reusable"); } - else if (addr.isany() && addr.family() == sa.family()) + // Muxer address is NOT a wildcard, so conflicts only with WILDCARD of the same type + else if (reqaddr.isany() && reqaddr.family() == mux_addr.family()) { LOGC(smlog.Error, - log << "bind: Wildcard address: " << addr.str() - << " conflicts with existting IP binding: " << sa.str()); + log << "bind: Wildcard address: " << reqaddr.str() + << " conflicts with existting IP binding: " << mux_addr.str()); throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); } // If this is bound to a certain address, AND: - else if (sa.equal_address(addr)) + else if (mux_addr.equal_address(reqaddr)) { - // - the address is the same as addr + // - the address is the same as reqaddr reuse_attempt = true; HLOGC(smlog.Debug, log << "bind: same IP address - multiplexer reusable"); } else { HLOGC(smlog.Debug, log << "bind: IP addresses differ - ALLOWED to create a new multiplexer"); + continue; } // Otherwise: - // - the address is different than addr + // - the address is different than reqaddr // - the address can't be reused, but this can go on with new one. // If this is a reusage attempt: if (reuse_attempt) { // - if the channel settings match, it can be reused - if (channelSettingsMatch(m.m_mcfg, cfgSocket)) + if (channelSettingsMatch(m.m_mcfg, cfgSocket) && inet6SettingsCompat(mux_addr, m.m_mcfg, reqaddr, cfgSocket)) { HLOGC(smlog.Debug, log << "bind: reusing multiplexer for port " << port); // reuse the existing multiplexer @@ -2941,7 +3092,7 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U { // - if not, it's a conflict LOGC(smlog.Error, - log << "bind: Address: " << addr.str() << " conflicts with binding: " << sa.str() + log << "bind: Address: " << reqaddr.str() << " conflicts with binding: " << mux_addr.str() << " due to channel settings"); throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); } @@ -2950,13 +3101,15 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U // candidates, proceed with creating a new multiplexer. // Note that a binding to a different IP address is not treated - // as a candidate for either reuseage or conflict. + // as a candidate for either reusage or conflict. + LOGC(smlog.Fatal, log << "SHOULD NOT GET HERE!!!"); + SRT_ASSERT(false); } } // a new multiplexer is needed CMultiplexer m; - configureMuxer((m), s, addr.family()); + configureMuxer((m), s, reqaddr.family()); try { @@ -2965,25 +3118,39 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U if (udpsock) { - // In this case, addr contains the address + // In this case, reqaddr contains the address // that has been extracted already from the // given socket - m.m_pChannel->attach(*udpsock, addr); + m.m_pChannel->attach(*udpsock, reqaddr); } - else if (addr.empty()) + else if (reqaddr.empty()) { // The case of previously used case of a NULL address. // This here is used to pass family only, in this case // just automatically bind to the "0" address to autoselect // everything. - m.m_pChannel->open(addr.family()); + m.m_pChannel->open(reqaddr.family()); } else { // If at least the IP address is specified, then bind to that // address, but still possibly autoselect the outgoing port, if the // port was specified as 0. - m.m_pChannel->open(addr); + m.m_pChannel->open(reqaddr); + } + + // AFTER OPENING, check the matter of IPV6_V6ONLY option, + // as it decides about the fact that the occupied binding address + // in case of wildcard is both :: and 0.0.0.0, or only ::. + if (reqaddr.family() == AF_INET6 && m.m_mcfg.iIpV6Only == -1) + { + // XXX We don't know how probable it is to get the error here + // and resulting -1 value. As a fallback for that case, the value -1 + // is honored here, just all side-bindings for other sockes will be + // rejected as a potential conflict, even if binding would be accepted + // in these circumstances. Only a perfect match in case of potential + // overlapping will be accepted on the same port. + m.m_mcfg.iIpV6Only = m.m_pChannel->sockopt(IPPROTO_IPV6, IPV6_V6ONLY, -1); } m.m_pTimer = new CTimer; @@ -3032,8 +3199,14 @@ bool srt::CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) CMultiplexer* mux = map_getp(m_mMultiplexer, ls->m_iMuxID); // NOTE: - // THIS BELOW CODE is only for a highly unlikely, and probably buggy, - // situation when the Multiplexer wasn't found by ID recorded in the listener. + // THIS BELOW CODE is only for a highly unlikely situation when the listener + // socket has been closed in the meantime when the accepted socket is being + // processed. This procedure is different than updateMux because this time we + // only want to have a multiplexer socket to be assigned to the accepted socket. + // It is also unlikely that the listener socket is garbage-collected so fast, so + // this procedure will most likely find the multiplexer of the zombie listener socket, + // which no longer accepts new connections (the listener is withdrawn immediately from + // the port) that wasn't yet completely deleted. CMultiplexer* fallback = NULL; if (!mux) { @@ -3060,8 +3233,9 @@ bool srt::CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) mux = &m; // best match break; } - else + else if (m.m_iIPversion == AF_INET6) { + // Allowed fallback case when we only need an accepted socket. fallback = &m; } } @@ -4369,7 +4543,7 @@ int epoll_wait2(int eid, int* lwnum) { // This API is an alternative format for epoll_wait, created for - // compatability with other languages. Users need to pass in an array + // compatibility with other languages. Users need to pass in an array // for holding the returned sockets, with the maximum array length // stored in *rnum, etc., which will be updated with returned number // of sockets. diff --git a/trunk/3rdparty/srt-1-fit/srtcore/api.h b/trunk/3rdparty/srt-1-fit/srtcore/api.h index ca812bf9e6..9ba77d23a8 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/api.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/api.h @@ -193,6 +193,15 @@ class CUDTSocket /// to finish sending the data that were scheduled for sending so far. void setClosed(); + // This is necessary to be called from the group before the group clears + // the connection with the socket. As for managed groups (and there are + // currently no other group types), a socket disconnected from the group + // is no longer usable. + void setClosing() + { + core().m_bClosing = true; + } + /// This does the same as setClosed, plus sets the m_bBroken to true. /// Such a socket can still be read from so that remaining data from /// the receiver buffer can be read, but no longer sends anything. @@ -447,6 +456,8 @@ class CUDTUnited /// @param cfgSocket socket configuration. /// @return tru if configurations match, false otherwise. static bool channelSettingsMatch(const CSrtMuxerConfig& cfgMuxer, const CSrtConfig& cfgSocket); + static bool inet6SettingsCompat(const sockaddr_any& muxaddr, const CSrtMuxerConfig& cfgMuxer, + const sockaddr_any& reqaddr, const CSrtMuxerConfig& cfgSocket); private: std::map m_mMultiplexer; // UDP multiplexer diff --git a/trunk/3rdparty/srt-1-fit/srtcore/buffer.cpp b/trunk/3rdparty/srt-1-fit/srtcore/buffer.cpp deleted file mode 100644 index 20149446f9..0000000000 --- a/trunk/3rdparty/srt-1-fit/srtcore/buffer.cpp +++ /dev/null @@ -1,2298 +0,0 @@ -/* - * SRT - Secure, Reliable, Transport - * Copyright (c) 2018 Haivision Systems Inc. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - */ - -/***************************************************************************** -Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the - above copyright notice, this list of conditions - and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the University of Illinois - nor the names of its contributors may be used to - endorse or promote products derived from this - software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*****************************************************************************/ - -/***************************************************************************** -written by - Yunhong Gu, last updated 03/12/2011 -modified by - Haivision Systems Inc. -*****************************************************************************/ - -#include "platform_sys.h" - -#include -#include -#include "buffer.h" -#include "packet.h" -#include "core.h" // provides some constants -#include "logging.h" - -namespace srt { - -using namespace std; -using namespace srt_logging; -using namespace sync; - -// You can change this value at build config by using "ENFORCE" options. -#if !defined(SRT_MAVG_SAMPLING_RATE) -#define SRT_MAVG_SAMPLING_RATE 40 -#endif - -bool AvgBufSize::isTimeToUpdate(const time_point& now) const -{ - const int usMAvgBasePeriod = 1000000; // 1s in microseconds - const int us2ms = 1000; - const int msMAvgPeriod = (usMAvgBasePeriod / SRT_MAVG_SAMPLING_RATE) / us2ms; - const uint64_t elapsed_ms = count_milliseconds(now - m_tsLastSamplingTime); // ms since last sampling - return (elapsed_ms >= msMAvgPeriod); -} - -void AvgBufSize::update(const steady_clock::time_point& now, int pkts, int bytes, int timespan_ms) -{ - const uint64_t elapsed_ms = count_milliseconds(now - m_tsLastSamplingTime); // ms since last sampling - m_tsLastSamplingTime = now; - const uint64_t one_second_in_ms = 1000; - if (elapsed_ms > one_second_in_ms) - { - // No sampling in last 1 sec, initialize average - m_dCountMAvg = pkts; - m_dBytesCountMAvg = bytes; - m_dTimespanMAvg = timespan_ms; - return; - } - - // - // weight last average value between -1 sec and last sampling time (LST) - // and new value between last sampling time and now - // |elapsed_ms| - // +----------------------------------+-------+ - // -1 LST 0(now) - // - m_dCountMAvg = avg_iir_w<1000, double>(m_dCountMAvg, pkts, elapsed_ms); - m_dBytesCountMAvg = avg_iir_w<1000, double>(m_dBytesCountMAvg, bytes, elapsed_ms); - m_dTimespanMAvg = avg_iir_w<1000, double>(m_dTimespanMAvg, timespan_ms, elapsed_ms); -} - -int round_val(double val) -{ - return static_cast(round(val)); -} - -CRateEstimator::CRateEstimator() - : m_iInRatePktsCount(0) - , m_iInRateBytesCount(0) - , m_InRatePeriod(INPUTRATE_FAST_START_US) // 0.5 sec (fast start) - , m_iInRateBps(INPUTRATE_INITIAL_BYTESPS) -{} - -void CRateEstimator::setInputRateSmpPeriod(int period) -{ - m_InRatePeriod = (uint64_t)period; //(usec) 0=no input rate calculation -} - -void CRateEstimator::updateInputRate(const time_point& time, int pkts, int bytes) -{ - // no input rate calculation - if (m_InRatePeriod == 0) - return; - - if (is_zero(m_tsInRateStartTime)) - { - m_tsInRateStartTime = time; - return; - } - else if (time < m_tsInRateStartTime) - { - // Old packets are being submitted for estimation, e.g. during the backup link activation. - return; - } - - m_iInRatePktsCount += pkts; - m_iInRateBytesCount += bytes; - - // Trigger early update in fast start mode - const bool early_update = (m_InRatePeriod < INPUTRATE_RUNNING_US) && (m_iInRatePktsCount > INPUTRATE_MAX_PACKETS); - - const uint64_t period_us = count_microseconds(time - m_tsInRateStartTime); - if (early_update || period_us > m_InRatePeriod) - { - // Required Byte/sec rate (payload + headers) - m_iInRateBytesCount += (m_iInRatePktsCount * CPacket::SRT_DATA_HDR_SIZE); - m_iInRateBps = (int)(((int64_t)m_iInRateBytesCount * 1000000) / period_us); - HLOGC(bslog.Debug, - log << "updateInputRate: pkts:" << m_iInRateBytesCount << " bytes:" << m_iInRatePktsCount - << " rate=" << (m_iInRateBps * 8) / 1000 << "kbps interval=" << period_us); - m_iInRatePktsCount = 0; - m_iInRateBytesCount = 0; - m_tsInRateStartTime = time; - - setInputRateSmpPeriod(INPUTRATE_RUNNING_US); - } -} - -CSndBuffer::CSndBuffer(int size, int maxpld) - : m_BufLock() - , m_pBlock(NULL) - , m_pFirstBlock(NULL) - , m_pCurrBlock(NULL) - , m_pLastBlock(NULL) - , m_pBuffer(NULL) - , m_iNextMsgNo(1) - , m_iSize(size) - , m_iBlockLen(maxpld) - , m_iCount(0) - , m_iBytesCount(0) -{ - // initial physical buffer of "size" - m_pBuffer = new Buffer; - m_pBuffer->m_pcData = new char[m_iSize * m_iBlockLen]; - m_pBuffer->m_iSize = m_iSize; - m_pBuffer->m_pNext = NULL; - - // circular linked list for out bound packets - m_pBlock = new Block; - Block* pb = m_pBlock; - char* pc = m_pBuffer->m_pcData; - - for (int i = 0; i < m_iSize; ++i) - { - pb->m_iMsgNoBitset = 0; - pb->m_pcData = pc; - pc += m_iBlockLen; - - if (i < m_iSize - 1) - { - pb->m_pNext = new Block; - pb = pb->m_pNext; - } - } - pb->m_pNext = m_pBlock; - - m_pFirstBlock = m_pCurrBlock = m_pLastBlock = m_pBlock; - - setupMutex(m_BufLock, "Buf"); -} - -CSndBuffer::~CSndBuffer() -{ - Block* pb = m_pBlock->m_pNext; - while (pb != m_pBlock) - { - Block* temp = pb; - pb = pb->m_pNext; - delete temp; - } - delete m_pBlock; - - while (m_pBuffer != NULL) - { - Buffer* temp = m_pBuffer; - m_pBuffer = m_pBuffer->m_pNext; - delete[] temp->m_pcData; - delete temp; - } - - releaseMutex(m_BufLock); -} - -void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl) -{ - int32_t& w_msgno = w_mctrl.msgno; - int32_t& w_seqno = w_mctrl.pktseq; - int64_t& w_srctime = w_mctrl.srctime; - const int& ttl = w_mctrl.msgttl; - const int iPktLen = m_iBlockLen; // Payload length per packet. - int iNumBlocks = len / iPktLen; - if ((len % m_iBlockLen) != 0) - ++iNumBlocks; - - HLOGC(bslog.Debug, - log << "addBuffer: needs=" << iNumBlocks << " buffers for " << len << " bytes. Taken=" << m_iCount << "/" << m_iSize); - // Retrieve current time before locking the mutex to be closer to packet submission event. - const steady_clock::time_point tnow = steady_clock::now(); - - ScopedLock bufferguard(m_BufLock); - // Dynamically increase sender buffer if there is not enough room. - while (iNumBlocks + m_iCount >= m_iSize) - { - HLOGC(bslog.Debug, log << "addBuffer: ... still lacking " << (iNumBlocks + m_iCount - m_iSize) << " buffers..."); - increase(); - } - - const int32_t inorder = w_mctrl.inorder ? MSGNO_PACKET_INORDER::mask : 0; - HLOGC(bslog.Debug, - log << CONID() << "addBuffer: adding " << iNumBlocks << " packets (" << len << " bytes) to send, msgno=" - << (w_msgno > 0 ? w_msgno : m_iNextMsgNo) << (inorder ? "" : " NOT") << " in order"); - - // Calculate origin time (same for all blocks of the message). - m_tsLastOriginTime = w_srctime ? time_point() + microseconds_from(w_srctime) : tnow; - // Rewrite back the actual value, even if it stays the same, so that the calling facilities can reuse it. - // May also be a subject to conversion error, thus the actual value is signalled back. - w_srctime = count_microseconds(m_tsLastOriginTime.time_since_epoch()); - - // The sequence number passed to this function is the sequence number - // that the very first packet from the packet series should get here. - // If there's more than one packet, this function must increase it by itself - // and then return the accordingly modified sequence number in the reference. - - Block* s = m_pLastBlock; - - if (w_msgno == SRT_MSGNO_NONE) // DEFAULT-UNCHANGED msgno supplied - { - HLOGC(bslog.Debug, log << "addBuffer: using internally managed msgno=" << m_iNextMsgNo); - w_msgno = m_iNextMsgNo; - } - else - { - HLOGC(bslog.Debug, log << "addBuffer: OVERWRITTEN by msgno supplied by caller: msgno=" << w_msgno); - m_iNextMsgNo = w_msgno; - } - - for (int i = 0; i < iNumBlocks; ++i) - { - int pktlen = len - i * iPktLen; - if (pktlen > iPktLen) - pktlen = iPktLen; - - HLOGC(bslog.Debug, - log << "addBuffer: %" << w_seqno << " #" << w_msgno << " offset=" << (i * iPktLen) - << " size=" << pktlen << " TO BUFFER:" << (void*)s->m_pcData); - memcpy((s->m_pcData), data + i * iPktLen, pktlen); - s->m_iLength = pktlen; - - s->m_iSeqNo = w_seqno; - w_seqno = CSeqNo::incseq(w_seqno); - - s->m_iMsgNoBitset = m_iNextMsgNo | inorder; - if (i == 0) - s->m_iMsgNoBitset |= PacketBoundaryBits(PB_FIRST); - if (i == iNumBlocks - 1) - s->m_iMsgNoBitset |= PacketBoundaryBits(PB_LAST); - // NOTE: if i is neither 0 nor size-1, it resuls with PB_SUBSEQUENT. - // if i == 0 == size-1, it results with PB_SOLO. - // Packets assigned to one message can be: - // [PB_FIRST] [PB_SUBSEQUENT] [PB_SUBSEQUENT] [PB_LAST] - 4 packets per message - // [PB_FIRST] [PB_LAST] - 2 packets per message - // [PB_SOLO] - 1 packet per message - - s->m_iTTL = ttl; - s->m_tsRexmitTime = time_point(); - s->m_tsOriginTime = m_tsLastOriginTime; - - // Should never happen, as the call to increase() should ensure enough buffers. - SRT_ASSERT(s->m_pNext); - s = s->m_pNext; - } - m_pLastBlock = s; - - m_iCount += iNumBlocks; - m_iBytesCount += len; - - m_rateEstimator.updateInputRate(m_tsLastOriginTime, iNumBlocks, len); - updAvgBufSize(m_tsLastOriginTime); - - // MSGNO_SEQ::mask has a form: 00000011111111... - // At least it's known that it's from some index inside til the end (to bit 0). - // If this value has been reached in a step of incrementation, it means that the - // maximum value has been reached. Casting to int32_t to ensure the same sign - // in comparison, although it's far from reaching the sign bit. - - const int nextmsgno = ++MsgNo(m_iNextMsgNo); - HLOGC(bslog.Debug, log << "CSndBuffer::addBuffer: updating msgno: #" << m_iNextMsgNo << " -> #" << nextmsgno); - m_iNextMsgNo = nextmsgno; -} - -int CSndBuffer::addBufferFromFile(fstream& ifs, int len) -{ - const int iPktLen = m_iBlockLen; // Payload length per packet. - int iNumBlocks = len / iPktLen; - if ((len % m_iBlockLen) != 0) - ++iNumBlocks; - - HLOGC(bslog.Debug, - log << "addBufferFromFile: size=" << m_iCount << " reserved=" << m_iSize << " needs=" << iPktLen - << " buffers for " << len << " bytes"); - - // dynamically increase sender buffer - while (iPktLen + m_iCount >= m_iSize) - { - HLOGC(bslog.Debug, - log << "addBufferFromFile: ... still lacking " << (iPktLen + m_iCount - m_iSize) << " buffers..."); - increase(); - } - - HLOGC(bslog.Debug, - log << CONID() << "addBufferFromFile: adding " << iPktLen << " packets (" << len - << " bytes) to send, msgno=" << m_iNextMsgNo); - - Block* s = m_pLastBlock; - int total = 0; - for (int i = 0; i < iPktLen; ++i) - { - if (ifs.bad() || ifs.fail() || ifs.eof()) - break; - - int pktlen = len - i * iPktLen; - if (pktlen > iPktLen) - pktlen = iPktLen; - - HLOGC(bslog.Debug, - log << "addBufferFromFile: reading from=" << (i * iPktLen) << " size=" << pktlen - << " TO BUFFER:" << (void*)s->m_pcData); - ifs.read(s->m_pcData, pktlen); - if ((pktlen = int(ifs.gcount())) <= 0) - break; - - // currently file transfer is only available in streaming mode, message is always in order, ttl = infinite - s->m_iMsgNoBitset = m_iNextMsgNo | MSGNO_PACKET_INORDER::mask; - if (i == 0) - s->m_iMsgNoBitset |= PacketBoundaryBits(PB_FIRST); - if (i == iPktLen - 1) - s->m_iMsgNoBitset |= PacketBoundaryBits(PB_LAST); - // NOTE: PB_FIRST | PB_LAST == PB_SOLO. - // none of PB_FIRST & PB_LAST == PB_SUBSEQUENT. - - s->m_iLength = pktlen; - s->m_iTTL = SRT_MSGTTL_INF; - s = s->m_pNext; - - total += pktlen; - } - m_pLastBlock = s; - - enterCS(m_BufLock); - m_iCount += iPktLen; - m_iBytesCount += total; - - leaveCS(m_BufLock); - - m_iNextMsgNo++; - if (m_iNextMsgNo == int32_t(MSGNO_SEQ::mask)) - m_iNextMsgNo = 1; - - return total; -} - -int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs, int& w_seqnoinc) -{ - int readlen = 0; - w_seqnoinc = 0; - - ScopedLock bufferguard(m_BufLock); - while (m_pCurrBlock != m_pLastBlock) - { - // Make the packet REFLECT the data stored in the buffer. - w_packet.m_pcData = m_pCurrBlock->m_pcData; - readlen = m_pCurrBlock->m_iLength; - w_packet.setLength(readlen); - w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; - - // 1. On submission (addBuffer), the KK flag is set to EK_NOENC (0). - // 2. The readData() is called to get the original (unique) payload not ever sent yet. - // The payload must be encrypted for the first time if the encryption - // is enabled (arg kflgs != EK_NOENC). The KK encryption flag of the data packet - // header must be set and remembered accordingly (see EncryptionKeySpec). - // 3. The next time this packet is read (only for retransmission), the payload is already - // encrypted, and the proper flag value is already stored. - - // TODO: Alternatively, encryption could happen before the packet is submitted to the buffer - // (before the addBuffer() call), and corresponding flags could be set accordingly. - // This may also put an encryption burden on the application thread, rather than the sending thread, - // which could be more efficient. Note that packet sequence number must be properly set in that case, - // as it is used as a counter for the AES encryption. - if (kflgs == -1) - { - HLOGC(bslog.Debug, log << CONID() << " CSndBuffer: ERROR: encryption required and not possible. NOT SENDING."); - readlen = 0; - } - else - { - m_pCurrBlock->m_iMsgNoBitset |= MSGNO_ENCKEYSPEC::wrap(kflgs); - } - - Block* p = m_pCurrBlock; - w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; - w_srctime = m_pCurrBlock->m_tsOriginTime; - m_pCurrBlock = m_pCurrBlock->m_pNext; - - if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - w_srctime) > p->m_iTTL)) - { - LOGC(bslog.Warn, log << CONID() << "CSndBuffer: skipping packet %" << p->m_iSeqNo << " #" << p->getMsgSeq() << " with TTL=" << p->m_iTTL); - // Skip this packet due to TTL expiry. - readlen = 0; - ++w_seqnoinc; - continue; - } - - HLOGC(bslog.Debug, log << CONID() << "CSndBuffer: extracting packet size=" << readlen << " to send"); - break; - } - - return readlen; -} - -CSndBuffer::time_point CSndBuffer::peekNextOriginal() const -{ - ScopedLock bufferguard(m_BufLock); - if (m_pCurrBlock == m_pLastBlock) - return time_point(); - - return m_pCurrBlock->m_tsOriginTime; -} - -int32_t CSndBuffer::getMsgNoAt(const int offset) -{ - ScopedLock bufferguard(m_BufLock); - - Block* p = m_pFirstBlock; - - if (p) - { - HLOGC(bslog.Debug, - log << "CSndBuffer::getMsgNoAt: FIRST MSG: size=" << p->m_iLength << " %" << p->m_iSeqNo << " #" - << p->getMsgSeq() << " !" << BufferStamp(p->m_pcData, p->m_iLength)); - } - - if (offset >= m_iCount) - { - // Prevent accessing the last "marker" block - LOGC(bslog.Error, - log << "CSndBuffer::getMsgNoAt: IPE: offset=" << offset << " not found, max offset=" << m_iCount); - return SRT_MSGNO_CONTROL; - } - - // XXX Suboptimal procedure to keep the blocks identifiable - // by sequence number. Consider using some circular buffer. - int i; - Block* ee SRT_ATR_UNUSED = 0; - for (i = 0; i < offset && p; ++i) - { - ee = p; - p = p->m_pNext; - } - - if (!p) - { - LOGC(bslog.Error, - log << "CSndBuffer::getMsgNoAt: IPE: offset=" << offset << " not found, stopped at " << i << " with #" - << (ee ? ee->getMsgSeq() : SRT_MSGNO_NONE)); - return SRT_MSGNO_CONTROL; - } - - HLOGC(bslog.Debug, - log << "CSndBuffer::getMsgNoAt: offset=" << offset << " found, size=" << p->m_iLength << " %" << p->m_iSeqNo - << " #" << p->getMsgSeq() << " !" << BufferStamp(p->m_pcData, p->m_iLength)); - - return p->getMsgSeq(); -} - -int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen) -{ - int32_t& msgno_bitset = w_packet.m_iMsgNo; - - ScopedLock bufferguard(m_BufLock); - - Block* p = m_pFirstBlock; - - // XXX Suboptimal procedure to keep the blocks identifiable - // by sequence number. Consider using some circular buffer. - for (int i = 0; i < offset && p != m_pLastBlock; ++i) - { - p = p->m_pNext; - } - if (p == m_pLastBlock) - { - LOGC(qslog.Error, log << "CSndBuffer::readData: offset " << offset << " too large!"); - return 0; - } -#if ENABLE_HEAVY_LOGGING - const int32_t first_seq = p->m_iSeqNo; - int32_t last_seq = p->m_iSeqNo; -#endif - - // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. - - // If so, then inform the caller that it should first take care of the whole - // message (all blocks with that message id). Shift the m_pCurrBlock pointer - // to the position past the last of them. Then return -1 and set the - // msgno_bitset return reference to the message id that should be dropped as - // a whole. - - // After taking care of that, the caller should immediately call this function again, - // this time possibly in order to find the real data to be sent. - - // if found block is stale - // (This is for messages that have declared TTL - messages that fail to be sent - // before the TTL defined time comes, will be dropped). - - if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - p->m_tsOriginTime) > p->m_iTTL)) - { - int32_t msgno = p->getMsgSeq(); - w_msglen = 1; - p = p->m_pNext; - bool move = false; - while (p != m_pLastBlock && msgno == p->getMsgSeq()) - { -#if ENABLE_HEAVY_LOGGING - last_seq = p->m_iSeqNo; -#endif - if (p == m_pCurrBlock) - move = true; - p = p->m_pNext; - if (move) - m_pCurrBlock = p; - w_msglen++; - } - - HLOGC(qslog.Debug, - log << "CSndBuffer::readData: due to TTL exceeded, SEQ " << first_seq << " - " << last_seq << ", " - << w_msglen << " packets to drop, msgno=" << msgno); - - // If readData returns -1, then msgno_bitset is understood as a Message ID to drop. - // This means that in this case it should be written by the message sequence value only - // (not the whole 4-byte bitset written at PH_MSGNO). - msgno_bitset = msgno; - return -1; - } - - w_packet.m_pcData = p->m_pcData; - const int readlen = p->m_iLength; - w_packet.setLength(readlen); - - // XXX Here the value predicted to be applied to PH_MSGNO field is extracted. - // As this function is predicted to extract the data to send as a rexmited packet, - // the packet must be in the form ready to send - so, in case of encryption, - // encrypted, and with all ENC flags already set. So, the first call to send - // the packet originally (the other overload of this function) must set these - // flags. - w_packet.m_iMsgNo = p->m_iMsgNoBitset; - w_srctime = p->m_tsOriginTime; - - // This function is called when packet retransmission is triggered. - // Therefore we are setting the rexmit time. - p->m_tsRexmitTime = steady_clock::now(); - - HLOGC(qslog.Debug, - log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.m_iSeqNo - << " size=" << readlen << " to send [REXMIT]"); - - return readlen; -} - -sync::steady_clock::time_point CSndBuffer::getPacketRexmitTime(const int offset) -{ - ScopedLock bufferguard(m_BufLock); - const Block* p = m_pFirstBlock; - - // XXX Suboptimal procedure to keep the blocks identifiable - // by sequence number. Consider using some circular buffer. - for (int i = 0; i < offset; ++i) - { - SRT_ASSERT(p); - p = p->m_pNext; - } - - SRT_ASSERT(p); - return p->m_tsRexmitTime; -} - -void CSndBuffer::ackData(int offset) -{ - ScopedLock bufferguard(m_BufLock); - - bool move = false; - for (int i = 0; i < offset; ++i) - { - m_iBytesCount -= m_pFirstBlock->m_iLength; - if (m_pFirstBlock == m_pCurrBlock) - move = true; - m_pFirstBlock = m_pFirstBlock->m_pNext; - } - if (move) - m_pCurrBlock = m_pFirstBlock; - - m_iCount -= offset; - - updAvgBufSize(steady_clock::now()); -} - -int CSndBuffer::getCurrBufSize() const -{ - return m_iCount; -} - -int CSndBuffer::getAvgBufSize(int& w_bytes, int& w_tsp) -{ - ScopedLock bufferguard(m_BufLock); /* Consistency of pkts vs. bytes vs. spantime */ - - /* update stats in case there was no add/ack activity lately */ - updAvgBufSize(steady_clock::now()); - - // Average number of packets and timespan could be small, - // so rounding is beneficial, while for the number of - // bytes in the buffer is a higher value, so rounding can be omitted, - // but probably better to round all three values. - w_bytes = round_val(m_mavg.bytes()); - w_tsp = round_val(m_mavg.timespan_ms()); - return round_val(m_mavg.pkts()); -} - -void CSndBuffer::updAvgBufSize(const steady_clock::time_point& now) -{ - if (!m_mavg.isTimeToUpdate(now)) - return; - - int bytes = 0; - int timespan_ms = 0; - const int pkts = getCurrBufSize((bytes), (timespan_ms)); - m_mavg.update(now, pkts, bytes, timespan_ms); -} - -int CSndBuffer::getCurrBufSize(int& w_bytes, int& w_timespan) -{ - w_bytes = m_iBytesCount; - /* - * Timespan can be less then 1000 us (1 ms) if few packets. - * Also, if there is only one pkt in buffer, the time difference will be 0. - * Therefore, always add 1 ms if not empty. - */ - w_timespan = 0 < m_iCount ? (int) count_milliseconds(m_tsLastOriginTime - m_pFirstBlock->m_tsOriginTime) + 1 : 0; - - return m_iCount; -} - -CSndBuffer::duration CSndBuffer::getBufferingDelay(const time_point& tnow) const -{ - ScopedLock lck(m_BufLock); - SRT_ASSERT(m_pFirstBlock); - if (m_iCount == 0) - return duration(0); - - return tnow - m_pFirstBlock->m_tsOriginTime; -} - -int CSndBuffer::dropLateData(int& w_bytes, int32_t& w_first_msgno, const steady_clock::time_point& too_late_time) -{ - int dpkts = 0; - int dbytes = 0; - bool move = false; - int32_t msgno = 0; - - ScopedLock bufferguard(m_BufLock); - for (int i = 0; i < m_iCount && m_pFirstBlock->m_tsOriginTime < too_late_time; ++i) - { - dpkts++; - dbytes += m_pFirstBlock->m_iLength; - msgno = m_pFirstBlock->getMsgSeq(); - - if (m_pFirstBlock == m_pCurrBlock) - move = true; - m_pFirstBlock = m_pFirstBlock->m_pNext; - } - - if (move) - { - m_pCurrBlock = m_pFirstBlock; - } - m_iCount -= dpkts; - - m_iBytesCount -= dbytes; - w_bytes = dbytes; - - // We report the increased number towards the last ever seen - // by the loop, as this last one is the last received. So remained - // (even if "should remain") is the first after the last removed one. - w_first_msgno = ++MsgNo(msgno); - - updAvgBufSize(steady_clock::now()); - - return (dpkts); -} - -void CSndBuffer::increase() -{ - int unitsize = m_pBuffer->m_iSize; - - // new physical buffer - Buffer* nbuf = NULL; - try - { - nbuf = new Buffer; - nbuf->m_pcData = new char[unitsize * m_iBlockLen]; - } - catch (...) - { - delete nbuf; - throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); - } - nbuf->m_iSize = unitsize; - nbuf->m_pNext = NULL; - - // insert the buffer at the end of the buffer list - Buffer* p = m_pBuffer; - while (p->m_pNext != NULL) - p = p->m_pNext; - p->m_pNext = nbuf; - - // new packet blocks - Block* nblk = NULL; - try - { - nblk = new Block; - } - catch (...) - { - delete nblk; - throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); - } - Block* pb = nblk; - for (int i = 1; i < unitsize; ++i) - { - pb->m_pNext = new Block; - pb = pb->m_pNext; - } - - // insert the new blocks onto the existing one - pb->m_pNext = m_pLastBlock->m_pNext; - m_pLastBlock->m_pNext = nblk; - - pb = nblk; - char* pc = nbuf->m_pcData; - for (int i = 0; i < unitsize; ++i) - { - pb->m_pcData = pc; - pb = pb->m_pNext; - pc += m_iBlockLen; - } - - m_iSize += unitsize; - - HLOGC(bslog.Debug, - log << "CSndBuffer: BUFFER FULL - adding " << (unitsize * m_iBlockLen) << " bytes spread to " << unitsize - << " blocks" - << " (total size: " << m_iSize << " bytes)"); -} - -//////////////////////////////////////////////////////////////////////////////// - -#if (!ENABLE_NEW_RCVBUFFER) - -/* - * RcvBuffer (circular buffer): - * - * |<------------------- m_iSize ----------------------------->| - * | |<--- acked pkts -->|<--- m_iMaxPos --->| | - * | | | | | - * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ - * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] - * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ - * | | | | - * | | \__last pkt received - * | \___ m_iLastAckPos: last ack sent - * \___ m_iStartPos: first message to read - * - * m_pUnit[i]->m_iFlag: 0:free, 1:good, 2:passack, 3:dropped - * - * thread safety: - * m_iStartPos: CUDT::m_RecvLock - * m_iLastAckPos: CUDT::m_AckLock - * m_iMaxPos: none? (modified on add and ack - */ - -CRcvBuffer::CRcvBuffer(CUnitQueue* queue, int bufsize_pkts) - : m_pUnit(NULL) - , m_iSize(bufsize_pkts) - , m_pUnitQueue(queue) - , m_iStartPos(0) - , m_iLastAckPos(0) - , m_iMaxPos(0) - , m_iNotch(0) - , m_BytesCountLock() - , m_iBytesCount(0) - , m_iAckedPktsCount(0) - , m_iAckedBytesCount(0) - , m_uAvgPayloadSz(7 * 188) -{ - m_pUnit = new CUnit*[m_iSize]; - for (int i = 0; i < m_iSize; ++i) - m_pUnit[i] = NULL; - - setupMutex(m_BytesCountLock, "BytesCount"); -} - -CRcvBuffer::~CRcvBuffer() -{ - for (int i = 0; i < m_iSize; ++i) - { - if (m_pUnit[i] != NULL) - { - m_pUnitQueue->makeUnitFree(m_pUnit[i]); - } - } - - delete[] m_pUnit; - - releaseMutex(m_BytesCountLock); -} - -void CRcvBuffer::countBytes(int pkts, int bytes, bool acked) -{ - /* - * Byte counter changes from both sides (Recv & Ack) of the buffer - * so the higher level lock is not enough for thread safe op. - * - * pkts are... - * added (bytes>0, acked=false), - * acked (bytes>0, acked=true), - * removed (bytes<0, acked=n/a) - */ - ScopedLock cg(m_BytesCountLock); - - if (!acked) // adding new pkt in RcvBuffer - { - m_iBytesCount += bytes; /* added or removed bytes from rcv buffer */ - if (bytes > 0) /* Assuming one pkt when adding bytes */ - m_uAvgPayloadSz = ((m_uAvgPayloadSz * (100 - 1)) + bytes) / 100; - } - else // acking/removing pkts to/from buffer - { - m_iAckedPktsCount += pkts; /* acked or removed pkts from rcv buffer */ - m_iAckedBytesCount += bytes; /* acked or removed bytes from rcv buffer */ - - if (bytes < 0) - m_iBytesCount += bytes; /* removed bytes from rcv buffer */ - } -} - -int CRcvBuffer::addData(CUnit* unit, int offset) -{ - SRT_ASSERT(unit != NULL); - if (offset >= getAvailBufSize()) - return -1; - - const int pos = (m_iLastAckPos + offset) % m_iSize; - if (offset >= m_iMaxPos) - m_iMaxPos = offset + 1; - - if (m_pUnit[pos] != NULL) - { - HLOGC(qrlog.Debug, log << "addData: unit %" << unit->m_Packet.m_iSeqNo << " rejected, already exists"); - return -1; - } - m_pUnit[pos] = unit; - countBytes(1, (int)unit->m_Packet.getLength()); - - m_pUnitQueue->makeUnitGood(unit); - - HLOGC(qrlog.Debug, - log << "addData: unit %" << unit->m_Packet.m_iSeqNo << " accepted, off=" << offset << " POS=" << pos); - return 0; -} - -int CRcvBuffer::readBuffer(char* data, int len) -{ - int p = m_iStartPos; - int lastack = m_iLastAckPos; - int rs = len; - IF_HEAVY_LOGGING(char* begin = data); - - const bool bTsbPdEnabled = m_tsbpd.isEnabled(); - const steady_clock::time_point now = (bTsbPdEnabled ? steady_clock::now() : steady_clock::time_point()); - - HLOGC(brlog.Debug, log << CONID() << "readBuffer: start=" << p << " lastack=" << lastack); - while ((p != lastack) && (rs > 0)) - { - if (m_pUnit[p] == NULL) - { - LOGC(brlog.Error, log << CONID() << "IPE readBuffer on null packet pointer"); - return -1; - } - - const CPacket& pkt = m_pUnit[p]->m_Packet; - - if (bTsbPdEnabled) - { - HLOGC(brlog.Debug, - log << CONID() << "readBuffer: chk if time2play:" - << " NOW=" << FormatTime(now) - << " PKT TS=" << FormatTime(getPktTsbPdTime(pkt.getMsgTimeStamp()))); - - if ((getPktTsbPdTime(pkt.getMsgTimeStamp()) > now)) - break; /* too early for this unit, return whatever was copied */ - } - - const int pktlen = (int) pkt.getLength(); - const int remain_pktlen = pktlen - m_iNotch; - - const int unitsize = std::min(remain_pktlen, rs); - - HLOGC(brlog.Debug, - log << CONID() << "readBuffer: copying buffer #" << p << " targetpos=" << int(data - begin) - << " sourcepos=" << m_iNotch << " size=" << unitsize << " left=" << (unitsize - rs)); - memcpy((data), pkt.m_pcData + m_iNotch, unitsize); - - data += unitsize; - - if (rs >= remain_pktlen) - { - freeUnitAt(p); - p = shiftFwd(p); - - m_iNotch = 0; - } - else - m_iNotch += rs; - - rs -= unitsize; - } - - /* we removed acked bytes form receive buffer */ - countBytes(-1, -(len - rs), true); - m_iStartPos = p; - - return len - rs; -} - -int CRcvBuffer::readBufferToFile(fstream& ofs, int len) -{ - int p = m_iStartPos; - int lastack = m_iLastAckPos; - int rs = len; - - int32_t trace_seq SRT_ATR_UNUSED = SRT_SEQNO_NONE; - int trace_shift SRT_ATR_UNUSED = -1; - - while ((p != lastack) && (rs > 0)) - { -#if ENABLE_LOGGING - ++trace_shift; -#endif - // Skip empty units. Note that this shouldn't happen - // in case of a file transfer. - if (!m_pUnit[p]) - { - p = shiftFwd(p); - LOGC(brlog.Error, log << "readBufferToFile: IPE: NULL unit found in file transmission, last good %" - << trace_seq << " + " << trace_shift); - continue; - } - - const CPacket& pkt = m_pUnit[p]->m_Packet; - -#if ENABLE_LOGGING - trace_seq = pkt.getSeqNo(); -#endif - const int pktlen = (int) pkt.getLength(); - const int remain_pktlen = pktlen - m_iNotch; - - const int unitsize = std::min(remain_pktlen, rs); - - ofs.write(pkt.m_pcData + m_iNotch, unitsize); - if (ofs.fail()) - break; - - if (rs >= remain_pktlen) - { - freeUnitAt(p); - p = shiftFwd(p); - - m_iNotch = 0; - } - else - m_iNotch += rs; - - rs -= unitsize; - } - - /* we removed acked bytes form receive buffer */ - countBytes(-1, -(len - rs), true); - m_iStartPos = p; - - return len - rs; -} - -int CRcvBuffer::ackData(int len) -{ - SRT_ASSERT(len < m_iSize); - SRT_ASSERT(len > 0); - int end = shift(m_iLastAckPos, len); - - { - int pkts = 0; - int bytes = 0; - for (int i = m_iLastAckPos; i != end; i = shiftFwd(i)) - { - if (m_pUnit[i] == NULL) - continue; - - pkts++; - bytes += (int)m_pUnit[i]->m_Packet.getLength(); - } - if (pkts > 0) - countBytes(pkts, bytes, true); - } - - HLOGC(brlog.Debug, - log << "ackData: shift by " << len << ", start=" << m_iStartPos << " end=" << m_iLastAckPos << " -> " << end); - - m_iLastAckPos = end; - m_iMaxPos -= len; - if (m_iMaxPos < 0) - m_iMaxPos = 0; - - // Returned value is the distance towards the starting - // position from m_iLastAckPos, which is in sync with CUDT::m_iRcvLastSkipAck. - // This should help determine the sequence number at first read-ready position. - - const int dist = m_iLastAckPos - m_iStartPos; - if (dist < 0) - return dist + m_iSize; - return dist; -} - -void CRcvBuffer::skipData(int len) -{ - /* - * Caller need protect both AckLock and RecvLock - * to move both m_iStartPos and m_iLastAckPost - */ - if (m_iStartPos == m_iLastAckPos) - m_iStartPos = (m_iStartPos + len) % m_iSize; - m_iLastAckPos = (m_iLastAckPos + len) % m_iSize; - m_iMaxPos -= len; - if (m_iMaxPos < 0) - m_iMaxPos = 0; -} - -size_t CRcvBuffer::dropData(int len) -{ - // This function does the same as skipData, although skipData - // should work in the condition of absence of data, so no need - // to force the units in the range to be freed. This function - // works in more general condition where we don't know if there - // are any data in the given range, but want to remove these - // "sequence positions" from the buffer, whether there are data - // at them or not. - - size_t stats_bytes = 0; - - int p = m_iStartPos; - int past_q = shift(p, len); - while (p != past_q) - { - if (m_pUnit[p] && m_pUnit[p]->m_iFlag == CUnit::GOOD) - { - stats_bytes += m_pUnit[p]->m_Packet.getLength(); - freeUnitAt(p); - } - - p = shiftFwd(p); - } - - m_iStartPos = past_q; - return stats_bytes; -} - -bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, - bool& w_passack, - int32_t& w_skipseqno, - int32_t& w_curpktseq, - int32_t base_seq) -{ - HLOGC(brlog.Debug, log << "getRcvFirstMsg: base_seq=" << base_seq); - w_skipseqno = SRT_SEQNO_NONE; - w_passack = false; - // tsbpdtime will be retrieved by the below call - // Returned values: - // - tsbpdtime: real time when the packet is ready to play (whether ready to play or not) - // - w_passack: false (the report concerns a packet with an exactly next sequence) - // - w_skipseqno == SRT_SEQNO_NONE: no packets to skip towards the first RTP - // - w_curpktseq: that exactly packet that is reported (for debugging purposes) - // - @return: whether the reported packet is ready to play - - /* Check the acknowledged packets */ - // getRcvReadyMsg returns true if the time to play for the first message - // that larger than base_seq is in the past. - if (getRcvReadyMsg((w_tsbpdtime), (w_curpktseq), -1, base_seq)) - { - HLOGC(brlog.Debug, log << "getRcvFirstMsg: ready CONTIG packet: %" << w_curpktseq); - return true; - } - else if (!is_zero(w_tsbpdtime)) - { - HLOGC(brlog.Debug, log << "getRcvFirstMsg: packets found, but in future"); - // This means that a message next to be played, has been found, - // but the time to play is in future. - return false; - } - - // Falling here means that there are NO PACKETS in the ACK-ed region - // (m_iStartPos - m_iLastAckPos), but we may have something in the - // region (m_iLastAckPos - (m_iLastAckPos+m_iMaxPos)), that is, packets - // that may be separated from the last ACK-ed by lost ones. - - // Below this line we have only two options: - // - m_iMaxPos == 0, which means that no more packets are in the buffer - // - returned: tsbpdtime=0, w_passack=true, w_skipseqno=SRT_SEQNO_NONE, w_curpktseq=, @return false - // - m_iMaxPos > 0, which means that there are packets arrived after a lost packet: - // - returned: tsbpdtime=PKT.TS, w_passack=true, w_skipseqno=PKT.SEQ, w_curpktseq=PKT, @return LOCAL(PKT.TS) <= - // NOW - - /* - * No acked packets ready but caller want to know next packet to wait for - * Check the not yet acked packets that may be stuck by missing packet(s). - */ - bool haslost = false; - int last_ready_pos = -1; - steady_clock::time_point tsbpdtime = steady_clock::time_point(); - w_tsbpdtime = steady_clock::time_point(); - w_passack = true; - - // XXX SUSPECTED ISSUE with this algorithm: - // The above call to getRcvReadyMsg() should report as to whether: - // - there is an EXACTLY NEXT SEQUENCE packet - // - this packet is ready to play. - // - // Situations handled after the call are when: - // - there's the next sequence packet available and it is ready to play - // - there are no packets at all, ready to play or not - // - // So, the remaining situation is that THERE ARE PACKETS that follow - // the current sequence, but they are not ready to play. This includes - // packets that have the exactly next sequence and packets that jump - // over a lost packet. - // - // As the getRcvReadyMsg() function walks through the incoming units - // to see if there's anything that satisfies these conditions, it *SHOULD* - // be also capable of checking if the next available packet, if it is - // there, is the next sequence packet or not. Retrieving this exactly - // packet would be most useful, as the test for play-readiness and - // sequentiality can be done on it directly. - // - // When done so, the below loop would be completely unnecessary. - - // Logical description of the below algorithm: - // 1. update w_tsbpdtime and w_curpktseq if found one packet ready to play - // - keep check the next packet if still smaller than base_seq - // 2. set w_skipseqno if found packets before w_curpktseq lost - // if no packets larger than base_seq ready to play, return the largest RTP - // else return the first one that larger than base_seq and rady to play - - for (int i = m_iLastAckPos, n = shift(m_iLastAckPos, m_iMaxPos); i != n; i = shiftFwd(i)) - { - if (!m_pUnit[i] || m_pUnit[i]->m_iFlag != CUnit::GOOD) - { - /* There are packets in the sequence not received yet */ - haslost = true; - HLOGC(brlog.Debug, log << "getRcvFirstMsg: empty hole at *" << i); - } - else - { - tsbpdtime = getPktTsbPdTime(m_pUnit[i]->m_Packet.getMsgTimeStamp()); - /* Packet ready to play */ - if (tsbpdtime <= steady_clock::now()) - { - // If the last ready-to-play packet exists, free it. - if (!is_zero(w_tsbpdtime)) { - HLOGC(brlog.Debug, - log << "getRcvFirstMsg: found next ready packet, free last %" - << w_curpktseq << " POS=" << last_ready_pos); - SRT_ASSERT(w_curpktseq != SRT_SEQNO_NONE); - freeUnitAt(last_ready_pos); - } - w_tsbpdtime = tsbpdtime; - w_curpktseq = m_pUnit[i]->m_Packet.m_iSeqNo; - last_ready_pos = i; - if (haslost) - w_skipseqno = w_curpktseq; - - if (base_seq != SRT_SEQNO_NONE && CSeqNo::seqcmp(w_curpktseq, base_seq) <= 0) - { - HLOGC(brlog.Debug, - log << "getRcvFirstMsg: found ready packet %" << w_curpktseq - << " but not larger than base_seq, try next"); - continue; - } - - HLOGC(brlog.Debug, - log << "getRcvFirstMsg: found ready packet, nSKIPPED: " - << ((i - m_iLastAckPos + m_iSize) % m_iSize)); - - // NOTE: if haslost is not set, it means that this is the VERY FIRST - // packet, that is, packet currently at pos = m_iLastAckPos. There's no - // possibility that it is so otherwise because: - // - if this first good packet is ready to play, THIS HERE RETURNS NOW. - // ... - return true; - } - - if (!is_zero(w_tsbpdtime)) { - return true; - } - HLOGC(brlog.Debug, - log << "getRcvFirstMsg: found NOT READY packet, nSKIPPED: " - << ((i - m_iLastAckPos + m_iSize) % m_iSize)); - // ... and if this first good packet WASN'T ready to play, THIS HERE RETURNS NOW, TOO, - // just states that there's no ready packet to play. - // ... - return false; - } - // ... and if this first packet WASN'T GOOD, the loop continues, however since now - // the 'haslost' is set, which means that it continues only to find the first valid - // packet after stating that the very first packet isn't valid. - } - if (!is_zero(w_tsbpdtime)) { - return true; - } - HLOGC(brlog.Debug, log << "getRcvFirstMsg: found NO PACKETS"); - return false; -} - -steady_clock::time_point CRcvBuffer::debugGetDeliveryTime(int offset) -{ - int i; - if (offset > 0) - i = shift(m_iStartPos, offset); - else - i = m_iStartPos; - - CUnit* u = m_pUnit[i]; - if (!u || u->m_iFlag != CUnit::GOOD) - return steady_clock::time_point(); - - return getPktTsbPdTime(u->m_Packet.getMsgTimeStamp()); -} - -int32_t CRcvBuffer::getTopMsgno() const -{ - if (m_iStartPos == m_iLastAckPos) - return SRT_MSGNO_NONE; // No message is waiting - - if (!m_pUnit[m_iStartPos]) - return SRT_MSGNO_NONE; // pity - - return m_pUnit[m_iStartPos]->m_Packet.getMsgSeq(); -} - -bool CRcvBuffer::getRcvReadyMsg(steady_clock::time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto, int base_seq) -{ - const bool havelimit = upto != -1; - int end = -1, past_end = -1; - if (havelimit) - { - int stretch = (m_iSize + m_iStartPos - m_iLastAckPos) % m_iSize; - if (upto > stretch) - { - HLOGC(brlog.Debug, log << "position back " << upto << " exceeds stretch " << stretch); - // Do nothing. This position is already gone. - return false; - } - - end = m_iLastAckPos - upto; - if (end < 0) - end += m_iSize; - past_end = shiftFwd(end); // For in-loop comparison - HLOGC(brlog.Debug, log << "getRcvReadyMsg: will read from position " << end); - } - - // NOTE: position m_iLastAckPos in the buffer represents the sequence number of - // CUDT::m_iRcvLastSkipAck. Therefore 'upto' contains a positive value that should - // be decreased from m_iLastAckPos to get the position in the buffer that represents - // the sequence number up to which we'd like to read. - IF_HEAVY_LOGGING(const char* reason = "NOT RECEIVED"); - - for (int i = m_iStartPos, n = m_iLastAckPos; i != n; i = shiftFwd(i)) - { - // In case when we want to read only up to given sequence number, stop - // the loop if this number was reached. This number must be extracted from - // the buffer and any following must wait here for "better times". Note - // that the unit that points to the requested sequence must remain in - // the buffer, unless there is no valid packet at that position, in which - // case it is allowed to point to the NEXT sequence towards it, however - // if it does, this cell must remain in the buffer for prospective recovery. - if (havelimit && i == past_end) - break; - - bool freeunit = false; - - /* Skip any invalid skipped/dropped packets */ - if (m_pUnit[i] == NULL) - { - HLOGC(brlog.Debug, - log << "getRcvReadyMsg: POS=" << i << " +" << ((i - m_iStartPos + m_iSize) % m_iSize) - << " SKIPPED - no unit there"); - m_iStartPos = shiftFwd(m_iStartPos); - continue; - } - - w_curpktseq = m_pUnit[i]->m_Packet.getSeqNo(); - - if (m_pUnit[i]->m_iFlag != CUnit::GOOD) - { - HLOGC(brlog.Debug, - log << "getRcvReadyMsg: POS=" << i << " +" << ((i - m_iStartPos + m_iSize) % m_iSize) - << " SKIPPED - unit not good"); - freeunit = true; - } - else - { - // This does: - // 1. Get the TSBPD time of the unit. Stop and return false if this unit - // is not yet ready to play. - // 2. If it's ready to play, check also if it's decrypted. If not, skip it. - // 3. Check also if it's larger than base_seq, if not, skip it. - // 4. If it's ready to play, decrypted and larger than base, stop and return it. - if (!havelimit) - { - w_tsbpdtime = getPktTsbPdTime(m_pUnit[i]->m_Packet.getMsgTimeStamp()); - const steady_clock::duration towait = (w_tsbpdtime - steady_clock::now()); - if (towait.count() > 0) - { - HLOGC(brlog.Debug, - log << "getRcvReadyMsg: POS=" << i << " +" << ((i - m_iStartPos + m_iSize) % m_iSize) - << " pkt %" << w_curpktseq << " NOT ready to play (only in " << count_milliseconds(towait) - << "ms)"); - return false; - } - - if (m_pUnit[i]->m_Packet.getMsgCryptoFlags() != EK_NOENC) - { - IF_HEAVY_LOGGING(reason = "DECRYPTION FAILED"); - freeunit = true; /* packet not decrypted */ - } - else if (base_seq != SRT_SEQNO_NONE && CSeqNo::seqcmp(w_curpktseq, base_seq) <= 0) - { - IF_HEAVY_LOGGING(reason = "smaller than base_seq"); - w_tsbpdtime = steady_clock::time_point(); - freeunit = true; - } - else - { - HLOGC(brlog.Debug, - log << "getRcvReadyMsg: POS=" << i << " +" << ((i - m_iStartPos + m_iSize) % m_iSize) - << " pkt %" << w_curpktseq << " ready to play (delayed " << count_milliseconds(towait) - << "ms)"); - return true; - } - } - // In this case: - // 1. We don't even look into the packet if this is not the requested sequence. - // All packets that are earlier than the required sequence will be dropped. - // 2. When found the packet with expected sequence number, and the condition for - // good unit is passed, we get the timestamp. - // 3. If the packet is not decrypted, we allow it to be removed - // 4. If we reached the required sequence, and the packet is good, KEEP IT in the buffer, - // and return with the pointer pointing to this very buffer. Only then return true. - else - { - // We have a limit up to which the reading will be done, - // no matter if the time has come or not - although retrieve it. - if (i == end) - { - HLOGC(brlog.Debug, log << "CAUGHT required seq position " << i); - // We have the packet we need. Extract its data. - w_tsbpdtime = getPktTsbPdTime(m_pUnit[i]->m_Packet.getMsgTimeStamp()); - - // If we have a decryption failure, allow the unit to be released. - if (m_pUnit[i]->m_Packet.getMsgCryptoFlags() != EK_NOENC) - { - IF_HEAVY_LOGGING(reason = "DECRYPTION FAILED"); - freeunit = true; /* packet not decrypted */ - } - else - { - // Stop here and keep the packet in the buffer, so it will be - // next extracted. - HLOGC(brlog.Debug, - log << "getRcvReadyMsg: packet seq=" << w_curpktseq << " ready for extraction"); - return true; - } - } - else - { - HLOGC(brlog.Debug, log << "SKIPPING position " << i); - // Continue the loop and remove the current packet because - // its sequence number is too old. - freeunit = true; - } - } - } - - if (freeunit) - { - HLOGC(brlog.Debug, log << "getRcvReadyMsg: POS=" << i << " FREED: " << reason); - /* removed skipped, dropped, undecryptable bytes from rcv buffer */ - const int rmbytes = (int)m_pUnit[i]->m_Packet.getLength(); - countBytes(-1, -rmbytes, true); - - freeUnitAt(i); - m_iStartPos = shiftFwd(m_iStartPos); - } - } - - HLOGC(brlog.Debug, log << "getRcvReadyMsg: nothing to deliver: " << reason); - return false; -} - -/* - * Return receivable data status (packet timestamp_us ready to play if TsbPd mode) - * Return playtime (tsbpdtime) of 1st packet in queue, ready to play or not - * - * Return data ready to be received (packet timestamp_us ready to play if TsbPd mode) - * Using getRcvDataSize() to know if there is something to read as it was widely - * used in the code (core.cpp) is expensive in TsbPD mode, hence this simpler function - * that only check if first packet in queue is ready. - */ -bool CRcvBuffer::isRcvDataReady(steady_clock::time_point& w_tsbpdtime, int32_t& w_curpktseq, int32_t seqdistance) -{ - w_tsbpdtime = steady_clock::time_point(); - - if (m_tsbpd.isEnabled()) - { - const CPacket* pkt = getRcvReadyPacket(seqdistance); - if (!pkt) - { - HLOGC(brlog.Debug, log << "isRcvDataReady: packet NOT extracted."); - return false; - } - - /* - * Acknowledged data is available, - * Only say ready if time to deliver. - * Report the timestamp, ready or not. - */ - w_curpktseq = pkt->getSeqNo(); - w_tsbpdtime = getPktTsbPdTime(pkt->getMsgTimeStamp()); - - // If seqdistance was passed, then return true no matter what the - // TSBPD time states. - if (seqdistance != -1 || w_tsbpdtime <= steady_clock::now()) - { - HLOGC(brlog.Debug, - log << "isRcvDataReady: packet extracted seqdistance=" << seqdistance - << " TsbPdTime=" << FormatTime(w_tsbpdtime)); - return true; - } - - HLOGC(brlog.Debug, log << "isRcvDataReady: packet extracted, but NOT READY"); - return false; - } - - return isRcvDataAvailable(); -} - -// XXX This function may be called only after checking -// if m_bTsbPdMode. -CPacket* CRcvBuffer::getRcvReadyPacket(int32_t seqdistance) -{ - // If asked for readiness of a packet at given sequence distance - // (that is, we need to extract the packet with given sequence number), - // only check if this cell is occupied in the buffer, and if so, - // if it's occupied with a "good" unit. That's all. It doesn't - // matter whether it's ready to play. - if (seqdistance != -1) - { - // Note: seqdistance is the value to to go BACKWARDS from m_iLastAckPos, - // which is the position that is in sync with CUDT::m_iRcvLastSkipAck. This - // position is the sequence number of a packet that is NOT received, but it's - // expected to be received as next. So the minimum value of seqdistance is 1. - - // SANITY CHECK - if (seqdistance == 0) - { - LOGC(brlog.Fatal, log << "IPE: trying to extract packet past the last ACK-ed!"); - return 0; - } - - if (seqdistance > getRcvDataSize()) - { - HLOGC(brlog.Debug, - log << "getRcvReadyPacket: Sequence offset=" << seqdistance - << " is in the past (start=" << m_iStartPos << " end=" << m_iLastAckPos << ")"); - return 0; - } - - int i = shift(m_iLastAckPos, -seqdistance); - if (m_pUnit[i] && m_pUnit[i]->m_iFlag == CUnit::GOOD) - { - HLOGC(brlog.Debug, log << "getRcvReadyPacket: FOUND PACKET %" << m_pUnit[i]->m_Packet.getSeqNo()); - return &m_pUnit[i]->m_Packet; - } - - HLOGC(brlog.Debug, log << "getRcvReadyPacket: Sequence offset=" << seqdistance << " IS NOT RECEIVED."); - return 0; - } - - IF_HEAVY_LOGGING(int nskipped = 0); - for (int i = m_iStartPos, n = m_iLastAckPos; i != n; i = shiftFwd(i)) - { - /* - * Skip missing packets that did not arrive in time. - */ - if (m_pUnit[i] && m_pUnit[i]->m_iFlag == CUnit::GOOD) - { - HLOGC(brlog.Debug, - log << "getRcvReadyPacket: Found next packet seq=%" << m_pUnit[i]->m_Packet.getSeqNo() << " (" - << nskipped << " empty cells skipped)"); - return &m_pUnit[i]->m_Packet; - } - IF_HEAVY_LOGGING(++nskipped); - } - - return 0; -} - -#if ENABLE_HEAVY_LOGGING -// This function is for debug purposes only and it's called only -// from within HLOG* macros. -void CRcvBuffer::reportBufferStats() const -{ - int nmissing = 0; - int32_t low_seq = SRT_SEQNO_NONE, high_seq = SRT_SEQNO_NONE; - int32_t low_ts = 0, high_ts = 0; - - for (int i = m_iStartPos, n = m_iLastAckPos; i != n; i = (i + 1) % m_iSize) - { - if (m_pUnit[i] && m_pUnit[i]->m_iFlag == CUnit::GOOD) - { - low_seq = m_pUnit[i]->m_Packet.m_iSeqNo; - low_ts = m_pUnit[i]->m_Packet.m_iTimeStamp; - break; - } - ++nmissing; - } - - // Not sure if a packet MUST BE at the last ack pos position, so check, just in case. - int n = m_iLastAckPos; - if (m_pUnit[n] && m_pUnit[n]->m_iFlag == CUnit::GOOD) - { - high_ts = m_pUnit[n]->m_Packet.m_iTimeStamp; - high_seq = m_pUnit[n]->m_Packet.m_iSeqNo; - } - else - { - // Possibilities are: - // m_iStartPos == m_iLastAckPos, high_ts == low_ts, defined. - // No packet: low_ts == 0, so high_ts == 0, too. - high_ts = low_ts; - } - // The 32-bit timestamps are relative and roll over oftten; what - // we really need is the timestamp difference. The only place where - // we can ask for the time base is the upper time because when trying - // to receive the time base for the lower time we'd break the requirement - // for monotonic clock. - - uint64_t upper_time = high_ts; - uint64_t lower_time = low_ts; - - if (lower_time > upper_time) - upper_time += uint64_t(CPacket::MAX_TIMESTAMP) + 1; - - int32_t timespan = upper_time - lower_time; - int seqspan = 0; - if (low_seq != SRT_SEQNO_NONE && high_seq != SRT_SEQNO_NONE) - { - seqspan = CSeqNo::seqoff(low_seq, high_seq); - } - - LOGC(brlog.Debug, - log << "RCV BUF STATS: seqspan=%(" << low_seq << "-" << high_seq << ":" << seqspan << ") missing=" << nmissing - << "pkts"); - LOGC(brlog.Debug, - log << "RCV BUF STATS: timespan=" << timespan << "us (lo=" << lower_time << " hi=" << upper_time << ")"); -} - -#endif // ENABLE_HEAVY_LOGGING - -bool CRcvBuffer::isRcvDataReady() -{ - steady_clock::time_point tsbpdtime; - int32_t seq; - - return isRcvDataReady((tsbpdtime), (seq), -1); -} - -int CRcvBuffer::getAvailBufSize() const -{ - // One slot must be empty in order to tell the difference between "empty buffer" and "full buffer" - return m_iSize - getRcvDataSize() - 1; -} - -int CRcvBuffer::getRcvDataSize() const -{ - if (m_iLastAckPos >= m_iStartPos) - return m_iLastAckPos - m_iStartPos; - - return m_iSize + m_iLastAckPos - m_iStartPos; -} - -int CRcvBuffer::debugGetSize() const -{ - // Does exactly the same as getRcvDataSize, but - // it should be used FOR INFORMATIONAL PURPOSES ONLY. - // The source values might be changed in another thread - // during the calculation, although worst case the - // resulting value may differ to the real buffer size by 1. - int from = m_iStartPos, to = m_iLastAckPos; - int size = to - from; - if (size < 0) - size += m_iSize; - - return size; -} - -/* Return moving average of acked data pkts, bytes, and timespan (ms) of the receive buffer */ -int CRcvBuffer::getRcvAvgDataSize(int& bytes, int& timespan) -{ - // Average number of packets and timespan could be small, - // so rounding is beneficial, while for the number of - // bytes in the buffer is a higher value, so rounding can be omitted, - // but probably better to round all three values. - timespan = round_val(m_mavg.timespan_ms()); - bytes = round_val(m_mavg.bytes()); - return round_val(m_mavg.pkts()); -} - -/* Update moving average of acked data pkts, bytes, and timespan (ms) of the receive buffer */ -void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now) -{ - if (!m_mavg.isTimeToUpdate(now)) - return; - - int bytes = 0; - int timespan_ms = 0; - const int pkts = getRcvDataSize(bytes, timespan_ms); - m_mavg.update(now, pkts, bytes, timespan_ms); -} - -/* Return acked data pkts, bytes, and timespan (ms) of the receive buffer */ -int CRcvBuffer::getRcvDataSize(int& bytes, int& timespan) -{ - timespan = 0; - if (m_tsbpd.isEnabled()) - { - // Get a valid startpos. - // Skip invalid entries in the beginning, if any. - int startpos = m_iStartPos; - for (; startpos != m_iLastAckPos; startpos = shiftFwd(startpos)) - { - if ((NULL != m_pUnit[startpos]) && (CUnit::GOOD == m_pUnit[startpos]->m_iFlag)) - break; - } - - int endpos = m_iLastAckPos; - - if (m_iLastAckPos != startpos) - { - /* - * |<--- DataSpan ---->|<- m_iMaxPos ->| - * +---+---+---+---+---+---+---+---+---+---+---+--- - * | | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | | m_pUnits[] - * +---+---+---+---+---+---+---+---+---+---+---+--- - * | | - * \_ m_iStartPos \_ m_iLastAckPos - * - * m_pUnits[startpos] shall be valid (->m_iFlag==CUnit::GOOD). - * If m_pUnits[m_iLastAckPos-1] is not valid (NULL or ->m_iFlag!=CUnit::GOOD), - * it means m_pUnits[m_iLastAckPos] is valid since a valid unit is needed to skip. - * Favor m_pUnits[m_iLastAckPos] if valid over [m_iLastAckPos-1] to include the whole acked interval. - */ - if ((m_iMaxPos <= 0) || (!m_pUnit[m_iLastAckPos]) || (m_pUnit[m_iLastAckPos]->m_iFlag != CUnit::GOOD)) - { - endpos = (m_iLastAckPos == 0 ? m_iSize - 1 : m_iLastAckPos - 1); - } - - if ((NULL != m_pUnit[endpos]) && (NULL != m_pUnit[startpos])) - { - const steady_clock::time_point startstamp = - getPktTsbPdTime(m_pUnit[startpos]->m_Packet.getMsgTimeStamp()); - const steady_clock::time_point endstamp = getPktTsbPdTime(m_pUnit[endpos]->m_Packet.getMsgTimeStamp()); - /* - * There are sampling conditions where spantime is < 0 (big unsigned value). - * It has been observed after changing the SRT latency from 450 to 200 on the sender. - * - * Possible packet order corruption when dropping packet, - * cause by bad thread protection when adding packet in queue - * was later discovered and fixed. Security below kept. - * - * DateTime RecvRate LostRate DropRate AvailBw RTT RecvBufs PdDelay - * 2014-12-08T15:04:25-0500 4712 110 0 96509 33.710 393 450 - * 2014-12-08T15:04:35-0500 4512 95 0 107771 33.493 1496542976 200 - * 2014-12-08T15:04:40-0500 4213 106 3 107352 53.657 9499425 200 - * 2014-12-08T15:04:45-0500 4575 104 0 102194 53.614 59666 200 - * 2014-12-08T15:04:50-0500 4475 124 0 100543 53.526 505 200 - */ - if (endstamp > startstamp) - timespan = count_milliseconds(endstamp - startstamp); - } - /* - * Timespan can be less then 1000 us (1 ms) if few packets. - * Also, if there is only one pkt in buffer, the time difference will be 0. - * Therefore, always add 1 ms if not empty. - */ - if (0 < m_iAckedPktsCount) - timespan += 1; - } - } - HLOGF(brlog.Debug, "getRcvDataSize: %6d %6d %6d ms\n", m_iAckedPktsCount, m_iAckedBytesCount, timespan); - bytes = m_iAckedBytesCount; - return m_iAckedPktsCount; -} - -unsigned CRcvBuffer::getRcvAvgPayloadSize() const -{ - return m_uAvgPayloadSz; -} - -CRcvBuffer::ReadingState CRcvBuffer::debugGetReadingState() const -{ - ReadingState readstate; - - readstate.iNumAcknowledged = 0; - readstate.iNumUnacknowledged = m_iMaxPos; - - if ((NULL != m_pUnit[m_iStartPos]) && (m_pUnit[m_iStartPos]->m_iFlag == CUnit::GOOD)) - { - if (m_tsbpd.isEnabled()) - readstate.tsStart = m_tsbpd.getPktTsbPdTime(m_pUnit[m_iStartPos]->m_Packet.getMsgTimeStamp()); - - readstate.iNumAcknowledged = m_iLastAckPos > m_iStartPos - ? m_iLastAckPos - m_iStartPos - : m_iLastAckPos + (m_iSize - m_iStartPos); - } - - // All further stats are valid if TSBPD is enabled. - if (!m_tsbpd.isEnabled()) - return readstate; - - // m_iLastAckPos points to the first unacknowledged packet - const int iLastAckPos = (m_iLastAckPos - 1) % m_iSize; - if (m_iLastAckPos != m_iStartPos && (NULL != m_pUnit[iLastAckPos]) && (m_pUnit[iLastAckPos]->m_iFlag == CUnit::GOOD)) - { - readstate.tsLastAck = m_tsbpd.getPktTsbPdTime(m_pUnit[iLastAckPos]->m_Packet.getMsgTimeStamp()); - } - - const int iEndPos = (m_iLastAckPos + m_iMaxPos - 1) % m_iSize; - if (m_iMaxPos == 0) - { - readstate.tsEnd = readstate.tsLastAck; - } - else if ((NULL != m_pUnit[iEndPos]) && (m_pUnit[iEndPos]->m_iFlag == CUnit::GOOD)) - { - readstate.tsEnd = m_tsbpd.getPktTsbPdTime(m_pUnit[iEndPos]->m_Packet.getMsgTimeStamp()); - } - - return readstate; -} - -string CRcvBuffer::strFullnessState(const time_point& tsNow) const -{ - const ReadingState bufstate = debugGetReadingState(); - stringstream ss; - - ss << "Space avail " << getAvailBufSize() << "/" << m_iSize; - ss << " pkts. Packets ACKed: " << bufstate.iNumAcknowledged; - if (!is_zero(bufstate.tsStart) && !is_zero(bufstate.tsLastAck)) - { - ss << " (TSBPD ready in "; - ss << count_milliseconds(bufstate.tsStart - tsNow); - ss << " : "; - ss << count_milliseconds(bufstate.tsLastAck - tsNow); - ss << " ms)"; - } - - ss << ", not ACKed: " << bufstate.iNumUnacknowledged; - if (!is_zero(bufstate.tsStart) && !is_zero(bufstate.tsEnd)) - { - ss << ", timespan "; - ss << count_milliseconds(bufstate.tsEnd - bufstate.tsStart); - ss << " ms"; - } - - ss << ". " SRT_SYNC_CLOCK_STR " drift " << getDrift() / 1000 << " ms."; - return ss.str(); -} - -void CRcvBuffer::dropMsg(int32_t msgno, bool using_rexmit_flag) -{ - for (int i = m_iStartPos, n = shift(m_iLastAckPos, m_iMaxPos); i != n; i = shiftFwd(i)) - if ((m_pUnit[i] != NULL) && (m_pUnit[i]->m_Packet.getMsgSeq(using_rexmit_flag) == msgno)) - m_pUnit[i]->m_iFlag = CUnit::DROPPED; -} - -void CRcvBuffer::applyGroupTime(const steady_clock::time_point& timebase, - bool wrp, - uint32_t delay, - const steady_clock::duration& udrift) -{ - m_tsbpd.applyGroupTime(timebase, wrp, delay, udrift); -} - -void CRcvBuffer::applyGroupDrift(const steady_clock::time_point& timebase, - bool wrp, - const steady_clock::duration& udrift) -{ - m_tsbpd.applyGroupDrift(timebase, wrp, udrift); -} - -void CRcvBuffer::getInternalTimeBase(steady_clock::time_point& w_timebase, bool& w_wrp, steady_clock::duration& w_udrift) -{ - return m_tsbpd.getInternalTimeBase(w_timebase, w_wrp, w_udrift); -} - -steady_clock::time_point CRcvBuffer::getPktTsbPdTime(uint32_t usPktTimestamp) -{ - // Updating TSBPD time here is not very accurate and prevents from making the function constant. - // For now preserving the existing behavior. - m_tsbpd.updateTsbPdTimeBase(usPktTimestamp); - return m_tsbpd.getPktTsbPdTime(usPktTimestamp); -} - -void CRcvBuffer::setRcvTsbPdMode(const steady_clock::time_point& timebase, const steady_clock::duration& delay) -{ - const bool no_wrap_check = false; - m_tsbpd.setTsbPdMode(timebase, no_wrap_check, delay); -} - -bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t timestamp_us, const time_point& tsPktArrival, int rtt) -{ - return m_tsbpd.addDriftSample(timestamp_us, tsPktArrival, rtt); -} - -int CRcvBuffer::readMsg(char* data, int len) -{ - SRT_MSGCTRL dummy = srt_msgctrl_default; - return readMsg(data, len, (dummy), -1); -} - -// NOTE: The order of ref-arguments is odd because: -// - data and len shall be close to one another -// - upto is last because it's a kind of unusual argument that has a default value -int CRcvBuffer::readMsg(char* data, int len, SRT_MSGCTRL& w_msgctl, int upto) -{ - int p = -1, q = -1; - bool passack; - - bool empty = accessMsg((p), (q), (passack), (w_msgctl.srctime), upto); - if (empty) - return 0; - - // This should happen just once. By 'empty' condition - // we have a guarantee that m_pUnit[p] exists and is valid. - CPacket& pkt1 = m_pUnit[p]->m_Packet; - - // This returns the sequence number and message number to - // the API caller. - w_msgctl.pktseq = pkt1.getSeqNo(); - w_msgctl.msgno = pkt1.getMsgSeq(); - - return extractData((data), len, p, q, passack); -} - -#ifdef SRT_DEBUG_TSBPD_OUTJITTER -void CRcvBuffer::debugTraceJitter(time_point playtime) -{ - uint64_t ms = count_microseconds(steady_clock::now() - playtime); - if (ms / 10 < 10) - m_ulPdHisto[0][ms / 10]++; - else if (ms / 100 < 10) - m_ulPdHisto[1][ms / 100]++; - else if (ms / 1000 < 10) - m_ulPdHisto[2][ms / 1000]++; - else - m_ulPdHisto[3][1]++; -} -#endif /* SRT_DEBUG_TSBPD_OUTJITTER */ - -bool CRcvBuffer::accessMsg(int& w_p, int& w_q, bool& w_passack, int64_t& w_playtime, int upto) -{ - // This function should do the following: - // 1. Find the first packet starting the next message (or just next packet) - // 2. When found something ready for extraction, return true. - // 3. w_p and w_q point the index range for extraction - // 4. passack decides if this range shall be removed after extraction - - bool empty = true; - - if (m_tsbpd.isEnabled()) - { - w_passack = false; - int seq = 0; - - steady_clock::time_point play_time; - const bool isReady = getRcvReadyMsg(play_time, (seq), upto); - w_playtime = count_microseconds(play_time.time_since_epoch()); - - if (isReady) - { - empty = false; - // In TSBPD mode you always read one message - // at a time and a message always fits in one UDP packet, - // so in one "unit". - w_p = w_q = m_iStartPos; - - debugTraceJitter(play_time); - } - } - else - { - w_playtime = 0; - if (scanMsg((w_p), (w_q), (w_passack))) - empty = false; - } - - return empty; -} - -int CRcvBuffer::extractData(char* data, int len, int p, int q, bool passack) -{ - SRT_ASSERT(len > 0); - int rs = len > 0 ? len : 0; - const int past_q = shiftFwd(q); - while (p != past_q) - { - const int pktlen = (int)m_pUnit[p]->m_Packet.getLength(); - // When unitsize is less than pktlen, only a fragment is copied to the output 'data', - // but still the whole packet is removed from the receiver buffer. - if (pktlen > 0) - countBytes(-1, -pktlen, true); - - const int unitsize = ((rs >= 0) && (pktlen > rs)) ? rs : pktlen; - - HLOGC(brlog.Debug, log << "readMsg: checking unit POS=" << p); - - if (unitsize > 0) - { - memcpy((data), m_pUnit[p]->m_Packet.m_pcData, unitsize); - data += unitsize; - rs -= unitsize; - IF_HEAVY_LOGGING(readMsgHeavyLogging(p)); - } - else - { - HLOGC(brlog.Debug, log << CONID() << "readMsg: SKIPPED POS=" << p << " - ZERO SIZE UNIT"); - } - - // Note special case for live mode (one packet per message and TSBPD=on): - // - p == q (that is, this loop passes only once) - // - no passack (the unit is always removed from the buffer) - if (!passack) - { - HLOGC(brlog.Debug, log << CONID() << "readMsg: FREEING UNIT POS=" << p); - freeUnitAt(p); - } - else - { - HLOGC(brlog.Debug, log << CONID() << "readMsg: PASSACK UNIT POS=" << p); - m_pUnit[p]->m_iFlag = CUnit::PASSACK; - } - - p = shiftFwd(p); - } - - if (!passack) - m_iStartPos = past_q; - - HLOGC(brlog.Debug, - log << "rcvBuf/extractData: begin=" << m_iStartPos << " reporting extraction size=" << (len - rs)); - - return len - rs; -} - -string CRcvBuffer::debugTimeState(size_t first_n_pkts) const -{ - stringstream ss; - int ipos = m_iStartPos; - for (size_t i = 0; i < first_n_pkts; ++i, ipos = CSeqNo::incseq(ipos)) - { - const CUnit* unit = m_pUnit[ipos]; - if (!unit) - { - ss << "pkt[" << i << "] missing, "; - continue; - } - - const CPacket& pkt = unit->m_Packet; - ss << "pkt[" << i << "] ts=" << pkt.getMsgTimeStamp() << ", "; - } - return ss.str(); -} - -#if ENABLE_HEAVY_LOGGING -void CRcvBuffer::readMsgHeavyLogging(int p) -{ - static steady_clock::time_point prev_now; - static steady_clock::time_point prev_srctime; - const CPacket& pkt = m_pUnit[p]->m_Packet; - - const int32_t seq = pkt.m_iSeqNo; - - steady_clock::time_point nowtime = steady_clock::now(); - steady_clock::time_point srctime = getPktTsbPdTime(m_pUnit[p]->m_Packet.getMsgTimeStamp()); - - const int64_t timediff_ms = count_milliseconds(nowtime - srctime); - const int64_t nowdiff_ms = is_zero(prev_now) ? count_milliseconds(nowtime - prev_now) : 0; - const int64_t srctimediff_ms = is_zero(prev_srctime) ? count_milliseconds(srctime - prev_srctime) : 0; - - const int next_p = shiftFwd(p); - CUnit* u = m_pUnit[next_p]; - string next_playtime; - if (u && u->m_iFlag == CUnit::GOOD) - { - next_playtime = FormatTime(getPktTsbPdTime(u->m_Packet.getMsgTimeStamp())); - } - else - { - next_playtime = "NONE"; - } - - LOGC(brlog.Debug, - log << CONID() << "readMsg: DELIVERED seq=" << seq << " T=" << FormatTime(srctime) << " in " << timediff_ms - << "ms - TIME-PREVIOUS: PKT: " << srctimediff_ms << " LOCAL: " << nowdiff_ms << " !" - << BufferStamp(pkt.data(), pkt.size()) << " NEXT pkt T=" << next_playtime); - - prev_now = nowtime; - prev_srctime = srctime; -} -#endif - -bool CRcvBuffer::scanMsg(int& w_p, int& w_q, bool& w_passack) -{ - // empty buffer - if ((m_iStartPos == m_iLastAckPos) && (m_iMaxPos <= 0)) - { - HLOGC(brlog.Debug, log << "scanMsg: empty buffer"); - return false; - } - - int rmpkts = 0; - int rmbytes = 0; - // skip all bad msgs at the beginning - // This loop rolls until the "buffer is empty" (head == tail), - // in particular, there's no unit accessible for the reader. - while (m_iStartPos != m_iLastAckPos) - { - // Roll up to the first valid unit - if (!m_pUnit[m_iStartPos]) - { - if (++m_iStartPos == m_iSize) - m_iStartPos = 0; - continue; - } - - // Note: PB_FIRST | PB_LAST == PB_SOLO. - // testing if boundary() & PB_FIRST tests if the msg is first OR solo. - if (m_pUnit[m_iStartPos]->m_iFlag == CUnit::GOOD && m_pUnit[m_iStartPos]->m_Packet.getMsgBoundary() & PB_FIRST) - { - bool good = true; - - // look ahead for the whole message - - // We expect to see either of: - // [PB_FIRST] [PB_SUBSEQUENT] [PB_SUBSEQUENT] [PB_LAST] - // [PB_SOLO] - // but not: - // [PB_FIRST] NULL ... - // [PB_FIRST] FREE/PASSACK/DROPPED... - // If the message didn't look as expected, interrupt this. - - // This begins with a message starting at m_iStartPos - // up to m_iLastAckPos OR until the PB_LAST message is found. - // If any of the units on this way isn't good, this OUTER loop - // will be interrupted. - for (int i = m_iStartPos; i != m_iLastAckPos;) - { - if (!m_pUnit[i] || m_pUnit[i]->m_iFlag != CUnit::GOOD) - { - good = false; - break; - } - - // Likewise, boundary() & PB_LAST will be satisfied for last OR solo. - if (m_pUnit[i]->m_Packet.getMsgBoundary() & PB_LAST) - break; - - if (++i == m_iSize) - i = 0; - } - - if (good) - break; - } - - rmpkts++; - rmbytes += (int) freeUnitAt((size_t) m_iStartPos); - - m_iStartPos = shiftFwd(m_iStartPos); - } - /* we removed bytes form receive buffer */ - countBytes(-rmpkts, -rmbytes, true); - - // Not sure if this is correct, but this above 'while' loop exits - // under the following conditions only: - // - m_iStartPos == m_iLastAckPos (that makes passack = true) - // - found at least GOOD unit with PB_FIRST and not all messages up to PB_LAST are good, - // in which case it returns with m_iStartPos <% m_iLastAckPos (earlier) - // Also all units that lied before m_iStartPos are removed. - - w_p = -1; // message head - w_q = m_iStartPos; // message tail - w_passack = m_iStartPos == m_iLastAckPos; - bool found = false; - - // looking for the first message - //>>m_pUnit[size + m_iMaxPos] is not valid - - // XXX Would be nice to make some very thorough refactoring here. - - // This rolls by q variable from m_iStartPos up to m_iLastAckPos, - // actually from the first message up to the one with PB_LAST - // or PB_SOLO boundary. - - // The 'i' variable used in this loop is just a stub and it's - // even hard to define the unit here. It is "shift towards - // m_iStartPos", so the upper value is m_iMaxPos + size. - // m_iMaxPos is itself relative to m_iLastAckPos, so - // the upper value is m_iMaxPos + difference between - // m_iLastAckPos and m_iStartPos, so that this value is relative - // to m_iStartPos. - // - // The 'i' value isn't used anywhere, although the 'q' value rolls - // in this loop in sync with 'i', with the difference that 'q' is - // wrapped around, and 'i' is just incremented normally. - // - // This makes that this loop rolls in the range by 'q' from - // m_iStartPos to m_iStartPos + UPPER, - // where UPPER = m_iLastAckPos -% m_iStartPos + m_iMaxPos - // This embraces the range from the current reading head up to - // the last packet ever received. - // - // 'passack' is set to true when the 'q' has passed through - // the border of m_iLastAckPos and fallen into the range - // of unacknowledged packets. - - for (int i = 0, n = m_iMaxPos + getRcvDataSize(); i < n; ++i) - { - if (m_pUnit[w_q] && m_pUnit[w_q]->m_iFlag == CUnit::GOOD) - { - // Equivalent pseudocode: - // PacketBoundary bound = m_pUnit[w_q]->m_Packet.getMsgBoundary(); - // if ( IsSet(bound, PB_FIRST) ) - // w_p = w_q; - // if ( IsSet(bound, PB_LAST) && w_p != -1 ) - // found = true; - // - // Not implemented this way because it uselessly check w_p for -1 - // also after setting it explicitly. - - switch (m_pUnit[w_q]->m_Packet.getMsgBoundary()) - { - case PB_SOLO: // 11 - w_p = w_q; - found = true; - break; - - case PB_FIRST: // 10 - w_p = w_q; - break; - - case PB_LAST: // 01 - if (w_p != -1) - found = true; - break; - - case PB_SUBSEQUENT:; // do nothing (caught first, rolling for last) - } - } - else - { - // a hole in this message, not valid, restart search - w_p = -1; - } - - // 'found' is set when the current iteration hit a message with PB_LAST - // (including PB_SOLO since the very first message). - if (found) - { - // the msg has to be ack'ed or it is allowed to read out of order, and was not read before - if (!w_passack || !m_pUnit[w_q]->m_Packet.getMsgOrderFlag()) - { - HLOGC(brlog.Debug, log << "scanMsg: found next-to-broken message, delivering OUT OF ORDER."); - break; - } - - found = false; - } - - if (++w_q == m_iSize) - w_q = 0; - - if (w_q == m_iLastAckPos) - w_passack = true; - } - - // no msg found - if (!found) - { - // NOTE: - // This situation may only happen if: - // - Found a packet with PB_FIRST, so w_p = w_q at the moment when it was found - // - Possibly found following components of that message up to shifted w_q - // - Found no terminal packet (PB_LAST) for that message. - - // if the message is larger than the receiver buffer, return part of the message - if ((w_p != -1) && (shiftFwd(w_q) == w_p)) - { - HLOGC(brlog.Debug, log << "scanMsg: BUFFER FULL and message is INCOMPLETE. Returning PARTIAL MESSAGE."); - found = true; - } - else - { - HLOGC(brlog.Debug, log << "scanMsg: PARTIAL or NO MESSAGE found: p=" << w_p << " q=" << w_q); - } - } - else - { - HLOGC(brlog.Debug, - log << "scanMsg: extracted message p=" << w_p << " q=" << w_q << " (" - << ((w_q - w_p + m_iSize + 1) % m_iSize) << " packets)"); - } - - return found; -} - -#endif // !ENABLE_NEW_RCVBUFFER - -} // namespace srt diff --git a/trunk/3rdparty/srt-1-fit/srtcore/buffer.h b/trunk/3rdparty/srt-1-fit/srtcore/buffer.h deleted file mode 100644 index 48bcb43113..0000000000 --- a/trunk/3rdparty/srt-1-fit/srtcore/buffer.h +++ /dev/null @@ -1,611 +0,0 @@ -/* - * SRT - Secure, Reliable, Transport - * Copyright (c) 2018 Haivision Systems Inc. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - */ - -/***************************************************************************** -Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the - above copyright notice, this list of conditions - and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the University of Illinois - nor the names of its contributors may be used to - endorse or promote products derived from this - software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*****************************************************************************/ - -/***************************************************************************** -written by - Yunhong Gu, last updated 05/05/2009 -modified by - Haivision Systems Inc. -*****************************************************************************/ - -#ifndef INC_SRT_BUFFER_H -#define INC_SRT_BUFFER_H - -#include "udt.h" -#include "list.h" -#include "queue.h" -#include "tsbpd_time.h" -#include "utilities.h" - -// The notation used for "circular numbers" in comments: -// The "cicrular numbers" are numbers that when increased up to the -// maximum become zero, and similarly, when the zero value is decreased, -// it turns into the maximum value minus one. This wrapping works the -// same for adding and subtracting. Circular numbers cannot be multiplied. - -// Operations done on these numbers are marked with additional % character: -// a %> b : a is later than b -// a ++% (++%a) : shift a by 1 forward -// a +% b : shift a by b -// a == b : equality is same as for just numbers - -namespace srt { - -/// The AvgBufSize class is used to calculate moving average of the buffer (RCV or SND) -class AvgBufSize -{ - typedef sync::steady_clock::time_point time_point; - -public: - AvgBufSize() - : m_dBytesCountMAvg(0.0) - , m_dCountMAvg(0.0) - , m_dTimespanMAvg(0.0) - { - } - -public: - bool isTimeToUpdate(const time_point& now) const; - void update(const time_point& now, int pkts, int bytes, int timespan_ms); - -public: - inline double pkts() const { return m_dCountMAvg; } - inline double timespan_ms() const { return m_dTimespanMAvg; } - inline double bytes() const { return m_dBytesCountMAvg; } - -private: - time_point m_tsLastSamplingTime; - double m_dBytesCountMAvg; - double m_dCountMAvg; - double m_dTimespanMAvg; -}; - -/// The class to estimate source bitrate based on samples submitted to the buffer. -/// Is currently only used by the CSndBuffer. -class CRateEstimator -{ - typedef sync::steady_clock::time_point time_point; - typedef sync::steady_clock::duration duration; -public: - CRateEstimator(); - -public: - uint64_t getInRatePeriod() const { return m_InRatePeriod; } - - /// Retrieve input bitrate in bytes per second - int getInputRate() const { return m_iInRateBps; } - - void setInputRateSmpPeriod(int period); - - /// Update input rate calculation. - /// @param [in] time current time in microseconds - /// @param [in] pkts number of packets newly added to the buffer - /// @param [in] bytes number of payload bytes in those newly added packets - /// - /// @return Current size of the data in the sending list. - void updateInputRate(const time_point& time, int pkts = 0, int bytes = 0); - - void resetInputRateSmpPeriod(bool disable = false) { setInputRateSmpPeriod(disable ? 0 : INPUTRATE_FAST_START_US); } - -private: // Constants - static const uint64_t INPUTRATE_FAST_START_US = 500000; // 500 ms - static const uint64_t INPUTRATE_RUNNING_US = 1000000; // 1000 ms - static const int64_t INPUTRATE_MAX_PACKETS = 2000; // ~ 21 Mbps of 1316 bytes payload - static const int INPUTRATE_INITIAL_BYTESPS = BW_INFINITE; - -private: - int m_iInRatePktsCount; // number of payload bytes added since InRateStartTime - int m_iInRateBytesCount; // number of payload bytes added since InRateStartTime - time_point m_tsInRateStartTime; - uint64_t m_InRatePeriod; // usec - int m_iInRateBps; // Input Rate in Bytes/sec -}; - -class CSndBuffer -{ - typedef sync::steady_clock::time_point time_point; - typedef sync::steady_clock::duration duration; - -public: - // XXX There's currently no way to access the socket ID set for - // whatever the buffer is currently working for. Required to find - // some way to do this, possibly by having a "reverse pointer". - // Currently just "unimplemented". - std::string CONID() const { return ""; } - - /// @brief CSndBuffer constructor. - /// @param size initial number of blocks (each block to store one packet payload). - /// @param maxpld maximum packet payload. - CSndBuffer(int size = 32, int maxpld = 1500); - ~CSndBuffer(); - -public: - /// Insert a user buffer into the sending list. - /// For @a w_mctrl the following fields are used: - /// INPUT: - /// - msgttl: timeout for retransmitting the message, if lost - /// - inorder: request to deliver the message in order of sending - /// - srctime: local time as a base for packet's timestamp (0 if unused) - /// - pktseq: sequence number to be stamped on the packet (-1 if unused) - /// - msgno: message number to be stamped on the packet (-1 if unused) - /// OUTPUT: - /// - srctime: local time stamped on the packet (same as input, if input wasn't 0) - /// - pktseq: sequence number to be stamped on the next packet - /// - msgno: message number stamped on the packet - /// @param [in] data pointer to the user data block. - /// @param [in] len size of the block. - /// @param [inout] w_mctrl Message control data - SRT_ATTR_EXCLUDES(m_BufLock) - void addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl); - - /// Read a block of data from file and insert it into the sending list. - /// @param [in] ifs input file stream. - /// @param [in] len size of the block. - /// @return actual size of data added from the file. - SRT_ATTR_EXCLUDES(m_BufLock) - int addBufferFromFile(std::fstream& ifs, int len); - - /// Find data position to pack a DATA packet from the furthest reading point. - /// @param [out] packet the packet to read. - /// @param [out] origintime origin time stamp of the message - /// @param [in] kflags Odd|Even crypto key flag - /// @param [out] seqnoinc the number of packets skipped due to TTL, so that seqno should be incremented. - /// @return Actual length of data read. - SRT_ATTR_EXCLUDES(m_BufLock) - int readData(CPacket& w_packet, time_point& w_origintime, int kflgs, int& w_seqnoinc); - - /// Peek an information on the next original data packet to send. - /// @return origin time stamp of the next packet; epoch start time otherwise. - SRT_ATTR_EXCLUDES(m_BufLock) - time_point peekNextOriginal() const; - - /// Find data position to pack a DATA packet for a retransmission. - /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// @param [out] packet the packet to read. - /// @param [out] origintime origin time stamp of the message - /// @param [out] msglen length of the message - /// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded). - SRT_ATTR_EXCLUDES(m_BufLock) - int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); - - /// Get the time of the last retransmission (if any) of the DATA packet. - /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// - /// @return Last time of the last retransmission event for the corresponding DATA packet. - SRT_ATTR_EXCLUDES(m_BufLock) - time_point getPacketRexmitTime(const int offset); - - /// Update the ACK point and may release/unmap/return the user data according to the flag. - /// @param [in] offset number of packets acknowledged. - int32_t getMsgNoAt(const int offset); - - void ackData(int offset); - - /// Read size of data still in the sending list. - /// @return Current size of the data in the sending list. - int getCurrBufSize() const; - - SRT_ATTR_EXCLUDES(m_BufLock) - int dropLateData(int& bytes, int32_t& w_first_msgno, const time_point& too_late_time); - - void updAvgBufSize(const time_point& time); - int getAvgBufSize(int& bytes, int& timespan); - int getCurrBufSize(int& bytes, int& timespan); - - /// @brief Get the buffering delay of the oldest message in the buffer. - /// @return the delay value. - SRT_ATTR_EXCLUDES(m_BufLock) - duration getBufferingDelay(const time_point& tnow) const; - - uint64_t getInRatePeriod() const { return m_rateEstimator.getInRatePeriod(); } - - /// Retrieve input bitrate in bytes per second - int getInputRate() const { return m_rateEstimator.getInputRate(); } - - void resetInputRateSmpPeriod(bool disable = false) { m_rateEstimator.resetInputRateSmpPeriod(disable); } - - const CRateEstimator& getRateEstimator() const { return m_rateEstimator; } - - void setRateEstimator(const CRateEstimator& other) { m_rateEstimator = other; } - -private: - void increase(); - -private: - mutable sync::Mutex m_BufLock; // used to synchronize buffer operation - - struct Block - { - char* m_pcData; // pointer to the data block - int m_iLength; // payload length of the block. - - int32_t m_iMsgNoBitset; // message number - int32_t m_iSeqNo; // sequence number for scheduling - time_point m_tsOriginTime; // block origin time (either provided from above or equals the time a message was submitted for sending. - time_point m_tsRexmitTime; // packet retransmission time - int m_iTTL; // time to live (milliseconds) - - Block* m_pNext; // next block - - int32_t getMsgSeq() - { - // NOTE: this extracts message ID with regard to REXMIT flag. - // This is valid only for message ID that IS GENERATED in this instance, - // not provided by the peer. This can be otherwise sent to the peer - it doesn't matter - // for the peer that it uses LESS bits to represent the message. - return m_iMsgNoBitset & MSGNO_SEQ::mask; - } - - } * m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock; - - // m_pBlock: The head pointer - // m_pFirstBlock: The first block - // m_pCurrBlock: The current block - // m_pLastBlock: The last block (if first == last, buffer is empty) - - struct Buffer - { - char* m_pcData; // buffer - int m_iSize; // size - Buffer* m_pNext; // next buffer - } * m_pBuffer; // physical buffer - - int32_t m_iNextMsgNo; // next message number - - int m_iSize; // buffer size (number of packets) - const int m_iBlockLen; // maximum length of a block holding packet payload (excluding packet header). - int m_iCount; // number of used blocks - - int m_iBytesCount; // number of payload bytes in queue - time_point m_tsLastOriginTime; - - AvgBufSize m_mavg; - CRateEstimator m_rateEstimator; - -private: - CSndBuffer(const CSndBuffer&); - CSndBuffer& operator=(const CSndBuffer&); -}; - -//////////////////////////////////////////////////////////////////////////////// - -#if (!ENABLE_NEW_RCVBUFFER) - -class CRcvBuffer -{ - typedef sync::steady_clock::time_point time_point; - typedef sync::steady_clock::duration duration; - -public: - // XXX There's currently no way to access the socket ID set for - // whatever the queue is currently working for. Required to find - // some way to do this, possibly by having a "reverse pointer". - // Currently just "unimplemented". - std::string CONID() const { return ""; } - - static const int DEFAULT_SIZE = 65536; - /// Construct the buffer. - /// @param [in] queue CUnitQueue that actually holds the units (packets) - /// @param [in] bufsize_pkts in units (packets) - CRcvBuffer(CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE); - ~CRcvBuffer(); - -public: - /// Write data into the buffer. - /// @param [in] unit pointer to a data unit containing new packet - /// @param [in] offset offset from last ACK point. - /// @return 0 is success, -1 if data is repeated. - int addData(CUnit* unit, int offset); - - /// Read data into a user buffer. - /// @param [in] data pointer to user buffer. - /// @param [in] len length of user buffer. - /// @return size of data read. - int readBuffer(char* data, int len); - - /// Read data directly into file. - /// @param [in] file C++ file stream. - /// @param [in] len expected length of data to write into the file. - /// @return size of data read. - int readBufferToFile(std::fstream& ofs, int len); - - /// Update the ACK point of the buffer. - /// @param [in] len number of units to be acknowledged. - /// @return 1 if a user buffer is fulfilled, otherwise 0. - int ackData(int len); - - /// Query how many buffer space left for data receiving. - /// Actually only acknowledged packets, that are still in the buffer, - /// are considered to take buffer space. - /// - /// @return size of available buffer space (including user buffer) for data receiving. - /// Not counting unacknowledged packets. - int getAvailBufSize() const; - - /// Query how many data has been continuously received (for reading) and ready to play (tsbpdtime < now). - /// @return size of valid (continous) data for reading. - int getRcvDataSize() const; - - /// Query how many data was received and acknowledged. - /// @param [out] bytes bytes - /// @param [out] spantime spantime - /// @return size in pkts of acked data. - int getRcvDataSize(int& bytes, int& spantime); - - /// Query a 1 sec moving average of how many data was received and acknowledged. - /// @param [out] bytes bytes - /// @param [out] spantime spantime - /// @return size in pkts of acked data. - int getRcvAvgDataSize(int& bytes, int& spantime); - - /// Query how many data of the receive buffer is acknowledged. - /// @param [in] now current time in us. - /// @return none. - void updRcvAvgDataSize(const time_point& now); - - /// Query the received average payload size. - /// @return size (bytes) of payload size - unsigned getRcvAvgPayloadSize() const; - - struct ReadingState - { - time_point tsStart; - time_point tsLastAck; - time_point tsEnd; - int iNumAcknowledged; - int iNumUnacknowledged; - }; - - ReadingState debugGetReadingState() const; - - /// Form a string of the current buffer fullness state. - /// number of packets acknowledged, TSBPD readiness, etc. - std::string strFullnessState(const time_point& tsNow) const; - - /// Mark the message to be dropped from the message list. - /// @param [in] msgno message number. - /// @param [in] using_rexmit_flag whether the MSGNO field uses rexmit flag (if not, one more bit is part of the - /// msgno value) - void dropMsg(int32_t msgno, bool using_rexmit_flag); - - /// read a message. - /// @param [out] data buffer to write the message into. - /// @param [in] len size of the buffer. - /// @return actuall size of data read. - int readMsg(char* data, int len); - -#if ENABLE_HEAVY_LOGGING - void readMsgHeavyLogging(int p); -#endif - - /// read a message. - /// @param [out] data buffer to write the message into. - /// @param [in] len size of the buffer. - /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay - /// @return actuall size of data read. - int readMsg(char* data, int len, SRT_MSGCTRL& w_mctrl, int upto); - - /// Query if data is ready to read (tsbpdtime <= now if TsbPD is active). - /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay - /// of next packet in recv buffer, ready or not. - /// @param [out] curpktseq Sequence number of the packet if there is one ready to play - /// @return true if ready to play, false otherwise (tsbpdtime may be !0 in - /// both cases). - bool isRcvDataReady(time_point& w_tsbpdtime, int32_t& w_curpktseq, int32_t seqdistance); - -#ifdef SRT_DEBUG_TSBPD_OUTJITTER - void debugTraceJitter(time_point t); -#else - void debugTraceJitter(time_point) {} -#endif /* SRT_DEBUG_TSBPD_OUTJITTER */ - - bool isRcvDataReady(); - bool isRcvDataAvailable() { return m_iLastAckPos != m_iStartPos; } - CPacket* getRcvReadyPacket(int32_t seqdistance); - - /// Set TimeStamp-Based Packet Delivery Rx Mode - /// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay - /// @param [in] delay aggreed TsbPD delay - void setRcvTsbPdMode(const time_point& timebase, const duration& delay); - - /// Add packet timestamp for drift caclculation and compensation - /// @param [in] timestamp packet time stamp - /// @param [in] tsPktArrival arrival time of the packet used to extract the drift sample. - /// @param [in] rtt RTT sample - bool addRcvTsbPdDriftSample(uint32_t timestamp, const time_point& tsPktArrival, int rtt); - -#ifdef SRT_DEBUG_TSBPD_DRIFT - void printDriftHistogram(int64_t iDrift); - void printDriftOffset(int tsbPdOffset, int tsbPdDriftAvg); -#endif - - /// Get information on the 1st message in queue. - // Parameters (of the 1st packet queue, ready to play or not): - /// @param [out] w_tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 - /// if none - /// @param [out] w_passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app) - /// @param [out] w_skipseqno SRT_SEQNO_NONE or seq number of 1st unacknowledged pkt ready to play preceeded by - /// missing packets. - /// @param base_seq SRT_SEQNO_NONE or desired, ignore seq smaller than base if exist packet ready-to-play - /// and larger than base - /// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true - /// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE: - /// IF skipseqno != SRT_SEQNO_NONE, packet ready to play preceeded by missing packets.; - /// IF skipseqno == SRT_SEQNO_NONE, no missing packet but 1st not ready to play. - bool getRcvFirstMsg(time_point& w_tsbpdtime, - bool& w_passack, - int32_t& w_skipseqno, - int32_t& w_curpktseq, - int32_t base_seq = SRT_SEQNO_NONE); - - /// Update the ACK point of the buffer. - /// @param [in] len size of data to be skip & acknowledged. - void skipData(int len); - -#if ENABLE_HEAVY_LOGGING - void reportBufferStats() const; // Heavy logging Debug only -#endif - bool empty() const - { - // This will not always return the intended value, - // that is, it may return false when the buffer really is - // empty - but it will return true then in one of next calls. - // This function will be always called again at some point - // if it returned false, and on true the connection - // is going to be broken - so this behavior is acceptable. - return m_iStartPos == m_iLastAckPos; - } - bool full() const { return m_iStartPos == (m_iLastAckPos + 1) % m_iSize; } - int capacity() const { return m_iSize; } - -private: - /// This gives up unit at index p. The unit is given back to the - /// free unit storage for further assignment for the new incoming - /// data. - size_t freeUnitAt(size_t p) - { - CUnit* u = m_pUnit[p]; - m_pUnit[p] = NULL; - size_t rmbytes = u->m_Packet.getLength(); - m_pUnitQueue->makeUnitFree(u); - return rmbytes; - } - - /// Adjust receive queue to 1st ready to play message (tsbpdtime < now). - /// Parameters (of the 1st packet queue, ready to play or not): - /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if - /// none - /// @param base_seq SRT_SEQNO_NONE or desired, ignore seq smaller than base - /// @retval true 1st packet ready to play without discontinuity (no hole) - /// @retval false tsbpdtime = 0: no packet ready to play - bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto, int base_seq = SRT_SEQNO_NONE); - -public: - /// @brief Get clock drift in microseconds. - int64_t getDrift() const { return m_tsbpd.drift(); } - -public: - int32_t getTopMsgno() const; - - void getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift); - - void applyGroupTime(const time_point& timebase, bool wrapcheck, uint32_t delay, const duration& udrift); - void applyGroupDrift(const time_point& timebase, bool wrapcheck, const duration& udrift); - time_point getPktTsbPdTime(uint32_t timestamp); - int debugGetSize() const; - time_point debugGetDeliveryTime(int offset); - - size_t dropData(int len); - -private: - int extractData(char* data, int len, int p, int q, bool passack); - bool accessMsg(int& w_p, int& w_q, bool& w_passack, int64_t& w_playtime, int upto); - - /// Describes the state of the first N packets - std::string debugTimeState(size_t first_n_pkts) const; - - /// thread safe bytes counter of the Recv & Ack buffer - /// @param [in] pkts acked or removed pkts from rcv buffer (used with acked = true) - /// @param [in] bytes number of bytes added/delete (if negative) to/from rcv buffer. - /// @param [in] acked true when adding new pkt in RcvBuffer; false when acking/removing pkts to/from buffer - void countBytes(int pkts, int bytes, bool acked = false); - -private: - bool scanMsg(int& w_start, int& w_end, bool& w_passack); - - int shift(int basepos, int shift) const { return (basepos + shift) % m_iSize; } - - /// Simplified versions with ++ and --; avoid using division instruction - int shiftFwd(int basepos) const - { - if (++basepos == m_iSize) - return 0; - return basepos; - } - - int shiftBack(int basepos) const - { - if (basepos == 0) - return m_iSize - 1; - return --basepos; - } - -private: - CUnit** m_pUnit; // Array of pointed units collected in the buffer - const int m_iSize; // Size of the internal array of CUnit* items - CUnitQueue* m_pUnitQueue; // the shared unit queue - - int m_iStartPos; // HEAD: first packet available for reading - int m_iLastAckPos; // the last ACKed position (exclusive), follows the last readable - // EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1 - int m_iMaxPos; // delta between acked-TAIL and reception-TAIL - - int m_iNotch; // the starting read point of the first unit - // (this is required for stream reading mode; it's - // the position in the first unit in the list - // up to which data are already retrieved; - // in message reading mode it's unused and always 0) - - sync::Mutex m_BytesCountLock; // used to protect counters operations - int m_iBytesCount; // Number of payload bytes in the buffer - int m_iAckedPktsCount; // Number of acknowledged pkts in the buffer - int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer - unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation - - CTsbpdTime m_tsbpd; - - AvgBufSize m_mavg; - -private: - CRcvBuffer(); - CRcvBuffer(const CRcvBuffer&); - CRcvBuffer& operator=(const CRcvBuffer&); -}; - -#endif // !ENABLE_NEW_RCVBUFFER - -} // namespace srt - -#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/buffer_rcv.cpp b/trunk/3rdparty/srt-1-fit/srtcore/buffer_rcv.cpp index 8ecbd10fe1..1f46788aab 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/buffer_rcv.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/buffer_rcv.cpp @@ -1,4 +1,48 @@ -#if ENABLE_NEW_RCVBUFFER +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +/***************************************************************************** +Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + #include #include #include "buffer_rcv.h" @@ -31,15 +75,15 @@ namespace { #define IF_RCVBUF_DEBUG(instr) (void)0 - // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosInc) % iSize]. + // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. // The right edge is included because we expect iFirstNonreadPos to be // right after the last valid packet position if all packets are available. - bool isInRange(int iStartPos, int iMaxPosInc, size_t iSize, int iFirstNonreadPos) + bool isInRange(int iStartPos, int iMaxPosOff, size_t iSize, int iFirstNonreadPos) { if (iFirstNonreadPos == iStartPos) return true; - const int iLastPos = (iStartPos + iMaxPosInc) % iSize; + const int iLastPos = (iStartPos + iMaxPosOff) % iSize; const bool isOverrun = iLastPos < iStartPos; if (isOverrun) @@ -54,7 +98,7 @@ namespace { * RcvBufferNew (circular buffer): * * |<------------------- m_iSize ----------------------------->| - * | |<----------- m_iMaxPosInc ------------>| | + * | |<----------- m_iMaxPosOff ------------>| | * | | | | * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] @@ -68,17 +112,17 @@ namespace { * thread safety: * m_iStartPos: CUDT::m_RecvLock * m_iLastAckPos: CUDT::m_AckLock - * m_iMaxPosInc: none? (modified on add and ack + * m_iMaxPosOff: none? (modified on add and ack */ -CRcvBufferNew::CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI) +CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI) : m_entries(size) , m_szSize(size) // TODO: maybe just use m_entries.size() , m_pUnitQueue(unitqueue) , m_iStartSeqNo(initSeqNo) , m_iStartPos(0) , m_iFirstNonreadPos(0) - , m_iMaxPosInc(0) + , m_iMaxPosOff(0) , m_iNotch(0) , m_numOutOfOrderPackets(0) , m_iFirstReadableOutOfOrder(-1) @@ -91,9 +135,9 @@ CRcvBufferNew::CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, SRT_ASSERT(size < size_t(std::numeric_limits::max())); // All position pointers are integers } -CRcvBufferNew::~CRcvBufferNew() +CRcvBuffer::~CRcvBuffer() { - // Can be optimized by only iterating m_iMaxPosInc from m_iStartPos. + // Can be optimized by only iterating m_iMaxPosOff from m_iStartPos. for (FixedArray::iterator it = m_entries.begin(); it != m_entries.end(); ++it) { if (!it->pUnit) @@ -104,14 +148,14 @@ CRcvBufferNew::~CRcvBufferNew() } } -int CRcvBufferNew::insert(CUnit* unit) +int CRcvBuffer::insert(CUnit* unit) { SRT_ASSERT(unit != NULL); const int32_t seqno = unit->m_Packet.getSeqNo(); const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); IF_RCVBUF_DEBUG(ScopedLog scoped_log); - IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::insert: seqno " << seqno); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::insert: seqno " << seqno); IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << unit->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); IF_RCVBUF_DEBUG(scoped_log.ss << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); @@ -132,8 +176,8 @@ int CRcvBufferNew::insert(CUnit* unit) SRT_ASSERT((m_iStartPos + offset) / m_szSize < 2); const int pos = (m_iStartPos + offset) % m_szSize; - if (offset >= m_iMaxPosInc) - m_iMaxPosInc = offset + 1; + if (offset >= m_iMaxPosOff) + m_iMaxPosOff = offset + 1; // Packet already exists SRT_ASSERT(pos >= 0 && pos < int(m_szSize)); @@ -144,7 +188,7 @@ int CRcvBufferNew::insert(CUnit* unit) } SRT_ASSERT(m_entries[pos].pUnit == NULL); - m_pUnitQueue->makeUnitGood(unit); + m_pUnitQueue->makeUnitTaken(unit); m_entries[pos].pUnit = unit; m_entries[pos].status = EntryState_Avail; countBytes(1, (int)unit->m_Packet.getLength()); @@ -162,10 +206,10 @@ int CRcvBufferNew::insert(CUnit* unit) return 0; } -int CRcvBufferNew::dropUpTo(int32_t seqno) +int CRcvBuffer::dropUpTo(int32_t seqno) { IF_RCVBUF_DEBUG(ScopedLog scoped_log); - IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); if (len <= 0) @@ -174,9 +218,9 @@ int CRcvBufferNew::dropUpTo(int32_t seqno) return 0; } - m_iMaxPosInc -= len; - if (m_iMaxPosInc < 0) - m_iMaxPosInc = 0; + m_iMaxPosOff -= len; + if (m_iMaxPosOff < 0) + m_iMaxPosOff = 0; const int iDropCnt = len; while (len > 0) @@ -192,91 +236,87 @@ int CRcvBufferNew::dropUpTo(int32_t seqno) m_iStartSeqNo = seqno; // Move forward if there are "read/drop" entries. releaseNextFillerEntries(); - // Set nonread position to the starting position before updating, - // because start position was increased, and preceeding packets are invalid. - m_iFirstNonreadPos = m_iStartPos; - updateNonreadPos(); + + // If the nonread position is now behind the starting position, set it to the starting position and update. + // Preceding packets were likely missing, and the non read position can probably be moved further now. + if (CSeqNo::seqcmp(m_iFirstNonreadPos, m_iStartPos) < 0) + { + m_iFirstNonreadPos = m_iStartPos; + updateNonreadPos(); + } if (!m_tsbpd.isEnabled() && m_bMessageAPI) updateFirstReadableOutOfOrder(); return iDropCnt; } -int CRcvBufferNew::dropAll() +int CRcvBuffer::dropAll() { if (empty()) return 0; - const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosInc); + const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); return dropUpTo(end_seqno); } -int CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) +int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, DropActionIfExists actionOnExisting) { IF_RCVBUF_DEBUG(ScopedLog scoped_log); - IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::dropMessage: seqnolo " << seqnolo << " seqnohi " << seqnohi << " m_iStartSeqNo " << m_iStartSeqNo); - // TODO: count bytes as removed? - const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); - if (msgno != 0) - { - IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << msgno); - int minDroppedOffset = -1; - int iDropCnt = 0; - for (int i = m_iStartPos; i != end_pos; i = incPos(i)) - { - // TODO: Maybe check status? - if (!m_entries[i].pUnit) - continue; - - // TODO: Break the loop if a massege has been found. No need to search further. - const int32_t msgseq = m_entries[i].pUnit->m_Packet.getMsgSeq(m_bPeerRexmitFlag); - if (msgseq == msgno) - { - ++iDropCnt; - dropUnitInPos(i); - m_entries[i].status = EntryState_Drop; - if (minDroppedOffset == -1) - minDroppedOffset = offPos(m_iStartPos, i); - } - } - IF_RCVBUF_DEBUG(scoped_log.ss << " iDropCnt " << iDropCnt); - // Check if units before m_iFirstNonreadPos are dropped. - bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); - releaseNextFillerEntries(); - if (needUpdateNonreadPos) - { - m_iFirstNonreadPos = m_iStartPos; - updateNonreadPos(); - } - if (!m_tsbpd.isEnabled() && m_bMessageAPI) - { - if (!checkFirstReadableOutOfOrder()) - m_iFirstReadableOutOfOrder = -1; - updateFirstReadableOutOfOrder(); - } - return iDropCnt; - } + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropMessage(): %(" << seqnolo << " - " << seqnohi << ")" + << " #" << msgno << " actionOnExisting=" << actionOnExisting << " m_iStartSeqNo=%" + << m_iStartSeqNo); - // Drop by packet seqno range. + // Drop by packet seqno range to also wipe those packets that do not exist in the buffer. const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); if (offset_b < 0) { - LOGC(rbuflog.Debug, log << "CRcvBufferNew.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " - << seqnohi << "]. Buffer start " << m_iStartSeqNo << "."); + LOGC(rbuflog.Debug, log << "CRcvBuffer.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " + << seqnohi << "]. Buffer start " << m_iStartSeqNo << "."); return 0; } - const int start_off = max(0, offset_a); - const int last_pos = incPos(m_iStartPos, offset_b); + const bool bKeepExisting = (actionOnExisting == KEEP_EXISTING); int minDroppedOffset = -1; int iDropCnt = 0; - for (int i = incPos(m_iStartPos, start_off); i != end_pos && i != last_pos; i = incPos(i)) + const int start_off = max(0, offset_a); + const int start_pos = incPos(m_iStartPos, start_off); + const int end_off = min((int) m_szSize - 1, offset_b + 1); + const int end_pos = incPos(m_iStartPos, end_off); + bool bDropByMsgNo = msgno > SRT_MSGNO_CONTROL; // Excluding both SRT_MSGNO_NONE (-1) and SRT_MSGNO_CONTROL (0). + for (int i = start_pos; i != end_pos; i = incPos(i)) { - // Don't drop messages, if all its packets are already in the buffer. - // TODO: Don't drop a several-packet message if all packets are in the buffer. - if (m_entries[i].pUnit && m_entries[i].pUnit->m_Packet.getMsgBoundary() == PB_SOLO) + // Check if the unit was already dropped earlier. + if (m_entries[i].status == EntryState_Drop) continue; + if (m_entries[i].pUnit) + { + const PacketBoundary bnd = packetAt(i).getMsgBoundary(); + + // Don't drop messages, if all its packets are already in the buffer. + // TODO: Don't drop a several-packet message if all packets are in the buffer. + if (bKeepExisting && bnd == PB_SOLO) + { + bDropByMsgNo = false; // Solo packet, don't search for the rest of the message. + LOGC(rbuflog.Debug, + log << "CRcvBuffer::dropMessage(): Skipped dropping an existing SOLO packet %" + << packetAt(i).getSeqNo() << "."); + continue; + } + + const int32_t msgseq = packetAt(i).getMsgSeq(m_bPeerRexmitFlag); + if (msgno > SRT_MSGNO_CONTROL && msgseq != msgno) + { + LOGC(rbuflog.Warn, log << "CRcvBuffer.dropMessage(): Packet seqno %" << packetAt(i).getSeqNo() << " has msgno " << msgseq << " differs from requested " << msgno); + } + + if (bDropByMsgNo && bnd == PB_FIRST) + { + // First packet of the message is about to be dropped. That was the only reason to search for msgno. + bDropByMsgNo = false; + } + } + dropUnitInPos(i); ++iDropCnt; m_entries[i].status = EntryState_Drop; @@ -284,11 +324,48 @@ int CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) minDroppedOffset = offPos(m_iStartPos, i); } - LOGC(rbuflog.Debug, log << "CRcvBufferNew.dropMessage(): [" << seqnolo << "; " - << seqnohi << "]."); + if (bDropByMsgNo) + { + // If msgno is specified, potentially not the whole message was dropped using seqno range. + // The sender might have removed the first packets of the message, and thus @a seqnolo may point to a packet in the middle. + // The sender should have the last packet of the message it is requesting to be dropped. + // Therefore we don't search forward, but need to check earlier packets in the RCV buffer. + // Try to drop by the message number in case the message starts earlier than @a seqnolo. + const int stop_pos = decPos(m_iStartPos); + for (int i = start_pos; i != stop_pos; i = decPos(i)) + { + // Can't drop if message number is not known. + if (!m_entries[i].pUnit) // also dropped earlier. + continue; + + const PacketBoundary bnd = packetAt(i).getMsgBoundary(); + const int32_t msgseq = packetAt(i).getMsgSeq(m_bPeerRexmitFlag); + if (msgseq != msgno) + break; + + if (bKeepExisting && bnd == PB_SOLO) + { + LOGC(rbuflog.Debug, + log << "CRcvBuffer::dropMessage(): Skipped dropping an existing SOLO message packet %" + << packetAt(i).getSeqNo() << "."); + break; + } + + ++iDropCnt; + dropUnitInPos(i); + m_entries[i].status = EntryState_Drop; + // As the search goes backward, i is always earlier than minDroppedOffset. + minDroppedOffset = offPos(m_iStartPos, i); + + // Break the loop if the start of the message has been found. No need to search further. + if (bnd == PB_FIRST) + break; + } + IF_RCVBUF_DEBUG(scoped_log.ss << " iDropCnt " << iDropCnt); + } // Check if units before m_iFirstNonreadPos are dropped. - bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); + const bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); releaseNextFillerEntries(); if (needUpdateNonreadPos) { @@ -305,19 +382,19 @@ int CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) return iDropCnt; } -int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) +int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) { const bool canReadInOrder = hasReadableInorderPkts(); if (!canReadInOrder && m_iFirstReadableOutOfOrder < 0) { - LOGC(rbuflog.Warn, log << "CRcvBufferNew.readMessage(): nothing to read. Ignored isRcvDataReady() result?"); + LOGC(rbuflog.Warn, log << "CRcvBuffer.readMessage(): nothing to read. Ignored isRcvDataReady() result?"); return 0; } const int readPos = canReadInOrder ? m_iStartPos : m_iFirstReadableOutOfOrder; IF_RCVBUF_DEBUG(ScopedLog scoped_log); - IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::readMessage. m_iStartSeqNo " << m_iStartSeqNo << " m_iStartPos " << m_iStartPos << " readPos " << readPos); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::readMessage. m_iStartSeqNo " << m_iStartSeqNo << " m_iStartPos " << m_iStartPos << " readPos " << readPos); size_t remain = len; char* dst = data; @@ -329,11 +406,11 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) SRT_ASSERT(m_entries[i].pUnit); if (!m_entries[i].pUnit) { - LOGC(rbuflog.Error, log << "CRcvBufferNew::readMessage(): null packet encountered."); + LOGC(rbuflog.Error, log << "CRcvBuffer::readMessage(): null packet encountered."); break; } - const CPacket& packet = m_entries[i].pUnit->m_Packet; + const CPacket& packet = packetAt(i); const size_t pktsize = packet.getLength(); const int32_t pktseqno = packet.getSeqNo(); @@ -368,8 +445,8 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) if (updateStartPos) { m_iStartPos = incPos(i); - --m_iMaxPosInc; - SRT_ASSERT(m_iMaxPosInc >= 0); + --m_iMaxPosOff; + SRT_ASSERT(m_iMaxPosOff >= 0); m_iStartSeqNo = CSeqNo::incseq(pktseqno); } else @@ -390,7 +467,7 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) releaseNextFillerEntries(); - if (!isInRange(m_iStartPos, m_iMaxPosInc, m_szSize, m_iFirstNonreadPos)) + if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; //updateNonreadPos(); @@ -440,7 +517,7 @@ namespace { } } -int CRcvBufferNew::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) +int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) { int p = m_iStartPos; const int end_pos = m_iFirstNonreadPos; @@ -458,7 +535,7 @@ int CRcvBufferNew::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) return -1; } - const srt::CPacket& pkt = m_entries[p].pUnit->m_Packet; + const srt::CPacket& pkt = packetAt(p); if (bTsbPdEnabled) { @@ -486,8 +563,8 @@ int CRcvBufferNew::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) m_iNotch = 0; m_iStartPos = p; - --m_iMaxPosInc; - SRT_ASSERT(m_iMaxPosInc >= 0); + --m_iMaxPosOff; + SRT_ASSERT(m_iMaxPosOff >= 0); m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); } else @@ -502,8 +579,8 @@ int CRcvBufferNew::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) // Update positions // Set nonread position to the starting position before updating, - // because start position was increased, and preceeding packets are invalid. - if (!isInRange(m_iStartPos, m_iMaxPosInc, m_szSize, m_iFirstNonreadPos)) + // because start position was increased, and preceding packets are invalid. + if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; } @@ -516,22 +593,22 @@ int CRcvBufferNew::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) return iBytesRead; } -int CRcvBufferNew::readBuffer(char* dst, int len) +int CRcvBuffer::readBuffer(char* dst, int len) { return readBufferTo(len, copyBytesToBuf, reinterpret_cast(dst)); } -int CRcvBufferNew::readBufferToFile(fstream& ofs, int len) +int CRcvBuffer::readBufferToFile(fstream& ofs, int len) { return readBufferTo(len, writeBytesToFile, reinterpret_cast(&ofs)); } -bool CRcvBufferNew::hasAvailablePackets() const +bool CRcvBuffer::hasAvailablePackets() const { return hasReadableInorderPkts() || (m_numOutOfOrderPackets > 0 && m_iFirstReadableOutOfOrder != -1); } -int CRcvBufferNew::getRcvDataSize() const +int CRcvBuffer::getRcvDataSize() const { if (m_iFirstNonreadPos >= m_iStartPos) return m_iFirstNonreadPos - m_iStartPos; @@ -539,36 +616,40 @@ int CRcvBufferNew::getRcvDataSize() const return int(m_szSize + m_iFirstNonreadPos - m_iStartPos); } -int CRcvBufferNew::getTimespan_ms() const +int CRcvBuffer::getTimespan_ms() const { if (!m_tsbpd.isEnabled()) return 0; - if (m_iMaxPosInc == 0) + if (m_iMaxPosOff == 0) return 0; - const int lastpos = incPos(m_iStartPos, m_iMaxPosInc - 1); - int startpos = m_iStartPos; - - while (m_entries[startpos].pUnit == NULL) + int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); + // Normally the last position should always be non empty + // if TSBPD is enabled (reading out of order is not allowed). + // However if decryption of the last packet fails, it may be dropped + // from the buffer (AES-GCM), and the position will be empty. + SRT_ASSERT(m_entries[lastpos].pUnit != NULL || m_entries[lastpos].status == EntryState_Drop); + while (m_entries[lastpos].pUnit == NULL && lastpos != m_iStartPos) { - if (startpos == lastpos) - break; + lastpos = decPos(lastpos); + } + + if (m_entries[lastpos].pUnit == NULL) + return 0; + int startpos = m_iStartPos; + while (m_entries[startpos].pUnit == NULL && startpos != lastpos) + { startpos = incPos(startpos); } if (m_entries[startpos].pUnit == NULL) return 0; - // Should not happen - SRT_ASSERT(m_entries[lastpos].pUnit != NULL); - if (m_entries[lastpos].pUnit == NULL) - return 0; - const steady_clock::time_point startstamp = - getPktTsbPdTime(m_entries[startpos].pUnit->m_Packet.getMsgTimeStamp()); - const steady_clock::time_point endstamp = getPktTsbPdTime(m_entries[lastpos].pUnit->m_Packet.getMsgTimeStamp()); + getPktTsbPdTime(packetAt(startpos).getMsgTimeStamp()); + const steady_clock::time_point endstamp = getPktTsbPdTime(packetAt(lastpos).getMsgTimeStamp()); if (endstamp < startstamp) return 0; @@ -577,7 +658,7 @@ int CRcvBufferNew::getTimespan_ms() const return static_cast(count_milliseconds(endstamp - startstamp) + 1); } -int CRcvBufferNew::getRcvDataSize(int& bytes, int& timespan) const +int CRcvBuffer::getRcvDataSize(int& bytes, int& timespan) const { ScopedLock lck(m_BytesCountLock); bytes = m_iBytesCount; @@ -585,16 +666,16 @@ int CRcvBufferNew::getRcvDataSize(int& bytes, int& timespan) const return m_iPktsCount; } -CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstValidPacketInfo() const +CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const { - const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); + const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); for (int i = m_iStartPos; i != end_pos; i = incPos(i)) { // TODO: Maybe check status? if (!m_entries[i].pUnit) continue; - const CPacket& packet = m_entries[i].pUnit->m_Packet; + const CPacket& packet = packetAt(i); const PacketInfo info = { packet.getSeqNo(), i != m_iStartPos, getPktTsbPdTime(packet.getMsgTimeStamp()) }; return info; } @@ -603,20 +684,20 @@ CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstValidPacketInfo() const return info; } -std::pair CRcvBufferNew::getAvailablePacketsRange() const +std::pair CRcvBuffer::getAvailablePacketsRange() const { const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, (int) countReadable()); return std::pair(m_iStartSeqNo, seqno_last); } -size_t CRcvBufferNew::countReadable() const +size_t CRcvBuffer::countReadable() const { if (m_iFirstNonreadPos >= m_iStartPos) return m_iFirstNonreadPos - m_iStartPos; return m_szSize + m_iFirstNonreadPos - m_iStartPos; } -bool CRcvBufferNew::isRcvDataReady(time_point time_now) const +bool CRcvBuffer::isRcvDataReady(time_point time_now) const { const bool haveInorderPackets = hasReadableInorderPkts(); if (!m_tsbpd.isEnabled()) @@ -636,7 +717,7 @@ bool CRcvBufferNew::isRcvDataReady(time_point time_now) const return info.tsbpd_time <= time_now; } -CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstReadablePacketInfo(time_point time_now) const +CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_now) const { const PacketInfo unreadableInfo = {SRT_SEQNO_NONE, false, time_point()}; const bool hasInorderPackets = hasReadableInorderPkts(); @@ -645,7 +726,7 @@ CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstReadablePacketInfo(time_point t { if (hasInorderPackets) { - const CPacket& packet = m_entries[m_iStartPos].pUnit->m_Packet; + const CPacket& packet = packetAt(m_iStartPos); const PacketInfo info = {packet.getSeqNo(), false, time_point()}; return info; } @@ -653,7 +734,7 @@ CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstReadablePacketInfo(time_point t if (m_iFirstReadableOutOfOrder >= 0) { SRT_ASSERT(m_numOutOfOrderPackets > 0); - const CPacket& packet = m_entries[m_iFirstReadableOutOfOrder].pUnit->m_Packet; + const CPacket& packet = packetAt(m_iFirstReadableOutOfOrder); const PacketInfo info = {packet.getSeqNo(), true, time_point()}; return info; } @@ -671,7 +752,7 @@ CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstReadablePacketInfo(time_point t return unreadableInfo; } -void CRcvBufferNew::countBytes(int pkts, int bytes) +void CRcvBuffer::countBytes(int pkts, int bytes) { ScopedLock lock(m_BytesCountLock); m_iBytesCount += bytes; // added or removed bytes from rcv buffer @@ -680,7 +761,7 @@ void CRcvBufferNew::countBytes(int pkts, int bytes) m_uAvgPayloadSz = avg_iir<100>(m_uAvgPayloadSz, (unsigned) bytes); } -void CRcvBufferNew::releaseUnitInPos(int pos) +void CRcvBuffer::releaseUnitInPos(int pos) { CUnit* tmp = m_entries[pos].pUnit; m_entries[pos] = Entry(); // pUnit = NULL; status = Empty @@ -688,15 +769,15 @@ void CRcvBufferNew::releaseUnitInPos(int pos) m_pUnitQueue->makeUnitFree(tmp); } -bool CRcvBufferNew::dropUnitInPos(int pos) +bool CRcvBuffer::dropUnitInPos(int pos) { if (!m_entries[pos].pUnit) return false; if (m_tsbpd.isEnabled()) { - updateTsbPdTimeBase(m_entries[pos].pUnit->m_Packet.getMsgTimeStamp()); + updateTsbPdTimeBase(packetAt(pos).getMsgTimeStamp()); } - else if (m_bMessageAPI && !m_entries[pos].pUnit->m_Packet.getMsgOrderFlag()) + else if (m_bMessageAPI && !packetAt(pos).getMsgOrderFlag()) { --m_numOutOfOrderPackets; if (pos == m_iFirstReadableOutOfOrder) @@ -706,7 +787,7 @@ bool CRcvBufferNew::dropUnitInPos(int pos) return true; } -void CRcvBufferNew::releaseNextFillerEntries() +void CRcvBuffer::releaseNextFillerEntries() { int pos = m_iStartPos; while (m_entries[pos].status == EntryState_Read || m_entries[pos].status == EntryState_Drop) @@ -715,24 +796,24 @@ void CRcvBufferNew::releaseNextFillerEntries() releaseUnitInPos(pos); pos = incPos(pos); m_iStartPos = pos; - --m_iMaxPosInc; - if (m_iMaxPosInc < 0) - m_iMaxPosInc = 0; + --m_iMaxPosOff; + if (m_iMaxPosOff < 0) + m_iMaxPosOff = 0; } } // TODO: Is this function complete? There are some comments left inside. -void CRcvBufferNew::updateNonreadPos() +void CRcvBuffer::updateNonreadPos() { - if (m_iMaxPosInc == 0) + if (m_iMaxPosOff == 0) return; - const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); // The empty position right after the last valid entry. + const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. int pos = m_iFirstNonreadPos; while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail) { - if (m_bMessageAPI && (m_entries[pos].pUnit->m_Packet.getMsgBoundary() & PB_FIRST) == 0) + if (m_bMessageAPI && (packetAt(pos).getMsgBoundary() & PB_FIRST) == 0) break; for (int i = pos; i != end_pos; i = incPos(i)) @@ -743,7 +824,7 @@ void CRcvBufferNew::updateNonreadPos() } // Check PB_LAST only in message mode. - if (!m_bMessageAPI || m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) + if (!m_bMessageAPI || packetAt(i).getMsgBoundary() & PB_LAST) { m_iFirstNonreadPos = incPos(i); break; @@ -757,13 +838,13 @@ void CRcvBufferNew::updateNonreadPos() } } -int CRcvBufferNew::findLastMessagePkt() +int CRcvBuffer::findLastMessagePkt() { for (int i = m_iStartPos; i != m_iFirstNonreadPos; i = incPos(i)) { SRT_ASSERT(m_entries[i].pUnit); - if (m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) + if (packetAt(i).getMsgBoundary() & PB_LAST) { return i; } @@ -772,7 +853,7 @@ int CRcvBufferNew::findLastMessagePkt() return -1; } -void CRcvBufferNew::onInsertNotInOrderPacket(int insertPos) +void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) { if (m_numOutOfOrderPackets == 0) return; @@ -788,9 +869,9 @@ void CRcvBufferNew::onInsertNotInOrderPacket(int insertPos) // Just a sanity check. This function is called when a new packet is added. // So the should be unacknowledged packets. - SRT_ASSERT(m_iMaxPosInc > 0); + SRT_ASSERT(m_iMaxPosOff > 0); SRT_ASSERT(m_entries[insertPos].pUnit); - const CPacket& pkt = m_entries[insertPos].pUnit->m_Packet; + const CPacket& pkt = packetAt(insertPos); const PacketBoundary boundary = pkt.getMsgBoundary(); //if ((boundary & PB_FIRST) && (boundary & PB_LAST)) @@ -816,19 +897,19 @@ void CRcvBufferNew::onInsertNotInOrderPacket(int insertPos) return; } -bool CRcvBufferNew::checkFirstReadableOutOfOrder() +bool CRcvBuffer::checkFirstReadableOutOfOrder() { - if (m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder < 0 || m_iMaxPosInc == 0) + if (m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder < 0 || m_iMaxPosOff == 0) return false; - const int endPos = incPos(m_iStartPos, m_iMaxPosInc); + const int endPos = incPos(m_iStartPos, m_iMaxPosOff); int msgno = -1; for (int pos = m_iFirstReadableOutOfOrder; pos != endPos; pos = incPos(pos)) { if (!m_entries[pos].pUnit) return false; - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgOrderFlag()) return false; @@ -844,12 +925,12 @@ bool CRcvBufferNew::checkFirstReadableOutOfOrder() return false; } -void CRcvBufferNew::updateFirstReadableOutOfOrder() +void CRcvBuffer::updateFirstReadableOutOfOrder() { if (hasReadableInorderPkts() || m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder >= 0) return; - if (m_iMaxPosInc == 0) + if (m_iMaxPosOff == 0) return; // TODO: unused variable outOfOrderPktsRemain? @@ -857,7 +938,7 @@ void CRcvBufferNew::updateFirstReadableOutOfOrder() // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosInc - 1) % m_szSize; + const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; int posFirst = -1; int posLast = -1; @@ -871,7 +952,7 @@ void CRcvBufferNew::updateFirstReadableOutOfOrder() continue; } - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgOrderFlag()) // Skip in order packet { @@ -907,11 +988,11 @@ void CRcvBufferNew::updateFirstReadableOutOfOrder() return; } -int CRcvBufferNew::scanNotInOrderMessageRight(const int startPos, int msgNo) const +int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const { // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosInc - 1) % m_szSize; + const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; if (startPos == lastPos) return -1; @@ -922,7 +1003,7 @@ int CRcvBufferNew::scanNotInOrderMessageRight(const int startPos, int msgNo) con if (!m_entries[pos].pUnit) break; - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { @@ -938,9 +1019,9 @@ int CRcvBufferNew::scanNotInOrderMessageRight(const int startPos, int msgNo) con return -1; } -int CRcvBufferNew::scanNotInOrderMessageLeft(const int startPos, int msgNo) const +int CRcvBuffer::scanNotInOrderMessageLeft(const int startPos, int msgNo) const { - // Search preceeding packets to the left. + // Search preceding packets to the left. // First check if there are packets to the left. if (startPos == m_iStartPos) return -1; @@ -953,7 +1034,7 @@ int CRcvBufferNew::scanNotInOrderMessageLeft(const int startPos, int msgNo) cons if (!m_entries[pos].pUnit) return -1; - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { @@ -969,17 +1050,17 @@ int CRcvBufferNew::scanNotInOrderMessageLeft(const int startPos, int msgNo) cons return -1; } -bool CRcvBufferNew::addRcvTsbPdDriftSample(uint32_t usTimestamp, const time_point& tsPktArrival, int usRTTSample) +bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t usTimestamp, const time_point& tsPktArrival, int usRTTSample) { return m_tsbpd.addDriftSample(usTimestamp, tsPktArrival, usRTTSample); } -void CRcvBufferNew::setTsbPdMode(const steady_clock::time_point& timebase, bool wrap, duration delay) +void CRcvBuffer::setTsbPdMode(const steady_clock::time_point& timebase, bool wrap, duration delay) { m_tsbpd.setTsbPdMode(timebase, wrap, delay); } -void CRcvBufferNew::applyGroupTime(const steady_clock::time_point& timebase, +void CRcvBuffer::applyGroupTime(const steady_clock::time_point& timebase, bool wrp, uint32_t delay, const steady_clock::duration& udrift) @@ -987,41 +1068,44 @@ void CRcvBufferNew::applyGroupTime(const steady_clock::time_point& timebase, m_tsbpd.applyGroupTime(timebase, wrp, delay, udrift); } -void CRcvBufferNew::applyGroupDrift(const steady_clock::time_point& timebase, +void CRcvBuffer::applyGroupDrift(const steady_clock::time_point& timebase, bool wrp, const steady_clock::duration& udrift) { m_tsbpd.applyGroupDrift(timebase, wrp, udrift); } -CRcvBufferNew::time_point CRcvBufferNew::getTsbPdTimeBase(uint32_t usPktTimestamp) const +CRcvBuffer::time_point CRcvBuffer::getTsbPdTimeBase(uint32_t usPktTimestamp) const { return m_tsbpd.getTsbPdTimeBase(usPktTimestamp); } -void CRcvBufferNew::updateTsbPdTimeBase(uint32_t usPktTimestamp) +void CRcvBuffer::updateTsbPdTimeBase(uint32_t usPktTimestamp) { m_tsbpd.updateTsbPdTimeBase(usPktTimestamp); } -string CRcvBufferNew::strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const +string CRcvBuffer::strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const { stringstream ss; - ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize; - ss << " pkts. "; - if (m_tsbpd.isEnabled() && m_iMaxPosInc > 0) + ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo + << " m_iStartPos=" << m_iStartPos << " m_iMaxPosOff=" << m_iMaxPosOff << ". "; + + ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; + + if (m_tsbpd.isEnabled() && m_iMaxPosOff > 0) { const PacketInfo nextValidPkt = getFirstValidPacketInfo(); ss << "(TSBPD ready in "; if (!is_zero(nextValidPkt.tsbpd_time)) { ss << count_milliseconds(nextValidPkt.tsbpd_time - tsNow) << "ms"; - const int iLastPos = incPos(m_iStartPos, m_iMaxPosInc - 1); + const int iLastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); if (m_entries[iLastPos].pUnit) { ss << ", timespan "; - const uint32_t usPktTimestamp = m_entries[iLastPos].pUnit->m_Packet.getMsgTimeStamp(); + const uint32_t usPktTimestamp = packetAt(iLastPos).getMsgTimeStamp(); ss << count_milliseconds(m_tsbpd.getPktTsbPdTime(usPktTimestamp) - nextValidPkt.tsbpd_time); ss << " ms"; } @@ -1030,7 +1114,6 @@ string CRcvBufferNew::strFullnessState(int iFirstUnackSeqNo, const time_point& t { ss << "n/a"; } - ss << "). "; } @@ -1038,13 +1121,13 @@ string CRcvBufferNew::strFullnessState(int iFirstUnackSeqNo, const time_point& t return ss.str(); } -CRcvBufferNew::time_point CRcvBufferNew::getPktTsbPdTime(uint32_t usPktTimestamp) const +CRcvBuffer::time_point CRcvBuffer::getPktTsbPdTime(uint32_t usPktTimestamp) const { return m_tsbpd.getPktTsbPdTime(usPktTimestamp); } /* Return moving average of acked data pkts, bytes, and timespan (ms) of the receive buffer */ -int CRcvBufferNew::getRcvAvgDataSize(int& bytes, int& timespan) +int CRcvBuffer::getRcvAvgDataSize(int& bytes, int& timespan) { // Average number of packets and timespan could be small, // so rounding is beneficial, while for the number of @@ -1056,7 +1139,7 @@ int CRcvBufferNew::getRcvAvgDataSize(int& bytes, int& timespan) } /* Update moving average of acked data pkts, bytes, and timespan (ms) of the receive buffer */ -void CRcvBufferNew::updRcvAvgDataSize(const steady_clock::time_point& now) +void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now) { if (!m_mavg.isTimeToUpdate(now)) return; @@ -1068,5 +1151,3 @@ void CRcvBufferNew::updRcvAvgDataSize(const steady_clock::time_point& now) } } // namespace srt - -#endif // ENABLE_NEW_RCVBUFFER diff --git a/trunk/3rdparty/srt-1-fit/srtcore/buffer_rcv.h b/trunk/3rdparty/srt-1-fit/srtcore/buffer_rcv.h index d628c459fd..d4b50fab7c 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/buffer_rcv.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/buffer_rcv.h @@ -11,12 +11,9 @@ #ifndef INC_SRT_BUFFER_RCV_H #define INC_SRT_BUFFER_RCV_H -#if ENABLE_NEW_RCVBUFFER - -#include "buffer.h" // AvgBufSize +#include "buffer_tools.h" // AvgBufSize #include "common.h" #include "queue.h" -#include "sync.h" #include "tsbpd_time.h" namespace srt @@ -26,7 +23,7 @@ namespace srt * Circular receiver buffer. * * |<------------------- m_szSize ---------------------------->| - * | |<------------ m_iMaxPosInc ----------->| | + * | |<------------ m_iMaxPosOff ----------->| | * | | | | * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] @@ -45,15 +42,15 @@ namespace srt * first_nonread_pos_: */ -class CRcvBufferNew +class CRcvBuffer { typedef sync::steady_clock::time_point time_point; typedef sync::steady_clock::duration duration; public: - CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI); + CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI); - ~CRcvBufferNew(); + ~CRcvBuffer(); public: /// Insert a unit into the buffer. @@ -77,14 +74,29 @@ class CRcvBufferNew /// @return the number of dropped packets. int dropAll(); - /// @brief Drop the whole message from the buffer. - /// If message number is 0, then use sequence numbers to locate sequence range to drop [seqnolo, seqnohi]. - /// When one packet of the message is in the range of dropping, the whole message is to be dropped. + enum DropActionIfExists { + DROP_EXISTING = 0, + KEEP_EXISTING = 1 + }; + + /// @brief Drop a sequence of packets from the buffer. + /// If @a msgno is valid, sender has requested to drop the whole message by TTL. In this case it has to also provide a pkt seqno range. + /// However, if a message has been partially acknowledged and already removed from the SND buffer, + /// the @a seqnolo might specify some position in the middle of the message, not the very first packet. + /// If those packets have been acknowledged, they must exist in the receiver buffer unless already read. + /// In this case the @a msgno should be used to determine starting packets of the message. + /// Some packets of the message can be missing on the receiver, therefore the actual drop should still be performed by pkt seqno range. + /// If message number is 0 or SRT_MSGNO_NONE, then use sequence numbers to locate sequence range to drop [seqnolo, seqnohi]. + /// A SOLO message packet can be kept depending on @a actionOnExisting value. + /// TODO: A message in general can be kept if all of its packets are in the buffer, depending on @a actionOnExisting value. + /// This is done to avoid dropping existing packet when the sender was asked to re-transmit a packet from an outdated loss report, + /// which is already not available in the SND buffer. /// @param seqnolo sequence number of the first packet in the dropping range. /// @param seqnohi sequence number of the last packet in the dropping range. /// @param msgno message number to drop (0 if unknown) + /// @param actionOnExisting Should an exising SOLO packet be dropped from the buffer or preserved? /// @return the number of packets actually dropped. - int dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno); + int dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, DropActionIfExists actionOnExisting); /// Read the whole message from one or several packets. /// @@ -130,9 +142,8 @@ class CRcvBufferNew const int iRBufSeqNo = getStartSeqNo(); if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo { - // Full capacity is available, still don't want to encourage extra packets to come. - // Note: CSeqNo::seqlen(n, n) returns 1. - return capacity() - CSeqNo::seqlen(iFirstUnackSeqNo, iRBufSeqNo) + 1; + // Full capacity is available. + return capacity(); } // Note: CSeqNo::seqlen(n, n) returns 1. @@ -140,13 +151,14 @@ class CRcvBufferNew } /// @brief Checks if the buffer has packets available for reading regardless of the TSBPD. + /// A message is available for reading only if all of its packets are present in the buffer. /// @return true if there are packets available for reading, false otherwise. bool hasAvailablePackets() const; /// Query how many data has been continuously received (for reading) and available for reading out /// regardless of the TSBPD. /// TODO: Rename to countAvailablePackets(). - /// @return size of valid (continous) data for reading. + /// @return size of valid (continuous) data for reading. int getRcvDataSize() const; /// Get the number of packets, bytes and buffer timespan. @@ -165,11 +177,11 @@ class CRcvBufferNew /// Parameters (of the 1st packet queue, ready to play or not): /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if /// none - /// @param [out] passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app) - /// @param [out] skipseqno -1 or seq number of 1st unacknowledged pkt ready to play preceeded by missing packets. + /// @param [out] passack true if 1st ready packet is not yet acknowledged (allowed to be delivered to the app) + /// @param [out] skipseqno -1 or sequence number of 1st unacknowledged packet (after one or more missing packets) that is ready to play. /// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true /// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE: - /// IF skipseqno != -1, packet ready to play preceeded by missing packets.; + /// IF skipseqno != -1, packet ready to play preceded by missing packets.; /// IF skipseqno == -1, no missing packet but 1st not ready to play. PacketInfo getFirstValidPacketInfo() const; @@ -185,7 +197,7 @@ class CRcvBufferNew bool empty() const { - return (m_iMaxPosInc == 0); + return (m_iMaxPosOff == 0); } /// Return buffer capacity. @@ -227,6 +239,18 @@ class CRcvBufferNew inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); } + inline int cmpPos(int pos2, int pos1) const + { + // XXX maybe not the best implementation, but this keeps up to the rule + const int off1 = pos1 >= m_iStartPos ? pos1 - m_iStartPos : pos1 + (int)m_szSize - m_iStartPos; + const int off2 = pos2 >= m_iStartPos ? pos2 - m_iStartPos : pos2 + (int)m_szSize - m_iStartPos; + + return off2 - off1; + } + + // NOTE: Assumes that pUnit != NULL + CPacket& packetAt(int pos) { return m_entries[pos].pUnit->m_Packet; } + const CPacket& packetAt(int pos) const { return m_entries[pos].pUnit->m_Packet; } private: void countBytes(int pkts, int bytes); @@ -268,7 +292,7 @@ class CRcvBufferNew int getTimespan_ms() const; private: - // TODO: Call makeUnitGood upon assignment, and makeUnitFree upon clearing. + // TODO: Call makeUnitTaken upon assignment, and makeUnitFree upon clearing. // TODO: CUnitPtr is not in use at the moment, but may be a smart pointer. // class CUnitPtr // { @@ -313,7 +337,7 @@ class CRcvBufferNew int m_iStartSeqNo; int m_iStartPos; // the head position for I/O (inclusive) int m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) - int m_iMaxPosInc; // the furthest data position + int m_iMaxPosOff; // the furthest data position int m_iNotch; // the starting read point of the first unit size_t m_numOutOfOrderPackets; // The number of stored packets with "inorder" flag set to false @@ -326,7 +350,7 @@ class CRcvBufferNew /// Set TimeStamp-Based Packet Delivery Rx Mode /// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay /// @param [in] wrap Is in wrapping period - /// @param [in] delay aggreed TsbPD delay + /// @param [in] delay agreed TsbPD delay /// /// @return 0 void setTsbPdMode(const time_point& timebase, bool wrap, duration delay); @@ -344,6 +368,8 @@ class CRcvBufferNew time_point getTsbPdTimeBase(uint32_t usPktTimestamp) const; void updateTsbPdTimeBase(uint32_t usPktTimestamp); + bool isTsbPd() const { return m_tsbpd.isEnabled(); } + /// Form a string of the current buffer fullness state. /// number of packets acknowledged, TSBPD readiness, etc. std::string strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const; @@ -363,5 +389,4 @@ class CRcvBufferNew } // namespace srt -#endif // ENABLE_NEW_RCVBUFFER #endif // INC_SRT_BUFFER_RCV_H diff --git a/trunk/3rdparty/srt-1-fit/srtcore/buffer_snd.cpp b/trunk/3rdparty/srt-1-fit/srtcore/buffer_snd.cpp new file mode 100644 index 0000000000..26f885dd66 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/buffer_snd.cpp @@ -0,0 +1,730 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 03/12/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#include "platform_sys.h" + +#include +#include "buffer_snd.h" +#include "packet.h" +#include "core.h" // provides some constants +#include "logging.h" + +namespace srt { + +using namespace std; +using namespace srt_logging; +using namespace sync; + +CSndBuffer::CSndBuffer(int size, int maxpld, int authtag) + : m_BufLock() + , m_pBlock(NULL) + , m_pFirstBlock(NULL) + , m_pCurrBlock(NULL) + , m_pLastBlock(NULL) + , m_pBuffer(NULL) + , m_iNextMsgNo(1) + , m_iSize(size) + , m_iBlockLen(maxpld) + , m_iAuthTagSize(authtag) + , m_iCount(0) + , m_iBytesCount(0) +{ + // initial physical buffer of "size" + m_pBuffer = new Buffer; + m_pBuffer->m_pcData = new char[m_iSize * m_iBlockLen]; + m_pBuffer->m_iSize = m_iSize; + m_pBuffer->m_pNext = NULL; + + // circular linked list for out bound packets + m_pBlock = new Block; + Block* pb = m_pBlock; + char* pc = m_pBuffer->m_pcData; + + for (int i = 0; i < m_iSize; ++i) + { + pb->m_iMsgNoBitset = 0; + pb->m_pcData = pc; + pc += m_iBlockLen; + + if (i < m_iSize - 1) + { + pb->m_pNext = new Block; + pb = pb->m_pNext; + } + } + pb->m_pNext = m_pBlock; + + m_pFirstBlock = m_pCurrBlock = m_pLastBlock = m_pBlock; + + setupMutex(m_BufLock, "Buf"); +} + +CSndBuffer::~CSndBuffer() +{ + Block* pb = m_pBlock->m_pNext; + while (pb != m_pBlock) + { + Block* temp = pb; + pb = pb->m_pNext; + delete temp; + } + delete m_pBlock; + + while (m_pBuffer != NULL) + { + Buffer* temp = m_pBuffer; + m_pBuffer = m_pBuffer->m_pNext; + delete[] temp->m_pcData; + delete temp; + } + + releaseMutex(m_BufLock); +} + +void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl) +{ + int32_t& w_msgno = w_mctrl.msgno; + int32_t& w_seqno = w_mctrl.pktseq; + int64_t& w_srctime = w_mctrl.srctime; + const int& ttl = w_mctrl.msgttl; + const int iPktLen = getMaxPacketLen(); + const int iNumBlocks = countNumPacketsRequired(len, iPktLen); + + HLOGC(bslog.Debug, + log << "addBuffer: needs=" << iNumBlocks << " buffers for " << len << " bytes. Taken=" << m_iCount << "/" << m_iSize); + // Retrieve current time before locking the mutex to be closer to packet submission event. + const steady_clock::time_point tnow = steady_clock::now(); + + ScopedLock bufferguard(m_BufLock); + // Dynamically increase sender buffer if there is not enough room. + while (iNumBlocks + m_iCount >= m_iSize) + { + HLOGC(bslog.Debug, log << "addBuffer: ... still lacking " << (iNumBlocks + m_iCount - m_iSize) << " buffers..."); + increase(); + } + + const int32_t inorder = w_mctrl.inorder ? MSGNO_PACKET_INORDER::mask : 0; + HLOGC(bslog.Debug, + log << CONID() << "addBuffer: adding " << iNumBlocks << " packets (" << len << " bytes) to send, msgno=" + << (w_msgno > 0 ? w_msgno : m_iNextMsgNo) << (inorder ? "" : " NOT") << " in order"); + + // Calculate origin time (same for all blocks of the message). + m_tsLastOriginTime = w_srctime ? time_point() + microseconds_from(w_srctime) : tnow; + // Rewrite back the actual value, even if it stays the same, so that the calling facilities can reuse it. + // May also be a subject to conversion error, thus the actual value is signalled back. + w_srctime = count_microseconds(m_tsLastOriginTime.time_since_epoch()); + + // The sequence number passed to this function is the sequence number + // that the very first packet from the packet series should get here. + // If there's more than one packet, this function must increase it by itself + // and then return the accordingly modified sequence number in the reference. + + Block* s = m_pLastBlock; + + if (w_msgno == SRT_MSGNO_NONE) // DEFAULT-UNCHANGED msgno supplied + { + HLOGC(bslog.Debug, log << "addBuffer: using internally managed msgno=" << m_iNextMsgNo); + w_msgno = m_iNextMsgNo; + } + else + { + HLOGC(bslog.Debug, log << "addBuffer: OVERWRITTEN by msgno supplied by caller: msgno=" << w_msgno); + m_iNextMsgNo = w_msgno; + } + + for (int i = 0; i < iNumBlocks; ++i) + { + int pktlen = len - i * iPktLen; + if (pktlen > iPktLen) + pktlen = iPktLen; + + HLOGC(bslog.Debug, + log << "addBuffer: %" << w_seqno << " #" << w_msgno << " offset=" << (i * iPktLen) + << " size=" << pktlen << " TO BUFFER:" << (void*)s->m_pcData); + memcpy((s->m_pcData), data + i * iPktLen, pktlen); + s->m_iLength = pktlen; + + s->m_iSeqNo = w_seqno; + w_seqno = CSeqNo::incseq(w_seqno); + + s->m_iMsgNoBitset = m_iNextMsgNo | inorder; + if (i == 0) + s->m_iMsgNoBitset |= PacketBoundaryBits(PB_FIRST); + if (i == iNumBlocks - 1) + s->m_iMsgNoBitset |= PacketBoundaryBits(PB_LAST); + // NOTE: if i is neither 0 nor size-1, it resuls with PB_SUBSEQUENT. + // if i == 0 == size-1, it results with PB_SOLO. + // Packets assigned to one message can be: + // [PB_FIRST] [PB_SUBSEQUENT] [PB_SUBSEQUENT] [PB_LAST] - 4 packets per message + // [PB_FIRST] [PB_LAST] - 2 packets per message + // [PB_SOLO] - 1 packet per message + + s->m_iTTL = ttl; + s->m_tsRexmitTime = time_point(); + s->m_tsOriginTime = m_tsLastOriginTime; + + // Should never happen, as the call to increase() should ensure enough buffers. + SRT_ASSERT(s->m_pNext); + s = s->m_pNext; + } + m_pLastBlock = s; + + m_iCount += iNumBlocks; + m_iBytesCount += len; + + m_rateEstimator.updateInputRate(m_tsLastOriginTime, iNumBlocks, len); + updAvgBufSize(m_tsLastOriginTime); + + // MSGNO_SEQ::mask has a form: 00000011111111... + // At least it's known that it's from some index inside til the end (to bit 0). + // If this value has been reached in a step of incrementation, it means that the + // maximum value has been reached. Casting to int32_t to ensure the same sign + // in comparison, although it's far from reaching the sign bit. + + const int nextmsgno = ++MsgNo(m_iNextMsgNo); + HLOGC(bslog.Debug, log << "CSndBuffer::addBuffer: updating msgno: #" << m_iNextMsgNo << " -> #" << nextmsgno); + m_iNextMsgNo = nextmsgno; +} + +int CSndBuffer::addBufferFromFile(fstream& ifs, int len) +{ + const int iPktLen = getMaxPacketLen(); + const int iNumBlocks = countNumPacketsRequired(len, iPktLen); + + HLOGC(bslog.Debug, + log << "addBufferFromFile: size=" << m_iCount << " reserved=" << m_iSize << " needs=" << iPktLen + << " buffers for " << len << " bytes"); + + // dynamically increase sender buffer + while (iNumBlocks + m_iCount >= m_iSize) + { + HLOGC(bslog.Debug, + log << "addBufferFromFile: ... still lacking " << (iNumBlocks + m_iCount - m_iSize) << " buffers..."); + increase(); + } + + HLOGC(bslog.Debug, + log << CONID() << "addBufferFromFile: adding " << iPktLen << " packets (" << len + << " bytes) to send, msgno=" << m_iNextMsgNo); + + Block* s = m_pLastBlock; + int total = 0; + for (int i = 0; i < iNumBlocks; ++i) + { + if (ifs.bad() || ifs.fail() || ifs.eof()) + break; + + int pktlen = len - i * iPktLen; + if (pktlen > iPktLen) + pktlen = iPktLen; + + HLOGC(bslog.Debug, + log << "addBufferFromFile: reading from=" << (i * iPktLen) << " size=" << pktlen + << " TO BUFFER:" << (void*)s->m_pcData); + ifs.read(s->m_pcData, pktlen); + if ((pktlen = int(ifs.gcount())) <= 0) + break; + + // currently file transfer is only available in streaming mode, message is always in order, ttl = infinite + s->m_iMsgNoBitset = m_iNextMsgNo | MSGNO_PACKET_INORDER::mask; + if (i == 0) + s->m_iMsgNoBitset |= PacketBoundaryBits(PB_FIRST); + if (i == iNumBlocks - 1) + s->m_iMsgNoBitset |= PacketBoundaryBits(PB_LAST); + // NOTE: PB_FIRST | PB_LAST == PB_SOLO. + // none of PB_FIRST & PB_LAST == PB_SUBSEQUENT. + + s->m_iLength = pktlen; + s->m_iTTL = SRT_MSGTTL_INF; + s = s->m_pNext; + + total += pktlen; + } + m_pLastBlock = s; + + enterCS(m_BufLock); + m_iCount += iNumBlocks; + m_iBytesCount += total; + + leaveCS(m_BufLock); + + m_iNextMsgNo++; + if (m_iNextMsgNo == int32_t(MSGNO_SEQ::mask)) + m_iNextMsgNo = 1; + + return total; +} + +int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs, int& w_seqnoinc) +{ + int readlen = 0; + w_seqnoinc = 0; + + ScopedLock bufferguard(m_BufLock); + while (m_pCurrBlock != m_pLastBlock) + { + // Make the packet REFLECT the data stored in the buffer. + w_packet.m_pcData = m_pCurrBlock->m_pcData; + readlen = m_pCurrBlock->m_iLength; + w_packet.setLength(readlen, m_iBlockLen); + w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; + + // 1. On submission (addBuffer), the KK flag is set to EK_NOENC (0). + // 2. The readData() is called to get the original (unique) payload not ever sent yet. + // The payload must be encrypted for the first time if the encryption + // is enabled (arg kflgs != EK_NOENC). The KK encryption flag of the data packet + // header must be set and remembered accordingly (see EncryptionKeySpec). + // 3. The next time this packet is read (only for retransmission), the payload is already + // encrypted, and the proper flag value is already stored. + + // TODO: Alternatively, encryption could happen before the packet is submitted to the buffer + // (before the addBuffer() call), and corresponding flags could be set accordingly. + // This may also put an encryption burden on the application thread, rather than the sending thread, + // which could be more efficient. Note that packet sequence number must be properly set in that case, + // as it is used as a counter for the AES encryption. + if (kflgs == -1) + { + HLOGC(bslog.Debug, log << CONID() << " CSndBuffer: ERROR: encryption required and not possible. NOT SENDING."); + readlen = 0; + } + else + { + m_pCurrBlock->m_iMsgNoBitset |= MSGNO_ENCKEYSPEC::wrap(kflgs); + } + + Block* p = m_pCurrBlock; + w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; + w_srctime = m_pCurrBlock->m_tsOriginTime; + m_pCurrBlock = m_pCurrBlock->m_pNext; + + if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - w_srctime) > p->m_iTTL)) + { + LOGC(bslog.Warn, log << CONID() << "CSndBuffer: skipping packet %" << p->m_iSeqNo << " #" << p->getMsgSeq() << " with TTL=" << p->m_iTTL); + // Skip this packet due to TTL expiry. + readlen = 0; + ++w_seqnoinc; + continue; + } + + HLOGC(bslog.Debug, log << CONID() << "CSndBuffer: extracting packet size=" << readlen << " to send"); + break; + } + + return readlen; +} + +CSndBuffer::time_point CSndBuffer::peekNextOriginal() const +{ + ScopedLock bufferguard(m_BufLock); + if (m_pCurrBlock == m_pLastBlock) + return time_point(); + + return m_pCurrBlock->m_tsOriginTime; +} + +int32_t CSndBuffer::getMsgNoAt(const int offset) +{ + ScopedLock bufferguard(m_BufLock); + + Block* p = m_pFirstBlock; + + if (p) + { + HLOGC(bslog.Debug, + log << "CSndBuffer::getMsgNoAt: FIRST MSG: size=" << p->m_iLength << " %" << p->m_iSeqNo << " #" + << p->getMsgSeq() << " !" << BufferStamp(p->m_pcData, p->m_iLength)); + } + + if (offset >= m_iCount) + { + // Prevent accessing the last "marker" block + LOGC(bslog.Error, + log << "CSndBuffer::getMsgNoAt: IPE: offset=" << offset << " not found, max offset=" << m_iCount); + return SRT_MSGNO_CONTROL; + } + + // XXX Suboptimal procedure to keep the blocks identifiable + // by sequence number. Consider using some circular buffer. + int i; + Block* ee SRT_ATR_UNUSED = 0; + for (i = 0; i < offset && p; ++i) + { + ee = p; + p = p->m_pNext; + } + + if (!p) + { + LOGC(bslog.Error, + log << "CSndBuffer::getMsgNoAt: IPE: offset=" << offset << " not found, stopped at " << i << " with #" + << (ee ? ee->getMsgSeq() : SRT_MSGNO_NONE)); + return SRT_MSGNO_CONTROL; + } + + HLOGC(bslog.Debug, + log << "CSndBuffer::getMsgNoAt: offset=" << offset << " found, size=" << p->m_iLength << " %" << p->m_iSeqNo + << " #" << p->getMsgSeq() << " !" << BufferStamp(p->m_pcData, p->m_iLength)); + + return p->getMsgSeq(); +} + +int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen) +{ + int32_t& msgno_bitset = w_packet.m_iMsgNo; + + ScopedLock bufferguard(m_BufLock); + + Block* p = m_pFirstBlock; + + // XXX Suboptimal procedure to keep the blocks identifiable + // by sequence number. Consider using some circular buffer. + for (int i = 0; i < offset && p != m_pLastBlock; ++i) + { + p = p->m_pNext; + } + if (p == m_pLastBlock) + { + LOGC(qslog.Error, log << "CSndBuffer::readData: offset " << offset << " too large!"); + return 0; + } +#if ENABLE_HEAVY_LOGGING + const int32_t first_seq = p->m_iSeqNo; + int32_t last_seq = p->m_iSeqNo; +#endif + + // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. + + // If so, then inform the caller that it should first take care of the whole + // message (all blocks with that message id). Shift the m_pCurrBlock pointer + // to the position past the last of them. Then return -1 and set the + // msgno_bitset return reference to the message id that should be dropped as + // a whole. + + // After taking care of that, the caller should immediately call this function again, + // this time possibly in order to find the real data to be sent. + + // if found block is stale + // (This is for messages that have declared TTL - messages that fail to be sent + // before the TTL defined time comes, will be dropped). + + if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - p->m_tsOriginTime) > p->m_iTTL)) + { + int32_t msgno = p->getMsgSeq(); + w_msglen = 1; + p = p->m_pNext; + bool move = false; + while (p != m_pLastBlock && msgno == p->getMsgSeq()) + { +#if ENABLE_HEAVY_LOGGING + last_seq = p->m_iSeqNo; +#endif + if (p == m_pCurrBlock) + move = true; + p = p->m_pNext; + if (move) + m_pCurrBlock = p; + w_msglen++; + } + + HLOGC(qslog.Debug, + log << "CSndBuffer::readData: due to TTL exceeded, SEQ " << first_seq << " - " << last_seq << ", " + << w_msglen << " packets to drop, msgno=" << msgno); + + // If readData returns -1, then msgno_bitset is understood as a Message ID to drop. + // This means that in this case it should be written by the message sequence value only + // (not the whole 4-byte bitset written at PH_MSGNO). + msgno_bitset = msgno; + return -1; + } + + w_packet.m_pcData = p->m_pcData; + const int readlen = p->m_iLength; + w_packet.setLength(readlen, m_iBlockLen); + + // XXX Here the value predicted to be applied to PH_MSGNO field is extracted. + // As this function is predicted to extract the data to send as a rexmited packet, + // the packet must be in the form ready to send - so, in case of encryption, + // encrypted, and with all ENC flags already set. So, the first call to send + // the packet originally (the other overload of this function) must set these + // flags. + w_packet.m_iMsgNo = p->m_iMsgNoBitset; + w_srctime = p->m_tsOriginTime; + + // This function is called when packet retransmission is triggered. + // Therefore we are setting the rexmit time. + p->m_tsRexmitTime = steady_clock::now(); + + HLOGC(qslog.Debug, + log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.m_iSeqNo + << " size=" << readlen << " to send [REXMIT]"); + + return readlen; +} + +sync::steady_clock::time_point CSndBuffer::getPacketRexmitTime(const int offset) +{ + ScopedLock bufferguard(m_BufLock); + const Block* p = m_pFirstBlock; + + // XXX Suboptimal procedure to keep the blocks identifiable + // by sequence number. Consider using some circular buffer. + for (int i = 0; i < offset; ++i) + { + SRT_ASSERT(p); + p = p->m_pNext; + } + + SRT_ASSERT(p); + return p->m_tsRexmitTime; +} + +void CSndBuffer::ackData(int offset) +{ + ScopedLock bufferguard(m_BufLock); + + bool move = false; + for (int i = 0; i < offset; ++i) + { + m_iBytesCount -= m_pFirstBlock->m_iLength; + if (m_pFirstBlock == m_pCurrBlock) + move = true; + m_pFirstBlock = m_pFirstBlock->m_pNext; + } + if (move) + m_pCurrBlock = m_pFirstBlock; + + m_iCount -= offset; + + updAvgBufSize(steady_clock::now()); +} + +int CSndBuffer::getCurrBufSize() const +{ + return m_iCount; +} + +int CSndBuffer::getMaxPacketLen() const +{ + return m_iBlockLen - m_iAuthTagSize; +} + +int CSndBuffer::countNumPacketsRequired(int iPldLen) const +{ + const int iPktLen = getMaxPacketLen(); + return countNumPacketsRequired(iPldLen, iPktLen); +} + +int CSndBuffer::countNumPacketsRequired(int iPldLen, int iPktLen) const +{ + return (iPldLen + iPktLen - 1) / iPktLen; +} + +namespace { +int round_val(double val) +{ + return static_cast(round(val)); +} +} + +int CSndBuffer::getAvgBufSize(int& w_bytes, int& w_tsp) +{ + ScopedLock bufferguard(m_BufLock); /* Consistency of pkts vs. bytes vs. spantime */ + + /* update stats in case there was no add/ack activity lately */ + updAvgBufSize(steady_clock::now()); + + // Average number of packets and timespan could be small, + // so rounding is beneficial, while for the number of + // bytes in the buffer is a higher value, so rounding can be omitted, + // but probably better to round all three values. + w_bytes = round_val(m_mavg.bytes()); + w_tsp = round_val(m_mavg.timespan_ms()); + return round_val(m_mavg.pkts()); +} + +void CSndBuffer::updAvgBufSize(const steady_clock::time_point& now) +{ + if (!m_mavg.isTimeToUpdate(now)) + return; + + int bytes = 0; + int timespan_ms = 0; + const int pkts = getCurrBufSize((bytes), (timespan_ms)); + m_mavg.update(now, pkts, bytes, timespan_ms); +} + +int CSndBuffer::getCurrBufSize(int& w_bytes, int& w_timespan) const +{ + w_bytes = m_iBytesCount; + /* + * Timespan can be less then 1000 us (1 ms) if few packets. + * Also, if there is only one pkt in buffer, the time difference will be 0. + * Therefore, always add 1 ms if not empty. + */ + w_timespan = 0 < m_iCount ? (int) count_milliseconds(m_tsLastOriginTime - m_pFirstBlock->m_tsOriginTime) + 1 : 0; + + return m_iCount; +} + +CSndBuffer::duration CSndBuffer::getBufferingDelay(const time_point& tnow) const +{ + ScopedLock lck(m_BufLock); + SRT_ASSERT(m_pFirstBlock); + if (m_iCount == 0) + return duration(0); + + return tnow - m_pFirstBlock->m_tsOriginTime; +} + +int CSndBuffer::dropLateData(int& w_bytes, int32_t& w_first_msgno, const steady_clock::time_point& too_late_time) +{ + int dpkts = 0; + int dbytes = 0; + bool move = false; + int32_t msgno = 0; + + ScopedLock bufferguard(m_BufLock); + for (int i = 0; i < m_iCount && m_pFirstBlock->m_tsOriginTime < too_late_time; ++i) + { + dpkts++; + dbytes += m_pFirstBlock->m_iLength; + msgno = m_pFirstBlock->getMsgSeq(); + + if (m_pFirstBlock == m_pCurrBlock) + move = true; + m_pFirstBlock = m_pFirstBlock->m_pNext; + } + + if (move) + { + m_pCurrBlock = m_pFirstBlock; + } + m_iCount -= dpkts; + + m_iBytesCount -= dbytes; + w_bytes = dbytes; + + // We report the increased number towards the last ever seen + // by the loop, as this last one is the last received. So remained + // (even if "should remain") is the first after the last removed one. + w_first_msgno = ++MsgNo(msgno); + + updAvgBufSize(steady_clock::now()); + + return (dpkts); +} + +void CSndBuffer::increase() +{ + int unitsize = m_pBuffer->m_iSize; + + // new physical buffer + Buffer* nbuf = NULL; + try + { + nbuf = new Buffer; + nbuf->m_pcData = new char[unitsize * m_iBlockLen]; + } + catch (...) + { + delete nbuf; + throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); + } + nbuf->m_iSize = unitsize; + nbuf->m_pNext = NULL; + + // insert the buffer at the end of the buffer list + Buffer* p = m_pBuffer; + while (p->m_pNext != NULL) + p = p->m_pNext; + p->m_pNext = nbuf; + + // new packet blocks + Block* nblk = NULL; + try + { + nblk = new Block; + } + catch (...) + { + delete nblk; + throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); + } + Block* pb = nblk; + for (int i = 1; i < unitsize; ++i) + { + pb->m_pNext = new Block; + pb = pb->m_pNext; + } + + // insert the new blocks onto the existing one + pb->m_pNext = m_pLastBlock->m_pNext; + m_pLastBlock->m_pNext = nblk; + + pb = nblk; + char* pc = nbuf->m_pcData; + for (int i = 0; i < unitsize; ++i) + { + pb->m_pcData = pc; + pb = pb->m_pNext; + pc += m_iBlockLen; + } + + m_iSize += unitsize; + + HLOGC(bslog.Debug, + log << "CSndBuffer: BUFFER FULL - adding " << (unitsize * m_iBlockLen) << " bytes spread to " << unitsize + << " blocks" + << " (total size: " << m_iSize << " bytes)"); +} + +} // namespace srt diff --git a/trunk/3rdparty/srt-1-fit/srtcore/buffer_snd.h b/trunk/3rdparty/srt-1-fit/srtcore/buffer_snd.h new file mode 100644 index 0000000000..4440b9bfd6 --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/buffer_snd.h @@ -0,0 +1,259 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +/***************************************************************************** +Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 05/05/2009 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#ifndef INC_SRT_BUFFER_SND_H +#define INC_SRT_BUFFER_SND_H + +#include "srt.h" +#include "packet.h" +#include "buffer_tools.h" + +// The notation used for "circular numbers" in comments: +// The "cicrular numbers" are numbers that when increased up to the +// maximum become zero, and similarly, when the zero value is decreased, +// it turns into the maximum value minus one. This wrapping works the +// same for adding and subtracting. Circular numbers cannot be multiplied. + +// Operations done on these numbers are marked with additional % character: +// a %> b : a is later than b +// a ++% (++%a) : shift a by 1 forward +// a +% b : shift a by b +// a == b : equality is same as for just numbers + +namespace srt { + +class CSndBuffer +{ + typedef sync::steady_clock::time_point time_point; + typedef sync::steady_clock::duration duration; + +public: + // XXX There's currently no way to access the socket ID set for + // whatever the buffer is currently working for. Required to find + // some way to do this, possibly by having a "reverse pointer". + // Currently just "unimplemented". + std::string CONID() const { return ""; } + + /// @brief CSndBuffer constructor. + /// @param size initial number of blocks (each block to store one packet payload). + /// @param maxpld maximum packet payload (including auth tag). + /// @param authtag auth tag length in bytes (16 for GCM, 0 otherwise). + CSndBuffer(int size = 32, int maxpld = 1500, int authtag = 0); + ~CSndBuffer(); + +public: + /// Insert a user buffer into the sending list. + /// For @a w_mctrl the following fields are used: + /// INPUT: + /// - msgttl: timeout for retransmitting the message, if lost + /// - inorder: request to deliver the message in order of sending + /// - srctime: local time as a base for packet's timestamp (0 if unused) + /// - pktseq: sequence number to be stamped on the packet (-1 if unused) + /// - msgno: message number to be stamped on the packet (-1 if unused) + /// OUTPUT: + /// - srctime: local time stamped on the packet (same as input, if input wasn't 0) + /// - pktseq: sequence number to be stamped on the next packet + /// - msgno: message number stamped on the packet + /// @param [in] data pointer to the user data block. + /// @param [in] len size of the block. + /// @param [inout] w_mctrl Message control data + SRT_ATTR_EXCLUDES(m_BufLock) + void addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl); + + /// Read a block of data from file and insert it into the sending list. + /// @param [in] ifs input file stream. + /// @param [in] len size of the block. + /// @return actual size of data added from the file. + SRT_ATTR_EXCLUDES(m_BufLock) + int addBufferFromFile(std::fstream& ifs, int len); + + /// Find data position to pack a DATA packet from the furthest reading point. + /// @param [out] packet the packet to read. + /// @param [out] origintime origin time stamp of the message + /// @param [in] kflags Odd|Even crypto key flag + /// @param [out] seqnoinc the number of packets skipped due to TTL, so that seqno should be incremented. + /// @return Actual length of data read. + SRT_ATTR_EXCLUDES(m_BufLock) + int readData(CPacket& w_packet, time_point& w_origintime, int kflgs, int& w_seqnoinc); + + /// Peek an information on the next original data packet to send. + /// @return origin time stamp of the next packet; epoch start time otherwise. + SRT_ATTR_EXCLUDES(m_BufLock) + time_point peekNextOriginal() const; + + /// Find data position to pack a DATA packet for a retransmission. + /// @param [in] offset offset from the last ACK point (backward sequence number difference) + /// @param [out] packet the packet to read. + /// @param [out] origintime origin time stamp of the message + /// @param [out] msglen length of the message + /// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded). + SRT_ATTR_EXCLUDES(m_BufLock) + int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); + + /// Get the time of the last retransmission (if any) of the DATA packet. + /// @param [in] offset offset from the last ACK point (backward sequence number difference) + /// + /// @return Last time of the last retransmission event for the corresponding DATA packet. + SRT_ATTR_EXCLUDES(m_BufLock) + time_point getPacketRexmitTime(const int offset); + + /// Update the ACK point and may release/unmap/return the user data according to the flag. + /// @param [in] offset number of packets acknowledged. + int32_t getMsgNoAt(const int offset); + + void ackData(int offset); + + /// Read size of data still in the sending list. + /// @return Current size of the data in the sending list. + int getCurrBufSize() const; + + SRT_ATTR_EXCLUDES(m_BufLock) + int dropLateData(int& bytes, int32_t& w_first_msgno, const time_point& too_late_time); + + void updAvgBufSize(const time_point& time); + int getAvgBufSize(int& bytes, int& timespan); + int getCurrBufSize(int& bytes, int& timespan) const; + + + /// Het maximum payload length per packet. + int getMaxPacketLen() const; + + /// @brief Count the number of required packets to store the payload (message). + /// @param iPldLen the length of the payload to check. + /// @return the number of required data packets. + int countNumPacketsRequired(int iPldLen) const; + + /// @brief Count the number of required packets to store the payload (message). + /// @param iPldLen the length of the payload to check. + /// @param iMaxPktLen the maximum payload length of the packet (the value returned from getMaxPacketLen()). + /// @return the number of required data packets. + int countNumPacketsRequired(int iPldLen, int iMaxPktLen) const; + + /// @brief Get the buffering delay of the oldest message in the buffer. + /// @return the delay value. + SRT_ATTR_EXCLUDES(m_BufLock) + duration getBufferingDelay(const time_point& tnow) const; + + uint64_t getInRatePeriod() const { return m_rateEstimator.getInRatePeriod(); } + + /// Retrieve input bitrate in bytes per second + int getInputRate() const { return m_rateEstimator.getInputRate(); } + + void resetInputRateSmpPeriod(bool disable = false) { m_rateEstimator.resetInputRateSmpPeriod(disable); } + + const CRateEstimator& getRateEstimator() const { return m_rateEstimator; } + + void setRateEstimator(const CRateEstimator& other) { m_rateEstimator = other; } + +private: + void increase(); + +private: + mutable sync::Mutex m_BufLock; // used to synchronize buffer operation + + struct Block + { + char* m_pcData; // pointer to the data block + int m_iLength; // payload length of the block (excluding auth tag). + + int32_t m_iMsgNoBitset; // message number + int32_t m_iSeqNo; // sequence number for scheduling + time_point m_tsOriginTime; // block origin time (either provided from above or equals the time a message was submitted for sending. + time_point m_tsRexmitTime; // packet retransmission time + int m_iTTL; // time to live (milliseconds) + + Block* m_pNext; // next block + + int32_t getMsgSeq() + { + // NOTE: this extracts message ID with regard to REXMIT flag. + // This is valid only for message ID that IS GENERATED in this instance, + // not provided by the peer. This can be otherwise sent to the peer - it doesn't matter + // for the peer that it uses LESS bits to represent the message. + return m_iMsgNoBitset & MSGNO_SEQ::mask; + } + + } * m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock; + + // m_pBlock: The head pointer + // m_pFirstBlock: The first block + // m_pCurrBlock: The current block + // m_pLastBlock: The last block (if first == last, buffer is empty) + + struct Buffer + { + char* m_pcData; // buffer + int m_iSize; // size + Buffer* m_pNext; // next buffer + } * m_pBuffer; // physical buffer + + int32_t m_iNextMsgNo; // next message number + + int m_iSize; // buffer size (number of packets) + const int m_iBlockLen; // maximum length of a block holding packet payload and AUTH tag (excluding packet header). + const int m_iAuthTagSize; // Authentication tag size (if GCM is enabled). + int m_iCount; // number of used blocks + + int m_iBytesCount; // number of payload bytes in queue + time_point m_tsLastOriginTime; + + AvgBufSize m_mavg; + CRateEstimator m_rateEstimator; + +private: + CSndBuffer(const CSndBuffer&); + CSndBuffer& operator=(const CSndBuffer&); +}; + +} // namespace srt + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/buffer_tools.cpp b/trunk/3rdparty/srt-1-fit/srtcore/buffer_tools.cpp new file mode 100644 index 0000000000..0dcf2547fd --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/buffer_tools.cpp @@ -0,0 +1,275 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +/***************************************************************************** +Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 03/12/2011 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#include "platform_sys.h" +#include "buffer_tools.h" +#include "packet.h" +#include "logger_defs.h" +#include "utilities.h" + +namespace srt { + +using namespace std; +using namespace srt_logging; +using namespace sync; + +// You can change this value at build config by using "ENFORCE" options. +#if !defined(SRT_MAVG_SAMPLING_RATE) +#define SRT_MAVG_SAMPLING_RATE 40 +#endif + +bool AvgBufSize::isTimeToUpdate(const time_point& now) const +{ + const int usMAvgBasePeriod = 1000000; // 1s in microseconds + const int us2ms = 1000; + const int msMAvgPeriod = (usMAvgBasePeriod / SRT_MAVG_SAMPLING_RATE) / us2ms; + const uint64_t elapsed_ms = count_milliseconds(now - m_tsLastSamplingTime); // ms since last sampling + return (elapsed_ms >= msMAvgPeriod); +} + +void AvgBufSize::update(const steady_clock::time_point& now, int pkts, int bytes, int timespan_ms) +{ + const uint64_t elapsed_ms = count_milliseconds(now - m_tsLastSamplingTime); // ms since last sampling + m_tsLastSamplingTime = now; + const uint64_t one_second_in_ms = 1000; + if (elapsed_ms > one_second_in_ms) + { + // No sampling in last 1 sec, initialize average + m_dCountMAvg = pkts; + m_dBytesCountMAvg = bytes; + m_dTimespanMAvg = timespan_ms; + return; + } + + // + // weight last average value between -1 sec and last sampling time (LST) + // and new value between last sampling time and now + // |elapsed_ms| + // +----------------------------------+-------+ + // -1 LST 0(now) + // + m_dCountMAvg = avg_iir_w<1000, double>(m_dCountMAvg, pkts, elapsed_ms); + m_dBytesCountMAvg = avg_iir_w<1000, double>(m_dBytesCountMAvg, bytes, elapsed_ms); + m_dTimespanMAvg = avg_iir_w<1000, double>(m_dTimespanMAvg, timespan_ms, elapsed_ms); +} + +CRateEstimator::CRateEstimator() + : m_iInRatePktsCount(0) + , m_iInRateBytesCount(0) + , m_InRatePeriod(INPUTRATE_FAST_START_US) // 0.5 sec (fast start) + , m_iInRateBps(INPUTRATE_INITIAL_BYTESPS) +{} + +void CRateEstimator::setInputRateSmpPeriod(int period) +{ + m_InRatePeriod = (uint64_t)period; //(usec) 0=no input rate calculation +} + +void CRateEstimator::updateInputRate(const time_point& time, int pkts, int bytes) +{ + // no input rate calculation + if (m_InRatePeriod == 0) + return; + + if (is_zero(m_tsInRateStartTime)) + { + m_tsInRateStartTime = time; + return; + } + else if (time < m_tsInRateStartTime) + { + // Old packets are being submitted for estimation, e.g. during the backup link activation. + return; + } + + m_iInRatePktsCount += pkts; + m_iInRateBytesCount += bytes; + + // Trigger early update in fast start mode + const bool early_update = (m_InRatePeriod < INPUTRATE_RUNNING_US) && (m_iInRatePktsCount > INPUTRATE_MAX_PACKETS); + + const uint64_t period_us = count_microseconds(time - m_tsInRateStartTime); + if (!early_update && period_us <= m_InRatePeriod) + return; + + // Required Byte/sec rate (payload + headers) + m_iInRateBytesCount += (m_iInRatePktsCount * CPacket::SRT_DATA_HDR_SIZE); + m_iInRateBps = (int)(((int64_t)m_iInRateBytesCount * 1000000) / period_us); + HLOGC(bslog.Debug, + log << "updateInputRate: pkts:" << m_iInRateBytesCount << " bytes:" << m_iInRatePktsCount + << " rate=" << (m_iInRateBps * 8) / 1000 << "kbps interval=" << period_us); + m_iInRatePktsCount = 0; + m_iInRateBytesCount = 0; + m_tsInRateStartTime = time; + + setInputRateSmpPeriod(INPUTRATE_RUNNING_US); +} + +CSndRateEstimator::CSndRateEstimator(const time_point& tsNow) + : m_tsFirstSampleTime(tsNow) + , m_iFirstSampleIdx(0) + , m_iCurSampleIdx(0) + , m_iRateBps(0) +{ + +} + +void CSndRateEstimator::addSample(const time_point& ts, int pkts, size_t bytes) +{ + const int iSampleDeltaIdx = (int) count_milliseconds(ts - m_tsFirstSampleTime) / SAMPLE_DURATION_MS; + const int delta = NUM_PERIODS - iSampleDeltaIdx; + + // TODO: -delta <= NUM_PERIODS, then just reset the state on the estimator. + + if (iSampleDeltaIdx >= 2 * NUM_PERIODS) + { + // Just reset the estimator and start like if new. + for (int i = 0; i < NUM_PERIODS; ++i) + { + const int idx = incSampleIdx(m_iFirstSampleIdx, i); + m_Samples[idx].reset(); + + if (idx == m_iCurSampleIdx) + break; + } + + m_iFirstSampleIdx = 0; + m_iCurSampleIdx = 0; + m_iRateBps = 0; + m_tsFirstSampleTime += milliseconds_from(iSampleDeltaIdx * SAMPLE_DURATION_MS); + } + else if (iSampleDeltaIdx > NUM_PERIODS) + { + // In run-time a constant flow of samples is expected. Once all periods are filled (after 1 second of sampling), + // the iSampleDeltaIdx should be either (NUM_PERIODS - 1), + // or NUM_PERIODS. In the later case it means the start of a new sampling period. + int d = delta; + while (d < 0) + { + m_Samples[m_iFirstSampleIdx].reset(); + m_iFirstSampleIdx = incSampleIdx(m_iFirstSampleIdx); + m_tsFirstSampleTime += milliseconds_from(SAMPLE_DURATION_MS); + m_iCurSampleIdx = incSampleIdx(m_iCurSampleIdx); + ++d; + } + } + + // Check if the new sample period has started. + const int iNewDeltaIdx = (int) count_milliseconds(ts - m_tsFirstSampleTime) / SAMPLE_DURATION_MS; + if (incSampleIdx(m_iFirstSampleIdx, iNewDeltaIdx) != m_iCurSampleIdx) + { + // Now there should be some periods (at most last NUM_PERIODS) ready to be summed, + // rate estimation updated, after which all the new entry should be added. + Sample sum; + int iNumPeriods = 0; + bool bMetNonEmpty = false; + for (int i = 0; i < NUM_PERIODS; ++i) + { + const int idx = incSampleIdx(m_iFirstSampleIdx, i); + const Sample& s = m_Samples[idx]; + sum += s; + if (bMetNonEmpty || !s.empty()) + { + ++iNumPeriods; + bMetNonEmpty = true; + } + + if (idx == m_iCurSampleIdx) + break; + } + + if (iNumPeriods == 0) + { + m_iRateBps = 0; + } + else + { + m_iRateBps = sum.m_iBytesCount * 1000 / (iNumPeriods * SAMPLE_DURATION_MS); + } + + HLOGC(bslog.Note, + log << "CSndRateEstimator: new rate estimation :" << (m_iRateBps * 8) / 1000 << " kbps. Based on " + << iNumPeriods << " periods, " << sum.m_iPktsCount << " packets, " << sum.m_iBytesCount << " bytes."); + + // Shift one sampling period to start collecting the new one. + m_iCurSampleIdx = incSampleIdx(m_iCurSampleIdx); + m_Samples[m_iCurSampleIdx].reset(); + + // If all NUM_SAMPLES are recorded, the first position has to be shifted as well. + if (delta <= 0) + { + m_iFirstSampleIdx = incSampleIdx(m_iFirstSampleIdx); + m_tsFirstSampleTime += milliseconds_from(SAMPLE_DURATION_MS); + } + } + + m_Samples[m_iCurSampleIdx].m_iBytesCount += bytes; + m_Samples[m_iCurSampleIdx].m_iPktsCount += pkts; +} + +int CSndRateEstimator::getCurrentRate() const +{ + SRT_ASSERT(m_iCurSampleIdx >= 0 && m_iCurSampleIdx < NUM_PERIODS); + return (int) avg_iir<16, unsigned long long>(m_iRateBps, m_Samples[m_iCurSampleIdx].m_iBytesCount * 1000 / SAMPLE_DURATION_MS); +} + +int CSndRateEstimator::incSampleIdx(int val, int inc) const +{ + SRT_ASSERT(inc >= 0 && inc <= NUM_PERIODS); + val += inc; + while (val >= NUM_PERIODS) + val -= NUM_PERIODS; + return val; +} + +} + diff --git a/trunk/3rdparty/srt-1-fit/srtcore/buffer_tools.h b/trunk/3rdparty/srt-1-fit/srtcore/buffer_tools.h new file mode 100644 index 0000000000..e6ce89d0de --- /dev/null +++ b/trunk/3rdparty/srt-1-fit/srtcore/buffer_tools.h @@ -0,0 +1,201 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +/***************************************************************************** +Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the + above copyright notice, this list of conditions + and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the University of Illinois + nor the names of its contributors may be used to + endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/***************************************************************************** +written by + Yunhong Gu, last updated 05/05/2009 +modified by + Haivision Systems Inc. +*****************************************************************************/ + +#ifndef INC_SRT_BUFFER_TOOLS_H +#define INC_SRT_BUFFER_TOOLS_H + +#include "common.h" + +namespace srt +{ + +/// The AvgBufSize class is used to calculate moving average of the buffer (RCV or SND) +class AvgBufSize +{ + typedef sync::steady_clock::time_point time_point; + +public: + AvgBufSize() + : m_dBytesCountMAvg(0.0) + , m_dCountMAvg(0.0) + , m_dTimespanMAvg(0.0) + { + } + +public: + bool isTimeToUpdate(const time_point& now) const; + void update(const time_point& now, int pkts, int bytes, int timespan_ms); + +public: + inline double pkts() const { return m_dCountMAvg; } + inline double timespan_ms() const { return m_dTimespanMAvg; } + inline double bytes() const { return m_dBytesCountMAvg; } + +private: + time_point m_tsLastSamplingTime; + double m_dBytesCountMAvg; + double m_dCountMAvg; + double m_dTimespanMAvg; +}; + +/// The class to estimate source bitrate based on samples submitted to the buffer. +/// Is currently only used by the CSndBuffer. +class CRateEstimator +{ + typedef sync::steady_clock::time_point time_point; + typedef sync::steady_clock::duration duration; +public: + CRateEstimator(); + +public: + uint64_t getInRatePeriod() const { return m_InRatePeriod; } + + /// Retrieve input bitrate in bytes per second + int getInputRate() const { return m_iInRateBps; } + + void setInputRateSmpPeriod(int period); + + /// Update input rate calculation. + /// @param [in] time current time + /// @param [in] pkts number of packets newly added to the buffer + /// @param [in] bytes number of payload bytes in those newly added packets + void updateInputRate(const time_point& time, int pkts = 0, int bytes = 0); + + void resetInputRateSmpPeriod(bool disable = false) { setInputRateSmpPeriod(disable ? 0 : INPUTRATE_FAST_START_US); } + +private: // Constants + static const uint64_t INPUTRATE_FAST_START_US = 500000; // 500 ms + static const uint64_t INPUTRATE_RUNNING_US = 1000000; // 1000 ms + static const int64_t INPUTRATE_MAX_PACKETS = 2000; // ~ 21 Mbps of 1316 bytes payload + static const int INPUTRATE_INITIAL_BYTESPS = BW_INFINITE; + +private: + int m_iInRatePktsCount; // number of payload packets added since InRateStartTime. + int m_iInRateBytesCount; // number of payload bytes added since InRateStartTime. + time_point m_tsInRateStartTime; + uint64_t m_InRatePeriod; // usec + int m_iInRateBps; // Input Rate in Bytes/sec +}; + + +class CSndRateEstimator +{ + typedef sync::steady_clock::time_point time_point; + +public: + CSndRateEstimator(const time_point& tsNow); + + /// Add sample. + /// @param [in] time sample (sending) time. + /// @param [in] pkts number of packets in the sample. + /// @param [in] bytes number of payload bytes in the sample. + void addSample(const time_point& time, int pkts = 0, size_t bytes = 0); + + /// Retrieve estimated bitrate in bytes per second + int getRate() const { return m_iRateBps; } + + /// Retrieve estimated bitrate in bytes per second inluding the current sampling interval. + int getCurrentRate() const; + +private: + static const int NUM_PERIODS = 10; + static const int SAMPLE_DURATION_MS = 100; // 100 ms + struct Sample + { + int m_iPktsCount; // number of payload packets + int m_iBytesCount; // number of payload bytes + + void reset() + { + m_iPktsCount = 0; + m_iBytesCount = 0; + } + + Sample() + : m_iPktsCount(0) + , m_iBytesCount(0) + { + } + + Sample(int iPkts, int iBytes) + : m_iPktsCount(iPkts) + , m_iBytesCount(iBytes) + { + } + + Sample operator+(const Sample& other) + { + return Sample(m_iPktsCount + other.m_iPktsCount, m_iBytesCount + other.m_iBytesCount); + } + + Sample& operator+=(const Sample& other) + { + *this = *this + other; + return *this; + } + + bool empty() const { return m_iPktsCount == 0; } + }; + + int incSampleIdx(int val, int inc = 1) const; + + Sample m_Samples[NUM_PERIODS]; + + time_point m_tsFirstSampleTime; //< Start time of the first sameple. + int m_iFirstSampleIdx; //< Index of the first sample. + int m_iCurSampleIdx; //< Index of the current sample being collected. + int m_iRateBps; // Input Rate in Bytes/sec +}; + +} // namespace srt + +#endif diff --git a/trunk/3rdparty/srt-1-fit/srtcore/cache.cpp b/trunk/3rdparty/srt-1-fit/srtcore/cache.cpp index e4fa90d874..4cda9a70f3 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/cache.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/cache.cpp @@ -62,7 +62,7 @@ srt::CInfoBlock& srt::CInfoBlock::copyFrom(const CInfoBlock& obj) return *this; } -bool srt::CInfoBlock::operator==(const CInfoBlock& obj) +bool srt::CInfoBlock::operator==(const CInfoBlock& obj) const { if (m_iIPversion != obj.m_iIPversion) return false; diff --git a/trunk/3rdparty/srt-1-fit/srtcore/cache.h b/trunk/3rdparty/srt-1-fit/srtcore/cache.h index 0dd57ba01c..47633706a5 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/cache.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/cache.h @@ -253,7 +253,7 @@ class CInfoBlock CInfoBlock& copyFrom(const CInfoBlock& obj); CInfoBlock(const CInfoBlock& src) { copyFrom(src); } CInfoBlock& operator=(const CInfoBlock& src) { return copyFrom(src); } - bool operator==(const CInfoBlock& obj); + bool operator==(const CInfoBlock& obj) const; CInfoBlock* clone(); int getKey(); void release() {} diff --git a/trunk/3rdparty/srt-1-fit/srtcore/channel.cpp b/trunk/3rdparty/srt-1-fit/srtcore/channel.cpp index 30fd987787..091adf1159 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/channel.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/channel.cpp @@ -139,7 +139,23 @@ static int set_cloexec(int fd, int set) srt::CChannel::CChannel() : m_iSocket(INVALID_SOCKET) +#ifdef SRT_ENABLE_PKTINFO + , m_bBindMasked(true) +#endif { +#ifdef SRT_ENABLE_PKTINFO + // Do the check for ancillary data buffer size, kinda assertion + static const size_t CMSG_MAX_SPACE = sizeof (CMSGNodeIPv4) + sizeof (CMSGNodeIPv6); + + if (CMSG_MAX_SPACE < CMSG_SPACE(sizeof(in_pktinfo)) + CMSG_SPACE(sizeof(in6_pktinfo))) + { + LOGC(kmlog.Fatal, log << "Size of CMSG_MAX_SPACE=" + << CMSG_MAX_SPACE << " too short for cmsg " + << CMSG_SPACE(sizeof(in_pktinfo)) << ", " + << CMSG_SPACE(sizeof(in6_pktinfo)) << " - PLEASE FIX"); + throw CUDTException(MJ_SETUP, MN_NONE, 0); + } +#endif } srt::CChannel::~CChannel() {} @@ -207,6 +223,9 @@ void srt::CChannel::open(const sockaddr_any& addr) throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); m_BindAddr = addr; +#ifdef SRT_ENABLE_PKTINFO + m_bBindMasked = m_BindAddr.isany(); +#endif LOGC(kmlog.Debug, log << "CHANNEL: Bound to local address: " << m_BindAddr.str()); setUDPSockOpt(); @@ -247,6 +266,12 @@ void srt::CChannel::open(int family) } m_BindAddr = sockaddr_any(res->ai_addr, (sockaddr_any::len_t)res->ai_addrlen); +#ifdef SRT_ENABLE_PKTINFO + // We know that this is intentionally bound now to "any", + // so the requester-destination address must be remembered and passed. + m_bBindMasked = true; +#endif + ::freeaddrinfo(res); HLOGC(kmlog.Debug, log << "CHANNEL: Bound to local address: " << m_BindAddr.str()); @@ -472,6 +497,27 @@ void srt::CChannel::setUDPSockOpt() if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(timeval))) throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); #endif + +#ifdef SRT_ENABLE_PKTINFO + if (m_bBindMasked) + { + HLOGP(kmlog.Debug, "Socket bound to ANY - setting PKTINFO for address retrieval"); + const int on = 1, off SRT_ATR_UNUSED = 0; + + if (m_BindAddr.family() == AF_INET || m_mcfg.iIpV6Only == 0) + { + ::setsockopt(m_iSocket, IPPROTO_IP, IP_PKTINFO, (char*)&on, sizeof(on)); + } + + if (m_BindAddr.family() == AF_INET6) + { + ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + } + + // XXX Unknown why this has to be off. RETEST. + //::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); + } +#endif } void srt::CChannel::close() const @@ -502,6 +548,11 @@ void srt::CChannel::setConfig(const CSrtMuxerConfig& config) m_mcfg = config; } +void srt::CChannel::getSocketOption(int level, int option, char* pw_dataptr, socklen_t& w_len, int& w_status) +{ + w_status = ::getsockopt(m_iSocket, level, option, (pw_dataptr), (&w_len)); +} + int srt::CChannel::getIpTTL() const { if (m_iSocket == INVALID_SOCKET) @@ -610,11 +661,19 @@ void srt::CChannel::getPeerAddr(sockaddr_any& w_addr) const w_addr.len = namelen; } -int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const +int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const sockaddr_any& source_addr SRT_ATR_UNUSED) const { - HLOGC(kslog.Debug, - log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.m_iID - << " size=" << packet.getLength() << " pkt.ts=" << packet.m_iTimeStamp << " " << packet.Info()); +#if ENABLE_HEAVY_LOGGING + ostringstream dsrc; +#ifdef SRT_ENABLE_PKTINFO + dsrc << " sourceIP=" << (m_bBindMasked && !source_addr.isany() ? source_addr.str() : "default"); +#endif + + LOGC(kslog.Debug, + log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.m_iID + << " size=" << packet.getLength() << " pkt.ts=" << packet.m_iTimeStamp + << dsrc.str() << " " << packet.Info()); +#endif #ifdef SRT_TEST_FAKE_LOSS @@ -683,16 +742,51 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const mh.msg_namelen = addr.size(); mh.msg_iov = (iovec*)packet.m_PacketVector; mh.msg_iovlen = 2; - mh.msg_control = NULL; - mh.msg_controllen = 0; + bool have_set_src = false; + +#ifdef SRT_ENABLE_PKTINFO + + // Note that even if PKTINFO is desired, the first caller's packet will be sent + // without ancillary info anyway because there's no "peer" yet to know where to send it. + if (m_bBindMasked && source_addr.family() != AF_UNSPEC && !source_addr.isany()) + { + if (!setSourceAddress(mh, source_addr)) + { + LOGC(kslog.Error, log << "CChannel::setSourceAddress: source address invalid family #" << source_addr.family() << ", NOT setting."); + } + else + { + HLOGC(kslog.Debug, log << "CChannel::setSourceAddress: setting as " << source_addr.str()); + have_set_src = true; + } + } + +#endif + + if (!have_set_src) + { + mh.msg_control = NULL; + mh.msg_controllen = 0; + } mh.msg_flags = 0; - const int res = ::sendmsg(m_iSocket, &mh, 0); + const int res = (int)::sendmsg(m_iSocket, &mh, 0); #else DWORD size = (DWORD)(CPacket::HDR_SIZE + packet.getLength()); int addrsize = addr.size(); - int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, NULL, NULL); - res = (0 == res) ? size : -1; + WSAOVERLAPPED overlapped; + SecureZeroMemory((PVOID)&overlapped, sizeof(WSAOVERLAPPED)); + int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, &overlapped, NULL); + + if (res == SOCKET_ERROR && NET_ERROR == WSA_IO_PENDING) + { + DWORD dwFlags = 0; + const bool bCompleted = WSAGetOverlappedResult(m_iSocket, &overlapped, &size, true, &dwFlags); + WSACloseEvent(overlapped.hEvent); + res = bCompleted ? 0 : -1; + } + + res = (0 == res) ? size : -1; #endif packet.toHL(); @@ -725,18 +819,36 @@ srt::EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet } #ifndef _WIN32 + msghdr mh; // will not be used on failure + if (select_ret > 0) { - msghdr mh; mh.msg_name = (w_addr.get()); mh.msg_namelen = w_addr.size(); mh.msg_iov = (w_packet.m_PacketVector); mh.msg_iovlen = 2; + + // Default mh.msg_control = NULL; mh.msg_controllen = 0; + +#ifdef SRT_ENABLE_PKTINFO + // Without m_bBindMasked, we don't need ancillary data - the source + // address will always be the bound address. + if (m_bBindMasked) + { + // Extract the destination IP address from the ancillary + // data. This might be interesting for the connection to + // know to which address the packet should be sent back during + // the handshake and then addressed when sending during connection. + mh.msg_control = (m_acCmsgRecvBuffer); + mh.msg_controllen = sizeof m_acCmsgRecvBuffer; + } +#endif + mh.msg_flags = 0; - recv_size = ::recvmsg(m_iSocket, (&mh), 0); + recv_size = (int)::recvmsg(m_iSocket, (&mh), 0); msg_flags = mh.msg_flags; } @@ -779,6 +891,17 @@ srt::EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet goto Return_error; } +#ifdef SRT_ENABLE_PKTINFO + if (m_bBindMasked) + { + // Extract the address. Set it explicitly; if this returns address that isany(), + // it will simply set this on the packet so that it behaves as if nothing was + // extracted (it will "fail the old way"). + w_packet.m_DestAddr = getTargetAddress(mh); + HLOGC(krlog.Debug, log << CONID() << "(sys)recvmsg: ANY BOUND, retrieved DEST ADDR: " << w_packet.m_DestAddr.str()); + } +#endif + #else // XXX REFACTORING NEEDED! // This procedure uses the WSARecvFrom function that just reads @@ -876,9 +999,30 @@ srt::EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet // packet was received, so the packet will be then retransmitted. if (msg_flags != 0) { +#if ENABLE_HEAVY_LOGGING + + std::ostringstream flg; + +#if !defined(_WIN32) + + static const pair errmsgflg [] = { + make_pair(MSG_OOB, "OOB"), + make_pair(MSG_EOR, "EOR"), + make_pair(MSG_TRUNC, "TRUNC"), + make_pair(MSG_CTRUNC, "CTRUNC") + }; + + for (size_t i = 0; i < Size(errmsgflg); ++i) + if ((msg_flags & errmsgflg[i].first) != 0) + flg << " " << errmsgflg[i].second; + + // This doesn't work the same way on Windows, so on Windows just skip it. +#endif + HLOGC(krlog.Debug, log << CONID() << "NET ERROR: packet size=" << recv_size << " msg_flags=0x" << hex << msg_flags - << ", possibly MSG_TRUNC)"); + << ", detected flags:" << flg.str()); +#endif status = RST_AGAIN; goto Return_error; } diff --git a/trunk/3rdparty/srt-1-fit/srtcore/channel.h b/trunk/3rdparty/srt-1-fit/srtcore/channel.h index 0255102fe7..1bfcc47c85 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/channel.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/channel.h @@ -115,9 +115,10 @@ class CChannel /// Send a packet to the given address. /// @param [in] addr pointer to the destination address. /// @param [in] packet reference to a CPacket entity. + /// @param [in] src source address to sent on an outgoing packet (if not ANY) /// @return Actual size of data sent. - int sendto(const sockaddr_any& addr, srt::CPacket& packet) const; + int sendto(const sockaddr_any& addr, srt::CPacket& packet, const sockaddr_any& src) const; /// Receive a packet from the channel and record the source address. /// @param [in] addr pointer to the source address. @@ -128,6 +129,21 @@ class CChannel void setConfig(const CSrtMuxerConfig& config); + void getSocketOption(int level, int sockoptname, char* pw_dataptr, socklen_t& w_len, int& w_status); + + template + Type sockopt(int level, int sockoptname, Type deflt) + { + Type retval; + socklen_t socklen = sizeof retval; + int status; + getSocketOption(level, sockoptname, ((char*)&retval), (socklen), (status)); + if (status == -1) + return deflt; + + return retval; + } + /// Get the IP TTL. /// @param [in] ttl IP Time To Live. /// @return TTL. @@ -160,6 +176,121 @@ class CChannel // although the object itself isn't considered modified. mutable CSrtMuxerConfig m_mcfg; // Note: ReuseAddr is unused and ineffective. sockaddr_any m_BindAddr; + + // This feature is not enabled on Windows, for now. + // This is also turned off in case of MinGW +#ifdef SRT_ENABLE_PKTINFO + bool m_bBindMasked; // True if m_BindAddr is INADDR_ANY. Need for quick check. + + // Calculating the required space is extremely tricky, and whereas on most + // platforms it's possible to define it this way: + // + // size_t s = max( CMSG_SPACE(sizeof(in_pktinfo)), CMSG_SPACE(sizeof(in6_pktinfo)) ) + // + // ...on some platforms however CMSG_SPACE macro can't be resolved as constexpr. + // + // This structure is exclusively used to determine the required size for + // CMSG buffer so that it can be allocated in a solid block with CChannel. + // NOT TO BE USED to access any data inside the CMSG message. + struct CMSGNodeIPv4 + { + in_pktinfo in4; + size_t extrafill; + cmsghdr hdr; + }; + + struct CMSGNodeIPv6 + { + in6_pktinfo in6; + size_t extrafill; + cmsghdr hdr; + }; + + // This is 'mutable' because it's a utility buffer defined here + // to avoid unnecessary re-allocations. + mutable char m_acCmsgRecvBuffer [sizeof (CMSGNodeIPv4) + sizeof (CMSGNodeIPv6)]; // Reserved space for ancillary data with pktinfo + mutable char m_acCmsgSendBuffer [sizeof (CMSGNodeIPv4) + sizeof (CMSGNodeIPv6)]; // Reserved space for ancillary data with pktinfo + + // IMPORTANT!!! This function shall be called EXCLUSIVELY just after + // calling ::recvmsg function. It uses a static buffer to supply data + // for the call, and it's stated that only one thread is trying to + // use a CChannel object in receiving mode. + sockaddr_any getTargetAddress(const msghdr& msg) const + { + // Loop through IP header messages + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + for (cmsg = CMSG_FIRSTHDR(&msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR(((msghdr*)&msg), cmsg)) + { + // This should be safe - this packet contains always either + // IPv4 headers or IPv6 headers. + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) + { + in_pktinfo *dest_ip_ptr = (in_pktinfo*)CMSG_DATA(cmsg); + return sockaddr_any(dest_ip_ptr->ipi_addr, 0); + } + + if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) + { + in6_pktinfo* dest_ip_ptr = (in6_pktinfo*)CMSG_DATA(cmsg); + return sockaddr_any(dest_ip_ptr->ipi6_addr, 0); + } + } + + // Fallback for an error + return sockaddr_any(m_BindAddr.family()); + } + + // IMPORTANT!!! This function shall be called EXCLUSIVELY just before + // calling ::sendmsg function. It uses a static buffer to supply data + // for the call, and it's stated that only one thread is trying to + // use a CChannel object in sending mode. + bool setSourceAddress(msghdr& mh, const sockaddr_any& adr) const + { + // In contrast to an advice followed on the net, there's no case of putting + // both IPv4 and IPv6 ancillary data, case we could have them. Only one + // IP version is used and it's the version as found in @a adr, which should + // be the version used for binding. + + if (adr.family() == AF_INET) + { + mh.msg_control = m_acCmsgSendBuffer; + mh.msg_controllen = CMSG_SPACE(sizeof(in_pktinfo)); + cmsghdr* cmsg_send = CMSG_FIRSTHDR(&mh); + + // after initializing msghdr & control data to CMSG_SPACE(sizeof(struct in_pktinfo)) + cmsg_send->cmsg_level = IPPROTO_IP; + cmsg_send->cmsg_type = IP_PKTINFO; + cmsg_send->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + in_pktinfo* pktinfo = (in_pktinfo*) CMSG_DATA(cmsg_send); + pktinfo->ipi_ifindex = 0; + pktinfo->ipi_spec_dst = adr.sin.sin_addr; + + return true; + } + + if (adr.family() == AF_INET6) + { + mh.msg_control = m_acCmsgSendBuffer; + mh.msg_controllen = CMSG_SPACE(sizeof(in6_pktinfo)); + cmsghdr* cmsg_send = CMSG_FIRSTHDR(&mh); + + cmsg_send->cmsg_level = IPPROTO_IPV6; + cmsg_send->cmsg_type = IPV6_PKTINFO; + cmsg_send->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); + in6_pktinfo* pktinfo = (in6_pktinfo*) CMSG_DATA(cmsg_send); + pktinfo->ipi6_ifindex = 0; + pktinfo->ipi6_addr = adr.sin6.sin6_addr; + + return true; + } + + return false; + } + +#endif // SRT_ENABLE_PKTINFO + }; } // namespace srt diff --git a/trunk/3rdparty/srt-1-fit/srtcore/common.cpp b/trunk/3rdparty/srt-1-fit/srtcore/common.cpp index 78014d7921..b621c8025e 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/common.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/common.cpp @@ -168,11 +168,7 @@ void srt::CIPAddress::ntop(const sockaddr_any& addr, uint32_t ip[4]) } else { - const sockaddr_in6* a = &addr.sin6; - ip[3] = (a->sin6_addr.s6_addr[15] << 24) + (a->sin6_addr.s6_addr[14] << 16) + (a->sin6_addr.s6_addr[13] << 8) + a->sin6_addr.s6_addr[12]; - ip[2] = (a->sin6_addr.s6_addr[11] << 24) + (a->sin6_addr.s6_addr[10] << 16) + (a->sin6_addr.s6_addr[9] << 8) + a->sin6_addr.s6_addr[8]; - ip[1] = (a->sin6_addr.s6_addr[7] << 24) + (a->sin6_addr.s6_addr[6] << 16) + (a->sin6_addr.s6_addr[5] << 8) + a->sin6_addr.s6_addr[4]; - ip[0] = (a->sin6_addr.s6_addr[3] << 24) + (a->sin6_addr.s6_addr[2] << 16) + (a->sin6_addr.s6_addr[1] << 8) + a->sin6_addr.s6_addr[0]; + std::memcpy(ip, addr.sin6.sin6_addr.s6_addr, 16); } } @@ -223,18 +219,7 @@ void srt::CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], const soc // Here both agent and peer use IPv6, in which case // `ip` contains the full IPv6 address, so just copy // it as is. - - // XXX Possibly, a simple - // memcpy( (a->sin6_addr.s6_addr), ip, 16); - // would do the same thing, and faster. The address in `ip`, - // even though coded here as uint32_t, is still big endian. - for (int i = 0; i < 4; ++ i) - { - a->sin6_addr.s6_addr[i * 4 + 0] = ip[i] & 0xFF; - a->sin6_addr.s6_addr[i * 4 + 1] = (unsigned char)((ip[i] & 0xFF00) >> 8); - a->sin6_addr.s6_addr[i * 4 + 2] = (unsigned char)((ip[i] & 0xFF0000) >> 16); - a->sin6_addr.s6_addr[i * 4 + 3] = (unsigned char)((ip[i] & 0xFF000000) >> 24); - } + std::memcpy(a->sin6_addr.s6_addr, ip, 16); return; // The address is written, nothing left to do. } @@ -457,7 +442,7 @@ std::string TransmissionEventStr(ETransmissionEvent ev) return vals[ev]; } -bool SrtParseConfig(string s, SrtConfig& w_config) +bool SrtParseConfig(const string& s, SrtConfig& w_config) { using namespace std; diff --git a/trunk/3rdparty/srt-1-fit/srtcore/common.h b/trunk/3rdparty/srt-1-fit/srtcore/common.h index 227a91861c..5021fa5a88 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/common.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/common.h @@ -53,7 +53,6 @@ modified by #ifndef INC_SRT_COMMON_H #define INC_SRT_COMMON_H -#define _CRT_SECURE_NO_WARNINGS 1 // silences windows complaints for sscanf #include #include #include @@ -1393,8 +1392,11 @@ inline ATR_CONSTEXPR uint32_t SrtVersion(int major, int minor, int patch) inline int32_t SrtParseVersion(const char* v) { int major, minor, patch; +#if defined(_MSC_VER) + int result = sscanf_s(v, "%d.%d.%d", &major, &minor, &patch); +#else int result = sscanf(v, "%d.%d.%d", &major, &minor, &patch); - +#endif if (result != 3) { return 0; @@ -1409,12 +1411,16 @@ inline std::string SrtVersionString(int version) int minor = (version/0x100)%0x100; int major = version/0x10000; - char buf[20]; - sprintf(buf, "%d.%d.%d", major, minor, patch); + char buf[22]; +#if defined(_MSC_VER) && _MSC_VER < 1900 + _snprintf(buf, sizeof(buf) - 1, "%d.%d.%d", major, minor, patch); +#else + snprintf(buf, sizeof(buf), "%d.%d.%d", major, minor, patch); +#endif return buf; } -bool SrtParseConfig(std::string s, SrtConfig& w_config); +bool SrtParseConfig(const std::string& s, SrtConfig& w_config); } // namespace srt diff --git a/trunk/3rdparty/srt-1-fit/srtcore/congctl.cpp b/trunk/3rdparty/srt-1-fit/srtcore/congctl.cpp index 33b5389e02..91c73d6609 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/congctl.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/congctl.cpp @@ -230,7 +230,7 @@ class LiveCC: public SrtCongestionControlBase * For realtime Transport Stream content, pkts/sec is not a good indication of time to transmit * since packets are not filled to m_iMSS and packet size average is lower than (7*188) * for low bit rates. - * If NAK report is lost, another cycle (RTT) is requred which is bad for low latency so we + * If NAK report is lost, another cycle (RTT) is required which is bad for low latency so we * accelerate the NAK Reports frequency, at the cost of possible duplicate resend. * Finally, the UDT4 native minimum NAK interval (m_ullMinNakInt_tk) is 300 ms which is too high * (~10 i30 video frames) to maintain low latency. diff --git a/trunk/3rdparty/srt-1-fit/srtcore/congctl.h b/trunk/3rdparty/srt-1-fit/srtcore/congctl.h index b957dbdd02..a2264b5948 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/congctl.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/congctl.h @@ -52,8 +52,8 @@ class SrtCongestion struct IsName { - std::string n; - IsName(std::string nn): n(nn) {} + const std::string n; + IsName(const std::string& nn): n(nn) {} bool operator()(NamePtr np) { return n == np.first; } }; diff --git a/trunk/3rdparty/srt-1-fit/srtcore/core.cpp b/trunk/3rdparty/srt-1-fit/srtcore/core.cpp index 93224f418b..084cad670f 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/core.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/core.cpp @@ -62,6 +62,7 @@ modified by #include #include #include "srt.h" +#include "access_control.h" // Required for SRT_REJX_FALLBACK #include "queue.h" #include "api.h" #include "core.h" @@ -203,6 +204,9 @@ struct SrtOptionAction #endif flags[SRTO_PACKETFILTER] = SRTO_R_PRE; flags[SRTO_RETRANSMITALGO] = SRTO_R_PRE; +#ifdef ENABLE_AEAD_API_PREVIEW + flags[SRTO_CRYPTOMODE] = SRTO_R_PRE; +#endif // For "private" options (not derived from the listener // socket by an accepted socket) provide below private_default @@ -289,7 +293,6 @@ void srt::CUDT::construct() m_iTsbPdDelay_ms = 0; m_iPeerTsbPdDelay_ms = 0; m_bPeerTsbPd = false; - m_iPeerTsbPdDelay_ms = 0; m_bTsbPd = false; m_bTsbPdAckWakeup = false; m_bGroupTsbPd = false; @@ -302,7 +305,13 @@ void srt::CUDT::construct() // m_cbPacketArrival.set(this, &CUDT::defaultPacketArrival); } -srt::CUDT::CUDT(CUDTSocket* parent): m_parent(parent) +srt::CUDT::CUDT(CUDTSocket* parent) + : m_parent(parent) +#ifdef ENABLE_MAXREXMITBW + , m_SndRexmitRate(sync::steady_clock::now()) +#endif + , m_iISN(-1) + , m_iPeerISN(-1) { construct(); @@ -325,7 +334,13 @@ srt::CUDT::CUDT(CUDTSocket* parent): m_parent(parent) } -srt::CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) +srt::CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor) + : m_parent(parent) +#ifdef ENABLE_MAXREXMITBW + , m_SndRexmitRate(sync::steady_clock::now()) +#endif + , m_iISN(-1) + , m_iPeerISN(-1) { construct(); @@ -611,8 +626,8 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) } // Fallback: return from internal data - strcpy(((char*)optval), m_config.sBindToDevice.c_str()); - optlen = m_config.sBindToDevice.size(); + optlen = (int)m_config.sBindToDevice.copy((char*)optval, (size_t)optlen - 1); + ((char*)optval)[optlen] = '\0'; #else LOGC(smlog.Error, log << "SRTO_BINDTODEVICE is not supported on that platform"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -734,16 +749,16 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) if (size_t(optlen) < m_config.sStreamName.size() + 1) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - strcpy((char *)optval, m_config.sStreamName.c_str()); - optlen = (int) m_config.sStreamName.size(); + optlen = (int)m_config.sStreamName.copy((char*)optval, (size_t)optlen - 1); + ((char*)optval)[optlen] = '\0'; break; case SRTO_CONGESTION: if (size_t(optlen) < m_config.sCongestion.size() + 1) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - strcpy((char *)optval, m_config.sCongestion.c_str()); - optlen = (int) m_config.sCongestion.size(); + optlen = (int)m_config.sCongestion.copy((char*)optval, (size_t)optlen - 1); + ((char*)optval)[optlen] = '\0'; break; case SRTO_MESSAGEAPI: @@ -802,14 +817,23 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) if (size_t(optlen) < m_config.sPacketFilterConfig.size() + 1) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - strcpy((char *)optval, m_config.sPacketFilterConfig.c_str()); - optlen = (int) m_config.sPacketFilterConfig.size(); + optlen = (int)m_config.sPacketFilterConfig.copy((char*)optval, (size_t)optlen - 1); + ((char*)optval)[optlen] = '\0'; break; case SRTO_RETRANSMITALGO: *(int32_t *)optval = m_config.iRetransmitAlgo; optlen = sizeof(int32_t); break; +#ifdef ENABLE_AEAD_API_PREVIEW + case SRTO_CRYPTOMODE: + if (m_pCryptoControl) + *(int32_t*)optval = m_pCryptoControl->getCryptoMode(); + else + *(int32_t*)optval = m_config.iCryptoMode; + optlen = sizeof(int32_t); + break; +#endif default: throw CUDTException(MJ_NOTSUP, MN_NONE, 0); @@ -824,7 +848,7 @@ SRT_ERRNO srt::CUDT::applyMemberConfigObject(const SRT_SocketOptionObject& opt) for (size_t i = 0; i < opt.options.size(); ++i) { SRT_SocketOptionObject::SingleOption* o = opt.options[i]; - HLOGC(smlog.Debug, log << "applyMemberConfigObject: OPTION @" << m_SocketID << " #" << o->option); + HLOGC(smlog.Debug, log << CONID() << "applyMemberConfigObject: OPTION @" << m_SocketID << " #" << o->option); this_opt = SRT_SOCKOPT(o->option); setOpt(this_opt, o->storage, o->length); } @@ -859,14 +883,11 @@ string srt::CUDT::getstreamid(SRTSOCKET u) // XXX REFACTOR: Make common code for CUDT constructor and clearData, // possibly using CUDT::construct. +// Initial sequence number, loss, acknowledgement, etc. void srt::CUDT::clearData() { - // Initial sequence number, loss, acknowledgement, etc. - int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE; - - m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; - - HLOGC(cnlog.Debug, log << "clearData: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + m_iMaxSRTPayloadSize = m_config.iMSS - CPacket::UDP_HDR_SIZE - CPacket::HDR_SIZE; + HLOGC(cnlog.Debug, log << CONID() << "clearData: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); m_iEXPCount = 1; m_iBandwidth = 1; // pkts/sec @@ -949,9 +970,11 @@ void srt::CUDT::open() m_tsLastRspAckTime = currtime; m_tsLastSndTime.store(currtime); +#if ENABLE_BONDING m_tsUnstableSince = steady_clock::time_point(); m_tsFreshActivation = steady_clock::time_point(); m_tsWarySince = steady_clock::time_point(); +#endif m_iReXmitCount = 1; m_iPktCount = 0; @@ -989,7 +1012,7 @@ size_t srt::CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgt if (srtlen < SRT_HS_E_SIZE) { LOGC(cnlog.Fatal, - log << "IPE: fillSrtHandshake: buffer too small: " << srtlen << " (expected: " << SRT_HS_E_SIZE << ")"); + log << CONID() << "IPE: fillSrtHandshake: buffer too small: " << srtlen << " (expected: " << SRT_HS_E_SIZE << ")"); return 0; } @@ -1007,7 +1030,7 @@ size_t srt::CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgt case SRT_CMD_HSRSP: return fillSrtHandshake_HSRSP((aw_srtdata), srtlen, hs_version); default: - LOGC(cnlog.Fatal, log << "IPE: fillSrtHandshake/sendSrtMsg called with value " << msgtype); + LOGC(cnlog.Fatal, log << CONID() << "IPE: fillSrtHandshake/sendSrtMsg called with value " << msgtype); return 0; } } @@ -1068,7 +1091,7 @@ size_t srt::CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_STREAM; HLOGC(cnlog.Debug, - log << "HSREQ/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(aw_srtdata[SRT_HS_LATENCY]) + log << CONID() << "HSREQ/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(aw_srtdata[SRT_HS_LATENCY]) << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(aw_srtdata[SRT_HS_LATENCY]) << "] FLAGS[" << SrtFlagString(aw_srtdata[SRT_HS_FLAGS]) << "]"); @@ -1082,7 +1105,7 @@ size_t srt::CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen // getting first received HSREQ. Doesn't look possible in both HSv4 and HSv5. if (is_zero(m_tsRcvPeerStartTime)) { - LOGC(cnlog.Fatal, log << "IPE: fillSrtHandshake_HSRSP: m_tsRcvPeerStartTime NOT SET!"); + LOGC(cnlog.Fatal, log << CONID() << "IPE: fillSrtHandshake_HSRSP: m_tsRcvPeerStartTime NOT SET!"); return 0; } @@ -1110,7 +1133,7 @@ size_t srt::CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen } else { - HLOGC(cnlog.Debug, log << "HSRSP/snd: TSBPD off, NOT responding TSBPDRCV flag."); + HLOGC(cnlog.Debug, log << CONID() << "HSRSP/snd: TSBPD off, NOT responding TSBPDRCV flag."); } // Hsv5, only when peer has declared TSBPD mode. @@ -1123,12 +1146,13 @@ size_t srt::CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen aw_srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms); HLOGC(cnlog.Debug, - log << "HSRSP/snd: HSv5 peer uses TSBPD, responding TSBPDSND latency=" << m_iPeerTsbPdDelay_ms); + log << CONID() + << "HSRSP/snd: HSv5 peer uses TSBPD, responding TSBPDSND latency=" << m_iPeerTsbPdDelay_ms); } else { HLOGC(cnlog.Debug, - log << "HSRSP/snd: HSv" << (hs_version == CUDT::HS_VERSION_UDT4 ? 4 : 5) + log << CONID() << "HSRSP/snd: HSv" << (hs_version == CUDT::HS_VERSION_UDT4 ? 4 : 5) << " with peer TSBPD=" << (m_bPeerTsbPd ? "on" : "off") << " - NOT responding TSBPDSND"); } @@ -1159,24 +1183,25 @@ size_t srt::CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen { // Peer does not request to use rexmit flag, if so, // we won't use as well. - HLOGC(cnlog.Debug, log << "HSRSP/snd: AGENT understands REXMIT flag, but PEER DOES NOT. NOT setting."); + HLOGC(cnlog.Debug, + log << CONID() << "HSRSP/snd: AGENT understands REXMIT flag, but PEER DOES NOT. NOT setting."); } else { // Request that the rexmit bit be used as a part of msgno. aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG; - HLOGF(cnlog.Debug, "HSRSP/snd: AGENT UNDERSTANDS REXMIT flag and PEER reported that it does, too."); + HLOGP(cnlog.Debug, "HSRSP/snd: AGENT UNDERSTANDS REXMIT flag and PEER reported that it does, too."); } } else { // Since this is now in the code, it can occur only in case when you change the // version specification in the build configuration. - HLOGF(cnlog.Debug, "HSRSP/snd: AGENT DOES NOT UNDERSTAND REXMIT flag"); + HLOGP(cnlog.Debug, "HSRSP/snd: AGENT DOES NOT UNDERSTAND REXMIT flag"); } HLOGC(cnlog.Debug, - log << "HSRSP/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(aw_srtdata[SRT_HS_LATENCY]) + log << CONID() << "HSRSP/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(aw_srtdata[SRT_HS_LATENCY]) << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(aw_srtdata[SRT_HS_LATENCY]) << "] FLAGS[" << SrtFlagString(aw_srtdata[SRT_HS_FLAGS]) << "]"); @@ -1186,15 +1211,12 @@ size_t srt::CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen size_t srt::CUDT::prepareSrtHsMsg(int cmd, uint32_t *srtdata, size_t size) { size_t srtlen = fillSrtHandshake(srtdata, size, cmd, handshakeVersion()); - HLOGF(cnlog.Debug, - "CMD:%s(%d) Len:%d Version: %s Flags: %08X (%s) sdelay:%d", - MessageTypeStr(UMSG_EXT, cmd).c_str(), - cmd, - (int)(srtlen * sizeof(int32_t)), - SrtVersionString(srtdata[SRT_HS_VERSION]).c_str(), - srtdata[SRT_HS_FLAGS], - SrtFlagString(srtdata[SRT_HS_FLAGS]).c_str(), - srtdata[SRT_HS_LATENCY]); + HLOGC(cnlog.Debug, log << "CMD:" << MessageTypeStr(UMSG_EXT, cmd) << "(" << cmd << ") Len:" + << int(srtlen * sizeof(int32_t)) + << " Version: " << SrtVersionString(srtdata[SRT_HS_VERSION]) + << " Flags: " << srtdata[SRT_HS_FLAGS] + << " (" << SrtFlagString(srtdata[SRT_HS_FLAGS]) << ") sdelay:" + << srtdata[SRT_HS_LATENCY]); return srtlen; } @@ -1238,7 +1260,7 @@ void srt::CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, size_t srtlen_in) break; default: - LOGF(cnlog.Error, "sndSrtMsg: IPE: cmd=%d unsupported", cmd); + LOGC(cnlog.Error, log << "sndSrtMsg: IPE: cmd=" << cmd << " unsupported"); break; } @@ -1278,12 +1300,6 @@ size_t srt::CUDT::fillHsExtGroup(uint32_t* pcmdspec) SRT_GROUP_TYPE tp = m_parent->m_GroupOf->type(); uint32_t flags = 0; - // Note: if agent is a listener, and the current version supports - // both sync methods, this flag might have been changed according to - // the wish of the caller. - if (m_parent->m_GroupOf->synconmsgno()) - flags |= SRT_GFLAG_SYNCONMSG; - // NOTE: this code remains as is for historical reasons. // The initial implementation stated that the peer id be // extracted so that it can be reported and possibly the @@ -1329,8 +1345,8 @@ size_t srt::CUDT::fillHsExtKMREQ(uint32_t* pcmdspec, size_t ki) const uint32_t* keydata = reinterpret_cast(m_pCryptoControl->getKmMsg_data(ki)); HLOGC(cnlog.Debug, - log << "createSrtHandshake: KMREQ: adding key #" << ki << " length=" << ra_size - << " words (KmMsg_size=" << msglen << ")"); + log << CONID() << "createSrtHandshake: KMREQ: adding key #" << ki << " length=" << ra_size + << " words (KmMsg_size=" << msglen << ")"); // XXX INSECURE ": [" << FormatBinaryString((uint8_t*)keydata, msglen) << "]"; // Yes, I know HtoNLA and NtoHLA do exactly the same operation, but I want @@ -1353,7 +1369,9 @@ size_t srt::CUDT::fillHsExtKMRSP(uint32_t* pcmdspec, const uint32_t* kmdata, siz if (kmdata_wordsize == 0) { - LOGC(cnlog.Warn, log << "createSrtHandshake: Agent has PW, but Peer sent no KMREQ. Sending error KMRSP response"); + LOGC(cnlog.Warn, + log << CONID() + << "createSrtHandshake: Agent has PW, but Peer sent no KMREQ. Sending error KMRSP response"); ra_size = 1; keydata = failure_kmrsp; @@ -1366,7 +1384,7 @@ size_t srt::CUDT::fillHsExtKMRSP(uint32_t* pcmdspec, const uint32_t* kmdata, siz if (!kmdata) { m_RejectReason = SRT_REJ_IPE; - LOGC(cnlog.Fatal, log << "createSrtHandshake: IPE: srtkm_cmd=SRT_CMD_KMRSP and no kmdata!"); + LOGC(cnlog.Fatal, log << CONID() << "createSrtHandshake: IPE: srtkm_cmd=SRT_CMD_KMRSP and no kmdata!"); return 0; } ra_size = kmdata_wordsize; @@ -1375,9 +1393,9 @@ size_t srt::CUDT::fillHsExtKMRSP(uint32_t* pcmdspec, const uint32_t* kmdata, siz *pcmdspec = HS_CMDSPEC_CMD::wrap(SRT_CMD_KMRSP) | HS_CMDSPEC_SIZE::wrap((uint32_t) ra_size); HLOGC(cnlog.Debug, - log << "createSrtHandshake: KMRSP: applying returned key length=" - << ra_size); // XXX INSECURE << " words: [" << FormatBinaryString((uint8_t*)kmdata, - // kmdata_wordsize*sizeof(uint32_t)) << "]"; + log << CONID() << "createSrtHandshake: KMRSP: applying returned key length=" + << ra_size); // XXX INSECURE << " words: [" << FormatBinaryString((uint8_t*)kmdata, + // kmdata_wordsize*sizeof(uint32_t)) << "]"; NtoHLA((space), keydata, ra_size); return ra_size; @@ -1406,7 +1424,8 @@ bool srt::CUDT::createSrtHandshake( if (w_hs.m_extension) { // Should be impossible - LOGC(cnlog.Error, log << "createSrtHandshake: IPE: EXTENSION SET WHEN peer reports version 4 - fixing..."); + LOGC(cnlog.Error, + log << CONID() << "createSrtHandshake: IPE: EXTENSION SET WHEN peer reports version 4 - fixing..."); w_hs.m_extension = false; } } @@ -1416,9 +1435,9 @@ bool srt::CUDT::createSrtHandshake( } HLOGC(cnlog.Debug, - log << "createSrtHandshake: buf size=" << w_pkt.getLength() << " hsx=" << MessageTypeStr(UMSG_EXT, srths_cmd) - << " kmx=" << MessageTypeStr(UMSG_EXT, srtkm_cmd) << " kmdata_wordsize=" << kmdata_wordsize - << " version=" << w_hs.m_iVersion); + log << CONID() << "createSrtHandshake: buf size=" << w_pkt.getLength() + << " hsx=" << MessageTypeStr(UMSG_EXT, srths_cmd) << " kmx=" << MessageTypeStr(UMSG_EXT, srtkm_cmd) + << " kmdata_wordsize=" << kmdata_wordsize << " version=" << w_hs.m_iVersion); // Once you are certain that the version is HSv5, set the enc type flags // to advertise pbkeylen. Otherwise make sure that the old interpretation @@ -1434,7 +1453,8 @@ bool srt::CUDT::createSrtHandshake( if (w_hs.m_iReqType == URQ_CONCLUSION && srths_cmd == SRT_CMD_HSRSP && is_zero(m_tsRcvPeerStartTime)) { LOGC(cnlog.Error, - log << "createSrtHandshake: IPE (non-fatal): Attempting to craft HSRSP without received HSREQ. " + log << CONID() + << "createSrtHandshake: IPE (non-fatal): Attempting to craft HSRSP without received HSREQ. " "BLOCKING extensions."); w_hs.m_extension = false; } @@ -1450,7 +1470,7 @@ bool srt::CUDT::createSrtHandshake( IF_HEAVY_LOGGING(bool whether = m_config.iSndCryptoKeyLen != 0); HLOGC(cnlog.Debug, - log << "createSrtHandshake: " << (whether ? "" : "NOT ") + log << CONID() << "createSrtHandshake: " << (whether ? "" : "NOT ") << " Advertising PBKEYLEN - value = " << m_config.iSndCryptoKeyLen); // Note: This is required only when sending a HS message without SRT extensions. @@ -1474,7 +1494,8 @@ bool srt::CUDT::createSrtHandshake( size_t hs_size = w_pkt.getLength(); w_hs.store_to((w_pkt.m_pcData), (hs_size)); w_pkt.setLength(hs_size); - HLOGC(cnlog.Debug, log << "createSrtHandshake: (no ext) size=" << hs_size << " data: " << w_hs.show()); + HLOGC(cnlog.Debug, + log << CONID() << "createSrtHandshake: (no ext) size=" << hs_size << " data: " << w_hs.show()); return true; } @@ -1482,7 +1503,8 @@ bool srt::CUDT::createSrtHandshake( if (srths_cmd == SRT_CMD_HSREQ && m_SrtHsSide == HSD_RESPONDER) { m_RejectReason = SRT_REJ_IPE; - LOGC(cnlog.Fatal, log << "IPE: SRT_CMD_HSREQ was requested to be sent in HSv5 by an INITIATOR side!"); + LOGC(cnlog.Fatal, + log << CONID() << "IPE: SRT_CMD_HSREQ was requested to be sent in HSv5 by an INITIATOR side!"); return false; // should cause rejection } @@ -1589,7 +1611,7 @@ bool srt::CUDT::createSrtHandshake( } #endif - HLOGC(cnlog.Debug, log << "createSrtHandshake: (ext: " << logext.str() << ") data: " << w_hs.show()); + HLOGC(cnlog.Debug, log << CONID() << "createSrtHandshake: (ext: " << logext.str() << ") data: " << w_hs.show()); // NOTE: The HSREQ is practically always required, although may happen // in future that CONCLUSION can be sent multiple times for a separate @@ -1619,7 +1641,7 @@ bool srt::CUDT::createSrtHandshake( *pcmdspec = HS_CMDSPEC_CMD::wrap(srths_cmd) | HS_CMDSPEC_SIZE::wrap((uint32_t) ra_size); HLOGC(cnlog.Debug, - log << "createSrtHandshake: after HSREQ: offset=" << offset << " HSREQ size=" << ra_size + log << CONID() << "createSrtHandshake: after HSREQ: offset=" << offset << " HSREQ size=" << ra_size << " space left: " << (total_ra_size - offset)); // Use only in REQ phase and only if stream name is set @@ -1634,7 +1656,8 @@ bool srt::CUDT::createSrtHandshake( { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Warn, - log << "createSrtHandshake: stream id too long, limited to " << (size_limit - 1) << " bytes"); + log << CONID() << "createSrtHandshake: stream id too long, limited to " << (size_limit - 1) + << " bytes"); return false; } @@ -1642,7 +1665,7 @@ bool srt::CUDT::createSrtHandshake( ra_size = fillHsExtConfigString(p + offset - 1, SRT_CMD_SID, m_config.sStreamName.str()); HLOGC(cnlog.Debug, - log << "createSrtHandshake: after SID [" << m_config.sStreamName.c_str() + log << CONID() << "createSrtHandshake: after SID [" << m_config.sStreamName.c_str() << "] length=" << m_config.sStreamName.size() << " alignedln=" << (4 * ra_size) << ": offset=" << offset << " SID size=" << ra_size << " space left: " << (total_ra_size - offset)); } @@ -1657,7 +1680,7 @@ bool srt::CUDT::createSrtHandshake( ra_size = fillHsExtConfigString(p + offset - 1, SRT_CMD_CONGESTION, sm); HLOGC(cnlog.Debug, - log << "createSrtHandshake: after CONGCTL [" << sm << "] length=" << sm.size() + log << CONID() << "createSrtHandshake: after CONGCTL [" << sm << "] length=" << sm.size() << " alignedln=" << (4 * ra_size) << ": offset=" << offset << " CONGCTL size=" << ra_size << " space left: " << (total_ra_size - offset)); } @@ -1668,9 +1691,9 @@ bool srt::CUDT::createSrtHandshake( ra_size = fillHsExtConfigString(p + offset - 1, SRT_CMD_FILTER, m_config.sPacketFilterConfig.str()); HLOGC(cnlog.Debug, - log << "createSrtHandshake: after filter [" << m_config.sPacketFilterConfig.c_str() << "] length=" - << m_config.sPacketFilterConfig.size() << " alignedln=" << (4 * ra_size) << ": offset=" << offset - << " filter size=" << ra_size << " space left: " << (total_ra_size - offset)); + log << CONID() << "createSrtHandshake: after filter [" << m_config.sPacketFilterConfig.c_str() + << "] length=" << m_config.sPacketFilterConfig.size() << " alignedln=" << (4 * ra_size) << ": offset=" + << offset << " filter size=" << ra_size << " space left: " << (total_ra_size - offset)); } #if ENABLE_BONDING @@ -1693,7 +1716,7 @@ bool srt::CUDT::createSrtHandshake( // This may only happen if since last check of m_GroupOf pointer the socket was removed // from the group in the meantime, which can only happen due to that the group was closed. // In such a case it simply means that the handshake process was requested to be interrupted. - LOGC(cnlog.Fatal, log << "GROUP DISAPPEARED. Socket not capable of continuing HS"); + LOGC(cnlog.Fatal, log << CONID() << "GROUP DISAPPEARED. Socket not capable of continuing HS"); return false; } else @@ -1701,15 +1724,16 @@ bool srt::CUDT::createSrtHandshake( if (m_parent->m_GroupOf->closing()) { m_RejectReason = SRT_REJ_IPE; - LOGC(cnlog.Error, log << "createSrtHandshake: group is closing during the process, rejecting."); + LOGC(cnlog.Error, + log << CONID() << "createSrtHandshake: group is closing during the process, rejecting."); return false; - } offset += ra_size + 1; ra_size = fillHsExtGroup(p + offset - 1); - HLOGC(cnlog.Debug, log << "createSrtHandshake: after GROUP [" << sm << "] length=" << sm.size() - << ": offset=" << offset << " GROUP size=" << ra_size << " space left: " << (total_ra_size - offset)); + HLOGC(cnlog.Debug, + log << CONID() << "createSrtHandshake: after GROUP [" << sm << "] length=" << sm.size() << ": offset=" + << offset << " GROUP size=" << ra_size << " space left: " << (total_ra_size - offset)); } } #endif @@ -1718,15 +1742,16 @@ bool srt::CUDT::createSrtHandshake( if (have_kmreq) { HLOGC(cnlog.Debug, - log << "createSrtHandshake: " + log << CONID() << "createSrtHandshake: " << (m_config.CryptoSecret.len > 0 ? "Agent uses ENCRYPTION" : "Peer requires ENCRYPTION")); if (!m_pCryptoControl && (srtkm_cmd == SRT_CMD_KMREQ || srtkm_cmd == SRT_CMD_KMRSP)) { m_RejectReason = SRT_REJ_IPE; - LOGC(cnlog.Error, log << "createSrtHandshake: IPE: need to send KM, but CryptoControl does not exist." - << " Socket state: connected=" << boolalpha << m_bConnected << ", connecting=" << m_bConnecting - << ", broken=" << m_bBroken << ", closing=" << m_bClosing << "."); + LOGC(cnlog.Error, + log << CONID() << "createSrtHandshake: IPE: need to send KM, but CryptoControl does not exist." + << " Socket state: connected=" << boolalpha << m_bConnected << ", connecting=" << m_bConnecting + << ", broken=" << m_bBroken << ", closing=" << m_bClosing << "."); return false; } @@ -1750,7 +1775,7 @@ bool srt::CUDT::createSrtHandshake( if (!have_any_keys) { m_RejectReason = SRT_REJ_IPE; - LOGC(cnlog.Error, log << "createSrtHandshake: IPE: all keys have expired, no KM to send."); + LOGC(cnlog.Error, log << CONID() << "createSrtHandshake: IPE: all keys have expired, no KM to send."); return false; } } @@ -1762,7 +1787,7 @@ bool srt::CUDT::createSrtHandshake( else { m_RejectReason = SRT_REJ_IPE; - LOGC(cnlog.Fatal, log << "createSrtHandshake: IPE: wrong value of srtkm_cmd: " << srtkm_cmd); + LOGC(cnlog.Fatal, log << CONID() << "createSrtHandshake: IPE: wrong value of srtkm_cmd: " << srtkm_cmd); return false; } } @@ -1778,8 +1803,8 @@ bool srt::CUDT::createSrtHandshake( w_pkt.setLength((ra_size + offset) * sizeof(int32_t)); HLOGC(cnlog.Debug, - log << "createSrtHandshake: filled HSv5 handshake flags: " << CHandShake::ExtensionFlagStr(w_hs.m_iType) - << " length: " << w_pkt.getLength() << " bytes"); + log << CONID() << "createSrtHandshake: filled HSv5 handshake flags: " + << CHandShake::ExtensionFlagStr(w_hs.m_iType) << " length: " << w_pkt.getLength() << " bytes"); return true; } @@ -1951,7 +1976,8 @@ bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) int res = SRT_CMD_NONE; - HLOGC(cnlog.Debug, log << "Dispatching message type=" << etype << " data length=" << (len / sizeof(int32_t))); + HLOGC(cnlog.Debug, + log << CONID() << "Dispatching message type=" << etype << " data length=" << (len / sizeof(int32_t))); switch (etype) { case SRT_CMD_HSREQ: @@ -1980,17 +2006,18 @@ bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) if (m_config.bEnforcedEnc) { LOGC(cnlog.Warn, - log << "KMREQ FAILURE: " << KmStateStr(SRT_KM_STATE(srtdata_out[0])) + log << CONID() << "KMREQ FAILURE: " << KmStateStr(SRT_KM_STATE(srtdata_out[0])) << " - rejecting per enforced encryption"); res = SRT_CMD_NONE; break; } HLOGC(cnlog.Debug, - log << "MKREQ -> KMRSP FAILURE state: " << KmStateStr(SRT_KM_STATE(srtdata_out[0]))); + log << CONID() + << "MKREQ -> KMRSP FAILURE state: " << KmStateStr(SRT_KM_STATE(srtdata_out[0]))); } else { - HLOGC(cnlog.Debug, log << "KMREQ -> requested to send KMRSP length=" << len_out); + HLOGC(cnlog.Debug, log << CONID() << "KMREQ -> requested to send KMRSP length=" << len_out); } sendSrtMsg(SRT_CMD_KMRSP, srtdata_out, len_out); } @@ -1998,7 +2025,7 @@ bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) // Please review later. else { - LOGC(cnlog.Warn, log << "KMREQ failed to process the request - ignoring"); + LOGC(cnlog.Warn, log << CONID() << "KMREQ failed to process the request - ignoring"); } return true; // already done what's necessary @@ -2047,17 +2074,13 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint { m_RejectReason = SRT_REJ_ROGUE; /* Packet smaller than minimum compatible packet size */ - LOGF(cnlog.Error, "HSREQ/rcv: cmd=%d(HSREQ) len=%" PRIzu " invalid", SRT_CMD_HSREQ, bytelen); + LOGC(cnlog.Error, log << "HSREQ/rcv: cmd=" << SRT_CMD_HSREQ << "(HSREQ) len=" << bytelen << " invalid"); return SRT_CMD_NONE; } - LOGF(cnlog.Note, - "HSREQ/rcv: cmd=%d(HSREQ) len=%" PRIzu " vers=0x%x opts=0x%x delay=%d", - SRT_CMD_HSREQ, - bytelen, - srtdata[SRT_HS_VERSION], - srtdata[SRT_HS_FLAGS], - SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); + LOGC(cnlog.Note, log << "HSREQ/rcv: cmd=" << SRT_CMD_HSREQ << "(HSREQ) len=" << bytelen + << hex << " vers=0x" << srtdata[SRT_HS_VERSION] << " opts=0x" << srtdata[SRT_HS_FLAGS] + << dec << " delay=" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); m_uPeerSrtVersion = srtdata[SRT_HS_VERSION]; m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS]; @@ -2068,7 +2091,7 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, - log << "HSREQ/rcv: With HSv4 version >= " << SrtVersionString(SRT_VERSION_FEAT_HSv5) + log << CONID() << "HSREQ/rcv: With HSv4 version >= " << SrtVersionString(SRT_VERSION_FEAT_HSv5) << " is not acceptable."); return SRT_CMD_REJECT; } @@ -2079,7 +2102,8 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, - log << "HSREQ/rcv: With HSv5 version must be >= " << SrtVersionString(SRT_VERSION_FEAT_HSv5) << " ."); + log << CONID() << "HSREQ/rcv: With HSv5 version must be >= " << SrtVersionString(SRT_VERSION_FEAT_HSv5) + << " ."); return SRT_CMD_REJECT; } } @@ -2089,19 +2113,19 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint { m_RejectReason = SRT_REJ_VERSION; LOGC(cnlog.Error, - log << "HSREQ/rcv: Peer version: " << SrtVersionString(m_uPeerSrtVersion) + log << CONID() << "HSREQ/rcv: Peer version: " << SrtVersionString(m_uPeerSrtVersion) << " is too old for requested: " << SrtVersionString(m_config.uMinimumPeerSrtVersion) << " - REJECTING"); return SRT_CMD_REJECT; } HLOGC(cnlog.Debug, - log << "HSREQ/rcv: PEER Version: " << SrtVersionString(m_uPeerSrtVersion) << " Flags: " << m_uPeerSrtFlags - << "(" << SrtFlagString(m_uPeerSrtFlags) + log << CONID() << "HSREQ/rcv: PEER Version: " << SrtVersionString(m_uPeerSrtVersion) + << " Flags: " << m_uPeerSrtFlags << "(" << SrtFlagString(m_uPeerSrtFlags) << ") Min req version:" << SrtVersionString(m_config.uMinimumPeerSrtVersion)); m_bPeerRexmitFlag = IsSet(m_uPeerSrtFlags, SRT_OPT_REXMITFLG); - HLOGF(cnlog.Debug, "HSREQ/rcv: peer %s REXMIT flag", m_bPeerRexmitFlag ? "UNDERSTANDS" : "DOES NOT UNDERSTAND"); + HLOGC(cnlog.Debug, log << CONID() << "HSREQ/rcv: peer " << (m_bPeerRexmitFlag ? "UNDERSTANDS" : "DOES NOT UNDERSTAND") << " REXMIT flag"); // Check if both use the same API type. Reject if not. bool peer_message_api = !IsSet(m_uPeerSrtFlags, SRT_OPT_STREAM); @@ -2109,7 +2133,7 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint { m_RejectReason = SRT_REJ_MESSAGEAPI; LOGC(cnlog.Error, - log << "HSREQ/rcv: Agent uses " << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") + log << CONID() << "HSREQ/rcv: Agent uses " << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") << " API, but the Peer declares " << (peer_message_api ? "MESSAGE" : "STREAM") << " API. Not compatible transmission type, rejecting."); return SRT_CMD_REJECT; @@ -2135,11 +2159,13 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, - log << "HSREQ/rcv: Peer sent only VERSION + FLAGS HSREQ, but TSBPD flags are set. Rejecting."); + log << CONID() + << "HSREQ/rcv: Peer sent only VERSION + FLAGS HSREQ, but TSBPD flags are set. Rejecting."); return SRT_CMD_REJECT; } - LOGC(cnlog.Warn, log << "HSREQ/rcv: Peer sent only VERSION + FLAGS HSREQ, not getting any TSBPD settings."); + LOGC(cnlog.Warn, + log << CONID() << "HSREQ/rcv: Peer sent only VERSION + FLAGS HSREQ, not getting any TSBPD settings."); // Don't process any further settings in this case. Turn off TSBPD, just for a case. m_bTsbPd = false; m_bPeerTsbPd = false; @@ -2153,7 +2179,8 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint // TimeStamp-based Packet Delivery feature enabled if (!isOPT_TsbPd()) { - LOGC(cnlog.Warn, log << "HSREQ/rcv: Agent did not set rcv-TSBPD - ignoring proposed latency from peer"); + LOGC(cnlog.Warn, + log << CONID() << "HSREQ/rcv: Agent did not set rcv-TSBPD - ignoring proposed latency from peer"); // Note: also don't set the peer TSBPD flag HERE because // - in HSv4 it will be a sender, so it doesn't matter anyway @@ -2181,8 +2208,8 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint // "proposed" by the peer. int maxdelay = std::max(m_iTsbPdDelay_ms, peer_decl_latency); HLOGC(cnlog.Debug, - log << "HSREQ/rcv: LOCAL/RCV LATENCY: Agent:" << m_iTsbPdDelay_ms << " Peer:" << peer_decl_latency - << " Selecting:" << maxdelay); + log << CONID() << "HSREQ/rcv: LOCAL/RCV LATENCY: Agent:" << m_iTsbPdDelay_ms + << " Peer:" << peer_decl_latency << " Selecting:" << maxdelay); m_iTsbPdDelay_ms = maxdelay; m_bTsbPd = true; } @@ -2190,7 +2217,7 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint else { std::string how_about_agent = isOPT_TsbPd() ? "BUT AGENT DOES" : "and nor does Agent"; - HLOGC(cnlog.Debug, log << "HSREQ/rcv: Peer DOES NOT USE latency for sending - " << how_about_agent); + HLOGC(cnlog.Debug, log << CONID() << "HSREQ/rcv: Peer DOES NOT USE latency for sending - " << how_about_agent); } // This happens when the HSv5 RESPONDER receives the HSREQ message; it declares @@ -2209,14 +2236,15 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint int peer_decl_latency = SRT_HS_LATENCY_RCV::unwrap(latencystr); int maxdelay = std::max(m_iPeerTsbPdDelay_ms, peer_decl_latency); HLOGC(cnlog.Debug, - log << "HSREQ/rcv: PEER/RCV LATENCY: Agent:" << m_iPeerTsbPdDelay_ms << " Peer:" << peer_decl_latency - << " Selecting:" << maxdelay); + log << CONID() << "HSREQ/rcv: PEER/RCV LATENCY: Agent:" << m_iPeerTsbPdDelay_ms + << " Peer:" << peer_decl_latency << " Selecting:" << maxdelay); m_iPeerTsbPdDelay_ms = maxdelay; } else { std::string how_about_agent = isOPT_TsbPd() ? "BUT AGENT DOES" : "and nor does Agent"; - HLOGC(cnlog.Debug, log << "HSREQ/rcv: Peer DOES NOT USE latency for receiving - " << how_about_agent); + HLOGC(cnlog.Debug, + log << CONID() << "HSREQ/rcv: Peer DOES NOT USE latency for receiving - " << how_about_agent); } if (hsv > CUDT::HS_VERSION_UDT4) @@ -2244,14 +2272,14 @@ int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint // With HSv4 we accept only version less than 1.3.0 if (hsv == CUDT::HS_VERSION_UDT4 && srtdata[SRT_HS_VERSION] >= SRT_VERSION_FEAT_HSv5) { - LOGC(cnlog.Error, log << "HSRSP/rcv: With HSv4 version >= 1.2.0 is not acceptable."); + LOGC(cnlog.Error, log << CONID() << "HSRSP/rcv: With HSv4 version >= 1.2.0 is not acceptable."); return SRT_CMD_NONE; } if (bytelen < SRT_CMD_HSRSP_MINSZ) { /* Packet smaller than minimum compatible packet size */ - LOGF(cnlog.Error, "HSRSP/rcv: cmd=%d(HSRSP) len=%" PRIzu " invalid", SRT_CMD_HSRSP, bytelen); + LOGC(cnlog.Error, log << CONID() << "HSRSP/rcv: cmd=" << SRT_CMD_HSRSP << "(HSRSP) len=" << bytelen << " invalid"); return SRT_CMD_NONE; } @@ -2271,28 +2299,29 @@ int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint // if the agent has this value already "borrowed" from a master socket // that was in the group at the time when it was added. m_tsRcvPeerStartTime = steady_clock::now() - microseconds_from(ts); - HLOGC(cnlog.Debug, log << "HSRSP/rcv: PEER START TIME not yet defined, setting: " << FormatTime(m_tsRcvPeerStartTime)); + HLOGC(cnlog.Debug, + log << CONID() + << "HSRSP/rcv: PEER START TIME not yet defined, setting: " << FormatTime(m_tsRcvPeerStartTime)); } else { - HLOGC(cnlog.Debug, log << "HSRSP/rcv: PEER START TIME already set (derived): " << FormatTime(m_tsRcvPeerStartTime)); + HLOGC(cnlog.Debug, + log << CONID() + << "HSRSP/rcv: PEER START TIME already set (derived): " << FormatTime(m_tsRcvPeerStartTime)); } m_uPeerSrtVersion = srtdata[SRT_HS_VERSION]; m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS]; - HLOGF(cnlog.Debug, - "HSRSP/rcv: Version: %s Flags: SND:%08X (%s)", - SrtVersionString(m_uPeerSrtVersion).c_str(), - m_uPeerSrtFlags, - SrtFlagString(m_uPeerSrtFlags).c_str()); - + HLOGC(cnlog.Debug, log << "HSRSP/rcv: Version: " << SrtVersionString(m_uPeerSrtVersion) + << " Flags: SND:" << setw(8) << setfill('0') << hex << m_uPeerSrtFlags + << setw(0) << " (" << SrtFlagString(m_uPeerSrtFlags) << ")"); // Basic version check if (m_uPeerSrtVersion < m_config.uMinimumPeerSrtVersion) { m_RejectReason = SRT_REJ_VERSION; LOGC(cnlog.Error, - log << "HSRSP/rcv: Peer version: " << SrtVersionString(m_uPeerSrtVersion) + log << CONID() << "HSRSP/rcv: Peer version: " << SrtVersionString(m_uPeerSrtVersion) << " is too old for requested: " << SrtVersionString(m_config.uMinimumPeerSrtVersion) << " - REJECTING"); return SRT_CMD_REJECT; @@ -2307,7 +2336,7 @@ int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint m_bPeerTsbPd = true; m_iPeerTsbPdDelay_ms = SRT_HS_LATENCY_LEG::unwrap(srtdata[SRT_HS_LATENCY]); HLOGC(cnlog.Debug, - log << "HSRSP/rcv: LATENCY: Peer/snd:" << m_iPeerTsbPdDelay_ms + log << CONID() << "HSRSP/rcv: LATENCY: Peer/snd:" << m_iPeerTsbPdDelay_ms << " (Agent: declared:" << m_iTsbPdDelay_ms << " rcv:" << m_iTsbPdDelay_ms << ")"); } // TSBPDSND isn't set in HSv4 by the RESPONDER, because HSv4 RESPONDER is always RECEIVER. @@ -2322,11 +2351,11 @@ int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint // TsbPd feature enabled m_bPeerTsbPd = true; m_iPeerTsbPdDelay_ms = SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY]); - HLOGC(cnlog.Debug, log << "HSRSP/rcv: LATENCY: Peer/snd:" << m_iPeerTsbPdDelay_ms << "ms"); + HLOGC(cnlog.Debug, log << CONID() << "HSRSP/rcv: LATENCY: Peer/snd:" << m_iPeerTsbPdDelay_ms << "ms"); } else { - HLOGC(cnlog.Debug, log << "HSRSP/rcv: Peer (responder) DOES NOT USE latency"); + HLOGC(cnlog.Debug, log << CONID() << "HSRSP/rcv: Peer (responder) DOES NOT USE latency"); } // PEER WILL SEND TSBPD == AGENT SHALL RECEIVE TSBPD. @@ -2335,7 +2364,8 @@ int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint if (!isOPT_TsbPd()) { LOGC(cnlog.Warn, - log << "HSRSP/rcv: BUG? Peer (responder) declares sending latency, but Agent turned off TSBPD."); + log << CONID() + << "HSRSP/rcv: BUG? Peer (responder) declares sending latency, but Agent turned off TSBPD."); } else { @@ -2343,7 +2373,7 @@ int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint // Take this value as a good deal. In case when the Peer did not "correct" the latency // because it has TSBPD turned off, just stay with the present value defined in options. m_iTsbPdDelay_ms = SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY]); - HLOGC(cnlog.Debug, log << "HSRSP/rcv: LATENCY Agent/rcv: " << m_iTsbPdDelay_ms << "ms"); + HLOGC(cnlog.Debug, log << CONID() << "HSRSP/rcv: LATENCY Agent/rcv: " << m_iTsbPdDelay_ms << "ms"); } } } @@ -2375,7 +2405,7 @@ int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint } else { - HLOGF(cnlog.Debug, "HSRSP/rcv: <1.2.0 Agent DOESN'T understand REXMIT flag"); + HLOGP(cnlog.Debug, "HSRSP/rcv: <1.2.0 Agent DOESN'T understand REXMIT flag"); } handshakeDone(); @@ -2398,7 +2428,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, if (m_ConnRes.m_iVersion > HS_VERSION_UDT4 && hs.m_iVersion == 0) { m_RejectReason = SRT_REJ_PEER; - LOGC(cnlog.Error, log << "HS VERSION = 0, meaning the handshake has been rejected."); + LOGC(cnlog.Error, log << CONID() << "HS VERSION = 0, meaning the handshake has been rejected."); return false; } @@ -2421,7 +2451,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, m_RejectReason = SRT_REJ_ROGUE; // This would mean that the handshake was at least HSv5, but somehow no extras were added. // Dismiss it then, however this has to be logged. - LOGC(cnlog.Error, log << "HS VERSION=" << hs.m_iVersion << " but no handshake extension found!"); + LOGC(cnlog.Error, log << CONID() << "HS VERSION=" << hs.m_iVersion << " but no handshake extension found!"); return false; } @@ -2430,12 +2460,14 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, if (ext_flags == 0) { m_RejectReason = SRT_REJ_ROGUE; - LOGC(cnlog.Error, log << "HS VERSION=" << hs.m_iVersion << " but no handshake extension flags are set!"); + LOGC(cnlog.Error, + log << CONID() << "HS VERSION=" << hs.m_iVersion << " but no handshake extension flags are set!"); return false; } HLOGC(cnlog.Debug, - log << "HS VERSION=" << hs.m_iVersion << " EXTENSIONS: " << CHandShake::ExtensionFlagStr(ext_flags)); + log << CONID() << "HS VERSION=" << hs.m_iVersion + << " EXTENSIONS: " << CHandShake::ExtensionFlagStr(ext_flags)); // Ok, now find the beginning of an int32_t array that follows the UDT handshake. uint32_t* p = reinterpret_cast(hspkt.m_pcData + CHandShake::m_iContentSize); @@ -2445,7 +2477,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, if (IsSet(ext_flags, CHandShake::HS_EXT_HSREQ)) { - HLOGC(cnlog.Debug, log << "interpretSrtHandshake: extracting HSREQ/RSP type extension"); + HLOGC(cnlog.Debug, log << CONID() << "interpretSrtHandshake: extracting HSREQ/RSP type extension"); uint32_t *begin = p; uint32_t *next = 0; size_t length = size / sizeof(uint32_t); @@ -2466,8 +2498,8 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, - log << "HS-ext HSREQ found but invalid size: " << bytelen << " (expected: " << SRT_HS_E_SIZE - << ")"); + log << CONID() << "HS-ext HSREQ found but invalid size: " << bytelen + << " (expected: " << SRT_HS_E_SIZE << ")"); return false; // don't interpret } @@ -2477,7 +2509,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, { // m_RejectReason already set LOGC(cnlog.Error, - log << "interpretSrtHandshake: process HSREQ returned unexpected value " << rescmd); + log << CONID() << "interpretSrtHandshake: process HSREQ returned unexpected value " << rescmd); return false; } handshakeDone(); @@ -2492,8 +2524,8 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, - log << "HS-ext HSRSP found but invalid size: " << bytelen << " (expected: " << SRT_HS_E_SIZE - << ")"); + log << CONID() << "HS-ext HSRSP found but invalid size: " << bytelen + << " (expected: " << SRT_HS_E_SIZE << ")"); return false; // don't interpret } @@ -2508,7 +2540,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, if (m_RejectReason == SRT_REJ_UNKNOWN) m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, - log << "interpretSrtHandshake: process HSRSP returned unexpected value " << rescmd); + log << CONID() << "interpretSrtHandshake: process HSRSP returned unexpected value " << rescmd); return false; } handshakeDone(); @@ -2517,7 +2549,8 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, else if (cmd == SRT_CMD_NONE) { m_RejectReason = SRT_REJ_ROGUE; - LOGC(cnlog.Warn, log << "interpretSrtHandshake: no HSREQ/HSRSP block found in the handshake msg!"); + LOGC(cnlog.Warn, + log << CONID() << "interpretSrtHandshake: no HSREQ/HSRSP block found in the handshake msg!"); // This means that there can be no more processing done by FindExtensionBlock(). // And we haven't found what we need - otherwise one of the above cases would pass // and lead to exit this loop immediately. @@ -2536,7 +2569,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, } } - HLOGC(cnlog.Debug, log << "interpretSrtHandshake: HSREQ done, checking KMREQ"); + HLOGC(cnlog.Debug, log << CONID() << "interpretSrtHandshake: HSREQ done, checking KMREQ"); // Now check the encrypted @@ -2544,7 +2577,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, if (IsSet(ext_flags, CHandShake::HS_EXT_KMREQ)) { - HLOGC(cnlog.Debug, log << "interpretSrtHandshake: extracting KMREQ/RSP type extension"); + HLOGC(cnlog.Debug, log << CONID() << "interpretSrtHandshake: extracting KMREQ/RSP type extension"); #ifdef SRT_ENABLE_ENCRYPTION if (!m_pCryptoControl->hasPassphrase()) @@ -2553,12 +2586,15 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, { m_RejectReason = SRT_REJ_UNSECURE; LOGC(cnlog.Error, - log << "HS KMREQ: Peer declares encryption, but agent does not - rejecting per enforced encryption"); + log << CONID() + << "HS KMREQ: Peer declares encryption, but agent does not - rejecting per enforced " + "encryption"); return false; } LOGC(cnlog.Warn, - log << "HS KMREQ: Peer declares encryption, but agent does not - still allowing connection."); + log << CONID() + << "HS KMREQ: Peer declares encryption, but agent does not - still allowing connection."); // Still allow for connection, and allow Agent to send unencrypted stream to the peer. // Also normally allow the key to be processed; worst case it will send the failure response. @@ -2574,7 +2610,8 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, int cmd = FindExtensionBlock(begin, length, (blocklen), (next)); HLOGC(cnlog.Debug, - log << "interpretSrtHandshake: found extension: (" << cmd << ") " << MessageTypeStr(UMSG_EXT, cmd)); + log << CONID() << "interpretSrtHandshake: found extension: (" << cmd << ") " + << MessageTypeStr(UMSG_EXT, cmd)); size_t bytelen = blocklen * sizeof(uint32_t); if (cmd == SRT_CMD_KMREQ) @@ -2582,7 +2619,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, if (!out_data || !pw_len) { m_RejectReason = SRT_REJ_IPE; - LOGC(cnlog.Fatal, log << "IPE: HS/KMREQ extracted without passing target buffer!"); + LOGC(cnlog.Fatal, log << CONID() << "IPE: HS/KMREQ extracted without passing target buffer!"); return false; } @@ -2593,11 +2630,24 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, m_RejectReason = SRT_REJ_IPE; // Something went wrong. HLOGC(cnlog.Debug, - log << "interpretSrtHandshake: IPE/EPE KMREQ processing failed - returned " << res); + log << CONID() << "interpretSrtHandshake: IPE/EPE KMREQ processing failed - returned " + << res); return false; } if (*pw_len == 1) { +#ifdef ENABLE_AEAD_API_PREVIEW + if (m_pCryptoControl->m_RcvKmState == SRT_KM_S_BADCRYPTOMODE) + { + // Cryptographic modes mismatch. Not acceptable at all. + m_RejectReason = SRT_REJ_CRYPTO; + LOGC(cnlog.Error, + log << CONID() + << "interpretSrtHandshake: KMREQ result: Bad crypto mode - rejecting"); + return false; + } +#endif + // This means that there was an abnormal encryption situation occurred. // This is inacceptable in case of strict encryption. if (m_config.bEnforcedEnc) @@ -2611,7 +2661,8 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, m_RejectReason = SRT_REJ_UNSECURE; } LOGC(cnlog.Error, - log << "interpretSrtHandshake: KMREQ result abnornal - rejecting per enforced encryption"); + log << CONID() + << "interpretSrtHandshake: KMREQ result abnornal - rejecting per enforced encryption"); return false; } } @@ -2622,8 +2673,16 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, int res = m_pCryptoControl->processSrtMsg_KMRSP(begin + 1, bytelen, HS_VERSION_SRT1); if (m_config.bEnforcedEnc && res == -1) { - m_RejectReason = SRT_REJ_UNSECURE; - LOGC(cnlog.Error, log << "KMRSP failed - rejecting connection as per enforced encryption."); + if (m_pCryptoControl->m_SndKmState == SRT_KM_S_BADSECRET) + m_RejectReason = SRT_REJ_BADSECRET; +#ifdef ENABLE_AEAD_API_PREVIEW + else if (m_pCryptoControl->m_SndKmState == SRT_KM_S_BADCRYPTOMODE) + m_RejectReason = SRT_REJ_CRYPTO; +#endif + else + m_RejectReason = SRT_REJ_UNSECURE; + LOGC(cnlog.Error, + log << CONID() << "KMRSP failed - rejecting connection as per enforced encryption."); return false; } encrypted = true; @@ -2631,12 +2690,12 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, else if (cmd == SRT_CMD_NONE) { m_RejectReason = SRT_REJ_ROGUE; - LOGC(cnlog.Error, log << "HS KMREQ expected - none found!"); + LOGC(cnlog.Error, log << CONID() << "HS KMREQ expected - none found!"); return false; } else { - HLOGC(cnlog.Debug, log << "interpretSrtHandshake: ... skipping " << MessageTypeStr(UMSG_EXT, cmd)); + HLOGC(cnlog.Debug, log << CONID() << "interpretSrtHandshake: ... skipping " << MessageTypeStr(UMSG_EXT, cmd)); if (NextExtensionBlock((begin), next, (length))) continue; } @@ -2651,13 +2710,15 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, { m_RejectReason = SRT_REJ_UNSECURE; LOGC(cnlog.Error, - log << "HS KMREQ: Peer declares encryption, but agent didn't enable it at compile time - rejecting " + log << CONID() + << "HS KMREQ: Peer declares encryption, but agent didn't enable it at compile time - rejecting " "per enforced encryption"); return false; } LOGC(cnlog.Warn, - log << "HS KMREQ: Peer declares encryption, but agent didn't enable it at compile time - still allowing " + log << CONID() + << "HS KMREQ: Peer declares encryption, but agent didn't enable it at compile time - still allowing " "connection."); encrypted = true; #endif @@ -2676,7 +2737,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, if (IsSet(ext_flags, CHandShake::HS_EXT_CONFIG)) { - HLOGC(cnlog.Debug, log << "interpretSrtHandshake: extracting various CONFIG extensions"); + HLOGC(cnlog.Debug, log << CONID() << "interpretSrtHandshake: extracting various CONFIG extensions"); uint32_t *begin = p; uint32_t *next = 0; @@ -2688,7 +2749,8 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, int cmd = FindExtensionBlock(begin, length, (blocklen), (next)); HLOGC(cnlog.Debug, - log << "interpretSrtHandshake: found extension: (" << cmd << ") " << MessageTypeStr(UMSG_EXT, cmd)); + log << CONID() << "interpretSrtHandshake: found extension: (" << cmd << ") " + << MessageTypeStr(UMSG_EXT, cmd)); const size_t bytelen = blocklen * sizeof(uint32_t); if (cmd == SRT_CMD_SID) @@ -2696,15 +2758,15 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, if (!bytelen || bytelen > CSrtConfig::MAX_SID_LENGTH) { LOGC(cnlog.Error, - log << "interpretSrtHandshake: STREAMID length " << bytelen << " is 0 or > " << +CSrtConfig::MAX_SID_LENGTH - << " - PROTOCOL ERROR, REJECTING"); + log << CONID() << "interpretSrtHandshake: STREAMID length " << bytelen << " is 0 or > " + << +CSrtConfig::MAX_SID_LENGTH << " - PROTOCOL ERROR, REJECTING"); return false; } // Copied through a cleared array. This is because the length is aligned to 4 // where the padding is filled by zero bytes. For the case when the string is // exactly of a 4-divisible length, we make a big array with maximum allowed size // filled with zeros. Copying to this array should then copy either only the valid - // characters of the string (if the lenght is divisible by 4), or the string with + // characters of the string (if the length is divisible by 4), or the string with // padding zeros. In all these cases in the resulting array we should have all // subsequent characters of the string plus at least one '\0' at the end. This will // make it a perfect NUL-terminated string, to be used to initialize a string. @@ -2717,23 +2779,23 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, m_config.sStreamName.set(target, strlen(target)); HLOGC(cnlog.Debug, - log << "CONNECTOR'S REQUESTED SID [" << m_config.sStreamName.c_str() << "] (bytelen=" << bytelen - << " blocklen=" << blocklen << ")"); + log << CONID() << "CONNECTOR'S REQUESTED SID [" << m_config.sStreamName.c_str() + << "] (bytelen=" << bytelen << " blocklen=" << blocklen << ")"); } else if (cmd == SRT_CMD_CONGESTION) { if (have_congctl) { m_RejectReason = SRT_REJ_ROGUE; - LOGC(cnlog.Error, log << "CONGCTL BLOCK REPEATED!"); + LOGC(cnlog.Error, log << CONID() << "CONGCTL BLOCK REPEATED!"); return false; } if (!bytelen || bytelen > CSrtConfig::MAX_CONG_LENGTH) { LOGC(cnlog.Error, - log << "interpretSrtHandshake: CONGESTION-control type length " << bytelen << " is 0 or > " - << +CSrtConfig::MAX_CONG_LENGTH << " - PROTOCOL ERROR, REJECTING"); + log << CONID() << "interpretSrtHandshake: CONGESTION-control type length " << bytelen + << " is 0 or > " << +CSrtConfig::MAX_CONG_LENGTH << " - PROTOCOL ERROR, REJECTING"); return false; } // Declare that congctl has been received @@ -2754,28 +2816,29 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, { m_RejectReason = SRT_REJ_CONGESTION; LOGC(cnlog.Error, - log << "PEER'S CONGCTL '" << sm << "' does not match AGENT'S CONGCTL '" << agsm << "'"); + log << CONID() << "PEER'S CONGCTL '" << sm << "' does not match AGENT'S CONGCTL '" << agsm + << "'"); return false; } HLOGC(cnlog.Debug, - log << "CONNECTOR'S CONGCTL [" << sm << "] (bytelen=" << bytelen << " blocklen=" << blocklen - << ")"); + log << CONID() << "CONNECTOR'S CONGCTL [" << sm << "] (bytelen=" << bytelen + << " blocklen=" << blocklen << ")"); } else if (cmd == SRT_CMD_FILTER) { if (have_filter) { m_RejectReason = SRT_REJ_FILTER; - LOGC(cnlog.Error, log << "FILTER BLOCK REPEATED!"); + LOGC(cnlog.Error, log << CONID() << "FILTER BLOCK REPEATED!"); return false; } if (!bytelen || bytelen > CSrtConfig::MAX_PFILTER_LENGTH) { LOGC(cnlog.Error, - log << "interpretSrtHandshake: packet-filter type length " << bytelen << " is 0 or > " - << +CSrtConfig::MAX_PFILTER_LENGTH << " - PROTOCOL ERROR, REJECTING"); + log << CONID() << "interpretSrtHandshake: packet-filter type length " << bytelen + << " is 0 or > " << +CSrtConfig::MAX_PFILTER_LENGTH << " - PROTOCOL ERROR, REJECTING"); return false; } // Declare that filter has been received @@ -2784,16 +2847,19 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, char target[CSrtConfig::MAX_PFILTER_LENGTH + 1]; memset((target), 0, CSrtConfig::MAX_PFILTER_LENGTH + 1); memcpy((target), begin + 1, bytelen); + // Un-swap on big endian machines + ItoHLA((uint32_t *)target, (uint32_t *)target, blocklen); + string fltcfg = target; HLOGC(cnlog.Debug, - log << "PEER'S FILTER CONFIG [" << fltcfg << "] (bytelen=" << bytelen << " blocklen=" << blocklen - << ")"); + log << CONID() << "PEER'S FILTER CONFIG [" << fltcfg << "] (bytelen=" << bytelen + << " blocklen=" << blocklen << ")"); if (!checkApplyFilterConfig(fltcfg)) { m_RejectReason = SRT_REJ_FILTER; - LOGC(cnlog.Error, log << "PEER'S FILTER CONFIG [" << fltcfg << "] has been rejected"); + LOGC(cnlog.Error, log << CONID() << "PEER'S FILTER CONFIG [" << fltcfg << "] has been rejected"); return false; } } @@ -2810,7 +2876,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, if (bytelen < GRPD_MIN_SIZE * GRPD_FIELD_SIZE || bytelen % GRPD_FIELD_SIZE) { m_RejectReason = SRT_REJ_ROGUE; - LOGC(cnlog.Error, log << "PEER'S GROUP wrong size: " << (bytelen/GRPD_FIELD_SIZE)); + LOGC(cnlog.Error, log << CONID() << "PEER'S GROUP wrong size: " << (bytelen/GRPD_FIELD_SIZE)); return false; } size_t groupdata_size = bytelen / GRPD_FIELD_SIZE; @@ -2823,7 +2889,9 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, } have_group = true; - HLOGC(cnlog.Debug, log << "CONNECTOR'S PEER GROUP [" << groupdata[0] << "] (bytelen=" << bytelen << " blocklen=" << blocklen << ")"); + HLOGC(cnlog.Debug, + log << CONID() << "CONNECTOR'S PEER GROUP [" << groupdata[0] << "] (bytelen=" << bytelen + << " blocklen=" << blocklen << ")"); } #endif else if (cmd == SRT_CMD_NONE) @@ -2833,7 +2901,8 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, else { // Found some block that is not interesting here. Skip this and get the next one. - HLOGC(cnlog.Debug, log << "interpretSrtHandshake: ... skipping " << MessageTypeStr(UMSG_EXT, cmd)); + HLOGC(cnlog.Debug, + log << CONID() << "interpretSrtHandshake: ... skipping " << MessageTypeStr(UMSG_EXT, cmd)); } if (!NextExtensionBlock((begin), next, (length))) @@ -2849,13 +2918,15 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, { m_RejectReason = SRT_REJ_UNSECURE; LOGC(cnlog.Error, - log << "HS EXT: Agent declares encryption, but Peer does not - rejecting connection per " + log << CONID() + << "HS EXT: Agent declares encryption, but Peer does not - rejecting connection per " "enforced encryption."); return false; } LOGC(cnlog.Warn, - log << "HS EXT: Agent declares encryption, but Peer does not (Agent can still receive unencrypted packets " + log << CONID() + << "HS EXT: Agent declares encryption, but Peer does not (Agent can still receive unencrypted packets " "from Peer)."); // This is required so that the sender is still allowed to send data, when encryption is required, @@ -2871,7 +2942,8 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, { m_RejectReason = SRT_REJ_CONGESTION; LOGC(cnlog.Error, - log << "HS EXT: Agent uses '" << agsm << "' congctl, but peer DID NOT DECLARE congctl (assuming 'live')."); + log << CONID() << "HS EXT: Agent uses '" << agsm + << "' congctl, but peer DID NOT DECLARE congctl (assuming 'live')."); return false; } @@ -2887,7 +2959,9 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, if (!have_group) { m_RejectReason = SRT_REJ_GROUP; - LOGC(cnlog.Error, log << "HS EXT: agent is a group member, but the listener did not respond with group ID. Rejecting."); + LOGC(cnlog.Error, + log << CONID() + << "HS EXT: agent is a group member, but the listener did not respond with group ID. Rejecting."); return false; } } @@ -2945,7 +3019,7 @@ bool srt::CUDT::checkApplyFilterConfig(const std::string &confstr) } HLOGC(cnlog.Debug, - log << "checkApplyFilterConfig: param: LOCAL: " << Printable(mycfg.parameters) + log << CONID() << "checkApplyFilterConfig: param: LOCAL: " << Printable(mycfg.parameters) << " FORGN: " << Printable(cfg.parameters)); ostringstream myos; @@ -2957,12 +3031,12 @@ bool srt::CUDT::checkApplyFilterConfig(const std::string &confstr) m_config.sPacketFilterConfig.set(myos.str()); - HLOGC(cnlog.Debug, log << "checkApplyFilterConfig: Effective config: " << thisconf); + HLOGC(cnlog.Debug, log << CONID() << "checkApplyFilterConfig: Effective config: " << thisconf); } else { // Take the foreign configuration as a good deal. - HLOGC(cnlog.Debug, log << "checkApplyFilterConfig: Good deal config: " << thisconf); + HLOGC(cnlog.Debug, log << CONID() << "checkApplyFilterConfig: Good deal config: " << thisconf); m_config.sPacketFilterConfig.set(confstr); } @@ -2970,7 +3044,7 @@ bool srt::CUDT::checkApplyFilterConfig(const std::string &confstr) if (m_config.zExpPayloadSize > efc_max_payload_size) { LOGC(cnlog.Warn, - log << "Due to filter-required extra " << cfg.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to " + log << CONID() << "Due to filter-required extra " << cfg.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to " << efc_max_payload_size << " bytes"); m_config.zExpPayloadSize = efc_max_payload_size; } @@ -2997,7 +3071,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A if (m_config.iGroupConnect == 0) { m_RejectReason = SRT_REJ_GROUP; - LOGC(cnlog.Error, log << "HS/GROUP: this socket is not allowed for group connect."); + LOGC(cnlog.Error, log << CONID() << "HS/GROUP: this socket is not allowed for group connect."); return false; } @@ -3005,14 +3079,15 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A if (gtp >= SRT_GTYPE_E_END) { m_RejectReason = SRT_REJ_GROUP; - LOGC(cnlog.Error, log << "HS/GROUP: incorrect group type value " << gtp << " (max is " << SRT_GTYPE_E_END << ")"); + LOGC(cnlog.Error, + log << CONID() << "HS/GROUP: incorrect group type value " << gtp << " (max is " << SRT_GTYPE_E_END << ")"); return false; } if ((grpid & SRTGROUP_MASK) == 0) { m_RejectReason = SRT_REJ_ROGUE; - LOGC(cnlog.Error, log << "HS/GROUP: socket ID passed as a group ID is not a group ID"); + LOGC(cnlog.Error, log << CONID() << "HS/GROUP: socket ID passed as a group ID is not a group ID"); return false; } @@ -3023,8 +3098,10 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A #if ENABLE_HEAVY_LOGGING static const char* hs_side_name[] = {"draw", "initiator", "responder"}; - HLOGC(cnlog.Debug, log << "interpretGroup: STATE: HsSide=" << hs_side_name[m_SrtHsSide] << " HS MSG: " << MessageTypeStr(UMSG_EXT, hsreq_type_cmd) - << " $" << grpid << " type=" << gtp << " weight=" << link_weight << " flags=0x" << std::hex << link_flags); + HLOGC(cnlog.Debug, + log << CONID() << "interpretGroup: STATE: HsSide=" << hs_side_name[m_SrtHsSide] + << " HS MSG: " << MessageTypeStr(UMSG_EXT, hsreq_type_cmd) << " $" << grpid << " type=" << gtp + << " weight=" << link_weight << " flags=0x" << std::hex << link_flags); #endif // XXX Here are two separate possibilities: @@ -3036,7 +3113,10 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A if (m_SrtHsSide == HSD_DRAW) { m_RejectReason = SRT_REJ_IPE; - LOGC(cnlog.Error, log << "IPE: interpretGroup: The HS side should have been already decided; it's still DRAW. Grouping rejected."); + LOGC(cnlog.Error, + log << CONID() + << "IPE: interpretGroup: The HS side should have been already decided; it's still DRAW. Grouping " + "rejected."); return false; } @@ -3058,7 +3138,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A // but the initiator did not request any group membership presence. // Currently impossible situation. m_RejectReason = SRT_REJ_IPE; - LOGC(cnlog.Error, log << "IPE: HS/RSP: group membership responded, while not requested."); + LOGC(cnlog.Error, log << CONID() << "IPE: HS/RSP: group membership responded, while not requested."); return false; } @@ -3068,7 +3148,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A // Now we know the group exists, but it might still be closed if (pg->closing()) { - LOGC(cnlog.Error, log << "HS/RSP: group was closed in the process, can't continue connecting"); + LOGC(cnlog.Error, log << CONID() << "HS/RSP: group was closed in the process, can't continue connecting"); m_RejectReason = SRT_REJ_IPE; return false; } @@ -3079,7 +3159,9 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A // This is the first connection within this group, so this group // has just been informed about the peer membership. Accept it. pg->set_peerid(grpid); - HLOGC(cnlog.Debug, log << "HS/RSP: group $" << pg->id() << " -> peer $" << pg->peerid() << ", copying characteristic data"); + HLOGC(cnlog.Debug, + log << CONID() << "HS/RSP: group $" << pg->id() << " -> peer $" << pg->peerid() + << ", copying characteristic data"); // The call to syncWithSocket is copying // some interesting data from the first connected @@ -3093,14 +3175,17 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A // different peers). else if (peer != grpid) { - LOGC(cnlog.Error, log << "IPE: HS/RSP: group membership responded for peer $" << grpid - << " but the current socket's group $" << pg->id() << " has already a peer $" << peer); + LOGC(cnlog.Error, + log << CONID() << "IPE: HS/RSP: group membership responded for peer $" << grpid + << " but the current socket's group $" << pg->id() << " has already a peer $" << peer); m_RejectReason = SRT_REJ_GROUP; return false; } else { - HLOGC(cnlog.Debug, log << "HS/RSP: group $" << pg->id() << " ALREADY MAPPED to peer mirror $" << pg->peerid()); + HLOGC(cnlog.Debug, + log << CONID() << "HS/RSP: group $" << pg->id() << " ALREADY MAPPED to peer mirror $" + << pg->peerid()); } } else @@ -3126,7 +3211,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A { // Strange, we just added it... m_RejectReason = SRT_REJ_IPE; - LOGC(cnlog.Fatal, log << "IPE: socket not in group after adding to it"); + LOGC(cnlog.Fatal, log << CONID() << "IPE: socket not in group after adding to it"); return false; } @@ -3168,12 +3253,13 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 { if (gp->type() != gtp) { - LOGC(gmlog.Error, log << "HS: GROUP TYPE COLLISION: peer group=$" << peergroup << " type " << gtp - << " agent group=$" << gp->id() << " type" << gp->type()); + LOGC(gmlog.Error, + log << CONID() << "HS: GROUP TYPE COLLISION: peer group=$" << peergroup << " type " << gtp + << " agent group=$" << gp->id() << " type" << gp->type()); return -1; } - HLOGC(gmlog.Debug, log << "makeMePeerOf: group for peer=$" << peergroup << " found: $" << gp->id()); + HLOGC(gmlog.Debug, log << CONID() << "makeMePeerOf: group for peer=$" << peergroup << " found: $" << gp->id()); if (!gp->groupEmpty()) was_empty = false; @@ -3205,10 +3291,11 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 // Therefore such a group shall always be considered opened. gp->setOpen(); - HLOGC(gmlog.Debug, log << "makeMePeerOf: no group has peer=$" << peergroup << " - creating new mirror group $" << gp->id()); + HLOGC(gmlog.Debug, + log << CONID() << "makeMePeerOf: no group has peer=$" << peergroup << " - creating new mirror group $" + << gp->id()); } - { ScopedLock glock (*gp->exp_groupLock()); if (gp->closing()) @@ -3234,8 +3321,8 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 { // XXX This is internal error. Report it, but continue // (A newly created socket from acceptAndRespond should not have any group membership yet) - LOGC(gmlog.Error, log << "IPE (non-fatal): the socket is in the group, but has no clue about it!"); - s->m_GroupOf = gp; + LOGC(gmlog.Error, log << CONID() << "IPE (non-fatal): the socket is in the group, but has no clue about it!"); + s->m_GroupOf = gp; s->m_GroupMemberData = f; return 0; } @@ -3263,22 +3350,19 @@ void srt::CUDT::synchronizeWithGroup(CUDTGroup* gp) if (!gp->applyGroupTime((start_time), (peer_start_time))) { - HLOGC(gmlog.Debug, log << "synchronizeWithGroup: @" << m_SocketID - << " DERIVED: ST=" - << FormatTime(m_stats.tsStartTime) << " -> " - << FormatTime(start_time) << " PST=" - << FormatTime(m_tsRcvPeerStartTime) << " -> " - << FormatTime(peer_start_time)); - m_stats.tsStartTime = start_time; + HLOGC(gmlog.Debug, + log << CONID() << "synchronizeWithGroup: ST=" << FormatTime(m_stats.tsStartTime) << " -> " + << FormatTime(start_time) << " PST=" << FormatTime(m_tsRcvPeerStartTime) << " -> " + << FormatTime(peer_start_time)); + m_stats.tsStartTime = start_time; m_tsRcvPeerStartTime = peer_start_time; } else { // This was the first connected socket and it defined start time. - HLOGC(gmlog.Debug, log << "synchronizeWithGroup: @" << m_SocketID - << " DEFINED: ST=" - << FormatTime(m_stats.tsStartTime) - << " PST=" << FormatTime(m_tsRcvPeerStartTime)); + HLOGC(gmlog.Debug, + log << CONID() << "synchronizeWithGroup: ST=" << FormatTime(m_stats.tsStartTime) + << " PST=" << FormatTime(m_tsRcvPeerStartTime)); } steady_clock::time_point rcv_buffer_time_base; @@ -3302,20 +3386,19 @@ void srt::CUDT::synchronizeWithGroup(CUDTGroup* gp) // time to not fill a network window. enterCS(m_RecvLock); m_pRcvBuffer->applyGroupTime(rcv_buffer_time_base, rcv_buffer_wrap_period, m_iTsbPdDelay_ms * 1000, rcv_buffer_udrift); -#if ENABLE_NEW_RCVBUFFER m_pRcvBuffer->setPeerRexmitFlag(m_bPeerRexmitFlag); -#endif leaveCS(m_RecvLock); - HLOGF(gmlog.Debug, "AFTER HS: Set Rcv TsbPd mode: delay=%u.%03us GROUP TIME BASE: %s%s", - m_iTsbPdDelay_ms/1000, - m_iTsbPdDelay_ms%1000, - FormatTime(rcv_buffer_time_base).c_str(), - rcv_buffer_wrap_period ? " (WRAP PERIOD)" : " (NOT WRAP PERIOD)"); + HLOGC(gmlog.Debug, log << "AFTER HS: Set Rcv TsbPd mode: delay=" + << (m_iTsbPdDelay_ms/1000) << "." << (m_iTsbPdDelay_ms%1000) + << "s GROUP TIME BASE: " << FormatTime(rcv_buffer_time_base) + << " (" << (rcv_buffer_wrap_period ? "" : "NOT") << " WRAP PERIOD)"); } else { - HLOGC(gmlog.Debug, log << "AFTER HS: (GROUP, but " << (m_bTsbPd ? "FIRST SOCKET is initialized normally)" : "no TSBPD set)")); + HLOGC(gmlog.Debug, + log << CONID() << "AFTER HS: (GROUP, but " + << (m_bTsbPd ? "FIRST SOCKET is initialized normally)" : "no TSBPD set)")); updateSrtRcvSettings(); } @@ -3323,30 +3406,22 @@ void srt::CUDT::synchronizeWithGroup(CUDTGroup* gp) // with updateAfterSrtHandshake(). updateSrtSndSettings(); - if (gp->synconmsgno()) + // These are the values that are normally set initially by setters. + int32_t snd_isn = m_iSndLastAck, rcv_isn = m_iRcvLastAck; + if (!gp->applyGroupSequences(m_SocketID, (snd_isn), (rcv_isn))) { - HLOGC(gmlog.Debug, log << "synchronizeWithGroup: @" << m_SocketID << ": NOT synchronizing sequence numbers."); + HLOGC(gmlog.Debug, + log << CONID() << "synchronizeWithGroup: DERIVED ISN: RCV=%" << m_iRcvLastAck << " -> %" << rcv_isn + << " (shift by " << CSeqNo::seqcmp(rcv_isn, m_iRcvLastAck) << ") SND=%" << m_iSndLastAck + << " -> %" << snd_isn << " (shift by " << CSeqNo::seqcmp(snd_isn, m_iSndLastAck) << ")"); + setInitialRcvSeq(rcv_isn); + setInitialSndSeq(snd_isn); } else { - // These are the values that are normally set initially by setters. - int32_t snd_isn = m_iSndLastAck, rcv_isn = m_iRcvLastAck; - if (!gp->applyGroupSequences(m_SocketID, (snd_isn), (rcv_isn))) - { - HLOGC(gmlog.Debug, log << "synchronizeWithGroup: @" << m_SocketID - << " DERIVED ISN: RCV=%" << m_iRcvLastAck << " -> %" << rcv_isn - << " (shift by " << CSeqNo::seqcmp(rcv_isn, m_iRcvLastAck) - << ") SND=%" << m_iSndLastAck << " -> %" << snd_isn - << " (shift by " << CSeqNo::seqcmp(snd_isn, m_iSndLastAck) << ")"); - setInitialRcvSeq(rcv_isn); - setInitialSndSeq(snd_isn); - } - else - { - HLOGC(gmlog.Debug, log << "synchronizeWithGroup: @" << m_SocketID - << " DEFINED ISN: RCV=%" << m_iRcvLastAck - << " SND=%" << m_iSndLastAck); - } + HLOGC(gmlog.Debug, + log << CONID() << "synchronizeWithGroup: DEFINED ISN: RCV=%" << m_iRcvLastAck << " SND=%" + << m_iSndLastAck); } } #endif @@ -3388,6 +3463,10 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // handle handshake extension flags. m_ConnReq.m_iType = UDT_DGRAM; + // Auto mode for Caller and in Rendezvous is equivalent to CIPHER_MODE_AES_CTR. + if (m_config.iCryptoMode == CSrtConfig::CIPHER_MODE_AUTO) + m_config.iCryptoMode = CSrtConfig::CIPHER_MODE_AES_CTR; + // This is my current configuration if (m_config.bRendezvous) { @@ -3407,9 +3486,9 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // This will be also passed to a HSv4 rendezvous, but fortunately the old // SRT didn't read this field from URQ_WAVEAHAND message, only URQ_CONCLUSION. m_ConnReq.m_iType = SrtHSRequest::wrapFlags(false /* no MAGIC here */, m_config.iSndCryptoKeyLen); - bool whether SRT_ATR_UNUSED = m_config.iSndCryptoKeyLen != 0; + IF_HEAVY_LOGGING(const bool whether = m_config.iSndCryptoKeyLen != 0); HLOGC(aclog.Debug, - log << "startConnect (rnd): " << (whether ? "" : "NOT ") + log << CONID() << "startConnect (rnd): " << (whether ? "" : "NOT ") << " Advertising PBKEYLEN - value = " << m_config.iSndCryptoKeyLen); m_RdvState = CHandShake::RDV_WAVING; m_SrtHsSide = HSD_DRAW; // initially not resolved. @@ -3441,17 +3520,16 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) if (forced_isn == SRT_SEQNO_NONE) { forced_isn = generateISN(); - HLOGC(aclog.Debug, log << "startConnect: ISN generated = " << forced_isn); + HLOGC(aclog.Debug, log << CONID() << "startConnect: ISN generated = " << forced_isn); } else { - HLOGC(aclog.Debug, log << "startConnect: ISN forced = " << forced_isn); + HLOGC(aclog.Debug, log << CONID() << "startConnect: ISN forced = " << forced_isn); } m_iISN = m_ConnReq.m_iISN = forced_isn; setInitialSndSeq(m_iISN); - m_SndLastAck2Time = steady_clock::now(); // Inform the server my configurations. CPacket reqpkt; @@ -3479,20 +3557,26 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // necessarily is to be the size of the data. reqpkt.setLength(hs_size); - steady_clock::time_point now = steady_clock::now(); - setPacketTS(reqpkt, now); + const steady_clock::time_point tnow = steady_clock::now(); + m_SndLastAck2Time = tnow; + setPacketTS(reqpkt, tnow); HLOGC(cnlog.Debug, - log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.m_iTimeStamp << "). SENDING HS: " << m_ConnReq.show()); + log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.m_iTimeStamp + << "). SENDING HS: " << m_ConnReq.show()); /* * Race condition if non-block connect response thread scheduled before we set m_bConnecting to true? * Connect response will be ignored and connecting will wait until timeout. * Maybe m_ConnectionLock handling problem? Not used in CUDT::connect(const CPacket& response) */ - m_tsLastReqTime = now; + m_tsLastReqTime = tnow; m_bConnecting = true; - m_pSndQueue->sendto(serv_addr, reqpkt); + + // At this point m_SourceAddr is probably default-any, but this function + // now requires that the address be specified here because there will be + // no possibility to do it at any next stage of sending. + m_pSndQueue->sendto(serv_addr, reqpkt, m_SourceAddr); // /// @@ -3526,10 +3610,14 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) CUDTException e; EConnectStatus cst = CONN_CONTINUE; + // This is a temporary place to store the DESTINATION IP from the incoming packet. + // We can't record this address yet until the cookie-confirmation is done, for safety reasons. + sockaddr_any use_source_adr(serv_addr.family()); while (!m_bClosing) { - const steady_clock::duration tdiff = steady_clock::now() - m_tsLastReqTime.load(); + const steady_clock::time_point local_tnow = steady_clock::now(); + const steady_clock::duration tdiff = local_tnow - m_tsLastReqTime.load(); // avoid sending too many requests, at most 1 request per 250ms // SHORT VERSION: @@ -3542,12 +3630,12 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) if (count_milliseconds(tdiff) > 250) { HLOGC(cnlog.Debug, - log << "startConnect: LOOP: time to send (" << count_milliseconds(tdiff) << " > 250 ms). size=" << reqpkt.getLength()); + log << CONID() << "startConnect: LOOP: time to send (" << count_milliseconds(tdiff) + << " > 250 ms). size=" << reqpkt.getLength()); if (m_config.bRendezvous) reqpkt.m_iID = m_ConnRes.m_iID; - now = steady_clock::now(); #if ENABLE_HEAVY_LOGGING { CHandShake debughs; @@ -3558,19 +3646,23 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) } #endif - m_tsLastReqTime = now; - setPacketTS(reqpkt, now); - m_pSndQueue->sendto(serv_addr, reqpkt); + m_tsLastReqTime = local_tnow; + setPacketTS(reqpkt, local_tnow); + m_pSndQueue->sendto(serv_addr, reqpkt, use_source_adr); } else { - HLOGC(cnlog.Debug, log << "startConnect: LOOP: too early to send - " << count_milliseconds(tdiff) << " < 250ms"); + HLOGC(cnlog.Debug, + log << CONID() << "startConnect: LOOP: too early to send - " << count_milliseconds(tdiff) + << " < 250ms"); } cst = CONN_CONTINUE; response.setLength(m_iMaxSRTPayloadSize); if (m_pRcvQueue->recvfrom(m_SocketID, (response)) > 0) { + use_source_adr = response.udpDestAddr(); + HLOGC(cnlog.Debug, log << CONID() << "startConnect: got response for connect request"); cst = processConnectResponse(response, &e); @@ -3608,11 +3700,29 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) cst = processRendezvous(&response, serv_addr, RST_OK, (reqpkt)); if (cst == CONN_CONTINUE) continue; - break; + + HLOGC(cnlog.Debug, + log << CONID() << "startConnect: processRendezvous returned cst=" << ConnectStatusStr(cst)); + + if (cst == CONN_REJECT) + { + // Just in case it wasn't set, set this as a fallback + if (m_RejectReason == SRT_REJ_UNKNOWN) + m_RejectReason = SRT_REJ_ROGUE; + + // rejection or erroneous code. + reqpkt.setLength(m_iMaxSRTPayloadSize); + reqpkt.setControl(UMSG_HANDSHAKE); + sendRendezvousRejection(serv_addr, (reqpkt)); + } } if (cst == CONN_REJECT) + { + HLOGC(cnlog.Debug, + log << CONID() << "startConnect: REJECTED by processConnectResponse - sending SHUTDOWN"); sendCtrl(UMSG_SHUTDOWN); + } if (cst != CONN_CONTINUE && cst != CONN_CONFUSED) break; // --> OUTSIDE-LOOP @@ -3620,9 +3730,10 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // IMPORTANT // [[using assert(m_pCryptoControl != nullptr)]]; - // new request/response should be sent out immediately on receving a response + // new request/response should be sent out immediately on receiving a response HLOGC(cnlog.Debug, - log << "startConnect: SYNC CONNECTION STATUS:" << ConnectStatusStr(cst) << ", REQ-TIME: LOW."); + log << CONID() << "startConnect: SYNC CONNECTION STATUS:" << ConnectStatusStr(cst) + << ", REQ-TIME: LOW."); m_tsLastReqTime = steady_clock::time_point(); // Now serialize the handshake again to the existing buffer so that it's @@ -3635,7 +3746,8 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // small to store the CONCLUSION handshake (with HSv5 extensions). reqpkt.setLength(m_iMaxSRTPayloadSize); - HLOGC(cnlog.Debug, log << "startConnect: creating HS CONCLUSION: buffer size=" << reqpkt.getLength()); + HLOGC(cnlog.Debug, + log << CONID() << "startConnect: creating HS CONCLUSION: buffer size=" << reqpkt.getLength()); // NOTE: BUGFIX: SERIALIZE AGAIN. // The original UDT code didn't do it, so it was theoretically @@ -3653,7 +3765,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // are sent only when there is a rendezvous mode or non-blocking mode. if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (reqpkt), (m_ConnReq))) { - LOGC(cnlog.Warn, log << "createSrtHandshake failed - REJECTING."); + LOGC(cnlog.Warn, log << CONID() << "createSrtHandshake failed - REJECTING."); cst = CONN_REJECT; break; } @@ -3665,14 +3777,15 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) } HLOGC(cnlog.Debug, - log << "startConnect: timeout from Q:recvfrom, looping again; cst=" << ConnectStatusStr(cst)); + log << CONID() << "startConnect: timeout from Q:recvfrom, looping again; cst=" << ConnectStatusStr(cst)); #if ENABLE_HEAVY_LOGGING // Non-fatal assertion if (cst == CONN_REJECT) // Might be returned by processRendezvous { LOGC(cnlog.Error, - log << "startConnect: IPE: cst=REJECT NOT EXPECTED HERE, the loop should've been interrupted!"); + log << CONID() + << "startConnect: IPE: cst=REJECT NOT EXPECTED HERE, the loop should've been interrupted!"); break; } #endif @@ -3682,7 +3795,8 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // timeout e = CUDTException(MJ_SETUP, MN_TIMEOUT, 0); m_RejectReason = SRT_REJ_TIMEOUT; - HLOGC(cnlog.Debug, log << "startConnect: TTL time " << FormatTime(ttl_time) << " exceeded, TIMEOUT."); + HLOGC(cnlog.Debug, + log << CONID() << "startConnect: TTL time " << FormatTime(ttl_time) << " exceeded, TIMEOUT."); break; } } @@ -3719,14 +3833,13 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) } HLOGC(cnlog.Debug, - log << CONID() << "startConnect: handshake exchange succeeded."); + log << CONID() << "startConnect: handshake exchange succeeded. sourceIP=" << m_SourceAddr.str()); // Parameters at the end. HLOGC(cnlog.Debug, - log << "startConnect: END. Parameters:" - " mss=" - << m_config.iMSS << " max-cwnd-size=" << m_CongCtl->cgWindowMaxSize() - << " cwnd-size=" << m_CongCtl->cgWindowSize() << " rtt=" << m_iSRTT << " bw=" << m_iBandwidth); + log << CONID() << "startConnect: END. Parameters: mss=" << m_config.iMSS + << " max-cwnd-size=" << m_CongCtl->cgWindowMaxSize() << " cwnd-size=" << m_CongCtl->cgWindowSize() + << " rtt=" << m_iSRTT << " bw=" << m_iBandwidth); } // Asynchronous connection @@ -3766,7 +3879,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, setPacketTS(request, now); HLOGC(cnlog.Debug, - log << "processAsyncConnectRequest: REQ-TIME: HIGH. Should prevent too quick responses."); + log << CONID() << "processAsyncConnectRequest: REQ-TIME: HIGH. Should prevent too quick responses."); m_tsLastReqTime = now; // ID = 0, connection request request.m_iID = !m_config.bRendezvous ? 0 : m_ConnRes.m_iID; @@ -3779,12 +3892,13 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, if (cst == CONN_RENDEZVOUS) { - HLOGC(cnlog.Debug, log << "processAsyncConnectRequest: passing to processRendezvous"); + HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: passing to processRendezvous"); cst = processRendezvous(pResponse, serv_addr, rst, (request)); if (cst == CONN_ACCEPT) { HLOGC(cnlog.Debug, - log << "processAsyncConnectRequest: processRendezvous completed the process and responded by itself. " + log << CONID() + << "processAsyncConnectRequest: processRendezvous completed the process and responded by itself. " "Done."); return true; } @@ -3793,7 +3907,13 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, { // processRendezvous already set the reject reason LOGC(cnlog.Warn, - log << "processAsyncConnectRequest: REJECT reported from processRendezvous, not processing further."); + log << CONID() + << "processAsyncConnectRequest: REJECT reported from processRendezvous, not processing further."); + + if (m_RejectReason == SRT_REJ_UNKNOWN) + m_RejectReason = SRT_REJ_ROGUE; + + sendRendezvousRejection(serv_addr, (request)); status = false; } } @@ -3801,26 +3921,28 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, { // m_RejectReason already set at worker_ProcessAddressedPacket. LOGC(cnlog.Warn, - log << "processAsyncConnectRequest: REJECT reported from HS processing: " - << srt_rejectreason_str(m_RejectReason) - << " - not processing further"); + log << CONID() << "processAsyncConnectRequest: REJECT reported from HS processing: " + << srt_rejectreason_str(m_RejectReason) << " - not processing further"); // m_tsLastReqTime = steady_clock::time_point(); XXX ? return false; } else { // (this procedure will be also run for HSv4 rendezvous) - HLOGC(cnlog.Debug, log << "processAsyncConnectRequest: serializing HS: buffer size=" << request.getLength()); + HLOGC(cnlog.Debug, + log << CONID() << "processAsyncConnectRequest: serializing HS: buffer size=" << request.getLength()); if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (request), (m_ConnReq))) { // All 'false' returns from here are IPE-type, mostly "invalid argument" plus "all keys expired". - LOGC(cnlog.Error, log << "IPE: processAsyncConnectRequest: createSrtHandshake failed, dismissing."); + LOGC(cnlog.Error, + log << CONID() << "IPE: processAsyncConnectRequest: createSrtHandshake failed, dismissing."); status = false; } else { HLOGC(cnlog.Debug, - log << "processAsyncConnectRequest: sending HS reqtype=" << RequestTypeStr(m_ConnReq.m_iReqType) + log << CONID() + << "processAsyncConnectRequest: sending HS reqtype=" << RequestTypeStr(m_ConnReq.m_iReqType) << " to socket " << request.m_iID << " size=" << request.getLength()); } } @@ -3837,18 +3959,38 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, */ } - HLOGC(cnlog.Debug, log << "processAsyncConnectRequest: setting REQ-TIME HIGH, SENDING HS:" << m_ConnReq.show()); + HLOGC(cnlog.Debug, + log << CONID() << "processAsyncConnectRequest: setting REQ-TIME HIGH, SENDING HS:" << m_ConnReq.show()); m_tsLastReqTime = steady_clock::now(); - m_pSndQueue->sendto(serv_addr, request); + m_pSndQueue->sendto(serv_addr, request, m_SourceAddr); return status; } +void srt::CUDT::sendRendezvousRejection(const sockaddr_any& serv_addr, CPacket& r_rsppkt) +{ + // We can reuse m_ConnReq because we are about to abandon the connection process. + m_ConnReq.m_iReqType = URQFailure(m_RejectReason); + + // Assumed that r_rsppkt refers to a packet object that was already prepared + // to be used for storing the handshake there. + size_t size = r_rsppkt.getLength(); + m_ConnReq.store_to((r_rsppkt.m_pcData), (size)); + r_rsppkt.setLength(size); + + HLOGC(cnlog.Debug, log << CONID() << "sendRendezvousRejection: using code=" << m_ConnReq.m_iReqType + << " for reject reason code " << m_RejectReason << " (" << srt_rejectreason_str(m_RejectReason) << ")"); + + setPacketTS(r_rsppkt, steady_clock::now()); + m_pSndQueue->sendto(serv_addr, r_rsppkt, m_SourceAddr); +} + void srt::CUDT::cookieContest() { if (m_SrtHsSide != HSD_DRAW) return; - LOGC(cnlog.Debug, log << "cookieContest: agent=" << m_ConnReq.m_iCookie << " peer=" << m_ConnRes.m_iCookie); + LOGC(cnlog.Debug, + log << CONID() << "cookieContest: agent=" << m_ConnReq.m_iCookie << " peer=" << m_ConnRes.m_iCookie); // Here m_ConnReq.m_iCookie is a local cookie value sent in connection request to the peer. // m_ConnRes.m_iCookie is a cookie value sent by the peer in its connection request. @@ -3919,9 +4061,10 @@ EConnectStatus srt::CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatas if (!m_pCryptoControl) { m_RejectReason = SRT_REJ_IPE; - LOGC(cnlog.Error, log << "IPE: craftKmResponse needs to send KM, but CryptoControl does not exist." - << " Socket state: connected=" << boolalpha << m_bConnected << ", connecting=" << m_bConnecting - << ", broken=" << m_bBroken << ", opened " << m_bOpened << ", closing=" << m_bClosing << "."); + LOGC(cnlog.Error, + log << CONID() << "IPE: craftKmResponse needs to send KM, but CryptoControl does not exist." + << " Socket state: connected=" << boolalpha << m_bConnected << ", connecting=" << m_bConnecting + << ", broken=" << m_bBroken << ", opened " << m_bOpened << ", closing=" << m_bClosing << "."); return CONN_REJECT; } // This is a periodic handshake update, so you need to extract the KM data from the @@ -3937,8 +4080,8 @@ EConnectStatus srt::CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatas case SRT_KM_S_BADSECRET: { HLOGC(cnlog.Debug, - log << "craftKmResponse: No KMX recorded, status = " - << KmStateStr(m_pCryptoControl->m_RcvKmState) << ". Respond it."); + log << CONID() << "craftKmResponse: No KMX recorded, status = " + << KmStateStr(m_pCryptoControl->m_RcvKmState) << ". Respond it."); // Just do the same thing as in CCryptoControl::processSrtMsg_KMREQ for that case, // that is, copy the NOSECRET code into KMX message. @@ -3957,9 +4100,9 @@ EConnectStatus srt::CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatas // Remaining situations: // - password only on this site: shouldn't be considered to be sent to a no-password site LOGC(cnlog.Error, - log << "craftKmResponse: IPE: PERIODIC HS: NO KMREQ RECORDED KMSTATE: RCV=" - << KmStateStr(m_pCryptoControl->m_RcvKmState) - << " SND=" << KmStateStr(m_pCryptoControl->m_SndKmState)); + log << CONID() << "craftKmResponse: IPE: PERIODIC HS: NO KMREQ RECORDED KMSTATE: RCV=" + << KmStateStr(m_pCryptoControl->m_RcvKmState) + << " SND=" << KmStateStr(m_pCryptoControl->m_SndKmState)); return CONN_REJECT; } break; @@ -3971,20 +4114,20 @@ EConnectStatus srt::CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatas if (msgsize > w_kmdatasize * 4) { // Sanity check - LOGC(cnlog.Error, log << "IPE: KMX data not aligned to 4 bytes! size=" << msgsize); + LOGC(cnlog.Error, log << CONID() << "IPE: KMX data not aligned to 4 bytes! size=" << msgsize); memset((aw_kmdata + (w_kmdatasize * 4)), 0, msgsize - (w_kmdatasize * 4)); ++w_kmdatasize; } HLOGC(cnlog.Debug, - log << "craftKmResponse: getting KM DATA from the fore-recorded KMX from KMREQ, size=" - << w_kmdatasize); + log << CONID() << "craftKmResponse: getting KM DATA from the fore-recorded KMX from KMREQ, size=" + << w_kmdatasize); memcpy((aw_kmdata), m_pCryptoControl->getKmMsg_data(0), msgsize); } } else { - HLOGC(cnlog.Debug, log << "craftKmResponse: no KMX flag - not extracting KM data for KMRSP"); + HLOGC(cnlog.Debug, log << CONID() << "craftKmResponse: no KMX flag - not extracting KM data for KMRSP"); w_kmdatasize = 0; } @@ -3997,7 +4140,7 @@ EConnectStatus srt::CUDT::processRendezvous( { if (m_RdvState == CHandShake::RDV_CONNECTED) { - HLOGC(cnlog.Debug, log << "processRendezvous: already in CONNECTED state."); + HLOGC(cnlog.Debug, log << CONID() << "processRendezvous: already in CONNECTED state."); return CONN_ACCEPT; } @@ -4013,7 +4156,7 @@ EConnectStatus srt::CUDT::processRendezvous( { m_RejectReason = SRT_REJ_RDVCOOKIE; LOGC(cnlog.Error, - log << "COOKIE CONTEST UNRESOLVED: can't assign connection roles, please wait another minute."); + log << CONID() << "COOKIE CONTEST UNRESOLVED: can't assign connection roles, please wait another minute."); return CONN_REJECT; } @@ -4031,7 +4174,8 @@ EConnectStatus srt::CUDT::processRendezvous( { m_RejectReason = RejectReasonForURQ(rsp_type); HLOGC(cnlog.Debug, - log << "processRendezvous: rejecting due to switch-state response: " << RequestTypeStr(rsp_type)); + log << CONID() + << "processRendezvous: rejecting due to switch-state response: " << RequestTypeStr(rsp_type)); return CONN_REJECT; } checkUpdateCryptoKeyLen("processRendezvous", m_ConnRes.m_iType); @@ -4045,18 +4189,19 @@ EConnectStatus srt::CUDT::processRendezvous( m_ConnReq.m_extension = needs_extension; // This must be done before prepareConnectionObjects(), because it sets ISN and m_iMaxSRTPayloadSize needed to create buffers. - if (!applyResponseSettings()) + if (!applyResponseSettings(pResponse)) { - LOGC(cnlog.Error, log << "processRendezvous: rogue peer"); + LOGC(cnlog.Error, log << CONID() << "processRendezvous: rogue peer"); return CONN_REJECT; } // The CryptoControl must be created by the prepareConnectionObjects() before interpreting and creating HSv5 extensions // because the it will be used there. - if (!prepareConnectionObjects(m_ConnRes, m_SrtHsSide, NULL)) + if (!prepareConnectionObjects(m_ConnRes, m_SrtHsSide, NULL) || !prepareBuffers(NULL)) { // m_RejectReason already handled - HLOGC(cnlog.Debug, log << "processRendezvous: rejecting due to problems in prepareConnectionObjects."); + HLOGC(cnlog.Debug, + log << CONID() << "processRendezvous: rejecting due to problems in prepareConnectionObjects."); return CONN_REJECT; } @@ -4074,21 +4219,21 @@ EConnectStatus srt::CUDT::processRendezvous( { m_RejectReason = SRT_REJ_IPE; LOGC(cnlog.Fatal, - log << "IPE: rst=RST_OK, but the packet has set -1 length - REJECTING (REQ-TIME: LOW)"); + log << CONID() << "IPE: rst=RST_OK, but the packet has set -1 length - REJECTING (REQ-TIME: LOW)"); return CONN_REJECT; } if (!interpretSrtHandshake(m_ConnRes, *pResponse, kmdata, &kmdatasize)) { HLOGC(cnlog.Debug, - log << "processRendezvous: rejecting due to problems in interpretSrtHandshake REQ-TIME: LOW."); + log << CONID() << "processRendezvous: rejecting due to problems in interpretSrtHandshake REQ-TIME: LOW."); return CONN_REJECT; } updateAfterSrtHandshake(HS_VERSION_SRT1); // Pass on, inform about the shortened response-waiting period. - HLOGC(cnlog.Debug, log << "processRendezvous: setting REQ-TIME: LOW. Forced to respond immediately."); + HLOGC(cnlog.Debug, log << CONID() << "processRendezvous: setting REQ-TIME: LOW. Forced to respond immediately."); } else { @@ -4104,7 +4249,8 @@ EConnectStatus srt::CUDT::processRendezvous( m_ConnReq.m_extension = true; HLOGC(cnlog.Debug, - log << "processRendezvous: HSREQ extension ok, creating HSRSP response. kmdatasize=" << kmdatasize); + log << CONID() + << "processRendezvous: HSREQ extension ok, creating HSRSP response. kmdatasize=" << kmdatasize); w_reqpkt.setLength(m_iMaxSRTPayloadSize); if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, @@ -4112,7 +4258,8 @@ EConnectStatus srt::CUDT::processRendezvous( (w_reqpkt), (m_ConnReq))) { HLOGC(cnlog.Debug, - log << "processRendezvous: rejecting due to problems in createSrtHandshake. REQ-TIME: LOW"); + log << CONID() + << "processRendezvous: rejecting due to problems in createSrtHandshake. REQ-TIME: LOW"); m_tsLastReqTime = steady_clock::time_point(); return CONN_REJECT; } @@ -4136,22 +4283,25 @@ EConnectStatus srt::CUDT::processRendezvous( // Actually the -1 length would be an IPE, but it's likely that this was reported already. HLOGC( cnlog.Debug, - log << "processRendezvous: no INCOMING packet, NOT interpreting extensions (relying on exising data)"); + log << CONID() + << "processRendezvous: no INCOMING packet, NOT interpreting extensions (relying on exising data)"); } else { HLOGC(cnlog.Debug, - log << "processRendezvous: INITIATOR, will send AGREEMENT - interpreting HSRSP extension"); + log << CONID() << "processRendezvous: INITIATOR, will send AGREEMENT - interpreting HSRSP extension"); if (!interpretSrtHandshake(m_ConnRes, *pResponse, 0, 0)) { // m_RejectReason is already set, so set the reqtype accordingly m_ConnReq.m_iReqType = URQFailure(m_RejectReason); + return CONN_REJECT; } } // This should be false, make a kinda assert here. if (needs_extension) { - LOGC(cnlog.Fatal, log << "IPE: INITIATOR responding AGREEMENT should declare no extensions to HS"); + LOGC(cnlog.Fatal, + log << CONID() << "IPE: INITIATOR responding AGREEMENT should declare no extensions to HS"); m_ConnReq.m_extension = false; } updateAfterSrtHandshake(HS_VERSION_SRT1); @@ -4164,13 +4314,13 @@ EConnectStatus srt::CUDT::processRendezvous( if (rsp_type == URQ_DONE) { - HLOGC(cnlog.Debug, log << "... WON'T SEND any response, both sides considered connected"); + HLOGC(cnlog.Debug, log << CONID() << "... WON'T SEND any response, both sides considered connected"); } else { HLOGC(cnlog.Debug, - log << "... WILL SEND " << RequestTypeStr(rsp_type) << " " << (m_ConnReq.m_extension ? "with" : "without") - << " SRT HS extensions"); + log << CONID() << "... WILL SEND " << RequestTypeStr(rsp_type) << " " + << (m_ConnReq.m_extension ? "with" : "without") << " SRT HS extensions"); } // This marks the information for the serializer that @@ -4186,7 +4336,7 @@ EConnectStatus srt::CUDT::processRendezvous( if (cst == CONN_REJECT) { // m_RejectReason already set - HLOGC(cnlog.Debug, log << "processRendezvous: rejecting due to problems in postConnect."); + HLOGC(cnlog.Debug, log << CONID() << "processRendezvous: rejecting due to problems in postConnect."); return CONN_REJECT; } } @@ -4197,7 +4347,7 @@ EConnectStatus srt::CUDT::processRendezvous( // this time with URQ_AGREEMENT message, but still consider yourself connected. if (rsp_type == URQ_DONE) { - HLOGC(cnlog.Debug, log << "processRendezvous: rsp=DONE, reporting ACCEPT (nothing to respond)"); + HLOGC(cnlog.Debug, log << CONID() << "processRendezvous: rsp=DONE, reporting ACCEPT (nothing to respond)"); return CONN_ACCEPT; } @@ -4211,7 +4361,7 @@ EConnectStatus srt::CUDT::processRendezvous( (w_reqpkt), (m_ConnReq))) { // m_RejectReason already set - LOGC(cnlog.Warn, log << "createSrtHandshake failed (IPE?), connection rejected. REQ-TIME: LOW"); + LOGC(cnlog.Warn, log << CONID() << "createSrtHandshake failed (IPE?), connection rejected. REQ-TIME: LOW"); m_tsLastReqTime = steady_clock::time_point(); return CONN_REJECT; } @@ -4236,9 +4386,10 @@ EConnectStatus srt::CUDT::processRendezvous( m_tsLastReqTime = now; setPacketTS(w_reqpkt, now); HLOGC(cnlog.Debug, - log << "processRendezvous: rsp=AGREEMENT, reporting ACCEPT and sending just this one, REQ-TIME HIGH."); + log << CONID() + << "processRendezvous: rsp=AGREEMENT, reporting ACCEPT and sending just this one, REQ-TIME HIGH."); - m_pSndQueue->sendto(serv_addr, w_reqpkt); + m_pSndQueue->sendto(serv_addr, w_reqpkt, m_SourceAddr); return CONN_ACCEPT; } @@ -4253,7 +4404,8 @@ EConnectStatus srt::CUDT::processRendezvous( } else { - HLOGC(cnlog.Debug, log << "processRendezvous: REQ-TIME: remains previous value, consider yourself connected"); + HLOGC(cnlog.Debug, + log << CONID() << "processRendezvous: REQ-TIME: remains previous value, consider yourself connected"); } return CONN_CONTINUE; } @@ -4275,7 +4427,7 @@ EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTEx // This is required in HSv5 rendezvous, in which it should send the URQ_AGREEMENT message to // the peer, however switch to connected state. HLOGC(cnlog.Debug, - log << "processConnectResponse: TYPE:" + log << CONID() << "processConnectResponse: TYPE:" << (response.isControl() ? MessageTypeStr(response.getType(), response.getExtendedType()) : string("DATA"))); // ConnectStatus res = CONN_REJECT; // used later for status - must be declared here due to goto POST_CONNECT. @@ -4335,10 +4487,33 @@ EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTEx << "processConnectResponse: CONFUSED: expected UMSG_HANDSHAKE as connection not yet established, " "got: " << MessageTypeStr(response.getType(), response.getExtendedType())); + + if (response.getType() == UMSG_SHUTDOWN) + { + LOGC(cnlog.Error, + log << CONID() << "processConnectResponse: UMSG_SHUTDOWN received, rejecting connection."); + return CONN_REJECT; + } + } + + if (m_config.bRendezvous) + { + // In rendezvous mode we expect that both sides are known + // to the service operator (unlike a listener, which may + // operate connections from unknown sources). This means that + // the connection process should be terminated anyway, on + // whichever side it would happen. + return CONN_REJECT; } + return CONN_CONFUSED; } + if (m_config.bRendezvous) + { + m_SourceAddr = response.udpDestAddr(); + } + if (m_ConnRes.load_from(response.m_pcData, response.getLength()) == -1) { m_RejectReason = SRT_REJ_ROGUE; @@ -4350,9 +4525,12 @@ EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTEx } HLOGC(cnlog.Debug, log << CONID() << "processConnectResponse: HS RECEIVED: " << m_ConnRes.show()); - if (m_ConnRes.m_iReqType > URQ_FAILURE_TYPES) + if (m_ConnRes.m_iReqType >= URQ_FAILURE_TYPES) { m_RejectReason = RejectReasonForURQ(m_ConnRes.m_iReqType); + LOGC(cnlog.Warn, + log << CONID() << "processConnectResponse: rejecting per reception of a rejection HS response: " + << RequestTypeStr(m_ConnRes.m_iReqType)); return CONN_REJECT; } @@ -4361,7 +4539,7 @@ EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTEx // Yes, we do abort to prevent buffer overrun. Set your MSS correctly // and you'll avoid problems. m_RejectReason = SRT_REJ_ROGUE; - LOGC(cnlog.Fatal, log << "MSS size " << m_config.iMSS << "exceeds MTU size!"); + LOGC(cnlog.Fatal, log << CONID() << "MSS size " << m_config.iMSS << "exceeds MTU size!"); return CONN_REJECT; } @@ -4444,11 +4622,14 @@ EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTEx // it means that it is HSv5 capable. It can still accept the HSv4 handshake. if (m_ConnRes.m_iVersion > HS_VERSION_UDT4) { - int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); + const int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); if (hs_flags != SrtHSRequest::SRT_MAGIC_CODE) { - LOGC(cnlog.Warn, log << "processConnectResponse: Listener HSv5 did not set the SRT_MAGIC_CODE"); + LOGC(cnlog.Warn, + log << CONID() << "processConnectResponse: Listener HSv5 did not set the SRT_MAGIC_CODE."); + m_RejectReason = SRT_REJ_ROGUE; + return CONN_REJECT; } checkUpdateCryptoKeyLen("processConnectResponse", m_ConnRes.m_iType); @@ -4458,7 +4639,7 @@ EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTEx m_ConnReq.m_iVersion = HS_VERSION_SRT1; // CONTROVERSIAL: use 0 as m_iType according to the meaning in HSv5. // The HSv4 client might not understand it, which means that agent - // must switch itself to HSv4 rendezvous, and this time iType sould + // must switch itself to HSv4 rendezvous, and this time iType should // be set to UDT_DGRAM value. m_ConnReq.m_iType = 0; @@ -4491,11 +4672,11 @@ EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTEx return postConnect(&response, false, eout); } -bool srt::CUDT::applyResponseSettings() ATR_NOEXCEPT +bool srt::CUDT::applyResponseSettings(const CPacket* pHspkt /*[[nullable]]*/) ATR_NOEXCEPT { if (!m_ConnRes.valid()) { - LOGC(cnlog.Error, log << "applyResponseSettings: ROGUE HANDSHAKE - rejecting"); + LOGC(cnlog.Error, log << CONID() << "applyResponseSettings: ROGUE HANDSHAKE - rejecting"); m_RejectReason = SRT_REJ_ROGUE; return false; } @@ -4512,12 +4693,15 @@ bool srt::CUDT::applyResponseSettings() ATR_NOEXCEPT m_iRcvCurrPhySeqNo = CSeqNo::decseq(m_ConnRes.m_iISN); m_PeerID = m_ConnRes.m_iID; memcpy((m_piSelfIP), m_ConnRes.m_piPeerIP, sizeof m_piSelfIP); + if (pHspkt) + m_SourceAddr = pHspkt->udpDestAddr(); HLOGC(cnlog.Debug, log << CONID() << "applyResponseSettings: HANSHAKE CONCLUDED. SETTING: payload-size=" << m_iMaxSRTPayloadSize - << " mss=" << m_ConnRes.m_iMSS << " flw=" << m_ConnRes.m_iFlightFlagSize << " isn=" << m_ConnRes.m_iISN - << " peerID=" << m_ConnRes.m_iID); - + << " mss=" << m_ConnRes.m_iMSS << " flw=" << m_ConnRes.m_iFlightFlagSize << " peer-ISN=" << m_ConnRes.m_iISN + << " local-ISN=" << m_iISN + << " peerID=" << m_ConnRes.m_iID + << " sourceIP=" << m_SourceAddr.str()); return true; } @@ -4530,6 +4714,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, // in rendezvous it's completed before calling this function. if (!rendezvous) { + HLOGC(cnlog.Debug, log << CONID() << boolalpha << "postConnect: packet:" << bool(pResponse) << " rendezvous:" << rendezvous); // The "local storage depleted" case shouldn't happen here, but // this is a theoretical path that needs prevention. bool ok = pResponse; @@ -4556,7 +4741,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, // // Currently just this function must be called always BEFORE prepareConnectionObjects // everywhere except acceptAndRespond(). - ok = applyResponseSettings(); + ok = applyResponseSettings(pResponse); // This will actually be done also in rendezvous HSv4, // however in this case the HSREQ extension will not be attached, @@ -4569,6 +4754,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, // In this situation the interpretation of handshake was already done earlier. ok = ok && pResponse->isControl(); ok = ok && interpretSrtHandshake(m_ConnRes, *pResponse, 0, 0); + ok = ok && prepareBuffers(eout); if (!ok) { @@ -4651,7 +4837,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, // XXX Problem around CONN_CONFUSED! // If some too-eager packets were received from a listener // that thinks it's connected, but his last handshake was missed, - // they are collected by CRcvQueue::storePkt. The removeConnector + // they are collected by CRcvQueue::storePktClone. The removeConnector // function will want to delete them all, so it would be nice // if these packets can be re-delivered. Of course the listener // should be prepared to resend them (as every packet can be lost @@ -4673,7 +4859,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, // Ok, no more things to be done as per "clear connecting state" if (!s) { - LOGC(cnlog.Error, log << "Connection broken in the process - socket @" << m_SocketID << " closed"); + LOGC(cnlog.Error, log << CONID() << "Connection broken in the process - socket closed"); m_RejectReason = SRT_REJ_CLOSE; if (eout) { @@ -4758,8 +4944,9 @@ void srt::CUDT::checkUpdateCryptoKeyLen(const char *loghdr SRT_ATR_UNUSED, int32 if (m_config.iSndCryptoKeyLen == 0) { m_config.iSndCryptoKeyLen = rcv_pbkeylen; - HLOGC(cnlog.Debug, log << loghdr << ": PBKEYLEN adopted from advertised value: " - << m_config.iSndCryptoKeyLen); + HLOGC(cnlog.Debug, + log << CONID() << loghdr + << ": PBKEYLEN adopted from advertised value: " << m_config.iSndCryptoKeyLen); } else if (m_config.iSndCryptoKeyLen != rcv_pbkeylen) { @@ -4768,25 +4955,25 @@ void srt::CUDT::checkUpdateCryptoKeyLen(const char *loghdr SRT_ATR_UNUSED, int32 if (!m_config.bDataSender) { LOGC(cnlog.Warn, - log << loghdr << ": PBKEYLEN conflict - OVERRIDDEN " << m_config.iSndCryptoKeyLen << " by " - << rcv_pbkeylen << " from PEER (as AGENT is not SRTO_SENDER)"); + log << CONID() << loghdr << ": PBKEYLEN conflict - OVERRIDDEN " << m_config.iSndCryptoKeyLen + << " by " << rcv_pbkeylen << " from PEER (as AGENT is not SRTO_SENDER)"); m_config.iSndCryptoKeyLen = rcv_pbkeylen; } else { LOGC(cnlog.Warn, - log << loghdr << ": PBKEYLEN conflict - keep " << m_config.iSndCryptoKeyLen + log << CONID() << loghdr << ": PBKEYLEN conflict - keep " << m_config.iSndCryptoKeyLen << "; peer-advertised PBKEYLEN " << rcv_pbkeylen << " rejected because Agent is SRTO_SENDER"); } } } else if (enc_flags != 0) { - LOGC(cnlog.Error, log << loghdr << ": IPE: enc_flags outside allowed 2, 3, 4: " << enc_flags); + LOGC(cnlog.Error, log << CONID() << loghdr << ": IPE: enc_flags outside allowed 2, 3, 4: " << enc_flags); } else { - HLOGC(cnlog.Debug, log << loghdr << ": No encryption flags found in type field: " << typefield); + HLOGC(cnlog.Debug, log << CONID() << loghdr << ": No encryption flags found in type field: " << typefield); } } @@ -4837,7 +5024,7 @@ void srt::CUDT::rendezvousSwitchState(UDTRequestType& w_rsptype, bool& w_needs_e #if ENABLE_HEAVY_LOGGING - HLOGC(cnlog.Debug, log << "rendezvousSwitchState: HS: " << m_ConnRes.show()); + HLOGC(cnlog.Debug, log << CONID() << "rendezvousSwitchState: HS: " << m_ConnRes.show()); struct LogAtTheEnd { @@ -4922,10 +5109,10 @@ void srt::CUDT::rendezvousSwitchState(UDTRequestType& w_rsptype, bool& w_needs_e // If no HSRSP attached, stay in this state. if (hs_flags == 0) { - HLOGC( - cnlog.Debug, - log << "rendezvousSwitchState: " - "{INITIATOR}[ATTENTION] awaits CONCLUSION+HSRSP, got CONCLUSION, remain in [ATTENTION]"); + HLOGC(cnlog.Debug, + log << CONID() + << "rendezvousSwitchState: {INITIATOR}[ATTENTION] awaits CONCLUSION+HSRSP, got " + "CONCLUSION, remain in [ATTENTION]"); w_rsptype = URQ_CONCLUSION; w_needs_extension = true; // If you expect to receive HSRSP, continue sending HSREQ return; @@ -4942,10 +5129,10 @@ void srt::CUDT::rendezvousSwitchState(UDTRequestType& w_rsptype, bool& w_needs_e // (Although this seems completely impossible). if (hs_flags == 0) { - LOGC( - cnlog.Warn, - log << "rendezvousSwitchState: (IPE!)" - "{RESPONDER}[ATTENTION] awaits CONCLUSION+HSREQ, got CONCLUSION, remain in [ATTENTION]"); + LOGC(cnlog.Warn, + log << CONID() + << "rendezvousSwitchState: (IPE!){RESPONDER}[ATTENTION] awaits CONCLUSION+HSREQ, got " + "CONCLUSION, remain in [ATTENTION]"); w_rsptype = URQ_CONCLUSION; w_needs_extension = false; // If you received WITHOUT extensions, respond WITHOUT extensions (wait // for the right message) @@ -4958,7 +5145,7 @@ void srt::CUDT::rendezvousSwitchState(UDTRequestType& w_rsptype, bool& w_needs_e return; } - LOGC(cnlog.Error, log << "RENDEZVOUS COOKIE DRAW! Cannot resolve to a valid state."); + LOGC(cnlog.Error, log << CONID() << "RENDEZVOUS COOKIE DRAW! Cannot resolve to a valid state."); // Fallback for cookie draw m_RdvState = CHandShake::RDV_INVALID; w_rsptype = URQFailure(SRT_REJ_RDVCOOKIE); @@ -5023,7 +5210,8 @@ void srt::CUDT::rendezvousSwitchState(UDTRequestType& w_rsptype, bool& w_needs_e // Received REPEATED empty conclusion that has initially switched it into FINE state. // To exit FINE state we need the CONCLUSION message with HSRSP. HLOGC(cnlog.Debug, - log << "rendezvousSwitchState: {INITIATOR}[FINE] m_RcvBufferLock); const steady_clock::time_point tnow = steady_clock::now(); self->m_pRcvBuffer->updRcvAvgDataSize(tnow); - const srt::CRcvBufferNew::PacketInfo info = self->m_pRcvBuffer->getFirstValidPacketInfo(); + const srt::CRcvBuffer::PacketInfo info = self->m_pRcvBuffer->getFirstValidPacketInfo(); const bool is_time_to_deliver = !is_zero(info.tsbpd_time) && (tnow >= info.tsbpd_time); tsNextDelivery = info.tsbpd_time; @@ -5286,6 +5476,10 @@ void * srt::CUDT::tsbpd(void* param) tsNextDelivery = steady_clock::time_point(); // Ready to read, nothing to wait for. } + // We may just briefly unlocked the m_RecvLock, so we need to check m_bClosing again to avoid deadlock. + if (self->m_bClosing) + break; + if (!is_zero(tsNextDelivery)) { IF_HEAVY_LOGGING(const steady_clock::duration timediff = tsNextDelivery - tnow); @@ -5334,15 +5528,7 @@ int srt::CUDT::rcvDropTooLateUpTo(int seqno) if (CSeqNo::seqcmp(seqno, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) seqno = CSeqNo::incseq(m_iRcvCurrSeqNo); - const int seq_gap_len = CSeqNo::seqoff(m_iRcvLastSkipAck, seqno); - - // seq_gap_len can be <= 0 if a packet has been dropped by the sender. - if (seq_gap_len > 0) - { - // Remove [from,to-inclusive] - dropFromLossLists(m_iRcvLastSkipAck, CSeqNo::decseq(seqno)); - m_iRcvLastSkipAck = seqno; - } + dropFromLossLists(SRT_SEQNO_NONE, CSeqNo::decseq(seqno)); const int iDropCnt = m_pRcvBuffer->dropUpTo(seqno); if (iDropCnt > 0) @@ -5356,305 +5542,89 @@ int srt::CUDT::rcvDropTooLateUpTo(int seqno) return iDropCnt; } -#else -void * srt::CUDT::tsbpd(void *param) +void srt::CUDT::setInitialRcvSeq(int32_t isn) { - CUDT *self = (CUDT *)param; - - THREAD_STATE_INIT("SRT:TsbPd"); - -#if ENABLE_BONDING - // Make the TSBPD thread a "client" of the group, - // which will ensure that the group will not be physically - // deleted until this thread exits. - // NOTE: DO NOT LEAD TO EVER CANCEL THE THREAD!!! - CUDTUnited::GroupKeeper gkeeper (self->uglobal(), self->m_parent); + m_iRcvLastAck = isn; +#ifdef ENABLE_LOGGING + m_iDebugPrevLastAck = isn; #endif + m_iRcvLastAckAck = isn; + m_iRcvCurrSeqNo = CSeqNo::decseq(isn); - UniqueLock recv_lock (self->m_RecvLock); - CSync recvdata_cc (self->m_RecvDataCond, recv_lock); - CSync tsbpd_cc (self->m_RcvTsbPdCond, recv_lock); - - self->m_bTsbPdAckWakeup = true; - while (!self->m_bClosing) + sync::ScopedLock rb(m_RcvBufferLock); + if (m_pRcvBuffer) { - int32_t current_pkt_seq = 0; - steady_clock::time_point tsbpdtime; - bool rxready = false; - int32_t rcv_base_seq = SRT_SEQNO_NONE; -#if ENABLE_BONDING - bool shall_update_group = false; - if (gkeeper.group) + if (!m_pRcvBuffer->empty()) { - // Functions called below will lock m_GroupLock, which in hierarchy - // lies after m_RecvLock. Must unlock m_RecvLock to be able to lock - // m_GroupLock inside the calls. - InvertedLock unrecv(self->m_RecvLock); - rcv_base_seq = gkeeper.group->getRcvBaseSeqNo(); + LOGC(cnlog.Error, log << CONID() << "IPE: setInitialRcvSeq expected empty RCV buffer. Dropping all."); + const int iDropCnt = m_pRcvBuffer->dropAll(); + const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); + sync::ScopedLock sl(m_StatsLock); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); } -#endif - - enterCS(self->m_RcvBufferLock); - - self->m_pRcvBuffer->updRcvAvgDataSize(steady_clock::now()); - - if (self->m_bTLPktDrop) - { - int32_t skiptoseqno = SRT_SEQNO_NONE; - bool passack = true; // Get next packet to wait for even if not acked - rxready = self->m_pRcvBuffer->getRcvFirstMsg((tsbpdtime), (passack), (skiptoseqno), (current_pkt_seq), rcv_base_seq); - - HLOGC(tslog.Debug, - log << boolalpha << "NEXT PKT CHECK: rdy=" << rxready << " passack=" << passack << " skipto=%" - << skiptoseqno << " current=%" << current_pkt_seq << " buf-base=%" << self->m_iRcvLastSkipAck); - /* - * VALUES RETURNED: - * - * rxready: if true, packet at head of queue ready to play - * tsbpdtime: timestamp of packet at head of queue, ready or not. 0 if none. - * passack: if true, ready head of queue not yet acknowledged - * skiptoseqno: sequence number of packet at head of queue if ready to play but - * some preceeding packets are missing (need to be skipped). -1 if none. - */ - if (rxready) - { - /* Packet ready to play according to time stamp but... */ - int seqlen = CSeqNo::seqoff(self->m_iRcvLastSkipAck, skiptoseqno); - - if (skiptoseqno != SRT_SEQNO_NONE && seqlen > 0) - { - /* - * skiptoseqno != SRT_SEQNO_NONE, - * packet ready to play but preceeded by missing packets (hole). - */ - self->updateForgotten(seqlen, self->m_iRcvLastSkipAck, skiptoseqno); - self->m_pRcvBuffer->skipData(seqlen); + m_pRcvBuffer->setStartSeqNo(isn); + } +} - self->m_iRcvLastSkipAck = skiptoseqno; -#if ENABLE_BONDING - shall_update_group = true; -#endif +bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout) +{ + // This will be lazily created due to being the common + // code with HSv5 rendezvous, in which this will be run + // in a little bit "randomly selected" moment, but must + // be run once in the whole connection process. + if (m_pCryptoControl) + { + HLOGC(rslog.Debug, log << CONID() << "prepareConnectionObjects: (lazy) already created."); + return true; + } -#if ENABLE_LOGGING - int64_t timediff_us = 0; - if (!is_zero(tsbpdtime)) - timediff_us = count_microseconds(steady_clock::now() - tsbpdtime); -#if ENABLE_HEAVY_LOGGING - HLOGC(tslog.Debug, - log << self->CONID() << "tsbpd: DROPSEQ: up to seqno %" << CSeqNo::decseq(skiptoseqno) << " (" - << seqlen << " packets) playable at " << FormatTime(tsbpdtime) << " delayed " - << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') << (timediff_us % 1000) << " ms"); -#endif - LOGC(brlog.Warn, - log << self->CONID() << "RCV-DROPPED " << seqlen << " packet(s), packet seqno %" << skiptoseqno - << " delayed for " << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') - << (timediff_us % 1000) << " ms"); -#endif + // HSv5 is always bidirectional + const bool bidirectional = (hs.m_iVersion > HS_VERSION_UDT4); - tsbpdtime = steady_clock::time_point(); //Next sent ack will unblock - rxready = false; - } - else if (passack) - { - /* Packets ready to play but not yet acknowledged (should happen within 10ms) */ - rxready = false; - tsbpdtime = steady_clock::time_point(); // Next sent ack will unblock - } /* else packet ready to play */ - } /* else packets not ready to play */ + // HSD_DRAW is received only if this side is listener. + // If this side is caller with HSv5, HSD_INITIATOR should be passed. + // If this is a rendezvous connection with HSv5, the handshake role + // is taken from m_SrtHsSide field. + if (hsd == HSD_DRAW) + { + if (bidirectional) + { + hsd = HSD_RESPONDER; // In HSv5, listener is always RESPONDER and caller always INITIATOR. } else { - rxready = self->m_pRcvBuffer->isRcvDataReady((tsbpdtime), (current_pkt_seq), -1 /*get first ready*/); + hsd = m_config.bDataSender ? HSD_INITIATOR : HSD_RESPONDER; } - leaveCS(self->m_RcvBufferLock); + } - if (rxready) - { - HLOGC(tslog.Debug, - log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << current_pkt_seq << " (belated " - << (count_milliseconds(steady_clock::now() - tsbpdtime)) << "ms)"); - /* - * There are packets ready to be delivered - * signal a waiting "recv" call if there is any data available - */ - if (self->m_config.bSynRecving) - { - recvdata_cc.notify_one_locked(recv_lock); - } - /* - * Set EPOLL_IN to wakeup any thread waiting on epoll - */ - self->uglobal().m_EPoll.update_events(self->m_SocketID, self->m_sPollID, SRT_EPOLL_IN, true); -#if ENABLE_BONDING - // If this is NULL, it means: - // - the socket never was a group member - // - the socket was a group member, but: - // - was just removed as a part of closure - // - and will never be member of the group anymore + if (!createCrypter(hsd, bidirectional)) // Make sure CC is created (lazy) + { + if (eout) + *eout = CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); + m_RejectReason = SRT_REJ_RESOURCE; + return false; + } - // If this is not NULL, it means: - // - This socket is currently member of the group - // - This socket WAS a member of the group, though possibly removed from it already, BUT: - // - the group that this socket IS OR WAS member of is in the GroupKeeper - // - the GroupKeeper prevents the group from being deleted - // - it is then completely safe to access the group here, - // EVEN IF THE SOCKET THAT WAS ITS MEMBER IS BEING DELETED. + return true; +} - // It is ensured that the group object exists here because GroupKeeper - // keeps it busy, even if you just closed the socket, remove it as a member - // or even the group is empty and was explicitly closed. - if (gkeeper.group) - { - // Functions called below will lock m_GroupLock, which in hierarchy - // lies after m_RecvLock. Must unlock m_RecvLock to be able to lock - // m_GroupLock inside the calls. - InvertedLock unrecv(self->m_RecvLock); - // The current "APP reader" needs to simply decide as to whether - // the next CUDTGroup::recv() call should return with no blocking or not. - // When the group is read-ready, it should update its pollers as it sees fit. - - // NOTE: this call will set lock to m_GroupOf->m_GroupLock - HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: GROUP: checking if %" << current_pkt_seq << " makes group readable"); - gkeeper.group->updateReadState(self->m_SocketID, current_pkt_seq); - - if (shall_update_group) - { - // A group may need to update the parallelly used idle links, - // should it have any. Pass the current socket position in order - // to skip it from the group loop. - // NOTE: SELF LOCKING. - gkeeper.group->updateLatestRcv(self->m_parent); - } - } -#endif - CGlobEvent::triggerEvent(); - tsbpdtime = steady_clock::time_point(); - } - - if (!is_zero(tsbpdtime)) - { - IF_HEAVY_LOGGING(const steady_clock::duration timediff = tsbpdtime - steady_clock::now()); - /* - * Buffer at head of queue is not ready to play. - * Schedule wakeup when it will be. - */ - self->m_bTsbPdAckWakeup = false; - HLOGC(tslog.Debug, - log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << current_pkt_seq - << " T=" << FormatTime(tsbpdtime) << " - waiting " << count_milliseconds(timediff) << "ms"); - THREAD_PAUSED(); - tsbpd_cc.wait_until(tsbpdtime); - THREAD_RESUMED(); - } - else - { - /* - * We have just signaled epoll; or - * receive queue is empty; or - * next buffer to deliver is not in receive queue (missing packet in sequence). - * - * Block until woken up by one of the following event: - * - All ready-to-play packets have been pulled and EPOLL_IN cleared (then loop to block until next pkt time - * if any) - * - New buffers ACKed - * - Closing the connection - */ - HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: no data, scheduling wakeup at ack"); - self->m_bTsbPdAckWakeup = true; - THREAD_PAUSED(); - tsbpd_cc.wait(); - THREAD_RESUMED(); - } - - HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP!!!"); - } - THREAD_EXIT(); - HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING"); - return NULL; -} -#endif // ENABLE_NEW_RCVBUFFER - -void srt::CUDT::setInitialRcvSeq(int32_t isn) -{ - m_iRcvLastAck = isn; -#ifdef ENABLE_LOGGING - m_iDebugPrevLastAck = m_iRcvLastAck; -#endif - m_iRcvLastSkipAck = m_iRcvLastAck; - m_iRcvLastAckAck = isn; - m_iRcvCurrSeqNo = CSeqNo::decseq(isn); - -#if ENABLE_NEW_RCVBUFFER - sync::ScopedLock rb(m_RcvBufferLock); - if (m_pRcvBuffer) - { - if (!m_pRcvBuffer->empty()) - { - LOGC(cnlog.Error, log << "IPE: setInitialRcvSeq expected empty RCV buffer. Dropping all."); - const int iDropCnt = m_pRcvBuffer->dropAll(); - const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - sync::ScopedLock sl(m_StatsLock); - m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); - } - - m_pRcvBuffer->setStartSeqNo(m_iRcvLastSkipAck); - } -#endif -} - -void srt::CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) -{ - enterCS(m_StatsLock); - // Estimate dropped bytes from average payload size. - const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.dropped.count(stats::BytesPackets(seqlen * avgpayloadsz, (uint32_t) seqlen)); - leaveCS(m_StatsLock); - - dropFromLossLists(lastack, CSeqNo::decseq(skiptoseqno)); //remove(from,to-inclusive) -} - -bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout) +bool srt::CUDT::prepareBuffers(CUDTException* eout) { - // This will be lazily created due to being the common - // code with HSv5 rendezvous, in which this will be run - // in a little bit "randomly selected" moment, but must - // be run once in the whole connection process. if (m_pSndBuffer) { - HLOGC(rslog.Debug, log << "prepareConnectionObjects: (lazy) already created."); + HLOGC(rslog.Debug, log << CONID() << "prepareBuffers: (lazy) already created."); return true; } - - // HSv5 is always bidirectional - const bool bidirectional = (hs.m_iVersion > HS_VERSION_UDT4); - - // HSD_DRAW is received only if this side is listener. - // If this side is caller with HSv5, HSD_INITIATOR should be passed. - // If this is a rendezvous connection with HSv5, the handshake role - // is taken from m_SrtHsSide field. - if (hsd == HSD_DRAW) - { - if (bidirectional) - { - hsd = HSD_RESPONDER; // In HSv5, listener is always RESPONDER and caller always INITIATOR. - } - else - { - hsd = m_config.bDataSender ? HSD_INITIATOR : HSD_RESPONDER; - } - } - + try { - m_pSndBuffer = new CSndBuffer(32, m_iMaxSRTPayloadSize); -#if ENABLE_NEW_RCVBUFFER - SRT_ASSERT(m_iISN != -1); - m_pRcvBuffer = new srt::CRcvBufferNew(m_iISN, m_config.iRcvBufSize, m_pRcvQueue->m_pUnitQueue, m_config.bMessageAPI); -#else - m_pRcvBuffer = new CRcvBuffer(m_pRcvQueue->m_pUnitQueue, m_config.iRcvBufSize); -#endif - // after introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice space. + // CryptoControl has to be initialized and in case of RESPONDER the KM REQ must be processed (interpretSrtHandshake(..)) for the crypto mode to be deduced. + const int authtag = (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM) ? HAICRYPT_AUTHTAG_MAX : 0; + m_pSndBuffer = new CSndBuffer(32, m_iMaxSRTPayloadSize, authtag); + SRT_ASSERT(m_iPeerISN != -1); + m_pRcvBuffer = new srt::CRcvBuffer(m_iPeerISN, m_config.iRcvBufSize, m_pRcvQueue->m_pUnitQueue, m_config.bMessageAPI); + // After introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice a space. m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2); m_pRcvLossList = new CRcvLossList(m_config.iFlightFlagSize); } @@ -5662,25 +5632,16 @@ bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd { // Simply reject. if (eout) - { *eout = CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); - } - m_RejectReason = SRT_REJ_RESOURCE; - return false; - } - - if (!createCrypter(hsd, bidirectional)) // Make sure CC is created (lazy) - { m_RejectReason = SRT_REJ_RESOURCE; return false; } - return true; } void srt::CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) { - // this is a reponse handshake + // this is a response handshake w_hs.m_iReqType = URQ_CONCLUSION; w_hs.m_iMSS = m_config.iMSS; w_hs.m_iFlightFlagSize = m_config.flightCapacity(); @@ -5699,7 +5660,7 @@ void srt::CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& w_hs) { - HLOGC(cnlog.Debug, log << "acceptAndRespond: setting up data according to handshake"); + HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: setting up data according to handshake"); ScopedLock cg(m_ConnectionLock); @@ -5731,12 +5692,13 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE; m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; - HLOGC(cnlog.Debug, log << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); // Prepare all structures if (!prepareConnectionObjects(w_hs, HSD_DRAW, 0)) { - HLOGC(cnlog.Debug, log << "acceptAndRespond: prepareConnectionObjects failed - responding with REJECT."); + HLOGC(cnlog.Debug, + log << CONID() << "acceptAndRespond: prepareConnectionObjects failed - responding with REJECT."); // If the SRT Handshake extension was provided and wasn't interpreted // correctly, the connection should be rejected. // @@ -5772,7 +5734,8 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& size_t kmdatasize = SRTDATA_MAXSIZE; if (!interpretSrtHandshake(w_hs, hspkt, (kmdata), (&kmdatasize))) { - HLOGC(cnlog.Debug, log << "acceptAndRespond: interpretSrtHandshake failed - responding with REJECT."); + HLOGC(cnlog.Debug, + log << CONID() << "acceptAndRespond: interpretSrtHandshake failed - responding with REJECT."); // If the SRT Handshake extension was provided and wasn't interpreted // correctly, the connection should be rejected. // @@ -5783,6 +5746,19 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& throw CUDTException(MJ_SETUP, MN_REJECTED, 0); } + if (!prepareBuffers(NULL)) + { + HLOGC(cnlog.Debug, + log << CONID() << "acceptAndRespond: prepareConnectionObjects failed - responding with REJECT."); + // If the SRT buffers failed to be allocated, + // the connection must be rejected. + // + // Respond with the rejection message and exit with exception + // so that the caller will know that this new socket should be deleted. + w_hs.m_iReqType = URQFailure(m_RejectReason); + throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + } + // Synchronize the time NOW because the following function is about // to use the start time to pass it to the receiver buffer data. bool have_group = false; @@ -5843,13 +5819,17 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // This will serialize the handshake according to its current form. HLOGC(cnlog.Debug, - log << "acceptAndRespond: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); + log << CONID() + << "acceptAndRespond: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (response), (w_hs))) { - LOGC(cnlog.Error, log << "acceptAndRespond: error creating handshake response"); + LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: error creating handshake response"); throw CUDTException(MJ_SETUP, MN_REJECTED, 0); } + // We can safely assign it here stating that this has passed the cookie test. + m_SourceAddr = hspkt.udpDestAddr(); + #if ENABLE_HEAVY_LOGGING { // To make sure what REALLY is being sent, parse back the handshake @@ -5859,7 +5839,8 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: sending HS from agent @" << debughs.m_iID << " to peer @" << response.m_iID - << "HS:" << debughs.show()); + << "HS:" << debughs.show() + << " sourceIP=" << m_SourceAddr.str()); } #endif @@ -5871,6 +5852,15 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& addressAndSend((response)); } +bool srt::CUDT::frequentLogAllowed(const time_point& tnow) const +{ +#ifndef SRT_LOG_SLOWDOWN_FREQ_MS +#define SRT_LOG_SLOWDOWN_FREQ_MS 1000 +#endif + + return (m_tsLogSlowDown + milliseconds_from(SRT_LOG_SLOWDOWN_FREQ_MS)) <= tnow; +} + // This function is required to be called when a caller receives an INDUCTION // response from the listener and would like to create a CONCLUSION that includes // the SRT handshake extension. This extension requires that the crypter object @@ -5898,7 +5888,7 @@ bool srt::CUDT::createCrypter(HandshakeSide side, bool bidirectional) if (bidirectional || m_config.bDataSender) { - HLOGC(rslog.Debug, log << "createCrypter: setting RCV/SND KeyLen=" << m_config.iSndCryptoKeyLen); + HLOGC(rslog.Debug, log << CONID() << "createCrypter: setting RCV/SND KeyLen=" << m_config.iSndCryptoKeyLen); m_pCryptoControl->setCryptoKeylen(m_config.iSndCryptoKeyLen); } @@ -5936,7 +5926,7 @@ SRT_REJECT_REASON srt::CUDT::setupCC() // At this point we state everything is checked and the appropriate // corrector type is already selected, so now create it. - HLOGC(pflog.Debug, log << "filter: Configuring: " << m_config.sPacketFilterConfig.c_str()); + HLOGC(pflog.Debug, log << CONID() << "filter: Configuring: " << m_config.sPacketFilterConfig.c_str()); bool status = true; try { @@ -5976,13 +5966,13 @@ SRT_REJECT_REASON srt::CUDT::setupCC() m_tsLastSndTime.store(currtime); HLOGC(rslog.Debug, - log << "setupCC: setting parameters: mss=" << m_config.iMSS << " maxCWNDSize/FlowWindowSize=" << m_iFlowWindowSize - << " rcvrate=" << m_iDeliveryRate << "p/s (" << m_iByteDeliveryRate << "B/S)" + log << CONID() << "setupCC: setting parameters: mss=" << m_config.iMSS << " maxCWNDSize/FlowWindowSize=" + << m_iFlowWindowSize << " rcvrate=" << m_iDeliveryRate << "p/s (" << m_iByteDeliveryRate << "B/S)" << " rtt=" << m_iSRTT << " bw=" << m_iBandwidth); if (!updateCC(TEV_INIT, EventVariant(TEV_INIT_RESET))) { - LOGC(rslog.Error, log << "setupCC: IPE: resrouces not yet initialized!"); + LOGC(rslog.Error, log << CONID() << "setupCC: IPE: resrouces not yet initialized!"); return SRT_REJ_IPE; } return SRT_REJ_UNKNOWN; @@ -5998,7 +5988,7 @@ void srt::CUDT::considerLegacySrtHandshake(const steady_clock::time_point &timeb if (m_iSndHsRetryCnt <= 0) { - HLOGC(cnlog.Debug, log << "Legacy HSREQ: not needed, expire counter=" << m_iSndHsRetryCnt); + HLOGC(cnlog.Debug, log << CONID() << "Legacy HSREQ: not needed, expire counter=" << m_iSndHsRetryCnt); return; } @@ -6018,7 +6008,8 @@ void srt::CUDT::considerLegacySrtHandshake(const steady_clock::time_point &timeb */ if (timebase > now) // too early { - HLOGC(cnlog.Debug, log << "Legacy HSREQ: TOO EARLY, will still retry " << m_iSndHsRetryCnt << " times"); + HLOGC(cnlog.Debug, + log << CONID() << "Legacy HSREQ: TOO EARLY, will still retry " << m_iSndHsRetryCnt << " times"); return; } } @@ -6027,43 +6018,48 @@ void srt::CUDT::considerLegacySrtHandshake(const steady_clock::time_point &timeb else if (m_iSndHsRetryCnt < SRT_MAX_HSRETRY + 1) { HLOGC(cnlog.Debug, - log << "Legacy HSREQ: INITIAL, REPEATED, so not to be done. Will repeat on sending " << m_iSndHsRetryCnt - << " times"); + log << CONID() << "Legacy HSREQ: INITIAL, REPEATED, so not to be done. Will repeat on sending " + << m_iSndHsRetryCnt << " times"); return; } - HLOGC(cnlog.Debug, log << "Legacy HSREQ: SENDING, will repeat " << m_iSndHsRetryCnt << " times if no response"); + HLOGC(cnlog.Debug, + log << CONID() << "Legacy HSREQ: SENDING, will repeat " << m_iSndHsRetryCnt << " times if no response"); m_iSndHsRetryCnt--; m_tsSndHsLastTime = now; sendSrtMsg(SRT_CMD_HSREQ); } -void srt::CUDT::checkSndTimers(Whether2RegenKm regen) +void srt::CUDT::checkSndTimers() { if (m_SrtHsSide == HSD_INITIATOR) { - HLOGC(cnlog.Debug, log << "checkSndTimers: HS SIDE: INITIATOR, considering legacy handshake with timebase"); + HLOGC(cnlog.Debug, + log << CONID() << "checkSndTimers: HS SIDE: INITIATOR, considering legacy handshake with timebase"); // Legacy method for HSREQ, only if initiator. considerLegacySrtHandshake(m_tsSndHsLastTime + microseconds_from(m_iSRTT * 3 / 2)); } else { HLOGC(cnlog.Debug, - log << "checkSndTimers: HS SIDE: " << (m_SrtHsSide == HSD_RESPONDER ? "RESPONDER" : "DRAW (IPE?)") + log << CONID() + << "checkSndTimers: HS SIDE: " << (m_SrtHsSide == HSD_RESPONDER ? "RESPONDER" : "DRAW (IPE?)") << " - not considering legacy handshake"); } - // This must be done always on sender, regardless of HS side. - // When regen == DONT_REGEN_KM, it's a handshake call, so do - // it only for initiator. - if (regen || m_SrtHsSide == HSD_INITIATOR) - { - // Don't call this function in "non-regen mode" (sending only), - // if this side is RESPONDER. This shall be called only with - // regeneration request, which is required by the sender. - if (m_pCryptoControl) - m_pCryptoControl->sendKeysToPeer(this, SRTT(), regen); - } + // Retransmit KM request after a timeout if there is no response (KM RSP). + // Or send KM REQ in case of the HSv4. + ScopedLock lck(m_ConnectionLock); + if (m_pCryptoControl) + m_pCryptoControl->sendKeysToPeer(this, SRTT()); +} + +void srt::CUDT::checkSndKMRefresh() +{ + // Do not apply the regenerated key to the to the receiver context. + const bool bidir = false; + if (m_pCryptoControl) + m_pCryptoControl->regenCryptoKm(this, bidir); } void srt::CUDT::addressAndSend(CPacket& w_pkt) @@ -6076,7 +6072,7 @@ void srt::CUDT::addressAndSend(CPacket& w_pkt) // before sending for performance purposes, // and then modification is undone. Logically then // there's no modification here. - m_pSndQueue->sendto(m_PeerAddr, w_pkt); + m_pSndQueue->sendto(m_PeerAddr, w_pkt, m_SourceAddr); } // [[using maybe_locked(m_GlobControlLock, if called from GC)]] @@ -6094,13 +6090,13 @@ bool srt::CUDT::closeInternal() // that has m_bBroken == false or m_bConnected == true. // If it is intended to forcefully close the socket, make sure // that it's in response to a broken connection. - HLOGC(smlog.Debug, log << CONID() << " - closing socket:"); + HLOGC(smlog.Debug, log << CONID() << "closing socket"); if (m_config.Linger.l_onoff != 0) { const steady_clock::time_point entertime = steady_clock::now(); - HLOGC(smlog.Debug, log << CONID() << " ... (linger)"); + HLOGC(smlog.Debug, log << CONID() << "... (linger)"); while (!m_bBroken && m_bConnected && (m_pSndBuffer->getCurrBufSize() > 0) && (steady_clock::now() - entertime < seconds_from(m_config.Linger.l_linger))) { @@ -6115,7 +6111,7 @@ bool srt::CUDT::closeInternal() m_tsLingerExpiration = entertime + seconds_from(m_config.Linger.l_linger); HLOGC(smlog.Debug, - log << "CUDT::close: linger-nonblocking, setting expire time T=" + log << CONID() << "CUDT::close: linger-nonblocking, setting expire time T=" << FormatTime(m_tsLingerExpiration)); return false; @@ -6151,13 +6147,13 @@ bool srt::CUDT::closeInternal() leaveCS(uglobal().m_EPoll.m_EPollLock); // trigger any pending IO events. - HLOGC(smlog.Debug, log << "close: SETTING ERR readiness on E" << Printable(epollid) << " of @" << m_SocketID); + HLOGC(smlog.Debug, log << CONID() << "close: SETTING ERR readiness on E" << Printable(epollid)); uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_ERR, true); // then remove itself from all epoll monitoring int no_events = 0; for (set::iterator i = epollid.begin(); i != epollid.end(); ++i) { - HLOGC(smlog.Debug, log << "close: CLEARING subscription on E" << (*i) << " of @" << m_SocketID); + HLOGC(smlog.Debug, log << CONID() << "close: CLEARING subscription on E" << (*i)); try { uglobal().m_EPoll.update_usock(*i, m_SocketID, &no_events); @@ -6168,7 +6164,7 @@ bool srt::CUDT::closeInternal() // the epoll system to this socket. If it's unsubscribed already, // that's even better. } - HLOGC(smlog.Debug, log << "close: removing E" << (*i) << " from back-subscribers of @" << m_SocketID); + HLOGC(smlog.Debug, log << CONID() << "close: removing E" << (*i) << " from back-subscribers"); } // Not deleting elements from m_sPollID inside the loop because it invalidates @@ -6233,7 +6229,7 @@ bool srt::CUDT::closeInternal() m_bConnected = false; } - HLOGC(smlog.Debug, log << "CLOSING, joining send/receive threads"); + HLOGC(smlog.Debug, log << CONID() << "CLOSING, joining send/receive threads"); // waiting all send and recv calls to stop ScopedLock sendguard(m_SendLock); @@ -6290,12 +6286,12 @@ int srt::CUDT::receiveBuffer(char *data, int len) // make this function return 0, potentially also without breaking // the connection and potentially also with losing no ability to // send some larger portion of data next time. - HLOGC(arlog.Debug, log << "STREAM API, SHUTDOWN: marking as EOF"); + HLOGC(arlog.Debug, log << CONID() << "STREAM API, SHUTDOWN: marking as EOF"); return 0; } HLOGC(arlog.Debug, - log << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") - << " SHUTDOWN. Reporting as BROKEN."); + log << CONID() << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") + << " SHUTDOWN. Reporting as BROKEN."); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -6342,11 +6338,11 @@ int srt::CUDT::receiveBuffer(char *data, int len) // See at the beginning if (!m_config.bMessageAPI && m_bShutdown) { - HLOGC(arlog.Debug, log << "STREAM API, SHUTDOWN: marking as EOF"); + HLOGC(arlog.Debug, log << CONID() << "STREAM API, SHUTDOWN: marking as EOF"); return 0; } HLOGC(arlog.Debug, - log << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") + log << CONID() << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") << " SHUTDOWN. Reporting as BROKEN."); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -6388,7 +6384,7 @@ int srt::CUDT::sndDropTooLate() if (!m_config.bMessageAPI) { - LOGC(aslog.Error, log << "The SRTO_TLPKTDROP flag can only be used with message API."); + LOGC(aslog.Error, log << CONID() << "The SRTO_TLPKTDROP flag can only be used with message API."); throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0); } @@ -6396,7 +6392,7 @@ int srt::CUDT::sndDropTooLate() const int buffdelay_ms = (int) count_milliseconds(m_pSndBuffer->getBufferingDelay(tnow)); // high threshold (msec) at tsbpd_delay plus sender/receiver reaction time (2 * 10ms) - // Minimum value must accomodate an I-Frame (~8 x average frame size) + // Minimum value must accommodate an I-Frame (~8 x average frame size) // >>need picture rate or app to set min treshold // >>using 1 sec for worse case 1 frame using all bit budget. // picture rate would be useful in auto SRT setting for min latency @@ -6437,8 +6433,9 @@ int srt::CUDT::sndDropTooLate() m_iSndCurrSeqNo = minlastack; } - HLOGC(aslog.Debug, log << "SND-DROP: %(" << realack << "-" << m_iSndCurrSeqNo << ") n=" - << dpkts << "pkt " << dbytes << "B, span=" << buffdelay_ms << " ms, FIRST #" << first_msgno); + HLOGC(aslog.Debug, + log << CONID() << "SND-DROP: %(" << realack << "-" << m_iSndCurrSeqNo << ") n=" << dpkts << "pkt " << dbytes + << "B, span=" << buffdelay_ms << " ms, FIRST #" << first_msgno); #if ENABLE_BONDING // This is done with a presumption that the group @@ -6488,7 +6485,7 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) if (len <= 0) { - LOGC(aslog.Error, log << "INVALID: Data size for sending declared with length: " << len); + LOGC(aslog.Error, log << CONID() << "INVALID: Data size for sending declared with length: " << len); return 0; } @@ -6496,7 +6493,9 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) { if (w_mctrl.msgno < 1 || w_mctrl.msgno > MSGNO_SEQ_MAX) { - LOGC(aslog.Error, log << "INVALID forced msgno " << w_mctrl.msgno << ": can be -1 (trap) or <1..." << MSGNO_SEQ_MAX << ">"); + LOGC(aslog.Error, + log << CONID() << "INVALID forced msgno " << w_mctrl.msgno << ": can be -1 (trap) or <1..." + << MSGNO_SEQ_MAX << ">"); throw CUDTException(MJ_NOTSUP, MN_INVAL); } } @@ -6541,7 +6540,7 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) if (m_config.bMessageAPI && len > int(m_config.iSndBufSize * m_iMaxSRTPayloadSize)) { LOGC(aslog.Error, - log << "Message length (" << len << ") exceeds the size of sending buffer: " + log << CONID() << "Message length (" << len << ") exceeds the size of sending buffer: " << (m_config.iSndBufSize * m_iMaxSRTPayloadSize) << ". Use SRTO_SNDBUF if needed."); throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0); } @@ -6570,15 +6569,20 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) // to modify m_pSndBuffer and m_pSndLossList const int iPktsTLDropped SRT_ATR_UNUSED = sndDropTooLate(); - int minlen = 1; // Minimum sender buffer space required for STREAM API - if (m_config.bMessageAPI) + // For MESSAGE API the minimum outgoing buffer space required is + // the size that can carry over the whole message as passed here. + // Otherwise it is allowed to send less bytes. + const int iNumPktsRequired = m_config.bMessageAPI ? m_pSndBuffer->countNumPacketsRequired(len) : 1; + + if (m_bTsbPd && iNumPktsRequired > 1) { - // For MESSAGE API the minimum outgoing buffer space required is - // the size that can carry over the whole message as passed here. - minlen = (len + m_iMaxSRTPayloadSize - 1) / m_iMaxSRTPayloadSize; + LOGC(aslog.Error, + log << CONID() << "Message length (" << len << ") can't fit into a single data packet (" + << m_pSndBuffer->getMaxPacketLen() << " bytes max)."); + throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0); } - if (sndBuffersLeft() < minlen) + if (sndBuffersLeft() < iNumPktsRequired) { //>>We should not get here if SRT_ENABLE_TLPKTDROP // XXX Check if this needs to be removed, or put to an 'else' condition for m_bTLPktDrop. @@ -6591,7 +6595,7 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) if (m_config.iSndTimeOut < 0) { - while (stillConnected() && sndBuffersLeft() < minlen && m_bPeerHealth) + while (stillConnected() && sndBuffersLeft() < iNumPktsRequired && m_bPeerHealth) m_SendBlockCond.wait(sendblock_lock); } else @@ -6599,7 +6603,7 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) const steady_clock::time_point exptime = steady_clock::now() + milliseconds_from(m_config.iSndTimeOut); THREAD_PAUSED(); - while (stillConnected() && sndBuffersLeft() < minlen && m_bPeerHealth) + while (stillConnected() && sndBuffersLeft() < iNumPktsRequired && m_bPeerHealth) { if (!m_SendBlockCond.wait_until(sendblock_lock, exptime)) break; @@ -6621,11 +6625,11 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) /* * The code below is to return ETIMEOUT when blocking mode could not get free buffer in time. - * If no free buffer available in non-blocking mode, we alredy returned. If buffer availaible, + * If no free buffer available in non-blocking mode, we alredy returned. If buffer available, * we test twice if this code is outside the else section. * This fix move it in the else (blocking-mode) section */ - if (sndBuffersLeft() < minlen) + if (sndBuffersLeft() < iNumPktsRequired) { if (m_config.iSndTimeOut >= 0) throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0); @@ -6645,7 +6649,8 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) // // ERGO: never happens? LOGC(aslog.Fatal, - log << "IPE: sendmsg: the loop exited, while not enough size, still connected, peer healthy. " + log << CONID() + << "IPE: sendmsg: the loop exited, while not enough size, still connected, peer healthy. " "Impossible."); return 0; @@ -6716,14 +6721,16 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) if (w_mctrl.srctime && w_mctrl.srctime < count_microseconds(m_stats.tsStartTime.time_since_epoch())) { LOGC(aslog.Error, - log << "Wrong source time was provided. Sending is rejected."); + log << CONID() << "Wrong source time was provided. Sending is rejected."); throw CUDTException(MJ_NOTSUP, MN_INVALMSGAPI); } if (w_mctrl.srctime && (!m_config.bMessageAPI || !m_bTsbPd)) { - HLOGC(aslog.Warn, - log << "Source time can only be used with TSBPD and Message API enabled. Using default time instead."); + HLOGC( + aslog.Warn, + log << CONID() + << "Source time can only be used with TSBPD and Message API enabled. Using default time instead."); w_mctrl.srctime = 0; } @@ -6755,7 +6762,7 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) // IF there was a packet drop on the sender side, report congestion to the app. if (iPktsTLDropped > 0) { - LOGC(aslog.Error, log << "sendmsg2: CONGESTION; reporting error"); + LOGC(aslog.Error, log << CONID() << "sendmsg2: CONGESTION; reporting error"); throw CUDTException(MJ_AGAIN, MN_CONGESTION, 0); } #endif /* SRT_ENABLE_ECN */ @@ -6799,7 +6806,7 @@ int srt::CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) if (len <= 0) { - LOGC(arlog.Error, log << "Length of '" << len << "' supplied to srt_recvmsg."); + LOGC(arlog.Error, log << CONID() << "Length of '" << len << "' supplied to srt_recvmsg."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } @@ -6809,29 +6816,21 @@ int srt::CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) return receiveBuffer(data, len); } -size_t srt::CUDT::getAvailRcvBufferSizeLock() const -{ - ScopedLock lck(m_RcvBufferLock); - return getAvailRcvBufferSizeNoLock(); -} - +// [[using locked(m_RcvBufferLock)]] size_t srt::CUDT::getAvailRcvBufferSizeNoLock() const { -#if ENABLE_NEW_RCVBUFFER return m_pRcvBuffer->getAvailSize(m_iRcvLastAck); -#else - return m_pRcvBuffer->getAvailBufSize(); -#endif } bool srt::CUDT::isRcvBufferReady() const { ScopedLock lck(m_RcvBufferLock); -#if ENABLE_NEW_RCVBUFFER return m_pRcvBuffer->isRcvDataReady(steady_clock::now()); -#else - return m_pRcvBuffer->isRcvDataReady(); -#endif +} + +bool srt::CUDT::isRcvBufferReadyNoLock() const +{ + return m_pRcvBuffer->isRcvDataReady(steady_clock::now()); } // int by_exception: accepts values of CUDTUnited::ErrorHandling: @@ -6874,15 +6873,10 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ { HLOGC(arlog.Debug, log << CONID() << "receiveMessage: CONNECTION BROKEN - reading from recv buffer just for formality"); enterCS(m_RcvBufferLock); -#if ENABLE_NEW_RCVBUFFER const int res = (m_pRcvBuffer->isRcvDataReady(steady_clock::now())) ? m_pRcvBuffer->readMessage(data, len, &w_mctrl) : 0; -#else - const int res = m_pRcvBuffer->readMsg(data, len); -#endif leaveCS(m_RcvBufferLock); - w_mctrl.srctime = 0; // Kick TsbPd thread to schedule next wakeup (if running) if (m_bTsbPd) @@ -6914,21 +6908,13 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ return res; } -#if !ENABLE_NEW_RCVBUFFER - const int seqdistance = -1; -#endif - if (!m_config.bSynRecving) { HLOGC(arlog.Debug, log << CONID() << "receiveMessage: BEGIN ASYNC MODE. Going to extract payload size=" << len); enterCS(m_RcvBufferLock); -#if ENABLE_NEW_RCVBUFFER const int res = (m_pRcvBuffer->isRcvDataReady(steady_clock::now())) ? m_pRcvBuffer->readMessage(data, len, &w_mctrl) : 0; -#else - const int res = m_pRcvBuffer->readMsg(data, len, (w_mctrl), seqdistance); -#endif leaveCS(m_RcvBufferLock); HLOGC(arlog.Debug, log << CONID() << "AFTER readMsg: (NON-BLOCKING) result=" << res); @@ -6990,13 +6976,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ do { -#if ENABLE_NEW_RCVBUFFER if (stillConnected() && !timeout && !m_pRcvBuffer->isRcvDataReady(steady_clock::now())) -#else - steady_clock::time_point tstime SRT_ATR_UNUSED; - int32_t seqno; - if (stillConnected() && !timeout && !m_pRcvBuffer->isRcvDataReady((tstime), (seqno), seqdistance)) -#endif { /* Kick TsbPd thread to schedule next wakeup (if running) */ if (m_bTsbPd) @@ -7052,11 +7032,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ */ enterCS(m_RcvBufferLock); -#if ENABLE_NEW_RCVBUFFER res = m_pRcvBuffer->readMessage((data), len, &w_mctrl); -#else - res = m_pRcvBuffer->readMsg((data), len, (w_mctrl), seqdistance); -#endif leaveCS(m_RcvBufferLock); HLOGC(arlog.Debug, log << CONID() << "AFTER readMsg: (BLOCKING) result=" << res); @@ -7128,7 +7104,8 @@ int64_t srt::CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int blo if (!m_pCryptoControl || !m_pCryptoControl->isSndEncryptionOK()) { LOGC(aslog.Error, - log << "Encryption is required, but the peer did not supply correct credentials. Sending rejected."); + log << CONID() + << "Encryption is required, but the peer did not supply correct credentials. Sending rejected."); throw CUDTException(MJ_SETUP, MN_SECURITY, 0); } @@ -7137,7 +7114,7 @@ int64_t srt::CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int blo if (m_pSndBuffer->getCurrBufSize() == 0) { // delay the EXP timer to avoid mis-fired timeout - // XXX Lock ??? ScopedLock ack_lock(m_RecvAckLock); + ScopedLock ack_lock(m_RecvAckLock); m_tsLastRspAckTime = steady_clock::now(); m_iReXmitCount = 1; } @@ -7254,7 +7231,8 @@ int64_t srt::CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int blo if (isOPT_TsbPd()) { - LOGC(arlog.Error, log << "Reading from file is incompatible with TSBPD mode and would cause a deadlock\n"); + LOGC(arlog.Error, + log << CONID() << "Reading from file is incompatible with TSBPD mode and would cause a deadlock"); throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0); } @@ -7403,7 +7381,7 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->byteRcvLoss = m_stats.rcvr.lost.trace.bytesWithHdr(); perf->pktSndDrop = m_stats.sndr.dropped.trace.count(); - perf->pktRcvDrop = m_stats.rcvr.dropped.trace.count() + m_stats.rcvr.undecrypted.trace.count(); + perf->pktRcvDrop = m_stats.rcvr.dropped.trace.count(); perf->byteSndDrop = m_stats.sndr.dropped.trace.bytesWithHdr(); perf->byteRcvDrop = m_stats.rcvr.dropped.trace.bytesWithHdr(); perf->pktRcvUndecrypt = m_stats.rcvr.undecrypted.trace.count(); @@ -7434,10 +7412,10 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->byteRcvLossTotal = m_stats.rcvr.lost.total.bytesWithHdr(); perf->pktSndDropTotal = m_stats.sndr.dropped.total.count(); - perf->pktRcvDropTotal = m_stats.rcvr.dropped.total.count() + m_stats.rcvr.undecrypted.total.count(); + perf->pktRcvDropTotal = m_stats.rcvr.dropped.total.count(); // TODO: The payload is dropped. Probably header sizes should not be counted? perf->byteSndDropTotal = m_stats.sndr.dropped.total.bytesWithHdr(); - perf->byteRcvDropTotal = m_stats.rcvr.dropped.total.bytesWithHdr() + m_stats.rcvr.undecrypted.total.bytesWithHdr(); + perf->byteRcvDropTotal = m_stats.rcvr.dropped.total.bytesWithHdr(); perf->pktRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.count(); perf->byteRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.bytes(); @@ -7549,7 +7527,7 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) return false; } - HLOGC(rslog.Debug, log << "updateCC: EVENT:" << TransmissionEventStr(evt)); + HLOGC(rslog.Debug, log << CONID() << "updateCC: EVENT:" << TransmissionEventStr(evt)); if (evt == TEV_INIT) { @@ -7645,7 +7623,7 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) #endif } - HLOGC(rslog.Debug, log << "udpateCC: finished handling for EVENT:" << TransmissionEventStr(evt)); + HLOGC(rslog.Debug, log << CONID() << "udpateCC: finished handling for EVENT:" << TransmissionEventStr(evt)); return true; } @@ -7719,30 +7697,10 @@ void srt::CUDT::releaseSynch() leaveCS(m_RecvLock); } -// [[using locked(m_RcvBufferLock)]]; -void srt::CUDT::ackDataUpTo(int32_t ack) -{ - const int acksize SRT_ATR_UNUSED = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); - - HLOGC(xtlog.Debug, log << "ackDataUpTo: %" << m_iRcvLastSkipAck << " -> %" << ack - << " (" << acksize << " packets)"); - - m_iRcvLastAck = ack; - m_iRcvLastSkipAck = ack; -#if !ENABLE_NEW_RCVBUFFER - // NOTE: This is new towards UDT and prevents spurious - // wakeup of select/epoll functions when no new packets - // were signed off for extraction. - if (acksize > 0) - { - m_pRcvBuffer->ackData(acksize); - } -#endif -} - -#if ENABLE_BONDING && ENABLE_NEW_RCVBUFFER -void srt::CUDT::dropToGroupRecvBase() { +#if ENABLE_BONDING +void srt::CUDT::dropToGroupRecvBase() +{ int32_t group_recv_base = SRT_SEQNO_NONE; if (m_parent->m_GroupOf) { @@ -7766,8 +7724,8 @@ void srt::CUDT::dropToGroupRecvBase() { if (cnt > 0) { HLOGC(grlog.Debug, - log << "dropToGroupRecvBase: " << CONID() << " dropped " << cnt << " packets before ACK: group_recv_base=" - << group_recv_base << " m_iRcvLastSkipAck=" << m_iRcvLastSkipAck + log << CONID() << "dropToGroupRecvBase: dropped " << cnt << " packets before ACK: group_recv_base=" + << group_recv_base << " m_iRcvLastAck=" << m_iRcvLastAck << " m_iRcvCurrSeqNo=" << m_iRcvCurrSeqNo << " m_bTsbPd=" << m_bTsbPd); } } @@ -7783,7 +7741,6 @@ static void DebugAck(string hdr, int prev, int ack) return; } - prev = CSeqNo::incseq(prev); int diff = CSeqNo::seqoff(prev, ack); if (diff < 0) { @@ -7825,7 +7782,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement ctrlpkt.pack(pkttype, lparam); ctrlpkt.m_iID = m_PeerID; - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; @@ -7840,7 +7797,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp ctrlpkt.pack(pkttype, NULL, lossdata, bytes); ctrlpkt.m_iID = m_PeerID; - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); enterCS(m_StatsLock); m_stats.rcvr.sentNak.count(1); @@ -7861,7 +7818,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp { ctrlpkt.pack(pkttype, NULL, data, losslen * 4); ctrlpkt.m_iID = m_PeerID; - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); enterCS(m_StatsLock); m_stats.rcvr.sentNak.count(1); @@ -7891,7 +7848,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_CGWARNING: // 100 - Congestion Warning ctrlpkt.pack(pkttype); ctrlpkt.m_iID = m_PeerID; - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); m_tsLastWarningTime = steady_clock::now(); @@ -7900,14 +7857,14 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_KEEPALIVE: // 001 - Keep-alive ctrlpkt.pack(pkttype); ctrlpkt.m_iID = m_PeerID; - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; case UMSG_HANDSHAKE: // 000 - Handshake ctrlpkt.pack(pkttype, NULL, rparam, sizeof(CHandShake)); ctrlpkt.m_iID = m_PeerID; - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; @@ -7916,21 +7873,21 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp break; ctrlpkt.pack(pkttype); ctrlpkt.m_iID = m_PeerID; - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; case UMSG_DROPREQ: // 111 - Msg drop request ctrlpkt.pack(pkttype, lparam, rparam, 8); ctrlpkt.m_iID = m_PeerID; - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; case UMSG_PEERERROR: // 1000 - acknowledge the peer side a special error ctrlpkt.pack(pkttype, lparam); ctrlpkt.m_iID = m_PeerID; - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; @@ -7946,13 +7903,35 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp m_tsLastSndTime.store(steady_clock::now()); } +// [[using locked(m_RcvBufferLock)]] +bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason) +{ + { + ScopedLock losslock (m_RcvLossLock); + const int32_t seq = m_pRcvLossList->getFirstLostSeq(); + if (seq != SRT_SEQNO_NONE) + { + HLOGC(xtlog.Debug, log << "NONCONT-SEQUENCE: first loss %" << seq << " (loss len=" << + m_pRcvLossList->getLossLength() << ")"); + w_seq = seq; + w_log_reason = "first lost"; + return true; + } + } + + w_seq = CSeqNo::incseq(m_iRcvCurrSeqNo); + HLOGC(xtlog.Debug, log << "NONCONT-SEQUENCE: past-recv %" << w_seq); + w_log_reason = "expected next"; + + return true; +} + + int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { SRT_ASSERT(ctrlpkt.getMsgTimeStamp() != 0); - int32_t ack; // First unacknowledged packet seqnuence number (acknowledge up to ack). int nbsent = 0; int local_prevack = 0; - #if ENABLE_HEAVY_LOGGING struct SaveBack { @@ -7965,62 +7944,71 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) local_prevack = m_iDebugPrevLastAck; - string reason = "first lost"; // just for "a reason" of giving particular % for ACK #endif + string reason; // just for "a reason" of giving particular % for ACK -#if ENABLE_BONDING && ENABLE_NEW_RCVBUFFER +#if ENABLE_BONDING dropToGroupRecvBase(); #endif - { - // If there is no loss, the ACK is the current largest sequence number plus 1; - // Otherwise it is the smallest sequence number in the receiver loss list. - ScopedLock lock(m_RcvLossLock); - // TODO: Consider the Fresh Loss list as well!!! - ack = m_pRcvLossList->getFirstLostSeq(); - } - - // We don't need to check the length prematurely, - // if length is 0, this will return SRT_SEQNO_NONE. - // If so happened, simply use the latest received pkt + 1. - if (ack == SRT_SEQNO_NONE) - { - ack = CSeqNo::incseq(m_iRcvCurrSeqNo); - IF_HEAVY_LOGGING(reason = "expected next"); - } + // The TSBPD thread may change the first lost sequence record (TLPKTDROP). + // To avoid it the m_RcvBufferLock has to be acquired. + UniqueLock bufflock(m_RcvBufferLock); + // The full ACK should be sent to indicate there is now available space in the RCV buffer + // since the last full ACK. It should unblock the sender to proceed further. + const bool bNeedFullAck = (m_bBufferWasFull && getAvailRcvBufferSizeNoLock() > 0); + int32_t ack; // First unacknowledged packet sequence number (acknowledge up to ack). + if (!getFirstNoncontSequence((ack), (reason))) + return nbsent; - if (m_iRcvLastAckAck == ack) + if (m_iRcvLastAckAck == ack && !bNeedFullAck) { - HLOGC(xtlog.Debug, log << "sendCtrl(UMSG_ACK): last ACK %" << ack << "(" << reason << ") == last ACKACK"); - return nbsent; + HLOGC(xtlog.Debug, + log << CONID() << "sendCtrl(UMSG_ACK): last ACK %" << ack << "(" << reason << ") == last ACKACK"); + return nbsent; } - // send out a lite ACK // to save time on buffer processing and bandwidth/AS measurement, a lite ACK only feeds back an ACK number - if (size == SEND_LITE_ACK) + if (size == SEND_LITE_ACK && !bNeedFullAck) { + bufflock.unlock(); ctrlpkt.pack(UMSG_ACK, NULL, &ack, size); ctrlpkt.m_iID = m_PeerID; - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); - DebugAck("sendCtrl(lite):" + CONID(), local_prevack, ack); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); + DebugAck(CONID() + "sendCtrl(lite): ", local_prevack, ack); return nbsent; } - // There are new received packets to acknowledge, update related information. - /* tsbpd thread may also call ackData when skipping packet so protect code */ - UniqueLock bufflock(m_RcvBufferLock); - // IF ack %> m_iRcvLastAck + // There are new received packets to acknowledge, update related information. if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) { - ackDataUpTo(ack); + // Sanity check if the "selected ACK" points to a sequence + // in the past for the buffer. This SHOULD NEVER HAPPEN because + // on drop the loss records should have been removed, and the last received + // sequence also can't be in the past towards the buffer. + + // NOTE: This problem has been observed when the packet sequence + // was incorrectly removed from the receiver loss list. This should + // then stay here as a condition in order to detect this problem, + // should it happen in the future. + if (CSeqNo::seqcmp(ack, m_pRcvBuffer->getStartSeqNo()) < 0) + { + LOGC(xtlog.Error, + log << CONID() << "sendCtrlAck: IPE: invalid ACK from %" << m_iRcvLastAck << " to %" << ack << " (" + << CSeqNo::seqoff(m_iRcvLastAck, ack) << " packets) buffer=%" << m_pRcvBuffer->getStartSeqNo()); + } + else + { + HLOGC(xtlog.Debug, + log << CONID() << "sendCtrlAck: %" << m_iRcvLastAck << " -> %" << ack << " (" + << CSeqNo::seqoff(m_iRcvLastAck, ack) << " packets)"); + } + + m_iRcvLastAck = ack; #if ENABLE_BONDING -#if ENABLE_NEW_RCVBUFFER const int32_t group_read_seq = m_pRcvBuffer->getFirstReadablePacketInfo(steady_clock::now()).seqno; -#else - const int32_t group_read_seq = CSeqNo::decseq(ack); -#endif #endif InvertedLock un_bufflock (m_RcvBufferLock); @@ -8057,14 +8045,13 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) } } #endif - IF_HEAVY_LOGGING(int32_t oldack = m_iRcvLastSkipAck); - // If TSBPD is enabled, then INSTEAD OF signaling m_RecvDataCond, // signal m_RcvTsbPdCond. This will kick in the tsbpd thread, which // will signal m_RecvDataCond when there's time to play for particular // data packet. - HLOGC(xtlog.Debug, log << "ACK: clip %" << oldack << "-%" << ack - << ", REVOKED " << CSeqNo::seqoff(ack, m_iRcvLastAck) << " from RCV buffer"); + HLOGC(xtlog.Debug, + log << CONID() << "ACK: clip %" << m_iRcvLastAck << "-%" << ack << ", REVOKED " + << CSeqNo::seqoff(ack, m_iRcvLastAck) << " from RCV buffer"); if (m_bTsbPd) { @@ -8079,11 +8066,9 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { CUniqueSync rdcc (m_RecvLock, m_RecvDataCond); -#if ENABLE_NEW_RCVBUFFER // Locks m_RcvBufferLock, which is unlocked above by InvertedLock un_bufflock. // Must check read-readiness under m_RecvLock to protect the epoll from concurrent changes in readBuffer() if (isRcvBufferReady()) -#endif { if (m_config.bSynRecving) { @@ -8122,21 +8107,21 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) CGlobEvent::triggerEvent(); } } - else if (ack == m_iRcvLastAck) + else if (ack == m_iRcvLastAck && !bNeedFullAck) { // If the ACK was just sent already AND elapsed time did not exceed RTT, if ((steady_clock::now() - m_tsLastAckTime) < (microseconds_from(m_iSRTT + 4 * m_iRTTVar))) { - HLOGC(xtlog.Debug, log << "sendCtrl(UMSG_ACK): ACK %" << ack << " just sent - too early to repeat"); + HLOGC(xtlog.Debug, + log << CONID() << "sendCtrl(UMSG_ACK): ACK %" << ack << " just sent - too early to repeat"); return nbsent; } } - else + else if (!bNeedFullAck) { // Not possible (m_iRcvCurrSeqNo+1 <% m_iRcvLastAck ?) - LOGC(xtlog.Error, log << "sendCtrl(UMSG_ACK): IPE: curr %" << ack - << " <% last %" << m_iRcvLastAck); + LOGC(xtlog.Error, log << CONID() << "sendCtrl(UMSG_ACK): IPE: curr %" << ack << " <% last %" << m_iRcvLastAck); return nbsent; } @@ -8144,7 +8129,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // [[using locked(m_RcvBufferLock)]]; // Send out the ACK only if has not been received by the sender before - if (CSeqNo::seqcmp(m_iRcvLastAck, m_iRcvLastAckAck) > 0) + if (CSeqNo::seqcmp(m_iRcvLastAck, m_iRcvLastAckAck) > 0 || bNeedFullAck) { // NOTE: The BSTATS feature turns on extra fields above size 6 // also known as ACKD_TOTAL_SIZE_VER100. @@ -8160,10 +8145,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) data[ACKD_RTT] = m_iSRTT; data[ACKD_RTTVAR] = m_iRTTVar; data[ACKD_BUFFERLEFT] = (int) getAvailRcvBufferSizeNoLock(); - // a minimum flow window of 2 is used, even if buffer is full, to break potential deadlock - if (data[ACKD_BUFFERLEFT] < 2) - data[ACKD_BUFFERLEFT] = 2; - + m_bBufferWasFull = data[ACKD_BUFFERLEFT] == 0; if (steady_clock::now() - m_tsLastAckTime > m_tdACKInterval) { int rcvRate; @@ -8197,8 +8179,8 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) ctrlpkt.m_iID = m_PeerID; setPacketTS(ctrlpkt, steady_clock::now()); - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); - DebugAck("sendCtrl(UMSG_ACK): " + CONID(), local_prevack, ack); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); + DebugAck(CONID() + "sendCtrl(UMSG_ACK): ", local_prevack, ack); m_ACKWindow.store(m_iAckSeqNo, m_iRcvLastAck); @@ -8208,7 +8190,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) } else { - HLOGC(xtlog.Debug, log << "sendCtrl(UMSG_ACK): " << CONID() << "ACK %" << m_iRcvLastAck + HLOGC(xtlog.Debug, log << CONID() << "sendCtrl(UMSG_ACK): " << "ACK %" << m_iRcvLastAck << " <=% ACKACK %" << m_iRcvLastAckAck << " - NOT SENDING ACK"); } @@ -8269,7 +8251,7 @@ void srt::CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) ScopedLock glock (uglobal().m_GlobControlLock); if (m_parent->m_GroupOf) { - HLOGC(inlog.Debug, log << "ACK: acking group sender buffer for #" << msgno_at_last_acked_seq); + HLOGC(inlog.Debug, log << CONID() << "ACK: acking group sender buffer for #" << msgno_at_last_acked_seq); // Guard access to m_iSndAckedMsgNo field // Note: This can't be done inside CUDTGroup::ackMessage @@ -8335,12 +8317,9 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ m_iFlowWindowSize = m_iFlowWindowSize - CSeqNo::seqoff(m_iSndLastAck, ackdata_seqno); m_iSndLastAck = ackdata_seqno; - // TODO: m_tsLastRspAckTime should be protected with m_RecvAckLock - // because the sendmsg2 may want to change it at the same time. m_tsLastRspAckTime = currtime; m_iReXmitCount = 1; // Reset re-transmit count since last ACK } - return; } @@ -8366,52 +8345,61 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // // Protect packet retransmission - enterCS(m_RecvAckLock); - - // Check the validation of the ack - if (CSeqNo::seqcmp(ackdata_seqno, CSeqNo::incseq(m_iSndCurrSeqNo)) > 0) { - leaveCS(m_RecvAckLock); - // this should not happen: attack or bug - LOGC(gglog.Error, - log << CONID() << "ATTACK/IPE: incoming ack seq " << ackdata_seqno << " exceeds current " + ScopedLock ack_lock(m_RecvAckLock); + + // Check the validation of the ack + if (CSeqNo::seqcmp(ackdata_seqno, CSeqNo::incseq(m_iSndCurrSeqNo)) > 0) + { + // this should not happen: attack or bug + LOGC(gglog.Error, + log << CONID() << "ATTACK/IPE: incoming ack seq " << ackdata_seqno << " exceeds current " << m_iSndCurrSeqNo << " by " << (CSeqNo::seqoff(m_iSndCurrSeqNo, ackdata_seqno) - 1) << "!"); - m_bBroken = true; - m_iBrokenCounter = 0; - return; - } + m_bBroken = true; + m_iBrokenCounter = 0; + return; + } if (CSeqNo::seqcmp(ackdata_seqno, m_iSndLastAck) >= 0) { + const int cwnd1 = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow)); + const bool bWasStuck = cwnd1<= getFlightSpan(); // Update Flow Window Size, must update before and together with m_iSndLastAck m_iFlowWindowSize = ackdata[ACKD_BUFFERLEFT]; m_iSndLastAck = ackdata_seqno; m_tsLastRspAckTime = currtime; m_iReXmitCount = 1; // Reset re-transmit count since last ACK - } - /* - * We must not ignore full ack received by peer - * if data has been artificially acked by late packet drop. - * Therefore, a distinct ack state is used for received Ack (iSndLastFullAck) - * and ack position in send buffer (m_iSndLastDataAck). - * Otherwise, when severe congestion causing packet drops (and m_iSndLastDataAck update) - * occures, we drop received acks (as duplicates) and do not update stats like RTT, - * which may go crazy and stay there, preventing proper stream recovery. - */ - - if (CSeqNo::seqoff(m_iSndLastFullAck, ackdata_seqno) <= 0) - { - // discard it if it is a repeated ACK - leaveCS(m_RecvAckLock); - return; + const int cwnd = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow)); + if (bWasStuck && cwnd > getFlightSpan()) + { + m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); + HLOGC(gglog.Debug, + log << CONID() << "processCtrlAck: could reschedule SND. iFlowWindowSize " << m_iFlowWindowSize + << " SPAN " << getFlightSpan() << " ackdataseqno %" << ackdata_seqno); + } } - m_iSndLastFullAck = ackdata_seqno; - // + /* + * We must not ignore full ack received by peer + * if data has been artificially acked by late packet drop. + * Therefore, a distinct ack state is used for received Ack (iSndLastFullAck) + * and ack position in send buffer (m_iSndLastDataAck). + * Otherwise, when severe congestion causing packet drops (and m_iSndLastDataAck update) + * occures, we drop received acks (as duplicates) and do not update stats like RTT, + * which may go crazy and stay there, preventing proper stream recovery. + */ + + if (CSeqNo::seqoff(m_iSndLastFullAck, ackdata_seqno) <= 0) + { + // discard it if it is a repeated ACK + return; + } + m_iSndLastFullAck = ackdata_seqno; + } + // // END of the new code with TLPKTDROP // - leaveCS(m_RecvAckLock); #if ENABLE_BONDING if (m_parent->m_GroupOf) { @@ -8547,7 +8535,6 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // cudt->deliveryRate() instead. } - checkSndTimers(REGEN_KM); updateCC(TEV_ACK, EventVariant(ackdata_seqno)); enterCS(m_StatsLock); @@ -8574,9 +8561,9 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr } LOGC(inlog.Error, - log << CONID() << "IPE: ACK record not found, can't estimate RTT " - << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo - << ", RTT (EWMA): " << m_iSRTT << ")"); + log << CONID() << "ACK record not found, can't estimate RTT " + << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo + << ", RTT (EWMA): " << m_iSRTT << ")"); return; } @@ -8652,87 +8639,87 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) // This variable is used in "normal" logs, so it may cause a warning // when logging is forcefully off. - int32_t wrong_loss SRT_ATR_UNUSED = CSeqNo::m_iMaxSeqNo; + int32_t wrong_loss SRT_ATR_UNUSED = SRT_SEQNO_NONE; // protect packet retransmission { ScopedLock ack_lock(m_RecvAckLock); // decode loss list message and insert loss into the sender loss list - for (int i = 0, n = (int)(ctrlpkt.getLength() / 4); i < n; ++i) + for (int i = 0, n = (int)losslist_len; i < n; ++i) { + // IF the loss is a range if (IsSet(losslist[i], LOSSDATA_SEQNO_RANGE_FIRST)) { - // Then it's this is a specification with HI in a consecutive cell. + // Then it's this is a specification with HI in a consecutive cell. const int32_t losslist_lo = SEQNO_VALUE::unwrap(losslist[i]); const int32_t losslist_hi = losslist[i + 1]; - // specification means that the consecutive cell has been already interpreted. + // specification means that the consecutive cell has been already interpreted. ++i; - HLOGF(inlog.Debug, - "%sreceived UMSG_LOSSREPORT: %d-%d (%d packets)...", CONID().c_str(), - losslist_lo, - losslist_hi, - CSeqNo::seqoff(losslist_lo, losslist_hi) + 1); + HLOGC(inlog.Debug, log << CONID() << "received UMSG_LOSSREPORT: " + << losslist_lo << "-" << losslist_hi + << " (" << CSeqNo::seqlen(losslist_lo, losslist_hi) << " packets)..."); if ((CSeqNo::seqcmp(losslist_lo, losslist_hi) > 0) || (CSeqNo::seqcmp(losslist_hi, m_iSndCurrSeqNo) > 0)) { + // LO must not be greater than HI. + // HI must not be greater than the most recent sent seq. LOGC(inlog.Warn, log << CONID() << "rcv LOSSREPORT rng " << losslist_lo << " - " << losslist_hi << " with last sent " << m_iSndCurrSeqNo << " - DISCARDING"); - // seq_a must not be greater than seq_b; seq_b must not be greater than the most recent sent seq secure = false; wrong_loss = losslist_hi; break; } int num = 0; - // IF losslist_lo %>= m_iSndLastAck + // IF losslist_lo %>= m_iSndLastAck if (CSeqNo::seqcmp(losslist_lo, m_iSndLastAck) >= 0) { HLOGC(inlog.Debug, log << CONID() << "LOSSREPORT: adding " << losslist_lo << " - " << losslist_hi << " to loss list"); num = m_pSndLossList->insert(losslist_lo, losslist_hi); } - // ELSE IF losslist_hi %>= m_iSndLastAck - else if (CSeqNo::seqcmp(losslist_hi, m_iSndLastAck) >= 0) - { - // This should be theoretically impossible because this would mean - // that the received packet loss report informs about the loss that predates - // the ACK sequence. - // However, this can happen if the packet reordering has caused the earlier sent - // LOSSREPORT will be delivered after later sent ACK. Whatever, ACK should be - // more important, so simply drop the part that predates ACK. - HLOGC(inlog.Debug, log << CONID() << "LOSSREPORT: adding " - << m_iSndLastAck << "[ACK] - " << losslist_hi << " to loss list"); - num = m_pSndLossList->insert(m_iSndLastAck, losslist_hi); - } + // ELSE losslist_lo %< m_iSndLastAck else { - // This should be treated as IPE, but this may happen in one situtation: + // This should be theoretically impossible because this would mean that + // the received packet loss report informs about the loss that predates + // the ACK sequence. + // However, this can happen in these situations: + // - if the packet reordering has caused the earlier sent LOSSREPORT will be + // delivered after later sent ACK. Whatever, ACK should be more important, + // so simply drop the part that predates ACK. // - redundancy second link (ISN was screwed up initially, but late towards last sent) // - initial DROPREQ was lost // This just causes repeating DROPREQ, as when the receiver continues sending // LOSSREPORT, it's probably UNAWARE OF THE SITUATION. - // + int32_t dropreq_hi = losslist_hi; + IF_HEAVY_LOGGING(const char* drop_type = "completely"); + + // IF losslist_hi %>= m_iSndLastAck + if (CSeqNo::seqcmp(losslist_hi, m_iSndLastAck) >= 0) + { + HLOGC(inlog.Debug, log << CONID() << "LOSSREPORT: adding " + << m_iSndLastAck << "[ACK] - " << losslist_hi << " to loss list"); + num = m_pSndLossList->insert(m_iSndLastAck, losslist_hi); + dropreq_hi = CSeqNo::decseq(m_iSndLastAck); + IF_HEAVY_LOGGING(drop_type = "partially"); + } + + // In distinction to losslist, DROPREQ has always just one range, + // and the data are , with no range bit. + int32_t seqpair[2] = { losslist_lo, dropreq_hi }; + const int32_t no_msgno = 0; // We don't know. + // When this DROPREQ gets lost in UDP again, the receiver will do one of these: // - repeatedly send LOSSREPORT (as per NAKREPORT), so this will happen again // - finally give up rexmit request as per TLPKTDROP (DROPREQ should make // TSBPD wake up should it still wait for new packets to get ACK-ed) - - HLOGC(inlog.Debug, log << CONID() << "LOSSREPORT: IGNORED with SndLastAck=%" - << m_iSndLastAck << ": %" << losslist_lo << "-" << losslist_hi - << " - sending DROPREQ (IPE or DROPREQ lost with ISN screw)"); - - // This means that the loss touches upon a range that wasn't ever sent. - // Normally this should never happen, but this might be a case when the - // ISN FIX for redundant connection was missed. - - // In distinction to losslist, DROPREQ has always a range - // always just one range, and the data are , with no range bit. - int32_t seqpair[2] = { losslist_lo, losslist_hi }; - const int32_t no_msgno = 0; // We don't know - this wasn't ever sent - + HLOGC(inlog.Debug, + log << CONID() << "LOSSREPORT: " << drop_type << " IGNORED with SndLastAck=%" << m_iSndLastAck + << ": %" << losslist_lo << "-" << dropreq_hi << " - sending DROPREQ"); sendCtrl(UMSG_DROPREQ, &no_msgno, seqpair, sizeof(seqpair)); } @@ -8740,25 +8727,42 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) m_stats.sndr.lost.count(num); leaveCS(m_StatsLock); } - else if (CSeqNo::seqcmp(losslist[i], m_iSndLastAck) >= 0) + // ELSE the loss is a single seq + else { - if (CSeqNo::seqcmp(losslist[i], m_iSndCurrSeqNo) > 0) + // IF loss_seq %>= m_iSndLastAck + if (CSeqNo::seqcmp(losslist[i], m_iSndLastAck) >= 0) { - LOGC(inlog.Warn, log << CONID() << "rcv LOSSREPORT pkt %" << losslist[i] - << " with last sent %" << m_iSndCurrSeqNo << " - DISCARDING"); - // seq_a must not be greater than the most recent sent seq - secure = false; - wrong_loss = losslist[i]; - break; - } + if (CSeqNo::seqcmp(losslist[i], m_iSndCurrSeqNo) > 0) + { + LOGC(inlog.Warn, log << CONID() << "rcv LOSSREPORT pkt %" << losslist[i] + << " with last sent %" << m_iSndCurrSeqNo << " - DISCARDING"); + // loss_seq must not be greater than the most recent sent seq + secure = false; + wrong_loss = losslist[i]; + break; + } - HLOGC(inlog.Debug, log << CONID() << "rcv LOSSREPORT: %" - << losslist[i] << " (1 packet)"); - const int num = m_pSndLossList->insert(losslist[i], losslist[i]); + HLOGC(inlog.Debug, + log << CONID() << "LOSSREPORT: adding %" << losslist[i] << " (1 packet) to loss list"); + const int num = m_pSndLossList->insert(losslist[i], losslist[i]); - enterCS(m_StatsLock); - m_stats.sndr.lost.count(num); - leaveCS(m_StatsLock); + enterCS(m_StatsLock); + m_stats.sndr.lost.count(num); + leaveCS(m_StatsLock); + } + // ELSE loss_seq %< m_iSndLastAck + else + { + // In distinction to losslist, DROPREQ has always just one range, + // and the data are , with no range bit. + int32_t seqpair[2] = { losslist[i], losslist[i] }; + const int32_t no_msgno = 0; // We don't know. + HLOGC(inlog.Debug, + log << CONID() << "LOSSREPORT: IGNORED with SndLastAck=%" << m_iSndLastAck << ": %" << losslist[i] + << " - sending DROPREQ"); + sendCtrl(UMSG_DROPREQ, &no_msgno, seqpair, sizeof(seqpair)); + } } } } @@ -8882,7 +8886,7 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) { response.m_iID = m_PeerID; setPacketTS(response, steady_clock::now()); - const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response); + const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response, m_SourceAddr); if (nbsent) { m_tsLastSndTime.store(steady_clock::now()); @@ -8909,8 +8913,7 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) { const bool using_rexmit_flag = m_bPeerRexmitFlag; ScopedLock rblock(m_RcvBufferLock); -#if ENABLE_NEW_RCVBUFFER - const int iDropCnt = m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); + const int iDropCnt = m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag), CRcvBuffer::KEEP_EXISTING); if (iDropCnt > 0) { @@ -8924,9 +8927,6 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); leaveCS(m_StatsLock); } -#else - m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); -#endif } // When the drop request was received, it means that there are // packets for which there will never be ACK sent; if the TSBPD thread @@ -9081,29 +9081,21 @@ void srt::CUDT::updateSrtRcvSettings() ScopedLock lock(m_RecvLock); // NOTE: remember to also update synchronizeWithGroup() if more settings are updated here. -#if ENABLE_NEW_RCVBUFFER m_pRcvBuffer->setPeerRexmitFlag(m_bPeerRexmitFlag); -#endif // XXX m_bGroupTsbPd is ignored with SRT_ENABLE_APP_READER if (m_bTsbPd || m_bGroupTsbPd) { -#if ENABLE_NEW_RCVBUFFER m_pRcvBuffer->setTsbPdMode(m_tsRcvPeerStartTime, false, milliseconds_from(m_iTsbPdDelay_ms)); -#else - m_pRcvBuffer->setRcvTsbPdMode(m_tsRcvPeerStartTime, milliseconds_from(m_iTsbPdDelay_ms)); -#endif - HLOGF(cnlog.Debug, - "AFTER HS: Set Rcv TsbPd mode%s: delay=%u.%03us RCV START: %s", - (m_bGroupTsbPd ? " (AS GROUP MEMBER)" : ""), - m_iTsbPdDelay_ms / 1000, - m_iTsbPdDelay_ms % 1000, - FormatTime(m_tsRcvPeerStartTime).c_str()); + HLOGC(cnlog.Debug, log << "AFTER HS: Set Rcv TsbPd mode" + << (m_bGroupTsbPd ? " (AS GROUP MEMBER)" : "") + << ": delay=" << (m_iTsbPdDelay_ms / 1000) << "." << (m_iTsbPdDelay_ms % 1000) + << "s RCV START: " << FormatTime(m_tsRcvPeerStartTime).c_str()); } else { - HLOGC(cnlog.Debug, log << "AFTER HS: Rcv TsbPd mode not set"); + HLOGC(cnlog.Debug, log << CONID() << "AFTER HS: Rcv TsbPd mode not set"); } } @@ -9118,21 +9110,20 @@ void srt::CUDT::updateSrtSndSettings() * For sender to apply Too-Late Packet Drop * option (m_bTLPktDrop) must be enabled and receiving peer shall support it */ - HLOGF(cnlog.Debug, - "AFTER HS: Set Snd TsbPd mode %s TLPktDrop: delay=%d.%03ds START TIME: %s", - m_bPeerTLPktDrop ? "with" : "without", - m_iPeerTsbPdDelay_ms/1000, m_iPeerTsbPdDelay_ms%1000, - FormatTime(m_stats.tsStartTime).c_str()); + HLOGC(cnlog.Debug, log << "AFTER HS: Set Snd TsbPd mode " + << (m_bPeerTLPktDrop ? "with" : "without") + << " TLPktDrop: delay=" << (m_iPeerTsbPdDelay_ms/1000) << "." << (m_iPeerTsbPdDelay_ms%1000) + << "s START TIME: " << FormatTime(m_stats.tsStartTime).c_str()); } else { - HLOGC(cnlog.Debug, log << "AFTER HS: Snd TsbPd mode not set"); + HLOGC(cnlog.Debug, log << CONID() << "AFTER HS: Snd TsbPd mode not set"); } } void srt::CUDT::updateAfterSrtHandshake(int hsv) { - HLOGC(cnlog.Debug, log << "updateAfterSrtHandshake: HS version " << hsv); + HLOGC(cnlog.Debug, log << CONID() << "updateAfterSrtHandshake: HS version " << hsv); // This is blocked from being run in the "app reader" version because here // every socket does its TsbPd independently, just the sequence screwup is // done and the application reader sorts out packets by sequence numbers, @@ -9163,9 +9154,9 @@ void srt::CUDT::updateAfterSrtHandshake(int hsv) const char* grpspec = ""; #endif - HLOGC(cnlog.Debug, log << "updateAfterSrtHandshake: version=" - << m_ConnRes.m_iVersion << " side=" << hs_side[m_SrtHsSide] - << grpspec); + HLOGC(cnlog.Debug, + log << CONID() << "updateAfterSrtHandshake: version=" << m_ConnRes.m_iVersion + << " side=" << hs_side[m_SrtHsSide] << grpspec); #endif if (hsv > HS_VERSION_UDT4) @@ -9185,7 +9176,7 @@ void srt::CUDT::updateAfterSrtHandshake(int hsv) } } -int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime) +int srt::CUDT::packLostData(CPacket& w_packet) { // protect m_iSndLastDataAck from updating by ACK processing UniqueLock ackguard(m_RecvAckLock); @@ -9203,9 +9194,10 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi // XXX Likely that this will never be executed because if the upper // sequence is not in the sender buffer, then most likely the loss // was completely ignored. - LOGC(qrlog.Error, log << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(m_iSeqNo " - << w_packet.m_iSeqNo << ", m_iSndLastDataAck " << m_iSndLastDataAck - << ")=" << offset << ". Continue"); + LOGC(qrlog.Error, + log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(m_iSeqNo " + << w_packet.m_iSeqNo << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset + << ". Continue"); // No matter whether this is right or not (maybe the attack case should be // considered, and some LOSSREPORT flood prevention), send the drop request @@ -9216,9 +9208,10 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi }; w_packet.m_iMsgNo = 0; // Message number is not known, setting all 32 bits to 0. - HLOGC(qrlog.Debug, log << "PEER reported LOSS not from the sending buffer - requesting DROP: " - << "msg=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" - << seqpair[0] << " - " << seqpair[1] << "(" << (-offset) << " packets)"); + HLOGC(qrlog.Debug, + log << CONID() << "PEER reported LOSS not from the sending buffer - requesting DROP: msg=" + << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" + << (-offset) << " packets)"); sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); continue; @@ -9238,8 +9231,8 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi } int msglen; - - const int payload = m_pSndBuffer->readData(offset, (w_packet), (w_origintime), (msglen)); + steady_clock::time_point tsOrigin; + const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (msglen)); if (payload == -1) { int32_t seqpair[2]; @@ -9248,9 +9241,9 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi seqpair[1] = CSeqNo::incseq(seqpair[0], msglen - 1); HLOGC(qrlog.Debug, - log << "loss-reported packets expired in SndBuf - requesting DROP: " - << "msgno=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen - << " SEQ:" << seqpair[0] << " - " << seqpair[1]); + log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: msgno=" + << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen << " SEQ:" << seqpair[0] << " - " + << seqpair[1]); sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); // skip all dropped packets @@ -9261,6 +9254,13 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi else if (payload == 0) continue; + // The packet has been ecrypted, thus the authentication tag is expected to be stored + // in the SND buffer as well right after the payload. + if (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM) + { + w_packet.setLength(w_packet.getLength() + HAICRYPT_AUTHTAG_MAX); + } + // At this point we no longer need the ACK lock, // because we are going to return from the function. // Therefore unlocking in order not to block other threads. @@ -9278,6 +9278,11 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi { w_packet.m_iMsgNo |= PACKET_SND_REXMIT; } + setDataPacketTS(w_packet, tsOrigin); + +#ifdef ENABLE_MAXREXMITBW + m_SndRexmitRate.addSample(time_now, 1, w_packet.getLength()); +#endif return payload; } @@ -9371,6 +9376,42 @@ class snd_logger snd_logger g_snd_logger; #endif // SRT_DEBUG_TRACE_SND +void srt::CUDT::setPacketTS(CPacket& p, const time_point& ts) +{ + enterCS(m_StatsLock); + const time_point tsStart = m_stats.tsStartTime; + leaveCS(m_StatsLock); + p.m_iTimeStamp = makeTS(ts, tsStart); +} + +void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) +{ + enterCS(m_StatsLock); + const time_point tsStart = m_stats.tsStartTime; + leaveCS(m_StatsLock); + + if (!m_bPeerTsbPd) + { + // If TSBPD is disabled, use the current time as the source (timestamp using the sending time). + p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart); + return; + } + + // TODO: Might be better for performance to ensure this condition is always false, and just use SRT_ASSERT here. + if (ts < tsStart) + { + p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart); + LOGC(qslog.Warn, + log << CONID() << "setPacketTS: reference time=" << FormatTime(ts) + << " is in the past towards start time=" << FormatTime(tsStart) + << " - setting NOW as reference time for the data packet"); + return; + } + + // Use the provided source time for the timestamp. + p.m_iTimeStamp = makeTS(ts, tsStart); +} + bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) { // Prioritization of original packets only applies to Live CC. @@ -9404,29 +9445,39 @@ bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) return false; } +#ifdef ENABLE_MAXREXMITBW + m_SndRexmitRate.addSample(tnow, 0, 0); // Update the estimation. + const int64_t iRexmitRateBps = m_SndRexmitRate.getRate(); + const int64_t iRexmitRateLimitBps = m_config.llMaxRexmitBW; + if (iRexmitRateLimitBps >= 0 && iRexmitRateBps > iRexmitRateLimitBps) + { + // Too many retransmissions, so don't send anything. + // TODO: When to wake up next time? + return false; + } +#endif + #if SRT_DEBUG_TRACE_SND g_snd_logger.state.canRexmit = true; #endif return true; } -std::pair srt::CUDT::packData(CPacket& w_packet) +bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime, sockaddr_any& w_src_addr) { int payload = 0; bool probe = false; - steady_clock::time_point origintime; bool new_packet_packed = false; - bool filter_ctl_pkt = false; const steady_clock::time_point enter_time = steady_clock::now(); + w_nexttime = enter_time; + if (!is_zero(m_tsNextSendTime) && enter_time > m_tsNextSendTime) { m_tdSendTimeDiff = m_tdSendTimeDiff.load() + (enter_time - m_tsNextSendTime); } - string reason = "reXmit"; - ScopedLock connectguard(m_ConnectionLock); // If a closing action is done simultaneously, then // m_bOpened should already be false, and it's set @@ -9436,23 +9487,23 @@ std::pair srt::CUDT::packData(CPacket& w_packet) // start the dissolving process, this process will // not be started until this function is finished. if (!m_bOpened) - return std::make_pair(false, enter_time); + return false; payload = isRetransmissionAllowed(enter_time) - ? packLostData((w_packet), (origintime)) + ? packLostData((w_packet)) : 0; + IF_HEAVY_LOGGING(const char* reason); // The source of the data packet (normal/rexmit/filter) if (payload > 0) { - reason = "reXmit"; + IF_HEAVY_LOGGING(reason = "reXmit"); } else if (m_PacketFilter && m_PacketFilter.packControlPacket(m_iSndCurrSeqNo, m_pCryptoControl->getSndCryptoFlags(), (w_packet))) { - HLOGC(qslog.Debug, log << "filter: filter/CTL packet ready - packing instead of data."); + HLOGC(qslog.Debug, log << CONID() << "filter: filter/CTL packet ready - packing instead of data."); payload = (int) w_packet.getLength(); - reason = "filter"; - filter_ctl_pkt = true; // Mark that this packet ALREADY HAS timestamp field and it should not be set + IF_HEAVY_LOGGING(reason = "filter"); // Stats ScopedLock lg(m_StatsLock); @@ -9460,11 +9511,11 @@ std::pair srt::CUDT::packData(CPacket& w_packet) } else { - if (!packUniqueData(w_packet, origintime)) + if (!packUniqueData(w_packet)) { m_tsNextSendTime = steady_clock::time_point(); m_tdSendTimeDiff = steady_clock::duration(); - return std::make_pair(false, enter_time); + return false; } new_packet_packed = true; @@ -9473,49 +9524,14 @@ std::pair srt::CUDT::packData(CPacket& w_packet) probe = true; payload = (int) w_packet.getLength(); - reason = "normal"; + IF_HEAVY_LOGGING(reason = "normal"); } - // Normally packet.m_iTimeStamp field is set exactly here, - // usually as taken from m_stats.tsStartTime and current time, unless live - // mode in which case it is based on 'origintime' as set during scheduling. - // In case when this is a filter control packet, the m_iTimeStamp field already - // contains the exactly needed value, and it's a timestamp clip, not a real - // timestamp. - if (!filter_ctl_pkt) - { - if (m_bPeerTsbPd) - { - /* - * When timestamp is carried over in this sending stream from a received stream, - * it may be older than the session start time causing a negative packet time - * that may block the receiver's Timestamp-based Packet Delivery. - * XXX Isn't it then better to not decrease it by m_stats.tsStartTime? As long as it - * doesn't screw up the start time on the other side. - */ - if (origintime >= m_stats.tsStartTime) - { - setPacketTS(w_packet, origintime); - } - else - { - setPacketTS(w_packet, steady_clock::now()); - LOGC(qslog.Warn, log << "packData: reference time=" << FormatTime(origintime) - << " is in the past towards start time=" << FormatTime(m_stats.tsStartTime) - << " - setting NOW as reference time for the data packet"); - } - } - else - { - setPacketTS(w_packet, steady_clock::now()); - } - } - - w_packet.m_iID = m_PeerID; + w_packet.m_iID = m_PeerID; // Set the destination SRT socket ID. if (new_packet_packed && m_PacketFilter) { - HLOGC(qslog.Debug, log << "filter: Feeding packet for source clip"); + HLOGC(qslog.Debug, log << CONID() << "filter: Feeding packet for source clip"); m_PacketFilter.feedSource((w_packet)); } @@ -9566,7 +9582,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) if (sendbrw >= sendint) { - // Send immidiately + // Send immediately m_tsNextSendTime = enter_time; // ATOMIC NOTE: this is the only thread that @@ -9580,19 +9596,23 @@ std::pair srt::CUDT::packData(CPacket& w_packet) } #endif } + HLOGC(qslog.Debug, log << "packData: Setting source address: " << m_SourceAddr.str()); + w_src_addr = m_SourceAddr; + w_nexttime = m_tsNextSendTime; - return std::make_pair(payload >= 0, m_tsNextSendTime); + return payload >= 0; // XXX shouldn't be > 0 ? == 0 is only when buffer range exceeded. } -bool srt::CUDT::packUniqueData(CPacket& w_packet, time_point& w_origintime) +bool srt::CUDT::packUniqueData(CPacket& w_packet) { // Check the congestion/flow window limit const int cwnd = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow)); const int flightspan = getFlightSpan(); if (cwnd <= flightspan) { - HLOGC(qslog.Debug, log << "packData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_dCongestionWindow - << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << flightspan); + HLOGC(qslog.Debug, + log << CONID() << "packUniqueData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_dCongestionWindow + << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << flightspan); return false; } @@ -9603,15 +9623,19 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet, time_point& w_origintime) // isn't a useless redundant state copy. If it is, then taking the flags here can be removed. const int kflg = m_pCryptoControl->getSndCryptoFlags(); int pktskipseqno = 0; - const int pld_size = m_pSndBuffer->readData((w_packet), (w_origintime), kflg, (pktskipseqno)); + time_point tsOrigin; + const int pld_size = m_pSndBuffer->readData((w_packet), (tsOrigin), kflg, (pktskipseqno)); if (pktskipseqno) { // Some packets were skipped due to TTL expiry. m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo, pktskipseqno); + HLOGC(qslog.Debug, log << "packUniqueData: reading skipped " << pktskipseqno << " seq up to %" << m_iSndCurrSeqNo + << " due to TTL expiry"); } if (pld_size == 0) { + HLOGC(qslog.Debug, log << "packUniqueData: nothing extracted from the buffer"); return false; } @@ -9637,7 +9661,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet, time_point& w_origintime) // no ACK to be awaited. We can screw up all the variables that are // initialized from ISN just after connection. LOGC(qslog.Note, - log << CONID() << "packData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo + log << CONID() << "packUniqueData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " for the first packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } @@ -9645,7 +9669,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet, time_point& w_origintime) { // There will be a serious data discrepancy between the agent and the peer. LOGC(qslog.Error, - log << CONID() << "IPE: packData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo + log << CONID() << "IPE: packUniqueData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " in the middle of transition: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } @@ -9658,7 +9682,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet, time_point& w_origintime) seqpair[1] = CSeqNo::decseq(w_packet.m_iSeqNo); const int32_t no_msgno = 0; LOGC(qslog.Debug, - log << CONID() << "packData: Sending DROPREQ: SEQ: " << seqpair[0] << " - " << seqpair[1] << " (" + log << CONID() << "packUniqueData: Sending DROPREQ: SEQ: " << seqpair[0] << " - " << seqpair[1] << " (" << packetspan << " packets)"); sendCtrl(UMSG_DROPREQ, &no_msgno, seqpair, sizeof(seqpair)); // In case when this message is lost, the peer will still get the @@ -9687,7 +9711,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet, time_point& w_origintime) #endif { HLOGC(qslog.Debug, - log << CONID() << "packData: Applying EXTRACTION sequence " << m_iSndCurrSeqNo + log << CONID() << "packUniqueData: Applying EXTRACTION sequence " << m_iSndCurrSeqNo << " over SCHEDULING sequence " << w_packet.m_iSeqNo << " for socket not in group:" << " DIFF=" << CSeqNo::seqcmp(m_iSndCurrSeqNo, w_packet.m_iSeqNo) << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); @@ -9695,7 +9719,10 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet, time_point& w_origintime) w_packet.m_iSeqNo = m_iSndCurrSeqNo; } - // Encrypt if 1st time this packet is sent and crypto is enabled + // Set missing fields before encrypting the packet, because those fields might be used for encryption. + w_packet.m_iID = m_PeerID; // Destination SRT Socket ID + setDataPacketTS(w_packet, tsOrigin); + if (kflg != EK_NOENC) { // Note that the packet header must have a valid seqno set, as it is used as a counter for encryption. @@ -9705,9 +9732,11 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet, time_point& w_origintime) { // Encryption failed //>>Add stats for crypto failure - LOGC(qslog.Warn, log << "ENCRYPT FAILED - packet won't be sent, size=" << pld_size); + LOGC(qslog.Warn, log << CONID() << "ENCRYPT FAILED - packet won't be sent, size=" << pld_size); return false; } + + checkSndKMRefresh(); } #if SRT_DEBUG_TRACE_SND @@ -9748,8 +9777,6 @@ void srt::CUDT::processClose() void srt::CUDT::sendLossReport(const std::vector > &loss_seqs) { - typedef vector > loss_seqs_t; - vector seqbuffer; seqbuffer.reserve(2 * loss_seqs.size()); // pessimistic for (loss_seqs_t::const_iterator i = loss_seqs.begin(); i != loss_seqs.end(); ++i) @@ -9757,17 +9784,14 @@ void srt::CUDT::sendLossReport(const std::vector > & if (i->first == i->second) { seqbuffer.push_back(i->first); - HLOGF(qrlog.Debug, "lost packet %d: sending LOSSREPORT", i->first); + HLOGC(qrlog.Debug, log << "lost packet " << i->first << ": sending LOSSREPORT"); } else { seqbuffer.push_back(i->first | LOSSDATA_SEQNO_RANGE_FIRST); seqbuffer.push_back(i->second); - HLOGF(qrlog.Debug, - "lost packets %d-%d (%d packets): sending LOSSREPORT", - i->first, - i->second, - 1 + CSeqNo::seqcmp(i->second, i->first)); + HLOGC(qrlog.Debug, log << "lost packets " << i->first << "-" << i->second + << " (" << (1 + CSeqNo::seqcmp(i->second, i->first)) << " packets): sending LOSSREPORT"); } } @@ -9781,7 +9805,7 @@ void srt::CUDT::sendLossReport(const std::vector > & bool srt::CUDT::overrideSndSeqNo(int32_t seq) { // This function is intended to be called from the socket - // group managmenet functions to synchronize the sequnece in + // group management functions to synchronize the sequnece in // all sockes in the bonding group. THIS sequence given // here is the sequence TO BE STAMPED AT THE EXACTLY NEXT // sent payload. Therefore, screw up the ISN to exactly this @@ -9823,26 +9847,16 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) // the latter is ahead with the number of packets already scheduled, but // not yet sent. - HLOGC(gslog.Debug, log << CONID() << "overrideSndSeqNo: sched-seq=" << m_iSndNextSeqNo << " send-seq=" << m_iSndCurrSeqNo - << " (unchanged)" - ); + HLOGC(gslog.Debug, + log << CONID() << "overrideSndSeqNo: sched-seq=" << m_iSndNextSeqNo << " send-seq=" << m_iSndCurrSeqNo + << " (unchanged)"); return true; } -int srt::CUDT::processData(CUnit* in_unit) +int srt::CUDT::checkLazySpawnTsbPdThread() { - if (m_bClosing) - return -1; - - CPacket &packet = in_unit->m_Packet; - - // Just heard from the peer, reset the expiration count. - m_iEXPCount = 1; - m_tsLastRspTime.store(steady_clock::now()); - const bool need_tsbpd = m_bTsbPd || m_bGroupTsbPd; - // We are receiving data, start tsbpd thread if TsbPd is enabled if (need_tsbpd && !m_RcvTsbPdThread.joinable()) { ScopedLock lock(m_RcvTsbPdStartupLock); @@ -9854,14 +9868,10 @@ int srt::CUDT::processData(CUnit* in_unit) #if ENABLE_HEAVY_LOGGING std::ostringstream tns1, tns2; // Take the last 2 ciphers from the socket ID. - tns1 << m_SocketID; + tns1 << setfill('0') << setw(2) << m_SocketID; std::string s = tns1.str(); tns2 << "SRT:TsbPd:@" << s.substr(s.size()-2, 2); - - const string& tn = tns2.str(); - - ThreadName tnkeep(tn); - const string& thname = tn; + const string thname = tns2.str(); #else const string thname = "SRT:TsbPd"; #endif @@ -9869,13 +9879,285 @@ int srt::CUDT::processData(CUnit* in_unit) return -1; } + return 0; +} + +CUDT::time_point srt::CUDT::getPktTsbPdTime(void*, const CPacket& packet) +{ + return m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp()); +} + +SRT_ATR_UNUSED static const char *const s_rexmitstat_str[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; + +// [[using locked(m_RcvBufferLock)]] +int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) +{ + bool excessive SRT_ATR_UNUSED = true; // stays true unless it was successfully added + + w_new_inserted = false; + const int32_t bufseq = m_pRcvBuffer->getStartSeqNo(); + + // Loop over all incoming packets that were filtered out. + // In case when there is no filter, there's just one packet in 'incoming', + // the one that came in the input of this function. + for (vector::const_iterator unitIt = incoming.begin(); unitIt != incoming.end(); ++unitIt) + { + CUnit * u = *unitIt; + CPacket &rpkt = u->m_Packet; + const int pktrexmitflag = m_bPeerRexmitFlag ? (rpkt.getRexmitFlag() ? 1 : 0) : 2; + const bool retransmitted = pktrexmitflag == 1; + + bool adding_successful = true; + + const int32_t bufidx = CSeqNo::seqoff(bufseq, rpkt.m_iSeqNo); + + IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); + + // bufidx < 0: the packet is in the past for the buffer + // seqno <% m_iRcvLastAck : the sequence may be within the buffer, + // but if so, it is in the acknowledged-but-not-retrieved area. + + // NOTE: if we have a situation when there are any packets in the + // acknowledged area, but they aren't retrieved, this area DOES NOT + // contain any losses. So a packet in this area is at best a duplicate. + + // In case when a loss would be abandoned (TLPKTDROP), there must at + // some point happen to be an empty first cell in the buffer, followed + // somewhere by a valid packet. If this state is achieved at some point, + // the acknowledgement sequence should be equal to the beginning of the + // buffer. Then, when TSBPD decides to drop these initial empty cells, + // we'll have: (m_iRcvLastAck <% buffer->getStartSeqNo()) - and in this + // case (bufidx < 0) condition will be satisfied also for this case. + // + // The only case when bufidx > 0, but packet seq is <% m_iRcvLastAck + // is when the packet sequence is within the initial contiguous area, + // which never contains losses, so discarding this packet does not + // discard a loss coverage, even if this were past ACK. + + if (bufidx < 0 || CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvLastAck) < 0) + { + time_point pts = getPktTsbPdTime(NULL, rpkt); + + enterCS(m_StatsLock); + const double bltime = (double) CountIIR( + uint64_t(m_stats.traceBelatedTime) * 1000, + count_microseconds(steady_clock::now() - pts), 0.2); + + m_stats.traceBelatedTime = bltime / 1000.0; + m_stats.rcvr.recvdBelated.count(rpkt.getLength()); + leaveCS(m_StatsLock); + HLOGC(qrlog.Debug, + log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo << " bufidx=" << bufidx << " (BELATED/" + << s_rexmitstat_str[pktrexmitflag] << ") with ACK %" << m_iRcvLastAck + << " FLAGS: " << rpkt.MessageFlagStr()); + continue; + } + + if (bufidx >= int(m_pRcvBuffer->capacity())) + { + // This is already a sequence discrepancy. Probably there could be found + // some way to make it continue reception by overriding the sequence and + // make a kinda TLKPTDROP, but there has been found no reliable way to do this. + if (m_bTsbPd && m_bTLPktDrop && m_pRcvBuffer->empty()) + { + // Only in live mode. In File mode this shall not be possible + // because the sender should stop sending in this situation. + // In Live mode this means that there is a gap between the + // lowest sequence in the empty buffer and the incoming sequence + // that exceeds the buffer size. Receiving data in this situation + // is no longer possible and this is a point of no return. + + LOGC(qrlog.Error, log << CONID() << + "SEQUENCE DISCREPANCY. BREAKING CONNECTION." + " %" << rpkt.m_iSeqNo + << " buffer=(%" << bufseq + << ":%" << m_iRcvCurrSeqNo // -1 = size to last index + << "+%" << CSeqNo::incseq(bufseq, int(m_pRcvBuffer->capacity()) - 1) + << "), " << (m_pRcvBuffer->capacity() - bufidx + 1) + << " past max. Reception no longer possible. REQUESTING TO CLOSE."); + + return -2; + } + else + { + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + << ", insert offset " << bufidx << ". " + << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) + ); + + return -1; + } + } + + const int buffer_add_result = m_pRcvBuffer->insert(u); + if (buffer_add_result < 0) + { + // The insert() result is -1 if at the position evaluated from this packet's + // sequence number there already is a packet. + // So this packet is "redundant". + IF_HEAVY_LOGGING(exc_type = "UNACKED"); + adding_successful = false; + } + else + { + w_new_inserted = true; + + IF_HEAVY_LOGGING(exc_type = "ACCEPTED"); + excessive = false; + if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) + { + // TODO: reset and restore the timestamp if TSBPD is disabled. + // Reset retransmission flag (must be excluded from GCM auth tag). + u->m_Packet.setRexmitFlag(false); + const EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; + u->m_Packet.setRexmitFlag(retransmitted); // Recover the flag. + + if (rc != ENCS_CLEAR) + { + adding_successful = false; + IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED"); + + // If TSBPD is disabled, then SRT either operates in buffer mode, of in message API without a restriction + // of a single message packet. In that case just dropping a packet is not enough. + // In message mode the whole message has to be dropped. + // However, when decryption fails the message number in the packet cannot be trusted. + // The packet has to be removed from the RCV buffer based on that pkt sequence number, + // and the sequence number itself must go into the RCV loss list. + // See issue ##2626. + SRT_ASSERT(m_bTsbPd); + + // Drop the packet from the receiver buffer. + // The packet was added to the buffer based on the sequence number, therefore sequence number should be used to drop it from the buffer. + // A drawback is that it would prevent a valid packet with the same sequence number, if it happens to arrive later, to end up in the buffer. + const int iDropCnt = m_pRcvBuffer->dropMessage(u->m_Packet.getSeqNo(), u->m_Packet.getSeqNo(), SRT_MSGNO_NONE, CRcvBuffer::DROP_EXISTING); + + const steady_clock::time_point tnow = steady_clock::now(); + ScopedLock lg(m_StatsLock); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * rpkt.getLength(), iDropCnt)); + m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1)); + if (frequentLogAllowed(tnow)) + { + LOGC(qrlog.Warn, log << CONID() << "Decryption failed (seqno %" << u->m_Packet.getSeqNo() << "), dropped " + << iDropCnt << ". pktRcvUndecryptTotal=" << m_stats.rcvr.undecrypted.total.count() << "."); + m_tsLogSlowDown = tnow; + } + } + } + else if (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM) + { + // Unencrypted packets are not allowed. + const int iDropCnt = m_pRcvBuffer->dropMessage(u->m_Packet.getSeqNo(), u->m_Packet.getSeqNo(), SRT_MSGNO_NONE, CRcvBuffer::DROP_EXISTING); + + const steady_clock::time_point tnow = steady_clock::now(); + ScopedLock lg(m_StatsLock); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt* rpkt.getLength(), iDropCnt)); + m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1)); + if (frequentLogAllowed(tnow)) + { + LOGC(qrlog.Warn, log << CONID() << "Packet not encrypted (seqno %" << u->m_Packet.getSeqNo() << "), dropped " + << iDropCnt << ". pktRcvUndecryptTotal=" << m_stats.rcvr.undecrypted.total.count() << "."); + m_tsLogSlowDown = tnow; + } + } + } + + if (adding_successful) + { + ScopedLock statslock(m_StatsLock); + m_stats.rcvr.recvdUnique.count(u->m_Packet.getLength()); + } + +#if ENABLE_HEAVY_LOGGING + std::ostringstream expectspec; + if (excessive) + expectspec << "EXCESSIVE(" << exc_type << ")"; + else + expectspec << "ACCEPTED"; + + std::ostringstream bufinfo; + + if (m_pRcvBuffer) + { + // XXX Fix this when the end of contiguous region detection is added. + const int ackidx = std::max(0, CSeqNo::seqoff(m_pRcvBuffer->getStartSeqNo(), m_iRcvLastAck)); + + bufinfo << " BUF.s=" << m_pRcvBuffer->capacity() + << " avail=" << (int(m_pRcvBuffer->capacity()) - ackidx) + << " buffer=(%" << bufseq + << ":%" << m_iRcvCurrSeqNo // -1 = size to last index + << "+%" << CSeqNo::incseq(bufseq, int(m_pRcvBuffer->capacity()) - 1) + << ")"; + } + + // Empty buffer info in case of groupwise receiver. + // There's no way to obtain this information here. + + LOGC(qrlog.Debug, log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo + << bufinfo.str() + << " RSL=" << expectspec.str() + << " SN=" << s_rexmitstat_str[pktrexmitflag] + << " FLAGS: " + << rpkt.MessageFlagStr()); +#endif + + // Decryption should have made the crypto flags EK_NOENC. + // Otherwise it's an error. + if (adding_successful) + { + HLOGC(qrlog.Debug, + log << CONID() + << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); + + if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. + { + int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); + int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); + w_srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); + HLOGC(qrlog.Debug, log << "pkt/LOSS DETECTED: %" << seqlo << " - %" << seqhi); + } + } + + // Update the current largest sequence number that has been received. + // Or it is a retransmitted packet, remove it from receiver loss list. + if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0) + { + m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received + } + else + { + unlose(rpkt); // was BELATED or RETRANSMITTED + w_was_sent_in_order &= 0 != pktrexmitflag; + } + } + + return 0; +} + +int srt::CUDT::processData(CUnit* in_unit) +{ + if (m_bClosing) + return -1; + + CPacket &packet = in_unit->m_Packet; + + // Just heard from the peer, reset the expiration count. + m_iEXPCount = 1; + m_tsLastRspTime.store(steady_clock::now()); + + + // We are receiving data, start tsbpd thread if TsbPd is enabled + if (-1 == checkLazySpawnTsbPdThread()) + { + return -1; + } + const int pktrexmitflag = m_bPeerRexmitFlag ? (packet.getRexmitFlag() ? 1 : 0) : 2; + const bool retransmitted = pktrexmitflag == 1; #if ENABLE_HEAVY_LOGGING - static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; string rexmit_reason; #endif - if (pktrexmitflag == 1) + if (retransmitted) { // This packet was retransmitted enterCS(m_StatsLock); @@ -9886,6 +10168,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Check if packet was retransmitted on request or on ack timeout // Search the sequence in the loss record. rexmit_reason = " by "; + ScopedLock lock(m_RcvLossLock); if (!m_pRcvLossList->find(packet.m_iSeqNo, packet.m_iSeqNo)) rexmit_reason += "BLIND"; else @@ -9931,7 +10214,6 @@ int srt::CUDT::processData(CUnit* in_unit) // this function will extract and test as needed. const bool unordered = CSeqNo::seqcmp(packet.m_iSeqNo, m_iRcvCurrSeqNo) <= 0; - const bool retransmitted = m_bPeerRexmitFlag && packet.getRexmitFlag(); // Retransmitted and unordered packets do not provide expected measurement. // We expect the 16th and 17th packet to be sent regularly, @@ -9946,7 +10228,6 @@ int srt::CUDT::processData(CUnit* in_unit) loss_seqs_t srt_loss_seqs; vector incoming; bool was_sent_in_order = true; - bool reorder_prevent_lossreport = false; // If the peer doesn't understand REXMIT flag, send rexmit request // always immediately. @@ -9954,37 +10235,26 @@ int srt::CUDT::processData(CUnit* in_unit) if (m_bPeerRexmitFlag) initial_loss_ttl = m_iReorderTolerance; - // After introduction of packet filtering, the "recordable loss detection" - // does not exactly match the true loss detection. When a FEC filter is - // working, for example, then getting one group filled with all packet but - // the last one and the FEC control packet, in this special case this packet - // won't be notified at all as lost because it will be recovered by the - // filter immediately before anyone notices what happened (and the loss - // detection for the further functionality is checked only afterwards, - // and in this case the immediate recovery makes the loss to not be noticed - // at all). - // - // Because of that the check for losses must happen BEFORE passing the packet - // to the filter and before the filter could recover the packet before anyone - // notices :) - - if (packet.getMsgSeq() != SRT_MSGNO_CONTROL) // disregard filter-control packets, their seq may mean nothing - { - int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo); - // Difference between these two sequence numbers is expected to be: - // 0 - duplicated last packet (theory only) - // 1 - subsequent packet (alright) - // <0 - belated or recovered packet - // >1 - jump over a packet loss (loss = seqdiff-1) + // Track packet loss in statistics early, because a packet filter (e.g. FEC) might recover it later on, + // supply the missing packet(s), and the loss will no longer be visible for the code that follows. + if (packet.getMsgSeq(m_bPeerRexmitFlag) != SRT_MSGNO_CONTROL) // disregard filter-control packets, their seq may mean nothing + { + const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo); + // Difference between these two sequence numbers is expected to be: + // 0 - duplicated last packet (theory only) + // 1 - subsequent packet (alright) + // <0 - belated or recovered packet + // >1 - jump over a packet loss (loss = seqdiff-1) if (diff > 1) { const int loss = diff - 1; // loss is all that is above diff == 1 + ScopedLock lg(m_StatsLock); const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); m_stats.rcvr.lost.count(stats::BytesPackets(loss * avgpayloadsz, (uint32_t) loss)); HLOGC(qrlog.Debug, - log << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " + log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " << CSeqNo::decseq(packet.m_iSeqNo) << "]"); } @@ -10013,242 +10283,91 @@ int srt::CUDT::processData(CUnit* in_unit) // This check is needed as after getting the lock the socket // could be potentially removed. It is however granted that as long // as gi is non-NULL iterator, the group does exist and it does contain - // this socket as member (that is, 'gi' cannot be a dangling iterator). + // this socket as member (that is, 'gi' cannot be a dangling pointer). if (gi != NULL) { if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely { HLOGC(qrlog.Debug, - log << "processData: IN-GROUP rcv state transition " << srt_log_grp_state[gi->rcvstate] + log << CONID() << "processData: IN-GROUP rcv state transition " << srt_log_grp_state[gi->rcvstate] << " -> RUNNING."); gi->rcvstate = SRT_GST_RUNNING; } else { - HLOGC(qrlog.Debug, log << "processData: IN-GROUP rcv state transition NOT DONE - state:" - << srt_log_grp_state[gi->rcvstate]); + HLOGC(qrlog.Debug, + log << CONID() << "processData: IN-GROUP rcv state transition NOT DONE - state:" + << srt_log_grp_state[gi->rcvstate]); } } } #endif + bool new_inserted = false; + + if (m_PacketFilter) + { + // Stuff this data into the filter + m_PacketFilter.receive(in_unit, (incoming), (filter_loss_seqs)); + HLOGC(qrlog.Debug, + log << CONID() << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) + << " loss to report, " + << (m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS ? "FIND & REPORT LOSSES YOURSELF" + : "REPORT ONLY THOSE")); + } + else + { + // Stuff in just one packet that has come in. + incoming.push_back(in_unit); + } + { // Start of offset protected section // Prevent TsbPd thread from modifying Ack position while adding data // offset from RcvLastAck in RcvBuffer must remain valid between seqoff() and addData() UniqueLock recvbuf_acklock(m_RcvBufferLock); + // Needed for possibly check for needsQuickACK. + const bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.m_iSeqNo, m_pRcvBuffer->getStartSeqNo()) < 0); + + const int res = handleSocketPacketReception(incoming, + (new_inserted), + (was_sent_in_order), + (srt_loss_seqs)); - // vector undec_units; - if (m_PacketFilter) + if (res == -2) { - // Stuff this data into the filter - m_PacketFilter.receive(in_unit, (incoming), (filter_loss_seqs)); - HLOGC(qrlog.Debug, - log << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) - << " loss to report, " - << (m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS ? "FIND & REPORT LOSSES YOURSELF" - : "REPORT ONLY THOSE")); + // This is a scoped lock with AckLock, but for the moment + // when processClose() is called this lock must be taken out, + // otherwise this will cause a deadlock. We don't need this + // lock anymore, and at 'return' it will be unlocked anyway. + recvbuf_acklock.unlock(); + processClose(); + + return -1; } - else + + if (res == -1) { - // Stuff in just one packet that has come in. - incoming.push_back(in_unit); + return -1; } - bool excessive = true; // stays true unless it was successfully added - - // Needed for possibly check for needsQuickACK. - bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.m_iSeqNo, m_iRcvLastSkipAck) < 0); - - // Loop over all incoming packets that were filtered out. - // In case when there is no filter, there's just one packet in 'incoming', - // the one that came in the input of this function. - for (vector::iterator unitIt = incoming.begin(); unitIt != incoming.end(); ++unitIt) + if (!srt_loss_seqs.empty()) { - CUnit * u = *unitIt; - CPacket &rpkt = u->m_Packet; - - // m_iRcvLastSkipAck is the base sequence number for the receiver buffer. - // This is the offset in the buffer; if this is negative, it means that - // this sequence is already in the past and the buffer is not interested. - // Meaning, this packet will be rejected, even if it could potentially be - // one of missing packets in the transmission. - int32_t offset = CSeqNo::seqoff(m_iRcvLastSkipAck, rpkt.m_iSeqNo); - - IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); - - if (offset < 0) - { - IF_HEAVY_LOGGING(exc_type = "BELATED"); - steady_clock::time_point tsbpdtime = m_pRcvBuffer->getPktTsbPdTime(rpkt.getMsgTimeStamp()); - const double bltime = (double) CountIIR( - uint64_t(m_stats.traceBelatedTime) * 1000, - count_microseconds(steady_clock::now() - tsbpdtime), 0.2); - - enterCS(m_StatsLock); - m_stats.traceBelatedTime = bltime / 1000.0; - m_stats.rcvr.recvdBelated.count(rpkt.getLength()); - leaveCS(m_StatsLock); - HLOGC(qrlog.Debug, - log << CONID() << "RECEIVED: seq=" << packet.m_iSeqNo << " offset=" << offset << " (BELATED/" - << rexmitstat[pktrexmitflag] << rexmit_reason << ") FLAGS: " << packet.MessageFlagStr()); - continue; - } - - const int avail_bufsize = (int) getAvailRcvBufferSizeNoLock(); - if (offset >= avail_bufsize) - { - // This is already a sequence discrepancy. Probably there could be found - // some way to make it continue reception by overriding the sequence and - // make a kinda TLKPTDROP, but there has been found no reliable way to do this. - if (m_bTsbPd && m_bTLPktDrop && m_pRcvBuffer->empty()) - { - // Only in live mode. In File mode this shall not be possible - // because the sender should stop sending in this situation. - // In Live mode this means that there is a gap between the - // lowest sequence in the empty buffer and the incoming sequence - // that exceeds the buffer size. Receiving data in this situation - // is no longer possible and this is a point of no return. - - LOGC(qrlog.Error, log << CONID() << - "SEQUENCE DISCREPANCY. BREAKING CONNECTION." - " seq=" << rpkt.m_iSeqNo - << " buffer=(" << m_iRcvLastSkipAck - << ":" << m_iRcvCurrSeqNo // -1 = size to last index - << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, int(m_pRcvBuffer->capacity()) - 1) - << "), " << (offset-avail_bufsize+1) - << " past max. Reception no longer possible. REQUESTING TO CLOSE."); - - // This is a scoped lock with AckLock, but for the moment - // when processClose() is called this lock must be taken out, - // otherwise this will cause a deadlock. We don't need this - // lock anymore, and at 'return' it will be unlocked anyway. - recvbuf_acklock.unlock(); - processClose(); - return -1; - } - else - { -#if ENABLE_NEW_RCVBUFFER - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo - << ", insert offset " << offset << ". " - << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) - ); -#else - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo - << ", insert offset " << offset << ". " - << m_pRcvBuffer->strFullnessState(steady_clock::now()) - ); -#endif - - return -1; - } - } - - bool adding_successful = true; -#if ENABLE_NEW_RCVBUFFER - if (m_pRcvBuffer->insert(u) < 0) -#else - if (m_pRcvBuffer->addData(u, offset) < 0) -#endif - { - // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. - // So this packet is "redundant". - IF_HEAVY_LOGGING(exc_type = "UNACKED"); - adding_successful = false; - } - else - { - IF_HEAVY_LOGGING(exc_type = "ACCEPTED"); - excessive = false; - if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) - { - EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; - if (rc != ENCS_CLEAR) - { - // Heavy log message because if seen once the message may happen very often. - HLOGC(qrlog.Debug, log << CONID() << "ERROR: packet not decrypted, dropping data."); - adding_successful = false; - IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED"); - - ScopedLock lg(m_StatsLock); - m_stats.rcvr.undecrypted.count(stats::BytesPackets(pktsz, 1)); - } - } - } - - if (adding_successful) - { - ScopedLock statslock(m_StatsLock); - m_stats.rcvr.recvdUnique.count(u->m_Packet.getLength()); - } + ScopedLock lock(m_RcvLossLock); -#if ENABLE_HEAVY_LOGGING - std::ostringstream expectspec; - if (excessive) - expectspec << "EXCESSIVE(" << exc_type << rexmit_reason << ")"; - else - expectspec << "ACCEPTED"; - - LOGC(qrlog.Debug, log << CONID() << "RECEIVED: seq=" << rpkt.m_iSeqNo - << " offset=" << offset - << " BUFr=" << avail_bufsize - << " avail=" << getAvailRcvBufferSizeNoLock() - << " buffer=(" << m_iRcvLastSkipAck - << ":" << m_iRcvCurrSeqNo // -1 = size to last index - << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) - << ") " - << " RSL=" << expectspec.str() - << " SN=" << rexmitstat[pktrexmitflag] - << " FLAGS: " - << rpkt.MessageFlagStr()); -#endif + HLOGC(qrlog.Debug, + log << CONID() << "processData: RECORDING LOSS: " << Printable(srt_loss_seqs) + << " tolerance=" << initial_loss_ttl); - // Decryption should have made the crypto flags EK_NOENC. - // Otherwise it's an error. - if (adding_successful) + for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i) { - // XXX move this code do CUDT::defaultPacketArrival and call it from here: - // srt_loss_seqs = CALLBACK_CALL(m_cbPacketArrival, rpkt); - - HLOGC(qrlog.Debug, - log << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); - - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. + m_pRcvLossList->insert(i->first, i->second); + if (initial_loss_ttl) { - int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); - int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); - - srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); - - if (initial_loss_ttl) - { - // pack loss list for (possibly belated) NAK - // The LOSSREPORT will be sent in a while. - - for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i) - { - m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); - } - HLOGC(qrlog.Debug, - log << "FreshLoss: added sequences: " << Printable(srt_loss_seqs) - << " tolerance: " << initial_loss_ttl); - reorder_prevent_lossreport = true; - } + // The LOSSREPORT will be sent after initial_loss_ttl. + m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); } } - - // Update the current largest sequence number that has been received. - // Or it is a retransmitted packet, remove it from receiver loss list. - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0) - { - m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received - } - else - { - unlose(rpkt); // was BELATED or RETRANSMITTED - was_sent_in_order &= 0 != pktrexmitflag; - } } // This is moved earlier after introducing filter because it shouldn't @@ -10269,7 +10388,7 @@ int srt::CUDT::processData(CUnit* in_unit) } } - if (excessive) + if (!new_inserted) { return -1; } @@ -10295,37 +10414,21 @@ int srt::CUDT::processData(CUnit* in_unit) if (!srt_loss_seqs.empty()) { - // A loss is detected - { - // TODO: Can unlock rcvloss after m_pRcvLossList->insert(...)? - // And probably protect m_FreshLoss as well. - - HLOGC(qrlog.Debug, log << "processData: LOSS DETECTED, %: " << Printable(srt_loss_seqs) << " - RECORDING."); - // if record_loss == false, nothing will be contained here - // Insert lost sequence numbers to the receiver loss list - ScopedLock lg(m_RcvLossLock); - for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i) - { - // If loss found, insert them to the receiver loss list - m_pRcvLossList->insert(i->first, i->second); - } - } - const bool report_recorded_loss = !m_PacketFilter || m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS; - if (!reorder_prevent_lossreport && report_recorded_loss) + if (!initial_loss_ttl && report_recorded_loss) { - HLOGC(qrlog.Debug, log << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs)); + HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs)); sendLossReport(srt_loss_seqs); } if (m_bTsbPd) { - HLOGC(qrlog.Debug, log << "loss: signaling TSBPD cond"); + HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); } else { - HLOGC(qrlog.Debug, log << "loss: socket is not TSBPD, not signaling"); + HLOGC(qrlog.Debug, log << CONID() << "loss: socket is not TSBPD, not signaling"); } } @@ -10336,12 +10439,12 @@ int srt::CUDT::processData(CUnit* in_unit) // With NEVER, nothing is to be reported. if (!filter_loss_seqs.empty()) { - HLOGC(qrlog.Debug, log << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); + HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); sendLossReport(filter_loss_seqs); if (m_bTsbPd) { - HLOGC(qrlog.Debug, log << "loss: signaling TSBPD cond"); + HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); } } @@ -10380,11 +10483,8 @@ int srt::CUDT::processData(CUnit* in_unit) // into two records. for (; i != m_FreshLoss.end() && i->ttl <= 0; ++i) { - HLOGF(qrlog.Debug, - "Packet seq %d-%d (%d packets) considered lost - sending LOSSREPORT", - i->seq[0], - i->seq[1], - CSeqNo::seqoff(i->seq[0], i->seq[1]) + 1); + HLOGC(qrlog.Debug, log << "Packet seq " << i->seq[0] << "-" << i->seq[1] + << " (" << (CSeqNo::seqoff(i->seq[0], i->seq[1]) + 1) << " packets) considered lost - sending LOSSREPORT"); addLossRecord(lossdata, i->seq[0], i->seq[1]); } @@ -10401,13 +10501,9 @@ int srt::CUDT::processData(CUnit* in_unit) } else { - HLOGF(qrlog.Debug, - "STILL %" PRIzu " FRESH LOSS RECORDS, FIRST: %d-%d (%d) TTL: %d", - m_FreshLoss.size(), - i->seq[0], - i->seq[1], - 1 + CSeqNo::seqoff(i->seq[0], i->seq[1]), - i->ttl); + HLOGC(qrlog.Debug, log << "STILL " << m_FreshLoss.size() << " FRESH LOSS RECORDS, FIRST: " + << i->seq[0] << "-" << i->seq[1] + << " (" << (1 + CSeqNo::seqoff(i->seq[0], i->seq[1])) << ") TTL: " << i->ttl); } // Phase 2: rest of the records should have TTL decreased. @@ -10436,9 +10532,8 @@ int srt::CUDT::processData(CUnit* in_unit) enterCS(m_StatsLock); m_stats.traceReorderDistance--; leaveCS(m_StatsLock); - HLOGF(qrlog.Debug, - "ORDERED DELIVERY of 50 packets in a row - decreasing tolerance to %d", - m_iReorderTolerance); + HLOGC(qrlog.Debug, log << "ORDERED DELIVERY of 50 packets in a row - decreasing tolerance to " + << m_iReorderTolerance); } } } @@ -10447,8 +10542,22 @@ int srt::CUDT::processData(CUnit* in_unit) } #if ENABLE_BONDING + +// NOTE: this is updated from the value of m_iRcvLastAck, +// which might be past the buffer and potentially cause setting +// the value to the last received and re-requiring retransmission. +// Worst case is that there could be a few packets to tear the transmission +// even more (as there will be likely no time to recover them), but +// if the transmission was already torn in the previously active link +// this shouldn't be a problem that these packets won't be recovered +// after activating the second link, although will be retried this way. void srt::CUDT::updateIdleLinkFrom(CUDT* source) { + int bufseq; + { + ScopedLock lg (m_RcvBufferLock); + bufseq = source->m_pRcvBuffer->getStartSeqNo(); + } ScopedLock lg (m_RecvLock); if (!m_pRcvBuffer->empty()) @@ -10457,14 +10566,19 @@ void srt::CUDT::updateIdleLinkFrom(CUDT* source) return; } - // XXX Try to optimize this. Note that here happens: - // - decseq just to have a value to compare directly - // - seqcmp with that value - // - if passed, in setInitialRcvSeq there's the same decseq again - int32_t new_last_rcv = CSeqNo::decseq(source->m_iRcvLastSkipAck); + int32_t new_last_rcv = source->m_iRcvLastAck; + + if (CSeqNo::seqcmp(new_last_rcv, bufseq) < 0) + { + // Emergency check whether the last ACK was behind the + // buffer. This may happen when TSBPD dropped empty cells. + // This may cause that the newly activated link may derive + // these empty cells which will never be recovered. + new_last_rcv = bufseq; + } - // if (new_last_rcv <% m_iRcvCurrSeqNo) - if (CSeqNo::seqcmp(new_last_rcv, m_iRcvCurrSeqNo) < 0) + // if (new_last_rcv <=% m_iRcvCurrSeqNo) + if (CSeqNo::seqcmp(new_last_rcv, m_iRcvCurrSeqNo) <= 0) { // Reject the change because that would shift the reception pointer backwards. HLOGC(grlog.Debug, log << "grp: NOT updating rcv-seq in @" << m_SocketID @@ -10474,71 +10588,10 @@ void srt::CUDT::updateIdleLinkFrom(CUDT* source) } HLOGC(grlog.Debug, log << "grp: updating rcv-seq in @" << m_SocketID - << " from @" << source->m_SocketID << ": %" << source->m_iRcvLastSkipAck); - setInitialRcvSeq(source->m_iRcvLastSkipAck); + << " from @" << source->m_SocketID << ": %" << new_last_rcv); + setInitialRcvSeq(new_last_rcv); } -// XXX This function is currently unused. It should be fixed and put into use. -// See the blocked call in CUDT::processData(). -// XXX REVIEW LOCKS WHEN REACTIVATING! -srt::CUDT::loss_seqs_t srt::CUDT::defaultPacketArrival(void* vself, CPacket& pkt) -{ -// [[using affinity(m_pRcvBuffer->workerThread())]]; - CUDT* self = (CUDT*)vself; - loss_seqs_t output; - - // XXX When an alternative packet arrival callback is installed - // in case of groups, move this part to the groupwise version. - - if (self->m_parent->m_GroupOf) - { - groups::SocketData* gi = self->m_parent->m_GroupMemberData; - if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely - { - HLOGC(qrlog.Debug, log << "defaultPacketArrival: IN-GROUP rcv state transition to RUNNING. NOT checking for loss"); - gi->rcvstate = SRT_GST_RUNNING; - return output; - } - } - - const int initial_loss_ttl = (self->m_bPeerRexmitFlag) ? self->m_iReorderTolerance : 0; - - int seqdiff = CSeqNo::seqcmp(pkt.m_iSeqNo, self->m_iRcvCurrSeqNo); - - HLOGC(qrlog.Debug, log << "defaultPacketArrival: checking sequence " << pkt.m_iSeqNo - << " against latest " << self->m_iRcvCurrSeqNo << " (distance: " << seqdiff << ")"); - - // Loss detection. - if (seqdiff > 1) // packet is later than the very subsequent packet - { - const int32_t seqlo = CSeqNo::incseq(self->m_iRcvCurrSeqNo); - const int32_t seqhi = CSeqNo::decseq(pkt.m_iSeqNo); - - { - // If loss found, insert them to the receiver loss list - ScopedLock lg (self->m_RcvLossLock); - self->m_pRcvLossList->insert(seqlo, seqhi); - - if (initial_loss_ttl) - { - // pack loss list for (possibly belated) NAK - // The LOSSREPORT will be sent in a while. - self->m_FreshLoss.push_back(CRcvFreshLoss(seqlo, seqhi, initial_loss_ttl)); - HLOGF(qrlog.Debug, "defaultPacketArrival: added loss sequence %d-%d (%d) with tolerance %d", seqlo, seqhi, - 1+CSeqNo::seqcmp(seqhi, seqlo), initial_loss_ttl); - } - } - - if (!initial_loss_ttl) - { - // old code; run immediately when tolerance = 0 - // or this feature isn't used because of the peer - output.push_back(make_pair(seqlo, seqhi)); - } - } - - return output; -} #endif /// This function is called when a packet has arrived, which was behind the current @@ -10577,7 +10630,7 @@ void srt::CUDT::unlose(const CPacket &packet) was_reordered = !packet.getRexmitFlag(); if (was_reordered) { - HLOGF(qrlog.Debug, "received out-of-band packet seq %d", sequence); + HLOGC(qrlog.Debug, log << "received out-of-band packet %" << sequence); const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.m_iSeqNo)); enterCS(m_StatsLock); @@ -10586,11 +10639,8 @@ void srt::CUDT::unlose(const CPacket &packet) if (seqdiff > m_iReorderTolerance) { const int new_tolerance = min(seqdiff, m_config.iMaxReorderTolerance); - HLOGF(qrlog.Debug, - "Belated by %d seqs - Reorder tolerance %s %d", - seqdiff, - (new_tolerance == m_iReorderTolerance) ? "REMAINS with" : "increased to", - new_tolerance); + HLOGC(qrlog.Debug, log << "Belated by " << seqdiff << " seqs - Reorder tolerance " + << (new_tolerance == m_iReorderTolerance ? "REMAINS with " : "increased to ") << new_tolerance); m_iReorderTolerance = new_tolerance; has_increased_tolerance = true; // Yes, even if reorder tolerance is already at maximum - this prevents decreasing tolerance. @@ -10603,7 +10653,7 @@ void srt::CUDT::unlose(const CPacket &packet) } else { - HLOGF(qrlog.Debug, "received reXmitted or belated packet seq %d (distinction not supported by peer)", sequence); + HLOGC(qrlog.Debug, log << "received reXmitted or belated packet seq " << sequence << " (distinction not supported by peer)"); } // Don't do anything if "belated loss report" feature is not used. @@ -10617,55 +10667,10 @@ void srt::CUDT::unlose(const CPacket &packet) if (m_bPeerRexmitFlag == 0 || m_iReorderTolerance == 0) return; - size_t i = 0; - int had_ttl = 0; - for (i = 0; i < m_FreshLoss.size(); ++i) - { - had_ttl = m_FreshLoss[i].ttl; - switch (m_FreshLoss[i].revoke(sequence)) - { - case CRcvFreshLoss::NONE: - continue; // Not found. Search again. - - case CRcvFreshLoss::STRIPPED: - goto breakbreak; // Found and the modification is applied. We're done here. - - case CRcvFreshLoss::DELETE: - // No more elements. Kill it. - m_FreshLoss.erase(m_FreshLoss.begin() + i); - // Every loss is unique. We're done here. - goto breakbreak; - - case CRcvFreshLoss::SPLIT: - // Oh, this will be more complicated. This means that it was in between. - { - // So create a new element that will hold the upper part of the range, - // and this one modify to be the lower part of the range. - - // Keep the current end-of-sequence value for the second element - int32_t next_end = m_FreshLoss[i].seq[1]; - - // seq-1 set to the end of this element - m_FreshLoss[i].seq[1] = CSeqNo::decseq(sequence); - // seq+1 set to the begin of the next element - int32_t next_begin = CSeqNo::incseq(sequence); - - // Use position of the NEXT element because insertion happens BEFORE pointed element. - // Use the same TTL (will stay the same in the other one). - m_FreshLoss.insert(m_FreshLoss.begin() + i + 1, - CRcvFreshLoss(next_begin, next_end, m_FreshLoss[i].ttl)); - } - goto breakbreak; - } - } - - // Could have made the "return" instruction instead of goto, but maybe there will be something - // to add in future, so keeping that. -breakbreak:; - - if (i != m_FreshLoss.size()) + int had_ttl = 0; + if (CRcvFreshLoss::removeOne((m_FreshLoss), sequence, (&had_ttl))) { - HLOGF(qrlog.Debug, "sequence %d removed from belated lossreport record", sequence); + HLOGC(qrlog.Debug, log << "sequence " << sequence << " removed from belated lossreport record"); } if (was_reordered) @@ -10678,7 +10683,7 @@ breakbreak:; else if (had_ttl > 2) { ++m_iConsecEarlyDelivery; // otherwise, and if it arrived quite earlier, increase counter - HLOGF(qrlog.Debug, "... arrived at TTL %d case %d", had_ttl, m_iConsecEarlyDelivery); + HLOGC(qrlog.Debug, log << "... arrived at TTL " << had_ttl << " case " << m_iConsecEarlyDelivery); // After 10 consecutive if (m_iConsecEarlyDelivery >= 10) @@ -10690,10 +10695,8 @@ breakbreak:; enterCS(m_StatsLock); m_stats.traceReorderDistance--; leaveCS(m_StatsLock); - HLOGF(qrlog.Debug, - "... reached %d times - decreasing tolerance to %d", - m_iConsecEarlyDelivery, - m_iReorderTolerance); + HLOGC(qrlog.Debug, log << "... reached " << m_iConsecEarlyDelivery + << " times - decreasing tolerance to " << m_iReorderTolerance); } } } @@ -10704,9 +10707,47 @@ breakbreak:; void srt::CUDT::dropFromLossLists(int32_t from, int32_t to) { ScopedLock lg(m_RcvLossLock); - m_pRcvLossList->remove(from, to); - HLOGF(qrlog.Debug, "%sTLPKTDROP seq %d-%d (%d packets)", CONID().c_str(), from, to, CSeqNo::seqoff(from, to)); + IF_HEAVY_LOGGING(bool autodetected = false); + int32_t begin SRT_ATR_UNUSED; + if (from == SRT_SEQNO_NONE) + { + begin = m_pRcvLossList->removeUpTo(to); + IF_HEAVY_LOGGING(autodetected = true); + } + else + { + begin = from; + m_pRcvLossList->remove(from, to); + } + +#if ENABLE_HEAVY_LOGGING + ostringstream range; + if (begin == SRT_SEQNO_NONE) + { + range << "no"; + } + else + { + int off = CSeqNo::seqoff(begin, to); + if (off < 0) + { + range << "WEIRD NUMBER OF"; + } + else + { + range << (off + 1); + } + } + + static const char* const beginwhere[2] = {"explicit", "detected"}; + + const char* const reqtype = (from == SRT_SEQNO_NONE) ? "TLPKTDROP" : "DROPREQ"; + + HLOGC(qrlog.Debug, log << CONID() << "DROP PER " << reqtype << " %" << begin + << "[" << beginwhere[1*autodetected] << "]-" << to << " (" + << range.str() << " packets)"); +#endif if (m_bPeerRexmitFlag == 0 || m_iReorderTolerance == 0) return; @@ -10808,12 +10849,12 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // XXX ASSUMPTIONS: // [[using assert(packet.m_iID == 0)]] - HLOGC(cnlog.Debug, log << "processConnectRequest: received a connection request"); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: received a connection request"); if (m_bClosing) { m_RejectReason = SRT_REJ_CLOSE; - HLOGC(cnlog.Debug, log << "processConnectRequest: ... NOT. Rejecting because closing."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... NOT. Rejecting because closing."); return m_RejectReason; } @@ -10825,7 +10866,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (m_bBroken) { m_RejectReason = SRT_REJ_CLOSE; - HLOGC(cnlog.Debug, log << "processConnectRequest: ... NOT. Rejecting because broken."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... NOT. Rejecting because broken."); return m_RejectReason; } // When CHandShake::m_iContentSize is used in log, the file fails to link! @@ -10841,8 +10882,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) { m_RejectReason = SRT_REJ_ROGUE; HLOGC(cnlog.Debug, - log << "processConnectRequest: ... NOT. Wrong size: " << packet.getLength() << " (expected: " << exp_len - << ")"); + log << CONID() << "processConnectRequest: ... NOT. Wrong size: " << packet.getLength() + << " (expected: " << exp_len << ")"); return m_RejectReason; } @@ -10852,7 +10893,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (!packet.isControl(UMSG_HANDSHAKE)) { m_RejectReason = SRT_REJ_ROGUE; - LOGC(cnlog.Error, log << "processConnectRequest: the packet received as handshake is not a handshake message"); + LOGC(cnlog.Error, + log << CONID() << "processConnectRequest: the packet received as handshake is not a handshake message"); return m_RejectReason; } @@ -10870,14 +10912,22 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) int32_t cookie_val = bake(addr); - HLOGC(cnlog.Debug, log << "processConnectRequest: new cookie: " << hex << cookie_val); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: new cookie: " << hex << cookie_val); + + // Remember the incoming destination address here and use it as a source + // address when responding. It's not possible to record this address yet + // because this happens still in the frames of the listener socket. Only + // when processing switches to the newly spawned accepted socket can the + // address be recorded in its m_SourceAddr field. + sockaddr_any use_source_addr = packet.udpDestAddr(); // REQUEST:INDUCTION. // Set a cookie, a target ID, and send back the same as // RESPONSE:INDUCTION. if (hs.m_iReqType == URQ_INDUCTION) { - HLOGC(cnlog.Debug, log << "processConnectRequest: received type=induction, sending back with cookie+socket"); + HLOGC(cnlog.Debug, + log << CONID() << "processConnectRequest: received type=induction, sending back with cookie+socket"); // XXX That looks weird - the calculated md5 sum out of the given host/port/timestamp // is 16 bytes long, but CHandShake::m_iCookie has 4 bytes. This then effectively copies @@ -10906,7 +10956,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) hs.m_iType = SrtHSRequest::wrapFlags(true /*put SRT_MAGIC_CODE in HSFLAGS*/, m_config.iSndCryptoKeyLen); bool whether SRT_ATR_UNUSED = m_config.iSndCryptoKeyLen != 0; HLOGC(cnlog.Debug, - log << "processConnectRequest: " << (whether ? "" : "NOT ") + log << CONID() << "processConnectRequest: " << (whether ? "" : "NOT ") << " Advertising PBKEYLEN - value = " << m_config.iSndCryptoKeyLen); size_t size = packet.getLength(); @@ -10914,9 +10964,9 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) setPacketTS(packet, steady_clock::now()); // Display the HS before sending it to peer - HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (i): " << hs.show()); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (i): " << hs.show()); - m_pSndQueue->sendto(addr, packet); + m_pSndQueue->sendto(addr, packet, use_source_addr); return SRT_REJ_UNKNOWN; // EXCEPTION: this is a "no-error" code. } @@ -10927,13 +10977,14 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (!hs.valid()) { - LOGC(cnlog.Error, log << "processConnectRequest: ROGUE HS RECEIVED. Rejecting"); + LOGC(cnlog.Error, log << CONID() << "processConnectRequest: ROGUE HS RECEIVED. Rejecting"); m_RejectReason = SRT_REJ_ROGUE; return SRT_REJ_ROGUE; } HLOGC(cnlog.Debug, - log << "processConnectRequest: received type=" << RequestTypeStr(hs.m_iReqType) << " - checking cookie..."); + log << CONID() << "processConnectRequest: received type=" << RequestTypeStr(hs.m_iReqType) + << " - checking cookie..."); if (hs.m_iCookie != cookie_val) { cookie_val = bake(addr, cookie_val, -1); // SHOULD generate an earlier, distracted cookie @@ -10941,15 +10992,15 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (hs.m_iCookie != cookie_val) { m_RejectReason = SRT_REJ_RDVCOOKIE; - HLOGC(cnlog.Debug, log << "processConnectRequest: ...wrong cookie " << hex << cookie_val << ". Ignoring."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ...wrong cookie " << hex << cookie_val << ". Ignoring."); return m_RejectReason; } - HLOGC(cnlog.Debug, log << "processConnectRequest: ... correct (FIXED) cookie. Proceeding."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (FIXED) cookie. Proceeding."); } else { - HLOGC(cnlog.Debug, log << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); } int32_t id = hs.m_iID; @@ -10994,16 +11045,16 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (!accepted_hs) { HLOGC(cnlog.Debug, - log << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason - << " MSG: " << srt_rejectreason_str(m_RejectReason)); + log << CONID() << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason + << " MSG: " << srt_rejectreason_str(m_RejectReason)); // mismatch, reject the request hs.m_iReqType = URQFailure(m_RejectReason); size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); packet.m_iID = id; setPacketTS(packet, steady_clock::now()); - HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (e): " << hs.show()); - m_pSndQueue->sendto(addr, packet); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (e): " << hs.show()); + m_pSndQueue->sendto(addr, packet, use_source_addr); } else { @@ -11026,11 +11077,11 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (result == -1) { hs.m_iReqType = URQFailure(error); - LOGF(cnlog.Warn, "processConnectRequest: rsp(REJECT): %d - %s", hs.m_iReqType, srt_rejectreason_str(error)); + LOGC(cnlog.Warn, log << "processConnectRequest: rsp(REJECT): " << hs.m_iReqType << " - " << srt_rejectreason_str(error)); } // The `acpu` not NULL means connection exists, the `result` should be 0. It is not checked here though. - // The `newConnection(..)` only sends reponse for newly created connection. + // The `newConnection(..)` only sends response for newly created connection. // The connection already exists (no new connection has been created, no response sent). // Send the conclusion response manually here in case the peer has missed the first one. // The value `result` here should be 0. @@ -11072,14 +11123,14 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) (packet), (hs))) { HLOGC(cnlog.Debug, - log << "processConnectRequest: rejecting due to problems in createSrtHandshake."); - result = -1; // enforce fallthrough for the below condition! + log << CONID() << "processConnectRequest: rejecting due to problems in createSrtHandshake."); + result = -1; // enforce fallthrough for the below condition! hs.m_iReqType = URQFailure(m_RejectReason == SRT_REJ_UNKNOWN ? int(SRT_REJ_IPE) : m_RejectReason.load()); } else { // Send the crafted handshake - HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING (repeated) HS (a): " << hs.show()); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING (repeated) HS (a): " << hs.show()); acpu->addressAndSend((packet)); } } @@ -11089,8 +11140,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // BUG! There is no need to update write-readiness on the listener socket once new connection is accepted. // Only read-readiness has to be updated, but it is done so in the newConnection(..) function. // See PR #1831 and issue #1667. - HLOGC(cnlog.Debug, log << "processConnectRequest: @" << m_SocketID - << " accepted connection, updating epoll to write-ready"); + HLOGC(cnlog.Debug, + log << CONID() << "processConnectRequest: accepted connection, updating epoll to write-ready"); // New connection has been accepted or an existing one has been found. Update epoll write-readiness. // a new connection has been created, enable epoll for write @@ -11113,7 +11164,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) setPacketTS((rsp), steady_clock::now()); rsp.pack(UMSG_SHUTDOWN); rsp.m_iID = m_PeerID; - m_pSndQueue->sendto(addr, rsp); + m_pSndQueue->sendto(addr, rsp, use_source_addr); } else { @@ -11125,12 +11176,12 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) packet.setLength(size); packet.m_iID = id; setPacketTS(packet, steady_clock::now()); - HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (a): " << hs.show()); - m_pSndQueue->sendto(addr, packet); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (a): " << hs.show()); + m_pSndQueue->sendto(addr, packet, use_source_addr); } } } - LOGC(cnlog.Note, log << "listen ret: " << hs.m_iReqType << " - " << RequestTypeStr(hs.m_iReqType)); + LOGC(cnlog.Note, log << CONID() << "listen ret: " << hs.m_iReqType << " - " << RequestTypeStr(hs.m_iReqType)); return RejectReasonForURQ(hs.m_iReqType); } @@ -11207,7 +11258,10 @@ int srt::CUDT::checkNAKTimer(const steady_clock::time_point& currtime) * not knowing what to retransmit when the only NAK sent by receiver is lost, * all packets past last ACK are retransmitted (rexmitMethod() == SRM_FASTREXMIT). */ + enterCS(m_RcvLossLock); const int loss_len = m_pRcvLossList->getLossLength(); + leaveCS(m_RcvLossLock); + SRT_ASSERT(loss_len >= 0); int debug_decision = BECAUSE_NO_REASON; @@ -11284,7 +11338,7 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec // Application will detect this when it calls any UDT methods next time. // HLOGC(xtlog.Debug, - log << "CONNECTION EXPIRED after " << count_milliseconds(currtime - last_rsp_time) << "ms"); + log << CONID() << "CONNECTION EXPIRED after " << count_milliseconds(currtime - last_rsp_time) << "ms"); m_bClosing = true; m_bBroken = true; m_iBrokenCounter = 30; @@ -11299,8 +11353,8 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec } HLOGC(xtlog.Debug, - log << "EXP TIMER: count=" << m_iEXPCount << "/" << (+COMM_RESPONSE_MAX_EXP) << " elapsed=" - << (count_microseconds(currtime - last_rsp_time)) << "/" << (+PEER_IDLE_TMO_US) << "us"); + log << CONID() << "EXP TIMER: count=" << m_iEXPCount << "/" << (+COMM_RESPONSE_MAX_EXP) + << " elapsed=" << (count_microseconds(currtime - last_rsp_time)) << "/" << (+PEER_IDLE_TMO_US) << "us"); ++m_iEXPCount; @@ -11322,6 +11376,9 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec void srt::CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) { + // Check if HSv4 should be retransmitted, and if KM_REQ should be resent if the side is INITIATOR. + checkSndTimers(); + // There are two algorithms of blind packet retransmission: LATEREXMIT and FASTREXMIT. // // LATEREXMIT is only used with FileCC. @@ -11338,11 +11395,14 @@ void srt::CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) // in the sender's buffer will be added to the SND loss list and retransmitted. // - const uint64_t rtt_syn = (m_iSRTT + 4 * m_iRTTVar + 2 * COMM_SYN_INTERVAL_US); - const uint64_t exp_int_us = (m_iReXmitCount * rtt_syn + COMM_SYN_INTERVAL_US); + { + ScopedLock ack_lock(m_RecvAckLock); + const uint64_t rtt_syn = (m_iSRTT + 4 * m_iRTTVar + 2 * COMM_SYN_INTERVAL_US); + const uint64_t exp_int_us = (m_iReXmitCount * rtt_syn + COMM_SYN_INTERVAL_US); - if (currtime <= (m_tsLastRspAckTime + microseconds_from(exp_int_us))) - return; + if (currtime <= (m_tsLastRspAckTime + microseconds_from(exp_int_us))) + return; + } // If there is no unacknowledged data in the sending buffer, // then there is nothing to retransmit. @@ -11385,7 +11445,6 @@ void srt::CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) ++m_iReXmitCount; - checkSndTimers(DONT_REGEN_KM); const ECheckTimerStage stage = is_fastrexmit ? TEV_CHT_FASTREXMIT : TEV_CHT_REXMIT; updateCC(TEV_CHECKTIMER, EventVariant(stage)); @@ -11463,12 +11522,15 @@ void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) token = m_parent->m_GroupMemberData->token; if (m_parent->m_GroupMemberData->sndstate == SRT_GST_PENDING) { - HLOGC(gmlog.Debug, log << "updateBrokenConnection: a pending link was broken - will be removed"); + HLOGC(gmlog.Debug, log << CONID() << "updateBrokenConnection: a pending link was broken - will be removed"); pending_broken = true; } else { - HLOGC(gmlog.Debug, log << "updateBrokenConnection: state=" << CUDTGroup::StateStr(m_parent->m_GroupMemberData->sndstate) << " a used link was broken - not closing automatically"); + HLOGC(gmlog.Debug, + log << CONID() << "updateBrokenConnection: state=" + << CUDTGroup::StateStr(m_parent->m_GroupMemberData->sndstate) + << " a used link was broken - not closing automatically"); } m_parent->m_GroupMemberData->sndstate = SRT_GST_BROKEN; @@ -11665,8 +11727,8 @@ bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShak if (!bytelen || bytelen > CSrtConfig::MAX_SID_LENGTH) { LOGC(cnlog.Error, - log << "interpretSrtHandshake: STREAMID length " << bytelen << " is 0 or > " << +CSrtConfig::MAX_SID_LENGTH - << " - PROTOCOL ERROR, REJECTING"); + log << CONID() << "interpretSrtHandshake: STREAMID length " << bytelen << " is 0 or > " + << +CSrtConfig::MAX_SID_LENGTH << " - PROTOCOL ERROR, REJECTING"); return false; } // See comment at CUDT::interpretSrtHandshake(). @@ -11702,7 +11764,8 @@ bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShak #if ENABLE_BONDING if (have_group && acore->m_config.iGroupConnect == 0) { - HLOGC(cnlog.Debug, log << "runAcceptHook: REJECTING connection WITHOUT calling the hook - groups not allowed"); + HLOGC(cnlog.Debug, + log << CONID() << "runAcceptHook: REJECTING connection WITHOUT calling the hook - groups not allowed"); return false; } @@ -11711,6 +11774,8 @@ bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShak acore->m_HSGroupType = gt; #endif + // Set the default value + acore->m_RejectReason = SRT_REJX_FALLBACK; try { int result = CALLBACK_CALL(m_cbAcceptHook, acore->m_SocketID, hs.m_iVersion, peer, target); @@ -11723,6 +11788,7 @@ bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShak return false; } + acore->m_RejectReason = SRT_REJ_UNKNOWN; return true; } @@ -11751,10 +11817,8 @@ void srt::CUDT::processKeepalive(const CPacket& ctrlpkt, const time_point& tsArr } #endif -#if ENABLE_NEW_RCVBUFFER ScopedLock lck(m_RcvBufferLock); m_pRcvBuffer->updateTsbPdTimeBase(ctrlpkt.getMsgTimeStamp()); if (m_config.bDriftTracer) m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, -1); -#endif } diff --git a/trunk/3rdparty/srt-1-fit/srtcore/core.h b/trunk/3rdparty/srt-1-fit/srtcore/core.h index fa58ca7c2e..71c955c331 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/core.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/core.h @@ -59,7 +59,7 @@ modified by #include "srt.h" #include "common.h" #include "list.h" -#include "buffer.h" +#include "buffer_snd.h" #include "buffer_rcv.h" #include "window.h" #include "packet.h" @@ -288,7 +288,7 @@ class CUDT { #if ENABLE_LOGGING std::ostringstream os; - os << "@" << m_SocketID << ":"; + os << "@" << m_SocketID << ": "; return os.str(); #else return ""; @@ -312,8 +312,10 @@ class CUDT int32_t schedSeqNo() const { return m_iSndNextSeqNo; } bool overrideSndSeqNo(int32_t seq); +#if ENABLE_BONDING sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime.load(); } sync::steady_clock::time_point freshActivationStart() const { return m_tsFreshActivation; } +#endif int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } int flowWindowSize() const { return m_iFlowWindowSize; } @@ -371,27 +373,34 @@ class CUDT int minSndSize(int len = 0) const { const int ps = (int) maxPayloadSize(); - if (len == 0) // wierd, can't use non-static data member as default argument! + if (len == 0) // weird, can't use non-static data member as default argument! len = ps; return m_config.bMessageAPI ? (len+ps-1)/ps : 1; } - int32_t makeTS(const time_point& from_time) const + static int32_t makeTS(const time_point& from_time, const time_point& tsStartTime) { // NOTE: // - This calculates first the time difference towards start time. // - This difference value is also CUT OFF THE SEGMENT information // (a multiple of MAX_TIMESTAMP+1) // So, this can be simply defined as: TS = (RTS - STS) % (MAX_TIMESTAMP+1) - // XXX Would be nice to check if local_time > m_tsStartTime, - // otherwise it may go unnoticed with clock skew. - return (int32_t) sync::count_microseconds(from_time - m_stats.tsStartTime); + SRT_ASSERT(from_time >= tsStartTime); + return (int32_t) sync::count_microseconds(from_time - tsStartTime); } - void setPacketTS(CPacket& p, const time_point& local_time) - { - p.m_iTimeStamp = makeTS(local_time); - } + /// @brief Set the timestamp field of the packet using the provided value (no check) + /// @param p the packet structure to set the timestamp on. + /// @param ts timestamp to use as a source for packet timestamp. + SRT_ATTR_EXCLUDES(m_StatsLock) + void setPacketTS(CPacket& p, const time_point& ts); + + /// @brief Set the timestamp field of the packet according the TSBPD mode. + /// Also checks the connection start time (m_tsStartTime). + /// @param p the packet structure to set the timestamp on. + /// @param ts timestamp to use as a source for packet timestamp. Ignored if m_bPeerTsbPd is false. + SRT_ATTR_EXCLUDES(m_StatsLock) + void setDataPacketTS(CPacket& p, const time_point& ts); // Utility used for closing a listening socket // immediately to free the socket @@ -416,11 +425,7 @@ class CUDT SRTU_PROPERTY_RO(SRTSOCKET, id, m_SocketID); SRTU_PROPERTY_RO(bool, isClosing, m_bClosing); -#if ENABLE_NEW_RCVBUFFER - SRTU_PROPERTY_RO(srt::CRcvBufferNew*, rcvBuffer, m_pRcvBuffer); -#else - SRTU_PROPERTY_RO(CRcvBuffer*, rcvBuffer, m_pRcvBuffer); -#endif + SRTU_PROPERTY_RO(srt::CRcvBuffer*, rcvBuffer, m_pRcvBuffer); SRTU_PROPERTY_RO(bool, isTLPktDrop, m_bTLPktDrop); SRTU_PROPERTY_RO(bool, isSynReceiving, m_config.bSynRecving); SRTU_PROPERTY_RR(sync::Condition*, recvDataCond, &m_RecvDataCond); @@ -441,16 +446,13 @@ class CUDT private: /// initialize a UDT entity and bind to a local address. - void open(); /// Start listening to any connection request. - void setListenState(); /// Connect to a UDT entity listening at address "peer". /// @param peer [in] The address of the listening UDT entity. - void startConnect(const sockaddr_any& peer, int32_t forced_isn); /// Process the response handshake packet. Failure reasons can be: @@ -461,7 +463,6 @@ class CUDT /// @retval 0 Connection successful /// @retval 1 Connection in progress (m_ConnReq turned into RESPONSE) /// @retval -1 Connection failed - SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) EConnectStatus processConnectResponse(const CPacket& pkt, CUDTException* eout) ATR_NOEXCEPT; @@ -484,15 +485,20 @@ class CUDT /// @param rst Current read status to know if the HS packet was freshly received from the peer, or this is only a periodic update (RST_AGAIN) SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) EConnectStatus processRendezvous(const CPacket* response, const sockaddr_any& serv_addr, EReadStatus, CPacket& reqpkt); + void sendRendezvousRejection(const sockaddr_any& serv_addr, CPacket& request); + + /// Create the CryptoControl object based on the HS packet. + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) + bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException* eout); - /// Create the CryptoControl object based on the HS packet. Allocates sender and receiver buffers and loss lists. + /// Allocates sender and receiver buffers and loss lists. SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) - bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout); + bool prepareBuffers(CUDTException* eout); SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) EConnectStatus postConnect(const CPacket* response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT; - SRT_ATR_NODISCARD bool applyResponseSettings() ATR_NOEXCEPT; + SRT_ATR_NODISCARD bool applyResponseSettings(const CPacket* hspkt /*[[nullable]]*/) ATR_NOEXCEPT; SRT_ATR_NODISCARD EConnectStatus processAsyncConnectResponse(const CPacket& pkt) ATR_NOEXCEPT; SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket* response, const sockaddr_any& serv_addr); SRT_ATR_NODISCARD EConnectStatus craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize); @@ -662,8 +668,14 @@ class CUDT /// the receiver fresh loss list. void unlose(const CPacket& oldpacket); void dropFromLossLists(int32_t from, int32_t to); + bool getFirstNoncontSequence(int32_t& w_seq, std::string& w_log_reason); + + SRT_ATTR_EXCLUDES(m_ConnectionLock) + void checkSndTimers(); + + /// @brief Check and perform KM refresh if needed. + void checkSndKMRefresh(); - void checkSndTimers(Whether2RegenKm regen = DONT_REGEN_KM); void handshakeDone() { m_iSndHsRetryCnt = 0; @@ -708,18 +720,17 @@ class CUDT SRT_ATTR_EXCLUDES(m_RcvBufferLock) bool isRcvBufferReady() const; + SRT_ATTR_REQUIRES(m_RcvBufferLock) + bool isRcvBufferReadyNoLock() const; + // TSBPD thread main function. static void* tsbpd(void* param); -#if ENABLE_NEW_RCVBUFFER - /// Drop too late packets (receiver side). Updaet loss lists and ACK positions. + /// Drop too late packets (receiver side). Update loss lists and ACK positions. /// The @a seqno packet itself is not dropped. /// @param seqno [in] The sequence number of the first packets following those to be dropped. /// @return The number of packets dropped. int rcvDropTooLateUpTo(int seqno); -#endif - - void updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno); static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt); static loss_seqs_t groupPacketArrival(void* vself, CPacket& pkt); @@ -811,6 +822,9 @@ class CUDT CSndBuffer* m_pSndBuffer; // Sender buffer CSndLossList* m_pSndLossList; // Sender loss list CPktTimeWindow<16, 16> m_SndTimeWindow; // Packet sending time window +#ifdef ENABLE_MAXREXMITBW + CSndRateEstimator m_SndRexmitRate; // Retransmission rate estimation. +#endif atomic_duration m_tdSendInterval; // Inter-packet time, in CPU clock cycles @@ -826,6 +840,7 @@ class CUDT duration m_tdACKInterval; // ACK interval duration m_tdNAKInterval; // NAK interval + SRT_ATTR_GUARDED_BY(m_RecvAckLock) atomic_time_point m_tsLastRspTime; // Timestamp of last response from the peer time_point m_tsLastRspAckTime; // (SND) Timestamp of last ACK from the peer @@ -851,7 +866,7 @@ class CUDT // and this is the sequence number that refers to the block at position [0]. Upon acknowledgement, // this value is shifted to the acknowledged position, and the blocks are removed from the // m_pSndBuffer buffer up to excluding this sequence number. - // XXX CONSIDER removing this field and give up the maintenance of this sequence number + // XXX CONSIDER removing this field and giving up the maintenance of this sequence number // to the sending buffer. This way, extraction of an old packet for retransmission should // require only the lost sequence number, and how to find the packet with this sequence // will be up to the sending buffer. @@ -894,14 +909,22 @@ class CUDT SRT_ATTR_GUARDED_BY(m_RecvAckLock) int32_t m_iReXmitCount; // Re-Transmit Count since last ACK + time_point m_tsLogSlowDown; // The last time a log message from the "slow down" group was shown. + // The "slow down" group of logs are those that can be printed too often otherwise, but can't be turned off (warnings and errors). + // Currently only used by decryption failure message, therefore no mutex protection needed. + + /// @brief Check if a frequent log can be shown. + /// @param tnow current time + /// @return true if it is ok to print a frequent log message. + bool frequentLogAllowed(const time_point& tnow) const; + private: // Receiving related data -#if ENABLE_NEW_RCVBUFFER - CRcvBufferNew* m_pRcvBuffer; //< Receiver buffer -#else CRcvBuffer* m_pRcvBuffer; //< Receiver buffer -#endif + SRT_ATTR_GUARDED_BY(m_RcvLossLock) CRcvLossList* m_pRcvLossList; //< Receiver loss list + SRT_ATTR_GUARDED_BY(m_RcvLossLock) std::deque m_FreshLoss; //< Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for. + int m_iReorderTolerance; //< Current value of dynamic reorder tolerance int m_iConsecEarlyDelivery; //< Increases with every OOO packet that came m_iRcvCurrSeqNo; // (RCV) Largest received sequence number. RcvQTh, TSBPDTh. int32_t m_iRcvCurrPhySeqNo; // Same as m_iRcvCurrSeqNo, but physical only (disregarding a filter) - + bool m_bBufferWasFull; // Indicate that RX buffer was full last time a ack was sent int32_t m_iPeerISN; // Initial Sequence Number of the peer side uint32_t m_uPeerSrtVersion; @@ -959,7 +981,7 @@ class CUDT mutable sync::Mutex m_RcvBufferLock; // Protects the state of the m_pRcvBuffer // Protects access to m_iSndCurrSeqNo, m_iSndLastAck - sync::Mutex m_RecvAckLock; // Protects the state changes while processing incomming ACK (SRT_EPOLL_OUT) + sync::Mutex m_RecvAckLock; // Protects the state changes while processing incoming ACK (SRT_EPOLL_OUT) sync::Condition m_RecvDataCond; // used to block "srt_recv*" when there is no data. Use together with m_RecvLock sync::Mutex m_RecvLock; // used to synchronize "srt_recv*" call, protects TSBPD drift updates (CRcvBuffer::isRcvDataReady()) @@ -1036,32 +1058,50 @@ class CUDT void updateSndLossListOnACK(int32_t ackdata_seqno); /// Pack a packet from a list of lost packets. - /// /// @param packet [in, out] a packet structure to fill - /// @param origintime [in, out] origin timestamp of the packet - /// /// @return payload size on success, <=0 on failure - int packLostData(CPacket &packet, time_point &origintime); + int packLostData(CPacket &packet); /// Pack a unique data packet (never sent so far) in CPacket for sending. - /// /// @param packet [in, out] a CPacket structure to fill. - /// @param origintime [in, out] origin timestamp of the packet. /// /// @return true if a packet has been packets; false otherwise. - bool packUniqueData(CPacket& packet, time_point& origintime); + bool packUniqueData(CPacket& packet); /// Pack in CPacket the next data to be send. /// - /// @param packet [in, out] a CPacket structure to fill + /// @param packet [out] a CPacket structure to fill + /// @param nexttime [out] Time when this socket should be next time picked up for processing. + /// @param src_addr [out] Source address to pass to channel's sendto /// - /// @return A pair of values is returned (is_payload_valid, timestamp). - /// If is_payload_valid is false, there was nothing packed for sending, - /// and the timestamp value should be ignored. - /// The timestamp is the full source/origin timestamp of the data. - std::pair packData(CPacket& packet); + /// @retval true A packet was extracted for sending, the socket should be rechecked at @a nexttime + /// @retval false Nothing was extracted for sending, @a nexttime should be ignored + bool packData(CPacket& packet, time_point& nexttime, sockaddr_any& src_addr); int processData(CUnit* unit); + + /// This function passes the incoming packet to the initial processing + /// (like packet filter) and is about to store it effectively to the + /// receiver buffer and do some postprocessing (decryption) if necessary + /// and report the status thereof. + /// + /// @param incoming [in] The packet coming from the network medium + /// @param w_new_inserted [out] Set false, if the packet already exists, otherwise true (packet added) + /// @param w_was_sent_in_order [out] Set false, if the packet was belated, but had no R flag set. + /// @param w_srt_loss_seqs [out] Gets inserted a loss, if this function has detected it. + /// + /// @return 0 The call was successful (regardless if the packet was accepted or not). + /// @return -1 The call has failed: no space left in the buffer. + /// @return -2 The incoming packet exceeds the expected sequence by more than a length of the buffer (irrepairable discrepancy). + int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); + + /// Get the packet's TSBPD time. + /// The @a grp passed by void* is not used yet + /// and shall not be used when ENABLE_BONDING=0. + time_point getPktTsbPdTime(void* grp, const CPacket& packet); + + /// Checks and spawns the TSBPD thread if required. + int checkLazySpawnTsbPdThread(); void processClose(); /// Process the request after receiving the handshake from caller. @@ -1075,12 +1115,7 @@ class CUDT static void addLossRecord(std::vector& lossrecord, int32_t lo, int32_t hi); int32_t bake(const sockaddr_any& addr, int32_t previous_cookie = 0, int correction = 0); - /// @brief Acknowledge reading position up to the @p seq. - /// Updates m_iRcvLastAck and m_iRcvLastSkipAck to @p seq. - /// @param seq first unacknowledged packet sequence number. - void ackDataUpTo(int32_t seq); - -#if ENABLE_BONDING && ENABLE_NEW_RCVBUFFER +#if ENABLE_BONDING /// @brief Drop packets in the recv buffer behind group_recv_base. /// Updates m_iRcvLastSkipAck if it's behind group_recv_base. void dropToGroupRecvBase(); @@ -1088,9 +1123,6 @@ class CUDT void processKeepalive(const CPacket& ctrlpkt, const time_point& tsArrival); - /// Locks m_RcvBufferLock and retrieves the available size of the receiver buffer. - SRT_ATTR_EXCLUDES(m_RcvBufferLock) - size_t getAvailRcvBufferSizeLock() const; /// Retrieves the available size of the receiver buffer. /// Expects that m_RcvBufferLock is locked. @@ -1120,10 +1152,12 @@ class CUDT static const int PACKETPAIR_MASK = 0xF; private: // Timers functions +#if ENABLE_BONDING time_point m_tsFreshActivation; // GROUPS: time of fresh activation of the link, or 0 if past the activation phase or idle time_point m_tsUnstableSince; // GROUPS: time since unexpected ACK delay experienced, or 0 if link seems healthy time_point m_tsWarySince; // GROUPS: time since an unstable link has first some response - +#endif + static const int BECAUSE_NO_REASON = 0, // NO BITS BECAUSE_ACK = 1 << 0, BECAUSE_LITEACK = 1 << 1, @@ -1142,6 +1176,7 @@ class CUDT CSndQueue* m_pSndQueue; // packet sending queue CRcvQueue* m_pRcvQueue; // packet receiving queue sockaddr_any m_PeerAddr; // peer address + sockaddr_any m_SourceAddr; // override UDP source address with this one when sending uint32_t m_piSelfIP[4]; // local UDP IP address CSNode* m_pSNode; // node information for UDT list used in snd queue CRNode* m_pRNode; // node information for UDT list used in rcv queue diff --git a/trunk/3rdparty/srt-1-fit/srtcore/crypto.cpp b/trunk/3rdparty/srt-1-fit/srtcore/crypto.cpp index 4740102980..fdd643f22b 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/crypto.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/crypto.cpp @@ -57,11 +57,18 @@ std::string KmStateStr(SRT_KM_STATE state) TAKE(SECURING); TAKE(NOSECRET); TAKE(BADSECRET); +#ifdef ENABLE_AEAD_API_PREVIEW + TAKE(BADCRYPTOMODE); +#endif #undef TAKE default: { char buf[256]; - sprintf(buf, "??? (%d)", state); +#if defined(_MSC_VER) && _MSC_VER < 1900 + _snprintf(buf, sizeof(buf) - 1, "??? (%d)", state); +#else + snprintf(buf, sizeof(buf), "??? (%d)", state); +#endif return buf; } } @@ -79,6 +86,15 @@ void srt::CCryptoControl::globalInit() #endif } +bool srt::CCryptoControl::isAESGCMSupported() +{ +#ifdef SRT_ENABLE_ENCRYPTION + return HaiCrypt_IsAESGCM_Supported() != 0; +#else + return false; +#endif +} + #if ENABLE_LOGGING std::string srt::CCryptoControl::FormatKmMessage(std::string hdr, int cmd, size_t srtlen) { @@ -112,7 +128,7 @@ void srt::CCryptoControl::createFakeSndContext() if (!m_iSndKmKeyLen) m_iSndKmKeyLen = 16; - if (!createCryptoCtx(m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX, (m_hSndCrypto))) + if (!createCryptoCtx((m_hSndCrypto), m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX, false)) { HLOGC(cnlog.Debug, log << "Error: Can't create fake crypto context for sending - sending will return ERROR!"); m_hSndCrypto = 0; @@ -139,7 +155,7 @@ int srt::CCryptoControl::processSrtMsg_KMREQ( // what has called this function. The HSv5 handshake only enforces bidirectional // connection. - bool bidirectional = hsv > CUDT::HS_VERSION_UDT4; + const bool bidirectional = hsv > CUDT::HS_VERSION_UDT4; // Local macro to return rejection appropriately. // CHANGED. The first version made HSv5 reject the connection. @@ -152,6 +168,10 @@ int srt::CCryptoControl::processSrtMsg_KMREQ( bool wasb4 SRT_ATR_UNUSED = false; size_t sek_len = 0; + const bool bUseGCM = + (m_iCryptoMode == CSrtConfig::CIPHER_MODE_AUTO && kmdata[HCRYPT_MSG_KM_OFS_CIPHER] == HCRYPT_CIPHER_AES_GCM) || + (m_iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM); + // What we have to do: // If encryption is on (we know that by having m_KmSecret nonempty), create // the crypto context (if bidirectional, create for both sending and receiving). @@ -199,13 +219,16 @@ int srt::CCryptoControl::processSrtMsg_KMREQ( } wasb4 = m_hRcvCrypto; - if (!createCryptoCtx(m_iRcvKmKeyLen, HAICRYPT_CRYPTO_DIR_RX, (m_hRcvCrypto))) + if (!createCryptoCtx((m_hRcvCrypto), m_iRcvKmKeyLen, HAICRYPT_CRYPTO_DIR_RX, bUseGCM)) { LOGC(cnlog.Error, log << "processSrtMsg_KMREQ: Can't create RCV CRYPTO CTX - must reject..."); m_RcvKmState = SRT_KM_S_NOSECRET; KMREQ_RESULT_REJECTION(); } + // Deduce resulting mode. + m_iCryptoMode = bUseGCM ? CSrtConfig::CIPHER_MODE_AES_GCM : CSrtConfig::CIPHER_MODE_AES_CTR; + if (!wasb4) { HLOGC(cnlog.Debug, log << "processSrtMsg_KMREQ: created RX ENC with KeyLen=" << m_iRcvKmKeyLen); @@ -230,6 +253,15 @@ int srt::CCryptoControl::processSrtMsg_KMREQ( w_srtlen = 1; LOGC(cnlog.Warn, log << "KMREQ/rcv: (snd) Rx process failure - BADSECRET"); break; + case HAICRYPT_ERROR_CIPHER: +#ifdef ENABLE_AEAD_API_PREVIEW + m_RcvKmState = m_SndKmState = SRT_KM_S_BADCRYPTOMODE; +#else + m_RcvKmState = m_SndKmState = SRT_KM_S_BADSECRET; // Use "bad secret" as a fallback. +#endif + w_srtlen = 1; + LOGC(cnlog.Warn, log << "KMREQ/rcv: (snd) Rx process failure - BADCRYPTOMODE"); + break; case HAICRYPT_ERROR: //Other errors default: m_RcvKmState = m_SndKmState = SRT_KM_S_NOSECRET; @@ -244,7 +276,6 @@ int srt::CCryptoControl::processSrtMsg_KMREQ( // until the next KMREQ is received as a key regeneration. m_bErrorReported = false; - if (w_srtlen == 1) goto HSv4_ErrorReport; @@ -379,6 +410,14 @@ int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len m_SndKmState = SRT_KM_S_UNSECURED; retstatus = 0; break; +#ifdef ENABLE_AEAD_API_PREVIEW + case SRT_KM_S_BADCRYPTOMODE: + // The peer expects to use a different cryptographic mode (e.g. AES-GCM, not AES-CTR). + m_RcvKmState = SRT_KM_S_BADCRYPTOMODE; + m_SndKmState = SRT_KM_S_BADCRYPTOMODE; + retstatus = -1; + break; +#endif default: LOGC(cnlog.Fatal, log << "processSrtMsg_KMRSP: IPE: unknown peer error state: " @@ -427,8 +466,9 @@ int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len return retstatus; } -void srt::CCryptoControl::sendKeysToPeer(CUDT* sock SRT_ATR_UNUSED, int iSRTT SRT_ATR_UNUSED, Whether2RegenKm regen SRT_ATR_UNUSED) +void srt::CCryptoControl::sendKeysToPeer(CUDT* sock SRT_ATR_UNUSED, int iSRTT SRT_ATR_UNUSED) { + sync::ScopedLock lck(m_mtxLock); if (!m_hSndCrypto || m_SndKmState == SRT_KM_S_UNSECURED) { HLOGC(cnlog.Debug, log << "sendKeysToPeer: NOT sending/regenerating keys: " @@ -461,21 +501,13 @@ void srt::CCryptoControl::sendKeysToPeer(CUDT* sock SRT_ATR_UNUSED, int iSRTT SR } } } - - - if (regen) - { - regenCryptoKm( - sock, // send UMSG_EXT + SRT_CMD_KMREQ to the peer using this socket - false // Do not apply the regenerated key to the to the receiver context - ); // regenerate and send - } #endif } -#ifdef SRT_ENABLE_ENCRYPTION -void srt::CCryptoControl::regenCryptoKm(CUDT* sock, bool bidirectional) +void srt::CCryptoControl::regenCryptoKm(CUDT* sock SRT_ATR_UNUSED, bool bidirectional SRT_ATR_UNUSED) { +#ifdef SRT_ENABLE_ENCRYPTION + sync::ScopedLock lck(m_mtxLock); if (!m_hSndCrypto) return; @@ -549,8 +581,8 @@ void srt::CCryptoControl::regenCryptoKm(CUDT* sock, bool bidirectional) if (sent) m_SndKmLastTime = srt::sync::steady_clock::now(); -} #endif +} srt::CCryptoControl::CCryptoControl(SRTSOCKET id) : m_SocketID(id) @@ -560,9 +592,9 @@ srt::CCryptoControl::CCryptoControl(SRTSOCKET id) , m_RcvKmState(SRT_KM_S_UNSECURED) , m_KmRefreshRatePkt(0) , m_KmPreAnnouncePkt(0) + , m_iCryptoMode(CSrtConfig::CIPHER_MODE_AUTO) , m_bErrorReported(false) { - m_KmSecret.len = 0; //send m_SndKmMsg[0].MsgLen = 0; @@ -587,6 +619,19 @@ bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool b // Set UNSECURED state as default m_RcvKmState = SRT_KM_S_UNSECURED; + m_iCryptoMode = cfg.iCryptoMode; + +#ifdef SRT_ENABLE_ENCRYPTION + if (!cfg.bTSBPD && m_iCryptoMode == CSrtConfig::CIPHER_MODE_AUTO) + m_iCryptoMode = CSrtConfig::CIPHER_MODE_AES_CTR; + const bool bUseGCM = m_iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM; + + if (bUseGCM && !isAESGCMSupported()) + { + LOGC(cnlog.Warn, log << "CCryptoControl: AES GCM is not supported by the crypto service provider."); + return false; + } +#endif // Set security-pending state, if a password was set. m_SndKmState = hasPassphrase() ? SRT_KM_S_SECURING : SRT_KM_S_UNSECURED; @@ -594,7 +639,7 @@ bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool b m_KmPreAnnouncePkt = cfg.uKmPreAnnouncePkt; m_KmRefreshRatePkt = cfg.uKmRefreshRatePkt; - if ( side == HSD_INITIATOR ) + if (side == HSD_INITIATOR) { if (hasPassphrase()) { @@ -605,13 +650,13 @@ bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool b m_iSndKmKeyLen = 16; } - bool ok = createCryptoCtx(m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX, (m_hSndCrypto)); + bool ok = createCryptoCtx((m_hSndCrypto), m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX, bUseGCM); HLOGC(cnlog.Debug, log << "CCryptoControl::init: creating SND crypto context: " << ok); if (ok && bidirectional) { m_iRcvKmKeyLen = m_iSndKmKeyLen; - int st = HaiCrypt_Clone(m_hSndCrypto, HAICRYPT_CRYPTO_DIR_RX, &m_hRcvCrypto); + const int st = HaiCrypt_Clone(m_hSndCrypto, HAICRYPT_CRYPTO_DIR_RX, &m_hRcvCrypto); HLOGC(cnlog.Debug, log << "CCryptoControl::init: creating CLONED RCV crypto context: status=" << st); ok = st == 0; } @@ -630,12 +675,14 @@ bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool b NULL, // Do not send the key (the KM msg will be attached to the HSv5 handshake) bidirectional // replicate the key to the receiver context, if bidirectional ); + + m_iCryptoMode = bUseGCM ? CSrtConfig::CIPHER_MODE_AES_GCM : CSrtConfig::CIPHER_MODE_AES_CTR; #else // This error would be a consequence of setting the passphrase, while encryption // is turned off at compile time. Setting the password itself should be not allowed // so this could only happen as a consequence of an IPE. LOGC(cnlog.Error, log << "CCryptoControl::init: IPE: encryption not supported"); - return true; + return false; #endif } else @@ -654,6 +701,7 @@ bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool b void srt::CCryptoControl::close() { /* Wipeout secrets */ + sync::ScopedLock lck(m_mtxLock); memset(&m_KmSecret, 0, sizeof(m_KmSecret)); } @@ -691,9 +739,8 @@ static std::string CryptoFlags(int flg) } // namespace srt #endif // ENABLE_HEAVY_LOGGING -bool srt::CCryptoControl::createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir cdir, HaiCrypt_Handle& w_hCrypto) +bool srt::CCryptoControl::createCryptoCtx(HaiCrypt_Handle& w_hCrypto, size_t keylen, HaiCrypt_CryptoDir cdir, bool bAESGCM) { - if (w_hCrypto) { // XXX You can check here if the existing handle represents @@ -715,7 +762,7 @@ bool srt::CCryptoControl::createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir cdir m_KmRefreshRatePkt = 2000; m_KmPreAnnouncePkt = 500; #endif - crypto_cfg.flags = HAICRYPT_CFG_F_CRYPTO | (cdir == HAICRYPT_CRYPTO_DIR_TX ? HAICRYPT_CFG_F_TX : 0); + crypto_cfg.flags = HAICRYPT_CFG_F_CRYPTO | (cdir == HAICRYPT_CRYPTO_DIR_TX ? HAICRYPT_CFG_F_TX : 0) | (bAESGCM ? HAICRYPT_CFG_F_GCM : 0); crypto_cfg.xport = HAICRYPT_XPT_SRT; crypto_cfg.cryspr = HaiCryptCryspr_Get_Instance(); crypto_cfg.key_len = (size_t)keylen; @@ -724,7 +771,6 @@ bool srt::CCryptoControl::createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir cdir crypto_cfg.km_refresh_rate_pkt = m_KmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : m_KmRefreshRatePkt; crypto_cfg.km_pre_announce_pkt = m_KmPreAnnouncePkt == 0 ? SRT_CRYPT_KM_PRE_ANNOUNCE : m_KmPreAnnouncePkt; crypto_cfg.secret = m_KmSecret; - //memcpy(&crypto_cfg.secret, &m_KmSecret, sizeof(crypto_cfg.secret)); HLOGC(cnlog.Debug, log << "CRYPTO CFG: flags=" << CryptoFlags(crypto_cfg.flags) << " xport=" << crypto_cfg.xport << " cryspr=" << crypto_cfg.cryspr << " keylen=" << crypto_cfg.key_len << " passphrase_length=" << crypto_cfg.secret.len); @@ -740,7 +786,7 @@ bool srt::CCryptoControl::createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir cdir return true; } #else -bool srt::CCryptoControl::createCryptoCtx(size_t, HaiCrypt_CryptoDir, HaiCrypt_Handle&) +bool srt::CCryptoControl::createCryptoCtx(HaiCrypt_Handle&, size_t, HaiCrypt_CryptoDir, bool) { return false; } @@ -754,6 +800,8 @@ srt::EncryptionStatus srt::CCryptoControl::encrypt(CPacket& w_packet SRT_ATR_UNU if ( getSndCryptoFlags() == EK_NOENC ) return ENCS_CLEAR; + // Note that in case of GCM the header has to zero Retransmitted Packet Flag (R). + // If TSBPD is disabled, timestamp also has to be zeroed. int rc = HaiCrypt_Tx_Data(m_hSndCrypto, ((uint8_t*)w_packet.getHeader()), ((uint8_t*)w_packet.m_pcData), w_packet.getLength()); if (rc < 0) { @@ -826,10 +874,10 @@ srt::EncryptionStatus srt::CCryptoControl::decrypt(CPacket& w_packet SRT_ATR_UNU return ENCS_FAILED; } - int rc = HaiCrypt_Rx_Data(m_hRcvCrypto, ((uint8_t *)w_packet.getHeader()), ((uint8_t *)w_packet.m_pcData), w_packet.getLength()); - if ( rc <= 0 ) + const int rc = HaiCrypt_Rx_Data(m_hRcvCrypto, ((uint8_t *)w_packet.getHeader()), ((uint8_t *)w_packet.m_pcData), w_packet.getLength()); + if (rc <= 0) { - LOGC(cnlog.Error, log << "decrypt ERROR (IPE): HaiCrypt_Rx_Data failure=" << rc << " - returning failed decryption"); + LOGC(cnlog.Note, log << "decrypt ERROR: HaiCrypt_Rx_Data failure=" << rc << " - returning failed decryption"); // -1: decryption failure // 0: key not received yet return ENCS_FAILED; diff --git a/trunk/3rdparty/srt-1-fit/srtcore/crypto.h b/trunk/3rdparty/srt-1-fit/srtcore/crypto.h index 2c2b352502..370d5529ca 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/crypto.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/crypto.h @@ -50,8 +50,6 @@ const size_t SRT_KMR_KMSTATE = 0; #define SRT_CMD_MAXSZ HCRYPT_MSG_KM_MAX_SZ /* Maximum SRT custom messages payload size (bytes) */ const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ/sizeof(uint32_t); -enum Whether2RegenKm {DONT_REGEN_KM = 0, REGEN_KM = 1}; - class CCryptoControl { SRTSOCKET m_SocketID; @@ -69,10 +67,12 @@ class CCryptoControl // putting the whole HaiCrypt_Cfg object here. int m_KmRefreshRatePkt; int m_KmPreAnnouncePkt; + int m_iCryptoMode; HaiCrypt_Secret m_KmSecret; //Key material shared secret // Sender sync::steady_clock::time_point m_SndKmLastTime; + sync::Mutex m_mtxLock; // A mutex to protect concurrent access to CCryptoControl. struct { unsigned char Msg[HCRYPT_MSG_KM_MAX_SZ]; size_t MsgLen; @@ -87,6 +87,8 @@ class CCryptoControl public: static void globalInit(); + static bool isAESGCMSupported(); + bool sendingAllowed() { // This function is called to state as to whether the @@ -109,15 +111,16 @@ class CCryptoControl return m_KmSecret.len > 0; } -private: -#ifdef SRT_ENABLE_ENCRYPTION - /// Regenerate cryptographic key material. + int getCryptoMode() const + { + return m_iCryptoMode; + } + + /// Regenerate cryptographic key material if needed. /// @param[in] sock If not null, the socket will be used to send the KM message to the peer (e.g. KM refresh). /// @param[in] bidirectional If true, the key material will be regenerated for both directions (receiver and sender). + SRT_ATTR_EXCLUDES(m_mtxLock) void regenCryptoKm(CUDT* sock, bool bidirectional); -#endif - -public: size_t KeyLen() { return m_iSndKmKeyLen; } @@ -205,13 +208,17 @@ class CCryptoControl std::string FormatKmMessage(std::string hdr, int cmd, size_t srtlen); bool init(HandshakeSide, const CSrtConfig&, bool); + SRT_ATTR_EXCLUDES(m_mtxLock) void close(); - /// @return True if the handshake is in progress. + /// (Re)send KM request to a peer on timeout. /// This function is used in: - /// - HSv4 (initial key material exchange - in HSv5 it's attached to handshake) - /// - case of key regeneration, which should be then exchanged again. - void sendKeysToPeer(CUDT* sock, int iSRTT, Whether2RegenKm regen); + /// - HSv4 (initial key material exchange - in HSv5 it's attached to handshake). + /// - The case of key regeneration (KM refresh), when a new key has to be sent again. + /// In this case the first sending happens in regenCryptoKm(..). This function + /// retransmits the KM request by timeout if not KM response has been received. + SRT_ATTR_EXCLUDES(m_mtxLock) + void sendKeysToPeer(CUDT* sock, int iSRTT); void setCryptoSecret(const HaiCrypt_Secret& secret) { @@ -224,7 +231,7 @@ class CCryptoControl m_iRcvKmKeyLen = keylen; } - bool createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir tx, HaiCrypt_Handle& rh); + bool createCryptoCtx(HaiCrypt_Handle& rh, size_t keylen, HaiCrypt_CryptoDir tx, bool bAESGCM); int getSndCryptoFlags() const { diff --git a/trunk/3rdparty/srt-1-fit/srtcore/epoll.cpp b/trunk/3rdparty/srt-1-fit/srtcore/epoll.cpp index 37d1d30dad..269b9ff593 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/epoll.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/epoll.cpp @@ -66,6 +66,7 @@ modified by #include "epoll.h" #include "logging.h" #include "udt.h" +#include "utilities.h" using namespace std; using namespace srt::sync; @@ -214,8 +215,8 @@ void srt::CEPoll::clear_ready_usocks(CEPollDesc& d, int direction) } } - for (size_t i = 0; i < cleared.size(); ++i) - d.removeSubscription(cleared[i]); + for (size_t j = 0; j < cleared.size(); ++j) + d.removeSubscription(cleared[j]); } int srt::CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) @@ -639,8 +640,8 @@ int srt::CEPoll::wait(const int eid, set* readfds, set* wr #ifdef LINUX const int max_events = ed.m_sLocals.size(); SRT_ASSERT(max_events > 0); - epoll_event ev[max_events]; - int nfds = ::epoll_wait(ed.m_iLocalID, ev, max_events, 0); + srt::FixedArray ev(max_events); + int nfds = ::epoll_wait(ed.m_iLocalID, ev.data(), ev.size(), 0); IF_HEAVY_LOGGING(const int prev_total = total); for (int i = 0; i < nfds; ++ i) @@ -660,23 +661,23 @@ int srt::CEPoll::wait(const int eid, set* readfds, set* wr #elif defined(BSD) || TARGET_OS_MAC struct timespec tmout = {0, 0}; - const int max_events = ed.m_sLocals.size(); + const int max_events = (int)ed.m_sLocals.size(); SRT_ASSERT(max_events > 0); - struct kevent ke[max_events]; + srt::FixedArray ke(max_events); - int nfds = kevent(ed.m_iLocalID, NULL, 0, ke, max_events, &tmout); + int nfds = kevent(ed.m_iLocalID, NULL, 0, ke.data(), (int)ke.size(), &tmout); IF_HEAVY_LOGGING(const int prev_total = total); for (int i = 0; i < nfds; ++ i) { if ((NULL != lrfds) && (ke[i].filter == EVFILT_READ)) { - lrfds->insert(ke[i].ident); + lrfds->insert((int)ke[i].ident); ++ total; } if ((NULL != lwfds) && (ke[i].filter == EVFILT_WRITE)) { - lwfds->insert(ke[i].ident); + lwfds->insert((int)ke[i].ident); ++ total; } } @@ -702,7 +703,7 @@ int srt::CEPoll::wait(const int eid, set* readfds, set* wr if (lwfds) FD_SET(*i, &rqwritefds); if ((int)*i > max_fd) - max_fd = *i; + max_fd = (int)*i; } IF_HEAVY_LOGGING(const int prev_total = total); diff --git a/trunk/3rdparty/srt-1-fit/srtcore/epoll.h b/trunk/3rdparty/srt-1-fit/srtcore/epoll.h index 7b0d941c8d..00d46ceb41 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/epoll.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/epoll.h @@ -69,8 +69,11 @@ class CUDTGroup; class CEPollDesc { +#ifdef __GNUG__ const int m_iID; // epoll ID - +#else + const int m_iID SRT_ATR_UNUSED; // epoll ID +#endif struct Wait; struct Notice: public SRT_EPOLL_EVENT diff --git a/trunk/3rdparty/srt-1-fit/srtcore/fec.cpp b/trunk/3rdparty/srt-1-fit/srtcore/fec.cpp index 6b9981abbc..a41e3a33bb 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/fec.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/fec.cpp @@ -345,13 +345,13 @@ void FECFilterBuiltin::ConfigureColumns(Container& which, int32_t isn) { offset = col + 1; // +1 because we want it for the next column HLOGC(pflog.Debug, log << "ConfigureColumns: [" << (col+1) << "]... (resetting to row 0: +" - << offset << " %" << CSeqNo::incseq(isn, offset) << ")"); + << offset << " %" << CSeqNo::incseq(isn, (int32_t)offset) << ")"); } else { offset += 1 + sizeRow(); HLOGC(pflog.Debug, log << "ConfigureColumns: [" << (col+1) << "] ... (continue +" - << offset << " %" << CSeqNo::incseq(isn, offset) << ")"); + << offset << " %" << CSeqNo::incseq(isn, (int32_t)offset) << ")"); } } } @@ -453,7 +453,9 @@ void FECFilterBuiltin::feedSource(CPacket& packet) HLOGC(pflog.Debug, log << "FEC:feedSource: %" << packet.getSeqNo() << " rowoff=" << baseoff << " column=" << vert_gx << " .base=%" << vert_base << " coloff=" << vert_off); - if (vert_off >= 0 && sizeCol() > 1) + // [[assert sizeCol() >= 2]]; // see the condition above. + + if (vert_off >= 0) { // BEWARE! X % Y with different signedness upgrades int to unsigned! @@ -468,7 +470,7 @@ void FECFilterBuiltin::feedSource(CPacket& packet) return; } - SRT_ASSERT(vert_off >= 0); + // [[assert vert_off >= 0]]; // this condition branch int vert_pos = vert_off / int(sizeRow()); HLOGC(pflog.Debug, log << "FEC:feedSource: %" << packet.getSeqNo() @@ -496,7 +498,6 @@ void FECFilterBuiltin::feedSource(CPacket& packet) } else { - HLOGC(pflog.Debug, log << "FEC:feedSource: %" << packet.getSeqNo() << " B:%" << baseoff << " H:*[" << horiz_pos << "] V(B=%" << vert_base << ")[col=" << vert_gx << "]" @@ -604,8 +605,8 @@ void FECFilterBuiltin::ClipData(Group& g, uint16_t length_net, uint8_t kflg, } // Fill the rest with zeros. When this packet is going to be - // recovered, the payload extraced from this process will have - // the maximum lenght, but it will be cut to the right length + // recovered, the payload extracted from this process will have + // the maximum length, but it will be cut to the right length // and these padding 0s taken out. for (size_t i = payload_size; i < payloadSize(); ++i) g.payload_clip[i] = g.payload_clip[i] ^ 0; @@ -1128,10 +1129,10 @@ static void DebugPrintCells(int32_t base, const std::deque& cells, size_t for ( ; i < cells.size(); i += row_size ) { std::ostringstream os; - os << "cell[" << i << "-" << (i+row_size-1) << "] %" << CSeqNo::incseq(base, i) << ":"; + os << "cell[" << i << "-" << (i+row_size-1) << "] %" << CSeqNo::incseq(base, (int32_t)i) << ":"; for (size_t y = 0; y < row_size; ++y) { - os << " " << CellMark(cells, i+y); + os << " " << CellMark(cells, (int)(i+y)); } LOGP(pflog.Debug, os.str()); } @@ -1568,11 +1569,10 @@ size_t FECFilterBuiltin::ExtendRows(size_t rowx) const size_t size_in_packets = rowx * numberCols(); const int n_series = int(rowx / numberRows()); - if (size_in_packets > rcvBufferSize() && n_series > 2) + if (CheckEmergencyShrink(n_series, size_in_packets)) { - HLOGC(pflog.Debug, log << "FEC: Emergency resize, rowx=" << rowx << " series=" << n_series + HLOGC(pflog.Debug, log << "FEC: DONE Emergency resize, rowx=" << rowx << " series=" << n_series << "npackets=" << size_in_packets << " exceeds buf=" << rcvBufferSize()); - EmergencyShrink(n_series); } // Create and configure next groups. @@ -1700,8 +1700,27 @@ bool FECFilterBuiltin::IsLost(int32_t seq) const return rcv.cells[offset]; } -void FECFilterBuiltin::EmergencyShrink(size_t n_series) +bool FECFilterBuiltin::CheckEmergencyShrink(size_t n_series, size_t size_in_packets) { + // The minimum required size of the covered sequence range must be such + // that groups for packets from the previous range must be still reachable. + // It's then "this and previous" series in case of even arrangement. + // + // For staircase arrangement the potential range for a single column series + // (number of columns equal to a row size) spans for 2 matrices (rows * cols) + // minus one row. As dismissal is only allowed to be done by one full series + // of rows and columns, the history must keep as many groups as needed to reach + // out for this very packet of this group and all packets in the same row. + // Hence we need two series of columns to cover a similar range as two row, twice. + + const size_t min_series_history = m_arrangement_staircase ? 4 : 2; + + if (n_series <= min_series_history) + return false; + + if (size_in_packets < rcvBufferSize() && n_series < SRT_FEC_MAX_RCV_HISTORY) + return false; + // Shrink is required in order to prepare place for // either vertical or horizontal group in series `n_series`. @@ -1782,7 +1801,7 @@ void FECFilterBuiltin::EmergencyShrink(size_t n_series) else { HLOGC(pflog.Debug, log << "FEC: Shifting rcv row %" << oldbase << " -> %" << newbase); - rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.end() + shift_rows); + rcv.rowq.erase(rcv.rowq.begin(), rcv.rowq.begin() + shift_rows); } const size_t shift_cols = shift_series * numberCols(); @@ -1816,6 +1835,8 @@ void FECFilterBuiltin::EmergencyShrink(size_t n_series) rcv.cells.push_back(false); } rcv.cell_base = newbase; + + return true; } FECFilterBuiltin::EHangStatus FECFilterBuiltin::HangVertical(const CPacket& rpkt, signed char fec_col, loss_seqs_t& irrecover) @@ -1932,7 +1953,7 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t { HLOGC(pflog.Debug, log << "FEC/V: ... [" << i << "] base=%" << pg.base << " TOO EARLY (last=%" - << CSeqNo::incseq(pg.base, (sizeCol()-1)*sizeRow()) + << CSeqNo::incseq(pg.base, (int32_t)((sizeCol()-1)*sizeRow())) << ")"); continue; } @@ -1943,7 +1964,7 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t HLOGC(pflog.Debug, log << "FEC/V: ... [" << i << "] base=%" << pg.base << " - PAST last=%" - << CSeqNo::incseq(pg.base, (sizeCol()-1)*sizeRow()) + << CSeqNo::incseq(pg.base, (int32_t)((sizeCol()-1)*sizeRow())) << " - collecting losses."); pg.dismissed = true; // mark irrecover already collected @@ -2489,11 +2510,11 @@ size_t FECFilterBuiltin::ExtendColumns(size_t colgx) // of packets as many as the number of rows, so simply multiply this. const size_t size_in_packets = colgx * numberRows(); const size_t n_series = colgx / numberCols(); - if (size_in_packets > rcvBufferSize()/2 || n_series > SRT_FEC_MAX_RCV_HISTORY) + + if (CheckEmergencyShrink(n_series, size_in_packets)) { - HLOGC(pflog.Debug, log << "FEC: Emergency resize, colgx=" << colgx << " series=" << n_series + HLOGC(pflog.Debug, log << "FEC: DONE Emergency resize, colgx=" << colgx << " series=" << n_series << "npackets=" << size_in_packets << " exceeds buf=" << rcvBufferSize()); - EmergencyShrink(n_series); } else { diff --git a/trunk/3rdparty/srt-1-fit/srtcore/fec.h b/trunk/3rdparty/srt-1-fit/srtcore/fec.h index 71a6adfa7d..0297094752 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/fec.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/fec.h @@ -47,7 +47,7 @@ class FECFilterBuiltin: public SrtPacketFilterBase size_t drop; //< by how much the sequence should increase to get to the next series size_t collected; //< how many packets were taken to collect the clip - Group(): base(CSeqNo::m_iMaxSeqNo), step(0), drop(0), collected(0) + Group(): base(SRT_SEQNO_NONE), step(0), drop(0), collected(0) { } @@ -87,7 +87,7 @@ class FECFilterBuiltin: public SrtPacketFilterBase #if ENABLE_HEAVY_LOGGING std::string DisplayStats() { - if (base == CSeqNo::m_iMaxSeqNo) + if (base == SRT_SEQNO_NONE) return "UNINITIALIZED!!!"; std::ostringstream os; @@ -222,7 +222,7 @@ class FECFilterBuiltin: public SrtPacketFilterBase void RcvRebuild(Group& g, int32_t seqno, Group::Type tp); int32_t RcvGetLossSeqHoriz(Group& g); int32_t RcvGetLossSeqVert(Group& g); - void EmergencyShrink(size_t n_series); + bool CheckEmergencyShrink(size_t n_series, size_t size_in_packets); static void TranslateLossRecords(const std::set& loss, loss_seqs_t& irrecover); void RcvCheckDismissColumn(int32_t seqno, int colgx, loss_seqs_t& irrecover); diff --git a/trunk/3rdparty/srt-1-fit/srtcore/filelist.maf b/trunk/3rdparty/srt-1-fit/srtcore/filelist.maf index 560a0463b3..512e3524d2 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/filelist.maf +++ b/trunk/3rdparty/srt-1-fit/srtcore/filelist.maf @@ -2,8 +2,9 @@ SOURCES api.cpp -buffer.cpp +buffer_snd.cpp buffer_rcv.cpp +buffer_tools.cpp cache.cpp channel.cpp common.cpp @@ -53,8 +54,9 @@ udt.h PRIVATE HEADERS api.h -buffer.h +buffer_snd.h buffer_rcv.h +buffer_tools.h cache.h channel.h common.h diff --git a/trunk/3rdparty/srt-1-fit/srtcore/group.cpp b/trunk/3rdparty/srt-1-fit/srtcore/group.cpp index 5975cc9ae2..001dd4802d 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/group.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/group.cpp @@ -226,12 +226,12 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data) data.sndstate = SRT_GST_PENDING; data.rcvstate = SRT_GST_PENDING; - HLOGC(gmlog.Debug, log << "CUDTGroup::add: adding new member @" << data.id); + LOGC(gmlog.Note, log << "group/add: adding member @" << data.id << " into group $" << id()); m_Group.push_back(data); gli_t end = m_Group.end(); if (m_iMaxPayloadSize == -1) { - int plsize = data.ps->core().OPT_PayloadSize(); + int plsize = (int)data.ps->core().OPT_PayloadSize(); HLOGC(gmlog.Debug, log << "CUDTGroup::add: taking MAX payload size from socket @" << data.ps->m_SocketID << ": " << plsize << " " << (plsize ? "(explicit)" : "(unspecified = fallback to 1456)")); @@ -251,7 +251,6 @@ CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) : m_Global(CUDT::uglobal()) , m_GroupID(-1) , m_PeerGroupID(-1) - , m_bSyncOnMsgNo(false) , m_type(gtype) , m_listener() , m_iBusy() @@ -336,6 +335,7 @@ void CUDTGroup::GroupContainer::erase(CUDTGroup::gli_t it) } } m_List.erase(it); + --m_SizeCache; } void CUDTGroup::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) @@ -394,12 +394,6 @@ void CUDTGroup::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) break; - case SRTO_CONGESTION: - // Currently no socket groups allow any other - // congestion control mode other than live. - LOGP(gmlog.Error, "group option: SRTO_CONGESTION is only allowed as 'live' and cannot be changed"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - default: break; } @@ -554,7 +548,7 @@ void CUDTGroup::deriveSettings(CUDT* u) if (u->m_config.CryptoSecret.len) { string password((const char*)u->m_config.CryptoSecret.str, u->m_config.CryptoSecret.len); - m_config.push_back(ConfigItem(SRTO_PASSPHRASE, password.c_str(), password.size())); + m_config.push_back(ConfigItem(SRTO_PASSPHRASE, password.c_str(), (int)password.size())); } IM(SRTO_KMREFRESHRATE, uKmRefreshRatePkt); @@ -563,7 +557,7 @@ void CUDTGroup::deriveSettings(CUDT* u) string cc = u->m_CongCtl.selected_name(); if (cc != "live") { - m_config.push_back(ConfigItem(SRTO_CONGESTION, cc.c_str(), cc.size())); + m_config.push_back(ConfigItem(SRTO_CONGESTION, cc.c_str(), (int)cc.size())); } // NOTE: This is based on information extracted from the "semi-copy-constructor" of CUDT class. @@ -893,6 +887,16 @@ void CUDTGroup::close() HLOGC(smlog.Debug, log << "group/close: IPE(NF): group member @" << ig->id << " already deleted"); continue; } + + // Make the socket closing BEFORE withdrawing its group membership + // because a socket created as a group member cannot be valid + // without the group. + // This is not true in case of non-managed groups, which + // only collect sockets, but also non-managed groups should not + // use common group buffering and tsbpd. Also currently there are + // no other groups than managed one. + s->setClosing(); + s->m_GroupOf = NULL; s->m_GroupMemberData = NULL; HLOGC(smlog.Debug, log << "group/close: CUTTING OFF @" << ig->id << " (found as @" << s->m_SocketID << ") from the group"); @@ -1219,12 +1223,10 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // and therefore take over the leading role in setting the ISN. If the // second one fails, too, then the only remaining idle link will simply // go with its own original sequence. - // - // On the opposite side the reader should know that the link is inactive - // so the first received payload activates it. Activation of an idle link - // means that the very first packet arriving is TAKEN AS A GOOD DEAL, that is, - // no LOSSREPORT is sent even if the sequence looks like a "jumped over". - // Only for activated links is the LOSSREPORT sent upon seqhole detection. + + // On the opposite side, if the first packet arriving looks like a jump over, + // the corresponding LOSSREPORT is sent. For packets that are truly lost, + // the sender retransmits them, for packets that before ISN, DROPREQ is sent. // Now we can go to the idle links and attempt to send the payload // also over them. @@ -1714,7 +1716,7 @@ int CUDTGroup::getGroupData_LOCKED(SRT_SOCKGROUPDATA* pdata, size_t* psize) copyGroupData(*d, (pdata[i])); } - return m_Group.size(); + return (int)m_Group.size(); } // [[using locked(this->m_GroupLock)]] @@ -1888,7 +1890,7 @@ void CUDTGroup::recv_CollectAliveAndBroken(vector& alive, set::const_iterator si = readySockets.begin(); si != readySockets.end(); ++si) { CUDTSocket* ps = *si; @@ -2215,7 +2216,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } } - const CRcvBufferNew::PacketInfo info = + const CRcvBuffer::PacketInfo info = ps->core().m_pRcvBuffer->getFirstReadablePacketInfo(steady_clock::now()); if (info.seqno == SRT_SEQNO_NONE) { @@ -2292,13 +2293,14 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) m_stats.recv.count(res); updateAvgPayloadSize(res); + bool canReadFurther = false; for (vector::const_iterator si = aliveMembers.begin(); si != aliveMembers.end(); ++si) { CUDTSocket* ps = *si; ScopedLock lg(ps->core().m_RcvBufferLock); if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) { - int cnt = ps->core().rcvDropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); + const int cnt = ps->core().rcvDropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); if (cnt > 0) { HLOGC(grlog.Debug, @@ -2306,596 +2308,22 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) << " packets after reading: m_RcvBaseSeqNo=" << m_RcvBaseSeqNo); } } - } - for (vector::const_iterator si = aliveMembers.begin(); si != aliveMembers.end(); ++si) - { - CUDTSocket* ps = *si; - if (!ps->core().isRcvBufferReady()) + + if (!ps->core().isRcvBufferReadyNoLock()) m_Global.m_EPoll.update_events(ps->m_SocketID, ps->core().m_sPollID, SRT_EPOLL_IN, false); + else + canReadFurther = true; } + if (!canReadFurther) + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + return res; } LOGC(grlog.Error, log << "grp/recv: UNEXPECTED RUN PATH, ABANDONING."); m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); } -#else -// The "app reader" version of the reading function. -// This reads the packets from every socket treating them as independent -// and prepared to work with the application. Then packets are sorted out -// by getting the sequence number. -int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) -{ - typedef map::iterator pit_t; - // Later iteration over it might be less efficient than - // by vector, but we'll also often try to check a single id - // if it was ever seen broken, so that it's skipped. - set broken; - size_t output_size = 0; - - // First, acquire GlobControlLock to make sure all member sockets still exist - enterCS(m_Global.m_GlobControlLock); - ScopedLock guard(m_GroupLock); - - if (m_bClosing) - { - // The group could be set closing in the meantime, but if - // this is only about to be set by another thread, this thread - // must fist wait for being able to acquire this lock. - // The group will not be deleted now because it is added usage counter - // by this call, but will be released once it exits. - leaveCS(m_Global.m_GlobControlLock); - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - } - - // Now, still under lock, check if all sockets still can be dispatched - send_CheckValidSockets(); - leaveCS(m_Global.m_GlobControlLock); - - if (m_bClosing) - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - - for (;;) - { - if (!m_bOpened || !m_bConnected) - { - LOGC(grlog.Error, - log << boolalpha << "group/recv: ERROR opened=" << m_bOpened << " connected=" << m_bConnected); - throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); - } - - // Check first the ahead packets if you have any to deliver. - if (m_RcvBaseSeqNo != SRT_SEQNO_NONE && !m_Positions.empty()) - { - // This function also updates the group sequence pointer. - ReadPos* pos = checkPacketAhead(); - if (pos) - { - if (size_t(len) < pos->packet.size()) - throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0); - - HLOGC(grlog.Debug, - log << "group/recv: delivering AHEAD packet %" << pos->mctrl.pktseq << " #" << pos->mctrl.msgno - << ": " << BufferStamp(&pos->packet[0], pos->packet.size())); - memcpy(buf, &pos->packet[0], pos->packet.size()); - fillGroupData((w_mc), pos->mctrl); - m_RcvBaseSeqNo = pos->mctrl.pktseq; - len = pos->packet.size(); - pos->packet.clear(); - - // Update stats as per delivery - m_stats.recv.count(len); - updateAvgPayloadSize(len); - - // We predict to have only one packet ahead, others are pending to be reported by tsbpd. - // This will be "re-enabled" if the later check puts any new packet into ahead. - m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); - - return len; - } - } - - // LINK QUALIFICATION NAMES: - // - // HORSE: Correct link, which delivers the very next sequence. - // Not necessarily this link is currently active. - // - // KANGAROO: Got some packets dropped and the sequence number - // of the packet jumps over the very next sequence and delivers - // an ahead packet. - // - // ELEPHANT: Is not ready to read, while others are, or reading - // up to the current latest delivery sequence number does not - // reach this sequence and the link becomes non-readable earlier. - - // The above condition has ruled out one kangaroo and turned it - // into a horse. - - // Below there's a loop that will try to extract packets. Kangaroos - // will be among the polled ones because skipping them risks that - // the elephants will take over the reading. Links already known as - // elephants will be also polled in an attempt to revitalize the - // connection that experienced just a short living choking. - // - // After polling we attempt to read from every link that reported - // read-readiness and read at most up to the sequence equal to the - // current delivery sequence. - - // Links that deliver a packet below that sequence will be retried - // until they deliver no more packets or deliver the packet of - // expected sequence. Links that don't have a record in m_Positions - // and report readiness will be always read, at least to know what - // sequence they currently stand on. - // - // Links that are already known as kangaroos will be polled, but - // no reading attempt will be done. If after the reading series - // it will turn out that we have no more horses, the slowest kangaroo - // will be "upgraded to a horse" (the ahead link with a sequence - // closest to the current delivery sequence will get its sequence - // set as current delivered and its recorded ahead packet returned - // as the read packet). - - // If we find at least one horse, the packet read from that link - // will be delivered. All other link will be just ensured update - // up to this sequence number, or at worst all available packets - // will be read. In this case all kangaroos remain kangaroos, - // until the current delivery sequence m_RcvBaseSeqNo will be lifted - // to the sequence recorded for these links in m_Positions, - // during the next time ahead check, after which they will become - // horses. - - const size_t size = m_Group.size(); - - // Prepare first the list of sockets to be added as connect-pending - // and as read-ready, then unlock the group, and then add them to epoll. - vector aliveMembers; - recv_CollectAliveAndBroken(aliveMembers, broken); - - const vector ready_sockets = recv_WaitForReadReady(aliveMembers, broken); - // m_GlobControlLock lifted, m_GroupLock still locked. - // Now we can safely do this scoped way. - - if (!m_bSynRecving && ready_sockets.empty()) - { - HLOGC(grlog.Debug, - log << "group/rcv $" << m_GroupID << ": Not available AT THIS TIME, NOT READ-READY now."); - m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); - throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); - } - - // Ok, now we need to have some extra qualifications: - // 1. If a socket has no registry yet, we read anyway, just - // to notify the current position. We read ONLY ONE PACKET this time, - // we'll worry later about adjusting it to the current group sequence - // position. - // 2. If a socket is already position ahead, DO NOT read from it, even - // if it is ready. - - // The state of things whether we were able to extract the very next - // sequence will be simply defined by the fact that `output` is nonempty. - - int32_t next_seq = m_RcvBaseSeqNo; - - if (m_bClosing) - { - HLOGC(gslog.Debug, log << "grp/sendBroadcast: GROUP CLOSED, ABANDONING"); - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - } - // - // NOTE: Although m_GlobControlLock is lifted here so potentially sockets - // colected in ready_sockets could be closed at any time, all of them are member - // sockets of this group. Therefore the first socket attempted to be closed will - // have to remove the socket from the group, and this will require lock on GroupLock, - // which is still applied here. So this will have to wait for this function to finish - // (or block on swait, in which case the lock is lifted) anyway. - - for (vector::const_iterator si = ready_sockets.begin(); si != ready_sockets.end(); ++si) - { - CUDTSocket* ps = *si; - SRTSOCKET id = ps->m_SocketID; - ReadPos* p = NULL; - pit_t pe = m_Positions.find(id); - if (pe != m_Positions.end()) - { - p = &pe->second; - - // Possible results of comparison: - // x < 0: the sequence is in the past, the socket should be adjusted FIRST - // x = 0: the socket should be ready to get the exactly next packet - // x = 1: the case is already handled by GroupCheckPacketAhead. - // x > 1: AHEAD. DO NOT READ. - const int seqdiff = CSeqNo::seqcmp(p->mctrl.pktseq, m_RcvBaseSeqNo); - if (seqdiff > 1) - { - HLOGC(grlog.Debug, - log << "group/recv: EPOLL: @" << id << " %" << p->mctrl.pktseq << " AHEAD %" << m_RcvBaseSeqNo - << ", not reading."); - continue; - } - } - else - { - // The position is not known, so get the position on which - // the socket is currently standing. - pair ee = m_Positions.insert(make_pair(id, ReadPos(ps->core().m_iRcvLastSkipAck))); - p = &(ee.first->second); - HLOGC(grlog.Debug, - log << "group/recv: EPOLL: @" << id << " %" << p->mctrl.pktseq << " NEW SOCKET INSERTED"); - } - - // Read from this socket stubbornly, until: - // - reading is no longer possible (AGAIN) - // - the sequence difference is >= 1 - - for (;;) - { - SRT_MSGCTRL mctrl = srt_msgctrl_default; - - // Read the data into the user's buffer. This is an optimistic - // prediction that we'll read the right data. This will be overwritten - // by "more correct data" if found more appropriate later. But we have to - // copy these data anyway anywhere, even if they need to fall on the floor later. - int stat; - char extrabuf[SRT_LIVE_MAX_PLSIZE]; - char* msgbuf = NULL; - if (output_size) - { - // We already have the target data in `buf`. Now reading extra data potentially redundant (to be ignored) - // or AHEAD (to be buffered internally by the group) - msgbuf = extrabuf; - stat = ps->core().receiveMessage((extrabuf), SRT_LIVE_MAX_PLSIZE, (mctrl), CUDTUnited::ERH_RETURN); - HLOGC(grlog.Debug, - log << "group/recv: @" << id << " EXTRACTED EXTRA data with %" << mctrl.pktseq - << " #" << mctrl.msgno << ": " << (stat <= 0 ? "(NOTHING)" : BufferStamp(extrabuf, stat)) - << (CSeqNo::seqcmp(mctrl.pktseq, m_RcvBaseSeqNo) > 1 ? " - TO STORE" : " - TO IGNORE")); - } - else - { - msgbuf = buf; - stat = ps->core().receiveMessage((buf), len, (mctrl), CUDTUnited::ERH_RETURN); - HLOGC(grlog.Debug, - log << "group/recv: @" << id << " EXTRACTED data with %" << mctrl.pktseq << " #" - << mctrl.msgno << ": " << (stat <= 0 ? "(NOTHING)" : BufferStamp(buf, stat))); - } - if (stat == 0) - { - HLOGC(grlog.Debug, log << "group/recv @" << id << ": SPURIOUS epoll, ignoring"); - // This is returned in case of "again". In case of errors, we have SRT_ERROR. - // Do not treat this as spurious, just stop reading. - break; - } - - if (stat == SRT_ERROR) - { - HLOGC(grlog.Debug, log << "group/recv: @" << id << ": " << srt_getlasterror_str()); - broken.insert(ps); - break; - } - - // NOTE: checks against m_RcvBaseSeqNo and decisions based on it - // must NOT be done if m_RcvBaseSeqNo is SRT_SEQNO_NONE, which - // means that we are about to deliver the very first packet and we - // take its sequence number as a good deal. - - // The order must be: - // - check discrepancy - // - record the sequence - // - check ordering. - // The second one must be done always, but failed discrepancy - // check should exclude the socket from any further checks. - // That's why the common check for m_RcvBaseSeqNo != SRT_SEQNO_NONE can't - // embrace everything below. - - // We need to first qualify the sequence, just for a case - if (m_RcvBaseSeqNo != SRT_SEQNO_NONE && !isValidSeqno(m_RcvBaseSeqNo, mctrl.pktseq)) - { - // This error should be returned if the link turns out - // to be the only one, or set to the group data. - // err = SRT_ESECFAIL; - LOGC(grlog.Error, - log << "group/recv: @" << id << ": SEQUENCE DISCREPANCY: base=%" << m_RcvBaseSeqNo - << " vs pkt=%" << mctrl.pktseq << ", setting ESECFAIL"); - broken.insert(ps); - break; - } - - // Rewrite it to the state for a case when next reading - // would not succeed. Do not insert the buffer here because - // this is only required when the sequence is ahead; for that - // it will be fixed later. - p->mctrl.pktseq = mctrl.pktseq; - - if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) - { - // Now we can safely check it. - const int seqdiff = CSeqNo::seqcmp(mctrl.pktseq, m_RcvBaseSeqNo); - - if (seqdiff <= 0) - { - HLOGC(grlog.Debug, - log << "group/recv: @" << id << " %" << mctrl.pktseq << " #" << mctrl.msgno - << " BEHIND base=%" << m_RcvBaseSeqNo << " - discarding"); - // The sequence is recorded, the packet has to be discarded. - m_stats.recvDiscard.count(stat); - continue; - } - - // Now we have only two possibilities: - // seqdiff == 1: The very next sequence, we want to read and return the packet. - // seqdiff > 1: The packet is ahead - record the ahead packet, but continue with the others. - - if (seqdiff > 1) - { - HLOGC(grlog.Debug, - log << "@" << id << " %" << mctrl.pktseq << " #" << mctrl.msgno << " AHEAD base=%" - << m_RcvBaseSeqNo); - p->packet.assign(msgbuf, msgbuf + stat); - p->mctrl = mctrl; - break; // Don't read from that socket anymore. - } - } - - // We have seqdiff = 1, or we simply have the very first packet - // which's sequence is taken as a good deal. Update the sequence - // and record output. - - if (output_size) - { - HLOGC(grlog.Debug, - log << "group/recv: @" << id << " %" << mctrl.pktseq << " #" << mctrl.msgno << " REDUNDANT"); - break; - } - - HLOGC(grlog.Debug, - log << "group/recv: @" << id << " %" << mctrl.pktseq << " #" << mctrl.msgno << " DELIVERING"); - output_size = stat; - fillGroupData((w_mc), mctrl); - - // Update stats as per delivery - m_stats.recv.count(output_size); - updateAvgPayloadSize(output_size); - - // Record, but do not update yet, until all sockets are handled. - next_seq = mctrl.pktseq; - break; - } - } - -#if ENABLE_HEAVY_LOGGING - if (!broken.empty()) - { - std::ostringstream brks; - for (set::iterator b = broken.begin(); b != broken.end(); ++b) - brks << "@" << (*b)->m_SocketID << " "; - LOGC(grlog.Debug, log << "group/recv: REMOVING BROKEN: " << brks.str()); - } -#endif - - vector brokenid; - // Now remove all broken sockets from aheads, if any. - // Even if they have already delivered a packet. - for (set::iterator di = broken.begin(); di != broken.end(); ++di) - { - CUDTSocket* ps = *di; - m_Positions.erase(ps->m_SocketID); - //ps->setBrokenClosed(); - } - - // Force closing - { - InvertedLock ung (m_GroupLock); - for (set::iterator b = broken.begin(); b != broken.end(); ++b) - { - CUDT::uglobal().close(*b); - } - } - - if (broken.size() >= size) // This > is for sanity check - { - // All broken - HLOGC(grlog.Debug, log << "group/recv: All sockets broken"); - m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); - - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - } - - // May be required to be re-read. - broken.clear(); - - if (output_size) - { - // We have extracted something, meaning that we have the sequence shift. - // Update it now and don't do anything else with the sockets. - - // Sanity check - if (next_seq == SRT_SEQNO_NONE) - { - LOGP(grlog.Error, "IPE: next_seq not set after output extracted!"); - - // This should never happen, but the only way to keep the code - // safe an recoverable is to use the incremented sequence. By - // leaving the sequence as is there's a risk of hangup. - // Not doing it in case of SRT_SEQNO_NONE as it would make a valid %0. - if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) - m_RcvBaseSeqNo = CSeqNo::incseq(m_RcvBaseSeqNo); - } - else - { - m_RcvBaseSeqNo = next_seq; - } - - const ReadPos* pos = checkPacketAhead(); - if (!pos) - { - // Don't clear the read-readinsess state if you have a packet ahead because - // if you have, the next read call will return it. - m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); - } - - HLOGC(grlog.Debug, - log << "group/recv: successfully extracted packet size=" << output_size << " - returning"); - return output_size; - } - - HLOGC(grlog.Debug, log << "group/recv: NOT extracted anything - checking for a need to kick kangaroos"); - - // Check if we have any sockets left :D - - // Here we surely don't have any more HORSES, - // only ELEPHANTS and KANGAROOS. Qualify them and - // attempt to at least take advantage of KANGAROOS. - - // In this position all links are either: - // - updated to the current position - // - updated to the newest possible possition available - // - not yet ready for extraction (not present in the group) - - // If we haven't extracted the very next sequence position, - // it means that we might only have the ahead packets read, - // that is, the next sequence has been dropped by all links. - - if (!m_Positions.empty()) - { - // This might notify both lingering links, which didn't - // deliver the required sequence yet, and links that have - // the sequence ahead. Review them, and if you find at - // least one packet behind, just wait for it to be ready. - // Use again the waiting function because we don't want - // the general waiting procedure to skip others. - set elephants; - - // const because it's `typename decltype(m_Positions)::value_type` - pair* slowest_kangaroo = 0; - - for (pit_t rp = m_Positions.begin(); rp != m_Positions.end(); ++rp) - { - // NOTE that m_RcvBaseSeqNo in this place wasn't updated - // because we haven't successfully extracted anything. - int seqdiff = CSeqNo::seqcmp(rp->second.mctrl.pktseq, m_RcvBaseSeqNo); - if (seqdiff < 0) - { - elephants.insert(rp->first); - } - // If seqdiff == 0, we have a socket ON TRACK. - else if (seqdiff > 0) - { - // If there's already a slowest_kangaroo, seqdiff decides if this one is slower. - // Otherwise it is always slower by having no competition. - seqdiff = slowest_kangaroo - ? CSeqNo::seqcmp(slowest_kangaroo->second.mctrl.pktseq, rp->second.mctrl.pktseq) - : 1; - if (seqdiff > 0) - { - slowest_kangaroo = &*rp; - } - } - } - - // Note that if no "slowest_kangaroo" was found, it means - // that we don't have kangaroos. - if (slowest_kangaroo) - { - // We have a slowest kangaroo. Elephants must be ignored. - // Best case, they will get revived, worst case they will be - // soon broken. - // - // As we already have the packet delivered by the slowest - // kangaroo, we can simply return it. - - // Check how many were skipped and add them to the stats - const int32_t jump = (CSeqNo(slowest_kangaroo->second.mctrl.pktseq) - CSeqNo(m_RcvBaseSeqNo)) - 1; - if (jump > 0) - { - m_stats.recvDrop.count(stats::BytesPackets(jump * static_cast(avgRcvPacketSize()), jump)); - LOGC(grlog.Warn, - log << "@" << m_GroupID << " GROUP RCV-DROPPED " << jump << " packet(s): seqno %" - << m_RcvBaseSeqNo << " to %" << slowest_kangaroo->second.mctrl.pktseq); - } - - m_RcvBaseSeqNo = slowest_kangaroo->second.mctrl.pktseq; - vector& pkt = slowest_kangaroo->second.packet; - if (size_t(len) < pkt.size()) - throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0); - - HLOGC(grlog.Debug, - log << "@" << slowest_kangaroo->first << " KANGAROO->HORSE %" - << slowest_kangaroo->second.mctrl.pktseq << " #" << slowest_kangaroo->second.mctrl.msgno - << ": " << BufferStamp(&pkt[0], pkt.size())); - - memcpy(buf, &pkt[0], pkt.size()); - fillGroupData((w_mc), slowest_kangaroo->second.mctrl); - len = pkt.size(); - pkt.clear(); - - // Update stats as per delivery - m_stats.recv.count(len); - updateAvgPayloadSize(len); - - // It is unlikely to have a packet ahead because usually having one packet jumped-ahead - // clears the possibility of having aheads at all. - // XXX Research if this is possible at all; if it isn't, then don't waste time on - // looking for it. - const ReadPos* pos = checkPacketAhead(); - if (!pos) - { - // Don't clear the read-readinsess state if you have a packet ahead because - // if you have, the next read call will return it. - m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); - } - return len; - } - - HLOGC(grlog.Debug, - log << "group/recv: " - << (elephants.empty() ? "NO LINKS REPORTED ANY FRESHER PACKET." : "ALL LINKS ELEPHANTS.") - << " Re-polling."); - } - else - { - HLOGC(grlog.Debug, log << "group/recv: POSITIONS EMPTY - Re-polling."); - } - } -} -#endif - -// [[using locked(m_GroupLock)]] -CUDTGroup::ReadPos* CUDTGroup::checkPacketAhead() -{ - typedef map::iterator pit_t; - ReadPos* out = 0; - - // This map no longer maps only ahead links. - // Here are all links, and whether ahead, it's defined by the sequence. - for (pit_t i = m_Positions.begin(); i != m_Positions.end(); ++i) - { - // i->first: socket ID - // i->second: ReadPos { sequence, packet } - // We are not interested with the socket ID because we - // aren't going to read from it - we have the packet already. - ReadPos& a = i->second; - - const int seqdiff = CSeqNo::seqcmp(a.mctrl.pktseq, m_RcvBaseSeqNo); - if (seqdiff == 1) - { - // The very next packet. Return it. - HLOGC(grlog.Debug, - log << "group/recv: Base %" << m_RcvBaseSeqNo << " ahead delivery POSSIBLE %" << a.mctrl.pktseq - << " #" << a.mctrl.msgno << " from @" << i->first << ")"); - out = &a; - } - else if (seqdiff < 1 && !a.packet.empty()) - { - HLOGC(grlog.Debug, - log << "group/recv: @" << i->first << " dropping collected ahead %" << a.mctrl.pktseq << "#" - << a.mctrl.msgno << " with base %" << m_RcvBaseSeqNo); - a.packet.clear(); - } - // In case when it's >1, keep it in ahead - } - - return out; -} const char* CUDTGroup::StateStr(CUDTGroup::GroupState st) { @@ -3684,7 +3112,7 @@ void CUDTGroup::sendBackup_CheckUnstableSockets(SendBackupCtx& w_sendBackupCtx, << " is qualified as unstable, but does not have the 'unstable since' timestamp. Still marking for closure."); } - const int unstable_for_ms = count_milliseconds(currtime - sock.m_tsUnstableSince); + const int unstable_for_ms = (int)count_milliseconds(currtime - sock.m_tsUnstableSince); if (unstable_for_ms < sock.peerIdleTimeout_ms()) continue; @@ -4399,7 +3827,7 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) { // NOTE: an exception from here will interrupt the loop // and will be caught in the upper level. - stat = core.sendmsg2(i->data, i->size, (i->mc)); + stat = core.sendmsg2(i->data, (int)i->size, (i->mc)); if (stat == -1) { // Stop sending if one sending ended up with error @@ -4529,7 +3957,7 @@ void CUDTGroup::updateLatestRcv(CUDTSocket* s) HLOGC(grlog.Debug, log << "updateLatestRcv: BACKUP group, updating from active link @" << s->m_SocketID << " with %" - << s->core().m_iRcvLastSkipAck); + << s->core().m_iRcvLastAck); CUDT* source = &s->core(); vector targets; diff --git a/trunk/3rdparty/srt-1-fit/srtcore/group.h b/trunk/3rdparty/srt-1-fit/srtcore/group.h index 1bd84aeda0..c2863b44e7 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/group.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/group.h @@ -155,7 +155,7 @@ class CUDTGroup srt::sync::ScopedLock g(m_GroupLock); bool empty = false; - HLOGC(gmlog.Debug, log << "group/remove: going to remove @" << id << " from $" << m_GroupID); + LOGC(gmlog.Note, log << "group/remove: removing member @" << id << " from group $" << m_GroupID); gli_t f = std::find_if(m_Group.begin(), m_Group.end(), HaveID(id)); if (f != m_Group.end()) @@ -194,9 +194,6 @@ class CUDTGroup m_bConnected = false; } - // XXX BUGFIX - m_Positions.erase(id); - return !empty; } @@ -265,7 +262,7 @@ class CUDTGroup /// @param[in] pktseq Packet sequence number currently tried to be sent /// @param[out] w_u CUDT unit of the current member (to allow calling overrideSndSeqNo) /// @param[out] w_curseq Group's current sequence number (either -1 or the value used already for other links) - /// @param[out] w_final_stat w_final_stat = send_status if sending succeded. + /// @param[out] w_final_stat w_final_stat = send_status if sending succeeded. /// /// @returns true if the sending operation result (submitted in stat) is a success, false otherwise. bool sendBackup_CheckSendStatus(const time_point& currtime, @@ -406,7 +403,9 @@ class CUDTGroup SRTSOCKET m_PeerGroupID; struct GroupContainer { - std::list m_List; + private: + std::list m_List; + sync::atomic m_SizeCache; /// This field is used only by some types of groups that need /// to keep track as to which link was lately used. Note that @@ -414,8 +413,11 @@ class CUDTGroup /// must be appropriately reset. gli_t m_LastActiveLink; + public: + GroupContainer() - : m_LastActiveLink(m_List.end()) + : m_SizeCache(0) + , m_LastActiveLink(m_List.end()) { } @@ -425,18 +427,18 @@ class CUDTGroup gli_t begin() { return m_List.begin(); } gli_t end() { return m_List.end(); } bool empty() { return m_List.empty(); } - void push_back(const SocketData& data) { m_List.push_back(data); } + void push_back(const SocketData& data) { m_List.push_back(data); ++m_SizeCache; } void clear() { m_LastActiveLink = end(); m_List.clear(); + m_SizeCache = 0; } - size_t size() { return m_List.size(); } + size_t size() { return m_SizeCache; } void erase(gli_t it); }; GroupContainer m_Group; - const bool m_bSyncOnMsgNo; // It goes into a dedicated HS field. Could be true for balancing groups (not implemented). SRT_GROUP_TYPE m_type; CUDTSocket* m_listener; // A "group" can only have one listener. srt::sync::atomic m_iBusy; @@ -641,20 +643,6 @@ class CUDTGroup time_point m_tsStartTime; time_point m_tsRcvPeerStartTime; - struct ReadPos - { - std::vector packet; - SRT_MSGCTRL mctrl; - ReadPos(int32_t s) - : mctrl(srt_msgctrl_default) - { - mctrl.pktseq = s; - } - }; - std::map m_Positions; - - ReadPos* checkPacketAhead(); - void recv_CollectAliveAndBroken(std::vector& w_alive, std::set& w_broken); /// The function polls alive member sockets and retrieves a list of read-ready. @@ -813,7 +801,6 @@ class CUDTGroup SRTU_PROPERTY_RW_CHAIN(CUDTGroup, int32_t, currentSchedSequence, m_iLastSchedSeqNo); SRTU_PROPERTY_RRW(std::set&, epollset, m_sPollID); SRTU_PROPERTY_RW_CHAIN(CUDTGroup, int64_t, latency, m_iTsbPdDelay_us); - SRTU_PROPERTY_RO(bool, synconmsgno, m_bSyncOnMsgNo); SRTU_PROPERTY_RO(bool, closing, m_bClosing); }; diff --git a/trunk/3rdparty/srt-1-fit/srtcore/handshake.cpp b/trunk/3rdparty/srt-1-fit/srtcore/handshake.cpp index 9e867c97e9..f8f03c84d8 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/handshake.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/handshake.cpp @@ -140,7 +140,8 @@ const char* srt_rejectreason_name [] = { "CONGESTION", "FILTER", "GROUP", - "TIMEOUT" + "TIMEOUT", + "CRYPTO" }; } diff --git a/trunk/3rdparty/srt-1-fit/srtcore/list.cpp b/trunk/3rdparty/srt-1-fit/srtcore/list.cpp index 2125995958..b6e70d39d7 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/list.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/list.cpp @@ -61,10 +61,12 @@ namespace srt_logging { extern Logger qrlog; extern Logger qslog; +extern Logger tslog; } using srt_logging::qrlog; using srt_logging::qslog; +using srt_logging::tslog; using namespace srt::sync; @@ -504,7 +506,7 @@ srt::CRcvLossList::~CRcvLossList() delete[] m_caSeq; } -void srt::CRcvLossList::insert(int32_t seqno1, int32_t seqno2) +int srt::CRcvLossList::insert(int32_t seqno1, int32_t seqno2) { // Data to be inserted must be larger than all those in the list if (m_iLargestSeq != SRT_SEQNO_NONE && CSeqNo::seqcmp(seqno1, m_iLargestSeq) <= 0) @@ -522,7 +524,7 @@ void srt::CRcvLossList::insert(int32_t seqno1, int32_t seqno2) log << "RCV-LOSS/insert: (" << seqno1 << "," << seqno2 << ") to be inserted is too small: m_iLargestSeq=" << m_iLargestSeq << ", m_iLength=" << m_iLength << ", m_iHead=" << m_iHead << ", m_iTail=" << m_iTail << " -- REJECTING"); - return; + return 0; } } m_iLargestSeq = seqno2; @@ -538,19 +540,19 @@ void srt::CRcvLossList::insert(int32_t seqno1, int32_t seqno2) m_caSeq[m_iHead].inext = -1; m_caSeq[m_iHead].iprior = -1; - m_iLength += CSeqNo::seqlen(seqno1, seqno2); - - return; + const int n = CSeqNo::seqlen(seqno1, seqno2); + m_iLength += n; + return n; } // otherwise searching for the position where the node should be - int offset = CSeqNo::seqoff(m_caSeq[m_iHead].seqstart, seqno1); + const int offset = CSeqNo::seqoff(m_caSeq[m_iHead].seqstart, seqno1); if (offset < 0) { LOGC(qrlog.Error, log << "RCV-LOSS/insert: IPE: new LOSS %(" << seqno1 << "-" << seqno2 << ") PREDATES HEAD %" << m_caSeq[m_iHead].seqstart << " -- REJECTING"); - return; + return -1; } int loc = (m_iHead + offset) % m_iSize; @@ -575,7 +577,9 @@ void srt::CRcvLossList::insert(int32_t seqno1, int32_t seqno2) m_iTail = loc; } - m_iLength += CSeqNo::seqlen(seqno1, seqno2); + const int n = CSeqNo::seqlen(seqno1, seqno2); + m_iLength += n; + return n; } bool srt::CRcvLossList::remove(int32_t seqno) @@ -715,22 +719,45 @@ bool srt::CRcvLossList::remove(int32_t seqno) bool srt::CRcvLossList::remove(int32_t seqno1, int32_t seqno2) { - if (seqno1 <= seqno2) + if (CSeqNo::seqcmp(seqno1, seqno2) > 0) { - for (int32_t i = seqno1; i <= seqno2; ++i) - remove(i); + return false; } - else + for (int32_t i = seqno1; CSeqNo::seqcmp(i, seqno2) <= 0; i = CSeqNo::incseq(i)) { - for (int32_t j = seqno1; j < CSeqNo::m_iMaxSeqNo; ++j) - remove(j); - for (int32_t k = 0; k <= seqno2; ++k) - remove(k); + remove(i); } - return true; } +int32_t srt::CRcvLossList::removeUpTo(int32_t seqno_last) +{ + int32_t first = getFirstLostSeq(); + if (first == SRT_SEQNO_NONE) + { + //HLOGC(tslog.Debug, log << "rcv-loss: DROP to %" << seqno_last << " - empty list"); + return first; // empty, so nothing to remove + } + + if (CSeqNo::seqcmp(seqno_last, first) < 0) + { + //HLOGC(tslog.Debug, log << "rcv-loss: DROP to %" << seqno_last << " - first %" << first << " is newer, exitting"); + return first; // seqno_last older than first - nothing to remove + } + + HLOGC(tslog.Debug, log << "rcv-loss: DROP to %" << seqno_last << " ..."); + + // NOTE: seqno_last is past-the-end here. Removed are only seqs + // that are earlier than this. + for (int32_t i = first; CSeqNo::seqcmp(i, seqno_last) <= 0; i = CSeqNo::incseq(i)) + { + //HLOGC(tslog.Debug, log << "... removing %" << i); + remove(i); + } + + return first; +} + bool srt::CRcvLossList::find(int32_t seqno1, int32_t seqno2) const { if (0 == m_iLength) @@ -839,8 +866,10 @@ srt::CRcvFreshLoss::Emod srt::CRcvFreshLoss::revoke(int32_t lo, int32_t hi) // ITEM: <--- delete // If the sequence range is older than the range to be revoked, // delete it anyway. - if (CSeqNo::seqcmp(lo, seq[1]) > 0) + if (lo != SRT_SEQNO_NONE && CSeqNo::seqcmp(lo, seq[1]) > 0) return DELETE; + // IF is NONE, then rely simply on that item.hi <% arg.hi, + // which is a condition at the end. // LOHI: // ITEM: <-- NOTFOUND @@ -868,3 +897,53 @@ srt::CRcvFreshLoss::Emod srt::CRcvFreshLoss::revoke(int32_t lo, int32_t hi) return DELETE; } + +bool srt::CRcvFreshLoss::removeOne(std::deque& w_container, int32_t sequence, int* pw_had_ttl) +{ + for (size_t i = 0; i < w_container.size(); ++i) + { + const int had_ttl = w_container[i].ttl; + Emod wh = w_container[i].revoke(sequence); + + if (wh == NONE) + continue; // Not found. Search again. + + if (wh == DELETE) // ... oo ... x ... o ... => ... oo ... o ... + { + // Removed the only element in the record - remove the record. + w_container.erase(w_container.begin() + i); + } + else if (wh == SPLIT) // ... ooxooo ... => ... oo ... ooo ... + { + // Create a new element that will hold the upper part of the range, + // and the found one modify to be the lower part of the range. + + // Keep the current end-of-sequence value for the second element + int32_t next_end = w_container[i].seq[1]; + + // seq-1 set to the end of this element + w_container[i].seq[1] = CSeqNo::decseq(sequence); + // seq+1 set to the begin of the next element + int32_t next_begin = CSeqNo::incseq(sequence); + + // Use position of the NEXT element because insertion happens BEFORE pointed element. + // Use the same TTL (will stay the same in the other one). + w_container.insert(w_container.begin() + i + 1, + CRcvFreshLoss(next_begin, next_end, w_container[i].ttl)); + } + // For STRIPPED: ... xooo ... => ... ooo ... + // i.e. there's nothing to do. + + // Every loss is unique. We're done here. + if (pw_had_ttl) + *pw_had_ttl = had_ttl; + + return true; + } + + if (pw_had_ttl) + *pw_had_ttl = 0; + return false; + +} + diff --git a/trunk/3rdparty/srt-1-fit/srtcore/list.h b/trunk/3rdparty/srt-1-fit/srtcore/list.h index 03f05e9279..8f921c698d 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/list.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/list.h @@ -53,6 +53,8 @@ modified by #ifndef INC_SRT_LIST_H #define INC_SRT_LIST_H +#include + #include "udt.h" #include "common.h" @@ -84,6 +86,12 @@ class CSndLossList void traceState() const; + // Debug/unittest support. + + int head() const { return m_iHead; } + int next(int loc) const { return m_caSeq[loc].inext; } + int last() const { return m_iLastInsertPos; } + private: struct Seq { @@ -118,6 +126,8 @@ class CSndLossList /// @param seqno2 last sequence number in range (SRT_SEQNO_NONE if no range) bool updateElement(int pos, int32_t seqno1, int32_t seqno2); + static const int LOC_NONE = -1; + private: CSndLossList(const CSndLossList&); CSndLossList& operator=(const CSndLossList&); @@ -134,8 +144,8 @@ class CRcvLossList /// Insert a series of loss seq. no. between "seqno1" and "seqno2" into the receiver's loss list. /// @param [in] seqno1 sequence number starts. /// @param [in] seqno2 seqeunce number ends. - - void insert(int32_t seqno1, int32_t seqno2); + /// @return length of the loss record inserted (seqlen(seqno1, seqno2)), -1 on error. + int insert(int32_t seqno1, int32_t seqno2); /// Remove a loss seq. no. from the receiver's loss list. /// @param [in] seqno sequence number. @@ -150,6 +160,12 @@ class CRcvLossList bool remove(int32_t seqno1, int32_t seqno2); + + /// Remove all numbers that precede the given sequence number. + /// @param [in] seqno sequence number. + /// @return the first removed sequence number + int32_t removeUpTo(int32_t seqno); + /// Find if there is any lost packets whose sequence number falling seqno1 and seqno2. /// @param [in] seqno1 start sequence number. /// @param [in] seqno2 end sequence number. @@ -264,6 +280,8 @@ struct CRcvFreshLoss Emod revoke(int32_t sequence); Emod revoke(int32_t lo, int32_t hi); + + static bool removeOne(std::deque& w_container, int32_t sequence, int* had_ttl = NULL); }; } // namespace srt diff --git a/trunk/3rdparty/srt-1-fit/srtcore/logging.h b/trunk/3rdparty/srt-1-fit/srtcore/logging.h index e79785b46a..e90ad4ac21 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/logging.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/logging.h @@ -60,6 +60,7 @@ written by // LOGF uses printf-like style formatting. // Usage: LOGF(gglog.Debug, "%s: %d", param1.c_str(), int(param2)); +// NOTE: LOGF is deprecated and should not be used #define LOGF(logdes, ...) if (logdes.CheckEnabled()) logdes().setloc(__FILE__, __LINE__, __FUNCTION__).form(__VA_ARGS__) // LOGP is C++11 only OR with only one string argument. @@ -165,14 +166,24 @@ struct SRT_API LogDispatcher // See Logger::Logger; we know this has normally 2 characters, // except !!FATAL!!, which has 9. Still less than 32. - strcpy(prefix, your_pfx); - // If the size of the FA name together with severity exceeds the size, // just skip the former. if (logger_pfx && strlen(prefix) + strlen(logger_pfx) + 1 < MAX_PREFIX_SIZE) { - strcat(prefix, ":"); - strcat(prefix, logger_pfx); +#if defined(_MSC_VER) && _MSC_VER < 1900 + _snprintf(prefix, MAX_PREFIX_SIZE, "%s:%s", your_pfx, logger_pfx); +#else + snprintf(prefix, MAX_PREFIX_SIZE + 1, "%s:%s", your_pfx, logger_pfx); +#endif + } + else + { +#ifdef _MSC_VER + strncpy_s(prefix, MAX_PREFIX_SIZE + 1, your_pfx, _TRUNCATE); +#else + strncpy(prefix, your_pfx, MAX_PREFIX_SIZE); + prefix[MAX_PREFIX_SIZE] = '\0'; +#endif } } @@ -242,7 +253,9 @@ struct SRT_API LogDispatcher return *this; } - DummyProxy& form(const char*, ...) + // DEPRECATED: DO NOT use LOGF/HLOGF macros anymore. + // Use iostream-style formatting with LOGC or a direct argument with LOGP. + SRT_ATR_DEPRECATED_PX DummyProxy& form(const char*, ...) SRT_ATR_DEPRECATED { return *this; } @@ -356,7 +369,11 @@ struct LogDispatcher::Proxy { char buf[512]; - vsprintf(buf, fmts, ap); +#if defined(_MSC_VER) && _MSC_VER < 1900 + _vsnprintf(buf, sizeof(buf) - 1, fmts, ap); +#else + vsnprintf(buf, sizeof(buf), fmts, ap); +#endif size_t len = strlen(buf); if ( buf[len-1] == '\n' ) { diff --git a/trunk/3rdparty/srt-1-fit/srtcore/md5.cpp b/trunk/3rdparty/srt-1-fit/srtcore/md5.cpp index 72bda857b0..7fd139944e 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/md5.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/md5.cpp @@ -27,7 +27,7 @@ This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at - http://www.ietf.org/rfc/rfc1321.txt + http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being @@ -38,165 +38,166 @@ that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order - either statically or dynamically; added missing #include - in library. + either statically or dynamically; added missing #include + in library. 2002-03-11 lpd Corrected argument list for main(), and added int return - type, in test program and T value program. + type, in test program and T value program. 2002-02-21 lpd Added missing #include in test program. 2000-07-03 lpd Patched to eliminate warnings about "constant is - unsigned in ANSI C, signed in traditional"; made test program - self-checking. + unsigned in ANSI C, signed in traditional"; made test program + self-checking. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 1999-05-03 lpd Original version. */ #include "md5.h" +#include #include /* * All symbols have been put under the srt namespace * to avoid potential linkage conflicts. */ -namespace srt { +namespace srt +{ -#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN -# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else -# define BYTE_ORDER 0 +#define BYTE_ORDER 0 #endif #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) -#define T3 0x242070db +#define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) -#define T6 0x4787c62a +#define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) -#define T9 0x698098d8 +#define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) -#define T13 0x6b901122 +#define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) -#define T16 0x49b40821 +#define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) -#define T19 0x265e5a51 +#define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) -#define T22 0x02441453 +#define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) -#define T25 0x21e1cde6 +#define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) -#define T28 0x455a14ed +#define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) -#define T31 0x676f02d9 +#define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) -#define T35 0x6d9d6122 +#define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) -#define T38 0x4bdecfa9 +#define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) -#define T41 0x289b7ec6 +#define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) -#define T44 0x04881d05 +#define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) -#define T47 0x1fa27cf8 +#define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) -#define T50 0x432aff97 +#define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) -#define T53 0x655b59c3 +#define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) -#define T57 0x6fa87e4f +#define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) -#define T60 0x4e0811a1 +#define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) -#define T63 0x2ad7d2bb +#define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) - -static void -md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +static void md5_process(md5_state_t* pms, const md5_byte_t* data /*[64]*/) { - md5_word_t - a = pms->abcd[0], b = pms->abcd[1], - c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs. */ - md5_word_t xbuf[16]; - const md5_word_t *X; + md5_word_t xbuf[16]; + const md5_word_t* X; #endif { #if BYTE_ORDER == 0 - /* - * Determine dynamically whether this is a big-endian or - * little-endian machine, since we can use a more efficient - * algorithm on the latter. - */ - static const int w = 1; - - if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t*)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!(uintptr_t(data) & 3)) + { + /* data are properly aligned */ + X = (const md5_word_t*)data; + } + else + { + /* not aligned */ + memcpy((xbuf), data, 64); + X = xbuf; + } + } #endif -#if BYTE_ORDER <= 0 /* little-endian */ - { - /* - * On little-endian machines, we can process properly aligned - * data without copying it. - */ - if (!((data - (const md5_byte_t *)0) & 3)) { - /* data are properly aligned */ - X = (const md5_word_t *)data; - } else { - /* not aligned */ - memcpy((xbuf), data, 64); - X = xbuf; - } - } +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ #endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t* xp = data; + int i; + #if BYTE_ORDER == 0 - else /* dynamic big-endian */ + X = xbuf; /* (dynamic only) */ +#else +#define xbuf X /* (static only) */ #endif -#if BYTE_ORDER >= 0 /* big-endian */ - { - /* - * On big-endian machines, we must arrange the bytes in the - * right order. - */ - const md5_byte_t *xp = data; - int i; - -# if BYTE_ORDER == 0 - X = xbuf; /* (dynamic only) */ -# else -# define xbuf X /* (static only) */ -# endif - for (i = 0; i < 16; ++i, xp += 4) - xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); - } + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } #endif } @@ -206,184 +207,179 @@ md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + F(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b +#define SET(a, b, c, d, k, s, Ti) \ + t = a + F(b, c, d) + X[k] + Ti; \ + a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ - SET(a, b, c, d, 0, 7, T1); - SET(d, a, b, c, 1, 12, T2); - SET(c, d, a, b, 2, 17, T3); - SET(b, c, d, a, 3, 22, T4); - SET(a, b, c, d, 4, 7, T5); - SET(d, a, b, c, 5, 12, T6); - SET(c, d, a, b, 6, 17, T7); - SET(b, c, d, a, 7, 22, T8); - SET(a, b, c, d, 8, 7, T9); - SET(d, a, b, c, 9, 12, T10); + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); - SET(a, b, c, d, 12, 7, T13); + SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET - /* Round 2. */ - /* Let [abcd k s i] denote the operation - a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + G(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 1, 5, T17); - SET(d, a, b, c, 6, 9, T18); +#define SET(a, b, c, d, k, s, Ti) \ + t = a + G(b, c, d) + X[k] + Ti; \ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); - SET(b, c, d, a, 0, 20, T20); - SET(a, b, c, d, 5, 5, T21); - SET(d, a, b, c, 10, 9, T22); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); - SET(b, c, d, a, 4, 20, T24); - SET(a, b, c, d, 9, 5, T25); - SET(d, a, b, c, 14, 9, T26); - SET(c, d, a, b, 3, 14, T27); - SET(b, c, d, a, 8, 20, T28); - SET(a, b, c, d, 13, 5, T29); - SET(d, a, b, c, 2, 9, T30); - SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET - /* Round 3. */ - /* Let [abcd k s t] denote the operation - a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + H(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 5, 4, T33); - SET(d, a, b, c, 8, 11, T34); +#define SET(a, b, c, d, k, s, Ti) \ + t = a + H(b, c, d) + X[k] + Ti; \ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); - SET(a, b, c, d, 1, 4, T37); - SET(d, a, b, c, 4, 11, T38); - SET(c, d, a, b, 7, 16, T39); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); - SET(a, b, c, d, 13, 4, T41); - SET(d, a, b, c, 0, 11, T42); - SET(c, d, a, b, 3, 16, T43); - SET(b, c, d, a, 6, 23, T44); - SET(a, b, c, d, 9, 4, T45); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); - SET(b, c, d, a, 2, 23, T48); + SET(b, c, d, a, 2, 23, T48); #undef SET - /* Round 4. */ - /* Let [abcd k s t] denote the operation - a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + I(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 0, 6, T49); - SET(d, a, b, c, 7, 10, T50); +#define SET(a, b, c, d, k, s, Ti) \ + t = a + I(b, c, d) + X[k] + Ti; \ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); - SET(b, c, d, a, 5, 21, T52); - SET(a, b, c, d, 12, 6, T53); - SET(d, a, b, c, 3, 10, T54); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); - SET(b, c, d, a, 1, 21, T56); - SET(a, b, c, d, 8, 6, T57); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); - SET(c, d, a, b, 6, 15, T59); + SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); - SET(a, b, c, d, 4, 6, T61); + SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); - SET(c, d, a, b, 2, 15, T63); - SET(b, c, d, a, 9, 21, T64); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); #undef SET - /* Then perform the following additions. (That is increment each - of the four registers by the value it had before this block - was started.) */ + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } -void -md5_init(md5_state_t *pms) +void md5_init(md5_state_t* pms) { pms->count[0] = pms->count[1] = 0; - pms->abcd[0] = 0x67452301; - pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; - pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; - pms->abcd[3] = 0x10325476; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; } -void -md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +void md5_append(md5_state_t* pms, const md5_byte_t* data, int nbytes) { - const md5_byte_t *p = data; - int left = nbytes; - int offset = (pms->count[0] >> 3) & 63; - md5_word_t nbits = (md5_word_t)(nbytes << 3); + const md5_byte_t* p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) - return; + return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) - pms->count[1]++; + pms->count[1]++; /* Process an initial partial block. */ - if (offset) { - int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); - - memcpy((pms->buf + offset), p, copy); - if (offset + copy < 64) - return; - p += copy; - left -= copy; - md5_process(pms, pms->buf); + if (offset) + { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy((pms->buf + offset), p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) - md5_process(pms, p); + md5_process(pms, p); /* Process a final partial block. */ if (left) - memcpy((pms->buf), p, left); + memcpy((pms->buf), p, left); } -void -md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +void md5_finish(md5_state_t* pms, md5_byte_t digest[16]) { - static const md5_byte_t pad[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - md5_byte_t data[8]; - int i; + static const md5_byte_t pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + md5_byte_t data[8]; + int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) - data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) - digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } } // namespace srt diff --git a/trunk/3rdparty/srt-1-fit/srtcore/md5.h b/trunk/3rdparty/srt-1-fit/srtcore/md5.h index 98bd076651..643981c01f 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/md5.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/md5.h @@ -27,7 +27,7 @@ This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at - http://www.ietf.org/rfc/rfc1321.txt + http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being @@ -38,23 +38,24 @@ that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed - references to Ghostscript; clarified derivation from RFC 1321; - now handles byte order either statically or dynamically. + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); - added conditionalization for C++ compilation from Martin - Purschke . + added conditionalization for C++ compilation from Martin + Purschke . 1999-05-03 lpd Original version. */ #ifndef md5_INCLUDED -# define md5_INCLUDED +#define md5_INCLUDED /* * All symbols have been put under the srt namespace * to avoid potential linkage conflicts. */ -namespace srt { +namespace srt +{ /* * This package supports both compile-time and run-time determination of CPU @@ -67,23 +68,24 @@ namespace srt { */ typedef unsigned char md5_byte_t; /* 8-bit byte */ -typedef unsigned int md5_word_t; /* 32-bit word */ +typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ -typedef struct md5_state_s { - md5_word_t count[2]; /* message length in bits, lsw first */ - md5_word_t abcd[4]; /* digest buffer */ - md5_byte_t buf[64]; /* accumulate block */ +typedef struct md5_state_s +{ + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; /* Initialize the algorithm. */ -void md5_init(md5_state_t *pms); +void md5_init(md5_state_t* pms); /* Append a string to the message. */ -void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); +void md5_append(md5_state_t* pms, const md5_byte_t* data, int nbytes); /* Finish the message and return the digest. */ -void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); +void md5_finish(md5_state_t* pms, md5_byte_t digest[16]); } // namespace srt diff --git a/trunk/3rdparty/srt-1-fit/srtcore/packet.cpp b/trunk/3rdparty/srt-1-fit/srtcore/packet.cpp index aacc37f2c9..fbb56a42c7 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/packet.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/packet.cpp @@ -151,11 +151,11 @@ modified by // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // Loss List Field Coding: -// For any consectutive lost seqeunce numbers that the differnece between +// For any consecutive lost seqeunce numbers that the differnece between // the last and first is more than 1, only record the first (a) and the // the last (b) sequence numbers in the loss list field, and modify the // the first bit of a to 1. -// For any single loss or consectutive loss less than 2 packets, use +// For any single loss or consecutive loss less than 2 packets, use // the original sequence numbers in the field. #include "platform_sys.h" @@ -172,9 +172,12 @@ extern Logger inlog; } using namespace srt_logging; +namespace srt { + // Set up the aliases in the constructure -srt::CPacket::CPacket() - : m_extra_pad() +CPacket::CPacket() + : m_nHeader() // Silences GCC 12 warning "used uninitialized". + , m_extra_pad() , m_data_owned(false) , m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO])) , m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO])) @@ -194,12 +197,12 @@ srt::CPacket::CPacket() m_PacketVector[PV_DATA].set(NULL, 0); } -char* srt::CPacket::getData() +char* CPacket::getData() { return (char*)m_PacketVector[PV_DATA].dataRef(); } -void srt::CPacket::allocate(size_t alloc_buffer_size) +void CPacket::allocate(size_t alloc_buffer_size) { if (m_data_owned) { @@ -213,14 +216,15 @@ void srt::CPacket::allocate(size_t alloc_buffer_size) m_data_owned = true; } -void srt::CPacket::deallocate() +void CPacket::deallocate() { if (m_data_owned) delete[](char*) m_PacketVector[PV_DATA].data(); m_PacketVector[PV_DATA].set(NULL, 0); + m_data_owned = false; } -char* srt::CPacket::release() +char* CPacket::release() { // When not owned, release returns NULL. char* buffer = NULL; @@ -234,31 +238,99 @@ char* srt::CPacket::release() return buffer; } -srt::CPacket::~CPacket() +CPacket::~CPacket() { // PV_HEADER is always owned, PV_DATA may use a "borrowed" buffer. // Delete the internal buffer only if it was declared as owned. - if (m_data_owned) - delete[](char*) m_PacketVector[PV_DATA].data(); + deallocate(); } -size_t srt::CPacket::getLength() const +size_t CPacket::getLength() const { return m_PacketVector[PV_DATA].size(); } -void srt::CPacket::setLength(size_t len) +void CPacket::setLength(size_t len) { m_PacketVector[PV_DATA].setLength(len); } -void srt::CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, size_t size) +void CPacket::setLength(size_t len, size_t cap) +{ + SRT_ASSERT(len <= cap); + setLength(len); + m_zCapacity = cap; +} + +#if ENABLE_HEAVY_LOGGING +// Debug only +static std::string FormatNumbers(UDTMessageType pkttype, const int32_t* lparam, void* rparam, const size_t size) +{ + // This may be changed over time, so use special interpretation + // only for certain types, and still display all data, no matter + // if it is expected to provide anything or not. + std::ostringstream out; + + out << "ARG="; + if (lparam) + out << *lparam; + else + out << "none"; + + if (size == 0) + { + out << " [no data]"; + return out.str(); + } + else if (!rparam) + { + out << " [ {" << size << "} ]"; + return out.str(); + } + + bool interp_as_seq = (pkttype == UMSG_LOSSREPORT || pkttype == UMSG_DROPREQ); + bool display_dec = (pkttype == UMSG_ACK || pkttype == UMSG_ACKACK || pkttype == UMSG_DROPREQ); + + out << " [ "; + + // Will be effective only for hex/oct. + out << std::showbase; + + const size_t size32 = size/4; + for (size_t i = 0; i < size32; ++i) + { + int32_t val = ((int32_t*)rparam)[i]; + if (interp_as_seq) + { + if (val & LOSSDATA_SEQNO_RANGE_FIRST) + out << "<" << (val & (~LOSSDATA_SEQNO_RANGE_FIRST)) << ">"; + else + out << val; + } + else + { + if (!display_dec) + { + out << std::hex; + out << val << "/"; + out << std::dec; + } + out << val; + + } + out << " "; + } + + out << "]"; + return out.str(); +} +#endif + +void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, size_t size) { // Set (bit-0 = 1) and (bit-1~15 = type) setControl(pkttype); - HLOGC(inlog.Debug, - log << "pack: type=" << MessageTypeStr(pkttype) << " ARG=" << (lparam ? Sprint(*lparam) : std::string("NULL")) - << " [ " << (rparam ? Sprint(*(int32_t*)rparam) : std::string()) << " ]"); + HLOGC(inlog.Debug, log << "pack: type=" << MessageTypeStr(pkttype) << " " << FormatNumbers(pkttype, lparam, rparam, size)); // Set additional information and control information field switch (pkttype) @@ -364,7 +436,7 @@ void srt::CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rpa } } -void srt::CPacket::toNL() +void CPacket::toNL() { // XXX USE HtoNLA! if (isControl()) @@ -382,7 +454,7 @@ void srt::CPacket::toNL() } } -void srt::CPacket::toHL() +void CPacket::toHL() { // convert back into local host order uint32_t* p = m_nHeader; @@ -399,22 +471,22 @@ void srt::CPacket::toHL() } } -srt::IOVector* srt::CPacket::getPacketVector() +IOVector* CPacket::getPacketVector() { return m_PacketVector; } -srt::UDTMessageType srt::CPacket::getType() const +UDTMessageType CPacket::getType() const { return UDTMessageType(SEQNO_MSGTYPE::unwrap(m_nHeader[SRT_PH_SEQNO])); } -int srt::CPacket::getExtendedType() const +int CPacket::getExtendedType() const { return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]); } -int32_t srt::CPacket::getAckSeqNo() const +int32_t CPacket::getAckSeqNo() const { // read additional information field // This field is used only in UMSG_ACK and UMSG_ACKACK, @@ -423,7 +495,7 @@ int32_t srt::CPacket::getAckSeqNo() const return m_nHeader[SRT_PH_MSGNO]; } -uint16_t srt::CPacket::getControlFlags() const +uint16_t CPacket::getControlFlags() const { // This returns exactly the "extended type" value, // which is not used at all in case when the standard @@ -432,17 +504,17 @@ uint16_t srt::CPacket::getControlFlags() const return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]); } -srt::PacketBoundary srt::CPacket::getMsgBoundary() const +PacketBoundary CPacket::getMsgBoundary() const { return PacketBoundary(MSGNO_PACKET_BOUNDARY::unwrap(m_nHeader[SRT_PH_MSGNO])); } -bool srt::CPacket::getMsgOrderFlag() const +bool CPacket::getMsgOrderFlag() const { return 0 != MSGNO_PACKET_INORDER::unwrap(m_nHeader[SRT_PH_MSGNO]); } -int32_t srt::CPacket::getMsgSeq(bool has_rexmit) const +int32_t CPacket::getMsgSeq(bool has_rexmit) const { if (has_rexmit) { @@ -454,13 +526,18 @@ int32_t srt::CPacket::getMsgSeq(bool has_rexmit) const } } -bool srt::CPacket::getRexmitFlag() const +bool CPacket::getRexmitFlag() const { - // return false; // return 0 != MSGNO_REXMIT::unwrap(m_nHeader[SRT_PH_MSGNO]); } -srt::EncryptionKeySpec srt::CPacket::getMsgCryptoFlags() const +void CPacket::setRexmitFlag(bool bRexmit) +{ + const int32_t clr_msgno = m_nHeader[SRT_PH_MSGNO] & ~MSGNO_REXMIT::mask; + m_nHeader[SRT_PH_MSGNO] = clr_msgno | MSGNO_REXMIT::wrap(bRexmit? 1 : 0); +} + +EncryptionKeySpec CPacket::getMsgCryptoFlags() const { return EncryptionKeySpec(MSGNO_ENCKEYSPEC::unwrap(m_nHeader[SRT_PH_MSGNO])); } @@ -468,32 +545,30 @@ srt::EncryptionKeySpec srt::CPacket::getMsgCryptoFlags() const // This is required as the encryption/decryption happens in place. // This is required to clear off the flags after decryption or set // crypto flags after encrypting a packet. -void srt::CPacket::setMsgCryptoFlags(EncryptionKeySpec spec) +void CPacket::setMsgCryptoFlags(EncryptionKeySpec spec) { int32_t clr_msgno = m_nHeader[SRT_PH_MSGNO] & ~MSGNO_ENCKEYSPEC::mask; m_nHeader[SRT_PH_MSGNO] = clr_msgno | EncryptionKeyBits(spec); } -uint32_t srt::CPacket::getMsgTimeStamp() const +uint32_t CPacket::getMsgTimeStamp() const { - // SRT_DEBUG_TSBPD_WRAP may enable smaller timestamp for faster wraparoud handling tests + // SRT_DEBUG_TSBPD_WRAP used to enable smaller timestamps for faster testing of how wraparounds are handled return (uint32_t)m_nHeader[SRT_PH_TIMESTAMP] & TIMESTAMP_MASK; } -srt::CPacket* srt::CPacket::clone() const +CPacket* CPacket::clone() const { CPacket* pkt = new CPacket; memcpy((pkt->m_nHeader), m_nHeader, HDR_SIZE); - pkt->m_pcData = new char[m_PacketVector[PV_DATA].size()]; - memcpy((pkt->m_pcData), m_pcData, m_PacketVector[PV_DATA].size()); - pkt->m_PacketVector[PV_DATA].setLength(m_PacketVector[PV_DATA].size()); + pkt->allocate(this->getLength()); + SRT_ASSERT(this->getLength() == pkt->getLength()); + memcpy((pkt->m_pcData), m_pcData, this->getLength()); + pkt->m_DestAddr = m_DestAddr; return pkt; } -namespace srt -{ - // Useful for debugging std::string PacketMessageFlagStr(uint32_t msgno_field) { @@ -522,10 +597,8 @@ inline void SprintSpecialWord(std::ostream& os, int32_t val) os << val; } -} // namespace srt - #if ENABLE_LOGGING -std::string srt::CPacket::Info() +std::string CPacket::Info() { std::ostringstream os; os << "TARGET=@" << m_iID << " "; @@ -580,3 +653,5 @@ std::string srt::CPacket::Info() return os.str(); } #endif + +} // end namespace srt diff --git a/trunk/3rdparty/srt-1-fit/srtcore/packet.h b/trunk/3rdparty/srt-1-fit/srtcore/packet.h index a288caa525..027d5f0b3b 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/packet.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/packet.h @@ -150,7 +150,7 @@ const int32_t LOSSDATA_SEQNO_RANGE_LAST = 0, LOSSDATA_SEQNO_SOLO = 0; inline int32_t CreateControlSeqNo(UDTMessageType type) { - return SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(size_t(type)); + return SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(uint32_t(type)); } inline int32_t CreateControlExtSeqNo(int exttype) @@ -236,6 +236,11 @@ class CPacket /// @param len [in] the payload or the control information field length. void setLength(size_t len); + /// Set the payload or the control information field length. + /// @param len [in] the payload or the control information field length. + /// @param cap [in] capacity (if known). + void setLength(size_t len, size_t cap); + /// Pack a Control packet. /// @param pkttype [in] packet type filed. /// @param lparam [in] pointer to the first data structure, explained by the packet type. @@ -286,6 +291,8 @@ class CPacket /// (because the peer will understand this bit as a part of MSGNO field). bool getRexmitFlag() const; + void setRexmitFlag(bool bRexmit); + /// Read the message sequence number. /// @return packet header field [1] int32_t getMsgSeq(bool has_rexmit = true) const; @@ -300,6 +307,8 @@ class CPacket /// @return packet header field [2] (bit 0~31, bit 0-26 if SRT_DEBUG_TSBPD_WRAP). uint32_t getMsgTimeStamp() const; + sockaddr_any udpDestAddr() const { return m_DestAddr; } + #ifdef SRT_DEBUG_TSBPD_WRAP // Receiver static const uint32_t MAX_TIMESTAMP = 0x07FFFFFF; // 27 bit fast wraparound for tests (~2m15s) #else @@ -335,6 +344,8 @@ class CPacket int32_t m_extra_pad; bool m_data_owned; + sockaddr_any m_DestAddr; + size_t m_zCapacity; protected: CPacket& operator=(const CPacket&); @@ -368,6 +379,8 @@ class CPacket char* data() { return m_pcData; } const char* data() const { return m_pcData; } size_t size() const { return getLength(); } + size_t capacity() const { return m_zCapacity; } + void setCapacity(size_t cap) { m_zCapacity = cap; } uint32_t header(SrtPktHeaderFields field) const { return m_nHeader[field]; } #if ENABLE_LOGGING diff --git a/trunk/3rdparty/srt-1-fit/srtcore/packetfilter.cpp b/trunk/3rdparty/srt-1-fit/srtcore/packetfilter.cpp index 305af35471..37785f43a1 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/packetfilter.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/packetfilter.cpp @@ -26,7 +26,7 @@ using namespace std; using namespace srt_logging; using namespace srt::sync; -bool srt::ParseFilterConfig(string s, SrtFilterConfig& w_config, PacketFilter::Factory** ppf) +bool srt::ParseFilterConfig(const string& s, SrtFilterConfig& w_config, PacketFilter::Factory** ppf) { if (!SrtParseConfig(s, (w_config))) return false; @@ -43,7 +43,7 @@ bool srt::ParseFilterConfig(string s, SrtFilterConfig& w_config, PacketFilter::F return true; } -bool srt::ParseFilterConfig(string s, SrtFilterConfig& w_config) +bool srt::ParseFilterConfig(const string& s, SrtFilterConfig& w_config) { return ParseFilterConfig(s, (w_config), NULL); } @@ -130,7 +130,7 @@ void srt::PacketFilter::receive(CUnit* unit, std::vector& w_incoming, lo { // For the sake of rebuilding MARK THIS UNIT GOOD, otherwise the // unit factory will supply it from getNextAvailUnit() as if it were not in use. - unit->m_iFlag = CUnit::GOOD; + unit->m_bTaken = true; HLOGC(pflog.Debug, log << "FILTER: PASSTHRU current packet %" << unit->m_Packet.getSeqNo()); w_incoming.push_back(unit); } @@ -169,7 +169,7 @@ void srt::PacketFilter::receive(CUnit* unit, std::vector& w_incoming, lo InsertRebuilt(w_incoming, m_unitq); ScopedLock lg(m_parent->m_StatsLock); - m_parent->m_stats.rcvr.suppliedByFilter.count(nsupply); + m_parent->m_stats.rcvr.suppliedByFilter.count((uint32_t)nsupply); } // Now that all units have been filled as they should be, @@ -178,11 +178,11 @@ void srt::PacketFilter::receive(CUnit* unit, std::vector& w_incoming, lo // Wanted units will be set GOOD flag, unwanted will remain // with FREE and therefore will be returned at the next // call to getNextAvailUnit(). - unit->m_iFlag = CUnit::FREE; + unit->m_bTaken = false; for (vector::iterator i = w_incoming.begin(); i != w_incoming.end(); ++i) { CUnit* u = *i; - u->m_iFlag = CUnit::FREE; + u->m_bTaken = false; } // Packets must be sorted by sequence number, ascending, in order @@ -251,9 +251,9 @@ void srt::PacketFilter::InsertRebuilt(vector& incoming, CUnitQueue* uq) break; } - // LOCK the unit as GOOD because otherwise the next + // LOCK the unit as taken because otherwise the next // call to getNextAvailUnit will return THE SAME UNIT. - u->m_iFlag = CUnit::GOOD; + u->m_bTaken = true; // After returning from this function, all units will be // set back to FREE so that the buffer can decide whether // it wants them or not. diff --git a/trunk/3rdparty/srt-1-fit/srtcore/packetfilter.h b/trunk/3rdparty/srt-1-fit/srtcore/packetfilter.h index c465145b3a..429e81e795 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/packetfilter.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/packetfilter.h @@ -51,7 +51,7 @@ class PacketFilter virtual ~Factory(); }; private: - friend bool ParseFilterConfig(std::string s, SrtFilterConfig& out, PacketFilter::Factory** ppf); + friend bool ParseFilterConfig(const std::string& s, SrtFilterConfig& out, PacketFilter::Factory** ppf); template class Creator: public Factory @@ -212,7 +212,7 @@ bool CheckFilterCompat(SrtFilterConfig& w_agent, SrtFilterConfig peer); inline void PacketFilter::feedSource(CPacket& w_packet) { SRT_ASSERT(m_filter); return m_filter->feedSource((w_packet)); } inline SRT_ARQLevel PacketFilter::arqLevel() { SRT_ASSERT(m_filter); return m_filter->arqLevel(); } -bool ParseFilterConfig(std::string s, SrtFilterConfig& out, PacketFilter::Factory** ppf); +bool ParseFilterConfig(const std::string& s, SrtFilterConfig& out, PacketFilter::Factory** ppf); } // namespace srt diff --git a/trunk/3rdparty/srt-1-fit/srtcore/packetfilter_api.h b/trunk/3rdparty/srt-1-fit/srtcore/packetfilter_api.h index d714b865b9..3bfba7c763 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/packetfilter_api.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/packetfilter_api.h @@ -81,7 +81,7 @@ struct SrtPacket }; -bool ParseFilterConfig(std::string s, SrtFilterConfig& w_config); +bool ParseFilterConfig(const std::string& s, SrtFilterConfig& w_config); class SrtPacketFilterBase diff --git a/trunk/3rdparty/srt-1-fit/srtcore/platform_sys.h b/trunk/3rdparty/srt-1-fit/srtcore/platform_sys.h index cb5d0afd91..e2f0aa4d9c 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/platform_sys.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/platform_sys.h @@ -24,7 +24,6 @@ #ifdef _WIN32 - #define _CRT_SECURE_NO_WARNINGS 1 // silences windows complaints for sscanf #include #include #include @@ -40,9 +39,6 @@ #include #include - #if defined(_MSC_VER) - #pragma warning(disable: 4251 26812) - #endif #else #if defined(__APPLE__) && __APPLE__ diff --git a/trunk/3rdparty/srt-1-fit/srtcore/queue.cpp b/trunk/3rdparty/srt-1-fit/srtcore/queue.cpp index 054ed6406a..863148b34a 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/queue.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/queue.cpp @@ -125,7 +125,7 @@ srt::CUnitQueue::CQEntry* srt::CUnitQueue::allocateEntry(const int iNumUnits, co for (int i = 0; i < iNumUnits; ++i) { - tempu[i].m_iFlag = CUnit::FREE; + tempu[i].m_bTaken = false; tempu[i].m_Packet.m_pcData = tempb + i * mss; } @@ -172,7 +172,7 @@ srt::CUnit* srt::CUnitQueue::getNextAvailUnit() const CUnit* end = m_pCurrQueue->m_pUnit + m_pCurrQueue->m_iSize; for (; m_pAvailUnit != end; ++m_pAvailUnit, ++units_checked) { - if (m_pAvailUnit->m_iFlag == CUnit::FREE) + if (!m_pAvailUnit->m_bTaken) { return m_pAvailUnit; } @@ -188,19 +188,19 @@ srt::CUnit* srt::CUnitQueue::getNextAvailUnit() void srt::CUnitQueue::makeUnitFree(CUnit* unit) { SRT_ASSERT(unit != NULL); - SRT_ASSERT(unit->m_iFlag != CUnit::FREE); - unit->m_iFlag.store(CUnit::FREE); + SRT_ASSERT(unit->m_bTaken); + unit->m_bTaken.store(false); --m_iNumTaken; } -void srt::CUnitQueue::makeUnitGood(CUnit* unit) +void srt::CUnitQueue::makeUnitTaken(CUnit* unit) { ++m_iNumTaken; SRT_ASSERT(unit != NULL); - SRT_ASSERT(unit->m_iFlag == CUnit::FREE); - unit->m_iFlag.store(CUnit::GOOD); + SRT_ASSERT(!unit->m_bTaken); + unit->m_bTaken.store(true); } srt::CSndUList::CSndUList(sync::CTimer* pTimer) @@ -481,6 +481,25 @@ bool srt::CSndQueue::getBind(char* dst, size_t len) const } #endif +#if defined(SRT_DEBUG_SNDQ_HIGHRATE) +static void CSndQueueDebugHighratePrint(const srt::CSndQueue* self, const steady_clock::time_point currtime) +{ + if (self->m_DbgTime <= currtime) + { + fprintf(stdout, + "SndQueue %lu slt:%lu nrp:%lu snt:%lu nrt:%lu ctw:%lu\n", + self->m_WorkerStats.lIteration, + self->m_WorkerStats.lSleepTo, + self->m_WorkerStats.lNotReadyPop, + self->m_WorkerStats.lSendTo, + self->m_WorkerStats.lNotReadyTs, + self->m_WorkerStats.lCondWait); + memset(&self->m_WorkerStats, 0, sizeof(self->m_WorkerStats)); + self->m_DbgTime = currtime + self->m_DbgPeriod; + } +} +#endif + void* srt::CSndQueue::worker(void* param) { CSndQueue* self = (CSndQueue*)param; @@ -492,34 +511,32 @@ void* srt::CSndQueue::worker(void* param) #endif #if defined(SRT_DEBUG_SNDQ_HIGHRATE) - CTimer::rdtsc(self->m_ullDbgTime); - self->m_ullDbgPeriod = uint64_t(5000000) * CTimer::getCPUFrequency(); - self->m_ullDbgTime += self->m_ullDbgPeriod; +#define IF_DEBUG_HIGHRATE(statement) statement + self->m_DbgTime = sync::steady_clock::now(); + self->m_DbgPeriod = sync::microseconds_from(5000000); + self->m_DbgTime += self->m_DbgPeriod; +#else +#define IF_DEBUG_HIGHRATE(statement) (void)0 #endif /* SRT_DEBUG_SNDQ_HIGHRATE */ while (!self->m_bClosing) { const steady_clock::time_point next_time = self->m_pSndUList->getNextProcTime(); -#if defined(SRT_DEBUG_SNDQ_HIGHRATE) - self->m_WorkerStats.lIteration++; -#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + INCREMENT_THREAD_ITERATIONS(); + + IF_DEBUG_HIGHRATE(self->m_WorkerStats.lIteration++); if (is_zero(next_time)) { -#if defined(SRT_DEBUG_SNDQ_HIGHRATE) - self->m_WorkerStats.lNotReadyTs++; -#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + IF_DEBUG_HIGHRATE(self->m_WorkerStats.lNotReadyTs++); // wait here if there is no sockets with data to be sent THREAD_PAUSED(); if (!self->m_bClosing) { self->m_pSndUList->waitNonEmpty(); - -#if defined(SRT_DEBUG_SNDQ_HIGHRATE) - self->m_WorkerStats.lCondWait++; -#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + IF_DEBUG_HIGHRATE(self->m_WorkerStats.lCondWait++); } THREAD_RESUMED(); @@ -529,43 +546,23 @@ void* srt::CSndQueue::worker(void* param) // wait until next processing time of the first socket on the list const steady_clock::time_point currtime = steady_clock::now(); -#if defined(SRT_DEBUG_SNDQ_HIGHRATE) - if (self->m_ullDbgTime <= currtime) - { - fprintf(stdout, - "SndQueue %lu slt:%lu nrp:%lu snt:%lu nrt:%lu ctw:%lu\n", - self->m_WorkerStats.lIteration, - self->m_WorkerStats.lSleepTo, - self->m_WorkerStats.lNotReadyPop, - self->m_WorkerStats.lSendTo, - self->m_WorkerStats.lNotReadyTs, - self->m_WorkerStats.lCondWait); - memset(&self->m_WorkerStats, 0, sizeof(self->m_WorkerStats)); - self->m_ullDbgTime = currtime + self->m_ullDbgPeriod; - } -#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ - - THREAD_PAUSED(); + IF_DEBUG_HIGHRATE(CSndQueueDebugHighratePrint(self, currtime)); if (currtime < next_time) { + THREAD_PAUSED(); self->m_pTimer->sleep_until(next_time); - -#if defined(HAI_DEBUG_SNDQ_HIGHRATE) - self->m_WorkerStats.lSleepTo++; -#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + THREAD_RESUMED(); + IF_DEBUG_HIGHRATE(self->m_WorkerStats.lSleepTo++); } - THREAD_RESUMED(); // Get a socket with a send request if any. CUDT* u = self->m_pSndUList->pop(); if (u == NULL) { -#if defined(SRT_DEBUG_SNDQ_HIGHRATE) - self->m_WorkerStats.lNotReadyPop++; -#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + IF_DEBUG_HIGHRATE(self->m_WorkerStats.lNotReadyPop++); continue; } - + #define UST(field) ((u->m_b##field) ? "+" : "-") << #field << " " HLOGC(qslog.Debug, log << "CSndQueue: requesting packet from @" << u->socketID() << " STATUS: " << UST(Listening) @@ -575,46 +572,44 @@ void* srt::CSndQueue::worker(void* param) if (!u->m_bConnected || u->m_bBroken) { -#if defined(SRT_DEBUG_SNDQ_HIGHRATE) - self->m_WorkerStats.lNotReadyPop++; -#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + IF_DEBUG_HIGHRATE(self->m_WorkerStats.lNotReadyPop++); continue; } // pack a packet from the socket CPacket pkt; - const std::pair res_time = u->packData((pkt)); + steady_clock::time_point next_send_time; + sockaddr_any source_addr; + const bool res = u->packData((pkt), (next_send_time), (source_addr)); - // Check if payload size is invalid. - if (res_time.first == false) + // Check if extracted anything to send + if (res == false) { -#if defined(SRT_DEBUG_SNDQ_HIGHRATE) - self->m_WorkerStats.lNotReadyPop++; -#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + IF_DEBUG_HIGHRATE(self->m_WorkerStats.lNotReadyPop++); continue; } const sockaddr_any addr = u->m_PeerAddr; - const steady_clock::time_point next_send_time = res_time.second; if (!is_zero(next_send_time)) self->m_pSndUList->update(u, CSndUList::DO_RESCHEDULE, next_send_time); HLOGC(qslog.Debug, log << self->CONID() << "chn:SENDING: " << pkt.Info()); - self->m_pChannel->sendto(addr, pkt); + self->m_pChannel->sendto(addr, pkt, source_addr); -#if defined(SRT_DEBUG_SNDQ_HIGHRATE) - self->m_WorkerStats.lSendTo++; -#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + IF_DEBUG_HIGHRATE(self->m_WorkerStats.lSendTo++); } THREAD_EXIT(); return NULL; } -int srt::CSndQueue::sendto(const sockaddr_any& w_addr, CPacket& w_packet) +int srt::CSndQueue::sendto(const sockaddr_any& addr, CPacket& w_packet, const sockaddr_any& src) { // send out the packet immediately (high priority), this is a control packet - m_pChannel->sendto(w_addr, w_packet); + // NOTE: w_packet is passed by mutable reference because this function will do + // a modification in place and then it will revert it. After returning this object + // should look unmodified, hence it is here passed without a reference marker. + m_pChannel->sendto(addr, w_packet, src); return (int)w_packet.getLength(); } @@ -842,14 +837,42 @@ srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& { ScopedLock vg(m_RIDListLock); + IF_HEAVY_LOGGING(const char* const id_type = w_id ? "THIS ID" : "A NEW CONNECTION"); + // TODO: optimize search for (list::const_iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) { if (i->m_PeerAddr == addr && ((w_id == 0) || (w_id == i->m_iID))) { + // This procedure doesn't exactly respond to the original UDT idea. + // As the "rendezvous queue" is used for both handling rendezvous and + // the caller sockets in the non-blocking mode (for blocking mode the + // entire handshake procedure is handled in a loop-style in CUDT::startConnect), + // the RID list should give up a socket entity in the following cases: + // 1. For THE SAME id as passed in w_id, respond always, as per a caller + // socket that is currently trying to connect and is managed with + // HS roundtrips in an event-style. Same for rendezvous. + // 2. For the "connection request" ID=0 the found socket should be given up + // ONLY IF it is rendezvous. Normally ID=0 is only for listener as a + // connection request. But if there was a listener, then this function + // wouldn't even be called, as this case would be handled before trying + // to call this function. + // + // This means: if an incoming ID is 0, then this search should succeed ONLY + // IF THE FOUND SOCKET WAS RENDEZVOUS. + + if (!w_id && !i->m_pUDT->m_config.bRendezvous) + { + HLOGC(cnlog.Debug, + log << "RID: found id @" << i->m_iID << " while looking for " + << id_type << " FROM " << i->m_PeerAddr.str() + << ", but it's NOT RENDEZVOUS, skipping"); + continue; + } + HLOGC(cnlog.Debug, - log << "RID: found id @" << i->m_iID << " while looking for " - << (w_id ? "THIS ID FROM " : "A NEW CONNECTION FROM ") << i->m_PeerAddr.str()); + log << "RID: found id @" << i->m_iID << " while looking for " + << id_type << " FROM " << i->m_PeerAddr.str()); w_id = i->m_iID; return i->m_pUDT; } @@ -908,10 +931,26 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst EReadStatus read_st = rst; EConnectStatus conn_st = cst; - if (i->id != dest_id) + if (cst != CONN_RENDEZVOUS && dest_id != 0) { - read_st = RST_AGAIN; - conn_st = CONN_AGAIN; + if (i->id != dest_id) + { + HLOGC(cnlog.Debug, log << "updateConnStatus: cst=" << ConnectStatusStr(cst) << " but for RID @" << i->id + << " dest_id=@" << dest_id << " - resetting to AGAIN"); + + read_st = RST_AGAIN; + conn_st = CONN_AGAIN; + } + else + { + HLOGC(cnlog.Debug, log << "updateConnStatus: cst=" << ConnectStatusStr(cst) << " for @" + << i->id); + } + } + else + { + HLOGC(cnlog.Debug, log << "updateConnStatus: cst=" << ConnectStatusStr(cst) << " and dest_id=@" << dest_id + << " - NOT checking against RID @" << i->id); } HLOGC(cnlog.Debug, @@ -1123,7 +1162,6 @@ srt::CRcvQueue::~CRcvQueue() while (!i->second.empty()) { CPacket* pkt = i->second.front(); - delete[] pkt->m_pcData; delete pkt; i->second.pop(); } @@ -1182,6 +1220,8 @@ void* srt::CRcvQueue::worker(void* param) { bool have_received = false; EReadStatus rst = self->worker_RetrieveUnit((id), (unit), (sa)); + + INCREMENT_THREAD_ITERATIONS(); if (rst == RST_OK) { if (id < 0) @@ -1324,14 +1364,12 @@ srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_un { // no space, skip this packet CPacket temp; - temp.m_pcData = new char[m_szPayloadSize]; - temp.setLength(m_szPayloadSize); + temp.allocate(m_szPayloadSize); THREAD_PAUSED(); EReadStatus rst = m_pChannel->recvfrom((w_addr), (temp)); THREAD_RESUMED(); // Note: this will print nothing about the packet details unless heavy logging is on. LOGC(qrlog.Error, log << CONID() << "LOCAL STORAGE DEPLETED. Dropping 1 packet: " << temp.Info()); - delete[] temp.m_pcData; // Be transparent for RST_ERROR, but ignore the correct // data read and fake that the packet was dropped. @@ -1500,7 +1538,7 @@ srt::EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUni if (cst == CONN_CONFUSED) { LOGC(cnlog.Warn, log << "AsyncOrRND: PACKET NOT HANDSHAKE - re-requesting handshake from peer"); - storePkt(id, unit->m_Packet.clone()); + storePktClone(id, unit->m_Packet); if (!u->processAsyncConnectRequest(RST_AGAIN, CONN_CONTINUE, &unit->m_Packet, u->m_PeerAddr)) { // Reuse previous behavior to reject a packet @@ -1575,7 +1613,7 @@ srt::EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUni log << "AsyncOrRND: packet RESOLVED TO ID=" << id << " -- continuing through CENTRAL PACKET QUEUE"); // This is where also the packets for rendezvous connection will be landing, // in case of a synchronous connection. - storePkt(id, unit->m_Packet.clone()); + storePktClone(id, unit->m_Packet); return CONN_CONTINUE; } @@ -1637,8 +1675,8 @@ int srt::CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) memcpy((w_packet.m_nHeader), newpkt->m_nHeader, CPacket::HDR_SIZE); memcpy((w_packet.m_pcData), newpkt->m_pcData, newpkt->getLength()); w_packet.setLength(newpkt->getLength()); + w_packet.m_DestAddr = newpkt->m_DestAddr; - delete[] newpkt->m_pcData; delete newpkt; // remove this message from queue, @@ -1693,7 +1731,6 @@ void srt::CRcvQueue::removeConnector(const SRTSOCKET& id) log << "removeConnector: ... and its packet queue with " << i->second.size() << " packets collected"); while (!i->second.empty()) { - delete[] i->second.front()->m_pcData; delete i->second.front(); i->second.pop(); } @@ -1726,7 +1763,7 @@ srt::CUDT* srt::CRcvQueue::getNewEntry() return u; } -void srt::CRcvQueue::storePkt(int32_t id, CPacket* pkt) +void srt::CRcvQueue::storePktClone(int32_t id, const CPacket& pkt) { CUniqueSync passcond(m_BufferLock, m_BufferCond); @@ -1734,22 +1771,22 @@ void srt::CRcvQueue::storePkt(int32_t id, CPacket* pkt) if (i == m_mBuffer.end()) { - m_mBuffer[id].push(pkt); + m_mBuffer[id].push(pkt.clone()); passcond.notify_one(); } else { - // avoid storing too many packets, in case of malfunction or attack + // Avoid storing too many packets, in case of malfunction or attack. if (i->second.size() > 16) return; - i->second.push(pkt); + i->second.push(pkt.clone()); } } void srt::CMultiplexer::destroy() { - // Reverse order of the assigned + // Reverse order of the assigned. delete m_pRcvQueue; delete m_pSndQueue; delete m_pTimer; diff --git a/trunk/3rdparty/srt-1-fit/srtcore/queue.h b/trunk/3rdparty/srt-1-fit/srtcore/queue.h index 51292e43d7..dd68a77214 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/queue.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/queue.h @@ -71,16 +71,7 @@ class CUDT; struct CUnit { CPacket m_Packet; // packet - enum Flag - { - FREE = 0, - GOOD = 1, - PASSACK = 2, - DROPPED = 3 - }; - - // TODO: The new RcvBuffer allows to use atomic_bool here. - sync::atomic m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped + sync::atomic m_bTaken; // true if the unit is is use (can be stored in the RCV buffer). }; class CUnitQueue @@ -106,7 +97,7 @@ class CUnitQueue void makeUnitFree(CUnit* unit); - void makeUnitGood(CUnit* unit); + void makeUnitTaken(CUnit* unit); private: struct CQEntry @@ -203,7 +194,7 @@ class CSndUList void insert_(const sync::steady_clock::time_point& ts, const CUDT* u); /// Insert a new UDT instance into the list without realloc. - /// Should be called if there is a gauranteed space for the element. + /// Should be called if there is a guaranteed space for the element. /// /// @param [in] ts time stamp: next processing time /// @param [in] u pointer to the UDT instance @@ -419,25 +410,25 @@ class CSndQueue /// Initialize the sending queue. /// @param [in] c UDP channel to be associated to the queue /// @param [in] t Timer - void init(CChannel* c, sync::CTimer* t); - /// Send out a packet to a given address. + /// Send out a packet to a given address. The @a src parameter is + /// blindly passed by the caller down the call with intention to + /// be received eventually by CChannel::sendto, and used only if + /// appropriate conditions state so. /// @param [in] addr destination address - /// @param [in] packet packet to be sent out + /// @param [in,ref] packet packet to be sent out + /// @param [in] src The source IP address (details above) /// @return Size of data sent out. - - int sendto(const sockaddr_any& addr, CPacket& packet); + int sendto(const sockaddr_any& addr, CPacket& packet, const sockaddr_any& src); /// Get the IP TTL. /// @param [in] ttl IP Time To Live. /// @return TTL. - int getIpTTL() const; /// Get the IP Type of Service. /// @return ToS. - int getIpToS() const; #ifdef SRT_ENABLE_BINDTODEVICE @@ -460,9 +451,10 @@ class CSndQueue sync::atomic m_bClosing; // closing the worker +public: #if defined(SRT_DEBUG_SNDQ_HIGHRATE) //>>debug high freq worker - uint64_t m_ullDbgPeriod; - uint64_t m_ullDbgTime; + sync::steady_clock::duration m_DbgPeriod; + mutable sync::steady_clock::time_point m_DbgTime; struct { unsigned long lIteration; // @@ -471,14 +463,15 @@ class CSndQueue unsigned long lSendTo; unsigned long lNotReadyTs; unsigned long lCondWait; // block on m_WindowCond - } m_WorkerStats; + } mutable m_WorkerStats; #endif /* SRT_DEBUG_SNDQ_HIGHRATE */ +private: + #if ENABLE_LOGGING static int m_counter; #endif -private: CSndQueue(const CSndQueue&); CSndQueue& operator=(const CSndQueue&); }; @@ -533,7 +526,7 @@ class CRcvQueue CUnitQueue* m_pUnitQueue; // The received packet queue CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue CHash* m_pHash; // Hash table for UDT socket looking up - CChannel* m_pChannel; // UDP channel for receving packets + CChannel* m_pChannel; // UDP channel for receiving packets sync::CTimer* m_pTimer; // shared timer with the snd queue int m_iIPversion; // IP version @@ -558,7 +551,7 @@ class CRcvQueue bool ifNewEntry(); CUDT* getNewEntry(); - void storePkt(int32_t id, CPacket* pkt); + void storePktClone(int32_t id, const CPacket& pkt); private: sync::Mutex m_LSLock; diff --git a/trunk/3rdparty/srt-1-fit/srtcore/socketconfig.cpp b/trunk/3rdparty/srt-1-fit/srtcore/socketconfig.cpp index a49d1d639e..5c5b8e0903 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/socketconfig.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/socketconfig.cpp @@ -69,7 +69,7 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - int ival = cast_optval(optval, optlen); + const int ival = cast_optval(optval, optlen); if (ival < int(CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -236,6 +236,21 @@ struct CSrtConfigSetter } }; +#ifdef ENABLE_MAXREXMITBW +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int64_t val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.llMaxRexmitBW = val; + } +}; +#endif + template<> struct CSrtConfigSetter { @@ -333,7 +348,17 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.bTSBPD = cast_optval(optval, optlen); + const bool val = cast_optval(optval, optlen); +#ifdef SRT_ENABLE_ENCRYPTION + if (val == false && co.iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) + { + using namespace srt_logging; + LOGC(aclog.Error, log << "Can't disable TSBPD as long as AES GCM is enabled."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } +#endif + + co.bTSBPD = val; } }; template<> @@ -502,7 +527,7 @@ struct CSrtConfigSetter if (val < 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - using namespace sync; + using namespace srt::sync; co.tdConnTimeOut = milliseconds_from(val); } }; @@ -601,7 +626,7 @@ struct CSrtConfigSetter if (val > SRT_LIVE_MAX_PLSIZE) { - LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE, maximum payload per MTU."); + LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << ", maximum payload per MTU."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } @@ -622,12 +647,22 @@ struct CSrtConfigSetter if (size_t(val) > efc_max_payload_size) { LOGC(aclog.Error, - log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size + log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << " bytes decreased by " << fc.extra_size << " required for packet filter header"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } } + // Not checking AUTO to allow defaul 1456 bytes. + if ((co.iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) + && (val > (SRT_LIVE_MAX_PLSIZE - HAICRYPT_AUTHTAG_MAX))) + { + LOGC(aclog.Error, + log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << " bytes decreased by " << HAICRYPT_AUTHTAG_MAX + << " required for AES-GCM."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + co.zExpPayloadSize = val; } }; @@ -883,6 +918,40 @@ struct CSrtConfigSetter } }; +#ifdef ENABLE_AEAD_API_PREVIEW +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + const int val = cast_optval(optval, optlen); +#ifdef SRT_ENABLE_ENCRYPTION + if (val < CSrtConfig::CIPHER_MODE_AUTO || val > CSrtConfig::CIPHER_MODE_AES_GCM) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + if (val == CSrtConfig::CIPHER_MODE_AES_GCM && !HaiCrypt_IsAESGCM_Supported()) + { + LOGC(aclog.Error, log << "AES GCM is not supported by the crypto provider."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + if (val == CSrtConfig::CIPHER_MODE_AES_GCM && !co.bTSBPD) + { + LOGC(aclog.Error, log << "Enable TSBPD to use AES GCM."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + co.iCryptoMode = val; +#else + LOGC(aclog.Error, log << "SRT was built without crypto module."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); +#endif + + } +}; +#endif + int dispatchSet(SRT_SOCKOPT optName, CSrtConfig& co, const void* optval, int optlen) { switch (optName) @@ -940,6 +1009,12 @@ int dispatchSet(SRT_SOCKOPT optName, CSrtConfig& co, const void* optval, int opt DISPATCH(SRTO_IPV6ONLY); DISPATCH(SRTO_PACKETFILTER); DISPATCH(SRTO_RETRANSMITALGO); +#ifdef ENABLE_AEAD_API_PREVIEW + DISPATCH(SRTO_CRYPTOMODE); +#endif +#ifdef ENABLE_MAXREXMITBW + DISPATCH(SRTO_MAXREXMITBW); +#endif #undef DISPATCH default: @@ -987,7 +1062,7 @@ bool SRT_SocketOptionObject::add(SRT_SOCKOPT optname, const void* optval, size_t case SRTO_PEERIDLETIMEO: case SRTO_RCVBUF: //SRTO_RCVSYN - must be always false in groups - //SRTO_RCVTIMEO - must be alwyas -1 in groups + //SRTO_RCVTIMEO - must be always -1 in groups case SRTO_SNDBUF: case SRTO_SNDDROPDELAY: //SRTO_TLPKTDROP - per transmission setting diff --git a/trunk/3rdparty/srt-1-fit/srtcore/socketconfig.h b/trunk/3rdparty/srt-1-fit/srtcore/socketconfig.h index 3fbf0e264c..403616edfe 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/socketconfig.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/socketconfig.h @@ -91,19 +91,27 @@ struct CSrtMuxerConfig int iUDPSndBufSize; // UDP sending buffer size int iUDPRcvBufSize; // UDP receiving buffer size - bool operator==(const CSrtMuxerConfig& other) const + // NOTE: this operator is not reversable. The syntax must use: + // muxer_entry == socket_entry + bool isCompatWith(const CSrtMuxerConfig& other) const { #define CEQUAL(field) (field == other.field) return CEQUAL(iIpTTL) && CEQUAL(iIpToS) - && CEQUAL(iIpV6Only) && CEQUAL(bReuseAddr) #ifdef SRT_ENABLE_BINDTODEVICE && CEQUAL(sBindToDevice) #endif && CEQUAL(iUDPSndBufSize) - && CEQUAL(iUDPRcvBufSize); + && CEQUAL(iUDPRcvBufSize) + && (other.iIpV6Only == -1 || CEQUAL(iIpV6Only)) + // NOTE: iIpV6Only is not regarded because + // this matches only in case of IPv6 with "any" address. + // And this aspect must be checked separately because here + // this procedure has no access to neither the address, + // nor the IP version (family). #undef CEQUAL + && true; } CSrtMuxerConfig() @@ -150,6 +158,16 @@ class StringStorage return set(s.c_str(), s.size()); } + size_t copy(char* s, size_t length) const + { + if (!s) + return 0; + + size_t copy_len = std::min((size_t)len, length); + memcpy(s, stor, copy_len); + return copy_len; + } + std::string str() const { return len == 0 ? std::string() : std::string(stor); @@ -176,6 +194,13 @@ struct CSrtConfig: CSrtMuxerConfig DEF_LINGER_S = 3*60, // 3 minutes DEF_CONNTIMEO_S = 3; // 3 seconds + enum + { + CIPHER_MODE_AUTO = 0, + CIPHER_MODE_AES_CTR = 1, + CIPHER_MODE_AES_GCM = 2 + }; + static const int COMM_RESPONSE_TIMEOUT_MS = 5 * 1000; // 5 seconds static const uint32_t COMM_DEF_MIN_STABILITY_TIMEOUT_MS = 60; // 60 ms @@ -189,8 +214,8 @@ struct CSrtConfig: CSrtMuxerConfig size_t zExpPayloadSize; // Expected average payload size (user option) // Options - bool bSynSending; // Sending syncronization mode - bool bSynRecving; // Receiving syncronization mode + bool bSynSending; // Sending synchronization mode + bool bSynRecving; // Receiving synchronization mode int iFlightFlagSize; // Maximum number of packets in flight from the peer side int iSndBufSize; // Maximum UDT sender buffer size int iRcvBufSize; // Maximum UDT receiver buffer size @@ -202,6 +227,9 @@ struct CSrtConfig: CSrtMuxerConfig int iSndTimeOut; // sending timeout in milliseconds int iRcvTimeOut; // receiving timeout in milliseconds int64_t llMaxBW; // maximum data transfer rate (threshold) +#ifdef ENABLE_MAXREXMITBW + int64_t llMaxRexmitBW; // maximum bandwidth limit for retransmissions (Bytes/s). +#endif // These fields keep the options for encryption // (SRTO_PASSPHRASE, SRTO_PBKEYLEN). Crypto object is @@ -224,6 +252,7 @@ struct CSrtConfig: CSrtMuxerConfig int iPeerIdleTimeout_ms; // Timeout for hearing anything from the peer (ms). uint32_t uMinStabilityTimeout_ms; int iRetransmitAlgo; + int iCryptoMode; // SRTO_CRYPTOMODE int64_t llInputBW; // Input stream rate (bytes/sec). 0: use internally estimated input bandwidth int64_t llMinInputBW; // Minimum input stream rate estimate (bytes/sec) @@ -263,6 +292,9 @@ struct CSrtConfig: CSrtMuxerConfig , iSndTimeOut(-1) , iRcvTimeOut(-1) , llMaxBW(-1) +#ifdef ENABLE_MAXREXMITBW + , llMaxRexmitBW(-1) +#endif , bDataSender(false) , bMessageAPI(true) , bTSBPD(true) @@ -275,6 +307,7 @@ struct CSrtConfig: CSrtMuxerConfig , iPeerIdleTimeout_ms(COMM_RESPONSE_TIMEOUT_MS) , uMinStabilityTimeout_ms(COMM_DEF_MIN_STABILITY_TIMEOUT_MS) , iRetransmitAlgo(1) + , iCryptoMode(CIPHER_MODE_AUTO) , llInputBW(0) , llMinInputBW(0) , iOverheadBW(25) diff --git a/trunk/3rdparty/srt-1-fit/srtcore/srt.h b/trunk/3rdparty/srt-1-fit/srtcore/srt.h index 6f2ee934dd..53b6fd274c 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/srt.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/srt.h @@ -72,7 +72,7 @@ written by #endif -// Stadnard attributes +// Standard attributes // When compiling in C++17 mode, use the standard C++17 attributes // (out of these, only [[deprecated]] is supported in C++14, so @@ -238,7 +238,13 @@ typedef enum SRT_SOCKOPT { SRTO_GROUPMINSTABLETIMEO, // Minimum Link Stability timeout (backup mode) in milliseconds (ENABLE_BONDING) SRTO_GROUPTYPE, // Group type to which an accepted socket is about to be added, available in the handshake (ENABLE_BONDING) SRTO_PACKETFILTER = 60, // Add and configure a packet filter - SRTO_RETRANSMITALGO = 61, // An option to select packet retransmission algorithm + SRTO_RETRANSMITALGO = 61, // An option to select packet retransmission algorithm +#ifdef ENABLE_AEAD_API_PREVIEW + SRTO_CRYPTOMODE = 62, // Encryption cipher mode (AES-CTR, AES-GCM, ...). +#endif +#ifdef ENABLE_MAXREXMITBW + SRTO_MAXREXMITBW = 63, // Maximum bandwidth limit for retransmision (Bytes/s) +#endif SRTO_E_SIZE // Always last element, not a valid option. } SRT_SOCKOPT; @@ -553,6 +559,9 @@ enum SRT_REJECT_REASON SRT_REJ_FILTER, // incompatible packet filter SRT_REJ_GROUP, // incompatible group SRT_REJ_TIMEOUT, // connection timeout +#ifdef ENABLE_AEAD_API_PREVIEW + SRT_REJ_CRYPTO, // conflicting cryptographic configurations +#endif SRT_REJ_E_SIZE, }; @@ -634,11 +643,14 @@ enum SRT_REJECT_REASON enum SRT_KM_STATE { - SRT_KM_S_UNSECURED = 0, //No encryption - SRT_KM_S_SECURING = 1, //Stream encrypted, exchanging Keying Material - SRT_KM_S_SECURED = 2, //Stream encrypted, keying Material exchanged, decrypting ok. - SRT_KM_S_NOSECRET = 3, //Stream encrypted and no secret to decrypt Keying Material - SRT_KM_S_BADSECRET = 4 //Stream encrypted and wrong secret, cannot decrypt Keying Material + SRT_KM_S_UNSECURED = 0, // No encryption + SRT_KM_S_SECURING = 1, // Stream encrypted, exchanging Keying Material + SRT_KM_S_SECURED = 2, // Stream encrypted, keying Material exchanged, decrypting ok. + SRT_KM_S_NOSECRET = 3, // Stream encrypted and no secret to decrypt Keying Material + SRT_KM_S_BADSECRET = 4 // Stream encrypted and wrong secret is used, cannot decrypt Keying Material +#ifdef ENABLE_AEAD_API_PREVIEW + ,SRT_KM_S_BADCRYPTOMODE = 5 // Stream encrypted but wrong cryptographic mode is used, cannot decrypt. Since v1.5.2. +#endif }; enum SRT_EPOLL_OPT diff --git a/trunk/3rdparty/srt-1-fit/srtcore/srt_attr_defs.h b/trunk/3rdparty/srt-1-fit/srtcore/srt_attr_defs.h index ee4c85b0d4..84daabeb1c 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/srt_attr_defs.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/srt_attr_defs.h @@ -89,7 +89,7 @@ used by SRT library internally. // - Other compilers: none. /////////////////////////////////////////////////////////////////////////////// #if _MSC_VER >= 1920 -// In case of MSVC these attributes have to preceed the attributed objects (variable, function). +// In case of MSVC these attributes have to precede the attributed objects (variable, function). // E.g. SRT_ATTR_GUARDED_BY(mtx) int object; // It is tricky to annotate e.g. the following function, as clang complaints it does not know 'm'. // SRT_ATTR_EXCLUDES(m) SRT_ATTR_ACQUIRE(m) diff --git a/trunk/3rdparty/srt-1-fit/srtcore/srt_c_api.cpp b/trunk/3rdparty/srt-1-fit/srtcore/srt_c_api.cpp index f675e990ac..885c800681 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/srt_c_api.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/srt_c_api.cpp @@ -439,6 +439,9 @@ const char* const srt_rejection_reason_msg [] = { "Packet Filter settings error", "Group settings collision", "Connection timeout" +#ifdef ENABLE_AEAD_API_PREVIEW + ,"Crypto mode" +#endif }; // Deprecated, available in SRT API. @@ -460,6 +463,9 @@ extern const char* const srt_rejectreason_msg[] = { srt_rejection_reason_msg[14], srt_rejection_reason_msg[15], srt_rejection_reason_msg[16] +#ifdef ENABLE_AEAD_API_PREVIEW + , srt_rejection_reason_msg[17] +#endif }; const char* srt_rejectreason_str(int id) diff --git a/trunk/3rdparty/srt-1-fit/srtcore/srt_compat.c b/trunk/3rdparty/srt-1-fit/srtcore/srt_compat.c index 1473a7b946..fbf4859ae2 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/srt_compat.c +++ b/trunk/3rdparty/srt-1-fit/srtcore/srt_compat.c @@ -70,7 +70,7 @@ extern const char * SysStrError(int errnum, char * buf, size_t buflen) // your compilation fails when you use wide characters. // The problem is that when TCHAR != char, then the buffer written this way // would have to be converted to ASCII, not just copied by strncpy. - FormatMessage(0 + FormatMessageA(0 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, @@ -87,8 +87,12 @@ extern const char * SysStrError(int errnum, char * buf, size_t buflen) if (lpMsgBuf) { +#ifdef _MSC_VER + strncpy_s(buf, buflen, lpMsgBuf, _TRUNCATE); +#else strncpy(buf, lpMsgBuf, buflen-1); buf[buflen-1] = 0; +#endif LocalFree((HLOCAL)lpMsgBuf); } else diff --git a/trunk/3rdparty/srt-1-fit/srtcore/strerror_defs.cpp b/trunk/3rdparty/srt-1-fit/srtcore/strerror_defs.cpp index 1b4c72e4e5..e99c8e2778 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/strerror_defs.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/strerror_defs.cpp @@ -71,12 +71,12 @@ const char* strerror_msgs_notsup [] = { "Operation not supported: Invalid socket ID", // MN_SIDINVAL = 4 "Operation not supported: Cannot do this operation on an UNBOUND socket", // MN_ISUNBOUND = 5 "Operation not supported: Socket is not in listening state", // MN_NOLISTEN = 6 - "Operation not supported: Listen/accept is not supported in rendezous connection setup", // MN_ISRENDEZVOUS = 7 + "Operation not supported: Listen/accept is not supported in rendezvous connection setup", // MN_ISRENDEZVOUS = 7 "Operation not supported: Cannot call connect on UNBOUND socket in rendezvous connection setup", // MN_ISRENDUNBOUND = 8 - "Operation not supported: Incorrect use of Message API (sendmsg/recvmsg).", // MN_INVALMSGAPI = 9 - "Operation not supported: Incorrect use of Buffer API (send/recv) or File API (sendfile/recvfile).", // MN_INVALBUFFERAPI = 10 + "Operation not supported: Incorrect use of Message API (sendmsg/recvmsg)", // MN_INVALMSGAPI = 9 + "Operation not supported: Incorrect use of Buffer API (send/recv) or File API (sendfile/recvfile)", // MN_INVALBUFFERAPI = 10 "Operation not supported: Another socket is already listening on the same port", // MN_BUSY = 11 - "Operation not supported: Message is too large to send (it must be less than the SRT send buffer size)", // MN_XSIZE = 12 + "Operation not supported: Message is too large to send", // MN_XSIZE = 12 "Operation not supported: Invalid epoll ID", // MN_EIDINVAL = 13 "Operation not supported: All sockets removed from epoll, waiting would deadlock", // MN_EEMPTY = 14 "Operation not supported: Another socket is bound to that port and is not reusable for requested settings", // MN_BUSYPORT = 15 diff --git a/trunk/3rdparty/srt-1-fit/srtcore/sync.cpp b/trunk/3rdparty/srt-1-fit/srtcore/sync.cpp index 7f2bc12ab9..a7cebb9097 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/sync.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/sync.cpp @@ -66,7 +66,7 @@ std::string FormatTimeSys(const steady_clock::time_point& timestamp) const steady_clock::time_point now_timestamp = steady_clock::now(); const int64_t delta_us = count_microseconds(timestamp - now_timestamp); const int64_t delta_s = - floor((static_cast(count_microseconds(now_timestamp.time_since_epoch()) % 1000000) + delta_us) / 1000000.0); + static_cast(floor((static_cast(count_microseconds(now_timestamp.time_since_epoch()) % 1000000) + delta_us) / 1000000.0)); const time_t tt = now_s + delta_s; struct tm tm = SysLocalTime(tt); // in seconds char tmp_buf[512]; @@ -94,7 +94,11 @@ bool StartThread(CThread& th, void* (*f) (void*), void* args, const string& name th.create_thread(f, args); #endif } +#if ENABLE_HEAVY_LOGGING catch (const CThreadException& e) +#else + catch (const CThreadException&) +#endif { HLOGC(inlog.Debug, log << name << ": failed to start thread. " << e.what()); return false; diff --git a/trunk/3rdparty/srt-1-fit/srtcore/sync.h b/trunk/3rdparty/srt-1-fit/srtcore/sync.h index 5d62536eb0..87be6f4584 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/sync.h +++ b/trunk/3rdparty/srt-1-fit/srtcore/sync.h @@ -11,6 +11,8 @@ #ifndef INC_SRT_SYNC_H #define INC_SRT_SYNC_H +#include "platform_sys.h" + #include #include #ifdef ENABLE_STDCXX_SYNC @@ -233,7 +235,7 @@ inline Duration operator*(const int& lhs, const Duration // class AtomicDuration; // template @@ -602,7 +604,7 @@ class CEvent /// Causes the current thread to block until /// a specific time is reached. /// - /// @return true if condition occured or spuriously woken up + /// @return true if condition occurred or spuriously woken up /// false on timeout bool lock_wait_until(const steady_clock::time_point& tp); @@ -613,7 +615,7 @@ class CEvent /// It may also be unblocked spuriously. /// Uses internal mutex to lock. /// - /// @return true if condition occured or spuriously woken up + /// @return true if condition occurred or spuriously woken up /// false on timeout bool lock_wait_for(const steady_clock::duration& rel_time); @@ -624,7 +626,7 @@ class CEvent /// It may also be unblocked spuriously. /// When unblocked, regardless of the reason, lock is reacquiredand wait_for() exits. /// - /// @return true if condition occured or spuriously woken up + /// @return true if condition occurred or spuriously woken up /// false on timeout bool wait_for(UniqueLock& lk, const steady_clock::duration& rel_time); diff --git a/trunk/3rdparty/srt-1-fit/srtcore/sync_posix.cpp b/trunk/3rdparty/srt-1-fit/srtcore/sync_posix.cpp index c44fe86c27..8cb475ea76 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/sync_posix.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/sync_posix.cpp @@ -52,7 +52,7 @@ static void rdtsc(uint64_t& x) asm("mov %0=ar.itc" : "=r"(x)::"memory"); #elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_AMD64_RDTSC uint32_t lval, hval; - asm("rdtsc" : "=a"(lval), "=d"(hval)); + asm volatile("rdtsc" : "=a"(lval), "=d"(hval)); x = hval; x = (x << 32) | lval; #elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_WINQPC diff --git a/trunk/3rdparty/srt-1-fit/srtcore/window.cpp b/trunk/3rdparty/srt-1-fit/srtcore/window.cpp index b077178c96..46889ecb07 100644 --- a/trunk/3rdparty/srt-1-fit/srtcore/window.cpp +++ b/trunk/3rdparty/srt-1-fit/srtcore/window.cpp @@ -93,7 +93,7 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3 r_ack = r_aSeq[i].iACK; // Calculate RTT estimate - const int rtt = count_microseconds(currtime - r_aSeq[i].tsTimeStamp); + const int rtt = (int)count_microseconds(currtime - r_aSeq[i].tsTimeStamp); if (i + 1 == r_iHead) { @@ -112,7 +112,7 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3 } // Head has exceeded the physical window boundary, so it is behind tail - for (int j = r_iTail, n = r_iHead + size; j < n; ++ j) + for (int j = r_iTail, n = r_iHead + (int)size; j < n; ++ j) { // Looking for an identical ACK Seq. No. if (seq == r_aSeq[j % size].iACKSeqNo) @@ -122,7 +122,7 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3 r_ack = r_aSeq[j].iACK; // Calculate RTT estimate - const int rtt = count_microseconds(currtime - r_aSeq[j].tsTimeStamp); + const int rtt = (int)count_microseconds(currtime - r_aSeq[j].tsTimeStamp); if (j == r_iHead) { @@ -176,7 +176,7 @@ int srt::CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, const int* bp = abytes; // median filtering const int* p = window; - for (int i = 0, n = asize; i < n; ++ i) + for (int i = 0, n = (int)asize; i < n; ++ i) { if ((*p < upper) && (*p > lower)) { @@ -192,7 +192,7 @@ int srt::CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, if (count > (asize >> 1)) { bytes += (srt::CPacket::SRT_DATA_HDR_SIZE * count); //Add protocol headers to bytes received - bytesps = (unsigned long)ceil(1000000.0 / (double(sum) / double(bytes))); + bytesps = (int)ceil(1000000.0 / (double(sum) / double(bytes))); return (int)ceil(1000000.0 / (sum / count)); } else @@ -240,7 +240,7 @@ int srt::CPktTimeWindowTools::getBandwidth_in(const int* window, int* replica, s // median filtering const int* p = window; - for (int i = 0, n = psize; i < n; ++ i) + for (int i = 0, n = (int)psize; i < n; ++ i) { if ((*p < upper) && (*p > lower)) { From 3231ce298bc0e866e04e1ecb898adbd9bbb90eaa Mon Sep 17 00:00:00 2001 From: chenhaibo <495810242@qq.com> Date: Thu, 21 Sep 2023 13:39:55 +0000 Subject: [PATCH 2/3] update README.md of 3rdparty --- trunk/3rdparty/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trunk/3rdparty/README.md b/trunk/3rdparty/README.md index ebeebd526d..3bcd047936 100644 --- a/trunk/3rdparty/README.md +++ b/trunk/3rdparty/README.md @@ -9,8 +9,8 @@ nginx-1.5.7.zip * for srs to support hls streaming. srt-1-fit -srt-1.4.1.tar.gz -* https://github.com/Haivision/srt/releases/tag/v1.4.1 +srt-1.5.3.tar.gz +* https://github.com/Haivision/srt/releases/tag/v1.5.3 * https://ossrs.net/lts/zh-cn/license#srt openssl-1.1-fit From 5b02a534013135d07fa48f3f9a02a40fbd43dd78 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 21 Sep 2023 22:02:08 +0800 Subject: [PATCH 3/3] Update release to v5.0.183 v6.0.81 --- trunk/doc/CHANGELOG.md | 2 ++ trunk/src/core/srs_core_version5.hpp | 2 +- trunk/src/core/srs_core_version6.hpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index 6547d041f7..cda2780a62 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -7,6 +7,7 @@ The changelog for SRS. ## SRS 6.0 Changelog +* v6.0, 2023-09-21, Merge [#3808](https://github.com/ossrs/srs/pull/3808): Upgrade libsrt to v1.5.3. v6.0.81 (#3808) * v6.0, 2023-09-21, Merge [#3404](https://github.com/ossrs/srs/pull/3404): WebRTC: Support WHEP for play. v6.0.80 (#3404) * v6.0, 2023-09-21, Merge [#3807](https://github.com/ossrs/srs/pull/3807): Prevent the output of srt logs in utest. v6.0.79 (#3807) * v6.0, 2023-09-21, Merge [#3696](https://github.com/ossrs/srs/pull/3696): SRT: modify log level from error to debug when no socket to accept. v6.0.78 (#3696) @@ -92,6 +93,7 @@ The changelog for SRS. ## SRS 5.0 Changelog +* v5.0, 2023-09-21, Merge [#3808](https://github.com/ossrs/srs/pull/3808): Upgrade libsrt to v1.5.3. v5.0.183 (#3808) * v5.0, 2023-09-21, Merge [#3404](https://github.com/ossrs/srs/pull/3404): WebRTC: Support WHEP for play. v5.0.182 (#3404) * v5.0, 2023-09-21, Merge [#3807](https://github.com/ossrs/srs/pull/3807): Prevent the output of srt logs in utest. v5.0.181 (#3807) * v5.0, 2023-09-21, Merge [#3696](https://github.com/ossrs/srs/pull/3696): SRT: modify log level from error to debug when no socket to accept. v5.0.180 (#3696) diff --git a/trunk/src/core/srs_core_version5.hpp b/trunk/src/core/srs_core_version5.hpp index ad57ddc131..e754894673 100644 --- a/trunk/src/core/srs_core_version5.hpp +++ b/trunk/src/core/srs_core_version5.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 5 #define VERSION_MINOR 0 -#define VERSION_REVISION 182 +#define VERSION_REVISION 183 #endif diff --git a/trunk/src/core/srs_core_version6.hpp b/trunk/src/core/srs_core_version6.hpp index 14930eab82..6a005842fd 100644 --- a/trunk/src/core/srs_core_version6.hpp +++ b/trunk/src/core/srs_core_version6.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 6 #define VERSION_MINOR 0 -#define VERSION_REVISION 80 +#define VERSION_REVISION 81 #endif