#include <assert.h>
|
#include <errno.h>
|
#if !defined(WIN32) && !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PROSPERO) && !defined(SN_TARGET_PSP2) && !defined(NN_COMPILER_RVCT)
|
#include <dlfcn.h>
|
#endif
|
#include <limits.h>
|
#include <stdio.h>
|
#include <string.h>
|
#include <stdlib.h>
|
|
#if !defined(NN_COMPILER_RVCT) && !defined(SN_TARGET_PSP2)
|
#include <sys/types.h>
|
#endif
|
#if !defined(WIN32) && !defined(NN_COMPILER_RVCT)
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
#endif
|
|
#if LINUX
|
#include <linux/netlink.h>
|
#include <linux/rtnetlink.h>
|
#include <linux/if_arp.h>
|
#endif /* def LINUX */
|
#if !defined(WIN32) && !defined(NN_COMPILER_RVCT) && !defined(SN_TARGET_PSP2)
|
#include <netinet/in.h>
|
#endif
|
|
#if ANDROID
|
#include <android/log.h>
|
#endif
|
|
#include "logger.h"
|
#include "xamarin_getifaddrs.h"
|
|
/* These aren't defined in android's rtnetlink.h (as of ndk 9d). We define fake values for them if
|
* they aren't found so that the debug code works properly. We could skip them but future versions
|
* of the NDK might include definitions for them.
|
*/
|
#ifndef IFLA_LINKINFO
|
#define IFLA_LINKINFO 1000
|
#endif
|
|
#ifndef IFLA_NET_NS_PID
|
#define IFLA_NET_NS_PID 1001
|
#endif
|
|
#ifndef IFLA_IFALIAS
|
#define IFLA_IFALIAS 1002
|
#endif
|
|
#ifndef IFLA_NUM_VF
|
#define IFLA_NUM_VF 1003
|
#endif
|
|
#ifndef IFLA_VFINFO_LIST
|
#define IFLA_VFINFO_LIST 1004
|
#endif
|
|
#ifndef IFLA_STATS64
|
#define IFLA_STATS64 1005
|
#endif
|
|
#ifndef IFLA_VF_PORTS
|
#define IFLA_VF_PORTS 1006
|
#endif
|
|
#ifndef IFLA_PORT_SELF
|
#define IFLA_PORT_SELF 1007
|
#endif
|
|
#ifndef IFLA_AF_SPEC
|
#define IFLA_AF_SPEC 1008
|
#endif
|
|
#ifndef IFLA_GROUP
|
#define IFLA_GROUP 1009
|
#endif
|
|
#ifndef IFLA_NET_NS_FD
|
#define IFLA_NET_NS_FD 1010
|
#endif
|
|
#ifndef IFLA_EXT_MASK
|
#define IFLA_EXT_MASK 1011
|
#endif
|
|
#ifndef IFLA_PROMISCUITY
|
#define IFLA_PROMISCUITY 1012
|
#endif
|
|
#ifndef IFLA_NUM_TX_QUEUES
|
#define IFLA_NUM_TX_QUEUES 1013
|
#endif
|
|
#ifndef IFLA_NUM_RX_QUEUES
|
#define IFLA_NUM_RX_QUEUES 1014
|
#endif
|
|
#ifndef IFLA_CARRIER
|
#define IFLA_CARRIER 1015
|
#endif
|
|
#ifndef IFLA_PHYS_PORT_ID
|
#define IFLA_PHYS_PORT_ID 1016
|
#endif
|
|
/* The amount of data we read from the kernel in one call */
|
#define RESPONSE_BUFFER_SIZE 1024
|
|
/* Maximum interface address label size, should be more than enough */
|
#define MAX_IFA_LABEL_SIZE 1024
|
|
#if LINUX
|
|
/* This is the message we send to the kernel */
|
typedef struct
|
{
|
struct nlmsghdr header;
|
struct rtgenmsg message;
|
} netlink_request;
|
|
typedef struct
|
{
|
int sock_fd;
|
int seq;
|
struct sockaddr_nl them; /* kernel end */
|
struct sockaddr_nl us; /* our end */
|
struct msghdr message_header; /* for use with sendmsg */
|
struct iovec payload_vector; /* Used to send netlink_request */
|
} netlink_session;
|
|
/* Turns out that quite a few link types have address length bigger than the 8 bytes allocated in
|
* this structure as defined by the OS. Examples are Infiniband or ipv6 tunnel devices
|
*/
|
struct sockaddr_ll_extended
|
{
|
unsigned short int sll_family;
|
unsigned short int sll_protocol;
|
int sll_ifindex;
|
unsigned short int sll_hatype;
|
unsigned char sll_pkttype;
|
unsigned char sll_halen;
|
unsigned char sll_addr[24];
|
};
|
|
static int parse_netlink_reply(netlink_session *session, struct _monodroid_ifaddrs **ifaddrs_head, struct _monodroid_ifaddrs **last_ifaddr);
|
static struct _monodroid_ifaddrs *get_link_info(const struct nlmsghdr *message);
|
static struct _monodroid_ifaddrs *get_link_address(const struct nlmsghdr *message, struct _monodroid_ifaddrs **ifaddrs_head);
|
static int open_netlink_session(netlink_session *session);
|
static int send_netlink_dump_request(netlink_session *session, int type);
|
static int append_ifaddr(struct _monodroid_ifaddrs *addr, struct _monodroid_ifaddrs **ifaddrs_head, struct _monodroid_ifaddrs **last_ifaddr);
|
static int fill_ll_address(struct sockaddr_ll_extended **sa, struct ifinfomsg *net_interface, void *rta_data, int rta_payload_length);
|
static int fill_sa_address(struct sockaddr **sa, struct ifaddrmsg *net_address, void *rta_data, int rta_payload_length);
|
static void free_single_xamarin_ifaddrs(struct _monodroid_ifaddrs **ifap);
|
static void get_ifaddrs_impl(int(**getifaddrs_impl) (struct _monodroid_ifaddrs **ifap), void(**freeifaddrs_impl) (struct _monodroid_ifaddrs *ifa));
|
static struct _monodroid_ifaddrs *find_interface_by_index(int index, struct _monodroid_ifaddrs **ifaddrs_head);
|
static char *get_interface_name_by_index(int index, struct _monodroid_ifaddrs **ifaddrs_head);
|
static int get_interface_flags_by_index(int index, struct _monodroid_ifaddrs **ifaddrs_head);
|
static int calculate_address_netmask(struct _monodroid_ifaddrs *ifa, struct ifaddrmsg *net_address);
|
#if DEBUG
|
static void print_ifla_name(int id);
|
static void print_address_list(char *title, struct _monodroid_ifaddrs *list);
|
#endif
|
|
/* We don't use 'struct ifaddrs' since that doesn't exist in Android's bionic, but since our
|
* version of the structure is 100% compatible we can just use it instead
|
*/
|
typedef int (*getifaddrs_impl_fptr)(struct _monodroid_ifaddrs **);
|
typedef void (*freeifaddrs_impl_fptr)(struct _monodroid_ifaddrs *ifa);
|
|
static getifaddrs_impl_fptr getifaddrs_impl = NULL;
|
static freeifaddrs_impl_fptr freeifaddrs_impl = NULL;
|
|
void
|
_monodroid_getifaddrs_init()
|
{
|
get_ifaddrs_impl(&getifaddrs_impl, &freeifaddrs_impl);
|
}
|
|
int
|
_monodroid_getifaddrs(struct _monodroid_ifaddrs **ifap)
|
{
|
netlink_session session;
|
struct _monodroid_ifaddrs *ifaddrs_head;
|
struct _monodroid_ifaddrs *last_ifaddr;
|
int ret = -1;
|
|
if (getifaddrs_impl)
|
return (*getifaddrs_impl)(ifap);
|
|
if (!ifap)
|
{
|
goto cleanup;
|
}
|
|
*ifap = NULL;
|
ifaddrs_head = 0;
|
last_ifaddr = 0;
|
|
if (open_netlink_session(&session) < 0)
|
{
|
goto cleanup;
|
}
|
|
/* Request information about the specified link. In our case it will be all of them since we
|
request the root of the link tree below
|
*/
|
if ((send_netlink_dump_request(&session, RTM_GETLINK) < 0) ||
|
(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))
|
{
|
_monodroid_freeifaddrs(ifaddrs_head);
|
goto cleanup;
|
}
|
|
ret = 0;
|
*ifap = ifaddrs_head;
|
#if DEBUG
|
print_address_list("Initial interfaces list", *ifap);
|
#endif
|
|
cleanup:
|
if (session.sock_fd >= 0)
|
{
|
close(session.sock_fd);
|
session.sock_fd = -1;
|
}
|
|
return ret;
|
}
|
|
void
|
_monodroid_freeifaddrs(struct _monodroid_ifaddrs *ifa)
|
{
|
struct _monodroid_ifaddrs *cur, *next;
|
|
if (!ifa)
|
return;
|
|
if (freeifaddrs_impl)
|
{
|
(*freeifaddrs_impl)(ifa);
|
return;
|
}
|
|
#if DEBUG
|
print_address_list("List passed to freeifaddrs", ifa);
|
#endif
|
cur = ifa;
|
while (cur)
|
{
|
next = cur->ifa_next;
|
free_single_xamarin_ifaddrs(&cur);
|
cur = next;
|
}
|
}
|
|
static void
|
get_ifaddrs_impl(int(**getifaddrs_impl) (struct _monodroid_ifaddrs **ifap), void(**freeifaddrs_impl) (struct _monodroid_ifaddrs *ifa))
|
{
|
void *libc;
|
|
assert(getifaddrs_impl);
|
assert(freeifaddrs_impl);
|
|
libc = dlopen("libc.so", RTLD_NOW);
|
if (libc)
|
{
|
*getifaddrs_impl = dlsym(libc, "getifaddrs");
|
if (*getifaddrs_impl)
|
*freeifaddrs_impl = dlsym(libc, "freeifaddrs");
|
}
|
|
if (!*getifaddrs_impl)
|
log_info(LOG_NET, "This libc does not have getifaddrs/freeifaddrs, using Xamarin's\n");
|
else
|
log_info(LOG_NET, "This libc has getifaddrs/freeifaddrs\n");
|
}
|
|
static void
|
free_single_xamarin_ifaddrs(struct _monodroid_ifaddrs **ifap)
|
{
|
struct _monodroid_ifaddrs *ifa = ifap ? *ifap : NULL;
|
if (!ifa)
|
return;
|
|
if (ifa->ifa_name)
|
free(ifa->ifa_name);
|
|
if (ifa->ifa_addr)
|
free(ifa->ifa_addr);
|
|
if (ifa->ifa_netmask)
|
free(ifa->ifa_netmask);
|
|
if (ifa->_monodroid_ifa_broadaddr)
|
free(ifa->_monodroid_ifa_broadaddr);
|
|
if (ifa->ifa_data)
|
free(ifa->ifa_data);
|
|
free(ifa);
|
*ifap = NULL;
|
}
|
|
static int
|
open_netlink_session(netlink_session *session)
|
{
|
assert(session != 0);
|
|
memset(session, 0, sizeof(*session));
|
session->sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
if (session->sock_fd == -1)
|
{
|
log_warn(LOG_NETLINK, "Failed to create a netlink socket. %s\n", strerror(errno));
|
return -1;
|
}
|
|
/* Fill out addresses */
|
session->us.nl_family = AF_NETLINK;
|
session->us.nl_pid = getpid();
|
session->us.nl_groups = 0;
|
|
session->them.nl_family = AF_NETLINK;
|
|
if (bind(session->sock_fd, (struct sockaddr *)&session->us, sizeof(session->us)) < 0)
|
{
|
log_warn(LOG_NETLINK, "Failed to bind to the netlink socket. %s\n", strerror(errno));
|
return -1;
|
}
|
|
return 0;
|
}
|
|
static int
|
send_netlink_dump_request(netlink_session *session, int type)
|
{
|
netlink_request request;
|
|
memset(&request, 0, sizeof(request));
|
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
|
/* Flags (from netlink.h):
|
NLM_F_REQUEST - it's a request message
|
NLM_F_DUMP - gives us the root of the link tree and returns all links matching our requested
|
AF, which in our case means all of them (AF_PACKET)
|
*/
|
request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH;
|
request.header.nlmsg_seq = ++session->seq;
|
request.header.nlmsg_pid = session->us.nl_pid;
|
request.header.nlmsg_type = type;
|
|
/* AF_PACKET means we want to see everything */
|
request.message.rtgen_family = AF_PACKET;
|
|
memset(&session->payload_vector, 0, sizeof(session->payload_vector));
|
session->payload_vector.iov_len = request.header.nlmsg_len;
|
session->payload_vector.iov_base = &request;
|
|
memset(&session->message_header, 0, sizeof(session->message_header));
|
session->message_header.msg_namelen = sizeof(session->them);
|
session->message_header.msg_name = &session->them;
|
session->message_header.msg_iovlen = 1;
|
session->message_header.msg_iov = &session->payload_vector;
|
|
if (sendmsg(session->sock_fd, (const struct msghdr*)&session->message_header, 0) < 0)
|
{
|
log_warn(LOG_NETLINK, "Failed to send netlink message. %s\n", strerror(errno));
|
return -1;
|
}
|
|
return 0;
|
}
|
|
static int
|
append_ifaddr(struct _monodroid_ifaddrs *addr, struct _monodroid_ifaddrs **ifaddrs_head, struct _monodroid_ifaddrs **last_ifaddr)
|
{
|
assert(addr);
|
assert(ifaddrs_head);
|
assert(last_ifaddr);
|
|
if (!*ifaddrs_head)
|
{
|
*ifaddrs_head = *last_ifaddr = addr;
|
if (!*ifaddrs_head)
|
return -1;
|
}
|
else if (!*last_ifaddr)
|
{
|
struct _monodroid_ifaddrs *last = *ifaddrs_head;
|
|
while (last->ifa_next)
|
last = last->ifa_next;
|
*last_ifaddr = last;
|
}
|
|
addr->ifa_next = NULL;
|
if (addr == *last_ifaddr)
|
return 0;
|
|
assert(addr != *last_ifaddr);
|
(*last_ifaddr)->ifa_next = addr;
|
*last_ifaddr = addr;
|
assert((*last_ifaddr)->ifa_next == NULL);
|
|
return 0;
|
}
|
|
static int
|
parse_netlink_reply(netlink_session *session, struct _monodroid_ifaddrs **ifaddrs_head, struct _monodroid_ifaddrs **last_ifaddr)
|
{
|
ssize_t length;
|
struct msghdr netlink_reply;
|
struct iovec reply_vector;
|
struct nlmsghdr *current_message;
|
struct _monodroid_ifaddrs *addr;
|
unsigned char response[RESPONSE_BUFFER_SIZE];
|
|
assert(session);
|
assert(ifaddrs_head);
|
assert(last_ifaddr);
|
|
while (1)
|
{
|
memset(response, 0, RESPONSE_BUFFER_SIZE);
|
memset(&reply_vector, 0, sizeof(reply_vector));
|
reply_vector.iov_len = RESPONSE_BUFFER_SIZE;
|
reply_vector.iov_base = response;
|
|
memset(&netlink_reply, 0, sizeof(netlink_reply));
|
netlink_reply.msg_namelen = sizeof(&session->them);
|
netlink_reply.msg_name = &session->them;
|
netlink_reply.msg_iovlen = 1;
|
netlink_reply.msg_iov = &reply_vector;
|
|
log_debug(LOG_NETLINK, "receiving message...\n");
|
length = recvmsg(session->sock_fd, &netlink_reply, 0);
|
log_debug(LOG_NETLINK, " length == %d\n", (int)length);
|
|
if (length < 0)
|
{
|
log_debug(LOG_NETLINK, "Failed to receive reply from netlink. %s\n", strerror(errno));
|
return -1;
|
}
|
|
if (length == 0)
|
return 0; /* done, apparently */
|
|
for (current_message = (struct nlmsghdr*)response; current_message && NLMSG_OK(current_message, length); current_message = NLMSG_NEXT(current_message, length))
|
{
|
log_debug(LOG_NETLINK, "next message... (type: %u)\n", current_message->nlmsg_type);
|
switch (current_message->nlmsg_type)
|
{
|
/* See rtnetlink.h */
|
case RTM_NEWLINK:
|
log_debug(LOG_NETLINK, " dumping link...\n");
|
addr = get_link_info(current_message);
|
if (!addr || append_ifaddr(addr, ifaddrs_head, last_ifaddr) < 0)
|
return -1;
|
log_debug(LOG_NETLINK, " done\n");
|
break;
|
|
case RTM_NEWADDR:
|
log_debug(LOG_NETLINK, " got an address\n");
|
addr = get_link_address(current_message, ifaddrs_head);
|
if (!addr || append_ifaddr(addr, ifaddrs_head, last_ifaddr) < 0)
|
return -1;
|
break;
|
|
case NLMSG_DONE:
|
log_debug(LOG_NETLINK, " message done\n");
|
return 0;
|
}
|
}
|
}
|
}
|
|
static int
|
fill_sa_address(struct sockaddr **sa, struct ifaddrmsg *net_address, void *rta_data, int rta_payload_length)
|
{
|
assert(sa);
|
assert(net_address);
|
assert(rta_data);
|
|
switch (net_address->ifa_family)
|
{
|
case AF_INET:
|
{
|
struct sockaddr_in *sa4;
|
assert(rta_payload_length == 4); /* IPv4 address length */
|
sa4 = (struct sockaddr_in*)calloc(1, sizeof(*sa4));
|
if (!sa4)
|
return -1;
|
|
sa4->sin_family = AF_INET;
|
memcpy(&sa4->sin_addr, rta_data, rta_payload_length);
|
*sa = (struct sockaddr*)sa4;
|
break;
|
}
|
|
case AF_INET6:
|
{
|
struct sockaddr_in6 *sa6;
|
assert(rta_payload_length == 16); /* IPv6 address length */
|
sa6 = (struct sockaddr_in6*)calloc(1, sizeof(*sa6));
|
if (!sa6)
|
return -1;
|
|
sa6->sin6_family = AF_INET6;
|
memcpy(&sa6->sin6_addr, rta_data, rta_payload_length);
|
if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sa6->sin6_addr))
|
sa6->sin6_scope_id = net_address->ifa_index;
|
*sa = (struct sockaddr*)sa6;
|
break;
|
}
|
|
default:
|
{
|
struct sockaddr *sagen;
|
assert(rta_payload_length <= sizeof(sagen->sa_data));
|
*sa = sagen = (struct sockaddr*)calloc(1, sizeof(*sagen));
|
if (sagen)
|
return -1;
|
|
sagen->sa_family = net_address->ifa_family;
|
memcpy(&sagen->sa_data, rta_data, rta_payload_length);
|
break;
|
}
|
}
|
|
return 0;
|
}
|
|
static int
|
fill_ll_address(struct sockaddr_ll_extended **sa, struct ifinfomsg *net_interface, void *rta_data, int rta_payload_length)
|
{
|
assert(sa);
|
assert(net_interface);
|
|
/* Always allocate, do not free - caller may reuse the same variable */
|
*sa = calloc(1, sizeof(**sa));
|
if (!*sa)
|
return -1;
|
|
(*sa)->sll_family = AF_PACKET; /* Always for physical links */
|
|
/* The assert can only fail for Iniband links, which are quite unlikely to be found
|
* in any mobile devices
|
*/
|
log_debug(LOG_NETLINK, "rta_payload_length == %d; sizeof sll_addr == %d; hw type == 0x%X\n", rta_payload_length, sizeof((*sa)->sll_addr), net_interface->ifi_type);
|
if (rta_payload_length > sizeof((*sa)->sll_addr))
|
{
|
log_info(LOG_NETLINK, "Address is too long to place in sockaddr_ll (%d > %d)", rta_payload_length, sizeof((*sa)->sll_addr));
|
free(*sa);
|
*sa = NULL;
|
return -1;
|
}
|
|
(*sa)->sll_ifindex = net_interface->ifi_index;
|
(*sa)->sll_hatype = net_interface->ifi_type;
|
(*sa)->sll_halen = rta_payload_length;
|
memcpy((*sa)->sll_addr, rta_data, rta_payload_length);
|
|
return 0;
|
}
|
|
static struct _monodroid_ifaddrs *
|
find_interface_by_index(int index, struct _monodroid_ifaddrs **ifaddrs_head)
|
{
|
struct _monodroid_ifaddrs *cur;
|
if (!ifaddrs_head || !*ifaddrs_head)
|
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
|
*/
|
cur = *ifaddrs_head;
|
while (cur)
|
{
|
if (cur->ifa_addr && cur->ifa_addr->sa_family == AF_PACKET && ((struct sockaddr_ll_extended*)cur->ifa_addr)->sll_ifindex == index)
|
return cur;
|
if (cur == cur->ifa_next)
|
break;
|
cur = cur->ifa_next;
|
}
|
|
return NULL;
|
}
|
|
static char *
|
get_interface_name_by_index(int index, struct _monodroid_ifaddrs **ifaddrs_head)
|
{
|
struct _monodroid_ifaddrs *iface = find_interface_by_index(index, ifaddrs_head);
|
if (!iface || !iface->ifa_name)
|
return NULL;
|
|
return iface->ifa_name;
|
}
|
|
static int
|
get_interface_flags_by_index(int index, struct _monodroid_ifaddrs **ifaddrs_head)
|
{
|
struct _monodroid_ifaddrs *iface = find_interface_by_index(index, ifaddrs_head);
|
if (!iface)
|
return 0;
|
|
return iface->ifa_flags;
|
}
|
|
static int
|
calculate_address_netmask(struct _monodroid_ifaddrs *ifa, struct ifaddrmsg *net_address)
|
{
|
if (ifa->ifa_addr && ifa->ifa_addr->sa_family != AF_UNSPEC && ifa->ifa_addr->sa_family != AF_PACKET)
|
{
|
uint32_t prefix_length = 0;
|
uint32_t data_length = 0;
|
unsigned char *netmask_data = NULL;
|
|
switch (ifa->ifa_addr->sa_family)
|
{
|
case AF_INET:
|
{
|
struct sockaddr_in *sa = (struct sockaddr_in*)calloc(1, sizeof(struct sockaddr_in));
|
if (!sa)
|
return -1;
|
|
ifa->ifa_netmask = (struct sockaddr*)sa;
|
prefix_length = net_address->ifa_prefixlen;
|
if (prefix_length > 32)
|
prefix_length = 32;
|
data_length = sizeof(sa->sin_addr);
|
netmask_data = (unsigned char*)&sa->sin_addr;
|
break;
|
}
|
|
case AF_INET6:
|
{
|
struct sockaddr_in6 *sa = (struct sockaddr_in6*)calloc(1, sizeof(struct sockaddr_in6));
|
if (!sa)
|
return -1;
|
|
ifa->ifa_netmask = (struct sockaddr*)sa;
|
prefix_length = net_address->ifa_prefixlen;
|
if (prefix_length > 128)
|
prefix_length = 128;
|
data_length = sizeof(sa->sin6_addr);
|
netmask_data = (unsigned char*)&sa->sin6_addr;
|
break;
|
}
|
}
|
|
if (ifa->ifa_netmask && netmask_data)
|
{
|
/* Fill the first X bytes with 255 */
|
uint32_t prefix_bytes = prefix_length / 8;
|
uint32_t postfix_bytes;
|
int i;
|
|
if (prefix_bytes > data_length)
|
{
|
errno = EINVAL;
|
return -1;
|
}
|
postfix_bytes = data_length - prefix_bytes;
|
memset(netmask_data, 0xFF, prefix_bytes);
|
if (postfix_bytes > 0)
|
memset(netmask_data + prefix_bytes + 1, 0x00, postfix_bytes);
|
log_debug(LOG_NETLINK, " calculating netmask, prefix length is %u bits (%u bytes), data length is %u bytes\n", prefix_length, prefix_bytes, data_length);
|
if (prefix_bytes + 2 < data_length)
|
/* Set the rest of the mask bits in the byte following the last 0xFF value */
|
netmask_data[prefix_bytes + 1] = 0xff << (8 - (prefix_length % 8));
|
log_debug(LOG_NETLINK, " netmask is: ");
|
for (i = 0; i < data_length; i++)
|
{
|
log_debug(LOG_NETLINK, "%s%u", i == 0 ? "" : ".", (unsigned char)ifa->ifa_netmask->sa_data[i]);
|
}
|
log_debug(LOG_NETLINK, "\n");
|
}
|
}
|
|
return 0;
|
}
|
|
static struct _monodroid_ifaddrs *
|
get_link_address(const struct nlmsghdr *message, struct _monodroid_ifaddrs **ifaddrs_head)
|
{
|
size_t length;
|
struct rtattr *attribute;
|
struct ifaddrmsg *net_address;
|
struct _monodroid_ifaddrs *ifa = NULL;
|
struct sockaddr **sa;
|
int payload_size;
|
|
assert(message);
|
net_address = NLMSG_DATA(message);
|
length = IFA_PAYLOAD(message);
|
if (length <= 0)
|
{
|
goto error;
|
}
|
|
ifa = calloc(1, sizeof(*ifa));
|
if (!ifa)
|
{
|
goto error;
|
}
|
|
ifa->ifa_flags = get_interface_flags_by_index(net_address->ifa_index, ifaddrs_head);
|
|
attribute = IFA_RTA(net_address);
|
while (RTA_OK(attribute, length))
|
{
|
payload_size = RTA_PAYLOAD(attribute);
|
sa = NULL;
|
|
switch (attribute->rta_type)
|
{
|
case IFA_LABEL:
|
{
|
int room_for_trailing_null = 0;
|
|
if (payload_size > MAX_IFA_LABEL_SIZE)
|
{
|
payload_size = MAX_IFA_LABEL_SIZE;
|
room_for_trailing_null = 1;
|
}
|
|
if (payload_size > 0)
|
{
|
ifa->ifa_name = (char*)malloc(payload_size + room_for_trailing_null);
|
if (!ifa->ifa_name)
|
{
|
goto error;
|
}
|
|
memcpy(ifa->ifa_name, RTA_DATA(attribute), payload_size);
|
if (room_for_trailing_null)
|
ifa->ifa_name[payload_size] = '\0';
|
}
|
break;
|
}
|
|
case IFA_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_ADDRESST occured earlier and that address
|
* is indeed the P2P destination one.
|
*/
|
ifa->_monodroid_ifa_dstaddr = ifa->ifa_addr;
|
ifa->ifa_addr = 0;
|
}
|
sa = &ifa->ifa_addr;
|
break;
|
|
case IFA_BROADCAST:
|
if (ifa->_monodroid_ifa_dstaddr)
|
{
|
/* IFA_LOCAL happened earlier, undo its effect here */
|
free(ifa->_monodroid_ifa_dstaddr);
|
ifa->_monodroid_ifa_dstaddr = NULL;
|
}
|
sa = &ifa->_monodroid_ifa_broadaddr;
|
break;
|
|
case IFA_ADDRESS:
|
if (ifa->ifa_addr)
|
{
|
/* Apparently IFA_LOCAL occured earlier and we have a P2P connection
|
* here. IFA_LOCAL carries the destination address, move it there
|
*/
|
ifa->_monodroid_ifa_dstaddr = ifa->ifa_addr;
|
ifa->ifa_addr = NULL;
|
}
|
sa = &ifa->ifa_addr;
|
break;
|
}
|
|
if (sa)
|
{
|
if (fill_sa_address(sa, net_address, RTA_DATA(attribute), RTA_PAYLOAD(attribute)) < 0)
|
{
|
goto error;
|
}
|
}
|
|
attribute = RTA_NEXT(attribute, length);
|
}
|
|
/* glibc stores the associated interface name in the address if IFA_LABEL never occured */
|
if (!ifa->ifa_name)
|
{
|
char *name = get_interface_name_by_index(net_address->ifa_index, ifaddrs_head);
|
log_debug(LOG_NETLINK, " address has no name/label, getting one from interface\n");
|
ifa->ifa_name = name ? strdup(name) : NULL;
|
}
|
log_debug(LOG_NETLINK, " address label: %s\n", ifa->ifa_name);
|
|
if (calculate_address_netmask(ifa, net_address) < 0)
|
{
|
goto error;
|
}
|
|
return ifa;
|
|
error :
|
{
|
/* errno may be modified by free, or any other call inside the free_single_xamarin_ifaddrs
|
* function. We don't care about errors in there since it is more important to know how we
|
* failed to obtain the link address and not that we went OOM. Save and restore the value
|
* after the resources are freed.
|
*/
|
int errno_save = errno;
|
free_single_xamarin_ifaddrs(&ifa);
|
errno = errno_save;
|
return NULL;
|
}
|
}
|
|
static struct _monodroid_ifaddrs *
|
get_link_info(const struct nlmsghdr *message)
|
{
|
ssize_t length;
|
struct rtattr *attribute;
|
struct ifinfomsg *net_interface;
|
struct _monodroid_ifaddrs *ifa = NULL;
|
struct sockaddr_ll_extended *sa = NULL;
|
|
assert(message);
|
net_interface = NLMSG_DATA(message);
|
length = message->nlmsg_len - NLMSG_LENGTH(sizeof(*net_interface));
|
if (length <= 0)
|
{
|
goto error;
|
}
|
|
ifa = calloc(1, sizeof(*ifa));
|
if (!ifa)
|
{
|
goto error;
|
}
|
|
ifa->ifa_flags = net_interface->ifi_flags;
|
attribute = IFLA_RTA(net_interface);
|
while (RTA_OK(attribute, length))
|
{
|
switch (attribute->rta_type)
|
{
|
case IFLA_IFNAME:
|
ifa->ifa_name = strdup(RTA_DATA(attribute));
|
if (!ifa->ifa_name)
|
{
|
goto error;
|
}
|
log_debug(LOG_NETLINK, " interface name (payload length: %d; string length: %d)\n", RTA_PAYLOAD(attribute), strlen(ifa->ifa_name));
|
log_debug(LOG_NETLINK, " %s\n", ifa->ifa_name);
|
break;
|
|
case IFLA_BROADCAST:
|
log_debug(LOG_NETLINK, " interface broadcast (%d bytes)\n", RTA_PAYLOAD(attribute));
|
if (fill_ll_address(&sa, net_interface, RTA_DATA(attribute), RTA_PAYLOAD(attribute)) < 0)
|
{
|
goto error;
|
}
|
ifa->_monodroid_ifa_broadaddr = (struct sockaddr*)sa;
|
break;
|
|
case IFLA_ADDRESS:
|
log_debug(LOG_NETLINK, " interface address (%d bytes)\n", RTA_PAYLOAD(attribute));
|
if (fill_ll_address(&sa, net_interface, RTA_DATA(attribute), RTA_PAYLOAD(attribute)) < 0)
|
{
|
goto error;
|
}
|
ifa->ifa_addr = (struct sockaddr*)sa;
|
break;
|
|
default:
|
log_debug(LOG_NETLINK, " rta_type: ");
|
#if DEBUG
|
print_ifla_name(attribute->rta_type);
|
#endif
|
break;
|
}
|
|
attribute = RTA_NEXT(attribute, length);
|
}
|
log_debug(LOG_NETLINK, "link flags: 0x%X", ifa->ifa_flags);
|
return ifa;
|
|
error:
|
if (sa)
|
free(sa);
|
free_single_xamarin_ifaddrs(&ifa);
|
|
return NULL;
|
}
|
|
#else
|
void
|
_monodroid_getifaddrs_init(void)
|
{
|
}
|
|
int
|
_monodroid_getifaddrs(struct _monodroid_ifaddrs **ifap)
|
{
|
*ifap = NULL;
|
return 0;
|
}
|
|
void _monodroid_freeifaddrs(struct _monodroid_ifaddrs *ifa)
|
{
|
}
|
|
#endif
|
|
#if LINUX && DEBUG
|
#define ENUM_VALUE_ENTRY(enumvalue) { enumvalue, #enumvalue }
|
struct enumvalue
|
{
|
int value;
|
char *name;
|
};
|
|
struct enumvalue iflas[] = {
|
ENUM_VALUE_ENTRY(IFLA_UNSPEC),
|
ENUM_VALUE_ENTRY(IFLA_ADDRESS),
|
ENUM_VALUE_ENTRY(IFLA_BROADCAST),
|
ENUM_VALUE_ENTRY(IFLA_IFNAME),
|
ENUM_VALUE_ENTRY(IFLA_MTU),
|
ENUM_VALUE_ENTRY(IFLA_LINK),
|
ENUM_VALUE_ENTRY(IFLA_QDISC),
|
ENUM_VALUE_ENTRY(IFLA_STATS),
|
ENUM_VALUE_ENTRY(IFLA_COST),
|
ENUM_VALUE_ENTRY(IFLA_PRIORITY),
|
ENUM_VALUE_ENTRY(IFLA_MASTER),
|
ENUM_VALUE_ENTRY(IFLA_WIRELESS),
|
ENUM_VALUE_ENTRY(IFLA_PROTINFO),
|
ENUM_VALUE_ENTRY(IFLA_TXQLEN),
|
ENUM_VALUE_ENTRY(IFLA_MAP),
|
ENUM_VALUE_ENTRY(IFLA_WEIGHT),
|
ENUM_VALUE_ENTRY(IFLA_OPERSTATE),
|
ENUM_VALUE_ENTRY(IFLA_LINKMODE),
|
ENUM_VALUE_ENTRY(IFLA_LINKINFO),
|
ENUM_VALUE_ENTRY(IFLA_NET_NS_PID),
|
ENUM_VALUE_ENTRY(IFLA_IFALIAS),
|
ENUM_VALUE_ENTRY(IFLA_NUM_VF),
|
ENUM_VALUE_ENTRY(IFLA_VFINFO_LIST),
|
ENUM_VALUE_ENTRY(IFLA_STATS64),
|
ENUM_VALUE_ENTRY(IFLA_VF_PORTS),
|
ENUM_VALUE_ENTRY(IFLA_PORT_SELF),
|
ENUM_VALUE_ENTRY(IFLA_AF_SPEC),
|
ENUM_VALUE_ENTRY(IFLA_GROUP),
|
ENUM_VALUE_ENTRY(IFLA_NET_NS_FD),
|
ENUM_VALUE_ENTRY(IFLA_EXT_MASK),
|
ENUM_VALUE_ENTRY(IFLA_PROMISCUITY),
|
ENUM_VALUE_ENTRY(IFLA_NUM_TX_QUEUES),
|
ENUM_VALUE_ENTRY(IFLA_NUM_RX_QUEUES),
|
ENUM_VALUE_ENTRY(IFLA_CARRIER),
|
ENUM_VALUE_ENTRY(IFLA_PHYS_PORT_ID),
|
{ -1, 0 }
|
};
|
|
static void
|
print_ifla_name(int id)
|
{
|
int i = 0;
|
while (1)
|
{
|
if (iflas[i].value == -1 && iflas[i].name == 0)
|
{
|
log_info(LOG_NETLINK, "Unknown ifla->name: unknown id %d\n", id);
|
break;
|
}
|
|
if (iflas[i].value != id)
|
{
|
i++;
|
continue;
|
}
|
log_info(LOG_NETLINK, "ifla->name: %s (%d)\n", iflas[i].name, iflas[i].value);
|
break;
|
}
|
}
|
|
static void
|
print_address_list(char *title, struct _monodroid_ifaddrs *list)
|
{
|
struct _monodroid_ifaddrs *cur;
|
char *msg, *tmp;
|
|
if (!list)
|
{
|
log_info(LOG_NETLINK, "monodroid-net", "No list to print in %s", __FUNCTION__);
|
return;
|
}
|
|
cur = list;
|
msg = NULL;
|
while (cur)
|
{
|
tmp = NULL;
|
asprintf(&tmp, "%s%s%p (%s; %p)", msg ? msg : "", msg ? " -> " : "", cur, cur->ifa_name, cur->ifa_name);
|
if (msg)
|
free(msg);
|
msg = tmp;
|
cur = cur->ifa_next;
|
}
|
|
log_info(LOG_NETLINK, "%s: %s", title, msg ? msg : "[no addresses]");
|
free(msg);
|
}
|
|
#endif
|