File Coverage

blib/lib/BGPmon/Translator/XFB2BGPdump.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package BGPmon::Translator::XFB2BGPdump;
2             our $VERSION = '2.0';
3              
4 2     2   160608 use 5.14.0;
  2         10  
  2         114  
5 2     2   13 use strict;
  2         3  
  2         69  
6 2     2   10 use warnings;
  2         4  
  2         63  
7             #use XML::LibXML;
8 2     2   14268 use BGPmon::Translator::XFB2PerlHash;
  0            
  0            
9              
10             use Data::Dumper;
11              
12             require Exporter;
13             our $AUTOLOAD;
14             our @ISA = qw(Exporter);
15             our @EXPORT_OK = qw(translate_message translate_msg get_error_code
16             get_error_message get_error_msg);
17              
18             # Private variables
19             our $initialized = 0;
20             our $base_str;
21              
22             # Variables for libxml use.
23             our $xml_schema;
24             our $parser;
25             our $context;
26              
27             # Variables for error checking and reporting.
28             my %error_code;
29             my %error_msg;
30              
31             # Error messages
32             use constant NO_ERROR_CODE => 0;
33             use constant NO_ERROR_MESSAGE => 'No error.';
34             use constant NO_MSG_ERROR => 701;
35             use constant NO_MSG_MESSAGE =>
36             'XML to BGPdump translator did not get a xml message';
37             use constant PARSER_CREATE_FAIL_CODE => 702;
38             use constant PARSER_CREATE_FAIL_MESSAGE =>
39             'Error creating parser in XML to BGPdump translator';
40             use constant INCOMPLETE_MSG_CODE => 703;
41             use constant INCOMPLETE_MSG_MESSAGE =>
42             'XML to BGPdump translator did not receive a complete XML message.';
43             use constant MSG_PARSE_CODE => 704;
44             use constant MSG_PARSE_MESSAGE => 'Error parsing XML message.';
45             use constant NOT_UPDATE_OR_TABLE_MSG_CODE => 705;
46             use constant NOT_UPDATE_OR_TABLE_MSG_MESSAGE =>
47             'Received message is not an UPDATE message.';
48             use constant NO_TIMESTAMP_CODE => 706;
49             use constant NO_TIMESTAMP_MESSAGE =>
50             'Received message does not have a timestamp.';
51             use constant NO_PEER_ORIGIN_AS_CODE => 707;
52             use constant NO_PEER_ORIGIN_AS_MESSAGE =>
53             'Received message does not have a peer origin AS.';
54             use constant NO_AS_PATH_CODE => 711;
55             use constant NO_AS_PATH_MESSAGE =>
56             'Received message that does not have an AS_PATH';
57             use constant NO_PEER_ADDRESS_CODE => 708;
58             use constant NO_PEER_ADDRESS_MESSAGE =>
59             'Received message does not have a peer address.';
60             use constant BAD_NLRI_AFI_SAFI_CODE => 709;
61             use constant BAD_NLRI_AFI_SAFI_MESSAGE =>
62             'Bad AFI and SAFI values in NLRI section. Should be IPV4/UNICAST.';
63             use constant BAD_WITHDRAWN_AFI_SAFI_CODE => 710;
64             use constant BAD_WITHDRAWN_AFI_SAFI_MESSAGE =>
65             'Bad AFI and SAFI values in WITHDRAWN section. Should be IPV4/UNICAST.';
66             use constant ARGUMENT_ERROR_CODE => 797;
67             use constant ARGUMENT_ERROR_MESSAGE => 'Invalid number of arguments.';
68             use constant INVALID_FUNCTION_SPECIFIED_CODE => 798;
69             use constant INVALID_FUNCTION_SPECIFIED_MESSAGE =>
70             'Invalid function name specified.';
71             use constant UNKNOWN_ERROR_CODE => 799;
72             use constant UNKNOWN_ERROR_MSG => 'Unknown error occurred.';
73              
74             $error_code{'translate_message'} = NO_ERROR_CODE;
75             $error_msg{'translate_message'} = NO_ERROR_MESSAGE;
76              
77             =head1 NAME
78              
79             BGPmon::Translator::XFB2BGPdump - Converts an XML message into an array of
80             BGPdump formatted messages.
81              
82             =head1 SYNOPSIS
83              
84             This module takes a XML message as input and outputs a string in
85             libbgpdump format.
86              
87             use BGPmon::Translator::XFB2BGPdump;
88              
89             my %bgpdump_strs = translate_msg ($xml_message);
90              
91             foreach my $safi (sort{$a <= $b} keys %bgpdump_strs) {
92              
93             print "Printing bgpdump lines for prefixes with safi value $safi\n";
94              
95             my @safi_lines = @{$bgpdump_strs{$safi}};
96            
97             foreach my $bgpdump_line (@safi_lines) {
98            
99             print "$bgpdump_line\n";
100            
101             }
102              
103             }
104              
105              
106             =head1 EXPORT
107              
108             translate_message
109             translate_msg
110             get_error_code
111             get_error_message
112             get_error_msg
113              
114             =head1 SUBROUTINES/METHODS
115              
116             =head2 translate_message
117              
118             This function accepts exactly one XML message and returns a hash of arrays,
119             indexed by SAFI values. Each array contains bgpdump formatted strings for the
120             SAFI value.
121              
122             Inputs:
123             1. xmlmessage : Exactly one XML message.
124              
125             Outputs:
126             1. Returns a hash of arrays, indexed by SAFI values. Each array contains
127             bgpdump formatted strings for the SAFI value.
128              
129             =cut
130              
131             sub translate_message {
132             my $xml_msg = shift;
133              
134             # Output hash.
135             #my %output;
136             my $output = [];
137              
138             # Set the error code to NO_ERROR initially.
139             $error_code{'translate_message'} = NO_ERROR_CODE;
140             $error_msg{'translate_message'} = NO_ERROR_MESSAGE;
141              
142             # Get XML message from arguments
143             unless (defined($xml_msg)) {
144             $error_code{'translate_message'} = NO_MSG_ERROR;
145             $error_msg{'translate_message'} = NO_MSG_MESSAGE;
146             #return %output;
147             return undef;
148             }
149              
150             # Check if we have a complete XML message
151             if ($xml_msg !~ //) {
152             $error_code{'translate_message'} = INCOMPLETE_MSG_CODE;
153             $error_msg{'translate_message'} = INCOMPLETE_MSG_MESSAGE;
154             #return %output;
155             return undef;
156             }
157              
158             # Parse XML message and check if valid.
159             my $doc = BGPmon::Translator::XFB2PerlHash::translate_msg($xml_msg);
160             if(BGPmon::Translator::XFB2PerlHash::get_error_code('translate_msg') != 0){
161             $error_code{'translate_message'} = MSG_PARSE_CODE;
162             $error_msg{'translate_message'} = MSG_PARSE_MESSAGE;
163             #return %output;
164             return undef;
165             }
166             # TODO: add validation
167              
168              
169             # Start parsing out message elements.
170              
171             # Check if message is from a live stream or a table dump
172             my $collection = BGPmon::Translator::XFB2PerlHash::get_content(
173             "/BGP_MONITOR_MESSAGE/COLLECTION/content");
174              
175             if (!defined($collection) or
176             ($collection ne "LIVE" and $collection ne "TABLE_DUMP")) {
177             $error_code{'translate_message'} = NOT_UPDATE_OR_TABLE_MSG_CODE;
178             $error_msg{'translate_message'} = NOT_UPDATE_OR_TABLE_MSG_MESSAGE;
179             #return %output;
180             return undef;
181             }
182              
183             # Get the timestamp.
184             my $ts = BGPmon::Translator::XFB2PerlHash::get_content(
185             "/BGP_MONITOR_MESSAGE/OBSERVED_TIME/TIMESTAMP/content");
186             if (!defined($ts)) {
187             $error_code{'translate_message'} = NO_TIMESTAMP_CODE;
188             $error_msg{'translate_message'} = NO_TIMESTAMP_MESSAGE;
189             #return %output;
190             return undef;
191             }
192              
193             # Set the base string. This is the beginning of each output string.
194             $base_str = "";
195             if($collection eq "LIVE"){
196             $base_str = "BGP4MP|$ts";
197             } elsif ($collection eq "TABLE_DUMP"){
198             $base_str = "TABLE_DUMP2|$ts";
199             }
200              
201             # Get the peer origin AS.
202             my $src_as = BGPmon::Translator::XFB2PerlHash::get_content(
203             "/BGP_MONITOR_MESSAGE/SOURCE/ASN2/content");
204             $src_as = BGPmon::Translator::XFB2PerlHash::get_content(
205             "/BGP_MONITOR_MESSAGE/SOURCE/ASN4/content") if(!defined($src_as));
206             unless (defined($src_as)) {
207             $error_code{'translate_message'} = NO_PEER_ORIGIN_AS_CODE;
208             $error_msg{'translate_message'} = NO_PEER_ORIGIN_AS_MESSAGE;
209             #return %output;
210             return undef;
211             }
212              
213             # Get the peer address.
214             my $src_addr = BGPmon::Translator::XFB2PerlHash::get_content(
215             "/BGP_MONITOR_MESSAGE/SOURCE/ADDRESS/content");
216             unless ($src_addr) {
217             $error_code{'translate_message'} = NO_PEER_ADDRESS_CODE;
218             $error_msg{'translate_message'} = NO_PEER_ADDRESS_MESSAGE;
219             #return %output;
220             return undef;
221             }
222              
223             # Get AS_PATH
224             my $as_path = "";
225             my $ashash = BGPmon::Translator::XFB2PerlHash::get_content(
226             "/BGP_MONITOR_MESSAGE/bgp:UPDATE/bgp:AS_PATH/bgp:AS_SEQUENCE/bgp:ASN4/");
227             $ashash = BGPmon::Translator::XFB2PerlHash::get_content(
228             "/BGP_MONITOR_MESSAGE/bgp:UPDATE/bgp:AS_PATH/bgp:AS_SEQUENCE/bgp:ASN2/")
229             if not defined $ashash;
230             if(defined($ashash)){
231             if(ref($ashash) eq "HASH"){
232             $as_path = join(" ", $as_path, $ashash->{'content'});
233             }
234             elsif(ref($ashash) eq "ARRAY"){
235             my @as_arr = @$ashash;
236             foreach(@as_arr){
237             $as_path = join(" ", $as_path, $_->{'content'});
238             }
239             }
240             }
241             # Remove trailing whitespace from AS PATH.
242             $as_path =~ s/^\s+//;
243              
244             # Get IPv4 announced list.
245             my $pref_arr = BGPmon::Translator::XFB2PerlHash::get_content(
246             '/BGP_MONITOR_MESSAGE/bgp:UPDATE/bgp:NLRI/');
247             if(defined($pref_arr)){
248              
249             my @pr = ();
250             if(ref($pref_arr) eq "HASH"){
251             push(@pr, $pref_arr);
252             }
253             elsif(ref($pref_arr) eq "ARRAY"){
254             @pr = (@pr, @$pref_arr);
255             }
256              
257              
258             foreach my $prefix (@pr) {
259             # Get prefix and afi
260             my $addr = $prefix->{'content'};
261             my $afi = $prefix->{'afi'};
262              
263             # In the NLRI section, AFI should be IPV4 always.
264             if ($afi ne '1') {
265             $error_code{'translate_message'} = BAD_NLRI_AFI_SAFI_CODE;
266             $error_msg{'translate_message'} = BAD_NLRI_AFI_SAFI_MESSAGE;
267             return undef;
268             }
269             write_out_line(as_path => $as_path,
270             pref => $addr,
271             peer_ip => $src_addr,
272             type => "A",
273             src_as => $src_as,
274             #op => \%output);
275             op => $output);
276             }
277             }
278              
279             # Get IPv4 withdrawn list.
280             my $pref_arr_w = BGPmon::Translator::XFB2PerlHash::get_content(
281             '/BGP_MONITOR_MESSAGE/bgp:UPDATE/bgp:WITHDRAW/');
282             if(defined($pref_arr_w)){
283              
284              
285             my @pr = ();
286             if(ref($pref_arr_w) eq "HASH"){
287             push(@pr, $pref_arr_w);
288             }
289             elsif(ref($pref_arr_w) eq "ARRAY"){
290             @pr = (@pr, @$pref_arr_w);
291             }
292              
293             foreach my $prefix (@pr) {
294             # Get prefix, afi and safi
295             my $addr = $prefix->{'content'};
296             my $afi = $prefix->{'afi'};
297              
298             # In the WITHDRAWN section, AFI should be IPV4 always.
299             if ($afi ne '1') {
300             $error_code{'translate_message'} = BAD_WITHDRAWN_AFI_SAFI_CODE;
301             $error_msg{'translate_message'} = BAD_WITHDRAWN_AFI_SAFI_MESSAGE;
302             next;
303             }
304             write_out_line(pref => $addr,
305             peer_ip => $src_addr,
306             type => "W",
307             src_as => $src_as,
308             op => $output);
309             }
310             }
311              
312             # Get IPv6 announced list.
313             my $pref_arr_six = BGPmon::Translator::XFB2PerlHash::get_content(
314             '/BGP_MONITOR_MESSAGE/bgp:UPDATE/bgp:MP_REACH_NLRI/bgp:MP_NLRI/');
315             if(defined($pref_arr_six)){
316              
317             my @pr = ();
318             if(ref($pref_arr_six) eq "HASH"){
319             push(@pr, $pref_arr_six);
320             }
321             elsif(ref($pref_arr_six) eq "ARRAY"){
322             @pr = (@pr, @$pref_arr_six);
323             }
324              
325             foreach my $prefix (@pr) {
326             # Get prefix, afi
327             my $addr = $prefix->{'content'};
328             my $afi = $prefix->{'afi'};
329              
330             write_out_line(as_path => $as_path,
331             pref => $addr,
332             peer_ip => $src_addr,
333             type => "A",
334             src_as => $src_as,
335             #op => \%output);
336             op => $output);
337             }
338             }
339              
340             # Get IPv6 withdrawn list.
341             my $pref_arr_sixw = BGPmon::Translator::XFB2PerlHash::get_content(
342             '/BGP_MONITOR_MESSAGE/bgp:UPDATE/bgp:MP_UNREACH_NLRI/bgp:MP_NLRI/');
343             if(defined($pref_arr_sixw)){
344              
345             my @pr = ();
346             if(ref($pref_arr_sixw) eq "HASH"){
347             push(@pr, $pref_arr_sixw);
348             }
349             elsif(ref($pref_arr_sixw) eq "ARRAY"){
350             @pr = (@pr, @$pref_arr_sixw);
351             }
352              
353             foreach my $prefix (@pr) {
354             # Get prefix, afi
355             my $addr = $prefix->{'content'};
356             my $afi = $prefix->{'afi'};
357              
358             write_out_line(pref => $addr,
359             peer_ip => $src_addr,
360             type => "W",
361             src_as => $src_as,
362             #op => \%output);
363             op => $output);
364             }
365             }
366              
367             # Return the output hash.
368             #return %output;
369             return $output;
370             }
371              
372             =head2 translate_msg
373              
374             Shorthand call for translate_message.
375              
376             =cut
377             sub translate_msg {
378             my $msg = shift;
379             return translate_message($msg);
380             }
381              
382             =head2 get_error_code
383              
384             Get the error code for a given function
385             Input : the name of the function whose error code we should report
386             Output: the function's error code
387             or ARGUMENT_ERROR if the user did not supply a function
388             or INVALID_FUNCTION_SPECIFIED if the user provided an invalid function
389             Usage: my $err_code = get_error_code("connect_archive");
390              
391             =cut
392             sub get_error_code {
393             my $function = shift;
394             unless (defined $function) {
395             return ARGUMENT_ERROR_CODE;
396             }
397              
398             return $error_code{$function} if (defined $error_code{$function});
399             return INVALID_FUNCTION_SPECIFIED_CODE;
400             }
401              
402             =head2 get_error_message {
403              
404             Get the error message of a given function
405             Input : the name of the function whose error message we should report
406             Output: the function's error message
407             or ARGUMENT_ERROR if the user did not supply a function
408             or INVALID_FUNCTION_SPECIFIED if the user provided an invalid function
409             Usage: my $err_msg = get_error_message("read_xml_message");
410              
411             =cut
412             sub get_error_message {
413             my $function = shift;
414             unless (defined $function) {
415             return ARGUMENT_ERROR_MESSAGE;
416             }
417              
418             return $error_msg{$function} if (defined $error_msg{$function});
419             return INVALID_FUNCTION_SPECIFIED_MESSAGE;
420             }
421              
422             =head2 get_error_msg
423              
424             Shorthand call for get_error_message
425              
426             =cut
427              
428             sub get_error_msg{
429             my $fname = shift;
430             return get_error_message($fname);
431             }
432              
433             # Private functions.
434              
435             ## Write an output line to the appropriate output array
436             sub write_out_line {
437             my %args = @_;
438              
439             my $as_path = "";
440             if ($args{as_path}) {
441             $as_path = $args{as_path};
442             }
443              
444             my $safi = 1;
445             # if ($args{safi}) {
446             # $safi = $args{safi};
447             # }
448              
449             my $type = $args{type};
450             my $pref = $args{pref};
451             my $peer_ip = $args{peer_ip};
452             my $src_as = $args{src_as};
453             my $op = $args{op};
454              
455             # Check if msg is UNICAST. If it is,
456             # push to output array in hash with key = 1
457             my $line = join("|", $base_str, $type, $peer_ip, $src_as, $pref, $as_path);
458              
459             # Remove trailing '|' at the end of withdrawn messages.
460             if ($type eq "W") {
461             chop($line);
462             }
463              
464             #push(@{$op->{$safi}}, $line);
465             push(@$op, $line);
466             }
467              
468              
469              
470             =head1 BUGS
471              
472             Please report any bugs or feature requests to
473             C, or through the web interface at
474             L.
475              
476              
477             =head1 SUPPORT
478              
479             You can find documentation for this module with the perldoc command.
480              
481             perldoc BGPmon::Client
482              
483             =cut
484             =head1 LICENSE AND COPYRIGHT
485             Copyright (c) 2012 Colorado State University
486              
487             Permission is hereby granted, free of charge, to any person
488             obtaining a copy of this software and associated documentation
489             files (the "Software"), to deal in the Software without
490             restriction, including without limitation the rights to use,
491             copy, modify, merge, publish, distribute, sublicense, and/or
492             sell copies of the Software, and to permit persons to whom
493             the Software is furnished to do so, subject to the following
494             conditions:
495              
496             The above copyright notice and this permission notice shall be
497             included in all copies or substantial portions of the Software.
498              
499             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
500             EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
501             OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
502             NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
503             HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
504             WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
505             FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
506             OTHER DEALINGS IN THE SOFTWARE.
507              
508              
509             File: XFB2BGPdump.pm
510            
511             Authors: M. Lawrence Weikum, Kaustubh Gadkari, Dan Massey, Cathie Olschanowsky
512            
513             Date: 13 October 2013
514             =cut
515             1;