Rewrite regress sendsrcaddr so that is uses UDP sockets on localhost,
authorbluhm <bluhm@openbsd.org>
Tue, 11 Jul 2017 00:25:19 +0000 (00:25 +0000)
committerbluhm <bluhm@openbsd.org>
Tue, 11 Jul 2017 00:25:19 +0000 (00:25 +0000)
does not try to connect to google and avoids BPF programming.

regress/sys/netinet/sendsrcaddr/Makefile
regress/sys/netinet/sendsrcaddr/runtest.c

index f5a8cf5..600df5e 100644 (file)
@@ -1,30 +1,65 @@
-# $OpenBSD: Makefile,v 1.1 2016/08/16 22:25:08 vgross Exp $
-
-PROG      = runtest
-DESTADDR   = www.google.com
-TESTIFACE  = vether12
-TESTNET   != jot -r -s '.' 2 0 255
-RESV_ADDR  = 10.${TESTNET}.1
-BIND_ADDR  = 10.${TESTNET}.2
-CMSG_ADDR  = 10.${TESTNET}.3
-NONE_ADDR  = 10.${TESTNET}.4
-
-run-regress-runtest: ${PROG}
+# $OpenBSD: Makefile,v 1.2 2017/07/11 00:25:19 bluhm Exp $
+
+PROG =         runtest
+CFLAGS =       -Wall
+DESTADDR =     127.0.0.1
+TESTIFACE =    vether12
+TESTNET !!=    jot -s '.' 2 0 255
+RESV_ADDR =    10.${TESTNET}.1
+BIND_ADDR =    10.${TESTNET}.2
+CMSG_ADDR =    10.${TESTNET}.3
+NONE_ADDR =    10.${TESTNET}.4
+CLEANFILES =   stamp-*
+
+REGRESS_TARGETS =      run-regress-1 \
+                       run-regress-2 \
+                       run-regress-3 \
+                       run-regress-4 \
+                       run-regress-5 \
+                       run-regress-6 \
+                       run-regress-7 \
+                       run-regress-8 \
+                       run-regress-9 \
+                       run-regress-cleanup
+
+stamp-setup:
+       -! ${SUDO} ifconfig ${TESTIFACE} destroy
        ${SUDO} ifconfig ${TESTIFACE} create
        ${SUDO} ifconfig ${TESTIFACE} inet ${RESV_ADDR}/24 up
        ${SUDO} ifconfig ${TESTIFACE} inet ${BIND_ADDR}/24 alias
        ${SUDO} ifconfig ${TESTIFACE} inet ${CMSG_ADDR}/24 alias
-       sleep 1
        ${SUDO} ifconfig ${TESTIFACE}
-       ${.OBJDIR}/${PROG}  0 ${DESTADDR}=destination ${RESV_ADDR}=reserved_saddr ${BIND_ADDR}=bind_saddr
-       ${.OBJDIR}/${PROG}  0 ${DESTADDR}=destination ${RESV_ADDR}=reserved_saddr ${BIND_ADDR}=bind_saddr ${CMSG_ADDR}=cmsg_saddr,wire_saddr
-       ${.OBJDIR}/${PROG}  0 ${DESTADDR}=destination ${RESV_ADDR}=reserved_saddr ${BIND_ADDR}=bind_saddr ${BIND_ADDR}=cmsg_saddr,wire_saddr
-       ${.OBJDIR}/${PROG} 49 ${DESTADDR}=destination ${RESV_ADDR}=reserved_saddr ${BIND_ADDR}=bind_saddr ${NONE_ADDR}=cmsg_saddr
-       ${.OBJDIR}/${PROG} 48 ${DESTADDR}=destination ${RESV_ADDR}=reserved_saddr ${BIND_ADDR}=bind_saddr ${RESV_ADDR}=cmsg_saddr
-       ${.OBJDIR}/${PROG}  0 ${DESTADDR}=destination ${RESV_ADDR}=reserved_saddr 0.0.0.0=bind_saddr ${BIND_ADDR}=cmsg_saddr,wire_saddr
-       ${.OBJDIR}/${PROG}  0 ${DESTADDR}=destination ${RESV_ADDR}=reserved_saddr 0.0.0.0=bind_saddr ${RESV_ADDR}=cmsg_saddr,wire_saddr
-       ${.OBJDIR}/${PROG}  0 ${DESTADDR}=destination ${RESV_ADDR}=reserved_saddr ${BIND_ADDR}=bind_saddr 0.0.0.0=cmsg_saddr
-       ${.OBJDIR}/${PROG} 22 ${DESTADDR}=destination ${RESV_ADDR}=reserved_saddr ${BIND_ADDR}=bind_saddr ${CMSG_ADDR}=cmsg_saddr fuzz
-       ${SUDO} ifconfig ${TESTIFACE} destroy
+       date >$@
+
+run-regress-1: ${PROG} stamp-setup
+       ./${PROG} -D ${DESTADDR} -R ${RESV_ADDR} -B ${BIND_ADDR} -W ${BIND_ADDR}
+
+run-regress-2: ${PROG} stamp-setup
+       ./${PROG} -D ${DESTADDR} -R ${RESV_ADDR} -B ${BIND_ADDR} -C ${CMSG_ADDR} -W ${CMSG_ADDR}
+
+run-regress-3: ${PROG} stamp-setup
+       ./${PROG} -D ${DESTADDR} -R ${RESV_ADDR} -B ${BIND_ADDR} -C ${BIND_ADDR} -W ${BIND_ADDR}
+
+run-regress-4: ${PROG} stamp-setup
+       ./${PROG} -D ${DESTADDR} -R ${RESV_ADDR} -B ${BIND_ADDR} -C ${NONE_ADDR} -E 49
+
+run-regress-5: ${PROG} stamp-setup
+       ./${PROG} -D ${DESTADDR} -R ${RESV_ADDR} -B ${BIND_ADDR} -C ${RESV_ADDR} -E 48
+
+run-regress-6: ${PROG} stamp-setup
+       ./${PROG} -D ${DESTADDR} -R ${RESV_ADDR} -B 0.0.0.0 -C ${BIND_ADDR} -W ${BIND_ADDR}
+
+run-regress-7: ${PROG} stamp-setup
+       ./${PROG} -D ${DESTADDR} -R ${RESV_ADDR} -B 0.0.0.0 -C ${RESV_ADDR} -W ${RESV_ADDR}
+
+run-regress-8: ${PROG} stamp-setup
+       ./${PROG} -D ${DESTADDR} -R ${RESV_ADDR} -B ${BIND_ADDR} -C 0.0.0.0 -W ${BIND_ADDR}
+
+run-regress-9: ${PROG} stamp-setup
+       ./${PROG} -D ${DESTADDR} -R ${RESV_ADDR} -B ${BIND_ADDR} -C ${CMSG_ADDR} -f -E 22
+
+run-regress-cleanup:
+       rm -f stamp-setup
+       -${SUDO} ifconfig ${TESTIFACE} destroy
 
 .include <bsd.regress.mk>
index 8329d96..96667a5 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2016 Vincent Gross <vincent.gross@kilob.yt>
+ * Copyright (c) 2017 Alexander Bluhm <bluhm@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
 #include <err.h>
 #include <errno.h>
-#include <fcntl.h>
 #include <getopt.h>
 #include <netdb.h>
 #include <poll.h>
-#include <signal.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <unistd.h>
 
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include <net/bpf.h>
-#include <net/if.h>
-
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/if_ether.h>
-
-#include <arpa/inet.h>
-
-#define PORTNUM "23000"
 #define PAYLOAD "payload"
 
-char cmd_tmpl[] = "route get %s | awk '/interface:/ { printf($2) }'";
-#define CMD_TMPL_SZ sizeof(cmd_tmpl)
-
 int fuzzit;
 
-void check_packet_tx(int);
+void __dead usage(const char *);
+int udp_bind(struct sockaddr_in *);
+int udp_send(int, struct sockaddr_in *, struct sockaddr_in *);
+struct sockaddr_in * udp_recv(int s, struct sockaddr_in *);
+
+void __dead
+usage(const char *msg)
+{
+       if (msg != NULL)
+               fprintf(stderr, "%s\n", msg);
+       fprintf(stderr, "runtest [-f] -D destination -B bind [-C cmesg] "
+           "[-E error] -R reserved -W wire\n");
+       exit(1);
+}
 
 int
 main(int argc, char *argv[])
 {
-       int                       i;
-       char                     *argp, *addr, *flag;
-
-       struct addrinfo           hints;
-       struct addrinfo          *inai;
-
-       struct sockaddr_in       *dst_sin = NULL;
-       struct sockaddr_in       *reserved_sin = NULL;
-       struct sockaddr_in       *bind_sin = NULL;
-       struct sockaddr_in       *cmsg_sin = NULL;
-       struct sockaddr_in       *wire_sin = NULL;
-
-       int                       ch, rc, wstat, expected = -1;
-       int                       first_sock;
-       pid_t                     pid;
-
-       const char               *numerr;
-       char                      adrbuf[40];
-       const char               *adrp;
-
-       char                     *dst_str = NULL;
-       char                      cmd[CMD_TMPL_SZ + INET_ADDRSTRLEN];
-       FILE                     *outif_pipe;
-       char                      ifname_buf[IF_NAMESIZE];
-       size_t                    ifname_len;
-
-       int                       bpf_fd;
-
+       int ch, error, errexpect, bind_sock, dest_sock, resv_sock;
+       char addr[16];
+       const char *errstr;
+       struct addrinfo hints, *res;
+       struct sockaddr_in *bind_sin, *cmsg_sin, *dest_sin, *resv_sin,
+           *wire_sin, *from_sin;
 
        bzero(&hints, sizeof(hints));
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_DGRAM;
 
-       expected = strtonum(argv[1], 0, 255, &numerr);
-       if (numerr != NULL)
-               errx(2, "strtonum(%s): %s", optarg, numerr);
-
-       for (i = 2; i < argc; i++) {
-               argp = argv[i];
-               if (strcmp("fuzz",argp) == 0) {
+       bind_sin = cmsg_sin = dest_sin = resv_sin = wire_sin = NULL;
+       errexpect = 0;
+
+       while ((ch = getopt(argc, argv, "B:C:D:E:fR:W:")) != -1) {
+               switch (ch) {
+               case 'B':
+                       error = getaddrinfo(optarg, NULL, &hints, &res);
+                       if (error)
+                               errx(1, "-B: %s", gai_strerror(error));
+                       bind_sin = (struct sockaddr_in *)res->ai_addr;
+                       break;
+               case 'C':
+                       error = getaddrinfo(optarg, NULL, &hints, &res);
+                       if (error)
+                               errx(1, "-C: %s", gai_strerror(error));
+                       cmsg_sin = (struct sockaddr_in *)res->ai_addr;
+                       break;
+               case 'D':
+                       error = getaddrinfo(optarg, NULL, &hints, &res);
+                       if (error)
+                               errx(1, "-D: %s", gai_strerror(error));
+                       dest_sin = (struct sockaddr_in *)res->ai_addr;
+                       break;
+               case 'E':
+                       errexpect = strtonum(optarg, 1, 255, &errstr);
+                       if (errstr != NULL)
+                               errx(1, "error number is %s: %s",
+                                   errstr, optarg);
+                       break;
+               case 'f':
                        fuzzit = 1;
-                       continue;
-               }
-               addr = strsep(&argp, "=");
-               rc = getaddrinfo(addr, PORTNUM, &hints, &inai);
-               if (rc)
-                       errx(2, "getaddrinfo(%s) = %d: %s",
-                           argv[0], rc, gai_strerror(rc));
-               if (argp == NULL)
-                       errx(2, "arg must be of form <addr>=<flag>,<flag>");
-
-               for (; (flag = strsep(&argp,",")) != NULL;) {
-                       if (strcmp("destination",flag) == 0 && dst_sin == NULL) {
-                               dst_sin = (struct sockaddr_in *)inai->ai_addr;
-                               /* get output interface */
-                               snprintf(cmd, sizeof(cmd), cmd_tmpl, addr);
-                               outif_pipe = popen(cmd, "re");
-                               if (outif_pipe == NULL)
-                                       err(2, "popen(route get)");
-                               if (fgets(ifname_buf, IF_NAMESIZE, outif_pipe) == NULL)
-                                       err(2, "fgets()");
-                               pclose(outif_pipe);
-                               if (strlen(ifname_buf) == 0)
-                                       err(2, "strlen(ifname_buf) == 0");
-                       }
-                       if (strcmp("reserved_saddr",flag) == 0 && reserved_sin == NULL)
-                               reserved_sin = (struct sockaddr_in *)inai->ai_addr;
-                       if (strcmp("bind_saddr",flag) == 0 && bind_sin == NULL)
-                               bind_sin = (struct sockaddr_in *)inai->ai_addr;
-                       if (strcmp("cmsg_saddr",flag) == 0 && cmsg_sin == NULL)
-                               cmsg_sin = (struct sockaddr_in *)inai->ai_addr;
-                       if (strcmp("wire_saddr",flag) == 0 && wire_sin == NULL)
-                               wire_sin = (struct sockaddr_in *)inai->ai_addr;
+                       break;
+               case 'R':
+                       error = getaddrinfo(optarg, NULL, &hints, &res);
+                       if (error)
+                               errx(1, "-R: %s", gai_strerror(error));
+                       resv_sin = (struct sockaddr_in *)res->ai_addr;
+                       break;
+               case 'W':
+                       error = getaddrinfo(optarg, NULL, &hints, &res);
+                       if (error)
+                               errx(1, "-W: %s", gai_strerror(error));
+                       wire_sin = (struct sockaddr_in *)res->ai_addr;
+                       break;
+               default:
+                       usage(NULL);
                }
        }
+       argc -= optind;
+       argv += optind;
 
-       if (reserved_sin == NULL)
-               errx(2, "reserved_sin == NULL");
+       if (argc > 0)
+               usage("too many arguments");
 
        if (bind_sin == NULL)
-               errx(2, "bind_sin == NULL");
+               usage("no bind addr");
 
-       if (dst_sin == NULL)
-               errx(2, "dst_sin == NULL");
+       if (dest_sin == NULL)
+               usage("no destination addr");
 
-       if (expected < 0)
-               errx(2, "need expected");
+       if (resv_sin == NULL)
+               usage("no reserved addr");
 
+       /* bind on address that cannot be used */
+       resv_sock = udp_bind(resv_sin);
 
-       if (wire_sin != NULL)
-               bpf_fd = setup_bpf(ifname_buf, wire_sin, dst_sin);
+       /* bind socket that should receive the packet */
+       dest_sock = udp_bind(dest_sin);
 
-       first_sock = udp_first(reserved_sin);
+       /* bind socket that is used to send the packet */
+       bind_sin->sin_port = resv_sin->sin_port;
+       bind_sock = udp_bind(bind_sin);
+       error = udp_send(bind_sock, cmsg_sin, dest_sin);
 
-       pid = fork();
-       if (pid == 0) {
-               return udp_override(dst_sin, bind_sin, cmsg_sin);
+       if (errexpect && !error) {
+               errno = errexpect;
+               err(2, "udp send succeeded, but expected error");
+       }
+       if (!errexpect && error) {
+               errno = error;
+               err(2, "no error expected, but udp send failed");
+       }
+       if (errexpect != error) {
+               errno = error;
+               err(2, "expected error %d, but udp send failed", errexpect);
        }
-       (void)wait(&wstat);
-       close(first_sock);
-
-       if (!WIFEXITED(wstat))
-               errx(2, "error setting up override");
-
-       if (WEXITSTATUS(wstat) != expected)
-               errx(2, "expected %d, got %d", expected, WEXITSTATUS(wstat));
-
-       if (wire_sin != NULL)
-               check_packet_tx(bpf_fd);
-
-       return EXIT_SUCCESS;
-}
-
-
-struct bpf_insn outgoing_bpf_filter[] = {
-       /* ethertype = IP */
-       BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
-       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 9),
-
-       /* Make sure it's a UDP packet. */
-       BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
-       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 7),
-
-       /* Fragments are handled as errors */
-       BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
-       BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 5, 0),
-
-       /* Make sure it's from the right address */
-       BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 26),
-       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0, 0, 3), /* Need to patch this */
-
-       /* Make sure it's to the right address */
-       BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 30),
-       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0, 0, 1), /* Need to patch this */
-#if 0
-       /* Get the IP header length. */
-       BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
-
-       /* Make sure it's to the right port. */
-       BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
-       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0, 0, 1),
-#endif
-       /* If we passed all the tests, ask for the whole packet. */
-       BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
-
-       /* Otherwise, drop it. */
-       BPF_STMT(BPF_RET+BPF_K, 0),
-};
-
-int outgoing_bpf_filter_len = sizeof(outgoing_bpf_filter)/sizeof(struct bpf_insn);
 
-int
-setup_bpf(char *ifname, struct sockaddr_in *from, struct sockaddr_in *to)
-{
-       int fd;
-       struct ifreq ifr;
-       u_int flag;
-       struct bpf_version vers;
-       struct bpf_program prog;
-
-       fd = open("/dev/bpf", O_RDWR | O_CLOEXEC);
-       if (fd == -1)
-               err(2, "open(/dev/bpf)");
-
-       if (ioctl(fd, BIOCVERSION, &vers) < 0)
-               err(2, "ioctl(BIOCVERSION)");
-
-       if (vers.bv_major != BPF_MAJOR_VERSION ||
-           vers.bv_minor < BPF_MINOR_VERSION)
-               errx(2, "bpf version mismatch, expected %d.%d, got %d.%d",
-                   BPF_MAJOR_VERSION, BPF_MINOR_VERSION,
-                   vers.bv_major, vers.bv_minor);
-
-       strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-       if (ioctl(fd, BIOCSETIF, &ifr) < 0)
-               err(2, "ioctl(BIOCSETIF)");
-
-       flag = 1;
-       if (ioctl(fd, BIOCIMMEDIATE, &flag) < 0)
-               err(2, "ioctl(BIOCIMMEDIATE)");
-
-       flag = BPF_DIRECTION_IN;
-       if (ioctl(fd, BIOCSDIRFILT, &flag) < 0)
-               err(2, "ioctl(BIOCDIRFILT)");
-
-       outgoing_bpf_filter[7].k = ntohl(from->sin_addr.s_addr) ;
-       outgoing_bpf_filter[9].k = ntohl(to->sin_addr.s_addr) ;
-#if 0
-       outgoing_bpf_filter[12].k = (u_int32_t)ntohs(to->sin_port) ;
-#endif
-
-       prog.bf_len = outgoing_bpf_filter_len;
-       prog.bf_insns = outgoing_bpf_filter;
-       if (ioctl(fd, BIOCSETF, &prog) < 0)
-               err(2, "ioctl(BIOCSETF)");
-
-       return fd;
-}
+       if (wire_sin != NULL) {
+               from_sin = udp_recv(dest_sock, dest_sin);
+               if (from_sin == NULL)
+                       errx(2, "receive timeout");
+               inet_ntop(from_sin->sin_family, &from_sin->sin_addr,
+                   addr, sizeof(addr));
+               if (from_sin->sin_addr.s_addr != wire_sin->sin_addr.s_addr)
+                       errx(2, "receive addr %s", addr);
+               if (from_sin->sin_port != bind_sin->sin_port)
+                       errx(2, "receive port %d", ntohs(from_sin->sin_port));
+       }
 
-void
-check_packet_tx(int fd)
-{
-       u_int            buf_max;
-       size_t           len;
-       struct pollfd    pfd;
-       int              pollrc;
-       char            *buf, *payload;
-       struct bpf_hdr  *hdr;
-       struct ip       *ip;
-
-       if (ioctl(fd, BIOCGBLEN, &buf_max) < 0)
-               err(2, "ioctl(BIOCGBLEN)");
-
-       if (buf_max <= 0)
-               errx(2, "buf_max = %d <= 0", buf_max);
-
-       buf = malloc(buf_max);
-       if (!buf)
-               err(2, "malloc(buf_max)");
-
-       pfd.fd = fd;
-       pfd.events = POLLIN;
-       pollrc = poll(&pfd, 1, 5000);
-       if (pollrc == -1)
-               err(2, "poll()");
-       if (pollrc == 0)
-               errx(2, "poll() timeout");
-
-       len = read(fd, buf, buf_max);
-       if (len <= 0)
-               err(2, "read(/dev/bpf)");
-       len = BPF_WORDALIGN(len);
-
-       if (len < sizeof(hdr))
-               errx(2, "short read, len < sizeof(bpf_hdr)");
-
-       hdr = (struct bpf_hdr *)buf;
-       if (hdr->bh_hdrlen + hdr->bh_caplen > len)
-               errx(2, "buffer too small for the whole capture");
-
-       /* XXX we could try again if enough space in the buffer */
-       if (hdr->bh_caplen != hdr->bh_datalen)
-               errx(2, "partial capture");
-
-       ip = (struct ip *)(buf + hdr->bh_hdrlen + ETHER_HDR_LEN);
-       payload = ((char *)ip + ip->ip_hl*4 + 8);
-
-       if (strcmp(PAYLOAD,payload) != 0)
-               errx(2, "payload corrupted");
-
-       return;
+       return 0;
 }
 
 int
-udp_first(struct sockaddr_in *src)
+udp_bind(struct sockaddr_in *src)
 {
-       int s_con;
-
-       s_con = socket(src->sin_family, SOCK_DGRAM, 0);
-       if (s_con == -1)
-               err(2, "udp_bind: socket()");
-
-       if (bind(s_con, (struct sockaddr *)src, src->sin_len))
-               err(2, "udp_bind: bind()");
-
-       return s_con;
+       int s, reuse, salen;
+       char addr[16];
+
+       inet_ntop(src->sin_family, &src->sin_addr, addr, sizeof(addr));
+
+       if ((s = socket(src->sin_family, SOCK_DGRAM, 0)) == -1)
+               err(1, "socket %s", addr);
+       reuse = ntohl(src->sin_addr.s_addr) == INADDR_ANY ? 1 : 0;
+       if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))
+           == -1)
+               err(1, "setsockopt %s", addr);
+       if (bind(s, (struct sockaddr *)src, src->sin_len) == -1)
+               err(1, "bind %s", addr);
+       /* fill out port */
+       salen = sizeof(*src);
+       if (getsockname(s, (struct sockaddr *)src, &salen))
+               err(1, "getsockname %s", addr);
+
+       return s;
 }
 
-
 int
-udp_override(struct sockaddr_in *dst, struct sockaddr_in *src_bind,
-    struct sockaddr_in *src_sendmsg)
+udp_send(int s, struct sockaddr_in *src, struct sockaddr_in *dst)
 {
-       int                      s, optval, error, saved_errno;
-       ssize_t                  send_rc;
        struct msghdr            msg;
        struct iovec             iov;
        struct cmsghdr          *cmsg;
@@ -339,34 +204,16 @@ udp_override(struct sockaddr_in *dst, struct sockaddr_in *src_bind,
 #define CMSGBUF_SP     CMSGSP_SADDR + CMSGSP_HOPLIM + CMSGSP_BOGUS + 3
        unsigned char            cmsgbuf[CMSGBUF_SP];
 
-       bzero(&msg, sizeof(msg));
-       bzero(&cmsgbuf, sizeof(cmsgbuf));
-
-       s = socket(src_bind->sin_family, SOCK_DGRAM, 0);
-       if (s == -1) {
-               warn("udp_override: socket()");
-               kill(getpid(), SIGTERM);
-       }
-
-       optval = 1;
-       if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int))) {
-               warn("udp_override: setsockopt(SO_REUSEADDR)");
-               kill(getpid(), SIGTERM);
-       }
-
-       if (bind(s, (struct sockaddr *)src_bind, src_bind->sin_len)) {
-               warn("udp_override: bind()");
-               kill(getpid(), SIGTERM);
-       }
-
        iov.iov_base = PAYLOAD;
        iov.iov_len = strlen(PAYLOAD) + 1;
+       bzero(&msg, sizeof(msg));
        msg.msg_name = dst;
        msg.msg_namelen = dst->sin_len;
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
 
-       if (src_sendmsg) {
+       if (src) {
+               bzero(&cmsgbuf, sizeof(cmsgbuf));
                msg.msg_control = &cmsgbuf;
                msg.msg_controllen = CMSGSP_SADDR;
                cmsg = CMSG_FIRSTHDR(&msg);
@@ -374,7 +221,7 @@ udp_override(struct sockaddr_in *dst, struct sockaddr_in *src_bind,
                cmsg->cmsg_level = IPPROTO_IP;
                cmsg->cmsg_type = IP_SENDSRCADDR;
                sendopt = (struct in_addr *)CMSG_DATA(cmsg);
-               memcpy(sendopt, &src_sendmsg->sin_addr, sizeof(*sendopt));
+               memcpy(sendopt, &src->sin_addr, sizeof(*sendopt));
                if (fuzzit) {
                        msg.msg_controllen = CMSGBUF_SP;
                        cmsg = CMSG_NXTHDR(&msg, cmsg);
@@ -391,12 +238,43 @@ udp_override(struct sockaddr_in *dst, struct sockaddr_in *src_bind,
                }
        }
 
-       send_rc = sendmsg(s, &msg, 0);
-       saved_errno = errno;
+       if (sendmsg(s, &msg, 0) == -1)
+               return errno;
 
-       close(s);
+       return 0;
+}
 
-       if (send_rc == iov.iov_len)
-               return 0;
-       return saved_errno;
+struct sockaddr_in *
+udp_recv(int s, struct sockaddr_in *dst)
+{
+       struct sockaddr_in *src;
+       struct pollfd pfd[1];
+       char addr[16], buf[256];
+       int nready, len, salen;
+
+       inet_ntop(dst->sin_family, &dst->sin_addr, addr, sizeof(addr));
+
+       pfd[0].fd = s;
+       pfd[0].events = POLLIN;
+       nready = poll(pfd, 1, 2 * 1000);
+       if (nready == -1)
+               err(1, "poll");
+       if (nready == 0)
+               return NULL;
+       if ((pfd[0].revents & POLLIN) == 0)
+               errx(1, "event %d %s", pfd[0].revents, addr);
+
+       if ((src = malloc(sizeof(*src))) == NULL)
+               err(1, "malloc");
+       salen = sizeof(*src);
+       if ((len = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)src,
+           &salen)) == -1)
+               err(1, "recvfrom %s", addr);
+
+       if (len != strlen(PAYLOAD) + 1)
+               errx(1, "recvfrom %s len %d", addr, len);
+       if (strcmp(buf, PAYLOAD) != 0)
+               errx(1, "recvfrom %s payload", addr);
+
+       return src;
 }