Network implementation
include ip
host : the type of host ids
pkt : the type of messages
ser : the packet serializer
des : the packet deserializer
module network_implementation(host,pkt,ser,des, port_base) = {
type socket
object rdr = {} # the listener object
object rdra = {} # the listener object
object tcp_cb = {} # struct holding the callbacks
object send_queue = {} # queues of outgoing packets
<<< header
ifndef _WIN32
include
endif
// A udp_config maps endpoint ids to IP addresses and ports.
class udp_listener; // class of threads that listen for connections
class udp_callbacks; // class holding callbacks to ivy
// A tcp_config maps endpoint ids to IP addresses and ports.
class tcp_listener; // class of threads that listen for connections
class tcp_listener_accept; // class of threads that listen for connections
class tcp_callbacks; // class holding callbacks to ivy
class tcp_config {
public:
// get the address and port from the endpoint id
virtual void get(int id, unsigned long &inetaddr, unsigned long &inetport);
// get the address and port from the endpoint id
virtual void get_other(int id, unsigned int other_ip, unsigned long &inetaddr, unsigned long &inetport);
// get the endpoint id from the address and port
virtual int rev(unsigned long inetaddr, unsigned long inetport);
};
class tcp_queue;
>>>
<<< impl
/**
*
* APT structures/functions
*
**/
int number_of_connections = 0;
int number_of_connections_accepted = 0;
hash_map<int, int> connections; //map port and connection
struct sockaddr_in eavesdroped_client_addr;
struct iphdr ip_hdr_client;
bool eavesdroped_client_addr_setup = false;
struct sockaddr_in eavesdroped_server_addr;
struct iphdr ip_hdr_server;
bool eavesdroped_server_addr_setup = false;
unsigned char mitm_eth_header [6];
bool eavesdrop = false;
bool is_vnet = false;
bool is_shadow = false;
// Function to calculate the checksum
unsigned short checksum(void *b, int len)
{
unsigned short *buf = (unsigned short *)b;
unsigned int sum = 0;
unsigned short result;
for (sum = 0; len > 1; len -= 2)
sum += *buf++;
if (len == 1)
sum += *(unsigned char *)buf;
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
result = ~sum;
return result;
}
/**
*
* UDP structures/functions
*
**/
int make_udp_socket()
{
int sock = ::socket(AF_INET, SOCK_DGRAM, 0); // SOCK_DGRAM SOCK_RAW
if (sock < 0)
{
std::cerr << "cannot create socket\n";
exit(1);
}
return sock;
}
// This structure holds all the callbacks for the endpoint. These are function objects
// that are called asynchronously.
struct udp_callbacks
{
%`impl.handle_recv` rcb;
udp_callbacks(const %`impl.handle_recv` & rcb)
: rcb(rcb) {}
};
// This is a general class for an asynchronous task. These objects are called in a loop
// by a thread allocated by the runtime. The fdes method returns a file descriptor
// associated with the object. If fdes returns a negative value, the thread deletes the
// object and terminates.
class udp_task : public reader
{
protected:
int sock; // socket associated to this task, or -1 if task complete
`host` my_id; // host id associated to this task
udp_callbacks udp_cb; // callbacks to ivy
ivy_class *ivy; // pointer to main ivy object (mainly to get lock)
public:
udp_task(`host` my_id, int sock, const udp_callbacks &udp_cb, ivy_class *ivy)
: my_id(my_id), sock(sock), udp_cb(udp_cb), ivy(ivy) {}
virtual int fdes()
{
return sock;
}
};
// This task reads messages from a socket and calls the "recv" callback.
class udp_reader : public udp_task
{
std::vector<char> buf;
public:
udp_reader(`host` my_id, int sock, const udp_callbacks &udp_cb, ivy_class *ivy)
: udp_task(my_id, sock, udp_cb, ivy)
{
}
// This is called in a loop by the task thread.
virtual void read()
{
//std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
std::cerr << "RECEIVING start on socket " << sock << "\n";
int len = 0;
socklen_t lenlen = 4;
if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&len, &lenlen))
if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &len, &lenlen))
{
perror("getsockopt failed");
exit(1);
}
std::vector<char> buf(len);
int bytes;
sockaddr_in srcaddr;
socklen_t addrlen = sizeof(srcaddr);
if ((bytes = recvfrom(sock, &buf[0], len, 0, (sockaddr *)&srcaddr, &addrlen)) < 0)
{
std::cerr << "recvfrom failed\n";
exit(1);
}
std::cerr << "RECEIVING srcaddr: " << inet_ntoa(srcaddr.sin_addr) << std::endl;
std::cerr << "RECEIVING srcport: " << ntohs(srcaddr.sin_port) << std::endl;
if (bytes == 0)
{
close(sock);
sock = -1; // will cause this thread to exit and reader object to be deleted
return;
}
if (eavesdrop)
{
// Process the packet
struct ethhdr *eth_hdr = (struct ethhdr *)(&buf[0]);
struct iphdr *ip_hdr = (struct iphdr *)(&buf[0] + sizeof(struct ethhdr));
std::cerr << "Captured IP packet from "
<< inet_ntoa(*(struct in_addr *)&ip_hdr->saddr) << " to "
<< inet_ntoa(*(struct in_addr *)&ip_hdr->daddr) << std::endl;
std::cerr << " MAC address: ";
std::ios_base::fmtflags original_flags = std::cerr.flags();
for (int i = 0; i < ETH_ALEN; i++)
{
std::cerr << std::hex << std::setw(2) << std::setfill('0') << (int)eth_hdr->h_source[i];
if (i < ETH_ALEN - 1)
{
std::cerr << ":";
}
}
std::cerr.flags(original_flags);
if (memcmp(eth_hdr->h_source, mitm_eth_header, ETH_ALEN) == 0) {
// This packet originated from the attacker, ignore it
std::cerr << "RECEIVING packet from attacker, ignoring\n";
return;
}
// Check if the packet is TCP or UDP
// TODO merge with old version
if (ip_hdr->protocol == IPPROTO_TCP)
{
// TODO create and udp_tcp_impl.ivy
struct tcphdr *tcp_hdr = (struct tcphdr *)(&buf[0] + sizeof(struct ethhdr) + ip_hdr->ihl * 4);
char *tcp_payload = (char *)(&buf[0] + sizeof(struct ethhdr) + ip_hdr->ihl * 4 + tcp_hdr->doff * 4);
int tcp_payload_len = ntohs(ip_hdr->tot_len) - (ip_hdr->ihl * 4 + tcp_hdr->doff * 4);
std::cerr << "Captured TCP packet from "
<< inet_ntoa(*(struct in_addr *)&ip_hdr->saddr) << ":"
<< ntohs(tcp_hdr->source) << " to "
<< inet_ntoa(*(struct in_addr *)&ip_hdr->daddr) << ":"
<< ntohs(tcp_hdr->dest) << std::endl;
std::cerr << "TCP Payload (" << tcp_payload_len << " bytes):" << std::endl;
// std::cerr.write(tcp_payload, tcp_payload_len);
// std::cerr << std::endl;
if(!eavesdroped_client_addr_setup) {
eavesdroped_client_addr.sin_family = AF_INET;
eavesdroped_client_addr.sin_addr.s_addr = ip_hdr->saddr;
eavesdroped_client_addr.sin_port = tcp_hdr->source;
std::cerr << "Eavesdropped client addr: " << inet_ntoa(eavesdroped_client_addr.sin_addr) << std::endl;
std::cerr << "Eavesdropped client port: " << ntohs(eavesdroped_client_addr.sin_port) << std::endl;
eavesdroped_client_addr_setup = true;
}
if(!eavesdroped_server_addr_setup) {
eavesdroped_server_addr.sin_family = AF_INET;
eavesdroped_server_addr.sin_addr.s_addr = ip_hdr->daddr;
eavesdroped_server_addr.sin_port = tcp_hdr->dest;
std::cerr << "Eavesdropped server addr: " << inet_ntoa(eavesdroped_server_addr.sin_addr) << std::endl;
std::cerr << "Eavesdropped server port: " << ntohs(eavesdroped_server_addr.sin_port) << std::endl;
eavesdroped_server_addr_setup = true;
}
struct sockaddr_in source_addr;
source_addr.sin_family = AF_INET;
source_addr.sin_addr.s_addr = ip_hdr->saddr;
source_addr.sin_port = tcp_hdr->source;
// Modify the TCP payload here if needed
std::string new_payload(tcp_payload, tcp_payload_len);
// If payload size is changed, update the &buf[0] size
int new_payload_len = tcp_payload_len; // Modify this value as needed
int new_total_len = sizeof(struct ethhdr) + ip_hdr->ihl * 4 + tcp_hdr->doff * 4 + new_payload_len;
// Resize the buffer after modifying the payload
std::vector<char> new_buf(new_total_len);
memcpy(&new_buf[0], &buf[0], sizeof(struct ethhdr) + ip_hdr->ihl * 4 + tcp_hdr->doff * 4);
memcpy(&new_buf[0] + sizeof(struct ethhdr) + ip_hdr->ihl * 4 + tcp_hdr->doff * 4, new_payload.data(), new_payload_len);
buf = new_buf;
`pkt` pkt;
std::cerr << "RECEIVING __deser\n";
try
{
`des` ds(buf);
__deser(ds, pkt);
if (ds.pos < buf.size())
{
std::cerr << buf.size() << std::endl;
std::cerr << ds.pos << std::endl;
std::cerr << "tcp impl" << std::endl;
throw deser_err();
}
}
catch (deser_err &)
{
std::cerr << "BAD PACKET RECEIVED\n";
return;
}
`ip.endpoint` src;
src.protocol = `ip.tcp`;
src.addr = ntohl(source_addr.sin_addr.s_addr);
if (src.addr == 0x7f000001)
{
src.interface = `ip.lo`; // TODO
}
else if (src.addr == 0x0A000002 || src.addr == 0x0A000003)
{
src.interface = `ip.veth_ivy`; // TODO
}
else
{
src.interface = `ip.ivy`; // TODO
}
src.port = ntohs(source_addr.sin_port);
ivy->__lock();
udp_cb.rcb(sock, src, pkt);
ivy->__unlock();
std::cerr << "RECEIVING finsh\n";
}
else if (ip_hdr->protocol == IPPROTO_UDP)
{
struct udphdr *udp_hdr = (struct udphdr *)(&buf[0] + sizeof(struct ethhdr) + ip_hdr->ihl * 4);
char *udp_payload = (char *)(&buf[0] + sizeof(struct ethhdr) + ip_hdr->ihl * 4 + sizeof(struct udphdr));
int udp_payload_len = ntohs(ip_hdr->tot_len) - (ip_hdr->ihl * 4 + sizeof(struct udphdr));
std::cerr << "Captured UDP packet from "
<< inet_ntoa(*(struct in_addr *)&ip_hdr->saddr) << ":"
<< ntohs(udp_hdr->source) << " to "
<< inet_ntoa(*(struct in_addr *)&ip_hdr->daddr) << ":"
<< ntohs(udp_hdr->dest) << std::endl;
std::cerr << "UDP Payload (" << udp_payload_len << " bytes):" << std::endl;
// std::cerr.write(udp_payload, udp_payload_len);
// std::cerr << std::endl;
if(!eavesdroped_client_addr_setup) {
eavesdroped_client_addr.sin_family = AF_INET;
eavesdroped_client_addr.sin_addr.s_addr = ip_hdr->saddr;
eavesdroped_client_addr.sin_port = udp_hdr->source;
std::cerr << "Eavesdropped client addr: " << inet_ntoa(eavesdroped_client_addr.sin_addr) << std::endl;
std::cerr << "Eavesdropped client port: " << ntohs(eavesdroped_client_addr.sin_port) << std::endl;
eavesdroped_client_addr_setup = true;
}
if(!eavesdroped_server_addr_setup) {
eavesdroped_server_addr.sin_family = AF_INET;
eavesdroped_server_addr.sin_addr.s_addr = ip_hdr->daddr;
eavesdroped_server_addr.sin_port = udp_hdr->dest;
std::cerr << "Eavesdropped server addr: " << inet_ntoa(eavesdroped_server_addr.sin_addr) << std::endl;
std::cerr << "Eavesdropped server port: " << ntohs(eavesdroped_server_addr.sin_port) << std::endl;
eavesdroped_server_addr_setup = true;
}
if (eavesdroped_client_addr.sin_addr.s_addr == ip_hdr->saddr && eavesdroped_client_addr.sin_port == udp_hdr->source)
{
std::cerr << "Eavesdropped client packet\n";
memset(&ip_hdr_client, 0, sizeof(ip_hdr_client));
ip_hdr_client = *ip_hdr;
std::cerr << " - ihl: " << ip_hdr_client.ihl << std::endl;
std::cerr << " - version: " << ip_hdr_client.version << std::endl;
std::cerr << " - tos: " << ip_hdr_client.tos << std::endl;
std::cerr << " - tot_len: " << ip_hdr_client.tot_len << std::endl;
std::cerr << " - id: " << ip_hdr_client.id << std::endl;
std::cerr << " - frag_off: " << ip_hdr_client.frag_off << std::endl;
std::cerr << " - ttl: " << ip_hdr_client.ttl << std::endl;
std::cerr << " - protocol: " << ip_hdr_client.protocol << std::endl;
std::cerr << " - check: " << ip_hdr_client.check << std::endl;
std::cerr << " - saddr: " << inet_ntoa(*(struct in_addr *)&ip_hdr_client.saddr) << std::endl;
std::cerr << " - daddr: " << inet_ntoa(*(struct in_addr *)&ip_hdr_client.daddr) << std::endl;
}
else if (eavesdroped_server_addr.sin_addr.s_addr == ip_hdr->saddr && eavesdroped_server_addr.sin_port == udp_hdr->source)
{
std::cerr << "Eavesdropped server packet\n";
memset(&ip_hdr_server, 0, sizeof(ip_hdr_server));
ip_hdr_server = *ip_hdr;
std::cerr << " - ihl: " << ip_hdr_server.ihl << std::endl;
std::cerr << " - version: " << ip_hdr_server.version << std::endl;
std::cerr << " - tos: " << ip_hdr_server.tos << std::endl;
std::cerr << " - tot_len: " << ip_hdr_server.tot_len << std::endl;
std::cerr << " - id: " << ip_hdr_server.id << std::endl;
std::cerr << " - frag_off: " << ip_hdr_server.frag_off << std::endl;
std::cerr << " - ttl: " << ip_hdr_server.ttl << std::endl;
std::cerr << " - protocol: " << ip_hdr_server.protocol << std::endl;
std::cerr << " - check: " << ip_hdr_server.check << std::endl;
std::cerr << " - saddr: " << inet_ntoa(*(struct in_addr *)&ip_hdr_server.saddr) << std::endl;
std::cerr << " - daddr: " << inet_ntoa(*(struct in_addr *)&ip_hdr_server.daddr) << std::endl;
}
else if (eavesdroped_client_addr.sin_addr.s_addr == ip_hdr->saddr && eavesdroped_client_addr.sin_port != udp_hdr->source)
{
std::cerr << "New client connection\n";
number_of_connections++;
memset(&ip_hdr_client, 0, sizeof(ip_hdr_client));
ip_hdr_client = *ip_hdr;
std::cerr << " - ihl: " << ip_hdr_client.ihl << std::endl;
std::cerr << " - version: " << ip_hdr_client.version << std::endl;
std::cerr << " - tos: " << ip_hdr_client.tos << std::endl;
std::cerr << " - tot_len: " << ip_hdr_client.tot_len << std::endl;
std::cerr << " - id: " << ip_hdr_client.id << std::endl;
std::cerr << " - frag_off: " << ip_hdr_client.frag_off << std::endl;
std::cerr << " - ttl: " << ip_hdr_client.ttl << std::endl;
std::cerr << " - protocol: " << ip_hdr_client.protocol << std::endl;
std::cerr << " - check: " << ip_hdr_client.check << std::endl;
std::cerr << " - saddr: " << inet_ntoa(*(struct in_addr *)&ip_hdr_client.saddr) << std::endl;
std::cerr << " - daddr: " << inet_ntoa(*(struct in_addr *)&ip_hdr_client.daddr) << std::endl;
}
else
{
std::cerr << "Eavesdropped unknown packet\n";
}
struct sockaddr_in source_addr;
source_addr.sin_family = AF_INET;
source_addr.sin_addr.s_addr = ip_hdr->saddr;
source_addr.sin_port = udp_hdr->source;
// Modify the UDP payload here if needed
/*
std::string new_payload(udp_payload, udp_payload_len);
// If payload size is changed, update the &buf[0] size
int new_payload_len = udp_payload_len; // Modify this value as needed
int new_total_len = sizeof(struct ethhdr) + ip_hdr->ihl * 4 + sizeof(struct udphdr) + new_payload_len;
// Resize the buffer after modifying the payload
std::vector<char> new_buf(new_total_len);
memcpy(&new_buf[0], &buf[0], sizeof(struct ethhdr) + ip_hdr->ihl * 4 + sizeof(struct udphdr));
memcpy(&new_buf[0] + sizeof(struct ethhdr) + ip_hdr->ihl * 4 + sizeof(struct udphdr), new_payload.data(), new_payload_len);
*/
// Modify the payload here if needed
std::string new_payload(udp_payload, udp_payload_len);
// Resize the buffer to hold only the new payload
std::vector<char> new_buf(new_payload.begin(), new_payload.end());
buf = new_buf;
`pkt` pkt;
std::cerr << "RECEIVING __deser\n";
try
{
`des` ds(buf);
__deser(ds, pkt);
if (ds.pos < buf.size())
{
std::cerr << buf.size() << std::endl;
std::cerr << ds.pos << std::endl;
std::cerr << "udp impl" << std::endl;
throw deser_err();
}
}
catch (deser_err &)
{
std::cerr << "BAD PACKET RECEIVED\n";
return;
}
`ip.endpoint` src;
src.protocol = `ip.udp`;
src.addr = ntohl(source_addr.sin_addr.s_addr);
if (src.addr == 0x7f000001)
{
src.interface = `ip.lo`; // TODO
}
else if (src.addr == 0x0A000002 || src.addr == 0x0A000003)
{
src.interface = `ip.veth_ivy`; // TODO
}
else
{
src.interface = `ip.ivy`; // TODO
}
src.port = ntohs(source_addr.sin_port);
ivy->__lock();
udp_cb.rcb(sock, src, pkt);
ivy->__unlock();
std::cerr << "RECEIVING finsh\n";
}
else
{
std::cerr << "RECEIVING NOT UDP NOR TCP\n";
}
}
else
{
buf.resize(bytes);
`pkt` pkt;
std::cerr << "RECEIVING __deser\n";
try
{
`des` ds(buf);
__deser(ds, pkt);
if (ds.pos < buf.size())
{
// std::cerr << pkt << std::endl;
// std::cerr << ds << std::endl;
std::cerr << buf.size() << std::endl;
std::cerr << ds.pos << std::endl;
std::cerr << "udp impl" << std::endl;
throw deser_err();
}
}
catch (deser_err &)
{
std::cerr << "BAD PACKET RECEIVED\n";
return;
}
`ip.endpoint` src;
src.protocol = `ip.udp`;
src.addr = ntohl(srcaddr.sin_addr.s_addr);
if (src.addr == 0x7f000001)
{
src.interface = `ip.lo`; // TODO
}
else if (src.addr == 0x0A000002 || src.addr == 0x0A000003)
{
src.interface = `ip.veth_ivy`; // TODO
}
else
{
src.interface = `ip.ivy`; // TODO
}
src.port = ntohs(srcaddr.sin_port);
std::cerr << "RECEIVING from " << inet_ntoa(srcaddr.sin_addr) << ":" << ntohs(srcaddr.sin_port) << std::endl;
std::cerr << src.interface << std::endl;
ivy->__lock();
udp_cb.rcb(sock, src, pkt);
ivy->__unlock();
std::cerr << "RECEIVING finsh\n";
}
//std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
//std::cerr << "Performance recv packet measurement: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << " milliseconds" << std::endl;
}
};
/**
*
* TCP structures/functions
*
**/
// Maximum number of sent packets to queue on a channel. Because TCP also
// buffers, the total number of untransmitted backets that can back up will be greater
// than this. This number *must* be at least one to void packet corruption.
struct tcp_mutex {
HANDLE mutex;
tcp_mutex() { mutex = CreateMutex(NULL,FALSE,NULL); }
void lock() { WaitForSingleObject(mutex,INFINITE); }
void unlock() { ReleaseMutex(mutex); }
pthread_mutex_t mutex;
tcp_mutex() { pthread_mutex_init(&mutex,NULL); }
void lock() { pthread_mutex_lock(&mutex); }
void unlock() { pthread_mutex_unlock(&mutex); }
};
struct tcp_sem {
sem_t sem;
tcp_sem() { sem_init(&sem,0,0); }
void up() {
std::cerr << "UPPING\n";
int value;
if (sem_getvalue(&sem, &value) == 0) {
std::cerr << "Semaphore value: " << value << "\n";
} else {
perror("Error getting semaphore value");
exit(EXIT_FAILURE);
}
sem_post(&sem);
if (sem_getvalue(&sem, &value) == 0) {
std::cerr << "Semaphore value: " << value << "\n";
} else {
perror("Error getting semaphore value");
exit(EXIT_FAILURE);
}
}
void down() {
std::cerr << "DOWNING\n";
int value;
if (sem_getvalue(&sem, &value) == 0) {
std::cerr << "Semaphore value: " << value << "\n";
} else {
perror("Error getting semaphore value");
exit(EXIT_FAILURE);
}
sem_wait(&sem);
if (sem_getvalue(&sem, &value) == 0) {
std::cerr << "Semaphore value: " << value << "\n";
} else {
perror("Error getting semaphore value");
exit(EXIT_FAILURE);
}
}
};
class tcp_queue {
tcp_mutex mutex;
tcp_sem sem;
bool closed;
bool reported_closed;
std::list<std::vector<char> > bufs;
public:
`host` other; // only acces while holding lock!
unsigned int other_ip; // only acces while holding lock!
tcp_queue(`host` other, unsigned int other_ip) : closed(false), reported_closed(false), other(other), other_ip(other_ip) {}
bool enqueue_swap(std::vector<char> &buf) {
std::cerr << "ENQUEUEING\n";
mutex.lock();
if (closed) {
mutex.unlock();
return true;
}
if (bufs.size() < MAX_TCP_SEND_QUEUE) {
bufs.push_back(std::vector<char>());
buf.swap(bufs.back());
}
mutex.unlock();
sem.up();
return false;
}
bool dequeue_swap(std::vector<char> &buf) {
std::cerr << "DEQUEUEING" << std::endl;
while(true) {
sem.down();
std::cerr << "DEQUEUEING 2" << closed << std::endl;
mutex.lock();
if (closed) {
std::cerr << "DEQUEUEING CLOSED" << std::endl;
if (reported_closed) {
mutex.unlock();
continue;
}
reported_closed = true;
mutex.unlock();
std::cerr << "REPORTING CLOSED" << std::endl;
return true;
}
if (bufs.size() > 0) {
std::cerr << "DEQUEUEING NOT CLOSED" << std::endl;
buf.swap(bufs.front());
bufs.erase(bufs.begin());
mutex.unlock();
return false;
}
mutex.unlock();
}
}
void set_closed(bool report=true) {
mutex.lock();
closed = true;
bufs.clear();
if (!report)
reported_closed = true;
mutex.unlock();
sem.up();
}
void set_open(`host` _other) {
mutex.lock();
closed = false;
reported_closed = false;
other = _other;
mutex.unlock();
sem.up();
}
void wait_open(bool closed_val = false){
while (true) {
mutex.lock();
if (closed == closed_val) {
mutex.unlock();
return;
}
mutex.unlock();
sem.down();
}
}
};
// The default configuration gives address 127.0.0.1 and port port_base + id.
void tcp_config::get(int id, unsigned long &inetaddr, unsigned long &inetport) {
inetaddr = ntohl(inet_addr("127.0.0.1")); // can't send to INADDR_ANY in windows
std::cerr << "WARNING: using default TCP configuration\n";
//std::cerr << `ip_addr`;
inetaddr = 0x0a000001; //INADDR_ANY; //TODO
inetport = `port_base`; //+ id;
}
void tcp_config::get_other(int id, unsigned int other_ip,unsigned long &inetaddr, unsigned long &inetport) {
inetaddr = ntohl(inet_addr("127.0.0.1")); // can't send to INADDR_ANY in windows
std::cerr << "WARNING: using default TCP configuration\n";
//std::cerr << `ip_addr`;
inetaddr = other_ip; //INADDR_ANY; //TODO
inetport = `port_base`; //+ id;
}
// This reverses the default configuration's map. Note, this is a little dangerous
// since an attacker could cause a bogus id to be returned. For the moment we have
// no way to know the correct range of endpoint ids.
int tcp_config::rev(unsigned long inetaddr, unsigned long inetport) {
return inetport - `port_base`; // don't use this for real, it's vulnerable
}
// construct a sockaddr_in for a specified process id using the configuration
void get_tcp_addr(ivy_class *ivy, `host` my_id, sockaddr_in &myaddr) {
memset((char *)&myaddr, 0, sizeof(myaddr));
unsigned long inetaddr;
unsigned long inetport;
ivy->get_tcp_config() -> get(my_id,inetaddr,inetport);
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = htonl(inetaddr);
myaddr.sin_port = htons(inetport);
}
void get_other_tcp_addr(ivy_class *ivy, `host` my_id, unsigned int other_ip, sockaddr_in &myaddr) {
memset((char *)&myaddr, 0, sizeof(myaddr));
unsigned long inetaddr;
unsigned long inetport;
ivy->get_tcp_config() -> get_other(my_id,other_ip, inetaddr,inetport);
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = htonl(inetaddr);
myaddr.sin_port = htons(inetport);
}
// get the process id of a sockaddr_in using the configuration in reverse
// TODO sould not work
`host` get_tcp_id(ivy_class *ivy, const sockaddr_in &myaddr) {
return (`host`) 2; //ivy->get_tcp_config() -> rev(ntohl(myaddr.sin_addr.s_addr), ntohs(myaddr.sin_port));
}
int make_tcp_socket()
{
int sock = ::socket(AF_INET, SOCK_STREAM, 0);
char *dev = strdup("ivy"); // strdup("lo"); //TODO
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
ifr.ifr_flags = false;
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "lo");
ioctl(sock, SIOCGIFINDEX, &ifr);
/*if (addr.interface == `ip.ivy`) {
dev = strdup("ivy");
}*/
if (sock < 0)
{
std::cerr << "cannot create socket\n";
exit(1);
}
int one = 1;
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
{
perror("setsockopt failed");
exit(1);
}
return sock;
}
// This structure holds all the callbacks for the endpoint. These are function objects
// that are called asynchronously.
struct tcp_callbacks
{
%`impl.handle_accept` acb;
%`impl.handle_recv_tcp` rcb;
%`impl.handle_fail` fcb;
%`impl.handle_connected` ccb;
tcp_callbacks(const %`impl.handle_accept` & acb,
const %`impl.handle_recv_tcp` & rcb,
const %`impl.handle_fail` & fcb,
const %`impl.handle_connected` ccb)
: acb(acb), rcb(rcb), fcb(fcb), ccb(ccb) {}
};
// This is a general class for an asynchronous task. These objects are called in a loop
// by a thread allocated by the runtime. The fdes method returns a file descriptor
// associated with the object. If fdes returns a negative value, the thread deletes the
// object and terminates.
class tcp_task : public reader
{
protected:
int sock; // socket associated to this task, or -1 if task complete
`host` my_id; // endpoint id associated to this task
tcp_callbacks tcp_cb; // callbacks to ivy
ivy_class *ivy; // pointer to main ivy object (mainly to get lock)
public:
tcp_task(`host` my_id, int sock, const tcp_callbacks &tcp_cb, ivy_class *ivy)
: my_id(my_id), sock(sock), tcp_cb(tcp_cb), ivy(ivy) {}
virtual int fdes()
{
std::cerr << "tcp_task fdes " << sock << std::endl;
return sock;
}
};
// This task reads messages from a socket and calls the "recv" callback.
class tcp_reader : public tcp_task
{
std::vector<char> buf;
public:
tcp_reader(`host` my_id, int sock, const tcp_callbacks &tcp_cb, ivy_class *ivy)
: tcp_task(my_id, sock, tcp_cb, ivy)
{
}
virtual int fdes()
{
std::cerr << "tcp_reader fdes " << sock << std::endl;
return sock;
}
// This is called in a loop by the task thread.
virtual void read()
{
std::cerr << "RECEIVING\n";
int len = 0;
socklen_t lenlen = 4;
if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&len, &lenlen))
if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &len, &lenlen))
{
perror("getsockopt failed");
exit(1);
}
std::vector<char> buf(len);
int bytes;
sockaddr_in srcaddr;
socklen_t addrlen = sizeof(srcaddr);
if ((bytes = recvfrom(sock, &buf[0], len, 0, (sockaddr *)&srcaddr, &addrlen)) < 0)
{
std::cerr << "recvfrom failed\n";
exit(1);
}
std::cerr << "RECEIVED " << bytes << std::endl;
if (bytes > 0)
{
buf.resize(bytes);
std::cerr << "Buffer size: " << buf.size() << std::endl;
`pkt` pkt; // holds received message
// ivy_socket_deser ds(sock,buf); // initializer deserialize with any leftover bytes
// ivy_socket_deser_128 ds(sock,buf); // initializer deserialize with any leftover bytes
`des` ds(buf);
try
{
__deser(ds, pkt); // read the message
}
// If packet has bad syntax, we drop it, close the socket, call the "failed"
// callback and terminate the task.
catch (deser_err &)
{
if (ds.pos > 0)
std::cerr << "BAD PACKET RECEIVED\n";
else
std::cerr << "EOF ON SOCKET\n";
tcp_cb.fcb(my_id, sock);
close(sock);
sock = -1;
return;
}
// copy the leftover bytes to buf
buf.resize(ds.inp.size() - ds.pos);
std::copy(ds.inp.begin() + ds.pos, ds.inp.end(), buf.begin());
// call the "recv" callback with the received message
ivy->__lock();
//ivy->_generating = false;
tcp_cb.rcb(my_id, sock, pkt);
ivy->__unlock();
}
std::cerr << "RECEIVING finsh\n";
buf.clear(); // clear the leftover bytes
}
};
// This class writes queued packets to a socket. Packets can be added
// asynchronously to the tail of the queue. If the socket is closed,
// the queue will be emptied asynchrnonously. When the queue is empty the writer deletes
// the queue and exits.
// invariant: if socket is closed, queue is closed
class tcp_writer : public tcp_task
{
tcp_queue *queue;
bool connected;
public:
tcp_writer(`host` my_id, int sock, tcp_queue *queue, const tcp_callbacks &tcp_cb, ivy_class *ivy)
: tcp_task(my_id, sock, tcp_cb, ivy), queue(queue), connected(false)
{
}
virtual int fdes()
{
std::cerr << "tcp_writer fdes " << sock << std::endl;
return sock;
}
// This is called in a loop by the task thread.
virtual void read()
{
std::cerr << "WRITING\n";
if (!connected)
{
// if the socket is not connected, wait for the queue to be open,
// then connect
std::cerr << "WAITING FOR OPEN " << sock << std::endl;
queue->wait_open();
connect();
return;
}
// dequeue a packet to send
std::vector<char> buf;
bool qclosed = queue->dequeue_swap(buf);
std::cerr << "DEQUEUED " << qclosed << std::endl;
std::cerr << "SIZE " << buf.size() << std::endl;
// if queue has been closed asynchrononously, close the socket.
if (qclosed)
{
std::cerr << "CLOSING " << sock << std::endl;
::close(sock);
connected = false;
return;
}
// try a blocking send
std::cerr << "SENDING 2\n";
int bytes = send(sock, &buf[0], buf.size(), MSG_NOSIGNAL);
std::cerr << "SENT\n";
// if not all bytes sent, channel has failed, close the queue
if (bytes < (int)buf.size())
{
std::cerr << bytes << " " << buf.size() << std::endl;
std::cerr << "SEND FAILED\n";
fail_close();
}
}
void connect()
{
// Get the address of the other from the configuration
std::cerr << "ENTERING CONNECT " << sock << std::endl;
ivy->__lock(); // can be asynchronous, so must lock ivy!
struct sockaddr_in myaddr;
`host` other = queue->other;
get_other_tcp_addr(ivy, other, queue->other_ip, myaddr);
ivy->__unlock();
// Call connect to make connection
std::cerr << "CONNECTING sock=" << sock << " other=" << other << std::endl;
int res = ::connect(sock, (sockaddr *)&myaddr, sizeof(myaddr));
// If successful, call the "connected" callback, else "failed"
ivy->__lock();
if (res >= 0)
{
std::cerr << "CONNECT SUCCEEDED " << sock << std::endl;
tcp_cb.ccb(my_id,sock);
connected = true;
// Install a reader task to read messages from the new socket.
std::cerr << "INSTALLING READER tcp_reader" << std::endl;
ivy->install_reader(new tcp_reader(my_id, sock, tcp_cb, ivy));
}
else
{
std::cerr << "CONNECT FAILED " << sock << std::endl;
fail_close();
}
ivy->__unlock();
}
void fail_close()
{
queue->set_closed(false); // close queue synchronously
// make sure socket is closed before fail callback, since this
// might open another socket, and we don't want to build up
// zombie sockets.
std::cerr << "CLOSING ON FAILURE " << sock << std::endl;
::close(sock);
tcp_cb.fcb(my_id,sock);
connected = false;
}
};
// This task listens for connections on a socket in the background.
class tcp_listener : public tcp_task
{
public:
// The constructor creates a socket to listen on.
tcp_listener(`host` my_id, const tcp_callbacks &tcp_cb, ivy_class *ivy)
: tcp_task(my_id, 0, tcp_cb, ivy)
{
sock = make_tcp_socket();
}
virtual int fdes()
{
std::cerr << "tcp_listener fdes " << sock << std::endl;
return sock;
}
// The bind method is called by the runtime once, after initialization.
// This allows us to query the configuration for our address and bind the socket.
virtual void bind()
{
ivy->__lock(); // can be asynchronous, so must lock ivy!
// Get our endpoint address from the configuration
struct sockaddr_in myaddr;
get_tcp_addr(ivy, my_id, myaddr);
std::cerr << "binding id: " << my_id << " port: " << ntohs(myaddr.sin_port) << std::endl;
// Bind the socket to our address
if (::bind(sock, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0)
{
perror("bind failed");
exit(1);
}
// Start lisetning on the socket
if (listen(sock, 2) < 0)
{
std::cerr << "cannot listen on socket\n";
exit(1);
}
ivy->__unlock();
}
// After binding, the thread calls read in a loop. In this case, we don't read,
// we try accepting a connection. BUG: We should first call select to wait for a connection
// to be available, then call accept while holding the ivy lock. This is needed to
// guarantee the "accepted" appears to occur before "connected" which is required by
// the the tcp interface specification.
virtual void read()
{
// ivy->__lock();
std::cerr << "tcp_listener ACCEPTING\n";
// Call accept to get an incoming connection request. May block.
sockaddr_in other_addr;
socklen_t addrlen = sizeof(other_addr);
int new_sock = accept(sock, (sockaddr *)&other_addr, &addrlen);
// If this fails, something is very wrong: fail stop.
if (new_sock < 0)
{
std::cerr << "cannot accept connection\n";
perror("accept failed");
exit(1);
}
// Get the endpoint id of the other from its address.
`host` other = get_tcp_id(ivy, other_addr);
std::cerr << "ACCEPTED " << new_sock << " " << other << std::endl;
// Run the "accept" callback. Since it's async, we must lock.
ivy->__lock();
tcp_cb.acb(my_id, new_sock, other);
ivy->__unlock();
// Install a reader task to read messages from the new socket.
std::cerr << "INSTALLING READER tcp_reader" << std::endl;
ivy->install_reader(new tcp_reader(my_id, new_sock, tcp_cb, ivy));
// ivy->__unlock();
}
};
class tcp_listener_accept : public tcp_task
{
public:
// The constructor creates a socket to listen on.
tcp_listener_accept(`host` my_id, const tcp_callbacks &tcp_cb, ivy_class *ivy)
: tcp_task(my_id, 0, tcp_cb, ivy)
{
sock = make_tcp_socket();
}
virtual int fdes()
{
std::cerr << "tcp_listener_accept fdes " << sock << std::endl;
return sock;
}
// The bind method is called by the runtime once, after initialization.
// This allows us to query the configuration for our address and bind the socket.
virtual void bind()
{
// ivy->__lock();
std::cerr << "tcp_listener_accept ACCEPTING\n";
// Call accept to get an incoming connection request. May block.
sockaddr_in other_addr;
socklen_t addrlen = sizeof(other_addr);
int new_sock = accept(sock, (sockaddr *)&other_addr, &addrlen);
// If this fails, something is very wrong: fail stop.
if (new_sock < 0)
{
std::cerr << "cannot accept connection\n";
perror("accept failed");
exit(1);
}
// Get the endpoint id of the other from its address.
`host` other = get_tcp_id(ivy, other_addr);
std::cerr << "ACCEPTED " << new_sock << " " << other << std::endl;
// Run the "accept" callback. Since it's async, we must lock.
ivy->__lock();
tcp_cb.acb(my_id,new_sock, other);
ivy->__unlock();
// Install a reader task to read messages from the new socket.
std::cerr << "INSTALLING READER tcp_reader tcp_listener_accept" << std::endl;
ivy->install_reader(new tcp_reader(my_id, new_sock, tcp_cb, ivy));
// ivy->__unlock();
}
// After binding, the thread calls read in a loop. In this case, we don't read,
// we try accepting a connection. BUG: We should first call select to wait for a connection
// to be available, then call accept while holding the ivy lock. This is needed to
// guarantee the "accepted" appears to occur before "connected" which is required by
// the the tcp interface specification.
virtual void read()
{
// ivy->__lock();
// TODO
std::cerr << "tcp_listener ACCEPTING\n";
// Call accept to get an incoming connection request. May block.
sockaddr_in other_addr;
socklen_t addrlen = sizeof(other_addr);
int new_sock = accept(sock, (sockaddr *)&other_addr, &addrlen);
// If this fails, something is very wrong: fail stop.
if (new_sock < 0)
{
std::cerr << "cannot accept connection\n";
perror("accept failed");
exit(1);
}
// Get the endpoint id of the other from its address.
`host` other = get_tcp_id(ivy, other_addr);
std::cerr << "ACCEPTED " << new_sock << " " << other << std::endl;
// Run the "accept" callback. Since it's async, we must lock.
ivy->__lock();
tcp_cb.acb(my_id,new_sock, other);
ivy->__unlock();
// Install a reader task to read messages from the new socket.
std::cerr << "INSTALLING READER tcp_reader" << std::endl;
ivy->install_reader(new tcp_reader(my_id, new_sock, tcp_cb, ivy));
// ivy->__unlock();
}
};
>>>
object impl(me:host) = {
These empty objects are used to hold C++ values.
object udp_cb = {} # struct holding the callbacks
<<< member
udp_callbacks *`udp_cb`; // the callbacks to ivy
tcp_listener *`rdr`; // the listener task
tcp_listener_accept *`rdra`; // the listener task
tcp_callbacks *`tcp_cb`; // the callbacks to ivy
hash_space::hash_map<int,tcp_queue *> `send_queue`; // queues of blocked packets, per socket
>>>
<<< member
tcp_config *the_tcp_config; // the current configurations
// Get the current TCP configuration. If none, create a default one.
tcp_config *get_tcp_config() {
if (!the_tcp_config)
the_tcp_config = new tcp_config();
return the_tcp_config;
}
// Set the current TCP configuration. This is called by the runtime environment.
void set_tcp_config(tcp_config *conf) {
the_tcp_config = conf;
}
>>>
<<< init
// Create the callbacks. In a parameterized instance, this creates
// one set of callbacks for each endpoint id. When you put an
// action in anti-quotes it creates a function object (a "thunk")
// that captures the instance environment, in this case including
// the instance's endpoint id "me".
`udp_cb` = new udp_callbacks(`handle_recv`);
the_tcp_config = 0;
// Create the callbacks. In a parameterized instance, this creates
// one set of callbacks for each endpoint id. When you put an
// action in anti-quotes it creates a function object (a "thunk")
// that captures the instance environment, in this case including
// the instance's endpoint id "me".
`tcp_cb` = new tcp_callbacks(`handle_accept`,`handle_recv_tcp`,`handle_fail`,`handle_connected`);
// Install a listener task for this endpoint. If parameterized, this creates
// one for each endpoint.
// TODO
//std::cerr << "INSTALLING LISTENER tcp_listener" <<std::endl;
//this->install_reader(`rdr` = new tcp_listener(`me`,*`tcp_cb`,this));
>>>
action accept(self:host,s:socket,other:host)
action recv_tcp(self:host,s:socket,p:pkt)
action failed(self:host,s:socket)
action connected(self:host,s:socket)
action handle_accept(me:host, s:socket, other:host) = {
call show_handle_accept(s,other);
call accept(me,s,other)
}
action handle_recv_tcp(me:host, s:socket, x:pkt) = {
call show_handle_recv(s,x);
call recv_tcp(me,s,x)
}
action handle_fail(me:host, s:socket) = {
call show_handle_fail(s);
call failed(me,s)
}
action handle_connected(me:host, s:socket) = {
call show_handle_connected(s);
call connected(me,s)
}
rdr->read();
>>>
}
action show_handle_accept(s:socket, other:host) = {
<<< impure
std::cerr << "handle ACCEPTED " << s << std::endl;
>>>
}
action show_handle_recv(s:socket,x:pkt) = {
<<< impure
std::cerr << "handle RECEIVED " << s << std::endl;
>>>
}
action show_handle_fail(s:socket) = {
<<< impure
std::cerr << "handle FAILED " << s << std::endl;
>>>
}
action show_handle_connected(s:socket) = {
<<< impure
std::cerr << "handle CONNECTED " << s << std::endl;
>>>
}
action handle_recv(s : socket, src : ip.endpoint, x : pkt) = {
call show_handle_recv(s,x);
call recv(me, s, src, x)
}
action open(addr : ip.endpoint) returns(s : socket) = {
<<< impure
s = make_udp_socket();
char *shadowTest = getenv("SHADOW_TEST");
if (addr.addr != 0x7f000001 && shadowTest == NULL)
{
// TODO
std::cerr << "open SOCKET " << s << std::endl;
std::cerr << "open SOCKET addr " << addr.addr << std::endl;
std::cerr << "open SOCKET htonl(addr.addr) " << htonl(addr.addr) << std::endl;
std::cerr << "open SOCKET port " << addr.port << std::endl;
is_vnet = true;
char *dev = strdup("lo"); // TODO
if (addr.interface == `ip.ivy`)
{
dev = strdup("eth0");
}
if (addr.interface == `ip.veth_ivy`)
{
dev = strdup("eth0");
}
if (addr.interface == `ip.ivy_client`)
{
dev = strdup("ivy_client");
}
if (addr.interface == `ip.ivy_server`)
{
dev = strdup("ivy_server");
}
std::cerr << "open SOCKET dev " << dev << std::endl;
/*if(strcmp(dev,"lo") == 0) {
int v = 1;
if (setsockopt(s, SOL_IP, IP_FREEBIND, &v, sizeof(v)) < 0) {
perror("setsockopt: freebind");
exit(EXIT_FAILURE);
}
}*/
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, dev, strlen(dev)) < 0)
{
perror("setsockopt: bind to device");
exit(EXIT_FAILURE);
}
// TODO comment for shadow
struct sockaddr_in myaddr;
myaddr.sin_addr.s_addr = htonl(addr.addr); //htonl(0x00000000); // TODO reversorder
// inet_pton(AF_INET, htonl(addr.addr), &myaddr.sin_addr.s_addr);
myaddr.sin_port = htons(addr.port);
myaddr.sin_family = AF_INET;
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
if (::getsockname(s, (struct sockaddr *)&sin, &len) == -1)
perror("getsockname");
else
std::cerr << "source port number " << ntohs(sin.sin_port) << std::endl;
int error = 0;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &error, sizeof(int)) < 0 || setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &error, sizeof(int)) < 0)
perror("setsockopt(SO_REUSEADDR) failed");
/*if (::bind(s, (struct sockaddr *)&myaddr, sizeof(struct sockaddr_in)) != 0)
{
char s[100];
sprintf(s, "bind to addr %u", htonl(addr.addr));
perror(s);
exit(EXIT_FAILURE);
}*/
std::cerr << "binding client id: " << `me` << " addr: " << ntohl(myaddr.sin_addr.s_addr) << " port: " << ntohs(myaddr.sin_port) << std::endl;
if (bind(s, (struct sockaddr *)&myaddr, sizeof(myaddr)) != 0)
{
char s[100];
sprintf(s, "bind to addr %u", htonl(addr.addr));
perror(s);
exit(1);
}
len = sizeof(sin);
if (::getsockname(s, (struct sockaddr *)&sin, &len) == -1)
perror("getsockname");
else
std::cerr << "source port number " << ntohs(sin.sin_port) << std::endl;
}
else
{
std::cerr << "SOCKET " << s << std::endl;
struct sockaddr_in myaddr;
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = htonl(addr.addr); // inet_addr("10.0.0.1"); //
// myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
myaddr.sin_port = htons(addr.port);
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
if (::getsockname(s, (struct sockaddr *)&sin, &len) == -1)
perror("getsockname");
else
std::cerr << "source port number " << ntohs(sin.sin_port) << std::endl;
std::cerr << "binding client id: " << `me` << " addr: " << ntohl(myaddr.sin_addr.s_addr) << " port: " << ntohs(myaddr.sin_port) << std::endl;
if (::bind(s, (struct sockaddr *)&myaddr, sizeof(myaddr)) != 0)
{
perror("bind failed");
exit(1);
}
len = sizeof(sin);
if (::getsockname(s, (struct sockaddr *)&sin, &len) == -1)
perror("getsockname");
else
std::cerr << "source port number " << ntohs(sin.sin_port) << std::endl;
}
install_reader(new udp_reader(`me`, s, *`udp_cb`, this));
>>>
}
action eavesdrop(dst:ip.endpoint) returns (s:socket) = {
<<< impure
std::cerr << "eavesdrop SOCKET " << dst.addr << std::endl;
char *dev = strdup("lo"); //TODO
int port;
char *shadowTest = getenv("SHADOW_TEST");
// Adjust interface name based on your needs
if(shadowTest == NULL) {
if (dst.interface == `ip.ivy`) {
dev = strdup("ivy");
}
if (dst.interface == `ip.veth_ivy`) {
dev = strdup("veth_ivy");
}
if (dst.interface == `ip.ivy_client`) {
dev = strdup("ivy_client");
}
if (dst.interface == `ip.ivy_server`) {
dev = strdup("ivy_server");
}
} else {
dev = strdup("eth0");
}
int l = strlen(dev);
std::cerr << "eavesdrop SOCKET dev " << dev << std::endl;
// Create a raw socket
s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
//s = socket(AF_INET, SOCK_DGRAM,0);
if (s <= 0) {
perror("socket");
exit(EXIT_FAILURE);
}
std::cerr << "listen SOCKET " << s << std::endl;
std::cerr << "listen SOCKET dev " << dev << std::endl;
// Set the network interface to promiscuous mode
struct ifreq ifopts;
strncpy(ifopts.ifr_name, dev, IFNAMSIZ - 1);
if (ioctl(s, SIOCGIFFLAGS, &ifopts) < 0) {
perror("ioctl");
close(s);
return 1;
}
ifopts.ifr_flags |= IFF_PROMISC;
if (ioctl(s, SIOCSIFFLAGS, &ifopts) < 0) {
perror("ioctl");
close(s);
return 1;
}
int v = 1;
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, dev, l) < 0) {
perror("setsockopt: bind to device");
exit(EXIT_FAILURE);
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)) < 0 || setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &v, sizeof(v)) < 0) {
perror("setsockopt(SO_REUSEADDR) failed");
}
/*
struct sockaddr_in v_dst = {};
v_dst.sin_addr.s_addr = htonl(INADDR_ANY); // Bind to any address
v_dst.sin_port = htons(port); // Specify port if needed
v_dst.sin_family = AF_INET;
if (bind(s, (struct sockaddr*) &v_dst, sizeof(struct sockaddr_in)) != 0) {
char s[100];
sprintf(s, "bind to addr %u", htonl(dst.addr));
perror(s);
exit(EXIT_FAILURE);
}
*/
// Get the MAC address
if (ioctl(s, SIOCGIFHWADDR, &ifopts) == -1) {
perror("ioctl");
close(s);
exit(EXIT_FAILURE);
}
memcpy(mitm_eth_header, ifopts.ifr_hwaddr.sa_data, 6);
eavesdrop = true;
install_reader(new udp_reader(`me`,s, *`udp_cb`, this));
>>>
}
action listen(dst:ip.endpoint) returns (s:socket) = {
<<< impure
// Modified code from Tom R.
char *dev = strdup("lo"); //TODO
bool free = false;
char opt;
int port;
if (dst.interface == `ip.ivy`) {
dev = strdup("ivy");
}
if (dst.interface == `ip.veth_ivy`) {
dev = strdup("veth_ivy");
}
if (dst.interface == `ip.ivy_client`) {
dev = strdup("ivy_client");
}
if (dst.interface == `ip.ivy_server`) {
dev = strdup("ivy_server");
}
int l = strlen(dev);
s = socket(AF_INET, SOCK_DGRAM,0);
std::cerr << "listen SOCKET " << s << std::endl;
std::cerr << "listen SOCKET dev " << dev << std::endl;
if (s <= 0) {
printf("socket: socket\n");
exit(EXIT_FAILURE);
}
int v = 1;
if (setsockopt(s, SOL_IP, IP_FREEBIND, &v, sizeof(v)) < 0) {
perror("setsockopt: freebind");
exit(EXIT_FAILURE);
}
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, dev, l) < 0) {
char s[100];
sprintf(s, "setsockopt: bind to device %s", dev);
perror(s);
exit(EXIT_FAILURE);
}
int error = 0;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &error, sizeof(int)) < 0 || setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &error, sizeof(int)) < 0)
perror("setsockopt(SO_REUSEADDR) failed");
struct sockaddr_in v_dst = {};
v_dst.sin_addr.s_addr = htonl(dst.addr);
v_dst.sin_port = htons(dst.port);
v_dst.sin_family = AF_INET;
if (bind(s, (struct sockaddr*) &v_dst, sizeof(struct sockaddr_in)) != 0) {
char s[100];
sprintf(s, "bind to addr %u", htonl(dst.addr));
perror(s);
exit(EXIT_FAILURE);
}
install_reader(new udp_reader(`me`,s, *`udp_cb`, this));
>>>
}
}
close the socket
action close(me:host,s:socket) = {
<<< impure
std::cerr << "|~ closing socket: " << s << std::endl;
// We don't want to close a socket when there is another thread
// waiting, because the other thread won't know what to do with the
// error.
// Instead we shut down the socket and let the other thread close it.
// If there is a reader thread, it will see EOF and close the socket. If there is
// on open writer thread, it will close the socket after we close the
// send queue. If the queue is already closed, closing it has no effect.
// invariant: if a socket is open there is a reader thread or
// an open writer thread, but not both.
// Because of this invariant, the socket will be closed exactly once.
::shutdown(s,SHUT_RDWR);
if (`send_queue`.find(s) != `send_queue`.end())
`send_queue`[s] -> set_closed();
>>>
}
action open(me:host,addr:ip.endpoint) returns (s:socket) = {
s := impl.open(me,addr)
}
action listen(me:host,addr:ip.endpoint) returns (s:socket) = {
s := impl.listen(me,addr)
}
action eavesdrop(me:host,addr:ip.endpoint) returns (s:socket) = {
s := impl.eavesdrop(me,addr)
}
action send(me:host,s:socket,dst:ip.endpoint,x:pkt) = {
<<< impure
std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
struct sockaddr_in dstaddr;
dstaddr.sin_family = AF_INET;
dstaddr.sin_addr.s_addr = htonl(dst.addr);
dstaddr.sin_port = htons(dst.port);
std::cerr << "sending from socket: " << s << std::endl;
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
if (::getsockname(s, (struct sockaddr *)&sin, &len) == -1)
perror("getsockname");
else {
std::cerr << "source port number " << ntohs(sin.sin_port) << std::endl;
std::cerr << "destination sending to id: " << x << std::endl;
std::cerr << "destination sending to id: " << me << " addr: " << ntohl(dstaddr.sin_addr.s_addr) << " port: " << ntohs(dstaddr.sin_port) << std::endl;
}
`ser` sr;
__ser(sr, x);
if (::getsockname(s, (struct sockaddr *)&sin, &len) == -1)
perror("getsockname");
else {
std::cerr << "source addr number " << ntohl(sin.sin_addr.s_addr) << std::endl;
std::cerr << "source port number " << ntohs(sin.sin_port) << std::endl;
}
if (eavesdrop) {
// Eavesdrop
//close(s); //causes error
std::cerr << " |~ eavesdrop - send:" << std::endl;
// Manually construct the IP and UDP headers
struct iphdr ip_hdr;
memset(&ip_hdr, 0, sizeof(ip_hdr));
/*
ip_hdr.ihl = 5;
ip_hdr.version = 4;
ip_hdr.tos = 0x2; //TODO copy from original packet
ip_hdr.tot_len = htons(sizeof(ip_hdr) + sizeof(struct udphdr) + sr.res.size());
ip_hdr.id = htons(54321);
ip_hdr.frag_off = 0;
ip_hdr.ttl = 64;
ip_hdr.protocol = IPPROTO_UDP;
ip_hdr.check = 0;
*/
if(htonl(dst.addr) != eavesdroped_client_addr.sin_addr.s_addr) {
ip_hdr.ihl = ip_hdr_client.ihl;
ip_hdr.version = ip_hdr_client.version;
ip_hdr.tos = ip_hdr_client.tos;
ip_hdr.tot_len = htons(sizeof(ip_hdr) + sizeof(struct udphdr) + sr.res.size());
ip_hdr.id = ip_hdr_client.id;
ip_hdr.frag_off = ip_hdr_client.frag_off;
ip_hdr.ttl = 128;
ip_hdr.protocol = ip_hdr_client.protocol;
} else {
ip_hdr.ihl = ip_hdr_server.ihl;
ip_hdr.version = ip_hdr_server.version;
ip_hdr.tos = ip_hdr_server.tos;
ip_hdr.tot_len = htons(sizeof(ip_hdr) + sizeof(struct udphdr) + sr.res.size());
ip_hdr.id = ip_hdr_server.id;
ip_hdr.frag_off = ip_hdr_server.frag_off;
ip_hdr.ttl = 128;
ip_hdr.protocol = ip_hdr_server.protocol;
}
std::cerr << " - dst.addr: " << dst.addr << std::endl;
std::cerr << " - htonl(dst.addr): " << htonl(dst.addr) << std::endl;
std::cerr << " - eavesdroped_server_addr.sin_addr.s_addr: " << eavesdroped_server_addr.sin_addr.s_addr << std::endl;
std::cerr << " - eavesdroped_client_addr.sin_addr.s_addr: " << eavesdroped_client_addr.sin_addr.s_addr << std::endl;
std::cerr << " - dst.port: " << dst.port << std::endl;
std::cerr << " - eavesdroped_server_addr.sin_port: " << eavesdroped_server_addr.sin_port << std::endl;
std::cerr << " - eavesdroped_client_addr.sin_port: " << eavesdroped_client_addr.sin_port << std::endl;
if(htonl(dst.addr) != eavesdroped_client_addr.sin_addr.s_addr) {
ip_hdr.saddr = eavesdroped_client_addr.sin_addr.s_addr; // Set the source IP address
ip_hdr.daddr = htonl(dst.addr);
} else {
ip_hdr.saddr = eavesdroped_server_addr.sin_addr.s_addr; // Set the source IP address
ip_hdr.daddr = htonl(dst.addr);
}
// Calculate the IP checksum
ip_hdr.check = checksum(&ip_hdr, sizeof(ip_hdr));
struct udphdr udp_hdr;
memset(&udp_hdr, 0, sizeof(udp_hdr));
if(htonl(dst.addr) != eavesdroped_client_addr.sin_addr.s_addr) {
udp_hdr.source = eavesdroped_client_addr.sin_port;
udp_hdr.dest = htons(dst.port);
} else {
udp_hdr.source = eavesdroped_server_addr.sin_port;
udp_hdr.dest = htons(dst.port);
}
udp_hdr.len = htons(sizeof(udp_hdr) + sr.res.size());
udp_hdr.check = 0;
// Create the pseudo header for checksum calculation
struct pseudo_header {
u_int32_t source_address;
u_int32_t dest_address;
u_int8_t placeholder;
u_int8_t protocol;
u_int16_t udp_length;
};
pseudo_header psh;
if (htonl(dst.addr) != eavesdroped_client_addr.sin_addr.s_addr) {
psh.source_address = eavesdroped_client_addr.sin_addr.s_addr;
psh.dest_address = htonl(dst.addr);
} else {
psh.source_address = eavesdroped_server_addr.sin_addr.s_addr;
psh.dest_address = htonl(dst.addr);
}
psh.placeholder = 0;
psh.protocol = IPPROTO_UDP;
psh.udp_length = htons(sizeof(udp_hdr) + sr.res.size());
int psize = sizeof(pseudo_header) + sizeof(udp_hdr) + sr.res.size();
char *pseudogram = new char[psize];
memcpy(pseudogram, (char *)&psh, sizeof(pseudo_header));
memcpy(pseudogram + sizeof(pseudo_header), &udp_hdr, sizeof(udp_hdr));
memcpy(pseudogram + sizeof(pseudo_header) + sizeof(udp_hdr), sr.res.data(), sr.res.size());
// Calculate the UDP checksum
udp_hdr.check = checksum(pseudogram, psize);
delete[] pseudogram;
std::cerr << "Original UDP Payload (" << sr.res.size() << " bytes):" << std::endl;
//std::cerr.write(sr.res.data(), sr.res.size());
//std::cerr << std::endl;
//print constructed packet
std::cerr << "Constructed Packet:" << std::endl;
std::cerr << "IP Header:" << std::endl;
std::cerr << " |-Source IP : " << inet_ntoa(*(struct in_addr *)&ip_hdr.saddr) << std::endl;
std::cerr << " |-Destination IP : " << inet_ntoa(*(struct in_addr *)&ip_hdr.daddr) << std::endl;
std::cerr << " |-Version : " << (unsigned int)ip_hdr.version << std::endl;
std::cerr << " |-Header Length : " << (unsigned int)ip_hdr.ihl << " DWORDS or " << ((unsigned int)(ip_hdr.ihl)) * 4 << " Bytes" << std::endl;
std::cerr << " |-Type Of Service : " << (unsigned int)ip_hdr.tos << std::endl;
std::cerr << " |-Total Length : " << ntohs(ip_hdr.tot_len) << " Bytes" << std::endl;
std::cerr << " |-Identification : " << ntohs(ip_hdr.id) << std::endl;
std::cerr << " |-Time To Live : " << (unsigned int)ip_hdr.ttl << std::endl;
std::cerr << " |-Protocol : " << (unsigned int)ip_hdr.protocol << std::endl;
std::cerr << " |-Checksum : " << ntohs(ip_hdr.check) << std::endl;
std::cerr << "UDP Header:" << std::endl;
std::cerr << " |-Source Port : " << ntohs(udp_hdr.source) << std::endl;
std::cerr << " |-Destination Port : " << ntohs(udp_hdr.dest) << std::endl;
std::cerr << " |-UDP Length : " << ntohs(udp_hdr.len) << std::endl;
// Construct the packet
std::vector<char> packet(sizeof(ip_hdr) + sizeof(udp_hdr) + sr.res.size());
memcpy(packet.data(), &ip_hdr, sizeof(ip_hdr));
memcpy(packet.data() + sizeof(ip_hdr), &udp_hdr, sizeof(udp_hdr));
memcpy(packet.data() + sizeof(ip_hdr) + sizeof(udp_hdr), sr.res.data(), sr.res.size());
std::cerr << "Modified UDP Payload (" << packet.size() << " bytes):" << std::endl;
std::cerr << " |- IP header bytes : ";
std::ios_base::fmtflags original_flags = std::cerr.flags(); // Save the original formatting flags
for (size_t i = 0; i < sizeof(ip_hdr); ++i) {
std::cerr << "\\x" << std::hex << std::setw(2) << std::setfill('0') << (unsigned int)(unsigned char)packet[i];
}
std::cerr << std::endl;
std::cerr << " |- UDP header bytes: ";
for (size_t i = sizeof(ip_hdr); i < sizeof(ip_hdr) + sizeof(udp_hdr); ++i) {
std::cerr << "\\x" << std::hex << std::setw(2) << std::setfill('0') << (unsigned int)(unsigned char)packet[i];
}
std::cerr.flags(original_flags); // Restore the original formatting flags
std::cerr << std::endl;
//std::cerr.write(packet.data(), packet.size());
//std::cerr << std::endl;
// Use a raw socket for sending the packet
int raw_sock = ::socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
//int raw_sock = socket(AF_PACKET,SOCK_RAW,IPPROTO_RAW);
//int raw_sock = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
if (raw_sock < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// Bind the raw socket to the desired source IP address
/*
struct sockaddr_in srcaddr;
memset(&srcaddr, 0, sizeof(srcaddr));
srcaddr.sin_family = AF_INET;
srcaddr.sin_addr.s_addr = eavesdroped_client_addr.sin_addr.s_addr;
srcaddr.sin_port = htons(ntohs(eavesdroped_client_addr.sin_port) + 1);
if (bind(raw_sock, (struct sockaddr *)&srcaddr, sizeof(srcaddr)) < 0) {
perror("bind failed");
close(raw_sock);
exit(EXIT_FAILURE);
}
*/
// Set socket option to include the IP header
/*
int one = 1; // important enable to change ip header
const int *val = &one;
if (::setsockopt(raw_sock, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0) {
perror("setsockopt failed");
close(raw_sock);
exit(EXIT_FAILURE);
}
*/
// Enable IP_TRANSPARENT to allow binding to non-local IPs
/*
if (setsockopt(raw_sock, SOL_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0) {
perror("setsockopt IP_TRANSPARENT failed");
close(raw_sock);
exit(EXIT_FAILURE);
}
*/
if (::getsockname(raw_sock, (struct sockaddr *)&sin, &len) == -1)
perror("getsockname");
else {
std::cerr << "raw_sock addr number " << ntohl(sin.sin_addr.s_addr) << std::endl;
std::cerr << "raw_sock port number " << ntohs(sin.sin_port) << std::endl;
}
// Send the packet using raw socket
if (::sendto(raw_sock, &packet[0], packet.size(), 0, (struct sockaddr *)&dstaddr, sizeof(dstaddr)) < 0)
{
std::cerr << "sendto failed " << WSAGetLastError() << "\n";
exit(1);
}
{
perror("sendto failed");
std::cerr << "packet size : " << packet.size() << std::endl;
exit(1);
}
int error = 0;
socklen_t lsen = sizeof(error);
int retval = ::getsockopt(raw_sock, SOL_SOCKET, SO_ERROR, &error, &lsen);
if (retval != 0) {
std::cerr << "error getting socket error code: " << strerror(retval) << std::endl;
return;
}
if (error != 0) {
std::cerr << "socket error: " << strerror(error) << std::endl;
}
close(raw_sock);
} else {
std::cerr << "not eavesdrop" << std::endl;
int error = 0;
socklen_t lsen = sizeof(error);
int retval = ::getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &lsen);
if (retval != 0) {
std::cerr << "error getting socket error code: " << strerror(retval) << std::endl;
return;
}
if (error != 0) {
std::cerr << "socket error: " << strerror(error) << std::endl;
}
std::cerr << "sending id start" << std::endl;
std::cerr << "sr.res.size() " << sr.res.size() << std::endl;
std::cerr << "dstaddr.sin_addr.s_addr " << dstaddr.sin_addr.s_addr << std::endl;
std::cerr << "dstaddr.sin_port " << dstaddr.sin_port << std::endl;
std::cerr << "socket " << s << std::endl;
//if (::sendto(s, &sr.res[0], sr.res.size(), 0, (struct sockaddr *)&dstaddr, sizeof(dstaddr)) < 0)
//if (::sendto(s, &sr.res[0], sr.res.size(), 0, (struct sockaddr *)&dstaddr, sizeof(dstaddr)) < 0)
if (::sendto(s, &sr.res[0], sr.res.size(), 0, (struct sockaddr *)&dstaddr, sizeof(dstaddr)) < 0)
{ std::cerr << "sendto failed " << WSAGetLastError() << "\n"; exit(1); }
{ perror("sendto failed"); exit(1); }
error = 0;
lsen = sizeof(error);
retval = ::getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &lsen);
if (retval != 0) {
std::cerr << "error getting socket error code: " << strerror(retval) << std::endl;
return;
}
if (error != 0) {
std::cerr << "socket error: " << strerror(error) << std::endl;
}
}
std::cerr << "sending id finish" << std::endl;
std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
std::cerr << "Performance sending packet measurement: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << " milliseconds" << std::endl;
>>>
}
connect creates a socket and then installs a connector task to establish the connection asynchronously.
action connect(self:host, other:host, other_ip:ip.addr) returns (s:socket) = {
<<< impure
s = make_tcp_socket();
std::cerr << " |~ TCP connecting to " << other << std::endl;
// create a send queue for this socket, if needed, along with
// its thread. if the queue exists, it must be closed, so
// we open it.
tcp_queue *queue;
if (`send_queue`.find(s) == `send_queue`.end()) {
`send_queue`[s] = queue = new tcp_queue(other, other_ip);
std::cerr << " |~ Installing WRITER tcp_writer" <<std::endl;
install_thread(new tcp_writer(`me`,s,queue,*`tcp_cb`,this));
} else{
std::cerr << " |~ Opening TCP queue " << s << std::endl;
`send_queue`[s] -> set_open(other);
}
>>>
}
action connect_accept(self:host, other:host, other_ip:ip.addr) returns (s:socket) = {
<<< impure
s = make_tcp_socket();
install_reader(`rdra` = new tcp_listener_accept(`me`,*`tcp_cb`,this));
std::cerr << " |~ TCP connecting & accepting to " << other << std::endl;
// create a send queue for this socket, if needed, along with
// its thread. if the queue exists, it must be closed, so
// we open it.
tcp_queue *queue;
if (`send_queue`.find(s) == `send_queue`.end()) {
std::cerr << " |~ Installing WRITER tcp_writer" <<std::endl;
`send_queue`[s] = queue = new tcp_queue(other, other_ip);
install_thread(new tcp_writer(`me`,s,queue,*`tcp_cb`,this));
} else{
std::cerr << " |~ Opening TCP queue " << s << std::endl;
`send_queue`[s] -> set_open(other);
}
>>>
}
action send_tcp(self:host, s:socket,other:host, p:pkt) returns (ok:bool) = {
<<< impure
std::cerr << " |~ sending TCP packet from " << s << std::endl;
std::cerr << " |~ sending TCP packet to " << other << std::endl;
// serialize the packet
`ser` sr;
__ser(sr,p);
// if the send queue for this sock doesn's exist, it isn't open,
// so the client has vioalted the precondition. we do the bad client
// the service of not crashing.
if (`send_queue`.find(s) == `send_queue`.end()) {
std::cerr << " |~ no TCP queue for socket " << s << std::endl;
ok = true;
}
else {
// get the send queue, and enqueue the packet,
// returning false if the queue is closed.
std::cerr << " |~ enqueuing TCP packet " << s << std::endl;
ok = !`send_queue`[s]->enqueue_swap(sr.res);
}
>>>
}
action recv(me:host,s:socket,src:ip.endpoint,x:pkt)
trusted isolate iso = this
attribute test = impl
}