#include <sys/ioctl.h>
#include <sys/sockio.h>
#define ifaddrs ifa_info
#ifndef ifa_broadaddr
#define ifa_broadaddr ifa_dstaddr
#endif
#include <sys/cdefs.h>
#define IFA_NAME 16
#define IFA_HADDR 8
struct ifa_info {
char ifa_name[IFA_NAME];
u_char ifa_haddr[IFA_HADDR];
u_short ifa_hlen;
short ifa_flags;
short ifa_myflags;
struct sockaddr *ifa_addr;
struct sockaddr *ifa_brdaddr;
struct sockaddr *ifa_dstaddr;
struct ifa_info *ifa_next;
};
#define IFI_ALIAS 1
struct ifa_info *get_ifa_info(int, int);
struct ifa_info *Get_ifa_info(int, int);
void free_ifa_info(struct ifa_info *);
#define HAVE_SOCKADDR_SA_LEN 1
struct ifa_info *
get_ifa_info(int family, int doaliases)
{
struct ifa_info *ifi, *ifihead, **ifipnext;
int sockfd, len, lastlen, flags, myflags;
char *ptr, *buf, lastname[IFNAMSIZ], *cptr;
struct ifconf ifc;
struct ifreq *ifr, ifrcopy;
struct sockaddr_in *sinptr;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
lastlen = 0;
len = 100 * sizeof(struct ifreq);
for ( ; ; ) {
buf = (char *) malloc(len);
ifc.ifc_len = len;
ifc.ifc_buf = buf;
if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
if (errno != EINVAL || lastlen != 0)
debugf("ioctl error");
} else {
if (ifc.ifc_len == lastlen)
break;
lastlen = ifc.ifc_len;
}
len += 10 * sizeof(struct ifreq);
free(buf);
}
ifihead = NULL;
ifipnext = &ifihead;
lastname[0] = 0;
for (ptr = buf; ptr < buf + ifc.ifc_len; ) {
ifr = (struct ifreq *) ptr;
#ifdef HAVE_SOCKADDR_SA_LEN
len = MAX(sizeof(struct sockaddr), ifr->ifr_addr.sa_len);
#else
switch (ifr->ifr_addr.sa_family) {
#ifdef IPV6
case AF_INET6:
len = sizeof(struct sockaddr_in6);
break;
#endif
case AF_INET:
default:
len = sizeof(struct sockaddr);
break;
}
#endif
ptr += sizeof(ifr->ifr_name) + len;
if (ifr->ifr_addr.sa_family != family)
continue;
myflags = 0;
if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL)
*cptr = 0;
if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) {
if (doaliases == 0)
continue;
myflags = IFI_ALIAS;
}
memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
ifrcopy = *ifr;
ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
flags = ifrcopy.ifr_flags;
if ((flags & IFF_UP) == 0)
continue;
ifi = (struct ifa_info *) calloc(1, sizeof(struct ifa_info));
*ifipnext = ifi;
ifipnext = &ifi->ifa_next;
ifi->ifa_flags = flags;
ifi->ifa_myflags = myflags;
memcpy(ifi->ifa_name, ifr->ifr_name, IFA_NAME);
ifi->ifa_name[IFA_NAME-1] = '\0';
switch (ifr->ifr_addr.sa_family) {
case AF_INET:
sinptr = (struct sockaddr_in *) &ifr->ifr_addr;
if (ifi->ifa_addr == NULL) {
ifi->ifa_addr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in));
memcpy(ifi->ifa_addr, sinptr, sizeof(struct sockaddr_in));
#ifdef SIOCGIFBRDADDR
if (flags & IFF_BROADCAST) {
ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy);
sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr;
ifi->ifa_brdaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in));
memcpy(ifi->ifa_brdaddr, sinptr, sizeof(struct sockaddr_in));
}
#endif
#ifdef SIOCGIFDSTADDR
if (flags & IFF_POINTOPOINT) {
ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr;
ifi->ifa_dstaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in));
memcpy(ifi->ifa_dstaddr, sinptr, sizeof(struct sockaddr_in));
}
#endif
}
break;
default:
break;
}
}
free(buf);
return(ifihead);
}
mDNSlocal void freeifaddrs(struct ifa_info *ifihead)
{
struct ifa_info *ifi, *ifinext;
for (ifi = ifihead; ifi != NULL; ifi = ifinext) {
if (ifi->ifa_addr != NULL)
free(ifi->ifa_addr);
if (ifi->ifa_brdaddr != NULL)
free(ifi->ifa_brdaddr);
if (ifi->ifa_dstaddr != NULL)
free(ifi->ifa_dstaddr);
ifinext = ifi->ifa_next;
free(ifi);
}
}
struct ifa_info *
Get_ifa_info(int family, int doaliases)
{
struct ifa_info *ifi;
if ( (ifi = get_ifa_info(family, doaliases)) == NULL)
debugf("get_ifa_info error");
return(ifi);
}
mDNSlocal int getifaddrs(struct ifa_info **ifalist)
{
*ifalist = get_ifa_info(PF_INET, false);
if( ifalist == nil )
return -1;
else
return(0);
}