From 435204141637a8767a1cb452ee36311952156f99 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Sat, 7 Mar 2026 15:28:12 +0300 Subject: [PATCH] 1 --- src/etcp.c | 883 +++++++++++++++++++----------------- src/etcp.h | 5 + src/pkt_normalizer.c | 9 +- tests/bench_timeout_heap | Bin 195920 -> 196216 bytes tests/bench_uasync_timeouts | Bin 198704 -> 198992 bytes tools/bping/bping.c | 46 +- 6 files changed, 511 insertions(+), 432 deletions(-) diff --git a/src/etcp.c b/src/etcp.c index 30d842b..6e6d46f 100644 --- a/src/etcp.c +++ b/src/etcp.c @@ -1,5 +1,5 @@ -// etcp.c - ETCP Protocol Implementation (refactored and expanded based on etcp_protocol.txt) - +// etcp.c - ETCP Protocol Implementation (refactored and expanded based on etcp_protocol.txt) + #include "etcp.h" #include "etcp_debug.h" #include "etcp_loadbalancer.h" @@ -9,30 +9,30 @@ #include "../lib/ll_queue.h" #include "../lib/debug_config.h" #include "crc32.h" // For potential hashing, though not used yet. -#include -#include -#include -#include // For bandwidth calcs -#include // For UINT16_MAX -#include "../lib/mem.h" - -// Enable comprehensive debug output for ETCP module -#define DEBUG_CATEGORY_ETCP_DETAILED 1 - -// Constants from spec (adjusted for completeness) -#define MAX_INFLIGHT_BYTES 65536 // Initial window -#define RETRANS_K1 2.0f // RTT multiplier for retrans timeout -#define RETRANS_K2 1.5f // Jitter multiplier -#define ACK_DELAY_TB 20 // ACK timer delay (2ms in 0.1ms units) -#define BURST_DELAY_FACTOR 4 // Delay before burst -#define BURST_SIZE 5 // Packets in burst (1 delayed + 4 burst) -#define RTT_HISTORY_SIZE 10 // For jitter calc -#define MAX_PENDING 32 // For ACKs/retrans (arbitrary; adjust) -#define SECTION_HEADER_SIZE 3 // type(1) + len(2) - -// Container-of macro for getting struct from data pointer -//#define CONTAINER_OF(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member))) - +#include +#include +#include +#include // For bandwidth calcs +#include // For UINT16_MAX +#include "../lib/mem.h" + +// Enable comprehensive debug output for ETCP module +#define DEBUG_CATEGORY_ETCP_DETAILED 1 + +// Constants from spec (adjusted for completeness) +#define MAX_INFLIGHT_BYTES 65536 // Initial window +#define RETRANS_K1 2.0f // RTT multiplier for retrans timeout +#define RETRANS_K2 1.5f // Jitter multiplier +#define ACK_DELAY_TB 20 // ACK timer delay (2ms in 0.1ms units) +#define BURST_DELAY_FACTOR 4 // Delay before burst +#define BURST_SIZE 5 // Packets in burst (1 delayed + 4 burst) +#define RTT_HISTORY_SIZE 10 // For jitter calc +#define MAX_PENDING 32 // For ACKs/retrans (arbitrary; adjust) +#define SECTION_HEADER_SIZE 3 // type(1) + len(2) + +// Container-of macro for getting struct from data pointer +//#define CONTAINER_OF(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member))) + // Forward declarations static void input_queue_cb(struct ll_queue* q, void* arg); static void etcp_link_ready_callback(struct ETCP_CONN* etcp); @@ -91,75 +91,75 @@ static void drain_and_free_inflight_queue(struct ETCP_CONN* etcp, struct ll_queu queue_free(*q); *q = NULL; } - + uint16_t get_current_timestamp() { - return (uint16_t)get_time_tb(); -} - + return (uint16_t)get_time_tb(); +} + // Timestamp diff (with wrap-around) static uint16_t timestamp_diff(uint16_t t1, uint16_t t2) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); if (t1 >= t2) { - return t1 - t2; - } - return (UINT16_MAX - t2) + t1 + 1; -} - + return t1 - t2; + } + return (UINT16_MAX - t2) + t1 + 1; +} + // Create new ETCP connection struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); if (!instance) return NULL; - - - struct ETCP_CONN* etcp = u_calloc(1, sizeof(struct ETCP_CONN)); - if (!etcp) { - DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connection_create: creating connection failed for instance %p", instance); - return NULL; - } - - etcp->instance = instance; - etcp->input_queue = queue_new(instance->ua, 0, "ETCP input"); // No hash for input_queue - etcp->output_queue = queue_new(instance->ua, 0, "ETCP output"); // No hash for output_queue - etcp->input_send_q = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, "input_send_q"); // Hash for send_q - etcp->input_wait_ack = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, "input_wait_ack"); // Hash for wait_ack - etcp->recv_q = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, "recv_q"); // Hash for send_q - etcp->ack_q = queue_new(instance->ua, 0, "ack_q"); - etcp->inflight_pool = memory_pool_init(sizeof(struct INFLIGHT_PACKET)); - etcp->io_pool = memory_pool_init(sizeof(struct ETCP_FRAGMENT)); - etcp->optimal_inflight=10000; - // Initialize log_name with local node_id (peer will be updated later when known) - snprintf(etcp->log_name, sizeof(etcp->log_name), "%04llu->????", - (unsigned long long)(instance->node_id % 10000)); - - if (!etcp->input_queue || !etcp->output_queue || !etcp->input_send_q || !etcp->recv_q || !etcp->ack_q || - !etcp->input_wait_ack || !etcp->inflight_pool || !etcp->io_pool) { - DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connection_create: error - closing - input:%p output:%p send:%p wait:%x pool:%p", - etcp->input_queue, etcp->output_queue, etcp->input_send_q, etcp->input_wait_ack, etcp->inflight_pool); - etcp_connection_close(etcp); - return NULL; - } + + + struct ETCP_CONN* etcp = u_calloc(1, sizeof(struct ETCP_CONN)); + if (!etcp) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connection_create: creating connection failed for instance %p", instance); + return NULL; + } + + etcp->instance = instance; + etcp->input_queue = queue_new(instance->ua, 0, "ETCP input"); // No hash for input_queue + etcp->output_queue = queue_new(instance->ua, 0, "ETCP output"); // No hash for output_queue + etcp->input_send_q = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, "input_send_q"); // Hash for send_q + etcp->input_wait_ack = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, "input_wait_ack"); // Hash for wait_ack + etcp->recv_q = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, "recv_q"); // Hash for send_q + etcp->ack_q = queue_new(instance->ua, 0, "ack_q"); + etcp->inflight_pool = memory_pool_init(sizeof(struct INFLIGHT_PACKET)); + etcp->io_pool = memory_pool_init(sizeof(struct ETCP_FRAGMENT)); + etcp->optimal_inflight=10000; + // Initialize log_name with local node_id (peer will be updated later when known) + snprintf(etcp->log_name, sizeof(etcp->log_name), "%04llu->????", + (unsigned long long)(instance->node_id % 10000)); + + if (!etcp->input_queue || !etcp->output_queue || !etcp->input_send_q || !etcp->recv_q || !etcp->ack_q || + !etcp->input_wait_ack || !etcp->inflight_pool || !etcp->io_pool) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connection_create: error - closing - input:%p output:%p send:%p wait:%x pool:%p", + etcp->input_queue, etcp->output_queue, etcp->input_send_q, etcp->input_wait_ack, etcp->inflight_pool); + etcp_connection_close(etcp); + return NULL; + } // etcp->window_size = MAX_INFLIGHT_BYTES; // Not used etcp->mtu = 1500; // Default MTU etcp->next_tx_id = 1; - etcp->rtt_avg_10 = 10; // Initial guess (1ms) - etcp->rtt_avg_100 = 10; - etcp->rtt_history_idx = 0; - memset(etcp->rtt_history, 0, sizeof(etcp->rtt_history)); - - - etcp->normalizer = pn_init(etcp); - if (!etcp->normalizer) { - etcp_connection_close(etcp); - return NULL; - } - - // Set input queue callback - queue_set_callback(etcp->input_queue, input_queue_cb, etcp); - queue_set_callback(etcp->input_send_q, input_send_q_cb, etcp); - queue_set_callback(etcp->input_wait_ack, wait_ack_cb, etcp); - + etcp->rtt_avg_10 = 10; // Initial guess (1ms) + etcp->rtt_avg_100 = 10; + etcp->rtt_history_idx = 0; + memset(etcp->rtt_history, 0, sizeof(etcp->rtt_history)); + + + etcp->normalizer = pn_init(etcp); + if (!etcp->normalizer) { + etcp_connection_close(etcp); + return NULL; + } + + // Set input queue callback + queue_set_callback(etcp->input_queue, input_queue_cb, etcp); + queue_set_callback(etcp->input_send_q, input_send_q_cb, etcp); + queue_set_callback(etcp->input_wait_ack, wait_ack_cb, etcp); + etcp->link_ready_for_send_fn = etcp_link_ready_callback; // Register with routing module (must be done after normalizer is assigned) @@ -170,7 +170,7 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance) { return etcp; } - + // Close connection with NULL pointer safety (prevents double free) void etcp_connection_close(struct ETCP_CONN* etcp) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); @@ -215,36 +215,36 @@ void etcp_connection_close(struct ETCP_CONN* etcp) { queue_free(etcp->ack_q); etcp->ack_q = NULL; } - - // Free memory pools after all elements are returned - if (etcp->inflight_pool) { - memory_pool_destroy(etcp->inflight_pool); - etcp->inflight_pool = NULL; - } - - if (etcp->io_pool) { - memory_pool_destroy(etcp->io_pool); - etcp->io_pool = NULL; - } - - // Clear links list safely - if (etcp->links) { - struct ETCP_LINK* link = etcp->links; - while (link) { - struct ETCP_LINK* next = link->next; - etcp_link_close(link); - link = next; - } - etcp->links = NULL; - } - - // Clear next pointer to prevent dangling references - etcp->next = NULL; - - // TODO: Free rx_list, etc. - u_free(etcp); -} - + + // Free memory pools after all elements are returned + if (etcp->inflight_pool) { + memory_pool_destroy(etcp->inflight_pool); + etcp->inflight_pool = NULL; + } + + if (etcp->io_pool) { + memory_pool_destroy(etcp->io_pool); + etcp->io_pool = NULL; + } + + // Clear links list safely + if (etcp->links) { + struct ETCP_LINK* link = etcp->links; + while (link) { + struct ETCP_LINK* next = link->next; + etcp_link_close(link); + link = next; + } + etcp->links = NULL; + } + + // Clear next pointer to prevent dangling references + etcp->next = NULL; + + // TODO: Free rx_list, etc. + u_free(etcp); +} + // Reset connection void etcp_conn_reset(struct ETCP_CONN* etcp) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); @@ -255,6 +255,9 @@ void etcp_conn_reset(struct ETCP_CONN* etcp) { etcp->last_delivered_id = 0; etcp->rx_ack_till = 0; + // Устанавливаем флаг ожидания первого пакета + etcp->got_initial_pkt = 0; + // Reset metrics etcp->unacked_bytes = 0; etcp->rtt_last = 0; @@ -288,6 +291,23 @@ void etcp_conn_reset(struct ETCP_CONN* etcp) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "end"); } +void etcp_conn_reinit(struct ETCP_CONN* etcp) { + DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); + DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Reinitializing ETCP connection [%s]", etcp->log_name); + + // Сбрасываем initialized во всех линках + struct ETCP_LINK* link = etcp->links; + while (link) { + link->initialized = 0; + link = link->next; + } + + // Вызываем etcp_conn_reset для сброса состояния + etcp_conn_reset(etcp); + + DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "end"); +} + // Update log_name when peer_node_id becomes known void etcp_update_log_name(struct ETCP_CONN* etcp) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); @@ -300,42 +320,42 @@ void etcp_update_log_name(struct ETCP_CONN* etcp) { // ====================================================================== Отправка данных - + // Send data through ETCP connection // Allocates memory from data_pool and places in input queue // Returns: 0 on success, -1 on failure int etcp_int_send(struct ETCP_CONN* etcp, const void* data, uint16_t len) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); - + if (!etcp || !data || len == 0) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] invalid parameters (etcp=%p, data=%p, len=%zu)", etcp->log_name, etcp, data, len); return -1; } - + if (!etcp->input_queue) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] input_queue is NULL for etcp=%p", etcp->log_name, etcp); return -1; } - + // Check length against maximum packet size if (len > PACKET_DATA_SIZE) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] packet too large (len=%zu, max=%d)", etcp->log_name, len, PACKET_DATA_SIZE); return -1; } - + // Allocate packet data from data_pool (following ETCP reception pattern) uint8_t* packet_data = memory_pool_alloc(etcp->instance->data_pool); if (!packet_data) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] failed to allocate packet data from data_pool", etcp->log_name); return -1; } - - // Copy user data to packet buffer - memcpy(packet_data, data, len); - - // Create queue entry - this allocates ll_entry + data pointer - - + + // Copy user data to packet buffer + memcpy(packet_data, data, len); + + // Create queue entry - this allocates ll_entry + data pointer + + struct ETCP_FRAGMENT* pkt = (struct ETCP_FRAGMENT*)queue_entry_new_from_pool(etcp->io_pool); if (!pkt) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] failed to allocate queue entry", etcp->log_name); @@ -349,7 +369,7 @@ int etcp_int_send(struct ETCP_CONN* etcp, const void* data, uint16_t len) { pkt->ll.len = len; // размер packet_data DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] created PACKET %p with data %p (len=%zu)", etcp->log_name, pkt, packet_data, len); - + // Add to input queue - input_queue_cb will process it if (queue_data_put(etcp->input_queue, (struct ll_entry*)pkt, 0) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] failed to add to input queue", etcp->log_name); @@ -357,20 +377,20 @@ int etcp_int_send(struct ETCP_CONN* etcp, const void* data, uint16_t len) { memory_pool_free(etcp->io_pool, pkt); return -1; } - - return 0; -} - - - + + return 0; +} + + + static void input_queue_try_resume(struct ETCP_CONN* etcp) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); - - // если размер input_wait_ack+input_send_q в байтах < optimal_inflight то resume сейчас. - size_t wait_ack_bytes = queue_total_bytes(etcp->input_wait_ack); - size_t send_q_bytes = queue_total_bytes(etcp->input_send_q); - size_t total_bytes = wait_ack_bytes + send_q_bytes; - + + // если размер input_wait_ack+input_send_q в байтах < optimal_inflight то resume сейчас. + size_t wait_ack_bytes = queue_total_bytes(etcp->input_wait_ack); + size_t send_q_bytes = queue_total_bytes(etcp->input_send_q); + size_t total_bytes = wait_ack_bytes + send_q_bytes; + if (total_bytes <= etcp->optimal_inflight) { DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] resume callbacks: inflight_bytes=%d, input_len=%d", etcp->log_name, total_bytes, etcp->input_queue->total_bytes); queue_resume_callback(etcp->input_send_q);// вызвать лишний раз resume не страшно. @@ -378,7 +398,7 @@ static void input_queue_try_resume(struct ETCP_CONN* etcp) { if (queue_entry_count(etcp->input_send_q) == 0) queue_resume_callback(etcp->input_queue);// и только когда больше нечего отправлять - забираем новый пакет } } - + void etcp_stats(struct ETCP_CONN* etcp) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); if (!etcp) return; @@ -421,23 +441,23 @@ void etcp_stats(struct ETCP_CONN* etcp) { DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] last_delivered_id:%u", etcp->log_name, etcp->last_delivered_id); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] rx_ack_till: %u", etcp->log_name, etcp->rx_ack_till); } - + // Input callback for input_queue (добавление новых кодограмм в стек) // input_queue -> input_send_q static void input_queue_cb(struct ll_queue* q, void* arg) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg; struct ETCP_FRAGMENT* in_pkt = (struct ETCP_FRAGMENT*)queue_data_get(q); - + if (!in_pkt) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] cannot get element (pool=%p etcp=%p)", etcp->log_name, etcp->inflight_pool, etcp); queue_resume_callback(q); return; } - - - memory_pool_free(etcp->io_pool, in_pkt);// перемещаем из io_pool в inflight_pool - + + + memory_pool_free(etcp->io_pool, in_pkt);// перемещаем из io_pool в inflight_pool + // Create INFLIGHT_PACKET struct INFLIGHT_PACKET* p = memory_pool_alloc(etcp->inflight_pool); if (!p) { @@ -446,19 +466,19 @@ static void input_queue_cb(struct ll_queue* q, void* arg) { queue_resume_callback(q); return; } - - // Setup inflight packet (based on protocol.txt) - memset(p, 0, sizeof(*p)); - p->seq = etcp->next_tx_id++; // Assign seq - p->state = INFLIGHT_STATE_WAIT_SEND; - p->last_timestamp = 0; + + // Setup inflight packet (based on protocol.txt) + memset(p, 0, sizeof(*p)); + p->seq = etcp->next_tx_id++; // Assign seq + p->state = INFLIGHT_STATE_WAIT_SEND; + p->last_timestamp = 0; p->ll.dgram = in_pkt->ll.dgram; p->ll.dgram_pool = in_pkt->ll.dgram_pool; p->ll.len = in_pkt->ll.len; - - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] input -> inflight (seq=%u, len=%u)", etcp->log_name, p->seq, p->ll.len); - int len=p->ll.len;// сохраним len - + + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] input -> inflight (seq=%u, len=%u)", etcp->log_name, p->seq, p->ll.len); + int len=p->ll.len;// сохраним len + // Add to send queue if (queue_data_put(etcp->input_send_q, (struct ll_entry*)p, p->seq) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] failed to add packet seq=%u to input_send_q", etcp->log_name, p->seq); @@ -468,35 +488,35 @@ static void input_queue_cb(struct ll_queue* q, void* arg) { return; } etcp->unacked_bytes += len; - -// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "input_queue_cb: successfully moved from input_queue to input_send_q"); - - etcp_conn_process_send_queue(etcp);// сразу обработаем этот пакет -// input_queue_try_resume(etcp); - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] nextloop, input_queue size=%d ", etcp->log_name, q->count); -} - - + +// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "input_queue_cb: successfully moved from input_queue to input_send_q"); + + etcp_conn_process_send_queue(etcp);// сразу обработаем этот пакет +// input_queue_try_resume(etcp); + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] nextloop, input_queue size=%d ", etcp->log_name, q->count); +} + + static void input_send_q_cb(struct ll_queue* q, void* arg) {// etcp->input_send_q processing DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); struct ETCP_CONN* etcp=(struct ETCP_CONN*)arg; - -// size_t send_q_bytes = queue_total_bytes(etcp->input_send_q); -// size_t send_q_pkts = queue_entry_count(etcp->input_send_q); -// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "input_send_q_cb: input_send_q status: %d pkt %d bytes", send_q_pkts, send_q_bytes); - etcp_conn_process_send_queue(etcp); - } - - + +// size_t send_q_bytes = queue_total_bytes(etcp->input_send_q); +// size_t send_q_pkts = queue_entry_count(etcp->input_send_q); +// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "input_send_q_cb: input_send_q status: %d pkt %d bytes", send_q_pkts, send_q_bytes); + etcp_conn_process_send_queue(etcp); + } + + static void ack_timeout_check(void* arg) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg; uint64_t now = get_time_tb(); - uint64_t timeout = 1000;//(uint64_t)(etcp->rtt_avg_10 * RETRANS_K1) + (uint64_t)(etcp->jitter * RETRANS_K2); - -// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "ack_timeout_check: starting check, now=%llu, timeout=%llu, rtt_avg_10=%u, jitter=%u", -// (unsigned long long)now, (unsigned long long)timeout, etcp->rtt_avg_10, etcp->jitter); - + uint64_t timeout = 1000;//(uint64_t)(etcp->rtt_avg_10 * RETRANS_K1) + (uint64_t)(etcp->jitter * RETRANS_K2); + +// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "ack_timeout_check: starting check, now=%llu, timeout=%llu, rtt_avg_10=%u, jitter=%u", +// (unsigned long long)now, (unsigned long long)timeout, etcp->rtt_avg_10, etcp->jitter); + struct ll_entry* current; while (current = etcp->input_wait_ack->head) { struct INFLIGHT_PACKET* pkt = (struct INFLIGHT_PACKET*)current; @@ -521,42 +541,42 @@ static void ack_timeout_check(void* arg) { if (elapsed > timeout) { DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] ack_timeout_check: timeout for seq=%u, elapsed=%llu, timeout=%llu, send_count=%u. Moving: wait_ack -> send_q", etcp->log_name, pkt->seq, (unsigned long long)elapsed, (unsigned long long)timeout, pkt->send_count); - - // Increment counters - pkt->send_count++; - pkt->retrans_req_count++; // Optional, if used for retrans request logic - pkt->last_timestamp = now; - pkt->last_link = NULL; // Reset last link for re-selection - + + // Increment counters + pkt->send_count++; + pkt->retrans_req_count++; // Optional, if used for retrans request logic + pkt->last_timestamp = now; + pkt->last_link = NULL; // Reset last link for re-selection + // Remove from wait_ack queue_data_get(etcp->input_wait_ack); // Change state and add to send_q for retransmission pkt->state = INFLIGHT_STATE_WAIT_SEND; queue_data_put(etcp->input_send_q, (struct ll_entry*)pkt, pkt->seq); - - // Update stats - etcp->retransmissions_count++; - } - else {// не надо до конца сканировать - они уже сортированы по таймстемпу т.к. очередь fifo, а timestamp = время добавления в очередь = время отправки - // shedule timer - int64_t next_timeout=timeout - elapsed; - etcp->retrans_timer = uasync_set_timeout(etcp->instance->ua, next_timeout+10, etcp, ack_timeout_check); - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] ack_timeout_check: retransmission timer set for %llu units", etcp->log_name, next_timeout); - return; - } - } - // если всё выгребли - надо взвести resume - etcp->retrans_timer = NULL; - queue_resume_callback(etcp->input_wait_ack); -} - + + // Update stats + etcp->retransmissions_count++; + } + else {// не надо до конца сканировать - они уже сортированы по таймстемпу т.к. очередь fifo, а timestamp = время добавления в очередь = время отправки + // shedule timer + int64_t next_timeout=timeout - elapsed; + etcp->retrans_timer = uasync_set_timeout(etcp->instance->ua, next_timeout+10, etcp, ack_timeout_check); + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] ack_timeout_check: retransmission timer set for %llu units", etcp->log_name, next_timeout); + return; + } + } + // если всё выгребли - надо взвести resume + etcp->retrans_timer = NULL; + queue_resume_callback(etcp->input_wait_ack); +} + static void wait_ack_cb(struct ll_queue* q, void* arg) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg; ack_timeout_check(etcp);// ack_cb срабатывает когда init (таймер не инициализирован) или когда empty (таймер не активен) -} - +} + // Подготовить и отправить кодограмму // вызывается линком когда освобождается или очередью если появляются данные на передачу struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) { @@ -566,15 +586,15 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) { DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] no link available", etcp->log_name); return NULL;// если линков нет - ждём появления свободного } - - size_t send_q_size = queue_entry_count(etcp->input_send_q); - - if (send_q_size == 0) {// сгребаем из input_queue -// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: input_send_q empty, check if avail input_queue -> inflight"); - input_queue_try_resume(etcp); -// return NULL; - } - + + size_t send_q_size = queue_entry_count(etcp->input_send_q); + + if (send_q_size == 0) {// сгребаем из input_queue +// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: input_send_q empty, check if avail input_queue -> inflight"); + input_queue_try_resume(etcp); +// return NULL; + } + // First, check if there's a packet in input_send_q (retrans or new) // DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: getting packet from input_send_q"); struct INFLIGHT_PACKET* inf_pkt = (struct INFLIGHT_PACKET*)queue_data_get(etcp->input_send_q); @@ -586,39 +606,39 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) { inf_pkt->state=INFLIGHT_STATE_WAIT_ACK; queue_data_put(etcp->input_wait_ack, (struct ll_entry*)inf_pkt, inf_pkt->seq);// move dgram to wait_ack queue } - - size_t ack_q_size = queue_entry_count(etcp->ack_q); - + + size_t ack_q_size = queue_entry_count(etcp->ack_q); + if (!inf_pkt && ack_q_size == 0) { DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] no data/ack to send", etcp->log_name); return NULL; } - + struct ETCP_DGRAM* dgram = memory_pool_alloc(etcp->instance->pkt_pool); if (!dgram) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] failed to allocate ETCP_DGRAM", etcp->log_name); return NULL; } - - dgram->link = link; - dgram->noencrypt_len=0; - dgram->timestamp=get_current_timestamp(); - -// формат ack: [01] [elements count] [4 байта last_delivered_id] и <[4 байта seq][2 байта recv_ts][2 байта txrx delay ts]> x count - dgram->data[0]=1;// ack - int ptr=2; - - dgram->data[ptr++]=etcp->last_delivered_id; - dgram->data[ptr++]=etcp->last_delivered_id>>8; - dgram->data[ptr++]=etcp->last_delivered_id>>16; - dgram->data[ptr++]=etcp->last_delivered_id>>24; - - int data_len=0; - if (inf_pkt) data_len=inf_pkt->ll.len; - int remain_len=link->mtu -28/*udp headers*/ -13-8-4/*sc_nonce+tag size+crc*/ -9/*hdr[3] + min ack[6]*/ -5/*payload hdr*/ - data_len; -// DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "remain_len= %d pl=%d", remain_len, data_len); - -// добавим опциональные заголовки + + dgram->link = link; + dgram->noencrypt_len=0; + dgram->timestamp=get_current_timestamp(); + +// формат ack: [01] [elements count] [4 байта last_delivered_id] и <[4 байта seq][2 байта recv_ts][2 байта txrx delay ts]> x count + dgram->data[0]=1;// ack + int ptr=2; + + dgram->data[ptr++]=etcp->last_delivered_id; + dgram->data[ptr++]=etcp->last_delivered_id>>8; + dgram->data[ptr++]=etcp->last_delivered_id>>16; + dgram->data[ptr++]=etcp->last_delivered_id>>24; + + int data_len=0; + if (inf_pkt) data_len=inf_pkt->ll.len; + int remain_len=link->mtu -28/*udp headers*/ -13-8-4/*sc_nonce+tag size+crc*/ -9/*hdr[3] + min ack[6]*/ -5/*payload hdr*/ - data_len; +// DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "remain_len= %d pl=%d", remain_len, data_len); + +// добавим опциональные заголовки struct ACK_PACKET* ack_pkt; while (remain_len>=8) { ack_pkt = (struct ACK_PACKET*)queue_data_get(etcp->ack_q); @@ -640,98 +660,98 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) { dgram->data[ptr++]=dly>>8; DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] add ACK N%d dTS=%d", etcp->log_name, ack_pkt->seq, dly); queue_entry_free((struct ll_entry*)ack_pkt); - - if (inf_pkt && inf_pkt->ll.len+ptr>=etcp->mtu-10) break;// pkt len (надо просчитать точнее включая все заголовки) - if (ptr>500) break; - } - - dgram->data[1]=ptr/8; - - - if (link->last_recv_updated && remain_len>=5) {// если есть данные - добавим channel_timestamp - uint64_t now=get_time_tb(); - uint64_t dt=now - link->last_recv_local_time; - link->last_recv_updated=0; - if (dt<1000000) { - dgram->data[ptr++]=ETCP_SECTION_TIMESTAMP; - - uint16_t t=link->last_recv_timestamp + dt; - dgram->data[ptr++]=t; - dgram->data[ptr++]=t>>8; - - t=link->last_recv_local_time - link->last_recv_timestamp; - dgram->data[ptr++]=t; - dgram->data[ptr++]=t>>8; - remain_len-=5; - } - } - -// DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "remain_len(2)= %d", remain_len); - + + if (inf_pkt && inf_pkt->ll.len+ptr>=etcp->mtu-10) break;// pkt len (надо просчитать точнее включая все заголовки) + if (ptr>500) break; + } + + dgram->data[1]=ptr/8; + + + if (link->last_recv_updated && remain_len>=5) {// если есть данные - добавим channel_timestamp + uint64_t now=get_time_tb(); + uint64_t dt=now - link->last_recv_local_time; + link->last_recv_updated=0; + if (dt<1000000) { + dgram->data[ptr++]=ETCP_SECTION_TIMESTAMP; + + uint16_t t=link->last_recv_timestamp + dt; + dgram->data[ptr++]=t; + dgram->data[ptr++]=t>>8; + + t=link->last_recv_local_time - link->last_recv_timestamp; + dgram->data[ptr++]=t; + dgram->data[ptr++]=t>>8; + remain_len-=5; + } + } + +// DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "remain_len(2)= %d", remain_len); + if (inf_pkt) { // фрейм data (0) обязательно в конец DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] add DATA (seq=%u, len=%u), ack_size=%d", etcp->log_name, inf_pkt->seq, inf_pkt->ll.len, dgram->data[1]); - dgram->data[ptr++]=0;// payload - dgram->data[ptr++]=inf_pkt->seq; - dgram->data[ptr++]=inf_pkt->seq>>8; - dgram->data[ptr++]=inf_pkt->seq>>16; - dgram->data[ptr++]=inf_pkt->seq>>24; - - memcpy(&dgram->data[ptr], inf_pkt->ll.dgram, inf_pkt->ll.len); ptr+=inf_pkt->ll.len; - } + dgram->data[ptr++]=0;// payload + dgram->data[ptr++]=inf_pkt->seq; + dgram->data[ptr++]=inf_pkt->seq>>8; + dgram->data[ptr++]=inf_pkt->seq>>16; + dgram->data[ptr++]=inf_pkt->seq>>24; + + memcpy(&dgram->data[ptr], inf_pkt->ll.dgram, inf_pkt->ll.len); ptr+=inf_pkt->ll.len; + } else { int chk=queue_check_consistency(etcp->ack_q); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] only ACK (size=%d) packet with %d bytes total (chk=%d) rem=%d", etcp->log_name, ack_q_size, ptr, chk, remain_len); } if (ptr>=PACKET_DATA_SIZE-50) DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] SIZE ERROR!!! %d", ptr); - + dgram->data_len=ptr; - + etcp_dump_pkt_sections(dgram, link, 1); - + return dgram; -} - +} + // Callback for when a link is ready to send data static void etcp_link_ready_callback(struct ETCP_CONN* etcp) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); if (!etcp) return; -// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_link_ready_callback: processing send queue for etcp=%p", etcp); - etcp_conn_process_send_queue(etcp); -} - +// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_link_ready_callback: processing send queue for etcp=%p", etcp); + etcp_conn_process_send_queue(etcp); +} + // Process packets in send queue and transmit them static void etcp_conn_process_send_queue(struct ETCP_CONN* etcp) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); struct ETCP_DGRAM* dgram; - while(dgram = etcp_request_pkt(etcp)) { -// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_conn_process_send_queue: sending packet"); - etcp_loadbalancer_send(dgram); - } -} - + while(dgram = etcp_request_pkt(etcp)) { +// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_conn_process_send_queue: sending packet"); + etcp_loadbalancer_send(dgram); + } +} + static void ack_response_timer_cb(void* arg) {// проверяем неотправленные ack response и отправляем если надо. DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg; - etcp_conn_process_send_queue(etcp);// проталкиваем (она же должна отправлять только ack если больше ничего нет) -// если ack все еще заняты - обновляем таймаут - if (etcp->ack_q->count) etcp->ack_resp_timer = uasync_set_timeout(etcp->instance->ua, ACK_DELAY_TB, etcp, ack_response_timer_cb); - else etcp->ack_resp_timer=NULL; -} - -// ====================================================================== Прием данных - - + etcp_conn_process_send_queue(etcp);// проталкиваем (она же должна отправлять только ack если больше ничего нет) +// если ack все еще заняты - обновляем таймаут + if (etcp->ack_q->count) etcp->ack_resp_timer = uasync_set_timeout(etcp->instance->ua, ACK_DELAY_TB, etcp, ack_response_timer_cb); + else etcp->ack_resp_timer=NULL; +} + +// ====================================================================== Прием данных + + void etcp_output_try_assembly(struct ETCP_CONN* etcp) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); // пробуем собрать выходную очередь из фрагментов // DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] etcp=%p, last_delivered_id=%u, recv_q_count=%d", // etcp->log_name, etcp, etcp->last_delivered_id, queue_entry_count(etcp->recv_q)); - - uint32_t next_expected_id = etcp->last_delivered_id + 1; - int delivered_count = 0; - uint32_t delivered_bytes = 0; - + + uint32_t next_expected_id = etcp->last_delivered_id + 1; + int delivered_count = 0; + uint32_t delivered_bytes = 0; + // Look for contiguous packets starting from next_expected_id while (1) { struct ETCP_FRAGMENT* rx_pkt = (struct ETCP_FRAGMENT*)queue_find_data_by_id(etcp->recv_q, next_expected_id); @@ -761,107 +781,107 @@ void etcp_output_try_assembly(struct ETCP_CONN* etcp) { queue_data_put(etcp->recv_q, (struct ll_entry*)rx_pkt, next_expected_id); break; } - - // Update state for next iteration - etcp->last_delivered_id = next_expected_id; - next_expected_id++; - } - + + // Update state for next iteration + etcp->last_delivered_id = next_expected_id; + next_expected_id++; + } + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] delivered %u contiguous packets (%u bytes), last_delivered_id=%u, output_queue_count=%d", etcp->log_name, delivered_count, delivered_bytes, etcp->last_delivered_id, queue_entry_count(etcp->output_queue)); } - + // Process ACK receipt - remove acknowledged packet from inflight queues void etcp_ack_recv(struct ETCP_CONN* etcp, uint32_t seq, uint16_t ts, uint16_t dts) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); if (!etcp) return; - -// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] processing ACK for seq=%u, ts=%u, dts=%u", etcp->log_name, seq, ts, dts); - + +// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] processing ACK for seq=%u, ts=%u, dts=%u", etcp->log_name, seq, ts, dts); + // Find the acknowledged packet in the wait_ack queue struct INFLIGHT_PACKET* acked_pkt = (struct INFLIGHT_PACKET*)queue_find_data_by_id(etcp->input_wait_ack, seq); if (acked_pkt) queue_remove_data(etcp->input_wait_ack, (struct ll_entry*)acked_pkt); else { acked_pkt = (struct INFLIGHT_PACKET*)queue_find_data_by_id(etcp->input_send_q, seq); queue_remove_data(etcp->input_send_q, (struct ll_entry*)acked_pkt); } - - if (!acked_pkt) { - // Packet might be already acknowledged or not found - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_ack_recv: packet seq=%u not found in wait_ack queue", seq); - return; - } - - // Calculate RTT if timestamps are valid -/* if (ts != (uint16_t)-1 && dts != (uint16_t)-1) { - uint16_t rtt = timestamp_diff(ts, dts); - etcp->rtt_last = rtt; - - // Update RTT averages (exponential smoothing) - if (etcp->rtt_avg_10 == 0) { - etcp->rtt_avg_10 = rtt; - etcp->rtt_avg_100 = rtt; - } else { - // RTT average over 10 packets - etcp->rtt_avg_10 = (etcp->rtt_avg_10 * 9 + rtt) / 10; - // RTT average over 100 packets - etcp->rtt_avg_100 = (etcp->rtt_avg_100 * 99 + rtt) / 100; - } - - // Update jitter calculation (max - min of last 10 RTT samples) - etcp->rtt_history[etcp->rtt_history_idx] = rtt; - etcp->rtt_history_idx = (etcp->rtt_history_idx + 1) % 10; - - uint16_t rtt_min = UINT16_MAX, rtt_max = 0; - for (int i = 0; i < 10; i++) { - if (etcp->rtt_history[i] < rtt_min) rtt_min = etcp->rtt_history[i]; - if (etcp->rtt_history[i] > rtt_max) rtt_max = etcp->rtt_history[i]; - } - etcp->jitter = rtt_max - rtt_min; - + + if (!acked_pkt) { + // Packet might be already acknowledged or not found + DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_ack_recv: packet seq=%u not found in wait_ack queue", seq); + return; + } + + // Calculate RTT if timestamps are valid +/* if (ts != (uint16_t)-1 && dts != (uint16_t)-1) { + uint16_t rtt = timestamp_diff(ts, dts); + etcp->rtt_last = rtt; + + // Update RTT averages (exponential smoothing) + if (etcp->rtt_avg_10 == 0) { + etcp->rtt_avg_10 = rtt; + etcp->rtt_avg_100 = rtt; + } else { + // RTT average over 10 packets + etcp->rtt_avg_10 = (etcp->rtt_avg_10 * 9 + rtt) / 10; + // RTT average over 100 packets + etcp->rtt_avg_100 = (etcp->rtt_avg_100 * 99 + rtt) / 100; + } + + // Update jitter calculation (max - min of last 10 RTT samples) + etcp->rtt_history[etcp->rtt_history_idx] = rtt; + etcp->rtt_history_idx = (etcp->rtt_history_idx + 1) % 10; + + uint16_t rtt_min = UINT16_MAX, rtt_max = 0; + for (int i = 0; i < 10; i++) { + if (etcp->rtt_history[i] < rtt_min) rtt_min = etcp->rtt_history[i]; + if (etcp->rtt_history[i] > rtt_max) rtt_max = etcp->rtt_history[i]; + } + etcp->jitter = rtt_max - rtt_min; + // DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] RTT updated - last=%u, avg_10=%u, avg_100=%u, jitter=%u", // etcp->log_name, rtt, etcp->rtt_avg_10, etcp->rtt_avg_100, etcp->jitter); } -*/ - // Update connection statistics - etcp->unacked_bytes -= acked_pkt->ll.len; - etcp->bytes_sent_total += acked_pkt->ll.len; - etcp->ack_packets_count++; - - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] removed packet seq=%u from wait_ack, unacked_bytes now %u total acked=%u", etcp->log_name, seq, etcp->unacked_bytes, etcp->ack_packets_count); - - if (acked_pkt->ll.dgram) { - memory_pool_free(etcp->instance->data_pool, acked_pkt->ll.dgram); - } - memory_pool_free(etcp->inflight_pool, acked_pkt); - - // Try to resume sending more packets if window space opened up - input_queue_try_resume(etcp); - +*/ + // Update connection statistics + etcp->unacked_bytes -= acked_pkt->ll.len; + etcp->bytes_sent_total += acked_pkt->ll.len; + etcp->ack_packets_count++; + + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] removed packet seq=%u from wait_ack, unacked_bytes now %u total acked=%u", etcp->log_name, seq, etcp->unacked_bytes, etcp->ack_packets_count); + + if (acked_pkt->ll.dgram) { + memory_pool_free(etcp->instance->data_pool, acked_pkt->ll.dgram); + } + memory_pool_free(etcp->inflight_pool, acked_pkt); + + // Try to resume sending more packets if window space opened up + input_queue_try_resume(etcp); + // DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] completed for seq=%u", etcp->log_name, seq); } - - + + // Process incoming decrypted packet void etcp_conn_input(struct ETCP_DGRAM* pkt) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); if (!pkt || !pkt->data_len) return; - + etcp_dump_pkt_sections(pkt, pkt->link, 0); - + struct ETCP_CONN* etcp = pkt->link->etcp; - uint8_t* data = pkt->data; - uint16_t len = pkt->data_len; - uint16_t ts = pkt->timestamp; // Received timestamp - - // Note: Assume packet starts with sections after timestamp (but timestamp is already extracted in connections?). - // Protocol.txt: timestamp is first 2B, then sections. - // But in conn_input, pkt->data is after timestamp? Assume data starts with first section. - - while (len >= 1) { - uint8_t type = data[0]; - - // Process sections as per protocol.txt - switch (type) { + uint8_t* data = pkt->data; + uint16_t len = pkt->data_len; + uint16_t ts = pkt->timestamp; // Received timestamp + + // Note: Assume packet starts with sections after timestamp (but timestamp is already extracted in connections?). + // Protocol.txt: timestamp is first 2B, then sections. + // But in conn_input, pkt->data is after timestamp? Assume data starts with first section. + + while (len >= 1) { + uint8_t type = data[0]; + + // Process sections as per protocol.txt + switch (type) { case ETCP_SECTION_ACK: { int elm_cnt=data[1]; uint32_t till=data[2] | (data[3]<<8) | (data[4]<<16) | (data[5]<<24); @@ -877,34 +897,50 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) { while ((int32_t)(etcp->rx_ack_till-till)<0) { etcp->rx_ack_till++; etcp_ack_recv(etcp, etcp->rx_ack_till, -1, -1); }// подтверждаем всё по till break; } - case ETCP_SECTION_TIMESTAMP: { - uint16_t cur_ts=get_current_timestamp(); - uint16_t ret_ts=data[1] | (data[2]<<8);// cur_ts=ret_ts = RTT - uint16_t recv_dt=data[3] | (data[4]<<8); - pkt->link->rtt_last=cur_ts-ret_ts; - pkt->link->recv_dt_last=recv_dt; - data+=5; len-=5; - - struct ETCP_LINK* c=etcp->links; - int rtt_sum=0; - int tt_sum=0; - int cnt=0; - while (c) { - rtt_sum += c->rtt_last; - tt_sum += c->tt_last; - cnt++; - c=c->next; - } - etcp->rtt_last=rtt_sum/cnt; - etcp->tt_last=tt_sum/cnt; - break; - } - case ETCP_SECTION_PAYLOAD: { - - if (len>=5) { + case ETCP_SECTION_TIMESTAMP: { + uint16_t cur_ts=get_current_timestamp(); + uint16_t ret_ts=data[1] | (data[2]<<8);// cur_ts=ret_ts = RTT + uint16_t recv_dt=data[3] | (data[4]<<8); + pkt->link->rtt_last=cur_ts-ret_ts; + pkt->link->recv_dt_last=recv_dt; + data+=5; len-=5; + + struct ETCP_LINK* c=etcp->links; + int rtt_sum=0; + int tt_sum=0; + int cnt=0; + while (c) { + rtt_sum += c->rtt_last; + tt_sum += c->tt_last; + cnt++; + c=c->next; + } + etcp->rtt_last=rtt_sum/cnt; + etcp->tt_last=tt_sum/cnt; + break; + } + case ETCP_SECTION_PAYLOAD: { + + if (len>=5) { // формируем ACK - struct ACK_PACKET* p = (struct ACK_PACKET*)queue_entry_new_from_pool(etcp->instance->ack_pool); uint32_t seq=data[1] | (data[2]<<8) | (data[3]<<16) | (data[4]<<24); + if (etcp->got_initial_pkt == 0) { + if (seq==1) etcp->got_initial_pkt = 1; + else { + DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] Waiting for initial packet but recv seq=%d, ignoring", etcp->log_name, seq); + len=0; + break; + } + } + else { + uint32_t d=seq - etcp->last_delivered_id; + if (d>MAX_INFLIGHT_SIZE) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] Received packet out of inflight bounds: seq=%d last delivered=%d", etcp->log_name, seq, etcp->last_delivered_id); + len=0; + break; + } + } + struct ACK_PACKET* p = (struct ACK_PACKET*)queue_entry_new_from_pool(etcp->instance->ack_pool); p->seq=seq; p->pkt_timestamp=pkt->timestamp; p->recv_timestamp=get_current_timestamp(); @@ -920,12 +956,14 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) { uint8_t* payload_data = memory_pool_alloc(etcp->instance->data_pool); if (!payload_data) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] failed to allocate payload_data from data_pool", etcp->log_name); + len=0; break; } struct ETCP_FRAGMENT* rx_pkt = (struct ETCP_FRAGMENT*)queue_entry_new_from_pool(etcp->io_pool); if (!rx_pkt) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] failed to allocate rx_pkt from io_pool", etcp->log_name); memory_pool_free(etcp->instance->data_pool, payload_data); + len=0; break; } rx_pkt->seq=seq; @@ -936,23 +974,22 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) { // Copy the actual payload data memcpy(payload_data, data + 5, pkt_len); queue_data_put(etcp->recv_q, (struct ll_entry*)rx_pkt, seq); - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] packet seq=%u added to recv_q, calling assembly (last_delivered_id=%u)", etcp->log_name, seq, etcp->last_delivered_id); - if ((int32_t)(seq - etcp->last_delivered_id) == 1) etcp_output_try_assembly(etcp);// пробуем собрать выходную очередь из фрагментов - } - } - len=0; - break; - } - - default: - DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_conn_input: unknown section type=0x%02x", type); - len=0; - break; - } - - } - - memory_pool_free(etcp->instance->pkt_pool, pkt); // Free the incoming dgram -} - - + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] packet seq=%u added to recv_q, calling assembly (last_delivered_id=%u)", etcp->log_name, seq, etcp->last_delivered_id); + if ((int32_t)(seq - etcp->last_delivered_id) == 1) etcp_output_try_assembly(etcp);// пробуем собрать выходную очередь из фрагментов + } + } + len=0; + break; + } + + default: + DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_conn_input: unknown section type=0x%02x", type); + len=0; + break; + } + + } + + memory_pool_free(etcp->instance->pkt_pool, pkt); // Free the incoming dgram +} + diff --git a/src/etcp.h b/src/etcp.h index 77a63d3..a5ffaa3 100644 --- a/src/etcp.h +++ b/src/etcp.h @@ -34,6 +34,8 @@ uint16_t get_current_timestamp(void); #define INFLIGHT_STATE_WAIT_SEND 1 #define INFLIGHT_INITIAL_HASH_SIZE 1024 +#define MAX_INFLIGHT_SIZE 16384 // максимальное число элементов в inflight приёмной очереди (для предотвращения атак) + // в этот список пакет добавляется когда перемещается из input_queue в input_send_q, при этом к пакету добавляется struct INFLIGHT_PACKET из inflight_pool. // пакет полностью удаляется когда приходит ACK (либо conn_reset/close) struct INFLIGHT_PACKET {// выделяется из etcp->inflight_pool @@ -96,6 +98,7 @@ struct ETCP_CONN { // IDs and state + uint8_t got_initial_pkt; // uint32_t next_tx_id; // Next TX ID uint32_t last_rx_id; // Last received ID uint32_t last_delivered_id; // Last delivered to output_queue @@ -147,6 +150,8 @@ void etcp_connection_close(struct ETCP_CONN* etcp); void etcp_conn_reset(struct ETCP_CONN* etcp); +void etcp_conn_reinit(struct ETCP_CONN* etcp); + // Отправка: используем api ll_queue для очереди ETCP_CONN.input_queue // Прием: используем api ll_queue для очереди ETCP_CONN.output_queue // для очередей используется формат diff --git a/src/pkt_normalizer.c b/src/pkt_normalizer.c index b7b6362..7783091 100644 --- a/src/pkt_normalizer.c +++ b/src/pkt_normalizer.c @@ -351,14 +351,19 @@ static void pn_unpacker_cb(struct ll_queue* q, void* arg) { if (len - ptr < 2) { // Incomplete header, reset DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "reset state"); - pn_unpacker_reset_state(pn); +// pn_unpacker_reset_state(pn); + etcp_conn_reinit(pn->etcp); break; } uint16_t part_size = payload[ptr] | (payload[ptr + 1] << 8); // DEBUG_INFO(DEBUG_CATEGORY_NORMALIZER, "pn_unpacker: new fragment pkt_len=%d (at %d)", part_size, ptr); ptr += 2; - if (part_size<1 || part_size>1500) DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "PART_SIZE ERROR!!! %d", part_size); + if (part_size<1 || part_size>16384) { + DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "PART_SIZE ERROR!!! %d", part_size); + etcp_conn_reinit(pn->etcp); + break; + } pn->recvpart = ll_alloc_lldgram(part_size); if (!pn->recvpart) { diff --git a/tests/bench_timeout_heap b/tests/bench_timeout_heap index f7b2cc0ac4b1e80eec7257af2c2ece7491ec30ae..8900813230b84e2598e70e7b0c26e93a3156fabd 100755 GIT binary patch delta 41366 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 bI1o~J6{yx3_YJ0cLgOCkXc@11qK5rHp&cZ6 delta 41288 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 NC3PW_R1As~{tY?z$G`vp diff --git a/tests/bench_uasync_timeouts b/tests/bench_uasync_timeouts index 12c310cad4942318d769c0de818959e3c73e612d..3f522ae641455dc0eb939a96f9b4b2eb16648578 100755 GIT binary patch delta 41088 zcmagH33wDm^FKZ{yPIt8&3$Kga}gjXxkCa8;Yc7rfNkN)+bVLzS49x zzk9LUkM9TF+O^I!r!~#f%xQ)6f>x6DjqSOSit?6HtSI}ra$`5GP5N{y-}OZLHlov7 z=QcNJomP_h5N*}I%B)iZO0~&ZovA?Em9<`VZ?AR9cG0Wa(d@SLgJ#NkgWl5i=M>XV z>vQvHx0avVg(hoDa$U4Qqr6Poqh;qg=})bH-n^g}+9}G41GB=)>azFGRg{X_?;9^^ z7xJ>`(_P{DPZ7PT9VqyLW@yj1Z52;T@X?AuA%J*5*^WAD=whFwO@$6M@q73iFUIj6FQ`KxR1NkA(L)szjer<0a`+7CT-X?u=F>gbgg@*5*oJa{!YtP zny3Ypb>P00W#^4ZQv!~(wzXRw?cbGJQA=uYM{RHS8oHvT z_6SKi(?L%fab<;lXbuKKjJ*uzxsit#T?d7)=bZx(x zySH0=w|5J=sr}NsM@b#r1WH-a%6G$2wou1mxa|KhK1IjbjlN-gDC4!~fSw0B_)>Rm zai5t~tNq?5lTK+#Rk<`p8&Q=?_i6W6IjK;4xhgC>t`IYTTi$QO4Sj!m7z-)2HhTPb z$t!w^t?`O>t7=6_Im!*~vQg5Quj8y;8^&AeIBS>2csS#A+38Zsxkk@I?NHy(X_5A5 zzkTG?hV*YA{Eb~siz(n5K7_Tn(il^q?dTsz&uX9c&!F!$w*hT>NW5;6Xj{0h9;;1Ep|;F z`lE@Rd8E@6y3`3HY-S@cD~zzYCu$7Q8pc$S&@PU#cbgL+9e4#Zlr5WYRs5D;MphcF z@x4e^)>Kw|w2YnnKZqq|wO>Qb$i}9o#w`vAx3X{O`)gbxZPy&*^Js%MaC|O78QkNwcgNc!Hu&kvEN5lpct2gC zC>dWg9@o?fIc%T_)%1z>+=O9tLGzfHN%dOM#2RY5Yv;s7m7dVfPRdWa$4i&d21?y! zr9K9ElKL+)RGi!7ye1l+_KdhOHX+zryr$gD-X7Edx|XMZ14`8@S=y)6*ilapBt12`r~~ zMePV1T(F{c^n}K@HLn@bv|MXBBhNZ%S5wpFryFaui8I=fMSEsOI+?YzGq(ElxrBmW z>PxENzjL&eGqXw4_RWmP_D!Tm{gnepEn5Sl?fF?zxo^%_ zly_KTsnNC5WzM4Km{c$E+M%dS}8@*5)F+0S0@4u?o7LEHae3&L?J1SDR%8I5M zTjSr`A?T%4%aFD#_gtA@r>l)m!1&x=7}~pfuWaudBu6b*?$u0l z;v?Q<8U#AZmi=gLTm_+QS>0rFT2h2IX-E(dO+H2*>zjZ zH)Wd<3*JNd+Kx+Ymp{!oaQP=T$651y8q_WzM@kx#Io(HWi zIJ*eb&;iveYT>m98z0k-&vlTG_T$_XdPj?x=ZLtc1lB%NHrl+jj83*|I*&CM)~=}Y zS)^6XYi++b7pk{Bm~o)7&vQ*p9cwREto4qssQs&i4p-D!rf84Mi}H%qOFFI%wrPjw zW!u9F6{Qk2K7Y2UsZ;H@B?GX~(&0`me<&aQ>)N6{TKK*30bjHE{7O$`94Kq~R~cPt zyiea$-b4q=)@H{5DO>SpjWs_yQv3Mc3_7Nn=Q~_h zG+(y1Xzy%HgMG+f-Zn0^dE=^@q=qHVkNzx%PG*BYT_M@ zGj5<^UXTWIg9sZ7qv>sJ)xxBt7r?^7^p9tnn#zsEqdlf)<8tU)QJ3ICgB5it+qKgR z+o-=irFku?bRB%MsfibvccoMOTv2`EKB1FO8|23=c<+D#pl{##E6N@YN0-kwa~|IHg)54%JG;I(mkw#^OGZcS z!`{AR528BkDi_bPI-i(q?q6_>_R^B5stuAM_+Jbcvn0bs7(icZzx)H^oQAfHn2KSk zll7whRqsW;-iw-jX{X7IU@0v@+n0Q0Rq1wmgQ4-U zt4&P<23>x#FK4@)!b!Q2~xU;^&B2eqMe3oU1LXHv9< ztL^IfhqOmlXQs`6ND9chBVYp_A!NT;02u;S=mLt?*QTXK>s3BON7yum@q_<_ZMn)G z)aN$W$SOV&+OXOz^@j(w4Yf&W^$*I{%Kxisxly&)sQR#8wNATS+abaY#F9NPgVa3C zc`33tuWDV^bdER#q+}0I`{v>^$+Eb!_Vk)WY^gq6V<)@z^P1G)Jv@R{<+Z=}Evr41 z@iCUx=$~S>jI}-S)VpMDTY6r5b8Q|S)&5-DMZNNX)_z?p>#f#O));O2x>14KoIq-S z*v}(`-4uMKi*|8cyOdHeNhe^(cwF{j!j-qSe_hhAq^fV@njhFshtea{m9$jdqw65Cdu?Omtj)Z#T(7A82F;Yzo`uV=N1L)iwAN2MT^Hke36zSu zq6bj9u{A1mUva^2`Fql0L$9L73=|kO%4$C^ul=gg0)4Q!&H&~Q%y+s{O@LbR#AJaxykoXc=;RDXx&`H6Jz@(`lvL%q%FG7iASfc_l5T8 zyAgaB^*viT@?~|MY!&(6&BVk#d?^RvLfWVC7Q|JStaUH1JzKJ8n$+26R!a<>1Nqw~ zJgHgk&un#JlrdUpy)Sq$X#ez)JB{c5#xj7V>sv^}KJRgD)cu(Qb2p(8ePk|G)P9Rf z2@ndaE#d-#{Kk0j%Pr>G(z#`|9p;uTOG!r~#Imll(lXGT8$7gRNXgJkZW}ptE=f^iS%|&_@X+6@dlTa0pNf(e_@CKbz(Ceo+HRT~)JOd&bN4k&sk)A|) z6=|Lsv12CcBi&I0c}SJnO-(-{t)7GSEQ(7>UkCwMYpYjb$L6LeCu^IUwsLwO1mMxA zxW1`r0@58wHzPfXbO+L{4NXmlkRCvK2B`y2QlYa z51<{S#Rys>JY3Ka!S)Fdsu8|^L|TuaS?sAOHiYnMr0HAH5YnqiZz3Im{mVL7V@` zk={gFk2K5=?I2A?x&x`J7?}geR3R;X1Exc|80luD=aIgE^eWN=NKfu>YRa-f&_R?V zwH-o_v8G)`8iZ6i3dB* zx&!H4q>GQEVWjm)49Bs0zv-(3L|xV*wnND>0+c?ke)}n52=*TMdXFwY)*2nQpb-X2Uz+P1d^ zPCN47Nu!h6#COudZJ0YS)9s7f+emHDQt{XKoHC02M!J!jdCtv{{>eqz{7A;Ul>?sru*0U>uJOJeo=GYjsDv;K1k9(e^lC2tIZq z`uZ@#M!uEHEGy0HOm(WX@dZr9MvdNWk>S9E!*hnbcx&mvB&+}}f+?MRZOhb3U!M5b zkt&5KtJI3#%^f-yx!=JxnYqe*9cmwF-pNamHbGh0vrSDnJd+pGo&l{2w4nx#M{*x% zTS2okO}2#Dh3u!=sdqC~tC!~aUTknO#{fKH&0;28EUZ{7c`vZ23MFCBBiu;3UNQl+ zD$o!x zBs7<>1RENk{9m+q&?ekPO9Ee>mVdl;${3XFK}iO;R^@A}g0ZF6pq~eQ1k-Ub4O$-$ zWOq0|6Al~x{%APG!uQ7qUqD#1?rLf}B{>y^rC-(v-p}BpV;#Kr)GkvSp?%M?Km)~i| zZHu*O-^P2d_@J4ftkfR;Hs0&TSIq>+t`EQ6Zl;H|1>ZlL)FK)u5+U9^bT;)*xUpZT zM8Z^V)-TMf8xn`QT+f)IocTLN^$XM7Z|2}MyWP#D z^pbY)W)>~e{=8Wnxg%6jOtXBmNKt&%6BsemY~Nxa0qUb#uYbbf0h9hIQQbqe7ys!_ z<=V}ETBtt3yAp1FN|E1Va@uUKS$9A^H6M$=ZLehk@`^fdV-UqgUO@<7Y&&GV!R37* z+IGY!|8iq5nicLu({5f)RJ3|s!TR9s^$M8$y~b=jM=f)=V)kTug}_2XydpysWti6> z(+cexj@wX41E6YYr{G*gT~;uDM(0>1U)4NTk#QAbqh zy9lCe0bf~Xf~Ia3r-@ei5}NWPX#;bMg|AIkDO#nDVvU*F&=~QinX+SBf+eJ-Pbe&t zZd2UD5R!aiaN84-VhzQtR^Jdo7D}L@qQF9l^p+TDp>V1e^DUG#I0tH+v4)04L3PzT zR#8GD!dE~K#lL3OR3$VzXeJn}>YE^iI->WnS^a0!D50_83sDf}KWcon5*in}3W!4; zi%AliD90#Cb&6XSO76K6w5{q7T++s)8iJoupM!xyv%+~}u|s_bB15w!vPb<3vmrD` zA_vs>8OaatfbJbr-C2Rch&RMMH%jqsZpYI9f2vZGHY)UQ|ah6Xyzsq_jKId!_bZ|m-GeJ zqZGS!T;8d0rpVs5k<2j;H|K2J%<`sNWKoVu zMR$(m`E65G_;hr6=r+*8)W4xrbVcNNbl9ODgJ(u}4<@Xg&!{8NKy;;?$2-*iXehc* z2#?+#bqB;m_luU#1nLqjhSB{ca!maamWUqcie|o(YA(wjB#|?!H%QTgC30R3!eB%X zl}MxN!?K5a`=iyX>Z4F3dZcXVx?2!yI#vcXh1{;dRiek~QIOnjal7LsW+k^@VfN?= z67wdP8$WhL-y=~Qx%FZ_rphT2L~gwqn+nQN#y2wfeYtLG@0CvbKxR6y6NOr$e2s_Lvm{g z?~Pt5Ig80Hm+imWHw2dGNN%4nRx5iyfZX9naWftcRJ8ea~(WvCI0a~?}H9Kha6Heyxh zz;gE1p6{WiO)Y|R*^{FWBQPLRv9w5jP8&e>w1}B3hhwfiU2-exEj*FfGdyRC1-=yF zV%cTnCVP+-whUM$=~{8#u$sFQd7n*C&0){A@(QS{mM2$3#eZV8V$b)>LBm!x5}s)< zkR6Uv+*h+evuf{U`Gad^$Dy#dFXt7vQYV6iy^oHO8^<+!l}|N_6jfKj3%!sk_~~~g z@+=0S(PA&+p;wn6zS!G&au*f1M;U3aBjl#CeTwDuRaCc?n6mZ~=`U8*trZVs2dQqD z*x*OWt*aryUMB1AR^3*?W$fjWd$-AL0j7w(tMt)BCbuwg*^k=MLD9-a*{(K_)U4Sc z^xNuYAHq8abr3f{G=lY4+>&7z`!G3hjTX0qa8LViY3ZvLH{Mj(M@TbXx44buY9n={ zx+(4~-bcZ0u_M9Cn-FykI^sx_s=<53*EY&X8-oHziax>Q3g$?YD_EY4CXRF&O^Rg< zb7X|f7VZ4Wjvb>I?oZvb)eDcRGg*fC11hWKOqFUKQ@e_h0TfZP4zx&quQ7m}Ssa1Y zHE7zIEv;)blOsd3RV{}toOy`n?BPxff)o27u`hsm*|2!;1%61K-L za=qUm-SG_eIPO0&$}!UMBs;=QpiW+nPyCEyhlZcG;b`Pbn5RJq|`1TibLMT0$ zOT5icDlT5PRC?T$6=ENnjGBfb_lIz^H{jMg^ihmR{2p(f!!5B_#+Zfmci#j}6Qjk*Pzq1%#-%LP%Io>C)P`ht3z$J_*q(FcxFJ4TCc-paFw_pKZjAI z^Eg-2g-t-cq-v?$!Nc1Rj9K`CAkku)bRw2 z+T-N=2aq1ktV*9*2wtfPK7&AUXQ822VezDKvVB)Go6%+&%9YSeOvCseCBsH$@P&&h zQPVM?@mBI>>}4iLaR2$H6o+9n;}(9&)c))TqlH%lCHP;vLty8sdR~-7P_%C=2X^g= z5JP=UU0R9B5qLIi5?TaxqB3zdf*hkRfx|RBQ&IlGIpE&_Qy(Di=%xjbISGh$YXwB& zdcGS3QD1d7^466GaX)O)OjPx%}@Cm1i-kJ5>%xB%*S{tl*}Z_7A_8&irU<6a72n)}RWH%>s;Oxgn3^$iw2jXo|P2Ml;96Z_$((!0u@Y z#bEQ52$t)jbqvM0al8nP7d>MrJoGzsRB^9>HttWtv9n)>L%yhfEnG1a6~vDqFRAho zB!m~UooeV#@lp(hx_Ffdx#FcL2UQtmL)zde;%!wv#74{n{bvlNhiUa**|w6vgMLhv zgPy>H{ekNI4ICkT;2Di->~-XG5+*7?r~&*;3QuxF!ZKv&_Ey5aM&5lPG<2VX=x@!w z316J2ZWZ0_6h&`{d+ZeBV*iv%t+-36m3Jw%18(hM+I+WC>+VvjL5+I_93cas)CSf5 zI`XX4rhh5LGNe+zO4wH9-QQ>1ZNM#}8Ohyx@!BVyEeo)LRt#+Lv=;~#9se9?@O&Z;#8XfayQFrz+Ay$oQp5Anh;+$U zAbA%+x+K4EI|8siYVhwAW1P)C_PRmi2sTD+aZ?#?n<9l@uJ%!b$0p-L zyUr*v72q}=vPJ@ITpG|DlvThM0s@8s*^A7}jNAj{JThN0G8@QsWUh8*Y$>p4PgoKV za37Fa$V_5nJCH|_d4Q3u_7dp{8$F9D+qV44i14wat7l~&fLXW!N8e%!>z9_y)pco5B-A{x$ksKCYL;n#i6Dh&Y zQzjCQz;h*oR|`+2Sg{T%mHkm(7E=;&Hu(?iptRFTsqzyEE`wxR7h)l!@SrFit;0?X?FIr^uN`G^r7{ojze0#Hgchg^c*l`0Ze&7N|9;LNHUQA$n*tRpE18@Q7;0f$IJ6ZIVqn(@-{X8uN0V%^LHHUN67=yT>-O2dq}`V3hxBBsviAQ z(KLJ3NHjYfAVW+IGBW_yOoM-?a3xVo=`@SQ_9SDe-Yz~%q9omEdKsMTH0Ok^B{}3$ zZB7$8Ey*rxCW%3uWv}Th7PZ9dfS1MQmQ*5R>w?>wd73eFYWgX-h>i1idbHZ$(L3bg zsj~leH9J@bs==+&jSek>{tKEr)PT(W+@a$J|4zBdslxjV>g8ct{FH7MShoyI)1Mu5 zR8_JdG)eVY!$Nsa`VeG1z(TPn9j`oz+~WWnv&o%l2XnDr)!3Pu9>>(6DtC^hRtMVD zhp$J8&PB>h)Eup*pFjgP&fn?mAcIHmY)kI!#@p5Ez&c6|PRTGj`x3O+&U%@;ijvk8 zh}~`9*5s6y?Qiskl&4WEUQM2XS~kw#X>7E?bC;`Zx?Sxxu*RwWN1Gda9}T^yH-@oB z<|}~pX@h^KxZau^8FK&jj^IQwEzW)=4Q*h`pO8lBar8zUQc;|C_j`*fn-1n1QKwtdHD6~2Us_bKT*RIqX0aHSnC zYq`Pm@5hK8E|$Sg?7A)E7c-u6kCRXL94N>!=u>F&F+j$k?~(bARa(c|j>j~*>MHTNX}_4bqhkIicQf>y6It6{bwq64hiIzKXdcI*V%cb*99qImymBc+6y}#s)m2_wacQX zv}K+c(T3XlH{kK!M)EY<)Npc9>7Qap8)`wd;!GPF6dQ+aY5dHIQLrq$72?E#nI6bs!%%#1rX9v1ru+kbabnOeU+*N9T`J{vG?n z9+#T1)YGCei-IP1g&dn%y<3-YMi>4sT^={e34s#(Eb@51S(o-`x~N%~-pEw4E-xAU zJH^Q?il4#{7enBF`c(uGX_S10)!_UclZ-TY{x3BiGRjHmk9w)(-xZr%X_6smmCwt0 zff$)hfv$fW;p$_0(?kRK*<7FP55jf$B4sk9RZ-G?kY?lj9gUtcc>XVqzPc@A3F`GB zpa19@x$p!Gb>`?QAz~r324H1>%JnG`ZS?~5nx5PL3cs}l!PvMe2;NSFm&~??#h9O!0 zGVuoJQuPz<#qm5!5$**Pu5TDVy^HaNA?@~)h2?vrl%GcI#iCaM*`=IG#k!owAxA$H zl1qMVu@MISl(2c?lLCaaN#gGUinPt|z^m&BV|7g_6NzncES+5fZ{S_t_ZWQsg^WCT z`~{gS0R80AhEX^nhJT~YzgP)=)bkC1)dALphK!x!pSI)(*n%yk<#B_?*1ab1v-)s5pWJ(# zHR$YXSs4|qwtjp&RE#gAE>U+D47P_{FhZ5ENyrcWLcCXqy~mrK7YoVS+)N%5k@pz>0;{$2_xb;gw?mQrFSn-DO`az$`^Hf(ZC?Y}F&H zO5Q!A~rsg~eQsKRH$pMw2kuFq$}{NmwjM)*wl2V@!nN(?V8 zAG>Z#Pya+SzjM3n zryj7bJHQ%aujs(MwA?55poJ)YSM#L7k*!#MF*rD=elGqlrgRy& zuZ#2&Ocxm*P3R?GGV)~^2s__x3^JYg*nFehK!?0UG~znb-kGfHIxtqeqJKG;B5 z^9!#q#D|5=MwgrPR5l3>EH&G4e99E(5cS7lBIH zOyoQMY(?!NANp8)UW$P5vA9)Au|A{1K+W+l#`cVU7bzVnLXGY!$~#gES1+^?I5Grh zl0)bQdKq{GUV}WpQwqGK=lPAIlK5T=6y$$~EFW;{b@GYvz}K-aH|J9|=VhII((Am% z0%Dnm=aOD0R<9%5iS1AP@=UK3Yg9^w6PqKr5tr(d+Kmfl$n}p18fLys|8zM+iA>>brPs37S!BWe}b|5K0oB-2aAQO-o28dZLzUhKE{vp@rg3TbVXD+Vt8Zs|4!oog7<|HHB z%r#`L0Av`B%+Nb!QVO9^adr!YbQ@=lD2#_^hrx2Up-m0);hCebm|2D`!fMo$#tjd} zBPc+I-ehE20j&2JGIoj=%J6J@7;03bk>5}&;0Ta*V9Ejv=dfOFFq1MLJk-HM3LcN* zu>dLfUS#-F73;cY!I%aZ1!I@VCur&-?pX>8eEhaRJtr#5$-AI}?Z&&Bi~4FHc_N`oA=5CcY}C=uPxvEO-04D3pq=x1@YD}_;8;n9s!Bl)upo14|j zZ}rUAFxB0Bz1MJm&ErMyZqyZg4|k(qypsom7HjQ}r;udvU%dmuSD-DUm`Yhi*O(X5(zW&=QPw-YUD8U zr({$pk$CgFBBhe1yHcNkqdlre-8HRCfxJurL=C9Q!=z+lT*j=bVX9l#0j)DwEfk$O|MsT8M9@`P+$ zinuVe=-6x`dy%?CdO$=<+n24Lg3os;E{IFR%p=(#@w!12&(&tg2}SiX!ncJm0*@fI zG)C|e&!JYnymFY${KX=*mL*nmIkcqY(BDyFGf!nCMIvG5^IRu2I6N4V9p;nVTAD<3AJmR^*xnLjtH?>6#i}Z7 zCN7ERxhw~!G}*l`iPK!xQ~bqcdqs3#im(ZkvACbGRJKqCjTc?|;tP-CVq9NpN$-mK zzGSC1Vn<(!S0f_CXDBSnMYkNKQqfm=<$Z&1J0L^x{2I5mOw8m@kRroA<076)ruOV1 zenBxJqaTgIyx-K13S3F!(Vq*J#WPWc3&xZeZF$EVJ_ZN5U`b#HIfh7e2;lax!TEPA zMi6myChk?LM-$pw4+DH*k*0ANTtjcmTZ?1%a>)C_!s4i}Hyr8SQxqUmH^8XQ2z$sHGpL_kZ3qyC?JM8>~laF%r(0hHSq)l zI^Bp0VP;vq0NiodSV1bEy6@}wnv8TfqR zU{-*Gl}??ZH$h4bo8_SRU;rgXRT$JnreQoAy6`g#uRZFo!fznOxq=N2o_fFdGMD^v zNo|(54RjCa+)Q6#@H5?chwi@4pl>aK@T9+?>PAbw(R=uhylBh)M(;OT;<@)_;Vy(% zbV1!_u(BF+46zuToaYQWHwP6gSUuya(SQeJ)>$fz(xTf9@X0v4cbz4X8<+*z(*Ry8 zF;I1uHe9?BcmvbE0L@ir>B7VvAiMx5d<3^T%TT6K@4|kvnVAwBpm_44mh=*63oKWw z^)?6JZkm7Vz)df(wBn{uL(oxx8eu3o38ANw#o|$|SSiYuWH53_nl1VbBByJ9dqwHG zsSrHHc*0wWpifqdTTAZymZz~}WARqqbWUjD2k<%v|Fl|?dA=|b!ogct>0T4O{9pK+K`%YBaSOU1nA9K*1 z;glg~35st9 zG(K<*N_=aS$(CrIHQi9FT--ARKLdM4)DNL>%RzMP1M%Vzinsp&RvsUIlvb?{MNPR& zo;6VXGK8Yk#6aOU6lteGkusE`X|(8uB!Yh$fx4!dSE0wthFWZ&ZDQ$A9AP_y#Fs;< zt2#xdDWdN%)ZHug52IAE4SCfPEDj6_|7nBEW`E03P-9RW1#o*+_>ZALF=7b7rYTw@G^cNcjhhHw-1VqxgA% zTh%17cqE0X0ei%RQRE1G81Cq%J6PqTyuD6&_JweYHF07=%_-SKjD{IV-0Q6RE7@}@6So&cS zT^HYs!JG?<6is8Wf0`y*jwMF{e-$}ct&9?F$5BMYL<|JSg5vh^=*eBeR)}fiD3Z2`wMfGE>TK%FbVa>ra9xFUPl^2~ z&i+vr^E(sOYn-kSms-(J*2Oq~RPA?_*`tNucnWUI<6;COW`+EY1{Zf}pk8{rORl@h zW{KhBDVq+9jhy(24<{gUTpN$bkrg9CCSZTNNT-dPfN*n6&+kB94X}$76R0er6=pfw zmc9CM{2fDfwu_93*jJAg6%+9jtCu8uYau2g2EUhvDKB1`NLgyCLtLInWomba$i9ar z(RX6oJ=AHm+*pL4g*bL38MJ(qD0a9JmwANp%sxtQy|Gxt5WZ*&?`C;NnrD^^Ud=ZNx2WXB&wf=`Xh!N7z1E5JD&hzVcI1b7sQ6<|sF6&hI5 zix3{Lg$M>%;(;U~lgLOI5Z=mXi}%4z_lX}Tk+b9hNGS)k6Ts&QAbjjQlHna7ac($i zN6S8kfUE_z7NEXj^oW;{aiYg$SbVvdJeda2X>oWmwRB0VjCukcCNGoQIS8RG)>e9$ z_A<#W<_-wrgJ&-fzL{F``r8ph6akPEGYtG5rn$z%^jL>3hnRxC2HRH-yio*ryvh7J zZ5{ev1MnyW&E?dIA3*b?0FP3*Y??_vHG0S4;Eri)5PkA>+7!^n0z4i;<&iq=5#aSq z+Y9|h>9q5}zXbU5ruw{2GsiI(Uu)8-!MWX{4pLrhqvxzd34B)Zg zi~vXpU3J<>;C%q2Q@Fxth{Kfu+wq0j>QfD=Aw5wTQp&eH+}aKPAG|(8KztOEq8`Sr zVjQrxP|9aKGB1O|Ux&tiCss`*=fJBN&v$_z1=#-v()~N!jv1|EJ&;!jvpKn#9j%=^ zwrT&(;$sEz45WO<(&36UjU4gph2Mkl9YA^^fBDTXy`&eqqeGSi(Pwhoo{g)|%4-AxmsyxxHn(T>ZP$D14`l4blR)8PSW+KDiK*|TXvEZ$*CWq_xquV+Z ztp>=4w&##}3cyB$MUqBI6Djz9*vLViynZnSTi9@)fcYc99s3p=oDPHYf7ll+@N`@b zc}Lr9;BO)3Jb(>cG2JlmFCbh2NCT_rpTd3QY3`4VU!tg=PI2QF!m`7M>cUj{bAwQm z<%J}0YAA^Vr=6><2GRwYQa}v;> zVLBrzJ-~S0ZLeDP>OysG-AISs?+5esCsOKt^rrzzknu}48*)4BX#S`=HiVHSD4Vu8Sd$0;n5%p&eW~=w6T3 zB6Sw^FX)7Vwt$$w!0GV=;?FK!)JhN+0enZ#nxX90`+E@hn*dyq*<$}JigI;I(R*8? z_x5X)eg^R3v%?y-fI~q52Gb0XJNG%NJir|Qya&J+i}7l`V+zLNCX+bEuhD7YpoIbw z9vC89Rcv~q)F$zV!sCC1ODST#6Ltes8v%yg(`Nk?qxg2GM!O9GI7$W#G* z3V}>QW;DRw1xPF^el?pM*~}&@-iM-ffS41owVf;YFyl6m9s=NU+ifVeT(p^k_16#k z7Jek^smh1`ufX*QK$@ecn#<-0#hCd3y!a5cr`n58N_zqC!ZbchtyI&Pwifsjrkx98 zS`Vfj1pWrotZ2JZ9l;xxi@?tVJcfBIq4?Q@^d@)M#{vl51I_!TieKhXRB0FpK>*du zP?|T=C8<2ss`wGNmwa2&+tjenrMN{jcMKgyxqQ{ z(hr$RM)>zRbCIcGBpwKl);dPQfINfD6O8x*c?+3+0G}!#A0cxb;5KV8+ZL_}qTcpx zH1?-tQy53kjGXXZS;bNe<#ZS*YMA{tEbJw z?-=!>m6Mm~KH{c(UJQ~l%Fr)zFlgLL@%7Uf=9}kdc!5`6J{uT7&z&EBx8D4x^@7`4q zF94(!=86#u@l(BN*b5vtG*OiSQ13gk9L>Zi@Gc<%eRKlcP8uu^!65v6i1R)61_*QH z=inXjZ3z<~;UZaXvV@)}DMsx=fTbglfynd+C=T9pbn1`eXYRYIEJCgCDKb6_jJ)ZX z1g441s6N(@IUoZTFV7OK7f~E++;b5QDO_UwB0ShUAyzD+czRnr2Tb+N7Vj>?Vb~y< zriczM3Xi?B-p_@J<$wezQEcwG)!56uOee%b7bUvh1+t&yBaHtSSmFLQzZp{O&D_ zrPYMZ2A<7%#gkvq#`OTlQo;jf!D5OicmPdIrA0==Q%SBI@uOyqQ|6E-la=s~ak~$> z=H1KhsM3Q@iu+K1oE-M^DD^N^8qVJ;zD5~M@_ZAu&!RS$%i2Lo)Jf#ye!@1Rl!D+4 z+}%=2W!IFgcGg>M6+acP1^wmw3nlWX@LhswFio^xLNU1`QH{5I@>~>O2fAnXLmRl{ zh{OfKlrglDW!KC`F7;Kom(pX7SiFQntAY_cWe^LdRQB^FaESQ;FZouJ+B1#c%Rda@ z!=n(AhewBj?gz;Djo<6DwZ8}c4IoEcUP5tEe@9@aK~cOl<5xpLGe-jL`VDqA?JtQ| zODVQZEV$o8H$ZG;~9+*h2G>J9l)B)M_-8?$7-|N9UBX;Aen62*_uB z=Tx$~zXAOZllT?!oI%XRU;PvS^3Na$&&}i3HUnM5q%jzzx&4{c2=sG60lze!C%foU z8Jy#&KX`+UcJ>mgQ z&WX2HP@-xs6c<@LEZ|Gu;u*OaQC}lc5An@u5}d9uNAITpn~K_X$k5@@UTU z!K}?L>Q_=wSgQ^bm0|Lk1rxm|79(?26}V4{eJinH@+bo51lYrr5W*(24T2#|3Gw{D z4QxYUI#Yt&u_&cK1fmxa90vFd)iybpQ7;x{n_}a9D36A1J!Gj734C6!bYw3|?}HpJ zBf&wGxgC}Pep{2kf2kuHol;AUQa;dHE+(zQ8}Y}*8cyC8&msx@25n+R@-X$>rHjK{ z|9ln2W%Bc5y;1u+qlmWf$*tvc6jcH`1MJ`N?HAk}Cq(*c3K}w{nPoYMivTg1Sf!lI zXTo0+@L}G^0Q?jlz{}_!qZQNboS)I&CbAsiKJk+JCfHvSTUX<+=!E5ngGh^Sa2YRq zU!&|xfKu@f?)Fx7s8~xPNM%l+fk4VUe-=g*Bb(P zG5!O^{OE1vKdsCOfIoeYFgNC*iTh)c4$&?F zB5Xb5)`9G6$&x*?R)K>l_8lNA%x&AHYV1p>WQ^jNJ zDb=Yua9CO!Sc-iWgQ*NR#l1i3yZ_E?x#E}g_||Q@NY?NcDsf@#;$C#h6hx&JS%?NfMxZ7 z2-}F{v?$`_XE6#%OoU6X$&auKddYiPj(2>@VL2k6*hq~3UsGV&~tgUIX$Sc~S# zn&O(EJnA9RH&a-*6DW8Wpi8gSIUpGW89{zUSOKK;@4@mNKuW)f%ymXsdIX|m2*A2s zI3J*Bv12o36}>x8wuH?f4+ zs8>MS#Z26<_mDXP@aG!VM6u%m>R;6TUcE6aln8AU7>td9RQe(~F92lKzmU1c2v@bV zg|`8$Ggw5F81W#&@-p$}gA|t02J~crc_A1pk?Fz6Od$L}$ObVo5y%8&#sI9(3b%)7 zhO2nKp+$3s_sD#8qhQBSU+4k!@?@*+Y{@-k&VA_X_WJ*a^FlM{_T*;HHK_M^rNN0Uro8Y| zjzILQJ`sI=Jc;{S4R!AU$X+}td$AU1Ex=kW-g|_cZZje2(PHrn^ls03Kk8h->Oq^6 zu;)YR|Bp6X(1s^-K9tbRSqi;3*=CE*k5a63giR$kocU=C4g*e%7@b*uwfvTPb81 z7c5NI3#5G)mg&kwvah+MtGxvA&&nodK=nD*MicNUdC`{R+yoEKocLywZ-Lw~S4GWM ziVHo4x<|Oa(&ZKeP`hw$Z>xB5D>*D@QSg!YWGkh5-az(GK>8WswT+_eDMdK!0a(p7 zvLU%z8cUsGD-oTyVdGvYru~0qT?cp-Mc3Zhy?c|Ja&vEb0wFh%kVZn27Fr@81Q8NI z>4pfXG$}zkBmn^>3WVYaA_NS`FA4(c6(WLC1Vjj3q~}8s_$Vq^5aoZ*%&cI(d7iT~ z=ggTiXU^2!ot<6D!kbJF$EwFei?Uuw(HRg-XWgczOhM8&fxZT4PzaOObaMKh9XmA*&aQg*<4cSbN9s^OqVp8je*rx%}so)3^r0GB>06Ni5$8;od7#Ft6 z;?@_P0#*FcDi)rp;qK-J7#q@zmQTeZHx$En_?id|k2Ifk08e{>7`*!-K%WfDaepHB zdxy16E>Q_tAri94NRg}nT%(DTrVGjb0U!4cd#tW&s?3JX0ej5bkt&~AV1D`E39_d0 z@$aw%?kHzX*%_Wyjy2CTo>z{uWnbgV%h>=7miNn9TF5oD3_Xjzfvhwix@D++lebxo z4Ln7>U^VO3ZUy9WK%L2GU^@`_oRFD7_9L(t0GC)B4JjIscn+6ifG*~8BcVV`?2mAF z?pVVT-L4|Rt5u=C$zAbgt-;>31p2ZfJfIvDmz_TAh!EjOJ54^3{?l;v*k9(O-(^#= zx&7w5*gMpMC#+=ye0R}gv&w#fzrL1b`9}kL)9eNI`TW#c)}nCHOoZXkgoO(pqAhV# zHnvP$2sFoNB$_P}X^MLW(c###9R}L^3)<{QHtA3y6HfsFz9U~6tIdA&Ifi%6Kyv{h zJ{Wk{fjNkd$0`XTjTdC%D5SZFG@2;69c*~q1twZr7Qk4=if(nF(4h}8Dki+bMHP=1 z`W3wlv2cabgMK6t`i-wm`mM{v@(CdH8{ZiE6>4XI9|0uYLu(PrO%}@eWI->2Pn1lO z)EHpt_J^XAZlZyP(Uz29eZk`Qf;OxUWvGRn1FgpzN$ZhFG`}HQj|Sz4tKq+e{#@D9 zMTq|aF!qwc%?S3E!KDZe3jZApsE5#6s1aFCzV|&=E43E%3ny5F=*&i+WJbTV8rRw-zjDWYk12DKwVGkWu^_i>l-A|LQRi|^AN z-P7qL8{aCTT!0}=jxCFZknmV^B4=_r2!oKkA0VAZFj~9fG#@pB1;2oQUyu+w?dWBI z5m3y}ypMav1peFmxT(qHH9x>6!oIxO2P`#aT(QUjZ-TK@#Bu@6!`j$qoZ&?uunv0Y zbiVHc_BiXzUF%tg$Y-ZNNYNAhmr^v)j7fatdKS*+@mcFpq&<8!c-cLEeLd@+V2ujL zWlEICm21O;E@*cGP?Oqb2R?EGi|pH>Nb=|vC{=+YW-y{$FXE!N#iMvIHyjUSFCqgb zAW#R?nt%Y>oSlt8dqU;{$wQzgAxnVFKwv7b+Q8z2wOKL)H>#9eU>9%rhfHVDmqU@0 zkJq<5!;sV*rr2V}AxH-;G~CE?<*!T(@hcPKCg!;S<6~&yHgLk?M(bZDhWM9>A^v4z z^nr1nH8Qa*c-|2IGBJvgMS&syWnzebnHY2Vy^X9G=Q2&(#5&lICE!-?S^o7VR^Pr5 z;rg36+ssmRdje12%$DNwpM9IzoPc$RI_D5iKuUPtM=Y$69zQE900cz>$wQzwAUP0-7Q}F!0(1h!{|a#n5SUBIH6Uvd zC2D?k+cc*9W!iVd`q zNL1|Otwphc{s1rsV(D@sNff(1icO>n6#GQ8y~rmHq#6J{2@pJ38H#;194 zcenc!mg)aT0)3)G2|e89W#JiA$knw;_)tiB4q(y=?yH+L0I3M*6o3$ZRS5T3!kh8| zUI-sc!uz#NOu(X5$esVH%>6a#p%A~JrHVoJ_O?EOj|Jw(KfqT%pvMI8tgCH292^YDZ~ONvR)!ULsN;k(v_e9?;(bse{Li z(O7|lXyK2evjT*T?n0`=7$9wAXtpvwaSIMc+rihsYkpp7L4`8GNn_iCLfuiZqEPn~ zLWQ!WasREXQ}fJ+Z1T$gB=7#PP5L}+lM+66E9>W9jAA_RM};lpceXN5WG1w338+bJ z-~}heJOi{FPpiO=hdN3FW(7~gRFY{-%p>gl# zIlET-Sw#%lKbMM3T z4>LT&b3SMBzVx*#=79lx(&sEWcKk!xpHLalcLbt~g?z|IZ^xi*_&iJH-A7yXg%=s5*>#n}8Ut*m^=TFb;0j{9*?|prg@ft< z_xU9eO8KNAtouwv*zj41tL8Hu6z21rpr<5J$`~iSxX2Y48SqKk&qM-oEjSuEX?}AU zNfwcgA$~L{T@XEr*yvPk^!*Kb;2{*jo~4|zNZhwf$x^=4UYRsfTpaA(XkDDCKwINVI2HaCO}Uc#t-gh z5w(j|=45oRhz)^3bUr}%sW|L4B5T~@jy>3Wv<=yR3~)}x2=R6OD)4*{^eiBPrtWXV zJ-bgw$OhP!!*mDzDZLwL5FoG$%c@wbuvWlYDjW(LX-8olP`?J4qX7C}NRWWQBY;%> zCt&puN&^^aN)DYpLAOTqC3CL0K{9<8@WXrXiZx;Zzp{rt6Vn}nz@QF!tU(@Qj+ew*%y9z7~=Hcsvk$hG;mA+==*@IL7<$FZ9q06 zumNC1ssc8aZHC?r<@y$A0r+h<{mjL%sdy?2^#$>DJf^N(v>5ua$mA^K>p{`^|Ltcn zh1(&M4*3xUnk+iRIiTMHY_qWuvNic#iH9J0j)!% zrLe?wL3$KueIn5yG)p9G540_jt|DRI*8-AZ$!@@N0D)wkZmM3G6@VJT6`IfaD?{ah zxj#S{Y6t>!9K0~pO9&JIjKRF@YnIY-rX_8oA8yS|`lYJqpT*#z)8Is_T!6rQLa2K# zLtrVun8WP{@U|_JryXDsQ6rQf9jk$R{?fQ;UFH_vAJLxED*9hz5ZOBA42s^x!xtSu zAN>$=%K^^oL`M4|ycR}4wt0NqI>vTZlP z<^Lq~0@x12w%vp3qcqt-+YpIHg~A@9I;R33Pc*9Grvzy!&_zUg2g05f!uA8*L8MLo zxVsf&&uySL0L|(4qCKQLjNjEAKyZ6xIY=b5#UnUevKe^$FY-h`OLjU?LdC1B~(#UUZ11_~_Ja%+0*kL40C)yMzZF zVv+psqb#g(U&eaVgD@@C^_mp+p7gZqPLb_kk?lN4dlewE&E(4uvIxJmz}Enbe7^l4 zb`Fl>KS4Z>*LEIaZT-$Gao7gc_Yh{l3UF>AZU@h*#CN;*zkv$x9b(S(;jhaanH7K? zgnQ9Zf%#jM=pa%apwtT=qz}Hw3q-yP$werI4CUvanpf}1n)sN_-6wd z4eCgMF!^|uC!6HQw;pDJep3)L31G}3&0=Dov>0YW25adSR+|Tg*NJI}RNr%i1^YF$ zI2-boM_`auU??M|CnQtB5f%~uip4~Kr^#{*4^{^G7>t{Vd8K6DeuUL(K<88K0mRV{ zPS+s88L+#S0r>^gn}B#~trbe9R>7S|@rElP2ahTNt#+!2o*0}J?_n!{D-a-Uu62+l ztATV;0ORySX=&j0^;ja#su++)by!A)JB5n)?bLdYq*d=BQkE zQd&5+Y&jB$X+I0)Y6{S|+R>m97zS|d0rDOK%Lq9PsT3Qh%WlIsQ{*lGJA>XqT_4FFC-fN-yfto2v_Re-@n)WVclHF;Vwa41~ zo6O$YG;8%TbM2Z#w~nz1_ik;Vw`XkW8;$h#NmUL?)XGfIbQ^;Je5P(Q?8e*U#)<$l zT@+zdc}FzT8#7ksedkgAcR$@#-b9!;(Z?1;j|L&-%;Pp zJl^dc_2=xY(p#sio)_kPe&pD^AukRYJ0yR2-l$>T8H4mwHf$F!nE2wryb*&(knim~ zL_d;(?G@>Z^t#V~gItQ(E$pw>PBXG2g@LCHmD%VGrxB7FXd=Z^JLq*>jD`mVSJ#Dd~TW@swD*Ahh zUj32sHks)Op7lLcxGu5#QiWjL8I#GYA6t{mSAyAG|4+eMDn?JK%x#s*^a;U;7D@bg zqeos8ojpcUbCl*w6!Tn)j;ya@tM8}rg6`5V3*J)f%GxVL>BqBvM5~EM^CTUsZ!u}8=pNMwtydl$LHCGO+>_^CMp*|5 zIr-xhzoy#IXq9~l=x*eol}Q4ghzUnqMk(K0MH08(b*@+R>bq)o@r%eU`&$)VeSi25 zims_)7NfUte=2&BqT`90aQ=KWN{_=ygx5xT-J~~Icu|x5s2-xV7k*Z0l(&r^M$+s; zL{kB(zZ06L=+@)UcvWJq=+$4pyf1U3ABR(J15~wIc^>2WSCHE;D)s+7A;F|C%$O(> zyn!K#oZX`LllU-n3Ua~x3y6zKRnc0BB)g@jwGh#)o8+ROY>@EGaWWdHaFZlg3D3O> zSt~4AipyF}e z+q0G&rBh>-%>+LB8mfJc^xhU;KK+cHGOfM^Znq_5flR$i2{~)=&z6M!icWvfA~`OV zsWqG27ujS72g>LVt64Kq(W~!HX{PAa_oHhZ7e5U4rUu5>aeB*U!ktn@3yck&2JQ{&@ zSi_@)D6fy?nkc=NblHK5Um#EI-p*Y8Y*FMGC z)6}v_u}rN@v1etaX1xZ~OzSn6mG}Q$`y9~T@BPo`v+mw&?KSPS*B;J*Hm>*Dv(9T> zl3IDYzV$^LRVnf!&5zP;MQwGgXkSn2LAF4Xjs|Lvs$T{!KYXSB;L|T`xbm@m!m~#w zPP3&=-L-G6X?_X~)y(Myv{oxl|H{64tfCxJiWKEA3pb@`Z8K(5^1i1sb`ZU)b#Hr% zrfbDnn`xH~IEeP4Y!lqtS5PXii24b*OSaD+wb z#MSY7`Ert8KH%J!QScVgVeQAlp>$whNyk8DSwOv=*|x@1xHzM)sT(wr0I<`?y&FjnV|ZsE~wiCMd?}lZqpMwHB_e-O6swuwfkoG9ZPAS7ek~jr)eB_ z;Cj;sI{Ljte{XtIN57M(t7+HEW%WeUwDtWvQHu6a|1U%TL=Kd!xK`c0r1t6|1zkIA z=I(9K-W$-0&TBsn=vzF4S=QWVg_Undqiv*)BXBwYX1tG%a~gfccp>ApUjn@Zbok{q z+R}klRH6McFpCaoN#(iJM;lX~Ml-dC%VQ`~+gl!*V=BN5V9ST?_(9*_9>q$!Seq_) z_^*}&dW*g3w066EMR77D4exT$(iEoS>|I;NZ92~0r8E8q^r4<1wS3uhrGs{K(C0Kt z+jh^JMB0eKoq~=yi@^3)pAQ@xU^Eg#Z~ z_G&K<2_vB$8`7RmYBV%AY(q38mmPIp=3G0ZplgQ_T&2ltU;m;1F;S@2ZDfM_bK<_~ zBY!Y)GLMZhg^Y_qS2uAGm=!_T)DIE`TK$A_ny-B~!O?rLzf9m2%uurIKC9xp{3@z4 zXid*Yu(PI;+T$ghf>mHgIM=xkEPkaai zQ9P-=`juTPJHo4db!V2dGjhCl zmMTi-7fml{>XddIpedEKPkVmKC_1XyDzd0jE3BxdhA1(2{24s4kl} zdPW;^YIQT5sw+-=X+{hd(lawg&^9e?W+xi0&73()y>8XMoY|L2v+ki1ZN;o&a?_5_ ziVvLa&dTS`z00prnrl`t_0WPVyJ3+SQCUP8+SbbM>SZ_W+se$yR}c{%0l3tphrar} z%Y^|MetC`7cJ?@J|Litc%f6nS9zN{=8rF1~!n^}YYsc7;W~H^`r!;-7rOb(T>AR83?6w8{4}L8eREUp9;|_mrJyMP@r}M!vc@vED=+BR8;)CE zU8L|*p!gQ!yZeIe*d&2rtp0xt&@0d;amS` zuPxjKyf$Z>7CkSt*XMd09AxI&!c+giOO_R$1VO1*-&r>hL3_Frd-iA37({`Gzz1%= z2!@i{sIA)4dBHJ{{X@LAaOHpDOSD(#IZOMn$O>1ry(wZh6dkwZfxy>_YcLYXM(Qk! zI=7j5|waC~bO>b*g7dWYn z=C!a5{ab6l&>6nE7|}jgGT!_^2{m?fUBbSKXqVP`Kdx0TOmRGx3)hp6WHvNaJ@0aL zt^KZajaPhW?H|STZfTvRPCK+P(zCPP(sfNxhW72k97kS(qLe}6)^jdbx7x3ZhhQ_M zce^ED={f%AHHGK2{Cng5Z*%y3yPVE!C`taKgswO3)@I$?4(Fn0?oE!HIA1ORUindj zQS)8)7CKO}CZ{8ik`=#ITl1qjXg}YZNljY(ea@OBm@ipV_}M&6gY&3g-7zVx`EY=K z9BQv9mjiF}yeQ&+J;}KF4!();U;cKv&?f6ms5i^Q>SuFd6}!a|^af_$2ii0jW-iBJ zN;D0BPJ8FR9sYmkD~ddBlq`EkEvfyfX{R=CQHre{BEeXd_WYunpspNC-!AWzBwxf> zaL+$$!mM8GNR5PeW&=Ud|T)AfMLBzq4s5nWK|e`~~vs7Ddr+ zE%9l!e-3i4{C=#CKW4g`KH1sg{ff^qE+uOg#q+V`sFq&SAA8gNH3f7@J5&=NcB&ov z_N||y9OUKb>SxWI-|qXfCX(oHE%g3e`bI0ee|+S5oZpKNV&z6$eFxzIEoL3{=;50Xy{0E zo_6DbF+qXAn&$g}wCt!AnTpT_WNQK$d7TcZ4cHu=bj9`jk{%f}$Y?D55 zi*z&Eumbv$bR!c-@I8WD2G?vPbk=r zy9e1XRuE$-SfMKzsX1%Y(+hQ(-_S#rF`^$7j1*#-b5OuWS4$c1_S%x#Y}I44w!bzh zea~akT*ZHhR$yFMbc`XoRTtf*d9Lmpo(N*`!Mz|gkMj`R$)G!*Xp>iW5C4X5YYy_f zZ*IPsDw`*1$5$ufsPyA%2X)eX)}#fU;~^~XS^LYNlG=|mKf>M`U(*lg;2mo^ z&id{BdTncKw61HpR=;*!z>ydzuDx=YhX_X|B+F#Yt*&F6@hL_Y zaAv$C`!MDDpDDjM&H< zkXVlfL!zYi^PaUAni9ZuHKB{|D;v@lU=D(3-W{GMceM6g_AZs?U#kx8lmG3UPfBZV zH=Dd7Ao>*~dN)gWU~Jz)9~IowJpND&PTUO-h4h{K2>8C!4{a5wm(+E$m*#(4g_-;A z<#t#vG z@~H9rpV$Ymcd5{ZgWfyZ0}p2n9lQ}n^pUw-TKhGGx6$&nWrTKFB$l?*jBQGaz0Hro4SMynyhLyT-UA*lDF!7!DliJjE zkvP6TxGpi}rLmk0*PD*Bf;E;^I$E;KG8Z+%nQ5M+bXm}pqn%q$Auf8|40iD$Q zTbDCC5lv^&RI=iW>hjXMoJAmDi>#AH!V}uQ^*OkSxx7BY`#ao_$o>_Ny1ZqdpLX3# zTN>S)%K5NoQEHTo#ZZ6^xAH-ktH4cB8f)Pnmk&V!?tzNdxm;sV?m@W@Wh2UG zQEsnyx!y$CfU*N8(?cbFKp1t>F+8VxAdp*)XrDN-xc2Kh%}2W1f!t|7J>c!(8u z3ka21!oESd4l8G#hoacA1W!Sku^om`-az>a$}!KnT+0!WMjVe%qTG%{wHHTbzsq$K zW!V9j%jpe0D6>&kzJiFNyo7QsO665_808q0`)g2Hib^BOjDs$h*$0YICZQ~Q4W6Q` zL|KNi0p$d4N4WsyC6sGX-axq>rTuj%MCn9%0%Zou^C&A(-bA?+rI#-pL+M1h9c4{6 zD$X|$Vw7W0Ew;l2xSAxEhsOc+{5js5doAL|8ltw1_D0|M^QGS%;2Nc z$8Z$oo{s}vt`|{h1fc4uhnv>%?})*kJF-kdoaK`(08j&`6iTJpPbShf1R8-W$E z;@vKw?|Qd~x?I!lJr<{h9ZRP&ttWoBYYUGJ#Pf>}kL9}k@`%ed&0TxwXrz{Sygi;; zj6I%(r!|ip??HcQ7mjy=zNizYRb{mHw8)1 z4G*pL`>{cRJUwv>G*>obhM-K+#=aj=Sb&z$7jUC^R|_w5<)C2>%Zs@ufK~15zb4G`Fz5n?R=j7mXK}DR z7kmDUC1BcQgoc$Vjis_sUeesYZsU32*=G7ut>f1TUdit?<2P!wSzpI{{e7~TaA}3M z?dy2YITxA<%Dz)y?=sU8ZPEA7C0V2J)F9Z4htIBligfl3kw~b@W_?3Fd!ulw6SY1~ zM=4YDzg!e?7L&uCX4{O040Sjb9DBO00!W^^Vc(3)BUDP+_x{!WD72a%TT$k|oj*3( zKwEJ0CaK@_+c)P{Z^GI6(4S#=nse^YHmWN`bN{OyUJ!Kr>j50CkNlNQRoZWV6-8_h zQ54f$pKMYTAN4dw%{0%a2#CM>u-5`%u32*WazwzXSIr+&)VY zpJJ)C+h4WrfqQB`_GbHQmPNcasuhm}Qf$PRSZSBqk6LeW`#`9+A2ZrdKGL7&hWWv? zn`aCJt)ADhjC*+=0F$3*`6FLaa_&0Jpe)Z|L}-MkXRxA-@*IZxIL{&^{bbKN=$`I5 z8y-}7ehS&;o@bG$t2~#8PDD-`FUFwow!nh_*PeMW5qDVB5f%8%getrL1#1;(>MHRm z(Strefz5+t49u-&ygFT_D3!9sYBRN^a`Bd#a$>{aO>nYz2qKeVSKLFfGNlra35gBshK5kTag!^RkhqY3K#(PvC?Sb*jFMD8aoa+v z{Z@gtUA@9BZEclM{H*#I0u+)R7RUR#x&|skawKw4{R1;1q@6?>)RTVL*Q8V!y+A92gvEPmI>8$wCjl!%O!a&RnJS}|PsgKVaU>T#nhEWd9 z4|_vQb*I#vYv@auhvz3yp{V{?xWhc9V^(!Ow|IFqn9xi04LBC&Es;=lnfSn+5`7*- zH^bAt(=eV^wY6}$(=dt=!>rVb)`|zLR9xe|50Nj4NVnS1J9P(4L}UbWT~T?VkI0mW zRprejqOA|V9<=*SpEy&A$dZV^T7uOuB3mMX>J?;NM2uyq3X|!&{JMw2kPo-y0qffZbWDddq)S9Edsk-YiJzFE_%RP<`(_zf_fa5t z3&d+_#CR(o-Bb-X4iOWKU>w7cL`*b-QOU?8IS6*OBRe_ShK-5}$P7-D5UbsC}+F;u#P{$S2W43jce-M8T}HQZCX z?%W9JL8#)+!|&xlPa<2{tI+PwC)gOC7-sn+)5EMTuL)h*$Q64f<=C#_vok*HBULMlKDtx^AkS5dtpCZoen^#qbJs&~+5m<7+O zW1uIhubiEG)WI+m)jybr?V!2`dZNmsctSR)_hXxj8YGbu>Ul&WYH$>@HLAI+dWb~M zseFu&8Y+=Xsu_b3H5>@y-=uo8?vY-XVfBW(4UR;OmWFP+eTjUC8ZT2*A-CIXX@b7C zk=xG*bkszNS;=h^J3CooUgXxFEl<;D3%LzoY`Q*O$SsTIX6W;UYTPy>&QUY<8AEP= zGB!(Mq2$H~^{7gzG?LuTvuAT8<|Mam+=Y1(izl}ixov^OlF021Zo5y~wB z$Sn-%8nsMv7Li*`E=PQY&yVPPS91FoV=Lv-JcQg1v-#B$8%l0nxUJ6P3gm|o)&h*U zVvS6FoJYPR%tyBI=Q<|eWW-`Os=hq=T#EMXO z?Jw$4c20mnbryac{(k!IHwV5ug56ottax`vAkR6Xt#!<|1IitaP_8R#4>;#=daM^W zyeWm+i%1^|uVHmQE;K)Zigc!v;{7sIH9C^4-?ZpRj^etacEMmfT1$mibuzl?Nby*Y z7Q0%A+;OBvy^CpIrns}Vh&RzbgdFJ|kj%RE49Tphw{a)#$n@wCh}C+ zMj$gC?d3>CD(+>h(5yx|`di*&vEVqg4)Ec+;uaqRWT4MmqQ5VNjs6#C(w?vWt3J9YOko<|vZ0Q&HVkVn#ZOWpY?mw{RY{&hSmV=S%4+ zW03hr0k9z=FHdP>&)CbvbH4US$i9gmvatRl`%9jR7~vr|sZEx6F^)-d$Y>gE{H z2ddRxJUAo6*?Gk+6=8CWlEc(63i_3*hV?yTPX z27Z=06Rdn}P**8IQD%BM8k}wPxg<9vXS&>wusgw&GefQvMRI*|W(E%x z^ZdwxGoBEB)F*oY>^tMJyto&!n#K5W?}Z=lt(?g*PAeZ4nGIqV2(+bqui)^!`GM$8Mw2GJM%^Gi-I8(lU*T>_)};EFR)fMA03Rz zlZ&!ljX|_ya6}Qm_){tk5itQ&L{(xO7jwju0XQCv61M}WA8i+X11W+Y7E=SMThtYH zqe2?XCb#2=d(2e1NMS*_Bu)lW_e5Vf=gua*`eB;H+WdpO*ZG^ z@`9*UO%1aRL3^xU9P4mX6&vQ22L(xrJKK-py4>jbr!_oH0>QmAeq#DGHz{7ju!@BG+J|!6 zC~li6ZU#|oO(E*uze9IuXtb4&Xma}ubvW-uUGW<`XR;FN^kw5#Ihdg_QOt$I?Mf8x zZ@>fRvpBxEzso2ujn1bzDb50o;cZ9mtBU(YRApFiqarikrW6z?h(}zwPrBF^Oc`Ui z$;%8!z^X9*qAqZKlgd`vx zLI=Cggk!gv%4Zj|LpEuc>q)EaCm|4ru`{z>GkaoY9THVSQjnp^LGB#Bj?9Gnr@&_2 z8<5hL5UF5_@C_x0Hyn;&D(l?9q!fBXbU+K85yL|%eE3g>4ynk{6S)CCCbcEmMSt86 z>YjvfEry?+xUWLk9Ngj_$F)e*Lio8^+-whyZZUIKh{jL~s;T69ODD}TfA0KnHXBn}fi{s-AUgWF~5M2-zg0oh7EjMg$e9Q}|#N)bP?j9a{)sp&lE9Yj_*CHS4b zLjYAN-z+M^DauE6;{8S<)X*SP4^?ak$IaiT;*D_XMoxjX$XRm)9H!hXMfn>~>HY+m zQh>N4Fcv^o7a-Q{rBI1$S#Kz!LFzoztt$#^fxn>z6_7R>8rjwwbz+9vP9-1RXjZKWM$IvDt(Eq}*}BM}sWn|kqi1Xa}V zO-ULiZz3Rm%7i&eu*vy%oTBsx-kWK2sw$OmlS7SHlo`M$13db}?;(b2Rapb&Atp;q zEI&*@%NnrM0!&kZ2vis!=NUxO5P!Vae)Ut|$Xxas)7g*{= zAn&5`4kO!voI&M7fb~AZ{{DV?xT?%V!k`L|7O20?)bIp%m?=Z7^;>E-h=wQ%iaH6NQmEr$UxY;IX~^<= zG*|o_Me&v~(7jzmL{nlJXTBu_dBhj5W{py`T&;qF{zxXG0hFO+cQshvfR^hzi_J7- zuP&$9rW?&~iTY>?>iG%=IEpiSx0)I0iTc3+u9vL?DVTHaB~{)V$vk^mm1!2-3X9xp zs@&~^`Ihx{HKamZkERe0&a3b$=s70r4hrhPIhDY<^r0H_0ZauC1VfV=+lYF*gbL+~ z>d*JXNLn`($3T^PiUEP=0L zuY*GJT6|gsT{hFE*Yr>%aN4g`W!g&@>(#hAa0Cy5iyKtO`i)Ksiewq-l8+L~zPO)+ zpY9v*ll-E{bW#YpiJnf%$PPwaZKgej^4ta$kAE?qa&kqh@f?WPaNvFk5I7kY+N%CO zVwaPg)IpqbQrzejt-w8g#ssCanh^nI&tb9J36Qzmr$~32l%8fq>8@tKl89X9dQeL$ z4>fr1PGxib%_T@np}-_R7?Ig~0Gjs!WcHpwRGD(G8#G=k z3n0$3B#UQ>hf<_ie>GZb#l;vZs^NL}d_D}~BIQd2Ay-X$A5wO%-|_VogXe$xnuaxF zfx)0ECX=GXsNO%b=bg}U7LGOo^!fQ~i!m&FN=iG`dsvdu#miv&9w58;7b>>^R%$Pu zQ&x);v6RwlBo>z@gG5Rabj(z}k0e2AIa;hpBs0LY07z$4iWr#=WH2g&09LhwuJ4#A zjH9R;ccie>Ad!*>eFN2?!c=@@7mXH^KYk`dY%IXet^Tb+c@WrQfPXt6uc5M+km#)zCdoGa)Xg!K%zVlPk{d3pzHSqEu1|EJy zDwqERRN@%n<-Y)x_5i&3feT9tP{Z{cKx~gEb4_$>n0g*frC{pHj56g16fzs9(Lk8G zt;UZBpPlP>(rAXk^FPywm+yluN)|!zZ`F1PO5_BX2}9EX`98dUTaOxyt3_r4Huq#P zB!Qemy;8tk)=N?D2WJhy)D=h_Dr*?Y1F{K~jR3{6+lUP>-zh0PQOa#5+sm>OXm0Kg zFX{J)GqB<>=hxSUWnR)(3R@yYTX^YyL8K;<)53fI$D)5CCAjef{7;Hllt`ht4ej5C z0>yKQ)TZWRgih(Gn^BdG=x3qoHzgfi`vEPcOZa&P4n6^}bF2SNP+kLe0N`)NqIL?E z6O7n_+(6|!Mk0amaU&Sup9G{3l^jNT02zbIFh(K<0$YUIJjO-?c^s8Z0FVBnvK0l^ zgtmbjoFj)od>tTjT1(i;Wn1KxT#$jU+Fzd61=;?lPXiQ5!SFvvrFg@QcwW$6C zs&}aIXWAfOT)&fVKNvj!-+XJ;NgoGN_{_RpwWX&i33|efg%hLMEzY-UROSGzS%$!B zu``L1Eu1?;#K%d-*1tmBPNF0|Px>32oF}h}ykv5^ah{w{6Qh#J(S;=y3_35**)sL^ zHShTrSF!FDXHc}kli9L_XLZ(Fjhs50jGW?id{IkZJj@ttHDeb!A(yhTvYB}zqH29>O^GqTRDSxgkaDOr-=1 z-+av%XHuz0K5Mq^Gu)GA-Zl_6^Ab)>Z01gKHjlJS&XQJd@pP4EMLuuIgPFe)>V~@e zLc;3wasU=EyccX}LvbGCK{gJf*V^ErQhO1eMqQ|v7?noB4z~9bIR0s`D{`DKV>0Ya zqm0(QvJF$gN@#o3lMiBf+(4CGbk_c=UecB$g3|Gv%14YyCnrUT#px7Mb0(J$hK;Z3 zOdjuIQF_LZB;`1U=M4&7n}f`_iI!!)*lPf$AAmfB3cqMKT>|nfD$g)-7RY{7_5kcG z?{CHedIH!nfK`5R3tJno9-wO`5s*RtGP62$HT+c0L+T_YHNvW$>qchnsj>Dnc<#D-ylc{_DijOljO z(27T<7R(Gvt$$)fCMDGHy|@*_#>xI9l`A%37?;`mu7U6q3){a9gr9WSiVc-AugeUC z^K6(@c2{28#u}|GXIXC`JWuxJD&p;A@)t2}DJ$eozH=JMd=FGYkBDh)DU$k&Rc)!0 z-~D(f2eu-HHIu!vMu%v6?9)TAPG%MU>YScjp3uSBsZ zi;}#%;T#=!9GX^A^fa+I3)^;Lw9FuRXNh~_b4z%;#H^R6Gc)D^U!;7FdDn%KYN5c+ zbz{gbEwTA(JT<9!Bs-Xfm5}P|0GS2O0va#prAb zl}SAoIjmo1OvAMl9}5rdT)*STB!lPw=f{&qJ7J~7$&PHxphNo6304c6-57vM8N2b4 z&aYy%yqQh$-FVp^fo#*SLg+}R)Z^?2*YEf-*5LV{eu%L-*e_ynhLn~^z!1M!kO7#5 z$_%!=)6lS5Y|Wu)Y4|bZ1z)60hp}=>ngkVguHP~IjKT9i4R34*yKgkxT?jMto9(Ve zWi{KqY$#YQ3ffWV4DRq$BY#NQ2wSr#$;EcLe#h=SgXe$Rg^pbO!6+r&4KLx zMp)gTyCIIWqX1ehK5mEOFh4=PPuyxpv6d4^&#fXRm!dLxioF5Rf9z1U(MGn&**Mcc znDx5?F)Nosqxk9PZC#9?#aUi4c+S8`vUn<&5-t32b&@!pOL_X9_`Z=UtfszDx7czs zY;iM&hv)HyLXnq8-D}v*TRNvclPFuU)@b|a zE;)q_@&U^}UZAW7^}W%`N7vswiG}USA${snB#tD*C+X=2?J4Z8 zrEhGJvGn;Wq5Z|5?Wt92m(F-l&5y0V%i*U&Zy;6n0_1K#5N$To7-Pe2;}m^6kfX-C z7^%f4#P7k!f6XILh(Don9iTs;wPPdYtN8(K73y-e%QPBDdD_Bpi0=n&cCO20SL@5H z9SoMca~*Wnz&D}^vs+Z~A0#Pcdx{<6o%2uF|AQU+qNngHkhz{vK+ay(k;us>4KW^r z(~#g4Z%H`Ydqyk2^00koAe`3OnWZp-i$^C%JY0aI*PXqDBP92d5G9l&IsAhVXdYv! zgg~RQ7yWLQ@_2UKUHBGK>u`P;I4)h@5C-53258*|12DdAcQLS#;%K;-Ur1did>W7Y z9$$XG-vpNPfZT;ZF5<{DiuWPEQz!TPCfA(%}uXy7luW&jk+ zI)jD#vsXAeQkD;g#CqJICyFs0DPq!SY{zN5jeQEJ0UZ#vCKiVoCWnGX5+`-mr%LL2tWS5f=UxWiRPvEqZXt5 zKC?PLqZ^WutH#RPxWsziV7WUR`EbB0I34|aH2Xav41!g*<_&);o>@9+3x8!;s<+S*pgoPulrROB+)dD2?KG1Po9>(u|l8fUaho2?B* z_G`F-u+2B`*bFLGY!mJlns&EPt%0zyYt6EOLYR&8?Ey zSH*$64=+8x_7tN3(!>|o;8z`9VZAry@nVGd^@zJJq& z!s#3&hRJn|1}7cw^F=cXj1A3IQ`c(6Bp;zkLj7hbWs~YAfHT zbmONaW$CORkLu9M7j1F&nE@VtG{WCn`2pmXScFO*z@J}JPD5ohKyG~V3>BoPQ1Tna zyJKImbKThZimXcwmb;f4wY~`U@X}#NcN{K9Kt`@DJD`?7oRo*!{iwVQu)b|5SS>De zC#QVrV7qA0l3X@M7{KfPh2((PqZhTv} z#}FxLi37hqG|fQM2{^#tOPLk|p0F(1|9&~R2^8PgYr;`nG0hJ8H*AOoI?N^5|1s=>cnoGqb*fm^{?3BeB_ z=Kna5j$q0LjOMvo*`oYD@K9%)9LdROo(PbN??q(+z`C|YacNIHq&_Jo_9RF9c#aQy zvm87RGz)2{)U(hYL+B#dcx`Nt9f6PIN}H475aa{bHOp@W`(|BU{M1w5ntD;Hd@~A3 zewK$7|E8f&j(<8&+(5vXj##r8(s}+r1xWwejt`vxW6h7N9!h$UpQ3mIlSONI%POmVy%6! zrlp|AN}M~(C}AJrXC#Q>olr3;)*qt9rBVtCNtpyrpW2Jn^l0;#7PKO>u!N5o+$tL5i1AbSfVF zcacFi!COK}R*oVM7bP3R^*-FB);_!|n9cmv9HkV2J4 zbTN5x#vFu4rb+lwq7>K&3D?P|?~vU@|AACMUxuFH!>C>1{h14=}6ivs`A;mXaTpLKGNxpE!ueF7h_zqz15?TMUMdW44 zy-kcQr+Dj8a6~!n+eBSC#ZW8pQaKJ4?~8wNTL|*O#p;x;#iil}=~ zv=;^UP=xwxlo)ysO`sHU=pJfsO)EiHKDCV5IGDn6KeZ%qE}X#XbQIvWxC)kVZH~oY z;bhF;Xq2lq7j};-^y!pEx~PaU2-lpluq+MoLZY0qWU`Z&pyWKD1Ajev%2KcfPwAyW@g70^L#RMb@Q3wG{? zu@rPh-oF6+Nq`s6&qpk!?9VyirvSFwKsH&zmVwU)qs&W_^DsQ{nS%ygq*S9<`^B$A z@M(A}pS^rB+u zOS=LO>MUi3?n2!J&RxClWW=Y=(w5sN0q3>O^G!+X!L<8< zF9vj|!B3rKB-3a>hg_smoux`*Llh5Q{L|Kfw#ag$Qnxw0#dIomIySw?(wa>_3q_m4 zlte`hH@xIodxQ024=z#)att!h?qG2df1J~YccNbWkISH48MzWW5m_y6$%q7P>4EbM ztG8O>S^dvYe+l5(fUK}uQaPsw!Lu%aB>p~VuqYi)(IKiIlH+6KfWu;E_d_~C+b?!; z|8NS3bzyAdEU_b?`dP?515oEr*L&0e9bekTE5j+X%a2fEV19F^Yz$M*_s2QPf7%kD@5@7B7vW-s;dm(Z2>upzmnPRiD0tgp0YO@s|)c z0!958%$|nP6z0!qu3n#|yTQi8#nsVxC6OnrV<^gb56)NI_dER?J?H9uHyxju#pYX&1s^1S4uL~?q0@SdIaC?fo^xv@eweH=N%7j3a)R5<}Z zHm7m|W`Qjk;8}=C{js^A6Bwrg9|cGlHdU8eq!-F2ES$FENT6@cG7GnZX-f@$LUuq^ zFnGEW6G+zU_ zy<#-4;JJf1@oQB)J>e^s#T(ErT92b}8;iL|A}vLqag;IeHD2yoL&Yyg($$S<+90CG zW$`qXS|gj6tls`xYcS7UIfrH1gx>kG3b1FM3x za16SRic8}uGlPYKjF8-^fl*%P&6efOLKh))K2j7Ak3=8BF*|>LGl6=it`hwZM5b@%#b!c&GImpjh1+TT%(?Jiqwh7&eP$d zH%i(hDkfsG?hh9aPQ;nPSL~fg&PY2(LoOO~Ay)-(Pc-^}p+HJ2=vHi@Z|C?f17L$*j!yBe*$FrSSOk58hws2l&K zy3*j`1Dj1;oJ9DC4iTLe>zRsr;4UM@celZ_V@4bQk>5~}KAA#t=1P`wcD_HpGF5LG zEH@C^wMJXEqIyo!U1##Mk>b9|6drc?PIHdY%%{x@kz)5`g!7L`aT074B)*?aIk;(w znnF41OHsnR0!OWyDHwpWQR49_IQc~juL`U{KY~V6xZaaq)=3XkfbIQgv7>^53%|q0 z*}VD?x@wuCJH>0L1+yISx0#;auM-uuz-Z>Bsh>kP>zIm%2~*Q?&ngP1Qnu}s6vNnw z%xM%a9-qpKmP5QXm8Q`OkvxsMjh8!r*k{oCq|;cqyp>4KuQ4Zic=F8N$^hMFY|E;} z=R-dB<(cIcxf1E~06?B{^33h{l=Bqu#{vF-=7=-X$PrZs8TqQ|Jy72P#Q3$t>W@!Y z{GTfj-qXoZvOWeZjRkmUjOOnEmZL!aK;?Hv4g#?vNNx;ehEx17pO+#1b7E@|mZCIK zJ)L4k=RnhhR}S`X9gwL$S96`3IQTdliQcjOE+$jYv`rZ+QuV%fbk!) zz<9mCnTQ=CCaLU^ql?Oms z0*GB9d}mS2&~+H*$AE7FI5q?6vjRUS4C~klGvmo*(p*vYS zQnz*%IpaBN4}!oSrpv5-50&GLaMqqf<&${v;VgWppDk|BB8O#G4m=jImDJVeWpuaY zKJQUcU5Nw!uvT(YylRr0;?I!(4iG&L-Sgw!`*%|e-=t5t0a?G{i1#0+aK1ZN{P~ut zVU*FImKxRsdHrrmT962@_~t1QvatYp^VJoVB7nT_nyCJgD?(?JGgjVeG4n{Y4F$-X ztou-z50K#pmIb^t)fizWRlogMWKCzk?-F?QeUl3;E)Q50B~mHd9FR7Uceo(R^nQ;WbPiem#{)UjM@U(J!9n zlGsU!tHhpiPG$RipM6fvgfBe5U%ys!aQTt5Na^ zA40bl%h?9iStcD+&NMF{TPCLH%kBoX4eP#$aD4=j%b!r?k{AlV{Q+|M!^gO+za#K= zOylM6QFW{&R)R1I;8QVSfwEI)+z$LPK!}GMw7{5cKRn*5%H{4D8V&($TjAYyHS{|R zJ`x6g4WK^KU-z{gy7^;k@yc8pToa7p@d8Bu0Z!ZJnA!VuO@l$~2k;p`caE}O@Ai7& z4*_sRBn0B9sJgc^BS%N*dSBKHY(3wm>M^L+WAGAWb^<*4F$-xO?c^614R1yH*=YbTUk_~Wt4GLpv2DMUrpKwDDxWUw2G>&n8K-_~ zF2|_}_(i7ibIyKhe}0-7i%|~+c=F@UGBurPBY+QJ+G&honcA0W>w&LjniaOo)G>T6 zdKLIyfNhkQ5`w?7ka>0vgiin=2jTf|ZN$O_6d7_Egl_?=r{VNHo{I8S8}Z@-iuQ@e zlFY}Rc~I%UO`Kam&WauAjim_H5&yng}m1S(qqZgYpj7_Nb0>_SR*m$N>iT+}b5$QnL$@_lSOHHBy03GlxO z@GQYRXs70I&y_al8)!1&^7Vwv1T7iBNmHQ5Vkpo-OyV;~H=Q;M_zZx0Y#1xT@d1AI zhDjp?ZX)$wDyaD>LwCYoO=2fjLShksPpbZE2G8;apsxVrX!@(&`Kk3^z;7^(N7G*& z$FxF(G7}(2(_gJ&+5+Hnn8u^&uh#EIu@$%g@MtPQVY;`6KzI`n!hbzDS^RY`Zd3<| z$ot4K>F!~3y3mP3L!kv1DfM6uQ~mypJ_L8q$4`6wSkmxw2D&}~SYmJ&FVQc zgoWfeej@OZ0C_}R0DhZkUGou@4Po}NeP(3qLeXy#{?<&0$&2uK z^yfY)E{-oD-!Ocsf;RqROGVvg=;eDDhq%53_oQ8gcMZim z$4RccpMKelvE<>|oz273OT8t_Hsbvn3X8q7@pGu;Nck&~JVbF7xP946vxH?SCDuF- zGIKFaTV)Zf$VN?aI_^~8PWqrrNT+qWN!K600+(AksyUh%*_Ou-#7 zkn(7-ouWAru}BkAzLxy>jn`S$R$Vg>cn;SU5B`JAIR4w|2k1}PI1jbdSJ1=23Du}( z^D?2t-3zlbDY~8R%uJH|L;Se8la%@7!DRjAXc}tGN1Kl!(w8a?k-j94Hhzy{^zbip z%cuM@sg{DlW-2q1DmlJd8Bg*dEL465z1%KU1}c%PEbbHzBwc8~p|=aAao$T;y6dha z$IrxDJ3skmLWy`5eaj~g?tSAR?hEWs)JvQUICInS9ZP!(@&pGy^;3T}xCSF52+hbM zIYs$1K=+(`bWu6daleLRccnxy1y;{PEsf)*^c@0|LA1S(&Wd4_7j5A^pI6ILw5IVa z9DuYf1$gp5JrzWqm{tvZCV(Gj1(ST7wGrq#fSi~i`oug4{AEC)sCt0nBG1DUGez=Y zkZ*VbtHuKD^%V|Lo!%EOKR~hJ?c5Q!TatrU-PYi^)tMPuiyt4Lkb%C?aDfNA`QOz$ zCcQ@gw`>D{F8D9xF#kDNJqyUiHgchmC4K^S4UovI{w2ACd-BRChTwDcp5ds50cc=x{CMGW&N~>T#hf!{g9dSnX$nbqJaoMU&x07y{q%3 zU)gXw3BX;Q--RU>0-FZNV~^*TvBO7!@;?Zl$1mUJ4`Z${EIyuqyw}38Pv)`h3ZTQ8 z)GfRNXoH#d8t@kY`A48^q3oqvujKQq;e{i)&uYCjpU+SCN)>9oJzxCD9rznUe*!}I zJbe}V68X&ifIM>hWMftX5-(xASIFCg6rc%AN`dB;KKvO#f1tgYG#i7)@K3_5L{THUucSovsSaZ1N{Un8>man1G@#}jw47y4%H%%qf@XMAD~t#a z@{C*_WTo8-41dokuJhAjJKldQ)HA0XO`XA28qO`rohy`4@-_)eML!I#xttmJ7SqoE zz@fR^*%h+mU6znspBh2wB;2|`x5=piA$BPc;h@L2iJjQLjg}(&Y1yD-2;37%Gz1;1h%eq6Zg3OhTc)QY2Qcq1@4xb96(~^#bm( z>y|U#2m^MxFhC6d(`;(}WbY=FJwWINP+u?v6EUINLWHAHKbarxXDaUJjm9Doxt5ar zmR9N7C&FdED#W0*w2n@T@7B_@HKFl%lx%Zf#4T!IED#TgnAP8~WLrHYVpXri0r8TE zmuiUt;w=%oTFQuzL;}@xX7iOusCt_f*d-FFo?tdVi8$5OjD&bCgY)>~&!3&ZLVa%% zMnDN0jKcj>coWP7_j^!t>k7YYNf7~xs~EEfMle~ z&{Xf%@XqZ0H7Hi4U_7kTSi8-%{U`AyD3joRZM_rct<60rG?%t0gv^I_m?n8u@h56Mw@3ix)WeTKjd(Pa+$B(VXp*ft>DHC`rrcIwXPpPI59_6vX z2QiJGraegB+{KN+*8%J&7X!n!6d|;DFVT^SKt$jpfzDKaxZWr6RcvZa1#VK(Gg?ej=uy4dadi0D)-AIY_ff&fezr}@(6zcWP zE4o2w+$lAKd>z%T{iJ2@Wf2YIDI+X3JL;218>;IDlPhrl6! zvUh=&QTcdy1#sLC;{oKsA7bM<_((azVI=4F6tqtQgl-PQw_Boc6NSdl0bU6(?*nZG zD$5vo4#;{`)&Z%EG~_W*0%Lh<2a6d`gS#hcgX#EeHF7Kr7;A0Wk2P)T5f#YUns6kt7M zh`swLb_kTOg-Dc5B=X@?4_dGI=g3U9%YZ z$(NA^F?uV-dCTU{S}`RkSUkLyLLwZ~`1Pc4i&AzY5^r1uneVo~PnLpnCw}%dtWplM~`1 z&~r?hf`3T1sZ7B&b z`ub!N_q7=Ab^yp;JS}@M0_ZS+wTy+a!YcfX=5NvcvR-cOJN&~ zE{5U>jp8r39`Y!J1uIZ`d7evjJT++-6&C;@d{=aALTr--_=e(J7=FFKhXZCyN z&Gn=!gPX>X;Bi1-2E=ECW6-e82N!Kq%v*W#dKSjM;A7Ua#@-cD@_39eiZvCi_MLq7 zdKQwo4ry1DNP6UBZ%Kz-OMvbIq|mMB%-)42sQbXb1;p=y#XMZxz=mvQ1P*{GPcelx z!}cc%5ZC}5UV=0LXbNw*f%!FkGD9!ZI^9z|{DpCRJV-e0oXP+#Y5MBQGd3{aApb6y za{*$QZ-zhqal))cwV*R_*PVF^7s0tA*2G7rc~1U>-3MVZmM9>Zo) zWeDsB^fj;1A|}I_2)E`1n^}}cUAEv=uAzF#ec`Cs%v_n9b1P=cWnSKCk(tFYgp3kLUUN? z{%=Sg1^P&U<~t7z)q^t-eAV}nkZAn{6_F(DmBIUcj28<( zC1EiH@LdLmq*e#;8_*Tn0eq>eCG{XHr2?hRbrTx6SgNQ=ue&J4ATxLuA%|`vv#H>j z1aPkeDcO$-od}t8YEo35iHR@@7178*3Cf7QsOP?QRaSVrh;< z;_TO7$0ap0&)&wugF|Nj#S)^=sB|ciPE=|gc=133uMkdZ`a)^Yrbk zC(aGN50rJ|wcDAm|4VcJ5;Fl$LC}uz>_dB#+jgKdn|ZSx;JwVJ?qDx>Hu`k6o&-g) zr{XgLkEW8i7<4gwEB`2o;? z&W;Br!#;opi-E);5KYJjKn5bvmyh4c!aQr|>G^n|KFNLC$FXK7!%b#2d|eT?_T%`z zB9^XybVUoLb(1DOx}u4Xu4n^!_%0^Dx}u4%u4v+`E1LM~ik5^8#aK;zbwv|jUD0YV zf6djzS68%u^SQg&Y?jO|#jKZYcLWB#Wc+N08g^b#jHBZdikXjP3TSal`GR8BRJ|3> z_Y|}LW1sN!Pg!2}B1B!WQi9q%(jFtf?!l&u&Dhf&4+2A28x7pd}UmEYf376F*wK+7$SrZf>iLji8|Zw?=@TQ&QBJ-um~l-I`uZn1oN z`ZtIW#r~-t%iB8G;N(jYD3VY@`G*ph3?(*&lM)?}TU&sc?!w>HCLW}8pnZ6i1GA|g zzwN;EKaj^eu(J%?%aVpUL-tZ3d*T9Jvqet}_z-JLQTAIA9nF+`&p#xD-kfL3l%1Z&)UyIvORi2D?ku+@7HV;(*kH? zB2)7@hzhl8InuhpC()BEQX6Ij%qStb@p(vKaeYQ}CHNT98x&>)(0GI!e~br}FlYba z$n7P7#q*hhD)`rH#}X{RxAKuCIMaPMm;bwjrTD9ohgxtJr0dgzJ(*uEVZ+%b-m#Pg z52Evr!}Jc6tLJ5vd9qb;7*s_28YbG;IAlB;;5ANi9)Xo1RQ_i<7LwA0qht+< zpwIm}U)N96_n#6G_1&B=J;2)9+Dh#NLuY>B01I=b|CX{|mL#s0;ZF}kcx)NESw2rK zW5XNJzd5@Ky=&+3U1fNS@o*l$T!x%m6GIU9DQ8U^RiJcgyce}mt;JFxS|_%hkVK4< z-oyFGau(Zb9_XFDPN2lz)oA9OPKl>lp7;+#yI%zPOIYIsz-u}` zQ_fm=C<`eCHgZn7|Cz({a~7t)+#?dW)43>=$jOGV>O{wbHK!@~Rh&kF!kjJ$x@$D0 z4E_lbq5f;pS<(CN6A8ox;Amx|8Of?e8^p&^yx<`7irWX}-FB#M_+p}BAs)q>{(kF< zzu(eBly8YlKZ-j^^;Y<~gBVGMbM+AZ1$34t9AZN)3y|yCIKJ!<`p&(cSRw#S^v$R9 zt|Zaa3qJr;Uv!kO1<41f7och3dx(RJ(>?t434Ad~dewb3NR7Y~Oi9;(RO?9>;WPmn zMWmMz|BdHyXw?R2Ya-o?Ova&LS_&)Ofp-SD`C_-cSLVWufVa%DSZ0@ zGVyuv9`A4j|0;Q2@_i3m&I1!|MBav&Og`ZV3vk&NiWWg7uHpqpSi|(i$YMUgHVe(= zMBFu$au3k$06&^ZPl|o;1)%2vrfit!lq;pb2DBF7b`52GQlOWUJtta*wRh8;=rzXl}gIQ5G0@Tc%Xe?{h7>x_H2#JuKjpj48c159SfSZ|{m zc1=O!2AF;aX@iXtMgk2d(#Ife6itXO$7xQaVvsfqt~8*j04tTfV;7?cm8h95=3r+O z)ry*V6C8A{g)qYV2&^E4jIa@bLI9SXM~|^?Z3Y?=i~xpm;XX9yk+gjl=nnp360ieE z83LueO9d9TBl(LJtYP#-qXsTRi8a>iMzRy&JPK$>$zm_N@T3an=Ql%gw}P5a_mbu& zys`qv3)=FB71%DXmpmOI5Ih)>(_wu2cGEI4epSA%qS2w)Dq^&iMj^f-neqy9y#x@| zvaC6geg#@bBx-ESlBim|Qqki9rbJkGxv(x>a}$w@vB7U5UP7%9mCzNmE&%IxnD1^r z-*KGzM!Wz*3P7y@aT)?s388N$%|RdspgqdxcaG!D@Qr-#ass`oDGEG;)W@O96KFg6 zV9Emsjb@1EzXRw;08=iCI#Xy=3G^6|Zll%A5^d);&>KWLhyu?Ro#Z#54~aAbMVTW= z=I3EMfEZThi-f*Fy@_-J34<3kXaZ9-22R&{xl!p_O2gu~;vJ|Q9)}3J97mKg5rKAu zP$_#N&>f)l;L(*VrsHTMWswVBmY6GD!AA|TKY02AL_-{fKqev75Z^!`3!shXiz``V zK!}tOfZlk+Rqu`1<(vEPlae%Nf>rEq)r9c zP7yJJs`>!XPl!ad-?y!({WJ&x2*dUh_Q?P`m`F4@r;DLx4bWvoqQSYp;JOU-43T!h ziUZu^P?o?xSXctYc-?)VsJOnsdlQZ7a*!Zp0Ub@Gxezv32wMrXfJnPs@x(+7Hivx=dZu+j`LYN6gOSS;$`CAU&HSel0E zR50`gXmc0xH>+3-UwevK8ZMRG3t{g|U=_X6PMigu14JjL3nbqo2Qr`U5X==PeI zjBG*{$!uC9o3U>BHkr+IFiZnzU3srB@q)P@pZ+EOOIWaoUpj}i$B8eQtwouU)(FJt zvXl(#KunxM-9?a*M{>SfMC!J37t>qXyE4}_o?3<8v=$tziEA>SP{rCeE;i!3KC zFT0dRhn;p3-~M-b-C^dUXm%hon}#uFBvIc zKt5BgH3s(uaGob_^I|=p!qcpAwt2BGRviiMx2>bl6x3JHV7>?EcK~$=kY5n^84ycM zcDh?neF4b;9o_*VsM8Vf1H}yxItR#`;63F5{#+p4KurROW-w&2o+ta4iyzhp9&}r% zFl--iJP**u@S$f|a7Z(QaUNu`jvj(30}LaGDMglJ>ltQm;c2iM0eWgnuDvzVQ;MIA zRFf!WQjw9HaLy&}*^)cuEKb_51%(4bSE5v#k%B%VY+nWB z5UAyVFly;5rO>AVOgX~!&ehsJq-n!aD$M z|1*hcxIxE|GD>BPY6PIEaH&>d#K%bf8;LmjcM_~H8ed)NwmZtgwj)e&BE+a!aLXC z_1T4IqUZh@dpHPFX(N4Y)XXL&5BcC*YDBUCkveIeM;;Z@`;8iuOdz^7~l@}R& z9l)y(ev&B=fFE~20KI@1rFeBfINs6y0(;<^)>_eQDPFAuR6LMxLPwKh%!=lm7|J@h zws%cIr8ac~M`EbWz+-_sC%WorJ4_ADqQMXZ`!(n7ZeSBvc!N!kZO$l>5E2!T;uR9g z7-Y5mn{3t3(G88i9Z$cB!LQ#<=H3cQB(_y3+YVqA63nCqY1gqgUVqHN1vp+Ls*`~^ zR^DXiTc{5jIwt$5l|fp%CCxFerP_nBEqq5SwMTTdm6^?!PUZ+jH8Be_fGt4UXs@r?0Kd^~>RZlel@q2ozaJ6}S zUvIUTDUJy#>KF^#>8O4|9qHrfK3c6YIbz1Er(k_LC{O8WY@jLaWfQIwUyJh_3UfB@@rhysozoSt zQkfQ;e>YnS2ROEGP`&IZ`xnJ(1pC-=yI2h|2f805$xJb=b>HHSd{o8s$bE_3Qt0jo z`%KOB4*BANWGHgl?_?=FBT#UaIvoXi;elhrN%gTc)VMasq%<@Q8LF6#LYkB)Q+&K) zj^brCsvq0osHssi)urXCd6{N%biAalGO-+f@rv4uswwQM+J<2i7+R}NV;%YFT6If6 z{(MpZFVu8Tv0|U;iBr4^FZf0si3eNvzEPL3az6W-x)x7!La(cB12SkJg3A!U^eRnH zQ^hA;SN%N7=jn6|`@&u21vgbczVo{3>{0&$os0bQYf!vSc)=<<2W6g-tEaR|34TOh zTBLfhEZ+5o>X#BH87E?VAU7352DxNOum1!%UDB&kbv}B3L~a~k2XP4#Wps!vSiByq z67`hyHAE+U`V>iyjQ{9S`U@W{I*gPpxB1 zu4j6br+H9R(x)QAPaNkCGQK zRWjE<5s)pSTmW5+PRiHj>0}SxQ({FlUgHU_fs(8K@uMuAYbMY9934WsPk7zaqd!Fl9)bl`k!VIHePrEv%u`31}OdR zNiOLv5YcA((vj%3DYP3G%~s}?Eg8R*^n7Xg%aZ<;r0<-e6H4SHa6`}jH{J8+nLmnt zQ~)m^hlXGk)(DY8r1!keHD34T2rpo{O&`y4q~;i|g!|AD`jdLi%+F`3Hb=-W>JAr2 z)f2T=!)v@EO>5y|%szPNdq>meV^KNDq_yzyFVmA7ezc>=q;*lXs}B_3X%D7w7bmTu zT3N$moV3oa@BFG4=eMCUHV?7ZN&WPxIFuJTX>HZ_-r;w^Y+XN7C*wPC&LGF0;^Un` dj=rdq6 MAX_DATA_SIZE) return -1; + long max = strtol(colon + 1, &end, 10); + if (*end != '\0' || max < 0 || max > MAX_DATA_SIZE || max < min) return -1; + *min_val = (int)min; + *max_val = (int)max; + } else { + char *end; + long val = strtol(arg, &end, 10); + if (*end != '\0' || val < 0 || val > MAX_DATA_SIZE) return -1; + *min_val = *max_val = (int)val; + } + return 0; +} + void usage() { printf("Использование: bping [-s размер] [-p посылок] [-b пачек] [-i интервал] host\n\n"); printf(" -s размер данные в байтах (по умолчанию 56, макс ~65k)\n"); + printf(" формат: -s 1472 (фиксированный) или -s 100:600 (диапазон)\n"); printf(" -p посылок пингов в одной пачке (по умолчанию 10, макс 10000)\n"); printf(" -b пачек количество пачек (0 = ∞, по умолчанию 0)\n"); printf(" -i интервал между пачками в секундах (0.001, 0.05 и т.д., по умолчанию 1.0)\n\n"); @@ -71,7 +92,7 @@ void usage() { printf("Примеры:\n"); printf(" sudo bping 8.8.8.8\n"); printf(" bping -s 1472 -p 200 -i 0.05 1.1.1.1\n"); - printf(" bping -p 500 -b 0 -i 0.01 192.168.1.1\n"); + printf(" bping -s 100:600 -p 500 -i 0.01 192.168.1.1\n"); exit(1); } @@ -84,7 +105,12 @@ int main(int argc, char **argv) { int opt; while ((opt = getopt(argc, argv, "s:p:b:i:h")) != -1) { switch (opt) { - case 's': data_size = atoi(optarg); break; + case 's': + if (parse_size_arg(optarg, &data_size_min, &data_size_max) != 0) { + fprintf(stderr, "Ошибка: неверный формат размера. Используйте -s число или -s мин:макс\n"); + usage(); + } + break; case 'p': pings_per_burst = atoi(optarg); break; case 'b': bursts = atoi(optarg); break; case 'i': interval_sec = strtod(optarg, NULL); break; @@ -95,7 +121,7 @@ int main(int argc, char **argv) { if (optind >= argc) usage(); host = argv[optind]; - if (data_size < 0 || data_size > MAX_DATA_SIZE || pings_per_burst < 1 || pings_per_burst > MAX_PINGS_PER_BURST) { + if (data_size_min < 0 || data_size_max > MAX_DATA_SIZE || pings_per_burst < 1 || pings_per_burst > MAX_PINGS_PER_BURST) { usage(); } @@ -130,8 +156,13 @@ int main(int argc, char **argv) { uint16_t global_seq = 0; printf("🚀 bping → %s (raw ICMP)\n", host); - printf("Данные: %d байт | В пачке: %d | Пачек: %s | Интервал: %.3f сек\n\n", - data_size, pings_per_burst, (bursts == 0 ? "∞" : "ограничено"), interval_sec); + if (data_size_min == data_size_max) { + printf("Данные: %d байт | В пачке: %d | Пачек: %s | Интервал: %.3f сек\n\n", + data_size_min, pings_per_burst, (bursts == 0 ? "∞" : "ограничено"), interval_sec); + } else { + printf("Данные: %d:%d байт (случайно) | В пачке: %d | Пачек: %s | Интервал: %.3f сек\n\n", + data_size_min, data_size_max, pings_per_burst, (bursts == 0 ? "∞" : "ограничено"), interval_sec); + } // Выделяем память один раз struct timeval *send_times = malloc(pings_per_burst * sizeof(struct timeval)); @@ -150,10 +181,11 @@ int main(int argc, char **argv) { // === Отправляем пачку максимально быстро === int sent = 0; for (int i = 0; i < pings_per_burst; i++) { + int pkt_size = data_size_min + (rand() % (data_size_max - data_size_min + 1)); uint16_t seq = ++global_seq; seq_list[i] = seq; gettimeofday(&send_times[i], NULL); - send_one_ping(sock, &dest, data_size, ident, seq); + send_one_ping(sock, &dest, pkt_size, ident, seq); sent++; }