Skip to content

RADIUS attributes ​

What Kiwire sends and expects on the RADIUS side.

Kiwire4 doesn't run its own RADIUS server — FreeRADIUS talks to it over rlm_rest, posting form fields (derived from the real RADIUS packet via FreeRADIUS xlat expressions) to POST /radius with an action parameter. That one endpoint handles authorize, post-auth/check-cert, and accounting. Everything below is drawn from the code behind that endpoint and from the login check that builds the actual attribute maps.

Authentication request ​

Fields FreeRADIUS forwards on action=authorize, mapped from the real RADIUS Access-Request attributes:

POST fieldRADIUS attributeNotes
nasidNAS-IdentifierFalls back to parsing station_id when empty (handles Meraki/Aruba, which don't always send NAS-Identifier).
station_idCalled-Station-IdFormat AA-BB-CC-DD-EE-FF:SSID or AA:BB:CC:DD:EE:FF:SSID — split into controller MAC + SSID.
usernameUser-NameFor EAP inner-tunnel requests, overridden from a cache of the outer-tunnel context.
passwordUser-PasswordOnly forwarded/used for PAP — EAP/CHAP are verified by FreeRADIUS itself against the Cleartext-Password Kiwire returns.
macaddressCalling-Station-IdNormalised (dashes → colons, lowercase) before use.
controller_ipNAS-IP-Address
controller_zone(derived from Called-Station-Id)The SSID/zone half of station_id.
chap_challenge, chap_passwordCHAP-Challenge, CHAP-PasswordPresence of both flips auth type to CHAP.
eappresence of an EAP attributeFlips auth type to EAP; auth_method carries the inner method (peap/ttls/tls/nak).
auth_method—pap (default) unless CHAP/EAP detected.

Auth-type detection order is CHAP → EAP → PAP fallback. Password verification in the login check is skipped entirely for eap/chap/mschapv2 and for login_type=nak — those are verified by FreeRADIUS itself using the control:Cleartext-Password Kiwire returns in the Accept.

Access-Accept reply attributes ​

Built by the login check once the account passes, keyed with a control:/reply: prefix that maps to FreeRADIUS's rlm_rest JSON convention:

AttributeSourceNotes
control:Cleartext-PasswordDecrypted stored password (or the PMS-typed password)Lets FreeRADIUS do the actual PAP/CHAP comparison.
control:Auth-TypePAP/CHAP, set explicitlyLeft unset for EAP — FreeRADIUS's eap module already resolves it.
control:Simultaneous-UseProfile attribute JSON key control:Simultaneous-UseNot a database column — simultaneous_use was dropped from kiwire_profiles; it now lives only inside the JSON attribute blob, defaulting to 1 when absent.
control:Kiwire-Total-Quotaprofile.quota_limit (MB) × 1,048,576 → bytesInternal-only key — always replaced by a vendor-specific quota VSA (or dropped) during the vendor translation step below; a NAS never sees this attribute name.
reply:Session-TimeoutComputed from remaining credit, dynamicRemaining seconds for a time-type profile: Access-Period − elapsed (expiration profiles) or Max-All-Session − user.session_time (countdown profiles). Recomputed on every login, not a static profile field.
reply:Idle-Timeoutprofile.idle_timeout (seconds)Set only when > 0.
reply:Acct-Interim-IntervalProfile attribute JSON key reply:Acct-Interim-IntervalDefaults to 300 s if not set.
reply:WISPr-Bandwidth-Max-Down / Max-Up / Min-Up / Min-DownProfile attribute/attribute_custom JSONZero values are stripped before the reply is sent — a literal 0 means "0 bps / block all traffic" to most NAS vendors, not "unlimited", so it must never reach the wire.

Attribute merge order (later wins): explicit fields above → profile.attribute JSON (skipped for any key already set explicitly) → profile.attribute_custom JSON, which always overrides everything, including the explicit fields — it's the hand-edited escape hatch.

Accounting ​

action=accounting fields, mapped from the real Accounting-Request attributes:

POST fieldRADIUS attribute
typeAcct-Status-Type (Start / Interim-Update / Stop)
nasidNAS-Identifier
usernameUser-Name
macaddressCalling-Station-Id
unique_idAcct-Unique-Session-Id
session_idAcct-Session-Id
station_idCalled-Station-Id (used as the zone)
ipaddressFramed-IP-Address
controller_ipNAS-IP-Address
session_timeAcct-Session-Time (seconds, cumulative)
quota_in / quota_outAcct-Input-Octets / Acct-Output-Octets (32-bit, bytes)
quota_in_gw / quota_out_gwAcct-Input-Gigawords / Acct-Output-Gigawords
event-time (note the dash)Event-Timestamp
terminateAcct-Terminate-Cause

Accounting processing applies the Gigawords expansion (32-bit counter rollover) before dispatch, normalises the MAC, de-duplicates identical packets for 10 seconds (nasid+unique_id+type+tenant), and serialises Start/Interim-Update/Stop for the same session behind a per-session lock. For EAP-TLS, the username FreeRADIUS sends on accounting packets is the certificate CN, not the real Kiwire username — it's resolved back via a 6-hour cache keyed by MAC+NAS (falling back to a direct cert_name_slug lookup on a cache miss).

Vendor-specific attributes ​

Quota is translated to a vendor-specific attribute in a single step, just before the reply is sent. It removes the internal control:Kiwire-Total-Quota (bytes) and, if the value exceeds 2³² (4,294,967,296), splits it into a 32-bit value + gigawords, same as native RADIUS counters:

Vendor (kiwire_controller.vendor)Reply attributes
mikrotik, ubnt-console, ubnt, ubnt_controllerreply:Mikrotik-Total-Limit + reply:Mikrotik-Total-Limit-Gigawords
chillispot, pfsense, wifidogreply:ChilliSpot-Max-Total-Octets + reply:ChilliSpot-Max-Total-Gigawords
cambiumreply:CAMB-Traffic-Quota-Limit-Total + reply:CAMB-Traffic-Quota-Limit-Total-Gigwords (note: "Gigwords", not "Gigawords" — matches the actual Cambium dictionary spelling)
everything else (allied-telesis, aruba, aruba_os, cisco_wlc, cmcc, engenius, fortiap, fortigate, h3c, huawei, huawei-cloud-ugw, huawei-nce, huawei-wlc, meraki, mist, motorola, nomadix, nomadix_xml, other, peplink, ruijie-reyee, ruckus_ap, ruckus_scg, ruckus_vsz, sonicwall, sundray, tplink, virtual-nas, xirrus, zyxelap)none — the quota key is dropped silently; the NAS is expected to rely on Session-Timeout or its own accounting enforcement.

Every non-quota attribute (Session-Timeout, Idle-Timeout, WISPr bandwidth, etc.) passes through that step unchanged for every vendor.

Two additional VSA dictionaries are used specifically on the CoA/disconnect path (see below), not the login Access-Accept: WISPr under vendor id 14122, and Mikrotik under vendor id 14988.

CoA and disconnect ​

Kiwire sends RFC 5176 CoA-Request / Disconnect-Request packets directly to the NAS (not through FreeRADIUS's REST module) when switching a live session to a different profile or force-disconnecting a device.

CoA-Request sets:

AttributeSource
Session-Timeoutcontrol:Access-Period if set, else control:Max-All-Session, from the target profile's attributes.
WISPr-Bandwidth-Max-Up (VSA 14122/7), Min-Up (14122/5), Max-Down (14122/8), Min-Down (14122/6)Target profile's reply:WISPr-Bandwidth-*, only when > 0.
Mikrotik-Rate-Limit (VSA 14988/8)Target profile's reply:Mikrotik-Rate-Limit, when present.
Idle-TimeoutTarget profile's reply:Idle-Timeout, when > 0.
Event-TimestampCurrent time.
Session identificationRuckus SCG/VSZ: Acct-Session-Id only. Every other vendor: User-Name + Framed-IP-Address, plus a vendor quota VSA if the target profile has control:Kiwire-Total-Quota — Mikrotik-Total-Limit (VSA 14988/17) for mikrotik, ChilliSpot-Max-Total-Octets (VSA 14559/3) for chillispot.

Disconnect-Request sends session-identification attributes only (same Ruckus-vs-everyone-else split as above: Acct-Session-Id alone, or User-Name + Framed-IP-Address) — no profile or bandwidth attributes apply to a disconnect.

Not every vendor goes through native RADIUS CoA/Disconnect: UniFi (ubnt-console) is reauthorised/kicked via the UniFi controller API, and Nomadix bandwidth changes go through its own HTTP API — Kiwire picks the right transport per controller vendor before falling back to RADIUS CoA/Disconnect.

Last updated:

Kiwire platform documentation