You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

132 lines
4.1 KiB

#!/usr/bin/env python3
"""
utun control socket monitor client
Usage: python3 monitor.py [host] [port]
Default: localhost:5555
"""
import socket
import struct
import sys
import json
import time
# Command types from control_socket.h
CONTROL_CMD_GET_STATS = 1
CONTROL_CMD_RESET_STATS = 2
CONTROL_CMD_GET_STATUS = 3
# Response types
CONTROL_RESP_STATS = 1
CONTROL_RESP_STATUS = 2
CONTROL_RESP_ERROR = 3
# Error codes
CONTROL_ERR_NONE = 0
CONTROL_ERR_INVALID_CMD = 1
CONTROL_ERR_INTERNAL = 2
def send_command(host='192.168.29.117', port=5555, command=CONTROL_CMD_GET_STATS, sequence=1):
"""Send a control command and receive response"""
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(2.0)
# Build request packet (4 bytes: command + sequence)
# struct control_request_packet_t { uint16_t command; uint16_t sequence; }
request = struct.pack('<HH', command, sequence)
try:
sock.sendto(request, (host, port))
data, addr = sock.recvfrom(4096)
except socket.timeout:
print("Timeout: No response from server")
return None
finally:
sock.close()
return data
def parse_stats_response(data):
"""Parse statistics response packet"""
# Unpack header (common for all responses)
# struct control_response_header { uint16_t response_type; uint16_t error_code; uint16_t sequence; }
if len(data) < 6:
print("Invalid response length")
return None
resp_type, err_code, seq = struct.unpack('<HHH', data[:6])
if err_code != CONTROL_ERR_NONE:
print(f"Error response: code {err_code}")
return None
if resp_type == CONTROL_RESP_STATS:
# Parse full stats packet (need to know exact structure)
# For now, just print raw data size
print(f"Received stats packet: {len(data)} bytes")
# TODO: Implement full parsing based on control_socket.h structures
# This requires matching the packed C structures
return data
elif resp_type == CONTROL_RESP_STATUS:
# Parse status packet
print("Status response")
return data
elif resp_type == CONTROL_RESP_ERROR:
print("Error response")
return None
else:
print(f"Unknown response type: {resp_type}")
return None
def print_hex(data):
"""Print hex dump of data"""
for i in range(0, len(data), 16):
chunk = data[i:i+16]
hex_str = ' '.join(f'{b:02x}' for b in chunk)
ascii_str = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
print(f'{i:04x}: {hex_str:<48} {ascii_str}')
def main():
host = '127.0.0.1'
port = 5555
if len(sys.argv) > 1:
host = sys.argv[1]
if len(sys.argv) > 2:
port = int(sys.argv[2])
print(f"Connecting to {host}:{port}")
# Send GET_STATS command
data = send_command(host, port, CONTROL_CMD_GET_STATS, 1)
if data is None:
return
print(f"Received {len(data)} bytes")
print_hex(data)
# Attempt to parse basic info
if len(data) >= 6:
resp_type, err_code, seq = struct.unpack('<HHH', data[:6])
print(f"Response type: {resp_type}, Error: {err_code}, Sequence: {seq}")
if resp_type == CONTROL_RESP_STATS:
# Try to parse timestamp (uint64_t after header)
if len(data) >= 14:
timestamp = struct.unpack('<Q', data[6:14])[0]
print(f"Timestamp: {timestamp} μs ({timestamp/1000000:.3f} s)")
# Print approximate stats structure size
# The full stats packet should be 1024+ bytes
print(f"Expected stats size ~1024 bytes, got {len(data)}")
# Send GET_STATUS command
print("\n--- Requesting status ---")
data = send_command(host, port, CONTROL_CMD_GET_STATUS, 2)
if data:
print(f"Status response {len(data)} bytes")
if len(data) >= 6:
resp_type, err_code, seq = struct.unpack('<HHH', data[:6])
print(f"Response type: {resp_type}, Error: {err_code}, Sequence: {seq}")
if __name__ == '__main__':
main()