#!/usr/bin/perl # # Copyright (C) 2008 Sky Network Services. # Copyright (C) 2022 Network RADIUS. # # This program is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. # use strict; use warnings; use threads; use threads::shared; use Net::Radius::Packet; use Net::Radius::Dictionary; use NetSNMP::agent qw/:all/; use NetSNMP::ASN qw/:all/; use Socket qw(inet_ntop); use IO::Socket::INET; use Digest::HMAC_MD5; use Log::Log4perl qw/:easy/; #use Data::Dumper; #$Data::Dumper::Indent = 1; #$Data::Dumper::Sortkeys = 1; #$| = 1; my $cfg = { snmp => { agent => { Name => 'freeradius-snmp', AgentX => 1, }, oid_root => '1.3.6.1.2.1.67', oid_sub => { 1 => [qw/auth proxyauth/], 2 => [qw/acct proxyacct/], }, }, radius => { host => 'localhost', port => 18120, secret => 'adminsecret', # dictionary => '../radiusd/share/dictionary', dictionary => 'dictionary.hacked', refresh_rate => 20, }, log => { level => $WARN, # $DEBUG, $ERROR, etc. layout => '%d{ISO8601} <%p> (%L) %m%n', file => 'STDERR' }, clients => 1, # Or 0 to disable }; Log::Log4perl->easy_init($cfg->{log}); INFO 'starting'; my $running :shared; my %snmp_data :shared; my @snmp_data_k :shared; my %snmp_next :shared; INFO 'initializing snmp'; my $agent = new NetSNMP::agent(%{$cfg->{snmp}->{agent}}); radius_stats_init(); $running = 1; $SIG{INT} = sub { INFO 'stopping'; $running = 0; }; # # Background updater thread # INFO 'launching radius client thread'; threads->create(\&radius_updater); # # We export only the radiusAuthServ and radiusAccServ subtree # $agent->register( $cfg->{snmp}->{agent}->{Name}, $cfg->{snmp}->{oid_root}.'.'.$_, \&snmp_handler) or die foreach keys %{$cfg->{snmp}->{oid_sub}}; INFO 'entering client main loop'; $agent->agent_check_and_process(1) while $running; $agent->shutdown(); $_->join() for threads->list(); # # Initialize common radius client stuff # sub radius_stats_init { our ( $d, $s, $rid ); $d = new Net::Radius::Dictionary; $d->readfile($cfg->{radius}->{dictionary}); srand ($$ ^ time); $rid = int rand 255; $s = new IO::Socket::INET( PeerHost => $cfg->{radius}->{host}, PeerPort => $cfg->{radius}->{port}, Proto => 'udp', Timeout => 5) or die; } # # Build server status packet, send it, fetch and parse the result # sub radius_stats_get { my ($type, %args) = @_; our ($d, $s, $rid); my $p_req = new Net::Radius::Packet $d; $p_req->set_code('Status-Server'); $p_req->set_vsattr('FreeRADIUS', 'FreeRADIUS-Statistics-Type', $type); $p_req->set_vsattr('FreeRADIUS', $_, $args{$_}) foreach keys %args; # # Update id # $p_req->set_identifier($rid++); $p_req->set_authenticator(pack 'C*', map { int rand 255 } 0..15); # # Recalc authenticator # $p_req->set_attr('Message-Authenticator', "\0"x16, 1); $p_req->set_attr('Message-Authenticator', Digest::HMAC_MD5::hmac_md5($p_req->pack, $cfg->{radius}->{secret}), 1); # # Send brand new and shiny request # $s->send($p_req->pack) or die; my $p_data; if (defined $s->recv($p_data, 2048)) { my $p_res = new Net::Radius::Packet $d, $p_data; my %response = map { $_ => $p_res->vsattr($d->vendor_num('FreeRADIUS'), $_)->[0] } $p_res->vsattributes($d->vendor_num('FreeRADIUS')); return \%response; } else { warn "no answer, $!\n"; return undef; } } # # Wrappers for specific types of stats # sub radius_stats_get_global { return radius_stats_get(0x1f); } sub radius_stats_get_client { return radius_stats_get(0x3f, 'FreeRADIUS-Stats-Client-Number' => $_[0]); } # # Main loop of thread fetching status from freeradius server # sub radius_updater { while ($running) { INFO 'fetching new data'; my $main_stat = radius_stats_get_global(); if (defined $main_stat) { my @clients_stat = (); if ($cfg->{clients}) { my $client_id = 0; while (1) { my $client_stat = radius_stats_get_client($client_id); last unless exists $client_stat->{'FreeRADIUS-Stats-Client-IP-Address'} || exists $client_stat->{'FreeRADIUS-Stats-Client-IPv6-Address'}; push @clients_stat, $client_stat; $client_id += 1; } } INFO 'got data, updating stats'; radius_snmp_stats($main_stat, \@clients_stat); } else { WARN 'problem with fetching data'; } INFO 'stats updated, sleeping'; my $now = time; my $next_stats_time = $now + $cfg->{radius}->{refresh_rate}; do { sleep 1; $now = time; } while ($now < $next_stats_time && $running); } } # # Helper to get a dotted string from NetSNMP::OID # sub oid_s { return join '.', $_[0]->to_array; } # # Handler for snmp requests from master agent # sub snmp_handler { DEBUG 'got new request'; my ($handler, $registration_info, $request_info, $requests) = @_; lock %snmp_data; lock @snmp_data_k; lock %snmp_next; for (my $request = $requests; $request; $request = $request->next()) { INFO 'request type '.$request_info->getMode.' for oid: '.oid_s($request->getOID); if ($request_info->getMode == MODE_GET) { my $oid_s = oid_s($request->getOID); if (exists $snmp_data{$oid_s}) { $request->setValue($snmp_data{$oid_s}->[0], ''.$snmp_data{$oid_s}->[1]); } } elsif ($request_info->getMode == MODE_GETNEXT) { # # Do a fast lookup if we can... # my $oid = $snmp_next{oid_s($request->getOID)}; # # ... otherwise take the slow route # unless (defined $oid) { foreach ( @snmp_data_k ) { #the keys is sorted in ascending order, so we are looking for #first value bigger than one in request if ($request->getOID < NetSNMP::OID->new($_)) { $oid = $_; last; } } } next unless $oid; $request->setValue($snmp_data{$oid}->[0], ''.$snmp_data{$oid}->[1]); $request->setOID($oid); } else { $request->setError($request_info, SNMP_ERR_READONLY); # No write support } } DEBUG 'finished processing the request'; } # # Init part of subtree for handling radius AUTH statistics # sub radius_snmp_stats_init_auth { my ($snmp_data_n, $oid, $clients) = @_; @{$snmp_data_n->{$oid.'.1.1.1.1'} = &share([])} = (ASN_OCTET_STR, ''); # radiusAuthServIdent @{$snmp_data_n->{$oid.'.1.1.1.2'} = &share([])} = (ASN_TIMETICKS, 0); # radiusAuthServUpTime @{$snmp_data_n->{$oid.'.1.1.1.3'} = &share([])} = (ASN_TIMETICKS, 0); # radiusAuthServResetTime @{$snmp_data_n->{$oid.'.1.1.1.4'} = &share([])} = (ASN_INTEGER, 0); # radiusAuthServConfigReset @{$snmp_data_n->{$oid.'.1.1.1.5'} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServTotalAccessRequests @{$snmp_data_n->{$oid.'.1.1.1.6'} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServTotalInvalidRequests @{$snmp_data_n->{$oid.'.1.1.1.7'} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServTotalDupAccessRequests @{$snmp_data_n->{$oid.'.1.1.1.8'} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServTotalAccessAccepts @{$snmp_data_n->{$oid.'.1.1.1.9'} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServTotalAccessRejects @{$snmp_data_n->{$oid.'.1.1.1.10'} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServTotalAccessChallenges @{$snmp_data_n->{$oid.'.1.1.1.11'} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServTotalMalformedAccessRequests @{$snmp_data_n->{$oid.'.1.1.1.12'} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServTotalBadAuthenticators @{$snmp_data_n->{$oid.'.1.1.1.13'} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServTotalPacketsDropped @{$snmp_data_n->{$oid.'.1.1.1.14'} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServTotalUnknownTypes # # radiusAuthClientExtTable # for (1 .. scalar @$clients) { my $addrtype; my $addr; if (exists $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IP-Address'}) { $addrtype = 1; $addr = $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IP-Address'}; } elsif (exists $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IPv6-Address'}) { $addrtype = 2; $addr = inet_ntop(AF_INET6, $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IPv6-Address'}); } else { next; } @{$snmp_data_n->{$oid.'.1.1.1.16.1.1.'.$_} = &share([])} = (ASN_INTEGER, $_); # radiusAuthClientExtIndex @{$snmp_data_n->{$oid.'.1.1.1.16.1.2.'.$_} = &share([])} = (ASN_INTEGER, $addrtype); # radiusAuthClientInetAddressType @{$snmp_data_n->{$oid.'.1.1.1.16.1.3.'.$_} = &share([])} = (ASN_OCTET_STR, $addr); # radiusAuthClientInetAddress @{$snmp_data_n->{$oid.'.1.1.1.16.1.4.'.$_} = &share([])} = (ASN_OCTET_STR, $clients->[$_-1]->{'FreeRADIUS-Stats-Client-Number'}); # radiusAuthClientExtID @{$snmp_data_n->{$oid.'.1.1.1.16.1.5.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServExtAccessRequests @{$snmp_data_n->{$oid.'.1.1.1.16.1.6.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServExtDupAccessRequests @{$snmp_data_n->{$oid.'.1.1.1.16.1.7.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServExtAccessAccepts @{$snmp_data_n->{$oid.'.1.1.1.16.1.8.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServExtAccessRejects @{$snmp_data_n->{$oid.'.1.1.1.16.1.9.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServExtAccessChallenges @{$snmp_data_n->{$oid.'.1.1.1.16.1.10.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServExtMalformedAccessRequests @{$snmp_data_n->{$oid.'.1.1.1.16.1.11.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServExtBadAuthenticators @{$snmp_data_n->{$oid.'.1.1.1.16.1.12.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServExtPacketsDropped @{$snmp_data_n->{$oid.'.1.1.1.16.1.13.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAuthServExtUnknownTypes @{$snmp_data_n->{$oid.'.1.1.1.16.1.14.'.$_} = &share([])} = (ASN_TIMETICKS, 0); # radiusAuthServerCounterDiscontinuity } } # # Init part of subtree for handling radius ACCT statistics # sub radius_snmp_stats_init_acct { my ( $snmp_data_n, $oid, $clients ) = @_; @{$snmp_data_n->{$oid.'.1.1.1.1'} = &share([])} = (ASN_OCTET_STR, ''); # radiusAccServIdent @{$snmp_data_n->{$oid.'.1.1.1.2'} = &share([])} = (ASN_TIMETICKS, 0); # radiusAccServUpTime @{$snmp_data_n->{$oid.'.1.1.1.3'} = &share([])} = (ASN_TIMETICKS, 0); # radiusAccServResetTime @{$snmp_data_n->{$oid.'.1.1.1.4'} = &share([])} = (ASN_INTEGER, 0); # radiusAccServConfigReset @{$snmp_data_n->{$oid.'.1.1.1.5'} = &share([])} = (ASN_COUNTER, 0); # radiusAccServTotalRequests @{$snmp_data_n->{$oid.'.1.1.1.6'} = &share([])} = (ASN_COUNTER, 0); # radiusAccServTotalInvalidRequests @{$snmp_data_n->{$oid.'.1.1.1.7'} = &share([])} = (ASN_COUNTER, 0); # radiusAccServTotalDupRequests @{$snmp_data_n->{$oid.'.1.1.1.8'} = &share([])} = (ASN_COUNTER, 0); # radiusAccServTotalResponses @{$snmp_data_n->{$oid.'.1.1.1.9'} = &share([])} = (ASN_COUNTER, 0); # radiusAccServTotalMalformedRequests @{$snmp_data_n->{$oid.'.1.1.1.10'} = &share([])} = (ASN_COUNTER, 0); # radiusAccServTotalBadAuthenticators @{$snmp_data_n->{$oid.'.1.1.1.11'} = &share([])} = (ASN_COUNTER, 0); # radiusAccServTotalPacketsDropped @{$snmp_data_n->{$oid.'.1.1.1.12'} = &share([])} = (ASN_COUNTER, 0); # radiusAccServTotalNoRecords @{$snmp_data_n->{$oid.'.1.1.1.13'} = &share([])} = (ASN_COUNTER, 0); # radiusAccServTotalUnknownTypes # # radiusAccClientExtTable # for (1 .. scalar @$clients) { my $addrtype; my $addr; if (exists $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IP-Address'}) { $addrtype = 1; $addr = $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IP-Address'}; } elsif (exists $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IPv6-Address'}) { $addrtype = 2; $addr = inet_ntop(AF_INET6, $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IPv6-Address'}); } else { next; } @{$snmp_data_n->{$oid.'.1.1.1.15.1.1.'.$_} = &share([])} = (ASN_INTEGER, $_); # radiusAccClientExtIndex @{$snmp_data_n->{$oid.'.1.1.1.15.1.2.'.$_} = &share([])} = (ASN_INTEGER, $addrtype); # radiusAccClientInetAddressType @{$snmp_data_n->{$oid.'.1.1.1.15.1.3.'.$_} = &share([])} = (ASN_OCTET_STR, $addr); # radiusAccClientInetAddress @{$snmp_data_n->{$oid.'.1.1.1.15.1.4.'.$_} = &share([])} = (ASN_OCTET_STR, $clients->[$_-1]->{'FreeRADIUS-Stats-Client-Number'}); # radiusAccClientExtID @{$snmp_data_n->{$oid.'.1.1.1.15.1.5.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAccServExtPacketsDropped @{$snmp_data_n->{$oid.'.1.1.1.15.1.6.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAccServExtRequests @{$snmp_data_n->{$oid.'.1.1.1.15.1.7.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAccServExtDupRequests @{$snmp_data_n->{$oid.'.1.1.1.15.1.8.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAccServResponses @{$snmp_data_n->{$oid.'.1.1.1.15.1.9.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAccServExtBadAuthenticators @{$snmp_data_n->{$oid.'.1.1.1.15.1.10.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAccServExtMalformedRequests @{$snmp_data_n->{$oid.'.1.1.1.15.1.11.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAccServExtNoRecords @{$snmp_data_n->{$oid.'.1.1.1.15.1.12.'.$_} = &share([])} = (ASN_COUNTER, 0); # radiusAccServExtUnknownTypes @{$snmp_data_n->{$oid.'.1.1.1.15.1.13.'.$_} = &share([])} = (ASN_TIMETICKS, 0); # radiusAccServerCounterDiscontinuity } } # # Fill part of subtree with data from radius AUTH statistics # sub radius_snmp_stats_fill_auth { my ($snmp_data_n, $oid, $prefix, $main, $clients) = @_; my $time = time; $snmp_data_n->{$oid.'.1.1.1.1'}->[1] = 'snmp(over)radius'; $snmp_data_n->{$oid.'.1.1.1.2'}->[1] = ($time - $main->{'FreeRADIUS-Stats-Start-Time'})*100; $snmp_data_n->{$oid.'.1.1.1.3'}->[1] = ($time - $main->{'FreeRADIUS-Stats-HUP-Time'})*100; $snmp_data_n->{$oid.'.1.1.1.4'}->[1] = 0; $snmp_data_n->{$oid.'.1.1.1.5'}->[1] += $main->{$prefix.'Access-Requests'}; $snmp_data_n->{$oid.'.1.1.1.6'}->[1] += $main->{$prefix.'Auth-Invalid-Requests'}; $snmp_data_n->{$oid.'.1.1.1.7'}->[1] += $main->{$prefix.'Auth-Duplicate-Requests'}; $snmp_data_n->{$oid.'.1.1.1.8'}->[1] += $main->{$prefix.'Access-Accepts'}; $snmp_data_n->{$oid.'.1.1.1.9'}->[1] += $main->{$prefix.'Access-Rejects'}; $snmp_data_n->{$oid.'.1.1.1.10'}->[1] += $main->{$prefix.'Access-Challenges'}; $snmp_data_n->{$oid.'.1.1.1.11'}->[1] += $main->{$prefix.'Auth-Malformed-Requests'}; $snmp_data_n->{$oid.'.1.1.1.12'}->[1] += 0; $snmp_data_n->{$oid.'.1.1.1.13'}->[1] += $main->{$prefix.'Auth-Dropped-Requests'}; $snmp_data_n->{$oid.'.1.1.1.14'}->[1] += $main->{$prefix.'Auth-Unknown-Types'}; for (1 .. scalar @$clients) { next unless exists $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IP-Address'} || exists $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IPv6-Address'}; $snmp_data_n->{$oid.'.1.1.1.16.1.5.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Access-Requests'} || 0; $snmp_data_n->{$oid.'.1.1.1.16.1.6.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Auth-Duplicate-Requests'} || 0; $snmp_data_n->{$oid.'.1.1.1.16.1.7.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Access-Accepts'} || 0; $snmp_data_n->{$oid.'.1.1.1.16.1.8.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Access-Rejects'} || 0; $snmp_data_n->{$oid.'.1.1.1.16.1.9.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Access-Challenges'} || 0; $snmp_data_n->{$oid.'.1.1.1.16.1.10.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Auth-Malformed-Requests'} || 0; $snmp_data_n->{$oid.'.1.1.1.16.1.11.'.$_}->[1] += 0; $snmp_data_n->{$oid.'.1.1.1.16.1.12.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Auth-Dropped-Requests'} || 0; $snmp_data_n->{$oid.'.1.1.1.16.1.13.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Auth-Unknown-Types'} || 0; $snmp_data_n->{$oid.'.1.1.1.16.1.14.'.$_}->[1] += 0; } } # # Fill part of subtree with data from radius ACCT statistics # sub radius_snmp_stats_fill_acct { my ( $snmp_data_n, $oid, $prefix, $main, $clients ) = @_; my $time = time; $snmp_data_n->{$oid.'.1.1.1.1'}->[1] = 'snmp(over)radius'; $snmp_data_n->{$oid.'.1.1.1.2'}->[1] = ($time - $main->{'FreeRADIUS-Stats-Start-Time'})*100; $snmp_data_n->{$oid.'.1.1.1.3'}->[1] = ($time - $main->{'FreeRADIUS-Stats-HUP-Time'})*100; $snmp_data_n->{$oid.'.1.1.1.4'}->[1] = 0; $snmp_data_n->{$oid.'.1.1.1.5'}->[1] += $main->{$prefix.'Accounting-Requests'}; $snmp_data_n->{$oid.'.1.1.1.6'}->[1] += $main->{$prefix.'Acct-Invalid-Requests'}; $snmp_data_n->{$oid.'.1.1.1.7'}->[1] += $main->{$prefix.'Acct-Duplicate-Requests'}; $snmp_data_n->{$oid.'.1.1.1.8'}->[1] += $main->{$prefix.'Accounting-Responses'}; $snmp_data_n->{$oid.'.1.1.1.9'}->[1] += $main->{$prefix.'Acct-Malformed-Requests'}; $snmp_data_n->{$oid.'.1.1.1.10'}->[1] += 0; $snmp_data_n->{$oid.'.1.1.1.11'}->[1] += $main->{$prefix.'Acct-Dropped-Requests'}; $snmp_data_n->{$oid.'.1.1.1.12'}->[1] += 0; $snmp_data_n->{$oid.'.1.1.1.13'}->[1] += $main->{$prefix.'Acct-Unknown-Types'}; for (1 .. scalar @$clients) { next unless exists $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IP-Address'} || exists $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IPv6-Address'}; $snmp_data_n->{$oid.'.1.1.1.15.1.5.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Acct-Dropped-Requests'} || 0; $snmp_data_n->{$oid.'.1.1.1.15.1.6.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Accounting-Requests'} || 0; $snmp_data_n->{$oid.'.1.1.1.15.1.7.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Acct-Duplicate-Requests'} || 0; $snmp_data_n->{$oid.'.1.1.1.15.1.8.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Accounting-Responses'} || 0; $snmp_data_n->{$oid.'.1.1.1.15.1.9.'.$_}->[1] += 0; $snmp_data_n->{$oid.'.1.1.1.15.1.10.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Acct-Malformed-Requests'} || 0; $snmp_data_n->{$oid.'.1.1.1.15.1.11.'.$_}->[1] += 0; $snmp_data_n->{$oid.'.1.1.1.15.1.12.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Acct-Unknown-Types'} || 0; $snmp_data_n->{$oid.'.1.1.1.15.1.13.'.$_}->[1] += 0; } } # # Update statistics # sub radius_snmp_stats { my ($main, $clients) = @_; my %snmp_data_n; # # We have to go through all oid's # foreach my $oid_s ( keys %{$cfg->{snmp}->{oid_sub}} ) { # # We're rebuilding the tree for data. We could do it only once, but it # will change when we will start handling more dynamic tree (clients) # my %types = map { $_ => 1 } map { /(?:proxy)?(\w+)/; $1 } @{$cfg->{snmp}->{oid_sub}->{$oid_s}}; WARN 'two conflicting types for oid '.$oid_s if scalar keys %types > 1; if ((keys %types)[0] eq 'auth') { radius_snmp_stats_init_auth(\%snmp_data_n, $cfg->{snmp}->{oid_root}.'.'.$oid_s, $clients); } elsif ( (keys %types)[0] eq 'acct' ) { radius_snmp_stats_init_acct(\%snmp_data_n, $cfg->{snmp}->{oid_root}.'.'.$oid_s, $clients); } else { WARN 'unknown subtree type '.(keys %types)[0]; } # # Refill the statistics # foreach my $type (@{$cfg->{snmp}->{oid_sub}->{$oid_s}}) { if ($type eq 'auth') { radius_snmp_stats_fill_auth( \%snmp_data_n, $cfg->{snmp}->{oid_root}.'.'.$oid_s, 'FreeRADIUS-Total-', $main, $clients); } elsif ($type eq 'proxyauth') { radius_snmp_stats_fill_auth( \%snmp_data_n, $cfg->{snmp}->{oid_root}.'.'.$oid_s, 'FreeRADIUS-Total-Proxy-', $main, $clients); } elsif ($type eq 'acct') { radius_snmp_stats_fill_acct( \%snmp_data_n, $cfg->{snmp}->{oid_root}.'.'.$oid_s, 'FreeRADIUS-Total-', $main, $clients); } elsif ($type eq 'proxyacct') { radius_snmp_stats_fill_acct( \%snmp_data_n, $cfg->{snmp}->{oid_root}.'.'.$oid_s, 'FreeRADIUS-Total-Proxy-', $main, $clients); } else { WARN 'unknown subtree type '.$type; } } } # # Copy the rebuilt tree to the shared variables # my @k = map { oid_s($_) } sort { $a <=> $b } map { NetSNMP::OID->new($_) } keys %snmp_data_n; my %snmp_next_n = (); $snmp_next_n{$k[$_]} = $k[$_+1] for (0 .. $#k-1); lock %snmp_data; lock @snmp_data_k; lock %snmp_next; %snmp_data = %snmp_data_n; @snmp_data_k = @k; %snmp_next = %snmp_next_n; } =head1 NAME freeradius snmp agentx subagent =head1 VERSION =head1 SYNOPSIS make sure snmpd is agentx master (snmpd.conf): master agentx run the script (no demonizing support yet): ./freeradius-snmp.pl then you can walk the tree (default oid): snmpbulkwalk -On -v2c -cpublic localhost .1.3.6.1.2.1.67 if per-client stats collection is enabled then you can do the following: snmptable -v2c -cpublic localhost .1.3.6.1.2.1.67.1.1.1.1.16 snmptable -v2c -cpublic localhost .1.3.6.1.2.1.67.2.1.1.1.15 =head1 DESCRIPTION =head1 DEPENDENCIES Net-Radius (either 1.56 + net-radius-freeradius-dictionary.diff to use freeradius dictionaries or vanilla upstream one + dictionary.hacked) NetSNMP perl modules (available with net-snmp distribution) Digest::HMAC Log::Log4perl =head1 AUTHOR Stanislaw Sawa =head1 COPYRIGHT Copyright (C) 2008 Sky Network Services. Copyright (C) 2022 Network RADIUS. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.