Fixes to build without PTHREADs
[freeradius.git] / scripts / snmp-proxy / freeradius-snmp.pl
1 #!/usr/bin/perl
2 #
3 # Copyright (C) 2008 Sky Network Services. All Rights Reserved.
4
5 # This program is free software; you can redistribute it and/or modify it
6 # under the same terms as Perl itself.
7 #
8 use strict;
9 use warnings;
10
11 use threads;
12 use threads::shared;
13
14 use Net::Radius::Packet;
15 use Net::Radius::Dictionary;
16 use NetSNMP::agent qw/:all/;
17 use NetSNMP::ASN qw/:all/;
18 use IO::Socket::INET;
19 use Digest::HMAC_MD5;
20 use Log::Log4perl qw/:easy/;
21 #use Data::Dumper;
22 #$Data::Dumper::Indent = 1;
23 #$Data::Dumper::Sortkeys = 1;
24 #$| = 1;
25
26 # config. should be really loaded from some file
27 my $cfg = {
28     snmp => {
29         agent => {
30             Name => 'freeradius-snmp',
31             AgentX => 1,
32         },
33         oid_root => '1.3.6.1.2.1.67',
34         oid_sub => {
35             1 => [qw/auth proxyauth/],
36             2 => [qw/acct proxyacct/],
37         },
38     },
39
40     radius => {
41         host => 'localhost',
42         port => 18120,
43         secret => 'adminsecret',
44 #        dictionary => '../radiusd/share/dictionary',
45         dictionary => 'dictionary.hacked',
46         refresh_rate => 20,
47     },
48
49
50     log => {
51         level => $DEBUG,
52         layout => '%d{ISO8601} <%p> (%L) %m%n',
53         file => 'STDERR'
54     },
55
56     clients => undef,
57 };
58 Log::Log4perl->easy_init($cfg->{log});
59
60 INFO 'starting';
61 my %snmp_data :shared;
62 my @snmp_data_k :shared;
63
64
65 INFO 'initializing snmp';
66 my $agent = new NetSNMP::agent(%{$cfg->{snmp}->{agent}});
67
68 #lets create the thread as early as possible (but it has to be AFTER initializing snmp)
69 INFO 'launching radius client thread';
70 threads->create(\&radius_updater);
71
72 #we export only subtrees, not the whole tree
73 $agent->register(
74     $cfg->{snmp}->{agent}->{Name},
75     $cfg->{snmp}->{oid_root}.'.'.$_, \&snmp_handler) or die
76   foreach keys %{$cfg->{snmp}->{oid_sub}};
77
78 INFO 'entering client main loop';
79 $agent->main_loop;
80
81 WARN 'something caused me to exit';
82 exit 0;
83
84
85 # initialize common radius client stuff
86 sub radius_stats_init {
87     our ( $d, $s, $rid );
88
89     $d = new Net::Radius::Dictionary;
90     $d->readfile($cfg->{radius}->{dictionary});
91     srand ($$ ^ time);
92     $rid = int rand 255;
93
94     $s = new IO::Socket::INET(
95         PeerHost => $cfg->{radius}->{host},
96         PeerPort => $cfg->{radius}->{port},
97         Proto => 'udp',
98         Timeout => 5) or die;
99
100 }
101
102 # build server status packet, send it, fetch and parse the result
103 sub radius_stats_get {
104     my ( $type, %args ) = @_;
105
106     our ( $d, $s, $rid );
107
108     my $p_req = new Net::Radius::Packet $d;
109     $p_req->set_code('Status-Server');
110     $p_req->set_vsattr('FreeRADIUS', 'FreeRADIUS-Statistics-Type', $type);
111     $p_req->set_vsattr('FreeRADIUS', $_, $args{$_}) foreach keys %args;
112
113     #update id
114     $p_req->set_identifier($rid++);
115     $p_req->set_authenticator(pack 'C*', map { int rand 255 } 0..15);
116
117     #recalc authenticator
118     $p_req->set_attr('Message-Authenticator', "\0"x16, 1);
119     $p_req->set_attr('Message-Authenticator', Digest::HMAC_MD5::hmac_md5($p_req->pack, $cfg->{radius}->{secret}), 1);
120
121     #send brand new and shiny request
122     $s->send($p_req->pack) or die;
123
124     my $p_data;
125     if ( defined $s->recv($p_data, 2048) ) {
126         my $p_res = new Net::Radius::Packet $d, $p_data;
127
128         my %response =  map {
129             $_ => $p_res->vsattr($d->vendor_num('FreeRADIUS'), $_)->[0]
130         } $p_res->vsattributes($d->vendor_num('FreeRADIUS'));
131         return \%response;
132
133     }else {
134         warn "no answer, $!\n";
135         return undef;
136     }
137
138 }
139
140 #wrappers for specific types of stats
141 sub radius_stats_get_global { return radius_stats_get(0x1f); }
142 sub radius_stats_get_client { return radius_stats_get(0x3f, 'FreeRADIUS-Stats-Client-Number' => $_[0]); }
143
144
145 #main loop of thread fetching status from freeradius server
146 sub radius_updater {
147     radius_stats_init();
148
149     while (1) {
150         INFO 'fetching new data';
151         my $main_stat = radius_stats_get_global();
152
153         if ( defined $main_stat ) {
154             my @clients_stat = ();
155
156             if ( $cfg->{clients} ) {
157                 my $client_id = 0;
158
159                 while (1) {
160                     my $client_stat = radius_stats_get_client($client_id);
161                     last unless exists $client_stat->{'FreeRADIUS-Stats-Client-IP-Address'};
162                     push @clients_stat, $client_stat;
163                     $client_id += 1;
164                 }
165             }
166
167             #todo ng: update on demand, and update only parts of snmp visible stats
168
169             INFO 'got data, updating stats';
170             radius_snmp_stats($main_stat, \@clients_stat);
171
172         }else {
173             WARN 'problem with fetching data';
174
175         }
176
177         INFO 'stats updated, sleeping';
178         sleep $cfg->{radius}->{refresh_rate};
179     }
180
181 }
182
183 #helper to get string from NetSNMP::OID
184 sub oid_s { return join '.', $_[0]->to_array; }
185
186 #handler for snmp requests from master agent(clients)
187 sub snmp_handler {
188     DEBUG 'got new request';
189     my ($handler, $registration_info, $request_info, $requests) = @_;
190
191     lock %snmp_data;
192     lock @snmp_data_k;
193
194     for ( my $request = $requests; $request; $request = $request->next() ) {
195         INFO 'request type '.$request_info->getMode.' for oid: '.oid_s($request->getOID);
196
197         if ( $request_info->getMode == MODE_GET ) {
198             my $oid_s = oid_s($request->getOID);
199             if ( exists $snmp_data{$oid_s} ) {
200                 $request->setValue($snmp_data{$oid_s}->[0], ''.$snmp_data{$oid_s}->[1]);
201             }
202
203         }elsif ( $request_info->getMode == MODE_GETNEXT ) {
204             foreach my $oid ( @snmp_data_k ) {
205                 #the keys is sorted in ascending order, so we are looking for
206                 #first value bigger than one in request
207                 if ( $request->getOID < NetSNMP::OID->new($oid) ) {
208                     $request->setValue($snmp_data{$oid}->[0], ''.$snmp_data{$oid}->[1]);
209                     $request->setOID($oid);
210                     last;
211                 }
212             }
213
214         }else {
215             #no write support
216             $request->setError($request_info, SNMP_ERR_READONLY);
217
218         }
219     }
220     DEBUG 'finished processing the request';
221 }
222
223 #init part of subtree for handling radius auth statistics
224 sub radius_snmp_stats_init_auth {
225     my ( $snmp_data_n, $oid, $clients ) = @_;
226
227     @{$snmp_data_n->{$oid.'.1.1.1.1'} = &share([])} = (ASN_OCTET_STR, ''); #radiusAuthServIdent
228     @{$snmp_data_n->{$oid.'.1.1.1.2'} = &share([])} = (ASN_TIMETICKS, 0); #radiusAuthServUpTime
229     @{$snmp_data_n->{$oid.'.1.1.1.3'} = &share([])} = (ASN_TIMETICKS, 0); #radiusAuthServResetTime
230     @{$snmp_data_n->{$oid.'.1.1.1.4'} = &share([])} = (ASN_INTEGER, 0); #radiusAuthServConfigReset
231     @{$snmp_data_n->{$oid.'.1.1.1.5'} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServTotalAccessRequests
232     @{$snmp_data_n->{$oid.'.1.1.1.6'} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServTotalInvalidRequests
233     @{$snmp_data_n->{$oid.'.1.1.1.7'} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServTotalDupAccessRequests
234     @{$snmp_data_n->{$oid.'.1.1.1.8'} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServTotalAccessAccepts
235     @{$snmp_data_n->{$oid.'.1.1.1.9'} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServTotalAccessRejects
236     @{$snmp_data_n->{$oid.'.1.1.1.10'} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServTotalAccessChallenges
237     @{$snmp_data_n->{$oid.'.1.1.1.11'} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServTotalMalformedAccessRequests
238     @{$snmp_data_n->{$oid.'.1.1.1.12'} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServTotalBadAuthenticators
239     @{$snmp_data_n->{$oid.'.1.1.1.13'} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServTotalPacketsDropped
240     @{$snmp_data_n->{$oid.'.1.1.1.14'} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServTotalUnknownTypes
241
242     #radiusAuthClientTable
243     for (1 .. scalar @$clients) {
244         @{$snmp_data_n->{$oid.'.1.1.1.15.1.1.'.$_} = &share([])} = (ASN_INTEGER, $_); #radiusAuthClientIndex
245         @{$snmp_data_n->{$oid.'.1.1.1.15.1.2.'.$_} = &share([])} = (ASN_IPADDRESS, pack 'C4', split /\./, $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IP-Address'}); #radiusAuthClientAddress
246         @{$snmp_data_n->{$oid.'.1.1.1.15.1.3.'.$_} = &share([])} = (ASN_OCTET_STR, $clients->[$_-1]->{'FreeRADIUS-Stats-Client-Number'}); #radiusAuthClientID
247 #        @{$snmp_data_n->{$oid.'.1.1.1.15.1.4.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServAccessRequests
248 #        @{$snmp_data_n->{$oid.'.1.1.1.15.1.5.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServDupAccessRequests
249 #        @{$snmp_data_n->{$oid.'.1.1.1.15.1.6.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServAccessAccepts
250 #        @{$snmp_data_n->{$oid.'.1.1.1.15.1.7.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServAccessRejects
251 #        @{$snmp_data_n->{$oid.'.1.1.1.15.1.8.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServAccessChallenges
252 #        @{$snmp_data_n->{$oid.'.1.1.1.15.1.9.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServMalformedAccessRequests
253 #        @{$snmp_data_n->{$oid.'.1.1.1.15.1.10.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServBadAuthenticators
254 #        @{$snmp_data_n->{$oid.'.1.1.1.15.1.11.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServPacketsDropped
255 #        @{$snmp_data_n->{$oid.'.1.1.1.15.1.12.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAuthServUnknownTypes
256     }
257 }
258
259 #init part of subtree for handling radius acct statistics
260 sub radius_snmp_stats_init_acct {
261     my ( $snmp_data_n, $oid, $clients ) = @_;
262
263     @{$snmp_data_n->{$oid.'.1.1.1.1'} = &share([])} = (ASN_OCTET_STR, ''); #radiusAccServIdent
264     @{$snmp_data_n->{$oid.'.1.1.1.2'} = &share([])} = (ASN_TIMETICKS, 0); #radiusAccServUpTime
265     @{$snmp_data_n->{$oid.'.1.1.1.3'} = &share([])} = (ASN_TIMETICKS, 0); #radiusAccServResetTime
266     @{$snmp_data_n->{$oid.'.1.1.1.4'} = &share([])} = (ASN_INTEGER, 0); #radiusAccServConfigReset
267     @{$snmp_data_n->{$oid.'.1.1.1.5'} = &share([])} = (ASN_COUNTER, 0); #radiusAccServTotalRequests
268     @{$snmp_data_n->{$oid.'.1.1.1.6'} = &share([])} = (ASN_COUNTER, 0); #radiusAccServTotalInvalidRequests
269     @{$snmp_data_n->{$oid.'.1.1.1.7'} = &share([])} = (ASN_COUNTER, 0); #radiusAccServTotalDupRequests
270     @{$snmp_data_n->{$oid.'.1.1.1.8'} = &share([])} = (ASN_COUNTER, 0); #radiusAccServTotalResponses
271     @{$snmp_data_n->{$oid.'.1.1.1.9'} = &share([])} = (ASN_COUNTER, 0); #radiusAccServTotalMalformedRequests
272     @{$snmp_data_n->{$oid.'.1.1.1.10'} = &share([])} = (ASN_COUNTER, 0); #radiusAccServTotalBadAuthenticators
273     @{$snmp_data_n->{$oid.'.1.1.1.11'} = &share([])} = (ASN_COUNTER, 0); #radiusAccServTotalPacketsDropped
274     @{$snmp_data_n->{$oid.'.1.1.1.12'} = &share([])} = (ASN_COUNTER, 0); #radiusAccServTotalNoRecords
275     @{$snmp_data_n->{$oid.'.1.1.1.13'} = &share([])} = (ASN_COUNTER, 0); #radiusAccServTotalUnknownTypes
276
277     #radiusAccClientTable
278     for (1 .. scalar @$clients) {
279         @{$snmp_data_n->{$oid.'.1.1.1.14.1.1.'.$_} = &share([])} = (ASN_INTEGER, $_); #radiusAccClientIndex
280         @{$snmp_data_n->{$oid.'.1.1.1.14.1.2.'.$_} = &share([])} = (ASN_IPADDRESS, pack 'C4', split /\./, $clients->[$_-1]->{'FreeRADIUS-Stats-Client-IP-Address'}); #radiusAccClientAddress
281         @{$snmp_data_n->{$oid.'.1.1.1.14.1.3.'.$_} = &share([])} = (ASN_OCTET_STR, $clients->[$_-1]->{'FreeRADIUS-Stats-Client-Number'}); #radiusAccClientID
282 #        @{$snmp_data_n->{$oid.'.1.1.1.14.1.4.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServPacketsDropped
283 #        @{$snmp_data_n->{$oid.'.1.1.1.14.1.5.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServRequests
284 #        @{$snmp_data_n->{$oid.'.1.1.1.14.1.6.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServDupRequests
285 #        @{$snmp_data_n->{$oid.'.1.1.1.14.1.7.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServResponses
286 #        @{$snmp_data_n->{$oid.'.1.1.1.14.1.8.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServBadAuthenticators
287 #        @{$snmp_data_n->{$oid.'.1.1.1.14.1.9.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServMalformedRequests
288 #        @{$snmp_data_n->{$oid.'.1.1.1.14.1.10.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServNoRecords
289 #        @{$snmp_data_n->{$oid.'.1.1.1.14.1.11.'.$_} = &share([])} = (ASN_COUNTER, 0); #radiusAccServUnknownTypes
290     }
291 }
292
293 #fill part of subtree with data from radius auth statistics
294 sub radius_snmp_stats_fill_auth {
295     my ( $snmp_data_n, $oid, $prefix, $main, $clients ) = @_;
296     #hmm .. proxy?
297
298     my $time = time;
299
300     $snmp_data_n->{$oid.'.1.1.1.1'}->[1] = 'snmp(over)radius';
301     $snmp_data_n->{$oid.'.1.1.1.2'}->[1] = ($time - $main->{'FreeRADIUS-Stats-Start-Time'})*100;
302     $snmp_data_n->{$oid.'.1.1.1.3'}->[1] = ($time - $main->{'FreeRADIUS-Stats-HUP-Time'})*100;
303     $snmp_data_n->{$oid.'.1.1.1.4'}->[1] = 0;
304     $snmp_data_n->{$oid.'.1.1.1.5'}->[1] += $main->{$prefix.'Access-Requests'};
305     $snmp_data_n->{$oid.'.1.1.1.6'}->[1] += $main->{$prefix.'Auth-Invalid-Requests'};
306     $snmp_data_n->{$oid.'.1.1.1.7'}->[1] += $main->{$prefix.'Auth-Duplicate-Requests'};
307     $snmp_data_n->{$oid.'.1.1.1.8'}->[1] += $main->{$prefix.'Access-Accepts'};
308     $snmp_data_n->{$oid.'.1.1.1.9'}->[1] += $main->{$prefix.'Access-Rejects'};
309     $snmp_data_n->{$oid.'.1.1.1.10'}->[1] += $main->{$prefix.'Access-Challenges'};
310     $snmp_data_n->{$oid.'.1.1.1.11'}->[1] += $main->{$prefix.'Auth-Malformed-Requests'};
311     $snmp_data_n->{$oid.'.1.1.1.12'}->[1] += 0;
312     $snmp_data_n->{$oid.'.1.1.1.13'}->[1] += $main->{$prefix.'Auth-Dropped-Requests'};
313     $snmp_data_n->{$oid.'.1.1.1.14'}->[1] += $main->{$prefix.'Auth-Unknown-Types'};
314
315     for (1 .. scalar @$clients) {
316 #         $snmp_data_n->{$oid.'.1.1.1.15.1.4.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Access-Requests'};
317 #         $snmp_data_n->{$oid.'.1.1.1.15.1.5.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Auth-Duplicate-Requests'};
318 #         $snmp_data_n->{$oid.'.1.1.1.15.1.6.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Access-Accepts'};
319 #         $snmp_data_n->{$oid.'.1.1.1.15.1.7.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Access-Rejects'};
320 #         $snmp_data_n->{$oid.'.1.1.1.15.1.8.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Access-Challenges'};
321 #         $snmp_data_n->{$oid.'.1.1.1.15.1.9.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Auth-Malformed-Requests'};
322 #         $snmp_data_n->{$oid.'.1.1.1.15.1.10.'.$_}->[1] += 0;
323 #         $snmp_data_n->{$oid.'.1.1.1.15.1.11.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Auth-Dropped-Requests'};
324 #         $snmp_data_n->{$oid.'.1.1.1.15.1.12.'.$_}->[1] += $clients->[$_-1]->{$prefix.'Auth-Unknown-Types'};
325     }
326 }
327
328 #fill part of subtree with data from radius acct statistics
329 sub radius_snmp_stats_fill_acct {
330     my ( $snmp_data_n, $oid, $prefix, $main, $clients ) = @_;
331     #hmm .. proxy?
332
333     my $time = time;
334
335     $snmp_data_n->{$oid.'.1.1.1.1'}->[1] = 'snmp(over)radius';
336     $snmp_data_n->{$oid.'.1.1.1.2'}->[1] = ($time - $main->{'FreeRADIUS-Stats-Start-Time'})*100;
337     $snmp_data_n->{$oid.'.1.1.1.3'}->[1] = ($time - $main->{'FreeRADIUS-Stats-HUP-Time'})*100;
338     $snmp_data_n->{$oid.'.1.1.1.4'}->[1] = 0;
339     $snmp_data_n->{$oid.'.1.1.1.5'}->[1] += $main->{$prefix.'Accounting-Requests'};
340     $snmp_data_n->{$oid.'.1.1.1.6'}->[1] += $main->{$prefix.'Acct-Invalid-Requests'};
341     $snmp_data_n->{$oid.'.1.1.1.7'}->[1] += $main->{$prefix.'Acct-Duplicate-Requests'};
342     $snmp_data_n->{$oid.'.1.1.1.8'}->[1] += $main->{$prefix.'Accounting-Responses'};
343     $snmp_data_n->{$oid.'.1.1.1.9'}->[1] += $main->{$prefix.'Acct-Malformed-Requests'};
344     $snmp_data_n->{$oid.'.1.1.1.10'}->[1] += 0;
345     $snmp_data_n->{$oid.'.1.1.1.11'}->[1] += $main->{$prefix.'Acct-Dropped-Requests'};
346     $snmp_data_n->{$oid.'.1.1.1.12'}->[1] += 0;
347     $snmp_data_n->{$oid.'.1.1.1.13'}->[1] += $main->{$prefix.'Acct-Unknown-Types'};
348
349     for (1 .. scalar @$clients) {
350 #         $snmp_data_n->{$oid.'.1.1.1.14.1.4.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServPacketsDropped';
351 #         $snmp_data_n->{$oid.'.1.1.1.14.1.5.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServRequests';
352 #         $snmp_data_n->{$oid.'.1.1.1.14.1.6.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServDupRequests';
353 #         $snmp_data_n->{$oid.'.1.1.1.14.1.7.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServResponses';
354 #         $snmp_data_n->{$oid.'.1.1.1.14.1.8.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServBadAuthenticators';
355 #         $snmp_data_n->{$oid.'.1.1.1.14.1.9.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServMalformedRequests';
356 #         $snmp_data_n->{$oid.'.1.1.1.14.1.10.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServNoRecords';
357 #         $snmp_data_n->{$oid.'.1.1.1.14.1.11.'.$_}->[1] += $clients->[$_-1]->{$prefix.''};# 'radiusAccServUnknownTypes';
358     }
359 }
360
361 #update statistics
362 sub radius_snmp_stats {
363     my ( $main, $clients ) = @_;
364
365 #print Dumper($main, $clients);
366
367     my %snmp_data_n;
368
369     # we have to go through all oid's
370     foreach my $oid_s ( keys %{$cfg->{snmp}->{oid_sub}} ) {
371
372         #we're rebuilding the tree for data
373         #we could do it only once, but it will change when we will start handling more dynamic
374         #tree (clients)
375         my %types = map { $_ => 1 } map { /(?:proxy)?(\w+)/; $1 } @{$cfg->{snmp}->{oid_sub}->{$oid_s}};
376         WARN 'two conflicting types for oid '.$oid_s  if scalar keys %types > 1;
377
378         if ( (keys %types)[0] eq 'auth' ) {
379             radius_snmp_stats_init_auth(\%snmp_data_n, $cfg->{snmp}->{oid_root}.'.'.$oid_s, $clients);
380
381         }elsif ( (keys %types)[0] eq 'acct' ) {
382             radius_snmp_stats_init_acct(\%snmp_data_n, $cfg->{snmp}->{oid_root}.'.'.$oid_s, $clients);
383
384         }else {
385             WARN 'unknown subtree type '.(keys %types)[0];
386
387         }
388
389         #now lets refill the statistics
390         foreach my $type ( @{$cfg->{snmp}->{oid_sub}->{$oid_s}} ) {     
391             if ( $type eq 'auth' ) {
392                 radius_snmp_stats_fill_auth(
393                     \%snmp_data_n, $cfg->{snmp}->{oid_root}.'.'.$oid_s,
394                     'FreeRADIUS-Total-', $main, $clients);
395
396             }elsif ( $type eq 'proxyauth' ) {
397                 radius_snmp_stats_fill_auth(
398                     \%snmp_data_n, $cfg->{snmp}->{oid_root}.'.'.$oid_s,
399                     'FreeRADIUS-Total-Proxy-', $main, $clients);
400
401             }elsif ( $type eq 'acct' ) {
402                 radius_snmp_stats_fill_acct(
403                     \%snmp_data_n, $cfg->{snmp}->{oid_root}.'.'.$oid_s,
404                     'FreeRADIUS-Total-', $main, $clients);
405
406             }elsif ( $type eq 'proxyacct' ) {
407                 radius_snmp_stats_fill_acct(
408                     \%snmp_data_n, $cfg->{snmp}->{oid_root}.'.'.$oid_s,
409                     'FreeRADIUS-Total-Proxy-', $main, $clients);
410
411             }else {
412                 WARN 'unknown subtree type '.$type;
413             }
414             
415         }
416     }
417
418     #we rebuild the tree, so lets now lock the shared variables and push new data there
419     lock %snmp_data;
420     lock @snmp_data_k;
421
422     %snmp_data = %snmp_data_n;
423     @snmp_data_k = map { oid_s($_) } sort { $a <=> $b } map { NetSNMP::OID->new($_) } keys %snmp_data_n;
424 }
425
426 =head1 NAME
427
428 freeradius snmp agentx subagent
429
430 =head1 VERSION
431
432 =head1 SYNOPSIS
433
434 make sure snmpd is agentx master (snmpd.conf):
435 master agentx
436
437 run the script (no demonizing support yet):
438
439 ./freeradius-snmp.pl
440
441 then you can walk the tree (default oid):
442
443 snmpbulkwalk -On -v2c -cpublic localhost .1.3.6.1.2.1.67
444
445 =head1 DESCRIPTION
446
447 =head1 DEPENDENCIES
448
449 Net-Radius (either 1.56 + net-radius-freeradius-dictionary.diff to use freeradius dictionaries
450   or vanilla upstream one + dictionary.hacked)
451 NetSNMP perl modules (available with net-snmp distribution)
452 Digest::HMAC
453 Log::Log4perl
454
455 =head1 AUTHOR
456
457 Stanislaw Sawa <stanislaw.sawa(at)sns.bskyb.com>
458
459 =head1 COPYRIGHT
460
461 Copyright (C) 2008 Sky Network Services. All Rights Reserved.
462
463 This program is free software; you can redistribute it and/or modify it
464 under the same terms as Perl itself.