Introduce Ipv6UpstreamRule to bpf tethering offload

Replaced startUpstreamIpv6Forwarding and stopUpstreamIpv6Forwarding
with add/remove Ipv6UpstreamRule. This is a preparation for following
CLs which will pass the upstream's prefixes to the bpf map. We might
have more than one upstream rules.

Also renamed Ipv6ForwardingRule to Ipv6DownstreamRule since we have
defined the Ipv6UpstreamRule.

No logic changes are being made in this CL.

Test: atest TetheringTests
Bug: 261923493
Change-Id: I022f97c2daf468bbd4a4279a069ccf498013e7e7
This commit is contained in:
KH Shi
2023-08-15 07:13:00 +00:00
parent 760bcac429
commit 59ad35ee19
7 changed files with 381 additions and 304 deletions

View File

@@ -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<TetherStatsValue> tetherOffloadGetStats() {

View File

@@ -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<TetherStatsValue> tetherOffloadGetStats() {

View File

@@ -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.

View File

@@ -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;

View File

@@ -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<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
mIpv6ForwardingRules = new LinkedHashMap<>();
private final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6DownstreamRule>>
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<Inet6Address,
Ipv6ForwardingRule>());
if (!mIpv6DownstreamRules.containsKey(ipServer)) {
mIpv6DownstreamRules.put(ipServer, new LinkedHashMap<Inet6Address,
Ipv6DownstreamRule>());
}
LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer);
LinkedHashMap<Inet6Address, Ipv6DownstreamRule> 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<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer);
LinkedHashMap<Inet6Address, Ipv6DownstreamRule> 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<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(
ipServer);
final LinkedHashMap<Inet6Address, Ipv6DownstreamRule> 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<Ipv6ForwardingRule>(rules.values())) {
tetherOffloadRuleRemove(ipServer, rule);
for (final Ipv6DownstreamRule rule : new ArrayList<Ipv6DownstreamRule>(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<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(
ipServer);
final LinkedHashMap<Inet6Address, Ipv6DownstreamRule> 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<Ipv6ForwardingRule> rulesCopy = new ArrayList<>(rules.values());
for (final Ipv6ForwardingRule rule : rulesCopy) {
final ArrayList<Ipv6DownstreamRule> 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<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>> entry :
mIpv6ForwardingRules.entrySet()) {
for (Map.Entry<IpServer, LinkedHashMap<Inet6Address, Ipv6DownstreamRule>> 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<Inet6Address, Ipv6ForwardingRule> rules = entry.getValue();
for (Ipv6ForwardingRule rule : rules.values()) {
LinkedHashMap<Inet6Address, Ipv6DownstreamRule> 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<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
.values()) {
for (Ipv6ForwardingRule rule : rules.values()) {
for (LinkedHashMap<Inet6Address, Ipv6DownstreamRule> 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<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
.values()) {
for (Ipv6ForwardingRule rule : rules.values()) {
for (LinkedHashMap<Inet6Address, Ipv6DownstreamRule> 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<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
.values()) {
for (Ipv6ForwardingRule rule : rules.values()) {
for (LinkedHashMap<Inet6Address, Ipv6DownstreamRule> 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<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
getForwardingRulesForTesting() {
return mIpv6ForwardingRules;
final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6DownstreamRule>>
getIpv6DownstreamRulesForTesting() {
return mIpv6DownstreamRules;
}
// Return upstream interface name map. This is used for testing only.

View File

@@ -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.

View File

@@ -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<Inet6Address, Ipv6ForwardingRule> rules =
coordinator.getForwardingRulesForTesting().get(mIpServer);
final Ipv6DownstreamRule rule = buildTestDownstreamRule(ifIndex, neigh, mac);
coordinator.addIpv6DownstreamRule(mIpServer, rule);
verifyNeverAddDownstreamRule();
LinkedHashMap<Inet6Address, Ipv6DownstreamRule> 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<Inet6Address, Ipv6ForwardingRule>();
rules = new LinkedHashMap<Inet6Address, Ipv6DownstreamRule>();
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<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
ipv6ForwardingRules = coordinator.getForwardingRulesForTesting();
final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> addressRuleMap =
final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6DownstreamRule>>
ipv6DownstreamRules = coordinator.getIpv6DownstreamRulesForTesting();
final LinkedHashMap<Inet6Address, Ipv6DownstreamRule> addressRuleMap =
new LinkedHashMap<>();
addressRuleMap.put(rule.address, rule);
ipv6ForwardingRules.put(mIpServer, addressRuleMap);
ipv6DownstreamRules.put(mIpServer, addressRuleMap);
verifyDump(coordinator);
}