diff options
| author | Lorenzo Colitti <lorenzo@google.com> | 2017-09-01 06:05:27 +0000 |
|---|---|---|
| committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-09-01 06:05:27 +0000 |
| commit | a2d039c860379779bc079bc0e800d7b472e7bb3f (patch) | |
| tree | ad82a946ebd6456e3a4ba9f73dd8ac41c65b1080 /server/TetherController.cpp | |
| parent | 84344e1fe371852e38ca19586061977616b21a9a (diff) | |
| parent | 4604b4a4319fe749a812b7c32cfdd459b0f53f47 (diff) | |
Merge changes Ifd209207,Ib7440f93,Idc80bfd4
* changes:
Rename natctrl_* iptables rules to tetherctrl_*
Remove support for filtering tether stats.
Move all tethering functionality into TetherController.
Diffstat (limited to 'server/TetherController.cpp')
| -rw-r--r-- | server/TetherController.cpp | 373 |
1 files changed, 372 insertions, 1 deletions
diff --git a/server/TetherController.cpp b/server/TetherController.cpp index 1785ec71..ad326823 100644 --- a/server/TetherController.cpp +++ b/server/TetherController.cpp @@ -14,10 +14,11 @@ * limitations under the License. */ -#include <stdlib.h> #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <netdb.h> +#include <stdlib.h> #include <string.h> #include <sys/socket.h> @@ -29,6 +30,8 @@ #include <arpa/inet.h> #define LOG_TAG "TetherController" +#include <android-base/strings.h> +#include <android-base/stringprintf.h> #include <cutils/log.h> #include <cutils/properties.h> @@ -37,8 +40,13 @@ #include "Permission.h" #include "InterfaceController.h" #include "NetworkController.h" +#include "ResponseCode.h" #include "TetherController.h" +using android::base::Join; +using android::base::StringPrintf; +using android::base::StringAppendF; + namespace { const char BP_TOOLS_MODE[] = "bp-tools"; @@ -90,6 +98,15 @@ bool inBpToolsMode() { namespace android { namespace net { +auto TetherController::iptablesRestoreFunction = execIptablesRestoreWithOutput; + +const int MAX_IPT_OUTPUT_LINE_LEN = 256; + +const std::string GET_TETHER_STATS_COMMAND = StringPrintf( + "*filter\n" + "-nvx -L %s\n" + "COMMIT\n", android::net::TetherController::LOCAL_TETHER_COUNTERS_CHAIN); + TetherController::TetherController() { mDnsNetId = 0; mDaemonFd = -1; @@ -105,6 +122,7 @@ TetherController::~TetherController() { mInterfaces.clear(); mDnsForwarders.clear(); mForwardingRequests.clear(); + ifacePairList.clear(); } bool TetherController::setIpFwdEnabled() { @@ -371,5 +389,358 @@ const std::list<std::string> &TetherController::getTetheredInterfaceList() const return mInterfaces; } +int TetherController::setupIptablesHooks() { + int res; + res = setDefaults(); + if (res < 0) { + return res; + } + + // Used to limit downstream mss to the upstream pmtu so we don't end up fragmenting every large + // packet tethered devices send. This is IPv4-only, because in IPv6 we send the MTU in the RA. + // This is no longer optional and tethering will fail to start if it fails. + std::string mssRewriteCommand = StringPrintf( + "*mangle\n" + "-A %s -p tcp --tcp-flags SYN SYN -j TCPMSS --clamp-mss-to-pmtu\n" + "COMMIT\n", LOCAL_MANGLE_FORWARD); + + // This is for tethering counters. This chain is reached via --goto, and then RETURNS. + std::string defaultCommands = StringPrintf( + "*filter\n" + ":%s -\n" + "COMMIT\n", LOCAL_TETHER_COUNTERS_CHAIN); + + res = iptablesRestoreFunction(V4, mssRewriteCommand, nullptr); + if (res < 0) { + return res; + } + + res = iptablesRestoreFunction(V4V6, defaultCommands, nullptr); + if (res < 0) { + return res; + } + + ifacePairList.clear(); + + return 0; +} + +int TetherController::setDefaults() { + std::string v4Cmd = StringPrintf( + "*filter\n" + ":%s -\n" + "-A %s -j DROP\n" + "COMMIT\n" + "*nat\n" + ":%s -\n" + "COMMIT\n", LOCAL_FORWARD, LOCAL_FORWARD, LOCAL_NAT_POSTROUTING); + + std::string v6Cmd = StringPrintf( + "*filter\n" + ":%s -\n" + "COMMIT\n" + "*raw\n" + ":%s -\n" + "COMMIT\n", LOCAL_FORWARD, LOCAL_RAW_PREROUTING); + + int res = iptablesRestoreFunction(V4, v4Cmd, nullptr); + if (res < 0) { + return res; + } + + res = iptablesRestoreFunction(V6, v6Cmd, nullptr); + if (res < 0) { + return res; + } + + natCount = 0; + + return 0; +} + +int TetherController::enableNat(const char* intIface, const char* extIface) { + ALOGV("enableNat(intIface=<%s>, extIface=<%s>)",intIface, extIface); + + if (!isIfaceName(intIface) || !isIfaceName(extIface)) { + errno = ENODEV; + return -1; + } + + /* Bug: b/9565268. "enableNat wlan0 wlan0". For now we fail until java-land is fixed */ + if (!strcmp(intIface, extIface)) { + ALOGE("Duplicate interface specified: %s %s", intIface, extIface); + errno = EINVAL; + return -1; + } + + // add this if we are the first added nat + if (natCount == 0) { + std::vector<std::string> v4Cmds = { + "*nat", + StringPrintf("-A %s -o %s -j MASQUERADE", LOCAL_NAT_POSTROUTING, extIface), + "COMMIT\n" + }; + + /* + * IPv6 tethering doesn't need the state-based conntrack rules, so + * it unconditionally jumps to the tether counters chain all the time. + */ + std::vector<std::string> v6Cmds = { + "*filter", + StringPrintf("-A %s -g %s", LOCAL_FORWARD, LOCAL_TETHER_COUNTERS_CHAIN), + "COMMIT\n" + }; + + if (iptablesRestoreFunction(V4, Join(v4Cmds, '\n'), nullptr) || + iptablesRestoreFunction(V6, Join(v6Cmds, '\n'), nullptr)) { + ALOGE("Error setting postroute rule: iface=%s", extIface); + // unwind what's been done, but don't care about success - what more could we do? + setDefaults(); + return -1; + } + } + + if (setForwardRules(true, intIface, extIface) != 0) { + ALOGE("Error setting forward rules"); + if (natCount == 0) { + setDefaults(); + } + errno = ENODEV; + return -1; + } + + natCount++; + return 0; +} + +bool TetherController::checkTetherCountingRuleExist(const std::string& pair_name) { + return std::find(ifacePairList.begin(), ifacePairList.end(), pair_name) != ifacePairList.end(); +} + +/* static */ +std::string TetherController::makeTetherCountingRule(const char *if1, const char *if2) { + return StringPrintf("-A %s -i %s -o %s -j RETURN", LOCAL_TETHER_COUNTERS_CHAIN, if1, if2); +} + +int TetherController::setForwardRules(bool add, const char *intIface, const char *extIface) { + const char *op = add ? "-A" : "-D"; + + std::string rpfilterCmd = StringPrintf( + "*raw\n" + "%s %s -i %s -m rpfilter --invert ! -s fe80::/64 -j DROP\n" + "COMMIT\n", op, LOCAL_RAW_PREROUTING, intIface); + if (iptablesRestoreFunction(V6, rpfilterCmd, nullptr) == -1 && add) { + return -1; + } + + std::vector<std::string> v4 = { + "*filter", + StringPrintf("%s %s -i %s -o %s -m state --state ESTABLISHED,RELATED -g %s", + op, LOCAL_FORWARD, extIface, intIface, LOCAL_TETHER_COUNTERS_CHAIN), + StringPrintf("%s %s -i %s -o %s -m state --state INVALID -j DROP", + op, LOCAL_FORWARD, intIface, extIface), + StringPrintf("%s %s -i %s -o %s -g %s", + op, LOCAL_FORWARD, intIface, extIface, LOCAL_TETHER_COUNTERS_CHAIN), + }; + + std::vector<std::string> v6 = { + "*filter", + }; + + /* We only ever add tethering quota rules so that they stick. */ + std::string pair1 = StringPrintf("%s_%s", intIface, extIface); + if (add && !checkTetherCountingRuleExist(pair1)) { + v4.push_back(makeTetherCountingRule(intIface, extIface)); + v6.push_back(makeTetherCountingRule(intIface, extIface)); + } + std::string pair2 = StringPrintf("%s_%s", extIface, intIface); + if (add && !checkTetherCountingRuleExist(pair2)) { + v4.push_back(makeTetherCountingRule(extIface, intIface)); + v6.push_back(makeTetherCountingRule(extIface, intIface)); + } + + // Always make sure the drop rule is at the end. + // TODO: instead of doing this, consider just rebuilding LOCAL_FORWARD completely from scratch + // every time, starting with ":tetherctrl_FORWARD -\n". This would likely be a bit simpler. + if (add) { + v4.push_back(StringPrintf("-D %s -j DROP", LOCAL_FORWARD)); + v4.push_back(StringPrintf("-A %s -j DROP", LOCAL_FORWARD)); + } + + v4.push_back("COMMIT\n"); + v6.push_back("COMMIT\n"); + + // We only add IPv6 rules here, never remove them. + if (iptablesRestoreFunction(V4, Join(v4, '\n'), nullptr) == -1 || + (add && iptablesRestoreFunction(V6, Join(v6, '\n'), nullptr) == -1)) { + // unwind what's been done, but don't care about success - what more could we do? + if (add) { + setForwardRules(false, intIface, extIface); + } + return -1; + } + + if (add && !checkTetherCountingRuleExist(pair1)) { + ifacePairList.push_front(pair1); + } + if (add && !checkTetherCountingRuleExist(pair2)) { + ifacePairList.push_front(pair2); + } + + return 0; +} + +int TetherController::disableNat(const char* intIface, const char* extIface) { + if (!isIfaceName(intIface) || !isIfaceName(extIface)) { + errno = ENODEV; + return -1; + } + + setForwardRules(false, intIface, extIface); + if (--natCount <= 0) { + // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0 + setDefaults(); + } + return 0; +} + +void TetherController::addStats(TetherStatsList& statsList, const TetherStats& stats) { + for (TetherStats& existing : statsList) { + if (existing.addStatsIfMatch(stats)) { + return; + } + } + // No match. Insert a new interface pair. + statsList.push_back(stats); +} + +/* + * Parse the ptks and bytes out of: + * Chain tetherctrl_counters (4 references) + * pkts bytes target prot opt in out source destination + * 26 2373 RETURN all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0 + * 27 2002 RETURN all -- rmnet0 wlan0 0.0.0.0/0 0.0.0.0/0 + * 1040 107471 RETURN all -- bt-pan rmnet0 0.0.0.0/0 0.0.0.0/0 + * 1450 1708806 RETURN all -- rmnet0 bt-pan 0.0.0.0/0 0.0.0.0/0 + * or: + * Chain tetherctrl_counters (0 references) + * pkts bytes target prot opt in out source destination + * 0 0 RETURN all wlan0 rmnet_data0 ::/0 ::/0 + * 0 0 RETURN all rmnet_data0 wlan0 ::/0 ::/0 + * + */ +int TetherController::addForwardChainStats(TetherStatsList& statsList, + const std::string& statsOutput, + std::string &extraProcessingInfo) { + int res; + std::string statsLine; + char iface0[MAX_IPT_OUTPUT_LINE_LEN]; + char iface1[MAX_IPT_OUTPUT_LINE_LEN]; + char rest[MAX_IPT_OUTPUT_LINE_LEN]; + + TetherStats stats; + const TetherStats empty; + const char *buffPtr; + int64_t packets, bytes; + int statsFound = 0; + + std::stringstream stream(statsOutput); + + // Skip headers. + for (int i = 0; i < 2; i++) { + std::getline(stream, statsLine, '\n'); + extraProcessingInfo += statsLine + "\n"; + if (statsLine.empty()) { + ALOGE("Empty header while parsing tethering stats"); + return -1; + } + } + + while (std::getline(stream, statsLine, '\n')) { + buffPtr = statsLine.c_str(); + + /* Clean up, so a failed parse can still print info */ + iface0[0] = iface1[0] = rest[0] = packets = bytes = 0; + if (strstr(buffPtr, "0.0.0.0")) { + // IPv4 has -- indicating what to do with fragments... + // 26 2373 RETURN all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0 + res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all -- %s %s 0.%s", + &packets, &bytes, iface0, iface1, rest); + } else { + // ... but IPv6 does not. + // 26 2373 RETURN all wlan0 rmnet0 ::/0 ::/0 + res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all %s %s ::/%s", + &packets, &bytes, iface0, iface1, rest); + } + ALOGV("parse res=%d iface0=<%s> iface1=<%s> pkts=%" PRId64" bytes=%" PRId64" rest=<%s> orig line=<%s>", res, + iface0, iface1, packets, bytes, rest, buffPtr); + extraProcessingInfo += buffPtr; + extraProcessingInfo += "\n"; + + if (res != 5) { + return -EREMOTEIO; + } + /* + * The following assumes that the 1st rule has in:extIface out:intIface, + * which is what TetherController sets up. + * The 1st matches rx, and sets up the pair for the tx side. + */ + if (!stats.intIface[0]) { + ALOGV("0Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets); + stats.intIface = iface0; + stats.extIface = iface1; + stats.rxPackets = packets; + stats.rxBytes = bytes; + } else if (stats.intIface == iface1 && stats.extIface == iface0) { + ALOGV("0Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets); + stats.txPackets = packets; + stats.txBytes = bytes; + } + if (stats.rxBytes != -1 && stats.txBytes != -1) { + ALOGV("rx_bytes=%" PRId64" tx_bytes=%" PRId64, stats.rxBytes, stats.txBytes); + addStats(statsList, stats); + statsFound++; + stats = empty; + } + } + + /* It is always an error to find only one side of the stats. */ + if (((stats.rxBytes == -1) != (stats.txBytes == -1)) || !statsFound) { + return -1; + } + return 0; +} + +std::string TetherController::TetherStats::getStatsLine() const { + std::string msg; + StringAppendF(&msg, "%s %s %" PRId64" %" PRId64" %" PRId64" %" PRId64, intIface.c_str(), + extIface.c_str(), rxBytes, rxPackets, txBytes, txPackets); + return msg; +} + +int TetherController::getTetherStats(SocketClient *cli, std::string &extraProcessingInfo) { + TetherStatsList statsList; + + for (const IptablesTarget target : {V4, V6}) { + std::string statsString; + if (int ret = iptablesRestoreFunction(target, GET_TETHER_STATS_COMMAND, &statsString)) { + ALOGE("Failed to run %s err=%d", GET_TETHER_STATS_COMMAND.c_str(), ret); + return -1; + } + + if (int ret = addForwardChainStats(statsList, statsString, extraProcessingInfo)) { + return ret; + } + } + + for (const auto& stats: statsList) { + cli->sendMsg(ResponseCode::TetheringStatsListResult, + stats.getStatsLine().c_str(), false); + } + cli->sendMsg(ResponseCode::CommandOkay, "Tethering stats list completed", false); + + return 0; +} + } // namespace net } // namespace android |
