jesnault-bgp4r 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/bgp/aggregator.rb +104 -0
  2. data/bgp/as_path.rb +223 -0
  3. data/bgp/atomic_aggregate.rb +42 -0
  4. data/bgp/attribute.rb +181 -0
  5. data/bgp/attributes.rb +34 -0
  6. data/bgp/cluster_list.rb +117 -0
  7. data/bgp/common.rb +204 -0
  8. data/bgp/communities.rb +139 -0
  9. data/bgp/extended_communities.rb +107 -0
  10. data/bgp/extended_community.rb +254 -0
  11. data/bgp/iana.rb +269 -0
  12. data/bgp/io.rb +116 -0
  13. data/bgp/label.rb +94 -0
  14. data/bgp/local_pref.rb +75 -0
  15. data/bgp/message.rb +894 -0
  16. data/bgp/mp_reach.rb +208 -0
  17. data/bgp/multi_exit_disc.rb +76 -0
  18. data/bgp/neighbor.rb +291 -0
  19. data/bgp/next_hop.rb +63 -0
  20. data/bgp/nlri.rb +303 -0
  21. data/bgp/orf.rb +88 -0
  22. data/bgp/origin.rb +88 -0
  23. data/bgp/originator_id.rb +73 -0
  24. data/bgp/path_attribute.rb +210 -0
  25. data/bgp/prefix_orf.rb +263 -0
  26. data/bgp/rd.rb +107 -0
  27. data/examples/bgp +65 -0
  28. data/examples/routegen +85 -0
  29. data/examples/routegen.yml +50 -0
  30. data/test/aggregator_test.rb +66 -0
  31. data/test/as_path_test.rb +149 -0
  32. data/test/atomic_aggregate_test.rb +35 -0
  33. data/test/attribute_test.rb +57 -0
  34. data/test/cluster_list_test.rb +39 -0
  35. data/test/common_test.rb +68 -0
  36. data/test/communities_test.rb +75 -0
  37. data/test/extended_communities_test.rb +111 -0
  38. data/test/extended_community_test.rb +93 -0
  39. data/test/label_test.rb +50 -0
  40. data/test/local_pref_test.rb +43 -0
  41. data/test/message_test.rb +294 -0
  42. data/test/mp_reach_test.rb +143 -0
  43. data/test/multi_exit_disc_test.rb +46 -0
  44. data/test/neighbor_test.rb +50 -0
  45. data/test/next_hop_test.rb +37 -0
  46. data/test/nlri_test.rb +189 -0
  47. data/test/origin_test.rb +57 -0
  48. data/test/originator_id_test.rb +38 -0
  49. data/test/path_attribute_test.rb +127 -0
  50. data/test/prefix_orf_test.rb +97 -0
  51. data/test/rd_test.rb +44 -0
  52. metadata +84 -11
data/bgp/aggregator.rb ADDED
@@ -0,0 +1,104 @@
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
+ require 'bgp/attribute'
24
+
25
+ module BGP
26
+
27
+ class Aggregator < Attr
28
+
29
+ def initialize(*args)
30
+ @flags, @type, @as4byte =OPTIONAL_TRANSITIVE, AGGREGATOR, false
31
+ if args[0].is_a?(String) and args[0].is_packed?
32
+ parse(*args)
33
+ elsif args.size==2 and
34
+ args[0].is_a?(String) and (args[1].is_a?(Fixnum) or args[1].is_a?(Bignum))
35
+ @ip_address = IPAddr.create(args[0])
36
+ @as = args[1]
37
+ elsif args[0].is_a?(self.class)
38
+ parse(args[0].encode, *args[1..-1])
39
+ else
40
+ raise ArgumentError, "invalid argument, #{args.inspect}"
41
+ end
42
+ end
43
+
44
+ def address=(val)
45
+ @ip_address=IPAddr.create(val)
46
+ end
47
+
48
+ def address
49
+ @ip_address.to_s
50
+ end
51
+
52
+ def as(sep='')
53
+ case sep
54
+ when '.'
55
+ has = @as >> 16
56
+ las = @as & 0xffff
57
+ [has,las].join('.')
58
+ else
59
+ @as
60
+ end
61
+ end
62
+
63
+ def aggregator(as4byte=@as4byte)
64
+ "#{address}, #{as(as4byte ? '.':'')}"
65
+ end
66
+
67
+ def to_s(method=:default)
68
+ super(aggregator, method)
69
+ end
70
+
71
+ def parse(s,as4byte=false)
72
+ @flags, @type, len, @as, addr = super(s, as4byte ? 'NN' : 'nN')
73
+ self.address = addr
74
+ end
75
+
76
+ def encode(as4byte=@as4byte)
77
+ f = as4byte ? 'N' : 'n'
78
+ super([@as].pack(f) + @ip_address.hton)
79
+ end
80
+
81
+ end
82
+
83
+ class As4_aggregator < Aggregator
84
+ def initialize(*args)
85
+ super(*args)
86
+ @flags, @type, @as4byte =OPTIONAL_TRANSITIVE, AS4_AGGREGATOR, true
87
+ end
88
+ def parse(s,as4byte=@as4byte)
89
+ super(s,true)
90
+ end
91
+ def encode(as4byte=@as4byte)
92
+ f = as4byte ? 'N' : 'n'
93
+ super([@as].pack(f) + @ip_address.hton)
94
+ end
95
+
96
+ def aggregator(as4byte=@as4byte)
97
+ super(true)
98
+ end
99
+
100
+ end
101
+
102
+ end
103
+
104
+ load "../test/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0
data/bgp/as_path.rb ADDED
@@ -0,0 +1,223 @@
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
+ require 'bgp/attribute'
24
+
25
+ module BGP
26
+
27
+ class As_path < Attr
28
+
29
+ ##############################
30
+
31
+ class Segment
32
+ include ATTR
33
+
34
+ def self.factory(s, as4byte=false)
35
+ seg_type, num = s.slice(0,2).unpack('CC')
36
+ len = num * (as4byte ? 4 : 2)+2
37
+ segment = s.slice!(0, len).is_packed
38
+ case seg_type
39
+ when SET ; Set.new(segment, as4byte)
40
+ when SEQUENCE ; Sequence.new(segment, as4byte)
41
+ when CONFED_SEQUENCE ; Confed_sequence.new(segment, as4byte)
42
+ when CONFED_SET ; Confed_set.new(segment, as4byte)
43
+ end
44
+ end
45
+
46
+ attr_reader :as
47
+
48
+ def initialize(seg_type, *args)
49
+ @as=[]
50
+ if seg_type.is_a?(String) and seg_type.is_packed?
51
+ parse(seg_type, *args)
52
+ elsif args.size>0 and args.is_a?(Array)
53
+ self.seg_type = seg_type
54
+ @as = args
55
+ else
56
+ raise ArgumentError, "invalid argument #{args.inspect}"
57
+ end
58
+
59
+ end
60
+
61
+ def seg_type
62
+ @seg_type
63
+ end
64
+
65
+ def seg_type=(val)
66
+ case val
67
+ when :set ; @seg_type = SET
68
+ when :sequence ; @seg_type = SEQUENCE
69
+ when :confed_sequence ; @seg_type = CONFED_SEQUENCE
70
+ when :confed_set ; @seg_type = CONFED_SET
71
+ else
72
+ raise ArgumentError, "invalid segment type #{val.class} #{val}"
73
+ end
74
+ end
75
+
76
+ def encode(as4byte=false)
77
+ [@seg_type, @as.size, @as].flatten.pack("CC#{as4byte ? 'N' : 'n'}*")
78
+ end
79
+
80
+ def as4byte?
81
+ defined?(@as4byte) and @as4byte
82
+ end
83
+
84
+ def parse(s, as4byte=false)
85
+ @seg_type, skip, *@as = s.unpack("CC#{as4byte ? 'N' : 'n'}*")
86
+ @as = [@as].flatten
87
+ end
88
+
89
+ def to_s
90
+ case @seg_type
91
+ when SET ; s = "{" ; join=", "
92
+ when SEQUENCE ; s = "" ; join= " "
93
+ when CONFED_SET ; s = "[" ; join= ", "
94
+ when CONFED_SEQUENCE ; s = "(" ; join= " "
95
+ else ; s = "?("
96
+ end
97
+ s += @as.join(join)
98
+ case @seg_type
99
+ when SET ; s += "}"
100
+ when CONFED_SET ; s += "]"
101
+ when CONFED_SEQUENCE ; s += ")"
102
+ when SEQUENCE
103
+ else ; s += ")"
104
+ end
105
+ s
106
+ end
107
+
108
+ end
109
+
110
+ class As_path::Set < As_path::Segment
111
+ def initialize(*args)
112
+ if args[0].is_a?(String)
113
+ super(*args)
114
+ else
115
+ super(:set, *args)
116
+ end
117
+ end
118
+ end
119
+
120
+ class As_path::Sequence < As_path::Segment
121
+ def initialize(*args)
122
+ if args[0].is_a?(String)
123
+ super(*args)
124
+ else
125
+ super(:sequence, *args)
126
+ end
127
+ end
128
+ end
129
+
130
+ class As_path::Confed_set < As_path::Segment
131
+ def initialize(*args)
132
+ if args[0].is_a?(String)
133
+ super(*args)
134
+ else
135
+ super(:confed_set, *args)
136
+ end
137
+ end
138
+ end
139
+
140
+ class As_path::Confed_sequence < As_path::Segment
141
+ def initialize(*args)
142
+ if args[0].is_a?(String)
143
+ super(*args)
144
+ else
145
+ super(:confed_sequence, *args)
146
+ end
147
+ end
148
+ end
149
+
150
+ ##############################
151
+
152
+ def integer?(arg)
153
+ arg.is_a?(Fixnum) or arg.is_a?(Bignum)
154
+ end
155
+
156
+ attr_accessor :as4byte
157
+
158
+ def initialize(*args)
159
+
160
+ @flags, @type, @segments, @as4byte = WELL_KNOWN_MANDATORY, AS_PATH, [], false
161
+
162
+ if args[0].is_a?(String) and args[0].is_packed?
163
+ parse(*args)
164
+ elsif args[0].is_a?(self.class)
165
+ parse(args[0].encode, *args[1..-1])
166
+ elsif integer?(args[0])
167
+ @segments << Sequence.new(*args.dup)
168
+ elsif args[0].is_a?(Segment)
169
+ unless args.find { |seg| ! seg.is_a?(Segment) }.nil?
170
+ raise ArgumentError, "at least one arg is not a segment"
171
+ end
172
+ @segments = args.dup
173
+ end
174
+
175
+ end
176
+
177
+ def <<(val)
178
+ raise ArgumentError, "invalid argument, #{val.class} #{val}" unless val.is_a?(Segment)
179
+ @segments << val
180
+ end
181
+
182
+ def encode(as4byte=@as4byte)
183
+ super(@segments.collect { |segment| segment.encode(as4byte) }.join)
184
+ end
185
+
186
+ def encode4
187
+ encode(true)
188
+ end
189
+
190
+ def as_path
191
+ return 'empty' if @segments.empty?
192
+ @segments.collect { |seg| seg.to_s }.join(' ')
193
+ end
194
+
195
+ def to_s(method=:default, as4byte=false)
196
+ super(as_path, method, as4byte)
197
+ end
198
+
199
+ private
200
+
201
+ def parse(s,as4byte=false)
202
+ @flags, @type, len, value=super(s)
203
+ while value.size>0
204
+ @segments << Segment.factory(value.is_packed, as4byte)
205
+ end
206
+ end
207
+
208
+ end
209
+
210
+ class As4_path < As_path
211
+ def initialize(*args)
212
+ super(*args)
213
+ @flags, @type, @as4byte =OPTIONAL_TRANSITIVE, AS4_PATH, true
214
+ end
215
+ def parse(s,as4byte=@as4byte)
216
+ super(s,true)
217
+ end
218
+ end
219
+
220
+ end
221
+
222
+ load "../test/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0
223
+
@@ -0,0 +1,42 @@
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
+
24
+ require 'bgp/attribute'
25
+
26
+ module BGP
27
+
28
+ class Atomic_aggregate < Attr
29
+ def initialize(arg=nil)
30
+ @flags, @type=OPTIONAL, ATOMIC_AGGREGATE
31
+ if arg.is_a?(String) and arg.is_packed?
32
+ parse(arg)
33
+ elsif arg.nil?
34
+ else
35
+ raise ArgumentError, "invalid argument, #{arg.class} #{arg}"
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ load "../test/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0
data/bgp/attribute.rb ADDED
@@ -0,0 +1,181 @@
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
+ require 'bgp/common'
24
+
25
+ module BGP
26
+
27
+ module ATTR
28
+
29
+ ### http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-2
30
+
31
+ ORIGIN = 1
32
+ AS_PATH = 2
33
+ NEXT_HOP = 3
34
+ MULTI_EXIT_DISC = 4
35
+ LOCAL_PREF = 5
36
+ ATOMIC_AGGREGATE = 6
37
+ AGGREGATOR = 7
38
+ COMMUNITIES = 8
39
+ ORIGINATOR_ID = 9
40
+ CLUSTER_LIST = 10
41
+
42
+ MP_REACH = 14
43
+ MP_UNREACH = 15
44
+ EXTENDED_COMMUNITY = 16
45
+ AS4_PATH = 17
46
+ AS4_AGGREGATOR = 18
47
+
48
+ SET = 1
49
+ SEQUENCE = 2
50
+ CONFED_SEQUENCE = 3
51
+ CONFED_SET = 4
52
+
53
+ OPTIONAL = 0x8
54
+ TRANSITIVE = 0x4
55
+ PARTIAL = 0x2
56
+ EXTENDED_LENGTH = 0x1
57
+ NONE = 0x0
58
+
59
+ WELL_KNOWN_MANDATORY = TRANSITIVE
60
+ WELL_KNOWN_DISCRETIONARY = TRANSITIVE
61
+ OPTIONAL_TRANSITIVE = OPTIONAL | TRANSITIVE
62
+ OPTIONAL_NON_TRANSITIVE = OPTIONAL
63
+
64
+ def encode(value='',value_fmt=nil)
65
+ len, len_fmt = value.size, 'C'
66
+ if len>255
67
+ @flags |= EXTENDED_LENGTH
68
+ len_fmt='n'
69
+ end
70
+ if value_fmt
71
+ [@flags<<4, @type, len, *value].pack("CC#{len_fmt}#{value_fmt}")
72
+ else
73
+ ([@flags<<4, @type, len].pack("CC#{len_fmt}") + value).is_packed
74
+ end
75
+ end
76
+
77
+ def parse(*args)
78
+ _parse_(*args)
79
+ end
80
+
81
+ def _parse_(s,vf=nil)
82
+ value, arr = '', []
83
+ flags = s.unpack('C')[0]
84
+ if flags & 0x10>0
85
+ arr = s.unpack("CCn")
86
+ len = arr[2]
87
+ if vf
88
+ value = s[4..-1].unpack(vf)
89
+ else
90
+ value = s[4..-1].unpack("a#{len}")
91
+ end
92
+ s.slice!(0,arr[2]+4).is_packed
93
+ else
94
+ arr = s.unpack("CCC")
95
+ len = arr[2]
96
+ if vf
97
+ value = s[3..-1].unpack(vf)
98
+ else
99
+ value = s[3..-1].unpack("a#{len}")
100
+ end
101
+ s.slice!(0,arr[2]+3).is_packed
102
+ end
103
+ arr[0]= arr[0] >>4
104
+ value[0].is_packed unless vf
105
+ arr + value
106
+ end
107
+
108
+ def name
109
+ self.class.to_s.split('::').last
110
+ end
111
+
112
+ def flags
113
+ flags = @flags
114
+ s ="["
115
+ (flags&8>0) ? s +="O" : s += "w"
116
+ (flags&4>0) ? s +="T" : s += "n"
117
+ (flags&2>0) ? s +="P" : s += "c"
118
+ (flags&1>0) ? s +="E" : s += "r"
119
+ s +="]"
120
+ end
121
+
122
+ def flags_short
123
+ flags = @flags
124
+ s ="["
125
+ s +="O" if (flags&8>0)
126
+ s +="T" if (flags&4>0)
127
+ s +="P" if (flags&2>0)
128
+ s +="E" if (flags&1>0)
129
+ s +="]"
130
+ end
131
+
132
+ def attribute_name
133
+ name.split('_').collect { |w| w.capitalize }.join(' ')
134
+ end
135
+
136
+ def to_s(value='', mode=:tcpdump, as4byte=false)
137
+
138
+ shex = as4byte ? to_shex4_len(20) : to_shex_len(20)
139
+
140
+ mode = :brief unless [:tcpdump, :brief, :hexlify].include?(mode)
141
+
142
+ case mode
143
+ when :brief
144
+ sprintf "%s %4s %10s: [%s]%s", flags, "(#{@type})", attribute_name, shex, "#{value.size>0 ? " '#{value}'" :''}"
145
+ when :hexlify
146
+ s = sprintf "%s %4s %10s: [%s]%s", flags, "(#{@type})", attribute_name, shex, "#{value.size>0 ? " '#{value}" :''}"
147
+ s +="\n\n"
148
+ if as4byte
149
+ s += self.encode4.hexlify.join("\n")
150
+ else
151
+ s += self.encode.hexlify.join("\n")
152
+ end
153
+ when :tcpdump
154
+ if as4byte
155
+ f, t, len, enc_value = _parse_(self.encode4, nil)
156
+ else
157
+ f, t, len, enc_value = _parse_(self.encode, nil)
158
+ end
159
+ s = sprintf "%s (%d), length: %d, Flags %s: %s", attribute_name, @type, len, flags_short, value
160
+ s += enc_value.hexlify.join(("\n "))
161
+ end
162
+ end
163
+
164
+ end
165
+
166
+ class Attr
167
+
168
+ include ATTR
169
+ include Comparable
170
+
171
+ def method_missing(name, *args, &block)
172
+ if name == :encode4
173
+ send :encode, *args, &block
174
+ else
175
+ super
176
+ end
177
+ end
178
+
179
+ end
180
+
181
+ end
data/bgp/attributes.rb ADDED
@@ -0,0 +1,34 @@
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
+ require 'bgp/origin'
24
+ require 'bgp/next_hop'
25
+ require 'bgp/local_pref'
26
+ require 'bgp/multi_exit_disc'
27
+ require 'bgp/as_path'
28
+ require 'bgp/communities'
29
+ require 'bgp/aggregator'
30
+ require 'bgp/atomic_aggregate'
31
+ require 'bgp/originator_id'
32
+ require 'bgp/cluster_list'
33
+ require 'bgp/mp_reach'
34
+ require 'bgp/extended_communities'
@@ -0,0 +1,117 @@
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
+
24
+ require 'bgp/attribute'
25
+
26
+ module BGP
27
+
28
+ class Cluster_list < Attr
29
+
30
+ ##########################################################
31
+ # CLUSTER ID #
32
+ ##########################################################
33
+ class Id
34
+ def initialize(id)
35
+ @id = IPAddr.create(id)
36
+ end
37
+ def to_s
38
+ @id.to_s
39
+ end
40
+ def to_i
41
+ @id.to_i
42
+ end
43
+ def encode
44
+ @id.hton
45
+ end
46
+ end
47
+
48
+ ##########################################################
49
+ # CLUSTER_LIST ATTRIBUTE #
50
+ ##########################################################
51
+
52
+ def initialize(*args)
53
+ @flags, @type = OPTIONAL, CLUSTER_LIST
54
+ if args[0].is_a?(String) and args[0].is_packed?
55
+ parse(args[0])
56
+ elsif args[0].is_a?(self.class)
57
+ parse(args[0].encode, *args[1..-1])
58
+ else
59
+ add(*args)
60
+ end
61
+ end
62
+
63
+ def add(*args)
64
+ @cluster_ids ||=[]
65
+ args.flatten.each do |arg|
66
+ if arg.is_a?(String) and arg.split(' ').size>1
67
+ arg.split.each { |v| @cluster_ids << Id.new(v) }
68
+ elsif arg.is_a?(String) and arg.split(',').size>1
69
+ arg.split(',').each { |v| @cluster_ids << Id.new(v) }
70
+ elsif arg.is_a?(Id)
71
+ @cluster_ids << arg
72
+ else
73
+ @cluster_ids << Id.new(arg)
74
+ end
75
+ end
76
+ end
77
+ alias << add
78
+
79
+ def cluster_list
80
+ @cluster_ids.collect { |comm| comm.to_s }.join(' ')
81
+ end
82
+
83
+ def to_s(method=:default)
84
+ super(cluster_list, method)
85
+ end
86
+
87
+ def to_ary
88
+ @cluster_ids.collect { |c| c.to_i }
89
+ end
90
+
91
+ def encode
92
+ super(@cluster_ids.collect { |comm| comm.encode }.join)
93
+ end
94
+
95
+ def parse(s)
96
+ @flags, @type, len, value=super(s)
97
+ self << value.unpack("N#{len/4}")
98
+ end
99
+
100
+ def sort
101
+ Cluster_list.new(to_ary.sort)
102
+ end
103
+
104
+ def sort!
105
+ @cluster_ids = @cluster_ids.sort_by { |c| c.to_i }
106
+ self
107
+ end
108
+
109
+ def <=>(other)
110
+ self.sort.to_shex <=> other.sort.to_shex
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+
117
+ load "../test/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0