# 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

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**



Inicialize a VM


:::
## 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:
:::

### 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.
:::

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*.

:::
:::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:

:::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:

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:

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:

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
```

7. No `h1`, execute:
```
python send.py
```
:::info
Note que até agora o exercício está muito semelhante ao anterior...
:::


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).
:::


:::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.
:::