diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index fc601794..64830207 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -25,7 +25,8 @@ jobs: - name: Build linux tests run: | mkdir -p build/port - make + make EXTRA_CFLAGS="-DWOLFIP_RAWSOCKETS=1 -DWOLFIP_PACKET_SOCKETS=1" + make EXTRA_CFLAGS="-DWOLFIP_RAWSOCKETS=1 -DWOLFIP_PACKET_SOCKETS=1" build/raw_ping build/packet_ping - name: Run standalone "event loop" test timeout-minutes: 5 @@ -73,6 +74,18 @@ jobs: set -euo pipefail timeout --preserve-status 2m sudo LD_PRELOAD=$PWD/libwolfip.so ping -c 5 10.10.10.1 + - name: Testing raw sockets with raw_ping + timeout-minutes: 2 + run: | + set -euo pipefail + timeout --preserve-status 2m sudo ./build/raw_ping 10.10.10.1 + + - name: Testing packet sockets with packet_ping + timeout-minutes: 2 + run: | + set -euo pipefail + timeout --preserve-status 2m sudo ./build/packet_ping wtcp0 10.10.10.1 + - name: Install check run: | sudo apt-get install -y check diff --git a/Makefile b/Makefile index 23a345a8..742af932 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ CC?=gcc CFLAGS:=-Wall -Werror -Wextra -I. -D_GNU_SOURCE CFLAGS+=-g -ggdb -Wdeclaration-after-statement +EXTRA_CFLAGS?= +CFLAGS+=$(EXTRA_CFLAGS) LDFLAGS+=-pthread # additional debug flags: # CFLAGS+=-DDEBUG_TAP @@ -155,7 +157,8 @@ endif 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 + build/ipfilter-logger \ + build/test-esp build/esp-server LIB=libwolfip.so PREFIX=/usr/local @@ -233,6 +236,14 @@ build/tcp_netcat_select: $(OBJ) build/port/posix/bsd_socket.o build/test/tcp_net @echo "[LD] $@" @$(CC) $(CFLAGS) -o $@ $(BEGIN_GROUP) $(^) $(LDFLAGS) $(END_GROUP) +build/raw_ping: $(OBJ) build/port/posix/bsd_socket.o build/test/raw_ping.o + @echo "[LD] $@" + @$(CC) $(CFLAGS) -o $@ $(BEGIN_GROUP) $(^) $(LDFLAGS) $(END_GROUP) + +build/packet_ping: $(OBJ) build/port/posix/bsd_socket.o build/test/packet_ping.o + @echo "[LD] $@" + @$(CC) $(CFLAGS) -o $@ $(BEGIN_GROUP) $(^) $(LDFLAGS) $(END_GROUP) + build/test-wolfssl:CFLAGS+=-Wno-cpp -DWOLFSSL_DEBUG -DWOLFSSL_WOLFIP build/test-httpd:CFLAGS+=-Wno-cpp -DWOLFSSL_DEBUG -DWOLFSSL_WOLFIP -Isrc/http diff --git a/config.h b/config.h index cb48afb6..8194fecd 100644 --- a/config.h +++ b/config.h @@ -29,6 +29,28 @@ #define WOLFIP_MAX_INTERFACES 2 #endif +#ifndef WOLFIP_RAWSOCKETS +#define WOLFIP_RAWSOCKETS 0 +#endif + +#ifndef WOLFIP_MAX_RAWSOCKETS +#define WOLFIP_MAX_RAWSOCKETS 4 +#endif + +#ifndef WOLFIP_PACKET_SOCKETS +#define WOLFIP_PACKET_SOCKETS 0 +#endif + +#if WOLFIP_PACKET_SOCKETS && !defined(ETHERNET) +#undef WOLFIP_PACKET_SOCKETS +#define WOLFIP_PACKET_SOCKETS 0 +#error "WOLFIP_PACKET_SOCKETS requires ETHERNET to be defined. Please adjust your configuration." +#endif + +#ifndef WOLFIP_MAX_PACKETSOCKETS +#define WOLFIP_MAX_PACKETSOCKETS 2 +#endif + #ifndef WOLFIP_ENABLE_FORWARDING #define WOLFIP_ENABLE_FORWARDING 0 #endif diff --git a/src/port/posix/bsd_socket.c b/src/port/posix/bsd_socket.c index c2798549..60c8f4d0 100644 --- a/src/port/posix/bsd_socket.c +++ b/src/port/posix/bsd_socket.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -106,6 +110,7 @@ static int (*host_setsockopt) (int sockfd, int level, int optname, const void *o 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); +static int (*host_ioctl) (int fd, unsigned long request, ...); static int (*host_poll) (struct pollfd *fds, nfds_t nfds, int timeout); static int (*host_select) (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); @@ -113,6 +118,20 @@ static int (*host_fcntl) (int fd, int cmd, ...); #define WOLFIP_MAX_PUBLIC_FDS 256 +static int wolfip_ifindex_user_to_stack(int ifindex) +{ + if (ifindex <= 0) + return ifindex; + return ifindex - 1; +} + +static int wolfip_ifindex_stack_to_user(int ifindex) +{ + if (ifindex < 0) + return ifindex; + return ifindex + 1; +} + struct wolfip_fd_entry { int internal_fd; /* MARK_* encoded */ int public_fd; /* Returned to user; read end of pipe */ @@ -161,6 +180,12 @@ static struct wolfip_fd_entry wolfip_fd_entries[WOLFIP_MAX_PUBLIC_FDS]; static int tcp_entry_for_slot[MAX_TCPSOCKETS]; static int udp_entry_for_slot[MAX_UDPSOCKETS]; static int icmp_entry_for_slot[MAX_ICMPSOCKETS]; +#if WOLFIP_RAWSOCKETS +static int raw_entry_for_slot[WOLFIP_MAX_RAWSOCKETS]; +#endif +#if WOLFIP_PACKET_SOCKETS +static int packet_entry_for_slot[WOLFIP_MAX_PACKETSOCKETS]; +#endif enum wolfip_dns_wait_type { DNS_WAIT_NONE = 0, @@ -210,6 +235,14 @@ static void wolfip_fd_pool_init(void) udp_entry_for_slot[i] = -1; for (i = 0; i < MAX_ICMPSOCKETS; i++) icmp_entry_for_slot[i] = -1; +#if WOLFIP_RAWSOCKETS + for (i = 0; i < WOLFIP_MAX_RAWSOCKETS; i++) + raw_entry_for_slot[i] = -1; +#endif +#if WOLFIP_PACKET_SOCKETS + for (i = 0; i < WOLFIP_MAX_PACKETSOCKETS; i++) + packet_entry_for_slot[i] = -1; +#endif init_done = 1; } @@ -231,6 +264,20 @@ static struct wolfip_fd_entry *wolfip_entry_from_internal(int internal_fd) if (pos < 0 || pos >= MAX_ICMPSOCKETS) return NULL; idx = icmp_entry_for_slot[pos]; +#if WOLFIP_RAWSOCKETS + } else if (IS_SOCKET_RAW(internal_fd)) { + int pos = SOCKET_UNMARK(internal_fd); + if (pos < 0 || pos >= WOLFIP_MAX_RAWSOCKETS) + return NULL; + idx = raw_entry_for_slot[pos]; +#endif +#if WOLFIP_PACKET_SOCKETS + } else if (IS_SOCKET_PACKET(internal_fd)) { + int pos = SOCKET_UNMARK(internal_fd); + if (pos < 0 || pos >= WOLFIP_MAX_PACKETSOCKETS) + return NULL; + idx = packet_entry_for_slot[pos]; +#endif } else { return NULL; } @@ -264,6 +311,18 @@ static void wolfip_fd_detach_internal(int internal_fd) int pos = SOCKET_UNMARK(internal_fd); if (pos >= 0 && pos < MAX_ICMPSOCKETS) icmp_entry_for_slot[pos] = -1; +#if WOLFIP_RAWSOCKETS + } else if (IS_SOCKET_RAW(internal_fd)) { + int pos = SOCKET_UNMARK(internal_fd); + if (pos >= 0 && pos < WOLFIP_MAX_RAWSOCKETS) + raw_entry_for_slot[pos] = -1; +#endif +#if WOLFIP_PACKET_SOCKETS + } else if (IS_SOCKET_PACKET(internal_fd)) { + int pos = SOCKET_UNMARK(internal_fd); + if (pos >= 0 && pos < WOLFIP_MAX_PACKETSOCKETS) + packet_entry_for_slot[pos] = -1; +#endif } } @@ -281,6 +340,18 @@ static void wolfip_fd_attach_internal(int internal_fd, int entry_idx) int pos = SOCKET_UNMARK(internal_fd); if (pos >= 0 && pos < MAX_ICMPSOCKETS) icmp_entry_for_slot[pos] = entry_idx; +#if WOLFIP_RAWSOCKETS + } else if (IS_SOCKET_RAW(internal_fd)) { + int pos = SOCKET_UNMARK(internal_fd); + if (pos >= 0 && pos < WOLFIP_MAX_RAWSOCKETS) + raw_entry_for_slot[pos] = entry_idx; +#endif +#if WOLFIP_PACKET_SOCKETS + } else if (IS_SOCKET_PACKET(internal_fd)) { + int pos = SOCKET_UNMARK(internal_fd); + if (pos >= 0 && pos < WOLFIP_MAX_PACKETSOCKETS) + packet_entry_for_slot[pos] = entry_idx; +#endif } } @@ -850,6 +921,7 @@ static int wolfip_take_gai_alloc(struct addrinfo *res) int wolfIP_sock_sendmsg(struct wolfIP *ipstack, int sockfd, const struct msghdr *msg, int flags) { const struct wolfIP_sockaddr *dest = NULL; + struct wolfIP_sockaddr_ll tmp; socklen_t addrlen = 0; size_t total_len = 0; int ret; @@ -862,6 +934,14 @@ int wolfIP_sock_sendmsg(struct wolfIP *ipstack, int sockfd, const struct msghdr if (msg->msg_name && msg->msg_namelen > 0) { dest = (const struct wolfIP_sockaddr *)msg->msg_name; addrlen = msg->msg_namelen; + if (msg->msg_namelen >= sizeof(tmp)) { + const struct sockaddr *sa = (const struct sockaddr *)msg->msg_name; + if (sa->sa_family == AF_PACKET) { + memcpy(&tmp, msg->msg_name, sizeof(tmp)); + tmp.sll_ifindex = wolfip_ifindex_user_to_stack(tmp.sll_ifindex); + dest = (const struct wolfIP_sockaddr *)&tmp; + } + } } if (msg->msg_iovlen == 1) { payload = msg->msg_iov[0].iov_base; @@ -935,6 +1015,13 @@ int wolfIP_sock_recvmsg(struct wolfIP *ipstack, int sockfd, struct msghdr *msg, msg->msg_namelen = addrlen; msg->msg_flags = 0; wolfip_fill_ttl_control(ipstack, sockfd, msg); + if (src && msg->msg_namelen >= sizeof(struct wolfIP_sockaddr_ll)) { + struct sockaddr *sa = (struct sockaddr *)src; + if (sa->sa_family == AF_PACKET) { + struct wolfIP_sockaddr_ll *sll = (struct wolfIP_sockaddr_ll *)src; + sll->sll_ifindex = wolfip_ifindex_stack_to_user(sll->sll_ifindex); + } + } } if (ret == -WOLFIP_EAGAIN) return -EWOULDBLOCK; @@ -1141,6 +1228,105 @@ int wolfIP_sock_select(struct wolfIP *ipstack, int nfds, fd_set *readfds, fd_set return ret; } +int ioctl(int fd, unsigned long request, ...) +{ + va_list ap; + uintptr_t arg; + void *argp; + struct ifreq *ifr; + int i; + + va_start(ap, request); + arg = va_arg(ap, uintptr_t); + va_end(ap); + argp = (void *)arg; + + if (in_the_stack) { + return host_ioctl ? host_ioctl(fd, request, arg) : -1; + } + + if (request == SIOCGIFINDEX || request == SIOCGIFHWADDR || request == SIOCGIFADDR) { + struct wolfip_fd_entry *entry = wolfip_entry_from_public(fd); + if (!entry) { + return host_ioctl ? host_ioctl(fd, request, arg) : -1; + } + ifr = (struct ifreq *)argp; + if (!ifr) { + errno = EINVAL; + return -1; + } + for (i = 0; i < WOLFIP_MAX_INTERFACES; i++) { + struct wolfIP_ll_dev *ll = wolfIP_getdev_ex(IPSTACK, (unsigned int)i); + if (!ll) + continue; + if (ifr->ifr_name[0] != '\0' && + strncmp(ifr->ifr_name, (char *)ll->ifname, IFNAMSIZ) != 0) + continue; + if (request == SIOCGIFINDEX) { + ifr->ifr_ifindex = wolfip_ifindex_stack_to_user(i); + return 0; + } else if (request == SIOCGIFHWADDR) { + ifr->ifr_hwaddr.sa_family = ARPHRD_ETHER; + memcpy(ifr->ifr_hwaddr.sa_data, ll->mac, 6); + return 0; + } else if (request == SIOCGIFADDR) { + struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr; + ip4 ip = 0, mask = 0, gw = 0; + memset(sin, 0, sizeof(*sin)); + wolfIP_ipconfig_get_ex(IPSTACK, (unsigned int)i, &ip, &mask, &gw); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = ee32(ip); + return 0; + } + } + errno = ENODEV; + return -1; + } + + if (request == SIOCGARP) { + struct arpreq *ar = (struct arpreq *)argp; + struct sockaddr_in *pa; + uint8_t mac[6]; + unsigned int if_idx; + int ret; + + if (!ar) { + errno = EINVAL; + return -1; + } + if (ar->arp_pa.sa_family != AF_INET) { + errno = EAFNOSUPPORT; + return -1; + } + pa = (struct sockaddr_in *)&ar->arp_pa; + if_idx = 0; + if (ar->arp_dev[0] != '\0') { + for (if_idx = 0; if_idx < WOLFIP_MAX_INTERFACES; if_idx++) { + struct wolfIP_ll_dev *ll = wolfIP_getdev_ex(IPSTACK, if_idx); + if (!ll) + continue; + if (strncmp(ar->arp_dev, (char *)ll->ifname, IFNAMSIZ) == 0) + break; + } + if (if_idx >= WOLFIP_MAX_INTERFACES) { + errno = ENODEV; + return -1; + } + } + ret = wolfIP_arp_lookup_ex(IPSTACK, if_idx, ee32(pa->sin_addr.s_addr), mac); + if (ret < 0) { + errno = ENXIO; + return -1; + } + ar->arp_ha.sa_family = ARPHRD_ETHER; + memcpy(ar->arp_ha.sa_data, mac, 6); + ar->arp_flags = ATF_COM; + return 0; + } + + return host_ioctl ? host_ioctl(fd, request, arg) : -1; +} + int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { int ret; if (in_the_stack) { @@ -1160,6 +1346,14 @@ int socket(int domain, int type, int protocol) { if (in_the_stack) { return host_socket(domain, type, protocol); } +#if !WOLFIP_PACKET_SOCKETS + if (domain == AF_PACKET) + return host_socket(domain, type, protocol); +#endif +#if !WOLFIP_RAWSOCKETS + if (base_type == SOCK_RAW) + return host_socket(domain, type, protocol); +#endif internal_fd = wolfIP_sock_socket(IPSTACK, domain, base_type, protocol); if (internal_fd < 0) { errno = -internal_fd; @@ -1179,7 +1373,37 @@ int listen(int sockfd, int backlog) { } int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { - conditional_steal_call(bind, sockfd, addr, addrlen); + if (in_the_stack) { + return host_bind(sockfd, addr, addrlen); + } else { + int internal_fd = wolfip_fd_internal_from_public(sockfd); + pthread_mutex_lock(&wolfIP_mutex); + if (internal_fd >= 0) { + int ret; + if (addr && addrlen >= sizeof(struct wolfIP_sockaddr_ll) && + addr->sa_family == AF_PACKET) { + struct wolfIP_sockaddr_ll tmp; + memcpy(&tmp, addr, sizeof(tmp)); + tmp.sll_ifindex = wolfip_ifindex_user_to_stack(tmp.sll_ifindex); + ret = wolfIP_sock_bind(IPSTACK, internal_fd, + (const struct wolfIP_sockaddr *)&tmp, addrlen); + } else { + ret = wolfIP_sock_bind(IPSTACK, internal_fd, + (const struct wolfIP_sockaddr *)addr, addrlen); + } + if (ret < 0) { + errno = ret; + pthread_mutex_unlock(&wolfIP_mutex); + return -1; + } + pthread_mutex_unlock(&wolfIP_mutex); + errno = 0; + return ret; + } else { + pthread_mutex_unlock(&wolfIP_mutex); + return host_bind(sockfd, addr, addrlen); + } + } } int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) { @@ -1294,7 +1518,56 @@ int connect(int sockfd, const struct sockaddr *addr, socklen_t 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, POLLIN, buf, len, flags, addr, addrlen); + if (in_the_stack) { + return host_recvfrom(sockfd, buf, len, flags, addr, addrlen); + } else { + int internal_fd = wolfip_fd_internal_from_public(sockfd); + pthread_mutex_lock(&wolfIP_mutex); + if (internal_fd >= 0) { + ssize_t ret; + int nonblock = wolfip_fd_is_nonblock(sockfd); + struct wolfip_fd_entry *entry = wolfip_entry_from_public(sockfd); + do { + ret = wolfIP_sock_recvfrom(IPSTACK, internal_fd, buf, len, flags, + (struct wolfIP_sockaddr *)addr, addrlen); + if (ret >= 0) { + if (addr && addrlen && *addrlen >= sizeof(struct wolfIP_sockaddr_ll) && + addr->sa_family == AF_PACKET) { + struct wolfIP_sockaddr_ll *sll = (struct wolfIP_sockaddr_ll *)addr; + sll->sll_ifindex = wolfip_ifindex_stack_to_user(sll->sll_ifindex); + } + pthread_mutex_unlock(&wolfIP_mutex); + return ret; + } + if (ret != -EAGAIN) + break; + if (nonblock) { + errno = EAGAIN; + pthread_mutex_unlock(&wolfIP_mutex); + return -1; + } + if (entry) { + int wait_ret = wolfip_wait_for_event_locked(entry, POLLIN, + entry->rcv_timeout_ms); + if (wait_ret < 0) { + errno = -wait_ret; + pthread_mutex_unlock(&wolfIP_mutex); + return -1; + } + } else { + pthread_mutex_unlock(&wolfIP_mutex); + usleep(1000); + pthread_mutex_lock(&wolfIP_mutex); + } + } while (1); + errno = ret; + pthread_mutex_unlock(&wolfIP_mutex); + return -1; + } else { + pthread_mutex_unlock(&wolfIP_mutex); + return host_recvfrom(sockfd, buf, len, flags, addr, addrlen); + } + } } ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) { @@ -1409,6 +1682,8 @@ ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct int wait_ret; struct wolfip_fd_entry *entry; size_t sent = 0; + struct wolfIP_sockaddr_ll tmp; + const struct sockaddr *use_addr = addr; if (in_the_stack) { return host_sendto(sockfd, buf, len, flags, addr, addrlen); @@ -1422,10 +1697,15 @@ ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct nonblock = wolfip_fd_is_nonblock(sockfd); is_stream = IS_SOCKET_TCP(internal_fd) ? 1 : 0; entry = wolfip_entry_from_public(sockfd); + if (addr && addrlen >= sizeof(tmp) && addr->sa_family == AF_PACKET) { + memcpy(&tmp, addr, sizeof(tmp)); + tmp.sll_ifindex = wolfip_ifindex_user_to_stack(tmp.sll_ifindex); + use_addr = (const struct sockaddr *)&tmp; + } while (sent < len) { ret = wolfIP_sock_sendto(IPSTACK, internal_fd, (const uint8_t *)buf + sent, len - sent, flags, - (const struct wolfIP_sockaddr *)addr, addrlen); + (const struct wolfIP_sockaddr *)use_addr, addrlen); if (ret > 0) { sent += (size_t)ret; if (nonblock || !is_stream) @@ -1682,6 +1962,7 @@ void __attribute__((constructor)) init_wolfip_posix() { swap_socketcall(poll, "poll"); swap_socketcall(select, "select"); swap_socketcall(fcntl, "fcntl"); + swap_socketcall(ioctl, "ioctl"); pthread_mutex_init(&wolfIP_mutex, NULL); wolfIP_init_static(&IPSTACK); diff --git a/src/test/packet_ping.c b/src/test/packet_ping.c new file mode 100644 index 00000000..249852c1 --- /dev/null +++ b/src/test/packet_ping.c @@ -0,0 +1,316 @@ +/* + * packet_ping.c + * + * Copyright (C) 2025 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 + * + * Very simplified ping-like utility using: + * socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP)) + * + * Sends 3 ICMP echo requests of 64 bytes (8B header + 56B data) + * to the host given, via the specified interface. + * + * Needs root or CAP_NET_RAW. + * + * Usage: + * packet_raw_ping + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#define NUM_PINGS_PACKET 3 +#define NUM_PINGS_INET 2 + +#define ICMP_DATA_SIZE 56 +#define ICMP_HEADER_SIZE (sizeof(struct icmphdr)) +#define ICMP_PACKET_SIZE (ICMP_HEADER_SIZE + ICMP_DATA_SIZE) + +#define IP_HEADER_SIZE (sizeof(struct iphdr)) +#define IP_PACKET_SIZE (IP_HEADER_SIZE + ICMP_PACKET_SIZE) + +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif + +#ifndef ETH_HLEN +#define ETH_HLEN 14 +#endif + +#define ETH_FRAME_SIZE (ETH_HLEN + IP_PACKET_SIZE) +#define RECV_BUF_SIZE 2048 + +#ifndef ETH_P_IP +/* Fallback if ETH_P_IP is not defined */ +#define ETH_P_IP 0x0800 +#endif + +static unsigned short checksum(void *b, int len) +{ + unsigned short *buf; + unsigned int sum; + unsigned short result; + + buf = (unsigned short *)b; + sum = 0; + + while (len > 1) { + sum += *buf++; + len -= 2; + } + + if (len == 1) { + sum += *(unsigned char *)buf; + } + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + result = (unsigned short)~sum; + + return result; +} + +/* Get interface index, MAC address and IPv4 address */ +static int get_iface_info(const char *ifname, + int *ifindex, + unsigned char *mac, + struct in_addr *ip) +{ + int fd; + struct ifreq ifr; + struct sockaddr_in *sin; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("socket(AF_INET,SOCK_DGRAM)"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); + + /* Get index */ + if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) { + perror("ioctl(SIOCGIFINDEX)"); + close(fd); + return -1; + } + *ifindex = ifr.ifr_ifindex; + + /* Get MAC address */ + if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) { + perror("ioctl(SIOCGIFHWADDR)"); + close(fd); + return -1; + } + memcpy(mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + /* Get IPv4 address */ + if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) { + perror("ioctl(SIOCGIFADDR)"); + close(fd); + return -1; + } + sin = (struct sockaddr_in *)&ifr.ifr_addr; + *ip = sin->sin_addr; + + close(fd); + return 0; +} + +/* Build an IPv4 + ICMP echo request packet */ +static void build_icmp_packet(char *buf, size_t buflen, + struct in_addr src_ip, + struct in_addr dst_ip, + uint16_t ident, + uint16_t seq) +{ + struct iphdr *ip = (struct iphdr *)buf; + struct icmphdr *icmp = (struct icmphdr *)(buf + sizeof(struct iphdr)); + unsigned char *data = (unsigned char *)(icmp + 1); + int icmp_len = (int)ICMP_PACKET_SIZE; + + memset(buf, 0, buflen); + + icmp->type = ICMP_ECHO; + icmp->code = 0; + icmp->un.echo.id = htons(ident); + icmp->un.echo.sequence = htons(seq); + memset(data, 0xAB, ICMP_DATA_SIZE); + + icmp->checksum = 0; + icmp->checksum = checksum(icmp, icmp_len); + + ip->ihl = 5; + ip->version = 4; + ip->tos = 0; + ip->tot_len = htons(IP_PACKET_SIZE); + ip->id = htons(seq); + ip->frag_off = 0; + ip->ttl = 64; + ip->protocol = IPPROTO_ICMP; + ip->check = 0; + ip->saddr = src_ip.s_addr; + ip->daddr = dst_ip.s_addr; + ip->check = checksum(ip, IP_HEADER_SIZE); +} + +int main(int argc, char *argv[]) +{ + int sockfd; + struct addrinfo hints; + struct addrinfo *res = NULL; + const char *host; + const char *iface; + + unsigned char if_mac[ETH_ALEN]; + struct in_addr if_ip; + int ifindex; + pid_t pid; + + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + iface = argv[1]; + host = argv[2]; + + if (get_iface_info(iface, &ifindex, if_mac, &if_ip) < 0) { + fprintf(stderr, "Failed to get iface info.\n"); + return 1; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_RAW; + hints.ai_protocol = IPPROTO_ICMP; + + if (getaddrinfo(host, NULL, &hints, &res) != 0) { + perror("getaddrinfo"); + return 1; + } + + sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP)); + if (sockfd < 0) { + perror("socket(AF_PACKET)"); + freeaddrinfo(res); + return 1; + } + + printf("PACKET PING %s (%s) on iface %s\n", host, + inet_ntoa(((struct sockaddr_in *)res->ai_addr)->sin_addr), iface); + + pid = getpid() & 0xFFFF; + + for (int i = 1; i <= NUM_PINGS_PACKET; i++) { + char sendbuf[ETH_FRAME_SIZE]; + struct ethhdr *eth = (struct ethhdr *)sendbuf; + char ip_icmp[IP_PACKET_SIZE]; + struct sockaddr_ll addr = {0}; + + /* Build IP+ICMP */ + build_icmp_packet(ip_icmp, sizeof(ip_icmp), + if_ip, + ((struct sockaddr_in *)res->ai_addr)->sin_addr, + (uint16_t)pid, (uint16_t)i); + + /* Eth header */ + memset(sendbuf, 0, sizeof(sendbuf)); + memset(eth->h_dest, 0xFF, ETH_ALEN); /* broadcast for simplicity */ + memcpy(eth->h_source, if_mac, ETH_ALEN); + eth->h_proto = htons(ETH_P_IP); + + /* Copy IP+ICMP */ + memcpy(sendbuf + ETH_HLEN, ip_icmp, IP_PACKET_SIZE); + + addr.sll_family = AF_PACKET; + addr.sll_protocol = htons(ETH_P_IP); + addr.sll_ifindex = ifindex; + addr.sll_halen = ETH_ALEN; + memset(addr.sll_addr, 0xFF, ETH_ALEN); + + if (sendto(sockfd, sendbuf, ETH_FRAME_SIZE, 0, + (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("sendto"); + continue; + } + + /* Receive */ + { + char recvbuf[RECV_BUF_SIZE]; + ssize_t n; + struct sockaddr_ll from; + socklen_t fromlen = sizeof(from); + struct ethhdr *reth; + struct iphdr *riph; + struct icmphdr *ricmp; + + n = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, + (struct sockaddr *)&from, &fromlen); + if (n < 0) { + perror("recvfrom"); + continue; + } + + if ((size_t)n < sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct icmphdr)) + continue; + + reth = (struct ethhdr *)recvbuf; + if (ntohs(reth->h_proto) != ETH_P_IP) + continue; + + riph = (struct iphdr *)(recvbuf + ETH_HLEN); + if (riph->protocol != IPPROTO_ICMP) + continue; + + ricmp = (struct icmphdr *)(recvbuf + ETH_HLEN + (riph->ihl * 4)); + if (ricmp->type == ICMP_ECHOREPLY && + ntohs(ricmp->un.echo.id) == (uint16_t)pid) { + printf("Reply from %s: icmp_seq=%d\n", + inet_ntoa(((struct sockaddr_in *)res->ai_addr)->sin_addr), i); + } + } + + sleep(1); + } + + freeaddrinfo(res); + close(sockfd); + return 0; +} diff --git a/src/test/raw_ping.c b/src/test/raw_ping.c new file mode 100644 index 00000000..f26d67a2 --- /dev/null +++ b/src/test/raw_ping.c @@ -0,0 +1,196 @@ +/* raw_ping.c + * + * Copyright (C) 2025 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 + +#define PACKET_SIZE 64 +#define NUM_PINGS 3 + +static unsigned short icmp_checksum(void *b, int len) +{ + unsigned short *buf; + unsigned int sum; + unsigned short result; + + buf = (unsigned short *)b; + sum = 0; + + while (len > 1) { + sum += *buf++; + len -= 2; + } + + if (len == 1) { + sum += *(unsigned char *)buf; + } + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + result = (unsigned short)~sum; + + return result; +} + +int main(int argc, char *argv[]) +{ + int sockfd; + struct addrinfo hints; + struct addrinfo *res; + int ret; + const char *host; + struct sockaddr_in *addr_in; + pid_t pid; + int seq; + int replies = 0; + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + host = argv[1]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; /* IPv4 */ + hints.ai_socktype = SOCK_RAW; /* raw socket */ + hints.ai_protocol = IPPROTO_ICMP; /* ICMP */ + + ret = getaddrinfo(host, NULL, &hints, &res); + if (ret != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); + return 1; + } + + sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (sockfd < 0) { + perror("socket (need root / CAP_NET_RAW)"); + freeaddrinfo(res); + return 1; + } + + addr_in = (struct sockaddr_in *)res->ai_addr; + + printf("PING %s (%s): %d bytes ICMP data, %d probes\n", + host, + inet_ntoa(addr_in->sin_addr), + PACKET_SIZE - (int)sizeof(struct icmphdr), + NUM_PINGS); + + pid = getpid() & 0xFFFF; + + for (seq = 1; seq <= NUM_PINGS; ++seq) { + unsigned char packet[PACKET_SIZE]; + struct icmphdr *icmp; + int payload_size; + unsigned char *data; + struct timeval send_time; + struct timeval recv_time; + ssize_t sent; + unsigned char recvbuf[1024]; + struct sockaddr_in src; + socklen_t srclen = sizeof(src); + int n; + struct iphdr *ip_hdr; + struct icmphdr *icmp_hdr; + double rtt; + + memset(packet, 0, sizeof(packet)); + icmp = (struct icmphdr *)packet; + icmp->type = ICMP_ECHO; + icmp->code = 0; + icmp->un.echo.id = htons((uint16_t)pid); + icmp->un.echo.sequence = htons((uint16_t)seq); + + payload_size = PACKET_SIZE - (int)sizeof(struct icmphdr); + data = packet + sizeof(struct icmphdr); + memset(data, 0xA5, payload_size); + + gettimeofday(&send_time, NULL); + memcpy(data, &send_time, sizeof(send_time)); + + icmp->checksum = 0; + icmp->checksum = icmp_checksum(packet, PACKET_SIZE); + + sent = sendto(sockfd, packet, PACKET_SIZE, 0, + (struct sockaddr *)addr_in, sizeof(*addr_in)); + if (sent < 0) { + perror("sendto"); + continue; + } + + n = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, + (struct sockaddr *)&src, &srclen); + if (n < 0) { + perror("recvfrom"); + continue; + } + + ip_hdr = (struct iphdr *)recvbuf; + icmp_hdr = (struct icmphdr *)(recvbuf + (ip_hdr->ihl * 4)); + + if (icmp_hdr->type == ICMP_ECHOREPLY && + ntohs(icmp_hdr->un.echo.id) == (uint16_t)pid) { + + gettimeofday(&recv_time, NULL); + { + struct timeval *sent_time_ptr = (struct timeval *)(icmp_hdr + 1); + double sent_ms = sent_time_ptr->tv_sec * 1000.0 + + sent_time_ptr->tv_usec / 1000.0; + double recv_ms = recv_time.tv_sec * 1000.0 + + recv_time.tv_usec / 1000.0; + rtt = recv_ms - sent_ms; + } + + printf("%d bytes from %s: icmp_seq=%d ttl=%d time=%.3f ms\n", + n - (int)(ip_hdr->ihl * 4), + inet_ntoa(src.sin_addr), + ntohs(icmp_hdr->un.echo.sequence), + ip_hdr->ttl, + rtt); + + replies++; + } + + sleep(1); + } + + close(sockfd); + freeaddrinfo(res); + + if (replies == 0) { + fprintf(stderr, "No replies received.\n"); + return 1; + } + + return 0; +} diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 48a9b862..0d07050e 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -24,6 +24,10 @@ #define DEBUG_UDP 1 #undef CONFIG_IPFILTER #define CONFIG_IPFILTER 1 +#undef WOLFIP_RAWSOCKETS +#define WOLFIP_RAWSOCKETS 1 +#undef WOLFIP_PACKET_SOCKETS +#define WOLFIP_PACKET_SOCKETS 1 #undef WOLFIP_MAX_INTERFACES #define WOLFIP_MAX_INTERFACES 3 #undef WOLFIP_ENABLE_LOOPBACK @@ -581,6 +585,677 @@ START_TEST(test_fifo_init) } END_TEST +START_TEST(test_raw_socket_recv_captures_ip_header) +{ + struct wolfIP s; + int sd; + uint8_t frame_buf[sizeof(struct wolfIP_ip_packet) + 8]; + struct wolfIP_ip_packet *frame = (struct wolfIP_ip_packet *)frame_buf; + uint8_t payload[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + struct wolfIP_sockaddr_in from; + socklen_t from_len = sizeof(from); + uint8_t rxbuf[64]; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_RAW, WI_IPPROTO_UDP); + ck_assert_int_ge(sd, 0); + + memset(frame, 0, sizeof(frame_buf)); + memcpy(frame->eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + memcpy(frame->eth.src, "\xaa\xbb\xcc\xdd\xee\xff", 6); + frame->eth.type = ee16(ETH_TYPE_IP); + frame->ver_ihl = 0x45; + frame->ttl = 32; + frame->proto = WI_IPPROTO_UDP; + frame->len = ee16(IP_HEADER_LEN + sizeof(payload)); + frame->src = ee32(0x0A000002U); + frame->dst = ee32(0x0A000001U); + memcpy(frame->data, payload, sizeof(payload)); + frame->csum = 0; + iphdr_set_checksum(frame); + + wolfIP_recv_ex(&s, TEST_PRIMARY_IF, frame, ETH_HEADER_LEN + IP_HEADER_LEN + sizeof(payload)); + + memset(rxbuf, 0, sizeof(rxbuf)); + memset(&from, 0, sizeof(from)); + from_len = sizeof(from); + ck_assert_int_eq(wolfIP_sock_recvfrom(&s, sd, rxbuf, sizeof(rxbuf), 0, + (struct wolfIP_sockaddr *)&from, &from_len), + IP_HEADER_LEN + (int)sizeof(payload)); + ck_assert_mem_eq(rxbuf, ((uint8_t *)frame) + ETH_HEADER_LEN, + IP_HEADER_LEN + sizeof(payload)); + ck_assert_uint_eq(ee32(from.sin_addr.s_addr), ee32(frame->src)); + ck_assert_uint_eq(from_len, sizeof(from)); +} +END_TEST + +START_TEST(test_raw_socket_send_hdrincl_respected) +{ + struct wolfIP s; + int sd; + uint8_t ip_buf[sizeof(struct wolfIP_ip_packet) + 4]; + struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)ip_buf; + struct wolfIP_sockaddr_in sin; + uint8_t payload[4] = {0xAA, 0xBB, 0xCC, 0xDD}; + uint32_t dst_ip = 0x0A000002U; + uint8_t nh_mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60}; + int one = 1; + uint8_t *wire_ip; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + s.arp.neighbors[0].ip = dst_ip; + s.arp.neighbors[0].if_idx = TEST_PRIMARY_IF; + memcpy(s.arp.neighbors[0].mac, nh_mac, 6); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_RAW, WI_IPPROTO_ICMP); + ck_assert_int_ge(sd, 0); + ck_assert_int_eq(wolfIP_sock_setsockopt(&s, sd, WOLFIP_SOL_IP, WOLFIP_IP_HDRINCL, + &one, sizeof(one)), 0); + + memset(ip, 0, sizeof(ip_buf)); + ip->ver_ihl = 0x45; + ip->ttl = 99; + ip->proto = WI_IPPROTO_ICMP; + ip->len = ee16(IP_HEADER_LEN + sizeof(payload)); + ip->src = ee32(0x0A000001U); + ip->dst = ee32(dst_ip); + memcpy(ip->data, payload, sizeof(payload)); + ip->csum = 0; + iphdr_set_checksum(ip); + wire_ip = ((uint8_t *)ip) + ETH_HEADER_LEN; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ee32(dst_ip); + + last_frame_sent_size = 0; + memset(last_frame_sent, 0, sizeof(last_frame_sent)); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, sd, wire_ip, + IP_HEADER_LEN + sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), + (int)(IP_HEADER_LEN + sizeof(payload))); + wolfIP_poll(&s, 0); + + ck_assert_uint_eq(last_frame_sent_size, ETH_HEADER_LEN + IP_HEADER_LEN + sizeof(payload)); + { + struct wolfIP_ip_packet *sent = (struct wolfIP_ip_packet *)last_frame_sent; + ck_assert_uint_eq(sent->ttl, 99); + ck_assert_uint_eq(sent->proto, WI_IPPROTO_ICMP); + ck_assert_uint_eq(sent->len, ee16(IP_HEADER_LEN + sizeof(payload))); + ck_assert_uint_eq(sent->src, ip->src); + ck_assert_uint_eq(sent->dst, ip->dst); + ck_assert_mem_eq(sent->data, payload, sizeof(payload)); + ck_assert_mem_eq(sent->eth.dst, nh_mac, 6); + ck_assert_mem_eq(sent->eth.src, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + } +} +END_TEST + +START_TEST(test_raw_socket_send_builds_ip_header) +{ + struct wolfIP s; + int sd; + uint8_t payload[6] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + struct wolfIP_sockaddr_in sin; + uint32_t dst_ip = 0x0A00000BU; + uint8_t nh_mac[6] = {0x21, 0x22, 0x23, 0x24, 0x25, 0x26}; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + s.arp.neighbors[0].ip = dst_ip; + s.arp.neighbors[0].if_idx = TEST_PRIMARY_IF; + memcpy(s.arp.neighbors[0].mac, nh_mac, sizeof(nh_mac)); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_RAW, WI_IPPROTO_UDP); + ck_assert_int_ge(sd, 0); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ee32(dst_ip); + + last_frame_sent_size = 0; + memset(last_frame_sent, 0, sizeof(last_frame_sent)); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(payload)); + wolfIP_poll(&s, 0); + + ck_assert_uint_eq(last_frame_sent_size, ETH_HEADER_LEN + IP_HEADER_LEN + sizeof(payload)); + { + struct wolfIP_ip_packet *sent = (struct wolfIP_ip_packet *)last_frame_sent; + ck_assert_uint_eq(sent->ver_ihl, 0x45); + ck_assert_uint_eq(ee16(sent->len), IP_HEADER_LEN + sizeof(payload)); + ck_assert_uint_eq(sent->proto, WI_IPPROTO_UDP); + ck_assert_uint_eq(sent->dst, ee32(dst_ip)); + ck_assert_uint_eq(sent->src, ee32(0x0A000001U)); + ck_assert_uint_ne(sent->csum, 0); + ck_assert_mem_eq(sent->data, payload, sizeof(payload)); + ck_assert_mem_eq(sent->eth.dst, nh_mac, 6); + ck_assert_mem_eq(sent->eth.src, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + } +} +END_TEST + +START_TEST(test_raw_socket_sendto_short_addrlen_returns_einval) +{ + struct wolfIP s; + int sd; + struct wolfIP_sockaddr_in sin; + uint8_t payload[4] = {0xDE, 0xAD, 0xBE, 0xEF}; + socklen_t bad_len = (socklen_t)(sizeof(struct wolfIP_sockaddr_in) - 1); + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_RAW, WI_IPPROTO_UDP); + ck_assert_int_ge(sd, 0); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, bad_len), + -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_getsockopt_invalid_fd_returns_einval) +{ + struct wolfIP s; + int value = 0; + socklen_t optlen = sizeof(value); + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_getsockopt(&s, -1, WOLFIP_SOL_IP, WOLFIP_IP_RECVTTL, + &value, &optlen), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_getsockopt_invalid_raw_fd_returns_einval) +{ + struct wolfIP s; + int value = 0; + socklen_t optlen = sizeof(value); + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_getsockopt(&s, MARK_RAW_SOCKET | WOLFIP_MAX_RAWSOCKETS, + WOLFIP_SOL_IP, WOLFIP_IP_RECVTTL, &value, &optlen), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_getsockopt_invalid_packet_fd_returns_einval) +{ + struct wolfIP s; + int value = 0; + socklen_t optlen = sizeof(value); + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_getsockopt(&s, MARK_PACKET_SOCKET | WOLFIP_MAX_PACKETSOCKETS, + WOLFIP_SOL_IP, WOLFIP_IP_RECVTTL, &value, &optlen), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_getsockopt_unsupported_option_returns_einval) +{ + struct wolfIP s; + int value = 0; + socklen_t optlen = sizeof(value); + int sd; + + wolfIP_init(&s); + mock_link_init(&s); + + sd = wolfIP_sock_socket(&s, AF_INET, SOCK_DGRAM, 0); + ck_assert_int_ge(sd, 0); + ck_assert_int_eq(wolfIP_sock_getsockopt(&s, sd, WOLFIP_SOL_IP, 0x1234, + &value, &optlen), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_packet_socket_recv_frame) +{ +#if WOLFIP_PACKET_SOCKETS + struct wolfIP s; + int sd; + uint8_t frame_buf[ETH_HEADER_LEN + 6]; + struct wolfIP_eth_frame *frame = (struct wolfIP_eth_frame *)frame_buf; + uint8_t payload[6] = {0, 1, 2, 3, 4, 5}; + struct wolfIP_sockaddr_ll sll; + struct wolfIP_sockaddr_ll bind_sll; + socklen_t sll_len = sizeof(sll); + uint8_t rxbuf[sizeof(struct wolfIP_eth_frame) + sizeof(payload)]; + struct wolfIP_ll_dev *ll; + + wolfIP_init(&s); + mock_link_init(&s); + ll = wolfIP_getdev_ex(&s, TEST_PRIMARY_IF); + ck_assert_ptr_nonnull(ll); + + sd = wolfIP_sock_socket(&s, AF_PACKET, IPSTACK_SOCK_RAW, ee16(ETH_TYPE_IP)); + ck_assert_int_ge(sd, 0); + + memset(&bind_sll, 0, sizeof(bind_sll)); + bind_sll.sll_family = AF_PACKET; + bind_sll.sll_protocol = ee16(ETH_TYPE_IP); + bind_sll.sll_ifindex = TEST_PRIMARY_IF; + bind_sll.sll_halen = 6; + ck_assert_int_eq(wolfIP_sock_bind(&s, sd, + (struct wolfIP_sockaddr *)&bind_sll, sizeof(bind_sll)), 0); + + memset(frame, 0, sizeof(frame_buf)); + memcpy(frame->dst, ll->mac, 6); + memcpy(frame->src, "\xaa\xbb\xcc\xdd\xee\xff", 6); + frame->type = ee16(ETH_TYPE_IP); + memcpy(frame->data, payload, sizeof(payload)); + + wolfIP_recv_ex(&s, TEST_PRIMARY_IF, frame, (uint32_t)(ETH_HEADER_LEN + sizeof(payload))); + + memset(&sll, 0, sizeof(sll)); + sll_len = sizeof(sll); + ck_assert_int_eq(wolfIP_sock_recvfrom(&s, sd, rxbuf, sizeof(rxbuf), 0, + (struct wolfIP_sockaddr *)&sll, &sll_len), + (int)(ETH_HEADER_LEN + sizeof(payload))); + ck_assert_mem_eq(rxbuf, frame, ETH_HEADER_LEN + sizeof(payload)); + ck_assert_uint_eq(sll.sll_family, AF_PACKET); + ck_assert_uint_eq(sll.sll_protocol, frame->type); + ck_assert_int_eq(sll.sll_ifindex, TEST_PRIMARY_IF); + ck_assert_uint_eq(sll.sll_halen, 6); + ck_assert_mem_eq(sll.sll_addr, frame->src, 6); +#else + ck_abort_msg("WOLFIP_PACKET_SOCKETS disabled"); +#endif +} +END_TEST + +START_TEST(test_packet_socket_send_frame) +{ +#if WOLFIP_PACKET_SOCKETS + struct wolfIP s; + int sd; + struct wolfIP_sockaddr_ll sll; + uint8_t frame_buf[ETH_HEADER_LEN + 8]; + struct wolfIP_eth_frame *ethf = (struct wolfIP_eth_frame *)frame_buf; + struct wolfIP_ll_dev *ll; + struct wolfIP_sockaddr_ll bind_sll; + + wolfIP_init(&s); + mock_link_init(&s); + ll = wolfIP_getdev_ex(&s, TEST_PRIMARY_IF); + ck_assert_ptr_nonnull(ll); + + sd = wolfIP_sock_socket(&s, AF_PACKET, IPSTACK_SOCK_RAW, ee16(ETH_TYPE_IP)); + ck_assert_int_ge(sd, 0); + + memset(&bind_sll, 0, sizeof(bind_sll)); + bind_sll.sll_family = AF_PACKET; + bind_sll.sll_protocol = ee16(ETH_TYPE_IP); + bind_sll.sll_ifindex = TEST_PRIMARY_IF; + bind_sll.sll_halen = 6; + memset(bind_sll.sll_addr, 0xFF, 6); + ck_assert_int_eq(wolfIP_sock_bind(&s, sd, + (struct wolfIP_sockaddr *)&bind_sll, sizeof(bind_sll)), 0); + + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_protocol = ee16(ETH_TYPE_IP); + sll.sll_ifindex = TEST_PRIMARY_IF; + sll.sll_halen = 6; + memset(sll.sll_addr, 0xFF, 6); + + memset(frame_buf, 0, sizeof(frame_buf)); + memcpy(ethf->dst, "\xff\xff\xff\xff\xff\xff", 6); + memcpy(ethf->src, "\x00\x00\x00\x00\x00\x01", 6); + ethf->type = ee16(ETH_TYPE_IP); + memset(ethf->data, 0xAB, 8); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, sd, frame_buf, sizeof(frame_buf), 0, + (struct wolfIP_sockaddr *)&sll, sizeof(sll)), (int)sizeof(frame_buf)); + { + struct packetsocket *ps = &s.packetsockets[SOCKET_UNMARK(sd)]; + struct pkt_desc *desc = fifo_peek(&ps->txbuf); + if (desc) { + struct wolfIP_eth_frame *queued = + (struct wolfIP_eth_frame *)(ps->txmem + desc->pos + sizeof(*desc)); + ck_assert_uint_eq(desc->len, sizeof(frame_buf)); + ck_assert_mem_eq(queued->dst, "\xff\xff\xff\xff\xff\xff", 6); + ck_assert_mem_eq(queued->src, ll->mac, 6); + ck_assert_uint_eq(queued->type, ee16(ETH_TYPE_IP)); + ck_assert_mem_eq(queued->data, ethf->data, 8); + } + ck_assert_uint_eq(ps->if_idx, TEST_PRIMARY_IF); + } +#else + ck_abort_msg("WOLFIP_PACKET_SOCKETS disabled"); +#endif +} +END_TEST + +START_TEST(test_packet_socket_recv_wrong_proto_ignored) +{ +#if WOLFIP_PACKET_SOCKETS + struct wolfIP s; + int sd; + uint8_t frame_buf[ETH_HEADER_LEN + 6]; + struct wolfIP_eth_frame *frame = (struct wolfIP_eth_frame *)frame_buf; + struct wolfIP_ll_dev *ll; + uint8_t rxbuf[64]; + + wolfIP_init(&s); + mock_link_init(&s); + ll = wolfIP_getdev_ex(&s, TEST_PRIMARY_IF); + ck_assert_ptr_nonnull(ll); + + sd = wolfIP_sock_socket(&s, AF_PACKET, IPSTACK_SOCK_RAW, ee16(ETH_TYPE_IP)); + ck_assert_int_ge(sd, 0); + + memset(frame, 0, sizeof(frame_buf)); + memcpy(frame->dst, ll->mac, 6); + memcpy(frame->src, "\xaa\xbb\xcc\xdd\xee\xff", 6); + frame->type = ee16(ETH_TYPE_ARP); + memset(frame->data, 0xAA, 6); + + wolfIP_recv_ex(&s, TEST_PRIMARY_IF, frame, (uint32_t)(ETH_HEADER_LEN + 6)); + + ck_assert_int_eq(wolfIP_sock_recvfrom(&s, sd, rxbuf, sizeof(rxbuf), 0, NULL, NULL), + -WOLFIP_EAGAIN); +#else + ck_abort_msg("WOLFIP_PACKET_SOCKETS disabled"); +#endif +} +END_TEST + +START_TEST(test_packet_socket_recv_other_interface_ignored) +{ +#if WOLFIP_PACKET_SOCKETS + struct wolfIP s; + int sd; + uint8_t frame_buf[ETH_HEADER_LEN + 6]; + struct wolfIP_eth_frame *frame = (struct wolfIP_eth_frame *)frame_buf; + uint8_t rxbuf[64]; + uint8_t other_mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60}; + + wolfIP_init(&s); + mock_link_init(&s); + mock_link_init_idx(&s, TEST_SECOND_IF, other_mac); + + sd = wolfIP_sock_socket(&s, AF_PACKET, IPSTACK_SOCK_RAW, ee16(ETH_TYPE_IP)); + ck_assert_int_ge(sd, 0); + + { + struct wolfIP_sockaddr_ll bind_sll; + memset(&bind_sll, 0, sizeof(bind_sll)); + bind_sll.sll_family = AF_PACKET; + bind_sll.sll_protocol = ee16(ETH_TYPE_IP); + bind_sll.sll_ifindex = TEST_PRIMARY_IF; + bind_sll.sll_halen = 6; + ck_assert_int_eq(wolfIP_sock_bind(&s, sd, + (struct wolfIP_sockaddr *)&bind_sll, sizeof(bind_sll)), 0); + } + + memset(frame, 0, sizeof(frame_buf)); + memcpy(frame->dst, other_mac, 6); + memcpy(frame->src, "\xaa\xbb\xcc\xdd\xee\xff", 6); + frame->type = ee16(ETH_TYPE_IP); + memset(frame->data, 0xBB, 6); + + wolfIP_recv_ex(&s, TEST_SECOND_IF, frame, (uint32_t)(ETH_HEADER_LEN + 6)); + + ck_assert_int_eq(wolfIP_sock_recvfrom(&s, sd, rxbuf, sizeof(rxbuf), 0, NULL, NULL), + -WOLFIP_EAGAIN); +#else + ck_abort_msg("WOLFIP_PACKET_SOCKETS disabled"); +#endif +} +END_TEST + +START_TEST(test_raw_socket_recv_protocol_mismatch) +{ + struct wolfIP s; + int sd; + uint8_t frame_buf[sizeof(struct wolfIP_ip_packet) + 4]; + struct wolfIP_ip_packet *frame = (struct wolfIP_ip_packet *)frame_buf; + uint8_t payload[4] = {9, 8, 7, 6}; + uint8_t rxbuf[32]; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_RAW, WI_IPPROTO_UDP); + ck_assert_int_ge(sd, 0); + + memset(frame, 0, sizeof(frame_buf)); + memcpy(frame->eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + memcpy(frame->eth.src, "\xaa\xbb\xcc\xdd\xee\x00", 6); + frame->eth.type = ee16(ETH_TYPE_IP); + frame->ver_ihl = 0x45; + frame->ttl = 16; + frame->proto = WI_IPPROTO_TCP; + frame->len = ee16(IP_HEADER_LEN + sizeof(payload)); + frame->src = ee32(0x0A000010U); + frame->dst = ee32(0x0A000001U); + memcpy(frame->data, payload, sizeof(payload)); + frame->csum = 0; + iphdr_set_checksum(frame); + + wolfIP_recv_ex(&s, TEST_PRIMARY_IF, frame, ETH_HEADER_LEN + IP_HEADER_LEN + sizeof(payload)); + + memset(rxbuf, 0, sizeof(rxbuf)); + ck_assert_int_eq(wolfIP_sock_recvfrom(&s, sd, rxbuf, sizeof(rxbuf), 0, NULL, NULL), + -WOLFIP_EAGAIN); +} +END_TEST + +START_TEST(test_raw_socket_recv_short_frame_ignored) +{ + struct wolfIP s; + int sd; + uint8_t frame_buf[ETH_HEADER_LEN + IP_HEADER_LEN - 1]; + uint8_t rxbuf[32]; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_RAW, WI_IPPROTO_UDP); + ck_assert_int_ge(sd, 0); + + memset(frame_buf, 0, sizeof(frame_buf)); + wolfIP_recv_ex(&s, TEST_PRIMARY_IF, frame_buf, (uint32_t)sizeof(frame_buf)); + + ck_assert_int_eq(wolfIP_sock_recvfrom(&s, sd, rxbuf, sizeof(rxbuf), 0, NULL, NULL), + -WOLFIP_EAGAIN); +} +END_TEST + +START_TEST(test_raw_socket_recv_too_short_drop_returns_einval) +{ + struct wolfIP s; + int sd; + struct rawsocket *rs; + uint8_t pkt[IP_HEADER_LEN - 1]; + uint8_t rxbuf[32]; + + wolfIP_init(&s); + mock_link_init(&s); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_RAW, WI_IPPROTO_UDP); + ck_assert_int_ge(sd, 0); + rs = &s.rawsockets[SOCKET_UNMARK(sd)]; + + memset(pkt, 0, sizeof(pkt)); + ck_assert_int_eq(fifo_push(&rs->rxbuf, pkt, sizeof(pkt)), 0); + + ck_assert_int_eq(wolfIP_sock_recvfrom(&s, sd, rxbuf, sizeof(rxbuf), 0, NULL, NULL), + -WOLFIP_EINVAL); + ck_assert_ptr_eq(fifo_peek(&rs->rxbuf), NULL); +} +END_TEST + +START_TEST(test_raw_socket_recv_uses_ip_total_length) +{ + struct wolfIP s; + int sd; + uint8_t frame_buf[ETH_HEADER_LEN + IP_HEADER_LEN + 6]; + struct wolfIP_ip_packet *frame = (struct wolfIP_ip_packet *)frame_buf; + uint8_t payload[2] = {0xAB, 0xCD}; + uint8_t padding[4] = {0x99, 0x98, 0x97, 0x96}; + uint8_t rxbuf[64]; + int ret; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_RAW, WI_IPPROTO_UDP); + ck_assert_int_ge(sd, 0); + + memset(frame, 0, sizeof(frame_buf)); + memcpy(frame->eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + memcpy(frame->eth.src, "\xaa\xbb\xcc\xdd\xee\xff", 6); + frame->eth.type = ee16(ETH_TYPE_IP); + frame->ver_ihl = 0x45; + frame->ttl = 32; + frame->proto = WI_IPPROTO_UDP; + frame->len = ee16(IP_HEADER_LEN + sizeof(payload)); + frame->src = ee32(0x0A000002U); + frame->dst = ee32(0x0A000001U); + memcpy(frame->data, payload, sizeof(payload)); + memcpy(frame->data + sizeof(payload), padding, sizeof(padding)); + frame->csum = 0; + iphdr_set_checksum(frame); + + wolfIP_recv_ex(&s, TEST_PRIMARY_IF, frame, + ETH_HEADER_LEN + IP_HEADER_LEN + sizeof(payload) + sizeof(padding)); + + memset(rxbuf, 0, sizeof(rxbuf)); + ret = wolfIP_sock_recvfrom(&s, sd, rxbuf, sizeof(rxbuf), 0, NULL, NULL); + ck_assert_int_eq(ret, (int)(IP_HEADER_LEN + sizeof(payload))); + ck_assert_mem_eq(rxbuf, ((uint8_t *)frame) + ETH_HEADER_LEN, + IP_HEADER_LEN + sizeof(payload)); +} +END_TEST + +START_TEST(test_udp_short_frame_does_not_overread) +{ + struct wolfIP s; + int sd; + uint8_t frame_buf[ETH_HEADER_LEN + IP_HEADER_LEN]; + struct wolfIP_ip_packet *frame = (struct wolfIP_ip_packet *)frame_buf; + uint8_t rxbuf[16]; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + sd = wolfIP_sock_socket(&s, AF_INET, SOCK_DGRAM, 0); + ck_assert_int_ge(sd, 0); + + memset(frame, 0, sizeof(frame_buf)); + memcpy(frame->eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + memcpy(frame->eth.src, "\xaa\xbb\xcc\xdd\xee\xff", 6); + frame->eth.type = ee16(ETH_TYPE_IP); + frame->ver_ihl = 0x45; + frame->ttl = 64; + frame->proto = WI_IPPROTO_UDP; + frame->len = ee16(IP_HEADER_LEN); + frame->src = ee32(0x0A000002U); + frame->dst = ee32(0x0A000001U); + frame->csum = 0; + iphdr_set_checksum(frame); + + wolfIP_recv_ex(&s, TEST_PRIMARY_IF, frame, (uint32_t)sizeof(frame_buf)); + + ck_assert_int_eq(wolfIP_sock_recvfrom(&s, sd, rxbuf, sizeof(rxbuf), 0, NULL, NULL), + -WOLFIP_EAGAIN); +} +END_TEST + +START_TEST(test_raw_socket_close_clears_entry) +{ + struct wolfIP s; + int sd; + struct rawsocket *rs; + + wolfIP_init(&s); + mock_link_init(&s); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_RAW, WI_IPPROTO_UDP); + ck_assert_int_ge(sd, 0); + rs = &s.rawsockets[SOCKET_UNMARK(sd)]; + rs->used = 1; + rs->events = CB_EVENT_READABLE; + + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); + ck_assert_uint_eq(rs->used, 0); + ck_assert_uint_eq(rs->events, 0); +} +END_TEST + +START_TEST(test_packet_socket_close_clears_entry) +{ +#if WOLFIP_PACKET_SOCKETS + struct wolfIP s; + int sd; + struct packetsocket *ps; + + wolfIP_init(&s); + mock_link_init(&s); + + sd = wolfIP_sock_socket(&s, AF_PACKET, IPSTACK_SOCK_RAW, ETH_TYPE_IP); + ck_assert_int_ge(sd, 0); + ps = &s.packetsockets[SOCKET_UNMARK(sd)]; + ps->used = 1; + ps->events = CB_EVENT_READABLE; + + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); + ck_assert_uint_eq(ps->used, 0); + ck_assert_uint_eq(ps->events, 0); +#else + ck_abort_msg("WOLFIP_PACKET_SOCKETS disabled"); +#endif +} +END_TEST + +START_TEST(test_arp_lookup_ex_basic) +{ +#ifdef ETHERNET + struct wolfIP s; + uint8_t mac[6]; + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_arp_lookup_ex(&s, 0, ee32(0x0A000002U), mac), -1); + + s.arp.neighbors[0].ip = ee32(0x0A000002U); + s.arp.neighbors[0].if_idx = 0; + memcpy(s.arp.neighbors[0].mac, "\x10\x20\x30\x40\x50\x60", 6); + ck_assert_int_eq(wolfIP_arp_lookup_ex(&s, 0, ee32(0x0A000002U), mac), 0); + ck_assert_mem_eq(mac, "\x10\x20\x30\x40\x50\x60", 6); +#else + ck_abort_msg("ETHERNET disabled"); +#endif +} +END_TEST + START_TEST(test_fifo_peek_wraps_tail_when_head_lt_tail) { struct fifo f; @@ -4415,7 +5090,7 @@ START_TEST(test_sock_opts_unknown_level) ck_assert_int_gt(udp_sd, 0); ck_assert_int_eq(wolfIP_sock_setsockopt(&s, udp_sd, 0xFFFF, 0, &value, len), 0); - ck_assert_int_eq(wolfIP_sock_getsockopt(&s, udp_sd, 0xFFFF, 0, &value, &len), 0); + ck_assert_int_eq(wolfIP_sock_getsockopt(&s, udp_sd, 0xFFFF, 0, &value, &len), -WOLFIP_EINVAL); } END_TEST @@ -4433,7 +5108,7 @@ START_TEST(test_sock_opts_sol_ip_unknown_optname) ck_assert_int_gt(udp_sd, 0); ck_assert_int_eq(wolfIP_sock_setsockopt(&s, udp_sd, WOLFIP_SOL_IP, 0xFFFF, &value, len), 0); - ck_assert_int_eq(wolfIP_sock_getsockopt(&s, udp_sd, WOLFIP_SOL_IP, 0xFFFF, &value, &len), 0); + ck_assert_int_eq(wolfIP_sock_getsockopt(&s, udp_sd, WOLFIP_SOL_IP, 0xFFFF, &value, &len), -WOLFIP_EINVAL); } END_TEST @@ -17760,6 +18435,26 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_icmp_try_recv_mismatch_local_ip); tcase_add_test(tc_proto, test_icmp_try_recv_mismatch_src_port); tcase_add_test(tc_proto, test_icmp_try_recv_mismatch_remote_ip); + tcase_add_test(tc_proto, test_raw_socket_recv_captures_ip_header); + tcase_add_test(tc_proto, test_raw_socket_send_hdrincl_respected); + tcase_add_test(tc_proto, test_raw_socket_send_builds_ip_header); + tcase_add_test(tc_proto, test_raw_socket_sendto_short_addrlen_returns_einval); + tcase_add_test(tc_proto, test_getsockopt_invalid_fd_returns_einval); + tcase_add_test(tc_proto, test_getsockopt_invalid_raw_fd_returns_einval); + tcase_add_test(tc_proto, test_getsockopt_invalid_packet_fd_returns_einval); + tcase_add_test(tc_proto, test_getsockopt_unsupported_option_returns_einval); + tcase_add_test(tc_proto, test_packet_socket_recv_frame); + tcase_add_test(tc_proto, test_packet_socket_send_frame); + tcase_add_test(tc_proto, test_packet_socket_recv_wrong_proto_ignored); + tcase_add_test(tc_proto, test_packet_socket_recv_other_interface_ignored); + tcase_add_test(tc_proto, test_raw_socket_recv_protocol_mismatch); + tcase_add_test(tc_proto, test_raw_socket_recv_short_frame_ignored); + tcase_add_test(tc_proto, test_raw_socket_recv_too_short_drop_returns_einval); + tcase_add_test(tc_proto, test_raw_socket_recv_uses_ip_total_length); + tcase_add_test(tc_proto, test_udp_short_frame_does_not_overread); + tcase_add_test(tc_proto, test_raw_socket_close_clears_entry); + tcase_add_test(tc_proto, test_packet_socket_close_clears_entry); + tcase_add_test(tc_proto, test_arp_lookup_ex_basic); tcase_add_test(tc_proto, test_wolfip_recv_on_not_for_us); tcase_add_test(tc_proto, test_wolfip_recv_on_filter_drop_eth); #if WOLFIP_ENABLE_FORWARDING diff --git a/src/wolfip.c b/src/wolfip.c index 2fbe3f78..ec099aeb 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -1072,6 +1072,54 @@ struct tsocket { void *callback_arg; }; static void close_socket(struct tsocket *ts); + +#if WOLFIP_RAWSOCKETS +struct rawsocket { + struct fifo rxbuf; + struct fifo txbuf; + ip4 local_ip, remote_ip; + ip4 bound_local_ip; + struct wolfIP *S; +#ifdef ETHERNET + uint8_t nexthop_mac[6]; +#endif + uint16_t protocol; + uint8_t if_idx; + uint8_t dontroute; + uint8_t ipheader_include; + uint8_t recv_ttl; + uint8_t last_pkt_ttl; + uint8_t rxmem[RXBUF_SIZE]; + uint8_t txmem[TXBUF_SIZE]; + uint8_t used; + uint16_t events; + tsocket_cb callback; + void *callback_arg; +}; + +#if WOLFIP_PACKET_SOCKETS +struct sock_ll { + uint8_t src_mac[6]; + uint8_t dst_mac[6]; +}; + +struct packetsocket { + struct fifo rxbuf; + struct fifo txbuf; + uint8_t rxmem[RXBUF_SIZE]; + uint8_t txmem[TXBUF_SIZE]; + struct sock_ll macs; + struct wolfIP_sockaddr_ll bind_addr; + uint16_t protocol; + uint8_t if_idx; + uint8_t used; + uint16_t events; + struct wolfIP *S; + tsocket_cb callback; + void *callback_arg; +}; +#endif +#endif static inline uint32_t tcp_seq_inc(uint32_t seq, uint32_t n); static inline int tcp_seq_leq(uint32_t a, uint32_t b); static inline int tcp_seq_lt(uint32_t a, uint32_t b); @@ -1163,6 +1211,12 @@ struct wolfIP { struct tsocket tcpsockets[MAX_TCPSOCKETS]; struct tsocket udpsockets[MAX_UDPSOCKETS]; struct tsocket icmpsockets[MAX_ICMPSOCKETS]; +#if WOLFIP_RAWSOCKETS + struct rawsocket rawsockets[WOLFIP_MAX_RAWSOCKETS]; +#if WOLFIP_PACKET_SOCKETS + struct packetsocket packetsockets[WOLFIP_MAX_PACKETSOCKETS]; +#endif +#endif uint16_t ipcounter; uint64_t last_tick; #ifdef ETHERNET @@ -1350,6 +1404,33 @@ static inline unsigned int wolfIP_socket_if_idx(const struct tsocket *t) return t->if_idx; } +#if WOLFIP_RAWSOCKETS +static unsigned int wolfIP_if_for_local_ip(struct wolfIP *s, ip4 local_ip, int *found); +static unsigned int raw_route_for_ip(struct wolfIP *s, struct rawsocket *rs, ip4 dest, int dontroute) +{ + unsigned int if_idx = 0; + int match = 0; + if (!s) + return 0; + if (rs && rs->bound_local_ip != IPADDR_ANY) { + if_idx = wolfIP_if_for_local_ip(s, rs->bound_local_ip, &match); + if (match) + return if_idx; + } + if (dontroute) { + unsigned int i; + for (i = 0; i < s->if_count; i++) { + struct ipconf *conf = wolfIP_ipconf_at(s, i); + if (!conf || conf->ip == IPADDR_ANY) + continue; + if (ip_is_local_conf(conf, dest)) + return i; + } + } + return wolfIP_route_for_ip(s, dest); +} +#endif + #if CONFIG_IPFILTER static int wolfIP_filter_notify_socket_event( enum wolfIP_filter_reason reason, @@ -1497,6 +1578,22 @@ void wolfIP_register_callback(struct wolfIP *s, int sock_fd, tsocket_cb cb, t->callback = cb; t->callback_arg = arg; } +#if WOLFIP_RAWSOCKETS + else if (IS_SOCKET_RAW(sock_fd)) { + if (SOCKET_UNMARK(sock_fd) >= WOLFIP_MAX_RAWSOCKETS) + return; + s->rawsockets[SOCKET_UNMARK(sock_fd)].callback = cb; + s->rawsockets[SOCKET_UNMARK(sock_fd)].callback_arg = arg; + } +#endif +#if WOLFIP_PACKET_SOCKETS + else if (IS_SOCKET_PACKET(sock_fd)) { + if (SOCKET_UNMARK(sock_fd) >= WOLFIP_MAX_PACKETSOCKETS) + return; + s->packetsockets[SOCKET_UNMARK(sock_fd)].callback = cb; + s->packetsockets[SOCKET_UNMARK(sock_fd)].callback_arg = arg; + } +#endif } /* Timers */ @@ -1677,6 +1774,55 @@ static struct tsocket *icmp_new_socket(struct wolfIP *s) return NULL; } +#if WOLFIP_RAWSOCKETS +static struct rawsocket *raw_new_socket(struct wolfIP *s, int protocol, int ipheader_include) +{ + int i; + + for (i = 0; i < WOLFIP_MAX_RAWSOCKETS; i++) { + struct rawsocket *r = &s->rawsockets[i]; + if (!r->used) { + memset(r, 0, sizeof(struct rawsocket)); + r->used = 1; + r->S = s; + r->protocol = (uint16_t)protocol; + r->ipheader_include = ipheader_include ? 1 : 0; + fifo_init(&r->rxbuf, r->rxmem, RXBUF_SIZE); + fifo_init(&r->txbuf, r->txmem, TXBUF_SIZE); + r->events |= CB_EVENT_WRITABLE; + return r; + } + } + return NULL; +} +#endif + +#if WOLFIP_PACKET_SOCKETS +static struct packetsocket *packet_new_socket(struct wolfIP *s, int protocol) +{ + int i; + + for (i = 0; i < WOLFIP_MAX_PACKETSOCKETS; i++) { + struct packetsocket *p = &s->packetsockets[i]; + if (!p->used) { + memset(p, 0, sizeof(struct packetsocket)); + p->used = 1; + p->protocol = (uint16_t)protocol; + p->if_idx = 0; + p->bind_addr.sll_family = AF_PACKET; + p->bind_addr.sll_protocol = (uint16_t)protocol; + p->bind_addr.sll_halen = 6; + fifo_init(&p->rxbuf, p->rxmem, RXBUF_SIZE); + fifo_init(&p->txbuf, p->txmem, TXBUF_SIZE); + p->events |= CB_EVENT_WRITABLE; + p->S = s; + return p; + } + } + return NULL; +} +#endif + static void icmp_try_recv(struct wolfIP *s, unsigned int if_idx, struct wolfIP_icmp_packet *icmp, uint32_t frame_len) { @@ -1704,6 +1850,74 @@ static void icmp_try_recv(struct wolfIP *s, unsigned int if_idx, } } +#if WOLFIP_RAWSOCKETS +static void raw_try_recv(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_packet *ip, uint32_t frame_len) +{ + uint32_t payload_len = frame_len; + const uint8_t *packet = (const uint8_t *)ip; + uint32_t ip_len; +#ifdef ETHERNET + if (frame_len <= ETH_HEADER_LEN) + return; + payload_len -= ETH_HEADER_LEN; + packet += ETH_HEADER_LEN; +#endif + if (payload_len < IP_HEADER_LEN) + return; + ip_len = (uint32_t)ee16(ip->len); + if (ip_len < IP_HEADER_LEN) + return; + if (ip_len > payload_len) + return; + payload_len = ip_len; + (void)if_idx; + for (int i = 0; i < WOLFIP_MAX_RAWSOCKETS; i++) { + struct rawsocket *r = &s->rawsockets[i]; + if (!r->used) + continue; + if (r->protocol != 0 && r->protocol != ip->proto) + continue; + if (fifo_space(&r->rxbuf) < payload_len + sizeof(struct pkt_desc)) + continue; + fifo_push(&r->rxbuf, (void *)packet, payload_len); + r->last_pkt_ttl = ip->ttl; + r->events |= CB_EVENT_READABLE; + } +} +#endif + +#if WOLFIP_PACKET_SOCKETS +static void packet_try_recv(struct wolfIP *s, unsigned int if_idx, struct wolfIP_eth_frame *eth, uint32_t frame_len) +{ + uint16_t proto = eth->type; + uint32_t record_len; + uint8_t record[1 + LINK_MTU]; + int i; + + if (frame_len > LINK_MTU || frame_len == 0) + return; + record_len = frame_len + 1; + record[0] = (uint8_t)if_idx; + memcpy(record + 1, eth, frame_len); + + for (i = 0; i < WOLFIP_MAX_PACKETSOCKETS; i++) { + struct packetsocket *p = &s->packetsockets[i]; + if (!p->used) + continue; + if (p->protocol && p->protocol != proto) + continue; + if ((p->bind_addr.sll_ifindex >= 0) && + (unsigned int)p->bind_addr.sll_ifindex != if_idx && + p->bind_addr.sll_ifindex != 0) + continue; + if (fifo_space(&p->rxbuf) < record_len + sizeof(struct pkt_desc)) + continue; + fifo_push(&p->rxbuf, record, record_len); + p->events |= CB_EVENT_READABLE; + } +} +#endif + /* TCP */ static uint32_t tcp_initial_cwnd(uint32_t peer_rwnd) { @@ -3616,6 +3830,24 @@ static void close_socket(struct tsocket *ts) memset(ts, 0, sizeof(struct tsocket)); } +#if WOLFIP_RAWSOCKETS +static void close_rawsocket(struct rawsocket *rs) +{ + if (!rs) + return; + memset(rs, 0, sizeof(struct rawsocket)); +} +#endif + +#if WOLFIP_PACKET_SOCKETS +static void close_packetsocket(struct packetsocket *ps) +{ + if (!ps) + return; + memset(ps, 0, sizeof(struct packetsocket)); +} +#endif + static struct tsocket *wolfIP_socket_from_fd(struct wolfIP *s, int sockfd) { if (!s || sockfd < 0) @@ -3636,12 +3868,41 @@ static struct tsocket *wolfIP_socket_from_fd(struct wolfIP *s, int sockfd) return NULL; } +#if WOLFIP_RAWSOCKETS +static struct rawsocket *wolfIP_rawsocket_from_fd(struct wolfIP *s, int sockfd) +{ + if (!s || sockfd < 0 || !IS_SOCKET_RAW(sockfd)) + return NULL; + if (SOCKET_UNMARK(sockfd) >= WOLFIP_MAX_RAWSOCKETS) + return NULL; + if (!s->rawsockets[SOCKET_UNMARK(sockfd)].used) + return NULL; + return &s->rawsockets[SOCKET_UNMARK(sockfd)]; +} +#endif + +#if WOLFIP_PACKET_SOCKETS +static struct packetsocket *wolfIP_packetsocket_from_fd(struct wolfIP *s, int sockfd) +{ + if (!s || sockfd < 0 || !IS_SOCKET_PACKET(sockfd)) + return NULL; + if (SOCKET_UNMARK(sockfd) >= WOLFIP_MAX_PACKETSOCKETS) + return NULL; + if (!s->packetsockets[SOCKET_UNMARK(sockfd)].used) + return NULL; + return &s->packetsockets[SOCKET_UNMARK(sockfd)]; +} +#endif + int wolfIP_sock_socket(struct wolfIP *s, int domain, int type, int protocol) { struct tsocket *ts; +#if WOLFIP_RAWSOCKETS || WOLFIP_PACKET_SOCKETS + int base_type = type; +#endif if (domain != AF_INET) - return -1; + goto packet_try; if (type == IPSTACK_SOCK_STREAM) { ts = tcp_new_socket(s); if (!ts) @@ -3662,6 +3923,32 @@ int wolfIP_sock_socket(struct wolfIP *s, int domain, int type, int protocol) return -1; } } +#if WOLFIP_RAWSOCKETS + else if (base_type == IPSTACK_SOCK_RAW) { + struct rawsocket *rs; + int hdrincl = 0; +#ifdef IPPROTO_RAW + if (protocol == IPPROTO_RAW) + hdrincl = 1; +#endif + rs = raw_new_socket(s, protocol, hdrincl); + if (!rs) + return -1; + return (int)((rs - s->rawsockets) | MARK_RAW_SOCKET); + } +#endif + return -1; + +packet_try: +#if WOLFIP_PACKET_SOCKETS + if (domain == AF_PACKET && base_type == IPSTACK_SOCK_RAW) { + struct packetsocket *ps; + ps = packet_new_socket(s, protocol); + if (!ps) + return -1; + return (int)((ps - s->packetsockets) | MARK_PACKET_SOCKET); + } +#endif return -1; } @@ -3722,6 +4009,33 @@ int wolfIP_sock_connect(struct wolfIP *s, int sockfd, const struct wolfIP_sockad } return 0; } +#if WOLFIP_RAWSOCKETS + else if (IS_SOCKET_RAW(sockfd)) { + unsigned int if_idx; + struct ipconf *conf; + struct rawsocket *rs = wolfIP_rawsocket_from_fd(s, sockfd); + if (!rs) + return -WOLFIP_EINVAL; + if ((sin->sin_family != AF_INET) || (addrlen < sizeof(struct wolfIP_sockaddr_in))) + return -WOLFIP_EINVAL; + rs->remote_ip = ee32(sin->sin_addr.s_addr); + if_idx = raw_route_for_ip(s, rs, rs->remote_ip, rs->dontroute); + rs->if_idx = (uint8_t)if_idx; + if (!rs->ipheader_include) { + conf = wolfIP_ipconf_at(s, if_idx); + if (rs->bound_local_ip != IPADDR_ANY) + rs->local_ip = rs->bound_local_ip; + else if (conf && conf->ip != IPADDR_ANY) + rs->local_ip = conf->ip; + else { + struct ipconf *primary = wolfIP_primary_ipconf(s); + if (primary && primary->ip != IPADDR_ANY) + rs->local_ip = primary->ip; + } + } + return 0; + } +#endif if (!IS_SOCKET_TCP(sockfd)) return -WOLFIP_EINVAL; @@ -3879,9 +4193,15 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len struct wolfIP_tcp_seg *tcp; struct wolfIP_udp_datagram *udp; struct wolfIP_icmp_packet *icmp; +#if WOLFIP_RAWSOCKETS + struct wolfIP_ip_packet *rip; +#endif tcp = (struct wolfIP_tcp_seg *)frame; udp = (struct wolfIP_udp_datagram *)frame; icmp = (struct wolfIP_icmp_packet *)frame; +#if WOLFIP_RAWSOCKETS + rip = (struct wolfIP_ip_packet *)frame; +#endif (void)flags; if (sockfd < 0) @@ -4058,7 +4378,146 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len if (fifo_push(&ts->sock.udp.txbuf, icmp, sizeof(struct wolfIP_ip_packet) + payload_len) < 0) return -WOLFIP_EAGAIN; return (int)payload_len; - } else return -1; + } +#if WOLFIP_RAWSOCKETS + else if (IS_SOCKET_RAW(sockfd)) { + const struct wolfIP_sockaddr_in *sin = (const struct wolfIP_sockaddr_in *)dest_addr; + struct rawsocket *rs; + unsigned int if_idx; + struct ipconf *conf; + ip4 dst_ip = 0; + int use_dontroute = 0; + uint32_t total_len; + + rs = wolfIP_rawsocket_from_fd(s, sockfd); + if (!rs) + return -WOLFIP_EINVAL; + if (len == 0 || len > (LINK_MTU - ETH_HEADER_LEN)) + return -WOLFIP_EINVAL; + if (sin) { + if (addrlen < sizeof(struct wolfIP_sockaddr_in)) + return -WOLFIP_EINVAL; + dst_ip = ee32(sin->sin_addr.s_addr); + rs->remote_ip = dst_ip; + } else { + dst_ip = rs->remote_ip; + } + use_dontroute = rs->dontroute; +#ifdef MSG_DONTROUTE + if (flags & MSG_DONTROUTE) + use_dontroute = 1; +#else + (void)flags; +#endif + + if (rs->ipheader_include) { + if (len < IP_HEADER_LEN) + return -WOLFIP_EINVAL; +#ifdef ETHERNET + memset(rip, 0, ETH_HEADER_LEN); +#endif + memcpy(((uint8_t *)rip) + ETH_HEADER_LEN, buf, len); + rip->ttl = ((const uint8_t *)buf)[8]; + total_len = (uint32_t)len + ETH_HEADER_LEN; + if (dst_ip == 0) + dst_ip = ee32(rip->dst); + else + rip->dst = ee32(dst_ip); + if (rs->remote_ip == 0 && dst_ip != 0) + rs->remote_ip = dst_ip; + rs->local_ip = ee32(rip->src); + } else { + ip4 src_ip = rs->local_ip; + total_len = (uint32_t)len + IP_HEADER_LEN + ETH_HEADER_LEN; + if (dst_ip == 0) + return -WOLFIP_EINVAL; + if_idx = raw_route_for_ip(s, rs, dst_ip, use_dontroute); + conf = wolfIP_ipconf_at(s, if_idx); + rs->if_idx = (uint8_t)if_idx; + if (rs->bound_local_ip != IPADDR_ANY) + src_ip = rs->bound_local_ip; + else if (conf && conf->ip != IPADDR_ANY) + src_ip = conf->ip; + else { + struct ipconf *primary = wolfIP_primary_ipconf(s); + if (primary && primary->ip != IPADDR_ANY) + src_ip = primary->ip; + } + rs->local_ip = src_ip; + memset(rip, 0, sizeof(struct wolfIP_ip_packet)); + rip->ver_ihl = 0x45; + rip->tos = 0; + rip->len = ee16((uint16_t)(len + IP_HEADER_LEN)); + rip->id = ee16(ipcounter_next(s)); + rip->flags_fo = 0; + rip->ttl = 64; + rip->proto = (uint8_t)rs->protocol; + rip->src = ee32(src_ip); + rip->dst = ee32(dst_ip); + rip->csum = 0; + iphdr_set_checksum(rip); + memcpy(rip->data, buf, len); + } + if (dst_ip == 0) + return -WOLFIP_EINVAL; + if_idx = raw_route_for_ip(s, rs, dst_ip, use_dontroute); + rs->if_idx = (uint8_t)if_idx; + if (total_len > LINK_MTU) + return -WOLFIP_EINVAL; + if (fifo_space(&rs->txbuf) < total_len) + return -WOLFIP_EAGAIN; + if (fifo_push(&rs->txbuf, rip, total_len) < 0) + return -WOLFIP_EAGAIN; + return (int)len; + } +#endif + +#if WOLFIP_PACKET_SOCKETS + else if (IS_SOCKET_PACKET(sockfd)) { + struct packetsocket *ps = wolfIP_packetsocket_from_fd(s, sockfd); + const struct wolfIP_sockaddr_ll *sll = (const struct wolfIP_sockaddr_ll *)dest_addr; + struct wolfIP_eth_frame *ethf; + uint8_t pkt_frame[LINK_MTU]; + unsigned int tx_if = 0; + uint16_t proto = 0; + + if (!ps) + return -WOLFIP_EINVAL; + if (len < ETH_HEADER_LEN || len > LINK_MTU) + return -WOLFIP_EINVAL; + if (dest_addr && addrlen < sizeof(struct wolfIP_sockaddr_ll)) + return -WOLFIP_EINVAL; + memcpy(pkt_frame, buf, len); + ethf = (struct wolfIP_eth_frame *)pkt_frame; + tx_if = ps->if_idx; + proto = ps->protocol; + if (sll) { + if (sll->sll_ifindex >= 0 && (unsigned int)sll->sll_ifindex < s->if_count) + tx_if = (unsigned int)sll->sll_ifindex; + if (sll->sll_halen >= 6) + memcpy(ethf->dst, sll->sll_addr, 6); + if (sll->sll_protocol) + proto = sll->sll_protocol; + } + if (tx_if >= s->if_count) + tx_if = 0; + { + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, tx_if); + if (ll) + memcpy(ethf->src, ll->mac, 6); + } + if (proto) + ethf->type = proto; + ps->if_idx = (uint8_t)tx_if; + if (fifo_space(&ps->txbuf) < len) + return -WOLFIP_EAGAIN; + if (fifo_push(&ps->txbuf, pkt_frame, (uint32_t)len) < 0) + return -WOLFIP_EAGAIN; + return (int)len; + } +#endif + + return -1; } int wolfIP_sock_send(struct wolfIP *s, int sockfd, const void *buf, size_t len, int flags) @@ -4181,8 +4640,86 @@ int wolfIP_sock_recvfrom(struct wolfIP *s, int sockfd, void *buf, size_t len, in if (fifo_peek(&ts->sock.udp.rxbuf) == NULL) ts->events &= ~CB_EVENT_READABLE; return (int)seg_len; - } else - return -WOLFIP_EINVAL; + } +#if WOLFIP_RAWSOCKETS + else if (IS_SOCKET_RAW(sockfd)) { + struct rawsocket *rs; + struct wolfIP_sockaddr_in *sin = (struct wolfIP_sockaddr_in *)src_addr; + const uint8_t *pkt; + ip4 src_ip; + rs = wolfIP_rawsocket_from_fd(s, sockfd); + if (!rs) + return -WOLFIP_EINVAL; + if (sin && addrlen && *addrlen < sizeof(struct wolfIP_sockaddr_in)) + return -WOLFIP_EINVAL; + desc = fifo_peek(&rs->rxbuf); + if (!desc) + return -WOLFIP_EAGAIN; + if (desc->len < IP_HEADER_LEN) { + fifo_pop(&rs->rxbuf); + return -WOLFIP_EINVAL; + } + if (desc->len > len) + return -1; + pkt = rs->rxmem + desc->pos + sizeof(*desc); + memcpy(&src_ip, pkt + 12, sizeof(src_ip)); + if (sin) { + sin->sin_family = AF_INET; + sin->sin_port = 0; + sin->sin_addr.s_addr = src_ip; + if (addrlen) + *addrlen = sizeof(struct wolfIP_sockaddr_in); + } + memcpy(buf, pkt, desc->len); + fifo_pop(&rs->rxbuf); + if (fifo_peek(&rs->rxbuf) == NULL) + rs->events &= ~CB_EVENT_READABLE; + return (int)desc->len; + } +#endif +#if WOLFIP_PACKET_SOCKETS + else if (IS_SOCKET_PACKET(sockfd)) { + struct packetsocket *ps = wolfIP_packetsocket_from_fd(s, sockfd); + struct wolfIP_sockaddr_ll *sll = (struct wolfIP_sockaddr_ll *)src_addr; + struct wolfIP_eth_frame *ethf; + uint8_t if_idx_byte; + uint32_t frame_len; + uint8_t *pkt; + if (!ps) + return -WOLFIP_EINVAL; + if (sll && addrlen && *addrlen < sizeof(struct wolfIP_sockaddr_ll)) + return -WOLFIP_EINVAL; + desc = fifo_peek(&ps->rxbuf); + if (!desc) + return -WOLFIP_EAGAIN; + if (desc->len == 0) + return -WOLFIP_EAGAIN; + if (desc->len - 1 > len) + return -1; + pkt = ps->rxmem + desc->pos + sizeof(*desc); + if_idx_byte = pkt[0]; + ethf = (struct wolfIP_eth_frame *)(pkt + 1); + frame_len = desc->len - 1; + if (sll) { + memset(sll, 0, sizeof(*sll)); + sll->sll_family = AF_PACKET; + sll->sll_protocol = ethf->type; + sll->sll_ifindex = if_idx_byte; + sll->sll_hatype = 1; + sll->sll_pkttype = 0; + sll->sll_halen = 6; + memcpy(sll->sll_addr, ethf->src, 6); + if (addrlen) + *addrlen = sizeof(struct wolfIP_sockaddr_ll); + } + memcpy(buf, ethf, frame_len); + fifo_pop(&ps->rxbuf); + if (fifo_peek(&ps->rxbuf) == NULL) + ps->events &= ~CB_EVENT_READABLE; + return (int)frame_len; + } +#endif + return -WOLFIP_EINVAL; } int wolfIP_sock_recv(struct wolfIP *s, int sockfd, void *buf, size_t len, int flags) @@ -4198,7 +4735,40 @@ int wolfIP_sock_read(struct wolfIP *s, int sockfd, void *buf, size_t len) int wolfIP_sock_setsockopt(struct wolfIP *s, int sockfd, int level, int optname, const void *optval, socklen_t optlen) { - struct tsocket *ts = wolfIP_socket_from_fd(s, sockfd); + struct tsocket *ts; +#if WOLFIP_RAWSOCKETS + if (IS_SOCKET_RAW(sockfd)) { + struct rawsocket *rs = wolfIP_rawsocket_from_fd(s, sockfd); + int enable; + if (!rs) + return -WOLFIP_EINVAL; + if (!optval || optlen < (socklen_t)sizeof(int)) + return -WOLFIP_EINVAL; + memcpy(&enable, optval, sizeof(int)); + if (level == WOLFIP_SOL_IP && optname == WOLFIP_IP_RECVTTL) { + rs->recv_ttl = enable ? 1 : 0; + return 0; + } else if (level == WOLFIP_SOL_IP && optname == WOLFIP_IP_HDRINCL) { + rs->ipheader_include = enable ? 1 : 0; + return 0; + } else if (level == WOLFIP_SOL_SOCKET && optname == WOLFIP_SO_DONTROUTE) { + rs->dontroute = enable ? 1 : 0; + return 0; + } + return -WOLFIP_EINVAL; + } +#endif +#if WOLFIP_PACKET_SOCKETS + if (IS_SOCKET_PACKET(sockfd)) { + (void)s; + (void)level; + (void)optname; + (void)optval; + (void)optlen; + return 0; + } +#endif + ts = wolfIP_socket_from_fd(s, sockfd); if (!ts) return -WOLFIP_EINVAL; if (level == WOLFIP_SOL_IP && optname == WOLFIP_IP_RECVTTL) { @@ -4214,7 +4784,20 @@ int wolfIP_sock_setsockopt(struct wolfIP *s, int sockfd, int level, int optname, int wolfIP_sock_get_recv_ttl(struct wolfIP *s, int sockfd, int *ttl) { - struct tsocket *ts = wolfIP_socket_from_fd(s, sockfd); + struct tsocket *ts; +#if WOLFIP_RAWSOCKETS + if (IS_SOCKET_RAW(sockfd)) { + struct rawsocket *rs = wolfIP_rawsocket_from_fd(s, sockfd); + if (!rs) + return -WOLFIP_EINVAL; + if (!rs->recv_ttl) + return 0; + if (ttl) + *ttl = rs->last_pkt_ttl; + return 1; + } +#endif + ts = wolfIP_socket_from_fd(s, sockfd); if (!ts) return -WOLFIP_EINVAL; if (!ts->recv_ttl) @@ -4227,19 +4810,57 @@ int wolfIP_sock_get_recv_ttl(struct wolfIP *s, int sockfd, int *ttl) int wolfIP_sock_getsockopt(struct wolfIP *s, int sockfd, int level, int optname, void *optval, socklen_t *optlen) { - struct tsocket *ts = wolfIP_socket_from_fd(s, sockfd); - if (!ts) + struct tsocket *ts = NULL; +#if WOLFIP_RAWSOCKETS + struct rawsocket *rs = NULL; +#endif +#if WOLFIP_PACKET_SOCKETS + struct packetsocket *ps = NULL; +#endif + + if (sockfd < 0) return -WOLFIP_EINVAL; +#if WOLFIP_RAWSOCKETS + if (IS_SOCKET_RAW(sockfd)) { + rs = wolfIP_rawsocket_from_fd(s, sockfd); + if (!rs) + return -WOLFIP_EINVAL; + } else +#endif +#if WOLFIP_PACKET_SOCKETS + if (IS_SOCKET_PACKET(sockfd)) { + ps = wolfIP_packetsocket_from_fd(s, sockfd); + if (!ps) + return -WOLFIP_EINVAL; + } else +#endif + { + ts = wolfIP_socket_from_fd(s, sockfd); + if (!ts) + return -WOLFIP_EINVAL; + } + if (level == WOLFIP_SOL_IP && optname == WOLFIP_IP_RECVTTL) { int value; if (!optval || !optlen || *optlen < (socklen_t)sizeof(int)) return -WOLFIP_EINVAL; - value = ts->recv_ttl ? ts->last_pkt_ttl : 0; - memcpy(optval, &value, sizeof(int)); - *optlen = sizeof(int); - return 0; +#if WOLFIP_RAWSOCKETS + if (rs) { + value = rs->recv_ttl ? rs->last_pkt_ttl : 0; + memcpy(optval, &value, sizeof(int)); + *optlen = sizeof(int); + return 0; + } +#endif + if (ts) { + value = ts->recv_ttl ? ts->last_pkt_ttl : 0; + memcpy(optval, &value, sizeof(int)); + *optlen = sizeof(int); + return 0; + } + return -WOLFIP_EINVAL; } - return 0; + return -WOLFIP_EINVAL; } int wolfIP_sock_close(struct wolfIP *s, int sockfd) { @@ -4306,7 +4927,26 @@ int wolfIP_sock_close(struct wolfIP *s, int sockfd) ts->local_ip, ts->src_port, ts->remote_ip, 0); close_socket(ts); return 0; - } else return -1; + } +#if WOLFIP_RAWSOCKETS + else if (IS_SOCKET_RAW(sockfd)) { + struct rawsocket *rs = wolfIP_rawsocket_from_fd(s, sockfd); + if (!rs) + return -WOLFIP_EINVAL; + close_rawsocket(rs); + return 0; + } +#endif +#if WOLFIP_PACKET_SOCKETS + else if (IS_SOCKET_PACKET(sockfd)) { + struct packetsocket *ps = wolfIP_packetsocket_from_fd(s, sockfd); + if (!ps) + return -WOLFIP_EINVAL; + close_packetsocket(ps); + return 0; + } +#endif + else return -1; return 0; } @@ -4348,6 +4988,27 @@ int wolfIP_sock_getsockname(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr sin->sin_addr.s_addr = ee32(ts->local_ip); return 0; } +#if WOLFIP_RAWSOCKETS + else if (IS_SOCKET_RAW(sockfd)) { + struct rawsocket *rs = wolfIP_rawsocket_from_fd(s, sockfd); + if (!rs) + return -WOLFIP_EINVAL; + sin->sin_family = AF_INET; + sin->sin_port = 0; + sin->sin_addr.s_addr = ee32(rs->local_ip); + return 0; + } +#endif +#if WOLFIP_PACKET_SOCKETS + else if (IS_SOCKET_PACKET(sockfd)) { + struct packetsocket *ps = wolfIP_packetsocket_from_fd(s, sockfd); + struct wolfIP_sockaddr_ll *sll = (struct wolfIP_sockaddr_ll *)addr; + if (!ps || !sll || (addrlen && *addrlen < sizeof(struct wolfIP_sockaddr_ll))) + return -WOLFIP_EINVAL; + memcpy(sll, &ps->bind_addr, sizeof(*sll)); + return 0; + } +#endif return -1; } @@ -4355,17 +5016,36 @@ int wolfIP_sock_can_read(struct wolfIP *s, int sockfd) { struct tsocket *ts = wolfIP_socket_from_fd(s, sockfd); - if (!ts) - return -WOLFIP_EINVAL; if (IS_SOCKET_TCP(sockfd)) { + if (!ts) + return -WOLFIP_EINVAL; if (queue_len(&ts->sock.tcp.rxbuf) > 0) return 1; if (ts->sock.tcp.state == TCP_CLOSE_WAIT || ts->sock.tcp.state == TCP_CLOSED) return 1; return 0; } - if (IS_SOCKET_UDP(sockfd) || IS_SOCKET_ICMP(sockfd)) + if (IS_SOCKET_UDP(sockfd) || IS_SOCKET_ICMP(sockfd)) { + if (!ts) + return -WOLFIP_EINVAL; return fifo_len(&ts->sock.udp.rxbuf) > 0 ? 1 : 0; + } +#if WOLFIP_RAWSOCKETS + if (IS_SOCKET_RAW(sockfd)) { + struct rawsocket *rs = wolfIP_rawsocket_from_fd(s, sockfd); + if (!rs) + return -WOLFIP_EINVAL; + return fifo_len(&rs->rxbuf) > 0 ? 1 : 0; + } +#endif +#if WOLFIP_PACKET_SOCKETS + if (IS_SOCKET_PACKET(sockfd)) { + struct packetsocket *ps = wolfIP_packetsocket_from_fd(s, sockfd); + if (!ps) + return -WOLFIP_EINVAL; + return fifo_len(&ps->rxbuf) > 0 ? 1 : 0; + } +#endif return -WOLFIP_EINVAL; } @@ -4373,17 +5053,36 @@ int wolfIP_sock_can_write(struct wolfIP *s, int sockfd) { struct tsocket *ts = wolfIP_socket_from_fd(s, sockfd); - if (!ts) - return -WOLFIP_EINVAL; if (IS_SOCKET_TCP(sockfd)) { + if (!ts) + return -WOLFIP_EINVAL; if (ts->sock.tcp.state == TCP_SYN_SENT) return 0; if (ts->sock.tcp.state != TCP_ESTABLISHED) return 1; return tx_has_writable_space(ts) ? 1 : 0; } - if (IS_SOCKET_UDP(sockfd) || IS_SOCKET_ICMP(sockfd)) + if (IS_SOCKET_UDP(sockfd) || IS_SOCKET_ICMP(sockfd)) { + if (!ts) + return -WOLFIP_EINVAL; return tx_has_writable_space(ts) ? 1 : 0; + } +#if WOLFIP_RAWSOCKETS + if (IS_SOCKET_RAW(sockfd)) { + struct rawsocket *rs = wolfIP_rawsocket_from_fd(s, sockfd); + if (!rs) + return -WOLFIP_EINVAL; + return fifo_space(&rs->txbuf) > 0 ? 1 : 0; + } +#endif +#if WOLFIP_PACKET_SOCKETS + if (IS_SOCKET_PACKET(sockfd)) { + struct packetsocket *ps = wolfIP_packetsocket_from_fd(s, sockfd); + if (!ps) + return -WOLFIP_EINVAL; + return fifo_space(&ps->txbuf) > 0 ? 1 : 0; + } +#endif return -WOLFIP_EINVAL; } @@ -4396,13 +5095,44 @@ int wolfIP_sock_bind(struct wolfIP *s, int sockfd, const struct wolfIP_sockaddr const struct wolfIP_sockaddr_in *sin = (const struct wolfIP_sockaddr_in *)addr; int match = 0; unsigned int if_idx; +#if WOLFIP_PACKET_SOCKETS + uint16_t sa_family; + if (!addr || addrlen < sizeof(uint16_t)) + return -WOLFIP_EINVAL; + sa_family = addr->sa_family; +#else if (!sin || addrlen < sizeof(struct wolfIP_sockaddr_in)) return -WOLFIP_EINVAL; +#endif if (sockfd < 0) return -WOLFIP_EINVAL; +#if WOLFIP_PACKET_SOCKETS + if (IS_SOCKET_PACKET(sockfd)) { + struct packetsocket *ps = wolfIP_packetsocket_from_fd(s, sockfd); + const struct wolfIP_sockaddr_ll *sll = (const struct wolfIP_sockaddr_ll *)addr; + struct wolfIP_ll_dev *ll; + if (!ps || sa_family != AF_PACKET || addrlen < sizeof(struct wolfIP_sockaddr_ll)) + return -WOLFIP_EINVAL; + if (sll->sll_ifindex < 0 || (unsigned int)sll->sll_ifindex >= s->if_count) + return -WOLFIP_EINVAL; + ps->if_idx = (uint8_t)sll->sll_ifindex; + ps->protocol = sll->sll_protocol; + memcpy(&ps->bind_addr, sll, sizeof(ps->bind_addr)); + ll = wolfIP_ll_at(s, ps->if_idx); + if (ll) + memcpy(ps->macs.src_mac, ll->mac, 6); + if (ps->bind_addr.sll_halen == 0) + ps->bind_addr.sll_halen = 6; + return 0; + } +#endif + + if (!sin || addrlen < sizeof(struct wolfIP_sockaddr_in)) + return -WOLFIP_EINVAL; + bind_ip = ee32(sin->sin_addr.s_addr); if_idx = wolfIP_if_for_local_ip(s, bind_ip, &match); conf = wolfIP_ipconf_at(s, if_idx); @@ -4511,6 +5241,26 @@ int wolfIP_sock_bind(struct wolfIP *s, int sockfd, const struct wolfIP_sockaddr } } return 0; +#if WOLFIP_RAWSOCKETS + } else if (IS_SOCKET_RAW(sockfd)) { + struct rawsocket *rs = wolfIP_rawsocket_from_fd(s, sockfd); + if (!rs) + return -WOLFIP_EINVAL; + if (sin->sin_family != AF_INET) + return -WOLFIP_EINVAL; + rs->if_idx = (uint8_t)if_idx; + rs->bound_local_ip = bind_ip; + if (bind_ip != IPADDR_ANY) + rs->local_ip = bind_ip; + else if (conf && conf->ip != IPADDR_ANY) + rs->local_ip = conf->ip; + else { + struct ipconf *primary = wolfIP_primary_ipconf(s); + if (primary && primary->ip != IPADDR_ANY) + rs->local_ip = primary->ip; + } + return 0; +#endif } else return -1; } @@ -4547,19 +5297,33 @@ int wolfIP_sock_getpeername(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr struct wolfIP_sockaddr_in *sin = (struct wolfIP_sockaddr_in *)addr; if (sockfd < 0) return -WOLFIP_EINVAL; - if (!IS_SOCKET_TCP(sockfd)) { - return -1; + if (IS_SOCKET_TCP(sockfd)) { + if (SOCKET_UNMARK(sockfd) >= MAX_TCPSOCKETS) + return -WOLFIP_EINVAL; + ts = &s->tcpsockets[SOCKET_UNMARK(sockfd)]; + if (!sin || !addrlen || *addrlen < sizeof(struct wolfIP_sockaddr_in)) + return -1; + sin->sin_family = AF_INET; + sin->sin_port = ee16(ts->dst_port); + sin->sin_addr.s_addr = ee32(ts->remote_ip); + return 0; } - if (SOCKET_UNMARK(sockfd) >= MAX_TCPSOCKETS) - return -WOLFIP_EINVAL; - - ts = &s->tcpsockets[SOCKET_UNMARK(sockfd)]; - if (!sin || !addrlen || *addrlen < sizeof(struct wolfIP_sockaddr_in)) - return -1; - sin->sin_family = AF_INET; - sin->sin_port = ee16(ts->dst_port); - sin->sin_addr.s_addr = ee32(ts->remote_ip); - return 0; +#if WOLFIP_RAWSOCKETS + if (IS_SOCKET_RAW(sockfd)) { + struct rawsocket *rs = wolfIP_rawsocket_from_fd(s, sockfd); + if (!rs) + return -WOLFIP_EINVAL; + if (!sin || !addrlen || *addrlen < sizeof(struct wolfIP_sockaddr_in)) + return -1; + if (rs->remote_ip == 0) + return -1; + sin->sin_family = AF_INET; + sin->sin_port = 0; + sin->sin_addr.s_addr = ee32(rs->remote_ip); + return 0; + } +#endif + return -1; } @@ -5244,6 +6008,13 @@ static int arp_lookup(struct wolfIP *s, unsigned int if_idx, ip4 ip, uint8_t *ma return -1; } +int wolfIP_arp_lookup_ex(struct wolfIP *s, unsigned int if_idx, ip4 ip, uint8_t *mac) +{ + if (!s || !mac) + return -WOLFIP_EINVAL; + return arp_lookup(s, if_idx, ip, mac); +} + #endif /* Initialize the IP stack */ @@ -5337,6 +6108,9 @@ static inline void ip_recv(struct wolfIP *s, unsigned int if_idx, #endif if (wolfIP_filter_notify_ip(WOLFIP_FILT_RECEIVING, s, if_idx, ip, len) != 0) return; +#if WOLFIP_RAWSOCKETS + raw_try_recv(s, if_idx, ip, len); +#endif #if WOLFIP_ENABLE_FORWARDING if (ip->ver_ihl == 0x45) { ip4 dest = ee32(ip->dst); @@ -5400,6 +6174,12 @@ static inline void ip_recv(struct wolfIP *s, unsigned int if_idx, } else if (ip->ver_ihl == 0x45 && ip->proto == 0x11) { struct wolfIP_udp_datagram *udp = (struct wolfIP_udp_datagram *)ip; + if (len < sizeof(struct wolfIP_udp_datagram)) + return; + if (ee16(udp->len) < UDP_HEADER_LEN) + return; + if (ee16(udp->len) > len - ETH_HEADER_LEN - IP_HEADER_LEN) + return; #ifdef DEBUG_UDP wolfIP_print_udp(udp); #endif /* DEBUG_UDP */ @@ -5436,6 +6216,9 @@ static void wolfIP_recv_on(struct wolfIP *s, unsigned int if_idx, void *buf, uin #endif /* DEBUG_ETH */ if (wolfIP_filter_notify_eth(WOLFIP_FILT_RECEIVING, s, if_idx, eth, len) != 0) return; +#if WOLFIP_PACKET_SOCKETS + packet_try_recv(s, if_idx, eth, len); +#endif if (eth->type == ee16(ETH_TYPE_IP)) { struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)eth; if ((memcmp(eth->dst, ll->mac, 6) != 0) && (memcmp(eth->dst, "\xff\xff\xff\xff\xff\xff", 6) != 0)) { @@ -5840,6 +6623,24 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) ts->events = 0; } } +#if WOLFIP_RAWSOCKETS + for (i = 0; i < WOLFIP_MAX_RAWSOCKETS; i++) { + struct rawsocket *r = &s->rawsockets[i]; + if (r->used && (r->callback) && (r->events)) { + r->callback(i | MARK_RAW_SOCKET, r->events, r->callback_arg); + r->events = 0; + } + } +#endif +#if WOLFIP_PACKET_SOCKETS + for (i = 0; i < WOLFIP_MAX_PACKETSOCKETS; i++) { + struct packetsocket *p = &s->packetsockets[i]; + if (p->used && (p->callback) && (p->events)) { + p->callback(i | MARK_PACKET_SOCKET, p->events, p->callback_arg); + p->events = 0; + } + } +#endif /* Step 4: attempt to write any pending data */ /** @@ -6074,6 +6875,95 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) desc = fifo_peek(&t->sock.udp.txbuf); } } +#if WOLFIP_RAWSOCKETS + for (i = 0; i < WOLFIP_MAX_RAWSOCKETS; i++) { + struct rawsocket *r = &s->rawsockets[i]; + struct pkt_desc *desc; + if (!r->used) + continue; + desc = fifo_peek(&r->txbuf); + while (desc) { + struct wolfIP_ip_packet *ip = + (struct wolfIP_ip_packet *)(r->txmem + desc->pos + sizeof(*desc)); + ip4 dst_ip = ee32(ip->dst); + unsigned int tx_if = r->if_idx; + ip4 nexthop; +#ifdef ETHERNET + struct ipconf *conf; +#endif + if (dst_ip == 0) { + fifo_pop(&r->txbuf); + desc = fifo_peek(&r->txbuf); + continue; + } + if (tx_if >= s->if_count) + tx_if = raw_route_for_ip(s, r, dst_ip, r->dontroute); + r->if_idx = (uint8_t)tx_if; +#ifdef ETHERNET + conf = wolfIP_ipconf_at(s, tx_if); + nexthop = r->dontroute ? dst_ip : wolfIP_select_nexthop(conf, dst_ip); + if (wolfIP_is_loopback_if(tx_if)) { + struct wolfIP_ll_dev *loop = wolfIP_ll_at(s, tx_if); + if (loop) + memcpy(r->nexthop_mac, loop->mac, 6); + } else if ((!IS_IP_BCAST(nexthop) && (arp_lookup(s, tx_if, nexthop, r->nexthop_mac) < 0))) { + arp_request(s, tx_if, nexthop); + break; + } else if (IS_IP_BCAST(nexthop)) { + memset(r->nexthop_mac, 0xFF, 6); + } +#else + nexthop = dst_ip; +#endif + if (!r->ipheader_include) { + ip->csum = 0; + iphdr_set_checksum(ip); + } + if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, s, tx_if, ip, desc->len) != 0) + break; +#ifdef ETHERNET + if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, s, tx_if, &ip->eth, desc->len) != 0) + break; + eth_output_add_header(s, tx_if, r->nexthop_mac, &ip->eth, ETH_TYPE_IP); +#endif + { + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, tx_if); + if (ll && ll->send) + ll->send(ll, ip, desc->len); + } + fifo_pop(&r->txbuf); + desc = fifo_peek(&r->txbuf); + (void)nexthop; + } + } +#endif +#if WOLFIP_PACKET_SOCKETS + for (i = 0; i < WOLFIP_MAX_PACKETSOCKETS; i++) { + struct packetsocket *p = &s->packetsockets[i]; + struct pkt_desc *desc; + if (!p->used) + continue; + desc = fifo_peek(&p->txbuf); + while (desc) { + uint8_t *frame = p->txmem + desc->pos + sizeof(*desc); + unsigned int tx_if = p->if_idx; + if (tx_if >= s->if_count) + tx_if = 0; + if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, s, tx_if, + (struct wolfIP_eth_frame *)frame, desc->len) != 0) { + desc = fifo_next(&p->txbuf, desc); + continue; + } + { + struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, tx_if); + if (ll && ll->send) + ll->send(ll, frame, desc->len); + } + fifo_pop(&p->txbuf); + desc = fifo_peek(&p->txbuf); + } + } +#endif return 0; } diff --git a/wolfip.h b/wolfip.h index 309f7722..fb9bc4d2 100644 --- a/wolfip.h +++ b/wolfip.h @@ -32,6 +32,22 @@ typedef unsigned long size_t; #endif #endif +#ifndef WOLFIP_SOL_SOCKET +#ifdef SOL_SOCKET +#define WOLFIP_SOL_SOCKET SOL_SOCKET +#else +#define WOLFIP_SOL_SOCKET 1 +#endif +#endif + +#ifndef WOLFIP_SOL_PACKET +#ifdef SOL_PACKET +#define WOLFIP_SOL_PACKET SOL_PACKET +#else +#define WOLFIP_SOL_PACKET 263 +#endif +#endif + #ifndef WOLFIP_IP_RECVTTL #ifdef IP_RECVTTL #define WOLFIP_IP_RECVTTL IP_RECVTTL @@ -40,6 +56,22 @@ typedef unsigned long size_t; #endif #endif +#ifndef WOLFIP_IP_HDRINCL +#ifdef IP_HDRINCL +#define WOLFIP_IP_HDRINCL IP_HDRINCL +#else +#define WOLFIP_IP_HDRINCL 3 +#endif +#endif + +#ifndef WOLFIP_SO_DONTROUTE +#ifdef SO_DONTROUTE +#define WOLFIP_SO_DONTROUTE SO_DONTROUTE +#else +#define WOLFIP_SO_DONTROUTE 5 +#endif +#endif + /* Types */ struct wolfIP; typedef uint32_t ip4; @@ -114,10 +146,14 @@ struct ipconf { #define MARK_TCP_SOCKET 0x100 /* Mark a socket as TCP */ #define MARK_UDP_SOCKET 0x200 /* Mark a socket as UDP */ #define MARK_ICMP_SOCKET 0x400 /* Mark a socket as ICMP */ +#define MARK_RAW_SOCKET 0x800 /* Mark a socket as RAW */ +#define MARK_PACKET_SOCKET 0x1000 /* Mark a socket as PACKET */ #define IS_SOCKET_TCP(fd) (((fd) & MARK_TCP_SOCKET) == MARK_TCP_SOCKET) #define IS_SOCKET_UDP(fd) (((fd) & MARK_UDP_SOCKET) == MARK_UDP_SOCKET) #define IS_SOCKET_ICMP(fd)(((fd) & MARK_ICMP_SOCKET) == MARK_ICMP_SOCKET) +#define IS_SOCKET_RAW(fd) (((fd) & MARK_RAW_SOCKET) == MARK_RAW_SOCKET) +#define IS_SOCKET_PACKET(fd) (((fd) & MARK_PACKET_SOCKET) == MARK_PACKET_SOCKET) #define SOCKET_UNMARK(fd) ((fd) & 0xFF) /* Compile-time sanity check for socket marks & number of sockets */ @@ -129,6 +165,14 @@ struct ipconf { #error "MARK_UDP_SOCKET must be less than MARK_ICMP_SOCKET" #endif +#if (MARK_ICMP_SOCKET >= MARK_RAW_SOCKET) +#error "MARK_ICMP_SOCKET must be less than MARK_RAW_SOCKET" +#endif + +#if (MARK_RAW_SOCKET >= MARK_PACKET_SOCKET) +#error "MARK_RAW_SOCKET must be less than MARK_PACKET_SOCKET" +#endif + #if MAX_TCPSOCKETS > 255 #error "MAX_TCPSOCKETS must be less than 256" #endif @@ -141,11 +185,26 @@ struct ipconf { #error "MAX_ICMPSOCKETS must be less than 256" #endif +#if WOLFIP_RAWSOCKETS +#if WOLFIP_MAX_RAWSOCKETS > 255 +#error "WOLFIP_MAX_RAWSOCKETS must be less than 256" +#endif +#endif + +#if WOLFIP_PACKET_SOCKETS +#if WOLFIP_MAX_PACKETSOCKETS > 255 +#error "WOLFIP_MAX_PACKETSOCKETS must be less than 256" +#endif +#endif #ifndef WOLF_POSIX #define IPSTACK_SOCK_STREAM 1 #define IPSTACK_SOCK_DGRAM 2 +#define IPSTACK_SOCK_RAW 3 +#ifndef AF_PACKET +#define AF_PACKET 17 +#endif struct wolfIP_sockaddr_in { @@ -180,14 +239,38 @@ struct msghdr { #ifndef AF_INET #define AF_INET 2 #endif +#ifndef AF_PACKET +#define AF_PACKET 17 +#endif #else #include #include #include #include #include +#include +#ifdef __has_include +#if __has_include() +#include +#define wolfIP_sockaddr_ll sockaddr_ll +#endif +#endif #define wolfIP_sockaddr_in sockaddr_in #define wolfIP_sockaddr sockaddr +#define IPSTACK_SOCK_RAW SOCK_RAW +#endif + +#ifndef wolfIP_sockaddr_ll +struct wolfIP_sockaddr_ll { + unsigned short sll_family; + unsigned short sll_protocol; + int sll_ifindex; + unsigned short sll_hatype; + unsigned char sll_pkttype; + unsigned char sll_halen; + unsigned char sll_addr[8]; +}; +typedef struct wolfIP_sockaddr_ll wolfIP_sockaddr_ll; #endif int wolfIP_sock_socket(struct wolfIP *s, int domain, int type, int protocol); @@ -253,6 +336,7 @@ struct wolfIP_ll_dev *wolfIP_getdev(struct wolfIP *s); struct wolfIP_ll_dev *wolfIP_getdev_ex(struct wolfIP *s, unsigned int if_idx); void wolfIP_ipconfig_set_ex(struct wolfIP *s, unsigned int if_idx, ip4 ip, ip4 mask, ip4 gw); void wolfIP_ipconfig_get_ex(struct wolfIP *s, unsigned int if_idx, ip4 *ip, ip4 *mask, ip4 *gw); +int wolfIP_arp_lookup_ex(struct wolfIP *s, unsigned int if_idx, ip4 ip, uint8_t *mac); /* Callback flags */ #define CB_EVENT_READABLE 0x01 /* Accepted connection or data available */