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