xref: /openbsd-src/regress/sys/netinet/sendsrcaddr/runtest.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*
2  * Copyright (c) 2016 Vincent Gross <vincent.gross@kilob.yt>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include <err.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <getopt.h>
21 #include <netdb.h>
22 #include <poll.h>
23 #include <signal.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 
29 #include <sys/ioctl.h>
30 #include <sys/socket.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 
34 #include <net/bpf.h>
35 #include <net/if.h>
36 
37 #include <netinet/in.h>
38 #include <netinet/ip.h>
39 #include <netinet/if_ether.h>
40 
41 #include <arpa/inet.h>
42 
43 #define PORTNUM "23000"
44 #define PAYLOAD "payload"
45 
46 char cmd_tmpl[] = "route get %s | awk '/interface:/ { printf($2) }'";
47 #define CMD_TMPL_SZ sizeof(cmd_tmpl)
48 
49 int fuzzit;
50 
51 void check_packet_tx(int);
52 
53 int
54 main(int argc, char *argv[])
55 {
56 	int			  i;
57 	char			 *argp, *addr, *flag;
58 
59 	struct addrinfo		  hints;
60 	struct addrinfo		 *inai;
61 
62 	struct sockaddr_in	 *dst_sin = NULL;
63 	struct sockaddr_in	 *reserved_sin = NULL;
64 	struct sockaddr_in	 *bind_sin = NULL;
65 	struct sockaddr_in	 *cmsg_sin = NULL;
66 	struct sockaddr_in	 *wire_sin = NULL;
67 
68 	int			  ch, rc, wstat, expected = -1;
69 	int			  first_sock;
70 	pid_t			  pid;
71 
72 	const char		 *numerr;
73 	char			  adrbuf[40];
74 	const char		 *adrp;
75 
76 	char			 *dst_str = NULL;
77 	char			  cmd[CMD_TMPL_SZ + INET_ADDRSTRLEN];
78 	FILE			 *outif_pipe;
79 	char			  ifname_buf[IF_NAMESIZE];
80 	size_t			  ifname_len;
81 
82 	int			  bpf_fd;
83 
84 
85 	bzero(&hints, sizeof(hints));
86 	hints.ai_family = AF_INET;
87 	hints.ai_socktype = SOCK_DGRAM;
88 
89 	expected = strtonum(argv[1], 0, 255, &numerr);
90 	if (numerr != NULL)
91 		errx(2, "strtonum(%s): %s", optarg, numerr);
92 
93 	for (i = 2; i < argc; i++) {
94 		argp = argv[i];
95 		if (strcmp("fuzz",argp) == 0) {
96 			fuzzit = 1;
97 			continue;
98 		}
99 		addr = strsep(&argp, "=");
100 		rc = getaddrinfo(addr, PORTNUM, &hints, &inai);
101 		if (rc)
102 			errx(2, "getaddrinfo(%s) = %d: %s",
103 			    argv[0], rc, gai_strerror(rc));
104 		if (argp == NULL)
105 			errx(2, "arg must be of form <addr>=<flag>,<flag>");
106 
107 		for (; (flag = strsep(&argp,",")) != NULL;) {
108 			if (strcmp("destination",flag) == 0 && dst_sin == NULL) {
109 				dst_sin = (struct sockaddr_in *)inai->ai_addr;
110 				/* get output interface */
111 				snprintf(cmd, sizeof(cmd), cmd_tmpl, addr);
112 				outif_pipe = popen(cmd, "re");
113 				if (outif_pipe == NULL)
114 					err(2, "popen(route get)");
115 				if (fgets(ifname_buf, IF_NAMESIZE, outif_pipe) == NULL)
116 					err(2, "fgets()");
117 				pclose(outif_pipe);
118 				if (strlen(ifname_buf) == 0)
119 					err(2, "strlen(ifname_buf) == 0");
120 			}
121 			if (strcmp("reserved_saddr",flag) == 0 && reserved_sin == NULL)
122 				reserved_sin = (struct sockaddr_in *)inai->ai_addr;
123 			if (strcmp("bind_saddr",flag) == 0 && bind_sin == NULL)
124 				bind_sin = (struct sockaddr_in *)inai->ai_addr;
125 			if (strcmp("cmsg_saddr",flag) == 0 && cmsg_sin == NULL)
126 				cmsg_sin = (struct sockaddr_in *)inai->ai_addr;
127 			if (strcmp("wire_saddr",flag) == 0 && wire_sin == NULL)
128 				wire_sin = (struct sockaddr_in *)inai->ai_addr;
129 		}
130 	}
131 
132 	if (reserved_sin == NULL)
133 		errx(2, "reserved_sin == NULL");
134 
135 	if (bind_sin == NULL)
136 		errx(2, "bind_sin == NULL");
137 
138 	if (dst_sin == NULL)
139 		errx(2, "dst_sin == NULL");
140 
141 	if (expected < 0)
142 		errx(2, "need expected");
143 
144 
145 	if (wire_sin != NULL)
146 		bpf_fd = setup_bpf(ifname_buf, wire_sin, dst_sin);
147 
148 	first_sock = udp_first(reserved_sin);
149 
150 	pid = fork();
151 	if (pid == 0) {
152 		return udp_override(dst_sin, bind_sin, cmsg_sin);
153 	}
154 	(void)wait(&wstat);
155 	close(first_sock);
156 
157 	if (!WIFEXITED(wstat))
158 		errx(2, "error setting up override");
159 
160 	if (WEXITSTATUS(wstat) != expected)
161 		errx(2, "expected %d, got %d", expected, WEXITSTATUS(wstat));
162 
163 	if (wire_sin != NULL)
164 		check_packet_tx(bpf_fd);
165 
166 	return EXIT_SUCCESS;
167 }
168 
169 
170 struct bpf_insn outgoing_bpf_filter[] = {
171 	/* ethertype = IP */
172 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
173 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 9),
174 
175 	/* Make sure it's a UDP packet. */
176 	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
177 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 7),
178 
179 	/* Fragments are handled as errors */
180 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
181 	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 5, 0),
182 
183 	/* Make sure it's from the right address */
184 	BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 26),
185 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0, 0, 3), /* Need to patch this */
186 
187 	/* Make sure it's to the right address */
188 	BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 30),
189 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0, 0, 1), /* Need to patch this */
190 #if 0
191 	/* Get the IP header length. */
192 	BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
193 
194 	/* Make sure it's to the right port. */
195 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
196 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0, 0, 1),
197 #endif
198 	/* If we passed all the tests, ask for the whole packet. */
199 	BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
200 
201 	/* Otherwise, drop it. */
202 	BPF_STMT(BPF_RET+BPF_K, 0),
203 };
204 
205 int outgoing_bpf_filter_len = sizeof(outgoing_bpf_filter)/sizeof(struct bpf_insn);
206 
207 int
208 setup_bpf(char *ifname, struct sockaddr_in *from, struct sockaddr_in *to)
209 {
210 	int fd;
211 	struct ifreq ifr;
212 	u_int flag;
213 	struct bpf_version vers;
214 	struct bpf_program prog;
215 
216 	fd = open("/dev/bpf", O_RDWR | O_CLOEXEC);
217 	if (fd == -1)
218 		err(2, "open(/dev/bpf)");
219 
220 	if (ioctl(fd, BIOCVERSION, &vers) < 0)
221 		err(2, "ioctl(BIOCVERSION)");
222 
223 	if (vers.bv_major != BPF_MAJOR_VERSION ||
224 	    vers.bv_minor < BPF_MINOR_VERSION)
225 		errx(2, "bpf version mismatch, expected %d.%d, got %d.%d",
226 		    BPF_MAJOR_VERSION, BPF_MINOR_VERSION,
227 		    vers.bv_major, vers.bv_minor);
228 
229 	strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
230 	if (ioctl(fd, BIOCSETIF, &ifr) < 0)
231 		err(2, "ioctl(BIOCSETIF)");
232 
233 	flag = 1;
234 	if (ioctl(fd, BIOCIMMEDIATE, &flag) < 0)
235 		err(2, "ioctl(BIOCIMMEDIATE)");
236 
237 	flag = BPF_DIRECTION_IN;
238 	if (ioctl(fd, BIOCSDIRFILT, &flag) < 0)
239 		err(2, "ioctl(BIOCDIRFILT)");
240 
241 	outgoing_bpf_filter[7].k = ntohl(from->sin_addr.s_addr) ;
242 	outgoing_bpf_filter[9].k = ntohl(to->sin_addr.s_addr) ;
243 #if 0
244 	outgoing_bpf_filter[12].k = (u_int32_t)ntohs(to->sin_port) ;
245 #endif
246 
247 	prog.bf_len = outgoing_bpf_filter_len;
248 	prog.bf_insns = outgoing_bpf_filter;
249 	if (ioctl(fd, BIOCSETF, &prog) < 0)
250 		err(2, "ioctl(BIOCSETF)");
251 
252 	return fd;
253 }
254 
255 void
256 check_packet_tx(int fd)
257 {
258 	u_int		 buf_max;
259 	size_t		 len;
260 	struct pollfd	 pfd;
261 	int		 pollrc;
262 	char		*buf, *payload;
263 	struct bpf_hdr	*hdr;
264 	struct ip	*ip;
265 
266 	if (ioctl(fd, BIOCGBLEN, &buf_max) < 0)
267 		err(2, "ioctl(BIOCGBLEN)");
268 
269 	if (buf_max <= 0)
270 		errx(2, "buf_max = %d <= 0", buf_max);
271 
272 	buf = malloc(buf_max);
273 	if (!buf)
274 		err(2, "malloc(buf_max)");
275 
276 	pfd.fd = fd;
277 	pfd.events = POLLIN;
278 	pollrc = poll(&pfd, 1, 5000);
279 	if (pollrc == -1)
280 		err(2, "poll()");
281 	if (pollrc == 0)
282 		errx(2, "poll() timeout");
283 
284 	len = read(fd, buf, buf_max);
285 	if (len <= 0)
286 		err(2, "read(/dev/bpf)");
287 	len = BPF_WORDALIGN(len);
288 
289 	if (len < sizeof(hdr))
290 		errx(2, "short read, len < sizeof(bpf_hdr)");
291 
292 	hdr = (struct bpf_hdr *)buf;
293 	if (hdr->bh_hdrlen + hdr->bh_caplen > len)
294 		errx(2, "buffer too small for the whole capture");
295 
296 	/* XXX we could try again if enough space in the buffer */
297 	if (hdr->bh_caplen != hdr->bh_datalen)
298 		errx(2, "partial capture");
299 
300 	ip = (struct ip *)(buf + hdr->bh_hdrlen + ETHER_HDR_LEN);
301 	payload = ((char *)ip + ip->ip_hl*4 + 8);
302 
303 	if (strcmp(PAYLOAD,payload) != 0)
304 		errx(2, "payload corrupted");
305 
306 	return;
307 }
308 
309 int
310 udp_first(struct sockaddr_in *src)
311 {
312 	int s_con;
313 
314 	s_con = socket(src->sin_family, SOCK_DGRAM, 0);
315 	if (s_con == -1)
316 		err(2, "udp_bind: socket()");
317 
318 	if (bind(s_con, (struct sockaddr *)src, src->sin_len))
319 		err(2, "udp_bind: bind()");
320 
321 	return s_con;
322 }
323 
324 
325 int
326 udp_override(struct sockaddr_in *dst, struct sockaddr_in *src_bind,
327     struct sockaddr_in *src_sendmsg)
328 {
329 	int			 s, optval, error, saved_errno;
330 	ssize_t			 send_rc;
331 	struct msghdr		 msg;
332 	struct iovec		 iov;
333 	struct cmsghdr		*cmsg;
334 	struct in_addr		*sendopt;
335 	int			*hopopt;
336 #define CMSGSP_SADDR	CMSG_SPACE(sizeof(u_int32_t))
337 #define CMSGSP_HOPLIM	CMSG_SPACE(sizeof(int))
338 #define CMSGSP_BOGUS	CMSG_SPACE(12)
339 #define CMSGBUF_SP	CMSGSP_SADDR + CMSGSP_HOPLIM + CMSGSP_BOGUS + 3
340 	unsigned char		 cmsgbuf[CMSGBUF_SP];
341 
342 	bzero(&msg, sizeof(msg));
343 	bzero(&cmsgbuf, sizeof(cmsgbuf));
344 
345 	s = socket(src_bind->sin_family, SOCK_DGRAM, 0);
346 	if (s == -1) {
347 		warn("udp_override: socket()");
348 		kill(getpid(), SIGTERM);
349 	}
350 
351 	optval = 1;
352 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int))) {
353 		warn("udp_override: setsockopt(SO_REUSEADDR)");
354 		kill(getpid(), SIGTERM);
355 	}
356 
357 	if (bind(s, (struct sockaddr *)src_bind, src_bind->sin_len)) {
358 		warn("udp_override: bind()");
359 		kill(getpid(), SIGTERM);
360 	}
361 
362 	iov.iov_base = PAYLOAD;
363 	iov.iov_len = strlen(PAYLOAD) + 1;
364 	msg.msg_name = dst;
365 	msg.msg_namelen = dst->sin_len;
366 	msg.msg_iov = &iov;
367 	msg.msg_iovlen = 1;
368 
369 	if (src_sendmsg) {
370 		msg.msg_control = &cmsgbuf;
371 		msg.msg_controllen = CMSGSP_SADDR;
372 		cmsg = CMSG_FIRSTHDR(&msg);
373 		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
374 		cmsg->cmsg_level = IPPROTO_IP;
375 		cmsg->cmsg_type = IP_SENDSRCADDR;
376 		sendopt = (struct in_addr *)CMSG_DATA(cmsg);
377 		memcpy(sendopt, &src_sendmsg->sin_addr, sizeof(*sendopt));
378 		if (fuzzit) {
379 			msg.msg_controllen = CMSGBUF_SP;
380 			cmsg = CMSG_NXTHDR(&msg, cmsg);
381 			cmsg->cmsg_len = CMSG_LEN(sizeof(int));
382 			cmsg->cmsg_level = IPPROTO_IPV6;
383 			cmsg->cmsg_type = IPV6_UNICAST_HOPS;
384 			hopopt = (int *)CMSG_DATA(cmsg);
385 			*hopopt = 8;
386 
387 			cmsg = CMSG_NXTHDR(&msg, cmsg);
388 			cmsg->cmsg_len = CMSG_LEN(sizeof(int)) + 15;
389 			cmsg->cmsg_level = IPPROTO_IPV6;
390 			cmsg->cmsg_type = IPV6_UNICAST_HOPS;
391 		}
392 	}
393 
394 	send_rc = sendmsg(s, &msg, 0);
395 	saved_errno = errno;
396 
397 	close(s);
398 
399 	if (send_rc == iov.iov_len)
400 		return 0;
401 	return saved_errno;
402 }
403