Netd callbacks for socket calls in Bionic
Netd callbacks for socket calls sendto(), sendmsg()
and sendmmsg(). It's controlled by two system properties:
[1] ro.vendor.redirect_socket_calls set once in vendor_init context,
read by libnetd_client. It determines if socket calls are shimmed.
[2] net.redirect_socket_calls.hooked set by System Server, read by
shimmed functions. It determines if shimmed functions dispatch
FwmarkCommands.
Bug: 141611769
Change-Id: I3b4a613469bb2b6c9673219217dab121cf392cd5
diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp
index d5945d0..634480b 100644
--- a/client/NetdClient.cpp
+++ b/client/NetdClient.cpp
@@ -22,6 +22,7 @@
#include <resolv.h>
#include <stdlib.h>
#include <sys/socket.h>
+#include <sys/system_properties.h>
#include <sys/un.h>
#include <unistd.h>
@@ -49,6 +50,12 @@
// Keep this in sync with CMD_BUF_SIZE in FrameworkListener.cpp.
constexpr size_t MAX_CMD_SIZE = 4096;
+// Whether sendto(), sendmsg(), sendmmsg() in libc are shimmed or not. This property is evaluated at
+// process start time and is cannot change at runtime on a given device.
+constexpr char PROPERTY_REDIRECT_SOCKET_CALLS[] = "ro.vendor.redirect_socket_calls";
+// Whether some shimmed functions dispatch FwmarkCommand or not. The property can be changed by
+// System Server at runtime. Note: accept4(), socket(), connect() are always shimmed.
+constexpr char PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED[] = "net.redirect_socket_calls.hooked";
std::atomic_uint netIdForProcess(NETID_UNSET);
std::atomic_uint netIdForResolv(NETID_UNSET);
@@ -58,12 +65,30 @@
typedef int (*SocketFunctionType)(int, int, int);
typedef unsigned (*NetIdForResolvFunctionType)(unsigned);
typedef int (*DnsOpenProxyType)();
+typedef int (*SendmmsgFunctionType)(int, const mmsghdr*, unsigned int, int);
+typedef ssize_t (*SendmsgFunctionType)(int, const msghdr*, unsigned int);
+typedef int (*SendtoFunctionType)(int, const void*, size_t, int, const sockaddr*, socklen_t);
// These variables are only modified at startup (when libc.so is loaded) and never afterwards, so
// it's okay that they are read later at runtime without a lock.
Accept4FunctionType libcAccept4 = nullptr;
ConnectFunctionType libcConnect = nullptr;
SocketFunctionType libcSocket = nullptr;
+SendmmsgFunctionType libcSendmmsg = nullptr;
+SendmsgFunctionType libcSendmsg = nullptr;
+SendtoFunctionType libcSendto = nullptr;
+
+static bool propertyValueIsTrue(const char* prop_name) {
+ char prop_value[PROP_VALUE_MAX] = {0};
+ int length = __system_property_get(prop_name, prop_value);
+ if (length > 0 && length < PROP_VALUE_MAX) {
+ prop_value[length] = '\0';
+ if (strcmp(prop_value, "true") == 0) {
+ return true;
+ }
+ }
+ return false;
+}
int checkSocket(int socketFd) {
if (socketFd < 0) {
@@ -119,8 +144,9 @@
int netdClientConnect(int sockfd, const sockaddr* addr, socklen_t addrlen) {
const bool shouldSetFwmark = shouldMarkSocket(sockfd, addr);
if (shouldSetFwmark) {
+ FwmarkConnectInfo connectInfo(0, 0, addr);
FwmarkCommand command = {FwmarkCommand::ON_CONNECT, 0, 0, 0};
- if (int error = FwmarkClient().send(&command, sockfd, nullptr)) {
+ if (int error = FwmarkClient().send(&command, sockfd, &connectInfo)) {
errno = -error;
return -1;
}
@@ -158,6 +184,48 @@
return socketFd;
}
+int netdClientSendmmsg(int sockfd, const mmsghdr* msgs, unsigned int msgcount, int flags) {
+ if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED)) {
+ const sockaddr* addr = nullptr;
+ if ((msgcount > 0) && (msgs != nullptr) && (msgs[0].msg_hdr.msg_name != nullptr)) {
+ addr = reinterpret_cast<const sockaddr*>(msgs[0].msg_hdr.msg_name);
+ if ((addr != nullptr) && (FwmarkCommand::isSupportedFamily(addr->sa_family))) {
+ FwmarkConnectInfo sendmmsgInfo(0, 0, addr);
+ FwmarkCommand command = {FwmarkCommand::ON_SENDMMSG, 0, 0, 0};
+ FwmarkClient().send(&command, sockfd, &sendmmsgInfo);
+ }
+ }
+ }
+ return libcSendmmsg(sockfd, msgs, msgcount, flags);
+}
+
+ssize_t netdClientSendmsg(int sockfd, const msghdr* msg, unsigned int flags) {
+ if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED)) {
+ const sockaddr* addr = nullptr;
+ if ((msg != nullptr) && (msg->msg_name != nullptr)) {
+ addr = reinterpret_cast<const sockaddr*>(msg->msg_name);
+ if ((addr != nullptr) && (FwmarkCommand::isSupportedFamily(addr->sa_family))) {
+ FwmarkConnectInfo sendmsgInfo(0, 0, addr);
+ FwmarkCommand command = {FwmarkCommand::ON_SENDMSG, 0, 0, 0};
+ FwmarkClient().send(&command, sockfd, &sendmsgInfo);
+ }
+ }
+ }
+ return libcSendmsg(sockfd, msg, flags);
+}
+
+int netdClientSendto(int sockfd, const void* buf, size_t bufsize, int flags, const sockaddr* addr,
+ socklen_t addrlen) {
+ if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED)) {
+ if ((addr != nullptr) && (FwmarkCommand::isSupportedFamily(addr->sa_family))) {
+ FwmarkConnectInfo sendtoInfo(0, 0, addr);
+ FwmarkCommand command = {FwmarkCommand::ON_SENDTO, 0, 0, 0};
+ FwmarkClient().send(&command, sockfd, &sendtoInfo);
+ }
+ }
+ return libcSendto(sockfd, buf, bufsize, flags, addr, addrlen);
+}
+
unsigned getNetworkForResolv(unsigned netId) {
if (netId != NETID_UNSET) {
return netId;
@@ -334,6 +402,39 @@
}
}
+extern "C" void netdClientInitSendmmsg(SendmmsgFunctionType* function) {
+ if (!propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS)) {
+ return;
+ }
+
+ if (function && *function) {
+ libcSendmmsg = *function;
+ *function = netdClientSendmmsg;
+ }
+}
+
+extern "C" void netdClientInitSendmsg(SendmsgFunctionType* function) {
+ if (!propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS)) {
+ return;
+ }
+
+ if (function && *function) {
+ libcSendmsg = *function;
+ *function = netdClientSendmsg;
+ }
+}
+
+extern "C" void netdClientInitSendto(SendtoFunctionType* function) {
+ if (!propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS)) {
+ return;
+ }
+
+ if (function && *function) {
+ libcSendto = *function;
+ *function = netdClientSendto;
+ }
+}
+
extern "C" void netdClientInitNetIdForResolv(NetIdForResolvFunctionType* function) {
if (function) {
*function = getNetworkForResolv;