Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release/6.0-maui] [Android] Fix accessing network interfaces information #63628

Merged
Show file tree
Hide file tree
Changes from all commits
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
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 @@ -123,9 +123,11 @@ public virtual void CopyTo(System.Net.NetworkInformation.IPAddressInformation[]
public abstract partial class IPGlobalProperties
{
protected IPGlobalProperties() { }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract string DhcpScopeName { get; }
public abstract string DomainName { get; }
public abstract string HostName { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract bool IsWinsProxy { get; }
public abstract System.Net.NetworkInformation.NetBiosNodeType NodeType { get; }
public virtual System.IAsyncResult BeginGetUnicastAddresses(System.AsyncCallback? callback, object? state) { throw null; }
Expand All @@ -136,58 +138,92 @@ protected IPGlobalProperties() { }
public abstract System.Net.IPEndPoint[] GetActiveTcpListeners();
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract System.Net.IPEndPoint[] GetActiveUdpListeners();
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract System.Net.NetworkInformation.IcmpV4Statistics GetIcmpV4Statistics();
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract System.Net.NetworkInformation.IcmpV6Statistics GetIcmpV6Statistics();
[System.Runtime.Versioning.UnsupportedOSPlatform("illumos")]
[System.Runtime.Versioning.UnsupportedOSPlatform("solaris")]
public static System.Net.NetworkInformation.IPGlobalProperties GetIPGlobalProperties() { throw null; }
public abstract System.Net.NetworkInformation.IPGlobalStatistics GetIPv4GlobalStatistics();
public abstract System.Net.NetworkInformation.IPGlobalStatistics GetIPv6GlobalStatistics();
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract System.Net.NetworkInformation.TcpStatistics GetTcpIPv4Statistics();
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract System.Net.NetworkInformation.TcpStatistics GetTcpIPv6Statistics();
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract System.Net.NetworkInformation.UdpStatistics GetUdpIPv4Statistics();
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract System.Net.NetworkInformation.UdpStatistics GetUdpIPv6Statistics();
public virtual System.Net.NetworkInformation.UnicastIPAddressInformationCollection GetUnicastAddresses() { throw null; }
public virtual System.Threading.Tasks.Task<System.Net.NetworkInformation.UnicastIPAddressInformationCollection> GetUnicastAddressesAsync() { throw null; }
}
public abstract partial class IPGlobalStatistics
{
protected IPGlobalStatistics() { }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract int DefaultTtl { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract bool ForwardingEnabled { get; }
public abstract int NumberOfInterfaces { get; }
public abstract int NumberOfIPAddresses { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract int NumberOfRoutes { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long OutputPacketRequests { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long OutputPacketRoutingDiscards { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long OutputPacketsDiscarded { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long OutputPacketsWithNoRoute { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long PacketFragmentFailures { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long PacketReassembliesRequired { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long PacketReassemblyFailures { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long PacketReassemblyTimeout { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long PacketsFragmented { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long PacketsReassembled { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long ReceivedPackets { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long ReceivedPacketsDelivered { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long ReceivedPacketsDiscarded { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long ReceivedPacketsForwarded { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long ReceivedPacketsWithAddressErrors { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long ReceivedPacketsWithHeadersErrors { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract long ReceivedPacketsWithUnknownProtocol { get; }
}
public abstract partial class IPInterfaceProperties
{
protected IPInterfaceProperties() { }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract System.Net.NetworkInformation.IPAddressInformationCollection AnycastAddresses { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract System.Net.NetworkInformation.IPAddressCollection DhcpServerAddresses { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract System.Net.NetworkInformation.IPAddressCollection DnsAddresses { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract string DnsSuffix { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract System.Net.NetworkInformation.GatewayIPAddressInformationCollection GatewayAddresses { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract bool IsDnsEnabled { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract bool IsDynamicDnsEnabled { get; }
public abstract System.Net.NetworkInformation.MulticastIPAddressInformationCollection MulticastAddresses { get; }
public abstract System.Net.NetworkInformation.UnicastIPAddressInformationCollection UnicastAddresses { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract System.Net.NetworkInformation.IPAddressCollection WinsServersAddresses { get; }
public abstract System.Net.NetworkInformation.IPv4InterfaceProperties GetIPv4Properties();
public abstract System.Net.NetworkInformation.IPv6InterfaceProperties GetIPv6Properties();
Expand All @@ -212,11 +248,16 @@ public abstract partial class IPv4InterfaceProperties
{
protected IPv4InterfaceProperties() { }
public abstract int Index { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract bool IsAutomaticPrivateAddressingActive { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract bool IsAutomaticPrivateAddressingEnabled { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract bool IsDhcpEnabled { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract bool IsForwardingEnabled { get; }
public abstract int Mtu { get; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public abstract bool UsesWins { get; }
}
public abstract partial class IPv4InterfaceStatistics
Expand Down Expand Up @@ -316,7 +357,9 @@ protected NetworkInterface() { }
[System.Runtime.Versioning.UnsupportedOSPlatform("solaris")]
public static System.Net.NetworkInformation.NetworkInterface[] GetAllNetworkInterfaces() { throw null; }
public virtual System.Net.NetworkInformation.IPInterfaceProperties GetIPProperties() { throw null; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public virtual System.Net.NetworkInformation.IPInterfaceStatistics GetIPStatistics() { throw null; }
[System.Runtime.Versioning.UnsupportedOSPlatform("android")]
public virtual System.Net.NetworkInformation.IPv4InterfaceStatistics GetIPv4Statistics() { throw null; }
[System.Runtime.Versioning.UnsupportedOSPlatform("illumos")]
[System.Runtime.Versioning.UnsupportedOSPlatform("solaris")]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Linux;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)-FreeBSD;$(NetCoreAppCurrent)-illumos;$(NetCoreAppCurrent)-Solaris;$(NetCoreAppCurrent)</TargetFrameworks>
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Linux;$(NetCoreAppCurrent)-Android;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)-FreeBSD;$(NetCoreAppCurrent)-illumos;$(NetCoreAppCurrent)-Solaris;$(NetCoreAppCurrent)</TargetFrameworks>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
Expand Down Expand Up @@ -118,8 +118,8 @@
<Compile Include="$(CommonPath)Interop\Unix\Interop.Errors.cs" Link="Common\Interop\CoreLib\Unix\Interop.Errors.cs" />
<Compile Include="$(CommonPath)System\IO\RowConfigReader.cs" Link="Common\System\IO\RowConfigReader.cs" />
</ItemGroup>
<!-- Linux -->
<ItemGroup Condition="'$(TargetsLinux)' == 'true'">
<!-- Linux (other than Android) -->
<ItemGroup Condition="'$(TargetsLinux)' == 'true' and '$(TargetsAndroid)' != 'true'">
<Compile Include="System\Net\NetworkInformation\ExceptionHelper.Linux.cs" />
<Compile Include="System\Net\NetworkInformation\LinuxIcmpV4Statistics.cs" />
<Compile Include="System\Net\NetworkInformation\LinuxIcmpV6Statistics.cs" />
Expand All @@ -144,6 +144,17 @@
<Compile Include="$(CommonPath)System\IO\StringParser.cs" Link="Common\System\IO\StringParser.cs" />
<Compile Include="$(CommonPath)Interop\Linux\Interop.LinuxNetDeviceFlags.cs" Link="Common\Interop\Linux\Interop.LinuxNetDeviceFlags.cs" />
</ItemGroup>
<!-- Android -->
<ItemGroup Condition="'$(TargetsAndroid)' == 'true'">
<Compile Include="System\Net\NetworkInformation\IPGlobalPropertiesPal.Android.cs" />
<Compile Include="System\Net\NetworkInformation\AndroidIPGlobalProperties.cs" />
<Compile Include="System\Net\NetworkInformation\AndroidIPGlobalStatistics.cs" />
<Compile Include="System\Net\NetworkInformation\AndroidIPInterfaceProperties.cs" />
<Compile Include="System\Net\NetworkInformation\AndroidIPv4InterfaceProperties.cs" />
<Compile Include="System\Net\NetworkInformation\AndroidIPv6InterfaceProperties.cs" />
<Compile Include="System\Net\NetworkInformation\NetworkInterfacePal.Android.cs" />
<Compile Include="System\Net\NetworkInformation\AndroidNetworkInterface.cs" />
</ItemGroup>
<!-- OSX -->
<ItemGroup Condition="'$(TargetsOSX)' == 'true' or '$(TargetsiOS)' == 'true' or '$(TargetstvOS)' == 'true' or '$(TargetsFreeBSD)' == 'true'">
<Compile Include="System\Net\NetworkInformation\IPGlobalPropertiesPal.Bsd.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Net.NetworkInformation
{
internal sealed class AndroidIPGlobalProperties : UnixIPGlobalProperties
{
public override TcpConnectionInformation[] GetActiveTcpConnections() => throw new PlatformNotSupportedException();

public override IPEndPoint[] GetActiveTcpListeners() => throw new PlatformNotSupportedException();

public override IPEndPoint[] GetActiveUdpListeners() => throw new PlatformNotSupportedException();

public override IcmpV4Statistics GetIcmpV4Statistics() => throw new PlatformNotSupportedException();

public override IcmpV6Statistics GetIcmpV6Statistics() => throw new PlatformNotSupportedException();

public override IPGlobalStatistics GetIPv4GlobalStatistics()
=> new AndroidIPGlobalStatistics(ipv4: true);

public override IPGlobalStatistics GetIPv6GlobalStatistics()
=> new AndroidIPGlobalStatistics(ipv4: false);

public override TcpStatistics GetTcpIPv4Statistics() => throw new PlatformNotSupportedException();

public override TcpStatistics GetTcpIPv6Statistics() => throw new PlatformNotSupportedException();

public override UdpStatistics GetUdpIPv4Statistics() => throw new PlatformNotSupportedException();

public override UdpStatistics GetUdpIPv6Statistics() => throw new PlatformNotSupportedException();
}
}
Loading