3 changed files with 182 additions and 51 deletions
Binary file not shown.
Binary file not shown.
@ -1,103 +1,234 @@ |
|||||||
#include <stdio.h> |
#include <stdio.h> |
||||||
#include <stdlib.h> |
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
#include <unistd.h> |
#include <unistd.h> |
||||||
#include <sys/wait.h> |
#include <sys/socket.h> |
||||||
|
#include <sys/types.h> |
||||||
|
#include <sys/time.h> |
||||||
|
#include <netinet/in.h> |
||||||
|
#include <netinet/ip.h> |
||||||
|
#include <netinet/ip_icmp.h> |
||||||
|
#include <arpa/inet.h> |
||||||
|
#include <netdb.h> |
||||||
#include <signal.h> |
#include <signal.h> |
||||||
#include <time.h> |
|
||||||
#include <string.h> |
|
||||||
#include <errno.h> |
#include <errno.h> |
||||||
|
#include <time.h> |
||||||
|
|
||||||
|
#define MAX_DATA_SIZE 65500 |
||||||
|
#define MAX_PINGS_PER_BURST 10000 |
||||||
|
#define RECV_TIMEOUT_SEC 5 |
||||||
|
|
||||||
static char* host = NULL; |
static char *host = NULL; |
||||||
static int size = 56; |
static int data_size = 56; |
||||||
static int pings_per_burst = 10; |
static int pings_per_burst = 10; |
||||||
static int bursts = 0; // 0 = бесконечно
|
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() { |
void usage() { |
||||||
printf("Использование: burstping [-s размер] [-p посылок] [-b пачек] [-i интервал] host\n\n"); |
printf("Использование: bping [-s размер] [-p посылок] [-b пачек] [-i интервал] host\n\n"); |
||||||
printf(" -s размер Размер данных в ICMP-пакете (по умолчанию 56)\n"); |
printf(" -s размер данные в байтах (по умолчанию 56, макс ~65k)\n"); |
||||||
printf(" -p посылок Количество пингов в одной пачке (по умолчанию 10)\n"); |
printf(" -p посылок пингов в одной пачке (по умолчанию 10, макс 10000)\n"); |
||||||
printf(" -b пачек Количество пачек (0 = бесконечно, по умолчанию 0)\n"); |
printf(" -b пачек количество пачек (0 = ∞, по умолчанию 0)\n"); |
||||||
printf(" -i интервал Интервал между пачками в секундах (можно 0.3, 0.05 и т.д., по умолчанию 1.0)\n\n"); |
printf(" -i интервал между пачками в секундах (0.001, 0.05 и т.д., по умолчанию 1.0)\n\n"); |
||||||
|
printf("Внимание: требует cap_net_raw или root!\n"); |
||||||
printf("Примеры:\n"); |
printf("Примеры:\n"); |
||||||
printf(" burstping 8.8.8.8\n"); |
printf(" sudo bping 8.8.8.8\n"); |
||||||
printf(" burstping -s 1472 -p 100 -i 0.2 1.1.1.1\n"); |
printf(" bping -s 1472 -p 200 -i 0.05 1.1.1.1\n"); |
||||||
printf(" burstping -p 50 -b 30 -i 1 google.com\n"); |
printf(" bping -p 500 -b 0 -i 0.01 192.168.1.1\n"); |
||||||
printf(" burstping -p 200 -i 0.05 -b 0 10.0.0.1 # бесконечный burst\n"); |
|
||||||
exit(1); |
exit(1); |
||||||
} |
} |
||||||
|
|
||||||
void sigint_handler(int sig) { |
void sigint_handler(int sig) { |
||||||
printf("\n\n⛔ burstping остановлен по Ctrl+C\n"); |
printf("\n\n⛔ bping остановлен\n"); |
||||||
exit(0); |
exit(0); |
||||||
} |
} |
||||||
|
|
||||||
int main(int argc, char** argv) { |
int main(int argc, char **argv) { |
||||||
int opt; |
int opt; |
||||||
while ((opt = getopt(argc, argv, "s:p:b:i:h")) != -1) { |
while ((opt = getopt(argc, argv, "s:p:b:i:h")) != -1) { |
||||||
switch (opt) { |
switch (opt) { |
||||||
case 's': size = atoi(optarg); break; |
case 's': data_size = atoi(optarg); break; |
||||||
case 'p': pings_per_burst = atoi(optarg); break; |
case 'p': pings_per_burst = atoi(optarg); break; |
||||||
case 'b': bursts = 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 'h': |
||||||
case '?': usage(); |
case '?': usage(); |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
if (optind >= argc) usage(); |
if (optind >= argc) usage(); |
||||||
host = argv[optind]; |
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); |
signal(SIGINT, sigint_handler); |
||||||
|
|
||||||
printf("🚀 burstping → %s\n", host); |
// Создаём raw сокет
|
||||||
printf("Пакет: %d байт | Посылок в пачке: %d | Пачек: %s | Интервал: %.3f сек\n\n", |
int sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); |
||||||
size, pings_per_burst, (bursts == 0 ? "∞" : "ограничено"), interval); |
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; |
int burst_num = 0; |
||||||
while (1) { |
while (1) { |
||||||
burst_num++; |
burst_num++; |
||||||
printf("=== Пачка #%d ===\n", burst_num); |
printf("=== Пачка #%d ===\n", burst_num); |
||||||
|
|
||||||
// Запускаем пачку параллельно
|
// === Отправляем пачку максимально быстро ===
|
||||||
int launched = 0; |
int sent = 0; |
||||||
for (int i = 0; i < pings_per_burst; i++) { |
for (int i = 0; i < pings_per_burst; i++) { |
||||||
pid_t pid = fork(); |
uint16_t seq = ++global_seq; |
||||||
if (pid == 0) { |
seq_list[i] = seq; |
||||||
// Дочерний процесс
|
gettimeofday(&send_times[i], NULL); |
||||||
char sz[16], cnt[4]; |
send_one_ping(sock, &dest, data_size, ident, seq); |
||||||
snprintf(sz, sizeof(sz), "%d", size); |
sent++; |
||||||
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"); |
|
||||||
} |
|
||||||
} |
} |
||||||
|
|
||||||
// Ждём завершения всех пингов этой пачки
|
// === Собираем ответы этой пачки ===
|
||||||
for (int i = 0; i < launched; i++) { |
int received = 0; |
||||||
wait(NULL); |
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 (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; |
||||||
|
|
||||||
if (bursts > 0 && burst_num >= bursts) { |
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; |
break; |
||||||
} |
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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 > 0.0) { |
if (interval_sec > 0) { |
||||||
struct timespec ts; |
struct timespec ts; |
||||||
ts.tv_sec = (time_t)interval; |
ts.tv_sec = (time_t)interval_sec; |
||||||
ts.tv_nsec = (long)((interval - ts.tv_sec) * 1e9); |
ts.tv_nsec = (long)((interval_sec - ts.tv_sec) * 1e9); |
||||||
nanosleep(&ts, NULL); |
nanosleep(&ts, NULL); |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
printf("✅ burstping завершён.\n"); |
free(send_times); |
||||||
|
free(seq_list); |
||||||
|
close(sock); |
||||||
|
printf("✅ bping завершён.\n"); |
||||||
return 0; |
return 0; |
||||||
} |
} |
||||||
|
|||||||
Loading…
Reference in new issue