diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:49:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:49:46 +0000 |
commit | 50b37d4a27d3295a29afca2286f1a5a086142cec (patch) | |
tree | 9212f763934ee090ef72d823f559f52ce387f268 /scripts/snmp-proxy/freeradius-snmp.pl | |
parent | Initial commit. (diff) | |
download | freeradius-50b37d4a27d3295a29afca2286f1a5a086142cec.tar.xz freeradius-50b37d4a27d3295a29afca2286f1a5a086142cec.zip |
Adding upstream version 3.2.1+dfsg.upstream/3.2.1+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | scripts/snmp-proxy/freeradius-snmp.pl | 585 |
1 files changed, 585 insertions, 0 deletions
diff --git a/scripts/snmp-proxy/freeradius-snmp.pl b/scripts/snmp-proxy/freeradius-snmp.pl new file mode 100644 index 0000000..f30fc7d --- /dev/null +++ b/scripts/snmp-proxy/freeradius-snmp.pl @@ -0,0 +1,585 @@ +#!/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 <stanislaw.sawa(at)sns.bskyb.com> + +=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. |