diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index fc601794..0ab8ddb4 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -34,6 +34,13 @@ jobs: timeout --preserve-status 5m sudo ./build/test-evloop sudo killall tcpdump || true + - name: Run standalone "event loop" TUN test + timeout-minutes: 5 + run: | + set -euo pipefail + timeout --preserve-status 5m sudo ./build/test-evloop-tun + sudo killall tcpdump || true + - name: Run standalone "IPsec esp" test timeout-minutes: 7 run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index bd616c0f..4c5c8e87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,6 +93,15 @@ add_executable(test-evloop ${EXCLUDE_TEST_BINARY} ) add_test(NAME evloop COMMAND test-evloop) +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + add_executable(test-evloop-tun ${EXCLUDE_TEST_BINARY} + src/test/test_eventloop_tun.c + src/port/posix/linux_tun.c + ${WOLFIP_SRCS} + ) + add_test(NAME evloop-tun COMMAND test-evloop-tun) +endif() + add_executable(test-dns ${EXCLUDE_TEST_BINARY} src/test/test_dhcp_dns.c ${WOLFIP_SRCS} diff --git a/Makefile b/Makefile index 23a345a8..7bdbb111 100644 --- a/Makefile +++ b/Makefile @@ -156,6 +156,9 @@ EXE=build/tcpecho build/tcp_netcat_poll build/tcp_netcat_select \ build/test-evloop build/test-dns build/test-wolfssl-forwarding \ build/test-ttl-expired build/test-wolfssl build/test-httpd \ build/ipfilter-logger build/test-esp build/esp-server +ifeq ($(UNAME_S),Linux) + EXE+= build/test-evloop-tun +endif LIB=libwolfip.so PREFIX=/usr/local @@ -217,6 +220,10 @@ build/test-evloop: $(OBJ) build/test/test_eventloop.o @echo "[LD] $@" @$(CC) $(CFLAGS) -o $@ $(BEGIN_GROUP) $(^) $(LDFLAGS) $(END_GROUP) +build/test-evloop-tun: $(OBJ) build/test/test_eventloop_tun.o build/port/posix/linux_tun.o + @echo "[LD] $@" + @$(CC) $(CFLAGS) -o $@ $(BEGIN_GROUP) $(^) $(LDFLAGS) $(END_GROUP) + build/test-dns: $(OBJ) build/test/test_dhcp_dns.o @echo "[LD] $@" @$(CC) $(CFLAGS) -o $@ $(BEGIN_GROUP) $(^) $(LDFLAGS) $(END_GROUP) diff --git a/docs/API.md b/docs/API.md index 70a45871..a760971a 100644 --- a/docs/API.md +++ b/docs/API.md @@ -26,12 +26,15 @@ wolfIP is a minimal TCP/IP stack designed for resource-constrained embedded syst struct wolfIP_ll_dev { uint8_t mac[6]; // Device MAC address char ifname[16]; // Interface name + uint8_t non_ethernet; // L3-only link (no Ethernet header/ARP when set) int (*poll)(struct wolfIP_ll_dev *ll, void *buf, uint32_t len); // Receive function int (*send)(struct wolfIP_ll_dev *ll, void *buf, uint32_t len); // Transmit function }; ``` wolfIP maintains an array of these descriptors sized by `WOLFIP_MAX_INTERFACES` (default `1`). Call `wolfIP_getdev_ex()` to access a specific slot; the legacy `wolfIP_getdev()` helper targets the first hardware slot (index `0` normally, or `1` when the optional loopback interface is enabled). +When `non_ethernet` is set, the interface is treated as L3-only point-to-point: the stack skips ARP/neighbor resolution, omits Ethernet headers on transmit, and expects receive buffers to begin at the IP header. + ### IP Configuration ```c struct ipconf { diff --git a/src/port/posix/linux_tun.c b/src/port/posix/linux_tun.c new file mode 100644 index 00000000..6b6195b3 --- /dev/null +++ b/src/port/posix/linux_tun.c @@ -0,0 +1,237 @@ +/* linux_tun.c + * + * Linux TUN (L3) interface for wolfIP. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfIP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WOLF_POSIX +#include "config.h" +#include "wolfip.h" +#undef WOLF_POSIX + +static int tun_fd = -1; + +static int tun_poll(struct wolfIP_ll_dev *ll, void *buf, uint32_t len) +{ + struct pollfd pfd; + int ret; + (void)ll; + if (tun_fd < 0) + return -1; + pfd.fd = tun_fd; + pfd.events = POLLIN; + ret = poll(&pfd, 1, 2); + if (ret < 0) { + perror("poll"); + return -1; + } + if (ret == 0) + return 0; + return read(tun_fd, buf, len); +} + +static int tun_send(struct wolfIP_ll_dev *ll, void *buf, uint32_t len) +{ + (void)ll; + if (tun_fd < 0) + return -1; + return write(tun_fd, buf, len); +} + +static int nl_addattr(struct nlmsghdr *nlh, size_t maxlen, int type, + const void *data, size_t alen) +{ + size_t len = RTA_LENGTH(alen); + struct rtattr *rta; + size_t newlen = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(len); + + if (newlen > maxlen) + return -1; + rta = (struct rtattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); + rta->rta_type = type; + rta->rta_len = (unsigned short)len; + if (alen > 0 && data) + memcpy(RTA_DATA(rta), data, alen); + nlh->nlmsg_len = (unsigned int)newlen; + return 0; +} + +static int tun_add_host_route(const char *ifname, uint32_t peer_ip) +{ + int fd; + int ifindex; + struct { + struct nlmsghdr nlh; + struct rtmsg rtm; + char buf[128]; + } req; + struct sockaddr_nl nladdr; + + ifindex = if_nametoindex(ifname); + if (ifindex == 0) + return -1; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.nlh.nlmsg_type = RTM_NEWROUTE; + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE; + req.nlh.nlmsg_seq = 1; + req.rtm.rtm_family = AF_INET; + req.rtm.rtm_dst_len = 32; + req.rtm.rtm_table = RT_TABLE_MAIN; + req.rtm.rtm_protocol = RTPROT_BOOT; + req.rtm.rtm_scope = RT_SCOPE_LINK; + req.rtm.rtm_type = RTN_UNICAST; + + if (nl_addattr(&req.nlh, sizeof(req), RTA_DST, &peer_ip, sizeof(peer_ip)) < 0) + return -1; + if (nl_addattr(&req.nlh, sizeof(req), RTA_OIF, &ifindex, sizeof(ifindex)) < 0) + return -1; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) + return -1; + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + if (sendto(fd, &req, req.nlh.nlmsg_len, 0, + (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) { + close(fd); + return -1; + } + close(fd); + return 0; +} + +int tun_init(struct wolfIP_ll_dev *ll, const char *ifname, + uint32_t host_ip, uint32_t peer_ip) +{ + struct ifreq ifr; + struct sockaddr_in *addr; + int sock_fd; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ - 1] = '\0'; + + tun_fd = open("/dev/net/tun", O_RDWR); + if (tun_fd < 0 || ioctl(tun_fd, TUNSETIFF, (void *)&ifr) != 0) { + perror("ioctl TUNSETIFF"); + if (tun_fd >= 0) { + close(tun_fd); + tun_fd = -1; + } + return -1; + } + { + int flags = fcntl(tun_fd, F_GETFL, 0); + if (flags >= 0) + (void)fcntl(tun_fd, F_SETFL, flags | O_NONBLOCK); + } + + if (ll) { + memset(ll->mac, 0, sizeof(ll->mac)); + strncpy(ll->ifname, ifname, sizeof(ll->ifname) - 1); + ll->ifname[sizeof(ll->ifname) - 1] = '\0'; + ll->non_ethernet = 1; + ll->poll = tun_poll; + ll->send = tun_send; + } + + sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (sock_fd < 0) { + perror("socket"); + close(tun_fd); + tun_fd = -1; + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ - 1] = '\0'; + if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) < 0) { + perror("ioctl SIOCGIFFLAGS"); + close(sock_fd); + close(tun_fd); + tun_fd = -1; + return -1; + } + ifr.ifr_flags |= (IFF_UP | IFF_RUNNING | IFF_POINTOPOINT); + if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) < 0) { + perror("ioctl SIOCSIFFLAGS"); + close(sock_fd); + close(tun_fd); + tun_fd = -1; + return -1; + } + + addr = (struct sockaddr_in *)&ifr.ifr_addr; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = host_ip; + if (ioctl(sock_fd, SIOCSIFADDR, &ifr) < 0) { + perror("ioctl SIOCSIFADDR"); + close(sock_fd); + close(tun_fd); + tun_fd = -1; + return -1; + } + + addr = (struct sockaddr_in *)&ifr.ifr_dstaddr; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = peer_ip; + if (ioctl(sock_fd, SIOCSIFDSTADDR, &ifr) < 0) { + perror("ioctl SIOCSIFDSTADDR"); + close(sock_fd); + close(tun_fd); + tun_fd = -1; + return -1; + } + + addr = (struct sockaddr_in *)&ifr.ifr_netmask; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = htonl(0xFFFFFFFFU); + if (ioctl(sock_fd, SIOCSIFNETMASK, &ifr) < 0) { + perror("ioctl SIOCSIFNETMASK"); + close(sock_fd); + close(tun_fd); + tun_fd = -1; + return -1; + } + + (void)tun_add_host_route(ifname, peer_ip); + printf("Successfully initialized tun device %s\n", ifname); + close(sock_fd); + return 0; +} diff --git a/src/test/test_eventloop_tun.c b/src/test/test_eventloop_tun.c new file mode 100644 index 00000000..1ca50c22 --- /dev/null +++ b/src/test/test_eventloop_tun.c @@ -0,0 +1,567 @@ +/* test_eventloop_tun.c + * + * Copyright (C) 2024 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfIP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "wolfip.h" + +#define TEST_SIZE (4 * 1024) + +#define BUFFER_SIZE TEST_SIZE + +static int listen_fd = -1, client_fd = -1; +static int exit_ok = 0, exit_count = 0; +static uint8_t buf[TEST_SIZE]; +static int tot_sent = 0; +static int tot_recv = 0; +static int wolfIP_closing = 0; +static int closed = 0; +static int conn_fd = -1; +static int client_connected = 0; +/* "Test pattern - -" 16 chars without trailing null. */ +static const uint8_t test_pattern[16] = {0x54, 0x65, 0x73, 0x74, 0x20, 0x70, + 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, + 0x20, 0x2d, 0x20, 0x2d}; + + +/* wolfIP: server side callback. */ +static void server_cb(int fd, uint16_t event, void *arg) +{ + int ret = 0; + if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { + client_fd = wolfIP_sock_accept((struct wolfIP *)arg, listen_fd, NULL, NULL); + if (client_fd > 0) { + printf("accept: %04x\n", client_fd); + } + } else if ((fd == client_fd) && (event & CB_EVENT_READABLE )) { + ret = wolfIP_sock_recvfrom((struct wolfIP *)arg, client_fd, buf, sizeof(buf), 0, NULL, NULL); + if (ret != -EAGAIN) { + if (ret < 0) { + printf("Recv error: %d\n", ret); + wolfIP_sock_close((struct wolfIP *)arg, client_fd); + } else if (ret == 0) { + printf("Client side closed the connection.\n"); + wolfIP_sock_close((struct wolfIP *)arg, client_fd); + printf("Server: Exiting.\n"); + exit_ok = 1; + } else if (ret > 0) { + printf("recv: %d, echoing back\n", ret); + tot_recv += ret; + } + } + } + if ((event & CB_EVENT_WRITABLE) || ((ret > 0) && !closed)) { + int snd_ret; + if ((tot_sent >= 4096) && wolfIP_closing) { + wolfIP_sock_close((struct wolfIP *)arg, client_fd); + printf("Server: I closed the connection.\n"); + closed = 1; + exit_ok = 1; + } + if ((!closed) && (tot_sent < tot_recv)) { + snd_ret = wolfIP_sock_sendto((struct wolfIP *)arg, client_fd, buf + tot_sent, tot_recv - tot_sent, 0, NULL, 0); + if (snd_ret != -EAGAIN) { + if (snd_ret < 0) { + printf("Send error: %d\n", snd_ret); + wolfIP_sock_close((struct wolfIP *)arg, client_fd); + } else { + tot_sent += snd_ret; + printf("sent %d bytes\n", snd_ret); + if (tot_recv == tot_sent) { + tot_sent = 0; + tot_recv = 0; + } + } + } + } + } + if (event & CB_EVENT_CLOSED) { + printf("Closing %d, client fd: %d\n", fd, client_fd); + } + if ((fd == client_fd) && (event & CB_EVENT_CLOSED)) { + printf("Client side closed the connection (EVENT_CLOSED)\n"); + wolfIP_sock_close((struct wolfIP *)arg, client_fd); + client_fd = -1; + printf("Server: Exiting.\n"); + exit_ok = 1; + } + (void)arg; +} + +/* Client-side callback. */ +static void client_cb(int fd, uint16_t event, void *arg) +{ + struct wolfIP *s = (struct wolfIP *)arg; + uint32_t i; + int ret; + static unsigned int total_r = 0, total_w = 0; + if (fd == conn_fd) { + if ((event & CB_EVENT_WRITABLE) && (client_connected == 0)) { + printf("Client: connected\n"); + client_connected = 1; + } + } + if (total_w == 0) { + for (i = 0; i < sizeof(buf); i += sizeof(test_pattern)) { + memcpy(buf + i, test_pattern, sizeof(test_pattern)); + } + } + if (client_connected && (event & CB_EVENT_WRITABLE) && (total_w < sizeof(buf))) { + ret = wolfIP_sock_sendto(s, fd, buf + total_w, sizeof(buf) - total_w, 0, NULL, 0); + if (ret <= 0) { + printf("Test client write: %d\n", ret); + return; + } + total_w += ret; + } + + while ((total_r < total_w) && (event & CB_EVENT_READABLE)) { + ret = wolfIP_sock_recvfrom(s, fd, buf + total_r, sizeof(buf) - total_r, 0, NULL, NULL); + if (ret < 0){ + if (ret != -EAGAIN) { + printf("Client read: %d\n", ret); + } + return; + } + if (ret == 0) { + printf("Client read: server has closed the connection.\n"); + return; + } + total_r += ret; + printf("Client RX total: %u\n", total_r); + } + if (total_r == sizeof(buf)) { + exit_ok = 1; + for (i = 0; i < sizeof(buf); i += sizeof(test_pattern)) { + if (memcmp(buf + i, test_pattern, sizeof(test_pattern))) { + char slice[sizeof(test_pattern) + 1]; + printf("test client: pattern mismatch\n"); + printf("at position %u\n", i); + memcpy(slice, buf + i, sizeof(test_pattern)); + slice[sizeof(test_pattern)] = '\0'; + printf("%s\n", slice); + return; + } + } + if (wolfIP_closing) { + wolfIP_sock_close(s, fd); + conn_fd = -1; + } + printf("Test client: success\n"); + } +} + +/* wolfIP side: main loop of the stack under test. */ +static int test_loop(struct wolfIP *s, int active_close) +{ + exit_ok = 0; + exit_count = 0; + tot_sent = 0; + wolfIP_closing = active_close; + closed = 0; + + while(1) { + uint32_t ms_next; + struct timeval tv; + gettimeofday(&tv, NULL); + ms_next = wolfIP_poll(s, tv.tv_sec * 1000 + tv.tv_usec / 1000); + usleep(ms_next * 1000); + if (exit_ok > 0) { + if (exit_count++ < 10) + continue; + else break; + } + } + return 0; +} + +/* Test code (host side). + * Thread with client to test the echoserver. + */ +void *pt_echoclient(void *arg) +{ + int fd, ret; + unsigned total_r = 0; + unsigned i; + uint8_t local_buf[BUFFER_SIZE]; + uint32_t *srv_addr = (uint32_t *)arg; + int old_flags = -1; + fd_set wfds, rfds; + struct timeval tv; + socklen_t errlen; + int err; + struct sockaddr_in remote_sock = { + .sin_family = AF_INET, + .sin_port = htons(8), /* Echo */ + }; + remote_sock.sin_addr.s_addr = *srv_addr; + fd = socket(AF_INET, IPSTACK_SOCK_STREAM, 0); + if (fd < 0) { + printf("test client socket: %d\n", fd); + return (void *)-1; + } + sleep(1); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)); + printf("Connecting to echo server\n"); + old_flags = fcntl(fd, F_GETFL, 0); + if (old_flags < 0) { + perror("fcntl(F_GETFL)"); + close(fd); + return (void *)-1; + } + if (fcntl(fd, F_SETFL, old_flags | O_NONBLOCK) < 0) { + perror("fcntl(F_SETFL)"); + close(fd); + return (void *)-1; + } + ret = connect(fd, (struct sockaddr *)&remote_sock, sizeof(remote_sock)); + if (ret < 0) { + err = errno; + printf("test client connect returned %d, errno=%d (%s)\n", ret, err, strerror(err)); + if (err != EINPROGRESS) { + perror("connect"); + close(fd); + return (void *)-1; + } + printf("Waiting for connect to complete...\n"); + while (1) { + tv.tv_sec = 5; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_SET(fd, &rfds); + FD_SET(fd, &wfds); + ret = select(fd + 1, &rfds, &wfds, NULL, &tv); + if (ret <= 0) { + printf("select returned %d (timeout or error)\n", ret); + if (ret < 0) { + perror("select"); + close(fd); + return (void *)-1; + } + } + errlen = sizeof(err); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) < 0) { + perror("getsockopt(SO_ERROR)"); + close(fd); + return (void *)-1; + } + if (err == 0) { + printf("connect completed after select()\n"); + break; + } + if (ret == 0) { + printf("connect still in progress after timeout\n"); + continue; + } + if (err != EINPROGRESS && err != EALREADY && err != EWOULDBLOCK && err != EAGAIN) { + printf("connect completed with error: %d (%s)\n", err, strerror(err)); + close(fd); + return (void *)-1; + } + } + } else { + printf("connect returned immediately\n"); + } + if (fcntl(fd, F_SETFL, old_flags) < 0) + perror("fcntl(restore)"); + printf("test client: connect succeeded\n"); + for (i = 0; i < sizeof(local_buf); i += sizeof(test_pattern)) { + memcpy(local_buf + i, test_pattern, sizeof(test_pattern)); + } + ret = write(fd, local_buf, sizeof(local_buf)); + if (ret < 0) { + int werr = errno; + printf("test client write: %d (errno=%d: %s)\n", ret, werr, strerror(werr)); + perror("write"); + return (void *)-1; + } + printf("test client: wrote %d bytes\n", ret); + while (total_r < sizeof(local_buf)) { + ret = read(fd, local_buf + total_r, sizeof(local_buf) - total_r); + if (ret < 0) { + printf("failed test client read: %d\n", ret); + return (void *)-1; + } + if (ret == 0) { + printf("test client read: server has closed the connection.\n"); + if (wolfIP_closing) + return (void *)0; + else + return (void *)-1; + } + total_r += ret; + printf("test client: read %d bytes (total %u)\n", ret, total_r); + } + for (i = 0; i < sizeof(local_buf); i += sizeof(test_pattern)) { + if (memcmp(local_buf + i, test_pattern, sizeof(test_pattern))) { + char slice[sizeof(test_pattern) + 1]; + printf("test client: pattern mismatch\n"); + printf("at position %u\n", i); + memcpy(slice, local_buf + i, sizeof(test_pattern)); + slice[sizeof(test_pattern)] = '\0'; + printf("%s\n", slice); + return (void *)-1; + } + } + close(fd); + printf("Test client: success\n"); + return (void *)0; +} + +/* Test code (host side). + * Thread with echo server to test the client. + */ +static void *pt_echoserver(void *arg) +{ + int fd, ret; + unsigned total_r = 0; + uint8_t local_buf[BUFFER_SIZE]; + struct sockaddr_in local_sock = { + .sin_family = AF_INET, + .sin_port = htons(8), /* Echo */ + .sin_addr.s_addr = 0 + }; + wolfIP_closing = (uintptr_t)arg; + fd = socket(AF_INET, IPSTACK_SOCK_STREAM, 0); + if (fd < 0) { + printf("test server socket: %d\n", fd); + return (void *)-1; + } + local_sock.sin_addr.s_addr = inet_addr(HOST_STACK_IP); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)); + ret = bind(fd, (struct sockaddr *)&local_sock, sizeof(local_sock)); + if (ret < 0) { + printf("test server bind: %d (%s)\n", ret, strerror(errno)); + return (void *)-1; + } + ret = listen(fd, 1); + if (ret < 0) { + printf("test server listen: %d\n", ret); + return (void *)-1; + } + printf("Waiting for client\n"); + ret = accept(fd, NULL, NULL); + if (ret < 0) { + printf("test server accept: %d\n", ret); + return (void *)-1; + } + printf("test server: client %d connected\n", ret); + fd = ret; + while (1) { + ret = read(fd, local_buf + total_r, sizeof(local_buf) - total_r); + if (ret < 0) { + printf("failed test server read: %d (%s) \n", ret, strerror(errno)); + return (void *)-1; + } + if (ret == 0) { + printf("test server read: client has closed the connection.\n"); + if (wolfIP_closing) + return (void *)0; + else + return (void *)-1; + } + total_r += ret; + write(fd, local_buf + total_r - ret, ret); + } +} + + +/* Network device initialization: TUN (L3) */ +#if defined(__linux__) +extern int tun_init(struct wolfIP_ll_dev *dev, const char *name, + uint32_t host_ip, uint32_t peer_ip); +#endif + +/* Test cases */ + +void test_wolfip_echoserver(struct wolfIP *s, uint32_t srv_ip) +{ + int ret, test_ret = 0; + pthread_t pt; + struct wolfIP_sockaddr_in local_sock = { + .sin_family = AF_INET, + .sin_port = ee16(8), /* Echo */ + .sin_addr.s_addr = 0 + }; + printf("TCP server tests\n"); + + listen_fd = wolfIP_sock_socket(s, AF_INET, IPSTACK_SOCK_STREAM, 0); + printf("socket: %04x\n", listen_fd); + wolfIP_register_callback(s, listen_fd, server_cb, s); + + pthread_create(&pt, NULL, pt_echoclient, &srv_ip); + printf("Starting test: echo server close-wait\n"); + ret = wolfIP_sock_bind(s, listen_fd, (struct wolfIP_sockaddr *)&local_sock, sizeof(local_sock)); + printf("bind: %d\n", ret); + ret = wolfIP_sock_listen(s, listen_fd, 1); + printf("listen: %d\n", ret); + ret = test_loop(s, 0); + pthread_join(pt, (void **)&test_ret); + printf("Test echo server close-wait: %d\n", ret); + printf("Test host client: %d\n", test_ret); + sleep(1); + + pthread_create(&pt, NULL, pt_echoclient, &srv_ip); + printf("Starting test: echo server active close\n"); + ret = test_loop(s, 1); + printf("Test echo server close-wait: %d\n", ret); + pthread_join(pt, (void **)&test_ret); + printf("Test host client: %d\n", test_ret); + sleep(1); + + wolfIP_sock_close(s, listen_fd); +} + +void test_wolfip_echoclient(struct wolfIP *s) +{ + int ret, test_ret = 0; + pthread_t pt; + struct wolfIP_sockaddr_in remote_sock; + /* Client side test: client is closing the connection */ + remote_sock.sin_family = AF_INET; + remote_sock.sin_port = ee16(8); + remote_sock.sin_addr.s_addr = inet_addr(HOST_STACK_IP); + printf("TCP client tests\n"); + conn_fd = wolfIP_sock_socket(s, AF_INET, IPSTACK_SOCK_STREAM, 0); + printf("client socket: %04x\n", conn_fd); + wolfIP_register_callback(s, conn_fd, client_cb, s); + printf("Connecting to %s:8\n", HOST_STACK_IP); + wolfIP_sock_connect(s, conn_fd, (struct wolfIP_sockaddr *)&remote_sock, sizeof(remote_sock)); + pthread_create(&pt, NULL, pt_echoserver, (void*)1); + printf("Starting test: echo client active close\n"); + ret = test_loop(s, 1); + printf("Test echo client active close: %d\n", ret); + pthread_join(pt, (void **)&test_ret); + printf("Test host server: %d\n", test_ret); + + if (conn_fd >= 0) { + wolfIP_sock_close(s, conn_fd); + conn_fd = -1; + } + + /* Client side test: server is closing the connection */ + /* Excluded for now because binding twice on port 8 is not supported */ +#if 0 + conn_fd = wolfIP_sock_socket(s, AF_INET, IPSTACK_SOCK_STREAM, 0); + if (conn_fd < 0) { + printf("cannot create socket: %d\n", conn_fd); + } + printf("client socket: %04x\n", conn_fd); + wolfIP_register_callback(s, conn_fd, client_cb, s); + printf("Connecting to %s:8\n", HOST_STACK_IP); + wolfIP_sock_connect(s, conn_fd, (struct wolfIP_sockaddr *)&remote_sock, sizeof(remote_sock)); + pthread_create(&pt, NULL, pt_echoserver, (void*)0); + printf("Starting test: echo client passive close\n"); + ret = test_loop(s, 0); + printf("Test echo client, server closing: %d\n", ret); + pthread_join(pt, (void **)&test_ret); + printf("Test host server: %d\n", test_ret); +#endif + + + +} + +/* Main test function. */ +int main(int argc, char **argv) +{ + struct wolfIP *s; + struct wolfIP_ll_dev *tapdev; + struct timeval tv = {0, 0}; + struct in_addr host_stack_ip; + struct in_addr peer_stack_ip; + uint32_t srv_ip; + ip4 ip = 0, nm = 0, gw = 0; + + (void)argc; + (void)argv; + (void)ip; + (void)nm; + (void)gw; + (void)tv; + wolfIP_init_static(&s); + tapdev = wolfIP_getdev(s); + if (!tapdev) + return 1; + inet_aton(HOST_STACK_IP, &host_stack_ip); + inet_aton(WOLFIP_IP, &peer_stack_ip); +#if defined(__linux__) + if (tun_init(tapdev, "wtun0", host_stack_ip.s_addr, peer_stack_ip.s_addr) < 0) { + perror("tun init"); + return 2; + } +#else + fprintf(stderr, "test_eventloop_tun is supported only on Linux.\n"); + return 2; +#endif + { +#if !defined(__FreeBSD__) && !defined(__APPLE__) + char cmd[128]; + snprintf(cmd, sizeof(cmd), "tcpdump -i %s -w test.pcap &", tapdev->ifname); + system(cmd); +#else + (void)tapdev; +#endif + } + +#ifdef DHCP + gettimeofday(&tv, NULL); + wolfIP_poll(s, tv.tv_sec * 1000 + tv.tv_usec / 1000); + dhcp_client_init(s); + do { + gettimeofday(&tv, NULL); + wolfIP_poll(s, tv.tv_sec * 1000 + tv.tv_usec / 1000); + usleep(1000); + wolfIP_ipconfig_get(s, &ip, &nm, &gw); + } while (!dhcp_bound(s)); + printf("DHCP: obtained IP address.\n"); + wolfIP_ipconfig_get(s, &ip, &nm, &gw); + srv_ip = htonl(ip); +#else + wolfIP_ipconfig_set(s, atoip4(WOLFIP_IP), atoip4("255.255.255.255"), + atoip4(HOST_STACK_IP)); + printf("IP: manually configured\n"); + inet_pton(AF_INET, WOLFIP_IP, &srv_ip); +#endif + + /* Server side test */ + test_wolfip_echoserver(s, srv_ip); + + /* Client side test */ + test_wolfip_echoclient(s); + +#if !defined(__FreeBSD__) && !defined(__APPLE__) + system("killall tcpdump"); +#endif + return 0; +} diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 9f63ef9c..0f899675 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -8673,8 +8673,9 @@ END_TEST START_TEST(test_forward_packet_invalid_if) { struct wolfIP s; - uint8_t buf[ETH_HEADER_LEN + IP_HEADER_LEN]; - struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)buf; + uint8_t buf[ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN]; + struct wolfIP_tcp_seg *tcp = (struct wolfIP_tcp_seg *)buf; + struct wolfIP_ip_packet *ip = &tcp->ip; wolfIP_init(&s); mock_link_init(&s); @@ -8682,6 +8683,7 @@ START_TEST(test_forward_packet_invalid_if) memset(ip, 0, sizeof(buf)); ip->proto = WI_IPPROTO_TCP; + ip->len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN); wolfIP_forward_packet(&s, s.if_count + 1, ip, (uint32_t)sizeof(buf), NULL, 1); ck_assert_uint_eq(last_frame_sent_size, 0); @@ -10162,13 +10164,87 @@ START_TEST(test_wolfip_recv_on_forward_arp_hit_sends) ck_assert_uint_eq(((struct wolfIP_ip_packet *)last_frame_sent)->ttl, 3); } END_TEST + +START_TEST(test_wolfip_forward_non_ethernet_in_to_ethernet_out) +{ + struct wolfIP s; + uint8_t ip_buf[ETH_HEADER_LEN + IP_HEADER_LEN + UDP_HEADER_LEN]; + struct wolfIP_udp_datagram *udp = (struct wolfIP_udp_datagram *)ip_buf; + struct wolfIP_ip_packet *ip = &udp->ip; + ip4 primary_ip = 0x0A000001U; + ip4 secondary_ip = 0xC0A80101U; + uint8_t mac[6] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + mock_link_init(&s); + s.ll_dev[TEST_PRIMARY_IF].non_ethernet = 1; + arp_store_neighbor(&s, TEST_SECOND_IF, 0xC0A80199U, mac); + last_frame_sent_size = 0; + + memset(ip_buf, 0, sizeof(ip_buf)); + ip->ver_ihl = 0x45; + ip->ttl = 4; + ip->proto = WI_IPPROTO_UDP; + ip->len = ee16(IP_HEADER_LEN + UDP_HEADER_LEN); + ip->src = ee32(0x0A000099U); + ip->dst = ee32(0xC0A80199U); + udp->src_port = ee16(1234); + udp->dst_port = ee16(5678); + udp->len = ee16(UDP_HEADER_LEN); + fix_ip_checksum(ip); + + wolfIP_recv_on(&s, TEST_PRIMARY_IF, ip, (uint32_t)sizeof(ip_buf)); + ck_assert_uint_eq(last_frame_sent_size, (uint32_t)sizeof(ip_buf)); + ck_assert_uint_eq(((struct wolfIP_eth_frame *)last_frame_sent)->type, ee16(ETH_TYPE_IP)); + ck_assert_mem_eq(((struct wolfIP_eth_frame *)last_frame_sent)->src, + s.ll_dev[TEST_SECOND_IF].mac, 6); + ck_assert_uint_eq(((struct wolfIP_ip_packet *)last_frame_sent)->ttl, 3); +} +END_TEST + +START_TEST(test_wolfip_forward_ethernet_in_to_non_ethernet_out) +{ + struct wolfIP s; + uint8_t ip_buf[ETH_HEADER_LEN + IP_HEADER_LEN + UDP_HEADER_LEN]; + struct wolfIP_udp_datagram *udp = (struct wolfIP_udp_datagram *)ip_buf; + struct wolfIP_ip_packet *ip = &udp->ip; + ip4 primary_ip = 0x0A000001U; + ip4 secondary_ip = 0xC0A80101U; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + mock_link_init(&s); + s.ll_dev[TEST_SECOND_IF].non_ethernet = 1; + last_frame_sent_size = 0; + + memset(ip_buf, 0, sizeof(ip_buf)); + ip->eth.type = ee16(ETH_TYPE_IP); + memcpy(ip->eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + memcpy(ip->eth.src, "\x01\x02\x03\x04\x05\x06", 6); + ip->ver_ihl = 0x45; + ip->ttl = 4; + ip->proto = WI_IPPROTO_UDP; + ip->len = ee16(IP_HEADER_LEN + UDP_HEADER_LEN); + ip->src = ee32(0x0A000099U); + ip->dst = ee32(0xC0A80199U); + udp->src_port = ee16(1234); + udp->dst_port = ee16(5678); + udp->len = ee16(UDP_HEADER_LEN); + fix_ip_checksum(ip); + + wolfIP_recv_on(&s, TEST_PRIMARY_IF, ip, (uint32_t)sizeof(ip_buf)); + ck_assert_uint_eq(last_frame_sent_size, (uint32_t)(sizeof(ip_buf) - ETH_HEADER_LEN)); + ck_assert_uint_eq(last_frame_sent[0], 0x45); + ck_assert_uint_eq(last_frame_sent[8], 3); +} +END_TEST #endif START_TEST(test_forward_packet_no_send) { struct wolfIP s; - uint8_t buf[ETH_HEADER_LEN + IP_HEADER_LEN]; - struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)buf; + uint8_t buf[ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN]; + struct wolfIP_tcp_seg *tcp = (struct wolfIP_tcp_seg *)buf; + struct wolfIP_ip_packet *ip = &tcp->ip; wolfIP_init(&s); mock_link_init(&s); @@ -10176,6 +10252,7 @@ START_TEST(test_forward_packet_no_send) memset(ip, 0, sizeof(buf)); ip->proto = WI_IPPROTO_TCP; + ip->len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN); last_frame_sent_size = 0; wolfIP_forward_packet(&s, TEST_PRIMARY_IF, ip, (uint32_t)sizeof(buf), NULL, 1); ck_assert_uint_eq(last_frame_sent_size, 0); @@ -10235,6 +10312,113 @@ START_TEST(test_forward_packet_send_paths) } END_TEST +START_TEST(test_ll_send_frame_non_ethernet_strips) +{ + struct wolfIP s; + uint8_t buf[ETH_HEADER_LEN + IP_HEADER_LEN]; + + wolfIP_init(&s); + mock_link_init(&s); + s.ll_dev[TEST_PRIMARY_IF].non_ethernet = 1; + + memset(buf, 0, sizeof(buf)); + buf[ETH_HEADER_LEN] = 0x45; + last_frame_sent_size = 0; + + wolfIP_ll_send_frame(&s, TEST_PRIMARY_IF, buf, (uint32_t)sizeof(buf)); + ck_assert_uint_eq(last_frame_sent_size, (uint32_t)(sizeof(buf) - ETH_HEADER_LEN)); + ck_assert_mem_eq(last_frame_sent, buf + ETH_HEADER_LEN, sizeof(buf) - ETH_HEADER_LEN); +} +END_TEST + +START_TEST(test_ll_send_frame_non_ethernet_short_len) +{ + struct wolfIP s; + uint8_t buf[ETH_HEADER_LEN]; + + wolfIP_init(&s); + mock_link_init(&s); + s.ll_dev[TEST_PRIMARY_IF].non_ethernet = 1; + + last_frame_sent_size = 0; + wolfIP_ll_send_frame(&s, TEST_PRIMARY_IF, buf, (uint32_t)sizeof(buf)); + ck_assert_uint_eq(last_frame_sent_size, 0); +} +END_TEST + +START_TEST(test_ll_helpers_invalid_inputs) +{ + struct wolfIP s; + uint8_t buf[4] = {0}; + + wolfIP_init(&s); + mock_link_init(&s); + last_frame_sent_size = 0; + + ck_assert_int_eq(wolfIP_ll_is_non_ethernet(NULL, 0), 0); + ck_assert_int_eq(wolfIP_ll_is_non_ethernet(&s, s.if_count), 0); + + wolfIP_ll_send_frame(NULL, 0, buf, (uint32_t)sizeof(buf)); + ck_assert_uint_eq(last_frame_sent_size, 0); + + wolfIP_ll_send_frame(&s, s.if_count, buf, (uint32_t)sizeof(buf)); + ck_assert_uint_eq(last_frame_sent_size, 0); +} +END_TEST + +START_TEST(test_non_ethernet_recv_oversize_dropped) +{ + struct wolfIP s; + struct tsocket *ts; + struct wolfIP_ll_dev *ll; + struct wolfIP_ip_packet tmp; + uint8_t *ip_hdr; + uint8_t buf[LINK_MTU]; + uint32_t local_ip = 0x0A000001U; + uint32_t src_ip = 0x0A0000A1U; + uint32_t dst_ip = local_ip; + struct { + uint16_t src_port; + uint16_t dst_port; + uint16_t len; + uint16_t csum; + } PACKED udp_hdr; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + + ll = wolfIP_getdev_ex(&s, TEST_PRIMARY_IF); + ck_assert_ptr_nonnull(ll); + ll->non_ethernet = 1; + + ts = udp_new_socket(&s); + ck_assert_ptr_nonnull(ts); + ts->src_port = 1234; + ts->local_ip = local_ip; + + memset(buf, 0, sizeof(buf)); + memset(&tmp, 0, sizeof(tmp)); + tmp.ver_ihl = 0x45; + tmp.ttl = 64; + tmp.proto = WI_IPPROTO_UDP; + tmp.len = ee16(IP_HEADER_LEN + UDP_HEADER_LEN); + tmp.src = ee32(src_ip); + tmp.dst = ee32(dst_ip); + iphdr_set_checksum(&tmp); + ip_hdr = ((uint8_t *)&tmp) + ETH_HEADER_LEN; + memcpy(buf, ip_hdr, IP_HEADER_LEN); + udp_hdr.src_port = ee16(1111); + udp_hdr.dst_port = ee16(1234); + udp_hdr.len = ee16(UDP_HEADER_LEN); + udp_hdr.csum = 0; + memcpy(buf + IP_HEADER_LEN, &udp_hdr, sizeof(udp_hdr)); + + wolfIP_recv_ex(&s, TEST_PRIMARY_IF, buf, (uint32_t)(LINK_MTU - ETH_HEADER_LEN + 1)); + ck_assert_ptr_eq(fifo_peek(&ts->sock.udp.rxbuf), NULL); +} +END_TEST + START_TEST(test_forward_packet_filter_drop_udp_icmp) { struct wolfIP s; @@ -18476,6 +18660,10 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_forward_packet_filter_drop); tcase_add_test(tc_utils, test_forward_packet_send_paths); tcase_add_test(tc_utils, test_forward_packet_filter_drop_udp_icmp); + tcase_add_test(tc_utils, test_ll_send_frame_non_ethernet_strips); + tcase_add_test(tc_utils, test_ll_send_frame_non_ethernet_short_len); + tcase_add_test(tc_utils, test_ll_helpers_invalid_inputs); + tcase_add_test(tc_utils, test_non_ethernet_recv_oversize_dropped); #endif tcase_add_test(tc_utils, test_dns_format_ptr_name); tcase_add_test(tc_utils, test_dns_skip_and_copy_name); @@ -18705,6 +18893,8 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_wolfip_recv_on_forward_ttl_exceeded); tcase_add_test(tc_proto, test_wolfip_recv_on_forward_arp_queue); tcase_add_test(tc_proto, test_wolfip_recv_on_forward_arp_hit_sends); + tcase_add_test(tc_proto, test_wolfip_forward_non_ethernet_in_to_ethernet_out); + tcase_add_test(tc_proto, test_wolfip_forward_ethernet_in_to_non_ethernet_out); #endif tcase_add_test(tc_proto, test_select_nexthop_variants); tcase_add_test(tc_proto, test_route_for_ip_variants); diff --git a/src/wolfesp.c b/src/wolfesp.c index 61069a36..003884f8 100644 --- a/src/wolfesp.c +++ b/src/wolfesp.c @@ -1664,6 +1664,9 @@ esp_send(struct wolfIP_ll_dev * ll_dev, const struct wolfIP_ip_packet *ip, uint16_t ip_final_len = len; int esp_rc = 0; + if (!ll_dev || ll_dev->non_ethernet) + return 1; + esp = (struct wolfIP_ip_packet *) frame; memcpy(esp, ip, ETH_HEADER_LEN + len); diff --git a/src/wolfip.c b/src/wolfip.c index 25f4178f..3668191b 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -1255,6 +1255,27 @@ static inline struct wolfIP_ll_dev *wolfIP_ll_at(struct wolfIP *s, unsigned int return &s->ll_dev[if_idx]; } +static inline int wolfIP_ll_is_non_ethernet(struct wolfIP *s, unsigned int if_idx) +{ + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); + return (ll && ll->non_ethernet) ? 1 : 0; +} + +static inline void wolfIP_ll_send_frame(struct wolfIP *s, unsigned int if_idx, + void *buf, uint32_t len) +{ + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); + if (!ll || !ll->send) + return; + if (ll->non_ethernet) { + if (len <= ETH_HEADER_LEN) + return; + ll->send(ll, (uint8_t *)buf + ETH_HEADER_LEN, len - ETH_HEADER_LEN); + return; + } + ll->send(ll, buf, len); +} + static inline struct ipconf *wolfIP_ipconf_at(struct wolfIP *s, unsigned int if_idx) { if (!s || if_idx >= s->if_count) @@ -1470,14 +1491,18 @@ static void wolfIP_send_ttl_exceeded(struct wolfIP *s, unsigned int if_idx, icmp.ip.dst = orig->src; icmp.ip.csum = 0; iphdr_set_checksum(&icmp.ip); - eth_output_add_header(s, if_idx, orig->eth.src, &icmp.ip.eth, ETH_TYPE_IP); + if (!wolfIP_ll_is_non_ethernet(s, if_idx)) { + eth_output_add_header(s, if_idx, orig->eth.src, &icmp.ip.eth, ETH_TYPE_IP); + } if (wolfIP_filter_notify_icmp(WOLFIP_FILT_SENDING, s, if_idx, icmp_pkt, sizeof(icmp)) != 0) return; if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, s, if_idx, &icmp.ip, sizeof(icmp)) != 0) return; - if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, s, if_idx, &icmp.ip.eth, sizeof(icmp)) != 0) - return; - ll->send(ll, &icmp, sizeof(icmp)); + if (!wolfIP_ll_is_non_ethernet(s, if_idx)) { + if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, s, if_idx, &icmp.ip.eth, sizeof(icmp)) != 0) + return; + } + wolfIP_ll_send_frame(s, if_idx, &icmp, sizeof(icmp)); } #else static void wolfIP_send_ttl_exceeded(struct wolfIP *s, unsigned int if_idx, @@ -2408,9 +2433,11 @@ static int tcp_send_zero_wnd_probe(struct tsocket *t) struct wolfIP_ll_dev *loop = wolfIP_ll_at(t->S, tx_if); if (loop) memcpy(t->nexthop_mac, loop->mac, 6); - } else if (arp_lookup(t->S, tx_if, nexthop, t->nexthop_mac) < 0) { - arp_request(t->S, tx_if, nexthop); - return -1; + } else if (!wolfIP_ll_is_non_ethernet(t->S, tx_if)) { + if (arp_lookup(t->S, tx_if, nexthop, t->nexthop_mac) < 0) { + arp_request(t->S, tx_if, nexthop); + return -1; + } } #endif ip_output_add_header(t, (struct wolfIP_ip_packet *)probe, WI_IPPROTO_TCP, @@ -2421,15 +2448,12 @@ static int tcp_send_zero_wnd_probe(struct tsocket *t) if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, t->S, tx_if, &probe->ip, sizeof(probe_frame)) != 0) return -1; #ifdef ETHERNET - if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, t->S, tx_if, &probe->ip.eth, sizeof(probe_frame)) != 0) - return -1; -#endif - { - struct wolfIP_ll_dev *ll = wolfIP_ll_at(t->S, tx_if); - if (!ll || !ll->send) + if (!wolfIP_ll_is_non_ethernet(t->S, tx_if)) { + if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, t->S, tx_if, &probe->ip.eth, sizeof(probe_frame)) != 0) return -1; - ll->send(ll, probe, sizeof(probe_frame)); } +#endif + wolfIP_ll_send_frame(t->S, tx_if, probe, sizeof(probe_frame)); return 0; } @@ -2618,6 +2642,10 @@ static int wolfIP_forward_prepare(struct wolfIP *s, unsigned int out_if, #ifdef ETHERNET if (!broadcast || !mac) return 0; + if (wolfIP_ll_is_non_ethernet(s, out_if)) { + *broadcast = 0; + return 1; + } if (wolfIP_is_loopback_if(out_if)) { struct wolfIP_ll_dev *loop = wolfIP_ll_at(s, out_if); if (loop) @@ -2649,14 +2677,13 @@ static void wolfIP_forward_packet(struct wolfIP *s, unsigned int out_if, const uint8_t *mac, int broadcast) { #ifdef ETHERNET - struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, out_if); int drop = 0; - if (!ll || !ll->send) - return; - if (broadcast) - eth_output_add_header(s, out_if, NULL, &ip->eth, ETH_TYPE_IP); - else - eth_output_add_header(s, out_if, mac, &ip->eth, ETH_TYPE_IP); + if (!wolfIP_ll_is_non_ethernet(s, out_if)) { + if (broadcast) + eth_output_add_header(s, out_if, NULL, &ip->eth, ETH_TYPE_IP); + else + eth_output_add_header(s, out_if, mac, &ip->eth, ETH_TYPE_IP); + } if (ip->proto == WI_IPPROTO_TCP) drop = wolfIP_filter_notify_tcp(WOLFIP_FILT_SENDING, s, out_if, (struct wolfIP_tcp_seg *)ip, len); @@ -2670,9 +2697,11 @@ static void wolfIP_forward_packet(struct wolfIP *s, unsigned int out_if, return; if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, s, out_if, ip, len) != 0) return; - if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, s, out_if, &ip->eth, len) != 0) - return; - ll->send(ll, ip, len); + if (!wolfIP_ll_is_non_ethernet(s, out_if)) { + if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, s, out_if, &ip->eth, len) != 0) + return; + } + wolfIP_ll_send_frame(s, out_if, ip, len); #else (void)s; (void)out_if; @@ -2724,8 +2753,10 @@ static int ip_output_add_header(struct tsocket *t, struct wolfIP_ip_packet *ip, } #ifdef ETHERNET if_idx = wolfIP_socket_if_idx(t); - eth_output_add_header(t->S, if_idx, t->nexthop_mac, (struct wolfIP_eth_frame *)ip, - ETH_TYPE_IP); + if (!wolfIP_ll_is_non_ethernet(t->S, if_idx)) { + eth_output_add_header(t->S, if_idx, t->nexthop_mac, (struct wolfIP_eth_frame *)ip, + ETH_TYPE_IP); + } #else (void)if_idx; #endif @@ -4607,7 +4638,6 @@ static void icmp_input(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_p { struct wolfIP_icmp_packet *icmp = (struct wolfIP_icmp_packet *)ip; uint32_t tmp; - struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); /* validate minimum ICMP packet length */ if (len < sizeof(struct wolfIP_icmp_packet)) @@ -4637,18 +4667,21 @@ static void icmp_input(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_p ip->csum = 0; iphdr_set_checksum(ip); #ifdef ETHERNET - eth_output_add_header(s, if_idx, ip->eth.src, &ip->eth, ETH_TYPE_IP); + if (!wolfIP_ll_is_non_ethernet(s, if_idx)) { + eth_output_add_header(s, if_idx, ip->eth.src, &ip->eth, ETH_TYPE_IP); + } #endif if (wolfIP_filter_notify_icmp(WOLFIP_FILT_SENDING, s, if_idx, icmp, len) != 0) return; if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, s, if_idx, ip, len) != 0) return; #ifdef ETHERNET - if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, s, if_idx, &ip->eth, len) != 0) - return; + if (!wolfIP_ll_is_non_ethernet(s, if_idx)) { + if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, s, if_idx, &ip->eth, len) != 0) + return; + } #endif - if (ll && ll->send) - ll->send(ll, ip, len); + wolfIP_ll_send_frame(s, if_idx, ip, len); } } @@ -5290,7 +5323,7 @@ static void arp_request(struct wolfIP *s, unsigned int if_idx, ip4 tip) struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); struct ipconf *conf; - if (!ll) + if (!ll || ll->non_ethernet) return; conf = wolfIP_ipconf_at(s, if_idx); if (!conf) @@ -5331,7 +5364,7 @@ static void arp_recv(struct wolfIP *s, unsigned int if_idx, void *buf, int len) if (len < (int)sizeof(struct arp_packet)) return; - if (!ll) + if (!ll || ll->non_ethernet) return; conf = wolfIP_ipconf_at(s, if_idx); if (!conf) @@ -5531,8 +5564,12 @@ static inline void ip_recv(struct wolfIP *s, unsigned int if_idx, /* note: esp transport mode only handled here. * ip forwarding would require esp tunnel mode. */ if (ip->proto == 0x32) { + int err; + if (wolfIP_ll_is_non_ethernet(s, if_idx)) { + return; + } /* proto is ESP 0x32 (50), try to unwrap. */ - int err = esp_transport_unwrap(ip, &len); + err = esp_transport_unwrap(ip, &len); if (err) { LOG("info: failed to unwrap esp packet, dropping.\n"); return; @@ -5576,6 +5613,11 @@ static void wolfIP_recv_on(struct wolfIP *s, unsigned int if_idx, void *buf, uin ll = wolfIP_ll_at(s, if_idx); if (!ll) return; + if (ll->non_ethernet) { + struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)buf; + ip_recv(s, if_idx, ip, len); + return; + } eth = (struct wolfIP_eth_frame *)buf; #ifdef DEBUG_ETH wolfIP_print_eth(eth, len); @@ -5605,11 +5647,33 @@ static void wolfIP_recv_on(struct wolfIP *s, unsigned int if_idx, void *buf, uin */ void wolfIP_recv(struct wolfIP *s, void *buf, uint32_t len) { + if (wolfIP_ll_is_non_ethernet(s, WOLFIP_PRIMARY_IF_IDX)) { + uint8_t frame[LINK_MTU]; + if (len > LINK_MTU - ETH_HEADER_LEN) + return; +#if ETH_HEADER_LEN > 0 + memset(frame, 0, ETH_HEADER_LEN); +#endif + memcpy(frame + ETH_HEADER_LEN, buf, len); + wolfIP_recv_on(s, WOLFIP_PRIMARY_IF_IDX, frame, len + ETH_HEADER_LEN); + return; + } wolfIP_recv_on(s, WOLFIP_PRIMARY_IF_IDX, buf, len); } void wolfIP_recv_ex(struct wolfIP *s, unsigned int if_idx, void *buf, uint32_t len) { + if (wolfIP_ll_is_non_ethernet(s, if_idx)) { + uint8_t frame[LINK_MTU]; + if (len > LINK_MTU - ETH_HEADER_LEN) + return; +#if ETH_HEADER_LEN > 0 + memset(frame, 0, ETH_HEADER_LEN); +#endif + memcpy(frame + ETH_HEADER_LEN, buf, len); + wolfIP_recv_on(s, if_idx, frame, len + ETH_HEADER_LEN); + return; + } wolfIP_recv_on(s, if_idx, buf, len); } @@ -5950,7 +6014,16 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) if (!ll || !ll->poll) continue; do { - len = ll->poll(ll, buf, LINK_MTU); + if (ll->non_ethernet) { +#if ETH_HEADER_LEN > 0 + memset(buf, 0, ETH_HEADER_LEN); +#endif + len = ll->poll(ll, buf + ETH_HEADER_LEN, LINK_MTU - ETH_HEADER_LEN); + if (len > 0) + len += ETH_HEADER_LEN; + } else { + len = ll->poll(ll, buf, LINK_MTU); + } if (len > 0) { /* Process packet */ wolfIP_recv_on(s, if_idx, buf, len); @@ -6022,10 +6095,12 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) struct wolfIP_ll_dev *loop = wolfIP_ll_at(s, tx_if); if (loop) memcpy(ts->nexthop_mac, loop->mac, 6); - } else if (arp_lookup(s, tx_if, nexthop, ts->nexthop_mac) < 0) { - /* Send ARP request */ - arp_request(s, tx_if, nexthop); - break; + } else if (!wolfIP_ll_is_non_ethernet(s, tx_if)) { + if (arp_lookup(s, tx_if, nexthop, ts->nexthop_mac) < 0) { + /* Send ARP request */ + arp_request(s, tx_if, nexthop); + break; + } } #endif { @@ -6057,24 +6132,28 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) break; } #ifdef ETHERNET - if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, ts->S, tx_if, &tcp->ip.eth, desc->len) != 0) { - break; + if (!wolfIP_ll_is_non_ethernet(ts->S, tx_if)) { + if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, ts->S, tx_if, &tcp->ip.eth, desc->len) != 0) { + break; + } } #endif { - struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, tx_if); - if (ll && ll->send) { - #ifdef WOLFIP_ESP + #ifdef WOLFIP_ESP + if (!wolfIP_ll_is_non_ethernet(s, tx_if)) { + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, tx_if); int esp_err = esp_send(ll, (struct wolfIP_ip_packet *)tcp, size); if (esp_err == 1) { /* ipsec not configured on this interface. * send plaintext. */ - ll->send(ll, tcp, desc->len); + wolfIP_ll_send_frame(s, tx_if, tcp, desc->len); } - #else - ll->send(ll, tcp, desc->len); - #endif /* WOLFIP_ESP */ + } else { + wolfIP_ll_send_frame(s, tx_if, tcp, desc->len); } + #else + wolfIP_ll_send_frame(s, tx_if, tcp, desc->len); + #endif /* WOLFIP_ESP */ } desc->flags |= PKT_FLAG_SENT; desc->flags &= ~PKT_FLAG_RETRANS; @@ -6138,13 +6217,14 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) struct wolfIP_ll_dev *loop = wolfIP_ll_at(s, tx_if); if (loop) memcpy(t->nexthop_mac, loop->mac, 6); - } else { + } else if (!wolfIP_ll_is_non_ethernet(s, tx_if)) { if ((!IS_IP_BCAST(nexthop) && (arp_lookup(s, tx_if, nexthop, t->nexthop_mac) < 0))) { /* Send ARP request */ arp_request(s, tx_if, nexthop); break; } - if (IS_IP_BCAST(nexthop)) memset(t->nexthop_mac, 0xFF, 6); + if (IS_IP_BCAST(nexthop)) + memset(t->nexthop_mac, 0xFF, 6); } #endif len = desc->len - ETH_HEADER_LEN; @@ -6154,25 +6234,29 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, t->S, tx_if, &udp->ip, desc->len) != 0) break; #ifdef ETHERNET - if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, t->S, tx_if, &udp->ip.eth, desc->len) != 0) - break; + if (!wolfIP_ll_is_non_ethernet(t->S, tx_if)) { + if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, t->S, tx_if, &udp->ip.eth, desc->len) != 0) + break; + } #endif { - struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, tx_if); - if (ll && ll->send) { - #ifdef DEBUG_UDP - wolfIP_print_udp(udp); - #endif /* DEBUG_UDP */ - #ifdef WOLFIP_ESP + #ifdef DEBUG_UDP + wolfIP_print_udp(udp); + #endif /* DEBUG_UDP */ + #ifdef WOLFIP_ESP + if (!wolfIP_ll_is_non_ethernet(s, tx_if)) { + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, tx_if); if (esp_send(ll, (struct wolfIP_ip_packet *)udp, len) == 1) { /* ipsec not configured on this interface. * send plaintext. */ - ll->send(ll, udp, desc->len); + wolfIP_ll_send_frame(s, tx_if, udp, desc->len); } - #else - ll->send(ll, udp, desc->len); - #endif /* WOLFIP_ESP */ + } else { + wolfIP_ll_send_frame(s, tx_if, udp, desc->len); } + #else + wolfIP_ll_send_frame(s, tx_if, udp, desc->len); + #endif /* WOLFIP_ESP */ } fifo_pop(&t->sock.udp.txbuf); desc = fifo_peek(&t->sock.udp.txbuf); @@ -6191,7 +6275,7 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) struct wolfIP_ll_dev *loop = wolfIP_ll_at(s, tx_if); if (loop) memcpy(t->nexthop_mac, loop->mac, 6); - } else { + } else if (!wolfIP_ll_is_non_ethernet(s, tx_if)) { if ((!IS_IP_BCAST(nexthop) && (arp_lookup(s, tx_if, nexthop, t->nexthop_mac) < 0))) { arp_request(s, tx_if, nexthop); break; @@ -6207,13 +6291,13 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, t->S, tx_if, &icmp->ip, desc->len) != 0) break; #ifdef ETHERNET - if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, t->S, tx_if, &icmp->ip.eth, desc->len) != 0) - break; + if (!wolfIP_ll_is_non_ethernet(t->S, tx_if)) { + if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, t->S, tx_if, &icmp->ip.eth, desc->len) != 0) + break; + } #endif { - struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, tx_if); - if (ll && ll->send) - ll->send(ll, icmp, desc->len); + wolfIP_ll_send_frame(s, tx_if, icmp, desc->len); } fifo_pop(&t->sock.udp.txbuf); desc = fifo_peek(&t->sock.udp.txbuf); diff --git a/wolfip.h b/wolfip.h index 309f7722..312ee912 100644 --- a/wolfip.h +++ b/wolfip.h @@ -96,6 +96,7 @@ typedef uint32_t ip4; struct wolfIP_ll_dev { uint8_t mac[6]; char ifname[16]; + uint8_t non_ethernet; /* poll function */ int (*poll)(struct wolfIP_ll_dev *ll, void *buf, uint32_t len); /* send function */