M1-nw-project/protocol.cpp
2016-11-20 00:15:52 +01:00

212 lines
4.9 KiB
C++

/***************************************************************************
* By Théophile Bastian, 2017
* M1 Network course project at ENS Cachan, Juliusz Chroboczek.
* License: WTFPL v2 <http://www.wtfpl.net/>
**************************************************************************/
#include "protocol.h"
#include <cstring>
using namespace std;
const size_t MAX_MTU = (1 << 16) + 42;
Protocol::Protocol(const SockAddr& listenAddr, u64 selfId) :
sock(socket(PF_INET6, SOCK_DGRAM, 0)), listenAddr(listenAddr),
selfId(selfId), terminating(false)
{
if(bind(sock, (struct sockaddr*)(&listenAddr), sizeof(listenAddr)) != 0) {
perror("Cannot bind socket");
throw NwError();
}
// Set socket reception timeout
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100000;
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
perror("Cannot set socket timeout");
throw NwError();
}
startPollNetwork();
}
Protocol::~Protocol() {
terminating = true;
pollThread.join();
}
void Protocol::addIdAddr(const SockAddr& addr, u64 id) {
addrMap.insert({id, addr});
}
bool Protocol::readyRead() const {
return !availPackets.empty();
}
Bytes Protocol::getPacket(SockAddr* from) {
if(!readyRead())
return Bytes();
AvailPacket pck;
{
lock_guard<mutex>(this->availPacketsMutex);
pck = availPackets.front();
availPackets.pop();
}
if(from != NULL)
*from = pck.from;
return pck.data;
}
void Protocol::sendBody(const Bytes& body, const SockAddr& to) {
Bytes pck;
pck << csts::MAGIC << csts::VERSION << (u16)body.size() << selfId
<< body;
//TODO check size < MTU
char buffer[MAX_MTU];
pck.writeToBuffer(buffer, MAX_MTU);
ssize_t rc =
sendto(sock, buffer, pck.size(), 0, (struct sockaddr*)&to, sizeof(to));
if(rc != (ssize_t)pck.size()) {
//TODO log warning.
}
}
void Protocol::sendBody(const Bytes& body, const u64& id) {
return sendBody(body, addrOfId(id));
}
void Protocol::sendEmpty(const SockAddr& to) {
sendBody(Bytes(), to);
}
void Protocol::sendEmpty(u64 to) {
sendEmpty(addrOfId(to));
}
void Protocol::sendIHU(const SockAddr& to, u64 id) {
Bytes pck;
pck << csts::TLV_IHU << (u8) 8 << id;
sendBody(pck, to);
}
void Protocol::sendIHU(u64 id) {
sendIHU(addrOfId(id), id);
}
void Protocol::sendNReq(const SockAddr& to) {
Bytes pck;
pck << csts::TLV_NR << (u8) 0;
sendBody(pck, to);
}
void Protocol::sendNReq(u64 id) {
sendNReq(addrOfId(id));
}
void Protocol::sendNeighbours(const SockAddr& to,
const std::vector<Neighbour>& neigh) {
Bytes pck;
u8 len = neigh.size() * (8+16+2);
pck << csts::TLV_NEIGH << len;
for(const Neighbour& nei : neigh) {
u8 addr[16];
memcpy(addr, nei.addr.sin6_addr.s6_addr, 16);
pck << nei.id;
for(int b=0; b < 16; b++)
pck << addr[b];
pck << (u16)nei.addr.sin6_port;
}
sendBody(pck, to);
}
void Protocol::sendNeighbours(u64 id, const std::vector<Neighbour>& neigh) {
sendNeighbours(addrOfId(id), neigh);
}
void Protocol::sendIHave(const SockAddr& to, u64 datId, u32 seqno) {
Bytes pck;
pck << csts::TLV_IHAVE << (u8) 12 << seqno << datId;
sendBody(pck, to);
}
void Protocol::sendIHave(u64 id, u64 datId, u32 seqno) {
sendIHave(addrOfId(id), datId, seqno);
}
void Protocol::startPollNetwork() {
pollThread = std::thread([this] { this->pollNetwork(); });
}
void Protocol::pollNetwork() {
u8 buffer[MAX_MTU];
SockAddr fromAddr6;
struct sockaddr* fromAddr = (struct sockaddr*)&fromAddr6;
socklen_t fromAddrLen;
while(!terminating) {
//TODO is it blocking?
ssize_t readDat = recvfrom(sock, buffer, MAX_MTU, 0,
fromAddr, &fromAddrLen);
if(readDat < 0) {
perror("Warning: could not read from socket");
continue;
}
else if(readDat == 0)
continue;
Bytes data(buffer, readDat);
u8 magic, version;
data >> magic >> version;
if(magic != csts::MAGIC) {
//TODO log
continue;
}
if(version != csts::VERSION) {
//TODO log
continue;
}
u16 bodyLen;
data >> bodyLen;
if(data.size() < bodyLen + 64u) {
//TODO log bad length
continue;
}
else if(data.size() != bodyLen + 64u) {
//TODO log warning bad length
}
SockAddr convFromAddr;
if(fromAddr->sa_family == AF_INET)
convFromAddr = addrOfV4(*(struct sockaddr_in*)fromAddr);
else if(fromAddr->sa_family == AF_INET6)
convFromAddr = *(SockAddr*)fromAddr;
else {
fprintf(stderr, "ERROR: Unknown address family.");
continue;
}
AvailPacket pck;
pck.from = convFromAddr;
pck.data = data;
{
lock_guard<mutex>(this->availPacketsMutex);
availPackets.push(pck);
}
}
}
const SockAddr& Protocol::addrOfId(u64 id) {
try {
return addrMap.at(id);
} catch(std::out_of_range& e) {
throw UnknownId(id);
}
}
SockAddr Protocol::addrOfV4(const sockaddr_in& addrv4) {
SockAddr out;
memset(&out, 0, sizeof(SockAddr));
out.sin6_family = AF_INET6;
out.sin6_port = addrv4.sin_port;
char addr6[17];
inet_pton(AF_INET6, "::ffff:0:0", addr6);
memcpy(addr6 + 12, &addrv4.sin_addr.s_addr, 4);
return out;
}