bpf tethering offload - add src subnet to upstream ipv6 direction

This patch is based on aosp/2535559 from maze@.

Add source prefix into the upstream key such that only packets which
source IPv6 address matches it will be forwarded to the upstream
interface.

In this patch, the source prefix is set to zero so there is no
behavior changes. Next CL in patch series will use the real source
prefixes retrieved from upstream interface.

Test: atest TetheringTests
Bug: 261923493
Change-Id: I43d068a29b937c7dfeb6fab632a8effb47ee2263
diff --git a/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java b/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
index 898b124..4c9460b 100644
--- a/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
@@ -17,6 +17,7 @@
 package com.android.networkstack.tethering.apishim.api30;
 
 import android.net.INetd;
+import android.net.IpPrefix;
 import android.net.MacAddress;
 import android.net.TetherStatsParcel;
 import android.os.RemoteException;
@@ -81,14 +82,14 @@
 
     @Override
     public boolean startUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
-            @NonNull MacAddress inDstMac, @NonNull MacAddress outSrcMac,
-            @NonNull MacAddress outDstMac, int mtu) {
+            @NonNull IpPrefix sourcePrefix, @NonNull MacAddress inDstMac,
+            @NonNull MacAddress outSrcMac, @NonNull MacAddress outDstMac, int mtu) {
         return true;
     }
 
     @Override
-    public boolean stopUpstreamIpv6Forwarding(int downstreamIfindex,
-            int upstreamIfindex, @NonNull MacAddress inDstMac) {
+    public boolean stopUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
+            @NonNull IpPrefix sourcePrefix, @NonNull MacAddress inDstMac) {
         return true;
     }
 
diff --git a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
index 3cad1c6..062ecc5 100644
--- a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
@@ -17,7 +17,9 @@
 package com.android.networkstack.tethering.apishim.api31;
 
 import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
+import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
 
+import android.net.IpPrefix;
 import android.net.MacAddress;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -48,6 +50,9 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
 
 /**
  * Bpf coordinator class for API shims.
@@ -196,13 +201,23 @@
         return true;
     }
 
+    @NonNull
+    private TetherUpstream6Key makeUpstream6Key(int downstreamIfindex, @NonNull MacAddress inDstMac,
+            @NonNull IpPrefix sourcePrefix) {
+        byte[] prefixBytes = Arrays.copyOf(sourcePrefix.getRawAddress(), 8);
+        long prefix64 = ByteBuffer.wrap(prefixBytes).order(ByteOrder.BIG_ENDIAN).getLong();
+        return new TetherUpstream6Key(downstreamIfindex, inDstMac, prefix64);
+    }
+
     @Override
     public boolean startUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
-            @NonNull MacAddress inDstMac, @NonNull MacAddress outSrcMac,
-            @NonNull MacAddress outDstMac, int mtu) {
+            @NonNull IpPrefix sourcePrefix, @NonNull MacAddress inDstMac,
+            @NonNull MacAddress outSrcMac, @NonNull MacAddress outDstMac, int mtu) {
         if (!isInitialized()) return false;
+        // RFC7421_PREFIX_LENGTH = 64 which is the most commonly used IPv6 subnet prefix length.
+        if (sourcePrefix.getPrefixLength() != RFC7421_PREFIX_LENGTH) return false;
 
-        final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfindex, inDstMac);
+        final TetherUpstream6Key key = makeUpstream6Key(downstreamIfindex, inDstMac, sourcePrefix);
         final Tether6Value value = new Tether6Value(upstreamIfindex, outSrcMac,
                 outDstMac, OsConstants.ETH_P_IPV6, mtu);
         try {
@@ -216,10 +231,12 @@
 
     @Override
     public boolean stopUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
-            @NonNull MacAddress inDstMac) {
+            @NonNull IpPrefix sourcePrefix, @NonNull MacAddress inDstMac) {
         if (!isInitialized()) return false;
+        // RFC7421_PREFIX_LENGTH = 64 which is the most commonly used IPv6 subnet prefix length.
+        if (sourcePrefix.getPrefixLength() != RFC7421_PREFIX_LENGTH) return false;
 
-        final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfindex, inDstMac);
+        final TetherUpstream6Key key = makeUpstream6Key(downstreamIfindex, inDstMac, sourcePrefix);
         try {
             mBpfUpstream6Map.deleteEntry(key);
         } catch (ErrnoException e) {
diff --git a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
index 51cecfe..25fa8bc 100644
--- a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
+++ b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
@@ -16,6 +16,7 @@
 
 package com.android.networkstack.tethering.apishim.common;
 
+import android.net.IpPrefix;
 import android.net.MacAddress;
 import android.util.SparseArray;
 
@@ -79,25 +80,27 @@
 
      * @param downstreamIfindex the downstream interface index
      * @param upstreamIfindex the upstream interface index
+     * @param sourcePrefix the source IPv6 prefix
      * @param inDstMac the destination MAC address to use for XDP
      * @param outSrcMac the source MAC address to use for packets
      * @param outDstMac the destination MAC address to use for packets
      * @return true if operation succeeded or was a no-op, false otherwise
      */
     public abstract boolean startUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
-            @NonNull MacAddress inDstMac, @NonNull MacAddress outSrcMac,
-            @NonNull MacAddress outDstMac, int mtu);
+            @NonNull IpPrefix sourcePrefix, @NonNull MacAddress inDstMac,
+            @NonNull MacAddress outSrcMac, @NonNull MacAddress outDstMac, int mtu);
 
     /**
      * Stops IPv6 forwarding between the specified interfaces.
 
      * @param downstreamIfindex the downstream interface index
      * @param upstreamIfindex the upstream interface index
+     * @param sourcePrefix the valid source IPv6 prefix
      * @param inDstMac the destination MAC address to use for XDP
      * @return true if operation succeeded or was a no-op, false otherwise
      */
-    public abstract boolean stopUpstreamIpv6Forwarding(int downstreamIfindex,
-            int upstreamIfindex, @NonNull MacAddress inDstMac);
+    public abstract boolean stopUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex,
+            @NonNull IpPrefix sourcePrefix, @NonNull MacAddress inDstMac);
 
     /**
      * Return BPF tethering offload statistics.
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 976f5df..f22ccbd 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -37,6 +37,7 @@
 
 import android.app.usage.NetworkStatsManager;
 import android.net.INetd;
+import android.net.IpPrefix;
 import android.net.LinkProperties;
 import android.net.MacAddress;
 import android.net.NetworkStats;
@@ -119,6 +120,7 @@
     private static final int DUMP_TIMEOUT_MS = 10_000;
     private static final MacAddress NULL_MAC_ADDRESS = MacAddress.fromString(
             "00:00:00:00:00:00");
+    private static final IpPrefix IPV6_ZERO_PREFIX64 = new IpPrefix("::/64");
     private static final String TETHER_DOWNSTREAM4_MAP_PATH = makeMapPath(DOWNSTREAM, 4);
     private static final String TETHER_UPSTREAM4_MAP_PATH = makeMapPath(UPSTREAM, 4);
     private static final String TETHER_DOWNSTREAM6_FS_PATH = makeMapPath(DOWNSTREAM, 6);
@@ -615,8 +617,9 @@
             final int upstream = rule.upstreamIfindex;
             // TODO: support upstream forwarding on non-point-to-point interfaces.
             // TODO: get the MTU from LinkProperties and update the rules when it changes.
-            if (!mBpfCoordinatorShim.startUpstreamIpv6Forwarding(downstream, upstream, rule.srcMac,
-                    NULL_MAC_ADDRESS, NULL_MAC_ADDRESS, NetworkStackConstants.ETHER_MTU)) {
+            if (!mBpfCoordinatorShim.startUpstreamIpv6Forwarding(downstream, upstream,
+                    IPV6_ZERO_PREFIX64, rule.srcMac, NULL_MAC_ADDRESS, NULL_MAC_ADDRESS,
+                    NetworkStackConstants.ETHER_MTU)) {
                 mLog.e("Failed to enable upstream IPv6 forwarding from "
                         + getIfName(downstream) + " to " + getIfName(upstream));
             }
@@ -657,7 +660,7 @@
             final int downstream = rule.downstreamIfindex;
             final int upstream = rule.upstreamIfindex;
             if (!mBpfCoordinatorShim.stopUpstreamIpv6Forwarding(downstream, upstream,
-                    rule.srcMac)) {
+                    IPV6_ZERO_PREFIX64, rule.srcMac)) {
                 mLog.e("Failed to disable upstream IPv6 forwarding from "
                         + getIfName(downstream) + " to " + getIfName(upstream));
             }
diff --git a/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java b/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java
index 5893885..36a1c3c 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetherUpstream6Key.java
@@ -29,13 +29,17 @@
     @Field(order = 0, type = Type.S32)
     public final int iif; // The input interface index.
 
-    @Field(order = 1, type = Type.EUI48, padding = 2)
+    @Field(order = 1, type = Type.EUI48, padding = 6)
     public final MacAddress dstMac; // Destination ethernet mac address (zeroed iff rawip ingress).
 
-    public TetherUpstream6Key(int iif, @NonNull final MacAddress dstMac) {
+    @Field(order = 2, type = Type.S64)
+    public final long src64; // The top 64-bits of the source ip.
+
+    public TetherUpstream6Key(int iif, @NonNull final MacAddress dstMac, long src64) {
         Objects.requireNonNull(dstMac);
 
         this.iif = iif;
         this.dstMac = dstMac;
+        this.src64 = src64;
     }
 }
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 46e50ef..464778f 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -985,7 +985,7 @@
             throws Exception {
         if (!mBpfDeps.isAtLeastS()) return;
         final TetherUpstream6Key key = new TetherUpstream6Key(TEST_IFACE_PARAMS.index,
-                TEST_IFACE_PARAMS.macAddr);
+                TEST_IFACE_PARAMS.macAddr, 0);
         final Tether6Value value = new Tether6Value(upstreamIfindex,
                 MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS,
                 ETH_P_IPV6, NetworkStackConstants.ETHER_MTU);
@@ -996,7 +996,7 @@
             throws Exception {
         if (!mBpfDeps.isAtLeastS()) return;
         final TetherUpstream6Key key = new TetherUpstream6Key(TEST_IFACE_PARAMS.index,
-                TEST_IFACE_PARAMS.macAddr);
+                TEST_IFACE_PARAMS.macAddr, 0);
         verifyWithOrder(inOrder, mBpfUpstream6Map).deleteEntry(key);
     }
 
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index 4f32f3c..8bc4c18 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -641,7 +641,7 @@
     private void verifyStartUpstreamIpv6Forwarding(@Nullable InOrder inOrder, int downstreamIfIndex,
             MacAddress downstreamMac, int upstreamIfindex) throws Exception {
         if (!mDeps.isAtLeastS()) return;
-        final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfIndex, downstreamMac);
+        final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfIndex, downstreamMac, 0);
         final Tether6Value value = new Tether6Value(upstreamIfindex,
                 MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS,
                 ETH_P_IPV6, NetworkStackConstants.ETHER_MTU);
@@ -652,7 +652,7 @@
             MacAddress downstreamMac)
             throws Exception {
         if (!mDeps.isAtLeastS()) return;
-        final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfIndex, downstreamMac);
+        final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfIndex, downstreamMac, 0);
         verifyWithOrder(inOrder, mBpfUpstream6Map).deleteEntry(key);
     }
 
@@ -2192,7 +2192,7 @@
         mBpfDownstream6Map.insertEntry(rule.makeTetherDownstream6Key(), rule.makeTether6Value());
 
         final TetherUpstream6Key upstream6Key = new TetherUpstream6Key(DOWNSTREAM_IFINDEX,
-                DOWNSTREAM_MAC);
+                DOWNSTREAM_MAC, 0);
         final Tether6Value upstream6Value = new Tether6Value(UPSTREAM_IFINDEX,
                 MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS,
                 ETH_P_IPV6, NetworkStackConstants.ETHER_MTU);
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
index 8645dd7..c752779 100644
--- a/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -194,6 +194,7 @@
 
     TetherUpstream6Key ku = {
             .iif = skb->ifindex,
+            .src64 = 0,
     };
     if (is_ethernet) __builtin_memcpy(downstream ? kd.dstMac : ku.dstMac, eth->h_dest, ETH_ALEN);
 
diff --git a/bpf_progs/offload.h b/bpf_progs/offload.h
index 9dae6c9..1e28f01 100644
--- a/bpf_progs/offload.h
+++ b/bpf_progs/offload.h
@@ -135,10 +135,10 @@
 typedef struct {
     uint32_t iif;              // The input interface index
     uint8_t dstMac[ETH_ALEN];  // destination ethernet mac address (zeroed iff rawip ingress)
-    uint8_t zero[2];           // zero pad for 8 byte alignment
-                               // TODO: extend this to include src ip /64 subnet
+    uint8_t zero[6];           // zero pad for 8 byte alignment
+    uint64_t src64;            // Top 64-bits of the src ip
 } TetherUpstream6Key;
-STRUCT_SIZE(TetherUpstream6Key, 12);
+STRUCT_SIZE(TetherUpstream6Key, 4 + 6 + 6 + 8);  // 24
 
 typedef struct {
     uint32_t iif;              // The input interface index