Class: Xlat::Rfc7915
- Inherits:
-
Object
- Object
- Xlat::Rfc7915
- Extended by:
- Common
- Includes:
- Common
- Defined in:
- lib/xlat/rfc7915.rb
Overview
RFC 7915 based stateless IPv4/IPv6 translator (SIIT). Intentionally not thread-safe.
datatracker.ietf.org/doc/html/rfc7915 www.rfc-editor.org/info/rfc7915
Defined Under Namespace
Classes: BufferInUse
Constant Summary collapse
- MAX_FRAGMENT_ID =
0xffffffff- ICMPV6V4_TYPE_MAP =
- ICMPV6_POINTER_MAP =
- ICMPV4V6_TYPE_MAP =
- ICMPV4_POINTER_MAP =
Instance Attribute Summary collapse
-
#next_fragment_identifier ⇒ Object
Returns the value of attribute next_fragment_identifier.
Instance Method Summary collapse
-
#initialize(source_address_translator:, destination_address_translator:, for_icmp: false) ⇒ Rfc7915
constructor
A new instance of Rfc7915.
- #return_buffer_ownership ⇒ Object
-
#translate_to_ipv4(ipv6_packet, max_length) ⇒ Object
Returns array of bytestrings to send as a IPv4 packet.
-
#translate_to_ipv6(ipv4_packet, max_length) ⇒ Object
Returns array of bytestrings to send as a IPv6 packet.
Methods included from Common
Constructor Details
#initialize(source_address_translator:, destination_address_translator:, for_icmp: false) ⇒ Rfc7915
Returns a new instance of Rfc7915.
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/xlat/rfc7915.rb', line 20 def initialize(source_address_translator:, destination_address_translator:, for_icmp: false) @source_address_translator = source_address_translator @destination_address_translator = destination_address_translator # checksum_neutrality = @source_address_translator.checksum_neutral? && @destination_address_translator.checksum_neutral? @next_fragment_identifier = 0 @ipv4_new_header_buffer = IO::Buffer.new(20) @ipv6_new_header_buffer = IO::Buffer.new(40) @output = [] @new_header_buffer_in_use = false return_buffer_ownership unless for_icmp @inner_icmp = self.class.new(source_address_translator: destination_address_translator, destination_address_translator: source_address_translator, for_icmp: true) @inner_packet = Protocols::Ip.new(icmp_payload: true) end end |
Instance Attribute Details
#next_fragment_identifier ⇒ Object
Returns the value of attribute next_fragment_identifier.
40 41 42 |
# File 'lib/xlat/rfc7915.rb', line 40 def next_fragment_identifier @next_fragment_identifier end |
Instance Method Details
#return_buffer_ownership ⇒ Object
195 196 197 198 199 200 201 202 203 204 |
# File 'lib/xlat/rfc7915.rb', line 195 def return_buffer_ownership @new_header_buffer_in_use = false @ipv4_new_header_buffer.clear @ipv6_new_header_buffer.clear @output.clear if @inner_icmp @inner_icmp.return_buffer_ownership end nil end |
#translate_to_ipv4(ipv6_packet, max_length) ⇒ Object
Returns array of bytestrings to send as a IPv4 packet. May update original packet content.
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/xlat/rfc7915.rb', line 48 def translate_to_ipv4(ipv6_packet, max_length) raise BufferInUse if @new_header_buffer_in_use raise ArgumentError unless ipv6_packet.version.to_i == 6 icmp_payload = @inner_icmp.nil? @new_header_buffer_in_use = true new_header_buffer = @ipv4_new_header_buffer ipv6_bytes = ipv6_packet.bytes ipv6_bytes_offset = ipv6_packet.bytes_offset cs_delta = 0 # delta for incremental update of upper-layer checksum fields # Version = 4, IHL = 5 new_header_buffer.set_value(:U8, 0, (4 << 4) + 5) # FIXME: ToS ignored # Total Length = copy from IPv6; may be updated in later step ipv4_length = ipv6_packet.l4_length + 20 # not considering as a checksum delta because upper layer packet length doesn't take this into account; cs_delta += ipv6_length - ipv4_length new_header_buffer.set_value(:U16, 2, ipv4_length) # Identification = generate new_header_buffer.set_value(:U16, 4, make_fragment_id()) # TTL = copy from IPv6 new_header_buffer.set_value(:U8, 8, ipv6_bytes.get_value(:U8, ipv6_bytes_offset + 7)) # Protocol = copy from IPv6; may be updated in later step for ICMPv6=>4 conversion new_header_buffer.set_value(:U8, 9, ipv6_packet.proto) # Source and Destination address cs_delta_a = @source_address_translator.translate_address_to_ipv4(ipv6_bytes.slice(ipv6_bytes_offset + 8,16), new_header_buffer, 12) or return return_buffer_ownership() cs_delta_b = @destination_address_translator.translate_address_to_ipv4(ipv6_bytes.slice(ipv6_bytes_offset + 24,16), new_header_buffer, 16) or return return_buffer_ownership() cs_delta += cs_delta_a + cs_delta_b # TODO: DF bit # TODO: discard if expired source route option is present if ipv6_packet.proto == 58 # icmpv6 icmp_result, icmp_output = translate_icmpv6_to_icmpv4(ipv6_packet, new_header_buffer, max_length - 20) return return_buffer_ownership() unless icmp_result cs_delta += icmp_result end unless icmp_output l4_length = ipv6_packet.l4_bytes_length unless 20 + l4_length <= max_length if icmp_payload l4_length = max_length - 20 else # FIXME: this should not happen return return_buffer_ownership() end end end #p ipv6_bytes.chars.map { _1.ord.to_s(16).rjust(2,'0') }.join(' ') #p new_header_buffer.chars.map { _1.ord.to_s(16).rjust(2,'0') }.join(' ') ipv4_packet = ipv6_packet.convert_version!(Protocols::Ip::Ipv4, new_header_buffer, cs_delta) ipv4_packet.apply_changes # Recompute checksum (this must be performed after Ip#apply_changes as it updates ipv4 checksum field along with l4 checksum field using delta, # while new_header_buffer has no prior checksum value) new_header_buffer.set_value(:U16, 10, 0) cksum = Protocols::Ip.checksum(new_header_buffer) new_header_buffer.set_value(:U16, 10, cksum) # TODO: Section 5.4. Generation of ICMPv6 Error Messages # TODO: Section 5.1.1. IPv6 Fragment Processing @output << new_header_buffer if icmp_output @output.concat(icmp_output) else @output << ipv4_packet.l4_bytes.slice(ipv4_packet.l4_bytes_offset, l4_length) end @output end |
#translate_to_ipv6(ipv4_packet, max_length) ⇒ Object
Returns array of bytestrings to send as a IPv6 packet. May update original packet content.
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/xlat/rfc7915.rb', line 129 def translate_to_ipv6(ipv4_packet, max_length) raise BufferInUse if @new_header_buffer_in_use raise ArgumentError unless ipv4_packet.version.to_i == 4 icmp_payload = @inner_icmp.nil? # TODO: support fragment extension and esp # TODO: ignore extension @new_header_buffer_in_use = true ipv4_bytes = ipv4_packet.bytes ipv4_bytes_offset = ipv4_packet.bytes_offset new_header_buffer = @ipv6_new_header_buffer cs_delta = 0 # delta for incremental update of upper-layer checksum fields # Version = 6, traffic class = 0 new_header_buffer.set_value(:U8, 0, 6 << 4) # Flow label = 0 # IPv6 Length = IPv4 total length - IPv4 header length; may be updated in later step ipv6_length = ipv4_packet.l4_length # not considering as a checksum delta because upper layer packet length doesn't take this into account; cs_delta += ipv4_length - ipv6_length new_header_buffer.set_value(:U16, 4, ipv6_length) # Next Header = copy from IPv4; may be updated in later step for ICMPv6=>4 conversion new_header_buffer.set_value(:U8, 6, ipv4_packet.proto) # Hop limit = copy from IPv4 new_header_buffer.set_value(:U8, 7, ipv4_bytes.get_value(:U8, ipv4_bytes_offset + 8)) # Source and Destination address cs_delta_a = @destination_address_translator.translate_address_to_ipv6(ipv4_bytes.slice(ipv4_bytes_offset + 12,4), new_header_buffer, 8) or return return_buffer_ownership() cs_delta_b = @source_address_translator.translate_address_to_ipv6(ipv4_bytes.slice(ipv4_bytes_offset + 16,4), new_header_buffer, 24) or return return_buffer_ownership() cs_delta += cs_delta_a + cs_delta_b if ipv4_packet.proto == 1 # icmpv4 icmp_result, icmp_output = translate_icmpv4_to_icmpv6(ipv4_packet, new_header_buffer, max_length - 40) return return_buffer_ownership() unless icmp_result cs_delta += icmp_result end unless icmp_output l4_length = ipv4_packet.l4_bytes_length unless 40 + l4_length <= max_length if icmp_payload l4_length = max_length - 40 else # FIXME: generate "fragmentation needed" if DF=1 return return_buffer_ownership() end end end # TODO: generate udp checksum option (section 4.5.) ipv6_packet = ipv4_packet.convert_version!(Protocols::Ip::Ipv6, new_header_buffer, cs_delta) ipv6_packet.apply_changes # TODO: Section 4.4. Generation of ICMPv4 Error Message @output << new_header_buffer if icmp_output @output.concat(icmp_output) else @output << ipv6_packet.l4_bytes.slice(ipv6_packet.l4_bytes_offset, l4_length) end @output end |