Add binder IPCs to add and remove downstream IPv6 tethering rules
Test: None
Change-Id: Idcb6b9c80de499fafb29e4e8b9202d7b7386340c
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index a62d774..9dd4f2e 100644
--- a/server/TetherController.cpp
+++ b/server/TetherController.cpp
@@ -58,8 +58,11 @@
namespace android {
namespace net {
+using android::base::Error;
using android::base::Join;
using android::base::Pipe;
+using android::base::Result;
+using android::base::StringAppendF;
using android::base::StringPrintf;
using android::base::unique_fd;
using android::netdutils::DumpWriter;
@@ -159,6 +162,16 @@
} else {
setIpFwdEnabled();
}
+
+ // Open BPF maps, ignoring errors because the device might not support BPF offload.
+ int fd = getTetherIngressMapFd();
+ if (fd != -1) {
+ mBpfIngressMap.reset(fd);
+ }
+ fd = getTetherStatsMapFd();
+ if (fd != -1) {
+ mBpfStatsMap.reset(fd);
+ }
}
bool TetherController::setIpFwdEnabled() {
@@ -792,6 +805,57 @@
return 0;
}
+Result<void> TetherController::addDownstreamIpv6Rule(int intIfaceIndex, int extIfaceIndex,
+ const std::vector<uint8_t>& ipAddress,
+ const std::vector<uint8_t>& srcL2Address,
+ const std::vector<uint8_t>& dstL2Address) {
+ ethhdr hdr = {
+ .h_proto = htons(ETH_P_IPV6),
+ };
+ if (ipAddress.size() != sizeof(in6_addr)) {
+ return Error(EINVAL) << "Invalid IP address length " << ipAddress.size();
+ }
+ if (srcL2Address.size() != sizeof(hdr.h_source)) {
+ return Error(EINVAL) << "Invalid L2 src address length " << srcL2Address.size();
+ }
+ if (dstL2Address.size() != sizeof(hdr.h_dest)) {
+ return Error(EINVAL) << "Invalid L2 dst address length " << dstL2Address.size();
+ }
+ memcpy(&hdr.h_dest, dstL2Address.data(), sizeof(hdr.h_dest));
+ memcpy(&hdr.h_source, srcL2Address.data(), sizeof(hdr.h_source));
+
+ TetherIngressKey key = {
+ .iif = static_cast<uint32_t>(extIfaceIndex),
+ .neigh6 = *(const in6_addr*)ipAddress.data(),
+ };
+
+ TetherIngressValue value = {
+ static_cast<uint32_t>(intIfaceIndex),
+ hdr,
+ };
+
+ return mBpfIngressMap.writeValue(key, value, BPF_ANY);
+}
+
+Result<void> TetherController::removeDownstreamIpv6Rule(int extIfaceIndex,
+ const std::vector<uint8_t>& ipAddress) {
+ if (ipAddress.size() != sizeof(in6_addr)) {
+ return Error(EINVAL) << "Invalid IP address length " << ipAddress.size();
+ }
+
+ TetherIngressKey key = {
+ .iif = static_cast<uint32_t>(extIfaceIndex),
+ .neigh6 = *(const in6_addr*)ipAddress.data(),
+ };
+
+ Result<void> ret = mBpfIngressMap.deleteValue(key);
+
+ // Silently return success if the rule did not exist.
+ if (!ret.ok() && ret.error().code() == ENOENT) return {};
+
+ return ret;
+}
+
void TetherController::addStats(TetherStatsList& statsList, const TetherStats& stats) {
for (TetherStats& existing : statsList) {
if (existing.addStatsIfMatch(stats)) {
@@ -981,6 +1045,67 @@
}
}
+namespace {
+
+std::string l2ToString(const uint8_t* addr, size_t len) {
+ std::string str;
+
+ if (len == 0) return str;
+
+ StringAppendF(&str, "%02x", addr[0]);
+ for (size_t i = 1; i < len; i++) {
+ StringAppendF(&str, ":%02x", addr[i]);
+ }
+
+ return str;
+}
+
+} // namespace
+
+void TetherController::dumpBpf(DumpWriter& dw) {
+ if (!mBpfIngressMap.isValid() || !mBpfStatsMap.isValid()) {
+ dw.println("BPF not supported");
+ return;
+ }
+
+ dw.println("BPF ingress map: iif v6addr -> oif srcmac dstmac ethertype");
+ const auto printIngressMap = [&dw](const TetherIngressKey& key, const TetherIngressValue& value,
+ const BpfMap<TetherIngressKey, TetherIngressValue>&) {
+ char addr[INET6_ADDRSTRLEN];
+ std::string src = l2ToString(value.macHeader.h_source, sizeof(value.macHeader.h_source));
+ std::string dst = l2ToString(value.macHeader.h_dest, sizeof(value.macHeader.h_dest));
+ inet_ntop(AF_INET6, &key.neigh6, addr, sizeof(addr));
+
+ dw.println("%d %s -> %d %s %s %04x", key.iif, addr, value.oif, src.c_str(), dst.c_str(),
+ ntohs(value.macHeader.h_proto));
+
+ return Result<void>();
+ };
+
+ dw.incIndent();
+ auto ret = mBpfIngressMap.iterateWithValue(printIngressMap);
+ if (!ret.ok()) {
+ dw.println("Error printing BPF ingress map: %s", ret.error().message().c_str());
+ }
+ dw.decIndent();
+
+ dw.println("BPF stats (downlink): iif -> packets bytes errors");
+ const auto printStatsMap = [&dw](const uint32_t& key, const TetherStatsValue& value,
+ const BpfMap<uint32_t, TetherStatsValue>&) {
+ dw.println("%d -> %" PRIu64 " %" PRIu64 " %" PRIu64, key, value.rxPackets, value.rxBytes,
+ value.rxErrors);
+
+ return Result<void>();
+ };
+
+ dw.incIndent();
+ ret = mBpfStatsMap.iterateWithValue(printStatsMap);
+ if (!ret.ok()) {
+ dw.println("Error printing BPF stats map: %s", ret.error().message().c_str());
+ }
+ dw.decIndent();
+}
+
void TetherController::dump(DumpWriter& dw) {
std::lock_guard guard(lock);
@@ -998,6 +1123,8 @@
}
dumpIfaces(dw);
+ dw.println("");
+ dumpBpf(dw);
}
} // namespace net