# Laboratório 5: P4 In-Band Network Telemetry Requisitos: * [Virtualbox](https://www.virtualbox.org/) * [VM do P4 2019](https://drive.google.com/uc?id=1lYF4NgFkYoRqtskdGTMxy3sXUV0jkMxo&export=download) - **Obs.:** A VM é a mesma do tutorial anterior ([Lab 4: Introdução a programação de dispositivos com a linguagem P4](https://hackmd.io/fB9jMwUWTWe642Z020PycQ)) e já contém o switch virtual modelo BMv2 e o Mininet. ## Introdução Neste laboratório, vamos explorar o poder da linguagem P4 para realizar o monitoramento de redes. Cada arquitetura (por exemplo, *TNA*, *v1model*) possui diferentes restrições para a coleta de métricas na rede. Por exemplo, o switch virtual BMv2 utiliza arquitetura *v1model*, que é um modelo de arquitetura para experimentos virtuais. Nela, podemos encontrar uma série de informações que podem ser coletadas de forma intrínsica do dispositivo -- como pode ser visto no seguinte [link](https://github.com/p4lang/p4c/blob/main/p4include/v1model.p4). Além disso, essas e outras informações podem ser combinadas para gerar diferentes informações. Veja abaixo alguns exemplos de aplicações do P4 para o monitoramento: ### Exemplos de aplicação 1. Na arquitetura *v1model*, podemos coletar o instante de tempo (timestamp) em que o pacote chegou ao pipeline de ingresso e o tempo em que ele chegou ao pipeline de egresso da arquitetura: ``` @alias("intrinsic_metadata.ingress_global_timestamp") bit<48> ingress_global_timestamp; @alias("intrinsic_metadata.egress_global_timestamp") bit<48> egress_global_timestamp; ``` Dessa forma, basta calcular a diferença entre ambos, para obter uma terceira informação: o tempo em que o pacote estava sendo processado no pipeline de ingresso! 2. Poderíamos tomar decisões de roteamento customizado, com base na coleta das portas de ingresso/egresso durante o caminho do fluxo: ``` #define MAX_HOPS 10 probe_fwd_t[MAX_HOPS] probe_fwd; header probe_fwd_t { bit<8> egress_spec; } ``` No trecho de código acima, estamos definindo um vetor de tamanho `MAX_HOPS`, que define quantos headers do tipo `probe_fwd_t` podemos armazenar em um pacote de rede, contendo a informação da porta de saída `egress_spec` do *hop* anterior. Dessa forma, podemos armazenar até 10 portas de saídas ao longo do caminho e enviar esta informação a um coletor onde essas informações podem ser utilizadas como métricas para futuras decisões. :::warning ## OPCIONAL: Instalação da VM - caso não tenha feito no último LAB Atenção: Os equipamentos devem ter 25GB ou mais livres de espaço em disco para importar a VM. 1. Acesse a página de [Downloads](https://www.virtualbox.org/wiki/Downloads) do VirtualBox. 2. Logo, selecione a sua plataforma corretamente -- Windows, Linux, MAC, etc -- e baixe o Virtualbox ![Página de Downloads do Virtualbox](https://hackmd.io/_uploads/rk6b_LaWa.png) Obs.: Se for Ubuntu, instale da seguinte forma: ``` sudo dpkg -i virtualbox-<versao>~Ubuntu~<versao>_amd64.deb ``` Se reclamar de dependências, execute: ``` sudo apt install -f ``` 3. Importe a VM que foi baixada: **P4 Tutorial 2019-04-25.ova** ![](https://hackmd.io/_uploads/Bk3Ih8T-p.png) ![](https://hackmd.io/_uploads/HkWjnIaZp.png) ![](https://hackmd.io/_uploads/HyytZPpZa.png) Inicialize a VM ![](https://hackmd.io/_uploads/Sy9dGP6-6.png) ![](https://hackmd.io/_uploads/BkW5fPa-6.png) ::: ## Configuração A VM roda o SO Ubuntu 16.04 e dispõe de todas as funcionalidades do Mininet e da linguagem P4 já instaladas. Além disso, a VM possui todos os arquivos e pacotes necessários para utilização nas atividades do laboratório. Dessa forma, o aluno não precisará instalar quaisquer ferramentas para realizar esta atividade. Todas as dicas e configurações mostradas para VirtualBox podem ser realizadas pelo próprio aluno em um computador pessoal. Primeiro acesso à Máquina Virtual: :::info Para acessar a VM do P4 utilize as seguintes credenciais: Login: p4 Password: p4 ::: Após o acesso à maquina virtual, abra o terminal e faça o download do projeto Github contendo os exercicios necessários. ``` git clone https://github.com/arielgoes/Lab-5_P4_INT_UNICAMP.git ``` ## FAQ - Potenciais problemas Sempre que você estiver com algum problema na execução do laboratório, retorne ao FAQ para verificar se a solução não está aqui. :::warning Execute sempre o comando make clean entre uma execução e outra ::: :::warning No caso de um erro similar a: Exception: Error creating interface pair (s1-eth2,s2-eth2): RTNETLINK answers: File exists Execute o comando sudo mn -c ou make clean ::: :::warning No caso de erro de compilação, e você tiver convicção de que o seu código esta correto, exclua as pastas 'build', 'logs' e 'pcaps' da pasta do exercicio atual. Também, é recomendado que você exclua essas pastas **antes** de cada nova execução do comando `make run`. ::: ## Atividades Práticas Neste tutorial, vamos realizar o monitoramento de rede com os conceitos de In-Band Network Telemetry (INT) e a linguagem P4. Para isso, vamos criar pacotes *probe* (ou seja, manualmente construídos) e coletar métricas de desempenho da rede. Mais especificamente, vamos monitorar a taxa de transferência à nível de portas dos switches - onde os pacotes estiverem passando. A estrutura dos pacotes *probe* que serão enviados pode ser resumida abaixo: ``` // Top-level probe header, indicates how many hops this probe // packet has traversed so far. header probe_t { bit<8> hop_cnt; } // The data added to the probe by each switch at each hop. header probe_data_t { bit<1> bos; bit<7> swid; bit<8> port; bit<32> byte_cnt; time_t last_time; time_t cur_time; } // Indicates the egress port the switch should send this probe // packet out of. There is one of these headers for each hop. header probe_fwd_t { bit<8> egress_spec; } ``` Cada pacote probe, contém esses três *headers*: 1. O header `probe_t` realiza a contagem dos hops/saltos em que o pacote passou; 2. O header `probe_data_t`, coleta (de fato) as informações de monitoramento nos switches. Quando o pacote chega ao destino, ele deve ser lido para se obter os resultados; 3. O header `probe_fwd_t` define por qual porta o pacote de egresso do switch o pacote deverá ser encaminhado. *Ex.: Se o pacote contiver a sequência de headers `probe_fwd`: 1-2-2, significa que o pacote deverá ser encaminhado pela porta 1 do primeiro switch, porta 2 do segundo switch e porta 2 do terceiro switch*. ``` probe_pkt=Ether(src=get_if_hwaddr('<nome-interface>'), dst='ff:ff:ff:ff:ff:ff') / \ Probe(hop_cnt=0) / \ ProbeFwd(egress_spec=1) / \ ProbeFwd(egress_spec=2) / \ ProbeFwd(egress_spec=2) ``` :::warning Durante o laboratório, iremos criar e monitorar uma topologia em linha (topologia linear), como a demonstrada pela figura abaixo: ::: ![](https://hackmd.io/_uploads/B1m_EugGa.png) ### Atividade 1: Criando uma topologia linear Neste exercício, vamos criar a topologia descrita, que contem 3 switches e 2 hosts. :::info H1 - S1 - S2 - S3 - H2 ::: Para isso: 1. Acesse o exercício 1 no seu caminho local. Por exemplo `cd /home/p4/Lab-5_P4_INT_UNICAMP/exercises/ex1` 2. Altere o arquivo `topology.json` desta pasta (`subl topology.json`), modificando o número de hosts e os links, modificando a topologia com o número de hosts e links abaixo: Hosts: ``` h1 e h2 ``` Links: ``` h1 <--> s1, s1 <--> s2, s2 <--> s3, s3 <--> h2 ``` Original: `topology.json:` ``` { "hosts": [ "h1", "h2", "h3" ], "switches": { "s1": { "runtime_json" : "s1-runtime.json" }, "s2": { "runtime_json" : "s2-runtime.json" }, "s3": { "runtime_json" : "s3-runtime.json" } }, "links": [ ["h1", "s1"], ["s1", "s2"], ["s1", "s3"], ["s3", "s2"], ["s2", "h2"], ["s3", "h3"] ] } ``` :::warning Dica: O arquivo `topology.json` original possui mais hosts do que o necessário, e os links não estão de acordo com o requisitado. ::: ![Imagem gerada no website Spear by Narmox http://demo.spear.narmox.com/app/?apiurl=demo#!/mininet](https://hackmd.io/_uploads/S1kouhAZ6.png) A imagem acima deve ser a topologia final. Caso queira verificar, diferentes comandos no Mininet podem mostrar informações da topologia. Por exemplo, os comando `dump` e `links`: Após realizar as alterações acima, vamos levantar o ambiente Mininet: 1. Ainda na pasta do exercício 1 `ex1`, execute o comando a seguir no terminal para levantar os hosts, switches e links que serão lidos do arquivo `topology.json`: ``` make ``` 2. Verifique se as alterações realizadas na topologia refletem a nova topologia linear `H1 - S1 - S2 - S3 - H2`: ``` mininet> dump <P4Host h1: h1-eth0:10.0.1.1 pid=2251> <P4Host h2: h2-eth0:10.0.3.2 pid=2254> <ConfiguredP4RuntimeSwitch s1: lo:127.0.0.1,s1-eth1:None,s1-eth2:None pid=2257> <ConfiguredP4RuntimeSwitch s2: lo:127.0.0.1,s2-eth1:None,s2-eth2:None pid=2261> <ConfiguredP4RuntimeSwitch s3: lo:127.0.0.1,s3-eth1:None,s3-eth2:None pid=2265> mininet> links h1-eth0<->s1-eth1 (OK OK) h2-eth0<->s3-eth1 (OK OK) s1-eth2<->s2-eth1 (OK OK) s2-eth2<->s3-eth2 (OK OK) ``` :::info A saída dos comandos `dump` e `links` pode ser utilizada no seguinte [**link**](http://demo.spear.narmox.com/app/?apiurl=demo#!/mininet) para visualização da topologia. Basta copiar e colar a sua saída nos campos designados e pressionar o botão *Render Graph*. ![](https://hackmd.io/_uploads/rycqTSlMa.png) ::: :::info Obs.: Os arquivos com as regras de encaminhamento (s1-runtime.json, s2-runtime.json, s3-runtime.json) já estão alterados corretamente. Basta alterar o `topology.json`. Obs.: Estamos assumindo que o mininet irá atribuir os seguintes IPs: H1 - 10.0.1.1 H2 - 10.0.3.2 E os seguintes MACs: H1 - 00:00:00:00:01:01 H2 - 00:00:00:00:03:02 ::: :::warning **Antes de continuar para o `Exercício 2`, verifique o seguinte:** No terminal onde o Mininet está sendo executado, ou seja, com o prefixo `mininet>`, execute `h1 ping h2`: ``` mininet> pingall ``` A saída esperada deve estar de forma similar abaixo: ``` ... *** Ping: testing ping reachability h1 -> h2 h2 -> h1 *** Results: 0% dropped (2/2 received) mininet> ``` ::: ### Atividade 2: Monitorando os enlaces de rede O **objetivo** deste exercício é **analisar a estrutura de um programa P4** que permita a um host monitorar a utilização de todos os links na rede. Este exercício se baseia no exercício básico de encaminhamento IPv4, portanto, certifique-se de concluí-lo antes de tentar este. Especificamente, iremos modificar o programa P4 básico para processar um pacote de sonda/probe com *source routing*, de modo que ele seja capaz de capturar a utilização do link de saída/egresso em cada salto, de um host (por exemplo, `H1`) para um host destino (por exemplo, o `H2`) - com a finalidade de realizar monitoramento do estado da rede. Antes de tudo, vamos: 1. Acessar a pasta na qual o exercício 2 está localizado: ``` cd /home/p4/Lab-5_P4_INT_UNICAMP/exercises/ex2 ``` 2. Visualizar o código P4 `basic.p4`: ``` subl basic.p4 ``` :::info Vamos começar, entendendo quais protocolos e headers estamos utilizado. Responda: 1. O que são as seguintes linhas no começo do código P4? ``` const bit<16> TYPE_IPV4 = 0x800; const bit<16> TYPE_PROBE = 0x812; ``` Para mais informações da pergunta #1 acima, acesse o seguinte [link](https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers) ::: Agora, veja a representação do parser no código P4 e na imagem a seguir: ![](https://hackmd.io/_uploads/SJz-Yfxza.png) :::info Responda: 1. Se eu mandar um pacote com headers TCP ou UDP, vão ser aceitos ou descartados? 2. Podem existir headers empilhados (ou seja vários iguais) nesse parser? Se sim, existe um limite? ::: Agora, vamos realizar uma breve análise sobre o que está sendo feito nesse código. :::warning Derrube o ambiente anterior (caso esteja executando o Mininet ainda): ``` mininet> exit make clean ``` ::: 1. Levante o ambiente novamente: ``` make ``` 2. Teste a conectividade: ``` mininet> pingall *** Ping: testing ping reachability h1 -> h2 h2 -> h1 *** Results: 0% dropped (2/2 received) mininet> ``` 3. Abra um terminal para o host `H1` e outro para o `H2`: ``` mininet> xterm h1 h2 ``` 4. Execute o script de servidor no `H2` para esperar por pacotes de rede. ``` H2: python receive.py ``` :::info Verifique o script `send.py`: ``` probe_pkt=Ether(src=get_if_hwaddr('h1-eth0'), dst='ff:ff:ff:ff:ff:ff') / \ Probe(hop_cnt=0) / \ ProbeFwd(egress_spec=2) / \ ProbeFwd(egress_spec=2) / \ ProbeFwd(egress_spec=1) ``` Responda: 1. A definição do pacote `probe_pkt` está definindo o MAC de origem a partir de uma interface do `H1`? 2. Por que existem três headers `ProbeFwd` empilhados? :::warning Dica: Para a pergunta #2 acima, você pode: * Verificar o parser do código; * Verificar a existência ou não de vetores na `struct headers`; * Conhecer a topologia em que estamos trabalhando. ::: 5. Execute o script para enviar pacotes no `H1` ``` H1: python send.py ``` 6. Verifique se o `H2` está recebendo pacotes no terminal aberto com o comando`xterm h2` anteriormente: :::info Exemplo de saída esperada pelo Passo #6: ![](https://hackmd.io/_uploads/H1OxrbgMa.png) Responda: 1. Sua saída está parecida? 2. Por que você acha que o throughput do switch 3 é maior que o 2 e, respectivamente, maior que o switch 1? :::warning Dica (pergunta #2): Qual a diferença dos pacotes que passam pelo switch 1, switch 2, e switch 3? ::: ::: ### Atividade 3: Detectando variações nas métricas de Throughput No exercício anterior, foi possível visualizar (ao final) que os bytes estão sendo coletados por pacotes e essas informações são utilizadas para computar o throughput em cada porta (no script `receive.py`). O que aconteria se adiconássemos mais um host (ou seja, o `H3`) enviando mais informações? Isso iria refletir no throughput nas portas onde o fluxo do `H3` transitar? Para responder a essa pergunta, vamos modificar a topologia do exercício 2 - de forma similar à alteração realizada no exercício 1. Neste caso, vamos adicionar o `H3`, conectado ao switch 2, por exemplo: ![](https://hackmd.io/_uploads/SyPC4dxz6.png) 1. Abra a topologia `topology.json` na pasta `ex3`: ``` { "hosts": [ "h1", "h2" ], "switches": { "s1": { "runtime_json" : "s1-runtime.json" }, "s2": { "runtime_json" : "s2-runtime.json" }, "s3": { "runtime_json" : "s3-runtime.json" } }, "links": [ ["h1", "s1"], ["s1", "s2"], ["s2", "s3"], ["s3", "h2"] ] } ``` 2. Altere a topologia da seguinte forma: ``` { "hosts": [ "h1", "h2", "h3" ], "switches": { "s1": { "runtime_json" : "s1-runtime.json" }, "s2": { "runtime_json" : "s2-runtime.json" }, "s3": { "runtime_json" : "s3-runtime.json" } }, "links": [ ["h1", "s1"], ["s1", "s2"], ["s2", "s3"], ["s3", "h2"], ["s2", "h3"] ] } ``` Topologia desejada: ![](https://hackmd.io/_uploads/BJs-Y8eza.png) Agora temos um host para cada switch. 3. Levante o ambiente Mininet do exercício 3 `ex3`: ``` make clean make ``` 4. Verifique a conectividade entre os hosts no terminal `mininet>`: ``` mininet> pingall *** Ping: testing ping reachability h1 -> h2 h3 h2 -> h1 h3 h3 -> h1 h2 *** Results: 0% dropped (6/6 received) ``` 5. Abra um terminal para cada host (`h1`, `h2`, `h3`): ``` mininet> xterm h1 h2 h3 ``` 6. No `h2`, execute: ``` python receive.py ``` ![](https://hackmd.io/_uploads/SkaZ5vgGT.png) 7. No `h1`, execute: ``` python send.py ``` :::info Note que até agora o exercício está muito semelhante ao anterior... ::: ![](https://hackmd.io/_uploads/BJGt9DxGp.png) ![](https://hackmd.io/_uploads/rJ4c9vlMT.png) 8. No `h3`, execute: ``` iperf -c 10.0.3.2 -u -t 10 -b 1Mb ``` :::info O comando acima irá enviar pacotes UDP `-udp` a uma taxa de 1Mbit/s `-1Mb` durante 10 segundos `-t 10` para o 'h2' `-c 10.0.3.2`. ::: :::warning Estamos assumindo que *10.0.3.2* é o IP destino do `h2` que está conectado no switch 3 (no canto direito da topologia linear). ::: ![](https://hackmd.io/_uploads/r1Sn9PeM6.png) ![](https://hackmd.io/_uploads/ryOTqvgfp.png) :::info Responda: 1. Você percebeu alguma diferença na taxa de transferência? Justifique. 2. Quais portas estão sendo afetadas pelo envio de informação? Justifique. ::: :::warning Dica (pergunta #2): Verifique se houve alguma alteração no script `send.py` e nas regras de encaminhamento (por exemplo, `s1-runtime.json`, `s2-runtime.json`, `s3-runtime.json`). ::: :::success Parabéns! Se você chegou até aqui, já conseguiu concluir com sucesso este laboratório. :::