/* * Copyright (C) 2017 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. */ #define LOG_TAG "VtsOffloadConfigV1_0TargetTest" #include #include #include #include #include #include #include #include #include #include #include #include using android::base::StringPrintf; using android::base::unique_fd; using android::hardware::hidl_handle; using android::hardware::hidl_string; using android::hardware::Return; using android::hardware::tetheroffload::config::V1_0::IOffloadConfig; using android::hardware::Void; using android::sp; #define ASSERT_TRUE_CALLBACK \ [&](bool success, const hidl_string& errMsg) { ASSERT_TRUE(success) << errMsg.c_str(); } #define ASSERT_FALSE_CALLBACK \ [&](bool success, const hidl_string& errMsg) { ASSERT_FALSE(success) << errMsg.c_str(); } const unsigned kFd1Groups = NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY; const unsigned kFd2Groups = NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY; inline const sockaddr* asSockaddr(const sockaddr_nl* nladdr) { return reinterpret_cast(nladdr); } int netlinkSocket(int protocol, unsigned groups) { unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, protocol)); if (s.get() < 0) { return -errno; } const struct sockaddr_nl bind_addr = { .nl_family = AF_NETLINK, .nl_pad = 0, .nl_pid = 0, .nl_groups = groups, }; if (::bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) != 0) { return -errno; } const struct sockaddr_nl kernel_addr = { .nl_family = AF_NETLINK, .nl_pad = 0, .nl_pid = 0, .nl_groups = groups, }; if (::connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) { return -errno; } return s.release(); } int netlinkSocket(unsigned groups) { return netlinkSocket(NETLINK_NETFILTER, groups); } // Test environment for OffloadConfig HIDL HAL. class OffloadConfigHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { public: // get the test environment singleton static OffloadConfigHidlEnvironment* Instance() { static OffloadConfigHidlEnvironment* instance = new OffloadConfigHidlEnvironment; return instance; } virtual void registerTestServices() override { registerTestService(); } private: OffloadConfigHidlEnvironment() {} }; class OffloadConfigHidlTest : public testing::VtsHalHidlTargetTestBase { public: virtual void SetUp() override { config = testing::VtsHalHidlTargetTestBase::getService( OffloadConfigHidlEnvironment::Instance()->getServiceName()); ASSERT_NE(nullptr, config.get()) << "Could not get HIDL instance"; } virtual void TearDown() override {} sp config; }; // Ensure handles can be set with correct socket options. TEST_F(OffloadConfigHidlTest, TestSetHandles) { // Try multiple times in a row to see if it provokes file descriptor leaks. for (int i = 0; i < 1024; i++) { unique_fd fd1(netlinkSocket(kFd1Groups)); if (fd1.get() < 0) { ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno)); FAIL(); } native_handle_t* const nativeHandle1 = native_handle_create(1, 0); nativeHandle1->data[0] = fd1.release(); hidl_handle h1; h1.setTo(nativeHandle1, true); unique_fd fd2(netlinkSocket(kFd2Groups)); if (fd2.get() < 0) { ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno)); FAIL(); } native_handle_t* const nativeHandle2 = native_handle_create(1, 0); nativeHandle2->data[0] = fd2.release(); hidl_handle h2; h2.setTo(nativeHandle2, true); const Return ret = config->setHandles(h1, h2, ASSERT_TRUE_CALLBACK); ASSERT_TRUE(ret.isOk()); } } // Passing a handle without an associated file descriptor should return an error // (e.g. "Failed Input Checks"). Check that this occurs when both FDs are empty. TEST_F(OffloadConfigHidlTest, TestSetHandleNone) { native_handle_t* const nativeHandle1 = native_handle_create(0, 0); hidl_handle h1; h1.setTo(nativeHandle1, true); native_handle_t* const nativeHandle2 = native_handle_create(0, 0); hidl_handle h2; h2.setTo(nativeHandle2, true); const Return ret = config->setHandles(h1, h2, ASSERT_FALSE_CALLBACK); ASSERT_TRUE(ret.isOk()); } // Passing a handle without an associated file descriptor should return an error // (e.g. "Failed Input Checks"). Check that this occurs when FD2 is empty. TEST_F(OffloadConfigHidlTest, TestSetHandle1Only) { unique_fd fd1(netlinkSocket(kFd1Groups)); if (fd1.get() < 0) { ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno)); FAIL(); } native_handle_t* const nativeHandle1 = native_handle_create(1, 0); nativeHandle1->data[0] = fd1.release(); hidl_handle h1; h1.setTo(nativeHandle1, true); native_handle_t* const nativeHandle2 = native_handle_create(0, 0); hidl_handle h2; h2.setTo(nativeHandle2, true); const Return ret = config->setHandles(h1, h2, ASSERT_FALSE_CALLBACK); ASSERT_TRUE(ret.isOk()); } // Passing a handle without an associated file descriptor should return an error // (e.g. "Failed Input Checks"). Check that this occurs when FD1 is empty. TEST_F(OffloadConfigHidlTest, TestSetHandle2OnlyNotOk) { native_handle_t* const nativeHandle1 = native_handle_create(0, 0); hidl_handle h1; h1.setTo(nativeHandle1, true); unique_fd fd2(netlinkSocket(kFd2Groups)); if (fd2.get() < 0) { ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno)); FAIL(); } native_handle_t* const nativeHandle2 = native_handle_create(1, 0); nativeHandle2->data[0] = fd2.release(); hidl_handle h2; h2.setTo(nativeHandle2, true); const Return ret = config->setHandles(h1, h2, ASSERT_FALSE_CALLBACK); ASSERT_TRUE(ret.isOk()); } int main(int argc, char** argv) { ::testing::AddGlobalTestEnvironment(OffloadConfigHidlEnvironment::Instance()); ::testing::InitGoogleTest(&argc, argv); OffloadConfigHidlEnvironment::Instance()->init(&argc, argv); int status = RUN_ALL_TESTS(); ALOGE("Test result with status=%d", status); return status; }