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