You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
opentracker/ot_livesync.c

240 lines
7.1 KiB
C

/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
It is considered beerware. Prost. Skol. Cheers or whatever.
16 years ago
$id$ */
/* System */
5 months ago
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
/* Libowfat */
#include "byte.h"
16 years ago
#include "ip6.h"
5 months ago
#include "ndelay.h"
#include "socket.h"
/* Opentracker */
#include "ot_accesslist.h"
5 months ago
#include "ot_livesync.h"
#include "ot_mutex.h"
5 months ago
#include "ot_stats.h"
#include "trackerlogic.h"
#ifdef WANT_SYNC_LIVE
5 months ago
char groupip_1[4] = {224, 0, 23, 5};
5 months ago
#define LIVESYNC_INCOMING_BUFFSIZE (256 * 256)
5 months ago
#define LIVESYNC_OUTGOING_BUFFSIZE_PEERS 1480
#define LIVESYNC_OUTGOING_WATERMARK_PEERS (sizeof(ot_peer) + sizeof(ot_hash))
5 months ago
#define LIVESYNC_MAXDELAY 15 /* seconds */
enum { OT_SYNC_PEER4, OT_SYNC_PEER6 };
/* Forward declaration */
5 months ago
static void *livesync_worker(void *args);
/* For outgoing packets */
5 months ago
static int64 g_socket_in = -1;
/* For incoming packets */
5 months ago
static int64 g_socket_out = -1;
static pthread_mutex_t g_outbuf_mutex = PTHREAD_MUTEX_INITIALIZER;
typedef struct {
5 months ago
uint8_t data[LIVESYNC_OUTGOING_BUFFSIZE_PEERS];
size_t fill;
ot_time next_packet_time;
} sync_buffer;
static sync_buffer g_v6_buf;
static sync_buffer g_v4_buf;
5 months ago
static pthread_t thread_id;
void livesync_init() {
5 months ago
if (g_socket_in == -1)
exerr("No socket address for live sync specified.");
/* Prepare outgoing peers buffer */
5 months ago
memcpy(g_v6_buf.data, &g_tracker_id, sizeof(g_tracker_id));
memcpy(g_v4_buf.data, &g_tracker_id, sizeof(g_tracker_id));
5 months ago
uint32_pack_big((char *)g_v6_buf.data + sizeof(g_tracker_id), OT_SYNC_PEER6);
uint32_pack_big((char *)g_v4_buf.data + sizeof(g_tracker_id), OT_SYNC_PEER4);
5 months ago
g_v6_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t);
g_v4_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t);
g_v6_buf.next_packet_time = g_now_seconds + LIVESYNC_MAXDELAY;
g_v4_buf.next_packet_time = g_now_seconds + LIVESYNC_MAXDELAY;
5 months ago
pthread_create(&thread_id, NULL, livesync_worker, NULL);
}
16 years ago
void livesync_deinit() {
5 months ago
if (g_socket_in != -1)
close(g_socket_in);
if (g_socket_out != -1)
close(g_socket_out);
5 months ago
pthread_cancel(thread_id);
}
5 months ago
void livesync_bind_mcast(ot_ip6 ip, uint16_t port) {
char tmpip[4] = {0, 0, 0, 0};
16 years ago
char *v4ip;
5 months ago
if (!ip6_isv4mapped(ip))
16 years ago
exerr("v6 mcast support not yet available.");
5 months ago
v4ip = ip + 12;
5 months ago
if (g_socket_in != -1)
exerr("Error: Livesync listen ip specified twice.");
5 months ago
if ((g_socket_in = socket_udp4()) < 0)
exerr("Error: Cant create live sync incoming socket.");
ndelay_off(g_socket_in);
5 months ago
if (socket_bind4_reuse(g_socket_in, tmpip, port) == -1)
exerr("Error: Cant bind live sync incoming socket.");
5 months ago
if (socket_mcjoin4(g_socket_in, groupip_1, v4ip))
exerr("Error: Cant make live sync incoming socket join mcast group.");
5 months ago
if ((g_socket_out = socket_udp4()) < 0)
exerr("Error: Cant create live sync outgoing socket.");
if (socket_bind4_reuse(g_socket_out, v4ip, port) == -1)
exerr("Error: Cant bind live sync outgoing socket.");
socket_mcttl4(g_socket_out, 1);
socket_mcloop4(g_socket_out, 0);
}
/* Caller MUST hold g_outbuf_mutex. Returns with g_outbuf_mutex unlocked */
5 months ago
static void livesync_issue_peersync(sync_buffer *buf) {
char mycopy[LIVESYNC_OUTGOING_BUFFSIZE_PEERS];
size_t fill = buf->fill;
5 months ago
memcpy(mycopy, buf->data, fill);
buf->fill = sizeof(g_tracker_id) + sizeof(uint32_t);
buf->next_packet_time = g_now_seconds + LIVESYNC_MAXDELAY;
/* From now this thread has a local copy of the buffer and
has modified the protected element */
pthread_mutex_unlock(&g_outbuf_mutex);
socket_send4(g_socket_out, mycopy, fill, groupip_1, LIVESYNC_PORT);
}
5 months ago
static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size) {
size_t off = sizeof(g_tracker_id) + sizeof(uint32_t);
/* Now basic sanity checks have been done on the live sync packet
We might add more testing and logging. */
5 months ago
while ((ssize_t)(off + sizeof(ot_hash) + peer_size) <= ws->request_size) {
memcpy(&ws->peer, ws->request + off + sizeof(ot_hash), peer_size);
ws->hash = (ot_hash *)(ws->request + off);
5 months ago
if (!g_opentracker_running)
return;
5 months ago
if (OT_PEERFLAG(ws->peer) & PEER_FLAG_STOPPED)
remove_peer_from_torrent(FLAG_MCA, ws);
else
5 months ago
add_peer_to_torrent_and_return_peers(FLAG_MCA, ws, /* amount = */ 0);
5 months ago
off += sizeof(ot_hash) + peer_size;
}
5 months ago
stats_issue_event(EVENT_SYNC, 0, (ws->request_size - sizeof(g_tracker_id) - sizeof(uint32_t)) / ((ssize_t)sizeof(ot_hash) + peer_size));
}
/* Tickle the live sync module from time to time, so no events get
stuck when there's not enough traffic to fill udp packets fast
enough */
5 months ago
void livesync_ticker() {
/* livesync_issue_peersync sets g_next_packet_time */
pthread_mutex_lock(&g_outbuf_mutex);
5 months ago
if (g_now_seconds > g_v6_buf.next_packet_time && g_v6_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t))
livesync_issue_peersync(&g_v6_buf);
else
pthread_mutex_unlock(&g_outbuf_mutex);
pthread_mutex_lock(&g_outbuf_mutex);
5 months ago
if (g_now_seconds > g_v4_buf.next_packet_time && g_v4_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t))
livesync_issue_peersync(&g_v4_buf);
else
pthread_mutex_unlock(&g_outbuf_mutex);
}
/* Inform live sync about whats going on. */
5 months ago
void livesync_tell(struct ot_workstruct *ws) {
size_t peer_size; /* initialized in next line */
ot_peer *peer_src = peer_from_peer6(&ws->peer, &peer_size);
sync_buffer *dest_buf = peer_size == OT_PEER_SIZE6 ? &g_v6_buf : &g_v4_buf;
pthread_mutex_lock(&g_outbuf_mutex);
5 months ago
memcpy(dest_buf->data + dest_buf->fill, ws->hash, sizeof(ot_hash));
dest_buf->fill += sizeof(ot_hash);
5 months ago
memcpy(dest_buf->data + dest_buf->fill, peer_src, peer_size);
dest_buf->fill += peer_size;
5 months ago
if (dest_buf->fill >= LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS)
livesync_issue_peersync(dest_buf);
else
pthread_mutex_unlock(&g_outbuf_mutex);
}
5 months ago
static void *livesync_worker(void *args) {
** struct ot_workstruct gets ritcher (and will become even ritcher soon). This is where we encapsulate all per-request data from peer to hash to peer_id, so that it is available everywhere without passing hundreds of pointers down the stack. Most functions that do work down the stack now accept an ot_workstruct and some flags. So it can end up in the stats/event-handler where it will be the default parameter in the future. ** peer_id is now being copied by default and moved to ot_workstruct So it is available in stats and subsequent functions. ** sync scrape madness is gone SYNC_SCRAPE was intended to sync tracker state that would normally be lost on restarts i.e. downloaded counts per torrent. The way was to push it in the tracker cloud after finding all neighbouring trackers. This is madness. It never was tested and can be done per tracker by fetching stats/mode=statedump from time to time and starting opentracker with the -l option later. ** livesync thread has its own ot_workstruct now So it can behave like ot_udp and ot_http against trackerlogic.c and get rid of the first half of the embarrassing global variables. The sending half will be fixed soon [tm]. ** stats can log completed events The author recognizes the needs of original content distributors to keep track of the amount of times a work has been downloaded. While not feasible and used on openbittorrent and other open and anonymous tracker installations, a tracker user can now choose to send those events to syslog.
15 years ago
struct ot_workstruct ws;
5 months ago
ot_ip6 in_ip;
uint16_t in_port;
16 years ago
(void)args;
** struct ot_workstruct gets ritcher (and will become even ritcher soon). This is where we encapsulate all per-request data from peer to hash to peer_id, so that it is available everywhere without passing hundreds of pointers down the stack. Most functions that do work down the stack now accept an ot_workstruct and some flags. So it can end up in the stats/event-handler where it will be the default parameter in the future. ** peer_id is now being copied by default and moved to ot_workstruct So it is available in stats and subsequent functions. ** sync scrape madness is gone SYNC_SCRAPE was intended to sync tracker state that would normally be lost on restarts i.e. downloaded counts per torrent. The way was to push it in the tracker cloud after finding all neighbouring trackers. This is madness. It never was tested and can be done per tracker by fetching stats/mode=statedump from time to time and starting opentracker with the -l option later. ** livesync thread has its own ot_workstruct now So it can behave like ot_udp and ot_http against trackerlogic.c and get rid of the first half of the embarrassing global variables. The sending half will be fixed soon [tm]. ** stats can log completed events The author recognizes the needs of original content distributors to keep track of the amount of times a work has been downloaded. While not feasible and used on openbittorrent and other open and anonymous tracker installations, a tracker user can now choose to send those events to syslog.
15 years ago
/* Initialize our "thread local storage" */
5 months ago
ws.inbuf = ws.request = malloc(LIVESYNC_INCOMING_BUFFSIZE);
ws.outbuf = ws.reply = 0;
5 months ago
memcpy(in_ip, V4mappedprefix, sizeof(V4mappedprefix));
5 months ago
while (1) {
ws.request_size = socket_recv4(g_socket_in, (char *)ws.inbuf, LIVESYNC_INCOMING_BUFFSIZE, 12 + (char *)in_ip, &in_port);
/* Expect at least tracker id and packet type */
5 months ago
if (ws.request_size <= (ssize_t)(sizeof(g_tracker_id) + sizeof(uint32_t)))
continue;
5 months ago
if (!accesslist_is_blessed(in_ip, OT_PERMISSION_MAY_LIVESYNC))
continue;
5 months ago
if (!memcmp(ws.inbuf, &g_tracker_id, sizeof(g_tracker_id))) {
/* TODO: log packet coming from ourselves */
continue;
}
5 months ago
switch (uint32_read_big(sizeof(g_tracker_id) + (char *)ws.inbuf)) {
case OT_SYNC_PEER6:
5 months ago
livesync_handle_peersync(&ws, OT_PEER_SIZE6);
break;
case OT_SYNC_PEER4:
5 months ago
livesync_handle_peersync(&ws, OT_PEER_SIZE4);
break;
default:
break;
}
}
/* Never returns. */
return NULL;
}
#endif