mirror of
https://github.com/fluencelabs/musl
synced 2025-07-30 22:01:57 +00:00
add getifaddrs
supports ipv4 and ipv6, but not the "extended" usage where usage statistics and other info are assigned to ifa_data members of duplicate entries with AF_PACKET family.
This commit is contained in:
191
src/network/getifaddrs.c
Normal file
191
src/network/getifaddrs.c
Normal file
@@ -0,0 +1,191 @@
|
||||
/* (C) 2013 John Spencer. released under musl's standard MIT license. */
|
||||
#undef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#include <ifaddrs.h>
|
||||
#include <stdlib.h>
|
||||
#include <net/if.h> /* IFNAMSIZ, ifreq, ifconf */
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h> /* inet_pton */
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
static struct ifaddrs* list_add(struct ifaddrs** list, struct ifaddrs** head, char* ifname)
|
||||
{
|
||||
struct ifaddrs* curr = calloc(1, sizeof(struct ifaddrs));
|
||||
if(curr) {
|
||||
curr->ifa_name = strdup(ifname);
|
||||
if(!curr->ifa_name) {
|
||||
free(curr);
|
||||
curr = 0;
|
||||
goto out;
|
||||
}
|
||||
if(*head) (*head)->ifa_next = curr;
|
||||
*head = curr;
|
||||
if(!*list) *list = curr;
|
||||
}
|
||||
out:
|
||||
return curr;
|
||||
}
|
||||
|
||||
void freeifaddrs(struct ifaddrs *ifp)
|
||||
{
|
||||
struct ifaddrs *head = 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;
|
||||
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)
|
||||
{
|
||||
FILE* f = fopen("/proc/net/if_inet6", "r");
|
||||
/* 00000000000000000000000000000001 01 80 10 80 lo
|
||||
A B C D E F
|
||||
all numbers in hex
|
||||
A = addr B=netlink device#, C=prefix length,
|
||||
D = scope value (ipv6.h) E = interface flags (rnetlink.h, addrconf.c)
|
||||
F = if name */
|
||||
char v6conv[32 + 7 + 1], *v6;
|
||||
char *line, linebuf[512];
|
||||
if(!f) return;
|
||||
while((line = fgets(linebuf, sizeof linebuf, f))) {
|
||||
v6 = v6conv;
|
||||
size_t i = 0;
|
||||
for(; i < 8; i++) {
|
||||
memcpy(v6, line, 4);
|
||||
v6+=4;
|
||||
*v6++=':';
|
||||
line+=4;
|
||||
}
|
||||
--v6; *v6 = 0;
|
||||
line++;
|
||||
unsigned b, c, d, e;
|
||||
char name[IFNAMSIZ+1];
|
||||
if(5 == sscanf(line, "%x %x %x %x %s", &b, &c, &d, &e, name)) {
|
||||
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);
|
||||
if(!curr) goto out;
|
||||
curr->ifa_addr = sockaddr_in6_dup(&sa);
|
||||
ipv6netmask(c, &sa);
|
||||
curr->ifa_netmask = sockaddr_in6_dup(&sa);
|
||||
/* 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;
|
||||
} else errno = 0;
|
||||
}
|
||||
}
|
||||
out:
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
int getifaddrs(struct ifaddrs **ifap)
|
||||
{
|
||||
FILE* f = fopen("/proc/net/dev", "r");
|
||||
/* 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;
|
||||
|
||||
char* line; char linebuf[512];
|
||||
while((line = fgets(linebuf, sizeof linebuf, f))) {
|
||||
while(isspace(*line) && *line) line++;
|
||||
char* start = line;
|
||||
while(*line && isalnum(*line)) line++;
|
||||
if(line > start && *line == ':') {
|
||||
// found interface
|
||||
*line = 0;
|
||||
struct ifaddrs* curr = list_add(&list, &head, start);
|
||||
if(!curr) {
|
||||
fclose(f);
|
||||
goto err2;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if(sock == -1) goto err2;
|
||||
struct ifreq reqs[32]; /* arbitrary chosen boundary */
|
||||
struct ifconf conf = {.ifc_len = sizeof reqs, .ifc_req = reqs};
|
||||
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) {
|
||||
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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
struct ifreq req;
|
||||
snprintf(req.ifr_name, sizeof req.ifr_name, "%s", head->ifa_name);
|
||||
if(-1 == ioctl(sock, SIOCGIFFLAGS, &req)) goto err;
|
||||
|
||||
head->ifa_flags = req.ifr_flags;
|
||||
if(head->ifa_addr) {
|
||||
/* or'ing flags with IFF_LOWER_UP on active interfaces to mimic glibc */
|
||||
head->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);
|
||||
|
||||
if(head->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);
|
||||
} else {
|
||||
if(-1 == ioctl(sock, SIOCGIFBRDADDR, &req)) goto err;
|
||||
head->ifa_ifu.ifu_broadaddr = sockaddr_in_dup((struct sockaddr_in*) &req.ifr_broadaddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
close(sock);
|
||||
void* last = 0;
|
||||
for(head = list; head; head=head->ifa_next) last=head;
|
||||
head = last;
|
||||
dealwithipv6(&list, &head);
|
||||
*ifap = list;
|
||||
return 0;
|
||||
err:
|
||||
close(sock);
|
||||
err2:
|
||||
freeifaddrs(list);
|
||||
return -1;
|
||||
}
|
||||
|
Reference in New Issue
Block a user