diff --git a/src/routing_layer/topo_group.c b/src/routing_layer/topo_group.c index a3d93665..f9a679b2 100644 --- a/src/routing_layer/topo_group.c +++ b/src/routing_layer/topo_group.c @@ -1265,7 +1265,12 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from } int dyn = topo_node_dyn_size(ni); - if ((size_t)dyn + sizeof(struct TOPOMSG_NODEINFO_PKT) != len) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "Incorrect packet size (%s)", from->log_name); return -1; } + if (dyn < 0) return -1; // Причина уже записана в topo_node_dyn_size. + if ((size_t)dyn != len - sizeof(struct TOPOMSG_NODEINFO_PKT)) { + DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO size mismatch from=%s node=%016llx length=%zu expected=%zu", + from->log_name, (unsigned long long)ni->node_id, len, sizeof(struct TOPOMSG_NODEINFO_PKT) + (size_t)dyn); + return -1; + } uint64_t node_id = ni->node_id; if (ni->hop_count >= MAX_HOPS) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO from %s dropped: too many hops (%d)", from->log_name, ni->hop_count); return -1; } diff --git a/src/routing_layer/topo_node.c b/src/routing_layer/topo_node.c index f0233dc4..5f4b50a9 100644 --- a/src/routing_layer/topo_node.c +++ b/src/routing_layer/topo_node.c @@ -475,15 +475,20 @@ int topo_node_verify(const struct TOPO_NODE* ni) { /* Вычисляет суммарный размер динамической части NODEINFO по счётчикам заголовка. */ int topo_node_dyn_size(const struct TOPOMSG_NODE* msg) { + if (!msg) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO size: missing header"); return -1; } + if (!(msg->flags & TOPO_FLAG_SEND_SUBNETS) && (msg->local_v4_subnets || msg->local_v6_subnets)) { + DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO size: subnets without flag node=%016llx flags=%02x v4=%u v6=%u", + (unsigned long long)msg->node_id, msg->flags, msg->local_v4_subnets, msg->local_v6_subnets); + return -1; + } return msg->node_name_len + msg->local_v4_sockets * sizeof(struct TOPOMSG_SOCKMETA4) + msg->local_v4_addrs * sizeof(struct TOPOMSG_ADDR4) + msg->local_v6_sockets * sizeof(struct TOPOMSG_SOCKMETA6) + msg->local_v6_addrs * sizeof(struct TOPOMSG_ADDR6) + msg->local_reality_socks * sizeof(struct TOPOMSG_REALITY_SOCK) - + ((msg->flags & TOPO_FLAG_SEND_SUBNETS) - ? (msg->local_v4_subnets * sizeof(struct TOPOMSG_SUBNET4) + msg->local_v6_subnets * sizeof(struct TOPOMSG_SUBNET6)) - : 0) + + msg->local_v4_subnets * sizeof(struct TOPOMSG_SUBNET4) + + msg->local_v6_subnets * sizeof(struct TOPOMSG_SUBNET6) + msg->hop_count * 8; } @@ -491,7 +496,7 @@ int topo_node_dyn_size(const struct TOPOMSG_NODE* msg) { int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* nq, uint64_t group_id, uint8_t flags, uint8_t* out, size_t out_max, uint16_t cumulative_rtt) { - if (!ni || !nq || !out) return -1; + if (!ni || !nq || !out) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO encode: missing node/group/output"); return -1; } struct TOPOMSG_NODE msg; memset(&msg, 0, sizeof(msg)); @@ -510,7 +515,8 @@ int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* nq, topo_list_count((struct _topo_head*)ni->v6_sock_meta) > UINT8_MAX || topo_list_count((struct _topo_head*)ni->v6_addrs) > UINT8_MAX || topo_list_count((struct _topo_head*)ni->reality_socks) > UINT8_MAX || - (nq->subnets && (topo_list_count((struct _topo_head*)nq->subnets->v4_subnets) > UINT8_MAX || + ((flags & TOPO_FLAG_SEND_SUBNETS) && nq->subnets && + (topo_list_count((struct _topo_head*)nq->subnets->v4_subnets) > UINT8_MAX || topo_list_count((struct _topo_head*)nq->subnets->v6_subnets) > UINT8_MAX))) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node exceeds wire format limits"); return -1; } @@ -519,18 +525,25 @@ int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* nq, msg.local_v6_sockets = topo_list_count((struct _topo_head*)ni->v6_sock_meta); msg.local_v6_addrs = topo_list_count((struct _topo_head*)ni->v6_addrs); msg.local_reality_socks = topo_list_count((struct _topo_head*)ni->reality_socks); - msg.local_v4_subnets = nq->subnets ? topo_list_count((struct _topo_head*)nq->subnets->v4_subnets) : 0; - msg.local_v6_subnets = nq->subnets ? topo_list_count((struct _topo_head*)nq->subnets->v6_subnets) : 0; - { uint8_t hc; uint64_t* hl = topo_node_best_hop_list(nq, &hc, NULL); - msg.hop_count = hc; } + if ((flags & TOPO_FLAG_SEND_SUBNETS) && nq->subnets) { + msg.local_v4_subnets = topo_list_count((struct _topo_head*)nq->subnets->v4_subnets); + msg.local_v6_subnets = topo_list_count((struct _topo_head*)nq->subnets->v6_subnets); + } + uint64_t *hop_list = topo_node_best_hop_list(nq, &msg.hop_count, NULL); msg.client_type = ni->client_type; msg.client_activity = ni->client_activity; msg.cumulative_rtt = cumulative_rtt; if (nq->radio) msg.flags |= TOPO_FLAG_RADIO; msg.flags |= nq->phone_status; - size_t total = TOPOMSG_NODE_HDR_SIZE + (size_t)topo_node_dyn_size(&msg); - if (total > out_max) return -1; + int dyn = topo_node_dyn_size(&msg); + if (dyn < 0) return -1; // Причина уже записана в topo_node_dyn_size. + size_t total = TOPOMSG_NODE_HDR_SIZE + (size_t)dyn; + if (total > out_max) { + DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO encode: output too small node=%016llx need=%zu capacity=%zu", + (unsigned long long)ni->node_id, total, out_max); + return -1; + } memcpy(out, &msg, TOPOMSG_NODE_HDR_SIZE); uint8_t* dp = out + TOPOMSG_NODE_HDR_SIZE; @@ -574,8 +587,9 @@ int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* nq, }} } } - { uint8_t hc; uint64_t* hl = topo_node_best_hop_list(nq, &hc, NULL); - if (hc && hl) { size_t sz = hc * 8; memcpy(dp, hl, sz); dp += sz; } } + if (msg.hop_count) { size_t size = msg.hop_count * sizeof(*hop_list); memcpy(dp, hop_list, size); dp += size; } + DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "NODEINFO encoded: node=%016llx bytes=%zu flags=%02x subnets=%u/%u hops=%u", + (unsigned long long)ni->node_id, total, msg.flags, msg.local_v4_subnets, msg.local_v6_subnets, msg.hop_count); return (int)(dp - out); } @@ -585,129 +599,190 @@ int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* nq, int topo_node_deserialize(struct TOPO_GROUP* group, const uint8_t* data, size_t len, struct TOPO_NODE** out_ni, struct TOPO_NODESUBNETS** out_subnets, uint64_t** out_hop_list, uint8_t* out_hop_count, uint16_t* out_cumulative_rtt) { - if (!group || !data || len < TOPOMSG_NODE_HDR_SIZE || !out_ni) return -1; - + if (out_ni) *out_ni = NULL; + if (out_subnets) *out_subnets = NULL; + if (out_hop_list) *out_hop_list = NULL; + if (out_hop_count) *out_hop_count = 0; + if (out_cumulative_rtt) *out_cumulative_rtt = 0; + if (!group || !group->instance || !group->instance->topo_groups || !data || !out_ni) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO decode: missing group/instance/registry/data/output"); + return -1; + } + DEBUG_TRACE(DEBUG_CATEGORY_BGP, "NODEINFO decode: length=%zu", len); + if (len < TOPOMSG_NODE_HDR_SIZE) { + DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO decode: short header length=%zu need=%zu", len, TOPOMSG_NODE_HDR_SIZE); + return -1; + } const struct TOPOMSG_NODE* msg = (const struct TOPOMSG_NODE*)data; int dyn = topo_node_dyn_size(msg); - if ((size_t)dyn + TOPOMSG_NODE_HDR_SIZE != len) return -1; + if (dyn < 0) return -1; // Причина уже записана в topo_node_dyn_size. + if ((size_t)dyn != len - TOPOMSG_NODE_HDR_SIZE) { + DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO decode: size mismatch node=%016llx length=%zu expected=%zu flags=%02x subnets=%u/%u hops=%u", + (unsigned long long)msg->node_id, len, TOPOMSG_NODE_HDR_SIZE + (size_t)dyn, + msg->flags, msg->local_v4_subnets, msg->local_v6_subnets, msg->hop_count); + return -1; + } - struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE)); - if (!ni) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "alloc TOPO_NODE failed"); return -1; } - ni->group_ref_count = 0; - ni->node_id = msg->node_id; - ni->ver = msg->ver; - ni->timestamp = msg->timestamp; + struct TOPO_GROUPS* groups = group->instance->topo_groups; + struct TOPO_NODE* ni = u_calloc(1, sizeof(*ni)); + struct TOPO_NODESUBNETS* subnets = NULL; + uint64_t* hops = NULL; + const uint8_t* dp = data + TOPOMSG_NODE_HDR_SIZE; + const char* section = "node"; + unsigned index = 0; + if (!ni) goto no_memory; + ni->node_id = msg->node_id; ni->ver = msg->ver; ni->timestamp = msg->timestamp; memcpy(ni->public_key, msg->public_key, SC_PUBKEY_SIZE); memcpy(ni->ed25519_public_key, msg->ed25519_public_key, SC_PUBKEY_SIZE); memcpy(ni->x25519_self_sig, msg->x25519_self_sig, 64); - ni->client_type = msg->client_type; - ni->client_activity = msg->client_activity; - - const uint8_t* dp = data + TOPOMSG_NODE_HDR_SIZE; + ni->client_type = msg->client_type; ni->client_activity = msg->client_activity; + section = "name"; if (msg->node_name_len) { ni->node_name = u_malloc(msg->node_name_len + 1); - if (ni->node_name) { memcpy(ni->node_name, dp, msg->node_name_len); ni->node_name[msg->node_name_len] = 0; } - dp += msg->node_name_len; + if (!ni->node_name) goto no_memory; + memcpy(ni->node_name, dp, msg->node_name_len); ni->node_name[msg->node_name_len] = 0; } + dp += msg->node_name_len; - { struct TOPO_SOCKMETA4* head = NULL; struct TOPO_SOCKMETA4* tail = NULL; - for (int i = 0; i < msg->local_v4_sockets; i++) { + /* Списки сразу принадлежат ni: общий выход ошибки видит и незавершённую секцию. */ + section = "v4 socket metadata"; + { struct TOPO_SOCKMETA4** tail = &ni->v4_sock_meta; + for (index = 0; index < msg->local_v4_sockets; index++) { const struct TOPOMSG_SOCKMETA4* w = (const struct TOPOMSG_SOCKMETA4*)dp; dp += sizeof(*w); - struct TOPO_SOCKMETA4* e = memory_pool_alloc(group->instance->topo_groups->v4_sock_meta_pool); - if (!e) { topo_node_free_raw(group->instance->topo_groups, ni); return -1; } - e->id = w->id; e->config_type = w->config_type; e->next = NULL; - if (sockmeta_wire_read((const uint8_t *)w, &e->address_profile)) { - memory_pool_free(group->instance->topo_groups->v4_sock_meta_pool, e); - ni->v4_sock_meta = head; topo_node_free_raw(group->instance->topo_groups, ni); return -1; - } - if (!head) head = tail = e; else { tail->next = e; tail = e; } + struct TOPO_SOCKMETA4* e = memory_pool_alloc(groups->v4_sock_meta_pool); + if (!e) goto no_memory; + *tail = e; tail = &e->next; + e->id = w->id; e->config_type = w->config_type; + if (sockmeta_wire_read((const uint8_t*)w, &e->address_profile)) goto invalid_profile; } - ni->v4_sock_meta = head; } - { struct TOPO_ADDR4* head = NULL; struct TOPO_ADDR4* tail = NULL; - for (int i = 0; i < msg->local_v4_addrs; i++) { + } + section = "v4 addresses"; + { struct TOPO_ADDR4** tail = &ni->v4_addrs; + for (index = 0; index < msg->local_v4_addrs; index++) { const struct TOPOMSG_ADDR4* w = (const struct TOPOMSG_ADDR4*)dp; dp += sizeof(*w); - struct TOPO_ADDR4* e = memory_pool_alloc(group->instance->topo_groups->v4_addr_pool); - if (!e) { topo_node_free_raw(group->instance->topo_groups, ni); return -1; } - memcpy(e->addr, w->addr, 4); e->port = w->port; e->type = w->type; e->socket_id = w->socket_id; e->protocol = w->protocol; e->next = NULL; - if (!head) head = tail = e; else { tail->next = e; tail = e; } + struct TOPO_ADDR4* e = memory_pool_alloc(groups->v4_addr_pool); + if (!e) goto no_memory; + *tail = e; tail = &e->next; + memcpy(e->addr, w->addr, 4); e->port = w->port; e->type = w->type; + e->socket_id = w->socket_id; e->protocol = w->protocol; } - ni->v4_addrs = head; } - { struct TOPO_SOCKMETA6* head = NULL; struct TOPO_SOCKMETA6* tail = NULL; - for (int i = 0; i < msg->local_v6_sockets; i++) { + } + section = "v6 socket metadata"; + { struct TOPO_SOCKMETA6** tail = &ni->v6_sock_meta; + for (index = 0; index < msg->local_v6_sockets; index++) { const struct TOPOMSG_SOCKMETA6* w = (const struct TOPOMSG_SOCKMETA6*)dp; dp += sizeof(*w); - struct TOPO_SOCKMETA6* e = memory_pool_alloc(group->instance->topo_groups->v6_sock_meta_pool); - if (!e) { topo_node_free_raw(group->instance->topo_groups, ni); return -1; } - e->id = w->id; e->config_type = w->config_type; e->next = NULL; - if (sockmeta_wire_read((const uint8_t *)w, &e->address_profile)) { - memory_pool_free(group->instance->topo_groups->v6_sock_meta_pool, e); - ni->v6_sock_meta = head; topo_node_free_raw(group->instance->topo_groups, ni); return -1; - } - if (!head) head = tail = e; else { tail->next = e; tail = e; } + struct TOPO_SOCKMETA6* e = memory_pool_alloc(groups->v6_sock_meta_pool); + if (!e) goto no_memory; + *tail = e; tail = &e->next; + e->id = w->id; e->config_type = w->config_type; + if (sockmeta_wire_read((const uint8_t*)w, &e->address_profile)) goto invalid_profile; } - ni->v6_sock_meta = head; } - { struct TOPO_ADDR6* head = NULL; struct TOPO_ADDR6* tail = NULL; - for (int i = 0; i < msg->local_v6_addrs; i++) { + } + section = "v6 addresses"; + { struct TOPO_ADDR6** tail = &ni->v6_addrs; + for (index = 0; index < msg->local_v6_addrs; index++) { const struct TOPOMSG_ADDR6* w = (const struct TOPOMSG_ADDR6*)dp; dp += sizeof(*w); - struct TOPO_ADDR6* e = memory_pool_alloc(group->instance->topo_groups->v6_addr_pool); - if (!e) { topo_node_free_raw(group->instance->topo_groups, ni); return -1; } - memcpy(e->addr, w->addr, 16); e->port = w->port; e->type = w->type; e->socket_id = w->socket_id; e->protocol = w->protocol; e->next = NULL; - if (!head) head = tail = e; else { tail->next = e; tail = e; } + struct TOPO_ADDR6* e = memory_pool_alloc(groups->v6_addr_pool); + if (!e) goto no_memory; + *tail = e; tail = &e->next; + memcpy(e->addr, w->addr, 16); e->port = w->port; e->type = w->type; + e->socket_id = w->socket_id; e->protocol = w->protocol; } - ni->v6_addrs = head; } - { struct TOPO_REALITY_SOCK* head = NULL; struct TOPO_REALITY_SOCK* tail = NULL; - for (int i = 0; i < msg->local_reality_socks; i++) { + } + section = "REALITY sockets"; + { struct TOPO_REALITY_SOCK** tail = &ni->reality_socks; + for (index = 0; index < msg->local_reality_socks; index++) { const struct TOPOMSG_REALITY_SOCK* w = (const struct TOPOMSG_REALITY_SOCK*)dp; dp += sizeof(*w); - struct TOPO_REALITY_SOCK* e = u_calloc(1, sizeof(struct TOPO_REALITY_SOCK)); - if (!e) { topo_node_free_raw(group->instance->topo_groups, ni); return -1; } + struct TOPO_REALITY_SOCK* e = u_calloc(1, sizeof(*e)); + if (!e) goto no_memory; + *tail = e; tail = &e->next; e->socket_id = w->socket_id; - memcpy(e->short_id, w->short_id, 8); - memcpy(e->server_pubkey, w->server_pubkey, 32); - memcpy(e->version, w->version, 3); - memcpy(e->server_name, w->server_name, TOPO_REALITY_SNAME_MAX); + memcpy(e->short_id, w->short_id, 8); memcpy(e->server_pubkey, w->server_pubkey, 32); + memcpy(e->version, w->version, 3); memcpy(e->server_name, w->server_name, TOPO_REALITY_SNAME_MAX); e->server_name[TOPO_REALITY_SNAME_MAX - 1] = 0; - e->next = NULL; - if (!head) head = tail = e; else { tail->next = e; tail = e; } } - ni->reality_socks = head; } + } - if (out_subnets && (msg->flags & TOPO_FLAG_SEND_SUBNETS) && (msg->local_v4_subnets || msg->local_v6_subnets)) { - struct TOPO_NODESUBNETS* r = u_calloc(1, sizeof(struct TOPO_NODESUBNETS)); - if (!r) { topo_node_free_raw(group->instance->topo_groups, ni); return -1; } - for (int i = 0; i < msg->local_v4_subnets; i++) { + section = "subnets"; index = 0; + if (out_subnets && (msg->local_v4_subnets || msg->local_v6_subnets)) { + subnets = u_calloc(1, sizeof(*subnets)); + if (!subnets) goto no_memory; + } + /* Курсор проходит все присутствующие секции; out_subnets управляет только выделением памяти. */ + section = "v4 subnets"; + { struct TOPO_SUBNET4** tail = subnets ? &subnets->v4_subnets : NULL; + for (index = 0; index < msg->local_v4_subnets; index++) { const struct TOPOMSG_SUBNET4* w = (const struct TOPOMSG_SUBNET4*)dp; dp += sizeof(*w); - struct TOPO_SUBNET4* e = memory_pool_alloc(group->instance->topo_groups->v4_subnet_pool); - if (!e) { u_free(r); topo_node_free_raw(group->instance->topo_groups, ni); return -1; } + if (w->prefix_length > 32) { + DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO decode: invalid IPv4 prefix node=%016llx index=%u prefix=%u", + (unsigned long long)msg->node_id, index, w->prefix_length); + goto fail; + } + if (!subnets) continue; + struct TOPO_SUBNET4* e = memory_pool_alloc(groups->v4_subnet_pool); + if (!e) goto no_memory; + *tail = e; tail = &e->next; memcpy(e->addr, w->addr, 4); e->prefix_length = w->prefix_length; - e->next = r->v4_subnets; r->v4_subnets = e; } - for (int i = 0; i < msg->local_v6_subnets; i++) { + } + section = "v6 subnets"; + { struct TOPO_SUBNET6** tail = subnets ? &subnets->v6_subnets : NULL; + for (index = 0; index < msg->local_v6_subnets; index++) { const struct TOPOMSG_SUBNET6* w = (const struct TOPOMSG_SUBNET6*)dp; dp += sizeof(*w); - struct TOPO_SUBNET6* e = memory_pool_alloc(group->instance->topo_groups->v6_subnet_pool); - if (!e) { u_free(r); topo_node_free_raw(group->instance->topo_groups, ni); return -1; } + if (w->prefix_length > 128) { + DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO decode: invalid IPv6 prefix node=%016llx index=%u prefix=%u", + (unsigned long long)msg->node_id, index, w->prefix_length); + goto fail; + } + if (!subnets) continue; + struct TOPO_SUBNET6* e = memory_pool_alloc(groups->v6_subnet_pool); + if (!e) goto no_memory; + *tail = e; tail = &e->next; memcpy(e->addr, w->addr, 16); e->prefix_length = w->prefix_length; - e->next = r->v6_subnets; r->v6_subnets = e; } - *out_subnets = r; - } else if (!(msg->flags & TOPO_FLAG_SEND_SUBNETS)) { - dp += msg->local_v4_subnets * sizeof(struct TOPOMSG_SUBNET4) - + msg->local_v6_subnets * sizeof(struct TOPOMSG_SUBNET6); - if (out_subnets) *out_subnets = NULL; - } else { - if (out_subnets) *out_subnets = NULL; } - if (out_hop_list && msg->hop_count > 0) { - size_t hsz = msg->hop_count * 8; - *out_hop_list = u_malloc(hsz); - if (!*out_hop_list) { topo_node_free_raw(group->instance->topo_groups, ni); return -1; } - memcpy(*out_hop_list, dp, hsz); dp += hsz; - *out_hop_count = msg->hop_count; + section = "hops"; index = 0; + size_t hop_bytes = msg->hop_count * sizeof(*hops); + if (out_hop_list && hop_bytes) { + hops = u_malloc(hop_bytes); + if (!hops) goto no_memory; + memcpy(hops, dp, hop_bytes); + } + dp += hop_bytes; + if ((size_t)(dp - data) != len) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO decode: cursor mismatch node=%016llx consumed=%zu length=%zu", + (unsigned long long)msg->node_id, (size_t)(dp - data), len); + goto fail; } - if (out_cumulative_rtt) *out_cumulative_rtt = msg->cumulative_rtt; *out_ni = ni; + if (out_subnets) *out_subnets = subnets; + if (out_hop_list) *out_hop_list = hops; + if (out_hop_count) *out_hop_count = msg->hop_count; + if (out_cumulative_rtt) *out_cumulative_rtt = msg->cumulative_rtt; + DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "NODEINFO decoded: node=%016llx bytes=%zu flags=%02x subnets=%u/%u hops=%u keep_subnets=%d keep_hops=%d", + (unsigned long long)msg->node_id, len, msg->flags, msg->local_v4_subnets, msg->local_v6_subnets, + msg->hop_count, out_subnets != NULL, out_hop_list != NULL); return 0; + +invalid_profile: + DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO decode: invalid profile node=%016llx section=%s index=%u", + (unsigned long long)msg->node_id, section, index); + goto fail; +no_memory: + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO decode: allocation failed node=%016llx section=%s index=%u offset=%zu", + (unsigned long long)msg->node_id, section, index, (size_t)(dp - data)); +fail: + topo_node_free_raw(groups, ni); + if (subnets) { + free_v4_sub_list(groups->v4_subnet_pool, subnets->v4_subnets); + free_v6_sub_list(groups->v6_subnet_pool, subnets->v6_subnets); + u_free(subnets); + } + u_free(hops); + return -1; } /* Ищет per-group узел TOPO_GROUP_NODE в группе по node_id (NULL, если нет). */ diff --git a/src/routing_layer/topo_node.h b/src/routing_layer/topo_node.h index 64cdb9a2..91c61557 100644 --- a/src/routing_layer/topo_node.h +++ b/src/routing_layer/topo_node.h @@ -248,13 +248,19 @@ void topo_nodeq_clear_paths(struct TOPO_GROUP_NODE* nq); * убирает из group->nodes и освобождает сам nq. Единственный корректный способ удаления узла. */ void topo_nodeq_remove_node(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* nq); -/* Размер переменной части NODEINFO по счётчикам заголовка. */ +/* Размер переменной части NODEINFO или -1 при NULL/счётчиках подсетей без TOPO_FLAG_SEND_SUBNETS. */ int topo_node_dyn_size(const struct TOPOMSG_NODE* msg); -/* Собрать NODEINFO в out; длина или -1. Входные структуры остаются у вызывающего. */ +/* Собрать NODEINFO в out[out_max]; длина или -1. Входные структуры остаются у вызывающего. + * Без TOPO_FLAG_SEND_SUBNETS обе секции подсетей отсутствуют, их счётчики равны 0. */ int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* nq, uint64_t group_id, uint8_t flags, uint8_t* out, size_t out_max, uint16_t cumulative_rtt); -/* Разобрать NODEINFO; 0/-1. Выходные объекты выделены отдельно и передаются вызывающему. */ +/* Разобрать ровно len байт NODEINFO; 0/-1. group->instance->topo_groups содержит пулы списков. + * out_ni обязателен, остальные выходы независимы и необязательны. Отсутствие выхода не меняет обход/проверки секций. + * Перед разбором все переданные выходы обнуляются, прежние объекты не освобождаются. + * При успехе node уничтожается topo_node_destroy, подсети возвращаются в пулы groups, hops освобождается u_free. + * Пустые секции дают NULL/0; при ошибке выходы остаются NULL/0, частичные объекты освобождены. + * Проверяет размер, профили и /0..32, /0..128; подпись и допустимость BGP-пути проверяет вызывающий. */ int topo_node_deserialize(struct TOPO_GROUP* group, const uint8_t* data, size_t len, struct TOPO_NODE** out_ni, struct TOPO_NODESUBNETS** out_subnets, uint64_t** out_hop_list, uint8_t* out_hop_count, uint16_t* out_cumulative_rtt);