Add a test for BPF tethering offload.

Bug: 150736748
Test: test-only change
Change-Id: Id1549cc00cde72f8d2b269cf47b475190ebf66ed
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index f4da09a..255de2d 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -32,6 +32,7 @@
 #include <ifaddrs.h>
 #include <linux/if.h>
 #include <linux/if_tun.h>
+#include <net/ethernet.h>
 #include <net/if.h>
 #include <netdb.h>
 #include <netinet/in.h>
@@ -41,6 +42,7 @@
 #include <sys/types.h>
 
 #include <android-base/file.h>
+#include <android-base/format.h>
 #include <android-base/macros.h>
 #include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
@@ -3599,3 +3601,111 @@
     EXPECT_TRUE(mNetd->tetherOffloadRuleAdd(rule).isOk());
     EXPECT_TRUE(mNetd->tetherOffloadRuleRemove(rule).isOk());
 }
+
+static bool expectPacket(int fd, uint8_t* ipPacket, ssize_t ipLen) {
+    constexpr bool kDebug = false;
+
+    uint8_t buf[ETHER_HDR_LEN + 1500];
+
+    // Wait a bit to ensure that the packet we're interested in has arrived.
+    // TODO: speed this up.
+    usleep(100 * 1000);
+
+    ssize_t bytesRead;
+    ssize_t expectedLen = ipLen + ETHER_HDR_LEN;
+    while ((bytesRead = read(fd, buf, sizeof(buf))) >= 0) {
+        if (kDebug) {
+            std::cerr << fmt::format(
+                    "Expected: {:02x}\n  Actual: {:02x}\n",
+                    fmt::join(ipPacket, ipPacket + ipLen, " "),
+                    fmt::join(buf + ETHER_HDR_LEN, buf + ETHER_HDR_LEN + ipLen, " "));
+        }
+
+        if (bytesRead != expectedLen) {
+            continue;
+        }
+
+        if (!memcmp(ipPacket, buf + ETHER_HDR_LEN, ipLen)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+TEST_F(BinderTest, TetherOffloadForwarding) {
+    SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED;
+
+    constexpr const char* kDownstreamPrefix = "2001:db8:2::/64";
+
+    ipv6hdr hdr = {
+            .version = 6,
+            .payload_len = 0,
+            .nexthdr = 59,  // No next header.
+            .hop_limit = 64,
+            .saddr = {{{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                        0x00, 0x00, 0x00, 0x01}}},
+            .daddr = {{{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                        0x0f, 0x00, 0xca, 0xfe}}},
+    };
+
+    // Use one of the test's tun interfaces as upstream.
+    // It must be part of a network or it will not have the clsact attached.
+    EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk());
+    EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk());
+    int fd1 = sTun.getFdForTesting();
+
+    // Create our own tap as a downstream.
+    TunInterface tap;
+    ASSERT_EQ(0, tap.init(true /* isTap */));
+    ASSERT_LE(tap.name().size(), static_cast<size_t>(IFNAMSIZ));
+    int fd2 = tap.getFdForTesting();
+
+    // Set it to nonblocking so that expectPacket can work.
+    int flags = fcntl(fd2, F_GETFL, 0);
+    fcntl(fd2, F_SETFL, flags | O_NONBLOCK);
+
+    // Downstream interface setup. Add to local network, add directly-connected route, etc.
+    binder::Status status = mNetd->networkAddInterface(INetd::LOCAL_NET_ID, tap.name());
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+    status = mNetd->tetherInterfaceAdd(tap.name());
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+    expectTetherInterfaceConfigureForIPv6Router(tap.name());
+
+    // Can't easily use INetd::NEXTHOP_NONE because it is a String16 constant. Use "" instead.
+    status = mNetd->networkAddRoute(INetd::LOCAL_NET_ID, tap.name(), kDownstreamPrefix, "");
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+    // Set up forwarding. All methods take intIface first and extIface second.
+    status = mNetd->tetherAddForward(tap.name(), sTun.name());
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+    status = mNetd->ipfwdAddInterfaceForward(tap.name(), sTun.name());
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+    std::vector<uint8_t> kDummyMac = {02, 00, 00, 00, 00, 00};
+    uint8_t* daddr = reinterpret_cast<uint8_t*>(&hdr.daddr);
+    std::vector<uint8_t> dstAddr(daddr, daddr + sizeof(hdr.daddr));
+
+    TetherOffloadRuleParcel rule = makeTetherOffloadRule(sTun.ifindex(), tap.ifindex(), dstAddr,
+                                                         128, kDummyMac, kDummyMac);
+    status = mNetd->tetherOffloadRuleAdd(rule);
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+    // Receive a packet on sTun.
+    EXPECT_EQ((ssize_t)sizeof(hdr), write(fd1, &hdr, sizeof(hdr)));
+
+    // Expect a packet identical to hdr, except with a TTL of 63.
+    ipv6hdr hdr2 = hdr;
+    hdr2.hop_limit = hdr.hop_limit - 1;
+    EXPECT_TRUE(expectPacket(fd2, (uint8_t*)&hdr2, sizeof(hdr2)));
+
+    // Clean up.
+    EXPECT_TRUE(mNetd->tetherOffloadRuleRemove(rule).isOk());
+    EXPECT_TRUE(mNetd->ipfwdRemoveInterfaceForward(tap.name(), sTun.name()).isOk());
+    EXPECT_TRUE(mNetd->tetherRemoveForward(tap.name(), sTun.name()).isOk());
+    EXPECT_TRUE(mNetd->networkRemoveRoute(INetd::LOCAL_NET_ID, tap.name(), kDownstreamPrefix, "")
+                        .isOk());
+    EXPECT_TRUE(mNetd->tetherInterfaceRemove(tap.name()).isOk());
+    EXPECT_TRUE(mNetd->networkRemoveInterface(INetd::LOCAL_NET_ID, tap.name()).isOk());
+    EXPECT_TRUE(mNetd->networkRemoveInterface(TEST_NETID1, sTun.name()).isOk());
+}