jesnault-bgp4r 0.0.2 → 0.0.3

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 (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