summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/Android.mk2
-rw-r--r--client/NetdClient.cpp76
-rw-r--r--include/FwmarkCommand.h16
-rw-r--r--include/NetdClient.h34
-rw-r--r--server/FwmarkServer.cpp45
5 files changed, 135 insertions, 38 deletions
diff --git a/client/Android.mk b/client/Android.mk
index 2a4878c4..0332deaf 100644
--- a/client/Android.mk
+++ b/client/Android.mk
@@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_C_INCLUDES := system/netd/include
+LOCAL_C_INCLUDES := bionic/libc/dns/include system/netd/include
LOCAL_MODULE := libnetd_client
LOCAL_SRC_FILES := FwmarkClient.cpp NetdClient.cpp
diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp
index 2e8390c8..31c2e4d2 100644
--- a/client/NetdClient.cpp
+++ b/client/NetdClient.cpp
@@ -14,8 +14,11 @@
* limitations under the License.
*/
+#include "NetdClient.h"
+
#include "FwmarkClient.h"
#include "FwmarkCommand.h"
+#include "resolv_netid.h"
#include <sys/socket.h>
#include <unistd.h>
@@ -31,14 +34,17 @@ int closeFdAndRestoreErrno(int fd) {
typedef int (*ConnectFunctionType)(int, const sockaddr*, socklen_t);
typedef int (*AcceptFunctionType)(int, sockaddr*, socklen_t*);
+typedef unsigned (*NetIdForResolvFunctionType)(unsigned);
+// 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.
ConnectFunctionType libcConnect = 0;
AcceptFunctionType libcAccept = 0;
int netdClientConnect(int sockfd, const sockaddr* addr, socklen_t addrlen) {
if (FwmarkClient::shouldSetFwmark(sockfd, addr)) {
- char data[] = {FWMARK_COMMAND_ON_CONNECT};
- if (!FwmarkClient().send(data, sizeof(data), sockfd)) {
+ FwmarkCommand command = {FwmarkCommand::ON_CONNECT, 0};
+ if (!FwmarkClient().send(&command, sizeof(command), sockfd)) {
return -1;
}
}
@@ -59,14 +65,49 @@ int netdClientAccept(int sockfd, sockaddr* addr, socklen_t* addrlen) {
addr = &socketAddress;
}
if (FwmarkClient::shouldSetFwmark(acceptedSocket, addr)) {
- char data[] = {FWMARK_COMMAND_ON_ACCEPT};
- if (!FwmarkClient().send(data, sizeof(data), acceptedSocket)) {
+ FwmarkCommand command = {FwmarkCommand::ON_ACCEPT, 0};
+ if (!FwmarkClient().send(&command, sizeof(command), acceptedSocket)) {
return closeFdAndRestoreErrno(acceptedSocket);
}
}
return acceptedSocket;
}
+// TODO: Convert to C++11 std::atomic<unsigned>.
+volatile sig_atomic_t netIdForProcess = NETID_UNSET;
+volatile sig_atomic_t netIdForResolv = NETID_UNSET;
+
+unsigned getNetworkForResolv(unsigned netId) {
+ if (netId != NETID_UNSET) {
+ return netId;
+ }
+ netId = netIdForProcess;
+ if (netId != NETID_UNSET) {
+ return netId;
+ }
+ return netIdForResolv;
+}
+
+bool setNetworkForTarget(unsigned netId, volatile sig_atomic_t* target) {
+ if (netId == NETID_UNSET) {
+ *target = netId;
+ return true;
+ }
+ // Verify that we are allowed to use |netId|, by creating a socket and trying to have it marked
+ // with the netId. Don't create an AF_INET socket, because then the creation itself might cause
+ // another check with the fwmark server (see netdClientSocket()), which would be wasteful.
+ int socketFd = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (socketFd < 0) {
+ return false;
+ }
+ bool status = setNetworkForSocket(netId, socketFd);
+ closeFdAndRestoreErrno(socketFd);
+ if (status) {
+ *target = netId;
+ }
+ return status;
+}
+
} // namespace
extern "C" void netdClientInitConnect(ConnectFunctionType* function) {
@@ -82,3 +123,30 @@ extern "C" void netdClientInitAccept(AcceptFunctionType* function) {
*function = netdClientAccept;
}
}
+
+extern "C" void netdClientInitNetIdForResolv(NetIdForResolvFunctionType* function) {
+ if (function) {
+ *function = getNetworkForResolv;
+ }
+}
+
+extern "C" unsigned getNetworkForProcess() {
+ return netIdForProcess;
+}
+
+extern "C" bool setNetworkForSocket(unsigned netId, int socketFd) {
+ if (socketFd < 0) {
+ errno = EBADF;
+ return false;
+ }
+ FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId};
+ return FwmarkClient().send(&command, sizeof(command), socketFd);
+}
+
+extern "C" bool setNetworkForProcess(unsigned netId) {
+ return setNetworkForTarget(netId, &netIdForProcess);
+}
+
+extern "C" bool setNetworkForResolv(unsigned netId) {
+ return setNetworkForTarget(netId, &netIdForResolv);
+}
diff --git a/include/FwmarkCommand.h b/include/FwmarkCommand.h
index 5854a816..9ec93e20 100644
--- a/include/FwmarkCommand.h
+++ b/include/FwmarkCommand.h
@@ -17,13 +17,15 @@
#ifndef NETD_INCLUDE_FWMARK_COMMAND_H
#define NETD_INCLUDE_FWMARK_COMMAND_H
-#include <stdint.h>
-
// Commands sent from clients to the fwmark server to mark sockets (i.e., set their SO_MARK).
-const uint8_t FWMARK_COMMAND_ON_CREATE = 0;
-const uint8_t FWMARK_COMMAND_ON_CONNECT = 1;
-const uint8_t FWMARK_COMMAND_ON_ACCEPT = 2;
-const uint8_t FWMARK_COMMAND_SELECT_NETWORK = 3;
-const uint8_t FWMARK_COMMAND_PROTECT_FROM_VPN = 4;
+struct FwmarkCommand {
+ enum {
+ ON_ACCEPT,
+ ON_CONNECT,
+ SELECT_NETWORK,
+ PROTECT_FROM_VPN,
+ } cmdId;
+ unsigned netId; // used only in the SELECT_NETWORK command; ignored otherwise.
+};
#endif // NETD_INCLUDE_FWMARK_COMMAND_H
diff --git a/include/NetdClient.h b/include/NetdClient.h
new file mode 100644
index 00000000..c3f24095
--- /dev/null
+++ b/include/NetdClient.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef NETD_INCLUDE_NETD_CLIENT_H
+#define NETD_INCLUDE_NETD_CLIENT_H
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+bool setNetworkForSocket(unsigned netId, int socketFd);
+
+unsigned getNetworkForProcess(void);
+bool setNetworkForProcess(unsigned netId);
+
+bool setNetworkForResolv(unsigned netId);
+
+__END_DECLS
+
+#endif // NETD_INCLUDE_NETD_CLIENT_H
diff --git a/server/FwmarkServer.cpp b/server/FwmarkServer.cpp
index 187d4bfb..56127ca0 100644
--- a/server/FwmarkServer.cpp
+++ b/server/FwmarkServer.cpp
@@ -21,16 +21,9 @@
#include "NetworkController.h"
#include "PermissionsController.h"
-
#include <sys/socket.h>
#include <unistd.h>
-namespace {
-
-const int MAX_COMMAND_LENGTH = 16;
-
-} // namespace
-
FwmarkServer::FwmarkServer(NetworkController* networkController,
PermissionsController* permissionsController)
: SocketListener("fwmarkd", true),
@@ -57,9 +50,10 @@ bool FwmarkServer::onDataAvailable(SocketClient* client) {
}
void FwmarkServer::processClient(SocketClient* client, int* fd) {
- char command[MAX_COMMAND_LENGTH] = {};
+ FwmarkCommand command;
+
iovec iov;
- iov.iov_base = command;
+ iov.iov_base = &command;
iov.iov_len = sizeof(command);
msghdr message;
@@ -81,6 +75,11 @@ void FwmarkServer::processClient(SocketClient* client, int* fd) {
return;
}
+ if (messageLength != sizeof(command)) {
+ errno = EINVAL;
+ return;
+ }
+
cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
if (cmsgh && cmsgh->cmsg_level == SOL_SOCKET && cmsgh->cmsg_type == SCM_RIGHTS &&
cmsgh->cmsg_len == CMSG_LEN(sizeof(*fd))) {
@@ -98,14 +97,18 @@ void FwmarkServer::processClient(SocketClient* client, int* fd) {
return;
}
- switch (command[0]) {
- case FWMARK_COMMAND_ON_CREATE: {
- // on socket creation
- // TODO
+ switch (command.cmdId) {
+ case FwmarkCommand::ON_ACCEPT: {
+ // Called after a socket accept(). The kernel would've marked the netId into the socket
+ // already, so we just need to check permissions here.
+ if (!mPermissionsController->isUserPermittedOnNetwork(client->getUid(), fwmark.netId)) {
+ errno = EPERM;
+ return;
+ }
break;
}
- case FWMARK_COMMAND_ON_CONNECT: {
+ case FwmarkCommand::ON_CONNECT: {
// Set the netId (of the default network) into the fwmark, if it has not already been
// set explicitly. Called before a socket connect() happens.
if (!fwmark.explicitlySelected) {
@@ -114,23 +117,13 @@ void FwmarkServer::processClient(SocketClient* client, int* fd) {
break;
}
- case FWMARK_COMMAND_ON_ACCEPT: {
- // Called after a socket accept(). The kernel would've marked the netId into the socket
- // already, so we just need to check permissions here.
- if (!mPermissionsController->isUserPermittedOnNetwork(client->getUid(), fwmark.netId)) {
- errno = EPERM;
- return;
- }
- break;
- }
-
- case FWMARK_COMMAND_SELECT_NETWORK: {
+ case FwmarkCommand::SELECT_NETWORK: {
// set socket mark
// TODO
break;
}
- case FWMARK_COMMAND_PROTECT_FROM_VPN: {
+ case FwmarkCommand::PROTECT_FROM_VPN: {
// set vpn protect
// TODO
break;