diff options
Diffstat (limited to 'wifi/netlinkinterceptor/aidl/default')
11 files changed, 591 insertions, 0 deletions
diff --git a/wifi/netlinkinterceptor/aidl/default/Android.bp b/wifi/netlinkinterceptor/aidl/default/Android.bp new file mode 100644 index 0000000000..5227e51fac --- /dev/null +++ b/wifi/netlinkinterceptor/aidl/default/Android.bp @@ -0,0 +1,48 @@ +// +// Copyright (C) 2021 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +cc_binary { + name: "android.hardware.net.nlinterceptor-service.default", + init_rc: ["nlinterceptor-default.rc"], + vintf_fragments: ["nlinterceptor-default.xml"], + vendor: true, + relative_install_path: "hw", + defaults: ["nlinterceptor@defaults"], + shared_libs: [ + "android.hardware.net.nlinterceptor-V1-ndk", + "libbase", + "libbinder_ndk", + ], + static_libs: [ + "libnlinterceptor", + "libnl++", + ], + srcs: [ + "InterceptorRelay.cpp", + "NetlinkInterceptor.cpp", + "service.cpp", + "util.cpp", + ], +} diff --git a/wifi/netlinkinterceptor/aidl/default/InterceptorRelay.cpp b/wifi/netlinkinterceptor/aidl/default/InterceptorRelay.cpp new file mode 100644 index 0000000000..ded9122a6f --- /dev/null +++ b/wifi/netlinkinterceptor/aidl/default/InterceptorRelay.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2021 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 "InterceptorRelay.h" + +#include <android-base/logging.h> +#include <libnl++/printer.h> +#include <poll.h> + +#include <chrono> + +#include "util.h" + +namespace android::nlinterceptor { +using namespace std::chrono_literals; + +static constexpr std::chrono::milliseconds kPollTimeout = 300ms; +static constexpr bool kSuperVerbose = true; + +InterceptorRelay::InterceptorRelay(uint32_t nlFamily, uint32_t clientNlPid, + const std::string& clientName) + : mClientName(clientName), + mNlSocket(std::make_optional<nl::Socket>(nlFamily, 0, 0)), + mClientNlPid(clientNlPid) {} + +InterceptorRelay::~InterceptorRelay() { + mRunning = false; + if (mRelayThread.joinable()) mRelayThread.join(); +} + +uint32_t InterceptorRelay::getPid() { + auto pidMaybe = mNlSocket->getPid(); + CHECK(pidMaybe.has_value()) << "Failed to get pid of nl::Socket!"; + return *pidMaybe; +} + +void InterceptorRelay::relayMessages() { + pollfd fds[] = { + mNlSocket->preparePoll(POLLIN), + }; + while (mRunning) { + if (poll(fds, countof(fds), kPollTimeout.count()) < 0) { + PLOG(FATAL) << "poll failed"; + return; + } + const auto nlsockEvents = fds[0].revents; + + if (isSocketBad(nlsockEvents)) { + LOG(ERROR) << "Netlink socket is bad"; + mRunning = false; + return; + } + if (!isSocketReadable(nlsockEvents)) continue; + + const auto [msgMaybe, sa] = mNlSocket->receiveFrom(); + if (!msgMaybe.has_value()) { + LOG(ERROR) << "Failed to receive Netlink data!"; + mRunning = false; + return; + } + const auto msg = *msgMaybe; + if (!msg.firstOk()) { + LOG(ERROR) << "Netlink packet is malformed!"; + // Test messages might be empty, this isn't fatal. + continue; + } + if constexpr (kSuperVerbose) { + LOG(VERBOSE) << "[" << mClientName + << "] nlMsg: " << nl::toString(msg, NETLINK_GENERIC); + } + + uint32_t destinationPid = 0; + if (sa.nl_pid == 0) { + destinationPid = mClientNlPid; + } + + if (!mNlSocket->send(msg, destinationPid)) { + LOG(ERROR) << "Failed to send Netlink message!"; + mRunning = false; + return; + } + } + LOG(VERBOSE) << "[" << mClientName << "] Exiting relay thread!"; +} + +bool InterceptorRelay::start() { + if (mRunning) { + LOG(ERROR) + << "Can't relay messages: InterceptorRelay is already running!"; + return false; + } + if (mRelayThread.joinable()) { + LOG(ERROR) << "relay thread is already running!"; + return false; + } + if (!mNlSocket.has_value()) { + LOG(ERROR) << "Netlink socket not initialized!"; + return false; + } + + mRunning = true; + mRelayThread = std::thread(&InterceptorRelay::relayMessages, this); + + LOG(VERBOSE) << "Relay threads initialized"; + return true; +} + +bool InterceptorRelay::subscribeGroup(uint32_t nlGroup) { + return mNlSocket->addMembership(nlGroup); +} + +bool InterceptorRelay::unsubscribeGroup(uint32_t nlGroup) { + return mNlSocket->dropMembership(nlGroup); +} + +} // namespace android::nlinterceptor diff --git a/wifi/netlinkinterceptor/aidl/default/InterceptorRelay.h b/wifi/netlinkinterceptor/aidl/default/InterceptorRelay.h new file mode 100644 index 0000000000..0178c90e1e --- /dev/null +++ b/wifi/netlinkinterceptor/aidl/default/InterceptorRelay.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 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 <libnl++/Socket.h> + +#include <mutex> +#include <thread> + +namespace android::nlinterceptor { + +class InterceptorRelay { + public: + /** + * Wrapper around the netlink socket and thread which relays messages. + * + * \param nlFamily - netlink family to use for the netlink socket. + * \param clientNlPid - pid of the client netlink socket. + * \param clientName - name of the client to be used for debugging. + */ + InterceptorRelay(uint32_t nlFamily, uint32_t clientNlPid, + const std::string& clientName); + + /** + * Stops the relay thread if running and destroys itself. + */ + ~InterceptorRelay(); + + /** + * Returns the PID of the internal Netlink socket. + * + * \return value of PID, + */ + uint32_t getPid(); + + /** + * Spawns relay thread. + */ + bool start(); + + /** + * Subscribes the internal socket to a single Netlink multicast group. + * + * \param nlGroup - Netlink group to subscribe to. + * \returns - true for success, false for failure. + */ + bool subscribeGroup(uint32_t nlGroup); + + /** + * Unsubscribes the internal socket from a single Netlink multicast group. + * + * \param nlGroup - Netlink group to unsubscribe from. + * \returns - true for success, false for failure. + */ + bool unsubscribeGroup(uint32_t nlGroup); + + private: + std::string mClientName; ///< Name of client (Wificond, for example). + std::optional<nl::Socket> mNlSocket; + const uint32_t mClientNlPid = 0; ///< pid of client NL socket. + + /** + * If set to true, the relay thread should be running. Setting this to false + * stops the relay thread. + */ + std::atomic_bool mRunning = false; + + /** + * Reads incoming Netlink messages destined for mNlSocket. If from the + * kernel, the message is relayed to the client specified in the + * constructor. Otherwise, the message is relayed to the kernel. This will + * run as long as mRunning is set to true. + */ + void relayMessages(); + + std::thread mRelayThread; +}; + +} // namespace android::nlinterceptor diff --git a/wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.cpp b/wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.cpp new file mode 100644 index 0000000000..908ecf22de --- /dev/null +++ b/wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2021 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 "NetlinkInterceptor.h" + +#include <android-base/logging.h> +#include <libnl++/Socket.h> + +namespace android::nlinterceptor { + +ndk::ScopedAStatus NetlinkInterceptor::createSocket( + int32_t nlFamilyAidl, int32_t clientNlPidAidl, + const std::string& clientName, AidlInterceptedSocket* interceptedSocket) { + auto nlFamily = static_cast<uint32_t>(nlFamilyAidl); + auto clientNlPid = static_cast<uint32_t>(clientNlPidAidl); + uint32_t interceptorNlPid = 0; + + std::unique_ptr<InterceptorRelay> interceptor = + std::make_unique<InterceptorRelay>(nlFamily, clientNlPid, clientName); + + interceptorNlPid = interceptor->getPid(); + + if (interceptorNlPid == 0) { + LOG(ERROR) << "Failed to create a Netlink socket for " << clientName + << ", " << nlFamily << ":" << clientNlPid; + return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR)); + } + + if (mClientMap.find({nlFamily, interceptorNlPid}) != mClientMap.end()) { + LOG(ERROR) << "A socket with pid " << interceptorNlPid + << " already exists!"; + return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR)); + } + + if (!interceptor->start()) { + LOG(ERROR) << "Failed to start interceptor thread!"; + return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR)); + } + + if (!mClientMap + .emplace(InterceptedSocket(nlFamily, interceptorNlPid), + std::move(interceptor)) + .second) { + // If this happens, it is very bad. + LOG(FATAL) << "Failed to insert interceptor instance with pid " + << interceptorNlPid << " into map!"; + return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR)); + } + + interceptedSocket->nlFamily = nlFamily; + interceptedSocket->portId = interceptorNlPid; + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus NetlinkInterceptor::closeSocket( + const AidlInterceptedSocket& interceptedSocket) { + InterceptedSocket sock(interceptedSocket); + + auto interceptorIt = mClientMap.find(sock); + if (interceptorIt == mClientMap.end()) { + LOG(ERROR) << "closeSocket Failed! No such socket " << sock; + return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR)); + } + mClientMap.erase(interceptorIt); + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus NetlinkInterceptor::subscribeGroup( + const AidlInterceptedSocket& interceptedSocket, int32_t nlGroupAidl) { + InterceptedSocket sock(interceptedSocket); + auto nlGroup = static_cast<uint32_t>(nlGroupAidl); + + auto interceptorIt = mClientMap.find(sock); + if (interceptorIt == mClientMap.end()) { + LOG(ERROR) << "subscribeGroup failed! No such socket " << sock; + return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR)); + } + + auto& interceptor = interceptorIt->second; + if (!interceptor->subscribeGroup(nlGroup)) { + LOG(ERROR) << "Failed to subscribe " << sock << " to " << nlGroup; + return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR)); + } + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus NetlinkInterceptor::unsubscribeGroup( + const AidlInterceptedSocket& interceptedSocket, int32_t nlGroupAidl) { + InterceptedSocket sock(interceptedSocket); + auto nlGroup = static_cast<uint32_t>(nlGroupAidl); + + auto interceptorIt = mClientMap.find(sock); + if (interceptorIt == mClientMap.end()) { + LOG(ERROR) << "unsubscribeGroup failed! No such socket " << sock; + return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR)); + } + + auto& interceptor = interceptorIt->second; + if (!interceptor->unsubscribeGroup(nlGroup)) { + LOG(ERROR) << "Failed to unsubscribe " << sock << " from " << nlGroup; + return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR)); + } + return ndk::ScopedAStatus::ok(); +} +} // namespace android::nlinterceptor diff --git a/wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.h b/wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.h new file mode 100644 index 0000000000..8345654fa7 --- /dev/null +++ b/wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021 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 <aidl/android/hardware/net/nlinterceptor/BnInterceptor.h> +#include <libnlinterceptor/libnlinterceptor.h> + +#include <map> + +#include "InterceptorRelay.h" + +namespace android::nlinterceptor { + +class NetlinkInterceptor + : public ::aidl::android::hardware::net::nlinterceptor::BnInterceptor { + using ClientMap = + std::map<::android::nlinterceptor::InterceptedSocket, + std::unique_ptr<::android::nlinterceptor::InterceptorRelay>>; + + using AidlInterceptedSocket = + ::aidl::android::hardware::net::nlinterceptor::InterceptedSocket; + + public: + ndk::ScopedAStatus createSocket( + int32_t nlFamily, int32_t clientNlPid, const std::string& clientName, + AidlInterceptedSocket* interceptedSocket) override; + + ndk::ScopedAStatus closeSocket( + const AidlInterceptedSocket& interceptedSocket) override; + + ndk::ScopedAStatus subscribeGroup( + const AidlInterceptedSocket& interceptedSocket, + int32_t nlGroup) override; + + ndk::ScopedAStatus unsubscribeGroup( + const AidlInterceptedSocket& interceptedSocket, + int32_t nlGroup) override; + + private: + ClientMap mClientMap; +}; + +} // namespace android::nlinterceptor diff --git a/wifi/netlinkinterceptor/aidl/default/OWNERS b/wifi/netlinkinterceptor/aidl/default/OWNERS new file mode 100644 index 0000000000..b738dac662 --- /dev/null +++ b/wifi/netlinkinterceptor/aidl/default/OWNERS @@ -0,0 +1,2 @@ +chrisweir@google.com +twasilczyk@google.com diff --git a/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.rc b/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.rc new file mode 100644 index 0000000000..353cb27cd0 --- /dev/null +++ b/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.rc @@ -0,0 +1,4 @@ +service nlinterceptor /vendor/bin/hw/android.hardware.net.nlinterceptor-service.default + class hal + user root + group system inet diff --git a/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.xml b/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.xml new file mode 100644 index 0000000000..d7d257e000 --- /dev/null +++ b/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.xml @@ -0,0 +1,9 @@ +<manifest version="1.0" type="device"> + <hal format="aidl"> + <name>android.hardware.net.nlinterceptor</name> + <interface> + <name>IInterceptor</name> + <instance>default</instance> + </interface> + </hal> +</manifest> diff --git a/wifi/netlinkinterceptor/aidl/default/service.cpp b/wifi/netlinkinterceptor/aidl/default/service.cpp new file mode 100644 index 0000000000..2aec3a5780 --- /dev/null +++ b/wifi/netlinkinterceptor/aidl/default/service.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 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 <android-base/logging.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> + +#include "NetlinkInterceptor.h" + +namespace android::nlinterceptor { +using namespace std::string_literals; + +static void service() { + base::SetDefaultTag("nlinterceptor"); + base::SetMinimumLogSeverity(base::VERBOSE); + LOG(DEBUG) << "Netlink Interceptor service starting..."; + + // TODO(202549296): Sometimes this causes an Address Sanitizer error. + auto interceptor = ndk::SharedRefBase::make<NetlinkInterceptor>(); + const auto instance = NetlinkInterceptor::descriptor + "/default"s; + const auto status = AServiceManager_addService( + interceptor->asBinder().get(), instance.c_str()); + CHECK(status == STATUS_OK); + + ABinderProcess_joinThreadPool(); + LOG(FATAL) << "Netlink Interceptor has stopped"; +} + +} // namespace android::nlinterceptor + +int main() { + ::android::nlinterceptor::service(); + return 0; +} diff --git a/wifi/netlinkinterceptor/aidl/default/util.cpp b/wifi/netlinkinterceptor/aidl/default/util.cpp new file mode 100644 index 0000000000..c7347472c9 --- /dev/null +++ b/wifi/netlinkinterceptor/aidl/default/util.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 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 "util.h" + +#include <poll.h> + +namespace android::nlinterceptor { + +bool isSocketReadable(const short revents) { return 0 != (revents & POLLIN); } + +bool isSocketBad(const short revents) { + return 0 != (revents & (POLLERR | POLLHUP | POLLNVAL)); +} + +} // namespace android::nlinterceptor diff --git a/wifi/netlinkinterceptor/aidl/default/util.h b/wifi/netlinkinterceptor/aidl/default/util.h new file mode 100644 index 0000000000..9b8ec63c1a --- /dev/null +++ b/wifi/netlinkinterceptor/aidl/default/util.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 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 <android-base/macros.h> + +#include <functional> + +namespace android::nlinterceptor { + +/** + * Handy-dandy helper to get the size of a statically initialized array. + * + * \param N the array to get the size of. + * \return the size of the array. + */ +template <typename T, size_t N> +size_t countof(T (&)[N]) { + return N; +} + +/** + * Helper to check if socket is readable (POLLIN is set). + * + * \param revents pollfd.revents value to check. + * \return true if socket is ready to read. + */ +bool isSocketReadable(short revents); + +/** + * Helper to check if socket is bad (POLLERR, POLLHUP or POLLNVAL is set). + * + * \param revents pollfd.revents value to check. + * \return true if socket is bad. + */ +bool isSocketBad(short revents); + +} // namespace android::nlinterceptor |