diff options
Diffstat (limited to 'src/ot_livesync.c')
| -rw-r--r-- | src/ot_livesync.c | 241 |
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 | |||
| 24 | ot_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 | |||
| 33 | enum { OT_SYNC_PEER4, OT_SYNC_PEER6 }; | ||
| 34 | |||
| 35 | /* Forward declaration */ | ||
| 36 | static void *livesync_worker(void *args); | ||
| 37 | |||
| 38 | /* For incoming packets */ | ||
| 39 | static int g_socket_in = -1; | ||
| 40 | |||
| 41 | /* For outgoing packets */ | ||
| 42 | static int g_socket_out = -1; | ||
| 43 | |||
| 44 | static pthread_mutex_t g_outbuf_mutex = PTHREAD_MUTEX_INITIALIZER; | ||
| 45 | typedef struct { | ||
| 46 | uint8_t data[LIVESYNC_OUTGOING_BUFFSIZE_PEERS]; | ||
| 47 | size_t fill; | ||
| 48 | ot_time next_packet_time; | ||
| 49 | } sync_buffer; | ||
| 50 | |||
| 51 | static sync_buffer g_v6_buf; | ||
| 52 | static sync_buffer g_v4_buf; | ||
| 53 | |||
| 54 | static pthread_t thread_id; | ||
| 55 | static int thread_started; | ||
| 56 | static _Atomic int thread_left; | ||
| 57 | void 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 | |||
| 80 | void 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 | |||
| 97 | void 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 */ | ||
| 112 | static 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 | |||
| 127 | static 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 */ | ||
| 161 | void 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. */ | ||
| 177 | void 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 | |||
| 196 | static 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 | ||
