Forum Discussion

Peter_Z's avatar
Peter_Z
Icon for Cirrus rankCirrus
Nov 08, 2010

Radius Loadbalancing with iRule

Hello,

 

 

We need to write an iRule to loadbalance the RADIUS traffic. The users are authenticating with RADIUS servers which are load balanced on the F5 LTM. The ISP is doing a NAT so that every user UDP datagram is comming with the same source IP and source UDP port.

 

We are thinking to write an iRule to load balance the traffic based on the username so we could distribute those RADIUS requests evenly among multiple servers. The username value is basically in the following format:

 

 

XXXXXX@YYY

 

 

where XXXX is the username - it varies in value and length for different users, YYY is a domain (i.e. company.com).

 

We were thinking to build the iRule which reads the username value from the beginning up to '@' sign and depending on the ending character of that username will send the traffic to specific pool member and possibly create a persistence record (uie persistence).

 

 

We found some information on the following devcentral links:

 

 

http://devcentral.f5.com/tutorials/techtips/tabid/63/articletype/articleview/articleid/187/radius-load-balancing-with-irules.aspx

 

http://devcentral.f5.com/tutorials/techtips/tabid/63/articletype/articleview/articleid/149/radius-aware-load-balancing-via-irules.aspx

 

 

The first article seems to be not useful based on the comments below, so we tried to build an iRule based on the latter but we were not successful. We're seeing some errors in the logs.

 

 

Could you assist us to build such an iRule? We are in hurry and have to provide a solution to customer very soon. We are running v9.4.8.

 

 

Thank you

 

  • if you dont need persistent based on Radius username, could you just use UDP datagram LB? (enable one option in UDP profile that say something like "datagram LB")

     

     

    Nat
  • If the client <-> AAA server handshake contains more than 2 packets, it would probably mean that the second packet from the client can be sent to different server which we want to avoid.

     

  • This doesn't seem to solve the issue. The traffic is sent to single server only. I've added some lines to log which server was selected and it is always the same one:

     

     

    when CLIENT_DATA {

     

    if { [UDP::payload length] > 4 } {

     

    binary scan [UDP::payload] c@20a* hdr_code rest_string

     

    while { [string length $rest_string] > 4} {

     

    binary scan $rest_string cca* attr_id attr_length rest_string

     

    scan $attr_length %i length

     

    set ff [format "a%da*" [expr {$length} - 2]]

     

    switch $attr_id {

     

    1 {

     

    if the type of attrbuite is RADIUS_ATTR_USER_

     

    binary scan $rest_string $ff attr_value rest_string

     

    persist uie $attr_value

     

    set SERVER [LB::server addr]

     

    log "Username: $attr_value Sent to member $SERVER"

     

    break

     

    }

     

    default {

     

    binary scan $rest_string $ff attr_value rest_string

     

    }

     

    }

     

    }

     

    }

     

    }

     

    }

     

     

    And this is the log file excerpt:

     

     

    Nov 10 22:55:31 local/tmm info tmm[3356]: 01220002:6: Rule RADIUS_LB_2 : Username: admin@home Sent to member 10.20.0.201

     

    Nov 10 22:56:10 local/tmm info tmm[3356]: 01220002:6: Rule RADIUS_LB_2 : Username: root Sent to member 10.20.0.201

     

    Nov 10 23:00:51 local/tmm info tmm[3356]: 01220002:6: Rule RADIUS_LB_2 : Username: roii Sent to member 10.20.0.201

     

    Nov 10 23:00:57 local/tmm info tmm[3356]: 01220002:6: Rule RADIUS_LB_2 : Username: rsdsds Sent to member 10.20.0.201
  • not sure if you have the UDP datagram LB option enabled?

     

    you may also try changing CLIENT_DATA to CLIENT_ACCEPTED
  • Yes, we have datagram LB enabled and idle timeout set to immediate under UDP profile. When I changed event in the iRule from CLIENT_DATA to CLIENT_ACCEPTED, the rule seemed to pick up no server (or at least no server appeared in the logs):

     

     

     

    Nov 11 15:06:05 local/tmm info tmm[3377]: 01220002:6: Rule RADIUS_LB_2 : Username: abcd Sent to member

     

    Nov 11 15:06:15 local/tmm info tmm[3377]: 01220002:6: Rule RADIUS_LB_2 : Username: abcd Sent to member

     

     

     

    When changed back the server is logged again:

     

     

    Nov 11 15:07:02 local/tmm info tmm[3377]: 01220002:6: Rule RADIUS_LB_2 : Username: abcdwee Sent to member 10.20.0.201

     

    Nov 11 15:07:12 local/tmm info tmm[3377]: 01220002:6: Rule RADIUS_LB_2 : Username: abcdwee Sent to member 10.20.0.202

     

  • I'm trying to build similar iRule (starting from the switch command, rest is ommited):

     

     

    switch $attr_id {

     

    1 { if the type of attrbuite is RADIUS_ATTR_USER_NAME

     

    binary scan $rest_string $ff attr_value rest_string

     

    set USER_NAME [substr $attr_value 0 "@"]

     

    log "AAA ID: $attr_value User: $USER_NAME"

     

    persist uie $USER_NAME

     

    if { ($USER_NAME ends_with "a") or ($USER_NAME ends_with "b") }

     

    {

     

    log "Username $USER_NAME ends with a|b"

     

    pool RADIUS_POOL member 10.20.0.201

     

    }

     

    elseif { $USER_NAME ends_with "t"}

     

    {

     

    log "Username $USER_NAME ends with t"

     

    pool RADIUS_POOL member 10.20.0.202

     

    }

     

    else {log "Username $USER_NAME"}

     

    }

     

    default { binary scan $rest_string $ff attr_value rest_string }

     

    }

     

    }

     

    }

     

    }

     

     

    The problem is, that if we remove the default pool from the VS configuration, the traffic is never sent to the backend server.

     

  • i think when you move rule to CLIENT_ACCEPTED, it just hasn't pick server yet so log show nothing. you may try splitting those part... (move that log to client_data or lb_selected event instead. here is an example.

    
    when CLIENT_ACCEPTED {
      if { [UDP::payload length] > 4 } {
        binary scan [UDP::payload] c@20a* hdr_code rest_string
          while { [string length $rest_string] > 4} {
            binary scan $rest_string cca* attr_id attr_length rest_string
            scan $attr_length %i length
            set ff [format "a%da*" [expr {$length} - 2]]
            switch $attr_id {
                   1 {
                        if the type of attrbuite is RADIUS_ATTR_USER_
                        binary scan $rest_string $ff attr_value rest_string
                        persist uie $attr_value
                        break
                      }
                    default { 
                       binary scan $rest_string $ff attr_value rest_string
                     }
                    }
              }
           } 
       }
     } 
    when CLIENT_DATA {
                        set SERVER [LB::server addr]
                        log "Username: $attr_value Sent to member $SERVER"
    }
    
    

    one thing to note, if you choose timeout immediate, if src port of radius request has been changed, when server reply with radius response, LTM may not have information to transform src port back to original. (if your radius client does not care about the destination port of returning packet, you may ignore this)

    I would suggest UDP datagram LB with at least a few seconds timeout (or timeout which is long enough for your server to process the packet).

    Nat