feat: implement packet sniffer with database integration and API endpoints
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 9s
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 9s
This commit is contained in:
Binary file not shown.
@@ -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")
|
||||
86
backend/src/network_sniffer.py
Normal file
86
backend/src/network_sniffer.py
Normal file
@@ -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
|
||||
29
backend/src/routes_sniffer.py
Normal file
29
backend/src/routes_sniffer.py
Normal file
@@ -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"}
|
||||
Reference in New Issue
Block a user