Merge "Add forwarding methods to RoutingCoordinator" into main
This commit is contained in:
@@ -839,7 +839,38 @@ public class IpServer extends StateMachineShim {
|
|||||||
}
|
}
|
||||||
} catch (ServiceSpecificException | RemoteException e) {
|
} catch (ServiceSpecificException | RemoteException e) {
|
||||||
mLog.e("Failed to add " + mIfaceName + " to local table: ", e);
|
mLog.e("Failed to add " + mIfaceName + " to local table: ", e);
|
||||||
return;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addInterfaceForward(@NonNull final String fromIface,
|
||||||
|
@NonNull final String toIface) throws ServiceSpecificException, RemoteException {
|
||||||
|
if (null != mRoutingCoordinator.value) {
|
||||||
|
mRoutingCoordinator.value.addInterfaceForward(fromIface, toIface);
|
||||||
|
} else {
|
||||||
|
mNetd.tetherAddForward(fromIface, toIface);
|
||||||
|
mNetd.ipfwdAddInterfaceForward(fromIface, toIface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeInterfaceForward(@NonNull final String fromIface,
|
||||||
|
@NonNull final String toIface) {
|
||||||
|
if (null != mRoutingCoordinator.value) {
|
||||||
|
try {
|
||||||
|
mRoutingCoordinator.value.removeInterfaceForward(fromIface, toIface);
|
||||||
|
} catch (ServiceSpecificException e) {
|
||||||
|
mLog.e("Exception in removeInterfaceForward", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
mNetd.ipfwdRemoveInterfaceForward(fromIface, toIface);
|
||||||
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
|
mLog.e("Exception in ipfwdRemoveInterfaceForward", e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mNetd.tetherRemoveForward(fromIface, toIface);
|
||||||
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
|
mLog.e("Exception in disableNat", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1349,16 +1380,7 @@ public class IpServer extends StateMachineShim {
|
|||||||
// to remove their rules, which generates errors.
|
// to remove their rules, which generates errors.
|
||||||
// Just do the best we can.
|
// Just do the best we can.
|
||||||
mBpfCoordinator.maybeDetachProgram(mIfaceName, upstreamIface);
|
mBpfCoordinator.maybeDetachProgram(mIfaceName, upstreamIface);
|
||||||
try {
|
removeInterfaceForward(mIfaceName, upstreamIface);
|
||||||
mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface);
|
|
||||||
} catch (RemoteException | ServiceSpecificException e) {
|
|
||||||
mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString());
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
mNetd.tetherRemoveForward(mIfaceName, upstreamIface);
|
|
||||||
} catch (RemoteException | ServiceSpecificException e) {
|
|
||||||
mLog.e("Exception in disableNat: " + e.toString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1414,10 +1436,9 @@ public class IpServer extends StateMachineShim {
|
|||||||
|
|
||||||
mBpfCoordinator.maybeAttachProgram(mIfaceName, ifname);
|
mBpfCoordinator.maybeAttachProgram(mIfaceName, ifname);
|
||||||
try {
|
try {
|
||||||
mNetd.tetherAddForward(mIfaceName, ifname);
|
addInterfaceForward(mIfaceName, ifname);
|
||||||
mNetd.ipfwdAddInterfaceForward(mIfaceName, ifname);
|
|
||||||
} catch (RemoteException | ServiceSpecificException e) {
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
mLog.e("Exception enabling NAT: " + e.toString());
|
mLog.e("Exception enabling iface forward", e);
|
||||||
cleanupUpstream();
|
cleanupUpstream();
|
||||||
mLastError = TETHER_ERROR_ENABLE_FORWARDING_ERROR;
|
mLastError = TETHER_ERROR_ENABLE_FORWARDING_ERROR;
|
||||||
transitionTo(mInitialState);
|
transitionTo(mInitialState);
|
||||||
|
|||||||
@@ -322,6 +322,23 @@ public class IpServerTest {
|
|||||||
when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(DEFAULT_USING_BPF_OFFLOAD);
|
when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(DEFAULT_USING_BPF_OFFLOAD);
|
||||||
when(mTetherConfig.useLegacyDhcpServer()).thenReturn(false /* default value */);
|
when(mTetherConfig.useLegacyDhcpServer()).thenReturn(false /* default value */);
|
||||||
|
|
||||||
|
// Simulate the behavior of RoutingCoordinator
|
||||||
|
if (null != mRoutingCoordinatorManager.value) {
|
||||||
|
doAnswer(it -> {
|
||||||
|
final String fromIface = (String) it.getArguments()[0];
|
||||||
|
final String toIface = (String) it.getArguments()[1];
|
||||||
|
mNetd.tetherAddForward(fromIface, toIface);
|
||||||
|
mNetd.ipfwdAddInterfaceForward(fromIface, toIface);
|
||||||
|
return null;
|
||||||
|
}).when(mRoutingCoordinatorManager.value).addInterfaceForward(any(), any());
|
||||||
|
doAnswer(it -> {
|
||||||
|
final String fromIface = (String) it.getArguments()[0];
|
||||||
|
final String toIface = (String) it.getArguments()[1];
|
||||||
|
mNetd.ipfwdRemoveInterfaceForward(fromIface, toIface);
|
||||||
|
mNetd.tetherRemoveForward(fromIface, toIface);
|
||||||
|
return null;
|
||||||
|
}).when(mRoutingCoordinatorManager.value).removeInterfaceForward(any(), any());
|
||||||
|
}
|
||||||
mBpfDeps = new BpfCoordinator.Dependencies() {
|
mBpfDeps = new BpfCoordinator.Dependencies() {
|
||||||
@NonNull
|
@NonNull
|
||||||
public Handler getHandler() {
|
public Handler getHandler() {
|
||||||
|
|||||||
@@ -72,4 +72,24 @@ interface IRoutingCoordinator {
|
|||||||
* unix errno.
|
* unix errno.
|
||||||
*/
|
*/
|
||||||
void removeInterfaceFromNetwork(int netId, in String iface);
|
void removeInterfaceFromNetwork(int netId, in String iface);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add forwarding ip rule
|
||||||
|
*
|
||||||
|
* @param fromIface interface name to add forwarding ip rule
|
||||||
|
* @param toIface interface name to add forwarding ip rule
|
||||||
|
* @throws ServiceSpecificException in case of failure, with an error code indicating the
|
||||||
|
* cause of the failure.
|
||||||
|
*/
|
||||||
|
void addInterfaceForward(in String fromIface, in String toIface);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove forwarding ip rule
|
||||||
|
*
|
||||||
|
* @param fromIface interface name to remove forwarding ip rule
|
||||||
|
* @param toIface interface name to remove forwarding ip rule
|
||||||
|
* @throws ServiceSpecificException in case of failure, with an error code indicating the
|
||||||
|
* cause of the failure.
|
||||||
|
*/
|
||||||
|
void removeInterfaceForward(in String fromIface, in String toIface);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,4 +123,36 @@ public class RoutingCoordinatorManager {
|
|||||||
throw e.rethrowFromSystemServer();
|
throw e.rethrowFromSystemServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add forwarding ip rule
|
||||||
|
*
|
||||||
|
* @param fromIface interface name to add forwarding ip rule
|
||||||
|
* @param toIface interface name to add forwarding ip rule
|
||||||
|
* @throws ServiceSpecificException in case of failure, with an error code indicating the
|
||||||
|
* cause of the failure.
|
||||||
|
*/
|
||||||
|
public void addInterfaceForward(final String fromIface, final String toIface) {
|
||||||
|
try {
|
||||||
|
mService.addInterfaceForward(fromIface, toIface);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove forwarding ip rule
|
||||||
|
*
|
||||||
|
* @param fromIface interface name to remove forwarding ip rule
|
||||||
|
* @param toIface interface name to remove forwarding ip rule
|
||||||
|
* @throws ServiceSpecificException in case of failure, with an error code indicating the
|
||||||
|
* cause of the failure.
|
||||||
|
*/
|
||||||
|
public void removeInterfaceForward(final String fromIface, final String toIface) {
|
||||||
|
try {
|
||||||
|
mService.removeInterfaceForward(fromIface, toIface);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,16 @@ package com.android.server.connectivity;
|
|||||||
import static com.android.net.module.util.NetdUtils.toRouteInfoParcel;
|
import static com.android.net.module.util.NetdUtils.toRouteInfoParcel;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.content.Context;
|
|
||||||
import android.net.INetd;
|
import android.net.INetd;
|
||||||
import android.net.IRoutingCoordinator;
|
import android.net.IRoutingCoordinator;
|
||||||
import android.net.RouteInfo;
|
import android.net.RouteInfo;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceSpecificException;
|
import android.os.ServiceSpecificException;
|
||||||
|
import android.util.ArraySet;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.GuardedBy;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to coordinate routing across multiple clients.
|
* Class to coordinate routing across multiple clients.
|
||||||
@@ -115,4 +119,89 @@ public class RoutingCoordinatorService extends IRoutingCoordinator.Stub {
|
|||||||
throws ServiceSpecificException, RemoteException {
|
throws ServiceSpecificException, RemoteException {
|
||||||
mNetd.networkRemoveInterface(netId, iface);
|
mNetd.networkRemoveInterface(netId, iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Object mIfacesLock = new Object();
|
||||||
|
private static final class ForwardingPair {
|
||||||
|
@NonNull public final String fromIface;
|
||||||
|
@NonNull public final String toIface;
|
||||||
|
ForwardingPair(@NonNull final String fromIface, @NonNull final String toIface) {
|
||||||
|
this.fromIface = fromIface;
|
||||||
|
this.toIface = toIface;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof ForwardingPair)) return false;
|
||||||
|
|
||||||
|
final ForwardingPair that = (ForwardingPair) o;
|
||||||
|
|
||||||
|
return fromIface.equals(that.fromIface) && toIface.equals(that.toIface);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = fromIface.hashCode();
|
||||||
|
result = 2 * result + toIface.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mIfacesLock")
|
||||||
|
private final ArraySet<ForwardingPair> mForwardedInterfaces = new ArraySet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add forwarding ip rule
|
||||||
|
*
|
||||||
|
* @param fromIface interface name to add forwarding ip rule
|
||||||
|
* @param toIface interface name to add forwarding ip rule
|
||||||
|
* @throws ServiceSpecificException in case of failure, with an error code indicating the
|
||||||
|
* cause of the failure.
|
||||||
|
*/
|
||||||
|
public void addInterfaceForward(final String fromIface, final String toIface)
|
||||||
|
throws ServiceSpecificException, RemoteException {
|
||||||
|
Objects.requireNonNull(fromIface);
|
||||||
|
Objects.requireNonNull(toIface);
|
||||||
|
synchronized (mIfacesLock) {
|
||||||
|
if (mForwardedInterfaces.size() == 0) {
|
||||||
|
mNetd.ipfwdEnableForwarding("RoutingCoordinator");
|
||||||
|
}
|
||||||
|
final ForwardingPair fwp = new ForwardingPair(fromIface, toIface);
|
||||||
|
if (mForwardedInterfaces.contains(fwp)) {
|
||||||
|
throw new IllegalStateException("Forward already exists between ifaces "
|
||||||
|
+ fromIface + " → " + toIface);
|
||||||
|
}
|
||||||
|
mForwardedInterfaces.add(fwp);
|
||||||
|
// Enables NAT for v4 and filters packets from unknown interfaces
|
||||||
|
mNetd.tetherAddForward(fromIface, toIface);
|
||||||
|
mNetd.ipfwdAddInterfaceForward(fromIface, toIface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove forwarding ip rule
|
||||||
|
*
|
||||||
|
* @param fromIface interface name to remove forwarding ip rule
|
||||||
|
* @param toIface interface name to remove forwarding ip rule
|
||||||
|
* @throws ServiceSpecificException in case of failure, with an error code indicating the
|
||||||
|
* cause of the failure.
|
||||||
|
*/
|
||||||
|
public void removeInterfaceForward(final String fromIface, final String toIface)
|
||||||
|
throws ServiceSpecificException, RemoteException {
|
||||||
|
Objects.requireNonNull(fromIface);
|
||||||
|
Objects.requireNonNull(toIface);
|
||||||
|
synchronized (mIfacesLock) {
|
||||||
|
final ForwardingPair fwp = new ForwardingPair(fromIface, toIface);
|
||||||
|
if (!mForwardedInterfaces.contains(fwp)) {
|
||||||
|
throw new IllegalStateException("No forward set up between interfaces "
|
||||||
|
+ fromIface + " → " + toIface);
|
||||||
|
}
|
||||||
|
mForwardedInterfaces.remove(fwp);
|
||||||
|
mNetd.ipfwdRemoveInterfaceForward(fromIface, toIface);
|
||||||
|
mNetd.tetherRemoveForward(fromIface, toIface);
|
||||||
|
if (mForwardedInterfaces.size() == 0) {
|
||||||
|
mNetd.ipfwdDisableForwarding("RoutingCoordinator");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.connectivity
|
||||||
|
|
||||||
|
import android.net.INetd
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
|
||||||
|
import com.android.testutils.DevSdkIgnoreRunner
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.ArgumentMatchers.any
|
||||||
|
import org.mockito.Mockito.inOrder
|
||||||
|
import org.mockito.Mockito.mock
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
|
@RunWith(DevSdkIgnoreRunner::class)
|
||||||
|
@IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
|
||||||
|
class RoutingCoordinatorServiceTest {
|
||||||
|
val mNetd = mock(INetd::class.java)
|
||||||
|
val mService = RoutingCoordinatorService(mNetd)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInterfaceForward() {
|
||||||
|
val inOrder = inOrder(mNetd)
|
||||||
|
|
||||||
|
mService.addInterfaceForward("from1", "to1")
|
||||||
|
inOrder.verify(mNetd).ipfwdEnableForwarding(any())
|
||||||
|
inOrder.verify(mNetd).tetherAddForward("from1", "to1")
|
||||||
|
inOrder.verify(mNetd).ipfwdAddInterfaceForward("from1", "to1")
|
||||||
|
|
||||||
|
mService.addInterfaceForward("from2", "to1")
|
||||||
|
inOrder.verify(mNetd).tetherAddForward("from2", "to1")
|
||||||
|
inOrder.verify(mNetd).ipfwdAddInterfaceForward("from2", "to1")
|
||||||
|
|
||||||
|
assertFailsWith<IllegalStateException> {
|
||||||
|
// Can't add the same pair again
|
||||||
|
mService.addInterfaceForward("from2", "to1")
|
||||||
|
}
|
||||||
|
|
||||||
|
mService.removeInterfaceForward("from1", "to1")
|
||||||
|
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward("from1", "to1")
|
||||||
|
inOrder.verify(mNetd).tetherRemoveForward("from1", "to1")
|
||||||
|
|
||||||
|
mService.removeInterfaceForward("from2", "to1")
|
||||||
|
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward("from2", "to1")
|
||||||
|
inOrder.verify(mNetd).tetherRemoveForward("from2", "to1")
|
||||||
|
|
||||||
|
inOrder.verify(mNetd).ipfwdDisableForwarding(any())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user