Fix compat_ifaddrs.c - network addresses are now shown on Android

This commit is contained in:
twinaphex 2016-03-03 03:44:16 +01:00
parent 5b4ba2a449
commit dfa315f0d5

View File

@ -23,22 +23,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <compat/ifaddrs.h> #include <compat/ifaddrs.h>
#include <retro_miscellaneous.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <errno.h> #include <errno.h>
#ifndef _WIN32
#include <unistd.h> #include <unistd.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/if_arp.h> #include <net/if_arp.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/if_packet.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#endif
typedef struct NetlinkList typedef struct NetlinkList
{ {
@ -49,61 +45,55 @@ typedef struct NetlinkList
static int netlink_socket(void) static int netlink_socket(void)
{ {
struct sockaddr_nl l_addr;
int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if(l_socket < 0) if(l_socket < 0)
{
return -1; return -1;
}
struct sockaddr_nl l_addr;
memset(&l_addr, 0, sizeof(l_addr)); memset(&l_addr, 0, sizeof(l_addr));
l_addr.nl_family = AF_NETLINK; l_addr.nl_family = AF_NETLINK;
if(bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0) if(bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0)
goto error; {
return l_socket;
error:
close(l_socket); close(l_socket);
return -1; return -1;
} }
return l_socket;
}
static int netlink_send(int p_socket, int p_request) static int netlink_send(int p_socket, int p_request)
{ {
struct
{
struct nlmsghdr m_hdr;
struct rtgenmsg m_msg;
} l_data;
memset(&l_data, 0, sizeof(l_data));
l_data.m_hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
l_data.m_hdr.nlmsg_type = p_request;
l_data.m_hdr.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
l_data.m_hdr.nlmsg_pid = 0;
l_data.m_hdr.nlmsg_seq = p_socket;
l_data.m_msg.rtgen_family = AF_UNSPEC;
struct sockaddr_nl l_addr; struct sockaddr_nl l_addr;
char l_buffer[NLMSG_ALIGN(sizeof(struct nlmsghdr))
+ NLMSG_ALIGN(sizeof(struct rtgenmsg))];
struct nlmsghdr *l_hdr = NULL;
struct rtgenmsg *l_msg = NULL;
memset(l_buffer, 0, sizeof(l_buffer));
l_hdr = (struct nlmsghdr *)l_buffer;
l_msg = (struct rtgenmsg *)NLMSG_DATA(l_hdr);
l_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*l_msg));
l_hdr->nlmsg_type = p_request;
l_hdr->nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
l_hdr->nlmsg_pid = 0;
l_hdr->nlmsg_seq = p_socket;
l_msg->rtgen_family = AF_UNSPEC;
memset(&l_addr, 0, sizeof(l_addr)); memset(&l_addr, 0, sizeof(l_addr));
l_addr.nl_family = AF_NETLINK; l_addr.nl_family = AF_NETLINK;
return (sendto(p_socket, &l_data.m_hdr, l_data.m_hdr.nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr)));
return (sendto(p_socket, l_hdr,
l_hdr->nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr)));
} }
static int netlink_recv(int p_socket, void *p_buffer, size_t p_len) static int netlink_recv(int p_socket, void *p_buffer, size_t p_len)
{ {
struct msghdr l_msg; struct msghdr l_msg;
struct sockaddr_nl l_addr;
struct iovec l_iov = { p_buffer, p_len }; struct iovec l_iov = { p_buffer, p_len };
struct sockaddr_nl l_addr;
for(;;) for(;;)
{ {
int l_result;
l_msg.msg_name = (void *)&l_addr; l_msg.msg_name = (void *)&l_addr;
l_msg.msg_namelen = sizeof(l_addr); l_msg.msg_namelen = sizeof(l_addr);
l_msg.msg_iov = &l_iov; l_msg.msg_iov = &l_iov;
@ -111,18 +101,21 @@ static int netlink_recv(int p_socket, void *p_buffer, size_t p_len)
l_msg.msg_control = NULL; l_msg.msg_control = NULL;
l_msg.msg_controllen = 0; l_msg.msg_controllen = 0;
l_msg.msg_flags = 0; l_msg.msg_flags = 0;
int l_result = recvmsg(p_socket, &l_msg, 0);
l_result = recvmsg(p_socket, &l_msg, 0);
if(l_result < 0) if(l_result < 0)
{ {
if(errno == EINTR) if(errno == EINTR)
{
continue; continue;
}
return -2; return -2;
} }
if(l_msg.msg_flags & MSG_TRUNC) /* buffer was too small */ if(l_msg.msg_flags & MSG_TRUNC)
{ // buffer was too small
return -1; return -1;
}
return l_result; return l_result;
} }
} }
@ -134,25 +127,30 @@ static struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_don
for(;;) for(;;)
{ {
int l_read;
free(l_buffer); free(l_buffer);
l_buffer = malloc(l_size); l_buffer = malloc(l_size);
l_read = netlink_recv(p_socket, l_buffer, l_size); if (l_buffer == NULL)
{
return NULL;
}
int l_read = netlink_recv(p_socket, l_buffer, l_size);
*p_size = l_read; *p_size = l_read;
if(l_read == -2) if(l_read == -2)
goto error; {
free(l_buffer);
return NULL;
}
if(l_read >= 0) if(l_read >= 0)
{ {
pid_t l_pid = getpid(); pid_t l_pid = getpid();
struct nlmsghdr *l_hdr = (struct nlmsghdr *)l_buffer; struct nlmsghdr *l_hdr;
for(l_hdr = (struct nlmsghdr *)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read))
for(; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read))
{ {
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
{
continue; continue;
}
if(l_hdr->nlmsg_type == NLMSG_DONE) if(l_hdr->nlmsg_type == NLMSG_DONE)
{ {
@ -161,35 +159,35 @@ static struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_don
} }
if(l_hdr->nlmsg_type == NLMSG_ERROR) if(l_hdr->nlmsg_type == NLMSG_ERROR)
goto error; {
free(l_buffer);
return NULL;
} }
break; }
return l_buffer;
} }
l_size *= 2; l_size *= 2;
} }
}
return l_buffer; static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size)
{
error: NetlinkList *l_item = malloc(sizeof(NetlinkList));
free(l_buffer); if (l_item == NULL)
{
return NULL; return NULL;
} }
static NetlinkList *newListItem(struct nlmsghdr *p_data,
unsigned int p_size)
{
NetlinkList *l_item = malloc(sizeof(NetlinkList));
l_item->m_next = NULL; l_item->m_next = NULL;
l_item->m_data = p_data; l_item->m_data = p_data;
l_item->m_size = p_size; l_item->m_size = p_size;
return l_item; return l_item;
} }
static void freeResultList(NetlinkList *p_list) static void freeResultList(NetlinkList *p_list)
{ {
NetlinkList *l_cur = NULL; NetlinkList *l_cur;
while(p_list) while(p_list)
{ {
l_cur = p_list; l_cur = p_list;
@ -201,35 +199,48 @@ static void freeResultList(NetlinkList *p_list)
static NetlinkList *getResultList(int p_socket, int p_request) static NetlinkList *getResultList(int p_socket, int p_request)
{ {
int l_size; if(netlink_send(p_socket, p_request) < 0)
int l_done = 0; {
return NULL;
}
NetlinkList *l_list = NULL; NetlinkList *l_list = NULL;
NetlinkList *l_end = NULL; NetlinkList *l_end = NULL;
if(netlink_send(p_socket, p_request) < 0) int l_size;
return NULL; int l_done = 0;
while(!l_done) while(!l_done)
{ {
NetlinkList *l_item = NULL; struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done);
struct nlmsghdr *l_hdr = if(!l_hdr)
getNetlinkResponse(p_socket, &l_size, &l_done); { // error
if(!l_hdr) /* error */
{
freeResultList(l_list); freeResultList(l_list);
return NULL; return NULL;
} }
l_item = newListItem(l_hdr, l_size); NetlinkList *l_item = newListItem(l_hdr, l_size);
if (!l_item)
{
freeResultList(l_list);
return NULL;
}
if(!l_list) if(!l_list)
{
l_list = l_item; l_list = l_item;
}
else else
{
l_end->m_next = l_item; l_end->m_next = l_item;
}
l_end = l_item; l_end = l_item;
} }
return l_list; return l_list;
} }
static size_t maxSize(size_t a, size_t b)
{
return (a > b ? a : b);
}
static size_t calcAddrLen(sa_family_t p_family, int p_dataSize) static size_t calcAddrLen(sa_family_t p_family, int p_dataSize)
{ {
switch(p_family) switch(p_family)
@ -239,18 +250,13 @@ static size_t calcAddrLen(sa_family_t p_family, int p_dataSize)
case AF_INET6: case AF_INET6:
return sizeof(struct sockaddr_in6); return sizeof(struct sockaddr_in6);
case AF_PACKET: case AF_PACKET:
return MAX(sizeof(struct sockaddr_ll), return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize);
offsetof(struct sockaddr_ll, sll_addr) + p_dataSize);
default: default:
break; return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize);
}
} }
return MAX(sizeof(struct sockaddr), static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size)
offsetof(struct sockaddr, sa_data) + p_dataSize);
}
static void makeSockaddr(sa_family_t p_family,
struct sockaddr *p_dest, void *p_data, size_t p_size)
{ {
switch(p_family) switch(p_family)
{ {
@ -274,23 +280,22 @@ static void makeSockaddr(sa_family_t p_family,
static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry) static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry)
{ {
if(!*p_resultList) if(!*p_resultList)
{
*p_resultList = p_entry; *p_resultList = p_entry;
}
else else
{ {
struct ifaddrs *l_cur = *p_resultList; struct ifaddrs *l_cur = *p_resultList;
while(l_cur->ifa_next) while(l_cur->ifa_next)
{
l_cur = l_cur->ifa_next; l_cur = l_cur->ifa_next;
}
l_cur->ifa_next = p_entry; l_cur->ifa_next = p_entry;
} }
} }
static void interpretLink(struct nlmsghdr *p_hdr, static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList)
struct ifaddrs **p_links, struct ifaddrs **p_resultList)
{ {
char *l_name = NULL;
char *l_addr = NULL;
char *l_data = NULL;
struct ifaddrs *l_entry = NULL;
struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr); struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr);
size_t l_nameSize = 0; size_t l_nameSize = 0;
@ -298,19 +303,15 @@ static void interpretLink(struct nlmsghdr *p_hdr,
size_t l_dataSize = 0; size_t l_dataSize = 0;
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
struct rtattr *l_rta = (struct rtattr *)(((char *)l_info) struct rtattr *l_rta;
+ NLMSG_ALIGN(sizeof(struct ifinfomsg))); for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
for(; RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{ {
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type) switch(l_rta->rta_type)
{ {
case IFLA_ADDRESS: case IFLA_ADDRESS:
case IFLA_BROADCAST: case IFLA_BROADCAST:
l_addrSize += NLMSG_ALIGN( l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize));
calcAddrLen(AF_PACKET, l_rtaDataSize));
break; break;
case IFLA_IFNAME: case IFLA_IFNAME:
l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
@ -323,26 +324,29 @@ static void interpretLink(struct nlmsghdr *p_hdr,
} }
} }
l_entry = malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize + l_dataSize); struct ifaddrs *l_entry = malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize);
if (l_entry == NULL)
{
return -1;
}
memset(l_entry, 0, sizeof(struct ifaddrs)); memset(l_entry, 0, sizeof(struct ifaddrs));
l_entry->ifa_name = ""; l_entry->ifa_name = "";
l_name = ((char *)l_entry) + sizeof(struct ifaddrs); char *l_index = ((char *)l_entry) + sizeof(struct ifaddrs);
l_addr = l_name + l_nameSize; char *l_name = l_index + sizeof(int);
l_data = l_addr + l_addrSize; char *l_addr = l_name + l_nameSize;
char *l_data = l_addr + l_addrSize;
// save the interface index so we can look it up when handling the addresses.
memcpy(l_index, &l_info->ifi_index, sizeof(int));
l_entry->ifa_flags = l_info->ifi_flags; l_entry->ifa_flags = l_info->ifi_flags;
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
l_rta = (struct rtattr *)(((char *)l_info) for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
+ NLMSG_ALIGN(sizeof(struct ifinfomsg)));
for(; RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{ {
void *l_rtaData = RTA_DATA(l_rta); void *l_rtaData = RTA_DATA(l_rta);
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type) switch(l_rta->rta_type)
{ {
case IFLA_ADDRESS: case IFLA_ADDRESS:
@ -352,12 +356,14 @@ static void interpretLink(struct nlmsghdr *p_hdr,
makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index; ((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index;
((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type; ((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type;
if(l_rta->rta_type == IFLA_ADDRESS) if(l_rta->rta_type == IFLA_ADDRESS)
{
l_entry->ifa_addr = (struct sockaddr *)l_addr; l_entry->ifa_addr = (struct sockaddr *)l_addr;
}
else else
{
l_entry->ifa_broadaddr = (struct sockaddr *)l_addr; l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
}
l_addr += NLMSG_ALIGN(l_addrLen); l_addr += NLMSG_ALIGN(l_addrLen);
break; break;
} }
@ -376,37 +382,56 @@ static void interpretLink(struct nlmsghdr *p_hdr,
} }
addToEnd(p_resultList, l_entry); addToEnd(p_resultList, l_entry);
p_links[l_info->ifi_index - 1] = l_entry; return 0;
} }
static void interpretAddr(struct nlmsghdr *p_hdr, static struct ifaddrs *findInterface(int p_index, struct ifaddrs **p_links, int p_numLinks)
struct ifaddrs **p_links, struct ifaddrs **p_resultList)
{ {
char *l_name = NULL; int l_num = 0;
char *l_addr = NULL; struct ifaddrs *l_cur = *p_links;
struct ifaddrs *l_entry = NULL; while(l_cur && l_num < p_numLinks)
struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr); {
size_t l_nameSize = 0; char *l_indexPtr = ((char *)l_cur) + sizeof(struct ifaddrs);
size_t l_addrSize = 0; int l_index;
int l_addedNetmask = 0; memcpy(&l_index, l_indexPtr, sizeof(int));
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); if(l_index == p_index)
struct rtattr *l_rta = (struct rtattr *)(((char *)l_info) {
+ NLMSG_ALIGN(sizeof(struct ifaddrmsg))); return l_cur;
}
for(; RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) l_cur = l_cur->ifa_next;
++l_num;
}
return NULL;
}
static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, int p_numLinks)
{ {
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr);
struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks);
if(l_info->ifa_family == AF_PACKET) if(l_info->ifa_family == AF_PACKET)
continue; {
return 0;
}
size_t l_nameSize = 0;
size_t l_addrSize = 0;
int l_addedNetmask = 0;
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
struct rtattr *l_rta;
for(l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type) switch(l_rta->rta_type)
{ {
case IFA_ADDRESS: case IFA_ADDRESS:
case IFA_LOCAL: case IFA_LOCAL:
if(( l_info->ifa_family == AF_INET if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask)
|| l_info->ifa_family == AF_INET6) && !l_addedNetmask) { // make room for netmask
{ /* make room for netmask */
l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
l_addedNetmask = 1; l_addedNetmask = 1;
} }
@ -421,60 +446,67 @@ static void interpretAddr(struct nlmsghdr *p_hdr,
} }
} }
l_entry = malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize); struct ifaddrs *l_entry = malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize);
if (l_entry == NULL)
{
return -1;
}
memset(l_entry, 0, sizeof(struct ifaddrs)); memset(l_entry, 0, sizeof(struct ifaddrs));
l_entry->ifa_name = p_links[l_info->ifa_index - 1]->ifa_name; l_entry->ifa_name = (l_interface ? l_interface->ifa_name : "");
char *l_name = ((char *)l_entry) + sizeof(struct ifaddrs);
char *l_addr = l_name + l_nameSize;
l_entry->ifa_flags = l_info->ifa_flags;
if(l_interface)
{
l_entry->ifa_flags |= l_interface->ifa_flags;
}
l_name = ((char *)l_entry) + sizeof(struct ifaddrs);
l_addr = l_name + l_nameSize;
l_entry->ifa_flags = l_info->ifa_flags | p_links[l_info->ifa_index - 1]->ifa_flags;
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
l_rta = (struct rtattr *)(((char *)l_info) for(l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
+ NLMSG_ALIGN(sizeof(struct ifaddrmsg)));
for(; RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{ {
void *l_rtaData = RTA_DATA(l_rta); void *l_rtaData = RTA_DATA(l_rta);
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type) switch(l_rta->rta_type)
{ {
case IFA_ADDRESS: case IFA_ADDRESS:
case IFA_BROADCAST: case IFA_BROADCAST:
case IFA_LOCAL: case IFA_LOCAL:
{ {
size_t l_addrLen = calcAddrLen( size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize);
l_info->ifa_family, l_rtaDataSize); makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
makeSockaddr(l_info->ifa_family,
(struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
if(l_info->ifa_family == AF_INET6) if(l_info->ifa_family == AF_INET6)
{ {
if( IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) if(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData))
|| IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData)) {
((struct sockaddr_in6 *)l_addr)->sin6_scope_id ((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index;
= l_info->ifa_index; }
} }
/* apparently in a point-to-point network IFA_ADDRESS contains
* the dest address and IFA_LOCAL contains the local address */
if(l_rta->rta_type == IFA_ADDRESS) if(l_rta->rta_type == IFA_ADDRESS)
{ { // apparently in a point-to-point network IFA_ADDRESS contains the dest address and IFA_LOCAL contains the local address
if(l_entry->ifa_addr) if(l_entry->ifa_addr)
{
l_entry->ifa_dstaddr = (struct sockaddr *)l_addr; l_entry->ifa_dstaddr = (struct sockaddr *)l_addr;
}
else else
{
l_entry->ifa_addr = (struct sockaddr *)l_addr; l_entry->ifa_addr = (struct sockaddr *)l_addr;
} }
}
else if(l_rta->rta_type == IFA_LOCAL) else if(l_rta->rta_type == IFA_LOCAL)
{ {
if(l_entry->ifa_addr) if(l_entry->ifa_addr)
{
l_entry->ifa_dstaddr = l_entry->ifa_addr; l_entry->ifa_dstaddr = l_entry->ifa_addr;
}
l_entry->ifa_addr = (struct sockaddr *)l_addr; l_entry->ifa_addr = (struct sockaddr *)l_addr;
} }
else else
{
l_entry->ifa_broadaddr = (struct sockaddr *)l_addr; l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
}
l_addr += NLMSG_ALIGN(l_addrLen); l_addr += NLMSG_ALIGN(l_addrLen);
break; break;
} }
@ -488,134 +520,138 @@ static void interpretAddr(struct nlmsghdr *p_hdr,
} }
} }
if(l_entry->ifa_addr if(l_entry->ifa_addr && (l_entry->ifa_addr->sa_family == AF_INET || l_entry->ifa_addr->sa_family == AF_INET6))
&& ( l_entry->ifa_addr->sa_family == AF_INET
|| l_entry->ifa_addr->sa_family == AF_INET6))
{ {
unsigned i;
char l_mask[16] = {0};
unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128); unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128);
unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix ? l_maxPrefix : l_info->ifa_prefixlen);
? l_maxPrefix : l_info->ifa_prefixlen); char l_mask[16] = {0};
unsigned i;
for(i=0; i<(l_prefix/8); ++i) for(i=0; i<(l_prefix/8); ++i)
{
l_mask[i] = 0xff; l_mask[i] = 0xff;
}
if(l_prefix % 8)
{
l_mask[i] = 0xff << (8 - (l_prefix % 8)); l_mask[i] = 0xff << (8 - (l_prefix % 8));
}
makeSockaddr(l_entry->ifa_addr->sa_family, makeSockaddr(l_entry->ifa_addr->sa_family, (struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8);
(struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8);
l_entry->ifa_netmask = (struct sockaddr *)l_addr; l_entry->ifa_netmask = (struct sockaddr *)l_addr;
} }
addToEnd(p_resultList, l_entry); addToEnd(p_resultList, l_entry);
return 0;
} }
static void interpret( static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList)
int p_socket, {
NetlinkList *p_netlinkList, int l_numLinks = 0;
struct ifaddrs **p_links, pid_t l_pid = getpid();
struct ifaddrs **p_resultList) for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
{
unsigned int l_nlsize = p_netlinkList->m_size;
struct nlmsghdr *l_hdr;
for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
{
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
{
continue;
}
if(l_hdr->nlmsg_type == NLMSG_DONE)
{
break;
}
if(l_hdr->nlmsg_type == RTM_NEWLINK)
{
if(interpretLink(l_hdr, p_resultList) == -1)
{
return -1;
}
++l_numLinks;
}
}
}
return l_numLinks;
}
static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList, int p_numLinks)
{ {
pid_t l_pid = getpid(); pid_t l_pid = getpid();
for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next) for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
{ {
unsigned int l_nlsize = p_netlinkList->m_size; unsigned int l_nlsize = p_netlinkList->m_size;
struct nlmsghdr *l_hdr = p_netlinkList->m_data; struct nlmsghdr *l_hdr;
for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
for(; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) {
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
{ {
if( (pid_t)l_hdr->nlmsg_pid != l_pid
|| (int)l_hdr->nlmsg_seq != p_socket)
continue; continue;
}
if(l_hdr->nlmsg_type == NLMSG_DONE) if(l_hdr->nlmsg_type == NLMSG_DONE)
{
break; break;
if(l_hdr->nlmsg_type == RTM_NEWLINK)
interpretLink(l_hdr, p_links, p_resultList);
else if(l_hdr->nlmsg_type == RTM_NEWADDR)
interpretAddr(l_hdr, p_links, p_resultList);
}
}
} }
static unsigned countLinks(int p_socket, NetlinkList *p_netlinkList) if(l_hdr->nlmsg_type == RTM_NEWADDR)
{ {
pid_t l_pid; if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1)
unsigned l_links = 0;
for(l_pid = getpid(); p_netlinkList; p_netlinkList = p_netlinkList->m_next)
{ {
unsigned int l_nlsize = p_netlinkList->m_size; return -1;
struct nlmsghdr *l_hdr = p_netlinkList->m_data;
for(; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
{
if( (pid_t)l_hdr->nlmsg_pid != l_pid
|| (int)l_hdr->nlmsg_seq != p_socket)
continue;
if(l_hdr->nlmsg_type == NLMSG_DONE)
break;
if(l_hdr->nlmsg_type == RTM_NEWLINK)
++l_links;
} }
} }
}
return l_links; }
return 0;
} }
int getifaddrs(struct ifaddrs **ifap) int getifaddrs(struct ifaddrs **ifap)
{ {
int l_socket;
unsigned l_numLinks;
struct ifaddrs *l_links = NULL;
NetlinkList *l_linkResults = NULL;
NetlinkList *l_addrResults = NULL;
if(!ifap) if(!ifap)
{
return -1; return -1;
}
*ifap = NULL; *ifap = NULL;
l_socket = netlink_socket(); int l_socket = netlink_socket();
if(l_socket < 0) if(l_socket < 0)
{
return -1; return -1;
}
l_linkResults = getResultList(l_socket, RTM_GETLINK); NetlinkList *l_linkResults = getResultList(l_socket, RTM_GETLINK);
if(!l_linkResults) if(!l_linkResults)
goto error; {
close(l_socket);
l_addrResults = getResultList(l_socket, RTM_GETADDR); return -1;
}
NetlinkList *l_addrResults = getResultList(l_socket, RTM_GETADDR);
if(!l_addrResults) if(!l_addrResults)
goto error; {
l_numLinks = countLinks(l_socket, l_linkResults)
+ countLinks(l_socket, l_addrResults);
l_links = (struct ifaddrs*)calloc(l_numLinks, sizeof(struct ifaddrs));
interpret(l_socket, l_linkResults, &l_links, ifap);
interpret(l_socket, l_addrResults, &l_links, ifap);
freeResultList(l_linkResults);
freeResultList(l_addrResults);
close(l_socket); close(l_socket);
return 0;
error:
close(l_socket);
if (l_linkResults)
freeResultList(l_linkResults); freeResultList(l_linkResults);
return -1; return -1;
} }
int l_result = 0;
int l_numLinks = interpretLinks(l_socket, l_linkResults, ifap);
if(l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1)
{
l_result = -1;
}
freeResultList(l_linkResults);
freeResultList(l_addrResults);
close(l_socket);
return l_result;
}
void freeifaddrs(struct ifaddrs *ifa) void freeifaddrs(struct ifaddrs *ifa)
{ {
struct ifaddrs *l_cur = NULL; struct ifaddrs *l_cur;
while(ifa) while(ifa)
{ {
l_cur = ifa; l_cur = ifa;