UNIVERSIDADE NOVA DE LISBOA Faculdade de Ciências e Tecnologia Departamento de Engenharia Electrotécnica REDES INTEGRADAS DE TELECOMUNICAÇÕES I 2006 / 2007 Mestrado Integrado em Engenharia Electrotécnica e de Computadores 4º ano 7º semestre 2º Trabalho Prático: Gateway multicast para troca de ficheiros entre IPv6 e IPv4 http://tele1.dee.fct.unl.pt Luis Bernardo
1. Objectivos Familiarização com a programação usando endereços IPv6 e IPv4, os ambientes gráficos Gnome/Gtk+ e Eclipse, e os mecanismos de gestão de processos e de comunicação entre processos no sistema operativo Linux. O trabalho consiste no desenvolvimento de uma gateway para uma aplicação de troca de ficheiros numa rede dual stack. A gateway regista-se num endereço IPv6 Multicast e num endereço IPv4 Multicast, e reenvia os pacotes de pesquisa entre estes dois endereços. Depois propaga as respostas, permitindo suportar a pesquisa em paralelo nos dois grupos de clientes. Para permitir a transferência de ficheiros entre IPv6 e IPv4, a aplicação realiza tradução de endereços nos pacotes de respostas, intermediando as ligações entre IPv4 e IPv6. Cada ligação entre sockets IPv4 e IPv6 é realizada num sub-processo independente. O trabalho consiste no desenvolvimento de um executável: gateway2stack. 2. Especificações A aplicação gateway2stack interliga clientes de troca de ficheiros a operarem no domínio IPv4 com clientes a operarem no domínio IPv6. Dentro de cada domínio é usada a aplicação, fornecida juntamente com o enunciado, que suporta a pesquisa na rede e a descarga dos ficheiros localizados através de uma ligação. 2.1. A aplicação A aplicação realiza a pesquisa por ficheiros utilizando um socket datagrama. Para receber pedidos usa um socket associado ao endereço multicast do grupo e configurado para o número de porto do grupo. Para enviar pedidos e receber resposta usa outro socket privado (com um porto único). Para trocar ficheiros são usados um socket para receber ligações (com um porto único) mais um número arbitrário de sockets para enviar ficheiros. Canal Multicast IPv6 ou IPv4
A aplicação começa por aguardar que o utilizador seleccione o domínio (IPv4 ou IPv6) e configure o canal (endereço multicast + número de porto) onde pretende escutar. Após o utilizador premir um botão, a aplicação arranca com todos os sockets, e fica preparada para enviar ou receber mensagens ou ligações. Paralelamente, a aplicação permite modificar a lista de ficheiros partilhados. Quando o utilizador escolhe a pesquisa de um ficheiro, especifica quantas ligações pretende usar para o descarregar (N). A aplicação começa por enviar para o grupo o pedido e fica à espera de N respostas até um tempo máximo de espera. Caso tenha tido pelo menos uma resposta, lança um sub-processo por cada ligação, que cria um socket temporário para receber um fragmento do ficheiro. Caso falhe uma das ligações, tenta usar outro servidor para receber o fragmento omisso. Depois de receber todos os fragmentos, cria um ficheiro completo, numa directoria de saída definida na interface gráfica. Após a recepção de uma nova ligação, também cria um sub-processo para enviar o ficheiro pedido. A janela principal listar todos os sub-processos activos, os ficheiros recebidos, e a percentagem de bytes transferidos. 2.2. A aplicação gateway2stack A aplicação gateway2stack necessita de quatro parâmetros de configuração: os endereços IPv4 e IPv6 multicast dos grupos (por omissão vão-se usar os endereços "225.10.33.1" e "ff18:10:33::1") e os números de porto IPv4 e IPv6 (por omissão vão-se usar os portos 19999 e, respectivamente). A aplicação gateway deve receber os pedidos de cada uma das redes e propagá-los na outra rede, desde que ele não tenha sido propagado anteriormente. Para tolerar a existência de vários gateways activos em cada instante, a difusão na outra rede deve ser precedida de um atraso aleatório. A gateway deve criar uma tabela indexada pela chave de pesquisa, de pesquisas activas. As respostas devem ser repropagadas no sentido inverso. No caso de uma pesquisa IPv6 para IPv4, os endereços IPv4 devem ser traduzidos nos duais na forma "::ffff:ipv4". Numa pesquisa de IPv4 para IPv6, o gateway deve trocar os endereços e portos respectivamente para o endereço IPv4 local e para o porto do gateway. Neste segundo cenário (IPv4 para IPv6), o gateway vai receber as ligações dos clientes, e vai estabelecer uma ligação para o cliente IPv6 remoto, funcionando como intermediário. Opcionalmente, pode ainda recuperar transparentemente de falha de servidores IPv6, restabelecendo a ligação com outro servidor disponível na rede, antes de fechar a ligação para o cliente IPv4. 3
gateway2stack IPv4 IPv6 19999 IPv4 Canal Multicast IPv6 Canal Multi-cast IPv4 19999 IPv4 IPv6 IPv6 Para facilitar o desenvolvimento do trabalho, é fornecido um projecto Eclipse com o ficheiro de especificação de interface "gui_t2.glade" com a janela principal do programa, mais algumas funções adicionais de acesso à interface gráfica e de interacção com sockets. Estes ficheiros já incluem a leitura dos parâmetros de configuração dos sockets multicast e o esqueleto das funções de callback de dados, (que processam os pedidos). 2.3. Protocolo de pesquisa de ficheiros A partir do momento em que ficar activa, a aplicação pode enviar um pacote de pesquisa de ficheiro para o grupo. O nome a pesquisar não inclui o caminho (nomes de directórios) e é terminado com o carácter '\0'. Cada pacote de pesquisa contém uma chave única composta pela concatenação do endereço IP, número de porto privado, e número de sequência de pesquisa, permitindo a existência de vários pedidos simultaneamente, do mesmo cliente. A mensagem de pedido enviada para o grupo consiste num octeto com o tipo de mensagem, seguida do comprimento da chave do pedido, da cadeia de caracteres da chave, e de uma cadeia de caracteres com o nome que se pretende pesquisar (terminado com o caracter '\0'). A chave é criada como a composição do endereço IP, número de porto, e um número de sequência incrementado para cada pedido, podendo-se considerar única. Mensagem pesquisa de ficheiro: { sequência contígua de } unsigned char tipo; // tipo de mensagem - Query= 1 int len_chave; // comprimento de 'chave' char *chave; // array de caracteres terminado por '\0' char *nome; // array de caracteres terminado por '\0' 1 4 strlen(chave)+1 strlen(nome)+1 tipo len_chave chave nome Query tipo= 1 4
A aplicação gateway2stack deve memorizar todas as mensagens de pedido recebidas e os endereços e portos dos emissores respectivos, mantendo essa informação durante pelo menos 10 segundos, de forma a saber quais os pedidos que apareceram em cada uma das redes, e mais tarde, encaminhar as mensagens de resposta. Caso seja um pedido novo, deve reenviá-lo para a outra rede, introduzindo um atraso aleatório, de maneira a tolerar a existência de vários gateways a ligar duas redes. Caso não receba nenhuma resposta deve abortar a sessão associada ao pedido. Ao receber o pacote de resposta (Hit), a aplicação gateway2stack deve validar se a chave existe na sua lista de mensagens de pedido reenviadas. Depois deve traduzir a resposta para uma válida na rede de destino: Na rede IPv6, deve modificar o endereço IPv4 recebido para o endereço IPv6 equivalente (::ffff:ipv4), deixando que a pilha dupla de protocolos dos nós resolva o problema da ligação destino a destino; Na rede IPv4, deve modificar o endereço IPv6 recebido para o endereço IPv4 do gateway, e substituir o número de porto pelo número do porto v4 do gateway. Neste caso deve memorizar todos os Hits recebidos numa lista indexada pelas chaves, que depois usa quando recebe a ligação dos clientes. A mensagem de resposta enviada pela aplicação para o emissor do pedido consiste num octeto com o tipo de mensagem, seguida da comprimento da chave e da chave recebida no pedido, do nome do ficheiro pedido, do comprimento do ficheiro, e do valor de hash do conteúdo do ficheiro, do porto do socket onde recebe ligações, e do endereço IP do servidor em formato de texto. Mensagem encontrou ficheiro: { sequência contígua de } unsigned char tipo; // tipo de mensagem - Hit=2 int len_chave; // comprimento de 'chave' char *chave; // array de caracteres terminado por '\0' char *nome; // array de caracteres terminado por '\0' long len; // Comprimento do ficheiro encontrado int hash; // Valor de hash do ficheiro encontrado unsigned short portos; // porto do servidor char *ip; // array de caracteres com endereço IPv4 ou IPv6, // terminado por '\0' strlen(nome) 1 4 len_chave +1 tipo len_chave 4 chave nome len 4 hash 2 portos strlen(ip) +1 ip Hit tipo= 2 2.4. Transferência de ficheiros Depois de reunir as várias respostas, a aplicação selecciona um número de servidores igual (ou menor, se não houve respostas suficientes) ao número pedido pelo utilizador (N), e descarrega o ficheiro em N segmentos. Calcula qual a dimensão dos segmentos que pretende de cada servidor, e descarrega cada segmento individualmente a partir de ofertas distintas. A comunicação entre duas aplicações é composta por uma troca de mensagens: O iniciador da ligação envia uma mensagem de pedido de ficheiro, com a estrutura representada abaixo (inclui dados recebidos no pacote Hit (chave, filename, hash e len), e indica qual o segmento que pretende receber do emissor (os caracteres a começar na posição n0 até à posição n1-1)); o receptor responde enviando os bytes do ficheiro pedidos. Pedido de ficheiro no canal : { sequência contígua de } int len_chave; // comprimento de 'chave' 5
char *chave; int len_filename; char *filename; int hash; long len; int n0; int n1; // array de caracteres terminado por '\0' // Comprimento do nome do ficheiro (máx. 256 bytes) // array de caracteres terminado por '\0' // Valor de hash do ficheiro encontrado // Comprimento do ficheiro encontrado // Primeiro byte a enviar (começa em 0 o ficheiro) // Byte final (transmitir até n1-1) 4 len_chave 4 len_filename 4 4 4 4 len_chave chave len_filename filename hash len n0 n1 Nos pedidos de ficheiros de clientes IPv4 para clientes IPv6, a aplicação gateway2stack vai receber as ligações dos clientes IPv4 no seu socket IPv4 local. Para optimizar o paralelismo da aplicação, a leitura e escrita de blocos dos ficheiros trocados entre os clientes deve ser realizada em sub-processos criados especificamente para cada ligação. Após receber uma ligação de um cliente no socket v4 (1), é disparada a callback de aceitação de ligações. Esta callback deverá lançar um sub-processo (2), que deverá ficar bloqueado à espera da mensagem de pedido no socket da nova ligação. Após receber o pedido, o subprocesso deve enviar esta informação para o processo pai (4), que após consultar a lista de Hits, deve mandar o endereço IPv6 e o porto do cliente IPv6 a contactar (4). O subprocesso deverá depois criar uma nova ligação v6 para o cliente e enviar-lhe a mensagem de pedido (5), ficando a partir dai num ciclo de espera de pacotes de dados na ligação v6 e reenvio na ligação v4. Depois de terminar a transferência, e passado um tempo de segurança (para o caso de o cliente pedir outro bloco, para recuperar um erro), o gateway deve eliminar a informação sobre a chave de sessão. gateway2stack (1) (3) (4) (2) socket local sinal SIGUSR1 (5) (6) (6) Os sub-processos poderão comunicar com os processos pai através de um socket local (AF_UNIX) criado antes da separação do sub-processo, enviando mensagens com uma estrutura arbitrária, que informem o processo pai de: conteúdo do cabeçalho para selecção do Hit e preenchimento da interface gráfica; informação sobre evolução da transferência; fim da transferência (com informação sobre se houve erro, tempo de transferência e número de bytes). Como estas mensagens são internas ao programa, não são normalizadas no enunciado, devendo ser os alunos a definir a sua estrutura. O processo pai pode também comunicar com os subprocessos utilizando o sinal SIGUSR1 para os fazer parar. O processo pai também vai poder seguir o que está a ocorrer nos sub-processos através da rotina de tratamento do sinal SIGCHLD, executada quando morre um dos seus filhos. Através da análise do motivo de saída (_exit ou sinal) e do valor do _exit, é possível detectar se o processo enviou uma mensagem através do socket local, ou se falhou. 6
Extra: A aplicação gateway2stack pode (tentar) recuperar da falha de clientes IPv6 quando recebe mais do que um Hit para um pedido. Caso a ligação v6 falhe, o processo filho pode enviar uma mensagem para o processo pai a indicar qual foi o último byte recebido. Desta forma, o processo pai pode seleccionar um novo cliente IPv6, e desencadear a criação de uma nova ligação v6 para outro cliente IPv6, pedindo-se apenas os caracteres que faltam. Esta reparação seria transparente para o cliente IPv4 que pediu o ficheiro, pois a ligação v4 nunca seria destruída. 3. Desenvolvimento da aplicação Dados locais da aplicação Botão de arranque Configuração dos grupos IPv6 e IPv4 Lista de Query activas (sessões) Apagar Query Limpar janela de Log Parar transferência Lista de subprocessos activos Janela de Log Para facilitar o desenvolvimento da aplicação é fornecido uma gateway de teste totalmente funcional (gateway2stack), mais um ficheiro glade-2 com a definição da interface gráfica da gateway de teste, representada acima. Também o núcleo inicial do programa, e um conjunto de funções para gerir as tabelas gráficas (acrescentar e retirar elementos, pesquisar elementos), sockets (ficheiro sock.c), etc. Observe-se que os programas de teste fornecidos são intencionalmente lentos porque incluiem um 'usleep' no ciclo de envio/retransmissão/recepção de ficheiro, que o faz "dormir" durante 5 ms entre blocos do ficheiro, mas permite o tratamento dos eventos gráficos (leitura de botões, etc.). A interface contém uma linha inicial onde escreve os dados locais da aplicação (endereços IPv6 e IPv4, o número de porto onde recebe ligações e o número de processo). O botão "Activo" controla o arranque do servidor. A segunda linha permite configurar os endereços multicast e os números de porto IPv6 e IPv4. A quarta linha contém uma tabela (do tipo GtkCList) com a lista de pedidos (Query) recebidos, que aguardam Hits ou têm ligações activas. A tabela representa a chave de pesquisa da sessão, a lista de Hits recebida, e o número de subprocessos activos. A linha seguinte contém os comandos de paragem de sessões (Query) ou transferência de ficheiros activas, ou limpar a janela de log. As linhas devem ser previamente marcadas antes de premir os botões. A sexta linha contém a lista de sub-processos activos num dado instante, com o número de pid, de pipe, a chave de sessão, o endereço IP do servidor, o nome do ficheiro, e a percentagem de bytes transferidos. 7
A sétima, e última linha, contém uma caixa para escrever mensagens. O trabalho deve ser desenvolvido em várias fases distintas: 1. ler e compreender o código que é fornecido com o enunciado do trabalho (ANTES DA PRIMEIRA AULA); 2. declarar todos os sockets e terminar a programação das rotinas "init_sockets", "close_sock" e "close_sock", associando às callbacks existentes no código para tratar os eventos respectivos o código disponibilizado inclui a maior parte deste passo comentado; só tem de tirar os comentários e COMPREENDER; 3. programar a rotina de recepção do pacote "Query" para IPv4 e IPv6 (procure definir uma função que trate os dois tipos). Deverá definir uma estrutura (struct) para memorizar o conteúdo do pacote (e para guardar uma GList de Hits). Deverá também programar um temporizador com um tempo aleatório, para reenviar o pacote Query na outra rede, que evite o envio duplicado de Queries numa rede; 4. programar a rotina de recepção do pacote "Hit". Esta função deve reenviar os pacotes Hit, modificando o endereço IP. Para Hits IPv6, deverá definir uma estrutura para memorizar o conteúdo dos pacotes e guardá-los na lista da Query associada; 5. programar um temporizador para remover uma Query quando expira o tempo máximo sem receber uma ligação ; 6. programar a rotina de recepção de ligações, de forma a lançar um sub-processo que recebe a mensagem de pedido (esta leitura não pode ser feita no processo pai, para não o bloquear), e a envia ao processo pai; 7. programar a rotina de recepção de dados no socket local AF_UNIX do pai, de forma a receber o cabeçalho e enviar um endereço IPv6 e número de porto; 8. continuar a programação do sub-processo, recebendo o IP e o porto, estabelecendo a ligação, e ficando em ciclo a reenviar o ficheiro bloco a bloco, até chegar ao fim. Durante este processo pode enviar mensagens para o processo pai, pelo socket AF_UNIX, a informar o estado do processo; 9. SE TIVER TEMPO programe a recuperação de erros em clientes IPv6, reestabelecendo uma ligação para outro cliente, caso a inicial falhe. Para se conseguir chegar ao fim das seis semanas do trabalho (uma de aprendizagem + cinco de trabalho) com tudo pronto é necessário utilizar todas as aulas práticas, devendo-se: começar a fase (3) durante a semana 8; a fase (4) durante a semana 9; a fase (6) durante a semana 10; e a fase (8) durante a semana 11. No mínimo, pretende-se que todos os alunos cheguem ao fim da fase 5. Não se esqueça que no fim do semestre vai ser necessário entregar TODOS os trabalhos. Não deixe para a última semana o que pode fazer ao longo das primeiras semanas, porque NÃO VAI CONSEGUIR FAZER TODO O TRABALHO NA ÚLTIMA SEMANA. 8
Postura dos Alunos Cada grupo deve ter em consideração o seguinte: Não perca tempo com a estética de entrada e saída de dados Programe de acordo com os princípios gerais de uma boa codificação (utilização de indentação, apresentação de comentários, uso de variáveis com nomes conformes às suas funções...) e Proceda de modo a que o trabalho a fazer fique equitativamente distribuído pelos dois membros do grupo. DATAS LIMITE A parte laboratorial é composta por dois trabalhos de avaliação. A duração prevista é de respectivamente 4 e 5 semanas para o primeiro e para o segundo trabalho, onde a primeira semana é usada para aprendizagem ao ambiente de desenvolvimento. A parte prática tem o seu início formal na semana de 18 de Setembro. O quadro seguinte mostra as datas de entrega de cada trabalho de avaliação (P) e as datas previstas para os testes teóricos (T): Setembro 2005 Outubro 2005 1 2 3 1 2 3 4 5 6 7 3 4 5 6 7 8 9 4 8 9 10 11 12 13 14 1 10 11 12 13 14 15 16 5 15 L1 17 18 19 20 T1 2 17 18 19 20 21 22 23 6 22 23 24 25 26 27 28 3 24 25 26 27 28 29 30 7 29 30 31 Novembro 2005 Dezembro 2005 8 1 2 3 4 12 1 2 9 5 6 7 8 9 10 11 13 3 4 5 6 7 8 T2 10 12 13 14 15 16 17 18 14 10 11 12 13 14 15 16 11 19 20 21 22 23 24 25 17 18 19 20 21 22 23 12 26 27 28 29 L2 24 N 26 27 28 29 30 9