Skip to content

Latest commit

 

History

22 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LateFrame

Current version: 0.4.1

LateFrame is a Linux UDP traffic generator for experiments where packet inter-arrival time matters.

It was developed as the traffic-generation and replay framework used in the evaluation of LatencyScope. If you use LateFrame in academic work, please cite:

@misc{maghsoudnia2026latencyscopesystemlevelmathematicalframework,
      title={LatencyScope: A System-Level Mathematical Framework for 5G RAN Latency}, 
      author={Arman Maghsoudnia and Aoyu Gong and Raphael Cannatà and Dan Mihai Dumitriu and Haitham Hassanieh},
      year={2026},
      eprint={2511.21277},
      archivePrefix={arXiv},
      primaryClass={cs.NI},
      url={https://arxiv.org/abs/2511.21277}, 
}

The motivation is simple: ping and fping are useful network tools, but they are not precise packet schedulers. If you need to generate traffic at a target interval with low jitter, they are the wrong baseline.

LateFrame uses absolute deadline scheduling on CLOCK_MONOTONIC, CPU pinning, and a minimal send loop to keep pacing tight. By default it uses timerfd, and it can also use clock_nanosleep() with an optional busy-spin window.

Modes

LateFrame supports two main use cases:

  • synthetic traffic generation with controlled inter-arrival timing
  • replay of previously captured packet traces from PCAP files through UDP encapsulation

Synthetic generation is useful when you want a clean constant, Poisson, or Gaussian process.

PCAP replay is useful when you need to reproduce a captured trace while preserving the original packet timing. LateFrame retains as much as possible from the end of each captured packet inside a UDP payload, so the replayed packet matches the captured packet length whenever possible after accounting for the replay encapsulation overhead. This matters in replay scenarios where sending packets with the same size and timing is more important than reconstructing the exact original packet contents.

This is especially useful for experiments and research where accurate workload replay in terms of packet size and timing matters more than generating identical packets.

If the original packet is smaller than the required replay encapsulation overhead, the replayed packet cannot match the original packet size and will be larger. In this case, the user is warned.

Result

The current comparison uses a 100 ms target interval and 1000 transmitted packets. The figure below includes:

  • the combined comparison across the three LateFrame runs plus ping and fping
  • one standalone plot for each sender
  • trimmed zoomed plots for ping and fping with the top and bottom 1% removed

LateFrame timerfd: count=999, mean=100.000013938 ms, std=0.032544443 ms
LateFrame spin 50us: count=999, mean=100.000007255 ms, std=0.038343503 ms
LateFrame spin 100us: count=999, mean=99.999996038 ms, std=0.046501921 ms
ping: count=999, mean=104.012259731 ms, std=0.517701949 ms
fping: count=999, mean=100.000159280 ms, std=0.247248914 ms

Inter-arrival results grid

Two things stand out in the captures:

  • all three LateFrame runs stay centered on the 100 ms target
  • ping misses the target mean by about 4 ms
  • ping and fping both show substantially wider spread than any of the LateFrame runs

Build

Dependencies on Debian or Ubuntu:

sudo apt install build-essential make libpcap-dev tshark

Build:

git clone https://github.com/arman-maghsoudnia/LateFrame.git
cd LateFrame
make

The binary is produced at package/usr/bin/lateframe.

Install:

sudo make install

Or:

make install PREFIX=/opt/lateframe

Usage

lateframe [options]

Main options:

  • -n, --num-packets: number of packets to send
  • -i, --interface: source interface
  • -d, --destination: destination IPv4 address
  • -p, --port: destination UDP port
  • -t, --distribution: constant, poisson, gaussian, or pcap
  • -a, --param: interval in ms, lambda in packets/s, or Gaussian mean in ms
  • -S, --sigma: Gaussian sigma in ms
  • -s, --size: payload size in bytes
  • -f, --pcap-file: PCAP file for replay mode
  • --wait-mode: timerfd or nanosleep for the packet pacing wait primitive
  • --spin-us: busy-spin window in microseconds for nanosleep wait mode
  • --no-cpu-pin: Disable CPU pinning (default: enabled)
  • -l, --log: log sends to stdout and /tmp/lateframe.log
  • -D, --dscp: Assign same DSCP value to all generated or replayed packets (0 to 63)
  • -c, --capture: capture generated packets to /tmp/lateframe-capture.pcap

Notes:

  • Option order does not matter.
  • Both --num-packets and legacy --num_packets are accepted.
  • --wait-mode defaults to timerfd.
  • --wait-mode nanosleep requires --spin-us.
  • Generated traffic modes require -n, -s, and -a.
  • Gaussian mode also requires -S.
  • PCAP mode ignores -n, -s, -a, and -S.
  • CPU pinning can be disabled with --no-cpu-pin. The pinned vs. unpinned replay comparison results and reproduction commands are documented in comparison-data/CPU_pinning/README.md.

Examples:

sudo lateframe -n 1000 -i eth0 -d 192.168.1.10 -p 12345 -t constant -a 100 -s 256
sudo lateframe -n 1000 -i eth0 -d 192.168.1.10 -p 12345 -t poisson -a 100 -s 256
sudo lateframe -n 1000 -i eth0 -d 192.168.1.10 -p 12345 -t gaussian -a 40 -S 2 -s 256
sudo lateframe -i eth0 -d 192.168.1.10 -p 12345 -t pcap -f trace.pcap
sudo lateframe -i eth0 -d 192.168.1.10 -p 12345 -t pcap -f trace.pcap --wait-mode nanosleep --spin-us 100

Reproducing The Comparison

The PCAPs used for the current result are in comparison-data/generated/. They were captured with the commands below.

fping

Send:

sudo fping -c 1000 -p 100 128.178.122.100

Capture on destination:

sudo tshark -i eno1 -f "icmp[0] = 8 and host 128.178.122.100" -w /tmp/fping-test.pcap

ping

Send:

sudo ping 128.178.122.100 -i 0.1 -c 1000

Capture on destination:

sudo tshark -i eno1 -f "icmp[0] = 8 and host 128.178.122.100" -w /tmp/ping-test.pcap

LateFrame timerfd

Send:

sudo lateframe -n 1000 -i eno1 -d 128.178.122.100 -p 12345 -t constant -a 100 -s 256 -c --wait-mode timerfd

Capture on destination:

sudo tshark -i eno1 -f "udp and host 128.178.122.100 and port 12345" -w /tmp/lateframe-out-timerfd.pcap

LateFrame nanosleep spin 50us

Send:

sudo lateframe -n 1000 -i eno1 -d 128.178.122.100 -p 12345 -t constant -a 100 -s 256 -c --wait-mode nanosleep --spin-us 50

Capture on destination:

sudo tshark -i eno1 -f "udp and host 128.178.122.100 and port 12345" -w /tmp/lateframe-out-spin50.pcap

LateFrame nanosleep spin 100us

Send:

sudo lateframe -n 1000 -i eno1 -d 128.178.122.100 -p 12345 -t constant -a 100 -s 256 -c --wait-mode nanosleep --spin-us 100

Capture on destination:

sudo tshark -i eno1 -f "udp and host 128.178.122.100 and port 12345" -w /tmp/lateframe-out-spin100.pcap

Versions used:

  • ping: ping from iputils 20240117
  • fping: Version 5.1

Host used for the run:

  • Architecture: x86_64
  • CPU: 12th Gen Intel(R) Core(TM) i7-1260P
  • Vendor: GenuineIntel
  • Sockets: 1
  • Cores per socket: 12
  • Threads per core: 2
  • Logical CPUs: 16
  • CPU max frequency: 4700.0000 MHz
  • CPU min frequency: 400.0000 MHz
  • Kernel: 6.8.0-111-lowlatency
  • CPU governor: performance on all cores
  • L1d cache: 448 KiB (12 instances)
  • L1i cache: 640 KiB (12 instances)
  • L2 cache: 9 MiB (6 instances)
  • L3 cache: 18 MiB (1 instance)
  • NUMA nodes: 1
  • Virtualization: VT-x

Plotting

To regenerate the figures from the PCAPs:

python3 scripts/plot_interarrival_density.py \
  --series "lateframe-timerfd=LateFrame timerfd=comparison-data/generated/lateframe-out-timerfd.pcap" \
  --series "lateframe-spin-50us=LateFrame spin 50us=comparison-data/generated/lateframe-out-spin50.pcap" \
  --series "lateframe-spin-100us=LateFrame spin 100us=comparison-data/generated/lateframe-out-spin100.pcap" \
  --series "ping=ping=comparison-data/generated/ping-test.pcap" \
  --series "fping=fping=comparison-data/generated/fping-test.pcap" \
  --zoomed ping \
  --zoomed fping \
  --output docs/generated/interarrival-density-comparison.png \
  --individual-output-dir docs/generated/individual-density-plots \
  --zoomed-output-dir docs/generated/zoomed-density-plots \
  --unified-output docs/generated/interarrival-density-results-grid.png

This produces:

  • docs/generated/interarrival-density-comparison.png
  • docs/generated/individual-density-plots/lateframe-timerfd-interarrival-density.png
  • docs/generated/individual-density-plots/lateframe-spin-50us-interarrival-density.png
  • docs/generated/individual-density-plots/lateframe-spin-100us-interarrival-density.png
  • docs/generated/individual-density-plots/ping-interarrival-density.png
  • docs/generated/individual-density-plots/fping-interarrival-density.png
  • docs/generated/zoomed-density-plots/ping-interarrival-density-trimmed.png
  • docs/generated/zoomed-density-plots/fping-interarrival-density-trimmed.png
  • docs/generated/interarrival-density-results-grid.png

To regenerate the replay comparison figures from all replay captures in comparison-data/replay/:

python3 scripts/plot_interarrival_diff.py \
  --original comparison-data/generated/ping-test.pcap \
  --replay "timerfd=timerfd=comparison-data/replay/ping-test-replayed-result-timerfd.pcap" \
  --replay "spin50=nanosleep spin 50us=comparison-data/replay/ping-test-replayed-result-spin50.pcap" \
  --replay "spin100=nanosleep spin 100us=comparison-data/replay/ping-test-replayed-result-spin100.pcap" \
  --output-dir docs/replay \
  --output-prefix ping-replay

This produces one heartbeat plot and one density plot per replay PCAP, plus:

  • docs/replay/ping-replay-interarrival-diff-heartbeat-aggregate.png
  • docs/replay/ping-replay-interarrival-diff-density-aggregate.png

PCAP Replay

PCAP replay mode does not send raw frames. It reads a PCAP, preserves the observed inter-arrival timing, and encapsulates the captured bytes into UDP packets.

For a captured packet of length X, LateFrame preserves as many bytes as possible from the end of that packet and uses them as the UDP payload, so:

  • if X is at least as large as the replay encapsulation overhead, the replayed packet has length X
  • if X is smaller than the replay encapsulation overhead, the replayed packet must be larger than the original

This is not raw frame replay. It is timing-preserving UDP encapsulation of captured packet bytes.

For Ethernet captures, the replay encapsulation overhead is Ethernet + IPv4 + UDP headers. For raw IPv4 captures, it is IPv4 + UDP headers. LateFrame prints a warning when exact size matching is not possible.

Any captured packet bytes can be encapsulated this way. If a packet was truncated in the PCAP, replay uses the captured length, because the missing bytes are not available.

LateFrame supports two pacing backends for replay and generated traffic:

  • timerfd: the default backend, using absolute deadline scheduling through timerfd
  • nanosleep: absolute clock_nanosleep() plus a configurable busy-spin window through --spin-us

Both are supported because the better choice depends on the host. In the replay measurements below, busy waiting slightly improved the replay error compared with timerfd, while timerfd remains the default because it is the more conservative baseline.

Replay Results

For replay evaluation, we replay the existing comparison-data/generated/ping-test.pcap trace and compare the inter-arrival difference between the original capture and the replayed output.

Measured replay error against comparison-data/generated/ping-test.pcap:

  • timerfd: abs mean 0.012260538 ms, std 0.025607717 ms, min -0.179767609 ms, max 0.172853470 ms
  • nanosleep --spin-us 50: abs mean 0.012215671 ms, std 0.023457672 ms, min -0.134944916 ms, max 0.136375427 ms
  • nanosleep --spin-us 100: abs mean 0.013313733 ms, std 0.023949685 ms, min -0.119447708 ms, max 0.086784363 ms

The aggregate heartbeat plot is shown first because it makes packet-by-packet outliers easier to compare across the three pacing modes, followed by the aggregate density view.

Replay inter-arrival heartbeat aggregate

Replay inter-arrival density aggregate

Reproducing Replay Results

For the replay comparison, we use the already captured comparison-data/generated/ping-test.pcap file as the source trace.

timerfd

Capture on the same machine:

sudo tshark -i eno1 -f "udp port 12345" -w /tmp/ping-test-replayed-result-timerfd.pcap

Send:

sudo lateframe -i enp113s0 -d 128.178.122.100 -p 12345 -t pcap -f comparison-data/generated/ping-test.pcap --wait-mode timerfd

nanosleep spin 50us

Capture on the same machine:

sudo tshark -i eno1 -f "udp port 12345" -w /tmp/ping-test-replayed-result-spin50.pcap

Send:

sudo lateframe -i enp113s0 -d 128.178.122.100 -p 12345 -t pcap -f comparison-data/generated/ping-test.pcap --wait-mode nanosleep --spin-us 50

nanosleep spin 100us

Capture on the same machine:

sudo tshark -i eno1 -f "udp port 12345" -w /tmp/ping-test-replayed-result-spin100.pcap

Send:

sudo lateframe -i enp113s0 -d 128.178.122.100 -p 12345 -t pcap -f comparison-data/generated/ping-test.pcap --wait-mode nanosleep --spin-us 100

Then copy the replay PCAPs into comparison-data/replay/ and run:

python3 scripts/plot_interarrival_diff.py \
  --original comparison-data/generated/ping-test.pcap \
  --replay "timerfd=timerfd=comparison-data/replay/ping-test-replayed-result-timerfd.pcap" \
  --replay "spin50=nanosleep spin 50us=comparison-data/replay/ping-test-replayed-result-spin50.pcap" \
  --replay "spin100=nanosleep spin 100us=comparison-data/replay/ping-test-replayed-result-spin100.pcap" \
  --output-dir docs/replay \
  --output-prefix ping-replay

How It Works

  • Absolute deadline scheduling on CLOCK_MONOTONIC using either timerfd or clock_nanosleep()
  • CPU pinning
  • Best-effort SCHED_FIFO
  • Pre-built payloads for generated traffic
  • Optional tshark capture during transmission

For generated traffic modes, LateFrame writes a sequence ID at the beginning of each UDP payload. That makes packet matching easier in captures and receiver logs.

Output Files

  • /tmp/lateframe.log
  • /tmp/lateframe-capture.pcap

About

No description, website, or topics provided.

Resources

Stars

11 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages