Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ jobs:
results/ci*/**/*.svg
results/ci*/**/*.md
results/ci*/**/*.txt
!**/input.bin
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
/server
/src/client
/src/server
/src/*.o
*.o
*.bin

# Python environments and caches
.venv/
Expand All @@ -14,12 +15,11 @@ __pycache__/
.coverage
htmlcov/

# Local experiment outputs; retain the two verified bundles and history archive
# Local experiment outputs; retain the two verified bundles
/results/*
!/results/README.md
!/results/rubric-final/
!/results/rubric-final-native/
!/results/archive/
*.part.??????

# OS/editor scratch files
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ test: all
python3 tests/test_controller.py
python3 tests/test_metrics.py
python3 tests/test_local.py
python3 tests/test_visualize.py
30 changes: 25 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
# Reliable UDP file transfer with SDN failover

## Live telemetry console

Run the console from the repository root. It builds the C binaries and opens the browser.

```sh
python3 scripts/run.py viz
```

Choose transport flags and click **Run Live C Transfer**. The console captures actual
24-byte headers through a UDP relay, streams sender/receiver JSONL records, shows
the sender ring snapshots, and reports independent SHA-256 verification. The relay
adds 15 ms in each direction to make the transitions visible. **Drop next DATA**
discards one observed DATA datagram.

Load a trial from the selector or enter a directory under results/ and select
**Tail active trial** for a running Mininet experiment. Replay preserves recorded
monotonic timing with play, pause, single-step and speed controls. Older recordings
supply only the fields they retained. The localhost run supplies transport data,
while Mininet recordings supply controller events.


Transfer a binary file over UDP while an SDN controller routes around a failed
or degraded network path. The C application repairs missing packets; the OS-Ken
controller restores reachability through a four-switch Mininet diamond.
or degraded network path. The C11 transport implements Selective Repeat, SACK, adaptive RTO and congestion control. The Python OpenFlow 1.3 controller schedules adaptive probes, reroutes around failures and supports weighted multipath DATA.

## Try a file transfer on localhost

Expand Down Expand Up @@ -72,8 +92,8 @@ docker run --rm --privileged -v "$PWD/results:/project/results" rudp-lab python
| `mininet/` | Diamond topology and netem impairments |
| `scripts/` | Setup, demo, evaluation, local benchmark and plots |
| `tests/` | Protocol, transport, controller and integration tests |
| `results/` | Final Linux/local evidence and labelled historical archive |
| `docs/` | [Architecture](docs/ARCHITECTURE.md), [code map](docs/CODE_MAP.md), [design decisions](docs/DESIGN_DECISIONS.md), [validation](docs/VALIDATION.md), [report](docs/REPORT.md), [viva](docs/VIVA.md), [rubric](docs/RUBRIC_MAPPING.md) |
| `results/` | Final Linux/local evidence |
| `docs/` | [Architecture](docs/ARCHITECTURE.md), [Report](docs/REPORT.md), [Demo](docs/DEMO.md), [Viva](docs/VIVA.md) |

This is a topology-specific lab implementation, not a production transport.
It provides no authentication, encryption, persistent resume or TCP fairness.
Expand All @@ -85,4 +105,4 @@ It provides no authentication, encryption, persistent resume or TCP fairness.

The retained Linux trial shows the sequence of controller and application events.
Across the measured evaluation, 132 transfers matched their hashes and 48 static
failures were expected. [Measurements and scope](docs/KEY_RESULTS.md).
failures were expected. [Measurements and scope](docs/REPORT.md).
8 changes: 8 additions & 0 deletions controller/failover.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ def _monitor(self):
)

self.next_probe_ns[key] = now + interval_ns
self._event("probe_schedule", path=path, direction=direction,
interval_seconds=interval_ns / 1_000_000_000)


# Wake at the next probe deadline or pending-probe timeout.
# Cap sleep at 250 ms so health and controller state are refreshed.
Expand Down Expand Up @@ -454,6 +457,11 @@ def _reconcile(self, cause, force=False):
)

rules = forwarding_rules(path)
self._event("flow_rules", path=path,
rules={str(dpid): [dict(in_port=r.in_port, eth_type=r.eth_type,
out_port=r.out_port) for r in entries]
for dpid, entries in rules.items()})


# Install the selected route and remove stale multipath rules.
for dpid, dp in sorted(self.datapaths.items()):
Expand Down
57 changes: 54 additions & 3 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ particular ACK. Final output SHA-256 establishes integrity independently.
No route or no later useful ACK is censored/N/A, never zero. Repeated-fault
attribution ends at the next intervention; a failure on the inactive branch is
labeled separately. The joined SVG and recovery JSON preserve all three timestamps.
See PROGRESS_LOG.md, DEMO.md and scripts/evaluate.py.
See [Demo](DEMO.md) for commands.

## Optional multipath DATA and adaptive probes

Expand All @@ -169,5 +169,56 @@ Probe deadlines are independent per branch/direction. Successful healthy probes
increase spacing up to 1.5 seconds; loss halves it to a 100 ms minimum. Degraded
paths are capped at 250 ms. The tracker still expires outstanding probes at
500 ms; the 1.5 s spacing leaves that deadline inside the 2 s health freshness.
These changes need fresh Linux measurements; historical fixed-interval
results do not measure their overhead or recovery behavior.
The [report](REPORT.md) separates targeted Linux measurements of adaptive probes
from the repeated matrix collected with fixed probe spacing.

## Implementation map

| Location | Entry point | Responsibility |
| --- | --- | --- |
| `src/packet.h` | constants, packet types | 24-byte wire header, 1024-byte chunks, 32-slot bound |
| `src/client.c` | `main` → `exchange` → `send_file` | Metadata/negotiation, reliable DATA, explicit FIN |
| `src/client.c` | `send_file` send loop | Sequence span and CWND jointly bound outstanding sends |
| `src/client.c` | `send_file` ACK block | Validate SACK before state changes; mark coverage and collect clean RTT samples |
| `src/client.c` | `gap_reports` / `fast_sent` | Three distinct later ACKs trigger one fast retry; timeout remains the fallback |
| `src/server.c` | `data_ack`, `main` | Buffer reordered DATA, drain the contiguous prefix, send ACK/bitmap |
| `src/server.c` | FIN handling | Verify size/CRC, close and rename temporary output; re-ACK duplicate FIN |
| `src/utils.c` | `packet_encode`, `packet_decode` | Explicit network-order serialization and CRC/length validation |
| `src/utils.c` | `receive_packet`, `send_packet` | UDP socket operations and bounded receive wait |
| `controller/routing.py` | `PathPolicy.choose` | Exclude hard failures; prefer materially better fresh health |
| `controller/probes.py` | `ProbeTracker` | Validate independent branch probes, expire losses, smooth directional samples |
| `controller/failover.py` | `_monitor`, `port_status`, `_reconcile`, `barrier_reply` | Probe scheduling, hard-failure events, rule installation and readiness |
| `scripts/commands/run_experiments.py` | `run_trial`, `recovery_metrics` | Inject faults, verify endpoint/hash outcomes and join recovery events |
| `scripts/commands/evaluate.py` | `linux_plan`, `main` | Run tests and repeated matrix; save manifest and plots |


## Design trade-offs

| Choice | Reason | Accepted cost / limit |
| --- | --- | --- |
| C/POSIX UDP and one event loop | Socket mechanics, ACKs and retries are explainable | Single upload per receiver; no shared service |
| Selective Repeat, 32 slots | Buffer later packets; retry only missing coverage; bounded memory | Head holes limit sequence span; high bandwidth-delay paths can underfill |
| 32-bit bitmap plus cumulative base | Repeated receiver state repairs lost ACK information in eight payload bytes | One bitmap revision, explicit opt-in START types, no automatic downgrade |
| Adaptive RTO + Karn + backoff | Avoid repeated premature retries on slow paths; reject ambiguous RTTs | Initial 200 ms may still be too short; 50–2000 ms lab bounds |
| Three distinct later ACKs | Fast gap recovery without a new DATA format | Reordering may trigger a needless retry; no full TCP recovery |
| Capped slow start / additive increase | Basic sender response to loss | Short uncontended flows get slower; fairness is unproven |
| Same monotonic clock | Join application behavior to link-down and rule readiness | Only comparable on the same kernel |
| Bidirectional branch probes | Measure the idle backup and soft degradation without changing UDP | Probe loss is noisy; control-channel latency contributes; small extra traffic |
| EWMA, freshness, score threshold, hold-down | Avoid routing on stale/small transient differences | Soft detection deliberately slower than hard failure |
| Hard failures override health | A live but unhealthy route is preferable to a broken one | If neither branch works, retries cannot manufacture connectivity |
| Independent SHA-256 and atomic rename | Strong experimental correctness evidence; failed transfer preserves destination | CRC-32 itself is not authentication; rename is not power-loss durability |
| CC off in the main matrix | Isolate routing, RTO and ACK format effects | CWND behavior is verified and separately ablated, not a fairness claim |

## Telemetry console

The viz command serves a loopback-only HTTP interface using the Python standard
library. A UDP relay captures actual serialized headers and adds a fixed 15 ms
delay per direction. An opt-in RUDP_VIZ_RING trace records the sender's real
window slots and gap counters. Normal protocol execution keeps that trace disabled.
Independent SHA-256 compares source and finalized output after both endpoints exit.

The SSE journal lives in a temporary file. Browser reconnects resume using event
IDs, while slow readers can catch up independently of UDP. A session owns its
subprocesses and cleans up on stop. Recorded trials merge sender, receiver and
controller JSONL events by monotonic timestamp. Header captures and ring snapshots
require instrumentation present at recording time.
54 changes: 0 additions & 54 deletions docs/CODE_MAP.md

This file was deleted.

Loading
Loading