Skip to content

Commit 450b8f4

Browse files
[release/7.0][Android] Backport fix NetworkInterface.GetAllNetworkInterfaces (#76565)
* [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 <[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]> * [Android] Fix NetworkInterface.GetAllNetworkInterfaces on API 21-23 (#76541) * Bring back pal_ifaddrs * Update the header file Co-authored-by: Alexander Köplinger <[email protected]>
1 parent 8743673 commit 450b8f4

File tree

4 files changed

+84
-42
lines changed

4 files changed

+84
-42
lines changed

src/native/libs/System.Native/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ include(${CMAKE_CURRENT_LIST_DIR}/extra_libs.cmake)
8383
set(NATIVE_LIBS_EXTRA)
8484
append_extra_system_libs(NATIVE_LIBS_EXTRA)
8585

86-
if (CLR_CMAKE_TARGET_ANDROID)
86+
if (CLR_CMAKE_TARGET_ANDROID AND NOT HAVE_GETIFADDRS)
87+
add_definitions(-DANDROID_GETIFADDRS_WORKAROUND)
8788
add_compile_options(-Wno-gnu-zero-variadic-macro-arguments)
8889

8990
list (APPEND NATIVE_LIBS_EXTRA -llog)

src/native/libs/System.Native/pal_ifaddrs.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,7 @@ static int parse_netlink_reply(struct netlink_session *session, struct ifaddrs *
708708
return ret;
709709
}
710710

711-
int getifaddrs(struct ifaddrs **ifap)
711+
int _netlink_getifaddrs(struct ifaddrs **ifap)
712712
{
713713
int ret = -1;
714714

@@ -728,7 +728,7 @@ int getifaddrs(struct ifaddrs **ifap)
728728
(parse_netlink_reply(&session, &ifaddrs_head, &last_ifaddr) < 0) ||
729729
(send_netlink_dump_request(&session, RTM_GETADDR) < 0) ||
730730
(parse_netlink_reply(&session, &ifaddrs_head, &last_ifaddr) < 0)) {
731-
freeifaddrs (ifaddrs_head);
731+
_netlink_freeifaddrs (ifaddrs_head);
732732
goto cleanup;
733733
}
734734

@@ -744,7 +744,7 @@ int getifaddrs(struct ifaddrs **ifap)
744744
return ret;
745745
}
746746

747-
void freeifaddrs(struct ifaddrs *ifa)
747+
void _netlink_freeifaddrs(struct ifaddrs *ifa)
748748
{
749749
struct ifaddrs *cur, *next;
750750

src/native/libs/System.Native/pal_ifaddrs.h

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,15 @@
77
#error The pal_ifaddrs.h shim is intended only for Android
88
#endif
99

10+
#if __ANDROID_API__ >= 24
11+
#error The pal_ifaddrs.h shim is only necessary for Android API 21-23 and it should be removed now that the minimum supported API level is 24 or higher
12+
#endif
13+
1014
// Android doesn't include the getifaddrs and freeifaddrs functions in older Bionic libc (pre API 24).
11-
// In recent Android versions (Android 11+) the data returned by the getifaddrs function is not valid.
1215
// This shim is a port of Xamarin Android's implementation of getifaddrs using Netlink.
16+
// https://github.com/xamarin/xamarin-android/blob/681887ebdbd192ce7ce1cd02221d4939599ba762/src/monodroid/jni/xamarin_getifaddrs.h
1317

14-
#include "pal_compiler.h"
15-
#include "pal_config.h"
16-
#include "pal_types.h"
17-
18-
#include <sys/cdefs.h>
19-
#include <netinet/in.h>
20-
#include <sys/socket.h>
21-
22-
struct ifaddrs
23-
{
24-
struct ifaddrs *ifa_next;
25-
char *ifa_name;
26-
unsigned int ifa_flags;
27-
struct sockaddr *ifa_addr;
28-
struct sockaddr *ifa_netmask;
29-
union
30-
{
31-
struct sockaddr *ifu_broadaddr;
32-
struct sockaddr *ifu_dstaddr;
33-
} ifa_ifu;
34-
void *ifa_data;
35-
};
36-
37-
// Synonym for `ifa_ifu.ifu_broadaddr` in `struct ifaddrs`.
38-
#define ifa_broadaddr ifa_ifu.ifu_broadaddr
39-
// Synonym for `ifa_ifu.ifu_dstaddr` in `struct ifaddrs`.
40-
#define ifa_dstaddr ifa_ifu.ifu_dstaddr
18+
#include <ifaddrs.h>
4119

42-
int getifaddrs (struct ifaddrs **ifap);
43-
void freeifaddrs (struct ifaddrs *ifap);
20+
int _netlink_getifaddrs (struct ifaddrs **ifap);
21+
void _netlink_freeifaddrs (struct ifaddrs *ifap);

src/native/libs/System.Native/pal_interfaceaddresses.c

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
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>
20+
#include "pal_ifaddrs.h" // fallback for Android API 21-23
1921
#endif
2022
#include <net/if.h>
2123
#include <netinet/in.h>
@@ -100,12 +102,56 @@ static inline uint8_t mask2prefix(uint8_t* mask, int length)
100102
return len;
101103
}
102104

105+
#ifdef ANDROID_GETIFADDRS_WORKAROUND
106+
// This workaround is necessary as long as we support Android API 21-23 and it can be removed once
107+
// we drop support for these old 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+
if (android_get_device_api_level() >= 24)
114+
{
115+
// Bionic on API 24+ contains the getifaddrs/freeifaddrs functions but the NDK doesn't expose those functions
116+
// in ifaddrs.h when the minimum supported SDK is lower than 24 and therefore we need to load them manually
117+
void *libc = dlopen("libc.so", RTLD_NOW);
118+
if (libc)
119+
{
120+
getifaddrs = (int (*)(struct ifaddrs**)) dlsym(libc, "getifaddrs");
121+
freeifaddrs = (void (*)(struct ifaddrs*)) dlsym(libc, "freeifaddrs");
122+
}
123+
}
124+
else
125+
{
126+
// Bionic on API 21-23 doesn't contain the implementation of getifaddrs/freeifaddrs at all
127+
// and we need to reimplement it using netlink (see pal_ifaddrs)
128+
getifaddrs = _netlink_getifaddrs;
129+
freeifaddrs = _netlink_freeifaddrs;
130+
}
131+
}
132+
133+
static bool ensure_getifaddrs_is_loaded()
134+
{
135+
static pthread_once_t getifaddrs_is_loaded = PTHREAD_ONCE_INIT;
136+
pthread_once(&getifaddrs_is_loaded, try_loading_getifaddrs);
137+
return getifaddrs != NULL && freeifaddrs != NULL;
138+
}
139+
#endif
140+
103141
int32_t SystemNative_EnumerateInterfaceAddresses(void* context,
104142
IPv4AddressFound onIpv4Found,
105143
IPv6AddressFound onIpv6Found,
106144
LinkLayerAddressFound onLinkLayerFound)
107145
{
108-
#if HAVE_GETIFADDRS || defined(TARGET_ANDROID)
146+
#ifdef ANDROID_GETIFADDRS_WORKAROUND
147+
if (!ensure_getifaddrs_is_loaded())
148+
{
149+
errno = ENOTSUP;
150+
return -1;
151+
}
152+
#endif
153+
154+
#if HAVE_GETIFADDRS || defined(ANDROID_GETIFADDRS_WORKAROUND)
109155
struct ifaddrs* headAddr;
110156
if (getifaddrs(&headAddr) == -1)
111157
{
@@ -250,7 +296,15 @@ int32_t SystemNative_EnumerateInterfaceAddresses(void* context,
250296

251297
int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInterfaceInfo **interfaceList, int32_t * addressCount, IpAddressInfo **addressList )
252298
{
253-
#if HAVE_GETIFADDRS || defined(TARGET_ANDROID)
299+
#ifdef ANDROID_GETIFADDRS_WORKAROUND
300+
if (!ensure_getifaddrs_is_loaded())
301+
{
302+
errno = ENOTSUP;
303+
return -1;
304+
}
305+
#endif
306+
307+
#if HAVE_GETIFADDRS || defined(ANDROID_GETIFADDRS_WORKAROUND)
254308
struct ifaddrs* head; // Pointer to block allocated by getifaddrs().
255309
struct ifaddrs* ifaddrsEntry;
256310
IpAddressInfo *ai;
@@ -289,7 +343,16 @@ int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInter
289343
// To save allocation need for separate free() we will allocate one memory chunk
290344
// where we first write out NetworkInterfaceInfo entries immediately followed by
291345
// IpAddressInfo list.
292-
void * memoryBlock = calloc((size_t)count, sizeof(NetworkInterfaceInfo));
346+
#ifdef TARGET_ANDROID
347+
// Since Android API 30, getifaddrs returns only AF_INET and AF_INET6 addresses and we do not
348+
// get any AF_PACKET addresses and so count == ip4count + ip6count. We need to make sure that
349+
// the memoryBlock is large enough to hold all interfaces (up to `count` entries) and all
350+
// addresses (ip4count + ip6count) without any overlap between interfaceList and addressList.
351+
int entriesCount = count + ip4count + ip6count;
352+
#else
353+
int entriesCount = count;
354+
#endif
355+
void * memoryBlock = calloc((size_t)entriesCount, sizeof(NetworkInterfaceInfo));
293356
if (memoryBlock == NULL)
294357
{
295358
errno = ENOMEM;
@@ -300,7 +363,7 @@ int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInter
300363
ifaddrsEntry = head;
301364
*interfaceList = nii = (NetworkInterfaceInfo*)memoryBlock;
302365
// address of first IpAddressInfo after all NetworkInterfaceInfo entries.
303-
*addressList = ai = (IpAddressInfo*)(nii + (count - ip4count - ip6count));
366+
*addressList = ai = (IpAddressInfo*)(nii + (entriesCount - ip4count - ip6count));
304367

305368
while (ifaddrsEntry != NULL)
306369
{
@@ -324,7 +387,7 @@ int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInter
324387
memcpy(nii->Name, ifaddrsEntry->ifa_name, sizeof(nii->Name));
325388
nii->InterfaceIndex = if_nametoindex(ifaddrsEntry->ifa_name);
326389
nii->Speed = -1;
327-
nii->HardwareType = NetworkInterfaceType_Unknown;
390+
nii->HardwareType = ((ifaddrsEntry->ifa_flags & IFF_LOOPBACK) == IFF_LOOPBACK) ? NetworkInterfaceType_Loopback : NetworkInterfaceType_Unknown;
328391

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

0 commit comments

Comments
 (0)