SNTP simple runs request iterations with exponential backoff. If the net interface is a slower connection (ie. CAT M1 modems) then the request will be sent but the response may take time to be received, thus causing a timeout and another request to be sent. Because of the nature of UDP and the fact that the same socket (source IP/port combo) is being used for both requests, a delayed response to the first request can be received as the response to the second request, causing -EINVAL to be returned when the timestamps mismatch (see subsys/net/lib/sntp/sntp.c). The solution provided retries receiving the response when the timestamp is mismatched (without sending an additional request). Signed-off-by: Marcus Penate <marcus.penate@ellenbytech.com>
111 lines
2.3 KiB
C
111 lines
2.3 KiB
C
/*
|
|
* Copyright (c) 2019 Linaro Limited
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
|
|
#include <zephyr/net/sntp.h>
|
|
#include <zephyr/net/socketutils.h>
|
|
|
|
static int sntp_simple_helper(struct sockaddr *addr, socklen_t addr_len, uint32_t timeout,
|
|
struct sntp_time *ts)
|
|
{
|
|
int res;
|
|
struct sntp_ctx sntp_ctx;
|
|
uint64_t deadline;
|
|
uint32_t iter_timeout;
|
|
bool first_iter;
|
|
|
|
res = sntp_init(&sntp_ctx, addr, addr_len);
|
|
if (res < 0) {
|
|
return res;
|
|
}
|
|
|
|
if (timeout == SYS_FOREVER_MS) {
|
|
deadline = (uint64_t)timeout;
|
|
} else {
|
|
deadline = k_uptime_get() + (uint64_t)timeout;
|
|
}
|
|
|
|
/* Timeout for current iteration */
|
|
iter_timeout = 100;
|
|
first_iter = true;
|
|
|
|
while (k_uptime_get() < deadline) {
|
|
res = sntp_query(&sntp_ctx, iter_timeout, ts);
|
|
|
|
if (res != -ETIMEDOUT) {
|
|
if (false == first_iter && -ERANGE == res) {
|
|
while (-ERANGE == res) {
|
|
/* Possible out of order packet received.
|
|
* Retry recv with current iteration timeout
|
|
* until an error or timeout (flushing the socket
|
|
* of old iteration responses until we timeout or
|
|
* receive our iteration's response)
|
|
*/
|
|
res = sntp_recv_response(&sntp_ctx, iter_timeout, ts);
|
|
}
|
|
|
|
if (res != ETIMEDOUT) {
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Exponential backoff with limit */
|
|
if (iter_timeout < 1000) {
|
|
iter_timeout *= 2;
|
|
}
|
|
|
|
if (first_iter) {
|
|
first_iter = false;
|
|
}
|
|
}
|
|
|
|
sntp_close(&sntp_ctx);
|
|
|
|
return res;
|
|
}
|
|
|
|
int sntp_simple_addr(struct sockaddr *addr, socklen_t addr_len, uint32_t timeout,
|
|
struct sntp_time *ts)
|
|
{
|
|
/* 123 is the standard SNTP port per RFC4330 */
|
|
int res = net_port_set_default(addr, 123);
|
|
|
|
if (res < 0) {
|
|
return res;
|
|
}
|
|
|
|
return sntp_simple_helper(addr, addr_len, timeout, ts);
|
|
}
|
|
|
|
int sntp_simple(const char *server, uint32_t timeout, struct sntp_time *ts)
|
|
{
|
|
int res;
|
|
static struct zsock_addrinfo hints;
|
|
struct zsock_addrinfo *addr;
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
hints.ai_protocol = 0;
|
|
/* 123 is the standard SNTP port per RFC4330 */
|
|
res = net_getaddrinfo_addr_str(server, "123", &hints, &addr);
|
|
if (res < 0) {
|
|
/* Just in case, as namespace for getaddrinfo errors is
|
|
* different from errno errors.
|
|
*/
|
|
errno = EDOM;
|
|
return res;
|
|
}
|
|
res = sntp_simple_helper(addr->ai_addr, addr->ai_addrlen, timeout, ts);
|
|
|
|
zsock_freeaddrinfo(addr);
|
|
|
|
return res;
|
|
}
|