CipherSniff / app.py
Cipher29's picture
Upload app.py
e36b96b verified
import pyshark
import threading
import time
import numpy as np
import pandas as pd
from queue import Queue, Empty
import netifaces as net
import os
import joblib
from threading import Lock
import streamlit as st
current_dir = os.path.dirname(os.path.abspath(__file__))
MODEL_PATH = os.path.join(current_dir, 'Anomaly_Model.joblib')
flow_dict_lock = threading.Lock()
FEATURE_NAMES = [
' Destination Port',
' Flow Duration',
' Total Fwd Packets',
' Total Backward Packets',
'Total Length of Fwd Packets',
' Total Length of Bwd Packets',
' Fwd Packet Length Max',
' Fwd Packet Length Min',
' Fwd Packet Length Mean',
' Fwd Packet Length Std',
'Bwd Packet Length Max',
' Bwd Packet Length Min',
' Bwd Packet Length Mean',
' Bwd Packet Length Std',
'Flow Bytes/s',
' Flow Packets/s',
' Flow IAT Mean',
' Flow IAT Std',
' Flow IAT Max',
' Flow IAT Min',
'Fwd IAT Total',
' Fwd IAT Mean',
' Fwd IAT Std',
' Fwd IAT Max',
' Fwd IAT Min',
'Bwd IAT Total',
' Bwd IAT Mean',
' Bwd IAT Std',
' Bwd IAT Max',
' Bwd IAT Min',
'Fwd PSH Flags',
' Bwd PSH Flags',
' Fwd URG Flags',
' Bwd URG Flags',
' Fwd Header Length',
' Bwd Header Length',
'Fwd Packets/s',
' Bwd Packets/s',
' Min Packet Length',
' Max Packet Length',
' Packet Length Mean',
' Packet Length Std',
' Packet Length Variance',
'FIN Flag Count',
' SYN Flag Count',
' RST Flag Count',
' PSH Flag Count',
' ACK Flag Count',
' URG Flag Count',
' CWE Flag Count',
' ECE Flag Count',
' Down/Up Ratio',
' Average Packet Size',
' Avg Fwd Segment Size',
' Avg Bwd Segment Size',
' Fwd Header Length.1',
'Fwd Avg Bytes/Bulk',
' Fwd Avg Packets/Bulk',
' Fwd Avg Bulk Rate',
' Bwd Avg Bytes/Bulk',
' Bwd Avg Packets/Bulk',
'Bwd Avg Bulk Rate',
'Subflow Fwd Packets',
' Subflow Fwd Bytes',
' Subflow Bwd Packets',
' Subflow Bwd Bytes',
'Init_Win_bytes_forward',
' Init_Win_bytes_backward',
' act_data_pkt_fwd',
' min_seg_size_forward',
'Active Mean',
' Active Std',
' Active Max',
' Active Min',
'Idle Mean',
' Idle Std',
' Idle Max',
' Idle Min'
]
# Add verification after loading the model
def verify_features():
"""Verify that we have all required features and they match exactly"""
print(f"\nFeature Verification:")
print(f"Total features in training data: {len(FEATURE_NAMES)}")
print(f"Features in loaded model: {len(pipeline['selected_features'])}")
# Check for missing features
missing_features = set(FEATURE_NAMES) - set(pipeline['selected_features'])
if missing_features:
print("\nWARNING: Missing features in model:")
for feature in missing_features:
print(f"- {feature}")
# Check for extra features
extra_features = set(pipeline['selected_features']) - set(FEATURE_NAMES)
if extra_features:
print("\nWARNING: Extra features in model:")
for feature in extra_features:
print(f"- {feature}")
# Print first few features for verification
print("\nFirst 5 features:")
for i, feature in enumerate(FEATURE_NAMES[:5]):
print(f"{i+1}. '{feature}'")
# Add these constants at the top
PACKET_TIMEOUT = 60 # Flow expiration timeout in seconds
QUEUE_SIZE = 10000 # Maximum packet queue size
VERBOSE = False # Enable/disable detailed logging
# Add a packet counter class
class PacketStats:
def __init__(self):
self.total_packets = 0
self.ddos_flows = 0
self.benign_flows = 0
self.start_time = time.time()
self.lock = threading.Lock()
def update_stats(self, is_ddos):
with self.lock:
self.total_packets += 1
if is_ddos:
self.ddos_flows += 1
else:
self.benign_flows += 1
def print_stats(self):
with self.lock:
elapsed_time = time.time() - self.start_time
print(f"\nMonitoring Statistics:")
print(f"Running time: {elapsed_time:.2f} seconds")
print(f"Total packets processed: {self.total_packets}")
print(f"DDoS flows detected: {self.ddos_flows}")
print(f"Benign flows detected: {self.benign_flows}")
try:
print("Loading model...")
# Use the constructed path to load the model
model_data = joblib.load(MODEL_PATH)
pipeline = {
'model': model_data['model'],
'scaler': model_data['model'].named_steps['scaler'],
'selector': model_data['model'].named_steps['feature_selection'],
'variance_selector': model_data['model'].named_steps['variance_threshold'],
'selected_features': model_data['feature_names']
}
print("Model loaded successfully")
except Exception as e:
print(f"Error loading model: {e}")
raise
class Flow:
def is_expired(self, timeout=60):
return (time.time() - self.flow_end_time) > timeout
def __init__(self, src_ip, src_port, dst_ip, dst_port, protocol):
# Flow identifiers
self.src_ip = src_ip
self.src_port = src_port
self.dst_ip = dst_ip
self.dst_port = dst_port
self.protocol = protocol
# Packet tracking
self.total_fwd_packets = 0
self.total_bwd_packets = 0
self.total_length_fwd_packets = 0
self.total_length_bwd_packets = 0
# Packet lengths
self.fwd_packet_lengths = []
self.bwd_packet_lengths = []
self.packet_lengths = [] # All packet lengths
# Inter-arrival times
self.fwd_iat = []
self.bwd_iat = []
self.flow_iat = []
self.last_fwd_packet_time = None
self.last_bwd_packet_time = None
self.last_packet_time = None
# Header lengths
self.fwd_header_length = 0
self.bwd_header_length = 0
# Flags
self.fin_flag_count = 0
self.syn_flag_count = 0
self.rst_flag_count = 0
self.psh_flag_count = 0
self.ack_flag_count = 0
self.urg_flag_count = 0
self.cwe_flag_count = 0
self.ece_flag_count = 0
# Window sizes
self.init_win_bytes_forward = None
self.init_win_bytes_backward = None
self.act_data_pkt_fwd = 0
self.min_seg_size_forward = None
# Active and Idle times
self.flow_start_time = time.time()
self.flow_end_time = self.flow_start_time
self.active_times = []
self.idle_times = []
self.last_active_time = None
# Other features
self.flow_packet_times = []
def add_packet(self, packet, direction):
try:
if not hasattr(packet, 'sniff_timestamp'):
if VERBOSE:
print(f"Packet missing sniff_timestamp: {packet}")
return
current_time = float(packet.sniff_timestamp)
self.flow_end_time = current_time
# Track packet times for flow IAT
self.flow_packet_times.append(current_time)
if len(self.flow_packet_times) > 1:
iat = self.flow_packet_times[-1] - self.flow_packet_times[-2]
self.flow_iat.append(iat)
# Packet length - add error checking
try:
packet_length = int(packet.length)
except (AttributeError, ValueError) as e:
if VERBOSE:
print(f"Error getting packet length: {e}, packet: {packet}")
return
self.packet_lengths.append(packet_length)
# Detailed error handling for IP addresses
try:
if hasattr(packet, 'ip'):
src_ip = packet.ip.src
dst_ip = packet.ip.dst
elif hasattr(packet, 'ipv6'):
src_ip = packet.ipv6.src
dst_ip = packet.ipv6.dst
else:
if VERBOSE:
print(f"Packet has no IP layer: {packet}")
return
except AttributeError as e:
if VERBOSE:
print(f"Error accessing IP addresses: {e}, packet: {packet}")
return
current_time = float(packet.sniff_timestamp)
self.flow_end_time = current_time
# Track packet times for flow IAT
self.flow_packet_times.append(current_time)
if len(self.flow_packet_times) > 1:
iat = self.flow_packet_times[-1] - self.flow_packet_times[-2]
self.flow_iat.append(iat)
# Packet length
packet_length = int(packet.length)
self.packet_lengths.append(packet_length)
# Header length calculation
header_length = 14 # Ethernet header
if hasattr(packet, 'ip'):
src_ip = packet.ip.src
dst_ip = packet.ip.dst
elif hasattr(packet, 'ipv6'):
src_ip = packet.ipv6.src
dst_ip = packet.ipv6.dst
else:
return
if hasattr(packet, 'tcp'):
header_length += int(packet.tcp.hdr_len or 0)
if hasattr(packet.tcp, 'flags'):
flags = int(packet.tcp.flags_hex, 16)
self.fin_flag_count += bool(flags & 0x01)
self.syn_flag_count += bool(flags & 0x02)
self.rst_flag_count += bool(flags & 0x04)
self.psh_flag_count += bool(flags & 0x08)
self.ack_flag_count += bool(flags & 0x10)
self.urg_flag_count += bool(flags & 0x20)
self.ece_flag_count += bool(flags & 0x40)
self.cwe_flag_count += bool(flags & 0x80)
if direction == 'forward' and self.init_win_bytes_forward is None:
self.init_win_bytes_forward = int(packet.tcp.window_size or 0)
elif direction == 'backward' and self.init_win_bytes_backward is None:
self.init_win_bytes_backward = int(packet.tcp.window_size or 0)
if self.min_seg_size_forward is None:
self.min_seg_size_forward = int(packet.tcp.hdr_len or 0)
elif hasattr(packet, 'udp'):
header_length += 8
if direction == 'forward':
self.total_fwd_packets += 1
self.total_length_fwd_packets += packet_length
self.fwd_packet_lengths.append(packet_length)
self.fwd_header_length += header_length
self.act_data_pkt_fwd += 1
if self.last_fwd_packet_time is not None:
iat = current_time - self.last_fwd_packet_time
self.fwd_iat.append(iat)
self.last_fwd_packet_time = current_time
else:
self.total_bwd_packets += 1
self.total_length_bwd_packets += packet_length
self.bwd_packet_lengths.append(packet_length)
self.bwd_header_length += header_length
if self.last_bwd_packet_time is not None:
iat = current_time - self.last_bwd_packet_time
self.bwd_iat.append(iat)
self.last_bwd_packet_time = current_time
except Exception as e:
if VERBOSE:
print(f"Error processing packet: {str(e)}")
print(f"Packet details: {packet}")
import traceback
print(traceback.format_exc())
def compute_features(self):
# Compute statistical features for packet lengths
fwd_pl_array = np.array(self.fwd_packet_lengths)
bwd_pl_array = np.array(self.bwd_packet_lengths)
all_pl_array = np.array(self.packet_lengths)
# Handle empty arrays
if len(fwd_pl_array) == 0:
fwd_pl_array = np.array([0])
if len(bwd_pl_array) == 0:
bwd_pl_array = np.array([0])
if len(all_pl_array) == 0:
all_pl_array = np.array([0])
if len(self.fwd_iat) == 0:
self.fwd_iat = [0]
if len(self.bwd_iat) == 0:
self.bwd_iat = [0]
if len(self.flow_iat) == 0:
self.flow_iat = [0]
flow_duration = (self.flow_end_time - self.flow_start_time) * 1e6 # in microseconds
# Compute features
features = {
' Destination Port': self.dst_port,
' Flow Duration': flow_duration,
' Total Fwd Packets': self.total_fwd_packets,
' Total Backward Packets': self.total_bwd_packets,
'Total Length of Fwd Packets': self.total_length_fwd_packets,
' Total Length of Bwd Packets': self.total_length_bwd_packets,
' Fwd Packet Length Max': np.max(fwd_pl_array),
' Fwd Packet Length Min': np.min(fwd_pl_array),
' Fwd Packet Length Mean': np.mean(fwd_pl_array),
' Fwd Packet Length Std': np.std(fwd_pl_array),
'Bwd Packet Length Max': np.max(bwd_pl_array),
' Bwd Packet Length Min': np.min(bwd_pl_array),
' Bwd Packet Length Mean': np.mean(bwd_pl_array),
' Bwd Packet Length Std': np.std(bwd_pl_array),
'Flow Bytes/s': ((self.total_length_fwd_packets + self.total_length_bwd_packets) / flow_duration) * 1e6 if flow_duration > 0 else 0,
' Flow Packets/s': ((self.total_fwd_packets + self.total_bwd_packets) / flow_duration) * 1e6 if flow_duration > 0 else 0,
' Flow IAT Mean': np.mean(self.flow_iat),
' Flow IAT Std': np.std(self.flow_iat),
' Flow IAT Max': np.max(self.flow_iat),
' Flow IAT Min': np.min(self.flow_iat),
'Fwd IAT Total': sum(self.fwd_iat),
' Fwd IAT Mean': np.mean(self.fwd_iat),
' Fwd IAT Std': np.std(self.fwd_iat),
' Fwd IAT Max': np.max(self.fwd_iat),
' Fwd IAT Min': np.min(self.fwd_iat),
'Bwd IAT Total': sum(self.bwd_iat),
' Bwd IAT Mean': np.mean(self.bwd_iat),
' Bwd IAT Std': np.std(self.bwd_iat),
' Bwd IAT Max': np.max(self.bwd_iat),
' Bwd IAT Min': np.min(self.bwd_iat),
'Fwd PSH Flags': 0,
' Bwd PSH Flags': 0,
' Fwd URG Flags': 0,
' Bwd URG Flags': 0,
' Fwd Header Length': self.fwd_header_length,
' Bwd Header Length': self.bwd_header_length,
'Fwd Packets/s': (self.total_fwd_packets / flow_duration) * 1e6 if flow_duration > 0 else 0,
' Bwd Packets/s': (self.total_bwd_packets / flow_duration) * 1e6 if flow_duration > 0 else 0,
' Min Packet Length': np.min(all_pl_array),
' Max Packet Length': np.max(all_pl_array),
' Packet Length Mean': np.mean(all_pl_array),
' Packet Length Std': np.std(all_pl_array),
' Packet Length Variance': np.var(all_pl_array),
'FIN Flag Count': self.fin_flag_count,
' SYN Flag Count': self.syn_flag_count,
' RST Flag Count': self.rst_flag_count,
' PSH Flag Count': self.psh_flag_count,
' ACK Flag Count': self.ack_flag_count,
' URG Flag Count': self.urg_flag_count,
' CWE Flag Count': self.cwe_flag_count,
' ECE Flag Count': self.ece_flag_count,
' Down/Up Ratio': (self.total_fwd_packets / self.total_bwd_packets) if self.total_bwd_packets > 0 else 0,
' Average Packet Size': (np.mean(all_pl_array)) if len(all_pl_array) > 0 else 0,
' Avg Fwd Segment Size': (self.total_length_fwd_packets / self.total_fwd_packets) if self.total_fwd_packets > 0 else 0,
' Avg Bwd Segment Size': (self.total_length_bwd_packets / self.total_bwd_packets) if self.total_bwd_packets > 0 else 0,
' Fwd Header Length.1': self.fwd_header_length,
'Fwd Avg Bytes/Bulk': 0,
' Fwd Avg Packets/Bulk': 0,
' Fwd Avg Bulk Rate': 0,
' Bwd Avg Bytes/Bulk': 0,
' Bwd Avg Packets/Bulk': 0,
'Bwd Avg Bulk Rate': 0,
'Subflow Fwd Packets': self.total_fwd_packets,
' Subflow Fwd Bytes': self.total_length_fwd_packets,
' Subflow Bwd Packets': self.total_bwd_packets,
' Subflow Bwd Bytes': self.total_length_bwd_packets,
'Init_Win_bytes_forward': self.init_win_bytes_forward or 0,
' Init_Win_bytes_backward': self.init_win_bytes_backward or 0,
' act_data_pkt_fwd': self.act_data_pkt_fwd,
' min_seg_size_forward': self.min_seg_size_forward or 0,
'Active Mean': 0,
' Active Std': 0,
' Active Max': 0,
' Active Min': 0,
'Idle Mean': 0,
' Idle Std': 0,
' Idle Max': 0,
' Idle Min': 0,
}
for feature in FEATURE_NAMES:
if feature not in features:
features[feature] = 0
return features
def get_all_interfaces():
"""
Get all available network interfaces with their IP addresses.
"""
try:
interfaces = net.interfaces()
excluded_interfaces = ['lo', 'lo0', 'bridge', 'docker', 'vmnet']
available_interfaces = []
for iface in interfaces:
if any(excluded in iface for excluded in excluded_interfaces):
continue
try:
addrs = net.ifaddresses(iface)
ip_info = addrs.get(net.AF_INET)
if ip_info:
ip_addr = ip_info[0].get('addr', 'N/A')
available_interfaces.append((iface, ip_addr))
else:
available_interfaces.append((iface, 'N/A'))
except ValueError:
continue
return available_interfaces
except Exception as e:
print(f"Error getting network interfaces: {e}")
return []
def capture_packets(interface_name, packet_queue, stop_event):
try:
capture = pyshark.LiveCapture(interface=interface_name)
for packet in capture.sniff_continuously():
if stop_event.is_set():
break
packet_queue.put(packet)
except Exception as e:
print(f"Error capturing packets: {e}")
NUM_THREADS = 4 # Number of threads for packet processing
def start_processing_threads(packet_queue, flow_dict, pipeline, stats):
"""
Start multiple threads to process packets in parallel.
"""
for _ in range(NUM_THREADS):
thread = threading.Thread(
target=process_packets,
args=(packet_queue, flow_dict, pipeline, stats),
daemon=True
)
thread.start()
def process_packets(packet_queue, flow_dict, pipeline, stats):
while True:
try:
try:
packet = packet_queue.get(timeout=1)
except Empty:
continue
if not hasattr(packet, 'ip'):
if VERBOSE:
print(f"Skipping non-IP packet: {packet}")
continue
# Extract packet information outside the lock
try:
if not hasattr(packet.ip, 'src') or not hasattr(packet.ip, 'dst'):
if VERBOSE:
print(f"Packet missing IP addresses: {packet}")
continue
src_ip = packet.ip.src
dst_ip = packet.ip.dst
# Get port information
if hasattr(packet, 'tcp'):
src_port = int(packet.tcp.srcport)
dst_port = int(packet.tcp.dstport)
protocol = 'TCP'
elif hasattr(packet, 'udp'):
src_port = int(packet.udp.srcport)
dst_port = int(packet.udp.dstport)
protocol = 'UDP'
else:
continue
# Create flow keys
forward_key = (src_ip, src_port, dst_ip, dst_port, protocol)
backward_key = (dst_ip, dst_port, src_ip, src_port, protocol)
# Update stats first
stats.update_stats(False)
# Now use the lock when accessing flow_dict
with flow_dict_lock:
# Get or create flow
if forward_key in flow_dict:
flow = flow_dict[forward_key]
direction = 'forward'
elif backward_key in flow_dict:
flow = flow_dict[backward_key]
direction = 'backward'
else:
flow = Flow(src_ip, src_port, dst_ip, dst_port, protocol)
flow_dict[forward_key] = flow
direction = 'forward'
# Add packet to flow while still holding the lock
flow.add_packet(packet, direction)
# Check for expired flows while holding the lock
for flow_key, flow in list(flow_dict.items()):
if flow.is_expired(timeout=60):
try:
# Extract features and make prediction
features = flow.compute_features()
features_df = pd.DataFrame([features])
features_df = features_df[pipeline['selected_features']]
X = features_df.copy()
X = pipeline['variance_selector'].transform(X)
X = pipeline['scaler'].transform(X)
X = pipeline['selector'].transform(X)
prediction = pipeline['model'].predict(X)
# Log prediction
src_ip, src_port, dst_ip, dst_port, proto = flow_key
status = 'DDoS' if prediction[0] == 1 else 'Normal'
packets = flow.total_fwd_packets + flow.total_bwd_packets
print(f"[{time.strftime('%H:%M:%S')}] {src_ip}:{src_port} β†’ {dst_ip}:{dst_port} | {proto} | Packets: {packets} | Status: {status}")
except Exception as e:
print(f"Prediction error: {e}")
finally:
del flow_dict[flow_key]
except AttributeError as e:
if VERBOSE:
print(f"Packet parsing error: {e}")
continue
except Exception as e:
if VERBOSE:
print(f"Processing error: {e}")
continue
def process_packets(packet_queue, flow_dict, pipeline, stats):
while True:
try:
try:
packet = packet_queue.get(timeout=1)
except Empty:
continue
if not hasattr(packet, 'ip'):
if VERBOSE:
print(f"Skipping non-IP packet: {packet}")
continue
try:
# Extract flow information with error checking
if not hasattr(packet.ip, 'src') or not hasattr(packet.ip, 'dst'):
if VERBOSE:
print(f"Packet missing IP addresses: {packet}")
continue
src_ip = packet.ip.src
dst_ip = packet.ip.dst
# Get port information with better error handling
if hasattr(packet, 'tcp'):
try:
src_port = int(packet.tcp.srcport)
dst_port = int(packet.tcp.dstport)
protocol = 'TCP'
except (AttributeError, ValueError) as e:
if VERBOSE:
print(f"Error getting TCP ports: {e}")
continue
elif hasattr(packet, 'udp'):
try:
src_port = int(packet.udp.srcport)
dst_port = int(packet.udp.dstport)
protocol = 'UDP'
except (AttributeError, ValueError) as e:
if VERBOSE:
print(f"Error getting UDP ports: {e}")
continue
else:
if VERBOSE:
print(f"Packet is neither TCP nor UDP: {packet}")
continue
# Process flow and update statistics
forward_key = (src_ip, src_port, dst_ip, dst_port, protocol)
backward_key = (dst_ip, dst_port, src_ip, src_port, protocol)
# Update stats first
stats.update_stats(False)
# Get or create flow
if forward_key in flow_dict:
flow = flow_dict[forward_key]
direction = 'forward'
elif backward_key in flow_dict:
flow = flow_dict[backward_key]
direction = 'backward'
else:
flow = Flow(src_ip, src_port, dst_ip, dst_port, protocol)
flow_dict[forward_key] = flow
direction = 'forward'
flow.add_packet(packet, direction)
except AttributeError as e:
if VERBOSE:
print(f"Packet parsing error: {e}")
print(f"Packet details: {packet}")
continue
except Exception as e:
if VERBOSE:
print(f"Processing error: {e}")
import traceback
print(traceback.format_exc())
continue
def select_interface(interfaces):
"""Select network interface for packet capture"""
if len(interfaces) == 1:
# If only one active interface, automatically select it
interface_name = interfaces[0][0]
print(f"Automatically selected interface: {interface_name} (IP: {interfaces[0][1]})")
return interface_name
# Display multiple active interfaces and let user select
print("\nAvailable Network Interfaces:")
for idx, (iface, ip_addr) in enumerate(interfaces):
print(f"{idx}: {iface} (IP: {ip_addr})")
while True:
try:
selected_idx = int(input("\nSelect interface index for capture: "))
if 0 <= selected_idx < len(interfaces):
return interfaces[selected_idx][0]
print("Invalid selection. Try again.")
except ValueError:
print("Please enter a valid number.")
def predict_flow(flow, pipeline):
"""Make prediction for a single flow"""
features = flow.compute_features()
features_df = pd.DataFrame([features])
features_df = features_df[pipeline['selected_features']]
X = features_df.copy()
X = pipeline['variance_selector'].transform(X)
X = pipeline['scaler'].transform(X)
X = pipeline['selector'].transform(X)
return pipeline['model'].predict(X)[0]
def start_capture_threads(interface_name, packet_queue, flow_dict, pipeline, stats, stop_event):
"""Start capture and processing threads"""
capture_thread = threading.Thread(
target=capture_packets,
args=(interface_name, packet_queue, stop_event),
daemon=True
)
processing_thread = threading.Thread(
target=process_packets,
args=(packet_queue, flow_dict, pipeline, stats),
daemon=True
)
capture_thread.start()
processing_thread.start()
return [capture_thread, processing_thread]
def cleanup(stop_event, threads, stats):
"""Clean up threads and display final statistics"""
stop_event.set()
for thread in threads:
thread.join(timeout=5)
stats.print_stats()
print("\nCapture stopped.")
def main():
print("Network Traffic DDoS Monitor")
# Verify features first thing in main
verify_features()
# Initialize statistics
stats = PacketStats()
# Initialize queue and flow tracking
packet_queue = Queue(maxsize=QUEUE_SIZE)
flow_dict = {}
# Get interfaces and setup capture
interfaces = [(iface, ip) for iface, ip in get_all_interfaces() if ip != 'N/A']
if not interfaces:
print("No active network interfaces found.")
return
interface_name = select_interface(interfaces)
print(f"\nStarting capture on: {interface_name}")
# Start capture
stop_event = threading.Event()
threads = start_capture_threads(interface_name, packet_queue, flow_dict, pipeline, stats, stop_event)
try:
while True:
time.sleep(10)
stats.print_stats()
except KeyboardInterrupt:
print("\nStopping capture...")
finally:
cleanup(stop_event, threads, stats)
# Feature extraction and prediction
print("\nProcessing captured network flows...")
features_list = []
predictions = []
for flow_key, flow in flow_dict.items():
try:
# Get features
features = flow.compute_features()
features_list.append(features)
# Create DataFrame with only the required features
features_df = pd.DataFrame([features])
feature_vector = pd.DataFrame(columns=pipeline['selected_features'])
for feature in pipeline['selected_features']:
feature_vector[feature] = features_df.get(feature, 0)
# Apply the pipeline transformations
X = pipeline['variance_selector'].transform(feature_vector)
X = pipeline['scaler'].transform(X)
X = pipeline['selector'].transform(X)
# Make prediction
prediction = pipeline['model'].predict(X)
predictions.append(prediction[0])
# Print prediction
src_ip, src_port, dst_ip, dst_port, proto = flow_key
print(f"Flow: {src_ip}:{src_port} -> {dst_ip}:{dst_port} ({proto})")
print(f"Prediction: {'BENIGN' if prediction[0] == 0 else 'DDoS'}")
print(f"Total packets: Forward={flow.total_fwd_packets}, Backward={flow.total_bwd_packets}")
print("-" * 50)
except Exception as e:
print(f"Error processing flow: {str(e)}")
# Save results if we have any
if features_list:
df = pd.DataFrame(features_list)
df['Prediction'] = predictions
output_file = 'network_traffic_predictions.csv'
df.to_csv(output_file, index=False)
print(f"\nFeatures and predictions saved to {output_file}")
print(f"Total flows captured: {len(features_list)}")
else:
print("No network flows were captured.")
def streamlit_app():
st.title("Real-Time Network Traffic DDoS Monitor")
st.markdown("Monitor your network traffic in real time and detect potential DDoS attacks.")
# Initialize session state
if 'is_scanning' not in st.session_state:
st.session_state.is_scanning = False
if 'packet_queue' not in st.session_state:
st.session_state.packet_queue = Queue(maxsize=QUEUE_SIZE)
if 'flow_dict' not in st.session_state:
st.session_state.flow_dict = {}
if 'stats' not in st.session_state:
st.session_state.stats = PacketStats()
if 'stop_event' not in st.session_state:
st.session_state.stop_event = threading.Event()
if 'selected_interface' not in st.session_state:
st.session_state.selected_interface = None
# Get interfaces and handle interface selection
interfaces = [(iface, ip) for iface, ip in get_all_interfaces() if ip != 'N/A']
if not interfaces:
st.error("No active network interfaces found.")
return
# Automatic interface selection if only one available
if len(interfaces) == 1:
if not st.session_state.selected_interface:
st.session_state.selected_interface = interfaces[0][0]
st.info(f"Using network interface: {st.session_state.selected_interface} (IP: {interfaces[0][1]})")
else:
# Show selection box only if multiple interfaces available
st.session_state.selected_interface = st.selectbox(
"Select Network Interface",
[iface[0] for iface in interfaces],
key='interface_select'
)
# Control buttons in the same row
col1, col2 = st.columns(2)
with col1:
if st.button("Start Scanning", key='start_button'):
st.session_state.is_scanning = True
st.session_state.stop_event.clear()
threads = start_capture_threads(
st.session_state.selected_interface,
st.session_state.packet_queue,
st.session_state.flow_dict,
pipeline,
st.session_state.stats,
st.session_state.stop_event
)
st.session_state.threads = threads
with col2:
if st.button("Stop Scanning", key='stop_button'):
st.session_state.is_scanning = False
if hasattr(st.session_state, 'stop_event'):
st.session_state.stop_event.set()
if hasattr(st.session_state, 'threads'):
for thread in st.session_state.threads:
thread.join(timeout=5)
# Display statistics
if st.session_state.is_scanning:
stats_container = st.container()
with stats_container:
st.markdown("### Statistics:")
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Total Packets", st.session_state.stats.total_packets)
with col2:
st.metric("DDoS Flows", st.session_state.stats.ddos_flows)
with col3:
st.metric("Benign Flows", st.session_state.stats.benign_flows)
# Display active flows
flow_data = []
for flow_key, flow in st.session_state.flow_dict.items():
src_ip, src_port, dst_ip, dst_port, protocol = flow_key
packets = flow.total_fwd_packets + flow.total_bwd_packets
flow_data.append([src_ip, src_port, dst_ip, dst_port, protocol, packets])
if flow_data:
st.markdown("### Active Flows")
df = pd.DataFrame(flow_data,
columns=["Src IP", "Src Port", "Dst IP", "Dst Port", "Protocol", "Packets"])
st.dataframe(df, use_container_width=True)
if st.session_state.is_scanning:
time.sleep(0.01)
st.rerun()
if __name__ == "__main__":
st.set_page_config(
page_title="DDoS Monitor",
page_icon="πŸ”",
layout="wide",
initial_sidebar_state="collapsed"
)
streamlit_app()