Network implementation

include ip
This is an implementation of a generic UDP and/or TCP endpoint. It allows a host to open a socket and to send and receive packets on it. The network is unreliable and allows packet duplication. Parameters are:

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) = {
The type of socket descriptors

    type socket
TCP objects:

    object rdr = {}         # the listener object
    object rdra = {}        # the listener object
    object tcp_cb = {}      # struct holding the callbacks
    object send_queue = {}  # queues of outgoing packets
This code goes in the C++ header file, ahead of the ivy object declaration. Here, we put declarations (perhaps forward) of any auxiliary classes we need). We need to be careful that the names of these don't clash with other modules. However, duplicates are removed, so we don't have to worry about multiple instances of this module clashing.

    <<< header
include

ifndef _WIN32

include include include include include include include include include include 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;
    >>>
This code goes in the C++ implementation file.Here, we put implementations of the classes declared in the header, and auxiliary functions.

    <<< 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;
ifdef _WIN32
                if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&len, &lenlen))
else
                if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &len, &lenlen))
endif
                {
                    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.
define MAX_TCP_SEND_QUEUE 16

        struct tcp_mutex {
ifdef _WIN32
                HANDLE mutex;
                tcp_mutex() { mutex = CreateMutex(NULL,FALSE,NULL); }
                void lock() { WaitForSingleObject(mutex,INFINITE); }
                void unlock() { ReleaseMutex(mutex); }
else
                pthread_mutex_t mutex;
                tcp_mutex() { pthread_mutex_init(&mutex,NULL); }
                void lock() { pthread_mutex_lock(&mutex); }
                void unlock() { pthread_mutex_unlock(&mutex); }
endif
        };

        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) {
ifdef _WIN32
                inetaddr = ntohl(inet_addr("127.0.0.1")); // can't send to INADDR_ANY in windows
else
                std::cerr << "WARNING: using default TCP configuration\n";
                //std::cerr << `ip_addr`;
                inetaddr =  0x0a000001; //INADDR_ANY; //TODO
endif
                inetport = `port_base`; //+ id;
        }

        void tcp_config::get_other(int id, unsigned int other_ip,unsigned long &inetaddr, unsigned long &inetport) {
ifdef _WIN32
                inetaddr = ntohl(inet_addr("127.0.0.1")); // can't send to INADDR_ANY in windows
else
                std::cerr << "WARNING: using default TCP configuration\n";
                //std::cerr << `ip_addr`;
                inetaddr =  other_ip; //INADDR_ANY; //TODO
endif
                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;
ifdef _WIN32
                if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&len, &lenlen))
else
                if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &len, &lenlen))
endif
                {
                    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) = {
Here we put any new members of the ivy C++ class. If we have allocated a per-instance object, we declared it here anti-quoted. The plugs in the actual member name, which may be any array if this is a parameterized instance.

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

        >>>
Here, we put code to go in the initializer. If this is a parameterized instance, then this code will be run in a loop, so we have to be careful that any initialization of common objects is idempotent.

        <<< 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));

        >>>
These actions are handlers for the callbacks. They just insert the endpoint's id and call the corresponding callback action.

        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)
These actions are handlers for the callbacks. They just insert the endpoint's id and call the corresponding callback action.

        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)
        }
implement connected(self:host, s:socket) { <<< impure std::cerr << "CONNECTED " << s << std::endl; //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)
        }
open creates a socket and binds it to the requested endpoint.

        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));

            >>>
        }
    }
These are the implementations of the interface calls. These operations are synchronous.

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();

        >>>
    }
open creates a socket and binds it to the requested endpoint.

    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)
    }
send transmits a packet synchronously.
    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)
ifdef _WIN32
                {
                    std::cerr << "sendto failed " << WSAGetLastError() << "\n";
                    exit(1);
                }
else
                {
                    perror("sendto failed");
                    std::cerr << "packet size : " <<  packet.size() << std::endl;
                    exit(1);
                }
endif

            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)
ifdef _WIN32
                { std::cerr << "sendto failed " << WSAGetLastError() << "\n"; exit(1); }
else
                { perror("sendto failed"); exit(1); }
endif
            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;
        >>>
    }
These are the implementations of the interface calls. These operations are synchronous.

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);
            }
        >>>
    }
send serializes the message and calls unix send to send it in the given socket. It returns true if sending was successful. Not, a "successfully" sent packet could still be lost to due congestion. If we want to maintain ordered semantics, we will need to have an additional return code indicating the packet could not be queued (analogous to EAGAIN).

    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);
            }
        >>>
    }
callback on reception of message, to be implemented by user.

    action recv(me:host,s:socket,src:ip.endpoint,x:pkt)

    trusted isolate iso = this

    attribute test = impl
}