Merge changes from topic "iinterceptor"
* changes: Add VTS for Netlink Interceptor Add default implementation for Nlinterceptor Move interceptor AIDL definition
This commit is contained in:
commit
8c42fdd095
24 changed files with 1466 additions and 0 deletions
|
@ -331,6 +331,13 @@
|
|||
<instance>default</instance>
|
||||
</interface>
|
||||
</hal>
|
||||
<hal format="aidl" optional="true">
|
||||
<name>android.hardware.net.nlinterceptor</name>
|
||||
<interface>
|
||||
<name>IInterceptor</name>
|
||||
<instance>default</instance>
|
||||
</interface>
|
||||
</hal>
|
||||
<hal format="aidl" optional="true">
|
||||
<name>android.hardware.oemlock</name>
|
||||
<version>1</version>
|
||||
|
|
36
wifi/netlinkinterceptor/aidl/Android.bp
Normal file
36
wifi/netlinkinterceptor/aidl/Android.bp
Normal file
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// 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"],
|
||||
}
|
||||
|
||||
aidl_interface {
|
||||
name: "android.hardware.net.nlinterceptor",
|
||||
vendor_available: true,
|
||||
srcs: ["android/hardware/net/nlinterceptor/*.aidl"],
|
||||
stability: "vintf",
|
||||
backend: {
|
||||
java: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.net.nlinterceptor;
|
||||
@VintfStability
|
||||
interface IInterceptor {
|
||||
int createSocket(in int nlFamily, in int clientNlPid, in String clientName);
|
||||
void closeSocket(in int nlFamily, in int interceptorNlPid);
|
||||
void subscribeGroup(in int nlFamily, in int interceptorNlPid, in int nlGroup);
|
||||
void unsubscribeGroup(in int nlFamily, in int interceptorNlPid, in int nlGroup);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.net.nlinterceptor;
|
||||
@VintfStability
|
||||
interface IInterceptor {
|
||||
android.hardware.net.nlinterceptor.InterceptedSocket createSocket(in int nlFamily, in int clientNlPid, in String clientName);
|
||||
void closeSocket(in android.hardware.net.nlinterceptor.InterceptedSocket handle);
|
||||
void subscribeGroup(in android.hardware.net.nlinterceptor.InterceptedSocket handle, in int nlGroup);
|
||||
void unsubscribeGroup(in android.hardware.net.nlinterceptor.InterceptedSocket handle, in int nlGroup);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.net.nlinterceptor;
|
||||
@VintfStability
|
||||
parcelable InterceptedSocket {
|
||||
int nlFamily;
|
||||
int portId;
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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 android.hardware.net.nlinterceptor;
|
||||
|
||||
import android.hardware.net.nlinterceptor.InterceptedSocket;
|
||||
|
||||
/**
|
||||
* Netlink Interceptor
|
||||
*
|
||||
* This HAL provides a way for Android services to route their Netlink traffic to a location other
|
||||
* than the Kernel. One might want to do this for a variety of reasons:
|
||||
* -> Route Netlink traffic to a different host.
|
||||
* -> Route Netlink traffic to a different VM.
|
||||
* -> Convert Netlink commands into proprietary vendor hardware commands.
|
||||
*
|
||||
* Some important notes regarding Netlink Interceptor.
|
||||
* -> All int values are treated as unsigned.
|
||||
* -> Users of Netlink Interceptor must close their sockets with closeSocket manually.
|
||||
* -> PID != process ID. In this case, it is "port ID", a unique number assigned by the kernel to a
|
||||
* given Netlink socket.
|
||||
* -> Netlink PIDs are only unique per family. This means that for all NETLINK_GENERIC sockets,
|
||||
* there can only be one socket with PID "1234". HOWEVER, there can ALSO be a Netlink socket
|
||||
* using NETLINK_ROUTE which has a PID of "1234". Hence, in order to uniquely identify a Netlink
|
||||
* socket, both the PID and Netlink Family are required.
|
||||
*/
|
||||
@VintfStability
|
||||
interface IInterceptor {
|
||||
/**
|
||||
* Creates a Netlink socket on both the HU and TCU, and a bi-directional gRPC stream to carry
|
||||
* data between them. This must be closed by the caller with closeSocket().
|
||||
*
|
||||
* @param nlFamily - Netlink Family. Support for families other than NETLINK_GENERIC is still
|
||||
* experimental.
|
||||
* @param clientNlPid - Port ID of the caller's Netlink socket.
|
||||
* @param clientName - Human readable name of the caller. Used for debugging.
|
||||
*
|
||||
* @return InterceptedSocket identifying the socket on the HU allocated for the caller.
|
||||
*/
|
||||
InterceptedSocket createSocket(in int nlFamily, in int clientNlPid, in String clientName);
|
||||
|
||||
/**
|
||||
* Closes a socket and gRPC stream given the socket's identifier. This must be invoked manually
|
||||
* by the caller of createSocket().
|
||||
*
|
||||
* @param handle - unique identifier for a socket returned by createSocket.
|
||||
*/
|
||||
void closeSocket(in InterceptedSocket handle);
|
||||
|
||||
/**
|
||||
* Subscribes a socket on the TCU to a Netlink multicast group.
|
||||
*
|
||||
* @param handle - unique identifier for a socket returned by createSocket.
|
||||
* @param nlGroup - A single Netlink multicast group that the caller wants to subscribe to.
|
||||
*/
|
||||
void subscribeGroup(in InterceptedSocket handle, in int nlGroup);
|
||||
|
||||
/**
|
||||
* Unsubscribes a socket on the TCU from a Netlink multicast group.
|
||||
*
|
||||
* @param handle - unique identifier for a socket returned by createSocket.
|
||||
* @param nlGroup - A single Netlink multicast group that the caller wants to unsubscribe from.
|
||||
*/
|
||||
void unsubscribeGroup(in InterceptedSocket handle, in int nlGroup);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 android.hardware.net.nlinterceptor;
|
||||
|
||||
/**
|
||||
* Unique identifier for a Netlink socket.
|
||||
*/
|
||||
@VintfStability
|
||||
parcelable InterceptedSocket {
|
||||
/**
|
||||
* Netlink family of the identified socket
|
||||
*/
|
||||
int nlFamily;
|
||||
|
||||
/**
|
||||
* Netlink port ID of the identified socket.
|
||||
*/
|
||||
int portId;
|
||||
}
|
39
wifi/netlinkinterceptor/aidl/default/Android.bp
Normal file
39
wifi/netlinkinterceptor/aidl/default/Android.bp
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
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",
|
||||
],
|
||||
}
|
129
wifi/netlinkinterceptor/aidl/default/InterceptorRelay.cpp
Normal file
129
wifi/netlinkinterceptor/aidl/default/InterceptorRelay.cpp
Normal file
|
@ -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
|
93
wifi/netlinkinterceptor/aidl/default/InterceptorRelay.h
Normal file
93
wifi/netlinkinterceptor/aidl/default/InterceptorRelay.h
Normal file
|
@ -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
|
121
wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.cpp
Normal file
121
wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.cpp
Normal file
|
@ -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
|
57
wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.h
Normal file
57
wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.h
Normal file
|
@ -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
|
2
wifi/netlinkinterceptor/aidl/default/OWNERS
Normal file
2
wifi/netlinkinterceptor/aidl/default/OWNERS
Normal file
|
@ -0,0 +1,2 @@
|
|||
chrisweir@google.com
|
||||
twasilczyk@google.com
|
|
@ -0,0 +1,4 @@
|
|||
service nlinterceptor /vendor/bin/hw/android.hardware.net.nlinterceptor-service.default
|
||||
class hal
|
||||
user root
|
||||
group system inet
|
|
@ -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>
|
47
wifi/netlinkinterceptor/aidl/default/service.cpp
Normal file
47
wifi/netlinkinterceptor/aidl/default/service.cpp
Normal file
|
@ -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;
|
||||
}
|
29
wifi/netlinkinterceptor/aidl/default/util.cpp
Normal file
29
wifi/netlinkinterceptor/aidl/default/util.cpp
Normal file
|
@ -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
|
52
wifi/netlinkinterceptor/aidl/default/util.h
Normal file
52
wifi/netlinkinterceptor/aidl/default/util.h
Normal file
|
@ -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
|
56
wifi/netlinkinterceptor/libnlinterceptor/Android.bp
Normal file
56
wifi/netlinkinterceptor/libnlinterceptor/Android.bp
Normal file
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
cc_defaults {
|
||||
name: "nlinterceptor@defaults",
|
||||
cpp_std: "experimental",
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wsuggest-override",
|
||||
"-Werror",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libutils",
|
||||
],
|
||||
sanitize: {
|
||||
address: true,
|
||||
undefined: true,
|
||||
all_undefined: true,
|
||||
fuzzer: true,
|
||||
cfi: true,
|
||||
integer_overflow: true,
|
||||
scs: true,
|
||||
},
|
||||
strip: {
|
||||
keep_symbols_and_debug_frame: true,
|
||||
},
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "libnlinterceptor",
|
||||
defaults: ["nlinterceptor@defaults"],
|
||||
vendor_available: true,
|
||||
shared_libs: [
|
||||
"android.hardware.net.nlinterceptor-V1-ndk",
|
||||
"libbinder_ndk",
|
||||
],
|
||||
srcs: [
|
||||
"libnlinterceptor.cpp",
|
||||
],
|
||||
export_include_dirs: ["include"],
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <aidl/android/hardware/net/nlinterceptor/InterceptedSocket.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace android::nlinterceptor {
|
||||
|
||||
/**
|
||||
* Wrapper structure to uniquely identifies a socket that Netlink Interceptor
|
||||
* has allocated for us.
|
||||
*/
|
||||
struct InterceptedSocket {
|
||||
uint32_t nlFamily;
|
||||
uint32_t portId;
|
||||
|
||||
InterceptedSocket(
|
||||
::aidl::android::hardware::net::nlinterceptor::InterceptedSocket sock);
|
||||
InterceptedSocket(uint32_t nlFamily, uint32_t portId);
|
||||
|
||||
bool operator<(const InterceptedSocket& other) const;
|
||||
operator sockaddr_nl() const;
|
||||
operator ::aidl::android::hardware::net::nlinterceptor::InterceptedSocket()
|
||||
const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Output stream operator for InterceptedSocket
|
||||
*/
|
||||
std::ostream& operator<<(std::ostream& os, const InterceptedSocket& sock);
|
||||
|
||||
/**
|
||||
* Checks if an instance Netlink Interceptor exists.
|
||||
*
|
||||
* \return true if supported, false if not.
|
||||
*/
|
||||
bool isEnabled();
|
||||
|
||||
/**
|
||||
* Asks Netlink Interceptor to allocate a socket to which we can send Netlink
|
||||
* traffic.
|
||||
*
|
||||
* \param clientSocket - File descriptor for the client's Netlink socket.
|
||||
* \param clientName - Human readable name of the client application.
|
||||
* \return Identifier for the socket created by Netlink Interceptor, nullopt on
|
||||
* error.
|
||||
*/
|
||||
std::optional<InterceptedSocket> createSocket(base::borrowed_fd clientSocket,
|
||||
const std::string& clientName);
|
||||
|
||||
/**
|
||||
* Asks Netlink Interceptor to close a socket that it created for us previously,
|
||||
* if it exists.
|
||||
*
|
||||
* \param sock - Identifier for the socket created by Netlink Interceptor.
|
||||
*/
|
||||
void closeSocket(const InterceptedSocket& sock);
|
||||
|
||||
/**
|
||||
* Asks Netlink Interceptor to subscribe a socket that it created for us
|
||||
* previously to a specified multicast group.
|
||||
*
|
||||
* \param sock - Identifier for the socket created by Netlink Interceptor.
|
||||
* \param group - A single Netlink multicast group for which we would like to
|
||||
* receive events.
|
||||
* \return true for success, false if something went wrong.
|
||||
*/
|
||||
bool subscribe(const InterceptedSocket& sock, uint32_t group);
|
||||
|
||||
/**
|
||||
* Asks Netlink Interceptor to unsubscribe a socket that it created for us
|
||||
* previously from a specified multicast group.
|
||||
*
|
||||
* \param sock - Identifier for the socket created by Netlink Interceptor.
|
||||
* \param group - A single Netlink multicast group for which we no longer wish
|
||||
* to receive events.
|
||||
* \return true for success, false if something went wrong.
|
||||
*/
|
||||
bool unsubscribe(const InterceptedSocket& sock, uint32_t group);
|
||||
} // namespace android::nlinterceptor
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// C wrappers for libnlinterceptor
|
||||
struct android_nlinterceptor_InterceptedSocket {
|
||||
uint32_t nlFamily;
|
||||
uint32_t portId;
|
||||
};
|
||||
|
||||
bool android_nlinterceptor_isEnabled();
|
||||
|
||||
bool android_nlinterceptor_createSocket(
|
||||
int clientSocketFd, const char* clientName,
|
||||
struct android_nlinterceptor_InterceptedSocket* interceptedSocket);
|
||||
|
||||
void android_nlinterceptor_closeSocket(
|
||||
const struct android_nlinterceptor_InterceptedSocket* sock);
|
||||
|
||||
bool android_nlinterceptor_subscribe(
|
||||
const struct android_nlinterceptor_InterceptedSocket* sock, uint32_t group);
|
||||
|
||||
bool android_nlinterceptor_unsubscribe(
|
||||
const struct android_nlinterceptor_InterceptedSocket* sock, uint32_t group);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
174
wifi/netlinkinterceptor/libnlinterceptor/libnlinterceptor.cpp
Normal file
174
wifi/netlinkinterceptor/libnlinterceptor/libnlinterceptor.cpp
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* 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 <aidl/android/hardware/net/nlinterceptor/IInterceptor.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/macros.h>
|
||||
#include <android/binder_manager.h>
|
||||
#include <libnlinterceptor/libnlinterceptor.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace android::nlinterceptor {
|
||||
using namespace std::string_literals;
|
||||
using namespace ::aidl::android::hardware::net::nlinterceptor;
|
||||
using base::borrowed_fd;
|
||||
using AidlInterceptedSocket =
|
||||
::aidl::android::hardware::net::nlinterceptor::InterceptedSocket;
|
||||
|
||||
static const auto kServiceName = IInterceptor::descriptor + "/default"s;
|
||||
|
||||
InterceptedSocket::InterceptedSocket(
|
||||
::aidl::android::hardware::net::nlinterceptor::InterceptedSocket sock)
|
||||
: nlFamily(sock.nlFamily), portId(sock.portId) {}
|
||||
|
||||
InterceptedSocket::InterceptedSocket(uint32_t nlFamily, uint32_t portId)
|
||||
: nlFamily(nlFamily), portId(portId) {}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const InterceptedSocket& sock) {
|
||||
return os << "family: " << sock.nlFamily << ", portId: " << sock.portId;
|
||||
}
|
||||
|
||||
bool InterceptedSocket::operator<(const InterceptedSocket& other) const {
|
||||
if (nlFamily != other.nlFamily) {
|
||||
return nlFamily < other.nlFamily;
|
||||
}
|
||||
return portId < other.portId;
|
||||
}
|
||||
|
||||
InterceptedSocket::operator sockaddr_nl() const {
|
||||
return {
|
||||
.nl_family = AF_NETLINK,
|
||||
.nl_pad = 0,
|
||||
.nl_pid = portId,
|
||||
.nl_groups = 0,
|
||||
};
|
||||
}
|
||||
|
||||
InterceptedSocket::operator AidlInterceptedSocket() const {
|
||||
return {
|
||||
.nlFamily = static_cast<int32_t>(nlFamily),
|
||||
.portId = static_cast<int32_t>(portId),
|
||||
};
|
||||
}
|
||||
|
||||
bool isEnabled() {
|
||||
static std::mutex supportedMutex;
|
||||
static std::optional<bool> interceptorSupported;
|
||||
// Avoid querying service manager when we can cache the result.
|
||||
if (interceptorSupported.has_value()) return *interceptorSupported;
|
||||
std::lock_guard lock(supportedMutex);
|
||||
if (interceptorSupported.has_value()) return *interceptorSupported;
|
||||
|
||||
if (!AServiceManager_isDeclared(kServiceName.c_str())) {
|
||||
interceptorSupported = false;
|
||||
return false;
|
||||
}
|
||||
interceptorSupported = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static IInterceptor& getInstance() {
|
||||
static std::mutex instanceMutex;
|
||||
static std::shared_ptr<IInterceptor> interceptorInstance;
|
||||
CHECK(isEnabled()) << "Can't getInstance! Interceptor not supported!";
|
||||
// Don't overwrite the pointer once we've acquired it.
|
||||
if (interceptorInstance != nullptr) return *interceptorInstance;
|
||||
std::lock_guard lock(instanceMutex);
|
||||
if (interceptorInstance != nullptr) return *interceptorInstance;
|
||||
interceptorInstance = IInterceptor::fromBinder(
|
||||
ndk::SpAIBinder(AServiceManager_waitForService(kServiceName.c_str())));
|
||||
CHECK(interceptorInstance != nullptr)
|
||||
<< "Failed to get Netlink Interceptor service!";
|
||||
return *interceptorInstance;
|
||||
}
|
||||
|
||||
std::optional<InterceptedSocket> createSocket(borrowed_fd clientSocket,
|
||||
const std::string& clientName) {
|
||||
sockaddr_nl nladdr = {};
|
||||
socklen_t nlsize = sizeof(nladdr);
|
||||
if (getsockname(clientSocket.get(), reinterpret_cast<sockaddr*>(&nladdr),
|
||||
&nlsize) < 0) {
|
||||
PLOG(ERROR) << "Failed to get pid of fd passed by " << clientName;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
::aidl::android::hardware::net::nlinterceptor::InterceptedSocket
|
||||
interceptedSocket;
|
||||
auto aidlStatus = getInstance().createSocket(
|
||||
nladdr.nl_family, nladdr.nl_pid, clientName, &interceptedSocket);
|
||||
if (!aidlStatus.isOk()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return InterceptedSocket{nladdr.nl_family,
|
||||
uint32_t(interceptedSocket.portId)};
|
||||
}
|
||||
|
||||
void closeSocket(const InterceptedSocket& sock) {
|
||||
auto aidlStatus = getInstance().closeSocket(sock);
|
||||
if (!aidlStatus.isOk()) {
|
||||
LOG(ERROR) << "Failed to close socket with pid = " << sock.portId;
|
||||
}
|
||||
}
|
||||
|
||||
bool subscribe(const InterceptedSocket& sock, uint32_t group) {
|
||||
auto aidlStatus = getInstance().subscribeGroup(sock, group);
|
||||
return aidlStatus.isOk();
|
||||
}
|
||||
|
||||
bool unsubscribe(const InterceptedSocket& sock, uint32_t group) {
|
||||
auto aidlStatus = getInstance().unsubscribeGroup(sock, group);
|
||||
return aidlStatus.isOk();
|
||||
}
|
||||
|
||||
extern "C" bool android_nlinterceptor_isEnabled() { return isEnabled(); }
|
||||
|
||||
extern "C" bool android_nlinterceptor_createSocket(
|
||||
int clientSocketFd, const char* clientName,
|
||||
android_nlinterceptor_InterceptedSocket* interceptedSocket) {
|
||||
if (!clientName || clientSocketFd <= 0) return false;
|
||||
const auto maybeSocket =
|
||||
createSocket(borrowed_fd(clientSocketFd), clientName);
|
||||
if (!maybeSocket) return false;
|
||||
*interceptedSocket = {.nlFamily = maybeSocket->nlFamily,
|
||||
.portId = maybeSocket->portId};
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" void android_nlinterceptor_closeSocket(
|
||||
const android_nlinterceptor_InterceptedSocket* sock) {
|
||||
if (!sock) {
|
||||
LOG(ERROR) << "Can't close socket identified by a null pointer!";
|
||||
return;
|
||||
}
|
||||
closeSocket({sock->nlFamily, sock->portId});
|
||||
}
|
||||
|
||||
extern "C" bool android_nlinterceptor_subscribe(
|
||||
const android_nlinterceptor_InterceptedSocket* sock, uint32_t group) {
|
||||
if (!sock) return false;
|
||||
return subscribe({sock->nlFamily, sock->portId}, group);
|
||||
}
|
||||
|
||||
extern "C" bool android_nlinterceptor_unsubscribe(
|
||||
const android_nlinterceptor_InterceptedSocket* sock, uint32_t group) {
|
||||
if (!sock) return false;
|
||||
return unsubscribe({sock->nlFamily, sock->portId}, group);
|
||||
}
|
||||
|
||||
} // namespace android::nlinterceptor
|
2
wifi/netlinkinterceptor/vts/OWNERS
Normal file
2
wifi/netlinkinterceptor/vts/OWNERS
Normal file
|
@ -0,0 +1,2 @@
|
|||
chrisweir@google.com
|
||||
twasilczyk@google.com
|
51
wifi/netlinkinterceptor/vts/functional/Android.bp
Normal file
51
wifi/netlinkinterceptor/vts/functional/Android.bp
Normal file
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// 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_test {
|
||||
name: "VtsHalNetlinkInterceptorV1_0Test",
|
||||
defaults: [
|
||||
"VtsHalTargetTestDefaults",
|
||||
"use_libaidlvintf_gtest_helper_static",
|
||||
],
|
||||
cpp_std: "experimental",
|
||||
srcs: [
|
||||
"interceptor_aidl_test.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.net.nlinterceptor-V1-ndk",
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
],
|
||||
static_libs: [
|
||||
"libgmock",
|
||||
"android.hardware.automotive.can@libnetdevice",
|
||||
"libnl++",
|
||||
],
|
||||
test_suites: [
|
||||
"general-tests",
|
||||
"vts",
|
||||
],
|
||||
disable_framework: true,
|
||||
}
|
195
wifi/netlinkinterceptor/vts/functional/interceptor_aidl_test.cpp
Normal file
195
wifi/netlinkinterceptor/vts/functional/interceptor_aidl_test.cpp
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* 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 <aidl/Gtest.h>
|
||||
#include <aidl/Vintf.h>
|
||||
#include <aidl/android/hardware/net/nlinterceptor/IInterceptor.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android/binder_manager.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <libnetdevice/libnetdevice.h>
|
||||
#include <libnl++/MessageFactory.h>
|
||||
#include <libnl++/Socket.h>
|
||||
#include <libnl++/printer.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
using aidl::android::hardware::net::nlinterceptor::IInterceptor;
|
||||
using AidlInterceptedSocket =
|
||||
::aidl::android::hardware::net::nlinterceptor::InterceptedSocket;
|
||||
using namespace std::chrono_literals;
|
||||
using namespace std::string_literals;
|
||||
|
||||
class InterceptorAidlTest : public ::testing::TestWithParam<std::string> {
|
||||
public:
|
||||
virtual void SetUp() override {
|
||||
android::base::SetDefaultTag("InterceptorAidlTest");
|
||||
android::base::SetMinimumLogSeverity(android::base::VERBOSE);
|
||||
const auto instance = IInterceptor::descriptor + "/default"s;
|
||||
mNlInterceptorService = IInterceptor::fromBinder(
|
||||
ndk::SpAIBinder(AServiceManager_getService(instance.c_str())));
|
||||
|
||||
ASSERT_NE(mNlInterceptorService, nullptr);
|
||||
mSocket = std::make_unique<android::nl::Socket>(NETLINK_ROUTE);
|
||||
ASSERT_TRUE(mSocket->getPid().has_value());
|
||||
|
||||
// If the test broke last run, clean up our mess, don't worry about "no
|
||||
// such device".
|
||||
if (android::netdevice::del(mTestIfaceName)) {
|
||||
LOG(WARNING) << "Test interface wasn't cleaned up on previous run!";
|
||||
}
|
||||
}
|
||||
|
||||
void multicastReceiver();
|
||||
|
||||
std::shared_ptr<IInterceptor> mNlInterceptorService;
|
||||
std::unique_ptr<android::nl::Socket> mSocket;
|
||||
bool mRunning;
|
||||
bool mGotMulticast;
|
||||
const std::string mTestIfaceName = "interceptorvts0";
|
||||
};
|
||||
|
||||
TEST_P(InterceptorAidlTest, createSocketTest) {
|
||||
// Ask IInterceptor for a socket.
|
||||
AidlInterceptedSocket interceptedSocket;
|
||||
auto aidlStatus = mNlInterceptorService->createSocket(
|
||||
NETLINK_ROUTE, *(mSocket->getPid()), "createSocketTest",
|
||||
&interceptedSocket);
|
||||
ASSERT_TRUE(aidlStatus.isOk());
|
||||
ASSERT_NE(interceptedSocket.portId, 0);
|
||||
uint32_t interceptorPid = interceptedSocket.portId;
|
||||
|
||||
// Ask the kernel to tell us what interfaces are available.
|
||||
android::nl::MessageFactory<rtgenmsg> req(RTM_GETLINK,
|
||||
NLM_F_REQUEST | NLM_F_DUMP);
|
||||
req->rtgen_family = AF_PACKET;
|
||||
sockaddr_nl sa = {.nl_family = AF_NETLINK,
|
||||
.nl_pad = 0,
|
||||
.nl_pid = interceptorPid,
|
||||
.nl_groups = 0};
|
||||
EXPECT_TRUE(mSocket->send(req, sa));
|
||||
|
||||
// We'll likely get back several messages, as indicated by the MULTI flag.
|
||||
unsigned received = 0;
|
||||
for (const auto msg : *mSocket) {
|
||||
ASSERT_NE(msg->nlmsg_type, NLMSG_ERROR);
|
||||
++received;
|
||||
break;
|
||||
if (msg->nlmsg_type == NLMSG_DONE) {
|
||||
// TODO(202548749): NLMSG_DONE on NETLINK_ROUTE doesn't work?
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_GE(received, 1);
|
||||
|
||||
// Close the socket and make sure it's stopped working.
|
||||
aidlStatus = mNlInterceptorService->closeSocket(interceptedSocket);
|
||||
EXPECT_TRUE(aidlStatus.isOk());
|
||||
EXPECT_FALSE(mSocket->send(req, sa));
|
||||
}
|
||||
|
||||
static bool isSocketReadable(const short revents) {
|
||||
return 0 != (revents & POLLIN);
|
||||
}
|
||||
|
||||
static bool isSocketBad(const short revents) {
|
||||
return 0 != (revents & (POLLERR | POLLHUP | POLLNVAL));
|
||||
}
|
||||
|
||||
void InterceptorAidlTest::multicastReceiver() {
|
||||
pollfd fds[] = {
|
||||
mSocket->preparePoll(POLLIN),
|
||||
};
|
||||
while (mRunning) {
|
||||
if (poll(fds, 1, 300) < 0) {
|
||||
PLOG(FATAL) << "poll failed";
|
||||
return;
|
||||
}
|
||||
const auto nlsockEvents = fds[0].revents;
|
||||
ASSERT_FALSE(isSocketBad(nlsockEvents));
|
||||
if (!isSocketReadable(nlsockEvents)) continue;
|
||||
|
||||
const auto [msgMaybe, sa] = mSocket->receiveFrom();
|
||||
ASSERT_TRUE(msgMaybe.has_value());
|
||||
auto msg = *msgMaybe;
|
||||
|
||||
// Multicast messages have 0 for their pid and sequence number.
|
||||
if (msg->nlmsg_pid == 0 && msg->nlmsg_seq == 0) {
|
||||
mGotMulticast = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(InterceptorAidlTest, subscribeGroupTest) {
|
||||
// Ask IInterceptor for a socket.
|
||||
AidlInterceptedSocket interceptedSocket;
|
||||
auto aidlStatus = mNlInterceptorService->createSocket(
|
||||
NETLINK_ROUTE, *(mSocket->getPid()), "subscribeGroupTest",
|
||||
&interceptedSocket);
|
||||
ASSERT_TRUE(aidlStatus.isOk());
|
||||
ASSERT_TRUE(interceptedSocket.portId != 0);
|
||||
|
||||
// Listen for interface up/down events.
|
||||
aidlStatus =
|
||||
mNlInterceptorService->subscribeGroup(interceptedSocket, RTNLGRP_LINK);
|
||||
ASSERT_TRUE(aidlStatus.isOk());
|
||||
|
||||
// Start a thread to receive a multicast
|
||||
mRunning = true;
|
||||
mGotMulticast = false;
|
||||
std::thread successfulReceiver(&InterceptorAidlTest::multicastReceiver,
|
||||
this);
|
||||
|
||||
// TODO(201695162): use futures with wait_for instead of a sleep_for().
|
||||
std::this_thread::sleep_for(50ms);
|
||||
// create a network interface and bring it up to trigger a multicast event.
|
||||
ASSERT_TRUE(android::netdevice::add(mTestIfaceName, /*type=*/"dummy"));
|
||||
ASSERT_TRUE(android::netdevice::up(mTestIfaceName));
|
||||
std::this_thread::sleep_for(50ms);
|
||||
EXPECT_TRUE(mGotMulticast);
|
||||
mRunning = false;
|
||||
successfulReceiver.join();
|
||||
|
||||
// Stop listening to interface up/down events.
|
||||
aidlStatus = mNlInterceptorService->unsubscribeGroup(interceptedSocket,
|
||||
RTNLGRP_LINK);
|
||||
ASSERT_TRUE(aidlStatus.isOk());
|
||||
|
||||
// This time, we should hear nothing.
|
||||
mGotMulticast = false;
|
||||
mRunning = true;
|
||||
std::thread unsuccessfulReceiver(&InterceptorAidlTest::multicastReceiver,
|
||||
this);
|
||||
std::this_thread::sleep_for(50ms);
|
||||
ASSERT_TRUE(android::netdevice::down(mTestIfaceName));
|
||||
ASSERT_TRUE(android::netdevice::del(mTestIfaceName));
|
||||
std::this_thread::sleep_for(50ms);
|
||||
EXPECT_FALSE(mGotMulticast);
|
||||
mRunning = false;
|
||||
unsuccessfulReceiver.join();
|
||||
|
||||
aidlStatus = mNlInterceptorService->closeSocket(interceptedSocket);
|
||||
EXPECT_TRUE(aidlStatus.isOk());
|
||||
}
|
||||
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InterceptorAidlTest);
|
||||
INSTANTIATE_TEST_SUITE_P(PerInstance, InterceptorAidlTest,
|
||||
testing::ValuesIn(android::getAidlHalInstanceNames(
|
||||
IInterceptor::descriptor)),
|
||||
android::PrintInstanceNameToString);
|
Loading…
Reference in a new issue