File Coverage

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


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package Net::BGP::Update;
4 4     4   2056 use bytes;
  4         4  
  4         21  
5              
6 4     4   104 use strict;
  4         6  
  4         105  
7 4         269 use vars qw(
8             $VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS
9             @BGP_PATH_ATTR_FLAGS
10 4     4   17 );
  4         6  
11              
12             ## Inheritance and Versioning ##
13              
14 4     4   721 use Net::BGP::NLRI qw( :origin );
  4         7  
  4         594  
15              
16             @ISA = qw( Exporter Net::BGP::NLRI );
17             $VERSION = '0.16';
18              
19             ## Module Imports ##
20              
21 4     4   25 use Carp;
  4         11  
  4         239  
22 4     4   22 use IO::Socket;
  4         4  
  4         23  
23 4     4   1911 use Net::BGP::Notification qw( :errors );
  4         8  
  4         15483  
24              
25             ## General Definitions ##
26              
27 30     30 0 111 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 65 sub BGP_PATH_ATTR_AS_PATH { 2 }
34 29     29 0 112 sub BGP_PATH_ATTR_NEXT_HOP { 3 }
35 4     4 0 8 sub BGP_PATH_ATTR_MULTI_EXIT_DISC { 4 }
36 4     4 0 8 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 5 sub BGP_PATH_ATTR_COMMUNITIES { 8 }
40 3     3 0 6 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 853 my $proto = shift;
98 19   33     103 my $class = ref $proto || $proto;
99              
100 19 100       75 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         4 $this->nlri(shift);
106 1         2 $this->withdrawn(shift);
107 1         3 return $this;
108             };
109              
110 18         24 my ($arg, $value);
111 0         0 my @super_arg;
112 0         0 my %this_arg;
113 18         49 $this_arg{_withdrawn} = [];
114 18         44 $this_arg{_nlri} = [];
115              
116 18         88 while ( defined($arg = shift()) ) {
117 3         5 $value = shift();
118              
119 3 100       18 if ( $arg =~ /nlri/i ) {
    100          
120 1         6 $this_arg{_nlri} = $value;
121             }
122             elsif ( $arg =~ /withdraw/i ) {
123 1         4 $this_arg{_withdrawn} = $value;
124             }
125             else {
126 1         5 push(@super_arg,$arg,$value);
127             }
128             }
129              
130 18         122 my $this = $class->SUPER::new(@super_arg);
131              
132 18         52 @{$this}{keys %this_arg} = values(%this_arg);
  18         375  
133              
134 18         45 bless($this, $class);
135              
136 18         58 return ( $this );
137             }
138              
139             sub clone
140             {
141 5     5 0 526 my $proto = shift;
142 5   66     25 my $class = ref $proto || $proto;
143 5 100       12 $proto = shift unless ref $proto;
144              
145 5         23 my $clone = $class->SUPER::clone($proto);
146              
147 5         10 foreach my $key (qw(_nlri _withdrawn ))
148             {
149 10         8 $clone->{$key} = [ @{$proto->{$key}} ];
  10         34  
150             }
151              
152 5         20 return ( bless($clone, $class) );
153             }
154              
155             sub nlri
156             {
157 7     7 0 354 my $this = shift();
158              
159 7 100       20 $this->{_nlri} = @_ ? shift() : $this->{_nlri};
160 7         26 return ( $this->{_nlri} );
161             }
162              
163             sub withdrawn
164             {
165 7     7 0 14 my $this = shift();
166              
167 7 100       21 $this->{_withdrawn} = @_ ? shift() : $this->{_withdrawn};
168 7         28 return ( $this->{_withdrawn} );
169             }
170              
171             sub ashash
172             {
173 1     1 0 2 my $this = shift();
174              
175 1         2 my (%res,$nlri);
176              
177 1 50       9 $nlri = clone Net::BGP::NLRI($this) if defined($this->{_nlri});
178              
179 1         2 foreach my $prefix (@{$this->{_nlri}})
  1         4  
180             {
181 1         4 $res{$prefix} = $nlri;
182             };
183              
184 1         3 foreach my $prefix (@{$this->withdrawn})
  1         4  
185             {
186 1         4 $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   3640 my ($class, $buffer, $options) = @_;
197            
198 16 100       55 if (!defined($options)) { $options = {}; }
  10         22  
199 16   100     92 $options->{as4} ||= 0;
200              
201 16         59 my $this = $class->new();
202              
203 16         53 $this->_decode_message($buffer, $options);
204              
205 15         57 return $this;
206             }
207              
208             sub _encode_attr
209             {
210 39     39   66 my ($this, $type, $data) = @_;
211 39         44 my $buffer = '';
212              
213 39         51 my $flag = $BGP_PATH_ATTR_FLAGS[$type];
214 39         38 my $len_format = 'C';
215              
216 39         47 my $len = length($data);
217 39 50       95 if ($len > 255)
218             {
219 0         0 $flag |= $BGP_PATH_ATTR_FLAG_EXTLEN;
220 0         0 $len_format = 'n';
221             }
222              
223 39         84 $buffer .= pack('CC', $flag, $type);
224 39         52 $buffer .= pack($len_format, $len);
225 39         44 $buffer .= $data;
226              
227 39         107 return ( $buffer );
228             }
229              
230             sub _decode_message
231             {
232 16     16   32 my ($this, $buffer, $options) = @_;
233            
234 16 50       42 if (!defined($options)) { $options = {}; }
  0         0  
235 16   100     67 $options->{as4} ||= 0;
236              
237 16         23 my $offset = 0;
238 16         14 my $length;
239              
240             # decode the Withdrawn Routes field
241 16         70 $length = unpack('n', substr($buffer, $offset, 2));
242 16         29 $offset += 2;
243              
244 16 50       69 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         62 $this->_decode_withdrawn(substr($buffer, $offset, $length));
252 16         32 $offset += $length;
253              
254             # decode the Path Attributes field
255 16         45 $length = unpack('n', substr($buffer, $offset, 2));
256 16         181 $offset += 2;
257              
258 16 50       54 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       100 return if $length == 0; # withdrawn routes only
266              
267 15         55 $this->_decode_path_attributes(
268             substr($buffer, $offset, $length),
269             $options
270             );
271              
272 14         23 $offset += $length;
273              
274             # decode the Network Layer Reachability Information field
275 14         46 $this->_decode_nlri(substr($buffer, $offset));
276             }
277              
278             sub _decode_origin
279             {
280 15     15   35 my ($this, $buffer) = @_;
281              
282 15         37 $this->{_origin} = unpack('C', $buffer);
283 15         47 $this->{_attr_mask}->[BGP_PATH_ATTR_ORIGIN] ++;
284              
285 15         31 return ( undef );
286             }
287              
288             sub _decode_as_path
289             {
290 17     17   54 my ($this, $buffer, $options) = @_;
291              
292 17 100       70 if (!defined($options)) { $options = {}; }
  3         4  
293 17   100     69 $options->{as4} ||= 0;
294              
295 17         37 $this->{_as_path_raw} = $buffer;
296              
297 17         27 my $as4path = '';
298 17 100       51 if ( exists $this->{_as4_path_raw} ) {
299 3         4 $as4path = $this->{_as4_path_raw};
300             }
301              
302 17         69 my $path = Net::BGP::ASPath->_new_from_msg(
303             $buffer,
304             $as4path,
305             $options
306             );
307              
308 17         27 $this->{_as_path} = $path;
309 17         75 $this->{_attr_mask}->[BGP_PATH_ATTR_AS_PATH] ++;
310              
311 17         40 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   7 my ($this, $buffer) = @_;
319              
320 3         4 $this->{_as4_path_raw} = $buffer;
321 3         6 $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       16 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         7 $this->{_attr_mask}->[BGP_PATH_ATTR_AS_PATH] --;
330 3         5 $this->_decode_as_path( $this->{_as_path_raw} );
331             }
332              
333 3         5 return ( undef );
334             }
335              
336             sub _decode_next_hop
337             {
338 14     14   32 my ($this, $buffer) = @_;
339 14         16 my ($data);
340              
341 14 50       37 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         134 $this->{_next_hop} = inet_ntoa($buffer);
352 14         47 $this->{_attr_mask}->[BGP_PATH_ATTR_NEXT_HOP] ++;
353              
354 14         27 return ( undef );
355             }
356              
357             sub _decode_med
358             {
359 2     2   3 my ($this, $buffer) = @_;
360 2         3 my ($data);
361              
362 2 50       4 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         6 $this->{_med} = unpack('N', $buffer);
372 2         4 $this->{_attr_mask}->[BGP_PATH_ATTR_MULTI_EXIT_DISC] ++;
373              
374 2         3 return ( undef );
375             }
376              
377             sub _decode_local_pref
378             {
379 2     2   3 my ($this, $buffer) = @_;
380 2         2 my ($data);
381              
382 2 50       9 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         5 $this->{_local_pref} = unpack('N', $buffer);
392 2         4 $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   4 my ($this, $buffer) = @_;
504 2         2 my ($as, $val, $ii, $offset, $count);
505 0         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         3 $count = length($buffer) / 4;
518 2         5 for ( $ii = 0; $ii < $count; $ii++ ) {
519 2         4 $as = unpack('n', substr($buffer, $offset, 2));
520 2         7 $val = unpack('n', substr($buffer, $offset + 2, 2));
521 2         1 push(@{$this->{_communities}}, join(":", $as, $val));
  2         8  
522 2         5 $offset += 4;
523             }
524              
525 2         5 $this->{_attr_mask}->[BGP_PATH_ATTR_COMMUNITIES] ++;
526              
527 2         3 return ( undef );
528             }
529              
530             sub _decode_path_attributes
531             {
532 15     15   36 my ($this, $buffer, $options) = @_;
533              
534 15 50       39 if (!defined($options)) { $options = {}; }
  0         0  
535 15   100     52 $options->{as4} ||= 0;
536              
537 15         17 my ($offset, $data_length);
538 0         0 my ($flags, $type, $length, $len_format, $len_bytes, $sub, $data);
539 0         0 my ($error_data, $ii);
540 15         101 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         27 $offset = 0;
563 15         17 $data_length = length($buffer);
564              
565 15         35 while ( $data_length ) {
566 53         136 $flags = unpack('C', substr($buffer, $offset++, 1));
567 53         88 $type = unpack('C', substr($buffer, $offset++, 1));
568              
569 53         67 $len_format = 'C';
570 53         44 $len_bytes = 1;
571 53 50       109 if ( $flags & $BGP_PATH_ATTR_FLAG_EXTLEN ) {
572 0         0 $len_format = 'n';
573 0         0 $len_bytes = 2;
574             }
575              
576 53         83 $length = unpack($len_format, substr($buffer, $offset, $len_bytes));
577 53         60 $offset += $len_bytes;
578              
579 53 50       112 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       109 if (defined $decode_sub[$type])
590             {
591 53         105 $error_data = substr(
592             $buffer,
593             $offset - $len_bytes - 2,
594             $length + $len_bytes + 2
595              
596             );
597              
598 53         56 my $flagmasked = $flags;
599 53         65 $flagmasked &= ~$BGP_PATH_ATTR_FLAG_EXTLEN;
600 53         94 $flagmasked &= ~$BGP_PATH_ATTR_FLAG_RESERVED;
601              
602 53 100       132 if ( $BGP_PATH_ATTR_FLAGS[$type] != $flagmasked ) {
603              
604             # See RFC4271 Section 5
605 3 100 66     20 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         5 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         62 $sub = $decode_sub[$type];
626 52         135 $this->$sub(substr($buffer, $offset, $length), $options);
627             }
628              
629 52         79 $offset += $length;
630 52         143 $data_length -= ($length + $len_bytes + 2);
631             }
632              
633             ## Check for missing mandatory well-known attributes
634             ##
635 14         38 for my $attr (@_BGP_MANDATORY_ATTRS)
636             {
637 42 50       103 $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       22 if ( grep { defined $_ and $_ > 1 } @{$this->{_attr_mask}||[]} )
  153 50       476  
  14 50       55  
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   52 my ($this, $buffer) = @_;
659 30         40 my ($offset, $data_length);
660 0         0 my ($prefix, $prefix_bits, $prefix_bytes, $ii, @prefix_list);
661              
662 30         30 $offset = 0;
663 30         35 $data_length = length($buffer);
664              
665 30         79 while ( $data_length ) {
666 25         111 $prefix_bits = unpack('C', substr($buffer, $offset++, 1));
667 25 100       92 $prefix_bytes = int($prefix_bits / 8) + (($prefix_bits % 8) ? 1 : 0);
668              
669 25 50       62 if ( $prefix_bytes > ($data_length - 1)) {
670 0         0 return ( FALSE );
671             }
672              
673 25         27 $prefix = 0;
674 25         60 for ( $ii = 0; $ii < $prefix_bytes; $ii++ ) {
675 83         247 $prefix |= (unpack('C', substr($buffer, $offset++, 1)) << (24 - ($ii * 8)));
676             }
677              
678 25         57 $prefix = pack('N', $prefix);
679 25         125 push(@prefix_list, inet_ntoa($prefix) . "/" . $prefix_bits);
680 25         233 $data_length -= ($prefix_bytes + 1);
681             }
682              
683 30         120 return ( TRUE, @prefix_list );
684             }
685              
686             sub _decode_withdrawn
687             {
688 16     16   49 my ($this, $buffer) = @_;
689 16         21 my ($result, @prefix_list);
690              
691 16         41 ($result, @prefix_list) = $this->_decode_prefix_list($buffer);
692 16 50       39 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         20 push(@{$this->{_withdrawn}}, @prefix_list);
  16         47  
700             }
701              
702             sub _decode_nlri
703             {
704 14     14   36 my ($this, $buffer) = @_;
705 14         23 my ($result, @prefix_list);
706              
707 14         28 ($result, @prefix_list) = $this->_decode_prefix_list($buffer);
708 14 50       37 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         16 push(@{$this->{_nlri}}, @prefix_list);
  14         62  
716             }
717              
718             sub _encode_message
719             {
720 12     12   87 my ($this, $options) = @_;
721              
722 12 100       29 if (!defined($options)) { $options = {}; }
  6         10  
723 12   100     51 $options->{as4} ||= 0;
724              
725 12         15 my ($buffer, $withdrawn, $path_attr, $nlri);
726              
727             # encode the Withdrawn Routes field
728 12         40 $withdrawn = $this->_encode_prefix_list($this->{_withdrawn});
729 12         40 $buffer = pack('n', length($withdrawn)) . $withdrawn;
730              
731             # encode the Path Attributes field
732 12         42 $path_attr = $this->_encode_path_attributes( $options );
733 12         44 $buffer .= (pack('n', length($path_attr)) . $path_attr);
734              
735             # encode the Network Layer Reachability Information field
736 12         39 $buffer .= $this->_encode_prefix_list($this->{_nlri});
737              
738 12         54 return ( $buffer );
739             }
740              
741             sub _encode_prefix
742             {
743 22     22   34 my $prefix = shift();
744 22         35 my ($buffer, $length, @octets);
745              
746 22         80 ($prefix, $length) = split('/', $prefix);
747              
748 22         65 $buffer = pack('C', $length);
749              
750 22         67 @octets = split(/\./, $prefix);
751 22         100 while ( $length > 0 ) {
752 74         118 $buffer .= pack('C', shift(@octets));
753 74         157 $length -= 8;
754             }
755              
756 22         63 return ( $buffer );
757             }
758              
759             sub _encode_prefix_list
760             {
761 24     24   34 my ($this, $prefix_list) = @_;
762 24         25 my ($prefix, $buffer);
763              
764 24         28 $buffer = '';
765 24         31 foreach $prefix ( @{$prefix_list} ) {
  24         55  
766 22         83 $buffer .= _encode_prefix($prefix);
767             }
768              
769 24         54 return ( $buffer );
770             }
771              
772             sub _encode_origin
773             {
774 11     11   20 my $this = shift();
775              
776 11         30 $this->_encode_attr(BGP_PATH_ATTR_ORIGIN,
777             pack('C', $this->{_origin}));
778             }
779              
780             sub _encode_as_path
781             {
782 11     11   20 my ($this, $options) = @_;
783              
784 11 50       29 if (!defined($options)) { $options = {}; }
  0         0  
785 11   100     43 $options->{as4} ||= 0;
786              
787 11         54 my ($as_buffer, $as4_buffer) = $this->{_as_path}->_encode($options);
788              
789 11         16 my $output;
790              
791 11         32 $output = $this->_encode_attr(BGP_PATH_ATTR_AS_PATH, $as_buffer);
792              
793 11 50       30 if (defined $as4_buffer) {
794 0         0 $output .= $this->_encode_attr(BGP_PATH_ATTR_AS4_PATH, $as4_buffer);
795             }
796              
797 11         27 return $output;
798             }
799              
800             sub _encode_next_hop
801             {
802 11     11   14 my $this = shift();
803 11         31 $this->_encode_attr(BGP_PATH_ATTR_NEXT_HOP,
804             inet_aton($this->{_next_hop}));
805             }
806              
807             sub _encode_med
808             {
809 2     2   2 my $this = shift();
810 2         4 $this->_encode_attr(BGP_PATH_ATTR_MULTI_EXIT_DISC,
811             pack('N', $this->{_med}));
812             }
813              
814             sub _encode_local_pref
815             {
816 2     2   3 my $this = shift();
817 2         7 $this->_encode_attr(BGP_PATH_ATTR_LOCAL_PREF,
818             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 0         0 $aggr = pack('N', $this->{_aggregator}->[0]) .
838             inet_aton($this->{_aggregator}->[1]);
839              
840 0         0 $ret = $this->_encode_attr(BGP_PATH_ATTR_AGGREGATOR, $aggr);
841             } elsif ($aggr <= 65535) {
842 0         0 $aggr = pack('n', $this->{_aggregator}->[0]) .
843             inet_aton($this->{_aggregator}->[1]);
844              
845 0         0 $ret = $this->_encode_attr(BGP_PATH_ATTR_AGGREGATOR, $aggr);
846             } else {
847 0         0 $aggr = pack('n', 23456) .
848             inet_aton($this->{_aggregator}->[1]);
849            
850 0         0 $ret = $this->_encode_attr(BGP_PATH_ATTR_AGGREGATOR, $aggr);
851            
852 0         0 $aggr = pack('N', $this->{_aggregator}->[0]) .
853             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   2 my $this = shift();
864 2         2 my ($as, $val, $community, @communities);
865 0         0 my ($buffer, $community_buffer);
866              
867 2         2 @communities = @{$this->{_communities}};
  2         4  
868 2         3 foreach $community ( @communities ) {
869 2         7 ($as, $val) = split(/\:/, $community);
870 2         6 $community_buffer .= pack('nn', $as, $val);
871             }
872              
873 2         4 $this->_encode_attr(BGP_PATH_ATTR_COMMUNITIES, $community_buffer);
874             }
875              
876             sub _encode_path_attributes
877             {
878 12     12   19 my ($this, $options) = @_;
879              
880 12 50       34 if (!defined($options)) { $options = {}; }
  0         0  
881 12   100     43 $options->{as4} ||= 0;
882              
883 12         22 my $buffer;
884              
885 12         21 $buffer = '';
886              
887             # do not encode path attributes if no NLRI is present
888 12 100 50     51 unless ((defined $this->{_nlri})
  12         35  
889             && scalar(@{$this->{_nlri}})) {
890 1         3 return ( $buffer );
891             }
892              
893             # encode the ORIGIN path attribute
894 11 50       43 if ( ! defined($this->{_origin}) ) {
895 0         0 carp "mandatory path attribute ORIGIN not defined\n";
896             }
897 11         32 $buffer = $this->_encode_origin();
898              
899             # encode the AS_PATH path attribute
900 11 50       40 if ( ! defined($this->{_as_path}) ) {
901 0         0 carp "mandatory path attribute AS_PATH not defined\n";
902             }
903 11         31 $buffer .= $this->_encode_as_path($options);
904              
905             # encode the NEXT_HOP path attribute
906 11 50       44 if ( ! defined($this->{_next_hop}) ) {
907 0         0 carp "mandatory path attribute NEXT_HOP not defined\n";
908             }
909 11         39 $buffer .= $this->_encode_next_hop();
910              
911             # encode the MULTI_EXIT_DISC path attribute
912 11 100       44 if ( defined($this->{_med}) ) {
913 2         5 $buffer .= $this->_encode_med();
914             }
915              
916             # encode the LOCAL_PREF path attribute
917 11 100       54 if ( defined($this->{_local_pref}) ) {
918 2         4 $buffer .= $this->_encode_local_pref();
919             }
920              
921             # encode the ATOMIC_AGGREGATE path attribute
922 11 50       43 if ( defined($this->{_atomic_agg}) ) {
923 0         0 $buffer .= $this->_encode_atomic_aggregate();
924             }
925              
926             # encode the AGGREGATOR path attribute
927 11 50       12 if ( scalar(@{$this->{_aggregator}}) ) {
  11         42  
928 0         0 $buffer .= $this->_encode_aggregator($options);
929             }
930              
931             # encode the COMMUNITIES path attribute
932 11 100       16 if ( scalar(@{$this->{_communities}}) ) {
  11         34  
933 2         5 $buffer .= $this->_encode_communities();
934             }
935              
936 11         38 return ( $buffer );
937             }
938              
939             ## POD ##
940              
941             =pod
942              
943             =head1 NAME
944              
945             Net::BGP::Update - 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 B sends an UPDATE message to its peer, it does so
988             by passing a B 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             B 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 Net::BGP::Update 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 Net::BGP::Update 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 Net::BGP::NLRI 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 Net::BGP::Update object
1049              
1050             $clone = $update->clone();
1051              
1052             This method creates an exact copy of the Net::BGP::Update object, 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 in found in the nlri
1071             and withdrawn fields. Withdrawn networks has undefined as value, while nlri
1072             prefixes all has the same reference to a Net::BGP::NLRI object matching the
1073             Update object self.
1074              
1075             =head1 EXPORTS
1076              
1077             The module does not export anything.
1078              
1079             =head1 SEE ALSO
1080              
1081             B, B, B, B, B,
1082             B, B
1083              
1084             =head1 AUTHOR
1085              
1086             Stephen J. Scheck
1087              
1088             =cut
1089              
1090             ## End Package Net::BGP::Update ##
1091              
1092             1;