Spdk/lib/sock/vpp/vpp.c
Tomasz Kulasek 349f43c2fc sock: fix socket memory free
Memory for socket structure is allocated in the selected net
framework but freed in the libsocket on the higher level.

This patch moves memory free to the net framework implementation.

Change-Id: Ia3d4e1553a858a38beb390986e9af105778c12c7
Signed-off-by: Tomasz Kulasek <tomaszx.kulasek@intel.com>
Reviewed-on: https://review.gerrithub.io/421587
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Chandler-Test-Pool: SPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
2018-08-10 15:38:38 +00:00

647 lines
14 KiB
C

/*-
* BSD LICENSE
*
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* 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.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "spdk/stdinc.h"
#include "spdk/log.h"
#include "spdk/sock.h"
#include "spdk/net.h"
#include "spdk/string.h"
#include "spdk_internal/sock.h"
#include <vcl/vppcom.h>
#define MAX_TMPBUF 1024
#define PORTNUMLEN 32
static bool g_vpp_initialized = false;
struct spdk_vpp_sock {
struct spdk_sock base;
int fd;
};
struct spdk_vpp_sock_group_impl {
struct spdk_sock_group_impl base;
int fd;
};
static int
get_addr_str(struct sockaddr *sa, char *host, size_t hlen)
{
const char *result = NULL;
if (sa == NULL || host == NULL) {
return -1;
}
if (sa->sa_family == AF_INET) {
result = inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr),
host, hlen);
} else {
result = inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr),
host, hlen);
}
if (result == NULL) {
return -1;
}
return 0;
}
#define __vpp_sock(sock) (struct spdk_vpp_sock *)sock
#define __vpp_group_impl(group) (struct spdk_vpp_sock_group_impl *)group
static inline void
vcom_socket_copy_ep_to_sockaddr(struct sockaddr *addr, socklen_t *len, vppcom_endpt_t *ep)
{
int sa_len, copy_len;
assert(ep->vrf == VPPCOM_VRF_DEFAULT);
if (ep->is_ip4 == VPPCOM_IS_IP4) {
addr->sa_family = AF_INET;
((struct sockaddr_in *) addr)->sin_port = ep->port;
if (*len > sizeof(struct sockaddr_in)) {
*len = sizeof(struct sockaddr_in);
}
sa_len = sizeof(struct sockaddr_in) - sizeof(struct in_addr);
copy_len = *len - sa_len;
if (copy_len > 0) {
memcpy(&((struct sockaddr_in *) addr)->sin_addr, ep->ip, copy_len);
}
} else {
addr->sa_family = AF_INET6;
((struct sockaddr_in6 *) addr)->sin6_port = ep->port;
if (*len > sizeof(struct sockaddr_in6)) {
*len = sizeof(struct sockaddr_in6);
}
sa_len = sizeof(struct sockaddr_in6) - sizeof(struct in6_addr);
copy_len = *len - sa_len;
if (copy_len > 0) {
memcpy(&((struct sockaddr_in6 *) addr)->sin6_addr, ep->ip, copy_len);
}
}
}
static int
getsockname_vpp(int fd, struct sockaddr *addr, socklen_t *len)
{
vppcom_endpt_t ep;
uint32_t size = sizeof(ep);
uint8_t addr_buf[sizeof(struct in6_addr)];
int rc;
if (!addr || !len) {
return -EFAULT;
}
ep.ip = addr_buf;
rc = vppcom_session_attr(fd, VPPCOM_ATTR_GET_LCL_ADDR, &ep, &size);
if (rc == VPPCOM_OK) {
vcom_socket_copy_ep_to_sockaddr(addr, len, &ep);
}
return rc;
}
static int
getpeername_vpp(int sock, struct sockaddr *addr, socklen_t *len)
{
vppcom_endpt_t ep;
uint32_t size = sizeof(ep);
uint8_t addr_buf[sizeof(struct in6_addr)];
int rc;
if (!addr || !len) {
return -EFAULT;
}
ep.ip = addr_buf;
rc = vppcom_session_attr(sock, VPPCOM_ATTR_GET_PEER_ADDR, &ep, &size);
if (rc == VPPCOM_OK) {
vcom_socket_copy_ep_to_sockaddr(addr, len, &ep);
}
return rc;
}
static int
spdk_vpp_sock_getaddr(struct spdk_sock *_sock, char *saddr, int slen, char *caddr, int clen)
{
struct spdk_vpp_sock *sock = __vpp_sock(_sock);
struct sockaddr sa;
socklen_t salen;
int rc;
assert(sock != NULL);
assert(g_vpp_initialized);
memset(&sa, 0, sizeof(sa));
salen = sizeof(sa);
rc = getsockname_vpp(sock->fd, &sa, &salen);
if (rc != 0) {
errno = -rc;
SPDK_ERRLOG("getsockname_vpp() failed (errno=%d)\n", errno);
return -1;
}
rc = get_addr_str(&sa, saddr, slen);
if (rc != 0) {
/* Errno already set by get_addr_str() */
SPDK_ERRLOG("get_addr_str() failed (errno=%d)\n", errno);
return -1;
}
memset(&sa, 0, sizeof(sa));
salen = sizeof(sa);
rc = getpeername_vpp(sock->fd, &sa, &salen);
if (rc != 0) {
errno = -rc;
SPDK_ERRLOG("getpeername_vpp() failed (errno=%d)\n", errno);
return -1;
}
rc = get_addr_str(&sa, caddr, clen);
if (rc != 0) {
/* Errno already set by get_addr_str() */
SPDK_ERRLOG("get_addr_str() failed (errno=%d)\n", errno);
return -1;
}
return 0;
}
enum spdk_vpp_create_type {
SPDK_SOCK_CREATE_LISTEN,
SPDK_SOCK_CREATE_CONNECT,
};
static struct spdk_sock *
spdk_vpp_sock_create(const char *ip, int port, enum spdk_vpp_create_type type)
{
struct spdk_vpp_sock *sock;
int fd, rc;
vppcom_endpt_t endpt;
uint8_t addr_buf[sizeof(struct in6_addr)];
if (ip == NULL) {
return NULL;
}
/* Check address family */
if (inet_pton(AF_INET, ip, &addr_buf)) {
endpt.is_ip4 = VPPCOM_IS_IP4;
} else if (inet_pton(AF_INET6, ip, &addr_buf)) {
endpt.is_ip4 = VPPCOM_IS_IP6;
} else {
SPDK_ERRLOG("IP address with invalid format\n");
return NULL;
}
endpt.vrf = VPPCOM_VRF_DEFAULT;
endpt.ip = (uint8_t *)&addr_buf;
endpt.port = htons(port);
fd = vppcom_session_create(VPPCOM_VRF_DEFAULT, VPPCOM_PROTO_TCP, 1 /* is_nonblocking */);
if (fd < 0) {
errno = -fd;
SPDK_ERRLOG("vppcom_session_create() failed, errno = %d\n", errno);
return NULL;
}
if (type == SPDK_SOCK_CREATE_LISTEN) {
rc = vppcom_session_bind(fd, &endpt);
if (rc != VPPCOM_OK) {
errno = -rc;
SPDK_ERRLOG("vppcom_session_bind() failed, errno = %d\n", errno);
vppcom_session_close(fd);
return NULL;
}
rc = vppcom_session_listen(fd, 512);
if (rc != VPPCOM_OK) {
errno = -rc;
SPDK_ERRLOG("vppcom_session_listen() failed, errno = %d\n", errno);
vppcom_session_close(fd);
return NULL;
}
} else if (type == SPDK_SOCK_CREATE_CONNECT) {
rc = vppcom_session_connect(fd, &endpt);
if (rc != VPPCOM_OK) {
errno = -rc;
SPDK_ERRLOG("vppcom_session_connect() failed, errno = %d\n", errno);
vppcom_session_close(fd);
return NULL;
}
}
sock = calloc(1, sizeof(*sock));
if (sock == NULL) {
errno = -ENOMEM;
SPDK_ERRLOG("sock allocation failed\n");
vppcom_session_close(fd);
return NULL;
}
sock->fd = fd;
return &sock->base;
}
static struct spdk_sock *
spdk_vpp_sock_listen(const char *ip, int port)
{
if (!g_vpp_initialized) {
return NULL;
}
return spdk_vpp_sock_create(ip, port, SPDK_SOCK_CREATE_LISTEN);
}
static struct spdk_sock *
spdk_vpp_sock_connect(const char *ip, int port)
{
if (!g_vpp_initialized) {
return NULL;
}
return spdk_vpp_sock_create(ip, port, SPDK_SOCK_CREATE_CONNECT);
}
static struct spdk_sock *
spdk_vpp_sock_accept(struct spdk_sock *_sock)
{
struct spdk_vpp_sock *sock = __vpp_sock(_sock);
vppcom_endpt_t endpt;
uint8_t ip[16];
int rc;
struct spdk_vpp_sock *new_sock;
double wait_time = -1.0;
endpt.ip = ip;
assert(sock != NULL);
assert(g_vpp_initialized);
rc = vppcom_session_accept(sock->fd, &endpt, O_NONBLOCK, wait_time);
if (rc < 0) {
errno = -rc;
return NULL;
}
new_sock = calloc(1, sizeof(*sock));
if (new_sock == NULL) {
SPDK_ERRLOG("sock allocation failed\n");
vppcom_session_close(rc);
return NULL;
}
new_sock->fd = rc;
return &new_sock->base;
}
static int
spdk_vpp_sock_close(struct spdk_sock *_sock)
{
struct spdk_vpp_sock *sock = __vpp_sock(_sock);
int rc;
assert(sock != NULL);
assert(g_vpp_initialized);
rc = vppcom_session_close(sock->fd);
if (rc != VPPCOM_OK) {
errno = -rc;
return -1;
}
free(sock);
return 0;
}
static ssize_t
spdk_vpp_sock_recv(struct spdk_sock *_sock, void *buf, size_t len)
{
struct spdk_vpp_sock *sock = __vpp_sock(_sock);
int rc;
assert(sock != NULL);
assert(g_vpp_initialized);
rc = vppcom_session_read(sock->fd, buf, len);
if (rc < 0) {
errno = -rc;
return -1;
}
return rc;
}
static ssize_t
spdk_vpp_sock_writev(struct spdk_sock *_sock, struct iovec *iov, int iovcnt)
{
struct spdk_vpp_sock *sock = __vpp_sock(_sock);
ssize_t total = 0;
int i, rc;
assert(sock != NULL);
assert(g_vpp_initialized);
for (i = 0; i < iovcnt; ++i) {
rc = vppcom_session_write(sock->fd, iov[i].iov_base, iov[i].iov_len);
if (rc < 0) {
if (total > 0) {
break;
} else {
errno = -rc;
return -1;
}
} else {
total += rc;
}
}
return total;
}
/*
* TODO: Check if there are similar parameters to configure in VPP
* to three below.
*/
static int
spdk_vpp_sock_set_recvlowat(struct spdk_sock *_sock, int nbytes)
{
assert(g_vpp_initialized);
return 0;
}
static int
spdk_vpp_sock_set_recvbuf(struct spdk_sock *_sock, int sz)
{
assert(g_vpp_initialized);
return 0;
}
static int
spdk_vpp_sock_set_sendbuf(struct spdk_sock *_sock, int sz)
{
assert(g_vpp_initialized);
return 0;
}
static bool
spdk_vpp_sock_is_ipv6(struct spdk_sock *_sock)
{
struct spdk_vpp_sock *sock = __vpp_sock(_sock);
vppcom_endpt_t ep;
uint32_t size = sizeof(ep);
uint8_t addr_buf[sizeof(struct in6_addr)];
int rc;
assert(sock != NULL);
assert(g_vpp_initialized);
ep.ip = addr_buf;
rc = vppcom_session_attr(sock->fd, VPPCOM_ATTR_GET_LCL_ADDR, &ep, &size);
if (rc != VPPCOM_OK) {
errno = -rc;
return false;
}
return (ep.is_ip4 == VPPCOM_IS_IP6);
}
static bool
spdk_vpp_sock_is_ipv4(struct spdk_sock *_sock)
{
struct spdk_vpp_sock *sock = __vpp_sock(_sock);
vppcom_endpt_t ep;
uint32_t size = sizeof(ep);
uint8_t addr_buf[sizeof(struct in6_addr)];
int rc;
assert(sock != NULL);
assert(g_vpp_initialized);
ep.ip = addr_buf;
rc = vppcom_session_attr(sock->fd, VPPCOM_ATTR_GET_LCL_ADDR, &ep, &size);
if (rc != VPPCOM_OK) {
errno = -rc;
return false;
}
return (ep.is_ip4 == VPPCOM_IS_IP4);
}
static struct spdk_sock_group_impl *
spdk_vpp_sock_group_impl_create(void)
{
struct spdk_vpp_sock_group_impl *group_impl;
int fd;
if (!g_vpp_initialized) {
return NULL;
}
group_impl = calloc(1, sizeof(*group_impl));
if (group_impl == NULL) {
SPDK_ERRLOG("sock_group allocation failed\n");
return NULL;
}
fd = vppcom_epoll_create();
if (fd < 0) {
free(group_impl);
return NULL;
}
group_impl->fd = fd;
return &group_impl->base;
}
static int
spdk_vpp_sock_group_impl_add_sock(struct spdk_sock_group_impl *_group, struct spdk_sock *_sock)
{
struct spdk_vpp_sock_group_impl *group = __vpp_group_impl(_group);
struct spdk_vpp_sock *sock = __vpp_sock(_sock);
int rc;
struct epoll_event event;
assert(sock != NULL);
assert(group != NULL);
assert(g_vpp_initialized);
event.events = EPOLLIN;
event.data.ptr = sock;
rc = vppcom_epoll_ctl(group->fd, EPOLL_CTL_ADD, sock->fd, &event);
if (rc != VPPCOM_OK) {
errno = -rc;
return -1;
}
return 0;
}
static int
spdk_vpp_sock_group_impl_remove_sock(struct spdk_sock_group_impl *_group, struct spdk_sock *_sock)
{
struct spdk_vpp_sock_group_impl *group = __vpp_group_impl(_group);
struct spdk_vpp_sock *sock = __vpp_sock(_sock);
int rc;
struct epoll_event event;
assert(sock != NULL);
assert(group != NULL);
assert(g_vpp_initialized);
rc = vppcom_epoll_ctl(group->fd, EPOLL_CTL_DEL, sock->fd, &event);
if (rc != VPPCOM_OK) {
errno = -rc;
return -1;
}
return 0;
}
static int
spdk_vpp_sock_group_impl_poll(struct spdk_sock_group_impl *_group, int max_events,
struct spdk_sock **socks)
{
struct spdk_vpp_sock_group_impl *group = __vpp_group_impl(_group);
int num_events, i;
struct epoll_event events[MAX_EVENTS_PER_POLL];
assert(group != NULL);
assert(socks != NULL);
assert(g_vpp_initialized);
num_events = vppcom_epoll_wait(group->fd, events, max_events, 0);
if (num_events < 0) {
errno = -num_events;
return -1;
}
for (i = 0; i < num_events; i++) {
socks[i] = events[i].data.ptr;
}
return num_events;
}
static int
spdk_vpp_sock_group_impl_close(struct spdk_sock_group_impl *_group)
{
struct spdk_vpp_sock_group_impl *group = __vpp_group_impl(_group);
int rc;
assert(group != NULL);
assert(g_vpp_initialized);
rc = vppcom_session_close(group->fd);
if (rc != VPPCOM_OK) {
errno = -rc;
return -1;
}
return 0;
}
static struct spdk_net_impl g_vpp_net_impl = {
.name = "vpp",
.getaddr = spdk_vpp_sock_getaddr,
.connect = spdk_vpp_sock_connect,
.listen = spdk_vpp_sock_listen,
.accept = spdk_vpp_sock_accept,
.close = spdk_vpp_sock_close,
.recv = spdk_vpp_sock_recv,
.writev = spdk_vpp_sock_writev,
.set_recvlowat = spdk_vpp_sock_set_recvlowat,
.set_recvbuf = spdk_vpp_sock_set_recvbuf,
.set_sendbuf = spdk_vpp_sock_set_sendbuf,
.is_ipv6 = spdk_vpp_sock_is_ipv6,
.is_ipv4 = spdk_vpp_sock_is_ipv4,
.group_impl_create = spdk_vpp_sock_group_impl_create,
.group_impl_add_sock = spdk_vpp_sock_group_impl_add_sock,
.group_impl_remove_sock = spdk_vpp_sock_group_impl_remove_sock,
.group_impl_poll = spdk_vpp_sock_group_impl_poll,
.group_impl_close = spdk_vpp_sock_group_impl_close,
};
SPDK_NET_IMPL_REGISTER(vpp, &g_vpp_net_impl);
static int
spdk_vpp_net_framework_init(void)
{
int rc;
char *app_name;
app_name = spdk_sprintf_alloc("SPDK_%d", getpid());
if (app_name == NULL) {
SPDK_ERRLOG("Cannot alloc memory for SPDK app name\n");
return -ENOMEM;
}
rc = vppcom_app_create(app_name);
if (rc == 0) {
g_vpp_initialized = true;
}
free(app_name);
return 0;
}
static void
spdk_vpp_net_framework_fini(void)
{
if (g_vpp_initialized) {
vppcom_app_destroy();
}
}
static struct spdk_net_framework g_vpp_net_framework = {
.name = "vpp",
.init = spdk_vpp_net_framework_init,
.fini = spdk_vpp_net_framework_fini,
};
SPDK_NET_FRAMEWORK_REGISTER(vpp, &g_vpp_net_framework);