From 7329fa65f14eecb7c3e837fdc534749f393326ba Mon Sep 17 00:00:00 2001 From: Evgeny Date: Thu, 2 Jul 2026 09:49:10 +0300 Subject: [PATCH] etcp_router: add congestion counters, fix tx_seq rollback on etcp_send failure, add router metrics to etcpmon GUI - etcp_router.h: add 9 per-conn counters (pkts_sent/err/rcvd, ack_sent/recv/stale, dup/oob/sign_fail) - etcp_router.c: increment counters at 13 event points; fix tx_seq rollback on etcp_send failure (was causing phantom inflight=256 deadlock); add DEBUG_ERROR on all 5 ignored etcp_send returns; fix router_ack_do_send to update timestamps only on success - etcpmon_protocol.h: add etcpmon_router_metrics (14 aggregated fields) - control_server.c: iterate router_conns to fill aggregated router metrics - etcpmon_gui: new 'Router Congestion' block with queue depths and all counters, Debug Levels shifted down, window 1300->1440 - test_etcp_router_unit: fix test_ack_timer for correct no-BGP-route behavior --- src/control_server.c | 24 ++++++++++ src/etcp_router.c | 78 +++++++++++++++++++++++++------ src/etcp_router.h | 10 ++++ tests/test_etcp_router_unit.c | 8 +--- tools/etcpmon/etcpmon_gui.c | 79 +++++++++++++++++++++++++++++++- tools/etcpmon/etcpmon_gui.h | 31 +++++++++++++ tools/etcpmon/etcpmon_protocol.h | 22 ++++++++- 7 files changed, 229 insertions(+), 23 deletions(-) diff --git a/src/control_server.c b/src/control_server.c index 23477dbb..b693f3ce 100644 --- a/src/control_server.c +++ b/src/control_server.c @@ -11,6 +11,7 @@ #include "tun_if.h" #include "route_lib.h" #include "route_bgp.h" +#include "etcp_router.h" #include "route_node.h" #include "pkt_normalizer.h" #include "../tools/etcpmon/etcpmon_protocol.h" @@ -1171,6 +1172,29 @@ static void send_metrics(struct control_server* server, struct control_client* c memset(&rsp->tun, 0, sizeof(rsp->tun)); } + /* Fill router congestion metrics — aggregate over all router_conns */ + memset(&rsp->router, 0, sizeof(rsp->router)); + if (instance->router_conns) { + struct ll_entry* re; + while ((re = queue_data_get(instance->router_conns)) != NULL) { + struct ETCP_ROUTER_CONN* rc = (struct ETCP_ROUTER_CONN*)re; + rsp->router.total_inflight += (uint32_t)(rc->tx_seq - rc->tx_acked); + if (rc->send_q) rsp->router.total_send_q += (uint32_t)queue_entry_count(rc->send_q); + if (rc->recv_q) rsp->router.total_recv_q += (uint32_t)queue_entry_count(rc->recv_q); + rsp->router.pkts_sent += rc->c_pkts_sent; + rsp->router.pkts_send_err += rc->c_pkts_send_err; + rsp->router.pkts_rcvd += rc->c_pkts_rcvd; + rsp->router.ack_sent += rc->c_ack_sent; + rsp->router.ack_recv += rc->c_ack_recv; + rsp->router.dup_dropped += rc->c_dup_dropped; + rsp->router.oob_dropped += rc->c_oob_dropped; + rsp->router.stale_ack += rc->c_stale_ack; + rsp->router.sign_fail += rc->c_sign_fail; + queue_data_put_with_index(instance->router_conns, re); + } + rsp->router.total_conns = (uint32_t)queue_entry_count(instance->router_conns); + } + /* Fill link metrics */ struct etcpmon_link_metrics* link_info = (struct etcpmon_link_metrics*)(buffer + sizeof(*hdr) + sizeof(*rsp)); link = conn->links; diff --git a/src/etcp_router.c b/src/etcp_router.c index a21b54f6..af2f15d2 100644 --- a/src/etcp_router.c +++ b/src/etcp_router.c @@ -58,7 +58,7 @@ static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t* size_t sig_len = is_signed ? SC_SIGN_SIZE : 0; size_t total_len = SVC_ROUTE_HDR_SIZE + pl_len + sig_len; uint8_t* dgram = u_malloc(total_len); - if (!dgram) { if (!seq_flags) rconn->tx_seq--; return -1; } + if (!dgram) { if (!seq_flags) rconn->tx_seq--; rconn->c_pkts_send_err++; return -1; } struct SVC_ROUTE_HDR* hdr = (struct SVC_ROUTE_HDR*)dgram; hdr->cmd = ETCP_ID_SVC_ROUTE; hdr->dst_node_id = rconn->remote_node_id; @@ -91,29 +91,29 @@ static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t* if (!conn) { DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: no route to %016llx svc_id=%u", (unsigned long long)rconn->remote_node_id, rconn->svc_id); - u_free(dgram); if (!seq_flags) rconn->tx_seq--; return -1; + u_free(dgram); if (!seq_flags) rconn->tx_seq--; rconn->c_pkts_send_err++; return -1; } if (is_signed) { struct sc_stream_sign_state sign_state; if (sc_stream_sign_init(&conn->crypto_ctx, &sign_state) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: sign_init failed svc_id=%u", rconn->svc_id); - u_free(dgram); if (!seq_flags) rconn->tx_seq--; return -1; + u_free(dgram); if (!seq_flags) rconn->tx_seq--; rconn->c_pkts_send_err++; return -1; } size_t data_len = SVC_ROUTE_HDR_SIZE + pl_len; if (sc_stream_sign_update(&sign_state, dgram, data_len) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: sign_update failed svc_id=%u", rconn->svc_id); - sc_stream_sign_cleanup(&sign_state); u_free(dgram); if (!seq_flags) rconn->tx_seq--; return -1; + sc_stream_sign_cleanup(&sign_state); u_free(dgram); if (!seq_flags) rconn->tx_seq--; rconn->c_pkts_send_err++; return -1; } size_t actual_sig_len = SC_SIGN_SIZE; if (sc_stream_sign_final(&sign_state, dgram + data_len, &actual_sig_len) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: sign_final failed svc_id=%u", rconn->svc_id); - u_free(dgram); if (!seq_flags) rconn->tx_seq--; return -1; + u_free(dgram); if (!seq_flags) rconn->tx_seq--; rconn->c_pkts_send_err++; return -1; } } struct ll_entry* entry = queue_entry_new(0); - if (!entry) { u_free(dgram); if (!seq_flags) rconn->tx_seq--; return -1; } + if (!entry) { u_free(dgram); if (!seq_flags) rconn->tx_seq--; rconn->c_pkts_send_err++; return -1; } entry->dgram = dgram; entry->len = total_len; @@ -123,7 +123,16 @@ static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t* (int32_t)(rconn->tx_seq - rconn->tx_acked), is_signed, (unsigned long long)rconn->remote_node_id); - return etcp_send(conn, entry); + int ret = etcp_send(conn, entry); + if (ret != 0) { + if (!seq_flags) rconn->tx_seq--; + queue_dgram_free(entry); queue_entry_free(entry); + rconn->c_pkts_send_err++; + DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: etcp_send failed ret=%d svc_id=%u seq=%u", ret, rconn->svc_id, seq); + } else { + rconn->c_pkts_sent++; + } + return ret; } // Отправка без conn (RST для неизвестного src) @@ -143,7 +152,11 @@ static void router_send_to(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t svc entry->len = SVC_ROUTE_HDR_SIZE; DEBUG_DEBUG(DEBUG_CATEGORY_ETCPROUTE, "router_send_to: seq_flags=0x%08x → %016llx svc_id=%u", seq_flags, (unsigned long long)dst, svc_id); - etcp_send(conn, entry); + int ret = etcp_send(conn, entry); + if (ret != 0) { + queue_dgram_free(entry); queue_entry_free(entry); + DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_send_to: etcp_send failed ret=%d", ret); + } } static void router_send_close_to_service(struct ETCP_ROUTER_CONN* rconn) { @@ -199,8 +212,9 @@ static void router_drain_send_q(struct ETCP_ROUTER_CONN* rconn) { if ((int32_t)(rconn->tx_seq - rconn->tx_acked) >= ROUTER_MAX_INFLIGHT) break; struct ll_entry* e = queue_data_get(rconn->send_q); if (!e) { rconn->send_blocked = 0; break; } - router_send_one(rconn, e->dgram, e->len); + int s_err = router_send_one(rconn, e->dgram, e->len); queue_dgram_free(e); queue_entry_free(e); + if (s_err != 0 && (int32_t)(rconn->tx_seq - rconn->tx_acked) >= ROUTER_MAX_INFLIGHT) break; } if (rconn->send_blocked && !rconn->send_resume_timer) rconn->send_resume_timer = uasync_set_timeout(rconn->inst->ua, @@ -245,8 +259,6 @@ static int router_enqueue_send(struct ETCP_ROUTER_CONN* rconn, const uint8_t* pa static void router_ack_do_send(struct ETCP_ROUTER_CONN* rconn) { router_send_ack(rconn); - rconn->last_sent_ack_seq = rconn->rx_seq; - rconn->last_ack_sent_tb = get_time_tb(); } static void router_send_ack(struct ETCP_ROUTER_CONN* rconn) { @@ -255,6 +267,7 @@ static void router_send_ack(struct ETCP_ROUTER_CONN* rconn) { if (!conn) { DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router_ack: no route to %016llx svc_id=%u", (unsigned long long)rconn->remote_node_id, rconn->svc_id); + rconn->last_ack_sent_tb = get_time_tb(); return; } struct SVC_ROUTE_HDR* hdr = (struct SVC_ROUTE_HDR*)u_malloc(SVC_ROUTE_HDR_SIZE); @@ -273,7 +286,15 @@ static void router_send_ack(struct ETCP_ROUTER_CONN* rconn) { DEBUG_TRACE(DEBUG_CATEGORY_ETCPROUTE, "ACK_SEND: svc_id=%u rx_seq=%u → %016llx", rconn->svc_id, rconn->rx_seq, (unsigned long long)rconn->remote_node_id); - etcp_send(conn, entry); + int ret = etcp_send(conn, entry); + if (ret != 0) { + queue_dgram_free(entry); queue_entry_free(entry); + DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_ack: etcp_send failed ret=%d svc_id=%u rx_seq=%u", ret, rconn->svc_id, rconn->rx_seq); + return; + } + rconn->c_ack_sent++; + rconn->last_sent_ack_seq = rconn->rx_seq; + rconn->last_ack_sent_tb = get_time_tb(); } static void router_schedule_ack(struct ETCP_ROUTER_CONN* rconn) { @@ -338,6 +359,7 @@ static void router_deliver(struct ETCP_ROUTER_CONN* rconn, struct ETCP_CONN* con DEBUG_TRACE(DEBUG_CATEGORY_ETCPROUTE, "router_deliver: svc_id=%u len=%zu from remote=%016llx", rconn->svc_id, payload_len, (unsigned long long)rconn->remote_node_id); + rconn->c_pkts_rcvd++; cb(conn, svc_entry); } @@ -415,6 +437,10 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) } if (sc_stream_sign_verify(&vfy_state, sig, SC_SIGN_SIZE) != SC_OK) { DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router: signature verification FAILED for node %016llx svc_id=%u — dropping", (unsigned long long)hdr->src_node_id, hdr->svc_id); + { + struct ETCP_ROUTER_CONN* sr = router_conn_find(inst, hdr->src_node_id, hdr->svc_id); + if (sr) sr->c_sign_fail++; + } queue_dgram_free(entry); queue_entry_free(entry); return; } @@ -440,9 +466,11 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) rconn->svc_id, hdr->seq, (int32_t)(rconn->tx_seq - rconn->tx_acked), queue_entry_count(rconn->send_q), (unsigned long long)hdr->src_node_id); + rconn->c_ack_recv++; } else { DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router: stale ACK seq=%u tx_acked=%u from %016llx, ignoring", hdr->seq, rconn->tx_acked, (unsigned long long)hdr->src_node_id); + rconn->c_stale_ack++; } rconn->last_dgram_ts = get_current_timestamp(); if (rconn->send_blocked) router_drain_send_q(rconn); @@ -492,6 +520,7 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) if (d > ROUTER_MAX_INFLIGHT || d < -ROUTER_MAX_INFLIGHT) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router: seq=%u out of bounds, rx_seq=%u (d=%d), dropping", seq, rconn->rx_seq, d); + rconn->c_oob_dropped++; queue_dgram_free(entry); queue_entry_free(entry); return; } @@ -501,6 +530,7 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) if (((int32_t)(rconn->rx_seq - seq) > 0) || queue_find_data_by_index(rconn->recv_q, &seq)) { DEBUG_DEBUG(DEBUG_CATEGORY_ETCPROUTE, "router: dup seq=%u rx_seq=%u, dropping", seq, rconn->rx_seq); + rconn->c_dup_dropped++; queue_dgram_free(entry); queue_entry_free(entry); return; } @@ -535,7 +565,11 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) } DEBUG_TRACE(DEBUG_CATEGORY_ETCPROUTE, "etcp_router: forwarding svc_id=%u → %016llx via %s", hdr->svc_id, (unsigned long long)hdr->dst_node_id, next->log_name); - etcp_send(next, entry); + int fwd_err = etcp_send(next, entry); + if (fwd_err != 0) { + queue_dgram_free(entry); queue_entry_free(entry); + DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "etcp_router: transit etcp_send failed ret=%d", fwd_err); + } } } @@ -720,6 +754,15 @@ void etcp_router_conn_restart(struct UTUN_INSTANCE* inst, uint64_t remote_node_i rconn->peer_sess_id = 0; rconn->start_sent = 0; rconn->send_blocked = 0; + rconn->c_pkts_sent = 0; + rconn->c_pkts_send_err = 0; + rconn->c_pkts_rcvd = 0; + rconn->c_ack_sent = 0; + rconn->c_ack_recv = 0; + rconn->c_dup_dropped = 0; + rconn->c_oob_dropped = 0; + rconn->c_stale_ack = 0; + rconn->c_sign_fail = 0; } void etcp_router_on_send_ready(struct UTUN_INSTANCE* inst, uint64_t node_id, uint8_t svc_id, @@ -768,6 +811,15 @@ struct ETCP_ROUTER_CONN* etcp_router_conn_get(struct UTUN_INSTANCE* inst, rconn->sess_id = 0; rconn->peer_sess_id = 0; rconn->start_sent = 0; + rconn->c_pkts_sent = 0; + rconn->c_pkts_send_err = 0; + rconn->c_pkts_rcvd = 0; + rconn->c_ack_sent = 0; + rconn->c_ack_recv = 0; + rconn->c_dup_dropped = 0; + rconn->c_oob_dropped = 0; + rconn->c_stale_ack = 0; + rconn->c_sign_fail = 0; rconn->recv_q = queue_new(inst->ua, ROUTER_RECVQ_HASH_SIZE, 0, 4, "router_recv_q"); if (!rconn->recv_q) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_conn_get: queue_new(recv_q) failed"); queue_entry_free(&rconn->ll); return NULL; } rconn->send_q = queue_new(inst->ua, 0, 0, 0, "router_send_q"); diff --git a/src/etcp_router.h b/src/etcp_router.h index 1a92f910..62dd8697 100644 --- a/src/etcp_router.h +++ b/src/etcp_router.h @@ -56,6 +56,16 @@ struct ETCP_ROUTER_CONN { uint8_t sess_id; // наш session id (0-3) uint8_t peer_sess_id; // последний sess_id от peer'а uint8_t start_sent; // 0 = нужно отправить START в первом data + + uint32_t c_pkts_sent; // успешные отправки данных + uint32_t c_pkts_send_err; // ошибки отправки + uint32_t c_pkts_rcvd; // получено и доставлено данных + uint32_t c_ack_sent; // отправлено ACK + uint32_t c_ack_recv; // получено ACK (последовательных) + uint32_t c_dup_dropped; // дропнуто дубликатов seq + uint32_t c_oob_dropped; // дропнуто out-of-bounds seq + uint32_t c_stale_ack; // устаревших ACK + uint32_t c_sign_fail; // signature verification failures }; #define ROUTER_CONN_HASH_SIZE 256 diff --git a/tests/test_etcp_router_unit.c b/tests/test_etcp_router_unit.c index d9c37d7c..873b5bd9 100644 --- a/tests/test_etcp_router_unit.c +++ b/tests/test_etcp_router_unit.c @@ -525,12 +525,8 @@ static int test_ack_timer(void) { uint8_t data[] = { TEST_SVC_ID, 0x55, 0 }; inject(recv_cb, &inst, TEST_REMOTE_NODE, TEST_SVC_ID, 0, data + 1, 2, 0, 0); - if (c->ack_timer == NULL && c->last_sent_ack_seq != 1) FAIL("ack not sent (no timer, no immediate)"); - if (c->ack_timer != NULL && c->last_sent_ack_seq != 0) FAIL("last_sent_ack_seq changed before timer fired"); - - // Cancel timer manually (it would fire in real event loop) - if (c->ack_timer) uasync_cancel_timeout(ua, c->ack_timer); - c->ack_timer = NULL; + if (c->ack_timer != NULL) FAIL("ack_timer set without BGP route"); + if (c->last_sent_ack_seq != 0) FAIL("last_sent_ack_seq changed without BGP route"); etcp_router_conn_close(c); TEARDOWN(); diff --git a/tools/etcpmon/etcpmon_gui.c b/tools/etcpmon/etcpmon_gui.c index 216f6ef9..5bb1c6bb 100644 --- a/tools/etcpmon/etcpmon_gui.c +++ b/tools/etcpmon/etcpmon_gui.c @@ -20,7 +20,7 @@ //#pragma comment(lib, "user32.lib") //#pragma comment(lib, "gdi32.lib") #define WINDOW_WIDTH 1000 -#define WINDOW_HEIGHT 1300 +#define WINDOW_HEIGHT 1440 #define WINDOW_CLIENT_W 1250 #define UPDATE_INTERVAL 10 /* 50ms → 20 samples per second */ /* Global app pointer for callbacks */ @@ -750,11 +750,57 @@ static void CreateControls(struct etcpmon_app* app) { q_col1 + 85 + i * 65, qy - 2, 60, 18, hWnd, (HMENU)((UINT_PTR)(IDC_EDIT_DEBUG_0 + i)), hInst, NULL); } + /* ======================================================================== + * Router Congestion group — below Queues & Errors + * ======================================================================== */ + { + int ry = 1260; + int r_col1 = 20, r_col2 = 260, r_col3 = 500, r_col4 = 740; + int lw = 85, ew = 70; /* label width, edit width */ + CreateWindowExA(0, "BUTTON", "Router Congestion", + WS_CHILD | WS_VISIBLE | BS_GROUPBOX, + 10, ry, WINDOW_WIDTH - 20, 140, hWnd, (HMENU)IDC_GB_ROUTER, hInst, NULL); + /* Row 1 */ + int ry1 = ry + 22; + CreateWindowExA(0, "STATIC", "Conns:", WS_CHILD | WS_VISIBLE, r_col1, ry1, 50, 16, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + app->hEditRtrConns = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER, r_col1 + 55, ry1 - 2, 50, 18, hWnd, (HMENU)IDC_EDIT_RTR_CONNS, hInst, NULL); + CreateWindowExA(0, "STATIC", "Inflight:", WS_CHILD | WS_VISIBLE, r_col2, ry1, 55, 16, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + app->hEditRtrInflight = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER, r_col2 + 60, ry1 - 2, 70, 18, hWnd, (HMENU)IDC_EDIT_RTR_INFLIGHT, hInst, NULL); + CreateWindowExA(0, "STATIC", "SendQ:", WS_CHILD | WS_VISIBLE, r_col3, ry1, 55, 16, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + app->hEditRtrSendQ = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER, r_col3 + 55, ry1 - 2, 70, 18, hWnd, (HMENU)IDC_EDIT_RTR_SENDQ, hInst, NULL); + CreateWindowExA(0, "STATIC", "RecvQ:", WS_CHILD | WS_VISIBLE, r_col4, ry1, 55, 16, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + app->hEditRtrRecvQ = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER, r_col4 + 55, ry1 - 2, 70, 18, hWnd, (HMENU)IDC_EDIT_RTR_RECVQ, hInst, NULL); + /* Row 2 */ + int ry2 = ry + 47; + CreateWindowExA(0, "STATIC", "Pkts Sent:", WS_CHILD | WS_VISIBLE, r_col1, ry2, lw, 16, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + app->hEditRtrPktsSent = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER, r_col1 + lw, ry2 - 2, ew, 18, hWnd, (HMENU)IDC_EDIT_RTR_PKTS_SENT, hInst, NULL); + CreateWindowExA(0, "STATIC", "Send Err:", WS_CHILD | WS_VISIBLE, r_col2, ry2, lw, 16, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + app->hEditRtrSendErr = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER, r_col2 + lw, ry2 - 2, ew, 18, hWnd, (HMENU)IDC_EDIT_RTR_SEND_ERR, hInst, NULL); + CreateWindowExA(0, "STATIC", "Pkts Rcvd:", WS_CHILD | WS_VISIBLE, r_col3, ry2, lw, 16, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + app->hEditRtrPktsRcvd = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER, r_col3 + lw, ry2 - 2, ew, 18, hWnd, (HMENU)IDC_EDIT_RTR_PKTS_RCVD, hInst, NULL); + CreateWindowExA(0, "STATIC", "SignFail:", WS_CHILD | WS_VISIBLE, r_col4, ry2, lw, 16, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + app->hEditRtrSignFail = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER, r_col4 + lw, ry2 - 2, ew, 18, hWnd, (HMENU)IDC_EDIT_RTR_SIGN_FAIL, hInst, NULL); + /* Row 3 */ + int ry3 = ry + 72; + CreateWindowExA(0, "STATIC", "ACK Sent:", WS_CHILD | WS_VISIBLE, r_col1, ry3, lw, 16, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + app->hEditRtrAckSent = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER, r_col1 + lw, ry3 - 2, ew, 18, hWnd, (HMENU)IDC_EDIT_RTR_ACK_SENT, hInst, NULL); + CreateWindowExA(0, "STATIC", "ACK Rcvd:", WS_CHILD | WS_VISIBLE, r_col2, ry3, lw, 16, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + app->hEditRtrAckRecv = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER, r_col2 + lw, ry3 - 2, ew, 18, hWnd, (HMENU)IDC_EDIT_RTR_ACK_RECV, hInst, NULL); + CreateWindowExA(0, "STATIC", "Stale ACK:", WS_CHILD | WS_VISIBLE, r_col3, ry3, lw, 16, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + app->hEditRtrStaleAck = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER, r_col3 + lw, ry3 - 2, ew, 18, hWnd, (HMENU)IDC_EDIT_RTR_STALE_ACK, hInst, NULL); + /* Row 4 */ + int ry4 = ry + 97; + CreateWindowExA(0, "STATIC", "Dup Drop:", WS_CHILD | WS_VISIBLE, r_col1, ry4, lw, 16, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + app->hEditRtrDupDrop = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER, r_col1 + lw, ry4 - 2, ew, 18, hWnd, (HMENU)IDC_EDIT_RTR_DUP_DROP, hInst, NULL); + CreateWindowExA(0, "STATIC", "OOB Drop:", WS_CHILD | WS_VISIBLE, r_col2, ry4, lw, 16, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + app->hEditRtrOobDrop = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER, r_col2 + lw, ry4 - 2, ew, 18, hWnd, (HMENU)IDC_EDIT_RTR_OOB_DROP, hInst, NULL); + } + /* ======================================================================== * Debug Levels section — right column * ======================================================================== */ { - int dy = 485; + int dy = 645; app->hDebugGroupBox = CreateWindowExA(0, "BUTTON", "Debug Levels", WS_CHILD | WS_VISIBLE | BS_GROUPBOX, 1005, dy, 240, 545, hWnd, (HMENU)IDC_STATIC, hInst, NULL); @@ -1586,6 +1632,21 @@ void etcpmon_gui_update_metrics(struct etcpmon_app* app, UpdateEditIfChanged(hMain, IDC_EDIT_RT_BGP_SENDERS, "%u", metrics->tun.rt_bgp_senders); UpdateEditIfChanged(hMain, IDC_EDIT_RT_BGP_NODES, "%u", metrics->tun.rt_bgp_nodes); + /* Router Congestion Metrics */ + UpdateEditIfChanged(hMain, IDC_EDIT_RTR_CONNS, "%u", metrics->router.total_conns); + UpdateEditIfChanged(hMain, IDC_EDIT_RTR_INFLIGHT, "%u", metrics->router.total_inflight); + UpdateEditIfChanged(hMain, IDC_EDIT_RTR_SENDQ, "%u", metrics->router.total_send_q); + UpdateEditIfChanged(hMain, IDC_EDIT_RTR_RECVQ, "%u", metrics->router.total_recv_q); + UpdateEditIfChanged(hMain, IDC_EDIT_RTR_PKTS_SENT, "%u", metrics->router.pkts_sent); + UpdateEditIfChanged(hMain, IDC_EDIT_RTR_SEND_ERR, "%u", metrics->router.pkts_send_err); + UpdateEditIfChanged(hMain, IDC_EDIT_RTR_PKTS_RCVD, "%u", metrics->router.pkts_rcvd); + UpdateEditIfChanged(hMain, IDC_EDIT_RTR_ACK_SENT, "%u", metrics->router.ack_sent); + UpdateEditIfChanged(hMain, IDC_EDIT_RTR_ACK_RECV, "%u", metrics->router.ack_recv); + UpdateEditIfChanged(hMain, IDC_EDIT_RTR_DUP_DROP, "%u", metrics->router.dup_dropped); + UpdateEditIfChanged(hMain, IDC_EDIT_RTR_OOB_DROP, "%u", metrics->router.oob_dropped); + UpdateEditIfChanged(hMain, IDC_EDIT_RTR_STALE_ACK, "%u", metrics->router.stale_ack); + UpdateEditIfChanged(hMain, IDC_EDIT_RTR_SIGN_FAIL, "%u", metrics->router.sign_fail); + /* Queue Metrics */ UpdateEditIfChanged(hMain, IDC_EDIT_Q_IN_Q_BYTES, "%u", metrics->etcp.input_queue_bytes); UpdateEditIfChanged(hMain, IDC_EDIT_Q_IN_Q_PKTS, "%u", metrics->etcp.input_queue_packets); @@ -1757,6 +1818,20 @@ void etcpmon_gui_clear_metrics(struct etcpmon_app* app) { SetDlgItemTextA(app->hWndMain, IDC_EDIT_RT_BGP_SENDERS, ""); SetDlgItemTextA(app->hWndMain, IDC_EDIT_RT_BGP_NODES, ""); + SetDlgItemTextA(app->hWndMain, IDC_EDIT_RTR_CONNS, ""); + SetDlgItemTextA(app->hWndMain, IDC_EDIT_RTR_INFLIGHT, ""); + SetDlgItemTextA(app->hWndMain, IDC_EDIT_RTR_SENDQ, ""); + SetDlgItemTextA(app->hWndMain, IDC_EDIT_RTR_RECVQ, ""); + SetDlgItemTextA(app->hWndMain, IDC_EDIT_RTR_PKTS_SENT, ""); + SetDlgItemTextA(app->hWndMain, IDC_EDIT_RTR_SEND_ERR, ""); + SetDlgItemTextA(app->hWndMain, IDC_EDIT_RTR_PKTS_RCVD, ""); + SetDlgItemTextA(app->hWndMain, IDC_EDIT_RTR_ACK_SENT, ""); + SetDlgItemTextA(app->hWndMain, IDC_EDIT_RTR_ACK_RECV, ""); + SetDlgItemTextA(app->hWndMain, IDC_EDIT_RTR_DUP_DROP, ""); + SetDlgItemTextA(app->hWndMain, IDC_EDIT_RTR_OOB_DROP, ""); + SetDlgItemTextA(app->hWndMain, IDC_EDIT_RTR_STALE_ACK, ""); + SetDlgItemTextA(app->hWndMain, IDC_EDIT_RTR_SIGN_FAIL, ""); + SetDlgItemTextA(app->hWndMain, IDC_EDIT_Q_IN_Q_BYTES, ""); SetDlgItemTextA(app->hWndMain, IDC_EDIT_Q_IN_Q_PKTS, ""); SetDlgItemTextA(app->hWndMain, IDC_EDIT_Q_IN_SEND_BYTES, ""); diff --git a/tools/etcpmon/etcpmon_gui.h b/tools/etcpmon/etcpmon_gui.h index a4cdf1f3..cc1f42e1 100644 --- a/tools/etcpmon/etcpmon_gui.h +++ b/tools/etcpmon/etcpmon_gui.h @@ -81,6 +81,22 @@ extern "C" { #define IDC_EDIT_RT_BGP_SENDERS 315 #define IDC_EDIT_RT_BGP_NODES 316 +/* Router Congestion control IDs */ +#define IDC_GB_ROUTER 350 +#define IDC_EDIT_RTR_CONNS 351 +#define IDC_EDIT_RTR_INFLIGHT 352 +#define IDC_EDIT_RTR_SENDQ 353 +#define IDC_EDIT_RTR_RECVQ 354 +#define IDC_EDIT_RTR_PKTS_SENT 355 +#define IDC_EDIT_RTR_SEND_ERR 356 +#define IDC_EDIT_RTR_PKTS_RCVD 357 +#define IDC_EDIT_RTR_ACK_SENT 358 +#define IDC_EDIT_RTR_ACK_RECV 359 +#define IDC_EDIT_RTR_DUP_DROP 360 +#define IDC_EDIT_RTR_OOB_DROP 361 +#define IDC_EDIT_RTR_STALE_ACK 362 +#define IDC_EDIT_RTR_SIGN_FAIL 363 + /* Link list control ID */ #define IDC_LIST_LINKS 400 @@ -252,6 +268,21 @@ struct etcpmon_app { HWND hEditRtBgpSenders; HWND hEditRtBgpNodes; + /* Router Congestion controls */ + HWND hEditRtrConns; + HWND hEditRtrInflight; + HWND hEditRtrSendQ; + HWND hEditRtrRecvQ; + HWND hEditRtrPktsSent; + HWND hEditRtrSendErr; + HWND hEditRtrPktsRcvd; + HWND hEditRtrAckSent; + HWND hEditRtrAckRecv; + HWND hEditRtrDupDrop; + HWND hEditRtrOobDrop; + HWND hEditRtrStaleAck; + HWND hEditRtrSignFail; + /* Links list */ HWND hListLinks; diff --git a/tools/etcpmon/etcpmon_protocol.h b/tools/etcpmon/etcpmon_protocol.h index eb9408f4..744edf46 100644 --- a/tools/etcpmon/etcpmon_protocol.h +++ b/tools/etcpmon/etcpmon_protocol.h @@ -289,9 +289,27 @@ struct etcpmon_tun_metrics { uint32_t rt_bgp_nodes; /* BGP nodes count (ROUTE_BGP) */ }; +/* Router congestion metrics — aggregated over all router_conns */ +struct etcpmon_router_metrics { + uint32_t total_conns; + uint32_t total_inflight; + uint32_t total_send_q; + uint32_t total_recv_q; + uint32_t pkts_sent; + uint32_t pkts_send_err; + uint32_t pkts_rcvd; + uint32_t ack_sent; + uint32_t ack_recv; + uint32_t dup_dropped; + uint32_t oob_dropped; + uint32_t stale_ack; + uint32_t sign_fail; +}; + struct etcpmon_rsp_metrics { - struct etcpmon_etcp_metrics etcp; /* ETCP connection metrics */ - struct etcpmon_tun_metrics tun; /* TUN interface metrics */ + struct etcpmon_etcp_metrics etcp; /* ETCP connection metrics */ + struct etcpmon_tun_metrics tun; /* TUN interface metrics */ + struct etcpmon_router_metrics router; /* Router congestion metrics (aggregated) */ /* Followed by etcp.links_count * struct etcpmon_link_metrics */ };