diff --git a/service/native/libs/libclat/Android.bp b/service/native/libs/libclat/Android.bp new file mode 100644 index 0000000000..a45ffa6153 --- /dev/null +++ b/service/native/libs/libclat/Android.bp @@ -0,0 +1,42 @@ +// Copyright (C) 2022 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. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_library_static { + name: "libclat", + defaults: ["netd_defaults"], + srcs: ["clatutils.cpp"], + stl: "libc++_static", + static_libs: ["libip_checksum"], + export_include_dirs: ["include"], + min_sdk_version: "30", + apex_available: ["com.android.tethering"], +} + +cc_test { + name: "libclat_test", + defaults: ["netd_defaults"], + test_suites: ["device-tests"], + srcs: [ + "clatutils_test.cpp", + ], + static_libs: [ + "libbase", + "libclat", + "libip_checksum", + ], +} \ No newline at end of file diff --git a/service/native/libs/libclat/clatutils.cpp b/service/native/libs/libclat/clatutils.cpp new file mode 100644 index 0000000000..2a53ba8079 --- /dev/null +++ b/service/native/libs/libclat/clatutils.cpp @@ -0,0 +1,51 @@ +// Copyright (C) 2022 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. + +#include "libclat/clatutils.h" + +#include + +extern "C" { +#include "checksum.h" +} + +namespace android { +namespace net { +namespace clat { + +// Alters the bits in the IPv6 address to make them checksum neutral with v4 and nat64Prefix. +void makeChecksumNeutral(in6_addr* v6, const in_addr v4, const in6_addr& nat64Prefix) { + // Fill last 8 bytes of IPv6 address with random bits. + arc4random_buf(&v6->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 = (v6->s6_addr[11] << 8) + v6->s6_addr[12]; + + uint32_t c1 = ip_checksum_add(0, &v4, sizeof(v4)); + uint32_t c2 = ip_checksum_add(0, &nat64Prefix, sizeof(nat64Prefix)) + + ip_checksum_add(0, v6, sizeof(*v6)); + + uint16_t delta = ip_checksum_adjust(middlebytes, c1, c2); + v6->s6_addr[11] = delta >> 8; + v6->s6_addr[12] = delta & 0xff; +} + +} // namespace clat +} // namespace net +} // namespace android diff --git a/service/native/libs/libclat/clatutils_test.cpp b/service/native/libs/libclat/clatutils_test.cpp new file mode 100644 index 0000000000..1900abf5e2 --- /dev/null +++ b/service/native/libs/libclat/clatutils_test.cpp @@ -0,0 +1,93 @@ +// Copyright (C) 2022 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. + +#include "libclat/clatutils.h" + +#include +#include +#include + +extern "C" { +#include "checksum.h" +} + +// Default translation parameters. +static const char kIPv4LocalAddr[] = "192.0.0.4"; + +namespace android { +namespace net { +namespace clat { + +using base::StringPrintf; + +class ClatUtils : public ::testing::Test {}; + +TEST_F(ClatUtils, MakeChecksumNeutral) { + // We can't test generateIPv6Address here since it requires manipulating routing, which we can't + // do without talking to the real netd on the system. + uint32_t rand = arc4random_uniform(0xffffffff); + uint16_t rand1 = rand & 0xffff; + uint16_t rand2 = (rand >> 16) & 0xffff; + std::string v6PrefixStr = StringPrintf("2001:db8:%x:%x", rand1, rand2); + std::string v6InterfaceAddrStr = StringPrintf("%s::%x:%x", v6PrefixStr.c_str(), rand2, rand1); + std::string nat64PrefixStr = StringPrintf("2001:db8:%x:%x::", rand2, rand1); + + in_addr v4 = {inet_addr(kIPv4LocalAddr)}; + in6_addr v6InterfaceAddr; + ASSERT_TRUE(inet_pton(AF_INET6, v6InterfaceAddrStr.c_str(), &v6InterfaceAddr)); + in6_addr nat64Prefix; + ASSERT_TRUE(inet_pton(AF_INET6, nat64PrefixStr.c_str(), &nat64Prefix)); + + // Generate a boatload of random IIDs. + int onebits = 0; + uint64_t prev_iid = 0; + for (int i = 0; i < 100000; i++) { + in6_addr v6 = v6InterfaceAddr; + makeChecksumNeutral(&v6, v4, nat64Prefix); + + // Check the generated IP address is in the same prefix as the interface IPv6 address. + EXPECT_EQ(0, memcmp(&v6, &v6InterfaceAddr, 8)); + + // Check that consecutive IIDs are not the same. + uint64_t iid = *(uint64_t*)(&v6.s6_addr[8]); + ASSERT_TRUE(iid != prev_iid) + << "Two consecutive random IIDs are the same: " << std::showbase << std::hex << iid + << "\n"; + prev_iid = iid; + + // Check that the IID is checksum-neutral with the NAT64 prefix and the + // local prefix. + uint16_t c1 = ip_checksum_finish(ip_checksum_add(0, &v4, sizeof(v4))); + uint16_t c2 = ip_checksum_finish(ip_checksum_add(0, &nat64Prefix, sizeof(nat64Prefix)) + + ip_checksum_add(0, &v6, sizeof(v6))); + + if (c1 != c2) { + char v6Str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &v6, v6Str, sizeof(v6Str)); + FAIL() << "Bad IID: " << v6Str << " not checksum-neutral with " << kIPv4LocalAddr + << " and " << nat64PrefixStr.c_str() << std::showbase << std::hex + << "\n IPv4 checksum: " << c1 << "\n IPv6 checksum: " << c2 << "\n"; + } + + // Check that IIDs are roughly random and use all the bits by counting the + // total number of bits set to 1 in a random sample of 100000 generated IIDs. + onebits += __builtin_popcountll(*(uint64_t*)&iid); + } + EXPECT_LE(3190000, onebits); + EXPECT_GE(3210000, onebits); +} + +} // namespace clat +} // namespace net +} // namespace android diff --git a/service/native/libs/libclat/include/libclat/clatutils.h b/service/native/libs/libclat/include/libclat/clatutils.h new file mode 100644 index 0000000000..05c99df66d --- /dev/null +++ b/service/native/libs/libclat/include/libclat/clatutils.h @@ -0,0 +1,26 @@ +// Copyright (C) 2022 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. + +#pragma once +#include +#include + +namespace android { +namespace net { +namespace clat { + +void makeChecksumNeutral(in6_addr* v6, const in_addr v4, const in6_addr& nat64Prefix); +} // namespace clat +} // namespace net +} // namespace android