В процессе разработки
This commit is contained in:
13
makefile
13
makefile
@@ -20,7 +20,7 @@ PLUTO_B ?= 192.168.3.1
|
||||
BIN := build
|
||||
SRC := src
|
||||
|
||||
all: $(BIN)/transmitter $(BIN)/receiver $(BIN)/ofdm_bench $(BIN)/udp_gw
|
||||
all: $(BIN)/transmitter $(BIN)/receiver $(BIN)/ofdm_bench $(BIN)/udp_gw $(BIN)/udp_probe
|
||||
|
||||
$(BIN):
|
||||
mkdir -p $(BIN)
|
||||
@@ -39,6 +39,10 @@ $(BIN)/ofdm_bench: $(SRC)/ofdm_bench.c | $(BIN)
|
||||
$(BIN)/udp_gw: $(SRC)/udp_gw.c | $(BIN)
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
# Тестовый генератор/приёмник UDP для бринг-апа udp_gw (busybox без nc/socat).
|
||||
$(BIN)/udp_probe: $(SRC)/udp_probe.c | $(BIN)
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
# Оффлайн-самопроверка GF(256) — нативная сборка (gcc хоста, напр. RPi/aarch64)
|
||||
$(BIN)/gftest: $(SRC)/group_fec.c $(SRC)/group_fec.h | $(BIN)
|
||||
cc -O2 -Wall -DGF_SELFTEST $(SRC)/group_fec.c -o $@
|
||||
@@ -51,6 +55,7 @@ tx: $(BIN)/transmitter
|
||||
rx: $(BIN)/receiver
|
||||
bench: $(BIN)/ofdm_bench
|
||||
gw: $(BIN)/udp_gw
|
||||
probe: $(BIN)/udp_probe
|
||||
gftest: $(BIN)/gftest
|
||||
$(BIN)/gftest
|
||||
gwtest: $(BIN)/gwtest
|
||||
@@ -76,8 +81,8 @@ clean:
|
||||
rm -rf $(BIN)
|
||||
|
||||
help:
|
||||
@echo "make [all] — собрать transmitter, receiver, ofdm_bench, udp_gw (static armhf)"
|
||||
@echo "make tx|rx|bench|gw — собрать отдельную цель"
|
||||
@echo "make [all] — собрать transmitter, receiver, ofdm_bench, udp_gw, udp_probe (static armhf)"
|
||||
@echo "make tx|rx|bench|gw|probe — собрать отдельную цель"
|
||||
@echo "make gftest — оффлайн-самопроверка group-FEC (нативный cc)"
|
||||
@echo "make gwtest — оффлайн-самопроверка парсера udp_gw (нативный cc)"
|
||||
@echo "make deploy — залить бинарники на обе платы (scp -O в /tmp)"
|
||||
@@ -86,4 +91,4 @@ help:
|
||||
@echo "make clean — удалить build/"
|
||||
@echo "переменные: PLUTO_A=$(PLUTO_A) PLUTO_B=$(PLUTO_B) XARM=$(XARM)"
|
||||
|
||||
.PHONY: all tx rx bench gw gftest gwtest deploy test check-env clean help
|
||||
.PHONY: all tx rx bench gw probe gftest gwtest deploy test check-env clean help
|
||||
@@ -17,7 +17,7 @@ else
|
||||
SCP=(scp -O)
|
||||
fi
|
||||
|
||||
FILES=(build/transmitter build/receiver build/ofdm_bench build/udp_gw)
|
||||
FILES=(build/transmitter build/receiver build/ofdm_bench build/udp_gw build/udp_probe)
|
||||
TARGETS=()
|
||||
|
||||
case "${1:-}" in
|
||||
|
||||
137
src/udp_probe.c
Normal file
137
src/udp_probe.c
Normal file
@@ -0,0 +1,137 @@
|
||||
// udp_probe.c — минимальный генератор/приёмник UDP для тестирования udp_gw
|
||||
// (roadmap §10 п.8). НЕ часть штатного тракта — только инструмент бринг-апа
|
||||
// и E2E-тестов: busybox-rootfs Pluto без nc/socat (проверено на железе),
|
||||
// а Python на хосте запрещён (CLAUDE.md правило №1) — значит свой маленький
|
||||
// C-бинарь, тот же кросс-компилятор, что и весь тракт.
|
||||
//
|
||||
// send: udp_probe send ip:port РАЗМЕР_ЧАНКА [ПАУЗА_МС] stdin -> UDP
|
||||
// recv: udp_probe recv ПОРТ ТАЙМАУТ_ПРОСТОЯ_С UDP -> stdout
|
||||
//
|
||||
// recv сам завершается после ТАЙМАУТ_ПРОСТОЯ_С секунд без новых датаграмм
|
||||
// (busybox без `timeout` — та же причина, что и в scripts/test_file_link.sh).
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
static volatile sig_atomic_t g_stop = 0;
|
||||
static void on_signal(int s) { (void)s; g_stop = 1; }
|
||||
|
||||
static int parse_hostport(const char *hostport, struct sockaddr_in *out) {
|
||||
const char *colon = strrchr(hostport, ':');
|
||||
if (!colon || colon == hostport) return -1;
|
||||
char host[64];
|
||||
size_t hlen = (size_t)(colon - hostport);
|
||||
if (hlen >= sizeof(host)) return -1;
|
||||
memcpy(host, hostport, hlen); host[hlen] = 0;
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->sin_family = AF_INET;
|
||||
out->sin_port = htons((uint16_t)atoi(colon + 1));
|
||||
return inet_pton(AF_INET, host, &out->sin_addr) == 1 ? 0 : -1;
|
||||
}
|
||||
|
||||
// stdin читается чанками по `chunk` байт, каждый чанк — одна UDP-датаграмма.
|
||||
// Короткий чанк (fread < chunk) — признак EOF (как fread в transmitter.c).
|
||||
static int mode_send(const char *hostport, int chunk, int pause_ms) {
|
||||
struct sockaddr_in dst;
|
||||
if (parse_hostport(hostport, &dst) != 0) {
|
||||
fprintf(stderr, "udp_probe: не разобрал '%s' (ожидался ip:port)\n", hostport);
|
||||
return 1;
|
||||
}
|
||||
if (chunk <= 0 || chunk > 65000) {
|
||||
fprintf(stderr, "udp_probe: размер чанка вне диапазона\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0) { perror("socket"); return 1; }
|
||||
|
||||
uint8_t *buf = malloc((size_t)chunk);
|
||||
uint64_t sent = 0, bytes = 0;
|
||||
for (;;) {
|
||||
size_t n = fread(buf, 1, (size_t)chunk, stdin);
|
||||
if (n == 0) break;
|
||||
if (sendto(fd, buf, n, 0, (struct sockaddr *)&dst, sizeof(dst)) < 0)
|
||||
fprintf(stderr, "udp_probe: sendto: %s\n", strerror(errno));
|
||||
else { sent++; bytes += n; }
|
||||
if (n < (size_t)chunk) break; // короткое чтение = EOF
|
||||
if (pause_ms > 0) usleep((useconds_t)pause_ms * 1000);
|
||||
}
|
||||
fprintf(stderr, "udp_probe send: отправлено %llu датаграмм, %llu байт\n",
|
||||
(unsigned long long)sent, (unsigned long long)bytes);
|
||||
free(buf);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Дампит содержимое каждой полученной датаграммы в stdout как есть (порядок
|
||||
// прихода). Для сверки с исходником через md5sum, как в остальных e2e-тестах
|
||||
// репозитория — если ничего не потерялось и не переупорядочилось, конкатенация
|
||||
// совпадёт байт-в-байт.
|
||||
static int mode_recv(int port, int idle_timeout_s) {
|
||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0) { perror("socket"); return 1; }
|
||||
int one = 1;
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_port = htons((uint16_t)port);
|
||||
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
perror("bind"); close(fd); return 1;
|
||||
}
|
||||
|
||||
signal(SIGINT, on_signal);
|
||||
signal(SIGTERM, on_signal);
|
||||
|
||||
uint8_t buf[65536];
|
||||
uint64_t recvd = 0, bytes = 0;
|
||||
time_t last_rx = time(NULL);
|
||||
while (!g_stop) {
|
||||
fd_set rfds; FD_ZERO(&rfds); FD_SET(fd, &rfds);
|
||||
struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
|
||||
int sel = select(fd + 1, &rfds, NULL, NULL, &tv);
|
||||
if (sel < 0) { if (errno == EINTR) continue; break; }
|
||||
if (sel > 0 && FD_ISSET(fd, &rfds)) {
|
||||
ssize_t n = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
|
||||
if (n < 0) { if (errno == EINTR) continue; break; }
|
||||
fwrite(buf, 1, (size_t)n, stdout);
|
||||
fflush(stdout);
|
||||
recvd++; bytes += (uint64_t)n;
|
||||
last_rx = time(NULL);
|
||||
}
|
||||
if (idle_timeout_s > 0 && time(NULL) - last_rx >= idle_timeout_s) break;
|
||||
}
|
||||
fprintf(stderr, "udp_probe recv: получено %llu датаграмм, %llu байт\n",
|
||||
(unsigned long long)recvd, (unsigned long long)bytes);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage(const char *prog) {
|
||||
fprintf(stderr, "udp_probe — тестовый генератор/приёмник UDP (busybox без nc)\n\n");
|
||||
fprintf(stderr, " %s send ip:port РАЗМЕР_ЧАНКА [ПАУЗА_МС] stdin -> UDP\n", prog);
|
||||
fprintf(stderr, " %s recv ПОРТ ТАЙМАУТ_ПРОСТОЯ_С UDP -> stdout\n", prog);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc >= 4 && !strcmp(argv[1], "send"))
|
||||
return mode_send(argv[2], atoi(argv[3]), argc >= 5 ? atoi(argv[4]) : 0);
|
||||
if (argc >= 4 && !strcmp(argv[1], "recv"))
|
||||
return mode_recv(atoi(argv[2]), atoi(argv[3]));
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user