#!/usr/bin/perl -w

use strict;
use utf8;
use warnings;

BEGIN {
    unshift @INC, ( $ENV{POPHOME} || '@POPHOME@' ) . '/lib'
      unless $ENV{KUNDE_NO_PERLPATH};
}

use Cf qw($ACCDB $ACCHOST $ACCPASS $ACCUSER);
use Date::Calc qw(Add_Delta_Days Date_to_Days Mktime);
use Dbase::Getopt qw(:DEFAULT getopt_date);
use Dbase::Globals qw(find_descr get_ipkunde);
use Dbase::Help qw(:readonly DoFn DoTime in_list isodate);
use Dbase::IP ();

sub getopt_day {
    my ($option) = @_;
    my @l = localtime( my $time = &getopt_date );
    die "Bei -$option sind nur tagesgenaue Angaben möglich.\n"
      if grep $_, @l[ 0 .. 2 ];
    $time;
}

sub epoch2jjjjmmtt($) { sprintf '%d%02d%02d', isodate(shift) }

my ( $Seit, $Vor );
my @IgnoriereZiele = map find_descr( ziel => $_, 1 ), qw(lokal noris);

GetOptions(
    'ignoriere-ziele=s' => sub {
        ( undef, my $value ) = @_;
        @IgnoriereZiele = map find_descr( ziel => $_, 1 ), split /\W+/, $value;
    },
    'min-bytes=i' => \my $MinBytes,
    'seit=s'      => sub { $Seit = &getopt_day },
    'vor=s'       => sub { $Vor = &getopt_day },
);

my $now = DoTime();

$Seit =
  defined $Vor
  ? Mktime( Add_Delta_Days( isodate($Vor), -7 ), (0) x 3 )
  : getopt_day( undef, 'midnight -1 week' )
  unless defined $Seit;

$MinBytes =
  42 *
  2**20 *
  ( Date_to_Days( isodate( defined $Vor ? $Vor : $now ) ) -
      Date_to_Days( isodate($Seit) ) );

my @where = 'datum >= ' . epoch2jjjjmmtt($Seit);
push @where, 'datum < ' . epoch2jjjjmmtt($Vor) if defined $Vor;
push @where, in_list( ziel => NOT => @IgnoriereZiele ) if @IgnoriereZiele;

my $einzel_db = Dbase->new(
    DATAHOST   => $ACCHOST,
    DATAHOST2  => $ACCHOST,
    DATAUSER   => $ACCUSER,
    DATAPASS   => $ACCPASS,
    DBDATABASE => $ACCDB,
) or die;

my $lines;
$einzel_db->DoSelect(
    sub {
        my ( $ip, $bytes ) = @_;
        unless ( defined( my $id = get_ipkunde($ip) ) ) {
            print "$ip ($bytes): unbekannt.\n";
        }
        else {
            my ( $ip6, $bits ) =
              DoFn("SELECT ip6, bits FROM ipkunde WHERE id = $id")
              or die;

            if ($bits) {
                my $ip_o = Dbase::IP->new_db( $ip6, $bits ) or die;

                unless ( $lines++ ) {
                    my $headline =
                      sprintf
                      'IP-Adresse      Teil des Bereiches        Traffic '
                      . (
                        defined $Vor
                        ? isodate($Seit) . ' - ' . isodate($Vor)
                        : 'seit ' . isodate($Seit)
                      );
                    print $headline, "\n", '-' x length $headline, "\n";
                }

                printf "%-16s%5d:%-20s%8.f MiB\n", $ip, $id, $ip_o,
                  $bytes / 2**20;
            }
        }
    },
    <<HEAD . join( '', map <<WHERE, @where ) . <<TAIL);
	SELECT   INET_NTOA( IF( quelle < 0, POW(2,32) + quelle, quelle ) ),
	         SUM(bytes) Traffic
	FROM     netflow
	WHERE    kunde = 1
HEAD
	     AND $_
WHERE
	GROUP BY quelle
	HAVING   Traffic > $MinBytes
	ORDER BY Traffic DESC
TAIL

__END__

=encoding utf8

=head1 NAME

geistertraffic - nicht zugeordnete IP-Adressen mit Traffic finden

=head1 SYNOPSE

    geistertraffic

=head1 BESCHREIBUNG

Dieses Tool ermittelt anhand des Einzel-IP-Accountings und der Datenbank, für
welche IP-Adressen signifikant Traffic aufläuft, obwohl sie weder einem Kunden
zugeordnet noch als Host bei Kunde POP eingetragen sind.

=head1 OPTIONEN

=over 4

=item -ignoriere-ziele Ziel-Liste

zur Angabe einer komma-getrennten Liste von Zielen; entsprechender Traffic
wird dann ignoriert.
Voreingestellt ist C<lokal,noris>, weil dieser Traffic den Kunden üblicherweise
nicht berechnet wird.

=item -min-bytes Bytes

zur Festlegung des Mindestraffics, der pro IP-Adresse im fraglichen Zeitraum
angefallen sein muss;
Default: durchschnittlich 42 MiB pro Tag

=item -seit Tag

Nur Accounting-Datensätze berücksichtigen, die nicht älter sind als das
(tagesgenau) angegebene Datum;
Default: eine Woche vor L</-vor>

=item -vor Tag

Nur Accounting-Datensätze ausgeben, die älter sind als das (tagesgenau)
angegebene Datum.

=item -help

=item -?

um (nur) diese Dokumentation anzeigen zu lassen

=over back

=head1 BEKANNTE FEHLER

Bei der Überprüfung, ob die fraglichen IP-Adressen einem Kunden oder einem
bekannten Host zugeordnet sind, werden nur die aktuellen Zuordnungen beachtet.

=back

