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.
Files changed (99) hide show
  1. data/README.rdoc +1 -10
  2. data/bgp/io.rb +4 -2
  3. data/bgp/messages/capability.rb +176 -0
  4. data/bgp/messages/keepalive.rb +30 -0
  5. data/bgp/messages/markers.rb +10 -0
  6. data/bgp/messages/message.rb +85 -0
  7. data/bgp/messages/messages.rb +17 -0
  8. data/bgp/messages/notification.rb +116 -0
  9. data/bgp/messages/open.rb +117 -0
  10. data/bgp/messages/route_refresh.rb +181 -0
  11. data/bgp/messages/update.rb +148 -0
  12. data/bgp/neighbor.rb +20 -11
  13. data/bgp/nlris/inet.rb +18 -0
  14. data/bgp/{label.rb → nlris/label.rb} +1 -1
  15. data/bgp/nlris/labeled.rb +39 -0
  16. data/bgp/nlris/nlri.rb +154 -0
  17. data/bgp/{attributes.rb → nlris/nlris.rb} +11 -13
  18. data/bgp/nlris/prefix.rb +33 -0
  19. data/bgp/{rd.rb → nlris/rd.rb} +1 -1
  20. data/bgp/nlris/vpn.rb +68 -0
  21. data/bgp/optional_parameters/as4.rb +59 -0
  22. data/bgp/optional_parameters/capabilities.rb +13 -0
  23. data/bgp/optional_parameters/capability.rb +145 -0
  24. data/bgp/optional_parameters/dynamic.rb +37 -0
  25. data/bgp/optional_parameters/graceful_restart.rb +116 -0
  26. data/bgp/optional_parameters/mbgp.rb +97 -0
  27. data/bgp/optional_parameters/optional_parameter.rb +76 -0
  28. data/bgp/optional_parameters/orf.rb +90 -0
  29. data/bgp/optional_parameters/route_refresh.rb +36 -0
  30. data/bgp/{prefix_orf.rb → orfs/prefix_orf.rb} +4 -10
  31. data/bgp/{aggregator.rb → path_attributes/aggregator.rb} +2 -2
  32. data/bgp/{as_path.rb → path_attributes/as_path.rb} +2 -2
  33. data/bgp/{atomic_aggregate.rb → path_attributes/atomic_aggregate.rb} +2 -2
  34. data/bgp/{attribute.rb → path_attributes/attribute.rb} +2 -2
  35. data/bgp/path_attributes/attributes.rb +21 -0
  36. data/bgp/{cluster_list.rb → path_attributes/cluster_list.rb} +2 -2
  37. data/bgp/{communities.rb → path_attributes/communities.rb} +2 -2
  38. data/bgp/{extended_communities.rb → path_attributes/extended_communities.rb} +3 -3
  39. data/bgp/{extended_community.rb → path_attributes/extended_community.rb} +1 -1
  40. data/bgp/{local_pref.rb → path_attributes/local_pref.rb} +2 -2
  41. data/bgp/{mp_reach.rb → path_attributes/mp_reach.rb} +3 -69
  42. data/bgp/path_attributes/mp_unreach.rb +97 -0
  43. data/bgp/{multi_exit_disc.rb → path_attributes/multi_exit_disc.rb} +2 -2
  44. data/bgp/{next_hop.rb → path_attributes/next_hop.rb} +2 -2
  45. data/bgp/{origin.rb → path_attributes/origin.rb} +2 -2
  46. data/bgp/{originator_id.rb → path_attributes/originator_id.rb} +2 -2
  47. data/bgp/{path_attribute.rb → path_attributes/path_attribute.rb} +6 -7
  48. data/bgp4r.gemspec +127 -67
  49. data/bgp4r.rb +5 -25
  50. data/examples/routegen +1 -1
  51. data/examples/routegen.yml +21 -5
  52. data/examples/simple.rb +4 -4
  53. data/examples/unit-testing/malformed_update.rb +1 -1
  54. data/examples/unit-testing/unknown_transitive_attr.rb +18 -10
  55. data/test/messages/capability_test.rb +113 -0
  56. data/test/messages/keepalive_test.rb +34 -0
  57. data/test/messages/markers_test.rb +33 -0
  58. data/test/messages/message_test.rb +43 -0
  59. data/test/messages/notification_test.rb +49 -0
  60. data/test/messages/open_test.rb +98 -0
  61. data/test/messages/route_refresh_test.rb +77 -0
  62. data/test/messages/update_test.rb +133 -0
  63. data/test/neighbor_test.rb +8 -9
  64. data/test/nlris/inet_test.rb +43 -0
  65. data/test/{label_test.rb → nlris/labeled_test.rb} +1 -1
  66. data/test/{nlri_test.rb → nlris/nlri_test.rb} +5 -1
  67. data/test/{rd_test.rb → nlris/rd_test.rb} +1 -2
  68. data/test/optional_parameters/as4_test.rb +34 -0
  69. data/test/optional_parameters/capability_test.rb +69 -0
  70. data/test/optional_parameters/dynamic_test.rb +34 -0
  71. data/test/optional_parameters/graceful_restart_test.rb +23 -0
  72. data/test/optional_parameters/mbgp_test.rb +35 -0
  73. data/test/optional_parameters/optional_parameter_test.rb +51 -0
  74. data/test/optional_parameters/orf_test.rb +42 -0
  75. data/test/optional_parameters/route_refresh_test.rb +32 -0
  76. data/test/{prefix_orf_test.rb → orfs/prefix_orf_test.rb} +2 -1
  77. data/test/{aggregator_test.rb → path_attributes/aggregator_test.rb} +1 -1
  78. data/test/{as_path_test.rb → path_attributes/as_path_test.rb} +1 -1
  79. data/test/{atomic_aggregate_test.rb → path_attributes/atomic_aggregate_test.rb} +1 -1
  80. data/test/{attribute_test.rb → path_attributes/attribute_test.rb} +2 -2
  81. data/test/{cluster_list_test.rb → path_attributes/cluster_list_test.rb} +1 -1
  82. data/test/{communities_test.rb → path_attributes/communities_test.rb} +1 -1
  83. data/test/{extended_communities_test.rb → path_attributes/extended_communities_test.rb} +1 -1
  84. data/test/{extended_community_test.rb → path_attributes/extended_community_test.rb} +1 -1
  85. data/test/{local_pref_test.rb → path_attributes/local_pref_test.rb} +1 -1
  86. data/test/{mp_reach_test.rb → path_attributes/mp_reach_test.rb} +1 -37
  87. data/test/path_attributes/mp_unreach_test.rb +67 -0
  88. data/test/{multi_exit_disc_test.rb → path_attributes/multi_exit_disc_test.rb} +1 -1
  89. data/test/{next_hop_test.rb → path_attributes/next_hop_test.rb} +1 -1
  90. data/test/{origin_test.rb → path_attributes/origin_test.rb} +2 -1
  91. data/test/{originator_id_test.rb → path_attributes/originator_id_test.rb} +1 -1
  92. data/test/{path_attribute_test.rb → path_attributes/path_attribute_test.rb} +2 -2
  93. metadata +122 -67
  94. data/bgp/message.rb +0 -921
  95. data/bgp/nlri.rb +0 -303
  96. data/bgp/version.rb +0 -3
  97. data/examples/unit-testing/keepalive_set_to_zeo.rb +0 -64
  98. data/test/message_test.rb +0 -347
  99. /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 jesnault-bgp4r
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 |h|
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,10 @@
1
+ require 'bgp/messages/update'
2
+ module BGP
3
+ def Update.end_of_rib_marker(*args)
4
+ if args.empty?
5
+ Update.new(Path_attribute.new()) if args.empty?
6
+ elsif args[0].is_a?(Hash)
7
+ Update.new(Path_attribute.new(Mp_unreach.new(*args)))
8
+ end
9
+ end
10
+ 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