bgp4r 0.0.13 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/bgp/messages/update.rb +8 -0
  2. data/bgp/nlris/label.rb +9 -1
  3. data/bgp/nlris/labeled.rb +11 -0
  4. data/bgp/nlris/nlri.rb +15 -4
  5. data/bgp/nlris/prefix.rb +11 -0
  6. data/bgp/nlris/rd.rb +13 -1
  7. data/bgp/nlris/vpn.rb +59 -10
  8. data/bgp/path_attributes/aggregator.rb +19 -0
  9. data/bgp/path_attributes/aigp.rb +63 -0
  10. data/bgp/path_attributes/as_path.rb +57 -0
  11. data/bgp/path_attributes/atomic_aggregate.rb +3 -0
  12. data/bgp/path_attributes/attribute.rb +10 -0
  13. data/bgp/path_attributes/attributes.rb +1 -0
  14. data/bgp/path_attributes/cluster_list.rb +24 -1
  15. data/bgp/path_attributes/communities.rb +32 -1
  16. data/bgp/path_attributes/extended_communities.rb +26 -0
  17. data/bgp/path_attributes/extended_community.rb +101 -16
  18. data/bgp/path_attributes/local_pref.rb +10 -0
  19. data/bgp/path_attributes/mp_reach.rb +21 -2
  20. data/bgp/path_attributes/mp_unreach.rb +5 -1
  21. data/bgp/path_attributes/multi_exit_disc.rb +10 -0
  22. data/bgp/path_attributes/next_hop.rb +20 -0
  23. data/bgp/path_attributes/origin.rb +14 -0
  24. data/bgp/path_attributes/originator_id.rb +17 -6
  25. data/bgp/path_attributes/path_attribute.rb +87 -2
  26. data/bgp/path_attributes/tlvs/tlv.rb +18 -0
  27. data/test/unit/messages/update_test.rb +79 -5
  28. data/test/unit/neighbor/neighbor_test.rb +0 -1
  29. data/test/unit/nlris/label_test.rb +6 -0
  30. data/test/unit/nlris/labeled_test.rb +12 -0
  31. data/test/unit/nlris/prefix_test.rb +5 -1
  32. data/test/unit/nlris/rd_test.rb +2 -0
  33. data/test/unit/nlris/vpn_test.rb +50 -0
  34. data/test/unit/path_attributes/aggregator_test.rb +6 -0
  35. data/test/unit/path_attributes/{aigp.rb → aigp_test.rb} +29 -0
  36. data/test/unit/path_attributes/as_path_test.rb +53 -0
  37. data/test/unit/path_attributes/atomic_aggregate_test.rb +3 -0
  38. data/test/unit/path_attributes/cluster_list_test.rb +15 -10
  39. data/test/unit/path_attributes/communities_test.rb +9 -4
  40. data/test/unit/path_attributes/extended_communities_test.rb +31 -3
  41. data/test/unit/path_attributes/extended_community_test.rb +52 -9
  42. data/test/unit/path_attributes/local_pref_test.rb +5 -0
  43. data/test/unit/path_attributes/mp_reach_test.rb +51 -4
  44. data/test/unit/path_attributes/mp_unreach_test.rb +9 -7
  45. data/test/unit/path_attributes/next_hop_test.rb +3 -0
  46. data/test/unit/path_attributes/origin_test.rb +1 -0
  47. data/test/unit/path_attributes/path_attribute_test.rb +53 -2
  48. data/test/unit/path_attributes/tlvs/tlv_test.rb +33 -0
  49. data/test/unit/path_attributes/tunnel_encapsulation_test (Jean-Michel's Laptop's conflicted copy 2011-11-02).rb +388 -0
  50. metadata +14 -4
@@ -183,6 +183,14 @@ class BGP::Update < BGP::Message
183
183
  s.join("\n") + "\n" + msg.hexlify.join("\n") + "\n"
184
184
  end
185
185
 
186
+ def to_hash
187
+ h = {}
188
+ h[:withdrawns] = @withdrawn.to_ary if @withdrawn
189
+ h[:path_attributes] = @path_attribute.to_hash if @path_attribute
190
+ h[:nlris] = @nlri.to_ary if @nlri
191
+ h
192
+ end
193
+
186
194
  def self.withdrawn(u)
187
195
  if u.nlri and u.nlri.size>0
188
196
  Update.new(Withdrawn.new(*(u.nlri.nlris.collect { |n| n.to_s})))
data/bgp/nlris/label.rb CHANGED
@@ -58,8 +58,10 @@ module BGP
58
58
  @label_stack=[]
59
59
  if args.size==1 and args[0].is_a?(String) and args[0].is_packed?
60
60
  parse(args[0])
61
+ elsif args.size==1 and args[0].is_a?(Hash)
62
+ @label_stack = args[0][:labels].collect { |l| Label.new(l) }
61
63
  else
62
- args.each { |arg| @label_stack << (arg.is_a?(Label) ? arg : Label.new(arg)) }
64
+ @label_stack = args.collect { |arg| (arg.is_a?(Label) ? arg : Label.new(arg)) }
63
65
  end
64
66
  end
65
67
  def size
@@ -84,6 +86,12 @@ module BGP
84
86
  "Label Stack=#{@label_stack.collect{ |l| l.label }.join(',')} (bottom)"
85
87
  end
86
88
  end
89
+ def to_ary
90
+ @label_stack.collect { |e| e.label }
91
+ end
92
+ def to_hash
93
+ @label_stack.size==1 ? {:label=>@label_stack[0].label} : {:labels=> to_ary}
94
+ end
87
95
  def bit_length
88
96
  @label_stack.compact.size*24
89
97
  end
data/bgp/nlris/labeled.rb CHANGED
@@ -17,6 +17,11 @@ module BGP
17
17
  def initialize(*args)
18
18
  if args.size>0 and args[0].is_a?(String) and args[0].is_packed?
19
19
  parse(*args)
20
+ elsif args.size==1 and args[0].is_a?(Hash)
21
+ # TODO: a new_prefix :prefix=>, :labels=>
22
+ # :other=> ...
23
+ @prefix = Prefix.new(args[0][:prefix]) # assuming it's a Prefix, which it may not be
24
+ @labels = Label_stack.new(*args[0][:labels])
20
25
  else
21
26
  @prefix, *labels = args
22
27
  @labels = Label_stack.new(*labels)
@@ -25,6 +30,9 @@ module BGP
25
30
  #FIXME... a mixin path_id ????
26
31
  def path_id
27
32
  @prefix.path_id
33
+ rescue
34
+ # return nil
35
+ # RD: 0/0
28
36
  end
29
37
  def path_id=(val)
30
38
  @prefix.path_id=val
@@ -40,6 +48,9 @@ module BGP
40
48
  s
41
49
  end
42
50
  end
51
+ def to_hash
52
+ @labels.to_hash.merge(@prefix.to_hash)
53
+ end
43
54
  def to_s
44
55
  "#{@labels} #{@prefix}"
45
56
  end
data/bgp/nlris/nlri.rb CHANGED
@@ -94,18 +94,29 @@ module BGP
94
94
  def to_s(indent=0)
95
95
  @nlris.join("\n#{([' ']*indent).join}")
96
96
  end
97
-
97
+
98
98
  def size
99
99
  @nlris.size
100
100
  end
101
101
 
102
+ def to_ary
103
+ @nlris.collect { |n| n.to_s }
104
+ end
105
+
102
106
  end
103
107
 
104
108
  unless const_defined?(:Nlri)
105
- Nlri = Class.new(Base_nlri)
106
- Withdrawn = Class.new(Base_nlri)
109
+ Nlri = Class.new(Base_nlri) do
110
+ def to_hash
111
+ {:nlris=>to_ary}
112
+ end
113
+ end
114
+ Withdrawn = Class.new(Base_nlri) do
115
+ def to_hash
116
+ {:withdrawns=>to_ary}
117
+ end
118
+ end
107
119
  end
108
-
109
120
  class Nlri
110
121
  def self.factory(s, afi, safi, path_id=nil)
111
122
  if afi== 1 and safi==1
data/bgp/nlris/prefix.rb CHANGED
@@ -54,6 +54,9 @@ class Prefix
54
54
  @path_id=nil
55
55
  if args.size>1 and args[0].is_a?(Integer)
56
56
  @path_id, pfx, afi = args
57
+ elsif args.size==1 and args[0].is_a?(Hash)
58
+ @path_id = args[0][:path_id]
59
+ pfx = args[0][:prefix]
57
60
  else
58
61
  pfx, afi = args
59
62
  end
@@ -108,6 +111,14 @@ class Prefix
108
111
  end
109
112
  end
110
113
 
114
+ def to_hash
115
+ if extended?
116
+ {:prefix=> pfx_to_s}.merge({:path_id=>@path_id})
117
+ else
118
+ {:prefix=> pfx_to_s}
119
+ end
120
+ end
121
+
111
122
  def nexthop
112
123
  to_s.split('/')[0]
113
124
  end
data/bgp/nlris/rd.rb CHANGED
@@ -54,9 +54,21 @@ module BGP
54
54
  else
55
55
  @admin, @assign, @enc_type, = admin, assign, 0
56
56
  end
57
+ elsif args.size==1 and args[0].is_a?(Hash)
58
+ # assuming Rd.new :rd=> [100,100]
59
+ parse(Rd.new(*args[0][:rd]).encode)
57
60
  end
58
61
  end
59
-
62
+
63
+ def to_hash
64
+ #FIXME
65
+ if @admin.is_a?(IPAddr)
66
+ {:rd=> [@admin.to_s, @assign]}
67
+ else
68
+ {:rd=> [@admin, @assign]}
69
+ end
70
+ end
71
+
60
72
  def encode
61
73
  case @enc_type
62
74
  when 0
data/bgp/nlris/vpn.rb CHANGED
@@ -11,12 +11,20 @@ module BGP
11
11
  class Vpn
12
12
  attr_reader :prefix, :rd
13
13
  def initialize(*args)
14
+ @prefix=nil
14
15
  if args.size>0 and args[0].is_a?(String) and args[0].is_packed?
15
16
  parse(*args)
16
17
  else
17
- prefix, *rd = args
18
- self.prefix=(prefix)
19
- self.rd=rd
18
+ case args[0]
19
+ when String, Prefix
20
+ prefix, *rd = args
21
+ self.prefix=(prefix)
22
+ self.rd=rd
23
+ when Rd
24
+ *rd = args
25
+ @prefix=nil
26
+ self.rd=rd
27
+ end
20
28
  end
21
29
  end
22
30
  def prefix=(arg)
@@ -42,23 +50,38 @@ module BGP
42
50
  end
43
51
  def encode(len_included=true)
44
52
  if len_included
45
- [bit_length, @rd.encode, @prefix.encode_with_len_without_path_id].pack('Ca*a*')
53
+ [bit_length, @rd.encode, encode_prefix(false)].pack('Ca*a*')
46
54
  else
47
- @rd.encode + @prefix.encode_without_len_without_path_id
55
+ @rd.encode + encode_prefix(false)
48
56
  end
49
57
  end
58
+
59
+ def encode_prefix(len_included)
60
+ if @prefix
61
+ if len_included
62
+ @prefix.encode_with_len_without_path_id
63
+ else
64
+ @prefix.encode_without_len_without_path_id
65
+ end
66
+ else
67
+ ''
68
+ end
69
+ end
70
+
50
71
  def encode_without_len_without_path_id
51
72
  encode(false)
52
73
  end
74
+
53
75
  def path_id
54
76
  @prefix.path_id
55
- rescue
56
77
  end
57
78
  def path_id=(val)
58
79
  @prefix.path_id=val
59
80
  end
60
81
  def bit_length
61
- @rd.bit_length + @prefix.mlen
82
+ len = @rd.bit_length
83
+ len += @prefix.mlen if @prefix
84
+ len
62
85
  end
63
86
  def ipv4?
64
87
  @prefix.ipv4?
@@ -73,14 +96,40 @@ module BGP
73
96
  nbits = s.slice!(0,1).unpack('C')[0]
74
97
  rd,vpn = s.slice!(0,(7+nbits)/8).unpack("a8a*")
75
98
  @rd = Rd.new(rd.is_packed)
76
- @prefix= Prefix.new_ntop([nbits-64,vpn].pack('Ca*'), afi)
99
+ @prefix= Prefix.new_ntop([nbits-64,vpn].pack('Ca*'), afi) if vpn.size>0
77
100
  end
78
101
  def nexthop
79
102
  @prefix.nexthop
80
103
  end
81
- def to_s
104
+ def to_s(afi=1)
82
105
  #Label Stack=5806 (bottom) RD=3215:317720610, IPv4=10.45.142.64/32
83
- "#{@rd.to_s(false)}, #{@prefix.to_s_with_afi}"
106
+ "#{@rd.to_s(false)}, #{prefix_to_s(afi)}"
107
+ end
108
+
109
+ def to_hash
110
+ if @prefix
111
+ @rd.to_hash.merge(@prefix.to_hash)
112
+ else
113
+ @rd.to_hash
114
+ end
84
115
  end
116
+
117
+ private
118
+
119
+ def prefix_to_s(afi)
120
+ if @prefix
121
+ @prefix.to_s_with_afi
122
+ else
123
+ case afi
124
+ when 1 ; 'IPv4=0.0.0.0/0'
125
+ when 2 ; 'IPv6=0::0/0'
126
+ else
127
+ ''
128
+ end
129
+ end
130
+ end
131
+
85
132
  end
86
133
  end
134
+
135
+ load "../../test/unit/nlris/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0
@@ -36,6 +36,9 @@ module BGP
36
36
  @as = args[1]
37
37
  elsif args[0].is_a?(self.class)
38
38
  parse(args[0].encode, *args[1..-1])
39
+ elsif args[0].is_a?(Hash)
40
+ @ip_address = IPAddr.create(args[0][:address])
41
+ @as = args[0][:asn]
39
42
  else
40
43
  raise ArgumentError, "invalid argument, #{args.inspect}"
41
44
  end
@@ -48,6 +51,10 @@ module BGP
48
51
  def address
49
52
  @ip_address.to_s
50
53
  end
54
+
55
+ def asn
56
+ @as
57
+ end
51
58
 
52
59
  def as(sep='')
53
60
  case sep
@@ -78,6 +85,10 @@ module BGP
78
85
  super([@as].pack(f) + @ip_address.hton)
79
86
  end
80
87
 
88
+ def to_hash
89
+ {:asn=> asn, :address=> address}
90
+ end
91
+
81
92
  end
82
93
 
83
94
  class As4_aggregator < Aggregator
@@ -98,6 +109,14 @@ module BGP
98
109
  end
99
110
 
100
111
  end
112
+
113
+ class Aggregator
114
+ class << self
115
+ def new_hash(arg={})
116
+ new arg[:address], arg[:as]
117
+ end
118
+ end
119
+ end
101
120
 
102
121
  end
103
122
 
@@ -0,0 +1,63 @@
1
+ require 'bgp/path_attributes/attribute'
2
+
3
+ module BGP
4
+
5
+ class Aigp < Attr
6
+
7
+ def initialize(*args)
8
+ @flags, @type = OPTIONAL_NON_TRANSITIVE, ACCUMULATED_IGP_METRIC
9
+ if args[0].is_a?(String) and args[0].is_packed?
10
+ parse(args[0])
11
+ elsif args[0].is_a?(self.class)
12
+ parse(args[0].encode, *args[1..-1])
13
+ elsif args.size==1 and args[0].is_a?(Integer)
14
+ @aigp = args[0]
15
+ elsif args.empty?
16
+ @aigp=0
17
+ else
18
+ raise
19
+ end
20
+ end
21
+
22
+ def to_i
23
+ @aigp.to_i
24
+ end
25
+
26
+ def to_hash
27
+ {:metric=> to_i}
28
+ end
29
+
30
+ def accumulated_igp_metric
31
+ format("(0x%8.8x) %d", to_i, to_i)
32
+ end
33
+ alias metric accumulated_igp_metric
34
+
35
+ def to_s(method=:default)
36
+ super(accumulated_igp_metric, method)
37
+ end
38
+
39
+ def encode
40
+ super([1, 11, @aigp >> 32, @aigp & 0xffffffff].pack('CnN2'))
41
+ end
42
+
43
+ def parse(s)
44
+ @flags, @type, len, value=super(s)
45
+ _, _, high, low = value.unpack('CnNN')
46
+ @aigp = (high << 32) + low
47
+ end
48
+
49
+ end
50
+
51
+ class Aigp
52
+ class << self
53
+ def new_hash(_arg={})
54
+ arg = {:metric=>0}.merge(_arg)
55
+ new arg[:metric]
56
+ end
57
+ end
58
+ end
59
+
60
+
61
+ end
62
+
63
+ load "../../test/unit/path_attributes/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0
@@ -117,6 +117,9 @@ module BGP
117
117
  super(:set, *args)
118
118
  end
119
119
  end
120
+ def to_hash
121
+ {:set=> as}
122
+ end
120
123
  end
121
124
 
122
125
  class As_path::Sequence < As_path::Segment
@@ -127,6 +130,9 @@ module BGP
127
130
  super(:sequence, *args)
128
131
  end
129
132
  end
133
+ def to_hash
134
+ {:sequence=> as}
135
+ end
130
136
  end
131
137
 
132
138
  class As_path::Confed_set < As_path::Segment
@@ -137,6 +143,9 @@ module BGP
137
143
  super(:confed_set, *args)
138
144
  end
139
145
  end
146
+ def to_hash
147
+ {:confed_set=> as}
148
+ end
140
149
  end
141
150
 
142
151
  class As_path::Confed_sequence < As_path::Segment
@@ -147,6 +156,9 @@ module BGP
147
156
  super(:confed_sequence, *args)
148
157
  end
149
158
  end
159
+ def to_hash
160
+ {:confed_sequence=> as}
161
+ end
150
162
  end
151
163
 
152
164
  def integer?(arg)
@@ -204,6 +216,12 @@ module BGP
204
216
  @segments.find { |s| s.seg_type == SEQUENCE }
205
217
  end
206
218
 
219
+ def to_hash
220
+ h = {}
221
+ @segments.each { |s| h = h.merge(s.to_hash) }
222
+ { :as_path=> h}
223
+ end
224
+
207
225
  private
208
226
 
209
227
  def parse(s,as4byte=false)
@@ -225,6 +243,45 @@ module BGP
225
243
  end
226
244
  end
227
245
 
246
+ class As_path
247
+ class << self
248
+ def new_hash(arg={})
249
+ o = new
250
+ [:set, :sequence, :confed_sequence, :confed_set].each do |set_type|
251
+ next unless arg.has_key? set_type
252
+ case set_type
253
+ when :set
254
+ o << As_path::Set.new(*arg[:set])
255
+ when :sequence
256
+ o << As_path::Sequence.new(*arg[:sequence])
257
+ when :confed_set
258
+ o << As_path::Confed_set.new(*arg[:confed_set])
259
+ when :confed_sequence
260
+ o << As_path::Confed_sequence.new(*arg[:confed_sequence])
261
+ else
262
+ raise
263
+ end
264
+ end
265
+ o
266
+ end
267
+ end
268
+
269
+ class << self
270
+ def new_set(*args)
271
+ new_hash :set=>args
272
+ end
273
+ def new_sequence(*args)
274
+ new_hash :sequence=>args
275
+ end
276
+ def new_confed_set(*args)
277
+ new_hash :confed_set=>args
278
+ end
279
+ def new_confed_sequence(*args)
280
+ new_hash :confed_sequence=>args
281
+ end
282
+ end
283
+ end
284
+
228
285
  end
229
286
 
230
287
  load "../../test/unit/path_attributes/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0
@@ -35,6 +35,9 @@ module BGP
35
35
  raise ArgumentError, "invalid argument, #{arg.class} #{arg}"
36
36
  end
37
37
  end
38
+ def to_hash
39
+ {}
40
+ end
38
41
  end
39
42
 
40
43
  end
@@ -45,6 +45,8 @@ module BGP
45
45
  AS4_PATH = 17
46
46
  AS4_AGGREGATOR = 18
47
47
 
48
+ ACCUMULATED_IGP_METRIC = 26
49
+
48
50
  SET = 1
49
51
  SEQUENCE = 2
50
52
  CONFED_SEQUENCE = 3
@@ -136,6 +138,14 @@ module BGP
136
138
  s +="]"
137
139
  end
138
140
 
141
+ def is_optional?
142
+ (@flags&8>0)
143
+ end
144
+
145
+ def is_transitive?
146
+ (@flags&4>0)
147
+ end
148
+
139
149
  def attribute_name
140
150
  name.split('_').collect { |w| w.capitalize }.join(' ')
141
151
  end
@@ -12,6 +12,7 @@
12
12
  mp_unreach
13
13
  extended_communities
14
14
  path_attribute
15
+ aigp
15
16
  }.each do |attr|
16
17
  require "bgp/path_attributes/#{attr}"
17
18
  end
@@ -100,7 +100,11 @@ module BGP
100
100
  def sort
101
101
  Cluster_list.new(to_ary.sort)
102
102
  end
103
-
103
+
104
+ def to_hash
105
+ {:cluster_ids=>@cluster_ids.collect { |c| c.to_s }}
106
+ end
107
+
104
108
  def sort!
105
109
  @cluster_ids = @cluster_ids.sort_by { |c| c.to_i }
106
110
  self
@@ -111,6 +115,25 @@ module BGP
111
115
  end
112
116
 
113
117
  end
118
+
119
+ class Cluster_list
120
+ class << self
121
+ def new_hash(arg={})
122
+ o = new
123
+ [:cluster_ids].each do |set_type|
124
+ next unless arg.has_key? set_type
125
+ case set_type
126
+ when :cluster_ids
127
+ o << arg[set_type]
128
+ else
129
+ raise
130
+ end
131
+ end
132
+ o
133
+
134
+ end
135
+ end
136
+ end
114
137
 
115
138
  end
116
139
 
@@ -27,7 +27,17 @@ module BGP
27
27
  class Communities < Attr
28
28
 
29
29
  class Community
30
-
30
+
31
+ class << self
32
+ def method_missing(name, *args, &block)
33
+ if name.to_s =~ /^(no_export|no_advertise|no_export_sub_confed|no_peer)$/
34
+ new name
35
+ else
36
+ super
37
+ end
38
+ end
39
+ end
40
+
31
41
  unless const_defined? :NO_EXPORT
32
42
  NO_EXPORT = 0xFFFFFF01
33
43
  NO_ADVERTISE = 0xFFFFFF02
@@ -79,6 +89,22 @@ module BGP
79
89
 
80
90
  end
81
91
 
92
+ class << self
93
+ def new_hash(arg={})
94
+ o = new
95
+ [:communities].each do |set_type|
96
+ next unless arg.has_key? set_type
97
+ case set_type
98
+ when :communities
99
+ o << arg[set_type]
100
+ else
101
+ raise
102
+ end
103
+ end
104
+ o
105
+ end
106
+ end
107
+
82
108
  def initialize(*args)
83
109
  @flags, @type = OPTIONAL_TRANSITIVE, COMMUNITIES
84
110
  if args[0].is_a?(String) and args[0].is_packed?
@@ -114,6 +140,10 @@ module BGP
114
140
  def to_s(method=:default)
115
141
  super(communities, method)
116
142
  end
143
+
144
+ def to_hash
145
+ { :communities=> @communities.collect { |comm| comm.to_s } }
146
+ end
117
147
 
118
148
  def encode
119
149
  super(@communities.collect { |comm| comm.encode }.join)
@@ -171,4 +201,5 @@ module BGP
171
201
  end
172
202
 
173
203
  end
204
+
174
205
  load "../../test/unit/path_attributes/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0
@@ -28,6 +28,26 @@ module BGP
28
28
 
29
29
  class Extended_communities < Attr
30
30
 
31
+ class << self
32
+ def new_hash(arg={})
33
+ o = new
34
+ arg.keys.each do |comm|
35
+ case comm
36
+ when :color ; o << Color.new(*arg[comm])
37
+ when :route_target ; o << Route_target.new(*arg[comm])
38
+ when :link_bandwidth ; o << Link_bandwidth.new(*arg[comm])
39
+ when :ospf_domain_id ; o << Ospf_domain_id.new(*arg[comm])
40
+ when :encapsulation ; o << Encapsulation.new(*arg[comm])
41
+ when :route_origin ; o << Route_origin.new(*arg[comm])
42
+ when :ospf_router_id ; o << Ospf_router_id.new(arg[comm])
43
+ else
44
+ raise
45
+ end
46
+ end
47
+ o
48
+ end
49
+ end
50
+
31
51
  attr_reader :communities
32
52
 
33
53
  def initialize(*args)
@@ -59,6 +79,12 @@ module BGP
59
79
  end
60
80
  alias << add
61
81
 
82
+ def to_hash
83
+ h = {}
84
+ @communities.each { |c| h = h.merge( { c.class.to_s.split('::').last.downcase.to_sym => c.instance_eval { value2 } }) }
85
+ h
86
+ end
87
+
62
88
  def extended_communities
63
89
  len = @communities.size*8
64
90
  s=[]