fabtests/functional/msg_sockets.c (368 lines of code) (raw):
/*
* Copyright (c) 2013-2015 Intel Corporation. All rights reserved.
* Copyright (c) 2014-2016, Cisco Systems, Inc. All rights reserved.
*
* This software is available to you under the 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.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <rdma/fi_errno.h>
#include <rdma/fi_endpoint.h>
#include <rdma/fi_cm.h>
#include "shared.h"
union sockaddr_any {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
struct sockaddr_storage ss;
};
static union sockaddr_any bound_addr;
static size_t bound_addr_len = sizeof bound_addr;
/* Wrapper for memcmp for sockaddr. Note that the sockaddr structure may
* contain holes, so sockaddr's are expected to have been initialized to all
* zeroes prior to being filled with an address. */
static int
sockaddrcmp(const union sockaddr_any *actual, socklen_t actual_len,
const union sockaddr_any *expected, socklen_t expected_len)
{
if (actual->sa.sa_family != expected->sa.sa_family) {
return actual->sa.sa_family - expected->sa.sa_family;
} else if (actual_len != expected_len) {
return actual_len - expected_len;
}
/* Handle binds to wildcard addresses, for address types we know
* about */
switch (expected->sa.sa_family) {
case AF_INET:
if (expected->sin.sin_addr.s_addr == INADDR_ANY) {
return 0;
}
break;
case AF_INET6:
if (!memcmp(&expected->sin6.sin6_addr,
&in6addr_any, sizeof(struct in6_addr))) {
return 0;
}
break;
}
return memcmp(actual, expected, actual_len);
}
/* Returns a string for the given sockaddr using getnameinfo(). This returns a
* static buffer so it is not reentrant or thread-safe. Returns the string on
* success and NULL on failure. */
static const char *
sockaddrstr(const union sockaddr_any *addr, socklen_t len, char *buf, size_t buflen)
{
static char namebuf[BUFSIZ];
static char servbuf[BUFSIZ];
int errcode;
if ((errcode = getnameinfo(&addr->sa, len, namebuf, BUFSIZ,
servbuf, BUFSIZ,
NI_NUMERICHOST | NI_NUMERICSERV))) {
if (errcode != EAI_SYSTEM) {
fprintf(stderr, "getnameinfo: %s\n", gai_strerror(errcode));
} else {
fprintf(stderr, "getnameinfo: %s\n", strerror(errno));
}
return NULL;
}
snprintf(buf, buflen, "[%s]:%s", namebuf, servbuf);
return buf;
}
static int check_address(struct fid *fid, const char *message)
{
char buf1[BUFSIZ], buf2[BUFSIZ];
union sockaddr_any tmp;
size_t tmplen;
const char *ep_addr, *addr_expected;
int ret;
memset(&tmp, 0, sizeof tmp);
tmplen = sizeof tmp;
ret = fi_getname(fid, &tmp, &tmplen);
if (ret) {
FT_PRINTERR("fi_getname", ret);
}
if (sockaddrcmp(&tmp, tmplen, &bound_addr, bound_addr_len)) {
ep_addr = sockaddrstr(&tmp, tmplen, buf1, BUFSIZ);
if (!ep_addr) {
FT_ERR("Unable to get ep_addr as string!");
return -FI_EINVAL;
}
addr_expected = sockaddrstr(&bound_addr, bound_addr_len, buf2, BUFSIZ);
if (!addr_expected) {
FT_ERR("Unable to get addr_expected as string!");
return -FI_EINVAL;
}
FT_ERR("address changed after %s: got %s expected %s",
message, ep_addr, addr_expected);
return -FI_EINVAL;
}
return 0;
}
static int server_listen(void)
{
int ret;
ret = fi_pep_bind(pep, &eq->fid, 0);
if (ret) {
FT_PRINTERR("fi_pep_bind", ret);
return ret;
}
ret = fi_listen(pep);
if (ret) {
FT_PRINTERR("fi_listen", ret);
return ret;
}
return 0;
}
static int server_connect(void)
{
struct fi_eq_cm_entry entry;
uint32_t event;
ssize_t rd;
int ret;
/* Wait for connection request from client */
rd = fi_eq_sread(eq, &event, &entry, sizeof entry, -1, 0);
if (rd != sizeof entry) {
FT_PRINTERR("fi_eq_sread", rd);
return (int) rd;
}
fi = entry.info;
if (event != FI_CONNREQ) {
FT_ERR("Unexpected CM event %d", event);
ret = -FI_EOTHER;
goto err;
}
ret = fi_domain(fabric, fi, &domain, NULL);
if (ret) {
FT_PRINTERR("fi_domain", ret);
goto err;
}
ret = ft_alloc_active_res(fi);
if (ret)
goto err;
ret = ft_enable_ep_recv();
if (ret)
goto err;
/* Accept the incoming connection. Also transitions endpoint to active state */
ret = fi_accept(ep, NULL, 0);
if (ret) {
FT_PRINTERR("fi_accept", ret);
goto err;
}
/* Wait for the connection to be established */
rd = fi_eq_sread(eq, &event, &entry, sizeof entry, -1, 0);
if (rd != sizeof entry) {
FT_PRINTERR("fi_eq_sread", rd);
goto err;
}
if (event != FI_CONNECTED || entry.fid != &ep->fid) {
FT_ERR("Unexpected CM event %d fid %p (ep %p)", event, entry.fid, ep);
ret = -FI_EOTHER;
goto err;
}
ret = check_address(&ep->fid, "accept");
if (ret) {
goto err;
}
return 0;
err:
fi_reject(pep, fi->handle, NULL, 0);
return ret;
}
static int client_connect(void)
{
struct fi_eq_cm_entry entry;
uint32_t event;
ssize_t rd;
int ret;
ret = fi_getinfo(FT_FIVERSION, opts.dst_addr, opts.dst_port, 0, hints, &fi);
if (ret) {
FT_PRINTERR("fi_getinfo", ret);
return ret;
}
ret = fi_domain(fabric, fi, &domain, NULL);
if (ret) {
FT_PRINTERR("fi_domain", ret);
return ret;
}
ret = check_address(&pep->fid, "fi_endpoint (pep)");
if (ret)
return ret;
assert(fi->handle == &pep->fid);
ret = ft_alloc_active_res(fi);
if (ret)
return ret;
/* Close the passive endpoint that we "stole" the source address
* from */
FT_CLOSE_FID(pep);
ret = ft_enable_ep_recv();
if (ret)
return ret;
ret = check_address(&ep->fid, "fi_endpoint (ep)");
if (ret)
return ret;
/* Connect to server */
ret = fi_connect(ep, fi->dest_addr, NULL, 0);
if (ret) {
FT_PRINTERR("fi_connect", ret);
return ret;
}
/* Wait for the connection to be established */
rd = fi_eq_sread(eq, &event, &entry, sizeof entry, -1, 0);
if (rd != sizeof entry) {
FT_PROCESS_EQ_ERR(rd, eq, "fi_eq_sread", "listen");
return (int) rd;
}
if (event != FI_CONNECTED || entry.fid != &ep->fid) {
FT_ERR("Unexpected CM event %d fid %p (ep %p)", event, entry.fid, ep);
return -FI_EOTHER;
}
ret = check_address(&ep->fid, "connect");
if (ret) {
return ret;
}
return 0;
}
static int setup_handle(void)
{
static char buf[BUFSIZ];
struct addrinfo *ai, aihints;
const char *bound_addr_str;
char *saved_addr;
size_t saved_addrlen;
int ret;
ret = ft_startup();
if (ret) {
FT_ERR("ft_startup: %d", ret);
return ret;
}
memset(&aihints, 0, sizeof aihints);
aihints.ai_flags = AI_PASSIVE;
ret = getaddrinfo(opts.src_addr, opts.src_port, &aihints, &ai);
if (ret == EAI_SYSTEM) {
FT_ERR("getaddrinfo for %s:%s: %s",
opts.src_addr, opts.src_port, strerror(errno));
return -ret;
} else if (ret) {
FT_ERR("getaddrinfo: %s", gai_strerror(ret));
return -FI_ENODATA;
}
switch (ai->ai_family) {
case AF_INET:
hints->addr_format = FI_SOCKADDR_IN;
break;
case AF_INET6:
hints->addr_format = FI_SOCKADDR_IN6;
break;
}
/* Get fabric info */
ret = fi_getinfo(FT_FIVERSION, opts.src_addr, NULL, FI_SOURCE, hints, &fi_pep);
if (ret) {
FT_PRINTERR("fi_getinfo", ret);
goto freeai;
}
/* Open passive endpoint without source address */
saved_addr = fi_pep->src_addr;
saved_addrlen = fi_pep->src_addrlen;
fi_pep->src_addr = NULL;
fi_pep->src_addrlen = 0;
ret = fi_fabric(fi_pep->fabric_attr, &fabric, NULL);
if (ret) {
FT_PRINTERR("fi_fabric", ret);
goto out;
}
ret = fi_eq_open(fabric, &eq_attr, &eq, NULL);
if (ret) {
FT_PRINTERR("fi_eq_open", ret);
goto out;
}
/* Open a passive endpoint */
ret = fi_passive_ep(fabric, fi_pep, &pep, NULL);
if (ret) {
FT_PRINTERR("fi_passive_ep", ret);
goto out;
}
ret = fi_setname(&pep->fid, ai->ai_addr, ai->ai_addrlen);
if (ret) {
FT_PRINTERR("fi_setname", ret);
goto out;
}
ret = fi_getname(&pep->fid, &bound_addr, &bound_addr_len);
if (ret) {
FT_PRINTERR("fi_getname", ret);
goto out;
}
/* Verify port number */
switch (ai->ai_family) {
case AF_INET:
if (bound_addr.sin.sin_port == 0) {
FT_ERR("port number is 0 after fi_setname()");
ret = -FI_EINVAL;
goto out;
}
break;
case AF_INET6:
if (bound_addr.sin6.sin6_port == 0) {
FT_ERR("port number is 0 after fi_setname()");
ret = -FI_EINVAL;
goto out;
}
break;
}
bound_addr_str = sockaddrstr(&bound_addr, bound_addr_len, buf, BUFSIZ);
if (!bound_addr_str) {
FT_ERR("Unable to get bound_addr as string!");
ret = -FI_EINVAL;
goto out;
}
printf("bound_addr: \"%s\"\n", bound_addr_str);
hints->handle = &pep->fid;
out:
fi_pep->src_addr = saved_addr;
fi_pep->src_addrlen = saved_addrlen;
freeai:
freeaddrinfo(ai);
return ret;
}
static int run(void)
{
char *node, *service;
uint64_t flags;
int ret;
ret = ft_read_addr_opts(&node, &service, hints, &flags, &opts);
if (ret)
return ret;
if (!opts.src_port)
opts.src_port = "9229";
if (!opts.src_addr) {
fprintf(stderr, "Source address (-s) is required for this test\n");
return -EXIT_FAILURE;
}
ret = setup_handle();
if (ret)
return ret;
if (!opts.dst_addr) {
ret = server_listen();
if (ret)
return ret;
}
ret = opts.dst_addr ? client_connect() : server_connect();
if (ret) {
return ret;
}
ret = ft_send_recv_greeting(ep);
fi_shutdown(ep, 0);
return ret;
}
int main(int argc, char **argv)
{
int op, ret;
opts = INIT_OPTS;
opts.options |= FT_OPT_SIZE;
hints = fi_allocinfo();
if (!hints)
return EXIT_FAILURE;
while ((op = getopt(argc, argv, "h" ADDR_OPTS INFO_OPTS)) != -1) {
switch (op) {
default:
ft_parse_addr_opts(op, optarg, &opts);
ft_parseinfo(op, optarg, hints, &opts);
break;
case '?':
case 'h':
ft_usage(argv[0], "A simple MSG client-sever example.");
return EXIT_FAILURE;
}
}
if (optind < argc)
opts.dst_addr = argv[optind];
hints->ep_attr->type = FI_EP_MSG;
hints->caps = FI_MSG;
hints->domain_attr->mr_mode = opts.mr_mode;
hints->addr_format = FI_SOCKADDR;
ret = run();
ft_free_res();
return ft_exit_code(ret);
}