Compare commits

...

7 commits

10 changed files with 666 additions and 0 deletions

1
.gitignore vendored
View file

@ -28,3 +28,4 @@
*.out
*.app
projet.pdf

162
Bytes.cpp Normal file
View file

@ -0,0 +1,162 @@
/***************************************************************************
* By Théophile Bastian, 2017
* M1 Network course project at ENS Cachan, Juliusz Chroboczek.
* License: WTFPL v2 <http://www.wtfpl.net/>
**************************************************************************/
/***************************************************************************
* Reused from a former project, by Théophile Bastian & Nathanaël Courant
* git@github.com:tobast/sysres-pikern.git
**************************************************************************/
#include "Bytes.h"
#include <cstring>
#include <algorithm>
#include <arpa/inet.h>
using namespace std;
Bytes::Bytes() : data(), firstIndex(0) {}
Bytes::Bytes(const void* buff, size_t len) : data(), firstIndex(0) {
data.reserve(len);
for(size_t pos=0; pos < len; pos++)
data.push_back(((u8*)buff)[pos]);
}
u8& Bytes::operator[](const size_t pos) {
if(pos < size())
return data[pos+firstIndex];
throw OutOfRange();
}
u8 Bytes::operator[](const size_t pos) const {
if(pos < size())
return data[pos+firstIndex];
throw OutOfRange();
}
u16 Bytes::ushortAt(const size_t pos) const {
if(pos+1 < size())
return (((u16)data[pos+firstIndex+1])<<8)+data[pos+firstIndex];
throw OutOfRange();
}
size_t Bytes::size() const {
return data.size() - firstIndex;
}
Bytes& Bytes::operator<<(char v) {
insertData<char>(v);
return *this;
}
Bytes& Bytes::operator<<(u8 v) {
insertData<u8>(v);
return *this;
}
Bytes& Bytes::operator<<(u16 v) {
insertData<u16>(htons(v));
return *this;
}
Bytes& Bytes::operator<<(u32 v) {
insertData<u32>(htonl(v));
return *this;
}
Bytes& Bytes::operator<<(u64 v) {
insertData<u64>(v);
return *this;
}
Bytes& Bytes::operator<<(const Bytes& v) {
data.reserve(data.size() + v.size());
for(size_t byte=0; byte < v.size(); byte++)
data.push_back(v[byte]);
return *this;
}
Bytes& Bytes::operator<<(const char* str) {
unsigned len = strlen(str);
data.reserve(data.size() + len);
for(unsigned byte=0; byte < len; byte++)
data.push_back(str[byte]);
return *this;
}
Bytes& Bytes::operator>>(u8& v) {
extractData<u8>(v);
return *this;
}
Bytes& Bytes::operator>>(u16& v) {
extractData<u16>(v);
htons(v);
return *this;
}
Bytes& Bytes::operator>>(u32& v) {
extractData<u32>(v);
htonl(v);
return *this;
}
Bytes& Bytes::operator>>(u64& v) {
extractData<u64>(v);
return *this;
}
Bytes& Bytes::operator>>(char& v) {
extractData<char>(v);
return *this;
}
void Bytes::operator=(const Bytes& oth) {
firstIndex=0;
data.clear();
data.reserve(oth.size());
for(size_t pos=0; pos < oth.size(); pos++)
data.push_back(oth[pos]);
}
Bytes Bytes::sub(size_t beg, size_t len) const {
if(beg+len >= size())
throw OutOfRange();
Bytes out;
for(size_t byte=0; byte < len; byte++)
out << (*this)[byte+beg];
return out;
}
void Bytes::writeToBuffer(void* buff, unsigned maxLen) const {
if(maxLen == 0)
maxLen = size();
for(size_t pos=0; pos < min((size_t)maxLen, size()); pos++)
((u8*)buff)[pos] = (*this)[pos];
}
char charOfDigit(u8 dig) {
if(dig < 10)
return dig+'0';
return dig+'A'-10;
}
void hexdumpByte(Bytes& dest, u8 b) {
dest << charOfDigit(b/16) << charOfDigit(b & 0xf);
}
void Bytes::hexdump(Bytes& dest) const {
for(unsigned pos=0; pos < size(); pos++) {
hexdumpByte(dest, (*this)[pos]);
if(pos % 32 == 31)
dest << '\n';
else if(pos % 4 == 3)
dest << ' ';
}
if(size() % 32 != 0)
dest << '\n';
}
template<typename T> void Bytes::extractData(T& v) {
if(size() < sizeof(v))
throw OutOfRange();
for(size_t byte=0; byte < sizeof(v); byte++)
v = (v<<8) | data[firstIndex+byte];
firstIndex += sizeof(v);
}
template<typename T> void Bytes::insertData(T v) {
for(int byte=sizeof(v)-1; byte >= 0; byte--) {
data.push_back((u8) (v >> (8*byte)));
}
}

81
Bytes.h Normal file
View file

@ -0,0 +1,81 @@
/***************************************************************************
* By Théophile Bastian, 2017
* M1 Network course project at ENS Cachan, Juliusz Chroboczek.
* License: WTFPL v2 <http://www.wtfpl.net/>
**************************************************************************/
/***************************************************************************
* Reused from a former project, by Théophile Bastian & Nathanaël Courant
* git@github.com:tobast/sysres-pikern.git
**************************************************************************/
#pragma once
#include <cstdlib>
#include <vector>
#include <exception>
#include "data.h"
class Bytes {
public:
class OutOfRange : public std::exception {};
/// Exception -- thrown by operator[]
Bytes();
Bytes(const void* buff, size_t len);
/// Initializes the object with [buff], reading [len] bytes.
u8& operator[](const size_t pos);
u8 operator[](const size_t pos) const;
/// Accesses the [pos]th element. Throws OutOfRange if pos >= size()
u16 ushortAt(const size_t pos) const;
/// Accesses the 16 bits unsigned integet at position [pos].
/// Throws OutOfRange if [pos+15] >= size().
size_t size() const;
/// Returns the number of chars in the object.
Bytes& operator<<(char v);
Bytes& operator<<(u8 v);
Bytes& operator<<(u16 v);
Bytes& operator<<(u32 v);
Bytes& operator<<(u64 v);
Bytes& operator<<(const Bytes& v);
Bytes& operator<<(const char* str);
/// Appends the given data to the vector. Returns *this to allow
/// chaining.
Bytes& operator>>(u8& v);
Bytes& operator>>(u16& v);
Bytes& operator>>(u32& v);
Bytes& operator>>(u64& v);
Bytes& operator>>(char& c);
/// Extracts the given data type from the vector. Returns *this to
/// allow chaining.
void operator=(const Bytes& oth);
/// Copies the given Bytes object into its own data.
Bytes sub(size_t beg, size_t len) const;
/// Extracts a sub-bytes, beginning at character [beg], containing
/// [len] chars. If [beg+len] >= [size()], throws OutOfRange.
void writeToBuffer(void* buff, unsigned maxLen=0) const;
/// Writes the contents of this array in the buffer [buff].
/// The user must provide a buffer of at least [size()] bytes.
/// If [maxLen] != 0, writes at most [maxLen] bytes to buff.
void hexdump(Bytes& dest) const;
/// Dumps the packet to [dest] in hexadecimal form.
private: //meth
template<typename T> void insertData(T v);
template<typename T> void extractData(T& v);
private:
std::vector<u8> data;
size_t firstIndex;
};

17
Makefile Normal file
View file

@ -0,0 +1,17 @@
CXX=g++
CXXFLAGS=-Wall -Wextra -Werror -pedantic -std=c++14 -O2
CXXLIBS=-lpthread
OBJS = Bytes.o main.o protocol.o
TARGET = jeanhubert
all: $(TARGET)
$(TARGET): $(OBJS)
$(CXX) $(CXXLIBS) $(CXXFLAGS) $^ -o $@
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
rm -rf $(OBJS) $(TARGET)

24
data.h Normal file
View file

@ -0,0 +1,24 @@
/***************************************************************************
* By Théophile Bastian, 2017
* M1 Network course project at ENS Cachan, Juliusz Chroboczek.
* License: WTFPL v2 <http://www.wtfpl.net/>
**************************************************************************/
#pragma once
#include <cstdint>
#include <arpa/inet.h>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef struct sockaddr_in6 SockAddr;
struct Neighbour {
Neighbour(u64 id, const SockAddr& addr) : id(id), addr(addr) {}
u64 id;
SockAddr addr;
};

5
headcomment.txt Normal file
View file

@ -0,0 +1,5 @@
/***************************************************************************
* By Théophile Bastian, 2017
* M1 Network course project at ENS Cachan, Juliusz Chroboczek.
* License: WTFPL v2 <http://www.wtfpl.net/>
**************************************************************************/

41
main.cpp Normal file
View file

@ -0,0 +1,41 @@
/***************************************************************************
* By Théophile Bastian, 2017
* M1 Network course project at ENS Cachan, Juliusz Chroboczek.
* License: WTFPL v2 <http://www.wtfpl.net/>
**************************************************************************/
#include "data.h"
#include "protocol.h"
#include "nw_constants.h"
#include <cstring>
#include <cstdio>
#include <cstdlib>
int main(int /*argc*/, char** /*argv*/) {
srand(time(NULL)+42);
SockAddr addr;
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = csts::DEFAULT_PORT;
u64 myId=0;
for(int i=0; i < 8; i++) {
myId <<= 8;
myId += rand() % (1<<8);
}
SockAddr jch_addr;
memset(&jch_addr, 0, sizeof(jch_addr));
jch_addr.sin6_family = AF_INET6;
jch_addr.sin6_port = csts::DEFAULT_PORT;
if(inet_pton(AF_INET6, "::ffff::81.194.27.155", &jch_addr.sin6_addr)) {
perror("Cannot convert JCh address");
exit(1);
}
Protocol proto(addr, myId);
proto.sendEmpty(jch_addr);
return 0;
}

35
nw_constants.h Normal file
View file

@ -0,0 +1,35 @@
/***************************************************************************
* By Théophile Bastian, 2017
* M1 Network course project at ENS Cachan, Juliusz Chroboczek.
* License: WTFPL v2 <http://www.wtfpl.net/>
**************************************************************************/
#pragma once
#include "data.h"
namespace csts {
const u8 MAGIC = 57;
const u8 VERSION = 0;
const u8 TLV_PAD1 = 0;
const u8 TLV_PADN = 1;
const u8 TLV_IHU = 2;
const u8 TLV_NR = 3;
const u8 TLV_NEIGH = 4;
const u8 TLV_DATA = 5;
const u8 TLV_IHAVE = 6;
const u8 TLV_DATA_TEXT = 32;
const u8 TLV_DATA_PNG = 33;
const u8 TLV_DATA_JPG = 34;
const int TIMEOUT_UNIDIR = 100*1000; // ms
const int TIMEOUT_SYM_RECV = 150*1000; // ms
const int TIMEOUT_SYM_IHU = 300*1000; // ms
const u16 DEFAULT_PORT = 1192;
}

212
protocol.cpp Normal file
View file

@ -0,0 +1,212 @@
/***************************************************************************
* 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;
}

88
protocol.h Normal file
View file

@ -0,0 +1,88 @@
/***************************************************************************
* By Théophile Bastian, 2017
* M1 Network course project at ENS Cachan, Juliusz Chroboczek.
* License: WTFPL v2 <http://www.wtfpl.net/>
**************************************************************************/
#pragma once
#include <sys/socket.h>
#include <arpa/inet.h>
#include <queue>
#include <thread>
#include <mutex>
#include <unordered_map>
#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<Neighbour>& neigh);
void sendNeighbours(u64 id,
const std::vector<Neighbour>& 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<AvailPacket> availPackets;
std::unordered_map<u64, SockAddr> addrMap;
};