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