#!/usr/bin/perl -w

# Copyright (C) 2009-2010 Pierre Chifflier <chifflier@inl.fr>
#
# This program is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself, either Perl version 5.8.4 or,
# at your option, any later version of Perl 5 you may have available.

use strict;

use IO::Socket;
use Net::Pcap;

my $ULOGD_SOCKET_MARK = 0x41c90fd4;

my $dumpfile = shift or die "Unable to open pcap file";
my($pcap_t, $err);
my($ulogd_client);
my $socketfile = "/var/run/ulogd2.sock";
my $data_buffer;
my $linktype;
my $proto_offset;

my %linktype_to_offset = (
	Net::Pcap::DLT_LINUX_SLL => 14,
	Net::Pcap::DLT_EN10MB => 12,
);

sub connect_ulogd2 {
    (-S $socketfile) or die "ulogd2 socket $socketfile does not exist - is ulogd running ?";

    $ulogd_client = IO::Socket::UNIX->new(Peer  => $socketfile,
                                          Type      => SOCK_STREAM ) or die $!;
    $ulogd_client->autoflush(0);
}

sub print_padding
{
    my ($offset) = @_;
    my $padding;
    my $align = 8;
    my $data;

    $padding = ($align - ($offset % $align)) % $align;
    #print "offset: $offset padding $padding\n";

    $data = "\0" x $padding;
    $data_buffer .= $data;
}

sub process_pkt {
    my($user, $hdr, $pkt) = @_;

    if (($user ne "xyz") or !defined($hdr) or !defined($pkt)) {
        print("Bad args passed to callback\n");
        print("Bad user data\n"), if ($user ne "xyz");
        print("Bad pkthdr\n"), if (!defined($hdr));
        print("Bad pkt data\n"), if (!defined($pkt));
        print("not ok\n");
        exit;
    }

    #print "Header: len $hdr->{len}\n";
    #my $len = length $pkt;
    #print "Packet length: $len\n";

    my $size = length($pkt) - ($proto_offset+2);

    #my $pcaphdr = unpack ("H*", substr ($pkt, 0, 16));
    #printf("pcap hdr: $pcaphdr\n");
    my $proto = unpack ("H*", substr ($pkt, $proto_offset, 2));
    #printf("proto: $proto\n");

    if ($proto ne "0800") {
      print "ignoring packet with proto $proto\n";
      return;
    }

    #my $ip_firstbyte = unpack ("H*", substr ($pkt, $proto_offset+2, 2));
    #printf("ip_firstbyte: $ip_firstbyte\n");

    # decode packet for a SLL:
    # packet type (sent by us: 4)
    # link layer address type: 1
    # link layer address length: 6
    # src dst
    # protocol (IP, ARP, PPP, SNMP ...)
    # data
    my $srcmac = substr ($pkt, 6, 6);

    (my $hex_src = unpack("H*", $srcmac)) =~ s/(..)/$1:/g;
    chop $hex_src;
    #printf "source mac: $hex_src\n";

    my $hex_dst = "\0";

    # format data
    my $data;

    $data_buffer = undef;

    # ulogd packet signature
    $data = pack ('N', $ULOGD_SOCKET_MARK);

    $data_buffer .= $data;

    my $options_num=2;
    my $options_len=length($hex_src) + length($hex_dst);
    # total length (will be filled later)
    my $total_size = 0;
    $data = pack ('n', $total_size);
    $data_buffer .= $data;

    # reserved + payload length + payload
    $data = pack ('Nna*', 0, $size, substr($pkt,$proto_offset+2,$size));
    $data_buffer .= $data;
    print_padding($size);

    # options
    my $OOB_IN = 2;
    $data = pack ('NNa*', $OOB_IN, length($hex_src), $hex_src);
    $data_buffer .= $data;
    print_padding(length($hex_src));
    my $OOB_OUT = 3;
    $data = pack ('NNa*', $OOB_OUT, length($hex_dst), $hex_dst);
    $data_buffer .= $data;
    print_padding(length($hex_dst));

    # replace total size in buffer
    my $l = length($data_buffer) - 4;
    substr($data_buffer, 4, 2) = pack('n', $l);

    #(my $hex = unpack("H*", $data_buffer)) =~ s/(..)/$1 /g;
    #print "$l will be encoded as " . unpack("H*", pack('n', $l)) . "\n";
    #print $hex, "\n";

    print $ulogd_client $data_buffer;

    $ulogd_client->flush;

    #exit;
}


connect_ulogd2 or die $!;

$pcap_t = Net::Pcap::open_offline($dumpfile, \$err);
if (!defined($pcap_t)) {
    print("Net::Pcap::dump_open failed: ", Net::Pcap::geterr($pcap_t), "\n");
    exit;
}

$linktype = Net::Pcap::pcap_datalink($pcap_t);

if (not exists $linktype_to_offset{$linktype}) {
	print("Unsupported link type ", Net::Pcap::pcap_datalink_val_to_name($linktype), "\n");
	exit 1;
}

$proto_offset = $linktype_to_offset{$linktype};

Net::Pcap::loop($pcap_t, -1, \&process_pkt, "xyz");
Net::Pcap::close($pcap_t);



