Spaces:
Sleeping
Sleeping
Upload app.py
Browse files
app.py
ADDED
@@ -0,0 +1,940 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pyshark
|
2 |
+
import threading
|
3 |
+
import time
|
4 |
+
import numpy as np
|
5 |
+
import pandas as pd
|
6 |
+
from queue import Queue, Empty
|
7 |
+
import netifaces as net
|
8 |
+
import os
|
9 |
+
import joblib
|
10 |
+
from threading import Lock
|
11 |
+
import streamlit as st
|
12 |
+
|
13 |
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
14 |
+
|
15 |
+
MODEL_PATH = os.path.join(current_dir, 'Anomaly_Model.joblib')
|
16 |
+
flow_dict_lock = threading.Lock()
|
17 |
+
FEATURE_NAMES = [
|
18 |
+
' Destination Port',
|
19 |
+
' Flow Duration',
|
20 |
+
' Total Fwd Packets',
|
21 |
+
' Total Backward Packets',
|
22 |
+
'Total Length of Fwd Packets',
|
23 |
+
' Total Length of Bwd Packets',
|
24 |
+
' Fwd Packet Length Max',
|
25 |
+
' Fwd Packet Length Min',
|
26 |
+
' Fwd Packet Length Mean',
|
27 |
+
' Fwd Packet Length Std',
|
28 |
+
'Bwd Packet Length Max',
|
29 |
+
' Bwd Packet Length Min',
|
30 |
+
' Bwd Packet Length Mean',
|
31 |
+
' Bwd Packet Length Std',
|
32 |
+
'Flow Bytes/s',
|
33 |
+
' Flow Packets/s',
|
34 |
+
' Flow IAT Mean',
|
35 |
+
' Flow IAT Std',
|
36 |
+
' Flow IAT Max',
|
37 |
+
' Flow IAT Min',
|
38 |
+
'Fwd IAT Total',
|
39 |
+
' Fwd IAT Mean',
|
40 |
+
' Fwd IAT Std',
|
41 |
+
' Fwd IAT Max',
|
42 |
+
' Fwd IAT Min',
|
43 |
+
'Bwd IAT Total',
|
44 |
+
' Bwd IAT Mean',
|
45 |
+
' Bwd IAT Std',
|
46 |
+
' Bwd IAT Max',
|
47 |
+
' Bwd IAT Min',
|
48 |
+
'Fwd PSH Flags',
|
49 |
+
' Bwd PSH Flags',
|
50 |
+
' Fwd URG Flags',
|
51 |
+
' Bwd URG Flags',
|
52 |
+
' Fwd Header Length',
|
53 |
+
' Bwd Header Length',
|
54 |
+
'Fwd Packets/s',
|
55 |
+
' Bwd Packets/s',
|
56 |
+
' Min Packet Length',
|
57 |
+
' Max Packet Length',
|
58 |
+
' Packet Length Mean',
|
59 |
+
' Packet Length Std',
|
60 |
+
' Packet Length Variance',
|
61 |
+
'FIN Flag Count',
|
62 |
+
' SYN Flag Count',
|
63 |
+
' RST Flag Count',
|
64 |
+
' PSH Flag Count',
|
65 |
+
' ACK Flag Count',
|
66 |
+
' URG Flag Count',
|
67 |
+
' CWE Flag Count',
|
68 |
+
' ECE Flag Count',
|
69 |
+
' Down/Up Ratio',
|
70 |
+
' Average Packet Size',
|
71 |
+
' Avg Fwd Segment Size',
|
72 |
+
' Avg Bwd Segment Size',
|
73 |
+
' Fwd Header Length.1',
|
74 |
+
'Fwd Avg Bytes/Bulk',
|
75 |
+
' Fwd Avg Packets/Bulk',
|
76 |
+
' Fwd Avg Bulk Rate',
|
77 |
+
' Bwd Avg Bytes/Bulk',
|
78 |
+
' Bwd Avg Packets/Bulk',
|
79 |
+
'Bwd Avg Bulk Rate',
|
80 |
+
'Subflow Fwd Packets',
|
81 |
+
' Subflow Fwd Bytes',
|
82 |
+
' Subflow Bwd Packets',
|
83 |
+
' Subflow Bwd Bytes',
|
84 |
+
'Init_Win_bytes_forward',
|
85 |
+
' Init_Win_bytes_backward',
|
86 |
+
' act_data_pkt_fwd',
|
87 |
+
' min_seg_size_forward',
|
88 |
+
'Active Mean',
|
89 |
+
' Active Std',
|
90 |
+
' Active Max',
|
91 |
+
' Active Min',
|
92 |
+
'Idle Mean',
|
93 |
+
' Idle Std',
|
94 |
+
' Idle Max',
|
95 |
+
' Idle Min'
|
96 |
+
]
|
97 |
+
|
98 |
+
# Add verification after loading the model
|
99 |
+
def verify_features():
|
100 |
+
"""Verify that we have all required features and they match exactly"""
|
101 |
+
print(f"\nFeature Verification:")
|
102 |
+
print(f"Total features in training data: {len(FEATURE_NAMES)}")
|
103 |
+
print(f"Features in loaded model: {len(pipeline['selected_features'])}")
|
104 |
+
|
105 |
+
# Check for missing features
|
106 |
+
missing_features = set(FEATURE_NAMES) - set(pipeline['selected_features'])
|
107 |
+
if missing_features:
|
108 |
+
print("\nWARNING: Missing features in model:")
|
109 |
+
for feature in missing_features:
|
110 |
+
print(f"- {feature}")
|
111 |
+
|
112 |
+
# Check for extra features
|
113 |
+
extra_features = set(pipeline['selected_features']) - set(FEATURE_NAMES)
|
114 |
+
if extra_features:
|
115 |
+
print("\nWARNING: Extra features in model:")
|
116 |
+
for feature in extra_features:
|
117 |
+
print(f"- {feature}")
|
118 |
+
|
119 |
+
# Print first few features for verification
|
120 |
+
print("\nFirst 5 features:")
|
121 |
+
for i, feature in enumerate(FEATURE_NAMES[:5]):
|
122 |
+
print(f"{i+1}. '{feature}'")
|
123 |
+
|
124 |
+
|
125 |
+
|
126 |
+
# Add these constants at the top
|
127 |
+
PACKET_TIMEOUT = 60 # Flow expiration timeout in seconds
|
128 |
+
QUEUE_SIZE = 10000 # Maximum packet queue size
|
129 |
+
VERBOSE = False # Enable/disable detailed logging
|
130 |
+
|
131 |
+
# Add a packet counter class
|
132 |
+
class PacketStats:
|
133 |
+
def __init__(self):
|
134 |
+
self.total_packets = 0
|
135 |
+
self.ddos_flows = 0
|
136 |
+
self.benign_flows = 0
|
137 |
+
self.start_time = time.time()
|
138 |
+
self.lock = threading.Lock()
|
139 |
+
|
140 |
+
def update_stats(self, is_ddos):
|
141 |
+
with self.lock:
|
142 |
+
self.total_packets += 1
|
143 |
+
if is_ddos:
|
144 |
+
self.ddos_flows += 1
|
145 |
+
else:
|
146 |
+
self.benign_flows += 1
|
147 |
+
|
148 |
+
def print_stats(self):
|
149 |
+
with self.lock:
|
150 |
+
elapsed_time = time.time() - self.start_time
|
151 |
+
print(f"\nMonitoring Statistics:")
|
152 |
+
print(f"Running time: {elapsed_time:.2f} seconds")
|
153 |
+
print(f"Total packets processed: {self.total_packets}")
|
154 |
+
print(f"DDoS flows detected: {self.ddos_flows}")
|
155 |
+
print(f"Benign flows detected: {self.benign_flows}")
|
156 |
+
|
157 |
+
|
158 |
+
try:
|
159 |
+
print("Loading model...")
|
160 |
+
# Use the constructed path to load the model
|
161 |
+
model_data = joblib.load(MODEL_PATH)
|
162 |
+
pipeline = {
|
163 |
+
'model': model_data['model'],
|
164 |
+
'scaler': model_data['model'].named_steps['scaler'],
|
165 |
+
'selector': model_data['model'].named_steps['feature_selection'],
|
166 |
+
'variance_selector': model_data['model'].named_steps['variance_threshold'],
|
167 |
+
'selected_features': model_data['feature_names']
|
168 |
+
}
|
169 |
+
print("Model loaded successfully")
|
170 |
+
except Exception as e:
|
171 |
+
print(f"Error loading model: {e}")
|
172 |
+
raise
|
173 |
+
|
174 |
+
|
175 |
+
|
176 |
+
class Flow:
|
177 |
+
def is_expired(self, timeout=60):
|
178 |
+
return (time.time() - self.flow_end_time) > timeout
|
179 |
+
|
180 |
+
def __init__(self, src_ip, src_port, dst_ip, dst_port, protocol):
|
181 |
+
# Flow identifiers
|
182 |
+
self.src_ip = src_ip
|
183 |
+
self.src_port = src_port
|
184 |
+
self.dst_ip = dst_ip
|
185 |
+
self.dst_port = dst_port
|
186 |
+
self.protocol = protocol
|
187 |
+
|
188 |
+
# Packet tracking
|
189 |
+
self.total_fwd_packets = 0
|
190 |
+
self.total_bwd_packets = 0
|
191 |
+
self.total_length_fwd_packets = 0
|
192 |
+
self.total_length_bwd_packets = 0
|
193 |
+
|
194 |
+
# Packet lengths
|
195 |
+
self.fwd_packet_lengths = []
|
196 |
+
self.bwd_packet_lengths = []
|
197 |
+
self.packet_lengths = [] # All packet lengths
|
198 |
+
|
199 |
+
# Inter-arrival times
|
200 |
+
self.fwd_iat = []
|
201 |
+
self.bwd_iat = []
|
202 |
+
self.flow_iat = []
|
203 |
+
self.last_fwd_packet_time = None
|
204 |
+
self.last_bwd_packet_time = None
|
205 |
+
self.last_packet_time = None
|
206 |
+
|
207 |
+
# Header lengths
|
208 |
+
self.fwd_header_length = 0
|
209 |
+
self.bwd_header_length = 0
|
210 |
+
|
211 |
+
# Flags
|
212 |
+
self.fin_flag_count = 0
|
213 |
+
self.syn_flag_count = 0
|
214 |
+
self.rst_flag_count = 0
|
215 |
+
self.psh_flag_count = 0
|
216 |
+
self.ack_flag_count = 0
|
217 |
+
self.urg_flag_count = 0
|
218 |
+
self.cwe_flag_count = 0
|
219 |
+
self.ece_flag_count = 0
|
220 |
+
|
221 |
+
# Window sizes
|
222 |
+
self.init_win_bytes_forward = None
|
223 |
+
self.init_win_bytes_backward = None
|
224 |
+
self.act_data_pkt_fwd = 0
|
225 |
+
self.min_seg_size_forward = None
|
226 |
+
|
227 |
+
# Active and Idle times
|
228 |
+
self.flow_start_time = time.time()
|
229 |
+
self.flow_end_time = self.flow_start_time
|
230 |
+
self.active_times = []
|
231 |
+
self.idle_times = []
|
232 |
+
self.last_active_time = None
|
233 |
+
|
234 |
+
# Other features
|
235 |
+
self.flow_packet_times = []
|
236 |
+
|
237 |
+
def add_packet(self, packet, direction):
|
238 |
+
try:
|
239 |
+
if not hasattr(packet, 'sniff_timestamp'):
|
240 |
+
if VERBOSE:
|
241 |
+
print(f"Packet missing sniff_timestamp: {packet}")
|
242 |
+
return
|
243 |
+
|
244 |
+
current_time = float(packet.sniff_timestamp)
|
245 |
+
self.flow_end_time = current_time
|
246 |
+
|
247 |
+
# Track packet times for flow IAT
|
248 |
+
self.flow_packet_times.append(current_time)
|
249 |
+
if len(self.flow_packet_times) > 1:
|
250 |
+
iat = self.flow_packet_times[-1] - self.flow_packet_times[-2]
|
251 |
+
self.flow_iat.append(iat)
|
252 |
+
|
253 |
+
# Packet length - add error checking
|
254 |
+
try:
|
255 |
+
packet_length = int(packet.length)
|
256 |
+
except (AttributeError, ValueError) as e:
|
257 |
+
if VERBOSE:
|
258 |
+
print(f"Error getting packet length: {e}, packet: {packet}")
|
259 |
+
return
|
260 |
+
|
261 |
+
self.packet_lengths.append(packet_length)
|
262 |
+
|
263 |
+
# Detailed error handling for IP addresses
|
264 |
+
try:
|
265 |
+
if hasattr(packet, 'ip'):
|
266 |
+
src_ip = packet.ip.src
|
267 |
+
dst_ip = packet.ip.dst
|
268 |
+
elif hasattr(packet, 'ipv6'):
|
269 |
+
src_ip = packet.ipv6.src
|
270 |
+
dst_ip = packet.ipv6.dst
|
271 |
+
else:
|
272 |
+
if VERBOSE:
|
273 |
+
print(f"Packet has no IP layer: {packet}")
|
274 |
+
return
|
275 |
+
except AttributeError as e:
|
276 |
+
if VERBOSE:
|
277 |
+
print(f"Error accessing IP addresses: {e}, packet: {packet}")
|
278 |
+
return
|
279 |
+
current_time = float(packet.sniff_timestamp)
|
280 |
+
self.flow_end_time = current_time
|
281 |
+
|
282 |
+
# Track packet times for flow IAT
|
283 |
+
self.flow_packet_times.append(current_time)
|
284 |
+
if len(self.flow_packet_times) > 1:
|
285 |
+
iat = self.flow_packet_times[-1] - self.flow_packet_times[-2]
|
286 |
+
self.flow_iat.append(iat)
|
287 |
+
|
288 |
+
# Packet length
|
289 |
+
packet_length = int(packet.length)
|
290 |
+
self.packet_lengths.append(packet_length)
|
291 |
+
|
292 |
+
# Header length calculation
|
293 |
+
header_length = 14 # Ethernet header
|
294 |
+
if hasattr(packet, 'ip'):
|
295 |
+
src_ip = packet.ip.src
|
296 |
+
dst_ip = packet.ip.dst
|
297 |
+
elif hasattr(packet, 'ipv6'):
|
298 |
+
src_ip = packet.ipv6.src
|
299 |
+
dst_ip = packet.ipv6.dst
|
300 |
+
else:
|
301 |
+
return
|
302 |
+
|
303 |
+
if hasattr(packet, 'tcp'):
|
304 |
+
header_length += int(packet.tcp.hdr_len or 0)
|
305 |
+
if hasattr(packet.tcp, 'flags'):
|
306 |
+
flags = int(packet.tcp.flags_hex, 16)
|
307 |
+
self.fin_flag_count += bool(flags & 0x01)
|
308 |
+
self.syn_flag_count += bool(flags & 0x02)
|
309 |
+
self.rst_flag_count += bool(flags & 0x04)
|
310 |
+
self.psh_flag_count += bool(flags & 0x08)
|
311 |
+
self.ack_flag_count += bool(flags & 0x10)
|
312 |
+
self.urg_flag_count += bool(flags & 0x20)
|
313 |
+
self.ece_flag_count += bool(flags & 0x40)
|
314 |
+
self.cwe_flag_count += bool(flags & 0x80)
|
315 |
+
|
316 |
+
if direction == 'forward' and self.init_win_bytes_forward is None:
|
317 |
+
self.init_win_bytes_forward = int(packet.tcp.window_size or 0)
|
318 |
+
elif direction == 'backward' and self.init_win_bytes_backward is None:
|
319 |
+
self.init_win_bytes_backward = int(packet.tcp.window_size or 0)
|
320 |
+
|
321 |
+
if self.min_seg_size_forward is None:
|
322 |
+
self.min_seg_size_forward = int(packet.tcp.hdr_len or 0)
|
323 |
+
|
324 |
+
elif hasattr(packet, 'udp'):
|
325 |
+
header_length += 8
|
326 |
+
|
327 |
+
if direction == 'forward':
|
328 |
+
self.total_fwd_packets += 1
|
329 |
+
self.total_length_fwd_packets += packet_length
|
330 |
+
self.fwd_packet_lengths.append(packet_length)
|
331 |
+
self.fwd_header_length += header_length
|
332 |
+
self.act_data_pkt_fwd += 1
|
333 |
+
|
334 |
+
if self.last_fwd_packet_time is not None:
|
335 |
+
iat = current_time - self.last_fwd_packet_time
|
336 |
+
self.fwd_iat.append(iat)
|
337 |
+
self.last_fwd_packet_time = current_time
|
338 |
+
else:
|
339 |
+
self.total_bwd_packets += 1
|
340 |
+
self.total_length_bwd_packets += packet_length
|
341 |
+
self.bwd_packet_lengths.append(packet_length)
|
342 |
+
self.bwd_header_length += header_length
|
343 |
+
|
344 |
+
if self.last_bwd_packet_time is not None:
|
345 |
+
iat = current_time - self.last_bwd_packet_time
|
346 |
+
self.bwd_iat.append(iat)
|
347 |
+
self.last_bwd_packet_time = current_time
|
348 |
+
|
349 |
+
except Exception as e:
|
350 |
+
if VERBOSE:
|
351 |
+
print(f"Error processing packet: {str(e)}")
|
352 |
+
print(f"Packet details: {packet}")
|
353 |
+
import traceback
|
354 |
+
print(traceback.format_exc())
|
355 |
+
|
356 |
+
|
357 |
+
def compute_features(self):
|
358 |
+
# Compute statistical features for packet lengths
|
359 |
+
fwd_pl_array = np.array(self.fwd_packet_lengths)
|
360 |
+
bwd_pl_array = np.array(self.bwd_packet_lengths)
|
361 |
+
all_pl_array = np.array(self.packet_lengths)
|
362 |
+
|
363 |
+
# Handle empty arrays
|
364 |
+
if len(fwd_pl_array) == 0:
|
365 |
+
fwd_pl_array = np.array([0])
|
366 |
+
if len(bwd_pl_array) == 0:
|
367 |
+
bwd_pl_array = np.array([0])
|
368 |
+
if len(all_pl_array) == 0:
|
369 |
+
all_pl_array = np.array([0])
|
370 |
+
if len(self.fwd_iat) == 0:
|
371 |
+
self.fwd_iat = [0]
|
372 |
+
if len(self.bwd_iat) == 0:
|
373 |
+
self.bwd_iat = [0]
|
374 |
+
if len(self.flow_iat) == 0:
|
375 |
+
self.flow_iat = [0]
|
376 |
+
|
377 |
+
flow_duration = (self.flow_end_time - self.flow_start_time) * 1e6 # in microseconds
|
378 |
+
|
379 |
+
# Compute features
|
380 |
+
features = {
|
381 |
+
' Destination Port': self.dst_port,
|
382 |
+
' Flow Duration': flow_duration,
|
383 |
+
' Total Fwd Packets': self.total_fwd_packets,
|
384 |
+
' Total Backward Packets': self.total_bwd_packets,
|
385 |
+
'Total Length of Fwd Packets': self.total_length_fwd_packets,
|
386 |
+
' Total Length of Bwd Packets': self.total_length_bwd_packets,
|
387 |
+
' Fwd Packet Length Max': np.max(fwd_pl_array),
|
388 |
+
' Fwd Packet Length Min': np.min(fwd_pl_array),
|
389 |
+
' Fwd Packet Length Mean': np.mean(fwd_pl_array),
|
390 |
+
' Fwd Packet Length Std': np.std(fwd_pl_array),
|
391 |
+
'Bwd Packet Length Max': np.max(bwd_pl_array),
|
392 |
+
' Bwd Packet Length Min': np.min(bwd_pl_array),
|
393 |
+
' Bwd Packet Length Mean': np.mean(bwd_pl_array),
|
394 |
+
' Bwd Packet Length Std': np.std(bwd_pl_array),
|
395 |
+
'Flow Bytes/s': ((self.total_length_fwd_packets + self.total_length_bwd_packets) / flow_duration) * 1e6 if flow_duration > 0 else 0,
|
396 |
+
' Flow Packets/s': ((self.total_fwd_packets + self.total_bwd_packets) / flow_duration) * 1e6 if flow_duration > 0 else 0,
|
397 |
+
' Flow IAT Mean': np.mean(self.flow_iat),
|
398 |
+
' Flow IAT Std': np.std(self.flow_iat),
|
399 |
+
' Flow IAT Max': np.max(self.flow_iat),
|
400 |
+
' Flow IAT Min': np.min(self.flow_iat),
|
401 |
+
'Fwd IAT Total': sum(self.fwd_iat),
|
402 |
+
' Fwd IAT Mean': np.mean(self.fwd_iat),
|
403 |
+
' Fwd IAT Std': np.std(self.fwd_iat),
|
404 |
+
' Fwd IAT Max': np.max(self.fwd_iat),
|
405 |
+
' Fwd IAT Min': np.min(self.fwd_iat),
|
406 |
+
'Bwd IAT Total': sum(self.bwd_iat),
|
407 |
+
' Bwd IAT Mean': np.mean(self.bwd_iat),
|
408 |
+
' Bwd IAT Std': np.std(self.bwd_iat),
|
409 |
+
' Bwd IAT Max': np.max(self.bwd_iat),
|
410 |
+
' Bwd IAT Min': np.min(self.bwd_iat),
|
411 |
+
'Fwd PSH Flags': 0,
|
412 |
+
' Bwd PSH Flags': 0,
|
413 |
+
' Fwd URG Flags': 0,
|
414 |
+
' Bwd URG Flags': 0,
|
415 |
+
' Fwd Header Length': self.fwd_header_length,
|
416 |
+
' Bwd Header Length': self.bwd_header_length,
|
417 |
+
'Fwd Packets/s': (self.total_fwd_packets / flow_duration) * 1e6 if flow_duration > 0 else 0,
|
418 |
+
' Bwd Packets/s': (self.total_bwd_packets / flow_duration) * 1e6 if flow_duration > 0 else 0,
|
419 |
+
' Min Packet Length': np.min(all_pl_array),
|
420 |
+
' Max Packet Length': np.max(all_pl_array),
|
421 |
+
' Packet Length Mean': np.mean(all_pl_array),
|
422 |
+
' Packet Length Std': np.std(all_pl_array),
|
423 |
+
' Packet Length Variance': np.var(all_pl_array),
|
424 |
+
'FIN Flag Count': self.fin_flag_count,
|
425 |
+
' SYN Flag Count': self.syn_flag_count,
|
426 |
+
' RST Flag Count': self.rst_flag_count,
|
427 |
+
' PSH Flag Count': self.psh_flag_count,
|
428 |
+
' ACK Flag Count': self.ack_flag_count,
|
429 |
+
' URG Flag Count': self.urg_flag_count,
|
430 |
+
' CWE Flag Count': self.cwe_flag_count,
|
431 |
+
' ECE Flag Count': self.ece_flag_count,
|
432 |
+
' Down/Up Ratio': (self.total_fwd_packets / self.total_bwd_packets) if self.total_bwd_packets > 0 else 0,
|
433 |
+
' Average Packet Size': (np.mean(all_pl_array)) if len(all_pl_array) > 0 else 0,
|
434 |
+
' Avg Fwd Segment Size': (self.total_length_fwd_packets / self.total_fwd_packets) if self.total_fwd_packets > 0 else 0,
|
435 |
+
' Avg Bwd Segment Size': (self.total_length_bwd_packets / self.total_bwd_packets) if self.total_bwd_packets > 0 else 0,
|
436 |
+
' Fwd Header Length.1': self.fwd_header_length,
|
437 |
+
'Fwd Avg Bytes/Bulk': 0,
|
438 |
+
' Fwd Avg Packets/Bulk': 0,
|
439 |
+
' Fwd Avg Bulk Rate': 0,
|
440 |
+
' Bwd Avg Bytes/Bulk': 0,
|
441 |
+
' Bwd Avg Packets/Bulk': 0,
|
442 |
+
'Bwd Avg Bulk Rate': 0,
|
443 |
+
'Subflow Fwd Packets': self.total_fwd_packets,
|
444 |
+
' Subflow Fwd Bytes': self.total_length_fwd_packets,
|
445 |
+
' Subflow Bwd Packets': self.total_bwd_packets,
|
446 |
+
' Subflow Bwd Bytes': self.total_length_bwd_packets,
|
447 |
+
'Init_Win_bytes_forward': self.init_win_bytes_forward or 0,
|
448 |
+
' Init_Win_bytes_backward': self.init_win_bytes_backward or 0,
|
449 |
+
' act_data_pkt_fwd': self.act_data_pkt_fwd,
|
450 |
+
' min_seg_size_forward': self.min_seg_size_forward or 0,
|
451 |
+
'Active Mean': 0,
|
452 |
+
' Active Std': 0,
|
453 |
+
' Active Max': 0,
|
454 |
+
' Active Min': 0,
|
455 |
+
'Idle Mean': 0,
|
456 |
+
' Idle Std': 0,
|
457 |
+
' Idle Max': 0,
|
458 |
+
' Idle Min': 0,
|
459 |
+
}
|
460 |
+
|
461 |
+
for feature in FEATURE_NAMES:
|
462 |
+
if feature not in features:
|
463 |
+
features[feature] = 0
|
464 |
+
|
465 |
+
return features
|
466 |
+
|
467 |
+
def get_all_interfaces():
|
468 |
+
"""
|
469 |
+
Get all available network interfaces with their IP addresses.
|
470 |
+
"""
|
471 |
+
try:
|
472 |
+
interfaces = net.interfaces()
|
473 |
+
excluded_interfaces = ['lo', 'lo0', 'bridge', 'docker', 'vmnet']
|
474 |
+
available_interfaces = []
|
475 |
+
|
476 |
+
for iface in interfaces:
|
477 |
+
if any(excluded in iface for excluded in excluded_interfaces):
|
478 |
+
continue
|
479 |
+
|
480 |
+
try:
|
481 |
+
addrs = net.ifaddresses(iface)
|
482 |
+
ip_info = addrs.get(net.AF_INET)
|
483 |
+
if ip_info:
|
484 |
+
ip_addr = ip_info[0].get('addr', 'N/A')
|
485 |
+
available_interfaces.append((iface, ip_addr))
|
486 |
+
else:
|
487 |
+
available_interfaces.append((iface, 'N/A'))
|
488 |
+
except ValueError:
|
489 |
+
continue
|
490 |
+
|
491 |
+
return available_interfaces
|
492 |
+
except Exception as e:
|
493 |
+
print(f"Error getting network interfaces: {e}")
|
494 |
+
return []
|
495 |
+
|
496 |
+
def capture_packets(interface_name, packet_queue, stop_event):
|
497 |
+
try:
|
498 |
+
capture = pyshark.LiveCapture(interface=interface_name)
|
499 |
+
for packet in capture.sniff_continuously():
|
500 |
+
if stop_event.is_set():
|
501 |
+
break
|
502 |
+
packet_queue.put(packet)
|
503 |
+
except Exception as e:
|
504 |
+
print(f"Error capturing packets: {e}")
|
505 |
+
|
506 |
+
NUM_THREADS = 4 # Number of threads for packet processing
|
507 |
+
|
508 |
+
def start_processing_threads(packet_queue, flow_dict, pipeline, stats):
|
509 |
+
"""
|
510 |
+
Start multiple threads to process packets in parallel.
|
511 |
+
"""
|
512 |
+
for _ in range(NUM_THREADS):
|
513 |
+
thread = threading.Thread(
|
514 |
+
target=process_packets,
|
515 |
+
args=(packet_queue, flow_dict, pipeline, stats),
|
516 |
+
daemon=True
|
517 |
+
)
|
518 |
+
thread.start()
|
519 |
+
|
520 |
+
def process_packets(packet_queue, flow_dict, pipeline, stats):
|
521 |
+
while True:
|
522 |
+
try:
|
523 |
+
try:
|
524 |
+
packet = packet_queue.get(timeout=1)
|
525 |
+
except Empty:
|
526 |
+
continue
|
527 |
+
|
528 |
+
if not hasattr(packet, 'ip'):
|
529 |
+
if VERBOSE:
|
530 |
+
print(f"Skipping non-IP packet: {packet}")
|
531 |
+
continue
|
532 |
+
|
533 |
+
# Extract packet information outside the lock
|
534 |
+
try:
|
535 |
+
if not hasattr(packet.ip, 'src') or not hasattr(packet.ip, 'dst'):
|
536 |
+
if VERBOSE:
|
537 |
+
print(f"Packet missing IP addresses: {packet}")
|
538 |
+
continue
|
539 |
+
|
540 |
+
src_ip = packet.ip.src
|
541 |
+
dst_ip = packet.ip.dst
|
542 |
+
|
543 |
+
# Get port information
|
544 |
+
if hasattr(packet, 'tcp'):
|
545 |
+
src_port = int(packet.tcp.srcport)
|
546 |
+
dst_port = int(packet.tcp.dstport)
|
547 |
+
protocol = 'TCP'
|
548 |
+
elif hasattr(packet, 'udp'):
|
549 |
+
src_port = int(packet.udp.srcport)
|
550 |
+
dst_port = int(packet.udp.dstport)
|
551 |
+
protocol = 'UDP'
|
552 |
+
else:
|
553 |
+
continue
|
554 |
+
|
555 |
+
# Create flow keys
|
556 |
+
forward_key = (src_ip, src_port, dst_ip, dst_port, protocol)
|
557 |
+
backward_key = (dst_ip, dst_port, src_ip, src_port, protocol)
|
558 |
+
|
559 |
+
# Update stats first
|
560 |
+
stats.update_stats(False)
|
561 |
+
|
562 |
+
# Now use the lock when accessing flow_dict
|
563 |
+
with flow_dict_lock:
|
564 |
+
# Get or create flow
|
565 |
+
if forward_key in flow_dict:
|
566 |
+
flow = flow_dict[forward_key]
|
567 |
+
direction = 'forward'
|
568 |
+
elif backward_key in flow_dict:
|
569 |
+
flow = flow_dict[backward_key]
|
570 |
+
direction = 'backward'
|
571 |
+
else:
|
572 |
+
flow = Flow(src_ip, src_port, dst_ip, dst_port, protocol)
|
573 |
+
flow_dict[forward_key] = flow
|
574 |
+
direction = 'forward'
|
575 |
+
|
576 |
+
# Add packet to flow while still holding the lock
|
577 |
+
flow.add_packet(packet, direction)
|
578 |
+
|
579 |
+
# Check for expired flows while holding the lock
|
580 |
+
for flow_key, flow in list(flow_dict.items()):
|
581 |
+
if flow.is_expired(timeout=60):
|
582 |
+
try:
|
583 |
+
# Extract features and make prediction
|
584 |
+
features = flow.compute_features()
|
585 |
+
features_df = pd.DataFrame([features])
|
586 |
+
features_df = features_df[pipeline['selected_features']]
|
587 |
+
X = features_df.copy()
|
588 |
+
X = pipeline['variance_selector'].transform(X)
|
589 |
+
X = pipeline['scaler'].transform(X)
|
590 |
+
X = pipeline['selector'].transform(X)
|
591 |
+
prediction = pipeline['model'].predict(X)
|
592 |
+
|
593 |
+
# Log prediction
|
594 |
+
src_ip, src_port, dst_ip, dst_port, proto = flow_key
|
595 |
+
status = 'DDoS' if prediction[0] == 1 else 'Normal'
|
596 |
+
packets = flow.total_fwd_packets + flow.total_bwd_packets
|
597 |
+
print(f"[{time.strftime('%H:%M:%S')}] {src_ip}:{src_port} → {dst_ip}:{dst_port} | {proto} | Packets: {packets} | Status: {status}")
|
598 |
+
|
599 |
+
except Exception as e:
|
600 |
+
print(f"Prediction error: {e}")
|
601 |
+
finally:
|
602 |
+
del flow_dict[flow_key]
|
603 |
+
|
604 |
+
except AttributeError as e:
|
605 |
+
if VERBOSE:
|
606 |
+
print(f"Packet parsing error: {e}")
|
607 |
+
continue
|
608 |
+
|
609 |
+
except Exception as e:
|
610 |
+
if VERBOSE:
|
611 |
+
print(f"Processing error: {e}")
|
612 |
+
continue
|
613 |
+
|
614 |
+
def process_packets(packet_queue, flow_dict, pipeline, stats):
|
615 |
+
while True:
|
616 |
+
try:
|
617 |
+
try:
|
618 |
+
packet = packet_queue.get(timeout=1)
|
619 |
+
except Empty:
|
620 |
+
continue
|
621 |
+
|
622 |
+
if not hasattr(packet, 'ip'):
|
623 |
+
if VERBOSE:
|
624 |
+
print(f"Skipping non-IP packet: {packet}")
|
625 |
+
continue
|
626 |
+
|
627 |
+
try:
|
628 |
+
# Extract flow information with error checking
|
629 |
+
if not hasattr(packet.ip, 'src') or not hasattr(packet.ip, 'dst'):
|
630 |
+
if VERBOSE:
|
631 |
+
print(f"Packet missing IP addresses: {packet}")
|
632 |
+
continue
|
633 |
+
|
634 |
+
src_ip = packet.ip.src
|
635 |
+
dst_ip = packet.ip.dst
|
636 |
+
|
637 |
+
# Get port information with better error handling
|
638 |
+
if hasattr(packet, 'tcp'):
|
639 |
+
try:
|
640 |
+
src_port = int(packet.tcp.srcport)
|
641 |
+
dst_port = int(packet.tcp.dstport)
|
642 |
+
protocol = 'TCP'
|
643 |
+
except (AttributeError, ValueError) as e:
|
644 |
+
if VERBOSE:
|
645 |
+
print(f"Error getting TCP ports: {e}")
|
646 |
+
continue
|
647 |
+
elif hasattr(packet, 'udp'):
|
648 |
+
try:
|
649 |
+
src_port = int(packet.udp.srcport)
|
650 |
+
dst_port = int(packet.udp.dstport)
|
651 |
+
protocol = 'UDP'
|
652 |
+
except (AttributeError, ValueError) as e:
|
653 |
+
if VERBOSE:
|
654 |
+
print(f"Error getting UDP ports: {e}")
|
655 |
+
continue
|
656 |
+
else:
|
657 |
+
if VERBOSE:
|
658 |
+
print(f"Packet is neither TCP nor UDP: {packet}")
|
659 |
+
continue
|
660 |
+
|
661 |
+
# Process flow and update statistics
|
662 |
+
forward_key = (src_ip, src_port, dst_ip, dst_port, protocol)
|
663 |
+
backward_key = (dst_ip, dst_port, src_ip, src_port, protocol)
|
664 |
+
|
665 |
+
# Update stats first
|
666 |
+
stats.update_stats(False)
|
667 |
+
|
668 |
+
# Get or create flow
|
669 |
+
if forward_key in flow_dict:
|
670 |
+
flow = flow_dict[forward_key]
|
671 |
+
direction = 'forward'
|
672 |
+
elif backward_key in flow_dict:
|
673 |
+
flow = flow_dict[backward_key]
|
674 |
+
direction = 'backward'
|
675 |
+
else:
|
676 |
+
flow = Flow(src_ip, src_port, dst_ip, dst_port, protocol)
|
677 |
+
flow_dict[forward_key] = flow
|
678 |
+
direction = 'forward'
|
679 |
+
|
680 |
+
flow.add_packet(packet, direction)
|
681 |
+
|
682 |
+
except AttributeError as e:
|
683 |
+
if VERBOSE:
|
684 |
+
print(f"Packet parsing error: {e}")
|
685 |
+
print(f"Packet details: {packet}")
|
686 |
+
continue
|
687 |
+
|
688 |
+
except Exception as e:
|
689 |
+
if VERBOSE:
|
690 |
+
print(f"Processing error: {e}")
|
691 |
+
import traceback
|
692 |
+
print(traceback.format_exc())
|
693 |
+
continue
|
694 |
+
|
695 |
+
def select_interface(interfaces):
|
696 |
+
"""Select network interface for packet capture"""
|
697 |
+
if len(interfaces) == 1:
|
698 |
+
# If only one active interface, automatically select it
|
699 |
+
interface_name = interfaces[0][0]
|
700 |
+
print(f"Automatically selected interface: {interface_name} (IP: {interfaces[0][1]})")
|
701 |
+
return interface_name
|
702 |
+
|
703 |
+
# Display multiple active interfaces and let user select
|
704 |
+
print("\nAvailable Network Interfaces:")
|
705 |
+
for idx, (iface, ip_addr) in enumerate(interfaces):
|
706 |
+
print(f"{idx}: {iface} (IP: {ip_addr})")
|
707 |
+
|
708 |
+
while True:
|
709 |
+
try:
|
710 |
+
selected_idx = int(input("\nSelect interface index for capture: "))
|
711 |
+
if 0 <= selected_idx < len(interfaces):
|
712 |
+
return interfaces[selected_idx][0]
|
713 |
+
print("Invalid selection. Try again.")
|
714 |
+
except ValueError:
|
715 |
+
print("Please enter a valid number.")
|
716 |
+
|
717 |
+
def predict_flow(flow, pipeline):
|
718 |
+
"""Make prediction for a single flow"""
|
719 |
+
features = flow.compute_features()
|
720 |
+
features_df = pd.DataFrame([features])
|
721 |
+
features_df = features_df[pipeline['selected_features']]
|
722 |
+
|
723 |
+
X = features_df.copy()
|
724 |
+
X = pipeline['variance_selector'].transform(X)
|
725 |
+
X = pipeline['scaler'].transform(X)
|
726 |
+
X = pipeline['selector'].transform(X)
|
727 |
+
return pipeline['model'].predict(X)[0]
|
728 |
+
|
729 |
+
def start_capture_threads(interface_name, packet_queue, flow_dict, pipeline, stats, stop_event):
|
730 |
+
"""Start capture and processing threads"""
|
731 |
+
capture_thread = threading.Thread(
|
732 |
+
target=capture_packets,
|
733 |
+
args=(interface_name, packet_queue, stop_event),
|
734 |
+
daemon=True
|
735 |
+
)
|
736 |
+
|
737 |
+
processing_thread = threading.Thread(
|
738 |
+
target=process_packets,
|
739 |
+
args=(packet_queue, flow_dict, pipeline, stats),
|
740 |
+
daemon=True
|
741 |
+
)
|
742 |
+
|
743 |
+
capture_thread.start()
|
744 |
+
processing_thread.start()
|
745 |
+
|
746 |
+
return [capture_thread, processing_thread]
|
747 |
+
|
748 |
+
def cleanup(stop_event, threads, stats):
|
749 |
+
"""Clean up threads and display final statistics"""
|
750 |
+
stop_event.set()
|
751 |
+
for thread in threads:
|
752 |
+
thread.join(timeout=5)
|
753 |
+
stats.print_stats()
|
754 |
+
print("\nCapture stopped.")
|
755 |
+
|
756 |
+
|
757 |
+
def main():
|
758 |
+
print("Network Traffic DDoS Monitor")
|
759 |
+
|
760 |
+
# Verify features first thing in main
|
761 |
+
verify_features()
|
762 |
+
|
763 |
+
# Initialize statistics
|
764 |
+
stats = PacketStats()
|
765 |
+
|
766 |
+
# Initialize queue and flow tracking
|
767 |
+
packet_queue = Queue(maxsize=QUEUE_SIZE)
|
768 |
+
flow_dict = {}
|
769 |
+
|
770 |
+
# Get interfaces and setup capture
|
771 |
+
interfaces = [(iface, ip) for iface, ip in get_all_interfaces() if ip != 'N/A']
|
772 |
+
if not interfaces:
|
773 |
+
print("No active network interfaces found.")
|
774 |
+
return
|
775 |
+
|
776 |
+
interface_name = select_interface(interfaces)
|
777 |
+
print(f"\nStarting capture on: {interface_name}")
|
778 |
+
|
779 |
+
# Start capture
|
780 |
+
stop_event = threading.Event()
|
781 |
+
threads = start_capture_threads(interface_name, packet_queue, flow_dict, pipeline, stats, stop_event)
|
782 |
+
|
783 |
+
try:
|
784 |
+
while True:
|
785 |
+
time.sleep(10)
|
786 |
+
stats.print_stats()
|
787 |
+
|
788 |
+
except KeyboardInterrupt:
|
789 |
+
print("\nStopping capture...")
|
790 |
+
finally:
|
791 |
+
cleanup(stop_event, threads, stats)
|
792 |
+
|
793 |
+
# Feature extraction and prediction
|
794 |
+
print("\nProcessing captured network flows...")
|
795 |
+
features_list = []
|
796 |
+
predictions = []
|
797 |
+
|
798 |
+
for flow_key, flow in flow_dict.items():
|
799 |
+
try:
|
800 |
+
# Get features
|
801 |
+
features = flow.compute_features()
|
802 |
+
features_list.append(features)
|
803 |
+
|
804 |
+
# Create DataFrame with only the required features
|
805 |
+
features_df = pd.DataFrame([features])
|
806 |
+
feature_vector = pd.DataFrame(columns=pipeline['selected_features'])
|
807 |
+
for feature in pipeline['selected_features']:
|
808 |
+
feature_vector[feature] = features_df.get(feature, 0)
|
809 |
+
|
810 |
+
# Apply the pipeline transformations
|
811 |
+
X = pipeline['variance_selector'].transform(feature_vector)
|
812 |
+
X = pipeline['scaler'].transform(X)
|
813 |
+
X = pipeline['selector'].transform(X)
|
814 |
+
|
815 |
+
# Make prediction
|
816 |
+
prediction = pipeline['model'].predict(X)
|
817 |
+
predictions.append(prediction[0])
|
818 |
+
|
819 |
+
# Print prediction
|
820 |
+
src_ip, src_port, dst_ip, dst_port, proto = flow_key
|
821 |
+
print(f"Flow: {src_ip}:{src_port} -> {dst_ip}:{dst_port} ({proto})")
|
822 |
+
print(f"Prediction: {'BENIGN' if prediction[0] == 0 else 'DDoS'}")
|
823 |
+
print(f"Total packets: Forward={flow.total_fwd_packets}, Backward={flow.total_bwd_packets}")
|
824 |
+
print("-" * 50)
|
825 |
+
|
826 |
+
except Exception as e:
|
827 |
+
print(f"Error processing flow: {str(e)}")
|
828 |
+
|
829 |
+
# Save results if we have any
|
830 |
+
if features_list:
|
831 |
+
df = pd.DataFrame(features_list)
|
832 |
+
df['Prediction'] = predictions
|
833 |
+
output_file = 'network_traffic_predictions.csv'
|
834 |
+
df.to_csv(output_file, index=False)
|
835 |
+
print(f"\nFeatures and predictions saved to {output_file}")
|
836 |
+
print(f"Total flows captured: {len(features_list)}")
|
837 |
+
else:
|
838 |
+
print("No network flows were captured.")
|
839 |
+
|
840 |
+
def streamlit_app():
|
841 |
+
st.title("Real-Time Network Traffic DDoS Monitor")
|
842 |
+
st.markdown("Monitor your network traffic in real time and detect potential DDoS attacks.")
|
843 |
+
|
844 |
+
# Initialize session state
|
845 |
+
if 'is_scanning' not in st.session_state:
|
846 |
+
st.session_state.is_scanning = False
|
847 |
+
if 'packet_queue' not in st.session_state:
|
848 |
+
st.session_state.packet_queue = Queue(maxsize=QUEUE_SIZE)
|
849 |
+
if 'flow_dict' not in st.session_state:
|
850 |
+
st.session_state.flow_dict = {}
|
851 |
+
if 'stats' not in st.session_state:
|
852 |
+
st.session_state.stats = PacketStats()
|
853 |
+
if 'stop_event' not in st.session_state:
|
854 |
+
st.session_state.stop_event = threading.Event()
|
855 |
+
if 'selected_interface' not in st.session_state:
|
856 |
+
st.session_state.selected_interface = None
|
857 |
+
|
858 |
+
# Get interfaces and handle interface selection
|
859 |
+
interfaces = [(iface, ip) for iface, ip in get_all_interfaces() if ip != 'N/A']
|
860 |
+
|
861 |
+
if not interfaces:
|
862 |
+
st.error("No active network interfaces found.")
|
863 |
+
return
|
864 |
+
|
865 |
+
# Automatic interface selection if only one available
|
866 |
+
if len(interfaces) == 1:
|
867 |
+
if not st.session_state.selected_interface:
|
868 |
+
st.session_state.selected_interface = interfaces[0][0]
|
869 |
+
st.info(f"Using network interface: {st.session_state.selected_interface} (IP: {interfaces[0][1]})")
|
870 |
+
else:
|
871 |
+
# Show selection box only if multiple interfaces available
|
872 |
+
st.session_state.selected_interface = st.selectbox(
|
873 |
+
"Select Network Interface",
|
874 |
+
[iface[0] for iface in interfaces],
|
875 |
+
key='interface_select'
|
876 |
+
)
|
877 |
+
|
878 |
+
# Control buttons in the same row
|
879 |
+
col1, col2 = st.columns(2)
|
880 |
+
with col1:
|
881 |
+
if st.button("Start Scanning", key='start_button'):
|
882 |
+
st.session_state.is_scanning = True
|
883 |
+
st.session_state.stop_event.clear()
|
884 |
+
threads = start_capture_threads(
|
885 |
+
st.session_state.selected_interface,
|
886 |
+
st.session_state.packet_queue,
|
887 |
+
st.session_state.flow_dict,
|
888 |
+
pipeline,
|
889 |
+
st.session_state.stats,
|
890 |
+
st.session_state.stop_event
|
891 |
+
)
|
892 |
+
st.session_state.threads = threads
|
893 |
+
|
894 |
+
with col2:
|
895 |
+
if st.button("Stop Scanning", key='stop_button'):
|
896 |
+
st.session_state.is_scanning = False
|
897 |
+
if hasattr(st.session_state, 'stop_event'):
|
898 |
+
st.session_state.stop_event.set()
|
899 |
+
if hasattr(st.session_state, 'threads'):
|
900 |
+
for thread in st.session_state.threads:
|
901 |
+
thread.join(timeout=5)
|
902 |
+
|
903 |
+
# Display statistics
|
904 |
+
if st.session_state.is_scanning:
|
905 |
+
stats_container = st.container()
|
906 |
+
with stats_container:
|
907 |
+
st.markdown("### Statistics:")
|
908 |
+
col1, col2, col3 = st.columns(3)
|
909 |
+
with col1:
|
910 |
+
st.metric("Total Packets", st.session_state.stats.total_packets)
|
911 |
+
with col2:
|
912 |
+
st.metric("DDoS Flows", st.session_state.stats.ddos_flows)
|
913 |
+
with col3:
|
914 |
+
st.metric("Benign Flows", st.session_state.stats.benign_flows)
|
915 |
+
|
916 |
+
# Display active flows
|
917 |
+
flow_data = []
|
918 |
+
for flow_key, flow in st.session_state.flow_dict.items():
|
919 |
+
src_ip, src_port, dst_ip, dst_port, protocol = flow_key
|
920 |
+
packets = flow.total_fwd_packets + flow.total_bwd_packets
|
921 |
+
flow_data.append([src_ip, src_port, dst_ip, dst_port, protocol, packets])
|
922 |
+
|
923 |
+
if flow_data:
|
924 |
+
st.markdown("### Active Flows")
|
925 |
+
df = pd.DataFrame(flow_data,
|
926 |
+
columns=["Src IP", "Src Port", "Dst IP", "Dst Port", "Protocol", "Packets"])
|
927 |
+
st.dataframe(df, use_container_width=True)
|
928 |
+
|
929 |
+
if st.session_state.is_scanning:
|
930 |
+
time.sleep(0.01)
|
931 |
+
st.rerun()
|
932 |
+
|
933 |
+
if __name__ == "__main__":
|
934 |
+
st.set_page_config(
|
935 |
+
page_title="DDoS Monitor",
|
936 |
+
page_icon="🔍",
|
937 |
+
layout="wide",
|
938 |
+
initial_sidebar_state="collapsed"
|
939 |
+
)
|
940 |
+
streamlit_app()
|