Creating a proper RADIUS Accounting-Response packet in iRules

Creating a proper RADIUS Accounting-Response packet in iRules


If you do a lot of work with RADIUS messages being sent to your BIGIP so that you can get some information from another network node in the system, you are going to need to respond back to that node in a correct and proper way, so that you don’t mess it up, or otherwise fill up its error log with ‘improper response’ messages.

I was working on just that kind of iRule, and got it working. Here’s the code:

 1: ##
 2: ## RADIUS Acct-Resp packet
 3: ##
 4: ## input variables:
 5: ##  $rId     -> RADIUS Request ID field
 6: ##  $rAuth   -> RADIUS Request Authorization field
 7: ##  $static::SHARED_SECRET -> RADIUS Shared secret of the 2 nodes.
 8: ##
 9: UDP::drop           ;## kill the incoming packet, don’t need it.                                      
10: set RADcode 5       ;## RADIUS Accounting-Response Type code.
11: set md5hash [ format "05%02x00%02x" $rId 20 ]           
12: set md5hash $md5hash$rAuth$static::SHARED_SECRET        
13: set hash [ binary format H* $md5hash ]                  
14: set respAuth [md5 $hash]                                
15: set payldhdr [ binary format ccS $RADcode $rId 20 ]     
16: set payload $payldhdr$respAuth                          
17: UDP::respond $payload 

So the RADIUS Accounting-Response packet has, at a minimum,  four fields:  RADIUS Type code(1 byte), ID number(1 byte), Packet Length(2 byte Integer), and the Authenticator(16 bytes). Getting the response authenticator correct is really the only tricky part.

Line 5: The RADIUS Request ID field comes from the incoming packet. You can retrieve this value previously in the iRule using a line of code like this:  set rId [RADIUS::id]. Of course, this value can also be pulled out at the same time some other important fields are decoded using the sample code for line 6 below.

Line 6: The RADIUS Request Authorization field in the incoming packet is an MD5 hash of most if the incoming packet. To get this value, you will need to do a binary scan of the UDP packet:

    set payload [UDP::payload]
    binary scan $payload ccSH32a* rType rId rLen rAuth rPkt

In this example, we also pull out the Type Code (rType) and the packet ID (rId), the length of the overall packet, the Authorization field that we are looking for, and the rest of the packet, which should be all the AVP fields. 

Line 9: We don’t need (and usually don’t want) the RADIUS Request packet from going on from the BIGIP, so we just drop the packet to prevent this.


Line 10:  0x05 is the RADIUS Type Code for an Account-Response packet.


Line 11:  Here’s where we start working on our response authenticator. We are going to setup a hexstring (which is hex characters displayed as a string; IE> “05140016” is four bytes 0x05, 0x14, 0x00, 0x16), pack all of our values into it, convert it to real binary hex, do the MD5 hash on the result, and use the MD5 checksum result in the response packet. This line sets Type Code, id, and Length (hard coded to 20 bytes).


Line 12: Using the hexstring from the last line, it adds the Original request Authenticator, and the shared secret (which should already be in hexstring format) to it.  We now have our complete hexstring that needs to be run through the MD5 checksum function.


Line 13:  This line simply converts the hexstring to a binary hex value.


Line 14:  We get our valid Authenticator for the response.


Line 15:  Now we create the actual response packet. This line sets the first three fields: Type Code, id, and Length.


Line 16:  We add the header and the response Authenticator and we have our RADIUS Accounting-Response packet.


Line 17:  Send the response back.


The RADIUS Accounting-Response protocol is documented in RFC-2866, Section 4.2.


6 Answer(s):

# this irule shows how to craft a rfc compliant Radius Accouting Accept message
# upon an incoming Radius Accouting Request

when RULE_INIT {
set static::secret "mysecret"
binary scan $static::secret H* static::secrethex


# getting base information, see also RFC 2865
#0 1 2 3
#0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
#| Code | Identifier | Length |
#| Authenticator |
#| Attributes ...
binary scan [UDP::payload] cH2SH32 code ident len auth
# see also

# Checking if Radius Code is 4
# 4 Accounting-Request

if { $code == 4 } {

# setting response code
# 5 Accounting-Response
set code 5

# Accounting Response will be 20 bytes long
set len 20

# creating ResponseAuth =
# MD5(Code+ID+Length+RequestAuth+Attributes+Secret)

set md5me [binary format cH2SH32H8 $code $ident $len $auth $static::secrethex]

# running the MD5
set ResponseAuthRaw [ md5 $md5me]
# doing some string to binary conversion, i am sure i can avoid this, need to optimize it
binary scan $ResponseAuthRaw H* ResponseAuth
# crafting the response packet
set packetdata [binary format cH2SH32 $code $ident $len $ResponseAuth]
clientside { UDP::respond ${packetdata} }
else {
log local0. "Dropping Message"
Very useful!

I had a problem with it only using the first 4 characters of the shared secret. Made the adjustments as per below. Someone might have a more elegant way.

Added to RULE_INIT after set static::secret:
set static::secretlen [expr 2 * [string length $static::secret]]

Replaced existing set md5me line with:
set md5me [binary format cH2SH32H$static::secretlen $code $ident $len $auth $static::secrethex]

Hi all,

first of all, great job on this one, works flawlessly :-) i'm trying to understand couple of things here, and i would appreciate your help ,I could not understand what are the following steps are needed :

set md5me [binary format cH2SH32H$static::secretlen $code $ident $len $auth $static::secrethex]
# running the MD5 
set ResponseAuthRaw [ md5 $md5me] 
# doing some string to binary conversion, i am sure i can avoid this, need to optimize it 
binary scan $ResponseAuthRaw H* ResponseAuth 
# crafting the response packet 
set packetdata [binary format cH2SH32 $code $ident $len $ResponseAuth] 

he is creating response authenticator.

Response Authenticator

         The value of the Authenticator field in Access-Accept, Access-
         Reject, and Access-Challenge packets is called the Response
         Authenticator, and contains a one-way MD5 hash calculated over
         a stream of octets consisting of: the RADIUS packet, beginning
         with the Code field, including the Identifier, the Length, the
         Request Authenticator field from the Access-Request packet, and
         the response Attributes, followed by the shared secret.  That
         is, ResponseAuth =
         MD5(Code+ID+Length+RequestAuth+Attributes+Secret) where +
         denotes concatenation.

Remote Authentication Dial In User Service (RADIUS)

why do we need the binary scan? it's already done at the begining

why do we need the binary scan?

it converts binary (ResponseAuthRaw) to hex (ResponseAuth) which then is used to craft response (packetdata).

Your answer: