network-core 0.5.2__tar.gz → 0.5.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {network_core-0.5.2 → network_core-0.5.4}/PKG-INFO +1 -1
- {network_core-0.5.2 → network_core-0.5.4}/network_core/__init__.py +1 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/analysis.py +6 -3
- network_core-0.5.4/network_core/bin/process_pcap +0 -0
- network_core-0.5.4/network_core/pcap.py +99 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core.egg-info/PKG-INFO +1 -1
- {network_core-0.5.2 → network_core-0.5.4}/network_core.egg-info/SOURCES.txt +2 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core.egg-info/top_level.txt +1 -0
- {network_core-0.5.2 → network_core-0.5.4}/pyproject.toml +5 -2
- {network_core-0.5.2 → network_core-0.5.4}/network_core/conn.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/connOps.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/dataModels.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/http/__init__.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/http/httpExtract/__init__.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/http/httpExtract/helpers.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/http/httpExtract/parser.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/http/httpExtract/pdh.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/http/httpExtract/pdml_parser.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/http/httpIO.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/sni/__init__.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/sni/clientHello.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/sni/constants.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/sni/crypto.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/sni/helpers.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/sni/parsers.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/utils/__init__.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/utils/csvIO.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/utils/dt.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/utils/jsonIO.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core/utils/pcapIO.py +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core.egg-info/dependency_links.txt +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/network_core.egg-info/requires.txt +0 -0
- {network_core-0.5.2 → network_core-0.5.4}/setup.cfg +0 -0
|
@@ -300,8 +300,11 @@ def conn_features(
|
|
|
300
300
|
|
|
301
301
|
ub = _burst_stats(lengths, timestamps, directions, direction=0, gap_ms=500)
|
|
302
302
|
|
|
303
|
-
|
|
304
|
-
|
|
303
|
+
# Median size (bytes) of upload packets < 200 B — captures the ACK/header
|
|
304
|
+
# packet baseline. Agents send large bursts separated by pure ACK traffic
|
|
305
|
+
# (66 B), while chat sessions mix in more small-but-nonzero request packets.
|
|
306
|
+
small_ups = up_len[up_len < 200]
|
|
307
|
+
up_small_pkt_med = float(np.median(small_ups)) if len(small_ups) >= 3 else float("nan")
|
|
305
308
|
|
|
306
309
|
pattern = _classify_pattern(
|
|
307
310
|
n_pkts=n_pkts,
|
|
@@ -378,7 +381,7 @@ def conn_features(
|
|
|
378
381
|
"up_interburst_cv": ub["interburst_cv"],
|
|
379
382
|
"up_interburst_p95_ms": ub["interburst_p95_ms"],
|
|
380
383
|
"ttfb_ms": ttfb_ms,
|
|
381
|
-
"
|
|
384
|
+
"up_small_pkt_med": up_small_pkt_med,
|
|
382
385
|
"traffic_pattern": pattern,
|
|
383
386
|
}
|
|
384
387
|
|
|
Binary file
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Wrapper around the bundled process_pcap Go binary."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import signal
|
|
5
|
+
import subprocess
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
_BIN = Path(__file__).parent / "bin" / "process_pcap"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _ensure_binary() -> Path:
|
|
12
|
+
if not _BIN.exists():
|
|
13
|
+
raise RuntimeError(
|
|
14
|
+
f"process_pcap binary not found at {_BIN}. "
|
|
15
|
+
"Run `make build` in the network_core repo root."
|
|
16
|
+
)
|
|
17
|
+
return _BIN
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def process_pcap(pcap_path: str, flows_path: str, packets_path: str) -> None:
|
|
21
|
+
"""Run the Go flow extractor on a single pcap file.
|
|
22
|
+
|
|
23
|
+
Writes {flows_path} and {packets_path} as flat CSVs.
|
|
24
|
+
Removes stale outputs before running so partial prior runs never linger.
|
|
25
|
+
Blocks until the Go process exits.
|
|
26
|
+
"""
|
|
27
|
+
pcap = Path(pcap_path)
|
|
28
|
+
if not pcap.exists():
|
|
29
|
+
raise FileNotFoundError(f"pcap not found: {pcap_path}")
|
|
30
|
+
if pcap.suffix not in {".pcap", ".pcapng"}:
|
|
31
|
+
raise ValueError(f"Expected .pcap or .pcapng, got: {pcap.name}")
|
|
32
|
+
|
|
33
|
+
Path(flows_path).unlink(missing_ok=True)
|
|
34
|
+
Path(packets_path).unlink(missing_ok=True)
|
|
35
|
+
|
|
36
|
+
result = subprocess.run(
|
|
37
|
+
[str(_ensure_binary()), str(pcap), flows_path, packets_path],
|
|
38
|
+
capture_output=True,
|
|
39
|
+
text=True,
|
|
40
|
+
)
|
|
41
|
+
if result.returncode != 0:
|
|
42
|
+
raise RuntimeError(
|
|
43
|
+
f"process_pcap failed (exit {result.returncode}):\n{result.stderr.strip()}"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _derive_output_paths(pcap_path: str) -> tuple[str, str]:
|
|
48
|
+
"""Return (flows_path, packets_path) derived from a pcap file path."""
|
|
49
|
+
p = Path(pcap_path)
|
|
50
|
+
stem = p.stem
|
|
51
|
+
return str(p.parent / f"{stem}Flows.csv"), str(p.parent / f"{stem}Packets.csv")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def process_pcap_auto(pcap_path: str) -> tuple[str, str]:
|
|
55
|
+
"""Run process_pcap with output paths derived from the pcap filename.
|
|
56
|
+
|
|
57
|
+
Returns (flows_path, packets_path).
|
|
58
|
+
"""
|
|
59
|
+
flows_path, packets_path = _derive_output_paths(pcap_path)
|
|
60
|
+
process_pcap(pcap_path, flows_path, packets_path)
|
|
61
|
+
return flows_path, packets_path
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _iter_pcaps(root: str):
|
|
65
|
+
"""Yield (pcap_path, flows_path, packets_path) for every pcap under root."""
|
|
66
|
+
p = Path(root)
|
|
67
|
+
if p.is_dir():
|
|
68
|
+
for child in p.iterdir():
|
|
69
|
+
yield from _iter_pcaps(str(child))
|
|
70
|
+
elif p.suffix in {".pcap", ".pcapng"}:
|
|
71
|
+
flows, packets = _derive_output_paths(str(p))
|
|
72
|
+
yield str(p.resolve()), str(Path(flows).resolve()), str(Path(packets).resolve())
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def process_pcap_tree(root: str, force: bool = False) -> None:
|
|
76
|
+
"""Run process_pcap on every pcap file found recursively under root.
|
|
77
|
+
|
|
78
|
+
Skips files whose output CSVs already exist unless force=True.
|
|
79
|
+
Runs in a new process group so Ctrl-C cleanly kills the subprocess.
|
|
80
|
+
"""
|
|
81
|
+
for pcap_path, flows_path, packets_path in _iter_pcaps(root):
|
|
82
|
+
if not force and Path(flows_path).exists() and Path(packets_path).exists():
|
|
83
|
+
print(f"Skipping {Path(pcap_path).name} (outputs already exist)")
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
Path(flows_path).unlink(missing_ok=True)
|
|
87
|
+
Path(packets_path).unlink(missing_ok=True)
|
|
88
|
+
|
|
89
|
+
proc = subprocess.Popen(
|
|
90
|
+
[str(_ensure_binary()), pcap_path, flows_path, packets_path],
|
|
91
|
+
preexec_fn=os.setsid,
|
|
92
|
+
)
|
|
93
|
+
try:
|
|
94
|
+
proc.wait()
|
|
95
|
+
except KeyboardInterrupt:
|
|
96
|
+
os.killpg(proc.pid, signal.SIGTERM)
|
|
97
|
+
raise
|
|
98
|
+
if proc.returncode != 0:
|
|
99
|
+
raise RuntimeError(f"process_pcap failed (exit {proc.returncode}) on {pcap_path}")
|
|
@@ -4,11 +4,13 @@ network_core/analysis.py
|
|
|
4
4
|
network_core/conn.py
|
|
5
5
|
network_core/connOps.py
|
|
6
6
|
network_core/dataModels.py
|
|
7
|
+
network_core/pcap.py
|
|
7
8
|
network_core.egg-info/PKG-INFO
|
|
8
9
|
network_core.egg-info/SOURCES.txt
|
|
9
10
|
network_core.egg-info/dependency_links.txt
|
|
10
11
|
network_core.egg-info/requires.txt
|
|
11
12
|
network_core.egg-info/top_level.txt
|
|
13
|
+
network_core/bin/process_pcap
|
|
12
14
|
network_core/http/__init__.py
|
|
13
15
|
network_core/http/httpIO.py
|
|
14
16
|
network_core/http/httpExtract/__init__.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "network_core"
|
|
3
|
-
version = "0.5.
|
|
3
|
+
version = "0.5.4"
|
|
4
4
|
description = "Core networking utilities and data models"
|
|
5
5
|
authors = [{ name = "Your Name" }]
|
|
6
6
|
readme = "README.md"
|
|
@@ -22,4 +22,7 @@ requires = ["setuptools>=61.0"]
|
|
|
22
22
|
build-backend = "setuptools.build_meta"
|
|
23
23
|
|
|
24
24
|
[tool.setuptools.packages.find]
|
|
25
|
-
where = ["."]
|
|
25
|
+
where = ["."]
|
|
26
|
+
|
|
27
|
+
[tool.setuptools.package-data]
|
|
28
|
+
network_core = ["bin/process_pcap"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|