File Coverage

blib/lib/Net/BGP/Transport.pm
Criterion Covered Total %
statement 434 591 73.4
branch 110 210 52.3
condition 10 32 31.2
subroutine 99 118 83.9
pod 7 56 12.5
total 660 1007 65.5


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