Use device option to control BPF offload features

If BPF offload device config is not enabled:
- Does not add/remove offload forwarding rules through disabling IP
  neighbor monitor.
- Does not apply the RA MTU reduction.

Bug: 149997301
Test: atest IpServerTest
Change-Id: I2d6f80f0229f580c4b16243a064e889a6c37f77a
This commit is contained in:
Hungming Chen
2020-04-12 14:27:18 +08:00
parent 8bf2e7e05b
commit 3d8fa889b4
3 changed files with 90 additions and 17 deletions

View File

@@ -227,6 +227,7 @@ public class IpServer extends StateMachine {
private final int mInterfaceType; private final int mInterfaceType;
private final LinkProperties mLinkProperties; private final LinkProperties mLinkProperties;
private final boolean mUsingLegacyDhcp; private final boolean mUsingLegacyDhcp;
private final boolean mUsingBpfOffload;
private final Dependencies mDeps; private final Dependencies mDeps;
@@ -304,7 +305,8 @@ public class IpServer extends StateMachine {
public IpServer( public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log, String ifaceName, Looper looper, int interfaceType, SharedLog log,
INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) { INetd netd, Callback callback, boolean usingLegacyDhcp, boolean usingBpfOffload,
Dependencies deps) {
super(ifaceName, looper); super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName); mLog = log.forSubComponent(ifaceName);
mNetd = netd; mNetd = netd;
@@ -314,6 +316,7 @@ public class IpServer extends StateMachine {
mInterfaceType = interfaceType; mInterfaceType = interfaceType;
mLinkProperties = new LinkProperties(); mLinkProperties = new LinkProperties();
mUsingLegacyDhcp = usingLegacyDhcp; mUsingLegacyDhcp = usingLegacyDhcp;
mUsingBpfOffload = usingBpfOffload;
mDeps = deps; mDeps = deps;
resetLinkProperties(); resetLinkProperties();
mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
@@ -321,8 +324,15 @@ public class IpServer extends StateMachine {
mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog, mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog,
new MyNeighborEventConsumer()); new MyNeighborEventConsumer());
if (!mIpNeighborMonitor.start()) {
mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName); // IP neighbor monitor monitors the neighbor event for adding/removing offload
// forwarding rules per client. If BPF offload is not supported, don't start listening
// neighbor events. See updateIpv6ForwardingRules, addIpv6ForwardingRule,
// removeIpv6ForwardingRule.
if (mUsingBpfOffload) {
if (!mIpNeighborMonitor.start()) {
mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName);
}
} }
mInitialState = new InitialState(); mInitialState = new InitialState();
@@ -715,12 +725,12 @@ public class IpServer extends StateMachine {
final String upstreamIface = v6only.getInterfaceName(); final String upstreamIface = v6only.getInterfaceName();
params = new RaParams(); params = new RaParams();
// We advertise an mtu lower by 16, which is the closest multiple of 8 >= 14, // When BPF offload is enabled, we advertise an mtu lower by 16, which is the closest
// the ethernet header size. This makes kernel ebpf tethering offload happy. // multiple of 8 >= 14, the ethernet header size. This makes kernel ebpf tethering
// This hack should be reverted once we have the kernel fixed up. // offload happy. This hack should be reverted once we have the kernel fixed up.
// Note: this will automatically clamp to at least 1280 (ipv6 minimum mtu) // Note: this will automatically clamp to at least 1280 (ipv6 minimum mtu)
// see RouterAdvertisementDaemon.java putMtu() // see RouterAdvertisementDaemon.java putMtu()
params.mtu = v6only.getMtu() - 16; params.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu();
params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface); if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface);
@@ -844,6 +854,11 @@ public class IpServer extends StateMachine {
} }
private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) { private void addIpv6ForwardingRule(Ipv6ForwardingRule 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;
try { try {
mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel()); mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel());
mIpv6ForwardingRules.put(rule.address, rule); mIpv6ForwardingRules.put(rule.address, rule);
@@ -853,6 +868,11 @@ public class IpServer extends StateMachine {
} }
private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) { private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) {
// 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;
try { try {
mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel()); mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
if (removeFromMap) { if (removeFromMap) {

View File

@@ -2289,7 +2289,7 @@ public class Tethering {
final TetherState tetherState = new TetherState( final TetherState tetherState = new TetherState(
new IpServer(iface, mLooper, interfaceType, mLog, mNetd, new IpServer(iface, mLooper, interfaceType, mLog, mNetd,
makeControlCallback(), mConfig.enableLegacyDhcpServer, makeControlCallback(), mConfig.enableLegacyDhcpServer,
mDeps.getIpServerDependencies())); mConfig.enableBpfOffload, mDeps.getIpServerDependencies()));
mTetherStates.put(iface, tetherState); mTetherStates.put(iface, tetherState);
tetherState.ipServer.start(); tetherState.ipServer.start();
} }

View File

@@ -106,6 +106,7 @@ public class IpServerTest {
private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1"; private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1";
private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24; private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
private static final int DHCP_LEASE_TIME_SECS = 3600; private static final int DHCP_LEASE_TIME_SECS = 3600;
private static final boolean DEFAULT_USING_BPF_OFFLOAD = true;
private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams( private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */); IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
@@ -130,10 +131,11 @@ public class IpServerTest {
private NeighborEventConsumer mNeighborEventConsumer; private NeighborEventConsumer mNeighborEventConsumer;
private void initStateMachine(int interfaceType) throws Exception { private void initStateMachine(int interfaceType) throws Exception {
initStateMachine(interfaceType, false /* usingLegacyDhcp */); initStateMachine(interfaceType, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD);
} }
private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception { private void initStateMachine(int interfaceType, boolean usingLegacyDhcp,
boolean usingBpfOffload) throws Exception {
doAnswer(inv -> { doAnswer(inv -> {
final IDhcpServerCallbacks cb = inv.getArgument(2); final IDhcpServerCallbacks cb = inv.getArgument(2);
new Thread(() -> { new Thread(() -> {
@@ -165,7 +167,7 @@ public class IpServerTest {
mIpServer = new IpServer( mIpServer = new IpServer(
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
mCallback, usingLegacyDhcp, mDependencies); mCallback, usingLegacyDhcp, usingBpfOffload, mDependencies);
mIpServer.start(); mIpServer.start();
mNeighborEventConsumer = neighborCaptor.getValue(); mNeighborEventConsumer = neighborCaptor.getValue();
@@ -179,12 +181,13 @@ public class IpServerTest {
private void initTetheredStateMachine(int interfaceType, String upstreamIface) private void initTetheredStateMachine(int interfaceType, String upstreamIface)
throws Exception { throws Exception {
initTetheredStateMachine(interfaceType, upstreamIface, false); initTetheredStateMachine(interfaceType, upstreamIface, false,
DEFAULT_USING_BPF_OFFLOAD);
} }
private void initTetheredStateMachine(int interfaceType, String upstreamIface, private void initTetheredStateMachine(int interfaceType, String upstreamIface,
boolean usingLegacyDhcp) throws Exception { boolean usingLegacyDhcp, boolean usingBpfOffload) throws Exception {
initStateMachine(interfaceType, usingLegacyDhcp); initStateMachine(interfaceType, usingLegacyDhcp, usingBpfOffload);
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
if (upstreamIface != null) { if (upstreamIface != null) {
LinkProperties lp = new LinkProperties(); LinkProperties lp = new LinkProperties();
@@ -204,7 +207,8 @@ public class IpServerTest {
when(mDependencies.getIpNeighborMonitor(any(), any(), any())) when(mDependencies.getIpNeighborMonitor(any(), any(), any()))
.thenReturn(mIpNeighborMonitor); .thenReturn(mIpNeighborMonitor);
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog, mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies); mNetd, mCallback, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD,
mDependencies);
mIpServer.start(); mIpServer.start();
mLooper.dispatchAll(); mLooper.dispatchAll();
verify(mCallback).updateInterfaceState( verify(mCallback).updateInterfaceState(
@@ -494,7 +498,8 @@ public class IpServerTest {
@Test @Test
public void doesNotStartDhcpServerIfDisabled() throws Exception { public void doesNotStartDhcpServerIfDisabled() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */); initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */,
DEFAULT_USING_BPF_OFFLOAD);
dispatchTetherConnectionChanged(UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE);
verify(mDependencies, never()).makeDhcpServer(any(), any(), any()); verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
@@ -577,7 +582,8 @@ public class IpServerTest {
@Test @Test
public void addRemoveipv6ForwardingRules() throws Exception { public void addRemoveipv6ForwardingRules() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */); initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
DEFAULT_USING_BPF_OFFLOAD);
final int myIfindex = TEST_IFACE_PARAMS.index; final int myIfindex = TEST_IFACE_PARAMS.index;
final int notMyIfindex = myIfindex - 1; final int notMyIfindex = myIfindex - 1;
@@ -678,6 +684,53 @@ public class IpServerTest {
reset(mNetd); reset(mNetd);
} }
@Test
public void enableDisableUsingBpfOffload() throws Exception {
final int myIfindex = TEST_IFACE_PARAMS.index;
final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1");
final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00");
reset(mNetd);
// Expect that rules can be only added/removed when the BPF offload config is enabled.
// Note that the usingBpfOffload false case is not a realistic test case. Because IP
// neighbor monitor doesn't start if BPF offload is disabled, there should have no
// neighbor event listening. This is used for testing the protection check just in case.
// TODO: Perhaps remove this test once we don't need this check anymore.
for (boolean usingBpfOffload : new boolean[]{true, false}) {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
usingBpfOffload);
// A neighbor is added.
recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA);
if (usingBpfOffload) {
verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neigh, macA));
} else {
verify(mNetd, never()).tetherOffloadRuleAdd(any());
}
reset(mNetd);
// A neighbor is deleted.
recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
if (usingBpfOffload) {
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neigh, macNull));
} else {
verify(mNetd, never()).tetherOffloadRuleRemove(any());
}
reset(mNetd);
}
}
@Test
public void doesNotStartIpNeighborMonitorIfBpfOffloadDisabled() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
false /* usingBpfOffload */);
// IP neighbor monitor doesn't start if BPF offload is disabled.
verify(mIpNeighborMonitor, never()).start();
}
private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception { private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any()); verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks( verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(