def accept_aggregate(file, net_file, clients_dir, since='', filter_peer='', external_only='0'): """ Aggregate accept events per peer — total bytes, packets, top destinations. Used by wgctl activity to show accepted traffic alongside drops. external_only='1': only show traffic to external IPs (non-private) external_only='0': only show traffic to internal IPs (default) Output: peer|peer_name|bytes_in|bytes_out|packets_in|packets_out|conn_count dest|peer_name|dst_ip|dst_port|proto|bytes_total|conn_count """ from collections import defaultdict from itertools import groupby since_dt = parse_since(since) if since else None show_external = str(external_only) == '1' peer_stats = defaultdict(lambda: { 'bytes_in': 0, 'bytes_out': 0, 'packets_in': 0, 'packets_out': 0, 'conn_count': 0 }) # dest_stats = defaultdict(lambda: {'bytes': 0, 'count': 0}) dest_stats = defaultdict(lambda: {'bytes_orig': 0, 'bytes_reply': 0, 'count': 0}) try: with open(file) as f: for line in f: try: e = json.loads(line.strip()) peer = e.get('peer', '') if not peer: continue if filter_peer and peer != filter_peer: continue # Filter by external/internal is_external = e.get('external', False) if show_external and not is_external: continue if not show_external and is_external: continue if since_dt: ts_str = e.get('ts', '') try: from datetime import timezone ev_dt = datetime.fromisoformat( ts_str.replace('Z', '+00:00')) if ev_dt < since_dt: continue except Exception: pass dst_ip = e.get('dst_ip', '') dst_port = str(e.get('dst_port', '')) proto = e.get('proto', '') b_orig = e.get('bytes_orig', 0) b_reply = e.get('bytes_reply', 0) p_orig = e.get('packets_orig', 0) p_reply = e.get('packets_reply', 0) ps = peer_stats[peer] ps['bytes_out'] += b_orig ps['bytes_in'] += b_reply ps['packets_out'] += p_orig ps['packets_in'] += p_reply ps['conn_count'] += 1 dest_key = (peer, dst_ip, dst_port, proto) dest_stats[dest_key]['bytes_orig'] += b_orig dest_stats[dest_key]['bytes_reply'] += b_reply dest_stats[dest_key]['count'] += 1 except Exception: pass except Exception: pass # Output peer summaries for peer, ps in sorted(peer_stats.items()): print(f"peer|{peer}|{ps['bytes_in']}|{ps['bytes_out']}|" f"{ps['packets_in']}|{ps['packets_out']}|{ps['conn_count']}") # Output top 5 destinations per peer sorted by byte count dest_items = sorted( dest_stats.items(), key=lambda x: (x[0][0], -x[1]['bytes']) ) for peer, group in groupby(dest_items, key=lambda x: x[0][0]): top = list(group)[:20] for (p, dst_ip, dst_port, proto), stats in top: print(f"dest|{p}|{dst_ip}|{dst_port}|{proto}|" f"{stats['bytes_orig']}|{stats['bytes_reply']}|{stats['count']}")