Skip to content

Commit

Permalink
[Android] Fix NetworkInterface.GetAllNetworkInterfaces (dotnet#76370)
Browse files Browse the repository at this point in the history
* Revert "[Android] Port getifaddrs implementation from Xamarin.Android (dotnet#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 <[email protected]>

* 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 <[email protected]>
  • Loading branch information
simonrozsival and akoeplinger committed Oct 20, 2022
1 parent c92417c commit 2046cd7
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 9 deletions.
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

0 comments on commit 2046cd7

Please sign in to comment.