summaryrefslogtreecommitdiffstats
path: root/raddb/sites-available/coa-relay
diff options
context:
space:
mode:
Diffstat (limited to 'raddb/sites-available/coa-relay')
-rw-r--r--raddb/sites-available/coa-relay366
1 files changed, 366 insertions, 0 deletions
diff --git a/raddb/sites-available/coa-relay b/raddb/sites-available/coa-relay
new file mode 100644
index 0000000..7dac7b1
--- /dev/null
+++ b/raddb/sites-available/coa-relay
@@ -0,0 +1,366 @@
+# -*- text -*-
+######################################################################
+#
+# This virtual server simplifies the process of sending CoA-Request or
+# Disconnect-Request packets to a NAS.
+#
+# This virtual server will receive CoA-Request or Disconnect-Request
+# packets that contain *minimal* identifying information. e.g. Just
+# a User-Name, or maybe just an Acct-Session-Id attribute. It will
+# look up that information in a database in order to find the rest of
+# the session data. e.g. NAS-IP-Address, NAS-Identifier, NAS-Port,
+# etc. That information will be added to the packet, which will then
+# be sent to the NAS.
+#
+# This process is useful because NASes require the CoA packets to
+# contain "session identification" attributes in order to to do CoA
+# or Disconnect. If the attributes aren't in the packet, then the
+# NAS will NAK the request. This NAK happens even if you ask to
+# disconnect "User-Name = bob", and there is only one session with a
+# "bob" active.
+#
+# Using this virtual server makes the CoA or Disconnect process
+# easier. Just tell FreeRADIUS to disconnect "User-Name = bob", and
+# FreeRADIUS will take care of adding the "session identification"
+# attributes.
+#
+# The process is as follows:
+#
+# - A CoA/Disconnect-Request is received by FreeRADIUS.
+# - The radacct table is searched for active sessions that match each of
+# the provided identifier attributes: User-Name, Acct-Session-Id. The
+# search returns the owning NAS and Acct-Unique-Id for the matching
+# session/s.
+# - The original CoA/Disconnect-Request content is written to a detail file
+# with custom attributes representing the NAS and Acct-Session-Id.
+# - A detail reader follows the file and originates CoA/Disconenct-Requests
+# containing the original content, relayed to the corresponding NAS for
+# each session using the custom attributes.
+#
+# This simplifies scripting directly against a set of NAS devices since a
+# script need only send a single CoA/Disconnect to FreeRADIUS which will
+# then:
+#
+# - Lookup all active sessions belonging to a user, in the case that only a
+# User-Name attribute is provided in the request
+# - Handle routing of the request to the correct NAS, in the case of a
+# multi-NAS setup
+#
+# For example, to disconnect a specific session:
+#
+# $ echo 'Acct-Session-Id = "769df3 312343"' | \
+# radclient 127.0.0.1 disconnect testing123
+#
+# To perform a CoA update of all active sessions belonging to a user:
+#
+# $ cat <<EOF | radclient 127.0.0.1 coa testing123
+# User-Name = bob
+# Cisco-AVPair = "subscriber:sub-qos-policy-out=q_out_uncapped"
+# EOF
+#
+# In addition to configuring and activating this site, a detail
+# writer module must be configured in mods-enabled:
+#
+# detail detail_coa {
+# filename = ${radacctdir}/detail_coa
+# escape_filenames = no
+# permissions = 0600
+# header = "%t"
+# locking = yes
+# }
+#
+
+
+# Listen on a local CoA port.
+#
+# This uses the normal set of clients, with the same secret as for
+# authentication and accounting.
+#
+listen {
+ type = coa
+ ipaddr = 127.0.0.1
+ port = 3799
+ virtual_server = coa
+}
+
+#
+# Receive CoA/Disconnect, lookup sessions, write them to a detail file
+#
+server coa {
+ #
+ # When a packet is received, it is processed through the
+ # recv-coa section. This applies to *both* CoA-Request and
+ # Disconnect-Request packets.
+ #
+ recv-coa {
+ #
+ # Lookup all active sessions matching User-Name and/or
+ # Acct-Session-Id and write each session (which includes to
+ # owning NAS and session ID) to a detail file.
+ #
+ # Returns a single result in the format:
+ #
+ # NasIpAddress1#AcctSessionId1|NasIPAddress2#AcctSessionId2|...
+ #
+ # i.e. each session is separated by '|', and attributes
+ # within a session are separated by '#'
+ #
+ # You will likely have to update the SELECT to add in
+ # any other "session identification" attributes
+ # needed by the NAS. These may include NAS-Port,
+ # NAS-Identifier, etc. Only the NAS vendor knows
+ # what these attributes are unfortunately, so we
+ # cannot give more detailed advice here.
+ #
+ update control {
+
+ #
+ # Example MySQL lookup
+ #
+# Tmp-String-0 := "%{sql:SELECT IFNULL(GROUP_CONCAT(CONCAT(nasipaddress,'#',acctsessionid) separator '|'),'') FROM (SELECT * FROM radacct WHERE ('%{User-Name}'='' OR UserName='%{User-Name}') AND ('%{Acct-Session-Id}'='' OR acctsessionid = '%{Acct-Session-Id}') AND AcctStopTime IS NULL) a}"
+
+ #
+ # Example PostgreSQL lookup
+ #
+# Tmp-String-0 := "%{sql:SELECT STRING_AGG(CONCAT(nasipaddress,'#',acctsessionid),'|') FROM (SELECT * FROM radacct WHERE ('%{User-Name}'='' OR UserName='%{User-Name}') AND ('%{Acct-Session-Id}'='' OR acctsessionid = '%{Acct-Session-Id}') AND AcctStopTime IS NULL) a}"
+
+ }
+
+ #
+ # Split the string and split into pieces.
+ #
+ if (&control:Tmp-String-0 != "" && "%{explode:&control:Tmp-String-0 |}") {
+ foreach &control:Tmp-String-0 {
+ if ("%{Foreach-Variable-0}" =~ /([^#]*)#(.*)/) {
+ update request {
+ CoA-Packet-Type := "%{Packet-Type}"
+
+ #
+ # Or use CoA-Packet-DST-IPv6-Address
+ # for IPv6 address.
+ #
+ CoA-Packet-DST-IP-Address := "%{1}"
+
+ CoA-Acct-Session-Id := "%{2}"
+
+ #
+ # Add any other attributes here.
+ #
+
+ # Set the CoA/Disconnect port
+ CoA-Packet-DST-Port := 1700
+
+ # SQL-User-Name was left over
+ # from the xlat
+ SQL-User-Name !* ANY
+
+ # Uncomment if the NAS does not
+ # expect User-Name
+ #User-Name !* ANY
+
+ }
+
+ #
+ # If we're sending a CoA packet, send it out.
+ #
+ if ((CoA-Packet-DST-IP-Address || CoA-Packet-DST-IPv6-Address) && \
+ CoA-Acct-Session-Id != "") {
+ detail_coa.accounting
+ }
+ }
+ }
+ } else {
+ # No sessions found
+ reject
+ }
+
+ }
+}
+
+#
+# Detail file reader that processes the queue of CoA/Disconnect requests
+#
+server coa-buffered-reader {
+ listen {
+ #
+ # See sites-available/buffered-sql for more details on
+ # all the options available for the detail reader.
+ #
+ type = detail
+ filename = "${radacctdir}/detail_coa"
+ load_factor = 90
+ track = yes
+ }
+
+ #
+ # For historical reasons packets from the detail file reader are
+ # processed through the "accounting" section.
+ #
+ accounting {
+ switch &CoA-Packet-Type {
+ case "Disconnect-Request" {
+ update {
+ # Include given attributes
+ disconnect: += request:[*]
+
+ disconnect:Packet-DST-Port := \
+ &CoA-Packet-DST-Port
+
+ disconnect:Acct-Session-Id := \
+ &CoA-Acct-Session-Id
+
+ # Some NASs want these, others don't
+ disconnect:Event-Timestamp := "%l"
+ disconnect:Message-Authenticator := 0x00
+
+ #
+ # Remove attributes which will confuse the NAS
+ #
+ # The NAS will "helpfully" NAK the packet
+ # if it contains attributes which are NOT
+ # "session identification" attributes.
+ #
+ # Those attributes should be listed here.
+ #
+ disconnect:Acct-Delay-Time !* ANY
+ disconnect:Proxy-State !* ANY
+ }
+
+ if (CoA-Packet-DST-IP-Address) {
+ update {
+ disconnect:Packet-DST-IP-Address := \
+ &CoA-Packet-DST-IP-Address
+ }
+ } else {
+ update {
+ disconnect:Packet-DST-IPv6-Address := \
+ &CoA-Packet-DST-IPv6-Address
+ }
+ }
+ }
+
+ case "CoA-Request" {
+ update {
+ # Include given attributes
+ coa: += request:[*]
+
+ coa:Packet-DST-Port := \
+ &CoA-Packet-DST-Port
+
+ coa:Acct-Session-Id := \
+ &CoA-Acct-Session-Id
+
+ # Some NASs want these, others don't
+ coa:Event-Timestamp := "%l"
+ coa:Message-Authenticator := 0x00
+
+ #
+ # Remove attributes which will confuse the NAS
+ #
+ # The NAS will "helpfully" NAK the packet
+ # if it contains attributes which are NOT
+ # "session identification" attributes.
+ #
+ # Those attributes should be listed here.
+ #
+ coa:Acct-Delay-Time !* ANY
+ coa:Proxy-State !* ANY
+ }
+
+ if (CoA-Packet-DST-IP-Address) {
+ update {
+ coa:Packet-DST-IP-Address := \
+ &CoA-Packet-DST-IP-Address
+ }
+ } else {
+ update {
+ coa:Packet-DST-IPv6-Address := \
+ &CoA-Packet-DST-IPv6-Address
+ }
+ }
+
+ }
+ }
+
+ #
+ # ACK the CoA / Disconnect packet.
+ #
+ ok
+ }
+}
+
+
+# The CoA packet is in the "proxy-request" attribute list.
+# The CoA reply (if any) is in the "proxy-reply" attribute list.
+#
+server originate-coa-relay {
+ #
+ # Handle the responses here.
+ #
+ post-proxy {
+ switch &proxy-reply:Packet-Type {
+ case CoA-ACK {
+ ok
+ }
+
+ case CoA-NAK {
+ # the NAS didn't like the CoA request
+ ok
+ }
+
+ case Disconnect-ACK {
+ ok
+ }
+
+ case Disconnect-NAK {
+ # the NAS didn't like the Disconnect request
+ ok
+ }
+
+ # Invalid packet type. This shouldn't happen.
+ case {
+ fail
+ }
+ }
+
+ #
+ # These methods are run when there is NO response
+ # to the request.
+ #
+ Post-Proxy-Type Fail-CoA {
+ ok
+ }
+
+ Post-Proxy-Type Fail-Disconnect {
+ ok
+ }
+ }
+}
+
+
+#
+# Homeserver CoA / Disconnect endpoints
+#
+# See proxy.conf for more details on configuring a home_server and
+# home_server_pool.
+#
+home_server coa-nas1 {
+ type = coa
+
+ # Update these to match your NAS
+ ipaddr = 192.0.2.1
+ port = 1700
+ secret = testing1234
+
+ coa {
+ irt = 2
+ mrt = 16
+ mrc = 5
+ mrd = 30
+ }
+}
+home_server_pool coa-nas1 {
+ type = fail-over
+ home_server = coa-nas1
+ virtual_server = originate-coa-relay
+}