feat(clients): add per-client download/upload speed limits via tc HTB - #6043
feat(clients): add per-client download/upload speed limits via tc HTB#6043HamidRezaSZ wants to merge 11 commits into
Conversation
Store speedDown/speedUp on clients and shape traffic for observed client IPs on the primary interface so operators can cap bandwidth without Fail2ban.
This comment was marked as outdated.
This comment was marked as outdated.
Satisfy golangci-lint noctx on TcShaper.runTC.
Add default-off speedLimitEnable, aggregate multi-IP uploads via a shared police action, skip work when no limits exist, hide MTProto-only UI, and cover Sync with unit tests.
The findings are addressed in the latest commits: High — feature gating High — multi-IP upload cap Medium — MTProto UI Medium — tests Low — unnecessary DB work |
Use a uint32 loop bound so 32-bit builds accept the max police try count.
Describe Speed Down/Up fields and the default-off Client Speed Limit control in the clients guides.
Use Ant Design text color so the share-link code block passes Storybook axe color-contrast.
Log when primary iface detection fails, probe speed limits with SELECT LIMIT 1, timeout tc calls, and clear stale rules when no limits remain.
|
@MHSanaei, this pull request is working perfectly. Why hasn’t it been merged yet? :/ |
This comment was marked as outdated.
This comment was marked as outdated.
Post-merge review of the per-client speed limit feature turned up six defects, four of which silently disable the limits an operator set. Persistence: - Toggling a client's enable switch rebuilt the whole client payload from the hydrated record but omitted speedDown/speedUp, and the record merge applies both unconditionally, so every enable/disable reset the limits to unlimited. - The detached-client update path writes an explicit column map that never listed speed_down/speed_up, so editing a client with no inbound attached dropped the values. - New columns had no NULL backfill. AutoMigrate adds them, but an older SQLite ALTER TABLE can leave them NULL, and a NULL int fails every ClientRecord scan rather than just the shaper's probe. tc rule lifetime: - u32 filter handles are htid:hash:nodeid with a 12-bit node id, so tc rejects anything past 0xfff. The counter only ever went up, so after 4096 cumulative filter installs every add failed and shaping stopped for good. Handles are now recycled from the freed pool. - `tc actions add` refuses an index that already exists. A police action left behind by an unclean shutdown therefore blocked upload shaping permanently; `replace` creates or overwrites instead. - The reserved HTB catch-all class is 1:9999, and tc parses class ids as hex, so the allocator's decimal 9999 guard protected the wrong id. - The server kept a cleaned-up shaper pointer after a restart that disabled the setting; it is cleared on stop.
SummaryThis adds per-client download/upload caps stored on Reviewed head: 5162cc8 Note for context: three earlier PRs proposing the same feature were closed without discussion (#5629, #4141, #3507). It may be worth confirming with @MHSanaei that there is appetite for panel-managed FindingsSeverity: High / Confidence: Medium / Category: correctness Severity: Medium / Confidence: High / Category: reliability Severity: Medium / Confidence: Medium / Category: reliability Severity: Medium / Confidence: High / Category: correctness Severity: Medium / Confidence: High / Category: correctness Severity: Medium / Confidence: High / Category: performance Severity: Medium / Confidence: High / Category: design Severity: Medium / Confidence: High / Category: documentation Severity: Low / Confidence: High / Category: consistency Severity: Low / Confidence: High / Category: validation Severity: Low / Confidence: High / Category: convention Severity: Low / Confidence: High / Category: scope Severity: Low / Confidence: Medium / Category: multi-node Severity: Suggestion / Confidence: High / Category: testing Severity: Suggestion / Confidence: High / Category: maintainability Positive observations
VerdictRequest changes. The hardcoded This review was generated automatically; a maintainer may follow up. |
Store speedDown/speedUp on clients and shape traffic for observed client IPs on the primary interface so operators can cap bandwidth without Fail2ban.
Summary
Adds optional per-client download/upload speed limits (Mbps), stored on the client record and enforced with Linux
tcHTB (egress) plus a shared ingress police action per client on the primary interface. Shaping is driven by the existing online-IP scan job and is disabled by default behind aspeedLimitEnablesetting.Why
Operators often need to cap a client’s bandwidth without Fail2ban or IP bans. This gives panel-level speed controls that apply to observed client IPs on the host’s primary NIC, without seizing the host qdisc unless an admin explicitly opts in.
Type of change
Areas affected
How was this tested?
make gen.go test ./internal/web/service/ -run 'TestUniqueValidIPs|TestIPMatch|TestTcShaper'(Sync add/update/remove, shared police across multiple IPs).tctakeover on panel start/restart. Enable it, restart panel, then proceed.tcand root (orCAP_NET_ADMIN): connect a limited client, wait for the IP-scan job (~10s), then checktc class show/tc filter show/tc actionsfor HTB (down) and a shared police index referenced by each IP (up).limitIp> 1 and a nonzerospeedUp, confirm multiple observed IPs share one police action (aggregate upload cap).0(or disconnect), wait for the next sync, and confirm classes/filters/actions are removed.Screenshots / recordings
Breaking changes
None for API consumers beyond optional new fields (
speedDown,speedUp; default0= unlimited) and optional settingspeedLimitEnable(defaultfalse).Ops note: Shaping does not run until Client Speed Limit is enabled and the panel is restarted. When enabled on Linux, the panel takes over the primary interface root qdisc with HTB and may install an ingress qdisc—hosts with an existing custom
tc/QoS setup should opt in knowingly. Requirestcand sufficient privileges; unavailable platforms (e.g. Windows) skip shaping. New columns are added via GORM AutoMigrate (speed_down,speed_up).Checklist
go build ./...and the test suite pass locally.npm run lint,npm run typecheck, andnpm run buildpass.