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 NetworkInterface.GetAllNetworkInterfaces (#76370)
* Revert "[Android] Port getifaddrs implementation from Xamarin.Android (#71943)"

This reverts commit 1de4a5c.

* Fix allocating memory block for interfaces and addresses on recent Android SDKs

* Detect loopback interface on Android

* Add comment with explanation

* Simplify the changes to be closer to the original code

* Fix build

* Fix typos

Co-authored-by: Alexander Köplinger <alex.koeplinger@outlook.com>

* Improve comment

* Indent using spaces instead of tabs

* Remove check for ifaddrs.h

* Add ANDROID_GETIFADDRS_WORKAROUND

* Update comment

Co-authored-by: Alexander Köplinger <alex.koeplinger@outlook.com>
  • Loading branch information
simonrozsival and akoeplinger committed Oct 20, 2022
commit 2046cd7ea0a4e0a0db9c3a3342572f8675ed5dc1
4 changes: 3 additions & 1 deletion src/libraries/Native/Unix/System.Native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ include(${CMAKE_CURRENT_LIST_DIR}/extra_libs.cmake)
set(NATIVE_LIBS_EXTRA)
append_extra_system_libs(NATIVE_LIBS_EXTRA)

if (CLR_CMAKE_TARGET_ANDROID)
if (CLR_CMAKE_TARGET_ANDROID AND NOT HAVE_GETIFADDRS)
add_definitions(-DANDROID_GETIFADDRS_WORKAROUND)

add_compile_options(-Wno-gnu-zero-variadic-macro-arguments)

list (APPEND NATIVE_LIBS_EXTRA -llog)
Expand Down
67 changes: 59 additions & 8 deletions src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
#include <stdlib.h>
#include <sys/types.h>
#include <assert.h>
#if HAVE_GETIFADDRS && !defined(TARGET_ANDROID)
#if HAVE_GETIFADDRS || defined(ANDROID_GETIFADDRS_WORKAROUND)
#include <ifaddrs.h>
#endif
#ifdef TARGET_ANDROID
#include "pal_ifaddrs.h"
#ifdef ANDROID_GETIFADDRS_WORKAROUND
#include <dlfcn.h>
#include <pthread.h>
#endif
#include <net/if.h>
#include <netinet/in.h>
Expand Down Expand Up @@ -100,12 +101,45 @@ static inline uint8_t mask2prefix(uint8_t* mask, int length)
return len;
}

#ifdef ANDROID_GETIFADDRS_WORKAROUND
// 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 || defined(TARGET_ANDROID)
#ifdef ANDROID_GETIFADDRS_WORKAROUND
if (!ensure_getifaddrs_is_loaded())
{
errno = ENOTSUP;
return -1;
}
#endif

#if HAVE_GETIFADDRS || defined(ANDROID_GETIFADDRS_WORKAROUND)
struct ifaddrs* headAddr;
if (getifaddrs(&headAddr) == -1)
{
Expand Down Expand Up @@ -250,7 +284,15 @@ int32_t SystemNative_EnumerateInterfaceAddresses(void* context,

int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInterfaceInfo **interfaceList, int32_t * addressCount, IpAddressInfo **addressList )
{
#if HAVE_GETIFADDRS || defined(TARGET_ANDROID)
#ifdef ANDROID_GETIFADDRS_WORKAROUND
if (!ensure_getifaddrs_is_loaded())
{
errno = ENOTSUP;
return -1;
}
#endif

#if HAVE_GETIFADDRS || defined(ANDROID_GETIFADDRS_WORKAROUND)
struct ifaddrs* head; // Pointer to block allocated by getifaddrs().
struct ifaddrs* ifaddrsEntry;
IpAddressInfo *ai;
Expand Down Expand Up @@ -289,7 +331,16 @@ int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInter
// To save allocation need for separate free() we will allocate one memory chunk
// where we first write out NetworkInterfaceInfo entries immediately followed by
// IpAddressInfo list.
void * memoryBlock = calloc((size_t)count, sizeof(NetworkInterfaceInfo));
#ifdef TARGET_ANDROID
// Since Android API 30, getifaddrs returns only AF_INET and AF_INET6 addresses and we do not
// get any AF_PACKET addresses and so count == ip4count + ip6count. We need to make sure that
// the memoryBlock is large enough to hold all interfaces (up to `count` entries) and all
// addresses (ip4count + ip6count) without any overlap between interfaceList and addressList.
int entriesCount = count + ip4count + ip6count;
#else
int entriesCount = count;
#endif
void * memoryBlock = calloc((size_t)entriesCount, sizeof(NetworkInterfaceInfo));
if (memoryBlock == NULL)
{
errno = ENOMEM;
Expand All @@ -300,7 +351,7 @@ int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInter
ifaddrsEntry = head;
*interfaceList = nii = (NetworkInterfaceInfo*)memoryBlock;
// address of first IpAddressInfo after all NetworkInterfaceInfo entries.
*addressList = ai = (IpAddressInfo*)(nii + (count - ip4count - ip6count));
*addressList = ai = (IpAddressInfo*)(nii + (entriesCount - ip4count - ip6count));

while (ifaddrsEntry != NULL)
{
Expand All @@ -324,7 +375,7 @@ int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInter
memcpy(nii->Name, ifaddrsEntry->ifa_name, sizeof(nii->Name));
nii->InterfaceIndex = if_nametoindex(ifaddrsEntry->ifa_name);
nii->Speed = -1;
nii->HardwareType = NetworkInterfaceType_Unknown;
nii->HardwareType = ((ifaddrsEntry->ifa_flags & IFF_LOOPBACK) == IFF_LOOPBACK) ? NetworkInterfaceType_Loopback : NetworkInterfaceType_Unknown;

// Get operational state and multicast support.
if ((ifaddrsEntry->ifa_flags & (IFF_MULTICAST|IFF_ALLMULTI)) != 0)
Expand Down