/*
* Copyright (c) 2010-2014 Intel Corporation. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* $Id: cm.c 3453 2005-09-15 21:43:21Z sean.hefty $
*/
#include <config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include "cma.h"
#include <rdma/rdma_cma.h>
#include <infiniband/ib.h>
static struct rdma_addrinfo nohints;
static void ucma_convert_to_ai(struct addrinfo *ai,
const struct rdma_addrinfo *rai)
{
memset(ai, 0, sizeof(*ai));
if (rai->ai_flags & RAI_PASSIVE)
ai->ai_flags = AI_PASSIVE;
if (rai->ai_flags & RAI_NUMERICHOST)
ai->ai_flags |= AI_NUMERICHOST;
if (rai->ai_family != AF_IB)
ai->ai_family = rai->ai_family;
switch (rai->ai_qp_type) {
case IBV_QPT_RC:
case IBV_QPT_UC:
case IBV_QPT_XRC_SEND:
case IBV_QPT_XRC_RECV:
ai->ai_socktype = SOCK_STREAM;
break;
case IBV_QPT_UD:
ai->ai_socktype = SOCK_DGRAM;
break;
}
switch (rai->ai_port_space) {
case RDMA_PS_TCP:
ai->ai_protocol = IPPROTO_TCP;
break;
case RDMA_PS_IPOIB:
case RDMA_PS_UDP:
ai->ai_protocol = IPPROTO_UDP;
break;
case RDMA_PS_IB:
if (ai->ai_socktype == SOCK_STREAM)
ai->ai_protocol = IPPROTO_TCP;
else if (ai->ai_socktype == SOCK_DGRAM)
ai->ai_protocol = IPPROTO_UDP;
break;
}
if (rai->ai_flags & RAI_PASSIVE) {
ai->ai_addrlen = rai->ai_src_len;
ai->ai_addr = rai->ai_src_addr;
} else {
ai->ai_addrlen = rai->ai_dst_len;
ai->ai_addr = rai->ai_dst_addr;
}
ai->ai_canonname = rai->ai_dst_canonname;
ai->ai_next = NULL;
}
static int ucma_copy_addr(struct sockaddr **dst, socklen_t *dst_len,
struct sockaddr *src, socklen_t src_len)
{
*dst = malloc(src_len);
if (!(*dst))
return ERR(ENOMEM);
memcpy(*dst, src, src_len);
*dst_len = src_len;
return 0;
}
void ucma_set_sid(enum rdma_port_space ps, struct sockaddr *addr,
struct sockaddr_ib *sib)
{
__be16 port;
port = addr ? ucma_get_port(addr) : 0;
sib->sib_sid = htobe64(((uint64_t) ps << 16) + be16toh(port));
if (ps)
sib->sib_sid_mask = htobe64(RDMA_IB_IP_PS_MASK);
if (port)
sib->sib_sid_mask |= htobe64(RDMA_IB_IP_PORT_MASK);
}
static int ucma_convert_in6(int ps, struct sockaddr_ib **dst, socklen_t *dst_len,
struct sockaddr_in6 *src, socklen_t src_len)
{
*dst = calloc(1, sizeof(struct sockaddr_ib));
if (!(*dst))
return ERR(ENOMEM);
(*dst)->sib_family = AF_IB;
(*dst)->sib_pkey = htobe16(0xFFFF);
(*dst)->sib_flowinfo = src->sin6_flowinfo;
ib_addr_set(&(*dst)->sib_addr, src->sin6_addr.s6_addr32[0],
src->sin6_addr.s6_addr32[1], src->sin6_addr.s6_addr32[2],
src->sin6_addr.s6_addr32[3]);
ucma_set_sid(ps, (struct sockaddr *) src, *dst);
(*dst)->sib_scope_id = src->sin6_scope_id;
*dst_len = sizeof(struct sockaddr_ib);
return 0;
}
static int ucma_convert_to_rai(struct rdma_addrinfo *rai,
const struct rdma_addrinfo *hints,
const struct addrinfo *ai)
{
int ret;
if (hints->ai_qp_type) {
rai->ai_qp_type = hints->ai_qp_type;
} else {
switch (ai->ai_socktype) {
case SOCK_STREAM:
rai->ai_qp_type = IBV_QPT_RC;
break;
case SOCK_DGRAM:
rai->ai_qp_type = IBV_QPT_UD;
break;
}
}
if (hints->ai_port_space) {
rai->ai_port_space = hints->ai_port_space;
} else {
switch (ai->ai_protocol) {
case IPPROTO_TCP:
rai->ai_port_space = RDMA_PS_TCP;
break;
case IPPROTO_UDP:
rai->ai_port_space = RDMA_PS_UDP;
break;
}
}
if (ai->ai_flags & AI_PASSIVE) {
rai->ai_flags = RAI_PASSIVE;
if (ai->ai_canonname)
rai->ai_src_canonname = strdup(ai->ai_canonname);
if ((hints->ai_flags & RAI_FAMILY) && (hints->ai_family == AF_IB) &&
(hints->ai_flags & RAI_NUMERICHOST)) {
rai->ai_family = AF_IB;
ret = ucma_convert_in6(rai->ai_port_space,
(struct sockaddr_ib **) &rai->ai_src_addr,
&rai->ai_src_len,
(struct sockaddr_in6 *) ai->ai_addr,
ai->ai_addrlen);
} else {
rai->ai_family = ai->ai_family;
ret = ucma_copy_addr(&rai->ai_src_addr, &rai->ai_src_len,
ai->ai_addr, ai->ai_addrlen);
}
} else {
if (ai->ai_canonname)
rai->ai_dst_canonname = strdup(ai->ai_canonname);
if ((hints->ai_flags & RAI_FAMILY) && (hints->ai_family == AF_IB) &&
(hints->ai_flags & RAI_NUMERICHOST)) {
rai->ai_family = AF_IB;
ret = ucma_convert_in6(rai->ai_port_space,
(struct sockaddr_ib **) &rai->ai_dst_addr,
&rai->ai_dst_len,
(struct sockaddr_in6 *) ai->ai_addr,
ai->ai_addrlen);
} else {
rai->ai_family = ai->ai_family;
ret = ucma_copy_addr(&rai->ai_dst_addr, &rai->ai_dst_len,
ai->ai_addr, ai->ai_addrlen);
}
}
return ret;
}
static int ucma_getaddrinfo(const char *node, const char *service,
const struct rdma_addrinfo *hints,
struct rdma_addrinfo *rai)
{
struct addrinfo ai_hints;
struct addrinfo *ai;
int ret;
if (hints != &nohints) {
ucma_convert_to_ai(&ai_hints, hints);
ret = getaddrinfo(node, service, &ai_hints, &ai);
} else {
ret = getaddrinfo(node, service, NULL, &ai);
}
if (ret)
return ret;
ret = ucma_convert_to_rai(rai, hints, ai);
freeaddrinfo(ai);
return ret;
}
int rdma_getaddrinfo(const char *node, const char *service,
const struct rdma_addrinfo *hints,
struct rdma_addrinfo **res)
{
struct rdma_addrinfo *rai;
int ret;
if (!service && !node && !hints)
return ERR(EINVAL);
ret = ucma_init();
if (ret)
return ret;
rai = calloc(1, sizeof(*rai));
if (!rai)
return ERR(ENOMEM);
if (!hints)
hints = &nohints;
if (node || service) {
ret = ucma_getaddrinfo(node, service, hints, rai);
} else {
rai->ai_flags = hints->ai_flags;
rai->ai_family = hints->ai_family;
rai->ai_qp_type = hints->ai_qp_type;
rai->ai_port_space = hints->ai_port_space;
if (hints->ai_dst_len) {
ret = ucma_copy_addr(&rai->ai_dst_addr, &rai->ai_dst_len,
hints->ai_dst_addr, hints->ai_dst_len);
}
}
if (ret)
goto err;
if (!rai->ai_src_len && hints->ai_src_len) {
ret = ucma_copy_addr(&rai->ai_src_addr, &rai->ai_src_len,
hints->ai_src_addr, hints->ai_src_len);
if (ret)
goto err;
}
if (!(rai->ai_flags & RAI_PASSIVE))
ucma_ib_resolve(&rai, hints);
*res = rai;
return 0;
err:
rdma_freeaddrinfo(rai);
return ret;
}
void rdma_freeaddrinfo(struct rdma_addrinfo *res)
{
struct rdma_addrinfo *rai;
while (res) {
rai = res;
res = res->ai_next;
if (rai->ai_connect)
free(rai->ai_connect);
if (rai->ai_route)
free(rai->ai_route);
if (rai->ai_src_canonname)
free(rai->ai_src_canonname);
if (rai->ai_dst_canonname)
free(rai->ai_dst_canonname);
if (rai->ai_src_addr)
free(rai->ai_src_addr);
if (rai->ai_dst_addr)
free(rai->ai_dst_addr);
free(rai);
}
}