bgp4r 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/COPYING +674 -0
  2. data/LICENSE.txt +53 -0
  3. data/README.rdoc +259 -0
  4. data/bgp/aggregator.rb +104 -0
  5. data/bgp/as_path.rb +223 -0
  6. data/bgp/atomic_aggregate.rb +42 -0
  7. data/bgp/attribute.rb +181 -0
  8. data/bgp/attributes.rb +34 -0
  9. data/bgp/cluster_list.rb +117 -0
  10. data/bgp/common.rb +204 -0
  11. data/bgp/communities.rb +139 -0
  12. data/bgp/extended_communities.rb +107 -0
  13. data/bgp/extended_community.rb +254 -0
  14. data/bgp/iana.rb +269 -0
  15. data/bgp/io.rb +116 -0
  16. data/bgp/label.rb +94 -0
  17. data/bgp/local_pref.rb +75 -0
  18. data/bgp/message.rb +894 -0
  19. data/bgp/mp_reach.rb +208 -0
  20. data/bgp/multi_exit_disc.rb +76 -0
  21. data/bgp/neighbor.rb +291 -0
  22. data/bgp/next_hop.rb +63 -0
  23. data/bgp/nlri.rb +303 -0
  24. data/bgp/orf.rb +88 -0
  25. data/bgp/origin.rb +88 -0
  26. data/bgp/originator_id.rb +73 -0
  27. data/bgp/path_attribute.rb +210 -0
  28. data/bgp/prefix_orf.rb +263 -0
  29. data/bgp/rd.rb +107 -0
  30. data/examples/bgp +65 -0
  31. data/examples/routegen +85 -0
  32. data/examples/routegen.yml +50 -0
  33. data/test/aggregator_test.rb +66 -0
  34. data/test/as_path_test.rb +149 -0
  35. data/test/atomic_aggregate_test.rb +35 -0
  36. data/test/attribute_test.rb +57 -0
  37. data/test/cluster_list_test.rb +39 -0
  38. data/test/common_test.rb +68 -0
  39. data/test/communities_test.rb +75 -0
  40. data/test/extended_communities_test.rb +111 -0
  41. data/test/extended_community_test.rb +93 -0
  42. data/test/label_test.rb +50 -0
  43. data/test/local_pref_test.rb +43 -0
  44. data/test/message_test.rb +294 -0
  45. data/test/mp_reach_test.rb +143 -0
  46. data/test/multi_exit_disc_test.rb +46 -0
  47. data/test/neighbor_test.rb +50 -0
  48. data/test/next_hop_test.rb +37 -0
  49. data/test/nlri_test.rb +189 -0
  50. data/test/origin_test.rb +57 -0
  51. data/test/originator_id_test.rb +38 -0
  52. data/test/path_attribute_test.rb +127 -0
  53. data/test/prefix_orf_test.rb +97 -0
  54. data/test/rd_test.rb +44 -0
  55. metadata +133 -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
data/bgp/common.rb ADDED
@@ -0,0 +1,204 @@
1
+ # This file is part of BGP4R.
2
+ #
3
+ # BGP4R is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # BGP4R is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with Foobar. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'ipaddr'
17
+ require 'logger'
18
+
19
+ class IPAddr
20
+ alias encode hton
21
+
22
+ def self.create(arg)
23
+ if arg.is_a?(String) and arg.is_packed?
24
+ IPAddr.new_ntoh(arg)
25
+ elsif arg.is_a?(Integer)
26
+ IPAddr.new_ntoh([arg].pack('N'))
27
+ elsif arg.is_a?(Array) and arg[0].is_a?(Fixnum)
28
+ IPAddr.new_ntoh([arg].pack('C*'))
29
+ else
30
+ IPAddr.new(arg)
31
+ end
32
+ end
33
+
34
+ #TODO: if not used, get rid of it.
35
+ def self.new_nlri4(arg)
36
+ if arg.is_a?(String) and arg.is_packed?
37
+ arg +=([0]*3).pack('C*')
38
+ plen, *nlri = arg.unpack('CC4')
39
+ ipaddr = nlri.collect { |n| n.to_s }.join('.') + "/" + plen .to_s
40
+ IPAddr.new(ipaddr)
41
+ else
42
+ IPAddr.new(arg)
43
+ end
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
+
57
+ def mlen
58
+ @_jme_mlen_ ||= _mlen_
59
+ end
60
+
61
+ def _generate_network_inc_
62
+ max_len = ipv4? ? 32 : 128
63
+ Proc.new { |n| n*(2**(max_len - mlen)) }
64
+ end
65
+ def +(i)
66
+ [IPAddr.create(to_i + i).to_s, mlen].join("/")
67
+ end
68
+ def ^(i)
69
+ @increment ||= _generate_network_inc_
70
+ [IPAddr.create(to_i + @increment.call(i)).to_s, mlen].join("/")
71
+ end
72
+ private :_generate_network_inc_
73
+
74
+ def netmask
75
+ if ipv4?
76
+ [@mask_addr].pack('N').unpack('C4').collect { |x| x.to_s}.join('.')
77
+ else
78
+ #TODO netmask ipv6
79
+ @mask_addr
80
+ end
81
+ end
82
+
83
+ end
84
+
85
+
86
+ class Object
87
+ def to_shex(*args)
88
+ self.respond_to?(:encode) ? self.encode(*args).unpack('H*')[0] : ""
89
+ end
90
+ alias to_s_hexlify to_shex
91
+ def to_shex4(*args)
92
+ self.respond_to?(:encode4) ? self.encode4(*args).unpack('H*')[0] : ""
93
+ end
94
+ def to_shex_len(len, *args)
95
+ s = to_shex(*args)
96
+ "#{s[0..len]}#{s.size>len ? '...' : ''}"
97
+ end
98
+ def to_shex4_len(len, *args)
99
+ s = to_shex4(*args)
100
+ "#{s[0..len]}#{s.size>len ? '...' : ''}"
101
+ end
102
+ end
103
+
104
+ class Array
105
+ alias_method :old_pack, :pack
106
+ def pack(*args)
107
+ s = self.old_pack(*args)
108
+ s.instance_eval { @__is_packed__ = true }
109
+ s
110
+ end
111
+ end
112
+
113
+ class String
114
+ def is_packed?
115
+ defined?(@__is_packed__) and @__is_packed__
116
+ end
117
+ def is_packed
118
+ @__is_packed__ = true
119
+ self
120
+ end
121
+ alias packed? :is_packed?
122
+
123
+ def hexlify
124
+ return self unless is_packed?
125
+ s=self.dup
126
+ ls=[""]
127
+ n=0
128
+ while s.size>0
129
+ l = s.slice!(0,16)
130
+ ls << format("0x%4.4x: %s", n,
131
+ l.unpack("n#{l.size/2}").collect { |x| format("%4.4x",x) }.join(' '))
132
+ n+=1
133
+ end
134
+ ls
135
+ end
136
+
137
+ end
138
+
139
+ class Log < Logger
140
+ private_class_method :new
141
+ @@logger = nil
142
+ def initialize(s)
143
+ super(s)
144
+ @@time=Time.now
145
+ self.datetime_format = "%M:%S"
146
+ self.level = Logger::INFO
147
+ end
148
+ def Log.time_reset
149
+ @time = Time.now
150
+ end
151
+ def Log.create(s=STDERR)
152
+ @@logger ||= new(s)
153
+ end
154
+ def Log.set_filename(s)
155
+ @@logger = new(s)
156
+ end
157
+ def Log.level=(level)
158
+ return unless (0..4) === level
159
+ @@logger.level=(level)
160
+ end
161
+ def Log.level
162
+ case @@logger.level
163
+ when Logger::INFO ; "(#{Logger::INFO }) 'INFO'"
164
+ when Logger::DEBUG ; "(#{Logger::DEBUG }) 'DEBUG'"
165
+ when Logger::WARN ; "(#{Logger::WARN }) 'WARN'"
166
+ when Logger::ERROR ; "(#{Logger::ERROR }) 'ERROR'"
167
+ when Logger::FATAL ; "(#{Logger::FATAL }) 'FATAL'"
168
+ end
169
+ end
170
+ def Log.clear
171
+ `rm #{Log.filename}`
172
+ Log.set_filename(Log.filename)
173
+ end
174
+ def Log.filename
175
+ @@logger.instance_eval { @logdev.filename }
176
+ end
177
+ def Log.start(*arg)
178
+ Log.create(*arg)
179
+ end
180
+ def Log.info(txt)
181
+ @@logger.info(txt) unless @@logger.nil?
182
+ end
183
+ def Log.fatal(txt)
184
+ @@logger.fatal(txt) unless @@logger.nil?
185
+ end
186
+ def Log.error(txt)
187
+ @@logger.error(txt) unless @@logger.nil?
188
+ end
189
+ def Log.debug(txt)
190
+ @@logger.debug(txt) unless @@logger.nil?
191
+ end
192
+ def Log.warn(txt)
193
+ @@logger.warn(txt) unless @@logger.nil?
194
+ end
195
+ def Log.<<(txt)
196
+ elapsed = Time.now - @@time
197
+ @@logger << "<< #{format "%4.6f", elapsed}: #{txt}\n" unless @@logger.nil?
198
+ end
199
+ def Log.>>(txt)
200
+ elapsed = Time.now.to_f - @@time.to_f
201
+ @@logger << ">> #{format "%4.6f", elapsed}: #{txt}\n" unless @@logger.nil?
202
+ end
203
+ end
204
+
@@ -0,0 +1,139 @@
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
+
27
+ module BGP
28
+
29
+ class Communities < Attr
30
+
31
+ class Community
32
+
33
+ def initialize(arg)
34
+ if arg.is_a?(Symbol)
35
+ case arg
36
+ when :no_export ; @value=0xFFFFFF01
37
+ when :no_advertise ; @value=0xFFFFFF02
38
+ when :no_export_sub_confed ; @value=0xFFFFFF03
39
+ when :no_peer ; @value=0xFFFFFF04
40
+ else
41
+ raise ArgumentError, "invalid argument #{val}"
42
+ end
43
+ elsif arg.is_a?(String) and arg.split(':').size==2
44
+ self.value=arg.split(':').collect { |n| n.to_i }.pack('n2').unpack('N')[0]
45
+ else
46
+ self.value=arg
47
+ end
48
+ end
49
+
50
+ def value=(val)
51
+ raise ArgumentError, "invalid argument #{val}" unless val.is_a?(Fixnum) or val.is_a?(Bignum)
52
+ @value=val
53
+ end
54
+
55
+ def to_i
56
+ @value
57
+ end
58
+
59
+ def to_s
60
+ [@value >> 16, @value & 0xffff].join(':')
61
+ end
62
+
63
+ # The community attribute values ranging from 0x0000000 through
64
+ # 0x0000FFFF and 0xFFFF0000 through 0xFFFFFFFF are hereby reserved.
65
+ def is_reserved?
66
+ (0x0000000..0x0000FFFF ) === @value or (0xFFFF0000..0xFFFFFFFF) === @value
67
+ end
68
+
69
+ def encode
70
+ [@value].pack('N')
71
+ end
72
+
73
+ end
74
+
75
+ def initialize(*args)
76
+ @flags, @type = OPTIONAL_TRANSITIVE, COMMUNITIES
77
+ if args[0].is_a?(String) and args[0].is_packed?
78
+ parse(args[0])
79
+ elsif args[0].is_a?(self.class) and args[0].respond_to?(:encode)
80
+ parse(args[0].encode, *args[1..-1])
81
+ else
82
+ add(*args)
83
+ end
84
+ end
85
+
86
+ def add(*args)
87
+ @communities ||=[]
88
+ args.flatten.each do |arg|
89
+ if arg.is_a?(String) and arg.split(' ').size>1
90
+ arg.split.each { |v| @communities << Community.new(v) }
91
+ elsif arg.is_a?(String) and arg.split(',').size>1
92
+ arg.split(',').each { |v| @communities << Community.new(v) }
93
+ elsif arg.is_a?(Community)
94
+ @communities << arg
95
+ else
96
+ @communities << Community.new(arg)
97
+ end
98
+ end
99
+ end
100
+ alias << add
101
+
102
+ def communities
103
+ @communities.collect { |comm| comm.to_s }.join(' ')
104
+ end
105
+
106
+ def to_s(method=:default)
107
+ super(communities, method)
108
+ end
109
+
110
+ def to_ary
111
+ @communities.collect { |c| c.to_i }
112
+ end
113
+
114
+ def encode
115
+ super(@communities.collect { |comm| comm.encode }.join)
116
+ end
117
+
118
+ def parse(s)
119
+ @flags, @type, len, value=super(s)
120
+ self << value.unpack("N#{len/4}")
121
+ end
122
+
123
+ def sort
124
+ Communities.new(to_ary.sort)
125
+ end
126
+
127
+ def sort!
128
+ @communities = @communities.sort_by { |c| c.to_i }
129
+ self
130
+ end
131
+
132
+ def <=>(other)
133
+ self.sort.to_shex <=> other.sort.to_shex
134
+ end
135
+
136
+ end
137
+
138
+ end
139
+ load "../test/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0