#include "dapl.h"
#include "dapl_name_service.h"
#include <netinet/in.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_arp.h>
#include <net/if_types.h>
#include <arpa/inet.h>
#include <poll.h>
#include <ibd/ibd.h>
#ifdef IBHOSTS_NAMING
#define MAP_FILE "/etc/dapl/ibhosts"
#define MAX_GID_ENTRIES 32
DAPL_GID_MAP g_gid_map_table[MAX_GID_ENTRIES];
DAT_RETURN dapli_ns_create_gid_map(void);
DAT_RETURN dapli_ns_add_address(IN DAPL_GID_MAP *gme);
#endif
DAT_RETURN
dapls_ns_init(void)
{
DAT_RETURN dat_status;
dat_status = DAT_SUCCESS;
#ifdef IBHOSTS_NAMING
dat_status = dapli_ns_create_gid_map();
#endif
return (dat_status);
}
#ifdef IBHOSTS_NAMING
DAT_RETURN
dapli_ns_create_gid_map(void)
{
FILE *f;
ib_gid_t gid;
char hostname[128];
int rc;
struct addrinfo *addr;
struct sockaddr_in *si;
DAPL_GID_MAP gmt;
f = fopen(MAP_FILE, "r");
if (f == NULL) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR, "ERROR: Must have file <%s> "
"for IP/GID mappings\n", MAP_FILE);
return (DAT_ERROR(DAT_INTERNAL_ERROR, 0));
}
rc = fscanf(f, "%s " F64x " " F64x, hostname,
&gid.gid_prefix, &gid.gid_guid);
while (rc != EOF) {
rc = dapls_osd_getaddrinfo(hostname, &addr);
if (rc != 0) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"WARNING: <%s> not registered in "
"DNS, using dummy IP value\n", hostname);
gmt.ip_address = 0x01020304;
} else {
si = (struct sockaddr_in *)addr->ai_addr;
if (AF_INET == addr->ai_addr->sa_family) {
gmt.ip_address = si->sin_addr.s_addr;
} else {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"WARNING: <%s> Address family "
"not supported, using dummy "
"IP value\n", hostname);
gmt.ip_address = 0x01020304;
}
dapls_osd_freeaddrinfo(addr);
}
gmt.gid.gid_prefix = gid.gid_prefix;
gmt.gid.gid_guid = gid.gid_guid;
dapli_ns_add_address(&gmt);
rc = fscanf(f, "%s " F64x " " F64x, hostname,
&gid.gid_prefix, &gid.gid_guid);
}
(void) fclose(f);
return (DAT_SUCCESS);
}
DAT_RETURN
dapli_ns_add_address(
IN DAPL_GID_MAP *gme)
{
DAPL_GID_MAP *gmt;
int count;
gmt = g_gid_map_table;
for (count = 0, gmt = g_gid_map_table; gmt->ip_address; gmt++) {
count++;
}
if (count > MAX_GID_ENTRIES) {
return (DAT_ERROR(DAT_INSUFFICIENT_RESOURCES, 0));
}
*gmt = *gme;
return (DAT_SUCCESS);
}
DAT_RETURN
dapls_ns_lookup_address(
IN DAPL_IA *ia_ptr,
IN DAT_IA_ADDRESS_PTR remote_ia_address,
IN DAT_TIMEOUT timeout,
OUT ib_gid_t *gid)
{
DAPL_GID_MAP *gmt;
struct sockaddr_in *si;
ia_ptr = ia_ptr;
si = (struct sockaddr_in *)remote_ia_address;
for (gmt = g_gid_map_table; gmt->ip_address; gmt++) {
if (gmt->ip_address == si->sin_addr.s_addr) {
gid->gid_guid = gmt->gid.gid_guid;
gid->gid_prefix = gmt->gid.gid_prefix;
return (DAT_SUCCESS);
}
}
return (DAT_ERROR(DAT_INVALID_PARAMETER, 0));
}
#endif
char *
dapls_inet_ntop(struct sockaddr *addr, char *buf, size_t len)
{
void *addr_ptr;
if (addr->sa_family == AF_INET) {
addr_ptr = (void *)&((struct sockaddr_in *)addr)->sin_addr;
} else if (addr->sa_family == AF_INET6) {
addr_ptr = (void *)&((struct sockaddr_in6 *)addr)->sin6_addr;
} else {
if (len > strlen("bad address")) {
(void) sprintf(buf, "bad address");
}
return (buf);
}
return ((char *)inet_ntop(addr->sa_family, addr_ptr, buf, len));
}
#define NS_MAX_RETRIES 60
DAT_RETURN
dapls_ns_lookup_v4(
IN DAPL_IA *ia_ptr,
IN struct sockaddr_in *addr,
IN DAT_TIMEOUT timeout,
OUT ib_gid_t *gid);
DAT_RETURN
dapls_ns_lookup_v6(
IN DAPL_IA *ia_ptr,
IN struct sockaddr_in6 *addr,
IN DAT_TIMEOUT timeout,
OUT ib_gid_t *gid);
static int dapls_ns_subnet_match_v4(int s, DAPL_IA *ia_ptr,
struct sockaddr_in *addr);
static int dapls_ns_subnet_match_v6(int s, DAPL_IA *ia_ptr,
struct sockaddr_in6 *addr);
static int dapls_ns_send_packet_v6(int s, struct sockaddr_in6 *addr);
static int dapls_ns_resolve_addr(int af, struct sockaddr *addr,
DAT_TIMEOUT timeout);
DAT_RETURN
dapls_ns_lookup_address(
IN DAPL_IA *ia_ptr,
IN DAT_IA_ADDRESS_PTR remote_ia_address,
IN DAT_TIMEOUT timeout,
OUT ib_gid_t *gid)
{
DAT_RETURN dat_status;
struct sockaddr *sock = (struct sockaddr *)remote_ia_address;
if (sock->sa_family == AF_INET) {
dat_status = dapls_ns_lookup_v4(ia_ptr,
(struct sockaddr_in *)sock, timeout, gid);
} else if (sock->sa_family == AF_INET6) {
dat_status = dapls_ns_lookup_v6(ia_ptr,
(struct sockaddr_in6 *)sock, timeout, gid);
} else {
dat_status = DAT_INVALID_PARAMETER;
}
return (dat_status);
}
DAT_RETURN
dapls_ns_lookup_v4(
IN DAPL_IA *ia_ptr,
IN struct sockaddr_in *addr,
IN DAT_TIMEOUT timeout,
OUT ib_gid_t *gid)
{
struct xarpreq ar;
struct sockaddr_in *sin;
uchar_t *mac;
int s, retries = 0;
(void) dapl_os_memzero(&ar, sizeof (ar));
sin = (struct sockaddr_in *)&ar.xarp_pa;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = addr->sin_addr.s_addr;
ar.xarp_ha.sdl_family = AF_LINK;
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_lookup_v4: socket: %s\n", strerror(errno));
return (DAT_INTERNAL_ERROR);
}
if (dapls_ns_subnet_match_v4(s, ia_ptr, addr) != 0) {
(void) close(s);
return (DAT_INVALID_ADDRESS);
}
again:;
if (ioctl(s, SIOCGXARP, (caddr_t)&ar) < 0) {
if (retries <= NS_MAX_RETRIES &&
dapls_ns_resolve_addr(AF_INET, (struct sockaddr *)addr,
timeout) == 0) {
retries++;
goto again;
}
dapl_dbg_log(DAPL_DBG_TYPE_ERR, "ns_lookup_v4: giving up\n");
(void) close(s);
return (DAT_ERROR(DAT_INVALID_ADDRESS,
DAT_INVALID_ADDRESS_UNREACHABLE));
}
if ((ar.xarp_flags & ATF_COM) == 0 &&
ar.xarp_ha.sdl_type == IFT_IB && retries <= NS_MAX_RETRIES) {
retries++;
(void) sleep(1);
goto again;
}
(void) close(s);
mac = (uchar_t *)LLADDR(&ar.xarp_ha);
if (ar.xarp_flags & ATF_COM &&
ar.xarp_ha.sdl_type == IFT_IB &&
ar.xarp_ha.sdl_alen >= sizeof (ipoib_mac_t)) {
ib_gid_t tmp_gid;
(void) dapl_os_memcpy(&tmp_gid,
&((ipoib_mac_t *)mac)->ipoib_gidpref, sizeof (ib_gid_t));
gid->gid_prefix = BETOH_64(tmp_gid.gid_prefix);
gid->gid_guid = BETOH_64(tmp_gid.gid_guid);
} else {
int i, len;
len = ar.xarp_ha.sdl_alen;
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_lookup_v4: failed, non IB address: "
"len = %d, addr = 0x", len);
if (len > 0) {
for (i = 0; i < len; i++) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"%02x", (int)mac[i] & 0xff);
}
} else {
dapl_dbg_log(DAPL_DBG_TYPE_ERR, "0");
}
dapl_dbg_log(DAPL_DBG_TYPE_ERR, "\n");
return (DAT_INVALID_ADDRESS);
}
return (DAT_SUCCESS);
}
DAT_RETURN
dapls_ns_lookup_v6(
IN DAPL_IA *ia_ptr,
IN struct sockaddr_in6 *addr,
IN DAT_TIMEOUT timeout,
OUT ib_gid_t *gid)
{
struct lifreq lifr;
uchar_t *mac;
int s, retries = 0;
s = socket(AF_INET6, SOCK_DGRAM, 0);
if (s < 0) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_lookup_v6: socket: %s\n", strerror(errno));
return (DAT_INTERNAL_ERROR);
}
if (dapls_ns_subnet_match_v6(s, ia_ptr, addr) != 0) {
(void) close(s);
return (DAT_INVALID_ADDRESS);
}
(void) dapl_os_memzero(&lifr, sizeof (lifr));
(void) dapl_os_memcpy(&lifr.lifr_nd.lnr_addr, addr, sizeof (*addr));
(void) dapl_os_strcpy(lifr.lifr_name, ia_ptr->hca_ptr->name);
again:;
if (ioctl(s, SIOCLIFGETND, (caddr_t)&lifr) < 0) {
if (retries < NS_MAX_RETRIES &&
dapls_ns_send_packet_v6(s, addr) == 0 &&
dapls_ns_resolve_addr(AF_INET6, (struct sockaddr *)addr,
timeout) == 0) {
retries++;
goto again;
}
dapl_dbg_log(DAPL_DBG_TYPE_ERR, "ns_lookup_v6: giving up\n");
(void) close(s);
return (DAT_ERROR(DAT_INVALID_ADDRESS,
DAT_INVALID_ADDRESS_UNREACHABLE));
}
if (lifr.lifr_nd.lnr_hdw_len == 0 && retries <= NS_MAX_RETRIES) {
retries++;
(void) sleep(1);
goto again;
}
(void) close(s);
mac = (uchar_t *)lifr.lifr_nd.lnr_hdw_addr;
if (lifr.lifr_nd.lnr_hdw_len >= sizeof (ipoib_mac_t)) {
ib_gid_t tmp_gid;
(void) dapl_os_memcpy(&tmp_gid,
&((ipoib_mac_t *)mac)->ipoib_gidpref, sizeof (ib_gid_t));
gid->gid_prefix = BETOH_64(tmp_gid.gid_prefix);
gid->gid_guid = BETOH_64(tmp_gid.gid_guid);
} else {
int i, len;
len = lifr.lifr_nd.lnr_hdw_len;
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_lookup_v6: failed, non IB address: "
"len = %d, addr = 0x", len);
if (len > 0) {
for (i = 0; i < len; i++) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"%02x", (int)mac[i] & 0xff);
}
} else {
dapl_dbg_log(DAPL_DBG_TYPE_ERR, "0");
}
dapl_dbg_log(DAPL_DBG_TYPE_ERR, "\n");
return (DAT_INVALID_ADDRESS);
}
return (DAT_SUCCESS);
}
static int
dapls_ns_send_packet_v6(int s, struct sockaddr_in6 *addr)
{
if (sendto(s, NULL, 0, MSG_DONTROUTE, (struct sockaddr *)addr,
sizeof (*addr)) < 0) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_send_packet_v6: failed: %s\n", strerror(errno));
return (-1);
}
return (0);
}
static int
dapls_ns_subnet_match_v4(int s, DAPL_IA *ia_ptr, struct sockaddr_in *addr)
{
struct lifreq lifreq;
int retval;
uint32_t netmask, netaddr, netaddr_dest;
(void) dapl_os_strcpy(lifreq.lifr_name, ia_ptr->hca_ptr->name);
retval = ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifreq);
if (retval < 0) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_subnet_match_v4: cannot get netmask: %s\n",
strerror(errno));
return (-1);
}
netmask = ((struct sockaddr_in *)&lifreq.lifr_addr)->
sin_addr.s_addr;
retval = ioctl(s, SIOCGLIFADDR, (caddr_t)&lifreq);
if (retval < 0) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_subnet_match_v4: cannot get local addr: %s\n",
strerror(errno));
return (-1);
}
netaddr = ((struct sockaddr_in *)&lifreq.lifr_addr)->
sin_addr.s_addr & netmask;
netaddr_dest = addr->sin_addr.s_addr & netmask;
if (netaddr != netaddr_dest) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_subnet_match_v4: netaddrs don't match: "
"local %x, remote %x\n", netaddr, netaddr_dest);
return (-1);
}
return (0);
}
static int
dapls_ns_subnet_match_v6(int s, DAPL_IA *ia_ptr, struct sockaddr_in6 *addr)
{
struct lifreq lifreq;
struct sockaddr_in6 netmask_sock;
uchar_t *netmask, *local_addr, *dest_addr;
int i, retval;
(void) dapl_os_strcpy(lifreq.lifr_name, ia_ptr->hca_ptr->name);
retval = ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifreq);
if (retval < 0) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_subnet_match_v6: cannot get netmask: %s\n",
strerror(errno));
return (-1);
}
(void) dapl_os_memcpy(&netmask_sock, &lifreq.lifr_addr,
sizeof (netmask_sock));
retval = ioctl(s, SIOCGLIFADDR, (caddr_t)&lifreq);
if (retval < 0) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_subnet_match_v6: cannot get local addr: %s\n",
strerror(errno));
return (-1);
}
netmask = (uchar_t *)&netmask_sock.sin6_addr;
local_addr = (uchar_t *)&((struct sockaddr_in6 *)&lifreq.lifr_addr)->
sin6_addr;
dest_addr = (uchar_t *)&addr->sin6_addr;
for (i = 0; i < sizeof (addr->sin6_addr); i++) {
if (((local_addr[i] ^ dest_addr[i]) & netmask[i]) != 0) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_subnet_match_v6: subnets do not match\n");
return (-1);
}
}
return (0);
}
static int
dapls_ns_resolve_addr(int af, struct sockaddr *addr, DAT_TIMEOUT timeout)
{
struct sockaddr_storage sock;
struct sockaddr_in *v4dest;
struct sockaddr_in6 *v6dest;
struct pollfd pollfd;
int fd, retval;
int tmo;
int ip_version;
if (af == AF_INET) {
ip_version = 4;
} else if (af == AF_INET6) {
ip_version = 6;
} else {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_resolve_addr: invalid af %d\n", af);
return (-1);
}
fd = socket(af, SOCK_STREAM, 0);
if (fd < 0) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_resolve_addr: ipv%d, cannot create socket %s\n",
ip_version, strerror(errno));
return (-1);
}
retval = fcntl(fd, F_SETFL, O_NONBLOCK);
if (retval < 0) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_resolve_addr: ipv%d, fcntl failed: %s\n",
ip_version, strerror(errno));
(void) close(fd);
return (-1);
}
(void) dapl_os_memzero(&sock, sizeof (sock));
if (af == AF_INET) {
v4dest = (struct sockaddr_in *)&sock;
v4dest->sin_family = AF_INET;
v4dest->sin_addr.s_addr =
((struct sockaddr_in *)addr)->sin_addr.s_addr;
v4dest->sin_port = htons(9);
retval = connect(fd, (struct sockaddr *)v4dest,
sizeof (struct sockaddr_in));
} else {
v6dest = (struct sockaddr_in6 *)&sock;
v6dest->sin6_family = AF_INET6;
(void) dapl_os_memcpy(&v6dest->sin6_addr,
&((struct sockaddr_in6 *)addr)->sin6_addr,
sizeof (struct sockaddr_in6));
v6dest->sin6_port = htons(9);
retval = connect(fd, (struct sockaddr *)v6dest,
sizeof (struct sockaddr_in6));
}
if (retval == 0) {
(void) close(fd);
return (0);
}
if (retval < 0 && errno == ECONNREFUSED) {
errno = 0;
(void) close(fd);
return (0);
}
pollfd.fd = fd;
pollfd.events = POLLIN | POLLOUT;
pollfd.revents = 0;
if (timeout == DAT_TIMEOUT_INFINITE ||
timeout == 0) {
tmo = -1;
} else {
tmo = timeout/1000;
}
retval = poll(&pollfd, 1, tmo);
if (retval > 0) {
int so_error = 0, len = sizeof (so_error);
retval = getsockopt(fd, SOL_SOCKET, SO_ERROR,
&so_error, &len);
if (retval == 0) {
if (so_error != 0 && so_error != ECONNREFUSED) {
retval = -1;
errno = so_error;
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_resolve_addr: ipv%d, so_error: %s\n",
ip_version, strerror(errno));
}
} else {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_resolve_addr: ipv%d, getsockopt: %s\n",
ip_version, strerror(errno));
}
} else {
if (retval == 0) {
errno = ETIMEDOUT;
}
retval = -1;
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ns_resolve_addr: ipv%d, poll: %s\n",
ip_version, strerror(errno));
}
(void) close(fd);
return (retval);
}