Merge "Fix ConntrackSocketTest#testIpv4ConntrackSocket flaky"

This commit is contained in:
KH Shi
2022-12-06 05:46:54 +00:00
committed by Gerrit Code Review
2 changed files with 67 additions and 37 deletions

View File

@@ -295,8 +295,7 @@ public class OffloadHardwareInterface {
NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY); NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
if (h1 == null) return false; if (h1 == null) return false;
sendIpv4NfGenMsg(h1, (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET), requestSocketDump(h1);
(short) (NLM_F_REQUEST | NLM_F_DUMP));
final NativeHandle h2 = mDeps.createConntrackSocket( final NativeHandle h2 = mDeps.createConntrackSocket(
NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY); NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
@@ -325,7 +324,7 @@ public class OffloadHardwareInterface {
} }
@VisibleForTesting @VisibleForTesting
public void sendIpv4NfGenMsg(@NonNull NativeHandle handle, short type, short flags) { void sendIpv4NfGenMsg(@NonNull NativeHandle handle, short type, short flags) {
final int length = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE; final int length = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE;
final byte[] msg = new byte[length]; final byte[] msg = new byte[length];
final ByteBuffer byteBuffer = ByteBuffer.wrap(msg); final ByteBuffer byteBuffer = ByteBuffer.wrap(msg);
@@ -350,6 +349,12 @@ public class OffloadHardwareInterface {
} }
} }
@VisibleForTesting
void requestSocketDump(NativeHandle handle) {
sendIpv4NfGenMsg(handle, (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET),
(short) (NLM_F_REQUEST | NLM_F_DUMP));
}
private void closeFdInNativeHandle(final NativeHandle h) { private void closeFdInNativeHandle(final NativeHandle h) {
try { try {
h.close(); h.close();

View File

@@ -16,28 +16,32 @@
package com.android.networkstack.tethering; package com.android.networkstack.tethering;
import static android.system.OsConstants.EAGAIN;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.NETLINK_NETFILTER;
import static com.android.net.module.util.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE; import static com.android.net.module.util.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE;
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
import static com.android.networkstack.tethering.OffloadHardwareInterface.IPCTNL_MSG_CT_GET;
import static com.android.networkstack.tethering.OffloadHardwareInterface.IPCTNL_MSG_CT_NEW; import static com.android.networkstack.tethering.OffloadHardwareInterface.IPCTNL_MSG_CT_NEW;
import static com.android.networkstack.tethering.OffloadHardwareInterface.NFNL_SUBSYS_CTNETLINK; import static com.android.networkstack.tethering.OffloadHardwareInterface.NFNL_SUBSYS_CTNETLINK;
import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_DESTROY; import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_DESTROY;
import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_NEW; import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_NEW;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail;
import static org.junit.Assert.assertTrue;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.Looper; import android.os.Looper;
import android.os.NativeHandle; import android.os.NativeHandle;
import android.system.Os; import android.system.ErrnoException;
import android.util.Log;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
import com.android.net.module.util.SharedLog; import com.android.net.module.util.SharedLog;
import com.android.net.module.util.netlink.ConntrackMessage;
import com.android.net.module.util.netlink.NetlinkMessage;
import com.android.net.module.util.netlink.NetlinkSocket;
import com.android.net.module.util.netlink.StructNlMsgHdr; import com.android.net.module.util.netlink.StructNlMsgHdr;
import org.junit.Before; import org.junit.Before;
@@ -45,18 +49,18 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import java.io.FileDescriptor;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@SmallTest @SmallTest
public class ConntrackSocketTest { public class ConntrackSocketTest {
private static final long TIMEOUT = 500; private static final long TIMEOUT = 500;
private static final String TAG = ConntrackSocketTest.class.getSimpleName();
private HandlerThread mHandlerThread; private HandlerThread mHandlerThread;
private Handler mHandler; private Handler mHandler;
@@ -80,51 +84,72 @@ public class ConntrackSocketTest {
mOffloadHw = new OffloadHardwareInterface(mHandler, mLog, mDeps); mOffloadHw = new OffloadHardwareInterface(mHandler, mLog, mDeps);
} }
void findConnectionOrThrow(FileDescriptor fd, InetSocketAddress local, InetSocketAddress remote)
throws Exception {
Log.d(TAG, "Looking for socket " + local + " -> " + remote);
// Loop until the socket is found (and return) or recvMessage throws an exception.
while (true) {
final ByteBuffer buffer = NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, TIMEOUT);
// Parse all the netlink messages in the dump.
// NetlinkMessage#parse returns null if the message is truncated or invalid.
while (buffer.remaining() > 0) {
NetlinkMessage nlmsg = NetlinkMessage.parse(buffer, NETLINK_NETFILTER);
Log.d(TAG, "Got netlink message: " + nlmsg);
if (!(nlmsg instanceof ConntrackMessage)) {
continue;
}
StructNlMsgHdr nlmsghdr = nlmsg.getHeader();
ConntrackMessage ctmsg = (ConntrackMessage) nlmsg;
ConntrackMessage.Tuple tuple = ctmsg.tupleOrig;
if (nlmsghdr.nlmsg_type == (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_NEW)
&& tuple.protoNum == IPPROTO_TCP
&& tuple.srcIp.equals(local.getAddress())
&& tuple.dstIp.equals(remote.getAddress())
&& tuple.srcPort == (short) local.getPort()
&& tuple.dstPort == (short) remote.getPort()) {
return;
}
}
}
}
@Test @Test
public void testIpv4ConntrackSocket() throws Exception { public void testIpv4ConntrackSocket() throws Exception {
// Set up server and connect. // Set up server and connect.
final InetSocketAddress anyAddress = new InetSocketAddress( final InetAddress localhost = InetAddress.getByName("127.0.0.1");
InetAddress.getByName("127.0.0.1"), 0); final InetSocketAddress anyAddress = new InetSocketAddress(localhost, 0);
final ServerSocket serverSocket = new ServerSocket(); final ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(anyAddress); serverSocket.bind(anyAddress);
final SocketAddress theAddress = serverSocket.getLocalSocketAddress(); final InetSocketAddress theAddress =
(InetSocketAddress) serverSocket.getLocalSocketAddress();
// Make a connection to the server. // Make a connection to the server.
final Socket socket = new Socket(); final Socket socket = new Socket();
socket.connect(theAddress); socket.connect(theAddress);
final InetSocketAddress localAddress = (InetSocketAddress) socket.getLocalSocketAddress();
final Socket acceptedSocket = serverSocket.accept(); final Socket acceptedSocket = serverSocket.accept();
final NativeHandle handle = mDeps.createConntrackSocket( final NativeHandle handle = mDeps.createConntrackSocket(
NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY); NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
mOffloadHw.sendIpv4NfGenMsg(handle, mOffloadHw.requestSocketDump(handle);
(short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET),
(short) (NLM_F_REQUEST | NLM_F_DUMP));
boolean foundConntrackEntry = false;
ByteBuffer buffer = ByteBuffer.allocate(DEFAULT_RECV_BUFSIZE);
buffer.order(ByteOrder.nativeOrder());
try { try {
while (Os.read(handle.getFileDescriptor(), buffer) > 0) { findConnectionOrThrow(handle.getFileDescriptor(), localAddress, theAddress);
buffer.flip(); // No exceptions? Socket was found, test passes.
} catch (ErrnoException e) {
// TODO: ConntrackMessage should get a parse API like StructNlMsgHdr if (e.errno == EAGAIN) {
// so we can confirm that the conntrack added is for the TCP connection above. fail("Did not find socket " + localAddress + "->" + theAddress + " in dump");
final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(buffer); } else {
assertNotNull(nlmsghdr); throw e;
// As long as 1 conntrack entry is found test case will pass, even if it's not
// the from the TCP connection above.
if (nlmsghdr.nlmsg_type == ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW)) {
foundConntrackEntry = true;
break;
}
} }
} finally { } finally {
socket.close(); socket.close();
serverSocket.close(); serverSocket.close();
} acceptedSocket.close();
assertTrue("Did not receive any NFNL_SUBSYS_CTNETLINK/IPCTNL_MSG_CT_NEW message", }
foundConntrackEntry);
} }
} }