|
|
|
@ -12,17 +12,27 @@ |
|
|
|
#include <unistd.h> |
|
|
|
#include <sys/ioctl.h> |
|
|
|
|
|
|
|
static struct ifaddrs* list_add(struct ifaddrs** list, struct ifaddrs** head, char* ifname) |
|
|
|
typedef union { |
|
|
|
struct sockaddr_in6 v6; |
|
|
|
struct sockaddr_in v4; |
|
|
|
} soa; |
|
|
|
|
|
|
|
typedef struct ifaddrs_storage { |
|
|
|
struct ifaddrs ifa; |
|
|
|
soa addr; |
|
|
|
soa netmask; |
|
|
|
soa dst; |
|
|
|
char name[IFNAMSIZ+1]; |
|
|
|
} stor; |
|
|
|
#define next ifa.ifa_next |
|
|
|
|
|
|
|
static stor* list_add(stor** list, stor** head, char* ifname) |
|
|
|
{ |
|
|
|
struct ifaddrs* curr = calloc(1, sizeof(struct ifaddrs)); |
|
|
|
stor* curr = calloc(1, sizeof(stor)); |
|
|
|
if(curr) { |
|
|
|
curr->ifa_name = strdup(ifname); |
|
|
|
if(!curr->ifa_name) { |
|
|
|
free(curr); |
|
|
|
curr = 0; |
|
|
|
goto out; |
|
|
|
} |
|
|
|
if(*head) (*head)->ifa_next = curr; |
|
|
|
strcpy(curr->name, ifname); |
|
|
|
curr->ifa.ifa_name = curr->name; |
|
|
|
if(*head) (*head)->next = (struct ifaddrs*) curr; |
|
|
|
*head = curr; |
|
|
|
if(!*list) *list = curr; |
|
|
|
} |
|
|
|
@ -32,40 +42,21 @@ static struct ifaddrs* list_add(struct ifaddrs** list, struct ifaddrs** head, ch |
|
|
|
|
|
|
|
void freeifaddrs(struct ifaddrs *ifp) |
|
|
|
{ |
|
|
|
struct ifaddrs *head = ifp; |
|
|
|
stor *head = (stor *) ifp; |
|
|
|
while(head) { |
|
|
|
free(head->ifa_name); |
|
|
|
free(head->ifa_addr); |
|
|
|
free(head->ifa_netmask); |
|
|
|
free(head->ifa_ifu.ifu_dstaddr); |
|
|
|
free(head->ifa_data); |
|
|
|
void *p = head; |
|
|
|
head = head->ifa_next; |
|
|
|
head = (stor *) head->next; |
|
|
|
free(p); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static struct sockaddr *sockaddr_in_dup(struct sockaddr_in *src) |
|
|
|
{ |
|
|
|
struct sockaddr_in *nu = malloc(sizeof(struct sockaddr_in)); |
|
|
|
if(nu) *nu = *src; |
|
|
|
return (struct sockaddr*) nu; |
|
|
|
} |
|
|
|
|
|
|
|
static struct sockaddr *sockaddr_in6_dup(struct sockaddr_in6 *src) |
|
|
|
{ |
|
|
|
struct sockaddr_in6 *nu = malloc(sizeof(struct sockaddr_in6)); |
|
|
|
if(nu) *nu = *src; |
|
|
|
return (struct sockaddr*) nu; |
|
|
|
} |
|
|
|
|
|
|
|
static void ipv6netmask(unsigned prefix_length, struct sockaddr_in6 *sa) |
|
|
|
{ |
|
|
|
// FIXME: left for bit-wizard rich
|
|
|
|
memset(&sa->sin6_addr, -1, sizeof(sa->sin6_addr)); |
|
|
|
} |
|
|
|
|
|
|
|
static void dealwithipv6(struct ifaddrs **list, struct ifaddrs** head) |
|
|
|
static void dealwithipv6(stor **list, stor** head) |
|
|
|
{ |
|
|
|
FILE* f = fopen("/proc/net/if_inet6", "r"); |
|
|
|
/* 00000000000000000000000000000001 01 80 10 80 lo
|
|
|
|
@ -94,16 +85,18 @@ static void dealwithipv6(struct ifaddrs **list, struct ifaddrs** head) |
|
|
|
struct sockaddr_in6 sa = {0}; |
|
|
|
if(1 == inet_pton(AF_INET6, v6conv, &sa.sin6_addr)) { |
|
|
|
sa.sin6_family = AF_INET6; |
|
|
|
struct ifaddrs* curr = list_add(list, head, name); |
|
|
|
stor* curr = list_add(list, head, name); |
|
|
|
if(!curr) goto out; |
|
|
|
curr->ifa_addr = sockaddr_in6_dup(&sa); |
|
|
|
curr->addr.v6 = sa; |
|
|
|
curr->ifa.ifa_addr = (struct sockaddr*) &curr->addr; |
|
|
|
ipv6netmask(c, &sa); |
|
|
|
curr->ifa_netmask = sockaddr_in6_dup(&sa); |
|
|
|
curr->netmask.v6 = sa; |
|
|
|
curr->ifa.ifa_netmask = (struct sockaddr*) &curr->netmask; |
|
|
|
/* find ipv4 struct with the same interface name to copy flags */ |
|
|
|
struct ifaddrs* scan = *list; |
|
|
|
for(;scan && strcmp(name, scan->ifa_name);scan=scan->ifa_next); |
|
|
|
if(scan) curr->ifa_flags=scan->ifa_flags; |
|
|
|
else curr->ifa_flags = 0; |
|
|
|
stor* scan = *list; |
|
|
|
for(;scan && strcmp(name, scan->name);scan=(stor*)scan->next); |
|
|
|
if(scan) curr->ifa.ifa_flags = scan->ifa.ifa_flags; |
|
|
|
else curr->ifa.ifa_flags = 0; |
|
|
|
} else errno = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -114,12 +107,13 @@ static void dealwithipv6(struct ifaddrs **list, struct ifaddrs** head) |
|
|
|
int getifaddrs(struct ifaddrs **ifap) |
|
|
|
{ |
|
|
|
FILE* f = fopen("/proc/net/dev", "r"); |
|
|
|
if(!f) return -1; |
|
|
|
|
|
|
|
/* the alternative to parsing /proc.. seems to be iterating
|
|
|
|
through the interfaces using an index number in ifreq.ifr_ifindex |
|
|
|
until we get some error code back. the kernel will fill ifr_name field |
|
|
|
for valid ifindices (SIOCGIFINDEX) */ |
|
|
|
if(!f) return -1; |
|
|
|
struct ifaddrs *list = 0, *head = 0; |
|
|
|
stor *list = 0, *head = 0; |
|
|
|
|
|
|
|
char* line; char linebuf[512]; |
|
|
|
while((line = fgets(linebuf, sizeof linebuf, f))) { |
|
|
|
@ -129,7 +123,7 @@ int getifaddrs(struct ifaddrs **ifap) |
|
|
|
if(line > start && *line == ':') { |
|
|
|
// found interface
|
|
|
|
*line = 0; |
|
|
|
struct ifaddrs* curr = list_add(&list, &head, start); |
|
|
|
stor* curr = list_add(&list, &head, start); |
|
|
|
if(!curr) { |
|
|
|
fclose(f); |
|
|
|
goto err2; |
|
|
|
@ -145,47 +139,50 @@ int getifaddrs(struct ifaddrs **ifap) |
|
|
|
if(-1 == ioctl(sock, SIOCGIFCONF, &conf)) goto err; |
|
|
|
else { |
|
|
|
size_t reqitems = conf.ifc_len / sizeof(struct ifreq); |
|
|
|
for(head = list; head; head=head->ifa_next) { |
|
|
|
for(head = list; head; head = (stor*)head->next) { |
|
|
|
size_t i; |
|
|
|
for(i = 0; i < reqitems; i++) { |
|
|
|
// get SIOCGIFADDR of active interfaces.
|
|
|
|
if(!strcmp(reqs[i].ifr_name, head->ifa_name)) { |
|
|
|
head->ifa_addr = sockaddr_in_dup((struct sockaddr_in*) &reqs[i].ifr_addr); |
|
|
|
if(!strcmp(reqs[i].ifr_name, head->name)) { |
|
|
|
head->addr.v4 = *(struct sockaddr_in*)&reqs[i].ifr_addr; |
|
|
|
head->ifa.ifa_addr = (struct sockaddr*) &head->addr; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
struct ifreq req; |
|
|
|
snprintf(req.ifr_name, sizeof req.ifr_name, "%s", head->ifa_name); |
|
|
|
snprintf(req.ifr_name, sizeof req.ifr_name, "%s", head->name); |
|
|
|
if(-1 == ioctl(sock, SIOCGIFFLAGS, &req)) goto err; |
|
|
|
|
|
|
|
head->ifa_flags = req.ifr_flags; |
|
|
|
if(head->ifa_addr) { |
|
|
|
head->ifa.ifa_flags = req.ifr_flags; |
|
|
|
if(head->ifa.ifa_addr) { |
|
|
|
/* or'ing flags with IFF_LOWER_UP on active interfaces to mimic glibc */ |
|
|
|
head->ifa_flags |= IFF_LOWER_UP; |
|
|
|
head->ifa.ifa_flags |= IFF_LOWER_UP; |
|
|
|
if(-1 == ioctl(sock, SIOCGIFNETMASK, &req)) goto err; |
|
|
|
head->ifa_netmask = sockaddr_in_dup((struct sockaddr_in*) &req.ifr_netmask); |
|
|
|
head->netmask.v4 = *(struct sockaddr_in*)&req.ifr_netmask; |
|
|
|
head->ifa.ifa_netmask = (struct sockaddr*) &head->netmask; |
|
|
|
|
|
|
|
if(head->ifa_flags & IFF_POINTOPOINT) { |
|
|
|
if(head->ifa.ifa_flags & IFF_POINTOPOINT) { |
|
|
|
if(-1 == ioctl(sock, SIOCGIFDSTADDR, &req)) goto err; |
|
|
|
head->ifa_ifu.ifu_dstaddr = sockaddr_in_dup((struct sockaddr_in*) &req.ifr_dstaddr); |
|
|
|
head->dst.v4 = *(struct sockaddr_in*)&req.ifr_dstaddr; |
|
|
|
} else { |
|
|
|
if(-1 == ioctl(sock, SIOCGIFBRDADDR, &req)) goto err; |
|
|
|
head->ifa_ifu.ifu_broadaddr = sockaddr_in_dup((struct sockaddr_in*) &req.ifr_broadaddr); |
|
|
|
head->dst.v4 = *(struct sockaddr_in*)&req.ifr_broadaddr; |
|
|
|
} |
|
|
|
head->ifa.ifa_ifu.ifu_dstaddr = (struct sockaddr*) &head->dst; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
close(sock); |
|
|
|
void* last = 0; |
|
|
|
for(head = list; head; head=head->ifa_next) last=head; |
|
|
|
for(head = list; head; head=(stor*)head->next) last=head; |
|
|
|
head = last; |
|
|
|
dealwithipv6(&list, &head); |
|
|
|
*ifap = list; |
|
|
|
*ifap = (struct ifaddrs*) list; |
|
|
|
return 0; |
|
|
|
err: |
|
|
|
close(sock); |
|
|
|
err2: |
|
|
|
freeifaddrs(list); |
|
|
|
freeifaddrs((struct ifaddrs*) list); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
|