diff --git a/src/libraries/Native/Unix/System.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Native/CMakeLists.txt index 613ce0f1e7b64..1b06d83880533 100644 --- a/src/libraries/Native/Unix/System.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Native/CMakeLists.txt @@ -79,7 +79,8 @@ 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_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 22165b6520688..506127d641156 100644 --- a/src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c +++ b/src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c @@ -11,11 +11,13 @@ #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 +#include "pal_ifaddrs.h" // fallback for Android API 21-23 #endif #include #include @@ -100,12 +102,56 @@ static inline uint8_t mask2prefix(uint8_t* mask, int length) return len; } +#ifdef ANDROID_GETIFADDRS_WORKAROUND +// 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() +{ + if (android_get_device_api_level() >= 24) + { + // 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; + } +} + +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 +296,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 +343,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 +363,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 +387,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)