|
|
|
@ -13,6 +13,7 @@ |
|
|
|
#include <stdlib.h> |
|
|
|
#include <stdlib.h> |
|
|
|
#include <string.h> |
|
|
|
#include <string.h> |
|
|
|
#include <errno.h> |
|
|
|
#include <errno.h> |
|
|
|
|
|
|
|
#include <openssl/rand.h> |
|
|
|
#ifndef _WIN32 |
|
|
|
#ifndef _WIN32 |
|
|
|
#include <unistd.h> |
|
|
|
#include <unistd.h> |
|
|
|
#include <netinet/in.h> |
|
|
|
#include <netinet/in.h> |
|
|
|
@ -48,11 +49,11 @@ struct ip { |
|
|
|
#define ICMP_DEF_TTL 64 |
|
|
|
#define ICMP_DEF_TTL 64 |
|
|
|
#define ICMP_TIMEOUT_TB 50000 // 5s for echo reply
|
|
|
|
#define ICMP_TIMEOUT_TB 50000 // 5s for echo reply
|
|
|
|
|
|
|
|
|
|
|
|
struct icmp_proxy_ctx* g_icmp_ctx = NULL; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void req_expire_timer_cb(void* arg); |
|
|
|
static void req_expire_timer_cb(void* arg); |
|
|
|
static void req_expire(struct icmp_proxy_ctx* ctx); |
|
|
|
static void req_expire(struct icmp_proxy_ctx* ctx); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Сумма по сетевому порядку; нечётный хвост дополняется нулём без чтения за границей.
|
|
|
|
static uint16_t icmp_checksum(const uint8_t* data, size_t len) { |
|
|
|
static uint16_t icmp_checksum(const uint8_t* data, size_t len) { |
|
|
|
uint32_t sum = 0; |
|
|
|
uint32_t sum = 0; |
|
|
|
while (len >= 2) { sum += ((uint16_t)data[0] << 8) | data[1]; data += 2; len -= 2; } |
|
|
|
while (len >= 2) { sum += ((uint16_t)data[0] << 8) | data[1]; data += 2; len -= 2; } |
|
|
|
@ -61,9 +62,9 @@ static uint16_t icmp_checksum(const uint8_t* data, size_t len) { |
|
|
|
return htons((uint16_t)~sum); |
|
|
|
return htons((uint16_t)~sum); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static struct icmp_request* req_find_by_id(struct icmp_request* head, uint16_t echo_id, uint16_t echo_seq) { |
|
|
|
static struct icmp_request* req_find_by_id(struct icmp_request* head, uint16_t echo_id, uint16_t echo_seq, uint32_t src_ip) { |
|
|
|
struct icmp_request* r; |
|
|
|
struct icmp_request* r; |
|
|
|
for (r = head; r; r = r->next) if (r->echo_id == echo_id && r->echo_seq == echo_seq) return r; |
|
|
|
for (r = head; r; r = r->next) if (r->wire_id == echo_id && r->wire_seq == echo_seq && r->dst_ip == src_ip) return r; |
|
|
|
return NULL; |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -71,17 +72,25 @@ static struct icmp_request* req_find_by_id(struct icmp_request* head, uint16_t e |
|
|
|
static int exit_send_echo(struct UTUN_INSTANCE* inst, uint64_t client_node_id, |
|
|
|
static int exit_send_echo(struct UTUN_INSTANCE* inst, uint64_t client_node_id, |
|
|
|
uint32_t dst_ip, uint32_t orig_src_ip, uint16_t echo_id, uint16_t echo_seq, |
|
|
|
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) { |
|
|
|
const uint8_t* payload, size_t payload_len) { |
|
|
|
if (!g_icmp_ctx || g_icmp_ctx->raw_sock == SOCKET_INVALID) return -1; |
|
|
|
struct icmp_proxy_ctx* ctx = inst ? inst->icmp_proxy : NULL; |
|
|
|
|
|
|
|
if (!ctx || ctx->raw_sock == SOCKET_INVALID) return -1; |
|
|
|
|
|
|
|
if (payload_len > 65507) { DEBUG_WARN(DEBUG_CATEGORY_PROXY, "icmp_proxy: payload too large len=%zu", payload_len); return -1; } |
|
|
|
size_t icmp_len = ICMP_MINLEN + payload_len; |
|
|
|
size_t icmp_len = ICMP_MINLEN + payload_len; |
|
|
|
uint8_t* buf = u_malloc(icmp_len); |
|
|
|
uint8_t* buf = u_malloc(icmp_len); |
|
|
|
if (!buf) return -1; |
|
|
|
if (!buf) return -1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct icmp_request* r = u_calloc(1, sizeof(*r)); |
|
|
|
|
|
|
|
if (!r) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: request allocation failed"); u_free(buf); return -1; } |
|
|
|
|
|
|
|
do { |
|
|
|
|
|
|
|
uint32_t token = ++ctx->next_token; |
|
|
|
|
|
|
|
r->wire_id = htons((uint16_t)(token >> 16)); r->wire_seq = htons((uint16_t)token); |
|
|
|
|
|
|
|
} while (req_find_by_id(ctx->pending, r->wire_id, r->wire_seq, dst_ip)); |
|
|
|
struct icmp* icmp_hdr = (struct icmp*)buf; |
|
|
|
struct icmp* icmp_hdr = (struct icmp*)buf; |
|
|
|
memset(icmp_hdr, 0, icmp_len); |
|
|
|
memset(icmp_hdr, 0, icmp_len); |
|
|
|
icmp_hdr->icmp_type = ICMP_ECHO; |
|
|
|
icmp_hdr->icmp_type = ICMP_ECHO; |
|
|
|
icmp_hdr->icmp_code = 0; |
|
|
|
icmp_hdr->icmp_code = 0; |
|
|
|
icmp_hdr->icmp_id = echo_id; |
|
|
|
icmp_hdr->icmp_id = r->wire_id; |
|
|
|
icmp_hdr->icmp_seq = echo_seq; |
|
|
|
icmp_hdr->icmp_seq = r->wire_seq; |
|
|
|
if (payload_len > 0) memcpy(icmp_hdr->icmp_data, payload, payload_len); |
|
|
|
if (payload_len > 0) memcpy(icmp_hdr->icmp_data, payload, payload_len); |
|
|
|
icmp_hdr->icmp_cksum = 0; |
|
|
|
icmp_hdr->icmp_cksum = 0; |
|
|
|
icmp_hdr->icmp_cksum = icmp_checksum((const uint8_t*)icmp_hdr, icmp_len); |
|
|
|
icmp_hdr->icmp_cksum = icmp_checksum((const uint8_t*)icmp_hdr, icmp_len); |
|
|
|
@ -89,66 +98,68 @@ static int exit_send_echo(struct UTUN_INSTANCE* inst, uint64_t client_node_id, |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: sendto dst=0x%08x id=0x%04x seq=%u len=%zu", |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: sendto dst=0x%08x id=0x%04x seq=%u len=%zu", |
|
|
|
dst_ip, echo_id, echo_seq, icmp_len); |
|
|
|
dst_ip, echo_id, echo_seq, icmp_len); |
|
|
|
struct sockaddr_in addr = {.sin_family = AF_INET, .sin_addr = {.s_addr = dst_ip}}; |
|
|
|
struct sockaddr_in addr = {.sin_family = AF_INET, .sin_addr = {.s_addr = dst_ip}}; |
|
|
|
ssize_t n = sendto(g_icmp_ctx->raw_sock, buf, icmp_len, 0, (struct sockaddr*)&addr, sizeof(addr)); |
|
|
|
ssize_t n = sendto(ctx->raw_sock, buf, icmp_len, 0, (struct sockaddr*)&addr, sizeof(addr)); |
|
|
|
u_free(buf); |
|
|
|
u_free(buf); |
|
|
|
if (n < 0) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: sendto failed: %s", strerror(errno)); return -1; } |
|
|
|
if (n < 0) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: sendto failed: %s", strerror(errno)); u_free(r); return -1; } |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: sendto sent %zd bytes", n); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: sendto sent %zd bytes", n); |
|
|
|
|
|
|
|
|
|
|
|
struct icmp_request* r = u_calloc(1, sizeof(struct icmp_request)); |
|
|
|
|
|
|
|
if (!r) return 0; |
|
|
|
|
|
|
|
r->client_node_id = client_node_id; r->dst_ip = dst_ip; r->orig_src_ip = orig_src_ip; |
|
|
|
r->client_node_id = client_node_id; r->dst_ip = dst_ip; r->orig_src_ip = orig_src_ip; |
|
|
|
r->echo_id = echo_id; r->echo_seq = echo_seq; |
|
|
|
r->echo_id = echo_id; r->echo_seq = echo_seq; |
|
|
|
r->payload_len = payload_len > sizeof(r->payload) ? sizeof(r->payload) : payload_len; |
|
|
|
r->payload_len = payload_len > sizeof(r->payload) ? sizeof(r->payload) : payload_len; |
|
|
|
if (payload_len > 0) memcpy(r->payload, payload, r->payload_len); |
|
|
|
if (payload_len > 0) memcpy(r->payload, payload, r->payload_len); |
|
|
|
r->sent_tb = get_time_tb(); |
|
|
|
r->sent_tb = get_time_tb(); |
|
|
|
r->next = g_icmp_ctx->pending; g_icmp_ctx->pending = r; |
|
|
|
r->next = ctx->pending; ctx->pending = r; |
|
|
|
if (!g_icmp_ctx->expire_timer) |
|
|
|
if (!ctx->expire_timer) |
|
|
|
g_icmp_ctx->expire_timer = uasync_set_timeout(g_icmp_ctx->ua, g_icmp_ctx->request_timeout_tb, g_icmp_ctx, req_expire_timer_cb, "icmp_expire"); |
|
|
|
ctx->expire_timer = uasync_set_timeout(ctx->ua, ctx->request_timeout_tb, ctx, req_expire_timer_cb, "icmp_expire"); |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Чтение echo ответа из raw сокета, сопоставление по echo_id
|
|
|
|
// Чтение echo ответа из raw сокета, сопоставление по echo_id
|
|
|
|
static void raw_read_cb(socket_t sock, void* arg) { |
|
|
|
static void raw_read_cb(socket_t sock, void* arg) { |
|
|
|
(void)sock; (void)arg; |
|
|
|
(void)sock; |
|
|
|
if (!g_icmp_ctx || g_icmp_ctx->raw_sock == SOCKET_INVALID) return; |
|
|
|
struct icmp_proxy_ctx* ctx = arg; |
|
|
|
|
|
|
|
if (!ctx || ctx->raw_sock == SOCKET_INVALID) return; |
|
|
|
uint8_t buf[65536]; |
|
|
|
uint8_t buf[65536]; |
|
|
|
struct sockaddr_in from; socklen_t flen = sizeof(from); |
|
|
|
struct sockaddr_in from; socklen_t flen = sizeof(from); |
|
|
|
ssize_t n = recvfrom(g_icmp_ctx->raw_sock, buf, sizeof(buf), 0, (struct sockaddr*)&from, &flen); |
|
|
|
ssize_t n = recvfrom(ctx->raw_sock, buf, sizeof(buf), 0, (struct sockaddr*)&from, &flen); |
|
|
|
if (n <= 0) { if (n < 0) DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: recvfrom error: %s", strerror(errno)); return; } |
|
|
|
if (n <= 0) { if (n < 0) DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: recvfrom error: %s", strerror(errno)); return; } |
|
|
|
if (n < (ssize_t)(sizeof(struct ip) + ICMP_MINLEN)) return; |
|
|
|
if (n < (ssize_t)(sizeof(struct ip) + ICMP_MINLEN)) return; |
|
|
|
|
|
|
|
|
|
|
|
struct ip* ip_hdr = (struct ip*)buf; |
|
|
|
struct ip* ip_hdr = (struct ip*)buf; |
|
|
|
if (ip_hdr->ip_p != IPPROTO_ICMP) return; |
|
|
|
if (ip_hdr->ip_p != IPPROTO_ICMP) return; |
|
|
|
size_t ip_hdr_len = ip_hdr->ip_hl * 4; |
|
|
|
size_t ip_hdr_len = ip_hdr->ip_hl * 4; |
|
|
|
if (n < (ssize_t)(ip_hdr_len + ICMP_MINLEN)) return; |
|
|
|
if (ip_hdr_len < 20 || n < (ssize_t)(ip_hdr_len + ICMP_MINLEN)) return; |
|
|
|
struct icmp* icmp_hdr = (struct icmp*)(buf + ip_hdr_len); |
|
|
|
struct icmp* icmp_hdr = (struct icmp*)(buf + ip_hdr_len); |
|
|
|
if (icmp_hdr->icmp_type != ICMP_ECHOREPLY) return; |
|
|
|
if (icmp_hdr->icmp_type != ICMP_ECHOREPLY || icmp_hdr->icmp_code != 0) return; |
|
|
|
|
|
|
|
if (icmp_checksum((const uint8_t*)icmp_hdr, n - ip_hdr_len) != 0) { |
|
|
|
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_PROXY, "icmp_proxy: invalid reply checksum"); return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct icmp_request* r = req_find_by_id(g_icmp_ctx->pending, icmp_hdr->icmp_id, icmp_hdr->icmp_seq); |
|
|
|
struct icmp_request* r = req_find_by_id(ctx->pending, icmp_hdr->icmp_id, icmp_hdr->icmp_seq, from.sin_addr.s_addr); |
|
|
|
if (!r) { DEBUG_WARN(DEBUG_CATEGORY_PROXY, "icmp_proxy: unclaimed echo reply id=0x%04x seq=%u from=0x%08x", |
|
|
|
if (!r) { DEBUG_WARN(DEBUG_CATEGORY_PROXY, "icmp_proxy: unclaimed echo reply id=0x%04x seq=%u from=0x%08x", |
|
|
|
icmp_hdr->icmp_id, icmp_hdr->icmp_seq, from.sin_addr.s_addr); return; } |
|
|
|
icmp_hdr->icmp_id, icmp_hdr->icmp_seq, from.sin_addr.s_addr); return; } |
|
|
|
{ struct icmp_request** rp = &g_icmp_ctx->pending; |
|
|
|
{ struct icmp_request** rp = &ctx->pending; |
|
|
|
while (*rp) { if (*rp == r) { *rp = r->next; break; } rp = &(*rp)->next; } } |
|
|
|
while (*rp) { if (*rp == r) { *rp = r->next; break; } rp = &(*rp)->next; } } |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: echo reply id=0x%04x seq=%u from=0x%08x", |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: echo reply id=0x%04x seq=%u from=0x%08x", |
|
|
|
icmp_hdr->icmp_id, icmp_hdr->icmp_seq, from.sin_addr.s_addr); |
|
|
|
icmp_hdr->icmp_id, icmp_hdr->icmp_seq, from.sin_addr.s_addr); |
|
|
|
|
|
|
|
|
|
|
|
size_t payload_len = n - ip_hdr_len - ICMP_MINLEN; |
|
|
|
size_t payload_len = n - ip_hdr_len - ICMP_MINLEN; |
|
|
|
if (payload_len > 1500) payload_len = 1500; |
|
|
|
|
|
|
|
uint8_t* payload = (payload_len > 0) ? (uint8_t*)(icmp_hdr->icmp_data) : NULL; |
|
|
|
uint8_t* payload = (payload_len > 0) ? (uint8_t*)(icmp_hdr->icmp_data) : NULL; |
|
|
|
|
|
|
|
|
|
|
|
struct ll_entry* e = queue_entry_new(0); |
|
|
|
struct ll_entry* e = queue_entry_new(0); |
|
|
|
if (!e) return; |
|
|
|
if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: reply entry allocation failed"); u_free(r); return; } |
|
|
|
e->dgram = u_malloc(ICMP_PROXY_HDR_SIZE + payload_len); |
|
|
|
e->dgram = u_malloc(ICMP_PROXY_HDR_SIZE + payload_len); |
|
|
|
if (!e->dgram) { queue_entry_free(e); return; } |
|
|
|
if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: reply allocation failed"); queue_entry_free(e); u_free(r); return; } |
|
|
|
e->dgram[0] = ETCP_RT_ID_ICMP_PROXY; |
|
|
|
e->dgram[0] = ETCP_RT_ID_ICMP_PROXY; |
|
|
|
e->dgram[1] = ICMP_PROXY_SUBCMD_REPLY; |
|
|
|
e->dgram[1] = ICMP_PROXY_SUBCMD_REPLY; |
|
|
|
memcpy(e->dgram + 2, &r->dst_ip, 4); |
|
|
|
memcpy(e->dgram + 2, &r->dst_ip, 4); |
|
|
|
memcpy(e->dgram + 6, &r->orig_src_ip, 4); |
|
|
|
memcpy(e->dgram + 6, &r->orig_src_ip, 4); |
|
|
|
memcpy(e->dgram + 10, &icmp_hdr->icmp_id, 2); |
|
|
|
memcpy(e->dgram + 10, &r->echo_id, 2); |
|
|
|
memcpy(e->dgram + 12, &icmp_hdr->icmp_seq, 2); |
|
|
|
memcpy(e->dgram + 12, &r->echo_seq, 2); |
|
|
|
if (payload_len > 0) memcpy(e->dgram + ICMP_PROXY_HDR_SIZE, payload, payload_len); |
|
|
|
if (payload_len > 0) memcpy(e->dgram + ICMP_PROXY_HDR_SIZE, payload, payload_len); |
|
|
|
e->len = ICMP_PROXY_HDR_SIZE + payload_len; |
|
|
|
e->len = ICMP_PROXY_HDR_SIZE + payload_len; |
|
|
|
int ret = etcp_route_send(g_icmp_ctx->inst, TOPO_GROUP_UTUN, r->client_node_id, e, 0, 0); |
|
|
|
int ret = etcp_route_send(ctx->inst, TOPO_GROUP_UTUN, r->client_node_id, e, 0, 0); |
|
|
|
if (ret != 0) DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: etcp_route_send reply failed: %d", ret); |
|
|
|
if (ret != 0) DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: etcp_route_send reply failed: %d", ret); |
|
|
|
else DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: reply forwarded to client %016llx", (unsigned long long)r->client_node_id); |
|
|
|
else DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: reply forwarded to client %016llx", (unsigned long long)r->client_node_id); |
|
|
|
u_free(r); |
|
|
|
u_free(r); |
|
|
|
@ -158,8 +169,9 @@ static void raw_read_cb(socket_t sock, void* arg) { |
|
|
|
// Exit узел: принять REQUEST, отправить echo через raw сокет
|
|
|
|
// Exit узел: принять REQUEST, отправить echo через raw сокет
|
|
|
|
// ====================================================================
|
|
|
|
// ====================================================================
|
|
|
|
static void exit_handle_request(struct ETCP_CONN* conn, struct ll_entry* entry) { |
|
|
|
static void exit_handle_request(struct ETCP_CONN* conn, struct ll_entry* entry) { |
|
|
|
struct UTUN_INSTANCE* inst = conn ? conn->instance : (g_icmp_ctx ? g_icmp_ctx->inst : NULL); |
|
|
|
struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL; |
|
|
|
if (!inst || !g_icmp_ctx || entry->len < ICMP_PROXY_RECV_HDR_SIZE + 1) goto drop; |
|
|
|
struct icmp_proxy_ctx* ctx = inst ? inst->icmp_proxy : NULL; |
|
|
|
|
|
|
|
if (!inst || !ctx || !inst->tcp_proxy_server.enabled || entry->len < ICMP_PROXY_RECV_HDR_SIZE) goto drop; |
|
|
|
|
|
|
|
|
|
|
|
uint64_t client_node_id; memcpy(&client_node_id, entry->dgram + ROUTER_SVC_SRC_OFF, 8); |
|
|
|
uint64_t client_node_id; memcpy(&client_node_id, entry->dgram + ROUTER_SVC_SRC_OFF, 8); |
|
|
|
uint32_t dst_ip; memcpy(&dst_ip, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, 4); |
|
|
|
uint32_t dst_ip; memcpy(&dst_ip, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, 4); |
|
|
|
@ -169,9 +181,9 @@ static void exit_handle_request(struct ETCP_CONN* conn, struct ll_entry* entry) |
|
|
|
uint8_t* payload = entry->dgram + ICMP_PROXY_RECV_HDR_SIZE; |
|
|
|
uint8_t* payload = entry->dgram + ICMP_PROXY_RECV_HDR_SIZE; |
|
|
|
size_t payload_len = entry->len - ICMP_PROXY_RECV_HDR_SIZE; |
|
|
|
size_t payload_len = entry->len - ICMP_PROXY_RECV_HDR_SIZE; |
|
|
|
|
|
|
|
|
|
|
|
if (g_icmp_ctx->raw_sock != SOCKET_INVALID) { |
|
|
|
if (ctx->raw_sock != SOCKET_INVALID) { |
|
|
|
exit_send_echo(inst, client_node_id, dst_ip, orig_src_ip, echo_id, echo_seq, payload, payload_len); |
|
|
|
exit_send_echo(inst, client_node_id, dst_ip, orig_src_ip, echo_id, echo_seq, payload, payload_len); |
|
|
|
} else if (g_icmp_ctx->test_loopback) { |
|
|
|
} else if (ctx->test_loopback) { |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: test loopback reply to 0x%08x", dst_ip); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: test loopback reply to 0x%08x", dst_ip); |
|
|
|
struct ll_entry* e = queue_entry_new(0); |
|
|
|
struct ll_entry* e = queue_entry_new(0); |
|
|
|
if (e) { |
|
|
|
if (e) { |
|
|
|
@ -200,7 +212,7 @@ drop: |
|
|
|
// Сторона клиента: принять REPLY, доставить echo ответ в TUN
|
|
|
|
// Сторона клиента: принять REPLY, доставить echo ответ в TUN
|
|
|
|
// ====================================================================
|
|
|
|
// ====================================================================
|
|
|
|
static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry) { |
|
|
|
static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry) { |
|
|
|
if (entry->len < ICMP_PROXY_RECV_HDR_SIZE + 1) { queue_dgram_free(entry); queue_entry_free(entry); return; } |
|
|
|
if (entry->len < ICMP_PROXY_RECV_HDR_SIZE) { queue_dgram_free(entry); queue_entry_free(entry); return; } |
|
|
|
uint32_t src_ip; |
|
|
|
uint32_t src_ip; |
|
|
|
uint32_t orig_src_ip; |
|
|
|
uint32_t orig_src_ip; |
|
|
|
uint16_t echo_id, echo_seq; |
|
|
|
uint16_t echo_id, echo_seq; |
|
|
|
@ -210,7 +222,7 @@ static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry) |
|
|
|
memcpy(&echo_seq, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 11, 2); |
|
|
|
memcpy(&echo_seq, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 11, 2); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: client got reply id=0x%04x seq=%u from=0x%08x dst=0x%08x", |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: client got reply id=0x%04x seq=%u from=0x%08x dst=0x%08x", |
|
|
|
echo_id, echo_seq, src_ip, orig_src_ip); |
|
|
|
echo_id, echo_seq, src_ip, orig_src_ip); |
|
|
|
struct UTUN_INSTANCE* inst = conn ? conn->instance : (g_icmp_ctx ? g_icmp_ctx->inst : NULL); |
|
|
|
struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL; |
|
|
|
icmp_proxy_deliver_reply(inst, orig_src_ip, src_ip, echo_id, echo_seq, |
|
|
|
icmp_proxy_deliver_reply(inst, orig_src_ip, src_ip, echo_id, echo_seq, |
|
|
|
entry->dgram + ICMP_PROXY_RECV_HDR_SIZE, entry->len - ICMP_PROXY_RECV_HDR_SIZE); |
|
|
|
entry->dgram + ICMP_PROXY_RECV_HDR_SIZE, entry->len - ICMP_PROXY_RECV_HDR_SIZE); |
|
|
|
queue_dgram_free(entry); queue_entry_free(entry); |
|
|
|
queue_dgram_free(entry); queue_entry_free(entry); |
|
|
|
@ -220,13 +232,20 @@ static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry) |
|
|
|
// Единый etcp_router коллбэк
|
|
|
|
// Единый etcp_router коллбэк
|
|
|
|
// ====================================================================
|
|
|
|
// ====================================================================
|
|
|
|
void icmp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { |
|
|
|
void icmp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { |
|
|
|
if (!entry || !entry->dgram || entry->len < ROUTER_SVC_HDR_SIZE) { |
|
|
|
if (!entry || !entry->dgram || entry->len <= ROUTER_SVC_HDR_SIZE) { |
|
|
|
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } |
|
|
|
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
uint8_t subcmd = entry->dgram[ROUTER_SVC_PAYLOAD_OFF]; |
|
|
|
uint8_t subcmd = entry->dgram[ROUTER_SVC_PAYLOAD_OFF]; |
|
|
|
if (subcmd == ICMP_PROXY_SUBCMD_REQUEST) { exit_handle_request(conn, entry); return; } |
|
|
|
struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL; |
|
|
|
if (subcmd == ICMP_PROXY_SUBCMD_REPLY) { client_handle_reply(conn, entry); return; } |
|
|
|
uint64_t peer; memcpy(&peer, entry->dgram + ROUTER_SVC_SRC_OFF, 8); |
|
|
|
|
|
|
|
if (subcmd == ICMP_PROXY_SUBCMD_REQUEST && inst && inst->tcp_proxy_server.enabled) { |
|
|
|
|
|
|
|
exit_handle_request(conn, entry); return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (subcmd == ICMP_PROXY_SUBCMD_REPLY && inst && inst->tcp_proxy_client && peer == inst->tcp_proxy_client->via_node_id) { |
|
|
|
|
|
|
|
client_handle_reply(conn, entry); return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_PROXY, "icmp_proxy: rejected subcmd=%u peer=%016llx", subcmd, (unsigned long long)peer); |
|
|
|
queue_dgram_free(entry); queue_entry_free(entry); |
|
|
|
queue_dgram_free(entry); queue_entry_free(entry); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -236,7 +255,7 @@ void icmp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { |
|
|
|
int icmp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id, |
|
|
|
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, |
|
|
|
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) { |
|
|
|
const uint8_t* payload, size_t payload_len) { |
|
|
|
if (!inst) return -1; |
|
|
|
if (!inst || payload_len > 65507) { DEBUG_WARN(DEBUG_CATEGORY_PROXY, "icmp_proxy: invalid send"); return -1; } |
|
|
|
struct ll_entry* e = queue_entry_new(0); |
|
|
|
struct ll_entry* e = queue_entry_new(0); |
|
|
|
if (!e) return -1; |
|
|
|
if (!e) return -1; |
|
|
|
e->dgram = u_malloc(ICMP_PROXY_HDR_SIZE + payload_len); |
|
|
|
e->dgram = u_malloc(ICMP_PROXY_HDR_SIZE + payload_len); |
|
|
|
@ -301,8 +320,8 @@ int icmp_proxy_deliver_reply(struct UTUN_INSTANCE* inst, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void icmp_proxy_set_test_loopback(struct UTUN_INSTANCE* inst, int enabled) { |
|
|
|
void icmp_proxy_set_test_loopback(struct UTUN_INSTANCE* inst, int enabled) { |
|
|
|
(void)inst; |
|
|
|
struct icmp_proxy_ctx* ctx = inst ? inst->icmp_proxy : NULL; |
|
|
|
if (g_icmp_ctx) g_icmp_ctx->test_loopback = enabled; |
|
|
|
if (ctx) ctx->test_loopback = enabled; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void req_expire_timer_cb(void* arg) { |
|
|
|
static void req_expire_timer_cb(void* arg) { |
|
|
|
@ -327,6 +346,7 @@ static void req_expire(struct icmp_proxy_ctx* ctx) { |
|
|
|
// ====================================================================
|
|
|
|
// ====================================================================
|
|
|
|
int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) { |
|
|
|
int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) { |
|
|
|
if (!inst) return -1; |
|
|
|
if (!inst) return -1; |
|
|
|
|
|
|
|
if (inst->icmp_proxy) return 0; |
|
|
|
struct icmp_proxy_ctx* ctx = u_calloc(1, sizeof(struct icmp_proxy_ctx)); |
|
|
|
struct icmp_proxy_ctx* ctx = u_calloc(1, sizeof(struct icmp_proxy_ctx)); |
|
|
|
if (!ctx) return -1; |
|
|
|
if (!ctx) return -1; |
|
|
|
ctx->inst = inst; ctx->ua = ua; ctx->raw_sock = SOCKET_INVALID; |
|
|
|
ctx->inst = inst; ctx->ua = ua; ctx->raw_sock = SOCKET_INVALID; |
|
|
|
@ -334,15 +354,19 @@ int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) { |
|
|
|
ctx->is_exit = inst->tcp_proxy_server.enabled; |
|
|
|
ctx->is_exit = inst->tcp_proxy_server.enabled; |
|
|
|
ctx->test_loopback = 0; |
|
|
|
ctx->test_loopback = 0; |
|
|
|
ctx->expire_timer = NULL; |
|
|
|
ctx->expire_timer = NULL; |
|
|
|
g_icmp_ctx = ctx; |
|
|
|
if (RAND_bytes((unsigned char*)&ctx->next_token, sizeof(ctx->next_token)) != 1) { |
|
|
|
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: token initialization failed"); u_free(ctx); return -1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
inst->icmp_proxy = ctx; |
|
|
|
|
|
|
|
|
|
|
|
if (ctx->is_exit) { |
|
|
|
if (ctx->is_exit) { |
|
|
|
ctx->raw_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); |
|
|
|
ctx->raw_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); |
|
|
|
if (ctx->raw_sock == SOCKET_INVALID) |
|
|
|
if (ctx->raw_sock == SOCKET_INVALID) |
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: raw socket(SOCK_RAW) failed: %s", strerror(errno)); |
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: raw socket(SOCK_RAW) failed: %s", strerror(errno)); |
|
|
|
else { |
|
|
|
else { |
|
|
|
|
|
|
|
socket_set_nonblocking(ctx->raw_sock); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: raw socket created fd=%d", ctx->raw_sock); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: raw socket created fd=%d", ctx->raw_sock); |
|
|
|
ctx->raw_read_id = uasync_add_socket_t(ua, ctx->raw_sock, raw_read_cb, NULL, NULL, "icmp_raw", NULL); |
|
|
|
ctx->raw_read_id = uasync_add_socket_t(ua, ctx->raw_sock, raw_read_cb, NULL, NULL, "icmp_raw", ctx); |
|
|
|
if (!ctx->raw_read_id) { socket_close_wrapper(ctx->raw_sock); ctx->raw_sock = SOCKET_INVALID; } |
|
|
|
if (!ctx->raw_read_id) { socket_close_wrapper(ctx->raw_sock); ctx->raw_sock = SOCKET_INVALID; } |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -354,15 +378,16 @@ int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void icmp_proxy_destroy(struct UTUN_INSTANCE* inst) { |
|
|
|
void icmp_proxy_destroy(struct UTUN_INSTANCE* inst) { |
|
|
|
if (!inst || !g_icmp_ctx) return; |
|
|
|
struct icmp_proxy_ctx* ctx = inst ? inst->icmp_proxy : NULL; |
|
|
|
|
|
|
|
if (!ctx) return; |
|
|
|
etcp_router_unbind(inst, ETCP_RT_ID_ICMP_PROXY); |
|
|
|
etcp_router_unbind(inst, ETCP_RT_ID_ICMP_PROXY); |
|
|
|
if (g_icmp_ctx->expire_timer) { uasync_cancel_timeout(g_icmp_ctx->ua, g_icmp_ctx->expire_timer); g_icmp_ctx->expire_timer = NULL; } |
|
|
|
if (ctx->expire_timer) { uasync_cancel_timeout(ctx->ua, ctx->expire_timer); ctx->expire_timer = NULL; } |
|
|
|
if (g_icmp_ctx->raw_sock != SOCKET_INVALID) { |
|
|
|
if (ctx->raw_sock != SOCKET_INVALID) { |
|
|
|
if (g_icmp_ctx->raw_read_id) { uasync_remove_socket_t(g_icmp_ctx->ua, g_icmp_ctx->raw_sock); g_icmp_ctx->raw_read_id = NULL; } |
|
|
|
if (ctx->raw_read_id) { uasync_remove_socket_t(ctx->ua, ctx->raw_sock); ctx->raw_read_id = NULL; } |
|
|
|
socket_close_wrapper(g_icmp_ctx->raw_sock); |
|
|
|
socket_close_wrapper(ctx->raw_sock); |
|
|
|
} |
|
|
|
} |
|
|
|
struct icmp_request* r = g_icmp_ctx->pending; |
|
|
|
struct icmp_request* r = ctx->pending; |
|
|
|
while (r) { struct icmp_request* n = r->next; u_free(r); r = n; } |
|
|
|
while (r) { struct icmp_request* n = r->next; u_free(r); r = n; } |
|
|
|
u_free(g_icmp_ctx); g_icmp_ctx = NULL; |
|
|
|
u_free(ctx); inst->icmp_proxy = NULL; |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy destroyed"); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy destroyed"); |
|
|
|
} |
|
|
|
} |
|
|
|
|