File Coverage

blib/lib/Net/Netfilter/NetFlow/Process.pm
Criterion Covered Total %
statement 30 88 34.0
branch 0 20 0.0
condition 0 14 0.0
subroutine 10 14 71.4
pod 0 3 0.0
total 40 139 28.7


line stmt bran cond sub pod time code
1             package Net::Netfilter::NetFlow::Process;
2             {
3             $Net::Netfilter::NetFlow::Process::VERSION = '1.113260';
4             }
5              
6 1     1   4918 use strict;
  1         2  
  1         46  
7 1     1   5 use warnings FATAL => 'all';
  1         2  
  1         42  
8              
9 1     1   6 use base 'Exporter';
  1         2  
  1         113  
10             our @EXPORT = qw(
11             conntrack_init
12             ct2ft
13             ptee
14             );
15              
16 1     1   1009 use POSIX; # core
  1         7955  
  1         11  
17 1     1   7240 use Time::HiRes 'gettimeofday'; # core
  1         3713  
  1         6  
18 1     1   7592 use IPC::Run 'run';
  1         53769  
  1         65  
19 1     1   1165 use Log::Dispatch::Config;
  1         42413  
  1         23  
20 1     1   1183 use Log::Dispatch::Configurator::Any;
  1         2652  
  1         16  
21 1     1   39 use Net::Netfilter::NetFlow::Utils;
  1         2  
  1         81  
22 1     1   951 use Net::Netfilter::NetFlow::ConntrackFormat;
  1         3  
  1         1164  
23              
24             # poke conntrack kernel hooks into waking up (bug?)
25             sub conntrack_init {
26 0     0 0   my $config = shift;
27 0 0         my $conntrack = can_run($config->{conntrack}->{progname})
28             or die "Failed to find a local copy of conntrack in the path\n";
29              
30 0           run [$conntrack, format_args($config->{conntrack}, 'init_')],
31             '>', '/dev/null', '2>&1';
32             }
33              
34             # convert the conntrack output to flow-tools CSV input format
35             sub ct2ft {
36 0     0 0   my $config = shift;
37 0           my $got_alrm = 0;
38 0           my $tracker = {};
39              
40             # respond to SIGALRM (thanks go to perlipc man page)
41 0     0     my $alrm_handler = sub { ++$got_alrm };
  0            
42             # POSIX unmasks the sigprocmask properly
43 0           my $action = POSIX::SigAction->new($alrm_handler);
44 0           POSIX::sigaction(&POSIX::SIGALRM, $action);
45              
46 0   0       my $ttl = $config->{ct2ft}->{ttl} || 60 * 60 * 24 * 7; # seven days;
47 0           alarm $ttl;
48              
49             # XXX alarm will not fire until we have input to process
50 0           while (<>) {
51              
52             # pruge tracked connections older than TTL seconds
53 0 0         if ($got_alrm) {
54 0           alarm 0;
55 0           foreach my $p (keys %{$tracker}) {
  0            
56 0           foreach my $k (keys %{$tracker->{$p}}) {
  0            
57 0 0         delete $tracker->{$p}->{$k}
58             if $tracker->{$p}->{$k} < ($^T - $ttl);
59             }
60             }
61 0           $got_alrm = 0;
62 0           alarm $ttl;
63             }
64              
65 0           chomp;
66 0           s/[^\s\d.A-Z]//g;
67 0 0         next if m/^\s+$/;
68 0           my $line = $_;
69 0           my @fields = split /\s+/, $line;
70 0 0         next unless scalar @fields > 12;
71              
72 0 0         next unless $fields[1] =~ m/^(NEW|DESTROY)$/;
73 0           my $mode = $1;
74 0 0         next unless $fields[2] =~ m/^(1|6|17)$/;
75 0           my $proto = $1;
76              
77 0 0 0       next if $proto == 1 and
      0        
78             (($fields[5] ne '8') and ($fields[6] ne '8')); # only interested in ECHO
79              
80 0 0         if ($mode eq 'NEW') {
81 0           my $key = join ',', @fields[ @{$ct_new_key{$proto}} ];
  0            
82 0           $tracker->{$proto}->{$key} = $fields[0];
83 0           next;
84             }
85              
86 0           my $key = join ',', @fields[ @{$ct_destroy_key{$proto}} ];
  0            
87 0 0         next unless exists $tracker->{$proto}->{$key};
88              
89 0           my ($start_secs, $start_micsecs) = split /\./, $tracker->{$proto}->{$key};
90 0           my ($end_secs, $end_micsecs) = split /\./, $fields[0];
91              
92             # secs and nanosecs (^9) since 1970
93 0           my ($unix_secs, $micsecs) = gettimeofday;
94 0           my $unix_nsecs = $micsecs * 1_000;
95              
96             # millisecs (^3) since "boot"
97 0           my $sysuptime = (($unix_secs - $^T) * 1_000) + int ($micsecs / 1_000);
98              
99             # flow start/end in millisecs since "boot"
100 0           my $first = (($start_secs - $^T) * 1_000) + int ($start_micsecs / 1_000);
101 0           my $last = (($end_secs - $^T) * 1_000) + int ($end_micsecs / 1_000);
102              
103 0           for my $dir (qw( private_src public_src dst )) {
104 0           my ($dpkts, $doctets, $srcaddr, $dstaddr, $srcport, $dstport)
105 0           = @fields[ @{$ct_mask_fields{$proto}{$dir}} ];
106              
107 0   0       print join ',',
      0        
      0        
108             $unix_secs,
109             $unix_nsecs,
110             $sysuptime,
111             $config->{flow_send}->{args}->[0] || '127.0.0.1',
112             $dpkts,
113             $doctets,
114             $first,
115             $last,
116             $srcaddr,
117             $dstaddr,
118             '0.0.0.0', # NEXTHOP
119             0, # INPUT (SNMP idx)
120             0, # OUTPUT (SNMP idx)
121             $srcport || 0, # might be ICMP
122             $dstport || 0, # might be ICMP
123             $proto,
124             0, # TOS
125             0; # TCP_FLAGS
126 0           print "\n";
127             }
128             } # while (<>)
129             }
130              
131             # set up output tee to local syslog, and next process in pipe
132             sub ptee {
133 0     0 0   my $config = shift;
134 0           Log::Dispatch::Config->configure_and_watch(
135             Log::Dispatch::Configurator::Any->new($config->{ptee}->{conf}) );
136 0           my $dispatcher = Log::Dispatch::Config->instance;
137              
138 0           while (<>) {
139 0           $dispatcher->notice($_);
140             }
141             }
142              
143             __END__