Only send to downstream ifaces for null network

When sendMulticastPacket or sendUnicastPacket is called with the null
network, only send the packets to interfaces indexed with null, instead
of all interfaces.

When using MdnsMultinetworkSocketClient sending packets on the null
network means sending on tethered downstream interfaces. When
MdnsSocketClient is used (not used in the Android tree), sending on the
null network sends on all interfaces as MdnsSocketClient does not
support specific networks. This is clarified by explicitly throwing
when a non-null Network is attempted to be used with MdnsSocketClient
(but MdnsSocketClient is only used for tests in the Android tree).

Bug: 283708537
Test: atest
(cherry picked from https://android-review.googlesource.com/q/commit:87c374a37ac8698a98d0c35a3196922c69549cc7)
Merged-In: Ia0186bf8aa2e0fc5878d6071fd23599df8488616
Change-Id: Ia0186bf8aa2e0fc5878d6071fd23599df8488616
This commit is contained in:
Remi NGUYEN VAN
2023-05-22 13:35:34 +09:00
committed by Cherrypicker Worker
parent e82a699fc7
commit 85ff5b1323
5 changed files with 131 additions and 82 deletions

View File

@@ -211,7 +211,7 @@ public class EnqueueMdnsQueryCallable implements Callable<Pair<Integer, List<Str
| (expectUnicastResponse ? MdnsConstants.QCLASS_UNICAST : 0));
}
private void sendPacketTo(MdnsSocketClientBase requestSender, InetSocketAddress address)
private void sendPacketTo(MdnsSocketClient requestSender, InetSocketAddress address)
throws IOException {
DatagramPacket packet = packetWriter.getPacket(address);
if (expectUnicastResponse) {

View File

@@ -17,7 +17,6 @@
package com.android.server.connectivity.mdns;
import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread;
import static com.android.server.connectivity.mdns.util.MdnsUtils.isNetworkMatched;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -34,6 +33,7 @@ import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Objects;
/**
* The {@link MdnsMultinetworkSocketClient} manages the multinetwork socket for mDns
@@ -216,7 +216,10 @@ public class MdnsMultinetworkSocketClient implements MdnsSocketClientBase {
final Network network = activeSockets.valueAt(i);
// Check ip capability and network before sending packet
if (((isIpv6 && socket.hasJoinedIpv6()) || (isIpv4 && socket.hasJoinedIpv4()))
&& isNetworkMatched(targetNetwork, network)) {
// Contrary to MdnsUtils.isNetworkMatched, only send packets targeting
// the null network to interfaces that have the null network (tethering
// downstream interfaces).
&& Objects.equals(network, targetNetwork)) {
try {
socket.send(packet);
} catch (IOException e) {
@@ -248,12 +251,6 @@ public class MdnsMultinetworkSocketClient implements MdnsSocketClientBase {
}
}
/** Sends a mDNS request packet that asks for multicast response. */
@Override
public void sendMulticastPacket(@NonNull DatagramPacket packet) {
sendMulticastPacket(packet, null /* network */);
}
/**
* Sends a mDNS request packet via given network that asks for multicast response. Null network
* means sending packet via all networks.
@@ -263,12 +260,6 @@ public class MdnsMultinetworkSocketClient implements MdnsSocketClientBase {
mHandler.post(() -> sendMdnsPacket(packet, network));
}
/** Sends a mDNS request packet that asks for unicast response. */
@Override
public void sendUnicastPacket(@NonNull DatagramPacket packet) {
sendUnicastPacket(packet, null /* network */);
}
/**
* Sends a mDNS request packet via given network that asks for unicast response. Null network
* means sending packet via all networks.

View File

@@ -195,13 +195,11 @@ public class MdnsSocketClient implements MdnsSocketClientBase {
}
/** Sends a mDNS request packet that asks for multicast response. */
@Override
public void sendMulticastPacket(@NonNull DatagramPacket packet) {
sendMdnsPacket(packet, multicastPacketQueue);
}
/** Sends a mDNS request packet that asks for unicast response. */
@Override
public void sendUnicastPacket(DatagramPacket packet) {
if (useSeparateSocketForUnicast) {
sendMdnsPacket(packet, unicastPacketQueue);
@@ -210,6 +208,36 @@ public class MdnsSocketClient implements MdnsSocketClientBase {
}
}
@Override
public void sendMulticastPacket(@NonNull DatagramPacket packet, @Nullable Network network) {
if (network != null) {
throw new IllegalArgumentException("This socket client does not support sending to "
+ "specific networks");
}
sendMulticastPacket(packet);
}
@Override
public void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network) {
if (network != null) {
throw new IllegalArgumentException("This socket client does not support sending to "
+ "specific networks");
}
sendUnicastPacket(packet);
}
@Override
public void notifyNetworkRequested(
@NonNull MdnsServiceBrowserListener listener,
@Nullable Network network,
@NonNull SocketCreationCallback socketCreationCallback) {
if (network != null) {
throw new IllegalArgumentException("This socket client does not support requesting "
+ "specific networks");
}
socketCreationCallback.onSocketCreated(null);
}
private void sendMdnsPacket(DatagramPacket packet, Queue<DatagramPacket> packetQueueToUse) {
if (shouldStopSocketLoop && !MdnsConfigs.allowAddMdnsPacketAfterDiscoveryStops()) {
LOGGER.w("sendMdnsPacket() is called after discovery already stopped");

View File

@@ -38,35 +38,25 @@ public interface MdnsSocketClientBase {
/*** Set callback for receiving mDns response */
void setCallback(@Nullable Callback callback);
/*** Sends a mDNS request packet that asks for multicast response. */
void sendMulticastPacket(@NonNull DatagramPacket packet);
/**
* Send a mDNS request packet via given network that asks for multicast response.
*
* <p>The socket client may use a null network to identify some or all interfaces, in which case
* passing null sends the packet to these.
*/
void sendMulticastPacket(@NonNull DatagramPacket packet, @Nullable Network network);
/**
* Sends a mDNS request packet via given network that asks for multicast response. Null network
* means sending packet via all networks.
* Send a mDNS request packet via given network that asks for unicast response.
*
* <p>The socket client may use a null network to identify some or all interfaces, in which case
* passing null sends the packet to these.
*/
default void sendMulticastPacket(@NonNull DatagramPacket packet, @Nullable Network network) {
throw new UnsupportedOperationException(
"This socket client doesn't support per-network sending");
}
/*** Sends a mDNS request packet that asks for unicast response. */
void sendUnicastPacket(@NonNull DatagramPacket packet);
/**
* Sends a mDNS request packet via given network that asks for unicast response. Null network
* means sending packet via all networks.
*/
default void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network) {
throw new UnsupportedOperationException(
"This socket client doesn't support per-network sending");
}
void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network);
/*** Notify that the given network is requested for mdns discovery / resolution */
default void notifyNetworkRequested(@NonNull MdnsServiceBrowserListener listener,
@Nullable Network network, @NonNull SocketCreationCallback socketCreationCallback) {
socketCreationCallback.onSocketCreated(network);
}
void notifyNetworkRequested(@NonNull MdnsServiceBrowserListener listener,
@Nullable Network network, @NonNull SocketCreationCallback socketCreationCallback);
/*** Notify that the network is unrequested */
default void notifyNetworkUnrequested(@NonNull MdnsServiceBrowserListener listener) { }

View File

@@ -38,6 +38,7 @@ import android.os.Handler;
import android.os.HandlerThread;
import com.android.net.module.util.HexDump;
import com.android.server.connectivity.mdns.MdnsSocketClientBase.SocketCreationCallback;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
@@ -66,7 +67,7 @@ public class MdnsMultinetworkSocketClientTest {
@Mock private MdnsInterfaceSocket mSocket;
@Mock private MdnsServiceBrowserListener mListener;
@Mock private MdnsSocketClientBase.Callback mCallback;
@Mock private MdnsSocketClientBase.SocketCreationCallback mSocketCreationCallback;
@Mock private SocketCreationCallback mSocketCreationCallback;
private MdnsMultinetworkSocketClient mSocketClient;
private Handler mHandler;
@@ -113,22 +114,36 @@ public class MdnsMultinetworkSocketClientTest {
InetAddresses.parseNumericAddress("192.0.2.1"), 0 /* port */);
final DatagramPacket ipv6Packet = new DatagramPacket(BUFFER, 0 /* offset */, BUFFER.length,
InetAddresses.parseNumericAddress("2001:db8::"), 0 /* port */);
doReturn(true).when(mSocket).hasJoinedIpv4();
doReturn(true).when(mSocket).hasJoinedIpv6();
doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
final MdnsInterfaceSocket tetherIfaceSock1 = mock(MdnsInterfaceSocket.class);
final MdnsInterfaceSocket tetherIfaceSock2 = mock(MdnsInterfaceSocket.class);
for (MdnsInterfaceSocket socket : List.of(mSocket, tetherIfaceSock1, tetherIfaceSock2)) {
doReturn(true).when(socket).hasJoinedIpv4();
doReturn(true).when(socket).hasJoinedIpv6();
doReturn(createEmptyNetworkInterface()).when(socket).getInterface();
}
// Notify socket created
callback.onSocketCreated(mNetwork, mSocket, List.of());
verify(mSocketCreationCallback).onSocketCreated(mNetwork);
callback.onSocketCreated(null, tetherIfaceSock1, List.of());
verify(mSocketCreationCallback).onSocketCreated(null);
callback.onSocketCreated(null, tetherIfaceSock2, List.of());
verify(mSocketCreationCallback, times(2)).onSocketCreated(null);
// Send packet to IPv4 with target network and verify sending has been called.
mSocketClient.sendMulticastPacket(ipv4Packet, mNetwork);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket).send(ipv4Packet);
verify(tetherIfaceSock1, never()).send(any());
verify(tetherIfaceSock2, never()).send(any());
// Send packet to IPv6 without target network and verify sending has been called.
mSocketClient.sendMulticastPacket(ipv6Packet);
mSocketClient.sendMulticastPacket(ipv6Packet, null);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket).send(ipv6Packet);
verify(mSocket, never()).send(ipv6Packet);
verify(tetherIfaceSock1).send(ipv6Packet);
verify(tetherIfaceSock2).send(ipv6Packet);
}
@Test
@@ -181,61 +196,86 @@ public class MdnsMultinetworkSocketClientTest {
@Test
public void testSocketRemovedAfterNetworkUnrequested() throws IOException {
// Request a socket
final SocketCallback callback = expectSocketCallback(mListener, mNetwork);
// Request sockets on all networks
final SocketCallback callback = expectSocketCallback(mListener, null);
final DatagramPacket ipv4Packet = new DatagramPacket(BUFFER, 0 /* offset */, BUFFER.length,
InetAddresses.parseNumericAddress("192.0.2.1"), 0 /* port */);
// Notify 3 socket created, including 2 tethered interfaces (null network)
final MdnsInterfaceSocket socket2 = mock(MdnsInterfaceSocket.class);
final MdnsInterfaceSocket socket3 = mock(MdnsInterfaceSocket.class);
doReturn(true).when(mSocket).hasJoinedIpv4();
doReturn(true).when(mSocket).hasJoinedIpv6();
doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
// Notify socket created
callback.onSocketCreated(mNetwork, mSocket, List.of());
verify(mSocketCreationCallback).onSocketCreated(mNetwork);
// Send IPv4 packet and verify sending has been called.
mSocketClient.sendMulticastPacket(ipv4Packet);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket).send(ipv4Packet);
// Request another socket with null network
final MdnsServiceBrowserListener listener2 = mock(MdnsServiceBrowserListener.class);
final Network network2 = mock(Network.class);
final MdnsInterfaceSocket socket2 = mock(MdnsInterfaceSocket.class);
final SocketCallback callback2 = expectSocketCallback(listener2, null);
doReturn(true).when(socket2).hasJoinedIpv4();
doReturn(true).when(socket2).hasJoinedIpv6();
doReturn(true).when(socket3).hasJoinedIpv4();
doReturn(true).when(socket3).hasJoinedIpv6();
doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
doReturn(createEmptyNetworkInterface()).when(socket2).getInterface();
// Notify socket created for two networks.
callback2.onSocketCreated(mNetwork, mSocket, List.of());
callback2.onSocketCreated(network2, socket2, List.of());
verify(mSocketCreationCallback, times(2)).onSocketCreated(mNetwork);
verify(mSocketCreationCallback).onSocketCreated(network2);
doReturn(createEmptyNetworkInterface()).when(socket3).getInterface();
// Send IPv4 packet and verify sending to two sockets.
mSocketClient.sendMulticastPacket(ipv4Packet);
callback.onSocketCreated(mNetwork, mSocket, List.of());
callback.onSocketCreated(null, socket2, List.of());
callback.onSocketCreated(null, socket3, List.of());
verify(mSocketCreationCallback).onSocketCreated(mNetwork);
verify(mSocketCreationCallback, times(2)).onSocketCreated(null);
// Send IPv4 packet on the non-null Network and verify sending has been called.
mSocketClient.sendMulticastPacket(ipv4Packet, mNetwork);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket, times(2)).send(ipv4Packet);
verify(socket2).send(ipv4Packet);
verify(mSocket).send(ipv4Packet);
verify(socket2, never()).send(any());
verify(socket3, never()).send(any());
// Unrequest another socket
// Request another socket with null network, get the same interfaces
final SocketCreationCallback socketCreationCb2 = mock(SocketCreationCallback.class);
final MdnsServiceBrowserListener listener2 = mock(MdnsServiceBrowserListener.class);
// requestSocket is called a second time
final ArgumentCaptor<SocketCallback> callback2Captor =
ArgumentCaptor.forClass(SocketCallback.class);
mHandler.post(() -> mSocketClient.notifyNetworkRequested(
listener2, null, socketCreationCb2));
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mProvider, times(2)).requestSocket(eq(null), callback2Captor.capture());
final SocketCallback callback2 = callback2Captor.getAllValues().get(1);
// Notify socket created for all networks.
callback2.onSocketCreated(mNetwork, mSocket, List.of());
callback2.onSocketCreated(null, socket2, List.of());
callback2.onSocketCreated(null, socket3, List.of());
verify(socketCreationCb2).onSocketCreated(mNetwork);
verify(socketCreationCb2, times(2)).onSocketCreated(null);
// Send IPv4 packet to null network and verify sending to the 2 tethered interface sockets.
mSocketClient.sendMulticastPacket(ipv4Packet, null);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
// ipv4Packet still sent only once on mSocket: times(1) matches the packet sent earlier on
// mNetwork
verify(mSocket, times(1)).send(ipv4Packet);
verify(socket2).send(ipv4Packet);
verify(socket3).send(ipv4Packet);
// Unregister the second request
mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(listener2));
verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback2);
// Send IPv4 packet again and verify only sending via mSocket
mSocketClient.sendMulticastPacket(ipv4Packet);
// Send IPv4 packet again and verify it's still sent a second time
mSocketClient.sendMulticastPacket(ipv4Packet, null);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket, times(3)).send(ipv4Packet);
verify(socket2).send(ipv4Packet);
verify(socket2, times(2)).send(ipv4Packet);
verify(socket3, times(2)).send(ipv4Packet);
// Unrequest remaining socket
// Unrequest remaining sockets
mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(mListener));
verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback);
// Send IPv4 packet and verify no more sending.
mSocketClient.sendMulticastPacket(ipv4Packet);
mSocketClient.sendMulticastPacket(ipv4Packet, null);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket, times(3)).send(ipv4Packet);
verify(socket2).send(ipv4Packet);
verify(mSocket, times(1)).send(ipv4Packet);
verify(socket2, times(2)).send(ipv4Packet);
verify(socket3, times(2)).send(ipv4Packet);
}
@Test