From 244dce37224f43775ee0655d4b4ebd3fe79cd6fd Mon Sep 17 00:00:00 2001 From: Yang Sun Date: Wed, 10 Apr 2024 22:48:14 -0700 Subject: [PATCH] Add OT multicast addresses to tun interface. Bug: 333668308 Test: atest ThreadNetworkIntegrationTests:android.net.thread.ThreadIntegrationTest#joinNetwork_tunInterfaceJoinsAllRouterMulticastGroup Change-Id: I0bb91259ae5cbe0eb9c5d71b7aa4e5b2a95f8c6b --- .../server/thread/TunInterfaceController.java | 69 +++++++++++++++++-- thread/tests/integration/Android.bp | 1 + .../net/thread/ThreadIntegrationTest.java | 17 +++++ 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/thread/service/java/com/android/server/thread/TunInterfaceController.java b/thread/service/java/com/android/server/thread/TunInterfaceController.java index 97cdd5579c..dec72b260d 100644 --- a/thread/service/java/com/android/server/thread/TunInterfaceController.java +++ b/thread/service/java/com/android/server/thread/TunInterfaceController.java @@ -16,6 +16,8 @@ package com.android.server.thread; +import static android.system.OsConstants.EADDRINUSE; + import android.annotation.Nullable; import android.net.IpPrefix; import android.net.LinkAddress; @@ -39,6 +41,10 @@ import java.io.IOException; import java.io.InterruptedIOException; import java.net.Inet6Address; import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.MulticastSocket; +import java.net.NetworkInterface; +import java.net.SocketException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; @@ -58,12 +64,16 @@ public class TunInterfaceController { private ParcelFileDescriptor mParcelTunFd; private FileDescriptor mNetlinkSocket; private static int sNetlinkSeqNo = 0; + private final MulticastSocket mMulticastSocket; // For join group and leave group + private NetworkInterface mNetworkInterface; + private List mMulticastAddresses = new ArrayList<>(); /** Creates a new {@link TunInterfaceController} instance for given interface. */ public TunInterfaceController(String interfaceName) { mIfName = interfaceName; mLinkProperties.setInterfaceName(mIfName); mLinkProperties.setMtu(MTU); + mMulticastSocket = createMulticastSocket(); } /** Returns link properties of the Thread TUN interface. */ @@ -83,6 +93,11 @@ public class TunInterfaceController { } catch (ErrnoException e) { throw new IOException("Failed to create netlink socket", e); } + try { + mNetworkInterface = NetworkInterface.getByName(mIfName); + } catch (SocketException e) { + throw new IOException("Failed to get NetworkInterface", e); + } } public void destroyTunInterface() { @@ -94,6 +109,7 @@ public class TunInterfaceController { } mParcelTunFd = null; mNetlinkSocket = null; + mNetworkInterface = null; } /** Returns the FD of the tunnel interface. */ @@ -187,6 +203,7 @@ public class TunInterfaceController { public void updateAddresses(List addressInfoList) { final List newLinkAddresses = new ArrayList<>(); + final List newMulticastAddresses = new ArrayList<>(); boolean hasActiveOmrAddress = false; for (Ipv6AddressInfo addressInfo : addressInfoList) { @@ -199,12 +216,10 @@ public class TunInterfaceController { for (Ipv6AddressInfo addressInfo : addressInfoList) { InetAddress address = addressInfoToInetAddress(addressInfo); if (address.isMulticastAddress()) { - // TODO: Logging here will create repeated logs for a single multicast address, and - // it currently is not mandatory for debugging. Add log for ignored multicast - // address when necessary. - continue; + newMulticastAddresses.add(address); + } else { + newLinkAddresses.add(newLinkAddress(addressInfo, hasActiveOmrAddress)); } - newLinkAddresses.add(newLinkAddress(addressInfo, hasActiveOmrAddress)); } final CompareResult addressDiff = @@ -215,6 +230,17 @@ public class TunInterfaceController { for (LinkAddress linkAddress : addressDiff.added) { addAddress(linkAddress); } + + final CompareResult multicastAddressDiff = + new CompareResult<>(mMulticastAddresses, newMulticastAddresses); + for (InetAddress address : multicastAddressDiff.removed) { + leaveGroup(address); + } + for (InetAddress address : multicastAddressDiff.added) { + joinGroup(address); + } + mMulticastAddresses.clear(); + mMulticastAddresses.addAll(newMulticastAddresses); } private RouteInfo getRouteForAddress(LinkAddress linkAddress) { @@ -274,4 +300,37 @@ public class TunInterfaceController { deprecationTimeMillis, LinkAddress.LIFETIME_PERMANENT /* expirationTime */); } + + private MulticastSocket createMulticastSocket() { + try { + return new MulticastSocket(); + } catch (IOException e) { + throw new IllegalStateException("Failed to create multicast socket ", e); + } + } + + private void joinGroup(InetAddress address) { + InetSocketAddress socketAddress = new InetSocketAddress(address, 0); + try { + mMulticastSocket.joinGroup(socketAddress, mNetworkInterface); + } catch (IOException e) { + if (e.getCause() instanceof ErrnoException) { + ErrnoException ee = (ErrnoException) e.getCause(); + if (ee.errno == EADDRINUSE) { + Log.w(TAG, "Already joined group" + address.getHostAddress(), e); + return; + } + } + Log.e(TAG, "failed to join group " + address.getHostAddress(), e); + } + } + + private void leaveGroup(InetAddress address) { + InetSocketAddress socketAddress = new InetSocketAddress(address, 0); + try { + mMulticastSocket.leaveGroup(socketAddress, mNetworkInterface); + } catch (IOException e) { + Log.e(TAG, "failed to leave group " + address.getHostAddress(), e); + } + } } diff --git a/thread/tests/integration/Android.bp b/thread/tests/integration/Android.bp index 94985b1abb..71693af6bd 100644 --- a/thread/tests/integration/Android.bp +++ b/thread/tests/integration/Android.bp @@ -34,6 +34,7 @@ java_defaults { "testables", "ThreadNetworkTestUtils", "truth", + "ot-daemon-aidl-java", ], libs: [ "android.test.runner", diff --git a/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java b/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java index 1410d41987..e211e22e19 100644 --- a/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java +++ b/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java @@ -22,15 +22,18 @@ import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_STOPPED; import static android.net.thread.utils.IntegrationTestUtils.CALLBACK_TIMEOUT; import static android.net.thread.utils.IntegrationTestUtils.RESTART_JOIN_TIMEOUT; import static android.net.thread.utils.IntegrationTestUtils.getIpv6LinkAddresses; +import static android.net.thread.utils.IntegrationTestUtils.isInMulticastGroup; import static android.net.thread.utils.IntegrationTestUtils.waitFor; import static com.android.compatibility.common.util.SystemUtil.runShellCommand; import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow; +import static com.android.server.thread.openthread.IOtDaemon.TUN_IF_NAME; import static com.google.common.io.BaseEncoding.base16; import static com.google.common.truth.Truth.assertThat; import android.content.Context; +import android.net.InetAddresses; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.thread.utils.FullThreadDevice; @@ -83,6 +86,9 @@ public class ThreadIntegrationTest { private static final ActiveOperationalDataset DEFAULT_DATASET = ActiveOperationalDataset.fromThreadTlvs(DEFAULT_DATASET_TLVS); + private static final Inet6Address GROUP_ADDR_ALL_ROUTERS = + (Inet6Address) InetAddresses.parseNumericAddress("ff02::2"); + @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule(); private ExecutorService mExecutor; @@ -224,6 +230,13 @@ public class ThreadIntegrationTest { mOtCtl.executeCommand("br enable"); } + @Test + public void joinNetwork_tunInterfaceJoinsAllRouterMulticastGroup() throws Exception { + mController.joinAndWait(DEFAULT_DATASET); + + assertTunInterfaceMemberOfGroup(GROUP_ADDR_ALL_ROUTERS); + } + // TODO (b/323300829): add more tests for integration with linux platform and // ConnectivityService @@ -259,4 +272,8 @@ public class ThreadIntegrationTest { throw new IllegalStateException(e); } } + + private void assertTunInterfaceMemberOfGroup(Inet6Address address) throws Exception { + waitFor(() -> isInMulticastGroup(TUN_IF_NAME, address), TUN_ADDR_UPDATE_TIMEOUT); + } }