From d3090c845584dd2a36919a6b5a3eed8acd2e9704 Mon Sep 17 00:00:00 2001 From: Ben Walker Date: Tue, 2 Aug 2016 15:35:29 -0700 Subject: [PATCH] Add a network stack abstraction layer. This is a useful abstraction when you want to plug in a userspace networking layer instead of using the kernel. Change-Id: I7039d2987e6abad9dcd1987fa105282b1598e2f5 Signed-off-by: Ben Walker --- include/spdk/net.h | 87 ++++++ lib/Makefile | 3 +- lib/net/Makefile | 41 +++ lib/net/interface.c | 503 ++++++++++++++++++++++++++++++++ lib/net/net_framework_default.c | 67 +++++ lib/net/net_rpc.c | 180 ++++++++++++ lib/net/sock.c | 350 ++++++++++++++++++++++ 7 files changed, 1230 insertions(+), 1 deletion(-) create mode 100644 include/spdk/net.h create mode 100644 lib/net/Makefile create mode 100644 lib/net/interface.c create mode 100644 lib/net/net_framework_default.c create mode 100644 lib/net/net_rpc.c create mode 100644 lib/net/sock.c diff --git a/include/spdk/net.h b/include/spdk/net.h new file mode 100644 index 0000000000..a645a258da --- /dev/null +++ b/include/spdk/net.h @@ -0,0 +1,87 @@ +/*- + * 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. + */ + +/** \file + * Net framework abstraction layer + */ + +#ifndef SPDK_NET_H +#define SPDK_NET_H + +#include +#include +#include +#include + +#include "spdk/queue.h" + +#define IDLE_INTERVAL_TIME_IN_US 5000 + +const char *spdk_net_framework_get_name(void); +int spdk_net_framework_start(void); +void spdk_net_framework_clear_socket_association(int sock); +int spdk_net_framework_fini(void); +int spdk_net_framework_idle_time(void); + +#define SPDK_IFNAMSIZE 32 +#define SPDK_MAX_IP_PER_IFC 32 +#define SPDK_MAX_NIC_NUM 32 + +struct spdk_interface { + char name[SPDK_IFNAMSIZE]; + uint32_t index; + uint32_t num_ip_addresses; /* number of IP addresses defined */ + uint32_t ip_address[SPDK_MAX_IP_PER_IFC]; + TAILQ_ENTRY(spdk_interface) tailq; +}; + +int spdk_interface_add_ip_address(int ifc_index, char *ip_addr); +int spdk_interface_delete_ip_address(int ifc_index, char *ip_addr); +void *spdk_interface_get_list(void); + +int spdk_sock_getaddr(int sock, char *saddr, int slen, char *caddr, int clen); +int spdk_sock_connect(const char *ip, int port); +int spdk_sock_listen(const char *ip, int port); +int spdk_sock_accept(int sock); +int spdk_sock_close(int sock); +ssize_t spdk_sock_recv(int sock, void *buf, size_t len); +ssize_t spdk_sock_writev(int sock, struct iovec *iov, int iovcnt); + +int spdk_sock_set_recvlowat(int sock, int nbytes); +int spdk_sock_set_recvbuf(int sock, int sz); +int spdk_sock_set_sendbuf(int sock, int sz); + +bool spdk_sock_is_ipv6(int sock); +bool spdk_sock_is_ipv4(int sock); + +#endif /* SPDK_NET_FRAMEWORK_H */ diff --git a/lib/Makefile b/lib/Makefile index 714bc9aa85..fb61cfb64f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -34,7 +34,8 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk -DIRS-y += bdev conf copy cunit event json jsonrpc log memory rpc trace util nvme nvmf scsi ioat +DIRS-y += bdev conf copy cunit event json jsonrpc log memory \ + net rpc trace util nvme nvmf scsi ioat .PHONY: all clean $(DIRS-y) diff --git a/lib/net/Makefile b/lib/net/Makefile new file mode 100644 index 0000000000..c2cbc2aeca --- /dev/null +++ b/lib/net/Makefile @@ -0,0 +1,41 @@ +# +# 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. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +C_SRCS = interface.c sock.c net_framework_default.c net_rpc.c + +LIBNAME = net + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/lib/net/interface.c b/lib/net/interface.c new file mode 100644 index 0000000000..0f81c12c19 --- /dev/null +++ b/lib/net/interface.c @@ -0,0 +1,503 @@ +/*- + * 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 + +#include "spdk/log.h" +#include "spdk/event.h" +#include "spdk/net.h" + +#ifdef __linux__ /* Interface management is Linux-specific */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +static TAILQ_HEAD(, spdk_interface) g_interface_head; + +static pthread_mutex_t interface_lock = PTHREAD_MUTEX_INITIALIZER; + +static uint32_t spdk_get_ifc_ipv4(void) +{ + int ret; + int rtattrlen; + int netlink_fd; + uint32_t ipv4_addr; + + struct { + struct nlmsghdr n; + struct ifaddrmsg r; + } req; + struct rtattr *rta; + char buf[16384]; + struct nlmsghdr *nlmp; + struct ifaddrmsg *rtmp; + struct rtattr *rtatp; + struct spdk_interface *ifc; + + netlink_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (netlink_fd < 0) { + SPDK_ERRLOG("socket failed!\n"); + return 1; + } + + /* + * Prepare a message structure + */ + memset(&req, 0, sizeof(req)); + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; + req.n.nlmsg_type = RTM_GETADDR; + + /* IPv4 only */ + req.r.ifa_family = AF_INET; + + /* + * Fill up all the attributes for the rtnetlink header. + */ + rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.n.nlmsg_len)); + rta->rta_len = RTA_LENGTH(16); + + /* Send and recv the message from kernel */ + ret = send(netlink_fd, &req, req.n.nlmsg_len, 0); + if (ret < 0) { + SPDK_ERRLOG("netlink send failed: %s\n", strerror(errno)); + ret = 1; + goto exit; + } + + ret = recv(netlink_fd, buf, sizeof(buf), 0); + if (ret <= 0) { + SPDK_ERRLOG("netlink recv failed: %s\n", strerror(errno)); + ret = 1; + goto exit; + } + + for (nlmp = (struct nlmsghdr *)buf; ret > (int)sizeof(*nlmp);) { + int len = nlmp->nlmsg_len; + int req_len = len - sizeof(*nlmp); + + if (req_len < 0 || len > ret) { + SPDK_ERRLOG("error\n"); + ret = 1; + goto exit; + } + + if (!NLMSG_OK(nlmp, (uint32_t)ret)) { + SPDK_ERRLOG("NLMSG not OK\n"); + ret = 1; + goto exit; + } + + rtmp = (struct ifaddrmsg *)NLMSG_DATA(nlmp); + rtatp = (struct rtattr *)IFA_RTA(rtmp); + + rtattrlen = IFA_PAYLOAD(nlmp); + + for (; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) { + if (rtatp->rta_type == IFA_LOCAL) { + memcpy(&ipv4_addr, (struct in_addr *)RTA_DATA(rtatp), + sizeof(struct in_addr)); + TAILQ_FOREACH(ifc, &g_interface_head, tailq) { + if (ifc->index == rtmp->ifa_index) { + /* add a new IP address to interface */ + if (ifc->num_ip_addresses >= SPDK_MAX_IP_PER_IFC) { + SPDK_ERRLOG("SPDK: number of IP addresses supported for %s excceded. limit=%d\n", + ifc->name, + SPDK_MAX_IP_PER_IFC); + break; + } + ifc->ip_address[ifc->num_ip_addresses] = ipv4_addr; + ifc->num_ip_addresses++; + break; + } + } + } + } + ret -= NLMSG_ALIGN(len); + nlmp = (struct nlmsghdr *)((char *)nlmp + NLMSG_ALIGN(len)); + } + ret = 0; + +exit: + close(netlink_fd); + return ret; +} + + +static void spdk_process_new_interface_msg(struct nlmsghdr *h) +{ + int len; + struct spdk_interface *ifc; + struct ifinfomsg *iface; + struct rtattr *attribute; + + iface = (struct ifinfomsg *)NLMSG_DATA(h); + + ifc = (struct spdk_interface *) malloc(sizeof(*ifc)); + if (ifc == NULL) { + SPDK_ERRLOG("%s: Malloc failed\n", __func__); + exit(1); + } + + memset(ifc, 0, sizeof(*ifc)); + + /* Set interface index */ + ifc->index = iface->ifi_index; + + len = h->nlmsg_len - NLMSG_LENGTH(sizeof(*iface)); + + /* Loop over all attributes for the NEWLINK message */ + for (attribute = IFLA_RTA(iface); RTA_OK(attribute, len); attribute = RTA_NEXT(attribute, len)) { + switch (attribute->rta_type) { + case IFLA_IFNAME: + if (if_indextoname(iface->ifi_index, ifc->name) == NULL) { + SPDK_ERRLOG("Indextoname failed!\n"); + exit(1); + } + break; + default: + break; + } + } + TAILQ_INSERT_TAIL(&g_interface_head, ifc, tailq); +} + +static uint32_t spdk_prepare_ifc_list(void) +{ + uint32_t ret = 0; + struct nl_req_s { + struct nlmsghdr hdr; + struct rtgenmsg gen; + struct ifinfomsg ifi; + }; + int netlink_fd; + struct sockaddr_nl local; /* Our local (user space) side of the communication */ + struct sockaddr_nl kernel; /* The remote (kernel space) side of the communication */ + + struct msghdr rtnl_msg; /* Generic msghdr struct for use with sendmsg */ + struct iovec io; /* IO vector for sendmsg */ + + struct nl_req_s req; /* Structure that describes the rtnetlink packet itself */ + char reply[16384]; /* a large buffer to receive lots of link information */ + + pid_t pid = getpid(); /* Our process ID to build the correct netlink address */ + int end = 0; /* some flag to end loop parsing */ + + /* + * Prepare netlink socket for kernel/user space communication + */ + netlink_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (netlink_fd < 0) { + SPDK_ERRLOG("socket failed!\n"); + return 1; + } + + memset(&local, 0, sizeof(local)); /* Fill-in local address information */ + local.nl_family = AF_NETLINK; + local.nl_pid = pid; + local.nl_groups = 0; + + /* RTNL socket is ready to use, prepare and send L2 request. */ + memset(&rtnl_msg, 0, sizeof(rtnl_msg)); + memset(&kernel, 0, sizeof(kernel)); + memset(&req, 0, sizeof(req)); + + kernel.nl_family = AF_NETLINK; /* Fill-in kernel address (destination of our message) */ + + req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); + req.hdr.nlmsg_type = RTM_GETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.hdr.nlmsg_seq = 1; + req.hdr.nlmsg_pid = pid; + + req.ifi.ifi_family = AF_UNSPEC; + req.ifi.ifi_type = 1; + + io.iov_base = &req; + io.iov_len = req.hdr.nlmsg_len; + rtnl_msg.msg_iov = &io; + rtnl_msg.msg_iovlen = 1; + rtnl_msg.msg_name = &kernel; + rtnl_msg.msg_namelen = sizeof(kernel); + + if (sendmsg(netlink_fd, &rtnl_msg, 0) == -1) { + SPDK_ERRLOG("Sendmsg failed!\n"); + ret = 1; + goto exit; + } + + /* Parse reply */ + while (!end) { + int len; + struct nlmsghdr *msg_ptr; /* Pointer to current message part */ + + struct msghdr rtnl_reply; /* Generic msghdr structure for use with recvmsg */ + struct iovec io_reply; + + memset(&io_reply, 0, sizeof(io_reply)); + memset(&rtnl_reply, 0, sizeof(rtnl_reply)); + + io.iov_base = reply; + io.iov_len = 8192; + rtnl_reply.msg_iov = &io; + rtnl_reply.msg_iovlen = 1; + rtnl_reply.msg_name = &kernel; + rtnl_reply.msg_namelen = sizeof(kernel); + + /* Read as much data as fits in the receive buffer */ + len = recvmsg(netlink_fd, &rtnl_reply, 0); + if (len) { + for (msg_ptr = (struct nlmsghdr *) reply; NLMSG_OK(msg_ptr, (uint32_t)len); + msg_ptr = NLMSG_NEXT(msg_ptr, len)) { + switch (msg_ptr->nlmsg_type) { + case NLMSG_DONE: /* This is the special meaning NLMSG_DONE message we asked for by using NLM_F_DUMP flag */ + end++; + break; + case RTM_NEWLINK: /* This is a RTM_NEWLINK message, which contains lots of information about a link */ + spdk_process_new_interface_msg(msg_ptr); + break; + default: + break; + } + } + } + } +exit: + close(netlink_fd); + return ret; +} + +static int spdk_interface_available(uint32_t ifc_index) +{ + struct spdk_interface *ifc_entry; + + pthread_mutex_lock(&interface_lock); + TAILQ_FOREACH(ifc_entry, &g_interface_head, tailq) { + if (ifc_entry->index == ifc_index) { + pthread_mutex_unlock(&interface_lock); + return 0; + } + } + pthread_mutex_unlock(&interface_lock); + + return -1; +} + +static int netlink_addr_msg(uint32_t ifc_idx, uint32_t ip_address, uint32_t create) +{ + int fd; + struct sockaddr_nl la; + struct sockaddr_nl pa; + struct msghdr msg; + struct iovec iov; + int ifal; + struct { + struct nlmsghdr n; + struct ifaddrmsg r; + char buf[16384]; + } req; + struct rtattr *rta; + + if (spdk_interface_available(ifc_idx)) + return -1; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + + /* setup local address & bind using this address. */ + bzero(&la, sizeof(la)); + la.nl_family = AF_NETLINK; + la.nl_pid = getpid(); + bind(fd, (struct sockaddr *) &la, sizeof(la)); + + /* initalize RTNETLINK request buffer. */ + bzero(&req, sizeof(req)); + + /* compute the initial length of the service request. */ + ifal = sizeof(struct ifaddrmsg); + + /* add first attrib: set IP addr and RTNETLINK buffer size. */ + rta = (struct rtattr *) req.buf; + rta->rta_type = IFA_ADDRESS; + rta->rta_len = sizeof(struct rtattr) + 4; + memcpy(((char *)rta) + sizeof(struct rtattr), &ip_address, sizeof(ip_address)); + ifal += rta->rta_len; + + /* add second attrib. */ + rta = (struct rtattr *)(((char *)rta) + rta->rta_len); + rta->rta_type = IFA_LOCAL; + rta->rta_len = sizeof(struct rtattr) + 4; + memcpy(((char *)rta) + sizeof(struct rtattr), &ip_address, sizeof(ip_address)); + ifal += rta->rta_len; + + /* setup the NETLINK header. */ + req.n.nlmsg_len = NLMSG_LENGTH(ifal); + if (create) { + req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_APPEND; + req.n.nlmsg_type = RTM_NEWADDR; + } else { + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_DELADDR; + } + + /* setup the service header (struct rtmsg). */ + req.r.ifa_family = AF_INET; + req.r.ifa_prefixlen = 32; /*hardcoded*/ + req.r.ifa_flags = IFA_F_PERMANENT | IFA_F_SECONDARY; + req.r.ifa_index = ifc_idx; + req.r.ifa_scope = 0; + + /* create the remote address to communicate. */ + bzero(&pa, sizeof(pa)); + pa.nl_family = AF_NETLINK; + + /* initialize & create the struct msghdr supplied to the sendmsg() function. */ + bzero(&msg, sizeof(msg)); + msg.msg_name = (void *) &pa; + msg.msg_namelen = sizeof(pa); + + /* place the pointer & size of the RTNETLINK message in the struct msghdr. */ + iov.iov_base = (void *) &req.n; + iov.iov_len = req.n.nlmsg_len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + /* send the RTNETLINK message to kernel. */ + sendmsg(fd, &msg, 0); + close(fd); + return 0; +} + +static void spdk_interface_ip_update(void) +{ + struct spdk_interface *ifc_entry; + + pthread_mutex_lock(&interface_lock); + TAILQ_FOREACH(ifc_entry, &g_interface_head, tailq) { + ifc_entry->num_ip_addresses = 0; + memset(ifc_entry->ip_address, 0, sizeof(ifc_entry->ip_address)); + } + spdk_get_ifc_ipv4(); + pthread_mutex_unlock(&interface_lock); +} + +static int +spdk_interface_init(void) +{ + TAILQ_INIT(&g_interface_head); + spdk_prepare_ifc_list(); + spdk_get_ifc_ipv4(); + return 0; +} + +static int spdk_interface_destroy(void) +{ + struct spdk_interface *ifc_entry; + + while (!TAILQ_EMPTY(&g_interface_head)) { + ifc_entry = TAILQ_FIRST(&g_interface_head); + TAILQ_REMOVE(&g_interface_head, ifc_entry, tailq); + free(ifc_entry); + } + return 0; +} + +int +spdk_interface_add_ip_address(int ifc_index, char *ip_addr) +{ + uint32_t addr; + + addr = inet_addr(ip_addr); + return netlink_addr_msg(ifc_index, addr, 1); +} + +int +spdk_interface_delete_ip_address(int ifc_index, char *ip_addr) +{ + uint32_t addr; + + addr = inet_addr(ip_addr); + return netlink_addr_msg(ifc_index, addr, 0); +} + +void *spdk_interface_get_list(void) +{ + spdk_interface_ip_update(); + return &g_interface_head; +} + +#else /* Not Linux */ + +static int +spdk_interface_init(void) +{ + return 0; +} + +static int +spdk_interface_destroy(void) +{ + return 0; +} + +int +spdk_interface_add_ip_address(int ifc_index, char *ip_addr) +{ + return -1; +} + +int +spdk_interface_delete_ip_address(int ifc_index, char *ip_addr) +{ + return -1; +} + +void * +spdk_interface_get_list(void) +{ + return NULL; +} + +#endif + +SPDK_SUBSYSTEM_REGISTER(interface, spdk_interface_init, spdk_interface_destroy, NULL) diff --git a/lib/net/net_framework_default.c b/lib/net/net_framework_default.c new file mode 100644 index 0000000000..43c4a84414 --- /dev/null +++ b/lib/net/net_framework_default.c @@ -0,0 +1,67 @@ +/*- + * 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/event.h" +#include "spdk/net.h" + +__attribute__((weak)) +const char *spdk_net_framework_get_name(void) +{ + return "default"; +} + +__attribute__((weak)) +int spdk_net_framework_start(void) +{ + return 0; +} + +__attribute__((weak)) +int spdk_net_framework_fini(void) +{ + return 0; +} + +__attribute__((weak)) +void spdk_net_framework_clear_socket_association(int sock) +{ +} + +__attribute__((weak)) +int spdk_net_framework_idle_time(void) +{ + return IDLE_INTERVAL_TIME_IN_US; +} + +SPDK_SUBSYSTEM_REGISTER(net_framework, spdk_net_framework_start, spdk_net_framework_fini, NULL) +SPDK_SUBSYSTEM_DEPEND(net_framework, interface) diff --git a/lib/net/net_rpc.c b/lib/net/net_rpc.c new file mode 100644 index 0000000000..f6f9863b3a --- /dev/null +++ b/lib/net/net_rpc.c @@ -0,0 +1,180 @@ +/*- + * 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 +#include +#include +#include + +#include "spdk/log.h" +#include "spdk/rpc.h" +#include "spdk/net.h" + +struct rpc_ip_address { + int32_t ifc_index; + char *ip_address; +}; + +static void +free_rpc_ip_address(struct rpc_ip_address *req) +{ + free(req->ip_address); +} + +static const struct spdk_json_object_decoder rpc_ip_address_decoders[] = { + {"ifc_index", offsetof(struct rpc_ip_address, ifc_index), spdk_json_decode_int32}, + {"ip_address", offsetof(struct rpc_ip_address, ip_address), spdk_json_decode_string}, +}; + +static void +spdk_rpc_add_ip_address(struct spdk_jsonrpc_server_conn *conn, + const struct spdk_json_val *params, + const struct spdk_json_val *id) +{ + struct rpc_ip_address req = {}; + struct spdk_json_write_ctx *w; + + if (spdk_json_decode_object(params, rpc_ip_address_decoders, + sizeof(rpc_ip_address_decoders) / sizeof(*rpc_ip_address_decoders), + &req)) { + SPDK_TRACELOG(SPDK_TRACE_DEBUG, "spdk_json_decode_object failed\n"); + goto invalid; + } + + if (spdk_interface_add_ip_address(req.ifc_index, req.ip_address)) { + goto invalid; + } + + free_rpc_ip_address(&req); + + if (id == NULL) { + return; + } + + w = spdk_jsonrpc_begin_result(conn, id); + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(conn, w); + return; + +invalid: + spdk_jsonrpc_send_error_response(conn, id, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + free_rpc_ip_address(&req); +} +SPDK_RPC_REGISTER("add_ip_address", spdk_rpc_add_ip_address) + +static void +spdk_rpc_delete_ip_address(struct spdk_jsonrpc_server_conn *conn, + const struct spdk_json_val *params, + const struct spdk_json_val *id) +{ + struct rpc_ip_address req = {}; + struct spdk_json_write_ctx *w; + + if (spdk_json_decode_object(params, rpc_ip_address_decoders, + sizeof(rpc_ip_address_decoders) / sizeof(*rpc_ip_address_decoders), + &req)) { + SPDK_TRACELOG(SPDK_TRACE_DEBUG, "spdk_json_decode_object failed\n"); + goto invalid; + } + + if (spdk_interface_delete_ip_address(req.ifc_index, req.ip_address)) { + goto invalid; + } + + free_rpc_ip_address(&req); + + if (id == NULL) { + return; + } + + w = spdk_jsonrpc_begin_result(conn, id); + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(conn, w); + return; + +invalid: + spdk_jsonrpc_send_error_response(conn, id, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + free_rpc_ip_address(&req); +} +SPDK_RPC_REGISTER("delete_ip_address", spdk_rpc_delete_ip_address) + +static void +spdk_rpc_get_interfaces(struct spdk_jsonrpc_server_conn *conn, + const struct spdk_json_val *params, + const struct spdk_json_val *id) +{ + struct spdk_json_write_ctx *w; + TAILQ_HEAD(, spdk_interface) *interface_head = spdk_interface_get_list(); + struct spdk_interface *ifc; + char *ip_address; + struct in_addr inaddr; + uint32_t i; + + if (params != NULL) { + spdk_jsonrpc_send_error_response(conn, id, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "get_interfaces requires no parameters"); + return; + } + + if (id == NULL) { + return; + } + + w = spdk_jsonrpc_begin_result(conn, id); + spdk_json_write_array_begin(w); + + TAILQ_FOREACH(ifc, interface_head, tailq) { + spdk_json_write_object_begin(w); + + spdk_json_write_name(w, "name"); + spdk_json_write_string(w, ifc->name); + + spdk_json_write_name(w, "ifc_index"); + spdk_json_write_int32(w, ifc->index); + + spdk_json_write_name(w, "ip_addr"); + spdk_json_write_array_begin(w); + for (i = 0; i < ifc->num_ip_addresses; i++) { + memcpy(&inaddr, &ifc->ip_address[i], sizeof(uint32_t)); + ip_address = inet_ntoa(inaddr); + spdk_json_write_string(w, ip_address); + } + spdk_json_write_array_end(w); + + spdk_json_write_object_end(w); + } + spdk_json_write_array_end(w); + + spdk_jsonrpc_end_result(conn, w); +} +SPDK_RPC_REGISTER("get_interfaces", spdk_rpc_get_interfaces) diff --git a/lib/net/sock.c b/lib/net/sock.c new file mode 100644 index 0000000000..1a6cdf5c12 --- /dev/null +++ b/lib/net/sock.c @@ -0,0 +1,350 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spdk/event.h" +#include "spdk/log.h" +#include "spdk/net.h" + +#define MAX_TMPBUF 1024 +#define TIMEOUT_RW 60 +#define POLLWAIT 1000 +#define PORTNUMLEN 32 + +static int get_addr_str(struct sockaddr_in *paddr, char *host, int hlen) +{ + char buf[64]; + + if (paddr == NULL || host == NULL) + return -1; + + uint8_t *pa = (uint8_t *)&paddr->sin_addr.s_addr; + sprintf(buf, "%u.%u.%u.%u", pa[0], pa[1], pa[2], pa[3]); + strncpy(host, buf, hlen); + + return 0; +} + +int +spdk_sock_getaddr(int sock, char *saddr, int slen, char *caddr, int clen) +{ + struct sockaddr_storage sa; + socklen_t salen; + int rc; + + memset(&sa, 0, sizeof sa); + salen = sizeof sa; + rc = getsockname(sock, (struct sockaddr *) &sa, &salen); + if (rc != 0) { + SPDK_ERRLOG("getsockname() failed (errno=%d)\n", errno); + return -1; + } + + switch (sa.ss_family) { + case AF_UNIX: + /* Acceptable connection types that don't have IPs */ + return 0; + case AF_INET: + case AF_INET6: + /* Code below will get IP addresses */ + break; + default: + /* Unsupported socket family */ + return -1; + } + + rc = get_addr_str((struct sockaddr_in *)&sa, saddr, slen); + if (rc != 0) { + SPDK_ERRLOG("getnameinfo() failed (errno=%d)\n", errno); + return -1; + } + + memset(&sa, 0, sizeof sa); + salen = sizeof sa; + rc = getpeername(sock, (struct sockaddr *) &sa, &salen); + if (rc != 0) { + SPDK_ERRLOG("getpeername() failed (errno=%d)\n", errno); + return -1; + } + + rc = get_addr_str((struct sockaddr_in *)&sa, caddr, clen); + if (rc != 0) { + SPDK_ERRLOG("getnameinfo() failed (errno=%d)\n", errno); + return -1; + } + + return 0; +} + +enum spdk_sock_create_type { + SPDK_SOCK_CREATE_LISTEN, + SPDK_SOCK_CREATE_CONNECT, +}; + +static int +spdk_sock_create(const char *ip, int port, enum spdk_sock_create_type type) +{ + char buf[MAX_TMPBUF]; + char portnum[PORTNUMLEN]; + char *p; + struct addrinfo hints, *res, *res0; + int sock, nonblock; + int val = 1; + int rc; + + if (ip == NULL) + return -1; + if (ip[0] == '[') { + snprintf(buf, sizeof(buf), "%s", ip + 1); + p = strchr(buf, ']'); + if (p != NULL) + *p = '\0'; + ip = (const char *) &buf[0]; + if (strcasecmp(ip, "*") == 0) { + snprintf(buf, sizeof(buf), "::"); + ip = (const char *) &buf[0]; + } + } else if (strcasecmp(ip, "*") == 0) { + snprintf(buf, sizeof(buf), "0.0.0.0"); + ip = (const char *) &buf[0]; + } + + snprintf(portnum, sizeof portnum, "%d", port); + memset(&hints, 0, sizeof hints); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV; + hints.ai_flags |= AI_PASSIVE; + hints.ai_flags |= AI_NUMERICHOST; + rc = getaddrinfo(ip, portnum, &hints, &res0); + if (rc != 0) { + SPDK_ERRLOG("getaddrinfo() failed (errno=%d)\n", errno); + return -1; + } + + /* try listen */ + sock = -1; + for (res = res0; res != NULL; res = res->ai_next) { +retry: + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sock < 0) { + /* error */ + continue; + } + rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val); + if (rc != 0) { + close(sock); + /* error */ + continue; + } + rc = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof val); + if (rc != 0) { + close(sock); + /* error */ + continue; + } + + if (type == SPDK_SOCK_CREATE_LISTEN) { + rc = bind(sock, res->ai_addr, res->ai_addrlen); + if (rc != 0) { + SPDK_ERRLOG("bind() failed, errno = %d\n", errno); + switch (errno) { + case EINTR: + /* interrupted? */ + close(sock); + goto retry; + case EADDRNOTAVAIL: + SPDK_ERRLOG("IP address %s not available. " + "Verify IP address in config file " + "and make sure unbind script is " + "run before starting spdk app.\n", ip); + /* fallthrough */ + default: + /* try next family */ + close(sock); + sock = -1; + continue; + } + } + /* bind OK */ + rc = listen(sock, 512); + if (rc != 0) { + SPDK_ERRLOG("listen() failed, errno = %d\n", errno); + close(sock); + sock = -1; + break; + } + } else if (type == SPDK_SOCK_CREATE_CONNECT) { + rc = connect(sock, res->ai_addr, res->ai_addrlen); + if (rc != 0) { + SPDK_ERRLOG("connect() failed, errno = %d\n", errno); + /* try next family */ + close(sock); + sock = -1; + continue; + } + } + + nonblock = 1; + rc = ioctl(sock, FIONBIO, &nonblock); + if (rc != 0) { + SPDK_ERRLOG("ioctl(FIONBIO) failed\n"); + close(sock); + sock = -1; + break; + } + break; + } + freeaddrinfo(res0); + + if (sock < 0) { + return -1; + } + return sock; +} + +int +spdk_sock_listen(const char *ip, int port) +{ + return spdk_sock_create(ip, port, SPDK_SOCK_CREATE_LISTEN); +} + +int +spdk_sock_connect(const char *ip, int port) +{ + return spdk_sock_create(ip, port, SPDK_SOCK_CREATE_CONNECT); +} + +int +spdk_sock_accept(int sock) +{ + struct sockaddr_storage sa; + socklen_t salen; + + memset(&sa, 0, sizeof(sa)); + salen = sizeof(sa); + return accept(sock, (struct sockaddr *)&sa, &salen); +} + +int +spdk_sock_close(int sock) +{ + return close(sock); +} + +ssize_t +spdk_sock_recv(int sock, void *buf, size_t len) +{ + return recv(sock, buf, len, MSG_DONTWAIT); +} + +ssize_t +spdk_sock_writev(int sock, struct iovec *iov, int iovcnt) +{ + return writev(sock, iov, iovcnt); +} + +int +spdk_sock_set_recvlowat(int s, int nbytes) +{ + int val; + int rc; + + val = nbytes; + rc = setsockopt(s, SOL_SOCKET, SO_RCVLOWAT, &val, sizeof val); + if (rc != 0) + return -1; + return 0; +} + +int +spdk_sock_set_recvbuf(int sock, int sz) +{ + return setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + &sz, sizeof(sz)); +} + +int +spdk_sock_set_sendbuf(int sock, int sz) +{ + return setsockopt(sock, SOL_SOCKET, SO_SNDBUF, + &sz, sizeof(sz)); +} + +bool +spdk_sock_is_ipv6(int sock) +{ + struct sockaddr_storage sa; + socklen_t salen; + int rc; + + memset(&sa, 0, sizeof sa); + salen = sizeof sa; + rc = getsockname(sock, (struct sockaddr *) &sa, &salen); + if (rc != 0) { + SPDK_ERRLOG("getsockname() failed (errno=%d)\n", errno); + return false; + } + + return (sa.ss_family == AF_INET6); +} + +bool +spdk_sock_is_ipv4(int sock) +{ + struct sockaddr_storage sa; + socklen_t salen; + int rc; + + memset(&sa, 0, sizeof sa); + salen = sizeof sa; + rc = getsockname(sock, (struct sockaddr *) &sa, &salen); + if (rc != 0) { + SPDK_ERRLOG("getsockname() failed (errno=%d)\n", errno); + return false; + } + + return (sa.ss_family == AF_INET); +}