| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #!/usr/bin/perl | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | package Net::BGP::ASPath::AS; | 
| 4 | 7 |  |  | 7 |  | 2936 | use bytes; | 
|  | 7 |  |  |  |  | 79 |  | 
|  | 7 |  |  |  |  | 42 |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 | 7 |  |  | 7 |  | 210 | use strict; | 
|  | 7 |  |  |  |  | 14 |  | 
|  | 7 |  |  |  |  | 123 |  | 
| 7 | 7 |  |  | 7 |  | 32 | use Carp; | 
|  | 7 |  |  |  |  | 15 |  | 
|  | 7 |  |  |  |  | 347 |  | 
| 8 | 7 |  |  | 7 |  | 40 | use Exporter; | 
|  | 7 |  |  |  |  | 20 |  | 
|  | 7 |  |  |  |  | 228 |  | 
| 9 | 7 |  |  |  |  | 483 | use vars qw( | 
| 10 |  |  |  |  |  |  | $VERSION @ISA | 
| 11 | 7 |  |  | 7 |  | 37 | ); | 
|  | 7 |  |  |  |  | 43 |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | use overload | 
| 14 | 7 |  |  |  |  | 73 | '<=>'      => \&compare, | 
| 15 |  |  |  |  |  |  | '""'       => \&as_string, | 
| 16 | 7 |  |  | 7 |  | 39 | 'fallback' => 1; | 
|  | 7 |  |  |  |  | 14 |  | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | # DO NOT OVERLOAD @{} - it's an array - we need this! | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | $VERSION = '0.17'; | 
| 21 |  |  |  |  |  |  |  | 
| 22 | 7 |  |  | 7 |  | 2938 | use Net::BGP::Notification qw( :errors ); | 
|  | 7 |  |  |  |  | 18 |  | 
|  | 7 |  |  |  |  | 10648 |  | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | @Net::BGP::ASPath::AS_SEQUENCE::ISA = qw( Exporter ); | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | ## BGP AS_PATH Path Attribute Type Classes ## | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | my @BGP_PATH_ATTR_CLASS = ( | 
| 29 |  |  |  |  |  |  | undef,                                    # unused | 
| 30 |  |  |  |  |  |  | 'Net::BGP::ASPath::AS_SET',               # BGP_PATH_ATTR_AS_SET | 
| 31 |  |  |  |  |  |  | 'Net::BGP::ASPath::AS_SEQUENCE',          # BGP_PATH_ATTR_AS_SEQUENCE | 
| 32 |  |  |  |  |  |  | 'Net::BGP::ASPath::AS_CONFED_SEQUENCE',   # BGP_PATH_ATTR_AS_CONFED_SEQUENCE | 
| 33 |  |  |  |  |  |  | 'Net::BGP::ASPath::AS_CONFED_SET'         # BGP_PATH_ATTR_AS_CONFED_SET | 
| 34 |  |  |  |  |  |  | ); | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | ## Public Class Methods ## | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | sub new { | 
| 39 | 664 |  |  | 664 | 0 | 2968 | my ($class, $value) = (shift, shift); | 
| 40 |  |  |  |  |  |  |  | 
| 41 | 664 | 50 |  |  |  | 1379 | return $value->clone if (ref $value) =~ /^Net::BGP::ASPath::AS_/; | 
| 42 |  |  |  |  |  |  |  | 
| 43 | 664 |  |  |  |  | 904 | my ($this, $realclass); | 
| 44 |  |  |  |  |  |  |  | 
| 45 | 664 | 100 |  |  |  | 1168 | $value = '' unless defined($value); | 
| 46 |  |  |  |  |  |  |  | 
| 47 | 664 | 50 |  |  |  | 1225 | if (ref $value eq 'HASH') { | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | # Construct SET from HASH | 
| 50 | 0 | 0 |  |  |  | 0 | croak "Hash argument given for a non-set AS_PATH element" | 
| 51 |  |  |  |  |  |  | unless $class =~ /_SET$/; | 
| 52 | 0 |  |  |  |  | 0 | $this->{ keys %{$value} } = values(%{$value}); | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 53 | 0 |  |  |  |  | 0 | bless($this, $class); | 
| 54 | 0 |  |  |  |  | 0 | return $this; | 
| 55 |  |  |  |  |  |  | } | 
| 56 |  |  |  |  |  |  |  | 
| 57 | 664 | 100 |  |  |  | 1166 | if (ref $value eq 'ARRAY') { | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | # Construct SET from HASH | 
| 60 | 301 | 100 |  |  |  | 856 | if ($class =~ /_SEQUENCE$/) { | 
| 61 | 208 |  |  |  |  | 284 | push(@{$this}, @{$value}); | 
|  | 208 |  |  |  |  | 346 |  | 
|  | 208 |  |  |  |  | 828 |  | 
| 62 |  |  |  |  |  |  | } else { | 
| 63 | 93 |  |  |  |  | 146 | $this = {}; | 
| 64 | 93 |  |  |  |  | 128 | foreach my $a (@{$value}) { $this->{$a} = 1; } | 
|  | 93 |  |  |  |  | 184 |  | 
|  | 229 |  |  |  |  | 405 |  | 
| 65 |  |  |  |  |  |  | } | 
| 66 | 301 |  |  |  |  | 506 | bless($this, $class); | 
| 67 | 301 |  |  |  |  | 1127 | return $this; | 
| 68 |  |  |  |  |  |  | } | 
| 69 |  |  |  |  |  |  |  | 
| 70 | 363 | 50 |  |  |  | 578 | croak "Unknown argument type (" | 
| 71 |  |  |  |  |  |  | . (ref $value) | 
| 72 |  |  |  |  |  |  | . ") parsed as argument to AS_PATH construtor." | 
| 73 |  |  |  |  |  |  | if (ref $value); | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | # Only a scalar left - Parse string! | 
| 76 | 363 |  |  |  |  | 464 | my $confed = ''; | 
| 77 | 363 | 100 | 100 |  |  | 1166 | if (   ($value =~ /^\((.*)\)$/) | 
|  |  |  | 100 |  |  |  |  | 
| 78 |  |  |  |  |  |  | || ($value eq '' && $class =~ /_CONFED_/)) | 
| 79 |  |  |  |  |  |  | { | 
| 80 | 134 | 100 |  |  |  | 346 | $value = $1 if defined($1); | 
| 81 | 134 |  |  |  |  | 205 | $confed = '_CONFED'; | 
| 82 |  |  |  |  |  |  | } | 
| 83 | 363 | 100 | 100 |  |  | 1616 | if (   ($value =~ /^\{([0-9,]*)\}$/) | 
|  |  | 50 | 100 |  |  |  |  | 
| 84 |  |  |  |  |  |  | || ($value eq '' && $class =~ /_SET$/)) | 
| 85 |  |  |  |  |  |  | { | 
| 86 | 102 | 100 |  |  |  | 253 | my $set = defined $1 ? $1 : $value; | 
| 87 | 102 |  |  |  |  | 188 | $realclass = 'Net::BGP::ASPath::AS' . $confed . '_SET'; | 
| 88 | 102 |  |  |  |  | 151 | $this      = {}; | 
| 89 | 102 |  |  |  |  | 268 | foreach my $a (split(/,/, $set)) { $this->{$a} = 1; } | 
|  | 270 |  |  |  |  | 500 |  | 
| 90 |  |  |  |  |  |  | } elsif ($value =~ /^[0-9 ]*$/) { | 
| 91 | 261 |  |  |  |  | 571 | $realclass = 'Net::BGP::ASPath::AS' . $confed . '_SEQUENCE'; | 
| 92 | 261 |  |  |  |  | 1404 | $this = [ split(' ', $value) ]; | 
| 93 |  |  |  |  |  |  | } else { | 
| 94 | 0 |  |  |  |  | 0 | croak "$value is not a valid AS_PATH segment"; | 
| 95 |  |  |  |  |  |  | } | 
| 96 |  |  |  |  |  |  |  | 
| 97 | 363 | 50 | 66 |  |  | 1315 | croak "AS_PATH segment is a $realclass but was constructed as $class" | 
| 98 |  |  |  |  |  |  | if $class !~ /::AS$/ && $class ne $realclass; | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 363 |  |  |  |  | 617 | bless($this, $realclass); | 
| 101 | 363 |  |  |  |  | 792 | return ($this); | 
| 102 |  |  |  |  |  |  | } | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | sub _new_from_msg | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | # Constructor - returns object AND buffer with data removed | 
| 107 |  |  |  |  |  |  | { | 
| 108 | 44 |  |  | 44 |  | 105 | my ($class, $buffer, $args) = @_; | 
| 109 |  |  |  |  |  |  |  | 
| 110 | 44 | 50 |  |  |  | 90 | if (!defined($args)) { $args = {}; } | 
|  | 0 |  |  |  |  | 0 |  | 
| 111 | 44 |  | 100 |  |  | 176 | $args->{as4} ||= 0; | 
| 112 |  |  |  |  |  |  |  | 
| 113 | 44 | 100 |  |  |  | 88 | my $size = $args->{as4} ? 4 : 2; | 
| 114 |  |  |  |  |  |  |  | 
| 115 | 44 |  |  |  |  | 127 | my ($type, $len) = unpack('CC', $buffer); | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 44 | 50 |  |  |  | 118 | if ( ($len * $size + 2) > length($buffer)) { | 
| 118 | 0 |  |  |  |  | 0 | Net::BGP::Notification->throw( | 
| 119 |  |  |  |  |  |  | ErrorCode    => BGP_ERROR_CODE_UPDATE_MESSAGE, | 
| 120 |  |  |  |  |  |  | ErrorSubCode => BGP_ERROR_SUBCODE_BAD_AS_PATH | 
| 121 |  |  |  |  |  |  | ); | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 44 |  |  |  |  | 66 | my @list; | 
| 125 | 44 | 100 |  |  |  | 78 | if ($args->{as4}) { | 
| 126 | 9 |  |  |  |  | 38 | @list = unpack('N*', substr($buffer,2,(4*$len)) ); | 
| 127 |  |  |  |  |  |  | } else { | 
| 128 | 35 |  |  |  |  | 132 | @list = unpack('n*', substr($buffer,2,(2*$len)) ); | 
| 129 |  |  |  |  |  |  | } | 
| 130 | 44 |  |  |  |  | 98 | $class = $BGP_PATH_ATTR_CLASS[$type]; | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 44 | 100 |  |  |  | 108 | if (length($buffer) > 2+($size*$len)) { | 
| 133 | 10 |  |  |  |  | 22 | $buffer = substr($buffer, 2+($size*$len)); | 
| 134 |  |  |  |  |  |  | } else { | 
| 135 | 34 |  |  |  |  | 54 | $buffer = ''; | 
| 136 |  |  |  |  |  |  | } | 
| 137 | 44 |  |  |  |  | 140 | return ($class->new(\@list), $buffer); | 
| 138 |  |  |  |  |  |  | } | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | # This encodes the standard AS Path | 
| 141 |  |  |  |  |  |  | # TODO: Note that if AS4 != True, then there is an issue with this code. | 
| 142 |  |  |  |  |  |  | # In particular, it will stick 23456 into the confederation types.  In | 
| 143 |  |  |  |  |  |  | # theory, no confederation using AS4 should be transmitting confed types | 
| 144 |  |  |  |  |  |  | # to any node that is NOT using AS4, per RFC4893. | 
| 145 |  |  |  |  |  |  | # | 
| 146 |  |  |  |  |  |  | # But when this breaks the internet, it's not my fault. | 
| 147 |  |  |  |  |  |  | sub _encode { | 
| 148 | 35 |  |  | 35 |  | 71 | my ($this, $args) = @_; | 
| 149 | 35 | 50 |  |  |  | 82 | if (!defined($args)) { $args = {}; } | 
|  | 0 |  |  |  |  | 0 |  | 
| 150 | 35 |  | 100 |  |  | 142 | $args->{as4} ||= 0; | 
| 151 |  |  |  |  |  |  |  | 
| 152 | 35 |  |  |  |  | 109 | my $list = $this->asarray; | 
| 153 | 35 |  |  |  |  | 52 | my $len  = scalar @{$list}; | 
|  | 35 |  |  |  |  | 63 |  | 
| 154 | 35 |  |  |  |  | 91 | my $type = $this->type; | 
| 155 |  |  |  |  |  |  |  | 
| 156 | 35 |  |  |  |  | 52 | my $msg; | 
| 157 | 35 | 100 |  |  |  | 82 | if (!($args->{as4})) { | 
| 158 | 29 |  |  |  |  | 81 | $msg = pack('CC', $type, $len); | 
| 159 | 29 |  |  |  |  | 41 | foreach my $as ( @{$list} ) { | 
|  | 29 |  |  |  |  | 50 |  | 
| 160 | 371 | 100 |  |  |  | 668 | $msg .= ($as <= 65535) ? pack('n', $as) : pack('n', 23456); | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  | } else { | 
| 163 | 6 |  |  |  |  | 11 | $msg = pack('CCN*', $type, $len, @{$list}); | 
|  | 6 |  |  |  |  | 23 |  | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  |  | 
| 166 | 35 |  |  |  |  | 135 | return $msg; | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | # Determines if the path element has any ASNs > 23456 | 
| 170 |  |  |  |  |  |  | sub _has_as4 { | 
| 171 | 33 |  |  | 33 |  | 58 | my ($this) = @_; | 
| 172 |  |  |  |  |  |  |  | 
| 173 | 33 | 100 |  |  |  | 121 | if ( ref($this) =~ /_CONFED_/) { | 
| 174 |  |  |  |  |  |  | # No confeds in AS4_ paths | 
| 175 | 10 |  |  |  |  | 31 | return 0; | 
| 176 |  |  |  |  |  |  | } | 
| 177 |  |  |  |  |  |  |  | 
| 178 | 23 |  |  |  |  | 69 | my $list = $this->asarray; | 
| 179 | 23 |  |  |  |  | 42 | foreach my $as ( @{$list} ) { | 
|  | 23 |  |  |  |  | 52 |  | 
| 180 | 342 | 100 |  |  |  | 609 | if ($as > 65535) { return 1; } | 
|  | 6 |  |  |  |  | 25 |  | 
| 181 |  |  |  |  |  |  | } | 
| 182 |  |  |  |  |  |  |  | 
| 183 | 17 |  |  |  |  | 70 | return 0; | 
| 184 |  |  |  |  |  |  | } | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | sub compare { | 
| 187 | 0 |  |  | 0 | 0 | 0 | my ($this, $other) = @_; | 
| 188 | 0 | 0 |  |  |  | 0 | return undef unless defined($other); | 
| 189 | 0 |  |  |  |  | 0 | return $this->length <=> $other->length; | 
| 190 |  |  |  |  |  |  | } | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | sub clone { | 
| 193 | 253 |  |  | 253 | 0 | 358 | my $proto = shift; | 
| 194 | 253 |  | 33 |  |  | 514 | my $class = ref $proto || $proto; | 
| 195 | 253 | 50 |  |  |  | 448 | $proto = shift unless ref $proto; | 
| 196 |  |  |  |  |  |  |  | 
| 197 | 253 |  |  |  |  | 320 | my $clone; | 
| 198 | 253 | 100 |  |  |  | 681 | if ($class =~ /_SET$/) { | 
| 199 | 81 |  |  |  |  | 113 | return $class->new([ keys %{$proto} ]); | 
|  | 81 |  |  |  |  | 350 |  | 
| 200 |  |  |  |  |  |  | } else { | 
| 201 | 172 |  |  |  |  | 227 | return $class->new([ @{$proto} ]);    # Unblessed! | 
|  | 172 |  |  |  |  | 807 |  | 
| 202 |  |  |  |  |  |  | } | 
| 203 |  |  |  |  |  |  | } | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | sub asstring { | 
| 206 | 0 |  |  | 0 | 0 | 0 | my $this = shift; | 
| 207 | 0 |  |  |  |  | 0 | return $this->as_string(@_); | 
| 208 |  |  |  |  |  |  | } | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | sub as_string { | 
| 211 | 8 |  |  | 8 | 0 | 15 | my $this = shift; | 
| 212 | 8 | 50 |  |  |  | 19 | croak 'Instance of ASPath::AS should not exist!' | 
| 213 |  |  |  |  |  |  | if (ref $this eq 'Net::BGP::ASPath::AS'); | 
| 214 | 8 |  |  |  |  | 19 | return $this->as_string; | 
| 215 |  |  |  |  |  |  | } | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | sub asarray { | 
| 218 | 0 |  |  | 0 | 0 |  | my $this = shift; | 
| 219 | 0 | 0 |  |  |  |  | croak 'Instance of ASPath::AS should not exist!' | 
| 220 |  |  |  |  |  |  | if (ref $this eq 'Net::BGP::ASPath::AS'); | 
| 221 | 0 |  |  |  |  |  | return $this->asarray; | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | 1; |