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:655d0850acgit-subtree-split:2007830075
This commit is contained in:
commit
d89d5884eb
27 changed files with 3626 additions and 0 deletions
8
clatd/.clang-format
Normal file
8
clatd/.clang-format
Normal 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
118
clatd/Android.bp
Normal 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
5
clatd/BUGS
Normal 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
201
clatd/LICENSE
Normal 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
3
clatd/METADATA
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
third_party {
|
||||||
|
license_type: NOTICE
|
||||||
|
}
|
||||||
0
clatd/MODULE_LICENSE_APACHE2
Normal file
0
clatd/MODULE_LICENSE_APACHE2
Normal file
189
clatd/NOTICE
Normal file
189
clatd/NOTICE
Normal 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
2
clatd/OWNERS
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
set noparent
|
||||||
|
file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
|
||||||
5
clatd/PREUPLOAD.cfg
Normal file
5
clatd/PREUPLOAD.cfg
Normal 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
10
clatd/TEST_MAPPING
Normal 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
307
clatd/clatd.c
Normal 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
81
clatd/clatd.h
Normal 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
835
clatd/clatd_test.cpp
Normal 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
40
clatd/common.h
Normal 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
48
clatd/config.h
Normal 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
24
clatd/debug.h
Normal 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
250
clatd/dump.c
Normal 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
46
clatd/dump.h
Normal 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
177
clatd/icmp.c
Normal 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
45
clatd/icmp.h
Normal 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
Loading…
Reference in a new issue