#!/usr/local/bin/perl -w
require 5.003 ;
require Net::Pcap ;
use Msql () ;
sub FALSE { 0 } ;
sub TRUE { 1 } ;

$DEBUG = &FALSE ;
$DEBUG = &TRUE ;
$query_DEBUG = &FALSE ;
$query_DEBUG = &TRUE ;
#-----------------------------------------------------------------------
$CHUNK_SIZE=10 ;

sub update_existing_macips {
    my ( $macipref_arrref )  = @_ ;

    my ( $dbh ) = Msql->connect ( $db_server ) ;
    die ( "Couldn't connect to msql on $db_server: " , $Msql::db_errstr )
	unless $dbh ;
    $dbh->selectdb( $db_name ) ;
    die ( "Couldn't connect to $db_name on $db_server: " , $Msql::db_errstr )
	unless $dbh ;

    my ( $sth ) = $dbh->query ( 'select _seq from event' ) ;
    die ("Couldn't get event ID: ", $Msql::db_errstr ) unless $sth ;
    my ( $event ) = $sth->fetchrow ;
    print "Event ID: $event\n" if $DEBUG ;
    $sth=$dbh->query ( "insert into event ( id , timestamp ) values ( $event , " . time() . " )" ) ;
    die ("Couldn't add new event: ", $Msql::db_errstr ) unless $sth ;
    foreach $macipref ( @$macipref_arrref )
    {
	my ( $mac , $ip ) = @$macipref ;
	my ( $insert_query ) = "insert into mac2ip ( mac , ip , firstevent ) values ( \'$mac\' , \'$ip\' , $event )" ;
	print "$insert_query\n" if $query_DEBUG ;
	$Msql::QUIET = &TRUE ;
	my ( $sth ) = $dbh->query ( $insert_query ) ;
	$Msql::QUIET = &FALSE ;
	next if ( $dbh ->errmsg eq 'Non unique value for unique index' ) ;
	print "Errmsg :" . ( + $Msql::db_errstr ) . "\n" ;
	die ("Couldn't do query \"$insert_query\": ", $Msql::db_errstr )
	    unless $sth ;
    }
    while ( @$macipref_arrref )
    {
	# Chunk the selects into blocks of $CHUNK_SIZE
	my ( @macipref_chunk )
	    = splice ( @$macipref_arrref , 0 , $CHUNK_SIZE ) ;
	my @query_list = ( ) ;
	foreach $macipref ( @macipref_chunk )
	{
	    my ( $mac , $ip ) = @$macipref ;
	    push ( @query_list , "( mac = \'$mac\' AND ip = \'$ip\' )" ) ;
	}
	$update_query="update mac2ip set lastevent = $event where "
	    . join ( ' OR ' , @query_list ) ;
	print "Update query: $update_query\n" if $query_DEBUG ;
	my ( $sth ) = $dbh->query ( $update_query ) ;
	die ("Couldn't do query \"$update_query\": ", $Msql::db_errstr )
	    unless $sth ;
	print "Update query done\n" if $DEBUG ;
    }
}
#-----------------------------------------------------------------------
#$db_server = 'snoopy.ccc' ;
$db_server = 'localhost' ;
$db_name = 'crandb' ;

sub process_pkts
{
    my ( $pktref_arrref ) = shift ;
    my ( $macipref ) ;

    foreach $pktref ( @$pktref_arrref )
    {
	my ( $ether_dst , $ether_src , $ether_type , $dunno , $reply ,
	    $arp_src_mac , $arp_src_ipa, $arp_src_ipb, $arp_src_ipc, $arp_src_ipd )
	    = unpack ( 'H12 H12 H4 H14 C H12 C4' , $$pktref ) ;
	next unless ( $ether_src eq $arp_src_mac ) ;
	$arp_src_mac=~s/^(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)$/$1:$2:$3:$4:$5:$6/ ;
	push ( @$macipref , [ "$arp_src_mac" , "$arp_src_ipa.$arp_src_ipb.$arp_src_ipc.$arp_src_ipd" ] ) ;
	print "$arp_src_mac\t$arp_src_ipa.$arp_src_ipb.$arp_src_ipc.$arp_src_ipd\n" if $DEBUG  ;
    }
    &update_existing_macips ( $macipref ) ;
}

sub get_pkt
{
#    print "get_pkt\n" if $DEBUG ;
    my ( $arg , $pkt) = @_ ;
    push ( @$arg , \$pkt ) ;
}
#-----------------------------------------------------------------------
$pcap_dev = Net::Pcap::lookupdev (\$errbuf) ;
die ( "error: $errbuf" ) unless ( $pcap_dev ) ;
my ( $net , $mask , $bpf_prog ) ;
$status = Net::Pcap::lookupnet ( $pcap_dev , \$net , \$mask , \$errbuf ) ;
die ( "error: $errbuf" ) if ( $status == -1 ) ;
$pcap_desc = Net::Pcap::open_live ( $pcap_dev , 100 , 1, 1000, \$errbuf ) ;
die ( "error: $errbuf" ) unless ( $pcap_desc ) ;
Net::Pcap::compile ( $pcap_desc , \$bpf_prog, 'arp' , 0 , $mask ) ;
Net::Pcap::setfilter ( $pcap_desc , $bpf_prog ) ;

$pkt_cnt = 10 ;
my @pkts = () ;
Net::Pcap::loop ( $pcap_desc , $pkt_cnt , \&get_pkt , \@pkts ) ;
& process_pkts ( \@pkts ) ;
#-----------------------------------------------------------------------
