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.
Files changed (191) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +7 -0
  3. data/.rspec +3 -0
  4. data/Cargo.lock +348 -0
  5. data/Cargo.toml +7 -0
  6. data/Dockerfile +49 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +100 -0
  9. data/Rakefile +82 -0
  10. data/benchmark/run.rb +38 -0
  11. data/benchmark/tcp.cfg +23 -0
  12. data/clab/.gitignore +5 -0
  13. data/clab/464xlat-ce-pd.clab.yml +138 -0
  14. data/clab/build.sh +6 -0
  15. data/exe/xlat-siit +91 -0
  16. data/ext/xlat/io_buffer_ext/Cargo.toml +11 -0
  17. data/ext/xlat/io_buffer_ext/extconf.rb +4 -0
  18. data/ext/xlat/io_buffer_ext/src/gvl.rs +49 -0
  19. data/ext/xlat/io_buffer_ext/src/io_buffer.rs +67 -0
  20. data/ext/xlat/io_buffer_ext/src/lib.rs +83 -0
  21. data/fuzz/corpus/010507e2c9f3b5132cbf036c7197f155cd85cca8 +0 -0
  22. data/fuzz/corpus/019a67cbfe27ed6606fe9d9cbee1ba16eb160667 +0 -0
  23. data/fuzz/corpus/021f01b7a6d4ba3e8e45a733c453d6c84f8c4c38 +0 -0
  24. data/fuzz/corpus/02db19829d1a4cbe18127e9ab1f3792e0d7fd5f7 +0 -0
  25. data/fuzz/corpus/059b719daf320e809db75d37ecf7823ba33ca7aa +0 -0
  26. data/fuzz/corpus/080ce49cbdb1983f8b9af09d511c5ec5a6bb14ed +0 -0
  27. data/fuzz/corpus/080d7d8674e7d60ce13b48995567a4ad21a3d652 +0 -0
  28. data/fuzz/corpus/08d488926f31661a1b25f60a723ae1931c762255 +0 -0
  29. data/fuzz/corpus/09ee56fb3259c88ad2cb94951313d792f36da221 +0 -0
  30. data/fuzz/corpus/0bffe4074467b5c4ba2bd5aa33839c5a1b8c1d63 +0 -0
  31. data/fuzz/corpus/0dac63180387255727c844898164ce20ff08fded +0 -0
  32. data/fuzz/corpus/0dc6f6c09c5b7bb4da8d496ac924e7e0d4aa37ad +0 -0
  33. data/fuzz/corpus/0e3e8e947fd9bfed02f927d1a2651e484d231fc0 +0 -0
  34. data/fuzz/corpus/0e5fa83f0ef8ecbf774ac96b8bdeda31395a284d +0 -0
  35. data/fuzz/corpus/12f93fa0b09c44d6c61ccde2731b5554bb180144 +0 -0
  36. data/fuzz/corpus/1659c552b9be677f2c30210430a97c3628f615be +0 -0
  37. data/fuzz/corpus/183aeabfe58d5ccfd5f2ed84e2f5ee4931f14eb7 +0 -0
  38. data/fuzz/corpus/1b9293272fbacbfff7aff4fd951dd7beb6aed3bf +0 -0
  39. data/fuzz/corpus/1c30abedb2d0943ef79b0620f7b3a7e8cdebc67f +0 -0
  40. data/fuzz/corpus/1c479cc892198d0cc389cc681206393c8ae4cbb6 +0 -0
  41. data/fuzz/corpus/1ca9480af5da2221fc320bb281d7c4ff73d5f33c +0 -0
  42. data/fuzz/corpus/1d6087f18146fac3b663bcc635fb470cb651884b +0 -0
  43. data/fuzz/corpus/258d23cbc2c8081782f0dd539270848e275a8e15 +0 -0
  44. data/fuzz/corpus/27512fa5c0ac5f4d8393ab442b50af3e83047c01 +0 -0
  45. data/fuzz/corpus/29933024f32228a8b65419d63dcbb56124db0df7 +0 -0
  46. data/fuzz/corpus/2b19339a2f24978c8413912a8003ae8ba8885684 +0 -0
  47. data/fuzz/corpus/2f8d2861a271e3ee2f658b01ef6ba854d8837aff +0 -0
  48. data/fuzz/corpus/2fed10d20fc4ed25da9cebd41209886304b39465 +0 -0
  49. data/fuzz/corpus/30fd38c3af4877eda7b1ca2dee1a60c4fe0f757c +0 -0
  50. data/fuzz/corpus/3137ba78351b6757255261a4f364e44693c3c85f +0 -0
  51. data/fuzz/corpus/34e68133b209c490f960f9682d33d41aab99e60f +0 -0
  52. data/fuzz/corpus/359870740db1c245d6dfdad1f119c7b17c3f25fc +0 -0
  53. data/fuzz/corpus/37b1bb01b0544125df396b925e2c4242a90d1c52 +0 -0
  54. data/fuzz/corpus/387cab0f8ac36f0f5496f6b0aed7d6c7ca3f2dbf +0 -0
  55. data/fuzz/corpus/3c4e48335e11c3bd066bbb43db4bf82249668869 +0 -0
  56. data/fuzz/corpus/3ec111be62099aaa9b960f4b69c65a3b12962970 +0 -0
  57. data/fuzz/corpus/3fb2f3f01b304e48d9c60477dfe5834693ef80db +0 -0
  58. data/fuzz/corpus/40cf2f11c2ea03c56864f5097d2f989b14762426 +0 -0
  59. data/fuzz/corpus/40cfc6a6a60f2451974522e5450ba6434f61a8bb +0 -0
  60. data/fuzz/corpus/4c3f43f4654332520eb57a9bcfe95951857272ce +0 -0
  61. data/fuzz/corpus/4c85fdf6934e86766274dd668f3f69f15c7bbc11 +0 -0
  62. data/fuzz/corpus/4f708dd44266b8b567e295c03811feef57c0a493 +0 -0
  63. data/fuzz/corpus/50cfff5d1cfc310686d7c1f38787cb77ec6fd892 +0 -0
  64. data/fuzz/corpus/53b2e1cb235c4479711d9ac74a375330d69f9e4c +0 -0
  65. data/fuzz/corpus/560373973be830ca5a34ab8e4d4b25a942ad5ed6 +0 -0
  66. data/fuzz/corpus/57768a4d41ccb6149219ba3eb7fe9e5b191dc4aa +0 -0
  67. data/fuzz/corpus/5a7833a1a882226aa3831298fdb0d23b580016d8 +0 -0
  68. data/fuzz/corpus/5b394ecc62e63d030cfeff7e0c484c233c7e9d63 +0 -0
  69. data/fuzz/corpus/5c10ea32f35ab603f1acd9b3c3ac3c397c1c690a +0 -0
  70. data/fuzz/corpus/5d4f6df161ce9c6465641cea9e0e97771f4249cf +0 -0
  71. data/fuzz/corpus/5f29c11e0f583226dfa98c236b8d48c772bccbf1 +0 -0
  72. data/fuzz/corpus/603d6df8d387f6b191f9b732d8928638e2deab10 +0 -0
  73. data/fuzz/corpus/60e17201e5bc64340b37ac96ff6302546c231eea +0 -0
  74. data/fuzz/corpus/65cc3e13ae39a4cd1b207a3395b5205d3fbcae7b +0 -0
  75. data/fuzz/corpus/692c3712da22c928e0d1b5f91ea7a74ba6fcdf5f +0 -0
  76. data/fuzz/corpus/69ee357cc3bda8a068bc875dda0879bb462b4984 +0 -0
  77. data/fuzz/corpus/6a2310279f4fa43ee4ed3809bc039f8ec7bfdeb4 +0 -0
  78. data/fuzz/corpus/6bfdbaafbe1d257b49f58b7a2fa3fef51894d76a +0 -0
  79. data/fuzz/corpus/6d3c4288760aaf7131fb3caebb5b3aad6a1828fd +0 -0
  80. data/fuzz/corpus/6e2f243fb0020e9bfb797c0100da18a67c8f1cb7 +0 -0
  81. data/fuzz/corpus/6ee7bf4cd8249100046bd4ba28b6646dfd436bd4 +0 -0
  82. data/fuzz/corpus/70e0947673dd1cc2680f907ecfde339688c3fadb +0 -0
  83. data/fuzz/corpus/76b1ab6e0edbfeefd4a23fc8a18f7d5937fe3787 +0 -0
  84. data/fuzz/corpus/77d6e8a03cec0da5803042fd51030e97fb11f5fd +0 -0
  85. data/fuzz/corpus/79b7efd2703e6343b8daf769aea6a76b199d7aa2 +0 -0
  86. data/fuzz/corpus/7ab28e22d79e7f2774ab2b8baaddeedf58f37475 +0 -0
  87. data/fuzz/corpus/7cb99d73cc9c03dc463a7892b27ae78a857c2450 +0 -0
  88. data/fuzz/corpus/81f5010a3c5a44639c0fbb6325c163cd5f8b7ee9 +0 -0
  89. data/fuzz/corpus/8317c66a4178b54bfd405332490dfd82a3949244 +0 -0
  90. data/fuzz/corpus/871b1e5a6ad58fbd654725c0c280f1aa278cd7b5 +0 -0
  91. data/fuzz/corpus/879bd3b24e7c968141bf3c69fdfb11da99af8e8e +0 -0
  92. data/fuzz/corpus/8a6bc02a79659eb0c71d5bc784da0f1b351b4336 +0 -0
  93. data/fuzz/corpus/8d6d41bc7e792d6737c35d7c989c9772ad3ae1d6 +0 -0
  94. data/fuzz/corpus/8ee4e2e6827b38feaa739c7a61535087ae1ca91c +0 -0
  95. data/fuzz/corpus/911a7cad153c517c75ea7d410c4f093865da5b50 +0 -0
  96. data/fuzz/corpus/932c12b633a087eff4f195da9fed03e202190515 +0 -0
  97. data/fuzz/corpus/93c883857d41e5cdaaa4c2b04dd316da73c9c519 +0 -0
  98. data/fuzz/corpus/944e31de1264ab5347f37d1454b6bcdd06b85b51 +0 -0
  99. data/fuzz/corpus/9882bea35622d45012774dbf8f9658c597f07d51 +0 -0
  100. data/fuzz/corpus/989f9271f90207d498fb69eeba16b8393f8cc70b +0 -0
  101. data/fuzz/corpus/9927a043166634231659695111b473cbd19fbeec +0 -0
  102. data/fuzz/corpus/9a33c2ce796569be8450658c6231936816ee55bb +0 -0
  103. data/fuzz/corpus/9a51def27a9547cf9aa059809a2d5482f75ae8af +0 -0
  104. data/fuzz/corpus/9b915fd1452d10a00ae2ecb0d53784a127341e79 +0 -0
  105. data/fuzz/corpus/9de518e7fb61598ac152a91feaf2d7bbe3e8b943 +0 -0
  106. data/fuzz/corpus/9eaee706fb7d021dc5f0ecc79b524de9c74f439e +0 -0
  107. data/fuzz/corpus/9ecb6bbe9d4c7414406e4c002b449279bf898ee4 +0 -0
  108. data/fuzz/corpus/a389244e26c3e525393db7c6b89495981c5160e5 +0 -0
  109. data/fuzz/corpus/a52173807baf225d705386e47e57a115a8cb537c +0 -0
  110. data/fuzz/corpus/a6fdb8761221dfd348c29c9eb224eb5cf5e4849a +0 -0
  111. data/fuzz/corpus/a76ca8a3f3ffb4ff898f896b65a5bae2d0ed002a +0 -0
  112. data/fuzz/corpus/a91ff9c074e9503baa27194d220dca03926a4f3d +0 -0
  113. data/fuzz/corpus/aa66cf1089cea0a8aa7e54394e75d4f2949bba74 +0 -0
  114. data/fuzz/corpus/aafdf57c09c20115384292e1b39cf7dae6674fff +0 -0
  115. data/fuzz/corpus/af0d6601066ec5c6bac2f0a57b5753ef2826463d +0 -0
  116. data/fuzz/corpus/b26766574788c20a8ec6927c19327f3919c7a69f +0 -0
  117. data/fuzz/corpus/b2b582043818797e2a53a72e3f24e545fbda3b57 +0 -0
  118. data/fuzz/corpus/b31e0aa5172beb560c264d8b5c84932870271e9e +0 -0
  119. data/fuzz/corpus/b396625aa6e69a5eef782446bbc09606050abb8a +0 -0
  120. data/fuzz/corpus/b5693370c14ce015236f7746ee63d0af38ec89e4 +0 -0
  121. data/fuzz/corpus/b8fd1ed5f8c575a8ac6a7c468bf93f1f96f8edb4 +0 -0
  122. data/fuzz/corpus/b928cbb72b744adec35cba73db6b09b4c0c8af42 +0 -0
  123. data/fuzz/corpus/b9e36aaac143135fadf4bbcac8d892a7b515cbaf +0 -0
  124. data/fuzz/corpus/bae2b0281a4ce082295254dd4f49183ca6b5f2d3 +0 -0
  125. data/fuzz/corpus/bfdc9d194a5a4ed98adfa14a47cec84ca91aa4ad +0 -0
  126. data/fuzz/corpus/c2f64ce75de11ecb09496d5d95955da7730872e1 +0 -0
  127. data/fuzz/corpus/c45f32e7bfa9ea837d6cdd1844d7c8d354981242 +0 -0
  128. data/fuzz/corpus/c64d7ac2a14b5227524521fab16ff0ca4e5170d6 +0 -0
  129. data/fuzz/corpus/c6f8466a86b7371cbf715317cbd313f609ee739b +0 -0
  130. data/fuzz/corpus/c9d6e62a62fffb21687fae534b8113a07f92aee6 +0 -0
  131. data/fuzz/corpus/d0171338648afb30043ea71f3561c60fd8372a1d +0 -0
  132. data/fuzz/corpus/d0faa1afa3e94f6a51a1a4c0d3dafb2600592593 +0 -0
  133. data/fuzz/corpus/d467fc34aec5d7f2b24a3df67dbf99080dc00681 +0 -0
  134. data/fuzz/corpus/d696b7b20d1cd2a3c2bf47c302afbba1696632c9 +0 -0
  135. data/fuzz/corpus/d6d7695a3c86643a3ad779b75ea4d00fb535a3d9 +0 -0
  136. data/fuzz/corpus/d7b303e5aec6bac4311102a324cbea23663b2b20 +0 -0
  137. data/fuzz/corpus/d8afc09fe51ea23526db161a2a1cdacba0b1fefe +0 -0
  138. data/fuzz/corpus/d9bb42d0a55b8a200e3dfea84ca2e6e265ef2366 +0 -0
  139. data/fuzz/corpus/da6d7c1f2a7d5b0dabe71f048c9c1624c8ebdbd9 +0 -0
  140. data/fuzz/corpus/da70dbded78bc388cd692dc36b9da4697d5b6ad2 +0 -0
  141. data/fuzz/corpus/da71cde284c9f10c6c6952e2e800558720914c30 +0 -0
  142. data/fuzz/corpus/dcdfeaf43771474c3b236ffe79e14dc78b3b09ec +0 -0
  143. data/fuzz/corpus/dcef32e091776dc98ac4a4cf951528857d0b7ce1 +0 -0
  144. data/fuzz/corpus/ddedc95cd0c2db9ad8895fb33227e3e759f57ed9 +0 -0
  145. data/fuzz/corpus/de361721e7f714ac3a55ef5839801e0bf3566b37 +0 -0
  146. data/fuzz/corpus/e1c05af770b1443f8cfeff261affb50b3c551aa0 +0 -0
  147. data/fuzz/corpus/e62f88307794127ffccadf614b7062c3e78ce5c0 +0 -0
  148. data/fuzz/corpus/e85854d63d682e0a16f9aefa477a4337dbc63f5d +0 -0
  149. data/fuzz/corpus/e86666a117e96697f7823d6d61b448a76c7c8c64 +0 -0
  150. data/fuzz/corpus/e91897c8cefdd583e1d60e6a833fab40d775cff1 +0 -0
  151. data/fuzz/corpus/eb8ed69481cf71efdac8df0759d28624f2bc40cc +0 -0
  152. data/fuzz/corpus/ee9308e841589fbabf3013d9366ac6556d82bdf9 +0 -0
  153. data/fuzz/corpus/ef2a0b39689c489d499abc4188b45fafdba66ab8 +0 -0
  154. data/fuzz/corpus/f013d9c3de8fe4958ad84c56c651031314a92e26 +0 -0
  155. data/fuzz/corpus/f19cf033be3041feeee3eefe45ba5adbf2f5d421 +0 -0
  156. data/fuzz/corpus/f1c9fa3baf1730afaa330216edcb5ba34d2590b0 +0 -0
  157. data/fuzz/corpus/f1ed8642840969d76fff003dc68e48a717050e72 +0 -0
  158. data/fuzz/corpus/f2fbe027944e00facddfcb425be9c0269fe3b8b9 +0 -0
  159. data/fuzz/corpus/f7f6ff7ec8323de5100118914bacde5b4cc81a9e +0 -0
  160. data/fuzz/corpus/fade0bf074f861cdb9013fbdd3b084a60d9ac858 +0 -0
  161. data/fuzz/corpus/fd85b215e07f3ce968a5e0b810c6d04930b533e8 +0 -0
  162. data/fuzz/corpus/fd883fb9a67accfc7c61ddda6a781677cc8efc33 +0 -0
  163. data/fuzz/corpus/fd8953a87e3026bdb15d52faa387a6d49586cd66 +0 -0
  164. data/fuzz/corpus/fe46814360987e6b9cdaebf31a8a4ec7d26125b4 +0 -0
  165. data/fuzz/corpus/ffd623a08d5c93aae2ffed33346316d76d9abf1c +0 -0
  166. data/fuzz/instrumentation.rb +13 -0
  167. data/fuzz/run +11 -0
  168. data/fuzz/test_harness.rb +38 -0
  169. data/fuzz/test_tracer.rb +16 -0
  170. data/lib/xlat/adapters/linux_tun.rb +55 -0
  171. data/lib/xlat/address_translation.rb +32 -0
  172. data/lib/xlat/address_translators/rfc6052.rb +46 -0
  173. data/lib/xlat/common.rb +13 -0
  174. data/lib/xlat/pcap.rb +48 -0
  175. data/lib/xlat/protocols/icmp/base.rb +76 -0
  176. data/lib/xlat/protocols/icmp/echo.rb +71 -0
  177. data/lib/xlat/protocols/icmp/error.rb +70 -0
  178. data/lib/xlat/protocols/icmp.rb +14 -0
  179. data/lib/xlat/protocols/ip/ipv4.rb +106 -0
  180. data/lib/xlat/protocols/ip/ipv6.rb +133 -0
  181. data/lib/xlat/protocols/ip.rb +208 -0
  182. data/lib/xlat/protocols/tcp.rb +71 -0
  183. data/lib/xlat/protocols/tcpudp.rb +52 -0
  184. data/lib/xlat/protocols/udp.rb +66 -0
  185. data/lib/xlat/protocols.rb +0 -0
  186. data/lib/xlat/rfc7915.rb +580 -0
  187. data/lib/xlat/runner.rb +51 -0
  188. data/lib/xlat/version.rb +5 -0
  189. data/lib/xlat.rb +8 -0
  190. data/sig/xlat.rbs +4 -0
  191. 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
@@ -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
@@ -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