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.
Files changed (44) hide show
  1. data/bgp/common.rb +14 -13
  2. data/bgp/iana.rb +26 -1
  3. data/bgp/messages/capability.rb +3 -2
  4. data/bgp/messages/message.rb +3 -2
  5. data/bgp/messages/open.rb +2 -1
  6. data/bgp/messages/update.rb +80 -30
  7. data/bgp/neighbor/add_path_cap.rb +125 -0
  8. data/bgp/{neighbor.rb → neighbor/neighbor.rb} +64 -68
  9. data/bgp/nlris/nlri.rb +289 -15
  10. data/bgp/nlris/prefix.rb +3 -2
  11. data/bgp/nlris/vpn.rb +1 -8
  12. data/bgp/optional_parameters/add_path.rb +160 -0
  13. data/bgp/optional_parameters/capabilities.rb +1 -0
  14. data/bgp/optional_parameters/capability.rb +6 -0
  15. data/bgp/optional_parameters/graceful_restart.rb +6 -6
  16. data/bgp/optional_parameters/optional_parameter.rb +1 -0
  17. data/bgp/path_attributes/as_path.rb +1 -1
  18. data/bgp/path_attributes/attribute.rb +12 -5
  19. data/bgp/path_attributes/mp_reach.rb +142 -96
  20. data/bgp/path_attributes/mp_unreach.rb +73 -20
  21. data/bgp/path_attributes/path_attribute.rb +28 -5
  22. data/bgp4r.gemspec +21 -6
  23. data/bgp4r.rb +1 -1
  24. data/examples/unit-testing/malformed_update.rb +2 -1
  25. data/examples/unit-testing/test.rb +82 -0
  26. data/examples/unit-testing/test1.rb +82 -0
  27. data/examples/unit-testing/test2.rb +44 -0
  28. data/test/common_test.rb +7 -0
  29. data/test/helpers/server.rb +20 -0
  30. data/test/iana_test.rb +43 -0
  31. data/test/messages/open_test.rb +7 -2
  32. data/test/messages/update_test.rb +133 -36
  33. data/test/neighbor/add_path_cap_test.rb +54 -0
  34. data/test/neighbor/neighbor_test.rb +161 -0
  35. data/test/nlris/ext_nlri_test.rb +25 -60
  36. data/test/nlris/nlri_test.rb +93 -115
  37. data/test/optional_parameters/add_path_test.rb +53 -0
  38. data/test/optional_parameters/capability_test.rb +10 -0
  39. data/test/optional_parameters/graceful_restart_test.rb +1 -0
  40. data/test/path_attributes/mp_reach_test.rb +206 -8
  41. data/test/path_attributes/mp_unreach_test.rb +113 -5
  42. data/test/path_attributes/path_attribute_test.rb +34 -2
  43. metadata +20 -7
  44. 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
- #TODO netmask ipv6
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
- end
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
@@ -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
 
@@ -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, as4byte=false)
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, as4byte)
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) }.nil? ? false : true
88
+ @opt_parms.find { |a| a.is_a?(klass) }
88
89
  end
89
90
 
90
91
  def to_hash
@@ -42,15 +42,28 @@ require 'bgp/path_attributes/path_attribute'
42
42
 
43
43
  class BGP::Update < BGP::Message
44
44
  include BGP
45
- def as4byte?
46
- @as4byte ||= false
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
- @as4byte=false
53
- parse(*args)
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(as4byte=false)
87
- @as4byte=as4byte
106
+ def encode(session_info=@session_info)
88
107
  withdrawn, path_attribute, nlri = '', '', ''
89
- withdrawn = @withdrawn.encode(false) if defined? @withdrawn and @withdrawn
90
- path_attribute = @path_attribute.encode(as4byte) if defined?(@path_attribute) and @path_attribute
91
- nlri = @nlri.encode if defined? @nlri and @nlri
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
- # CHANGED ME: NO DEFAULT HERE, the factory calling us has to tell what it is giving us.
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
- self.withdrawn=Withdrawn.new(update.slice!(0,len).is_packed) if len>0
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
- enc_path_attribute = update.slice!(0,len).is_packed
109
- self.path_attribute=Path_attribute.new(enc_path_attribute, as4byte) if len>0
110
- self.nlri = Nlri.new(update) if update.size>0
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(as4byte=@as4byte)
130
- # def to_s(fmt=:tcpdump)
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
- s << @withdrawn.to_s if defined?(@withdrawn) and @withdrawn
135
- s << @path_attribute.to_s(fmt, as4byte) if defined?(@path_attribute) and @path_attribute
136
- s << @nlri.to_s if defined?(@nlri) and @nlri
137
- "Update Message (#{UPDATE}), #{as4byte ? "4 bytes AS, " : ''}length: #{msg.size}\n " + s.join("\n") + "\n" + msg.hexlify.join("\n") + "\n"
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