Measure owned endpoint mapping and inbound reachability separately
This commit is contained in:
1 parent
b54307a0df
commit
ff69552a61
51 files changed
+2620
-2483
No files matched your search
+151
-4
@@ -21,6 +21,11 @@
|
||||
#include "../../src/chat/invite_link.h"
|
||||
#include "../../src/stream/stream_priv.h"
|
||||
#include "../../src/stream/stream_member.h"
|
||||
#include "../../src/routing_layer/nat_detection.h"
|
||||
#include "../../src/routing_layer/route_ping.h"
|
||||
#include "../../src/routing_layer/topo_node_sqlite.h"
|
||||
#include "../../src/transport_layer/node_conn_direct.h"
|
||||
#include "../../src/transport_layer/etcp.h"
|
||||
#include "../../src/utun_instance.h"
|
||||
|
||||
#define LAB_STREAMS 4
|
||||
@@ -40,6 +45,9 @@ struct lab {
|
||||
struct lab_stream streams[LAB_STREAMS];
|
||||
struct chat_invite_build *invite;
|
||||
uint64_t invite_id, group;
|
||||
struct NODE_CONN_DIRECT *nat_handles[32];
|
||||
uint64_t nat_records[32];
|
||||
unsigned nat_handle_count, nat_record_count;
|
||||
int errors;
|
||||
};
|
||||
static volatile sig_atomic_t stopping;
|
||||
@@ -168,7 +176,47 @@ static void statistics(struct lab *lab, uint64_t id, int loads) {
|
||||
stream_member_load(lab->inst, lab->group, node->node_id, &state, NULL));
|
||||
}
|
||||
}
|
||||
printf("},\"streams\":["); separator = 0;
|
||||
printf("},\"address_profiles\":["); separator = 0;
|
||||
for (struct ETCP_SOCKET *socket = lab->inst->etcp_sockets; socket; socket = socket->next) {
|
||||
if (socket->probe_only) continue;
|
||||
struct nat_check_status status = {0}; nat_detection_get_status(lab->inst->nat_det, socket, &status);
|
||||
printf("%s{\"socket\":", separator++ ? "," : ""); json_string(socket->name);
|
||||
printf(",\"socket_id\":%u,\"generation\":\"%" PRIu64 "\",\"protocol\":\"%s\",\"local\":",
|
||||
socket->sock_id, socket->generation, socket->is_tcp ? "tcp" : "udp");
|
||||
json_string(sockaddr_storage_to_str(socket->is_tcp ? &socket->interface_addr : &socket->local_addr).str);
|
||||
printf(",\"mapping\":\"%s\",\"inbound\":\"%s\",\"evidence\":\"%" PRIu64 "\",\"checked_at\":\"%" PRIu64 "\",\"external\":",
|
||||
address_mapping_name(socket->address_profile.mapping), address_inbound_name(socket->address_profile.inbound),
|
||||
socket->address_profile.evidence_id, socket->address_profile.checked_at);
|
||||
const struct sockaddr_storage *external = &socket->nat_addr;
|
||||
if (!external->ss_family && socket->address_profile.mapping == ADDRESS_MAP_UNCHANGED)
|
||||
external = socket->is_tcp ? &socket->interface_addr : &socket->local_addr;
|
||||
json_string(external->ss_family ? sockaddr_storage_to_str(external).str : "");
|
||||
printf(",\"phase\":%u,\"reason\":\"%s\",\"observer\":\"%" PRIu64 "\",\"helper\":\"%" PRIu64 "\","
|
||||
"\"rounds\":%u,\"helper_reselections\":%u,\"history_complete\":%u,\"contacts\":[", status.phase,
|
||||
nat_check_reason_name(status.reason), status.observer, status.helper, status.rounds,
|
||||
status.helper_reselections, status.history_complete);
|
||||
uint8_t local[ROUTE_ADDR_WIRE_SIZE]; sqlite3_stmt *contacts = NULL; int contact_separator = 0;
|
||||
if (!socket->is_tcp && !route_ping_addr_encode(local, &socket->local_addr) &&
|
||||
sqlite3_prepare_v2(lab->inst->topo_sqlite_db, "SELECT remote FROM nat_contacts WHERE local=?",
|
||||
-1, &contacts, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_blob(contacts, 1, local, sizeof(local), SQLITE_TRANSIENT);
|
||||
while (sqlite3_step(contacts) == SQLITE_ROW) {
|
||||
const uint8_t *address = sqlite3_column_blob(contacts, 0); char text[64];
|
||||
if (!address || sqlite3_column_bytes(contacts, 0) != 17 || (address[0] != 4 && address[0] != 6)) continue;
|
||||
if (!inet_ntop(address[0] == 4 ? AF_INET : AF_INET6, address + 1, text, sizeof(text))) continue;
|
||||
if (contact_separator++) printf(",");
|
||||
json_string(text);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(contacts); printf("]}");
|
||||
}
|
||||
printf("],\"direct_up\":["); separator = 0;
|
||||
for (struct ll_entry *entry = lab->inst->connections->head; entry; entry = entry->next) {
|
||||
const struct ETCP_CONN *connection = ((const struct conn_queue_entry *)entry->data)->conn;
|
||||
if (connection && connection->links_up && !connection->close_requested && connection->state != 2)
|
||||
printf("%s\"%" PRIu64 "\"", separator++ ? "," : "", connection->peer_node_id);
|
||||
}
|
||||
printf("],\"streams\":["); separator = 0;
|
||||
for (unsigned i = 0; i < LAB_STREAMS; i++) {
|
||||
struct lab_stream *stream = &lab->streams[i];
|
||||
if (!stream->source) continue;
|
||||
@@ -242,6 +290,100 @@ static void invite_ready(void *arg, int result, const char *link) {
|
||||
json_string(link ? link : ""); printf("}\n");
|
||||
}
|
||||
|
||||
static struct TOPO_NODE *parse_snapshot(struct lab *lab, const char *json) {
|
||||
char hex[4097]; uint8_t wire[2048]; size_t length;
|
||||
if (json_flat_get(json, "snapshot", hex, sizeof(hex)) || !(length = strlen(hex)) || length % 2 || length / 2 > sizeof(wire)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "lab: invalid node snapshot size"); return NULL;
|
||||
}
|
||||
for (size_t i = 0; i < length / 2; i++) {
|
||||
unsigned value;
|
||||
if (sscanf(hex + i * 2, "%2x", &value) != 1) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "lab: invalid snapshot hex"); return NULL; }
|
||||
wire[i] = (uint8_t)value;
|
||||
}
|
||||
struct TOPO_GROUP group = {.instance = lab->inst};
|
||||
struct TOPO_NODE *node = NULL;
|
||||
if (topo_node_deserialize(&group, wire, length / 2, &node, NULL, NULL, NULL, NULL) < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "lab: snapshot decode failed"); return NULL;
|
||||
}
|
||||
if (node->node_id == lab->inst->node_id || topo_node_verify(node) < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "lab: snapshot signature/identity rejected");
|
||||
topo_node_destroy(lab->inst->topo_groups, node); return NULL;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/* Стенд переносит подписанные снимки через RPC только до запуска группы.
|
||||
* NCD handshake, наблюдения и весь трафик всё равно проходят по настоящей сети. */
|
||||
static int nat_command(struct lab *lab, const char *json, const char *operation, uint64_t id) {
|
||||
if (!strcmp(operation, "chat_start")) {
|
||||
int result = chat_service_start(lab->inst);
|
||||
if (!result) stream_set_event_callback(lab->inst, event, lab);
|
||||
reply(id, result); return 1;
|
||||
}
|
||||
if (!strcmp(operation, "nat_release")) {
|
||||
for (unsigned i = 0; i < lab->nat_handle_count; i++) node_conn_direct_close(lab->nat_handles[i]);
|
||||
for (unsigned i = 0; i < lab->nat_record_count; i++) topo_node_registry_unref(lab->inst->topo_groups, lab->nat_records[i]);
|
||||
lab->nat_handle_count = lab->nat_record_count = 0; reply(id, 0); return 1;
|
||||
}
|
||||
if (!strcmp(operation, "nat_auto")) {
|
||||
uint64_t enabled;
|
||||
if (number(json, "enabled", &enabled) || enabled > 1) { reply(id, -1); return 1; }
|
||||
nat_detection_set_automatic(lab->inst->nat_det, enabled); reply(id, 0); return 1;
|
||||
}
|
||||
if (!strcmp(operation, "nat_check")) {
|
||||
char name[32]; uint64_t observer;
|
||||
if (!json_flat_get(json, "socket", name, sizeof(name)) && !number(json, "observer", &observer)) {
|
||||
struct ETCP_SOCKET *socket = lab->inst->etcp_sockets;
|
||||
while (socket && (socket->probe_only || strcmp(socket->name, name))) socket = socket->next;
|
||||
reply(id, socket ? nat_detection_request_check_socket(lab->inst->nat_det, socket, observer) : -1);
|
||||
} else { nat_detection_request_check_all(lab->inst->nat_det); reply(id, 0); }
|
||||
return 1;
|
||||
}
|
||||
if (!strcmp(operation, "snapshot")) {
|
||||
uint8_t wire[2048]; struct TOPO_GROUP_NODE nq = {0};
|
||||
struct TOPO_NODE *node = topo_node_registry_find(lab->inst->topo_groups, lab->inst->node_id);
|
||||
int length = topo_node_serialize(node, &nq, 0, 0, wire, sizeof(wire), 0);
|
||||
if (length < 0) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "lab: snapshot serialize failed"); reply(id, -1); return 1; }
|
||||
printf("{\"id\":%" PRIu64 ",\"rc\":0,\"snapshot\":\"", id);
|
||||
for (int i = 0; i < length; i++) printf("%02x", wire[i]);
|
||||
printf("\"}\n"); return 1;
|
||||
}
|
||||
int store = !strcmp(operation, "nat_store"), link = !strcmp(operation, "nat_link");
|
||||
if (!store && !link) return 0;
|
||||
struct TOPO_NODE *node = parse_snapshot(lab, json);
|
||||
if (!node) { reply(id, -1); return 1; }
|
||||
int result = 0;
|
||||
if (link) {
|
||||
char source[32]; struct ETCP_SOCKET *socket = NULL;
|
||||
if (lab->nat_handle_count < 32 && !json_flat_get(json, "socket", source, sizeof(source)))
|
||||
for (socket = lab->inst->etcp_sockets; socket; socket = socket->next)
|
||||
if (!socket->probe_only && !strcmp(socket->name, source)) break;
|
||||
if (!socket) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "lab: native link source/handle unavailable"); result = -1; }
|
||||
else {
|
||||
struct NODE_CONN_DIRECT *handle = NULL;
|
||||
int opened = node_conn_direct_open_node(lab->inst, node->node_id, NULL, NULL, &handle, node, socket);
|
||||
if (opened == NCD_ERR || !handle) result = -1;
|
||||
else lab->nat_handles[lab->nat_handle_count++] = handle;
|
||||
}
|
||||
topo_node_destroy(lab->inst->topo_groups, node);
|
||||
} else {
|
||||
if (lab->nat_record_count == 32) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "lab: imported snapshot limit reached"); result = -1;
|
||||
topo_node_destroy(lab->inst->topo_groups, node);
|
||||
} else {
|
||||
struct TOPO_NODE *stored = topo_node_registry_store(lab->inst->topo_groups, node);
|
||||
if (!stored) { topo_node_destroy(lab->inst->topo_groups, node); result = -1; }
|
||||
else {
|
||||
unsigned i;
|
||||
for (i = 0; i < lab->nat_record_count; i++) if (lab->nat_records[i] == stored->node_id) break;
|
||||
if (i == lab->nat_record_count) lab->nat_records[lab->nat_record_count++] = stored->node_id;
|
||||
else topo_node_registry_unref(lab->inst->topo_groups, stored->node_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
reply(id, result); return 1;
|
||||
}
|
||||
|
||||
static void command(struct lab *lab, const char *json) {
|
||||
uint64_t id = 0, source = 0, value = 0;
|
||||
char operation[32];
|
||||
@@ -251,6 +393,7 @@ static void command(struct lab *lab, const char *json) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "lab: RPC id=%" PRIu64 " op=%s", id, operation);
|
||||
if (!strcmp(operation, "stats")) { number(json, "loads", &value); statistics(lab, id, value != 0); return; }
|
||||
if (!strcmp(operation, "quit")) { stopping = 1; reply(id, 0); return; }
|
||||
if (nat_command(lab, json, operation, id)) return;
|
||||
if (!strcmp(operation, "create")) {
|
||||
if (lab->group) { reply(id, -1); return; }
|
||||
chat_core_create_channel_auto(lab->inst, "stream-netns");
|
||||
@@ -354,7 +497,8 @@ int main(int argc, char **argv) {
|
||||
for (unsigned i = 0; i < 32; i++) printf("%02x", public_key[i]);
|
||||
printf("\",\"node\":\"%" PRIu64 "\"}\n", sc_derive_node_id_from_pubkey(public_key)); return 0;
|
||||
}
|
||||
if (argc != 3) { fprintf(stderr, "Usage: %s CONFIG LOG | --identity SEED\n", argv[0]); return 1; }
|
||||
int core_only = argc == 4 && !strcmp(argv[3], "--core-only");
|
||||
if (argc != 3 && !core_only) { fprintf(stderr, "Usage: %s CONFIG LOG [--core-only] | --identity SEED\n", argv[0]); return 1; }
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
signal(SIGTERM, stop_signal); signal(SIGINT, stop_signal); signal(SIGPIPE, SIG_IGN);
|
||||
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_enable_console(0); debug_enable_file_output(argv[2], 1);
|
||||
@@ -363,11 +507,12 @@ int main(int argc, char **argv) {
|
||||
struct lab *lab = u_calloc(1, sizeof(*lab));
|
||||
if (!async || !lab) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "lab: initialization allocation failed"); return 1; }
|
||||
lab->inst = utun_instance_create(async, argv[1]);
|
||||
if (!lab->inst || utun_core_start(lab->inst) || chat_service_start(lab->inst)) {
|
||||
if (!lab->inst || utun_core_start(lab->inst) || (!core_only && chat_service_start(lab->inst))) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "lab: service startup failed"); return 1;
|
||||
}
|
||||
if (core_only) nat_detection_set_automatic(lab->inst->nat_det, 0);
|
||||
debug_enable_console(0);
|
||||
stream_set_event_callback(lab->inst, event, lab);
|
||||
if (!core_only) stream_set_event_callback(lab->inst, event, lab);
|
||||
if (fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK) < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "lab: stdin nonblocking failed: %s", strerror(errno)); return 1;
|
||||
}
|
||||
@@ -397,6 +542,8 @@ int main(int argc, char **argv) {
|
||||
if (lab->streams[i].errors) lab->errors++;
|
||||
}
|
||||
int errors = lab->errors;
|
||||
for (unsigned i = 0; i < lab->nat_handle_count; i++) node_conn_direct_force_close(lab->nat_handles[i]);
|
||||
for (unsigned i = 0; i < lab->nat_record_count; i++) topo_node_registry_unref(lab->inst->topo_groups, lab->nat_records[i]);
|
||||
utun_instance_destroy(lab->inst); uasync_destroy(async, 1); u_free(lab);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "lab: stopped errors=%d", errors);
|
||||
debug_disable_file_output(); return errors ? 1 : 0;
|
||||
|
||||
Reference in new issue
Block a user