| 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; |