{"id":50559866,"url":"https://github.com/rs/lagsim","last_synced_at":"2026-06-04T11:01:40.316Z","repository":{"id":349241434,"uuid":"1201584772","full_name":"rs/lagsim","owner":"rs","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-05T00:23:29.000Z","size":371,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-05T00:28:03.427Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/rs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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":"2026-04-04T21:55:41.000Z","updated_at":"2026-04-05T00:23:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rs/lagsim","commit_stats":null,"previous_names":["rs/lagsim"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/rs/lagsim","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rs%2Flagsim","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rs%2Flagsim/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rs%2Flagsim/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rs%2Flagsim/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rs","download_url":"https://codeload.github.com/rs/lagsim/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rs%2Flagsim/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33901305,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-04T02:00:06.755Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2026-06-04T11:01:39.742Z","updated_at":"2026-06-04T11:01:40.310Z","avatar_url":"https://github.com/rs.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lagsim\n\n![lagsim](doc/header.png)\n\nNetwork condition simulator for Linux routers. Injects latency, jitter, packet loss, reordering, and duplication per client IP using `tc`/`netem`/`ifb`.\n\nComes with built-in profiles for common network conditions (3G, LTE, Satellite, Starlink, etc.) and an interactive TUI to manage them. Profiles support asymmetric upload/download parameters to model real-world links.\n\n![Screenshot](doc/screenshot.png)\n\n## How it works\n\nlagsim sets up an HTB qdisc tree on your LAN interface with per-client classes and netem leaf qdiscs. Ingress traffic is redirected through an IFB device so both upload and download are conditioned independently.\n\n```\nLAN clients \u003c──eth0──\u003e router \u003c──wan0──\u003e internet\n                 │\n         HTB + netem (egress/download)\n         IFB + netem (ingress/upload)\n```\n\nEach direction gets its own netem parameters, so profiles can model asymmetric links (e.g., DSL with fast download / slow upload, or cellular with higher uplink loss).\n\n## Install\n\n```bash\ngo build -o lagsim .\nsudo cp lagsim /usr/local/bin/\n```\n\n## Usage\n\n### Interactive TUI\n\n```bash\nsudo lagsim\n```\n\n| Key | Action |\n|-----|--------|\n| `↑`/`k`, `↓`/`j` | Navigate client list |\n| `Enter` | Open profile selection menu |\n| `e` | Edit device name (requires MAC) |\n| `r`, `Delete` | Remove profile from client |\n| `Esc` | Cancel / back to list |\n| `Ctrl+U` | Clear name in edit mode |\n| `q`, `Ctrl+C` | Quit |\n\n### CLI\n\n```bash\n# List clients and their profiles\nsudo lagsim list\n\n# Show available profiles\nsudo lagsim profiles\n\n# Apply a profile to a client\nsudo lagsim apply 192.168.1.100 3G\n\n# Remove conditioning from a client\nsudo lagsim remove 192.168.1.100\n\n# Initialize tc infrastructure and restore saved assignments\nsudo lagsim init\n\n# Tear down all tc rules\nsudo lagsim teardown\n\n# Dump raw tc state for debugging\nsudo lagsim status\n```\n\n### Flags\n\n| Flag | Description |\n|------|-------------|\n| `-c`, `--config` | Config file path (default `~/.config/lagsim.yaml`) |\n| `--dry-run` | Print tc commands without executing |\n| `-v`, `--verbose` | Verbose output |\n\nOn first run, lagsim auto-detects the LAN interface and subnet. If multiple interfaces are found, it prompts you to choose.\n\n## Built-in profiles\n\nEach parameter is applied per-direction (egress + ingress), so effective RTT is roughly 2x the delay value. Asymmetric values show ▲ upload and ▼ download on separate lines.\n\n| Profile | Delay | Jitter | Dist | Loss | Reorder | Slot | Rate |\n|---------|-------|--------|------|------|---------|------|------|\n| 3G | 100ms | ▲\u0026nbsp;50ms\u003cbr\u003e▼\u0026nbsp;30ms | paretonormal | ▲\u0026nbsp;2.5%\u003cbr\u003e▼\u0026nbsp;1.5% | – | 40ms 10ms | ▲\u0026nbsp;0.5\u0026nbsp;Mbps\u003cbr\u003e▼\u0026nbsp;2\u0026nbsp;Mbps |\n| LTE | 20ms | ▲\u0026nbsp;8ms\u003cbr\u003e▼\u0026nbsp;5ms | paretonormal | ▲\u0026nbsp;1%\u003cbr\u003e▼\u0026nbsp;0.5% | – | 10ms 3ms | ▲\u0026nbsp;15\u0026nbsp;Mbps\u003cbr\u003e▼\u0026nbsp;50\u0026nbsp;Mbps |\n| 5G | 5ms | 1ms | paretonormal | ▲\u0026nbsp;0.1%\u003cbr\u003e▼\u0026nbsp;0.05% | – | – | ▲\u0026nbsp;100\u0026nbsp;Mbps\u003cbr\u003e▼\u0026nbsp;300\u0026nbsp;Mbps |\n| Edge-2G | 150ms | ▲\u0026nbsp;100ms\u003cbr\u003e▼\u0026nbsp;60ms | paretonormal | ▲\u0026nbsp;8%\u003cbr\u003e▼\u0026nbsp;5% | – | 80ms 20ms | ▲\u0026nbsp;0.05\u0026nbsp;Mbps\u003cbr\u003e▼\u0026nbsp;0.1\u0026nbsp;Mbps |\n| Lossy-WiFi | 5ms | 3ms | pareto | 3% | 1% gap 5 | 5ms 2ms | 20\u0026nbsp;Mbps |\n| Starlink | 20ms | ▲\u0026nbsp;10ms\u003cbr\u003e▼\u0026nbsp;5ms | normal | ▲\u0026nbsp;1%\u003cbr\u003e▼\u0026nbsp;0.5% | 0.5% | – | ▲\u0026nbsp;20\u0026nbsp;Mbps\u003cbr\u003e▼\u0026nbsp;100\u0026nbsp;Mbps |\n| Satellite | 300ms | ▲\u0026nbsp;50ms\u003cbr\u003e▼\u0026nbsp;30ms | normal | ▲\u0026nbsp;2.5%\u003cbr\u003e▼\u0026nbsp;1.5% | – | – | ▲\u0026nbsp;1\u0026nbsp;Mbps\u003cbr\u003e▼\u0026nbsp;5\u0026nbsp;Mbps |\n| DSL | 15ms | 3ms | normal | 0.2% | – | – | ▲\u0026nbsp;3\u0026nbsp;Mbps\u003cbr\u003e▼\u0026nbsp;25\u0026nbsp;Mbps |\n| Cable | 5ms | 1ms | normal | 0.05% | – | – | ▲\u0026nbsp;20\u0026nbsp;Mbps\u003cbr\u003e▼\u0026nbsp;200\u0026nbsp;Mbps |\n| Airplane-WiFi | 150ms | ▲\u0026nbsp;50ms\u003cbr\u003e▼\u0026nbsp;30ms | pareto | ▲\u0026nbsp;5%\u003cbr\u003e▼\u0026nbsp;3% | 1% gap 5 | 30ms 10ms | ▲\u0026nbsp;1\u0026nbsp;Mbps\u003cbr\u003e▼\u0026nbsp;2\u0026nbsp;Mbps |\n| Congested | 50ms | 40ms | paretonormal | 5% | 2% gap 3 | – | ▲\u0026nbsp;0.5\u0026nbsp;Mbps\u003cbr\u003e▼\u0026nbsp;1\u0026nbsp;Mbps |\n| Bursty | 10ms | 2ms | – | gemodel (burst) | – | – | 50\u0026nbsp;Mbps |\n| ECN-Datacenter | 1ms | 0.5ms | normal | 2% ecn | – | – | 1\u0026nbsp;Gbps |\n| ECN-WAN | 25ms | 5ms | normal | 0.5% ecn | – | – | ▲\u0026nbsp;50\u0026nbsp;Mbps\u003cbr\u003e▼\u0026nbsp;100\u0026nbsp;Mbps |\n\nBuilt-in profiles are defined in code, not written to the config file.\n\n## Configuration\n\nConfiguration is stored in `~/.config/lagsim.yaml`:\n\n```yaml\ninterfaces:\n  lan: eth0       # LAN-facing interface (auto-detected on first run)\n  ifb: ifb0       # IFB device (created automatically)\n  subnet: 192.168.1.0/24\nroot_rate: 1gbit\n\nprofiles:\n  # Override a built-in profile\n  3G:\n    delay: 100ms\n    jitter: 30ms\n    correlation: 25%\n    loss: 1.5%\n    rate: 2mbit\n    upload:\n      rate: 0.5mbit\n\n  # Add a custom profile\n  My-VPN:\n    delay: 30ms\n    jitter: 5ms\n    loss: 0.1%\n    rate: 50mbit\n\n  # Disable a built-in profile\n  Edge-2G: null\n\nassignments:\n  192.168.1.100: 3G\n  192.168.1.101: LTE\n\nnames:\n  aa:bb:cc:dd:ee:f0: Living Room TV\n  aa:bb:cc:dd:ee:f1: Dad's Phone\n```\n\n### Custom profiles\n\nOnly profiles that differ from the built-in defaults are saved to the config file. You can:\n\n- **Add** custom profiles alongside the built-ins\n- **Override** a built-in by redefining it (full replacement, not merged)\n- **Disable** a built-in by setting it to `null`\n\n### Profile parameters\n\n| Parameter | Description | Example |\n|-----------|-------------|---------|\n| `delay` | Base latency added to each packet | `100ms` |\n| `jitter` | Random variation added to delay | `30ms` |\n| `correlation` | How much each packet's delay correlates with the previous | `25%` |\n| `distribution` | Jitter distribution: `normal`, `pareto`, or `paretonormal` | `paretonormal` |\n| `loss` | Packet loss — random or bursty (see below) | `1.5%` |\n| `ecn` | Mark packets with ECN CE bit instead of dropping (see below) | `true` |\n| `duplicate` | Packet duplication probability | `0.5%` |\n| `reorder` | Packet reordering — random or with gap (see below) | `1%` or `1% gap 5` |\n| `corrupt` | Packet corruption probability | `0.1%` |\n| `rate` | Bandwidth limit | `2mbit` |\n| `slot` | Packet batching interval — holds then releases in bursts | `20ms 5ms` |\n\nAll parameters are optional except `delay`. Values use `tc`/`netem` syntax.\n\n### Delay distribution\n\nWithout a distribution, jitter is uniformly random. Setting `distribution` shapes how jitter values are picked:\n\n- **`normal`** — bell curve around the base delay. Good for stable links (DSL, cable, satellite) where variation is symmetric.\n- **`pareto`** — heavy-tailed: most packets are near the base delay, but occasional packets get much larger spikes. Good for WiFi and other interference-prone links.\n- **`paretonormal`** — blend of both: normal most of the time with pareto-like tail spikes. Good for cellular networks where handoffs and contention cause intermittent latency bursts.\n\n### Bursty loss\n\nThe `loss` field supports netem's Gilbert-Elliott model for realistic bursty loss patterns — periods of clean transmission interrupted by short bursts of heavy packet loss:\n\n```yaml\nloss: \"gemodel p r 1-h 1-k\"\n```\n\n| Parameter | Meaning |\n|-----------|---------|\n| `p` | Probability of entering the bad (lossy) state |\n| `r` | Probability of returning to the good state |\n| `1-h` | Loss rate in the bad state (e.g., `100%` = total blackout) |\n| `1-k` | Loss rate in the good state (e.g., `0%` = no baseline loss) |\n\nExample: `loss: \"gemodel 0.5% 15% 100% 0%\"` — clean most of the time, with occasional short bursts (~7 packets) of 100% loss. This models WiFi interference, cellular handoffs, or buffer overflows.\n\n### ECN marking\n\nWhen `ecn: true` is set alongside `loss`, packets are marked with the ECN CE (Congestion Experienced) bit instead of being dropped. The packet still arrives, but ECN-aware TCP stacks treat it as a congestion signal and slow down. Useful for testing DCTCP, BBR, or QUIC ECN behavior:\n\n```yaml\nprofiles:\n  My-ECN-Test:\n    delay: 5ms\n    loss: 1%\n    ecn: true\n    rate: 1gbit\n```\n\n### Reorder with gap\n\nBy default, `reorder` randomly reorders packets. Adding `gap N` makes it deterministic: every Nth packet is reordered with the given probability. This is more realistic for triggering TCP fast-retransmit (which fires after 3 duplicate ACKs):\n\n```yaml\nreorder: \"1% gap 5\"    # every 5th packet has a 1% chance of being reordered\n```\n\n### Slot-based emission\n\nThe `slot` parameter batches packets into time slots instead of sending them individually. Packets are held and released in bursts, simulating WiFi TDMA scheduling or cellular resource allocation:\n\n```yaml\nslot: \"20ms 5ms\"    # release a batch every 20ms ± 5ms jitter\n```\n\nThis is especially noticeable for interactive traffic (VoIP, gaming) where micro-bursts affect perceived quality even when average throughput is fine.\n\n### Asymmetric profiles\n\nBase parameters apply to both directions. Add `download` and/or `upload` sections to override specific parameters per direction — only the fields you specify are overridden, the rest inherit from the base:\n\n```yaml\nprofiles:\n  My-Satellite:\n    delay: 300ms\n    jitter: 30ms\n    loss: 1.5%\n    rate: 5mbit\n    upload:\n      rate: 1mbit       # slower upload\n      jitter: 50ms      # more jitter on uplink\n      loss: 2.5%        # more loss on uplink\n    download:\n      rate: 10mbit      # faster download\n```\n\n### Device names\n\nCustom names are keyed by MAC address so they follow the device across IP changes. Edit names in the TUI with `e`, or set them directly in the config under `names`.\n\n### Persistence\n\nAssignments persist across reboots. Run `lagsim init` at startup to restore them (e.g. via systemd or cron `@reboot`):\n\n```bash\n# crontab -e\n@reboot /usr/local/bin/lagsim init\n```\n\n## Example deployment: lab VLAN router\n\nA practical way to use lagsim is to set up a dedicated Linux box as a router between a lab VLAN and the rest of your network. All devices on the lab VLAN — phones, tablets, TVs, IoT devices — get their traffic conditioned without any client-side configuration. Traffic between lab devices and anything on the other side (dev workstations, servers, the internet) goes through lagsim.\n\n```\n   ┌──────────┐  ┌──────────┐  ┌──────────┐\n   │   dev    │  │   dev    │  │ internet │\n   │workstat. │  │  server  │  │  gateway │\n   └────┬─────┘  └────┬─────┘  └────┬─────┘\n        │             │             │\n   ─────┴─────────────┴─────────────┴──── office LAN\n                       │\n                       │ eth0 (or eth0.100 VLAN tag)\n                ┌──────┴──────┐\n                │   lagsim    │\n                │  linux box  │\n                └──────┬──────┘\n                       │ eth1 (or eth0.200 VLAN tag)\n                       │\n          ┌────────────┼───────────┐\n          │            │           │\n     ┌────┴────┐ ┌─────┴─────┐ ┌───┴───┐\n     │  phone  │ │  tablet   │ │  TV   │\n     └─────────┘ └───────────┘ └───────┘\n              Lab WiFi (dedicated SSID)\n```\n\nThe Linux box can use two physical interfaces (e.g., `eth0` for the office LAN, `eth1` for the lab) or a single interface with VLAN tagging (e.g., `eth0.100` and `eth0.200`).\n\n### Setup\n\n1. **Create a lab VLAN** on your switch and assign a dedicated WiFi SSID to it\n2. **Configure the Linux box** with either two interfaces or VLAN sub-interfaces — one on the lab VLAN, one on the office LAN\n3. **Enable IP forwarding** so the box routes traffic between the two networks\n4. **Run lagsim** on the lab-facing interface:\n\n```bash\nsudo sysctl -w net.ipv4.ip_forward=1\nsudo lagsim\n```\n\nlagsim auto-detects the lab interface and discovers devices via ARP. You can then assign different profiles to different devices — for example, put a phone on \"3G\" and a TV on \"Satellite\" simultaneously.\n\nThis lets you test how your client/server application behaves under realistic network conditions: the clients are real devices on the lab VLAN, and the servers run on your workstation or dev servers on the office LAN — all traffic between them passes through lagsim.\n\n## Requirements\n\n- Linux with `tc`, `ip`, and the `ifb` kernel module\n- Root privileges\n- Go 1.24+ to build\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frs%2Flagsim","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frs%2Flagsim","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frs%2Flagsim/lists"}