Mudanças entre as edições de "Trabalho-Modulo1-RCO2-2011-1"

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
Linha 71: Linha 71:
  
 
<syntaxhighlight lang=bash>
 
<syntaxhighlight lang=bash>
 +
$ cd /hostlab/shared
 
$ make
 
$ make
 
gcc    -c -o proto.o proto.c
 
gcc    -c -o proto.o proto.c
Linha 76: Linha 77:
 
gcc -o proto proto.o utils.o -lutil
 
gcc -o proto proto.o utils.o -lutil
 
$ ls -l proto
 
$ ls -l proto
-rwxr-xr-x 1 sobral sobral 13943 2011-02-24 15:57 proto
+
-rwxr-xr-x 1 root root 13943 2011-02-24 15:57 proto
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
Para testar seu programa, execute-o assim: ''/hostlab/shared/proto /dev/ttyS0''. A figura abaixo mostra  um  exemplo de como compilar e executar o programa. Note que é necessário configurar a interface ''tun0'', que será a interface de rede associada ao enlace controlado por seu protocolo. Para poder testar adequadamente o seu enlace, configure-as da seguinte forma:
 
Para testar seu programa, execute-o assim: ''/hostlab/shared/proto /dev/ttyS0''. A figura abaixo mostra  um  exemplo de como compilar e executar o programa. Note que é necessário configurar a interface ''tun0'', que será a interface de rede associada ao enlace controlado por seu protocolo. Para poder testar adequadamente o seu enlace, configure-as da seguinte forma:
* Em ''a1'': ifconfig tun0 10.0.0.2 dstaddr 10.0.0.1
+
* Em ''a1'': ifconfig tun0 10.0.0.1 dstaddr 10.0.0.2
* Em ''a2'': ifconfig tun0 10.0.0.1 dstaddr 10.0.0.2
+
* Em ''a2'': ifconfig tun0 10.0.0.2 dstaddr 10.0.0.1
Assim, para testar a comunicação a partir de ''a1'' basta executar ''ping 10.0.0.1''. Se quiser testar a comunicação a partir de ''a2'' deve-se fazer ''ping 10.0.0.2''. No exemplo abaixo tentou-se fazer um ping a partir de ''a2''. Esse ping foi recebido pelo programa ''proto'' em ''a2'', que apenas mostrou que algo foi recebido.
+
Assim, para testar a comunicação a partir de ''a1'' basta executar ''ping 10.0.0.2''. Se quiser testar a comunicação a partir de ''a2'' deve-se fazer ''ping 10.0.0.1''. No exemplo abaixo tentou-se fazer um ping a partir de ''a2''. Esse ping foi recebido pelo programa ''proto'' em ''a2'', que apenas mostrou que algo foi recebido. O datagrama contendo a mensagem do ''ping'' é então transmitida pela porta serial (sem sofrer qualquer processamento, pois não existe ainda o protocolo de enlace), e recebida em ''a1''.
  
 
[[imagem:Netkit-trab-rco2-1.png]]
 
[[imagem:Netkit-trab-rco2-1.png]]

Edição das 09h11min de 25 de fevereiro de 2011

Introdução

O trabalho do primeiro módulo da disciplina trata da implementação de um protocolo de enlace ponto-a-ponto, que deve estabelecer um enlace entre dois computadores por meio de suas portas seriais. O protocolo de enlace deve-se integrar à pilha de protocolos TCP/IP do Linux, podendo assim transmitir e receber datagramas IP.

O protocolo de enlace deve prover os seguintes serviços de enlace:

  1. Encapsulamento e enquadramento (requisito mínimo, gerando um conceito C).
    • Seu protocolo deve definir um formato de quadro capaz de conter suas informações de controle (ao menos identificador de protocolo da camada superior, dados a serem transportados e código de detecção de erros).
  2. Detecção de erros (opcional, gerando conceito B)
    • O código de erros a ser usado deve ser do tipo CRC
  3. Controle de erros pare-e-espere (opcional, gerando conceito A)

Para realizar o trabalho será usado o Netkit, um projeto da Universidade de Roma para experimentação com redes virtuais baseadas em Linux.

Começando o trabalho

  • Arquivo com o experimento do Netkit
  • Lembre-se que:
    • Lstart: executa o experimento do Netkit, iniciando as máquinas virtuais
    • Lhalt: termina o experimento e matas as máquinas virtuais


O trabalho deve ser realizado usando o Netkit como plataforma de desenvolvimento. Deve-se primeiro obter o arquivo com o experimento do Netkit, e em seguida descompactá-lo. Ele contém a seguinte estrutura de arquivos e subdiretórios:

$ cd trabalho-rco2
$ ls -l
total 24
drwxr-xr-x 2 sobral sobral 4096 2011-02-24 15:06 a1
-rw-r--r-- 1 sobral sobral   47 2011-02-23 10:50 a1.startup
drwxr-xr-x 2 sobral sobral 4096 2011-02-23 11:01 a2
-rw-r--r-- 1 sobral sobral   47 2011-02-23 10:50 a2.startup
-rw-r--r-- 1 sobral sobral  253 2011-02-23 15:42 Lab.conf
-rw-r--r-- 1 sobral sobral    0 2011-02-23 10:47 lab.dep
drwxr-xr-x 2 sobral sobral 4096 2011-02-24 15:25 shared

Como se pode ver, há duas máquinas virtuais, a1 e a2, que são os computadores que devem se comunicar via portas seriais usando seu protocolo. O arquivo Lab.conf descreve a interligação desses computadores pelas portas seriais:

$ cat Lab.conf
LAB_DESCRIPTION="Protocolo de enlace do grupo XXX"
LAB_VERSION=1
LAB_AUTHOR="Seu Maneca e Dona Bilica"
LAB_EMAIL="maneca_e_bilica@ifsc.edu.br"

a2[ppp0]=serial
a1[ppp0]=serial

Os arquivos a1.startup e a2.startup contêm alguns comandos necessários para usar a porta serial em cada um desses computadores. Não é necessário que você os modifique. Caso resolva alterá-los, não remova os comandos que ali existem.

Os arquivos contendo o código-fonte inicial pro trabalho se encontra no subdiretório shared. São eles:

$ cd shared
$ ls -l
total 16
-rw-r--r-- 1 sobral sobral  101 2011-02-23 21:31 Makefile
-rw-r--r-- 1 sobral sobral 2375 2011-02-23 21:44 proto.c
-rw-r--r-- 1 sobral sobral 3389 2011-02-24 15:25 utils.c
-rw-r--r-- 1 sobral sobral 3224 2011-02-24 15:25 utils.h

A implementação do seu protocolo deve ser feita no arquivo proto.c. Os arquivos utils.h e utils.c contêm funções e definições necessárias para o funcionamento do protocolo. Eles tratam de como o protocolo interage com o sistema operacional, e assim ali estão os detalhes da comunicação com a porta serial e com a camada de rede.

A compilação e execução do seu programa deve ser feita dentro das máquinas virtuais. Assim, primeiro deve-se executar Lstart para iniciar o experimento. Uma vez iniciadas as máquinas virtuais, em uma delas entre no subdiretório /hostlab/shared. Lá você encontrará os arquivos do programa, e poderá compilá-los e testá-los.

Para compilar seu programa deve-se executar make, que resulta em um arquivo de programa chamado proto.

$ cd /hostlab/shared
$ make
gcc    -c -o proto.o proto.c
gcc    -c -o utils.o utils.c
gcc -o proto proto.o utils.o -lutil
$ ls -l proto
-rwxr-xr-x 1 root root 13943 2011-02-24 15:57 proto

Para testar seu programa, execute-o assim: /hostlab/shared/proto /dev/ttyS0. A figura abaixo mostra um exemplo de como compilar e executar o programa. Note que é necessário configurar a interface tun0, que será a interface de rede associada ao enlace controlado por seu protocolo. Para poder testar adequadamente o seu enlace, configure-as da seguinte forma:

  • Em a1: ifconfig tun0 10.0.0.1 dstaddr 10.0.0.2
  • Em a2: ifconfig tun0 10.0.0.2 dstaddr 10.0.0.1

Assim, para testar a comunicação a partir de a1 basta executar ping 10.0.0.2. Se quiser testar a comunicação a partir de a2 deve-se fazer ping 10.0.0.1. No exemplo abaixo tentou-se fazer um ping a partir de a2. Esse ping foi recebido pelo programa proto em a2, que apenas mostrou que algo foi recebido. O datagrama contendo a mensagem do ping é então transmitida pela porta serial (sem sofrer qualquer processamento, pois não existe ainda o protocolo de enlace), e recebida em a1.

Netkit-trab-rco2-1.png

Para finalizar o experimento e matar as máquinas virtuais, execute Lhalt.

O código-fonte inicial disponibilizado

Os arquivos iniciais do projeto do protocolo contém um esqueleto de código-fonte para orientar a escrita do programa. Além disso, há um conjunto de funções e tipos de dados para ajudar nessa tarefa. O esqueleto do programa se encontra em proto.c, cuja parte relevante está destacada abaixo:

/************************************************************
* Essas sao as funcoes iniciais do seu protocolo de enlace
*/

void vindo_da_camada_fisica(struct Link * link) {
  printf("Chegou algo da camada fisica ...\n");
}

void vindo_da_camada_de_rede(struct Link * link) {
  printf("Chegou algo da camada de rede (%d bytes)...\n",  link->bytesRede);
}

/*************************************************************/

Essas duas funções são o ponto de partida do seu protocolo, e devem ser escritas tendo em mente o seguinte:

  • void vindo_da_camada_fisica(struct Link * link): recebe os bytes vindos da porta serial, os quais estão guardados em link->fifo. Essa função deve reconstituir o quadro que foi transmitido do outro lado, realizando a operação de enquadramento. Note que os bytes são recebidos aos poucos, então essa função pode ser chamada várias vezes para que um quadro seja completamente recebido. Uma vez isso acontecendo, deve-se verificar se houve erro de transmissão. Se o quadro foi recebido corretamente, deve-se desencapsular o datagrama nele contido e enviá-lo para a camada de rede.
  • void vindo_da_camada_de_rede(struct Link * link): recebe da camada de rede um datagrama por inteiro, o qual fica armazenado em link->pduRede. O tamanho do datagrama recebido está em link->bytesRede. Ao receber um datagrama, o protocolo deve encapsulá-lo em um quadro, efetuar a operação de enquadramento e transmiti-lo pela porta serial.

Ambas funções possuem um único argumento struct Link * link, que contém os dados necessários para a comunicação com a porta serial e com a camada de rede. Você precisará usar alguns desses dados, em particular o campo link->fifo (fila com os bytes recebidos da serial), link->pduRede (último datagrama recebido da camada de rede) e link->bytesRede (tamanho em bytes do datagrama contido em link->pduRede). A definição de struct Link, contida em utils.h, está destacada abaixo:

struct Link {
  int serial; // o descritor da porta serial (não precisará deste campo)
  int net; // o descritor da camada de rede (não precisará deste campo)
  char dev[IFNAMSIZ]; // nome da interface tun (não precisará deste campo)

  struct Fifo fifo; // o buffer para receber o que vem da serial,
             // contendo os bytes jah recebidos e ainda nao processados
             // pelo protocolo de enlace

  char pduRede[MAXIMO]; // buffer que guarda uma PDU recebida da
              // camada de rede. Seu protocolo deve encapsular essa PDu dentro de 
              // um quadro e entao envia-lo pela porta serial.
  int bytesRede; // o tamanho da PDU recebida ...
};

Recebendo bytes vindos da porta serial

Toda vez que a porta serial receber alguns bytes, sua função vindo_da_camada_física(struct Link * link) será chamada. Os bytes recebidos da serial estarão guardados em uma fila, de forma que você possa retirá-los na ordem em que chegaram. O campo link->fifo representa essa fila, e para retirar bytes dele deve-se usar uma função específica:

// Retira um byte da fifo e o guarda em "byte". Se fifo estiver vazia 
// quando for fazer a retirada retorna 0, senao retorna 1.
int fifo_retira(struct Fifo * fifo, char * byte);

Assim, quando precisar obter os bytes recebidos da serial, basta chamar a função fifo_retira da seguinte forma:

int byte;

if (fifo_retira(&link->fifo, &byte)) {
  // processa o byte recebido
}

Note que se a fila estiver vazia, a função fifo_retira retorna o valor 0.

Enviando quadros para a porta serial e datagramas para a camada de rede

Quando tiver um quadro pronto para enviar para a porta serial, use a função envia_para_camada_fisica:

// envia um quadro pela porta serial. Retorna a quantidade de bytes de fato enviados.
int envia_para_camada_fisica(struct Link * link, char * quadro, int bytes);

O quadro deve estar contido no buffer quadro (um vetor de char), e no argumento bytes você deve informar quantos bytes há nesse quadro. A função irá retornar somente quando o quadro tiver terminado de ser enviado.

Quando tiver um datagrama pronto para ser enviado para a camada de rede, use a função envia_para_camada_de_rede:

// envia um datagrama IP para a camada de rede. Retorna a quantidade de bytes
// de fato enviados.
int envia_para_camada_de_rede(struct Link * link, char * datagrama, int bytes);

Os bytes do datagrama devem estar contidos no buffer datagrama (um vetor de char), e o argumento bytes informa sua quantidade de bytes.