MQTT e Mosquitto: Analise do protocolo com Wireshark
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 20.04 (IP 192.168.1.21);
- 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 broker (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 broker 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 broker (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 broker 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 broker (192.168.0.13) e o cliente subscritor (172.17.0.2):
Publish Message
- Esta segunda publicação refere-se a publicação do broker 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.
Experimento 2: Qualidade de Serviço (QoS)
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).
Experimento 3: Reter Mensagens Publicadas
Caso o cliente publique com o retainFlag = true o broker 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 broker publicou a mensagem retida ao cliente.
Experimento 4: 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
- Na marca de tempo 0.0... um cliente subscritor se conecta e subscreve um tópico (QoS=1) com sessão persistente.
- Na marca de tempo 12.8... o cliente publicador se conecta e publica uma mensagem e se desconecta e o broker também publica a mensagem ao subscritor.
- Na marca de tempo 21.3... o cliente subscritor se desconecta.
- Na marca de tempo 31.7... o cliente publicador publica outra mensagem. Note que neste momento o cliente subscritor estava desconectado.
- Na marca de tempo 42.5... o cliente subscritor volta a se conectar e imediatamente o broker publica a mensagem da sessão persistente, recebida quando o mesmo estava desconectado (mensagem marcada em azul).
Experimento 5: LastWill e Testamento
O cenário para testar as mensagens lastWill foi 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 broker, 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 broker 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 broker a encamimnha para o cliente 2;
- Na marca de tempo 35,6... a máquina do cliente 1 foi desconectada involuntariamente e o broker 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
- Caso o cliente 1 fosse desconectado normalmente (CTRL-C) uma mensagem de DISCONNECT seria enviada ao broker e a mensagem lastWill não seria enviada.
- Com uma desconexão involuntária a mensagem lastWill é enviada.
- Uma desconexão involuntária pode ser um problema na rede, erro no cliente, ausência de PINGREC etc.
- Simulamos uma desconexão involuntária com a parada da máquina do cliente. O broker 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)