MQTT e Mosquitto: Analise do protocolo com Wireshark

De Wiki Cursos IFPR Foz
Ir para navegaçãoIr para pesquisar

Analise do protocolo MQTT e Mosquitto com Wireshark

Ferramentas utilizadas

Para a análise do protocolo MQTT foi utilizado as seguintes ferramentas
  • mosquitto: rodando no localhost Ubuntu 18.04 (IP 192.168.0.13);
  • wireshark: rodando também no localhost;
  • mosquitto_sub: executado em um linux Alpine rodando em um contêiner Docker (IP 172.17.0.2);
  • mosquitto_pub: executado em um linux Alpine rodando em um contêiner Docker (IP 172.17.0.3).

Montagem dos contêineres Docker

Ver nos links informações sobre instalação do Docker e como rodar máquinas linux Alpine.

Rodar duas máquinas alpine no Docker:

docker run -dit --name alpine1 alpine ash
docker run -dit --name alpine2 alpine ash

Verificar os contêineres iniciados:

docker container ls

Verificar a configuração de rede:

docker network inspect bridge
Note que alpine1 e alpine2 pertencem a sub-rede 172.17.0.0/16 e gateway 172.17.0.1.
Este gateway conecta a sub-rede do Docker ao computador hospedeiro e vice-versa. Ver roteamento no hospedeiro com:
route -n

Usar o terminal de cada alpine para instalar o mosquitto-clients:

docker attach alpine1
Para abrir o terminal da alpine1
apk update
apk add mosquitto-clients
Para instalar pacotes na alpine1
Repetir o processo para a alpine2
CTRL-p CTRL-q
Para sair do terminal

Experimento 1: Operação básica do protocolo MQTT

Passos
  • Iniciar o mosquitto.
  • Iniciar o analisador de pacotes wireshark:
    • configurar captura para o endereço do gateway do docker (172.17.0.1);
    • configurar filtro para o protocolo mqtt.
  • Abrir um terminal na alpine1 para executar o mosquitto_sub:
mosquitto_sub -t "topico"
  • Observar no wireshark os pacotes capturados.
  • Abrir um terminal na alpine2 para executar o mosquitto_pub:
mosquitto_pub -m "mensagem" -t "topico"
  • Observar no wireshark os pacotes capturados.
  • Aguardar um tempo e verificar a captura de pacotes de PINGREC e PINGRESP no wireshark que visam manter a conexão ativa (keep alive).
  • No terminal da alpine1 realizar CTRL-C para cancelar a subscrição do tópico.
  • Observar no Wireshark os pacotes capturados.

Análise das mensagens do protocolo MQTT capturadas

Saída do Wireshark
Obtida com comando Statistics/Flow Graph.

Análise dos pacotes capturados
  • Após a execução do comando:
mosquitto_sub -t "topico"
  • Foram capturadas as mensagens MQTT entre o cliente subscritor (172.17.0.2) e o brocker (192.168.0.13):
Connect Command
Connect Ack
Subscribe Request
Subscribe Ack
Sequência de mensagens de conexão, reconhecimento, subscrição e reconhecimento.
Note que as mensagens estão todas na marca de tempo 6.45....
Note que o cliente usa porta 39550 e o brocker usa porta 1883.
  • Após a execução do comando:
mosquitto_pub -m "mensagem" -t "topico"
  • Foram capturadas as mensagens MQTT entre o cliente publicador (172.17.0.3) e o brocker (192.168.0.13):
Connect Command
Connect Ack
Publish Message
Publish Req
Disconnect Req
Sequência de mensagens de conexão, reconhecimento, publicação, reconhecimento e desconexão.
Note que o cliente usa porta 52856 e o brocker usa porta 1883.
Note também que o cliente publicador se desconecta após a publicação de sua mensagem.
  • Na sequência foi capturada mensagem entre o o brocker (192.168.0.13) e o cliente subscritor (172.17.0.2):
Publish Message
Esta segunda publicação refere-se a publicação do brocker ao subscritor (porta 39550).
Note que não houve mensagem de conexão, pois o subscritor já estava conectado.
  • Após um tempo de 60s no subscritor tivemos um sequência de keep alive.
Ping Request
Ping Response
Marca de tempo 65,99...
Note que as portas origem e destino são as mesmas do cliente subscritor.
  • Por fim, após o CTRL-C no subscritor tivemos a conexão encerrada.
Disconnect Req

Logs do servidor Mosquitto

No servidor onde foi executado o Mosquito com comando:

mosquitto -v

Produziu-se os seguintes logs:

$ mosquitto -v
1586286914: mosquitto version 1.6.8 starting
1586286914: Using default config.
1586286914: Opening ipv4 listen socket on port 1883.
1586286914: Opening ipv6 listen socket on port 1883.
1586286950: New connection from 172.17.0.2 on port 1883.
1586286950: New client connected from 172.17.0.2 as mosq-qBvzZOWQ3LYlYmmIad (p2, c1, k60). 
1586286950: No will message specified.
1586286950: Sending CONNACK to mosq-qBvzZOWQ3LYlYmmIad (0, 0)
1586286950: Received SUBSCRIBE from mosq-qBvzZOWQ3LYlYmmIad
1586286950: 	topico (QoS 0)
1586286950: mosq-qBvzZOWQ3LYlYmmIad 0 topico
1586286950: Sending SUBACK to mosq-qBvzZOWQ3LYlYmmIad
1586286970: New connection from 172.17.0.3 on port 1883.
1586286970: New client connected from 172.17.0.3 as mosq-IRSh0LaxjMfYDMRrLF (p2, c1, k60).
1586286970: No will message specified.
1586286970: Sending CONNACK to mosq-IRSh0LaxjMfYDMRrLF (0, 0)
1586286970: Received PUBLISH from mosq-IRSh0LaxjMfYDMRrLF (d0, q0, r0, m0, 'topico', ... (8 bytes))
1586286970: Sending PUBLISH to mosq-qBvzZOWQ3LYlYmmIad (d0, q0, r0, m0, 'topico', ... (8 bytes))
1586286970: Received DISCONNECT from mosq-IRSh0LaxjMfYDMRrLF
1586286970: Client mosq-IRSh0LaxjMfYDMRrLF disconnected.
1586287009: Received PINGREQ from mosq-qBvzZOWQ3LYlYmmIad
1586287009: Sending PINGRESP to mosq-qBvzZOWQ3LYlYmmIad
1586287015: Received DISCONNECT from mosq-qBvzZOWQ3LYlYmmIad
1586287015: Client mosq-qBvzZOWQ3LYlYmmIad disconnected.
Note que os logs no servidor correspondem as mensagens trocadas pelo protocolo MQTT.

Outros cenários de análise do protocolo MQTT

Subscrição e publicação com QoS=1

Comandos para subscrição e publicação:

mosquitto_sub -h 192.168.0.13  -t "topico" -q 1
mosquitto_pub -h 192.168.0.13  -m "mensagem" -t "topico" -q 1

Análise dos pacotes capturados
Cada mensagem de publicação (Publish) é seguida pelo reconhecimento (Publish Ack).

Subscrição e publicação com QoS=2

Comandos para subscrição e publicação:

mosquitto_sub -h 192.168.0.13  -t "topico" -q 2
mosquitto_pub -h 192.168.0.13  -m "mensagem" -t "topico" -q 2

Análise dos pacotes capturados
Cada mensagem de publicação (Publish) é seguida por mensagem de publicação recebida (Publish Received), depois publicação liberada (Publish Release) e publicação completa (Publish Complete).

Reter Mensagens Publicadas

Caso o cliente publique com o retainFlag = true o brocker vai reter a mensagem e publicá-la a um cliente subscritor assim que o mesmo realize a subscrição deste tópico.

Comandos para subscrição e publicação:

mosquitto_pub -h 192.168.0.13  -m "mensagem" -t "topico" -q 1 -r
mosquitto_sub -h 192.168.0.13  -t "topico" -q 1

Análise dos pacotes capturados
Nas primeiras mensagens capturadas o cliente se conectou, publicou uma mensagem (QoS=1) e se desconectou. Depois um cliente se conectou, subscreveu u tópico (QoS=1). Loco em seguida o brocker publicou a mensagem retida ao cliente.

Sessão Persistente

Caso o cliente subscritor se conecte com flag clean session = false a sessão é tratada como persistente e as subscrições do cliente vão continuar após a próxima conexão.

Para receber as mensagens que perdeu enquanto estava desconectado, as mesmas devem ter QoS 1 ou 2.

Comandos executados para subscrição e publicação:

mosquitto_sub -h 192.168.0.13  -t "topico" -q 1 -c
mosquitto_pub -h 192.168.0.13  -m "mensagem1" -t "topico" -q 1
Subscritor foi desconectado com CTRL-C
mosquitto_pub -h 192.168.0.13  -m "mensagem2" -t "topico" -q 1
mosquitto_sub -h 192.168.0.13  -t "topico" -q 1 -c

Análise dos pacotes capturados
  1. Na marca de tempo 0.0... um cliente subscritor se conecta e subscreve um tópico (QoS=1) com sessão persistente.
  2. Na marca de tempo 12.8... o cliente publicador se conecta e publica uma mensagem e se desconecta e o brocker também publica a mensagem ao subscritor.
  3. Na marca de tempo 21.3... o cliente subscritor se desconecta.
  4. Na marca de tempo 31.7... o cliente publicador publica outra mensagem. Note que neste momento o cliente subscritor estava desconectado.
  5. Na marca de tempo 42.5... o cliente subscritor volta a se conectar e imediatamente o brocker publica a mensagem da sessão persistente, recebida quando o mesmo estava desconectado (mensagem marcada em azul).

LastWill e Testamento

O cenário para testar as mensagens lastWillfoi inspirado no exemplo de aplicação descrito anteriormente na seção Will e Testamento.

Passos do experimento
  • O Cliente 1 (IP 172.17.0.2) se conectou ao brocker, iniciou publicação com repetição a cada 5s para o tópico = sensor/status e mensagem = on_line e flag setado para reter mensagem; também informou lastWill como tópico = sensor/status, mensagem = off_line e setou flag reter mensagem:
mosquitto_pub -h 192.168.0.13  -m "on-line" -t "sensor1/status" -r --repeat 20 --repeat-delay 5 --will-topic "sensor1/status" --will-payload "off-line" --will-retain
  • O Cliente 2 (IP 172.17.0.3) se conectou ao brocker e subscreveu o tópico = sensor/status:
mosquitto_sub -h 192.168.0.13  -t "sensor1/status"
  • Após algumas publicações a cada 5s, o Cliente 1 foi desconectado de forma involuntária, parando a máquina em que estava rodando no Docker a partir de um terminal do localhost:
docker stop alpine1

Análise dos pacotes capturados
  • Na marca de tempo 6,5... o cliente 1 se conecta, publica para o tópico = sensor/status a mensagem = on_line e informa lastWill;
  • Na marca de tempo 11,5... o cliente 1 publica novamente para o tópico = sensor/status a mensagem = on_line;
  • Na marca de tempo 16,5... o cliente 1 publica novamente para o tópico = sensor/status a mensagem = on_line;
  • Na marca de tempo 18,2... o cliente 2 se conecta e subscreve o tópico = sensor/status e recebe publicação da mensagem = on_line que estava retida;
  • Nas marcas de tempo 21,5..., 26,5... e 31,5..., o cliente 1 publica novamente para o tópico = sensor/status a mensagem = on_line e o brocker a encamimnha para o cliente 2;
  • Na marca de tempo 35,6... a máquina do cliente 1 foi desconectada involuntariamente e o brocker publica para o cliente 2 a mensagem = off_line.

Terminal do cliente 2:

# mosquitto_sub -h 192.168.0.13  -t "sensor1/status"
on-line
on-line
on-line
on-line
off-line
Observações sobre o lastWill
  1. Caso o cliente 1 fosse desconectado normalmente (CTRL-C) uma mensagem de DISCONNECT seria enviada ao brocker e a mensagem lastWill não seria enviada.
  2. Com uma desconexão involuntária a mensagem lastWill é enviada.
  3. Uma desconexão involuntária pode ser um problema na rede, erro no cliente, ausência de PINGREC etc.
  4. Simulamos uma desconexão involuntária com a parada da máquina do cliente. O brocker percebeu esta parada com o encerramento da conexão TCP, como pode-se ver nas mensagen TCP (FIN-ACK, FIN-ACK) entre as duas publicações em questão.

Referências


Evandro.cantu (discussão) 11h10min de 13 de abril de 2020 (-03)