LFT(8)

okay bestie, buckle up 🫢 you're reading the manual page for lft(8), from the System Manager's Manual, and it is a lot in the best way. drink some water first. πŸ’§


πŸŽ€ NAME πŸŽ€

lft β€” display the route packets take to a network host/socket using one of several layer-4 protocols and methods; optionally show heuristic network information in transitu. ✨


πŸ¦‹ SYNOPSIS πŸ¦‹

lft [-d dport] [-s sport] [-m retry min] [-M retry max] [-a ahead]
    [-c scatter ms] [-t timeout ms] [-l min ttl] [-H max ttl]
    [-L length] [-q ISN] [-D device] [-f device] [-G icons path]
    [-ACEINPRSTUVbeghinpruvxyzK] [<gateway> <...>] target:dport

πŸ’– DESCRIPTION πŸ’–

Okay so picture this: the Internet is a large and complex aggregation of network hardware, connected together by gateways. Tracking the route one's packets follow (or finding the miscreant gateway that's discarding your packets) can be difficult. (from traceroute(8)) β€” iconic quote, no notes. 😭

lft was developed to automate a solution to the above, taking into account that modern networks contain many configurations of load balancers, proxies, and stateful firewalls. like, the vibes out there are chaotic.

lft implements numerous network tracing methods and strategies. Generally, lft sends various types of layer-4 probes utilizing the IP protocol time to live field and attempts to elicit an ICMP time exceeded in transit response from each gateway along the path to some host. RFC 1393 Traceroute Using an IP Option is also available as one of several tracing methods. ✨

lft additionally listens for various messages along the way to assist network managers in ascertaining per-protocol heuristic routing information. lft can optionally retrieve various information about the networks it traverses using a variety of sources such as registries, routing arbiters, etc. detective mode activated πŸ•΅οΈ

The only mandatory parameter is the target host name or IP number. Options toggle the display of more interesting data or change the variables of the trace itself. The (-E/-e) adaptive option tries several combinations of TCP states (changing flags inside the probes it sends) in order to improve the chances of a successful trace and expose stateful packet filters. πŸ’…

Other options are:


πŸ’… OPTIONS πŸ’…

  • -d dport, --dport dport β€” sets dport as the destination TCP port of the probes LFT generates. Default is 443. This option is useful to see if packets follow a different route based on protocol destination β€” a likely scenario when load balancers or proxies are involved. It may also bypass less sophisticated packet filter configurations. πŸ‘€
  • -s sport, --sport sport β€” sets sport as the origin TCP port of every probe. This overrides the default per-hop source port and -z, fixing one source port for the whole trace. By default, LFT selects source ports from the IANA dynamic port range (49152–65535).
  • -z, --random-sport β€” use one consistent random source port for the entire trace instead of the per-hop default. All TTLs then share a single 5-tuple, keeping the trace on one ECMP path. πŸ›€οΈ

By default (neither -s nor -z), one-shot TCP traces use a distinct source port per hop, so each TTL is a separate 5-tuple. This prevents a stateful firewall from treating the trace as one flow and rate-limiting it; the trade is that hops may traverse different ECMP paths. In watch mode (-W) the source port is instead held constant within each cycle and rotated between cycles, preserving per-cycle ECMP consistency while still presenting a fresh flow each cycle. Per-hop variation applies to TCP only.

  • -m min, --min-probes min β€” set min as the minimum number of probes to send per hop. Default is 1.
  • -M max, --max-probes max β€” set max as the maximum number of probes to send per hop. Default is 2.
  • -a ahead, --ahead ahead β€” set ahead as the number of hops forward to query before waiting for a response. Default is 5.
  • -c scatter ms, --scatter scatter ms β€” set scatter ms as the minimum number of milliseconds to wait between sending probes. Default is 20. πŸ“
  • -t timeout ms, --timeout timeout ms β€” set timeout ms as the maximum number of milliseconds to wait before assuming a probe was lost/discarded. Default is 250. ⏳
  • -l min ttl, --min-ttl min ttl, --first-hop min ttl β€” set min ttl as the minimum TTL (time-to-live) on outgoing probes (essentially, the first hop in the line that you want to display). Default is 1.
  • -q ISN, --isn ISN β€” set ISN as the ISN (initial sequence number) of the first probe. If unset, one will be automatically generated using a pseudo-random, time-seeded algorithm. 🎲
  • -w window, --tcp-window window β€” set window as the TCP window size advertised in outgoing probe packets, in bytes. This option applies to TCP-based traces only. Default is 32768.
  • -B, --tcp-options β€” attach a realistic client TCP option set to SYN probes so they resemble a SYN sent by a real operating system rather than a bare, option-less one. By default lft sends TCP probes with no options; some firewalls that normalize or scrub TCP (for example pf(4) with scrub), as well as certain next-generation firewalls, treat an option-less SYN as anomalous and silently drop or reset it, causing a trace to stall near the source even though the target is reachable from an ordinary client. With -B, each SYN probe carries a common client option layout β€” Maximum Segment Size, SACK-permitted, Timestamps, and Window Scale β€” making the probe blend in with legitimate traffic. undercover mode 🧚

This affects the default and adaptive (-E) TCP engines only; it is ignored for basic (-b), UDP (-u), and ICMP (-p) traces, and for non-SYN probes. Reply correlation is unchanged, because the sequence number remains ahead of the options in the header. This behavior is off by default; enabling it does not change lft's output, only the packets on the wire.

  • -L length, --length length, --bytes length β€” set length as the size of probe packets in bytes. This includes layer-3 and layer-4 headers, but does not include layer-2 headers. For example, setting the length to 1500 would create a 1500-byte probe packet which would result in a 1514-byte frame on an Ethernet network. This option is silently ignored when -K is active; probe size is managed automatically by PMTUD. πŸ“
  • -K, --mtu, --path-mtu β€” enable active Path MTU Discovery (PMTUD). When this flag is set, lft queries the local interface MTU via ioctl(2) SIOCGIFMTU and starts probing at that size (typically 1500 bytes for Ethernet, up to 9000 for jumbo frames). All probes carry the DF (Don't Fragment) bit, which is already lft's default. πŸ›°οΈ

When a router cannot forward a probe because it exceeds an outbound link MTU, it returns an ICMP β€œfragmentation needed” message (type 3, code 4) containing the next-hop MTU. This class of message is colloquially called a β€œPTB” (Packet Too Big), a term that strictly refers to ICMPv6 Type 2 but is widely used for both IPv4 and IPv6. lft records the MTU at the responding hop and reduces subsequent probe sizes accordingly.

If a router silently drops an oversized DF probe without returning an ICMP message (a PMTUD black hole), lft flags the hop as [MTU Limit] and steps down to the next RFC 1191 plateau value to continue probing beyond the black hole. πŸ’€

PMTUD measures the forward-path MTU only; the return path may differ. Discovery stops at the first hop that limits the path MTU; hops beyond it see reduced-size probes that pass freely.

Probe design and known limitations. lft implements PMTUD by inflating the payload of its existing TCP, UDP, and ICMP probes to the current MTU estimate. For TCP probes, strictly speaking, SYN, FIN, and RST segments should carry no payload; some deep-packet-inspection firewalls enforce this and will drop oversized probes before they reach a bottleneck link. UDP datagrams and ICMP echo requests may carry any payload size without protocol violation. This is a known constraint of the technique for TCP mode. 😭

An alternative approach β€” sending ICMP echo requests exclusively for MTU probing, as ping(8) does with -M do β€” was considered but rejected. ICMP traffic is routinely deprioritized in router control planes and discarded wholesale by enterprise firewalls, making it a less faithful representation of real traffic and no more likely to traverse a restricted path than a TCP probe.

The payload-inflation approach is preferred because it exercises the same forwarding path as real application traffic. PTB messages are generated by routers at the IP layer, before any TCP inspection occurs, so the bottleneck is still correctly identified even when the probe carries an atypical payload. On the public Internet backbone β€” where MTU constraints most commonly arise β€” core routers perform IP-layer forwarding without TCP payload inspection, and the technique works reliably. Paths that traverse DPI firewalls configured to reject TCP-with-payload would equally reject any active PMTUD probing strategy; this is not a limitation specific to lft. πŸ‘‘

  • -D device, --device device, --receive-device device β€” set device as the network device or address to receive traffic. (e.g., "en1" or "1.2.3.4") If unset, lft will attempt to determine and acquire the appropriate interface based on routing. πŸ“₯
  • -f device, --from-device device, --source-device device β€” set device as the network device or address to transmit traffic. (e.g., "en1" or "1.2.3.4") If unset, lft will attempt to determine and acquire the appropriate interface based on routing. This serves to operate lft in a passive mode where you may transmit from a (potentially) spoofed IP address on one interface, yet receive on another. This allows you to trace from a different IP address whose traffic you can see in order to intercept replies. 🎭
  • -H ttl, --max-hops ttl, --ttl-max ttl β€” set ttl as the maximum TTL, essentially the maximum route traversal distance in hops. Default is 30.
  • -G icons path, --graphviz-icons icons path β€” set icons path as the path to GraphViz icons in connection with GraphViz output. 🎨
  • -I, --minimize-delay β€” Sets the ToS (Type of Service) bit on outgoing IP datagrams. The ToS gets set to the differentiated services request minimize-delay. basically β€œmake it snappy” energy ⚑
  • -i, --ignore-unreachables β€” Ignores ICMP unreachable messages; the trace keeps going through firewalls and other devices that send administratively-prohibited or host/net unreachable responses. By default, LFT stops the trace when it gets any of these ICMP unreachable codes: net unreachable (0), host unreachable (1), protocol unreachable (2), port unreachable (3), net administratively prohibited (9), host administratively prohibited (10), or communication administratively prohibited (13). This flag is implied by adaptive mode (-E).
  • -n, --numeric β€” Prints addresses numerically instead of symbolically and numerically. Disables use of the DNS resolver completely. no names, just numbers, we love a no-nonsense queen πŸ’…
  • -h, --hostnames-only β€” Prints addresses symbolically rather than symbolically and numerically. If the DNS resolver fails to resolve an address, that address gets printed numerically. so if DNS ghosts you, you get the digits anyway 😭
  • -E | -e, --adaptive β€” Enables use of the adaptive engine, which tries several combinations of TCP states (changing flags inside the probes it sends) to improve the chances of a successful trace. The engine also shows other useful stuff like stateful inspection firewalls or broken IP stacks it meets along the way. Adaptive mode automatically implies -i (--ignore-unreachables), letting the trace continue through devices that send ICMP unreachable responses instead of halting. glow-up mode fr ✨
  • -F, --fin β€” Enables use of TCP packets with the FIN flag set. This strategy fools unsophisticated packet filters that don't maintain a proper state table. Those devices will forward the packet to its destination instead of filtering it, assuming a handshake has already happened and the probes are part of an existing and valid TCP stream. sneaky but make it fashion πŸŽ€
  • -u, --udp β€” Enables use of UDP-based probes instead of TCP-based probes. Unlike the traditional traceroute method, which increments the UDP destination port with each TTL to identify replies, LFT sends all UDP probes to the same destination port and embeds a pseudorandom session-unique identifier in the IP ID field of each probe. Replies are matched to probes via the ID value echoed back in ICMP time-exceeded messages, keeping the five-tuple (source IP, destination IP, protocol, source port, destination port) constant across all TTLs. This makes sure every probe follows the same path through ECMP load-balanced networks. Many of LFT's other options (such as source and destination port selection) are still available. By default, LFT's UDP probes carry a small payload (unlike TCP probes, which carry none). ECMP said β€œsame path, bestie” πŸ¦‹
  • -N, --netname β€” Enables lookup and display of network or AS names (e.g., [GNTY-NETBLK-4]). This option queries Prefix WhoIs, RIPE NCC, or the RADB (as requested). If it's Prefix WhoIs or RADB, the network name gets displayed. If it's RIPE NCC, the AS name gets displayed. name drop slay πŸ‘‘
  • -P β€” Enables RFC 1393 tracing method using ICMP and an IP option. RFC 1393 (Traceroute Using an IP Option), published in January 1993, defined an elegant alternative to the traditional Van Jacobson traceroute method: rather than relying on TTL expiry and ICMP Time Exceeded messages, a single outbound packet carries a Traceroute IP option (type 82) that instructs each router in the path to send an ICMP Traceroute message back to the originator. In theory, this requires only n+1 packets instead of the 2n packets used by traditional traceroute.

In practice, no major router vendor β€” Cisco, Juniper, Nokia, Huawei, or any other β€” ever implemented RFC 1393 in their forwarding path. The mechanism had a chicken-and-egg problem: it required every router in the path to support the new IP option, whereas Van Jacobson's 1987 traceroute exploited TTL-expiry behavior that was already present in all routers and was universally deployed by the time RFC 1393 arrived. IP options processing also carries security risks, since options are handled in the router's slow path (control plane) and can be exploited for DoS amplification. Over time, ISPs and enterprises increasingly configured equipment to strip or drop packets carrying IP options entirely.

RFC 1393 never advanced beyond Experimental status. It was formally deprecated by RFC 6814 in November 2012, which moved it to Historic. RFC 7126 (February 2014) further recommends that routers SHOULD drop packets containing the Traceroute IP option, noting zero operational impact from doing so. This option is retained in lft for completeness, but it's unlikely to get responses from any production network equipment. history lesson but make it sad πŸ’€

  • -p, --icmp β€” Enables use of ICMP-based probes instead of TCP-based probes. This strategy is sometimes the fastest, but firewalls commonly filter ICMP at network borders. ICMP probes are echo request (ping) packets. ping ping bestie πŸ“
  • -b, --basic-tcp β€” Enables TCP basic tracing method. Unlike the default method, the basic method generates TCP probes without relying on sequence numbers being conveyed correctly. This makes LFT more compatible with networks employing NAT, but it's slower than the default method. TCP basic can also be used with adaptive mode (-E). NAT-friendly realness 🌸
  • -A, --asn β€” Enables lookup and display of AS (autonomous system) numbers (e.g., [1]). This option queries one of several whois servers (see options 'C' and 'r') to figure out the origin ASN of the IP address in question. By default, LFT uses the pWhoIs service, whose ASN data tends to be more accurate and more timely than using the RADB because it's derived from the Internet's global routing table. See www.pwhois.org. ASN tea β˜•
  • -r, --ripe-riswhois β€” Forces use of the RIPE NCC RIS whois service to lookup ASNs. This is an alternative source of timely ASN-related information built using the Internet's global routing table. See www.ripe.net/projects/ris. RIPE vibes 🌈
  • -C, --cymru β€” Forces use of the Cymru whois service to lookup ASNs. This is an alternative source of timely ASN-related information built using the Internet's global routing table. See www.cymru.com. Cymru crew πŸ“
  • -R, --radb β€” Forces use of the RADB whois service to lookup ASNs. This tends to be quick, but incomplete and usually inaccurate with regard to the 'actual' Internet routing table. See www.radb.net. quick but messy, we've all been there πŸ™ˆ
  • -T, --time-keeping β€” Enables display of LFT's execution timer. This option places timers on the trace itself and on lookups and name resolution to show where LFT is spending its time, waiting on resolvers, or processing trace packets. Use with -V (verbose) to display additional detail. time check bestie ⏰
  • -U, --time-keeping-utc β€” Displays all times in UTC/GMT0. This option also enables the -T option automatically. UTC core πŸ•°οΈ
  • -S, --quiet, --silent β€” Suppresses display of the real-time status bar. This option makes LFT show its completed trace output only, no-frills. quiet luxury πŸ’…
  • -x, --xml β€” Enables XML output and suppresses all other output to stdout. XML era πŸ“„
  • -g, --graphviz β€” Enables GraphViz output and suppresses all other output to stdout. GraphViz glam πŸ“Š
  • -o, --ascii β€” Displays the completed trace as a horizontal ASCII art hop diagram. Each hop occupies a column showing (top to bottom): the hop header, hostname, IP address, AS number (when -A is used), network name (when -N is used), round-trip time, and a feature annotation line for seam, firewall, or target-state information.

In -n mode the IP address appears on both the hostname and address lines for layout consistency. In -h mode the IP address line is suppressed; TTL information for cloaked hops moves to the AS number line so it shares a row with AS data from other hops instead of occupying a mostly-blank line of its own.

Cloaked hops, seam boundaries, firewall anomalies, and target state are each marked with a distinct feature label. In Unicode mode the labels are: β€˜β‡Ά Stateful FW’ (stateful packet-inspecting firewall), β€˜βš‘ Flag FW’ (flag-based packet filter), β€˜β‡„ BSD Bug’ (errant TTL reuse anomaly), β€˜β•³ ASN Seam’ (autonomous system boundary crossing), β€˜β•³ NET Seam’ (network boundary crossing), β€˜β–‘cloaked░’ (no-reply hop), β€˜[OPEN]’, β€˜[FILTERED]’, and β€˜[CLOSED]’ (target port state). Plain ASCII equivalents are used on terminals that don't support Unicode.

LFT automatically enables Unicode box-drawing characters and ANSI color when the terminal supports them (detected via TERM, COLORTERM, and isatty(3)). Set NO_COLOR to suppress color output. When the path is too wide for the terminal, the diagram wraps onto additional rows. ASCII art but make it fashion 🎨

  • -j N, --stats N β€” Collects N round-trip time samples per hop and displays a candlestick RTT chart above the hop diagram (implies -o). Each hop column renders a box-and-whisker bar: the solid block marks the average RTT; the top cap (┬) and bottom cap (β”΄) mark the maximum and minimum RTTs; the thin shaft (β”‚) between them shows the spread. Hops with no reply are rendered as a full-width column of shade blocks (β–‘). A Y-axis with labeled RTT ticks appears at the left edge; the X-axis footer shows the end-to-end RTT range derived from the target hop's own min/max samples and the destination in proto://address:port format (e.g. β€˜End-to-End RTT 15–81ms, 6 hops to tcp://208.74.248.40:443’). If the target was not reached the footer reads β€˜N hops traced, target unreachable’. Columns with a jitter spread exceeding 25 ms are highlighted in yellow with a β€˜βš‘ Nms Δ’ annotation. stats queen πŸ‘‘
  • --no-ascii-chart, --no-async-chart β€” bestie, this one just mutes the candlestick RTT chart that -j draws, but you're STILL collecting and showing all the per-hop RTT stats. ✨ Perfect when your terminal is criminally narrow, when you're piping output into another tool, or when you literally only want the numbers (min/avg/max/jitter). Heads up tho: it does absolutely nothing unless you also pass -j N. πŸ’…
  • -W N, --watch[=N, --continuous[=N]] β€” okay this is the main character option. πŸ‘‘ It turns on continuous monitoring mode, re-running the trace every N seconds (default: 5). LFT goes full interactive, full-screen ncurses(3) mode, showing per-hop RTT history, loss percentages, and network annotation (ASN, network name, organisation) live, in real time. No refresh, no re-typing, no stress. πŸ¦‹

Every cycle after the first auto-tunes the probe range and timeouts from the path it already learned β€” go read Topology inheritance below, it's giving smart-girl energy.

View 1 (the default) puts the -o candlestick chart on the top half of the screen with a rolling statistics table underneath it. View 2 swaps the chart for proportional RTT bar columns. View 3 is a full-height statistics table so you can see way more hops at once. Switch between them with 1, 2, or 3. Obsessed. πŸ’–

Here's what every column in that statistics table means:

  • HOP β€” the TTL value.
  • HOST β€” the IP address of the router that responded (or the PTR hostname when -h is active; see below).
  • LOSS% β€” packet loss percentage across every cycle observed.
  • MIN / AVG / MAX β€” minimum, mean, and maximum round-trip time, in milliseconds.
  • JIT β€” jitter, which is max - min over all the cycles.
  • ASN β€” the autonomous system number from pWhoIs (like 7922). Private and special-purpose addresses (RFC 1918, RFC 6598, loopback, link-local, etc.) get labelled with their IANA RFC tag instead (like RFC1918) β€” no pWhoIs lookup for those, bestie.
  • NETNAME β€” network name from pWhoIs (like COMCAST-7922).
  • ORGNAME β€” organisation name from pWhoIs (like Comcast Cable).
  • Sparkline β€” per-hop RTT history plotted as Unicode block characters, oldest sample on the left, newest on the right. Green means RTT is within 20% of the hop average; yellow is between 20% and 50% above average; magenta is more than 50% above average; red means a lost probe. 🌈

Intermediate hops that don't answer show up as * in the HOST column. Iconic behaviour, honestly.

If you didn't pass -j, each cycle collects 3 RTT samples per hop.

During every trace there's a live counter line showing, in order: replies received (↓N), probes sent (↑N), unique responding hops (⬑ N), and target reached status (βœ” or βœ—). Before ncurses initialises, that line just appears on the terminal directly; once ncurses is running, it lives on the bottom status row of every single view. πŸ’€

Keyboard controls β€” memorize these, they're the whole vibe:

  • 1 – 3 β€” switch between the three display views.
  • p β€” pause or resume trace cycling. While paused the status bar says β€œPaused...” and no new traces run until you hit p again.
  • r β€” reset ALL accumulated statistics (RTT history, loss counters, cycle count) and restart from a clean baseline.
  • c β€” clear every statistic, flush the DNS and pWhoIs cache, and redraw. Forces fresh lookups on the next cycle.
  • w β€” toggle the pWhoIs annotation columns (ASN / NETNAME / ORGNAME). Annotation is on by default; a bulk pWhoIs query runs at startup and refreshes as new hop addresses get discovered across cycles. This key shows or hides the annotation columns without throwing away the cached data.
  • h β€” toggle hostname display: when it's on, the HOST column shows each hop's PTR reverse-DNS name instead of its IP address.
  • v β€” open or close the log drawer, a panel at the bottom of the screen showing the raw LFT verbose output captured during each trace cycle.
  • x β€” save the current screen to a file. LFT prompts you for a filename (pre-filled with a timestamp) and writes both a plain-text .txt version and an ANSI-color .ansi version to the current directory.
  • + / - β€” bump the interval between cycles up or down by one second.
  • q β€” quit watch mode and exit.

Path changes get detected automatically with a heuristic built to alert you only on genuine reroutes. During an initial learning period β€” until the path length has been stable for three cycles, or eight cycles at most β€” everything is absorbed silently. 🫢 Once it's armed, a changed hop address has to persist for two observed cycles before it gets announced (that's 2-cycle hysteresis); the confirmed change is highlighted in the status bar for three seconds and logged to the log drawer. A change that reverts after a single cycle gets recorded in the log drawer as a flap, and a hop alternating among addresses previously seen at the same TTL gets recorded as an ECMP shift (that's multipath load-balancing, capped at three log lines per hop) β€” neither of those raises the status-bar alert. Path length changes with no mid-path substitution are also only noted in the log drawer. Cloaked cycles (a hop temporarily showing *) neither advance nor cancel a pending change. And the c key wipes the learned path state and drops you back into the learning period. ✨

For multi-cycle traces, watch mode keeps the per-protocol ECMP flow-identifier alive across cycles so every cycle traverses the same ECMP branch on multi-path networks: TCP source port, UDP IP-ID base, and the ICMP sequence-number anchor are all held constant for the whole life of the watch session. Want the underlying mechanism? See the lft(8) notes on ECMP flow-hash stability. 🧚

🧬 Topology inheritance

After the first cycle, watch mode learns the path length and the RTT envelope and uses them to speed up the cycles that follow. Once the same number of hops has been observed for three consecutive cycles, the TTL probe range narrows to learned_num_hops + --learn-margin, the in-flight probe pipeline widens to the full known path width (capped at 16), and the cloaked-hop timeout drops to max(8 Γ— max_observed_rtt, 200 ms). A cycle that misses the target, or a change in probe protocol, resets the learned state β€” back to broad-scan defaults until the path stabilizes again. πŸ’…

  • --no-learn β€” disable watch-mode topology inheritance. Every cycle then probes the full default TTL ladder with the conventional ahead-limit and timeout.
  • --learn-margin N β€” the TTL probe margin above the learned path length (default 2; clamped 0 .. 10). A bigger margin notices the path lengthening sooner, but the price is probing empty TTLs.
Okay so this whole option needs ncurses support compiled into LFT. If ncurses wasn't detected at build time, --watch prints an explanatory error and exits. The minimum supported terminal height is 24 rows. And yes bestie, superuser privileges (via sudo(8) or a setuid-root installation) are required. πŸ”₯
  • -y, --verify-seam β€” turns on network seam testing, in connection with GraphViz output. πŸŽ€
  • -V, --verbose β€” displays verbose output. Use more V's for more info. (Yes, literally just stack them. It's that kind of energy.)
  • -v, --version β€” displays version information, then exit(1).
  • -Q, --no-async-dns β€” disables asynchronous DNS resolution. When LFT is built with c-ares support (the default when the library is present at compile time), it sends a reverse-DNS (PTR) query for each IP address revealed by an incoming ICMP time-exceeded response at the same time as the rest of the probe traffic. By the time the trace finishes, most or all hostnames are already resolved, so lookups cost little to no extra time. ✨ This flag takes you back to the traditional synchronous getnameinfo(3) path, which resolves each hop one at a time after the trace completes. Super handy for debugging DNS stuff, or for environments where parallel DNS queries are a no-go. If LFT was built without c-ares, this flag is accepted but has no effect. πŸ™ˆ
  • --help β€” displays a brief usage summary and exits. Long-form equivalents (--dport, --min-probes, --ignore-unreachables, etc.) exist for all short options and are documented in this manual page right alongside their short counterparts. πŸ’œ

Any hosts you list after these options and before the final target host become a Loose Source and Record Route (LSRR). 🌸 lft encodes those intermediate addresses into the IP options field of every probe, using option type 131 as defined in RFC 791 (the original IPv4 specification).

LSRR on the modern Internet. The original point of source routing was to let a sender dictate the path a packet takes through the network β€” genuinely useful in early research networks for debugging routing policy and deliberately routing around link failures. But source routing can be exploited to bypass access controls, run reflective attacks, and probe otherwise-isolated network segments. As a consequence, virtually all ISPs and enterprise network operators block or silently strip packets carrying source route options at network ingress. RFC 7126 (February 2014) formally codified this, recommending that routers SHOULD drop packets containing LSRR or SSRR IP options, and noting there's zero operational impact from doing so, because no production traffic legitimately uses them. πŸ’€

Why LSRR is retained in lft. It's still here for controlled network environments where the intermediate devices are operator-managed and IP options processing is known to be enabled: lab networks, private AS testing, research testbeds, and similar settings. In those places, specifying intermediate waypoints can verify specific forwarding paths that are otherwise a nightmare to isolate. On any public Internet path, though? It'll produce no additional hops in the output. No cap. 🫢

Caution time, bestie β€” this one is lowkey scary πŸ‘€. Because -p (the ICMP probe mode) takes no argument, literally any extra token that shows up between -p and the target gets silently parsed as an LSRR intermediate hop instead of being flagged as an error. Not a warning. Just absorbed. Like it never happened. 🫠

So say you type:

lft -p 192.168.1.1 target.example.com

boom β€” 192.168.1.1 just became a source route waypoint</>, and a 12-byte LSRR IP option gets embedded into every single probe. Practically every modern router will just… not respond. πŸ™ˆ And here's the evil part: the trace will look like it's running completely normally, but every hop is cloaked. Iconic in the worst way. So verify your command syntax carefully whenever you're in ICMP mode. Fr fr. πŸ’€


✨ EXAMPLES ✨ πŸ“

A sample use and output might be:

[edge.lax]$ lft -S 4.2.2.2

Hop  LFT trace to vnsc-bak.sys.gtei.net (4.2.2.2):80/tcp
 1   ln-gateway.centergate.com (206.117.161.1) 0.5ms
 2   isi-acg.ln.net (130.152.136.1) 2.3ms
 3   isi-1-lngw2-atm.ln.net (130.152.180.21) 2.5ms
 4   gigabitethernet5-0.lsanca1-cr3.bbnplanet.net (4.24.4.249) 3.0ms
 5   p6-0.lsanca1-cr6.bbnplanet.net (4.24.4.2) 3.4ms
 6   p6-0.lsanca2-br1.bbnplanet.net (4.24.5.49) 3.3ms
 7   p15-0.snjpca1-br1.bbnplanet.net (4.24.5.58) 10.9ms
 8   so-3-0-0.mtvwca1-br1.bbnplanet.net (4.24.7.33) 11.1ms
 9   p7-0.mtvwca1-dc-dbe1.bbnplanet.net (4.24.9.166) 11.0ms
10   vlan40.mtvwca1-dc1-dfa1-rc1.bbnplanet.net (128.11.193.67) 11.1ms
**   [neglected] no reply packets received from TTLs 11 through 20
**   [4.2-3 BSD bug] the next gateway may errantly reply with reused TTLs
21   [target] vnsc-bak.sys.gtei.net (4.2.2.2) 11.2ms

The -S option got used to suppress the real-time status bar so the output stays clean. πŸ’…

And those ** notifiers chilling between hops 10 and 21? They're LFT handing you extra useful info, not just decoration:

  • the first one is a [neglected] indicator β€” it's telling you that none of the probes sent with those TTLs pulled a response. Could be a bunch of reasons!
  • the second one is your informative</> one: it's saying this is probably the result of a bug in the 4.[23] BSD network code (and all its derivatives). Here's the tea: BSD 4.x (where x < 3) sends an unreachable message using whatever TTL is left over in the original datagram. And since the remaining TTL is zero for gateways, the ICMP "time exceeded" is guaranteed to never make it back to us. LFT does its best to spot that condition instead of printing loads and loads of hops that don't exist (all while chasing a high enough TTL). Queen behaviour πŸ‘‘

Now, using the adaptive engine option:

[edge.lax]$ lft -E -S 4.2.2.1

Hop  LFT trace to vnsc-pri.sys.gtei.net (4.2.2.1):80/tcp
 1   ln-gateway.centergate.com (206.117.161.1) 0.5/0.5ms
 2   isi-acg.ln.net (130.152.136.1) 2.1/2.3ms
 3   isi-1-lngw2-atm.ln.net (130.152.180.21) 2.6/7.1ms
 4   gigabitethernet5-0.lsanca1-cr3.bbnplanet.net (4.24.4.249) 6.1/3.9ms
**   [firewall] the next gateway may statefully inspect packets
 5   p0-0-0.lsanca1-csr1.bbnplanet.net (4.24.4.10) 155.4/3.7ms
 6   [target] vnsc-pri.sys.gtei.net (4.2.2.1) 22.6/3.7/*/*/*/*/*ms

In the scenario above, the adaptive engine was able to identify a stateful, packet-inspecting firewall in the path. 🧚✨

Another example with more options:

[edge.lax]$ lft -S -A -T -m 2 -d 80 -s 53 www.yahoo.com

Hop  LFT trace to w9.scd.yahoo.com (66.218.71.88):80/tcp
 1   [226] ln-gateway.centergate.com (206.117.161.1)  1 ms
 2   [226] isi-acg.ln.net (130.152.136.1)  2 ms
 3   [226] isi-1-lngw2-atm.ln.net (130.152.180.21)  3 ms
 4   [1] gigether5-0.lsanca1-cr3.bbnplanet.net (4.24.4.249)  3 ms
 5   [1] p6-0.lsanca1-cr6.bbnplanet.net (4.24.4.2)  5 ms
 6   [1] p6-0.lsanca2-br1.bbnplanet.net (4.24.5.49)  3 ms
 7   [1] p1-0.lsanca2-cr2.bbnplanet.net (4.25.112.1)  3 ms
 8   [16852] pos4-0.core1.LosAngeles1.Level3.net (209.0.227.57)  3 ms
 9   [3356] so-4-0-0.mp1.LosAngeles1.Level3.net (209.247.10.193)  3 ms
10   [3356] so-3-0-0.mp2.SanJose1.Level3.net (64.159.1.130)  11 ms
11   [3356] gige10-0.ipcolo4.SanJose1.Level3.net (64.159.2.42)  11 ms
12   [3356] cust-int.level3.net (64.152.81.62)  52 ms
13   [10310] vl17.bas2.scd.yahoo.com (66.218.64.150)  53 ms
14   [10310] w9.scd.yahoo.com (66.218.71.88) [target]  54 ms

LFT's trace took 5.23 seconds.  Resolution required 3.58 seconds.

Note that the -Ar above displays ASNs using the RADB as a whois source. A better option may have been to use -A alone, or maybe -AC. Just saying πŸ‘€

And why not request netblock lookups?

[edge.lax]$ lft -S -N www.microsoft.com

Hop  LFT trace to www.us.microsoft.com (207.46.197.113):80/tcp
 1   [LOS-NETTOS-BLK4] ln-gateway.centergate.com (206.117.161.1)  2 ms
 2   [LOS-NETTOS] isi-acg.ln.net (130.152.136.1)  3 ms
 3   [LOS-NETTOS] isi-1-lngw2-pos.ln.net (130.152.80.30)  5 ms
 4   [GNTY-4-0] gigether5-0.lsanca1-cr3.bbnplanet.net (4.24.4.249)  4 ms
 5   [GNTY-4-0] p6-0.lsanca1-cr6.bbnplanet.net (4.24.4.2)  3 ms
 6   [GNTY-4-0] p6-0.lsanca2-br1.bbnplanet.net (4.24.5.49)  3 ms
 7   [GNTY-4-0] p15-0.snjpca1-br1.bbnplanet.net (4.24.5.58)  10 ms
 8   [GNTY-4-0] p9-0.snjpca1-br2.bbnplanet.net (4.24.9.130)  11 ms
 9   [GNTY-4-0] so-1-0-0.sttlwa2-br1.bbnplanet.net (4.0.3.229)  27 ms
10   [GNTY-4-0] so-0-0-0.sttlwa1-hcr1.bbnplanet.net (4.24.11.202)  28 ms
11   [GNTY-4-0] so-7-0-0.sttlwa1-hcr2.bbnplanet.net (4.24.10.234)  28 ms
12   [GNTY-4-0] p1-0.sttlwa1-cr2.bbnplanet.net (4.24.10.241)  29 ms
13   [GNTY-4-0] p2-0.msseattle.bbnplanet.net (4.25.89.6)  32 ms
14   [MICROSOFT-GLOBAL-NET] 207.46.154.9  32 ms
15   [MICROSOFT-GLOBAL-NET] 207.46.155.17  33 ms
16   [MICROSOFT-GLOBAL-NET] 207.46.129.51 [prohibited]  35 ms

πŸ”§ TROUBLESHOOTING πŸ”§ πŸ’œ

If your traces don't appear to go anywhere, there are a number of things to try. Bestie, let's diagnose 🩺

If you're getting an error related to permissions: yeah, that checks out β€” lft needs elevated privileges to open raw sockets and capture packets via pcap(3).

On Linux, the preferred approach is to grant file capabilities rather than installing a setuid root binary:

setcap cap_net_raw,cap_net_admin=eip /usr/sbin/lft

This requires the libcap2-bin package on Debian/Ubuntu or libcap on RHEL/Fedora. The make install target attempts this automatically and falls back to setuid root if setcap(8) is not available. On FreeBSD, lft must be installed setuid root.

On macOS, lft must be installed setuid root. Why? Because lft uses two separate privilege paths</>:

  • pcap(3) (via /dev/bpf* devices) for capturing response packets
  • a raw socket (SOCK_RAW) for sending probe packets

BPF device permissions (like the ChmodBPF mechanism Wireshark uses) only cover the capture side β€” they do not grant the ability to create raw sockets, which on macOS requires root. And unlike Linux, macOS has no setcap(8) equivalent to selectively grant raw socket capability to a binary. So that route just doesn't exist there 😭

To install setuid root:

sudo chown root lft && sudo chmod u+s lft

make install performs this step automatically when run as root. This is standard practice for network diagnostic tools on macOS; ping(8) and traceroute(8) ship setuid root for the same reason. So you're in good company 🫢

If you do not receive permissions-related errors but traces still don't go anywhere: first, activate verbose output by adding -VV to your command line options. Then, reading the verbose output, if you see trace probes going out but no replies being detected (as indicated by "RCVD" tags), you may:

  • Use the TCP basic (-b) method if you wish to use TCP probes and you fear NAT may be causing your trace to fail.
  • Alternatively, select a different trace method and protocol such as UDP (-u) or ICMP (-p).

If you're attempting to use RFC 1393 (-P) and your trace is failing, this is likely because network equipment somewhere in the path does not conform to RFC 1393. Your only option is to select an alternative tracing method or protocol.

If you're attempting to utilize adaptive mode (-E/-e) and traces fail: first try enabling NAT compatibility using TCP basic (-b). If traces still fail, the most likely reason is a close-proximity stateful firewall in your network, which prevents this feature from working.


πŸ‘‘ AUTHORS πŸ‘‘

Victor Oppleman, Eugene Antsilevitch, Sergey Kondryukov and other helpers around the world. Legends, all of them πŸ’–


πŸ› REPORTING BUGS πŸ›

To report bugs, send e-mail to <lft@oppleman.com>


πŸ” SEE ALSO πŸ”

traceroute(8), netstat(1), whois(1), whob(8)


🌸 HISTORY 🌸

The lft command first appeared in 1998 as 'fft'. It got renamed because of confusion with fast fourier transforms β€” and lft stands for 'layer four traceroute.' πŸ’… Thanks also to Nils McCarthy for writing 'FFT', LFT's predecessor. A true icon πŸŽ€


and that's a wrap, bestie β€” this is <bold>LFT(8)</>, source-slash-version LFT, dated April 7, 2026. go trace something cute πŸ¦‹βœ¨

πŸ“¦ where this page comes from