mirror of
https://github.com/cesanta/mongoose
synced 2026-08-25 22:26:06 -04:00
add tests for MIP BSD
This commit is contained in:
parent
b607a4209a
commit
80670e9ebc
9 changed files with 775 additions and 5 deletions
|
|
@ -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
38
test/bsd/FreeRTOSConfig.h
Normal 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
48
test/bsd/Makefile
Normal 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
102
test/bsd/bsd_client.c
Normal 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
151
test/bsd/bsd_shm_test.c
Normal 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
259
test/bsd/bsd_x_test.c
Normal 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;
|
||||
}
|
||||
30
test/bsd/mongoose_config.h
Normal file
30
test/bsd/mongoose_config.h
Normal 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
136
test/bsd/shm_tap_bridge.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue