The Radius Format

Each Radius UDR will be packed containing both the request and response UDRs for each case, along side the general NAS information, such as port and IP address.

Included with the Radius bundle is a general Radius format, containing all possible record types valid for Radius. The Radius agent will use this format for recognising the type of data. The actual decoding of the contents (requestMessage), and the encoding of the reply (responseMessage) must be handled through a user defined format.

Note!

Never change or reuse Radius output UDR from the Radius server.


FieldDescription

attributeType (int)

If the packetType is either ACCOUNTINT_REQUEST, DISCONNECT_REQUEST, or CHANGE_OF_AUTHORIZATION_REQUEST, this field will contain the value of the Acct-Status-Type attribute, otherwise it will have the value 0.

context (any)

This field stores information about the context in which the operation has been invoked.

hashKey (string)

Provided that duplicate checking is enabled, and CRC32 is selected as method, this field will contain the calculated check sum for the packet. For all other cases it will be set to NULL.

packetType (int)

This field indicates the type of Radius request, ACCESS_REQUEST, ACCOUNTING_REQUEST, DISCONNECT_REQUEST, or CHANGE_OF_AUTHORIZATION_REQUEST.

remoteIP (ipaddress)

The IP address of the NAS.

remotePort (int)

The port used for communication with the NAS.

requestMessage (bytearray)

This field contains a request UDR. Depending on the settings on the NAS, it can be any of the following packet types:

ACCESS_REQUEST
ACCOUNTING_REQUEST
DISCONNECT_REQUEST
CHANGE_OF_AUTHORIZATION_REQUEST 

In terms of authentication, CHAP is supported. PAP, MSCHAP and EAP are not fully supported. To use these protocols, you must configure the APL code as required. See the example below, APL code to configure authentication using CHAP.

For details on these standards, see RFC 2865 (http://www.ietf.org/rfc/rfc2865.txt), RFC 2866 (http://www.ietf.org/rfc/rfc2866.txt) and RFC 5176 (http://www.ietf.org/rfc/rfc5176.txt).

An Ultra Format Definition must be designed to handle decoding of this field.

responseMessage (bytearray)

The field containing a response UDR. Depending on the settings on the NAS, it can be any of the following packet types:

ACCESS_ACCEPT
ACCESS_REJECT
ACCOUNTING_RESPONSE
DISCONNECT_ACK
DISCONNECT_NAK
CHANGE_OF_AUTHORIZATION_ACK
CHANGE_OF_AUTHORIZATION_NAK 

In terms of authentication, CHAP is supported. PAP, MSCHAP and EAP are not fully supported. To use these protocols, you must configure the APL code as required. See the example below, APL code to configure authentication using CHAP.

For details on these standards, see RFC 2865 (http://www.ietf.org/rfc/rfc2865.txt), RFC 2866 (http://www.ietf.org/rfc/rfc2866.txt) and RFC 5176 (http://www.ietf.org/rfc/rfc5176.txt).

An Ultra Format Definition must be designed to handle decoding of this field.

sourceIP (ipaddress)

The source IP address.

sourcePort (int)

The source port.

statusMessage (string)

The status message field is set to communicate status while a message is being sent or received.

throttled (boolean)

This flag indicates whether the UDR has been throttled or not. Default is false, and if the UDR has been throttled, it will be set to yes. 

Example - APL code to configure authentication using CHAP

import ultra.Default.rad_def;
 
initialize {
    hashSetAlgorithm("MD5");
}
 
boolean isValidCHAP_Passwd(byte chap_iden,
                           bytearray chap_passwd, 
                           bytearray password, 
                           bytearray authenticator)
{
    bytearray calc1 = baCreate(1);
 
    baSet(calc1,0, chap_iden);
 
    bytearray calc2 = baAppend(calc1, password);
    bytearray calc3 = baAppend(calc2, authenticator);
 
    hashReset();
    hashUpdate(calc3);
 
    bytearray res = hashDigest();
 
    if (baSize(res) != baSize(chap_passwd)) {
        return false;
    }
 
    int len = baSize(res);
    int cnt = 0;
 
    while (cnt < len) {
 
       if (baGet(res,cnt) != baGet(chap_passwd, cnt))
       {
           return false;
       }
  
       cnt = cnt + 1;
    }
 
    // debug ("CHAP OK   !!!!!!!!!!!!");
 
    return true;
}
 
consume {
    Radius r = (Radius)input;  
 
    string result;
    list<drudr> UDRlist = listCreate(drudr);
 
    if (r.requestMessage == null)
    {
       debug("Error???!!");
       return;
    }
 
    result = udrDecode( "Request_Dec", // Decoder Name
                        UDRlist,
                        r.requestMessage,
                        true);
 
    if (null != result)
    {
        debug("udrDecode FAILED: "+result);
        return;
    }
 
 
    int i;
    int antObjects = listSize(UDRlist);
    drudr aReq;
     
 
    while (i < antObjects)
    {
        aReq = listGet(UDRlist, i);
 
        if (instanceOf(aReq, Accounting_Request_Int))
        {
             // debug("Accounting Request from: "+r.remoteIP+":"+r.remotePort);
             Accounting_Request_Int  req  = (Accounting_Request_Int)aReq;
             Accounting_Response_Int resp = udrCreate(Accounting_Response_Int);
             resp.Code          = 5;
             resp.Identifier    = req.Identifier;
             resp.Authenticator = req.Authenticator;
             resp.NAS_Port      = req.Identifier;
             r.responseMessage  = udrEncode("Response_Enc", resp);
             debug("Accounting Request: "+req.Identifier);
             udrRoute(r, "Responses");
        }
        else if (instanceOf(aReq, Access_Request_Int))
        {
             boolean shouldReject = false;
             string s_passwd = "hemligt"; // Lookup based on User-Name
             bytearray b_passwd;
             strToBA (b_passwd, s_passwd);
 
             debug("Access Request from: "+r.remoteIP+":"+r.remotePort);
             Access_Request_Int  req  = (Access_Request_Int)aReq;
             Access_Accept_Int resp   = udrCreate(Access_Accept_Int);
             Access_Reject_Int rej    = udrCreate(Access_Reject_Int);
 
             rej.Code            = 3;
             rej.Identifier      = req.Identifier;
             rej.Authenticator   = req.Authenticator;
 
             resp.Code            = 2;
             resp.Identifier      = req.Identifier;
             resp.Authenticator   = req.Authenticator;
             resp.Login_TCP_Port  = 6789;            // Dummy for my tests ONLY
             resp.NAS_Port        = req.Identifier;  // Dummy for my tests ONLY
              
             if (udrIsPresent(req.CHAP_Password))
             {
                 if (udrIsPresent(req.CHAP_Challenge)) {
                     // debug("CHAP Challenge based: "+req.CHAP_Challenge);
 
                     if (!isValidCHAP_Passwd(req.CHAP_Iden,req.CHAP_Password, b_passwd, req.CHAP_Challenge))
                     {
                          list<string> msg = listCreate(string, "CHAP failure: CHAP_Challenge based.");
                         shouldReject = true;
                         rej.Reply_Message = msg;
                     }
                 }
                 else {
                     string auth = baToStr(req.Authenticator);    
                     // debug("Authenticator based: "+auth);
 
                     if (!isValidCHAP_Passwd(req.CHAP_Iden,req.CHAP_Password, b_passwd, req.Authenticator))
                     {
                         list<string> msg = listCreate(string, "CHAP failure: Authenticator based.");
                         shouldReject = true;
                         rej.Reply_Message = msg;
                     }
                 }
 
                 if (udrIsPresent(req.User_Password)) {
                    list<string> msg = listCreate(string, "Protocol ERROR: Both User_Password and CHAP_Password is present.");
                    shouldReject = true;
                    rej.Reply_Message = msg;
                 }
             }
             else if (udrIsPresent(req.User_Password))
             {
                 // debug("User_Password: "+req.User_Password);
 
                 if ( (! strStartsWith(req.User_Password, s_passwd)) || (strLength(s_passwd) != strLength(req.User_Password))) {
                    list<string> msg = listCreate(string, "Invalid user name/password.");
                    shouldReject = true;
                    rej.Reply_Message = msg;
                 }
             }
             else
             {
                  list<string> msg = listCreate(string, "STATE NOT SUPPORTED YET!");
                  shouldReject = true;
                  rej.Reply_Message = msg;
             }
 
             if (shouldReject)
             {
                r.responseMessage    = udrEncode("Response_Enc", rej);
             }
             else 
             {
                r.responseMessage    = udrEncode("Response_Enc", resp);
             }
 
             debug(r.responseMessage);
             udrRoute(r, "Responses");
        }
        else
        {
              debug("*** Invalid radius request from "+r.remoteIP+":"+r.remotePort+" ***");
        }
 
        i = i + 1;
    }  
}