File Coverage

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


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package Net::BGP::ASPath::AS;
4 7     7   610 use bytes;
  7         19  
  7         36  
5              
6 7     7   185 use strict;
  7         9  
  7         164  
7 7     7   25 use Carp;
  7         8  
  7         337  
8 7     7   33 use Exporter;
  7         11  
  7         228  
9 7         515 use vars qw(
10             $VERSION @ISA
11 7     7   29 );
  7         13  
12              
13             use overload
14 7         57 '<=>' => \&compare,
15             '""' => \&as_string,
16 7     7   32 'fallback' => 1;
  7         9  
17              
18             # DO NOT OVERLOAD @{} - it's an array - we need this!
19              
20             $VERSION = '0.16';
21              
22 7     7   2874 use Net::BGP::Notification qw( :errors );
  7         12  
  7         8566  
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 2050 my ($class, $value) = (shift, shift);
40              
41 664 50       1176 return $value->clone if (ref $value) =~ /^Net::BGP::ASPath::AS_/;
42              
43 664         476 my ($this, $realclass);
44              
45 664 100       983 $value = '' unless defined($value);
46              
47 664 50       1079 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       1016 if (ref $value eq 'ARRAY') {
58              
59             # Construct SET from HASH
60 301 100       749 if ($class =~ /_SEQUENCE$/) {
61 208         186 push(@{$this}, @{$value});
  208         252  
  208         722  
62             } else {
63 93         98 $this = {};
64 93         99 foreach my $a (@{$value}) { $this->{$a} = 1; }
  93         142  
  229         386  
65             }
66 301         653 bless($this, $class);
67 301         1344 return $this;
68             }
69              
70 363 50       494 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         333 my $confed = '';
77 363 100 100     1217 if ( ($value =~ /^\((.*)\)$/)
      66        
78             || ($value eq '' && $class =~ /_CONFED_/))
79             {
80 134 100       292 $value = $1 if defined($1);
81 134         145 $confed = '_CONFED';
82             }
83 363 100 100     1691 if ( ($value =~ /^\{([0-9,]*)\}$/)
    50 66        
84             || ($value eq '' && $class =~ /_SET$/))
85             {
86 102 100       198 my $set = defined $1 ? $1 : $value;
87 102         139 $realclass = 'Net::BGP::ASPath::AS' . $confed . '_SET';
88 102         105 $this = {};
89 102         250 foreach my $a (split(/,/, $set)) { $this->{$a} = 1; }
  270         458  
90             } elsif ($value =~ /^[0-9 ]*$/) {
91 261         387 $realclass = 'Net::BGP::ASPath::AS' . $confed . '_SEQUENCE';
92 261         1219 $this = [ split(' ', $value) ];
93             } else {
94 0         0 croak "$value is not a valid AS_PATH segment";
95             }
96              
97 363 50 66     1267 croak "AS_PATH segment is a $realclass but was constructed as $class"
98             if $class !~ /::AS$/ && $class ne $realclass;
99              
100 363         649 bless($this, $realclass);
101 363         821 return ($this);
102             }
103              
104             sub _new_from_msg
105              
106             # Constructor - returns object AND buffer with data removed
107             {
108 44     44   69 my ($class, $buffer, $args) = @_;
109              
110 44 50       93 if (!defined($args)) { $args = {}; }
  0         0  
111 44   100     134 $args->{as4} ||= 0;
112              
113 44 100       95 my $size = $args->{as4} ? 4 : 2;
114              
115 44         129 my ($type, $len) = unpack('CC', $buffer);
116              
117 44 50       142 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         49 my @list;
125 44 100       95 if ($args->{as4}) {
126 9         42 @list = unpack('N*', substr($buffer,2,(4*$len)) );
127             } else {
128 35         152 @list = unpack('n*', substr($buffer,2,(2*$len)) );
129             }
130 44         81 $class = $BGP_PATH_ATTR_CLASS[$type];
131              
132 44 100       93 if (length($buffer) > 2+($size*$len)) {
133 10         22 $buffer = substr($buffer, 2+($size*$len));
134             } else {
135 34         44 $buffer = '';
136             }
137 44         147 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   49 my ($this, $args) = @_;
149 35 50       73 if (!defined($args)) { $args = {}; }
  0         0  
150 35   100     111 $args->{as4} ||= 0;
151              
152 35         122 my $list = $this->asarray;
153 35         42 my $len = scalar @{$list};
  35         45  
154 35         101 my $type = $this->type;
155              
156 35         34 my $msg;
157 35 100       90 if (!($args->{as4})) {
158 29         82 $msg = pack('CC', $type, $len);
159 29         30 foreach my $as ( @{$list} ) {
  29         44  
160 371 100       650 $msg .= ($as <= 65535) ? pack('n', $as) : pack('n', 23456);
161             }
162             } else {
163 6         8 $msg = pack('CCN*', $type, $len, @{$list});
  6         22  
164             }
165              
166 35         158 return $msg;
167             }
168              
169             # Determines if the path element has any ASNs > 23456
170             sub _has_as4 {
171 33     33   45 my ($this) = @_;
172            
173 33 100       120 if ( ref($this) =~ /_CONFED_/) {
174             # No confeds in AS4_ paths
175 10         37 return 0;
176             }
177              
178 23         58 my $list = $this->asarray;
179 23         33 foreach my $as ( @{$list} ) {
  23         38  
180 342 100       588 if ($as > 65535) { return 1; }
  6         29  
181             }
182              
183 17         81 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 226 my $proto = shift;
194 253   33     441 my $class = ref $proto || $proto;
195 253 50       369 $proto = shift unless ref $proto;
196              
197 253         177 my $clone;
198 253 100       555 if ($class =~ /_SET$/) {
199 81         64 return $class->new([ keys %{$proto} ]);
  81         347  
200             } else {
201 172         152 return $class->new([ @{$proto} ]); # Unblessed!
  172         849  
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 9 my $this = shift;
212 8 50       17 croak 'Instance of ASPath::AS should not exist!'
213             if (ref $this eq 'Net::BGP::ASPath::AS');
214 8         18 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;