File Coverage

lib/NetworkInfo/Discovery/Register.pm
Criterion Covered Total %
statement 6 309 1.9
branch 0 176 0.0
condition 0 47 0.0
subroutine 2 33 6.0
pod 26 27 96.3
total 34 592 5.7


line stmt bran cond sub pod time code
1             package NetworkInfo::Discovery::Register;
2              
3 1     1   7004 use strict;
  1         1  
  1         31  
4 1     1   5 use warnings;
  1         2  
  1         4531  
5              
6             =head1 NAME
7              
8             NetworkInfo::Discovery::Register - Register of network information
9              
10             =head1 SYNOPSIS
11              
12             use NetworkInfo::Discovery::Register;
13              
14             # is like doing a $r->autosave(1) and $r->file("/tmp/the.register")
15             my $r = new NetworkInfo::Discovery::Register(autosave=>1, file=>"/tmp/the.register");
16              
17             $r->read_register(); # restore state from last save
18              
19             # ACLs allow us to remember only what we are allowed to
20             $r->clear_acl;
21             $r->add_acl("allow", "192.168.1.3/24"); # 192.168.1.3/24 gets converted to 192.168.1.0/24
22             $r->add_acl("deny", "0.0.0.0/0");
23              
24             my $interface = { ip => '192.168.1.1',
25             mac => 'aa:bb:cc:dd:ee:ff',
26             mask => '255.255.255.0', # or 24 (as in prefix notation)
27             dns => 'www.somehost.org',
28             };
29              
30             $r->add_interface($interface);
31              
32             my $subnet = { ip => '192.168.1.0', # this is the network address
33             mask => 24, # could also be '255.255.255.0'
34             };
35              
36             $r->add_subnet($subnet);
37              
38             my $gateway = { ip => '192.168.1.254',
39             mask => 24,
40             mac => 'ff:ee:dd:cc:bb:aa',
41             dns => 'router.somehost.org',
42             };
43              
44             $r->add_gateway($gateway);
45              
46             $r->write_register(); # save state for future restore
47              
48             =head1 DESCRIPTION
49              
50             C is a place to combine all that we have discovered about the network.
51             As more information gets put in, the more corrolation we should see here.
52              
53             For example, finding the netmask of an interface is not easy to do.
54             If we happen to find a subnet from some source (say RIP, or an ICMP "Address Mask Request"),
55             we may later see that those hosts with no netmask probably fit into the subnet. Once we are sure of this,
56             we can add the netmask to the interfaces, and the interfaces into the subnet.
57             By combining our knowledge in this manner, hopefully we discover more than we would by finding
58             random bits of information.
59              
60             The register stores information about interfaces, gateways, and subnets.
61             Each is a list of hashes in it's core,
62             and thus has an extensible set of attributes that we can tag onto each object.
63             With that said, the pieces of information that I am using for corrolation is as follows
64             (a * denotes that an attribute is mandatory):
65              
66             interface
67             ip # * this is the ip address of the interface
68             mac # * this is the ethernet MAC address of the interface
69             mask # the network mask in prefix or dotted-quad format
70              
71             subnet
72             ip # * an ip address on the subnet
73             mask # * the network mask in prefix or dotted-quad
74              
75             gateway
76             ip # * ip address of the interface that is this gateway
77              
78             In this module we also provide for persistance using Storable. No one likes forgetting information, right?
79              
80             =head1 METHODS
81              
82             =over 4
83              
84             =item new
85              
86             =cut
87              
88             sub new {
89 0     0 1   my $proto = shift;
90 0           my %args = @_;
91              
92 0   0       my $class = ref($proto) || $proto;
93              
94 0           my $self = {
95             'subnets' => [], # list of host indexes.
96             'gateways' => [], # list of host/subnet lists
97             'interfaces'=> [], # list of things we know about an interface
98             'events' => [], # three dogs and a biscut
99             'mac2int' => {}, # lookup table for mac to interface
100             'ip2int' => {}, # lookup table for ip to interface
101             'file' => '',
102             'autosave' => 0,
103             '_acls' => [],
104             };
105              
106 0           bless ($self, $class);
107              
108 0 0         $self->{'file'} = $args{file} if (exists $args{file});
109 0 0         $self->{'autosave'} = $args{autosave} if (exists $args{autosave});
110              
111 0 0 0       if ($self->file && -r $self->file) {
112 0           $self = $self->read_register( );
113             }
114              
115             # add a subnet to cover all hosts
116 0           $self->add_subnet({ ip=>'0.0.0.0', mask=>0 });
117 0           return $self;
118             }
119              
120             =pod
121              
122             =item add_interface ($interface_hashref)
123              
124             =cut
125              
126             sub add_interface {
127 0     0 1   my $self = shift;
128 0           my $args = &verify_args(shift);
129              
130             # we must have an ip or a mac
131 0 0 0       return 0 unless ($args->{ip} or $args->{mac});
132              
133             # make sure we pass our ACLs
134 0 0         return 0 unless ($self->test_acl($args->{ip}));
135              
136             # if the interface exists, update it
137 0 0         if (my $int = $self->has_interface($args)) {
138 0           return $self->update_interface($int, $args);
139             }
140            
141             # set the creation date
142 0           $args->{create_date} = time;
143              
144              
145             # add us to the interface list
146 0           my $index = push (@{$self->{interfaces}}, $args);
  0            
147 0           $index--;
148              
149             # $self->add_event("add_interface: at index $index ip=>" . $args->{ip} . " mac=>" . $args->{mac} );
150              
151 0 0         unless ( defined $args->{mask} ) {
152 0           $args->{mask} = $self->guess_mask($args->{ip});
153 0           $args->{mask_prob} = .5;
154             }
155             # if we have an ip and a mask, we know our subnet
156 0 0 0       if ($args->{ip} and $args->{mask}) {
157 0           my $net;
158              
159             # create the subnet if it doesn't exist
160 0 0         unless ($net = $self->has_subnet({ ip=>$args->{ip}, mask=>$args->{mask } }) ) {
161 0           $net = $self->add_subnet({ ip=>$args->{ip}, mask=>$args->{mask } });
162             }
163              
164             # add this interface to the subnet
165 0           $self->add_interface_to_subnet($index, $net);
166             } else {
167             # we don't have a mask, add us to the default subnet
168 0           $self->add_interface_to_subnet($index, 0);
169             }
170            
171            
172             # index us by ip and by mac for fast lookups
173 0 0         $self->{ip2int}->{$args->{ip}} = $index if $args->{ip};
174 0 0         $self->{mac2int}->{$args->{mac}} = $index if $args->{mac};
175              
176             # being careful about the "0th" index, return the index
177 0 0         return "0 but true" if $index == 0;
178 0           return $index;
179             }
180              
181             =pod
182              
183             =item add_interface_to_subnet ($interface_index, $subnet_index)
184              
185             =cut
186              
187             sub add_interface_to_subnet {
188 0     0 1   my $self = shift;
189             # look out for "0 but true"
190 0           my $interface = int(shift);
191 0           my $subnet = int(shift);
192              
193 0           $self->add_event("add_interface_to_subnet interface=$interface, subnet=$subnet");
194             # add this interface to the subnet
195 0           push (@{$self->{subnets}->[$subnet]->{interfaces} }, $interface);
  0            
196              
197             # add a pointer to the subnet in the interface
198 0           $self->{interfaces}->[$interface]->{subnet} = $subnet;
199              
200             }
201              
202             =pod
203              
204             =item delete_interface ($interface_hashref)
205              
206             This cuts an interface out of the interface list.
207             To keep holes from forming in the list,
208             take the last interface off the list and put it in place of the one we want to delete.
209              
210             Also, keep track of pointers to subnets and gateways,
211             from subnets and gateways,
212             and interface lookup tables.
213              
214             The special case is when we are the last interface in the list, and should just cut us out.
215              
216             =cut
217              
218             sub delete_interface {
219 0     0 1   my $self = shift;
220 0           my $args = &verify_args(shift);
221              
222 0 0 0       return 0 unless ($args->{ip} or $args->{mac});
223            
224 0 0         if ( my $interface_index = $self->has_interface($args) ){
225 0           $self->add_event("delete_interface: from index $interface_index");
226             # index to the last interface in the list
227 0           my $last_index = $#{ $self->{interfaces} };
  0            
228              
229             # pop the last interface off
230 0           my $last_interface = pop(@{$self->{interfaces}});
  0            
231              
232             # remove the indexes for the last_interface
233 0 0         delete $self->{ip2int}->{$last_interface->{ip}} if exists $last_interface->{ip};
234 0 0         delete $self->{mac2int}->{$last_interface->{mac}} if exists $last_interface->{mac};
235              
236             # remove the last interface from any subnets and gateways
237 0 0         $self->remove_interface_from_subnet($last_index, $last_interface->{subnet}) if (exists $last_interface->{subnet});
238 0 0         $self->remove_interface_from_gateway($last_index, $last_interface->{gateway}) if (exists $last_interface->{gateway});
239              
240             # we are done if we happen to be the last interface
241 0 0         return 1 if ($last_index == $interface_index) ;
242              
243              
244 0           $self->add_event("delete_interface: swapping index $interface_index for $last_index");
245             # remove our interface, replace with the last one
246 0           my $cut_interface = splice(@{$self->{interfaces}}, $interface_index, 1, $last_interface );
  0            
247              
248             # clear out the cut interface's indexs
249 0 0         delete $self->{ip2int}->{$cut_interface->{ip}} if exists $cut_interface->{ip};
250 0 0         delete $self->{mac2int}->{$cut_interface->{mac}} if exists $cut_interface->{mac};
251              
252             # remove the cut interface from any subnets and gateways
253 0 0         $self->remove_interface_from_subnet($interface_index, $cut_interface->{subnet}) if (exists $cut_interface->{subnet});
254 0 0         $self->remove_interface_from_gateway($interface_index, $cut_interface->{gateway}) if (exists $cut_interface->{gateway});
255            
256            
257             # now update indexes for the last interface
258 0 0         $self->{ip2int}->{$last_interface->{ip}} = $interface_index if $last_interface->{ip};
259 0 0         $self->{mac2int}->{$last_interface->{mac}} = $interface_index if $last_interface->{mac};
260            
261             # finally, re-add the pointers to subnets and gateways
262 0 0         $self->add_interface_to_subnet($interface_index, $last_interface->{subnet}) if (exists $last_interface->{subnet});
263 0 0         $self->add_interface_to_gateway($interface_index, $last_interface->{gateway}) if (exists $last_interface->{gateway});
264             }
265              
266 0           return 1;
267             }
268              
269             =pod
270              
271             =item add_interface_to_gateway ($interface_index, $gateway_index)
272              
273             =cut
274              
275             sub add_interface_to_gateway {
276 0     0 1   my $self = shift;
277 0           my $interface = int(shift);
278 0           my $gateway = int(shift);
279              
280 0           $self->add_event("add_interface_to_gateway interface=$interface, gateway=$gateway");
281             # add us to the gateway
282 0           push ( @{$self->{gateways}->[$gateway]->{interfaces} }, $interface);
  0            
283             }
284             =pod
285              
286             =item remove_interface_from_gateway ($interface_index, $gateway_index)
287              
288             =cut
289              
290             sub remove_interface_from_gateway {
291 0     0 1   my $self = shift;
292 0           my $interface = int(shift);
293 0           my $gateway = int(shift);
294            
295 0           $self->add_event("remove_interface_from_gateway: interface=$interface, gateway=$gateway");
296              
297             # remove us from any gateways
298 0           @{$self->{gateways}->[$gateway]->{interfaces} } = grep { $_ != $interface }
  0            
  0            
299 0           @{$self->{gateways}->[$gateway]->{interfaces} };
300             }
301              
302             =pod
303              
304             =item remove_interface_from_subnet ($interface_index, $subnet_index)
305              
306             =cut
307              
308             sub remove_interface_from_subnet {
309 0     0 1   my $self = shift;
310 0           my $interface = int(shift);
311 0           my $subnet = int(shift);
312              
313 0           $self->add_event("remove_interface_from_subnet: interface=$interface, subnet=$subnet");
314             # remove us from any subnets
315 0           @{$self->{subnets}->[$subnet]->{interfaces} } =
  0            
316 0           grep { $_ != $interface } @{$self->{subnets}->[$subnet]->{interfaces} };
  0            
317              
318             # remove the pointer to the subnet from the interface
319 0 0         delete $self->{interfaces}->[$interface]->{subnet} if exists $self->{interfaces}->[$interface];
320             }
321              
322              
323             =pod
324              
325             =item has_interface($interface_hashref)
326              
327             =cut
328              
329             sub has_interface {
330 0     0 1   my $self = shift;
331 0           my $args = &verify_args(shift);
332              
333             #no warnings;
334              
335 0 0 0       if (exists $args->{ip} and exists $self->{ip2int}->{$args->{ip}} ) {
336 0           my $i = $self->{ip2int}->{$args->{ip}};
337 0 0         return "0 but true" if $i == 0;
338 0           return $i;
339             }
340 0 0 0       if ( exists $args->{mac} and exists $self->{mac2int}->{$args->{mac}} ) {
341 0           my $i = $self->{mac2int}->{$args->{mac}};
342 0 0         return "0 but true" if $i == 0;
343 0           return $i;
344             }
345            
346 0           return 0;
347             }
348              
349             #sub has_interface {
350             # my $self = shift;
351             # my %args = &verify_args(@_);
352             #
353             # return 0 unless ($args{ip} or $args{mac});
354             #
355             # no warnings;
356             # for (my $i=0; $i < @{$self->{interfaces}}; $i++) {
357             # if ( $self->{interfaces}->[$i]->{ip} eq $args{ip}
358             # or $self->{interfaces}->[$i]->{mac} eq $args{mac} ) {
359             #
360             # return "0 but true" if $i == 0;
361             # return $i;
362             # }
363             # }
364             #
365             # return 0;
366             #}
367              
368             =pod
369              
370             =item update_interface($interface_hashref)
371              
372             =cut
373              
374             sub update_interface {
375 0     0 1   my $self=shift;
376 0           my $interface = int(shift);
377 0           my $args = &verify_args(shift);
378            
379             # this create our new interface based on the old one
380 0           my %newint = %{$self->{interfaces}->[$interface]};
  0            
381              
382             # release old indexes
383 0 0         delete $self->{ip2int}->{$newint{ip}} if $newint{ip};
384 0 0         delete $self->{mac2int}->{$newint{mac}} if $newint{mac};
385              
386             # then over write the old one with the passed args
387 0           while (my ($k, $v) = each(%$args) ) {
388 0 0         $v="" unless $v;
389 0 0         $k="" unless $k;
390 0 0         if (exists $newint{$k}) {
391 0 0 0       if ($v and $newint{$k} ne $v) {
392             # make an event here...
393             #print "changed interface $interface key $k from $newint{$k} to $v\n";
394 0           $newint{$k} = $v;
395              
396             } else {
397             #print "left interface $interface alone for $k $newint{$k} == $v\n";
398             }
399             } else {
400             #print "added key $k to interface $interface\n";
401             }
402             }
403              
404             # finish moving the last interface into place
405 0           $newint{update_date} = time;
406 0           %{$self->{interfaces}->[$interface]} = %newint;
  0            
407              
408             # set new indexes
409 0 0         $self->{ip2int}->{$newint{ip}} = $interface if $newint{ip};
410 0 0         $self->{mac2int}->{$newint{mac}} = $interface if $newint{mac};
411              
412 0 0         return "0 but true" if $interface == 0;
413 0           return $interface;
414             }
415              
416              
417             =pod
418              
419             =item add_subnet($subnet_hashref)
420              
421             =cut
422              
423             sub add_subnet {
424 0     0 1   my $self = shift;
425 0           my $args = &verify_args(shift);
426              
427 0 0 0       return 0 unless ($args->{ip} and ($args->{mask} ne ""));
428              
429             # make sure we pass our ACLs
430 0 0         return 0 unless ($self->test_acl($args->{ip}));
431              
432 0           my $index;
433             # don't add the subnet unless it doesn't exist
434 0 0         unless ($index = $self->has_subnet({ ip=>$args->{ip}, mask=>$args->{mask} } )) {
435             # find our network address
436 0           my $ip = unpack("N", pack("C4", split(/\./, $args->{ip})));
437 0           my $networknum = ($ip >> (32 - $args->{mask})) << (32 - $args->{mask});
438            
439 0           $args->{ip} = join ('.', unpack( "C4", pack( "N", $networknum ) ) );
440            
441 0           $args->{create_date} = time;
442             # print "add_subnet \n";
443             # while (my ($k,$v) = each (%$args)){ print " $k=>$v\n"; }
444 0           $index = push (@{$self->{subnets}}, $args);
  0            
445 0           $index--;
446 0           $self->add_event("added subnet " . $args->{ip} . " at index $index");
447              
448             }
449 0 0         return "0 but true" if $index == 0;
450 0           return $index;
451             }
452              
453             =pod
454              
455             =item has_subnet($subnet_hashref)
456              
457             =cut
458              
459             sub has_subnet {
460 0     0 1   my $self = shift;
461 0           my $args = &verify_args(shift);
462              
463             # print "has_subnet: just entering, ip=>" . $args->{ip} . " mask=>". $args->{mask} . "\n";
464 0 0 0       return 0 unless ($args->{ip} and $args->{mask} ne "");
465              
466             # find our network address
467 0           my $ip = unpack("N", pack("C4", split(/\./, $args->{ip})));
468 0           my $networknum = ($ip >> (32 - $args->{mask})) << (32 - $args->{mask});
469 0           $args->{ip} = join ('.', unpack( "C4", pack( "N", $networknum ) ) );
470              
471              
472 0           for (my $i=0; $i < @{$self->{subnets}}; $i++) {
  0            
473 0 0 0       if ($self->{subnets}->[$i]->{ip} eq $args->{ip}
474             and $self->{subnets}->[$i]->{mask} eq $args->{mask} ) {
475 0 0         return "0 but true" if $i == 0;
476 0           return $i;
477             }
478             }
479              
480 0           return 0;
481             }
482              
483             ##########
484             ## Gateway stuff...
485             ###################
486              
487             =pod
488              
489             =item add_gateway($gateway_hashref)
490              
491             =cut
492              
493             sub add_gateway {
494 0     0 1   my $self = shift;
495 0           my $args = &verify_args(shift);
496              
497             # must have at least an ip
498 0 0         return 0 unless ($args->{ip});
499              
500             # make sure we pass our ACLs
501 0 0         return 0 unless ($self->test_acl($args->{ip}));
502              
503 0           my $gwindex;
504 0 0         if ($gwindex = $self->has_gateway($args)) {
505             # update the gateway...
506             } else {
507 0           $gwindex = @{ $self->{gateways} };
  0            
508 0           $args->{gateway} = $gwindex;
509              
510 0           my $interfaceindex;
511 0 0         if ($interfaceindex = $self->has_interface($args)) {
512 0           $self->update_interface($interfaceindex, $args);
513             } else {
514 0           $interfaceindex = $self->add_interface($args);
515             }
516              
517 0           my $gw;
518 0           push(@{ $gw->{interfaces} }, $interfaceindex);
  0            
519 0           push(@{ $gw->{subnets} }, $self->{interfaces}->[$interfaceindex]->{subnet});
  0            
520 0           push(@{ $self->{gateways} }, $gw);
  0            
521             }
522 0 0         return "0 but true" if $gwindex == 0;
523 0           return $gwindex;
524             }
525              
526              
527             =pod
528              
529             =item has_gateway($gateway_hashref)
530              
531             =cut
532              
533             sub has_gateway {
534 0     0 1   my $self = shift;
535 0           my $args = &verify_args(shift);
536              
537 0 0         return 0 unless ($args->{ip});
538            
539 0           for (my $i=0; $i < @{$self->{gateways}}; $i++) {
  0            
540             # if one of the gatway interfaces matches our ip
541 0 0         if ( grep { $self->{interfaces}->[$_]->{ip} eq $args->{ip} } @{ $self->{gateways}->[$i]->{interfaces} } ) {
  0            
  0            
542 0 0         return "0 but true" if $i == 0;
543 0           return $i;
544             }
545             }
546 0           return 0;
547             }
548              
549             =pod
550              
551             =item verify_args($hashref)
552              
553             internal only
554              
555             =cut
556              
557             sub verify_args{
558 0     0 1   my $args = shift;
559              
560             # print "got: " . join(',',keys(%$args)) . "\n";
561 0 0 0       if (exists $args->{ip} and $args->{ip} ) {
562 0 0         return unless $args->{ip} =~ m!^\d+\.\d+\.\d+\.\d+!;
563             }
564            
565 0 0 0       if ( exists $args->{mask} and $args->{mask} ne "") {
566 0 0         return unless $args->{mask} =~ m#^(?:\d+|\d+\.\d+\.\d+\.\d+)$#;
567 0           $args->{mask} = _mask2bits($args->{mask});
568             }
569              
570 0 0 0       if (exists $args->{mac} and $args->{mac}){
571 0 0         return unless $args->{mac} =~ m!^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}!;
572             }
573            
574 0           return $args;
575             }
576              
577             =pod
578              
579             =item verify_structure
580              
581             internal only.
582              
583             =cut
584              
585             sub verify_structure {
586 0     0 1   my $self = shift;
587              
588             # make sure interfaces are logical
589 0           my $i=0;
590 0           foreach my $int ( @{ $self->{interfaces} } ) {
  0            
591 0 0         if (exists $int->{subnet}) {
592 0 0         unless (grep {$_ == $i} @{$self->{subnets}->[$int->{subnet}]->{interfaces} } ) {
  0            
  0            
593 0           warn ("interface $i has subnet " . $int->{subnet} . " but subnet " . $int->{subnet} . " only has nterfaces [ " . join(', ',@{$self->{subnets}->[$int->{subnet}]->{interfaces} } ) . " ]\n");
  0            
594             }
595             }
596 0           $i++;
597             }
598              
599             # make sure subnets are logical
600 0           $i=0;
601 0           foreach my $net ( @{ $self->{subnets} } ) {
  0            
602 0 0         if (exists $net->{interfaces}) {
603 0           foreach my $int (@{ $net->{interfaces} } ) {
  0            
604 0 0         unless ($self->{interfaces}->[$int]->{subnet} eq $i) {
605 0           warn ("subnet $i has interface $int but interface $i has subnet " . $self->{interfaces}->[$int]->{subnet} . "\n" );
606             }
607             }
608             }
609 0           $i++;
610             }
611             }
612              
613             sub _mask2bits {
614 0     0     my $mask = shift;
615              
616             # if the mask is like 255.255.255.0, make it into 24
617 0 0         if ($mask =~ m!^\d+\.\d+\.\d+\.\d+!) {
618 0           my $mask_bits=unpack("B32", pack("C4", split(/\./, $mask)));
619 0           $mask=length( (split(/0/,$mask_bits,2))[0] );
620             }
621              
622 0           return $mask;
623             }
624             sub _bits2mask {
625 0     0     my $mask = shift;
626              
627             # if the mask is like 24 make it into 255.255.255.0
628 0 0         if ($mask =~ m/^\d+$/) {
629 0           $mask = pack('B32', 1 x $mask . 0 x (32-$mask));
630              
631 0           $mask= join (".", unpack("C4", $mask) );
632             }
633              
634 0           return $mask;
635             }
636              
637             sub _ip2int {
638 0     0     my $ip = shift;
639              
640 0 0         if ($ip =~ m!^\d+\.\d+\.\d+\.\d+!) {
641 0           $ip=unpack("N", pack("C4", split(/\./, $ip)));
642             }
643              
644 0           return $ip;
645             }
646              
647              
648             =pod
649              
650             =item print_register
651              
652             prints the formated register to STDOUT
653              
654             =cut
655              
656             sub print_register {
657 0     0 1   my $self = shift;
658              
659 0           require Data::Dumper;
660 0           print Data::Dumper->Dump([$self], [qw(self)]);
661             }
662             sub dump_us {
663 0     0 0   my $self = shift;
664            
665 0           require Data::Dumper;
666 0           print Data::Dumper->Dump([$self], [qw(self)]);
667             }
668              
669            
670             =pod
671              
672             =item read_register ([$filename])
673              
674             tries to read the register from a file.
675             if $filename is not give., tries to use what was set at creation
676             of this object.
677              
678             =cut
679              
680             sub read_register {
681 0     0 1   my $self = shift;
682 0           my $file;
683            
684 0 0         if (@_) {
    0          
685 0           $file = shift;
686             } elsif ( $self->file ) {
687 0           $file = $self->file;
688             } else {
689 0           return undef;
690             }
691              
692 0           require Storable;
693              
694 0           $self = Storable::retrieve($file);
695 0           $self->{restored} = time;
696 0           $self->file($file);
697              
698 0           return $self;
699             }
700              
701             =pod
702              
703             =item write_register ([$filename])
704              
705             stores the register in $filename.
706             if $filename is not given, tries to use what was set at creation
707             of this object.
708              
709             =cut
710              
711             sub write_register {
712 0     0 1   my $self = shift;
713 0           my $file;
714              
715 0 0         if (@_) {
    0          
716 0           $file = shift;
717             } elsif ( $self->file ) {
718 0           $file = $self->file;
719             } else {
720 0           return undef;
721             }
722              
723 0           require Storable;
724 0           Storable::nstore($self, $file);
725             }
726              
727             =pod
728              
729             =item file ([ $filename ])
730              
731             get/set the file to store data in
732              
733             =cut
734              
735             sub file {
736 0     0 1   my $self = shift;
737 0 0         $self->{'file'} = shift if (@_) ;
738 0           return $self->{'file'};
739             }
740              
741             =pod
742              
743             =item autosave
744              
745             get/set auto save. pass this a "1" to turn on, a "0" to turn off.
746             Autosave means that we will try to save the register to our "file" before
747             we exit.
748              
749             =cut
750              
751             sub autosave {
752 0     0 1   my $self = shift;
753 0 0         $self->{'autosave'} = shift if (@_) ;
754 0           return $self->{'autosave'};
755             }
756              
757             =pod
758              
759             =item test_acl ($ip_to_test)
760              
761             $ip_to_test is the ip addresse you want to check against the acl list set using add_acl.
762             it should be in the form "a.b.c.d".
763             we return as soon as we find a matching rule that says allow or deny.
764             we return 1 to accept it, 0 to deny it.
765              
766             =cut
767              
768             #sub test_acl {
769             # my ($self, $ip) = @_;
770             #
771             # # this is just for kicks... lets up pass in a host obj
772             # if (ref($ip) =~ m/^NetworkInfo::Discovery::Host/) {
773             # $ip = $ip->ipaddress;
774             # }
775             # # check it against each acl and try to buffer calls to the matcher
776             # my $lastAorD = "allow";
777             # my @buffered_ips;
778             #
779             # print "checking acls against $ip\n";
780             # foreach (@{$self->{'_acls'}}) {
781             # print "____:$_\n";
782             #
783             # m!^(allow|deny):(.*)!;
784             #
785             # # if this is the same type that we saw last time,
786             # if ($lastAorD eq $1) {
787             # # save it and keep going
788             # push(@buffered_ips, $2);
789             # next;
790             # }
791             #
792             # # otherwise, this is a change so
793             # # check what we have buffered
794             # if (@buffered_ips) {
795             # #we are supposed to allow these...
796             # if ($lastAorD eq "allow") {
797             # # return 1 to if we found an allow
798             # print "calling return 1 if ($self->acl_match($ip, @buffered_ips))\n";
799             # return 1 if ($self->acl_match($ip, @buffered_ips));
800             #
801             # #we are supposed to deny these...
802             # } else {
803             # # return 0 to if we found a deny match
804             # print "calling return 0 if ($self->acl_match($ip, @buffered_ips))\n";
805             # return 0 if ($self->acl_match($ip, @buffered_ips));
806             # }
807             #
808             # # we are done with the buffer, clen it out
809             # @buffered_ips=();
810             # }
811             #
812             #
813             # # save what we have now
814             # push(@buffered_ips, $2);
815             # # don't forget where we've been
816             # $lastAorD = $1;
817             #
818             # #thanks. may i have another?
819             # }
820             #}
821              
822             sub test_acl {
823 0     0 1   my ($self, $ip) = @_;
824              
825             # print "checking acls against $ip\n";
826 0           foreach (@{$self->{'_acls'}}) {
  0            
827             # print "____:$_\n";
828 0           m!^(allow|deny):(.*)!;
829              
830             #we are supposed to allow these...
831 0 0         if ($1 eq "allow") {
832             # return 1 to if we found an allow
833             # print "calling return 1 if ($self->acl_match($ip, $2))\n";
834 0 0         return 1 if ($self->acl_match($ip, $2));
835              
836             #we are supposed to deny these...
837             } else {
838             # return 0 to if we found a deny match
839             # print "calling return 0 if ($self->acl_match($ip, $2))\n";
840 0 0         return 0 if ($self->acl_match($ip, $2));
841             }
842             }
843             #if we passed all of the above, we must not have an acl for this ip
844 0           return 1;
845             }
846              
847             =pod
848              
849             =item acl_match ($ip_to_test, @against_these)
850              
851             ip is like 172.16.20.4
852             the acls are either in CIDR notation "172.16.4.12/25" or a single address
853             returns true if the ip matches the acl.
854             returns false otherwise
855              
856             =cut
857              
858             sub acl_match {
859 0     0 1   my ($self, $ip, @others) = @_;
860              
861             # get our ip in machine representation
862 0           my $mainIP = unpack("N", pack("C4", split(/\./, $ip)));
863              
864             # for all the acls
865 0           foreach (@others) {
866             # split off the CIDR mask if there is one
867 0           m!^(\d+\.\d+\.\d+\.\d+)(?:/(\d+))?!g;
868              
869             # 0.0.0.0/0 matches all
870 0 0 0       if (($1 eq "0.0.0.0") and ($2 eq 0)) {
871 0           return 1;
872             }
873              
874             # what is left over from the mask
875 0   0       my $bits = 32 - ($2 || 32);
876              
877             # put this acl into machine representation
878 0           my $otherIP = unpack("N", pack("C4", split(/\./, $1)));
879              
880             # keep only the important parts of the ip address/mask pair
881 0           my $maskedIP = $otherIP >> $bits;
882              
883             # if there was a CIDR mask
884 0 0         if ($bits) {
885             # return true if this one matches
886             #print "bits->$bits, maskedIP->$maskedIP, mainIP->" . ($mainIP>>$bits) . "\n";
887 0 0         return 1 if ($maskedIP == ($mainIP >> $bits));
888              
889             } else {
890             # return true if this one matches (without mask)
891 0           print "bits->$bits, maskedIP->$maskedIP, mainIP->$mainIP\n";
892 0 0         return 1 if ($maskedIP == $mainIP);
893             }
894             }
895              
896             # return false if we didn't match any acl
897 0           return 0;
898             }
899              
900             =pod
901              
902             =item add_acl ("(allow|deny)", @acls)
903              
904             this function sets a list of hosts/networks that we are allowed to discover.
905             note that order matters.
906             the first argument is set to allow or deny. the meaning should be clear.
907             @acls is a list of ip addresses in the form:
908             a.b.c.d/mask # to acl a whole network
909             or
910             a.b.c.d # to acl a host
911              
912             the following calls will allow us to discover stuff on only the network 172.16.1.0/24:
913             $d->add_acl("allow", "172.16.1.0/24");
914             $d->add_acl("deny", "0.0.0.0/0");
915              
916             the following calls will allow us to discover anything but stuff on network 172.16.1.0/24:
917             $d->add_acl("deny", "172.16.1.0/24");
918             $d->add_acl("allow", "0.0.0.0/0");
919              
920             =cut
921              
922             sub add_acl {
923 0     0 1   my ($self,$AorD, @acls) = @_;
924              
925             # only accept this if we have valid allow or deny rules.
926 0 0         return undef unless ($AorD =~ m/(allow|deny)/);
927              
928 0           foreach my $a (@acls) {
929             # only accept this if we have addresses like "a.b.c.d" or "a.b.c.d/n"
930 0 0         return undef unless($a =~ m!^\d+\.\d+\.\d+\.\d+(?:/\d+)?!);
931              
932 0           push (@{$self->{"_acls"}}, "$AorD:$a");
  0            
933             }
934 0           return 1;
935             }
936              
937             =pod
938              
939             =item clear_acl
940              
941             this function clears the acl list
942              
943             =cut
944              
945             sub clear_acl {
946 0     0 1   my $self = shift;
947 0           @{$self->{"_acls"}} = ();
  0            
948             }
949              
950             =pod
951              
952             =item guess_mask ($ip)
953              
954             attempt to guess the mask based on the ip.
955             returns the guessed mask
956              
957             =cut
958              
959             sub guess_mask {
960 0     0 1   my $self = shift;
961 0           my $ip = shift;
962              
963             # see how many ones lead the ipaddress
964 0           my $bits = _mask2bits($ip);
965 0           my $mask = 0;
966            
967 0 0         if ($bits eq 0) {
    0          
    0          
968             # class a address
969 0           $mask = "255.0.0.0";
970             } elsif ( $bits eq 1 ) {
971             # class B address
972 0           $mask = "255.255.0.0";
973             } elsif ( $bits eq 2 ) {
974             # class C address
975 0           $mask = "255.255.255.0";
976             }
977 0           return $mask;
978             }
979             =pod
980              
981             =item add_event ("string")
982              
983             add an event to the log
984              
985             =cut
986              
987             sub add_event {
988 0     0 1   my $self=shift;
989              
990 0           my $msg = time . " " . join(",",@_);
991            
992 0           push(@{$self->{events}}, $msg);
  0            
993             }
994              
995             =pod
996              
997             =item DESTROY
998              
999             just tries to write_register if we have autosave turned on
1000              
1001             =cut
1002              
1003             sub DESTROY {
1004 0     0     my $self=shift;
1005 0 0         $self->write_register() if ($self->autosave);
1006             }
1007              
1008             =pod
1009              
1010             =back
1011              
1012             =cut
1013              
1014             1;