18199dcaaSvgross /*
28199dcaaSvgross * Copyright (c) 2016 Vincent Gross <vincent.gross@kilob.yt>
3*7c4552f4Sbluhm * Copyright (c) 2017 Alexander Bluhm <bluhm@openbsd.org>
48199dcaaSvgross *
58199dcaaSvgross * Permission to use, copy, modify, and distribute this software for any
68199dcaaSvgross * purpose with or without fee is hereby granted, provided that the above
78199dcaaSvgross * copyright notice and this permission notice appear in all copies.
88199dcaaSvgross *
98199dcaaSvgross * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
108199dcaaSvgross * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
118199dcaaSvgross * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
128199dcaaSvgross * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
138199dcaaSvgross * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
148199dcaaSvgross * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
158199dcaaSvgross * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
168199dcaaSvgross */
178199dcaaSvgross
18*7c4552f4Sbluhm #include <sys/types.h>
19*7c4552f4Sbluhm #include <sys/socket.h>
20*7c4552f4Sbluhm
21*7c4552f4Sbluhm #include <arpa/inet.h>
22*7c4552f4Sbluhm #include <netinet/in.h>
23*7c4552f4Sbluhm
248199dcaaSvgross #include <err.h>
258199dcaaSvgross #include <errno.h>
268199dcaaSvgross #include <getopt.h>
278199dcaaSvgross #include <netdb.h>
288199dcaaSvgross #include <poll.h>
298199dcaaSvgross #include <stdlib.h>
308199dcaaSvgross #include <string.h>
318199dcaaSvgross #include <stdio.h>
328199dcaaSvgross #include <unistd.h>
338199dcaaSvgross
348199dcaaSvgross #define PAYLOAD "payload"
358199dcaaSvgross
368199dcaaSvgross int fuzzit;
378199dcaaSvgross
38*7c4552f4Sbluhm void __dead usage(const char *);
39*7c4552f4Sbluhm int udp_bind(struct sockaddr_in *);
40*7c4552f4Sbluhm int udp_send(int, struct sockaddr_in *, struct sockaddr_in *);
41*7c4552f4Sbluhm struct sockaddr_in * udp_recv(int s, struct sockaddr_in *);
42*7c4552f4Sbluhm
43*7c4552f4Sbluhm void __dead
usage(const char * msg)44*7c4552f4Sbluhm usage(const char *msg)
45*7c4552f4Sbluhm {
46*7c4552f4Sbluhm if (msg != NULL)
47*7c4552f4Sbluhm fprintf(stderr, "%s\n", msg);
48*7c4552f4Sbluhm fprintf(stderr, "runtest [-f] -D destination -B bind [-C cmesg] "
49*7c4552f4Sbluhm "[-E error] -R reserved -W wire\n");
50*7c4552f4Sbluhm exit(1);
51*7c4552f4Sbluhm }
528199dcaaSvgross
538199dcaaSvgross int
main(int argc,char * argv[])548199dcaaSvgross main(int argc, char *argv[])
558199dcaaSvgross {
56*7c4552f4Sbluhm int ch, error, errexpect, bind_sock, dest_sock, resv_sock;
57*7c4552f4Sbluhm char addr[16];
58*7c4552f4Sbluhm const char *errstr;
59*7c4552f4Sbluhm struct addrinfo hints, *res;
60*7c4552f4Sbluhm struct sockaddr_in *bind_sin, *cmsg_sin, *dest_sin, *resv_sin,
61*7c4552f4Sbluhm *wire_sin, *from_sin;
628199dcaaSvgross
638199dcaaSvgross bzero(&hints, sizeof(hints));
648199dcaaSvgross hints.ai_family = AF_INET;
658199dcaaSvgross hints.ai_socktype = SOCK_DGRAM;
668199dcaaSvgross
67*7c4552f4Sbluhm bind_sin = cmsg_sin = dest_sin = resv_sin = wire_sin = NULL;
68*7c4552f4Sbluhm errexpect = 0;
698199dcaaSvgross
70*7c4552f4Sbluhm while ((ch = getopt(argc, argv, "B:C:D:E:fR:W:")) != -1) {
71*7c4552f4Sbluhm switch (ch) {
72*7c4552f4Sbluhm case 'B':
73*7c4552f4Sbluhm error = getaddrinfo(optarg, NULL, &hints, &res);
74*7c4552f4Sbluhm if (error)
75*7c4552f4Sbluhm errx(1, "-B: %s", gai_strerror(error));
76*7c4552f4Sbluhm bind_sin = (struct sockaddr_in *)res->ai_addr;
77*7c4552f4Sbluhm break;
78*7c4552f4Sbluhm case 'C':
79*7c4552f4Sbluhm error = getaddrinfo(optarg, NULL, &hints, &res);
80*7c4552f4Sbluhm if (error)
81*7c4552f4Sbluhm errx(1, "-C: %s", gai_strerror(error));
82*7c4552f4Sbluhm cmsg_sin = (struct sockaddr_in *)res->ai_addr;
83*7c4552f4Sbluhm break;
84*7c4552f4Sbluhm case 'D':
85*7c4552f4Sbluhm error = getaddrinfo(optarg, NULL, &hints, &res);
86*7c4552f4Sbluhm if (error)
87*7c4552f4Sbluhm errx(1, "-D: %s", gai_strerror(error));
88*7c4552f4Sbluhm dest_sin = (struct sockaddr_in *)res->ai_addr;
89*7c4552f4Sbluhm break;
90*7c4552f4Sbluhm case 'E':
91*7c4552f4Sbluhm errexpect = strtonum(optarg, 1, 255, &errstr);
92*7c4552f4Sbluhm if (errstr != NULL)
93*7c4552f4Sbluhm errx(1, "error number is %s: %s",
94*7c4552f4Sbluhm errstr, optarg);
95*7c4552f4Sbluhm break;
96*7c4552f4Sbluhm case 'f':
978199dcaaSvgross fuzzit = 1;
98*7c4552f4Sbluhm break;
99*7c4552f4Sbluhm case 'R':
100*7c4552f4Sbluhm error = getaddrinfo(optarg, NULL, &hints, &res);
101*7c4552f4Sbluhm if (error)
102*7c4552f4Sbluhm errx(1, "-R: %s", gai_strerror(error));
103*7c4552f4Sbluhm resv_sin = (struct sockaddr_in *)res->ai_addr;
104*7c4552f4Sbluhm break;
105*7c4552f4Sbluhm case 'W':
106*7c4552f4Sbluhm error = getaddrinfo(optarg, NULL, &hints, &res);
107*7c4552f4Sbluhm if (error)
108*7c4552f4Sbluhm errx(1, "-W: %s", gai_strerror(error));
109*7c4552f4Sbluhm wire_sin = (struct sockaddr_in *)res->ai_addr;
110*7c4552f4Sbluhm break;
111*7c4552f4Sbluhm default:
112*7c4552f4Sbluhm usage(NULL);
1138199dcaaSvgross }
114*7c4552f4Sbluhm }
115*7c4552f4Sbluhm argc -= optind;
116*7c4552f4Sbluhm argv += optind;
1178199dcaaSvgross
118*7c4552f4Sbluhm if (argc > 0)
119*7c4552f4Sbluhm usage("too many arguments");
1208199dcaaSvgross
1218199dcaaSvgross if (bind_sin == NULL)
122*7c4552f4Sbluhm usage("no bind addr");
1238199dcaaSvgross
124*7c4552f4Sbluhm if (dest_sin == NULL)
125*7c4552f4Sbluhm usage("no destination addr");
1268199dcaaSvgross
127*7c4552f4Sbluhm if (resv_sin == NULL)
128*7c4552f4Sbluhm usage("no reserved addr");
1298199dcaaSvgross
130*7c4552f4Sbluhm /* bind on address that cannot be used */
131*7c4552f4Sbluhm resv_sock = udp_bind(resv_sin);
1328199dcaaSvgross
133*7c4552f4Sbluhm /* bind socket that should receive the packet */
134*7c4552f4Sbluhm dest_sock = udp_bind(dest_sin);
1358199dcaaSvgross
136*7c4552f4Sbluhm /* bind socket that is used to send the packet */
137*7c4552f4Sbluhm bind_sin->sin_port = resv_sin->sin_port;
138*7c4552f4Sbluhm bind_sock = udp_bind(bind_sin);
139*7c4552f4Sbluhm error = udp_send(bind_sock, cmsg_sin, dest_sin);
1408199dcaaSvgross
141*7c4552f4Sbluhm if (errexpect && !error) {
142*7c4552f4Sbluhm errno = errexpect;
143*7c4552f4Sbluhm err(2, "udp send succeeded, but expected error");
1448199dcaaSvgross }
145*7c4552f4Sbluhm if (!errexpect && error) {
146*7c4552f4Sbluhm errno = error;
147*7c4552f4Sbluhm err(2, "no error expected, but udp send failed");
148*7c4552f4Sbluhm }
149*7c4552f4Sbluhm if (errexpect != error) {
150*7c4552f4Sbluhm errno = error;
151*7c4552f4Sbluhm err(2, "expected error %d, but udp send failed", errexpect);
1528199dcaaSvgross }
1538199dcaaSvgross
154*7c4552f4Sbluhm if (wire_sin != NULL) {
155*7c4552f4Sbluhm from_sin = udp_recv(dest_sock, dest_sin);
156*7c4552f4Sbluhm if (from_sin == NULL)
157*7c4552f4Sbluhm errx(2, "receive timeout");
158*7c4552f4Sbluhm inet_ntop(from_sin->sin_family, &from_sin->sin_addr,
159*7c4552f4Sbluhm addr, sizeof(addr));
160*7c4552f4Sbluhm if (from_sin->sin_addr.s_addr != wire_sin->sin_addr.s_addr)
161*7c4552f4Sbluhm errx(2, "receive addr %s", addr);
162*7c4552f4Sbluhm if (from_sin->sin_port != bind_sin->sin_port)
163*7c4552f4Sbluhm errx(2, "receive port %d", ntohs(from_sin->sin_port));
1648199dcaaSvgross }
1658199dcaaSvgross
166*7c4552f4Sbluhm return 0;
1678199dcaaSvgross }
1688199dcaaSvgross
1698199dcaaSvgross int
udp_bind(struct sockaddr_in * src)170*7c4552f4Sbluhm udp_bind(struct sockaddr_in *src)
1718199dcaaSvgross {
172*7c4552f4Sbluhm int s, reuse, salen;
173*7c4552f4Sbluhm char addr[16];
1748199dcaaSvgross
175*7c4552f4Sbluhm inet_ntop(src->sin_family, &src->sin_addr, addr, sizeof(addr));
1768199dcaaSvgross
177*7c4552f4Sbluhm if ((s = socket(src->sin_family, SOCK_DGRAM, 0)) == -1)
178*7c4552f4Sbluhm err(1, "socket %s", addr);
179*7c4552f4Sbluhm reuse = ntohl(src->sin_addr.s_addr) == INADDR_ANY ? 1 : 0;
180*7c4552f4Sbluhm if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))
181*7c4552f4Sbluhm == -1)
182*7c4552f4Sbluhm err(1, "setsockopt %s", addr);
183*7c4552f4Sbluhm if (bind(s, (struct sockaddr *)src, src->sin_len) == -1)
184*7c4552f4Sbluhm err(1, "bind %s", addr);
185*7c4552f4Sbluhm /* fill out port */
186*7c4552f4Sbluhm salen = sizeof(*src);
187*7c4552f4Sbluhm if (getsockname(s, (struct sockaddr *)src, &salen))
188*7c4552f4Sbluhm err(1, "getsockname %s", addr);
1898199dcaaSvgross
190*7c4552f4Sbluhm return s;
1918199dcaaSvgross }
1928199dcaaSvgross
1938199dcaaSvgross int
udp_send(int s,struct sockaddr_in * src,struct sockaddr_in * dst)194*7c4552f4Sbluhm udp_send(int s, struct sockaddr_in *src, struct sockaddr_in *dst)
1958199dcaaSvgross {
1968199dcaaSvgross struct msghdr msg;
1978199dcaaSvgross struct iovec iov;
1988199dcaaSvgross struct cmsghdr *cmsg;
1998199dcaaSvgross struct in_addr *sendopt;
2008199dcaaSvgross int *hopopt;
2018199dcaaSvgross #define CMSGSP_SADDR CMSG_SPACE(sizeof(u_int32_t))
2028199dcaaSvgross #define CMSGSP_HOPLIM CMSG_SPACE(sizeof(int))
2038199dcaaSvgross #define CMSGSP_BOGUS CMSG_SPACE(12)
2048199dcaaSvgross #define CMSGBUF_SP CMSGSP_SADDR + CMSGSP_HOPLIM + CMSGSP_BOGUS + 3
2058199dcaaSvgross unsigned char cmsgbuf[CMSGBUF_SP];
2068199dcaaSvgross
2078199dcaaSvgross iov.iov_base = PAYLOAD;
2088199dcaaSvgross iov.iov_len = strlen(PAYLOAD) + 1;
209*7c4552f4Sbluhm bzero(&msg, sizeof(msg));
2108199dcaaSvgross msg.msg_name = dst;
2118199dcaaSvgross msg.msg_namelen = dst->sin_len;
2128199dcaaSvgross msg.msg_iov = &iov;
2138199dcaaSvgross msg.msg_iovlen = 1;
2148199dcaaSvgross
215*7c4552f4Sbluhm if (src) {
216*7c4552f4Sbluhm bzero(&cmsgbuf, sizeof(cmsgbuf));
2178199dcaaSvgross msg.msg_control = &cmsgbuf;
2188199dcaaSvgross msg.msg_controllen = CMSGSP_SADDR;
2198199dcaaSvgross cmsg = CMSG_FIRSTHDR(&msg);
2208199dcaaSvgross cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
2218199dcaaSvgross cmsg->cmsg_level = IPPROTO_IP;
2228199dcaaSvgross cmsg->cmsg_type = IP_SENDSRCADDR;
2238199dcaaSvgross sendopt = (struct in_addr *)CMSG_DATA(cmsg);
224*7c4552f4Sbluhm memcpy(sendopt, &src->sin_addr, sizeof(*sendopt));
2258199dcaaSvgross if (fuzzit) {
2268199dcaaSvgross msg.msg_controllen = CMSGBUF_SP;
2278199dcaaSvgross cmsg = CMSG_NXTHDR(&msg, cmsg);
2288199dcaaSvgross cmsg->cmsg_len = CMSG_LEN(sizeof(int));
2298199dcaaSvgross cmsg->cmsg_level = IPPROTO_IPV6;
2308199dcaaSvgross cmsg->cmsg_type = IPV6_UNICAST_HOPS;
2318199dcaaSvgross hopopt = (int *)CMSG_DATA(cmsg);
2328199dcaaSvgross *hopopt = 8;
2338199dcaaSvgross
2348199dcaaSvgross cmsg = CMSG_NXTHDR(&msg, cmsg);
2358199dcaaSvgross cmsg->cmsg_len = CMSG_LEN(sizeof(int)) + 15;
2368199dcaaSvgross cmsg->cmsg_level = IPPROTO_IPV6;
2378199dcaaSvgross cmsg->cmsg_type = IPV6_UNICAST_HOPS;
2388199dcaaSvgross }
2398199dcaaSvgross }
2408199dcaaSvgross
241*7c4552f4Sbluhm if (sendmsg(s, &msg, 0) == -1)
242*7c4552f4Sbluhm return errno;
2438199dcaaSvgross
2448199dcaaSvgross return 0;
245*7c4552f4Sbluhm }
246*7c4552f4Sbluhm
247*7c4552f4Sbluhm struct sockaddr_in *
udp_recv(int s,struct sockaddr_in * dst)248*7c4552f4Sbluhm udp_recv(int s, struct sockaddr_in *dst)
249*7c4552f4Sbluhm {
250*7c4552f4Sbluhm struct sockaddr_in *src;
251*7c4552f4Sbluhm struct pollfd pfd[1];
252*7c4552f4Sbluhm char addr[16], buf[256];
253*7c4552f4Sbluhm int nready, len, salen;
254*7c4552f4Sbluhm
255*7c4552f4Sbluhm inet_ntop(dst->sin_family, &dst->sin_addr, addr, sizeof(addr));
256*7c4552f4Sbluhm
257*7c4552f4Sbluhm pfd[0].fd = s;
258*7c4552f4Sbluhm pfd[0].events = POLLIN;
259*7c4552f4Sbluhm nready = poll(pfd, 1, 2 * 1000);
260*7c4552f4Sbluhm if (nready == -1)
261*7c4552f4Sbluhm err(1, "poll");
262*7c4552f4Sbluhm if (nready == 0)
263*7c4552f4Sbluhm return NULL;
264*7c4552f4Sbluhm if ((pfd[0].revents & POLLIN) == 0)
265*7c4552f4Sbluhm errx(1, "event %d %s", pfd[0].revents, addr);
266*7c4552f4Sbluhm
267*7c4552f4Sbluhm if ((src = malloc(sizeof(*src))) == NULL)
268*7c4552f4Sbluhm err(1, "malloc");
269*7c4552f4Sbluhm salen = sizeof(*src);
270*7c4552f4Sbluhm if ((len = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)src,
271*7c4552f4Sbluhm &salen)) == -1)
272*7c4552f4Sbluhm err(1, "recvfrom %s", addr);
273*7c4552f4Sbluhm
274*7c4552f4Sbluhm if (len != strlen(PAYLOAD) + 1)
275*7c4552f4Sbluhm errx(1, "recvfrom %s len %d", addr, len);
276*7c4552f4Sbluhm if (strcmp(buf, PAYLOAD) != 0)
277*7c4552f4Sbluhm errx(1, "recvfrom %s payload", addr);
278*7c4552f4Sbluhm
279*7c4552f4Sbluhm return src;
2808199dcaaSvgross }
281