Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release/6.0][Android] Backport fix NetworkInterface.GetAllNetworkInterfaces #77260

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 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,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)
Expand Down
18 changes: 9 additions & 9 deletions src/libraries/Native/Unix/System.Native/pal_ifaddrs.c
Original file line number Diff line number Diff line change
Expand Up @@ -312,15 +312,15 @@ 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;
}
ifa->ifa_broadaddr = (struct sockaddr*)sa;
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;
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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;

Expand All @@ -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;
}

Expand All @@ -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;

Expand Down
38 changes: 8 additions & 30 deletions src/libraries/Native/Unix/System.Native/pal_ifaddrs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <sys/cdefs.h>
#include <netinet/in.h>
#include <sys/socket.h>

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 <ifaddrs.h>

int getifaddrs (struct ifaddrs **ifap);
void freeifaddrs (struct ifaddrs *ifap);
int _netlink_getifaddrs (struct ifaddrs **ifap);
void _netlink_freeifaddrs (struct ifaddrs *ifap);
79 changes: 71 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,13 @@
#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>
#include "pal_ifaddrs.h" // fallback for Android API 21-23
#endif
#include <net/if.h>
#include <netinet/in.h>
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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)
{
Expand All @@ -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)
Expand Down