Comprehensive documentation of ttl features and options.
ttl supports three probe protocols:
ttl 8.8.8.8 -p icmpSends ICMP Echo Request packets. Most compatible but requires raw socket privileges.
ttl 8.8.8.8 -p udp
ttl 8.8.8.8 -p udp --port 33500 # Custom base port
ttl 8.8.8.8 -p udp --port 53 --fixed-port # Fixed port (DNS)Sends UDP packets to high ports. By default, the destination port increments per TTL to help with ECMP load balancing. Use --fixed-port to probe a specific service.
ttl 8.8.8.8 -p tcp
ttl 8.8.8.8 -p tcp --port 443 # Probe HTTPS
ttl 8.8.8.8 -p tcp --port 80 # Probe HTTPSends TCP SYN packets. Useful for tracing through firewalls that only allow specific ports.
ttl 8.8.8.8 # or: ttl 8.8.8.8 -p autoTries ICMP first, falls back to UDP, then TCP if raw sockets aren't available.
ttl 8.8.8.8 --flows 4
ttl 8.8.8.8 --flows 8 -p udp
ttl 8.8.8.8 --flows 4 --src-port 33000Discover multiple ECMP (Equal-Cost Multi-Path) routes by probing with different flow identifiers.
ECMP routers hash on the 5-tuple: (src_ip, dst_ip, src_port, dst_port, protocol). By varying the source port, each flow may take a different path through load-balanced routers.
- Each flow uses source port
base + flow_id - The TUI shows a "Paths" column when
--flows > 1 - Paths are highlighted when multiple responders are detected
ttl distinguishes two types of ECMP:
- Per-flow ECMP: Each flow consistently maps to one next-hop. Different flows take different paths. The Paths column shows unique flow primaries.
- Per-packet ECMP: Responses rotate between responders regardless of flow. The Paths column shows the actual number of observed responders.
Classification uses a primary concentration heuristic: if no single responder dominates a flow's responses (below 70% threshold), it's per-packet. The E indicator appears in the main table when ECMP is detected at a hop.
Note: --flows requires UDP or TCP probing (-p udp or -p tcp). ICMP probes have no port to vary, so flows are always 1 in ICMP mode. ttl warns at startup if --flows > 1 resolves to effective ICMP probing.
- Paris traceroute: Varies source port to enumerate paths
- Dublin traceroute: Also manipulates flow label (IPv6)
ttl implements Paris-style ECMP detection using source port variation.
ttl automatically detects NAT devices that rewrite source ports:
- Compares the source port sent vs returned in ICMP error payloads
- Displays "NAT" indicator in hop details when mismatch detected
- Useful for diagnosing carrier-grade NAT (CGNAT) or enterprise NAT
ttl --resolve-all google.com
ttl --resolve-all -6 cloudflare.com # Force IPv6
ttl --resolve-all example.com cdn.com # Multiple hostnamesTrace all IP addresses that a hostname resolves to. Useful for:
- Round-robin DNS: CDNs and load balancers often return multiple A records
- Dual-stack hosts: Compare IPv4 vs IPv6 paths to the same destination
- Anycast investigation: See if different IPs take different paths
- Resolves all A/AAAA records for each hostname
- Deduplicates by IP (merges hostnames that resolve to the same IP)
- Filters by IP family (uses IP family of the first resolved address)
- Shows skip count in status (e.g., "3 IPv6 skipped")
- Title bar shows
hostname -> IPwhen tracing a resolved hostname - Multiple hostnames resolving to same IP shown as
hostname (+N more) - Press
lto see all resolved targets with their stats
| Flag | Effect |
|---|---|
--resolve-all |
Enable multi-IP resolution |
-4 / --ipv4 |
Force IPv4 only (skip IPv6) |
-6 / --ipv6 |
Force IPv6 only (skip IPv4) |
ttl detects route instability when the primary responder IP changes at a hop:
- Main table shows
!indicator after hostname when route changes detected !is suppressed when ECMP is detected at the hop (Eshown instead) — per-packet load balancing is expected multi-path behavior, not instability- Hop detail view (Enter key) shows route change history with timestamps
- Uses hysteresis (margin of 2 responses) to avoid false positives
- Requires 5+ responses before recording changes (avoids startup noise)
- History capped at 50 changes per hop
- Only active in single-flow mode (disabled when
--flows > 1since ECMP expects path variation)
Route flaps can indicate:
- Unstable BGP routes
- Flapping links
- Load balancer issues
- Network convergence events
ttl --interface eth0 8.8.8.8
ttl --interface wlan0 1.1.1.1
ttl --interface eth0 --recv-any 8.8.8.8Bind probes to a specific network interface. Useful for:
- Multi-homed hosts with multiple uplinks
- VPN split tunneling testing
- Deterministic path selection
The --recv-any flag disables receiver socket binding, allowing asymmetric routing where replies arrive on a different interface.
When binding to an interface or when the source can be determined, the TUI title bar shows routing information:
ttl -- 8.8.8.8 -- eth0 (192.168.1.100 → 192.168.1.1) -- 100 probes
- Interface name (eth0, wlan0) - shown when
--interfaceis used - Source IP (192.168.1.100) - the local address used for probes
- Gateway (192.168.1.1) - the default gateway for the route
This helps verify which network path your probes are taking, especially useful on multi-homed systems or when testing VPN configurations.
ttl --size 1400 8.8.8.8 # Large packets for MTU testing
ttl --dscp 46 8.8.8.8 # EF (Expedited Forwarding)
ttl --dscp 34 8.8.8.8 # AF41 for video
ttl --dscp 46 --size 1400 8.8.8.8 # Combine bothControl probe packet size for MTU testing. Range: 36-9216 bytes for IPv4, 56-9216 for IPv6. Supports jumbo frames.
Set the DSCP (Differentiated Services Code Point) value in the IP header for QoS policy testing.
Common DSCP values:
| Value | Name | Use Case |
|---|---|---|
| 0 | Best Effort | Default |
| 46 | EF | VoIP, real-time |
| 34 | AF41 | Video conferencing |
| 26 | AF31 | Streaming media |
Verify with: sudo tcpdump -v -n icmp | grep tos
ttl --pmtud 8.8.8.8 # Standard ethernet (max 1500)
ttl --pmtud --jumbo 8.8.8.8 # Jumbo frame environments (max 9216)Discover the path MTU using binary search:
- Sends probes with Don't Fragment (DF) flag set
- Binary searches between min (68 for IPv4, 1280 for IPv6) and max (1500 or 9216)
- Uses ICMP "Fragmentation Needed" / "Packet Too Big" responses
- Results displayed in TUI title bar
By default, PMTUD uses 1500 bytes as the upper bound (standard ethernet MTU). Use --jumbo to search up to 9216 bytes for jumbo frame environments (data centers, 10GbE networks with 9000-byte MTUs).
PMTUD runs in the background after the destination is discovered, without interrupting normal tracing.
Note: IPv4 PMTUD is unavailable on NetBSD (no IP_DONTFRAG socket option). IPv6 PMTUD works normally.
ttl --pmtud 8.8.8.8 -c 50 --json > pmtud_results.jsonThe JSON output includes PMTUD state:
{
"pmtud": {
"min_size": 1400,
"max_size": 1400,
"current_size": 1400,
"successes": 0,
"failures": 0,
"discovered_mtu": 1400,
"phase": "Complete"
}
}Fields:
min_size: Lower bound (known to work)max_size: Upper bound (known to fail or untested)current_size: Size being tested in current binary search stepdiscovered_mtu: Final MTU whenphaseisCompletephase:WaitingForDestination,Searching, orComplete
ttl 8.8.8.8 # ASN lookup enabled
ttl 8.8.8.8 --no-asn # DisableQueries Team Cymru DNS for Autonomous System information. Displays AS number and organization name.
ttl 8.8.8.8 # rDNS enabled
ttl 8.8.8.8 --no-dns # Disable for faster startupParallel reverse DNS lookups for hop IP addresses.
ttl 8.8.8.8 --geoip-db /path/to/GeoLite2-City.mmdb
ttl 8.8.8.8 --no-geo # DisableShows city, region, and country for each hop. Requires a MaxMind GeoLite2-City database (free).
Setup:
-
Create a free MaxMind account at maxmind.com/en/geolite2/signup
-
Log in and go to Download Files in the left sidebar
-
Download GeoLite2 City (the
.mmdbfile, not CSV) -
Place the database file in one of these locations (checked in order):
~/.local/share/ttl/GeoLite2-City.mmdb # Linux ~/Library/Application Support/ttl/GeoLite2-City.mmdb # macOS ~/.config/ttl/GeoLite2-City.mmdb ./GeoLite2-City.mmdb # Current directory /usr/share/GeoIP/GeoLite2-City.mmdb # System-wide Linux /var/lib/GeoIP/GeoLite2-City.mmdb # System-wide Linux (alt)Or specify a custom path:
ttl 8.8.8.8 --geoip-db /path/to/GeoLite2-City.mmdb
Note: GeoIP is optional. Without the database, ttl works normally but won't show location data. MaxMind updates their database weekly; re-download periodically for accuracy.
ttl 8.8.8.8 # IX detection enabled (default)
ttl 8.8.8.8 --no-ix # DisableIdentifies Internet Exchange points in your path using PeeringDB data. When a hop's IP matches an IX peering LAN prefix, the hop detail view shows the IX name, city, and country.
How it works:
IX detection works out of the box with no configuration. On first use, ttl fetches IX prefix data from PeeringDB and caches it locally (~/.cache/ttl/peeringdb/ix_cache.json) for 24 hours.
API Key (optional but recommended):
Anonymous PeeringDB access has rate limits. For frequent use or scripting, set up an API key:
-
Create a free PeeringDB account at peeringdb.com/register
-
Log in and go to your profile (click username in top right)
-
Scroll to API Keys section and click Add API Key
-
Give it a name (e.g., "ttl") and copy the generated key
-
Configure the API key (choose one method):
Via Settings Modal (recommended):
- Press
sto open settings - Tab to the PeeringDB section
- Type your API key and press
Escto save - Key is saved to
~/.config/ttl/config.toml
Via environment variable:
# One-time use PEERINGDB_API_KEY=your_key_here ttl 8.8.8.8 # Persistent (add to ~/.bashrc or ~/.zshrc) export PEERINGDB_API_KEY="your_key_here"
Note: The environment variable takes precedence over the saved config.
- Press
Cache Status:
The settings modal shows PeeringDB cache status:
- Number of IX prefixes loaded
- Cache age (e.g., "3h ago")
- Expiry indicator when cache is older than 24 hours
- Press
rin the PeeringDB section to refresh the cache
Note: IX detection is optional. Without an API key, ttl uses anonymous access which works fine for occasional use. The API key just removes rate limiting for heavy usage.
Jitter measures RTT variance - the absolute difference between consecutive round-trip times.
| Metric | Description |
|---|---|
| Jitter (smoothed) | RFC 3550-style EWMA with 1/16 smoothing factor |
| Avg Jitter | Running mean of all jitter observations |
| Max Jitter | Largest single RTT change |
High jitter indicates path instability from congestion, route changes, or load balancing.
| Metric | Description |
|---|---|
| Loss % | Percentage of probes that timed out |
| Min/Avg/Max | RTT range across all samples |
| StdDev | Standard deviation (Welford's algorithm) |
| p50/p95/p99 | RTT percentiles from last 256 samples |
| Key | Action |
|---|---|
q / Ctrl+C |
Quit |
p / Space |
Pause/Resume |
r |
Reset all statistics |
t |
Cycle color theme |
w |
Cycle display mode (auto/compact/wide) |
s |
Open settings modal |
e |
Export current session to JSON |
? / h |
Show help dialog |
Tab / n |
Switch to next target |
Shift-Tab / N |
Switch to previous target |
l |
Open target list (multi-target mode) |
Up / k |
Move selection up |
Down / j |
Move selection down |
Enter |
Expand selected hop details |
Esc |
Close popup / Deselect |
| Replay | See Replay Controls for seek, speed, and position keys |
Press s to open the settings modal. Configure:
- Theme: Select from 11 built-in themes with live preview
- Display Mode: Control column widths (auto/compact/wide)
- PeeringDB: Configure API key and view cache status (only shown when IX detection is enabled)
| Key | Action |
|---|---|
Tab |
Switch between sections |
Up/Down or j/k |
Navigate within section |
Enter or Space |
Cycle option (theme/display mode) |
r |
Refresh PeeringDB cache (in PeeringDB section) |
Esc |
Close and save |
The display mode controls Host and ASN column widths:
| Mode | Description | Host Width | ASN Width |
|---|---|---|---|
| Auto (default) | Fits to content | 12-60 chars | 8-30 chars |
| Compact | Minimal widths | 20 chars | 12 chars |
| Wide | Generous widths | 45 chars | 24 chars |
Press w in the main view (or Enter in the Display Mode settings section) to cycle through modes. Auto mode is recommended for most use cases - it adapts to your content while respecting maximum caps to prevent layout issues.
When in the PeeringDB section, you can:
- Type your API key directly (text input with cursor support)
- View cache status: prefix count, age, and expiry indicator
- Press
rto refresh the cache from PeeringDB
Settings are saved to ~/.config/ttl/config.toml when exiting the TUI.
11 built-in themes available via --theme or t key:
| Theme | Description |
|---|---|
default |
Classic cyan borders |
kawaii |
Cute pastel colors |
cyber |
Neon cyan/magenta |
dracula |
Popular dark theme |
monochrome |
Grayscale only |
matrix |
Green on black |
nord |
Arctic blue tones |
gruvbox |
Retro warm colors |
catppuccin |
Soothing pastels |
tokyo_night |
City lights inspired |
solarized |
Precision readability |
Theme selection is persisted to ~/.config/ttl/config.toml.
ttl checks GitHub releases for new versions in a background thread at startup. The check is non-blocking and doesn't delay startup or probing.
- TUI banner: Yellow banner appears at the top of the screen when an update is available
- Dismiss: Press
uto dismiss the banner for the current session - Install-aware: The help overlay (
?) shows the appropriate update command based on how ttl was installed:- Homebrew:
brew upgrade ttl - Cargo:
cargo install ttl - Pre-built binary: link to GitHub releases
- Homebrew:
- Non-interactive mode: Update notice is printed to stderr after the run completes (JSON/CSV/report modes)
ttl 8.8.8.8 -c 100 --json > results.jsonFull session data including all hops, statistics, and enrichment.
ttl 8.8.8.8 -c 100 --csv > results.csvTabular format for spreadsheet analysis.
ttl 8.8.8.8 -c 100 --reportHuman-readable summary similar to mtr report mode.
ttl --replay results.json # Open in TUI (final state)
ttl --replay results.json --animate # Animated replay (10x speed)
ttl --replay results.json --animate --speed 1.0 # Real-time replay
ttl --replay results.json --report # Text reportLoad a previously saved JSON session for review. Use --animate to replay
the session showing hop-by-hop discovery as it happened.
During animated replay, a progress bar shows the current position, speed, and event count. The following controls are available:
| Key | Action |
|---|---|
p / Space |
Pause/resume |
Left / Right |
Seek ±0.5s |
[ / ] |
Seek ±5s |
+ / - |
Speed ±0.5x (0.5x–5.0x) |
Home |
Seek to start |
End |
Seek to end |
? |
Help (shows replay controls) |
Backward seeking rebuilds session state from scratch for correctness — fast even on long traces.
ttl [OPTIONS] <TARGETS>...
Arguments:
<TARGETS>... One or more target hostnames or IP addresses
Options:
-c, --count <N> Number of probe rounds (0 = infinite, default)
-i, --interval <S> Probe interval in seconds (default: 1.0)
-m, --max-ttl <N> Maximum TTL (default: 30, increase for long paths)
-p, --protocol <P> Probe protocol: auto, icmp, udp, tcp
--port <N> Base port for UDP/TCP probes
--fixed-port Use fixed port (no per-TTL variation)
--flows <N> Number of flows for ECMP (1-16, default: 1)
--src-port <N> Base source port for multi-flow (default: 50000)
--timeout <S> Probe timeout in seconds (default: 3)
--size <N> Packet size in bytes (36-9216)
--dscp <N> DSCP value for QoS testing (0-63)
--rate <N> Max probes per second (0 = unlimited)
--pmtud Enable Path MTU Discovery (max 1500)
--jumbo Enable jumbo frame detection (max 9216, requires --pmtud)
--source-ip <IP> Force specific source IP address
--interface <NAME> Bind probes to specific interface
--recv-any Don't bind receiver (asymmetric routing)
-4, --ipv4 Force IPv4
-6, --ipv6 Force IPv6
--resolve-all Trace all resolved IPs for hostnames
--wide Wide mode (expand columns for wider terminals)
--no-dns Skip reverse DNS lookups
--no-asn Skip ASN enrichment
--no-geo Skip geolocation
--no-ix Skip IX detection
--geoip-db <PATH> Path to MaxMind GeoLite2 database
--no-tui Streaming output mode
--report Batch report mode (requires -c)
--json JSON output (requires -c)
--csv CSV output (requires -c)
--replay <FILE> Replay a saved JSON session
--animate Animate replay (show probe-by-probe discovery)
--speed <N> Replay speed multiplier (default: 10.0, requires --animate)
--theme <NAME> Color theme
-h, --help Print help
-V, --version Print version
Pre-built binaries are available from GitHub Releases. Each release includes a SHA256SUMS file for verification.
curl -LO https://github.com/lance0/ttl/releases/latest/download/ttl-x86_64-unknown-linux-musl.tar.gz
curl -LO https://github.com/lance0/ttl/releases/latest/download/SHA256SUMS
sha256sum -c SHA256SUMS --ignore-missingcurl -LO https://github.com/lance0/ttl/releases/latest/download/ttl-aarch64-apple-darwin.tar.gz
curl -LO https://github.com/lance0/ttl/releases/latest/download/SHA256SUMS
shasum -a 256 -c SHA256SUMS --ignore-missingAvailable targets:
x86_64-unknown-linux-musl- Linux x86_64aarch64-unknown-linux-gnu- Linux ARM64aarch64-apple-darwin- macOS Apple Siliconx86_64-apple-darwin- macOS Intel