blob: c543fad62dba3066bbefede654474d91f1162486 [file] [log] [blame]
/*
* Copyright (C) 2020 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.networkstack.tethering;
import static android.net.util.TetheringUtils.uint16;
import static android.system.OsConstants.SOCK_STREAM;
import static android.system.OsConstants.AF_UNIX;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
import android.net.netlink.StructNlMsgHdr;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.NativeHandle;
import android.os.test.TestLooper;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.system.Os;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.FileDescriptor;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@RunWith(AndroidJUnit4.class)
@SmallTest
public final class OffloadHardwareInterfaceTest {
private static final String RMNET0 = "test_rmnet_data0";
private final TestLooper mTestLooper = new TestLooper();
private OffloadHardwareInterface mOffloadHw;
private ITetheringOffloadCallback mTetheringOffloadCallback;
private OffloadHardwareInterface.ControlCallback mControlCallback;
@Mock private IOffloadConfig mIOffloadConfig;
@Mock private IOffloadControl mIOffloadControl;
@Mock private NativeHandle mNativeHandle;
// Random values to test Netlink message.
private static final short TEST_TYPE = 184;
private static final short TEST_FLAGS = 263;
class MyDependencies extends OffloadHardwareInterface.Dependencies {
MyDependencies(SharedLog log) {
super(log);
}
@Override
public IOffloadConfig getOffloadConfig() {
return mIOffloadConfig;
}
@Override
public IOffloadControl getOffloadControl() {
return mIOffloadControl;
}
@Override
public NativeHandle createConntrackSocket(final int groups) {
return mNativeHandle;
}
}
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
final SharedLog log = new SharedLog("test");
mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log,
new MyDependencies(log));
mControlCallback = spy(new OffloadHardwareInterface.ControlCallback());
}
private void startOffloadHardwareInterface() throws Exception {
mOffloadHw.initOffloadConfig();
mOffloadHw.initOffloadControl(mControlCallback);
final ArgumentCaptor<ITetheringOffloadCallback> mOffloadCallbackCaptor =
ArgumentCaptor.forClass(ITetheringOffloadCallback.class);
verify(mIOffloadControl).initOffload(mOffloadCallbackCaptor.capture(), any());
mTetheringOffloadCallback = mOffloadCallbackCaptor.getValue();
}
@Test
public void testGetForwardedStats() throws Exception {
startOffloadHardwareInterface();
final OffloadHardwareInterface.ForwardedStats stats = mOffloadHw.getForwardedStats(RMNET0);
verify(mIOffloadControl).getForwardedStats(eq(RMNET0), any());
assertNotNull(stats);
}
@Test
public void testSetLocalPrefixes() throws Exception {
startOffloadHardwareInterface();
final ArrayList<String> localPrefixes = new ArrayList<>();
localPrefixes.add("127.0.0.0/8");
localPrefixes.add("fe80::/64");
mOffloadHw.setLocalPrefixes(localPrefixes);
verify(mIOffloadControl).setLocalPrefixes(eq(localPrefixes), any());
}
@Test
public void testSetDataLimit() throws Exception {
startOffloadHardwareInterface();
final long limit = 12345;
mOffloadHw.setDataLimit(RMNET0, limit);
verify(mIOffloadControl).setDataLimit(eq(RMNET0), eq(limit), any());
}
@Test
public void testSetUpstreamParameters() throws Exception {
startOffloadHardwareInterface();
final String v4addr = "192.168.10.1";
final String v4gateway = "192.168.10.255";
final ArrayList<String> v6gws = new ArrayList<>(0);
v6gws.add("2001:db8::1");
mOffloadHw.setUpstreamParameters(RMNET0, v4addr, v4gateway, v6gws);
verify(mIOffloadControl).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway),
eq(v6gws), any());
final ArgumentCaptor<ArrayList<String>> mArrayListCaptor =
ArgumentCaptor.forClass(ArrayList.class);
mOffloadHw.setUpstreamParameters(null, null, null, null);
verify(mIOffloadControl).setUpstreamParameters(eq(""), eq(""), eq(""),
mArrayListCaptor.capture(), any());
assertEquals(mArrayListCaptor.getValue().size(), 0);
}
@Test
public void testUpdateDownstreamPrefix() throws Exception {
startOffloadHardwareInterface();
final String ifName = "wlan1";
final String prefix = "192.168.43.0/24";
mOffloadHw.addDownstreamPrefix(ifName, prefix);
verify(mIOffloadControl).addDownstream(eq(ifName), eq(prefix), any());
mOffloadHw.removeDownstreamPrefix(ifName, prefix);
verify(mIOffloadControl).removeDownstream(eq(ifName), eq(prefix), any());
}
@Test
public void testTetheringOffloadCallback() throws Exception {
startOffloadHardwareInterface();
mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED);
mTestLooper.dispatchAll();
verify(mControlCallback).onStarted();
mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR);
mTestLooper.dispatchAll();
verify(mControlCallback).onStoppedError();
mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED);
mTestLooper.dispatchAll();
verify(mControlCallback).onStoppedUnsupported();
mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE);
mTestLooper.dispatchAll();
verify(mControlCallback).onSupportAvailable();
mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED);
mTestLooper.dispatchAll();
verify(mControlCallback).onStoppedLimitReached();
final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP);
mTetheringOffloadCallback.updateTimeout(tcpParams);
mTestLooper.dispatchAll();
verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP),
eq(tcpParams.src.addr),
eq(uint16(tcpParams.src.port)),
eq(tcpParams.dst.addr),
eq(uint16(tcpParams.dst.port)));
final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP);
mTetheringOffloadCallback.updateTimeout(udpParams);
mTestLooper.dispatchAll();
verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP),
eq(udpParams.src.addr),
eq(uint16(udpParams.src.port)),
eq(udpParams.dst.addr),
eq(uint16(udpParams.dst.port)));
}
@Test
public void testNetlinkMessage() throws Exception {
FileDescriptor writeSocket = new FileDescriptor();
FileDescriptor readSocket = new FileDescriptor();
try {
Os.socketpair(AF_UNIX, SOCK_STREAM, 0, writeSocket, readSocket);
} catch (ErrnoException e) {
fail();
return;
}
when(mNativeHandle.getFileDescriptor()).thenReturn(writeSocket);
mOffloadHw.sendNetlinkMessage(mNativeHandle, TEST_TYPE, TEST_FLAGS);
ByteBuffer buffer = ByteBuffer.allocate(StructNlMsgHdr.STRUCT_SIZE);
int read = Os.read(readSocket, buffer);
buffer.flip();
assertEquals(StructNlMsgHdr.STRUCT_SIZE, buffer.getInt());
assertEquals(TEST_TYPE, buffer.getShort());
assertEquals(TEST_FLAGS, buffer.getShort());
assertEquals(1 /* seq */, buffer.getInt());
assertEquals(0 /* pid */, buffer.getInt());
}
private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) {
final NatTimeoutUpdate params = new NatTimeoutUpdate();
params.proto = proto;
params.src.addr = "192.168.43.200";
params.src.port = 100;
params.dst.addr = "172.50.46.169";
params.dst.port = 150;
return params;
}
}