Skip to content

Commit 2046cd7

Browse files
[Android] Fix NetworkInterface.GetAllNetworkInterfaces (dotnet#76370)
* 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]>
1 parent c92417c commit 2046cd7

File tree

2 files changed

+62
-9
lines changed

2 files changed

+62
-9
lines changed

src/libraries/Native/Unix/System.Native/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ include(${CMAKE_CURRENT_LIST_DIR}/extra_libs.cmake)
7979
set(NATIVE_LIBS_EXTRA)
8080
append_extra_system_libs(NATIVE_LIBS_EXTRA)
8181

82-
if (CLR_CMAKE_TARGET_ANDROID)
82+
if (CLR_CMAKE_TARGET_ANDROID AND NOT HAVE_GETIFADDRS)
83+
add_definitions(-DANDROID_GETIFADDRS_WORKAROUND)
84+
8385
add_compile_options(-Wno-gnu-zero-variadic-macro-arguments)
8486

8587
list (APPEND NATIVE_LIBS_EXTRA -llog)

src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@
1111
#include <stdlib.h>
1212
#include <sys/types.h>
1313
#include <assert.h>
14-
#if HAVE_GETIFADDRS && !defined(TARGET_ANDROID)
14+
#if HAVE_GETIFADDRS || defined(ANDROID_GETIFADDRS_WORKAROUND)
1515
#include <ifaddrs.h>
1616
#endif
17-
#ifdef TARGET_ANDROID
18-
#include "pal_ifaddrs.h"
17+
#ifdef ANDROID_GETIFADDRS_WORKAROUND
18+
#include <dlfcn.h>
19+
#include <pthread.h>
1920
#endif
2021
#include <net/if.h>
2122
#include <netinet/in.h>
@@ -100,12 +101,45 @@ static inline uint8_t mask2prefix(uint8_t* mask, int length)
100101
return len;
101102
}
102103

104+
#ifdef ANDROID_GETIFADDRS_WORKAROUND
105+
// Try to load the getifaddrs and freeifaddrs functions manually.
106+
// This workaround is necessary on Android prior to API 24 and it can be removed once
107+
// we drop support for earlier Android versions.
108+
static int (*getifaddrs)(struct ifaddrs**) = NULL;
109+
static void (*freeifaddrs)(struct ifaddrs*) = NULL;
110+
111+
static void try_loading_getifaddrs()
112+
{
113+
void *libc = dlopen("libc.so", RTLD_NOW);
114+
if (libc)
115+
{
116+
getifaddrs = (int (*)(struct ifaddrs**)) dlsym(libc, "getifaddrs");
117+
freeifaddrs = (void (*)(struct ifaddrs*)) dlsym(libc, "freeifaddrs");
118+
}
119+
}
120+
121+
static bool ensure_getifaddrs_is_loaded()
122+
{
123+
static pthread_once_t getifaddrs_is_loaded = PTHREAD_ONCE_INIT;
124+
pthread_once(&getifaddrs_is_loaded, try_loading_getifaddrs);
125+
return getifaddrs != NULL && freeifaddrs != NULL;
126+
}
127+
#endif
128+
103129
int32_t SystemNative_EnumerateInterfaceAddresses(void* context,
104130
IPv4AddressFound onIpv4Found,
105131
IPv6AddressFound onIpv6Found,
106132
LinkLayerAddressFound onLinkLayerFound)
107133
{
108-
#if HAVE_GETIFADDRS || defined(TARGET_ANDROID)
134+
#ifdef ANDROID_GETIFADDRS_WORKAROUND
135+
if (!ensure_getifaddrs_is_loaded())
136+
{
137+
errno = ENOTSUP;
138+
return -1;
139+
}
140+
#endif
141+
142+
#if HAVE_GETIFADDRS || defined(ANDROID_GETIFADDRS_WORKAROUND)
109143
struct ifaddrs* headAddr;
110144
if (getifaddrs(&headAddr) == -1)
111145
{
@@ -250,7 +284,15 @@ int32_t SystemNative_EnumerateInterfaceAddresses(void* context,
250284

251285
int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInterfaceInfo **interfaceList, int32_t * addressCount, IpAddressInfo **addressList )
252286
{
253-
#if HAVE_GETIFADDRS || defined(TARGET_ANDROID)
287+
#ifdef ANDROID_GETIFADDRS_WORKAROUND
288+
if (!ensure_getifaddrs_is_loaded())
289+
{
290+
errno = ENOTSUP;
291+
return -1;
292+
}
293+
#endif
294+
295+
#if HAVE_GETIFADDRS || defined(ANDROID_GETIFADDRS_WORKAROUND)
254296
struct ifaddrs* head; // Pointer to block allocated by getifaddrs().
255297
struct ifaddrs* ifaddrsEntry;
256298
IpAddressInfo *ai;
@@ -289,7 +331,16 @@ int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInter
289331
// To save allocation need for separate free() we will allocate one memory chunk
290332
// where we first write out NetworkInterfaceInfo entries immediately followed by
291333
// IpAddressInfo list.
292-
void * memoryBlock = calloc((size_t)count, sizeof(NetworkInterfaceInfo));
334+
#ifdef TARGET_ANDROID
335+
// Since Android API 30, getifaddrs returns only AF_INET and AF_INET6 addresses and we do not
336+
// get any AF_PACKET addresses and so count == ip4count + ip6count. We need to make sure that
337+
// the memoryBlock is large enough to hold all interfaces (up to `count` entries) and all
338+
// addresses (ip4count + ip6count) without any overlap between interfaceList and addressList.
339+
int entriesCount = count + ip4count + ip6count;
340+
#else
341+
int entriesCount = count;
342+
#endif
343+
void * memoryBlock = calloc((size_t)entriesCount, sizeof(NetworkInterfaceInfo));
293344
if (memoryBlock == NULL)
294345
{
295346
errno = ENOMEM;
@@ -300,7 +351,7 @@ int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInter
300351
ifaddrsEntry = head;
301352
*interfaceList = nii = (NetworkInterfaceInfo*)memoryBlock;
302353
// address of first IpAddressInfo after all NetworkInterfaceInfo entries.
303-
*addressList = ai = (IpAddressInfo*)(nii + (count - ip4count - ip6count));
354+
*addressList = ai = (IpAddressInfo*)(nii + (entriesCount - ip4count - ip6count));
304355

305356
while (ifaddrsEntry != NULL)
306357
{
@@ -324,7 +375,7 @@ int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInter
324375
memcpy(nii->Name, ifaddrsEntry->ifa_name, sizeof(nii->Name));
325376
nii->InterfaceIndex = if_nametoindex(ifaddrsEntry->ifa_name);
326377
nii->Speed = -1;
327-
nii->HardwareType = NetworkInterfaceType_Unknown;
378+
nii->HardwareType = ((ifaddrsEntry->ifa_flags & IFF_LOOPBACK) == IFF_LOOPBACK) ? NetworkInterfaceType_Loopback : NetworkInterfaceType_Unknown;
328379

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

0 commit comments

Comments
 (0)