Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import time | |
| import random | |
| import folium | |
| from folium.plugins import MarkerCluster | |
| from streamlit_folium import st_folium | |
| import pygeoip | |
| from collections import deque, OrderedDict | |
| import datetime | |
| import plotly.graph_objs as go | |
| from plotly.subplots import make_subplots | |
| # Function to get geolocation | |
| def get_geolocation(ip): | |
| gi = pygeoip.GeoIP('GeoLiteCity.dat') | |
| try: | |
| return gi.record_by_addr(ip) | |
| except: | |
| return None | |
| # Function to simulate a DDoS attack | |
| def simulate_ddos_attack(): | |
| simulated_ips = [f"192.168.1.{random.randint(1, 255)}" for _ in range(10)] | |
| packets = random.randint(50, 200) # Random packet count | |
| return simulated_ips, packets | |
| # Set up the Streamlit app | |
| st.title("Real-Time Network Traffic DDoS Monitor") | |
| # Create a single stop button at the top of the app | |
| stop_button = st.button('Stop', key='stop_button') | |
| # Statistics section | |
| st.header("Statistics") | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| total_packets = st.empty() | |
| with col2: | |
| ddos_flows = st.empty() | |
| with col3: | |
| benign_flows = st.empty() | |
| # Divider line | |
| st.markdown("---") | |
| # Active Flows section | |
| st.header("Active Flows") | |
| # Create placeholders for the tables, graphs, and map | |
| active_flows_placeholder = st.empty() | |
| malicious_ips_placeholder = st.empty() | |
| graphs_placeholder = st.empty() | |
| map_placeholder = st.empty() | |
| # Initialize map in session state if it doesn't exist | |
| if 'map' not in st.session_state: | |
| st.session_state.map = folium.Map(location=[0, 0], zoom_start=2) | |
| st.session_state.marker_cluster = MarkerCluster().add_to(st.session_state.map) | |
| st.session_state.map_counter = 0 | |
| m = st.session_state.map | |
| marker_cluster = st.session_state.marker_cluster | |
| # Display the initial map | |
| st_folium(m, width=700, height=500, key="initial_map") | |
| # Load data in chunks | |
| chunk_size = 1000 # Adjust this value based on your needs | |
| data_iterator = pd.read_csv('SSDP_Flood_output_copy.csv', chunksize=chunk_size) | |
| # Initialize data structures | |
| if 'ip_packet_counts' not in st.session_state: | |
| st.session_state.ip_packet_counts = {} | |
| if 'time_series_data' not in st.session_state: | |
| st.session_state.time_series_data = [] | |
| if 'ip_packet_time_series' not in st.session_state: | |
| st.session_state.ip_packet_time_series = {} | |
| if 'recent_rows' not in st.session_state: | |
| st.session_state.recent_rows = deque(maxlen=10) # Correctly structured as a deque | |
| if 'malicious_ips' not in st.session_state: | |
| st.session_state.malicious_ips = OrderedDict() | |
| # Initialize counters | |
| if 'total_packet_count' not in st.session_state: | |
| st.session_state.total_packet_count = 0 | |
| if 'ddos_flow_count' not in st.session_state: | |
| st.session_state.ddos_flow_count = 0 | |
| if 'benign_flow_count' not in st.session_state: | |
| st.session_state.benign_flow_count = 0 | |
| # Flag to track if the map needs updating | |
| map_updated = False | |
| # Display Simulate DDoS Attack button | |
| if st.button("Simulate DDoS Attack"): | |
| simulated_ips, packets = simulate_ddos_attack() | |
| current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] | |
| for ip in simulated_ips: | |
| # Create a structured row to append | |
| row = { | |
| 'time': current_time, | |
| 'src': ip, | |
| 'sport': random.randint(1024, 65535), | |
| 'dst': '192.168.0.1', # Target IP | |
| 'dport': 80, # Target port | |
| 'protocol': 'TCP', | |
| 'packets': packets, | |
| 'label': 1 # Simulate as malicious | |
| } | |
| # Ensure the row is appended correctly | |
| st.session_state.recent_rows.append(row) | |
| # Update counters | |
| st.session_state.total_packet_count += packets | |
| st.session_state.ddos_flow_count += 1 | |
| # Update the statistics | |
| total_packets.metric("Total Packets", st.session_state.total_packet_count) | |
| ddos_flows.metric("DDoS Flows", st.session_state.ddos_flow_count) | |
| benign_flows.metric("Benign Flows", st.session_state.benign_flow_count) | |
| # Convert recent_rows deque to DataFrame | |
| try: | |
| active_flows_df = pd.DataFrame(list(st.session_state.recent_rows)) | |
| # Ensure DataFrame has expected columns | |
| if not {'time', 'src', 'sport', 'dst', 'dport', 'protocol', 'packets'}.issubset(active_flows_df.columns): | |
| st.error("DataFrame does not have the expected structure.") | |
| continue | |
| # Update active flows | |
| active_flows_placeholder.dataframe( | |
| active_flows_df[['time', 'src', 'sport', 'dst', 'dport', 'protocol', 'packets']], | |
| height=300, | |
| use_container_width=True, | |
| hide_index=True | |
| ) | |
| # Update the map with the simulated IP | |
| geo_info = get_geolocation(ip) | |
| if geo_info: | |
| folium.Marker( | |
| location=[geo_info['latitude'], geo_info['longitude']], | |
| popup=ip, | |
| icon=folium.Icon(color='red', icon='info-sign') | |
| ).add_to(marker_cluster) | |
| # Update the map view | |
| map_updated = True | |
| except Exception as e: | |
| st.error(f"Error creating DataFrame: {str(e)}") | |
| if map_updated: | |
| map_placeholder.empty() | |
| st.session_state.map_counter += 1 | |
| st_folium(m, width=700, height=500, key=f"map_{st.session_state.map_counter}") | |
| map_updated = False | |
| # Process data in chunks | |
| for chunk_index, chunk in enumerate(data_iterator): | |
| for row_index, row in chunk.iterrows(): | |
| if stop_button: | |
| st.write('Stopped by user') | |
| break | |
| # Update counters | |
| st.session_state.total_packet_count += row['packets'] | |
| if row['label'] == 1: | |
| st.session_state.ddos_flow_count += 1 | |
| else: | |
| st.session_state.benign_flow_count += 1 | |
| # Update statistics | |
| total_packets.metric("Total Packets", st.session_state.total_packet_count) | |
| ddos_flows.metric("DDoS Flows", st.session_state.ddos_flow_count) | |
| benign_flows.metric("Benign Flows", st.session_state.benign_flow_count) | |
| # Update the time column with current time | |
| current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] | |
| row['time'] = current_time | |
| # Update active flows table | |
| st.session_state.recent_rows.append(dict(row)) # Append the dictionary of the row | |
| # Convert recent_rows deque to DataFrame | |
| try: | |
| active_flows_df = pd.DataFrame(list(st.session_state.recent_rows)) | |
| active_flows_placeholder.dataframe( | |
| active_flows_df[['time', 'src', 'sport', 'dst', 'dport', 'protocol', 'packets']], | |
| height=300, | |
| use_container_width=True, | |
| hide_index=True | |
| ) | |
| except Exception as e: | |
| st.error(f"Error creating DataFrame: {str(e)}") | |
| # Update the malicious IPs list if the IP is malicious | |
| if row['label'] == 1: | |
| if row['src'] not in st.session_state.malicious_ips: | |
| st.session_state.malicious_ips[row['src']] = True | |
| if len(st.session_state.malicious_ips) > 10: | |
| st.session_state.malicious_ips.popitem(last=False) | |
| # Add new malicious IP to the map | |
| geo_info = get_geolocation(row['src']) | |
| if geo_info: | |
| folium.Marker( | |
| location=[geo_info['latitude'], geo_info['longitude']], | |
| popup=row['src'], | |
| icon=folium.Icon(color='red', icon='info-sign') | |
| ).add_to(marker_cluster) | |
| # Set flag to update map | |
| map_updated = True | |
| # Format malicious IPs as a numbered list | |
| malicious_ips_text = "**Recent Malicious IPs:**\n" | |
| for i, ip in enumerate(st.session_state.malicious_ips.keys(), 1): | |
| malicious_ips_text += f"{i}. <span style='color: red;'>{ip}</span>\n" | |
| malicious_ips_placeholder.markdown(malicious_ips_text, unsafe_allow_html=True) | |
| # Update packet counts for the source IP | |
| src_ip = row['src'] | |
| packets = row['packets'] | |
| if src_ip not in st.session_state.ip_packet_counts: | |
| st.session_state.ip_packet_counts[src_ip] = 0 | |
| st.session_state.ip_packet_time_series[src_ip] = [] | |
| st.session_state.ip_packet_counts[src_ip] += packets | |
| st.session_state.ip_packet_time_series[src_ip].append((current_time, st.session_state.ip_packet_counts[src_ip])) | |
| # Add current total packet count to time series data | |
| st.session_state.time_series_data.append((current_time, st.session_state.total_packet_count)) | |
| # Create and update the graphs | |
| if len(st.session_state.ip_packet_counts) > 0: | |
| fig = make_subplots(rows=3, cols=1, | |
| subplot_titles=("Top 10 Source IPs by Packet Count", | |
| "Total Packet Count Over Time", | |
| "Packet Count per Source IP Over Time")) | |
| top_ips = sorted(st.session_state.ip_packet_counts.items(), key=lambda x: x[1], reverse=True)[:10] | |
| ips, counts = zip(*top_ips) | |
| fig.add_trace(go.Bar(x=ips, y=counts), row=1, col=1) | |
| times, packet_counts = zip(*st.session_state.time_series_data[-100:]) | |
| fig.add_trace(go.Scatter(x=times, y=packet_counts, mode='lines'), row=2, col=1) | |
| for ip in ips: | |
| ip_times, ip_counts = zip(*st.session_state.ip_packet_time_series[ip][-100:]) | |
| fig.add_trace(go.Scatter(x=ip_times, y=ip_counts, mode='lines', name=ip), row=3, col=1) | |
| fig.update_layout(height=1200, showlegend=True) | |
| fig.update_xaxes(title_text="Source IP", row=1, col=1) | |
| fig.update_xaxes(title_text="Time", row=2, col=1) | |
| fig.update_xaxes(title_text="Time", row=3, col=1) | |
| fig.update_yaxes(title_text="Packet Count", row=1, col=1) | |
| fig.update_yaxes(title_text="Total Packet Count", row=2, col=1) | |
| fig.update_yaxes(title_text="Packet Count", row=3, col=1) | |
| graphs_placeholder.plotly_chart(fig, use_container_width=True) | |
| # Update the map if new points were added | |
| if map_updated: | |
| map_placeholder.empty() | |
| st.session_state.map_counter += 1 | |
| st_folium(m, width=700, height=500, key=f"map_{st.session_state.map_counter}") | |
| map_updated = False | |
| time.sleep(0.1) | |
| if stop_button: | |
| break | |
| st.write("Data processing complete") |