diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 60ccbd84..176bfa41 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -272,6 +272,24 @@ jobs: test/health.awk < log > json scp -o "StrictHostKeyChecking=no" json "root@176.9.217.245:/data/downloads/health/bsd_${{ matrix.cc }}_$(date +"%Y%m%d").json" + bsdbe: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: [bsd_s390] + name: bsd be ${{ matrix.target }} + steps: + - uses: actions/checkout@v5 + with: { fetch-depth: 2 } + - run: | + sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + source ./test/setup_ga_network.sh + make -C test port_tap_bridge + test/port_tap_bridge & + sleep 2 + make -C test/bsd ${{ matrix.target }} + s390: runs-on: ubuntu-latest strategy: diff --git a/test/bsd/Makefile b/test/bsd/Makefile index 5c1cf693..7be18f2a 100644 --- a/test/bsd/Makefile +++ b/test/bsd/Makefile @@ -13,6 +13,7 @@ else C_WARN ?= -Wno-deprecated endif +.PHONY: all bsd_test bsd_client shm_tap_bridge shm_port_bridge bsd_s390 clean SOURCES = bsd_shm_test.c ../../mongoose.c @@ -23,26 +24,42 @@ SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c CFLAGS += -IFreeRTOS-Kernel/include CFLAGS += -IFreeRTOS-Kernel/portable/ThirdParty/GCC/Posix -Wno-conversion -Wno-undef +DOCKER_BIN ?= docker +CWD ?= $(realpath $(CURDIR)) +ROOT_DIR = $(realpath $(CWD)/../..) +ENV ?= -e Tmp=. -e WINEDEBUG=-all +DOCKER ?= $(DOCKER_BIN) run --platform linux/amd64 --net=host --rm $(ENV) -v $(ROOT_DIR):$(ROOT_DIR) -w $(CWD) + # Mongoose options are defined in mongoose_config.h, this forces loading and skips OS detection CFLAGS_MONGOOSE += -DMG_ARCH=MG_ARCH_CUSTOM -all: shm_tap_bridge bsd_client FreeRTOS-Kernel bsd_test +all bsd: shm_tap_bridge bsd_client FreeRTOS-Kernel bsd_test ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_test $(ARGS) # requires shm_tap_bridge to be running -bsd_test: $(SOURCES) bsd_x_test.c ../../mongoose.h +bsd_test: $(SOURCES) bsd_x_test.c ../../mongoose.h shm_queue.h $(CC) $(SOURCES) $(wildcard FreeRTOS-Kernel/*.c) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) -o $@ bsd_client: bsd_client.c $(CC) bsd_client.c -pthread -o $@ # bridge shm queue <--> TAP interface -shm_tap_bridge: shm_tap_bridge.c +shm_tap_bridge: shm_tap_bridge.c shm_queue.h $(CC) shm_tap_bridge.c -pthread -o $@ +# requires test/port_tap_bridge to be running +bsd_s390: ASAN= +bsd_s390: CC = $(DOCKER) mdashnet/s390 cc +bsd_s390: RUN = $(DOCKER) --init mdashnet/s390 +bsd_s390: shm_port_bridge bsd_client FreeRTOS-Kernel bsd_test + $(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc' + +shm_port_bridge: shm_port_bridge.c shm_queue.h + $(CC) shm_port_bridge.c $(CFLAGS) -pthread -o $@ + clean: # Cleanup. Delete built program and all build artifacts - rm -rf bsd_test bsd_client *.o *.obj FreeRTOS-Kernel + rm -rf bsd_test bsd_client shm_port_bridge *.o *.obj FreeRTOS-Kernel FreeRTOS-Kernel: # FreeRTOS sources git clone -q -c advice.detachedHead=false --depth 1 -b V11.2.0 https://github.com/FreeRTOS/FreeRTOS-Kernel $@ diff --git a/test/bsd/bsd_shm_test.c b/test/bsd/bsd_shm_test.c index 474f1bd5..e3214466 100644 --- a/test/bsd/bsd_shm_test.c +++ b/test/bsd/bsd_shm_test.c @@ -1,36 +1,15 @@ +#include #include -#include #include #include #include "mongoose.h" +#include "shm_queue.h" #ifndef O_RDWR #define O_RDWR 02 #endif -#define FRAME_SIZE 1540 -#define FRAME_QUEUE_DEPTH 8 -#define SHM_NAME "/mongoose-shm" - -struct shm_frame { - size_t len; - uint8_t buf[FRAME_SIZE]; -}; - -struct shm_queue { - sem_t full; - sem_t empty; - size_t head; - size_t tail; - struct shm_frame frames[FRAME_QUEUE_DEPTH]; -}; - -struct shm_link { - struct shm_queue to_tap; - struct shm_queue from_tap; -}; - struct shm_driver_data { const char *name; FILE *fp; @@ -48,14 +27,21 @@ static bool shm_init(struct mg_tcpip_if *ifp) { int fd; fd = shm_open(d->name, O_RDWR, 0600); - if (fd < 0) return false; + if (fd < 0) { + printf("shm_open failed: %d\n", errno); + return false; + } d->fp = fdopen(fd, "r+"); - if (d->fp == NULL) return false; + if (d->fp == NULL) { + printf("fdopen failed: %d\n", errno); + return false; + } d->shm = (struct shm_link *) mmap(NULL, sizeof(*d->shm), PROT_READ | PROT_WRITE, MAP_SHARED, fileno(d->fp), 0); if (d->shm == MAP_FAILED) { + printf("mmap failed: %d\n", errno); fclose(d->fp); d->fp = NULL; d->shm = NULL; @@ -67,39 +53,17 @@ static bool shm_init(struct mg_tcpip_if *ifp) { static size_t shm_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) { struct shm_driver_data *d = (struct shm_driver_data *) ifp->driver_data; - struct shm_queue *q = &d->shm->to_tap; - struct shm_frame *f; - if (len > sizeof(q->frames[0].buf)) return 0; - if (sem_trywait(&q->empty) != 0) { + if (!shm_queue_push(&d->shm->to_tap, buf, len)) { printf("\nFRAME LOST\n"); return 0; } - f = &q->frames[q->tail]; - memcpy(f->buf, buf, len); - f->len = len; - q->tail = (q->tail + 1) % FRAME_QUEUE_DEPTH; - sem_post(&q->full); return len; } static size_t shm_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) { struct shm_driver_data *d = (struct shm_driver_data *) ifp->driver_data; - struct shm_queue *q = &d->shm->from_tap; - struct shm_frame *f; - size_t n; - - if (sem_trywait(&q->full) != 0) return 0; - f = &q->frames[q->head]; - n = f->len; - q->head = (q->head + 1) % FRAME_QUEUE_DEPTH; - if (n > len) { - sem_post(&q->empty); - return 0; - } - memcpy(buf, f->buf, n); - sem_post(&q->empty); - return n; + return shm_queue_pop(&d->shm->from_tap, buf, len); } static bool shm_poll(struct mg_tcpip_if *ifp, bool s1) { @@ -140,7 +104,7 @@ int main(void) { // Setup interface const char *mac = "02:00:01:02:03:78"; // MAC address - printf("Opened SHM interface\n"); + printf("Opening SHM interface\n"); usleep(200000); // 200 ms result = bsd_x_test(&shm_driver, &shm_data, mac, debug_level); diff --git a/test/bsd/shm_port_bridge.c b/test/bsd/shm_port_bridge.c new file mode 100644 index 00000000..c7757390 --- /dev/null +++ b/test/bsd/shm_port_bridge.c @@ -0,0 +1,138 @@ +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "shm_queue.h" + +static volatile sig_atomic_t s_signo; + +static void signal_handler(int signo) { + s_signo = signo; +} + +static int open_udp_socket(void) { + const uint16_t port = 0xAA55; // Local UDP port + const uint16_t tap_port = 0x55AA; // Host port_tap_bridge UDP port + struct sockaddr_in port_addr, tap_addr; + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + + memset(&port_addr, 0, sizeof(port_addr)); + port_addr.sin_family = AF_INET; + port_addr.sin_addr.s_addr = htonl(INADDR_ANY); + port_addr.sin_port = htons(port); + if (bind(fd, (struct sockaddr *) &port_addr, sizeof(port_addr)) < 0) { + perror("bind"); + close(fd); + return -1; + } + printf("Opened UDP socket: 127.0.0.1:%u (0x%04x)\n", (unsigned) port, + (unsigned) port); + + memset(&tap_addr, 0, sizeof(tap_addr)); + tap_addr.sin_family = AF_INET; + tap_addr.sin_addr.s_addr = htonl(0x7f000001U); // 127.0.0.1 + tap_addr.sin_port = htons(tap_port); + if (connect(fd, (struct sockaddr *) &tap_addr, sizeof(tap_addr)) < 0) { + perror("connect"); + close(fd); + return -1; + } + printf("Connected to TAP UDP socket: 127.0.0.1:%u (0x%04x)\n", + (unsigned) tap_port, (unsigned) tap_port); + + return fd; +} + +static struct shm_link *open_shm(int *shmfd) { + struct shm_link *shm; + + shm_unlink(SHM_NAME); + *shmfd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0600); + if (*shmfd < 0) { + perror("shm_open"); + return NULL; + } + if (ftruncate(*shmfd, (off_t) sizeof(*shm)) != 0) { + perror("ftruncate"); + close(*shmfd); + shm_unlink(SHM_NAME); + return NULL; + } + shm = (struct shm_link *) mmap(NULL, sizeof(*shm), PROT_READ | PROT_WRITE, + MAP_SHARED, *shmfd, 0); + if (shm == MAP_FAILED) { + perror("mmap"); + close(*shmfd); + shm_unlink(SHM_NAME); + return NULL; + } + + memset(shm, 0, sizeof(*shm)); + printf("Opened shared memory: %s\n", SHM_NAME); + + return shm; +} + +int main(void) { + int sockfd, shmfd = -1; + struct shm_link *shm; + + sockfd = open_udp_socket(); + if (sockfd < 0) return EXIT_FAILURE; + shm = open_shm(&shmfd); + if (shm == NULL) { + close(sockfd); + return EXIT_FAILURE; + } + + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + while (s_signo == 0) { + struct pollfd pfd = {sockfd, (short) (POLLIN | POLLPRI), 0}; + int result = poll(&pfd, 1, 1); + if (result < 0 && errno != EINTR) { + perror("poll"); + break; + } + if (result > 0 && (pfd.revents & (POLLIN | POLLPRI))) { + uint8_t buf[FRAME_SIZE]; + ssize_t nread = recv(sockfd, buf, sizeof(buf), 0); + if (nread > 0 && + !shm_queue_push(&shm->from_tap, buf, (size_t) nread)) { + printf("FRAME LOST: port -> SHM\n"); + } + } + { + uint8_t buf[FRAME_SIZE]; + size_t n = shm_queue_pop(&shm->to_tap, buf, sizeof(buf)); + if (n > 0) { + if (send(sockfd, buf, n, 0) < 0) perror("send"); + } + } + } + + munmap(shm, sizeof(*shm)); + close(shmfd); + shm_unlink(SHM_NAME); + close(sockfd); + return EXIT_SUCCESS; +} diff --git a/test/bsd/shm_queue.h b/test/bsd/shm_queue.h new file mode 100644 index 00000000..c5925f2d --- /dev/null +++ b/test/bsd/shm_queue.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#include + +#define FRAME_SIZE 1540 +#define FRAME_QUEUE_DEPTH 32 +#define SHM_NAME "/mongoose-shm" + +// Keep this SHM ABI semaphore-free. Some S390/Linux headers pull fcntl/socket +// declarations through , colliding with Mongoose BSD prototypes. +// Each direction is single-producer/single-consumer and polled, so a simple +// ring is enough for this test path. +struct shm_frame { + size_t len; + uint8_t buf[FRAME_SIZE]; +}; + +struct shm_queue { + volatile size_t head; + volatile size_t tail; + struct shm_frame frames[FRAME_QUEUE_DEPTH]; +}; + +struct shm_link { + struct shm_queue to_tap; + struct shm_queue from_tap; +}; + +static inline bool shm_queue_push(struct shm_queue *q, const void *buf, + size_t len) { + size_t tail = q->tail, next = (tail + 1) % FRAME_QUEUE_DEPTH; + struct shm_frame *f; + if (len > sizeof(q->frames[0].buf) || next == q->head) return false; + f = &q->frames[tail]; + memcpy(f->buf, buf, len); + f->len = len; + __sync_synchronize(); + q->tail = next; + return true; +} + +static inline size_t shm_queue_pop(struct shm_queue *q, void *buf, size_t len) { + size_t head = q->head, n; + struct shm_frame *f; + if (head == q->tail) return 0; + __sync_synchronize(); + f = &q->frames[head]; + n = f->len; + if (n > len) { + q->head = (head + 1) % FRAME_QUEUE_DEPTH; + return 0; + } + memcpy(buf, f->buf, n); + __sync_synchronize(); + q->head = (head + 1) % FRAME_QUEUE_DEPTH; + return n; +} diff --git a/test/bsd/shm_tap_bridge.c b/test/bsd/shm_tap_bridge.c index 138e2a68..1a20ed20 100644 --- a/test/bsd/shm_tap_bridge.c +++ b/test/bsd/shm_tap_bridge.c @@ -4,39 +4,18 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include - -#define FRAME_SIZE 1540 -#define FRAME_QUEUE_DEPTH 8 -#define SHM_NAME "/mongoose-shm" - -struct shm_frame { - size_t len; - uint8_t buf[FRAME_SIZE]; -}; - -struct shm_queue { - sem_t full; - sem_t empty; - size_t head; - size_t tail; - struct shm_frame frames[FRAME_QUEUE_DEPTH]; -}; - -struct shm_link { - struct shm_queue to_tap; - struct shm_queue from_tap; -}; +#include "shm_queue.h" static volatile sig_atomic_t s_signo; void signal_handler(int signo) { @@ -63,17 +42,22 @@ int main(void) { shm_unlink(SHM_NAME); shmfd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0600); - if (shmfd < 0) return EXIT_FAILURE; - if (ftruncate(shmfd, sizeof(*shm)) != 0) return EXIT_FAILURE; + if (shmfd < 0) { + printf("shm_open failed: %d\n", errno); + return EXIT_FAILURE; + } + if (ftruncate(shmfd, sizeof(*shm)) != 0) { + printf("ftruncate failed: %d\n", errno); + return EXIT_FAILURE; + } shm = (struct shm_link *) mmap(NULL, sizeof(*shm), PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0); - if (shm == MAP_FAILED) return EXIT_FAILURE; + if (shm == MAP_FAILED) { + printf("mmap failed: %d\n", errno); + return EXIT_FAILURE; + } memset(shm, 0, sizeof(*shm)); - sem_init(&shm->to_tap.full, 1, 0); - sem_init(&shm->to_tap.empty, 1, FRAME_QUEUE_DEPTH); - sem_init(&shm->from_tap.full, 1, 0); - sem_init(&shm->from_tap.empty, 1, FRAME_QUEUE_DEPTH); printf("Opened shared memory: %s\n", SHM_NAME); signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); @@ -94,41 +78,31 @@ int main(void) { break; default: // the number of fd/sockets with data available (see pfd.revents for each one); if (pfd[0].revents & (POLLIN | POLLPRI)) { - struct shm_queue *q = &shm->from_tap; - ssize_t nread; - if (sem_trywait(&q->empty) == 0) { - struct shm_frame *f = &q->frames[q->tail]; - nread = read(fd, f->buf, sizeof(f->buf)); + uint8_t buf[FRAME_SIZE]; + ssize_t nread = read(fd, buf, sizeof(buf)); // printf("Got at TAP: %d bytes\n", (int) nread); - if (nread > 0) { - f->len = (size_t) nread; - q->tail = (q->tail + 1) % FRAME_QUEUE_DEPTH; - sem_post(&q->full); -// printf("Sent to SHM\n"); - } else { - sem_post(&q->empty); + if (nread > 0) { + if (!shm_queue_push(&shm->from_tap, buf, (size_t) nread)) { + printf("FRAME LOST: TAP -> SHM\n"); } +// printf("Sent to SHM\n"); } } break; } - if (sem_trywait(&shm->to_tap.full) == 0) { - struct shm_queue *q = &shm->to_tap; - struct shm_frame *f = &q->frames[q->head]; + { + uint8_t buf[FRAME_SIZE]; + size_t n = shm_queue_pop(&shm->to_tap, buf, sizeof(buf)); + if (n > 0) { // printf("Got at SHM: %d bytes\n", (int) f->len); - write(fd, f->buf, f->len); - q->head = (q->head + 1) % FRAME_QUEUE_DEPTH; - sem_post(&q->empty); + write(fd, buf, n); // printf("Sent to TAP\n"); + } } } while (result >= 0 && s_signo == 0); if (result < 0) printf("looks like we got an error here... should I quit ? Waiting for CTRL-C\n"); } - sem_destroy(&shm->from_tap.empty); - sem_destroy(&shm->from_tap.full); - sem_destroy(&shm->to_tap.empty); - sem_destroy(&shm->to_tap.full); munmap(shm, sizeof(*shm)); close(shmfd); shm_unlink(SHM_NAME);