#include <sys_defs.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <unistd.h>
#ifdef USE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif
#include <errno.h>
#include <string.h>
#ifdef HAS_IPV6
#include <netdb.h>
#include <stdio.h>
#endif
#ifdef HAVE_GETIFADDRS
#include <ifaddrs.h>
#endif
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <inet_addr_list.h>
#include <inet_addr_local.h>
#include <myaddrinfo.h>
#include <sock_addr.h>
#include <mask_addr.h>
#include <hex_code.h>
static int ial_socket(int af)
{
const char *myname = "inet_addr_local[socket]";
int sock;
if ((sock = socket(af, SOCK_DGRAM, 0)) < 0) {
#ifdef HAS_IPV6
if (af == AF_INET6) {
if (msg_verbose)
msg_warn("%s: socket: %m", myname);
return (-1);
}
#endif
msg_fatal("%s: socket: %m", myname);
}
return (sock);
}
#ifdef HAVE_GETIFADDRS
static int ial_getifaddrs(INET_ADDR_LIST *addr_list,
INET_ADDR_LIST *mask_list,
int af)
{
const char *myname = "inet_addr_local[getifaddrs]";
struct ifaddrs *ifap, *ifa;
struct sockaddr *sa, *sam;
if (getifaddrs(&ifap) < 0)
msg_fatal("%s: getifaddrs: %m", myname);
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (!(ifa->ifa_flags & IFF_UP) || ifa->ifa_addr == 0)
continue;
sa = ifa->ifa_addr;
if (af != AF_UNSPEC && sa->sa_family != af)
continue;
sam = ifa->ifa_netmask;
if (sam == 0) {
msg_warn("ignoring interface with null netmask, address family %d",
sa->sa_family);
continue;
}
switch (sa->sa_family) {
case AF_INET:
if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY)
continue;
break;
#ifdef HAS_IPV6
case AF_INET6:
if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))
continue;
break;
#endif
default:
continue;
}
inet_addr_list_append(addr_list, sa);
if (mask_list != 0) {
#ifdef HAS_SA_LEN
sam->sa_len = sa->sa_family == AF_INET6 ?
sizeof(struct sockaddr_in6) :
sizeof(struct sockaddr_in);
#endif
sam->sa_family = sa->sa_family;
inet_addr_list_append(mask_list, sam);
}
}
freeifaddrs(ifap);
return (0);
}
#endif
#ifdef HAS_SIOCGLIF
#define NEXT_INTERFACE(lifr) (lifr + 1)
#define LIFREQ_SIZE(lifr) sizeof(lifr[0])
static int ial_siocglif(INET_ADDR_LIST *addr_list,
INET_ADDR_LIST *mask_list,
int af)
{
const char *myname = "inet_addr_local[siocglif]";
struct lifconf lifc;
struct lifreq *lifr;
struct lifreq *lifr_mask;
struct lifreq *the_end;
struct sockaddr *sa;
int sock;
VSTRING *buf;
if (af != AF_INET && af != AF_INET6)
msg_fatal("%s: address family was %d, must be AF_INET (%d) or "
"AF_INET6 (%d)", myname, af, AF_INET, AF_INET6);
sock = ial_socket(af);
if (sock < 0)
return (0);
buf = vstring_alloc(1024);
for (;;) {
memset(&lifc, 0, sizeof(lifc));
lifc.lifc_family = AF_UNSPEC;
lifc.lifc_len = vstring_avail(buf);
lifc.lifc_buf = vstring_str(buf);
if (ioctl(sock, SIOCGLIFCONF, (char *) &lifc) < 0) {
if (errno != EINVAL)
msg_fatal("%s: ioctl SIOCGLIFCONF: %m", myname);
} else if (lifc.lifc_len < vstring_avail(buf) / 2)
break;
VSTRING_SPACE(buf, vstring_avail(buf) * 2);
}
the_end = (struct lifreq *) (lifc.lifc_buf + lifc.lifc_len);
for (lifr = lifc.lifc_req; lifr < the_end;) {
sa = (struct sockaddr *) & lifr->lifr_addr;
if (sa->sa_family != af) {
lifr = NEXT_INTERFACE(lifr);
continue;
}
if (af == AF_INET) {
if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY) {
lifr = NEXT_INTERFACE(lifr);
continue;
}
#ifdef HAS_IPV6
} else if (af == AF_INET6) {
if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa))) {
lifr = NEXT_INTERFACE(lifr);
continue;
}
}
#endif
inet_addr_list_append(addr_list, sa);
if (mask_list) {
lifr_mask = (struct lifreq *) mymalloc(sizeof(struct lifreq));
memcpy((char *) lifr_mask, (char *) lifr, sizeof(struct lifreq));
if (ioctl(sock, SIOCGLIFNETMASK, lifr_mask) < 0)
msg_fatal("%s: ioctl(SIOCGLIFNETMASK): %m", myname);
inet_addr_list_append(mask_list,
(struct sockaddr *) & lifr_mask->lifr_addr);
myfree((char *) lifr_mask);
}
lifr = NEXT_INTERFACE(lifr);
}
vstring_free(buf);
(void) close(sock);
return (0);
}
#else
#if defined(_SIZEOF_ADDR_IFREQ)
#define NEXT_INTERFACE(ifr) ((struct ifreq *) \
((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr)))
#define IFREQ_SIZE(ifr) _SIZEOF_ADDR_IFREQ(*ifr)
#elif defined(HAS_SA_LEN)
#define NEXT_INTERFACE(ifr) ((struct ifreq *) \
((char *) ifr + sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len))
#define IFREQ_SIZE(ifr) (sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len)
#else
#define NEXT_INTERFACE(ifr) (ifr + 1)
#define IFREQ_SIZE(ifr) sizeof(ifr[0])
#endif
static int ial_siocgif(INET_ADDR_LIST *addr_list,
INET_ADDR_LIST *mask_list,
int af)
{
const char *myname = "inet_addr_local[siocgif]";
struct in_addr addr;
struct ifconf ifc;
struct ifreq *ifr;
struct ifreq *ifr_mask;
struct ifreq *the_end;
int sock;
VSTRING *buf;
sock = ial_socket(af);
if (sock < 0)
return (0);
buf = vstring_alloc(1024);
for (;;) {
ifc.ifc_len = vstring_avail(buf);
ifc.ifc_buf = vstring_str(buf);
if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) {
if (errno != EINVAL)
msg_fatal("%s: ioctl SIOCGIFCONF: %m", myname);
} else if (ifc.ifc_len < vstring_avail(buf) / 2)
break;
VSTRING_SPACE(buf, vstring_avail(buf) * 2);
}
the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
for (ifr = ifc.ifc_req; ifr < the_end;) {
if (ifr->ifr_addr.sa_family != af) {
ifr = NEXT_INTERFACE(ifr);
continue;
}
if (af == AF_INET) {
addr = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr;
if (addr.s_addr != INADDR_ANY) {
inet_addr_list_append(addr_list, &ifr->ifr_addr);
if (mask_list) {
ifr_mask = (struct ifreq *) mymalloc(IFREQ_SIZE(ifr));
memcpy((char *) ifr_mask, (char *) ifr, IFREQ_SIZE(ifr));
if (ioctl(sock, SIOCGIFNETMASK, ifr_mask) < 0)
msg_fatal("%s: ioctl SIOCGIFNETMASK: %m", myname);
ifr_mask->ifr_addr.sa_family = af;
#ifdef HAS_SA_LEN
ifr_mask->ifr_addr.sa_len = sizeof(struct sockaddr_in);
#endif
inet_addr_list_append(mask_list, &ifr_mask->ifr_addr);
myfree((char *) ifr_mask);
}
}
}
#ifdef HAS_IPV6
else if (af == AF_INET6) {
struct sockaddr *sa;
sa = SOCK_ADDR_PTR(&ifr->ifr_addr);
if (!(IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))) {
inet_addr_list_append(addr_list, sa);
if (mask_list) {
struct sockaddr_in6 mask6;
mask6 = *SOCK_ADDR_IN6_PTR(sa);
memset((char *) &mask6.sin6_addr, ~0,
sizeof(mask6.sin6_addr));
inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask6));
}
}
}
#endif
ifr = NEXT_INTERFACE(ifr);
}
vstring_free(buf);
(void) close(sock);
return (0);
}
#endif
#ifdef HAS_PROCNET_IFINET6
static int ial_procnet_ifinet6(INET_ADDR_LIST *addr_list,
INET_ADDR_LIST *mask_list)
{
const char *myname = "inet_addr_local[procnet_ifinet6]";
FILE *fp;
char buf[BUFSIZ];
unsigned plen;
VSTRING *addrbuf;
struct sockaddr_in6 addr;
struct sockaddr_in6 mask;
if ((fp = fopen(_PATH_PROCNET_IFINET6, "r")) != 0) {
addrbuf = vstring_alloc(MAI_V6ADDR_BYTES + 1);
memset((char *) &addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
#ifdef HAS_SA_LEN
addr.sin6_len = sizeof(addr);
#endif
mask = addr;
while (fgets(buf, sizeof(buf), fp) != 0) {
if (hex_decode(addrbuf, buf, MAI_V6ADDR_BYTES * 2) == 0
|| sscanf(buf + MAI_V6ADDR_BYTES * 2, " %*x %x", &plen) != 1
|| plen > MAI_V6ADDR_BITS) {
msg_warn("unexpected data in %s - skipping IPv6 configuration",
_PATH_PROCNET_IFINET6);
break;
}
addr.sin6_addr = *(struct in6_addr *) vstring_str(addrbuf);
inet_addr_list_append(addr_list, SOCK_ADDR_PTR(&addr));
memset((char *) &mask.sin6_addr, ~0, sizeof(mask.sin6_addr));
mask_addr((unsigned char *) &mask.sin6_addr,
sizeof(mask.sin6_addr), plen);
inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask));
}
vstring_free(addrbuf);
fclose(fp);
} else {
msg_warn("can't open %s (%m) - skipping IPv6 configuration",
_PATH_PROCNET_IFINET6);
}
return (0);
}
#endif
int inet_addr_local(INET_ADDR_LIST *addr_list, INET_ADDR_LIST *mask_list,
unsigned *addr_family_list)
{
const char *myname = "inet_addr_local";
int initial_count = addr_list->used;
unsigned family;
int count;
while ((family = *addr_family_list++) != 0) {
if (family == AF_INET) {
count = addr_list->used;
#if defined(HAVE_GETIFADDRS)
ial_getifaddrs(addr_list, mask_list, AF_INET);
#elif defined (HAS_SIOCGLIF)
ial_siocglif(addr_list, mask_list, AF_INET);
#else
ial_siocgif(addr_list, mask_list, AF_INET);
#endif
if (msg_verbose)
msg_info("%s: configured %d IPv4 addresses",
myname, addr_list->used - count);
}
#ifdef HAS_IPV6
else if (family == AF_INET6) {
count = addr_list->used;
#if defined(HAVE_GETIFADDRS)
ial_getifaddrs(addr_list, mask_list, AF_INET6);
#elif defined(HAS_PROCNET_IFINET6)
ial_procnet_ifinet6(addr_list, mask_list);
#elif defined(HAS_SIOCGLIF)
ial_siocglif(addr_list, mask_list, AF_INET6);
#else
ial_siocgif(addr_list, mask_list, AF_INET6);
#endif
if (msg_verbose)
msg_info("%s: configured %d IPv6 addresses", myname,
addr_list->used - count);
}
#endif
else
msg_panic("%s: unknown address family %d", myname, family);
}
return (addr_list->used - initial_count);
}
#ifdef TEST
#include <string.h>
#include <vstream.h>
#include <msg_vstream.h>
#include <inet_proto.h>
int main(int unused_argc, char **argv)
{
INET_ADDR_LIST addr_list;
INET_ADDR_LIST mask_list;
MAI_HOSTADDR_STR hostaddr;
MAI_HOSTADDR_STR hostmask;
struct sockaddr *sa;
int i;
INET_PROTO_INFO *proto_info;
msg_vstream_init(argv[0], VSTREAM_ERR);
msg_verbose = 1;
proto_info = inet_proto_init(argv[0],
argv[1] ? argv[1] : INET_PROTO_NAME_ALL);
inet_addr_list_init(&addr_list);
inet_addr_list_init(&mask_list);
inet_addr_local(&addr_list, &mask_list, proto_info->ai_family_list);
if (addr_list.used == 0)
msg_fatal("cannot find any active network interfaces");
if (addr_list.used == 1)
msg_warn("found only one active network interface");
for (i = 0; i < addr_list.used; i++) {
sa = SOCK_ADDR_PTR(addr_list.addrs + i);
SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa),
&hostaddr, (MAI_SERVPORT_STR *) 0, 0);
sa = SOCK_ADDR_PTR(mask_list.addrs + i);
SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa),
&hostmask, (MAI_SERVPORT_STR *) 0, 0);
vstream_printf("%s/%s\n", hostaddr.buf, hostmask.buf);
vstream_fflush(VSTREAM_OUT);
}
inet_addr_list_free(&addr_list);
inet_addr_list_free(&mask_list);
return (0);
}
#endif