bgp4r 0.0.11 → 0.0.12

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 (44) hide show
  1. data/bgp/common.rb +14 -13
  2. data/bgp/iana.rb +26 -1
  3. data/bgp/messages/capability.rb +3 -2
  4. data/bgp/messages/message.rb +3 -2
  5. data/bgp/messages/open.rb +2 -1
  6. data/bgp/messages/update.rb +80 -30
  7. data/bgp/neighbor/add_path_cap.rb +125 -0
  8. data/bgp/{neighbor.rb → neighbor/neighbor.rb} +64 -68
  9. data/bgp/nlris/nlri.rb +289 -15
  10. data/bgp/nlris/prefix.rb +3 -2
  11. data/bgp/nlris/vpn.rb +1 -8
  12. data/bgp/optional_parameters/add_path.rb +160 -0
  13. data/bgp/optional_parameters/capabilities.rb +1 -0
  14. data/bgp/optional_parameters/capability.rb +6 -0
  15. data/bgp/optional_parameters/graceful_restart.rb +6 -6
  16. data/bgp/optional_parameters/optional_parameter.rb +1 -0
  17. data/bgp/path_attributes/as_path.rb +1 -1
  18. data/bgp/path_attributes/attribute.rb +12 -5
  19. data/bgp/path_attributes/mp_reach.rb +142 -96
  20. data/bgp/path_attributes/mp_unreach.rb +73 -20
  21. data/bgp/path_attributes/path_attribute.rb +28 -5
  22. data/bgp4r.gemspec +21 -6
  23. data/bgp4r.rb +1 -1
  24. data/examples/unit-testing/malformed_update.rb +2 -1
  25. data/examples/unit-testing/test.rb +82 -0
  26. data/examples/unit-testing/test1.rb +82 -0
  27. data/examples/unit-testing/test2.rb +44 -0
  28. data/test/common_test.rb +7 -0
  29. data/test/helpers/server.rb +20 -0
  30. data/test/iana_test.rb +43 -0
  31. data/test/messages/open_test.rb +7 -2
  32. data/test/messages/update_test.rb +133 -36
  33. data/test/neighbor/add_path_cap_test.rb +54 -0
  34. data/test/neighbor/neighbor_test.rb +161 -0
  35. data/test/nlris/ext_nlri_test.rb +25 -60
  36. data/test/nlris/nlri_test.rb +93 -115
  37. data/test/optional_parameters/add_path_test.rb +53 -0
  38. data/test/optional_parameters/capability_test.rb +10 -0
  39. data/test/optional_parameters/graceful_restart_test.rb +1 -0
  40. data/test/path_attributes/mp_reach_test.rb +206 -8
  41. data/test/path_attributes/mp_unreach_test.rb +113 -5
  42. data/test/path_attributes/path_attribute_test.rb +34 -2
  43. metadata +20 -7
  44. data/test/neighbor_test.rb +0 -62
@@ -0,0 +1,160 @@
1
+ #--
2
+ # Copyright 2010 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/optional_parameters/capability'
24
+
25
+ module BGP::OPT_PARM::CAP
26
+
27
+ #
28
+ # Belongs to Neighbor this is a session attribute....
29
+ # def include_path_id?(speeker, peer, sr, afi, safi)
30
+ # # when sending afi, safi, speaker agrees to send and peer to receive
31
+ # # when receiving afi, safi, speaker agrees to recv and peer agress to send
32
+ # case sr
33
+ # when :recv
34
+ # speeker.has?(:recv, afi, safi) && peer.has?(:send, afi, safi)
35
+ # when :send
36
+ # speeker.has?(:send, afi, safi) && peer.has?(:recv, afi, safi)
37
+ # end
38
+ # end
39
+
40
+ class Add_path < BGP::OPT_PARM::Capability
41
+
42
+ class << self
43
+ def new_array(arg)
44
+ o = new
45
+ arg.each { |t| o.add(*t) }
46
+ o
47
+ end
48
+ end
49
+
50
+ def initialize(*args)
51
+ @af={}
52
+ if args.size>1
53
+ super(OPT_PARM::CAP_ADD_PATH)
54
+ add(*args)
55
+ elsif args.size==1 and args[0].is_a?(String)
56
+ parse(*args)
57
+ elsif args.empty?
58
+ super(OPT_PARM::CAP_ADD_PATH)
59
+ else
60
+ raise
61
+ end
62
+ end
63
+
64
+ def add(sr, afi,safi)
65
+ @af[[_afi(afi), _safi(safi)]] = _send_recv(sr)
66
+ end
67
+
68
+ def parse(s)
69
+ families = super(s)
70
+ while families.size>0
71
+ afi, safi, sr = families.slice!(0,4).unpack('nCC')
72
+ @af[[afi,safi]]=sr
73
+ end
74
+ end
75
+
76
+ # 0001 01 01
77
+ # 0002 80 02
78
+ # 0002 01 03
79
+
80
+ def send? afi, safi
81
+ has? :send, afi, safi
82
+ end
83
+
84
+ def recv? afi, safi
85
+ has? :recv, afi, safi
86
+ end
87
+
88
+ def has?(sr, afi, safi)
89
+ case sr
90
+ when :recv
91
+ @af.has_key?([afi,safi]) && (2..3) === @af[[afi,safi]]
92
+ when :send
93
+ @af.has_key?([afi,safi]) && (@af[[afi,safi]] == 1 or @af[[afi,safi]] ==3)
94
+ end
95
+ end
96
+
97
+ def encode
98
+ s = []
99
+ s << @af.to_a.sort.collect { |e| e.flatten.pack('nCC') }
100
+ super s.join
101
+ end
102
+
103
+ def to_s
104
+ s = []
105
+ s << "\n Add-path Extension (#{CAP_ADD_PATH}), length: #{encode.size}"
106
+ s = s.join("\n ")
107
+ super + (s + (['']+@af.to_a.collect { |e| address_family_to_s(*e)}).join("\n "))
108
+ end
109
+
110
+ private
111
+
112
+ def address_family_to_s(af, sr)
113
+ afi, safi = af
114
+ "AFI #{IANA.afi(afi)} (#{afi}), SAFI #{IANA.safi(safi)} (#{safi}), #{send_recv_to_s(sr)}"
115
+ end
116
+
117
+ def _send_recv(val)
118
+ case val
119
+ when :send, 1 ; 1
120
+ when :recv, :receive, 2 ; 2
121
+ when :send_and_recv, :send_recv, :send_and_receive, 3 ; 3
122
+ else
123
+ val
124
+ end
125
+ end
126
+
127
+ def send_recv_to_s(val)
128
+ case val
129
+ when 1 ; 'SEND (1)'
130
+ when 2 ; 'RECV (2)'
131
+ when 3 ; 'SEND_AND_RECV (3)'
132
+ else
133
+ 'bogus'
134
+ end
135
+ end
136
+
137
+ def _afi(val)
138
+ if val.is_a?(Fixnum)
139
+ val
140
+ elsif val == :ipv4
141
+ 1
142
+ elsif val == :ipv6
143
+ 2
144
+ end
145
+ end
146
+
147
+ def _safi(val)
148
+ if val.is_a?(Fixnum)
149
+ val
150
+ elsif val == :unicast
151
+ 1
152
+ elsif val == :multicast
153
+ 2
154
+ end
155
+ end
156
+
157
+ end
158
+ end
159
+
160
+ load "../../test/optional_parameters/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0
@@ -7,6 +7,7 @@ require 'bgp/optional_parameters/optional_parameter'
7
7
  require 'bgp/optional_parameters/orf'
8
8
  require 'bgp/optional_parameters/route_refresh'
9
9
  require 'bgp/optional_parameters/as4'
10
+ require 'bgp/optional_parameters/add_path'
10
11
 
11
12
  module BGP::OPT_PARM
12
13
  module DYN_CAP
@@ -114,6 +114,8 @@ module BGP::OPT_PARM
114
114
  CAP::Orf.new(s)
115
115
  when CAP_GR
116
116
  CAP::Graceful_restart.new(s)
117
+ when CAP_ADD_PATH
118
+ CAP::Add_path.new(s)
117
119
  when CAP_DYNAMIC
118
120
  CAP::Dynamic.new(s)
119
121
  else
@@ -134,8 +136,12 @@ module BGP::OPT_PARM
134
136
  DYN_CAP::Route_refresh.new(code)
135
137
  when CAP_ORF,CAP_ORF_CISCO
136
138
  DYN_CAP::Orf.new(s)
139
+ when CAP_ADD_PATH
140
+ DYN_CAP::Add_path.new(s)
137
141
  when CAP_GR
138
142
  DYN_CAP::Graceful_restart.new(s)
143
+ when CAP_ADD_PATH
144
+ CAP::Add_path.new(s)
139
145
  else
140
146
  Capability::Unknown.new(s)
141
147
  end
@@ -29,7 +29,7 @@ class Graceful_restart < BGP::OPT_PARM::Capability
29
29
  def initialize(*args)
30
30
  if args.size>1
31
31
  @restart_state, @restart_time = args
32
- @address_families = []
32
+ @tuples = []
33
33
  super(OPT_PARM::CAP_GR)
34
34
  else
35
35
  parse(*args)
@@ -37,16 +37,16 @@ class Graceful_restart < BGP::OPT_PARM::Capability
37
37
  end
38
38
 
39
39
  def add(afi,safi,flags)
40
- @address_families << [_afi(afi), _safi(safi), flags]
40
+ @tuples << [_afi(afi), _safi(safi), flags]
41
41
  end
42
42
 
43
43
  def parse(s)
44
- @address_families = []
44
+ @tuples = []
45
45
  o1, families = super(s).unpack('na*')
46
46
  @restart_state = o1 >> 12
47
47
  @restart_time = o1 & 0xfff
48
48
  while families.size>0
49
- @address_families << families.slice!(0,4).unpack('nCC')
49
+ @tuples << families.slice!(0,4).unpack('nCC')
50
50
  end
51
51
  end
52
52
 
@@ -54,7 +54,7 @@ class Graceful_restart < BGP::OPT_PARM::Capability
54
54
  def encode
55
55
  s = []
56
56
  s << [(@restart_state << 12) + @restart_time].pack('n')
57
- s << @address_families.collect { |af| af.pack('nCC') }
57
+ s << @tuples.collect { |af| af.pack('nCC') }
58
58
  super s.join
59
59
  end
60
60
  def to_s
@@ -62,7 +62,7 @@ class Graceful_restart < BGP::OPT_PARM::Capability
62
62
  s << "\n Graceful Restart Extension (#{CAP_GR}), length: 4"
63
63
  s << " Restart Flags: #{restart_flag}, Restart Time #{@restart_time}s"
64
64
  s = s.join("\n ")
65
- super + (s + (['']+@address_families.collect { |af| address_family(*af)}).join("\n "))
65
+ super + (s + (['']+@tuples.collect { |af| address_family(*af)}).join("\n "))
66
66
  end
67
67
 
68
68
  private
@@ -36,6 +36,7 @@ module OPT_PARM
36
36
  CAP_GR = 64
37
37
  CAP_AS4 = 65
38
38
  CAP_DYNAMIC = 67
39
+ CAP_ADD_PATH = 69
39
40
  CAP_ROUTE_REFRESH_CISCO = 128
40
41
  CAP_ORF_CISCO = 130
41
42
 
@@ -156,7 +156,7 @@ module BGP
156
156
  attr_accessor :as4byte
157
157
 
158
158
  def initialize(*args)
159
-
159
+
160
160
  @flags, @type, @segments, @as4byte = WELL_KNOWN_MANDATORY, AS_PATH, [], false
161
161
 
162
162
  if args[0].is_a?(String) and args[0].is_packed?
@@ -61,16 +61,23 @@ module BGP
61
61
  OPTIONAL_TRANSITIVE = OPTIONAL | TRANSITIVE
62
62
  OPTIONAL_NON_TRANSITIVE = OPTIONAL
63
63
 
64
- def encode(value='',value_fmt=nil)
65
- len, len_fmt = value.size, 'C'
64
+ def len_fmt(len)
66
65
  if len>255
67
66
  @flags |= EXTENDED_LENGTH
68
- len_fmt='n'
67
+ @_len_fmt='n'
68
+ else
69
+ @_len_fmt = 'C'
70
+ @flags &= ~EXTENDED_LENGTH
69
71
  end
72
+ end
73
+
74
+ def encode(value='',value_fmt=nil)
75
+ len = value.size
76
+ len_fmt(len)
70
77
  if value_fmt
71
- [@flags<<4, @type, len, *value].pack("CC#{len_fmt}#{value_fmt}")
78
+ [@flags<<4, @type, len, *value].pack("CC#{@_len_fmt}#{value_fmt}")
72
79
  else
73
- ([@flags<<4, @type, len].pack("CC#{len_fmt}") + value).is_packed
80
+ ([@flags<<4, @type, len].pack("CC#{@_len_fmt}") + value).is_packed
74
81
  end
75
82
  end
76
83
 
@@ -1,4 +1,4 @@
1
- #--
1
+ #--
2
2
  # Copyright 2008, 2009 Jean-Michel Esnault.
3
3
  # All rights reserved.
4
4
  # See LICENSE.txt for permissions.
@@ -24,119 +24,165 @@ require 'bgp/path_attributes/attribute'
24
24
  require 'bgp/nlris/nlris'
25
25
 
26
26
  module BGP
27
-
28
- class Mp_reach < Attr
29
-
30
- attr_reader :safi, :nlris
31
-
32
- def initialize(*args)
33
- @safi, @nexthops, @nlris= 1, [], [] # default is ipv4/unicast
34
- @flags, @type = OPTIONAL, MP_REACH
35
- if args[0].is_a?(String) and args[0].is_packed?
36
- parse(args[0])
37
- elsif args[0].is_a?(self.class)
38
- parse(args[0].encode, *args[1..-1])
39
- elsif args[0].is_a?(Hash) and args.size==1
40
- set(*args)
41
- else
42
- raise ArgumentError, "invalid argument"
43
- end
27
+
28
+ class Mp_reach < Attr
29
+
30
+ attr_reader :safi, :nlris, :path_id
31
+
32
+ def initialize(*args)
33
+ @safi, @nexthops, @nlris, @path_id= 1, [], [], nil # default is ipv4/unicast
34
+ @flags, @type = OPTIONAL, MP_REACH
35
+ if args[0].is_a?(String) and args[0].is_packed?
36
+ parse(*args)
37
+ elsif args[0].is_a?(self.class)
38
+ s = args.shift.encode
39
+ parse(s, *args)
40
+ elsif args[0].is_a?(Hash) and args.size==1
41
+ set(*args)
42
+ else
43
+ raise ArgumentError, "invalid argument"
44
44
  end
45
-
46
- def afi
47
- @afi ||= @nexthops[0].afi
45
+ end
46
+
47
+ def afi
48
+ @afi ||= @nexthops[0].afi
49
+ end
50
+
51
+ def set(h)
52
+ @safi = h[:safi]
53
+ @path_id = path_id = h[:path_id]
54
+ case @safi
55
+ when 1,2,4 ; @nexthops = [h[:nexthop]].flatten.collect { |nh| Prefix.new(nh) }
56
+ when 128,129 ; @nexthops = [h[:nexthop]].flatten.collect { |nh| Vpn.new(nh) }
57
+ else
48
58
  end
49
-
50
- def set(h)
51
- @safi = h[:safi]
52
- case @safi
53
- when 1,2,4 ; @nexthops = [h[:nexthop]].flatten.collect { |nh| Prefix.new(nh) }
54
- when 128,129 ; @nexthops = [h[:nexthop]].flatten.collect { |nh| Vpn.new(nh) }
55
- else
59
+ case @safi
60
+ when 1
61
+ @nlris = [h[:nlris]].flatten.collect do |n|
62
+ case n
63
+ when String
64
+ nlri = Inet_unicast.new(n)
65
+ path_id ? Ext_Nlri.new(path_id, nlri) : nlri
66
+ when Hash
67
+ path_id = n[:path_id] if n[:path_id]
68
+ nlri = Inet_unicast.new(n[:prefix])
69
+ path_id ? Ext_Nlri.new(path_id, nlri) : nlri
70
+ else
71
+ raise ArgumentError, "Invalid: #{n.inspect}"
72
+ end
56
73
  end
57
- case @safi
58
- when 1,2
59
- @nlris = [h[:prefix]].flatten.collect { |n| Prefix.new(n) }
60
- when 4
61
- @nlris = [h[:nlri]].flatten.collect { |n|
62
- prefix = n[:prefix].is_a?(String) ? Prefix.new(n[:prefix]) : n[:prefix]
63
- Labeled.new(prefix, *n[:label])
64
- }
65
- when 128,129 ; @nlris = [h[:nlri]].flatten.collect { |n|
74
+ when 2
75
+ @nlris = [h[:nlris]].flatten.collect do |n|
76
+ case n
77
+ when String
78
+ nlri = Inet_multicast.new(n)
79
+ path_id ? Ext_Nlri.new(path_id, nlri) : nlri
80
+ when Hash
81
+ path_id = n[:path_id] if n[:path_id]
82
+ nlri = Inet_multicast.new(n[:prefix])
83
+ path_id ? Ext_Nlri.new(path_id, nlri) : nlri
84
+ else
85
+ raise ArgumentError, "Invalid: #{n.inspect}"
86
+ end
87
+ end
88
+ when 4
89
+ @nlris = [h[:nlris]].flatten.collect do |n|
90
+ path_id = n[:path_id] || @path_id
91
+ prefix = n[:prefix].is_a?(String) ? Prefix.new(n[:prefix]) : n[:prefix]
92
+ nlri = Labeled.new(prefix, *n[:label])
93
+ path_id ? Ext_Nlri.new(path_id, nlri) : nlri
94
+ end
95
+ when 128,129
96
+ @nlris = [h[:nlris]].flatten.collect do |n|
97
+ path_id = n[:path_id] || @path_id
66
98
  prefix = n[:prefix].is_a?(Prefix) ? n[:prefix] : Prefix.new(n[:prefix])
67
99
  rd = n[:rd].is_a?(Rd) ? n[:rd] : Rd.new(*n[:rd])
68
- Labeled.new(Vpn.new(prefix,rd), *n[:label]) }
69
- else
100
+ nlri = Labeled.new(Vpn.new(prefix,rd), *n[:label])
101
+ path_id ? Ext_Nlri.new(path_id, nlri) : nlri
70
102
  end
103
+ else
71
104
  end
105
+ end
106
+
107
+ def nexthops
108
+ @nexthops.collect { |nh| nh.nexthop }.join(", ")
109
+ end
110
+
111
+ def mp_reach
112
+ "\n AFI #{IANA.afi(afi)} (#{afi}), SAFI #{IANA.safi(safi)} (#{safi})" +
113
+ "\n nexthop: " + nexthops +
114
+ (['']+ @nlris.collect { |nlri| nlri.to_s }).join("\n ")
115
+ end
116
+
117
+ def to_s(method=:default)
118
+ super(mp_reach, method)
119
+ end
120
+
121
+ def to_hash
122
+ end
123
+
124
+ def parse(s,arg=false)
72
125
 
73
- def nexthops
74
- @nexthops.collect { |nh| nh.nexthop }.join(", ")
75
- end
76
-
77
- def mp_reach
78
- "\n AFI #{IANA.afi(afi)} (#{afi}), SAFI #{IANA.safi(safi)} (#{safi})" +
79
- "\n nexthop: " + nexthops +
80
- (['']+ @nlris.collect { |nlri| nlri.to_s }).join("\n ")
81
- end
82
-
83
- def to_s(method=:default)
84
- super(mp_reach, method)
85
- end
126
+ @flags, @type, len, value = super(s)
127
+ @afi, @safi, nh_len = value.slice!(0,4).unpack('nCC')
128
+ parse_next_hops value.slice!(0,nh_len).is_packed
129
+ value.slice!(0,1)
86
130
 
87
- def to_hash
131
+ if arg.respond_to?(:path_id?)
132
+ path_id_flag = arg.path_id? :recv, @afi, @safi
133
+ else
134
+ path_id_flag = arg
88
135
  end
89
136
 
90
-
91
- def parse(s)
92
- @flags, @type, len, value = super(s)
93
- @afi, @safi, nh_len = value.slice!(0,4).unpack('nCC')
94
- parse_next_hops value.slice!(0,nh_len).is_packed
95
- value.slice!(0,1)
96
- while value.size>0
97
- blen = value.slice(0,1).unpack('C')[0]
98
- @nlris << Nlri.factory(value.slice!(0,(blen+7)/8+1), @afi, @safi)
137
+ while value.size>0
138
+ path_id = value.slice!(0,4).unpack('N')[0] if path_id_flag
139
+ blen = value.slice(0,1).unpack('C')[0]
140
+ nlri = Nlri.factory(value.slice!(0,(blen+7)/8+1), @afi, @safi)
141
+ if path_id_flag
142
+ @nlris << Ext_Nlri.new(path_id, nlri)
143
+ else
144
+ @nlris << nlri
99
145
  end
100
- raise RuntimeError, "leftover afer parsing: #{value.unpack('H*')}" if value.size>0
101
-
102
146
  end
103
-
104
- def parse_next_hops(s)
105
- while s.size>0
106
- case @safi
107
- when 1,2,4
108
- case @afi
109
- when 1
110
- @nexthops << Prefix.new([32,s.slice!(0,4)].pack('Ca*'),1)
111
- when 2
112
- @nexthops << Prefix.new([128,s.slice!(0,16)].pack('Ca*'),2)
113
- end
114
- when 128,129
115
- @nexthops << Vpn.new([64+32,s.slice!(0,12)].pack('Ca*'))
116
- else
117
- raise RuntimeError, "cannot parse nexthop for safi #{@safi}"
147
+ raise RuntimeError, "leftover afer parsing: #{value.unpack('H*')}" if value.size>0
148
+ end
149
+
150
+ def parse_next_hops(s)
151
+ while s.size>0
152
+ case @safi
153
+ when 1,2,4
154
+ case @afi
155
+ when 1
156
+ @nexthops << Prefix.new([32,s.slice!(0,4)].pack('Ca*'),1)
157
+ when 2
158
+ @nexthops << Prefix.new([128,s.slice!(0,16)].pack('Ca*'),2)
118
159
  end
160
+ when 128,129
161
+ @nexthops << Vpn.new([64+32,s.slice!(0,12)].pack('Ca*'))
162
+ else
163
+ raise RuntimeError, "cannot parse nexthop for safi #{@safi}"
119
164
  end
120
165
  end
121
-
122
- def encode(what=:mp_reach)
123
- case what
124
- when :mp_reach
125
- nexthops = @nexthops.collect { |nh| nh.encode(false) }.join
126
- super([afi, @safi, nexthops.size, nexthops, 0, @nlris.collect { |n| n.encode }.join].pack('nCCa*Ca*'))
127
- when :mp_unreach
128
- super([afi, @safi, @nlris.collect { |n| n.encode }.join].pack('nCa*'))
129
- end
130
- end
131
-
132
- def new_unreach
133
- s = encode(:mp_unreach)
134
- s[1]= [MP_UNREACH].pack('C')
135
- Mp_unreach.new(s)
166
+ end
167
+
168
+ def encode(what=:mp_reach)
169
+ case what
170
+ when :mp_reach
171
+ nexthops = @nexthops.collect { |nh| nh.encode(false) }.join
172
+ super([afi, @safi, nexthops.size, nexthops, 0, @nlris.collect { |n| n.encode }.join].pack('nCCa*Ca*'))
173
+ when :mp_unreach
174
+ super([afi, @safi, @nlris.collect { |n| n.encode }.join].pack('nCa*'))
136
175
  end
137
-
138
176
  end
139
-
177
+
178
+ def new_unreach
179
+ s = encode(:mp_unreach)
180
+ s[1]= [MP_UNREACH].pack('C')
181
+ Mp_unreach.new(s)
182
+ end
183
+
184
+ end
185
+
140
186
  end
141
187
 
142
188
  load "../../test/path_attributes/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0