File Coverage

blib/lib/Net/Flow.pm
Criterion Covered Total %
statement 162 449 36.0
branch 33 190 17.3
condition 3 44 6.8
subroutine 24 30 80.0
pod 2 11 18.1
total 224 724 30.9


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             #
3             #
4             # Atsushi Kobayashi
5             #
6             # Acknowledgments
7             # This module was supported by the Ministry of Internal Affairs and
8             # Communications of Japan.
9             #
10             # Flow.pm - 2008/12/04
11             #
12             # Copyright (c) 2007-2008 NTT Information Sharing Platform Laboratories
13             #
14             # This package is free software and is provided "as is" without express
15             # or implied warranty. It may be used, redistributed and/or modified
16             # under the terms of the Perl Artistic License (see
17             # http://www.perl.com/perl/misc/Artistic.html)
18             #
19              
20             package Net::Flow;
21              
22 1     1   16861 use 5.008008;
  1         4  
  1         31  
23 1     1   4 use strict;
  1         1  
  1         31  
24 1     1   4 use warnings;
  1         7  
  1         55  
25              
26 1     1   491 use Time::HiRes qw(tv_interval gettimeofday);
  1         1191  
  1         3  
27 1     1   145 use Digest::MD5 qw(md5_hex);
  1         2  
  1         60  
28 1     1   623 use Data::Dumper;
  1         9183  
  1         102  
29              
30 1     1   7 use Exporter;
  1         2  
  1         58  
31              
32             our @EXPORT_OK = qw(decode encode);
33             our $VERSION = '1.003';
34              
35 1     1   4 use constant NetFlowv5 => 5;
  1         2  
  1         62  
36 1     1   3 use constant NetFlowv8 => 8;
  1         1  
  1         31  
37 1     1   4 use constant NetFlowv9 => 9;
  1         1  
  1         29  
38 1     1   3 use constant IPFIX => 10;
  1         1  
  1         36  
39              
40 1     1   4 use constant NFWV9_DataTemplateSetId => 0;
  1         2  
  1         32  
41 1     1   3 use constant NFWV9_OptionTemplateSetId => 1;
  1         2  
  1         28  
42              
43 1     1   10 use constant IPFIX_DataTemplateSetId => 2;
  1         1  
  1         34  
44 1     1   4 use constant IPFIX_OptionTemplateSetId => 3;
  1         1  
  1         31  
45              
46 1     1   7 use constant MinDataSetId => 256;
  1         1  
  1         33  
47 1     1   4 use constant VariableLength => 65535;
  1         1  
  1         54  
48 1     1   6 use constant ShortVariableLength => 255;
  1         1  
  1         4462  
49              
50             my %TemplateForNetFlowv5 = (
51             'SetId' => 0,
52             'TemplateId' => 0,
53             'FieldCount' => 20,
54             'Template' => [
55             { 'Length' => 4, 'Id' => 8 }, # sourceIPv4Address
56             { 'Length' => 4, 'Id' => 12 }, # destinationIPv4Address
57             { 'Length' => 4, 'Id' => 15 }, # ipNextHopIPv4Address
58             { 'Length' => 2, 'Id' => 10 }, # ingressInterface
59             { 'Length' => 2, 'Id' => 14 }, # egressInterface
60             { 'Length' => 4, 'Id' => 2 }, # packetDeltaCount
61             { 'Length' => 4, 'Id' => 1 }, # octetDeltaCount
62             { 'Length' => 4, 'Id' => 22 }, # flowStartSysUpTime
63             { 'Length' => 4, 'Id' => 21 }, # flowEndSysUpTime
64             { 'Length' => 2, 'Id' => 7 }, # sourceTransportPort
65             { 'Length' => 2, 'Id' => 11 }, # destinationTransportPort
66             { 'Length' => 1, 'Id' => 210}, # paddingOctets
67             { 'Length' => 1, 'Id' => 6 }, # tcpControlBits
68             { 'Length' => 1, 'Id' => 4 }, # protocolIdentifier
69             { 'Length' => 1, 'Id' => 5 }, # ipClassOfService
70             { 'Length' => 2, 'Id' => 16 }, # bgpSourceAsNumber
71             { 'Length' => 2, 'Id' => 17 }, # bgpDestinationAsNumber
72             { 'Length' => 1, 'Id' => 9 }, # sourceIPv4PrefixLength
73             { 'Length' => 1, 'Id' => 13 }, # destinationIPv4PrefixLength
74             { 'Length' => 2, 'Id' => 210}, # paddingOctets
75             ],
76             );
77              
78             #################### START sub encode() ####################
79             sub encode {
80              
81 0     0 1 0 my ( $InputHeaderRef, $InputTemplateRef, $InputFlowRef, $MaxDatagram ) = @_;
82 0         0 my @Payloads = ();
83 0         0 my @FlowPacks = ();
84 0         0 my %FlowSetPayloads = ();
85 0         0 my $FlowSetHeaderLength = 4;
86 0         0 my @Errors = ();
87 0         0 my $Error = undef;
88              
89             # This is a terrible default, but it was the original behavior.
90 0 0       0 $InputHeaderRef->{TemplateResendSecs} = 0
91             unless defined $InputHeaderRef->{TemplateResendSecs};
92              
93 0   0     0 $InputHeaderRef->{_sysstarttime} ||= [gettimeofday];
94 0 0       0 check_header($InputHeaderRef) unless defined $InputHeaderRef->{_header_len};
95              
96 0         0 my $sendTemplates = 1; # Always is the default
97              
98 0         0 my $template_info;
99              
100             # if TemplateResendSecs is true someone has bothered to define it so
101             # we can do some extra work to see if we really need to send
102             # template info.
103 0 0       0 if ( $InputHeaderRef->{TemplateResendSecs} ) {
104 0         0 my $templates_id = scalar $InputTemplateRef;
105             ##warn "templates_id: $templates_id\n";
106 0   0     0 $InputHeaderRef->{_template_info}->{$templates_id} ||= {};
107 0         0 $template_info = $InputHeaderRef->{_template_info}->{$templates_id};
108 0         0 my $hash = md5_hex( Dumper($InputTemplateRef) );
109             ##warn Dumper($InputTemplateRef) unless defined $template_info->{hash};
110 0   0     0 $template_info->{hash} ||= $hash;
111 0 0       0 if ( $template_info->{hash} ne $hash ) {
112             ##warn "$template_info->{hash} ne $hash", "\n";
113             ##warn Dumper($InputTemplateRef);
114 0         0 $template_info->{hash} = $hash;
115 0         0 delete $template_info->{_template_sent};
116             }
117              
118             # This is a kludge until I come up with something better. Using
119             # the stringified $InputTemplateRef as an ID works, but if someone
120             # is passing in a new ref everytime this will slowly leak memory.
121             #
122             # I have arbitrarily chosen 50 as too many sets of template
123             # information to have. Hopefully this will keep the amount of
124             # looping through the _template_info hash to a minimum and also
125             # prevent memory from leaking endlessly.
126 0 0       0 if ( keys %{ $InputHeaderRef->{_template_info} } > 50 ) {
  0         0  
127 0         0 for my $key ( keys %{ $InputHeaderRef->{_template_info} } ) {
  0         0  
128 0         0 my $sent = $InputHeaderRef->{_template_info}->{$key}->{_template_sent};
129 0 0       0 if ( time - $sent > $InputHeaderRef->{TemplateResendSecs} ) {
130 0         0 delete $InputHeaderRef->{_template_info}->{$key};
131             }
132             }
133             }
134              
135             $sendTemplates = (
136 0 0       0 ( !defined $template_info->{_template_sent} )
137             ? 1
138             : ( ( time - $template_info->{_template_sent} ) >= $InputHeaderRef->{TemplateResendSecs} )
139             );
140             }
141              
142             #
143             # check header reference
144             #
145              
146 0         0 my ($ErrorRef) = &check_header($InputHeaderRef);
147              
148 0 0       0 push( @Errors, @{$ErrorRef} ) if ( defined $ErrorRef );
  0         0  
149              
150 0         0 my @flowRef;
151 0 0       0 if ($sendTemplates) {
152 0         0 push @flowRef, @{$InputTemplateRef};
  0         0  
153 0 0       0 $template_info->{_template_sent} = time if ref $template_info eq 'HASH';
154             }
155 0         0 push @flowRef, @{$InputFlowRef};
  0         0  
156              
157             ##warn scalar localtime ($template_info->{_template_sent}), "\n";
158              
159 0         0 foreach my $FlowRef (@flowRef) {
160 0         0 my $PackRef = undef;
161 0         0 my $ErrorRef = undef;
162 0         0 my $DecodeTemplateRef = undef;
163              
164 0 0       0 unless ( defined $FlowRef->{SetId} ) {
165 0         0 $Error = 'ERROR : NOTHING SETID VALUE';
166 0         0 push( @Errors, $Error );
167 0         0 next;
168             }
169              
170             #
171             # encode flow data
172             #
173              
174 0 0       0 if ( $FlowRef->{SetId} >= MinDataSetId ) {
175              
176             #
177             # searching for particular template
178             #
179              
180 0         0 ( $DecodeTemplateRef, $Error ) = &search_template( $FlowRef->{SetId}, $InputTemplateRef );
181              
182 0 0       0 if ( defined $DecodeTemplateRef ) {
183              
184 0         0 ( $PackRef, $ErrorRef ) = &flow_encode( $FlowRef, $DecodeTemplateRef );
185              
186             } else {
187              
188 0         0 $Error = "ERROR : NO TEMPLATE TEMPLATE ID=$FlowRef->{SetId}";
189 0         0 push( @Errors, $Error );
190              
191             }
192              
193             #
194             # encode template data
195             #
196              
197             } else {
198              
199 0         0 ( $PackRef, $ErrorRef ) = &template_encode( $FlowRef, $InputHeaderRef );
200              
201             }
202              
203 0 0       0 push( @FlowPacks, $PackRef )
204             if defined $PackRef;
205              
206 0 0       0 push( @Errors, @{$ErrorRef} ) if defined $ErrorRef;
  0         0  
207              
208             }
209              
210 0 0       0 unless (@FlowPacks) {
211              
212 0         0 $Error = 'ERROR : NO FLOW DATA';
213 0         0 push( @Errors, $Error );
214 0         0 return ( $InputHeaderRef, \@Payloads, \@Errors );
215              
216             }
217              
218             #
219             # encode NetFlowv9/IPFIX datagram
220             #
221              
222 0         0 my $FlowCount = 0;
223 0         0 my $DataCount = 0;
224 0         0 foreach my $FlowPackRef (@FlowPacks) {
225              
226 0 0       0 unless ( defined $FlowPackRef->{Pack} ) {
227 0         0 warn 'undefined $FlowPackRef->{Pack}', "\n";
228 0         0 next;
229             }
230              
231             #
232             # check datagram size
233             #
234              
235 0         0 my $TotalLength = $InputHeaderRef->{_header_len};
236              
237 0         0 foreach my $SetId ( keys %FlowSetPayloads ) {
238              
239 0         0 $TotalLength += length( $FlowSetPayloads{$SetId} ) + $FlowSetHeaderLength;
240              
241             }
242              
243 0 0       0 if ( ( length( $FlowPackRef->{Pack} ) + $TotalLength ) > $MaxDatagram ) {
244              
245             #
246             # make NetFlow/IPFIX datagram
247             #
248              
249 0         0 push( @Payloads, &datagram_encode( $InputHeaderRef, \%FlowSetPayloads, \$FlowCount, \$DataCount ) );
250              
251 0         0 %FlowSetPayloads = ();
252 0         0 $FlowCount = 0;
253 0         0 $DataCount = 0;
254             }
255              
256 0         0 $FlowSetPayloads{ $FlowPackRef->{SetId} } .= $FlowPackRef->{Pack};
257              
258 0 0       0 $DataCount++ if $FlowPackRef->{SetId} >= MinDataSetId;
259 0         0 $FlowCount++;
260              
261             }
262              
263             # Push a final flow if any.
264 0 0       0 if ( $FlowCount > 0 ) {
265 0         0 push( @Payloads, &datagram_encode( $InputHeaderRef, \%FlowSetPayloads, \$FlowCount, \$DataCount ) );
266              
267             }
268              
269 0         0 return ( $InputHeaderRef, \@Payloads, \@Errors );
270              
271             }
272             #################### END sub encode() ######################
273              
274             #################### START sub check_header() ##############
275              
276             sub check_header {
277 0     0 0 0 my ($InputHeaderRef) = @_;
278              
279 0         0 my @Errors;
280             my $Error;
281              
282 0 0       0 if ( $InputHeaderRef->{VersionNum} == IPFIX ) {
283 0         0 $InputHeaderRef->{_header_len} = 16;
284 0   0     0 $InputHeaderRef->{ObservationDomainId} ||= 0;
285 0   0     0 $InputHeaderRef->{SequenceNum} ||= 0;
286 0   0     0 $InputHeaderRef->{_export_time} = $InputHeaderRef->{UnixSecs} || time;
287             } else {
288 0 0       0 if ( $InputHeaderRef->{VersionNum} != NetFlowv9 ) {
289 0 0       0 if ( !defined $InputHeaderRef->{VersionNum} ) {
290 0         0 $Error = 'WARNING : NO HEADER VERSION NUMBER';
291             } else {
292 0         0 $Error = "WARNING : NO SUPPORT HEADER VERSION NUMBER $InputHeaderRef->{VersionNum}";
293             }
294 0         0 push( @Errors, $Error );
295             }
296 0         0 $InputHeaderRef->{VersionNum} = NetFlowv9;
297              
298 0         0 $InputHeaderRef->{_header_len} = 20;
299 0   0     0 $InputHeaderRef->{SourceId} ||= 0;
300 0   0     0 $InputHeaderRef->{SequenceNum} ||= 0;
301 0   0     0 $InputHeaderRef->{_export_time} = $InputHeaderRef->{UnixSecs} || time;
302 0         0 $InputHeaderRef->{SysUpTime} = int( tv_interval( $InputHeaderRef->{_sysstarttime} ) * 1000 );
303             }
304              
305 0         0 return ( \@Errors );
306             }
307             #################### END sub check_header() ################
308              
309             #################### START sub datagram_encode() ###########
310             sub datagram_encode {
311 0     0 0 0 my ( $HeaderRef, $FlowSetPayloadRef, $FlowCountRef, $DataCountRef ) = @_;
312 0         0 my $Payload = '';
313              
314             #
315             # encode flow set data
316             #
317              
318 0         0 foreach my $SetId ( sort { $a <=> $b } ( keys %{$FlowSetPayloadRef} ) ) {
  0         0  
  0         0  
319              
320             #
321             # make padding part
322             #
323              
324 0         0 my $padding = '';
325              
326 0         0 while ( ( length( $FlowSetPayloadRef->{$SetId} ) + length($padding) ) % 4 != 0 ) {
327 0         0 $padding .= "\0";
328             }
329              
330 0         0 my $set_len = ( length( $FlowSetPayloadRef->{$SetId} ) + length($padding) + 4 );
331              
332             # Pack set header
333 0         0 $Payload .= pack( 'n2', $SetId, $set_len );
334              
335             # Pack set data
336 0         0 $Payload .= $FlowSetPayloadRef->{$SetId};
337              
338             # Pack padding
339 0         0 $Payload .= $padding;
340             }
341              
342 0 0       0 if ( $HeaderRef->{VersionNum} == NetFlowv9 ) {
    0          
343              
344 0   0     0 $HeaderRef->{SysUpTime} ||= 0;
345 0   0     0 $HeaderRef->{_export_time} ||= 0;
346 0   0     0 $HeaderRef->{SequenceNum} ||= 0;
347 0         0 $HeaderRef->{SequenceNum} = ( $HeaderRef->{SequenceNum} + 1 ) % 0xFFFFFFFF;
348 0         0 $HeaderRef->{Count} = $$FlowCountRef;
349              
350 0         0 $Payload = pack( 'n2N4', @{$HeaderRef}{qw{VersionNum Count SysUpTime _export_time SequenceNum SourceId}} ) . $Payload;
  0         0  
351              
352             } elsif ( $HeaderRef->{VersionNum} == IPFIX ) {
353              
354 0         0 $Payload = pack( 'n2N3', $HeaderRef->{VersionNum}, ( length($Payload) + $HeaderRef->{_header_len} ), @{$HeaderRef}{qw{_export_time SequenceNum ObservationDomainId}} ) . $Payload;
  0         0  
355              
356 0         0 $HeaderRef->{SequenceNum} = ( $HeaderRef->{SequenceNum} + $$DataCountRef ) % 0xFFFFFFFF;
357              
358             }
359              
360              
361 0         0 return ( \$Payload );
362              
363             }
364             #################### END sub datagram_encode() #############
365              
366             #################### START sub flow_encode() ###############
367             sub flow_encode {
368 0     0 0 0 my ( $FlowRef, $DecodeTemplateRef ) = @_;
369 0         0 my %FlowData = ();
370 0         0 my @Errors = ();
371 0         0 my $Error = undef;
372 0         0 my %Count = ();
373              
374 0         0 $FlowData{SetId} = $DecodeTemplateRef->{TemplateId};
375              
376 0         0 foreach my $TemplateArrayRef ( @{ $DecodeTemplateRef->{Template} } ) {
  0         0  
377              
378 0         0 my $FlowValue = undef;
379              
380              
381 0   0     0 $Count{ $TemplateArrayRef->{Id} } ||= 0;
382              
383 0 0       0 if ( defined $FlowRef->{ $TemplateArrayRef->{Id} } ) {
384              
385             #
386             # One Template has multiple same Ids.
387             #
388              
389 0 0       0 if ( ref $FlowRef->{ $TemplateArrayRef->{Id} } ) {
390              
391 0         0 $FlowValue = @{ $FlowRef->{ $TemplateArrayRef->{Id} } }[ $Count{ $TemplateArrayRef->{Id} } ];
  0         0  
392              
393             #
394             # Each Id is different than others.
395             #
396              
397             } else {
398              
399 0         0 $FlowValue = $FlowRef->{ $TemplateArrayRef->{Id} };
400              
401             }
402              
403             #
404             # Variable Length Type
405             #
406              
407 0 0       0 if ( $TemplateArrayRef->{Length} == VariableLength ) {
408              
409 0         0 my $Length = length($FlowValue);
410              
411             #
412             # Value Length < 255
413             #
414              
415 0 0       0 if ( $Length < ShortVariableLength ) {
416              
417 0         0 $FlowData{Pack} .= pack( "C A$Length", $Length, $FlowValue );
418              
419             #
420             # Value Length > 255
421             #
422              
423             } else {
424              
425 0         0 $FlowData{Pack} .= pack( "C n A$Length", ShortVariableLength, $Length, $FlowValue );
426              
427             }
428              
429             #
430             # Fixed Length Type
431             #
432              
433             } else {
434              
435 0         0 $FlowData{Pack} .= pack( "A$TemplateArrayRef->{Length}", $FlowValue );
436              
437             }
438              
439              
440             } else {
441             $Data::Dumper::Sortkeys = sub {
442 0     0   0 my $h = shift;
443             return [
444             sort {
445 0 0 0     0 if ( $a =~ /^\d+$/ && $b =~ /^\d+$/ ) {
  0         0  
446 0         0 $a <=> $b;
447             } else {
448 0         0 lc($a) cmp lc($b);
449             }
450             } ( keys %$h )
451             ];
452 0         0 };
453              
454 0         0 $Error = "WARNING : NOT FIELD DATA INFORMATION ELEMENT ID=$TemplateArrayRef->{Id}";
455 0         0 push( @Errors, $Error );
456              
457 0 0       0 if ( $TemplateArrayRef->{Length} == VariableLength ) {
458              
459 0         0 $FlowData{Pack} .= pack( 'C', 0 );
460              
461             } else {
462              
463 0         0 $FlowData{Pack} .= pack("a$TemplateArrayRef->{Length}");
464              
465             }
466              
467             }
468              
469 0         0 $Count{ $TemplateArrayRef->{Id} }++;
470              
471             }
472              
473 0         0 return ( \%FlowData, \@Errors );
474              
475             }
476             #################### END sub flow_encode() #################
477              
478             #################### START sub template_encode() ###########
479             sub template_encode {
480 0     0 0 0 my ( $TemplateRef, $HeaderRef ) = @_;
481 0         0 my %TemplateData = ();
482 0         0 my $ScopeCount = 0;
483 0         0 my @Errors = ();
484 0         0 my $Error = undef;
485              
486             #
487             # check template hash reference
488             #
489              
490 0 0       0 unless ( defined $TemplateRef->{TemplateId} ) {
491 0         0 $Error = 'ERROR : NO TEMPLATE ID';
492 0         0 push( @Errors, $Error );
493             }
494              
495 0 0       0 unless ( defined $TemplateRef->{SetId} ) {
496 0         0 $Error = 'ERROR : NO SET ID';
497 0         0 push( @Errors, $Error );
498             }
499              
500 0 0       0 if ( $HeaderRef->{VersionNum} == NetFlowv9 ) {
    0          
501              
502 0 0 0     0 if ( $TemplateRef->{SetId} != NFWV9_DataTemplateSetId
503             && $TemplateRef->{SetId} != NFWV9_OptionTemplateSetId ) {
504              
505 0         0 $Error = "ERROR : UNMATCH SET ID FOR NETFLOWV9 TEMPLATE=$TemplateRef->{TemplateId}";
506 0         0 push( @Errors, $Error );
507              
508             }
509              
510             } elsif ( $HeaderRef->{VersionNum} == IPFIX ) {
511              
512 0 0 0     0 if ( $TemplateRef->{SetId} != IPFIX_DataTemplateSetId
513             && $TemplateRef->{SetId} != IPFIX_OptionTemplateSetId ) {
514              
515 0         0 $Error = "ERROR : UNMATCH SET ID FOR IPFIX TEMPLATE=$TemplateRef->{TemplateId}";
516 0         0 push( @Errors, $Error );
517              
518             }
519              
520             }
521              
522 0 0       0 return ( \%TemplateData, \@Errors ) if $#Errors >= 0;
523              
524 0         0 $TemplateData{SetId} = $TemplateRef->{SetId};
525              
526 0 0       0 $ScopeCount = $TemplateRef->{ScopeCount}
527             if defined $TemplateRef->{ScopeCount};
528              
529 0 0       0 $TemplateRef->{FieldCount} = $#{ $TemplateRef->{Template} } + 1
  0         0  
530             unless defined $TemplateRef->{FieldCount};
531              
532             #
533             # NetFlow v9 pack data template header
534             #
535              
536 0 0       0 if ( $TemplateRef->{SetId} == NFWV9_DataTemplateSetId ) {
    0          
    0          
    0          
537              
538 0         0 $TemplateData{Pack} = pack( 'n2', @{$TemplateRef}{qw{TemplateId FieldCount}} );
  0         0  
539              
540             #
541             # NetFlow v9 pack option template header
542             #
543              
544             } elsif ( $TemplateRef->{SetId} == NFWV9_OptionTemplateSetId ) {
545              
546 0         0 $TemplateData{Pack} = pack( 'n3', $TemplateRef->{TemplateId}, $ScopeCount * 4, ( $#{ $TemplateRef->{Template} } + 1 - $ScopeCount ) * 4, );
  0         0  
547              
548             #
549             # IPFIX pack data template header
550             #
551              
552             } elsif ( $TemplateRef->{SetId} == IPFIX_DataTemplateSetId ) {
553              
554             #
555             # Template Withdraw
556             #
557              
558 0 0       0 if ( $TemplateRef->{FieldCount} == 0 ) {
559              
560 0         0 $TemplateData{Pack} = pack( 'n2', $TemplateRef->{TemplateId}, 0 );
561              
562             } else {
563              
564 0         0 $TemplateData{Pack} = pack( 'n2', @{$TemplateRef}{qw{TemplateId FieldCount}} );
  0         0  
565              
566             }
567              
568             #
569             # IPFIX pack option template header
570             #
571              
572             } elsif ( $TemplateRef->{SetId} == IPFIX_OptionTemplateSetId ) {
573              
574             #
575             # Template Withdraw
576             #
577              
578 0 0       0 if ( $TemplateRef->{FieldCount} == 0 ) {
579              
580 0         0 $TemplateData{Pack} = pack( 'n2', $TemplateRef->{TemplateId}, 0 );
581              
582             } else {
583              
584 0         0 $TemplateData{Pack} = pack(
585             'n3',
586             $TemplateRef->{TemplateId},
587 0         0 ( $#{ $TemplateRef->{Template} } + 1 ), # -$ScopeCount
588             $ScopeCount,
589             );
590             }
591              
592             }
593              
594             #
595             # pack template
596             #
597              
598 0 0       0 if ( $TemplateRef->{FieldCount} > 0 ) {
599              
600 0         0 foreach my $Ref ( @{ $TemplateRef->{Template} } ) {
  0         0  
601              
602             #
603             # Enterprise Num
604             #
605              
606 0 0       0 if ( $Ref->{Id} =~ /([\d]+)\.([\d]+)/ ) {
607              
608 0         0 $TemplateData{Pack} .= pack( 'n2N', $2 + 0x8000, $Ref->{Length}, $1, );
609              
610             } else {
611              
612 0         0 $TemplateData{Pack} .= pack( 'n2', $Ref->{Id}, $Ref->{Length} );
613              
614             }
615              
616             }
617              
618             }
619              
620 0         0 return ( \%TemplateData, \@Errors );
621              
622             }
623             #################### END sub template_encode() #############
624              
625              
626             #################### START sub decode() ####################
627             sub decode {
628 1     1 1 10 my ( $NetFlowPktRef, $InputTemplateRef ) = @_;
629 1         2 my $NetFlowHeaderRef = undef;
630 1         2 my $FlowSetHeaderRef = undef;
631 1         2 my $TemplateRef = undef;
632 1         1 my @Template = ();
633 1         2 my @Flows = ();
634 1         2 my @Errors = ();
635 1         2 my $Error = undef;
636              
637 1         1 my $OffSet = 0;
638 1         2 my $FlowSetOffSet = 0;
639 1         2 my $FlowCount = 0;
640              
641             #
642             # check packet data
643             #
644              
645 1 50       5 if ( ref($NetFlowPktRef) ne 'SCALAR' ) {
646              
647 0         0 $Error = 'ERROR : NO PACKET DATA';
648 0         0 push( @Errors, $Error );
649              
650 0         0 return ( $NetFlowHeaderRef, \@Template, \@Flows, \@Errors );
651              
652             }
653              
654             #
655             # insert template data
656             #
657              
658 1 50 33     27 if ( defined($InputTemplateRef) || ref($InputTemplateRef) eq 'ARRAY' ) {
    50          
659              
660 0         0 push( @Template, @{$InputTemplateRef} );
  0         0  
661              
662             } elsif ( defined($InputTemplateRef) ) {
663              
664 0         0 $Error = 'WARNING : NOT REF TEMPLATE DATA';
665 0         0 push( @Errors, $Error );
666              
667             }
668              
669             #
670             # header decode
671             #
672              
673 1         5 ( $NetFlowHeaderRef, $Error ) = &header_decode( $NetFlowPktRef, \$OffSet );
674              
675             #
676             # IPFIX decode
677             #
678              
679 1 50       4 if ( $NetFlowHeaderRef->{VersionNum} == IPFIX ) {
    0          
    0          
    0          
680              
681 1         3 while ( $OffSet < $NetFlowHeaderRef->{Length} ) {
682              
683 4         5 my $DecodeTemplateRef = undef;
684 4         2 my $FlowRef = undef;
685 4         4 my $TemplateRef = undef;
686              
687 4 50       9 if ( ( length($$NetFlowPktRef) - $OffSet ) < 4 ) {
688              
689 0 0       0 if ( $FlowCount ne $NetFlowHeaderRef->{Count} ) {
690 0         0 $Error = 'WARNING : UNMATCH FLOW COUNT';
691 0         0 push( @Errors, $Error );
692             }
693              
694 0         0 last;
695             }
696              
697 4         4 $FlowSetOffSet = $OffSet;
698              
699             #
700             # decode flowset
701             #
702              
703 4         8 $FlowSetHeaderRef = &flowset_decode( $NetFlowPktRef, \$OffSet );
704              
705             #
706             # search for template
707             #
708              
709 4 100       11 if ( $FlowSetHeaderRef->{SetId} >= MinDataSetId ) {
710              
711 2         7 ( $DecodeTemplateRef, $Error ) = &search_template( $FlowSetHeaderRef->{SetId}, \@Template );
712              
713 2 50       6 unless ( defined $DecodeTemplateRef ) {
714              
715 0         0 push( @Errors, $Error );
716 0 0       0 $OffSet = $FlowSetHeaderRef->{Length} + $FlowSetOffSet
717             if defined $FlowSetHeaderRef->{Length};
718              
719 0         0 next;
720              
721             }
722              
723             }
724              
725 4         9 while ( $FlowSetHeaderRef->{Length} > ( $OffSet - $FlowSetOffSet ) ) {
726              
727             #
728             # check word alignment
729             #
730              
731 7 100       15 if ( ( $FlowSetHeaderRef->{Length} - ( $OffSet - $FlowSetOffSet ) ) < 4 ) {
732              
733 3         3 $OffSet = $FlowSetHeaderRef->{Length} + $FlowSetOffSet;
734 3         6 last;
735              
736             }
737              
738             #
739             # decode data template or option Template
740             #
741              
742 4 100       6 if ( $FlowSetHeaderRef->{SetId} < MinDataSetId ) {
743              
744 2         6 ( $TemplateRef, $Error ) = &template_decode( $NetFlowPktRef, \$OffSet, $FlowSetHeaderRef, \$NetFlowHeaderRef->{VersionNum} );
745              
746 2 50       3 if ( defined $Error ) {
747              
748 0         0 push( @Errors, $Error );
749 0         0 last;
750              
751             }
752              
753 2         2 $FlowCount++;
754              
755 2 50       3 @Template = grep { $_ if ( $_->{TemplateId} ne $TemplateRef->{TemplateId} ); } @Template;
  1         8  
756              
757 2         6 push( @Template, $TemplateRef );
758              
759             #
760             # decode flow records
761             #
762              
763             } else {
764              
765 2         6 ( $FlowRef, $Error ) = &flow_decode( $NetFlowPktRef, \$OffSet, $DecodeTemplateRef );
766              
767 2 50       4 if ( defined $Error ) {
768 0         0 push( @Errors, $Error );
769 0         0 last;
770             }
771              
772 2         3 $FlowCount++;
773 2         4 push( @Flows, $FlowRef );
774              
775             }
776              
777             }
778              
779             }
780              
781             #
782             # NetFlow version 9 decode
783             #
784              
785             } elsif ( $NetFlowHeaderRef->{VersionNum} == NetFlowv9 ) {
786              
787 0         0 while ( $FlowCount < $NetFlowHeaderRef->{Count} ) {
788 0         0 my $DecodeTemplateRef = undef;
789 0         0 my $FlowRef = undef;
790 0         0 my $TemplateRef = undef;
791              
792 0 0       0 if ( ( length($$NetFlowPktRef) - $OffSet ) < 4 ) {
793              
794 0 0       0 if ( $FlowCount ne $NetFlowHeaderRef->{Count} ) {
795 0         0 $Error = 'WARNING : UNMATCH FLOW COUNT';
796 0         0 push( @Errors, $Error );
797             }
798              
799 0         0 last;
800             }
801              
802 0         0 $FlowSetOffSet = $OffSet;
803              
804             #
805             # decode flowset
806             #
807              
808 0         0 $FlowSetHeaderRef = &flowset_decode( $NetFlowPktRef, \$OffSet );
809              
810             #
811             # search for template
812             #
813              
814 0 0       0 if ( $FlowSetHeaderRef->{SetId} >= MinDataSetId ) {
815              
816 0         0 ( $DecodeTemplateRef, $Error ) = &search_template( $FlowSetHeaderRef->{SetId}, \@Template );
817              
818 0 0       0 unless ( defined $DecodeTemplateRef ) {
819              
820 0         0 push( @Errors, $Error );
821 0 0       0 $OffSet = $FlowSetHeaderRef->{Length} + $FlowSetOffSet
822             if defined $FlowSetHeaderRef->{Length};
823              
824 0         0 next;
825              
826             }
827              
828             }
829              
830 0         0 while ( $FlowSetHeaderRef->{Length} > ( $OffSet - $FlowSetOffSet ) ) {
831              
832             #
833             # check word alignment
834             #
835              
836 0 0       0 if ( ( $FlowSetHeaderRef->{Length} - ( $OffSet - $FlowSetOffSet ) ) < 4 ) {
837              
838 0         0 $OffSet = $FlowSetHeaderRef->{Length} + $FlowSetOffSet;
839 0         0 last;
840              
841             }
842              
843             #
844             # decode data template or option Template
845             #
846              
847 0 0       0 if ( $FlowSetHeaderRef->{SetId} < MinDataSetId ) {
848              
849 0         0 ( $TemplateRef, $Error ) = &template_decode( $NetFlowPktRef, \$OffSet, $FlowSetHeaderRef, \$NetFlowHeaderRef->{VersionNum} );
850              
851 0 0       0 if ( defined $Error ) {
852              
853 0         0 push( @Errors, $Error );
854 0         0 last;
855              
856             }
857              
858 0         0 $FlowCount++;
859              
860 0 0       0 @Template = grep { $_ if ( $_->{TemplateId} ne $TemplateRef->{TemplateId} ); } @Template;
  0         0  
861              
862 0         0 push( @Template, $TemplateRef );
863              
864             #
865             # decode flow records
866             #
867              
868             } else {
869              
870 0         0 ( $FlowRef, $Error ) = &flow_decode( $NetFlowPktRef, \$OffSet, $DecodeTemplateRef );
871              
872 0 0       0 if ( defined $Error ) {
873 0         0 push( @Errors, $Error );
874 0         0 last;
875             }
876              
877 0         0 $FlowCount++;
878 0         0 push( @Flows, $FlowRef );
879              
880             }
881              
882             }
883              
884             }
885              
886             #
887             # NetFlow version 5 Decode
888             #
889              
890             } elsif ( $NetFlowHeaderRef->{VersionNum} == NetFlowv5 ) {
891              
892 0         0 while ( $FlowCount < $NetFlowHeaderRef->{Count} ) {
893              
894 0         0 my $FlowRef = undef;
895              
896 0         0 ( $FlowRef, $Error ) = &flow_decode( $NetFlowPktRef, \$OffSet, \%TemplateForNetFlowv5 );
897              
898 0         0 $FlowRef->{SetId} = undef;
899              
900 0 0       0 if ( defined $Error ) {
901              
902 0         0 push( @Errors, $Error );
903 0         0 last;
904              
905             }
906              
907 0         0 $FlowCount++;
908 0         0 push( @Flows, $FlowRef );
909              
910             }
911              
912             #
913             # NetFlow version 8 Decode
914             #
915              
916             } elsif ( $NetFlowHeaderRef->{VersionNum} == NetFlowv8 ) {
917              
918 0         0 $Error = 'ERROR : NOT SUPPORT NETFLOW VER.8';
919 0         0 push( @Errors, $Error );
920              
921             } else {
922              
923 0         0 $Error = 'ERROR : NOT NETFLOW DATA';
924 0         0 push( @Errors, $Error );
925              
926             }
927              
928 1         5 return ( $NetFlowHeaderRef, \@Template, \@Flows, \@Errors );
929              
930             }
931             #################### END sub decode() ######################
932              
933             #################### START sub search_template() ###########
934             sub search_template {
935 2     2 0 3 my ( $TemplateId, $TemplatesArrayRef ) = @_;
936 2         2 my $DecodeTemplateRef = undef;
937 2         1 my $Error = undef;
938              
939 2 100       3 ( $DecodeTemplateRef, undef ) = grep { $_ if $_->{TemplateId} eq $TemplateId; } @{$TemplatesArrayRef};
  4         11  
  2         2  
940              
941             #
942             # nothing template for flow data
943             #
944              
945 2 50       5 unless ( defined $DecodeTemplateRef ) {
946 0         0 $Error = "WARNING : NOT FOUND TEMPLATE=$TemplateId";
947             }
948              
949 2         9 return ( $DecodeTemplateRef, $Error );
950              
951             }
952              
953             #################### START sub header_decode() #############
954             sub header_decode {
955 1     1 0 2 my ( $NetFlowPktRef, $OffSetRef ) = @_;
956 1         2 my %NetFlowHeader = ();
957 1         2 my $error = undef;
958              
959             #
960             # Extract Version
961             #
962              
963 1         4 ( $NetFlowHeader{VersionNum} ) = unpack( 'n', $$NetFlowPktRef );
964              
965 1         3 $$OffSetRef += 2;
966              
967 1 50       3 if ( $NetFlowHeader{VersionNum} == IPFIX ) {
    0          
    0          
    0          
968              
969 1         8 ( @NetFlowHeader{qw{Length UnixSecs SequenceNum ObservationDomainId}} ) = unpack( "x$$OffSetRef nN3", $$NetFlowPktRef );
970              
971 1         3 $$OffSetRef += 2 + 4 * 3;
972              
973             } elsif ( $NetFlowHeader{VersionNum} == NetFlowv9 ) {
974              
975 0         0 ( @NetFlowHeader{qw{Count SysUpTime UnixSecs SequenceNum SourceId}} ) = unpack( "x$$OffSetRef nN4", $$NetFlowPktRef );
976              
977 0         0 $$OffSetRef += 2 + 4 * 4;
978              
979             } elsif ( $NetFlowHeader{VersionNum} == NetFlowv8 ) {
980             } elsif ( $NetFlowHeader{VersionNum} == NetFlowv5 ) {
981              
982 0         0 my $Sampling = undef;
983              
984             ( @NetFlowHeader{
985 0         0 qw{Count SysUpTime UnixSecs UnixNsecs FlowSequenceNum
986             EngineType EngineId}
987             },
988             $Sampling
989             )
990             = unpack(
991             "x$$OffSetRef nN4C2n",
992             $$NetFlowPktRef
993             );
994              
995 0         0 $NetFlowHeader{SamplingMode} = $Sampling >> 14;
996 0         0 $NetFlowHeader{SamplingInterval} = $Sampling & 0x3FFF;
997              
998 0         0 $$OffSetRef += 2 * 1 + 4 * 4 + 1 * 2 + 2 * 1;
999              
1000             }
1001              
1002 1         3 return ( \%NetFlowHeader, $error );
1003              
1004             }
1005             #################### END sub header_decode() ###############
1006              
1007             #################### START sub flowset_decode() ############
1008             sub flowset_decode {
1009 4     4 0 6 my ( $NetFlowPktRef, $OffSetRef ) = @_;
1010 4         5 my %FlowSetHeader = ();
1011 4         5 my @errors = ();
1012 4         3 my $error = undef;
1013              
1014 4         13 ( $FlowSetHeader{SetId}, $FlowSetHeader{Length} ) = unpack( "x$$OffSetRef n2", $$NetFlowPktRef );
1015              
1016 4         4 $$OffSetRef += 2 * 2;
1017              
1018 4         14 return ( \%FlowSetHeader );
1019              
1020             }
1021             #################### END sub flowset_decode() ##############
1022              
1023             #################### START sub template_decode() ###########
1024             sub template_decode {
1025 2     2 0 4 my ( $NetFlowPktRef, $OffSetRef, $FlowSetHeaderRef, $VerNumRef ) = @_;
1026 2         3 my %Template = ();
1027 2         2 my $error = undef;
1028              
1029 2         4 $Template{SetId} = $FlowSetHeaderRef->{SetId};
1030              
1031             #
1032             # decode data template for NetFlow v9 or IPFIX
1033             #
1034              
1035 2 100 66     24 if ( $FlowSetHeaderRef->{SetId} == NFWV9_DataTemplateSetId
    50          
    0          
1036             || $FlowSetHeaderRef->{SetId} == IPFIX_DataTemplateSetId ) {
1037              
1038 1         5 ( @Template{qw{TemplateId FieldCount}} ) = unpack( "x$$OffSetRef n2", $$NetFlowPktRef );
1039              
1040 1         2 $$OffSetRef += 2 * 2;
1041              
1042             #
1043             # decode option template for IPFIX
1044             #
1045              
1046             } elsif ( $FlowSetHeaderRef->{SetId} == IPFIX_OptionTemplateSetId ) {
1047              
1048 1         7 ( @Template{qw{TemplateId FieldCount}} ) = unpack( "x$$OffSetRef n2", $$NetFlowPktRef );
1049              
1050 1         3 $$OffSetRef += 2 * 2;
1051              
1052             #
1053             # template withdraw check
1054             #
1055              
1056 1 50       4 if ( $Template{FieldCount} != 0 ) {
1057              
1058 1         6 ( $Template{ScopeCount} ) = unpack( "x$$OffSetRef n", $$NetFlowPktRef );
1059 1         3 $$OffSetRef += 2 * 1;
1060              
1061             }
1062              
1063             #
1064             # decode option template for NetFlow v9
1065             #
1066              
1067             } elsif ( $FlowSetHeaderRef->{SetId} == NFWV9_OptionTemplateSetId ) {
1068              
1069 0         0 ( @Template{qw{TemplateId OptionScopeLength OptionLength}} ) = unpack( "x$$OffSetRef n3", $$NetFlowPktRef );
1070              
1071 0         0 $$OffSetRef += 2 * 3;
1072              
1073 0         0 $Template{FieldCount} = int( ( $Template{OptionScopeLength} + $Template{OptionLength} ) / 4 );
1074              
1075 0         0 $Template{ScopeCount} = int( ( $Template{OptionScopeLength} ) / 4 );
1076              
1077             }
1078              
1079 2 50       6 return ( undef, 'ERROR: No fieldcount' ) if ( !defined( $Template{FieldCount} ) );
1080              
1081 2         6 for ( my $n = 0; $n < $Template{FieldCount}; $n++ ) {
1082              
1083 11 50       14 if ( $FlowSetHeaderRef->{SetId} <= IPFIX_OptionTemplateSetId ) {
1084              
1085 11         17 ( @{$Template{Template}->[$n]}{qw{Id Length}} ) = unpack( "x$$OffSetRef n2", $$NetFlowPktRef );
  11         27  
1086 11         10 $$OffSetRef += 2 * 2;
1087              
1088             #
1089             # check enterprise number
1090             #
1091              
1092 11 50       15 if ( $$VerNumRef >= 10 ) {
1093              
1094 11 100       22 if ( $Template{Template}->[$n]->{Id} & 0x8000 ) {
1095              
1096 10         9 $Template{Template}->[$n]->{Id} -= 0x8000;
1097              
1098 10         20 ( $Template{Template}->[$n]->{EnterpriseNum} ) = unpack( "x$$OffSetRef N", $$NetFlowPktRef );
1099              
1100             # We have a PEN add it to the Id.
1101 10         20 $Template{Template}->[$n]->{Id} =
1102 10         8 join('.', @{$Template{Template}->[$n]}{qw{EnterpriseNum Id}});
1103              
1104 10         20 $$OffSetRef += 4;
1105              
1106             }
1107              
1108             }
1109              
1110             }
1111              
1112             }
1113              
1114 2         6 return ( \%Template, $error );
1115              
1116             }
1117             #################### END sub template_decode() #############
1118              
1119             #################### START sub flow_decode() ###############
1120             sub flow_decode {
1121 2     2 0 1 my ( $NetFlowPktRef, $OffSetRef, $TemplateRef ) = @_;
1122 2         3 my %Flow = ();
1123 2         6 my $error = undef;
1124 2         3 my $Length = undef;
1125              
1126 2 50       5 if ( defined $TemplateRef->{TemplateId} ) {
1127              
1128 2         3 $Flow{SetId} = $TemplateRef->{TemplateId};
1129              
1130             } else {
1131              
1132 0         0 $error = 'ERROR: NOT FOUND TEMPLATE ID';
1133              
1134             }
1135              
1136 2         3 foreach my $ref ( @{ $TemplateRef->{Template} } ) {
  2         5  
1137              
1138             #
1139             # Variable Length Type
1140             #
1141              
1142 11 100       16 if ( $ref->{Length} == VariableLength ) {
1143              
1144 7         12 $Length = unpack( "x$$OffSetRef C", $$NetFlowPktRef );
1145              
1146 7         3 $$OffSetRef++;
1147              
1148 7 50       13 if ( $Length == 255 ) {
1149              
1150 0         0 $Length = unpack( "x$$OffSetRef n", $$NetFlowPktRef );
1151              
1152 0         0 $$OffSetRef += 2;
1153              
1154             }
1155              
1156             #
1157             # Fixed Length Type
1158             #
1159              
1160             } else {
1161              
1162 4         5 $Length = $ref->{Length};
1163              
1164             }
1165              
1166             #
1167             # One Template has multiple same Ids.
1168             #
1169              
1170 11 50       14 if ( defined $Flow{ $ref->{Id} } ) {
1171              
1172 0         0 my $Value = unpack( "x$$OffSetRef a$Length", $$NetFlowPktRef );
1173              
1174 0 0       0 $Flow{ $ref->{Id} } = [ $Flow{ $ref->{Id} } ] unless ref $Flow{ $ref->{Id} };
1175              
1176 0         0 push( @{ $Flow{ $ref->{Id} } }, $Value );
  0         0  
1177              
1178             #
1179             # Each Id is different than others.
1180             #
1181              
1182             } else {
1183              
1184 11         30 $Flow{ $ref->{Id} } = unpack( "x$$OffSetRef a$Length", $$NetFlowPktRef );
1185              
1186             }
1187              
1188 11         10 $$OffSetRef += $Length;
1189              
1190             }
1191              
1192 2         5 return ( \%Flow, $error );
1193              
1194             }
1195             #################### END sub flow_decode() #################
1196              
1197             1;
1198              
1199             __END__