diff options
Diffstat (limited to 'src/ot_livesync.c')
| -rw-r--r-- | src/ot_livesync.c | 267 |
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 */ | ||
| 27 | static ot_ip6 g_group = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 224, 0, 23, 5}; | ||
| 28 | static 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 | |||
| 37 | enum { OT_SYNC_PEER4, OT_SYNC_PEER6 }; | ||
| 38 | |||
| 39 | /* Forward declaration */ | ||
| 40 | static void *livesync_worker(void *args); | ||
| 41 | |||
| 42 | /* For incoming packets */ | ||
| 43 | static int g_socket_in = -1; | ||
| 44 | |||
| 45 | /* For outgoing packets */ | ||
| 46 | static int g_socket_out = -1; | ||
| 47 | |||
| 48 | static pthread_mutex_t g_outbuf_mutex = PTHREAD_MUTEX_INITIALIZER; | ||
| 49 | typedef struct { | ||
| 50 | uint8_t data[LIVESYNC_OUTGOING_BUFFSIZE_PEERS]; | ||
| 51 | size_t fill; | ||
| 52 | ot_time next_packet_time; | ||
| 53 | } sync_buffer; | ||
| 54 | |||
| 55 | static sync_buffer g_v6_buf; | ||
| 56 | static sync_buffer g_v4_buf; | ||
| 57 | |||
| 58 | static pthread_t thread_id; | ||
| 59 | static int thread_started; | ||
| 60 | static _Atomic int thread_left; | ||
| 61 | void 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 | |||
| 84 | void 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 | |||
| 101 | int 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 | |||
| 109 | void 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 */ | ||
| 126 | static 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 | |||
| 141 | static 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 */ | ||
| 175 | void 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. */ | ||
| 194 | void 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 | |||
| 219 | static 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 | ||
