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