Bladeren bron

Find and ban peers with bad data.

Log which peer contributed what to a piece. Do not try to download the
same piece from the same peers. Don't download at all from peers implicated
in 3 bad pieces. When a previously bad piece has been downloaded successfully
the bad peer(s) can be found and banned.
master
Richard Nyberg 16 jaren geleden
bovenliggende
commit
8b04af7e91
10 gewijzigde bestanden met toevoegingen van 285 en 48 verwijderingen
  1. +4
    -0
      btpd/btpd.h
  2. +18
    -19
      btpd/download.c
  3. +4
    -0
      btpd/download.h
  4. +146
    -27
      btpd/download_subr.c
  5. +10
    -0
      btpd/net.c
  6. +1
    -0
      btpd/net.h
  7. +18
    -0
      btpd/net_types.h
  8. +74
    -2
      btpd/peer.c
  9. +8
    -0
      btpd/peer.h
  10. +2
    -0
      btpd/util.c

+ 4
- 0
btpd/btpd.h Bestand weergeven

@@ -58,6 +58,7 @@
#define BTPD_L_MSG 0x00000008
#define BTPD_L_BTPD 0x00000010
#define BTPD_L_POL 0x00000020
#define BTPD_L_BAD 0x00000040

extern long btpd_seconds;

@@ -90,6 +91,9 @@ uint32_t btpd_id_hash(const void *k);

const uint8_t *btpd_get_peer_id(void);

int btpd_id_eq(const void *id1, const void *id2);
uint32_t btpd_id_hash(const void *id);

void td_acquire_lock(void);
void td_release_lock(void);



+ 18
- 19
btpd/download.c Bestand weergeven

@@ -17,18 +17,17 @@ dl_on_piece_ann(struct peer *p, uint32_t index)
if (n->endgame) {
assert(pc != NULL);
peer_want(p, index);
if (!peer_chokes(p) && !peer_laden(p))
if (peer_leech_ok(p))
dl_assign_requests_eg(p);
} else if (pc == NULL) {
peer_want(p, index);
if (!peer_chokes(p) && !peer_laden(p)) {
if (peer_leech_ok(p)) {
pc = dl_new_piece(n, index);
if (pc != NULL)
dl_piece_assign_requests(pc, p);
dl_piece_assign_requests(pc, p);
}
} else if (!piece_full(pc)) {
peer_want(p, index);
if (!peer_chokes(p) && !peer_laden(p))
if (peer_leech_ok(p))
dl_piece_assign_requests(pc, p);
}
}
@@ -36,21 +35,17 @@ dl_on_piece_ann(struct peer *p, uint32_t index)
void
dl_on_download(struct peer *p)
{
assert(peer_wanted(p));
struct net *n = p->n;
if (n->endgame) {
if (n->endgame)
dl_assign_requests_eg(p);
} else {
unsigned count = dl_assign_requests(p);
if (count == 0 && !p->n->endgame) // We may have entered end game.
assert(!peer_wanted(p) || peer_laden(p));
}
else
dl_assign_requests(p);
}

void
dl_on_unchoke(struct peer *p)
{
if (peer_wanted(p))
if (peer_leech_ok(p))
dl_on_download(p);
}

@@ -90,8 +85,9 @@ dl_on_ok_piece(struct net *n, uint32_t piece)

if (n->endgame)
BTPDQ_FOREACH(p, &n->peers, p_entry)
if (peer_has(p, pc->index))
peer_unwant(p, pc->index);
peer_unwant(p, pc->index);

piece_log_good(pc);

assert(pc->nreqs == 0);
piece_free(pc);
@@ -114,14 +110,16 @@ dl_on_bad_piece(struct net *n, uint32_t piece)
pc->ngot = 0;
pc->nbusy = 0;

piece_log_bad(pc);

if (n->endgame) {
struct peer *p;
BTPDQ_FOREACH(p, &n->peers, p_entry) {
if (peer_has(p, pc->index) && peer_leech_ok(p) && !peer_laden(p))
if (peer_leech_ok(p) && peer_requestable(p, pc->index))
dl_assign_requests_eg(p);
}
} else
dl_on_piece_unfull(pc); // XXX: May get bad data again.
dl_on_piece_unfull(pc);
}

void
@@ -149,6 +147,7 @@ dl_on_block(struct peer *p, struct block_request *req,
struct net *n = p->n;
struct piece *pc = dl_find_piece(n, index);

piece_log_block(pc, p, begin);
cm_put_bytes(p->n->tp, index, begin, data, length);
pc->ngot++;

@@ -170,7 +169,7 @@ dl_on_block(struct peer *p, struct block_request *req,
continue;
BTPDQ_REMOVE(&pc->reqs, req, blk_entry);
nb_drop(req->msg);
if (peer_leech_ok(req->p) && !peer_laden(req->p))
if (peer_leech_ok(req->p))
dl_assign_requests_eg(req->p);
free(req);
}
@@ -186,7 +185,7 @@ dl_on_block(struct peer *p, struct block_request *req,
pc->nbusy--;
if (pc->ngot == pc->nblocks)
cm_test_piece(pc->n->tp, pc->index);
if (peer_leech_ok(p) && !peer_laden(p))
if (peer_leech_ok(p))
dl_assign_requests(p);
}
}

+ 4
- 0
btpd/download.h Bestand weergeven

@@ -6,6 +6,10 @@
int piece_full(struct piece *pc);
void piece_free(struct piece *pc);

void piece_log_bad(struct piece *pc);
void piece_log_good(struct piece *pc);
void piece_log_block(struct piece *pc, struct peer *p, uint32_t begin);

void dl_on_piece_unfull(struct piece *pc);

struct piece *dl_new_piece(struct net *n, uint32_t index);


+ 146
- 27
btpd/download_subr.c Bestand weergeven

@@ -24,6 +24,135 @@
#include <openssl/sha.h>
#include <stream.h>

static void
piece_new_log(struct piece *pc)
{
struct blog *log = btpd_calloc(1, sizeof(*log));
BTPDQ_INIT(&log->records);
BTPDQ_INSERT_HEAD(&pc->logs, log, entry);
}

static void
piece_log_hashes(struct piece *pc)
{
uint8_t *buf;
struct torrent *tp = pc->n->tp;
struct blog *log = BTPDQ_FIRST(&pc->logs);
log->hashes = btpd_malloc(20 * pc->nblocks);
for (unsigned i = 0; i < pc->nblocks; i++) {
uint32_t bsize = torrent_block_size(tp, pc->index, pc->nblocks, i);
cm_get_bytes(tp, pc->index, i * PIECE_BLOCKLEN, bsize, &buf);
SHA1(buf, bsize, &log->hashes[i * 20]);
free(buf);
}
}

static void
piece_log_free(struct piece *pc, struct blog *log)
{
struct blog_record *r, *rnext;
BTPDQ_FOREACH_MUTABLE(r, &log->records, entry, rnext) {
mp_drop(r->mp, pc->n);
free(r);
}
if (log->hashes != NULL)
free(log->hashes);
free(log);
}

static void
piece_kill_logs(struct piece *pc)
{
struct blog *log, *lnext;
BTPDQ_FOREACH_MUTABLE(log, &pc->logs, entry, lnext)
piece_log_free(pc, log);
BTPDQ_INIT(&pc->logs);
}

void
piece_log_bad(struct piece *pc)
{
struct blog *log = BTPDQ_FIRST(&pc->logs);
struct blog_record *r = BTPDQ_FIRST(&log->records);
struct meta_peer *culprit = NULL;

if (r == BTPDQ_LAST(&log->records, blog_record_tq)) {
unsigned i;
for (i = 0; i < pc->nblocks; i++)
if (!has_bit(r->down_field, i))
break;
if (i == pc->nblocks)
culprit = r->mp;
}
if (culprit != NULL) {
if (pc->n->endgame && culprit->p != NULL)
peer_unwant(culprit->p, pc->index);
net_ban_peer(pc->n, culprit);
BTPDQ_REMOVE(&pc->logs, log, entry);
piece_log_free(pc, log);
} else {
BTPDQ_FOREACH(r, &log->records, entry) {
if (r->mp->p != NULL) {
if (pc->n->endgame)
peer_unwant(r->mp->p, pc->index);
peer_bad_piece(r->mp->p, pc->index);
}
}
piece_log_hashes(pc);
}
piece_new_log(pc);
}

void
piece_log_good(struct piece *pc)
{
struct blog_record *r;
struct blog *log = BTPDQ_FIRST(&pc->logs), *bad = BTPDQ_NEXT(log, entry);

BTPDQ_FOREACH(r, &log->records, entry)
if (r->mp->p != NULL)
peer_good_piece(r->mp->p, pc->index);

if (bad != NULL)
piece_log_hashes(pc);

while (bad != NULL) {
BTPDQ_FOREACH(r, &bad->records, entry) {
int culprit = 0;
for (unsigned i = 0; i < pc->nblocks && !culprit; i++)
if (has_bit(r->down_field, i) && (
bcmp(&log->hashes[i*20], &bad->hashes[i*20], 20) != 0))
culprit = 1;
if (culprit)
net_ban_peer(pc->n, r->mp);
else if (r->mp->p != NULL)
peer_good_piece(r->mp->p, pc->index);
}

bad = BTPDQ_NEXT(bad, entry);
}
}

void
piece_log_block(struct piece *pc, struct peer *p, uint32_t begin)
{
struct blog_record *r;
struct blog *log = BTPDQ_FIRST(&pc->logs);
BTPDQ_FOREACH(r, &log->records, entry)
if (r->mp == p->mp)
break;
if (r == NULL) {
r = btpd_calloc(1, sizeof(*r) + ceil(pc->nblocks / 8.0));
r->mp = p->mp;
mp_hold(r->mp);
BTPDQ_INSERT_HEAD(&log->records, r, entry);
} else {
BTPDQ_REMOVE(&log->records, r, entry);
BTPDQ_INSERT_HEAD(&log->records, r, entry);
}
set_bit(r->down_field, begin / PIECE_BLOCKLEN);
}

static struct piece *
piece_alloc(struct net *n, uint32_t index)
{
@@ -54,10 +183,13 @@ piece_alloc(struct net *n, uint32_t index)
assert(pc->ngot < pc->nblocks);

BTPDQ_INIT(&pc->reqs);
BTPDQ_INIT(&pc->logs);

piece_new_log(pc);

n->npcs_busy++;
set_bit(n->busy_field, index);
BTPDQ_INSERT_HEAD(&n->getlst, pc, entry);
BTPDQ_INSERT_TAIL(&n->getlst, pc, entry);
return pc;
}

@@ -74,6 +206,7 @@ piece_free(struct piece *pc)
nb_drop(req->msg);
free(req);
}
piece_kill_logs(pc);
if (pc->eg_reqs != NULL) {
for (uint32_t i = 0; i < pc->nblocks; i++)
if (pc->eg_reqs[i] != NULL)
@@ -170,11 +303,9 @@ dl_enter_endgame(struct net *n)
}
BTPDQ_FOREACH(p, &n->peers, p_entry) {
assert(p->nwant == 0);
BTPDQ_FOREACH(pc, &n->getlst, entry) {
if (peer_has(p, pc->index))
peer_want(p, pc->index);
}
if (p->nwant > 0 && peer_leech_ok(p) && !peer_laden(p))
BTPDQ_FOREACH(pc, &n->getlst, entry)
peer_want(p, pc->index);
if (peer_leech_ok(p))
dl_assign_requests_eg(p);
}
}
@@ -192,7 +323,7 @@ dl_find_piece(struct net *n, uint32_t index)
static int
dl_piece_startable(struct peer *p, uint32_t index)
{
return peer_has(p, index) && !cm_has_piece(p->n->tp, index)
return peer_requestable(p, index) && !cm_has_piece(p->n->tp, index)
&& !has_bit(p->n->busy_field, index);
}

@@ -252,23 +383,12 @@ static void
dl_on_piece_full(struct piece *pc)
{
struct peer *p;
BTPDQ_FOREACH(p, &pc->n->peers, p_entry) {
if (peer_has(p, pc->index))
peer_unwant(p, pc->index);
}
BTPDQ_FOREACH(p, &pc->n->peers, p_entry)
peer_unwant(p, pc->index);
if (dl_should_enter_endgame(pc->n))
dl_enter_endgame(pc->n);
}

/*
* Allocate the piece indicated by the index for download.
* There's a small possibility that a piece is fully downloaded
* but haven't been tested. If such is the case the piece will
* be tested and NULL will be returned. Also, we might then enter
* end game.
*
* Return the piece or NULL.
*/
struct piece *
dl_new_piece(struct net *n, uint32_t index)
{
@@ -291,11 +411,10 @@ dl_on_piece_unfull(struct piece *pc)
struct peer *p;
assert(!piece_full(pc) && n->endgame == 0);
BTPDQ_FOREACH(p, &n->peers, p_entry)
if (peer_has(p, pc->index))
peer_want(p, pc->index);
peer_want(p, pc->index);
p = BTPDQ_FIRST(&n->peers);
while (p != NULL && !piece_full(pc)) {
if (peer_leech_ok(p) && !peer_laden(p))
if (peer_leech_ok(p) && peer_requestable(p, pc->index))
dl_piece_assign_requests(pc, p); // Cannot provoke end game here.
p = BTPDQ_NEXT(p, p_entry);
}
@@ -368,12 +487,12 @@ dl_piece_assign_requests(struct piece *pc, struct peer *p)
unsigned
dl_assign_requests(struct peer *p)
{
assert(!p->n->endgame && !peer_laden(p));
assert(!p->n->endgame && peer_leech_ok(p));
struct piece *pc;
struct net *n = p->n;
unsigned count = 0;
BTPDQ_FOREACH(pc, &n->getlst, entry) {
if (piece_full(pc) || !peer_has(p, pc->index))
if (piece_full(pc) || !peer_requestable(p, pc->index))
continue;
count += dl_piece_assign_requests(pc, p);
if (n->endgame)
@@ -455,7 +574,7 @@ dl_piece_assign_requests_eg(struct piece *pc, struct peer *p)
void
dl_assign_requests_eg(struct peer *p)
{
assert(!peer_laden(p));
assert(peer_leech_ok(p));
struct net *n = p->n;
struct piece_tq tmp;
BTPDQ_INIT(&tmp);
@@ -463,7 +582,7 @@ dl_assign_requests_eg(struct peer *p)
struct piece *pc = BTPDQ_FIRST(&n->getlst);
while (!peer_laden(p) && pc != NULL) {
struct piece *next = BTPDQ_NEXT(pc, entry);
if (peer_has(p, pc->index) && pc->nblocks != pc->ngot) {
if (peer_requestable(p, pc->index) && pc->nblocks != pc->ngot) {
dl_piece_assign_requests_eg(pc, p);
BTPDQ_REMOVE(&n->getlst, pc, entry);
BTPDQ_INSERT_HEAD(&tmp, pc, entry);


+ 10
- 0
btpd/net.c Bestand weergeven

@@ -23,6 +23,16 @@ struct peer_tq net_bw_readq = BTPDQ_HEAD_INITIALIZER(net_bw_readq);
struct peer_tq net_bw_writeq = BTPDQ_HEAD_INITIALIZER(net_bw_writeq);
struct peer_tq net_unattached = BTPDQ_HEAD_INITIALIZER(net_unattached);

void
net_ban_peer(struct net *n, struct meta_peer *mp)
{
if (mp->flags & PF_BANNED)
return;
mp_hold(mp); // Keep the meta peer alive
mp->flags |= PF_BANNED;
btpd_log(BTPD_L_BAD, "banned peer %p.\n", mp);
}

int
net_torrent_has_peer(struct net *n, const uint8_t *id)
{


+ 1
- 0
btpd/net.h Bestand weergeven

@@ -29,6 +29,7 @@ void net_start(struct torrent *tp);
void net_stop(struct torrent *tp);
int net_active(struct torrent *tp);

void net_ban_peer(struct net *n, struct meta_peer *mp);
int net_torrent_has_peer(struct net *n, const uint8_t *id);

void net_io_cb(int sd, short type, void *arg);


+ 18
- 0
btpd/net_types.h Bestand weergeven

@@ -4,6 +4,8 @@
BTPDQ_HEAD(peer_tq, peer);
BTPDQ_HEAD(piece_tq, piece);
BTPDQ_HEAD(block_request_tq, block_request);
BTPDQ_HEAD(blog_tq, blog);
BTPDQ_HEAD(blog_record_tq, blog_record);

struct net {
struct torrent *tp;
@@ -47,8 +49,11 @@ HTBL_TYPE(mptbl, meta_peer, uint8_t, id, chain);
struct peer {
int sd;
uint8_t *piece_field;
uint8_t *bad_field;
uint32_t npieces;
uint32_t nwant;
uint32_t npcs_bad;
int suspicion;

struct net *n;
struct meta_peer *mp;
@@ -102,6 +107,7 @@ struct piece {

struct net_buf **eg_reqs;
struct block_request_tq reqs;
struct blog_tq logs;

const uint8_t *have_field;
uint8_t *down_field;
@@ -109,6 +115,18 @@ struct piece {
BTPDQ_ENTRY(piece) entry;
};

struct blog {
BTPDQ_ENTRY(blog) entry;
struct blog_record_tq records;
uint8_t *hashes;
};

struct blog_record {
BTPDQ_ENTRY(blog_record) entry;
struct meta_peer *mp;
uint8_t down_field[];
};

struct block_request {
struct peer *p;
struct net_buf *msg;


+ 74
- 2
btpd/peer.c Bestand weergeven

@@ -26,6 +26,7 @@ mp_drop(struct meta_peer *mp, struct net *n)
assert(mp->refs > 0);
mp->refs--;
if (mp->refs == 0) {
assert(mp->p == NULL);
if (mp->flags & PF_ATTACHED)
assert(mptbl_remove(n->mptbl, mp->id) == mp);
mp_kill(mp);
@@ -70,6 +71,8 @@ peer_kill(struct peer *p)
free(p->in.buf);
if (p->piece_field != NULL)
free(p->piece_field);
if (p->bad_field != NULL)
free(p->bad_field);
free(p);
net_npeers--;
}
@@ -257,9 +260,14 @@ peer_choke(struct peer *p)
void
peer_want(struct peer *p, uint32_t index)
{
if (!has_bit(p->piece_field, index) || peer_has_bad(p, index))
return;
assert(p->nwant < p->npieces);
p->nwant++;
if (p->nwant == 1) {
p->mp->flags |= PF_I_WANT;
if (p->mp->flags & PF_SUSPECT)
return;
if (p->nreqs_out == 0) {
assert((p->mp->flags & PF_DO_UNWANT) == 0);
int unsent = 0;
@@ -272,17 +280,20 @@ peer_want(struct peer *p, uint32_t index)
assert((p->mp->flags & PF_DO_UNWANT) != 0);
p->mp->flags &= ~PF_DO_UNWANT;
}
p->mp->flags |= PF_I_WANT;
}
}

void
peer_unwant(struct peer *p, uint32_t index)
{
if (!has_bit(p->piece_field, index) || peer_has_bad(p, index))
return;
assert(p->nwant > 0);
p->nwant--;
if (p->nwant == 0) {
p->mp->flags &= ~PF_I_WANT;
if (p->mp->flags & PF_SUSPECT)
return;
p->t_nointerest = btpd_seconds;
if (p->nreqs_out == 0)
peer_send(p, nb_create_uninterest());
@@ -568,6 +579,8 @@ peer_on_cancel(struct peer *p, uint32_t index, uint32_t begin,
void
peer_on_tick(struct peer *p)
{
if (p->mp->flags & PF_BANNED)
goto kill;
if (p->mp->flags & PF_ATTACHED) {
if (BTPDQ_EMPTY(&p->outq)) {
if (btpd_seconds - p->t_lastwrite >= 120)
@@ -590,6 +603,52 @@ kill:
peer_kill(p);
}

void
peer_bad_piece(struct peer *p, uint32_t index)
{
if (p->npcs_bad == 0) {
assert(p->bad_field == NULL);
p->bad_field = btpd_calloc(ceil(p->n->tp->npieces / 8.0), 1);
}
assert(!has_bit(p->bad_field, index));
set_bit(p->bad_field, index);
p->npcs_bad++;
p->suspicion++;
if (p->suspicion == 3) {
btpd_log(BTPD_L_BAD, "suspect peer %p.\n", p);
p->mp->flags |= PF_SUSPECT;
if (p->nwant > 0) {
p->mp->flags &= ~PF_DO_UNWANT;
peer_send(p, nb_create_uninterest());
}
}
}

void
peer_good_piece(struct peer *p, uint32_t index)
{
if (peer_has_bad(p, index)) {
assert(p->npcs_bad > 0);
p->npcs_bad--;
if (p->npcs_bad == 0) {
free(p->bad_field);
p->bad_field = NULL;
} else
clear_bit(p->bad_field, index);
}
p->suspicion = 0;
if (p->mp->flags & PF_SUSPECT) {
btpd_log(BTPD_L_BAD, "unsuspect peer %p.\n", p);
p->mp->flags &= ~PF_SUSPECT;
if (p->nwant > 0) {
assert(p->mp->flags & PF_I_WANT);
peer_send(p, nb_create_interest());
}
if (peer_leech_ok(p))
dl_on_download(p);
}
}

int
peer_chokes(struct peer *p)
{
@@ -602,6 +661,12 @@ peer_has(struct peer *p, uint32_t index)
return has_bit(p->piece_field, index);
}

int
peer_has_bad(struct peer *p, uint32_t index)
{
return p->bad_field != NULL && has_bit(p->bad_field, index);
}

int
peer_laden(struct peer *p)
{
@@ -617,7 +682,8 @@ peer_wanted(struct peer *p)
int
peer_leech_ok(struct peer *p)
{
return (p->mp->flags & (PF_I_WANT|PF_P_CHOKE)) == PF_I_WANT;
return (p->mp->flags & (PF_BANNED|PF_SUSPECT|PF_I_WANT|PF_P_CHOKE))
== PF_I_WANT && !peer_laden(p);
}

int
@@ -638,3 +704,9 @@ peer_full(struct peer *p)
{
return p->npieces == p->n->tp->npieces;
}

int
peer_requestable(struct peer *p, uint32_t index)
{
return peer_has(p, index) && !peer_has_bad(p, index);
}

+ 8
- 0
btpd/peer.h Bestand weergeven

@@ -11,6 +11,8 @@
#define PF_NO_REQUESTS 0x80
#define PF_INCOMING 0x100
#define PF_DO_UNWANT 0x200
#define PF_SUSPECT 0x400
#define PF_BANNED 0x800

#define MAXPIECEMSGS 128
#define MAXPIPEDREQUESTS 10
@@ -61,9 +63,15 @@ int peer_chokes(struct peer *p);
int peer_wanted(struct peer *p);
int peer_laden(struct peer *p);
int peer_has(struct peer *p, uint32_t index);
int peer_has_bad(struct peer *p, uint32_t index);
int peer_leech_ok(struct peer *p);
int peer_full(struct peer *p);
void peer_bad_piece(struct peer *p, uint32_t index);
void peer_good_piece(struct peer *p, uint32_t index);
int peer_requestable(struct peer *p, uint32_t index);

void mp_hold(struct meta_peer *mp);
void mp_drop(struct meta_peer *mp, struct net *n);
void mp_kill(struct meta_peer *mp);

#endif

+ 2
- 0
btpd/util.c Bestand weergeven

@@ -83,6 +83,8 @@ logtype_str(uint32_t type)
case BTPD_L_CONN: return "conn";
case BTPD_L_TR: return "tracker";
case BTPD_L_MSG: return "msg";
case BTPD_L_POL: return "policy";
case BTPD_L_BAD: return "bad";
}
return "";
}


Laden…
Annuleren
Opslaan