summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man5/opentracker.conf.513
-rw-r--r--opentracker.conf.sample20
-rw-r--r--src/ot_config.c6
-rw-r--r--src/ot_livesync.c19
-rw-r--r--src/ot_livesync.h7
-rw-r--r--tests/integration/harness.py4
-rw-r--r--tests/integration/test_livesync.py42
7 files changed, 99 insertions, 12 deletions
diff --git a/man5/opentracker.conf.5 b/man5/opentracker.conf.5
index 67e624f..9a836ae 100644
--- a/man5/opentracker.conf.5
+++ b/man5/opentracker.conf.5
@@ -79,7 +79,18 @@ Specifies the path to the stats location. You can configure opentracker to appea
79Specifies the IP address or network of the reverse proxies. Opentracker will take the X-Forwarded-For address instead of the source IP address. Can be added more than once. 79Specifies the IP address or network of the reverse proxies. Opentracker will take the X-Forwarded-For address instead of the source IP address. Can be added more than once.
80.Pp 80.Pp
81.It livesync.cluster.listen Ar ip_address:port 81.It livesync.cluster.listen Ar ip_address:port
82Specifies the IP address and port opentracker will listen on for incoming live sync packets to keep a cluster of opentrackers synchronized. 82Specifies the IP address and port opentracker will listen on for incoming live
83sync packets to keep a cluster of opentrackers synchronized. Every member of a
84cluster has to agree on the port, which is also the one packets are sent to.
85.Pp
86.It livesync.cluster.group Ar ip_address
87Specifies the multicast group a cluster talks in. For reaosns of backwards
88comaptibility with legacy trackers, it defaults to 224.0.23.5, which,
89technically sits in a range IANA assigns on request.
90
91A cluster of its own is better served by an address out of 239.192.0.0/14,
92which RFC 2365 sets aside for private use. Packets carry a ttl of 1 either
93way and never leave the local link.
83.Pp 94.Pp
84.It livesync.cluster.node_ip Ar ip_address 95.It livesync.cluster.node_ip Ar ip_address
85Specifies one trusted IP address for sync between trackers running in a cluster. Can be added more than once. 96Specifies one trusted IP address for sync between trackers running in a cluster. Can be added more than once.
diff --git a/opentracker.conf.sample b/opentracker.conf.sample
index 491c88a..7b35369 100644
--- a/opentracker.conf.sample
+++ b/opentracker.conf.sample
@@ -138,12 +138,22 @@
138# livesync.cluster.listen 192.168.0.1:9696 138# livesync.cluster.listen 192.168.0.1:9696
139# 139#
140# Note that two udp sockets will be opened. One on ip address 0.0.0.0 140# Note that two udp sockets will be opened. One on ip address 0.0.0.0
141# port 9696, that will join the multicast group 224.0.42.23 for incoming 141# port 9696, that will join the multicast group for incoming udp packets
142# udp packets and one on ip address 192.168.0.1 port 9696 for outgoing 142# and one on ip address 192.168.0.1 port 9696 for outgoing ones. Every
143# udp packets. 143# member of a cluster has to agree on the port as well as the group.
144#
145# The group a cluster talks in.
146#
147# livesync.cluster.group 239.192.23.5
148#
149# For reaosns of backwards comaptibility with legacy trackers, it
150# defaults to 224.0.23.5, which, technically sits in a range IANA
151# assigns on request.
152#
153# A cluster of your own is better served by an address out of
154# 239.192.0.0/14, which RFC 2365 sets aside for private use. Packets
155# carry a ttl of 1 either way, so they never leave the local link.
144# 156#
145# As of now one and only one ip address must be given, if opentracker
146# was built with the WANT_SYNC_LIVE feature.
147# 157#
148 158
149# IV) Sync between trackers running in a cluster is restricted to packets 159# IV) Sync between trackers running in a cluster is restricted to packets
diff --git a/src/ot_config.c b/src/ot_config.c
index 95136b5..4064d1b 100644
--- a/src/ot_config.c
+++ b/src/ot_config.c
@@ -208,6 +208,7 @@ typedef enum {
208 CONFIG_TRACKER_REDIRECT_URL, 208 CONFIG_TRACKER_REDIRECT_URL,
209 CONFIG_LIVESYNC_CLUSTER_NODE_IP, 209 CONFIG_LIVESYNC_CLUSTER_NODE_IP,
210 CONFIG_LIVESYNC_CLUSTER_LISTEN, 210 CONFIG_LIVESYNC_CLUSTER_LISTEN,
211 CONFIG_LIVESYNC_CLUSTER_GROUP,
211} config_kind; 212} config_kind;
212 213
213typedef struct { 214typedef struct {
@@ -247,6 +248,7 @@ static const config_key config_keys[] = {
247#ifdef WANT_SYNC_LIVE 248#ifdef WANT_SYNC_LIVE
248 CONFIG_KEY("livesync.cluster.node_ip", CONFIG_LIVESYNC_CLUSTER_NODE_IP), 249 CONFIG_KEY("livesync.cluster.node_ip", CONFIG_LIVESYNC_CLUSTER_NODE_IP),
249 CONFIG_KEY("livesync.cluster.listen", CONFIG_LIVESYNC_CLUSTER_LISTEN), 250 CONFIG_KEY("livesync.cluster.listen", CONFIG_LIVESYNC_CLUSTER_LISTEN),
251 CONFIG_KEY("livesync.cluster.group", CONFIG_LIVESYNC_CLUSTER_GROUP),
250#endif 252#endif
251}; 253};
252static const size_t config_entry_count = sizeof(config_keys) / sizeof(config_keys[0]); 254static const size_t config_entry_count = sizeof(config_keys) / sizeof(config_keys[0]);
@@ -378,6 +380,10 @@ static int parse_configfile(char *config_filename) {
378 set_config_option(&g_redirecturl, p); 380 set_config_option(&g_redirecturl, p);
379 break; 381 break;
380#ifdef WANT_SYNC_LIVE 382#ifdef WANT_SYNC_LIVE
383 case CONFIG_LIVESYNC_CLUSTER_GROUP:
384 if (!ip_parse(p, tmpip) || livesync_set_group(tmpip) == -1)
385 goto parse_error;
386 break;
381 case CONFIG_LIVESYNC_CLUSTER_NODE_IP: 387 case CONFIG_LIVESYNC_CLUSTER_NODE_IP:
382 if (!scan_ip6_net(p, &tmpnet)) 388 if (!scan_ip6_net(p, &tmpnet))
383 goto parse_error; 389 goto parse_error;
diff --git a/src/ot_livesync.c b/src/ot_livesync.c
index 9770bbc..85577f7 100644
--- a/src/ot_livesync.c
+++ b/src/ot_livesync.c
@@ -22,7 +22,10 @@
22 22
23#ifdef WANT_SYNC_LIVE 23#ifdef WANT_SYNC_LIVE
24 24
25ot_ip6 groupip_1 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 224, 0, 23, 5}; 25/* The multicast group, legacy opentrackers joined by default 224.0.23.5, so it
26 stays the default */
27static ot_ip6 g_group = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 224, 0, 23, 5};
28static uint16_t g_port = LIVESYNC_PORT;
26 29
27#define LIVESYNC_INCOMING_BUFFSIZE (256 * 256) 30#define LIVESYNC_INCOMING_BUFFSIZE (256 * 256)
28 31
@@ -95,6 +98,14 @@ void livesync_deinit(void) {
95 close(g_socket_out); 98 close(g_socket_out);
96} 99}
97 100
101int livesync_set_group(const ot_ip6 group) {
102 /* v4 multicast is 224.0.0.0/4, and the socket layer has nothing else */
103 if (!ip_is_v4mapped(group) || (group[12] & 0xf0) != 0xe0)
104 return -1;
105 memcpy(g_group, group, sizeof(ot_ip6));
106 return 0;
107}
108
98void livesync_bind_mcast(ot_ip6 ip, uint16_t port) { 109void livesync_bind_mcast(ot_ip6 ip, uint16_t port) {
99 if (!ip_is_v4mapped(ip)) 110 if (!ip_is_v4mapped(ip))
100 exerr("v6 mcast support not yet available."); 111 exerr("v6 mcast support not yet available.");
@@ -102,7 +113,9 @@ void livesync_bind_mcast(ot_ip6 ip, uint16_t port) {
102 if (g_socket_in != -1) 113 if (g_socket_in != -1)
103 exerr("Error: Livesync listen ip specified twice."); 114 exerr("Error: Livesync listen ip specified twice.");
104 115
105 if ((g_socket_in = socket_mcast_join(groupip_1, ip, port)) == -1) 116 g_port = port;
117
118 if ((g_socket_in = socket_mcast_join(g_group, ip, port)) == -1)
106 exerr("Error: Cant bind live sync incoming socket."); 119 exerr("Error: Cant bind live sync incoming socket.");
107 120
108 if ((g_socket_out = socket_mcast_bind(ip, port, 0)) == -1) 121 if ((g_socket_out = socket_mcast_bind(ip, port, 0)) == -1)
@@ -122,7 +135,7 @@ static void livesync_issue_peersync(sync_buffer *buf) {
122 has modified the protected element */ 135 has modified the protected element */
123 pthread_mutex_unlock(&g_outbuf_mutex); 136 pthread_mutex_unlock(&g_outbuf_mutex);
124 137
125 socket_mcast_send(g_socket_out, mycopy, fill, groupip_1, LIVESYNC_PORT); 138 socket_mcast_send(g_socket_out, mycopy, fill, g_group, g_port);
126} 139}
127 140
128static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size) { 141static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size) {
diff --git a/src/ot_livesync.h b/src/ot_livesync.h
index 4bd3af0..cf3c64c 100644
--- a/src/ot_livesync.h
+++ b/src/ot_livesync.h
@@ -7,7 +7,8 @@
7#include "trackerlogic.h" 7#include "trackerlogic.h"
8 8
9/* 9/*
10 Syncing is done as udp packets in the multicast domain 224.0.42.5 port 9696 10 Syncing is done as udp packets in the multicast group 224.0.23.5, on the
11 port livesync.cluster.listen names, 9696 by default
11 12
12 Each tracker should join the multicast group and send its live sync packets 13 Each tracker should join the multicast group and send its live sync packets
13 to that group, using a ttl of 1 14 to that group, using a ttl of 1
@@ -47,6 +48,10 @@
47void livesync_init(void); 48void livesync_init(void);
48void livesync_deinit(void); 49void livesync_deinit(void);
49 50
51/* The group to join and to send to, (defaults to 224.0.23.5).
52 Returns -1 if group is not a v4 multicast address. */
53int livesync_set_group(const ot_ip6 group);
54
50/* Join multicast group for listening and create sending socket */ 55/* Join multicast group for listening and create sending socket */
51void livesync_bind_mcast(ot_ip6 ip, uint16_t port); 56void livesync_bind_mcast(ot_ip6 ip, uint16_t port);
52 57
diff --git a/tests/integration/harness.py b/tests/integration/harness.py
index a9d38ca..10aa6de 100644
--- a/tests/integration/harness.py
+++ b/tests/integration/harness.py
@@ -192,7 +192,7 @@ class Tracker:
192 def torrent_count(self): 192 def torrent_count(self):
193 return int(self.stats("torr").split()[0]) 193 return int(self.stats("torr").split()[0])
194 194
195 def livesync_send(self, payload): 195 def livesync_send(self, payload, group=LIVESYNC_GROUP):
196 """Put a packet on the live sync group. 196 """Put a packet on the live sync group.
197 197
198 It has to go to the group rather than straight at the port: the 198 It has to go to the group rather than straight at the port: the
@@ -204,7 +204,7 @@ class Tracker:
204 sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton("127.0.0.1")) 204 sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton("127.0.0.1"))
205 sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1) 205 sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
206 sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1) 206 sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1)
207 sock.sendto(payload, (LIVESYNC_GROUP, self.livesync_port)) 207 sock.sendto(payload, (group, self.livesync_port))
208 finally: 208 finally:
209 sock.close() 209 sock.close()
210 210
diff --git a/tests/integration/test_livesync.py b/tests/integration/test_livesync.py
index 3a9ee58..52e871f 100644
--- a/tests/integration/test_livesync.py
+++ b/tests/integration/test_livesync.py
@@ -164,3 +164,45 @@ class LiveSyncUnconfigured(TrackerTestCase):
164 for i in range(200): 164 for i in range(200):
165 self.tracker.announce(self.hash_for("no-cluster-%d" % i), port=6881 + i, left=0) 165 self.tracker.announce(self.hash_for("no-cluster-%d" % i), port=6881 + i, left=0)
166 self.assertEqual(self.tracker.torrent_count(), 200) 166 self.assertEqual(self.tracker.torrent_count(), 200)
167
168
169# RFC 2365 sets 239.192.0.0/14 aside for exactly this kind of use
170OWN_GROUP = "239.192.0.7"
171
172
173@unittest.skipUnless(has_livesync(), "this build has no WANT_SYNC_LIVE")
174class LiveSyncGroup(TrackerTestCase):
175 config = ("livesync.cluster.group %s\n"
176 "livesync.cluster.listen 127.0.0.1:{livesync_port}\n"
177 "livesync.cluster.node_ip 127.0.0.1\n" % OWN_GROUP)
178
179 def test_the_configured_group_is_joined(self):
180 info_hash = self.hash_for("own-group")
181 self.tracker.livesync_send(packet(SYNC_PEER4, [(info_hash, peer4("10.1.3.1", 6881, FLAG_SEEDING))]),
182 group=OWN_GROUP)
183 self.assertTrue(self.tracker.wait_until(lambda: self.tracker.torrent_count() == 1))
184
185 def test_the_default_group_is_left_alone(self):
186 """A cluster that picked its own group should not hear the one every
187 other opentracker is on."""
188 info_hash = self.hash_for("not-our-group")
189 self.tracker.livesync_send(packet(SYNC_PEER4, [(info_hash, peer4("10.1.3.2", 6881, FLAG_SEEDING))]))
190 self.assertFalse(self.tracker.wait_until(lambda: self.tracker.torrent_count() != 0, timeout=1))
191
192
193@unittest.skipUnless(has_livesync(), "this build has no WANT_SYNC_LIVE")
194class LiveSyncGroupRefused(TrackerTestCase):
195 """A unicast address is not a group. The line is refused like any other
196 bad config line, which leaves the default standing."""
197
198 config = ("livesync.cluster.group 10.0.0.1\n"
199 "livesync.cluster.listen 127.0.0.1:{livesync_port}\n"
200 "livesync.cluster.node_ip 127.0.0.1\n")
201
202 def test_the_line_is_refused(self):
203 self.assertIn("Parse error", self.tracker.output())
204
205 def test_and_the_default_group_still_works(self):
206 info_hash = self.hash_for("fallback-group")
207 self.tracker.livesync_send(packet(SYNC_PEER4, [(info_hash, peer4("10.1.3.3", 6881, FLAG_SEEDING))]))
208 self.assertTrue(self.tracker.wait_until(lambda: self.tracker.torrent_count() == 1))