Added posix non-blocking sockets, new tests
All checks were successful
/ unit_test (push) Successful in 51s

This commit is contained in:
Daniele Lacamera 2024-11-17 07:11:00 +01:00
parent 5c3cd8bf1e
commit 1490f8dba1
8 changed files with 310 additions and 42 deletions

1
.gitignore vendored
View file

@ -1,6 +1,7 @@
*.o
*.a
*.pcap
*.so
build/*
test/unit/unit
tags

View file

@ -1,39 +1,62 @@
CC?=gcc
CFLAGS:=-Wall -Werror -Wextra -I.
CFLAGS+=-g -ggdb
LDFLAGS+=-pthread
OBJ=build/femtotcp.o \
build/test/test_linux_eventloop.o build/port/posix/linux_tap.o
build/port/posix/linux_tap.o
all: build/test-evloop
EXE=build/tcpecho build/test-evloop
LIB=libfemtotcp.so
all: $(EXE) $(LIB)
#Static library
static: CFLAGS+=-static
static: libtcpip.a
libtcpip.a: $(OBJ)
@ar rcs $@ $^
libfemtotcp.so:CFLAGS+=-fPIC
libfemtotcp.so: build/pie/port/posix/bsd_socket.o build/pie/femtotcp.o \
build/pie/port/posix/linux_tap.o
@mkdir -p `dirname $@` || true
@echo "[LD] $@"
@$(CC) $(CFLAGS) $(LDFLAGS) -shared -o $@ -Wl,--start-group $(^) -Wl,--end-group
clean:
@rm -rf build
@make -C src/test/unit clean
# Test
asan: build/test-evloop
asan: $(EXE) $(LIB)
asan:CFLAGS+=-fsanitize=address
asan:LDFLAGS+=-static-libasan
build/test:CFLAGS+=-g -ggdb -DTEST_MAIN -DETHERNET
build/test:LDFLAGS+=-pthread
build/test-evloop: $(OBJ)
# Test
build/test-evloop: $(OBJ) build/test/test_linux_eventloop.o
@echo "[LD] $@"
@$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJ)
@$(CC) $(CFLAGS) $(LDFLAGS) -o $@ -Wl,--start-group $(^) -Wl,--end-group
build/tcpecho: $(OBJ) build/port/posix/bsd_socket.o build/test/tcp_echo.o
@echo "[LD] $@"
@$(CC) $(CFLAGS) $(LDFLAGS) -o $@ -Wl,--start-group $(^) -Wl,--end-group
build/%.o: src/%.c
@mkdir -p `dirname $@` || true
@echo "[CC] $<"
@$(CC) $(CFLAGS) -c $< -o $@
build/pie/%.o: src/%.c
@mkdir -p `dirname $@` || true
@echo "[CC] $<"
@$(CC) $(CFLAGS) -c $< -o $@
unit:
@make -C src/test/unit
@mkdir -p build/test/

View file

@ -11,4 +11,8 @@
#define MAX_NEIGHBORS 16
/* Linux test configuration */
#define FEMTOTCP_IP "10.10.10.2"
#define LINUX_IP "10.10.10.1"
#endif

View file

@ -12,6 +12,7 @@ typedef uint32_t ip4;
#define ee32(x) __builtin_bswap32(x)
#define DEBUG
#ifdef DEBUG
#include <stdio.h>
#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__)
@ -39,9 +40,15 @@ struct ipconf {
};
/* Socket interface */
#define MARK_TCP_SOCKET 0x1000 /* Mark a socket as TCP */
#define MARK_UDP_SOCKET 0x4000 /* Mark a socket as UDP */
#ifndef FEMTO_POSIX
#define IPSTACK_SOCK_STREAM 1
#define IPSTACK_SOCK_DGRAM 2
struct ipstack_sockaddr_in {
uint16_t sin_family;
uint16_t sin_port;
@ -96,6 +103,8 @@ ip4 atoip4(const char *ip);
#define CB_EVENT_TIMEOUT 0x08 /* Timeout */
void ipstack_register_callback(struct ipstack *s, int sock_fd, void (*cb)(int sock_fd, uint16_t events, void *arg), void *arg);
/* External requirements */
uint32_t ipstack_getrandom(void);

View file

@ -14,8 +14,6 @@
#define MAX_TIMERS MAX_TCPSOCKETS * 3
/* Constants */
#define MARK_TCP_SOCKET 0x1000 /* Mark a socket as TCP */
#define MARK_UDP_SOCKET 0x4000 /* Mark a socket as UDP */
#define IPPROTO_ICMP 0x01
#define IPPROTO_TCP 0x06

View file

@ -1,11 +1,39 @@
/* POSIX socket calls wrapper for femtoTCP */
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <pthread.h>
#include <errno.h>
#include <dlfcn.h>
#include <sys/time.h>
#define FEMTO_POSIX
#include "config.h"
#include "femtotcp.h"
static __thread int in_the_stack = 0;
static __thread struct ipstack IPSTACK;
static __thread int in_the_stack = 1;
static struct ipstack *IPSTACK = NULL;
/* host_ functions are the original functions from the libc */
static int (*host_socket ) (int domain, int type, int protocol) = NULL;
static int (*host_bind ) (int sockfd, const struct sockaddr *addr, socklen_t addrlen);
static int (*host_connect ) (int sockfd, const struct sockaddr *addr, socklen_t addrlen);
static int (*host_accept ) (int sockfd, struct sockaddr *addr, socklen_t *addrlen);
static int (*host_listen ) (int sockfd, int backlog);
static ssize_t (*host_recvfrom) (int sockfd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen);
static ssize_t (*host_recv ) (int sockfd, void *buf, size_t len, int flags);
static ssize_t (*host_read ) (int sockfd, void *buf, size_t len);
static ssize_t (*host_sendto ) (int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen);
static ssize_t (*host_send ) (int sockfd, const void *buf, size_t len, int flags);
static ssize_t (*host_write ) (int sockfd, const void *buf, size_t len);
static int (*host_close ) (int sockfd);
static int (*host_setsockopt) (int sockfd, int level, int optname, const void *optval, socklen_t optlen);
static int (*host_getsockopt) (int sockfd, int level, int optname, void *optval, socklen_t *optlen);
static int (*host_getsockname) (int sockfd, struct sockaddr *addr, socklen_t *addrlen);
static int (*host_getpeername) (int sockfd, struct sockaddr *addr, socklen_t *addrlen);
#define swap_socketcall(call, name) \
{ \
@ -13,51 +41,192 @@ static __thread struct ipstack IPSTACK;
if (host_##call == NULL) { \
*(void **)(&host_##call) = dlsym(RTLD_NEXT, name); \
if ((msg = dlerror()) != NULL) \
fprintf (stderr, "%s: dlsym(%s): %s\n", "picotcp", name, msg); \
fprintf (stderr, "%s: dlsym(%s): %s\n", "femtoTCP", name, msg); \
} \
}
#define conditional_steal_call(call, fd, ...) \
if(in_the_stack) { \
return host_##call(i, ## __VA_ARGS__); \
return host_##call(fd, ## __VA_ARGS__); \
} else { \
if (get_femto_fd(i) > -1) { \
int __femto_retval = ft_posix_##call(&IPSTACK, fd, ## __VA_ARGS__); \
if ((fd & (MARK_TCP_SOCKET | MARK_UDP_SOCKET)) != 0) { \
int __femto_retval = ft_##call(IPSTACK, fd, ## __VA_ARGS__); \
if (__femto_retval < 0) { \
errno = __femto_retval; \
return -1; \
} \
return __femto_retval; \
}else { \
return host_##call(i, ## __VA_ARGS__); \
return host_##call(fd, ## __VA_ARGS__); \
} \
}
/* Generator for a wrapper function intercepting the return value
* '-11' (EAGAIN). It keeps blocking until the underlying femtoTCP
* stack is ready to process the call.
*/
#define GEN_SOCKET_CALL(call) \
int ft_posix_##call(struct ipstack *ipstack, int fd, ...) { \
int ret; \
va_list args; \
va_start(args, fd); \
do { \
ret = call(fd, args); \
if (ret == -11) { \
pthread_yield(); \
#define conditional_steal_blocking_call(call, fd, ...) \
if(in_the_stack) { \
return host_##call(fd, ## __VA_ARGS__); \
} else { \
if ((fd & (MARK_TCP_SOCKET | MARK_UDP_SOCKET)) != 0) { \
int __femto_retval; \
do { \
__femto_retval = ft_##call(IPSTACK, fd, ## __VA_ARGS__); \
if (__femto_retval == -11) { \
usleep(1000); \
} \
} while (__femto_retval == -11); \
if (__femto_retval < 0) { \
errno = __femto_retval; \
return -1; \
} \
return __femto_retval; \
}else { \
return host_##call(fd, ## __VA_ARGS__); \
} \
} while (ret == -11); \
va_end(args); \
return ret; \
}
int ft_setsockopt(struct ipstack *ipstack, int fd, int level, int optname, const void *optval, socklen_t optlen) {
(void)ipstack;
(void)fd;
(void)level;
(void)optname;
(void)optval;
(void)optlen;
return 0;
}
int ft_getsockopt(struct ipstack *ipstack, int fd, int level, int optname, void *optval, socklen_t *optlen) {
(void)ipstack;
(void)fd;
(void)level;
(void)optname;
(void)optval;
(void)optlen;
return 0;
}
int socket(int domain, int type, int protocol) {
if (in_the_stack) {
return host_socket(domain, type, protocol);
} else {
return ft_socket(IPSTACK, domain, type, protocol);
}
}
int listen(int sockfd, int backlog) {
conditional_steal_call(listen, sockfd, backlog);
}
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
conditional_steal_call(bind, sockfd, addr, addrlen);
}
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) {
conditional_steal_call(getsockopt, sockfd, level, optname, optval, optlen);
}
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
conditional_steal_call(getpeername, sockfd, addr, addrlen);
}
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
conditional_steal_call(getsockname, sockfd, addr, addrlen);
}
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) {
conditional_steal_call(setsockopt, sockfd, level, optname, optval, optlen);
}
int close(int sockfd) {
conditional_steal_call(close, sockfd);
}
/* Blocking calls */
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
conditional_steal_blocking_call(accept, sockfd, addr, addrlen);
}
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
conditional_steal_blocking_call(connect, sockfd, addr, addrlen);
}
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen) {
conditional_steal_blocking_call(recvfrom, sockfd, buf, len, flags, addr, addrlen);
}
ssize_t recv(int sockfd, void *buf, size_t len, int flags) {
conditional_steal_blocking_call(recv, sockfd, buf, len, flags);
}
ssize_t read(int sockfd, void *buf, size_t len) {
conditional_steal_blocking_call(read, sockfd, buf, len);
}
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen) {
conditional_steal_blocking_call(sendto, sockfd, buf, len, flags, addr, addrlen);
}
ssize_t send(int sockfd, const void *buf, size_t len, int flags) {
conditional_steal_blocking_call(send, sockfd, buf, len, flags);
}
ssize_t write(int sockfd, const void *buf, size_t len) {
conditional_steal_blocking_call(write, sockfd, buf, len);
}
GEN_SOCKET_CALL(acccept)
GEN_SOCKET_CALL(bind)
GEN_SOCKET_CALL(close)
GEN_SOCKET_CALL(connect)
GEN_SOCKET_CALL(sendto)
GEN_SOCKET_CALL(recvfrom)
/* Catch-all function to initialize a new tap device as the network interface.
* This is defined in port/linux.c
* */
extern int tap_init(struct ll *dev, const char *name, uint32_t host_ip);
void *ft_posix_ip_loop(void *arg) {
struct ipstack *ipstack = (struct ipstack *)arg;
uint32_t ms_next;
struct timeval tv;
while (1) {
gettimeofday(&tv, NULL);
ms_next = ipstack_poll(ipstack, tv.tv_sec * 1000 + tv.tv_usec / 1000);
usleep(ms_next * 1000);
in_the_stack = 1;
}
return NULL;
}
void __attribute__((constructor)) init_femto_posix() {
struct in_addr linux_ip;
struct ll *tapdev;
pthread_t ipstack_thread;
if (IPSTACK)
return;
inet_aton(LINUX_IP, &linux_ip);
swap_socketcall(socket, "socket");
swap_socketcall(bind, "bind");
swap_socketcall(listen, "listen");
swap_socketcall(accept, "accept");
swap_socketcall(connect, "connect");
swap_socketcall(sendto, "sendto");
swap_socketcall(recvfrom, "recvfrom");
swap_socketcall(recv, "recv");
swap_socketcall(send, "send");
swap_socketcall(close, "close");
swap_socketcall(write, "write");
swap_socketcall(read, "read");
swap_socketcall(getsockname, "getsockname");
swap_socketcall(getpeername, "getpeername");
swap_socketcall(setsockopt, "getaddrinfo");
swap_socketcall(getsockopt, "freeaddrinfo");
ipstack_init_static(&IPSTACK);
tapdev = ipstack_getdev(IPSTACK);
if (tap_init(tapdev, "femt0", linux_ip.s_addr) < 0) {
perror("tap init");
}
ipstack_ipconfig_set(IPSTACK, atoip4(FEMTOTCP_IP), atoip4("255.255.255.0"),
atoip4(LINUX_IP));
printf("IP: manually configured - %s\n", FEMTOTCP_IP);
sleep(1);
pthread_create(&ipstack_thread, NULL, ft_posix_ip_loop, IPSTACK);
in_the_stack = 0;
}

65
src/test/tcp_echo.c Normal file
View file

@ -0,0 +1,65 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8
#define BUFFER_SIZE 1024
int main() {
int server_fd, client_fd;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE];
// Create a socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("Socket failed");
exit(EXIT_FAILURE);
}
printf("Socket created: %d\n", server_fd);
// Bind to the specified port
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("Bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Bind successful\n");
// Start listening for incoming connections
if (listen(server_fd, 3) < 0) {
perror("Listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Echo server listening on port %d\n", PORT);
while (1) {
// Accept a client connection
if ((client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
perror("Accept failed");
continue;
}
printf("Client connected, fd: %d\n", client_fd);
ssize_t bytes_read;
while ((bytes_read = read(client_fd, buffer, BUFFER_SIZE)) > 0) {
write(client_fd, buffer, bytes_read); // Echo data back to the client
}
printf("Client disconnected\n");
close(client_fd);
}
close(server_fd);
return 0;
}

View file

@ -3,15 +3,14 @@
#include <arpa/inet.h>
#include <pthread.h>
#include <stdlib.h>
#include "femtotcp.h"
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "femtotcp.h"
//#define DHCP
#define FEMTOTCP_IP "10.10.10.2"
#define LINUX_IP "10.10.10.1"
#define TEST_SIZE (8 * 1024)
#define BUFFER_SIZE TEST_SIZE