diff --git a/protocol.cpp b/protocol.cpp new file mode 100644 index 0000000..0e13887 --- /dev/null +++ b/protocol.cpp @@ -0,0 +1,212 @@ +/*************************************************************************** + * By Théophile Bastian, 2017 + * M1 Network course project at ENS Cachan, Juliusz Chroboczek. + * License: WTFPL v2 + **************************************************************************/ + +#include "protocol.h" +#include + +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(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& 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& 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(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; +} + diff --git a/protocol.h b/protocol.h new file mode 100644 index 0000000..c641965 --- /dev/null +++ b/protocol.h @@ -0,0 +1,88 @@ +/*************************************************************************** + * By Théophile Bastian, 2017 + * M1 Network course project at ENS Cachan, Juliusz Chroboczek. + * License: WTFPL v2 + **************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "nw_constants.h" +#include "Bytes.h" + +class Protocol { + public: + class NwError : public std::exception {}; + class ThreadError : public std::exception {}; + class UnknownId : public std::exception { + public: + UnknownId(u64 id) : _id(id) {} + u64 id() const { return _id; } + private: + u64 _id; + }; + + Protocol(const SockAddr& dest, u64 selfId); + ~Protocol(); + + void addIdAddr(const sockaddr_in6& addr, u64 id); + + bool readyRead() const; + /** Returns whether a packet is available. */ + + Bytes getPacket(SockAddr* from); + /** Appends the body of a full packet to `out`. */ + + void sendBody(const Bytes& body, const SockAddr& to); + void sendBody(const Bytes& body, const u64& id); + /** Sends the given `body` (wrapped in headers) */ + + void sendEmpty(const SockAddr& to); + void sendEmpty(u64 to); + + void sendIHU(const SockAddr& to, u64 id); + void sendIHU(u64 id); + /** Sends a IHU packet */ + + void sendNReq(const SockAddr& to); + void sendNReq(u64 id); + /** Sends a neighbour request packet */ + + void sendNeighbours(const SockAddr& to, + const std::vector& neigh); + void sendNeighbours(u64 id, + const std::vector& neigh); + /** Sends a neighbours list packet */ + + void sendIHave(const SockAddr& to, u64 datId, u32 seqno); + void sendIHave(u64 id, u64 datId, u32 seqno); + /** Sends a IHave packet for `datId` */ + + private: //meth + void startPollNetwork(); + void pollNetwork(); + const SockAddr& addrOfId(u64 id); + SockAddr addrOfV4(const sockaddr_in& addrv4); + + private: + struct AvailPacket { + SockAddr from; + Bytes data; + }; + + int sock; + SockAddr listenAddr; + u64 selfId; + bool terminating; + + std::thread pollThread; + std::mutex availPacketsMutex; + std::queue availPackets; + std::unordered_map addrMap; +}; +