mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2026-10-31 16:37:42 -05:00
109 lines
8.0 KiB
JSON
109 lines
8.0 KiB
JSON
{
|
|
"product": "LibreQoS",
|
|
"branchName": "radius-accounting-dynamic-circuits",
|
|
"summary": "Extend the opt-in RADIUS accounting listener from session tracking to dynamic circuit application, with fallback speeds and MAC-based ShapedDevices matching for operator-managed account plans.",
|
|
"stories": [
|
|
{
|
|
"id": "RADIUS-010",
|
|
"title": "Gate RADIUS dynamic circuit application",
|
|
"description": "As a LibreQoS operator, I want RADIUS dynamic circuit application to have its own explicit activation settings, so that I can receive accounting packets without changing shaping until I choose to enable it.",
|
|
"priority": 1,
|
|
"passes": true,
|
|
"acceptanceCriteria": [
|
|
"RADIUS accounting continues to listen and track sessions when dynamic circuit application is disabled.",
|
|
"RADIUS-created dynamic circuits are not applied unless both RADIUS application settings and the existing dynamic_circuits feature are enabled.",
|
|
"Invalid activation settings are rejected during configuration validation before listener startup.",
|
|
"Config round-trip tests cover disabled application, enabled application, missing dynamic_circuits dependency, and valid activation settings."
|
|
]
|
|
},
|
|
{
|
|
"id": "RADIUS-011",
|
|
"title": "Configure fallback RADIUS speeds",
|
|
"description": "As a LibreQoS operator, I want an optional default speed profile for RADIUS-created circuits, so that sessions without packet-provided or lookup-provided rates can still be shaped at a safe default plan.",
|
|
"priority": 1,
|
|
"passes": true,
|
|
"acceptanceCriteria": [
|
|
"RADIUS accounting configuration can define download_min_mbps, upload_min_mbps, download_max_mbps, and upload_max_mbps as an optional fallback profile.",
|
|
"Validation rejects non-finite rates, rates less than or equal to zero, and minimum rates greater than maximum rates.",
|
|
"When no packet or lookup rate exists and no fallback profile is configured, the session remains pending with a missing-rate reason.",
|
|
"When a fallback profile is configured, it is used only after higher-priority rate sources fail to resolve a rate."
|
|
]
|
|
},
|
|
{
|
|
"id": "RADIUS-012",
|
|
"title": "Match ShapedDevices by RADIUS MAC",
|
|
"description": "As a LibreQoS operator, I want RADIUS accounting sessions to optionally match ShapedDevices entries by MAC address, so that my existing account-plan sync can provide circuit metadata while RADIUS supplies the active IP addresses.",
|
|
"priority": 1,
|
|
"passes": true,
|
|
"acceptanceCriteria": [
|
|
"A configuration option enables or disables matching RADIUS Calling-Station-Id values against ShapedDevices MAC values.",
|
|
"MAC matching normalizes common colon, hyphen, dot, plain-hex, and mixed-case formats before comparison.",
|
|
"A unique MAC match supplies circuit ID, circuit name, device ID, device name, parent fields, speed fields, and SQM override metadata for the dynamic circuit.",
|
|
"RADIUS-provided IPv4 and IPv6 address data replaces the matched ShapedDevices address fields for the active dynamic circuit.",
|
|
"No match or more than one matching ShapedDevices row leaves the session pending and emits no dynamic circuit."
|
|
]
|
|
},
|
|
{
|
|
"id": "RADIUS-013",
|
|
"title": "Resolve RADIUS sessions into shaped devices",
|
|
"description": "As a LibreQoS operator, I want shapeable RADIUS sessions converted into valid dynamic circuit device definitions, so that active subscriber addresses can be attached to the correct circuit and plan.",
|
|
"priority": 1,
|
|
"passes": true,
|
|
"acceptanceCriteria": [
|
|
"A session becomes shapeable only when it has a stable circuit identity, device identity, at least one IP address or prefix, parent attachment metadata, and resolved rates.",
|
|
"When no ShapedDevices MAC match supplies identity, the default circuit ID is derived from NAS identity and Acct-Session-Id, and display names prefer User-Name and Calling-Station-Id when present.",
|
|
"Rate resolution uses decoded RADIUS rate data before ShapedDevices MAC-match rates, and uses the configured fallback profile only when both are missing.",
|
|
"Missing IP, missing parent, missing rate, and ambiguous MAC match cases remain pending with distinct reasons.",
|
|
"Unit tests cover Start without IP, Interim activation with IP, matched ShapedDevices metadata with RADIUS IP replacement, and fallback identity generation."
|
|
]
|
|
},
|
|
{
|
|
"id": "RADIUS-014",
|
|
"title": "Apply RADIUS dynamic circuit upserts",
|
|
"description": "As a LibreQoS operator, I want accepted RADIUS Start and Interim-Update packets to create or update dynamic circuits through the existing daemon path, so that active sessions are shaped without editing ShapedDevices.csv.",
|
|
"priority": 1,
|
|
"passes": true,
|
|
"acceptanceCriteria": [
|
|
"Shapeable Start and Interim-Update events submit CreateDynamicCircuit requests with the resolved ShapedDevice payload.",
|
|
"RADIUS dynamic circuit application uses the existing lqosd dynamic circuit path and never writes dynamic_circuits.json directly from lqos_radius.",
|
|
"The UDP Accounting-Response path does not wait for dynamic circuit persistence or bakery overlay work to finish.",
|
|
"Create or update failures are logged with circuit and session identity and do not stop the RADIUS listener.",
|
|
"Tests use fake command or bus sinks and do not require live shaping, bakery, TC, XDP, or root privileges."
|
|
]
|
|
},
|
|
{
|
|
"id": "RADIUS-015",
|
|
"title": "Expire RADIUS dynamic circuits",
|
|
"description": "As a LibreQoS operator, I want RADIUS-created dynamic circuits removed when accounting sessions stop or expire, so that stale subscriber IPs do not remain shaped after the session ends.",
|
|
"priority": 1,
|
|
"passes": true,
|
|
"acceptanceCriteria": [
|
|
"Stop events submit RemoveDynamicCircuit for the active circuit ID, and duplicate Stop events are harmless.",
|
|
"Sessions that exceed default_ttl_seconds without refresh remove any active RADIUS-created dynamic circuit.",
|
|
"Accounting-On and Accounting-Off events mark matching NAS sessions stale and remove active circuits no later than stale_grace_seconds unless refreshed by Start or Interim-Update.",
|
|
"Removal uses the same circuit ID that was used for creation, including IDs supplied by ShapedDevices MAC matching.",
|
|
"Fake-time tests cover Stop removal, TTL expiry removal, stale NAS reset expiry, and refresh before expiry."
|
|
]
|
|
},
|
|
{
|
|
"id": "RADIUS-016",
|
|
"title": "Report RADIUS activation outcomes",
|
|
"description": "As a LibreQoS operator, I want clear RADIUS dynamic circuit diagnostics, so that I can see why a session was shaped, pending, expired, or rejected without exposing shared secrets.",
|
|
"priority": 2,
|
|
"passes": true,
|
|
"acceptanceCriteria": [
|
|
"Diagnostics distinguish active, pending, stopped, stale, expired, and apply-failed RADIUS session states.",
|
|
"Pending diagnostics include missing IP, missing parent, missing rate, missing session identity, no MAC match, and ambiguous MAC match reasons.",
|
|
"Create, update, remove, and expiry counters are tracked separately from packet accept and reject counters.",
|
|
"Logs and diagnostics include circuit/session identifiers needed for troubleshooting but never include shared secret contents."
|
|
]
|
|
}
|
|
],
|
|
"notes": [
|
|
"This PRD starts after the completed listener, packet parsing, session tracking, and deferred command-intent work from RADIUS-001 through RADIUS-009.",
|
|
"RADIUS-provided IP and prefix data is authoritative for active dynamic circuit addresses; ShapedDevices MAC matches supply account-plan and topology metadata only.",
|
|
"Dynamic circuit application must use the existing lqosd dynamic circuit path and must continue to respect the existing dynamic_circuits.enabled safety gate.",
|
|
"Automated tests for this PRD should use fake sinks, fake time, and loopback-only packet handling; they must not require root privileges or live TC/XDP shaping state."
|
|
]
|
|
}
|