bgp4r 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +1 -10
- data/bgp/io.rb +4 -2
- data/bgp/messages/capability.rb +176 -0
- data/bgp/messages/keepalive.rb +30 -0
- data/bgp/messages/markers.rb +10 -0
- data/bgp/messages/message.rb +85 -0
- data/bgp/messages/messages.rb +17 -0
- data/bgp/messages/notification.rb +116 -0
- data/bgp/messages/open.rb +117 -0
- data/bgp/messages/route_refresh.rb +181 -0
- data/bgp/messages/update.rb +148 -0
- data/bgp/neighbor.rb +20 -11
- data/bgp/nlris/inet.rb +18 -0
- data/bgp/{label.rb → nlris/label.rb} +1 -1
- data/bgp/nlris/labeled.rb +39 -0
- data/bgp/nlris/nlri.rb +154 -0
- data/bgp/{attributes.rb → nlris/nlris.rb} +11 -13
- data/bgp/nlris/prefix.rb +33 -0
- data/bgp/{rd.rb → nlris/rd.rb} +1 -1
- data/bgp/nlris/vpn.rb +68 -0
- data/bgp/optional_parameters/as4.rb +59 -0
- data/bgp/optional_parameters/capabilities.rb +13 -0
- data/bgp/optional_parameters/capability.rb +145 -0
- data/bgp/optional_parameters/dynamic.rb +37 -0
- data/bgp/optional_parameters/graceful_restart.rb +116 -0
- data/bgp/optional_parameters/mbgp.rb +97 -0
- data/bgp/optional_parameters/optional_parameter.rb +76 -0
- data/bgp/optional_parameters/orf.rb +90 -0
- data/bgp/optional_parameters/route_refresh.rb +36 -0
- data/bgp/{prefix_orf.rb → orfs/prefix_orf.rb} +4 -10
- data/bgp/{aggregator.rb → path_attributes/aggregator.rb} +2 -2
- data/bgp/{as_path.rb → path_attributes/as_path.rb} +2 -2
- data/bgp/{atomic_aggregate.rb → path_attributes/atomic_aggregate.rb} +2 -2
- data/bgp/{attribute.rb → path_attributes/attribute.rb} +2 -2
- data/bgp/path_attributes/attributes.rb +21 -0
- data/bgp/{cluster_list.rb → path_attributes/cluster_list.rb} +2 -2
- data/bgp/{communities.rb → path_attributes/communities.rb} +2 -2
- data/bgp/{extended_communities.rb → path_attributes/extended_communities.rb} +3 -3
- data/bgp/{extended_community.rb → path_attributes/extended_community.rb} +1 -1
- data/bgp/{local_pref.rb → path_attributes/local_pref.rb} +2 -2
- data/bgp/{mp_reach.rb → path_attributes/mp_reach.rb} +3 -69
- data/bgp/path_attributes/mp_unreach.rb +97 -0
- data/bgp/{multi_exit_disc.rb → path_attributes/multi_exit_disc.rb} +2 -2
- data/bgp/{next_hop.rb → path_attributes/next_hop.rb} +2 -2
- data/bgp/{origin.rb → path_attributes/origin.rb} +2 -2
- data/bgp/{originator_id.rb → path_attributes/originator_id.rb} +2 -2
- data/bgp/{path_attribute.rb → path_attributes/path_attribute.rb} +6 -7
- data/bgp4r.gemspec +127 -67
- data/bgp4r.rb +5 -25
- data/examples/routegen +1 -1
- data/examples/routegen.yml +21 -5
- data/examples/simple.rb +4 -4
- data/examples/unit-testing/malformed_update.rb +1 -1
- data/examples/unit-testing/unknown_transitive_attr.rb +18 -10
- data/test/messages/capability_test.rb +113 -0
- data/test/messages/keepalive_test.rb +34 -0
- data/test/messages/markers_test.rb +33 -0
- data/test/messages/message_test.rb +43 -0
- data/test/messages/notification_test.rb +49 -0
- data/test/messages/open_test.rb +98 -0
- data/test/messages/route_refresh_test.rb +77 -0
- data/test/messages/update_test.rb +133 -0
- data/test/neighbor_test.rb +8 -9
- data/test/nlris/inet_test.rb +43 -0
- data/test/{label_test.rb → nlris/labeled_test.rb} +1 -1
- data/test/{nlri_test.rb → nlris/nlri_test.rb} +5 -1
- data/test/{rd_test.rb → nlris/rd_test.rb} +1 -2
- data/test/optional_parameters/as4_test.rb +34 -0
- data/test/optional_parameters/capability_test.rb +69 -0
- data/test/optional_parameters/dynamic_test.rb +34 -0
- data/test/optional_parameters/graceful_restart_test.rb +23 -0
- data/test/optional_parameters/mbgp_test.rb +35 -0
- data/test/optional_parameters/optional_parameter_test.rb +51 -0
- data/test/optional_parameters/orf_test.rb +42 -0
- data/test/optional_parameters/route_refresh_test.rb +32 -0
- data/test/{prefix_orf_test.rb → orfs/prefix_orf_test.rb} +2 -1
- data/test/{aggregator_test.rb → path_attributes/aggregator_test.rb} +1 -1
- data/test/{as_path_test.rb → path_attributes/as_path_test.rb} +1 -1
- data/test/{atomic_aggregate_test.rb → path_attributes/atomic_aggregate_test.rb} +1 -1
- data/test/{attribute_test.rb → path_attributes/attribute_test.rb} +2 -2
- data/test/{cluster_list_test.rb → path_attributes/cluster_list_test.rb} +1 -1
- data/test/{communities_test.rb → path_attributes/communities_test.rb} +1 -1
- data/test/{extended_communities_test.rb → path_attributes/extended_communities_test.rb} +1 -1
- data/test/{extended_community_test.rb → path_attributes/extended_community_test.rb} +1 -1
- data/test/{local_pref_test.rb → path_attributes/local_pref_test.rb} +1 -1
- data/test/{mp_reach_test.rb → path_attributes/mp_reach_test.rb} +1 -37
- data/test/path_attributes/mp_unreach_test.rb +67 -0
- data/test/{multi_exit_disc_test.rb → path_attributes/multi_exit_disc_test.rb} +1 -1
- data/test/{next_hop_test.rb → path_attributes/next_hop_test.rb} +1 -1
- data/test/{origin_test.rb → path_attributes/origin_test.rb} +2 -1
- data/test/{originator_id_test.rb → path_attributes/originator_id_test.rb} +1 -1
- data/test/{path_attribute_test.rb → path_attributes/path_attribute_test.rb} +2 -2
- metadata +122 -67
- data/bgp/message.rb +0 -921
- data/bgp/nlri.rb +0 -303
- data/bgp/version.rb +0 -3
- data/examples/unit-testing/keepalive_set_to_zeo.rb +0 -64
- data/test/message_test.rb +0 -347
- /data/bgp/{orf.rb → orfs/orf.rb} +0 -0
data/README.rdoc
CHANGED
|
@@ -227,18 +227,9 @@ Source code is hosted on github.
|
|
|
227
227
|
|
|
228
228
|
== Installation
|
|
229
229
|
|
|
230
|
-
==== Run the following if you haven't already:
|
|
231
|
-
|
|
232
|
-
gem sources -a http://gems.github.com
|
|
233
|
-
|
|
234
230
|
==== Install the gem
|
|
235
231
|
|
|
236
|
-
sudo gem install
|
|
237
|
-
|
|
238
|
-
=== Or clone bgp4r tree and set RUBYLIB env variable accordingly:
|
|
239
|
-
|
|
240
|
-
git clone git://github.com/jesnault/bgp4r.git
|
|
241
|
-
export RUBYLIB=/home/user/bgp4r
|
|
232
|
+
sudo gem install bgp4r
|
|
242
233
|
|
|
243
234
|
== Requirements
|
|
244
235
|
|
data/bgp/io.rb
CHANGED
|
@@ -23,8 +23,10 @@
|
|
|
23
23
|
|
|
24
24
|
require 'observer'
|
|
25
25
|
require 'thread'
|
|
26
|
+
require 'timeout'
|
|
26
27
|
require 'bgp/common'
|
|
27
|
-
require 'bgp/message'
|
|
28
|
+
require 'bgp/messages/message'
|
|
29
|
+
|
|
28
30
|
|
|
29
31
|
module BGP
|
|
30
32
|
module IO
|
|
@@ -95,7 +97,7 @@ module BGP
|
|
|
95
97
|
begin
|
|
96
98
|
while @continue
|
|
97
99
|
begin
|
|
98
|
-
Timeout::timeout(h) do |
|
|
100
|
+
Timeout::timeout(h) do |_h|
|
|
99
101
|
message, type = recv_msg(s)
|
|
100
102
|
break unless @continue
|
|
101
103
|
changed and notify_observers(:ev_msg, @type, message)
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright 2010 Jean-Michel Esnault.
|
|
3
|
+
# All rights reserved.
|
|
4
|
+
# See LICENSE.txt for permissions.
|
|
5
|
+
#
|
|
6
|
+
#
|
|
7
|
+
# This file is part of BGP4R.
|
|
8
|
+
#
|
|
9
|
+
# BGP4R is free software: you can redistribute it and/or modify
|
|
10
|
+
# it under the terms of the GNU General Public License as published by
|
|
11
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
12
|
+
# (at your option) any later version.
|
|
13
|
+
#
|
|
14
|
+
# BGP4R is distributed in the hope that it will be useful,
|
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17
|
+
# GNU General Public License for more details.
|
|
18
|
+
#
|
|
19
|
+
# You should have received a copy of the GNU General Public License
|
|
20
|
+
# along with BGP4R. If not, see <http://www.gnu.org/licenses/>.
|
|
21
|
+
#++
|
|
22
|
+
|
|
23
|
+
require 'bgp/messages/message'
|
|
24
|
+
|
|
25
|
+
module BGP::OPT_PARM
|
|
26
|
+
module CAP
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module BGP
|
|
31
|
+
|
|
32
|
+
class Capability < Message
|
|
33
|
+
|
|
34
|
+
class Revision
|
|
35
|
+
|
|
36
|
+
class <<self
|
|
37
|
+
|
|
38
|
+
def advertise(seqn, cap)
|
|
39
|
+
new :unset, :unset, :advertise, seqn, cap
|
|
40
|
+
end
|
|
41
|
+
def advertise_ack_request(seqn, cap)
|
|
42
|
+
new :unset, :set, :advertise, seqn, cap
|
|
43
|
+
end
|
|
44
|
+
def advertise_ack_response(seqn, cap)
|
|
45
|
+
new :set, :unset, :advertise, seqn, cap
|
|
46
|
+
end
|
|
47
|
+
def remove(seqn, cap)
|
|
48
|
+
new :unset, :unset, :remove, seqn, cap
|
|
49
|
+
end
|
|
50
|
+
def remove_ack_request(seqn, cap)
|
|
51
|
+
new :unset, :set, :remove, seqn, cap
|
|
52
|
+
end
|
|
53
|
+
def remove_ack_response(seqn, cap)
|
|
54
|
+
new :set, :unset, :remove, seqn, cap
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def initialize(*args)
|
|
60
|
+
if args.size> 1
|
|
61
|
+
init_ack_bit, ack_request_bit, action_bit, seqn, cap = args
|
|
62
|
+
@o1 = 0
|
|
63
|
+
@o1 |= 0x80 if init_ack_bit == :set
|
|
64
|
+
@o1 &= ~0x80 if init_ack_bit == :unset
|
|
65
|
+
@o1 |= 0x40 if ack_request_bit == :set
|
|
66
|
+
@o1 &= ~0x40 if ack_request_bit == :unset
|
|
67
|
+
@o1 |= 1 if action_bit == :remove
|
|
68
|
+
@o1 &= ~1 if action_bit == :advertise
|
|
69
|
+
@seqn = seqn
|
|
70
|
+
@cap = cap
|
|
71
|
+
elsif args.size==1 && args.is_a?(String)
|
|
72
|
+
else
|
|
73
|
+
parse args[0]
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def encode
|
|
78
|
+
[@o1,@seqn, @cap.encode].pack('CNa*')
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def parse(s)
|
|
82
|
+
@o1, @seqn, @code, len = s.slice!(0,8).unpack('CNCn')
|
|
83
|
+
cap_value = s.slice!(0,len)
|
|
84
|
+
@cap = BGP::OPT_PARM::Capability.dynamic_factory([@code,len,cap_value].pack('Cna*'))
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def to_s
|
|
88
|
+
format('0x%08d %-9s %3s (0x%02x) %s', @seqn, action_to_s, ack_to_s, @o1, cap_to_s)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def initiated?
|
|
92
|
+
! acknowledged?
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def acknowledged?
|
|
96
|
+
@o1 & 0x80 > 1
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def ack_requested?
|
|
100
|
+
@o1& 0x40 > 1
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def advertise?
|
|
104
|
+
!remove?
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def remove?
|
|
108
|
+
@o1 & 1 == 1
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def init_ack_to_s
|
|
112
|
+
initiated? ? '' : '(ack)'
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
def ack_to_s
|
|
118
|
+
if ack_requested?
|
|
119
|
+
'Req'
|
|
120
|
+
elsif acknowledged?
|
|
121
|
+
'Rsp'
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def action_to_s
|
|
126
|
+
advertise? ? 'Advertise' : 'Withdraw'
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def cap_to_s
|
|
130
|
+
@cap.to_s_brief
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def seqn_to_s
|
|
134
|
+
format('0x%08x', @seqn)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def initialize(*args)
|
|
140
|
+
@tuples =[]
|
|
141
|
+
if args.size==1 and args[0].is_a?(String)
|
|
142
|
+
parse args[0]
|
|
143
|
+
else
|
|
144
|
+
@msg_type=CAPABILITY
|
|
145
|
+
add *args
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def add(*revisions)
|
|
150
|
+
[revisions].flatten.find_all { |r| r.is_a?(Capability::Revision)}.each { |r| @tuples << r }
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
alias << add
|
|
154
|
+
|
|
155
|
+
def encode
|
|
156
|
+
super @tuples.collect { |r| r.encode }.join
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def parse(s)
|
|
160
|
+
revisions = super(s)
|
|
161
|
+
while revisions.size>0
|
|
162
|
+
@tuples << BGP::Capability::Revision.new(revisions)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def to_s
|
|
167
|
+
msg = self.encode
|
|
168
|
+
"Capability (#{CAPABILITY}), length: #{msg.size}\n" +
|
|
169
|
+
" Seqn Action Ack bits Capability\n " +
|
|
170
|
+
@tuples.collect { |t| t.to_s}.join("\n ")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
load "../../test/messages/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# KEEPALIVE Message Format
|
|
2
|
+
#
|
|
3
|
+
# BGP does not use any TCP-based, keep-alive mechanism to determine if
|
|
4
|
+
# peers are reachable. Instead, KEEPALIVE messages are exchanged
|
|
5
|
+
# between peers often enough not to cause the Hold Timer to expire. A
|
|
6
|
+
# reasonable maximum time between KEEPALIVE messages would be one third
|
|
7
|
+
# of the Hold Time interval. KEEPALIVE messages MUST NOT be sent more
|
|
8
|
+
# frequently than one per second. An implementation MAY adjust the
|
|
9
|
+
# rate at which it sends KEEPALIVE messages as a function of the Hold
|
|
10
|
+
# Time interval.
|
|
11
|
+
#
|
|
12
|
+
# If the negotiated Hold Time interval is zero, then periodic KEEPALIVE
|
|
13
|
+
# messages MUST NOT be sent.
|
|
14
|
+
#
|
|
15
|
+
# A KEEPALIVE message consists of only the message header and has a
|
|
16
|
+
# length of 19 octets.
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
require 'bgp/messages/message'
|
|
20
|
+
|
|
21
|
+
module BGP
|
|
22
|
+
class Keepalive < Message
|
|
23
|
+
def initialize
|
|
24
|
+
@msg_type=KEEPALIVE
|
|
25
|
+
end
|
|
26
|
+
def to_s
|
|
27
|
+
"Keepalive Message (#{KEEPALIVE}), length: 19" + ", [#{self.to_shex[32..-1]}]"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright 2008, 2009 Jean-Michel Esnault.
|
|
3
|
+
# All rights reserved.
|
|
4
|
+
# See LICENSE.txt for permissions.
|
|
5
|
+
#
|
|
6
|
+
#
|
|
7
|
+
# This file is part of BGP4R.
|
|
8
|
+
#
|
|
9
|
+
# BGP4R is free software: you can redistribute it and/or modify
|
|
10
|
+
# it under the terms of the GNU General Public License as published by
|
|
11
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
12
|
+
# (at your option) any later version.
|
|
13
|
+
#
|
|
14
|
+
# BGP4R is distributed in the hope that it will be useful,
|
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17
|
+
# GNU General Public License for more details.
|
|
18
|
+
#
|
|
19
|
+
# You should have received a copy of the GNU General Public License
|
|
20
|
+
# along with BGP4R. If not, see <http://www.gnu.org/licenses/>.
|
|
21
|
+
#++
|
|
22
|
+
|
|
23
|
+
module BGP
|
|
24
|
+
|
|
25
|
+
# http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-1
|
|
26
|
+
|
|
27
|
+
class UnknownBgpMessage < RuntimeError
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class Message
|
|
31
|
+
|
|
32
|
+
unless const_defined? :OPEN
|
|
33
|
+
OPEN = 1
|
|
34
|
+
UPDATE = 2
|
|
35
|
+
NOTIFICATION = 3
|
|
36
|
+
KEEPALIVE = 4
|
|
37
|
+
ROUTE_REFRESH = 5
|
|
38
|
+
CAPABILITY = 6
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def encode(message='')
|
|
42
|
+
len = message.size+19
|
|
43
|
+
[[255]*16,len,@msg_type,message].flatten.pack('C16nCa*')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def parse(s)
|
|
47
|
+
len, @msg_type, message = s[16..-1].unpack('nCa*')
|
|
48
|
+
message.is_packed
|
|
49
|
+
end
|
|
50
|
+
def self.factory(_s, as4byte=false)
|
|
51
|
+
s = [_s].pack('a*')
|
|
52
|
+
s.slice(18,1).unpack('C')[0]
|
|
53
|
+
case s.slice(18,1).unpack('C')[0]
|
|
54
|
+
when OPEN
|
|
55
|
+
Open.new(s)
|
|
56
|
+
when UPDATE
|
|
57
|
+
Update.new(s, as4byte)
|
|
58
|
+
when KEEPALIVE
|
|
59
|
+
Keepalive.new
|
|
60
|
+
when NOTIFICATION
|
|
61
|
+
Notification.new(s)
|
|
62
|
+
when ROUTE_REFRESH
|
|
63
|
+
if s.size > 23
|
|
64
|
+
Orf_route_refresh.new(s)
|
|
65
|
+
else
|
|
66
|
+
Route_refresh.new(s)
|
|
67
|
+
end
|
|
68
|
+
when CAPABILITY
|
|
69
|
+
Capability.new(s)
|
|
70
|
+
else
|
|
71
|
+
puts "don't know what kind of bgp messgage this is #{s.slice(18,1).unpack('C')}"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
def self.keepalive
|
|
75
|
+
Keepalive.new.encode
|
|
76
|
+
end
|
|
77
|
+
def self.route_refresh(afi,safi)
|
|
78
|
+
Route_refresh.new(afi,safi).encode
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
load "../../test/messages/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0
|
|
85
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module BGP
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
unless const_defined? :VERSION
|
|
5
|
+
BGP.const_set('VERSION', Gem.loaded_specs['bgp4r'].version.to_s)
|
|
6
|
+
end
|
|
7
|
+
rescue
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
%w{ update keepalive open notification capablity}.each do |m|
|
|
11
|
+
autoload "#{m}".capitalize.to_sym, "bgp/messages/#{m}"
|
|
12
|
+
end
|
|
13
|
+
autoload :Route_refresh, 'bgp/messages/route_refresh'
|
|
14
|
+
autoload :Orf_route_refresh, 'bgp/messages/route_refresh'
|
|
15
|
+
autoload :Prefix_orf, 'bgp/orfs/prefix_orf'
|
|
16
|
+
|
|
17
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
|
|
2
|
+
# NOTIFICATION Message Format
|
|
3
|
+
#
|
|
4
|
+
# A NOTIFICATION message is sent when an error condition is detected.
|
|
5
|
+
# The BGP connection is closed immediately after it is sent.
|
|
6
|
+
#
|
|
7
|
+
#
|
|
8
|
+
#
|
|
9
|
+
#
|
|
10
|
+
# Rekhter, et al. Standards Track [Page 21]
|
|
11
|
+
#
|
|
12
|
+
# RFC 4271 BGP-4 January 2006
|
|
13
|
+
#
|
|
14
|
+
#
|
|
15
|
+
# In addition to the fixed-size BGP header, the NOTIFICATION message
|
|
16
|
+
# contains the following fields:
|
|
17
|
+
#
|
|
18
|
+
# 0 1 2 3
|
|
19
|
+
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
20
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
21
|
+
# | Error code | Error subcode | Data (variable) |
|
|
22
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
23
|
+
#
|
|
24
|
+
|
|
25
|
+
require 'bgp/messages/message'
|
|
26
|
+
|
|
27
|
+
module BGP
|
|
28
|
+
|
|
29
|
+
class Notification < Message
|
|
30
|
+
|
|
31
|
+
def self.code_to_s(*args)
|
|
32
|
+
@code_to_s[args]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
@code_to_s=Hash.new("Undefined")
|
|
36
|
+
@code_to_s[[1]] = "Header Error"
|
|
37
|
+
@code_to_s[[2]] = "OPEN msg error"
|
|
38
|
+
@code_to_s[[3]] = "UPDATE msg error"
|
|
39
|
+
@code_to_s[[4]] = "Hold Timer Expired"
|
|
40
|
+
@code_to_s[[5]] = "Finite State Machine Error"
|
|
41
|
+
@code_to_s[[6]] = "Cease"
|
|
42
|
+
@code_to_s[[7]] = "CAPABILITY Message Error"
|
|
43
|
+
@code_to_s[[1,1]] = "Connection Not Synchronized"
|
|
44
|
+
@code_to_s[[1,2]] = "Bad Message Length"
|
|
45
|
+
@code_to_s[[1,3]] = "Bad Message Type"
|
|
46
|
+
@code_to_s[[2,1]] = "Unsupported Version Number"
|
|
47
|
+
@code_to_s[[2,2]] = "Bad Peer AS"
|
|
48
|
+
@code_to_s[[2,3]] = "Bad BGP Identifier"
|
|
49
|
+
@code_to_s[[2,4]] = "Unsupported Optional Parameter"
|
|
50
|
+
@code_to_s[[2,5]] = "[Deprecated]"
|
|
51
|
+
@code_to_s[[2,6]] = "Unacceptable Hold Time"
|
|
52
|
+
@code_to_s[[2,7]] = "Unsupported Optional Parameter"
|
|
53
|
+
@code_to_s[[3,1]] = "Malformed Attribute List"
|
|
54
|
+
@code_to_s[[3,2]] = "Unrecognized Well-known Attribute"
|
|
55
|
+
@code_to_s[[3,3]] = "Missing Well-known Attribute"
|
|
56
|
+
@code_to_s[[3,4]] = "Attribute Flags Error"
|
|
57
|
+
@code_to_s[[3,5]] = "Attribute Length Error"
|
|
58
|
+
@code_to_s[[3,6]] = "Invalid ORIGIN Attribute"
|
|
59
|
+
@code_to_s[[3,7]] = "Deprecated"
|
|
60
|
+
@code_to_s[[3,8]] = "Invalid NEXT_HOP Attribute"
|
|
61
|
+
@code_to_s[[3,9]] = "Optional Attribute Error"
|
|
62
|
+
@code_to_s[[3,10]] = "Invalid Network Field"
|
|
63
|
+
@code_to_s[[3,11]] = "Malformed AS_PATH"
|
|
64
|
+
@code_to_s[[6,1]] = "Maximum Number of Prefixes Reached"
|
|
65
|
+
@code_to_s[[6,2]] = "Administrative Shutdown"
|
|
66
|
+
@code_to_s[[6,3]] = "Peer De-configured"
|
|
67
|
+
@code_to_s[[6,4]] = "Administrative Reset"
|
|
68
|
+
@code_to_s[[6,5]] = "Connection Rejected"
|
|
69
|
+
@code_to_s[[6,6]] = "Other Configuration Change"
|
|
70
|
+
@code_to_s[[6,7]] = "Connection Collision Resolution"
|
|
71
|
+
@code_to_s[[6,8]] = "Out of Resources"
|
|
72
|
+
@code_to_s[[7,1]] = "Unknown Sequence Number"
|
|
73
|
+
@code_to_s[[7,2]] = "Invalid Capability Length"
|
|
74
|
+
@code_to_s[[7,3]] = "Malformed Capability Value"
|
|
75
|
+
@code_to_s[[7,4]] = "Unsupported Capability Code"
|
|
76
|
+
|
|
77
|
+
def initialize(*args)
|
|
78
|
+
@msg_type=NOTIFICATION
|
|
79
|
+
if args.size==1 and args[0].is_a?(String) and args[0].is_packed?
|
|
80
|
+
parse(args[0])
|
|
81
|
+
elsif args[0].is_a?(self.class)
|
|
82
|
+
parse(args[0].encode, *args[1..-1])
|
|
83
|
+
elsif args.size==1 and args[0].is_a?(Notification) and args[0].respond_to?(:encode)
|
|
84
|
+
parse(args[0].encode)
|
|
85
|
+
else
|
|
86
|
+
@code, @subcode, @data=args
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def encode
|
|
91
|
+
# default to ipv4 and unicast when not set
|
|
92
|
+
super([@code,@subcode, @data].pack('CCa*'))
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def parse(s)
|
|
96
|
+
@code, @subcode, @data= super(s).unpack('CCa*')
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def to_hash
|
|
100
|
+
{:code=> @code, :subcode=> @subcode, :data=>@data}
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def to_string
|
|
104
|
+
Notification.code_to_s(@code, @subcode)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def to_s
|
|
108
|
+
msg = self.encode
|
|
109
|
+
s = "Notification (#{NOTIFICATION}), length: #{msg.size}: "
|
|
110
|
+
s += "#{Notification.code_to_s(@code)} (#{@code}), #{Notification.code_to_s(@code, @subcode)} (#{@subcode}) "
|
|
111
|
+
s += "\ndata: [#{@data}]" if @data.size>0
|
|
112
|
+
s
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
|
|
2
|
+
# OPEN Message Format
|
|
3
|
+
#
|
|
4
|
+
# After a TCP connection is established, the first message sent by each
|
|
5
|
+
# side is an OPEN message. If the OPEN message is acceptable, a
|
|
6
|
+
# KEEPALIVE message confirming the OPEN is sent back.
|
|
7
|
+
#
|
|
8
|
+
# In addition to the fixed-size BGP header, the OPEN message contains
|
|
9
|
+
# the following fields:
|
|
10
|
+
#
|
|
11
|
+
# 0 1 2 3
|
|
12
|
+
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
13
|
+
# +-+-+-+-+-+-+-+-+
|
|
14
|
+
# | Version |
|
|
15
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
16
|
+
# | My Autonomous System |
|
|
17
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
18
|
+
# | Hold Time |
|
|
19
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
20
|
+
# | BGP Identifier |
|
|
21
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
22
|
+
# | Opt Parm Len |
|
|
23
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
24
|
+
# | |
|
|
25
|
+
# | Optional Parameters (variable) |
|
|
26
|
+
# | |
|
|
27
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
28
|
+
#
|
|
29
|
+
#
|
|
30
|
+
require 'bgp/messages/message'
|
|
31
|
+
require 'bgp/optional_parameters/capability'
|
|
32
|
+
|
|
33
|
+
module BGP
|
|
34
|
+
|
|
35
|
+
class Open < Message
|
|
36
|
+
|
|
37
|
+
include OPT_PARM
|
|
38
|
+
|
|
39
|
+
attr_reader :version, :local_as, :holdtime, :opt_parms
|
|
40
|
+
|
|
41
|
+
def initialize(*args)
|
|
42
|
+
@opt_parms=[]
|
|
43
|
+
if args.size==1 and args[0].is_a?(String) and args[0].is_packed?
|
|
44
|
+
parse(args[0])
|
|
45
|
+
elsif args[0].is_a?(self.class)
|
|
46
|
+
parse(args[0].encode, *args[1..-1])
|
|
47
|
+
else
|
|
48
|
+
@msg_type=OPEN
|
|
49
|
+
@version, @local_as, @holdtime, bgp_id, *@opt_parms=args
|
|
50
|
+
@bgp_id = IPAddr.new(bgp_id)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def <<(arg)
|
|
55
|
+
raise ArgumentError, "arg is not an Optional_parameter" unless arg.is_a?(Optional_parameter)
|
|
56
|
+
@opt_parms << arg
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def encode
|
|
60
|
+
opt_parms = @opt_parms.compact.collect { |cap| cap.encode }.join
|
|
61
|
+
s = [@version, @local_as, @holdtime, @bgp_id.hton].pack("Cnna4")
|
|
62
|
+
s += if opt_parms.size>255
|
|
63
|
+
[0xffff, opt_parms.size, opt_parms].pack("nna*")
|
|
64
|
+
else
|
|
65
|
+
[opt_parms.size, opt_parms].pack("Ca*")
|
|
66
|
+
end
|
|
67
|
+
super s
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def bgp_id
|
|
71
|
+
@bgp_id.to_s
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def to_s
|
|
75
|
+
msg = self.encode
|
|
76
|
+
"Open Message (#{OPEN}), length: #{msg.size}\n" +
|
|
77
|
+
" Version #{@version}, my AS #{@local_as}, Holdtime #{@holdtime}s, ID #{@bgp_id}" +
|
|
78
|
+
([""] + @opt_parms.compact.collect { |cap| cap.to_s } + [""]).join("\n ") +
|
|
79
|
+
msg.hexlify.join("\n") + "\n"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def find(klass)
|
|
83
|
+
@opt_parms.find { |a| a.is_a?(klass) }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def has?(klass)
|
|
87
|
+
@opt_parms.find { |a| a.is_a?(klass) }.nil? ? false : true
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def to_hash
|
|
91
|
+
h = {:version => @version, :my_as => @local_as, :holdtime => @holdtime, :bgp_id => bgp_id }
|
|
92
|
+
unless @opt_parms.empty?
|
|
93
|
+
h[:capabilities] = @opt_parms.collect { |opt| opt.to_hash }
|
|
94
|
+
end
|
|
95
|
+
h
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
def parse(_s)
|
|
101
|
+
s = super(_s)
|
|
102
|
+
if s[9,2].unpack('CC') == [255,255]
|
|
103
|
+
@version, @local_as, @holdtime, bgp_id, _, opt_parm_len, opt_parms = s.unpack('Cnna4nna*')
|
|
104
|
+
else
|
|
105
|
+
@version, @local_as, @holdtime, bgp_id, opt_parm_len, opt_parms = s.unpack('Cnna4Ca*')
|
|
106
|
+
end
|
|
107
|
+
while opt_parms.size>0
|
|
108
|
+
@opt_parms << Optional_parameter.factory(opt_parms)
|
|
109
|
+
end
|
|
110
|
+
@bgp_id = IPAddr.new_ntoh(bgp_id)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
load "../../test/messages/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0
|