Integração e Testes Globais

Em nossa implementação foi feito um arquivo chamado server.cpp que é responsável por receber requisições externas ao mesmo, que sejam destinadas para o IP e porta do servidor.

Esse servidor executa primeiramente o handshake para estabelecimento de conexão, ou seja, assim que receber um segmento com a flag SYN setada, o mesmo retorna um SYN ACK, e a aplicação externa por fim retorna um ACK confirmando assim o estabelecimento de conexão, a partir desse momento tanto o Sequence Number quanto o Acknowledgment Number estão em 1.

Com a conexão estabelecida (ou não, já que o server não mantém controle das conexões abertas) servidor responde a segmentos HTTP tanto GET quanto POST.

Existem dentro do servidor duas sequencias de bytes: um para o SYNACK do handshake e outra para as respostas HTTP (GET, POST). Também existem variáveis inteiras que guardam somatórios prévios do checksum tanto do IP quanto TCP. Esse somatório é feito a partir de campos que não variam dentro de cada segmento, como por exemplo Version, Type of Service e Time to Live. Existe também um arquivo chamado headers.h que possui a definição da Struct Segment que contem todos os campos IP e TCP para facilitar a manipulação de seus campos variáveis.

O calculo do checksum de cada Segment é feito antes do envio do mesmo, através da soma dos campos que não variam (IP: cabeçalho IP, quebrado em partes de 16-bit; TCP: Cabeçalho TCP, quebrado em partes de 16-bit). A outra parte do checksum é realizada somando-se os campos que são variáveis para cada segmento: porta destino, endereço de origem, endereço de destino, ack number, sequence number. Após isto é feita a soma do checksum 16-bit com o seu overflow e os bits são invertidos (complemento de 1, segundo RFC 793).

Para realizar, tanto o envio quanto o recebimento de pacotes, é utilizado raw socket, pois assim há um controle sobre todo o pacote a partir dos bytes do IP.

Quando há o envio de um segmento SYN (requisição de conexão, parte 1 do three-way handshake), enviamos um segmento SYN ACK (hábil para conexão, parte 2 do three-way handshake). Quando há o envio de uma requisição Http GET, enviamos uma resposta. Já para requisições POST, é invocada a chamada registrada para aquele recurso.

Código server.cpp:

#include <malloc.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "headers.h"
#include "errno.h"
#include <iostream>
#include <string.h>
#include <stdio.h>

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
void printDetails(unsigned char* p);


typedef void (*callback)(void);

unsigned int THIS_IP;
unsigned short THIS_PORT = htons(80);

int nResourcesGET;
int* hashesGET;
double** valuesGET;

int nResourcesPOST;
int* hashesPOST;
callback* valuesPOST;

int socket_receive;
int socket_send;

unsigned char responseSegmentBytes[] = {
    //IP
    0x45, 0x00, 0x00, 0x28,
    0x00, 0x00, 0x40, 0x00,
    0x40, 0x06, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    //TCP
    0x00, 0x50, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x50, 0x10, 0x7F, 0xFF,
    0x00, 0x00, 0x00, 0x00,
    //Data
    0x48, 0x54, 0x54, 0x50,
    0x2f, 0x31, 0x2e, 0x31,
    0x20, 0x32, 0x30, 0x30,
    0x20, 0x4f, 0x4b, 0x0d,
    0x0a, 0x41};

// Calculo do checksum TCP para os campos nao variaveis
unsigned int calculoPrevioParaChecksumTcpResponseSegment = 0x15501;//0xD07D;

unsigned char synAckSegmentBytes[] = {
    //IP
    0x45, 0x00, 0x00, 0x28,
    0x00, 0x00, 0x40, 0x00,
    0x40, 0x06, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    //TCP
    0x00, 0x50, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x50, 0x12, 0x7F, 0xFF,
    0x00, 0x00, 0x00, 0x00};

// Calculo do checksum TCP para os campos nao variaveis
unsigned int calculoPrevioParaChecksumTcpSynAckSegment = 0xD07B;

// Calculo do checksum IP para os campos nao variaveis
unsigned int calculoPrevioParaChecksumIpSegment = 0xC52E;

int makeHash(unsigned char* string) {
        int hash=7;
        for (int i = 0; string[i]; i++) {
                hash = hash * 31 + string[i];
        }
        return hash;
}

void registerGET(unsigned char** resourceNames, double** values, int n) {
        hashesGET = (int*) malloc(sizeof(int*) * n);
    for (int i = 0; i < n; i++) {
        hashesGET[i] = makeHash(resourceNames[i]);
    }
    valuesGET = values;
    nResourcesGET = n;
}

void registerPOST(unsigned char** resourceNames, callback* funcs, int n) {
        hashesPOST = (int*) malloc(sizeof(int*) * n);
    for (int i = 0; i < n; i++) {
        hashesPOST[i] = makeHash(resourceNames[i]);
    }
    valuesPOST = funcs;
    nResourcesPOST = n;
}

unsigned char buffer[128];


Segment* receiveNextDatagram(){
    
    if (recvfrom(socket_receive, &buffer, sizeof(buffer), 0,
                 NULL, NULL) < 0) {
        perror("PROBLEMAS NO RECEBIMENTO DE PACOTE");
    }

    return (Segment*) buffer;
}

void prepararSocket(){
    if ((socket_receive = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
        perror("ERRO: Socket nao pode ser criado...");
        exit(EXIT_FAILURE);
    }  

    if ((socket_send = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
        perror("ERRO: Socket nao pode ser criado...");
        exit(EXIT_FAILURE);
    }  
}

// Passo 2 do 3-Way Handshake do TCP
void sendSynAck(int destination, short destPort, int sequenceNumber){
    Segment* s = (Segment*)synAckSegmentBytes;
    
    //IP
    s->destIp = destination;
    //TCP
    s->destPort = destPort;
    s->ackNumber = sequenceNumber + 0x01000000;
    
    //Calculo do Checksum TCP
    unsigned int somaParaCheckSum = calculoPrevioParaChecksumTcpSynAckSegment;
    
    somaParaCheckSum += htons(s->ackNumber>>16);
    somaParaCheckSum += htons(s->ackNumber & 0xFFFF);
        
    somaParaCheckSum += htons(destPort);
    somaParaCheckSum += htons(destination>>16);
    somaParaCheckSum += htons(destination & 0xFFFF);
    s->checksumTcp = ntohs(~((somaParaCheckSum & 0xFFFF) + (somaParaCheckSum>>16)));
    
    //Calculo do Checksum IP
    somaParaCheckSum = calculoPrevioParaChecksumIpSegment;
    somaParaCheckSum += htons(destination>>16);
    somaParaCheckSum += htons(destination & 0xFFFF);
    s->checksumIp = ntohs(~((somaParaCheckSum & 0xFFFF) + (somaParaCheckSum>>16)));

    
    struct sockaddr_in dst_addr;
    memset(&dst_addr, 0, sizeof(dst_addr));
    dst_addr.sin_family = AF_INET;
    dst_addr.sin_port = htons(destPort);
    
    struct in_addr endIp;
    endIp.s_addr = destination;
    dst_addr.sin_addr = endIp;
    
    int tmp = sendto(socket_send, s, sizeof(*s), 0, (struct sockaddr *)&dst_addr, sizeof(dst_addr));
    std::cout << "Quantidade de bytes enviados: " << tmp << std::endl;
}

void sendResponse(unsigned int destination, unsigned short destPort, unsigned int sequenceNumber, unsigned int ackNumber, int dataLen){
    Segment* s = (Segment*)responseSegmentBytes;
    
    //IP
    s->destIp = destination;
    //TCP
    s->destPort = destPort;
    
    //Deal with ack and sequence numbers
//    std::cout << ackNumber << std::endl;
    
    s->ackNumber = htonl(ntohl(sequenceNumber) + 16);
    s->seqNumber = ackNumber;
    
    //Calculo do Checksum TCP
    unsigned int somaParaCheckSum = calculoPrevioParaChecksumTcpResponseSegment;
    
    somaParaCheckSum += htons(s->ackNumber>>16);
    somaParaCheckSum += htons(s->ackNumber & 0xFFFF);
    
    somaParaCheckSum += htons(s->seqNumber>>16);
    somaParaCheckSum += htons(s->seqNumber & 0xFFFF);
    
    somaParaCheckSum += htons(destPort);
    somaParaCheckSum += htons(destination>>16);
    somaParaCheckSum += htons(destination & 0xFFFF);

    int n = dataLen / 2;
    while (n) {
        unsigned short part = ((unsigned short *) (s + 1))[--n];
        somaParaCheckSum += htons(part);
        somaParaCheckSum += htons(part>>16);
        somaParaCheckSum += htons(part & 0xFFFF);
    }

    s->checksumTcp = ntohs(~((somaParaCheckSum & 0xFFFF) + (somaParaCheckSum>>16)));
    
    //Calculo do Checksum IP
    somaParaCheckSum = calculoPrevioParaChecksumIpSegment;
    somaParaCheckSum += htons(destination>>16);
    somaParaCheckSum += htons(destination & 0xFFFF);

    s->checksumIp = ntohs(~((somaParaCheckSum & 0xFFFF) + (somaParaCheckSum>>16)));

    
    struct sockaddr_in dst_addr;
    memset(&dst_addr, 0, sizeof(dst_addr));
    dst_addr.sin_family = AF_INET;
    dst_addr.sin_port = htons(destPort);
    
    struct in_addr endIp;
    endIp.s_addr = destination;
    dst_addr.sin_addr = endIp;
    
    int tmp = sendto(socket_send, s, sizeof(*s) + dataLen, 0, (struct sockaddr *)&dst_addr, sizeof(dst_addr));
    std::cout << "Quantidade de bytes enviados: " << tmp << std::endl;
}

void printDetails(Segment* s) {

    if (s->protocol != 6) {
        std::cout << "Received non-tcp packet." << std::endl;
        return;
    }

    struct in_addr sourceStruct;
    sourceStruct.s_addr = s->srcIp;
    char sourceString[15];
    inet_ntop(AF_INET, &sourceStruct, sourceString, 15);

    struct in_addr destinationStruct;
    destinationStruct.s_addr = s->destIp;
    char destinationString[15];
    inet_ntop(AF_INET, &destinationStruct, destinationString, 15);

    std::cout << sourceString <<
                 ":" <<
                 ntohs(s->srcPort) <<
                 " -> " <<      
                 destinationString <<
                 ":" <<
                 ntohs(s->destPort) <<
                 " (";


    if (s->headerLength != 5)
        std::cout << " !IP options (" << s->headerLength << ")! ";

    if (s->syn)
        std::cout << " SYN ";

    if (s->ack)
        std::cout << " ACK ";

    if (s->rst)
        std::cout << " RST ";

    std::cout << ")" << std::endl;
}

void setMyIpAddress() {
    //TODO: Pegar o IP na inicialização
    THIS_IP = 16777343;
    
    ((Segment*)synAckSegmentBytes)->srcIp = THIS_IP;
    ((Segment*)responseSegmentBytes)->srcIp = THIS_IP;
    
    calculoPrevioParaChecksumTcpSynAckSegment += htons(THIS_IP>>16);
    calculoPrevioParaChecksumTcpSynAckSegment += htons(THIS_IP & 0xFFFF);
    
    calculoPrevioParaChecksumTcpResponseSegment += htons(THIS_IP>>16);
    calculoPrevioParaChecksumTcpResponseSegment += htons(THIS_IP & 0xFFFF);
    
    calculoPrevioParaChecksumIpSegment += htons(THIS_IP>>16);
    calculoPrevioParaChecksumIpSegment += htons(THIS_IP & 0xFFFF);
}

void listen() {
    //printDetails(synAckSegment);
    prepararSocket();
    setMyIpAddress();

    while (1) {
        Segment* s = receiveNextDatagram();
        //printDetails(s);
        
        if ((s->destIp == THIS_IP) && (s->destPort == THIS_PORT)) {
                    printDetails(s);
            if (s->syn == 1) { // Segmento TCP requisitando conexão
                sendSynAck(s->srcIp, s->srcPort, s->seqNumber);
            } else {                
                unsigned char* data = ((unsigned char *) s) + 40;
                unsigned char i = 0;
                unsigned char resourceName[32];
                
                if (data[0] == 'G') { // GET
                    sendResponse(s->srcIp, s->srcPort, s->seqNumber, s->ackNumber, 18);
                    
                    while ((resourceName[i] = data[4 + i++]) != ' ');
                        resourceName[i - 1] = '\0';

                    int hash = makeHash(resourceName);
                    for (i = 0; i < nResourcesGET; i++) {
                        if (hashesGET[i] == hash) {
                            //TODO enviar o valor
                            //send(responseSegment, s->srcIp, s->srcPort, s->id, s->seqNumber + 1, s->ackNumber/*, *(valuesGET[i])*/);
                            break;
                        }
                    }

                } else if (data[2] == 'S') { // POST
                    while ((resourceName[i] = data[5 + i++]) != ' ');
                    resourceName[i - 1] = '\0';

                    int hash = makeHash(resourceName);
                    for (i = 0; i < nResourcesPOST; i++) {
                        if (hashesPOST[i] == hash) {
                            valuesPOST[i]();
                            break;
                        }
                    }
                }
            }
        }
    }
}

int main(){
    listen();
    return 1;
}

-------------------------------------------------------------------

Código hearders.h:

typedef struct {
    unsigned char      headerLength:4, /* Little-endian */
version:4;
    unsigned char      typeOfService;
    unsigned short size;
    unsigned short id;
    unsigned short fragmentOffset;
    unsigned char  ttl;
    unsigned char  protocol;
    unsigned short checksumIp;
    unsigned int   srcIp;
    unsigned int   destIp;
    unsigned short srcPort:16;
    unsigned short destPort:16;
    unsigned int   seqNumber:32;
    unsigned int   ackNumber:32;
    unsigned char  reserved:4;
    unsigned char  dataOffset:4;
    unsigned int   fin:1;
    unsigned int   syn:1;
    unsigned int   rst:1;
    unsigned int   psh:1;
    unsigned int   ack:1;
    unsigned int   urg:1;
    unsigned int   res2:2;
    unsigned short window:16;  
    unsigned short checksumTcp:16;
    unsigned short urgPtr:16;
} Segment;

 um exemplo de fluxo:

Imagem 1


Implementação e Testes de Unidade

Em nossa implementação foi feito um servidor responsável por receber segmentos, processá-los e enviá-los de volta, ao modo de um webservice.

Nosso servidor recebe todos os pacotes visíveis na rede, e quando um desses segmentos foi direcionado para o ip do servidor e a porta correta, o mesmo processa a informação. O servidor efetua hand shake para estabelecimento de conexão e aceita métodos GET e POST.

 

Como nosso servidor usa sempre um char* como pacote recebido a manipulação é sempre feita atráves "casts" desse array para arrays de diferentes tipos e assim conseguimos mudar os valores necessários como por exemplo levando em consideração os cabeçalhos IP/TCP se quisermos mudar o campo Source IP Address faremos o seguinte:

       ((unsigned int*)pacote)[3] = IP;

Pois levando em consideração que um INT possui 32 bits ao fazer o cast acima dividimos o char* em partes de 32 bits e portanto ao chamar [index]  conseguimos acessar um bloco de 32 bits que devemos alterar, nesse caso o bloco de Source IP Address.

IP HEADER


TCP HEADER 


 

Codigo do server.cpp

 

#include <malloc.h>

#include <stdlib.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <iostream>

 

constchar *inet_ntop(int af, constvoid *src, char *dst, socklen_t size);

void printDetails(unsignedchar* p);

 

typedefvoid (*callback)(void);

 

int THIS_IP = 16777343;

unsignedshort THIS_PORT = htons(80);

 

int nResourcesGET;

int* hashesGET;

double** valuesGET;

 

int nResourcesPOST;

int* hashesPOST;

callback* valuesPOST;

 

int socket_receive;

int socket_send;

 

unsigned char packet[] = { 0x45, 0x0, 0x0, 0x28, 0x42, 0x42, 0x0, 0x0, 0x40, 0xFF, 0x0, 0x0, 0x7f, 0x0, 0x0, 0x1, 0x7f, 0x0, 0x0, 0x1, 0x4, 0xc6, 0x7, 0xd2, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x62, 0x42, 0x42, 0x7f, 0xff, 0x0, 0x0, 0x0, 0x0, 0x42, 0x42, 0x41 };

 

unsigned char packetSynAck[] = {0x45, 0x0, 0x0, 0x28, 0x42, 0x42, 0x0, 0x0, 0x40, 0x6, 0x0, 0x0, 0x7f, 0x0, 0x0, 0x1, 0x7f, 0x0, 0x0, 0x1, 0x0, 0x50, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x52, 0x7f, 0xff, 0x0, 0x0, 0x0, 0x0};

 

 

int makeHash(unsignedchar* string) {

        int hash=7;

        for (int i = 0; string[i]; i++) {

                hash = hash * 31 + string[i];

        }

        return hash;

}

 

void registerGET(unsignedchar** resourceNames, double** values, int n) {

        hashesGET = (int*) malloc(sizeof(int*) * n);

for (int i = 0; i < n; i++) {

hashesGET[i] = makeHash(resourceNames[i]);

}

valuesGET = values;

nResourcesGET = n;

}

 

void registerPOST(unsignedchar** resourceNames, callback* funcs, int n) {

        hashesPOST = (int*) malloc(sizeof(int*) * n);

for (int i = 0; i < n; i++) {

hashesPOST[i] = makeHash(resourceNames[i]);

}

valuesPOST = funcs;

nResourcesPOST = n;

}

 

unsigned char buffer[128];

 

unsignedchar* receiveNextDatagram(){

    if (recvfrom(socket_receive, &buffer, sizeof(buffer), 0,

                 NULL, NULL) < 0)

        perror("PROBLEMAS NO RECEBIMENTO DE PACOTE");

    return buffer;

}

 

void prepararSocket(){

    if ((socket_receive = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {

        perror("ERRO: Socket nao pode ser criado...");

        exit(EXIT_FAILURE);

    }  

 

    if ((socket_send = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {

        perror("ERRO: Socket nao pode ser criado...");

        exit(EXIT_FAILURE);

    }  

}

 

void send(unsignedchar* p, int destination, short destPort) {

    struct sockaddr_in dst_addr;

    dst_addr.sin_family = AF_INET;

    dst_addr.sin_port = destPort;

    

    struct in_addr endIp;

    endIp.s_addr = destination;

    dst_addr.sin_addr = endIp;

 

    sendto(socket_send, p, 40/*sizeof(p)*/, 0

           (struct sockaddr *)&dst_addr, (socklen_t)sizeof(dst_addr));

}

 

void sendSynAck(int destination, short destPort,short identification, int sequenceNumber, int ackNumber){

    unsignedchar* p = packetSynAck;

    

    ((unsignedint*)p)[3] = THIS_IP;

    ((unsignedint*)p)[4] = destination;

    ((unsignedshort*)p)[11] = destPort;

    ((unsignedshort*) p)[4] = identification; 

    ((unsignedint*) p)[6] = ackNumber; //SEQUENCE

    ((unsignedint*) p)[7] = sequenceNumber+1; //ACK

 

    send(p, destination, destPort);

}

 

void sendResponse(int destination, short destPort,short identification, int sequenceNumber,int ackNumber,double value){

    unsignedchar* p = packet;

    

    ((unsignedint*)p)[3] = THIS_IP;

    ((unsignedint*)p)[4] = destination;

    ((unsignedshort*)p)[11] = destPort;

    ((unsignedshort*) p)[4] = identification; 

    ((unsignedint*) p)[6] = ackNumber; //SEQUENCE

    ((unsignedint*) p)[7] = sequenceNumber+1; //ACK

    //TODO valor

    

    send(p, destination, destPort);

}

 

void printDetails(unsignedchar* p) {

        unsignedchar protocol = ((unsignedchar*) p)[9];

 

        if (protocol != 6) {

            std::cout << "Received non-tcp packet." << std::endl;

            return;

        }

 

unsignedint destination = ((unsignedint*) p)[4];

unsignedshort destinationPort = ((unsignedshort*) p)[11];

        unsignedint source = ((unsignedint*) p)[3];

        unsignedshort sourcePort = ((unsignedshort*) p)[10];

 

        struct in_addr sourceStruct;

        sourceStruct.s_addr = source;

        char sourceString[15];

        inet_ntop(AF_INET, &sourceStruct, sourceString, 15);

 

        struct in_addr destinationStruct;

        destinationStruct.s_addr = destination;

        char destinationString[15];

        inet_ntop(AF_INET, &destinationStruct, destinationString, 15);

 

 

        int ack = p[33] & 16;

        int rst = p[33] & 4;

        int syn = p[33] & 2;

        //unsigned int sequenceNumber = ((unsigned int*) p)[6];

        //unsigned int ackNumber = ((unsigned int*) p)[7];

 

        std::cout << sourceString <<

                     ":" <<

                     ntohs(sourcePort) <<

                     " -> " <<      

                     destinationString <<

                     ":" <<

                     ntohs(destinationPort) <<

                     " (";

 

        unsignedchar ipHeaderLength = p[0] & 15;

 

        if (ipHeaderLength != 5)

            std::cout << " !IP options (" << (int)ipHeaderLength << ")! ";

 

        if (syn)

            std::cout << " SYN ";

 

        if (ack)

            std::cout << " ACK ";

 

        if (rst)

            std::cout << " RST ";

 

        std::cout << ")" << std::endl;

}

 

void listen() {

    printDetails(packetSynAck);

    

    prepararSocket();

 

while (1) {

unsignedchar* receivedSegment = receiveNextDatagram();

 

int destination = ((int*) receivedSegment)[4];

unsignedshort destinationPort = ((unsignedshort*) receivedSegment)[11];

 

        printDetails(receivedSegment);

        

if ((destination == THIS_IP) && (destinationPort == THIS_PORT)) {

unsignedint source = ((unsignedint*) receivedSegment)[3];

unsignedshort identification = ((unsignedshort*) receivedSegment)[4];

 

unsignedshort sourcePort = ((unsignedshort*) receivedSegment)[10];

 

int sequenceNumber = ((int*) receivedSegment)[6];

int ackNumber = ((int*) receivedSegment)[7];

            

if (receivedSegment[33] & 2) { // syn

sendSynAck(source, sourcePort, identification, sequenceNumber, ackNumber);

} else {

unsignedchar* data = receivedSegment + 40;

unsignedchar i = 0;

unsignedchar resourceName[32];

 

if (data[0] == 'G') { // GET

while ((resourceName[i] = data[4 + i++]) != ' ');

resourceName[i - 1] = '\0';

 

int hash = makeHash(resourceName);

for (i = 0; i < nResourcesGET; i++) {

if (hashesGET[i] == hash) {

sendResponse(source, sourcePort, identification, sequenceNumber, ackNumber, *(valuesGET[i]));

break;

}

}

 

} elseif (data[2] == 'S') { // POST

while ((resourceName[i] = data[5 + i++]) != ' ');

resourceName[i - 1] = '\0';

 

int hash = makeHash(resourceName);

for (i = 0; i < nResourcesPOST; i++) {

if (hashesPOST[i] == hash) {

valuesPOST[i]();

break;

}

}

}

}

}

}

}

 

int main(){

    listen();

    return1;

}

 

-----------------   Testes: arquivo: tests.py

import requests

import unittest

 

class TestServer(unittest.TestCase):

 

    def setUp(self):

        self.address = "127.0.0.1"

        self.port = "80"

 

    def test_get_resource(self):

        r = requests.get("">http://" + self.address + ":" + self.port + "/get")

        self.assertEqual(r.status_code, 200)

        self.assertEqual(r.text, "1234")

 

    def test_post_resource(self):

        r = requests.post("">http://" + self.address + ":" + self.port + "/post")

        self.assertEqual(r.status_code, 200)

        self.assertEqual(r.text, "1")

 

    def test_get_invalid_resource(self):

        r = requests.get("">http://" + self.address + ":" + self.port + "/get2")

        self.assertEqual(r.status_code, 404)

 

 

if __name__ == '__main__':

    unittest.main()

 

LOG ao executar os testes anteriores.

 

127.0.0.1:60995 -> 127.0.0.1:80 ( SYN )
127.0.0.1:80 -> 127.0.0.1:60995 ( SYN ACK )
127.0.0.1:60996 -> 127.0.0.1:80 ( SYN )
127.0.0.1:80 -> 127.0.0.1:60996 ( SYN ACK )
127.0.0.1:60996 -> 127.0.0.1:80 ( SYN )
127.0.0.1:80 -> 127.0.0.1:60996 ( SYN ACK )
127.0.0.1:60996 -> 127.0.0.1:80 ( SYN )
127.0.0.1:80 -> 127.0.0.1:60996 ( SYN ACK )

Relatório de Andamento do Projeto:


Com o objetivo de simplificar todo o processo que envolve a pilha TCP/IP, foi criado um bitmap relacionando cada campo do cabeçalho IP e TCP a um dado neste bitmap. Chamamos este bitmap de packet:


wireshark


char packet[] = { 0x45, 0x0, 0x0, 0x28, 0x42, 0x42, 0x0, 0x0, 0x40, 0xff, 0x0, 0x0, 0x7f, 0x0, 0x0, 0x1, 0x7f, 0x0, 0x0, 0x1, 0x4, 0xc6, 0x7, 0xd2, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x62, 0x42, 0x42, 0x7f, 0xff, 0x0, 0x0, 0x0, 0x0, 0x42, 0x42, 0x41 };


O print screen mostra para onde cada dado do packet é mapeado (foi usado o programa wireshark):
packet[0] = Version + Header Length
packet[1] = Diferentiated Services Field
packet[2,3] = Total Length
packet[4,5] = Identification
packet[6,7] = Flags + Offset
packet[8] = Time to live
packet[9] = Protocolo
packet[10,11] = checksum
packet[12,13,14,15] = source ip
packet[16,17,18,19] = destination ip
packet[20+] = cabeçalho TCP + dados

(Como ainda está em fase de teste, alguns campos podem não ter sidos mapeados corretamente, mas isto deverá ser corrigido na versão final)

IPHEADER:



Como pode ser visto, cada um dos campos corresponde a uma parte do cabeçalho IP.

em nossa aplicação os campos Version,Type of Service,TTL,Protocol nunca serão modificados.

Para o cabeçalho TCP, será trabalhada a mesma forma de mapeamento. Isto hoje está em andamento.

---

Para ser feito o teste, foi criado um programa send.c:

#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define DEST "127.0.0.1"


int main(int argc, char **argv){

    int s;
    struct sockaddr_in dst_addr;
    char packet[] = { 0x45, 0x0, 0x0, 0x28, 0x42, 0x42, 0x0, 0x0, 0x40, 0xFF, 0x0, 0x0, 0x7f, 0x0, 0x0, 0x1, 0x7f, 0x0, 0x0, 0x1, 0x4, 0xc6, 0x7, 0xd2, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x62, 0x42, 0x42, 0x7f, 0xff, 0x0, 0x0, 0x0, 0x0, 0x42, 0x42, 0x41 };

    if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
        perror("error:");
        exit(EXIT_FAILURE);
    }

    ((unsigned int*) packet)[4] = 0;
    ((unsigned short*) packet)[5] = 0xCA;

    dst_addr.sin_family = AF_INET;
    dst_addr.sin_port = 0; /* not needed in SOCK_RAW */
    inet_pton(AF_INET, DEST, (struct in_addr *)&dst_addr.sin_addr.s_addr);
    memset(dst_addr.sin_zero, 0, sizeof(dst_addr.sin_zero));

    while(42) {
        sleep(2);
        if (sendto(s, packet, sizeof(packet), 0,
                    (struct sockaddr *)&dst_addr, (socklen_t)sizeof(dst_addr)) < 0)
            perror("uh oh:");
        else printf("foi\n");
    }
    return(0);
}

Foi criado um Raw Socket para enviar os dados. Os dados são enviados através da função sendto(). Após compilar e rodar o programa, os pacotes são verificados através do programa wireshark.

TODO:
Cálculo do checksum: no linux isso é feito automaticamente, mas no EPOS será implementada otimizadamente esta parte.

Definição de bitmaps padrões para realização do handshake (necessário pois qualquer browser, por exemplo, necessita de um handshake para estabelecer uma conexão).

Definicao do sequence number (eliminar erros de retransmission) e do ack FLUXO: envio de pacote com sequence number = x e ack = y

Retorno do pacote com sequence number = y e ack = x + qtd bytes de dados (ou seja numero de sequencia dos proximos esperados).


----------


Nosso projeto consiste em implementar um bitmap para reduzir os recursos gastos por uma pilha TCP/IP comum. A soluçao tomada foi eliminar as tres camadas caracteristicas de uma rede (camada rede,transporte,aplicacao) criando uma classe que será responsavel por cuidar do protocolo TCP/IP e todos seus controles.


Sequência de um pacote de rede:

1. O pacote chega pela rede;

2. Os dados necessários são salvos (endereço do remetente etc.);

3. O pacote é modificado para posterior processamento (alguns campos do header são retirados etc.);

4.  O pacote modificado é comparado com o pacote de exemplo para verificar se o destinatário está correto e está bem construído;

5. Os recursos de resposta “são casados” de acordo com o endereço HTTP requisitado;

6. É preenchido o template de resposta, com os dados da aplicação e os valores salvos anteriormente;

7. Os checksums do IP e do TCP são criados;

8. A resposta é enviada pela rede.



Diagram2


A verificação será feita através de um sistema similar a expressões regulares, validando alguns campos, ignorando e/ou extraindo o valor de outros. Os campos a serem validados serão retirados de um pacote exemplo que será embutido na aplicação. Os campos variáveis a serem ignorados/guardados são:

- IP

      -- Identification-- Time To Live

      -- Header Checksum

      -- Source

- TCP

      -- Source Port

      -- Next Sequence Number

      -- Window Size Value

      -- Checksum


A resposta será feita através de um sistema de template, preenchendo os valores em branco com valores extraídos do pedido, calculados pela programa ou fornecido pela aplicação do usuário (como no caso da leitura de um valor).


O objetivo final é implementar um leitor de temperatura que responde a pedidos via HTTP, fornecendo a temperatura sempre que o usuário requisitar. Este webservice deverá ser compatível com as implementações comuns de outros sistemas, como Windows + Chrome e Ubuntu + Firefox.


Sobre o checksum:

Juntamente com o template, será disponibilizado um cálculo prévio para os checksums... O cálculo será completado após todo o pacote estar preenchido.

Código

headers.h

server.cpp