Fix tethering in the case of a regular upstream connection.
Fixes tethering via Ethernet, Bluetooth and WiFi (hotspot).
Tethering when the upstream has a DUN-specific APN is likely still broken
(untested).
For now, assign a fixed NetId (a hack) until we can change the framework to
create a valid NetworkAgent and all that jazz.
Bug: 15968336
Bug: 14988803
Change-Id: Idcf4d492d9329a9c87913e27be6dd835a792bea2
diff --git a/server/Android.mk b/server/Android.mk
index 28defba..475d817 100644
--- a/server/Android.mk
+++ b/server/Android.mk
@@ -48,6 +48,7 @@
FwmarkServer.cpp \
IdletimerController.cpp \
InterfaceController.cpp \
+ LocalNetwork.cpp \
MDnsSdListener.cpp \
NatController.cpp \
NetdCommand.cpp \
diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp
index b7723d4..f2a8745 100644
--- a/server/CommandListener.cpp
+++ b/server/CommandListener.cpp
@@ -174,9 +174,9 @@
if (!sSecondaryTableCtrl)
sSecondaryTableCtrl = new SecondaryTableController(sNetCtrl);
if (!sTetherCtrl)
- sTetherCtrl = new TetherController();
+ sTetherCtrl = new TetherController(sNetCtrl);
if (!sNatCtrl)
- sNatCtrl = new NatController(sSecondaryTableCtrl, sNetCtrl);
+ sNatCtrl = new NatController(sNetCtrl);
if (!sPppCtrl)
sPppCtrl = new PppController();
if (!sSoftapCtrl)
diff --git a/server/LocalNetwork.cpp b/server/LocalNetwork.cpp
new file mode 100644
index 0000000..ba0b21e
--- /dev/null
+++ b/server/LocalNetwork.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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 "LocalNetwork.h"
+
+#include "RouteController.h"
+
+#define LOG_TAG "Netd"
+#include "log/log.h"
+
+LocalNetwork::LocalNetwork(unsigned netId) : Network(netId) {
+}
+
+LocalNetwork::~LocalNetwork() {
+}
+
+Network::Type LocalNetwork::getType() const {
+ return LOCAL;
+}
+
+int LocalNetwork::addInterface(const std::string& interface) {
+ if (hasInterface(interface)) {
+ return 0;
+ }
+ if (int ret = RouteController::addInterfaceToLocalNetwork(mNetId, interface.c_str())) {
+ ALOGE("failed to add interface %s to local netId %u", interface.c_str(), mNetId);
+ return ret;
+ }
+ mInterfaces.insert(interface);
+ return 0;
+}
+
+int LocalNetwork::removeInterface(const std::string& interface) {
+ if (!hasInterface(interface)) {
+ return 0;
+ }
+ if (int ret = RouteController::removeInterfaceFromLocalNetwork(mNetId, interface.c_str())) {
+ ALOGE("failed to remove interface %s from local netId %u", interface.c_str(), mNetId);
+ return ret;
+ }
+ mInterfaces.erase(interface);
+ return 0;
+}
diff --git a/server/LocalNetwork.h b/server/LocalNetwork.h
new file mode 100644
index 0000000..89a67f4
--- /dev/null
+++ b/server/LocalNetwork.h
@@ -0,0 +1,33 @@
+/*
+ * 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_LOCAL_NETWORK_H
+#define NETD_SERVER_LOCAL_NETWORK_H
+
+#include "Network.h"
+
+class LocalNetwork : public Network {
+public:
+ explicit LocalNetwork(unsigned netId);
+ virtual ~LocalNetwork();
+
+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_LOCAL_NETWORK_H
diff --git a/server/NatController.cpp b/server/NatController.cpp
index 6c066f8..8340b73 100644
--- a/server/NatController.cpp
+++ b/server/NatController.cpp
@@ -33,15 +33,13 @@
#include "NatController.h"
#include "NetworkController.h"
-#include "SecondaryTableController.h"
#include "NetdConstants.h"
const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD";
const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING";
const char* NatController::LOCAL_TETHER_COUNTERS_CHAIN = "natctrl_tether_counters";
-NatController::NatController(SecondaryTableController *table_ctrl, NetworkController* net_ctrl) :
- mSecondaryTableCtrl(table_ctrl), mNetCtrl(net_ctrl) {
+NatController::NatController(NetworkController* net_ctrl) : mNetCtrl(net_ctrl) {
}
NatController::~NatController() {
@@ -114,12 +112,6 @@
{{IPTABLES_PATH, "-F", LOCAL_FORWARD,}, 1},
{{IPTABLES_PATH, "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
{{IPTABLES_PATH, "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1},
- {{IP_PATH, "rule", "flush"}, 0},
- {{IP_PATH, "-6", "rule", "flush"}, 0},
- {{IP_PATH, "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0},
- {{IP_PATH, "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0},
- {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0},
- {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0},
{{IP_PATH, "route", "flush", "cache"}, 0},
};
for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
@@ -134,17 +126,15 @@
return 0;
}
-int NatController::routesOp(bool add, const char *intIface, const char *extIface, char **argv, int addrCount) {
- unsigned netId = mNetCtrl->getNetworkForInterface(extIface);
+int NatController::routesOp(bool add, const char *intIface, char **argv, int addrCount) {
+ unsigned netId = mNetCtrl->getNetworkForInterface(intIface);
int ret = 0;
for (int i = 0; i < addrCount; i++) {
if (add) {
- ret |= mSecondaryTableCtrl->modifyFromRule(netId, ADD, argv[5+i]);
- ret |= mSecondaryTableCtrl->modifyLocalRoute(netId, ADD, intIface, argv[5+i]);
+ ret |= mNetCtrl->addRoute(netId, intIface, argv[5+i], NULL, false, INVALID_UID);
} else {
- ret |= mSecondaryTableCtrl->modifyLocalRoute(netId, DEL, intIface, argv[5+i]);
- ret |= mSecondaryTableCtrl->modifyFromRule(netId, DEL, argv[5+i]);
+ ret |= mNetCtrl->addRoute(netId, intIface, argv[5+i], NULL, false, INVALID_UID);
}
}
const char *cmd[] = {
@@ -183,9 +173,9 @@
errno = EINVAL;
return -1;
}
- if (routesOp(true, intIface, extIface, argv, addrCount)) {
+ if (routesOp(true, intIface, argv, addrCount)) {
ALOGE("Error setting route rules");
- routesOp(false, intIface, extIface, argv, addrCount);
+ routesOp(false, intIface, argv, addrCount);
errno = ENODEV;
return -1;
}
@@ -206,7 +196,7 @@
if (runCmd(ARRAY_SIZE(cmd), cmd)) {
ALOGE("Error seting postroute rule: iface=%s", extIface);
// unwind what's been done, but don't care about success - what more could we do?
- routesOp(false, intIface, extIface, argv, addrCount);
+ routesOp(false, intIface, argv, addrCount);
setDefaults();
return -1;
}
@@ -215,7 +205,7 @@
if (setForwardRules(true, intIface, extIface) != 0) {
ALOGE("Error setting forward rules");
- routesOp(false, intIface, extIface, argv, addrCount);
+ routesOp(false, intIface, argv, addrCount);
if (natCount == 0) {
setDefaults();
}
@@ -415,7 +405,7 @@
}
setForwardRules(false, intIface, extIface);
- routesOp(false, intIface, extIface, argv, addrCount);
+ routesOp(false, intIface, argv, addrCount);
if (--natCount <= 0) {
// handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
setDefaults();
diff --git a/server/NatController.h b/server/NatController.h
index afddb27..e3cc394 100644
--- a/server/NatController.h
+++ b/server/NatController.h
@@ -22,12 +22,11 @@
#include <string>
class NetworkController;
-class SecondaryTableController;
class NatController {
public:
- NatController(SecondaryTableController *table_ctrl, NetworkController* net_ctrl);
+ explicit NatController(NetworkController* net_ctrl);
virtual ~NatController();
int enableNat(const int argc, char **argv);
@@ -43,7 +42,6 @@
private:
int natCount;
- SecondaryTableController *mSecondaryTableCtrl;
NetworkController *mNetCtrl;
bool checkTetherCountingRuleExist(const char *pair_name);
@@ -52,7 +50,7 @@
int runCmd(int argc, const char **argv);
int setForwardRules(bool set, const char *intIface, const char *extIface);
int setTetherCountingRules(bool add, const char *intIface, const char *extIface);
- int routesOp(bool add, const char *intIface, const char *extIface, char **argv, int addrCount);
+ int routesOp(bool add, const char *intIface, char **argv, int addrCount);
};
#endif
diff --git a/server/Network.h b/server/Network.h
index f72cebb..39c81aa 100644
--- a/server/Network.h
+++ b/server/Network.h
@@ -26,6 +26,7 @@
class Network {
public:
enum Type {
+ LOCAL,
PHYSICAL,
VIRTUAL,
};
diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp
index 1487b72..3bc0e70 100644
--- a/server/NetworkController.cpp
+++ b/server/NetworkController.cpp
@@ -32,6 +32,7 @@
#include "NetworkController.h"
+#include "LocalNetwork.h"
#include "PhysicalNetwork.h"
#include "RouteController.h"
#include "VirtualNetwork.h"
@@ -110,6 +111,29 @@
return NETID_UNSET;
}
+unsigned NetworkController::getNetIdForLocalNetwork() const {
+ return MIN_NET_ID - 1;
+}
+
+int NetworkController::createLocalNetwork(unsigned netId) {
+ // TODO: Enable this check after removing the getNetIdForLocalNetwork() hack.
+ if (false) {
+ if (netId < MIN_NET_ID || netId > MAX_NET_ID) {
+ ALOGE("invalid netId %u", netId);
+ return -EINVAL;
+ }
+ }
+
+ if (isValidNetwork(netId)) {
+ ALOGE("duplicate netId %u", netId);
+ return -EEXIST;
+ }
+
+ android::RWLock::AutoWLock lock(mRWLock);
+ mNetworks[netId] = new LocalNetwork(netId);
+ return 0;
+}
+
int NetworkController::createPhysicalNetwork(unsigned netId, Permission permission) {
if (netId < MIN_NET_ID || netId > MAX_NET_ID) {
ALOGE("invalid netId %u", netId);
diff --git a/server/NetworkController.h b/server/NetworkController.h
index 217dfbc..f0a5ea7 100644
--- a/server/NetworkController.h
+++ b/server/NetworkController.h
@@ -52,6 +52,10 @@
unsigned getNetworkForUser(uid_t uid, unsigned requestedNetId, bool forDns) const;
unsigned getNetworkForInterface(const char* interface) const;
+ // TODO: Remove this hack.
+ unsigned getNetIdForLocalNetwork() const;
+
+ int createLocalNetwork(unsigned netId) WARN_UNUSED_RESULT;
int createPhysicalNetwork(unsigned netId, Permission permission) WARN_UNUSED_RESULT;
int createVirtualNetwork(unsigned netId, bool hasDns) WARN_UNUSED_RESULT;
int destroyNetwork(unsigned netId) WARN_UNUSED_RESULT;
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index d33c47c..8efbda4 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -42,7 +42,7 @@
const uint32_t RULE_PRIORITY_OUTPUT_INTERFACE = 14000;
const uint32_t RULE_PRIORITY_LEGACY_SYSTEM = 15000;
const uint32_t RULE_PRIORITY_LEGACY_NETWORK = 16000;
-// const uint32_t RULE_PRIORITY_LOCAL_NETWORK = 17000;
+const uint32_t RULE_PRIORITY_LOCAL_NETWORK = 17000;
// const uint32_t RULE_PRIORITY_TETHERING = 18000;
const uint32_t RULE_PRIORITY_IMPLICIT_NETWORK = 19000;
// const uint32_t RULE_PRIORITY_BYPASSABLE_VPN = 20000;
@@ -542,6 +542,38 @@
fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID);
}
+// A rule to allow local network routes to override the default network.
+WARN_UNUSED_RESULT int modifyLocalOverrideRule(uint32_t table, bool add) {
+ Fwmark fwmark;
+ Fwmark mask;
+
+ fwmark.explicitlySelected = false;
+ mask.explicitlySelected = true;
+
+ return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_LOCAL_NETWORK, table,
+ fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID);
+}
+
+WARN_UNUSED_RESULT int modifyLocalNetwork(unsigned netId, const char* interface, bool add) {
+ uint32_t table = getRouteTableForInterface(interface);
+ if (table == RT_TABLE_UNSPEC) {
+ return -ESRCH;
+ }
+
+ if (int ret = modifyIncomingPacketMark(netId, interface, PERMISSION_NONE, add)) {
+ return ret;
+ }
+ if (int ret = modifyExplicitNetworkRule(netId, table, PERMISSION_NONE, INVALID_UID, INVALID_UID,
+ add)) {
+ return ret;
+ }
+ if (int ret = modifyOutputInterfaceRule(interface, table, PERMISSION_NONE, INVALID_UID,
+ INVALID_UID, add)) {
+ return ret;
+ }
+ return modifyLocalOverrideRule(table, add);
+}
+
WARN_UNUSED_RESULT int modifyPhysicalNetwork(unsigned netId, const char* interface,
Permission permission, bool add) {
uint32_t table = getRouteTableForInterface(interface);
@@ -714,6 +746,25 @@
return 0;
}
+int RouteController::addInterfaceToLocalNetwork(unsigned netId, const char* interface) {
+ if (int ret = modifyLocalNetwork(netId, interface, ACTION_ADD)) {
+ return ret;
+ }
+ updateTableNamesFile();
+ return 0;
+}
+
+int RouteController::removeInterfaceFromLocalNetwork(unsigned netId, const char* interface) {
+ if (int ret = modifyLocalNetwork(netId, interface, ACTION_DEL)) {
+ return ret;
+ }
+ if (int ret = flushRoutes(interface)) {
+ return ret;
+ }
+ updateTableNamesFile();
+ return 0;
+}
+
int RouteController::addInterfaceToPhysicalNetwork(unsigned netId, const char* interface,
Permission permission) {
if (int ret = modifyPhysicalNetwork(netId, interface, permission, ACTION_ADD)) {
diff --git a/server/RouteController.h b/server/RouteController.h
index 86ac089..a3010de 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -37,6 +37,10 @@
static int Init() WARN_UNUSED_RESULT;
+ static int addInterfaceToLocalNetwork(unsigned netId, const char* interface) WARN_UNUSED_RESULT;
+ static int removeInterfaceFromLocalNetwork(unsigned netId,
+ const char* interface) WARN_UNUSED_RESULT;
+
static int addInterfaceToPhysicalNetwork(unsigned netId, const char* interface,
Permission permission) WARN_UNUSED_RESULT;
static int removeInterfaceFromPhysicalNetwork(unsigned netId, const char* interface,
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index fbee5a2..47b620d 100644
--- a/server/TetherController.cpp
+++ b/server/TetherController.cpp
@@ -33,8 +33,10 @@
#include "NetdConstants.h"
#include "TetherController.h"
+#include "NetworkController.h"
-TetherController::TetherController() {
+TetherController::TetherController(NetworkController* networkController) :
+ mNetworkController(networkController) {
mInterfaces = new InterfaceCollection();
mDnsForwarders = new NetAddressCollection();
mDaemonFd = -1;
@@ -170,6 +172,17 @@
ALOGD("Tethering services running");
}
+ unsigned netId = mNetworkController->getNetIdForLocalNetwork();
+ if (int ret = mNetworkController->createLocalNetwork(netId)) {
+ return ret;
+ }
+ // If any interfaces have already been configured, add them to the local network now.
+ for (InterfaceCollection::iterator it = mInterfaces->begin(); it != mInterfaces->end(); ++it) {
+ if (int ret = mNetworkController->addInterfaceToNetwork(netId, *it)) {
+ return ret;
+ }
+ }
+
return 0;
}
@@ -182,6 +195,9 @@
ALOGD("Stopping tethering services");
+ // Ignore any error.
+ (void) mNetworkController->destroyNetwork(mNetworkController->getNetIdForLocalNetwork());
+
kill(mDaemonPid, SIGTERM);
waitpid(mDaemonPid, NULL, 0);
mDaemonPid = 0;
@@ -291,6 +307,10 @@
}
return -1;
} else {
+ if (isTetheringStarted()) {
+ unsigned netId = mNetworkController->getNetIdForLocalNetwork();
+ return mNetworkController->addInterfaceToNetwork(netId, interface);
+ }
return 0;
}
}
@@ -300,6 +320,12 @@
ALOGD("untetherInterface(%s)", interface);
+ if (isTetheringStarted()) {
+ unsigned netId = mNetworkController->getNetIdForLocalNetwork();
+ // Ignore any error.
+ (void) mNetworkController->removeInterfaceFromNetwork(netId, interface);
+ }
+
for (it = mInterfaces->begin(); it != mInterfaces->end(); ++it) {
if (!strcmp(interface, *it)) {
free(*it);
diff --git a/server/TetherController.h b/server/TetherController.h
index 4da10b0..38f7593 100644
--- a/server/TetherController.h
+++ b/server/TetherController.h
@@ -24,14 +24,17 @@
typedef android::netd::List<char *> InterfaceCollection;
typedef android::netd::List<struct in_addr> NetAddressCollection;
+class NetworkController;
+
class TetherController {
InterfaceCollection *mInterfaces;
NetAddressCollection *mDnsForwarders;
pid_t mDaemonPid;
int mDaemonFd;
+ NetworkController* const mNetworkController;
public:
- TetherController();
+ explicit TetherController(NetworkController* networkController);
virtual ~TetherController();
int setIpFwdEnabled(bool enable);