File Coverage

blib/lib/Net/BGP/Transport.pm
Criterion Covered Total %
statement 434 593 73.1
branch 111 210 52.8
condition 9 32 28.1
subroutine 99 118 83.9
pod 7 56 12.5
total 660 1009 65.4


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package Net::BGP::Transport;
4 4     4   31 use bytes;
  4         7  
  4         27  
5              
6 4     4   128 use strict;
  4         9  
  4         105  
7 4     4   23 use Errno qw(EAGAIN);
  4         6  
  4         613  
8 4         3936 use vars qw(
9             $VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS @BGP @GENERIC
10             @BGP_EVENT_MESSAGE_MAP @BGP_EVENTS @BGP_FSM @BGP_STATES
11 4     4   28 );
  4         9  
12              
13             ## Inheritance and Versioning ##
14              
15             @ISA = qw( Exporter );
16             $VERSION = '0.18';
17              
18             ## General Definitions ##
19              
20 65     65 0 162 sub TRUE { 1 }
21 74     74 0 145 sub FALSE { 0 }
22              
23             ## BGP Network Constants ##
24              
25 0     0 0 0 sub BGP_PORT { 179 }
26 11     11 0 45 sub BGP_VERSION_4 { 4 }
27              
28             ## BGP General Constant Definitions ##
29              
30 114     114 0 370 sub BGP_MESSAGE_HEADER_LENGTH { 19 }
31 20     20 0 103 sub BGP_MAX_MESSAGE_LENGTH { 4096 }
32 6     6 0 16 sub BGP_CONNECT_RETRY_TIME { 120 }
33 6     6 0 63 sub BGP_HOLD_TIME { 90 }
34 6     6 0 15 sub BGP_KEEPALIVE_TIME { 30 }
35              
36             ## BGP Finite State Machine State Enumerations ##
37              
38 22     22 0 85 sub BGP_STATE_IDLE { 1 }
39 4     4 0 8 sub BGP_STATE_CONNECT { 2 }
40 0     0 0 0 sub BGP_STATE_ACTIVE { 3 }
41 4     4 0 10 sub BGP_STATE_OPEN_SENT { 4 }
42 22     22 0 84 sub BGP_STATE_OPEN_CONFIRM { 5 }
43 64     64 0 250 sub BGP_STATE_ESTABLISHED { 6 }
44              
45             ## BGP State Names ##
46              
47             @BGP_STATES = qw( Null Idle Connect Active OpenSent OpenConfirm Established );
48              
49             ## BGP Event Enumerations ##
50              
51 5     5 0 31 sub BGP_EVENT_START { 1 }
52 0     0 0 0 sub BGP_EVENT_STOP { 2 }
53 4     4 0 9 sub BGP_EVENT_TRANSPORT_CONN_OPEN { 3 }
54 0     0 0 0 sub BGP_EVENT_TRANSPORT_CONN_CLOSED { 4 }
55 0     0 0 0 sub BGP_EVENT_TRANSPORT_CONN_OPEN_FAILED { 5 }
56 0     0 0 0 sub BGP_EVENT_TRANSPORT_FATAL_ERROR { 6 }
57 76     76 0 147 sub BGP_EVENT_CONNECT_RETRY_TIMER_EXPIRED { 7 }
58 76     76 0 169 sub BGP_EVENT_HOLD_TIMER_EXPIRED { 8 }
59 76     76 0 216 sub BGP_EVENT_KEEPALIVE_TIMER_EXPIRED { 9 }
60 4     4 0 14 sub BGP_EVENT_RECEIVE_OPEN_MESSAGE { 10 }
61 12     12 0 28 sub BGP_EVENT_RECEIVE_KEEP_ALIVE_MESSAGE { 11 }
62 4     4 0 14 sub BGP_EVENT_RECEIVE_UPDATE_MESSAGE { 12 }
63 4     4 0 11 sub BGP_EVENT_RECEIVE_NOTIFICATION_MESSAGE { 13 }
64 4     4 0 9 sub BGP_EVENT_RECEIVE_REFRESH_MESSAGE { 14 }
65              
66             ## BGP Event Names ##
67              
68             @BGP_EVENTS = (
69             'Null',
70             'BGP Start',
71             'BGP Stop',
72             'BGP Transport connection open',
73             'BGP Transport connection closed',
74             'BGP Transport connection open failed',
75             'BGP Transport fatal error',
76             'ConnectRetry timer expired',
77             'Hold Timer expired',
78             'KeepAlive timer expired',
79             'Receive OPEN message',
80             'Receive KEEPALIVE message',
81             'Receive UPDATE message',
82             'Receive NOTIFICATION message',
83             'Receive REFRESH message'
84             );
85              
86             ## BGP Protocol Message Type Enumerations ##
87              
88 26     26 0 110 sub BGP_MESSAGE_OPEN { 1 }
89 4     4 0 15 sub BGP_MESSAGE_UPDATE { 2 }
90 2     2 0 9 sub BGP_MESSAGE_NOTIFICATION { 3 }
91 28     28 0 77 sub BGP_MESSAGE_KEEPALIVE { 4 }
92 22     22 0 80 sub BGP_MESSAGE_REFRESH { 5 }
93              
94             ## BGP Open Optional Parameter Types ##
95              
96 0     0 0 0 sub BGP_OPTION_AUTH { 1 }
97 36     36 0 84 sub BGP_OPTION_CAPABILITIES { 2 }
98              
99             ## BGP Open Capabilities Parameter Types
100 23     23 0 58 sub BGP_CAPABILITY_MBGP { 1 }
101 23     23 0 67 sub BGP_CAPABILITY_REFRESH { 2 }
102 20     20 0 52 sub BGP_CAPABILITY_AS4 { 65 }
103 23     23 0 53 sub BGP_CAPABILITY_REFRESH_OLD { 128 }
104              
105             ## Event-Message Type Correlation ##
106              
107             @BGP_EVENT_MESSAGE_MAP = (
108             undef,
109             BGP_EVENT_RECEIVE_OPEN_MESSAGE,
110             BGP_EVENT_RECEIVE_UPDATE_MESSAGE,
111             BGP_EVENT_RECEIVE_NOTIFICATION_MESSAGE,
112             BGP_EVENT_RECEIVE_KEEP_ALIVE_MESSAGE,
113             BGP_EVENT_RECEIVE_REFRESH_MESSAGE
114             );
115              
116             ## BGP FSM State Transition Table ##
117              
118             @BGP_FSM = (
119             undef, # Null (zero placeholder)
120              
121             [ # Idle
122             \&_close_session, # Default transition
123             \&_handle_bgp_start_event # BGP_EVENT_START
124             ],
125             [ # Connect
126             \&_close_session, # Default transition
127             \&_ignore_start_event, # BGP_EVENT_START
128             undef, # BGP_EVENT_STOP
129             \&_handle_bgp_conn_open, # BGP_EVENT_TRANSPORT_CONN_OPEN
130             undef, # BGP_EVENT_TRANSPORT_CONN_CLOSED
131             \&_handle_connect_retry_restart, # BGP_EVENT_TRANSPORT_CONN_OPEN_FAILED
132             undef, # BGP_EVENT_TRANSPORT_FATAL_ERROR
133             \&_handle_bgp_start_event # BGP_EVENT_CONNECT_RETRY_TIMER_EXPIRED
134             ],
135             [ # Active
136             \&_close_session, # Default transition
137             \&_ignore_start_event, # BGP_EVENT_START
138             undef, # BGP_EVENT_STOP
139             \&_handle_bgp_conn_open, # BGP_EVENT_TRANSPORT_CONN_OPEN
140             undef, # BGP_EVENT_TRANSPORT_CONN_CLOSED
141             \&_handle_connect_retry_restart, # BGP_EVENT_TRANSPORT_CONN_OPEN_FAILED
142             undef, # BGP_EVENT_TRANSPORT_FATAL_ERROR
143             \&_handle_bgp_start_event # BGP_EVENT_CONNECT_RETRY_TIMER_EXPIRED
144             ],
145             [ # OpenSent
146             \&_handle_bgp_fsm_error, # Default transition
147             \&_ignore_start_event, # BGP_EVENT_START
148             \&_cease, # BGP_EVENT_STOP
149             undef, # BGP_EVENT_TRANSPORT_CONN_OPEN
150             \&_handle_open_sent_disconnect, # BGP_EVENT_TRANSPORT_CONN_CLOSED
151             undef, # BGP_EVENT_TRANSPORT_CONN_OPEN_FAILED
152             \&_close_session, # BGP_EVENT_TRANSPORT_FATAL_ERROR
153             undef, # BGP_EVENT_CONNECT_RETRY_TIMER_EXPIRED
154             \&_handle_hold_timer_expired, # BGP_EVENT_HOLD_TIMER_EXPIRED
155             undef, # BGP_EVENT_KEEPALIVE_TIMER_EXPIRED
156             \&_handle_bgp_open_received, # BGP_EVENT_RECEIVE_OPEN_MESSAGE
157             undef, # BGP_EVENT_RECEIVE_KEEP_ALIVE_MESSAGE
158             undef, # BGP_EVENT_RECEIVE_UPDATE_MESSAGE
159             \&_handle_receive_notification_message,# BGP_EVENT_RECEIVE_NOTIFICATION_MESSAGE
160             ],
161             [ # OpenConfirm
162             \&_handle_bgp_fsm_error, # Default transition
163             \&_ignore_start_event, # BGP_EVENT_START
164             \&_cease, # BGP_EVENT_STOP
165             undef, # BGP_EVENT_TRANSPORT_CONN_OPEN
166             \&_close_session, # BGP_EVENT_TRANSPORT_CONN_CLOSED
167             undef, # BGP_EVENT_TRANSPORT_CONN_OPEN_FAILED
168             \&_close_session, # BGP_EVENT_TRANSPORT_FATAL_ERROR
169             undef, # BGP_EVENT_CONNECT_RETRY_TIMER_EXPIRED
170             \&_handle_hold_timer_expired, # BGP_EVENT_HOLD_TIMER_EXPIRED
171             \&_handle_keepalive_expired, # BGP_EVENT_KEEPALIVE_TIMER_EXPIRED
172             undef, # BGP_EVENT_RECEIVE_OPEN_MESSAGE
173             \&_handle_receive_keepalive_message, # BGP_EVENT_RECEIVE_KEEP_ALIVE_MESSAGE
174             undef, # BGP_EVENT_RECEIVE_UPDATE_MESSAGE
175             \&_handle_receive_notification_message,# BGP_EVENT_RECEIVE_NOTIFICATION_MESSAGE
176             \&_handle_receive_refresh_message # BGP_EVENT_RECEIVE_REFRESH_MESSAGE
177             ],
178             [ # Established
179             \&_handle_bgp_fsm_error, # Default transition
180             \&_ignore_start_event, # BGP_EVENT_START
181             \&_cease, # BGP_EVENT_STOP
182             undef, # BGP_EVENT_TRANSPORT_CONN_OPEN
183             \&_close_session, # BGP_EVENT_TRANSPORT_CONN_CLOSED
184             undef, # BGP_EVENT_TRANSPORT_CONN_OPEN_FAILED
185             \&_close_session, # BGP_EVENT_TRANSPORT_FATAL_ERROR
186             undef, # BGP_EVENT_CONNECT_RETRY_TIMER_EXPIRED
187             \&_handle_hold_timer_expired, # BGP_EVENT_HOLD_TIMER_EXPIRED
188             \&_handle_keepalive_expired, # BGP_EVENT_KEEPALIVE_TIMER_EXPIRED
189             undef, # BGP_EVENT_RECEIVE_OPEN_MESSAGE
190             \&_handle_receive_keepalive_message, # BGP_EVENT_RECEIVE_KEEP_ALIVE_MESSAGE
191             \&_handle_receive_update_message, # BGP_EVENT_RECEIVE_UPDATE_MESSAGE
192             \&_handle_receive_notification_message,# BGP_EVENT_RECEIVE_NOTIFICATION_MESSAGE
193             \&_handle_receive_refresh_message # BGP_EVENT_RECEIVE_REFRESH_MESSAGE
194             ]
195             );
196              
197             ## Socket States ##
198              
199 66     66 0 201 sub AWAITING_HEADER_START { 1 }
200 12     12 0 37 sub AWAITING_HEADER_FRAGMENT { 2 }
201 24     24 0 50 sub AWAITING_MESSAGE_FRAGMENT { 3 }
202              
203             ## Export Tag Definitions ##
204              
205             @EXPORT = ();
206             @EXPORT_OK = ();
207             %EXPORT_TAGS = (
208             ALL => [ @EXPORT, @EXPORT_OK ]
209             );
210              
211             ## Module Imports ##
212              
213 4     4   34 use Scalar::Util qw( weaken );
  4         9  
  4         270  
214 4     4   26 use Errno qw(EINPROGRESS ENOTCONN);
  4         7  
  4         171  
215 4     4   24 use Exporter;
  4         8  
  4         162  
216 4     4   37 use IO::Socket;
  4         28  
  4         30  
217 4     4   2073 use Carp;
  4         17  
  4         248  
218 4     4   36 use Carp qw(cluck);
  4         9  
  4         205  
219 4     4   29 use Net::BGP::Notification qw( :errors );
  4         6  
  4         884  
220 4     4   29 use Net::BGP::Refresh;
  4         7  
  4         166  
221 4     4   23 use Net::BGP::Update;
  4         6  
  4         24130  
222              
223             ## Generic Subroutines ##
224              
225             # This subroutine was snicked from David Town's excellent Net::SNMP
226             # module and renamed as dump_hex(). Removed class dependence and made
227             # into standalone subroutine.
228              
229             sub dump_hex
230             {
231 0     0 0 0 my $data = shift();
232 0         0 my ($length, $offset, $line, $hex) = (0, 0, '', '');
233 0         0 my $string;
234              
235 0         0 $string = '';
236 0         0 $length = length($data);
237              
238 0         0 while ($length > 0) {
239 0 0       0 if ($length >= 16) {
240 0         0 $line = substr($data, $offset, 16);
241             } else {
242 0         0 $line = substr($data, $offset, $length);
243             }
244 0         0 $hex = unpack('H*', $line);
245 0         0 $hex .= ' ' x (32 - length($hex));
246 0         0 $hex = sprintf("%s %s %s %s " x 4, unpack('a2' x 16, $hex));
247 0         0 $line =~ s/[\x00-\x1f\x7f-\xff]/./g;
248 0         0 $string .= sprintf("[%03d] %s %s\n", $offset, uc($hex), $line);
249 0         0 $offset += 16;
250 0         0 $length -= 16;
251             }
252              
253 0         0 return ( $string );
254             }
255              
256             ## Public Class Methods ##
257              
258             sub new
259             {
260 6     6 1 392 my $class = shift();
261 6         13 my ($arg, $value);
262              
263 6         21 my $this = {
264             _parent => undef,
265             _sibling => undef,
266             _bgp_version => BGP_VERSION_4,
267             _fsm_state => BGP_STATE_IDLE,
268             _peer_refresh => FALSE,
269             _peer_as4 => FALSE,
270             _peer_mbgp => FALSE,
271             _peer_announced_id => undef,
272             _event_queue => [],
273             _message_queue => [],
274             _hold_time => BGP_HOLD_TIME,
275             _hold_timer => undef,
276             _keep_alive_time => BGP_KEEPALIVE_TIME,
277             _keep_alive_timer => undef,
278             _connect_retry_time => BGP_CONNECT_RETRY_TIME,
279             _connect_retry_timer => undef,
280             _peer_socket => undef,
281             _peer_socket_connected => FALSE, # is AWARE - Not established, not socket->connected!
282             _last_timer_update => undef,
283             _in_msg_buffer => '',
284             _in_msg_buf_state => AWAITING_HEADER_START,
285             _in_msg_buf_bytes_exp => 0,
286             _in_msg_buf_type => 0,
287             _out_msg_buffer => ''
288             };
289              
290 6         16 bless($this, $class);
291              
292 6         20 while ( defined($arg = shift()) ) {
293 15         23 $value = shift();
294              
295 15 100       72 if ( $arg =~ /start/i ) {
    100          
    50          
    50          
    50          
296 5         17 $this->start();
297             }
298             elsif ( $arg =~ /parent/i ) {
299 6         17 $this->{_parent} = $value;
300             }
301             elsif ( $arg =~ /holdtime/i ) {
302 0         0 $this->{_hold_time} = $value;
303             }
304             elsif ( $arg =~ /connectretrytime/i ) {
305 0         0 $this->{_connect_retry_time} = $value;
306             }
307             elsif ( $arg =~ /keepalivetime/i ) {
308 4         15 $this->{_keep_alive_time} = $value;
309             }
310             else {
311 0         0 croak "unrecognized argument $arg\n";
312             }
313             }
314              
315 6         19 return ( $this );
316             }
317              
318             ## Public Object Methods ##
319              
320             sub start
321             {
322 5     5 1 17 my $this = shift();
323 5         16 $this->_enqueue_event(BGP_EVENT_START);
324             }
325              
326             sub stop
327             {
328 6     6 1 10 my $this = shift();
329 6         122 $this->{_fsm_state} = $this->_cease();
330             }
331              
332             sub version
333             {
334 0     0 1 0 return shift->{_bgp_version};
335             }
336              
337             sub is_established
338             {
339 0 0   0 1 0 return ( (shift->{_fsm_state} == BGP_STATE_ESTABLISHED) ? 1 : 0 );
340             }
341              
342             sub can_refresh
343             {
344 1     1 0 333 return shift->{_peer_refresh};
345             }
346              
347             sub can_as4
348             {
349 2     2 0 7 return shift->{_peer_as4};
350             }
351              
352             sub can_mbgp
353             {
354 3     3 0 12 return shift->{_peer_mbgp};
355             }
356              
357             sub update
358             {
359 4     4 1 12 my ($this, $update) = @_;
360              
361 4         12 my $result = FALSE;
362 4 50       21 if ( $this->{_fsm_state} == BGP_STATE_ESTABLISHED ) {
363              
364 4         34 my $encoded = $update->_encode_message( { as4 => $this->{_peer_as4} } );
365              
366 4         21 my $buffer = $this->_encode_bgp_update_message($encoded);
367 4         17 $this->_send_msg($buffer);
368 4         14 $result = TRUE;
369             }
370              
371 4         14 return $result;
372             }
373              
374             sub refresh
375             {
376 2     2 1 259 my $this = shift;
377              
378 2         8 my ($refresh) = @_;
379 2 50       37 $refresh = Net::BGP::Refresh->new(@_) unless ref $refresh eq 'Net::BGP::Refresh';
380              
381 2         8 my $result = FALSE;
382 2 50 33     9 if (( $this->{_fsm_state} == BGP_STATE_ESTABLISHED ) && $this->{_peer_refresh}) {
383 2         13 my $buffer = $this->_encode_bgp_refresh_message($refresh->_encode_message());
384 2         19 $this->_send_msg($buffer);
385 2         9 $result = TRUE;
386             }
387              
388 2         29 return $result;
389             }
390              
391             sub parent
392             {
393 110     110 0 430 return shift->{_parent};
394             }
395              
396             sub sibling
397             {
398 238     238 0 317 my $this = shift();
399 238 50       757 return undef unless defined $this->{_sibling};
400 0 0       0 return undef unless $this->parent->transport eq $this;
401 0         0 return $this->{_sibling};
402             }
403              
404             ## Private Class Methods ##
405              
406             sub _clone
407             {
408 0     0   0 my $this = shift();
409              
410 0 0       0 croak 'Cannot have more than one clone at a time!' if defined $this->{_sibling};
411              
412 0         0 my $clone = {};
413 0         0 foreach my $key ( keys(%{ $this }) ) {
  0         0  
414 0         0 $clone->{$key} = $this->{$key};
415             }
416              
417 0         0 bless($clone, ref($this));
418              
419             # override some of the inherited properties
420 0         0 $clone->{_peer_refresh} = FALSE;
421 0         0 $clone->{_peer_announced_id} = undef;
422 0         0 $clone->{_hold_timer} = undef;
423 0         0 $clone->{_keep_alive_timer} = undef;
424 0         0 $clone->{_fsm_state} = BGP_STATE_CONNECT;
425 0         0 $clone->{_event_queue} = [];
426 0         0 $clone->{_message_queue} = [];
427 0         0 $clone->{_peer_socket} = undef;
428 0         0 $clone->{_peer_socket_connected}= FALSE;
429 0         0 $clone->{_connect_retry_timer} = undef;
430 0         0 $clone->{_last_timer_update} = undef;
431 0         0 $clone->{_in_msg_buffer} = '';
432 0         0 $clone->{_in_msg_buf_state} = AWAITING_HEADER_START;
433 0         0 $clone->{_in_msg_buf_bytes_exp} = 0;
434 0         0 $clone->{_in_msg_buf_type} = 0;
435 0         0 $clone->{_out_msg_buffer} = '';
436 0         0 $clone->{_sibling} = $this;
437 0         0 $this->{_sibling} = $clone;
438              
439 0 0       0 if ( $this->{_fsm_state} != BGP_STATE_IDLE ) {
440 0         0 $clone->start();
441             }
442              
443 0         0 return $clone;
444             }
445              
446             ## Private Object Methods ##
447              
448             ## This creates AND throws a ::Notification object.
449             sub _error
450             {
451 0     0   0 my $this = shift();
452              
453 0   0     0 Net::BGP::Notification->throw(
454             ErrorCode => shift(),
455             ErrorSubCode => shift() || BGP_ERROR_SUBCODE_NULL,
456             ErrorData => shift()
457             );
458             }
459              
460             sub _is_connected
461             {
462 66     66   88 my $this = shift();
463 66         336 return ( $this->{_peer_socket_connected} );
464             }
465              
466             sub _get_socket
467             {
468 80     80   124 my $this = shift();
469 80         145 return ( $this->{_peer_socket} );
470             }
471              
472             sub _set_socket
473             {
474 2     2   5 my ($this, $socket) = @_;
475 2         11 $this->{_peer_socket} = $socket;
476 2         6 $this->{_peer_socket_connected} = TRUE;
477             }
478              
479             sub _enqueue_event
480             {
481 33     33   59 my $this = shift();
482 33         50 push(@{ $this->{_event_queue} }, shift());
  33         121  
483             }
484              
485             sub _dequeue_event
486             {
487 108     108   161 my $this = shift();
488 108         162 return ( shift(@{ $this->{_event_queue} }) );
  108         425  
489             }
490              
491             sub _enqueue_message
492             {
493 12     12   16 my $this = shift();
494 12         19 push(@{ $this->{_message_queue} }, shift());
  12         42  
495             }
496              
497             sub _dequeue_message
498             {
499 12     12   20 my $this = shift();
500 12         14 return ( shift(@{ $this->{_message_queue} }) );
  12         68  
501             }
502              
503             sub _handle_event
504             {
505 32     32   59 my ($this, $event) = @_;
506              
507 32         51 my $state = my $next_state = $this->{_fsm_state};
508              
509 32   0     133 my $action =
510             $BGP_FSM[$state]->[$event]
511             || $BGP_FSM[$state]->[0] ## default action
512             || undef ;
513              
514 32         60 eval {
515 32 50       112 $next_state = $action->($this) if defined $action;
516             };
517 32 50       98 if (my $oops = $@)
518             {
519 0 0       0 if (UNIVERSAL::isa($oops, 'Net::BGP::Notification'))
520             {
521 0         0 $this->_kill_session($oops);
522 0         0 $next_state = BGP_STATE_IDLE;
523             }
524             else
525             {
526 0         0 die $oops;
527             }
528             }
529              
530             # transition to next state
531 32 50       86 $this->{_fsm_state} = $next_state if defined $next_state;
532              
533             ## trigger callbacks if we changed states
534 32 100       149 if ($next_state != $state)
535             {
536 18 100       31 if ( $state == BGP_STATE_ESTABLISHED )
    100          
537             {
538             ## session has terminated
539             ##
540 2         8 $this->parent->reset_callback(undef)
541             }
542             elsif ( $next_state == BGP_STATE_ESTABLISHED )
543             {
544             ## newly established session
545             ##
546 4         9 $this->parent->refresh_callback(undef);
547             }
548              
549             # trigger post-transition actions
550 18         70 $this->_trigger_post_transition_action($state, $next_state);
551             }
552             }
553              
554             sub _trigger_post_transition_action
555             {
556 18     18   38 my ($this, $pre_state, $pos_state) = @_;
557              
558             # TODO:
559             #
560             # This needs to be broken out into a separate table similar to $BGP_FSM
561             # which triggers actions prior to state transition. Or, alternately,
562             # $BGP_FSM could be augmented to have an array of subrefs, one each for the
563             # pre- and post- transition action, rather than the current scalar subref.
564             # But I'm too lazy to refactor the entire table right now, so just handle
565             # the single current use case of firing the ESTABLISHED callback...
566              
567 18 100 66     32 if (($pre_state == BGP_STATE_OPEN_CONFIRM) && ($pos_state == BGP_STATE_ESTABLISHED)) {
568 4         8 $this->parent->established_callback();
569             }
570             }
571              
572             sub _handle_pending_events
573             {
574 76     76   107 my $this = shift();
575 76         99 my $event;
576              
577             # flush the outbound message buffer
578 76 50       210 if ( length($this->{_out_msg_buffer}) ) {
579 0         0 $this->_send_msg();
580             }
581              
582 76         158 while ( defined($event = $this->_dequeue_event()) ) {
583 32         70 $this->_handle_event($event);
584             }
585             }
586              
587             sub _update_timers
588             {
589 76     76   126 my ($this, $delta) = @_;
590 76         123 my ($timer, $key, $min, $min_time);
591 76         162 my %timers = (
592             _connect_retry_timer => BGP_EVENT_CONNECT_RETRY_TIMER_EXPIRED,
593             _hold_timer => BGP_EVENT_HOLD_TIMER_EXPIRED,
594             _keep_alive_timer => BGP_EVENT_KEEPALIVE_TIMER_EXPIRED
595             );
596              
597 76         113 $min_time = 3600;
598 76 50       191 if ( length($this->{_out_msg_buffer}) ) {
599 0         0 $min_time = 0;
600             }
601              
602             # Update BGP timers
603 76         226 foreach $timer ( keys(%timers) ) {
604 228 100       467 if ( defined($this->{$timer}) ) {
605 118         162 $this->{$timer} -= $delta;
606              
607 118 100       211 if ( $this->{$timer} <= 0 ) {
608 4         18 $this->{$timer} = 0;
609 4         38 $this->_enqueue_event($timers{$timer});
610             }
611              
612 118 100       220 if ( $this->{$timer} < $min_time ) {
613 87         154 $min_time = $this->{$timer};
614             }
615             }
616             }
617              
618             # Got a sibling-child?
619 76 50       162 if (defined $this->sibling)
620             {
621 0         0 my $sibmin = $this->sibling->_update_timers($delta);
622 0 0       0 $min_time = $sibmin if $sibmin < $min_time;
623             }
624              
625 76         250 return $min_time;
626             }
627              
628             sub _send_msg
629             {
630 20     20   47 my ($this, $msg, $oktofail) = @_;
631              
632              
633 20 50       95 unless (defined $this->{_peer_socket}) {
634 0 0       0 return if $oktofail;
635 0         0 cluck $this->parent->asstring . ": Internal error - no _peer_socket - Connection is shutdown\n";
636 0         0 $this->_cease;
637 0         0 return;
638             }
639              
640 20         55 my $buffer = $this->{_out_msg_buffer} . $msg;
641 20         84 my $sent = $this->{_peer_socket}->syswrite($buffer);
642              
643 20 50       1149 if ( ! defined($sent) ) {
644 0 0       0 return if $oktofail; # In a _cease process - Don't complain...
645 0 0       0 if ($!{EAGAIN} == 0) {
646 0         0 warn $this->parent->asstring . ": Error on socket write: $! - Connection is shutdown\n";
647 0         0 $this->_cease;
648             }
649             else {
650 0         0 $this->{_out_msg_buffer} = $buffer;
651             }
652              
653 0         0 return;
654             }
655              
656 20         86 $this->{_out_msg_buffer} = substr($buffer, $sent);
657             }
658              
659             sub _handle_socket_read_ready
660             {
661 32     32   48 my $this = shift();
662              
663 32         48 my $socket = $this->{_peer_socket};
664              
665 32 50       77 unless (defined $socket) {
666 0         0 warn $this->parent->asstring . ": Connection lost - Connection is fully shutdown now\n";
667 0         0 $this->_close_session();
668 0         0 $this->parent->reset_callback();
669 0         0 return;
670             }
671              
672 32         65 my $conn_closed = FALSE;
673 32         60 my $buffer = $this->{_in_msg_buffer};
674              
675 32 100       74 if ( $this->{_in_msg_buf_state} == AWAITING_HEADER_START ) {
    50          
    50          
676 20         42 my $num_read = $socket->sysread($buffer, BGP_MESSAGE_HEADER_LENGTH, length($buffer));
677              
678 20 50       511 if ($!) { # Something went wrong with none-blocking connect()
679 0         0 $this->{_peer_socket} = $socket = undef;
680 0         0 $this->_enqueue_event(BGP_EVENT_TRANSPORT_CONN_OPEN_FAILED);
681 0         0 return;
682             }
683              
684 20 50       59 if ( $num_read == 0 ) {
    50          
685 0         0 $conn_closed = TRUE;
686             }
687             elsif ( $num_read != BGP_MESSAGE_HEADER_LENGTH ) {
688 0         0 $this->{_in_msg_buf_state} = AWAITING_HEADER_FRAGMENT;
689 0         0 $this->{_in_msg_buf_bytes_exp} = (BGP_MESSAGE_HEADER_LENGTH) - ($num_read);
690 0         0 $this->{_in_msg_buffer} = $buffer;
691             }
692             else {
693 20         74 $this->_decode_bgp_message_header($buffer);
694 20         43 $this->{_in_msg_buffer} = '';
695             }
696             }
697             elsif ( $this->{_in_msg_buf_state} == AWAITING_HEADER_FRAGMENT ) {
698 0         0 my $num_read = $socket->sysread($buffer, $this->{_in_msg_buf_bytes_exp}, length($buffer));
699 0 0       0 if ( $num_read == 0 ) {
    0          
700 0         0 $conn_closed = TRUE;
701             }
702             elsif ( $num_read == $this->{_in_msg_buf_bytes_exp} ) {
703 0         0 $this->_decode_bgp_message_header($buffer);
704 0         0 $this->{_in_msg_buffer} = '';
705             }
706             else {
707 0         0 $this->{_in_msg_buf_bytes_exp} -= $num_read;
708 0         0 $this->{_in_msg_buffer} = $buffer;
709             }
710             }
711             elsif ( $this->{_in_msg_buf_state} == AWAITING_MESSAGE_FRAGMENT ) {
712 12         44 my $num_read = $socket->sysread($buffer, $this->{_in_msg_buf_bytes_exp}, length($buffer));
713 12 50 33     303 if ( ($num_read == 0) && ($this->{_in_msg_buf_bytes_exp} != 0) ) {
    50          
714 0         0 $conn_closed = TRUE;
715             }
716             elsif ( $num_read == $this->{_in_msg_buf_bytes_exp} ) {
717 12         46 $this->_enqueue_message($buffer);
718 12         56 $this->_enqueue_event($BGP_EVENT_MESSAGE_MAP[$this->{_in_msg_buf_type}]);
719 12         23 $this->{_in_msg_buffer} = '';
720 12         24 $this->{_in_msg_buf_state} = AWAITING_HEADER_START;
721             }
722             else {
723 0         0 $this->{_in_msg_buf_bytes_exp} -= $num_read;
724 0         0 $this->{_in_msg_buffer} = $buffer;
725             }
726             }
727             else {
728 0         0 croak("unknown socket state!\n");
729             }
730              
731 32 50       151 if ( $conn_closed ) {
732 0         0 $this->_enqueue_event(BGP_EVENT_TRANSPORT_CONN_CLOSED);
733             }
734             }
735              
736             sub _handle_socket_write_ready
737             {
738 4     4   7 my $this = shift();
739 4 50       12 return unless defined($this->{_peer_socket}); # Might have been closed by _handle_socket_read_ready!
740 4         8 $this->{_peer_socket_connected} = TRUE;
741 4         12 $this->_enqueue_event(BGP_EVENT_TRANSPORT_CONN_OPEN);
742             }
743              
744             sub _handle_socket_error_condition
745             {
746 0     0   0 my $this = shift();
747 0         0 warn "_handle_socket_error_condition()\n" . $this->{_peer_socket}->error(), "\n";
748             }
749              
750             sub _close_session
751             {
752 8     8   12 my $this = shift();
753 8         14 my $socket = $this->{_peer_socket};
754              
755 8 100       18 if ( defined($socket) ) {
756 4         40 $socket->close();
757             }
758              
759 8         365 $this->{_peer_socket} = $socket = undef;
760 8         21 $this->{_peer_socket_connected} = FALSE;
761 8         19 $this->{_in_msg_buffer} = '';
762 8         14 $this->{_out_msg_buffer} = '';
763 8         161 $this->{_in_msg_buf_state} = AWAITING_HEADER_START;
764 8         11 $this->{_hold_timer} = undef;
765 8         18 $this->{_keep_alive_timer} = undef;
766 8         12 $this->{_connect_retry_timer} = undef;
767 8         19 $this->{_message_queue} = [];
768              
769 8         20 return ( BGP_STATE_IDLE );
770             }
771              
772             sub _kill_session
773             {
774 6     6   17 my ($this, $error) = @_;
775 6         9 my $buffer;
776              
777 6 100       18 if (defined($this->{_peer_socket})) {
778 2         11 $buffer = $this->_encode_bgp_notification_message(
779             $error->error_code(),
780             $error->error_subcode(),
781             $error->error_data()
782             );
783              
784 2         12 $this->_send_msg($buffer,1);
785             };
786 6         25 $this->_close_session();
787              
788             # invoke user callback function
789 6         14 $this->parent->error_callback($error);
790             }
791              
792             sub _ignore_start_event
793             {
794 0     0   0 my $this = shift();
795 0         0 return ( $this->{_fsm_state} );
796             }
797              
798             sub _handle_receive_keepalive_message
799             {
800 8     8   14 my $this = shift();
801              
802             # restart Hold Timer
803 8 50       25 if ( $this->{_hold_time} != 0 ) {
804 8         14 $this->{_hold_timer} = $this->{_hold_time};
805             }
806              
807             # invoke user callback function
808 8         18 $this->parent->keepalive_callback();
809              
810 8         46 return ( BGP_STATE_ESTABLISHED );
811             }
812              
813             sub _handle_receive_update_message
814             {
815 4     4   9 my $this = shift();
816 4         9 my ($buffer, $update);
817              
818             # restart Hold Timer
819 4 50       14 if ( $this->{_hold_time} != 0 ) {
820 4         10 $this->{_hold_timer} = $this->{_hold_time};
821             }
822              
823 4         11 $buffer = $this->_dequeue_message();
824             $update = Net::BGP::Update->_new_from_msg(
825             $buffer,
826             { as4 => $this->{_peer_as4} }
827 4         57 );
828              
829             # invoke user callback function
830 4         16 $this->parent->update_callback($update);
831              
832 4         29 return ( BGP_STATE_ESTABLISHED );
833             }
834              
835             sub _handle_receive_refresh_message
836             {
837 2     2   7 my $this = shift();
838 2         6 my ($buffer, $refresh);
839              
840             # restart Hold Timer
841 2 50       28 if ( $this->{_hold_time} != 0 ) {
842 2         6 $this->{_hold_timer} = $this->{_hold_time};
843             }
844              
845 2         9 $buffer = $this->_dequeue_message();
846 2         17 $refresh = Net::BGP::Refresh->_new_from_msg($buffer);
847              
848 2 50       9 unless ( $this->parent->this_can_refresh ) {
849 0         0 Net::BGP::Notification->throw(
850             ErrorCode => BGP_ERROR_CODE_FINITE_STATE_MACHINE
851             );
852             }
853              
854             # invoke user callback function
855 2         10 $this->parent->refresh_callback($refresh);
856              
857 2         18 return ( BGP_STATE_ESTABLISHED );
858             }
859              
860             sub _handle_receive_notification_message
861             {
862 2     2   6 my $this = shift();
863 2         5 my $error;
864              
865 2         7 $error = $this->_decode_bgp_notification_message($this->_dequeue_message());
866 2         11 $this->_close_session();
867              
868             # invoke user callback function
869 2         9 $this->parent->notification_callback($error);
870              
871 2         6 return ( BGP_STATE_IDLE );
872             }
873              
874             sub _handle_keepalive_expired
875             {
876 4     4   10 my $this = shift();
877 4         5 my $buffer;
878              
879             # send KEEPALIVE message to peer
880 4         16 $buffer = $this->_encode_bgp_keepalive_message();
881 4         49 $this->_send_msg($buffer);
882              
883             # restart KeepAlive timer
884 4         10 $this->{_keep_alive_timer} = $this->{_keep_alive_time};
885              
886 4         14 return ( $this->{_fsm_state} );
887             }
888              
889             sub _handle_hold_timer_expired
890             {
891 0     0   0 my $this = shift();
892              
893 0         0 $this->_error(BGP_ERROR_CODE_HOLD_TIMER_EXPIRED);
894             }
895              
896             sub _handle_bgp_fsm_error
897             {
898 0     0   0 my $this = shift();
899              
900 0         0 $this->_error(BGP_ERROR_CODE_FINITE_STATE_MACHINE);
901             }
902              
903             sub _handle_bgp_conn_open
904             {
905 4     4   6 my $this = shift();
906 4         6 my $buffer;
907              
908             # clear ConnectRetry timer
909 4         15 $this->{_connect_retry_timer} = undef;
910              
911             # send OPEN message to peer
912 4         13 $buffer = $this->_encode_bgp_open_message();
913 4         14 $this->_send_msg($buffer);
914              
915 4         11 return ( BGP_STATE_OPEN_SENT );
916             }
917              
918             sub _handle_collision_selfdestuct
919             {
920 0     0   0 my $this = shift;
921 0         0 $this->stop();
922 0         0 $this->parent->transport($this->{_sibling});
923 0         0 $this->{_sibling}->{_sibling} = undef;
924             }
925              
926             sub _handle_bgp_open_received
927             {
928 4     4   9 my $this = shift();
929 4         7 my ($buffer, $this_id, $peer_id);
930              
931 4 50       11 if ( ! $this->_decode_bgp_open_message($this->_dequeue_message()) ) {
932             ; # do failure stuff
933 0         0 return ( BGP_STATE_IDLE );
934             }
935              
936             # check for connection collision
937 4 50       13 if ( defined($this->{_sibling}) ) {
938 0 0 0     0 if ( ($this->{_sibling}->{_fsm_state} == BGP_STATE_OPEN_SENT) ||
    0          
939             ($this->{_sibling}->{_fsm_state} == BGP_STATE_OPEN_CONFIRM) ) {
940              
941 0         0 $this_id = unpack('N', inet_aton($this->parent->this_id));
942 0         0 $peer_id = unpack('N', inet_aton($this->parent->peer_id));
943              
944 0 0       0 if ( $this_id < $peer_id ) {
945 0         0 $this->_handle_collision_selfdestuct;
946 0         0 return ( BGP_STATE_IDLE );
947             }
948             else {
949 0         0 $this->{_sibling}->_handle_collision_selfdestuct;
950             }
951             }
952             elsif ( ($this->{_sibling}->{_fsm_state} == BGP_STATE_ESTABLISHED) ) {
953 0         0 $this->_handle_collision_selfdestuct;
954 0         0 return ( BGP_STATE_IDLE );
955             }
956             else { # Other in Idle, conect, active
957 0         0 $this->{_sibling}->_handle_collision_selfdestuct;
958             }
959             }
960              
961             # clear the message buffer after decoding and validation
962 4         20 $this->{_message} = undef;
963              
964             # send KEEPALIVE message to peer
965 4         41 $buffer = $this->_encode_bgp_keepalive_message();
966 4         13 $this->_send_msg($buffer);
967              
968             # set Hold Time and KeepAlive timers
969 4 50       12 if ( $this->{_hold_time} != 0 ) {
970 4         10 $this->{_hold_timer} = $this->{_hold_time};
971 4         7 $this->{_keep_alive_timer} = $this->{_keep_alive_time};
972             }
973              
974             # invoke user callback function
975 4         10 $this->parent->open_callback();
976              
977             # transition to state OpenConfirm
978 4         799 return ( BGP_STATE_OPEN_CONFIRM );
979             }
980              
981             sub _handle_open_sent_disconnect
982             {
983 0     0   0 my $this = shift();
984              
985 0         0 $this->_close_session();
986 0         0 return ( $this->_handle_connect_retry_restart() );
987             }
988              
989             sub _handle_connect_retry_restart
990             {
991 0     0   0 my $this = shift();
992              
993             # restart ConnectRetry timer
994 0         0 $this->{_connect_retry_timer} = $this->{_connect_retry_time};
995              
996 0         0 return ( BGP_STATE_ACTIVE );
997             }
998              
999             sub _handle_bgp_start_event
1000             {
1001 4     4   7 my $this = shift();
1002 4         16 my ($socket, $proto, $remote_addr, $this_addr, $rv);
1003              
1004             # initialize ConnectRetry timer
1005 4 100       12 if ( ! $this->parent->is_passive ) {
1006 2         17 $this->{_connect_retry_timer} = $this->{_connect_retry_time};
1007             }
1008              
1009             # initiate the TCP transport connection
1010 4 100       12 if ( ! $this->parent->is_passive ) {
1011 2         5 eval {
1012 2         8 $socket = IO::Socket->new( Domain => AF_INET );
1013 2 50       373 if ( ! defined($socket) ) {
1014 0         0 die("IO::Socket construction failed");
1015             }
1016              
1017 2         91 $proto = getprotobyname('tcp');
1018 2         14 $rv = $socket->socket(PF_INET, SOCK_STREAM, $proto);
1019 2 50       106 if ( ! defined($rv) ) {
1020 0         0 die("socket() failed");
1021             }
1022              
1023 2         8 $this_addr = sockaddr_in(0, inet_aton($this->parent->this_id));
1024 2         21 $rv = $socket->bind($this_addr);
1025 2 50       56 if ( ! $rv ) {
1026 0         0 die("bind() failed");
1027             }
1028              
1029 2         7 $rv = $socket->blocking(FALSE);
1030 2 50       33 if ( ! defined($rv) ) {
1031 0         0 die("set socket non-blocking failed");
1032             }
1033              
1034 2         7 $remote_addr = sockaddr_in($this->parent->peer_port, inet_aton($this->parent->peer_id));
1035 2         19 $rv = $socket->connect($remote_addr);
1036 2 50       373 if ( ! defined($rv) ) {
1037 0 0       0 die "OK - but connect() failed: $!" unless ($! == EINPROGRESS);
1038             }
1039              
1040             # $rv = $socket->blocking(TRUE);
1041             # if ( ! defined($rv) ) {
1042             # die("set socket blocking failed");
1043             # }
1044             };
1045              
1046             # check for exception in transport initiation
1047 2 50       6 if ( $@ ) {
1048 0 0       0 carp $@ unless $@ =~ /^OK/;
1049 0 0       0 if ( defined($socket) ) {
1050 0         0 $socket->close();
1051             }
1052 0         0 $this->{_peer_socket} = $socket = undef;
1053 0         0 $this->_enqueue_event(BGP_EVENT_TRANSPORT_CONN_OPEN_FAILED);
1054             }
1055              
1056 2         5 $this->{_peer_socket} = $socket;
1057 2         5 $this->{_peer_socket_connected} = FALSE;
1058             }
1059              
1060 4         10 return ( BGP_STATE_CONNECT );
1061             }
1062              
1063             sub _min
1064             {
1065 5     5   16 my ($a, $b) = @_;
1066 5 50       16 return ( ($a < $b) ? $a : $b );
1067             }
1068              
1069             sub _cease
1070             {
1071 6     6   13 my $this = shift();
1072              
1073 6 50       100 if ( $this->{_fsm_state} == BGP_STATE_ESTABLISHED ) {
1074 6         16 $this->parent->reset_callback();
1075             }
1076              
1077 6         136 my $error = Net::BGP::Notification->new( ErrorCode => BGP_ERROR_CODE_CEASE );
1078              
1079 6         30 $this->_kill_session($error);
1080              
1081 6         19 return ( BGP_STATE_IDLE );
1082             }
1083              
1084             sub _encode_bgp_message
1085             {
1086 22     22   59 my ($this, $type, $payload) = @_;
1087 22         37 my ($buffer, $length);
1088              
1089 22         32 $buffer = '';
1090 22         43 $length = BGP_MESSAGE_HEADER_LENGTH;
1091              
1092 22 100       54 if ( defined($payload) ) {
1093 14         35 $length += length($payload);
1094 14         27 $buffer = $payload;
1095             }
1096              
1097             # encode the type field
1098 22         80 $buffer = pack('C', $type) . $buffer;
1099              
1100             # encode the length field
1101 22         56 $buffer = pack('n', $length) . $buffer;
1102              
1103             # encode the marker field
1104 22 50       59 if ( defined($this->{_auth_data}) ) {
1105 0         0 $buffer = $this->{_auth_data} . $buffer;
1106             }
1107             else {
1108 22         59 $buffer = (pack('C', 0xFF) x 16) . $buffer;
1109             }
1110              
1111 22         67 return ( $buffer );
1112             }
1113              
1114             sub _decode_bgp_message_header
1115             {
1116 20     20   64 my ($this, $header) = @_;
1117 20         49 my ($marker, $length, $type);
1118              
1119             # validate the BGP message header length
1120 20 50       45 if ( length($header) != BGP_MESSAGE_HEADER_LENGTH ) {
1121 0         0 $this->_error(
1122             BGP_ERROR_CODE_MESSAGE_HEADER,
1123             BGP_ERROR_SUBCODE_BAD_MSG_LENGTH,
1124             pack('n', length($header))
1125             );
1126             }
1127              
1128             # decode and validate the message header Marker field
1129 20         69 $marker = substr($header, 0, 16);
1130 20 50       62 if ( $marker ne (pack('C', 0xFF) x 16) ) {
1131 0         0 $this->_error(BGP_ERROR_CODE_MESSAGE_HEADER,
1132             BGP_ERROR_SUBCODE_CONN_NOT_SYNC);
1133             }
1134              
1135             # decode and validate the message header Length field
1136 20         102 $length = unpack('n', substr($header, 16, 2));
1137 20 50 33     49 if ( ($length < BGP_MESSAGE_HEADER_LENGTH) || ($length > BGP_MAX_MESSAGE_LENGTH) ) {
1138 0         0 $this->_error(
1139             BGP_ERROR_CODE_MESSAGE_HEADER,
1140             BGP_ERROR_SUBCODE_BAD_MSG_LENGTH,
1141             pack('n', $length)
1142             );
1143             }
1144              
1145             # decode and validate the message header Type field
1146 20         75 $type = unpack('C', substr($header, 18, 1));
1147 20 50 33     53 if ( ($type < BGP_MESSAGE_OPEN) || ($type > BGP_MESSAGE_REFRESH) ) {
1148 0         0 $this->_error(
1149             BGP_ERROR_CODE_MESSAGE_HEADER,
1150             BGP_ERROR_SUBCODE_BAD_MSG_TYPE,
1151             pack('C', $type)
1152             );
1153             }
1154              
1155 20 100       58 if ( $type == BGP_MESSAGE_KEEPALIVE ) {
1156 8         21 $this->{_in_msg_buffer} = '';
1157 8         17 $this->{_in_msg_buf_state} = AWAITING_HEADER_START;
1158 8         15 $this->{_in_msg_buf_bytes_exp} = 0;
1159 8         15 $this->{_in_msg_buf_type} = 0;
1160 8         20 $this->_enqueue_event(BGP_EVENT_RECEIVE_KEEP_ALIVE_MESSAGE);
1161             }
1162             else {
1163 12         30 $this->{_in_msg_buf_state} = AWAITING_MESSAGE_FRAGMENT;
1164 12         236 $this->{_in_msg_buf_bytes_exp} = $length - BGP_MESSAGE_HEADER_LENGTH;
1165 12         30 $this->{_in_msg_buf_type} = $type;
1166             }
1167              
1168             # indicate decoding and validation success
1169 20         65 return ( TRUE );
1170             }
1171              
1172             sub _encode_bgp_open_message
1173             {
1174 6     6   13 my $this = shift();
1175 6         9 my ($buffer, $length);
1176              
1177             # encode optional parameters and length
1178 6         10 my $opt = '';
1179              
1180 6 50       11 if ($this->parent->support_capabilities) {
1181              
1182 6 100       17 if ( defined($this->{_peer_announced_id}) ) {
1183             # We received an open from the other end
1184              
1185 2 50       16 if ($this->{_peer_mbgp}) {
1186 2         7 $opt .= $this->_encode_capability_mbgp();
1187             }
1188              
1189 2 100       18 if ($this->{_peer_as4}) {
1190 1         29 $opt .= $this->_encode_capability_as4();
1191             }
1192              
1193             } else {
1194             # We are sending the open
1195              
1196 4 50       23 if ( $this->parent->support_mbgp ) {
1197 4         11 $opt .= $this->_encode_capability_mbgp();
1198             }
1199 4 100       9 if ( $this->parent->this_can_as4 ) {
1200 2         11 $opt .= $this->_encode_capability_as4();
1201             }
1202              
1203             }
1204              
1205             # Both the standard (2) and Cisco (128) capabilities are sent
1206 6 50       12 if ($this->parent->this_can_refresh) {
1207 6         12 $opt .= $this->_encode_capability(BGP_CAPABILITY_REFRESH, '');
1208 6         13 $opt .= $this->_encode_capability(BGP_CAPABILITY_REFRESH_OLD, '');
1209             }
1210             }
1211              
1212 6         15 $buffer = pack('C', length($opt)) . $opt;
1213              
1214             # encode BGP Identifier field
1215 6         12 $buffer = inet_aton($this->parent->this_id) . $buffer;
1216              
1217             # encode Hold Time
1218 6         20 $buffer = pack('n', $this->{_hold_time}) . $buffer;
1219              
1220             # encode local Autonomous System number
1221 6 100       11 if ($this->parent->this_as > 65535) {
1222 3         5 $buffer = pack('n', 23456) . $buffer;
1223             } else {
1224 3         7 $buffer = pack('n', $this->parent->this_as) . $buffer;
1225             }
1226              
1227             # encode BGP version
1228 6         16 $buffer = pack('C', $this->{_bgp_version}) . $buffer;
1229              
1230 6         17 return ( $this->_encode_bgp_message(BGP_MESSAGE_OPEN, $buffer) );
1231             }
1232              
1233             sub _encode_capability_mbgp
1234             {
1235 6     6   10 my $this = shift;
1236              
1237             # Capability 1 with data of:
1238             # Address family 1 (IPv4), reserved bit 0, type 1 (unicast)
1239 6         12 my $cap = pack('ncc', 1, 0, 1);
1240 6         20 my $opt = $this->_encode_capability(BGP_CAPABILITY_MBGP, $cap);
1241              
1242 6         14 return $opt;
1243             }
1244              
1245             sub _encode_capability_as4
1246             {
1247 3     3   6 my $this = shift;
1248              
1249             # Capability 65 with data of the ASN
1250 3         4 my $cap = pack('N', $this->parent->this_as());
1251 3         7 my $opt = $this->_encode_capability(BGP_CAPABILITY_AS4, $cap);
1252              
1253 3         14 return $opt;
1254             }
1255              
1256             # Encodes a capability (inside the capability option)
1257             # RFC5492
1258             # Format is <2>
1259             sub _encode_capability
1260             {
1261 21     21   37 my ($this, $type, $data) = @_;
1262              
1263 21         28 my $opt = '';
1264 21         33 $opt .= pack('C', BGP_OPTION_CAPABILITIES); # Option Type
1265              
1266 21         32 my $cap = '';
1267 21         26 $cap .= pack('C', $type); # Capability Type
1268 21         32 $cap .= pack('C', length($data)); # Capability Data Len
1269 21         23 $cap .= $data; # Capability data
1270              
1271 21         28 $opt .= pack('C', length($cap)); # Option Data Len
1272 21         24 $opt .= $cap;
1273              
1274 21         45 return $opt;
1275             }
1276              
1277             sub _decode_bgp_open_message
1278             {
1279 5     5   393 my ($this, $buffer) = @_;
1280 5         22 my ($version, $as, $hold_time, $bgp_id);
1281              
1282             # decode and validate BGP version
1283 5         25 $version = unpack('C', substr($buffer, 0, 1));
1284 5 50       28 if ( $version != BGP_VERSION_4 ) {
1285 0         0 $this->_error(
1286             BGP_ERROR_CODE_OPEN_MESSAGE,
1287             BGP_ERROR_SUBCODE_BAD_VERSION_NUM,
1288             pack('n', BGP_VERSION_4)
1289             );
1290             }
1291              
1292             # decode and validate remote Autonomous System number
1293 5         23 $as = unpack('n', substr($buffer, 1, 2));
1294 5 100       18 if ( $as != $this->parent->peer_as ) {
1295 3 50       8 if ($this->parent->peer_as < 65536) {
    50          
1296 0         0 $this->_error(BGP_ERROR_CODE_OPEN_MESSAGE,
1297             BGP_ERROR_SUBCODE_BAD_PEER_AS);
1298             } elsif ($as != 23456) {
1299 0         0 $this->_error(BGP_ERROR_CODE_OPEN_MESSAGE,
1300             BGP_ERROR_SUBCODE_BAD_PEER_AS);
1301             }
1302             }
1303              
1304             # decode and validate received Hold Time
1305 5         28 $hold_time = _min(unpack('n', substr($buffer, 3, 2)), $this->{_hold_time});
1306 5 50 33     19 if ( ($hold_time < 3) && ($hold_time != 0) ) {
1307 0         0 $this->_error(BGP_ERROR_CODE_OPEN_MESSAGE,
1308             BGP_ERROR_SUBCODE_BAD_HOLD_TIME);
1309             }
1310              
1311             # decode received BGP Identifier
1312             # Spelling error is retained for compatibility.
1313 5         51 $this->{_peer_annonced_id} = inet_ntoa(substr($buffer, 5, 4));
1314 5         40 $this->{_peer_announced_id} = inet_ntoa(substr($buffer, 5, 4));
1315              
1316             # decode known Optional Parameters
1317 5         21 my $opt_length = unpack('c', substr($buffer, 9, 1));
1318 5         26 my $opt = substr($buffer, 10, $opt_length);
1319 5         94 while ($opt ne '')
1320             {
1321 15         74 my ($type, $length) = unpack('cc', substr($opt, 0, 2));
1322 15         42 my $value = substr($opt, 2, $length);
1323 15 50       30 if ($type eq BGP_OPTION_CAPABILITIES)
1324             {
1325 15         37 $this->_decode_capabilities($value);
1326             }
1327             else
1328             { # Unknown optional parameter!
1329             # XXX We should send a notify here.
1330             }
1331 15         56 $opt = substr($opt, 2+$length);
1332             };
1333              
1334             # set Hold Time to negotiated value
1335 5         9 $this->{_hold_time} = $hold_time;
1336              
1337             # indicate decoding and validation success
1338 5         11 return ( TRUE );
1339             }
1340              
1341             # Capabilities we don't understand get ignored.
1342             sub _decode_capabilities
1343             {
1344 15     15   47 my ($this, $value) = @_;
1345              
1346 15         91 $this->{'_peer_refresh'} = TRUE;
1347              
1348 15         41 while (length($value) > 0) {
1349              
1350 17 50       41 if (length($value) < 2) {
1351 0         0 $this->_error(BGP_ERROR_CODE_OPEN_MESSAGE,
1352             BGP_ERROR_SUBCODE_BAD_OPT_PARAMETER);
1353 0         0 return;
1354             }
1355              
1356 17         60 my ($type, $len) = unpack('cc', substr($value, 0, 2));
1357 17         42 my $data = substr($value, 2, $len);
1358              
1359 17         106 $this->_decode_one_capability($type, $len, $data);
1360              
1361 17         80 $value = substr($value, 2+$len);
1362             }
1363              
1364             }
1365              
1366             sub _decode_one_capability {
1367 17     17   53 my ($this, $type, $len, $data) = @_;
1368              
1369 17 50       46 if (length($data) != $len) {
1370 0         0 $this->_error(BGP_ERROR_CODE_OPEN_MESSAGE,
1371             BGP_ERROR_SUBCODE_BAD_OPT_PARAMETER);
1372             }
1373              
1374 17 100       56 if ($type == BGP_CAPABILITY_MBGP) {
1375 5         14 $this->{_peer_mbgp} = TRUE;
1376             }
1377              
1378 17 100       29 if ($type == BGP_CAPABILITY_REFRESH) {
1379 5         98 $this->{_peer_refresh} = TRUE;
1380             }
1381 17 50       88 if ($type == BGP_CAPABILITY_REFRESH_OLD) {
1382 0         0 $this->{_peer_refresh} = TRUE;
1383             }
1384              
1385 17 100       30 if ($type == BGP_CAPABILITY_AS4) {
1386              
1387 3 50       10 if ($len != 4) {
1388 0         0 $this->_error(BGP_ERROR_CODE_OPEN_MESSAGE,
1389             BGP_ERROR_SUBCODE_BAD_OPT_PARAMETER);
1390             }
1391            
1392 3         8 my $as = unpack('N', $data);
1393 3 50       9 if ($as != $this->parent->peer_as) {
1394 0         0 $this->_error(BGP_ERROR_CODE_OPEN_MESSAGE,
1395             BGP_ERROR_SUBCODE_BAD_PEER_AS);
1396             }
1397              
1398             # Both ends must support this.
1399 3 50       9 if ( $this->parent->this_can_as4 ) {
1400 3         7 $this->{_peer_as4} = TRUE;
1401             }
1402             }
1403              
1404             }
1405              
1406             sub _decode_bgp_notification_message
1407             {
1408 2     2   8 my ($this, $buffer) = @_;
1409 2         7 my ($error, $error_code, $error_subcode, $data);
1410              
1411             # decode and validate Error code
1412 2         21 $error_code = unpack('C', substr($buffer, 0, 1));
1413 2 50 33     22 if ( ($error_code < 1) || ($error_code > 6) ) {
1414 0         0 die("_decode_bgp_notification_message(): invalid error code = $error_code\n");
1415             }
1416              
1417             # decode and validate Error subcode
1418 2         76 $error_subcode = unpack('C', substr($buffer, 1, 1));
1419 2 50 33     38 if ( ($error_subcode < 0) || ($error_subcode > 11) ) {
1420 0         0 die("_decode_bgp_notification_message(): invalid error subcode = $error_subcode\n");
1421             }
1422              
1423             # decode Data field
1424 2         19 $data = substr($buffer, 2, length($buffer) - 2);
1425              
1426 2         18 return Net::BGP::Notification->new(
1427             ErrorCode => $error_code,
1428             ErrorSubcode => $error_subcode,
1429             ErrorData => $data);
1430             }
1431              
1432             sub _encode_bgp_keepalive_message
1433             {
1434 8     8   20 my $this = shift();
1435 8         20 return ( $this->_encode_bgp_message(BGP_MESSAGE_KEEPALIVE) );
1436             }
1437              
1438             sub _encode_bgp_update_message
1439             {
1440 4     4   12 my ($this, $buffer) = @_;
1441 4         15 return ( $this->_encode_bgp_message(BGP_MESSAGE_UPDATE, $buffer) );
1442             }
1443              
1444             sub _encode_bgp_refresh_message
1445             {
1446 2     2   9 my ($this, $buffer) = @_;
1447 2         8 return ( $this->_encode_bgp_message(BGP_MESSAGE_REFRESH, $buffer) );
1448             }
1449              
1450             sub _encode_bgp_notification_message
1451             {
1452 2     2   9 my ($this, $error_code, $error_subcode, $data) = @_;
1453 2         4 my $buffer;
1454              
1455             # encode the Data field
1456 2 50       8 $buffer = $data ? $data : '';
1457              
1458             # encode the Error Subcode field
1459 2         11 $buffer = pack('C', $error_subcode) . $buffer;
1460              
1461             # encode the Error Code field
1462 2         7 $buffer = pack('C', $error_code) . $buffer;
1463              
1464 2         13 return ( $this->_encode_bgp_message(BGP_MESSAGE_NOTIFICATION, $buffer) );
1465             }
1466              
1467             ## POD ##
1468              
1469             =pod
1470              
1471             =head1 NAME
1472              
1473             C - Class encapsulating BGP-4 transport session state and functionality
1474              
1475             =head1 SYNOPSIS
1476              
1477             use Net::BGP::Transport;
1478              
1479             $trans = Net::BGP::Transport->new(
1480             Start => 1,
1481             Parent => Net::BGP::Peer->new(),
1482             ConnectRetryTime => 300,
1483             HoldTime => 60,
1484             KeepAliveTime => 20
1485             );
1486              
1487             $version = $trans->version();
1488              
1489             $trans->start();
1490             $trans->stop();
1491              
1492             $trans->update($update);
1493             $trans->refresh($refresh);
1494              
1495              
1496             =head1 DESCRIPTION
1497              
1498             This module encapsulates the state and functionality associated with a BGP
1499             transport connection. Each instance of a C object
1500             corresponds to a TCP session with a distinct peer. It should not be used by
1501             itself, but encapsulated in a L object.
1502              
1503             =head1 CONSTRUCTOR
1504              
1505             =over 4
1506              
1507             =item new() - create a new C object
1508              
1509             This is the constructor for C objects. It returns a
1510             reference to the newly created object. The following named parameters may
1511             be passed to the constructor. Once the object is created, the information
1512             can not be changed.
1513              
1514             =over 4
1515              
1516             =item Start
1517              
1518             =item ConnectRetryTime
1519              
1520             =item HoldTime
1521              
1522             =item KeepAliveTime
1523              
1524             Has the same meaning as the equivalent named argument for L.
1525              
1526             =item Parent
1527              
1528             The parent L object.
1529              
1530             =back
1531              
1532             =item renew() - fetch the existing L object from the "object string".
1533              
1534             This "reconstructor" returns a previously constructed object from the
1535             perl generated string-context scalar of the object, e.g.
1536             I.
1537              
1538             =back
1539              
1540             =head1 ACCESSOR METHODS
1541              
1542             =over 4
1543              
1544             =item version()
1545              
1546             =item start()
1547              
1548             =item stop()
1549              
1550             =item update()
1551              
1552             =item refresh()
1553              
1554             =item is_established()
1555              
1556             These methods do the actual work for the methods of the same name in
1557             L.
1558              
1559             =back
1560              
1561             =head1 SEE ALSO
1562              
1563             Net::BGP::Peer, Net::BGP, Net::BGP::Update, Net::BGP::Refresh
1564              
1565             =head1 AUTHOR
1566              
1567             Stephen J. Scheck in original Peer.pm form
1568             Martin Lorensen separated into Transport.pm
1569              
1570             =cut
1571              
1572             ## End Package Net::BGP::Transport ##
1573              
1574             1;