1 package ArpHandler;
3 use Exporter;
4 @ISA = ("Exporter");
6 use strict;
7 use warnings;
8 use GOSA::GosaSupportDaemon;
9 use Getopt::Long;
10 use Config::IniFiles;
11 use POSIX;
12 use Fcntl;
13 use Net::LDAP;
14 use Net::LDAP::LDIF;
15 use Net::LDAP::Entry;
16 use Net::DNS;
17 use Switch;
18 use Data::Dumper;
19 use POE qw(Component::Pcap Component::ArpWatch);
21 BEGIN{}
22 END{}
24 my ($verbose, $cfg_file, $log_file, $pid_file, $foreground);
25 my ($timeout, $mailto, $mailfrom, $user, $group);
26 my ($procid, $pid, $loglevel);
27 my ($fifo_path, $max_process_timeout, $max_process );
28 my %daemon_children;
29 my ($ldap, $bind_phrase, $password, $ldap_base) ;
30 my $hosts_database={};
31 my $resolver=Net::DNS::Resolver->new;
33 $procid = -1 ;
34 $foreground = 0 ;
35 $verbose = 0 ;
36 $max_process = 2 ;
37 $max_process_timeout = 1 ;
38 $ldap_base = "dc=gonicus,dc=de" ;
39 #$ldap_path = "/var/run/gosa-support-daemon.socket";
40 #$log_path = "/var/log/gosa-support-daemon.log";
41 #$pid_path = "/var/run/gosa-support-daemon/gosa-support-daemon.pid";
43 #---------------------------------------------------------------------------
44 # parse commandline options
45 #---------------------------------------------------------------------------
46 #Getopt::Long::Configure( "bundling" );
47 #GetOptions( "v|verbose+" => \$verbose,
48 # "c|config=s" => \$cfg_file,
49 # "h|help" => \&usage,
50 # "l|logfile=s" => \$log_file,
51 # "p|pid=s" => \$pid_file,
52 # "f|foreground" => \$foreground);
53 #
54 #---------------------------------------------------------------------------
55 # read and set config parameters
56 #---------------------------------------------------------------------------
57 #my %cfg_defaults =
58 #("Allgemein" =>
59 # {"timeout" => [ \$timeout, 1000 ],
60 # "mailto" => [ \$mailto, 'root@localhost' ],
61 # "mailfrom" => [ \$mailfrom, 'sps-daemon@localhost' ],
62 # "user" => [ \$user, "nobody" ],
63 # "group" => [ \$group, "nogroup" ],
64 # "fifo_path" => [ \$fifo_path, "/home/rettenbe/gonicus/gosa-support/tmp/fifo" ],
65 # "log_file" => [ \$log_file, "/home/rettenbe/gonicus/gosa-support/tmp/gosa-support.log" ],
66 # "pid_file" => [ \$pid_file, "/home/rettenbe/gonicus/gosa-support/tmp/gosa-support.pid" ],
67 # "loglevel" => [ \$loglevel, 1]
68 # },
69 #"LDAP" =>
70 # {"bind" => [ \$bind_phrase, "cn=ldapadmin,dc=gonicus,dc=de" ],
71 # "password" => [ \$password, "tester" ],
72 # }
73 # );
74 #&read_configfile;
76 sub get_module_info {
77 my @info = (undef,
78 undef,
79 undef,
80 undef,
81 "socket",
82 );
84 my $device = 'eth0';
85 $ldap = Net::LDAP->new("ldap.intranet.gonicus.de") or die "$@";
87 POE::Session->create(
88 inline_states => {
89 _start => \&start,
90 _stop => sub {
91 $_[KERNEL]->post( arp_watch => 'shutdown' )
92 },
93 got_packet => \&got_packet,
94 },
95 );
97 return \@info;
98 }
100 sub process_incoming_msg {
101 return 0;
102 }
104 sub start {
105 POE::Component::ArpWatch->spawn(
106 Alias => 'arp_watch',
107 Device => 'eth0',
108 Dispatch => 'got_packet',
109 Session => $_[SESSION],
110 );
112 $_[KERNEL]->post( arp_watch => 'run' );
113 }
115 sub got_packet {
116 my $packet = $_[ARG0];
118 if($packet->{source_haddr} eq "00:00:00:00:00:00" ||
119 $packet->{source_haddr} eq "ff:ff:ff:ff:ff:ff" ||
120 $packet->{source_ipaddr} eq "0.0.0.0") {
121 return;
122 }
124 if(!exists($hosts_database->{$packet->{source_haddr}})) {
125 my $ldap_result=&get_host_from_ldap($packet->{source_haddr});
126 if(exists($ldap_result->{dn})) {
127 $hosts_database->{$packet->{source_haddr}}=$ldap_result;
128 $hosts_database->{$packet->{source_haddr}}->{dnsname}=($resolver->search($packet->{source_ipaddr}))->{answer}[0]->{ptrdname};
129 print STDERR "Host was found in LDAP as ".$ldap_result->{dn}."\n";
130 } else {
131 $hosts_database->{$packet->{source_haddr}}={
132 macAddress => $packet->{source_haddr},
133 ipHostNumber => $packet->{source_ipaddr},
134 dnsname => ($resolver->search($packet->{source_ipaddr}))->{answer}[0]->{ptrdname},
135 };
136 print STDERR "Host was not found in LDAP (".($hosts_database->{$packet->{source_haddr}}->{dnsname}).")\n";
137 }
138 } else {
139 print STDERR "Host already in cache (".($hosts_database->{$packet->{source_haddr}}->{dnsname}).")\n";
140 }
141 }
143 sub get_host_from_ldap {
144 my $mac=shift;
145 my $result={};
147 my $ldap_result= search_ldap_entry(
148 $ldap,
149 $ldap_base,
150 "(|(macAddress=$mac)(dhcpHWAddress=ethernet $mac))"
151 );
153 if($ldap_result->count==1) {
154 if(exists($ldap_result->{entries}[0]) &&
155 exists($ldap_result->{entries}[0]->{asn}->{objectName}) &&
156 exists($ldap_result->{entries}[0]->{asn}->{attributes})) {
158 for my $attribute(@{$ldap_result->{entries}[0]->{asn}->{attributes}}) {
159 if($attribute->{type} eq 'cn') {
160 $result->{cn} = $attribute->{vals}[0];
161 }
162 if($attribute->{type} eq 'macAddress') {
163 $result->{macAddress} = $attribute->{vals}[0];
164 }
165 if($attribute->{type} eq 'dhcpHWAddress') {
166 $result->{dhcpHWAddress} = $attribute->{vals}[0];
167 }
168 if($attribute->{type} eq 'ipHostNumber') {
169 $result->{ipHostNumber} = $attribute->{vals}[0];
170 }
171 }
172 }
173 $result->{dn} = $ldap_result->{entries}[0]->{asn}->{objectName};
174 }
176 return $result;
177 }
180 #=== FUNCTION ================================================================
181 # NAME: add_ldap_entry
182 # PURPOSE: adds an element to ldap-tree
183 # PARAMETERS:
184 # RETURNS: none
185 # DESCRIPTION: ????
186 # THROWS: no exceptions
187 # COMMENTS: none
188 # SEE ALSO: n/a
189 #===============================================================================
190 #sub add_ldap_entry {
191 # my ($ldap_tree, $ldap_base, $mac, $gotoSysStatus, $ip, $interface, $desc) = @_;
192 # my $dn = "cn=$mac,ou=incoming,$ldap_base";
193 # my $s_res = &search_ldap_entry($ldap_tree, $ldap_base, "(|(macAddress=$mac)(dhcpHWAddress=ethernet $mac))");
194 # my $c_res = $s_res->count;
195 # if($c_res == 1) {
196 # daemon_log("WARNING: macAddress $mac already in LDAP", 1);
197 # return;
198 # } elsif($c_res > 0) {
199 # daemon_log("ERROR: macAddress $mac exists $c_res times in LDAP", 1);
200 # return;
201 # }
202 #
203 # # create LDAP entry
204 # my $entry = Net::LDAP::Entry->new( $dn );
205 # $entry->dn($dn);
206 # $entry->add("objectClass" => "goHard");
207 # $entry->add("cn" => $mac);
208 # $entry->add("macAddress" => $mac);
209 # if(defined $gotoSysStatus) {$entry->add("gotoSysStatus" => $gotoSysStatus)}
210 # if(defined $ip) {$entry->add("ipHostNumber" => $ip) }
211 # #if(defined $interface) { }
212 # if(defined $desc) {$entry->add("description" => $desc) }
213 #
214 # # submit entry to LDAP
215 # my $result = $entry->update ($ldap_tree);
216 #
217 # # for $result->code constants please look at Net::LDAP::Constant
218 # my $log_time = localtime( time );
219 # if($result->code == 68) { # entry already exists
220 # daemon_log("WARNING: $log_time: $dn ".$result->error, 3);
221 # } elsif($result->code == 0) { # everything went fine
222 # daemon_log("$log_time: add entry $dn to ldap", 1);
223 # } else { # if any other error occur
224 # daemon_log("ERROR: $log_time: $dn, ".$result->code.", ".$result->error, 1);
225 # }
226 # return;
227 #}
230 #=== FUNCTION ================================================================
231 # NAME: change_ldap_entry
232 # PURPOSE: ????
233 # PARAMETERS: ????
234 # RETURNS: ????
235 # DESCRIPTION: ????
236 # THROWS: no exceptions
237 # COMMENTS: none
238 # SEE ALSO: n/a
239 #===============================================================================
240 #sub change_ldap_entry {
241 # my ($ldap_tree, $ldap_base, $mac, $gotoSysStatus ) = @_;
242 #
243 # # check if ldap_entry exists or not
244 # my $s_res = &search_ldap_entry($ldap_tree, $ldap_base, "(|(macAddress=$mac)(dhcpHWAddress=ethernet $mac))");
245 # my $c_res = $s_res->count;
246 # if($c_res == 0) {
247 # daemon_log("WARNING: macAddress $mac not in LDAP", 1);
248 # return;
249 # } elsif($c_res > 1) {
250 # daemon_log("ERROR: macAddress $mac exists $c_res times in LDAP", 1);
251 # return;
252 # }
253 #
254 # my $s_res_entry = $s_res->pop_entry();
255 # my $dn = $s_res_entry->dn();
256 # my $result = $ldap->modify( $dn, replace => {'gotoSysStatus' => $gotoSysStatus } );
257 #
258 # # for $result->code constants please look at Net::LDAP::Constant
259 # my $log_time = localtime( time );
260 # if($result->code == 32) { # entry doesnt exists
261 # &add_ldap_entry($mac, $gotoSysStatus);
262 # } elsif($result->code == 0) { # everything went fine
263 # daemon_log("$log_time: entry $dn changed successful", 1);
264 # } else { # if any other error occur
265 # daemon_log("ERROR: $log_time: $dn, ".$result->code.", ".$result->error, 1);
266 # }
267 #
268 # return;
269 #}
271 #=== FUNCTION ================================================================
272 # NAME: search_ldap_entry
273 # PURPOSE: ????
274 # PARAMETERS: [Net::LDAP] $ldap_tree - object of an ldap-tree
275 # string $sub_tree - dn of the subtree the search is performed
276 # string $search_string - either a string or a Net::LDAP::Filter object
277 # RETURNS: [Net::LDAP::Search] $msg - result object of the performed search
278 # DESCRIPTION: ????
279 # THROWS: no exceptions
280 # COMMENTS: none
281 # SEE ALSO: n/a
282 #===============================================================================
283 sub search_ldap_entry {
284 my ($ldap_tree, $sub_tree, $search_string) = @_;
285 my $msg = $ldap_tree->search( # perform a search
286 base => $sub_tree,
287 filter => $search_string,
288 ) or daemon_log("cannot perform search at ldap: $@", 1);
289 # if(defined $msg) {
290 # print $sub_tree."\t".$search_string."\t";
291 # print $msg->count."\n";
292 # foreach my $entry ($msg->entries) { $entry->dump; };
293 # }
295 return $msg;
296 }
300 #========= MAIN = main ========================================================
301 #daemon_log( "####### START DAEMON ######\n", 1 );
302 #&check_cmdline_param ;
303 #&check_pid;
304 #&open_fifo($fifo_path);
305 #
306 ## Just fork, if we"re not in foreground mode
307 #if( ! $foreground ) { $pid = fork(); }
308 #else { $pid = $$; }
309 #
310 ## Do something useful - put our PID into the pid_file
311 #if( 0 != $pid ) {
312 # open( LOCK_FILE, ">$pid_file" );
313 # print LOCK_FILE "$pid\n";
314 # close( LOCK_FILE );
315 # if( !$foreground ) { exit( 0 ) };
316 #}
317 #
318 #
319 #if( not -p $fifo_path ) { die "fifo file disappeared\n" }
320 #if($c_res == 1) {
321 # daemon_log("WARNING: macAddress $mac already in LDAP", 1);
322 # return;
323 # } elsif($c_res > 0) {
324 # daemon_log("ERROR: macAddress $mac exists $c_res times in LDAP", 1);
325 # return;
326 # }
327 #
328 # # create LDAP entry
329 # my $entry = Net::LDAP::Entry->new( $dn );
330 # $entry->dn($dn);
331 # $entry->add("objectClass" => "goHard");
332 # $entry->add("cn" => $mac);
333 # $entry->add("macAddress" => $mac);
334 # if(defined $gotoSysStatus) {$entry->add("gotoSysStatus" => $gotoSysStatus)}
335 # if(defined $ip) {$entry->add("ipHostNumber" => $ip) }
336 # #if(defined $interface) { }
337 # if(defined $desc) {$entry->add("description" => $desc) }
338 #
339 # # submit entry to LDAP
340 # my $result = $entry->update ($ldap_tree);
341 #
342 # # for $result->code constants please look at Net::LDAP::Constant
343 # my $log_time = localtime( time );
344 # if($result->code == 68) { # entry already exists
345 # daemon_log("WARNING: $log_time: $dn ".$result->error, 3);
346 # } elsif($result->code == 0) { # everything went fine
347 # daemon_log("$log_time: add entry $dn to ldap", 1);
348 # } else { # if any other error occur
349 # daemon_log("ERROR: $log_time: $dn, ".$result->code.", ".$result->error, 1);
350 # }
351 # return;
352 #}
355 #=== FUNCTION ================================================================
356 # NAME: change_ldap_entry
357 # PURPOSE: ????
358 # PARAMETERS: ????
359 # RETURNS: ????
360 # DESCRIPTION: ????
361 # THROWS: no exceptions
362 # COMMENTS: none
363 # SEE ALSO: n/a
364 #===============================================================================
365 #sub change_ldap_entry {
366 # my ($ldap_tree, $ldap_base, $mac, $gotoSysStatus ) = @_;
367 #
368 # # check if ldap_entry exists or not
369 # my $s_res = &search_ldap_entry($ldap_tree, $ldap_base, "(|(macAddress=$mac)(dhcpHWAddress=ethernet $mac))");
370 # my $c_res = $s_res->count;
371 # if($c_res == 0) {
372 # daemon_log("WARNING: macAddress $mac not in LDAP", 1);
373 # return;
374 # } elsif($c_res > 1) {
375 # daemon_log("ERROR: macAddress $mac exists $c_res times in LDAP", 1);
376 # return;
377 # }
378 #
379 # my $s_res_entry = $s_res->pop_entry();
380 # my $dn = $s_res_entry->dn();
381 # my $result = $ldap->modify( $dn, replace => {'gotoSysStatus' => $gotoSysStatus } );
382 #
383 # # for $result->code constants please look at Net::LDAP::Constant
384 # my $log_time = localtime( time );
385 # if($result->code == 32) { # entry doesnt exists
386 # &add_ldap_entry($mac, $gotoSysStatus);
387 # } elsif($result->code == 0) { # everything went fine
388 # daemon_log("$log_time: entry $dn changed successful", 1);
389 # } else { # if any other error occur
390 # daemon_log("ERROR: $log_time: $dn, ".$result->code.", ".$result->error, 1);
391 # }
392 #
393 # return;
394 #}
396 #=== FUNCTION ================================================================
397 # NAME: search_ldap_entry
398 # PURPOSE: ????
399 # PARAMETERS: [Net::LDAP] $ldap_tree - object of an ldap-tree
400 # string $sub_tree - dn of the subtree the search is performed
401 # string $search_string - either a string or a Net::LDAP::Filter object
402 # RETURNS: [Net::LDAP::Search] $msg - result object of the performed search
403 # DESCRIPTION: ????
404 # THROWS: no exceptions
405 # COMMENTS: none
406 # SEE ALSO: n/a
407 #===============================================================================
408 #sub search_ldap_entry {
409 # my ($ldap_tree, $sub_tree, $search_string) = @_;
410 # my $msg = $ldap_tree->search( # perform a search
411 # base => $sub_tree,
412 # filter => $search_string,
413 # ) or daemon_log("cannot perform search at ldap: $@", 1);
414 ## if(defined $msg) {
415 ## print $sub_tree."\t".$search_string."\t";
416 ## print $msg->count."\n";
417 ## foreach my $entry ($msg->entries) { $entry->dump; };
418 ## }
419 #
420 # return $msg;
421 #}
425 #========= MAIN = main ========================================================
426 #daemon_log( "####### START DAEMON ######\n", 1 );
427 #&check_cmdline_param ;
428 #&check_pid;
429 #&open_fifo($fifo_path);
430 #
431 ## Just fork, if we"re not in foreground mode
432 #if( ! $foreground ) { $pid = fork(); }
433 #else { $pid = $$; }
434 #
435 ## Do something useful - put our PID into the pid_file
436 #if( 0 != $pid ) {
437 # open( LOCK_FILE, ">$pid_file" );
438 # print LOCK_FILE "$pid\n";
439 # close( LOCK_FILE );
440 # if( !$foreground ) { exit( 0 ) };
441 #}
442 #
443 #
444 #if( not -p $fifo_path ) { die "fifo file disappeared\n" }
445 #sysopen(FIFO, $fifo_path, O_RDONLY) or die "can't read from $fifo_path: $!" ;
446 #
447 #while( 1 ) {
448 # # checke alle prozesse im hash daemon_children ob sie noch aktiv sind, wenn
449 # # nicht, dann entferne prozess aus hash
450 # while( (my $key, my $val) = each( %daemon_children) ) {
451 # my $status = waitpid( $key, &WNOHANG) ;
452 # if( $status == -1 ) {
453 # delete $daemon_children{$key} ;
454 # daemon_log("childprocess finished: $key", 3) ;
455 # }
456 # }
457 #
458 # # ist die max_process anzahl von prozesskindern erreicht, dann warte und
459 # # prüfe erneut, ob in der zwischenzeit prozesse fertig geworden sind
460 # if( keys( %daemon_children ) >= $max_process ) {
461 # sleep($max_process_timeout) ;
462 # next ;
463 # }
464 #
465 # my $msg = <FIFO>;
466 # if( not defined( $msg )) { next ; }
467 #
468 # chomp( $msg );
469 # if( length( $msg ) == 0 ) { next ; }
470 #
471 # my $forked_pid = fork();
472 ##=== PARENT = parent ==========================================================
473 # if ( $forked_pid != 0 ) {
474 # daemon_log("childprocess forked: $forked_pid", 3) ;
475 # $daemon_children{$forked_pid} = 0 ;
476 # }
477 ##=== CHILD = child ============================================================
478 # else {
479 # # parse the incoming message from arp, split the message and return
480 # # the values in an array. not defined values are set to "none"
481 # #my ($mac, $ip, $interface, $arp_sig, $desc) = &parse_input( $msg ) ;
482 # daemon_log( "childprocess read from arp: $fifo_path\nline: $msg", 3);
483 # my ($mac, $ip, $interface, $arp_sig, $desc) = split('\s', $msg, 5);
484 #
485 # # create connection to LDAP
486 # $#sysopen(FIFO, $fifo_path, O_RDONLY) or die "can't read from $fifo_path: $!" ;
487 #
488 #while( 1 ) {
489 # # checke alle prozesse im hash daemon_children ob sie noch aktiv sind, wenn
490 # # nicht, dann entferne prozess aus hash
491 # while( (my $key, my $val) = each( %daemon_children) ) {
492 # my $status = waitpid( $key, &WNOHANG) ;
493 # if( $status == -1 ) {
494 # delete $daemon_children{$key} ;
495 # daemon_log("childprocess finished: $key", 3) ;
496 # }
497 # }
498 #
499 # # ist die max_process anzahl von prozesskindern erreicht, dann warte und
500 # # prüfe erneut, ob in der zwischenzeit prozesse fertig geworden sind
501 # if( keys( %daemon_children ) >= $max_process ) {
502 # sleep($max_process_timeout) ;
503 # next ;
504 # }
505 #
506 # my $msg = <FIFO>;
507 # if( not defined( $msg )) { next ; }
508 #
509 # chomp( $msg );
510 # if( length( $msg ) == 0 ) { next ; }
511 #
512 # my $forked_pid = fork();
513 ##=== PARENT = parent ==========================================================
514 # if ( $forked_pid != 0 ) {
515 # daemon_log("childprocess forked: $forked_pid", 3) ;
516 # $daemon_children{$forked_pid} = 0 ;
517 # }
518 ##=== CHILD = child ============================================================
519 # else {
520 # # parse the incoming message from arp, split the message and return
521 # # the values in an array. not defined values are set to "none"
522 # #my ($mac, $ip, $interface, $arp_sig, $desc) = &parse_input( $msg ) ;
523 # daemon_log( "childprocess read from arp: $fifo_path\nline: $msg", 3);
524 # my ($mac, $ip, $interface, $arp_sig, $desc) = split('\s', $msg, 5);
525 #
526 # # create connection to LDAP
527 # $ldap = Net::LDAP->new( "localhost" ) or die "$@";
528 # $ldap->bind($bind_phrase,
529 # password => $password,
530 # ) ;
531 #
532 # switch($arp_sig) {
533 # case 0 {&change_ldap_entry($ldap, $ldap_base,
534 # $mac, "ip-changed",
535 # )}
536 # case 1 {&change_ldap_entry($ldap, $ldap_base,
537 # $mac, "mac-not-whitelisted",
538 # )}
539 # case 2 {&change_ldap_entry($ldap, $ldap_base,
540 # $mac, "mac-in-blacklist",
541 # )}
542 # case 3 {&add_ldap_entry($ldap, $ldap_base,
543 # $mac, "new-mac-address", $ip,
544 # $interface, $desc,
545 # )}
546 # case 4 {&change_ldap_entry($ldap, $ldap_base,
547 # $mac, "unauthorized-arp-request",
548 # )}
549 # case 5 {&change_ldap_entry($ldap, $ldap_base,
550 # $mac, "abusive-number-of-arp-requests",
551 # )}
552 # case 6 {&change_ldap_entry($ldap, $ldap_base,
553 # $mac, "ether-and-arp-mac-differs",
554 # )}
555 # case 7 {&change_ldap_entry($ldap, $ldap_base,
556 # $mac, "flood-detected",
557 # )}
558 # case 8 {&add_ldap_entry($ldap, $ldap_base,
559 # $mac, $ip, "new-system",
560 # )}
561 # case 9 {&change_ldap_entry($ldap, $ldap_base,
562 # $mac, "mac-changed",
563 # )}
564 # }
565 #
566 #
567 # ldap search
568 # my $base_phrase = "dc=gonicus,dc=de";
569 # my $filter_phrase = "cn=keinesorge";
570 # my $attrs_phrase = "cn macAdress";
571 # my $msg_search = $ldap->search( base => $base_phrase,
572 # filter => $filter_phrase,
573 # attrs => $attrs_phrase,
574 # );
575 # $msg_search->code && die $msg_search->error;
576 #
577 # my @entries = $msg_search->entries;
578 # my $max = $msg_search->count;
579 # print "anzahl der entries: $max\n";
580 # my $i;
581 # for ( $i = 0 ; $i < $max ; $i++ ) {
582 # my $entry = $msg_search->entry ( $i );
583 # foreach my $attr ( $entry->attributes ) {
584 # if( not $attr eq "cn") {
585 # next;
586 # }
587 # print join( "\n ", $attr, $entry->get_value( $attr ) ), "\n\n";
588 # }
589 # }
590 #
591 # # ldap add
592 #
593 #
594 # $ldap->unbind;
595 # exit;
596 # }
597 #
598 #}
600 1;