#! /usr/bin/perl -w

# vim:syntax=perl

use strict;
use lib '/usr/share/perl5';
use Lire::Time;
use Lire::Program qw/ :msg :dlf /;
use Lire::Firewall qw/ firewall_number2names /;

init_dlf_converter('firewall');

my $timestart = time() or lr_crit("can't get time");
my @ltime = localtime($timestart) or lr_crit("can't get time");

my $schema  = eval { Lire::DlfSchema::load_schema( "firewall" ) };
lr_err( "failed to load firewall schema: $@" ) if $@;
my $dlf_maker = $schema->make_hashref2asciidlf_func( qw/time rule action
    protocol from_ip from_port rcv_intf rcv_hwaddr to_ip to_port count/ );

my ($lines, $dlflines, $errorlines) = (0, 0, 0);

while (<>) {
    chomp;
# Apr 24 10:55:35 router 113613: 5d23h: %SEC-6-IPACCESSLOGP: list bigcompany 
#  permitted tcp 10.2.108.1(0) -> 10.72.251.5(0), 80 packets
# Apr 24 10:55:35 router 113614: 5d23h: %SEC-6-IPACCESSLOGDP: list bigcompany 
#  permitted icmp 10.2.108.1 -> 10.68.187.5 (0/0), 2 packets
# Apr 29 03:28:17 192.168.1.1 39724: 2w2d: %SEC-6-IPACCESSLOGP: list 102 
#  denied udp 192.168.1.169(13991) (Ethernet0 0004.ac38.4d3e) -> 
#  192.168.3.250(13991), 1 packet
# Apr 29 03:30:17 192.168.1.1 39725: 2w2d: %SEC-6-IPACCESSLOGP: list 102 
#  denied udp 192.168.1.163(13991) (Ethernet0 ) -> 192.168.3.200(13991), 1 
#  packet
    $lines++;

    my @w;
    my @v;

    if (
        not /%SEC-6-IPACCESSLOGD?P:/ or
        ( @w = split /: /) < 4 or
        ( @v = split(/ +/, $w[0]) ) < 5
    ) {
        $errorlines++;
        next;
    }

    my %dlf;
    eval {
        $dlf{'time'} = syslog2cal($v[0], $v[1], $v[2], \@ltime);
    };
    if ( $@ ) {
        lr_warn( $@ );
        lr_warn("syslog2cal on '$v[0]' '$v[1]' '$v[2]' failed\n");
        $errorlines++;
        next;
    }

# router 113613: 5d23h: %SEC-6-IPACCESSLOGP: list bigcompany permitted 
#  tcp 10.2.108.1(0) -> 10.72.251.5(0), 80 packets
# router 113614: 5d23h: %SEC-6-IPACCESSLOGDP: list bigcompany permitted 
#  icmp 10.2.108.1 -> 10.68.187.5 (0/0), 2 packets
# 192.168.1.1 39724: 2w2d: %SEC-6-IPACCESSLOGP: list 102 denied udp 
#  192.168.1.169(13991) (Ethernet0 0004.ac38.4d3e) -> 192.168.3.250(13991), 
#  1 packet
# 192.168.1.1 39725: 2w2d: %SEC-6-IPACCESSLOGP: list 102 denied udp 
#  192.168.1.163(13991) (Ethernet0 ) -> 192.168.3.200(13991), 1 packet

# %SEC-6-IPACCESSLOGP: list bigcompany permitted tcp 10.2.108.1(0) -> 
#  10.72.251.5(0), 80 packets
# %SEC-6-IPACCESSLOGDP: list bigcompany permitted icmp 10.2.108.1 -> 
#  10.68.187.5 (0/0), 2 packets
# %SEC-6-IPACCESSLOGP: list 102 denied udp 192.168.1.169(13991) 
#  (Ethernet0 0004.ac38.4d3e) -> 192.168.3.250(13991), 1 packet
# %SEC-6-IPACCESSLOGP: list 102 denied udp 192.168.1.163(13991) 
#  (Ethernet0 ) -> 192.168.3.200(13991), 1 packet

    if ((@v = split ' ', $w[3]) < 9) {
        $errorlines++;
        next;
    }

    if ($w[2] eq '%SEC-6-IPACCESSLOGDP') {
        shift @v; # List
        $dlf{'rule'} = shift @v;
        $dlf{'action'} = shift @v;
        $dlf{'protocol'} = shift @v;
        $dlf{'from_ip'} = shift @v;
        if ($v[0] =~ /^\(([^ ]+)/) {
            $dlf{'rcv_intf'} = $1;
            shift @v;
            my $hfield = $v[0];
            while ($hfield !~ /\)$/) { 
                shift @v; 
                $hfield.="_".$v[0]; 
            }
            if ($hfield =~ /^(.*)\)/) {
                $dlf{'rcv_hwaddr'} = $1;
            }
            shift @v;
        }
        shift @v;
        $dlf{'to_ip'} = shift @v;
        shift @v;
        $dlf{'count'} = shift @v;
    } elsif ($w[2] eq '%SEC-6-IPACCESSLOGP') {
        shift @v;   # list
        $dlf{'rule'} = shift @v;
        $dlf{'action'} = shift @v;
        $dlf{'protocol'} = shift @v;
        $v[0] =~ /(.*)\(([0-9]+)\)/;
        $dlf{'from_ip'} = $1;
        $dlf{'from_port'} = $2;
        shift @v;
        if ($v[0] =~ /^\(([^ ]+)/) {
            $dlf{'rcv_intf'} = $1;
            shift @v;
            # beware hardware adres field can contain spaces.
            my $hfield = $v[0];
            while ($hfield !~ /\)$/) { 
                shift @v; 
                $hfield.="_".$v[0]; 
            }
            if ($hfield =~ /^(.*)\)$/) {
                $dlf{'rcv_hwaddr'} = $1;
            }
            shift @v;
        }
        shift @v;
        $v[0]=~/(.*)\(([0-9]+)\)/;
        $dlf{'to_ip'} = $1;
        $dlf{'to_port'} = $2;
        shift @v;
        $dlf{'count'} = shift @v;
    } else {
        lr_debug("skipped line '$_', tag " . $w[2] . " doesn't match");
        $errorlines++;
        next;
    }

    my $dlf;
    eval {
	firewall_number2names( \%dlf );
        $dlf = $dlf_maker->( \%dlf )
    };
    if ( $@ ){
        lr_warn( $@ );
        lr_warn("cannot convert %dlf to dlf, skipping\n");
        $errorlines++;
        next;
    }

    print join( " ", @$dlf ), "\n";
    $dlflines++;
}

end_dlf_converter( $lines, $dlflines, $errorlines );

exit 0;

__END__

=pod

=head1 NAME

acl_cisco_log2dlf - convert cisco logs to dlf format

=head1 SYNOPSIS

B<acl_cisco_log2dlf>

=head1 DESCRIPTION

This script expects syslog-type logs from a CISCO IOS router on stdin.  These
look like e.g.

 Jul  3 00:00:39 router 40108: 4d09h: %SEC-6-IPACCESSLOGP:
  list FR_VA_in permitted udp 192.168.19.1(137) (Serial0/0.2 DLCI 120)
  -> 192.168.19.255(137), 2 packets
 Jul  3 00:02:39 router 40109: 4d09h: %SEC-6-IPACCESSLOGP: list FR_VA_in
  permitted udp 192.168.80.42(138) (Serial0/0.2 DLCI 120) ->
  192.60.60.148(138), 1 packet
 Jul  3 00:02:39 router 40110: 4d09h: %SEC-6-IPACCESSLOGDP: list FR_VA_in
  permitted icmp 192.168.80.82 (Serial0/0.2 DLCI 120) -> 149.1.1.1 (8/0),
  1 packet

or

 Aug 19 04:02:34 gateway.foo.bar 218963: Aug 19 04:02:32.977:
  %LINEPROTO-5-UPDOWN: Line protocol on Interface BRI0:1, changed state
  to down
 Aug 19 04:02:34 gateway.foo.bar 218964: Aug 19 04:02:33.262:
  %ISDN-6-DISCONNECT: Interface BRI0:1  disconnected from 172605440 acme,
  call lasted 42 seconds
 Aug 19 04:02:35 gateway.foo.bar 218965: Aug 19 04:02:33.266:
  %LINK-3-UPDOWN: Interface BRI0:1, changed state to down
 Aug 19 04:02:38 gateway.foo.bar 218966: Aug 19 04:02:36.103:
  %SEC-6-IPACCESSLOGP: list 102 denied tcp 100.198.139.148(4652) ->
  100.193.176.49(80), 1 packet
 Aug 19 04:02:45 gateway.foo.bar 218967: Aug 19 04:02:43.543:
  %ISDN-6-LAYER2DOWN: Layer 2 for Interface BR0, TEI 86 changed to down
 Aug 19 04:02:53 gateway.foo.bar 218968: Aug 19 04:02:51.471:
  %SEC-6-IPACCESSLOGP: list 102 denied tcp 100.74.103.1(2162) ->
  100.193.176.98(80), 1 packet

The outputted dlf files look like:

 994118619 permitted icmp 192.168.80.9 - Serial0/0.2 DLCI_120
  192.168.19.1 - 1
 994118619 permitted udp 192.168.19.1 138 Serial0/0.2 DLCI_120
  192.168.19.255 138 1

=head1 VERSION

$Id: acl_cisco_log2dlf.in,v 1.13 2002/02/05 18:26:40 flacoste Exp $

=head1 COPYRIGHT

Copyright (C) 2001 Joost Bekkers <joost@jodocus.org>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program (see COPYING); if not, check with
http://www.gnu.org/copyleft/gpl.html or write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.

=head1 AUTHOR

Initial code by Joost Bekkers <joost@jodocus.org>, now maintained by the
LogReport team

=cut

# Local Variables:
# mode: cperl
# End:
