|
|
|
|
@ -267,25 +267,27 @@ static int authorized(struct stream_network *net, uint64_t peer, const struct st
|
|
|
|
|
/* INFO и ACCEPT описывают реальный последний/начальный блок с его историей, а не BGP-метрику. */ |
|
|
|
|
static int info_payload(struct stream_local *local, uint8_t sub, uint64_t seq, uint64_t peer, uint64_t self, uint8_t *payload) { |
|
|
|
|
const uint8_t *wire; size_t length; uint64_t actual, first, last; struct stream_path path; |
|
|
|
|
if (stream_buffer_read(local->buffer, seq, sub, &wire, &length, &actual) != STREAM_OK |
|
|
|
|
int result = stream_buffer_read(local->buffer, seq, sub, &wire, &length, &actual); |
|
|
|
|
if (result == STREAM_EXPIRED) return STREAM_REASON_EXPIRED; |
|
|
|
|
if (result != STREAM_OK |
|
|
|
|
|| stream_buffer_range(local->buffer, sub, &first, &last) != STREAM_OK |
|
|
|
|
|| stream_buffer_path(local->buffer, actual, sub, &path) != STREAM_OK) return STREAM_REASON_POSITION; |
|
|
|
|
if (stream_path_validate(&path, local->descriptor.source_node_id, self, peer) != STREAM_OK || path.count == STREAM_PATH_MAX) |
|
|
|
|
return STREAM_REASON_LOOP; |
|
|
|
|
stream_write_be(payload, actual, 8); stream_write_be(payload + 8, last, 8); |
|
|
|
|
payload[16] = local->descriptor.n; payload[17] = local->descriptor.r; |
|
|
|
|
stream_write_be(payload + 18, local->descriptor.block_size, 2); |
|
|
|
|
/* byte 20 — число узлов, следует непосредственно за параметрами. */ |
|
|
|
|
stream_path_write(payload + 20, &path); return 0; |
|
|
|
|
stream_write_be(payload, actual, 8); stream_write_be(payload + 8, first, 8); stream_write_be(payload + 16, last, 8); |
|
|
|
|
payload[24] = local->descriptor.n; payload[25] = local->descriptor.r; |
|
|
|
|
stream_write_be(payload + 26, local->descriptor.block_size, 2); |
|
|
|
|
stream_path_write(payload + 28, &path); return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int parse_info(const struct stream_message *m, const struct stream_descriptor *d, uint64_t peer, uint64_t self, uint64_t *start) { |
|
|
|
|
struct stream_path path; size_t used; |
|
|
|
|
if (m->length < STREAM_INFO_SIZE || m->payload[16] != d->n || m->payload[17] != d->r |
|
|
|
|
|| stream_read_be(m->payload + 18, 2) != d->block_size |
|
|
|
|
|| stream_path_parse(m->payload + 20, m->length - 20, &path, &used) != STREAM_OK || m->length != 20 + used |
|
|
|
|
if (m->length < STREAM_INFO_SIZE || m->payload[24] != d->n || m->payload[25] != d->r |
|
|
|
|
|| stream_read_be(m->payload + 26, 2) != d->block_size |
|
|
|
|
|| stream_path_parse(m->payload + 28, m->length - 28, &path, &used) != STREAM_OK || m->length != 28 + used |
|
|
|
|
|| path.count == STREAM_PATH_MAX || stream_path_validate(&path, d->source_node_id, peer, self) != STREAM_OK |
|
|
|
|
|| stream_read_be(m->payload, 8) == UINT64_MAX || stream_read_be(m->payload, 8) > stream_read_be(m->payload + 8, 8)) { |
|
|
|
|
|| stream_read_be(m->payload, 8) == UINT64_MAX || stream_read_be(m->payload, 8) > stream_read_be(m->payload + 16, 8) |
|
|
|
|
|| stream_read_be(m->payload + 8, 8) > stream_read_be(m->payload + 16, 8)) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "stream INFO/ACCEPT invalid command=%u bytes=%zu", m->command, m->length); |
|
|
|
|
return STREAM_ERR_FORMAT; |
|
|
|
|
} |
|
|
|
|
@ -316,7 +318,8 @@ static int downstream_send(struct STREAM_RECEIVE *f) {
|
|
|
|
|
if (stream_local_get(p->network->inst, &f->descriptor, &local) != STREAM_OK) { flow_close(f, STREAM_REASON_STOPPED, 1); return 0; } |
|
|
|
|
struct stream_message m = {.descriptor = f->descriptor, .handle = f->info.handle, .subblock = f->subblock}; |
|
|
|
|
const uint8_t *wire; size_t length; uint64_t actual; |
|
|
|
|
int result = stream_buffer_read(local.buffer, f->info.next, f->subblock, &wire, &length, &actual); |
|
|
|
|
int result = f->info.next == UINT64_MAX ? STREAM_WAIT |
|
|
|
|
: stream_buffer_read(local.buffer, f->info.next, f->subblock, &wire, &length, &actual); |
|
|
|
|
if (result == STREAM_OK) { |
|
|
|
|
struct stream_path path; |
|
|
|
|
if (stream_buffer_path(local.buffer, actual, f->subblock, &path) != STREAM_OK |
|
|
|
|
@ -332,7 +335,11 @@ static int downstream_send(struct STREAM_RECEIVE *f) {
|
|
|
|
|
} |
|
|
|
|
if (result == STREAM_EXPIRED || result == STREAM_GAP) { |
|
|
|
|
uint64_t next; |
|
|
|
|
if (stream_buffer_next_seq(local.buffer, f->info.next, f->subblock, &next) == STREAM_OK && next > f->info.next) { |
|
|
|
|
int available = stream_buffer_next_seq(local.buffer, f->info.next, f->subblock, &next) == STREAM_OK; |
|
|
|
|
if (!available && local.finished && local.last != UINT64_MAX && f->info.next <= local.last) { |
|
|
|
|
next = local.last + 1; available = 1; |
|
|
|
|
} |
|
|
|
|
if (available && next > f->info.next) { |
|
|
|
|
uint8_t payload[16]; stream_write_be(payload, f->info.next, 8); stream_write_be(payload + 8, next, 8); |
|
|
|
|
m.command = STREAM_CMD_GAP; |
|
|
|
|
if (raw_send(p, m, payload, sizeof(payload)) == STREAM_OK) { |
|
|
|
|
@ -442,19 +449,22 @@ static void subscribe_request(struct stream_peer *p, struct stream_message m, st
|
|
|
|
|
} else if (f && !reason) { |
|
|
|
|
reason = info_payload(local, m.subblock, f->info.start, p->node_id, p->network->inst->node_id, payload); |
|
|
|
|
/* Идемпотентный ACCEPT возвращает первоначальный индекс даже если тот уже вытеснен. */ |
|
|
|
|
if (reason == STREAM_REASON_POSITION) { |
|
|
|
|
if (reason == STREAM_REASON_POSITION || reason == STREAM_REASON_EXPIRED) { |
|
|
|
|
reason = info_payload(local, m.subblock, UINT64_MAX, p->node_id, p->network->inst->node_id, payload); |
|
|
|
|
if (!reason) stream_write_be(payload, f->info.start, 8); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (reason) { |
|
|
|
|
uint8_t value = (uint8_t)reason; m.command = STREAM_CMD_REJECT; control_put(p, m, &value, 1); |
|
|
|
|
uint8_t rejection[17] = {(uint8_t)reason}; uint64_t first = UINT64_MAX, last = UINT64_MAX; |
|
|
|
|
if (m.subblock < local->descriptor.n) stream_buffer_range(local->buffer, m.subblock, &first, &last); |
|
|
|
|
stream_write_be(rejection + 1, first, 8); stream_write_be(rejection + 9, last, 8); |
|
|
|
|
m.command = STREAM_CMD_REJECT; control_put(p, m, rejection, sizeof(rejection)); |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "stream SUBSCRIBE rejected peer=%llx handle=%llu sub=%u reason=%d watermark=%llu", |
|
|
|
|
(unsigned long long)p->node_id, (unsigned long long)m.handle, m.subblock, reason, (unsigned long long)p->highwater); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
f->deadline = get_time_tb() + STREAM_LEASE_TB; |
|
|
|
|
m.command = STREAM_CMD_ACCEPT; control_put(p, m, payload, STREAM_INFO_SIZE + payload[20] * 8); |
|
|
|
|
m.command = STREAM_CMD_ACCEPT; control_put(p, m, payload, STREAM_INFO_SIZE + payload[28] * 8); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "stream ACCEPT queued peer=%llx handle=%llu sub=%u start=%llu lease=6000ms", |
|
|
|
|
(unsigned long long)p->node_id, (unsigned long long)m.handle, m.subblock, (unsigned long long)f->info.start); |
|
|
|
|
} |
|
|
|
|
@ -490,10 +500,10 @@ static void dispatch(struct stream_network *net, uint64_t sender, const uint8_t
|
|
|
|
|
int reason = info_payload(&local, m.subblock, UINT64_MAX, sender, net->inst->node_id, payload); |
|
|
|
|
m.command = reason ? STREAM_CMD_REJECT : STREAM_CMD_INFO_RESP; |
|
|
|
|
if (reason) { uint8_t value = (uint8_t)reason; control_put(p, m, &value, 1); } |
|
|
|
|
else control_put(p, m, payload, STREAM_INFO_SIZE + payload[20] * 8); |
|
|
|
|
else control_put(p, m, payload, STREAM_INFO_SIZE + payload[28] * 8); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
if (!p || !p->up || m.receiver_epoch != p->own_epoch |
|
|
|
|
if (!p || (!p->up && !routed) || m.receiver_epoch != p->own_epoch |
|
|
|
|
|| (p->other_epoch && m.sender_epoch != p->other_epoch)) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_STREAM, "stream stale/session packet peer=%llx command=%u handle=%llu", |
|
|
|
|
(unsigned long long)sender, m.command, (unsigned long long)m.handle); return; |
|
|
|
|
@ -546,7 +556,8 @@ static void dispatch(struct stream_network *net, uint64_t sender, const uint8_t
|
|
|
|
|
else flow_close(f, STREAM_REASON_PROTOCOL, 1); |
|
|
|
|
} else if (m.command == STREAM_CMD_RENEW && f && !m.length) f->deadline = get_time_tb() + STREAM_LEASE_TB; |
|
|
|
|
else if (m.command == STREAM_CMD_UNSUBSCRIBE && f && m.length == 1) flow_close(f, m.payload[0], 1); |
|
|
|
|
else if ((m.command == STREAM_CMD_CLOSED || m.command == STREAM_CMD_REJECT) && f && m.length == 1) flow_close(f, m.payload[0], 0); |
|
|
|
|
else if ((m.command == STREAM_CMD_CLOSED || m.command == STREAM_CMD_REJECT) && f && (m.length == 1 || m.length == 17)) |
|
|
|
|
flow_close(f, m.payload[0], 0); |
|
|
|
|
else DEBUG_DEBUG(DEBUG_CATEGORY_STREAM, "stream unknown/inactive issuance peer=%llx command=%u handle=%llu sub=%u bytes=%zu", |
|
|
|
|
(unsigned long long)sender, m.command, (unsigned long long)m.handle, m.subblock, m.length); |
|
|
|
|
p->busy--; peer_sweep(p); |
|
|
|
|
|