Add 'clatd/' from commit '20078300757aea3988fa8467649c19ae6d353af3'
Test: TreeHugger
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: I18ccf19024f0617778799f548c8707a518eefadc
git-subtree-dir: clatd
git-subtree-mainline: 655d0850ace85a606efe00bc6300d516b6bed238
git-subtree-split: 20078300757aea3988fa8467649c19ae6d353af3
diff --git a/clatd/.clang-format b/clatd/.clang-format
new file mode 100644
index 0000000..f1debbd
--- /dev/null
+++ b/clatd/.clang-format
@@ -0,0 +1,8 @@
+BasedOnStyle: Google
+AlignConsecutiveAssignments: true
+AlignEscapedNewlines: Right
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 2
+Cpp11BracedListStyle: false
+TabWidth: 2
diff --git a/clatd/Android.bp b/clatd/Android.bp
new file mode 100644
index 0000000..595c6b9
--- /dev/null
+++ b/clatd/Android.bp
@@ -0,0 +1,118 @@
+package {
+ default_applicable_licenses: ["external_android-clat_license"],
+}
+
+// Added automatically by a large-scale-change
+//
+// large-scale-change included anything that looked like it might be a license
+// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc.
+//
+// Please consider removing redundant or irrelevant files from 'license_text:'.
+// See: http://go/android-license-faq
+license {
+ name: "external_android-clat_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "LICENSE",
+ "NOTICE",
+ ],
+}
+
+cc_defaults {
+ name: "clatd_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused-parameter",
+
+ // Bug: http://b/33566695
+ "-Wno-address-of-packed-member",
+ ],
+}
+
+// Code used both by the daemon and by unit tests.
+filegroup {
+ name: "clatd_common",
+ srcs: [
+ "clatd.c",
+ "dump.c",
+ "icmp.c",
+ "ipv4.c",
+ "ipv6.c",
+ "logging.c",
+ "translate.c",
+ ],
+}
+
+// The clat daemon.
+cc_binary {
+ name: "clatd",
+ defaults: ["clatd_defaults"],
+ srcs: [
+ ":clatd_common",
+ "main.c"
+ ],
+ static_libs: [
+ "libip_checksum",
+ ],
+ shared_libs: [
+ "liblog",
+ ],
+ relative_install_path: "for-system",
+
+ // Static libc++ for smaller apex size while shipping clatd in the mainline module.
+ // See b/213123047
+ stl: "libc++_static",
+
+ // Only enable clang-tidy for the daemon, not the tests, because enabling it for the
+ // tests substantially increases build/compile cycle times and doesn't really provide a
+ // security benefit.
+ tidy: true,
+ tidy_checks: [
+ "-*",
+ "cert-*",
+ "clang-analyzer-security*",
+ // b/2043314, warnings on memcpy_s, memset_s, snprintf_s calls
+ // are blocking the migration from gnu99 to gnu11.
+ // Until those warnings are fixed, disable these checks.
+ "-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling",
+ "android-*",
+ ],
+ tidy_checks_as_errors: [
+ "clang-analyzer-security*",
+ "cert-*",
+ "android-*",
+ ],
+
+ apex_available: [
+ "com.android.tethering",
+ "//apex_available:platform",
+ ],
+ min_sdk_version: "30",
+}
+
+// Unit tests.
+cc_test {
+ name: "clatd_test",
+ defaults: ["clatd_defaults"],
+ srcs: [
+ ":clatd_common",
+ "clatd_test.cpp"
+ ],
+ static_libs: [
+ "libbase",
+ "libip_checksum",
+ "libnetd_test_tun_interface",
+ ],
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libnetutils",
+ ],
+ test_suites: ["device-tests"],
+ require_root: true,
+}
diff --git a/clatd/BUGS b/clatd/BUGS
new file mode 100644
index 0000000..24e6639
--- /dev/null
+++ b/clatd/BUGS
@@ -0,0 +1,5 @@
+known problems/assumptions:
+ - does not handle protocols other than ICMP, UDP, TCP and GRE/ESP
+ - assumes the handset has its own (routed) /64 ipv6 subnet
+ - assumes the /128 ipv6 subnet it generates can use the nat64 gateway
+ - assumes the nat64 gateway has the ipv4 address in the last 32 bits of the ipv6 address (that it uses a /96 plat subnet)
diff --git a/clatd/LICENSE b/clatd/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/clatd/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/clatd/METADATA b/clatd/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/clatd/METADATA
@@ -0,0 +1,3 @@
+third_party {
+ license_type: NOTICE
+}
diff --git a/clatd/MODULE_LICENSE_APACHE2 b/clatd/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/clatd/MODULE_LICENSE_APACHE2
diff --git a/clatd/NOTICE b/clatd/NOTICE
new file mode 100644
index 0000000..5943b54
--- /dev/null
+++ b/clatd/NOTICE
@@ -0,0 +1,189 @@
+ Copyright (c) 2010-2012, Daniel Drown
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/clatd/OWNERS b/clatd/OWNERS
new file mode 100644
index 0000000..c24680e
--- /dev/null
+++ b/clatd/OWNERS
@@ -0,0 +1,2 @@
+set noparent
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
diff --git a/clatd/PREUPLOAD.cfg b/clatd/PREUPLOAD.cfg
new file mode 100644
index 0000000..c8dbf77
--- /dev/null
+++ b/clatd/PREUPLOAD.cfg
@@ -0,0 +1,5 @@
+[Builtin Hooks]
+clang_format = true
+
+[Builtin Hooks Options]
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
diff --git a/clatd/TEST_MAPPING b/clatd/TEST_MAPPING
new file mode 100644
index 0000000..d36908a
--- /dev/null
+++ b/clatd/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ { "name": "clatd_test" },
+ { "name": "netd_integration_test" },
+ { "name": "netd_unit_test" },
+ { "name": "netdutils_test" },
+ { "name": "resolv_integration_test" },
+ { "name": "resolv_unit_test" }
+ ]
+}
diff --git a/clatd/clatd.c b/clatd/clatd.c
new file mode 100644
index 0000000..bac8b1d
--- /dev/null
+++ b/clatd/clatd.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2012 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * clatd.c - tun interface setup and main event loop
+ */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <linux/filter.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/if_tun.h>
+#include <linux/virtio_net.h>
+#include <net/if.h>
+#include <sys/uio.h>
+
+#include "clatd.h"
+#include "checksum.h"
+#include "config.h"
+#include "dump.h"
+#include "logging.h"
+#include "translate.h"
+
+struct clat_config Global_Clatd_Config;
+
+volatile sig_atomic_t running = 1;
+
+// reads IPv6 packet from AF_PACKET socket, translates to IPv4, writes to tun
+void process_packet_6_to_4(struct tun_data *tunnel) {
+ // ethernet header is 14 bytes, plus 4 for a normal VLAN tag or 8 for Q-in-Q
+ // we don't really support vlans (or especially Q-in-Q)...
+ // but a few bytes of extra buffer space doesn't hurt...
+ struct {
+ struct virtio_net_hdr vnet;
+ uint8_t payload[22 + MAXMTU];
+ char pad; // +1 to make packet truncation obvious
+ } buf;
+ struct iovec iov = {
+ .iov_base = &buf,
+ .iov_len = sizeof(buf),
+ };
+ char cmsg_buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))];
+ struct msghdr msgh = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cmsg_buf,
+ .msg_controllen = sizeof(cmsg_buf),
+ };
+ ssize_t readlen = recvmsg(tunnel->read_fd6, &msgh, /*flags*/ 0);
+
+ if (readlen < 0) {
+ if (errno != EAGAIN) {
+ logmsg(ANDROID_LOG_WARN, "%s: read error: %s", __func__, strerror(errno));
+ }
+ return;
+ } else if (readlen == 0) {
+ logmsg(ANDROID_LOG_WARN, "%s: packet socket removed?", __func__);
+ running = 0;
+ return;
+ } else if (readlen >= sizeof(buf)) {
+ logmsg(ANDROID_LOG_WARN, "%s: read truncation - ignoring pkt", __func__);
+ return;
+ }
+
+ bool ok = false;
+ __u32 tp_status = 0;
+ __u16 tp_net = 0;
+
+ for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
+ if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) {
+ struct tpacket_auxdata *aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg);
+ ok = true;
+ tp_status = aux->tp_status;
+ tp_net = aux->tp_net;
+ break;
+ }
+ }
+
+ if (!ok) {
+ // theoretically this should not happen...
+ static bool logged = false;
+ if (!logged) {
+ logmsg(ANDROID_LOG_ERROR, "%s: failed to fetch tpacket_auxdata cmsg", __func__);
+ logged = true;
+ }
+ }
+
+ const int payload_offset = offsetof(typeof(buf), payload);
+ if (readlen < payload_offset + tp_net) {
+ logmsg(ANDROID_LOG_WARN, "%s: ignoring %zd byte pkt shorter than %d+%u L2 header",
+ __func__, readlen, payload_offset, tp_net);
+ return;
+ }
+
+ const int pkt_len = readlen - payload_offset;
+
+ // This will detect a skb->ip_summed == CHECKSUM_PARTIAL packet with non-final L4 checksum
+ if (tp_status & TP_STATUS_CSUMNOTREADY) {
+ static bool logged = false;
+ if (!logged) {
+ logmsg(ANDROID_LOG_WARN, "%s: L4 checksum calculation required", __func__);
+ logged = true;
+ }
+
+ // These are non-negative by virtue of csum_start/offset being u16
+ const int cs_start = buf.vnet.csum_start;
+ const int cs_offset = cs_start + buf.vnet.csum_offset;
+ if (cs_start > pkt_len) {
+ logmsg(ANDROID_LOG_ERROR, "%s: out of range - checksum start %d > %d",
+ __func__, cs_start, pkt_len);
+ } else if (cs_offset + 1 >= pkt_len) {
+ logmsg(ANDROID_LOG_ERROR, "%s: out of range - checksum offset %d + 1 >= %d",
+ __func__, cs_offset, pkt_len);
+ } else {
+ uint16_t csum = ip_checksum(buf.payload + cs_start, pkt_len - cs_start);
+ if (!csum) csum = 0xFFFF; // required fixup for UDP, TCP must live with it
+ buf.payload[cs_offset] = csum & 0xFF;
+ buf.payload[cs_offset + 1] = csum >> 8;
+ }
+ }
+
+ translate_packet(tunnel->fd4, 0 /* to_ipv6 */, buf.payload + tp_net, pkt_len - tp_net);
+}
+
+// reads TUN_PI + L3 IPv4 packet from tun, translates to IPv6, writes to AF_INET6/RAW socket
+void process_packet_4_to_6(struct tun_data *tunnel) {
+ struct {
+ struct tun_pi pi;
+ uint8_t payload[MAXMTU];
+ char pad; // +1 byte to make packet truncation obvious
+ } buf;
+ ssize_t readlen = read(tunnel->fd4, &buf, sizeof(buf));
+
+ if (readlen < 0) {
+ if (errno != EAGAIN) {
+ logmsg(ANDROID_LOG_WARN, "%s: read error: %s", __func__, strerror(errno));
+ }
+ return;
+ } else if (readlen == 0) {
+ logmsg(ANDROID_LOG_WARN, "%s: tun interface removed", __func__);
+ running = 0;
+ return;
+ } else if (readlen >= sizeof(buf)) {
+ logmsg(ANDROID_LOG_WARN, "%s: read truncation - ignoring pkt", __func__);
+ return;
+ }
+
+ const int payload_offset = offsetof(typeof(buf), payload);
+
+ if (readlen < payload_offset) {
+ logmsg(ANDROID_LOG_WARN, "%s: short read: got %ld bytes", __func__, readlen);
+ return;
+ }
+
+ const int pkt_len = readlen - payload_offset;
+
+ uint16_t proto = ntohs(buf.pi.proto);
+ if (proto != ETH_P_IP) {
+ logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto);
+ return;
+ }
+
+ if (buf.pi.flags != 0) {
+ logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, buf.pi.flags);
+ }
+
+ translate_packet(tunnel->write_fd6, 1 /* to_ipv6 */, buf.payload, pkt_len);
+}
+
+// IPv6 DAD packet format:
+// Ethernet header (if needed) will be added by the kernel:
+// u8[6] src_mac; u8[6] dst_mac '33:33:ff:XX:XX:XX'; be16 ethertype '0x86DD'
+// IPv6 header:
+// be32 0x60000000 - ipv6, tclass 0, flowlabel 0
+// be16 payload_length '32'; u8 nxt_hdr ICMPv6 '58'; u8 hop limit '255'
+// u128 src_ip6 '::'
+// u128 dst_ip6 'ff02::1:ffXX:XXXX'
+// ICMPv6 header:
+// u8 type '135'; u8 code '0'; u16 icmp6 checksum; u32 reserved '0'
+// ICMPv6 neighbour solicitation payload:
+// u128 tgt_ip6
+// ICMPv6 ND options:
+// u8 opt nr '14'; u8 length '1'; u8[6] nonce '6 random bytes'
+void send_dad(int fd, const struct in6_addr* tgt) {
+ struct {
+ struct ip6_hdr ip6h;
+ struct nd_neighbor_solicit ns;
+ uint8_t ns_opt_nr;
+ uint8_t ns_opt_len;
+ uint8_t ns_opt_nonce[6];
+ } dad_pkt = {
+ .ip6h = {
+ .ip6_flow = htonl(6 << 28), // v6, 0 tclass, 0 flowlabel
+ .ip6_plen = htons(sizeof(dad_pkt) - sizeof(struct ip6_hdr)), // payload length, ie. 32
+ .ip6_nxt = IPPROTO_ICMPV6, // 58
+ .ip6_hlim = 255,
+ .ip6_src = {}, // ::
+ .ip6_dst.s6_addr = {
+ 0xFF, 0x02, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 1,
+ 0xFF, tgt->s6_addr[13], tgt->s6_addr[14], tgt->s6_addr[15],
+ }, // ff02::1:ffXX:XXXX - multicast group address derived from bottom 24-bits of tgt
+ },
+ .ns = {
+ .nd_ns_type = ND_NEIGHBOR_SOLICIT, // 135
+ .nd_ns_code = 0,
+ .nd_ns_cksum = 0, // will be calculated later
+ .nd_ns_reserved = 0,
+ .nd_ns_target = *tgt,
+ },
+ .ns_opt_nr = 14, // icmp6 option 'nonce' from RFC3971
+ .ns_opt_len = 1, // in units of 8 bytes, including option nr and len
+ .ns_opt_nonce = {}, // opt_len *8 - sizeof u8(opt_nr) - sizeof u8(opt_len) = 6 ranodmized bytes
+ };
+ arc4random_buf(&dad_pkt.ns_opt_nonce, sizeof(dad_pkt.ns_opt_nonce));
+
+ // 40 byte IPv6 header + 8 byte ICMPv6 header + 16 byte ipv6 target address + 8 byte nonce option
+ _Static_assert(sizeof(dad_pkt) == 40 + 8 + 16 + 8, "sizeof dad packet != 72");
+
+ // IPv6 header checksum is standard negated 16-bit one's complement sum over the icmpv6 pseudo
+ // header (which includes payload length, nextheader, and src/dst ip) and the icmpv6 payload.
+ //
+ // Src/dst ip immediately prefix the icmpv6 header itself, so can be handled along
+ // with the payload. We thus only need to manually account for payload len & next header.
+ //
+ // The magic '8' is simply the offset of the ip6_src field in the ipv6 header,
+ // ie. we're skipping over the ipv6 version, tclass, flowlabel, payload length, next header
+ // and hop limit fields, because they're not quite where we want them to be.
+ //
+ // ip6_plen is already in network order, while ip6_nxt is a single byte and thus needs htons().
+ uint32_t csum = dad_pkt.ip6h.ip6_plen + htons(dad_pkt.ip6h.ip6_nxt);
+ csum = ip_checksum_add(csum, &dad_pkt.ip6h.ip6_src, sizeof(dad_pkt) - 8);
+ dad_pkt.ns.nd_ns_cksum = ip_checksum_finish(csum);
+
+ const struct sockaddr_in6 dst = {
+ .sin6_family = AF_INET6,
+ .sin6_addr = dad_pkt.ip6h.ip6_dst,
+ .sin6_scope_id = if_nametoindex(Global_Clatd_Config.native_ipv6_interface),
+ };
+
+ sendto(fd, &dad_pkt, sizeof(dad_pkt), 0 /*flags*/, (const struct sockaddr *)&dst, sizeof(dst));
+}
+
+/* function: event_loop
+ * reads packets from the tun network interface and passes them down the stack
+ * tunnel - tun device data
+ */
+void event_loop(struct tun_data *tunnel) {
+ // Apparently some network gear will refuse to perform NS for IPs that aren't DAD'ed,
+ // this would then result in an ipv6-only network with working native ipv6, working
+ // IPv4 via DNS64, but non-functioning IPv4 via CLAT (ie. IPv4 literals + IPv4 only apps).
+ // The kernel itself doesn't do DAD for anycast ips (but does handle IPV6 MLD and handle ND).
+ // So we'll spoof dad here, and yeah, we really should check for a response and in
+ // case of failure pick a different IP. Seeing as 48-bits of the IP are utterly random
+ // (with the other 16 chosen to guarantee checksum neutrality) this seems like a remote
+ // concern...
+ // TODO: actually perform true DAD
+ send_dad(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet);
+
+ struct pollfd wait_fd[] = {
+ { tunnel->read_fd6, POLLIN, 0 },
+ { tunnel->fd4, POLLIN, 0 },
+ };
+
+ while (running) {
+ if (poll(wait_fd, ARRAY_SIZE(wait_fd), -1) == -1) {
+ if (errno != EINTR) {
+ logmsg(ANDROID_LOG_WARN, "event_loop/poll returned an error: %s", strerror(errno));
+ }
+ } else {
+ // Call process_packet if the socket has data to be read, but also if an
+ // error is waiting. If we don't call read() after getting POLLERR, a
+ // subsequent poll() will return immediately with POLLERR again,
+ // causing this code to spin in a loop. Calling read() will clear the
+ // socket error flag instead.
+ if (wait_fd[0].revents) process_packet_6_to_4(tunnel);
+ if (wait_fd[1].revents) process_packet_4_to_6(tunnel);
+ }
+ }
+}
diff --git a/clatd/clatd.h b/clatd/clatd.h
new file mode 100644
index 0000000..e170c58
--- /dev/null
+++ b/clatd/clatd.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * clatd.h - main routines used by clatd
+ */
+#ifndef __CLATD_H__
+#define __CLATD_H__
+
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/uio.h>
+
+struct tun_data;
+
+// IPv4 header has a u16 total length field, for maximum L3 mtu of 0xFFFF.
+//
+// Translating IPv4 to IPv6 requires removing the IPv4 header (20) and adding
+// an IPv6 header (40), possibly with an extra ipv6 fragment extension header (8).
+//
+// As such the maximum IPv4 L3 mtu size is 0xFFFF (by u16 tot_len field)
+// and the maximum IPv6 L3 mtu size is 0xFFFF + 28 (which is larger)
+//
+// A received non-jumbogram IPv6 frame could potentially be u16 payload_len = 0xFFFF
+// + sizeof ipv6 header = 40, bytes in size. But such a packet cannot be meaningfully
+// converted to IPv4 (it's too large). As such the restriction is the same: 0xFFFF + 28
+//
+// (since there's no jumbogram support in IPv4, IPv6 jumbograms cannot be meaningfully
+// converted to IPv4 anyway, and are thus entirely unsupported)
+#define MAXMTU (0xFFFF + 28)
+
+// logcat_hexdump() maximum binary data length, this is the maximum packet size
+// plus some extra space for various headers:
+// struct tun_pi (4 bytes)
+// struct virtio_net_hdr (10 bytes)
+// ethernet (14 bytes), potentially including vlan tag (4) or tags (8 or 12)
+// plus some extra just-in-case headroom, because it doesn't hurt.
+#define MAXDUMPLEN (64 + MAXMTU)
+
+#define CLATD_VERSION "1.7"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+extern volatile sig_atomic_t running;
+
+void event_loop(struct tun_data *tunnel);
+
+/* function: parse_int
+ * parses a string as a decimal/hex/octal signed integer
+ * str - the string to parse
+ * out - the signed integer to write to, gets clobbered on failure
+ */
+static inline int parse_int(const char *str, int *out) {
+ char *end_ptr;
+ *out = strtol(str, &end_ptr, 0);
+ return *str && !*end_ptr;
+}
+
+/* function: parse_unsigned
+ * parses a string as a decimal/hex/octal unsigned integer
+ * str - the string to parse
+ * out - the unsigned integer to write to, gets clobbered on failure
+ */
+static inline int parse_unsigned(const char *str, unsigned *out) {
+ char *end_ptr;
+ *out = strtoul(str, &end_ptr, 0);
+ return *str && !*end_ptr;
+}
+
+#endif /* __CLATD_H__ */
diff --git a/clatd/clatd_test.cpp b/clatd/clatd_test.cpp
new file mode 100644
index 0000000..0ed5f28
--- /dev/null
+++ b/clatd/clatd_test.cpp
@@ -0,0 +1,835 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * clatd_test.cpp - unit tests for clatd
+ */
+
+#include <iostream>
+
+#include <arpa/inet.h>
+#include <linux/if_packet.h>
+#include <netinet/in6.h>
+#include <stdio.h>
+#include <sys/uio.h>
+
+#include <gtest/gtest.h>
+
+#include "netutils/ifc.h"
+#include "tun_interface.h"
+
+extern "C" {
+#include "checksum.h"
+#include "clatd.h"
+#include "config.h"
+#include "translate.h"
+}
+
+// For convenience.
+#define ARRAYSIZE(x) sizeof((x)) / sizeof((x)[0])
+
+using android::net::TunInterface;
+
+// Default translation parameters.
+static const char kIPv4LocalAddr[] = "192.0.0.4";
+static const char kIPv6LocalAddr[] = "2001:db8:0:b11::464";
+static const char kIPv6PlatSubnet[] = "64:ff9b::";
+
+// clang-format off
+// Test packet portions. Defined as macros because it's easy to concatenate them to make packets.
+#define IPV4_HEADER(p, c1, c2) \
+ 0x45, 0x00, 0, 41, /* Version=4, IHL=5, ToS=0x80, len=41 */ \
+ 0x00, 0x00, 0x40, 0x00, /* ID=0x0000, flags=IP_DF, offset=0 */ \
+ 55, (p), (c1), (c2), /* TTL=55, protocol=p, checksum=c1,c2 */ \
+ 192, 0, 0, 4, /* Src=192.0.0.4 */ \
+ 8, 8, 8, 8, /* Dst=8.8.8.8 */
+#define IPV4_UDP_HEADER IPV4_HEADER(IPPROTO_UDP, 0x73, 0xb0)
+#define IPV4_ICMP_HEADER IPV4_HEADER(IPPROTO_ICMP, 0x73, 0xc0)
+
+#define IPV6_HEADER(p) \
+ 0x60, 0x00, 0, 0, /* Version=6, tclass=0x00, flowlabel=0 */ \
+ 0, 21, (p), 55, /* plen=11, nxthdr=p, hlim=55 */ \
+ 0x20, 0x01, 0x0d, 0xb8, /* Src=2001:db8:0:b11::464 */ \
+ 0x00, 0x00, 0x0b, 0x11, \
+ 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x04, 0x64, \
+ 0x00, 0x64, 0xff, 0x9b, /* Dst=64:ff9b::8.8.8.8 */ \
+ 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, \
+ 0x08, 0x08, 0x08, 0x08,
+#define IPV6_UDP_HEADER IPV6_HEADER(IPPROTO_UDP)
+#define IPV6_ICMPV6_HEADER IPV6_HEADER(IPPROTO_ICMPV6)
+
+#define UDP_LEN 21
+#define UDP_HEADER \
+ 0xc8, 0x8b, 0, 53, /* Port 51339->53 */ \
+ 0x00, UDP_LEN, 0, 0, /* Length 21, checksum empty for now */
+
+#define PAYLOAD 'H', 'e', 'l', 'l', 'o', ' ', 0x4e, 0xb8, 0x96, 0xe7, 0x95, 0x8c, 0x00
+
+#define IPV4_PING \
+ 0x08, 0x00, 0x88, 0xd0, /* Type 8, code 0, checksum 0x88d0 */ \
+ 0xd0, 0x0d, 0x00, 0x03, /* ID=0xd00d, seq=3 */
+
+#define IPV6_PING \
+ 0x80, 0x00, 0xc3, 0x42, /* Type 128, code 0, checksum 0xc342 */ \
+ 0xd0, 0x0d, 0x00, 0x03, /* ID=0xd00d, seq=3 */
+
+// Macros to return pseudo-headers from packets.
+#define IPV4_PSEUDOHEADER(ip, tlen) \
+ ip[12], ip[13], ip[14], ip[15], /* Source address */ \
+ ip[16], ip[17], ip[18], ip[19], /* Destination address */ \
+ 0, ip[9], /* 0, protocol */ \
+ ((tlen) >> 16) & 0xff, (tlen) & 0xff, /* Transport length */
+
+#define IPV6_PSEUDOHEADER(ip6, protocol, tlen) \
+ ip6[8], ip6[9], ip6[10], ip6[11], /* Source address */ \
+ ip6[12], ip6[13], ip6[14], ip6[15], \
+ ip6[16], ip6[17], ip6[18], ip6[19], \
+ ip6[20], ip6[21], ip6[22], ip6[23], \
+ ip6[24], ip6[25], ip6[26], ip6[27], /* Destination address */ \
+ ip6[28], ip6[29], ip6[30], ip6[31], \
+ ip6[32], ip6[33], ip6[34], ip6[35], \
+ ip6[36], ip6[37], ip6[38], ip6[39], \
+ ((tlen) >> 24) & 0xff, /* Transport length */ \
+ ((tlen) >> 16) & 0xff, \
+ ((tlen) >> 8) & 0xff, \
+ (tlen) & 0xff, \
+ 0, 0, 0, (protocol),
+
+// A fragmented DNS request.
+static const uint8_t kIPv4Frag1[] = {
+ 0x45, 0x00, 0x00, 0x24, 0xfe, 0x47, 0x20, 0x00, 0x40, 0x11,
+ 0x8c, 0x6d, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08,
+ 0x14, 0x5d, 0x00, 0x35, 0x00, 0x29, 0x68, 0xbb, 0x50, 0x47,
+ 0x01, 0x00, 0x00, 0x01, 0x00, 0x00
+};
+static const uint8_t kIPv4Frag2[] = {
+ 0x45, 0x00, 0x00, 0x24, 0xfe, 0x47, 0x20, 0x02, 0x40, 0x11,
+ 0x8c, 0x6b, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x69, 0x70, 0x76, 0x34, 0x06,
+ 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65
+};
+static const uint8_t kIPv4Frag3[] = {
+ 0x45, 0x00, 0x00, 0x1d, 0xfe, 0x47, 0x00, 0x04, 0x40, 0x11,
+ 0xac, 0x70, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01
+};
+static const uint8_t *kIPv4Fragments[] = { kIPv4Frag1, kIPv4Frag2, kIPv4Frag3 };
+static const size_t kIPv4FragLengths[] = { sizeof(kIPv4Frag1), sizeof(kIPv4Frag2),
+ sizeof(kIPv4Frag3) };
+
+static const uint8_t kIPv6Frag1[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x2c, 0x40, 0x20, 0x01,
+ 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08,
+ 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0xfe, 0x47, 0x14, 0x5d,
+ 0x00, 0x35, 0x00, 0x29, 0xeb, 0x91, 0x50, 0x47, 0x01, 0x00,
+ 0x00, 0x01, 0x00, 0x00
+};
+
+static const uint8_t kIPv6Frag2[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x2c, 0x40, 0x20, 0x01,
+ 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08,
+ 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0xfe, 0x47, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x69, 0x70, 0x76, 0x34, 0x06, 0x67, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65
+};
+
+static const uint8_t kIPv6Frag3[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x11, 0x2c, 0x40, 0x20, 0x01,
+ 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08,
+ 0x11, 0x00, 0x00, 0x20, 0x00, 0x00, 0xfe, 0x47, 0x03, 0x63,
+ 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01
+};
+static const uint8_t *kIPv6Fragments[] = { kIPv6Frag1, kIPv6Frag2, kIPv6Frag3 };
+static const size_t kIPv6FragLengths[] = { sizeof(kIPv6Frag1), sizeof(kIPv6Frag2),
+ sizeof(kIPv6Frag3) };
+
+static const uint8_t kReassembledIPv4[] = {
+ 0x45, 0x00, 0x00, 0x3d, 0xfe, 0x47, 0x00, 0x00, 0x40, 0x11,
+ 0xac, 0x54, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08,
+ 0x14, 0x5d, 0x00, 0x35, 0x00, 0x29, 0x68, 0xbb, 0x50, 0x47,
+ 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x69, 0x70, 0x76, 0x34, 0x06, 0x67, 0x6f, 0x6f, 0x67,
+ 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00,
+ 0x01
+};
+// clang-format on
+
+// Expected checksums.
+static const uint32_t kUdpPartialChecksum = 0xd5c8;
+static const uint32_t kPayloadPartialChecksum = 0x31e9c;
+static const uint16_t kUdpV4Checksum = 0xd0c7;
+static const uint16_t kUdpV6Checksum = 0xa74a;
+
+uint8_t ip_version(const uint8_t *packet) {
+ uint8_t version = packet[0] >> 4;
+ return version;
+}
+
+int is_ipv4_fragment(struct iphdr *ip) {
+ // A packet is a fragment if its fragment offset is nonzero or if the MF flag is set.
+ return ntohs(ip->frag_off) & (IP_OFFMASK | IP_MF);
+}
+
+int is_ipv6_fragment(struct ip6_hdr *ip6, size_t len) {
+ if (ip6->ip6_nxt != IPPROTO_FRAGMENT) {
+ return 0;
+ }
+ struct ip6_frag *frag = (struct ip6_frag *)(ip6 + 1);
+ return len >= sizeof(*ip6) + sizeof(*frag) &&
+ (frag->ip6f_offlg & (IP6F_OFF_MASK | IP6F_MORE_FRAG));
+}
+
+int ipv4_fragment_offset(struct iphdr *ip) {
+ return ntohs(ip->frag_off) & IP_OFFMASK;
+}
+
+int ipv6_fragment_offset(struct ip6_frag *frag) {
+ return ntohs((frag->ip6f_offlg & IP6F_OFF_MASK) >> 3);
+}
+
+void check_packet(const uint8_t *packet, size_t len, const char *msg) {
+ void *payload;
+ size_t payload_length = 0;
+ uint32_t pseudo_checksum = 0;
+ uint8_t protocol = 0;
+ int version = ip_version(packet);
+ switch (version) {
+ case 4: {
+ struct iphdr *ip = (struct iphdr *)packet;
+ ASSERT_GE(len, sizeof(*ip)) << msg << ": IPv4 packet shorter than IPv4 header\n";
+ EXPECT_EQ(5, ip->ihl) << msg << ": Unsupported IP header length\n";
+ EXPECT_EQ(len, ntohs(ip->tot_len)) << msg << ": Incorrect IPv4 length\n";
+ EXPECT_EQ(0, ip_checksum(ip, sizeof(*ip))) << msg << ": Incorrect IP checksum\n";
+ protocol = ip->protocol;
+ payload = ip + 1;
+ if (!is_ipv4_fragment(ip)) {
+ payload_length = len - sizeof(*ip);
+ pseudo_checksum = ipv4_pseudo_header_checksum(ip, payload_length);
+ }
+ ASSERT_TRUE(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || protocol == IPPROTO_ICMP)
+ << msg << ": Unsupported IPv4 protocol " << protocol << "\n";
+ break;
+ }
+ case 6: {
+ struct ip6_hdr *ip6 = (struct ip6_hdr *)packet;
+ ASSERT_GE(len, sizeof(*ip6)) << msg << ": IPv6 packet shorter than IPv6 header\n";
+ EXPECT_EQ(len - sizeof(*ip6), htons(ip6->ip6_plen)) << msg << ": Incorrect IPv6 length\n";
+
+ if (ip6->ip6_nxt == IPPROTO_FRAGMENT) {
+ struct ip6_frag *frag = (struct ip6_frag *)(ip6 + 1);
+ ASSERT_GE(len, sizeof(*ip6) + sizeof(*frag))
+ << msg << ": IPv6 fragment: short fragment header\n";
+ protocol = frag->ip6f_nxt;
+ payload = frag + 1;
+ // Even though the packet has a Fragment header, it might not be a fragment.
+ if (!is_ipv6_fragment(ip6, len)) {
+ payload_length = len - sizeof(*ip6) - sizeof(*frag);
+ }
+ } else {
+ // Since there are no extension headers except Fragment, this must be the payload.
+ protocol = ip6->ip6_nxt;
+ payload = ip6 + 1;
+ payload_length = len - sizeof(*ip6);
+ }
+ ASSERT_TRUE(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || protocol == IPPROTO_ICMPV6)
+ << msg << ": Unsupported IPv6 next header " << protocol;
+ if (payload_length) {
+ pseudo_checksum = ipv6_pseudo_header_checksum(ip6, payload_length, protocol);
+ }
+ break;
+ }
+ default:
+ FAIL() << msg << ": Unsupported IP version " << version << "\n";
+ return;
+ }
+
+ // If we understand the payload, verify the checksum.
+ if (payload_length) {
+ uint16_t checksum;
+ switch (protocol) {
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_ICMPV6:
+ checksum = ip_checksum_finish(ip_checksum_add(pseudo_checksum, payload, payload_length));
+ break;
+ case IPPROTO_ICMP:
+ checksum = ip_checksum(payload, payload_length);
+ break;
+ default:
+ checksum = 0; // Don't check.
+ break;
+ }
+ EXPECT_EQ(0, checksum) << msg << ": Incorrect transport checksum\n";
+ }
+
+ if (protocol == IPPROTO_UDP) {
+ struct udphdr *udp = (struct udphdr *)payload;
+ EXPECT_NE(0, udp->check) << msg << ": UDP checksum 0 should be 0xffff";
+ // If this is not a fragment, check the UDP length field.
+ if (payload_length) {
+ EXPECT_EQ(payload_length, ntohs(udp->len)) << msg << ": Incorrect UDP length\n";
+ }
+ }
+}
+
+void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int numpackets,
+ uint8_t *reassembled, size_t *reassembled_len, const char *msg) {
+ struct iphdr *ip = nullptr;
+ struct ip6_hdr *ip6 = nullptr;
+ size_t total_length, pos = 0;
+ uint8_t protocol = 0;
+ uint8_t version = ip_version(fragments[0]);
+
+ for (int i = 0; i < numpackets; i++) {
+ const uint8_t *packet = fragments[i];
+ int len = lengths[i];
+ int headersize, payload_offset;
+
+ ASSERT_EQ(ip_version(packet), version) << msg << ": Inconsistent fragment versions\n";
+ check_packet(packet, len, "Fragment sanity check");
+
+ switch (version) {
+ case 4: {
+ struct iphdr *ip_orig = (struct iphdr *)packet;
+ headersize = sizeof(*ip_orig);
+ ASSERT_TRUE(is_ipv4_fragment(ip_orig))
+ << msg << ": IPv4 fragment #" << i + 1 << " not a fragment\n";
+ ASSERT_EQ(pos, ipv4_fragment_offset(ip_orig) * 8 + ((i != 0) ? sizeof(*ip) : 0))
+ << msg << ": IPv4 fragment #" << i + 1 << ": inconsistent offset\n";
+
+ headersize = sizeof(*ip_orig);
+ payload_offset = headersize;
+ if (pos == 0) {
+ ip = (struct iphdr *)reassembled;
+ }
+ break;
+ }
+ case 6: {
+ struct ip6_hdr *ip6_orig = (struct ip6_hdr *)packet;
+ struct ip6_frag *frag = (struct ip6_frag *)(ip6_orig + 1);
+ ASSERT_TRUE(is_ipv6_fragment(ip6_orig, len))
+ << msg << ": IPv6 fragment #" << i + 1 << " not a fragment\n";
+ ASSERT_EQ(pos, ipv6_fragment_offset(frag) * 8 + ((i != 0) ? sizeof(*ip6) : 0))
+ << msg << ": IPv6 fragment #" << i + 1 << ": inconsistent offset\n";
+
+ headersize = sizeof(*ip6_orig);
+ payload_offset = sizeof(*ip6_orig) + sizeof(*frag);
+ if (pos == 0) {
+ ip6 = (struct ip6_hdr *)reassembled;
+ protocol = frag->ip6f_nxt;
+ }
+ break;
+ }
+ default:
+ FAIL() << msg << ": Invalid IP version << " << version;
+ }
+
+ // If this is the first fragment, copy the header.
+ if (pos == 0) {
+ ASSERT_LT(headersize, (int)*reassembled_len) << msg << ": Reassembly buffer too small\n";
+ memcpy(reassembled, packet, headersize);
+ total_length = headersize;
+ pos += headersize;
+ }
+
+ // Copy the payload.
+ int payload_length = len - payload_offset;
+ total_length += payload_length;
+ ASSERT_LT(total_length, *reassembled_len) << msg << ": Reassembly buffer too small\n";
+ memcpy(reassembled + pos, packet + payload_offset, payload_length);
+ pos += payload_length;
+ }
+
+ // Fix up the reassembled headers to reflect fragmentation and length (and IPv4 checksum).
+ ASSERT_EQ(total_length, pos) << msg << ": Reassembled packet length incorrect\n";
+ if (ip) {
+ ip->frag_off &= ~htons(IP_MF);
+ ip->tot_len = htons(total_length);
+ ip->check = 0;
+ ip->check = ip_checksum(ip, sizeof(*ip));
+ ASSERT_FALSE(is_ipv4_fragment(ip)) << msg << ": reassembled IPv4 packet is a fragment!\n";
+ }
+ if (ip6) {
+ ip6->ip6_nxt = protocol;
+ ip6->ip6_plen = htons(total_length - sizeof(*ip6));
+ ASSERT_FALSE(is_ipv6_fragment(ip6, ip6->ip6_plen))
+ << msg << ": reassembled IPv6 packet is a fragment!\n";
+ }
+
+ *reassembled_len = total_length;
+}
+
+void check_data_matches(const void *expected, const void *actual, size_t len, const char *msg) {
+ if (memcmp(expected, actual, len)) {
+ // Hex dump, 20 bytes per line, one space between bytes (1 byte = 3 chars), indented by 4.
+ int hexdump_len = len * 3 + (len / 20 + 1) * 5;
+ char expected_hexdump[hexdump_len], actual_hexdump[hexdump_len];
+ unsigned pos = 0;
+ for (unsigned i = 0; i < len; i++) {
+ if (i % 20 == 0) {
+ snprintf(expected_hexdump + pos, hexdump_len - pos, "\n ");
+ snprintf(actual_hexdump + pos, hexdump_len - pos, "\n ");
+ pos += 4;
+ }
+ snprintf(expected_hexdump + pos, hexdump_len - pos, " %02x", ((uint8_t *)expected)[i]);
+ snprintf(actual_hexdump + pos, hexdump_len - pos, " %02x", ((uint8_t *)actual)[i]);
+ pos += 3;
+ }
+ FAIL() << msg << ": Data doesn't match"
+ << "\n Expected:" << (char *) expected_hexdump
+ << "\n Actual:" << (char *) actual_hexdump << "\n";
+ }
+}
+
+void fix_udp_checksum(uint8_t *packet) {
+ uint32_t pseudo_checksum;
+ uint8_t version = ip_version(packet);
+ struct udphdr *udp;
+ switch (version) {
+ case 4: {
+ struct iphdr *ip = (struct iphdr *)packet;
+ udp = (struct udphdr *)(ip + 1);
+ pseudo_checksum = ipv4_pseudo_header_checksum(ip, ntohs(udp->len));
+ break;
+ }
+ case 6: {
+ struct ip6_hdr *ip6 = (struct ip6_hdr *)packet;
+ udp = (struct udphdr *)(ip6 + 1);
+ pseudo_checksum = ipv6_pseudo_header_checksum(ip6, ntohs(udp->len), IPPROTO_UDP);
+ break;
+ }
+ default:
+ FAIL() << "unsupported IP version" << version << "\n";
+ return;
+ }
+
+ udp->check = 0;
+ udp->check = ip_checksum_finish(ip_checksum_add(pseudo_checksum, udp, ntohs(udp->len)));
+}
+
+// Testing stub for send_rawv6. The real version uses sendmsg() with a
+// destination IPv6 address, and attempting to call that on our test socketpair
+// fd results in EINVAL.
+extern "C" void send_rawv6(int fd, clat_packet out, int iov_len) { writev(fd, out, iov_len); }
+
+void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t *out, size_t *outlen,
+ const char *msg) {
+ int fds[2];
+ if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, fds)) {
+ abort();
+ }
+
+ char foo[512];
+ snprintf(foo, sizeof(foo), "%s: Invalid original packet", msg);
+ check_packet(original, original_len, foo);
+
+ int read_fd, write_fd;
+ uint16_t expected_proto;
+ int version = ip_version(original);
+ switch (version) {
+ case 4:
+ expected_proto = htons(ETH_P_IPV6);
+ read_fd = fds[1];
+ write_fd = fds[0];
+ break;
+ case 6:
+ expected_proto = htons(ETH_P_IP);
+ read_fd = fds[0];
+ write_fd = fds[1];
+ break;
+ default:
+ FAIL() << msg << ": Unsupported IP version " << version << "\n";
+ break;
+ }
+
+ translate_packet(write_fd, (version == 4), original, original_len);
+
+ snprintf(foo, sizeof(foo), "%s: Invalid translated packet", msg);
+ if (version == 6) {
+ // Translating to IPv4. Expect a tun header.
+ struct tun_pi new_tun_header;
+ struct iovec iov[] = {
+ { &new_tun_header, sizeof(new_tun_header) },
+ { out, *outlen },
+ };
+
+ int len = readv(read_fd, iov, 2);
+ if (len > (int)sizeof(new_tun_header)) {
+ ASSERT_LT((size_t)len, *outlen) << msg << ": Translated packet buffer too small\n";
+ EXPECT_EQ(expected_proto, new_tun_header.proto) << msg << "Unexpected tun proto\n";
+ *outlen = len - sizeof(new_tun_header);
+ check_packet(out, *outlen, msg);
+ } else {
+ FAIL() << msg << ": Packet was not translated: len=" << len;
+ *outlen = 0;
+ }
+ } else {
+ // Translating to IPv6. Expect raw packet.
+ *outlen = read(read_fd, out, *outlen);
+ check_packet(out, *outlen, msg);
+ }
+}
+
+void check_translated_packet(const uint8_t *original, size_t original_len, const uint8_t *expected,
+ size_t expected_len, const char *msg) {
+ uint8_t translated[MAXMTU];
+ size_t translated_len = sizeof(translated);
+ do_translate_packet(original, original_len, translated, &translated_len, msg);
+ EXPECT_EQ(expected_len, translated_len) << msg << ": Translated packet length incorrect\n";
+ check_data_matches(expected, translated, translated_len, msg);
+}
+
+void check_fragment_translation(const uint8_t *original[], const size_t original_lengths[],
+ const uint8_t *expected[], const size_t expected_lengths[],
+ int numfragments, const char *msg) {
+ for (int i = 0; i < numfragments; i++) {
+ // Check that each of the fragments translates as expected.
+ char frag_msg[512];
+ snprintf(frag_msg, sizeof(frag_msg), "%s: fragment #%d", msg, i + 1);
+ check_translated_packet(original[i], original_lengths[i], expected[i], expected_lengths[i],
+ frag_msg);
+ }
+
+ // Sanity check that reassembling the original and translated fragments produces valid packets.
+ uint8_t reassembled[MAXMTU];
+ size_t reassembled_len = sizeof(reassembled);
+ reassemble_packet(original, original_lengths, numfragments, reassembled, &reassembled_len, msg);
+ check_packet(reassembled, reassembled_len, msg);
+
+ uint8_t translated[MAXMTU];
+ size_t translated_len = sizeof(translated);
+ do_translate_packet(reassembled, reassembled_len, translated, &translated_len, msg);
+ check_packet(translated, translated_len, msg);
+}
+
+int get_transport_checksum(const uint8_t *packet) {
+ struct iphdr *ip;
+ struct ip6_hdr *ip6;
+ uint8_t protocol;
+ const void *payload;
+
+ int version = ip_version(packet);
+ switch (version) {
+ case 4:
+ ip = (struct iphdr *)packet;
+ if (is_ipv4_fragment(ip)) {
+ return -1;
+ }
+ protocol = ip->protocol;
+ payload = ip + 1;
+ break;
+ case 6:
+ ip6 = (struct ip6_hdr *)packet;
+ protocol = ip6->ip6_nxt;
+ payload = ip6 + 1;
+ break;
+ default:
+ return -1;
+ }
+
+ switch (protocol) {
+ case IPPROTO_UDP:
+ return ((struct udphdr *)payload)->check;
+
+ case IPPROTO_TCP:
+ return ((struct tcphdr *)payload)->check;
+
+ case IPPROTO_FRAGMENT:
+ default:
+ return -1;
+ }
+}
+
+class ClatdTest : public ::testing::Test {
+ protected:
+ static TunInterface sTun;
+
+ virtual void SetUp() {
+ inet_pton(AF_INET, kIPv4LocalAddr, &Global_Clatd_Config.ipv4_local_subnet);
+ inet_pton(AF_INET6, kIPv6PlatSubnet, &Global_Clatd_Config.plat_subnet);
+ memset(&Global_Clatd_Config.ipv6_local_subnet, 0, sizeof(in6_addr));
+ Global_Clatd_Config.native_ipv6_interface = const_cast<char *>(sTun.name().c_str());
+ }
+
+ // Static because setting up the tun interface takes about 40ms.
+ static void SetUpTestCase() { ASSERT_EQ(0, sTun.init()); }
+
+ // Closing the socket removes the interface and IP addresses.
+ static void TearDownTestCase() { sTun.destroy(); }
+};
+
+TunInterface ClatdTest::sTun;
+
+void expect_ipv6_addr_equal(struct in6_addr *expected, struct in6_addr *actual) {
+ if (!IN6_ARE_ADDR_EQUAL(expected, actual)) {
+ char expected_str[INET6_ADDRSTRLEN], actual_str[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, expected, expected_str, sizeof(expected_str));
+ inet_ntop(AF_INET6, actual, actual_str, sizeof(actual_str));
+ FAIL()
+ << "Unexpected IPv6 address:: "
+ << "\n Expected: " << expected_str
+ << "\n Actual: " << actual_str
+ << "\n";
+ }
+}
+
+TEST_F(ClatdTest, TestIPv6PrefixEqual) {
+ EXPECT_TRUE(ipv6_prefix_equal(&Global_Clatd_Config.plat_subnet,
+ &Global_Clatd_Config.plat_subnet));
+ EXPECT_FALSE(ipv6_prefix_equal(&Global_Clatd_Config.plat_subnet,
+ &Global_Clatd_Config.ipv6_local_subnet));
+
+ struct in6_addr subnet2 = Global_Clatd_Config.ipv6_local_subnet;
+ EXPECT_TRUE(ipv6_prefix_equal(&Global_Clatd_Config.ipv6_local_subnet, &subnet2));
+ EXPECT_TRUE(ipv6_prefix_equal(&subnet2, &Global_Clatd_Config.ipv6_local_subnet));
+
+ subnet2.s6_addr[6] = 0xff;
+ EXPECT_FALSE(ipv6_prefix_equal(&Global_Clatd_Config.ipv6_local_subnet, &subnet2));
+ EXPECT_FALSE(ipv6_prefix_equal(&subnet2, &Global_Clatd_Config.ipv6_local_subnet));
+}
+
+TEST_F(ClatdTest, DataSanitycheck) {
+ // Sanity checks the data.
+ uint8_t v4_header[] = { IPV4_UDP_HEADER };
+ ASSERT_EQ(sizeof(struct iphdr), sizeof(v4_header)) << "Test IPv4 header: incorrect length\n";
+
+ uint8_t v6_header[] = { IPV6_UDP_HEADER };
+ ASSERT_EQ(sizeof(struct ip6_hdr), sizeof(v6_header)) << "Test IPv6 header: incorrect length\n";
+
+ uint8_t udp_header[] = { UDP_HEADER };
+ ASSERT_EQ(sizeof(struct udphdr), sizeof(udp_header)) << "Test UDP header: incorrect length\n";
+
+ // Sanity checks check_packet.
+ struct udphdr *udp;
+ uint8_t v4_udp_packet[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD };
+ udp = (struct udphdr *)(v4_udp_packet + sizeof(struct iphdr));
+ fix_udp_checksum(v4_udp_packet);
+ ASSERT_EQ(kUdpV4Checksum, udp->check) << "UDP/IPv4 packet checksum sanity check\n";
+ check_packet(v4_udp_packet, sizeof(v4_udp_packet), "UDP/IPv4 packet sanity check");
+
+ uint8_t v6_udp_packet[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD };
+ udp = (struct udphdr *)(v6_udp_packet + sizeof(struct ip6_hdr));
+ fix_udp_checksum(v6_udp_packet);
+ ASSERT_EQ(kUdpV6Checksum, udp->check) << "UDP/IPv6 packet checksum sanity check\n";
+ check_packet(v6_udp_packet, sizeof(v6_udp_packet), "UDP/IPv6 packet sanity check");
+
+ uint8_t ipv4_ping[] = { IPV4_ICMP_HEADER IPV4_PING PAYLOAD };
+ check_packet(ipv4_ping, sizeof(ipv4_ping), "IPv4 ping sanity check");
+
+ uint8_t ipv6_ping[] = { IPV6_ICMPV6_HEADER IPV6_PING PAYLOAD };
+ check_packet(ipv6_ping, sizeof(ipv6_ping), "IPv6 ping sanity check");
+
+ // Sanity checks reassemble_packet.
+ uint8_t reassembled[MAXMTU];
+ size_t total_length = sizeof(reassembled);
+ reassemble_packet(kIPv4Fragments, kIPv4FragLengths, ARRAYSIZE(kIPv4Fragments), reassembled,
+ &total_length, "Reassembly sanity check");
+ check_packet(reassembled, total_length, "IPv4 Reassembled packet is valid");
+ ASSERT_EQ(sizeof(kReassembledIPv4), total_length) << "IPv4 reassembly sanity check: length\n";
+ ASSERT_TRUE(!is_ipv4_fragment((struct iphdr *)reassembled))
+ << "Sanity check: reassembled packet is a fragment!\n";
+ check_data_matches(kReassembledIPv4, reassembled, total_length, "IPv4 reassembly sanity check");
+
+ total_length = sizeof(reassembled);
+ reassemble_packet(kIPv6Fragments, kIPv6FragLengths, ARRAYSIZE(kIPv6Fragments), reassembled,
+ &total_length, "IPv6 reassembly sanity check");
+ ASSERT_TRUE(!is_ipv6_fragment((struct ip6_hdr *)reassembled, total_length))
+ << "Sanity check: reassembled packet is a fragment!\n";
+ check_packet(reassembled, total_length, "IPv6 Reassembled packet is valid");
+}
+
+TEST_F(ClatdTest, PseudoChecksum) {
+ uint32_t pseudo_checksum;
+
+ uint8_t v4_header[] = { IPV4_UDP_HEADER };
+ uint8_t v4_pseudo_header[] = { IPV4_PSEUDOHEADER(v4_header, UDP_LEN) };
+ pseudo_checksum = ipv4_pseudo_header_checksum((struct iphdr *)v4_header, UDP_LEN);
+ EXPECT_EQ(ip_checksum_finish(pseudo_checksum),
+ ip_checksum(v4_pseudo_header, sizeof(v4_pseudo_header)))
+ << "ipv4_pseudo_header_checksum incorrect\n";
+
+ uint8_t v6_header[] = { IPV6_UDP_HEADER };
+ uint8_t v6_pseudo_header[] = { IPV6_PSEUDOHEADER(v6_header, IPPROTO_UDP, UDP_LEN) };
+ pseudo_checksum = ipv6_pseudo_header_checksum((struct ip6_hdr *)v6_header, UDP_LEN, IPPROTO_UDP);
+ EXPECT_EQ(ip_checksum_finish(pseudo_checksum),
+ ip_checksum(v6_pseudo_header, sizeof(v6_pseudo_header)))
+ << "ipv6_pseudo_header_checksum incorrect\n";
+}
+
+TEST_F(ClatdTest, TransportChecksum) {
+ uint8_t udphdr[] = { UDP_HEADER };
+ uint8_t payload[] = { PAYLOAD };
+ EXPECT_EQ(kUdpPartialChecksum, ip_checksum_add(0, udphdr, sizeof(udphdr)))
+ << "UDP partial checksum\n";
+ EXPECT_EQ(kPayloadPartialChecksum, ip_checksum_add(0, payload, sizeof(payload)))
+ << "Payload partial checksum\n";
+
+ uint8_t ip[] = { IPV4_UDP_HEADER };
+ uint8_t ip6[] = { IPV6_UDP_HEADER };
+ uint32_t ipv4_pseudo_sum = ipv4_pseudo_header_checksum((struct iphdr *)ip, UDP_LEN);
+ uint32_t ipv6_pseudo_sum =
+ ipv6_pseudo_header_checksum((struct ip6_hdr *)ip6, UDP_LEN, IPPROTO_UDP);
+
+ EXPECT_NE(0, ipv4_pseudo_sum);
+ EXPECT_NE(0, ipv6_pseudo_sum);
+ EXPECT_EQ(0x3ad0U, ipv4_pseudo_sum % 0xFFFF) << "IPv4 pseudo-checksum sanity check\n";
+ EXPECT_EQ(0x644dU, ipv6_pseudo_sum % 0xFFFF) << "IPv6 pseudo-checksum sanity check\n";
+ EXPECT_EQ(
+ kUdpV4Checksum,
+ ip_checksum_finish(ipv4_pseudo_sum + kUdpPartialChecksum + kPayloadPartialChecksum))
+ << "Unexpected UDP/IPv4 checksum\n";
+ EXPECT_EQ(
+ kUdpV6Checksum,
+ ip_checksum_finish(ipv6_pseudo_sum + kUdpPartialChecksum + kPayloadPartialChecksum))
+ << "Unexpected UDP/IPv6 checksum\n";
+
+ EXPECT_EQ(kUdpV6Checksum,
+ ip_checksum_adjust(kUdpV4Checksum, ipv4_pseudo_sum, ipv6_pseudo_sum))
+ << "Adjust IPv4/UDP checksum to IPv6\n";
+ EXPECT_EQ(kUdpV4Checksum,
+ ip_checksum_adjust(kUdpV6Checksum, ipv6_pseudo_sum, ipv4_pseudo_sum))
+ << "Adjust IPv6/UDP checksum to IPv4\n";
+}
+
+TEST_F(ClatdTest, AdjustChecksum) {
+ struct checksum_data {
+ uint16_t checksum;
+ uint32_t old_hdr_sum;
+ uint32_t new_hdr_sum;
+ uint16_t result;
+ } DATA[] = {
+ { 0x1423, 0xb8ec, 0x2d757, 0xf5b5 },
+ { 0xf5b5, 0x2d757, 0xb8ec, 0x1423 },
+ { 0xdd2f, 0x5555, 0x3285, 0x0000 },
+ { 0x1215, 0x5560, 0x15560 + 20, 0x1200 },
+ { 0xd0c7, 0x3ad0, 0x2644b, 0xa74a },
+ };
+ unsigned i = 0;
+
+ for (i = 0; i < ARRAYSIZE(DATA); i++) {
+ struct checksum_data *data = DATA + i;
+ uint16_t result = ip_checksum_adjust(data->checksum, data->old_hdr_sum, data->new_hdr_sum);
+ EXPECT_EQ(result, data->result)
+ << "Incorrect checksum" << std::showbase << std::hex
+ << "\n Expected: " << data->result
+ << "\n Actual: " << result
+ << "\n checksum=" << data->checksum
+ << " old_sum=" << data->old_hdr_sum << " new_sum=" << data->new_hdr_sum << "\n";
+ }
+}
+
+TEST_F(ClatdTest, Translate) {
+ // This test uses hardcoded packets so the clatd address must be fixed.
+ inet_pton(AF_INET6, kIPv6LocalAddr, &Global_Clatd_Config.ipv6_local_subnet);
+
+ uint8_t udp_ipv4[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD };
+ uint8_t udp_ipv6[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD };
+ fix_udp_checksum(udp_ipv4);
+ fix_udp_checksum(udp_ipv6);
+ check_translated_packet(udp_ipv4, sizeof(udp_ipv4), udp_ipv6, sizeof(udp_ipv6),
+ "UDP/IPv4 -> UDP/IPv6 translation");
+ check_translated_packet(udp_ipv6, sizeof(udp_ipv6), udp_ipv4, sizeof(udp_ipv4),
+ "UDP/IPv6 -> UDP/IPv4 translation");
+
+ uint8_t ipv4_ping[] = { IPV4_ICMP_HEADER IPV4_PING PAYLOAD };
+ uint8_t ipv6_ping[] = { IPV6_ICMPV6_HEADER IPV6_PING PAYLOAD };
+ check_translated_packet(ipv4_ping, sizeof(ipv4_ping), ipv6_ping, sizeof(ipv6_ping),
+ "ICMP->ICMPv6 translation");
+ check_translated_packet(ipv6_ping, sizeof(ipv6_ping), ipv4_ping, sizeof(ipv4_ping),
+ "ICMPv6->ICMP translation");
+}
+
+TEST_F(ClatdTest, Fragmentation) {
+ // This test uses hardcoded packets so the clatd address must be fixed.
+ inet_pton(AF_INET6, kIPv6LocalAddr, &Global_Clatd_Config.ipv6_local_subnet);
+
+ check_fragment_translation(kIPv4Fragments, kIPv4FragLengths, kIPv6Fragments, kIPv6FragLengths,
+ ARRAYSIZE(kIPv4Fragments), "IPv4->IPv6 fragment translation");
+
+ check_fragment_translation(kIPv6Fragments, kIPv6FragLengths, kIPv4Fragments, kIPv4FragLengths,
+ ARRAYSIZE(kIPv6Fragments), "IPv6->IPv4 fragment translation");
+}
+
+// picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix
+void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet,
+ struct in6_addr *plat_subnet) {
+ // Fill last 8 bytes of IPv6 address with random bits.
+ arc4random_buf(&myaddr->s6_addr[8], 8);
+
+ // Make the IID checksum-neutral. That is, make it so that:
+ // checksum(Local IPv4 | Remote IPv4) = checksum(Local IPv6 | Remote IPv6)
+ // in other words (because remote IPv6 = NAT64 prefix | Remote IPv4):
+ // checksum(Local IPv4) = checksum(Local IPv6 | NAT64 prefix)
+ // Do this by adjusting the two bytes in the middle of the IID.
+
+ uint16_t middlebytes = (myaddr->s6_addr[11] << 8) + myaddr->s6_addr[12];
+
+ uint32_t c1 = ip_checksum_add(0, ipv4_local_subnet, sizeof(*ipv4_local_subnet));
+ uint32_t c2 = ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) +
+ ip_checksum_add(0, myaddr, sizeof(*myaddr));
+
+ uint16_t delta = ip_checksum_adjust(middlebytes, c1, c2);
+ myaddr->s6_addr[11] = delta >> 8;
+ myaddr->s6_addr[12] = delta & 0xff;
+}
+
+void check_translate_checksum_neutral(const uint8_t *original, size_t original_len,
+ size_t expected_len, const char *msg) {
+ uint8_t translated[MAXMTU];
+ size_t translated_len = sizeof(translated);
+ do_translate_packet(original, original_len, translated, &translated_len, msg);
+ EXPECT_EQ(expected_len, translated_len) << msg << ": Translated packet length incorrect\n";
+ // do_translate_packet already checks packets for validity and verifies the checksum.
+ int original_check = get_transport_checksum(original);
+ int translated_check = get_transport_checksum(translated);
+ ASSERT_NE(-1, original_check);
+ ASSERT_NE(-1, translated_check);
+ ASSERT_EQ(original_check, translated_check)
+ << "Not checksum neutral: original and translated checksums differ\n";
+}
+
+TEST_F(ClatdTest, TranslateChecksumNeutral) {
+ // Generate a random clat IPv6 address and check that translation is checksum-neutral.
+ ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54",
+ &Global_Clatd_Config.ipv6_local_subnet));
+
+ gen_random_iid(&Global_Clatd_Config.ipv6_local_subnet, &Global_Clatd_Config.ipv4_local_subnet,
+ &Global_Clatd_Config.plat_subnet);
+
+ ASSERT_NE(htonl((uint32_t)0x00000464), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]);
+ ASSERT_NE((uint32_t)0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]);
+
+ // Check that translating UDP packets is checksum-neutral. First, IPv4.
+ uint8_t udp_ipv4[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD };
+ fix_udp_checksum(udp_ipv4);
+ check_translate_checksum_neutral(udp_ipv4, sizeof(udp_ipv4), sizeof(udp_ipv4) + 20,
+ "UDP/IPv4 -> UDP/IPv6 checksum neutral");
+
+ // Now try IPv6.
+ uint8_t udp_ipv6[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD };
+ // The test packet uses the static IID, not the random IID. Fix up the source address.
+ struct ip6_hdr *ip6 = (struct ip6_hdr *)udp_ipv6;
+ memcpy(&ip6->ip6_src, &Global_Clatd_Config.ipv6_local_subnet, sizeof(ip6->ip6_src));
+ fix_udp_checksum(udp_ipv6);
+ check_translate_checksum_neutral(udp_ipv4, sizeof(udp_ipv4), sizeof(udp_ipv4) + 20,
+ "UDP/IPv4 -> UDP/IPv6 checksum neutral");
+}
diff --git a/clatd/common.h b/clatd/common.h
new file mode 100644
index 0000000..e9551ee
--- /dev/null
+++ b/clatd/common.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * common.h - common definitions
+ */
+#ifndef __CLATD_COMMON_H__
+#define __CLATD_COMMON_H__
+
+#include <sys/uio.h>
+
+// A clat_packet is an array of iovec structures representing a packet that we are translating.
+// The CLAT_POS_XXX constants represent the array indices within the clat_packet that contain
+// specific parts of the packet. The packet_* functions operate on all the packet segments past a
+// given position.
+typedef enum {
+ CLAT_POS_TUNHDR,
+ CLAT_POS_IPHDR,
+ CLAT_POS_FRAGHDR,
+ CLAT_POS_TRANSPORTHDR,
+ CLAT_POS_ICMPERR_IPHDR,
+ CLAT_POS_ICMPERR_FRAGHDR,
+ CLAT_POS_ICMPERR_TRANSPORTHDR,
+ CLAT_POS_PAYLOAD,
+ CLAT_POS_MAX
+} clat_packet_index;
+typedef struct iovec clat_packet[CLAT_POS_MAX];
+
+#endif /* __CLATD_COMMON_H__ */
diff --git a/clatd/config.h b/clatd/config.h
new file mode 100644
index 0000000..9612192
--- /dev/null
+++ b/clatd/config.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * config.h - configuration settings
+ */
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+
+#include <linux/if.h>
+#include <netinet/in.h>
+
+struct tun_data {
+ char device4[IFNAMSIZ];
+ int read_fd6, write_fd6, fd4;
+};
+
+struct clat_config {
+ struct in6_addr ipv6_local_subnet;
+ struct in_addr ipv4_local_subnet;
+ struct in6_addr plat_subnet;
+ const char *native_ipv6_interface;
+};
+
+extern struct clat_config Global_Clatd_Config;
+
+/* function: ipv6_prefix_equal
+ * compares the /64 prefixes of two ipv6 addresses.
+ * a1 - first address
+ * a2 - second address
+ * returns: 0 if the subnets are different, 1 if they are the same.
+ */
+static inline int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) {
+ return !memcmp(a1, a2, 8);
+}
+
+#endif /* __CONFIG_H__ */
diff --git a/clatd/debug.h b/clatd/debug.h
new file mode 100644
index 0000000..8e09672
--- /dev/null
+++ b/clatd/debug.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * debug.h - debug settings
+ */
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+// set to 1 to enable debug logging and packet dumping.
+#define CLAT_DEBUG 0
+
+#endif /* __DEBUG_H__ */
diff --git a/clatd/dump.c b/clatd/dump.c
new file mode 100644
index 0000000..dff3d5e
--- /dev/null
+++ b/clatd/dump.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * dump.c - print various headers for debugging
+ */
+#include "dump.h"
+
+#include <arpa/inet.h>
+#include <linux/icmp.h>
+#include <linux/if_tun.h>
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "checksum.h"
+#include "clatd.h"
+#include "debug.h"
+#include "logging.h"
+
+#if CLAT_DEBUG
+
+/* print ip header */
+void dump_ip(struct iphdr *header) {
+ u_int16_t frag_flags;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ frag_flags = ntohs(header->frag_off);
+
+ printf("IP packet\n");
+ printf("header_len = %x\n", header->ihl);
+ printf("version = %x\n", header->version);
+ printf("tos = %x\n", header->tos);
+ printf("tot_len = %x\n", ntohs(header->tot_len));
+ printf("id = %x\n", ntohs(header->id));
+ printf("frag: ");
+ if (frag_flags & IP_RF) {
+ printf("(RF) ");
+ }
+ if (frag_flags & IP_DF) {
+ printf("DF ");
+ }
+ if (frag_flags & IP_MF) {
+ printf("MF ");
+ }
+ printf("offset = %x\n", frag_flags & IP_OFFMASK);
+ printf("ttl = %x\n", header->ttl);
+ printf("protocol = %x\n", header->protocol);
+ printf("checksum = %x\n", ntohs(header->check));
+ inet_ntop(AF_INET, &header->saddr, addrstr, sizeof(addrstr));
+ printf("saddr = %s\n", addrstr);
+ inet_ntop(AF_INET, &header->daddr, addrstr, sizeof(addrstr));
+ printf("daddr = %s\n", addrstr);
+}
+
+/* print ip6 header */
+void dump_ip6(struct ip6_hdr *header) {
+ char addrstr[INET6_ADDRSTRLEN];
+
+ printf("ipv6\n");
+ printf("version = %x\n", header->ip6_vfc >> 4);
+ printf("traffic class = %x\n", header->ip6_flow >> 20);
+ printf("flow label = %x\n", ntohl(header->ip6_flow & 0x000fffff));
+ printf("payload len = %x\n", ntohs(header->ip6_plen));
+ printf("next header = %x\n", header->ip6_nxt);
+ printf("hop limit = %x\n", header->ip6_hlim);
+
+ inet_ntop(AF_INET6, &header->ip6_src, addrstr, sizeof(addrstr));
+ printf("source = %s\n", addrstr);
+
+ inet_ntop(AF_INET6, &header->ip6_dst, addrstr, sizeof(addrstr));
+ printf("dest = %s\n", addrstr);
+}
+
+/* print icmp header */
+void dump_icmp(struct icmphdr *icmp) {
+ printf("ICMP\n");
+
+ printf("icmp.type = %x ", icmp->type);
+ if (icmp->type == ICMP_ECHOREPLY) {
+ printf("echo reply");
+ } else if (icmp->type == ICMP_ECHO) {
+ printf("echo request");
+ } else {
+ printf("other");
+ }
+ printf("\n");
+ printf("icmp.code = %x\n", icmp->code);
+ printf("icmp.checksum = %x\n", ntohs(icmp->checksum));
+ if (icmp->type == ICMP_ECHOREPLY || icmp->type == ICMP_ECHO) {
+ printf("icmp.un.echo.id = %x\n", ntohs(icmp->un.echo.id));
+ printf("icmp.un.echo.sequence = %x\n", ntohs(icmp->un.echo.sequence));
+ }
+}
+
+/* print icmp6 header */
+void dump_icmp6(struct icmp6_hdr *icmp6) {
+ printf("ICMP6\n");
+ printf("type = %x", icmp6->icmp6_type);
+ if (icmp6->icmp6_type == ICMP6_ECHO_REQUEST) {
+ printf("(echo request)");
+ } else if (icmp6->icmp6_type == ICMP6_ECHO_REPLY) {
+ printf("(echo reply)");
+ }
+ printf("\n");
+ printf("code = %x\n", icmp6->icmp6_code);
+
+ printf("checksum = %x\n", icmp6->icmp6_cksum);
+
+ if ((icmp6->icmp6_type == ICMP6_ECHO_REQUEST) || (icmp6->icmp6_type == ICMP6_ECHO_REPLY)) {
+ printf("icmp6_id = %x\n", icmp6->icmp6_id);
+ printf("icmp6_seq = %x\n", icmp6->icmp6_seq);
+ }
+}
+
+/* print udp header */
+void dump_udp_generic(const struct udphdr *udp, uint32_t temp_checksum, const uint8_t *payload,
+ size_t payload_size) {
+ uint16_t my_checksum;
+
+ temp_checksum = ip_checksum_add(temp_checksum, udp, sizeof(struct udphdr));
+ temp_checksum = ip_checksum_add(temp_checksum, payload, payload_size);
+ my_checksum = ip_checksum_finish(temp_checksum);
+
+ printf("UDP\n");
+ printf("source = %x\n", ntohs(udp->source));
+ printf("dest = %x\n", ntohs(udp->dest));
+ printf("len = %x\n", ntohs(udp->len));
+ printf("check = %x (mine %x)\n", udp->check, my_checksum);
+}
+
+/* print ipv4/udp header */
+void dump_udp(const struct udphdr *udp, const struct iphdr *ip, const uint8_t *payload,
+ size_t payload_size) {
+ uint32_t temp_checksum;
+ temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*udp) + payload_size);
+ dump_udp_generic(udp, temp_checksum, payload, payload_size);
+}
+
+/* print ipv6/udp header */
+void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, const uint8_t *payload,
+ size_t payload_size) {
+ uint32_t temp_checksum;
+ temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*udp) + payload_size, IPPROTO_UDP);
+ dump_udp_generic(udp, temp_checksum, payload, payload_size);
+}
+
+/* print tcp header */
+void dump_tcp_generic(const struct tcphdr *tcp, const uint8_t *options, size_t options_size,
+ uint32_t temp_checksum, const uint8_t *payload, size_t payload_size) {
+ uint16_t my_checksum;
+
+ temp_checksum = ip_checksum_add(temp_checksum, tcp, sizeof(struct tcphdr));
+ if (options) {
+ temp_checksum = ip_checksum_add(temp_checksum, options, options_size);
+ }
+ temp_checksum = ip_checksum_add(temp_checksum, payload, payload_size);
+ my_checksum = ip_checksum_finish(temp_checksum);
+
+ printf("TCP\n");
+ printf("source = %x\n", ntohs(tcp->source));
+ printf("dest = %x\n", ntohs(tcp->dest));
+ printf("seq = %x\n", ntohl(tcp->seq));
+ printf("ack = %x\n", ntohl(tcp->ack_seq));
+ printf("d_off = %x\n", tcp->doff);
+ printf("res1 = %x\n", tcp->res1);
+#ifdef __BIONIC__
+ printf("CWR = %x\n", tcp->cwr);
+ printf("ECE = %x\n", tcp->ece);
+#else
+ printf("CWR/ECE = %x\n", tcp->res2);
+#endif
+ printf("urg = %x ack = %x psh = %x rst = %x syn = %x fin = %x\n", tcp->urg, tcp->ack,
+ tcp->psh, tcp->rst, tcp->syn, tcp->fin);
+ printf("window = %x\n", ntohs(tcp->window));
+ printf("check = %x [mine %x]\n", tcp->check, my_checksum);
+ printf("urgent = %x\n", tcp->urg_ptr);
+
+ if (options) {
+ size_t i;
+
+ printf("options: ");
+ for (i = 0; i < options_size; i++) {
+ printf("%x ", *(options + i));
+ }
+ printf("\n");
+ }
+}
+
+/* print ipv4/tcp header */
+void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const uint8_t *payload,
+ size_t payload_size, const uint8_t *options, size_t options_size) {
+ uint32_t temp_checksum;
+
+ temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*tcp) + options_size + payload_size);
+ dump_tcp_generic(tcp, options, options_size, temp_checksum, payload, payload_size);
+}
+
+/* print ipv6/tcp header */
+void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, const uint8_t *payload,
+ size_t payload_size, const uint8_t *options, size_t options_size) {
+ uint32_t temp_checksum;
+
+ temp_checksum =
+ ipv6_pseudo_header_checksum(ip6, sizeof(*tcp) + options_size + payload_size, IPPROTO_TCP);
+ dump_tcp_generic(tcp, options, options_size, temp_checksum, payload, payload_size);
+}
+
+/* generic hex dump */
+void logcat_hexdump(const char *info, const uint8_t *data, size_t len) {
+ char output[MAXDUMPLEN * 3 + 2];
+ size_t i;
+
+ output[0] = '\0';
+ for (i = 0; i < len && i < MAXDUMPLEN; i++) {
+ snprintf(output + i * 3, 4, " %02x", data[i]);
+ }
+ output[len * 3 + 3] = '\0';
+
+ logmsg(ANDROID_LOG_WARN, "info %s len %d data%s", info, len, output);
+}
+
+void dump_iovec(const struct iovec *iov, int iov_len) {
+ int i;
+ char *str;
+ for (i = 0; i < iov_len; i++) {
+ asprintf(&str, "iov[%d]: ", i);
+ logcat_hexdump(str, iov[i].iov_base, iov[i].iov_len);
+ free(str);
+ }
+}
+#endif // CLAT_DEBUG
diff --git a/clatd/dump.h b/clatd/dump.h
new file mode 100644
index 0000000..6b96cd2
--- /dev/null
+++ b/clatd/dump.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * dump.h - debug functions
+ */
+#ifndef __DUMP_H__
+#define __DUMP_H__
+
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+void dump_ip(struct iphdr *header);
+void dump_icmp(struct icmphdr *icmp);
+void dump_udp(const struct udphdr *udp, const struct iphdr *ip, const uint8_t *payload,
+ size_t payload_size);
+void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const uint8_t *payload,
+ size_t payload_size, const uint8_t *options, size_t options_size);
+
+void dump_ip6(struct ip6_hdr *header);
+void dump_icmp6(struct icmp6_hdr *icmp6);
+void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, const uint8_t *payload,
+ size_t payload_size);
+void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, const uint8_t *payload,
+ size_t payload_size, const uint8_t *options, size_t options_size);
+
+void logcat_hexdump(const char *info, const uint8_t *data, size_t len);
+void dump_iovec(const struct iovec *iov, int iov_len);
+
+#endif /* __DUMP_H__ */
diff --git a/clatd/icmp.c b/clatd/icmp.c
new file mode 100644
index 0000000..f9ba113
--- /dev/null
+++ b/clatd/icmp.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * icmp.c - convenience functions for translating ICMP and ICMPv6 packets.
+ */
+
+#include <linux/icmp.h>
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip_icmp.h>
+
+#include "icmp.h"
+#include "logging.h"
+
+/* function: icmp_guess_ttl
+ * Guesses the number of hops a received packet has traversed based on its TTL.
+ * ttl - the ttl of the received packet.
+ */
+uint8_t icmp_guess_ttl(uint8_t ttl) {
+ if (ttl > 128) {
+ return 255 - ttl;
+ } else if (ttl > 64) {
+ return 128 - ttl;
+ } else if (ttl > 32) {
+ return 64 - ttl;
+ } else {
+ return 32 - ttl;
+ }
+}
+
+/* function: is_icmp_error
+ * Determines whether an ICMP type is an error message.
+ * type: the ICMP type
+ */
+int is_icmp_error(uint8_t type) { return type == 3 || type == 11 || type == 12; }
+
+/* function: is_icmp6_error
+ * Determines whether an ICMPv6 type is an error message.
+ * type: the ICMPv6 type
+ */
+int is_icmp6_error(uint8_t type) { return type < 128; }
+
+/* function: icmp_to_icmp6_type
+ * Maps ICMP types to ICMPv6 types. Partial implementation of RFC 6145, section 4.2.
+ * type - the ICMPv6 type
+ */
+uint8_t icmp_to_icmp6_type(uint8_t type, uint8_t code) {
+ switch (type) {
+ case ICMP_ECHO:
+ return ICMP6_ECHO_REQUEST;
+
+ case ICMP_ECHOREPLY:
+ return ICMP6_ECHO_REPLY;
+
+ case ICMP_TIME_EXCEEDED:
+ return ICMP6_TIME_EXCEEDED;
+
+ case ICMP_DEST_UNREACH:
+ // These two types need special translation which we don't support yet.
+ if (code != ICMP_UNREACH_PROTOCOL && code != ICMP_UNREACH_NEEDFRAG) {
+ return ICMP6_DST_UNREACH;
+ }
+ }
+
+ // We don't understand this ICMP type. Return parameter problem so the caller will bail out.
+ logmsg_dbg(ANDROID_LOG_DEBUG, "icmp_to_icmp6_type: unhandled ICMP type %d", type);
+ return ICMP6_PARAM_PROB;
+}
+
+/* function: icmp_to_icmp6_code
+ * Maps ICMP codes to ICMPv6 codes. Partial implementation of RFC 6145, section 4.2.
+ * type - the ICMP type
+ * code - the ICMP code
+ */
+uint8_t icmp_to_icmp6_code(uint8_t type, uint8_t code) {
+ switch (type) {
+ case ICMP_ECHO:
+ case ICMP_ECHOREPLY:
+ return 0;
+
+ case ICMP_TIME_EXCEEDED:
+ return code;
+
+ case ICMP_DEST_UNREACH:
+ switch (code) {
+ case ICMP_UNREACH_NET:
+ case ICMP_UNREACH_HOST:
+ return ICMP6_DST_UNREACH_NOROUTE;
+
+ case ICMP_UNREACH_PORT:
+ return ICMP6_DST_UNREACH_NOPORT;
+
+ case ICMP_UNREACH_NET_PROHIB:
+ case ICMP_UNREACH_HOST_PROHIB:
+ case ICMP_UNREACH_FILTER_PROHIB:
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ return ICMP6_DST_UNREACH_ADMIN;
+
+ // Otherwise, we don't understand this ICMP type/code combination. Fall through.
+ }
+ }
+ logmsg_dbg(ANDROID_LOG_DEBUG, "icmp_to_icmp6_code: unhandled ICMP type/code %d/%d", type, code);
+ return 0;
+}
+
+/* function: icmp6_to_icmp_type
+ * Maps ICMPv6 types to ICMP types. Partial implementation of RFC 6145, section 5.2.
+ * type - the ICMP type
+ */
+uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code) {
+ switch (type) {
+ case ICMP6_ECHO_REQUEST:
+ return ICMP_ECHO;
+
+ case ICMP6_ECHO_REPLY:
+ return ICMP_ECHOREPLY;
+
+ case ICMP6_DST_UNREACH:
+ return ICMP_DEST_UNREACH;
+
+ case ICMP6_TIME_EXCEEDED:
+ return ICMP_TIME_EXCEEDED;
+ }
+
+ // We don't understand this ICMP type. Return parameter problem so the caller will bail out.
+ logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type/code %d/%d", type, code);
+ return ICMP_PARAMETERPROB;
+}
+
+/* function: icmp6_to_icmp_code
+ * Maps ICMPv6 codes to ICMP codes. Partial implementation of RFC 6145, section 5.2.
+ * type - the ICMPv6 type
+ * code - the ICMPv6 code
+ */
+uint8_t icmp6_to_icmp_code(uint8_t type, uint8_t code) {
+ switch (type) {
+ case ICMP6_ECHO_REQUEST:
+ case ICMP6_ECHO_REPLY:
+ case ICMP6_TIME_EXCEEDED:
+ return code;
+
+ case ICMP6_DST_UNREACH:
+ switch (code) {
+ case ICMP6_DST_UNREACH_NOROUTE:
+ return ICMP_UNREACH_HOST;
+
+ case ICMP6_DST_UNREACH_ADMIN:
+ return ICMP_UNREACH_HOST_PROHIB;
+
+ case ICMP6_DST_UNREACH_BEYONDSCOPE:
+ return ICMP_UNREACH_HOST;
+
+ case ICMP6_DST_UNREACH_ADDR:
+ return ICMP_HOST_UNREACH;
+
+ case ICMP6_DST_UNREACH_NOPORT:
+ return ICMP_UNREACH_PORT;
+
+ // Otherwise, we don't understand this ICMPv6 type/code combination. Fall through.
+ }
+ }
+
+ logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_code: unhandled ICMP type/code %d/%d", type, code);
+ return 0;
+}
diff --git a/clatd/icmp.h b/clatd/icmp.h
new file mode 100644
index 0000000..632e92d
--- /dev/null
+++ b/clatd/icmp.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * icmp.c - convenience functions for translating ICMP and ICMPv6 packets.
+ */
+
+#ifndef __ICMP_H__
+#define __ICMP_H__
+
+#include <stdint.h>
+
+// Guesses the number of hops a received packet has traversed based on its TTL.
+uint8_t icmp_guess_ttl(uint8_t ttl);
+
+// Determines whether an ICMP type is an error message.
+int is_icmp_error(uint8_t type);
+
+// Determines whether an ICMPv6 type is an error message.
+int is_icmp6_error(uint8_t type);
+
+// Maps ICMP types to ICMPv6 types. Partial implementation of RFC 6145, section 4.2.
+uint8_t icmp_to_icmp6_type(uint8_t type, uint8_t code);
+
+// Maps ICMP codes to ICMPv6 codes. Partial implementation of RFC 6145, section 4.2.
+uint8_t icmp_to_icmp6_code(uint8_t type, uint8_t code);
+
+// Maps ICMPv6 types to ICMP types. Partial implementation of RFC 6145, section 5.2.
+uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code);
+
+// Maps ICMPv6 codes to ICMP codes. Partial implementation of RFC 6145, section 5.2.
+uint8_t icmp6_to_icmp_code(uint8_t type, uint8_t code);
+
+#endif /* __ICMP_H__ */
diff --git a/clatd/ipv4.c b/clatd/ipv4.c
new file mode 100644
index 0000000..2be02e3
--- /dev/null
+++ b/clatd/ipv4.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ipv4.c - takes ipv4 packets, finds their headers, and then calls translation functions on them
+ */
+#include <string.h>
+
+#include "checksum.h"
+#include "debug.h"
+#include "dump.h"
+#include "logging.h"
+#include "translate.h"
+
+/* function: icmp_packet
+ * translates an icmp packet
+ * out - output packet
+ * icmp - pointer to icmp header in packet
+ * checksum - pseudo-header checksum
+ * len - size of ip payload
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int icmp_packet(clat_packet out, clat_packet_index pos, const struct icmphdr *icmp,
+ uint32_t checksum, size_t len) {
+ const uint8_t *payload;
+ size_t payload_size;
+
+ if (len < sizeof(struct icmphdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "icmp_packet/(too small)");
+ return 0;
+ }
+
+ payload = (const uint8_t *)(icmp + 1);
+ payload_size = len - sizeof(struct icmphdr);
+
+ return icmp_to_icmp6(out, pos, icmp, checksum, payload, payload_size);
+}
+
+/* function: ipv4_packet
+ * translates an ipv4 packet
+ * out - output packet
+ * packet - packet data
+ * len - size of packet
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len) {
+ const struct iphdr *header = (struct iphdr *)packet;
+ struct ip6_hdr *ip6_targ = (struct ip6_hdr *)out[pos].iov_base;
+ struct ip6_frag *frag_hdr;
+ size_t frag_hdr_len;
+ uint8_t nxthdr;
+ const uint8_t *next_header;
+ size_t len_left;
+ uint32_t old_sum, new_sum;
+ int iov_len;
+
+ if (len < sizeof(struct iphdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/too short for an ip header");
+ return 0;
+ }
+
+ if (header->ihl < 5) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header length set to less than 5: %x", header->ihl);
+ return 0;
+ }
+
+ if ((size_t)header->ihl * 4 > len) { // ip header length larger than entire packet
+ logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header length set too large: %x", header->ihl);
+ return 0;
+ }
+
+ if (header->version != 4) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header version not 4: %x", header->version);
+ return 0;
+ }
+
+ /* rfc6145 - If any IPv4 options are present in the IPv4 packet, they MUST be
+ * ignored and the packet translated normally; there is no attempt to
+ * translate the options.
+ */
+
+ next_header = packet + header->ihl * 4;
+ len_left = len - header->ihl * 4;
+
+ nxthdr = header->protocol;
+ if (nxthdr == IPPROTO_ICMP) {
+ // ICMP and ICMPv6 have different protocol numbers.
+ nxthdr = IPPROTO_ICMPV6;
+ }
+
+ /* Fill in the IPv6 header. We need to do this before we translate the packet because TCP and
+ * UDP include parts of the IP header in the checksum. Set the length to zero because we don't
+ * know it yet.
+ */
+ fill_ip6_header(ip6_targ, 0, nxthdr, header);
+ out[pos].iov_len = sizeof(struct ip6_hdr);
+
+ /* Calculate the pseudo-header checksum.
+ * Technically, the length that is used in the pseudo-header checksum is the transport layer
+ * length, which is not the same as len_left in the case of fragmented packets. But since
+ * translation does not change the transport layer length, the checksum is unaffected.
+ */
+ old_sum = ipv4_pseudo_header_checksum(header, len_left);
+ new_sum = ipv6_pseudo_header_checksum(ip6_targ, len_left, nxthdr);
+
+ // If the IPv4 packet is fragmented, add a Fragment header.
+ frag_hdr = (struct ip6_frag *)out[pos + 1].iov_base;
+ frag_hdr_len = maybe_fill_frag_header(frag_hdr, ip6_targ, header);
+ out[pos + 1].iov_len = frag_hdr_len;
+
+ if (frag_hdr_len && frag_hdr->ip6f_offlg & IP6F_OFF_MASK) {
+ // Non-first fragment. Copy the rest of the packet as is.
+ iov_len = generic_packet(out, pos + 2, next_header, len_left);
+ } else if (nxthdr == IPPROTO_ICMPV6) {
+ iov_len = icmp_packet(out, pos + 2, (const struct icmphdr *)next_header, new_sum, len_left);
+ } else if (nxthdr == IPPROTO_TCP) {
+ iov_len =
+ tcp_packet(out, pos + 2, (const struct tcphdr *)next_header, old_sum, new_sum, len_left);
+ } else if (nxthdr == IPPROTO_UDP) {
+ iov_len =
+ udp_packet(out, pos + 2, (const struct udphdr *)next_header, old_sum, new_sum, len_left);
+ } else if (nxthdr == IPPROTO_GRE || nxthdr == IPPROTO_ESP) {
+ iov_len = generic_packet(out, pos + 2, next_header, len_left);
+ } else {
+#if CLAT_DEBUG
+ logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x", header->protocol);
+ logcat_hexdump("ipv4/protocol", packet, len);
+#endif
+ return 0;
+ }
+
+ // Set the length.
+ ip6_targ->ip6_plen = htons(packet_length(out, pos));
+ return iov_len;
+}
diff --git a/clatd/ipv6.c b/clatd/ipv6.c
new file mode 100644
index 0000000..05cd3ab
--- /dev/null
+++ b/clatd/ipv6.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ipv6.c - takes ipv6 packets, finds their headers, and then calls translation functions on them
+ */
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "checksum.h"
+#include "config.h"
+#include "debug.h"
+#include "dump.h"
+#include "logging.h"
+#include "translate.h"
+
+/* function: icmp6_packet
+ * takes an icmp6 packet and sets it up for translation
+ * out - output packet
+ * icmp6 - pointer to icmp6 header in packet
+ * checksum - pseudo-header checksum (unused)
+ * len - size of ip payload
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int icmp6_packet(clat_packet out, clat_packet_index pos, const struct icmp6_hdr *icmp6,
+ size_t len) {
+ const uint8_t *payload;
+ size_t payload_size;
+
+ if (len < sizeof(struct icmp6_hdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "icmp6_packet/(too small)");
+ return 0;
+ }
+
+ payload = (const uint8_t *)(icmp6 + 1);
+ payload_size = len - sizeof(struct icmp6_hdr);
+
+ return icmp6_to_icmp(out, pos, icmp6, payload, payload_size);
+}
+
+/* function: log_bad_address
+ * logs a bad address to android's log buffer if debugging is turned on
+ * fmt - printf-style format, use %s to place the address
+ * badaddr - the bad address in question
+ */
+#if CLAT_DEBUG
+void log_bad_address(const char *fmt, const struct in6_addr *src, const struct in6_addr *dst) {
+ char srcstr[INET6_ADDRSTRLEN];
+ char dststr[INET6_ADDRSTRLEN];
+
+ inet_ntop(AF_INET6, src, srcstr, sizeof(srcstr));
+ inet_ntop(AF_INET6, dst, dststr, sizeof(dststr));
+ logmsg_dbg(ANDROID_LOG_ERROR, fmt, srcstr, dststr);
+}
+#else
+#define log_bad_address(fmt, src, dst)
+#endif
+
+/* function: ipv6_packet
+ * takes an ipv6 packet and hands it off to the layer 4 protocol function
+ * out - output packet
+ * packet - packet data
+ * len - size of packet
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len) {
+ const struct ip6_hdr *ip6 = (struct ip6_hdr *)packet;
+ struct iphdr *ip_targ = (struct iphdr *)out[pos].iov_base;
+ struct ip6_frag *frag_hdr = NULL;
+ uint8_t protocol;
+ const uint8_t *next_header;
+ size_t len_left;
+ uint32_t old_sum, new_sum;
+ int iov_len;
+
+ if (len < sizeof(struct ip6_hdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header: %d", len);
+ return 0;
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ log_bad_address("ipv6_packet/multicast %s->%s", &ip6->ip6_src, &ip6->ip6_dst);
+ return 0; // silently ignore
+ }
+
+ // If the packet is not from the plat subnet to the local subnet, or vice versa, drop it, unless
+ // it's an ICMP packet (which can come from anywhere). We do not send IPv6 packets from the plat
+ // subnet to the local subnet, but these can appear as inner packets in ICMP errors, so we need
+ // to translate them. We accept third-party ICMPv6 errors, even though their source addresses
+ // cannot be translated, so that things like unreachables and traceroute will work. fill_ip_header
+ // takes care of faking a source address for them.
+ if (!(is_in_plat_subnet(&ip6->ip6_src) &&
+ IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &Global_Clatd_Config.ipv6_local_subnet)) &&
+ !(is_in_plat_subnet(&ip6->ip6_dst) &&
+ IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &Global_Clatd_Config.ipv6_local_subnet)) &&
+ ip6->ip6_nxt != IPPROTO_ICMPV6) {
+ log_bad_address("ipv6_packet/wrong source address: %s->%s", &ip6->ip6_src, &ip6->ip6_dst);
+ return 0;
+ }
+
+ next_header = packet + sizeof(struct ip6_hdr);
+ len_left = len - sizeof(struct ip6_hdr);
+
+ protocol = ip6->ip6_nxt;
+
+ /* Fill in the IPv4 header. We need to do this before we translate the packet because TCP and
+ * UDP include parts of the IP header in the checksum. Set the length to zero because we don't
+ * know it yet.
+ */
+ fill_ip_header(ip_targ, 0, protocol, ip6);
+ out[pos].iov_len = sizeof(struct iphdr);
+
+ // If there's a Fragment header, parse it and decide what the next header is.
+ // Do this before calculating the pseudo-header checksum because it updates the next header value.
+ if (protocol == IPPROTO_FRAGMENT) {
+ frag_hdr = (struct ip6_frag *)next_header;
+ if (len_left < sizeof(*frag_hdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for fragment header: %d", len);
+ return 0;
+ }
+
+ next_header += sizeof(*frag_hdr);
+ len_left -= sizeof(*frag_hdr);
+
+ protocol = parse_frag_header(frag_hdr, ip_targ);
+ }
+
+ // ICMP and ICMPv6 have different protocol numbers.
+ if (protocol == IPPROTO_ICMPV6) {
+ protocol = IPPROTO_ICMP;
+ ip_targ->protocol = IPPROTO_ICMP;
+ }
+
+ /* Calculate the pseudo-header checksum.
+ * Technically, the length that is used in the pseudo-header checksum is the transport layer
+ * length, which is not the same as len_left in the case of fragmented packets. But since
+ * translation does not change the transport layer length, the checksum is unaffected.
+ */
+ old_sum = ipv6_pseudo_header_checksum(ip6, len_left, protocol);
+ new_sum = ipv4_pseudo_header_checksum(ip_targ, len_left);
+
+ // Does not support IPv6 extension headers except Fragment.
+ if (frag_hdr && (frag_hdr->ip6f_offlg & IP6F_OFF_MASK)) {
+ iov_len = generic_packet(out, pos + 2, next_header, len_left);
+ } else if (protocol == IPPROTO_ICMP) {
+ iov_len = icmp6_packet(out, pos + 2, (const struct icmp6_hdr *)next_header, len_left);
+ } else if (protocol == IPPROTO_TCP) {
+ iov_len =
+ tcp_packet(out, pos + 2, (const struct tcphdr *)next_header, old_sum, new_sum, len_left);
+ } else if (protocol == IPPROTO_UDP) {
+ iov_len =
+ udp_packet(out, pos + 2, (const struct udphdr *)next_header, old_sum, new_sum, len_left);
+ } else if (protocol == IPPROTO_GRE || protocol == IPPROTO_ESP) {
+ iov_len = generic_packet(out, pos + 2, next_header, len_left);
+ } else {
+#if CLAT_DEBUG
+ logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt);
+ logcat_hexdump("ipv6/nxthdr", packet, len);
+#endif
+ return 0;
+ }
+
+ // Set the length and calculate the checksum.
+ ip_targ->tot_len = htons(ntohs(ip_targ->tot_len) + packet_length(out, pos));
+ ip_targ->check = ip_checksum(ip_targ, sizeof(struct iphdr));
+ return iov_len;
+}
diff --git a/clatd/logging.c b/clatd/logging.c
new file mode 100644
index 0000000..79d98e1
--- /dev/null
+++ b/clatd/logging.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * logging.c - print a log message
+ */
+
+#include <android/log.h>
+#include <stdarg.h>
+
+#include "debug.h"
+#include "logging.h"
+
+/* function: logmsg
+ * prints a log message to android's log buffer
+ * prio - the log message priority
+ * fmt - printf format specifier
+ * ... - printf format arguments
+ */
+void logmsg(int prio, const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ __android_log_vprint(prio, "clatd", fmt, ap);
+ va_end(ap);
+}
+
+/* function: logmsg_dbg
+ * prints a log message to android's log buffer if CLAT_DEBUG is set
+ * prio - the log message priority
+ * fmt - printf format specifier
+ * ... - printf format arguments
+ */
+#if CLAT_DEBUG
+void logmsg_dbg(int prio, const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ __android_log_vprint(prio, "clatd", fmt, ap);
+ va_end(ap);
+}
+#else
+void logmsg_dbg(__attribute__((unused)) int prio, __attribute__((unused)) const char *fmt, ...) {}
+#endif
diff --git a/clatd/logging.h b/clatd/logging.h
new file mode 100644
index 0000000..1f4b6b6
--- /dev/null
+++ b/clatd/logging.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * logging.h - print a log message
+ */
+
+#ifndef __LOGGING_H__
+#define __LOGGING_H__
+// for the priorities
+#include <android/log.h>
+
+void logmsg(int prio, const char *fmt, ...);
+void logmsg_dbg(int prio, const char *fmt, ...);
+
+#endif
diff --git a/clatd/main.c b/clatd/main.c
new file mode 100644
index 0000000..f888041
--- /dev/null
+++ b/clatd/main.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * main.c - main function
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/personality.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include "clatd.h"
+#include "common.h"
+#include "config.h"
+#include "logging.h"
+
+#define DEVICEPREFIX "v4-"
+
+/* function: stop_loop
+ * signal handler: stop the event loop
+ */
+static void stop_loop() { running = 0; };
+
+/* function: print_help
+ * in case the user is running this on the command line
+ */
+void print_help() {
+ printf("android-clat arguments:\n");
+ printf("-i [uplink interface]\n");
+ printf("-p [plat prefix]\n");
+ printf("-4 [IPv4 address]\n");
+ printf("-6 [IPv6 address]\n");
+ printf("-t [tun file descriptor number]\n");
+ printf("-r [read socket descriptor number]\n");
+ printf("-w [write socket descriptor number]\n");
+}
+
+/* function: main
+ * allocate and setup the tun device, then run the event loop
+ */
+int main(int argc, char **argv) {
+ struct tun_data tunnel;
+ int opt;
+ char *uplink_interface = NULL, *plat_prefix = NULL;
+ char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL, *read_sock_str = NULL,
+ *write_sock_str = NULL;
+ unsigned len;
+
+ while ((opt = getopt(argc, argv, "i:p:4:6:t:r:w:h")) != -1) {
+ switch (opt) {
+ case 'i':
+ uplink_interface = optarg;
+ break;
+ case 'p':
+ plat_prefix = optarg;
+ break;
+ case '4':
+ v4_addr = optarg;
+ break;
+ case '6':
+ v6_addr = optarg;
+ break;
+ case 't':
+ tunfd_str = optarg;
+ break;
+ case 'r':
+ read_sock_str = optarg;
+ break;
+ case 'w':
+ write_sock_str = optarg;
+ break;
+ case 'h':
+ print_help();
+ exit(0);
+ default:
+ logmsg(ANDROID_LOG_FATAL, "Unknown option -%c. Exiting.", (char)optopt);
+ exit(1);
+ }
+ }
+
+ if (uplink_interface == NULL) {
+ logmsg(ANDROID_LOG_FATAL, "clatd called without an interface");
+ exit(1);
+ }
+
+ if (tunfd_str != NULL && !parse_int(tunfd_str, &tunnel.fd4)) {
+ logmsg(ANDROID_LOG_FATAL, "invalid tunfd %s", tunfd_str);
+ exit(1);
+ }
+ if (!tunnel.fd4) {
+ logmsg(ANDROID_LOG_FATAL, "no tunfd specified on commandline.");
+ exit(1);
+ }
+
+ if (read_sock_str != NULL && !parse_int(read_sock_str, &tunnel.read_fd6)) {
+ logmsg(ANDROID_LOG_FATAL, "invalid read socket %s", read_sock_str);
+ exit(1);
+ }
+ if (!tunnel.read_fd6) {
+ logmsg(ANDROID_LOG_FATAL, "no read_fd6 specified on commandline.");
+ exit(1);
+ }
+
+ if (write_sock_str != NULL && !parse_int(write_sock_str, &tunnel.write_fd6)) {
+ logmsg(ANDROID_LOG_FATAL, "invalid write socket %s", write_sock_str);
+ exit(1);
+ }
+ if (!tunnel.write_fd6) {
+ logmsg(ANDROID_LOG_FATAL, "no write_fd6 specified on commandline.");
+ exit(1);
+ }
+
+ len = snprintf(tunnel.device4, sizeof(tunnel.device4), "%s%s", DEVICEPREFIX, uplink_interface);
+ if (len >= sizeof(tunnel.device4)) {
+ logmsg(ANDROID_LOG_FATAL, "interface name too long '%s'", tunnel.device4);
+ exit(1);
+ }
+
+ Global_Clatd_Config.native_ipv6_interface = uplink_interface;
+ if (!plat_prefix || inet_pton(AF_INET6, plat_prefix, &Global_Clatd_Config.plat_subnet) <= 0) {
+ logmsg(ANDROID_LOG_FATAL, "invalid IPv6 address specified for plat prefix: %s", plat_prefix);
+ exit(1);
+ }
+
+ if (!v4_addr || !inet_pton(AF_INET, v4_addr, &Global_Clatd_Config.ipv4_local_subnet.s_addr)) {
+ logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr);
+ exit(1);
+ }
+
+ if (!v6_addr || !inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) {
+ logmsg(ANDROID_LOG_FATAL, "Invalid source address %s", v6_addr);
+ exit(1);
+ }
+
+ logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s plat=%s v4=%s v6=%s", CLATD_VERSION,
+ uplink_interface, plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)",
+ v6_addr ? v6_addr : "(none)");
+
+ {
+ // Compile time detection of 32 vs 64-bit build. (note: C does not have 'constexpr')
+ // Avoid use of preprocessor macros to get compile time syntax checking even on 64-bit.
+ const int user_bits = sizeof(void*) * 8;
+ const bool user32 = (user_bits == 32);
+
+ // Note that on 64-bit all this personality related code simply compile optimizes out.
+ // 32-bit: fetch current personality (see 'man personality': 0xFFFFFFFF means retrieve only)
+ // On Linux fetching personality cannot fail.
+ const int prev_personality = user32 ? personality(0xFFFFFFFFuL) : PER_LINUX;
+ // 32-bit: attempt to get rid of kernel spoofing of 'uts.machine' architecture,
+ // In theory this cannot fail, as PER_LINUX should always be supported.
+ if (user32) (void)personality((prev_personality & ~PER_MASK) | PER_LINUX);
+ // 64-bit: this will compile time evaluate to false.
+ const bool was_linux32 = (prev_personality & PER_MASK) == PER_LINUX32;
+
+ struct utsname uts = {};
+ if (uname(&uts)) exit(1); // only possible error is EFAULT, but 'uts' is on stack
+
+ // sysname is likely 'Linux', release is 'kver', machine is kernel's *true* architecture
+ logmsg(ANDROID_LOG_INFO, "%d-bit userspace on %s kernel %s for %s%s.", user_bits,
+ uts.sysname, uts.release, uts.machine, was_linux32 ? " (was spoofed)" : "");
+
+ // 32-bit: try to return to the 'default' personality
+ // In theory this cannot fail, because it was already previously in use.
+ if (user32) (void)personality(prev_personality);
+ }
+
+ // Loop until someone sends us a signal or brings down the tun interface.
+ if (signal(SIGTERM, stop_loop) == SIG_ERR) {
+ logmsg(ANDROID_LOG_FATAL, "sigterm handler failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ event_loop(&tunnel);
+
+ logmsg(ANDROID_LOG_INFO, "Shutting down clat on %s", uplink_interface);
+
+ if (running) {
+ logmsg(ANDROID_LOG_INFO, "Clatd on %s waiting for SIGTERM", uplink_interface);
+ // let's give higher level java code 15 seconds to kill us,
+ // but eventually terminate anyway, in case system server forgets about us...
+ // sleep() should be interrupted by SIGTERM, the handler should clear running
+ sleep(15);
+ logmsg(ANDROID_LOG_INFO, "Clatd on %s %s SIGTERM", uplink_interface,
+ running ? "timed out waiting for" : "received");
+ } else {
+ logmsg(ANDROID_LOG_INFO, "Clatd on %s already received SIGTERM", uplink_interface);
+ }
+ return 0;
+}
diff --git a/clatd/translate.c b/clatd/translate.c
new file mode 100644
index 0000000..22830d89
--- /dev/null
+++ b/clatd/translate.c
@@ -0,0 +1,529 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * translate.c - CLAT functions / partial implementation of rfc6145
+ */
+#include "translate.h"
+
+#include <string.h>
+
+#include "checksum.h"
+#include "clatd.h"
+#include "common.h"
+#include "config.h"
+#include "debug.h"
+#include "icmp.h"
+#include "logging.h"
+
+/* function: packet_checksum
+ * calculates the checksum over all the packet components starting from pos
+ * checksum - checksum of packet components before pos
+ * packet - packet to calculate the checksum of
+ * pos - position to start counting from
+ * returns - the completed 16-bit checksum, ready to write into a checksum header field
+ */
+uint16_t packet_checksum(uint32_t checksum, clat_packet packet, clat_packet_index pos) {
+ int i;
+ for (i = pos; i < CLAT_POS_MAX; i++) {
+ if (packet[i].iov_len > 0) {
+ checksum = ip_checksum_add(checksum, packet[i].iov_base, packet[i].iov_len);
+ }
+ }
+ return ip_checksum_finish(checksum);
+}
+
+/* function: packet_length
+ * returns the total length of all the packet components after pos
+ * packet - packet to calculate the length of
+ * pos - position to start counting after
+ * returns: the total length of the packet components after pos
+ */
+uint16_t packet_length(clat_packet packet, clat_packet_index pos) {
+ size_t len = 0;
+ int i;
+ for (i = pos + 1; i < CLAT_POS_MAX; i++) {
+ len += packet[i].iov_len;
+ }
+ return len;
+}
+
+/* function: is_in_plat_subnet
+ * returns true iff the given IPv6 address is in the plat subnet.
+ * addr - IPv6 address
+ */
+int is_in_plat_subnet(const struct in6_addr *addr6) {
+ // Assumes a /96 plat subnet.
+ return (addr6 != NULL) && (memcmp(addr6, &Global_Clatd_Config.plat_subnet, 12) == 0);
+}
+
+/* function: ipv6_addr_to_ipv4_addr
+ * return the corresponding ipv4 address for the given ipv6 address
+ * addr6 - ipv6 address
+ * returns: the IPv4 address
+ */
+uint32_t ipv6_addr_to_ipv4_addr(const struct in6_addr *addr6) {
+ if (is_in_plat_subnet(addr6)) {
+ // Assumes a /96 plat subnet.
+ return addr6->s6_addr32[3];
+ } else if (IN6_ARE_ADDR_EQUAL(addr6, &Global_Clatd_Config.ipv6_local_subnet)) {
+ // Special-case our own address.
+ return Global_Clatd_Config.ipv4_local_subnet.s_addr;
+ } else {
+ // Third party packet. Let the caller deal with it.
+ return INADDR_NONE;
+ }
+}
+
+/* function: ipv4_addr_to_ipv6_addr
+ * return the corresponding ipv6 address for the given ipv4 address
+ * addr4 - ipv4 address
+ */
+struct in6_addr ipv4_addr_to_ipv6_addr(uint32_t addr4) {
+ struct in6_addr addr6;
+ // Both addresses are in network byte order (addr4 comes from a network packet, and the config
+ // file entry is read using inet_ntop).
+ if (addr4 == Global_Clatd_Config.ipv4_local_subnet.s_addr) {
+ return Global_Clatd_Config.ipv6_local_subnet;
+ } else {
+ // Assumes a /96 plat subnet.
+ addr6 = Global_Clatd_Config.plat_subnet;
+ addr6.s6_addr32[3] = addr4;
+ return addr6;
+ }
+}
+
+/* function: fill_tun_header
+ * fill in the header for the tun fd
+ * tun_header - tunnel header, already allocated
+ * proto - ethernet protocol id: ETH_P_IP(ipv4) or ETH_P_IPV6(ipv6)
+ */
+void fill_tun_header(struct tun_pi *tun_header, uint16_t proto) {
+ tun_header->flags = 0;
+ tun_header->proto = htons(proto);
+}
+
+/* function: fill_ip_header
+ * generate an ipv4 header from an ipv6 header
+ * ip_targ - (ipv4) target packet header, source: original ipv4 addr, dest: local subnet addr
+ * payload_len - length of other data inside packet
+ * protocol - protocol number (tcp, udp, etc)
+ * old_header - (ipv6) source packet header, source: nat64 prefix, dest: local subnet prefix
+ */
+void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol,
+ const struct ip6_hdr *old_header) {
+ int ttl_guess;
+ memset(ip, 0, sizeof(struct iphdr));
+
+ ip->ihl = 5;
+ ip->version = 4;
+ ip->tos = 0;
+ ip->tot_len = htons(sizeof(struct iphdr) + payload_len);
+ ip->id = 0;
+ ip->frag_off = htons(IP_DF);
+ ip->ttl = old_header->ip6_hlim;
+ ip->protocol = protocol;
+ ip->check = 0;
+
+ ip->saddr = ipv6_addr_to_ipv4_addr(&old_header->ip6_src);
+ ip->daddr = ipv6_addr_to_ipv4_addr(&old_header->ip6_dst);
+
+ // Third-party ICMPv6 message. This may have been originated by an native IPv6 address.
+ // In that case, the source IPv6 address can't be translated and we need to make up an IPv4
+ // source address. For now, use 255.0.0.<ttl>, which at least looks useful in traceroute.
+ if ((uint32_t)ip->saddr == INADDR_NONE) {
+ ttl_guess = icmp_guess_ttl(old_header->ip6_hlim);
+ ip->saddr = htonl((0xff << 24) + ttl_guess);
+ }
+}
+
+/* function: fill_ip6_header
+ * generate an ipv6 header from an ipv4 header
+ * ip6 - (ipv6) target packet header, source: local subnet prefix, dest: nat64 prefix
+ * payload_len - length of other data inside packet
+ * protocol - protocol number (tcp, udp, etc)
+ * old_header - (ipv4) source packet header, source: local subnet addr, dest: internet's ipv4 addr
+ */
+void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol,
+ const struct iphdr *old_header) {
+ memset(ip6, 0, sizeof(struct ip6_hdr));
+
+ ip6->ip6_vfc = 6 << 4;
+ ip6->ip6_plen = htons(payload_len);
+ ip6->ip6_nxt = protocol;
+ ip6->ip6_hlim = old_header->ttl;
+
+ ip6->ip6_src = ipv4_addr_to_ipv6_addr(old_header->saddr);
+ ip6->ip6_dst = ipv4_addr_to_ipv6_addr(old_header->daddr);
+}
+
+/* function: maybe_fill_frag_header
+ * fills a fragmentation header
+ * generate an ipv6 fragment header from an ipv4 header
+ * frag_hdr - target (ipv6) fragmentation header
+ * ip6_targ - target (ipv6) header
+ * old_header - (ipv4) source packet header
+ * returns: the length of the fragmentation header if present, or zero if not present
+ */
+size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ,
+ const struct iphdr *old_header) {
+ uint16_t frag_flags = ntohs(old_header->frag_off);
+ uint16_t frag_off = frag_flags & IP_OFFMASK;
+ if (frag_off == 0 && (frag_flags & IP_MF) == 0) {
+ // Not a fragment.
+ return 0;
+ }
+
+ frag_hdr->ip6f_nxt = ip6_targ->ip6_nxt;
+ frag_hdr->ip6f_reserved = 0;
+ // In IPv4, the offset is the bottom 13 bits; in IPv6 it's the top 13 bits.
+ frag_hdr->ip6f_offlg = htons(frag_off << 3);
+ if (frag_flags & IP_MF) {
+ frag_hdr->ip6f_offlg |= IP6F_MORE_FRAG;
+ }
+ frag_hdr->ip6f_ident = htonl(ntohs(old_header->id));
+ ip6_targ->ip6_nxt = IPPROTO_FRAGMENT;
+
+ return sizeof(*frag_hdr);
+}
+
+/* function: parse_frag_header
+ * return the length of the fragmentation header if present, or zero if not present
+ * generate an ipv6 fragment header from an ipv4 header
+ * frag_hdr - (ipv6) fragmentation header
+ * ip_targ - target (ipv4) header
+ * returns: the next header value
+ */
+uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ) {
+ uint16_t frag_off = (ntohs(frag_hdr->ip6f_offlg & IP6F_OFF_MASK) >> 3);
+ if (frag_hdr->ip6f_offlg & IP6F_MORE_FRAG) {
+ frag_off |= IP_MF;
+ }
+ ip_targ->frag_off = htons(frag_off);
+ ip_targ->id = htons(ntohl(frag_hdr->ip6f_ident) & 0xffff);
+ ip_targ->protocol = frag_hdr->ip6f_nxt;
+ return frag_hdr->ip6f_nxt;
+}
+
+/* function: icmp_to_icmp6
+ * translate ipv4 icmp to ipv6 icmp
+ * out - output packet
+ * icmp - source packet icmp header
+ * checksum - pseudo-header checksum
+ * payload - icmp payload
+ * payload_size - size of payload
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int icmp_to_icmp6(clat_packet out, clat_packet_index pos, const struct icmphdr *icmp,
+ uint32_t checksum, const uint8_t *payload, size_t payload_size) {
+ struct icmp6_hdr *icmp6_targ = out[pos].iov_base;
+ uint8_t icmp6_type;
+ int clat_packet_len;
+
+ memset(icmp6_targ, 0, sizeof(struct icmp6_hdr));
+
+ icmp6_type = icmp_to_icmp6_type(icmp->type, icmp->code);
+ icmp6_targ->icmp6_type = icmp6_type;
+ icmp6_targ->icmp6_code = icmp_to_icmp6_code(icmp->type, icmp->code);
+
+ out[pos].iov_len = sizeof(struct icmp6_hdr);
+
+ if (pos == CLAT_POS_TRANSPORTHDR && is_icmp_error(icmp->type) && icmp6_type != ICMP6_PARAM_PROB) {
+ // An ICMP error we understand, one level deep.
+ // Translate the nested packet (the one that caused the error).
+ clat_packet_len = ipv4_packet(out, pos + 1, payload, payload_size);
+
+ // The pseudo-header checksum was calculated on the transport length of the original IPv4
+ // packet that we were asked to translate. This transport length is 20 bytes smaller than it
+ // needs to be, because the ICMP error contains an IPv4 header, which we will be translating to
+ // an IPv6 header, which is 20 bytes longer. Fix it up here.
+ // We only need to do this for ICMP->ICMPv6, not ICMPv6->ICMP, because ICMP does not use the
+ // pseudo-header when calculating its checksum (as the IPv4 header has its own checksum).
+ checksum = checksum + htons(20);
+ } else if (icmp6_type == ICMP6_ECHO_REQUEST || icmp6_type == ICMP6_ECHO_REPLY) {
+ // Ping packet.
+ icmp6_targ->icmp6_id = icmp->un.echo.id;
+ icmp6_targ->icmp6_seq = icmp->un.echo.sequence;
+ out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload;
+ out[CLAT_POS_PAYLOAD].iov_len = payload_size;
+ clat_packet_len = CLAT_POS_PAYLOAD + 1;
+ } else {
+ // Unknown type/code. The type/code conversion functions have already logged an error.
+ return 0;
+ }
+
+ icmp6_targ->icmp6_cksum = 0; // Checksum field must be 0 when calculating checksum.
+ icmp6_targ->icmp6_cksum = packet_checksum(checksum, out, pos);
+
+ return clat_packet_len;
+}
+
+/* function: icmp6_to_icmp
+ * translate ipv6 icmp to ipv4 icmp
+ * out - output packet
+ * icmp6 - source packet icmp6 header
+ * payload - icmp6 payload
+ * payload_size - size of payload
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int icmp6_to_icmp(clat_packet out, clat_packet_index pos, const struct icmp6_hdr *icmp6,
+ const uint8_t *payload, size_t payload_size) {
+ struct icmphdr *icmp_targ = out[pos].iov_base;
+ uint8_t icmp_type;
+ int clat_packet_len;
+
+ memset(icmp_targ, 0, sizeof(struct icmphdr));
+
+ icmp_type = icmp6_to_icmp_type(icmp6->icmp6_type, icmp6->icmp6_code);
+ icmp_targ->type = icmp_type;
+ icmp_targ->code = icmp6_to_icmp_code(icmp6->icmp6_type, icmp6->icmp6_code);
+
+ out[pos].iov_len = sizeof(struct icmphdr);
+
+ if (pos == CLAT_POS_TRANSPORTHDR && is_icmp6_error(icmp6->icmp6_type) &&
+ icmp_type != ICMP_PARAMETERPROB) {
+ // An ICMPv6 error we understand, one level deep.
+ // Translate the nested packet (the one that caused the error).
+ clat_packet_len = ipv6_packet(out, pos + 1, payload, payload_size);
+ } else if (icmp_type == ICMP_ECHO || icmp_type == ICMP_ECHOREPLY) {
+ // Ping packet.
+ icmp_targ->un.echo.id = icmp6->icmp6_id;
+ icmp_targ->un.echo.sequence = icmp6->icmp6_seq;
+ out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload;
+ out[CLAT_POS_PAYLOAD].iov_len = payload_size;
+ clat_packet_len = CLAT_POS_PAYLOAD + 1;
+ } else {
+ // Unknown type/code. The type/code conversion functions have already logged an error.
+ return 0;
+ }
+
+ icmp_targ->checksum = 0; // Checksum field must be 0 when calculating checksum.
+ icmp_targ->checksum = packet_checksum(0, out, pos);
+
+ return clat_packet_len;
+}
+
+/* function: generic_packet
+ * takes a generic IP packet and sets it up for translation
+ * out - output packet
+ * pos - position in the output packet of the transport header
+ * payload - pointer to IP payload
+ * len - size of ip payload
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int generic_packet(clat_packet out, clat_packet_index pos, const uint8_t *payload, size_t len) {
+ out[pos].iov_len = 0;
+ out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload;
+ out[CLAT_POS_PAYLOAD].iov_len = len;
+
+ return CLAT_POS_PAYLOAD + 1;
+}
+
+/* function: udp_packet
+ * takes a udp packet and sets it up for translation
+ * out - output packet
+ * udp - pointer to udp header in packet
+ * old_sum - pseudo-header checksum of old header
+ * new_sum - pseudo-header checksum of new header
+ * len - size of ip payload
+ */
+int udp_packet(clat_packet out, clat_packet_index pos, const struct udphdr *udp, uint32_t old_sum,
+ uint32_t new_sum, size_t len) {
+ const uint8_t *payload;
+ size_t payload_size;
+
+ if (len < sizeof(struct udphdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "udp_packet/(too small)");
+ return 0;
+ }
+
+ payload = (const uint8_t *)(udp + 1);
+ payload_size = len - sizeof(struct udphdr);
+
+ return udp_translate(out, pos, udp, old_sum, new_sum, payload, payload_size);
+}
+
+/* function: tcp_packet
+ * takes a tcp packet and sets it up for translation
+ * out - output packet
+ * tcp - pointer to tcp header in packet
+ * checksum - pseudo-header checksum
+ * len - size of ip payload
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int tcp_packet(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp, uint32_t old_sum,
+ uint32_t new_sum, size_t len) {
+ const uint8_t *payload;
+ size_t payload_size, header_size;
+
+ if (len < sizeof(struct tcphdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "tcp_packet/(too small)");
+ return 0;
+ }
+
+ if (tcp->doff < 5) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "tcp_packet/tcp header length set to less than 5: %x", tcp->doff);
+ return 0;
+ }
+
+ if ((size_t)tcp->doff * 4 > len) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "tcp_packet/tcp header length set too large: %x", tcp->doff);
+ return 0;
+ }
+
+ header_size = tcp->doff * 4;
+ payload = ((const uint8_t *)tcp) + header_size;
+ payload_size = len - header_size;
+
+ return tcp_translate(out, pos, tcp, header_size, old_sum, new_sum, payload, payload_size);
+}
+
+/* function: udp_translate
+ * common between ipv4/ipv6 - setup checksum and send udp packet
+ * out - output packet
+ * udp - udp header
+ * old_sum - pseudo-header checksum of old header
+ * new_sum - pseudo-header checksum of new header
+ * payload - tcp payload
+ * payload_size - size of payload
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int udp_translate(clat_packet out, clat_packet_index pos, const struct udphdr *udp,
+ uint32_t old_sum, uint32_t new_sum, const uint8_t *payload, size_t payload_size) {
+ struct udphdr *udp_targ = out[pos].iov_base;
+
+ memcpy(udp_targ, udp, sizeof(struct udphdr));
+
+ out[pos].iov_len = sizeof(struct udphdr);
+ out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload;
+ out[CLAT_POS_PAYLOAD].iov_len = payload_size;
+
+ if (udp_targ->check) {
+ udp_targ->check = ip_checksum_adjust(udp->check, old_sum, new_sum);
+ } else {
+ // Zero checksums are special. RFC 768 says, "An all zero transmitted checksum value means that
+ // the transmitter generated no checksum (for debugging or for higher level protocols that
+ // don't care)." However, in IPv6 zero UDP checksums were only permitted by RFC 6935 (2013). So
+ // for safety we recompute it.
+ udp_targ->check = 0; // Checksum field must be 0 when calculating checksum.
+ udp_targ->check = packet_checksum(new_sum, out, pos);
+ }
+
+ // RFC 768: "If the computed checksum is zero, it is transmitted as all ones (the equivalent
+ // in one's complement arithmetic)."
+ if (!udp_targ->check) {
+ udp_targ->check = 0xffff;
+ }
+
+ return CLAT_POS_PAYLOAD + 1;
+}
+
+/* function: tcp_translate
+ * common between ipv4/ipv6 - setup checksum and send tcp packet
+ * out - output packet
+ * tcp - tcp header
+ * header_size - size of tcp header including options
+ * checksum - partial checksum covering ipv4/ipv6 header
+ * payload - tcp payload
+ * payload_size - size of payload
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp,
+ size_t header_size, uint32_t old_sum, uint32_t new_sum, const uint8_t *payload,
+ size_t payload_size) {
+ struct tcphdr *tcp_targ = out[pos].iov_base;
+ out[pos].iov_len = header_size;
+
+ if (header_size > MAX_TCP_HDR) {
+ // A TCP header cannot be more than MAX_TCP_HDR bytes long because it's a 4-bit field that
+ // counts in 4-byte words. So this can never happen unless there is a bug in the caller.
+ logmsg(ANDROID_LOG_ERROR, "tcp_translate: header too long %d > %d, truncating", header_size,
+ MAX_TCP_HDR);
+ header_size = MAX_TCP_HDR;
+ }
+
+ memcpy(tcp_targ, tcp, header_size);
+
+ out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload;
+ out[CLAT_POS_PAYLOAD].iov_len = payload_size;
+
+ tcp_targ->check = ip_checksum_adjust(tcp->check, old_sum, new_sum);
+
+ return CLAT_POS_PAYLOAD + 1;
+}
+
+// Weak symbol so we can override it in the unit test.
+void send_rawv6(int fd, clat_packet out, int iov_len) __attribute__((weak));
+
+void send_rawv6(int fd, clat_packet out, int iov_len) {
+ // A send on a raw socket requires a destination address to be specified even if the socket's
+ // protocol is IPPROTO_RAW. This is the address that will be used in routing lookups; the
+ // destination address in the packet header only affects what appears on the wire, not where the
+ // packet is sent to.
+ static struct sockaddr_in6 sin6 = { AF_INET6, 0, 0, { { { 0, 0, 0, 0 } } }, 0 };
+ static struct msghdr msg = {
+ .msg_name = &sin6,
+ .msg_namelen = sizeof(sin6),
+ };
+
+ msg.msg_iov = out, msg.msg_iovlen = iov_len,
+ sin6.sin6_addr = ((struct ip6_hdr *)out[CLAT_POS_IPHDR].iov_base)->ip6_dst;
+ sendmsg(fd, &msg, 0);
+}
+
+/* function: translate_packet
+ * takes a packet, translates it, and writes it to fd
+ * fd - fd to write translated packet to
+ * to_ipv6 - true if translating to ipv6, false if translating to ipv4
+ * packet - packet
+ * packetsize - size of packet
+ */
+void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packetsize) {
+ int iov_len = 0;
+
+ // Allocate buffers for all packet headers.
+ struct tun_pi tun_targ;
+ char iphdr[sizeof(struct ip6_hdr)];
+ char fraghdr[sizeof(struct ip6_frag)];
+ char transporthdr[MAX_TCP_HDR];
+ char icmp_iphdr[sizeof(struct ip6_hdr)];
+ char icmp_fraghdr[sizeof(struct ip6_frag)];
+ char icmp_transporthdr[MAX_TCP_HDR];
+
+ // iovec of the packets we'll send. This gets passed down to the translation functions.
+ clat_packet out = {
+ { &tun_targ, 0 }, // Tunnel header.
+ { iphdr, 0 }, // IP header.
+ { fraghdr, 0 }, // Fragment header.
+ { transporthdr, 0 }, // Transport layer header.
+ { icmp_iphdr, 0 }, // ICMP error inner IP header.
+ { icmp_fraghdr, 0 }, // ICMP error fragmentation header.
+ { icmp_transporthdr, 0 }, // ICMP error transport layer header.
+ { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload.
+ };
+
+ if (to_ipv6) {
+ iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize);
+ if (iov_len > 0) {
+ send_rawv6(fd, out, iov_len);
+ }
+ } else {
+ iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize);
+ if (iov_len > 0) {
+ fill_tun_header(&tun_targ, ETH_P_IP);
+ out[CLAT_POS_TUNHDR].iov_len = sizeof(tun_targ);
+ writev(fd, out, iov_len);
+ }
+ }
+}
diff --git a/clatd/translate.h b/clatd/translate.h
new file mode 100644
index 0000000..0e520f7
--- /dev/null
+++ b/clatd/translate.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * translate.h - translate from one version of ip to another
+ */
+#ifndef __TRANSLATE_H__
+#define __TRANSLATE_H__
+
+#include <linux/icmp.h>
+#include <linux/if_tun.h>
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#include "clatd.h"
+#include "common.h"
+
+#define MAX_TCP_HDR (15 * 4) // Data offset field is 4 bits and counts in 32-bit words.
+
+// Calculates the checksum over all the packet components starting from pos.
+uint16_t packet_checksum(uint32_t checksum, clat_packet packet, clat_packet_index pos);
+
+// Returns the total length of the packet components after pos.
+uint16_t packet_length(clat_packet packet, clat_packet_index pos);
+
+// Returns true iff the given IPv6 address is in the plat subnet.
+int is_in_plat_subnet(const struct in6_addr *addr6);
+
+// Functions to create tun, IPv4, and IPv6 headers.
+void fill_tun_header(struct tun_pi *tun_header, uint16_t proto);
+void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protocol,
+ const struct ip6_hdr *old_header);
+void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol,
+ const struct iphdr *old_header);
+
+// Translate and send packets.
+void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packetsize);
+
+// Translate IPv4 and IPv6 packets.
+int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len);
+int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len);
+
+// Deal with fragmented packets.
+size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ,
+ const struct iphdr *old_header);
+uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ);
+
+// Deal with fragmented packets.
+size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ,
+ const struct iphdr *old_header);
+uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ);
+
+// Translate ICMP packets.
+int icmp_to_icmp6(clat_packet out, clat_packet_index pos, const struct icmphdr *icmp,
+ uint32_t checksum, const uint8_t *payload, size_t payload_size);
+int icmp6_to_icmp(clat_packet out, clat_packet_index pos, const struct icmp6_hdr *icmp6,
+ const uint8_t *payload, size_t payload_size);
+
+// Translate generic IP packets.
+int generic_packet(clat_packet out, clat_packet_index pos, const uint8_t *payload, size_t len);
+
+// Translate TCP and UDP packets.
+int tcp_packet(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp, uint32_t old_sum,
+ uint32_t new_sum, size_t len);
+int udp_packet(clat_packet out, clat_packet_index pos, const struct udphdr *udp, uint32_t old_sum,
+ uint32_t new_sum, size_t len);
+
+int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp,
+ size_t header_size, uint32_t old_sum, uint32_t new_sum, const uint8_t *payload,
+ size_t payload_size);
+int udp_translate(clat_packet out, clat_packet_index pos, const struct udphdr *udp,
+ uint32_t old_sum, uint32_t new_sum, const uint8_t *payload, size_t payload_size);
+
+#endif /* __TRANSLATE_H__ */