File Coverage

blib/lib/Net/BGP/ASPath/AS.pm
Criterion Covered Total %
statement 105 123 85.3
branch 40 56 71.4
condition 19 22 86.3
subroutine 13 16 81.2
pod 0 6 0.0
total 177 223 79.3


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;