Add 'clatd/' from commit '2007830075'

Test: TreeHugger
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: I18ccf19024f0617778799f548c8707a518eefadc
git-subtree-dir: clatd
git-subtree-mainline: 655d0850ac
git-subtree-split: 2007830075
This commit is contained in:
Maciej Żenczykowski 2023-09-12 11:40:35 -07:00
commit d89d5884eb
27 changed files with 3626 additions and 0 deletions

8
clatd/.clang-format Normal file
View file

@ -0,0 +1,8 @@
BasedOnStyle: Google
AlignConsecutiveAssignments: true
AlignEscapedNewlines: Right
ColumnLimit: 100
CommentPragmas: NOLINT:.*
ContinuationIndentWidth: 2
Cpp11BracedListStyle: false
TabWidth: 2

118
clatd/Android.bp Normal file
View file

@ -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,
}

5
clatd/BUGS Normal file
View file

@ -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)

201
clatd/LICENSE Normal file
View file

@ -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.

3
clatd/METADATA Normal file
View file

@ -0,0 +1,3 @@
third_party {
license_type: NOTICE
}

View file

189
clatd/NOTICE Normal file
View file

@ -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

2
clatd/OWNERS Normal file
View file

@ -0,0 +1,2 @@
set noparent
file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking

5
clatd/PREUPLOAD.cfg Normal file
View file

@ -0,0 +1,5 @@
[Builtin Hooks]
clang_format = true
[Builtin Hooks Options]
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp

10
clatd/TEST_MAPPING Normal file
View file

@ -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" }
]
}

307
clatd/clatd.c Normal file
View file

@ -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);
}
}
}

81
clatd/clatd.h Normal file
View file

@ -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__ */

835
clatd/clatd_test.cpp Normal file
View file

@ -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");
}

40
clatd/common.h Normal file
View file

@ -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__ */

48
clatd/config.h Normal file
View file

@ -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__ */

24
clatd/debug.h Normal file
View file

@ -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__ */

250
clatd/dump.c Normal file
View file

@ -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

46
clatd/dump.h Normal file
View file

@ -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__ */

177
clatd/icmp.c Normal file
View file

@ -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;
}

45
clatd/icmp.h Normal file
View file

@ -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__ */

Some files were not shown because too many files have changed in this diff Show more