diff options
Diffstat (limited to 'utils/open-isns/sysdep-unix.c')
-rw-r--r-- | utils/open-isns/sysdep-unix.c | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/utils/open-isns/sysdep-unix.c b/utils/open-isns/sysdep-unix.c new file mode 100644 index 0000000..8c601a7 --- /dev/null +++ b/utils/open-isns/sysdep-unix.c @@ -0,0 +1,132 @@ +/* + * System dependent stuff + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <net/if.h> +#include <sys/ioctl.h> +#include <string.h> +#include <unistd.h> +#include "isns.h" +#include "util.h" + +int +isns_enumerate_portals(isns_portal_info_t *result, unsigned int max) +{ + char buffer[8192], *end, *ptr; + struct ifconf ifc; + unsigned int nportals = 0; + int fd = -1; + + if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + isns_error("%s: no socket - %m\n", __FUNCTION__); + return 0; + } + + ifc.ifc_buf = buffer; + ifc.ifc_len = sizeof(buffer); + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { + isns_error("ioctl(SIOCGIFCONF): %m\n"); + goto out; + } + + ptr = buffer; + end = buffer + ifc.ifc_len; + while (ptr < end) { + struct ifreq ifr; + struct sockaddr_storage ifaddr; + isns_portal_info_t portal; + int ifflags; + + memcpy(&ifr, ptr, sizeof(ifr)); + ptr += sizeof(ifr); + + /* Get the interface addr */ + memcpy(&ifaddr, &ifr.ifr_addr, sizeof(ifr.ifr_addr)); + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { + isns_error("ioctl(%s, SIOCGIFFLAGS): %m\n", + ifr.ifr_name); + continue; + } + ifflags = ifr.ifr_flags; + + if ((ifflags & IFF_UP) == 0) + continue; + if ((ifflags & IFF_LOOPBACK) != 0) + continue; + + if (!isns_portal_from_sockaddr(&portal, &ifaddr)) + continue; + + isns_debug_socket("Got interface %u: %s %s\n", + nportals, ifr.ifr_name, + isns_portal_string(&portal)); + if (nportals < max) + result[nportals++] = portal; + } + +out: + if (fd >= 0) + close(fd); + return nportals; +} + +int +isns_portal_from_sockaddr(isns_portal_info_t *portal, + const struct sockaddr_storage *addr) +{ + struct sockaddr_in6 *six; + struct sockaddr_in *sin; + + memset(portal, 0, sizeof(*portal)); + + /* May have to convert AF_INET to AF_INET6 */ + six = &portal->addr; + switch (addr->ss_family) { + case AF_INET6: + memcpy(six, addr, sizeof(*six)); + break; + + case AF_INET: + sin = (struct sockaddr_in *) addr; + six->sin6_family = AF_INET6; + six->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr; + six->sin6_port = sin->sin_port; + break; + + default: + return 0; + } + + return 1; +} + +int +isns_portal_to_sockaddr(const isns_portal_info_t *portal, + struct sockaddr_storage *addr) +{ + const struct sockaddr_in6 *six = &portal->addr; + struct sockaddr_in *sin; + + /* Check if this is really a v4 address is disguise. + * If so, explicitly use an AF_INET socket - the + * stack may not support IPv6. + */ + if (IN6_IS_ADDR_V4MAPPED(&six->sin6_addr) + || IN6_IS_ADDR_V4COMPAT(&six->sin6_addr)) { + sin = (struct sockaddr_in *) addr; + + memset(sin, 0, sizeof(*sin)); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = six->sin6_addr.s6_addr32[3]; + sin->sin_port = six->sin6_port; + + return sizeof(*sin); + } + + /* This is the genuine article */ + memcpy(addr, six, sizeof(*six)); + return sizeof(*six); +} |