source: hybrid/trunk/nanobsd/files/usr/local/sbin/dhcpd-snmp@ 10131

Last change on this file since 10131 was 10131, checked in by richardvm, 13 years ago

needed?

  • Property svn:executable set to *
File size: 10.3 KB
Line 
1#! /usr/bin/perl
2# --------------------------------------------------------------------
3# Copyright (C) 2006 Oliver Hitz <oliver@net-track.ch>
4#
5# $Id: dhcpd-snmp.in,v 1.2 2006/01/25 19:26:00 oli Exp $
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful, but
13# WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15# General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place - Suite 330, Boston,
20# MA 02111-1307, USA.
21# --------------------------------------------------------------------
22# dhcpd-snmp
23#
24# An extension for polling the active and available lease counts of a
25# running dhcpd.
26#
27# Please read the man page dhcpd-snmp(8) for instructions.
28# --------------------------------------------------------------------
29
30use Time::Local;
31use strict;
32
33# The base OID of this extension. Has to match the OID in snmpd.conf:
34my $baseoid = ".1.3.6.1.4.1.21695.1.2";
35
36# Results are cached for some seconds so that an SNMP walk doesn't
37# result in dhcpd.leases being parsed multiple times.
38my $cache_secs = 60;
39
40# --------------------------------------------------------------------
41
42my $mib;
43my $mibtime;
44
45# Load configuration file
46my $conf = read_configuration($ARGV[0]);
47
48# Switch on autoflush
49$| = 1;
50
51# Main loop
52while (my $cmd = <STDIN>) {
53 chomp $cmd;
54
55 if ($cmd eq "PING") {
56 print "PONG\n";
57 } elsif ($cmd eq "get") {
58 my $oid_in = <STDIN>;
59
60 my $oid = get_oid($oid_in);
61 my $mib = create_dhcp_mib();
62
63 if ($oid != 0 && defined($mib->{$oid})) {
64 print "$baseoid.$oid\n";
65 print $mib->{$oid}[0]."\n";
66 print $mib->{$oid}[1]."\n";
67 } else {
68 print "NONE\n";
69 }
70 } elsif ($cmd eq "getnext") {
71 my $oid_in = <STDIN>;
72
73 my $oid = get_oid($oid_in);
74 my $found = 0;
75
76 my $mib = create_dhcp_mib();
77 my @s = sort { oidcmp($a, $b) } keys %{ $mib };
78 for (my $i = 0; $i < @s; $i++) {
79 if (oidcmp($oid, $s[$i]) == -1) {
80 print "$baseoid.".$s[$i]."\n";
81 print $mib->{$s[$i]}[0]."\n";
82 print $mib->{$s[$i]}[1]."\n";
83 $found = 1;
84 last;
85 }
86 }
87 if (!$found) {
88 print "NONE\n";
89 }
90 } else {
91 # Unknown command
92 }
93}
94
95exit 0;
96
97sub get_oid
98{
99
100 my ($oid) = @_;
101 chomp $oid;
102
103 my $base = $baseoid;
104 $base =~ s/\./\\./g;
105
106 if ($oid !~ /^$base(\.|$)/) {
107 # Requested oid doesn't match base oid
108 return 0;
109 }
110
111 $oid =~ s/^$base\.?//;
112 return $oid;
113}
114
115sub oidcmp {
116 my ($x, $y) = @_;
117
118 my @a = split /\./, $x;
119 my @b = split /\./, $y;
120
121 my $i = 0;
122
123 while (1) {
124
125 if ($i > $#a) {
126 if ($i > $#b) {
127 return 0;
128 } else {
129 return -1;
130 }
131 } elsif ($i > $#b) {
132 return 1;
133 }
134
135 if ($a[$i] < $b[$i]) {
136 return -1;
137 } elsif ($a[$i] > $b[$i]) {
138 return 1;
139 }
140
141 $i++;
142 }
143}
144
145sub create_dhcp_mib
146{
147 # We cache the results for $cache_secs seconds
148 if (time - $mibtime < $cache_secs) {
149 return $mib;
150 }
151
152 # Read in all leases
153 read_leases();
154
155 my %dhcp = (
156 "1" => [ "integer", 0 ], # Number of pools
157 );
158
159 foreach my $i (keys %{ $conf->{"pools"} }) {
160 $dhcp{"1"}[1]++;
161
162 my $pool = $conf->{"pools"}->{$i};
163
164 $dhcp{"2.1.".$i} = [ "integer", $i ];
165 $dhcp{"2.2.".$i} = [ "string", $pool->{"name"} ];
166 $dhcp{"2.3.".$i} = [ "integer", $pool->{"total"} ];
167 $dhcp{"2.4.".$i} = [ "integer", $pool->{"active"} ];
168 $dhcp{"2.5.".$i} = [ "integer", $pool->{"expired"} ];
169 $dhcp{"2.6.".$i} = [ "integer", $pool->{"total"} - $pool->{"active"} ];
170 }
171
172 $mib = \%dhcp;
173 $mibtime = time;
174 return $mib;
175}
176
177sub ip2int {
178 my ($ip) = @_;
179
180 if ($ip =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/) {
181 return 256*(256*(256*$1+$2)+$3)+$4;
182 } else {
183 return -1;
184 }
185}
186
187sub read_leases
188{
189 # Clear leases
190 foreach my $i (keys %{ $conf->{"pools"} }) {
191 $conf->{"pools"}->{$i}->{"leases"} = ();
192 $conf->{"pools"}->{$i}->{"active"} = 0;
193 $conf->{"pools"}->{$i}->{"expired"} = 0;
194 }
195
196 # Read leases
197 if (!open(LEASES, $conf->{"leases"})) {
198 printf STDERR "Unable to open leases file '%s'!\n", $conf->{leases};
199 return;
200 }
201
202 my %l = undef;
203
204 while (my $line = <LEASES>) {
205 if ($line =~ /^lease (\d+\.\d+\.\d+\.\d+) \{$/) {
206 my $ip = ip2int($1);
207 undef %l;
208
209 foreach my $i (keys %{ $conf->{"pools"} }) {
210 my $pool = $conf->{"pools"}->{$i};
211 my $found = 0;
212
213 foreach my $r (@{ $pool->{"ranges"} }) {
214 if (($ip >= $r->{"from"}) && ($ip <= $r->{"to"})) {
215 %l = ( "pool" => $i, "ip" => $ip );
216 $found = 1;
217 last;
218 }
219 }
220 if ($found) {
221 last;
222 }
223 }
224 } elsif (defined %l && $line =~ /^\s+ends \d (\d+)\/(\d+)\/(\d+) (\d+):(\d+):(\d+);$/) {
225 $l{"ends"} = timegm($6, $5, $4, $3, $2-1, $1);
226 } elsif (defined %l && $line =~ /^\s+ends never;$/) {
227 $l{"ends"} = -1;
228 } elsif (defined %l && $line =~ /^\}$/) {
229 $conf->{"pools"}->{$l{"pool"}}->{"leases"}->{$l{"ip"}} = $l{"ends"};
230 }
231 }
232
233 close(LEASES);
234
235 # Count active and expired leases
236 my $now = time();
237
238 foreach my $i (keys %{ $conf->{"pools"} }) {
239 my $pool = $conf->{"pools"}->{$i};
240
241 foreach my $ip (keys %{ $pool->{"leases"} }) {
242 my $end = $pool->{"leases"}->{$ip};
243 if (($end == -1) || ($end >= $now)) {
244 $pool->{"active"}++;
245 } else {
246 $pool->{"expired"}++;
247 }
248 }
249 }
250}
251
252sub read_configuration
253{
254 my ($f) = @_;
255
256 my %conf = ( "leases" => undef,
257 "pools" => { } );
258
259 open C, "$f";
260 while (my $l = <C>) {
261 $l =~ s/#.*//;
262 $l =~ s/^\s*//;
263 $l =~ s/\s*$//;
264
265 if ($l eq "") {
266 next;
267 }
268
269 if ($l =~ /^leases:\s*(\S+)$/) {
270
271 $conf{"leases"} = $1;
272
273 # Check if file is readable
274 if (open(LEASES, $conf{"leases"})) {
275 close(LEASES);
276 } else {
277 printf STDERR "Unable to open leases file '%s'!\n", $conf{"leases"};
278 }
279
280 } elsif ($l =~ /^pool:\s*(\d+)\s*,\s*("[^"]*"|[^"][^,]*)\s*,\s*(.*)$/) {
281
282 # Read the pool definition
283 my %p = ( "index" => $1,
284 "name" => $2,
285 "ranges" => [ ],
286 "total" => 0,
287 "leases" => { } );
288
289 my @ranges = split /\s*,\s*/, $3;
290
291 $p{"name"} =~ s/^\"//;
292 $p{"name"} =~ s/\"$//;
293
294 foreach my $r (@ranges) {
295 if ($r !~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})-(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) {
296 printf STDERR "Invalid range definition '%s'.\n", $r;
297 next;
298 }
299
300 my ($from, $to) = ($1, $2);
301
302 my $fromip = ip2int($from);
303 my $toip = ip2int($to);
304
305 if ($toip < $fromip) {
306 my $t = $toip;
307 $toip = $fromip;
308 $fromip = $t;
309 }
310
311 $p{"total"} += $toip-$fromip+1;
312
313 my %range = ( "from" => $fromip,
314 "to" => $toip );
315
316 push @{ $p{"ranges"} }, \%range;
317 }
318
319 $conf{"pools"}{$p{"index"}} = \%p;
320 } else {
321
322 printf STDERR "Invalid line '%s'.\n", $l;
323
324 }
325 }
326
327 return \%conf;
328}
329
330__END__
331
332=head1 NAME
333
334dhcpd-snmp
335
336=head1 SYNOPSIS
337
338dhcpd-snmp dhcpd-snmp.conf
339
340=head1 DESCRIPTION
341
342B<dhcpd-snmp> is an extension for the Net-SNMP agent and the ISC DHCP
343server. It allows you to monitor and track the address usage of your
344dynamic IP address pools through SNMP.
345
346=head1 CONFIGURATION FILE
347
348The configuration file defines the location of the F<dhcpd.leases>
349file as well as the pools of which you want to access the lease
350counts.
351
352The file is in B<key: value> format and allows only two keys:
353
354=over 8
355
356=item B<leases>: C</var/lib/dhcp3/dhcpd.leases>
357
358Location of the F<dhcpd.leases> file. This file needs to be accessible
359by the script.
360
361=item B<pool>: C<index>, C<description>, C<ip1-ip2, ip3-ip4...>
362
363Defines a pool to monitor. C<index> is a unique numeric index,
364C<description> a textual description of this pool, and C<ip1-ip2,
365ip3-ip4, ...> defines the ranges of IP addresses belonging to this
366pool.
367
368=back
369
370Since this extension is a persistent script, changes to the
371configuration file require a restart of snmpd.
372
373=head1 INSTALLATION
374
375After installing the B<dhcpd-snmp> script and adapting the
376configuration file, it is best to test it manually. This can be done
377with the following dialog:
378
379 PING
380
381The script should return "PONG".
382
383 get
384 .1.3.6.1.4.1.21695.1.2.1
385
386The script should return three lines: the OID, "integer", and the
387number of configured pools.
388
389 get
390 .1.3.6.1.4.1.21695.1.2.2.2.1
391
392OID, "string", and the name of your first address pool.
393
394 get
395 .1.3.6.1.4.1.21695.1.2.2.4.1
396
397OID, "integer", and the number of active leases.
398
399Quit the dialog using CTRL-D.
400
401If everything works, insert the following line into your Net-SNMP's
402B<snmpd.conf> configuration file:
403
404 pass_persist .1.3.6.1.4.1.21695.1.2 path/to/dhcpd-snmp path/to/dhcpd-snmp.conf
405
406Net-SNMP will need to be restarted after this change.
407
408You should now be able to get the statistics using F<snmpwalk>, for example:
409
410 $ snmpwalk host community .1.3.6.1.4.1.21695.1.2
411
412This should give you a list of the statistics of your DHCP server.
413
414=head1 MIB
415
416The script returns the following variables:
417
418 .1.3.6.1.4.1.21695.1.2.1: number of configured pools
419 .1.3.6.1.4.1.21695.1.2.2.<pool>: pool description
420 .1.3.6.1.4.1.21695.1.2.3.<pool>: size of the pool (number of addresses)
421 .1.3.6.1.4.1.21695.1.2.4.<pool>: active leases
422 .1.3.6.1.4.1.21695.1.2.5.<pool>: expired leases
423 .1.3.6.1.4.1.21695.1.2.6.<pool>: available addresses (size - active leases)
424
425For a complete MIB file see the C<mibs> directory in the source archive.
426
427=head1 SECURITY
428
429It is assumed that users of this script know how to properly secure
430their snmpd. Please read the corresponding man pages on more
431information about this.
432
433=head1 COPYRIGHT AND LICENSE
434
435Copyright (C) 2006 Oliver Hitz
436
437This program is free software; you can redistribute it and/or modify
438it under the terms of the GNU General Public License as published by
439the Free Software Foundation; either version 2 of the License, or (at
440your option) any later version.
441
442This program is distributed in the hope that it will be useful, but
443WITHOUT ANY WARRANTY; without even the implied warranty of
444MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
445General Public License for more details.
446
447You should have received a copy of the GNU General Public License
448along with this program; if not, write to the Free Software
449Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
450USA.
451
452=cut
Note: See TracBrowser for help on using the repository browser.