Repository navigation
Conversation
|
There was a problem hiding this comment.
🟡 Changes recommended
The retransmission timer is not restarted after blocked output flushes, risking premature retransmission or timeout failure, and the threaded session-ticket test omits scheduled ACK processing needed for its timer assertions.
2 open issues
What changed in this PR
Implements wolfSSL’s OpenSSL-compatible DTLS timeout APIs so event loops can schedule retransmissions and DTLS 1.3 acknowledgments.
Changes:
- Adds per-connection retransmission and ACK timers with expiry handling, backoff, and reset logic.
- Consolidates millisecond clocks and updates platform-specific timing behavior.
- Adds DTLS timer tests covering expiry, blocked writes, ACKs, and suspended handshakes.
| File | Description |
|---|---|
wolfssl/internal.h |
Adds timer state and internal helper declarations. |
tests/api/test_dtls.h |
Registers the new timer test suite. |
tests/api/test_dtls.c |
Adds timer coverage and updates existing timeout tests. |
src/wolfio.c |
Uses shared timer-state detection for receive timeouts. |
src/tls13.c |
Removes clock implementations moved to internal.c. |
src/ssl_api_dtls.c |
Implements timeout queries and expired-timer handling. |
src/internal.c |
Adds timer helpers and consolidates platform clocks. |
src/dtls13.c |
Integrates retransmission and ACK timer management. |
cmake/config.in |
Exports HAVE_GETTIMEOFDAY to generated configuration. |
Review effort: Balanced
|
@claude review |
|
@wolfSSL-Fenrir-bot review balanced |
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #11974
Scan targets checked: wolfssl-src, wolfssl-bugs
Not reviewed (no scan target covers them): cmake/config.in, tests/api/test_dtls.h
Coverage: 6 of 7 in-scope changed file(s) opened by the reviewer; not opened: src/tls13.c
Fenrir result: Approved ✅
No new issues found in the changed files.
Advisory only — this automated result does not count as a GitHub approval.
Review tier: Balanced
031caf4 to
0bace31
Compare
julek-wolfssl
left a comment
There was a problem hiding this comment.
What is the motivation behind this refactor? The two API functions could be implemented without making changes to the DTLS stack.
| /* Don't use ssl->options.handShakeDone since it is true even if | ||
| * we are in the process of renegotiation */ |
There was a problem hiding this comment.
This comment is now misplaced.
There was a problem hiding this comment.
Moved it back above the handShakeState check it describes.
| #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) || defined(WOLFSSL_DTLS13) | ||
| #ifdef WOLFSSL_32BIT_MILLI_TIME | ||
| #ifndef NO_ASN_TIME | ||
| #if defined(USER_TICKS) | ||
| #if 0 | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| /* | ||
| write your own clock tick function if don't want gettimeofday() | ||
| needs millisecond accuracy but doesn't have to correlated to EPOCH | ||
| */ | ||
| } | ||
| #endif | ||
|
|
||
| #elif defined(WOLFSSL_OPTEE) | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| TEE_Time now; | ||
|
|
||
| /* Monotonic within the TA, and not the normal world's clock. */ | ||
| TEE_GetSystemTime(&now); | ||
|
|
||
| return (word32)((now.seconds * 1000) + now.millis); | ||
| } | ||
|
|
||
| #elif defined(TIME_OVERRIDES) | ||
| #if !defined(NO_ASN) && !defined(NO_ASN_TIME) | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| return (word32) wc_Time(0) * 1000; | ||
| } | ||
| #else | ||
| #ifndef HAVE_TIME_T_TYPE | ||
| typedef long time_t; | ||
| #endif | ||
| extern time_t XTIME(time_t * timer); | ||
|
|
||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| return (word32) XTIME(0) * 1000; | ||
| } | ||
| #endif | ||
|
|
||
| #elif defined(XTIME_MS) | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| return (word32)XTIME_MS(0); | ||
| } | ||
|
|
||
| #elif defined(USE_WINDOWS_API) | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| static int init = 0; | ||
| static LARGE_INTEGER freq; | ||
| LARGE_INTEGER count; | ||
|
|
||
| if (!init) { | ||
| QueryPerformanceFrequency(&freq); | ||
| init = 1; | ||
| } | ||
|
|
||
| QueryPerformanceCounter(&count); | ||
|
|
||
| return (word32)(count.QuadPart / (freq.QuadPart / 1000)); | ||
| } | ||
|
|
||
| #elif defined(HAVE_RTP_SYS) | ||
| #include "rtptime.h" | ||
|
|
||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| return (word32)rtp_get_system_sec() * 1000; | ||
| } | ||
| #elif defined(WOLFSSL_DEOS) | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| const word32 systemTickTimeInHz = 1000000 / systemTickInMicroseconds(); | ||
| word32 *systemTickPtr = systemTickPointer(); | ||
|
|
||
| return (word32) (*systemTickPtr/systemTickTimeInHz) * 1000; | ||
| } | ||
| #elif defined(MICRIUM) | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| OS_TICK ticks = 0; | ||
| OS_ERR err; | ||
|
|
||
| ticks = OSTimeGet(&err); | ||
|
|
||
| return (word32) (ticks / OSCfg_TickRate_Hz) * 1000; | ||
| } | ||
| #elif defined(MICROCHIP_TCPIP_V5) | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| return (word32) (TickGet() / (TICKS_PER_SECOND / 1000)); | ||
| } | ||
| #elif defined(MICROCHIP_TCPIP) | ||
| #if defined(MICROCHIP_MPLAB_HARMONY) | ||
| #include <system/tmr/sys_tmr.h> | ||
|
|
||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| return (word32)(SYS_TMR_TickCountGet() / | ||
| (SYS_TMR_TickCounterFrequencyGet() / 1000)); | ||
| } | ||
| #else | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| return (word32)(SYS_TICK_Get() / (SYS_TICK_TicksPerSecondGet() / 1000)); | ||
| } | ||
|
|
||
| #endif | ||
|
|
||
| #elif defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX) | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| TIME_STRUCT mqxTime; | ||
|
|
||
| _time_get_elapsed(&mqxTime); | ||
|
|
||
| return (word32) mqxTime.SECONDS * 1000; | ||
| } | ||
| #elif defined(FREESCALE_FREE_RTOS) || defined(FREESCALE_KSDK_FREERTOS) | ||
| #include "include/task.h" | ||
|
|
||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| return (unsigned int)(((float)xTaskGetTickCount()) / | ||
| (configTICK_RATE_HZ / 1000)); | ||
| } | ||
| #elif defined(FREESCALE_KSDK_BM) | ||
| #include "lwip/sys.h" /* lwIP */ | ||
|
|
||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| return sys_now(); | ||
| } | ||
|
|
||
| #elif defined(WOLFSSL_CMSIS_RTOS) || defined(WOLFSSL_CMSIS_RTOSv2) | ||
|
|
||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| return (word32)osKernelGetTickCount(); | ||
| } | ||
|
|
||
| #elif defined(WOLFSSL_TIRTOS) | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| return (word32) Seconds_get() * 1000; | ||
| } | ||
| #elif defined(WOLFSSL_UTASKER) | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| return (word32)(uTaskerSystemTick / (TICK_RESOLUTION / 1000)); | ||
| } | ||
| #elif defined(WOLFSSL_LINUXKM) | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| return (word32)wc_linuxkm_time_epoch_msecs(); | ||
| } | ||
| #elif defined(WOLFSSL_QNX_CAAM) | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| struct timespec now; | ||
| clock_gettime(CLOCK_REALTIME, &now); | ||
| return (word32)(now.tv_sec * 1000 + now.tv_nsec / 1000000); | ||
| } | ||
| #elif defined(FUSION_RTOS) | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| struct timeval now; | ||
| if (FCL_GETTIMEOFDAY(&now, 0) < 0) | ||
| return 0; | ||
|
|
||
| /* Convert to milliseconds number. */ | ||
| return (word32)(now.tv_sec * 1000 + now.tv_usec / 1000); | ||
| } | ||
| #elif defined(WOLFSSL_ZEPHYR) | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| int64_t t; | ||
| #if defined(CONFIG_ARCH_POSIX) | ||
| k_cpu_idle(); | ||
| #endif | ||
| t = k_uptime_get(); /* returns current uptime in milliseconds */ | ||
| return (word32)t; | ||
| } | ||
| #elif defined(FREERTOS) | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| return (word32)((uint64_t)(xTaskGetTickCount() * 1000) / | ||
| configTICK_RATE_HZ); | ||
| } | ||
| #else | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| word32 TimeNowInMilliseconds(void) | ||
| { | ||
| struct timeval now; | ||
|
|
||
| if (gettimeofday(&now, 0) < 0) | ||
| return 0; | ||
|
|
||
| /* Convert to milliseconds number. */ | ||
| return (word32)(now.tv_sec * 1000 + now.tv_usec / 1000); | ||
| } | ||
| #endif | ||
| #else | ||
| /* user must supply time in milliseconds function: | ||
| * word32 TimeNowInMilliseconds(void); | ||
| * The response is milliseconds elapsed | ||
| */ | ||
| #endif /* !NO_ASN_TIME */ | ||
| #else | ||
| #ifndef NO_ASN_TIME | ||
| #if defined(USER_TICKS) | ||
| #if 0 | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| /* | ||
| write your own clock tick function if don't want gettimeofday() | ||
| needs millisecond accuracy but doesn't have to correlated to EPOCH | ||
| */ | ||
| } | ||
| #endif | ||
|
|
||
| #elif defined(WOLFSSL_OPTEE) | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| TEE_Time now; | ||
|
|
||
| /* Monotonic within the TA, and not the normal world's clock. */ | ||
| TEE_GetSystemTime(&now); | ||
|
|
||
| return ((sword64)now.seconds * 1000) + (sword64)now.millis; | ||
| } | ||
|
|
||
| #elif defined(TIME_OVERRIDES) | ||
| #if !defined(NO_ASN) && !defined(NO_ASN_TIME) | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| return (sword64) wc_Time(0) * 1000; | ||
| } | ||
| #else | ||
| #ifndef HAVE_TIME_T_TYPE | ||
| typedef long time_t; | ||
| #endif | ||
| extern time_t XTIME(time_t * timer); | ||
|
|
||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 32-bit value. | ||
| */ | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| return (sword64) XTIME(0) * 1000; | ||
| } | ||
| #endif | ||
|
|
||
| #elif defined(XTIME_MS) | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| return (sword64)XTIME_MS(0); | ||
| } | ||
|
|
||
| #elif defined(USE_WINDOWS_API) | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 64-bit value. | ||
| */ | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| static int init = 0; | ||
| static LARGE_INTEGER freq; | ||
| LARGE_INTEGER count; | ||
|
|
||
| if (!init) { | ||
| QueryPerformanceFrequency(&freq); | ||
| init = 1; | ||
| } | ||
|
|
||
| QueryPerformanceCounter(&count); | ||
|
|
||
| return (sword64)(count.QuadPart / (freq.QuadPart / 1000)); | ||
| } | ||
|
|
||
| #elif defined(HAVE_RTP_SYS) | ||
| #include "rtptime.h" | ||
|
|
||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 64-bit value. | ||
| */ | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| return (sword64)rtp_get_system_sec() * 1000; | ||
| } | ||
| #elif defined(WOLFSSL_DEOS) | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| const word32 systemTickTimeInHz = 1000000 / systemTickInMicroseconds(); | ||
| word32 *systemTickPtr = systemTickPointer(); | ||
|
|
||
| return (sword64) (*systemTickPtr/systemTickTimeInHz) * 1000; | ||
| } | ||
| #elif defined(MICRIUM) | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 64-bit value. | ||
| */ | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| OS_TICK ticks = 0; | ||
| OS_ERR err; | ||
|
|
||
| ticks = OSTimeGet(&err); | ||
|
|
||
| return (sword64) (ticks / OSCfg_TickRate_Hz) * 1000; | ||
| } | ||
| #elif defined(MICROCHIP_TCPIP_V5) | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 64-bit value. | ||
| */ | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| return (sword64) (TickGet() / (TICKS_PER_SECOND / 1000)); | ||
| } | ||
| #elif defined(MICROCHIP_TCPIP) | ||
| #if defined(MICROCHIP_MPLAB_HARMONY) | ||
| #include <system/tmr/sys_tmr.h> | ||
|
|
||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 64-bit value. | ||
| */ | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| return (sword64)SYS_TMR_TickCountGet() / | ||
| (SYS_TMR_TickCounterFrequencyGet() / 1000); | ||
| } | ||
| #else | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 64-bit value. | ||
| */ | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| return (sword64)SYS_TICK_Get() / (SYS_TICK_TicksPerSecondGet() / 1000); | ||
| } | ||
|
|
||
| #endif | ||
|
|
||
| #elif defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX) | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 64-bit value. | ||
| */ | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| TIME_STRUCT mqxTime; | ||
|
|
||
| _time_get_elapsed(&mqxTime); | ||
|
|
||
| return (sword64) mqxTime.SECONDS * 1000; | ||
| } | ||
| #elif defined(FREESCALE_FREE_RTOS) || defined(FREESCALE_KSDK_FREERTOS) | ||
| #include "include/task.h" | ||
|
|
||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 64-bit value. | ||
| */ | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| return (sword64)xTaskGetTickCount() / (configTICK_RATE_HZ / 1000); | ||
| } | ||
| #elif defined(FREESCALE_KSDK_BM) | ||
| #include "lwip/sys.h" /* lwIP */ | ||
|
|
||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 64-bit value. | ||
| */ | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| return sys_now(); | ||
| } | ||
|
|
||
| #elif defined(WOLFSSL_CMSIS_RTOS) || defined(WOLFSSL_CMSIS_RTOSv2) | ||
|
|
||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| return (sword64)osKernelGetTickCount(); | ||
| } | ||
|
|
||
| #elif defined(WOLFSSL_TIRTOS) | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 64-bit value. | ||
| */ | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| return (sword64) Seconds_get() * 1000; | ||
| } | ||
| #elif defined(WOLFSSL_UTASKER) | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 64-bit value. | ||
| */ | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| return (sword64)(uTaskerSystemTick / (TICK_RESOLUTION / 1000)); | ||
| } | ||
| #elif defined(WOLFSSL_LINUXKM) | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| s64 t; | ||
| #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) | ||
| struct timespec ts; | ||
| getnstimeofday(&ts); | ||
| t = ts.tv_sec * (s64)1000; | ||
| t += ts.tv_nsec / (s64)1000000; | ||
| #else | ||
| struct timespec64 ts; | ||
| #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) | ||
| ts = current_kernel_time64(); | ||
| #else | ||
| ktime_get_coarse_real_ts64(&ts); | ||
| #endif | ||
| t = ts.tv_sec * 1000L; | ||
| t += ts.tv_nsec / 1000000L; | ||
| #endif | ||
| return (sword64)t; | ||
| } | ||
| #elif defined(WOLFSSL_QNX_CAAM) | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| struct timespec now; | ||
| clock_gettime(CLOCK_REALTIME, &now); | ||
| return (sword64)(now.tv_sec * 1000 + now.tv_nsec / 1000000); | ||
| } | ||
| #elif defined(FUSION_RTOS) | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 64-bit value. | ||
| */ | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| struct timeval now; | ||
| if (FCL_GETTIMEOFDAY(&now, 0) < 0) | ||
| return 0; | ||
|
|
||
| /* Convert to milliseconds number. */ | ||
| return (sword64)now.tv_sec * 1000 + now.tv_usec / 1000; | ||
| } | ||
| #elif defined(WOLFSSL_ZEPHYR) | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| int64_t t; | ||
| #if defined(CONFIG_ARCH_POSIX) | ||
| k_cpu_idle(); | ||
| #endif | ||
| t = k_uptime_get(); /* returns current uptime in milliseconds */ | ||
| return (sword64)t; | ||
| } | ||
| #elif defined(FREERTOS) | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| return (sword64)((uint64_t)(xTaskGetTickCount() * 1000) / | ||
| configTICK_RATE_HZ); | ||
| } | ||
| #else | ||
| /* The time in milliseconds. | ||
| * Used for tickets to represent difference between when first seen and when | ||
| * sending. | ||
| * | ||
| * returns the time in milliseconds as a 64-bit value. | ||
| */ | ||
| sword64 TimeNowInMilliseconds(void) | ||
| { | ||
| struct timeval now; | ||
|
|
||
| if (gettimeofday(&now, 0) < 0) | ||
| return 0; | ||
|
|
||
| /* Convert to milliseconds number. */ | ||
| return (sword64)now.tv_sec * 1000 + now.tv_usec / 1000; | ||
| } | ||
| #endif | ||
| #else | ||
| /* user must supply time in milliseconds function: | ||
| * sword64 TimeNowInMilliseconds(void); | ||
| * The response is milliseconds elapsed | ||
| */ | ||
| #endif /* !NO_ASN_TIME */ | ||
| #endif /* WOLFSSL_32BIT_MILLI_TIME */ | ||
| #endif /* HAVE_SESSION_TICKET || !NO_PSK || WOLFSSL_DTLS13 */ | ||
|
|
There was a problem hiding this comment.
With --disable-tls13 the tls13.c ladder isn't compiled, but the DTLS 1.2 timers now need a millisecond clock too. It moved next to LowResTimer in internal.c so both share one copy instead of a second ladder.
| /* Get the DTLS timers that run. | ||
| * | ||
| * @param [in] ssl SSL/TLS object. | ||
| * @param [out] ackStart Start of the DTLS 1.3 ack timer. May be NULL. | ||
| * @return DTLS_TIMER_RTX and DTLS_TIMER_ACK bits, 0 when none runs. | ||
| * @return BAD_MUTEX_E when locking fails. | ||
| */ | ||
| int DtlsTimersRunning(WOLFSSL* ssl, word32* ackStart) | ||
| { | ||
| int running = 0; | ||
|
|
||
| (void)ackStart; | ||
|
|
||
| #ifdef WOLFSSL_DTLS13 | ||
| if (IsAtLeastTLSv1_3(ssl->version)) { | ||
| if (ssl->dtls13Rtx.rtxRecords != NULL) | ||
| running |= DTLS_TIMER_RTX; | ||
| #ifdef WOLFSSL_RW_THREADED | ||
| if (wc_LockMutex(&ssl->dtls13Rtx.mutex) != 0) | ||
| return BAD_MUTEX_E; | ||
| #endif | ||
| if (ssl->dtls13FastTimeout && Dtls13AckSendable(ssl)) | ||
| running |= DTLS_TIMER_ACK; | ||
| #ifndef NO_ASN_TIME | ||
| if (ackStart != NULL) | ||
| *ackStart = ssl->dtls_ack_timer_start; | ||
| #endif | ||
| #ifdef WOLFSSL_RW_THREADED | ||
| wc_UnLockMutex(&ssl->dtls13Rtx.mutex); | ||
| #endif | ||
| return running; | ||
| } | ||
| #endif /* WOLFSSL_DTLS13 */ | ||
|
|
||
| if (ssl->options.handShakeState != HANDSHAKE_DONE && | ||
| (ssl->dtls_tx_msg_list != NULL || ssl->dtls_tx_msg != NULL) && | ||
| DtlsMsgPoolWaiting(ssl)) | ||
| running |= DTLS_TIMER_RTX; | ||
|
|
||
| return running; | ||
| } |
There was a problem hiding this comment.
Why is the return being used for both status return and error information?
There was a problem hiding this comment.
Split it: DtlsTimersRunning, and DtlsTimerLeft with the same pattern, now return 0 or BAD_MUTEX_E and report the timers through an out parameter.
| /* Return 1 when the DTLS timer clock counts whole seconds. */ | ||
| int DtlsTimerLowRes(void) | ||
| { | ||
| #if defined(MS_TIME_LOW_RES) || defined(MS_TIME_FALLBACK) | ||
| return 1; | ||
| #else | ||
| return 0; | ||
| #endif | ||
| } |
There was a problem hiding this comment.
This name implies it returns the same as LowResTimer.
There was a problem hiding this comment.
Renamed to DtlsTimerIsLowRes.
TimeNowInMilliseconds multiplied xTaskGetTickCount() by 1000 in TickType_t before widening it to 64 bits. With a 32 bit tick count the product wrapped after 2^32 / 1000 ticks, about 71 minutes at 1 kHz, and the clock jumped back to 0. Widen the tick count before multiplying. The FREESCALE_FREE_RTOS and FREESCALE_KSDK_FREERTOS variants divided the tick count by configTICK_RATE_HZ / 1000, which is 0 below 1 kHz, and the 32 bit variant divided in float, which drops milliseconds once the tick count passes 2^24. Use the same integer conversion there. TLS 1.3 ticket ages and the DTLS 1.3 timers read this clock.
tls13.c kept two copies of the TimeNowInMilliseconds platform ladder, one returning word32 for WOLFSSL_32BIT_MILLI_TIME and one returning sword64, with the same doc comment repeated in most branches. Keep one ladder and pick the return type with a local macro. Move it next to LowResTimer in internal.c, so both platform clocks live in one place and the millisecond clock no longer depends on tls13.c. The sys/time.h include moves with it, and the TIME_OVERRIDES branch reuses the time_t and XTIME declarations of the LowResTimer branch above it. Each branch computes what both copies did, with three exceptions. The gettimeofday() and FUSION_RTOS branches convert tv_sec to the return type before multiplying, so the 32 bit variant no longer overflows a 32 bit long and gives the same result mod 2^32. The 32 bit MICROCHIP_TCPIP variants cast the tick count before dividing, as the 64 bit ones did, which gives the same value for their 32 bit tick counters. The LinuxKM branch keeps both bodies, since the 32 bit one uses the NMI safe wc_linuxkm_time_epoch_msecs().
wolfSSL_DTLSv1_get_timeout always returned 0 and reported the full timeout instead of the time left, and wolfSSL_DTLSv1_handle_timeout was a stub that did nothing. OpenSSL style event loops that wait for get_timeout and then call handle_timeout never retransmitted a lost flight. Track two timers on the object. The retransmission timer restarts whenever a handshake message is stored for retransmission (each DTLS 1.2 pool save and DTLS 1.3 retransmit record), whenever the flight is resent, either on a timeout or because the peer retransmitted or acked part of it, including a resend that hit WANT_WRITE, on every timeout backoff, and when a valid handshake message, or in DTLS 1.3 an ACK that leaves nothing to retransmit, resets a backed off timeout to its initial value. Restarting on every stored message rather than only on an empty list keeps a renegotiation or post-handshake flight from inheriting the start time of an older flight that is still kept. A DTLS 1.2 flight counts as sent only in the states DtlsMsgPoolSend accepts for a retransmission, so no timer runs while a flight is still being written or waits for an asynchronous operation. A DTLS 1.3 flight counts as sent while records are kept for it. get_timeout and handle_timeout report no retransmission timer while the handshake is suspended on asynchronous crypto, non-blocking OCSP or a chain verify callback, so a slow signature cannot trigger a retransmission of a partly sent flight. The DTLS 1.3 ack timer runs for a quarter of the timeout while received records wait for an ack that can be sent. It starts when the first of those records is seen and whenever the quick timeout flag is set again. DtlsTimersRunning() reports which timer runs and replaces the inline check in EmbedReceiveFrom, which keeps its behaviour. The timers use TimeNowInMilliseconds, which is now also built for DTLS, so DTLS 1.2 builds without session tickets, PSK or TLS 1.3 no longer count whole seconds. Whole seconds stay the fallback where TLS 1.3 tickets, PSK and DTLS 1.3 do not require a millisecond clock. With USER_TICKS, or on a platform that would land in the gettimeofday() default without HAVE_GETTIMEOFDAY, the library defines no TimeNowInMilliseconds and the timers count LowResTimer seconds instead. Ports with only a seconds clock, such as Xilinx or Nucleus, keep building, and a USER_TICKS application that supplies its own TimeNowInMilliseconds still links. The DEOS clock now reads the tick counter through a const volatile pointer like the DEOS LowResTimer, since DTLS 1.2 builds now compile it. The Zephyr clock skips k_cpu_idle() on native_posix and native_sim like the Zephyr LowResTimer, since idling there advances simulated time and the DTLS timers now read this clock. CMake now exports HAVE_GETTIMEOFDAY to config.h as autotools does. The ladder marks its whole second branches and DtlsTimerLowRes reports that to the timers, so internal.h needs no second copy of the ladder. With a whole second clock the timers round elapsed time down by one tick so a timer never expires early. The ack timer is not rounded down, since an early ACK only makes the peer resend the records it is missing. A timeout of 0 means no timeout, so no timer runs. get_timeout reports at most DTLS_TIMEOUT_MAX seconds, which also keeps the millisecond count from overflowing. DtlsTimerNowMs and DtlsTimerLowRes are visible to the tests and get WOLFSSL_API_PREFIX_MAP aliases. Fix the DTLS 1.3 quick timeout. Dtls13RtxTimeout cleared the flag when it sent the ACKs and then checked it, so a timeout taken after the quarter wait also retransmitted the flight and doubled the timeout. Dtls13RtxTimeout now takes the timer that expired. handle_timeout passes the timer it found expired. The receive path and wolfSSL_dtls_got_timeout do not know how long the caller waited, so with the quick timeout flag set they send only the ACKs while the buffered flight has waited less than half its timeout, and retransmit with backoff once it waited longer. Without a clock a quick timeout only sends the ACKs and the next full timeout retransmits. A quick wait stays under half the timeout and a full one reaches it. A whole second clock is rounded down as for the timers, so a full wait can read as a quick one and retransmit one timeout later. With nothing buffered to retransmit, a timeout with the flag set only sends the ACKs instead of backing off. Flushing the seen records also clears the flag, so a flag left over from the previous flight no longer turns the next full timeout into an ACK-only one. A timeout within DTLS13_MIN_RTX_INTERVAL of the last retransmission no longer doubles the timeout when Dtls13RtxSendBuffered holds the retransmission back. Dtls13RtxSendBuffered now uses Dtls13RtxWaitMs for that check, and a clock that stepped back counts as elapsed, so it no longer holds the retransmission back until the clock catches up. test_dtls_timeout clears the last retransmission time before its second timeout, which falls within that interval of the first one. test_dtls_drop_client_ack takes one timeout instead of two, since the first one no longer finds a flag left over from the handshake. get_timeout now returns 1 with the time left while a timer runs and 0 otherwise, and no longer fills in dtls_timeout when no timer runs, so callers must check the return value. The time left includes any wait before DTLS 1.3 may retransmit again. No timer runs while the object's own output is still pending, so handle_timeout cannot queue a second copy of a blocked write. handle_timeout returns 0 when no timer expired. For an expired retransmission timer it sends any pending ACKs and retransmits with backoff, and returns 1. For an expired DTLS 1.3 ack timer it only sends the ACKs, without retransmitting or backing off, and returns 1. It returns -1 on error, including when the timeout reached its maximum, the ACK hits WANT_WRITE or the timers cannot be read under WOLFSSL_RW_THREADED, which also sets the error, while get_timeout returns 0. At the maximum timeout DTLS 1.3 now sets SOCKET_ERROR_E, as DTLS 1.2 does, instead of -1. Both return 0 when NO_ASN_TIME leaves no clock. Under WOLFSSL_RW_THREADED the DTLS 1.3 ack timer state now sits behind the dtls13Rtx mutex. The read thread sets the ack timer start and the quick timeout flag with it held, the timeout handler reads and clears the flag and the pending ACK state with it held, and get_timeout reads the start in the same locked section as the flag, so it never pairs a set flag with a stale start.
DtlsTimersRunning and DtlsTimerLeft returned either the timer state or BAD_MUTEX_E. They now return 0 or BAD_MUTEX_E and report the timers through an out parameter. Rename DtlsTimerLowRes to DtlsTimerIsLowRes, since the old name read like a variant of LowResTimer. Move the handShakeDone comment in EmbedReceiveFrom back above the check it describes.
0bace31 to
bf0dd3a
Compare
|
To report the time left, the library has to know when the current flight went out, and master doesn't keep that. Its only timer state is The rest came from edge cases the first version got wrong: the DTLS 1.3 ACK timer (a quarter of the timeout), no timer while our own write is blocked or the handshake is suspended on async crypto, and a DTLS 1.3 quick-timeout bug where If it makes the review easier, I can split the quick-timeout fix and the ladder move into separate PRs and keep this one to the timer itself. |
Dtls13RtxTimeout now takes the timer that expired. Pass 0 (not known), as the receive path does.


wolfSSL_DTLSv1_get_timeout always returned 0 with the full timeout and wolfSSL_DTLSv1_handle_timeout was a stub, so OpenSSL-style event loops never retransmitted a lost flight. This implements both on a per-object millisecond retransmission timer and a DTLS 1.3 ACK timer.
Changes
Testing