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