Skip to content

Commit

Permalink
[Android] Fix NetworkInterface.GetAllNetworkInterfaces on API 21-23 (d…
Browse files Browse the repository at this point in the history
…otnet#76541)

* Bring back pal_ifaddrs

* Update the header file
  • Loading branch information
simonrozsival committed Oct 20, 2022
1 parent 2046cd7 commit b47b3d2
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 47 deletions.
1 change: 0 additions & 1 deletion src/libraries/Native/Unix/System.Native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
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);
26 changes: 19 additions & 7 deletions src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#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 @@ -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;
}
}

Expand Down

0 comments on commit b47b3d2

Please sign in to comment.