1 /*
2 * Copyright (c) 2014, 2015, 2016 Machine Zone, Inc.
3 *
4 * Original author: Lev Walkin <lwalkin@machinezone.com>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27 #define _GNU_SOURCE
28 #include <getopt.h>
29 #include <sysexits.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <ifaddrs.h>
37 #include <net/if.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <netdb.h> /* gethostbyname(3) */
41 #include <fcntl.h>
42 #include <errno.h>
43 #include <assert.h>
44
45 #include "tcpkali_common.h"
46 #include "tcpkali_logging.h"
47 #include "tcpkali_iface.h"
48
49 /*
50 * Note: struct sockaddr_in6 is larger than struct sockaddr, hence
51 * the storage should be bigger. However, we shall not dereference
52 * the AF_INET (struct sockaddr_in *) as it were a larger structure.
53 * Therefore this code is rather complex.
54 */
55 void
address_add(struct addresses * aseq,struct sockaddr * sa)56 address_add(struct addresses *aseq, struct sockaddr *sa) {
57 /* Reallocate a bigger list and continue. Don't laugh. */
58 aseq->addrs =
59 realloc(aseq->addrs, (aseq->n_addrs + 1) * sizeof(aseq->addrs[0]));
60 assert(aseq->addrs);
61 switch(sa->sa_family) {
62 case AF_INET:
63 *(struct sockaddr_in *)&aseq->addrs[aseq->n_addrs] =
64 *(struct sockaddr_in *)sa;
65 aseq->n_addrs++;
66 break;
67 case AF_INET6:
68 *(struct sockaddr_in6 *)&aseq->addrs[aseq->n_addrs] =
69 *(struct sockaddr_in6 *)sa;
70 aseq->n_addrs++;
71 break;
72 default:
73 assert(!"Not IPv4 and not IPv6");
74 break;
75 }
76 }
77
78 typedef enum {
79 SMATCH_ADDR_ONLY,
80 SMATCH_ADDR_PORT,
81 } sockaddrs_cmp_e;
82 static int
sockaddrs_match(struct sockaddr * sa,struct sockaddr * sb,sockaddrs_cmp_e cmp)83 sockaddrs_match(struct sockaddr *sa, struct sockaddr *sb, sockaddrs_cmp_e cmp) {
84 if(sa->sa_family == sb->sa_family) {
85 switch(sa->sa_family) {
86 case AF_INET: {
87 struct sockaddr_in *sia = (struct sockaddr_in *)sa;
88 struct sockaddr_in *sib = (struct sockaddr_in *)sb;
89 if((cmp != SMATCH_ADDR_PORT || sia->sin_port == sib->sin_port)
90 && sia->sin_addr.s_addr == sib->sin_addr.s_addr) {
91 return 1;
92 }
93 } break;
94 case AF_INET6: {
95 struct sockaddr_in6 *sia = (struct sockaddr_in6 *)sa;
96 struct sockaddr_in6 *sib = (struct sockaddr_in6 *)sb;
97 if((cmp != SMATCH_ADDR_PORT || sia->sin6_port == sib->sin6_port)
98 && 0 == memcmp(&sia->sin6_addr, &sib->sin6_addr,
99 sizeof(struct in6_addr))) {
100 return 1;
101 }
102 } break;
103 }
104 }
105 return 0;
106 }
107
108 /*
109 * Return non-zero if such address already exists.
110 */
111 static int
address_is_member(struct addresses * aseq,struct sockaddr * sb)112 address_is_member(struct addresses *aseq, struct sockaddr *sb) {
113 for(size_t i = 0; i < aseq->n_addrs; i++) {
114 struct sockaddr *sa = (struct sockaddr *)&aseq->addrs[i];
115 if(sockaddrs_match(sa, sb, SMATCH_ADDR_PORT)) return 1;
116 }
117
118 return 0;
119 }
120
121 /*
122 * Display destination addresses with a given prefix, separator and suffix.
123 */
124 void
fprint_addresses(FILE * fp,char * prefix,char * separator,char * suffix,struct addresses addresses)125 fprint_addresses(FILE *fp, char *prefix, char *separator, char *suffix,
126 struct addresses addresses) {
127 for(size_t n = 0; n < addresses.n_addrs; n++) {
128 if(n == 0) {
129 fprintf(fp, "%s", prefix);
130 } else {
131 fprintf(fp, "%s", separator);
132 }
133 char buf[INET6_ADDRSTRLEN + 64];
134 fprintf(stderr, "%s",
135 format_sockaddr(&addresses.addrs[n], buf, sizeof(buf)));
136 if(n == addresses.n_addrs - 1) {
137 fprintf(fp, "%s", suffix);
138 }
139 }
140 }
141
142 /*
143 * Printable representation of a sockaddr.
144 */
145 const char *
format_sockaddr(struct sockaddr_storage * ss,char * buf,size_t size)146 format_sockaddr(struct sockaddr_storage *ss, char *buf, size_t size) {
147 void *in_addr;
148 uint16_t nport;
149 switch(ss->ss_family) {
150 case AF_INET:
151 in_addr = &((struct sockaddr_in *)ss)->sin_addr;
152 nport = ((struct sockaddr_in *)ss)->sin_port;
153 break;
154 case AF_INET6:
155 in_addr = &((struct sockaddr_in6 *)ss)->sin6_addr;
156 nport = ((struct sockaddr_in6 *)ss)->sin6_port;
157 break;
158 default:
159 assert(!"ipv4 or ipv6 expected");
160 return "<unknown>";
161 }
162 char ipbuf[INET6_ADDRSTRLEN];
163 const char *ip = inet_ntop(ss->ss_family, in_addr, ipbuf, sizeof(ipbuf));
164 snprintf(buf, size, "[%s]:%d", ip, ntohs(nport));
165 return buf;
166 }
167
168 /*
169 * Given a port, detect which addresses we can listen on, using this port.
170 */
171 struct addresses
detect_listen_addresses(const char * local_hostname,int listen_port)172 detect_listen_addresses(const char *local_hostname, int listen_port) {
173 struct addresses addresses = {0, 0};
174 struct addrinfo hints = {
175 .ai_family = AF_UNSPEC,
176 .ai_socktype = SOCK_STREAM,
177 .ai_protocol = IPPROTO_TCP,
178 .ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_ADDRCONFIG};
179 char service[32];
180 snprintf(service, sizeof(service), "%d", listen_port);
181
182 struct addrinfo *res;
183 int err = getaddrinfo(local_hostname, service, &hints, &res);
184 if(err != 0) {
185 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
186 exit(EXIT_FAILURE);
187 }
188
189 /* Move all of the addresses into the separate storage */
190 for(struct addrinfo *tmp = res; tmp; tmp = tmp->ai_next) {
191 address_add(&addresses, tmp->ai_addr);
192 }
193
194 freeaddrinfo(res);
195
196 fprint_addresses(stderr, "Listen on: ", "\nListen on: ", "\n", addresses);
197
198 return addresses;
199 }
200
201 /*
202 * Check whether we can bind to a specified IP.
203 */
204 static int
check_if_bindable_ip(struct sockaddr_storage * ss)205 check_if_bindable_ip(struct sockaddr_storage *ss) {
206 int rc;
207 int lsock = socket(ss->ss_family, SOCK_STREAM, IPPROTO_TCP);
208 assert(lsock != -1);
209 rc = bind(lsock, (struct sockaddr *)ss, sockaddr_len(ss));
210 close(lsock);
211 if(rc == -1) {
212 char buf[256];
213 fprintf(stderr, "%s is not local: %s\n",
214 format_sockaddr(ss, buf, sizeof(buf)), strerror(errno));
215 return -1;
216 }
217 return 0;
218 }
219
220 /*
221 * Parse the specified IP as it were a source IP, and add it to the list.
222 */
223 int
add_source_ip(struct addresses * addresses,const char * optarg)224 add_source_ip(struct addresses *addresses, const char *optarg) {
225 struct addrinfo hints = {.ai_family = AF_UNSPEC,
226 .ai_socktype = SOCK_STREAM,
227 .ai_protocol = IPPROTO_TCP,
228 .ai_flags = AI_PASSIVE | AI_ADDRCONFIG};
229
230 struct addrinfo *res;
231 int err = getaddrinfo(optarg, NULL, &hints, &res);
232 if(err != 0) {
233 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
234 return -1;
235 }
236
237 /* Move all of the addresses into the separate storage */
238 for(struct addrinfo *tmp = res; tmp; tmp = tmp->ai_next) {
239 address_add(addresses, tmp->ai_addr);
240 if(check_if_bindable_ip(&addresses->addrs[addresses->n_addrs - 1])
241 < 0) {
242 freeaddrinfo(res);
243 return -1;
244 }
245 }
246
247 freeaddrinfo(res);
248
249 return 0;
250 }
251
252 static void
reset_port(struct sockaddr_storage * ss,in_port_t new_port_value)253 reset_port(struct sockaddr_storage *ss, in_port_t new_port_value) {
254 switch(ss->ss_family) {
255 case AF_INET: {
256 struct sockaddr_in *sin = (struct sockaddr_in *)ss;
257 sin->sin_port = new_port_value;
258 } break;
259 case AF_INET6: {
260 struct sockaddr_in6 *sin = (struct sockaddr_in6 *)ss;
261 sin->sin6_port = new_port_value;
262 } break;
263 default:
264 assert(!"Not IPv4 and not IPv6");
265 break;
266 }
267 }
268
269 /*
270 * Return the name of the interface which contains given IP.
271 */
272 static const char *
interface_by_addr(struct ifaddrs * ifp,struct sockaddr * addr)273 interface_by_addr(struct ifaddrs *ifp, struct sockaddr *addr) {
274 for(; ifp; ifp = ifp->ifa_next) {
275 if(ifp->ifa_addr
276 && sockaddrs_match(addr, ifp->ifa_addr, SMATCH_ADDR_ONLY)) {
277 return ifp->ifa_name;
278 }
279 }
280
281 return NULL;
282 }
283
284 static int
detect_local_ip_for_remote(struct ifaddrs * ifp,struct sockaddr_storage * ss,struct sockaddr_storage * r_local_addr)285 detect_local_ip_for_remote(struct ifaddrs *ifp, struct sockaddr_storage *ss,
286 struct sockaddr_storage *r_local_addr) {
287 char tmpbuf[128];
288
289 int sockfd = socket(ss->ss_family, SOCK_STREAM, IPPROTO_TCP);
290 if(sockfd == -1) {
291 fprintf(stderr, "Could not open %s socket: %s\n",
292 ss->ss_family == AF_INET6 ? "IPv6" : "IPv4", strerror(errno));
293 return -1;
294 }
295
296 /* Enable non-blocking mode. */
297 int rc = fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK);
298 assert(rc != 1);
299
300 /*
301 * Try connecting to a destination, and figure out the IP of the local
302 * end of the connection while connection is pending. Then close quickly.
303 */
304 rc = connect(sockfd, (struct sockaddr *)ss, sockaddr_len(ss));
305 if(rc == -1 && errno != EINPROGRESS) {
306 fprintf(stderr, "Could not pre-check connection to %s: %s\n",
307 format_sockaddr(ss, tmpbuf, sizeof(tmpbuf)), strerror(errno));
308 close(sockfd);
309 return -1;
310 } else {
311 /*
312 * We could not connect to an address. Supposedly this is related
313 * to the fact that the system rather early figured we're not
314 * supposed to connect there. It can be related to IP filtering,
315 * or socket resource exhaustion, or the fact that we're connecting
316 * to something very local. A non-blocking connection
317 * to a local host may also result in EINPROGRESS, but that's
318 * probably not guaranteed. So, we check using a different algorithm
319 * if destination IP is local, then just use that interface.
320 */
321 const char *iface = interface_by_addr(ifp, (struct sockaddr *)ss);
322 if(iface) {
323 *r_local_addr = *ss;
324 reset_port(r_local_addr, 0);
325 close(sockfd);
326 return 0;
327 }
328 }
329
330 socklen_t local_addrlen = sizeof(*r_local_addr);
331 if(getsockname(sockfd, (struct sockaddr *)r_local_addr, &local_addrlen)
332 == -1) {
333 fprintf(stderr,
334 "Could not get local address when connecting to %s: %s\n",
335 format_sockaddr(ss, tmpbuf, sizeof(tmpbuf)), strerror(errno));
336 close(sockfd);
337 return -1;
338 } else {
339 /* We're going to bind to that port at some point. Must be zero. */
340 reset_port(r_local_addr, 0);
341 }
342
343 close(sockfd);
344 return 0;
345 }
346
347 static int
compare_ifnames(const char * a,const char * b)348 compare_ifnames(const char *a, const char *b) {
349 for(;; a++, b++) {
350 switch(*a) {
351 case '\0':
352 case ':':
353 if(*b == '\0' || *b == ':') break;
354 return -1;
355 default:
356 if(*a == *b) continue;
357 return -1;
358 }
359 break;
360 }
361
362 return 0;
363 }
364
365 #ifdef TCPKALI_IFACE_UNIT_TEST
366 int
main()367 main() {
368 const char *non_equivalents[][5] = {{"", "a", "b", "ab", "a0"},
369 {"a", "a0", "a1", "b0", "b1"}};
370 const char *equivalents[][5] = {
371 {"", ":", ":0", ":1", ":123"},
372 {"a", "a:", "a:0", "a:1", "a:123"},
373 {"ab", "ab:", "ab:0", "ab:1", "ab:123"},
374 {"bond0", "bond0:", "bond0:0", "bond0:1", "bond0:123"}};
375
376 /* Test non-equivalence */
377 for(size_t test = 0; test < 2; test++) {
378 for(size_t i = 0; i < 5; i++) {
379 for(size_t j = 0; j < 5; j++) {
380 if(i == j) continue;
381 const char *a = non_equivalents[test][i];
382 const char *b = non_equivalents[test][j];
383 assert(compare_ifnames(a, b) == -1);
384 }
385 }
386 }
387
388 /* Test equivalence */
389 for(size_t test = 0; test < 4; test++) {
390 for(size_t i = 0; i < 5; i++) {
391 for(size_t j = 0; j < 5; j++) {
392 if(i == j) continue;
393 const char *a = equivalents[test][i];
394 const char *b = equivalents[test][j];
395 assert(compare_ifnames(a, b) == 0);
396 }
397 }
398 }
399
400 return 0;
401 }
402 #endif
403
404
405 static int
collect_interface_addresses(struct ifaddrs * ifp,const char * ifname,sa_family_t family,struct addresses * ss)406 collect_interface_addresses(struct ifaddrs *ifp, const char *ifname,
407 sa_family_t family, struct addresses *ss) {
408 char tmpbuf[256];
409 int found = 0;
410
411 for(; ifp; ifp = ifp->ifa_next) {
412 if(ifp->ifa_addr && family == ifp->ifa_addr->sa_family
413 && compare_ifnames(ifp->ifa_name, ifname) == 0) {
414 /* Add address if it is not already there. */
415 if(!address_is_member(ss, ifp->ifa_addr)) {
416 fprintf(
417 stderr, "Interface %s address %s\n", ifname,
418 format_sockaddr((struct sockaddr_storage *)ifp->ifa_addr,
419 tmpbuf, sizeof(tmpbuf)));
420 address_add(ss, ifp->ifa_addr);
421 }
422 found = 1;
423 }
424 }
425
426 return found ? 0 : -1;
427 }
428
429 /*
430 * Given a list of destination addresses, populate the list of source
431 * addresses with compatible source (local) IPs.
432 */
433 int
detect_source_ips(struct addresses * dsts,struct addresses * srcs)434 detect_source_ips(struct addresses *dsts, struct addresses *srcs) {
435 struct ifaddrs *interfaces = 0;
436
437 int rc = getifaddrs(&interfaces);
438 if(rc == -1) {
439 /* Can't get interfaces... Won't try to use several source IPs. */
440 warning(
441 "Can't enumerate interfaces, "
442 "won't use multiple source IPs: %s\n",
443 strerror(errno));
444 return 0;
445 }
446
447 /* If we are not supposed to go anywhere, we won't invoke this function. */
448 if(dsts->n_addrs == 0) {
449 fprintf(stderr,
450 "Source IP detection failed: "
451 "No destination IPs are given\n");
452 freeifaddrs(interfaces);
453 return -1;
454 }
455
456 sa_family_t common_ss_family = 0;
457
458 for(size_t dst_idx = 0; dst_idx < dsts->n_addrs; dst_idx++) {
459 struct sockaddr_storage *ds = &dsts->addrs[dst_idx];
460 char tmpbuf[256];
461
462 /*
463 * For now, we can reliably detect source ips only
464 * when the address families of the destination ips are the same.
465 */
466 if(common_ss_family == 0) {
467 common_ss_family = ds->ss_family;
468 } else if(common_ss_family != ds->ss_family) {
469 warning(
470 "Could not detect local address when connecting to %s:"
471 " Multiple incompatible address families in destination.\n",
472 format_sockaddr(ds, tmpbuf, sizeof(tmpbuf)));
473 warning("Would not open more than 64k connections to %s\n",
474 format_sockaddr(ds, tmpbuf, sizeof(tmpbuf)));
475 srcs->n_addrs = 0;
476 freeifaddrs(interfaces);
477 return 0;
478 }
479
480 /*
481 * Attempt to create a connection and see what our
482 * local address looks like. Then search for that address
483 * among the interfaces.
484 */
485 struct sockaddr_storage local_addr;
486 if(detect_local_ip_for_remote(interfaces, ds, &local_addr)) {
487 freeifaddrs(interfaces);
488 return -1;
489 }
490
491 const char *ifname =
492 interface_by_addr(interfaces, (struct sockaddr *)&local_addr);
493 if(ifname == NULL) {
494 warning("Can't determine local interface to connect to %s\n",
495 format_sockaddr(ds, tmpbuf, sizeof(tmpbuf)));
496 warning("Would not open more than 64k connections to %s\n",
497 format_sockaddr(ds, tmpbuf, sizeof(tmpbuf)));
498 srcs->n_addrs = 0;
499 freeifaddrs(interfaces);
500 return 0;
501 }
502
503 if(collect_interface_addresses(interfaces, ifname, ds->ss_family, srcs)
504 == 0) {
505 fprintf(stderr, "Using interface %s to connect to %s\n", ifname,
506 format_sockaddr(ds, tmpbuf, sizeof(tmpbuf)));
507 } else {
508 fprintf(stderr, "Failed to collect IPs from interface %s\n",
509 ifname);
510 freeifaddrs(interfaces);
511 return -1;
512 }
513 }
514
515 freeifaddrs(interfaces);
516 return 0;
517 }
518