Update top_traffic.py
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
|
||||
# example: python3 top_traffic.py -d 300 --resolve --server-ip 10.20.10.11 -i bond0 --delete-pcap
|
||||
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import datetime as dt
|
||||
@@ -53,15 +52,16 @@ class Packet(object):
|
||||
|
||||
|
||||
class BucketSummary(object):
|
||||
__slots__ = ("start_ts", "end_ts", "bytes_total", "packets_total", "top_ips", "top_pairs")
|
||||
__slots__ = ("start_ts", "end_ts", "bytes_total", "packets_total", "top_ips", "top_pairs", "top_ports")
|
||||
|
||||
def __init__(self, start_ts, end_ts, bytes_total, packets_total, top_ips, top_pairs):
|
||||
def __init__(self, start_ts, end_ts, bytes_total, packets_total, top_ips, top_pairs, top_ports):
|
||||
self.start_ts = start_ts
|
||||
self.end_ts = end_ts
|
||||
self.bytes_total = bytes_total
|
||||
self.packets_total = packets_total
|
||||
self.top_ips = top_ips
|
||||
self.top_pairs = top_pairs
|
||||
self.top_ports = top_ports
|
||||
|
||||
|
||||
class CaptureError(RuntimeError):
|
||||
@@ -396,13 +396,11 @@ def top_n(counter, limit):
|
||||
return sorted(counter.items(), key=lambda kv: (-kv[1], kv[0]))[:limit]
|
||||
|
||||
|
||||
|
||||
def validate_lengths(packets):
|
||||
if not packets:
|
||||
return
|
||||
max_len = max(p.length for p in packets)
|
||||
avg_len = sum(p.length for p in packets) / float(len(packets))
|
||||
# Ethernet/IP packets from tcpdump -r should not produce absurd frame sizes.
|
||||
if max_len > 10 ** 7 or avg_len > 10 ** 6:
|
||||
raise AnalysisError(
|
||||
"Parser zwrocil nierealne dlugosci pakietow (max={}, avg={:.1f}). "
|
||||
@@ -426,10 +424,15 @@ def analyze_packets(packets, bucket_seconds, top_ip_limit, top_pair_limit, local
|
||||
per_ip_out = collections.Counter()
|
||||
per_pair = collections.Counter()
|
||||
per_proto = collections.Counter()
|
||||
per_port_total = collections.Counter()
|
||||
per_port_src = collections.Counter()
|
||||
per_port_dst = collections.Counter()
|
||||
|
||||
bucket_bytes = collections.Counter()
|
||||
bucket_packets = collections.Counter()
|
||||
bucket_ip_bytes = collections.defaultdict(collections.Counter)
|
||||
bucket_pair_bytes = collections.defaultdict(collections.Counter)
|
||||
bucket_port_bytes = collections.defaultdict(collections.Counter)
|
||||
|
||||
local_total = collections.Counter()
|
||||
internal_pairs = collections.Counter()
|
||||
@@ -438,6 +441,7 @@ def analyze_packets(packets, bucket_seconds, top_ip_limit, top_pair_limit, local
|
||||
for p in packets:
|
||||
src_is_local = p.src in local_ips
|
||||
dst_is_local = p.dst in local_ips
|
||||
ports_track = False
|
||||
|
||||
if local_ips:
|
||||
if src_is_local and not dst_is_local:
|
||||
@@ -447,6 +451,7 @@ def analyze_packets(packets, bucket_seconds, top_ip_limit, top_pair_limit, local
|
||||
bucket_key_ip = p.dst
|
||||
bucket_key_pair = "{} -> {}".format(p.src, p.dst)
|
||||
local_total[p.src] += p.length
|
||||
ports_track = True
|
||||
elif dst_is_local and not src_is_local:
|
||||
per_ip_total[p.src] += p.length
|
||||
per_ip_in[p.src] += p.length
|
||||
@@ -454,6 +459,7 @@ def analyze_packets(packets, bucket_seconds, top_ip_limit, top_pair_limit, local
|
||||
bucket_key_ip = p.src
|
||||
bucket_key_pair = "{} -> {}".format(p.src, p.dst)
|
||||
local_total[p.dst] += p.length
|
||||
ports_track = True
|
||||
elif src_is_local and dst_is_local:
|
||||
internal_pairs["{} -> {}".format(p.src, p.dst)] += p.length
|
||||
local_total[p.src] += p.length
|
||||
@@ -473,6 +479,7 @@ def analyze_packets(packets, bucket_seconds, top_ip_limit, top_pair_limit, local
|
||||
bucket_key_ip_src = p.src
|
||||
bucket_key_ip_dst = p.dst
|
||||
bucket_key_pair = "{} -> {}".format(p.src, p.dst)
|
||||
ports_track = True
|
||||
|
||||
if p.proto:
|
||||
per_proto[p.proto] += p.length
|
||||
@@ -481,6 +488,18 @@ def analyze_packets(packets, bucket_seconds, top_ip_limit, top_pair_limit, local
|
||||
bucket_bytes[idx] += p.length
|
||||
bucket_packets[idx] += 1
|
||||
|
||||
if ports_track:
|
||||
if p.sport:
|
||||
sport = str(p.sport)
|
||||
per_port_src[sport] += p.length
|
||||
per_port_total[sport] += p.length
|
||||
bucket_port_bytes[idx]["src:{}".format(sport)] += p.length
|
||||
if p.dport:
|
||||
dport = str(p.dport)
|
||||
per_port_dst[dport] += p.length
|
||||
per_port_total[dport] += p.length
|
||||
bucket_port_bytes[idx]["dst:{}".format(dport)] += p.length
|
||||
|
||||
if local_ips:
|
||||
if bucket_key_ip is not None:
|
||||
bucket_ip_bytes[idx][bucket_key_ip] += p.length
|
||||
@@ -503,6 +522,7 @@ def analyze_packets(packets, bucket_seconds, top_ip_limit, top_pair_limit, local
|
||||
packets_total=bucket_packets[idx],
|
||||
top_ips=top_n(bucket_ip_bytes[idx], 3),
|
||||
top_pairs=top_n(bucket_pair_bytes[idx], 3),
|
||||
top_ports=top_n(bucket_port_bytes[idx], 3),
|
||||
)
|
||||
)
|
||||
buckets.sort(key=lambda b: (-b.bytes_total, b.start_ts))
|
||||
@@ -544,6 +564,9 @@ def analyze_packets(packets, bucket_seconds, top_ip_limit, top_pair_limit, local
|
||||
"per_ip_out": top_n(per_ip_out, top_ip_limit),
|
||||
"per_pair": top_n(per_pair, top_pair_limit),
|
||||
"per_proto": top_n(per_proto, 10),
|
||||
"per_port_total": top_n(per_port_total, top_ip_limit),
|
||||
"per_port_src": top_n(per_port_src, top_ip_limit),
|
||||
"per_port_dst": top_n(per_port_dst, top_ip_limit),
|
||||
"buckets": buckets,
|
||||
"interesting": interesting,
|
||||
"avg_bucket": avg_bucket,
|
||||
@@ -597,22 +620,33 @@ def render_report(stats, resolve=False):
|
||||
lines.append("Ruch poza lokalnymi IP: {}".format(human_bytes(stats.get("unmatched_bytes", 0))))
|
||||
lines.append("")
|
||||
|
||||
def section(title, items, formatter=None):
|
||||
def section(title, items, formatter=None, show_avg=False):
|
||||
lines.append(title)
|
||||
lines.append("-" * len(title))
|
||||
count = 0
|
||||
for key, value in items:
|
||||
count += 1
|
||||
label = formatter(key) if formatter else key
|
||||
if show_avg:
|
||||
avg = value / max(0.001, stats["duration"])
|
||||
lines.append(
|
||||
"{:>2}. {:<52} {:>12} avg={:>12}".format(
|
||||
count, label[:52], human_bytes(value), human_bps(avg)
|
||||
)
|
||||
)
|
||||
else:
|
||||
lines.append("{:>2}. {:<52} {:>12}".format(count, label[:52], human_bytes(value)))
|
||||
if count == 0:
|
||||
lines.append("(brak danych)")
|
||||
lines.append("")
|
||||
|
||||
section("Top zdalne IP lacznie" if stats.get("local_mode") else "Top IP lacznie", stats["per_ip_total"], pretty_ip)
|
||||
section("Top zdalne IP inbound do serwera" if stats.get("local_mode") else "Top IP inbound do serwera", stats["per_ip_in"], pretty_ip)
|
||||
section("Top zdalne IP outbound z serwera" if stats.get("local_mode") else "Top IP outbound z serwera", stats["per_ip_out"], pretty_ip)
|
||||
section("Top zdalne IP lacznie" if stats.get("local_mode") else "Top IP lacznie", stats["per_ip_total"], pretty_ip, show_avg=True)
|
||||
section("Top zdalne IP inbound do serwera" if stats.get("local_mode") else "Top IP inbound do serwera", stats["per_ip_in"], pretty_ip, show_avg=True)
|
||||
section("Top zdalne IP outbound z serwera" if stats.get("local_mode") else "Top IP outbound z serwera", stats["per_ip_out"], pretty_ip, show_avg=True)
|
||||
section("Top pary serwer <-> zdalny host" if stats.get("local_mode") else "Top pary src -> dst", stats["per_pair"])
|
||||
section("Top porty lacznie", stats["per_port_total"])
|
||||
section("Top porty source", stats["per_port_src"])
|
||||
section("Top porty destination", stats["per_port_dst"])
|
||||
section("Top protokoly", stats["per_proto"])
|
||||
|
||||
lines.append("Najciekawsze momenty")
|
||||
@@ -622,6 +656,7 @@ def render_report(stats, resolve=False):
|
||||
rate = bucket.bytes_total / duration
|
||||
top_ips = ", ".join("{} [{}]".format(pretty_ip(ip), human_bytes(b)) for ip, b in bucket.top_ips)
|
||||
top_pairs = ", ".join("{} [{}]".format(pair, human_bytes(b)) for pair, b in bucket.top_pairs)
|
||||
top_ports = ", ".join("{} [{}]".format(port, human_bytes(b)) for port, b in bucket.top_ports)
|
||||
lines.append(
|
||||
"{}. {} .. {} | {} | {} | pakiety={}".format(
|
||||
idx,
|
||||
@@ -634,6 +669,7 @@ def render_report(stats, resolve=False):
|
||||
)
|
||||
lines.append(" Top IP: {}".format(top_ips or "-"))
|
||||
lines.append(" Top pair: {}".format(top_pairs or "-"))
|
||||
lines.append(" Top port: {}".format(top_ports or "-"))
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines)
|
||||
@@ -652,7 +688,7 @@ def interactive_shell(stats, resolve):
|
||||
inbound = dict(stats["per_ip_in"])
|
||||
outbound = dict(stats["per_ip_out"])
|
||||
|
||||
print("Tryb interaktywny. Komendy: top, ip <addr>, moments, pairs, quit")
|
||||
print("Tryb interaktywny. Komendy: top, ip <addr>, moments, pairs, ports, quit")
|
||||
while True:
|
||||
try:
|
||||
raw = input("traffic> ").strip()
|
||||
@@ -686,14 +722,28 @@ def interactive_shell(stats, resolve):
|
||||
for i, (pair, b) in enumerate(stats["per_pair"], start=1):
|
||||
print(" {:>2}. {:<52} {:>12}".format(i, pair[:52], human_bytes(b)))
|
||||
continue
|
||||
if raw == "ports":
|
||||
print("Top porty lacznie:")
|
||||
for i, (port, b) in enumerate(stats["per_port_total"], start=1):
|
||||
print(" {:>2}. {:<20} {:>12}".format(i, port[:20], human_bytes(b)))
|
||||
print("Top porty source:")
|
||||
for i, (port, b) in enumerate(stats["per_port_src"], start=1):
|
||||
print(" {:>2}. {:<20} {:>12}".format(i, port[:20], human_bytes(b)))
|
||||
print("Top porty destination:")
|
||||
for i, (port, b) in enumerate(stats["per_port_dst"], start=1):
|
||||
print(" {:>2}. {:<20} {:>12}".format(i, port[:20], human_bytes(b)))
|
||||
continue
|
||||
if raw.startswith("ip "):
|
||||
ip = raw[3:].strip()
|
||||
print("{}".format(pretty(ip)))
|
||||
print(" total: {}".format(human_bytes(totals.get(ip, 0))))
|
||||
print(" inbound: {}".format(human_bytes(inbound.get(ip, 0))))
|
||||
print(" outbound: {}".format(human_bytes(outbound.get(ip, 0))))
|
||||
print(" avg total: {}".format(human_bps(totals.get(ip, 0) / max(0.001, stats["duration"]))))
|
||||
print(" avg inbound: {}".format(human_bps(inbound.get(ip, 0) / max(0.001, stats["duration"]))))
|
||||
print(" avg outbound: {}".format(human_bps(outbound.get(ip, 0) / max(0.001, stats["duration"]))))
|
||||
continue
|
||||
print("Nieznana komenda. Uzyj: top, ip <addr>, moments, pairs, quit")
|
||||
print("Nieznana komenda. Uzyj: top, ip <addr>, moments, pairs, ports, quit")
|
||||
|
||||
|
||||
def parse_args():
|
||||
@@ -793,6 +843,9 @@ def main():
|
||||
"per_ip_out": stats["per_ip_out"],
|
||||
"per_pair": stats["per_pair"],
|
||||
"per_proto": stats["per_proto"],
|
||||
"per_port_total": stats["per_port_total"],
|
||||
"per_port_src": stats["per_port_src"],
|
||||
"per_port_dst": stats["per_port_dst"],
|
||||
"interesting": [
|
||||
{
|
||||
"start_ts": x.start_ts,
|
||||
@@ -801,6 +854,7 @@ def main():
|
||||
"packets_total": x.packets_total,
|
||||
"top_ips": x.top_ips,
|
||||
"top_pairs": x.top_pairs,
|
||||
"top_ports": x.top_ports,
|
||||
}
|
||||
for x in stats["interesting"]
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user