Artifact: USENIX Security 2027 supplementary material
Paper: FlyBlind: Cross-Slice Timeliness Attacks on UAV Situational Awareness over 5G
Author: (anonymous; double-blind submission)
Inst.: withheld (double-blind review)
Date: 2026
License: MIT (see LICENSE)
This package contains the FlyBlind experimental stack: a Kind-hosted OpenAirInterface (OAI) 5G SA lab with ORANSlice static PRB partitioning, ArduPilot SITL over UDP MAVLink on the PDU path, a four-layer campaign orchestrator (network, MAC/PRB, C2, physics), frozen campaign trees under 02_results/, and paper figure assets under 03_images/. Reviewers can install host dependencies, bring up a testbed, rerun paper and mitigation campaigns via the orchestrator, and audit the archived runs without relying on any workstation-specific layout.
- Purpose and scope
- Directory layout
- Hardware and software requirements
- Dependency bootstrap
- Testbed stacks (overview)
- Start the testbed
- Stop the testbed
- Experiment orchestration
- Results naming (
02_results/) - Playbook — reproduce frozen campaigns
- Figures and offline audits
- Troubleshooting
- Citation
FlyBlind studies Silent State Staleness: authorized uplink co-tenancy under soft slice isolation that ages GCS telemetry while conventional link KPIs (OWD, availability, GUIDED mode) remain green. The artifact separates:
| Layer | Role |
|---|---|
01_scripts/ |
Kind bootstrap, ORANSlice image builds, SITL helpers, orchestrators |
02_results/ |
Frozen campaigns (typically RUN01…RUN03) used in the paper |
03_images/ |
Paper figure assets (pdfs/, pngs/, icons/, scripts/) |
Two Kind stacks share the same topology vocabulary but must not be mixed: paper (tm-oranslice) and mitigation (tm-oranslice-mitigation). A third entry point, start_testbed_maxsched.sh, rebuilds an isolated gNB image for the max_sched_ues sensitivity sweep and must not replace canonical paper runs.
artifacts/
├── 01_scripts/
│ ├── 01_testbeds/ # Kind + OAI + ORANSlice (+ build/, vendor/, runtime*/)
│ ├── 02_experiments/ # orchestrate.py, orchestrate_mitigation.py, probes/, lib/
│ └── 03_sitl/ # ArduPilot SITL runtime + mission helpers
├── 02_results/ # frozen campaigns (RUN01..RUN03) + audits / max_sched_ues_sweep/
├── 03_images/ # paper figure assets (pdfs/, pngs/, icons/, scripts/)
├── install_dependencies.sh
├── LICENSE
└── README.md
Paths in this document are always relative to the artifacts/ root.
Hardware (recommended). amd64 or arm64 workstation/server with 16 GB+ RAM, 50 GB+ free disk, and CPU headroom for Kind + multiple OAI NR-UE pods + ArduPilot SITL.
Operating system. Debian/Ubuntu or a derivative with apt (see install_dependencies.sh). Other distributions can install the same components manually.
Software installed by bootstrap. Docker Engine (+ Compose v2), kubectl, kind v0.20.0, Helm-related tooling as needed by scripts, plus host utilities (git, socat, iptables, iperf3, PyYAML, pymavlink, etc.).
Network. Outbound HTTPS to pull container images, Helm/charts, and OAI reference configs. Air-gapped labs must mirror those assets first.
First ORANSlice image build. On a cold host, the first start_testbed*.sh invocation may build ORANSlice images (about 30–90 minutes). Subsequent starts reuse cached images.
From the artifacts/ root:
sudo bash install_dependencies.shAfter install, run newgrp docker or re-login so the invoking user can talk to the Docker daemon without sudo, then proceed to the testbed scripts.
| Stack | Start script | Kind cluster | Orchestrator | Use |
|---|---|---|---|---|
| Paper (default) | 01_scripts/01_testbeds/start_testbed.sh |
tm-oranslice (kind-tm-oranslice) |
orchestrate.py |
Canonical paper campaigns |
| Mitigation | 01_scripts/01_testbeds/start_testbed_mitigation.sh |
tm-oranslice-mitigation |
orchestrate_mitigation.py |
UL rrmPolicy, AQM, rate-limit A, MinRatio sweeps |
max_sched_ues |
01_scripts/01_testbeds/start_testbed_maxsched.sh |
uses paper Kind name + maxsched gNB image | orchestrate.py |
Reviewer sensitivity (#3); not for headline paper tables |
Common flow
cd artifacts/01_scripts/01_testbeds
./start_testbed.sh # or start_testbed_mitigation.sh / start_testbed_maxsched.sh
# wait until pods are Ready and SITL is warm
cd ../02_experiments
python3 -u orchestrate.py … # or orchestrate_mitigation.py …
./stop_testbed.sh # when finished, or before changing topology / MinRatio / 5QI / stackWhat the testbed provisions (brief). OAI core (AMF/SMF/UPF + MySQL + ext-DN), one gNB (RFsim), rogue UE fleet + GCS + UAV UEs, ArduPilot SITL with C2 over UDP MAVLink on the 5G PDU. In cross mode (paper default): slice A carries rogue/companion traffic (oai); slice B carries C2/UAS (oai.urllc). PDU addresses are not hard-coded — read them from the live pods (for example kubectl get pod -n ran -o wide and ip -4 addr on the UE tunnel interface). Use placeholders such as GCS_PDU / UAV_PDU in any manual checks.
cd artifacts/01_scripts/01_testbeds
./start_testbed.sh # cross, B0, GNB_PRB=106, 4 rogues
./start_testbed.sh cross B60 # 60% MinRatio on C2 slice
./start_testbed.sh cross B100
./start_testbed.sh intra # single-slice (legacy)
./start_testbed.sh cross rogues=4 maclog=debugPositional / token arguments (any order):
| Token | Meaning |
|---|---|
cross | intra |
Slice topology (default cross) |
B0…B100 |
rRMPolicyMinRatio % on the C2/UAS slice (cross) or sole slice (intra) |
rogues=1…4 |
Rogue UE fleet size (default 4) |
maclog=info|debug|analysis |
gNB MAC log verbosity (default info) |
Same tokens as the paper stack, plus:
| Token | Meaning |
|---|---|
A0…A100 |
MinRatio % on the attacker slice (DL+UL via rrmPolicy) |
AQM |
Stamp for host AQM ablation (UAV UL fq_codel; applied by orchestrate_mitigation.py) |
ul-rrmpolicy |
Force UL rrmPolicy path even with A0/B0 (rarely needed) |
Examples:
./start_testbed_mitigation.sh cross B60
./start_testbed_mitigation.sh cross A60
./start_testbed_mitigation.sh cross AQMRuntime configs live under runtime-mitigation/ (isolated from paper runtime/).
Isolated gNB image (localhost/oai-gnb-oranslice-maxsched:…) with opt-in TM_MAX_SCHED_UES. Paper image and vendor tree stay untouched. Prefer this only for the sensitivity sweep under 02_results/max_sched_ues_sweep/.
./stop_testbed.sh && ./start_testbed_maxsched.sh cross B0 maxsched=4
./stop_testbed.sh && ./start_testbed_maxsched.sh cross B0 maxsched=6
./stop_testbed.sh && ./start_testbed_maxsched.sh cross B0 maxsched=8
# equivalent: TM_MAX_SCHED_UES=8 ./start_testbed_maxsched.sh cross B0Unset maxsched / TM_MAX_SCHED_UES ⇒ automatic bandwidth cap (control). Supported override range: 1…16. See also 02_results/max_sched_ues_sweep/README.md.
| Variable | Typical values | Effect |
|---|---|---|
GNB_PRB |
106 (default), 51 |
NRB @ SCS 30 kHz (40 MHz / 20 MHz, TS 38.104) |
MIN_RATIO_SLICE_B |
0…100 |
Env form of B-label MinRatio (CLI B* preferred) |
EQUAL_5QI |
0 (default) / 1 |
Force rogue and C2 to the same 5QI |
EQUAL_5QI_VALUE |
default 9 |
5QI used when EQUAL_5QI=1 |
ROGUE_5QI / C2_5QI |
default separated (9 / 82) |
Explicit QoS split |
ORANSLICE_SKIP_BUILD |
1 |
Fail fast if images are missing (no auto-build) |
ARDUPILOT_ROOT |
vendor path | Override ArduPilot source/binary root |
SITL_IMAGE |
default ubuntu:24.04 |
Sidecar image for SITL |
Whenever you change topology, PRB MinRatio, 5QI, AQM stamp, or paper ↔ mitigation ↔ maxsched stack:
./stop_testbed.sh && ./start_testbed.sh [cross|intra] [B0|B60|…]
# or start_testbed_mitigation.sh / start_testbed_maxsched.shManual image rebuilds (only if needed):
./build/build_oranslice_images.sh
./build/build_oranslice_images_mitigation.sh
./build/build_oranslice_images_maxsched.shcd artifacts/01_scripts/01_testbeds
./stop_testbed.sh| Behavior | Detail |
|---|---|
| Tears down | Paper and mitigation Kind clusters, host leftovers, runtime/ and runtime-mitigation/ |
| Keeps | vendor/, Docker images, and all of 02_results/ |
--prune-docker |
Also runs docker system prune -af — destructive (removes compiled ORANSlice images) |
--keep-images |
No-op; images are kept by default |
The orchestrator does not start Kind. It assumes a matching testbed is already Running, then synchronizes four measurement layers:
- NETWORK — OWD / IPDV / availability probes
- MECHANISM — gNB MAC / PRB evidence
- C2 — setpoint-stream (default) / MAVLink mission integrity
- PHYSICS — track error, belief vs truth, flight outcome
| Entry point | Default kube context | Stack |
|---|---|---|
python3 orchestrate.py |
kind-tm-oranslice |
Paper / maxsched |
python3 orchestrate_mitigation.py |
kind-tm-oranslice-mitigation |
Mitigation |
Default namespaces: --namespace ran, --core-namespace core (UPF telemetry).
| Category | Flags | Notes |
|---|---|---|
| Campaign | --condition, --duration, --repeat, --min-valid-runs, --results-tag, --out |
--duration typically 180 or 600; frozen paper runs use 600 |
| Radio / topology | --topology, --gnb-prb |
106=40 MHz, 51=20 MHz |
| Attack (paper playbook) | --attack multi-rogue, --multi-rogue-count, --multi-rogue-phase-ms, --attack-start-after-s |
Frozen campaigns: N=1…4, phase 0, start after 45 s |
| Companion UL | --companion-video-mbps |
0, 8, 12, 24 in frozen trees |
| Mission | --workload, --setpoint-rate-hz, --trajectory, --trajectory-* |
Default: setpoint-stream, 10 Hz, circle |
| Reservation / naming | --reservation, --min-ratio-slice-b, --results-tag |
Folder tokens such as MITIGATION_PRB60, MITIGATION_UL_RRMPOLICY |
| Mitigation dataplane | --mitigation-aqm-ul, --mitigation-rate-limit-a, --mitigation-ul-rrmpolicy, --min-ratio-slice-a |
Effective on orchestrate_mitigation.py; on paper orchestrate.py, AQM/5QI flags are primarily naming tokens |
| Gates | --warmup, --avail-min-pct, --require-prb, --skip-sitl-warmup |
Baseline must pass mandatory gates |
| K8s | --kube-context, --namespace, --core-namespace |
Must match the live stack |
--help also lists legacy attack modes (multi-rogue-dl, *-sync, plus older pulse/flood/CBR-oriented options). Frozen campaigns in this artifact use multi-rogue only; treat other modes as unused for paper reproduction.
Baseline (paper, 600 s, 3 reps, companion 12 Mbps):
cd artifacts/01_scripts/02_experiments
python3 -u orchestrate.py --condition baseline \
--gnb-prb 106 --topology cross --companion-video-mbps 12 \
--duration 600 --warmup 10 --repeat 3Headline attack N4 (paper default):
python3 -u orchestrate.py --condition attack --attack multi-rogue \
--multi-rogue-count 4 --multi-rogue-phase-ms 0 \
--attack-start-after-s 45 --gnb-prb 106 --topology cross \
--companion-video-mbps 12 --duration 600 --warmup 10 --repeat 3Mitigation UL rrmPolicy (B60) — separate terminal already running mitigation testbed:
# terminal 1: ./start_testbed_mitigation.sh cross B60
python3 -u orchestrate_mitigation.py --condition attack --attack multi-rogue \
--multi-rogue-count 4 --multi-rogue-phase-ms 0 --attack-start-after-s 45 \
--reservation 60 --results-tag MITIGATION_UL_RRMPOLICY \
--companion-video-mbps 12 --duration 600 --warmup 10 --repeat 3Naming is defined in 01_scripts/02_experiments/lib/results_layout.py.
{PRB}_PRB-{TOPO}-{N}_ROGUES-{SCENARIO}-{DUR}S-{VID}MPBS/
analysis_params.json
matrix_*.json
RUN01/ … RUN03/
| Field | Meaning |
|---|---|
{PRB} |
106 or 51 |
{TOPO} |
CROSS or INTRA |
{N}_ROGUES |
Testbed capacity (fleet size), not attack dose |
| Dose | ATK_N1 … ATK_N4 inside {SCENARIO} |
| Baseline | BASELINE |
| Mitigations | Chain _AND_MITIGATION_* (e.g. MITIGATION_PRB60, MITIGATION_UL_RRMPOLICY, MITIGATION_AQM_UL, MITIGATION_RATELIMIT_A) |
{DUR}S |
Campaign duration token (600S main; 60S = short smoke only) |
{VID}MPBS |
Companion video demand (0MPBS, 8MPBS, 12MPBS, 24MPBS) |
If a campaign folder already exists, the orchestrator appends _a, _b, … to the transcript / collision suffix rather than overwriting.
Per-run artefacts (short list): four_layer_summary.json, run_meta.json, event_markers.csv, prb.csv / prb.json, network.json, c2.json, physics.json, c2_telemetry_uav.csv, telemetry_downlink_gcs.csv, aoi_timeseries.csv, trk_truth.json, attack_summary*.json, freshness_summary.json, plus live logs.
Validity: check RUN0x/four_layer_summary.json ("valid": true) and the campaign matrix_*.json / orchestrate transcript.
Step 0. Match the live testbed to the target folder: GNB_PRB, B-label / MinRatio, and paper vs mitigation vs maxsched stack. Then stop/start cleanly before changing any of those.
| Family | Frozen pattern in 02_results/ |
Testbed | Orchestrate template |
|---|---|---|---|
| Baselines | *-BASELINE-* (video × PRB × duration) |
start_testbed.sh cross B0 |
--condition baseline --companion-video-mbps {0,8,12,24} --duration 600 --gnb-prb {106,51} |
| Dose sweep | *-ATK_N1-* … *-ATK_N4-* |
cross B0 |
--condition attack --attack multi-rogue --multi-rogue-count {1..4} --multi-rogue-phase-ms 0 --attack-start-after-s 45 |
| Companion ablation | same trees with 0MPBS / 8MPBS / 12MPBS / 24MPBS |
idem | vary --companion-video-mbps |
| PRB ablation | 51_PRB-* vs 106_PRB-* |
restart with GNB_PRB=51 or 106 |
--gnb-prb 51 or 106 |
| Mitigation MinRatio B | *_MITIGATION_PRB{2,5,10,20,40,60,80}_*AND*UL_RRMPOLICY* |
start_testbed_mitigation.sh cross B{…} |
orchestrate_mitigation.py … --reservation {…} --results-tag MITIGATION_UL_RRMPOLICY |
| Mitigation controls | *_MITIGATION_AQM_UL*, *_MITIGATION_RATELIMIT_A* |
mitigation stack (+ AQM stamp when needed) |
--mitigation-aqm-ul or --mitigation-rate-limit-a |
max_sched_ues |
max_sched_ues_sweep/cap{4,6,8}/ |
start_testbed_maxsched.sh cross B0 maxsched={4,6,8} |
see sub-README; --results-tag MAXSCHEDN-SMOKE, --repeat 1 |
| Drain / phase audit | *_DRAIN540, phase_audit_*.json |
paper stack | special timing / offline audit only |
From 02_results/reservation_cost_audit.json → campaigns_106:
| B-label | Frozen campaign folder |
|---|---|
| B0 | 106_PRB-CROSS-4_ROGUES-ATK_N4-600S-12MPBS |
| B2 | 106_PRB-CROSS-4_ROGUES-ATK_N4_AND_MITIGATION_PRB2_AND_MITIGATION_UL_RRMPOLICY-600S-12MPBS |
| B5 | …_MITIGATION_PRB5_AND_MITIGATION_UL_RRMPOLICY-600S-12MPBS |
| B10 | …_MITIGATION_PRB10_AND_MITIGATION_UL_RRMPOLICY-600S-12MPBS |
| B20 | …_MITIGATION_PRB20_AND_MITIGATION_UL_RRMPOLICY-600S-12MPBS |
| B40 | …_MITIGATION_PRB40_AND_MITIGATION_UL_RRMPOLICY-600S-12MPBS |
| B60 | …_MITIGATION_PRB60_AND_MITIGATION_UL_RRMPOLICY-600S-12MPBS |
| B80 | …_MITIGATION_PRB80_AND_MITIGATION_UL_RRMPOLICY-600S-12MPBS |
Matching baseline folders exist with BASELINE_AND_MITIGATION_PRB* for the same B-labels. Parallel trees under 51_PRB-… cover the 20 MHz ablation (including mitigation MinRatio campaigns).
Short smokes. Folders ending in 60S (not 600S) are diagnostic / incomplete-duration runs. Do not treat them as substitutes for the main 600 s campaigns.
Post-run check. Compare new RUN0x/four_layer_summary.json and the campaign transcript against the frozen tree; confirm "valid": true and aggregate metrics in matrix_*.json.
max_sched_ues reproduction (detail in 02_results/max_sched_ues_sweep/README.md):
cd artifacts/01_scripts/01_testbeds
./stop_testbed.sh
./start_testbed_maxsched.sh cross B0 maxsched=N # N = 4, 6, or 8
cd ../02_experiments
python3 -u orchestrate.py --condition attack --attack multi-rogue \
--multi-rogue-count 4 --multi-rogue-phase-ms 0 --attack-start-after-s 45 \
--gnb-prb 106 --companion-video-mbps 12 --duration 600 --warmup 10 --repeat 1 \
--results-tag MAXSCHEDN-SMOKEHeadline metric for that sweep: delivery AoI = telemetry_arrival_delay_ms in four_layer_summary.json (not aoi_network_ms).
Paper figure assets live under 03_images/:
| Subfolder | Contents |
|---|---|
pdfs/ |
Vector figures used in the paper (fig_teaser, fig_uldlgrants, fig_pipeline, fig_pre_attack_drain, fig_paths, fig_orbit) |
pngs/ |
Raster previews of the same figures |
icons/ |
Icon assets referenced by TikZ / Matplotlib generators |
scripts/ |
Generators (build_fig_*.sh, generate_fig_*.py); run scripts/build_all.sh to rebuild |
Auditing frozen campaigns does not require re-running the testbed.
Precomputed aggregates already ship under 02_results/:
reservation_cost_audit.json/reservation_cost_audit.csv— MinRatio B map and per-run summariesphase_audit_*.json— phase-window audits for selected headline runs
- Changed topology, MinRatio, 5QI, AQM, or stack → always
./stop_testbed.sh && ./start_testbed*.sh …. - ORANSlice build too slow / already built →
ORANSLICE_SKIP_BUILD=1only if the required images already exist locally. - Baseline fails availability / SITL gates → check
ran/corepods Ready; do not pass--skip-sitl-warmupfor formal runs. - Folder collision in
02_results/→ orchestrator creates_a/_btranscript suffixes; inspect the newest transcript. - Paper vs mitigation → never point
orchestrate.pyat the mitigation cluster (or vice versa); kube-context defaults differ. - maxsched vs paper → use
start_testbed_maxsched.shonly for the sensitivity sweep; headline tables usestart_testbed.sh. --prune-docker→ removes local images; expect another 30–90 min rebuild.- PDU addresses → always read from live pods; do not copy lab IPs from elsewhere.
Please cite this supplementary material together with the USENIX Security 2027 paper FlyBlind: Cross-Slice Timeliness Attacks on UAV Situational Awareness over 5G using the artifact identifier issued by the conference (placeholder until the camera-ready / artifact badge ID is assigned).