This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
zhangyang-libzt/attic/VirtualSocketLayer.cpp
2018-07-19 17:19:06 -07:00

1251 lines
31 KiB
C++

/*
* ZeroTier SDK - Network Virtualization Everywhere
* Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial closed-source software that incorporates or links
* directly against ZeroTier software without disclosing the source code
* of your own application.
*/
/**
* @file
*
* VirtualSocket management layer
*/
#include "libztDefs.h"
#ifdef ZT_VIRTUAL_SOCKET
#ifdef __linux__
#include <net/if.h>
#include <net/ethernet.h>
#include <linux/if_packet.h>
#endif
extern int errno;
#include "libzt.h"
#include "VirtualTap.h"
#include "RingBuffer.h"
#include "VirtualSocket.h"
#include "VirtualSocketLayer.h"
#include "VirtualBindingPair.h"
#include "ZT1Service.h"
#include "Utilities.h"
#ifdef STACK_LWIP
#include "lwIP.h"
//#include "lwip/sockets.h"
#endif
#ifdef STACK_PICO
#include "picoTCP.h"
#include "pico_stack.h"
#include "pico_socket.h"
#endif
#include <map>
#include <utility>
std::map<int, std::pair<VirtualSocket*,VirtualTap*>*> fdmap;
std::map<int, VirtualSocket*> unmap;
// externs from VirtualSocketLayer.h
//std::map<int, VirtualSocket*> unmap;
//std::map<int, std::pair<VirtualSocket*,VirtualTap*>*> fdmap;
// int socket_family, int socket_type, int protocol
int virt_socket(int socket_family, int socket_type, int protocol) {
DEBUG_EXTRA();
int err = errno = 0;
if (socket_family < 0 || socket_type < 0 || protocol < 0) {
errno = EINVAL;
return -1;
}
if (socket_type == SOCK_SEQPACKET) {
DEBUG_ERROR("SOCK_SEQPACKET not yet supported.");
errno = EPROTONOSUPPORT; // seemingly closest match
return -1;
}
if (socket_type == SOCK_RAW) {
// VirtualSocket is only used to associate a socket with a VirtualTap, it has no other implication
VirtualSocket *vs = new VirtualSocket();
vs->socket_family = socket_family;
vs->socket_type = socket_type;
vs->protocol = protocol;
add_unassigned_virt_socket(vs->app_fd, vs);
return vs->app_fd;
}
#if defined(STACK_PICO)
struct pico_socket *p;
err = rd_pico_socket(&p, socket_family, socket_type, protocol);
if (err == false && p) {
VirtualSocket *vs = new VirtualSocket();
vs->socket_family = socket_family;
vs->socket_type = socket_type;
vs->pcb = p;
add_unassigned_virt_socket(vs->app_fd, vs);
err = vs->app_fd; // return one end of the socketpair
}
else {
DEBUG_ERROR("failed to create pico_socket");
errno = ENOMEM;
err = -1;
}
#endif
#if defined(STACK_LWIP)
// TODO: check for max lwIP timers/sockets
void *pcb;
err = rd_lwip_socket(&pcb, socket_family, socket_type, protocol);
if (pcb) {
VirtualSocket *vs = new VirtualSocket();
vs->socket_family = socket_family;
vs->socket_type = socket_type;
vs->pcb = pcb;
add_unassigned_virt_socket(vs->app_fd, vs);
// return one end of the socketpair for the app to use
err = vs->app_fd;
}
else {
DEBUG_ERROR("failed to create lwip pcb");
errno = ENOMEM;
err = -1;
}
#endif
//#if defined(DEFAULT_VS_LINGER)
/*
if (socket_type == SOCK_STREAM) {
linger lin;
unsigned int y=sizeof(lin);
lin.l_onoff=1;
lin.l_linger=10;
int fd = err;
if ((err = virt_setsockopt(fd, SOL_SOCKET, SO_LINGER, (void*)(&lin), y)) < 0) {
DEBUG_ERROR("error while setting default linger time on socket");
errno = -1; // TODO
return -1;
}
err = fd;
}
*/
//#endif
return err;
}
int virt_connect(int fd, const struct sockaddr *addr, socklen_t addrlen) {
DEBUG_INFO("fd=%d",fd);
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
DEBUG_ERROR("invalid socket, unable to locate VirtualSocket for fd=%d", fd);
errno = EBADF;
return -1;
}
struct pico_socket *ps = (struct pico_socket*)(vs->pcb);
if (addr == NULL) {
DEBUG_ERROR("invalid address for fd=%d", fd);
errno = EINVAL;
return -1;
}
if (addrlen <= 0) {
DEBUG_ERROR("invalid address length for fd=%d", fd);
errno = EINVAL;
return -1;
}
// TODO: Handle bad address lengths, right now this call will still
// succeed with a complete connect despite a bad address length.
// DEBUG_EXTRA("fd = %d, %s : %d", fd, ipstr, ntohs(port));
ZeroTier::InetAddress inet;
sockaddr2inet(vs->socket_family, addr, &inet);
VirtualTap *tap = getTapByAddr(&inet);
if (tap == NULL) {
DEBUG_ERROR("no route to host, could not find appropriate VirtualTap for fd=%d", fd);
errno = ENETUNREACH;
return -1;
}
#if defined(STACK_PICO)
// pointer to virtual tap we use in callbacks from the stack
ps->priv = new VirtualBindingPair(tap, vs);
#endif
#if defined(STACK_LWIP)
#endif
if ((err = tap->Connect(vs, addr, addrlen)) < 0) {
DEBUG_ERROR("error while connecting socket");
// errno will be set by tap->Connect
return -1;
}
// assign this VirtualSocket to the tap we decided on
tap->_VirtualSockets.push_back(vs);
vs->tap = tap;
vs->sock = tap->_phy.wrapSocket(vs->sdk_fd, vs);
// TODO: Consolidate these calls
del_unassigned_virt_socket(fd);
add_assigned_virt_socket(tap, vs, fd);
// save peer addr, for calls like getpeername
memcpy(&(vs->peer_addr), addr, sizeof(vs->peer_addr));
// Below will simulate BLOCKING/NON-BLOCKING behaviour
// NOTE: pico_socket_connect() will return 0 if no error happens immediately, but that doesn't indicate
// the connection was completed, for that we must wait for a callback from the stack. During that
// callback we will place the VirtualSocket in a VS_STATE_UNHANDLED_CONNECTED state to signal
// to the multiplexer logic that this connection is complete and a success value can be sent to the
// user application
int f_err, blocking = 1;
if ((f_err = fcntl(fd, F_GETFL, 0)) < 0) {
DEBUG_ERROR("fcntl error, err=%s, errno=%d", f_err, errno);
// errno will be set by fcntl
return -1;
}
blocking = !(f_err & O_NONBLOCK);
if (blocking == false) {
errno = EINPROGRESS; // can't connect immediately
err = -1;
}
if (blocking == true) {
bool complete = false;
while (true) {
// FIXME: locking and unlocking so often might cause significant performance overhead while outgoing VirtualSockets
// are being established (also applies to accept())
nanosleep((const struct timespec[]) {{0, (ZT_CONNECT_RECHECK_DELAY * 1000000)}}, NULL);
tap->_tcpconns_m.lock();
for (int i=0; i<tap->_VirtualSockets.size(); i++) {
#if defined(STACK_PICO)
DEBUG_EXTRA("checking tap->_VirtualSockets[i]=%p", tap->_VirtualSockets[i]);
if (tap->_VirtualSockets[i]->get_state() == PICO_ERR_ECONNRESET) {
errno = ECONNRESET;
DEBUG_ERROR("ECONNRESET");
err = -1;
}
#endif
#if defined(STACK_LWIP)
#endif
if (tap->_VirtualSockets[i]->get_state() == VS_STATE_UNHANDLED_CONNECTED) {
tap->_VirtualSockets[i]->set_state(VS_STATE_CONNECTED);
complete = true;
}
}
tap->_tcpconns_m.unlock();
if (complete) {
break;
}
}
}
return err;
}
int virt_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
DEBUG_ERROR("no VirtualSocket for fd=%d", fd);
errno = ENOTSOCK;
return -1;
}
struct pico_socket *ps = (struct pico_socket*)(vs->pcb);
// detect local interface binds
VirtualTap *tap = NULL;
if (vs->socket_family == AF_INET) {
struct sockaddr_in *in4 = (struct sockaddr_in *)addr;
if (in4->sin_addr.s_addr == INADDR_ANY) {
DEBUG_EXTRA("AF_INET, INADDR_ANY, binding to all interfaces");
// grab first vtap
if (vtaps.size()) {
tap = (VirtualTap*)(vtaps[0]); // pick any vtap
}
}
if (in4->sin_addr.s_addr == 0x7f000001) {
DEBUG_EXTRA("127.0.0.1, will bind to appropriate vtap when connection is inbound");
if (vtaps.size()) {
tap = (VirtualTap*)(vtaps[0]); // pick any vtap
}
}
}
if (vs->socket_family == AF_INET6) {
struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr;
if (memcmp((void*)&(in6->sin6_addr), (void*)&(in6addr_any), sizeof(in6addr_any)) == 0) {
DEBUG_EXTRA("AF_INET6, in6addr_any, binding to all interfaces");
if (vtaps.size()) {
tap = (VirtualTap*)(vtaps[0]); // pick any vtap
}
}
}
ZeroTier::InetAddress inet;
sockaddr2inet(vs->socket_family, addr, &inet);
if (tap == NULL) {
tap = getTapByAddr(&inet);
}
if (tap == NULL) {
DEBUG_ERROR("no matching interface to bind to, could not find appropriate VirtualTap for fd=%d", fd);
errno = ENETUNREACH;
return -1;
}
#if defined(STACK_PICO)
// used in callbacks from network stack
ps->priv = new VirtualBindingPair(tap, vs);
#endif
tap->addVirtualSocket(vs);
err = tap->Bind(vs, addr, addrlen);
if (err == 0) { // success
vs->tap = tap;
del_unassigned_virt_socket(fd);
add_assigned_virt_socket(tap, vs, fd);
}
return err;
}
int virt_listen(int fd, int backlog) {
DEBUG_EXTRA("fd=%d", fd);
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
DEBUG_ERROR("!vs");
return -1;
}
//std::pair<VirtualSocket*, VirtualTap*> *p = get_assigned_virtual_pair(fd);
_multiplexer_lock.lock();
VirtualTap *tap = vs->tap;
if (tap == NULL || vs == NULL) {
DEBUG_ERROR("unable to locate tap interface for file descriptor");
errno = EBADF;
return -1;
}
backlog = backlog > 128 ? 128 : backlog; // See: /proc/sys/net/core/somaxconn
err = tap->Listen(vs, backlog);
vs->set_state(VS_STATE_LISTENING);
_multiplexer_lock.unlock();
return err;
}
int virt_accept(int fd, struct sockaddr *addr, socklen_t *addrlen) {
int err = errno = 0;
DEBUG_EXTRA("fd=%d", fd);
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
if (addr && *addrlen <= 0) {
DEBUG_ERROR("invalid address length given");
errno = EINVAL; // TODO, not actually a valid error for this function
return -1;
}
if (virt_can_provision_new_socket(SOCK_STREAM) == false) {
DEBUG_ERROR("cannot provision additional socket due to limitation of network stack");
errno = EMFILE;
return -1;
}
//std::pair<VirtualSocket*, VirtualTap*> *p = get_assigned_virtual_pair(fd);
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
DEBUG_ERROR("!vs");
return -1;
}
_multiplexer_lock.lock();
//std::pair<VirtualSocket*, VirtualTap*> *p = get_assigned_virtual_pair(fd);
VirtualTap *tap = vs->tap;
// BLOCKING: loop and keep checking until we find a newly accepted VirtualSocket
int f_err, blocking = 1;
if ((f_err = fcntl(fd, F_GETFL, 0)) < 0) {
DEBUG_ERROR("fcntl error, err = %s, errno = %d", f_err, errno);
err = -1;
}
else {
blocking = !(f_err & O_NONBLOCK);
}
VirtualSocket *accepted_vs;
if (err == false) {
if (blocking == false) { // non-blocking
DEBUG_EXTRA("EWOULDBLOCK, assuming non-blocking mode");
errno = EWOULDBLOCK;
err = -1;
accepted_vs = tap->Accept(vs);
}
else { // blocking
while (true) {
nanosleep((const struct timespec[]) {{0, (ZT_ACCEPT_RECHECK_DELAY * 1000000)}}, NULL);
accepted_vs = tap->Accept(vs);
if (accepted_vs)
break; // accepted fd = err
}
}
if (accepted_vs) {
add_assigned_virt_socket(tap, accepted_vs, accepted_vs->app_fd);
err = accepted_vs->app_fd;
}
}
if (err > 0) {
if (addr && *addrlen) {
*addrlen = *addrlen < sizeof(accepted_vs->peer_addr) ? *addrlen : sizeof(accepted_vs->peer_addr);
// copy address into provided address buffer and len buffer
memcpy(addr, &(accepted_vs->peer_addr), *addrlen);
}
}
return err;
}
#if defined(__linux__)
int virt_accept4(int fd, struct sockaddr *addr, socklen_t *addrlen, int flags)
{
errno = 0;
//DEBUG_INFO("fd=%d", fd);
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
if (flags & SOCK_NONBLOCK) {
fcntl(fd, F_SETFL, O_NONBLOCK);
}
if (flags & SOCK_CLOEXEC) {
fcntl(fd, F_SETFL, FD_CLOEXEC);
}
addrlen = !addr ? 0 : addrlen;
return virt_accept(fd, addr, addrlen);
}
#endif
int virt_setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen)
{
DEBUG_EXTRA("fd=%d, level=%d, optname=%d", fd, level, optname);
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
DEBUG_ERROR("invalid fd=%d", fd);
errno = EBADF;
return -1;
}
#if defined(STACK_PICO)
err = rd_pico_setsockopt(vs, level, optname, optval, optlen);
#endif
#if defined(STACK_LWIP)
err = rd_lwip_setsockopt(vs, level, optname, optval, optlen);
#endif
return err;
}
int virt_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen)
{
DEBUG_EXTRA("fd=%d, level=%d, optname=%d", fd, level, optname);
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
DEBUG_ERROR("invalid fd=%d", fd);
errno = EBADF;
return -1;
}
#if defined(STACK_PICO)
err = rd_pico_getsockopt(vs, level, optname, optval, optlen);
#endif
#if defined(STACK_LWIP)
err = rd_lwip_getsockopt(vs, level, optname, optval, optlen);
#endif
return err;
}
int virt_getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen)
{
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
// TODO
return err;
}
int virt_getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen)
{
DEBUG_INFO("fd=%d", fd);
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
errno = ENOTCONN;
return -1;
}
memcpy(addr, &(vs->peer_addr), sizeof(struct sockaddr_storage));
return err;
}
int virt_gethostname(char *name, size_t len)
{
errno = 0;
return gethostname(name, len);
}
int virt_sethostname(const char *name, size_t len)
{
errno = 0;
return sethostname(name, len);
}
int virt_close(int fd)
{
DEBUG_EXTRA("fd=%d", fd);
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
DEBUG_ERROR("no vs found for fd=%d", fd);
errno = EBADF;
return -1;
}
del_virt_socket(fd);
if (vs->tap) {
vs->tap->Close(vs);
}
delete vs;
vs = NULL;
return err;
}
/*
int virt_poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
errno = 0;
return poll(fds, nfds, timeout);
}
*/
int virt_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
{
errno = 0;
return select(nfds, readfds, writefds, exceptfds, timeout);
}
int virt_fcntl(int fd, int cmd, int flags)
{
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
DEBUG_ERROR("invalid vs for fd=%d", fd);
errno = EBADF;
return -1;
}
err = fcntl(fd, cmd, flags);
return err;
}
int virt_ioctl(int fd, unsigned long request, void *argp)
{
int err = errno = -1;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
else {
#if defined(__linux__)
/*
if (argp)
{
struct ifreq *ifr = (struct ifreq *)argp;
VirtualTap *tap = getTapByName(ifr->ifr_name);
if (tap == NULL) {
DEBUG_ERROR("unable to locate tap interface with that name");
err = -1;
errno = EINVAL;
}
// index of VirtualTap interface
if (request == SIOCGIFINDEX) {
ifr->ifr_ifindex = tap->ifindex;
err = 0;
}
// MAC addres or VirtualTap
if (request == SIOCGIFHWADDR) {
tap->_mac.copyTo(&(ifr->ifr_hwaddr.sa_data), sizeof(ifr->ifr_hwaddr.sa_data));
err = 0;
}
// IP address of VirtualTap
if (request == SIOCGIFADDR) {
struct sockaddr_in *in4 = (struct sockaddr_in *)&(ifr->ifr_addr);
memcpy(&(in4->sin_addr.s_addr), tap->_ips[0].rawIpData(), sizeof(ifr->ifr_addr));
err = 0;
}
}
else {
DEBUG_INFO("!argp");
}
*/
#else
// err = ioctl(fd, request, argp);
err = -1;
#endif
}
return err;
}
ssize_t virt_sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen)
{
//DEBUG_TRANS("fd=%d", fd);
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
if (len == 0) {
return 0;
}
if (len > ZT_SOCKET_MSG_BUF_SZ) {
DEBUG_ERROR("msg is too long to be sent atomically (len=%d)", len);
errno = EMSGSIZE;
return -1;
}
if (buf == NULL) {
DEBUG_ERROR("msg buf is null");
errno = EINVAL;
return -1;
}
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
DEBUG_ERROR("no vs found for fd=%x", fd);
errno = EBADF;
return -1;
}
ZeroTier::InetAddress iaddr;
VirtualTap *tap;
if (vs->socket_type == SOCK_DGRAM) {
if (vs->socket_family == AF_INET)
{
char ipstr[INET_ADDRSTRLEN];
memset(ipstr, 0, INET_ADDRSTRLEN);
inet_ntop(AF_INET,
(const void *)&((struct sockaddr_in *)addr)->sin_addr.s_addr, ipstr, INET_ADDRSTRLEN);
iaddr.fromString(ipstr);
}
if (vs->socket_family == AF_INET6)
{
char ipstr[INET6_ADDRSTRLEN];
memset(ipstr, 0, INET6_ADDRSTRLEN);
inet_ntop(AF_INET6,
(const void *)&((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, ipstr, INET6_ADDRSTRLEN);
// TODO: This is a hack, determine a proper way to do this
char addrstr[INET6_ADDRSTRLEN];
sprintf(addrstr, "%s%s", ipstr, std::string("/40").c_str());
iaddr.fromString(addrstr);
}
// get tap
tap = getTapByAddr(&iaddr);
if (tap == NULL) {
DEBUG_INFO("SOCK_DGRAM, tap not found");
errno = EDESTADDRREQ; // TODO: double check this is the best errno to report
return -1;
}
// write
if ((err = tap->SendTo(vs, buf, len, flags, addr, addrlen)) < 0) {
DEBUG_ERROR("error while attempting to sendto");
errno = EINVAL; // TODO: Not correct, but what else could we use?
}
}
if (vs->socket_type == SOCK_RAW)
{
struct sockaddr_ll *socket_address = (struct sockaddr_ll *)addr;
VirtualTap *tap = getTapByIndex(socket_address->sll_ifindex);
if (tap) {
DEBUG_INFO("found interface of ifindex=%d", tap->ifindex);
err = tap->Write(vs, (void*)buf, len);
}
else {
DEBUG_ERROR("unable to locate tap of ifindex=%d", socket_address->sll_ifindex);
err = -1;
errno = EINVAL;
}
}
return err;
}
ssize_t virt_send(int fd, const void *buf, size_t len, int flags)
{
// DEBUG_TRANS("fd=%d", fd);
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
if (len == 0) {
return 0;
}
if (len > ZT_SOCKET_MSG_BUF_SZ) {
DEBUG_ERROR("msg is too long to be sent atomically (len=%d)", len);
errno = EMSGSIZE;
return -1;
}
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
DEBUG_ERROR("invalid vs for fd=%d", fd);
errno = EBADF;
return -1;
}
if (vs->socket_type != SOCK_STREAM) {
DEBUG_ERROR("the socket is not connection-mode, and no peer address is set for fd=%d", fd);
errno = EDESTADDRREQ;
return -1;
}
if (flags & MSG_DONTROUTE) {
DEBUG_INFO("MSG_DONTROUTE not implemented yet");
errno = EINVAL;
return -1;
}
if (flags & MSG_DONTWAIT) {
// The stack drivers and stack are inherently non-blocking by design, but we
// still need to modify the unix pipe connecting them to the application:
fcntl(fd, F_SETFL, O_NONBLOCK);
}
if (flags & MSG_EOR) {
DEBUG_INFO("MSG_EOR not implemented yet");
errno = EINVAL;
return -1;
}
if (flags & MSG_OOB) {
DEBUG_INFO("MSG_OOB not implemented yet");
errno = EINVAL;
return -1;
}
#if defined(__linux__)
if (flags & MSG_CONFIRM) {
DEBUG_INFO("MSG_CONFIRM not implemented yet");
errno = EINVAL;
return -1;
}
if (flags & MSG_MORE) {
DEBUG_INFO("MSG_MORE not implemented yet");
errno = EINVAL;
return -1;
}
if (flags & MSG_NOSIGNAL) {
DEBUG_INFO("MSG_NOSIGNAL not implemented yet");
errno = EINVAL;
return -1;
}
#endif
err = write(fd, buf, len);
// restore "per-call" flags
if (flags & MSG_DONTWAIT) {
int saved_flags = fcntl(fd, F_GETFL);
if (fcntl(fd, F_SETFL, saved_flags & ~O_NONBLOCK) < 0) { // mask out the blocking flag
return -1;
}
}
return err;
}
// TODO
ssize_t virt_sendmsg(int fd, const struct msghdr *msg, int flags)
{
DEBUG_TRANS("fd=%d", fd);
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
else {
err = sendmsg(fd, msg, flags);
}
return err;
}
ssize_t virt_recv(int fd, void *buf, size_t len, int flags)
{
DEBUG_TRANS("fd=%d", fd);
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
DEBUG_ERROR("invalid vs for fd=%d", fd);
errno = EBADF;
return -1;
}
if (vs->socket_type != SOCK_STREAM) {
DEBUG_ERROR("the socket is not connection-mode, and no peer address is set for fd=%d", fd);
errno = EDESTADDRREQ;
return -1;
}
if (vs->get_state() != VS_STATE_CONNECTED) {
DEBUG_ERROR("the socket is not in a connected state, fd=%d", fd);
errno = ENOTCONN;
return -1;
}
if (flags & MSG_DONTWAIT) {
// The stack drivers and stack are inherently non-blocking by design, but we
// still need to modify the unix pipe connecting them to the application:
fcntl(fd, F_SETFL, O_NONBLOCK);
}
if (flags & MSG_OOB) {
DEBUG_INFO("MSG_OOB not implemented yet");
errno = EINVAL;
return -1;
}
if (flags & MSG_TRUNC) {
DEBUG_INFO("MSG_TRUNC not implemented yet");
errno = EINVAL;
return -1;
}
if (flags & MSG_WAITALL) {
DEBUG_INFO("MSG_WAITALL not implemented yet");
errno = EINVAL;
return -1;
}
#if defined(__linux__)
if (flags & MSG_ERRQUEUE) {
DEBUG_INFO("MSG_ERRQUEUE not implemented yet");
errno = EINVAL;
return -1;
}
#endif
if (flags & MSG_PEEK) {
// MSG_PEEK doesn't require any special stack-related machinery so we can just
// pass it to a regular recv() call with no issue
err = recv(fd, buf, len, MSG_PEEK);
}
// restore "per-call" flags
if (flags & MSG_DONTWAIT) {
int saved_flags = fcntl(fd, F_GETFL);
if (fcntl(fd, F_SETFL, saved_flags & ~O_NONBLOCK) < 0) { // mask out the blocking flag
return -1;
}
}
return err;
}
ssize_t virt_recvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen)
{
//DEBUG_TRANS("fd=%d", fd);
int32_t r = 0;
errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
if (len == 0) {
return 0;
}
if (buf == NULL) {
DEBUG_ERROR("buf is null");
errno = EINVAL;
return -1;
}
char udp_msg_buf[ZT_SOCKET_MSG_BUF_SZ];
char *msg_ptr = udp_msg_buf;
memset(msg_ptr, 0, sizeof(int32_t)); // zero only len portion
int32_t udp_msg_len = 0;
// PEEK at the buffer and see if we can read a length, if not, err out
r = recv(fd, msg_ptr, sizeof(int32_t), MSG_PEEK);
if (r != sizeof(int32_t)) {
errno = EIO; // TODO: test for this
return -1;
}
// read of sizeof(int32_t) for the length of the datagram (including address)
r = read(fd, msg_ptr, sizeof(int32_t));
// copy to length variable
memcpy(&udp_msg_len, msg_ptr, sizeof(int32_t));
msg_ptr+=sizeof(int32_t);
if (udp_msg_len <= 0) {
DEBUG_ERROR("invalid datagram");
errno = EIO; // TODO: test for this
return -1;
}
// there is a datagram to read, so let's read it
// zero remainder of buffer
memset(msg_ptr, 0, ZT_SOCKET_MSG_BUF_SZ- sizeof(int32_t));
if ((r = read(fd, msg_ptr, udp_msg_len)) < 0) {
DEBUG_ERROR("invalid datagram");
errno = EIO; // TODO: test for this
return -1;
}
// get address
if (addr) {
if (*addrlen < sizeof(struct sockaddr_storage)) {
DEBUG_ERROR("invalid address length provided");
errno = EINVAL;
return -1;
}
*addrlen = sizeof(struct sockaddr_storage);
memcpy(addr, msg_ptr, *addrlen);
}
msg_ptr+=sizeof(struct sockaddr_storage);
// get payload
int32_t payload_sz = udp_msg_len - *addrlen;
int32_t write_sz = len < payload_sz ? len : payload_sz;
memcpy(buf, msg_ptr, write_sz);
return write_sz;
}
// TODO
ssize_t virt_recvmsg(int fd, struct msghdr *msg,int flags)
{
//DEBUG_TRANS("fd=%d", fd);
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
else {
err = recvmsg(fd, msg, flags);
}
return err;
}
int virt_read(int fd, void *buf, size_t len) {
DEBUG_TRANS("fd=%d", fd);
errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
DEBUG_ERROR("invalid vs for fd=%d", fd);
errno = EBADF;
return -1;
}
return read(fd, buf, len);
}
int virt_write(int fd, const void *buf, size_t len) {
DEBUG_TRANS("fd=%d", fd);
errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
DEBUG_ERROR("invalid vs for fd=%d", fd);
errno = EBADF;
return -1;
}
return write(fd, buf, len);
}
int virt_shutdown(int fd, int how)
{
int err = errno = 0;
if (fd < 0 || fd >= ZT_MAX_SOCKETS) {
errno = EBADF;
return -1;
}
if (how != SHUT_RD && how != SHUT_WR && how != SHUT_RDWR) {
errno = EINVAL;
return -1;
}
VirtualSocket *vs = get_virt_socket(fd);
if (vs == NULL) {
DEBUG_ERROR("invalid vs for fd=%d", fd);
errno = EBADF;
return -1;
}
if (vs->get_state() != VS_STATE_CONNECTED || vs->socket_type != SOCK_STREAM) {
DEBUG_ERROR("the socket is either not in a connected state, or isn't connection-based, fd=%d", fd);
errno = ENOTCONN;
return -1;
}
if (vs->tap) {
err = vs->tap->Shutdown(vs, how);
}
return err;
}
/****************************************************************************/
/* Socket API Helper functions --- DON'T CALL THESE DIRECTLY */
/****************************************************************************/
bool virt_can_provision_new_socket(int socket_type)
{
int can = false;
#if defined(STACK_PICO)
return !(pico_ntimers()+1 > PICO_MAX_TIMERS);
#endif
#if defined(STACK_LWIP)
if (socket_type == SOCK_STREAM) {
return !(rd_lwip_num_current_tcp_pcbs()+1 > MEMP_NUM_TCP_PCB);
}
if (socket_type == SOCK_DGRAM) {
return !(rd_lwip_num_current_udp_pcbs()+1 > MEMP_NUM_UDP_PCB);
}
if (socket_type == SOCK_RAW) {
return !(rd_lwip_num_current_raw_pcbs()+1 > MEMP_NUM_RAW_PCB);
}
can = true;
#endif
#if defined(NO_STACK)
// always true since there's no network stack timer/memory limitation
can = true;
#endif
return can;
}
int virt_num_active_sockets()
{
_multiplexer_lock.lock();
int num = unmap.size() + fdmap.size();
_multiplexer_lock.unlock();
return num;
}
int virt_maxsockets(int socket_type)
{
int max = 0;
#if defined(STACK_PICO)
// TODO: This is only an approximation
// TODO: distinquish by type
max = PICO_MAX_TIMERS - 10;
#endif
#if defined(STACK_LWIP)
if (socket_type == SOCK_STREAM) {
max = MEMP_NUM_TCP_PCB;
}
if (socket_type == SOCK_DGRAM) {
max = MEMP_NUM_UDP_PCB;
}
#endif
#if defined(NO_STACK)
// arbitrary
#if defined(__linux__)
max = RLIMIT_NOFILE;
#endif
#if defined(__APPLE__)
max = 1024;
#endif
#endif
return max;
}
/****************************************************************************/
/* VirtualSocket / VirtualTap helper functions - DON'T CALL THESE DIRECTLY */
/****************************************************************************/
VirtualSocket *get_virt_socket(int fd)
{
DEBUG_EXTRA("fd=%d", fd);
_multiplexer_lock.lock();
// try to locate in unmapped set
VirtualSocket *vs = unmap[fd];
if (vs == NULL) {
// if not, try to find in mapped set (bind to vtap has been performed)
std::pair<VirtualSocket*, VirtualTap*> *p = fdmap[fd];
if (p) {
vs = p->first;
}
else {
DEBUG_ERROR("unable to locate virtual socket");
}
}
_multiplexer_lock.unlock();
return vs;
}
int del_virt_socket(int fd)
{
DEBUG_EXTRA("fd=%d", fd);
int err = 0;
_multiplexer_lock.lock();
try {
std::map<int, VirtualSocket*>::iterator un_iter = unmap.find(fd);
if (un_iter != unmap.end()) {
unmap.erase(un_iter);
}
std::map<int, std::pair<VirtualSocket*,VirtualTap*>*>::iterator fd_iter = fdmap.find(fd);
if (fd_iter != fdmap.end()) {
fdmap.erase(fd_iter);
}
}
catch( ... ) {
DEBUG_ERROR("unable to remove virtual socket");
err = -1;
}
_multiplexer_lock.unlock();
return err;
}
int add_unassigned_virt_socket(int fd, VirtualSocket *vs)
{
DEBUG_EXTRA("fd=%d, vs=%p", fd, vs);
int err = 0;
_multiplexer_lock.lock();
try {
std::map<int, VirtualSocket*>::iterator un_iter = unmap.find(fd);
if (un_iter == unmap.end()) {
unmap[fd] = vs;
}
else {
DEBUG_ERROR("fd=%d already contained in <fd:vs> map", fd);
}
}
catch( ... ) {
DEBUG_ERROR("unable to add virtual socket");
err = -1;
}
_multiplexer_lock.unlock();
return err;
}
int del_unassigned_virt_socket(int fd)
{
DEBUG_EXTRA("fd=%d", fd);
int err = 0;
_multiplexer_lock.lock();
try {
std::map<int, VirtualSocket*>::iterator un_iter = unmap.find(fd);
if (un_iter != unmap.end()) {
unmap.erase(un_iter);
}
}
catch( ... ) {
DEBUG_ERROR("unable to remove virtual socket");
err = -1;
}
_multiplexer_lock.unlock();
return err;
}
int add_assigned_virt_socket(void *tap_ptr, VirtualSocket *vs, int fd)
{
VirtualTap *tap = (VirtualTap*)tap_ptr;
DEBUG_EXTRA("tap=%p, vs=%p, fd=%d", tap, vs, fd);
int err = 0;
_multiplexer_lock.lock();
try {
std::map<int, std::pair<VirtualSocket*,VirtualTap*>*>::iterator fd_iter;
fd_iter = fdmap.find(fd);
if (fd_iter == fdmap.end()) {
fdmap[fd] = new std::pair<VirtualSocket*,VirtualTap*>(vs, tap);
}
else {
DEBUG_ERROR("fd=%d already contained in <fd,<vs,vt>> map", fd);
}
}
catch( ... ) {
DEBUG_ERROR("unable to add virtual socket");
err = -1;
}
_multiplexer_lock.unlock();
return err;
}
int del_assigned_virt_socket(VirtualTap *tap, VirtualSocket *vs, int fd)
{
DEBUG_EXTRA("tap=%p, vs=%p, fd=%d", tap, vs, fd);
int err = 0;
_multiplexer_lock.lock();
try {
std::map<int, std::pair<VirtualSocket*,VirtualTap*>*>::iterator fd_iter;
fd_iter = fdmap.find(fd);
if (fd_iter != fdmap.end()) {
fdmap.erase(fd_iter);
}
}
catch( ... ) {
DEBUG_ERROR("unable to remove virtual socket");
err = -1;
}
_multiplexer_lock.unlock();
return err;
}
/*
std::pair<VirtualSocket*, VirtualTap*> *get_assigned_virtual_pair(int fd)
{
_multiplexer_lock.lock();
std::pair<VirtualSocket*, VirtualTap*> *p = fdmap[fd];
_multiplexer_lock.unlock();
return p;
}
*/
void disableTaps()
{
_vtaps_lock.lock();
for (int i=0; i<vtaps.size(); i++) {
DEBUG_EXTRA("vt=%p", vtaps[i]);
((VirtualTap*)vtaps[i])->_enabled = false;
}
_vtaps_lock.unlock();
}
void sockaddr2inet(int socket_family, const struct sockaddr *addr, ZeroTier::InetAddress *inet)
{
char ipstr[INET6_ADDRSTRLEN];
memset(ipstr, 0, INET6_ADDRSTRLEN);
if (socket_family == AF_INET) {
inet_ntop(AF_INET,
(const void *)&((struct sockaddr_in *)addr)->sin_addr.s_addr, ipstr, INET_ADDRSTRLEN);
inet->fromString(ipstr);
}
if (socket_family == AF_INET6) {
inet_ntop(AF_INET6,
(const void *)&((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, ipstr, INET6_ADDRSTRLEN);
char addrstr[64];
sprintf(addrstr, "%s", ipstr);
inet->fromString(addrstr);
}
}
#endif // ZT_VIRTUAL_SOCKET