diff --git a/tests/bench_timeout_heap b/tests/bench_timeout_heap index dab6341f..55251448 100755 Binary files a/tests/bench_timeout_heap and b/tests/bench_timeout_heap differ diff --git a/tests/bench_uasync_timeouts b/tests/bench_uasync_timeouts index 56796a41..6dc7e019 100755 Binary files a/tests/bench_uasync_timeouts and b/tests/bench_uasync_timeouts differ diff --git a/tools/bping/bping.c b/tools/bping/bping.c index 32de0d75..c82a2ad9 100644 --- a/tools/bping/bping.c +++ b/tools/bping/bping.c @@ -1,103 +1,234 @@ #include #include +#include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include -#include #include +#include + +#define MAX_DATA_SIZE 65500 +#define MAX_PINGS_PER_BURST 10000 +#define RECV_TIMEOUT_SEC 5 -static char* host = NULL; -static int size = 56; +static char *host = NULL; +static int data_size = 56; static int pings_per_burst = 10; static int bursts = 0; // 0 = бесконечно -static double interval = 1.0; +static double interval_sec = 1.0; + +unsigned short in_cksum(unsigned short *addr, int len) { + int nleft = len; + unsigned short *w = addr; + unsigned int sum = 0; + unsigned short answer = 0; + + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + if (nleft == 1) { + *(unsigned char *)(&answer) = *(unsigned char *)w; + sum += answer; + } + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + answer = ~sum; + return answer; +} + +void send_one_ping(int sock, const struct sockaddr_in *to, int datalen, uint16_t id, uint16_t seq) { + char packet[sizeof(struct icmphdr) + MAX_DATA_SIZE]; + struct icmphdr *icmp = (struct icmphdr *)packet; + + icmp->type = ICMP_ECHO; + icmp->code = 0; + icmp->un.echo.id = htons(id); + icmp->un.echo.sequence = htons(seq); + memset(packet + sizeof(struct icmphdr), 0x61, datalen); // 'a' как в ping + + icmp->checksum = 0; + icmp->checksum = in_cksum((unsigned short *)packet, sizeof(struct icmphdr) + datalen); + + sendto(sock, packet, sizeof(struct icmphdr) + datalen, 0, + (struct sockaddr *)to, sizeof(*to)); +} void usage() { - printf("Использование: burstping [-s размер] [-p посылок] [-b пачек] [-i интервал] host\n\n"); - printf(" -s размер Размер данных в ICMP-пакете (по умолчанию 56)\n"); - printf(" -p посылок Количество пингов в одной пачке (по умолчанию 10)\n"); - printf(" -b пачек Количество пачек (0 = бесконечно, по умолчанию 0)\n"); - printf(" -i интервал Интервал между пачками в секундах (можно 0.3, 0.05 и т.д., по умолчанию 1.0)\n\n"); + printf("Использование: bping [-s размер] [-p посылок] [-b пачек] [-i интервал] host\n\n"); + printf(" -s размер данные в байтах (по умолчанию 56, макс ~65k)\n"); + printf(" -p посылок пингов в одной пачке (по умолчанию 10, макс 10000)\n"); + printf(" -b пачек количество пачек (0 = ∞, по умолчанию 0)\n"); + printf(" -i интервал между пачками в секундах (0.001, 0.05 и т.д., по умолчанию 1.0)\n\n"); + printf("Внимание: требует cap_net_raw или root!\n"); printf("Примеры:\n"); - printf(" burstping 8.8.8.8\n"); - printf(" burstping -s 1472 -p 100 -i 0.2 1.1.1.1\n"); - printf(" burstping -p 50 -b 30 -i 1 google.com\n"); - printf(" burstping -p 200 -i 0.05 -b 0 10.0.0.1 # бесконечный burst\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"); exit(1); } void sigint_handler(int sig) { - printf("\n\n⛔ burstping остановлен по Ctrl+C\n"); + printf("\n\n⛔ bping остановлен\n"); exit(0); } -int main(int argc, char** argv) { +int main(int argc, char **argv) { int opt; while ((opt = getopt(argc, argv, "s:p:b:i:h")) != -1) { switch (opt) { - case 's': size = atoi(optarg); break; + case 's': data_size = atoi(optarg); break; case 'p': pings_per_burst = atoi(optarg); break; case 'b': bursts = atoi(optarg); break; - case 'i': interval = strtod(optarg, NULL); break; + case 'i': interval_sec = strtod(optarg, NULL); break; case 'h': case '?': usage(); } } - if (optind >= argc) usage(); host = argv[optind]; - if (size < 0 || pings_per_burst < 1) usage(); + if (data_size < 0 || data_size > MAX_DATA_SIZE || pings_per_burst < 1 || pings_per_burst > MAX_PINGS_PER_BURST) { + usage(); + } signal(SIGINT, sigint_handler); - printf("🚀 burstping → %s\n", host); - printf("Пакет: %d байт | Посылок в пачке: %d | Пачек: %s | Интервал: %.3f сек\n\n", - size, pings_per_burst, (bursts == 0 ? "∞" : "ограничено"), interval); + // Создаём raw сокет + int sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (sock < 0) { + if (errno == EPERM) + fprintf(stderr, "Ошибка: нужен root или cap_net_raw+ep\n"); + else + perror("socket"); + exit(1); + } + + // Разрешаем hostname + struct sockaddr_in dest; + memset(&dest, 0, sizeof(dest)); + dest.sin_family = AF_INET; + + if (inet_pton(AF_INET, host, &dest.sin_addr) <= 0) { + struct hostent *he = gethostbyname(host); + if (!he) { + fprintf(stderr, "Не могу разрешить %s\n", host); + close(sock); + exit(1); + } + memcpy(&dest.sin_addr, he->h_addr, he->h_length); + } + + uint16_t ident = getpid() & 0xFFFF; + 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); + + // Выделяем память один раз + struct timeval *send_times = malloc(pings_per_burst * sizeof(struct timeval)); + uint16_t *seq_list = malloc(pings_per_burst * sizeof(uint16_t)); + if (!send_times || !seq_list) { + fprintf(stderr, "malloc error\n"); + close(sock); + exit(1); + } int burst_num = 0; while (1) { burst_num++; printf("=== Пачка #%d ===\n", burst_num); - // Запускаем пачку параллельно - int launched = 0; + // === Отправляем пачку максимально быстро === + int sent = 0; for (int i = 0; i < pings_per_burst; i++) { - pid_t pid = fork(); - if (pid == 0) { - // Дочерний процесс - char sz[16], cnt[4]; - snprintf(sz, sizeof(sz), "%d", size); - snprintf(cnt, sizeof(cnt), "1"); - execlp("ping", "ping", "-c", cnt, "-s", sz, "-W", "2", "-n", host, (char*)NULL); - perror("execlp ping"); - exit(1); - } else if (pid > 0) { - launched++; - } else { - perror("fork"); - } + uint16_t seq = ++global_seq; + seq_list[i] = seq; + gettimeofday(&send_times[i], NULL); + send_one_ping(sock, &dest, data_size, ident, seq); + sent++; } - // Ждём завершения всех пингов этой пачки - for (int i = 0; i < launched; i++) { - wait(NULL); - } + // === Собираем ответы этой пачки === + int received = 0; + time_t collect_start = time(NULL); + char recv_buf[2048]; + + while (received < sent && (time(NULL) - collect_start < RECV_TIMEOUT_SEC)) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + struct timeval tv = {1, 0}; // 1 секунда на select + int ret = select(sock + 1, &fds, NULL, NULL, &tv); - if (bursts > 0 && burst_num >= bursts) { - break; + if (ret > 0 && FD_ISSET(sock, &fds)) { + struct sockaddr_in from; + socklen_t from_len = sizeof(from); + int len = recvfrom(sock, recv_buf, sizeof(recv_buf), 0, + (struct sockaddr *)&from, &from_len); + + if (len > 28) { // минимум IP + ICMP + struct iphdr *ip = (struct iphdr *)recv_buf; + int ip_hlen = ip->ihl * 4; + if (len < ip_hlen + sizeof(struct icmphdr)) continue; + + struct icmphdr *icmp = (struct icmphdr *)(recv_buf + ip_hlen); + if (icmp->type == ICMP_ECHOREPLY) { + uint16_t recv_id = ntohs(icmp->un.echo.id); + uint16_t recv_seq = ntohs(icmp->un.echo.sequence); + + if (recv_id == ident) { + // ищем в текущей пачке + for (int j = 0; j < sent; j++) { + if (seq_list[j] == recv_seq) { + struct timeval now; + gettimeofday(&now, NULL); + double rtt = (now.tv_sec - send_times[j].tv_sec) * 1000.0 + + (now.tv_usec - send_times[j].tv_usec) / 1000.0; + + char from_ip[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &from.sin_addr, from_ip, sizeof(from_ip)); + + printf("%d bytes from %s: icmp_seq=%d ttl=%d time=%.1f ms\n", + len - ip_hlen, from_ip, recv_seq, ip->ttl, rtt); + + received++; + seq_list[j] = 0; // пометили как обработано + break; + } + } + } + } + } + } } - // Интервал между пачками - if (interval > 0.0) { + double loss = sent ? 100.0 * (sent - received) / sent : 0; + printf("--- %d transmitted, %d received, %.0f%% loss ---\n\n", sent, received, loss); + + if (bursts > 0 && burst_num >= bursts) break; + + // интервал между пачками + if (interval_sec > 0) { struct timespec ts; - ts.tv_sec = (time_t)interval; - ts.tv_nsec = (long)((interval - ts.tv_sec) * 1e9); + ts.tv_sec = (time_t)interval_sec; + ts.tv_nsec = (long)((interval_sec - ts.tv_sec) * 1e9); nanosleep(&ts, NULL); } } - printf("✅ burstping завершён.\n"); + free(send_times); + free(seq_list); + close(sock); + printf("✅ bping завершён.\n"); return 0; }