File Coverage

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


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