Cipher29 commited on
Commit
e36b96b
·
verified ·
1 Parent(s): 30d081c

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +940 -0
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()