#!/usr/bin/env python3 from collections import defaultdict def parse_log(file_path): syn_counts = defaultdict(int) dest_ips = defaultdict(set) dest_ports = defaultdict(set) with open(file_path) as f: for line in f: parts = line.strip().split(None, 1) if len(parts) < 2: continue _, rest = parts fields = rest.split(',') if len(fields) < 23: continue proto = fields[16].lower() if proto != 'tcp': continue flags = fields[22] # Count only SYN packets (new connection attempts) if 'S' not in flags: continue src_ip = fields[18] dst_ip = fields[19] dst_port = fields[21] syn_counts[src_ip] += 1 dest_ips[src_ip].add(dst_ip) dest_ports[src_ip].add(dst_port) return syn_counts, dest_ips, dest_ports def main(): import argparse parser = argparse.ArgumentParser(description="Detect hosts with high outbound TCP SYN rates") parser.add_argument("logfile", help="Path to the router log file") parser.add_argument("--top", type=int, default=10, help="Number of top hosts to display") parser.add_argument("--min-conns", type=int, default=5, help="Minimum SYN connections to include") args = parser.parse_args() syn_counts, dest_ips, dest_ports = parse_log(args.logfile) # Sort by SYN count descending top = sorted(syn_counts.items(), key=lambda x: x[1], reverse=True) print(f"{'Source IP':<15} {'SYNs':<5} {'Unique Dest IPs':<15} {'Dest Ports'}") print("-" * 60) for ip, count in top[:args.top]: if count < args.min_conns: continue print(f"{ip:<15} {count:<5} {len(dest_ips[ip]):<15} {','.join(sorted(dest_ports[ip]))}") if __name__ == "__main__": main()