summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--opentracker.c242
-rw-r--r--opentracker.conf.sample28
-rw-r--r--ot_socket.c36
-rw-r--r--ot_socket.h9
-rw-r--r--ot_udp.c24
-rw-r--r--ot_udp.h4
-rw-r--r--tests/t_socket.c34
-rw-r--r--trackerlogic.c1
8 files changed, 253 insertions, 125 deletions
diff --git a/opentracker.c b/opentracker.c
index 5b197d1..5f21ede 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -48,7 +48,22 @@ int g_self_pipe[2];
48 48
49static char *g_serverdir; 49static char *g_serverdir;
50static char *g_serveruser; 50static char *g_serveruser;
51static unsigned int g_udp_workers; 51
52typedef struct {
53 ot_ip6 ip;
54 uint16_t port;
55 PROTO_FLAG proto;
56 int shared;
57 int workers;
58 int64_t sock;
59} ot_listen;
60#define OT_LISTEN_INHERIT (-1)
61
62/* Config entries collected from command line and config files */
63static ot_listen *g_listen;
64static size_t g_listen_count;
65static int g_listen_shared;
66static int g_listen_workers;
52 67
53static void panic(const char *routine) __attribute__((noreturn)); 68static void panic(const char *routine) __attribute__((noreturn));
54static void panic(const char *routine) { 69static void panic(const char *routine) {
@@ -338,44 +353,156 @@ static void *server_mainloop(void *args) {
338 return 0; 353 return 0;
339} 354}
340 355
341static int64_t ot_try_bind(ot_ip6 ip, uint16_t port, PROTO_FLAG proto) { 356static void listen_append(const ot_ip6 ip, uint16_t port, PROTO_FLAG proto, int shared, int workers) {
342 int64_t sock; 357 ot_listen *grown = realloc(g_listen, (g_listen_count + 1) * sizeof(ot_listen));
358 ot_listen *entry;
359
360 if (!grown)
361 panic("listen_append");
362 g_listen = grown;
363
364 entry = g_listen + g_listen_count++;
365 memcpy(entry->ip, ip, sizeof(ot_ip6));
366 entry->port = port;
367 entry->proto = proto;
368 entry->shared = shared;
369 entry->workers = workers;
370 entry->sock = -1;
371}
372
373typedef enum { LISTEN_OPTION_UNKNOWN, LISTEN_OPTION_SHARED, LISTEN_OPTION_WORKERS } listen_option_kind;
374
375typedef struct {
376 const char *name;
377 size_t len;
378 listen_option_kind kind;
379 int takes_value;
380} listen_option_key;
381
382#define LISTEN_OPTION_KEY(s, k, v) {s, sizeof(s) - 1, k, v}
383
384static const listen_option_key listen_option_keys[] = {
385 LISTEN_OPTION_KEY("shared", LISTEN_OPTION_SHARED, 0),
386 LISTEN_OPTION_KEY("workers", LISTEN_OPTION_WORKERS, 1),
387};
388static const size_t listen_option_count = sizeof(listen_option_keys) / sizeof(listen_option_keys[0]);
389
390static listen_option_kind parse_listen_option(char **p) {
391 size_t i;
392
393 for (i = 0; i < listen_option_count; ++i) {
394 const listen_option_key *key = &listen_option_keys[i];
395 char delimiter = (*p)[key->len];
396
397 if (strncmp(*p, key->name, key->len))
398 continue;
399 if (key->takes_value ? delimiter != '=' : (delimiter && !isspace((unsigned char)delimiter)))
400 continue;
401
402 *p += key->len + (key->takes_value ? 1 : 0);
403 return key->kind;
404 }
405 return LISTEN_OPTION_UNKNOWN;
406}
407
408/* Options trailing an address in a listen statement, in any order */
409static int listen_scan_options(char *p, int *shared, int *workers) {
410 uint64_t value;
411
412 for (;;) {
413 while (isspace((unsigned char)*p))
414 ++p;
415 if (!*p)
416 return 1;
417
418 switch (parse_listen_option(&p)) {
419 case LISTEN_OPTION_SHARED:
420 *shared = 1;
421 break;
422 case LISTEN_OPTION_WORKERS: {
423 size_t off = scan_udec(p, &value, UINT_MAX);
424
425 if (!off)
426 return 0;
427 *workers = (int)value;
428 p += off;
429 break;
430 }
431 default:
432 return 0;
433 }
434 }
435}
436
437static int listen_option(int value, int fallback) { return value == OT_LISTEN_INHERIT ? fallback : value; }
438
439static void listen_bind_one(ot_listen *entry) {
440 socket_sharing sharing = listen_option(entry->shared, g_listen_shared) ? SOCKET_SHARED : SOCKET_EXCLUSIVE;
343 441
344#ifdef _DEBUG 442#ifdef _DEBUG
345 { 443 {
346 char *protos[] = {"TCP", "UDP", "UDP mcast"}; 444 char *protos[] = {"TCP", "UDP", "UDP mcast"};
347 char _debug[512]; 445 char _debug[512];
348 int off = snprintf(_debug, sizeof(_debug), "Binding socket type %s to address [", protos[proto]); 446 int off = snprintf(_debug, sizeof(_debug), "Binding socket type %s to address [", protos[entry->proto]);
349 off += ip_format(_debug + off, ip); 447 off += ip_format(_debug + off, entry->ip);
350 snprintf(_debug + off, sizeof(_debug) - off, "]:%d...", port); 448 snprintf(_debug + off, sizeof(_debug) - off, "]:%d...", entry->port);
351 fputs(_debug, stderr); 449 fputs(_debug, stderr);
352 } 450 }
353#endif 451#endif
354 452
355 if (proto == FLAG_TCP) { 453#ifdef WANT_SYNC_LIVE
356 if ((sock = socket_tcp_listen(ip, port)) == -1) 454 if (entry->proto == FLAG_MCA) {
455 livesync_bind_mcast(entry->ip, entry->port);
456#ifdef _DEBUG
457 fputs(" success.\n", stderr);
458#endif
459 return;
460 }
461#endif
462
463 if (entry->proto == FLAG_TCP) {
464 if ((entry->sock = socket_tcp_listen(entry->ip, entry->port, sharing)) == -1)
357 panic("socket_tcp_listen"); 465 panic("socket_tcp_listen");
358 } else { 466 } else {
359 if ((sock = socket_udp_bind(ip, port)) == -1) 467 if ((entry->sock = socket_udp_bind(entry->ip, entry->port, sharing)) == -1)
360 panic("socket_udp_bind"); 468 panic("socket_udp_bind");
361 } 469 }
362 470
363 if (!io_fd(sock)) 471 if (!io_fd(entry->sock))
364 panic("io_fd"); 472 panic("io_fd");
365 473
366 io_setcookie(sock, (void *)proto); 474 io_setcookie(entry->sock, (void *)entry->proto);
367 475 io_wantread(entry->sock);
368 if ((proto == FLAG_UDP) && g_udp_workers) {
369 io_block(sock);
370 udp_init(sock, g_udp_workers);
371 } else
372 io_wantread(sock);
373 476
374#ifdef _DEBUG 477#ifdef _DEBUG
375 fputs(" success.\n", stderr); 478 fputs(" success.\n", stderr);
376#endif 479#endif
480}
481
482/* Bound in the order the entries were given.
483 Note: On macOS SO_REUSEPORT delivers everything to the socket that bound last. */
484static void listen_bind_all(void) {
485 size_t i;
486
487 for (i = 0; i < g_listen_count; ++i)
488 listen_bind_one(g_listen + i);
489}
490
491static void listen_start_udp_workers(void) {
492 size_t i;
377 493
378 return sock; 494 udp_init();
495
496 for (i = 0; i < g_listen_count; ++i) {
497 ot_listen *entry = g_listen + i;
498 int workers = listen_option(entry->workers, g_listen_workers);
499
500 if (entry->proto != FLAG_UDP || workers <= 0)
501 continue;
502
503 io_block(entry->sock);
504 udp_workers_install(entry->sock, (unsigned int)workers);
505 }
379} 506}
380 507
381char *set_config_option(char **option, char *value) { 508char *set_config_option(char **option, char *value) {
@@ -452,7 +579,7 @@ typedef enum {
452 CONFIG_LISTEN_TCP, 579 CONFIG_LISTEN_TCP,
453 CONFIG_LISTEN_UDP, 580 CONFIG_LISTEN_UDP,
454 CONFIG_LISTEN_UDP_WORKERS, 581 CONFIG_LISTEN_UDP_WORKERS,
455 CONFIG_LISTEN_REUSEPORT, 582 CONFIG_LISTEN_SHARED,
456 CONFIG_ACCESS_WHITELIST, 583 CONFIG_ACCESS_WHITELIST,
457 CONFIG_ACCESS_BLACKLIST, 584 CONFIG_ACCESS_BLACKLIST,
458 CONFIG_ACCESS_FIFO_ADD, 585 CONFIG_ACCESS_FIFO_ADD,
@@ -480,7 +607,7 @@ static const config_key config_keys[] = {
480 CONFIG_KEY("listen.tcp", CONFIG_LISTEN_TCP), 607 CONFIG_KEY("listen.tcp", CONFIG_LISTEN_TCP),
481 CONFIG_KEY("listen.udp", CONFIG_LISTEN_UDP), 608 CONFIG_KEY("listen.udp", CONFIG_LISTEN_UDP),
482 CONFIG_KEY("listen.udp.workers", CONFIG_LISTEN_UDP_WORKERS), 609 CONFIG_KEY("listen.udp.workers", CONFIG_LISTEN_UDP_WORKERS),
483 CONFIG_KEY("listen.reuseport", CONFIG_LISTEN_REUSEPORT), 610 CONFIG_KEY("listen.shared", CONFIG_LISTEN_SHARED),
484#ifdef WANT_ACCESSLIST_WHITE 611#ifdef WANT_ACCESSLIST_WHITE
485 CONFIG_KEY("access.whitelist", CONFIG_ACCESS_WHITELIST), 612 CONFIG_KEY("access.whitelist", CONFIG_ACCESS_WHITELIST),
486#elif defined(WANT_ACCESSLIST_BLACK) 613#elif defined(WANT_ACCESSLIST_BLACK)
@@ -537,9 +664,10 @@ int parse_configfile(char *config_filename) {
537 } 664 }
538 665
539 while (fgets(inbuf, sizeof(inbuf), config_file_handle)) { 666 while (fgets(inbuf, sizeof(inbuf), config_file_handle)) {
540 char *p = inbuf, *end; 667 char *p = inbuf, *end;
541 uint64_t value; 668 uint64_t value;
542 uint16_t tmpport = 6969; 669 uint16_t tmpport = 6969;
670 config_kind kind;
543 671
544 /* Skip white spaces */ 672 /* Skip white spaces */
545 while (isspace((unsigned char)*p)) 673 while (isspace((unsigned char)*p))
@@ -555,7 +683,8 @@ int parse_configfile(char *config_filename) {
555 *--end = 0; 683 *--end = 0;
556 684
557 /* Scan for commands */ 685 /* Scan for commands */
558 switch (parse_config_key(&p)) { 686 kind = parse_config_key(&p);
687 switch (kind) {
559 case CONFIG_TRACKER_ROOTDIR: 688 case CONFIG_TRACKER_ROOTDIR:
560 set_config_option(&g_serverdir, p); 689 set_config_option(&g_serverdir, p);
561 break; 690 break;
@@ -563,35 +692,29 @@ int parse_configfile(char *config_filename) {
563 set_config_option(&g_serveruser, p); 692 set_config_option(&g_serveruser, p);
564 break; 693 break;
565 case CONFIG_LISTEN_TCP_UDP: 694 case CONFIG_LISTEN_TCP_UDP:
566 if (!scan_ip6_port(p, tmpip, &tmpport))
567 goto parse_error;
568 ot_try_bind(tmpip, tmpport, FLAG_TCP);
569 ++bound;
570 ot_try_bind(tmpip, tmpport, FLAG_UDP);
571 ++bound;
572 break;
573 case CONFIG_LISTEN_TCP: 695 case CONFIG_LISTEN_TCP:
574 if (!scan_ip6_port(p, tmpip, &tmpport)) 696 case CONFIG_LISTEN_UDP: {
575 goto parse_error; 697 int shared = OT_LISTEN_INHERIT
576 ot_try_bind(tmpip, tmpport, FLAG_TCP); 698 int workers = OT_LISTEN_INHERIT;
577 ++bound; 699 int off = scan_ip6_port(p, tmpip, &tmpport);
578 break; 700
579 case CONFIG_LISTEN_UDP: 701 if (!off || !listen_scan_options(p + off, &shared, &workers))
580 if (!scan_ip6_port(p, tmpip, &tmpport))
581 goto parse_error; 702 goto parse_error;
582 ot_try_bind(tmpip, tmpport, FLAG_UDP); 703 if (kind != CONFIG_LISTEN_UDP)
583 ++bound; 704 listen_append(tmpip, tmpport, FLAG_TCP, shared, OT_LISTEN_INHERIT), ++bound;
705 if (kind != CONFIG_LISTEN_TCP)
706 listen_append(tmpip, tmpport, FLAG_UDP, shared, workers), ++bound;
584 break; 707 break;
708 }
585 case CONFIG_LISTEN_UDP_WORKERS: 709 case CONFIG_LISTEN_UDP_WORKERS:
586 if (scan_udec(p, &value, UINT_MAX)) 710 if (!scan_udec(p, &value, UINT_MAX))
587 g_udp_workers = (unsigned int)value; 711 goto parse_error;
712 g_listen_workers = (int)value;
588 break; 713 break;
589 case CONFIG_LISTEN_REUSEPORT: 714 case CONFIG_LISTEN_SHARED:
590 /* Like listen.udp.workers this is read when a socket is bound, so it
591 has to come before the listen lines it should apply to. */
592 if (!scan_udec(p, &value, 1)) 715 if (!scan_udec(p, &value, 1))
593 goto parse_error; 716 goto parse_error;
594 g_socket_reuseport = (int)value; 717 g_listen_shared = (int)value;
595 break; 718 break;
596#ifdef WANT_ACCESSLIST_WHITE 719#ifdef WANT_ACCESSLIST_WHITE
597 case CONFIG_ACCESS_WHITELIST: 720 case CONFIG_ACCESS_WHITELIST:
@@ -638,9 +761,14 @@ int parse_configfile(char *config_filename) {
638 break; 761 break;
639 case CONFIG_LIVESYNC_CLUSTER_LISTEN: 762 case CONFIG_LIVESYNC_CLUSTER_LISTEN:
640 tmpport = LIVESYNC_PORT; 763 tmpport = LIVESYNC_PORT;
641 if (!scan_ip6_port(p, tmpip, &tmpport)) 764 {
642 goto parse_error; 765 int shared = OT_LISTEN_INHERIT, dummy = OT_LISTEN_INHERIT;
643 livesync_bind_mcast(tmpip, tmpport); 766 int off = scan_ip6_port(p, tmpip, &tmpport);
767
768 if (!off || !listen_scan_options(p + off, &shared, &dummy))
769 goto parse_error;
770 listen_append(tmpip, tmpport, FLAG_MCA, shared, OT_LISTEN_INHERIT);
771 }
644 break; 772 break;
645#endif 773#endif
646 default: 774 default:
@@ -791,7 +919,7 @@ int main(int argc, char **argv) {
791 usage(argv[0]); 919 usage(argv[0]);
792 exit(1); 920 exit(1);
793 } 921 }
794 ot_try_bind(serverip, (uint16_t)value, FLAG_TCP); 922 listen_append(serverip, (uint16_t)value, FLAG_TCP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
795 bound++; 923 bound++;
796 break; 924 break;
797 case 'P': 925 case 'P':
@@ -799,7 +927,7 @@ int main(int argc, char **argv) {
799 usage(argv[0]); 927 usage(argv[0]);
800 exit(1); 928 exit(1);
801 } 929 }
802 ot_try_bind(serverip, (uint16_t)value, FLAG_UDP); 930 listen_append(serverip, (uint16_t)value, FLAG_UDP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
803 bound++; 931 bound++;
804 break; 932 break;
805#ifdef WANT_SYNC_LIVE 933#ifdef WANT_SYNC_LIVE
@@ -808,7 +936,7 @@ int main(int argc, char **argv) {
808 usage(argv[0]); 936 usage(argv[0]);
809 exit(1); 937 exit(1);
810 } 938 }
811 livesync_bind_mcast(serverip, (uint16_t)value); 939 listen_append(serverip, (uint16_t)value, FLAG_MCA, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
812 break; 940 break;
813#endif 941#endif
814 case 'd': 942 case 'd':
@@ -849,12 +977,13 @@ int main(int argc, char **argv) {
849 } 977 }
850 } 978 }
851 979
852 /* Bind to our default tcp/udp ports */
853 if (!bound) { 980 if (!bound) {
854 ot_try_bind(serverip, 6969, FLAG_TCP); 981 listen_append(serverip, 6969, FLAG_TCP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
855 ot_try_bind(serverip, 6969, FLAG_UDP); 982 listen_append(serverip, 6969, FLAG_UDP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
856 } 983 }
857 984
985 listen_bind_all();
986
858 defaul_signal_handlers(); 987 defaul_signal_handlers();
859 988
860#ifdef WANT_SYSLOGS 989#ifdef WANT_SYSLOGS
@@ -893,8 +1022,7 @@ int main(int argc, char **argv) {
893 1022
894 install_signal_handlers(); 1023 install_signal_handlers();
895 1024
896 if (!g_udp_workers) 1025 listen_start_udp_workers();
897 udp_init(-1, 0);
898 1026
899 server_mainloop(0); 1027 server_mainloop(0);
900 1028
diff --git a/opentracker.conf.sample b/opentracker.conf.sample
index 054e405..4adb736 100644
--- a/opentracker.conf.sample
+++ b/opentracker.conf.sample
@@ -7,14 +7,8 @@
7# If no listen option is given (here or on the command line), opentracker 7# If no listen option is given (here or on the command line), opentracker
8# listens on 0.0.0.0:6969 tcp and udp. 8# listens on 0.0.0.0:6969 tcp and udp.
9# 9#
10# The next variable determines if udp sockets are handled in the event 10# Addresses are collected while this file is read and bound afterwards,
11# loop (set it to 0, the default) or are handled in blocking reads in 11# so the order of the statements below does not matter.
12# dedicated worker threads. You have to set this value before the
13# listen.tcp_udp or listen.udp statements before it takes effect, but you
14# can re-set it for each listen statement. Normally you should keep it at
15# the top of the config file.
16#
17# listen.udp.workers 4
18# 12#
19# listen.tcp_udp 0.0.0.0 13# listen.tcp_udp 0.0.0.0
20# listen.tcp_udp 192.168.0.1:80 14# listen.tcp_udp 192.168.0.1:80
@@ -25,6 +19,24 @@
25# listen.tcp 0.0.0.0 19# listen.tcp 0.0.0.0
26# listen.udp 192.168.0.1:6969 20# listen.udp 192.168.0.1:6969
27# 21#
22# A listen statement takes options after the address:
23#
24# workers=<count> handle this udp socket in blocking reads in that
25# many dedicated worker threads instead of in the
26# event loop. Ignored for tcp.
27# shared allow another process to bind the same address and
28# let the kernel share the traffic. Only useful if you
29# deliberately run a second tracker there.
30#
31# listen.udp 10.0.0.5:6969 workers=4 shared
32#
33# The two statements below set defaults for every listen statement in
34# this file that does not name the option itself. They may appear
35# anywhere.
36#
37# listen.udp.workers 4
38# listen.shared 1
39#
28# Note, that using 0.0.0.0 for udp sockets may yield surprising results. 40# Note, that using 0.0.0.0 for udp sockets may yield surprising results.
29# An answer packet sent on that socket will not necessarily have the 41# An answer packet sent on that socket will not necessarily have the
30# source address that the requesting client may expect, but any address 42# source address that the requesting client may expect, but any address
diff --git a/ot_socket.c b/ot_socket.c
index e26ad52..84020ca 100644
--- a/ot_socket.c
+++ b/ot_socket.c
@@ -15,8 +15,6 @@
15#include "ot_ip.h" 15#include "ot_ip.h"
16#include "ot_socket.h" 16#include "ot_socket.h"
17 17
18int g_socket_reuseport;
19
20static void sockaddr_from_ip(struct sockaddr_in6 *sa, const ot_ip6 ip, uint16_t port) { 18static void sockaddr_from_ip(struct sockaddr_in6 *sa, const ot_ip6 ip, uint16_t port) {
21 memset(sa, 0, sizeof(*sa)); 19 memset(sa, 0, sizeof(*sa));
22 sa->sin6_family = AF_INET6; 20 sa->sin6_family = AF_INET6;
@@ -75,37 +73,43 @@ static int socket_v6(int type) {
75 return s; 73 return s;
76} 74}
77 75
78static int socket_bind_v6(int s, const ot_ip6 ip, uint16_t port) { 76/* The only place in the tree that needs to know what a kernel calls this */
79 struct sockaddr_in6 sa; 77static void socket_set_sharing(int s, socket_sharing sharing) {
80 int one = 1; 78 int one = 1;
81 79
82 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); 80 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
83#ifdef SO_REUSEPORT 81#ifdef SO_REUSEPORT
84 if (g_socket_reuseport) 82 if (sharing == SOCKET_SHARED)
85 setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); 83 setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
86#endif 84#endif
85}
86
87static int socket_bind_v6(int s, const ot_ip6 ip, uint16_t port, socket_sharing sharing) {
88 struct sockaddr_in6 sa;
89
90 socket_set_sharing(s, sharing);
87 91
88 sockaddr_from_ip(&sa, ip, port); 92 sockaddr_from_ip(&sa, ip, port);
89 return bind(s, (struct sockaddr *)&sa, sizeof(sa)); 93 return bind(s, (struct sockaddr *)&sa, sizeof(sa));
90} 94}
91 95
92int socket_tcp_listen(const ot_ip6 ip, uint16_t port) { 96int socket_tcp_listen(const ot_ip6 ip, uint16_t port, socket_sharing sharing) {
93 int s = socket_v6(SOCK_STREAM); 97 int s = socket_v6(SOCK_STREAM);
94 98
95 if (s == -1) 99 if (s == -1)
96 return -1; 100 return -1;
97 if (socket_bind_v6(s, ip, port) == -1 || listen(s, SOMAXCONN) == -1) 101 if (socket_bind_v6(s, ip, port, sharing) == -1 || listen(s, SOMAXCONN) == -1)
98 return close_and_fail(s); 102 return close_and_fail(s);
99 103
100 return s; 104 return s;
101} 105}
102 106
103int socket_udp_bind(const ot_ip6 ip, uint16_t port) { 107int socket_udp_bind(const ot_ip6 ip, uint16_t port, socket_sharing sharing) {
104 int s = socket_v6(SOCK_DGRAM); 108 int s = socket_v6(SOCK_DGRAM);
105 109
106 if (s == -1) 110 if (s == -1)
107 return -1; 111 return -1;
108 if (socket_bind_v6(s, ip, port) == -1) 112 if (socket_bind_v6(s, ip, port, sharing) == -1)
109 return close_and_fail(s); 113 return close_and_fail(s);
110 114
111 return s; 115 return s;
@@ -117,7 +121,7 @@ int socket_tcp_connect(const ot_ip6 srcip, uint16_t srcport, const ot_ip6 dstip,
117 121
118 if (s == -1) 122 if (s == -1)
119 return -1; 123 return -1;
120 if (socket_bind_v6(s, srcip, srcport) == -1) 124 if (socket_bind_v6(s, srcip, srcport, SOCKET_EXCLUSIVE) == -1)
121 return close_and_fail(s); 125 return close_and_fail(s);
122 126
123 sockaddr_from_ip(&sa, dstip, dstport); 127 sockaddr_from_ip(&sa, dstip, dstport);
@@ -187,18 +191,16 @@ static void sockaddr4_from_ip(struct sockaddr_in *sa, const ot_ip6 ip, uint16_t
187 memcpy(&sa->sin_addr, ip + sizeof(ip_v4mapped_prefix), 4); 191 memcpy(&sa->sin_addr, ip + sizeof(ip_v4mapped_prefix), 4);
188} 192}
189 193
194/* A multicast group is meant to have several listeners, so these are always
195 shared */
190static int socket_v4_bind(const ot_ip6 ip, uint16_t port) { 196static int socket_v4_bind(const ot_ip6 ip, uint16_t port) {
191 struct sockaddr_in sa; 197 struct sockaddr_in sa;
192 int s, one = 1; 198 int s;
193 199
194 if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1) 200 if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
195 return -1; 201 return -1;
196 202
197 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); 203 socket_set_sharing(s, SOCKET_SHARED);
198#ifdef SO_REUSEPORT
199 if (g_socket_reuseport)
200 setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
201#endif
202 204
203 sockaddr4_from_ip(&sa, ip, port); 205 sockaddr4_from_ip(&sa, ip, port);
204 if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) == -1) 206 if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) == -1)
diff --git a/ot_socket.h b/ot_socket.h
index 20fd839..21d04b1 100644
--- a/ot_socket.h
+++ b/ot_socket.h
@@ -15,13 +15,14 @@
15 cleared, so one descriptor serves both families and an IPv4 peer arrives 15 cleared, so one descriptor serves both families and an IPv4 peer arrives
16 as the v4 mapped address */ 16 as the v4 mapped address */
17 17
18/* Ask the kernel to let several processes bind the same address */ 18/* How an address may be shared with other sockets. Which socket option says
19extern int g_socket_reuseport; 19 so differs between kernels, which is socket_set_sharing's business */
20typedef enum { SOCKET_EXCLUSIVE, SOCKET_SHARED } socket_sharing;
20 21
21/* Create, bind and, for TCP, listen. The returned socket is non blocking 22/* Create, bind and, for TCP, listen. The returned socket is non blocking
22 Returns -1 on failure with errno set */ 23 Returns -1 on failure with errno set */
23int socket_tcp_listen(const ot_ip6 ip, uint16_t port); 24int socket_tcp_listen(const ot_ip6 ip, uint16_t port, socket_sharing sharing);
24int socket_udp_bind(const ot_ip6 ip, uint16_t port); 25int socket_udp_bind(const ot_ip6 ip, uint16_t port, socket_sharing sharing);
25 26
26int socket_tcp_connect(const ot_ip6 srcip, uint16_t srcport, const ot_ip6 dstip, uint16_t dstport); 27int socket_tcp_connect(const ot_ip6 srcip, uint16_t srcport, const ot_ip6 dstip, uint16_t dstport);
27int socket_is_connected(int s); 28int socket_is_connected(int s);
diff --git a/ot_udp.c b/ot_udp.c
index dc8267c..7f6517f 100644
--- a/ot_udp.c
+++ b/ot_udp.c
@@ -26,9 +26,6 @@ static uint32_t g_rijndael_round_key[44] = {0};
26static volatile uint32_t g_key_of_the_hour[2] = {0}; 26static volatile uint32_t g_key_of_the_hour[2] = {0};
27static volatile ot_time g_hour_of_the_key; 27static volatile ot_time g_hour_of_the_key;
28 28
29static pthread_mutex_t udp_start_mutex = PTHREAD_MUTEX_INITIALIZER;
30static pthread_cond_t udp_start_cond = PTHREAD_COND_INITIALIZER;
31static int g_opentracker_started = 0;
32 29
33static void udp_generate_rijndael_round_key(void) { 30static void udp_generate_rijndael_round_key(void) {
34 uint32_t key[16]; 31 uint32_t key[16];
@@ -211,11 +208,6 @@ static void *udp_worker(void *args) {
211 ws.debugbuf = malloc(G_DEBUGBUF_SIZE); 208 ws.debugbuf = malloc(G_DEBUGBUF_SIZE);
212#endif 209#endif
213 210
214 pthread_mutex_lock(&udp_start_mutex);
215 while(g_opentracker_running && !g_opentracker_started)
216 pthread_cond_wait(&udp_start_cond, &udp_start_mutex);
217 pthread_mutex_unlock(&udp_start_mutex);
218
219 while (g_opentracker_running) 211 while (g_opentracker_running)
220 handle_udp6(sock, &ws); 212 handle_udp6(sock, &ws);
221 213
@@ -227,20 +219,18 @@ static void *udp_worker(void *args) {
227 return NULL; 219 return NULL;
228} 220}
229 221
230void udp_init(int64_t sock, unsigned int worker_count) { 222void udp_init(void) {
231 pthread_t thread_id;
232 if (!g_rijndael_round_key[0]) 223 if (!g_rijndael_round_key[0])
233 udp_generate_rijndael_round_key(); 224 udp_generate_rijndael_round_key();
225}
226
227void udp_workers_install(int64_t sock, unsigned int worker_count) {
228 pthread_t thread_id;
229
234#ifdef _DEBUG 230#ifdef _DEBUG
235 fprintf(stderr, " installing %d workers on udp socket %ld\n", worker_count, (unsigned long)sock); 231 fprintf(stderr, "Installing %d workers on udp socket %ld\n", worker_count, (unsigned long)sock);
236#endif 232#endif
237 while (worker_count--) 233 while (worker_count--)
238 pthread_create(&thread_id, NULL, udp_worker, (void *)sock); 234 pthread_create(&thread_id, NULL, udp_worker, (void *)sock);
239} 235}
240 236
241void udp_kickoff(void) {
242 pthread_mutex_lock(&udp_start_mutex);
243 g_opentracker_started = 1;
244 pthread_cond_broadcast(&udp_start_cond);
245 pthread_mutex_unlock(&udp_start_mutex);
246}
diff --git a/ot_udp.h b/ot_udp.h
index 87d99a9..a8ab9fc 100644
--- a/ot_udp.h
+++ b/ot_udp.h
@@ -10,8 +10,8 @@
10 10
11#include "trackerlogic.h" 11#include "trackerlogic.h"
12 12
13void udp_init(int64_t sock, unsigned int worker_count); 13void udp_init(void);
14void udp_kickoff(void); 14void udp_workers_install(int64_t sock, unsigned int worker_count);
15int handle_udp6(int64_t serversocket, struct ot_workstruct *ws); 15int handle_udp6(int64_t serversocket, struct ot_workstruct *ws);
16 16
17#endif 17#endif
diff --git a/tests/t_socket.c b/tests/t_socket.c
index bbc9e5e..8eef7fd 100644
--- a/tests/t_socket.c
+++ b/tests/t_socket.c
@@ -91,14 +91,14 @@ static void case_socket_options(void) {
91 int udp, tcp; 91 int udp, tcp;
92 92
93 test_case("a bound udp socket is non blocking and serves both families"); 93 test_case("a bound udp socket is non blocking and serves both families");
94 udp = socket_udp_bind(ip_any, 0); 94 udp = socket_udp_bind(ip_any, 0, SOCKET_EXCLUSIVE);
95 CHECK(udp != -1); 95 CHECK(udp != -1);
96 CHECK_INT(is_nonblocking(udp), 1); 96 CHECK_INT(is_nonblocking(udp), 1);
97 CHECK_INT(sock_flag(udp, IPPROTO_IPV6, IPV6_V6ONLY), 0); 97 CHECK_INT(sock_flag(udp, IPPROTO_IPV6, IPV6_V6ONLY), 0);
98 close(udp); 98 close(udp);
99 99
100 test_case("a listening tcp socket is non blocking and serves both families"); 100 test_case("a listening tcp socket is non blocking and serves both families");
101 tcp = socket_tcp_listen(ip_any, 0); 101 tcp = socket_tcp_listen(ip_any, 0, SOCKET_EXCLUSIVE);
102 CHECK(tcp != -1); 102 CHECK(tcp != -1);
103 CHECK_INT(is_nonblocking(tcp), 1); 103 CHECK_INT(is_nonblocking(tcp), 1);
104 CHECK_INT(sock_flag(tcp, IPPROTO_IPV6, IPV6_V6ONLY), 0); 104 CHECK_INT(sock_flag(tcp, IPPROTO_IPV6, IPV6_V6ONLY), 0);
@@ -106,24 +106,20 @@ static void case_socket_options(void) {
106} 106}
107 107
108#ifdef SO_REUSEPORT 108#ifdef SO_REUSEPORT
109static void case_reuseport_is_opt_in(void) { 109static void case_sharing_is_opt_in(void) {
110 int s; 110 int s;
111 111
112 test_case("reuseport stays off unless the operator asks for it"); 112 test_case("an address is exclusive unless sharing is asked for");
113 g_socket_reuseport = 0; 113 s = socket_udp_bind(ip_loopback, 0, SOCKET_EXCLUSIVE);
114 s = socket_udp_bind(ip_loopback, 0);
115 CHECK(s != -1); 114 CHECK(s != -1);
116 CHECK_INT(sock_flag(s, SOL_SOCKET, SO_REUSEPORT), 0); 115 CHECK_INT(sock_flag(s, SOL_SOCKET, SO_REUSEPORT), 0);
117 close(s); 116 close(s);
118 117
119 test_case("reuseport reaches the socket when it is asked for"); 118 test_case("and shared when it is");
120 g_socket_reuseport = 1; 119 s = socket_udp_bind(ip_loopback, 0, SOCKET_SHARED);
121 s = socket_udp_bind(ip_loopback, 0);
122 CHECK(s != -1); 120 CHECK(s != -1);
123 CHECK_INT(sock_flag(s, SOL_SOCKET, SO_REUSEPORT), 1); 121 CHECK_INT(sock_flag(s, SOL_SOCKET, SO_REUSEPORT), 1);
124 close(s); 122 close(s);
125
126 g_socket_reuseport = 0;
127} 123}
128#endif 124#endif
129 125
@@ -135,7 +131,7 @@ static void case_udp_roundtrip(void) {
135 int s; 131 int s;
136 132
137 test_case("a datagram comes back with the sender's address and port"); 133 test_case("a datagram comes back with the sender's address and port");
138 s = socket_udp_bind(ip_loopback, 0); 134 s = socket_udp_bind(ip_loopback, 0, SOCKET_EXCLUSIVE);
139 CHECK(s != -1); 135 CHECK(s != -1);
140 bound_address(s, own_ip, &own_port); 136 bound_address(s, own_ip, &own_port);
141 CHECK(own_port != 0); 137 CHECK(own_port != 0);
@@ -158,9 +154,9 @@ static void case_udp_v4mapped(void) {
158 int receiver, sender; 154 int receiver, sender;
159 155
160 test_case("an IPv4 sender is reported in its v4 mapped form"); 156 test_case("an IPv4 sender is reported in its v4 mapped form");
161 receiver = socket_udp_bind(ip_v4_loopback, 0); 157 receiver = socket_udp_bind(ip_v4_loopback, 0, SOCKET_EXCLUSIVE);
162 CHECK(receiver != -1); 158 CHECK(receiver != -1);
163 sender = socket_udp_bind(ip_v4_loopback, 0); 159 sender = socket_udp_bind(ip_v4_loopback, 0, SOCKET_EXCLUSIVE);
164 CHECK(sender != -1); 160 CHECK(sender != -1);
165 bound_address(receiver, receiver_ip, &receiver_port); 161 bound_address(receiver, receiver_ip, &receiver_port);
166 bound_address(sender, sender_ip, &sender_port); 162 bound_address(sender, sender_ip, &sender_port);
@@ -184,7 +180,7 @@ static void case_tcp_roundtrip(void) {
184 int listener, client, accepted; 180 int listener, client, accepted;
185 181
186 test_case("a connection is accepted and carries the caller's address"); 182 test_case("a connection is accepted and carries the caller's address");
187 listener = socket_tcp_listen(ip_loopback, 0); 183 listener = socket_tcp_listen(ip_loopback, 0, SOCKET_EXCLUSIVE);
188 CHECK(listener != -1); 184 CHECK(listener != -1);
189 bound_address(listener, listener_ip, &listener_port); 185 bound_address(listener, listener_ip, &listener_port);
190 CHECK(listener_port != 0); 186 CHECK(listener_port != 0);
@@ -216,7 +212,7 @@ static void case_connect_refused(void) {
216 212
217 test_case("a refused connection is reported by socket_is_connected"); 213 test_case("a refused connection is reported by socket_is_connected");
218 /* Take a port, then drop it, so nothing is listening on it any more */ 214 /* Take a port, then drop it, so nothing is listening on it any more */
219 listener = socket_tcp_listen(ip_loopback, 0); 215 listener = socket_tcp_listen(ip_loopback, 0, SOCKET_EXCLUSIVE);
220 CHECK(listener != -1); 216 CHECK(listener != -1);
221 bound_address(listener, listener_ip, &port); 217 bound_address(listener, listener_ip, &port);
222 close(listener); 218 close(listener);
@@ -247,8 +243,8 @@ static void case_bind_failure(void) {
247 test_case("binding an address no interface carries fails"); 243 test_case("binding an address no interface carries fails");
248 before = next_descriptor(); 244 before = next_descriptor();
249 CHECK(before != -1); 245 CHECK(before != -1);
250 CHECK_INT(socket_tcp_listen(ip_unassigned, 0), -1); 246 CHECK_INT(socket_tcp_listen(ip_unassigned, 0, SOCKET_EXCLUSIVE), -1);
251 CHECK_INT(socket_udp_bind(ip_unassigned, 0), -1); 247 CHECK_INT(socket_udp_bind(ip_unassigned, 0, SOCKET_EXCLUSIVE), -1);
252 248
253 test_case("a failed bind leaves no socket behind"); 249 test_case("a failed bind leaves no socket behind");
254 CHECK_INT(next_descriptor(), before); 250 CHECK_INT(next_descriptor(), before);
@@ -294,7 +290,7 @@ static void case_mcast_sender(void) {
294void test_socket(void) { 290void test_socket(void) {
295 case_socket_options(); 291 case_socket_options();
296#ifdef SO_REUSEPORT 292#ifdef SO_REUSEPORT
297 case_reuseport_is_opt_in(); 293 case_sharing_is_opt_in();
298#endif 294#endif
299 case_udp_roundtrip(); 295 case_udp_roundtrip();
300 case_udp_v4mapped(); 296 case_udp_v4mapped();
diff --git a/trackerlogic.c b/trackerlogic.c
index db1d787..52c8c18 100644
--- a/trackerlogic.c
+++ b/trackerlogic.c
@@ -569,7 +569,6 @@ void trackerlogic_init(void) {
569 accesslist_init(); 569 accesslist_init();
570 livesync_init(); 570 livesync_init();
571 stats_init(); 571 stats_init();
572 udp_kickoff();
573} 572}
574 573
575void trackerlogic_deinit(void) { 574void trackerlogic_deinit(void) {