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 4c9460b423..0df904786f 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,8 +17,6 @@ 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; import android.os.ServiceSpecificException; @@ -33,7 +31,8 @@ import com.android.net.module.util.bpf.Tether4Key; import com.android.net.module.util.bpf.Tether4Value; import com.android.net.module.util.bpf.TetherStatsValue; import com.android.networkstack.tethering.BpfCoordinator.Dependencies; -import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; +import com.android.networkstack.tethering.BpfCoordinator.Ipv6DownstreamRule; +import com.android.networkstack.tethering.BpfCoordinator.Ipv6UpstreamRule; /** * Bpf coordinator class for API shims. @@ -58,7 +57,17 @@ public class BpfCoordinatorShimImpl }; @Override - public boolean tetherOffloadRuleAdd(@NonNull final Ipv6ForwardingRule rule) { + public boolean addIpv6UpstreamRule(@NonNull final Ipv6UpstreamRule rule) { + return true; + }; + + @Override + public boolean removeIpv6UpstreamRule(@NonNull final Ipv6UpstreamRule rule) { + return true; + } + + @Override + public boolean addIpv6DownstreamRule(@NonNull final Ipv6DownstreamRule rule) { try { mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel()); } catch (RemoteException | ServiceSpecificException e) { @@ -70,7 +79,7 @@ public class BpfCoordinatorShimImpl }; @Override - public boolean tetherOffloadRuleRemove(@NonNull final Ipv6ForwardingRule rule) { + public boolean removeIpv6DownstreamRule(@NonNull final Ipv6DownstreamRule rule) { try { mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel()); } catch (RemoteException | ServiceSpecificException e) { @@ -80,19 +89,6 @@ public class BpfCoordinatorShimImpl return true; } - @Override - public boolean startUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex, - @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 IpPrefix sourcePrefix, @NonNull MacAddress inDstMac) { - return true; - } - @Override @Nullable public SparseArray tetherOffloadGetStats() { 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 119fbc67a7..a280046c52 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 @@ -20,8 +20,6 @@ import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH; -import android.net.IpPrefix; -import android.net.MacAddress; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; @@ -39,7 +37,8 @@ import com.android.net.module.util.bpf.Tether4Value; import com.android.net.module.util.bpf.TetherStatsKey; import com.android.net.module.util.bpf.TetherStatsValue; import com.android.networkstack.tethering.BpfCoordinator.Dependencies; -import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; +import com.android.networkstack.tethering.BpfCoordinator.Ipv6DownstreamRule; +import com.android.networkstack.tethering.BpfCoordinator.Ipv6UpstreamRule; import com.android.networkstack.tethering.BpfUtils; import com.android.networkstack.tethering.Tether6Value; import com.android.networkstack.tethering.TetherDevKey; @@ -51,9 +50,6 @@ import com.android.networkstack.tethering.TetherUpstream6Key; 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. @@ -170,7 +166,40 @@ public class BpfCoordinatorShimImpl } @Override - public boolean tetherOffloadRuleAdd(@NonNull final Ipv6ForwardingRule rule) { + public boolean addIpv6UpstreamRule(@NonNull final Ipv6UpstreamRule rule) { + if (!isInitialized()) return false; + // RFC7421_PREFIX_LENGTH = 64 which is the most commonly used IPv6 subnet prefix length. + if (rule.sourcePrefix.getPrefixLength() != RFC7421_PREFIX_LENGTH) return false; + + final TetherUpstream6Key key = rule.makeTetherUpstream6Key(); + final Tether6Value value = rule.makeTether6Value(); + + try { + mBpfUpstream6Map.insertEntry(key, value); + } catch (ErrnoException | IllegalStateException e) { + mLog.e("Could not insert upstream IPv6 entry: " + e); + return false; + } + return true; + } + + @Override + public boolean removeIpv6UpstreamRule(@NonNull final Ipv6UpstreamRule rule) { + if (!isInitialized()) return false; + // RFC7421_PREFIX_LENGTH = 64 which is the most commonly used IPv6 subnet prefix length. + if (rule.sourcePrefix.getPrefixLength() != RFC7421_PREFIX_LENGTH) return false; + + try { + mBpfUpstream6Map.deleteEntry(rule.makeTetherUpstream6Key()); + } catch (ErrnoException e) { + mLog.e("Could not delete upstream IPv6 entry: " + e); + return false; + } + return true; + } + + @Override + public boolean addIpv6DownstreamRule(@NonNull final Ipv6DownstreamRule rule) { if (!isInitialized()) return false; final TetherDownstream6Key key = rule.makeTetherDownstream6Key(); @@ -187,7 +216,7 @@ public class BpfCoordinatorShimImpl } @Override - public boolean tetherOffloadRuleRemove(@NonNull final Ipv6ForwardingRule rule) { + public boolean removeIpv6DownstreamRule(@NonNull final Ipv6DownstreamRule rule) { if (!isInitialized()) return false; try { @@ -202,51 +231,6 @@ public class BpfCoordinatorShimImpl 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 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 = makeUpstream6Key(downstreamIfindex, inDstMac, sourcePrefix); - final Tether6Value value = new Tether6Value(upstreamIfindex, outSrcMac, - outDstMac, OsConstants.ETH_P_IPV6, mtu); - try { - mBpfUpstream6Map.insertEntry(key, value); - } catch (ErrnoException | IllegalStateException e) { - mLog.e("Could not insert upstream6 entry: " + e); - return false; - } - return true; - } - - @Override - public boolean stopUpstreamIpv6Forwarding(int downstreamIfindex, int upstreamIfindex, - @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 = makeUpstream6Key(downstreamIfindex, inDstMac, sourcePrefix); - try { - mBpfUpstream6Map.deleteEntry(key); - } catch (ErrnoException e) { - mLog.e("Could not delete upstream IPv6 entry: " + e); - return false; - } - return true; - } - @Override @Nullable public SparseArray tetherOffloadGetStats() { 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 25fa8bc9d2..d28a397cb4 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,8 +16,6 @@ package com.android.networkstack.tethering.apishim.common; -import android.net.IpPrefix; -import android.net.MacAddress; import android.util.SparseArray; import androidx.annotation.NonNull; @@ -28,7 +26,8 @@ import com.android.net.module.util.bpf.Tether4Key; import com.android.net.module.util.bpf.Tether4Value; import com.android.net.module.util.bpf.TetherStatsValue; import com.android.networkstack.tethering.BpfCoordinator.Dependencies; -import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; +import com.android.networkstack.tethering.BpfCoordinator.Ipv6DownstreamRule; +import com.android.networkstack.tethering.BpfCoordinator.Ipv6UpstreamRule; /** * Bpf coordinator class for API shims. @@ -54,53 +53,51 @@ public abstract class BpfCoordinatorShim { public abstract boolean isInitialized(); /** - * Adds a tethering offload rule to BPF map, or updates it if it already exists. + * Adds a tethering offload upstream rule to BPF map, or updates it if it already exists. + * + * An existing rule will be updated if the input interface, destination MAC and source prefix + * match. Otherwise, a new rule will be created. Note that this can be only called on handler + * thread. + * + * @param rule The rule to add or update. + * @return true if operation succeeded or was a no-op, false otherwise. + */ + public abstract boolean addIpv6UpstreamRule(@NonNull Ipv6UpstreamRule rule); + + /** + * Deletes a tethering offload upstream rule from the BPF map. + * + * An existing rule will be deleted if the input interface, destination MAC and source prefix + * match. It is not an error if there is no matching rule to delete. + * + * @param rule The rule to delete. + * @return true if operation succeeded or was a no-op, false otherwise. + */ + public abstract boolean removeIpv6UpstreamRule(@NonNull Ipv6UpstreamRule rule); + + /** + * Adds a tethering offload downstream rule to BPF map, or updates it if it already exists. * * Currently, only downstream /128 IPv6 entries are supported. An existing rule will be updated * if the input interface and destination prefix match. Otherwise, a new rule will be created. * Note that this can be only called on handler thread. * * @param rule The rule to add or update. + * @return true if operation succeeded or was a no-op, false otherwise. */ - public abstract boolean tetherOffloadRuleAdd(@NonNull Ipv6ForwardingRule rule); + public abstract boolean addIpv6DownstreamRule(@NonNull Ipv6DownstreamRule rule); /** - * Deletes a tethering offload rule from the BPF map. + * Deletes a tethering offload downstream rule from the BPF map. * * Currently, only downstream /128 IPv6 entries are supported. An existing rule will be deleted * if the destination IP address and the source interface match. It is not an error if there is * no matching rule to delete. * * @param rule The rule to delete. + * @return true if operation succeeded or was a no-op, false otherwise. */ - public abstract boolean tetherOffloadRuleRemove(@NonNull Ipv6ForwardingRule rule); - - /** - * Starts IPv6 forwarding between the specified interfaces. - - * @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 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 IpPrefix sourcePrefix, @NonNull MacAddress inDstMac); + public abstract boolean removeIpv6DownstreamRule(@NonNull Ipv6DownstreamRule rule); /** * Return BPF tethering offload statistics. diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 56b5c2ee21..bb09d0de36 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -77,7 +77,7 @@ import com.android.net.module.util.ip.IpNeighborMonitor; import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEvent; import com.android.networkstack.tethering.BpfCoordinator; import com.android.networkstack.tethering.BpfCoordinator.ClientInfo; -import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; +import com.android.networkstack.tethering.BpfCoordinator.Ipv6DownstreamRule; import com.android.networkstack.tethering.PrivateAddressCoordinator; import com.android.networkstack.tethering.TetheringConfiguration; import com.android.networkstack.tethering.metrics.TetheringMetrics; @@ -327,8 +327,8 @@ public class IpServer extends StateMachine { // IP neighbor monitor monitors the neighbor events for adding/removing offload // forwarding rules per client. If BPF offload is not supported, don't start listening - // for neighbor events. See updateIpv6ForwardingRules, addIpv6ForwardingRule, - // removeIpv6ForwardingRule. + // for neighbor events. See updateIpv6ForwardingRules, addIpv6DownstreamRule, + // removeIpv6DownstreamRule. if (mUsingBpfOffload && !mIpNeighborMonitor.start()) { mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName); } @@ -890,21 +890,21 @@ public class IpServer extends StateMachine { } } - private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) { + private void addIpv6DownstreamRule(Ipv6DownstreamRule rule) { // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF // offload is disabled. Add this check just in case. // TODO: Perhaps remove this protection check. if (!mUsingBpfOffload) return; - mBpfCoordinator.tetherOffloadRuleAdd(this, rule); + mBpfCoordinator.addIpv6DownstreamRule(this, rule); } - private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule) { + private void removeIpv6DownstreamRule(Ipv6DownstreamRule rule) { // TODO: Perhaps remove this protection check. - // See the related comment in #addIpv6ForwardingRule. + // See the related comment in #addIpv6DownstreamRule. if (!mUsingBpfOffload) return; - mBpfCoordinator.tetherOffloadRuleRemove(this, rule); + mBpfCoordinator.removeIpv6DownstreamRule(this, rule); } private void clearIpv6ForwardingRules() { @@ -915,7 +915,7 @@ public class IpServer extends StateMachine { private void updateIpv6ForwardingRule(int newIfindex) { // TODO: Perhaps remove this protection check. - // See the related comment in #addIpv6ForwardingRule. + // See the related comment in #addIpv6DownstreamRule. if (!mUsingBpfOffload) return; mBpfCoordinator.tetherOffloadRuleUpdate(this, newIfindex); @@ -954,22 +954,22 @@ public class IpServer extends StateMachine { } // When deleting rules, we still need to pass a non-null MAC, even though it's ignored. - // Do this here instead of in the Ipv6ForwardingRule constructor to ensure that we never - // add rules with a null MAC, only delete them. + // Do this here instead of in the Ipv6DownstreamRule constructor to ensure that we + // never add rules with a null MAC, only delete them. MacAddress dstMac = e.isValid() ? e.macAddr : NULL_MAC_ADDRESS; - Ipv6ForwardingRule rule = new Ipv6ForwardingRule(upstreamIfindex, - mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr, dstMac); + Ipv6DownstreamRule rule = new Ipv6DownstreamRule(upstreamIfindex, mInterfaceParams.index, + (Inet6Address) e.ip, mInterfaceParams.macAddr, dstMac); if (e.isValid()) { - addIpv6ForwardingRule(rule); + addIpv6DownstreamRule(rule); } else { - removeIpv6ForwardingRule(rule); + removeIpv6DownstreamRule(rule); } } // TODO: consider moving into BpfCoordinator. private void updateClientInfoIpv4(NeighborEvent e) { // TODO: Perhaps remove this protection check. - // See the related comment in #addIpv6ForwardingRule. + // See the related comment in #addIpv6DownstreamRule. if (!mUsingBpfOffload) return; if (e == null) return; diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java index f22ccbd132..7311125dcb 100644 --- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java +++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java @@ -88,6 +88,8 @@ import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -235,8 +237,8 @@ public class BpfCoordinator { // rules function without a valid IPv6 downstream interface index even if it may have one // before. IpServer would need to call getInterfaceParams() in the constructor instead of when // startIpv6() is called, and make mInterfaceParams final. - private final HashMap> - mIpv6ForwardingRules = new LinkedHashMap<>(); + private final HashMap> + mIpv6DownstreamRules = new LinkedHashMap<>(); // Map of downstream client maps. Each of these maps represents the IPv4 clients for a given // downstream. Needed to build IPv4 forwarding rules when conntrack events are received. @@ -499,8 +501,8 @@ public class BpfCoordinator { /** * Stop BPF tethering offload stats polling. * The data limit cleanup and the tether stats maps cleanup are not implemented here. - * These cleanups rely on all IpServers calling #tetherOffloadRuleRemove. After the - * last rule is removed from the upstream, #tetherOffloadRuleRemove does the cleanup + * These cleanups rely on all IpServers calling #removeIpv6DownstreamRule. After the + * last rule is removed from the upstream, #removeIpv6DownstreamRule does the cleanup * functionality. * Note that this can be only called on handler thread. */ @@ -589,22 +591,22 @@ public class BpfCoordinator { } /** - * Add forwarding rule. After adding the first rule on a given upstream, must add the data + * Add IPv6 downstream rule. After adding the first rule on a given upstream, must add the data * limit on the given upstream. * Note that this can be only called on handler thread. */ - public void tetherOffloadRuleAdd( - @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) { + public void addIpv6DownstreamRule( + @NonNull final IpServer ipServer, @NonNull final Ipv6DownstreamRule rule) { if (!isUsingBpf()) return; // TODO: Perhaps avoid to add a duplicate rule. - if (!mBpfCoordinatorShim.tetherOffloadRuleAdd(rule)) return; + if (!mBpfCoordinatorShim.addIpv6DownstreamRule(rule)) return; - if (!mIpv6ForwardingRules.containsKey(ipServer)) { - mIpv6ForwardingRules.put(ipServer, new LinkedHashMap()); + if (!mIpv6DownstreamRules.containsKey(ipServer)) { + mIpv6DownstreamRules.put(ipServer, new LinkedHashMap()); } - LinkedHashMap rules = mIpv6ForwardingRules.get(ipServer); + LinkedHashMap rules = mIpv6DownstreamRules.get(ipServer); // Add upstream and downstream interface index to dev map. maybeAddDevMap(rule.upstreamIfindex, rule.downstreamIfindex); @@ -613,15 +615,13 @@ public class BpfCoordinator { maybeSetLimit(rule.upstreamIfindex); if (!isAnyRuleFromDownstreamToUpstream(rule.downstreamIfindex, rule.upstreamIfindex)) { - final int downstream = rule.downstreamIfindex; - 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, - 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)); + Ipv6UpstreamRule upstreamRule = new Ipv6UpstreamRule(rule.upstreamIfindex, + rule.downstreamIfindex, IPV6_ZERO_PREFIX64, rule.srcMac, NULL_MAC_ADDRESS, + NULL_MAC_ADDRESS); + if (!mBpfCoordinatorShim.addIpv6UpstreamRule(upstreamRule)) { + mLog.e("Failed to add upstream IPv6 forwarding rule: " + upstreamRule); } } @@ -631,17 +631,17 @@ public class BpfCoordinator { } /** - * Remove forwarding rule. After removing the last rule on a given upstream, must clear + * Remove IPv6 downstream rule. After removing the last rule on a given upstream, must clear * data limit, update the last tether stats and remove the tether stats in the BPF maps. * Note that this can be only called on handler thread. */ - public void tetherOffloadRuleRemove( - @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) { + public void removeIpv6DownstreamRule( + @NonNull final IpServer ipServer, @NonNull final Ipv6DownstreamRule rule) { if (!isUsingBpf()) return; - if (!mBpfCoordinatorShim.tetherOffloadRuleRemove(rule)) return; + if (!mBpfCoordinatorShim.removeIpv6DownstreamRule(rule)) return; - LinkedHashMap rules = mIpv6ForwardingRules.get(ipServer); + LinkedHashMap rules = mIpv6DownstreamRules.get(ipServer); if (rules == null) return; // Must remove rules before calling #isAnyRuleOnUpstream because it needs to check if @@ -652,17 +652,16 @@ public class BpfCoordinator { // Remove the downstream entry if it has no more rule. if (rules.isEmpty()) { - mIpv6ForwardingRules.remove(ipServer); + mIpv6DownstreamRules.remove(ipServer); } // If no more rules between this upstream and downstream, stop upstream forwarding. if (!isAnyRuleFromDownstreamToUpstream(rule.downstreamIfindex, rule.upstreamIfindex)) { - final int downstream = rule.downstreamIfindex; - final int upstream = rule.upstreamIfindex; - if (!mBpfCoordinatorShim.stopUpstreamIpv6Forwarding(downstream, upstream, - IPV6_ZERO_PREFIX64, rule.srcMac)) { - mLog.e("Failed to disable upstream IPv6 forwarding from " - + getIfName(downstream) + " to " + getIfName(upstream)); + Ipv6UpstreamRule upstreamRule = new Ipv6UpstreamRule(rule.upstreamIfindex, + rule.downstreamIfindex, IPV6_ZERO_PREFIX64, rule.srcMac, NULL_MAC_ADDRESS, + NULL_MAC_ADDRESS); + if (!mBpfCoordinatorShim.removeIpv6UpstreamRule(upstreamRule)) { + mLog.e("Failed to remove upstream IPv6 forwarding rule: " + upstreamRule); } } @@ -678,13 +677,13 @@ public class BpfCoordinator { public void tetherOffloadRuleClear(@NonNull final IpServer ipServer) { if (!isUsingBpf()) return; - final LinkedHashMap rules = mIpv6ForwardingRules.get( - ipServer); + final LinkedHashMap rules = + mIpv6DownstreamRules.get(ipServer); if (rules == null) return; // Need to build a rule list because the rule map may be changed in the iteration. - for (final Ipv6ForwardingRule rule : new ArrayList(rules.values())) { - tetherOffloadRuleRemove(ipServer, rule); + for (final Ipv6DownstreamRule rule : new ArrayList(rules.values())) { + removeIpv6DownstreamRule(ipServer, rule); } } @@ -695,28 +694,28 @@ public class BpfCoordinator { public void tetherOffloadRuleUpdate(@NonNull final IpServer ipServer, int newUpstreamIfindex) { if (!isUsingBpf()) return; - final LinkedHashMap rules = mIpv6ForwardingRules.get( - ipServer); + final LinkedHashMap rules = + mIpv6DownstreamRules.get(ipServer); if (rules == null) return; // Need to build a rule list because the rule map may be changed in the iteration. // First remove all the old rules, then add all the new rules. This is because the upstream - // forwarding code in tetherOffloadRuleAdd cannot support rules on two upstreams at the + // forwarding code in addIpv6DownstreamRule cannot support rules on two upstreams at the // same time. Deleting the rules first ensures that upstream forwarding is disabled on the // old upstream when the last rule is removed from it, and re-enabled on the new upstream // when the first rule is added to it. // TODO: Once the IPv6 client processing code has moved from IpServer to BpfCoordinator, do // something smarter. - final ArrayList rulesCopy = new ArrayList<>(rules.values()); - for (final Ipv6ForwardingRule rule : rulesCopy) { + final ArrayList rulesCopy = new ArrayList<>(rules.values()); + for (final Ipv6DownstreamRule rule : rulesCopy) { // Remove the old rule before adding the new one because the map uses the same key for // both rules. Reversing the processing order causes that the new rule is removed as // unexpected. // TODO: Add new rule first to reduce the latency which has no rule. - tetherOffloadRuleRemove(ipServer, rule); + removeIpv6DownstreamRule(ipServer, rule); } - for (final Ipv6ForwardingRule rule : rulesCopy) { - tetherOffloadRuleAdd(ipServer, rule.onNewUpstream(newUpstreamIfindex)); + for (final Ipv6DownstreamRule rule : rulesCopy) { + addIpv6DownstreamRule(ipServer, rule.onNewUpstream(newUpstreamIfindex)); } } @@ -1142,14 +1141,14 @@ public class BpfCoordinator { private void dumpIpv6ForwardingRulesByDownstream(@NonNull IndentingPrintWriter pw) { pw.println("IPv6 Forwarding rules by downstream interface:"); pw.increaseIndent(); - if (mIpv6ForwardingRules.size() == 0) { - pw.println("No IPv6 rules"); + if (mIpv6DownstreamRules.size() == 0) { + pw.println("No downstream IPv6 rules"); pw.decreaseIndent(); return; } - for (Map.Entry> entry : - mIpv6ForwardingRules.entrySet()) { + for (Map.Entry> entry : + mIpv6DownstreamRules.entrySet()) { IpServer ipServer = entry.getKey(); // The rule downstream interface index is paired with the interface name from // IpServer#interfaceName. See #startIPv6, #updateIpv6ForwardingRules in IpServer. @@ -1158,8 +1157,8 @@ public class BpfCoordinator { + "[srcmac] [dstmac]"); pw.increaseIndent(); - LinkedHashMap rules = entry.getValue(); - for (Ipv6ForwardingRule rule : rules.values()) { + LinkedHashMap rules = entry.getValue(); + for (Ipv6DownstreamRule rule : rules.values()) { final int upstreamIfindex = rule.upstreamIfindex; pw.println(String.format("%d(%s) %d(%s) %s [%s] [%s]", upstreamIfindex, getIfName(upstreamIfindex), rule.downstreamIfindex, @@ -1406,13 +1405,13 @@ public class BpfCoordinator { pw.decreaseIndent(); } - /** IPv6 forwarding rule class. */ - public static class Ipv6ForwardingRule { - // The upstream6 and downstream6 rules are built as the following tables. Only raw ip - // upstream interface is supported. + /** IPv6 upstream forwarding rule class. */ + public static class Ipv6UpstreamRule { + // The upstream6 rules are built as the following tables. Only raw ip upstream interface is + // supported. // TODO: support ether ip upstream interface. // - // NAT network topology: + // Tethering network topology: // // public network (rawip) private network // | UE | @@ -1422,15 +1421,15 @@ public class BpfCoordinator { // // upstream6 key and value: // - // +------+-------------+ - // | TetherUpstream6Key | - // +------+------+------+ - // |field |iif |dstMac| - // | | | | - // +------+------+------+ - // |value |downst|downst| - // | |ream |ream | - // +------+------+------+ + // +------+-------------------+ + // | TetherUpstream6Key | + // +------+------+------+-----+ + // |field |iif |dstMac|src64| + // | | | | | + // +------+------+------+-----+ + // |value |downst|downst|upstr| + // | |ream |ream |eam | + // +------+------+------+-----+ // // +------+----------------------------------+ // | |Tether6Value | @@ -1442,6 +1441,92 @@ public class BpfCoordinator { // | |am | | |IP | | // +------+------+------+------+------+------+ // + public final int upstreamIfindex; + public final int downstreamIfindex; + @NonNull + public final IpPrefix sourcePrefix; + @NonNull + public final MacAddress inDstMac; + @NonNull + public final MacAddress outSrcMac; + @NonNull + public final MacAddress outDstMac; + + public Ipv6UpstreamRule(int upstreamIfindex, int downstreamIfindex, + @NonNull IpPrefix sourcePrefix, @NonNull MacAddress inDstMac, + @NonNull MacAddress outSrcMac, @NonNull MacAddress outDstMac) { + this.upstreamIfindex = upstreamIfindex; + this.downstreamIfindex = downstreamIfindex; + this.sourcePrefix = sourcePrefix; + this.inDstMac = inDstMac; + this.outSrcMac = outSrcMac; + this.outDstMac = outDstMac; + } + + /** + * Return a TetherUpstream6Key object built from the rule. + */ + @NonNull + public TetherUpstream6Key makeTetherUpstream6Key() { + byte[] prefixBytes = Arrays.copyOf(sourcePrefix.getRawAddress(), 8); + long prefix64 = ByteBuffer.wrap(prefixBytes).order(ByteOrder.BIG_ENDIAN).getLong(); + return new TetherUpstream6Key(downstreamIfindex, inDstMac, prefix64); + } + + /** + * Return a Tether6Value object built from the rule. + */ + @NonNull + public Tether6Value makeTether6Value() { + return new Tether6Value(upstreamIfindex, outDstMac, outSrcMac, ETH_P_IPV6, + NetworkStackConstants.ETHER_MTU); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Ipv6UpstreamRule)) return false; + Ipv6UpstreamRule that = (Ipv6UpstreamRule) o; + return this.upstreamIfindex == that.upstreamIfindex + && this.downstreamIfindex == that.downstreamIfindex + && Objects.equals(this.sourcePrefix, that.sourcePrefix) + && Objects.equals(this.inDstMac, that.inDstMac) + && Objects.equals(this.outSrcMac, that.outSrcMac) + && Objects.equals(this.outDstMac, that.outDstMac); + } + + @Override + public int hashCode() { + // TODO: if this is ever used in production code, don't pass ifindices + // to Objects.hash() to avoid autoboxing overhead. + return Objects.hash(upstreamIfindex, downstreamIfindex, sourcePrefix, inDstMac, + outSrcMac, outDstMac); + } + + @Override + public String toString() { + return "upstreamIfindex: " + upstreamIfindex + + ", downstreamIfindex: " + downstreamIfindex + + ", sourcePrefix: " + sourcePrefix + + ", inDstMac: " + inDstMac + + ", outSrcMac: " + outSrcMac + + ", outDstMac: " + outDstMac; + } + } + + /** IPv6 downstream forwarding rule class. */ + public static class Ipv6DownstreamRule { + // The downstream6 rules are built as the following tables. Only raw ip upstream interface + // is supported. + // TODO: support ether ip upstream interface. + // + // Tethering network topology: + // + // public network (rawip) private network + // | UE | + // +------------+ V +------------+------------+ V +------------+ + // | Sever +---------+ Upstream | Downstream +---------+ Client | + // +------------+ +------------+------------+ +------------+ + // // downstream6 key and value: // // +------+--------------------+ @@ -1475,11 +1560,11 @@ public class BpfCoordinator { @NonNull public final MacAddress dstMac; - public Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, + public Ipv6DownstreamRule(int upstreamIfindex, int downstreamIfindex, @NonNull Inet6Address address, @NonNull MacAddress srcMac, @NonNull MacAddress dstMac) { this.upstreamIfindex = upstreamIfindex; - this.downstreamIfindex = downstreamIfIndex; + this.downstreamIfindex = downstreamIfindex; this.address = address; this.srcMac = srcMac; this.dstMac = dstMac; @@ -1487,8 +1572,8 @@ public class BpfCoordinator { /** Return a new rule object which updates with new upstream index. */ @NonNull - public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) { - return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac, + public Ipv6DownstreamRule onNewUpstream(int newUpstreamIfindex) { + return new Ipv6DownstreamRule(newUpstreamIfindex, downstreamIfindex, address, srcMac, dstMac); } @@ -1528,8 +1613,8 @@ public class BpfCoordinator { @Override public boolean equals(Object o) { - if (!(o instanceof Ipv6ForwardingRule)) return false; - Ipv6ForwardingRule that = (Ipv6ForwardingRule) o; + if (!(o instanceof Ipv6DownstreamRule)) return false; + Ipv6DownstreamRule that = (Ipv6DownstreamRule) o; return this.upstreamIfindex == that.upstreamIfindex && this.downstreamIfindex == that.downstreamIfindex && Objects.equals(this.address, that.address) @@ -1870,9 +1955,9 @@ public class BpfCoordinator { } private int getInterfaceIndexFromRules(@NonNull String ifName) { - for (LinkedHashMap rules : mIpv6ForwardingRules - .values()) { - for (Ipv6ForwardingRule rule : rules.values()) { + for (LinkedHashMap rules : + mIpv6DownstreamRules.values()) { + for (Ipv6DownstreamRule rule : rules.values()) { final int upstreamIfindex = rule.upstreamIfindex; if (TextUtils.equals(ifName, mInterfaceNames.get(upstreamIfindex))) { return upstreamIfindex; @@ -1963,9 +2048,9 @@ public class BpfCoordinator { // TODO: Rename to isAnyIpv6RuleOnUpstream and define an isAnyRuleOnUpstream method that called // both isAnyIpv6RuleOnUpstream and mBpfCoordinatorShim.isAnyIpv4RuleOnUpstream. private boolean isAnyRuleOnUpstream(int upstreamIfindex) { - for (LinkedHashMap rules : mIpv6ForwardingRules - .values()) { - for (Ipv6ForwardingRule rule : rules.values()) { + for (LinkedHashMap rules : + mIpv6DownstreamRules.values()) { + for (Ipv6DownstreamRule rule : rules.values()) { if (upstreamIfindex == rule.upstreamIfindex) return true; } } @@ -1973,9 +2058,9 @@ public class BpfCoordinator { } private boolean isAnyRuleFromDownstreamToUpstream(int downstreamIfindex, int upstreamIfindex) { - for (LinkedHashMap rules : mIpv6ForwardingRules - .values()) { - for (Ipv6ForwardingRule rule : rules.values()) { + for (LinkedHashMap rules : + mIpv6DownstreamRules.values()) { + for (Ipv6DownstreamRule rule : rules.values()) { if (downstreamIfindex == rule.downstreamIfindex && upstreamIfindex == rule.upstreamIfindex) { return true; @@ -2226,13 +2311,13 @@ public class BpfCoordinator { CONNTRACK_TIMEOUT_UPDATE_INTERVAL_MS); } - // Return forwarding rule map. This is used for testing only. + // Return IPv6 downstream forwarding rule map. This is used for testing only. // Note that this can be only called on handler thread. @NonNull @VisibleForTesting - final HashMap> - getForwardingRulesForTesting() { - return mIpv6ForwardingRules; + final HashMap> + getIpv6DownstreamRulesForTesting() { + return mIpv6DownstreamRules; } // Return upstream interface name map. This is used for testing only. diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 464778f93e..19d70c614a 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -114,7 +114,7 @@ import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEvent; import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEventConsumer; import com.android.networkstack.tethering.BpfCoordinator; import com.android.networkstack.tethering.BpfCoordinator.ClientInfo; -import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; +import com.android.networkstack.tethering.BpfCoordinator.Ipv6DownstreamRule; import com.android.networkstack.tethering.PrivateAddressCoordinator; import com.android.networkstack.tethering.Tether6Value; import com.android.networkstack.tethering.TetherDevKey; @@ -899,9 +899,9 @@ public class IpServerTest { } @NonNull - private static Ipv6ForwardingRule makeForwardingRule( - int upstreamIfindex, @NonNull InetAddress dst, @NonNull MacAddress dstMac) { - return new Ipv6ForwardingRule(upstreamIfindex, TEST_IFACE_PARAMS.index, + private static Ipv6DownstreamRule makeDownstreamRule(int upstreamIfindex, + @NonNull InetAddress dst, @NonNull MacAddress dstMac) { + return new Ipv6DownstreamRule(upstreamIfindex, TEST_IFACE_PARAMS.index, (Inet6Address) dst, TEST_IFACE_PARAMS.macAddr, dstMac); } @@ -1064,16 +1064,16 @@ public class IpServerTest { // Events on this interface are received and sent to netd. recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA)); + verify(mBpfCoordinator).addIpv6DownstreamRule( + mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighA, macA)); verifyTetherOffloadRuleAdd(null, UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighA, macA); verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX); resetNetdBpfMapAndCoordinator(); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB)); + verify(mBpfCoordinator).addIpv6DownstreamRule( + mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighB, macB)); verifyTetherOffloadRuleAdd(null, UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighB, macB); verifyNoUpstreamIpv6ForwardingChange(null); @@ -1088,8 +1088,8 @@ public class IpServerTest { // A neighbor that is no longer valid causes the rule to be removed. // NUD_FAILED events do not have a MAC address. recvNewNeigh(myIfindex, neighA, NUD_FAILED, null); - verify(mBpfCoordinator).tetherOffloadRuleRemove( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macNull)); + verify(mBpfCoordinator).removeIpv6DownstreamRule( + mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighA, macNull)); verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighA, macNull); verifyNoUpstreamIpv6ForwardingChange(null); @@ -1097,8 +1097,8 @@ public class IpServerTest { // A neighbor that is deleted causes the rule to be removed. recvDelNeigh(myIfindex, neighB, NUD_STALE, macB); - verify(mBpfCoordinator).tetherOffloadRuleRemove( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macNull)); + verify(mBpfCoordinator).removeIpv6DownstreamRule( + mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighB, macNull)); verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighB, macNull); verifyStopUpstreamIpv6Forwarding(null); @@ -1155,13 +1155,13 @@ public class IpServerTest { lp.setInterfaceName(UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB)); + verify(mBpfCoordinator).addIpv6DownstreamRule( + mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighB, macB)); verifyTetherOffloadRuleAdd(null, UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighB, macB); verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX); - verify(mBpfCoordinator, never()).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA)); + verify(mBpfCoordinator, never()).addIpv6DownstreamRule( + mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighA, macA)); verifyNeverTetherOffloadRuleAdd( UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighA, macA); @@ -1178,13 +1178,13 @@ public class IpServerTest { dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA)); + verify(mBpfCoordinator).addIpv6DownstreamRule( + mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighA, macA)); verifyTetherOffloadRuleAdd(null, UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighA, macA); verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB)); + verify(mBpfCoordinator).addIpv6DownstreamRule( + mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neighB, macB)); verifyTetherOffloadRuleAdd(null, UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neighB, macB); resetNetdBpfMapAndCoordinator(); @@ -1222,16 +1222,16 @@ public class IpServerTest { resetNetdBpfMapAndCoordinator(); recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA); - verify(mBpfCoordinator).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macA)); + verify(mBpfCoordinator).addIpv6DownstreamRule( + mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neigh, macA)); verifyTetherOffloadRuleAdd(null, UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neigh, macA); verifyStartUpstreamIpv6Forwarding(null, UPSTREAM_IFINDEX); resetNetdBpfMapAndCoordinator(); recvDelNeigh(myIfindex, neigh, NUD_STALE, macA); - verify(mBpfCoordinator).tetherOffloadRuleRemove( - mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macNull)); + verify(mBpfCoordinator).removeIpv6DownstreamRule( + mIpServer, makeDownstreamRule(UPSTREAM_IFINDEX, neigh, macNull)); verifyTetherOffloadRuleRemove(null, UPSTREAM_IFINDEX, UPSTREAM_IFACE_PARAMS.macAddr, neigh, macNull); verifyStopUpstreamIpv6Forwarding(null); @@ -1244,13 +1244,13 @@ public class IpServerTest { resetNetdBpfMapAndCoordinator(); recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA); - verify(mBpfCoordinator, never()).tetherOffloadRuleAdd(any(), any()); + verify(mBpfCoordinator, never()).addIpv6DownstreamRule(any(), any()); verifyNeverTetherOffloadRuleAdd(); verifyNoUpstreamIpv6ForwardingChange(null); resetNetdBpfMapAndCoordinator(); recvDelNeigh(myIfindex, neigh, NUD_STALE, macA); - verify(mBpfCoordinator, never()).tetherOffloadRuleRemove(any(), any()); + verify(mBpfCoordinator, never()).removeIpv6DownstreamRule(any(), any()); verifyNeverTetherOffloadRuleRemove(); verifyNoUpstreamIpv6ForwardingChange(null); resetNetdBpfMapAndCoordinator(); @@ -1534,8 +1534,8 @@ public class IpServerTest { final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1"); final MacAddress mac = MacAddress.fromString("00:00:00:00:00:0a"); recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, mac); - verify(mBpfCoordinator, never()).tetherOffloadRuleAdd( - mIpServer, makeForwardingRule(IPSEC_IFINDEX, neigh, mac)); + verify(mBpfCoordinator, never()).addIpv6DownstreamRule( + mIpServer, makeDownstreamRule(IPSEC_IFINDEX, neigh, mac)); } // TODO: move to BpfCoordinatorTest once IpNeighborMonitor is migrated to BpfCoordinator. 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 8bc4c182a4..04eb4304cb 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java @@ -118,7 +118,8 @@ import com.android.net.module.util.netlink.NetlinkConstants; import com.android.net.module.util.netlink.NetlinkUtils; import com.android.networkstack.tethering.BpfCoordinator.BpfConntrackEventConsumer; import com.android.networkstack.tethering.BpfCoordinator.ClientInfo; -import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; +import com.android.networkstack.tethering.BpfCoordinator.Ipv6DownstreamRule; +import com.android.networkstack.tethering.BpfCoordinator.Ipv6UpstreamRule; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; @@ -192,6 +193,7 @@ public class BpfCoordinatorTest { private static final Inet4Address XLAT_LOCAL_IPV4ADDR = (Inet4Address) InetAddresses.parseNumericAddress("192.0.0.46"); private static final IpPrefix NAT64_IP_PREFIX = new IpPrefix("64:ff9b::/96"); + private static final IpPrefix IPV6_ZERO_PREFIX = new IpPrefix("::/64"); // Generally, public port and private port are the same in the NAT conntrack message. // TODO: consider using different private port and public port for testing. @@ -669,8 +671,8 @@ public class BpfCoordinatorTest { } } - private void verifyTetherOffloadRuleAdd(@Nullable InOrder inOrder, - @NonNull Ipv6ForwardingRule rule) throws Exception { + private void verifyAddDownstreamRule(@Nullable InOrder inOrder, + @NonNull Ipv6DownstreamRule rule) throws Exception { if (mDeps.isAtLeastS()) { verifyWithOrder(inOrder, mBpfDownstream6Map).updateEntry( rule.makeTetherDownstream6Key(), rule.makeTether6Value()); @@ -679,7 +681,7 @@ public class BpfCoordinatorTest { } } - private void verifyNeverTetherOffloadRuleAdd() throws Exception { + private void verifyNeverAddDownstreamRule() throws Exception { if (mDeps.isAtLeastS()) { verify(mBpfDownstream6Map, never()).updateEntry(any(), any()); } else { @@ -687,8 +689,8 @@ public class BpfCoordinatorTest { } } - private void verifyTetherOffloadRuleRemove(@Nullable InOrder inOrder, - @NonNull final Ipv6ForwardingRule rule) throws Exception { + private void verifyRemoveDownstreamRule(@Nullable InOrder inOrder, + @NonNull final Ipv6DownstreamRule rule) throws Exception { if (mDeps.isAtLeastS()) { verifyWithOrder(inOrder, mBpfDownstream6Map).deleteEntry( rule.makeTetherDownstream6Key()); @@ -697,7 +699,7 @@ public class BpfCoordinatorTest { } } - private void verifyNeverTetherOffloadRuleRemove() throws Exception { + private void verifyNeverRemoveDownstreamRule() throws Exception { if (mDeps.isAtLeastS()) { verify(mBpfDownstream6Map, never()).deleteEntry(any()); } else { @@ -768,17 +770,17 @@ public class BpfCoordinatorTest { // The #verifyTetherOffloadGetAndClearStats can't distinguish who has ever called // mBpfStatsMap#getValue and get a wrong calling count which counts all. final InOrder inOrder = inOrder(mNetd, mBpfDownstream6Map, mBpfLimitMap, mBpfStatsMap); - final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); - coordinator.tetherOffloadRuleAdd(mIpServer, rule); - verifyTetherOffloadRuleAdd(inOrder, rule); + final Ipv6DownstreamRule rule = buildTestDownstreamRule(mobileIfIndex, NEIGH_A, MAC_A); + coordinator.addIpv6DownstreamRule(mIpServer, rule); + verifyAddDownstreamRule(inOrder, rule); verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, QUOTA_UNLIMITED, true /* isInit */); // Removing the last rule on current upstream immediately sends the cleanup stuff to netd. updateStatsEntryForTetherOffloadGetAndClearStats( buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)); - coordinator.tetherOffloadRuleRemove(mIpServer, rule); - verifyTetherOffloadRuleRemove(inOrder, rule); + coordinator.removeIpv6DownstreamRule(mIpServer, rule); + verifyRemoveDownstreamRule(inOrder, rule); verifyTetherOffloadGetAndClearStats(inOrder, mobileIfIndex); } @@ -947,7 +949,7 @@ public class BpfCoordinatorTest { public final MacAddress srcMac; public final MacAddress dstMac; - TetherOffloadRuleParcelMatcher(@NonNull Ipv6ForwardingRule rule) { + TetherOffloadRuleParcelMatcher(@NonNull Ipv6DownstreamRule rule) { upstreamIfindex = rule.upstreamIfindex; downstreamIfindex = rule.downstreamIfindex; address = rule.address; @@ -971,21 +973,28 @@ public class BpfCoordinatorTest { } @NonNull - private TetherOffloadRuleParcel matches(@NonNull Ipv6ForwardingRule rule) { + private TetherOffloadRuleParcel matches(@NonNull Ipv6DownstreamRule rule) { return argThat(new TetherOffloadRuleParcelMatcher(rule)); } @NonNull - private static Ipv6ForwardingRule buildTestForwardingRule( + private static Ipv6UpstreamRule buildTestUpstreamRule(int upstreamIfindex) { + return new Ipv6UpstreamRule(upstreamIfindex, DOWNSTREAM_IFINDEX, + IPV6_ZERO_PREFIX, DOWNSTREAM_MAC, MacAddress.ALL_ZEROS_ADDRESS, + MacAddress.ALL_ZEROS_ADDRESS); + } + + @NonNull + private static Ipv6DownstreamRule buildTestDownstreamRule( int upstreamIfindex, @NonNull InetAddress address, @NonNull MacAddress dstMac) { - return new Ipv6ForwardingRule(upstreamIfindex, DOWNSTREAM_IFINDEX, (Inet6Address) address, - DOWNSTREAM_MAC, dstMac); + return new Ipv6DownstreamRule(upstreamIfindex, DOWNSTREAM_IFINDEX, + (Inet6Address) address, DOWNSTREAM_MAC, dstMac); } @Test public void testRuleMakeTetherDownstream6Key() throws Exception { final int mobileIfIndex = 100; - final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); + final Ipv6DownstreamRule rule = buildTestDownstreamRule(mobileIfIndex, NEIGH_A, MAC_A); final TetherDownstream6Key key = rule.makeTetherDownstream6Key(); assertEquals(key.iif, mobileIfIndex); @@ -998,7 +1007,7 @@ public class BpfCoordinatorTest { @Test public void testRuleMakeTether6Value() throws Exception { final int mobileIfIndex = 100; - final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); + final Ipv6DownstreamRule rule = buildTestDownstreamRule(mobileIfIndex, NEIGH_A, MAC_A); final Tether6Value value = rule.makeTether6Value(); assertEquals(value.oif, DOWNSTREAM_IFINDEX); @@ -1023,10 +1032,10 @@ public class BpfCoordinatorTest { // [1] Default limit. // Set the unlimited quota as default if the service has never applied a data limit for a // given upstream. Note that the data limit only be applied on an upstream which has rules. - final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); + final Ipv6DownstreamRule rule = buildTestDownstreamRule(mobileIfIndex, NEIGH_A, MAC_A); final InOrder inOrder = inOrder(mNetd, mBpfDownstream6Map, mBpfLimitMap, mBpfStatsMap); - coordinator.tetherOffloadRuleAdd(mIpServer, rule); - verifyTetherOffloadRuleAdd(inOrder, rule); + coordinator.addIpv6DownstreamRule(mIpServer, rule); + verifyAddDownstreamRule(inOrder, rule); verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, QUOTA_UNLIMITED, true /* isInit */); inOrder.verifyNoMoreInteractions(); @@ -1073,28 +1082,28 @@ public class BpfCoordinatorTest { verifyNeverTetherOffloadSetInterfaceQuota(inOrder); // Adding the first rule on current upstream immediately sends the quota to netd. - final Ipv6ForwardingRule ruleA = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); - coordinator.tetherOffloadRuleAdd(mIpServer, ruleA); - verifyTetherOffloadRuleAdd(inOrder, ruleA); + final Ipv6DownstreamRule ruleA = buildTestDownstreamRule(mobileIfIndex, NEIGH_A, MAC_A); + coordinator.addIpv6DownstreamRule(mIpServer, ruleA); + verifyAddDownstreamRule(inOrder, ruleA); verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, limit, true /* isInit */); inOrder.verifyNoMoreInteractions(); // Adding the second rule on current upstream does not send the quota to netd. - final Ipv6ForwardingRule ruleB = buildTestForwardingRule(mobileIfIndex, NEIGH_B, MAC_B); - coordinator.tetherOffloadRuleAdd(mIpServer, ruleB); - verifyTetherOffloadRuleAdd(inOrder, ruleB); + final Ipv6DownstreamRule ruleB = buildTestDownstreamRule(mobileIfIndex, NEIGH_B, MAC_B); + coordinator.addIpv6DownstreamRule(mIpServer, ruleB); + verifyAddDownstreamRule(inOrder, ruleB); verifyNeverTetherOffloadSetInterfaceQuota(inOrder); // Removing the second rule on current upstream does not send the quota to netd. - coordinator.tetherOffloadRuleRemove(mIpServer, ruleB); - verifyTetherOffloadRuleRemove(inOrder, ruleB); + coordinator.removeIpv6DownstreamRule(mIpServer, ruleB); + verifyRemoveDownstreamRule(inOrder, ruleB); verifyNeverTetherOffloadSetInterfaceQuota(inOrder); // Removing the last rule on current upstream immediately sends the cleanup stuff to netd. updateStatsEntryForTetherOffloadGetAndClearStats( buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)); - coordinator.tetherOffloadRuleRemove(mIpServer, ruleA); - verifyTetherOffloadRuleRemove(inOrder, ruleA); + coordinator.removeIpv6DownstreamRule(mIpServer, ruleA); + verifyRemoveDownstreamRule(inOrder, ruleA); verifyTetherOffloadGetAndClearStats(inOrder, mobileIfIndex); inOrder.verifyNoMoreInteractions(); } @@ -1124,23 +1133,23 @@ public class BpfCoordinatorTest { // [1] Adding rules on the upstream Ethernet. // Note that the default data limit is applied after the first rule is added. - final Ipv6ForwardingRule ethernetRuleA = buildTestForwardingRule( + final Ipv6DownstreamRule ethernetRuleA = buildTestDownstreamRule( ethIfIndex, NEIGH_A, MAC_A); - final Ipv6ForwardingRule ethernetRuleB = buildTestForwardingRule( + final Ipv6DownstreamRule ethernetRuleB = buildTestDownstreamRule( ethIfIndex, NEIGH_B, MAC_B); - coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleA); - verifyTetherOffloadRuleAdd(inOrder, ethernetRuleA); + coordinator.addIpv6DownstreamRule(mIpServer, ethernetRuleA); + verifyAddDownstreamRule(inOrder, ethernetRuleA); verifyTetherOffloadSetInterfaceQuota(inOrder, ethIfIndex, QUOTA_UNLIMITED, true /* isInit */); verifyStartUpstreamIpv6Forwarding(inOrder, DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC, ethIfIndex); - coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleB); - verifyTetherOffloadRuleAdd(inOrder, ethernetRuleB); + coordinator.addIpv6DownstreamRule(mIpServer, ethernetRuleB); + verifyAddDownstreamRule(inOrder, ethernetRuleB); // [2] Update the existing rules from Ethernet to cellular. - final Ipv6ForwardingRule mobileRuleA = buildTestForwardingRule( + final Ipv6DownstreamRule mobileRuleA = buildTestDownstreamRule( mobileIfIndex, NEIGH_A, MAC_A); - final Ipv6ForwardingRule mobileRuleB = buildTestForwardingRule( + final Ipv6DownstreamRule mobileRuleB = buildTestDownstreamRule( mobileIfIndex, NEIGH_B, MAC_B); updateStatsEntryForTetherOffloadGetAndClearStats( buildTestTetherStatsParcel(ethIfIndex, 10, 20, 30, 40)); @@ -1148,23 +1157,23 @@ public class BpfCoordinatorTest { // Update the existing rules for upstream changes. The rules are removed and re-added one // by one for updating upstream interface index by #tetherOffloadRuleUpdate. coordinator.tetherOffloadRuleUpdate(mIpServer, mobileIfIndex); - verifyTetherOffloadRuleRemove(inOrder, ethernetRuleA); - verifyTetherOffloadRuleRemove(inOrder, ethernetRuleB); + verifyRemoveDownstreamRule(inOrder, ethernetRuleA); + verifyRemoveDownstreamRule(inOrder, ethernetRuleB); verifyStopUpstreamIpv6Forwarding(inOrder, DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC); verifyTetherOffloadGetAndClearStats(inOrder, ethIfIndex); - verifyTetherOffloadRuleAdd(inOrder, mobileRuleA); + verifyAddDownstreamRule(inOrder, mobileRuleA); verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, QUOTA_UNLIMITED, true /* isInit */); verifyStartUpstreamIpv6Forwarding(inOrder, DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC, mobileIfIndex); - verifyTetherOffloadRuleAdd(inOrder, mobileRuleB); + verifyAddDownstreamRule(inOrder, mobileRuleB); // [3] Clear all rules for a given IpServer. updateStatsEntryForTetherOffloadGetAndClearStats( buildTestTetherStatsParcel(mobileIfIndex, 50, 60, 70, 80)); coordinator.tetherOffloadRuleClear(mIpServer); - verifyTetherOffloadRuleRemove(inOrder, mobileRuleA); - verifyTetherOffloadRuleRemove(inOrder, mobileRuleB); + verifyRemoveDownstreamRule(inOrder, mobileRuleA); + verifyRemoveDownstreamRule(inOrder, mobileRuleB); verifyStopUpstreamIpv6Forwarding(inOrder, DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC); verifyTetherOffloadGetAndClearStats(inOrder, mobileIfIndex); @@ -1201,37 +1210,37 @@ public class BpfCoordinatorTest { // The rule can't be added. final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1"); final MacAddress mac = MacAddress.fromString("00:00:00:00:00:0a"); - final Ipv6ForwardingRule rule = buildTestForwardingRule(ifIndex, neigh, mac); - coordinator.tetherOffloadRuleAdd(mIpServer, rule); - verifyNeverTetherOffloadRuleAdd(); - LinkedHashMap rules = - coordinator.getForwardingRulesForTesting().get(mIpServer); + final Ipv6DownstreamRule rule = buildTestDownstreamRule(ifIndex, neigh, mac); + coordinator.addIpv6DownstreamRule(mIpServer, rule); + verifyNeverAddDownstreamRule(); + LinkedHashMap rules = + coordinator.getIpv6DownstreamRulesForTesting().get(mIpServer); assertNull(rules); // The rule can't be removed. This is not a realistic case because adding rule is not // allowed. That implies no rule could be removed, cleared or updated. Verify these // cases just in case. - rules = new LinkedHashMap(); + rules = new LinkedHashMap(); rules.put(rule.address, rule); - coordinator.getForwardingRulesForTesting().put(mIpServer, rules); - coordinator.tetherOffloadRuleRemove(mIpServer, rule); - verifyNeverTetherOffloadRuleRemove(); - rules = coordinator.getForwardingRulesForTesting().get(mIpServer); + coordinator.getIpv6DownstreamRulesForTesting().put(mIpServer, rules); + coordinator.removeIpv6DownstreamRule(mIpServer, rule); + verifyNeverRemoveDownstreamRule(); + rules = coordinator.getIpv6DownstreamRulesForTesting().get(mIpServer); assertNotNull(rules); assertEquals(1, rules.size()); // The rule can't be cleared. coordinator.tetherOffloadRuleClear(mIpServer); - verifyNeverTetherOffloadRuleRemove(); - rules = coordinator.getForwardingRulesForTesting().get(mIpServer); + verifyNeverRemoveDownstreamRule(); + rules = coordinator.getIpv6DownstreamRulesForTesting().get(mIpServer); assertNotNull(rules); assertEquals(1, rules.size()); // The rule can't be updated. coordinator.tetherOffloadRuleUpdate(mIpServer, rule.upstreamIfindex + 1 /* new */); - verifyNeverTetherOffloadRuleRemove(); - verifyNeverTetherOffloadRuleAdd(); - rules = coordinator.getForwardingRulesForTesting().get(mIpServer); + verifyNeverRemoveDownstreamRule(); + verifyNeverAddDownstreamRule(); + rules = coordinator.getIpv6DownstreamRulesForTesting().get(mIpServer); assertNotNull(rules); assertEquals(1, rules.size()); } @@ -1669,17 +1678,17 @@ public class BpfCoordinatorTest { final BpfCoordinator coordinator = makeBpfCoordinator(); coordinator.addUpstreamNameToLookupTable(UPSTREAM_IFINDEX, UPSTREAM_IFACE); - final Ipv6ForwardingRule ruleA = buildTestForwardingRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A); - final Ipv6ForwardingRule ruleB = buildTestForwardingRule(UPSTREAM_IFINDEX, NEIGH_B, MAC_B); + final Ipv6DownstreamRule ruleA = buildTestDownstreamRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A); + final Ipv6DownstreamRule ruleB = buildTestDownstreamRule(UPSTREAM_IFINDEX, NEIGH_B, MAC_B); - coordinator.tetherOffloadRuleAdd(mIpServer, ruleA); + coordinator.addIpv6DownstreamRule(mIpServer, ruleA); verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(UPSTREAM_IFINDEX)), eq(new TetherDevValue(UPSTREAM_IFINDEX))); verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(DOWNSTREAM_IFINDEX)), eq(new TetherDevValue(DOWNSTREAM_IFINDEX))); clearInvocations(mBpfDevMap); - coordinator.tetherOffloadRuleAdd(mIpServer, ruleB); + coordinator.addIpv6DownstreamRule(mIpServer, ruleB); verify(mBpfDevMap, never()).updateEntry(any(), any()); } @@ -2139,9 +2148,15 @@ public class BpfCoordinatorTest { @Test public void testIpv6ForwardingRuleToString() throws Exception { - final Ipv6ForwardingRule rule = buildTestForwardingRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A); + final Ipv6DownstreamRule downstreamRule = buildTestDownstreamRule(UPSTREAM_IFINDEX, NEIGH_A, + MAC_A); assertEquals("upstreamIfindex: 1001, downstreamIfindex: 2001, address: 2001:db8::1, " - + "srcMac: 12:34:56:78:90:ab, dstMac: 00:00:00:00:00:0a", rule.toString()); + + "srcMac: 12:34:56:78:90:ab, dstMac: 00:00:00:00:00:0a", + downstreamRule.toString()); + final Ipv6UpstreamRule upstreamRule = buildTestUpstreamRule(UPSTREAM_IFINDEX); + assertEquals("upstreamIfindex: 1001, downstreamIfindex: 2001, sourcePrefix: ::/64, " + + "inDstMac: 12:34:56:78:90:ab, outSrcMac: 00:00:00:00:00:00, " + + "outDstMac: 00:00:00:00:00:00", upstreamRule.toString()); } private void verifyDump(@NonNull final BpfCoordinator coordinator) { @@ -2177,7 +2192,7 @@ public class BpfCoordinatorTest { // - dumpCounters // * mBpfErrorMap // - dumpIpv6ForwardingRulesByDownstream - // * mIpv6ForwardingRules + // * mIpv6DownstreamRules // dumpBpfForwardingRulesIpv4 mBpfDownstream4Map.insertEntry( @@ -2188,7 +2203,7 @@ public class BpfCoordinatorTest { new TestUpstream4Value.Builder().build()); // dumpBpfForwardingRulesIpv6 - final Ipv6ForwardingRule rule = buildTestForwardingRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A); + final Ipv6DownstreamRule rule = buildTestDownstreamRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A); mBpfDownstream6Map.insertEntry(rule.makeTetherDownstream6Key(), rule.makeTether6Value()); final TetherUpstream6Key upstream6Key = new TetherUpstream6Key(DOWNSTREAM_IFINDEX, @@ -2218,12 +2233,12 @@ public class BpfCoordinatorTest { new S32(1000 /* count */)); // dumpIpv6ForwardingRulesByDownstream - final HashMap> - ipv6ForwardingRules = coordinator.getForwardingRulesForTesting(); - final LinkedHashMap addressRuleMap = + final HashMap> + ipv6DownstreamRules = coordinator.getIpv6DownstreamRulesForTesting(); + final LinkedHashMap addressRuleMap = new LinkedHashMap<>(); addressRuleMap.put(rule.address, rule); - ipv6ForwardingRules.put(mIpServer, addressRuleMap); + ipv6DownstreamRules.put(mIpServer, addressRuleMap); verifyDump(coordinator); }