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;
+};
+