Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
[Android] Fix accessing network interfaces information (#62780)
* Re-enable tests

* Dynamically load getifaddrs if necessary

* Prevent redeclaration of the ifaddrs struct

* Fix typo

* Do not close the dynamic library

* Enable the fixed functional tests in CI

* Reduce code duplication for obtaining libc file name

* Simplify usage of the function pointers

* Move typedefs

* Rename the _ensure_ function

* Remove fptr typedefs

* Update comment

* Add missing include

* Update comment

* Remove unnecessary comment

* Move static variable

* Remove unnecessary change

* Move LIBC_FILENAME to the utilities header

* Avoid error if constant is undefined

* Conditionally include ifaddrs

* Try to fix cmake_symbol_exists issue for browser

* Minor tweaks

* Try different way of detecting getifaddrs

* Use the hack only for Android builds

* Revert "Move LIBC_FILENAME to the utilities header"

This reverts commit 4e67687.

* Revert "Reduce code duplication for obtaining libc file name"

This reverts commit aca15d1.

* Simplify opening libc

* Update code style

* Fix race condition

* Prevent race condition

* Switch locking implementation for a lock-free implementation

* Enable unit test for Android

* Try using weak symbols

* Fix function name

* Revert "Fix function name"

This reverts commit f927aae.

* Revert "Try using weak symbols"

This reverts commit 46d3ede.

* Refactor code to use pthread_once
  • Loading branch information
simonrozsival committed Jan 11, 2022
commit 6f494294b4147ce7f1e151778d62bc934e147cb3
1 change: 1 addition & 0 deletions src/libraries/Native/Unix/Common/pal_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#cmakedefine01 HAVE_F_FULLFSYNC
#cmakedefine01 HAVE_O_CLOEXEC
#cmakedefine01 HAVE_GETIFADDRS
#cmakedefine01 HAVE_IFADDRS
#cmakedefine01 HAVE_UTSNAME_DOMAINNAME
#cmakedefine01 HAVE_STAT64
#cmakedefine01 HAVE_FORK
Expand Down
77 changes: 72 additions & 5 deletions src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@
#include <stdlib.h>
#include <sys/types.h>
#include <assert.h>
#if HAVE_IFADDRS || HAVE_GETIFADDRS
#include <ifaddrs.h>
#endif
#if !HAVE_GETIFADDRS && TARGET_ANDROID
#include <dlfcn.h>
#include <pthread.h>
#endif
#include <net/if.h>
#include <netinet/in.h>
#include <string.h>
Expand Down Expand Up @@ -55,7 +61,6 @@
#endif
#endif

#if HAVE_GETIFADDRS
// Convert mask to prefix length e.g. 255.255.255.0 -> 24
// mask parameter is pointer to buffer where address starts and length is
// buffer length e.g. 4 for IPv4 and 16 for IPv6.
Expand Down Expand Up @@ -95,14 +100,67 @@ static inline uint8_t mask2prefix(uint8_t* mask, int length)

return len;
}

#if !HAVE_IFADDRS && TARGET_ANDROID
// This structure is exactly the same as struct ifaddrs defined in ifaddrs.h but since the header
// might not be available (e.g., in bionics used in Android before API 24) we need to mirror it here
// so that we can dynamically load the getifaddrs function and use it.
struct ifaddrs
{
struct ifaddrs *ifa_next;
char *ifa_name;
unsigned int ifa_flags;
struct sockaddr *ifa_addr;
struct sockaddr *ifa_netmask;
union
{
struct sockaddr *ifu_broadaddr;
struct sockaddr *ifu_dstaddr;
} ifa_ifu;
void *ifa_data;
};
#endif

#if !HAVE_GETIFADDRS && TARGET_ANDROID
// Try to load the getifaddrs and freeifaddrs functions manually.
// This workaround is necessary on Android prior to API 24 and it can be removed once
// we drop support for earlier Android versions.
static int (*getifaddrs)(struct ifaddrs**) = NULL;
static void (*freeifaddrs)(struct ifaddrs*) = NULL;

static void try_loading_getifaddrs()
{
void *libc = dlopen("libc.so", RTLD_NOW);
if (libc)
{
getifaddrs = (int (*)(struct ifaddrs**)) dlsym(libc, "getifaddrs");
freeifaddrs = (void (*)(struct ifaddrs*)) dlsym(libc, "freeifaddrs");
}
}

static bool ensure_getifaddrs_is_loaded()
{
static pthread_once_t getifaddrs_is_loaded = PTHREAD_ONCE_INIT;
pthread_once(&getifaddrs_is_loaded, try_loading_getifaddrs);
return getifaddrs != NULL && freeifaddrs != NULL;
}
#endif

int32_t SystemNative_EnumerateInterfaceAddresses(void* context,
IPv4AddressFound onIpv4Found,
IPv6AddressFound onIpv6Found,
LinkLayerAddressFound onLinkLayerFound)
{
#if HAVE_GETIFADDRS
#if !HAVE_GETIFADDRS && TARGET_ANDROID
// Workaround for Android API < 24
if (!ensure_getifaddrs_is_loaded())
{
errno = ENOTSUP;
return -1;
}
#endif

#if HAVE_GETIFADDRS || TARGET_ANDROID
struct ifaddrs* headAddr;
if (getifaddrs(&headAddr) == -1)
{
Expand Down Expand Up @@ -235,7 +293,7 @@ int32_t SystemNative_EnumerateInterfaceAddresses(void* context,
freeifaddrs(headAddr);
return 0;
#else
// Not supported on e.g. Android. Also, prevent a compiler error because parameters are unused
// Not supported. Also, prevent a compiler error because parameters are unused
(void)context;
(void)onIpv4Found;
(void)onIpv6Found;
Expand All @@ -247,7 +305,16 @@ int32_t SystemNative_EnumerateInterfaceAddresses(void* context,

int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInterfaceInfo **interfaceList, int32_t * addressCount, IpAddressInfo **addressList )
{
#if HAVE_GETIFADDRS
#if !HAVE_GETIFADDRS && TARGET_ANDROID
// Workaround for Android API < 24
if (!ensure_getifaddrs_is_loaded())
{
errno = ENOTSUP;
return -1;
}
#endif

#if HAVE_GETIFADDRS || TARGET_ANDROID
struct ifaddrs* head; // Pointer to block allocated by getifaddrs().
struct ifaddrs* ifaddrsEntry;
IpAddressInfo *ai;
Expand Down Expand Up @@ -453,7 +520,7 @@ int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInter

return 0;
#else
// Not supported on e.g. Android. Also, prevent a compiler error because parameters are unused
// Not supported. Also, prevent a compiler error because parameters are unused
(void)interfaceCount;
(void)interfaceList;
(void)addressCount;
Expand Down
16 changes: 14 additions & 2 deletions src/libraries/Native/Unix/configure.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ include(CheckStructHasMember)
include(CheckSymbolExists)
include(CheckTypeSize)
include(CheckLibraryExists)
include(CheckFunctionExists)

# CMP0075 Include file check macros honor CMAKE_REQUIRED_LIBRARIES.
if(POLICY CMP0075)
Expand Down Expand Up @@ -141,6 +142,18 @@ check_c_source_compiles(
"
HAVE_FLOCK64)

check_c_source_compiles(
"
#include <sys/types.h>
#include <ifaddrs.h>
int main(void)
{
struct ifaddrs ia;
return 0;
}
"
HAVE_IFADDRS)

check_symbol_exists(
O_CLOEXEC
fcntl.h
Expand All @@ -156,9 +169,8 @@ check_symbol_exists(
fcntl.h
HAVE_F_FULLFSYNC)

check_symbol_exists(
check_function_exists(
getifaddrs
ifaddrs.h
HAVE_GETIFADDRS)

check_symbol_exists(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public IPGlobalPropertiesTest(ITestOutputHelper output)
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50567", TestPlatforms.Android)]
public void IPGlobalProperties_AccessAllMethods_NoErrors()
{
IPGlobalProperties gp = IPGlobalProperties.GetIPGlobalProperties();
Expand All @@ -51,6 +52,7 @@ public void IPGlobalProperties_AccessAllMethods_NoErrors()

[Theory]
[MemberData(nameof(Loopbacks))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50567", TestPlatforms.Android)]
public void IPGlobalProperties_TcpListeners_Succeed(IPAddress address)
{
using (var server = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
Expand All @@ -77,6 +79,7 @@ public void IPGlobalProperties_TcpListeners_Succeed(IPAddress address)
[Theory]
[ActiveIssue("https://github.com/dotnet/runtime/issues/34690", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)]
[MemberData(nameof(Loopbacks))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50567", TestPlatforms.Android)]
public async Task IPGlobalProperties_TcpActiveConnections_Succeed(IPAddress address)
{
using (var server = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
Expand Down Expand Up @@ -106,6 +109,7 @@ public async Task IPGlobalProperties_TcpActiveConnections_Succeed(IPAddress addr
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50567", TestPlatforms.Android)]
public void IPGlobalProperties_TcpActiveConnections_NotListening()
{
TcpConnectionInformation[] tcpCconnections = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections();
Expand All @@ -116,7 +120,6 @@ public void IPGlobalProperties_TcpActiveConnections_NotListening()
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50567", TestPlatforms.Android)]
public async Task GetUnicastAddresses_NotEmpty()
{
IPGlobalProperties props = IPGlobalProperties.GetIPGlobalProperties();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ public NetworkInterfaceBasicTest(ITestOutputHelper output)
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50567", TestPlatforms.Android)]
public void BasicTest_GetNetworkInterfaces_AtLeastOne()
{
Assert.NotEqual<int>(0, NetworkInterface.GetAllNetworkInterfaces().Length);
Expand Down Expand Up @@ -57,7 +56,7 @@ public void BasicTest_AccessInstanceProperties_NoExceptions()
}

[Fact]
[PlatformSpecific(TestPlatforms.Linux)] // Some APIs are not supported on Linux
[PlatformSpecific(TestPlatforms.Linux|TestPlatforms.Android)] // Some APIs are not supported on Linux and Android
public void BasicTest_AccessInstanceProperties_NoExceptions_Linux()
{
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
Expand Down Expand Up @@ -132,7 +131,6 @@ public void BasicTest_AccessInstanceProperties_NoExceptions_Bsd()

[Fact]
[Trait("IPv4", "true")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50567", TestPlatforms.Android)]
public void BasicTest_StaticLoopbackIndex_MatchesLoopbackNetworkInterface()
{
Assert.True(Capability.IPv4Support());
Expand All @@ -156,7 +154,6 @@ public void BasicTest_StaticLoopbackIndex_MatchesLoopbackNetworkInterface()

[Fact]
[Trait("IPv4", "true")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50567", TestPlatforms.Android)]
public void BasicTest_StaticLoopbackIndex_ExceptionIfV4NotSupported()
{
Assert.True(Capability.IPv4Support());
Expand All @@ -166,7 +163,6 @@ public void BasicTest_StaticLoopbackIndex_ExceptionIfV4NotSupported()

[Fact]
[Trait("IPv6", "true")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50567", TestPlatforms.Android)]
public void BasicTest_StaticIPv6LoopbackIndex_MatchesLoopbackNetworkInterface()
{
Assert.True(Capability.IPv6Support());
Expand All @@ -191,7 +187,6 @@ public void BasicTest_StaticIPv6LoopbackIndex_MatchesLoopbackNetworkInterface()

[Fact]
[Trait("IPv6", "true")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50567", TestPlatforms.Android)]
public void BasicTest_StaticIPv6LoopbackIndex_ExceptionIfV6NotSupported()
{
Assert.True(Capability.IPv6Support());
Expand Down Expand Up @@ -272,7 +267,6 @@ public void BasicTest_GetIPInterfaceStatistics_Success_Bsd()


[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50567", TestPlatforms.Android)]
public void BasicTest_GetIsNetworkAvailable_Success()
{
Assert.True(NetworkInterface.GetIsNetworkAvailable());
Expand All @@ -284,7 +278,6 @@ public void BasicTest_GetIsNetworkAvailable_Success()
[SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on Browser, iOS, MacCatalyst, or tvOS.")]
[InlineData(false)]
[InlineData(true)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50567", TestPlatforms.Android)]
public async Task NetworkInterface_LoopbackInterfaceIndex_MatchesReceivedPackets(bool ipv6)
{
using (var client = new Socket(SocketType.Dgram, ProtocolType.Udp))
Expand Down
1 change: 0 additions & 1 deletion src/libraries/tests.proj
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@

<!-- PSNE -->
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Console/tests/System.Console.Tests.csproj" />
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Net.NetworkInformation/tests/FunctionalTests/System.Net.NetworkInformation.Functional.Tests.csproj" />
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Net.Primitives/tests/FunctionalTests/System.Net.Primitives.Functional.Tests.csproj" />

<!-- Crashes on CI (possibly flakey) -->
Expand Down
3 changes: 3 additions & 0 deletions src/mono/cmake/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,9 @@
/* Have getifaddrs */
#cmakedefine HAVE_GETIFADDRS 1

/* Have struct ifaddrs */
#cmakedefine HAVE_IFADDRS 1

/* Have access */
#cmakedefine HAVE_ACCESS 1

Expand Down