--- /dev/null
+/*
+ * test83: test bad network packets
+ */
+
+#define DEBUG 0
+
+#if DEBUG
+#define dbgprintf(...) do { \
+ struct timeval time = { }; \
+ gettimeofday(&time, NULL); \
+ fprintf(stderr, "[%2d:%.2d:%.2d.%.6d p%d %s:%d] ", \
+ (int) ((time.tv_sec / 3600) % 24), \
+ (int) ((time.tv_sec / 60) % 60), \
+ (int) (time.tv_sec % 60), \
+ time.tv_usec, \
+ getpid(), \
+ __FUNCTION__, \
+ __LINE__); \
+ fprintf(stderr, __VA_ARGS__); \
+ fflush(stderr); \
+ } while (0)
+#else
+#define dbgprintf(...)
+#endif
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <net/gen/ether.h>
+#include <net/gen/eth_io.h>
+#include <net/gen/in.h>
+#include <net/gen/ip_io.h>
+
+#include "common.h"
+
+int max_error = 100;
+
+/* https://tools.ietf.org/html/rfc791 */
+struct header_ip {
+ uint8_t ver_ihl; /* Version (4 bits) + IHL (4 bits) */
+ uint8_t tos; /* Type of Service */
+ uint16_t len; /* Total Length */
+ uint16_t id; /* Identification */
+ uint16_t fl_fo; /* Flags (3 bits) + Fragment Offset (13 bits) */
+ uint8_t ttl; /* Time to Live */
+ uint8_t prot; /* Protocol */
+ uint16_t cs; /* Header Checksum */
+ uint32_t src; /* Source Address */
+ uint32_t dst; /* Destination Address */
+ uint8_t opt[16]; /* Options */
+};
+#define IP_FLAG_EVIL (1 << 2)
+#define IP_FLAG_DF (1 << 1)
+#define IP_FLAG_MF (1 << 0)
+
+/* https://tools.ietf.org/html/rfc790 */
+#define IP_PROT_ICMP 1
+#define IP_PROT_TCP 6
+#define IP_PROT_UDP 17
+
+/* https://tools.ietf.org/html/rfc768 */
+struct header_udp {
+ uint16_t src; /* Source Port */
+ uint16_t dst; /* Destination Port */
+ uint16_t len; /* Length */
+ uint16_t cs; /* Checksum */
+};
+
+struct header_udp_pseudo {
+ uint32_t src;
+ uint32_t dst;
+ uint8_t zero;
+ uint8_t prot;
+ uint16_t len;
+};
+
+/* https://tools.ietf.org/html/rfc793 */
+struct header_tcp {
+ uint16_t src; /* Source Port */
+ uint16_t dst; /* Destination Port */
+ uint32_t seq; /* Sequence Number */
+ uint32_t ack; /* Acknowledgment Number */
+ uint8_t doff; /* Data Offset */
+ uint8_t fl; /* Flags */
+ uint16_t win; /* Window */
+ uint16_t cs; /* Checksum */
+ uint16_t uptr; /* Urgent Pointer */
+ uint8_t opt[16]; /* Options */
+};
+#define TCP_FLAG_URG (1 << 5)
+#define TCP_FLAG_ACK (1 << 4)
+#define TCP_FLAG_PSH (1 << 3)
+#define TCP_FLAG_RST (1 << 2)
+#define TCP_FLAG_SYN (1 << 1)
+#define TCP_FLAG_FIN (1 << 0)
+
+#define PORT_BASE 12345
+#define PORT_COUNT_TCP 4
+#define PORT_COUNT_UDP 2
+#define PORT_COUNT (PORT_COUNT_TCP + PORT_COUNT_UDP)
+
+#define PORT_BASE_SRC (PORT_BASE + PORT_COUNT)
+#define PORT_COUNT_SRC 79
+
+#define PAYLOADSIZE_COUNT 6
+static const size_t payloadsizes[] = {
+ 0,
+ 1,
+ 100,
+ 1024,
+ 2345,
+ 65535 - sizeof(struct header_ip) - sizeof(struct header_udp),
+};
+
+#define ADDR_COUNT_MAX 10
+static size_t addr_count;
+static uint32_t addrsrc = 0xc0000201; /* 192.0.2.1 (TEST-NET) */
+static uint32_t addrdst = 0x7f000001; /* 127.0.0.1 (localhost) */
+static uint32_t addrs[ADDR_COUNT_MAX] = {
+ 0x00000000, /* 0.0.0.0 (INADDR_NONE) */
+ 0x7f000001, /* 127.0.0.1 (localhost) */
+ 0xc0000201, /* 192.0.2.1 (TEST-NET) */
+ 0xffffffff, /* 255.255.255.255 (broadcast) */
+ /* local addresses will be added */
+};
+
+#define CLOSE(fd) do { assert(fd >= 0); if (close((fd)) != 0) efmt("close failed"); } while (0);
+enum server_action {
+ sa_close,
+ sa_read,
+ sa_selectr,
+ sa_selectrw,
+ sa_write,
+};
+static int server_done;
+
+static void server_alarm(int seconds);
+
+static char *sigstr_cat(char *p, const char *s) {
+ size_t slen = strlen(s);
+ memcpy(p, s, slen);
+ return p + slen;
+}
+
+static char *sigstr_itoa(char *p, unsigned long n) {
+ unsigned digit;
+ unsigned long factor = 1000000000UL;
+ int first = 1;
+
+ while (factor > 0) {
+ digit = (n / factor) % 10;
+ if (!first || digit || factor == 1) {
+ *(p++) = digit + '0';
+ first = 0;
+ }
+ factor /= 10;
+ }
+ return p;
+}
+
+static void dbgprintdata(const void *data, size_t size) {
+#if DEBUG
+ size_t addr;
+ const unsigned char *p = data;
+
+ for (addr = 0; addr < size; addr++) {
+ if (addr % 16 == 0) {
+ if (addr > 0) fprintf(stderr, "\n");
+ fprintf(stderr, "%.4zx", addr);
+ }
+ fprintf(stderr, " %.2x", p[addr]);
+ }
+ fprintf(stderr, "\n");
+ fflush(stderr);
+#endif
+}
+
+static void dbgprint_sig(const char *name) {
+#if DEBUG
+ char buf[256];
+ char *p = buf;
+
+ /* fprintf not used to be signal safe */
+ p = sigstr_cat(p, "[");
+ p = sigstr_itoa(p, getpid());
+ p = sigstr_cat(p, "] ");
+ p = sigstr_cat(p, name);
+ p = sigstr_cat(p, "\n");
+ write(STDERR_FILENO, buf, p - buf);
+#endif
+}
+
+#define SIGNAL(sig, handler) (signal_checked((sig), (handler), #sig, __FILE__, __FUNCTION__, __LINE__))
+
+static void signal_checked(int sig, void (* handler)(int), const char *signame,
+ const char *file, const char *func, int line) {
+ char buf[256];
+ char *p = buf;
+ struct sigaction sa = {
+ .sa_handler = handler,
+ };
+
+ if (sigaction(sig, &sa, NULL) == 0) return;
+
+ /* efmt not used to be signal safe */
+ p = sigstr_cat(p, "[");
+ p = sigstr_cat(p, file);
+ p = sigstr_cat(p, ":");
+ p = sigstr_itoa(p, line);
+ p = sigstr_cat(p, "] error: sigaction(");
+ p = sigstr_cat(p, signame);
+ p = sigstr_cat(p, ") failed in function ");
+ p = sigstr_cat(p, func);
+ p = sigstr_cat(p, ": ");
+ p = sigstr_itoa(p, errno);
+ p = sigstr_cat(p, "\n");
+ write(STDERR_FILENO, buf, p - buf);
+ errct++;
+}
+
+static void server_sigusr1(int signo) {
+ dbgprint_sig("SIGUSR1");
+
+ /* terminate on the first opportunity */
+ server_done = 1;
+
+ /* in case signal is caught before a blocking operation,
+ * keep interrupting
+ */
+ server_alarm(1);
+}
+
+static void server_stop(pid_t pid) {
+
+ if (pid < 0) return;
+
+ dbgprintf("sending SIGUSR1 to child %d\n", (int) pid);
+ if (kill(pid, SIGUSR1) != 0) efmt("kill failed");
+}
+
+static void server_wait(pid_t pid) {
+ int exitcode, status;
+ pid_t r;
+
+ if (pid < 0) return;
+
+ dbgprintf("waiting for child %d\n", (int) pid);
+ r = waitpid(pid, &status, 0);
+ if (r != pid) {
+ efmt("waitpid failed");
+ return;
+ }
+
+ if (WIFEXITED(status)) {
+ exitcode = WEXITSTATUS(status);
+ if (exitcode < 0) {
+ efmt("negative exit code from child %d\n", (int) pid);
+ } else {
+ dbgprintf("child exited exitcode=%d\n", exitcode);
+ errct += exitcode;
+ }
+ } else if (WIFSIGNALED(status)) {
+ efmt("child killed by signal %d", WTERMSIG(status));
+ } else {
+ efmt("child has unexpected exit status 0x%x", status);
+ }
+}
+
+static void server_sigalrm(int signum) {
+ server_alarm(1);
+}
+
+static void server_alarm(int seconds) {
+ SIGNAL(SIGALRM, server_sigalrm);
+ alarm(seconds);
+}
+
+static void server_no_alarm(void) {
+ int errno_old = errno;
+ alarm(0);
+ SIGNAL(SIGALRM, SIG_DFL);
+ errno = errno_old;
+}
+
+static int server_rw(int fd, int is_write, int *success) {
+ char buf[4096];
+ ssize_t r;
+
+ /* return 0 means close connection, *success=0 means stop server */
+
+ if (is_write) {
+ /* ignore SIGPIPE */
+ SIGNAL(SIGPIPE, SIG_IGN);
+
+ /* initialize buffer */
+ memset(buf, -1, sizeof(buf));
+ }
+
+ /* don't block for more than 1s */
+ server_alarm(1);
+
+ /* perform read or write operation */
+ dbgprintf("server_rw waiting is_write=%d\n", is_write);
+ r = is_write ? write(fd, buf, sizeof(buf)) : read(fd, buf, sizeof(buf));
+
+ /* stop alarm (preserves errno) */
+ server_no_alarm();
+
+ /* handle read/write result */
+ if (r >= 0) {
+ dbgprintf("server_rw done\n");
+ *success = 1;
+ return r > 0;
+ }
+
+ switch (errno) {
+ case EINTR:
+ dbgprintf("server_rw interrupted\n");
+ *success = 1;
+ return 0;
+ case ECONNRESET:
+ dbgprintf("server_rw connection reset\n");
+ *success = 1;
+ return 0;
+ case EPIPE:
+ if (is_write) {
+ dbgprintf("server_rw EPIPE\n");
+ *success = 1;
+ return 0;
+ }
+ /* fall through */
+ default:
+ efmt("%s failed", is_write ? "write" : "read");
+ *success = 0;
+ return 0;
+ }
+}
+
+static int server_select(int fd, int is_rw, int *success,
+ enum server_action *actionnext) {
+ int r;
+ fd_set readfds, writefds;
+ struct timeval timeout = { .tv_sec = 1, .tv_usec = 0 };
+
+ /* return 0 means close connection, *success=0 means stop server */
+
+ /* prepare fd sets */
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+ FD_ZERO(&writefds);
+ if (is_rw) FD_SET(fd, &writefds);
+
+ /* perform select */
+ errno = 0;
+ dbgprintf("server_select waiting\n");
+ r = select(fd + 1, &readfds, &writefds, NULL, &timeout);
+
+ /* handle result */
+ if (r < 0) {
+ switch (errno) {
+ case EINTR:
+ dbgprintf("server_select interrupted\n");
+ *success = 1;
+ return 0;
+ default:
+ efmt("select failed");
+ *success = 0;
+ return 0;
+ }
+ }
+ if (r == 0) {
+ dbgprintf("server_select nothing available\n");
+ *success = 1;
+ return 0;
+ }
+
+ if (FD_ISSET(fd, &readfds)) {
+ dbgprintf("server_select read available\n");
+ *actionnext = sa_read;
+ *success = 1;
+ return 1;
+ } else if (FD_ISSET(fd, &writefds)) {
+ dbgprintf("server_select write available\n");
+ *actionnext = sa_write;
+ *success = 1;
+ return 1;
+ }
+
+ *success = 0;
+ efmt("select did not set fd");
+ return 0;
+}
+
+static int server_accept(int servfd, int type, enum server_action action) {
+ enum server_action actionnext;
+ struct sockaddr addr;
+ socklen_t addrsize;
+ int connfd;
+ int success = 0;
+
+ /* if connection-oriented, accept a conmection */
+ if (type == SOCK_DGRAM) {
+ connfd = servfd;
+ } else {
+ dbgprintf("server_accept waiting for connection\n");
+ addrsize = sizeof(addr);
+ connfd = accept(servfd, &addr, &addrsize);
+ if (connfd < 0) {
+ switch (errno) {
+ case EINTR:
+ dbgprintf("server_accept interrupted\n");
+ return 1;
+ default:
+ efmt("cannot accept connection");
+ return 0;
+ }
+ }
+ dbgprintf("server_accept new connection\n");
+ }
+
+ /* perform requested action while the connection is open */
+ actionnext = action;
+ while (!server_done) {
+ switch (actionnext) {
+ case sa_close:
+ success = 1;
+ goto cleanup;
+ case sa_read:
+ if (!server_rw(connfd, 0, &success)) goto cleanup;
+ actionnext = action;
+ break;
+ case sa_selectr:
+ case sa_selectrw:
+ if (!server_select(connfd, actionnext == sa_selectrw,
+ &success, &actionnext)) {
+ goto cleanup;
+ }
+ break;
+ case sa_write:
+ if (!server_rw(connfd, 1, &success)) goto cleanup;
+ actionnext = action;
+ break;
+ default:
+ efmt("bad server action");
+ success = 0;
+ goto cleanup;
+ }
+ }
+
+ /* socket connection socket */
+cleanup:
+ dbgprintf("server_accept done success=%d\n", success);
+ if (connfd != servfd) CLOSE(connfd);
+ return success;
+}
+
+static pid_t server_start(int type, int port, enum server_action action) {
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(port),
+ .sin_addr = { htonl(INADDR_ANY) },
+ };
+ int fd;
+ pid_t pid = -1;
+
+ dbgprintf("server_start port %d\n", port);
+
+ /* create socket */
+ fd = socket(AF_INET, type, 0);
+ if (fd < 0) {
+ efmt("cannot create socket");
+ goto cleanup;
+ }
+
+ /* bind socket */
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0) {
+ efmt("cannot bind socket");
+ goto cleanup;
+ }
+
+ /* make it a server socket if needed */
+ if (type != SOCK_DGRAM) {
+ if (listen(fd, 5) != 0) {
+ efmt("cannot listen on socket");
+ goto cleanup;
+ }
+ }
+
+ /* intercept SIGUSR1 in case parent wants the server to stop */
+ SIGNAL(SIGUSR1, server_sigusr1);
+
+ /* fork; parent continues, child becomes server */
+ pid = fork();
+ if (pid < 0) {
+ efmt("cannot create socket");
+ goto cleanup;
+ }
+ if (pid) goto cleanup;
+
+ /* server loop */
+ dbgprintf("server_start child\n");
+ while (!server_done && server_accept(fd, type, action)) {}
+ dbgprintf("server_start child returns\n");
+
+ CLOSE(fd);
+ exit(errct);
+
+cleanup:
+ dbgprintf("server_start parent returns pid=%d\n", (int) pid);
+ if (fd >= 0) CLOSE(fd);
+ return pid;
+}
+
+static ssize_t send_packet_raw(int fd, const void *buf, size_t size) {
+ return write(fd, buf, size);
+}
+
+enum settings_ip {
+ si_bad_version = (1 << 0),
+ si_bad_ihl_small = (1 << 1),
+ si_bad_ihl_big = (1 << 2),
+ si_bad_len_small = (1 << 3),
+ si_bad_len_big = (1 << 4),
+ si_bad_len_huge = (1 << 5),
+ si_bad_cs = (1 << 6),
+ si_zero_cs = (1 << 7),
+
+ si_flag_evil = (1 << 8),
+ si_flag_df = (1 << 9),
+ si_flag_mf = (1 << 10),
+
+ si_opt_end = (1 << 11),
+ si_opt_topsec = (1 << 12),
+ si_opt_nop = (1 << 13),
+ si_opt_badopt = (1 << 14),
+ si_opt_badpad = (1 << 15),
+};
+
+enum settings_udp {
+ su_bad_len_small = (1 << 0),
+ su_bad_len_big = (1 << 1),
+ su_bad_len_huge = (1 << 2),
+ su_bad_cs = (1 << 3),
+ su_zero_cs = (1 << 4),
+};
+
+enum fragmode_ip {
+ fi_as_needed,
+ fi_one,
+ fi_two,
+ fi_frag_tiny,
+ fi_frag_overlap,
+ fi_frag_first,
+ fi_frag_last,
+ fi_frag_repeat,
+ fi_fo_max,
+};
+
+static uint16_t checksum_ip(const void *header, size_t headersize) {
+ const uint16_t *p = header;
+ uint32_t sum = 0;
+
+ while (headersize > 0) {
+ assert(headersize >= sizeof(*p));
+ sum += ntohs(*p);
+ headersize -= sizeof(*p);
+ p++;
+ }
+ sum += sum >> 16;
+ return htons(~sum);
+}
+
+static void send_packet_ip_base(
+ int fd,
+ enum settings_ip ipsettings,
+ uint8_t tos,
+ uint16_t id,
+ uint16_t fo,
+ uint8_t ttl,
+ uint8_t prot,
+ uint32_t srcip,
+ uint32_t dstip,
+ const void *payload,
+ size_t payloadsize) {
+ uint8_t ver = (ipsettings & si_bad_version) ? 3 : 4;
+ uint8_t ihl, ihl_fuzzed;
+ uint16_t fl = ((ipsettings & si_flag_evil) ? IP_FLAG_EVIL : 0) |
+ ((ipsettings & si_flag_df) ? IP_FLAG_DF : 0) |
+ ((ipsettings & si_flag_mf) ? IP_FLAG_MF : 0);
+ uint16_t len;
+ int optlen;
+ struct header_ip header = {
+ .tos = tos,
+ .id = htons(id),
+ .fl_fo = htons((fl << 13) | fo),
+ .ttl = ttl,
+ .prot = prot,
+ .cs = 0,
+ .src = htonl(srcip),
+ .dst = htonl(dstip),
+ };
+ char packet[6536];
+ size_t packetsize;
+ ssize_t r;
+
+ dbgprintf("sending IP packet src=%d.%d.%d.%d dst=%d.%d.%d.%d "
+ "payloadsize=%zu id=0x%.4x fragoff=%d%s\n",
+ (uint8_t) (srcip >> 24), (uint8_t) (srcip >> 16),
+ (uint8_t) (srcip >> 8), (uint8_t) (srcip >> 0),
+ (uint8_t) (dstip >> 24), (uint8_t) (dstip >> 16),
+ (uint8_t) (dstip >> 8), (uint8_t) (dstip >> 0),
+ payloadsize, id, fo, (ipsettings & si_flag_mf) ? " (MF)" : "");
+
+ optlen = 0;
+ if (ipsettings & si_opt_badpad) memset(header.opt, -1, sizeof(header.opt));
+ if (ipsettings & si_opt_nop) header.opt[optlen++] = 0x01;
+ if (ipsettings & si_opt_topsec) {
+ header.opt[optlen++] = 0x82;
+ header.opt[optlen++] = 0x0b;
+ header.opt[optlen++] = 0x6b; /* S: top secret */
+ header.opt[optlen++] = 0xc5; /* S: top secret */
+ header.opt[optlen++] = 0x00; /* C */
+ header.opt[optlen++] = 0x00; /* C */
+ header.opt[optlen++] = 'A'; /* H */
+ header.opt[optlen++] = 'B'; /* H */
+ header.opt[optlen++] = 'C'; /* TCC */
+ header.opt[optlen++] = 'D'; /* TCC */
+ header.opt[optlen++] = 'E'; /* TCC */
+ }
+ if (ipsettings & si_opt_badopt) header.opt[optlen++] = 0xff;
+ if (ipsettings & si_opt_end) header.opt[optlen++] = 0x00;
+ assert(optlen <= sizeof(header.opt));
+
+ ihl = ihl_fuzzed = (20 + optlen + 3) / 4;
+ if (ipsettings & si_bad_ihl_small) ihl_fuzzed = 4;
+ if (ipsettings & si_bad_ihl_big) ihl_fuzzed = 15;
+ header.ver_ihl = (ver << 4) | ihl_fuzzed;
+
+ len = ihl * 4 + payloadsize;
+ if (ipsettings & si_bad_len_small) len = ihl * 4 - 1;
+ if (ipsettings & si_bad_len_big) len += 1;
+ if (ipsettings & si_bad_len_huge) len = 0xffff;
+ header.len = htons(len);
+
+ packetsize = ihl * 4 + payloadsize;
+ if (packetsize > sizeof(packet)) {
+ payloadsize = sizeof(packet) - ihl * 4;
+ packetsize = sizeof(packet);
+ }
+
+ header.cs = checksum_ip(&header, ihl * 4);
+ if (ipsettings & si_zero_cs) header.cs = 0;
+ if (ipsettings & si_bad_cs) header.cs += 1;
+
+ memset(packet, 0, sizeof(packet));
+ memcpy(packet, &header, ihl * 4);
+ memcpy(packet + ihl * 4, payload, payloadsize);
+
+ errno = 0;
+ r = send_packet_raw(fd, packet, packetsize);
+ if (r == -1 && errno == EPACKSIZE &&
+ (packetsize < 60 || packetsize > 1514)) {
+ return;
+ }
+ if (r != packetsize) {
+ efmt("write to network interface failed");
+ }
+}
+
+static void send_packet_ip(
+ int fd,
+ enum settings_ip ipsettings,
+ uint8_t tos,
+ uint16_t id,
+ uint8_t ttl,
+ uint8_t prot,
+ uint32_t srcip,
+ uint32_t dstip,
+ enum fragmode_ip fragmode,
+ const void *payload,
+ size_t payloadsize) {
+ enum settings_ip flags;
+ size_t fragcount = 1;
+ size_t fragsize, fragsizecur;
+ size_t fragstart = 0;
+ size_t fragstep;
+
+ switch (fragmode) {
+ case fi_as_needed:
+ fragsize = fragstep = 1500;
+ fragcount = (payloadsize + fragsize - 1) / fragsize;
+ break;
+ case fi_one:
+ case fi_fo_max:
+ fragsize = fragstep = payloadsize;
+ break;
+ case fi_two:
+ fragcount = 2;
+ fragsize = fragstep = (payloadsize + 1) / 2;
+ break;
+ case fi_frag_tiny:
+ fragcount = (payloadsize >= 100) ? 100 :
+ (payloadsize < 1) ? 1 : payloadsize;
+ fragsize = fragstep = (payloadsize + fragcount - 1) / fragcount;
+ break;
+ case fi_frag_overlap:
+ fragcount = 2;
+ fragsize = (payloadsize * 2 + 2) / 3;
+ fragstep = (payloadsize + 1) / 2;
+ break;
+ case fi_frag_first:
+ fragcount = 1;
+ fragsize = fragstep = (payloadsize + 1) / 2;
+ break;
+ case fi_frag_last:
+ fragcount = 1;
+ fragsize = fragstep = (payloadsize + 1) / 2;
+ break;
+ case fi_frag_repeat:
+ fragcount = 2;
+ fragsize = payloadsize;
+ fragstep = 0;
+ break;
+ }
+
+ while (fragcount > 0) {
+ if (fragstart >= payloadsize) {
+ fragsizecur = 0;
+ } else if (payloadsize - fragstart < fragsize) {
+ fragsizecur = payloadsize - fragstart;
+ } else {
+ fragsizecur = fragsize;
+ }
+
+ flags = 0;
+ if (fragstart + fragsizecur < payloadsize) flags |= si_flag_mf;
+ send_packet_ip_base(
+ fd,
+ ipsettings | flags,
+ tos,
+ id,
+ (fragmode == fi_fo_max) ? 0x1fff : fragstart,
+ ttl,
+ prot,
+ srcip,
+ dstip,
+ (uint8_t *) payload + fragstart,
+ fragsizecur);
+
+ fragcount--;
+ fragstart += fragstep;
+ }
+}
+
+static uint32_t checksum_udp_sum(const void *buf, size_t size) {
+ const uint16_t *p = buf;
+ uint32_t sum = 0;
+
+ while (size > 0) {
+ assert(size >= sizeof(*p));
+ sum += ntohs(*p);
+ size -= sizeof(*p);
+ p++;
+ }
+ return sum;
+}
+
+static uint16_t checksum_udp(
+ uint32_t srcip,
+ uint32_t dstip,
+ uint8_t prot,
+ const void *packet,
+ size_t packetsize) {
+ uint32_t sum = 0;
+ struct header_udp_pseudo header = {
+ .src = htonl(srcip),
+ .dst = htonl(dstip),
+ .zero = 0,
+ .prot = prot,
+ .len = htons(packetsize),
+ };
+
+ sum = checksum_udp_sum(&header, sizeof(header)) +
+ checksum_udp_sum(packet, packetsize + packetsize % 2);
+ sum += sum >> 16;
+ return ntohs(~sum);
+}
+
+static void send_packet_udp(
+ int fd,
+ enum settings_ip ipsettings,
+ uint8_t tos,
+ uint16_t id,
+ uint8_t ttl,
+ uint8_t prot,
+ uint32_t srcip,
+ uint32_t dstip,
+ enum fragmode_ip fragmode,
+ enum settings_udp udpsettings,
+ uint16_t srcport,
+ uint16_t dstport,
+ const void *payload,
+ size_t payloadsize) {
+ uint16_t len;
+ struct header_udp header = {
+ .src = htons(srcport),
+ .dst = htons(dstport),
+ .cs = 0,
+ };
+ char packet[65536];
+ size_t packetsize;
+
+ dbgprintf("sending UDP packet srcport=%d dstport=%d payloadsize=%zu\n",
+ srcport, dstport, payloadsize);
+
+ len = sizeof(struct header_udp) + payloadsize;
+ if (udpsettings & su_bad_len_small) len = sizeof(struct header_udp) - 1;
+ if (udpsettings & su_bad_len_big) len += 1;
+ if (udpsettings & su_bad_len_huge) len = 65535 - sizeof(struct header_ip);
+ header.len = htons(len);
+
+ packetsize = sizeof(header) + payloadsize;
+ assert(packetsize <= sizeof(packet));
+
+ memcpy(packet, &header, sizeof(header));
+ memcpy(packet + sizeof(header), payload, payloadsize);
+ if (packetsize % 2) packet[packetsize] = 0;
+
+ header.cs = checksum_udp(srcip, dstip, prot, packet, packetsize);
+ if (udpsettings & su_zero_cs) header.cs = 0;
+ if (udpsettings & su_bad_cs) header.cs += 1;
+
+ memcpy(packet, &header, sizeof(header));
+ send_packet_ip(
+ fd,
+ ipsettings,
+ tos,
+ id,
+ ttl,
+ prot,
+ srcip,
+ dstip,
+ fragmode,
+ packet,
+ packetsize);
+}
+
+struct send_packet_udp_simple_params {
+ int fd;
+ enum settings_ip ipsettings;
+ uint8_t tos;
+ uint16_t *id;
+ uint8_t ttl;
+ uint8_t prot;
+ uint32_t srcip;
+ uint32_t dstip;
+ enum fragmode_ip fragmode;
+ enum settings_udp udpsettings;
+ uint16_t srcport;
+ uint16_t dstport;
+ size_t payloadsize;
+};
+
+static void send_packet_udp_simple(
+ const struct send_packet_udp_simple_params *params) {
+ int i;
+ char payload[65536];
+
+ assert(params->payloadsize <= sizeof(payload));
+ for (i = 0; i < params->payloadsize; i++) {
+ payload[i] = *params->id + i;
+ }
+
+ send_packet_udp(
+ params->fd,
+ params->ipsettings,
+ params->tos,
+ *params->id,
+ params->ttl,
+ params->prot,
+ params->srcip,
+ params->dstip,
+ params->fragmode,
+ params->udpsettings,
+ params->srcport,
+ params->dstport,
+ payload,
+ params->payloadsize);
+ *params->id += 5471;
+}
+
+static void send_packets_ip_settings(
+ const struct send_packet_udp_simple_params *paramsbase) {
+ struct send_packet_udp_simple_params params;
+ int i;
+ enum settings_ip ipsettings[] = {
+ 0,
+ si_bad_version,
+ si_bad_ihl_small,
+ si_bad_ihl_big,
+ si_bad_len_small,
+ si_bad_len_big,
+ si_bad_len_huge,
+ si_bad_cs,
+ si_zero_cs,
+ si_flag_evil,
+ si_flag_df,
+ si_flag_mf,
+ si_opt_end,
+ si_opt_topsec,
+ si_opt_nop,
+ si_opt_badopt,
+ si_opt_nop | si_opt_end | si_opt_badpad,
+ };
+ uint8_t ttls[] = { 0, 1, 127, 128, 255 };
+
+ /* various types of flags/options/corruptions */
+ params = *paramsbase;
+ for (i = 0; i < 17; i++) {
+ params.ipsettings = ipsettings[i];
+ send_packet_udp_simple(¶ms);
+ }
+
+ /* various TTL settings */
+ params = *paramsbase;
+ for (i = 0; i < 5; i++) {
+ params.ttl = ttls[i];
+ send_packet_udp_simple(¶ms);
+ }
+}
+
+static void send_packets_ip(int fd) {
+ enum fragmode_ip fragmode;
+ int i, j;
+ uint16_t id = 0;
+ struct send_packet_udp_simple_params params;
+ const struct send_packet_udp_simple_params paramsbase = {
+ .fd = fd,
+ .ipsettings = 0,
+ .tos = 0,
+ .id = &id,
+ .ttl = 10,
+ .prot = IP_PROT_UDP,
+ .srcip = addrsrc,
+ .dstip = addrdst,
+ .fragmode = fi_as_needed,
+ .udpsettings = 0,
+ .srcport = PORT_BASE + 0,
+ .dstport = PORT_BASE + 1,
+ .payloadsize = 1234,
+ };
+
+ /* send packets with various payload sizes and corruptions */
+ params = paramsbase;
+ for (i = 0; i < PAYLOADSIZE_COUNT; i++) {
+ params.payloadsize = payloadsizes[i];
+ send_packets_ip_settings(¶ms);
+ }
+
+ /* send packets with various addresses and corruptions */
+ params = paramsbase;
+ for (i = 0; i < addr_count; i++) {
+ for (j = 0; j < addr_count; j++) {
+ params.srcip = addrs[i];
+ params.dstip = addrs[j];
+ send_packets_ip_settings(¶ms);
+ }
+ }
+
+ /* send valid packets with various fragmentation settings */
+ params = paramsbase;
+ for (i = 0; i < PAYLOADSIZE_COUNT; i++) {
+ for (fragmode = fi_as_needed; fragmode <= fi_fo_max; fragmode++) {
+ params.payloadsize = payloadsizes[i];
+ params.fragmode = fragmode;
+ send_packet_udp_simple(¶ms);
+ }
+ }
+
+ /* send a packet for each protocol */
+ params = paramsbase;
+ for (i = 0; i < 256; i++) {
+ params.prot = i;
+ send_packet_udp_simple(¶ms);
+ }
+
+ /* send a packet for each tos */
+ params = paramsbase;
+ for (i = 0; i < 256; i++) {
+ params.tos = i;
+ send_packet_udp_simple(¶ms);
+ }
+}
+
+static void send_packets_udp(int fd) {
+ int i, j, k;
+ uint16_t id = 0;
+ struct send_packet_udp_simple_params params;
+ const struct send_packet_udp_simple_params paramsbase = {
+ .fd = fd,
+ .ipsettings = 0,
+ .tos = 0,
+ .id = &id,
+ .ttl = 10,
+ .prot = IP_PROT_UDP,
+ .srcip = addrsrc,
+ .dstip = addrdst,
+ .fragmode = fi_as_needed,
+ .udpsettings = 0,
+ .srcport = PORT_BASE + 0,
+ .dstport = PORT_BASE + 1,
+ .payloadsize = 1234,
+ };
+ uint16_t ports[] = {
+ 0,
+ PORT_BASE + 0,
+ PORT_BASE + 1,
+ 32767,
+ 65535,
+ };
+ enum settings_udp udpsettings[] = {
+ 0,
+ su_bad_len_small,
+ su_bad_len_big,
+ su_bad_len_huge,
+ su_bad_cs,
+ su_zero_cs,
+ };
+
+ /* send packets with various corruptions */
+ params = paramsbase;
+ for (i = 0; i < 6; i++) {
+ params.udpsettings = udpsettings[i];
+ send_packet_udp_simple(¶ms);
+ }
+
+ /* send packets with various addresses and ports */
+ params = paramsbase;
+ for (i = 0; i < addr_count; i++) {
+ for (j = 0; j < addr_count; j++) {
+ for (k = 0; k < 5; k++) {
+ params.srcip = addrs[i];
+ params.dstip = addrs[j];
+ params.dstport = ports[k];
+ send_packet_udp_simple(¶ms);
+ }
+ }
+ }
+ params = paramsbase;
+ for (i = 0; i < addr_count; i++) {
+ for (j = 0; j < 5; j++) {
+ for (k = 0; k < 5; k++) {
+ params.dstip = addrs[i];
+ params.srcport = ports[j];
+ params.dstport = ports[k];
+ send_packet_udp_simple(¶ms);
+ }
+ }
+ }
+}
+
+enum settings_tcp {
+ st_bad_doff_small = (1 << 0),
+ st_bad_doff_big = (1 << 1),
+ st_bad_doff_huge = (1 << 2),
+ st_bad_cs = (1 << 3),
+ st_zero_cs = (1 << 4),
+ st_opt_end = (1 << 5),
+ st_opt_nop = (1 << 6),
+ st_opt_mss_small = (1 << 7),
+ st_opt_mss_big = (1 << 8),
+ st_opt_mss_huge = (1 << 9),
+ st_opt_badpad = (1 << 10),
+};
+
+static void send_packet_tcp(
+ int fd,
+ enum settings_ip ipsettings,
+ uint8_t tos,
+ uint16_t id,
+ uint8_t ttl,
+ uint8_t prot,
+ uint32_t srcip,
+ uint32_t dstip,
+ enum fragmode_ip fragmode,
+ enum settings_tcp tcpsettings,
+ uint16_t srcport,
+ uint16_t dstport,
+ uint32_t seq,
+ uint32_t ack,
+ uint8_t fl,
+ uint16_t win,
+ uint16_t uptr,
+ const void *payload,
+ size_t payloadsize) {
+ uint8_t doff, doff_fuzzed;
+ int optlen;
+ struct header_tcp header = {
+ .src = htons(srcport),
+ .dst = htons(dstport),
+ .seq = htonl(seq),
+ .ack = htonl(ack),
+ .fl = fl,
+ .win = htons(win),
+ .cs = 0,
+ .uptr = htons(uptr),
+ };
+ char packet[65536];
+ size_t packetsize;
+
+ dbgprintf("sending TCP packet srcport=%d dstport=%d fl=%s%s%s%s%s%s "
+ "payloadsize=%zu\n", srcport, dstport,
+ (fl & TCP_FLAG_URG) ? " URG" : "",
+ (fl & TCP_FLAG_ACK) ? " ACK" : "",
+ (fl & TCP_FLAG_PSH) ? " PSH" : "",
+ (fl & TCP_FLAG_RST) ? " RST" : "",
+ (fl & TCP_FLAG_SYN) ? " SYN" : "",
+ (fl & TCP_FLAG_FIN) ? " FIN" : "",
+ payloadsize);
+
+ optlen = 0;
+ if (tcpsettings & st_opt_badpad) memset(header.opt, -1, sizeof(header.opt));
+ if (tcpsettings & st_opt_nop) header.opt[optlen++] = 0x01;
+ if (tcpsettings & st_opt_mss_small) {
+ header.opt[optlen++] = 0x02;
+ header.opt[optlen++] = 0x04;
+ header.opt[optlen++] = 0x00;
+ header.opt[optlen++] = 0x00;
+ }
+ if (tcpsettings & st_opt_mss_big) {
+ header.opt[optlen++] = 0x02;
+ header.opt[optlen++] = 0x04;
+ header.opt[optlen++] = 0x10;
+ header.opt[optlen++] = 0x00;
+ }
+ if (tcpsettings & st_opt_mss_huge) {
+ header.opt[optlen++] = 0x02;
+ header.opt[optlen++] = 0x04;
+ header.opt[optlen++] = 0xff;
+ header.opt[optlen++] = 0xff;
+ }
+ if (tcpsettings & st_opt_end) header.opt[optlen++] = 0x00;
+
+ doff = doff_fuzzed = (20 + optlen + 3) / 4;
+ if (tcpsettings & su_bad_len_small) doff_fuzzed -= 1;
+ if (tcpsettings & su_bad_len_big) doff_fuzzed += 1;
+ if (tcpsettings & su_bad_len_huge) doff_fuzzed = 15;
+ header.doff = doff_fuzzed << 4;
+
+ packetsize = doff * 4 + payloadsize;
+ assert(packetsize <= sizeof(packet));
+
+ memcpy(packet, &header, sizeof(header));
+ memcpy(packet + sizeof(header), payload, payloadsize);
+ if (packetsize % 2) packet[packetsize] = 0;
+
+ header.cs = checksum_udp(srcip, dstip, prot, packet, packetsize);
+ if (tcpsettings & su_zero_cs) header.cs = 0;
+ if (tcpsettings & su_bad_cs) header.cs += 1;
+
+ memcpy(packet, &header, sizeof(header));
+ send_packet_ip(
+ fd,
+ ipsettings,
+ tos,
+ id,
+ ttl,
+ prot,
+ srcip,
+ dstip,
+ fragmode,
+ packet,
+ packetsize);
+}
+
+struct send_packet_tcp_simple_params {
+ int fd;
+ enum settings_ip ipsettings;
+ uint8_t tos;
+ uint16_t *id;
+ uint8_t ttl;
+ uint8_t prot;
+ uint32_t srcip;
+ uint32_t dstip;
+ enum fragmode_ip fragmode;
+ enum settings_tcp tcpsettings;
+ uint16_t srcport;
+ uint16_t dstport;
+ uint32_t seq;
+ uint32_t ack;
+ uint8_t fl;
+ uint16_t win;
+ uint16_t uptr;
+ size_t payloadsize;
+};
+
+static void send_packet_tcp_simple(
+ const struct send_packet_tcp_simple_params *params) {
+ int i;
+ char payload[65536];
+
+ if (!params->srcip || !params->dstip) return; /* crashes QEMU */
+
+ assert(params->payloadsize <= sizeof(payload));
+ for (i = 0; i < params->payloadsize; i++) {
+ payload[i] = *params->id + i;
+ }
+ send_packet_tcp(
+ params->fd,
+ params->ipsettings,
+ params->tos,
+ *params->id,
+ params->ttl,
+ params->prot,
+ params->srcip,
+ params->dstip,
+ params->fragmode,
+ params->tcpsettings,
+ params->srcport,
+ params->dstport,
+ params->seq,
+ params->ack,
+ params->fl,
+ params->win,
+ params->uptr,
+ payload,
+ params->payloadsize);
+ *params->id += 5471;
+}
+
+static void send_packets_tcp(int fd) {
+ int i, j, k;
+ uint16_t id = 0;
+ const struct send_packet_tcp_simple_params paramsbase = {
+ .fd = fd,
+ .ipsettings = 0,
+ .tos = 0,
+ .id = &id,
+ .ttl = 10,
+ .prot = IP_PROT_TCP,
+ .srcip = addrsrc,
+ .dstip = addrdst,
+ .fragmode = fi_as_needed,
+ .tcpsettings = 0,
+ .srcport = PORT_BASE + 0,
+ .dstport = PORT_BASE + 1,
+ .seq = 0x12345678,
+ .ack = 0x87654321,
+ .fl = TCP_FLAG_SYN,
+ .win = 4096,
+ .uptr = 0,
+ .payloadsize = 1234,
+ };
+ uint16_t payloadsizes[] = {
+ 0,
+ 1,
+ 999,
+ 1500,
+ 1600,
+ 9999,
+ };
+ uint16_t ports[] = {
+ 0,
+ PORT_BASE + 0,
+ PORT_BASE + 1,
+ PORT_BASE + 2,
+ PORT_BASE + 3,
+ 32767,
+ 65535,
+ };
+ enum settings_tcp tcpsettings[] = {
+ 0,
+ st_bad_doff_small,
+ st_bad_doff_big,
+ st_bad_doff_huge,
+ st_bad_cs,
+ st_zero_cs,
+ st_opt_end,
+ st_opt_nop,
+ st_opt_mss_small,
+ st_opt_mss_big,
+ st_opt_mss_huge,
+ st_opt_badpad,
+ };
+ struct send_packet_tcp_simple_params params;
+
+ /* send packets with various corruptions */
+ params = paramsbase;
+ for (i = 0; i < 12; i++) {
+ params.tcpsettings = tcpsettings[i];
+ send_packet_tcp_simple(¶ms);
+ }
+
+ /* send packets with various addresses and ports */
+ params = paramsbase;
+ for (i = 0; i < addr_count; i++) {
+ for (j = 0; j < addr_count; j++) {
+ for (k = 0; k < 7; k++) {
+ params.srcip = addrs[i];
+ params.dstip = addrs[j];
+ params.dstport = ports[k];
+ send_packet_tcp_simple(¶ms);
+ }
+ }
+ }
+ params = paramsbase;
+ for (i = 0; i < addr_count; i++) {
+ for (j = 0; j < 7; j++) {
+ for (k = 0; k < 7; k++) {
+ params.dstip = addrs[i];
+ params.srcport = ports[j];
+ params.dstport = ports[k];
+ send_packet_tcp_simple(¶ms);
+ }
+ }
+ }
+
+ /* send packets with different sequence numbers */
+ params = paramsbase;
+ for (i = 0; i < 16; i++) {
+ params.seq = 0x1fffffff;
+ send_packet_tcp_simple(¶ms);
+ }
+
+ /* send packets with all combinations of flags */
+ params = paramsbase;
+ for (i = 0; i < 256; i++) {
+ params.fl = i;
+ send_packet_tcp_simple(¶ms);
+ }
+
+ /* send packets with different window sizes */
+ params = paramsbase;
+ for (i = 0; i < 6; i++) {
+ params.win = payloadsizes[i];
+ send_packet_tcp_simple(¶ms);
+ }
+
+ /* send packets with different payload sizes */
+ params = paramsbase;
+ for (i = 0; i < 6; i++) {
+ params.payloadsize = payloadsizes[i];
+ send_packet_tcp_simple(¶ms);
+ }
+}
+
+static void recv_packets_nb(int fd) {
+ char buf[4096];
+ int flags;
+ ssize_t r;
+
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0) {
+ efmt("fcntl(F_GETFL) failed");
+ return;
+ }
+
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
+ efmt("fcntl(F_SETFL) failed");
+ return;
+ }
+
+ for (;;) {
+ errno = 0;
+ r = read(fd, buf, sizeof(buf));
+ if (r <= 0) {
+ if (errno != EAGAIN) efmt("nb read failed");
+ dbgprintf("no more packets to receive\n");
+ break;
+ }
+ dbgprintf("received packet of size %zd\n", r);
+ }
+
+ if (fcntl(fd, F_SETFL, flags) == -1) {
+ efmt("fcntl(F_SETFL) failed");
+ return;
+ }
+}
+
+static struct timeval gettimeofday_checked(void) {
+ struct timeval time = {};
+
+ if (gettimeofday(&time, NULL) != 0) {
+ efmt("gettimeofday failed");
+ }
+ return time;
+}
+
+static int timeval_cmp(const struct timeval *x, const struct timeval *y) {
+ if (x->tv_sec < y->tv_sec) return -1;
+ if (x->tv_sec > y->tv_sec) return 1;
+ if (x->tv_usec < y->tv_usec) return -1;
+ if (x->tv_usec > y->tv_usec) return 1;
+ return 0;
+}
+
+static struct timeval timeval_sub(struct timeval x, struct timeval y) {
+ struct timeval z;
+
+ /* no negative result allowed */
+ if (timeval_cmp(&x, &y) < 0) {
+ memset(&z, 0, sizeof(z));
+ } else {
+ /* no negative tv_usec allowed */
+ if (x.tv_usec < y.tv_usec) {
+ x.tv_sec -= 1;
+ x.tv_usec += 1000000;
+ }
+
+ /* perform subtraction */
+ z.tv_sec = x.tv_sec - y.tv_sec;
+ z.tv_usec = x.tv_usec - y.tv_usec;
+ }
+ return z;
+}
+
+static size_t recv_packet_select(
+ int fd,
+ void *buf,
+ size_t size,
+ const struct timeval *deadline) {
+ int nfds;
+ ssize_t r;
+ fd_set readfds;
+ struct timeval timeout = timeval_sub(*deadline, gettimeofday_checked());
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+ errno = 0;
+ nfds = select(fd + 1, &readfds, NULL, NULL, &timeout);
+ if (nfds < 0 || nfds > 1) {
+ efmt("select failed");
+ return 0;
+ }
+
+ if (nfds == 0) {
+ if (FD_ISSET(fd, &readfds)) efmt("select spuriously set fd");
+ dbgprintf("no more packets to receive\n");
+ return 0;
+ }
+
+ if (!FD_ISSET(fd, &readfds)) {
+ efmt("select did not set fd");
+ return 0;
+ }
+
+ r = read(fd, buf, size);
+ if (r <= 0) {
+ efmt("read failed");
+ return 0;
+ }
+ dbgprintf("received packet of size %zd\n", r);
+
+ return r;
+}
+
+static void recv_packets_select(int fd) {
+ char buf[4096];
+ struct timeval deadline = gettimeofday_checked();
+
+ deadline.tv_sec++;
+ while (recv_packet_select(fd, buf, sizeof(buf), &deadline)) { }
+}
+
+static int open_raw_socket(int broadcast) {
+ int fd;
+ struct nwio_ethopt opt = { };
+ struct nwio_ethstat stat = { };
+
+ fd = open("/dev/eth", O_RDWR);
+ if (fd < 0) efmt("cannot open /dev/eth");
+
+ /* test NWIOGETHOPT */
+ if (ioctl(fd, NWIOGETHOPT, &opt) != 0) {
+ efmt("ioctl(NWIOGETHOPT) failed");
+ }
+
+ /* test NWIOGETHSTAT */
+ if (ioctl(fd, NWIOGETHSTAT, &stat) != 0) {
+ efmt("ioctl(NWIOGETHSTAT) failed");
+ }
+
+ /* test invalid NWIOSETHOPT input */
+ opt.nweo_flags = NWEO_COPY << 16;
+ if (ioctl(fd, NWIOSETHOPT, &opt) != -1 && errno != EBADMODE) {
+ efmt("ioctl(NWIOSETHOPT) should have returned EBADMODE");
+ }
+
+ opt.nweo_flags = NWEO_EN_LOC | NWEO_DI_LOC;
+ if (ioctl(fd, NWIOSETHOPT, &opt) != -1 && errno != EBADMODE) {
+ efmt("ioctl(NWIOSETHOPT) should have returned EBADMODE");
+ }
+
+ /* test NWIOSETHOPT with defaults */
+ opt.nweo_flags = 0;
+ if (ioctl(fd, NWIOSETHOPT, &opt) != 0) {
+ efmt("ioctl(NWIOSETHOPT) failed");
+ }
+
+ /* test NWIOGETHSTAT right after reconfiguring */
+ opt.nweo_flags = NWEO_EN_BROAD | NWEO_EN_MULTI | NWEO_EN_PROMISC;
+ if (ioctl(fd, NWIOSETHOPT, &opt) != 0) {
+ efmt("ioctl(NWIOSETHOPT) failed");
+ }
+
+ opt.nweo_flags = NWEO_DI_BROAD | NWEO_DI_MULTI | NWEO_DI_PROMISC;
+ if (ioctl(fd, NWIOSETHOPT, &opt) != 0) {
+ efmt("ioctl(NWIOSETHOPT) failed");
+ }
+
+ if (ioctl(fd, NWIOGETHSTAT, &stat) != 0) {
+ efmt("ioctl(NWIOGETHSTAT) failed");
+ }
+
+ /* configure /dev/eth the way we want it for the rest of the test */
+ opt.nweo_flags = NWEO_COPY | NWEO_EN_LOC | NWEO_EN_BROAD |
+ NWEO_EN_MULTI | NWEO_EN_PROMISC |
+ (broadcast ? NWEO_REMANY : NWEO_REMSPEC) |
+ NWEO_TYPESPEC | NWEO_RWDATONLY;
+ opt.nweo_type = htons(ETH_IP_PROTO);
+ memcpy(&opt.nweo_rem, &stat.nwes_addr, sizeof(opt.nweo_rem));
+ if (ioctl(fd, NWIOSETHOPT, &opt) != 0) {
+ efmt("ioctl(NWIOSETHOPT) failed");
+ }
+
+ return fd;
+}
+
+static void do_packets(void) {
+ int fd;
+
+ /* test IP and UDP with broadcast */
+ fd = open_raw_socket(1 /*broadcast*/);
+ if (fd < 0) return;
+
+ send_packets_ip(fd);
+ send_packets_udp(fd);
+ recv_packets_nb(fd);
+
+ CLOSE(fd);
+
+ /* test TCP locally to avoid crashing QEMU */
+ fd = open_raw_socket(0 /*broadcast*/);
+ if (fd < 0) return;
+
+ send_packets_tcp(fd);
+ recv_packets_select(fd);
+
+ CLOSE(fd);
+}
+
+static void add_local_ip(uint32_t ip) {
+ static int first = 1;
+ int i;
+
+ for (i = 0; i < addr_count; i++) {
+ if (addrs[i] == ip) return;
+ }
+ dbgprintf("found local IP: %d.%d.%d.%d\n",
+ (uint8_t) (ip >> 24), (uint8_t) (ip >> 16),
+ (uint8_t) (ip >> 8), (uint8_t) (ip >> 0));
+ if (addr_count < ADDR_COUNT_MAX) {
+ addrs[addr_count++] = ip;
+ }
+ if (first) {
+ addrdst = ip;
+ first = 0;
+ }
+}
+
+static void get_local_ip(void) {
+ char device[16];
+ int flags;
+ int ifno;
+ nwio_ipconf_t ipconf;
+ int ip_fd;
+
+ /* inspired by ifconfig */
+ for (ifno = 0; ifno < 32; ifno++) {
+ snprintf(device, sizeof(device), "/dev/ip%d", ifno);
+ ip_fd = open(device, O_RDWR);
+ if (ip_fd < 0) {
+ if (errno != ENOENT && errno != ENXIO) {
+ efmt("cannot open %s", device);
+ }
+ continue;
+ }
+
+ flags = fcntl(ip_fd, F_GETFL);
+ if (flags == -1) {
+ efmt("cannot get flags for %s", device);
+ goto next;
+ }
+ if (fcntl(ip_fd, F_SETFL, flags | O_NONBLOCK) == -1) {
+ efmt("cannot set flags for %s", device);
+ goto next;
+ }
+
+ if (ioctl(ip_fd, NWIOGIPCONF, &ipconf) == -1) {
+ if (errno != EAGAIN) {
+ efmt("cannot get IP address for %s", device);
+ }
+ goto next;
+ }
+
+ if (fcntl(ip_fd, F_SETFL, flags) == -1) {
+ efmt("cannot restore flags for %s", device);
+ }
+
+ add_local_ip(ntohl(ipconf.nwic_ipaddr));
+
+next:
+ CLOSE(ip_fd);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int i;
+ pid_t pids[PORT_COUNT];
+
+ start(83);
+
+ /* start servers so we have someone to talk to */
+ pids[0] = server_start(SOCK_STREAM, PORT_BASE + 0, sa_close);
+ pids[1] = server_start(SOCK_STREAM, PORT_BASE + 1, sa_read);
+ pids[2] = server_start(SOCK_STREAM, PORT_BASE + 2, sa_selectrw);
+ pids[3] = server_start(SOCK_STREAM, PORT_BASE + 3, sa_write);
+ pids[4] = server_start(SOCK_DGRAM, PORT_BASE + 0, sa_read);
+ pids[5] = server_start(SOCK_DGRAM, PORT_BASE + 1, sa_selectr);
+
+ /* send some bogus packets */
+ get_local_ip();
+ if (get_setting_use_network()) do_packets();
+
+ /* stop the servers */
+ for (i = 0; i < PORT_COUNT; i++) server_stop(pids[i]);
+ for (i = 0; i < PORT_COUNT; i++) server_wait(pids[i]);
+
+ quit();
+ return 0;
+}