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   2180 use bytes;
  4         7  
  4         21  
5              
6 4     4   120 use strict;
  4         7  
  4         108  
7 4         284 use vars qw(
8             $VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS
9             @BGP_PATH_ATTR_FLAGS
10 4     4   20 );
  4         6  
11              
12             ## Inheritance and Versioning ##
13              
14 4     4   973 use Net::BGP::NLRI qw( :origin );
  4         8  
  4         606  
15              
16             @ISA = qw( Exporter Net::BGP::NLRI );
17             $VERSION = '0.17';
18              
19             ## Module Imports ##
20              
21 4     4   33 use Carp;
  4         8  
  4         264  
22 4     4   27 use IO::Socket;
  4         8  
  4         27  
23 4     4   2020 use Net::BGP::Notification qw( :errors );
  4         9  
  4         19149  
24              
25             ## General Definitions ##
26              
27 30     30 0 101 sub TRUE { 1 }
28 0     0 0 0 sub FALSE { 0 }
29              
30             ## BGP Path Attribute Type Enumerations ##
31              
32 30     30 0 88 sub BGP_PATH_ATTR_ORIGIN { 1 }
33 35     35 0 72 sub BGP_PATH_ATTR_AS_PATH { 2 }
34 29     29 0 114 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 8 sub BGP_PATH_ATTR_COMMUNITIES { 8 }
40 3     3 0 8 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 1118 my $proto = shift;
98 19   33     80 my $class = ref $proto || $proto;
99              
100 19 100       59 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         11 $this->withdrawn(shift);
107 1         4 return $this;
108             };
109              
110 18         58 my ($arg, $value);
111 18         0 my @super_arg;
112 18         0 my %this_arg;
113 18         49 $this_arg{_withdrawn} = [];
114 18         38 $this_arg{_nlri} = [];
115              
116 18         60 while ( defined($arg = shift()) ) {
117 3         6 $value = shift();
118              
119 3 100       14 if ( $arg =~ /nlri/i ) {
    100          
120 1         5 $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         93 my $this = $class->SUPER::new(@super_arg);
131              
132 18         55 @{$this}{keys %this_arg} = values(%this_arg);
  18         237  
133              
134 18         40 bless($this, $class);
135              
136 18         49 return ( $this );
137             }
138              
139             sub clone
140             {
141 5     5 0 614 my $proto = shift;
142 5   66     16 my $class = ref $proto || $proto;
143 5 100       48 $proto = shift unless ref $proto;
144              
145 5         20 my $clone = $class->SUPER::clone($proto);
146              
147 5         11 foreach my $key (qw(_nlri _withdrawn ))
148             {
149 10         12 $clone->{$key} = [ @{$proto->{$key}} ];
  10         25  
150             }
151              
152 5         16 return ( bless($clone, $class) );
153             }
154              
155             sub nlri
156             {
157 7     7 0 322 my $this = shift();
158              
159 7 100       53 $this->{_nlri} = @_ ? shift() : $this->{_nlri};
160 7         28 return ( $this->{_nlri} );
161             }
162              
163             sub withdrawn
164             {
165 7     7 0 18 my $this = shift();
166              
167 7 100       19 $this->{_withdrawn} = @_ ? shift() : $this->{_withdrawn};
168 7         24 return ( $this->{_withdrawn} );
169             }
170              
171             sub ashash
172             {
173 1     1 0 3 my $this = shift();
174              
175 1         2 my (%res,$nlri);
176              
177 1 50       7 $nlri = clone Net::BGP::NLRI($this) if defined($this->{_nlri});
178              
179 1         17 foreach my $prefix (@{$this->{_nlri}})
  1         7  
180             {
181 1         3 $res{$prefix} = $nlri;
182             };
183              
184 1         2 foreach my $prefix (@{$this->withdrawn})
  1         3  
185             {
186 1         3 $res{$prefix} = undef;
187             };
188              
189 1         3 return \%res;
190             }
191              
192             ## Private Methods ##
193              
194             sub _new_from_msg
195             {
196 16     16   3529 my ($class, $buffer, $options) = @_;
197            
198 16 100       53 if (!defined($options)) { $options = {}; }
  10         19  
199 16   100     75 $options->{as4} ||= 0;
200              
201 16         50 my $this = $class->new();
202              
203 16         52 $this->_decode_message($buffer, $options);
204              
205 15         63 return $this;
206             }
207              
208             sub _encode_attr
209             {
210 39     39   97 my ($this, $type, $data) = @_;
211 39         66 my $buffer = '';
212              
213 39         60 my $flag = $BGP_PATH_ATTR_FLAGS[$type];
214 39         48 my $len_format = 'C';
215              
216 39         62 my $len = length($data);
217 39 50       97 if ($len > 255)
218             {
219 0         0 $flag |= $BGP_PATH_ATTR_FLAG_EXTLEN;
220 0         0 $len_format = 'n';
221             }
222              
223 39         101 $buffer .= pack('CC', $flag, $type);
224 39         63 $buffer .= pack($len_format, $len);
225 39         63 $buffer .= $data;
226              
227 39         103 return ( $buffer );
228             }
229              
230             sub _decode_message
231             {
232 16     16   45 my ($this, $buffer, $options) = @_;
233            
234 16 50       135 if (!defined($options)) { $options = {}; }
  0         0  
235 16   100     62 $options->{as4} ||= 0;
236              
237 16         84 my $offset = 0;
238 16         23 my $length;
239              
240             # decode the Withdrawn Routes field
241 16         67 $length = unpack('n', substr($buffer, $offset, 2));
242 16         30 $offset += 2;
243              
244 16 50       55 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         75 $this->_decode_withdrawn(substr($buffer, $offset, $length));
252 16         31 $offset += $length;
253              
254             # decode the Path Attributes field
255 16         46 $length = unpack('n', substr($buffer, $offset, 2));
256 16         29 $offset += 2;
257              
258 16 50       51 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       43 return if $length == 0; # withdrawn routes only
266              
267 15         74 $this->_decode_path_attributes(
268             substr($buffer, $offset, $length),
269             $options
270             );
271              
272 14         32 $offset += $length;
273              
274             # decode the Network Layer Reachability Information field
275 14         52 $this->_decode_nlri(substr($buffer, $offset));
276             }
277              
278             sub _decode_origin
279             {
280 15     15   55 my ($this, $buffer) = @_;
281              
282 15         40 $this->{_origin} = unpack('C', $buffer);
283 15         40 $this->{_attr_mask}->[BGP_PATH_ATTR_ORIGIN] ++;
284              
285 15         34 return ( undef );
286             }
287              
288             sub _decode_as_path
289             {
290 17     17   48 my ($this, $buffer, $options) = @_;
291              
292 17 100       51 if (!defined($options)) { $options = {}; }
  3         6  
293 17   100     69 $options->{as4} ||= 0;
294              
295 17         33 $this->{_as_path_raw} = $buffer;
296              
297 17         37 my $as4path = '';
298 17 100       78 if ( exists $this->{_as4_path_raw} ) {
299 3         7 $as4path = $this->{_as4_path_raw};
300             }
301              
302 17         66 my $path = Net::BGP::ASPath->_new_from_msg(
303             $buffer,
304             $as4path,
305             $options
306             );
307              
308 17         64 $this->{_as_path} = $path;
309 17         45 $this->{_attr_mask}->[BGP_PATH_ATTR_AS_PATH] ++;
310              
311 17         41 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         7 $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         17 $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   54 my ($this, $buffer) = @_;
339 14         23 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         115 $this->{_next_hop} = inet_ntoa($buffer);
352 14         51 $this->{_attr_mask}->[BGP_PATH_ATTR_NEXT_HOP] ++;
353              
354 14         28 return ( undef );
355             }
356              
357             sub _decode_med
358             {
359 2     2   6 my ($this, $buffer) = @_;
360 2         3 my ($data);
361              
362 2 50       12 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         7 $this->{_med} = unpack('N', $buffer);
372 2         5 $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   7 my ($this, $buffer) = @_;
380 2         4 my ($data);
381              
382 2 50       15 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         7 $this->{_local_pref} = unpack('N', $buffer);
392 2         6 $this->{_attr_mask}->[BGP_PATH_ATTR_LOCAL_PREF] ++;
393              
394 2         3 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   6 my ($this, $buffer) = @_;
504 2         6 my ($as, $val, $ii, $offset, $count);
505 2         0 my ($data);
506              
507 2 50       6 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         10 $count = length($buffer) / 4;
518 2         9 for ( $ii = 0; $ii < $count; $ii++ ) {
519 2         5 $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         15  
522 2         7 $offset += 4;
523             }
524              
525 2         6 $this->{_attr_mask}->[BGP_PATH_ATTR_COMMUNITIES] ++;
526              
527 2         4 return ( undef );
528             }
529              
530             sub _decode_path_attributes
531             {
532 15     15   50 my ($this, $buffer, $options) = @_;
533              
534 15 50       40 if (!defined($options)) { $options = {}; }
  0         0  
535 15   100     59 $options->{as4} ||= 0;
536              
537 15         53 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         89 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         24 $offset = 0;
563 15         25 $data_length = length($buffer);
564              
565 15         38 while ( $data_length ) {
566 53         141 $flags = unpack('C', substr($buffer, $offset++, 1));
567 53         116 $type = unpack('C', substr($buffer, $offset++, 1));
568              
569 53         88 $len_format = 'C';
570 53         65 $len_bytes = 1;
571 53 50       118 if ( $flags & $BGP_PATH_ATTR_FLAG_EXTLEN ) {
572 0         0 $len_format = 'n';
573 0         0 $len_bytes = 2;
574             }
575              
576 53         96 $length = unpack($len_format, substr($buffer, $offset, $len_bytes));
577 53         80 $offset += $len_bytes;
578              
579 53 50       118 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         155 $error_data = substr(
592             $buffer,
593             $offset - $len_bytes - 2,
594             $length + $len_bytes + 2
595              
596             );
597              
598 53         80 my $flagmasked = $flags;
599 53         78 $flagmasked &= ~$BGP_PATH_ATTR_FLAG_EXTLEN;
600 53         75 $flagmasked &= ~$BGP_PATH_ATTR_FLAG_RESERVED;
601              
602 53 100       128 if ( $BGP_PATH_ATTR_FLAGS[$type] != $flagmasked ) {
603              
604             # See RFC4271 Section 5
605 3 100 66     16 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         89 $sub = $decode_sub[$type];
626 52         146 $this->$sub(substr($buffer, $offset, $length), $options);
627             }
628              
629 52         110 $offset += $length;
630 52         139 $data_length -= ($length + $len_bytes + 2);
631             }
632              
633             ## Check for missing mandatory well-known attributes
634             ##
635 14         34 for my $attr (@_BGP_MANDATORY_ATTRS)
636             {
637 42 50       90 $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       23 if ( grep { defined $_ and $_ > 1 } @{$this->{_attr_mask}||[]} )
  153 50       514  
  14 50       45  
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   66 my ($this, $buffer) = @_;
659 30         69 my ($offset, $data_length);
660 30         0 my ($prefix, $prefix_bits, $prefix_bytes, $ii, @prefix_list);
661              
662 30         43 $offset = 0;
663 30         45 $data_length = length($buffer);
664              
665 30         78 while ( $data_length ) {
666 25         81 $prefix_bits = unpack('C', substr($buffer, $offset++, 1));
667 25 100       107 $prefix_bytes = int($prefix_bits / 8) + (($prefix_bits % 8) ? 1 : 0);
668              
669 25 50       68 if ( $prefix_bytes > ($data_length - 1)) {
670 0         0 return ( FALSE );
671             }
672              
673 25         51 $prefix = 0;
674 25         94 for ( $ii = 0; $ii < $prefix_bytes; $ii++ ) {
675 83         242 $prefix |= (unpack('C', substr($buffer, $offset++, 1)) << (24 - ($ii * 8)));
676             }
677              
678 25         64 $prefix = pack('N', $prefix);
679 25         135 push(@prefix_list, inet_ntoa($prefix) . "/" . $prefix_bits);
680 25         69 $data_length -= ($prefix_bytes + 1);
681             }
682              
683 30         83 return ( TRUE, @prefix_list );
684             }
685              
686             sub _decode_withdrawn
687             {
688 16     16   60 my ($this, $buffer) = @_;
689 16         39 my ($result, @prefix_list);
690              
691 16         42 ($result, @prefix_list) = $this->_decode_prefix_list($buffer);
692 16 50       37 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         26 push(@{$this->{_withdrawn}}, @prefix_list);
  16         42  
700             }
701              
702             sub _decode_nlri
703             {
704 14     14   72 my ($this, $buffer) = @_;
705 14         32 my ($result, @prefix_list);
706              
707 14         36 ($result, @prefix_list) = $this->_decode_prefix_list($buffer);
708 14 50       39 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         20 push(@{$this->{_nlri}}, @prefix_list);
  14         59  
716             }
717              
718             sub _encode_message
719             {
720 12     12   99 my ($this, $options) = @_;
721              
722 12 100       33 if (!defined($options)) { $options = {}; }
  6         9  
723 12   100     53 $options->{as4} ||= 0;
724              
725 12         31 my ($buffer, $withdrawn, $path_attr, $nlri);
726              
727             # encode the Withdrawn Routes field
728 12         39 $withdrawn = $this->_encode_prefix_list($this->{_withdrawn});
729 12         39 $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         48 $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         50 return ( $buffer );
739             }
740              
741             sub _encode_prefix
742             {
743 22     22   38 my $prefix = shift();
744 22         38 my ($buffer, $length, @octets);
745              
746 22         83 ($prefix, $length) = split('/', $prefix);
747              
748 22         63 $buffer = pack('C', $length);
749              
750 22         124 @octets = split(/\./, $prefix);
751 22         55 while ( $length > 0 ) {
752 74         155 $buffer .= pack('C', shift(@octets));
753 74         164 $length -= 8;
754             }
755              
756 22         66 return ( $buffer );
757             }
758              
759             sub _encode_prefix_list
760             {
761 24     24   49 my ($this, $prefix_list) = @_;
762 24         30 my ($prefix, $buffer);
763              
764 24         39 $buffer = '';
765 24         45 foreach $prefix ( @{$prefix_list} ) {
  24         53  
766 22         52 $buffer .= _encode_prefix($prefix);
767             }
768              
769 24         62 return ( $buffer );
770             }
771              
772             sub _encode_origin
773             {
774 11     11   18 my $this = shift();
775              
776             $this->_encode_attr(BGP_PATH_ATTR_ORIGIN,
777 11         26 pack('C', $this->{_origin}));
778             }
779              
780             sub _encode_as_path
781             {
782 11     11   21 my ($this, $options) = @_;
783              
784 11 50       33 if (!defined($options)) { $options = {}; }
  0         0  
785 11   100     47 $options->{as4} ||= 0;
786              
787 11         51 my ($as_buffer, $as4_buffer) = $this->{_as_path}->_encode($options);
788              
789 11         24 my $output;
790              
791 11         33 $output = $this->_encode_attr(BGP_PATH_ATTR_AS_PATH, $as_buffer);
792              
793 11 50       40 if (defined $as4_buffer) {
794 0         0 $output .= $this->_encode_attr(BGP_PATH_ATTR_AS4_PATH, $as4_buffer);
795             }
796              
797 11         33 return $output;
798             }
799              
800             sub _encode_next_hop
801             {
802 11     11   29 my $this = shift();
803             $this->_encode_attr(BGP_PATH_ATTR_NEXT_HOP,
804 11         26 inet_aton($this->{_next_hop}));
805             }
806              
807             sub _encode_med
808             {
809 2     2   3 my $this = shift();
810             $this->_encode_attr(BGP_PATH_ATTR_MULTI_EXIT_DISC,
811 2         5 pack('N', $this->{_med}));
812             }
813              
814             sub _encode_local_pref
815             {
816 2     2   4 my $this = shift();
817             $this->_encode_attr(BGP_PATH_ATTR_LOCAL_PREF,
818 2         4 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         5 my ($as, $val, $community, @communities);
865 2         0 my ($buffer, $community_buffer);
866              
867 2         3 @communities = @{$this->{_communities}};
  2         7  
868 2         4 foreach $community ( @communities ) {
869 2         7 ($as, $val) = split(/\:/, $community);
870 2         17 $community_buffer .= pack('nn', $as, $val);
871             }
872              
873 2         7 $this->_encode_attr(BGP_PATH_ATTR_COMMUNITIES, $community_buffer);
874             }
875              
876             sub _encode_path_attributes
877             {
878 12     12   21 my ($this, $options) = @_;
879              
880 12 50       41 if (!defined($options)) { $options = {}; }
  0         0  
881 12   100     57 $options->{as4} ||= 0;
882              
883 12         20 my $buffer;
884              
885 12         23 $buffer = '';
886              
887             # do not encode path attributes if no NLRI is present
888 12 100 50     36 unless ((defined $this->{_nlri})
889 12         43 && scalar(@{$this->{_nlri}})) {
890 1         3 return ( $buffer );
891             }
892              
893             # encode the ORIGIN path attribute
894 11 50       38 if ( ! defined($this->{_origin}) ) {
895 0         0 carp "mandatory path attribute ORIGIN not defined\n";
896             }
897 11         28 $buffer = $this->_encode_origin();
898              
899             # encode the AS_PATH path attribute
900 11 50       39 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       38 if ( ! defined($this->{_next_hop}) ) {
907 0         0 carp "mandatory path attribute NEXT_HOP not defined\n";
908             }
909 11         31 $buffer .= $this->_encode_next_hop();
910              
911             # encode the MULTI_EXIT_DISC path attribute
912 11 100       46 if ( defined($this->{_med}) ) {
913 2         5 $buffer .= $this->_encode_med();
914             }
915              
916             # encode the LOCAL_PREF path attribute
917 11 100       33 if ( defined($this->{_local_pref}) ) {
918 2         5 $buffer .= $this->_encode_local_pref();
919             }
920              
921             # encode the ATOMIC_AGGREGATE path attribute
922 11 50       72 if ( defined($this->{_atomic_agg}) ) {
923 0         0 $buffer .= $this->_encode_atomic_aggregate();
924             }
925              
926             # encode the AGGREGATOR path attribute
927 11 50       20 if ( scalar(@{$this->{_aggregator}}) ) {
  11         38  
928 0         0 $buffer .= $this->_encode_aggregator($options);
929             }
930              
931             # encode the COMMUNITIES path attribute
932 11 100       19 if ( scalar(@{$this->{_communities}}) ) {
  11         29  
933 2         5 $buffer .= $this->_encode_communities();
934             }
935              
936 11         35 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;