Browse Source

diagnostics: explain and rate-limit rejected ETCP session data

proxy
evgeny 3 days ago
parent
commit
4503bdfa7e
  1. 1
      src/transport_layer/etcp.h
  2. 12
      src/transport_layer/etcp_session.c
  3. 22
      tests/test_etcp_session.c

1
src/transport_layer/etcp.h

@ -204,6 +204,7 @@ struct ETCP_CONN {
uint64_t session_candidate, session_challenge, session_started; // эпоха-кандидат, challenge, время начала (0.1 ms)
void* session_timer; // таймер согласования транспортной сессии
uint8_t session_required; // только подтверждение challenge может завершить локальный reset
uint64_t session_data_errors; // отвергнутые заголовки DATA; ограничивает повторяющиеся сообщения об ошибке
uint8_t tx_state; // 0 - n/a, 1 - data_wait (queues empty), 2 - link_wait (link busy)
uint8_t links_up; // 0 - канал не готов для передачи, 1 - канал готов для передачи (хотя бы один линк не down)
uint8_t reset_done; // 0 - рукопожатие не завершено (реинит разрешён), 1 - соединение стабильно (реинит заблокирован)

12
src/transport_layer/etcp_session.c

@ -112,7 +112,17 @@ int etcp_session_receive(struct ETCP_CONN* c, const uint8_t* data, size_t len) {
int etcp_session_accept_data(struct ETCP_CONN* c, const uint8_t* data, size_t len) {
if (len <= ETCP_SESSION_HEADER_SIZE || data[0] != ETCP_SESSION_DATA || data[ETCP_SESSION_HEADER_SIZE] > 1) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP,"[%s] invalid session data size=%zu",c->log_name,len); return 0;
c->session_data_errors++;
if (c->session_data_errors <= 3 || c->session_data_errors % 1024 == 0) {
int code = len ? data[0] : -1;
int section = len > ETCP_SESSION_HEADER_SIZE ? data[ETCP_SESSION_HEADER_SIZE] : -1;
const char* reason = len <= ETCP_SESSION_HEADER_SIZE ? "short header" :
(code != ETCP_SESSION_DATA ? "missing session envelope" : "invalid stream section");
DEBUG_WARN(DEBUG_CATEGORY_ETCP,
"[%s] invalid session data: %s size=%zu code=%d expected=%u section=%d count=%llu",
c->log_name, reason, len, code, ETCP_SESSION_DATA, section, (unsigned long long)c->session_data_errors);
}
return 0;
}
uint64_t peer = read_id(data+1), target = read_id(data+9);
if (peer != c->peer_reset_id || target != c->reset_id || c->reinit_pending || !c->initialized) {

22
tests/test_etcp_session.c

@ -110,6 +110,27 @@ static struct frame stream_packet(int data) {
CHECK(etcp_encrypt_send(p)>0); memory_pool_free(ep[0].inst.pkt_pool,p);
CHECK(tail == head+1); struct frame f=frames[head++ % 4096]; return f;
}
static void data_header_validation(void) {
setup(0);
uint8_t data[ETCP_SESSION_HEADER_SIZE + ETCP_ACK_BASE_SIZE] = {0};
struct ETCP_CONN* c = ep[1].c;
etcp_session_encode(data, ETCP_SESSION_DATA, c->peer_reset_id, c->reset_id);
data[ETCP_SESSION_HEADER_SIZE] = ETCP_SECTION_ACK;
CHECK(etcp_session_accept_data(c, data, sizeof(data)));
CHECK(!etcp_session_accept_data(c, data, 0));
CHECK(!etcp_session_accept_data(c, data, ETCP_SESSION_HEADER_SIZE));
data[0] = ETCP_SECTION_ACK;
CHECK(!etcp_session_accept_data(c, data, sizeof(data)));
data[0] = ETCP_SESSION_DATA;
data[ETCP_SESSION_HEADER_SIZE] = ETCP_SESSION_DATA;
for (unsigned i = 3; i < 1024; i++) CHECK(!etcp_session_accept_data(c, data, sizeof(data)));
CHECK(c->session_data_errors == 1024);
data[ETCP_SESSION_HEADER_SIZE] = ETCP_SECTION_ACK;
CHECK(etcp_session_accept_data(c, data, sizeof(data)));
CHECK(c->session_data_errors == 1024 && c->initialized && !c->reinit_pending);
teardown();
puts("PASS session DATA header validation and rejection counter");
}
static void timestamp_boundaries(void) {
setup(0);
struct ETCP_LINK* link = &ep[0].links[0];
@ -161,6 +182,7 @@ int main(void) {
const char* log=getenv("SESSION_TEST_LOG");
if (log) { debug_enable_file_output(log,1); debug_set_category_level(DEBUG_CATEGORY_ETCP,DEBUG_LEVEL_DEBUG); }
size_t baseline=u_get_allocated_count();
data_header_validation(); CHECK(u_get_allocated_count() == baseline);
timestamp_boundaries(); CHECK(u_get_allocated_count() == baseline);
receive_window_case(0); CHECK(u_get_allocated_count() == baseline);
receive_window_case(1); CHECK(u_get_allocated_count() == baseline);

Loading…
Cancel
Save