diff --git a/backend/requirements.txt b/backend/requirements.txt index 4f1fdee..aba08c8 100644 Binary files a/backend/requirements.txt and b/backend/requirements.txt differ diff --git a/backend/src/main.py b/backend/src/main.py index 730379e..bc824de 100644 --- a/backend/src/main.py +++ b/backend/src/main.py @@ -3,6 +3,7 @@ from fastapi.middleware.cors import CORSMiddleware import os import src.Models.netplan as netplan import src.network_api as network_api +import src.routes_sniffer as sniffer_router from asyncio import subprocess from fastapi import HTTPException @@ -60,3 +61,4 @@ def nft_ruleset(): # --------------------- app.include_router(network_api.router, prefix="/network", tags=["network"]) +app.include_router(sniffer_router.router, prefix="/sniff") \ No newline at end of file diff --git a/backend/src/network_sniffer.py b/backend/src/network_sniffer.py new file mode 100644 index 0000000..1f9c2b8 --- /dev/null +++ b/backend/src/network_sniffer.py @@ -0,0 +1,86 @@ +import asyncio +from scapy.all import sniff, Ether, IP +import asyncpg +from typing import List +import threading + +DB_DSN = "postgresql://mitm_user:mitm_password@localhost:5432/mitm_db" + +sniffer_tasks = {} +sniffer_threads = {} + +async def db_insert_packet(pkt_info: dict): + conn = await asyncpg.connect(DB_DSN) + try: + await conn.execute(""" + INSERT INTO packets( + iface, direction, + src_mac, dst_mac, eth_type, + src_ip, dst_ip, protocol, + length, ebpf_verdict, ebpf_chain, raw + ) + VALUES( + $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12 + ); + """, + pkt_info["iface"], + pkt_info["direction"], + pkt_info["src_mac"], + pkt_info["dst_mac"], + pkt_info["eth_type"], + pkt_info["src_ip"], + pkt_info["dst_ip"], + pkt_info["protocol"], + pkt_info["length"], + pkt_info["ebpf_verdict"], + pkt_info["ebpf_chain"], + pkt_info["raw"] + ) + finally: + await conn.close() + + +def handle_packet(pkt, iface): + """ + Scapy callback → run in thread. + """ + pkt_info = { + "iface": iface, + "direction": "unknown", # You can set eth0=ingress, eth1=egress + "src_mac": pkt[Ether].src if Ether in pkt else None, + "dst_mac": pkt[Ether].dst if Ether in pkt else None, + "eth_type": pkt[Ether].type if Ether in pkt else None, + "src_ip": pkt[IP].src if IP in pkt else None, + "dst_ip": pkt[IP].dst if IP in pkt else None, + "protocol": pkt[IP].proto if IP in pkt else None, + "length": len(pkt), + "ebpf_verdict": None, # will integrate later + "ebpf_chain": None, + "raw": bytes(pkt) + } + + # push to asyncio loop + loop = asyncio.get_event_loop() + loop.create_task(db_insert_packet(pkt_info)) + + +def start_sniffer_thread(iface: str): + def sniff_blocking(): + sniff(prn=lambda x: handle_packet(x, iface), iface=iface, store=False) + + thread = threading.Thread(target=sniff_blocking, daemon=True) + thread.start() + return thread + + +async def start_sniffing(interfaces: List[str]): + for iface in interfaces: + if iface in sniffer_threads: + continue + sniffer_threads[iface] = start_sniffer_thread(iface) + + +async def stop_sniffing(): + # Scapy cannot easily stop sniff(), so we simply kill threads + sniffer_threads.clear() + return True diff --git a/backend/src/routes_sniffer.py b/backend/src/routes_sniffer.py new file mode 100644 index 0000000..d153fa6 --- /dev/null +++ b/backend/src/routes_sniffer.py @@ -0,0 +1,29 @@ +from fastapi import APIRouter +from typing import List +from network_sniffer import start_sniffing, stop_sniffing + +router = APIRouter() + +sniffer_running_interfaces: List[str] = [] + + +@router.post("/sniffer/start") +async def start_sniffer(bridge: str, interfaces: List[str]): + """ + Start packet sniffing on bridge member interfaces. + """ + global sniffer_running_interfaces + sniffer_running_interfaces = interfaces + + await start_sniffing(interfaces) + + return { + "status": "ok", + "started_on": interfaces + } + + +@router.post("/sniffer/stop") +async def stop_sniffer_api(): + await stop_sniffing() + return {"status": "stopped"} diff --git a/setup_database.sh b/setup_database.sh index 72f4aff..ccc6d96 100755 --- a/setup_database.sh +++ b/setup_database.sh @@ -17,12 +17,12 @@ sudo sed -i "s/^#listen_addresses =.*/listen_addresses = '*'/" "$PG_CONF" echo "[3] Updating pg_hba.conf for LAN + localhost access…" PG_HBA="/etc/postgresql/$(ls /etc/postgresql)/main/pg_hba.conf" -# Ensure localhost entry exists -if ! grep -q "host *all *all *127.0.0.1/32" "$PG_HBA"; then +# Add localhost entry if missing +if ! grep -Eq "^[ ]*host[ ]+all[ ]+all[ ]+127.0.0.1/32" "$PG_HBA"; then echo "host all all 127.0.0.1/32 md5" | sudo tee -a "$PG_HBA" fi -# Ensure LAN entry exists +# Add LAN entry if missing if ! grep -q "$LAN_SUBNET" "$PG_HBA"; then echo "host all all $LAN_SUBNET md5" | sudo tee -a "$PG_HBA" fi @@ -30,14 +30,16 @@ fi echo "[4] Restarting PostgreSQL…" sudo systemctl restart postgresql -echo "[5] Creating database and user (if not existing)…" +echo "[5] Creating database + user (idempotent)…" sudo -u postgres psql <