summaryrefslogtreecommitdiff
path: root/src/ot_livesync.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ot_livesync.c')
-rw-r--r--src/ot_livesync.c267
1 files changed, 267 insertions, 0 deletions
diff --git a/src/ot_livesync.c b/src/ot_livesync.c
new file mode 100644
index 0000000..85577f7
--- /dev/null
+++ b/src/ot_livesync.c
@@ -0,0 +1,267 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4/* System */
5#include <arpa/inet.h>
6#include <pthread.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/socket.h>
11#include <sys/types.h>
12#include <unistd.h>
13
14/* Opentracker */
15#include "ot_accesslist.h"
16#include "ot_ip.h"
17#include "ot_socket.h"
18#include "ot_livesync.h"
19#include "ot_mutex.h"
20#include "ot_stats.h"
21#include "trackerlogic.h"
22
23#ifdef WANT_SYNC_LIVE
24
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;
29
30#define LIVESYNC_INCOMING_BUFFSIZE (256 * 256)
31
32#define LIVESYNC_OUTGOING_BUFFSIZE_PEERS 1480
33#define LIVESYNC_OUTGOING_WATERMARK_PEERS (sizeof(ot_peer) + sizeof(ot_hash))
34
35#define LIVESYNC_MAXDELAY 15 /* seconds */
36
37enum { OT_SYNC_PEER4, OT_SYNC_PEER6 };
38
39/* Forward declaration */
40static void *livesync_worker(void *args);
41
42/* For incoming packets */
43static int g_socket_in = -1;
44
45/* For outgoing packets */
46static int g_socket_out = -1;
47
48static pthread_mutex_t g_outbuf_mutex = PTHREAD_MUTEX_INITIALIZER;
49typedef struct {
50 uint8_t data[LIVESYNC_OUTGOING_BUFFSIZE_PEERS];
51 size_t fill;
52 ot_time next_packet_time;
53} sync_buffer;
54
55static sync_buffer g_v6_buf;
56static sync_buffer g_v4_buf;
57
58static pthread_t thread_id;
59static int thread_started;
60static _Atomic int thread_left;
61void livesync_init(void) {
62 uint32_t packet_type6 = htonl(OT_SYNC_PEER6);
63 uint32_t packet_type4 = htonl(OT_SYNC_PEER4);
64
65 if (g_socket_in == -1)
66 return;
67
68 /* Prepare outgoing peers buffer */
69 memcpy(g_v6_buf.data, &g_tracker_id, sizeof(g_tracker_id));
70 memcpy(g_v4_buf.data, &g_tracker_id, sizeof(g_tracker_id));
71
72 memcpy(g_v6_buf.data + sizeof(g_tracker_id), &packet_type6, sizeof(packet_type6));
73 memcpy(g_v4_buf.data + sizeof(g_tracker_id), &packet_type4, sizeof(packet_type4));
74
75 g_v6_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t);
76 g_v4_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t);
77
78 g_v6_buf.next_packet_time = time_future_seconds(LIVESYNC_MAXDELAY);
79 g_v4_buf.next_packet_time = time_future_seconds(LIVESYNC_MAXDELAY);
80
81 thread_started = !pthread_create(&thread_id, NULL, livesync_worker, NULL);
82}
83
84void livesync_deinit(void) {
85 if (thread_started) {
86 /* Repeats so it interrupts a read already under way */
87 while (!atomic_load(&thread_left)) {
88 pthread_kill(thread_id, OT_SIGNAL_WAKE);
89 usleep(1000);
90 }
91 pthread_join(thread_id, NULL);
92 thread_started = 0;
93 }
94
95 if (g_socket_in != -1)
96 close(g_socket_in);
97 if (g_socket_out != -1)
98 close(g_socket_out);
99}
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
109void livesync_bind_mcast(ot_ip6 ip, uint16_t port) {
110 if (!ip_is_v4mapped(ip))
111 exerr("v6 mcast support not yet available.");
112
113 if (g_socket_in != -1)
114 exerr("Error: Livesync listen ip specified twice.");
115
116 g_port = port;
117
118 if ((g_socket_in = socket_mcast_join(g_group, ip, port)) == -1)
119 exerr("Error: Cant bind live sync incoming socket.");
120
121 if ((g_socket_out = socket_mcast_bind(ip, port, 0)) == -1)
122 exerr("Error: Cant bind live sync outgoing socket.");
123}
124
125/* Caller MUST hold g_outbuf_mutex. Returns with g_outbuf_mutex unlocked */
126static void livesync_issue_peersync(sync_buffer *buf) {
127 char mycopy[LIVESYNC_OUTGOING_BUFFSIZE_PEERS];
128 size_t fill = buf->fill;
129
130 memcpy(mycopy, buf->data, fill);
131 buf->fill = sizeof(g_tracker_id) + sizeof(uint32_t);
132 buf->next_packet_time = time_future_seconds(LIVESYNC_MAXDELAY);
133
134 /* From now this thread has a local copy of the buffer and
135 has modified the protected element */
136 pthread_mutex_unlock(&g_outbuf_mutex);
137
138 socket_mcast_send(g_socket_out, mycopy, fill, g_group, g_port);
139}
140
141static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size) {
142 size_t off = sizeof(g_tracker_id) + sizeof(uint32_t);
143
144 /* Prepare peer record for v4mapped expansion */
145 if (peer_size == OT_PEER_SIZE4)
146 memcpy(&ws->peer, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix));
147
148 /* Now basic sanity checks have been done on the live sync packet
149 We might add more testing and logging. */
150 while ((ssize_t)(off + sizeof(ot_hash) + peer_size) <= ws->request_size) {
151 char *peer_ip = ws->request + off + sizeof(ot_hash);
152 if (peer_size == OT_PEER_SIZE4)
153 memcpy(&ws->peer[0] + sizeof(ip_v4mapped_prefix), peer_ip, peer_size);
154 else
155 memcpy(&ws->peer, peer_ip, peer_size);
156 ws->hash = (ot_hash *)(ws->request + off);
157
158 if (!g_opentracker_running)
159 return;
160
161 if (OT_PEERFLAG(ws->peer) & PEER_FLAG_STOPPED)
162 remove_peer_from_torrent(FLAG_MCA, ws);
163 else
164 add_peer_to_torrent_and_return_peers(FLAG_MCA, ws, /* amount = */ 0);
165
166 off += sizeof(ot_hash) + peer_size;
167 }
168
169 stats_issue_event(ws, EVENT_SYNC, 0, (ws->request_size - sizeof(g_tracker_id) - sizeof(uint32_t)) / ((ssize_t)sizeof(ot_hash) + peer_size));
170}
171
172/* Tickle the live sync module from time to time, so no events get
173 stuck when there's not enough traffic to fill udp packets fast
174 enough */
175void livesync_ticker(void) {
176 if (g_socket_out == -1)
177 return;
178
179 /* livesync_issue_peersync sets g_next_packet_time */
180 pthread_mutex_lock(&g_outbuf_mutex);
181 if (time_now_seconds() > g_v6_buf.next_packet_time && g_v6_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t))
182 livesync_issue_peersync(&g_v6_buf);
183 else
184 pthread_mutex_unlock(&g_outbuf_mutex);
185
186 pthread_mutex_lock(&g_outbuf_mutex);
187 if (time_now_seconds() > g_v4_buf.next_packet_time && g_v4_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t))
188 livesync_issue_peersync(&g_v4_buf);
189 else
190 pthread_mutex_unlock(&g_outbuf_mutex);
191}
192
193/* Inform live sync about whats going on. */
194void livesync_tell(struct ot_workstruct *ws) {
195 size_t peer_size; /* initialized in next line */
196 ot_peer *peer_src;
197 sync_buffer *dest_buf;
198
199 if (g_socket_out == -1)
200 return;
201
202 peer_src = peer_from_peer6(&ws->peer, &peer_size);
203 dest_buf = peer_size == OT_PEER_SIZE6 ? &g_v6_buf : &g_v4_buf;
204
205 pthread_mutex_lock(&g_outbuf_mutex);
206
207 memcpy(dest_buf->data + dest_buf->fill, ws->hash, sizeof(ot_hash));
208 dest_buf->fill += sizeof(ot_hash);
209
210 memcpy(dest_buf->data + dest_buf->fill, peer_src, peer_size);
211 dest_buf->fill += peer_size;
212
213 if (dest_buf->fill >= LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS)
214 livesync_issue_peersync(dest_buf);
215 else
216 pthread_mutex_unlock(&g_outbuf_mutex);
217}
218
219static void *livesync_worker(void *args) {
220 struct ot_workstruct ws;
221 ot_ip6 in_ip;
222 uint16_t in_port;
223
224 (void)args;
225
226 if (workstruct_init(&ws, LIVESYNC_INCOMING_BUFFSIZE) == -1) {
227 fprintf(stderr, "Warning: live sync could not get its buffers and will not run.\n");
228 atomic_store(&thread_left, 1);
229 return NULL;
230 }
231 ws.request = ws.inbuf;
232
233 while (g_opentracker_running) {
234 uint32_t packet_type;
235
236 ws.request_size = socket_recv(g_socket_in, ws.inbuf, LIVESYNC_INCOMING_BUFFSIZE, in_ip, &in_port);
237
238 /* Expect at least tracker id and packet type */
239 if (ws.request_size <= (ssize_t)(sizeof(g_tracker_id) + sizeof(uint32_t)))
240 continue;
241 if (!accesslist_is_blessed(in_ip, OT_PERMISSION_MAY_LIVESYNC))
242 continue;
243 if (!memcmp(ws.inbuf, &g_tracker_id, sizeof(g_tracker_id))) {
244 /* TODO: log packet coming from ourselves */
245 continue;
246 }
247
248 memcpy(&packet_type, ws.inbuf + sizeof(g_tracker_id), sizeof(packet_type));
249
250 switch (ntohl(packet_type)) {
251 case OT_SYNC_PEER6:
252 livesync_handle_peersync(&ws, OT_PEER_SIZE6);
253 break;
254 case OT_SYNC_PEER4:
255 livesync_handle_peersync(&ws, OT_PEER_SIZE4);
256 break;
257 default:
258 break;
259 }
260 }
261
262 workstruct_deinit(&ws);
263 atomic_store(&thread_left, 1);
264 return NULL;
265}
266
267#endif