File Coverage

blib/lib/Net/Flow.pm
Criterion Covered Total %
statement 162 451 35.9
branch 33 190 17.3
condition 3 44 6.8
subroutine 24 30 80.0
pod 2 11 18.1
total 224 726 30.8


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   25386 use 5.008008;
  1         3  
  1         35  
23 1     1   6 use strict;
  1         1  
  1         30  
24 1     1   5 use warnings;
  1         6  
  1         37  
25              
26 1     1   936 use Time::HiRes qw(tv_interval gettimeofday);
  1         1892  
  1         5  
27 1     1   195 use Digest::MD5 qw(md5_hex);
  1         1  
  1         67  
28 1     1   1030 use Data::Dumper;
  1         10655  
  1         84  
29              
30 1     1   7 use Exporter;
  1         2  
  1         64  
31              
32             our @EXPORT_OK = qw(decode encode);
33             our $VERSION = '1.002';
34              
35 1     1   4 use constant NetFlowv5 => 5;
  1         1  
  1         53  
36 1     1   4 use constant NetFlowv8 => 8;
  1         2  
  1         36  
37 1     1   4 use constant NetFlowv9 => 9;
  1         2  
  1         35  
38 1     1   5 use constant IPFIX => 10;
  1         1  
  1         42  
39              
40 1     1   4 use constant NFWV9_DataTemplateSetId => 0;
  1         2  
  1         36  
41 1     1   4 use constant NFWV9_OptionTemplateSetId => 1;
  1         2  
  1         33  
42              
43 1     1   11 use constant IPFIX_DataTemplateSetId => 2;
  1         2  
  1         39  
44 1     1   5 use constant IPFIX_OptionTemplateSetId => 3;
  1         1  
  1         35  
45              
46 1     1   10 use constant MinDataSetId => 256;
  1         2  
  1         40  
47 1     1   5 use constant VariableLength => 65535;
  1         1  
  1         39  
48 1     1   4 use constant ShortVariableLength => 255;
  1         1  
  1         5088  
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 0         0 warn Dumper ($TemplateArrayRef);
454 0         0 warn Dumper ($FlowRef);
455              
456 0         0 $Error = "WARNING : NOT FIELD DATA INFORMATION ELEMENT ID=$TemplateArrayRef->{Id}";
457 0         0 push( @Errors, $Error );
458              
459 0 0       0 if ( $TemplateArrayRef->{Length} == VariableLength ) {
460              
461 0         0 $FlowData{Pack} .= pack( 'C', 0 );
462              
463             } else {
464              
465 0         0 $FlowData{Pack} .= pack("a$TemplateArrayRef->{Length}");
466              
467             }
468              
469             }
470              
471 0         0 $Count{ $TemplateArrayRef->{Id} }++;
472              
473             }
474              
475 0         0 return ( \%FlowData, \@Errors );
476              
477             }
478             #################### END sub flow_encode() #################
479              
480             #################### START sub template_encode() ###########
481             sub template_encode {
482 0     0 0 0 my ( $TemplateRef, $HeaderRef ) = @_;
483 0         0 my %TemplateData = ();
484 0         0 my $ScopeCount = 0;
485 0         0 my @Errors = ();
486 0         0 my $Error = undef;
487              
488             #
489             # check template hash reference
490             #
491              
492 0 0       0 unless ( defined $TemplateRef->{TemplateId} ) {
493 0         0 $Error = 'ERROR : NO TEMPLATE ID';
494 0         0 push( @Errors, $Error );
495             }
496              
497 0 0       0 unless ( defined $TemplateRef->{SetId} ) {
498 0         0 $Error = 'ERROR : NO SET ID';
499 0         0 push( @Errors, $Error );
500             }
501              
502 0 0       0 if ( $HeaderRef->{VersionNum} == NetFlowv9 ) {
    0          
503              
504 0 0 0     0 if ( $TemplateRef->{SetId} != NFWV9_DataTemplateSetId
505             && $TemplateRef->{SetId} != NFWV9_OptionTemplateSetId ) {
506              
507 0         0 $Error = "ERROR : UNMATCH SET ID FOR NETFLOWV9 TEMPLATE=$TemplateRef->{TemplateId}";
508 0         0 push( @Errors, $Error );
509              
510             }
511              
512             } elsif ( $HeaderRef->{VersionNum} == IPFIX ) {
513              
514 0 0 0     0 if ( $TemplateRef->{SetId} != IPFIX_DataTemplateSetId
515             && $TemplateRef->{SetId} != IPFIX_OptionTemplateSetId ) {
516              
517 0         0 $Error = "ERROR : UNMATCH SET ID FOR IPFIX TEMPLATE=$TemplateRef->{TemplateId}";
518 0         0 push( @Errors, $Error );
519              
520             }
521              
522             }
523              
524 0 0       0 return ( \%TemplateData, \@Errors ) if $#Errors >= 0;
525              
526 0         0 $TemplateData{SetId} = $TemplateRef->{SetId};
527              
528 0 0       0 $ScopeCount = $TemplateRef->{ScopeCount}
529             if defined $TemplateRef->{ScopeCount};
530              
531 0 0       0 $TemplateRef->{FieldCount} = $#{ $TemplateRef->{Template} } + 1
  0         0  
532             unless defined $TemplateRef->{FieldCount};
533              
534             #
535             # NetFlow v9 pack data template header
536             #
537              
538 0 0       0 if ( $TemplateRef->{SetId} == NFWV9_DataTemplateSetId ) {
    0          
    0          
    0          
539              
540 0         0 $TemplateData{Pack} = pack( 'n2', @{$TemplateRef}{qw{TemplateId FieldCount}} );
  0         0  
541              
542             #
543             # NetFlow v9 pack option template header
544             #
545              
546             } elsif ( $TemplateRef->{SetId} == NFWV9_OptionTemplateSetId ) {
547              
548 0         0 $TemplateData{Pack} = pack( 'n3', $TemplateRef->{TemplateId}, $ScopeCount * 4, ( $#{ $TemplateRef->{Template} } + 1 - $ScopeCount ) * 4, );
  0         0  
549              
550             #
551             # IPFIX pack data template header
552             #
553              
554             } elsif ( $TemplateRef->{SetId} == IPFIX_DataTemplateSetId ) {
555              
556             #
557             # Template Withdraw
558             #
559              
560 0 0       0 if ( $TemplateRef->{FieldCount} == 0 ) {
561              
562 0         0 $TemplateData{Pack} = pack( 'n2', $TemplateRef->{TemplateId}, 0 );
563              
564             } else {
565              
566 0         0 $TemplateData{Pack} = pack( 'n2', @{$TemplateRef}{qw{TemplateId FieldCount}} );
  0         0  
567              
568             }
569              
570             #
571             # IPFIX pack option template header
572             #
573              
574             } elsif ( $TemplateRef->{SetId} == IPFIX_OptionTemplateSetId ) {
575              
576             #
577             # Template Withdraw
578             #
579              
580 0 0       0 if ( $TemplateRef->{FieldCount} == 0 ) {
581              
582 0         0 $TemplateData{Pack} = pack( 'n2', $TemplateRef->{TemplateId}, 0 );
583              
584             } else {
585              
586 0         0 $TemplateData{Pack} = pack(
587             'n3',
588             $TemplateRef->{TemplateId},
589 0         0 ( $#{ $TemplateRef->{Template} } + 1 ), # -$ScopeCount
590             $ScopeCount,
591             );
592             }
593              
594             }
595              
596             #
597             # pack template
598             #
599              
600 0 0       0 if ( $TemplateRef->{FieldCount} > 0 ) {
601              
602 0         0 foreach my $Ref ( @{ $TemplateRef->{Template} } ) {
  0         0  
603              
604             #
605             # Enterprise Num
606             #
607              
608 0 0       0 if ( $Ref->{Id} =~ /([\d]+)\.([\d]+)/ ) {
609              
610 0         0 $TemplateData{Pack} .= pack( 'n2N', $2 + 0x8000, $Ref->{Length}, $1, );
611              
612             } else {
613              
614 0         0 $TemplateData{Pack} .= pack( 'n2', $Ref->{Id}, $Ref->{Length} );
615              
616             }
617              
618             }
619              
620             }
621              
622 0         0 return ( \%TemplateData, \@Errors );
623              
624             }
625             #################### END sub template_encode() #############
626              
627              
628             #################### START sub decode() ####################
629             sub decode {
630 1     1 1 12 my ( $NetFlowPktRef, $InputTemplateRef ) = @_;
631 1         1 my $NetFlowHeaderRef = undef;
632 1         2 my $FlowSetHeaderRef = undef;
633 1         2 my $TemplateRef = undef;
634 1         2 my @Template = ();
635 1         2 my @Flows = ();
636 1         1 my @Errors = ();
637 1         2 my $Error = undef;
638              
639 1         2 my $OffSet = 0;
640 1         2 my $FlowSetOffSet = 0;
641 1         2 my $FlowCount = 0;
642              
643             #
644             # check packet data
645             #
646              
647 1 50       4 if ( ref($NetFlowPktRef) ne 'SCALAR' ) {
648              
649 0         0 $Error = 'ERROR : NO PACKET DATA';
650 0         0 push( @Errors, $Error );
651              
652 0         0 return ( $NetFlowHeaderRef, \@Template, \@Flows, \@Errors );
653              
654             }
655              
656             #
657             # insert template data
658             #
659              
660 1 50 33     28 if ( defined($InputTemplateRef) || ref($InputTemplateRef) eq 'ARRAY' ) {
    50          
661              
662 0         0 push( @Template, @{$InputTemplateRef} );
  0         0  
663              
664             } elsif ( defined($InputTemplateRef) ) {
665              
666 0         0 $Error = 'WARNING : NOT REF TEMPLATE DATA';
667 0         0 push( @Errors, $Error );
668              
669             }
670              
671             #
672             # header decode
673             #
674              
675 1         5 ( $NetFlowHeaderRef, $Error ) = &header_decode( $NetFlowPktRef, \$OffSet );
676              
677             #
678             # IPFIX decode
679             #
680              
681 1 50       3 if ( $NetFlowHeaderRef->{VersionNum} == IPFIX ) {
    0          
    0          
    0          
682              
683 1         4 while ( $OffSet < $NetFlowHeaderRef->{Length} ) {
684              
685 4         5 my $DecodeTemplateRef = undef;
686 4         5 my $FlowRef = undef;
687 4         4 my $TemplateRef = undef;
688              
689 4 50       11 if ( ( length($$NetFlowPktRef) - $OffSet ) < 4 ) {
690              
691 0 0       0 if ( $FlowCount ne $NetFlowHeaderRef->{Count} ) {
692 0         0 $Error = 'WARNING : UNMATCH FLOW COUNT';
693 0         0 push( @Errors, $Error );
694             }
695              
696 0         0 last;
697             }
698              
699 4         5 $FlowSetOffSet = $OffSet;
700              
701             #
702             # decode flowset
703             #
704              
705 4         8 $FlowSetHeaderRef = &flowset_decode( $NetFlowPktRef, \$OffSet );
706              
707             #
708             # search for template
709             #
710              
711 4 100       12 if ( $FlowSetHeaderRef->{SetId} >= MinDataSetId ) {
712              
713 2         7 ( $DecodeTemplateRef, $Error ) = &search_template( $FlowSetHeaderRef->{SetId}, \@Template );
714              
715 2 50       11 unless ( defined $DecodeTemplateRef ) {
716              
717 0         0 push( @Errors, $Error );
718 0 0       0 $OffSet = $FlowSetHeaderRef->{Length} + $FlowSetOffSet
719             if defined $FlowSetHeaderRef->{Length};
720              
721 0         0 next;
722              
723             }
724              
725             }
726              
727 4         10 while ( $FlowSetHeaderRef->{Length} > ( $OffSet - $FlowSetOffSet ) ) {
728              
729             #
730             # check word alignment
731             #
732              
733 7 100       16 if ( ( $FlowSetHeaderRef->{Length} - ( $OffSet - $FlowSetOffSet ) ) < 4 ) {
734              
735 3         4 $OffSet = $FlowSetHeaderRef->{Length} + $FlowSetOffSet;
736 3         9 last;
737              
738             }
739              
740             #
741             # decode data template or option Template
742             #
743              
744 4 100       9 if ( $FlowSetHeaderRef->{SetId} < MinDataSetId ) {
745              
746 2         7 ( $TemplateRef, $Error ) = &template_decode( $NetFlowPktRef, \$OffSet, $FlowSetHeaderRef, \$NetFlowHeaderRef->{VersionNum} );
747              
748 2 50       7 if ( defined $Error ) {
749              
750 0         0 push( @Errors, $Error );
751 0         0 last;
752              
753             }
754              
755 2         3 $FlowCount++;
756              
757 2 50       4 @Template = grep { $_ if ( $_->{TemplateId} ne $TemplateRef->{TemplateId} ); } @Template;
  1         6  
758              
759 2         8 push( @Template, $TemplateRef );
760              
761             #
762             # decode flow records
763             #
764              
765             } else {
766              
767 2         4 ( $FlowRef, $Error ) = &flow_decode( $NetFlowPktRef, \$OffSet, $DecodeTemplateRef );
768              
769 2 50       5 if ( defined $Error ) {
770 0         0 push( @Errors, $Error );
771 0         0 last;
772             }
773              
774 2         2 $FlowCount++;
775 2         5 push( @Flows, $FlowRef );
776              
777             }
778              
779             }
780              
781             }
782              
783             #
784             # NetFlow version 9 decode
785             #
786              
787             } elsif ( $NetFlowHeaderRef->{VersionNum} == NetFlowv9 ) {
788              
789 0         0 while ( $FlowCount < $NetFlowHeaderRef->{Count} ) {
790 0         0 my $DecodeTemplateRef = undef;
791 0         0 my $FlowRef = undef;
792 0         0 my $TemplateRef = undef;
793              
794 0 0       0 if ( ( length($$NetFlowPktRef) - $OffSet ) < 4 ) {
795              
796 0 0       0 if ( $FlowCount ne $NetFlowHeaderRef->{Count} ) {
797 0         0 $Error = 'WARNING : UNMATCH FLOW COUNT';
798 0         0 push( @Errors, $Error );
799             }
800              
801 0         0 last;
802             }
803              
804 0         0 $FlowSetOffSet = $OffSet;
805              
806             #
807             # decode flowset
808             #
809              
810 0         0 $FlowSetHeaderRef = &flowset_decode( $NetFlowPktRef, \$OffSet );
811              
812             #
813             # search for template
814             #
815              
816 0 0       0 if ( $FlowSetHeaderRef->{SetId} >= MinDataSetId ) {
817              
818 0         0 ( $DecodeTemplateRef, $Error ) = &search_template( $FlowSetHeaderRef->{SetId}, \@Template );
819              
820 0 0       0 unless ( defined $DecodeTemplateRef ) {
821              
822 0         0 push( @Errors, $Error );
823 0 0       0 $OffSet = $FlowSetHeaderRef->{Length} + $FlowSetOffSet
824             if defined $FlowSetHeaderRef->{Length};
825              
826 0         0 next;
827              
828             }
829              
830             }
831              
832 0         0 while ( $FlowSetHeaderRef->{Length} > ( $OffSet - $FlowSetOffSet ) ) {
833              
834             #
835             # check word alignment
836             #
837              
838 0 0       0 if ( ( $FlowSetHeaderRef->{Length} - ( $OffSet - $FlowSetOffSet ) ) < 4 ) {
839              
840 0         0 $OffSet = $FlowSetHeaderRef->{Length} + $FlowSetOffSet;
841 0         0 last;
842              
843             }
844              
845             #
846             # decode data template or option Template
847             #
848              
849 0 0       0 if ( $FlowSetHeaderRef->{SetId} < MinDataSetId ) {
850              
851 0         0 ( $TemplateRef, $Error ) = &template_decode( $NetFlowPktRef, \$OffSet, $FlowSetHeaderRef, \$NetFlowHeaderRef->{VersionNum} );
852              
853 0 0       0 if ( defined $Error ) {
854              
855 0         0 push( @Errors, $Error );
856 0         0 last;
857              
858             }
859              
860 0         0 $FlowCount++;
861              
862 0 0       0 @Template = grep { $_ if ( $_->{TemplateId} ne $TemplateRef->{TemplateId} ); } @Template;
  0         0  
863              
864 0         0 push( @Template, $TemplateRef );
865              
866             #
867             # decode flow records
868             #
869              
870             } else {
871              
872 0         0 ( $FlowRef, $Error ) = &flow_decode( $NetFlowPktRef, \$OffSet, $DecodeTemplateRef );
873              
874 0 0       0 if ( defined $Error ) {
875 0         0 push( @Errors, $Error );
876 0         0 last;
877             }
878              
879 0         0 $FlowCount++;
880 0         0 push( @Flows, $FlowRef );
881              
882             }
883              
884             }
885              
886             }
887              
888             #
889             # NetFlow version 5 Decode
890             #
891              
892             } elsif ( $NetFlowHeaderRef->{VersionNum} == NetFlowv5 ) {
893              
894 0         0 while ( $FlowCount < $NetFlowHeaderRef->{Count} ) {
895              
896 0         0 my $FlowRef = undef;
897              
898 0         0 ( $FlowRef, $Error ) = &flow_decode( $NetFlowPktRef, \$OffSet, \%TemplateForNetFlowv5 );
899              
900 0         0 $FlowRef->{SetId} = undef;
901              
902 0 0       0 if ( defined $Error ) {
903              
904 0         0 push( @Errors, $Error );
905 0         0 last;
906              
907             }
908              
909 0         0 $FlowCount++;
910 0         0 push( @Flows, $FlowRef );
911              
912             }
913              
914             #
915             # NetFlow version 8 Decode
916             #
917              
918             } elsif ( $NetFlowHeaderRef->{VersionNum} == NetFlowv8 ) {
919              
920 0         0 $Error = 'ERROR : NOT SUPPORT NETFLOW VER.8';
921 0         0 push( @Errors, $Error );
922              
923             } else {
924              
925 0         0 $Error = 'ERROR : NOT NETFLOW DATA';
926 0         0 push( @Errors, $Error );
927              
928             }
929              
930 1         5 return ( $NetFlowHeaderRef, \@Template, \@Flows, \@Errors );
931              
932             }
933             #################### END sub decode() ######################
934              
935             #################### START sub search_template() ###########
936             sub search_template {
937 2     2 0 3 my ( $TemplateId, $TemplatesArrayRef ) = @_;
938 2         2 my $DecodeTemplateRef = undef;
939 2         2 my $Error = undef;
940              
941 2 100       3 ( $DecodeTemplateRef, undef ) = grep { $_ if $_->{TemplateId} eq $TemplateId; } @{$TemplatesArrayRef};
  4         15  
  2         4  
942              
943             #
944             # nothing template for flow data
945             #
946              
947 2 50       5 unless ( defined $DecodeTemplateRef ) {
948 0         0 $Error = "WARNING : NOT FOUND TEMPLATE=$TemplateId";
949             }
950              
951 2         4 return ( $DecodeTemplateRef, $Error );
952              
953             }
954              
955             #################### START sub header_decode() #############
956             sub header_decode {
957 1     1 0 3 my ( $NetFlowPktRef, $OffSetRef ) = @_;
958 1         2 my %NetFlowHeader = ();
959 1         2 my $error = undef;
960              
961             #
962             # Extract Version
963             #
964              
965 1         5 ( $NetFlowHeader{VersionNum} ) = unpack( 'n', $$NetFlowPktRef );
966              
967 1         2 $$OffSetRef += 2;
968              
969 1 50       3 if ( $NetFlowHeader{VersionNum} == IPFIX ) {
    0          
    0          
    0          
970              
971 1         8 ( @NetFlowHeader{qw{Length UnixSecs SequenceNum ObservationDomainId}} ) = unpack( "x$$OffSetRef nN3", $$NetFlowPktRef );
972              
973 1         2 $$OffSetRef += 2 + 4 * 3;
974              
975             } elsif ( $NetFlowHeader{VersionNum} == NetFlowv9 ) {
976              
977 0         0 ( @NetFlowHeader{qw{Count SysUpTime UnixSecs SequenceNum SourceId}} ) = unpack( "x$$OffSetRef nN4", $$NetFlowPktRef );
978              
979 0         0 $$OffSetRef += 2 + 4 * 4;
980              
981             } elsif ( $NetFlowHeader{VersionNum} == NetFlowv8 ) {
982             } elsif ( $NetFlowHeader{VersionNum} == NetFlowv5 ) {
983              
984 0         0 my $Sampling = undef;
985              
986             ( @NetFlowHeader{
987 0         0 qw{Count SysUpTime UnixSecs UnixNsecs FlowSequenceNum
988             EngineType EngineId}
989             },
990             $Sampling
991             )
992             = unpack(
993             "x$$OffSetRef nN4C2n",
994             $$NetFlowPktRef
995             );
996              
997 0         0 $NetFlowHeader{SamplingMode} = $Sampling >> 14;
998 0         0 $NetFlowHeader{SamplingInterval} = $Sampling & 0x3FFF;
999              
1000 0         0 $$OffSetRef += 2 * 1 + 4 * 4 + 1 * 2 + 2 * 1;
1001              
1002             }
1003              
1004 1         3 return ( \%NetFlowHeader, $error );
1005              
1006             }
1007             #################### END sub header_decode() ###############
1008              
1009             #################### START sub flowset_decode() ############
1010             sub flowset_decode {
1011 4     4 0 5 my ( $NetFlowPktRef, $OffSetRef ) = @_;
1012 4         6 my %FlowSetHeader = ();
1013 4         5 my @errors = ();
1014 4         5 my $error = undef;
1015              
1016 4         13 ( $FlowSetHeader{SetId}, $FlowSetHeader{Length} ) = unpack( "x$$OffSetRef n2", $$NetFlowPktRef );
1017              
1018 4         5 $$OffSetRef += 2 * 2;
1019              
1020 4         17 return ( \%FlowSetHeader );
1021              
1022             }
1023             #################### END sub flowset_decode() ##############
1024              
1025             #################### START sub template_decode() ###########
1026             sub template_decode {
1027 2     2 0 4 my ( $NetFlowPktRef, $OffSetRef, $FlowSetHeaderRef, $VerNumRef ) = @_;
1028 2         3 my %Template = ();
1029 2         2 my $error = undef;
1030              
1031 2         4 $Template{SetId} = $FlowSetHeaderRef->{SetId};
1032              
1033             #
1034             # decode data template for NetFlow v9 or IPFIX
1035             #
1036              
1037 2 100 66     24 if ( $FlowSetHeaderRef->{SetId} == NFWV9_DataTemplateSetId
    50          
    0          
1038             || $FlowSetHeaderRef->{SetId} == IPFIX_DataTemplateSetId ) {
1039              
1040 1         5 ( @Template{qw{TemplateId FieldCount}} ) = unpack( "x$$OffSetRef n2", $$NetFlowPktRef );
1041              
1042 1         2 $$OffSetRef += 2 * 2;
1043              
1044             #
1045             # decode option template for IPFIX
1046             #
1047              
1048             } elsif ( $FlowSetHeaderRef->{SetId} == IPFIX_OptionTemplateSetId ) {
1049              
1050 1         4 ( @Template{qw{TemplateId FieldCount}} ) = unpack( "x$$OffSetRef n2", $$NetFlowPktRef );
1051              
1052 1         2 $$OffSetRef += 2 * 2;
1053              
1054             #
1055             # template withdraw check
1056             #
1057              
1058 1 50       3 if ( $Template{FieldCount} != 0 ) {
1059              
1060 1         4 ( $Template{ScopeCount} ) = unpack( "x$$OffSetRef n", $$NetFlowPktRef );
1061 1         2 $$OffSetRef += 2 * 1;
1062              
1063             }
1064              
1065             #
1066             # decode option template for NetFlow v9
1067             #
1068              
1069             } elsif ( $FlowSetHeaderRef->{SetId} == NFWV9_OptionTemplateSetId ) {
1070              
1071 0         0 ( @Template{qw{TemplateId OptionScopeLength OptionLength}} ) = unpack( "x$$OffSetRef n3", $$NetFlowPktRef );
1072              
1073 0         0 $$OffSetRef += 2 * 3;
1074              
1075 0         0 $Template{FieldCount} = int( ( $Template{OptionScopeLength} + $Template{OptionLength} ) / 4 );
1076              
1077 0         0 $Template{ScopeCount} = int( ( $Template{OptionScopeLength} ) / 4 );
1078              
1079             }
1080              
1081 2 50       6 return ( undef, 'ERROR: No fieldcount' ) if ( !defined( $Template{FieldCount} ) );
1082              
1083 2         6 for ( my $n = 0; $n < $Template{FieldCount}; $n++ ) {
1084              
1085 11 50       17 if ( $FlowSetHeaderRef->{SetId} <= IPFIX_OptionTemplateSetId ) {
1086              
1087 11         20 ( @{$Template{Template}->[$n]}{qw{Id Length}} ) = unpack( "x$$OffSetRef n2", $$NetFlowPktRef );
  11         39  
1088 11         16 $$OffSetRef += 2 * 2;
1089              
1090             #
1091             # check enterprise number
1092             #
1093              
1094 11 50       19 if ( $$VerNumRef >= 10 ) {
1095              
1096 11 100       26 if ( $Template{Template}->[$n]->{Id} & 0x8000 ) {
1097              
1098 10         13 $Template{Template}->[$n]->{Id} -= 0x8000;
1099              
1100 10         25 ( $Template{Template}->[$n]->{EnterpriseNum} ) = unpack( "x$$OffSetRef N", $$NetFlowPktRef );
1101              
1102             # We have a PEN add it to the Id.
1103 10         28 $Template{Template}->[$n]->{Id} =
1104 10         11 join('.', @{$Template{Template}->[$n]}{qw{EnterpriseNum Id}});
1105              
1106 10         26 $$OffSetRef += 4;
1107              
1108             }
1109              
1110             }
1111              
1112             }
1113              
1114             }
1115              
1116 2         5 return ( \%Template, $error );
1117              
1118             }
1119             #################### END sub template_decode() #############
1120              
1121             #################### START sub flow_decode() ###############
1122             sub flow_decode {
1123 2     2 0 4 my ( $NetFlowPktRef, $OffSetRef, $TemplateRef ) = @_;
1124 2         3 my %Flow = ();
1125 2         7 my $error = undef;
1126 2         3 my $Length = undef;
1127              
1128 2 50       5 if ( defined $TemplateRef->{TemplateId} ) {
1129              
1130 2         5 $Flow{SetId} = $TemplateRef->{TemplateId};
1131              
1132             } else {
1133              
1134 0         0 $error = 'ERROR: NOT FOUND TEMPLATE ID';
1135              
1136             }
1137              
1138 2         3 foreach my $ref ( @{ $TemplateRef->{Template} } ) {
  2         5  
1139              
1140             #
1141             # Variable Length Type
1142             #
1143              
1144 11 100       18 if ( $ref->{Length} == VariableLength ) {
1145              
1146 7         12 $Length = unpack( "x$$OffSetRef C", $$NetFlowPktRef );
1147              
1148 7         10 $$OffSetRef++;
1149              
1150 7 50       11 if ( $Length == 255 ) {
1151              
1152 0         0 $Length = unpack( "x$$OffSetRef n", $$NetFlowPktRef );
1153              
1154 0         0 $$OffSetRef += 2;
1155              
1156             }
1157              
1158             #
1159             # Fixed Length Type
1160             #
1161              
1162             } else {
1163              
1164 4         6 $Length = $ref->{Length};
1165              
1166             }
1167              
1168             #
1169             # One Template has multiple same Ids.
1170             #
1171              
1172 11 50       20 if ( defined $Flow{ $ref->{Id} } ) {
1173              
1174 0         0 my $Value = unpack( "x$$OffSetRef a$Length", $$NetFlowPktRef );
1175              
1176 0 0       0 $Flow{ $ref->{Id} } = [ $Flow{ $ref->{Id} } ] unless ref $Flow{ $ref->{Id} };
1177              
1178 0         0 push( @{ $Flow{ $ref->{Id} } }, $Value );
  0         0  
1179              
1180             #
1181             # Each Id is different than others.
1182             #
1183              
1184             } else {
1185              
1186 11         40 $Flow{ $ref->{Id} } = unpack( "x$$OffSetRef a$Length", $$NetFlowPktRef );
1187              
1188             }
1189              
1190 11         15 $$OffSetRef += $Length;
1191              
1192             }
1193              
1194 2         5 return ( \%Flow, $error );
1195              
1196             }
1197             #################### END sub flow_decode() #################
1198              
1199             1;
1200              
1201             __END__