Add support for raw + af_packet sockets#63
Add support for raw + af_packet sockets#63danielinux wants to merge 8 commits intowolfSSL:masterfrom
Conversation
Add support for raw sockets and packet sockets. - Added support for BSD wrapper in posix systems. - Added 'raw_ping' and 'packet_ping' examples to craft ICMP echo requests / receive replies using the new sockets - Added automated tests to github workflow
There was a problem hiding this comment.
Pull request overview
Adds raw IP socket and AF_PACKET-style packet socket support to wolfIP, including POSIX preload shim enhancements and new example utilities for pinging via the new socket types.
Changes:
- Add RAW and PACKET socket types to the wolfIP socket API, including send/recv/bind/connect/poll support.
- Extend the POSIX LD_PRELOAD BSD socket shim with
ioctl()handling for interface/ARP queries needed by packet sockets. - Add new
raw_ping/packet_pingexamples and run them in CI.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
wolfip.h |
Adds socket constants, socket marks, and sockaddr_ll support plus ARP lookup API. |
src/wolfip.c |
Implements raw sockets + packet sockets, routing/ARP for raw TX, and capture paths for RX. |
src/test/unit/unit.c |
Adds unit tests covering raw/packet socket send/recv behaviors. |
src/test/raw_ping.c |
New raw-socket ping example for CI/manual testing. |
src/test/packet_ping.c |
New AF_PACKET ping example using ioctl-derived interface info. |
src/port/posix/bsd_socket.c |
Adds ioctl() interception and fd bookkeeping for raw/packet sockets. |
config.h |
Enables raw/packet sockets by default and sets default max counts. |
Makefile |
Builds the new ping example binaries. |
.github/workflows/linux.yml |
Runs raw_ping and packet_ping under LD_PRELOAD in CI. |
Comments suppressed due to low confidence (2)
src/test/raw_ping.c:55
- The checksum helper handles odd-length buffers incorrectly: for a trailing byte it should be added as the high-order byte of the final 16-bit word (i.e.,
byte << 8, zero-padded), not as a low-order byte. This matches how Internet checksums are computed (and how the stack’s checksum helpers handle odd lengths).
if (len == 1) {
sum += *(unsigned char *)buf;
}
src/test/packet_ping.c:99
- The checksum helper adds the trailing byte for odd-length buffers without shifting it into the high-order position of the final 16-bit word. For correctness with arbitrary payload sizes, treat the last byte as
byte << 8(zero-padded) whenlenis odd.
if (len == 1) {
sum += *(unsigned char *)buf;
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 10 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 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) { |
There was a problem hiding this comment.
ioctl varargs are read as unsigned long, but callers pass a pointer for requests like SIOCGIFINDEX/SIOCGARP. Reading a pointer argument with the wrong va_arg type is undefined behavior and can break on some ABIs. Read the vararg as void * (or uintptr_t) and forward that same pointer to host_ioctl.
| 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) { | |
| void *argp; | |
| struct ifreq *ifr; | |
| int i; | |
| va_start(ap, request); | |
| argp = va_arg(ap, void *); | |
| va_end(ap); | |
| if (in_the_stack) { | |
| return host_ioctl ? host_ioctl(fd, request, argp) : -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, argp) : -1; |
| ret = wolfIP_sock_bind(IPSTACK, internal_fd, | ||
| (const struct wolfIP_sockaddr *)addr, addrlen); | ||
| } | ||
| if (ret < 0) { |
There was a problem hiding this comment.
wolfIP APIs return negative error codes (e.g., -WOLFIP_EINVAL). Assigning errno = ret sets errno to a negative value. Set errno to -ret (positive) to match POSIX expectations.
| if (ret < 0) { | |
| errno = -ret; |
| } | ||
| if (ret != -EAGAIN) | ||
| break; | ||
| if (nonblock) { | ||
| errno = EAGAIN; | ||
| pthread_mutex_unlock(&wolfIP_mutex); | ||
| return -1; |
There was a problem hiding this comment.
This wrapper mixes host EAGAIN with wolfIP's -WOLFIP_EAGAIN and also assigns errno = ret (negative). Use wolfIP's return convention consistently: compare against -WOLFIP_EAGAIN and set errno to -ret when returning -1.
| } while (1); | ||
| errno = ret; | ||
| pthread_mutex_unlock(&wolfIP_mutex); |
There was a problem hiding this comment.
This wrapper mixes host EAGAIN with wolfIP's -WOLFIP_EAGAIN and also assigns errno = ret (negative). Use wolfIP's return convention consistently: compare against -WOLFIP_EAGAIN and set errno to -ret when returning -1.
| 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; |
There was a problem hiding this comment.
The loop advances desc with fifo_next() when a packet is filtered, but later always removes the head element with fifo_pop(). If desc is not the head, this can send one packet while popping a different one (queue corruption / wrong packet dropped). Since fifo_pop() only pops the head, the safe options are to (a) process only the head (on filter reject: break;), or (b) explicitly drop the head on filter reject (pop it) rather than skipping ahead.
| 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; | |
| /* Drop filtered packet from the head of the FIFO. */ | |
| fifo_pop(&p->txbuf); | |
| } | |
| else { | |
| 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); | |
| } |
| continue; | ||
| } | ||
| { |
There was a problem hiding this comment.
The loop advances desc with fifo_next() when a packet is filtered, but later always removes the head element with fifo_pop(). If desc is not the head, this can send one packet while popping a different one (queue corruption / wrong packet dropped). Since fifo_pop() only pops the head, the safe options are to (a) process only the head (on filter reject: break;), or (b) explicitly drop the head on filter reject (pop it) rather than skipping ahead.
| if (!ps) | ||
| return -WOLFIP_EINVAL; | ||
| if (sll && addrlen && *addrlen < sizeof(struct wolfIP_sockaddr_ll)) | ||
| return -WOLFIP_EINVAL; |
There was a problem hiding this comment.
Same issue as the raw socket path: returning plain -1 makes the error ambiguous for callers. Prefer a -WOLFIP_* error code consistent with other recv/send failures.
| return -WOLFIP_EINVAL; | |
| return -WOLFIP_EMSGSIZE; |
| return 0; | ||
| } | ||
| return -WOLFIP_EINVAL; | ||
| } | ||
| #endif | ||
| #if WOLFIP_PACKET_SOCKETS |
There was a problem hiding this comment.
For packet sockets, setsockopt currently returns success for any option and even ignores optval/optlen. This can mask real caller errors and diverges from the raw/INET behavior (which returns -WOLFIP_EINVAL for unsupported options). If no options are supported yet, return -WOLFIP_EINVAL (or selectively implement supported options) and validate optval/optlen accordingly.
| return 0; | |
| } | |
| return -WOLFIP_EINVAL; | |
| } | |
| #endif | |
| #if WOLFIP_PACKET_SOCKETS | |
| /* No socket options are currently supported for packet sockets. | |
| * To avoid masking caller errors and to match raw/INET behavior, | |
| * reject all setsockopt calls on packet sockets. | |
| */ | |
| (void)s; | |
| return -WOLFIP_EINVAL; |
Add support for raw sockets and packet sockets.
Superseeds #10