File Coverage

blib/lib/Net/BGP/Update.pm
Criterion Covered Total %
statement 307 414 74.1
branch 67 128 52.3
condition 22 34 64.7
subroutine 44 53 83.0
pod 0 17 0.0
total 440 646 68.1


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package Net::BGP::Update;
4 4     4   7784 use bytes;
  4         9  
  4         25  
5              
6 4     4   131 use strict;
  4         8  
  4         106  
7 4         305 use vars qw(
8             $VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS
9             @BGP_PATH_ATTR_FLAGS
10 4     4   19 );
  4         6  
11              
12             ## Inheritance and Versioning ##
13              
14 4     4   934 use Net::BGP::NLRI qw( :origin );
  4         10  
  4         777  
15              
16             @ISA = qw( Exporter Net::BGP::NLRI );
17             $VERSION = '0.18';
18              
19             ## Module Imports ##
20              
21 4     4   37 use Carp;
  4         9  
  4         269  
22 4     4   26 use IO::Socket;
  4         8  
  4         34  
23 4     4   2520 use Net::BGP::Notification qw( :errors );
  4         8  
  4         19834  
24              
25             ## General Definitions ##
26              
27 30     30 0 95 sub TRUE { 1 }
28 0     0 0 0 sub FALSE { 0 }
29              
30             ## BGP Path Attribute Type Enumerations ##
31              
32 30     30 0 95 sub BGP_PATH_ATTR_ORIGIN { 1 }
33 35     35 0 70 sub BGP_PATH_ATTR_AS_PATH { 2 }
34 29     29 0 137 sub BGP_PATH_ATTR_NEXT_HOP { 3 }
35 4     4 0 11 sub BGP_PATH_ATTR_MULTI_EXIT_DISC { 4 }
36 4     4 0 9 sub BGP_PATH_ATTR_LOCAL_PREF { 5 }
37 0     0 0 0 sub BGP_PATH_ATTR_ATOMIC_AGGREGATE { 6 }
38 0     0 0 0 sub BGP_PATH_ATTR_AGGREGATOR { 7 }
39 4     4 0 12 sub BGP_PATH_ATTR_COMMUNITIES { 8 }
40 3     3 0 7 sub BGP_PATH_ATTR_AS4_PATH { 17 }
41 0     0 0 0 sub BGP_PATH_ATTR_AS4_AGGREGATOR { 18 }
42              
43             ## BGP Path Attribute Flag Octets ##
44              
45             # This is the expected bits to be set in the flags section.
46             # Note that the PARTIAL is ignored where the flags indicate
47             # OPTIONAL + TRANSITIVE, because this can be set to 1 when
48             # passing through a router that doesn't understand the
49             # meaning of the optional attribute.
50             @BGP_PATH_ATTR_FLAGS = (
51             0x00, ## TODO: change to undef after warnings enabled
52             0x40,
53             0x40,
54             0x40,
55             0x80,
56             0x40,
57             0x40,
58             0xC0,
59             0xC0,
60             0x00, ## TODO: change to undef after warnings enabled
61             0x00, ## TODO: change to undef after warnings enabled
62             0x00, ## TODO: change to undef after warnings enabled
63             0x00, ## TODO: change to undef after warnings enabled
64             0x00, ## TODO: change to undef after warnings enabled
65             0x00, ## TODO: change to undef after warnings enabled
66             0x00, ## TODO: change to undef after warnings enabled
67             0x00, ## TODO: change to undef after warnings enabled
68             0xC0, # AS4_PATH
69             0xC0, # AS4_AGGREGATOR
70             );
71              
72             ## RFC 4271, sec 4.3
73             our $BGP_PATH_ATTR_FLAG_OPTIONAL = 0x80;
74             our $BGP_PATH_ATTR_FLAG_TRANSITIVE = 0x40;
75             our $BGP_PATH_ATTR_FLAG_PARTIAL = 0x20;
76             our $BGP_PATH_ATTR_FLAG_EXTLEN = 0x10;
77             our $BGP_PATH_ATTR_FLAG_RESERVED = 0x0F;
78              
79             ## Per RFC 4271, sec 5.
80             ##
81             our @_BGP_MANDATORY_ATTRS = ( BGP_PATH_ATTR_ORIGIN,
82             BGP_PATH_ATTR_AS_PATH,
83             BGP_PATH_ATTR_NEXT_HOP );
84              
85             ## Export Tag Definitions ##
86              
87             @EXPORT = ();
88             @EXPORT_OK = ();
89             %EXPORT_TAGS = (
90             ALL => [ @EXPORT, @EXPORT_OK ]
91             );
92              
93             ## Public Methods ##
94              
95             sub new
96             {
97 19     19 0 1133 my $proto = shift;
98 19   33     97 my $class = ref $proto || $proto;
99              
100 19 100       67 if (ref $_[0] eq 'Net::BGP::NLRI')
101             { # Construct from NLRI
102 1 50       4 $proto = shift unless ref $proto;
103 1         4 my $this = $proto->clone;
104 1         2 bless($this,$class);
105 1         5 $this->nlri(shift);
106 1         4 $this->withdrawn(shift);
107 1         3 return $this;
108             };
109              
110 18         70 my ($arg, $value);
111 18         0 my @super_arg;
112 18         0 my %this_arg;
113 18         51 $this_arg{_withdrawn} = [];
114 18         34 $this_arg{_nlri} = [];
115              
116 18         68 while ( defined($arg = shift()) ) {
117 3         7 $value = shift();
118              
119 3 100       14 if ( $arg =~ /nlri/i ) {
    100          
120 1         3 $this_arg{_nlri} = $value;
121             }
122             elsif ( $arg =~ /withdraw/i ) {
123 1         3 $this_arg{_withdrawn} = $value;
124             }
125             else {
126 1         4 push(@super_arg,$arg,$value);
127             }
128             }
129              
130 18         241 my $this = $class->SUPER::new(@super_arg);
131              
132 18         58 @{$this}{keys %this_arg} = values(%this_arg);
  18         225  
133              
134 18         36 bless($this, $class);
135              
136 18         53 return ( $this );
137             }
138              
139             sub clone
140             {
141 5     5 0 633 my $proto = shift;
142 5   66     16 my $class = ref $proto || $proto;
143 5 100       11 $proto = shift unless ref $proto;
144              
145 5         18 my $clone = $class->SUPER::clone($proto);
146              
147 5         8 foreach my $key (qw(_nlri _withdrawn ))
148             {
149 10         15 $clone->{$key} = [ @{$proto->{$key}} ];
  10         26  
150             }
151              
152 5         15 return ( bless($clone, $class) );
153             }
154              
155             sub nlri
156             {
157 7     7 0 341 my $this = shift();
158              
159 7 100       19 $this->{_nlri} = @_ ? shift() : $this->{_nlri};
160 7         38 return ( $this->{_nlri} );
161             }
162              
163             sub withdrawn
164             {
165 7     7 0 14 my $this = shift();
166              
167 7 100       19 $this->{_withdrawn} = @_ ? shift() : $this->{_withdrawn};
168 7         21 return ( $this->{_withdrawn} );
169             }
170              
171             sub ashash
172             {
173 1     1 0 3 my $this = shift();
174              
175 1         3 my (%res,$nlri);
176              
177 1 50       6 $nlri = clone Net::BGP::NLRI($this) if defined($this->{_nlri});
178              
179 1         3 foreach my $prefix (@{$this->{_nlri}})
  1         4  
180             {
181 1         14 $res{$prefix} = $nlri;
182             };
183              
184 1         3 foreach my $prefix (@{$this->withdrawn})
  1         3  
185             {
186 1         3 $res{$prefix} = undef;
187             };
188              
189 1         4 return \%res;
190             }
191              
192             ## Private Methods ##
193              
194             sub _new_from_msg
195             {
196 16     16   3646 my ($class, $buffer, $options) = @_;
197            
198 16 100       52 if (!defined($options)) { $options = {}; }
  10         21  
199 16   100     75 $options->{as4} ||= 0;
200              
201 16         43 my $this = $class->new();
202              
203 16         48 $this->_decode_message($buffer, $options);
204              
205 15         60 return $this;
206             }
207              
208             sub _encode_attr
209             {
210 39     39   101 my ($this, $type, $data) = @_;
211 39         57 my $buffer = '';
212              
213 39         55 my $flag = $BGP_PATH_ATTR_FLAGS[$type];
214 39         57 my $len_format = 'C';
215              
216 39         61 my $len = length($data);
217 39 50       89 if ($len > 255)
218             {
219 0         0 $flag |= $BGP_PATH_ATTR_FLAG_EXTLEN;
220 0         0 $len_format = 'n';
221             }
222              
223 39         99 $buffer .= pack('CC', $flag, $type);
224 39         77 $buffer .= pack($len_format, $len);
225 39         65 $buffer .= $data;
226              
227 39         105 return ( $buffer );
228             }
229              
230             sub _decode_message
231             {
232 16     16   137 my ($this, $buffer, $options) = @_;
233            
234 16 50       44 if (!defined($options)) { $options = {}; }
  0         0  
235 16   100     117 $options->{as4} ||= 0;
236              
237 16         28 my $offset = 0;
238 16         20 my $length;
239              
240             # decode the Withdrawn Routes field
241 16         65 $length = unpack('n', substr($buffer, $offset, 2));
242 16         28 $offset += 2;
243              
244 16 50       64 if ( $length > (length($buffer) - $offset) ) {
245 0         0 Net::BGP::Notification->throw(
246             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
247             ErrorSubCode => BGP_ERROR_SUBCODE_MALFORMED_ATTR_LIST
248             );
249             }
250              
251 16         76 $this->_decode_withdrawn(substr($buffer, $offset, $length));
252 16         36 $offset += $length;
253              
254             # decode the Path Attributes field
255 16         43 $length = unpack('n', substr($buffer, $offset, 2));
256 16         26 $offset += 2;
257              
258 16 50       45 if ( $length > (length($buffer) - $offset) ) {
259 0         0 Net::BGP::Notification->throw(
260             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
261             ErrorSubCode => BGP_ERROR_SUBCODE_MALFORMED_ATTR_LIST
262             );
263             }
264              
265 16 100       58 return if $length == 0; # withdrawn routes only
266              
267 15         62 $this->_decode_path_attributes(
268             substr($buffer, $offset, $length),
269             $options
270             );
271              
272 14         31 $offset += $length;
273              
274             # decode the Network Layer Reachability Information field
275 14         51 $this->_decode_nlri(substr($buffer, $offset));
276             }
277              
278             sub _decode_origin
279             {
280 15     15   50 my ($this, $buffer) = @_;
281              
282 15         41 $this->{_origin} = unpack('C', $buffer);
283 15         57 $this->{_attr_mask}->[BGP_PATH_ATTR_ORIGIN] ++;
284              
285 15         31 return ( undef );
286             }
287              
288             sub _decode_as_path
289             {
290 17     17   50 my ($this, $buffer, $options) = @_;
291              
292 17 100       40 if (!defined($options)) { $options = {}; }
  3         4  
293 17   100     69 $options->{as4} ||= 0;
294              
295 17         34 $this->{_as_path_raw} = $buffer;
296              
297 17         27 my $as4path = '';
298 17 100       39 if ( exists $this->{_as4_path_raw} ) {
299 3         4 $as4path = $this->{_as4_path_raw};
300             }
301              
302 17         67 my $path = Net::BGP::ASPath->_new_from_msg(
303             $buffer,
304             $as4path,
305             $options
306             );
307              
308 17         73 $this->{_as_path} = $path;
309 17         47 $this->{_attr_mask}->[BGP_PATH_ATTR_AS_PATH] ++;
310              
311 17         39 return ( undef );
312             }
313              
314             # We don't decode the AS4 path, we just stick it in this variable. That
315             # said, if we have already come across the AS_PATH (non AS4), we handle it.
316             sub _decode_as4_path
317             {
318 3     3   9 my ($this, $buffer) = @_;
319              
320 3         7 $this->{_as4_path_raw} = $buffer;
321 3         9 $this->{_attr_mask}->[BGP_PATH_ATTR_AS4_PATH] ++;
322              
323             # If we've already decoded the regular AS path, we need to reprocess
324             # it now that we have an AS4_PATH.
325 3 50       9 if ( defined $this->{_as_path_raw} ) {
326             # We decrement the ref count for the AS_PATH (16 bit) because
327             # this will otherwise trigger an error for having 2 AS_PATH
328             # attributes, when it's really we just called it twice.
329 3         6 $this->{_attr_mask}->[BGP_PATH_ATTR_AS_PATH] --;
330 3         8 $this->_decode_as_path( $this->{_as_path_raw} );
331             }
332              
333 3         6 return ( undef );
334             }
335              
336             sub _decode_next_hop
337             {
338 14     14   52 my ($this, $buffer) = @_;
339 14         26 my ($data);
340              
341 14 50       39 if ( length($buffer) != 0x04 ) {
342 0         0 $data = $this->_encode_attr(BGP_PATH_ATTR_NEXT_HOP, $buffer);
343 0         0 Net::BGP::Notification->throw(
344             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
345             ErrorSubCode => BGP_ERROR_SUBCODE_BAD_ATTR_LENGTH,
346             ErrorData => $data
347             );
348             }
349              
350             # TODO: check if _next_hop is a valid IP host address
351 14         108 $this->{_next_hop} = inet_ntoa($buffer);
352 14         48 $this->{_attr_mask}->[BGP_PATH_ATTR_NEXT_HOP] ++;
353              
354 14         27 return ( undef );
355             }
356              
357             sub _decode_med
358             {
359 2     2   6 my ($this, $buffer) = @_;
360 2         4 my ($data);
361              
362 2 50       13 if ( length($buffer) != 0x04 ) {
363 0         0 $data = $this->_encode_attr(BGP_PATH_ATTR_MULTI_EXIT_DISC, $buffer);
364 0         0 Net::BGP::Notification->throw(
365             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
366             ErrorSubCode => BGP_ERROR_SUBCODE_BAD_ATTR_LENGTH,
367             ErrorData => $data
368             );
369             }
370              
371 2         5 $this->{_med} = unpack('N', $buffer);
372 2         6 $this->{_attr_mask}->[BGP_PATH_ATTR_MULTI_EXIT_DISC] ++;
373              
374 2         4 return ( undef );
375             }
376              
377             sub _decode_local_pref
378             {
379 2     2   7 my ($this, $buffer) = @_;
380 2         3 my ($data);
381              
382 2 50       5 if ( length($buffer) != 0x04 ) {
383 0         0 $data = $this->_encode_attr(BGP_PATH_ATTR_LOCAL_PREF, $buffer);
384 0         0 Net::BGP::Notification->throw(
385             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
386             ErrorSubCode => BGP_ERROR_SUBCODE_BAD_ATTR_LENGTH,
387             ErrorData => $data
388             );
389             }
390              
391 2         4 $this->{_local_pref} = unpack('N', $buffer);
392 2         6 $this->{_attr_mask}->[BGP_PATH_ATTR_LOCAL_PREF] ++;
393              
394 2         4 return ( undef );
395             }
396              
397             sub _decode_atomic_aggregate
398             {
399 0     0   0 my ($this, $buffer) = @_;
400 0         0 my ($data);
401              
402 0 0       0 if ( length($buffer) ) {
403 0         0 $data = $this->_encode_attr(BGP_PATH_ATTR_ATOMIC_AGGREGATE, $buffer);
404 0         0 Net::BGP::Notification->throw(
405             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
406             ErrorSubCode => BGP_ERROR_SUBCODE_BAD_ATTR_LENGTH,
407             ErrorData => $data
408             );
409             }
410              
411 0         0 $this->{_atomic_agg} = TRUE;
412 0         0 $this->{_attr_mask}->[BGP_PATH_ATTR_ATOMIC_AGGREGATE] ++;
413              
414 0         0 return ( undef );
415             }
416              
417             sub _decode_aggregator
418             {
419 0     0   0 my ($this, $buffer, $options) = @_;
420              
421 0 0       0 if (!defined($options)) { $options = {}; }
  0         0  
422 0   0     0 $options->{as4} ||= 0;
423              
424 0         0 my ($data);
425              
426 0 0       0 if ($options->{as4}) {
427 0 0       0 if ( length($buffer) != 0x08 ) {
428 0         0 $data = $this->_encode_attr(BGP_PATH_ATTR_AGGREGATOR, $buffer);
429 0         0 Net::BGP::Notification->throw(
430             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
431             ErrorSubCode => BGP_ERROR_SUBCODE_BAD_ATTR_LENGTH,
432             ErrorData => $data
433             );
434             }
435              
436 0         0 $this->{_aggregator}->[0] = unpack('N', substr($buffer, 0, 4));
437 0         0 $this->{_aggregator}->[1] = inet_ntoa(substr($buffer, 4, 4));
438             } else {
439 0 0       0 if ( length($buffer) != 0x06 ) {
440 0         0 $data = $this->_encode_attr(BGP_PATH_ATTR_AGGREGATOR, $buffer);
441 0         0 Net::BGP::Notification->throw(
442             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
443             ErrorSubCode => BGP_ERROR_SUBCODE_BAD_ATTR_LENGTH,
444             ErrorData => $data
445             );
446             }
447              
448 0         0 $this->{_aggregator}->[0] = unpack('n', substr($buffer, 0, 2));
449 0         0 $this->{_aggregator}->[1] = inet_ntoa(substr($buffer, 2, 4));
450             }
451 0         0 $this->{_attr_mask}->[BGP_PATH_ATTR_AGGREGATOR] ++;
452              
453 0 0       0 if ( $options->{as4} ) { return ( undef ); }
  0         0  
454 0 0       0 if (!exists($this->{_as4_aggregator}->[0])) { return ( undef ); }
  0         0  
455              
456 0 0       0 if ($this->{_aggregator}->[0] != 23456) {
457             # Disregard _as4_aggregator if not AS_TRANS, per RFC4893 4.2.3
458 0         0 return ( undef );
459             }
460              
461 0         0 @{ $this->{_aggregator} } = @{ $this->{_as4_aggregator} };
  0         0  
  0         0  
462              
463 0         0 return ( undef );
464             }
465              
466             sub _decode_as4_aggregator
467             {
468 0     0   0 my ($this, $buffer, $options) = @_;
469            
470 0 0       0 if (!defined($options)) { $options = {}; }
  0         0  
471 0   0     0 $options->{as4} ||= 0;
472              
473 0         0 my ($data);
474              
475 0 0       0 if ( length($buffer) != 0x08 ) {
476 0         0 $data = $this->_encode_attr(BGP_PATH_ATTR_AS4_AGGREGATOR, $buffer);
477 0         0 Net::BGP::Notification->throw(
478             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
479             ErrorSubCode => BGP_ERROR_SUBCODE_BAD_ATTR_LENGTH,
480             ErrorData => $data
481             );
482             }
483              
484 0         0 $this->{_as4_aggregator}->[0] = unpack('N', substr($buffer, 0, 4));
485 0         0 $this->{_as4_aggregator}->[1] = inet_ntoa(substr($buffer, 4, 4));
486 0         0 $this->{_attr_mask}->[BGP_PATH_ATTR_AS4_AGGREGATOR] ++;
487            
488 0 0       0 if ( $options->{as4} ) { return ( undef ); }
  0         0  
489 0 0       0 if (!exists($this->{_aggregator}->[0])) { return ( undef ); }
  0         0  
490              
491 0 0       0 if ($this->{_aggregator}->[0] != 23456) {
492             # Disregard _as4_aggregator if not AS_TRANS, per RFC4893 4.2.3
493 0         0 return ( undef );
494             }
495              
496 0         0 @{ $this->{_aggregator} } = @{ $this->{_as4_aggregator} };
  0         0  
  0         0  
497              
498 0         0 return ( undef );
499             }
500              
501             sub _decode_communities
502             {
503 2     2   7 my ($this, $buffer) = @_;
504 2         5 my ($as, $val, $ii, $offset, $count);
505 2         0 my ($data);
506              
507 2 50       5 if ( length($buffer) % 0x04 ) {
508 0         0 $data = $this->_encode_attr(BGP_PATH_ATTR_COMMUNITIES, $buffer);
509 0         0 Net::BGP::Notification->throw(
510             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
511             ErrorSubCode => BGP_ERROR_SUBCODE_BAD_ATTR_LENGTH,
512             ErrorData => $data
513             );
514             }
515              
516 2         3 $offset = 0;
517 2         4 $count = length($buffer) / 4;
518 2         18 for ( $ii = 0; $ii < $count; $ii++ ) {
519 2         8 $as = unpack('n', substr($buffer, $offset, 2));
520 2         6 $val = unpack('n', substr($buffer, $offset + 2, 2));
521 2         4 push(@{$this->{_communities}}, join(":", $as, $val));
  2         10  
522 2         5 $offset += 4;
523             }
524              
525 2         12 $this->{_attr_mask}->[BGP_PATH_ATTR_COMMUNITIES] ++;
526              
527 2         3 return ( undef );
528             }
529              
530             sub _decode_path_attributes
531             {
532 15     15   68 my ($this, $buffer, $options) = @_;
533              
534 15 50       43 if (!defined($options)) { $options = {}; }
  0         0  
535 15   100     54 $options->{as4} ||= 0;
536              
537 15         61 my ($offset, $data_length);
538 15         0 my ($flags, $type, $length, $len_format, $len_bytes, $sub, $data);
539 15         0 my ($error_data, $ii);
540 15         90 my @decode_sub = (
541             undef, # 0
542             \&_decode_origin, # 1
543             \&_decode_as_path, # 2
544             \&_decode_next_hop, # 3
545             \&_decode_med, # 4
546             \&_decode_local_pref, # 5
547             \&_decode_atomic_aggregate, # 6
548             \&_decode_aggregator, # 7
549             \&_decode_communities, # 8
550             undef, # 9
551             undef, # 10
552             undef, # 11
553             undef, # 12
554             undef, # 13
555             undef, # 14
556             undef, # 15
557             undef, # 16
558             \&_decode_as4_path, # 17
559             \&_decode_as4_aggregator, # 18
560             );
561              
562 15         23 $offset = 0;
563 15         25 $data_length = length($buffer);
564              
565 15         38 while ( $data_length ) {
566 53         132 $flags = unpack('C', substr($buffer, $offset++, 1));
567 53         111 $type = unpack('C', substr($buffer, $offset++, 1));
568              
569 53         85 $len_format = 'C';
570 53         79 $len_bytes = 1;
571 53 50       488 if ( $flags & $BGP_PATH_ATTR_FLAG_EXTLEN ) {
572 0         0 $len_format = 'n';
573 0         0 $len_bytes = 2;
574             }
575              
576 53         105 $length = unpack($len_format, substr($buffer, $offset, $len_bytes));
577 53         74 $offset += $len_bytes;
578              
579 53 50       140 if ( $length > ($data_length - ($len_bytes + 2)) ) {
580 0         0 $data = substr($buffer, $offset - $len_bytes - 2, $length + $len_bytes + 2);
581 0         0 Net::BGP::Notification->throw(
582             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
583             ErrorSubCode => BGP_ERROR_SUBCODE_BAD_ATTR_LENGTH,
584             ErrorData => $error_data
585             );
586             }
587              
588             ## do we know how to decode this attribute?
589 53 50       112 if (defined $decode_sub[$type])
590             {
591 53         132 $error_data = substr(
592             $buffer,
593             $offset - $len_bytes - 2,
594             $length + $len_bytes + 2
595              
596             );
597              
598 53         71 my $flagmasked = $flags;
599 53         93 $flagmasked &= ~$BGP_PATH_ATTR_FLAG_EXTLEN;
600 53         75 $flagmasked &= ~$BGP_PATH_ATTR_FLAG_RESERVED;
601              
602 53 100       124 if ( $BGP_PATH_ATTR_FLAGS[$type] != $flagmasked ) {
603              
604             # See RFC4271 Section 5
605 3 100 66     19 if ( ( $flagmasked & $BGP_PATH_ATTR_FLAG_OPTIONAL )
      66        
606             && ( $flagmasked & $BGP_PATH_ATTR_FLAG_TRANSITIVE )
607             && ( $BGP_PATH_ATTR_FLAGS[$type] ==
608             ($flagmasked & ~$BGP_PATH_ATTR_FLAG_PARTIAL)
609             )
610             ) {
611             # In this case, the flags only differ in the partial bit
612             # So it's actually okay.
613             } else {
614 1         6 Net::BGP::Notification->throw(
615             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
616             ErrorSubCode => BGP_ERROR_SUBCODE_BAD_ATTR_FLAGS,
617             ErrorData => $error_data
618             );
619             }
620              
621             # Watch out for the do-nothing case in the "if" statement
622             # above.
623             }
624              
625 52         77 $sub = $decode_sub[$type];
626 52         147 $this->$sub(substr($buffer, $offset, $length), $options);
627             }
628              
629 52         98 $offset += $length;
630 52         136 $data_length -= ($length + $len_bytes + 2);
631             }
632              
633             ## Check for missing mandatory well-known attributes
634             ##
635 14         31 for my $attr (@_BGP_MANDATORY_ATTRS)
636             {
637 42 50       84 $this->{_attr_mask}->[$attr]
638             or Net::BGP::Notification->throw(
639             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
640             ErrorSubCode => BGP_ERROR_SUBCODE_MISSING_WELL_KNOWN_ATTR,
641             ErrorData => pack('C', $attr)
642             );
643             }
644              
645             ## Check for repeated attributes, which violates RFC 4271, sec 5.
646             ##
647 14 100       31 if ( grep { defined $_ and $_ > 1 } @{$this->{_attr_mask}||[]} )
  153 50       445  
  14 50       48  
648             {
649 0         0 Net::BGP::Notification->throw(
650             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
651             ErrorSubCode => BGP_ERROR_SUBCODE_MALFORMED_ATTR_LIST
652             );
653             }
654             }
655              
656             sub _decode_prefix_list
657             {
658 30     30   60 my ($this, $buffer) = @_;
659 30         83 my ($offset, $data_length);
660 30         0 my ($prefix, $prefix_bits, $prefix_bytes, $ii, @prefix_list);
661              
662 30         44 $offset = 0;
663 30         44 $data_length = length($buffer);
664              
665 30         76 while ( $data_length ) {
666 25         59 $prefix_bits = unpack('C', substr($buffer, $offset++, 1));
667 25 100       108 $prefix_bytes = int($prefix_bits / 8) + (($prefix_bits % 8) ? 1 : 0);
668              
669 25 50       66 if ( $prefix_bytes > ($data_length - 1)) {
670 0         0 return ( FALSE );
671             }
672              
673 25         36 $prefix = 0;
674 25         58 for ( $ii = 0; $ii < $prefix_bytes; $ii++ ) {
675 83         240 $prefix |= (unpack('C', substr($buffer, $offset++, 1)) << (24 - ($ii * 8)));
676             }
677              
678 25         64 $prefix = pack('N', $prefix);
679 25         131 push(@prefix_list, inet_ntoa($prefix) . "/" . $prefix_bits);
680 25         74 $data_length -= ($prefix_bytes + 1);
681             }
682              
683 30         64 return ( TRUE, @prefix_list );
684             }
685              
686             sub _decode_withdrawn
687             {
688 16     16   64 my ($this, $buffer) = @_;
689 16         28 my ($result, @prefix_list);
690              
691 16         44 ($result, @prefix_list) = $this->_decode_prefix_list($buffer);
692 16 50       36 if ( ! $result ) {
693 0         0 Net::BGP::Notification->throw(
694             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
695             ErrorSubCode => BGP_ERROR_SUBCODE_MALFORMED_ATTR_LIST
696             );
697             }
698              
699 16         25 push(@{$this->{_withdrawn}}, @prefix_list);
  16         38  
700             }
701              
702             sub _decode_nlri
703             {
704 14     14   50 my ($this, $buffer) = @_;
705 14         27 my ($result, @prefix_list);
706              
707 14         38 ($result, @prefix_list) = $this->_decode_prefix_list($buffer);
708 14 50       35 if ( ! $result ) {
709 0         0 Net::BGP::Notification->throw(
710             ErrorCode => BGP_ERROR_CODE_UPDATE_MESSAGE,
711             ErrorSubCode => BGP_ERROR_SUBCODE_BAD_NLRI
712             );
713             }
714              
715 14         23 push(@{$this->{_nlri}}, @prefix_list);
  14         57  
716             }
717              
718             sub _encode_message
719             {
720 12     12   98 my ($this, $options) = @_;
721              
722 12 100       37 if (!defined($options)) { $options = {}; }
  6         9  
723 12   100     56 $options->{as4} ||= 0;
724              
725 12         21 my ($buffer, $withdrawn, $path_attr, $nlri);
726              
727             # encode the Withdrawn Routes field
728 12         47 $withdrawn = $this->_encode_prefix_list($this->{_withdrawn});
729 12         52 $buffer = pack('n', length($withdrawn)) . $withdrawn;
730              
731             # encode the Path Attributes field
732 12         75 $path_attr = $this->_encode_path_attributes( $options );
733 12         54 $buffer .= (pack('n', length($path_attr)) . $path_attr);
734              
735             # encode the Network Layer Reachability Information field
736 12         37 $buffer .= $this->_encode_prefix_list($this->{_nlri});
737              
738 12         48 return ( $buffer );
739             }
740              
741             sub _encode_prefix
742             {
743 22     22   64 my $prefix = shift();
744 22         43 my ($buffer, $length, @octets);
745              
746 22         83 ($prefix, $length) = split('/', $prefix);
747              
748 22         66 $buffer = pack('C', $length);
749              
750 22         82 @octets = split(/\./, $prefix);
751 22         56 while ( $length > 0 ) {
752 74         138 $buffer .= pack('C', shift(@octets));
753 74         156 $length -= 8;
754             }
755              
756 22         60 return ( $buffer );
757             }
758              
759             sub _encode_prefix_list
760             {
761 24     24   42 my ($this, $prefix_list) = @_;
762 24         41 my ($prefix, $buffer);
763              
764 24         32 $buffer = '';
765 24         36 foreach $prefix ( @{$prefix_list} ) {
  24         60  
766 22         45 $buffer .= _encode_prefix($prefix);
767             }
768              
769 24         66 return ( $buffer );
770             }
771              
772             sub _encode_origin
773             {
774 11     11   21 my $this = shift();
775              
776             $this->_encode_attr(BGP_PATH_ATTR_ORIGIN,
777 11         35 pack('C', $this->{_origin}));
778             }
779              
780             sub _encode_as_path
781             {
782 11     11   22 my ($this, $options) = @_;
783              
784 11 50       35 if (!defined($options)) { $options = {}; }
  0         0  
785 11   100     45 $options->{as4} ||= 0;
786              
787 11         52 my ($as_buffer, $as4_buffer) = $this->{_as_path}->_encode($options);
788              
789 11         24 my $output;
790              
791 11         27 $output = $this->_encode_attr(BGP_PATH_ATTR_AS_PATH, $as_buffer);
792              
793 11 50       31 if (defined $as4_buffer) {
794 0         0 $output .= $this->_encode_attr(BGP_PATH_ATTR_AS4_PATH, $as4_buffer);
795             }
796              
797 11         25 return $output;
798             }
799              
800             sub _encode_next_hop
801             {
802 11     11   18 my $this = shift();
803             $this->_encode_attr(BGP_PATH_ATTR_NEXT_HOP,
804 11         28 inet_aton($this->{_next_hop}));
805             }
806              
807             sub _encode_med
808             {
809 2     2   4 my $this = shift();
810             $this->_encode_attr(BGP_PATH_ATTR_MULTI_EXIT_DISC,
811 2         4 pack('N', $this->{_med}));
812             }
813              
814             sub _encode_local_pref
815             {
816 2     2   3 my $this = shift();
817             $this->_encode_attr(BGP_PATH_ATTR_LOCAL_PREF,
818 2         5 pack('N', $this->{_local_pref}));
819             }
820              
821             sub _encode_atomic_aggregate
822             {
823 0     0   0 my $this = shift();
824 0         0 $this->_encode_attr(BGP_PATH_ATTR_ATOMIC_AGGREGATE);
825             }
826              
827             sub _encode_aggregator
828             {
829 0     0   0 my ($this, $options) = @_;
830              
831 0 0       0 if (!defined($options)) { $options = {}; }
  0         0  
832 0   0     0 $options->{as4} ||= 0;
833              
834 0         0 my ($aggr, $ret);
835              
836 0 0       0 if ($options->{as4}) {
    0          
837             $aggr = pack('N', $this->{_aggregator}->[0]) .
838 0         0 inet_aton($this->{_aggregator}->[1]);
839              
840 0         0 $ret = $this->_encode_attr(BGP_PATH_ATTR_AGGREGATOR, $aggr);
841             } elsif ($this->{_aggregator} <= 65535) {
842             $aggr = pack('n', $this->{_aggregator}->[0]) .
843 0         0 inet_aton($this->{_aggregator}->[1]);
844              
845 0         0 $ret = $this->_encode_attr(BGP_PATH_ATTR_AGGREGATOR, $aggr);
846             } else {
847             $aggr = pack('n', 23456) .
848 0         0 inet_aton($this->{_aggregator}->[1]);
849            
850 0         0 $ret = $this->_encode_attr(BGP_PATH_ATTR_AGGREGATOR, $aggr);
851            
852             $aggr = pack('N', $this->{_aggregator}->[0]) .
853 0         0 inet_aton($this->{_aggregator}->[1]);
854              
855 0         0 $ret .= $this->_encode_attr(BGP_PATH_ATTR_AS4_AGGREGATOR, $aggr);
856             }
857              
858 0         0 return $ret;
859             }
860              
861             sub _encode_communities
862             {
863 2     2   3 my $this = shift();
864 2         6 my ($as, $val, $community, @communities);
865 2         0 my ($buffer, $community_buffer);
866              
867 2         3 @communities = @{$this->{_communities}};
  2         5  
868 2         5 foreach $community ( @communities ) {
869 2         8 ($as, $val) = split(/\:/, $community);
870 2         8 $community_buffer .= pack('nn', $as, $val);
871             }
872              
873 2         30 $this->_encode_attr(BGP_PATH_ATTR_COMMUNITIES, $community_buffer);
874             }
875              
876             sub _encode_path_attributes
877             {
878 12     12   35 my ($this, $options) = @_;
879              
880 12 50       32 if (!defined($options)) { $options = {}; }
  0         0  
881 12   100     43 $options->{as4} ||= 0;
882              
883 12         17 my $buffer;
884              
885 12         22 $buffer = '';
886              
887             # do not encode path attributes if no NLRI is present
888 12 100 50     35 unless ((defined $this->{_nlri})
889 12         40 && scalar(@{$this->{_nlri}})) {
890 1         3 return ( $buffer );
891             }
892              
893             # encode the ORIGIN path attribute
894 11 50       33 if ( ! defined($this->{_origin}) ) {
895 0         0 carp "mandatory path attribute ORIGIN not defined\n";
896             }
897 11         33 $buffer = $this->_encode_origin();
898              
899             # encode the AS_PATH path attribute
900 11 50       45 if ( ! defined($this->{_as_path}) ) {
901 0         0 carp "mandatory path attribute AS_PATH not defined\n";
902             }
903 11         33 $buffer .= $this->_encode_as_path($options);
904              
905             # encode the NEXT_HOP path attribute
906 11 50       36 if ( ! defined($this->{_next_hop}) ) {
907 0         0 carp "mandatory path attribute NEXT_HOP not defined\n";
908             }
909 11         37 $buffer .= $this->_encode_next_hop();
910              
911             # encode the MULTI_EXIT_DISC path attribute
912 11 100       42 if ( defined($this->{_med}) ) {
913 2         7 $buffer .= $this->_encode_med();
914             }
915              
916             # encode the LOCAL_PREF path attribute
917 11 100       31 if ( defined($this->{_local_pref}) ) {
918 2         6 $buffer .= $this->_encode_local_pref();
919             }
920              
921             # encode the ATOMIC_AGGREGATE path attribute
922 11 50       34 if ( defined($this->{_atomic_agg}) ) {
923 0         0 $buffer .= $this->_encode_atomic_aggregate();
924             }
925              
926             # encode the AGGREGATOR path attribute
927 11 50       15 if ( scalar(@{$this->{_aggregator}}) ) {
  11         40  
928 0         0 $buffer .= $this->_encode_aggregator($options);
929             }
930              
931             # encode the COMMUNITIES path attribute
932 11 100       15 if ( scalar(@{$this->{_communities}}) ) {
  11         36  
933 2         6 $buffer .= $this->_encode_communities();
934             }
935              
936 11         32 return ( $buffer );
937             }
938              
939             ## POD ##
940              
941             =pod
942              
943             =head1 NAME
944              
945             C - Class encapsulating BGP-4 UPDATE message
946              
947             =head1 SYNOPSIS
948              
949             use Net::BGP::Update qw( :origin );
950              
951             # Constructor
952             $update = Net::BGP::Update->new(
953             NLRI => [ qw( 10/8 172.168/16 ) ],
954             Withdraw => [ qw( 192.168.1/24 172.10/16 192.168.2.1/32 ) ],
955             # For Net::BGP::NLRI
956             Aggregator => [ 64512, '10.0.0.1' ],
957             AsPath => [ 64512, 64513, 64514 ],
958             AtomicAggregate => 1,
959             Communities => [ qw( 64512:10000 64512:10001 ) ],
960             LocalPref => 100,
961             MED => 200,
962             NextHop => '10.0.0.1',
963             Origin => INCOMPLETE,
964             );
965              
966             # Construction from a NLRI object:
967             $nlri = Net::BGP::NLRI->new( ... );
968             $update = Net::BGP::Update->new($nlri,$nlri_ref,$withdrawn_ref);
969              
970             # Object Copy
971             $clone = $update->clone();
972              
973             # Accessor Methods
974             $nlri_ref = $update->nlri($nlri_ref);
975             $withdrawn_ref = $update->withdrawn($withdrawn_ref);
976             $prefix_hash_ref = $update->ashash;
977              
978             # Comparison
979             if ($update1 eq $update2) { ... }
980             if ($update1 ne $update2) { ... }
981              
982             =head1 DESCRIPTION
983              
984             This module encapsulates the data contained in a BGP-4 UPDATE message.
985             It provides a constructor, and accessor methods for each of the
986             message fields and well-known path attributes of an UPDATE. Whenever
987             a L sends an UPDATE message to its peer, it does so
988             by passing a C object to the peer object's I
989             method. Similarly, when the peer receives an UPDATE message from its
990             peer, the UPDATE callback is called and passed a reference to a
991             C object. The callback function can then examine
992             the UPDATE message fields by means of the accessor methods.
993              
994             =head1 CONSTRUCTOR
995              
996             I - create a new C object
997              
998             $update = Net::BGP::Update->new(
999             NLRI => [ qw( 10/8 172.168/16 ) ],
1000             Withdraw => [ qw( 192.168.1/24 172.10/16 192.168.2.1/32 ) ],
1001             # For Net::BGP::NLRI
1002             Aggregator => [ 64512, '10.0.0.1' ],
1003             AsPath => [ 64512, 64513, 64514 ],
1004             AtomicAggregate => 1,
1005             Communities => [ qw( 64512:10000 64512:10001 ) ],
1006             LocalPref => 100,
1007             MED => 200,
1008             NextHop => '10.0.0.1',
1009             Origin => INCOMPLETE,
1010             );
1011              
1012             This is the constructor for C objects. It returns a
1013             reference to the newly created object. The following named parameters may
1014             be passed to the constructor. See RFC 1771 for the semantics of each
1015             path attribute.
1016              
1017             An alternative is to construct an object from a L object:
1018              
1019             $nlri = Net::BGP::NLRI->new( ... );
1020             $nlri_ref = [ qw( 10/8 172.168/16 ) ];
1021             $withdrawn_ref = [ qw( 192.168.1/24 172.10/16 192.168.2.1/32 ) ];
1022             $update = Net::BGP::Update->new($nlri,$nlri_ref,$withdrawn_ref);
1023              
1024             The NLRI object will not be modified in any way.
1025              
1026             =head2 NLRI
1027              
1028             This parameter corresponds to the Network Layer Reachability Information (NLRI)
1029             field of an UPDATE message. It represents the route(s) being advertised in this
1030             particular UPDATE. It is expressed as an array reference of route prefixes which
1031             are encoded in a special format as perl strings: XXX.XXX.XXX.XXX/XX. The part
1032             preceding the slash is a dotted-decimal notation IP prefix. Only as many octets
1033             as are significant according to the mask need to be specified. The part following
1034             the slash is the mask which is an integer in the range [0,32] which indicates how
1035             many bits are significant in the prefix. At least one of either the NLRI or Withdraw
1036             parameters is mandatory and must always be provided to the constructor.
1037              
1038             =head2 Withdraw
1039              
1040             This parameter corresponds to the Withdrawn Routes field of an UPDATE message. It
1041             represents route(s) advertised by a previous UPDATE message which are now being
1042             withdrawn by this UPDATE. It is expressed in the same way as the NLRI parameter.
1043             At least one of either the NLRI or Withdraw parameters is mandatory and must
1044             always be provided to the constructor.
1045              
1046             =head1 OBJECT COPY
1047              
1048             I - clone a C object
1049              
1050             $clone = $update->clone();
1051              
1052             This method creates an exact copy of the C, with Withdrawn
1053             Routes, Path Attributes, and NLRI fields matching those of the original object.
1054             This is useful for propagating a modified UPDATE message when the original object
1055             needs to remain unchanged.
1056              
1057             =head1 ACCESSOR METHODS
1058              
1059             I
1060              
1061             I
1062              
1063             These accessor methods return the value(s) of the associated UPDATE message field
1064             if called with no arguments. If called with arguments, they set
1065             the associated field. The representation of parameters and return values is the
1066             same as described for the corresponding named constructor parameters above.
1067              
1068             I
1069              
1070             This method returns a hash reference index on the prefixes found in the NLRI
1071             and Withdraw fields. Withdrawn networks are set to C, while NLRI
1072             prefixes all have the same reference to the L object matching the
1073             Update object itself.
1074              
1075             =head1 EXPORTS
1076              
1077             This module does not export anything.
1078              
1079             =head1 SEE ALSO
1080              
1081             =over
1082              
1083             =item L
1084              
1085             =item L
1086              
1087             =item L
1088              
1089             =item L
1090              
1091             =item L
1092              
1093             =item L
1094              
1095             =item L
1096              
1097             =back
1098              
1099             =head1 AUTHOR
1100              
1101             Stephen J. Scheck
1102              
1103             =cut
1104              
1105             ## End Package Net::BGP::Update ##
1106              
1107             1;