# We check for this prefix to determine whether the class value was # generated by this server. It should be changed so that it is # globally unique. class_value_prefix = 'ai:' # # Replacement for the old rlm_acct_unique module # acct_unique { # # If we have a class attribute in the format # 'auth_id:[0-9a-f]{32}' it'll have a local value # (defined by insert_acct_class), this ensures # uniqueness and suitability. # # We could just use the Class attribute as # Acct-Unique-Session-Id, but this may cause problems # with NAS that carry Class values across between # multiple linked sessions. So we rehash class with # Acct-Session-ID to provide a truely unique session # identifier. # # Using a Class/Session-ID combination is more robust # than using elements in the Accounting-Request, # which may be subject to change, such as # NAS-IP-Address, Client-IP-Address and # NAS-Port-ID/NAS-Port. # # This policy should ensure that session data is not # affected if NAS IP addresses change, or the client # roams to a different 'port' whilst maintaining its # initial authentication session (Common in a # wireless environment). # update request { &Tmp-String-9 := "${policy.class_value_prefix}" } if (("%{hex:&Class}" =~ /^%{hex:&Tmp-String-9}/) && \ ("%{string:&Class}" =~ /^${policy.class_value_prefix}([0-9a-f]{32})/i)) { update request { &Acct-Unique-Session-Id := "%{md5:%{1},%{Acct-Session-ID}}" } } # # Not All devices respect RFC 2865 when dealing with # the class attribute, so be prepared to use the # older style of hashing scheme if a class attribute # is not included # else { update request { &Acct-Unique-Session-Id := "%{md5:%{User-Name},%{Acct-Session-ID},%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}},%{NAS-Identifier},%{NAS-Port-ID},%{NAS-Port}}" } } update request { &Tmp-String-9 !* ANY } } # # Insert a (hopefully unique) value into class # insert_acct_class { update reply { &Class = "${policy.class_value_prefix}%{md5:%t,%{Packet-Src-Port},%{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}},%{NAS-IP-Address},%{Calling-Station-ID},%{User-Name},%{session-state:User-Name} }" } } # # Merges Acct-[Input|Output]-Octets and Acct-[Input|Output]-Gigawords into Acct-[Input|Output]-Octets64 # # If the &Attr-Foo doesn't exist, it's value is taken as zero. # acct_counters64.preacct { update request { &Acct-Input-Octets64 = "%{expr:(&Acct-Input-Gigawords << 32) | &Acct-Input-Octets}" &Acct-Output-Octets64 = "%{expr:(&Acct-Output-Gigawords << 32) | &Acct-Output-Octets}" } } # # There is a delay between sending the Access-Accept and receiving # the corresponding Accounting-Request "start" packet. This delay # can be leveraged by a user to bypass Simultaneous-Use checks. # # The user can start up multiple sessions at the same time. When # that happens, both Simultaneous-Use checks are performed before any # Accounting-Request packet is received. Both Simultaneous-Use # checks will result in "no user session" in the radacct table, and # both sessions will be allowed. At some point later in time, the # Accounting-Request packets are received. But by then it's too # late. # # The solution is to insert a temporary session into the "radacct" # table, during the "post-auth" section. This is done by # uncommenting the "sql_session_start" entry in # sites-enabled/default. Then, reading # raddb/mods-config/sql/main/*/queries.conf, and looking for the # "sql_session_start" comments. Follow the instructions there to # finalize the configuration. # # The server will then create a temporary entry in "radacct" before # it returns the Access-Request. Any other Access-Request which is # received at the same time will then have it's Simultaneous-Use # check see that entry, and will be rejected. # # Subsequent Accounting-Request packets for the first session will # then UPDATE (not INSERT) the data for the session. # # There is still a small race condition as the Simultaneous-Use # checks are not done at the same time as updating radacct. But the # window of opportunity is much smaller. i.e. milliseconds, instead # of seconds. # # This policy can also be used to "bootstrap" accounting sessions. # If there is data which is only available in the Access-Request, # it can be placed in the accounting table. Then, when accounting # packets are received, they will update the row which contains # the session information. # sql_session_start.post-auth { acct_unique sql.accounting }