chatgui: add Status page in settings (NTP + connections status)
This commit is contained in:
1 parent
f0bb03c2cf
commit
960cde3eda
15 files changed
+651
-62
No files matched your search
@@ -56,6 +56,7 @@ check_PROGRAMS = \
|
||||
test_intensive_memory_pool \
|
||||
test_tcp_io \
|
||||
test_uasync_socket_race \
|
||||
test_ntp \
|
||||
bench_timeout_heap \
|
||||
bench_uasync_timeouts
|
||||
|
||||
@@ -223,6 +224,10 @@ test_uasync_socket_race_SOURCES = test_uasync_socket_race.c
|
||||
test_uasync_socket_race_CFLAGS = -I$(top_srcdir)/lib
|
||||
test_uasync_socket_race_LDADD = $(COMMON_LIBS)
|
||||
|
||||
test_ntp_SOURCES = test_ntp.c
|
||||
test_ntp_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
|
||||
test_ntp_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||
|
||||
test_memory_pool_and_config_SOURCES = test_memory_pool_and_config.c
|
||||
test_memory_pool_and_config_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
|
||||
test_memory_pool_and_config_LDADD = $(COMMON_LIBS)
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
/**
|
||||
* @file test_ntp.c
|
||||
* @brief NTP client test with local fake NTP server
|
||||
*
|
||||
* Starts a local NTP server in a separate thread that returns a known
|
||||
* time offset. Verifies the NTP client syncs correctly: synced flag,
|
||||
* offset computation, and ntp_time_get_us()/ntp_time_is_synced().
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/time.h>
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/u_async.h"
|
||||
#include "../src/utun_instance.h"
|
||||
#include "../src/ntp_time.h"
|
||||
#include "../src/config_parser.h"
|
||||
|
||||
#define TEST_KNOWN_OFFSET_SEC 5
|
||||
#define TEST_KNOWN_OFFSET_US (TEST_KNOWN_OFFSET_SEC * 1000000LL)
|
||||
#define TEST_TIMEOUT_SEC 10
|
||||
#define OFFSET_TOLERANCE_US 500000 // 500ms — generous for scheduling jitter on localhost
|
||||
#define SYNC_WAIT_POLL_MS 10
|
||||
|
||||
#define NTP_DELTA 2208988800ULL
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct ntp_packet {
|
||||
uint8_t li_vn_mode;
|
||||
uint8_t stratum;
|
||||
uint8_t poll;
|
||||
uint8_t precision;
|
||||
uint32_t root_delay;
|
||||
uint32_t root_dispersion;
|
||||
uint32_t ref_id;
|
||||
uint64_t ref_ts;
|
||||
uint64_t orig_ts;
|
||||
uint64_t recv_ts;
|
||||
uint64_t xmit_ts;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
_Static_assert(sizeof(struct ntp_packet) == 48, "bad ntp_packet size");
|
||||
|
||||
static volatile int g_server_running = 1;
|
||||
static volatile uint16_t g_server_port = 0;
|
||||
|
||||
static uint64_t timeval_to_ntp(struct timeval *tv) {
|
||||
uint64_t sec = (uint64_t)(tv->tv_sec + NTP_DELTA);
|
||||
uint64_t frac = ((uint64_t)tv->tv_usec << 32) / 1000000ULL;
|
||||
return (sec << 32) | frac;
|
||||
}
|
||||
|
||||
static void* test_server_thread(void* arg) {
|
||||
(void)arg;
|
||||
int sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock < 0) { g_server_running = 0; return NULL; }
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
addr.sin_port = 0;
|
||||
|
||||
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) { close(sock); g_server_running = 0; return NULL; }
|
||||
|
||||
socklen_t addr_len = sizeof(addr);
|
||||
getsockname(sock, (struct sockaddr*)&addr, &addr_len);
|
||||
g_server_port = ntohs(addr.sin_port);
|
||||
|
||||
struct timeval tv = {1, 0};
|
||||
while (g_server_running) {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sock, &fds);
|
||||
tv.tv_sec = 1; tv.tv_usec = 0;
|
||||
int r = select(sock + 1, &fds, NULL, NULL, &tv);
|
||||
if (r <= 0) continue;
|
||||
|
||||
struct ntp_packet request;
|
||||
struct sockaddr_in client;
|
||||
socklen_t client_len = sizeof(client);
|
||||
ssize_t n = recvfrom(sock, (char*)&request, sizeof(request), 0, (struct sockaddr*)&client, &client_len);
|
||||
if (n < (ssize_t)sizeof(request)) continue;
|
||||
|
||||
uint8_t mode = request.li_vn_mode & 0x07;
|
||||
if (mode != 3) continue; // only respond to client-mode
|
||||
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
now.tv_sec += TEST_KNOWN_OFFSET_SEC;
|
||||
uint64_t fake_ntp = timeval_to_ntp(&now);
|
||||
|
||||
struct ntp_packet reply;
|
||||
memset(&reply, 0, sizeof(reply));
|
||||
reply.li_vn_mode = (0 << 6) | (4 << 3) | 4; // stratum 0=unspec, v4, server
|
||||
reply.stratum = 1;
|
||||
reply.ref_id = htonl(0x7F000001); // 127.0.0.1
|
||||
reply.orig_ts = request.xmit_ts; // echo client's transmit
|
||||
reply.recv_ts = htobe64(fake_ntp);
|
||||
reply.xmit_ts = htobe64(fake_ntp);
|
||||
|
||||
sendto(sock, (const char*)&reply, sizeof(reply), 0, (struct sockaddr*)&client, sizeof(client));
|
||||
}
|
||||
close(sock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int wait_for_port(void) {
|
||||
for (int i = 0; i < 200; i++) {
|
||||
if (g_server_port != 0) return 0;
|
||||
usleep(5000);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int result = 1;
|
||||
|
||||
debug_config_init();
|
||||
debug_set_level(DEBUG_LEVEL_WARN);
|
||||
debug_set_categories(DEBUG_CATEGORY_GENERAL);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "=== NTP Test ===");
|
||||
|
||||
/* ── 1. Start fake NTP server thread ── */
|
||||
pthread_t server_thread;
|
||||
if (pthread_create(&server_thread, NULL, test_server_thread, NULL) != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "Failed to create server thread");
|
||||
return 1;
|
||||
}
|
||||
if (wait_for_port() != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "Server thread did not bind");
|
||||
g_server_running = 0;
|
||||
pthread_join(server_thread, NULL);
|
||||
return 1;
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "Fake NTP server on 127.0.0.1:%u, offset=+%ds", g_server_port, TEST_KNOWN_OFFSET_SEC);
|
||||
|
||||
/* ── 2. Create uTun instance with minimal config ── */
|
||||
struct UASYNC* ua = uasync_create();
|
||||
if (!ua) { g_server_running = 0; pthread_join(server_thread, NULL); return 1; }
|
||||
|
||||
utun_instance_set_tun_init_enabled(0);
|
||||
|
||||
/* Parse config from buffer and inject keys (avoid config_ensure_keys_and_node_id rewriting) */
|
||||
const char *config_text =
|
||||
"[global]\n"
|
||||
"my_node_name=ntp_test\n"
|
||||
"my_private_key=e8d4d14943ca3732d7802d4010ea4f6f8bdb71e2304f78d68f4f3dc31e760c79\n"
|
||||
"my_public_key=8bac8ff3b3344cd3c1168b4e55bb4673f4063b5d42afb05fc70658f5da27907c\n"
|
||||
"my_node_id=721256a8d204e355\n"
|
||||
"\n"
|
||||
"[ntp]\n"
|
||||
"enabled=yes\n"
|
||||
"server=placeholder\n"
|
||||
"interval=3600\n";
|
||||
|
||||
struct utun_config* cfg = parse_config_from_buf(config_text, strlen(config_text), "inline");
|
||||
if (!cfg) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "Failed to parse config from buffer");
|
||||
uasync_destroy(ua, 0);
|
||||
g_server_running = 0;
|
||||
pthread_join(server_thread, NULL);
|
||||
return 1;
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "Parsed config: ntp_enabled=%d server_count=%d",
|
||||
cfg->global.ntp_enabled, cfg->global.ntp_server_count);
|
||||
|
||||
struct UTUN_INSTANCE* inst = utun_instance_create_from_config(ua, cfg);
|
||||
if (!inst) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "Failed to create instance from config");
|
||||
free_config(cfg);
|
||||
uasync_destroy(ua, 0);
|
||||
g_server_running = 0;
|
||||
pthread_join(server_thread, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* utun_instance_create only calls instance_init_common (basic inits).
|
||||
utun_instance_init is needed for NTP, connections, etc. */
|
||||
utun_instance_init(inst);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "NTP state after init: enabled=%d server_count=%d interval=%d servers[0]=%s config_enabled=%d config_count=%d",
|
||||
inst->ntp.enabled, inst->ntp.server_count, inst->ntp.resync_interval_sec,
|
||||
inst->ntp.server_count > 0 ? inst->ntp.servers[0] : "NONE",
|
||||
inst->config ? inst->config->global.ntp_enabled : -1,
|
||||
inst->config ? inst->config->global.ntp_server_count : -1);
|
||||
|
||||
/* ── 3. Set test address BEFORE first uasync poll (timer fires at delay 0) ── */
|
||||
ntp_time_set_test_addr(&inst->ntp, "127.0.0.1", g_server_port);
|
||||
|
||||
/* ── 4. Poll until synced or timeout ── */
|
||||
uint64_t start_tb = get_time_tb();
|
||||
while (get_time_tb() - start_tb < (uint64_t)(TEST_TIMEOUT_SEC * 10000)) {
|
||||
uasync_poll(ua, SYNC_WAIT_POLL_MS);
|
||||
if (inst->ntp.synced) break;
|
||||
}
|
||||
|
||||
/* ── 5. Verify ── */
|
||||
if (!inst->ntp.synced) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "FAIL: NTP did not sync within %ds", TEST_TIMEOUT_SEC);
|
||||
goto cleanup;
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "NTP synced, offset=%lldus", (long long)inst->ntp.offset_us);
|
||||
|
||||
// Check offset is close to expected (formula gives server - client ≈ KNOWN_OFFSET)
|
||||
{
|
||||
int64_t diff = inst->ntp.offset_us - TEST_KNOWN_OFFSET_US;
|
||||
if (diff < 0) diff = -diff;
|
||||
if (diff > OFFSET_TOLERANCE_US) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "FAIL: offset %lldus too far from expected %lldus (diff=%lldus)",
|
||||
(long long)inst->ntp.offset_us, (long long)TEST_KNOWN_OFFSET_US, (long long)diff);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
// Check ntp_time_is_synced
|
||||
if (!ntp_time_is_synced(inst)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "FAIL: ntp_time_is_synced returned 0 after sync");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Check ntp_time_get_us internal consistency
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
int64_t raw_us = (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec;
|
||||
int64_t corrected_us = ntp_time_get_us(inst);
|
||||
int64_t expected_us = raw_us - inst->ntp.offset_us;
|
||||
int64_t diff = corrected_us - expected_us;
|
||||
if (diff < 0) diff = -diff;
|
||||
if (diff > 10000) { // 10ms — time passes between gettimeofday calls
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "FAIL: ntp_time_get_us(%lld) != gettimeofday(%lld) - offset(%lld), diff=%lldus",
|
||||
(long long)corrected_us, (long long)raw_us, (long long)inst->ntp.offset_us, (long long)diff);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
result = 0;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "NTP Test PASSED");
|
||||
|
||||
cleanup:
|
||||
utun_instance_destroy(inst);
|
||||
uasync_destroy(ua, 0);
|
||||
g_server_running = 0;
|
||||
pthread_join(server_thread, NULL);
|
||||
return result;
|
||||
}
|
||||
Reference in new issue
Block a user