xlat 0.1.0.alpha1
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.
- checksums.yaml +7 -0
- data/.dockerignore +7 -0
- data/.rspec +3 -0
- data/Cargo.lock +348 -0
- data/Cargo.toml +7 -0
- data/Dockerfile +49 -0
- data/LICENSE.txt +21 -0
- data/README.md +100 -0
- data/Rakefile +82 -0
- data/benchmark/run.rb +38 -0
- data/benchmark/tcp.cfg +23 -0
- data/clab/.gitignore +5 -0
- data/clab/464xlat-ce-pd.clab.yml +138 -0
- data/clab/build.sh +6 -0
- data/exe/xlat-siit +91 -0
- data/ext/xlat/io_buffer_ext/Cargo.toml +11 -0
- data/ext/xlat/io_buffer_ext/extconf.rb +4 -0
- data/ext/xlat/io_buffer_ext/src/gvl.rs +49 -0
- data/ext/xlat/io_buffer_ext/src/io_buffer.rs +67 -0
- data/ext/xlat/io_buffer_ext/src/lib.rs +83 -0
- data/fuzz/corpus/010507e2c9f3b5132cbf036c7197f155cd85cca8 +0 -0
- data/fuzz/corpus/019a67cbfe27ed6606fe9d9cbee1ba16eb160667 +0 -0
- data/fuzz/corpus/021f01b7a6d4ba3e8e45a733c453d6c84f8c4c38 +0 -0
- data/fuzz/corpus/02db19829d1a4cbe18127e9ab1f3792e0d7fd5f7 +0 -0
- data/fuzz/corpus/059b719daf320e809db75d37ecf7823ba33ca7aa +0 -0
- data/fuzz/corpus/080ce49cbdb1983f8b9af09d511c5ec5a6bb14ed +0 -0
- data/fuzz/corpus/080d7d8674e7d60ce13b48995567a4ad21a3d652 +0 -0
- data/fuzz/corpus/08d488926f31661a1b25f60a723ae1931c762255 +0 -0
- data/fuzz/corpus/09ee56fb3259c88ad2cb94951313d792f36da221 +0 -0
- data/fuzz/corpus/0bffe4074467b5c4ba2bd5aa33839c5a1b8c1d63 +0 -0
- data/fuzz/corpus/0dac63180387255727c844898164ce20ff08fded +0 -0
- data/fuzz/corpus/0dc6f6c09c5b7bb4da8d496ac924e7e0d4aa37ad +0 -0
- data/fuzz/corpus/0e3e8e947fd9bfed02f927d1a2651e484d231fc0 +0 -0
- data/fuzz/corpus/0e5fa83f0ef8ecbf774ac96b8bdeda31395a284d +0 -0
- data/fuzz/corpus/12f93fa0b09c44d6c61ccde2731b5554bb180144 +0 -0
- data/fuzz/corpus/1659c552b9be677f2c30210430a97c3628f615be +0 -0
- data/fuzz/corpus/183aeabfe58d5ccfd5f2ed84e2f5ee4931f14eb7 +0 -0
- data/fuzz/corpus/1b9293272fbacbfff7aff4fd951dd7beb6aed3bf +0 -0
- data/fuzz/corpus/1c30abedb2d0943ef79b0620f7b3a7e8cdebc67f +0 -0
- data/fuzz/corpus/1c479cc892198d0cc389cc681206393c8ae4cbb6 +0 -0
- data/fuzz/corpus/1ca9480af5da2221fc320bb281d7c4ff73d5f33c +0 -0
- data/fuzz/corpus/1d6087f18146fac3b663bcc635fb470cb651884b +0 -0
- data/fuzz/corpus/258d23cbc2c8081782f0dd539270848e275a8e15 +0 -0
- data/fuzz/corpus/27512fa5c0ac5f4d8393ab442b50af3e83047c01 +0 -0
- data/fuzz/corpus/29933024f32228a8b65419d63dcbb56124db0df7 +0 -0
- data/fuzz/corpus/2b19339a2f24978c8413912a8003ae8ba8885684 +0 -0
- data/fuzz/corpus/2f8d2861a271e3ee2f658b01ef6ba854d8837aff +0 -0
- data/fuzz/corpus/2fed10d20fc4ed25da9cebd41209886304b39465 +0 -0
- data/fuzz/corpus/30fd38c3af4877eda7b1ca2dee1a60c4fe0f757c +0 -0
- data/fuzz/corpus/3137ba78351b6757255261a4f364e44693c3c85f +0 -0
- data/fuzz/corpus/34e68133b209c490f960f9682d33d41aab99e60f +0 -0
- data/fuzz/corpus/359870740db1c245d6dfdad1f119c7b17c3f25fc +0 -0
- data/fuzz/corpus/37b1bb01b0544125df396b925e2c4242a90d1c52 +0 -0
- data/fuzz/corpus/387cab0f8ac36f0f5496f6b0aed7d6c7ca3f2dbf +0 -0
- data/fuzz/corpus/3c4e48335e11c3bd066bbb43db4bf82249668869 +0 -0
- data/fuzz/corpus/3ec111be62099aaa9b960f4b69c65a3b12962970 +0 -0
- data/fuzz/corpus/3fb2f3f01b304e48d9c60477dfe5834693ef80db +0 -0
- data/fuzz/corpus/40cf2f11c2ea03c56864f5097d2f989b14762426 +0 -0
- data/fuzz/corpus/40cfc6a6a60f2451974522e5450ba6434f61a8bb +0 -0
- data/fuzz/corpus/4c3f43f4654332520eb57a9bcfe95951857272ce +0 -0
- data/fuzz/corpus/4c85fdf6934e86766274dd668f3f69f15c7bbc11 +0 -0
- data/fuzz/corpus/4f708dd44266b8b567e295c03811feef57c0a493 +0 -0
- data/fuzz/corpus/50cfff5d1cfc310686d7c1f38787cb77ec6fd892 +0 -0
- data/fuzz/corpus/53b2e1cb235c4479711d9ac74a375330d69f9e4c +0 -0
- data/fuzz/corpus/560373973be830ca5a34ab8e4d4b25a942ad5ed6 +0 -0
- data/fuzz/corpus/57768a4d41ccb6149219ba3eb7fe9e5b191dc4aa +0 -0
- data/fuzz/corpus/5a7833a1a882226aa3831298fdb0d23b580016d8 +0 -0
- data/fuzz/corpus/5b394ecc62e63d030cfeff7e0c484c233c7e9d63 +0 -0
- data/fuzz/corpus/5c10ea32f35ab603f1acd9b3c3ac3c397c1c690a +0 -0
- data/fuzz/corpus/5d4f6df161ce9c6465641cea9e0e97771f4249cf +0 -0
- data/fuzz/corpus/5f29c11e0f583226dfa98c236b8d48c772bccbf1 +0 -0
- data/fuzz/corpus/603d6df8d387f6b191f9b732d8928638e2deab10 +0 -0
- data/fuzz/corpus/60e17201e5bc64340b37ac96ff6302546c231eea +0 -0
- data/fuzz/corpus/65cc3e13ae39a4cd1b207a3395b5205d3fbcae7b +0 -0
- data/fuzz/corpus/692c3712da22c928e0d1b5f91ea7a74ba6fcdf5f +0 -0
- data/fuzz/corpus/69ee357cc3bda8a068bc875dda0879bb462b4984 +0 -0
- data/fuzz/corpus/6a2310279f4fa43ee4ed3809bc039f8ec7bfdeb4 +0 -0
- data/fuzz/corpus/6bfdbaafbe1d257b49f58b7a2fa3fef51894d76a +0 -0
- data/fuzz/corpus/6d3c4288760aaf7131fb3caebb5b3aad6a1828fd +0 -0
- data/fuzz/corpus/6e2f243fb0020e9bfb797c0100da18a67c8f1cb7 +0 -0
- data/fuzz/corpus/6ee7bf4cd8249100046bd4ba28b6646dfd436bd4 +0 -0
- data/fuzz/corpus/70e0947673dd1cc2680f907ecfde339688c3fadb +0 -0
- data/fuzz/corpus/76b1ab6e0edbfeefd4a23fc8a18f7d5937fe3787 +0 -0
- data/fuzz/corpus/77d6e8a03cec0da5803042fd51030e97fb11f5fd +0 -0
- data/fuzz/corpus/79b7efd2703e6343b8daf769aea6a76b199d7aa2 +0 -0
- data/fuzz/corpus/7ab28e22d79e7f2774ab2b8baaddeedf58f37475 +0 -0
- data/fuzz/corpus/7cb99d73cc9c03dc463a7892b27ae78a857c2450 +0 -0
- data/fuzz/corpus/81f5010a3c5a44639c0fbb6325c163cd5f8b7ee9 +0 -0
- data/fuzz/corpus/8317c66a4178b54bfd405332490dfd82a3949244 +0 -0
- data/fuzz/corpus/871b1e5a6ad58fbd654725c0c280f1aa278cd7b5 +0 -0
- data/fuzz/corpus/879bd3b24e7c968141bf3c69fdfb11da99af8e8e +0 -0
- data/fuzz/corpus/8a6bc02a79659eb0c71d5bc784da0f1b351b4336 +0 -0
- data/fuzz/corpus/8d6d41bc7e792d6737c35d7c989c9772ad3ae1d6 +0 -0
- data/fuzz/corpus/8ee4e2e6827b38feaa739c7a61535087ae1ca91c +0 -0
- data/fuzz/corpus/911a7cad153c517c75ea7d410c4f093865da5b50 +0 -0
- data/fuzz/corpus/932c12b633a087eff4f195da9fed03e202190515 +0 -0
- data/fuzz/corpus/93c883857d41e5cdaaa4c2b04dd316da73c9c519 +0 -0
- data/fuzz/corpus/944e31de1264ab5347f37d1454b6bcdd06b85b51 +0 -0
- data/fuzz/corpus/9882bea35622d45012774dbf8f9658c597f07d51 +0 -0
- data/fuzz/corpus/989f9271f90207d498fb69eeba16b8393f8cc70b +0 -0
- data/fuzz/corpus/9927a043166634231659695111b473cbd19fbeec +0 -0
- data/fuzz/corpus/9a33c2ce796569be8450658c6231936816ee55bb +0 -0
- data/fuzz/corpus/9a51def27a9547cf9aa059809a2d5482f75ae8af +0 -0
- data/fuzz/corpus/9b915fd1452d10a00ae2ecb0d53784a127341e79 +0 -0
- data/fuzz/corpus/9de518e7fb61598ac152a91feaf2d7bbe3e8b943 +0 -0
- data/fuzz/corpus/9eaee706fb7d021dc5f0ecc79b524de9c74f439e +0 -0
- data/fuzz/corpus/9ecb6bbe9d4c7414406e4c002b449279bf898ee4 +0 -0
- data/fuzz/corpus/a389244e26c3e525393db7c6b89495981c5160e5 +0 -0
- data/fuzz/corpus/a52173807baf225d705386e47e57a115a8cb537c +0 -0
- data/fuzz/corpus/a6fdb8761221dfd348c29c9eb224eb5cf5e4849a +0 -0
- data/fuzz/corpus/a76ca8a3f3ffb4ff898f896b65a5bae2d0ed002a +0 -0
- data/fuzz/corpus/a91ff9c074e9503baa27194d220dca03926a4f3d +0 -0
- data/fuzz/corpus/aa66cf1089cea0a8aa7e54394e75d4f2949bba74 +0 -0
- data/fuzz/corpus/aafdf57c09c20115384292e1b39cf7dae6674fff +0 -0
- data/fuzz/corpus/af0d6601066ec5c6bac2f0a57b5753ef2826463d +0 -0
- data/fuzz/corpus/b26766574788c20a8ec6927c19327f3919c7a69f +0 -0
- data/fuzz/corpus/b2b582043818797e2a53a72e3f24e545fbda3b57 +0 -0
- data/fuzz/corpus/b31e0aa5172beb560c264d8b5c84932870271e9e +0 -0
- data/fuzz/corpus/b396625aa6e69a5eef782446bbc09606050abb8a +0 -0
- data/fuzz/corpus/b5693370c14ce015236f7746ee63d0af38ec89e4 +0 -0
- data/fuzz/corpus/b8fd1ed5f8c575a8ac6a7c468bf93f1f96f8edb4 +0 -0
- data/fuzz/corpus/b928cbb72b744adec35cba73db6b09b4c0c8af42 +0 -0
- data/fuzz/corpus/b9e36aaac143135fadf4bbcac8d892a7b515cbaf +0 -0
- data/fuzz/corpus/bae2b0281a4ce082295254dd4f49183ca6b5f2d3 +0 -0
- data/fuzz/corpus/bfdc9d194a5a4ed98adfa14a47cec84ca91aa4ad +0 -0
- data/fuzz/corpus/c2f64ce75de11ecb09496d5d95955da7730872e1 +0 -0
- data/fuzz/corpus/c45f32e7bfa9ea837d6cdd1844d7c8d354981242 +0 -0
- data/fuzz/corpus/c64d7ac2a14b5227524521fab16ff0ca4e5170d6 +0 -0
- data/fuzz/corpus/c6f8466a86b7371cbf715317cbd313f609ee739b +0 -0
- data/fuzz/corpus/c9d6e62a62fffb21687fae534b8113a07f92aee6 +0 -0
- data/fuzz/corpus/d0171338648afb30043ea71f3561c60fd8372a1d +0 -0
- data/fuzz/corpus/d0faa1afa3e94f6a51a1a4c0d3dafb2600592593 +0 -0
- data/fuzz/corpus/d467fc34aec5d7f2b24a3df67dbf99080dc00681 +0 -0
- data/fuzz/corpus/d696b7b20d1cd2a3c2bf47c302afbba1696632c9 +0 -0
- data/fuzz/corpus/d6d7695a3c86643a3ad779b75ea4d00fb535a3d9 +0 -0
- data/fuzz/corpus/d7b303e5aec6bac4311102a324cbea23663b2b20 +0 -0
- data/fuzz/corpus/d8afc09fe51ea23526db161a2a1cdacba0b1fefe +0 -0
- data/fuzz/corpus/d9bb42d0a55b8a200e3dfea84ca2e6e265ef2366 +0 -0
- data/fuzz/corpus/da6d7c1f2a7d5b0dabe71f048c9c1624c8ebdbd9 +0 -0
- data/fuzz/corpus/da70dbded78bc388cd692dc36b9da4697d5b6ad2 +0 -0
- data/fuzz/corpus/da71cde284c9f10c6c6952e2e800558720914c30 +0 -0
- data/fuzz/corpus/dcdfeaf43771474c3b236ffe79e14dc78b3b09ec +0 -0
- data/fuzz/corpus/dcef32e091776dc98ac4a4cf951528857d0b7ce1 +0 -0
- data/fuzz/corpus/ddedc95cd0c2db9ad8895fb33227e3e759f57ed9 +0 -0
- data/fuzz/corpus/de361721e7f714ac3a55ef5839801e0bf3566b37 +0 -0
- data/fuzz/corpus/e1c05af770b1443f8cfeff261affb50b3c551aa0 +0 -0
- data/fuzz/corpus/e62f88307794127ffccadf614b7062c3e78ce5c0 +0 -0
- data/fuzz/corpus/e85854d63d682e0a16f9aefa477a4337dbc63f5d +0 -0
- data/fuzz/corpus/e86666a117e96697f7823d6d61b448a76c7c8c64 +0 -0
- data/fuzz/corpus/e91897c8cefdd583e1d60e6a833fab40d775cff1 +0 -0
- data/fuzz/corpus/eb8ed69481cf71efdac8df0759d28624f2bc40cc +0 -0
- data/fuzz/corpus/ee9308e841589fbabf3013d9366ac6556d82bdf9 +0 -0
- data/fuzz/corpus/ef2a0b39689c489d499abc4188b45fafdba66ab8 +0 -0
- data/fuzz/corpus/f013d9c3de8fe4958ad84c56c651031314a92e26 +0 -0
- data/fuzz/corpus/f19cf033be3041feeee3eefe45ba5adbf2f5d421 +0 -0
- data/fuzz/corpus/f1c9fa3baf1730afaa330216edcb5ba34d2590b0 +0 -0
- data/fuzz/corpus/f1ed8642840969d76fff003dc68e48a717050e72 +0 -0
- data/fuzz/corpus/f2fbe027944e00facddfcb425be9c0269fe3b8b9 +0 -0
- data/fuzz/corpus/f7f6ff7ec8323de5100118914bacde5b4cc81a9e +0 -0
- data/fuzz/corpus/fade0bf074f861cdb9013fbdd3b084a60d9ac858 +0 -0
- data/fuzz/corpus/fd85b215e07f3ce968a5e0b810c6d04930b533e8 +0 -0
- data/fuzz/corpus/fd883fb9a67accfc7c61ddda6a781677cc8efc33 +0 -0
- data/fuzz/corpus/fd8953a87e3026bdb15d52faa387a6d49586cd66 +0 -0
- data/fuzz/corpus/fe46814360987e6b9cdaebf31a8a4ec7d26125b4 +0 -0
- data/fuzz/corpus/ffd623a08d5c93aae2ffed33346316d76d9abf1c +0 -0
- data/fuzz/instrumentation.rb +13 -0
- data/fuzz/run +11 -0
- data/fuzz/test_harness.rb +38 -0
- data/fuzz/test_tracer.rb +16 -0
- data/lib/xlat/adapters/linux_tun.rb +55 -0
- data/lib/xlat/address_translation.rb +32 -0
- data/lib/xlat/address_translators/rfc6052.rb +46 -0
- data/lib/xlat/common.rb +13 -0
- data/lib/xlat/pcap.rb +48 -0
- data/lib/xlat/protocols/icmp/base.rb +76 -0
- data/lib/xlat/protocols/icmp/echo.rb +71 -0
- data/lib/xlat/protocols/icmp/error.rb +70 -0
- data/lib/xlat/protocols/icmp.rb +14 -0
- data/lib/xlat/protocols/ip/ipv4.rb +106 -0
- data/lib/xlat/protocols/ip/ipv6.rb +133 -0
- data/lib/xlat/protocols/ip.rb +208 -0
- data/lib/xlat/protocols/tcp.rb +71 -0
- data/lib/xlat/protocols/tcpudp.rb +52 -0
- data/lib/xlat/protocols/udp.rb +66 -0
- data/lib/xlat/protocols.rb +0 -0
- data/lib/xlat/rfc7915.rb +580 -0
- data/lib/xlat/runner.rb +51 -0
- data/lib/xlat/version.rb +5 -0
- data/lib/xlat.rb +8 -0
- data/sig/xlat.rbs +4 -0
- metadata +246 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is based on the source code available at https://github.com/kazuho/rat under MIT License
|
4
|
+
#
|
5
|
+
# Copyright (c) 2022 Kazuho Oku
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be included in all
|
15
|
+
# copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23
|
+
# SOFTWARE.
|
24
|
+
|
25
|
+
|
26
|
+
require_relative './tcpudp'
|
27
|
+
|
28
|
+
module Xlat
|
29
|
+
module Protocols
|
30
|
+
class Udp < Tcpudp
|
31
|
+
PROTOCOL_ID = 17
|
32
|
+
CHECKSUM_OFFSET = 6
|
33
|
+
|
34
|
+
def parse
|
35
|
+
packet = @packet
|
36
|
+
return nil if packet.l4_bytes_length < (@icmp_payload ? 4 : 8)
|
37
|
+
|
38
|
+
# bytes = packet.l4_bytes
|
39
|
+
# offset = packet.l4_bytes_offset
|
40
|
+
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
def apply(cs_delta)
|
45
|
+
return if cs_delta.zero?
|
46
|
+
|
47
|
+
packet = @packet
|
48
|
+
return if packet.l4_bytes_length < 8
|
49
|
+
|
50
|
+
bytes = packet.l4_bytes
|
51
|
+
offset = packet.l4_bytes_offset
|
52
|
+
|
53
|
+
checksum = bytes.get_value(:U16, offset + 6)
|
54
|
+
return if checksum == 0 # TODO: in ipv6 this requires calculation
|
55
|
+
|
56
|
+
checksum = 0 if checksum == 0xFFFF
|
57
|
+
checksum = _adjust_checksum(checksum, cs_delta)
|
58
|
+
checksum = 0xFFFF if checksum == 0
|
59
|
+
|
60
|
+
bytes.set_value(:U16, offset + 6, checksum)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
File without changes
|
data/lib/xlat/rfc7915.rb
ADDED
@@ -0,0 +1,580 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'xlat/common'
|
4
|
+
|
5
|
+
module Xlat
|
6
|
+
# RFC 7915 based stateless IPv4/IPv6 translator (SIIT). Intentionally not thread-safe.
|
7
|
+
#
|
8
|
+
# https://datatracker.ietf.org/doc/html/rfc7915
|
9
|
+
# https://www.rfc-editor.org/info/rfc7915
|
10
|
+
class Rfc7915
|
11
|
+
extend Xlat::Common
|
12
|
+
include Xlat::Common
|
13
|
+
|
14
|
+
class BufferInUse < StandardError; end
|
15
|
+
|
16
|
+
MAX_FRAGMENT_ID = 0xffffffff
|
17
|
+
|
18
|
+
# @param source_address_translator [Xlat::AddressTranslation]
|
19
|
+
# @param destination_address_translator [Xlat::AddressTranslation]
|
20
|
+
def initialize(source_address_translator:, destination_address_translator:, for_icmp: false)
|
21
|
+
@source_address_translator = source_address_translator
|
22
|
+
@destination_address_translator = destination_address_translator
|
23
|
+
|
24
|
+
# checksum_neutrality = @source_address_translator.checksum_neutral? && @destination_address_translator.checksum_neutral?
|
25
|
+
|
26
|
+
@next_fragment_identifier = 0
|
27
|
+
|
28
|
+
@ipv4_new_header_buffer = IO::Buffer.new(20)
|
29
|
+
@ipv6_new_header_buffer = IO::Buffer.new(40)
|
30
|
+
@output = []
|
31
|
+
@new_header_buffer_in_use = false
|
32
|
+
return_buffer_ownership
|
33
|
+
|
34
|
+
unless for_icmp
|
35
|
+
@inner_icmp = self.class.new(source_address_translator: destination_address_translator, destination_address_translator: source_address_translator, for_icmp: true)
|
36
|
+
@inner_packet = Protocols::Ip.new(icmp_payload: true)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_reader :next_fragment_identifier
|
41
|
+
|
42
|
+
def next_fragment_identifier=(x)
|
43
|
+
@inner_icmp&.next_fragment_identifier = x
|
44
|
+
@next_fragment_identifier = x
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns array of bytestrings to send as a IPv4 packet. May update original packet content.
|
48
|
+
def translate_to_ipv4(ipv6_packet, max_length)
|
49
|
+
raise BufferInUse if @new_header_buffer_in_use
|
50
|
+
raise ArgumentError unless ipv6_packet.version.to_i == 6
|
51
|
+
icmp_payload = @inner_icmp.nil?
|
52
|
+
@new_header_buffer_in_use = true
|
53
|
+
new_header_buffer = @ipv4_new_header_buffer
|
54
|
+
ipv6_bytes = ipv6_packet.bytes
|
55
|
+
ipv6_bytes_offset = ipv6_packet.bytes_offset
|
56
|
+
|
57
|
+
cs_delta = 0 # delta for incremental update of upper-layer checksum fields
|
58
|
+
|
59
|
+
# Version = 4, IHL = 5
|
60
|
+
new_header_buffer.set_value(:U8, 0, (4 << 4) + 5)
|
61
|
+
|
62
|
+
# FIXME: ToS ignored
|
63
|
+
|
64
|
+
# Total Length = copy from IPv6; may be updated in later step
|
65
|
+
ipv4_length = ipv6_packet.l4_length + 20
|
66
|
+
# not considering as a checksum delta because upper layer packet length doesn't take this into account; cs_delta += ipv6_length - ipv4_length
|
67
|
+
new_header_buffer.set_value(:U16, 2, ipv4_length)
|
68
|
+
|
69
|
+
# Identification = generate
|
70
|
+
new_header_buffer.set_value(:U16, 4, make_fragment_id())
|
71
|
+
|
72
|
+
# TTL = copy from IPv6
|
73
|
+
new_header_buffer.set_value(:U8, 8, ipv6_bytes.get_value(:U8, ipv6_bytes_offset + 7))
|
74
|
+
|
75
|
+
# Protocol = copy from IPv6; may be updated in later step for ICMPv6=>4 conversion
|
76
|
+
new_header_buffer.set_value(:U8, 9, ipv6_packet.proto)
|
77
|
+
|
78
|
+
# Source and Destination address
|
79
|
+
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()
|
80
|
+
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()
|
81
|
+
cs_delta += cs_delta_a + cs_delta_b
|
82
|
+
|
83
|
+
# TODO: DF bit
|
84
|
+
# TODO: discard if expired source route option is present
|
85
|
+
|
86
|
+
if ipv6_packet.proto == 58 # icmpv6
|
87
|
+
icmp_result, icmp_output = translate_icmpv6_to_icmpv4(ipv6_packet, new_header_buffer, max_length - 20)
|
88
|
+
return return_buffer_ownership() unless icmp_result
|
89
|
+
cs_delta += icmp_result
|
90
|
+
end
|
91
|
+
|
92
|
+
unless icmp_output
|
93
|
+
l4_length = ipv6_packet.l4_bytes_length
|
94
|
+
unless 20 + l4_length <= max_length
|
95
|
+
if icmp_payload
|
96
|
+
l4_length = max_length - 20
|
97
|
+
else
|
98
|
+
# FIXME: this should not happen
|
99
|
+
return return_buffer_ownership()
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
#p ipv6_bytes.chars.map { _1.ord.to_s(16).rjust(2,'0') }.join(' ')
|
105
|
+
#p new_header_buffer.chars.map { _1.ord.to_s(16).rjust(2,'0') }.join(' ')
|
106
|
+
|
107
|
+
ipv4_packet = ipv6_packet.convert_version!(Protocols::Ip::Ipv4, new_header_buffer, cs_delta)
|
108
|
+
ipv4_packet.apply_changes
|
109
|
+
|
110
|
+
# Recompute checksum (this must be performed after Ip#apply_changes as it updates ipv4 checksum field along with l4 checksum field using delta,
|
111
|
+
# while new_header_buffer has no prior checksum value)
|
112
|
+
new_header_buffer.set_value(:U16, 10, 0)
|
113
|
+
cksum = Protocols::Ip.checksum(new_header_buffer)
|
114
|
+
new_header_buffer.set_value(:U16, 10, cksum)
|
115
|
+
|
116
|
+
# TODO: Section 5.4. Generation of ICMPv6 Error Messages
|
117
|
+
# TODO: Section 5.1.1. IPv6 Fragment Processing
|
118
|
+
|
119
|
+
@output << new_header_buffer
|
120
|
+
if icmp_output
|
121
|
+
@output.concat(icmp_output)
|
122
|
+
else
|
123
|
+
@output << ipv4_packet.l4_bytes.slice(ipv4_packet.l4_bytes_offset, l4_length)
|
124
|
+
end
|
125
|
+
@output
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns array of bytestrings to send as a IPv6 packet. May update original packet content.
|
129
|
+
def translate_to_ipv6(ipv4_packet, max_length)
|
130
|
+
raise BufferInUse if @new_header_buffer_in_use
|
131
|
+
raise ArgumentError unless ipv4_packet.version.to_i == 4
|
132
|
+
icmp_payload = @inner_icmp.nil?
|
133
|
+
# TODO: support fragment extension and esp
|
134
|
+
# TODO: ignore extension
|
135
|
+
@new_header_buffer_in_use = true
|
136
|
+
ipv4_bytes = ipv4_packet.bytes
|
137
|
+
ipv4_bytes_offset = ipv4_packet.bytes_offset
|
138
|
+
new_header_buffer = @ipv6_new_header_buffer
|
139
|
+
cs_delta = 0 # delta for incremental update of upper-layer checksum fields
|
140
|
+
|
141
|
+
# Version = 6, traffic class = 0
|
142
|
+
new_header_buffer.set_value(:U8, 0, 6 << 4)
|
143
|
+
|
144
|
+
# Flow label = 0
|
145
|
+
|
146
|
+
# IPv6 Length = IPv4 total length - IPv4 header length; may be updated in later step
|
147
|
+
ipv6_length = ipv4_packet.l4_length
|
148
|
+
# not considering as a checksum delta because upper layer packet length doesn't take this into account; cs_delta += ipv4_length - ipv6_length
|
149
|
+
new_header_buffer.set_value(:U16, 4, ipv6_length)
|
150
|
+
|
151
|
+
# Next Header = copy from IPv4; may be updated in later step for ICMPv6=>4 conversion
|
152
|
+
new_header_buffer.set_value(:U8, 6, ipv4_packet.proto)
|
153
|
+
|
154
|
+
# Hop limit = copy from IPv4
|
155
|
+
new_header_buffer.set_value(:U8, 7, ipv4_bytes.get_value(:U8, ipv4_bytes_offset + 8))
|
156
|
+
|
157
|
+
# Source and Destination address
|
158
|
+
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()
|
159
|
+
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()
|
160
|
+
cs_delta += cs_delta_a + cs_delta_b
|
161
|
+
|
162
|
+
if ipv4_packet.proto == 1 # icmpv4
|
163
|
+
icmp_result, icmp_output = translate_icmpv4_to_icmpv6(ipv4_packet, new_header_buffer, max_length - 40)
|
164
|
+
return return_buffer_ownership() unless icmp_result
|
165
|
+
cs_delta += icmp_result
|
166
|
+
end
|
167
|
+
|
168
|
+
unless icmp_output
|
169
|
+
l4_length = ipv4_packet.l4_bytes_length
|
170
|
+
unless 40 + l4_length <= max_length
|
171
|
+
if icmp_payload
|
172
|
+
l4_length = max_length - 40
|
173
|
+
else
|
174
|
+
# FIXME: generate "fragmentation needed" if DF=1
|
175
|
+
return return_buffer_ownership()
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# TODO: generate udp checksum option (section 4.5.)
|
181
|
+
ipv6_packet = ipv4_packet.convert_version!(Protocols::Ip::Ipv6, new_header_buffer, cs_delta)
|
182
|
+
ipv6_packet.apply_changes
|
183
|
+
|
184
|
+
# TODO: Section 4.4. Generation of ICMPv4 Error Message
|
185
|
+
|
186
|
+
@output << new_header_buffer
|
187
|
+
if icmp_output
|
188
|
+
@output.concat(icmp_output)
|
189
|
+
else
|
190
|
+
@output << ipv6_packet.l4_bytes.slice(ipv6_packet.l4_bytes_offset, l4_length)
|
191
|
+
end
|
192
|
+
@output
|
193
|
+
end
|
194
|
+
|
195
|
+
def return_buffer_ownership
|
196
|
+
@new_header_buffer_in_use = false
|
197
|
+
@ipv4_new_header_buffer.clear
|
198
|
+
@ipv6_new_header_buffer.clear
|
199
|
+
@output.clear
|
200
|
+
if @inner_icmp
|
201
|
+
@inner_icmp.return_buffer_ownership
|
202
|
+
end
|
203
|
+
nil
|
204
|
+
end
|
205
|
+
|
206
|
+
private def make_fragment_id
|
207
|
+
id = @next_fragment_identifier
|
208
|
+
@next_fragment_identifier = @next_fragment_identifier.succ & MAX_FRAGMENT_ID
|
209
|
+
id
|
210
|
+
end
|
211
|
+
|
212
|
+
gen_type_map = ->(h) do
|
213
|
+
ary = Array.new(0xff.succ)
|
214
|
+
h.each_key do |(type,_)|
|
215
|
+
tary = ary[type] = Array.new(0xff.succ.succ)
|
216
|
+
h.each do |(type2,code),res|
|
217
|
+
next unless type == type2
|
218
|
+
tary[code || 0x100] = res
|
219
|
+
end
|
220
|
+
tary.freeze
|
221
|
+
end
|
222
|
+
Ractor.make_shareable(ary)
|
223
|
+
end
|
224
|
+
|
225
|
+
gen_pointer_map = ->(h) do
|
226
|
+
ary = Array.new(40)
|
227
|
+
h.each do |from_,to|
|
228
|
+
from = from_.is_a?(Integer) ? from_..from_ : from_
|
229
|
+
from.each do |f|
|
230
|
+
ary[f] = to
|
231
|
+
end
|
232
|
+
end
|
233
|
+
Ractor.make_shareable(ary)
|
234
|
+
end
|
235
|
+
|
236
|
+
ICMPV6V4_TYPE_MAP = gen_type_map[{
|
237
|
+
[1,0] => [3,1,:error_payload_rfc4884], # destination unreachable, no route to destination
|
238
|
+
[1,1] => [3,10,:error_payload_rfc4884], # destination unreachable, admin prohibited
|
239
|
+
[1,2] => [3,1,:error_payload_rfc4884], # destination unreachable, beyond scope of source address
|
240
|
+
[1,3] => [3,1,:error_payload_rfc4884], # destination unreachable, address unreachable
|
241
|
+
[1,4] => [3,3,:error_payload_rfc4884], # destination unreachable, port unreachable
|
242
|
+
|
243
|
+
[2,nil] => [3,4,:mtu], # packet too big
|
244
|
+
|
245
|
+
[3,nil] => [11,nil,:error_payload_rfc4884], # time exceeded (code unchanged)
|
246
|
+
|
247
|
+
[4,0] => [12,0,:pointer], # parameter problem, err header field
|
248
|
+
[4,1] => [3,2,:error_payload], # parameter problem, unrecognised next header type
|
249
|
+
|
250
|
+
[128,0] => [8,0], # echo request
|
251
|
+
[129,0] => [0,0], # echo reply
|
252
|
+
}]
|
253
|
+
|
254
|
+
# https://datatracker.ietf.org/doc/html/rfc7915#section-5.2 Figure 6
|
255
|
+
ICMPV6_POINTER_MAP = gen_pointer_map[{
|
256
|
+
0 => 0,
|
257
|
+
1 => 1,
|
258
|
+
4 => 2,
|
259
|
+
5 => 2,
|
260
|
+
6 => 9,
|
261
|
+
7 => 8,
|
262
|
+
8..23 => 12,
|
263
|
+
24..39 => 16,
|
264
|
+
}]
|
265
|
+
|
266
|
+
ICMPV4V6_TYPE_MAP = gen_type_map[{
|
267
|
+
[0,nil] => [129,0], # echo
|
268
|
+
[8,nil] => [128,0], # echo reply
|
269
|
+
[3,0] => [1,0,:error_payload_rfc4884], # destination unreachable, net unreachable
|
270
|
+
[3,1] => [1,1,:error_payload_rfc4884], # destination unreachable, host unreachable
|
271
|
+
[3,2] => [4,1,:pointer_static_next_header], # destination unreachable, protocol unreachable
|
272
|
+
[3,3] => [1,4,:error_payload_rfc4884], # destination unreachable, port unreachable
|
273
|
+
[3,4] => [2,0,:mtu], # destination unreachable, fragmentation needed
|
274
|
+
[3,5] => [1,0,:error_payload_rfc4884], # destination unreachable, source route failed
|
275
|
+
[3,6] => [1,0,:error_payload_rfc4884], # destination unreachable, ?
|
276
|
+
[3,7] => [1,0,:error_payload_rfc4884], # destination unreachable, ?
|
277
|
+
[3,8] => [1,0,:error_payload_rfc4884], # destination unreachable, ?
|
278
|
+
[3,9] => [1,1,:error_payload_rfc4884], # destination unreachable, host admin prohibited
|
279
|
+
[3,10] => [1,1,:error_payload_rfc4884], # destination unreachable, host admin prohibited
|
280
|
+
[3,11] => [1,0,:error_payload_rfc4884], # destination unreachable, ?
|
281
|
+
[3,12] => [1,0,:error_payload_rfc4884], # destination unreachable, ?
|
282
|
+
[3,13] => [1,1,:error_payload_rfc4884], # destination unreachable, admin prohibited
|
283
|
+
[3,15] => [1,1,:error_payload_rfc4884], # destination unreachable, precedence cutoff in effect
|
284
|
+
[11,nil] => [3,nil,:error_payload_rfc4884], # time exceeded
|
285
|
+
[12,0] => [4,0,:pointer], # parameter problem, pointer indicates the error
|
286
|
+
[12,2] => [4,0,:pointer], # parameter problem, bad length
|
287
|
+
}]
|
288
|
+
|
289
|
+
# https://datatracker.ietf.org/doc/html/rfc7915#section-5.2 Figure 6
|
290
|
+
ICMPV4_POINTER_MAP = gen_pointer_map[{
|
291
|
+
0 => 0,
|
292
|
+
1 => 1,
|
293
|
+
2..3 => 4,
|
294
|
+
8 => 7,
|
295
|
+
9 => 6,
|
296
|
+
12..15 => 8,
|
297
|
+
16..19 => 24,
|
298
|
+
}]
|
299
|
+
|
300
|
+
private def translate_icmpv6_to_icmpv4(ipv6_packet, new_header_buffer, max_length)
|
301
|
+
icmpv6 = ipv6_packet.l4
|
302
|
+
return unless icmpv6
|
303
|
+
outer_cs_delta = 0
|
304
|
+
cs_delta = 0
|
305
|
+
|
306
|
+
code_handlers = ICMPV6V4_TYPE_MAP[icmpv6.type]
|
307
|
+
return unless code_handlers
|
308
|
+
type_handler = code_handlers[icmpv6.code] || code_handlers[0x100]
|
309
|
+
return unless type_handler
|
310
|
+
new_type,new_code,payload_handler = type_handler
|
311
|
+
|
312
|
+
l4_bytes = ipv6_packet.l4_bytes
|
313
|
+
l4_bytes_offset = ipv6_packet.l4_bytes_offset
|
314
|
+
|
315
|
+
#p l4: [l4_bytes[l4_bytes_offset..]].join.chars.map { _1.ord.to_s(16).rjust(2,'0') }.join(' ')
|
316
|
+
|
317
|
+
cs_delta += (new_type - icmpv6.type) * 256
|
318
|
+
l4_bytes.set_value(:U8, l4_bytes_offset, new_type)
|
319
|
+
if new_code
|
320
|
+
cs_delta += (new_code - icmpv6.code)
|
321
|
+
l4_bytes.set_value(:U8, l4_bytes_offset+1, new_code)
|
322
|
+
end
|
323
|
+
|
324
|
+
#p l4: [l4_bytes[l4_bytes_offset..]].join.chars.map { _1.ord.to_s(16).rjust(2,'0') }.join(' ')
|
325
|
+
|
326
|
+
translate_payload = false
|
327
|
+
l4_length_changed = false
|
328
|
+
|
329
|
+
case payload_handler
|
330
|
+
when nil
|
331
|
+
# do nothing
|
332
|
+
when :error_payload
|
333
|
+
translate_payload = true
|
334
|
+
when :error_payload_rfc4884
|
335
|
+
translate_payload = :error_payload_rfc4884
|
336
|
+
when :mtu
|
337
|
+
translate_payload = true
|
338
|
+
# https://datatracker.ietf.org/doc/html/rfc1191#section-4
|
339
|
+
mtu = l4_bytes.get_value(:U16, l4_bytes_offset+6)
|
340
|
+
l4_bytes.set_value(:U16, l4_bytes_offset+4, 0)
|
341
|
+
l4_bytes.set_value(:U16, l4_bytes_offset+6,mtu-20) # FIXME: not complete implementation
|
342
|
+
when :pointer
|
343
|
+
translate_payload = true
|
344
|
+
ptr = l4_bytes.get_value(:U8, l4_bytes_offset+7)
|
345
|
+
newptr = ICMPV6_POINTER_MAP[ptr]
|
346
|
+
return unless newptr
|
347
|
+
l4_bytes.set_value(:U8, l4_bytes_offset+4,newptr)
|
348
|
+
l4_bytes.set_value(:U8, l4_bytes_offset+5,0)
|
349
|
+
l4_bytes.set_value(:U8, l4_bytes_offset+6,0)
|
350
|
+
l4_bytes.set_value(:U8, l4_bytes_offset+7,0)
|
351
|
+
else
|
352
|
+
raise
|
353
|
+
end
|
354
|
+
|
355
|
+
if translate_payload
|
356
|
+
return unless @inner_icmp # Do not translate payload in nested ICMP
|
357
|
+
|
358
|
+
payload_bytes = icmpv6.payload_bytes
|
359
|
+
payload_bytes_offset = icmpv6.payload_bytes_offset
|
360
|
+
payload_bytes_length = icmpv6.payload_bytes_length
|
361
|
+
|
362
|
+
if translate_payload == :error_payload_rfc4884
|
363
|
+
original_datagram_length = l4_bytes.get_value(:U8, l4_bytes_offset+4) * 8
|
364
|
+
return unless original_datagram_length < payload_bytes_length
|
365
|
+
rfc4884 = original_datagram_length > 0
|
366
|
+
end
|
367
|
+
|
368
|
+
original_datagram = @inner_packet.parse(
|
369
|
+
bytes: payload_bytes,
|
370
|
+
bytes_offset: payload_bytes_offset,
|
371
|
+
bytes_length: rfc4884 ? original_datagram_length : payload_bytes_length,
|
372
|
+
)
|
373
|
+
return unless original_datagram && original_datagram.version.to_i == 6
|
374
|
+
|
375
|
+
max_length -= 8 # ICMPv4 header
|
376
|
+
original_datagram_translated = @inner_icmp.translate_to_ipv4(original_datagram, [max_length, 512].min)
|
377
|
+
return unless original_datagram_translated
|
378
|
+
|
379
|
+
output = [l4_bytes.slice(l4_bytes_offset, 8), *original_datagram_translated]
|
380
|
+
|
381
|
+
if rfc4884
|
382
|
+
translated_length = original_datagram_translated.sum(&:size)
|
383
|
+
|
384
|
+
if translated_length < 128
|
385
|
+
# RFC 4884: the "original datagram" field MUST contain at least 128 octets.
|
386
|
+
padding_length = 128 - translated_length
|
387
|
+
new_original_datagram_length = 128
|
388
|
+
else
|
389
|
+
# RFC 4884: the "original datagram" field MUST be zero padded to the nearest 32-bit boundary.
|
390
|
+
new_original_datagram_length = 4 * translated_length.ceildiv(4)
|
391
|
+
padding_length = new_original_datagram_length - translated_length
|
392
|
+
end
|
393
|
+
output << IO::Buffer.new(padding_length) if padding_length > 0
|
394
|
+
|
395
|
+
max_length -= new_original_datagram_length
|
396
|
+
extension = payload_bytes.slice(payload_bytes_offset + original_datagram_length, [payload_bytes_length - original_datagram_length, max_length].min)
|
397
|
+
output << extension
|
398
|
+
|
399
|
+
l4_bytes.set_value(:U8, l4_bytes_offset + 4, 0) # Reserved
|
400
|
+
l4_bytes.set_value(:U8, l4_bytes_offset + 5, new_original_datagram_length / 4)
|
401
|
+
end
|
402
|
+
|
403
|
+
l4_length_changed = output.sum(&:size)
|
404
|
+
|
405
|
+
# Force recalculation of ICMP checksum
|
406
|
+
l4_bytes.set_value(:U16, l4_bytes_offset+2,0)
|
407
|
+
cksum = output ? Protocols::Ip.checksum_list(output) : Protocols::Ip.checksum(l4_bytes.slice(l4_bytes_offset))
|
408
|
+
l4_bytes.set_value(:U16, l4_bytes_offset+2, cksum)
|
409
|
+
|
410
|
+
else
|
411
|
+
# For incremental checksum update, remove pseudo header from ICMP checksum
|
412
|
+
cs_delta -= sum16be(ipv6_packet.tuple) + ipv6_packet.l4_length + 58
|
413
|
+
|
414
|
+
checksum = l4_bytes.get_value(:U16, l4_bytes_offset+2)
|
415
|
+
checksum = Protocols::Ip.checksum_adjust(checksum, cs_delta)
|
416
|
+
checksum = 65535 if checksum == 0
|
417
|
+
checksum = l4_bytes.set_value(:U16, l4_bytes_offset+2, checksum)
|
418
|
+
end
|
419
|
+
|
420
|
+
### NOTE: this method must not return nil beyond this line - altering outer l3 header ###
|
421
|
+
|
422
|
+
if l4_length_changed
|
423
|
+
# Update Outer IPv4 Total Length Field
|
424
|
+
new_total_length = 20+l4_length_changed
|
425
|
+
#p act: [new_header_buffer.size,l4_bytes[l4_bytes_offset..].size].sum, new_total_length:, present_total_length: string_get16be(new_header_buffer,2)
|
426
|
+
outer_cs_delta += new_total_length - new_header_buffer.get_value(:U16, 2)
|
427
|
+
new_header_buffer.set_value(:U16, 2,new_total_length)
|
428
|
+
end
|
429
|
+
|
430
|
+
new_header_buffer.set_value(:U8, 9, 1) # protocol=icmpv4
|
431
|
+
outer_cs_delta += -57 # 58(icmpv6)-1(icmpv4)
|
432
|
+
|
433
|
+
#p l4: [l4_bytes[l4_bytes_offset..]].join.chars.map { _1.ord.to_s(16).rjust(2,'0') }.join(' ')
|
434
|
+
[outer_cs_delta, output]
|
435
|
+
end
|
436
|
+
|
437
|
+
private def translate_icmpv4_to_icmpv6(ipv4_packet, new_header_buffer, max_length)
|
438
|
+
icmpv4 = ipv4_packet.l4
|
439
|
+
return unless icmpv4
|
440
|
+
outer_cs_delta = 0
|
441
|
+
cs_delta = 0
|
442
|
+
|
443
|
+
code_handlers = ICMPV4V6_TYPE_MAP[icmpv4.type]
|
444
|
+
return unless code_handlers
|
445
|
+
type_handler = code_handlers[icmpv4.code] || code_handlers[0x100]
|
446
|
+
return unless type_handler
|
447
|
+
new_type,new_code,payload_handler = type_handler
|
448
|
+
|
449
|
+
l4_bytes = ipv4_packet.l4_bytes
|
450
|
+
l4_bytes_offset = ipv4_packet.l4_bytes_offset
|
451
|
+
|
452
|
+
|
453
|
+
cs_delta += (new_type - icmpv4.type) * 256
|
454
|
+
l4_bytes.set_value(:U8, l4_bytes_offset, new_type)
|
455
|
+
if new_code
|
456
|
+
cs_delta += (new_code - icmpv4.code)
|
457
|
+
l4_bytes.set_value(:U8, l4_bytes_offset+1, new_code)
|
458
|
+
end
|
459
|
+
|
460
|
+
#p l4: [l4_bytes[l4_bytes_offset..]].join.chars.map { _1.ord.to_s(16).rjust(2,'0') }.join(' ')
|
461
|
+
|
462
|
+
translate_payload = false
|
463
|
+
l4_length_changed = false
|
464
|
+
|
465
|
+
case payload_handler
|
466
|
+
when nil
|
467
|
+
# do nothing
|
468
|
+
when :error_payload
|
469
|
+
translate_payload = true
|
470
|
+
when :error_payload_rfc4884
|
471
|
+
translate_payload = :error_payload_rfc4884
|
472
|
+
|
473
|
+
when :mtu
|
474
|
+
translate_payload = true
|
475
|
+
# https://datatracker.ietf.org/doc/html/rfc1191#section-4
|
476
|
+
mtu = l4_bytes.get_value(:U16, l4_bytes_offset+6)
|
477
|
+
l4_bytes.set_value(:U16, l4_bytes_offset+4,0)
|
478
|
+
new_mtu = mtu+20 # FIXME: not complete implementation
|
479
|
+
new_mtu = 1280 if mtu < 1280
|
480
|
+
l4_bytes.set_value(:U16, l4_bytes_offset+6,new_mtu)
|
481
|
+
|
482
|
+
when :pointer, :pointer_static_next_header
|
483
|
+
translate_payload = true
|
484
|
+
ptr = l4_bytes.get_value(:U8, l4_bytes_offset+4)
|
485
|
+
|
486
|
+
newptr = case
|
487
|
+
when newptr == :pointer_static_next_header
|
488
|
+
6 # Next Header
|
489
|
+
else
|
490
|
+
ICMPV4_POINTER_MAP[ptr]
|
491
|
+
end
|
492
|
+
return unless newptr
|
493
|
+
l4_bytes.set_value(:U16, l4_bytes_offset+4,0)
|
494
|
+
l4_bytes.set_value(:U16, l4_bytes_offset+6,newptr)
|
495
|
+
|
496
|
+
else
|
497
|
+
raise
|
498
|
+
end
|
499
|
+
|
500
|
+
if translate_payload
|
501
|
+
return unless @inner_icmp # Do not translate payload in nested ICMP
|
502
|
+
|
503
|
+
payload_bytes = icmpv4.payload_bytes
|
504
|
+
payload_bytes_offset = icmpv4.payload_bytes_offset
|
505
|
+
payload_bytes_length = icmpv4.payload_bytes_length
|
506
|
+
|
507
|
+
if translate_payload == :error_payload_rfc4884
|
508
|
+
original_datagram_length = l4_bytes.get_value(:U8, l4_bytes_offset+5) * 4
|
509
|
+
return unless original_datagram_length < payload_bytes_length
|
510
|
+
rfc4884 = original_datagram_length > 0
|
511
|
+
end
|
512
|
+
|
513
|
+
original_datagram = @inner_packet.parse(
|
514
|
+
bytes: payload_bytes,
|
515
|
+
bytes_offset: payload_bytes_offset,
|
516
|
+
bytes_length: rfc4884 ? original_datagram_length : payload_bytes_length,
|
517
|
+
)
|
518
|
+
return unless original_datagram && original_datagram.version.to_i == 4
|
519
|
+
|
520
|
+
max_length -= 8 # ICMPv6 header
|
521
|
+
original_datagram_translated = original_datagram && @inner_icmp.translate_to_ipv6(original_datagram, [max_length, 1200].min)
|
522
|
+
return unless original_datagram_translated
|
523
|
+
|
524
|
+
output = [l4_bytes.slice(l4_bytes_offset, 8), *original_datagram_translated]
|
525
|
+
|
526
|
+
if rfc4884
|
527
|
+
translated_length = original_datagram_translated.sum(&:size)
|
528
|
+
|
529
|
+
# RFC 4884: the "original datagram" field MUST be zero padded to the nearest 64-bit boundary.
|
530
|
+
new_original_datagram_length = 8 * translated_length.ceildiv(8)
|
531
|
+
padding_length = new_original_datagram_length - translated_length
|
532
|
+
output << IO::Buffer.new(padding_length) if padding_length > 0
|
533
|
+
|
534
|
+
max_length -= new_original_datagram_length
|
535
|
+
extension = payload_bytes.slice(payload_bytes_offset + original_datagram_length, [payload_bytes_length - original_datagram_length, max_length].min)
|
536
|
+
output << extension
|
537
|
+
|
538
|
+
l4_bytes.set_value(:U8, l4_bytes_offset + 4, new_original_datagram_length / 8)
|
539
|
+
l4_bytes.set_value(:U8, l4_bytes_offset + 5, 0) # Reserved
|
540
|
+
end
|
541
|
+
|
542
|
+
l4_length_changed = output.sum(&:size)
|
543
|
+
|
544
|
+
# Force recalculation of ICMP checksum
|
545
|
+
l4_bytes.set_value(:U16, l4_bytes_offset+2,0)
|
546
|
+
cksum = Protocols::Ip.checksum_list(output)
|
547
|
+
l4_bytes.set_value(:U16, l4_bytes_offset+2, cksum)
|
548
|
+
cksum = Protocols::Ip.checksum_adjust(cksum, Common.sum16be(new_header_buffer.slice(8,32)) + l4_length_changed + 58) # pseudo header
|
549
|
+
l4_bytes.set_value(:U16, l4_bytes_offset+2, cksum)
|
550
|
+
|
551
|
+
else
|
552
|
+
# For incremental checksum update, ADD pseudo header to ICMP checksum
|
553
|
+
# [8,32] = src+dst addr
|
554
|
+
cs_delta += Common.sum16be(new_header_buffer.slice(8,32)) + ipv4_packet.l4_length + 58
|
555
|
+
|
556
|
+
checksum = l4_bytes.get_value(:U16, l4_bytes_offset+2)
|
557
|
+
checksum = Protocols::Ip.checksum_adjust(checksum, cs_delta)
|
558
|
+
checksum = 65535 if checksum == 0
|
559
|
+
checksum = l4_bytes.set_value(:U16, l4_bytes_offset+2, checksum)
|
560
|
+
end
|
561
|
+
|
562
|
+
|
563
|
+
### NOTE: this method must not return nil beyond this line - altering outer l3 header ###
|
564
|
+
|
565
|
+
if l4_length_changed
|
566
|
+
# Update Outer IPv6 Payload Length field
|
567
|
+
new_payload_length = l4_length_changed
|
568
|
+
outer_cs_delta += new_payload_length - new_header_buffer.get_value(:U16,4)
|
569
|
+
new_header_buffer.set_value(:U16,4,new_payload_length)
|
570
|
+
end
|
571
|
+
|
572
|
+
new_header_buffer.set_value(:U8, 6, 58) # nextheader=icmpv4
|
573
|
+
outer_cs_delta += 57 # 58(icmpv6)-1(icmpv4)
|
574
|
+
|
575
|
+
#p l4: [l4_bytes[l4_bytes_offset..]].join.chars.map { _1.ord.to_s(16).rjust(2,'0') }.join(' ')
|
576
|
+
[outer_cs_delta, output]
|
577
|
+
end
|
578
|
+
|
579
|
+
end
|
580
|
+
end
|
data/lib/xlat/runner.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'xlat/protocols/ip'
|
2
|
+
|
3
|
+
module Xlat
|
4
|
+
class Runner
|
5
|
+
def initialize(adapter:, translator:, logger: nil)
|
6
|
+
@adapter = adapter
|
7
|
+
@translator = translator
|
8
|
+
@logger = logger
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
mtu = @adapter.mtu
|
13
|
+
buffer = IO::Buffer.new(mtu)
|
14
|
+
parser = Protocols::Ip.new
|
15
|
+
|
16
|
+
loop do
|
17
|
+
length = @adapter.read(buffer)
|
18
|
+
if length < 0
|
19
|
+
@logger&.error { "Failed to read packet (errno=#{-length})" }
|
20
|
+
next
|
21
|
+
end
|
22
|
+
|
23
|
+
pkt = parser.parse(bytes: buffer, bytes_length: length)
|
24
|
+
unless pkt
|
25
|
+
@logger&.info { "DISCARD: not parsable: #{buffer.slice(0, length).inspect}" }
|
26
|
+
next
|
27
|
+
end
|
28
|
+
|
29
|
+
case
|
30
|
+
when pkt.version == Protocols::Ip::Ipv4
|
31
|
+
output = @translator.translate_to_ipv6(pkt, mtu)
|
32
|
+
when pkt.version == Protocols::Ip::Ipv6
|
33
|
+
output = @translator.translate_to_ipv4(pkt, mtu)
|
34
|
+
else
|
35
|
+
fail 'unknown IP version'
|
36
|
+
end
|
37
|
+
|
38
|
+
unless output
|
39
|
+
@logger&.info { "DISCARD: not translatable: #{buffer.slice(0, length).inspect}" }
|
40
|
+
next
|
41
|
+
end
|
42
|
+
|
43
|
+
@adapter.write(*output)
|
44
|
+
rescue
|
45
|
+
fail "BUG: #{buffer.slice(0, length).inspect}"
|
46
|
+
ensure
|
47
|
+
@translator.return_buffer_ownership if output
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|