[PATCH 0/7] RADIUS/TLS reverse CoA (draft-ietf-radext-reverse-coa)

Seb Adamski seb at bionic.network
Sun Oct 18 23:38:07 PDT 2026


This series lets hostapd accept Disconnect-Request and CoA-Request
messages that a RADIUS/TLS server sends on the TLS connection that
hostapd opened to it ("reverse CoA", draft-ietf-radext-reverse-coa-08).
This is needed when the NAS reaches its RADIUS proxy only outbound,
e.g., behind NAT in a roaming federation that uses RADIUS/TLS: the
proxy cannot open a connection to a UDP DAS listener on the NAS, so it
forwards the request over the existing connection. Section 6 of the
draft extends the routing of RFC 8559, Section 2.1 for this, and
Section 4 requires a static per-server flag on the client, off by
default, with no negotiation on the connection.

Patches:
1-2: Fixes for pre-existing DAS issues that apply on their own:
     radius_das_init() could close file descriptor 0 on an early
     failure, and a request with Proxy-State was NAKed with 401 instead
     of having Proxy-State echoed in the response (RFC 5176, 3.1;
     RFC 8559, 4.3.2).
3:   RADIUS/TLS receive path: reassemble the stream by the RADIUS Length
     field and decrypt all received data before processing it. This is
     a pre-existing problem (a second TLS record in the same read was
     lost when anything was sent on the connection, a second message in
     a record was ignored, and a split message was dropped), but
     server-initiated messages make it much more likely.
4:   Refactor: DAS request processing separated from the UDP socket
     handler, with the shared secret as a parameter. No change in
     behavior.
5:   RADIUS: accept the requests on the connection of a server
     with the flag set; drop them (with a log message) everywhere else
     before matching against pending requests. All RADIUS/TLS output
     goes through one function that uses MSG_NOSIGNAL and queues data
     on a short write or EAGAIN. CoA-NAK uses Error-Cause 406 instead of
     405 when CoA is not supported.
6:   hostapd configuration: auth_server_reverse_coa and
     acct_server_reverse_coa, documented in hostapd.conf. Also applied
     on configuration reload.
7:   hwsim tests with a small Python RADIUS/TLS server.

Overlap with "[PATCH 0/4] RADIUS/TLS with several messages in flight"
(4 Oct 2026): patch 3 here fixes the same two problems as patches 1/4
and 2/4 of that series, the TLS input buffer reset by
tls_connection_encrypt() and the missing framing by RADIUS Length.
That series changes the OpenSSL wrapper; this one drains the decrypt
function from radius_client.c and leaves the TLS wrappers alone, so
there is no change for the EAP methods. Either fix is fine for the
rest of this series. If the 4 Oct series is applied first, patch 3 can
be dropped and patch 5 goes on top of its radius_client_tls_rx(); I
have tested that combination (their four patches, then patches 1, 2
and 4-7 of this series) with the tests from both series. The tests
are complementary: that series covers back-to-back responses to
Accounting-Requests, this one covers server-initiated requests in the
same framing shapes.

Open design question: the option is per server
(auth_server_reverse_coa/acct_server_reverse_coa), which follows the
per-server flag in the draft and the existing auth_server_*/acct_server_*
layout. A single BSS-level option (e.g., radius_reverse_coa=1 for every
RADIUS/TLS server of the BSS) would be simpler to configure, since a
proxy can use either the authentication or the accounting connection.
I am happy to change it to that if preferred.

Not included, possible follow-ups:
- duplicate request detection per connection (RFC 5176, 2.3; RFC 6613,
  2.6.1 says the peer must not retransmit on the same connection)
- keeping the connection open with Status-Server watchdog messages and
  reconnecting when idle (draft-ietf-radext-reverse-coa-08, Section 5,
  Tw = 15 s); today, requests can only be received while a connection
  is open
- hostapd does not default to or enforce the shared secret "radsec" on
  RADIUS/TLS servers (pre-existing)
- the TLS handshake send path does not use MSG_NOSIGNAL, and
  application data received together with the last handshake message
  is not processed (both pre-existing)

Testing:
The new test cases need hostapd built with CONFIG_RADIUS_TLS=y (they are
skipped otherwise) and pyrad. With CONFIG_RADIUS_TLS=y added to
tests/hwsim/example-hostapd.config:

  ./run-tests.py radius_das_proxy_state 'radius_tls_reverse_coa_*'

or the whole module with ./run-tests.py -f radius. All radius_das_*,
radius_tls_* and new tests pass, also with hostapd built with
-fsanitize=address,undefined. Builds checked with and without
CONFIG_RADIUS_TLS (hwsim and default configs), wpa_supplicant and
eapol_test. The series was also run against a radsecproxy and a
FreeRADIUS server that send Disconnect-Request down the NAS's
connection, through two proxy hops.

Based on main at fcccc8c82.

Seb Adamski (7):
  RADIUS DAS: Initialize socket to -1 before bind
  RADIUS DAS: Echo Proxy-State in responses
  RADIUS: Reassemble RADIUS/TLS stream by RADIUS Length
  RADIUS DAS: Separate request processing from the UDP socket handler
  RADIUS: Accept Disconnect-Request and CoA-Request over RADIUS/TLS
  hostapd: Add reverse CoA over RADIUS/TLS configuration
  tests: RADIUS/TLS reverse CoA

 hostapd/config_file.c                         |   6 +
 hostapd/hostapd.conf                          |  16 +
 src/ap/ap_config.c                            |  35 +
 src/ap/hostapd.c                              | 107 ++-
 src/radius/radius_client.c                    | 625 +++++++++++++---
 src/radius/radius_client.h                    |  20 +
 src/radius/radius_das.c                       | 164 +++--
 src/radius/radius_das.h                       |  26 +
 .../auth_serv/radius_clients_radsec.conf      |   1 +
 tests/hwsim/dictionary.radius                 |   2 +
 tests/hwsim/radius_tls_server.py              | 429 +++++++++++
 tests/hwsim/test_radius.py                    | 677 +++++++++++++++++-
 12 files changed, 1940 insertions(+), 168 deletions(-)
 create mode 100644 tests/hwsim/auth_serv/radius_clients_radsec.conf
 create mode 100644 tests/hwsim/radius_tls_server.py


base-commit: fcccc8c82e0b33e348f3f34f3777186c9efc73f5
-- 
2.54.0 (Apple Git-157)





More information about the Hostap mailing list