diff --git a/.gitignore b/.gitignore index ab68491..d01d092 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.o *.a *.pcap +*.so build/* test/unit/unit tags diff --git a/Makefile b/Makefile index 1043176..8d824d0 100644 --- a/Makefile +++ b/Makefile @@ -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/ diff --git a/config.h b/config.h index 36bfcf3..78924f5 100644 --- a/config.h +++ b/config.h @@ -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 diff --git a/femtotcp.h b/femtotcp.h index 01f4686..9395ecc 100644 --- a/femtotcp.h +++ b/femtotcp.h @@ -12,6 +12,7 @@ typedef uint32_t ip4; #define ee32(x) __builtin_bswap32(x) #define DEBUG + #ifdef DEBUG #include #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); diff --git a/src/femtotcp.c b/src/femtotcp.c index 98e769b..6bc34b9 100644 --- a/src/femtotcp.c +++ b/src/femtotcp.c @@ -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 diff --git a/src/port/posix/bsd_socket.c b/src/port/posix/bsd_socket.c index e0e2079..4386cfb 100644 --- a/src/port/posix/bsd_socket.c +++ b/src/port/posix/bsd_socket.c @@ -1,11 +1,39 @@ /* POSIX socket calls wrapper for femtoTCP */ +#include +#include +#include #include +#include +#include +#include +#include +#include #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; +} diff --git a/src/test/tcp_echo.c b/src/test/tcp_echo.c new file mode 100644 index 0000000..9e32517 --- /dev/null +++ b/src/test/tcp_echo.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/test/test_linux_eventloop.c b/src/test/test_linux_eventloop.c index 7b6c7c9..dd4923e 100644 --- a/src/test/test_linux_eventloop.c +++ b/src/test/test_linux_eventloop.c @@ -3,15 +3,14 @@ #include #include #include -#include "femtotcp.h" #include #include #include #include +#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