1 /*
2  * Copyright (c) 2016 Vincent Gross <vincent.gross@kilob.yt>
3  * Copyright (c) 2017 Alexander Bluhm <bluhm@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 
21 #include <arpa/inet.h>
22 #include <netinet/in.h>
23 
24 #include <err.h>
25 #include <errno.h>
26 #include <getopt.h>
27 #include <netdb.h>
28 #include <poll.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 
34 #define PAYLOAD "payload"
35 
36 int fuzzit;
37 
38 void __dead usage(const char *);
39 int udp_bind(struct sockaddr_in *);
40 int udp_send(int, struct sockaddr_in *, struct sockaddr_in *);
41 struct sockaddr_in * udp_recv(int s, struct sockaddr_in *);
42 
43 void __dead
44 usage(const char *msg)
45 {
46 	if (msg != NULL)
47 		fprintf(stderr, "%s\n", msg);
48 	fprintf(stderr, "runtest [-f] -D destination -B bind [-C cmesg] "
49 	    "[-E error] -R reserved -W wire\n");
50 	exit(1);
51 }
52 
53 int
54 main(int argc, char *argv[])
55 {
56 	int ch, error, errexpect, bind_sock, dest_sock, resv_sock;
57 	char addr[16];
58 	const char *errstr;
59 	struct addrinfo hints, *res;
60 	struct sockaddr_in *bind_sin, *cmsg_sin, *dest_sin, *resv_sin,
61 	    *wire_sin, *from_sin;
62 
63 	bzero(&hints, sizeof(hints));
64 	hints.ai_family = AF_INET;
65 	hints.ai_socktype = SOCK_DGRAM;
66 
67 	bind_sin = cmsg_sin = dest_sin = resv_sin = wire_sin = NULL;
68 	errexpect = 0;
69 
70 	while ((ch = getopt(argc, argv, "B:C:D:E:fR:W:")) != -1) {
71 		switch (ch) {
72 		case 'B':
73 			error = getaddrinfo(optarg, NULL, &hints, &res);
74 			if (error)
75 				errx(1, "-B: %s", gai_strerror(error));
76 			bind_sin = (struct sockaddr_in *)res->ai_addr;
77 			break;
78 		case 'C':
79 			error = getaddrinfo(optarg, NULL, &hints, &res);
80 			if (error)
81 				errx(1, "-C: %s", gai_strerror(error));
82 			cmsg_sin = (struct sockaddr_in *)res->ai_addr;
83 			break;
84 		case 'D':
85 			error = getaddrinfo(optarg, NULL, &hints, &res);
86 			if (error)
87 				errx(1, "-D: %s", gai_strerror(error));
88 			dest_sin = (struct sockaddr_in *)res->ai_addr;
89 			break;
90 		case 'E':
91 			errexpect = strtonum(optarg, 1, 255, &errstr);
92 			if (errstr != NULL)
93 				errx(1, "error number is %s: %s",
94 				    errstr, optarg);
95 			break;
96 		case 'f':
97 			fuzzit = 1;
98 			break;
99 		case 'R':
100 			error = getaddrinfo(optarg, NULL, &hints, &res);
101 			if (error)
102 				errx(1, "-R: %s", gai_strerror(error));
103 			resv_sin = (struct sockaddr_in *)res->ai_addr;
104 			break;
105 		case 'W':
106 			error = getaddrinfo(optarg, NULL, &hints, &res);
107 			if (error)
108 				errx(1, "-W: %s", gai_strerror(error));
109 			wire_sin = (struct sockaddr_in *)res->ai_addr;
110 			break;
111 		default:
112 			usage(NULL);
113 		}
114 	}
115 	argc -= optind;
116 	argv += optind;
117 
118 	if (argc > 0)
119 		usage("too many arguments");
120 
121 	if (bind_sin == NULL)
122 		usage("no bind addr");
123 
124 	if (dest_sin == NULL)
125 		usage("no destination addr");
126 
127 	if (resv_sin == NULL)
128 		usage("no reserved addr");
129 
130 	/* bind on address that cannot be used */
131 	resv_sock = udp_bind(resv_sin);
132 
133 	/* bind socket that should receive the packet */
134 	dest_sock = udp_bind(dest_sin);
135 
136 	/* bind socket that is used to send the packet */
137 	bind_sin->sin_port = resv_sin->sin_port;
138 	bind_sock = udp_bind(bind_sin);
139 	error = udp_send(bind_sock, cmsg_sin, dest_sin);
140 
141 	if (errexpect && !error) {
142 		errno = errexpect;
143 		err(2, "udp send succeeded, but expected error");
144 	}
145 	if (!errexpect && error) {
146 		errno = error;
147 		err(2, "no error expected, but udp send failed");
148 	}
149 	if (errexpect != error) {
150 		errno = error;
151 		err(2, "expected error %d, but udp send failed", errexpect);
152 	}
153 
154 	if (wire_sin != NULL) {
155 		from_sin = udp_recv(dest_sock, dest_sin);
156 		if (from_sin == NULL)
157 			errx(2, "receive timeout");
158 		inet_ntop(from_sin->sin_family, &from_sin->sin_addr,
159 		    addr, sizeof(addr));
160 		if (from_sin->sin_addr.s_addr != wire_sin->sin_addr.s_addr)
161 			errx(2, "receive addr %s", addr);
162 		if (from_sin->sin_port != bind_sin->sin_port)
163 			errx(2, "receive port %d", ntohs(from_sin->sin_port));
164 	}
165 
166 	return 0;
167 }
168 
169 int
170 udp_bind(struct sockaddr_in *src)
171 {
172 	int s, reuse, salen;
173 	char addr[16];
174 
175 	inet_ntop(src->sin_family, &src->sin_addr, addr, sizeof(addr));
176 
177 	if ((s = socket(src->sin_family, SOCK_DGRAM, 0)) == -1)
178 		err(1, "socket %s", addr);
179 	reuse = ntohl(src->sin_addr.s_addr) == INADDR_ANY ? 1 : 0;
180 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))
181 	    == -1)
182 		err(1, "setsockopt %s", addr);
183 	if (bind(s, (struct sockaddr *)src, src->sin_len) == -1)
184 		err(1, "bind %s", addr);
185 	/* fill out port */
186 	salen = sizeof(*src);
187 	if (getsockname(s, (struct sockaddr *)src, &salen))
188 		err(1, "getsockname %s", addr);
189 
190 	return s;
191 }
192 
193 int
194 udp_send(int s, struct sockaddr_in *src, struct sockaddr_in *dst)
195 {
196 	struct msghdr		 msg;
197 	struct iovec		 iov;
198 	struct cmsghdr		*cmsg;
199 	struct in_addr		*sendopt;
200 	int			*hopopt;
201 #define CMSGSP_SADDR	CMSG_SPACE(sizeof(u_int32_t))
202 #define CMSGSP_HOPLIM	CMSG_SPACE(sizeof(int))
203 #define CMSGSP_BOGUS	CMSG_SPACE(12)
204 #define CMSGBUF_SP	CMSGSP_SADDR + CMSGSP_HOPLIM + CMSGSP_BOGUS + 3
205 	unsigned char		 cmsgbuf[CMSGBUF_SP];
206 
207 	iov.iov_base = PAYLOAD;
208 	iov.iov_len = strlen(PAYLOAD) + 1;
209 	bzero(&msg, sizeof(msg));
210 	msg.msg_name = dst;
211 	msg.msg_namelen = dst->sin_len;
212 	msg.msg_iov = &iov;
213 	msg.msg_iovlen = 1;
214 
215 	if (src) {
216 		bzero(&cmsgbuf, sizeof(cmsgbuf));
217 		msg.msg_control = &cmsgbuf;
218 		msg.msg_controllen = CMSGSP_SADDR;
219 		cmsg = CMSG_FIRSTHDR(&msg);
220 		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
221 		cmsg->cmsg_level = IPPROTO_IP;
222 		cmsg->cmsg_type = IP_SENDSRCADDR;
223 		sendopt = (struct in_addr *)CMSG_DATA(cmsg);
224 		memcpy(sendopt, &src->sin_addr, sizeof(*sendopt));
225 		if (fuzzit) {
226 			msg.msg_controllen = CMSGBUF_SP;
227 			cmsg = CMSG_NXTHDR(&msg, cmsg);
228 			cmsg->cmsg_len = CMSG_LEN(sizeof(int));
229 			cmsg->cmsg_level = IPPROTO_IPV6;
230 			cmsg->cmsg_type = IPV6_UNICAST_HOPS;
231 			hopopt = (int *)CMSG_DATA(cmsg);
232 			*hopopt = 8;
233 
234 			cmsg = CMSG_NXTHDR(&msg, cmsg);
235 			cmsg->cmsg_len = CMSG_LEN(sizeof(int)) + 15;
236 			cmsg->cmsg_level = IPPROTO_IPV6;
237 			cmsg->cmsg_type = IPV6_UNICAST_HOPS;
238 		}
239 	}
240 
241 	if (sendmsg(s, &msg, 0) == -1)
242 		return errno;
243 
244 	return 0;
245 }
246 
247 struct sockaddr_in *
248 udp_recv(int s, struct sockaddr_in *dst)
249 {
250 	struct sockaddr_in *src;
251 	struct pollfd pfd[1];
252 	char addr[16], buf[256];
253 	int nready, len, salen;
254 
255 	inet_ntop(dst->sin_family, &dst->sin_addr, addr, sizeof(addr));
256 
257 	pfd[0].fd = s;
258 	pfd[0].events = POLLIN;
259 	nready = poll(pfd, 1, 2 * 1000);
260 	if (nready == -1)
261 		err(1, "poll");
262 	if (nready == 0)
263 		return NULL;
264 	if ((pfd[0].revents & POLLIN) == 0)
265 		errx(1, "event %d %s", pfd[0].revents, addr);
266 
267 	if ((src = malloc(sizeof(*src))) == NULL)
268 		err(1, "malloc");
269 	salen = sizeof(*src);
270 	if ((len = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)src,
271 	    &salen)) == -1)
272 		err(1, "recvfrom %s", addr);
273 
274 	if (len != strlen(PAYLOAD) + 1)
275 		errx(1, "recvfrom %s len %d", addr, len);
276 	if (strcmp(buf, PAYLOAD) != 0)
277 		errx(1, "recvfrom %s payload", addr);
278 
279 	return src;
280 }
281