* Use the new net_bufs where it makes sense. * Take advantage of the reference count on net_bufs and only allocate the (un)choke and (un)interest messages once.master
@@ -3,6 +3,7 @@ btpd_SOURCES=\ | |||||
btpd.c btpd.h\ | btpd.c btpd.h\ | ||||
cli_if.c\ | cli_if.c\ | ||||
net.c net.h\ | net.c net.h\ | ||||
net_buf.c net_buf.h\ | |||||
queue.h \ | queue.h \ | ||||
peer.c peer.h\ | peer.c peer.h\ | ||||
policy_choke.c policy_if.c policy_subr.c policy.h\ | policy_choke.c policy_if.c policy_subr.c policy.h\ | ||||
@@ -145,6 +145,15 @@ btpd_init(void) | |||||
"More could be beneficial to the download performance.\n", | "More could be beneficial to the download performance.\n", | ||||
nfiles); | nfiles); | ||||
btpd.maxpeers = nfiles - 20; | btpd.maxpeers = nfiles - 20; | ||||
btpd.choke_msg = nb_create_choke(); | |||||
nb_hold(btpd.choke_msg); | |||||
btpd.unchoke_msg = nb_create_unchoke(); | |||||
nb_hold(btpd.unchoke_msg); | |||||
btpd.interest_msg = nb_create_interest(); | |||||
nb_hold(btpd.interest_msg); | |||||
btpd.uninterest_msg = nb_create_uninterest(); | |||||
nb_hold(btpd.uninterest_msg); | |||||
} | } | ||||
void | void | ||||
@@ -17,6 +17,7 @@ | |||||
#include "benc.h" | #include "benc.h" | ||||
#include "metainfo.h" | #include "metainfo.h" | ||||
#include "iobuf.h" | #include "iobuf.h" | ||||
#include "net_buf.h" | |||||
#include "net.h" | #include "net.h" | ||||
#include "peer.h" | #include "peer.h" | ||||
#include "torrent.h" | #include "torrent.h" | ||||
@@ -78,6 +79,11 @@ struct btpd { | |||||
struct event sigint; | struct event sigint; | ||||
struct event sigterm; | struct event sigterm; | ||||
struct event sigchld; | struct event sigchld; | ||||
struct net_buf *choke_msg; | |||||
struct net_buf *unchoke_msg; | |||||
struct net_buf *interest_msg; | |||||
struct net_buf *uninterest_msg; | |||||
}; | }; | ||||
extern struct btpd btpd; | extern struct btpd btpd; | ||||
@@ -18,8 +18,6 @@ | |||||
#include "btpd.h" | #include "btpd.h" | ||||
#define WRITE_TIMEOUT (& (struct timeval) { 60, 0 }) | |||||
#define min(x, y) ((x) <= (y) ? (x) : (y)) | #define min(x, y) ((x) <= (y) ? (x) : (y)) | ||||
static unsigned long | static unsigned long | ||||
@@ -70,101 +68,6 @@ net_read32(void *buf) | |||||
return ntohl(*(uint32_t *)buf); | return ntohl(*(uint32_t *)buf); | ||||
} | } | ||||
static void | |||||
kill_buf_no(char *buf, size_t len) | |||||
{ | |||||
//Nothing | |||||
} | |||||
static void | |||||
kill_buf_free(char *buf, size_t len) | |||||
{ | |||||
free(buf); | |||||
} | |||||
int | |||||
nb_drop(struct net_buf *nb) | |||||
{ | |||||
assert(nb->refs > 0); | |||||
nb->refs--; | |||||
if (nb->refs == 0) { | |||||
nb->kill_buf(nb->buf, nb->len); | |||||
free(nb); | |||||
return 1; | |||||
} else | |||||
return 0; | |||||
} | |||||
void | |||||
nb_hold(struct net_buf *nb) | |||||
{ | |||||
nb->refs++; | |||||
} | |||||
struct net_buf * | |||||
nb_create_alloc(short type, size_t len) | |||||
{ | |||||
struct net_buf *nb = btpd_calloc(1, sizeof(*nb) + len); | |||||
nb->type = type; | |||||
nb->buf = (char *)(nb + 1); | |||||
nb->len = len; | |||||
nb->kill_buf = kill_buf_no; | |||||
return nb; | |||||
} | |||||
struct net_buf * | |||||
nb_create_set(short type, char *buf, size_t len, | |||||
void (*kill_buf)(char *, size_t)) | |||||
{ | |||||
struct net_buf *nb = btpd_calloc(1, sizeof(*nb)); | |||||
nb->type = type; | |||||
nb->buf = buf; | |||||
nb->len = len; | |||||
nb->kill_buf = kill_buf; | |||||
return nb; | |||||
} | |||||
uint32_t | |||||
nb_get_index(struct net_buf *nb) | |||||
{ | |||||
switch (nb->type) { | |||||
case NB_CANCEL: | |||||
case NB_HAVE: | |||||
case NB_PIECE: | |||||
case NB_REQUEST: | |||||
return net_read32(nb->buf + 5); | |||||
default: | |||||
abort(); | |||||
} | |||||
} | |||||
uint32_t | |||||
nb_get_begin(struct net_buf *nb) | |||||
{ | |||||
switch (nb->type) { | |||||
case NB_CANCEL: | |||||
case NB_PIECE: | |||||
case NB_REQUEST: | |||||
return net_read32(nb->buf + 9); | |||||
default: | |||||
abort(); | |||||
} | |||||
} | |||||
uint32_t | |||||
nb_get_length(struct net_buf *nb) | |||||
{ | |||||
switch (nb->type) { | |||||
case NB_CANCEL: | |||||
case NB_REQUEST: | |||||
return net_read32(nb->buf + 13); | |||||
case NB_PIECE: | |||||
return net_read32(nb->buf) - 9; | |||||
default: | |||||
abort(); | |||||
} | |||||
} | |||||
void | void | ||||
kill_shake(struct input_reader *reader) | kill_shake(struct input_reader *reader) | ||||
{ | { | ||||
@@ -255,173 +158,6 @@ net_write(struct peer *p, unsigned long wmax) | |||||
return nwritten; | return nwritten; | ||||
} | } | ||||
void | |||||
net_send(struct peer *p, struct net_buf *nb) | |||||
{ | |||||
struct nb_link *nl = btpd_calloc(1, sizeof(*nl)); | |||||
nl->nb = nb; | |||||
nb_hold(nb); | |||||
if (BTPDQ_EMPTY(&p->outq)) { | |||||
assert(p->outq_off == 0); | |||||
event_add(&p->out_ev, WRITE_TIMEOUT); | |||||
} | |||||
BTPDQ_INSERT_TAIL(&p->outq, nl, entry); | |||||
} | |||||
/* | |||||
* Remove a network buffer from the peer's outq. | |||||
* If a part of the buffer already have been written | |||||
* to the network it cannot be removed. | |||||
* | |||||
* Returns 1 if the buffer is removed, 0 if not. | |||||
*/ | |||||
int | |||||
net_unsend(struct peer *p, struct nb_link *nl) | |||||
{ | |||||
if (!(nl == BTPDQ_FIRST(&p->outq) && p->outq_off > 0)) { | |||||
BTPDQ_REMOVE(&p->outq, nl, entry); | |||||
nb_drop(nl->nb); | |||||
free(nl); | |||||
if (BTPDQ_EMPTY(&p->outq)) { | |||||
if (p->flags & PF_ON_WRITEQ) { | |||||
BTPDQ_REMOVE(&btpd.writeq, p, wq_entry); | |||||
p->flags &= ~PF_ON_WRITEQ; | |||||
} else | |||||
event_del(&p->out_ev); | |||||
} | |||||
return 1; | |||||
} else | |||||
return 0; | |||||
} | |||||
void | |||||
net_send_piece(struct peer *p, uint32_t index, uint32_t begin, | |||||
char *block, size_t blen) | |||||
{ | |||||
struct net_buf *head, *piece; | |||||
btpd_log(BTPD_L_MSG, "send piece: %u, %u, %u\n", index, begin, blen); | |||||
head = nb_create_alloc(NB_PIECE, 13); | |||||
net_write32(head->buf, 9 + blen); | |||||
head->buf[4] = MSG_PIECE; | |||||
net_write32(head->buf + 5, index); | |||||
net_write32(head->buf + 9, begin); | |||||
net_send(p, head); | |||||
piece = nb_create_set(NB_TORRENTDATA, block, blen, kill_buf_free); | |||||
net_send(p, piece); | |||||
} | |||||
void | |||||
net_send_request(struct peer *p, struct piece_req *req) | |||||
{ | |||||
struct net_buf *out = nb_create_alloc(NB_REQUEST, 17); | |||||
net_write32(out->buf, 13); | |||||
out->buf[4] = MSG_REQUEST; | |||||
net_write32(out->buf + 5, req->index); | |||||
net_write32(out->buf + 9, req->begin); | |||||
net_write32(out->buf + 13, req->length); | |||||
net_send(p, out); | |||||
} | |||||
void | |||||
net_send_cancel(struct peer *p, struct piece_req *req) | |||||
{ | |||||
struct net_buf *out = nb_create_alloc(NB_CANCEL, 17); | |||||
net_write32(out->buf, 13); | |||||
out->buf[4] = MSG_CANCEL; | |||||
net_write32(out->buf + 5, req->index); | |||||
net_write32(out->buf + 9, req->begin); | |||||
net_write32(out->buf + 13, req->length); | |||||
net_send(p, out); | |||||
} | |||||
void | |||||
net_send_have(struct peer *p, uint32_t index) | |||||
{ | |||||
struct net_buf *out = nb_create_alloc(NB_HAVE, 9); | |||||
net_write32(out->buf, 5); | |||||
out->buf[4] = MSG_HAVE; | |||||
net_write32(out->buf + 5, index); | |||||
net_send(p, out); | |||||
} | |||||
void | |||||
net_send_multihave(struct peer *p) | |||||
{ | |||||
struct torrent *tp = p->tp; | |||||
struct net_buf *out = nb_create_alloc(NB_MULTIHAVE, 9 * tp->have_npieces); | |||||
for (uint32_t i = 0, count = 0; count < tp->have_npieces; i++) { | |||||
if (has_bit(tp->piece_field, i)) { | |||||
net_write32(out->buf + count * 9, 5); | |||||
out->buf[count * 9 + 4] = MSG_HAVE; | |||||
net_write32(out->buf + count * 9 + 5, i); | |||||
count++; | |||||
} | |||||
} | |||||
net_send(p, out); | |||||
} | |||||
void | |||||
net_send_onesized(struct peer *p, char mtype, int btype) | |||||
{ | |||||
struct net_buf *out = nb_create_alloc(btype, 5); | |||||
net_write32(out->buf, 1); | |||||
out->buf[4] = mtype; | |||||
net_send(p, out); | |||||
} | |||||
void | |||||
net_send_unchoke(struct peer *p) | |||||
{ | |||||
net_send_onesized(p, MSG_UNCHOKE, NB_UNCHOKE); | |||||
} | |||||
void | |||||
net_send_choke(struct peer *p) | |||||
{ | |||||
net_send_onesized(p, MSG_CHOKE, NB_CHOKE); | |||||
} | |||||
void | |||||
net_send_uninterest(struct peer *p) | |||||
{ | |||||
net_send_onesized(p, MSG_UNINTEREST, NB_UNINTEREST); | |||||
} | |||||
void | |||||
net_send_interest(struct peer *p) | |||||
{ | |||||
net_send_onesized(p, MSG_INTEREST, NB_INTEREST); | |||||
} | |||||
void | |||||
net_send_bitfield(struct peer *p) | |||||
{ | |||||
uint32_t plen = ceil(p->tp->meta.npieces / 8.0); | |||||
struct net_buf *out = nb_create_alloc(NB_BITFIELD, 5); | |||||
net_write32(out->buf, plen + 1); | |||||
out->buf[4] = MSG_BITFIELD; | |||||
net_send(p, out); | |||||
out = nb_create_set(NB_BITDATA, p->tp->piece_field, plen, kill_buf_no); | |||||
net_send(p, out); | |||||
} | |||||
void | |||||
net_send_shake(struct peer *p) | |||||
{ | |||||
struct net_buf *out = nb_create_alloc(NB_SHAKE, 68); | |||||
bcopy("\x13""BitTorrent protocol\0\0\0\0\0\0\0\0", out->buf, 28); | |||||
bcopy(p->tp->meta.info_hash, out->buf + 28, 20); | |||||
bcopy(btpd.peer_id, out->buf + 48, 20); | |||||
net_send(p, out); | |||||
} | |||||
static void | static void | ||||
kill_generic(struct input_reader *reader) | kill_generic(struct input_reader *reader) | ||||
{ | { | ||||
@@ -761,7 +497,7 @@ net_shake_read(struct peer *p, unsigned long rmax) | |||||
if (tp != NULL) { | if (tp != NULL) { | ||||
hs->state = SHAKE_INFO; | hs->state = SHAKE_INFO; | ||||
p->tp = tp; | p->tp = tp; | ||||
net_send_shake(p); | peer_send(p, nb_create_shake(p->tp)); | ||||
} else | } else | ||||
goto bad_shake; | goto bad_shake; | ||||
} else { | } else { | ||||
@@ -791,9 +527,11 @@ net_shake_read(struct peer *p, unsigned long rmax) | |||||
net_generic_reader(p); | net_generic_reader(p); | ||||
if (p->tp->have_npieces > 0) { | if (p->tp->have_npieces > 0) { | ||||
if (p->tp->have_npieces * 9 < 5 + ceil(p->tp->meta.npieces / 8.0)) | if (p->tp->have_npieces * 9 < 5 + ceil(p->tp->meta.npieces / 8.0)) | ||||
net_send_multihave(p); | peer_send(p, nb_create_multihave(p->tp)); | ||||
else | else { | ||||
net_send_bitfield(p); | peer_send(p, nb_create_bitfield(p->tp)); | ||||
peer_send(p, nb_create_bitdata(p->tp)); | |||||
} | |||||
} | } | ||||
cm_on_new_peer(p); | cm_on_new_peer(p); | ||||
} else | } else | ||||
@@ -826,7 +564,7 @@ net_handshake(struct peer *p, int incoming) | |||||
hs->rd.kill = kill_shake; | hs->rd.kill = kill_shake; | ||||
if (!incoming) | if (!incoming) | ||||
net_send_shake(p); | peer_send(p, nb_create_shake(p->tp)); | ||||
} | } | ||||
int | int | ||||
@@ -11,43 +11,7 @@ | |||||
#define MSG_PIECE 7 | #define MSG_PIECE 7 | ||||
#define MSG_CANCEL 8 | #define MSG_CANCEL 8 | ||||
#define NB_CHOKE 0 | #define WRITE_TIMEOUT (& (struct timeval) { 60, 0 }) | ||||
#define NB_UNCHOKE 1 | |||||
#define NB_INTEREST 2 | |||||
#define NB_UNINTEREST 3 | |||||
#define NB_HAVE 4 | |||||
#define NB_BITFIELD 5 | |||||
#define NB_REQUEST 6 | |||||
#define NB_PIECE 7 | |||||
#define NB_CANCEL 8 | |||||
#define NB_TORRENTDATA 10 | |||||
#define NB_MULTIHAVE 11 | |||||
#define NB_BITDATA 12 | |||||
#define NB_SHAKE 13 | |||||
struct net_buf { | |||||
short type; | |||||
unsigned refs; | |||||
char *buf; | |||||
size_t len; | |||||
void (*kill_buf)(char *, size_t); | |||||
}; | |||||
struct nb_link { | |||||
struct net_buf *nb; | |||||
BTPDQ_ENTRY(nb_link) entry; | |||||
}; | |||||
BTPDQ_HEAD(nb_tq, nb_link); | |||||
struct net_buf *nb_create_alloc(short type, size_t len); | |||||
struct net_buf *nb_create_set(short type, char *buf, size_t len, | |||||
void (*kill_buf)(char *, size_t)); | |||||
int nb_drop(struct net_buf *nb); | |||||
void nb_hold(struct net_buf *nb); | |||||
uint32_t nb_get_index(struct net_buf *nb); | |||||
uint32_t nb_get_begin(struct net_buf *nb); | |||||
uint32_t nb_get_length(struct net_buf *nb); | |||||
struct peer; | struct peer; | ||||
@@ -94,36 +58,18 @@ struct generic_reader { | |||||
char _io_buf[MAX_INPUT_LEFT]; | char _io_buf[MAX_INPUT_LEFT]; | ||||
}; | }; | ||||
struct piece_req { | |||||
uint32_t index, begin, length; | |||||
struct iob_link *head; /* Pointer to outgoing piece. */ | |||||
BTPDQ_ENTRY(piece_req) entry; | |||||
}; | |||||
BTPDQ_HEAD(piece_req_tq, piece_req); | |||||
void net_connection_cb(int sd, short type, void *arg); | void net_connection_cb(int sd, short type, void *arg); | ||||
void net_bw_rate(void); | void net_bw_rate(void); | ||||
void net_bw_cb(int sd, short type, void *arg); | void net_bw_cb(int sd, short type, void *arg); | ||||
struct peer; | |||||
void net_send_uninterest(struct peer *p); | |||||
void net_send_interest(struct peer *p); | |||||
void net_send_unchoke(struct peer *p); | |||||
void net_send_choke(struct peer *p); | |||||
void net_send_have(struct peer *p, uint32_t index); | |||||
void net_send_request(struct peer *p, struct piece_req *req); | |||||
void net_send_piece(struct peer *p, uint32_t index, uint32_t begin, | |||||
char *block, size_t blen); | |||||
void net_send_cancel(struct peer *p, struct piece_req *req); | |||||
int net_unsend(struct peer *p, struct nb_link *nl); | |||||
void net_handshake(struct peer *p, int incoming); | |||||
void net_read_cb(int sd, short type, void *arg); | void net_read_cb(int sd, short type, void *arg); | ||||
void net_write_cb(int sd, short type, void *arg); | void net_write_cb(int sd, short type, void *arg); | ||||
void net_handshake(struct peer *p, int incoming); | |||||
int net_connect2(struct sockaddr *sa, socklen_t salen, int *sd); | int net_connect2(struct sockaddr *sa, socklen_t salen, int *sd); | ||||
int net_connect(const char *ip, int port, int *sd); | int net_connect(const char *ip, int port, int *sd); | ||||
void net_write32(void *buf, uint32_t num); | |||||
uint32_t net_read32(void *buf); | |||||
#endif | #endif |
@@ -0,0 +1,234 @@ | |||||
#include <math.h> | |||||
#include <string.h> | |||||
#include "btpd.h" | |||||
static void | |||||
kill_buf_no(char *buf, size_t len) | |||||
{ | |||||
} | |||||
static void | |||||
kill_buf_free(char *buf, size_t len) | |||||
{ | |||||
free(buf); | |||||
} | |||||
static struct net_buf * | |||||
nb_create_alloc(short type, size_t len) | |||||
{ | |||||
struct net_buf *nb = btpd_calloc(1, sizeof(*nb) + len); | |||||
nb->type = type; | |||||
nb->buf = (char *)(nb + 1); | |||||
nb->len = len; | |||||
nb->kill_buf = kill_buf_no; | |||||
return nb; | |||||
} | |||||
static struct net_buf * | |||||
nb_create_set(short type, char *buf, size_t len, | |||||
void (*kill_buf)(char *, size_t)) | |||||
{ | |||||
struct net_buf *nb = btpd_calloc(1, sizeof(*nb)); | |||||
nb->type = type; | |||||
nb->buf = buf; | |||||
nb->len = len; | |||||
nb->kill_buf = kill_buf; | |||||
return nb; | |||||
} | |||||
static struct net_buf * | |||||
nb_create_onesized(char mtype, int btype) | |||||
{ | |||||
struct net_buf *out = nb_create_alloc(btype, 5); | |||||
net_write32(out->buf, 1); | |||||
out->buf[4] = mtype; | |||||
return out; | |||||
} | |||||
struct net_buf * | |||||
nb_create_piece(uint32_t index, uint32_t begin, size_t blen) | |||||
{ | |||||
struct net_buf *out; | |||||
btpd_log(BTPD_L_MSG, "send piece: %u, %u, %u\n", index, begin, blen); | |||||
out = nb_create_alloc(NB_PIECE, 13); | |||||
net_write32(out->buf, 9 + blen); | |||||
out->buf[4] = MSG_PIECE; | |||||
net_write32(out->buf + 5, index); | |||||
net_write32(out->buf + 9, begin); | |||||
return out; | |||||
} | |||||
struct net_buf * | |||||
nb_create_torrentdata(char *block, size_t blen) | |||||
{ | |||||
struct net_buf *out; | |||||
out = nb_create_set(NB_TORRENTDATA, block, blen, kill_buf_free); | |||||
return out; | |||||
} | |||||
struct net_buf * | |||||
nb_create_request(uint32_t index, uint32_t begin, uint32_t length) | |||||
{ | |||||
struct net_buf *out = nb_create_alloc(NB_REQUEST, 17); | |||||
net_write32(out->buf, 13); | |||||
out->buf[4] = MSG_REQUEST; | |||||
net_write32(out->buf + 5, index); | |||||
net_write32(out->buf + 9, begin); | |||||
net_write32(out->buf + 13, length); | |||||
return out; | |||||
} | |||||
struct net_buf * | |||||
nb_create_cancel(uint32_t index, uint32_t begin, uint32_t length) | |||||
{ | |||||
struct net_buf *out = nb_create_alloc(NB_CANCEL, 17); | |||||
net_write32(out->buf, 13); | |||||
out->buf[4] = MSG_CANCEL; | |||||
net_write32(out->buf + 5, index); | |||||
net_write32(out->buf + 9, begin); | |||||
net_write32(out->buf + 13, length); | |||||
return out; | |||||
} | |||||
struct net_buf * | |||||
nb_create_have(uint32_t index) | |||||
{ | |||||
struct net_buf *out = nb_create_alloc(NB_HAVE, 9); | |||||
net_write32(out->buf, 5); | |||||
out->buf[4] = MSG_HAVE; | |||||
net_write32(out->buf + 5, index); | |||||
return out; | |||||
} | |||||
struct net_buf * | |||||
nb_create_multihave(struct torrent *tp) | |||||
{ | |||||
struct net_buf *out = nb_create_alloc(NB_MULTIHAVE, 9 * tp->have_npieces); | |||||
for (uint32_t i = 0, count = 0; count < tp->have_npieces; i++) { | |||||
if (has_bit(tp->piece_field, i)) { | |||||
net_write32(out->buf + count * 9, 5); | |||||
out->buf[count * 9 + 4] = MSG_HAVE; | |||||
net_write32(out->buf + count * 9 + 5, i); | |||||
count++; | |||||
} | |||||
} | |||||
return out; | |||||
} | |||||
struct net_buf * | |||||
nb_create_unchoke(void) | |||||
{ | |||||
return nb_create_onesized(MSG_UNCHOKE, NB_UNCHOKE); | |||||
} | |||||
struct net_buf * | |||||
nb_create_choke(void) | |||||
{ | |||||
return nb_create_onesized(MSG_CHOKE, NB_CHOKE); | |||||
} | |||||
struct net_buf * | |||||
nb_create_uninterest(void) | |||||
{ | |||||
return nb_create_onesized(MSG_UNINTEREST, NB_UNINTEREST); | |||||
} | |||||
struct net_buf * | |||||
nb_create_interest(void) | |||||
{ | |||||
return nb_create_onesized(MSG_INTEREST, NB_INTEREST); | |||||
} | |||||
struct net_buf * | |||||
nb_create_bitfield(struct torrent *tp) | |||||
{ | |||||
uint32_t plen = ceil(tp->meta.npieces / 8.0); | |||||
struct net_buf *out = nb_create_alloc(NB_BITFIELD, 5); | |||||
net_write32(out->buf, plen + 1); | |||||
out->buf[4] = MSG_BITFIELD; | |||||
return out; | |||||
} | |||||
struct net_buf * | |||||
nb_create_bitdata(struct torrent *tp) | |||||
{ | |||||
uint32_t plen = ceil(tp->meta.npieces / 8.0); | |||||
struct net_buf *out = | |||||
nb_create_set(NB_BITDATA, tp->piece_field, plen, kill_buf_no); | |||||
return out; | |||||
} | |||||
struct net_buf * | |||||
nb_create_shake(struct torrent *tp) | |||||
{ | |||||
struct net_buf *out = nb_create_alloc(NB_SHAKE, 68); | |||||
bcopy("\x13""BitTorrent protocol\0\0\0\0\0\0\0\0", out->buf, 28); | |||||
bcopy(tp->meta.info_hash, out->buf + 28, 20); | |||||
bcopy(btpd.peer_id, out->buf + 48, 20); | |||||
return out; | |||||
} | |||||
uint32_t | |||||
nb_get_index(struct net_buf *nb) | |||||
{ | |||||
switch (nb->type) { | |||||
case NB_CANCEL: | |||||
case NB_HAVE: | |||||
case NB_PIECE: | |||||
case NB_REQUEST: | |||||
return net_read32(nb->buf + 5); | |||||
default: | |||||
abort(); | |||||
} | |||||
} | |||||
uint32_t | |||||
nb_get_begin(struct net_buf *nb) | |||||
{ | |||||
switch (nb->type) { | |||||
case NB_CANCEL: | |||||
case NB_PIECE: | |||||
case NB_REQUEST: | |||||
return net_read32(nb->buf + 9); | |||||
default: | |||||
abort(); | |||||
} | |||||
} | |||||
uint32_t | |||||
nb_get_length(struct net_buf *nb) | |||||
{ | |||||
switch (nb->type) { | |||||
case NB_CANCEL: | |||||
case NB_REQUEST: | |||||
return net_read32(nb->buf + 13); | |||||
case NB_PIECE: | |||||
return net_read32(nb->buf) - 9; | |||||
default: | |||||
abort(); | |||||
} | |||||
} | |||||
int | |||||
nb_drop(struct net_buf *nb) | |||||
{ | |||||
assert(nb->refs > 0); | |||||
nb->refs--; | |||||
if (nb->refs == 0) { | |||||
nb->kill_buf(nb->buf, nb->len); | |||||
free(nb); | |||||
return 1; | |||||
} else | |||||
return 0; | |||||
} | |||||
void | |||||
nb_hold(struct net_buf *nb) | |||||
{ | |||||
nb->refs++; | |||||
} |
@@ -0,0 +1,59 @@ | |||||
#ifndef BTPD_NET_BUF_H | |||||
#define BTPD_NET_BUF_H | |||||
#define NB_CHOKE 0 | |||||
#define NB_UNCHOKE 1 | |||||
#define NB_INTEREST 2 | |||||
#define NB_UNINTEREST 3 | |||||
#define NB_HAVE 4 | |||||
#define NB_BITFIELD 5 | |||||
#define NB_REQUEST 6 | |||||
#define NB_PIECE 7 | |||||
#define NB_CANCEL 8 | |||||
#define NB_TORRENTDATA 10 | |||||
#define NB_MULTIHAVE 11 | |||||
#define NB_BITDATA 12 | |||||
#define NB_SHAKE 13 | |||||
struct net_buf { | |||||
short type; | |||||
unsigned refs; | |||||
char *buf; | |||||
size_t len; | |||||
void (*kill_buf)(char *, size_t); | |||||
}; | |||||
struct nb_link { | |||||
struct net_buf *nb; | |||||
BTPDQ_ENTRY(nb_link) entry; | |||||
}; | |||||
BTPDQ_HEAD(nb_tq, nb_link); | |||||
struct torrent; | |||||
struct peer; | |||||
struct net_buf *nb_create_piece(uint32_t index, uint32_t begin, size_t blen); | |||||
struct net_buf *nb_create_torrentdata(char *block, size_t blen); | |||||
struct net_buf *nb_create_request(uint32_t index, | |||||
uint32_t begin, uint32_t length); | |||||
struct net_buf *nb_create_cancel(uint32_t index, | |||||
uint32_t begin, uint32_t length); | |||||
struct net_buf *nb_create_have(uint32_t index); | |||||
struct net_buf *nb_create_multihave(struct torrent *tp); | |||||
struct net_buf *nb_create_unchoke(void); | |||||
struct net_buf *nb_create_choke(void); | |||||
struct net_buf *nb_create_uninterest(void); | |||||
struct net_buf *nb_create_interest(void); | |||||
struct net_buf *nb_create_bitfield(struct torrent *tp); | |||||
struct net_buf *nb_create_bitdata(struct torrent *tp); | |||||
struct net_buf *nb_create_shake(struct torrent *tp); | |||||
int nb_drop(struct net_buf *nb); | |||||
void nb_hold(struct net_buf *nb); | |||||
uint32_t nb_get_index(struct net_buf *nb); | |||||
uint32_t nb_get_begin(struct net_buf *nb); | |||||
uint32_t nb_get_length(struct net_buf *nb); | |||||
#endif |
@@ -21,7 +21,6 @@ void | |||||
peer_kill(struct peer *p) | peer_kill(struct peer *p) | ||||
{ | { | ||||
struct nb_link *nl; | struct nb_link *nl; | ||||
struct piece_req *req; | |||||
btpd_log(BTPD_L_CONN, "killed peer.\n"); | btpd_log(BTPD_L_CONN, "killed peer.\n"); | ||||
@@ -45,11 +44,12 @@ peer_kill(struct peer *p) | |||||
free(nl); | free(nl); | ||||
nl = next; | nl = next; | ||||
} | } | ||||
req = BTPDQ_FIRST(&p->my_reqs); | nl = BTPDQ_FIRST(&p->my_reqs); | ||||
while (req != NULL) { | while (nl != NULL) { | ||||
struct piece_req *next = BTPDQ_NEXT(req, entry); | struct nb_link *next = BTPDQ_NEXT(nl, entry); | ||||
free(req); | nb_drop(nl->nb); | ||||
req = next; | free(nl); | ||||
nl = next; | |||||
} | } | ||||
p->reader->kill(p->reader); | p->reader->kill(p->reader); | ||||
@@ -59,32 +59,82 @@ peer_kill(struct peer *p) | |||||
btpd.npeers--; | btpd.npeers--; | ||||
} | } | ||||
void | |||||
peer_send(struct peer *p, struct net_buf *nb) | |||||
{ | |||||
struct nb_link *nl = btpd_calloc(1, sizeof(*nl)); | |||||
nl->nb = nb; | |||||
nb_hold(nb); | |||||
if (BTPDQ_EMPTY(&p->outq)) { | |||||
assert(p->outq_off == 0); | |||||
event_add(&p->out_ev, WRITE_TIMEOUT); | |||||
} | |||||
BTPDQ_INSERT_TAIL(&p->outq, nl, entry); | |||||
} | |||||
/* | |||||
* Remove a network buffer from the peer's outq. | |||||
* If a part of the buffer already have been written | |||||
* to the network it cannot be removed. | |||||
* | |||||
* Returns 1 if the buffer is removed, 0 if not. | |||||
*/ | |||||
int | |||||
peer_unsend(struct peer *p, struct nb_link *nl) | |||||
{ | |||||
if (!(nl == BTPDQ_FIRST(&p->outq) && p->outq_off > 0)) { | |||||
BTPDQ_REMOVE(&p->outq, nl, entry); | |||||
nb_drop(nl->nb); | |||||
free(nl); | |||||
if (BTPDQ_EMPTY(&p->outq)) { | |||||
if (p->flags & PF_ON_WRITEQ) { | |||||
BTPDQ_REMOVE(&btpd.writeq, p, wq_entry); | |||||
p->flags &= ~PF_ON_WRITEQ; | |||||
} else | |||||
event_del(&p->out_ev); | |||||
} | |||||
return 1; | |||||
} else | |||||
return 0; | |||||
} | |||||
void | void | ||||
peer_request(struct peer *p, uint32_t index, uint32_t begin, uint32_t len) | peer_request(struct peer *p, uint32_t index, uint32_t begin, uint32_t len) | ||||
{ | { | ||||
if (p->tp->endgame == 0) | if (p->tp->endgame == 0) | ||||
assert(p->nreqs_out < MAXPIPEDREQUESTS); | assert(p->nreqs_out < MAXPIPEDREQUESTS); | ||||
p->nreqs_out++; | p->nreqs_out++; | ||||
struct piece_req *req = btpd_calloc(1, sizeof(*req)); | struct net_buf *nb = nb_create_request(index, begin, len); | ||||
req->index = index; | struct nb_link *nl = btpd_calloc(1, sizeof(*nl)); | ||||
req->begin = begin; | nl->nb = nb; | ||||
req->length = len; | nb_hold(nb); | ||||
BTPDQ_INSERT_TAIL(&p->my_reqs, req, entry); | BTPDQ_INSERT_TAIL(&p->my_reqs, nl, entry); | ||||
net_send_request(p, req); | peer_send(p, nb); | ||||
} | } | ||||
void | void | ||||
peer_cancel(struct peer *p, uint32_t index, uint32_t begin, uint32_t len) | peer_cancel(struct peer *p, uint32_t index, uint32_t begin, uint32_t len) | ||||
{ | { | ||||
struct piece_req *req; | struct net_buf *nb = NULL; | ||||
struct nb_link *nl; | |||||
again: | again: | ||||
BTPDQ_FOREACH(req, &p->my_reqs, entry) | BTPDQ_FOREACH(nl, &p->my_reqs, entry) { | ||||
if (index == req->index && begin == req->begin && len == req->length) | int match = nb_get_begin(nl->nb) == begin | ||||
&& nb_get_index(nl->nb) == index | |||||
&& nb_get_length(nl->nb) == len; | |||||
if (match) | |||||
break; | break; | ||||
if (req != NULL) { | } | ||||
net_send_cancel(p, req); | if (nl != NULL) { | ||||
BTPDQ_REMOVE(&p->my_reqs, req, entry); | if (nb == NULL) { | ||||
free(req); | nb = nb_create_cancel(index, begin, len); | ||||
peer_send(p, nb); | |||||
} | |||||
BTPDQ_REMOVE(&p->my_reqs, nl, entry); | |||||
nb_drop(nl->nb); | |||||
free(nl); | |||||
p->nreqs_out--; | p->nreqs_out--; | ||||
goto again; | goto again; | ||||
} | } | ||||
@@ -93,14 +143,14 @@ again: | |||||
void | void | ||||
peer_have(struct peer *p, uint32_t index) | peer_have(struct peer *p, uint32_t index) | ||||
{ | { | ||||
net_send_have(p, index); | peer_send(p, nb_create_have(index)); | ||||
} | } | ||||
void | void | ||||
peer_unchoke(struct peer *p) | peer_unchoke(struct peer *p) | ||||
{ | { | ||||
p->flags &= ~PF_I_CHOKE; | p->flags &= ~PF_I_CHOKE; | ||||
net_send_unchoke(p); | peer_send(p, btpd.unchoke_msg); | ||||
} | } | ||||
void | void | ||||
@@ -112,14 +162,14 @@ peer_choke(struct peer *p) | |||||
if (nl->nb->type == NB_PIECE) { | if (nl->nb->type == NB_PIECE) { | ||||
struct nb_link *data = next; | struct nb_link *data = next; | ||||
next = BTPDQ_NEXT(next, entry); | next = BTPDQ_NEXT(next, entry); | ||||
if (net_unsend(p, nl)) | if (peer_unsend(p, nl)) | ||||
net_unsend(p, data); | peer_unsend(p, data); | ||||
} | } | ||||
nl = next; | nl = next; | ||||
} | } | ||||
p->flags |= PF_I_CHOKE; | p->flags |= PF_I_CHOKE; | ||||
net_send_choke(p); | peer_send(p, btpd.choke_msg); | ||||
} | } | ||||
void | void | ||||
@@ -129,7 +179,7 @@ peer_want(struct peer *p, uint32_t index) | |||||
p->nwant++; | p->nwant++; | ||||
if (p->nwant == 1) { | if (p->nwant == 1) { | ||||
p->flags |= PF_I_WANT; | p->flags |= PF_I_WANT; | ||||
net_send_interest(p); | peer_send(p, btpd.interest_msg); | ||||
} | } | ||||
} | } | ||||
@@ -140,7 +190,7 @@ peer_unwant(struct peer *p, uint32_t index) | |||||
p->nwant--; | p->nwant--; | ||||
if (p->nwant == 0) { | if (p->nwant == 0) { | ||||
p->flags &= ~PF_I_WANT; | p->flags &= ~PF_I_WANT; | ||||
net_send_uninterest(p); | peer_send(p, btpd.uninterest_msg); | ||||
} | } | ||||
} | } | ||||
@@ -275,16 +325,17 @@ void | |||||
peer_on_piece(struct peer *p, uint32_t index, uint32_t begin, | peer_on_piece(struct peer *p, uint32_t index, uint32_t begin, | ||||
uint32_t length, const char *data) | uint32_t length, const char *data) | ||||
{ | { | ||||
struct piece_req *req = BTPDQ_FIRST(&p->my_reqs); | struct nb_link *nl = BTPDQ_FIRST(&p->my_reqs); | ||||
if (req != NULL && | if (nl != NULL && | ||||
req->index == index && | nb_get_begin(nl->nb) == begin && | ||||
req->begin == begin && | nb_get_index(nl->nb) == index && | ||||
req->length == length) { | nb_get_length(nl->nb) == length) { | ||||
assert(p->nreqs_out > 0); | assert(p->nreqs_out > 0); | ||||
p->nreqs_out--; | p->nreqs_out--; | ||||
BTPDQ_REMOVE(&p->my_reqs, req, entry); | BTPDQ_REMOVE(&p->my_reqs, nl, entry); | ||||
free(req); | nb_drop(nl->nb); | ||||
free(nl); | |||||
cm_on_block(p, index, begin, length, data); | cm_on_block(p, index, begin, length, data); | ||||
} | } | ||||
@@ -296,7 +347,8 @@ peer_on_request(struct peer *p, uint32_t index, uint32_t begin, | |||||
{ | { | ||||
off_t cbegin = index * p->tp->meta.piece_length + begin; | off_t cbegin = index * p->tp->meta.piece_length + begin; | ||||
char * content = torrent_get_bytes(p->tp, cbegin, length); | char * content = torrent_get_bytes(p->tp, cbegin, length); | ||||
net_send_piece(p, index, begin, content, length); | peer_send(p, nb_create_piece(index, begin, length)); | ||||
peer_send(p, nb_create_torrentdata(content, length)); | |||||
} | } | ||||
void | void | ||||
@@ -310,8 +362,8 @@ peer_on_cancel(struct peer *p, uint32_t index, uint32_t begin, | |||||
&& nb_get_index(nl->nb) == index | && nb_get_index(nl->nb) == index | ||||
&& nb_get_length(nl->nb) == length) { | && nb_get_length(nl->nb) == length) { | ||||
struct nb_link *data = BTPDQ_NEXT(nl, entry); | struct nb_link *data = BTPDQ_NEXT(nl, entry); | ||||
if (net_unsend(p, nl)) | if (peer_unsend(p, nl)) | ||||
net_unsend(p, data); | peer_unsend(p, data); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
@@ -25,7 +25,7 @@ struct peer { | |||||
struct torrent *tp; | struct torrent *tp; | ||||
struct piece_req_tq my_reqs; | struct nb_tq my_reqs; | ||||
unsigned nreqs_out; | unsigned nreqs_out; | ||||
@@ -48,6 +48,9 @@ struct peer { | |||||
BTPDQ_HEAD(peer_tq, peer); | BTPDQ_HEAD(peer_tq, peer); | ||||
void peer_send(struct peer *p, struct net_buf *nb); | |||||
int peer_unsend(struct peer *p, struct nb_link *nl); | |||||
void peer_unchoke(struct peer *p); | void peer_unchoke(struct peer *p); | ||||
void peer_choke(struct peer *p); | void peer_choke(struct peer *p); | ||||
void peer_unwant(struct peer *p, uint32_t index); | void peer_unwant(struct peer *p, uint32_t index); | ||||
@@ -396,20 +396,22 @@ cm_unassign_requests(struct peer *p) | |||||
while (pc != NULL) { | while (pc != NULL) { | ||||
int was_full = piece_full(pc); | int was_full = piece_full(pc); | ||||
struct piece_req *req = BTPDQ_FIRST(&p->my_reqs); | struct nb_link *nl = BTPDQ_FIRST(&p->my_reqs); | ||||
while (req != NULL) { | while (nl != NULL) { | ||||
struct piece_req *next = BTPDQ_NEXT(req, entry); | struct nb_link *next = BTPDQ_NEXT(nl, entry); | ||||
if (pc->index == req->index) { | if (pc->index == nb_get_index(nl->nb)) { | ||||
uint32_t block = nb_get_begin(nl->nb) / PIECE_BLOCKLEN; | |||||
// XXX: Needs to be looked at if we introduce snubbing. | // XXX: Needs to be looked at if we introduce snubbing. | ||||
assert(has_bit(pc->down_field, req->begin / PIECE_BLOCKLEN)); | assert(has_bit(pc->down_field, block)); | ||||
clear_bit(pc->down_field, req->begin / PIECE_BLOCKLEN); | clear_bit(pc->down_field, block); | ||||
pc->nbusy--; | pc->nbusy--; | ||||
BTPDQ_REMOVE(&p->my_reqs, req, entry); | BTPDQ_REMOVE(&p->my_reqs, nl, entry); | ||||
free(req); | nb_drop(nl->nb); | ||||
free(nl); | |||||
} | } | ||||
req = next; | nl = next; | ||||
} | } | ||||
if (was_full && !piece_full(pc)) | if (was_full && !piece_full(pc)) | ||||
@@ -449,11 +451,12 @@ cm_assign_requests_eg(struct peer *p) | |||||
void | void | ||||
cm_unassign_requests_eg(struct peer *p) | cm_unassign_requests_eg(struct peer *p) | ||||
{ | { | ||||
struct piece_req *req = BTPDQ_FIRST(&p->my_reqs); | struct nb_link *nl = BTPDQ_FIRST(&p->my_reqs); | ||||
while (req != NULL) { | while (nl != NULL) { | ||||
struct piece_req *next = BTPDQ_NEXT(req, entry); | struct nb_link *next = BTPDQ_NEXT(nl, entry); | ||||
free(req); | nb_drop(nl->nb); | ||||
req = next; | free(nl); | ||||
nl = next; | |||||
} | } | ||||
BTPDQ_INIT(&p->my_reqs); | BTPDQ_INIT(&p->my_reqs); | ||||
p->nreqs_out = 0; | p->nreqs_out = 0; | ||||