From: Alan T. DeKok Date: Sun, 20 Jul 2008 08:16:21 +0000 (+0200) Subject: New SNMP code! X-Git-Tag: release_2_1_0~123 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=freeradius.git;a=commitdiff_plain;h=fcf48f4b8730ce37d11d81c676ab3a6f8b135e8c New SNMP code! --- diff --git a/scripts/snmp-proxy/README b/scripts/snmp-proxy/README new file mode 100644 index 0000000..d9741e7 --- /dev/null +++ b/scripts/snmp-proxy/README @@ -0,0 +1,32 @@ + The files in this directory replace the old FreeRADIUS SNMP +implementantion with a new one. + +net-radius-freeradius-dictionary.diff + Patch to enable the Perl Net::RADIUS module to read the + FreeRADIUS dictionary file format. + +dictionary.hacked + Dictionary used by Perl Net::RADIUS if it is NOT patched. + Do NOT use this dictionary with the FreeRADIUS server! + +freeradius-snmp.pl + Perl module that implements the connection between SNMP + and FreeRADIUS. + + See raddb/sites-available/status for information on using + Status-Server packets to obtain internal server statistics. + +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 + +This code is ALPHA. Please test, and return any fixes back to the +mailing list, or to bugs.freeradius.org. diff --git a/scripts/snmp-proxy/dictionary.hacked b/scripts/snmp-proxy/dictionary.hacked new file mode 100644 index 0000000..66b2159 --- /dev/null +++ b/scripts/snmp-proxy/dictionary.hacked @@ -0,0 +1,132 @@ +# +# This is a dictionary that should be used by the Perl module Net::RADIUS, +# if it has NOT been updated to parse the FreeRADIUS format dictionaries. +# +# It SHOULD NOT be used in the FreeRADIUS server or client! +# +ATTRIBUTE Message-Authenticator 80 octets + + +VENDOR FreeRADIUS 11344 + +ATTRIBUTE FreeRADIUS-Proxied-To 1 ipaddr + + +# +# This attribute is really a bitmask. +# +ATTRIBUTE FreeRADIUS-Statistics-Type 127 integer FreeRADIUS + +VALUE FreeRADIUS-Statistics-Type None 0 FreeRADIUS +VALUE FreeRADIUS-Statistics-Type Authentication 1 +VALUE FreeRADIUS-Statistics-Type Accounting 2 +VALUE FreeRADIUS-Statistics-Type Proxy-Authentication 4 +VALUE FreeRADIUS-Statistics-Type Proxy-Accounting 8 +VALUE FreeRADIUS-Statistics-Type Internal 0x10 +VALUE FreeRADIUS-Statistics-Type Client 0x20 +VALUE FreeRADIUS-Statistics-Type Server 0x40 +VALUE FreeRADIUS-Statistics-Type Home-Server 0x80 + +VALUE FreeRADIUS-Statistics-Type Auth-Acct 0x03 +VALUE FreeRADIUS-Statistics-Type Proxy-Auth-Acct 0x0c + +VALUE FreeRADIUS-Statistics-Type All 0x1f + +# +# Global authentication statistics for packets received by the server. +# +ATTRIBUTE FreeRADIUS-Total-Access-Requests 128 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Access-Accepts 129 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Access-Rejects 130 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Access-Challenges 131 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Auth-Responses 132 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Auth-Duplicate-Requests 133 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Auth-Malformed-Requests 134 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Auth-Invalid-Requests 135 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Auth-Dropped-Requests 136 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Auth-Unknown-Types 137 integer FreeRADIUS + +# +# Global statistics for auth packets sent by the server to all home servers +# +ATTRIBUTE FreeRADIUS-Total-Proxy-Access-Requests 138 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Access-Accepts 139 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Access-Rejects 140 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Access-Challenges 141 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Auth-Responses 142 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Auth-Duplicate-Requests 143 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Auth-Malformed-Requests 144 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Auth-Invalid-Requests 145 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Auth-Dropped-Requests 146 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Auth-Unknown-Types 147 integer FreeRADIUS + +# +# Global accounting statistics for packets received by the server. +# +ATTRIBUTE FreeRADIUS-Total-Accounting-Requests 148 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Accounting-Responses 149 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Acct-Duplicate-Requests 150 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Acct-Malformed-Requests 151 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Acct-Invalid-Requests 152 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Acct-Dropped-Requests 153 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Acct-Unknown-Types 154 integer FreeRADIUS + +# +# Global statistics for acct packets sent by the server to all home servers +# +ATTRIBUTE FreeRADIUS-Total-Proxy-Accounting-Requests 155 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Accounting-Responses 156 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Acct-Duplicate-Requests 157 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Acct-Malformed-Requests 158 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Acct-Invalid-Requests 159 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Acct-Dropped-Requests 160 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Total-Proxy-Acct-Unknown-Types 161 integer FreeRADIUS + +# +# Internal queues. Different packet types are put into different queues. +# +ATTRIBUTE FreeRADIUS-Queue-Len-Internal 162 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Queue-Len-Proxy 163 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Queue-Len-Auth 164 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Queue-Len-Acct 165 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Queue-Len-Detail 166 integer FreeRADIUS + +ATTRIBUTE FreeRADIUS-Stats-Client-IP-Address 167 ipaddr FreeRADIUS +ATTRIBUTE FreeRADIUS-Stats-Client-Number 168 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Stats-Client-Netmask 169 integer FreeRADIUS + +ATTRIBUTE FreeRADIUS-Stats-Server-IP-Address 170 ipaddr FreeRADIUS +ATTRIBUTE FreeRADIUS-Stats-Server-Port 171 integer FreeRADIUS + +ATTRIBUTE FreeRADIUS-Stats-Server-Outstanding-Requests 172 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Stats-Server-State 173 integer FreeRADIUS + +VALUE FreeRADIUS-Stats-Server-State Alive 0 +VALUE FreeRADIUS-Stats-Server-State Zombie 1 +VALUE FreeRADIUS-Stats-Server-State Dead 2 + +# +# When a home server is marked "dead" or "alive" +# +ATTRIBUTE FreeRADIUS-Stats-Server-Time-Of-Death 174 date FreeRADIUS +ATTRIBUTE FreeRADIUS-Stats-Server-Time-Of-Life 175 date FreeRADIUS + +# +# When this server was started. If start == hup, it hasn't been +# hup'd yet. This is friendlier than having hup == 0 on start. +# +ATTRIBUTE FreeRADIUS-Stats-Start-Time 176 date FreeRADIUS +ATTRIBUTE FreeRADIUS-Stats-HUP-Time 177 date FreeRADIUS + +# +# Exponential moving average of home server response time +# Window-1 is the average is calculated over "window" packets. +# Window-10 is the average is calculated over "10 * window" packets. +# +# Both Window-1 and Window-10 are times in microseconds +# (1/1000000 of a second). +# +ATTRIBUTE FreeRADIUS-Server-EMA-Window 178 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Server-EMA-USEC-Window-1 179 integer FreeRADIUS +ATTRIBUTE FreeRADIUS-Server-EMA-USEC-Window-10 180 integer FreeRADIUS + diff --git a/scripts/snmp-proxy/freeradius-snmp.pl b/scripts/snmp-proxy/freeradius-snmp.pl new file mode 100644 index 0000000..b650151 --- /dev/null +++ b/scripts/snmp-proxy/freeradius-snmp.pl @@ -0,0 +1,464 @@ +#!/usr/bin/perl +# +# Copyright (C) 2008 Sky Network Services. All Rights Reserved. +# +# 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 IO::Socket::INET; +use Digest::HMAC_MD5; +use Log::Log4perl qw/:easy/; +#use Data::Dumper; +#$Data::Dumper::Indent = 1; +#$Data::Dumper::Sortkeys = 1; +#$| = 1; + +# config. should be really loaded from some file +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 => $DEBUG, + layout => '%d{ISO8601} <%p> (%L) %m%n', + file => 'STDERR' + }, + + clients => undef, +}; +Log::Log4perl->easy_init($cfg->{log}); + +INFO 'starting'; +my %snmp_data :shared; +my @snmp_data_k :shared; + + +INFO 'initializing snmp'; +my $agent = new NetSNMP::agent(%{$cfg->{snmp}->{agent}}); + +#lets create the thread as early as possible (but it has to be AFTER initializing snmp) +INFO 'launching radius client thread'; +threads->create(\&radius_updater); + +#we export only subtrees, not the whole tree +$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->main_loop; + +WARN 'something caused me to exit'; +exit 0; + + +# 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 { + radius_stats_init(); + + while (1) { + 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'}; + push @clients_stat, $client_stat; + $client_id += 1; + } + } + + #todo ng: update on demand, and update only parts of snmp visible stats + + INFO 'got data, updating stats'; + radius_snmp_stats($main_stat, \@clients_stat); + + }else { + WARN 'problem with fetching data'; + + } + + INFO 'stats updated, sleeping'; + sleep $cfg->{radius}->{refresh_rate}; + } + +} + +#helper to get string from NetSNMP::OID +sub oid_s { return join '.', $_[0]->to_array; } + +#handler for snmp requests from master agent(clients) +sub snmp_handler { + DEBUG 'got new request'; + my ($handler, $registration_info, $request_info, $requests) = @_; + + lock %snmp_data; + lock @snmp_data_k; + + 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 ) { + foreach my $oid ( @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) ) { + $request->setValue($snmp_data{$oid}->[0], ''.$snmp_data{$oid}->[1]); + $request->setOID($oid); + last; + } + } + + }else { + #no write support + $request->setError($request_info, SNMP_ERR_READONLY); + + } + } + 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 + + #radiusAuthClientTable + for (1 .. scalar @$clients) { + @{$snmp_data_n->{$oid.'.1.1.1.15.1.1.'.$_} = &share([])} = (ASN_INTEGER, $_); #radiusAuthClientIndex + @{$snmp_data_n->{$oid.'.1.1.1.15.1.2.'.$_} = &share([])} = (ASN_IPADDRESS, pack 'C4', split /\./, $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IP-Address'}); #radiusAuthClientAddress + @{$snmp_data_n->{$oid.'.1.1.1.15.1.3.'.$_} = &share([])} = (ASN_OCTET_STR, $clients->[$_-1]->{'FreeRADIUS-Stats-Client-Number'}); #radiusAuthClientID +# @{$snmp_data_n->{$oid.'.1.1.1.15.1.4.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServAccessRequests +# @{$snmp_data_n->{$oid.'.1.1.1.15.1.5.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServDupAccessRequests +# @{$snmp_data_n->{$oid.'.1.1.1.15.1.6.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServAccessAccepts +# @{$snmp_data_n->{$oid.'.1.1.1.15.1.7.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServAccessRejects +# @{$snmp_data_n->{$oid.'.1.1.1.15.1.8.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServAccessChallenges +# @{$snmp_data_n->{$oid.'.1.1.1.15.1.9.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServMalformedAccessRequests +# @{$snmp_data_n->{$oid.'.1.1.1.15.1.10.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServBadAuthenticators +# @{$snmp_data_n->{$oid.'.1.1.1.15.1.11.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServPacketsDropped +# @{$snmp_data_n->{$oid.'.1.1.1.15.1.12.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServUnknownTypes + } +} + +#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 + + #radiusAccClientTable + for (1 .. scalar @$clients) { + @{$snmp_data_n->{$oid.'.1.1.1.14.1.1.'.$_} = &share([])} = (ASN_INTEGER, $_); #radiusAccClientIndex + @{$snmp_data_n->{$oid.'.1.1.1.14.1.2.'.$_} = &share([])} = (ASN_IPADDRESS, pack 'C4', split /\./, $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IP-Address'}); #radiusAccClientAddress + @{$snmp_data_n->{$oid.'.1.1.1.14.1.3.'.$_} = &share([])} = (ASN_OCTET_STR, $clients->[$_-1]->{'FreeRADIUS-Stats-Client-Number'}); #radiusAccClientID +# @{$snmp_data_n->{$oid.'.1.1.1.14.1.4.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServPacketsDropped +# @{$snmp_data_n->{$oid.'.1.1.1.14.1.5.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServRequests +# @{$snmp_data_n->{$oid.'.1.1.1.14.1.6.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServDupRequests +# @{$snmp_data_n->{$oid.'.1.1.1.14.1.7.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServResponses +# @{$snmp_data_n->{$oid.'.1.1.1.14.1.8.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServBadAuthenticators +# @{$snmp_data_n->{$oid.'.1.1.1.14.1.9.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServMalformedRequests +# @{$snmp_data_n->{$oid.'.1.1.1.14.1.10.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServNoRecords +# @{$snmp_data_n->{$oid.'.1.1.1.14.1.11.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServUnknownTypes + } +} + +#fill part of subtree with data from radius auth statistics +sub radius_snmp_stats_fill_auth { + my ( $snmp_data_n, $oid, $prefix, $main, $clients ) = @_; + #hmm .. proxy? + + 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) { +# $snmp_data_n->{$oid.'.1.1.1.15.1.4.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Access-Requests'}; +# $snmp_data_n->{$oid.'.1.1.1.15.1.5.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Auth-Duplicate-Requests'}; +# $snmp_data_n->{$oid.'.1.1.1.15.1.6.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Access-Accepts'}; +# $snmp_data_n->{$oid.'.1.1.1.15.1.7.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Access-Rejects'}; +# $snmp_data_n->{$oid.'.1.1.1.15.1.8.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Access-Challenges'}; +# $snmp_data_n->{$oid.'.1.1.1.15.1.9.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Auth-Malformed-Requests'}; +# $snmp_data_n->{$oid.'.1.1.1.15.1.10.'.$_}->[1] += 0; +# $snmp_data_n->{$oid.'.1.1.1.15.1.11.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Auth-Dropped-Requests'}; +# $snmp_data_n->{$oid.'.1.1.1.15.1.12.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Auth-Unknown-Types'}; + } +} + +#fill part of subtree with data from radius acct statistics +sub radius_snmp_stats_fill_acct { + my ( $snmp_data_n, $oid, $prefix, $main, $clients ) = @_; + #hmm .. proxy? + + 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) { +# $snmp_data_n->{$oid.'.1.1.1.14.1.4.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServPacketsDropped'; +# $snmp_data_n->{$oid.'.1.1.1.14.1.5.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServRequests'; +# $snmp_data_n->{$oid.'.1.1.1.14.1.6.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServDupRequests'; +# $snmp_data_n->{$oid.'.1.1.1.14.1.7.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServResponses'; +# $snmp_data_n->{$oid.'.1.1.1.14.1.8.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServBadAuthenticators'; +# $snmp_data_n->{$oid.'.1.1.1.14.1.9.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServMalformedRequests'; +# $snmp_data_n->{$oid.'.1.1.1.14.1.10.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServNoRecords'; +# $snmp_data_n->{$oid.'.1.1.1.14.1.11.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServUnknownTypes'; + } +} + +#update statistics +sub radius_snmp_stats { + my ( $main, $clients ) = @_; + +#print Dumper($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]; + + } + + #now lets 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; + } + + } + } + + #we rebuild the tree, so lets now lock the shared variables and push new data there + lock %snmp_data; + lock @snmp_data_k; + + %snmp_data = %snmp_data_n; + @snmp_data_k = map { oid_s($_) } sort { $a <=> $b } map { NetSNMP::OID->new($_) } keys %snmp_data_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 + +=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. All Rights Reserved. + +This program is free software; you can redistribute it and/or modify it +under the same terms as Perl itself. diff --git a/scripts/snmp-proxy/net-radius-freeradius-dictionary.diff b/scripts/snmp-proxy/net-radius-freeradius-dictionary.diff new file mode 100644 index 0000000..f379cf4 --- /dev/null +++ b/scripts/snmp-proxy/net-radius-freeradius-dictionary.diff @@ -0,0 +1,81 @@ +--- Net-Radius-1.56.orig/Radius/Dictionary.pm 2008-06-20 14:08:57.000000000 +0100 ++++ Net-Radius-1.56.1/Radius/Dictionary.pm 2008-06-20 15:54:33.000000000 +0100 +@@ -30,14 +30,23 @@ + + sub readfile { + my ($self, $filename) = @_; ++ my $dict; + +- open DICT, "<$filename"; ++ open $dict, "<$filename"; + +- while (defined(my $l = )) { ++ my @in_vendor = (); ++ ++ while (defined(my $l = <$dict>)) { + next if $l =~ /^\#/; + next unless my @l = split /\s+/, $l; + +- if ($l[0] =~ m/^vendor$/i) ++ if ($l[0] =~ m/^\$include$/i) ++ { ++ my @fn = split /\//, $filename; ++ $fn[$#fn] = $l[1]; ++ $self->readfile(join '/', @fn); ++ } ++ elsif ($l[0] =~ m/^vendor$/i) + { + if (defined $l[1] and defined $l[2] and $l[2] =~ /^[xo0-9]+$/) + { +@@ -53,8 +62,42 @@ + warn "Garbled VENDOR line $l\n"; + } + } ++ elsif ($l[0] =~ m/^begin-vendor$/i) ++ { ++ if ( defined $l[1] ) ++ { ++ push @in_vendor, $l[1]; ++ } ++ else ++ { ++ warn "Garbled BEGIN-VENDOR line $l\n"; ++ } ++ } ++ elsif ($l[0] =~ m/^end-vendor$/i) ++ { ++ if ( defined $l[1] ) ++ { ++ if ( $in_vendor[$#in_vendor] eq $l[1] ) { ++ pop @in_vendor; ++ }else { ++ warn "mismatched END-VENDOR line $l\n"; ++ } ++ } ++ else ++ { ++ warn "Garbled END-VENDOR line $l\n"; ++ } ++ } + elsif ($l[0] =~ m/^attribute$/i) + { ++ if (@l == 5) { ++ my @tags = grep { not ( m/^encrypt=\d$/ or m/^has_tag$/ ) } split /,/, pop @l; ++ push @l, join ',', @tags if scalar @tags; ++ } ++ if (@l == 4 and scalar @in_vendor) { ++ push @l, $in_vendor[$#in_vendor]; ++ } ++ + if (@l == 4) + { + $self->{attr}->{$l[1]} = [@l[2,3]]; +@@ -166,7 +209,7 @@ + warn "Warning: Weird dictionary line: $l\n"; + } + } +- close DICT; ++ close $dict; + } + + # Accessors for standard attributes