xref: /openbsd-src/regress/sys/netinet/sendsrcaddr/runtest.c (revision 7c4552f47293c6aede5d0485921e7e233b1b9b2d)
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