Trabalho-Modulo1-RCO2-2011-1

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar

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:
    • labstart: executa o experimento do Netkit, iniciando as máquinas virtuais
    • labhalt: termina o experimento e matas as máquinas virtuais


O trabalho deve ser realizado usando o Netkit como plataforma de desenvolvimento, que possibilita criar experimentos com redes virtuais (i.e. máquinas virtuais Linux interligadas por interfaces de rede também virtuais). Com ele se disponibilizou uma rede composta por somente dois computadores, a1 e a2, que estão interligados por suas portas seriais virtuais /dev/ttyS0, como mostrado na figura abaixo. Sobre esse enlace físico deve-se criar um enlace ponto-a-ponto, cujas extremidades são acessíveis por interfaces de rede tun0 em cada computador. Esse tipo de de interface de rede é um artifício do sistema operacional que possibilita a integração do protocolo de enlace a ser criado e a camada de rede (i.e., o protocolo IP). Apesar de se usar tal tipo de rede virtual para desenvolver o protocolo de enlace, uma vez concluído pode-se demonstrar que ele funciona também em um cenário real (computadores interligados por suas portas seriais).

Trab-rco2.png

Uma rede virtual do Netkit deve ser descrita por um arquivo de configuração que identifica as máquinas virtuais e especifica como elas devem ser interligadas. A rede virtual a ser usada como plataforma de desenvolvimento já foi configurada, e assim deve-se primeiro obter o arquivo trabalho-rco2.tar.gz que a descreve, e em seguida descompactá-lo. Ele contém a seguinte estrutura de arquivos e subdiretórios:

$ cd trabalho-rco2
$ ls -l
total 8
-rw-r--r-- 1 msobral msobral  229 2011-02-24 15:30 Lab.conf
drwxr-xr-x 2 msobral msobral 4096 2011-02-27 08:25 shared

O experimento contém duas máquinas virtuais, a1 e a2, que são os computadores que devem se comunicar via portas seriais usando seu protocolo. Isso pode ser vista lendo-se o arquivo Lab.conf, que descreve a interligação desses computadores pelas portas seriais:

$ cat Lab.conf
a1[type]=generic
a2[type]=generic

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

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 labstart 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 labhalt.

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 ...: fifo tem %d bytes\n",  link->fifo.bytes);
  // depois de reconstruir o quadro  recebido (fazer o enquadramento) e então
  // desencapsular um datagrama, este deve ser enviado para a camada de rede.
  // Use a seguinte função para enviar para a camada de rede:
  //
  // envia_para_camada_de_rede(struct Link * link, char * buffer_datagrama, int tamanho_datagrama);
  //
  // O datagrama deve estar contido em "buffer_datagrama".
  // O exemplo abaixo apenas copia os bytes recebidos e os entrega para a camada de rede, sem fazer qualquer 
  // processamento.

  char buffer_datagrama[MTU];
  int recebidos;

  printf("Chegou algo da camada fisica ...: fifo tem %d bytes\n",  link->fifo.bytes);
  for (recebidos=0; recebidos < MTU; recebidos++) {
    if (! fifo_retira(&link->fifo, &buffer_datagrama[recebidos])) break;
  }

  envia_para_camada_de_rede(link, buffer_datagrama, recebidos);

}

void vindo_da_camada_de_rede(struct Link * link) {
  printf("Chegou algo da camada de rede (%d bytes)...\n",  link->bytesRede);
  envia_para_camada_fisica(link, link->pduRede, 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 fifoOut; // o buffer dos bytes a serem transmitidos pela serial,
             // que constituem o quadro gerado pelo protocolo de enlace
             // (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 imediatamente, de forma que o quadro será transmitido gradualmente pela porta serial.

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. O datagrama será entregue imediatamente para a camada de rede através da interface tun0.

ATUALIZAÇÃO (23/03)

A escrita do programa pode ser facilitada com o uso do conceito de máquina de estados (também conhecido como autômato). Provavelmente vocês usaram isso na disciplina MIC. O uso de uma máquina de estados deve facilitar a recepção de quadros (função vindo_da_camada_física). A figura abaixo exemplifica uma máquina de estados para recepção, em que a transição de estado é desencadeada pela retirada de bytes da fila de recepção:

Fsm-recepção-proto-enlace-2011-1.png

Para cada byte retirado da fila uma ação deve ser realizada dependendo do estado em que se está:

  • Se o receptor estiver ocioso: a recepção de um byte 7E desencadeia o início da recepção de um quadro. Porém um byte recebido com qualquer outro valor deve ser simplesmente ignorado.
  • Se o receptor estiver recebendo um quadro: a recepção de um byte 7D acarreta o processamento de uma sequência de escape, em que se descarta esse byte 7D e se aceita o próximo byte. A recepção de um byte 7E significa fim de quadro, quando então o datagrama nele contido deve ser desencapsulado e entregue à camada de rede. Qualquer outro valor de byte implica seu armazenamento no buffer de recepção.

A escrita de uma máquina de estados em linguagem C pode ser feita com a estrutura de controle switch e o uso de uma variável para indicar qual o estado atual. O exemplo abaixo implementa uma máquina de estados com apenas dois estados, e pode ser usada como ponto de partida para a recepção do seu protocolo de enlace.

void vindo_da_camada_fisica(struct Link * link) {
  // Os estados são:
  // 0: INICIO, 1: recebendo
  // Uma variável "static" preserva seu valor mesmo após o término da função que a contém.
  static int estado = 0;

  // esta variável será útil para saber quantos bytes do quadro atual foram recebidos
  static int recebidos;

  int byte;

  // repete enquanto a fila não estiver  vazia ...
  while (fifo_retira(&link->fifo, &byte) {

    switch (estado) {
      case 0: // INICIO
        if (byte == 0x7E) { // inicio de quadro
          estado = 1; // vai pra estado "recebendo"
        }
        break;
      case 1:
        if (byte == 0x7E) { // fim de quadro
          estado = 0; // vai pra estado "INICIO"
          // desencapsula o datagrama e entrega pra camada de rede
        }
        // processa o byte recebido ...
        break;
    }
  }
}

Para quem for fazer a detecção de erros

O cálculo de FCS não é tão simples de fazer de forma eficiente, como explicado no apêndice C da RFC 1662:

The FCS was originally designed with hardware implementations in
mind.  A serial bit stream is transmitted on the wire, the FCS is
calculated over the serial data as it goes out, and the complement of
the resulting FCS is appended to the serial stream, followed by the
Flag Sequence.

The receiver has no way of determining that it has finished
calculating the received FCS until it detects the Flag Sequence.
Therefore, the FCS was designed so that a particular pattern results
when the FCS operation passes over the complemented FCS.  A good
frame is indicated by this "good FCS" value.

.. que pode ser traduzido assim:

O FCS foi projetado originalmente tendo em mente implementações em hardware. Uma sequência de bits
é transmitida no meio de transmissão, o FCS é calculado à medida que os dados serializados saem, e o
complemento do FCS resultante é adicionado ao final da sequência de bits, seguido pela Flag.

O receptor não tem como determinar que o cálculo do FCS recebido terminou até que a Flag é recebida.
Portanto, o FCS foi projetado de forma que se obtém um padrão em particular quando a operação do FCS é feita 
sobre o complemento do FCS. Um quadro "bom" é indicado pelo valor "bom FCS".

... como toda norma, entendê-la implica uma certa dose de conhecimento prévio e interpretação ;-) Em resumo, está escrito que a detecção de erro com FCS foi projetada para ser implementada em hardware, em que se pode criar um circuito lógico razoavelmente simples que cumpra essa função (ver livro "Comunicação de Dados, 3a ed.", de William Stallings). Em software deve-se criar um algoritmo que seja eficiente, para que o cálculo seja feito rapidamente.

O apêndice C dessa RFC 1662 contém algoritmos para calcular rapidamente tanto o CRC de 16 bits quanto o de 32 bits usados no PPP. Esses algoritmos estão convenientemente escritos em linguagem C, podendo ser importados e usados em outros programas. O cálculo e verificação do FCS com 16 bits segue abaixo (ele foi levemente reescrito para facilitar seu uso):

/*
    * u16 represents an unsigned 16-bit number.  Adjust the typedef for
    * your hardware.
    */
   typedef unsigned short u16;

   /*
    * FCS lookup table as calculated by the table generator.
    */
   static u16 fcstab[256] = {
      0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
      0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
      0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
      0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
      0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
      0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
      0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
      0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
      0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
      0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
      0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
      0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
      0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
      0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
      0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
      0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
      0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
      0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
      0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
      0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,

      0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
      0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
      0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
      0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
      0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
      0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
      0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
      0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
      0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
      0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
      0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
      0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
   };

   #define PPPINITFCS16    0xffff  /* Initial FCS value */
   #define PPPGOODFCS16    0xf0b8  /* Good final FCS value */

   /*
    * Calculate a new fcs given the current fcs and the new data.
    */
   u16 pppfcs16(fcs, cp, len)
       register u16 fcs;
       register unsigned char *cp;
       register int len;
   {
       ASSERT(sizeof (u16) == 2);
       ASSERT(((u16) -1) > 0);
       while (len--)
           fcs = (fcs >> 8) ^ fcstab[(fcs ^ *cp++) & 0xff];

       return (fcs);
   }

   /* calcula o valor de FCS e adiciona-o ao final do quadro */
   genfcs16(quadro, bytes)
       register unsigned char *quadro;
       register int bytes;
   {
       u16 trialfcs;

       trialfcs = pppfcs16( PPPINITFCS16, quadro, bytes );
       trialfcs ^= 0xffff;                 /* complement */
       quadro[bytes] = (trialfcs & 0x00ff);      /* least significant byte first */
       quadro[bytes+1] = ((trialfcs >> 8) & 0x00ff);
   }

   /* Verifica o valor de FCS contido no final de um quadro
    * Retorna 1 se FCS correto, e 0 se tiver erro */
   int checkfcs16(quadro, bytes)
       register unsigned char *quadro;
       register int bytes;
   {
       u16 trialfcs;

       trialfcs = pppfcs16( PPPINITFCS16, quadro, bytes);
       return ( trialfcs == PPPGOODFCS16 );
   }

ATUALIZAÇÃO (10/03)

Foi visto na aula de hoje que pode-se usar a função dump para mostrar o conteúdo de um quadro ou datagrama. Essa função mostra na tela os bytes em hexadecimal, e numa coluna à direita os caracteres correspondentes (se possível). O resultado é parecido com o que mostraria o tcpdump, como se pode ver abaixo:

Chegou algo da camada de rede (84 bytes)...
45 00 00 54 A8 B8 00 00 40 01 BD EE 0A 00 00 E..T....@......
02 0A 00 00 01 00 00 73 0A 11 02 00 01 46 18 .......s.....F.
79 4D CE 89 03 00 08 09 0A 0B 0C 0D 0E 0F 10 yM.............
11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F ...............
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E .!"#$%&'()*+,-.
2F 30 31 32 33 34 35 36 37                   /01234567

Para usar essa função, deve-se chamá-la passando como argumentos o buffer que contém o quadro ou datagrama e a respectiva quantidade de bytes. Por exemplo, para mostrar todo datagrama recebido da camada de rede pode-se fazer assim:

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

  // mostra o conteúdo do datagrama ...
  dump(link->pduRede, link->bytesRede);

  envia_para_camada_fisica(link, link->pduRede, link->bytesRede);
}

OBS: essa função está no arquivo utils.c, que fica no diretório trabalho-rco2/shared. Ela foi melhorada para apresentar os bytes de forma mais clara. Você deve baixar a nova versão do arquivo para usar a versão melhorada:

ATUALIZAÇÃO (03/03)

O Netkit foi atualizado, tendo sido acrescentada uma pequena funcionalidade ao link serial:

  • O link serial pode agora ter sua taxa de bits configurada. Assim, se ele for definido com uma taxa de bits de 128 kbps, a comunicação por esse link irá respeitar esse limite.

Para instalar essa atualização siga estes passos:

  1. Obtenha o arquivo de atualização netkit-update.tgz (aprox. 10 kB).
  2. Descompacte-o no mesmo diretório onde está instalado o Netkit. Por exemplo, se o Netkit está em /home/aluno/netkit, descompacte o arquivo netkit-update.tgz em /home/aluno:

Para configurar a taxa de transmissão de um link, edite o arquivo Lab.conf observando a especificação do link serial:

# Configuração de links seriais: a especificação do link serial é composta de:
# nome_do_link:BER:atraso:taxa_bits
#
# A especificação deve ser idêntica em ambas as máquinas virtuais a serem interligadas. A taxa de bits
# deve ser indicada em bits/segundo.
# O atraso de propagacao deve ser especificado em milissegundos.
#
# no exemplo abaixo, o link "serial", tem BER=0, atraso=0 e taxa de bits de 128 kbps

a1[ppp0]=serial:0:0:128000
a2[ppp0]=serial:0:0:128000

ATUALIZAÇÃO (02/03)

O Netkit foi atualizado, pois possuía três problemas:

  • Faltava o programa make, usado para compilar o programa do protocolo de enlace.
  • Ao descompactar o Netkit, era gerado um arquivo de 10 GB (o certo é pouco menos de 1 GB)
  • O emulador de link serial estava jogando lixo na linha, e isso iria atrapalhar a implementação do seu protocolo de enlace.

Remova a versão anterior e instale a versão abaixo: