{"id":39937519,"url":"https://github.com/tkjaer/etr","last_synced_at":"2026-04-02T00:02:16.704Z","repository":{"id":322648935,"uuid":"714759845","full_name":"tkjaer/etr","owner":"tkjaer","description":"ECMP-aware traceroute","archived":false,"fork":false,"pushed_at":"2026-03-24T21:24:23.000Z","size":6645,"stargazers_count":94,"open_issues_count":2,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-03-25T10:34:58.445Z","etag":null,"topics":["ecmp","neteng","traceroute"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tkjaer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-11-05T19:24:16.000Z","updated_at":"2026-03-24T21:24:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tkjaer/etr","commit_stats":null,"previous_names":["tkjaer/etr"],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/tkjaer/etr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkjaer%2Fetr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkjaer%2Fetr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkjaer%2Fetr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkjaer%2Fetr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tkjaer","download_url":"https://codeload.github.com/tkjaer/etr/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkjaer%2Fetr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31293148,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T21:15:39.731Z","status":"ssl_error","status_checked_at":"2026-04-01T21:15:34.046Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ecmp","neteng","traceroute"],"created_at":"2026-01-18T19:00:58.531Z","updated_at":"2026-04-02T00:02:16.668Z","avatar_url":"https://github.com/tkjaer.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ETR - ECMP Traceroute\n\nAn MTR-like tool for discovering and analyzing ECMP (Equal-Cost Multi-Path) network routes.\n\n\u003e **Note**: ETR is a work in progress, built as a learning project while exploring Go in a familiar (network engineering) domain. It was created out of a desire for an MTR-like tool that is ECMP-\"aware\" and capable of probing specific ECMP paths using consistent 5-tuple hashing. While functional and useful for network exploration, it's not yet recommended for production environments.\n\n## Features\n\nETR discovers multiple network paths by running parallel traceroute probes with different source ports, causing routers to select different ECMP routes. Each probe maintains a consistent 5-tuple (src IP, src port, dst IP, dst port, protocol) to repeatedly test the same path.\n\n- **Real-time TUI**: MTR-like interface with live statistics (RTT, delay variation, packet loss per hop)\n- **Parallel probes**: Run multiple simultaneous probes to discover different ECMP paths\n- **Path discovery**: Automatically discover all ECMP paths with `--discover`\n- **Protocol support**: TCP SYN and UDP probes (UDP payload length encodes probe details)\n- **JSON export**: Stream results to stdout or file for analysis and integration\n- **Path identification**: CRC32 or SHA256 hashing to identify unique routes\n- **Automatic destination detection**: Stops probing beyond the final destination\n\n**Use cases**: Network troubleshooting, ECMP path discovery, finding specific paths for tools like iperf\n\n## Demo\n\n![etr tui demo](https://github.com/user-attachments/assets/f5803a12-a4f1-4fa1-82a9-8526ba85d5af)\n\n## Installation\n\n- **Homebrew** (macOS/Linux):\n  ```bash\n  brew tap tkjaer/tap\n  brew install etr\n  ```\n- **Releases**: Download the latest macOS/Linux binary from the [releases page](https://github.com/tkjaer/etr/releases).\n- **APT repository** (Debian/Ubuntu):\n  ```bash\n  sudo mkdir -p /etc/apt/keyrings\n  curl -fsSL https://tkjaer.github.io/etr/etr.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/etr.gpg\n  echo \"deb [signed-by=/etc/apt/keyrings/etr.gpg] https://tkjaer.github.io/etr stable main\" | sudo tee /etc/apt/sources.list.d/etr.list\n  sudo apt update\n  sudo apt install etr\n  ```\n- **Source**: `go install github.com/tkjaer/etr/cmd/etr@latest` (requires libpcap headers).\n- **BSD**: Supported via source builds (OpenBSD/NetBSD limited to Ethernet source interfaces).\n\nDetailed platform notes (Gatekeeper prompts, package installs, raw-socket permissions) live in [docs/install.md](docs/install.md). The APT repository is signed and requires the published repository key.\n\n## Usage\n\n```bash\n# Basic TCP traceroute\netr example.com\n\n# UDP with 10 parallel probes to discover multiple paths\netr -U -P 10 example.com\n\n# Export JSON while showing TUI\netr -j output.json example.com\n\n# JSON-only output (no TUI)\netr -J example.com \u003e results.json\n\n# Custom port and extended monitoring\netr -p 80 -c 1000 -d 5s target.example.com\n\n# Discover all ECMP paths to a destination\netr --discover example.com\n```\n\n**Common options**:\n- `-4/-6`: Force IPv4 or IPv6 routing\n- `-T/-U`: TCP (default) or UDP probes\n- `-n`: Skip DNS lookups (show IPs only)\n- `-c \u003cn\u003e`: Probe iterations (default: unlimited)\n- `-P \u003cn\u003e`: Number of parallel probes (default: 5)\n- `-p \u003cport\u003e`: Destination port (default: 443 for TCP, 33434 for UDP)\n- `-s \u003cport\u003e`: Base source port (default: 50000)\n- `-j \u003cfile\u003e`: JSON output to file (keeps TUI)\n- `-J`: JSON output to stdout (disables TUI)\n- `-B`: Print a tcpdump-compatible BPF filter for the current options\n- `-a`, `--asn`: Enable ASN (Autonomous System Number) lookups for each hop\n- `--help`: Full option list\n\n**TUI controls**: `↑/↓` scroll, `←/→` or `Tab` switch views, `q` quit\n\n**Styling**: set the `NO_COLOR` environment variable to disable ANSI styling in the TUI.\n\n## Example: Finding ECMP Paths for iperf Testing\n\n```bash\n# Discover ECMP paths automatically\netr --discover target.example.com\n# Output shows each unique path with its source port and hop chain:\n#   path e82729e8  src-port :50001  10.0.1.1 → 91.100.34.1 → ... → 142.250.180.14\n#   path 1a7507c3  src-port :50004  10.0.1.1 → 91.100.34.1 → ... → 142.250.180.14\n\n# Use iperf with a discovered source port to test that exact ECMP path\niperf3 -c target.example.com --cport 50001\n```\n\n`--discover` probes with sequential source ports, confirms each path is stable,\nthen moves on. It defaults to `-P 2 -d 500ms` to reduce ICMP pressure on routers.\nRun `etr --help` for tuning flags.\n\n## JSON Output Format\n\nEach probe iteration outputs one line of JSON (newline-delimited):\n\n```json\n{\n  \"probe_id\": 0,\n  \"probe_num\": 1,\n  \"path_hash\": \"a3f5c2d1\",\n  \"source_ip\": \"198.51.100.1\",\n  \"source_port\": 50000,\n  \"destination_ip\": \"203.0.113.1\",\n  \"destination_port\": 443,\n  \"destination_ptr\": \"example.com\",\n  \"protocol\": \"TCP\",\n  \"reached_dest\": true,\n  \"hops\": [\n    {\n      \"ttl\": 1,\n      \"ip\": \"192.0.2.1\",\n      \"rtt\": 1234567,\n      \"timeout\": false,\n      \"ptr\": \"gateway.local\",\n      \"recv_time\": \"2025-10-27T12:00:00Z\"\n    }\n  ],\n  \"timestamp\": \"2025-10-27T12:00:00Z\"\n}\n```\n\n**Key fields:**\n- `path_hash`: Unique identifier for this network path (CRC32 or SHA256)\n- `probe_id`: Which parallel probe (0 to N-1)\n- `probe_num`: Iteration number (0, 1, 2, ...)\n- `reached_dest`: Whether the final destination was reached\n- `rtt`: Round-trip time in microseconds\n\n## Using `-B` with tcpdump\n\nETR can print the exact BPF filter it uses for capturing probe responses. This is useful if you want to:\n\n- **Watch traffic live** alongside ETR:\n\n  `tcpdump -i eth0 \"$(etr -B example.com)\"`\n\n- **Capture a trace** for troubleshooting or sharing with others:\n\n  `tcpdump -i eth0 -w etr-trace.pcap \"$(etr -B example.com)\"`\n\nThis keeps capture focused on the probe traffic ETR cares about and makes it easier to share evidence during debugging.\n\n## License\n\nMIT License - see LICENSE file for details.\n\n## Further Reading\n\n- [Installation details](docs/install.md)\n- [Contributing](CONTRIBUTING.md)\n- [Probe encoding design](docs/probe-encoding-design.md)\n- [Reusable APT Pages workflow](docs/apt-repo-workflow.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftkjaer%2Fetr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftkjaer%2Fetr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftkjaer%2Fetr/lists"}