Want to add a new column in radius log along with its populated values

I have added a coloum in radius log with a name IMEI and want to get the populated values but the catch is the value comes under Type 26 vendor specific, So for me pulling Type 26 is easy but with this nested condition Type 26 then inside it Type 20 is hard to pull the value and then populate it
for example
AVP: t=Vendor-Specific(26) l=24 vnd=3GPP(10415)
Type: 26
Length: 24
Vendor ID: 3GPP (10415)
VSA: t=3GPP-IMEISV(20) l=18 val=3563718618898701
Type: 20
Length: 18
3GPP-IMEISV: 3563718618898701

Hi there,

Let’s try to get this closer to running Zeek code. How are you accessing the attributes? Do you have a radius_message event handler and are trying to get to the right attribute(s)? If you have the right attribute, do you just need to do some string parsing on it? If so, Zeek has a bunch of string functions that might help you.

If you can isolate the problem to a small radius pcap and put some code in https://try.zeek.org so we can take a look at it, that’d be ideal.

Best,
Christian

Hi Christian,

Thank you for the suggestions!

I’ve encountered an issue where, although I can pull Type 26 successfully, the nested structure check for Type 20 seems to break down. Specifically, when checking for Type 26, the condition works fine, but when I try to access Type 20 inside it, the parsing seems to stop or break.

In the result$attribute buffer, there’s no sign of Type 20 being captured, and the output is showing something like this:

[26] = [\x00\x00\x05\xdb\x2a\x00\x00\x00\x00;\xff\x0cH3C WX5004< 19.100.100.6 f0:4b:ab:a7:21:3f;\x04R/N\xe2],
[26] = [\x00\x00\x00\x09\x01#client-mac-address=002b.7a0f.8e29],

None of the Type 26 values seem to contain Type 20. It’s possible that I’m missing something in the way I’m approaching the nested structure within Zeek.

Source code path : base/protocols/radius
file name : main.zeek

modified code for your reference
module RADIUS;

export {
        redef enum Log::ID += { LOG };

        global log_policy: Log::PolicyHook;

        type Info: record {
                ## Timestamp for when the event happened.
                ts           : time     &log;
                ## Unique ID for the connection.
                uid          : string   &log;
                ## The connection's 4-tuple of endpoint addresses/ports.
                id           : conn_id  &log;
                ## The username, if present.
                username     : string   &log &optional;
                ## MAC address, if present.
                mac          : string   &log &optional;
                ## The address given to the network access server, if
                ## present.  This is only a hint from the RADIUS server
                ## and the network access server is not required to honor
                ## the address.
                framed_addr  : addr     &log &optional;
                ## Address (IPv4, IPv6, or FQDN) of the initiator end of the tunnel,
                ## if present.  This is collected from the Tunnel-Client-Endpoint
                ## attribute.
                tunnel_client: string   &log &optional;
                ## Connect info, if present.
                connect_info : string   &log &optional;
                ## Reply message from the server challenge. This is
                ## frequently shown to the user authenticating.
                reply_msg    : string   &log &optional;
                ## Successful or failed authentication.
                result       : string   &log &optional;
                ## The duration between the first request and
                ## either the "Access-Accept" message or an error.
                ## If the field is empty, it means that either
                ## the request or response was not seen.
                ttl          : interval &log &optional;
                **# IMEI field added**
**                imei         : string   &log &optional;**

                ## Whether this has already been logged and can be ignored.
                logged       : bool     &default=F;
        };

        ## Event that can be handled to access the RADIUS record as it is sent on
        ## to the logging framework.
        global log_radius: event(rec: Info);

        ## RADIUS finalization hook.  Remaining RADIUS info may get logged when it's called.
        global finalize_radius: Conn::RemovalHook;
}

redef record connection += {
        radius: Info &optional;
};

const ports = { 1812/udp };
redef likely_server_ports += { ports };

event zeek_init() &priority=5
        {
        Log::create_stream(RADIUS::LOG, [$columns=Info, $ev=log_radius, $path="radius", $policy=log_policy]);
        Analyzer::register_for_ports(Analyzer::ANALYZER_RADIUS, ports);
        }

event radius_message(c: connection, result: RADIUS::Message) &priority=5
        {
        if ( ! c?$radius )
                {
                c$radius = Info($ts  = network_time(),
                                $uid = c$uid,
                                $id  = c$id);
                Conn::register_removal_hook(c, finalize_radius);
                }

        switch ( RADIUS::msg_types[result$code] )
                {
                case "Access-Request":
                        if ( result?$attributes )
                                {
                                print fmt("Attributes: %s", result$attributes); # Print all attributes
                                # User-Name
                                if ( ! c$radius?$username && 1 in result$attributes )
                                        c$radius$username = result$attributes[1][0];

                                # Calling-Station-Id (we expect this to be a MAC)
                                if ( ! c$radius?$mac && 31 in result$attributes )
                                        c$radius$mac = normalize_mac(result$attributes[31][0]);

                                # Tunnel-Client-EndPoint (useful for VPNs)
                                if ( ! c$radius?$tunnel_client && 66 in result$attributes )
                                        c$radius$tunnel_client = result$attributes[66][0];

                                # Connect-Info
                                if ( ! c$radius?$connect_info && 77 in result$attributes )
                                        c$radius$connect_info = result$attributes[77][0];

                                **# 3GPP IMEISV Attribute (VSA t=20)**
                                     if ( ! c$radius?$imei && 20 in result$attributes )
                                         {
                                            c$radius$imei = result$attributes[20][0];
                                         }
                                }
                        break;

                case "Access-Challenge":
                        if ( result?$attributes )
                                {
                                # Framed-IP-Address
                                if ( ! c$radius?$framed_addr && 8 in result$attributes )
                                        c$radius$framed_addr = raw_bytes_to_v4_addr(result$attributes[8][0]);

                                if ( ! c$radius?$reply_msg && 18 in result$attributes )
                                        c$radius$reply_msg = result$attributes[18][0];

                                }
                        break;

                case "Access-Accept":
                        c$radius$result = "success";
                        break;

                case "Access-Reject":
                        c$radius$result = "failed";
                        break;

                # TODO: Support RADIUS accounting. (add port 1813/udp above too)
                #case "Accounting-Request":
                #       break;
                #
                #case "Accounting-Response":
                #       break;
                }
        }

event radius_message(c: connection, result: RADIUS::Message) &priority=-5
        {
        if ( c$radius?$result )
                {
                local ttl = network_time() - c$radius$ts;
                if ( ttl != 0secs )
                        c$radius$ttl = ttl;

                Log::write(RADIUS::LOG, c$radius);

                delete c$radius;
                }
        }

hook finalize_radius(c: connection)
        {
        if ( c?$radius && ! c$radius$logged )
                {
                c$radius$result = "unknown";
                Log::write(RADIUS::LOG, c$radius);
                }
        }
  1. Do you have a PCAP you can share that has what you want in it?
  2. Often a very effective way to debug problems like this is to add print statements to dump out various variables/expressions to see for example whether it’s a problem in the analyzer (it’s not even providing the sub-attribute) or in your logic

I want to add the imei column and corresponding value , So I have resolved this issue by parsing the Type 26 to get the Type 20 value