diff options
| author | Felix Fietkau | 2026-02-13 08:52:09 +0000 |
|---|---|---|
| committer | Felix Fietkau | 2026-02-13 09:02:45 +0000 |
| commit | 1a73ded9f738d403784aa448910cf5c9f9b05e18 (patch) | |
| tree | 5fc0414dc2ecb1694d53731b2036d53d4d864d95 | |
| parent | 0efa2cd3b74ca295362341f7b21f0449066141f5 (diff) | |
usock: fix timeout handling in usock_inet_timeout()
Replace relative timeout arithmetic with an absolute CLOCK_MONOTONIC
deadline computed once at function entry. This avoids the case where
the IPv6 head-start delay subtraction produces a negative timeout for
the subsequent combined poll, causing it to return immediately.
Also give IPv6 a head start of min(300ms, remaining_timeout) instead
of skipping the head start entirely when the total timeout is <= 300ms.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
| -rw-r--r-- | usock.c | 50 |
1 files changed, 36 insertions, 14 deletions
@@ -142,6 +142,18 @@ static int usock_check_connect(int fd) return err ? -1 : 0; } +static int usock_timeout_remaining(struct timespec *deadline) +{ + struct timespec cur; + int msec; + + clock_gettime(CLOCK_MONOTONIC, &cur); + msec = (deadline->tv_sec - cur.tv_sec) * 1000; + msec += (deadline->tv_nsec - cur.tv_nsec) / 1000000; + + return msec > 0 ? msec : 0; +} + int usock_inet_timeout(int type, const char *host, const char *service, void *addr, int timeout) { @@ -162,8 +174,9 @@ int usock_inet_timeout(int type, const char *host, const char *service, { .fd = -1, .events = POLLOUT }, { .fd = -1, .events = POLLOUT }, }; + struct timespec deadline; int sock = -1; - int i; + int delay, i; if (getaddrinfo(host, service, &hints, &result)) return -1; @@ -173,6 +186,14 @@ int usock_inet_timeout(int type, const char *host, const char *service, goto free_addrinfo; } + clock_gettime(CLOCK_MONOTONIC, &deadline); + deadline.tv_nsec += (timeout % 1000) * 1000000; + if (deadline.tv_nsec >= 1000000000) { + deadline.tv_sec++; + deadline.tv_nsec -= 1000000000; + } + deadline.tv_sec += timeout / 1000; + for (rp = result; rp != NULL; rp = rp->ai_next) { if (rp->ai_family == AF_INET6 && !rp_v6) rp_v6 = rp; @@ -193,20 +214,20 @@ int usock_inet_timeout(int type, const char *host, const char *service, goto try_v4; } - if (timeout > 300) { - if (poll_restart(pfds, 1, 300) == 1) { - if (usock_check_connect(pfds[0].fd) == 0) { - rp = rp_v6; - sock = pfds[0].fd; - goto out; - } - close(pfds[0].fd); - pfds[0].fd = -1; - rp_v6 = NULL; - goto try_v4; + delay = usock_timeout_remaining(&deadline); + if (delay > 300) + delay = 300; + if (delay > 0 && poll_restart(pfds, 1, delay) == 1) { + if (usock_check_connect(pfds[0].fd) == 0) { + rp = rp_v6; + sock = pfds[0].fd; + goto out; } + close(pfds[0].fd); + pfds[0].fd = -1; + rp_v6 = NULL; + goto try_v4; } - timeout -= 300; } try_v4: @@ -224,7 +245,8 @@ try_v4: } wait: - poll_restart(pfds + !rp_v6, !!rp_v6 + !!rp_v4, timeout); + poll_restart(pfds + !rp_v6, !!rp_v6 + !!rp_v4, + usock_timeout_remaining(&deadline)); if ((pfds[0].revents & POLLOUT) && usock_check_connect(pfds[0].fd) == 0) { rp = rp_v6; |