#! /usr/bin/perl -w

# vim:syntax=perl

use strict;
use lib '/usr/local/share/perl5';
use Lire::DlfSchema;
use Lire::Program qw( :msg :dlf );
use Lire::Firewall qw/ firewall_number2names /;
use Lire::WELF;

init_dlf_converter( "firewall" );
my $schema	= Lire::DlfSchema::load_schema( "firewall" );
my $dlf_maker	=
  $schema->make_hashref2asciidlf_func( qw/time rule action protocol
					  rcv_intf from_ip from_port
					  to_ip to_port snt_intf
					  length count msg/);

my %wtproto2proto = 
  ( 
   http		=> [ "tcp", 80	],
   https	=> [ "tcp", 443 ],
   dns		=> [ "udp", 53	],
   ftp		=> [ "tcp", 80	],
   telnet	=> [ "tcp", 23	],
   smtp		=> [ "tcp", 25	],
   pop3		=> [ "tcp", 110 ],
   realaudio	=> [ "udp", "Real" ], # FIXME: Does this makes sense
  );

my $lines      = 0;
my $dlflines   = 0;
my $errorlines = 0;
my $parser = new Lire::WELF;
while (<>) {
    lire_chomp();
    $lines++;

    # Skip empty lines
    next if /^\s*$/;

    eval {
	my $welf = $parser->parse( $_ );

	die "not a firewall WELF record: id=", $welf->{id}, "\n"
	  unless $welf->{id} eq 'firewall';

	# Skip messages without src or dst.
	return unless $welf->{src} && $welf->{dst};

	my %dlf = ( time    => $welf->{time},
		    action  => "permitted",
		    count   => 1,
		    rule    => $welf->{rule},
		    msg	    => $welf->{msg},
		    from_ip   => $welf->{src},
		    from_port => $welf->{src_port},
		    rcv_intf  => $welf->{src_if},
		    to_ip     => $welf->{src},
		    to_port   => $welf->{src_port},
		    snt_intf  => $welf->{src_if},
		  );

	# When there is a proto, it is usually an accepted packet flow
	if ( $welf->{proto}) {
	    my $proto = lc $welf->{proto};

	    # Guess destination port based on that value
	    if ( exists $wtproto2proto{$proto}) {
		$dlf{protocol} = $wtproto2proto{$proto}[0];
		$dlf{to_port}  ||= $wtproto2proto{$proto}[1];
	    } elsif ( $proto =~ m,(.*)/(.*),) {
		$dlf{protocol} = $1;
		$dlf{to_port}  ||= $2;
	    } else {
		# Try some sane(?) defaults
		$dlf{protocol} = "tcp";
		$dlf{to_port}  ||= $proto;
	    }
	    # Netscreen ScreenOS 3.0.1. WELF logs features stuff like
            # Jul 18 19:02:49 10.0.0.1 id=firewall time="2002-07-18 19:01:06"
            #  fw=17010041 pri=7 rule=320001 proto=udp/port:1900
            #  src=10.0.0.2 dst=10.0.0.1 sent=0 rcvd=161 duration=0
            #  msg="Action:Deny" module=system level=information type=08668
            # ; this breaks heuristic.  fix it here.
	    if ( defined $welf->{msg} ) {
		if ( $welf->{msg} eq 'Action:Deny' ) {
		    $dlf{action} = "denied";
		} elsif ( $welf->{msg} eq 'Action:Permit' ) {
		    $dlf{action} = "permitted";
		}
	    }
            # fyi: accepted packets have Action:Permit
            # normal Netscreen logs sent via syslog look different (and more
            # useful), btw.
	} elsif ( exists $welf->{msg} ) {
	    # Dropped packet usually don't contains a 
	    # proto= field and the msg will contains dropped
	    # with the protocol
	    $dlf{action}   = "denied"
	      if $welf->{msg} =~ /dropped|rejected|blocked|attack/i;
	    if ( $welf->{msg} =~ /^(tcp|udp|icmp)/i ) {
		$dlf{protocol} = lc $1;
	    }
	}

	# Length should be computed from rcvd and sent, will be 0 on dropped
	# packet :(
	if ( $welf->{rcvd} || $welf->{sent}) {
	    $dlf{length} = 0;
	    $dlf{length} += $welf->{rcvd} if $welf->{rcvd};
	    $dlf{length} += $welf->{sent} if $welf->{sent};
	}

	firewall_number2names( \%dlf );

	my $dlf = $dlf_maker->( \%dlf );

	print join( " ", @$dlf), "\n";
	$dlflines++;
    };
    if ($@) {
	lr_warn( $@ );
	lr_notice( qq{cannot convert line $. "$_" to firewall dlf, skipping} );
	$errorlines++;
    }
}

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

__END__

=pod

=head1 NAME

welf2dlf - convert firewall logs in WebTrends Enhanced Log Format to DLF

=head1 SYNOPSIS

B<welf2dlf> I<file>

=head1 DESCRIPTION

B<welf2dlf> converts firewall logs in the WebTrends Enhanced Log
Format into the firewall DLF.

That format is defined at the following URL:
http://www.netiq.com/partners/technology/welf.asp

This converter also supports the SonicWall extensions.

A list of firewall products that supports that format can be found
at the following URL:
http://www.netiq.com/products/fwr/compatible.asp

=head1 IMPLEMENTATION NOTES

Since the firewall DLF only supports packet filters, not all records
will be mapped to DLF.

Dropped packets messages (those with a field msg="XXX packet dropped")
will be mapped to denied DLF packet. (As well as all msg="TCP
connection dropped" records). Those packets will have a length of 0
(that's a limitation of the format which don't log that information on
dropped packet).

Messages with a proto= field set will be interpretted as "permitted"
"packet". That's not exactly "right" because this message really
represent a "packets flow" and not a single packet.

Other records will be ignored.

You may use the welf_proxy service in the proxy superservice to
extract proxy level information from WELF logs.

=head1 THANKS

Mark D. Nagel, for giving feedback and supplying patches.

=head1 SEE ALSO

Lire::WELF(3) welf_proxy2dlf(1)

=head1 AUTHORS

Francis J. Lacoste <flacoste@logreport.org>

=head1 VERSION

$Id: welf2dlf.in,v 1.7 2002/08/19 00:47:37 flacoste Exp $

=head1 COPYRIGHT

Copyright (C) 2001 Stichting LogReport Foundation LogReport@LogReport.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.

=cut

# Local Variables:
# mode: cperl
# End:
