// icmp_proxy.h — ICMP echo (ping) прокси: client ↔ exit через etcp_router #ifndef ICMP_PROXY_H #define ICMP_PROXY_H #include #include "../lib/socket_compat.h" struct UTUN_INSTANCE; struct UASYNC; struct ll_entry; struct ETCP_CONN; // Sub-commands #define ICMP_PROXY_SUBCMD_REQUEST 0x01 // client→exit: пингани dst_ip #define ICMP_PROXY_SUBCMD_REPLY 0x02 // exit→client: echo reply // Message header (excluding svc_id byte) // svc_id(1) + subcmd(1) + sender_node_id(8) + dst_ip(4) + orig_src_ip(4) + icmp_id(2) + icmp_seq(2) + payload #define ICMP_PROXY_HDR_SIZE 22 // In-flight ICMP echo request (exit node side) struct icmp_request { struct icmp_request* next; uint64_t client_node_id; uint32_t dst_ip; uint32_t orig_src_ip; // IP исходного отправителя (чтобы вернуть reply правильному адресату) uint16_t echo_id; uint16_t echo_seq; uint8_t payload[1500]; size_t payload_len; uint64_t sent_tb; socket_t sock; // raw socket (only if we use per-request) }; struct icmp_proxy_ctx { int initialized; int is_exit; struct UTUN_INSTANCE* inst; struct UASYNC* ua; socket_t raw_sock; // one SOCK_RAW for all ICMP on exit void* raw_read_id; struct icmp_request* pending; uint64_t request_timeout_tb; int test_loopback; // 1 = virtual loopback when no raw socket (test only) }; int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua); void icmp_proxy_destroy(struct UTUN_INSTANCE* inst); // Unified etcp_router callback void icmp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry); // Client side: принять IP/ICMP echo request с TUN, отправить через etcp int icmp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id, uint32_t dst_ip, uint32_t orig_src_ip, uint16_t echo_id, uint16_t echo_seq, const uint8_t* payload, size_t payload_len); int icmp_proxy_deliver_reply(struct UTUN_INSTANCE* inst, uint32_t dst_ip, uint32_t src_ip, uint16_t echo_id, uint16_t echo_seq, const uint8_t* payload, size_t payload_len); void icmp_proxy_set_test_loopback(struct UTUN_INSTANCE* inst, int enabled); #endif // ICMP_PROXY_H