tunnelscope 0.2.2__tar.gz → 0.4.0__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.
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/MANIFEST.in +1 -0
- {tunnelscope-0.2.2/tunnelscope.egg-info → tunnelscope-0.4.0}/PKG-INFO +74 -22
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/README.md +73 -21
- tunnelscope-0.4.0/docs/environments.md +67 -0
- tunnelscope-0.4.0/docs/images/report-ui.png +0 -0
- tunnelscope-0.4.0/docs/ipv6.md +37 -0
- tunnelscope-0.4.0/docs/releases/v0.3.0.md +25 -0
- tunnelscope-0.4.0/docs/releases/v0.4.0.md +22 -0
- tunnelscope-0.4.0/docs/report-ui.md +22 -0
- tunnelscope-0.4.0/docs/size-sweep.md +29 -0
- tunnelscope-0.4.0/docs/validation/two-vm-size-sweep.md +44 -0
- tunnelscope-0.4.0/labs/ipv6_boundary.py +102 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/labs/mtu_boundary.py +10 -0
- tunnelscope-0.4.0/labs/vxlan_boundary.py +108 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/pyproject.toml +6 -2
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/tests/test_delivery.py +133 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/tests/test_fragmentation.py +48 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0/tunnelscope.egg-info}/PKG-INFO +74 -22
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/tunnelscope.egg-info/SOURCES.txt +14 -1
- tunnelscope-0.4.0/tunnelscope.egg-info/top_level.txt +3 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/tunnelscope.py +133 -42
- tunnelscope-0.4.0/tunnelscope_assets/__init__.py +1 -0
- tunnelscope-0.4.0/tunnelscope_assets/tunnelscope-logo-blue.png +0 -0
- tunnelscope-0.4.0/tunnelscope_view.py +145 -0
- tunnelscope-0.2.2/tunnelscope.egg-info/top_level.txt +0 -1
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/LICENSE +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/docs/bidirectional.md +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/docs/demo.md +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/docs/images/two-vm-results.png +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/docs/receiver-observations.md +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/docs/releases/v0.2.1.md +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/docs/releases/v0.2.2.md +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/docs/v0.1-scope.md +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/docs/validation/ack-return-blocked.md +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/docs/validation/mtu-boundary.md +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/docs/validation/pmtu-blackhole.md +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/docs/validation/two-vm-field-test.md +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/docs/validation/two-vm-receiver-correlation.md +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/examples/mtu-boundary.json +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/examples/pmtu-blackhole.json +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/labs/ack_return_blocked.py +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/labs/pmtu_blackhole.py +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/setup.cfg +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/tunnelscope-logo-blue.png +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/tunnelscope.egg-info/dependency_links.txt +0 -0
- {tunnelscope-0.2.2 → tunnelscope-0.4.0}/tunnelscope.egg-info/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tunnelscope
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Evidence-based MTU and packet-size diagnostics for Linux networks
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/lazypanic999/tunnelscope
|
|
@@ -22,7 +22,48 @@ Dynamic: license-file
|
|
|
22
22
|
|
|
23
23
|
Evidence-based MTU and packet-size diagnostics for Linux networks.
|
|
24
24
|
|
|
25
|
-
**Status: v0.
|
|
25
|
+
**Status: v0.4.0 alpha. IPv4/IPv6 UDP delivery probes, Linux no-fragment mode, per-direction report combination, optional receiver observations and controlled MTU labs are available. Automatic path-MTU discovery and root-cause diagnosis are not implemented.**
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
Requires Python 3.10 or newer. In an empty working directory on Linux (or
|
|
30
|
+
WSL2), install the published package in a virtual environment:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
python3 -m venv .venv
|
|
34
|
+
source .venv/bin/activate
|
|
35
|
+
python -m pip install tunnelscope
|
|
36
|
+
tunnelscope --help
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Open a second terminal and activate the same environment there with
|
|
40
|
+
`source .venv/bin/activate`. Run these commands in order on one machine; no
|
|
41
|
+
root access or network setup is needed for this loopback example.
|
|
42
|
+
|
|
43
|
+
Terminal 1 (receiver):
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
tunnelscope receive --max-packets 2 --output receiver.json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Wait for `Listening on 127.0.0.1:9471`, then run Terminal 2 (sender):
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
tunnelscope probe 127.0.0.1 --sizes 64,1200 --count 1 --output probe.json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The receiver exits after the two probes and saves `receiver.json`. Back in
|
|
56
|
+
either terminal, inspect the sender report and create a local HTML view:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
tunnelscope show probe.json
|
|
60
|
+
tunnelscope html probe.json --output probe.html
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Open `probe.html` in a browser. The receiver sends small acknowledgements, so
|
|
64
|
+
this example checks delivery in one direction only. A timeout is inconclusive;
|
|
65
|
+
it does not prove an MTU problem. For two machines, see the
|
|
66
|
+
[two-endpoint workflow](https://github.com/lazypanic999/tunnelscope/blob/main/docs/bidirectional.md).
|
|
26
67
|
|
|
27
68
|
## The problem
|
|
28
69
|
|
|
@@ -49,7 +90,7 @@ A failed probe alone will not be labeled as proof of an MTU problem. Reports sep
|
|
|
49
90
|
- [x] **M4 - Observe at the receiver:** record which identified probes reached the cooperating receiver and correlate them with sender outcomes.
|
|
50
91
|
- [x] **M5 - Make evidence usable:** validate receiver-observed timeouts in a controlled lab and provide a concise terminal view of reports.
|
|
51
92
|
|
|
52
|
-
|
|
93
|
+
The [tunnel and pod workflows](https://github.com/lazypanic999/tunnelscope/blob/main/docs/environments.md) include a validated local VXLAN overlay and guidance for existing VPNs and Kubernetes pods. The VPN and pod workflows have not yet been validated on a real deployment. 5G user-plane tunnels remain outside the current scope.
|
|
53
94
|
|
|
54
95
|
For a quick walkthrough of both controlled failure modes, see the [two-minute Linux demo](https://github.com/lazypanic999/tunnelscope/blob/main/docs/demo.md).
|
|
55
96
|
The [two-VM validation record](https://github.com/lazypanic999/tunnelscope/blob/main/docs/validation/two-vm-field-test.md) documents a successful bidirectional DF run between Ubuntu and Debian guests.
|
|
@@ -62,7 +103,16 @@ Use `tunnelscope show report.json` for a short terminal table of a probe,
|
|
|
62
103
|
receiver observation, bidirectional comparison, correlation, or lab report.
|
|
63
104
|
JSON remains the machine-readable source of evidence.
|
|
64
105
|
|
|
65
|
-
|
|
106
|
+
The CLI can also create a [self-contained HTML report view](https://github.com/lazypanic999/tunnelscope/blob/main/docs/report-ui.md)
|
|
107
|
+
that works offline:
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
tunnelscope html report.json --output report.html
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+

|
|
114
|
+
|
|
115
|
+
## Install from a source checkout
|
|
66
116
|
|
|
67
117
|
From the repository root, in a Python 3.10+ virtual environment:
|
|
68
118
|
|
|
@@ -71,29 +121,31 @@ python3 -m pip install .
|
|
|
71
121
|
tunnelscope --help
|
|
72
122
|
```
|
|
73
123
|
|
|
74
|
-
The package installs the `tunnelscope` command. It has no third-party runtime
|
|
124
|
+
The package installs the `tunnelscope` command. It has no third-party runtime
|
|
125
|
+
dependencies. The laboratory scripts still run from a source checkout.
|
|
75
126
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
Terminal 1 (receiver):
|
|
127
|
+
The CLI also supports `--sweep START:STOP:STEP` for a bounded
|
|
128
|
+
grid of UDP payload sizes. It includes the stop size even when the step does
|
|
129
|
+
not land on it. For example:
|
|
81
130
|
|
|
82
131
|
```sh
|
|
83
|
-
|
|
132
|
+
tunnelscope probe 192.0.2.2 --sweep 64:1400:100 --count 2 --dont-fragment --output sweep.json
|
|
84
133
|
```
|
|
85
134
|
|
|
86
|
-
|
|
135
|
+
The grid tests at most 16 sizes. The result records observations at those
|
|
136
|
+
sizes; it is not an exact path-MTU measurement. See the [size sweep guide](https://github.com/lazypanic999/tunnelscope/blob/main/docs/size-sweep.md).
|
|
87
137
|
|
|
88
|
-
|
|
89
|
-
python tunnelscope.py probe 127.0.0.1 --sizes 64,512,1200,1400 --count 3 --output report.json
|
|
90
|
-
```
|
|
138
|
+
## Probe and report details
|
|
91
139
|
|
|
92
|
-
|
|
140
|
+
No third-party runtime packages are needed. The commands below also work from
|
|
141
|
+
a source checkout by replacing `tunnelscope` with `python tunnelscope.py`.
|
|
142
|
+
By default, the receiver exits after 100 datagrams or 30 seconds without a
|
|
143
|
+
datagram. Restart it if it has already exited. `--max-packets` and
|
|
144
|
+
`--idle-timeout` change these limits.
|
|
93
145
|
|
|
94
146
|
Probe sizes are **UDP payload bytes**, including TunnelScope's 24-byte header. They exclude IP and UDP headers. The receiver sends only a small acknowledgement, so this is not a same-sized round-trip test. Reverse endpoint roles to investigate delivery in the other direction.
|
|
95
147
|
|
|
96
|
-
Each probe report includes a `source` snapshot taken from the first connected sender socket. It records the local IPv4 address. On Linux, it also records the selected interface and its link MTU when discoverable, plus `kernel_route_mtu_bytes` when the kernel supplies it. The link MTU and kernel route MTU are different facts; neither is an independently measured end-to-end path MTU. A failed connection may leave `source` as `null`, and optional fields may be absent.
|
|
148
|
+
Each probe report includes a `source` snapshot taken from the first connected sender socket. It records the local IPv4 or IPv6 address. On Linux, it also records the selected interface and its link MTU when discoverable, plus `kernel_route_mtu_bytes` when the kernel supplies it. The link MTU and kernel route MTU are different facts; neither is an independently measured end-to-end path MTU. A failed connection may leave `source` as `null`, and optional fields may be absent.
|
|
97
149
|
|
|
98
150
|
`acknowledged` means that the receiver got that payload and its acknowledgement returned. `timeout` means no matching acknowledgement arrived before the deadline; it does not prove an MTU issue. `socket_error` records a socket error. Without `--dont-fragment`, fragmentation follows OS defaults; the tool does not force it on or off. The CLI exits successfully when it produces a report; inspect per-probe statuses for delivery outcomes.
|
|
99
151
|
|
|
@@ -104,18 +156,18 @@ The `evidence` section lists counted observations, tentative hypotheses and miss
|
|
|
104
156
|
To compare directions, run `probe` once from A to B and once from B to A, then combine the two JSON files:
|
|
105
157
|
|
|
106
158
|
```sh
|
|
107
|
-
|
|
159
|
+
tunnelscope combine a-to-b.json b-to-a.json --output bidirectional.json
|
|
108
160
|
```
|
|
109
161
|
|
|
110
162
|
The command preserves the two reports and aligns per-size assessments; it does not initiate a remote measurement. See the [two-endpoint workflow](https://github.com/lazypanic999/tunnelscope/blob/main/docs/bidirectional.md) for the receiver and sender steps. `endpoint_check` compares the recorded IPv4 addresses but cannot authenticate endpoints or account for address translation.
|
|
111
163
|
|
|
112
|
-
### Linux
|
|
164
|
+
### Linux no-fragment mode
|
|
113
165
|
|
|
114
166
|
```sh
|
|
115
|
-
|
|
167
|
+
tunnelscope probe 192.0.2.2 --sizes 64,1252,1253,1400 --count 1 --dont-fragment --output report.json
|
|
116
168
|
```
|
|
117
169
|
|
|
118
|
-
Use the address of your lab receiver; `192.0.2.2` is an example.
|
|
170
|
+
Use the address of your lab receiver; `192.0.2.2` is an example. For IPv4, this mode sets `IP_MTU_DISCOVER=IP_PMTUDISC_DO` on each sender socket. The kernel sets DF and rejects datagrams above its currently known path MTU. For IPv6, it sets Linux `IPV6_DONTFRAG` to prevent source fragmentation. It does not bypass cached PMTU information or search automatically for a boundary. The flag requires Linux, including WSL2. Other platforms reject it; socket-option errors stop the experiment rather than silently reverting to defaults. See the [IPv6 workflow](https://github.com/lazypanic999/tunnelscope/blob/main/docs/ipv6.md).
|
|
119
171
|
|
|
120
172
|
In DF mode, `EMSGSIZE` is reported as `packet_too_large`, with its numeric `errno`. When available, `kernel_reported_mtu_bytes` contains the connected socket's `IP_MTU` value. That value is kernel knowledge, not an independently measured end-to-end MTU. Timeouts remain inconclusive. The JSON schema version remains 1 with additive fields and the new `packet_too_large` status; consumers must handle it explicitly.
|
|
121
173
|
|
|
@@ -143,7 +195,7 @@ This lab creates a sender, router and receiver in temporary network namespaces.
|
|
|
143
195
|
|
|
144
196
|
The known lab topology explains these timeouts; a timeout on an unknown network remains inconclusive. See the [validation record](https://github.com/lazypanic999/tunnelscope/blob/main/docs/validation/pmtu-blackhole.md) and [captured report](https://github.com/lazypanic999/tunnelscope/blob/main/examples/pmtu-blackhole.json). This lab demonstrates one controlled failure mode; it does not establish automatic black-hole detection.
|
|
145
197
|
|
|
146
|
-
The receiver binds to loopback by default. For your own two-host lab, use `--bind <receiver-lab-
|
|
198
|
+
The receiver binds to IPv4 loopback by default. For your own two-host lab, use `--bind <receiver-lab-address>` and probe that address. An IPv6 literal such as `::1` selects IPv6; `--family ipv6` selects an AAAA record for a hostname. The experimental protocol is unencrypted and unauthenticated; use a controlled lab, not a public service.
|
|
147
199
|
|
|
148
200
|
Run checks:
|
|
149
201
|
|
|
@@ -6,7 +6,48 @@
|
|
|
6
6
|
|
|
7
7
|
Evidence-based MTU and packet-size diagnostics for Linux networks.
|
|
8
8
|
|
|
9
|
-
**Status: v0.
|
|
9
|
+
**Status: v0.4.0 alpha. IPv4/IPv6 UDP delivery probes, Linux no-fragment mode, per-direction report combination, optional receiver observations and controlled MTU labs are available. Automatic path-MTU discovery and root-cause diagnosis are not implemented.**
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
Requires Python 3.10 or newer. In an empty working directory on Linux (or
|
|
14
|
+
WSL2), install the published package in a virtual environment:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
python3 -m venv .venv
|
|
18
|
+
source .venv/bin/activate
|
|
19
|
+
python -m pip install tunnelscope
|
|
20
|
+
tunnelscope --help
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Open a second terminal and activate the same environment there with
|
|
24
|
+
`source .venv/bin/activate`. Run these commands in order on one machine; no
|
|
25
|
+
root access or network setup is needed for this loopback example.
|
|
26
|
+
|
|
27
|
+
Terminal 1 (receiver):
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
tunnelscope receive --max-packets 2 --output receiver.json
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Wait for `Listening on 127.0.0.1:9471`, then run Terminal 2 (sender):
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
tunnelscope probe 127.0.0.1 --sizes 64,1200 --count 1 --output probe.json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The receiver exits after the two probes and saves `receiver.json`. Back in
|
|
40
|
+
either terminal, inspect the sender report and create a local HTML view:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
tunnelscope show probe.json
|
|
44
|
+
tunnelscope html probe.json --output probe.html
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Open `probe.html` in a browser. The receiver sends small acknowledgements, so
|
|
48
|
+
this example checks delivery in one direction only. A timeout is inconclusive;
|
|
49
|
+
it does not prove an MTU problem. For two machines, see the
|
|
50
|
+
[two-endpoint workflow](https://github.com/lazypanic999/tunnelscope/blob/main/docs/bidirectional.md).
|
|
10
51
|
|
|
11
52
|
## The problem
|
|
12
53
|
|
|
@@ -33,7 +74,7 @@ A failed probe alone will not be labeled as proof of an MTU problem. Reports sep
|
|
|
33
74
|
- [x] **M4 - Observe at the receiver:** record which identified probes reached the cooperating receiver and correlate them with sender outcomes.
|
|
34
75
|
- [x] **M5 - Make evidence usable:** validate receiver-observed timeouts in a controlled lab and provide a concise terminal view of reports.
|
|
35
76
|
|
|
36
|
-
|
|
77
|
+
The [tunnel and pod workflows](https://github.com/lazypanic999/tunnelscope/blob/main/docs/environments.md) include a validated local VXLAN overlay and guidance for existing VPNs and Kubernetes pods. The VPN and pod workflows have not yet been validated on a real deployment. 5G user-plane tunnels remain outside the current scope.
|
|
37
78
|
|
|
38
79
|
For a quick walkthrough of both controlled failure modes, see the [two-minute Linux demo](https://github.com/lazypanic999/tunnelscope/blob/main/docs/demo.md).
|
|
39
80
|
The [two-VM validation record](https://github.com/lazypanic999/tunnelscope/blob/main/docs/validation/two-vm-field-test.md) documents a successful bidirectional DF run between Ubuntu and Debian guests.
|
|
@@ -46,7 +87,16 @@ Use `tunnelscope show report.json` for a short terminal table of a probe,
|
|
|
46
87
|
receiver observation, bidirectional comparison, correlation, or lab report.
|
|
47
88
|
JSON remains the machine-readable source of evidence.
|
|
48
89
|
|
|
49
|
-
|
|
90
|
+
The CLI can also create a [self-contained HTML report view](https://github.com/lazypanic999/tunnelscope/blob/main/docs/report-ui.md)
|
|
91
|
+
that works offline:
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
tunnelscope html report.json --output report.html
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+

|
|
98
|
+
|
|
99
|
+
## Install from a source checkout
|
|
50
100
|
|
|
51
101
|
From the repository root, in a Python 3.10+ virtual environment:
|
|
52
102
|
|
|
@@ -55,29 +105,31 @@ python3 -m pip install .
|
|
|
55
105
|
tunnelscope --help
|
|
56
106
|
```
|
|
57
107
|
|
|
58
|
-
The package installs the `tunnelscope` command. It has no third-party runtime
|
|
108
|
+
The package installs the `tunnelscope` command. It has no third-party runtime
|
|
109
|
+
dependencies. The laboratory scripts still run from a source checkout.
|
|
59
110
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
Terminal 1 (receiver):
|
|
111
|
+
The CLI also supports `--sweep START:STOP:STEP` for a bounded
|
|
112
|
+
grid of UDP payload sizes. It includes the stop size even when the step does
|
|
113
|
+
not land on it. For example:
|
|
65
114
|
|
|
66
115
|
```sh
|
|
67
|
-
|
|
116
|
+
tunnelscope probe 192.0.2.2 --sweep 64:1400:100 --count 2 --dont-fragment --output sweep.json
|
|
68
117
|
```
|
|
69
118
|
|
|
70
|
-
|
|
119
|
+
The grid tests at most 16 sizes. The result records observations at those
|
|
120
|
+
sizes; it is not an exact path-MTU measurement. See the [size sweep guide](https://github.com/lazypanic999/tunnelscope/blob/main/docs/size-sweep.md).
|
|
71
121
|
|
|
72
|
-
|
|
73
|
-
python tunnelscope.py probe 127.0.0.1 --sizes 64,512,1200,1400 --count 3 --output report.json
|
|
74
|
-
```
|
|
122
|
+
## Probe and report details
|
|
75
123
|
|
|
76
|
-
|
|
124
|
+
No third-party runtime packages are needed. The commands below also work from
|
|
125
|
+
a source checkout by replacing `tunnelscope` with `python tunnelscope.py`.
|
|
126
|
+
By default, the receiver exits after 100 datagrams or 30 seconds without a
|
|
127
|
+
datagram. Restart it if it has already exited. `--max-packets` and
|
|
128
|
+
`--idle-timeout` change these limits.
|
|
77
129
|
|
|
78
130
|
Probe sizes are **UDP payload bytes**, including TunnelScope's 24-byte header. They exclude IP and UDP headers. The receiver sends only a small acknowledgement, so this is not a same-sized round-trip test. Reverse endpoint roles to investigate delivery in the other direction.
|
|
79
131
|
|
|
80
|
-
Each probe report includes a `source` snapshot taken from the first connected sender socket. It records the local IPv4 address. On Linux, it also records the selected interface and its link MTU when discoverable, plus `kernel_route_mtu_bytes` when the kernel supplies it. The link MTU and kernel route MTU are different facts; neither is an independently measured end-to-end path MTU. A failed connection may leave `source` as `null`, and optional fields may be absent.
|
|
132
|
+
Each probe report includes a `source` snapshot taken from the first connected sender socket. It records the local IPv4 or IPv6 address. On Linux, it also records the selected interface and its link MTU when discoverable, plus `kernel_route_mtu_bytes` when the kernel supplies it. The link MTU and kernel route MTU are different facts; neither is an independently measured end-to-end path MTU. A failed connection may leave `source` as `null`, and optional fields may be absent.
|
|
81
133
|
|
|
82
134
|
`acknowledged` means that the receiver got that payload and its acknowledgement returned. `timeout` means no matching acknowledgement arrived before the deadline; it does not prove an MTU issue. `socket_error` records a socket error. Without `--dont-fragment`, fragmentation follows OS defaults; the tool does not force it on or off. The CLI exits successfully when it produces a report; inspect per-probe statuses for delivery outcomes.
|
|
83
135
|
|
|
@@ -88,18 +140,18 @@ The `evidence` section lists counted observations, tentative hypotheses and miss
|
|
|
88
140
|
To compare directions, run `probe` once from A to B and once from B to A, then combine the two JSON files:
|
|
89
141
|
|
|
90
142
|
```sh
|
|
91
|
-
|
|
143
|
+
tunnelscope combine a-to-b.json b-to-a.json --output bidirectional.json
|
|
92
144
|
```
|
|
93
145
|
|
|
94
146
|
The command preserves the two reports and aligns per-size assessments; it does not initiate a remote measurement. See the [two-endpoint workflow](https://github.com/lazypanic999/tunnelscope/blob/main/docs/bidirectional.md) for the receiver and sender steps. `endpoint_check` compares the recorded IPv4 addresses but cannot authenticate endpoints or account for address translation.
|
|
95
147
|
|
|
96
|
-
### Linux
|
|
148
|
+
### Linux no-fragment mode
|
|
97
149
|
|
|
98
150
|
```sh
|
|
99
|
-
|
|
151
|
+
tunnelscope probe 192.0.2.2 --sizes 64,1252,1253,1400 --count 1 --dont-fragment --output report.json
|
|
100
152
|
```
|
|
101
153
|
|
|
102
|
-
Use the address of your lab receiver; `192.0.2.2` is an example.
|
|
154
|
+
Use the address of your lab receiver; `192.0.2.2` is an example. For IPv4, this mode sets `IP_MTU_DISCOVER=IP_PMTUDISC_DO` on each sender socket. The kernel sets DF and rejects datagrams above its currently known path MTU. For IPv6, it sets Linux `IPV6_DONTFRAG` to prevent source fragmentation. It does not bypass cached PMTU information or search automatically for a boundary. The flag requires Linux, including WSL2. Other platforms reject it; socket-option errors stop the experiment rather than silently reverting to defaults. See the [IPv6 workflow](https://github.com/lazypanic999/tunnelscope/blob/main/docs/ipv6.md).
|
|
103
155
|
|
|
104
156
|
In DF mode, `EMSGSIZE` is reported as `packet_too_large`, with its numeric `errno`. When available, `kernel_reported_mtu_bytes` contains the connected socket's `IP_MTU` value. That value is kernel knowledge, not an independently measured end-to-end MTU. Timeouts remain inconclusive. The JSON schema version remains 1 with additive fields and the new `packet_too_large` status; consumers must handle it explicitly.
|
|
105
157
|
|
|
@@ -127,7 +179,7 @@ This lab creates a sender, router and receiver in temporary network namespaces.
|
|
|
127
179
|
|
|
128
180
|
The known lab topology explains these timeouts; a timeout on an unknown network remains inconclusive. See the [validation record](https://github.com/lazypanic999/tunnelscope/blob/main/docs/validation/pmtu-blackhole.md) and [captured report](https://github.com/lazypanic999/tunnelscope/blob/main/examples/pmtu-blackhole.json). This lab demonstrates one controlled failure mode; it does not establish automatic black-hole detection.
|
|
129
181
|
|
|
130
|
-
The receiver binds to loopback by default. For your own two-host lab, use `--bind <receiver-lab-
|
|
182
|
+
The receiver binds to IPv4 loopback by default. For your own two-host lab, use `--bind <receiver-lab-address>` and probe that address. An IPv6 literal such as `::1` selects IPv6; `--family ipv6` selects an AAAA record for a hostname. The experimental protocol is unencrypted and unauthenticated; use a controlled lab, not a public service.
|
|
131
183
|
|
|
132
184
|
Run checks:
|
|
133
185
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Controlled tunnel and pod workflows
|
|
2
|
+
|
|
3
|
+
TunnelScope sends ordinary UDP payloads between a cooperating sender and
|
|
4
|
+
receiver. The tool does not configure a VPN, container network, Kubernetes
|
|
5
|
+
cluster or network policy. Run it only where both endpoints and the UDP path
|
|
6
|
+
are under your control. Use the tunnel or pod address as the destination, and
|
|
7
|
+
measure each direction separately.
|
|
8
|
+
|
|
9
|
+
## VXLAN overlay lab
|
|
10
|
+
|
|
11
|
+
On Linux with root and `iproute2`:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
sudo python3 labs/vxlan_boundary.py > vxlan-boundary.json
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The lab creates two temporary network namespaces. Their 1500-byte underlay
|
|
18
|
+
carries a VXLAN overlay with a 1450-byte interface MTU. In IPv4 DF mode, the
|
|
19
|
+
known maximum UDP payload on that overlay is 1422 bytes (1450 minus 20-byte
|
|
20
|
+
IPv4 and 8-byte UDP headers). The lab checks 1418–1426 bytes in both
|
|
21
|
+
directions, correlates receiver observations and removes its namespaces on
|
|
22
|
+
ordinary completion. It changes no host interface or route.
|
|
23
|
+
|
|
24
|
+
This validates an encapsulated virtual path, not a real VPN or a Kubernetes
|
|
25
|
+
cluster. A measured local tunnel-interface MTU and a kernel route MTU remain
|
|
26
|
+
different from independent proof of an unknown end-to-end path MTU.
|
|
27
|
+
|
|
28
|
+
## Existing VPN or tunnel
|
|
29
|
+
|
|
30
|
+
Use the addresses assigned inside the tunnel. Start the receiver on endpoint
|
|
31
|
+
B, then probe from endpoint A:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
tunnelscope receive --bind B_TUNNEL_IP --output b-receiver.json
|
|
35
|
+
tunnelscope probe B_TUNNEL_IP --sweep 1200:1400:25 --count 2 --dont-fragment --output a-to-b.json
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Run the commands in separate terminals. Repeat with A and B swapped, then use
|
|
39
|
+
`combine` for direction comparison and `correlate` with each receiver report.
|
|
40
|
+
Check the selected `source.interface` and its MTU in the probe report. If the
|
|
41
|
+
route unexpectedly uses a non-tunnel interface, the experiment did not measure
|
|
42
|
+
the intended VPN path. Use `--family ipv6` for an IPv6 hostname or an IPv6
|
|
43
|
+
literal address. Timeouts remain inconclusive without receiver or path
|
|
44
|
+
evidence.
|
|
45
|
+
|
|
46
|
+
## Existing Kubernetes pods
|
|
47
|
+
|
|
48
|
+
If two controlled pods already contain TunnelScope v0.3.0 and Python 3.10+,
|
|
49
|
+
use their pod addresses directly. In separate local terminals, for example:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
kubectl exec -n NAMESPACE POD_B -- tunnelscope receive --bind POD_B_IP --output /tmp/receiver.json
|
|
53
|
+
kubectl exec -n NAMESPACE POD_A -- tunnelscope probe POD_B_IP --sizes 64,1200,1400 --count 2 --dont-fragment --output /tmp/probe.json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Collect the JSON files from the pods and correlate them. Repeat with the roles
|
|
57
|
+
reversed. A pod may have more than one container; add `-c CONTAINER` to
|
|
58
|
+
`kubectl exec` when needed. Ensure that the intended UDP port (default 9471)
|
|
59
|
+
and the small ACK return traffic are allowed by the cluster's network policy.
|
|
60
|
+
Kubernetes [pod networking](https://kubernetes.io/docs/concepts/services-networking/)
|
|
61
|
+
and [NetworkPolicy](https://kubernetes.io/docs/concepts/services-networking/network-policies/)
|
|
62
|
+
are implemented by the cluster's network plugin; behavior can differ between
|
|
63
|
+
clusters. See the official [`kubectl exec` reference](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_exec/).
|
|
64
|
+
|
|
65
|
+
This pod recipe is untested here because no Kubernetes cluster was available.
|
|
66
|
+
It does not claim cluster-wide MTU discovery, support for every CNI, or the
|
|
67
|
+
ability to distinguish overlay loss from filtering on its own.
|
|
Binary file
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# IPv6 delivery experiments
|
|
2
|
+
|
|
3
|
+
From v0.3.0, the CLI supports IPv6 UDP payload probes and receiver observations.
|
|
4
|
+
Use an IPv6 literal for `probe` or `receive --bind`; the default receiver still
|
|
5
|
+
binds to IPv4 loopback. For a hostname, `--family ipv6` selects an AAAA record.
|
|
6
|
+
The default hostname behavior continues to prefer IPv4 for compatibility.
|
|
7
|
+
|
|
8
|
+
On one machine, start the receiver in one terminal:
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
tunnelscope receive --bind ::1 --output receiver-v6.json
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
In another terminal:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
tunnelscope probe ::1 --sizes 64,1200,1400 --count 2 --dont-fragment --output probe-v6.json
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
For a two-host test, use the receiver's reachable IPv6 address and repeat the
|
|
21
|
+
procedure with endpoint roles reversed. Link-local addresses need a zone
|
|
22
|
+
identifier such as `%eth0` on the sender. Reports use `source.ipv6`,
|
|
23
|
+
`target.ipv6`, `receiver.bind_ipv6` and `source_ipv6` in receiver observations.
|
|
24
|
+
The existing report schema version remains 1 with additive IPv6 fields.
|
|
25
|
+
|
|
26
|
+
On Linux, `--dont-fragment` sets `IPV6_DONTFRAG` on the sender socket. An
|
|
27
|
+
`EMSGSIZE` error is recorded as `packet_too_large`; a reported `IPV6_MTU`
|
|
28
|
+
value is the kernel's current route knowledge, not a separately measured
|
|
29
|
+
end-to-end path MTU. Without the flag, source fragmentation follows OS
|
|
30
|
+
defaults. Timeouts remain inconclusive. The receiver's acknowledgement is
|
|
31
|
+
small, so reverse roles to test the opposite direction with the same payload
|
|
32
|
+
size.
|
|
33
|
+
|
|
34
|
+
The [isolated IPv6 boundary lab](../labs/ipv6_boundary.py) verifies a
|
|
35
|
+
1280-byte veth link: 1228–1232-byte UDP payloads arrive, while 1233–1236-byte
|
|
36
|
+
payloads are rejected locally in no-fragment mode. It creates and removes
|
|
37
|
+
temporary network namespaces and requires root and `iproute2`.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# TunnelScope v0.3.0
|
|
2
|
+
|
|
3
|
+
Alpha release of the Python CLI with more controlled network experiments and
|
|
4
|
+
a local report view.
|
|
5
|
+
|
|
6
|
+
## Highlights
|
|
7
|
+
|
|
8
|
+
- IPv6 UDP probes, cooperating receiver reports, run correlation and
|
|
9
|
+
bidirectional comparisons. Linux `--dont-fragment` uses `IPV6_DONTFRAG` for
|
|
10
|
+
IPv6 and retains IPv4 DF behavior.
|
|
11
|
+
- Bounded `--sweep START:STOP:STEP` payload-size grids (at most 16 sizes).
|
|
12
|
+
- Reproducible network namespace labs for a known IPv6 link boundary and a
|
|
13
|
+
VXLAN overlay boundary, both checked in each direction.
|
|
14
|
+
- `tunnelscope html report.json --output report.html` creates a searchable
|
|
15
|
+
local report view. The report content remains in the generated HTML.
|
|
16
|
+
- VPN and Kubernetes pod workflow guidance. Those two deployment types are
|
|
17
|
+
documented but have not been validated on a real deployment.
|
|
18
|
+
|
|
19
|
+
## Known limits
|
|
20
|
+
|
|
21
|
+
The tool does not discover an exact end-to-end path MTU or prove a root cause
|
|
22
|
+
on an unknown network. A timeout remains inconclusive without additional
|
|
23
|
+
receiver or path evidence. The UDP protocol is unauthenticated and unencrypted;
|
|
24
|
+
use it only in a controlled environment. The HTML view fetches the project
|
|
25
|
+
logo from GitHub when online, but does not send the report to a service.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# TunnelScope v0.4.0
|
|
2
|
+
|
|
3
|
+
Alpha release focused on easier first use and portable reports.
|
|
4
|
+
|
|
5
|
+
## Highlights
|
|
6
|
+
|
|
7
|
+
- Generated HTML reports embed the project logo and work without a network
|
|
8
|
+
connection or extra files. Search now shows the number of matching rows and
|
|
9
|
+
a message when nothing matches.
|
|
10
|
+
- The README has a short path from installation to a local probe and HTML
|
|
11
|
+
report.
|
|
12
|
+
- `tunnelscope --version` shows the installed CLI version.
|
|
13
|
+
- `html`, `combine` and `correlate` reject an output path that points to an
|
|
14
|
+
input report, preventing accidental overwrite.
|
|
15
|
+
|
|
16
|
+
## Known limits
|
|
17
|
+
|
|
18
|
+
TunnelScope does not discover an exact end-to-end path MTU or prove a root
|
|
19
|
+
cause on an unknown network. A timeout remains inconclusive without more
|
|
20
|
+
receiver or path evidence. Its UDP protocol is unauthenticated and
|
|
21
|
+
unencrypted; use it only in a controlled environment. Real VPN and Kubernetes
|
|
22
|
+
workflows remain documented but unvalidated on a deployment.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Local HTML report view
|
|
2
|
+
|
|
3
|
+
The CLI can turn an existing JSON report into a searchable HTML file. From
|
|
4
|
+
v0.4.0, the logo is embedded too, so the file works fully offline. It does not
|
|
5
|
+
start a server or send the report to a remote service.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
tunnelscope html probe.json --output probe.html
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Open `probe.html` in a browser. Search filters visible rows; the raw JSON is
|
|
12
|
+
available in a collapsed section. The view supports probe, receiver,
|
|
13
|
+
correlation, bidirectional comparison and lab reports. It presents evidence
|
|
14
|
+
already in the JSON; it does not run a new measurement or infer a root cause.
|
|
15
|
+
The logo is embedded in the generated HTML. The HTML is a self-contained file:
|
|
16
|
+
the logo, report content, styles and search work without network access.
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
The generated HTML contains the complete report, including addresses and any
|
|
21
|
+
other fields in its JSON. Sanitize the JSON before sharing the HTML. Report
|
|
22
|
+
values are HTML-escaped before rendering.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Bounded payload-size sweep
|
|
2
|
+
|
|
3
|
+
From v0.3.0, `probe --sweep START:STOP:STEP` expands
|
|
4
|
+
an ascending grid of UDP payload sizes, always including `STOP` even if `STEP`
|
|
5
|
+
does not land exactly on it. The bounds are 32 to 8192 bytes and at most 16
|
|
6
|
+
sizes. `--sweep` and `--sizes` cannot be used together. `--count` repeats each
|
|
7
|
+
size; existing per-size status counts and individual results remain in JSON.
|
|
8
|
+
|
|
9
|
+
On a controlled Linux IPv4 link, start the receiver first and run:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
tunnelscope receive --bind RECEIVER_IP --output receiver.json
|
|
13
|
+
tunnelscope probe RECEIVER_IP --sweep 1200:1400:25 --count 2 --dont-fragment --output sweep.json
|
|
14
|
+
tunnelscope show sweep.json
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Run the receiver and sender commands on separate terminals or hosts. The
|
|
18
|
+
receiver's small acknowledgement does not test a same-sized return packet.
|
|
19
|
+
Repeat with the endpoint roles reversed to test the other direction.
|
|
20
|
+
|
|
21
|
+
A sweep samples only the listed sizes. A successful size is an observed working
|
|
22
|
+
UDP payload size under these conditions, not the exact path MTU. A
|
|
23
|
+
`packet_too_large` result reflects the sender kernel's DF rejection and
|
|
24
|
+
current route knowledge. A timeout is inconclusive even if nearby smaller
|
|
25
|
+
sizes succeeded. Compare receiver observations with `correlate` to learn
|
|
26
|
+
whether a timed-out probe reached the cooperating receiver.
|
|
27
|
+
|
|
28
|
+
The [direct-link MTU lab](../labs/mtu_boundary.py) checks this option against a
|
|
29
|
+
known 1280-byte virtual link in both directions.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Two-VM payload-size sweep validation
|
|
2
|
+
|
|
3
|
+
Recorded on 2026-10-28 using separate Ubuntu 24.04 and Debian 12 VirtualBox
|
|
4
|
+
guests connected through a temporary 1500-byte host-only link. Ubuntu ran
|
|
5
|
+
Python 3.12.3 and Debian ran Python 3.11.2. Both guests ran the same
|
|
6
|
+
`tunnelscope.py` file (SHA-256
|
|
7
|
+
`43b0cb76dfa6b58452b9ecc844a5db27c2d057cb7cad4c3735233f75d454a857`).
|
|
8
|
+
The temporary host-only addresses and raw JSON reports are kept locally in
|
|
9
|
+
ignored `output/field-test-2026-10-28/`; they are not committed.
|
|
10
|
+
|
|
11
|
+
## Procedure
|
|
12
|
+
|
|
13
|
+
The destination ran `receive --max-packets 5 --idle-timeout 20 --output receiver.json`.
|
|
14
|
+
The source then ran the development CLI in Linux IPv4 DF mode:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
python3 tunnelscope.py probe PEER_IP --sweep 1468:1476:1 --count 1 --timeout 2 --dont-fragment --output probe.json
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The two files were correlated after each direction. The same procedure was
|
|
21
|
+
repeated with guest roles reversed. The direct-link expected UDP payload
|
|
22
|
+
boundary is 1472 bytes: 1500-byte link MTU minus 20-byte IPv4 and 8-byte UDP
|
|
23
|
+
headers.
|
|
24
|
+
|
|
25
|
+
## Results
|
|
26
|
+
|
|
27
|
+
| Direction | 1468–1472 bytes | 1473–1476 bytes | Receiver observed | Run ID |
|
|
28
|
+
| --- | --- | --- | ---: | --- |
|
|
29
|
+
| Ubuntu to Debian | 5/5 acknowledged | 4/4 `packet_too_large` | 5/5 | matched |
|
|
30
|
+
| Debian to Ubuntu | 5/5 acknowledged | 4/4 `packet_too_large` | 5/5 | matched |
|
|
31
|
+
|
|
32
|
+
Each sender identified its host-only interface with MTU 1500. All eight
|
|
33
|
+
oversized attempts reported kernel MTU 1500. Both receiver reports exited at
|
|
34
|
+
`max_packets`; the correlations had zero unmatched observations and no
|
|
35
|
+
receiver-observed timeouts. The source file was checked by SHA-256 on both
|
|
36
|
+
guests before testing.
|
|
37
|
+
|
|
38
|
+
## Limits
|
|
39
|
+
|
|
40
|
+
This confirms the size grid and known boundary on one directly connected
|
|
41
|
+
virtual link. It does not discover an unknown multi-hop path MTU. The
|
|
42
|
+
`packet_too_large` statuses are local kernel rejections, and the successful
|
|
43
|
+
ACKs only confirm forward payload delivery plus a small reply. The same
|
|
44
|
+
experiment on an unknown path can produce timeouts for several reasons.
|