--- /dev/null
+include $(call all-subdir-makefiles)
include $(CLEAR_VARS)
-LOCAL_C_INCLUDES := bionic/libc/dns/include external/libcxx/include system/netd/include
+LOCAL_C_INCLUDES := bionic/libc/dns/include system/netd/include
LOCAL_CLANG := true
LOCAL_CPPFLAGS := -std=c++11 -Wall -Werror
LOCAL_MODULE := libnetd_client
#include "FwmarkClient.h"
+#include "FwmarkCommand.h"
+
#include <errno.h>
#include <stdlib.h>
#include <string.h>
}
}
-int FwmarkClient::send(void* data, size_t len, int fd) {
+int FwmarkClient::send(FwmarkCommand* data, int fd) {
mChannel = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (mChannel == -1) {
return -errno;
iovec iov;
iov.iov_base = data;
- iov.iov_len = len;
+ iov.iov_len = sizeof(*data);
msghdr message;
memset(&message, 0, sizeof(message));
char cmsg[CMSG_SPACE(sizeof(fd))];
} cmsgu;
- memset(cmsgu.cmsg, 0, sizeof(cmsgu.cmsg));
- message.msg_control = cmsgu.cmsg;
- message.msg_controllen = sizeof(cmsgu.cmsg);
+ if (data->cmdId != FwmarkCommand::QUERY_USER_ACCESS) {
+ memset(cmsgu.cmsg, 0, sizeof(cmsgu.cmsg));
+ message.msg_control = cmsgu.cmsg;
+ message.msg_controllen = sizeof(cmsgu.cmsg);
- cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
- cmsgh->cmsg_len = CMSG_LEN(sizeof(fd));
- cmsgh->cmsg_level = SOL_SOCKET;
- cmsgh->cmsg_type = SCM_RIGHTS;
- memcpy(CMSG_DATA(cmsgh), &fd, sizeof(fd));
+ cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
+ cmsgh->cmsg_len = CMSG_LEN(sizeof(fd));
+ cmsgh->cmsg_level = SOL_SOCKET;
+ cmsgh->cmsg_type = SCM_RIGHTS;
+ memcpy(CMSG_DATA(cmsgh), &fd, sizeof(fd));
+ }
if (TEMP_FAILURE_RETRY(sendmsg(mChannel, &message, 0)) == -1) {
return -errno;
#include <sys/socket.h>
+struct FwmarkCommand;
+
class FwmarkClient {
public:
// Returns true if a socket of the given |family| should be sent to the fwmark server to have
// Sends |data| to the fwmark server, along with |fd| as ancillary data using cmsg(3).
// Returns 0 on success or a negative errno value on failure.
- int send(void* data, size_t len, int fd);
+ int send(FwmarkCommand* data, int fd);
private:
int mChannel;
}
if (FwmarkClient::shouldSetFwmark(family)) {
FwmarkCommand command = {FwmarkCommand::ON_ACCEPT, 0, 0};
- if (int error = FwmarkClient().send(&command, sizeof(command), acceptedSocket)) {
+ if (int error = FwmarkClient().send(&command, acceptedSocket)) {
return closeFdAndSetErrno(acceptedSocket, error);
}
}
int netdClientConnect(int sockfd, const sockaddr* addr, socklen_t addrlen) {
if (sockfd >= 0 && addr && FwmarkClient::shouldSetFwmark(addr->sa_family)) {
FwmarkCommand command = {FwmarkCommand::ON_CONNECT, 0, 0};
- if (int error = FwmarkClient().send(&command, sizeof(command), sockfd)) {
+ if (int error = FwmarkClient().send(&command, sockfd)) {
errno = -error;
return -1;
}
return -EBADF;
}
FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0};
- return FwmarkClient().send(&command, sizeof(command), socketFd);
+ return FwmarkClient().send(&command, socketFd);
}
extern "C" int setNetworkForProcess(unsigned netId) {
return -EBADF;
}
FwmarkCommand command = {FwmarkCommand::PROTECT_FROM_VPN, 0, 0};
- return FwmarkClient().send(&command, sizeof(command), socketFd);
+ return FwmarkClient().send(&command, socketFd);
}
extern "C" int setNetworkForUser(uid_t uid, int socketFd) {
return -EBADF;
}
FwmarkCommand command = {FwmarkCommand::SELECT_FOR_USER, 0, uid};
- return FwmarkClient().send(&command, sizeof(command), socketFd);
+ return FwmarkClient().send(&command, socketFd);
+}
+
+extern "C" int queryUserAccess(uid_t uid, unsigned netId) {
+ FwmarkCommand command = {FwmarkCommand::QUERY_USER_ACCESS, netId, uid};
+ return FwmarkClient().send(&command, -1);
}
SELECT_NETWORK,
PROTECT_FROM_VPN,
SELECT_FOR_USER,
+ QUERY_USER_ACCESS,
} cmdId;
unsigned netId; // used only in the SELECT_NETWORK command; ignored otherwise.
- uid_t uid; // used only in the SELECT_FOR_USER command; ignored otherwise.
+ uid_t uid; // used only in the SELECT_FOR_USER and QUERY_USER_ACCESS commands;
+ // ignored otherwise.
};
#endif // NETD_INCLUDE_FWMARK_COMMAND_H
int setNetworkForUser(uid_t uid, int socketFd);
+int queryUserAccess(uid_t uid, unsigned netId);
+
__END_DECLS
#endif // NETD_INCLUDE_NETD_CLIENT_H
ClatdController.cpp \
CommandListener.cpp \
DnsProxyListener.cpp \
+ DummyNetwork.cpp \
FirewallController.cpp \
FwmarkServer.cpp \
IdletimerController.cpp \
break;
}
- fullCmd.insert(0, " ");
+ fullCmd.insert(0, " -w ");
fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH);
if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
cli->sendMsg(ResponseCode::CommandOkay, "MTU changed", false);
} else {
cli->sendMsg(ResponseCode::OperationFailed,
- "Failed to get MTU", true);
+ "Failed to set MTU", true);
}
return 0;
} else {
NetdCommand("ipfwd") {
}
-int CommandListener::IpFwdCmd::runCommand(SocketClient *cli,
- int argc, char **argv) {
- int rc = 0;
+int CommandListener::IpFwdCmd::runCommand(SocketClient *cli, int argc, char **argv) {
+ bool matched = false;
+ bool success;
- if (argc < 2) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
- return 0;
- }
-
- if (!strcmp(argv[1], "status")) {
- char *tmp = NULL;
+ if (argc == 2) {
+ // 0 1
+ // ipfwd status
+ if (!strcmp(argv[1], "status")) {
+ char *tmp = NULL;
- asprintf(&tmp, "Forwarding %s", (sTetherCtrl->getIpFwdEnabled() ? "enabled" : "disabled"));
- cli->sendMsg(ResponseCode::IpFwdStatusResult, tmp, false);
- free(tmp);
- return 0;
- } else if (!strcmp(argv[1], "enable")) {
- rc = sTetherCtrl->setIpFwdEnabled(true);
- } else if (!strcmp(argv[1], "disable")) {
- rc = sTetherCtrl->setIpFwdEnabled(false);
- } else {
+ asprintf(&tmp, "Forwarding %s",
+ ((sTetherCtrl->forwardingRequestCount() > 0) ? "enabled" : "disabled"));
+ cli->sendMsg(ResponseCode::IpFwdStatusResult, tmp, false);
+ free(tmp);
+ return 0;
+ }
+ } else if (argc == 3) {
+ // 0 1 2
+ // ipfwd enable <requester>
+ // ipfwd disable <requester>
+ if (!strcmp(argv[1], "enable")) {
+ matched = true;
+ success = sTetherCtrl->enableForwarding(argv[2]);
+ } else if (!strcmp(argv[1], "disable")) {
+ matched = true;
+ success = sTetherCtrl->disableForwarding(argv[2]);
+ }
+ } else if (argc == 4) {
+ // 0 1 2 3
+ // ipfwd add wlan0 dummy0
+ // ipfwd remove wlan0 dummy0
+ int ret = 0;
+ if (!strcmp(argv[1], "add")) {
+ matched = true;
+ ret = RouteController::enableTethering(argv[2], argv[3]);
+ } else if (!strcmp(argv[1], "remove")) {
+ matched = true;
+ ret = RouteController::disableTethering(argv[2], argv[3]);
+ }
+ success = (ret == 0);
+ errno = -ret;
+ }
+
+ if (!matched) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown ipfwd cmd", false);
return 0;
}
- if (!rc) {
+ if (success) {
cli->sendMsg(ResponseCode::CommandOkay, "ipfwd operation succeeded", false);
} else {
cli->sendMsg(ResponseCode::OperationFailed, "ipfwd operation failed", true);
}
-
return 0;
}
int rc = 0;
const char **argv = const_cast<const char **>(margv);
- if (argc < 2) {
+ if (argc < 3) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Resolver missing arguments", false);
return 0;
}
+ unsigned netId = stringToNetId(argv[2]);
+ // TODO: Consider making NetworkController.isValidNetwork() public
+ // and making that check here.
+
if (!strcmp(argv[1], "setnetdns")) {
// "resolver setnetdns <netId> <domains> <dns1> <dns2> ..."
if (argc >= 5) {
- rc = sResolverCtrl->setDnsServers(strtoul(argv[2], NULL, 0), argv[3], &argv[4], argc - 4);
+ rc = sResolverCtrl->setDnsServers(netId, argv[3], &argv[4], argc - 4);
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Wrong number of arguments to resolver setnetdns", false);
}
} else if (!strcmp(argv[1], "clearnetdns")) { // "resolver clearnetdns <netId>"
if (argc == 3) {
- rc = sResolverCtrl->clearDnsServers(strtoul(argv[2], NULL, 0));
+ rc = sResolverCtrl->clearDnsServers(netId);
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Wrong number of arguments to resolver clearnetdns", false);
}
} else if (!strcmp(argv[1], "flushnet")) { // "resolver flushnet <netId>"
if (argc == 3) {
- rc = sResolverCtrl->flushDnsCache(strtoul(argv[2], NULL, 0));
+ rc = sResolverCtrl->flushDnsCache(netId);
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Wrong number of arguments to resolver flushnet", false);
FirewallRule CommandListener::FirewallCmd::parseRule(const char* arg) {
if (!strcmp(arg, "allow")) {
return ALLOW;
- } else {
+ } else if (!strcmp(arg, "deny")) {
return DENY;
+ } else {
+ ALOGE("failed to parse uid rule (%s)", arg);
+ return ALLOW;
+ }
+}
+
+FirewallType CommandListener::FirewallCmd::parseFirewallType(const char* arg) {
+ if (!strcmp(arg, "whitelist")) {
+ return WHITELIST;
+ } else if (!strcmp(arg, "blacklist")) {
+ return BLACKLIST;
+ } else {
+ ALOGE("failed to parse firewall type (%s)", arg);
+ return BLACKLIST;
+ }
+}
+
+ChildChain CommandListener::FirewallCmd::parseChildChain(const char* arg) {
+ if (!strcmp(arg, "dozable")) {
+ return DOZABLE;
+ } else if (!strcmp(arg, "standby")) {
+ return STANDBY;
+ } else if (!strcmp(arg, "none")) {
+ return NONE;
+ } else {
+ ALOGE("failed to parse child firewall chain (%s)", arg);
+ return INVALID_CHAIN;
}
}
}
if (!strcmp(argv[1], "enable")) {
- int res = sFirewallCtrl->enableFirewall();
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: firewall enable <whitelist|blacklist>", false);
+ return 0;
+ }
+ FirewallType firewallType = parseFirewallType(argv[2]);
+
+ int res = sFirewallCtrl->enableFirewall(firewallType);
return sendGenericOkFail(cli, res);
}
if (!strcmp(argv[1], "disable")) {
}
if (!strcmp(argv[1], "set_uid_rule")) {
- if (argc != 4) {
+ if (argc != 5) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
- "Usage: firewall set_uid_rule <1000> <allow|deny>",
+ "Usage: firewall set_uid_rule <dozable|standby|none> <1000> <allow|deny>",
false);
return 0;
}
- int uid = atoi(argv[2]);
- FirewallRule rule = parseRule(argv[3]);
+ ChildChain childChain = parseChildChain(argv[2]);
+ if (childChain == INVALID_CHAIN) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Invalid chain name. Valid names are: <dozable|standby|none>",
+ false);
+ return 0;
+ }
+ int uid = atoi(argv[3]);
+ FirewallRule rule = parseRule(argv[4]);
+ int res = sFirewallCtrl->setUidRule(childChain, uid, rule);
+ return sendGenericOkFail(cli, res);
+ }
- int res = sFirewallCtrl->setUidRule(uid, rule);
+ if (!strcmp(argv[1], "enable_chain")) {
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: firewall enable_chain <dozable|standby>",
+ false);
+ return 0;
+ }
+
+ ChildChain childChain = parseChildChain(argv[2]);
+ int res = sFirewallCtrl->enableChildChains(childChain, true);
+ return sendGenericOkFail(cli, res);
+ }
+
+ if (!strcmp(argv[1], "disable_chain")) {
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: firewall disable_chain <dozable|standby>",
+ false);
+ return 0;
+ }
+
+ ChildChain childChain = parseChildChain(argv[2]);
+ int res = sFirewallCtrl->enableChildChains(childChain, false);
return sendGenericOkFail(cli, res);
}
if (nextArg == argc) {
return syntaxError(client, "Missing id");
}
+
+ bool userPermissions = !strcmp(argv[2], "user");
+ bool networkPermissions = !strcmp(argv[2], "network");
+ if (!userPermissions && !networkPermissions) {
+ return syntaxError(client, "Unknown argument");
+ }
+
std::vector<unsigned> ids;
for (; nextArg < argc; ++nextArg) {
- char* endPtr;
- unsigned id = strtoul(argv[nextArg], &endPtr, 0);
- if (!*argv[nextArg] || *endPtr) {
- return syntaxError(client, "Invalid id");
+ if (userPermissions) {
+ char* endPtr;
+ unsigned id = strtoul(argv[nextArg], &endPtr, 0);
+ if (!*argv[nextArg] || *endPtr) {
+ return syntaxError(client, "Invalid id");
+ }
+ ids.push_back(id);
+ } else {
+ // networkPermissions
+ ids.push_back(stringToNetId(argv[nextArg]));
}
- ids.push_back(id);
}
- if (!strcmp(argv[2], "user")) {
+ if (userPermissions) {
sNetCtrl->setPermissionForUsers(permission, ids);
- } else if (!strcmp(argv[2], "network")) {
+ } else {
+ // networkPermissions
if (int ret = sNetCtrl->setPermissionForNetworks(permission, ids)) {
return operationError(client, "setPermissionForNetworks() failed", ret);
}
- } else {
- return syntaxError(client, "Unknown argument");
}
+
return success(client);
}
protected:
int sendGenericOkFail(SocketClient *cli, int cond);
static FirewallRule parseRule(const char* arg);
+ static FirewallType parseFirewallType(const char* arg);
+ static ChildChain parseChildChain(const char* arg);
};
class ClatdCmd : public NetdCommand {
registerCmd(new GetHostByNameCmd(this));
}
-DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(SocketClient *c,
- char* host,
- char* service,
- struct addrinfo* hints,
- unsigned netId,
- uint32_t mark)
+DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(
+ SocketClient *c, char* host, char* service, struct addrinfo* hints,
+ const struct android_net_context& netcontext)
: mClient(c),
mHost(host),
mService(service),
mHints(hints),
- mNetId(netId),
- mMark(mark) {
+ mNetContext(netcontext) {
}
DnsProxyListener::GetAddrInfoHandler::~GetAddrInfoHandler() {
void DnsProxyListener::GetAddrInfoHandler::run() {
if (DBG) {
- ALOGD("GetAddrInfoHandler, now for %s / %s / %u / %u", mHost, mService, mNetId, mMark);
+ ALOGD("GetAddrInfoHandler, now for %s / %s / {%u,%u,%u,%u,%u}", mHost, mService,
+ mNetContext.app_netid, mNetContext.app_mark,
+ mNetContext.dns_netid, mNetContext.dns_mark,
+ mNetContext.uid);
}
struct addrinfo* result = NULL;
- uint32_t rv = android_getaddrinfofornet(mHost, mService, mHints, mNetId, mMark, &result);
+ uint32_t rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, &result);
if (rv) {
// getaddrinfo failed
mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, &rv, sizeof(rv));
unsigned netId = strtoul(argv[7], NULL, 10);
uid_t uid = cli->getUid();
- uint32_t mark = mDnsProxyListener->mNetCtrl->getNetworkForDns(&netId, uid);
+ struct android_net_context netcontext;
+ mDnsProxyListener->mNetCtrl->getNetworkContext(netId, uid, &netcontext);
if (ai_flags != -1 || ai_family != -1 ||
ai_socktype != -1 || ai_protocol != -1) {
// Only implement AI_ADDRCONFIG if application is using default network since our
// implementation only works on the default network.
if ((hints->ai_flags & AI_ADDRCONFIG) &&
- netId != mDnsProxyListener->mNetCtrl->getDefaultNetwork()) {
+ netcontext.dns_netid != mDnsProxyListener->mNetCtrl->getDefaultNetwork()) {
hints->ai_flags &= ~AI_ADDRCONFIG;
}
}
if (DBG) {
- ALOGD("GetAddrInfoHandler for %s / %s / %u / %d / %u",
+ ALOGD("GetAddrInfoHandler for %s / %s / {%u,%u,%u,%u,%u}",
name ? name : "[nullhost]",
service ? service : "[nullservice]",
- netId, uid, mark);
+ netcontext.app_netid, netcontext.app_mark,
+ netcontext.dns_netid, netcontext.dns_mark,
+ netcontext.uid);
}
cli->incRef();
DnsProxyListener::GetAddrInfoHandler* handler =
- new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints, netId, mark);
+ new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints, netcontext);
handler->start();
return 0;
#ifndef _DNSPROXYLISTENER_H__
#define _DNSPROXYLISTENER_H__
+#include <resolv_netid.h> // struct android_net_context
#include <sysutils/FrameworkListener.h>
#include "NetdCommand.h"
char* host,
char* service,
struct addrinfo* hints,
- unsigned netId,
- uint32_t mark);
+ const struct android_net_context& netcontext);
~GetAddrInfoHandler();
static void* threadStart(void* handler);
char* mHost; // owned
char* mService; // owned
struct addrinfo* mHints; // owned
- unsigned mNetId;
- uint32_t mMark;
+ struct android_net_context mNetContext;
};
/* ------ gethostbyname ------*/
--- /dev/null
+/*
+ * 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.
+ */
+
+#include "DummyNetwork.h"
+
+#include "RouteController.h"
+
+#define LOG_TAG "Netd"
+#include "log/log.h"
+#include "errno.h"
+
+const char* DummyNetwork::INTERFACE_NAME = "dummy0";
+
+DummyNetwork::DummyNetwork(unsigned netId) : Network(netId) {
+ mInterfaces.insert(INTERFACE_NAME);
+}
+
+DummyNetwork::~DummyNetwork() {
+}
+
+Network::Type DummyNetwork::getType() const {
+ return DUMMY;
+}
+
+int DummyNetwork::addInterface(const std::string& /* interface */) {
+ return -EINVAL;
+}
+
+int DummyNetwork::removeInterface(const std::string& /* interface */) {
+ return -EINVAL;
+}
--- /dev/null
+/*
+ * 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_SERVER_DUMMY_NETWORK_H
+#define NETD_SERVER_DUMMY_NETWORK_H
+
+#include "Network.h"
+
+class DummyNetwork : public Network {
+public:
+ static const char* INTERFACE_NAME;
+ explicit DummyNetwork(unsigned netId);
+ virtual ~DummyNetwork();
+
+private:
+ Type getType() const override;
+ int addInterface(const std::string& interface) override WARN_UNUSED_RESULT;
+ int removeInterface(const std::string& interface) override WARN_UNUSED_RESULT;
+};
+
+#endif // NETD_SERVER_DUMMY_NETWORK_H
#define LOG_NDEBUG 0
#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
#include "NetdConstants.h"
#include "FirewallController.h"
+const char* FirewallController::TABLE = "filter";
+
const char* FirewallController::LOCAL_INPUT = "fw_INPUT";
const char* FirewallController::LOCAL_OUTPUT = "fw_OUTPUT";
const char* FirewallController::LOCAL_FORWARD = "fw_FORWARD";
+const char* FirewallController::LOCAL_DOZABLE = "fw_dozable";
+const char* FirewallController::LOCAL_STANDBY = "fw_standby";
+
FirewallController::FirewallController(void) {
+ // If no rules are set, it's in BLACKLIST mode
+ mFirewallType = BLACKLIST;
}
int FirewallController::setupIptablesHooks(void) {
- return 0;
+ int res = 0;
+ // child chains are created but not attached, they will be attached explicitly.
+ FirewallType firewallType = getFirewallType(DOZABLE);
+ res |= createChain(LOCAL_DOZABLE, LOCAL_INPUT, firewallType);
+
+ firewallType = getFirewallType(STANDBY);
+ res |= createChain(LOCAL_STANDBY, LOCAL_INPUT, firewallType);
+
+ return res;
}
-int FirewallController::enableFirewall(void) {
+int FirewallController::enableFirewall(FirewallType ftype) {
int res = 0;
+ if (mFirewallType != ftype) {
+ // flush any existing rules
+ disableFirewall();
- // flush any existing rules
- disableFirewall();
-
- // create default rule to drop all traffic
- res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL);
- res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL);
- res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL);
+ if (ftype == WHITELIST) {
+ // create default rule to drop all traffic
+ res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL);
+ res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL);
+ res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL);
+ }
+ // Set this after calling disableFirewall(), since it defaults to WHITELIST there
+ mFirewallType = ftype;
+ }
return res;
}
int FirewallController::disableFirewall(void) {
int res = 0;
+ mFirewallType = WHITELIST;
+
// flush any existing rules
res |= execIptables(V4V6, "-F", LOCAL_INPUT, NULL);
res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL);
return res;
}
+int FirewallController::enableChildChains(ChildChain chain, bool enable) {
+ int res = 0;
+ const char* name;
+ switch(chain) {
+ case DOZABLE:
+ name = LOCAL_DOZABLE;
+ break;
+ case STANDBY:
+ name = LOCAL_STANDBY;
+ break;
+ default:
+ return res;
+ }
+
+ if (enable) {
+ res |= attachChain(name, LOCAL_INPUT);
+ res |= attachChain(name, LOCAL_OUTPUT);
+ } else {
+ res |= detachChain(name, LOCAL_INPUT);
+ res |= detachChain(name, LOCAL_OUTPUT);
+ }
+ return res;
+}
+
int FirewallController::isFirewallEnabled(void) {
// TODO: verify that rules are still in place near top
return -1;
}
int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) {
+ if (mFirewallType == BLACKLIST) {
+ // Unsupported in BLACKLIST mode
+ return -1;
+ }
+
if (!isIfaceName(iface)) {
errno = ENOENT;
return -1;
}
int FirewallController::setEgressSourceRule(const char* addr, FirewallRule rule) {
+ if (mFirewallType == BLACKLIST) {
+ // Unsupported in BLACKLIST mode
+ return -1;
+ }
+
IptablesTarget target = V4;
if (strchr(addr, ':')) {
target = V6;
int FirewallController::setEgressDestRule(const char* addr, int protocol, int port,
FirewallRule rule) {
+ if (mFirewallType == BLACKLIST) {
+ // Unsupported in BLACKLIST mode
+ return -1;
+ }
+
IptablesTarget target = V4;
if (strchr(addr, ':')) {
target = V6;
return res;
}
-int FirewallController::setUidRule(int uid, FirewallRule rule) {
+FirewallType FirewallController::getFirewallType(ChildChain chain) {
+ switch(chain) {
+ case DOZABLE:
+ return WHITELIST;
+ case STANDBY:
+ return BLACKLIST;
+ case NONE:
+ return mFirewallType;
+ default:
+ return BLACKLIST;
+ }
+}
+
+int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) {
char uidStr[16];
sprintf(uidStr, "%d", uid);
const char* op;
- if (rule == ALLOW) {
- op = "-I";
- } else {
- op = "-D";
+ const char* target;
+ FirewallType firewallType = getFirewallType(chain);
+ if (firewallType == WHITELIST) {
+ target = "RETURN";
+ op = (rule == ALLOW)? "-I" : "-D";
+ } else { // BLACKLIST mode
+ target = "DROP";
+ op = (rule == DENY)? "-I" : "-D";
}
int res = 0;
- res |= execIptables(V4V6, op, LOCAL_INPUT, "-m", "owner", "--uid-owner", uidStr,
- "-j", "RETURN", NULL);
- res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr,
- "-j", "RETURN", NULL);
+ switch(chain) {
+ case DOZABLE:
+ res |= execIptables(V4V6, op, LOCAL_DOZABLE, "-m", "owner", "--uid-owner",
+ uidStr, "-j", target, NULL);
+ break;
+ case STANDBY:
+ res |= execIptables(V4V6, op, LOCAL_STANDBY, "-m", "owner", "--uid-owner",
+ uidStr, "-j", target, NULL);
+ break;
+ case NONE:
+ res |= execIptables(V4V6, op, LOCAL_INPUT, "-m", "owner", "--uid-owner", uidStr,
+ "-j", target, NULL);
+ res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr,
+ "-j", target, NULL);
+ break;
+ default:
+ ALOGW("Unknown child chain: %d", chain);
+ break;
+ }
+ return res;
+}
+
+int FirewallController::attachChain(const char* childChain, const char* parentChain) {
+ return execIptables(V4V6, "-t", TABLE, "-A", parentChain, "-j", childChain, NULL);
+}
+
+int FirewallController::detachChain(const char* childChain, const char* parentChain) {
+ return execIptables(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
+}
+
+int FirewallController::createChain(const char* childChain,
+ const char* parentChain, FirewallType type) {
+ // Order is important, otherwise later steps may fail.
+ execIptablesSilently(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
+ execIptablesSilently(V4V6, "-t", TABLE, "-F", childChain, NULL);
+ execIptablesSilently(V4V6, "-t", TABLE, "-X", childChain, NULL);
+ int res = 0;
+ res |= execIptables(V4V6, "-t", TABLE, "-N", childChain, NULL);
+ if (type == WHITELIST) {
+ // create default white list for system uid range
+ char uidStr[16];
+ sprintf(uidStr, "0-%d", AID_APP - 1);
+ res |= execIptables(V4V6, "-A", childChain, "-m", "owner", "--uid-owner",
+ uidStr, "-j", "RETURN", NULL);
+ // create default rule to drop all traffic
+ res |= execIptables(V4V6, "-A", childChain, "-j", "DROP", NULL);
+ }
return res;
}
#include <string>
-enum FirewallRule { ALLOW, DENY };
+enum FirewallRule { DENY, ALLOW };
+
+// WHITELIST means the firewall denies all by default, uids must be explicitly ALLOWed
+// BLACKLIST means the firewall allows all by default, uids must be explicitly DENYed
+
+enum FirewallType { WHITELIST, BLACKLIST };
+
+enum ChildChain { NONE, DOZABLE, STANDBY, INVALID_CHAIN };
#define PROTOCOL_TCP 6
#define PROTOCOL_UDP 17
int setupIptablesHooks(void);
- int enableFirewall(void);
+ int enableFirewall(FirewallType);
int disableFirewall(void);
int isFirewallEnabled(void);
int setEgressSourceRule(const char*, FirewallRule);
/* Match traffic coming-in-from or going-out-to given address, port, and protocol. */
int setEgressDestRule(const char*, int, int, FirewallRule);
- /* Match traffic owned by given UID. */
- int setUidRule(int, FirewallRule);
+ /* Match traffic owned by given UID. This is specific to a particular chain. */
+ int setUidRule(ChildChain, int, FirewallRule);
+
+ int enableChildChains(ChildChain, bool);
+
+ static const char* TABLE;
static const char* LOCAL_INPUT;
static const char* LOCAL_OUTPUT;
static const char* LOCAL_FORWARD;
+ static const char* LOCAL_DOZABLE;
+ static const char* LOCAL_STANDBY;
+private:
+ FirewallType mFirewallType;
+ int attachChain(const char*, const char*);
+ int detachChain(const char*, const char*);
+ int createChain(const char*, const char*, FirewallType);
+ FirewallType getFirewallType(ChildChain);
};
#endif
return -EBADMSG;
}
+ Permission permission = mNetworkController->getPermissionForUser(client->getUid());
+
+ if (command.cmdId == FwmarkCommand::QUERY_USER_ACCESS) {
+ if ((permission & PERMISSION_SYSTEM) != PERMISSION_SYSTEM) {
+ return -EPERM;
+ }
+ return mNetworkController->checkUserNetworkAccess(command.uid, command.netId);
+ }
+
cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
if (cmsgh && cmsgh->cmsg_level == SOL_SOCKET && cmsgh->cmsg_type == SCM_RIGHTS &&
cmsgh->cmsg_len == CMSG_LEN(sizeof(*socketFd))) {
return -errno;
}
- Permission permission = mNetworkController->getPermissionForUser(client->getUid());
-
switch (command.cmdId) {
case FwmarkCommand::ON_ACCEPT: {
// Called after a socket accept(). The kernel would've marked the NetId and necessary
int res;
const char *cmd1[] = {
NULL, // To be filled inside runIpxtablesCmd
+ "-w",
"-t",
"raw",
"-F",
const char *cmd2[] = {
NULL, // To be filled inside runIpxtablesCmd
+ "-w",
"-t",
"mangle",
"-F",
const char *cmd1[] = {
NULL, // To be filled inside runIpxtablesCmd
+ "-w",
"-t",
"raw",
(op == IptOpAdd) ? "-A" : "-D",
const char *cmd2[] = {
NULL, // To be filled inside runIpxtablesCmd
+ "-w",
"-t",
"mangle",
(op == IptOpAdd) ? "-A" : "-D",
#include "InterfaceController.h"
#include "RouteController.h"
-using android::base::ReadFileToString;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
+namespace {
+
const char ipv6_proc_path[] = "/proc/sys/net/ipv6/conf";
+const char ipv4_neigh_conf_dir[] = "/proc/sys/net/ipv4/neigh";
+
+const char ipv6_neigh_conf_dir[] = "/proc/sys/net/ipv6/neigh";
+
const char sys_net_path[] = "/sys/class/net";
const char wl_util_path[] = "/system/xbin/wlutil";
+bool isInterfaceName(const char *name) {
+ return strcmp(name, ".") &&
+ strcmp(name, "..") &&
+ strcmp(name, "default") &&
+ strcmp(name, "all");
+}
+
+int writeValueToPath(
+ const char* dirname, const char* subdirname, const char* basename,
+ const char* value) {
+ std::string path(StringPrintf("%s/%s/%s", dirname, subdirname, basename));
+ return WriteStringToFile(value, path) ? 0 : -1;
+}
+
+void setOnAllInterfaces(const char* dirname, const char* basename, const char* value) {
+ // Set the default value, which is used by any interfaces that are created in the future.
+ writeValueToPath(dirname, "default", basename, value);
+
+ // Set the value on all the interfaces that currently exist.
+ DIR* dir = opendir(dirname);
+ if (!dir) {
+ ALOGE("Can't list %s: %s", dirname, strerror(errno));
+ return;
+ }
+ dirent* d;
+ while ((d = readdir(dir))) {
+ if ((d->d_type != DT_DIR) || !isInterfaceName(d->d_name)) {
+ continue;
+ }
+ writeValueToPath(dirname, d->d_name, basename, value);
+ }
+ closedir(dir);
+}
+
+void setIPv6UseOutgoingInterfaceAddrsOnly(const char *value) {
+ setOnAllInterfaces(ipv6_proc_path, "use_oif_addrs_only", value);
+}
+
+} // namespace
+
InterfaceController::InterfaceController() {
// Initial IPv6 settings.
// By default, accept_ra is set to 1 (accept RAs unless forwarding is on) on all interfaces.
// Enable optimistic DAD for IPv6 addresses on all interfaces.
setIPv6OptimisticMode("1");
-}
-InterfaceController::~InterfaceController() {
+ // Reduce the ARP/ND base reachable time from the default (30sec) to 15sec.
+ setBaseReachableTimeMs(15 * 1000);
+
+ // When sending traffic via a given interface use only addresses configured
+ // on that interface as possible source addresses.
+ setIPv6UseOutgoingInterfaceAddrsOnly("1");
}
-int InterfaceController::writeIPv6ProcPath(const char *interface, const char *setting, const char *value) {
- if (!isIfaceName(interface)) {
- errno = ENOENT;
- return -1;
- }
- std::string path(StringPrintf("%s/%s/%s", ipv6_proc_path, interface, setting));
- return WriteStringToFile(value, path);
+InterfaceController::~InterfaceController() {
}
int InterfaceController::setEnableIPv6(const char *interface, const int on) {
- // When disable_ipv6 changes from 1 to 0, the kernel starts autoconf.
- // When disable_ipv6 changes from 0 to 1, the kernel clears all autoconf
- // addresses and routes and disables IPv6 on the interface.
- const char *disable_ipv6 = on ? "0" : "1";
- return writeIPv6ProcPath(interface, "disable_ipv6", disable_ipv6);
+ if (!isIfaceName(interface)) {
+ errno = ENOENT;
+ return -1;
+ }
+ // When disable_ipv6 changes from 1 to 0, the kernel starts autoconf.
+ // When disable_ipv6 changes from 0 to 1, the kernel clears all autoconf
+ // addresses and routes and disables IPv6 on the interface.
+ const char *disable_ipv6 = on ? "0" : "1";
+ return writeValueToPath(ipv6_proc_path, interface, "disable_ipv6", disable_ipv6);
}
int InterfaceController::setIPv6PrivacyExtensions(const char *interface, const int on) {
- // 0: disable IPv6 privacy addresses
- // 0: enable IPv6 privacy addresses and prefer them over non-privacy ones.
- return writeIPv6ProcPath(interface, "use_tempaddr", on ? "2" : "0");
+ if (!isIfaceName(interface)) {
+ errno = ENOENT;
+ return -1;
+ }
+ // 0: disable IPv6 privacy addresses
+ // 0: enable IPv6 privacy addresses and prefer them over non-privacy ones.
+ return writeValueToPath(ipv6_proc_path, interface, "use_tempaddr", on ? "2" : "0");
}
// Enables or disables IPv6 ND offload. This is useful for 464xlat on wifi, IPv6 tethering, and
}
}
-int InterfaceController::isInterfaceName(const char *name) {
- return strcmp(name, ".") &&
- strcmp(name, "..") &&
- strcmp(name, "default") &&
- strcmp(name, "all");
-}
-
-void InterfaceController::setOnAllInterfaces(const char* filename, const char* value) {
- // Set the default value, which is used by any interfaces that are created in the future.
- writeIPv6ProcPath("default", filename, value);
-
- // Set the value on all the interfaces that currently exist.
- DIR* dir = opendir(ipv6_proc_path);
- if (!dir) {
- ALOGE("Can't list %s: %s", ipv6_proc_path, strerror(errno));
- return;
- }
- dirent* d;
- while ((d = readdir(dir))) {
- if (d->d_type == DT_DIR && isInterfaceName(d->d_name)) {
- writeIPv6ProcPath(d->d_name, filename, value);
- }
- }
- closedir(dir);
-}
-
void InterfaceController::setAcceptRA(const char *value) {
- setOnAllInterfaces("accept_ra", value);
+ setOnAllInterfaces(ipv6_proc_path, "accept_ra", value);
}
// |tableOrOffset| is interpreted as:
// ID to get the table. If it's set to -1000, routes from interface ID 5 will go into
// table 1005, etc.
void InterfaceController::setAcceptRARouteTable(int tableOrOffset) {
- std::string value(StringPrintf("%d", tableOrOffset));
- setOnAllInterfaces("accept_ra_rt_table", value.c_str());
+ std::string value(StringPrintf("%d", tableOrOffset));
+ setOnAllInterfaces(ipv6_proc_path, "accept_ra_rt_table", value.c_str());
}
int InterfaceController::setMtu(const char *interface, const char *mtu)
{
- if (!isIfaceName(interface)) {
- errno = ENOENT;
- return -1;
- }
- std::string path(StringPrintf("%s/%s/mtu", sys_net_path, interface));
- return WriteStringToFile(mtu, path);
+ if (!isIfaceName(interface)) {
+ errno = ENOENT;
+ return -1;
+ }
+ return writeValueToPath(sys_net_path, interface, "mtu", mtu);
+}
+
+void InterfaceController::setBaseReachableTimeMs(unsigned int millis) {
+ std::string value(StringPrintf("%u", millis));
+ setOnAllInterfaces(ipv4_neigh_conf_dir, "base_reachable_time_ms", value.c_str());
+ setOnAllInterfaces(ipv6_neigh_conf_dir, "base_reachable_time_ms", value.c_str());
}
void InterfaceController::setIPv6OptimisticMode(const char *value) {
- setOnAllInterfaces("optimistic_dad", value);
- setOnAllInterfaces("use_optimistic", value);
+ setOnAllInterfaces(ipv6_proc_path, "optimistic_dad", value);
+ setOnAllInterfaces(ipv6_proc_path, "use_optimistic", value);
}
int setMtu(const char *interface, const char *mtu);
private:
- int writeIPv6ProcPath(const char *interface, const char *setting, const char *value);
- int isInterfaceName(const char *name);
- void setOnAllInterfaces(const char* filename, const char* value);
void setAcceptRA(const char* value);
void setAcceptRARouteTable(int tableOrOffset);
+ void setBaseReachableTimeMs(unsigned int millis);
void setIPv6OptimisticMode(const char *value);
};
* Bug 17629786 asks to make the failure more obvious, or even fatal
* so that all builds eventually gain the performance improvement.
*/
- {{IPTABLES_PATH, "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
- {{IPTABLES_PATH, "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
- {{IPTABLES_PATH, "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1},
- {{IPTABLES_PATH, "-t", "mangle", "-A", LOCAL_MANGLE_FORWARD, "-p", "tcp", "--tcp-flags",
+ {{IPTABLES_PATH, "-w", "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
+ {{IPTABLES_PATH, "-w", "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
+ {{IPTABLES_PATH, "-w", "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1},
+ {{IPTABLES_PATH, "-w", "-t", "mangle", "-A", LOCAL_MANGLE_FORWARD, "-p", "tcp", "--tcp-flags",
"SYN", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu"}, 0},
};
for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
* - internally it will be memcopied to an array and terminated with a NULL.
*/
struct CommandsAndArgs defaultCommands[] = {
- {{IPTABLES_PATH, "-F", LOCAL_FORWARD,}, 1},
- {{IPTABLES_PATH, "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
- {{IPTABLES_PATH, "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1},
+ {{IPTABLES_PATH, "-w", "-F", LOCAL_FORWARD,}, 1},
+ {{IPTABLES_PATH, "-w", "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
+ {{IPTABLES_PATH, "-w", "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1},
};
for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
if (natCount == 0) {
const char *cmd[] = {
IPTABLES_PATH,
+ "-w",
"-t",
"nat",
"-A",
/* Always make sure the drop rule is at the end */
const char *cmd1[] = {
IPTABLES_PATH,
+ "-w",
"-D",
LOCAL_FORWARD,
"-j",
runCmd(ARRAY_SIZE(cmd1), cmd1);
const char *cmd2[] = {
IPTABLES_PATH,
+ "-w",
"-A",
LOCAL_FORWARD,
"-j",
};
runCmd(ARRAY_SIZE(cmd2), cmd2);
- if (int ret = RouteController::enableTethering(intIface, extIface)) {
- ALOGE("failed to add tethering rule for iif=%s oif=%s", intIface, extIface);
- if (natCount == 0) {
- setDefaults();
- }
- errno = -ret;
- return -1;
- }
-
natCount++;
return 0;
}
}
const char *cmd2b[] = {
IPTABLES_PATH,
+ "-w",
"-A",
LOCAL_TETHER_COUNTERS_CHAIN,
"-i",
const char *cmd3b[] = {
IPTABLES_PATH,
+ "-w",
"-A",
LOCAL_TETHER_COUNTERS_CHAIN,
"-i",
int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) {
const char *cmd1[] = {
IPTABLES_PATH,
+ "-w",
add ? "-A" : "-D",
LOCAL_FORWARD,
"-i",
const char *cmd2[] = {
IPTABLES_PATH,
+ "-w",
add ? "-A" : "-D",
LOCAL_FORWARD,
"-i",
const char *cmd3[] = {
IPTABLES_PATH,
+ "-w",
add ? "-A" : "-D",
LOCAL_FORWARD,
"-i",
return 0;
err_return:
- cmd2[1] = "-D";
+ cmd2[2] = "-D";
runCmd(ARRAY_SIZE(cmd2), cmd2);
err_invalid_drop:
- cmd1[1] = "-D";
+ cmd1[2] = "-D";
runCmd(ARRAY_SIZE(cmd1), cmd1);
return rc;
}
return -1;
}
- if (int ret = RouteController::disableTethering(intIface, extIface)) {
- ALOGE("failed to remove tethering rule for iif=%s oif=%s", intIface, extIface);
- errno = -ret;
- return -1;
- }
-
setForwardRules(false, intIface, extIface);
if (--natCount <= 0) {
// handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
std::list<const char*> argsList;
argsList.push_back(NULL);
const char* arg;
+
+ // Wait to avoid failure due to another process holding the lock
+ argsList.push_back("-w");
+
do {
arg = va_arg(args, const char *);
argsList.push_back(arg);
}
if (!strcmp(subsys, "net")) {
- int action = evt->getAction();
+ NetlinkEvent::Action action = evt->getAction();
const char *iface = evt->findParam("INTERFACE");
- if (action == evt->NlActionAdd) {
+ if (action == NetlinkEvent::Action::kAdd) {
notifyInterfaceAdded(iface);
- } else if (action == evt->NlActionRemove) {
+ } else if (action == NetlinkEvent::Action::kRemove) {
notifyInterfaceRemoved(iface);
- } else if (action == evt->NlActionChange) {
+ } else if (action == NetlinkEvent::Action::kChange) {
evt->dump();
notifyInterfaceChanged("nana", true);
- } else if (action == evt->NlActionLinkUp) {
+ } else if (action == NetlinkEvent::Action::kLinkUp) {
notifyInterfaceLinkChanged(iface, true);
- } else if (action == evt->NlActionLinkDown) {
+ } else if (action == NetlinkEvent::Action::kLinkDown) {
notifyInterfaceLinkChanged(iface, false);
- } else if (action == evt->NlActionAddressUpdated ||
- action == evt->NlActionAddressRemoved) {
+ } else if (action == NetlinkEvent::Action::kAddressUpdated ||
+ action == NetlinkEvent::Action::kAddressRemoved) {
const char *address = evt->findParam("ADDRESS");
const char *flags = evt->findParam("FLAGS");
const char *scope = evt->findParam("SCOPE");
- if (action == evt->NlActionAddressRemoved && iface && address) {
+ if (action == NetlinkEvent::Action::kAddressRemoved && iface && address) {
int resetMask = strchr(address, ':') ? RESET_IPV6_ADDRESSES : RESET_IPV4_ADDRESSES;
resetMask |= RESET_IGNORE_INTERFACE_ADDRESS;
if (int ret = ifc_reset_connections(iface, resetMask)) {
if (iface && flags && scope) {
notifyAddressChanged(action, address, iface, flags, scope);
}
- } else if (action == evt->NlActionRdnss) {
+ } else if (action == NetlinkEvent::Action::kRdnss) {
const char *lifetime = evt->findParam("LIFETIME");
const char *servers = evt->findParam("SERVERS");
if (lifetime && servers) {
notifyInterfaceDnsServers(iface, lifetime, servers);
}
- } else if (action == evt->NlActionRouteUpdated ||
- action == evt->NlActionRouteRemoved) {
+ } else if (action == NetlinkEvent::Action::kRouteUpdated ||
+ action == NetlinkEvent::Action::kRouteRemoved) {
const char *route = evt->findParam("ROUTE");
const char *gateway = evt->findParam("GATEWAY");
const char *iface = evt->findParam("INTERFACE");
const char *label = evt->findParam("INTERFACE");
const char *state = evt->findParam("STATE");
const char *timestamp = evt->findParam("TIME_NS");
+ const char *uid = evt->findParam("UID");
if (state)
- notifyInterfaceClassActivity(label, !strcmp("active", state), timestamp);
+ notifyInterfaceClassActivity(label, !strcmp("active", state),
+ timestamp, uid);
#if !LOG_NDEBUG
} else if (strcmp(subsys, "platform") && strcmp(subsys, "backlight")) {
}
void NetlinkHandler::notifyInterfaceClassActivity(const char *name,
- bool isActive, const char *timestamp) {
+ bool isActive,
+ const char *timestamp,
+ const char *uid) {
if (timestamp == NULL)
notify(ResponseCode::InterfaceClassActivity,
"IfaceClass %s %s", isActive ? "active" : "idle", name);
+ else if (uid != NULL && isActive)
+ notify(ResponseCode::InterfaceClassActivity,
+ "IfaceClass active %s %s %s", name, timestamp, uid);
else
notify(ResponseCode::InterfaceClassActivity,
"IfaceClass %s %s %s", isActive ? "active" : "idle", name, timestamp);
}
-void NetlinkHandler::notifyAddressChanged(int action, const char *addr,
+void NetlinkHandler::notifyAddressChanged(NetlinkEvent::Action action, const char *addr,
const char *iface, const char *flags,
const char *scope) {
notify(ResponseCode::InterfaceAddressChange,
"Address %s %s %s %s %s",
- (action == NetlinkEvent::NlActionAddressUpdated) ? kUpdated : kRemoved,
+ (action == NetlinkEvent::Action::kAddressUpdated) ? kUpdated : kRemoved,
addr, iface, flags, scope);
}
iface, lifetime, servers);
}
-void NetlinkHandler::notifyRouteChange(int action, const char *route,
+void NetlinkHandler::notifyRouteChange(NetlinkEvent::Action action, const char *route,
const char *gateway, const char *iface) {
notify(ResponseCode::RouteChange,
"Route %s %s%s%s%s%s",
- (action == NetlinkEvent::NlActionRouteUpdated) ? kUpdated : kRemoved,
+ (action == NetlinkEvent::Action::kRouteUpdated) ? kUpdated : kRemoved,
route,
*gateway ? " via " : "",
gateway,
#ifndef _NETLINKHANDLER_H
#define _NETLINKHANDLER_H
+#include <sysutils/NetlinkEvent.h>
#include <sysutils/NetlinkListener.h>
#include "NetlinkManager.h"
void notifyInterfaceLinkChanged(const char *name, bool isUp);
void notifyQuotaLimitReached(const char *name, const char *iface);
void notifyInterfaceClassActivity(const char *name, bool isActive,
- const char *timestamp);
- void notifyAddressChanged(int action, const char *addr, const char *iface,
+ const char *timestamp, const char *uid);
+ void notifyAddressChanged(NetlinkEvent::Action action, const char *addr, const char *iface,
const char *flags, const char *scope);
void notifyInterfaceDnsServers(const char *iface, const char *lifetime,
const char *servers);
- void notifyRouteChange(int action, const char *route, const char *gateway, const char *iface);
+ void notifyRouteChange(NetlinkEvent::Action action, const char *route, const char *gateway, const char *iface);
void notifyStrictCleartext(const char* uid, const char* hex);
};
#endif
class Network {
public:
enum Type {
+ DUMMY,
LOCAL,
PHYSICAL,
VIRTUAL,
#include "NetworkController.h"
+#include "DummyNetwork.h"
#include "Fwmark.h"
#include "LocalNetwork.h"
#include "PhysicalNetwork.h"
const unsigned NetworkController::MIN_OEM_ID = 1;
const unsigned NetworkController::MAX_OEM_ID = 50;
-// NetIds 51..98 are reserved for future use.
+const unsigned NetworkController::DUMMY_NET_ID = 51;
+// NetIds 52..98 are reserved for future use.
const unsigned NetworkController::LOCAL_NET_ID = 99;
// All calls to methods here are made while holding a write lock on mRWLock.
NetworkController::NetworkController() :
mDelegateImpl(new NetworkController::DelegateImpl(this)), mDefaultNetId(NETID_UNSET) {
mNetworks[LOCAL_NET_ID] = new LocalNetwork(LOCAL_NET_ID);
+ mNetworks[DUMMY_NET_ID] = new DummyNetwork(DUMMY_NET_ID);
}
unsigned NetworkController::getDefaultNetwork() const {
return mDefaultNetId;
}
+void NetworkController::getNetworkContext(
+ unsigned netId, uid_t uid, struct android_net_context* netcontext) const {
+ struct android_net_context nc = {
+ .app_netid = netId,
+ .app_mark = MARK_UNSET,
+ .dns_netid = netId,
+ .dns_mark = MARK_UNSET,
+ .uid = uid,
+ };
+
+ if (nc.app_netid == NETID_UNSET) {
+ nc.app_netid = getNetworkForConnect(uid);
+ }
+ Fwmark fwmark;
+ fwmark.netId = nc.app_netid;
+ nc.app_mark = fwmark.intValue;
+
+ nc.dns_mark = getNetworkForDns(&(nc.dns_netid), uid);
+
+ if (netcontext) {
+ *netcontext = nc;
+ }
+}
+
unsigned NetworkController::getNetworkForInterface(const char* interface) const {
android::RWLock::AutoRLock lock(mRWLock);
for (const auto& entry : mNetworks) {
static const unsigned MIN_OEM_ID;
static const unsigned MAX_OEM_ID;
static const unsigned LOCAL_NET_ID;
+ static const unsigned DUMMY_NET_ID;
NetworkController();
uint32_t getNetworkForDns(unsigned* netId, uid_t uid) const;
unsigned getNetworkForUser(uid_t uid) const;
unsigned getNetworkForConnect(uid_t uid) const;
+ void getNetworkContext(unsigned netId, uid_t uid, struct android_net_context* netcontext) const;
unsigned getNetworkForInterface(const char* interface) const;
bool isVirtualNetwork(unsigned netId) const;
#include "Fwmark.h"
#include "UidRanges.h"
+#include "DummyNetwork.h"
#include "base/file.h"
#define LOG_TAG "Netd"
#include "log/log.h"
#include "logwrap/logwrap.h"
+#include "netutils/ifc.h"
#include "resolv_netid.h"
using android::base::WriteStringToFile;
// BEGIN CONSTANTS --------------------------------------------------------------------------------
const uint32_t RULE_PRIORITY_VPN_OVERRIDE_SYSTEM = 10000;
+const uint32_t RULE_PRIORITY_VPN_OVERRIDE_OIF = 10500;
const uint32_t RULE_PRIORITY_VPN_OUTPUT_TO_LOCAL = 11000;
const uint32_t RULE_PRIORITY_SECURE_VPN = 12000;
const uint32_t RULE_PRIORITY_EXPLICIT_NETWORK = 13000;
const char* const IP_VERSIONS[] = {"-4", "-6"};
const uid_t UID_ROOT = 0;
+const char* const IIF_LOOPBACK = "lo";
const char* const IIF_NONE = NULL;
const char* const OIF_NONE = NULL;
const bool ACTION_ADD = true;
// Adds or removes a routing rule for IPv4 and IPv6.
//
-// + If |table| is non-zero, the rule points at the specified routing table. Otherwise, the rule
-// returns ENETUNREACH.
+// + If |priority| is RULE_PRIORITY_UNREACHABLE, the rule returns ENETUNREACH (i.e., specifies an
+// action of FR_ACT_UNREACHABLE). Otherwise, the rule specifies an action of FR_ACT_TO_TBL.
+// + If |table| is non-zero, the rule points at the specified routing table. Otherwise, the table is
+// unspecified. An unspecified table is only allowed when deleting a rule.
// + If |mask| is non-zero, the rule matches the specified fwmark and mask. Otherwise, |fwmark| is
// ignored.
// + If |iif| is non-NULL, the rule matches the specified incoming interface.
// Assemble a rule request and put it in an array of iovec structures.
fib_rule_hdr rule = {
- .action = static_cast<uint8_t>(table != RT_TABLE_UNSPEC ? FR_ACT_TO_TBL :
- FR_ACT_UNREACHABLE),
+ .action = static_cast<uint8_t>(priority != RULE_PRIORITY_UNREACHABLE ? FR_ACT_TO_TBL :
+ FR_ACT_UNREACHABLE),
+ // Note that here we're implicitly setting rule.table to 0. When we want to specify a
+ // non-zero table, we do this via the FRATTR_TABLE attribute.
};
+ // Don't ever create a rule that looks up table 0, because table 0 is the local table.
+ // It's OK to specify a table ID of 0 when deleting a rule, because that doesn't actually select
+ // table 0, it's a wildcard that matches anything.
+ if (table == RT_TABLE_UNSPEC && rule.action == FR_ACT_TO_TBL && action != RTM_DELRULE) {
+ ALOGE("RT_TABLE_UNSPEC only allowed when deleting rules");
+ return -ENOTUNIQ;
+ }
+
rtattr fraIifName = { U16_RTA_LENGTH(iifLength), FRA_IIFNAME };
rtattr fraOifName = { U16_RTA_LENGTH(oifLength), FRA_OIFNAME };
}
return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, priority, table, fwmark.intValue,
- mask.intValue, IIF_NONE, OIF_NONE, uidStart, uidEnd);
+ mask.intValue, IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd);
}
// A rule to allow system apps to send traffic over this VPN even if they are not part of the target
//
// Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already knows
// the outgoing interface (typically for link-local communications).
-WARN_UNUSED_RESULT int modifyOutputInterfaceRule(const char* interface, uint32_t table,
- Permission permission, uid_t uidStart,
- uid_t uidEnd, bool add) {
+WARN_UNUSED_RESULT int modifyOutputInterfaceRules(const char* interface, uint32_t table,
+ Permission permission, uid_t uidStart,
+ uid_t uidEnd, bool add) {
Fwmark fwmark;
Fwmark mask;
fwmark.permission = permission;
mask.permission = permission;
+ // If this rule does not specify a UID range, then also add a corresponding high-priority rule
+ // for UID. This covers forwarded packets and system daemons such as the tethering DHCP server.
+ if (uidStart == INVALID_UID && uidEnd == INVALID_UID) {
+ if (int ret = modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_VPN_OVERRIDE_OIF,
+ table, fwmark.intValue, mask.intValue, IIF_NONE, interface,
+ UID_ROOT, UID_ROOT)) {
+ return ret;
+ }
+ }
+
return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_OUTPUT_INTERFACE, table,
fwmark.intValue, mask.intValue, IIF_NONE, interface, uidStart, uidEnd);
}
fwmark.intValue, mask.intValue);
}
+int configureDummyNetwork() {
+ const char *interface = DummyNetwork::INTERFACE_NAME;
+ uint32_t table = getRouteTableForInterface(interface);
+ if (table == RT_TABLE_UNSPEC) {
+ // getRouteTableForInterface has already looged an error.
+ return -ESRCH;
+ }
+
+ ifc_init();
+ int ret = ifc_up(interface);
+ ifc_close();
+ if (ret) {
+ ALOGE("Can't bring up %s: %s", interface, strerror(errno));
+ return -errno;
+ }
+
+ if ((ret = modifyOutputInterfaceRules(interface, table, PERMISSION_NONE,
+ INVALID_UID, INVALID_UID, ACTION_ADD))) {
+ ALOGE("Can't create oif rules for %s: %s", interface, strerror(-ret));
+ return ret;
+ }
+
+ if ((ret = modifyIpRoute(RTM_NEWROUTE, table, interface, "0.0.0.0/0", NULL))) {
+ ALOGE("Can't add IPv4 default route to %s: %s", interface, strerror(-ret));
+ return ret;
+ }
+
+ if ((ret = modifyIpRoute(RTM_NEWROUTE, table, interface, "::/0", NULL))) {
+ ALOGE("Can't add IPv6 default route to %s: %s", interface, strerror(-ret));
+ return ret;
+ }
+
+ return 0;
+}
+
// Add a new rule to look up the 'main' table, with the same selectors as the "default network"
// rule, but with a lower priority. We will never create routes in the main table; it should only be
// used for directly-connected routes implicitly created by the kernel when adding IP addresses.
if (int ret = modifyIncomingPacketMark(netId, interface, PERMISSION_NONE, add)) {
return ret;
}
- return modifyOutputInterfaceRule(interface, ROUTE_TABLE_LOCAL_NETWORK, PERMISSION_NONE,
- INVALID_UID, INVALID_UID, add);
+ return modifyOutputInterfaceRules(interface, ROUTE_TABLE_LOCAL_NETWORK, PERMISSION_NONE,
+ INVALID_UID, INVALID_UID, add);
}
WARN_UNUSED_RESULT int modifyPhysicalNetwork(unsigned netId, const char* interface,
add)) {
return ret;
}
- if (int ret = modifyOutputInterfaceRule(interface, table, permission, INVALID_UID, INVALID_UID,
+ if (int ret = modifyOutputInterfaceRules(interface, table, permission, INVALID_UID, INVALID_UID,
add)) {
return ret;
}
range.second, add)) {
return ret;
}
- if (int ret = modifyOutputInterfaceRule(interface, table, PERMISSION_NONE, range.first,
- range.second, add)) {
+ if (int ret = modifyOutputInterfaceRules(interface, table, PERMISSION_NONE, range.first,
+ range.second, add)) {
return ret;
}
}
return ret;
}
+WARN_UNUSED_RESULT int clearTetheringRules(const char* inputInterface) {
+ int ret = 0;
+ while (ret == 0) {
+ ret = modifyIpRule(RTM_DELRULE, RULE_PRIORITY_TETHERING, 0, MARK_UNSET, MARK_UNSET,
+ inputInterface, OIF_NONE, INVALID_UID, INVALID_UID);
+ }
+
+ if (ret == -ENOENT) {
+ return 0;
+ } else {
+ return ret;
+ }
+}
+
} // namespace
int RouteController::Init(unsigned localNetId) {
if (int ret = addUnreachableRule()) {
return ret;
}
+ // Don't complain if we can't add the dummy network, since not all devices support it.
+ configureDummyNetwork();
+
updateTableNamesFile();
return 0;
}
if (int ret = flushRoutes(interface)) {
return ret;
}
+ if (int ret = clearTetheringRules(interface)) {
+ return ret;
+ }
updateTableNamesFile();
return 0;
}
"ssid=%s\n"
"channel=%d\n"
"ieee80211n=1\n"
- "hw_mode=g\n"
+ "hw_mode=%c\n"
"ignore_broadcast_ssid=%d\n"
"wowlan_triggers=any\n",
- argv[2], argv[3], channel, hidden));
+ argv[2], argv[3], channel, (channel <= 14) ? 'g' : 'a', hidden));
std::string fbuf;
if (argc > 7) {
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <base/file.h>
#define LOG_TAG "TetherController"
#include <cutils/log.h>
#include <cutils/properties.h>
#include "Permission.h"
#include "TetherController.h"
-using android::base::ReadFileToString;
-using android::base::WriteStringToFile;
+namespace {
+
+static const char BP_TOOLS_MODE[] = "bp-tools";
+static const char IPV4_FORWARDING_PROC_FILE[] = "/proc/sys/net/ipv4/ip_forward";
+static const char IPV6_FORWARDING_PROC_FILE[] = "/proc/sys/net/ipv6/conf/all/forwarding";
+
+bool writeToFile(const char* filename, const char* value) {
+ int fd = open(filename, O_WRONLY);
+ if (fd < 0) {
+ ALOGE("Failed to open %s: %s", filename, strerror(errno));
+ return false;
+ }
+
+ const ssize_t len = strlen(value);
+ if (write(fd, value, len) != len) {
+ ALOGE("Failed to write %s to %s: %s", value, filename, strerror(errno));
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+bool inBpToolsMode() {
+ // In BP tools mode, do not disable IP forwarding
+ char bootmode[PROPERTY_VALUE_MAX] = {0};
+ property_get("ro.bootmode", bootmode, "unknown");
+ return !strcmp(BP_TOOLS_MODE, bootmode);
+}
+
+} // namespace
TetherController::TetherController() {
mInterfaces = new InterfaceCollection();
mDnsForwarders = new NetAddressCollection();
mDaemonFd = -1;
mDaemonPid = 0;
+ if (inBpToolsMode()) {
+ enableForwarding(BP_TOOLS_MODE);
+ } else {
+ setIpFwdEnabled();
+ }
}
TetherController::~TetherController() {
mInterfaces->clear();
mDnsForwarders->clear();
+ mForwardingRequests.clear();
}
-int TetherController::setIpFwdEnabled(bool enable) {
-
- ALOGD("Setting IP forward enable = %d", enable);
-
- // In BP tools mode, do not disable IP forwarding
- char bootmode[PROPERTY_VALUE_MAX] = {0};
- property_get("ro.bootmode", bootmode, "unknown");
- if ((enable == false) && (0 == strcmp("bp-tools", bootmode))) {
- return 0;
- }
-
- if (!WriteStringToFile(enable ? "1" : "0", "/proc/sys/net/ipv4/ip_forward")) {
- ALOGE("Failed to write ip_forward (%s)", strerror(errno));
- return -1;
- }
+bool TetherController::setIpFwdEnabled() {
+ bool success = true;
+ const char* value = mForwardingRequests.empty() ? "0" : "1";
+ ALOGD("Setting IP forward enable = %s", value);
+ success &= writeToFile(IPV4_FORWARDING_PROC_FILE, value);
+ success &= writeToFile(IPV6_FORWARDING_PROC_FILE, value);
+ return success;
+}
- return 0;
+bool TetherController::enableForwarding(const char* requester) {
+ // Don't return an error if this requester already requested forwarding. Only return errors for
+ // things that the caller caller needs to care about, such as "couldn't write to the file to
+ // enable forwarding".
+ mForwardingRequests.insert(requester);
+ return setIpFwdEnabled();
}
-bool TetherController::getIpFwdEnabled() {
- std::string enabled;
- if (!ReadFileToString("/proc/sys/net/ipv4/ip_forward", &enabled)) {
- ALOGE("Failed to read ip_forward (%s)", strerror(errno));
- return -1;
- }
+bool TetherController::disableForwarding(const char* requester) {
+ mForwardingRequests.erase(requester);
+ return setIpFwdEnabled();
+}
- return (enabled == "1" ? true : false);
+size_t TetherController::forwardingRequestCount() {
+ return mForwardingRequests.size();
}
#define TETHER_START_CONST_ARG 8
#define _TETHER_CONTROLLER_H
#include <netinet/in.h>
+#include <set>
+#include <string>
#include "List.h"
NetAddressCollection *mDnsForwarders;
pid_t mDaemonPid;
int mDaemonFd;
+ std::set<std::string> mForwardingRequests;
public:
TetherController();
virtual ~TetherController();
- int setIpFwdEnabled(bool enable);
- bool getIpFwdEnabled();
+ bool enableForwarding(const char* requester);
+ bool disableForwarding(const char* requester);
+ size_t forwardingRequestCount();
int startTethering(int num_addrs, struct in_addr* addrs);
-
int stopTethering();
bool isTetheringStarted();
private:
int applyDnsInterfaces();
+ bool setIpFwdEnabled();
};
#endif
static bool oemCleanupHooks() {
const char *cmd1[] = {
IPTABLES_PATH,
+ "-w",
"-F",
"oem_out"
};
const char *cmd2[] = {
IPTABLES_PATH,
+ "-w",
"-F",
"oem_fwd"
};
const char *cmd3[] = {
IPTABLES_PATH,
+ "-w",
"-t",
"nat",
"-F",