TL; DR: Como canalizar corretamente através da UART a saída de umtcpdump
remoto para um local wireshark
?
Eu tento capturar pacotes que fluem através de um dispositivo incorporado para o qual não tenho a capacidade de instalar nada. Felizmente, existe um getty aberto na interface serial e o tcpdump instalado. Infelizmente, sem SSH, sem dumpcap, sem tshark.
Tubo direto
Eu tentei primeiro configurar o tty e passar os dados para o wireshark através de pipes.
stty -F /dev/ttyUSB0 raw
stty -F /dev/ttyUSB0 -echo -echoe -echok
cat /dev/ttyUSB0 | wireshark -k -i -
# On another terminal:
echo "tcpdump -U -s0 -i eth0 -w - 2>/dev/null" > /dev/ttyUSB0
O Wireshark reclama que a entrada não é válida no formato da libpcap, certamente porque o comando é ecoado de volta e eu não consegui me livrar disso.
Usando PySerial bruto
Então, decidi criar um script python para controlar como a tubulação funcionaria:
import serial
import sys
import subprocess
import fcntl
def main(args):
with serial.Serial('/dev/ttyUSB0', 115200, timeout=0) as ser:
length = ser.write(b"tcpdump -U -s0 -i eth0 -w - 2> /dev/null\n") + 1
# Discard the echoed command line
while length > 0:
discard = ser.read(length)
length -= len(discard)
# Spawn wireshark
wireshark = subprocess.Popen(
["wireshark", "-k", "-i", "-"], stdin=subprocess.PIPE
)
# Pipe data from serial to wireshark's input
while True:
data = ser.read(256)
wireshark.stdin.write(data)
try:
wireshark.stdin.flush()
except BrokenPipeError as e:
break
if len(data) > 0: print(data)
# Send "Ctrl+C" to tcpdump
ser.write(b"\x03")
wireshark.wait()
return 0
if __name__ == '__main__':
import sys
sys.exit(main(sys.argv))
Deixando de lado alguns problemas com a forma como o script deve terminar corretamente, isso não funcionou tão bem quanto eu imaginava. O Wireshark está feliz por algum tempo, mas logo a entrada fica corrompida e a gravação pára. Acho que isso acontece porque o tty no host ainda converte alguns caracteres especiais, provavelmente o avanço de linha ou o retorno de carro.
Ficando estúpido: hexdump em PySerial
Então eu sei que isso é ruim, mas como eu não tinha outras ideias, foi isso que eu criei:
import serial
import sys
import subprocess
import binascii
def main(args):
with serial.Serial('/dev/ttyUSB0', 115200, timeout=5) as ser:
# Spawn tcpdump on the host and convert the raw output to stupid hex format
# We need hexdump -C because that's the only format that doesn't mess up with the endianess
length = ser.write(b"tcpdump -U -s256 -i eth0 -w - 2> /dev/null | hexdump -C\n")
# Discard command line that is echoed
discard = ser.readline()
# Spawn wireshark
wireshark = subprocess.Popen(
["wireshark", "-k", "-i", "-"], stdin=subprocess.PIPE
)
while True:
# Process each line separately
data = ser.readline().decode('ascii')
elements = data.split()
# Remove the address and ascii convertion of hexdump and spaces
hexa = "".join(elements[1:17])
# Convert back hex to binary
real_data = binascii.unhexlify(hexa)
# Feed to the shark
wireshark.stdin.write(real_data)
try:
wireshark.stdin.flush()
except BrokenPipeError as e:
break
# Stop tcpdump
ser.write(b"\x03")
wireshark.wait()
return 0
if __name__ == '__main__':
import sys
sys.exit(main(sys.argv))
Infelizmente, apesar de funcionar um pouco mais do que a versão anterior, quando os quadros são um pouco grandes demais, o wireshark solta um problema dizendo que o quadro é muito grande, com um comprimento que é realmente ridículo (como -1562980309832) e novamente a gravação pára.
Por favor, ajude! :)
Você pode notar que tentei jogar com a opção -s
do tcpdump, mas não funcionou, mesmo com valores baixos.
Eu também tentei piping de picocom, sem sucesso.
Então, se você tem alguma idéia, qualquer software de tunelamento UART que funcione, qualquer observação sobre meu (incompetente) uso de stty, ou qualquer melhoria em meus scripts python, eu ficaria muito feliz!
O Wireshark é 2.2.5, o tcpdump é 4.5.0 com a libpcap 1.5.0.