From 2046cd7ea0a4e0a0db9c3a3342572f8675ed5dc1 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 3 Oct 2022 15:11:02 +0200 Subject: [PATCH 1/2] [Android] Fix NetworkInterface.GetAllNetworkInterfaces (#76370) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Revert "[Android] Port getifaddrs implementation from Xamarin.Android (#71943)" This reverts commit 1de4a5ca073da24211fc4d427b2224da2212f372. * 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 * 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 --- .../Native/Unix/System.Native/CMakeLists.txt | 4 +- .../System.Native/pal_interfaceaddresses.c | 67 ++++++++++++++++--- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Native/CMakeLists.txt index 613ce0f1e7b64..9e490e9f0e98e 100644 --- a/src/libraries/Native/Unix/System.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Native/CMakeLists.txt @@ -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) diff --git a/src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c b/src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c index 22165b6520688..0a92305bcbd48 100644 --- a/src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c +++ b/src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c @@ -11,11 +11,12 @@ #include #include #include -#if HAVE_GETIFADDRS && !defined(TARGET_ANDROID) +#if HAVE_GETIFADDRS || defined(ANDROID_GETIFADDRS_WORKAROUND) #include #endif -#ifdef TARGET_ANDROID -#include "pal_ifaddrs.h" +#ifdef ANDROID_GETIFADDRS_WORKAROUND +#include +#include #endif #include #include @@ -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) { @@ -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; @@ -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; @@ -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) { @@ -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) From b47b3d2074d1ff8e2e8f504ea9d8a34c9da6ea7b Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 3 Oct 2022 21:52:54 +0200 Subject: [PATCH 2/2] [Android] Fix NetworkInterface.GetAllNetworkInterfaces on API 21-23 (#76541) * Bring back pal_ifaddrs * Update the header file --- .../Native/Unix/System.Native/CMakeLists.txt | 1 - .../Native/Unix/System.Native/pal_ifaddrs.c | 18 ++++----- .../Native/Unix/System.Native/pal_ifaddrs.h | 38 ++++--------------- .../System.Native/pal_interfaceaddresses.c | 26 +++++++++---- 4 files changed, 36 insertions(+), 47 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Native/CMakeLists.txt index 9e490e9f0e98e..1b06d83880533 100644 --- a/src/libraries/Native/Unix/System.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Native/CMakeLists.txt @@ -81,7 +81,6 @@ append_extra_system_libs(NATIVE_LIBS_EXTRA) 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) diff --git a/src/libraries/Native/Unix/System.Native/pal_ifaddrs.c b/src/libraries/Native/Unix/System.Native/pal_ifaddrs.c index 9ef4ca3abda4e..b49edd513280e 100644 --- a/src/libraries/Native/Unix/System.Native/pal_ifaddrs.c +++ b/src/libraries/Native/Unix/System.Native/pal_ifaddrs.c @@ -312,7 +312,7 @@ static struct ifaddrs *get_link_info(struct nlmsghdr *message) break; case IFLA_BROADCAST: - LOG_DEBUG(" interface broadcast (%lu bytes)\n", RTA_PAYLOAD(attribute)); + LOG_DEBUG(" interface broadcast (%zu bytes)\n", RTA_PAYLOAD(attribute)); if (fill_ll_address(&sa, net_interface, RTA_DATA(attribute), RTA_PAYLOAD(attribute)) < 0) { goto error; } @@ -320,7 +320,7 @@ static struct ifaddrs *get_link_info(struct nlmsghdr *message) break; case IFLA_ADDRESS: - LOG_DEBUG(" interface address (%lu bytes)\n", RTA_PAYLOAD(attribute)); + LOG_DEBUG(" interface address (%zu bytes)\n", RTA_PAYLOAD(attribute)); if (fill_ll_address(&sa, net_interface, RTA_DATA(attribute), RTA_PAYLOAD(attribute)) < 0) { goto error; } @@ -352,7 +352,7 @@ static struct ifaddrs *find_interface_by_index(int index, struct ifaddrs **ifadd return NULL; /* Normally expensive, but with the small amount of links in the chain we'll deal with it's not - * worth the extra houskeeping and memory overhead + * worth the extra housekeeping and memory overhead */ cur = *ifaddrs_head; while (cur) { @@ -509,7 +509,7 @@ static struct ifaddrs *get_link_address(struct nlmsghdr *message, struct ifaddrs LOG_DEBUG(" attribute type: LOCAL"); if (ifa->ifa_addr) { /* P2P protocol, set the dst/broadcast address union from the original address. - * Since ifa_addr is set it means IFA_ADDRESS occured earlier and that address + * Since ifa_addr is set it means IFA_ADDRESS occurred earlier and that address * is indeed the P2P destination one. */ ifa->ifa_dstaddr = ifa->ifa_addr; @@ -531,7 +531,7 @@ static struct ifaddrs *get_link_address(struct nlmsghdr *message, struct ifaddrs case IFA_ADDRESS: LOG_DEBUG(" attribute type: ADDRESS"); if (ifa->ifa_addr) { - /* Apparently IFA_LOCAL occured earlier and we have a P2P connection + /* Apparently IFA_LOCAL occurred earlier and we have a P2P connection * here. IFA_LOCAL carries the destination address, move it there */ ifa->ifa_dstaddr = ifa->ifa_addr; @@ -570,7 +570,7 @@ static struct ifaddrs *get_link_address(struct nlmsghdr *message, struct ifaddrs attribute = RTA_NEXT(attribute, length); } - /* glibc stores the associated interface name in the address if IFA_LABEL never occured */ + /* glibc stores the associated interface name in the address if IFA_LABEL never occurred */ if (!ifa->ifa_name) { char *name = get_interface_name_by_index((int)(net_address->ifa_index), ifaddrs_head); LOG_DEBUG(" address has no name/label, getting one from interface\n"); @@ -708,7 +708,7 @@ static int parse_netlink_reply(struct netlink_session *session, struct ifaddrs * return ret; } -int getifaddrs(struct ifaddrs **ifap) +int _netlink_getifaddrs(struct ifaddrs **ifap) { int ret = -1; @@ -728,7 +728,7 @@ int getifaddrs(struct ifaddrs **ifap) (parse_netlink_reply(&session, &ifaddrs_head, &last_ifaddr) < 0) || (send_netlink_dump_request(&session, RTM_GETADDR) < 0) || (parse_netlink_reply(&session, &ifaddrs_head, &last_ifaddr) < 0)) { - freeifaddrs (ifaddrs_head); + _netlink_freeifaddrs (ifaddrs_head); goto cleanup; } @@ -744,7 +744,7 @@ int getifaddrs(struct ifaddrs **ifap) return ret; } -void freeifaddrs(struct ifaddrs *ifa) +void _netlink_freeifaddrs(struct ifaddrs *ifa) { struct ifaddrs *cur, *next; diff --git a/src/libraries/Native/Unix/System.Native/pal_ifaddrs.h b/src/libraries/Native/Unix/System.Native/pal_ifaddrs.h index e52a392b13d9d..0944eeb43eb9f 100644 --- a/src/libraries/Native/Unix/System.Native/pal_ifaddrs.h +++ b/src/libraries/Native/Unix/System.Native/pal_ifaddrs.h @@ -7,37 +7,15 @@ #error The pal_ifaddrs.h shim is intended only for Android #endif +#if __ANDROID_API__ >= 24 +#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 +#endif + // Android doesn't include the getifaddrs and freeifaddrs functions in older Bionic libc (pre API 24). -// In recent Android versions (Android 11+) the data returned by the getifaddrs function is not valid. // This shim is a port of Xamarin Android's implementation of getifaddrs using Netlink. +// https://github.com/xamarin/xamarin-android/blob/681887ebdbd192ce7ce1cd02221d4939599ba762/src/monodroid/jni/xamarin_getifaddrs.h -#include "pal_compiler.h" -#include "pal_config.h" -#include "pal_types.h" - -#include -#include -#include - -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; -}; - -// Synonym for `ifa_ifu.ifu_broadaddr` in `struct ifaddrs`. -#define ifa_broadaddr ifa_ifu.ifu_broadaddr -// Synonym for `ifa_ifu.ifu_dstaddr` in `struct ifaddrs`. -#define ifa_dstaddr ifa_ifu.ifu_dstaddr +#include -int getifaddrs (struct ifaddrs **ifap); -void freeifaddrs (struct ifaddrs *ifap); +int _netlink_getifaddrs (struct ifaddrs **ifap); +void _netlink_freeifaddrs (struct ifaddrs *ifap); diff --git a/src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c b/src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c index 0a92305bcbd48..506127d641156 100644 --- a/src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c +++ b/src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c @@ -17,6 +17,7 @@ #ifdef ANDROID_GETIFADDRS_WORKAROUND #include #include +#include "pal_ifaddrs.h" // fallback for Android API 21-23 #endif #include #include @@ -102,19 +103,30 @@ static inline uint8_t mask2prefix(uint8_t* mask, int length) } #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. +// This workaround is necessary as long as we support Android API 21-23 and it can be removed once +// we drop support for these old 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) + if (android_get_device_api_level() >= 24) { - getifaddrs = (int (*)(struct ifaddrs**)) dlsym(libc, "getifaddrs"); - freeifaddrs = (void (*)(struct ifaddrs*)) dlsym(libc, "freeifaddrs"); + // Bionic on API 24+ contains the getifaddrs/freeifaddrs functions but the NDK doesn't expose those functions + // in ifaddrs.h when the minimum supported SDK is lower than 24 and therefore we need to load them manually + void *libc = dlopen("libc.so", RTLD_NOW); + if (libc) + { + getifaddrs = (int (*)(struct ifaddrs**)) dlsym(libc, "getifaddrs"); + freeifaddrs = (void (*)(struct ifaddrs*)) dlsym(libc, "freeifaddrs"); + } + } + else + { + // Bionic on API 21-23 doesn't contain the implementation of getifaddrs/freeifaddrs at all + // and we need to reimplement it using netlink (see pal_ifaddrs) + getifaddrs = _netlink_getifaddrs; + freeifaddrs = _netlink_freeifaddrs; } }