bgp4r 0.0.11 → 0.0.12
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/bgp/common.rb +14 -13
- data/bgp/iana.rb +26 -1
- data/bgp/messages/capability.rb +3 -2
- data/bgp/messages/message.rb +3 -2
- data/bgp/messages/open.rb +2 -1
- data/bgp/messages/update.rb +80 -30
- data/bgp/neighbor/add_path_cap.rb +125 -0
- data/bgp/{neighbor.rb → neighbor/neighbor.rb} +64 -68
- data/bgp/nlris/nlri.rb +289 -15
- data/bgp/nlris/prefix.rb +3 -2
- data/bgp/nlris/vpn.rb +1 -8
- data/bgp/optional_parameters/add_path.rb +160 -0
- data/bgp/optional_parameters/capabilities.rb +1 -0
- data/bgp/optional_parameters/capability.rb +6 -0
- data/bgp/optional_parameters/graceful_restart.rb +6 -6
- data/bgp/optional_parameters/optional_parameter.rb +1 -0
- data/bgp/path_attributes/as_path.rb +1 -1
- data/bgp/path_attributes/attribute.rb +12 -5
- data/bgp/path_attributes/mp_reach.rb +142 -96
- data/bgp/path_attributes/mp_unreach.rb +73 -20
- data/bgp/path_attributes/path_attribute.rb +28 -5
- data/bgp4r.gemspec +21 -6
- data/bgp4r.rb +1 -1
- data/examples/unit-testing/malformed_update.rb +2 -1
- data/examples/unit-testing/test.rb +82 -0
- data/examples/unit-testing/test1.rb +82 -0
- data/examples/unit-testing/test2.rb +44 -0
- data/test/common_test.rb +7 -0
- data/test/helpers/server.rb +20 -0
- data/test/iana_test.rb +43 -0
- data/test/messages/open_test.rb +7 -2
- data/test/messages/update_test.rb +133 -36
- data/test/neighbor/add_path_cap_test.rb +54 -0
- data/test/neighbor/neighbor_test.rb +161 -0
- data/test/nlris/ext_nlri_test.rb +25 -60
- data/test/nlris/nlri_test.rb +93 -115
- data/test/optional_parameters/add_path_test.rb +53 -0
- data/test/optional_parameters/capability_test.rb +10 -0
- data/test/optional_parameters/graceful_restart_test.rb +1 -0
- data/test/path_attributes/mp_reach_test.rb +206 -8
- data/test/path_attributes/mp_unreach_test.rb +113 -5
- data/test/path_attributes/path_attribute_test.rb +34 -2
- metadata +20 -7
- data/test/neighbor_test.rb +0 -62
data/bgp/common.rb
CHANGED
@@ -42,17 +42,6 @@ class IPAddr
|
|
42
42
|
IPAddr.new(arg)
|
43
43
|
end
|
44
44
|
end
|
45
|
-
|
46
|
-
def _mlen_
|
47
|
-
m = @mask_addr
|
48
|
-
len = ipv6? ? 128 : 32
|
49
|
-
loop do
|
50
|
-
break if m & 1 > 0
|
51
|
-
m = m >> 1
|
52
|
-
len += -1
|
53
|
-
end
|
54
|
-
len
|
55
|
-
end
|
56
45
|
|
57
46
|
def mlen
|
58
47
|
@_jme_mlen_ ||= _mlen_
|
@@ -75,10 +64,22 @@ class IPAddr
|
|
75
64
|
if ipv4?
|
76
65
|
[@mask_addr].pack('N').unpack('C4').collect { |x| x.to_s}.join('.')
|
77
66
|
else
|
78
|
-
|
79
|
-
@mask_addr
|
67
|
+
@mask_addr.to_s(16).scan(/..../).collect { |x| x }.join(':')
|
80
68
|
end
|
81
69
|
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def _mlen_
|
74
|
+
m = @mask_addr
|
75
|
+
len = ipv6? ? 128 : 32
|
76
|
+
loop do
|
77
|
+
break if m & 1 > 0
|
78
|
+
m = m >> 1
|
79
|
+
len += -1
|
80
|
+
end
|
81
|
+
len
|
82
|
+
end
|
82
83
|
|
83
84
|
end
|
84
85
|
|
data/bgp/iana.rb
CHANGED
@@ -30,6 +30,14 @@ module IANA
|
|
30
30
|
''
|
31
31
|
end
|
32
32
|
end
|
33
|
+
def self.afi?(arg)
|
34
|
+
@h_afis ||= AFI.set_h_afis
|
35
|
+
@h_afis[arg]
|
36
|
+
end
|
37
|
+
def self.safi?(arg)
|
38
|
+
@h_safis ||= SAFI.set_h_safis
|
39
|
+
@h_safis[arg]
|
40
|
+
end
|
33
41
|
def self.safi(safi)
|
34
42
|
case safi
|
35
43
|
when SAFI::UNICAST_NLRI ; 'Unicast'
|
@@ -68,8 +76,25 @@ module IANA
|
|
68
76
|
FCWWNN = 23
|
69
77
|
GWID = 24
|
70
78
|
L2VPN = 25
|
71
|
-
|
79
|
+
def self.set_h_afis
|
80
|
+
h_afis = Hash.new
|
81
|
+
constants.each do |c|
|
82
|
+
h_afis.store(c.downcase.to_sym, const_get(c))
|
83
|
+
h_afis.store(const_get(c), c.split('_').collect { |w| w }.join(' '))
|
84
|
+
# h_afis.store(const_get(c), c.downcase.to_sym)
|
85
|
+
end
|
86
|
+
h_afis
|
87
|
+
end
|
88
|
+
end
|
72
89
|
module SAFI
|
90
|
+
def self.set_h_safis
|
91
|
+
h_safis = Hash.new
|
92
|
+
constants.each do |c|
|
93
|
+
h_safis.store(c.downcase.to_sym, const_get(c))
|
94
|
+
h_safis.store(const_get(c), c.split('_').collect { |w| w }.join(' '))
|
95
|
+
end
|
96
|
+
h_safis
|
97
|
+
end
|
73
98
|
UNICAST_NLRI = 1
|
74
99
|
MULTICAST_NLRI = 2
|
75
100
|
LABEL_NLRI = 4
|
data/bgp/messages/capability.rb
CHANGED
@@ -68,9 +68,10 @@ module BGP
|
|
68
68
|
@o1 &= ~1 if action_bit == :advertise
|
69
69
|
@seqn = seqn
|
70
70
|
@cap = cap
|
71
|
-
elsif args.size==1 && args.is_a?(String)
|
72
|
-
else
|
71
|
+
elsif args.size==1 && args[0].is_a?(String)
|
73
72
|
parse args[0]
|
73
|
+
else
|
74
|
+
raise ArgumentError, "Invalid argument #{args.inspect}"
|
74
75
|
end
|
75
76
|
end
|
76
77
|
|
data/bgp/messages/message.rb
CHANGED
@@ -47,14 +47,14 @@ module ::BGP
|
|
47
47
|
len, @msg_type, message = s[16..-1].unpack('nCa*')
|
48
48
|
message.is_packed
|
49
49
|
end
|
50
|
-
def self.factory(_s,
|
50
|
+
def self.factory(_s, session_info=nil)
|
51
51
|
s = [_s].pack('a*')
|
52
52
|
s.slice(18,1).unpack('C')[0]
|
53
53
|
case s.slice(18,1).unpack('C')[0]
|
54
54
|
when OPEN
|
55
55
|
Open.new(s)
|
56
56
|
when UPDATE
|
57
|
-
Update.new(s,
|
57
|
+
Update.new(s, session_info)
|
58
58
|
when KEEPALIVE
|
59
59
|
Keepalive.new
|
60
60
|
when NOTIFICATION
|
@@ -68,6 +68,7 @@ module ::BGP
|
|
68
68
|
when CAPABILITY
|
69
69
|
Capability.new(s)
|
70
70
|
else
|
71
|
+
#FIXME: raise UnknownMessageTypeError() ...
|
71
72
|
puts "don't know what kind of bgp messgage this is #{s.slice(18,1).unpack('C')}"
|
72
73
|
end
|
73
74
|
end
|
data/bgp/messages/open.rb
CHANGED
@@ -29,6 +29,7 @@
|
|
29
29
|
#
|
30
30
|
require 'bgp/messages/message'
|
31
31
|
require 'bgp/optional_parameters/capability'
|
32
|
+
require 'ipaddr'
|
32
33
|
|
33
34
|
module BGP
|
34
35
|
|
@@ -84,7 +85,7 @@ class Open < Message
|
|
84
85
|
end
|
85
86
|
|
86
87
|
def has?(klass)
|
87
|
-
@opt_parms.find { |a| a.is_a?(klass) }
|
88
|
+
@opt_parms.find { |a| a.is_a?(klass) }
|
88
89
|
end
|
89
90
|
|
90
91
|
def to_hash
|
data/bgp/messages/update.rb
CHANGED
@@ -42,15 +42,28 @@ require 'bgp/path_attributes/path_attribute'
|
|
42
42
|
|
43
43
|
class BGP::Update < BGP::Message
|
44
44
|
include BGP
|
45
|
-
|
46
|
-
|
45
|
+
|
46
|
+
class Info
|
47
|
+
def initialize(*args)
|
48
|
+
@as4byte=args[0]
|
49
|
+
end
|
50
|
+
def as4byte?
|
51
|
+
@as4byte
|
52
|
+
end
|
53
|
+
def recv_inet_unicast?
|
54
|
+
false
|
55
|
+
end
|
47
56
|
end
|
48
|
-
alias as4byte as4byte?
|
49
57
|
|
50
58
|
def initialize(*args)
|
59
|
+
@nlri, @path_attribute, @withdrawn=nil,nil,nil
|
60
|
+
@session_info = Info.new
|
51
61
|
if args[0].is_a?(String) and args[0].is_packed?
|
52
|
-
|
53
|
-
|
62
|
+
if args.size==1
|
63
|
+
parse args[0], @session_info
|
64
|
+
else
|
65
|
+
parse(*args)
|
66
|
+
end
|
54
67
|
elsif args[0].is_a?(self.class)
|
55
68
|
parse(args[0].encode, *args[1..-1])
|
56
69
|
else
|
@@ -67,47 +80,65 @@ class BGP::Update < BGP::Message
|
|
67
80
|
self.path_attribute = arg
|
68
81
|
elsif arg.is_a?(Nlri)
|
69
82
|
self.nlri = arg
|
83
|
+
elsif arg.is_a?(Ext_Nlri)
|
84
|
+
self.nlri = arg
|
70
85
|
end
|
71
86
|
}
|
72
87
|
end
|
73
88
|
|
89
|
+
def session_info=(val)
|
90
|
+
@session_info=val
|
91
|
+
raise unless val.respond_to? :as4byte?
|
92
|
+
end
|
93
|
+
|
74
94
|
def withdrawn=(val)
|
75
95
|
@withdrawn=val if val.is_a?(Withdrawn)
|
76
96
|
end
|
77
97
|
|
78
98
|
def nlri=(val)
|
79
|
-
@nlri=val if val.is_a?(Nlri)
|
99
|
+
@nlri=val if val.is_a?(Nlri) or val.is_a?(Ext_Nlri)
|
80
100
|
end
|
81
101
|
|
82
102
|
def path_attribute=(val)
|
83
103
|
@path_attribute=val if val.is_a?(Path_attribute)
|
84
104
|
end
|
85
105
|
|
86
|
-
def encode(
|
87
|
-
@as4byte=as4byte
|
106
|
+
def encode(session_info=@session_info)
|
88
107
|
withdrawn, path_attribute, nlri = '', '', ''
|
89
|
-
withdrawn = @withdrawn.encode
|
90
|
-
path_attribute = @path_attribute.encode(as4byte) if
|
91
|
-
|
92
|
-
super([withdrawn.size, withdrawn, path_attribute.size, path_attribute, nlri].pack('na*na*a*'))
|
108
|
+
withdrawn = @withdrawn.encode if @withdrawn
|
109
|
+
path_attribute = @path_attribute.encode(session_info.as4byte?) if @path_attribute
|
110
|
+
super([withdrawn.size, withdrawn, path_attribute.size, path_attribute, encoded_nlri].pack('na*na*a*'))
|
93
111
|
end
|
94
112
|
|
95
|
-
def encode4
|
96
|
-
encode(true)
|
113
|
+
def encode4()
|
114
|
+
encode(Info.new(true))
|
97
115
|
end
|
98
116
|
|
99
|
-
attr_reader :path_attribute, :nlri, :withdrawn
|
117
|
+
attr_reader :path_attribute, :nlri, :withdrawn, :session_info
|
100
118
|
|
101
|
-
|
102
|
-
def parse(s, as4byte=false)
|
103
|
-
@as4byte=as4byte
|
119
|
+
def parse(s, session_info)
|
104
120
|
update = super(s)
|
121
|
+
|
105
122
|
len = update.slice!(0,2).unpack('n')[0]
|
106
|
-
|
123
|
+
|
124
|
+
if len>0
|
125
|
+
s = update.slice!(0,len)
|
126
|
+
w = Withdrawn.new_ntop(s,ext_nlri?(session_info))
|
127
|
+
self.withdrawn= w
|
128
|
+
end
|
129
|
+
|
107
130
|
len = update.slice!(0,2).unpack('n')[0]
|
108
|
-
|
109
|
-
|
110
|
-
|
131
|
+
if len>0
|
132
|
+
enc_path_attribute = update.slice!(0,len).is_packed
|
133
|
+
self.path_attribute=Path_attribute.new(enc_path_attribute, session_info)
|
134
|
+
end
|
135
|
+
|
136
|
+
self.nlri = Nlri.factory(update, 1, 1, ext_nlri?(session_info)) if update.size>0
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
def ext_nlri?(session_info)
|
141
|
+
session_info and session_info.recv_inet_unicast?
|
111
142
|
end
|
112
143
|
|
113
144
|
def <<(val)
|
@@ -116,25 +147,39 @@ class BGP::Update < BGP::Message
|
|
116
147
|
@path_attribute << val
|
117
148
|
elsif val.is_a?(String)
|
118
149
|
begin
|
119
|
-
Nlri.new(val)
|
150
|
+
# Nlri.new(val)
|
120
151
|
@nlri ||=Nlri.new
|
121
152
|
@nlri << val
|
122
153
|
rescue => e
|
154
|
+
p "JME: ***** #{val.inspect}"
|
155
|
+
p e
|
156
|
+
raise
|
123
157
|
end
|
158
|
+
elsif val.is_a?(Ext_Nlri)
|
159
|
+
@nlri = val
|
124
160
|
elsif val.is_a?(Nlri)
|
125
161
|
val.to_s.split.each { |n| self << n }
|
162
|
+
else
|
163
|
+
raise ArgmentError, "Invalid arg: #{val.inspect}"
|
126
164
|
end
|
127
165
|
end
|
128
166
|
|
129
|
-
def to_s(
|
130
|
-
|
131
|
-
msg = encode(as4byte)
|
167
|
+
def to_s(session_info=@session_info)
|
168
|
+
msg = encode(session_info)
|
132
169
|
fmt=:tcpdump
|
133
170
|
s = []
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
171
|
+
if @withdrawn
|
172
|
+
s << "Withdrawn Routes:"
|
173
|
+
s << @withdrawn.to_s if @withdrawn
|
174
|
+
end
|
175
|
+
|
176
|
+
s << @path_attribute.to_s(fmt, session_info.as4byte?) if defined?(@path_attribute) and @path_attribute
|
177
|
+
if @nlri
|
178
|
+
s << "Network Layer Reachability Information:"
|
179
|
+
s << @nlri.to_s
|
180
|
+
end
|
181
|
+
"Update Message (#{UPDATE}), #{session_info.as4byte? ? "4 bytes AS, " : ''}length: #{msg.size}\n" +
|
182
|
+
s.join("\n") + "\n" + msg.hexlify.join("\n") + "\n"
|
138
183
|
end
|
139
184
|
|
140
185
|
def self.withdrawn(u)
|
@@ -146,6 +191,11 @@ class BGP::Update < BGP::Message
|
|
146
191
|
Update.new(pa)
|
147
192
|
end
|
148
193
|
end
|
194
|
+
|
195
|
+
def encoded_nlri
|
196
|
+
@nlri.encode if @nlri
|
197
|
+
end
|
198
|
+
|
149
199
|
end
|
150
200
|
|
151
201
|
load "../../test/messages/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0
|
@@ -0,0 +1,125 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2011 Jean-Michel Esnault.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# This file is part of BGP4R.
|
8
|
+
#
|
9
|
+
|
10
|
+
module BGP
|
11
|
+
|
12
|
+
class Neighbor
|
13
|
+
|
14
|
+
class Base_Capabilities
|
15
|
+
|
16
|
+
def initialize(speaker_open, peer_open)
|
17
|
+
@speaker = speaker_open # speaker_open.find(OPT_PARM::CAP::Add_path)
|
18
|
+
@peer = peer_open # peer_open.find(OPT_PARM::CAP::Add_path)
|
19
|
+
end
|
20
|
+
attr_reader :speaker, :peer
|
21
|
+
|
22
|
+
def as4byte?
|
23
|
+
(! @speaker.has?(OPT_PARM::CAP::As4).nil? and ! @peer.has?(OPT_PARM::CAP::As4).nil?)
|
24
|
+
end
|
25
|
+
|
26
|
+
def path_id_recv? afi, safi
|
27
|
+
path_id? :recv, afi, safi
|
28
|
+
end
|
29
|
+
|
30
|
+
def path_id_send? afi, safi
|
31
|
+
path_id? :send, afi, safi
|
32
|
+
end
|
33
|
+
|
34
|
+
def path_id? action, afi, safi
|
35
|
+
case action
|
36
|
+
when :recv, 'recv'
|
37
|
+
_path_id?(speaker_add_path_cap, peer_add_path_cap, :recv, afi, safi)
|
38
|
+
when :send, 'send'
|
39
|
+
_path_id?(speaker_add_path_cap, peer_add_path_cap, :send, afi, safi)
|
40
|
+
else
|
41
|
+
raise ArgumentError, "Invalid argument #{action}, #{afi}, #{safi} #{action.inspect}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def _path_id?(speaker, peer, sr, afi, safi)
|
48
|
+
return false unless speaker and peer
|
49
|
+
case sr
|
50
|
+
when :recv, 'recv'
|
51
|
+
speaker.has?(:recv, afi, safi) && peer.has?(:send, afi, safi)
|
52
|
+
when :send, 'send'
|
53
|
+
speaker.has?(:send, afi, safi) && peer.has?(:recv, afi, safi)
|
54
|
+
else
|
55
|
+
raise
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def speaker_add_path_cap
|
60
|
+
@speaker.find(OPT_PARM::CAP::Add_path)
|
61
|
+
end
|
62
|
+
def peer_add_path_cap
|
63
|
+
@peer.find(OPT_PARM::CAP::Add_path)
|
64
|
+
end
|
65
|
+
|
66
|
+
def method_missing(name, *args, &block)
|
67
|
+
if (/^(send|recv)_(ipv4|ipv6|inet|inet6)_(.+)\?$/ =~ name.to_s)
|
68
|
+
case $2
|
69
|
+
when 'ipv4', 'inet' ; afi = IANA.afi?(:ip)
|
70
|
+
when 'ipv6', 'inet6' ; afi = IANA.afi?(:ip6)
|
71
|
+
else
|
72
|
+
super
|
73
|
+
end
|
74
|
+
case $3
|
75
|
+
when 'unicast' ; safi = IANA.safi?(:unicast_nlri)
|
76
|
+
when 'multicast' ; safi = IANA.safi?(:multicast_nlri)
|
77
|
+
when 'mpls_vpn_unicast' ; safi = IANA.safi?(:mpls_vpn_unicast)
|
78
|
+
when 'mpls_vpn_multicast' ; safi = IANA.safi?(:mpls_vpn_multicast)
|
79
|
+
else
|
80
|
+
super
|
81
|
+
end
|
82
|
+
def_method(name,$1, afi,safi)
|
83
|
+
else
|
84
|
+
super
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def def_method(name, action, afi, safi)
|
91
|
+
self.class.instance_eval do
|
92
|
+
define_method("#{name}") do
|
93
|
+
path_id? action, afi, safi
|
94
|
+
end
|
95
|
+
end
|
96
|
+
__send__ name
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
class Capabilities < Base_Capabilities
|
102
|
+
def initialize(*args)
|
103
|
+
@memory = {}
|
104
|
+
super
|
105
|
+
end
|
106
|
+
def as4byte?
|
107
|
+
if @memory.has_key?(:as4byte)
|
108
|
+
@memory[:as4byte]
|
109
|
+
else
|
110
|
+
@memory[:as4byte]=super
|
111
|
+
end
|
112
|
+
end
|
113
|
+
def path_id?(*args)
|
114
|
+
if @memory.has_key?([:path_id, *args])
|
115
|
+
@memory[[:path_id, *args]]
|
116
|
+
else
|
117
|
+
@memory[[:path_id, *args]]=super
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
load "../../test/neighbor/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0
|