bgp4r 0.0.11 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
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