From 80670e9ebc49caf1776628688cbd1e8862217eca Mon Sep 17 00:00:00 2001 From: "Sergio R. Caprile" Date: Mon, 22 Jun 2026 09:58:59 -0300 Subject: [PATCH] add tests for MIP BSD --- test/Makefile | 6 +- test/bsd/FreeRTOSConfig.h | 38 ++++++ test/bsd/Makefile | 48 +++++++ test/bsd/bsd_client.c | 102 +++++++++++++++ test/bsd/bsd_shm_test.c | 151 +++++++++++++++++++++ test/bsd/bsd_x_test.c | 259 +++++++++++++++++++++++++++++++++++++ test/bsd/mongoose_config.h | 30 +++++ test/bsd/shm_tap_bridge.c | 136 +++++++++++++++++++ test/port_tap_bridge.c | 10 +- 9 files changed, 775 insertions(+), 5 deletions(-) create mode 100644 test/bsd/FreeRTOSConfig.h create mode 100644 test/bsd/Makefile create mode 100644 test/bsd/bsd_client.c create mode 100644 test/bsd/bsd_shm_test.c create mode 100644 test/bsd/bsd_x_test.c create mode 100644 test/bsd/mongoose_config.h create mode 100644 test/bsd/shm_tap_bridge.c diff --git a/test/Makefile b/test/Makefile index eecaeb73..12a133d9 100644 --- a/test/Makefile +++ b/test/Makefile @@ -206,12 +206,14 @@ mip_port_s390: RUN = $(DOCKER) --init mdashnet/s390 mip_port_s390: mip_port_test arm: DEFS += -DMG_ENABLE_POSIX_FS=0 -DMG_ENABLE_TCPIP=1 -DMG_ENABLE_TCPIP_DRIVER_INIT=0 -DMG_ARCH=MG_ARCH_ARMGCC -DNO_SLEEP_ABORT -arm: mongoose.h $(SRCS) +arm: mongoose.h $(SRCS) mip_test.c $(DOCKER) mdashnet/armgcc arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test -nostartfiles --specs nosys.specs -e 0 + $(DOCKER) mdashnet/armgcc arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb mip_test.c packed_fs.c $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o mip_test -nostartfiles --specs nosys.specs -e 0 riscv: DEFS += -DMG_ENABLE_POSIX_FS=0 -DMG_ENABLE_TCPIP=1 -DMG_ENABLE_TCPIP_DRIVER_INIT=0 -DMG_ARCH=MG_ARCH_ARMGCC -DNO_SLEEP_ABORT -riscv: mongoose.h $(SRCS) +riscv: mongoose.h $(SRCS) mip_test.c $(DOCKER) mdashnet/riscv riscv-none-elf-gcc -march=rv32imc -mabi=ilp32 $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test + $(DOCKER) mdashnet/riscv riscv-none-elf-gcc -march=rv32imc -mabi=ilp32 mip_test.c packed_fs.c $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o mip_test vc98: Makefile mongoose.h $(SRCS) $(DOCKER) mdashnet/vc98 wine cl $(SRCS) $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe diff --git a/test/bsd/FreeRTOSConfig.h b/test/bsd/FreeRTOSConfig.h new file mode 100644 index 00000000..47a8837f --- /dev/null +++ b/test/bsd/FreeRTOSConfig.h @@ -0,0 +1,38 @@ +#pragma once +#define FREERTOS_CONFIG_H + +#include + +#define configUSE_PREEMPTION 1 +#define configCPU_CLOCK_HZ SYS_FREQUENCY +#define configTICK_RATE_HZ 1000 +#define configMAX_PRIORITIES 5 +#define configUSE_16_BIT_TICKS 0 +#define configUSE_TICK_HOOK 0 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TIMERS 0 +#define configUSE_CO_ROUTINES 0 +#define configUSE_MALLOC_FAILED_HOOK 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configMINIMAL_STACK_SIZE 128 +#define configTOTAL_HEAP_SIZE (1024 * 128) +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_xTaskGetSchedulerState 1 + +#define configPRIO_BITS 4 + +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 +#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 +#define configKERNEL_INTERRUPT_PRIORITY \ + (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) + +#define configMAX_SYSCALL_INTERRUPT_PRIORITY \ + (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) + +#define configASSERT(expr) \ + if (!(expr)) printf("FAILURE %s:%d: %s\n", __FILE__, __LINE__, #expr) + +//#define vPortSVCHandler SVC_Handler +//#define xPortPendSVHandler PendSV_Handler +//xPortSysTickHandler diff --git a/test/bsd/Makefile b/test/bsd/Makefile new file mode 100644 index 00000000..5c1cf693 --- /dev/null +++ b/test/bsd/Makefile @@ -0,0 +1,48 @@ +WARN ?= -pedantic -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common -Wconversion -Wundef +OPTS ?= -O3 -g3 +ASAN ?= -fsanitize=address,undefined,alignment -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-common +ASAN_OPTIONS ?= detect_leaks=1 +COMMON_CFLAGS ?= $(C_WARN) $(WARN) -I. -I../.. $(TFLAGS) +CFLAGS ?= $(OPTS) $(ASAN) $(COMMON_CFLAGS) + +ifeq "$(findstring ++,$(CC))" "" +# $(CC) does not end with ++, i.e. we're using C. Apply C flags +C_WARN ?= -Wmissing-prototypes -Wstrict-prototypes +else +# $(CC) ends with ++, i.e. we're using C++. Apply C++ flags +C_WARN ?= -Wno-deprecated +endif + + +SOURCES = bsd_shm_test.c ../../mongoose.c + +# FreeRTOS +SOURCES += FreeRTOS-Kernel/portable/MemMang/heap_4.c +SOURCES += FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/port.c +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 + +# 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 + ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_test $(ARGS) + +# requires shm_tap_bridge to be running +bsd_test: $(SOURCES) bsd_x_test.c ../../mongoose.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 + $(CC) shm_tap_bridge.c -pthread -o $@ + +clean: # Cleanup. Delete built program and all build artifacts + rm -rf bsd_test bsd_client *.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_client.c b/test/bsd/bsd_client.c new file mode 100644 index 00000000..3aaf0497 --- /dev/null +++ b/test/bsd/bsd_client.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include +#include + +#define PORT 1234 +#define NUM_THREADS 12 +#define DATA_SIZE (4 * 1024) + +static int read_full(int fd, void *buf, size_t len) { + char *p = (char *) buf; + while (len > 0) { + ssize_t n = recv(fd, p, len, 0); + if (n <= 0) return -1; + p += n; + len -= (size_t) n; + } + return 0; +} + +static int write_full(int fd, const void *buf, size_t len) { + const char *p = (const char *) buf; + while (len > 0) { + ssize_t n = send(fd, p, len, 0); + if (n <= 0) return -1; + p += n; + len -= (size_t) n; + } + return 0; +} + +static void fill_text(char *buf, size_t len) { + size_t i; + for (i = 0; i < len; i++) buf[i] = (char) (' ' + random() % 95); +} + +static void *worker(void *arg) { + const char *host = (const char *) arg; + char tx[DATA_SIZE], rx[DATA_SIZE]; + struct sockaddr_in sa; + unsigned port = 0; + int fd; + + fill_text(tx, sizeof(tx)); + memset(rx, 0, sizeof(rx)); + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) goto fail; + + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(PORT); + if (inet_pton(AF_INET, host, &sa.sin_addr) != 1) goto fail_close; + printf("%d CONNECTING\n", fd); + if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) != 0) goto fail_close; + { + socklen_t len = sizeof(sa); + if (getsockname(fd, (struct sockaddr *) &sa, &len) != 0) goto fail_close; + port = (unsigned) ntohs(sa.sin_port); + } + printf("%d CONNECTED port: %u\n", fd, port); + printf("%d WRITING\n", fd); + if (write_full(fd, tx, sizeof(tx)) != 0) goto fail_close; + printf("%d READING\n", fd); + if (read_full(fd, rx, sizeof(rx)) != 0) goto fail_close; + if (memcmp(tx, rx, sizeof(tx)) != 0) goto fail_close; + + printf("%d OK-CLOSING\n", fd); + close(fd); + return NULL; + +fail_close: + printf("%d ERROR-CLOSING\n", fd); + close(fd); +fail: + exit(1); +} + +int main(int argc, char *argv[]) { + pthread_t threads[NUM_THREADS]; + int i; + + if (argc != 2) { + fprintf(stderr, "usage: %s IP\n", argv[0]); + return 1; + } + + srandom(1); + + for (i = 0; i < NUM_THREADS; i++) { + if (pthread_create(&threads[i], NULL, worker, argv[1]) != 0) return 1; + } + + for (i = 0; i < NUM_THREADS; i++) { + if (pthread_join(threads[i], NULL) != 0) return 1; + } + + return 0; +} diff --git a/test/bsd/bsd_shm_test.c b/test/bsd/bsd_shm_test.c new file mode 100644 index 00000000..474f1bd5 --- /dev/null +++ b/test/bsd/bsd_shm_test.c @@ -0,0 +1,151 @@ +#include +#include +#include +#include + +#include "mongoose.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; + struct shm_link *shm; +}; + +static struct shm_driver_data shm_data = { + SHM_NAME, + NULL, + NULL, +}; + +static bool shm_init(struct mg_tcpip_if *ifp) { + struct shm_driver_data *d = (struct shm_driver_data *) ifp->driver_data; + int fd; + + fd = shm_open(d->name, O_RDWR, 0600); + if (fd < 0) return false; + d->fp = fdopen(fd, "r+"); + if (d->fp == NULL) 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) { + fclose(d->fp); + d->fp = NULL; + d->shm = NULL; + return false; + } + + return true; +} + +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) { + 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; +} + +static bool shm_poll(struct mg_tcpip_if *ifp, bool s1) { + return s1 && ifp->driver_data ? true : false; +} + +static struct mg_tcpip_driver shm_driver = { + shm_init, + shm_tx, + shm_rx, + shm_poll, +}; + + +bool mg_random(void *buf, size_t len) { + bool success = false; + unsigned char *p = (unsigned char *) buf; + FILE *fp = fopen("/dev/urandom", "rb"); + if (fp != NULL) { + if (fread(buf, 1, len, fp) == len) success = true; + fclose(fp); + } + // If everything above did not work, fallback to a pseudo random generator + if (success == false) { + printf("\nWeak RNG: using rand()\n"); + while (len--) *p++ = (unsigned char) (rand() & 255); + } + return success; +} + + +#include "bsd_x_test.c" + + +int main(void) { + bool result; + const char *debug_level = getenv("V"); + // Setup interface + const char *mac = "02:00:01:02:03:78"; // MAC address + + printf("Opened SHM interface\n"); + usleep(200000); // 200 ms + + result = bsd_x_test(&shm_driver, &shm_data, mac, debug_level); + if (shm_data.shm != NULL) munmap(shm_data.shm, sizeof(*shm_data.shm)); + if (shm_data.fp != NULL) fclose(shm_data.fp); + if (!result) return EXIT_FAILURE; + return EXIT_SUCCESS; +} diff --git a/test/bsd/bsd_x_test.c b/test/bsd/bsd_x_test.c new file mode 100644 index 00000000..c210db46 --- /dev/null +++ b/test/bsd/bsd_x_test.c @@ -0,0 +1,259 @@ +#define BSDTEST_USING_DHCP 1 + +#include +#include + +static int s_num_tests = 0; +static bool s_error = false; +static volatile bool s_done = false, s_wait; + +#ifdef NO_ABORT +static int s_abort = 0; +#define ABORT() ++s_abort, s_error = true +#else +#ifdef NO_SLEEP_ABORT +#define ABORT() abort() +#else +#define ABORT() \ + sleep(2); /* 2s, GH print reason */ \ + abort(); +#endif +#endif + +#define ASSERT(expr) \ + do { \ + s_num_tests++; \ + if (!(expr)) { \ + printf("FAILURE %s:%d: %s\n", __FILE__, __LINE__, #expr); \ + fflush(stdout); \ + ABORT(); \ + } \ + } while (0) + +struct client_data { + char hostname[64]; + uint32_t ip; + bool done; +}; + +// client_task: connects to hostname:80, fetches "/", logs response length. +static void client_task(void *args) { + int fd, c; + struct client_data *d = (struct client_data *) args; + char req[256]; + fd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in sa = {.sin_family = AF_INET, .sin_port = htons(80)}; + memcpy(&sa.sin_addr, &d->ip, 4); + c = connect(fd, (struct sockaddr *) &sa, sizeof(sa)); + ASSERT(c >= 0); + if (c < 0) { + MG_ERROR(("connect failed")); + close(fd); + vTaskDelete(NULL); + return; + } + snprintf(req, sizeof(req),"GET / HTTP/1.0\r\nHost: %s\r\n\r\n", d->hostname); + send(fd, req, strlen(req), 0); + size_t total = 0; + char buf[512]; + ssize_t n; + while ((n = recv(fd, buf, sizeof(buf), 0)) > 0) total += (size_t) n; + MG_INFO(("%s response: %lu bytes", d->hostname, (unsigned long) total)); + ASSERT(total > 0); + close(fd); + d->done = true; + vTaskDelete(NULL); + (void) args; +} + +// echo_task: one per accepted connection. Echoes data back. +static void echo_task(void *args) { + int fd = (int) (uintptr_t) args; + char buf[512]; + ssize_t n; + while ((n = recv(fd, buf, sizeof(buf), 0)) > 0) send(fd, buf, (size_t) n, 0); + close(fd); + vTaskDelete(NULL); +} + +// accept loop. Waits for incoming connections on port 1234 and spawns +// an echo_task for each one, allowing concurrent clients. +static void atask(void *args) { + mg_bsd_init(); + int lfd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in sa = {.sin_family = AF_INET, .sin_port = htons(1234), .sin_addr = {INADDR_ANY}}; + bind(lfd, (struct sockaddr *) &sa, sizeof(sa)); + listen(lfd, 5); + fcntl(lfd, F_SETFL, O_NONBLOCK); + MG_INFO(("BSD echo server on :1234")); + while (!s_wait) { + int fd = accept(lfd, NULL, NULL); + if (fd < 0) { vTaskDelay(pdMS_TO_TICKS(10)); continue; } + // echo_task at higher priority than task1 + xTaskCreate(echo_task, "echo", 512, (void *) (uintptr_t) fd, configMAX_PRIORITIES - 1, NULL); + } + close(lfd); + s_wait = false; + vTaskDelete(NULL); + (void) args; +} + + +// task2: tests owner. +static void task2(void *args) { + struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager + char *ip, *cmd[3]; + pid_t pid; + int status; + extern char **environ; + +#define DASHBOARD(x) printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false":"true"); + + vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms + s_error = false; + s_wait = false; + // atask at higher priority than task1 + xTaskCreate(atask, "atask", 256, NULL, configMAX_PRIORITIES - 1, NULL); + ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip); + cmd[0] = "./bsd_client"; + cmd[1] = ip; + cmd[2] = NULL; + ASSERT(posix_spawn(&pid, cmd[0], NULL, NULL, cmd, environ) == 0); + for (;;) { + pid_t r = waitpid(pid, &status, WNOHANG); + ASSERT(r >= 0); + if (r == pid) break; + vTaskDelay(pdMS_TO_TICKS(10)); + } + ASSERT(WIFEXITED(status)); + ASSERT(WEXITSTATUS(status) == 0); + mg_free(ip); + s_wait = true; + while (s_wait) vTaskDelay(pdMS_TO_TICKS(10)); + DASHBOARD("accept"); + + vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms + s_error = false; + { + struct hostent *h = gethostbyname("invalidhostname"); + ASSERT(h == NULL); + h = gethostbyname("mongoose.ws"); + ASSERT(h != NULL); + if (h == NULL) { + MG_ERROR(("DNS failed")); + vTaskDelete(NULL); + return; + } + } + DASHBOARD("gethostbyname"); + + vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms + s_error = false; + s_wait = true; + { + struct client_data cd[3]; + struct hostent *h = gethostbyname("mongoose.ws"); + strcpy(cd[0].hostname, "mongoose.ws"); + memcpy(&cd[0].ip, h->h_addr, 4); + cd[0].done = false; + // client_task at higher priority than task1 + xTaskCreate(client_task, "client1", 1024, &cd[0], configMAX_PRIORITIES - 1, NULL); + h = gethostbyname("cesanta.com"); + strcpy(cd[1].hostname, "cesanta.com"); + memcpy(&cd[1].ip, h->h_addr, 4); + cd[1].done = false; + // client_task at higher priority than task1 + xTaskCreate(client_task, "client2", 1024, &cd[1], configMAX_PRIORITIES - 1, NULL); + h = gethostbyname("google.com"); + strcpy(cd[2].hostname, "google.com"); + memcpy(&cd[2].ip, h->h_addr, 4); + cd[2].done = false; + // client_task at higher priority than task1 + xTaskCreate(client_task, "client3", 1024, &cd[2], configMAX_PRIORITIES - 1, NULL); + while (!cd[0].done || !cd[1].done || !cd[2].done) vTaskDelay(pdMS_TO_TICKS(10)); + } + DASHBOARD("connect"); + // End task 1 + s_done = true; + while (s_done) vTaskDelay(pdMS_TO_TICKS(10)); + + // Clear + s_error = false; + mg_mgr_free(mgr); + ASSERT(mgr->conns == NULL); // Deconstruction OK + printf("HEALTH_DASHBOARD\t\"cleanup\": %s\n", s_error ? "false":"true"); + // last entry with no comma + vTaskEndScheduler(); +} + +// task1: network owner. Runs the Mongoose event loop and the BSD command queue. +// The interface needs to be started here, as FreeRTOS needs to be running (time base) +static void task1(void *args) { + struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager + + // Stack initialization, Network configuration (DHCP lease, ...) +#if BSDTEST_USING_DHCP == 0 + MG_INFO(("MIF configuration: Static IP")); + ASSERT(mif.ip != 0); // Check we have a satic IP assigned + mg_mgr_poll(&mgr, 0); // For initialisation +#else + MG_INFO(("MIF configuration: DHCP")); + ASSERT(mgr->ifp->ip == 0); // Check we are set for DHCP + while (mgr->ifp->ip == 0 && mg_millis() < 5000) { + mg_mgr_poll(mgr, 0); + } + if (mgr->ifp->ip == 0) MG_ERROR(("No ip assigned (DHCP lease may have failed).\n")); + ASSERT(mgr->ifp->ip != 0); // We have an IP (lease or static) +#endif + while (mgr->ifp->state != MG_TCPIP_STATE_READY) { + mg_mgr_poll(mgr, 0); + } + MG_INFO(("Interface started, starting main loop")); + xTaskCreate(task2, "task2", 256, mgr, configMAX_PRIORITIES - 2, NULL); + + while (!s_done) { + mg_mgr_poll(mgr, 0); + mg_bsd_poll(mgr); + } + s_done = false; + vTaskDelete(NULL); +} + +static bool bsd_x_test(struct mg_tcpip_driver *driver, void *driver_data, const char *mac, const char *debug_level) { + if (debug_level == NULL) debug_level = "3"; + mg_log_set(atoi(debug_level)); + + struct mg_mgr mgr; // Event manager + mg_mgr_init(&mgr); // Initialise event manager + + struct mg_tcpip_if mif; + memset(&mif, 0, sizeof(mif)); + + mif.driver = driver; + mif.driver_data = driver_data; + +#if BSDTEST_USING_DHCP == 1 +#else + mif.ip = mg_htonl(MG_U32(192, 168, 32, 2)); // Triggering a network failure + mif.mask = mg_htonl(MG_U32(255, 255, 255, 0)); + mif.gw = mg_htonl(MG_U32(192, 168, 32, 1)); +#endif + + sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mif.mac[0], &mif.mac[1], + &mif.mac[2], &mif.mac[3], &mif.mac[4], &mif.mac[5]); + + mg_tcpip_init(&mgr, &mif); + ASSERT(mgr.ifp != NULL); // driver init succeeded + MG_INFO(("Init done, starting interface")); + usleep(200000); // 200 ms + + xTaskCreate(task1, "task1", 2048, &mgr, configMAX_PRIORITIES - 2, NULL); + vTaskStartScheduler(); + +#ifdef NO_ABORT + if (s_abort != 0) return false; +#endif + + printf("SUCCESS. Total tests: %d\n", s_num_tests); + return true; +} diff --git a/test/bsd/mongoose_config.h b/test/bsd/mongoose_config.h new file mode 100644 index 00000000..31cc5bd2 --- /dev/null +++ b/test/bsd/mongoose_config.h @@ -0,0 +1,30 @@ +#pragma once + +// See https://mongoose.ws/documentation/#build-options +//#undef MG_ARCH +//#define MG_ARCH MG_ARCH_UNIX +#define MG_ENABLE_FREERTOS 1 + +#define MG_ENABLE_TCPIP 1 +#define MG_ENABLE_TCPIP_DRIVER_INIT 0 +#define MG_ENABLE_BSD_SOCKETS 1 +#define MG_ENABLE_BSD_LOG 1 +#define MG_ENABLE_LINES 1 +#define MG_ENABLE_CUSTOM_RANDOM 1 + +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include diff --git a/test/bsd/shm_tap_bridge.c b/test/bsd/shm_tap_bridge.c new file mode 100644 index 00000000..138e2a68 --- /dev/null +++ b/test/bsd/shm_tap_bridge.c @@ -0,0 +1,136 @@ +#include +#include +#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; +}; + +static volatile sig_atomic_t s_signo; +void signal_handler(int signo) { + s_signo = signo; +} + +int main(void) { + // Setup interface + const char *iface = "tap0"; // Network iface + const char *tuntap_device = "/dev/net/tun"; + int fd = open(tuntap_device, O_RDWR); + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, iface, IFNAMSIZ); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) { + printf("Failed to setup TAP interface: %s", ifr.ifr_name); + return EXIT_FAILURE; + } + printf("Opened TAP interface: %s\n", iface); + + int shmfd; + struct shm_link *shm; + + 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; + shm = (struct shm_link *) mmap(NULL, sizeof(*shm), PROT_READ | PROT_WRITE, + MAP_SHARED, shmfd, 0); + if (shm == MAP_FAILED) 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); + + while (s_signo == 0) { + struct pollfd pfd[1]; + int result; + do { + pfd[0].fd = fd; + pfd[0].events = POLLIN | POLLPRI; // will wait for input available (read ready), use POLLOUT for write + + // only read ready shown here, for write ready use the 3rd arg, and the 4th for exceptions + result = poll(pfd, 1, 1); // 1 pfd, TIMEOUT in milliseconds + switch(result) { + case -1: // error + break; + case 0: // timeout (if a timeout struct was passed to select) + 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)); +// 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); + } + } + } + break; + } + if (sem_trywait(&shm->to_tap.full) == 0) { + struct shm_queue *q = &shm->to_tap; + struct shm_frame *f = &q->frames[q->head]; +// 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); +// 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); + close(fd); +} diff --git a/test/port_tap_bridge.c b/test/port_tap_bridge.c index af96b0c3..b19d6de9 100644 --- a/test/port_tap_bridge.c +++ b/test/port_tap_bridge.c @@ -10,6 +10,7 @@ #include #include #include +#include #ifndef __OpenBSD__ #include @@ -20,7 +21,7 @@ #include #endif -static int s_signo; +static volatile sig_atomic_t s_signo; void signal_handler(int signo) { s_signo = signo; } @@ -49,6 +50,8 @@ int main(void) { return EXIT_FAILURE; } printf("Opened TAP interface: %s\n", iface); + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); int sockfd; struct sockaddr_in tap_addr, port_addr; @@ -97,8 +100,9 @@ int main(void) { } break; } - } while (result >= 0); - printf("looks like we got an error here... should I quit ? Waiting for CTRL-C\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"); } close(sockfd); close(fd);