Skip to main content
The IDS engine (ids.py) is the runtime heart of the system. It captures raw network packets via Scapy, runs each packet through six heuristic detectors in sequence, orchestrates ML classification, persists findings to SQLite and CSV, and pushes thread-safe signals to the GUI.

ComunicadorIDS — Qt signal bus

ComunicadorIDS is a QObject subclass that acts as the message bus between the engine (which runs in the Scapy sniffer thread) and the PyQt5 UI (which runs in the main thread). Using pyqtSignal makes all cross-thread updates safe without locks.
ids.py
class ComunicadorIDS(QObject):
    # Payload: [timestamp, ip_src, ip_dst, puerto, protocolo, flag, tipo_final]
    nuevo_evento = pyqtSignal(list)

    # Payload: [ip, accion, duracion, tipo_ataque, severidad]
    nuevo_bloqueo = pyqtSignal(list)

    # Payload: single-line packet summary string from Scapy
    nuevo_trafico = pyqtSignal(str)

comunicador = ComunicadorIDS()   # Global singleton — imported by interfasc.py
SignalTypeConsumers
nuevo_eventolist[7]Events table in the GUI
nuevo_bloqueolist[5]IPS panel in the GUI
nuevo_traficostr”Tráfico en Vivo” text panel

Global state variables

VariableTypePurpose
sniffing_activoboolGuards against double-starting the sniffer
ips_activoboolEnables or disables the active-response (IPS) path
snifferAsyncSniffer | NoneReference kept so detener_monitoreo() can call .stop()
These are module-level globals declared in ids.py. The GUI toggles ids.ips_activo directly via the IPS checkbox.

IP whitelist

Two collections define trusted sources that are skipped by the exploit and SQL injection detectors.
ids.py
IPS_CONFIABLES = {
    "192.168.0.15",
    "192.168.0.17",
    "104.22.1.235",  # Cloudflare
    "2.22.20.72",    # Akamai
    # ...
}

RANGOS_CONFIABLES = [
    ipaddress.ip_network('10.0.0.0/8'),       # RFC 1918 class A
    ipaddress.ip_network('172.16.0.0/12'),    # RFC 1918 class B
    ipaddress.ip_network('104.16.0.0/12'),    # Cloudflare
    ipaddress.ip_network('140.82.0.0/16'),    # GitHub
    # ...
]
To add a trusted host, append its IP string to IPS_CONFIABLES or add a CIDR block to RANGOS_CONFIABLES. The helper ip_en_rangos(ip) checks membership using Python’s ipaddress module.
The detectors for SYN Flood, DDoS, Port Scan, and UDP Flood do not consult the whitelist — they fire on volume thresholds regardless of source. Only the exploit and SQL injection detectors perform a whitelist check first.

SQLite schema

The engine opens intrusiones.db at startup with check_same_thread=False so the sniffer thread can write directly.
CREATE TABLE IF NOT EXISTS ataques (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    timestamp   TEXT,
    tipo_ataque TEXT,
    ip_src      TEXT,
    protocolo   TEXT,
    puerto      INTEGER
);

CREATE TABLE IF NOT EXISTS bloqueos (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    timestamp   TEXT,
    ip_src      TEXT,
    tipo_ataque TEXT,
    duracion    INTEGER,
    estado      TEXT   -- 'ACTIVO' | 'SIMULADO'
);
Both tables are created with IF NOT EXISTS, so restarting the application is safe and never truncates historical data.

Functions

iniciar_monitoreo

ids.py
def iniciar_monitoreo(iface=None):
Starts an AsyncSniffer on the given interface. The sniffer runs in its own internal thread managed by Scapy, so the call returns immediately without blocking the Qt event loop.
iface
str | None
default:"None"
Network interface name (e.g. "Ethernet", "Wi-Fi"). When None, Scapy uses the system default interface.
Internal behavior:
  • Sets sniffing_activo = True before starting (guards re-entry).
  • Creates AsyncSniffer(iface=iface, prn=procesar_paquete, store=False)store=False prevents unbounded memory growth.
  • On failure, resets sniffing_activo = False and prints an error.

detener_monitoreo

ids.py
def detener_monitoreo():
Stops the sniffer cleanly by calling sniffer.stop() and then sets the reference to None so the object can be garbage collected. Sets sniffing_activo = False.

procesar_paquete

ids.py
def procesar_paquete(packet):
    try:
        mostrar_paquete(packet)           # emit nuevo_trafico signal

        if packet.haslayer(scapy.IP):
            detectar_syn_flood(packet)
            detectar_ddos(packet)
            detectar_escaneo_puertos(packet)
            detectar_exploit(packet)
            detectar_sql_injection(packet)
            detectar_udp_flood(packet)
    except Exception as e:
        print(f"[X] Excepción en procesar_paquete: {e}")
This is the prn callback passed to AsyncSniffer. It runs in Scapy’s sniffer thread for every captured packet. The outer try/except ensures that an exception in one detector does not crash the sniffer process. Only packets with an IP layer are passed to the detectors. Raw Ethernet frames (ARP, etc.) are still forwarded to mostrar_paquete for the live traffic panel.

guardar_ataque

ids.py
def guardar_ataque(
    ip_src,
    tipo_ataque,
    protocolo,
    puerto,
    ip_dst="DESCONOCIDA",
    flag="N/A",
    usar_ml=True
):
Orchestrates every action taken when a detector fires. Steps in order:
  1. Self-exclusion — returns early if ip_src == MI_IP (the local machine’s own IP, auto-detected at startup).
  2. Throttle — skips if the same IP triggered an alert within the last TIEMPO_ENTRE_ALERTAS (2) seconds.
  3. ML classification — calls clasificar_ataque_ml() when ips_activo and usar_ml. If ML confidence ≥ 70%, uses the ML label; otherwise falls back to the heuristic label. SQL injection detectors pass usar_ml=False because the payload was already analyzed.
  4. SQLite writeINSERT INTO ataques with parameterized placeholders to prevent second-order SQL injection.
  5. CSV write — calls guardar_evento_en_dataset() to append to the feedback dataset.
  6. Signal emitcomunicador.nuevo_evento.emit([timestamp, ip_src, ip_dst, puerto, protocolo, flag, tipo_final]).
  7. Telegram alert — fires _enviar_alerta_async() in a daemon thread.
  8. IPS blocking — if ips_activo and the es_critico flag is set, calls respuesta_activa.bloquear_ip() and emits nuevo_bloqueo.
ip_src
str
Source IP address of the attacking host.
tipo_ataque
str
Heuristic label assigned by the detector (e.g. "SYN Flood", "Posible Exploit").
protocolo
str
Transport protocol string: "TCP" or "UDP".
puerto
int
Destination port number.
ip_dst
str
default:"DESCONOCIDA"
Destination IP address.
flag
str
default:"N/A"
TCP flag string from Scapy (e.g. "S" for SYN, "SA" for SYN-ACK).
usar_ml
bool
default:"True"
Set to False to skip ML classification. Used by the SQL injection detector since the payload analysis already confirms the attack type.

preprocesar_datos

ids.py
def preprocesar_datos(ip_src, ip_dst, puerto, protocolo, flag):
Builds the numeric feature vector used for real-time inference. This function mirrors the training-time preprocessing in CEREBRO.py but uses hash(ip) % (10**8) instead of socket.inet_aton because it operates without loading the IP encoders at runtime.
datos = {
    "ip_src":         hash(ip_src),
    "ip_dst":         hash(ip_dst),
    "puerto":         puerto,
    "protocolo_tcp":  1 if protocolo == 'TCP' else 0,
    "flag":           flag_encoded,
    "protocolo_num":  protocolo_encoded
}
The resulting list is ordered to match features_seleccionadas.pkl. Unknown protocols or flags fall back to -1.
preprocesar_datos in ids.py uses hash() for IP encoding, while CEREBRO.py uses socket.inet_aton + struct.unpack. These produce different numeric representations. The inference path in clasificar_ataque_ml() uses the hash() approach consistently.

Build docs developers (and LLMs) love