Add logging and multithreading over AX.25
This commit is contained in:
parent
42cd5f37bd
commit
bef2415ee7
@ -1,16 +1,22 @@
|
|||||||
import socket
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import logging.handlers
|
||||||
|
import socket
|
||||||
import ax25
|
import ax25
|
||||||
|
import ax25.ports
|
||||||
import ax25.socket
|
import ax25.socket
|
||||||
|
|
||||||
|
# Settings
|
||||||
BASE_CALLSIGN = "KI5QKX-10"
|
BASE_CALLSIGN = "KI5QKX-10"
|
||||||
NETWORK_NAME = "HAMNET-HOUSTON"
|
NETWORK_NAME = "HAMNET-HOUSTON"
|
||||||
|
|
||||||
BEACON_INTERVAL = 10 # seconds
|
BEACON_INTERVAL = 10 # seconds
|
||||||
BEACON_CALLSIGN = "CRAPR-0"
|
BEACON_CALLSIGN = "QST-0"
|
||||||
|
|
||||||
# Fake IP pool
|
# IP pool for assigning to clients
|
||||||
IP_POOL = [
|
IP_POOL = [
|
||||||
"44.127.254.12",
|
"44.127.254.12",
|
||||||
"44.127.254.13",
|
"44.127.254.13",
|
||||||
@ -24,72 +30,134 @@ DNS_SERVER = "44.127.254.1"
|
|||||||
|
|
||||||
active_leases = {}
|
active_leases = {}
|
||||||
|
|
||||||
|
# Setup logging
|
||||||
|
os.makedirs("logs", exist_ok=True)
|
||||||
|
logger = logging.getLogger("craprniac")
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
log_handler = logging.handlers.TimedRotatingFileHandler(
|
||||||
|
"logs/craprniac.log",
|
||||||
|
when="midnight",
|
||||||
|
interval=1,
|
||||||
|
backupCount=7
|
||||||
|
)
|
||||||
|
log_handler.setFormatter(logging.Formatter(
|
||||||
|
"%(asctime)s [%(levelname)s] %(message)s"
|
||||||
|
))
|
||||||
|
logger.addHandler(log_handler)
|
||||||
|
|
||||||
|
console_handler = logging.StreamHandler()
|
||||||
|
console_handler.setFormatter(logging.Formatter(
|
||||||
|
"%(asctime)s [%(levelname)s] %(message)s"
|
||||||
|
))
|
||||||
|
logger.addHandler(console_handler)
|
||||||
|
|
||||||
|
# Packet builders
|
||||||
def build_beacon():
|
def build_beacon():
|
||||||
return f"0.1|CRAP_BEACON|{BASE_CALLSIGN}|{NETWORK_NAME}"
|
return f"0.1|CRAP_BEACON|{BASE_CALLSIGN}|{NETWORK_NAME}"
|
||||||
|
|
||||||
def build_accept(client_callsign, ip_address):
|
def build_accept(client_callsign, ip_address):
|
||||||
return f"0.1|CRAP_ACCEPT|{client_callsign}|{NETWORK_NAME}|{ip_address}/{NETMASK}|{GATEWAY}|{DNS_SERVER}|{LEASE_TIME}"
|
return f"0.1|CRAP_ACCEPT|{client_callsign}|{NETWORK_NAME}|{ip_address}/{NETMASK}|{GATEWAY}|{DNS_SERVER}|{LEASE_TIME}"
|
||||||
|
|
||||||
def handle_request(data, addr, sock):
|
# Beaconing loop
|
||||||
|
def beacon_loop(beacon_sock):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# beacon_sock.connect(BEACON_CALLSIGN)
|
||||||
|
beacon_sock.sendto(build_beacon().encode('utf-8'), "QST-0")
|
||||||
|
logger.info("Sent beacon")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Beacon send failed: {e}")
|
||||||
|
time.sleep(BEACON_INTERVAL)
|
||||||
|
|
||||||
|
# Handle a single client connection
|
||||||
|
def handle_client(conn, addr):
|
||||||
|
try:
|
||||||
|
logger.info(f"[{addr}] Connection established.")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
data = conn.recv(1024)
|
||||||
|
if not data:
|
||||||
|
logger.info(f"[{addr}] Connection closed.")
|
||||||
|
break
|
||||||
|
|
||||||
|
decoded = data.decode('utf-8')
|
||||||
|
handle_request(decoded, addr, conn)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[{addr}] Error: {e}")
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# Handle incoming requests
|
||||||
|
def handle_request(data, src_callsign, conn):
|
||||||
parts = data.split('|')
|
parts = data.split('|')
|
||||||
|
|
||||||
|
if len(parts) < 2:
|
||||||
|
logger.warning(f"[{src_callsign}] Received malformed message: {data}")
|
||||||
|
return
|
||||||
|
|
||||||
if parts[1] == "CRAP_BEACON":
|
if parts[1] == "CRAP_BEACON":
|
||||||
return
|
return
|
||||||
|
|
||||||
if len(parts) < 4:
|
|
||||||
print(f"Received malformed message from {addr}: {data}")
|
|
||||||
return
|
|
||||||
if parts[1] != "CRAP_REQUEST":
|
if parts[1] != "CRAP_REQUEST":
|
||||||
print(f"Ignoring non-request message: {data}")
|
logger.warning(f"[{src_callsign}] Ignoring unknown message: {data}")
|
||||||
return
|
return
|
||||||
|
|
||||||
client_callsign = parts[2]
|
client_callsign = parts[2]
|
||||||
network_requested = parts[3]
|
network_requested = parts[3]
|
||||||
|
|
||||||
if network_requested != NETWORK_NAME:
|
if network_requested != NETWORK_NAME:
|
||||||
print(f"Client {client_callsign} requested wrong network ({network_requested}). Ignored.")
|
logger.warning(f"[{client_callsign}] Requested wrong network ({network_requested}). Ignored.")
|
||||||
return
|
return
|
||||||
|
|
||||||
print(f"Received join request from {client_callsign} ({addr}).")
|
logger.info(f"[{client_callsign}] Join request received.")
|
||||||
|
|
||||||
# Assign IP
|
# Assign IP
|
||||||
if client_callsign in active_leases:
|
if client_callsign in active_leases:
|
||||||
assigned_ip = active_leases[client_callsign]
|
assigned_ip = active_leases[client_callsign]
|
||||||
print(f"Client {client_callsign} already has IP {assigned_ip}.")
|
logger.info(f"[{client_callsign}] Already assigned {assigned_ip}.")
|
||||||
elif IP_POOL:
|
elif IP_POOL:
|
||||||
assigned_ip = IP_POOL.pop(0)
|
assigned_ip = IP_POOL.pop(0)
|
||||||
active_leases[client_callsign] = assigned_ip
|
active_leases[client_callsign] = assigned_ip
|
||||||
print(f"Assigned IP {assigned_ip} to {client_callsign}.")
|
logger.info(f"[{client_callsign}] Assigned new IP {assigned_ip}.")
|
||||||
else:
|
else:
|
||||||
print(f"No available IPs for {client_callsign}!")
|
logger.error(f"[{client_callsign}] No available IPs to assign!")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Send ACCEPT
|
# Send CRAP_ACCEPT
|
||||||
accept_message = build_accept(client_callsign, assigned_ip)
|
try:
|
||||||
sock.sendto(accept_message.encode('utf-8'), addr)
|
conn.send(build_accept(client_callsign, assigned_ip).encode('utf-8'))
|
||||||
print(f"Sent CRAP_ACCEPT to {client_callsign}.")
|
logger.info(f"[{client_callsign}] Sent CRAP_ACCEPT.")
|
||||||
|
except Exception as e:
|
||||||
def beacon_loop(sock):
|
logger.error(f"[{client_callsign}] Failed to send CRAP_ACCEPT: {e}")
|
||||||
while True:
|
|
||||||
beacon = build_beacon().encode('utf-8')
|
|
||||||
sock.sendto(beacon, (BEACON_CALLSIGN,))
|
|
||||||
print(f"Sent beacon: {beacon.decode('utf-8')}")
|
|
||||||
time.sleep(BEACON_INTERVAL)
|
|
||||||
|
|
||||||
|
# Main server function
|
||||||
def main():
|
def main():
|
||||||
# sock = socket.socket(socket.AF_AX25, socket.SOCK_DGRAM) # Old python networking way
|
# Beaconing socket
|
||||||
sock = ax25.socket.Socket()
|
beacon_sock = ax25.socket.Socket(socket.SOCK_DGRAM)
|
||||||
sock.bind((BASE_CALLSIGN,)) # Bind to our station callsign
|
beacon_sock.bind(BASE_CALLSIGN)
|
||||||
|
|
||||||
print(f"Base Station {BASE_CALLSIGN} starting up on {NETWORK_NAME}...")
|
# Server listening socket
|
||||||
|
server_sock = ax25.socket.Socket()
|
||||||
|
server_sock.bind(BASE_CALLSIGN)
|
||||||
|
server_sock.listen()
|
||||||
|
|
||||||
# Start beaconing in a separate thread
|
logger.info(f"Base Station {BASE_CALLSIGN} starting up on {NETWORK_NAME}...")
|
||||||
threading.Thread(target=beacon_loop, args=(sock,), daemon=True).start()
|
|
||||||
|
threading.Thread(target=beacon_loop, args=(beacon_sock,), daemon=True).start()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
data, addr = sock.recvfrom(1024)
|
try:
|
||||||
decoded = data.decode('utf-8')
|
conn, addr = server_sock.accept()
|
||||||
handle_request(decoded, addr, sock)
|
logger.info(f"Accepted connection from {addr}")
|
||||||
|
|
||||||
|
client_thread = threading.Thread(target=handle_client, args=(conn, addr))
|
||||||
|
client_thread.daemon = True
|
||||||
|
client_thread.start()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error accepting connection: {e}")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user