add tests for MIP BSD

This commit is contained in:
Sergio R. Caprile 2026-06-22 09:58:59 -03:00
parent b607a4209a
commit 80670e9ebc
9 changed files with 775 additions and 5 deletions

View file

@ -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

38
test/bsd/FreeRTOSConfig.h Normal file
View file

@ -0,0 +1,38 @@
#pragma once
#define FREERTOS_CONFIG_H
#include <stdio.h>
#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

48
test/bsd/Makefile Normal file
View file

@ -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 $@

102
test/bsd/bsd_client.c Normal file
View file

@ -0,0 +1,102 @@
#include <arpa/inet.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#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;
}

151
test/bsd/bsd_shm_test.c Normal file
View file

@ -0,0 +1,151 @@
#include <stdio.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/stat.h>
#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;
}

259
test/bsd/bsd_x_test.c Normal file
View file

@ -0,0 +1,259 @@
#define BSDTEST_USING_DHCP 1
#include <spawn.h>
#include <sys/wait.h>
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;
}

View file

@ -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 <ctype.h>
#include <inttypes.h>
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

136
test/bsd/shm_tap_bridge.c Normal file
View file

@ -0,0 +1,136 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <signal.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#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);
}

View file

@ -10,6 +10,7 @@
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <signal.h>
#ifndef __OpenBSD__
#include <linux/if.h>
@ -20,7 +21,7 @@
#include <net/if_types.h>
#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);