improved testing and connection handling

This commit is contained in:
Joseph Henry
2017-04-20 13:39:46 -07:00
parent c65b609fb4
commit 76abb3e0a2
30 changed files with 2430 additions and 775 deletions

View File

@@ -56,6 +56,7 @@ namespace ZeroTier {
std::queue<Connection*> _AcceptedConnections;
SocketTap *tap; // Reference to SocketTap
int state; // See ZeroTierSDK.h for (ZT_SOCK_STATE_*)
Connection() {
ZT_PHY_SOCKFD_TYPE fdpair[2];
@@ -69,5 +70,15 @@ namespace ZeroTier {
app_fd = fdpair[1];
}
};
/*
* A helper object for passing SocketTaps and Connections through the stack
*/
struct ConnectionPair
{
SocketTap *tap;
Connection *conn;
ConnectionPair(SocketTap *_tap, Connection *conn) : tap(_tap), conn(conn) {}
};
}
#endif

View File

@@ -37,302 +37,223 @@
namespace ZeroTier {
// Ignore these
void SocketTap::phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *local_address,
const struct sockaddr *from,void *data,unsigned long len) {}
void SocketTap::phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) {}
void SocketTap::phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,
const struct sockaddr *from) {}
void SocketTap::phyOnTcpClose(PhySocket *sock,void **uptr) {}
void SocketTap::phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) {}
void SocketTap::phyOnTcpWritable(PhySocket *sock,void **uptr, bool stack_invoked) {}
/****************************************************************************/
/* SocketTap Service */
/* - For each joined network a SocketTap will be created to administer I/O */
/* calls to the stack and the ZT virtual wire */
/****************************************************************************/
/****************************************************************************/
/* SocketTap Service */
/* - For each joined network a SocketTap will be created to administer I/O */
/* calls to the stack and the ZT virtual wire */
/****************************************************************************/
SocketTap::SocketTap(
const char *homePath,
const MAC &mac,
unsigned int mtu,
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
void (*handler)(void *,void*,uint64_t,const MAC &,const MAC &,
unsigned int,unsigned int,const void *,unsigned int),
void *arg) :
_homePath(homePath),
_mac(mac),
_mtu(mtu),
_nwid(nwid),
_handler(handler),
_arg(arg),
_phy(this,false,true),
_unixListenSocket((PhySocket *)0),
_enabled(true),
_run(true)
{
/*
char sockPath[4096];
Utils::snprintf(sockPath,sizeof(sockPath),"%s%s" ZT_SDK_RPC_DIR_PREFIX "/%.16llx",
homePath,ZT_PATH_SEPARATOR_S,_nwid,ZT_PATH_SEPARATOR_S,(unsigned long long)nwid);
_dev = sockPath;
_unixListenSocket = _phy.unixListen(sockPath,(void *)this);
chmod(sockPath, 0777); // make the RPC socket available to all users
if (!_unixListenSocket)
DEBUG_ERROR("unable to bind to: rpc = %s", sockPath);
else
DEBUG_INFO("rpc = %s", sockPath);
*/
_thread = Thread::start(this);
}
SocketTap::~SocketTap()
{
// TODO: Verify deletion of all objects
_run = false;
_phy.whack();
Thread::join(_thread);
_phy.close(_unixListenSocket,false);
}
void SocketTap::setEnabled(bool en)
{
_enabled = en;
}
bool SocketTap::enabled() const
{
return _enabled;
}
bool SocketTap::addIp(const InetAddress &ip)
{
DEBUG_INFO();
picotap = this;
picostack->pico_init_interface(this, ip);
_ips.push_back(ip);
return true;
}
bool SocketTap::removeIp(const InetAddress &ip)
{
DEBUG_INFO();
Mutex::Lock _l(_ips_m);
std::vector<InetAddress>::iterator i(std::find(_ips.begin(),_ips.end(),ip));
if (i == _ips.end())
return false;
_ips.erase(i);
if (ip.isV4()) {
// FIXME: De-register from network stacks
SocketTap::SocketTap(
const char *homePath,
const MAC &mac,
unsigned int mtu,
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
void (*handler)(void *,void*,uint64_t,const MAC &,const MAC &,
unsigned int,unsigned int,const void *,unsigned int),
void *arg) :
_handler(handler),
_homePath(homePath),
_arg(arg),
_enabled(true),
_run(true),
_mac(mac),
_mtu(mtu),
_nwid(nwid),
_unixListenSocket((PhySocket *)0),
_phy(this,false,true)
{
_thread = Thread::start(this);
}
return true;
}
std::vector<InetAddress> SocketTap::ips() const
{
Mutex::Lock _l(_ips_m);
return _ips;
}
void SocketTap::put(const MAC &from,const MAC &to,unsigned int etherType,
const void *data,unsigned int len)
{
DEBUG_INFO();
// RX packet
picostack->pico_rx(this, from,to,etherType,data,len);
}
std::string SocketTap::deviceName() const
{
return _dev;
}
void SocketTap::setFriendlyName(const char *friendlyName) {
DEBUG_INFO();
}
void SocketTap::scanMulticastGroups(std::vector<MulticastGroup> &added,
std::vector<MulticastGroup> &removed)
{
std::vector<MulticastGroup> newGroups;
Mutex::Lock _l(_multicastGroups_m);
// TODO: get multicast subscriptions from network stack
std::vector<InetAddress> allIps(ips());
for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip));
std::sort(newGroups.begin(),newGroups.end());
std::unique(newGroups.begin(),newGroups.end());
for(std::vector<MulticastGroup>::iterator m(newGroups.begin());m!=newGroups.end();++m) {
if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m))
added.push_back(*m);
SocketTap::~SocketTap()
{
// TODO: Verify deletion of all objects
_run = false;
_phy.whack();
Thread::join(_thread);
_phy.close(_unixListenSocket,false);
for(int i=0; i<_Connections.size(); i++) delete _Connections[i];
}
for(std::vector<MulticastGroup>::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) {
if (!std::binary_search(newGroups.begin(),newGroups.end(),*m))
removed.push_back(*m);
}
_multicastGroups.swap(newGroups);
}
void SocketTap::threadMain()
throw()
{
picostack->pico_loop(this);
}
Connection *SocketTap::getConnection(PhySocket *sock)
{
DEBUG_INFO();
for(size_t i=0;i<_Connections.size();++i) {
if(_Connections[i]->sock == sock)
return _Connections[i];
void SocketTap::setEnabled(bool en)
{
_enabled = en;
}
return NULL;
}
Connection *SocketTap::getConnection(struct pico_socket *sock)
{
DEBUG_INFO();
for(size_t i=0;i<_Connections.size();++i) {
if(_Connections[i]->picosock == sock)
return _Connections[i];
bool SocketTap::enabled() const
{
return _enabled;
}
return NULL;
}
void SocketTap::closeConnection(PhySocket *sock)
{
DEBUG_INFO();
Mutex::Lock _l(_close_m);
// Here we assume _tcpconns_m is already locked by caller
if(!sock) {
DEBUG_EXTRA("invalid PhySocket");
return;
bool SocketTap::addIp(const InetAddress &ip)
{
DEBUG_INFO();
picostack->pico_init_interface(this, ip);
_ips.push_back(ip);
return true;
}
picostack->pico_handleClose(sock);
Connection *conn = getConnection(sock);
if(!conn)
return;
for(size_t i=0;i<_Connections.size();++i) {
if(_Connections[i] == conn){
_Connections.erase(_Connections.begin() + i);
delete conn;
break;
bool SocketTap::removeIp(const InetAddress &ip)
{
DEBUG_INFO();
Mutex::Lock _l(_ips_m);
std::vector<InetAddress>::iterator i(std::find(_ips.begin(),_ips.end(),ip));
if (i == _ips.end())
return false;
_ips.erase(i);
if (ip.isV4()) {
// FIXME: De-register from network stacks
}
if (ip.isV6()) {
// FIXME: De-register from network stacks
}
return true;
}
if(!sock)
return;
close(_phy.getDescriptor(sock));
_phy.close(sock, false);
}
void SocketTap::phyOnUnixClose(PhySocket *sock,void **uptr) {
DEBUG_INFO();
//Mutex::Lock _l(_tcpconns_m);
//closeConnection(sock);
// FIXME:
}
void SocketTap::handleRead(PhySocket *sock,void **uptr,bool stack_invoked)
{
DEBUG_INFO();
picostack->pico_handleRead(sock, uptr, stack_invoked);
}
void SocketTap::phyOnUnixWritable(PhySocket *sock,void **uptr,bool stack_invoked)
{
DEBUG_INFO();
handleRead(sock,uptr,stack_invoked);
}
void SocketTap::phyOnUnixData(PhySocket *sock, void **uptr, void *data, ssize_t len)
{
DEBUG_INFO();
Connection *conn = getConnection(sock);
if(!conn)
return;
if(len) {
conn->txsz += len;
handleWrite(conn);
}
return;
}
/****************************************************************************/
/* SDK Socket API */
/****************************************************************************/
int SocketTap::Connect(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen) {
Mutex::Lock _l(_tcpconns_m);
return picostack->pico_Connect(conn, fd, addr, addrlen);
}
int SocketTap::Bind(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen) {
Mutex::Lock _l(_tcpconns_m);
return picostack->pico_Bind(conn, fd, addr, addrlen);
}
void SocketTap::Listen(Connection *conn, int fd, int backlog) {
Mutex::Lock _l(_tcpconns_m);
picostack->pico_Listen(conn, fd, backlog);
}
int SocketTap::Accept(Connection *conn) {
Mutex::Lock _l(_tcpconns_m);
return picostack->pico_Accept(conn);
}
/*------------------------------------------------------------------------------
----------------------------- RPC Handler functions ----------------------------
------------------------------------------------------------------------------*/
void SocketTap::handleGetsockname(PhySocket *sock, PhySocket *rpcSock,
void **uptr, struct getsockname_st *getsockname_rpc)
{
DEBUG_INFO();
Mutex::Lock _l(_tcpconns_m);
Connection *conn = getConnection(sock);
if(conn->local_addr == NULL){
DEBUG_EXTRA("no address info available. is it bound?");
struct sockaddr_storage storage;
memset(&storage, 0, sizeof(struct sockaddr_storage));
write(_phy.getDescriptor(rpcSock), NULL, sizeof(struct sockaddr_storage));
return;
std::vector<InetAddress> SocketTap::ips() const
{
Mutex::Lock _l(_ips_m);
return _ips;
}
write(_phy.getDescriptor(rpcSock), conn->local_addr, sizeof(struct sockaddr_storage));
}
void SocketTap::handleGetpeername(PhySocket *sock, PhySocket *rpcSock,
void **uptr, struct getsockname_st *getsockname_rpc)
{
DEBUG_INFO();
Mutex::Lock _l(_tcpconns_m);
Connection *conn = getConnection(sock);
if(conn->peer_addr == NULL){
DEBUG_EXTRA("no peer address info available. is it connected?");
struct sockaddr_storage storage;
memset(&storage, 0, sizeof(struct sockaddr_storage));
write(_phy.getDescriptor(rpcSock), NULL, sizeof(struct sockaddr_storage));
return;
void SocketTap::put(const MAC &from,const MAC &to,unsigned int etherType,
const void *data,unsigned int len)
{
// RX packet
picostack->pico_rx(this, from,to,etherType,data,len);
}
write(_phy.getDescriptor(rpcSock), conn->peer_addr, sizeof(struct sockaddr_storage));
}
// Write to the network stack (and thus out onto the network)
void SocketTap::handleWrite(Connection *conn)
{
DEBUG_INFO();
picostack->pico_handleWrite(conn);
}
std::string SocketTap::deviceName() const
{
return _dev;
}
void SocketTap::setFriendlyName(const char *friendlyName) {
DEBUG_INFO();
}
void SocketTap::scanMulticastGroups(std::vector<MulticastGroup> &added,
std::vector<MulticastGroup> &removed)
{
std::vector<MulticastGroup> newGroups;
Mutex::Lock _l(_multicastGroups_m);
// TODO: get multicast subscriptions from network stack
std::vector<InetAddress> allIps(ips());
for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip));
std::sort(newGroups.begin(),newGroups.end());
std::unique(newGroups.begin(),newGroups.end());
for(std::vector<MulticastGroup>::iterator m(newGroups.begin());m!=newGroups.end();++m) {
if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m))
added.push_back(*m);
}
for(std::vector<MulticastGroup>::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) {
if (!std::binary_search(newGroups.begin(),newGroups.end(),*m))
removed.push_back(*m);
}
_multicastGroups.swap(newGroups);
}
void SocketTap::threadMain()
throw()
{
picostack->pico_loop(this);
}
void SocketTap::phyOnUnixClose(PhySocket *sock,void **uptr)
{
Close((Connection*)uptr);
}
void SocketTap::phyOnUnixData(PhySocket *sock, void **uptr, void *data, ssize_t len)
{
Connection *conn = (Connection*)*uptr;
if(!conn)
return;
if(len) {
unsigned char *buf = (unsigned char*)data;
memcpy(conn->txbuf + conn->txsz, buf, len);
conn->txsz += len;
Write(conn);
}
return;
}
void SocketTap::phyOnUnixWritable(PhySocket *sock,void **uptr,bool stack_invoked)
{
Read(sock,uptr,stack_invoked);
}
/****************************************************************************/
/* SDK Socket API */
/****************************************************************************/
int SocketTap::Connect(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen) {
Mutex::Lock _l(_tcpconns_m);
return picostack->pico_Connect(conn, fd, addr, addrlen);
}
int SocketTap::Bind(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen) {
Mutex::Lock _l(_tcpconns_m);
return picostack->pico_Bind(conn, fd, addr, addrlen);
}
void SocketTap::Listen(Connection *conn, int fd, int backlog) {
Mutex::Lock _l(_tcpconns_m);
picostack->pico_Listen(conn, fd, backlog);
}
int SocketTap::Accept(Connection *conn) {
Mutex::Lock _l(_tcpconns_m);
return picostack->pico_Accept(conn);
}
void SocketTap::Read(PhySocket *sock,void **uptr,bool stack_invoked) {
picostack->pico_Read(this, sock, uptr, stack_invoked);
}
void SocketTap::Write(Connection *conn) {
picostack->pico_Write(conn);
}
void SocketTap::Close(Connection *conn) {
Mutex::Lock _l(_close_m);
// Here we assume _tcpconns_m is already locked by caller
if(!conn->sock) {
DEBUG_EXTRA("invalid PhySocket");
return;
}
if(!conn)
return;
for(size_t i=0;i<_Connections.size();++i) {
if(_Connections[i] == conn){
_Connections.erase(_Connections.begin() + i);
delete conn;
break;
}
}
if(!conn->sock)
return;
close(_phy.getDescriptor(conn->sock));
_phy.close(conn->sock, false);
}
/****************************************************************************/
/* Not used in this implementation */
/****************************************************************************/
void SocketTap::phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *local_address,
const struct sockaddr *from,void *data,unsigned long len) {}
void SocketTap::phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) {}
void SocketTap::phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,
const struct sockaddr *from) {}
void SocketTap::phyOnTcpClose(PhySocket *sock,void **uptr) {}
void SocketTap::phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) {}
void SocketTap::phyOnTcpWritable(PhySocket *sock,void **uptr, bool stack_invoked) {}
} // namespace ZeroTier

View File

@@ -45,34 +45,11 @@
#include "pico_icmp4.h"
#include "pico_dev_tap.h"
#include "pico_protocol.h"
#include "pico_socket.h"
#include "pico_device.h"
#include "pico_ipv6.h"
// ZT RPC structs
struct socket_st;
struct listen_st;
struct bind_st;
struct connect_st;
struct getsockname_st;
struct accept_st;
struct pico_socket;
namespace ZeroTier {
extern SocketTap *picotap;
/*
* A helper for passing a reference to _phy to stackrpc callbacks as a "state"
*/
struct Larg
{
SocketTap *tap;
Connection *conn;
Larg(SocketTap *_tap, Connection *conn) : tap(_tap), conn(conn) {}
};
/*
* Socket Tap -- emulates an Ethernet tap device
*/
@@ -97,19 +74,17 @@ namespace ZeroTier {
bool enabled() const;
/*
*
* Adds an address to the userspace stack interface associated with this SocketTap
*/
bool addIp(const InetAddress &ip);
/*
*
* Removes an address from the userspace stack interface associated with this SocketTap
*/
bool removeIp(const InetAddress &ip);
std::vector<InetAddress> ips() const;
std::vector<InetAddress> _ips;
/*
*
* Presents data to the userspace stack
*/
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,
unsigned int len);
@@ -135,73 +110,12 @@ namespace ZeroTier {
void threadMain()
throw();
std::string _homePath;
MAC _mac;
unsigned int _mtu;
uint64_t _nwid;
/*
*
* For moving data onto the ZeroTier virtual wire
*/
void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,
const void *,unsigned int);
void *_arg;
Phy<SocketTap *> _phy;
PhySocket *_unixListenSocket;
volatile bool _enabled;
volatile bool _run;
// picoTCP
unsigned char pico_frame_rxbuf[MAX_PICO_FRAME_RX_BUF_SZ];
int pico_frame_rxbuf_tot;
Mutex _pico_frame_rxbuf_m;
/****************************************************************************/
/* In these, we will call the stack's corresponding functions, this is *
* where one would put logic to select between different stacks */
/****************************************************************************/
int Connect(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen);
int Bind(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen);
void Listen(Connection *conn, int fd, int backlog);
int Accept(Connection *conn);
/*
* Return the address that the socket is bound to
*/
void handleGetsockname(PhySocket *sock, PhySocket *rpcsock, void **uptr, struct getsockname_st *getsockname_rpc);
/*
* Return the address of the peer connected to this socket
*/
void handleGetpeername(PhySocket *sock, PhySocket *rpcsock, void **uptr, struct getsockname_st *getsockname_rpc);
/*
* Writes data from the application's socket to the LWIP connection
*/
void handleWrite(Connection *conn);
// Unused -- no UDP or TCP from this thread/Phy<>
void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *local_address,
const struct sockaddr *from,void *data,unsigned long len);
void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success);
void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,
const struct sockaddr *from);
void phyOnTcpClose(PhySocket *sock,void **uptr);
void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len);
void phyOnTcpWritable(PhySocket *sock,void **uptr, bool stack_invoked);
/*
*
*/
void handleRead(PhySocket *sock,void **uptr,bool stack_invoked);
/*
* Signals us to close the TcpConnection associated with this PhySocket
*/
@@ -217,21 +131,22 @@ namespace ZeroTier {
*/
void phyOnUnixWritable(PhySocket *sock,void **uptr,bool lwip_invoked);
/*
* Returns a pointer to a TcpConnection associated with a given PhySocket
*/
Connection *getConnection(PhySocket *sock);
/****************************************************************************/
/* Vars */
/****************************************************************************/
/*
* Returns a pointer to a TcpConnection associated with a given pico_socket
*/
Connection *getConnection(struct pico_socket *socket);
std::vector<InetAddress> ips() const;
std::vector<InetAddress> _ips;
/*
* Closes a TcpConnection, associated connection strcuture,
* PhySocket, and underlying file descriptor
*/
void closeConnection(PhySocket *sock);
std::string _homePath;
void *_arg;
volatile bool _enabled;
volatile bool _run;
MAC _mac;
unsigned int _mtu;
uint64_t _nwid;
PhySocket *_unixListenSocket;
Phy<SocketTap *> _phy;
std::vector<Connection*> _Connections;
@@ -241,7 +156,86 @@ namespace ZeroTier {
std::vector<MulticastGroup> _multicastGroups;
Mutex _multicastGroups_m;
Mutex _ips_m, _tcpconns_m, _rx_buf_m, _close_m;
/****************************************************************************/
/* Guarded RX Frame Buffer for picoTCP */
/****************************************************************************/
unsigned char pico_frame_rxbuf[MAX_PICO_FRAME_RX_BUF_SZ];
int pico_frame_rxbuf_tot;
Mutex _pico_frame_rxbuf_m;
/****************************************************************************/
/* In these, we will call the stack's corresponding functions, this is */
/* where one would put logic to select between different stacks */
/****************************************************************************/
/*
* Connect to a remote host via the userspace stack interface associated with this SocketTap
*/
int Connect(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen);
/*
* Bind to the userspace stack interface associated with this SocketTap
*/
int Bind(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen);
/*
* Listen for a Connection
*/
void Listen(Connection *conn, int fd, int backlog);
/*
* Accepts an incoming Connection
*/
int Accept(Connection *conn);
/*
* Move data from RX buffer to application's "socket"
*/
void Read(PhySocket *sock,void **uptr,bool stack_invoked);
/*
* Move data from application's "socket" into network stack
*/
void Write(Connection *conn);
/*
* Closes a Connection
*/
void Close(Connection *conn);
/*
* Return the address that the socket is bound to
*/
void handleGetsockname(PhySocket *sock, PhySocket *rpcsock, void **uptr, struct getsockname_st *getsockname_rpc);
/*
* Return the address of the peer connected to this socket
*/
void handleGetpeername(PhySocket *sock, PhySocket *rpcsock, void **uptr, struct getsockname_st *getsockname_rpc);
/****************************************************************************/
/* Not used in this implementation */
/****************************************************************************/
void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *local_address,
const struct sockaddr *from,void *data,unsigned long len);
void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success);
void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,
const struct sockaddr *from);
void phyOnTcpClose(PhySocket *sock,void **uptr);
void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len);
void phyOnTcpWritable(PhySocket *sock,void **uptr, bool stack_invoked);
};
} // namespace ZeroTier
#endif

84
src/Utils.cpp Normal file
View File

@@ -0,0 +1,84 @@
/*
* ZeroTier SDK - Network Virtualization Everywhere
* Copyright (C) 2011-2016 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/>.
*/
#include <stdio.h>
#include <execinfo.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
void zt_dump_stacktrace(int sig) {
void *array[16];
size_t size;
size = backtrace(array, 16);
fprintf(stderr, "Error: signal %d:\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
exit(1);
}
/*
char *beautify_pico_error(int err)
{
switch(err){
PICO_ERR_NOERR = 0,
PICO_ERR_EPERM = 1,
PICO_ERR_ENOENT = 2,
PICO_ERR_EINTR = 4,
PICO_ERR_EIO = 5,
PICO_ERR_ENXIO = 6,
PICO_ERR_EAGAIN = 11,
PICO_ERR_ENOMEM = 12,
PICO_ERR_EACCESS = 13,
PICO_ERR_EFAULT = 14,
PICO_ERR_EBUSY = 16,
PICO_ERR_EEXIST = 17,
PICO_ERR_EINVAL = 22,
PICO_ERR_ENONET = 64,
PICO_ERR_EPROTO = 71,
PICO_ERR_ENOPROTOOPT = 92,
PICO_ERR_EPROTONOSUPPORT = 93,
PICO_ERR_EOPNOTSUPP = 95,
PICO_ERR_EADDRINUSE = 98,
PICO_ERR_EADDRNOTAVAIL = 99,
PICO_ERR_ENETDOWN = 100,
PICO_ERR_ENETUNREACH = 101,
PICO_ERR_ECONNRESET = 104,
PICO_ERR_EISCONN = 106,
PICO_ERR_ENOTCONN = 107,
PICO_ERR_ESHUTDOWN = 108,
PICO_ERR_ETIMEDOUT = 110,
PICO_ERR_ECONNREFUSED = 111,
PICO_ERR_EHOSTDOWN = 112,
PICO_ERR_EHOSTUNREACH = 113,
}
return err_text;
}
*/

27
src/Utils.hpp Normal file
View File

@@ -0,0 +1,27 @@
/*
* ZeroTier SDK - Network Virtualization Everywhere
* Copyright (C) 2011-2016 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/>.
*/
#ifndef SDK_UTILS_HPP
#define SDK_UTILS_HPP
/*
* Print a stacktrace
*/
void zt_dump_stacktrace(int sig);
#endif

View File

@@ -48,11 +48,24 @@ namespace ZeroTier {
std::string homeDir; // The resultant platform-specific dir we *must* use internally
std::string netDir; // Where network .conf files are to be written
/*
* Global reference to stack
*/
picoTCP *picostack = NULL;
/*
* "sockets" that have been created but not bound to a SocketTap interface yet
*/
std::map<int, Connection*> UnassignedConnections;
std::map<int, std::pair<Connection*,SocketTap*>*> AssignedFileDescriptors;
Mutex _multiplexer_lock;
Mutex _accepted_connection_lock;
// FIXME: make sure these are properly deleted
/*
* For fast lookup of Connections and SocketTaps via given file descriptor
*/
std::map<int, std::pair<Connection*,SocketTap*>*> AssignedConnections;
ZeroTier::Mutex _multiplexer_lock;
ZeroTier::Mutex _accepted_connection_lock;
}
/****************************************************************************/
@@ -260,12 +273,12 @@ int zts_get_peer_address(char *peer, const char *devID) {
void zts_enable_http_control_plane()
{
// TODO
}
void zts_disable_http_control_plane()
{
// TODO
}
/****************************************************************************/
@@ -278,6 +291,7 @@ void zts_disable_http_control_plane()
/****************************************************************************/
int zts_socket(ZT_SOCKET_SIG) {
DEBUG_ERROR("UnassConn=%d, AssigFDs=%d", ZeroTier::UnassignedConnections.size(), ZeroTier::AssignedConnections.size());
DEBUG_INFO();
ZeroTier::_multiplexer_lock.lock();
ZeroTier::Connection *conn = new ZeroTier::Connection();
@@ -316,7 +330,7 @@ int zts_socket(ZT_SOCKET_SIG) {
int zts_connect(ZT_CONNECT_SIG) {
if(!zt1Service) {
DEBUG_ERROR("zt1Service = NULL");
DEBUG_ERROR("Service not started. Call zts_start(path) first");
// errno = ?
return -1;
}
@@ -341,12 +355,9 @@ int zts_connect(ZT_CONNECT_SIG) {
(const void *)&((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, ipstr, INET6_ADDRSTRLEN);
}
iaddr.fromString(ipstr);
DEBUG_INFO("ipstr= %s", ipstr);
DEBUG_INFO("iaddr= %s", iaddr.toString().c_str());
tap = zt1Service->getTap(iaddr);
if(!tap) {
// TODO: More canonical error?
DEBUG_ERROR("no route to host");
@@ -354,13 +365,19 @@ int zts_connect(ZT_CONNECT_SIG) {
err = -1;
}
else {
conn->sock = tap->_phy.wrapSocket(conn->sdk_fd, conn); // wrap the socketpair we created earlier
conn->picosock->priv = new ZeroTier::Larg(tap, conn); // pointer to tap we use in callbacks from the stack
// pointer to tap we use in callbacks from the stack
conn->picosock->priv = new ZeroTier::ConnectionPair(tap, conn);
DEBUG_INFO("found appropriate SocketTap");
// TODO: Perhaps move this connect call outside of the lock
tap->_Connections.push_back(conn); // Give this Connection to the tap we decided on
err = tap->Connect(conn, fd, addr, addrlen); // Semantically: tap->stack->connect
conn->tap = tap;
// Semantically: tap->stack->connect
err = tap->Connect(conn, fd, addr, addrlen);
if(err == 0) {
tap->_Connections.push_back(conn); // Give this Connection to the tap we decided on
conn->tap = tap;
}
// Wrap the socketpair we created earlier
// For I/O loop participation and referencing the PhySocket's parent Connection in callbacks
conn->sock = tap->_phy.wrapSocket(conn->sdk_fd, conn);
}
}
else {
@@ -368,14 +385,50 @@ int zts_connect(ZT_CONNECT_SIG) {
// errno = ?
err = -1;
}
ZeroTier::AssignedFileDescriptors[fd] = new std::pair<ZeroTier::Connection*,ZeroTier::SocketTap*>(conn, tap);
ZeroTier::UnassignedConnections.erase(fd);
ZeroTier::AssignedConnections[fd] = new std::pair<ZeroTier::Connection*,ZeroTier::SocketTap*>(conn, tap);
ZeroTier::_multiplexer_lock.unlock();
// 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 Connection in a ZT_SOCK_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
// non-blocking
if(err == 0 && false)
{
errno = EINPROGRESS;
return -1;
}
// blocking
if(err == 0 && true) {
while(true)
{
usleep(ZT_CONNECT_RECHECK_DELAY * 1000);
tap->_tcpconns_m.lock();
for(int i=0; i<tap->_Connections.size(); i++)
{
if(tap->_Connections[i]->state == PICO_ERR_ECONNRESET) {
errno = ECONNRESET;
return -1;
}
if(tap->_Connections[i]->state == ZT_SOCK_STATE_UNHANDLED_CONNECTED) {
tap->_Connections[i]->state = ZT_SOCK_STATE_CONNECTED;
errno = 0;
return 0; // complete
}
}
tap->_tcpconns_m.unlock();
}
}
return err;
}
int zts_bind(ZT_BIND_SIG) {
if(!zt1Service) {
DEBUG_ERROR("zt1Service = NULL");
DEBUG_ERROR("Service not started. Call zts_start(path) first");
// errno = ?
return -1;
}
@@ -415,7 +468,7 @@ int zts_bind(ZT_BIND_SIG) {
else {
DEBUG_INFO("found appropriate SocketTap");
DEBUG_INFO("conn->picosock = %p", conn->picosock);
conn->picosock->priv = new ZeroTier::Larg(tap, conn);
conn->picosock->priv = new ZeroTier::ConnectionPair(tap, conn);
// TODO: Perhaps move this connect call outside of the lock
tap->_Connections.push_back(conn); // Give this Connection to the tap we decided on
err = tap->Bind(conn, fd, addr, addrlen); // Semantically: tap->stack->connect
@@ -427,37 +480,43 @@ int zts_bind(ZT_BIND_SIG) {
// errno = ?
err = -1;
}
ZeroTier::AssignedFileDescriptors[fd] = new std::pair<ZeroTier::Connection*,ZeroTier::SocketTap*>(conn, tap);
ZeroTier::AssignedConnections[fd] = new std::pair<ZeroTier::Connection*,ZeroTier::SocketTap*>(conn, tap);
ZeroTier::_multiplexer_lock.unlock();
return err;
}
int zts_listen(ZT_LISTEN_SIG) {
if(!zt1Service) {
DEBUG_ERROR("zt1Service = NULL");
DEBUG_ERROR("Service not started. Call zts_start(path) first");
// errno = ?
return -1;
}
int err;
ZeroTier::_multiplexer_lock.lock();
ZeroTier::Connection *conn = ZeroTier::AssignedFileDescriptors[fd]->first;
ZeroTier::SocketTap *tap = ZeroTier::AssignedFileDescriptors[fd]->second;
std::pair<ZeroTier::Connection*, ZeroTier::SocketTap*> *p = ZeroTier::AssignedConnections[fd];
if(!p) {
DEBUG_ERROR("unable to locate connection pair (did you zbind()?");
return -1;
}
ZeroTier::Connection *conn = p->first;
ZeroTier::SocketTap *tap = p->second;
if(!tap || !conn) {
DEBUG_ERROR("unable to locate tap interface for file descriptor");
err = -1;
return -1;
}
tap->Listen(conn, fd, backlog);
err = 0;
DEBUG_INFO("put %p into LISTENING state", conn);
DEBUG_INFO("put conn=%p into LISTENING state", conn);
ZeroTier::_multiplexer_lock.unlock();
return err;
}
int zts_accept(ZT_ACCEPT_SIG) {
int err;
ZeroTier::Connection *conn = ZeroTier::AssignedFileDescriptors[fd]->first;
ZeroTier::SocketTap *tap = ZeroTier::AssignedFileDescriptors[fd]->second;
ZeroTier::Connection *conn = ZeroTier::AssignedConnections[fd]->first;
ZeroTier::SocketTap *tap = ZeroTier::AssignedConnections[fd]->second;
// BLOCKING: loop and keep checking until we find a newly accepted connection
if(true) {
while(true) {
@@ -476,57 +535,75 @@ int zts_accept(ZT_ACCEPT_SIG) {
#if defined(__linux__)
int zts_accept4(ZT_ACCEPT4_SIG)
{
// TODO
return 0;
}
#endif
int zts_setsockopt(ZT_SETSOCKOPT_SIG)
{
// TODO
return 0;
}
int zts_getsockopt(ZT_GETSOCKOPT_SIG)
{
// TODO
return 0;
}
int zts_getsockname(ZT_GETSOCKNAME_SIG)
{
// TODO
return 0;
}
int zts_getpeername(ZT_GETPEERNAME_SIG)
{
// TODO
return 0;
}
int zts_close(ZT_CLOSE_SIG)
{
ZeroTier::_multiplexer_lock.lock();
ZeroTier::Connection *conn = ZeroTier::AssignedConnections[fd]->first;
ZeroTier::SocketTap *tap = ZeroTier::AssignedConnections[fd]->second;
// Tell the tap to stop monitoring this PhySocket
tap->Close(conn);
// Tell the userspace stack to close this pico_socket
ZeroTier::picostack->pico_Close(conn);
ZeroTier::_multiplexer_lock.unlock();
return 0;
}
int zts_fcntl(ZT_FCNTL_SIG)
{
// TODO
return 0;
}
ssize_t zts_sendto(ZT_SENDTO_SIG)
{
// TODO
return 0;
}
ssize_t zts_sendmsg(ZT_SENDMSG_SIG)
{
// TODO
return 0;
}
ssize_t zts_recvfrom(ZT_RECVFROM_SIG)
{
// TODO
return 0;
}
ssize_t zts_recvmsg(ZT_RECVMSG_SIG)
{
// TODO
return 0;
}
@@ -666,7 +743,7 @@ void *_start_service(void *thread_id) {
// Generate random port for new service instance
unsigned int randp = 0;
ZeroTier::Utils::getSecureRandom(&randp,sizeof(randp));
int servicePort = 9000 + (randp % 10000);
int servicePort = 9000 + (randp % 1000);
DEBUG_ERROR("servicePort = %d", servicePort);
for(;;) {

View File

@@ -27,6 +27,7 @@
#include "pico_ipv6.h"
#include "ZeroTierSDK.h"
#include "Utils.hpp"
#include "SocketTap.hpp"
#include "picoTCP.hpp"
@@ -36,13 +37,6 @@
#include "Constants.hpp"
#include "Phy.hpp"
// stack locks
ZeroTier::Mutex _lock;
ZeroTier::Mutex _lock_mem;
struct pico_socket;
struct pico_device;
extern "C" int pico_stack_init(void);
extern "C" void pico_stack_tick(void);
@@ -73,6 +67,8 @@ extern "C" struct pico_ipv6_link * pico_ipv6_link_add(PICO_IPV6_LINK_ADD_SIG);
namespace ZeroTier {
//extern ZeroTier::Mutex _accepted_connection_lock;
// Reference to the tap interface
// This is needed due to the fact that there's a lot going on in the tap interface
// that needs to be updated on each of the network stack's callbacks and not every
@@ -82,7 +78,6 @@ namespace ZeroTier {
// will make it easier to maintain multiple active tap interfaces
struct pico_device picodev;
SocketTap * picotap;
int pico_eth_send(struct pico_device *dev, void *buf, int len);
int pico_eth_poll(struct pico_device *dev, int loop_score);
@@ -105,6 +100,7 @@ namespace ZeroTier {
picodev.send = pico_eth_send; // tx
picodev.poll = pico_eth_poll; // rx
picodev.mtu = tap->_mtu;
picodev.tap = tap;
if( 0 != pico_device_init(&(picodev), "p0", mac)) {
DEBUG_ERROR("dev init failed");
return;
@@ -125,6 +121,7 @@ namespace ZeroTier {
pico_ipv6_link_add(&(picodev), ipaddr, netmask);
picodev.send = pico_eth_send; // tx
picodev.poll = pico_eth_poll; // rx
picodev.tap = tap;
uint8_t mac[PICO_SIZE_ETH];
tap->_mac.copyTo(mac, PICO_SIZE_ETH);
DEBUG_ATTN("mac = %s", tap->_mac.toString().c_str());
@@ -138,7 +135,6 @@ namespace ZeroTier {
}
}
// Main stack loop
void picoTCP::pico_loop(SocketTap *tap)
{
while(tap->_run)
@@ -151,7 +147,7 @@ namespace ZeroTier {
void picoTCP::pico_cb_tcp_read(ZeroTier::SocketTap *tap, struct pico_socket *s)
{
DEBUG_INFO();
Connection *conn = (Connection*)((Larg*)(s->priv))->conn;
Connection *conn = (Connection*)((ConnectionPair*)(s->priv))->conn;
if(conn) {
int r;
uint16_t port = 0;
@@ -180,7 +176,7 @@ namespace ZeroTier {
void picoTCP::pico_cb_udp_read(SocketTap *tap, struct pico_socket *s)
{
DEBUG_INFO();
Connection *conn = (Connection*)((Larg*)(s->priv))->conn;
Connection *conn = (Connection*)((ConnectionPair*)(s->priv))->conn;
if(conn) {
uint16_t port = 0;
@@ -242,7 +238,7 @@ namespace ZeroTier {
void picoTCP::pico_cb_tcp_write(SocketTap *tap, struct pico_socket *s)
{
DEBUG_INFO();
Connection *conn = (Connection*)((Larg*)(s->priv))->conn;
Connection *conn = (Connection*)((ConnectionPair*)(s->priv))->conn;
if(!conn) {
DEBUG_ERROR("invalid connection");
@@ -276,10 +272,10 @@ namespace ZeroTier {
{
DEBUG_INFO();
// TODO: Test API out of order so this check isn't necessary
if(!(SocketTap*)((Larg*)(s->priv)))
if(!(SocketTap*)((ConnectionPair*)(s->priv)))
return;
SocketTap *tap = (SocketTap*)((Larg*)(s->priv))->tap;
Connection *conn = (Connection*)((Larg*)(s->priv))->conn;
SocketTap *tap = (SocketTap*)((ConnectionPair*)(s->priv))->tap;
Connection *conn = (Connection*)((ConnectionPair*)(s->priv))->conn;
int err;
Mutex::Lock _l(tap->_tcpconns_m);
@@ -289,82 +285,79 @@ namespace ZeroTier {
// accept()
if (ev & PICO_SOCK_EV_CONN) {
uint32_t peer;
uint16_t port;
struct pico_socket *client_psock = pico_socket_accept(s, &peer, &port);
DEBUG_INFO("accepted (pico_sock=%p) on (pico_sock=%p)", client_psock, conn->picosock);
if(!client_psock) {
DEBUG_EXTRA("unable to accept conn. (event might not be incoming, not necessarily an error), picosock=%p", (conn->picosock));
}
if(conn->state == ZT_SOCK_STATE_LISTENING)
{
uint32_t peer;
uint16_t port;
struct pico_socket *client_psock = pico_socket_accept(s, &peer, &port);
if(!client_psock) {
if(pico_err == PICO_ERR_EINVAL)
DEBUG_ERROR("pico_err = PICO_ERR_EINVAL");
if(pico_err == PICO_ERR_EAGAIN)
DEBUG_ERROR("pico_err = PICO_ERR_EAGAIN");
return;
}
// Create a new Connection and add it to the queue,
// some time in the future a call to zts_multiplex_accept() will pick up
// this new connection, add it to the connection list and return its
// Connection->sock to the application
// Create a new Connection and add it to the queue,
// some time in the future a call to zts_multiplex_accept() will pick up
// this new connection, add it to the connection list and return its
// Connection->sock to the application
Connection *newConn = new Connection();
newConn->socket_type = SOCK_STREAM;
newConn->sock = tap->_phy.wrapSocket(newConn->sdk_fd, newConn);
newConn->picosock = client_psock;
newConn->tap = tap;
newConn->picosock->priv = new Larg(tap,newConn);
tap->_Connections.push_back(newConn);
conn->_AcceptedConnections.push(newConn);
Connection *newConn = new Connection();
newConn->socket_type = SOCK_STREAM;
newConn->picosock = client_psock;
newConn->tap = tap;
newConn->picosock->priv = new ConnectionPair(tap,newConn);
tap->_Connections.push_back(newConn);
conn->_AcceptedConnections.push(newConn);
// For I/O loop participation and referencing the PhySocket's parent Connection in callbacks
newConn->sock = tap->_phy.wrapSocket(newConn->sdk_fd, newConn);
}
if(conn->state != ZT_SOCK_STATE_LISTENING) {
// set state so blocking socket multiplexer logic will pick this up
conn->state = ZT_SOCK_STATE_UNHANDLED_CONNECTED;
}
}
if (ev & PICO_SOCK_EV_FIN) {
DEBUG_INFO("socket closed. exit normally. picosock=%p\n\n", s);
DEBUG_INFO("socket closed. exit normally. picosock=%p", s);
//pico_timer_add(2000, compare_results, NULL);
}
if (ev & PICO_SOCK_EV_ERR) {
DEBUG_INFO("socket error received" /*, strerror(pico_err)*/);
if(pico_err == PICO_ERR_ECONNRESET) {
DEBUG_ERROR("PICO_ERR_ECONNRESET");
conn->state = PICO_ERR_ECONNRESET;
}
DEBUG_INFO("socket error received pico_err=%d, picosock=%p", pico_err, s);
}
if (ev & PICO_SOCK_EV_CLOSE) {
err = pico_socket_close(s);
DEBUG_INFO("socket closure = %d, picosock=%p", err, s);
if(err==0) {
tap->closeConnection(conn->sock);
}
if(err==0)
tap->Close(conn);
return;
}
// Read from picoTCP socket
if (ev & PICO_SOCK_EV_RD) {
if(conn->socket_type==SOCK_STREAM)
pico_cb_tcp_read(picotap, s);
pico_cb_tcp_read(tap, s);
if(conn->socket_type==SOCK_DGRAM)
pico_cb_udp_read(picotap, s);
pico_cb_udp_read(tap, s);
}
// Write to picoTCP socket
if (ev & PICO_SOCK_EV_WR)
pico_cb_tcp_write(picotap, s);
pico_cb_tcp_write(tap, s);
}
// Called when an incoming ping is received
/*
static void pico_cb_ping(struct pico_icmp4_stats *s)
{
DEBUG_INFO();
char host[30];
pico_ipv4_to_string(host, s->dst.addr);
if (s->err == 0) {
printf("%lu bytes from %s: icmp_req=%lu ttl=%lu time=%lu ms\n", s->size,
host, s->seq, s->ttl, (long unsigned int)s->time);
} else {
printf("PING %lu to %s: Error %d\n", s->seq, host, s->err);
}
}
*/
int pico_eth_send(struct pico_device *dev, void *buf, int len)
{
DEBUG_INFO();
SocketTap *tap = (SocketTap*)(dev->tap);
struct pico_eth_hdr *ethhdr;
ethhdr = (struct pico_eth_hdr *)buf;
MAC src_mac;
MAC dest_mac;
src_mac.setTo(ethhdr->saddr, 6);
dest_mac.setTo(ethhdr->daddr, 6);
picotap->_handler(picotap->_arg,NULL,picotap->_nwid,src_mac,dest_mac,
tap->_handler(tap->_arg,NULL,tap->_nwid,src_mac,dest_mac,
Utils::ntoh((uint16_t)ethhdr->proto),0, ((char*)buf) + sizeof(struct pico_eth_hdr),len - sizeof(struct pico_eth_hdr));
return len;
}
@@ -372,7 +365,6 @@ namespace ZeroTier {
void picoTCP::pico_rx(SocketTap *tap, const MAC &from,const MAC &to,unsigned int etherType,
const void *data,unsigned int len)
{
DEBUG_INFO();
// Since picoTCP only allows the reception of frames from within the polling function, we
// must enqueue each frame into a memory structure shared by both threads. This structure will
Mutex::Lock _l(tap->_pico_frame_rxbuf_m);
@@ -385,85 +377,50 @@ namespace ZeroTier {
int newlen = len + sizeof(int) + sizeof(struct pico_eth_hdr);
int mylen;
// FIXME
while(newlen > (MAX_PICO_FRAME_RX_BUF_SZ-tap->pico_frame_rxbuf_tot) && ethhdr.proto == 56710)
{
mylen = 0;
//DEBUG_FLOW(" [ ZTWIRE -> FBUF ] not enough space left on RX frame buffer, dropping oldest packet in buffer");
/*
memcpy(&mylen, picotap->pico_frame_rxbuf, sizeof(len));
memmove(tap->pico_frame_rxbuf, tap->pico_frame_rxbuf + mylen, MAX_PICO_FRAME_RX_BUF_SZ-mylen); // shift buffer
picotap->pico_frame_rxbuf_tot-=mylen;
*/
memset(tap->pico_frame_rxbuf,0,MAX_PICO_FRAME_RX_BUF_SZ);
picotap->pico_frame_rxbuf_tot=0;
tap->pico_frame_rxbuf_tot=0;
}
memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot, &newlen, sizeof(newlen)); // size of frame + meta
memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot + sizeof(newlen), &ethhdr, sizeof(ethhdr)); // new eth header
memcpy(tap->pico_frame_rxbuf + tap->pico_frame_rxbuf_tot + sizeof(newlen) + sizeof(ethhdr), data, len); // frame data
tap->pico_frame_rxbuf_tot += newlen;
DEBUG_FLOW(" [ ZTWIRE -> FBUF ] Move FRAME(sz=%d) into FBUF(sz=%d), data_len=%d", newlen, picotap->pico_frame_rxbuf_tot, len);
DEBUG_FLOW(" [ ZWIRE -> FBUF ] Move FRAME(sz=%d) into FBUF(sz=%d), data_len=%d", newlen, tap->pico_frame_rxbuf_tot, len);
}
int pico_eth_poll(struct pico_device *dev, int loop_score)
{
// OPTIMIZATION: The copy logic and/or buffer structure should be reworked for better performance after the BETA
SocketTap *tap = (SocketTap*)(dev->tap);
// FIXME: The copy logic and/or buffer structure should be reworked for better performance after the BETA
// SocketTap *tap = (SocketTap*)netif->state;
Mutex::Lock _l(picotap->_pico_frame_rxbuf_m);
Mutex::Lock _l(tap->_pico_frame_rxbuf_m);
unsigned char frame[ZT_SDK_MTU];
int len;
while (picotap->pico_frame_rxbuf_tot > 0 && loop_score > 0) {
//DEBUG_FLOW(" [ FBUF -> STACK] Frame buffer SZ=%d", picotap->pico_frame_rxbuf_tot);
int err;
while (tap->pico_frame_rxbuf_tot > 0 && loop_score > 0) {
//DEBUG_FLOW(" [ FBUF -> STACK] Frame buffer SZ=%d", tap->pico_frame_rxbuf_tot);
memset(frame, 0, sizeof(frame));
len = 0;
memcpy(&len, picotap->pico_frame_rxbuf, sizeof(len)); // get frame len
memcpy(&len, tap->pico_frame_rxbuf, sizeof(len)); // get frame len
if(len >= 0) {
//DEBUG_FLOW(" [ FBUF -> STACK] Moving FRAME of size (%d) from FBUF(sz=%d) into stack",len, picotap->pico_frame_rxbuf_tot-len);
memcpy(frame, picotap->pico_frame_rxbuf + sizeof(len), len-(sizeof(len)) ); // get frame data
memmove(picotap->pico_frame_rxbuf, picotap->pico_frame_rxbuf + len, MAX_PICO_FRAME_RX_BUF_SZ-len); // shift buffer
pico_stack_recv(dev, (uint8_t*)frame, (len-sizeof(len)));
picotap->pico_frame_rxbuf_tot-=len;
//DEBUG_FLOW(" [ FBUF -> STACK] Moving FRAME of size (%d) from FBUF(sz=%d) into stack",len, tap->pico_frame_rxbuf_tot-len);
memcpy(frame, tap->pico_frame_rxbuf + sizeof(len), len-(sizeof(len)) ); // get frame data
memmove(tap->pico_frame_rxbuf, tap->pico_frame_rxbuf + len, MAX_PICO_FRAME_RX_BUF_SZ-len); // shift buffer
err = pico_stack_recv(dev, (uint8_t*)frame, (len-sizeof(len)));
tap->pico_frame_rxbuf_tot-=len;
}
else {
DEBUG_ERROR("Invalid frame size (%d). Exiting.",len);
exit(0);
zt_dump_stacktrace(0);
}
loop_score--;
}
return loop_score;
}
void picoTCP::pico_handleWrite(Connection *conn)
{
DEBUG_INFO();
if(!conn || !conn->picosock) {
DEBUG_ERROR(" invalid connection");
return;
}
int max, r, max_write_len = conn->txsz < ZT_SDK_MTU ? conn->txsz : ZT_SDK_MTU;
if((r = pico_socket_write(conn->picosock, &conn->txbuf, max_write_len)) < 0) {
DEBUG_ERROR("unable to write to picosock=%p, r=%d", (conn->picosock), r);
return;
}
// adjust buffer
int sz = (conn->txsz)-r;
if(sz)
memmove(&conn->txbuf, (conn->txbuf+r), sz);
conn->txsz -= r;
if(conn->socket_type == SOCK_STREAM) {
max = ZT_TCP_TX_BUF_SZ;
DEBUG_TRANS("[TCP TX] ---> :: {TX: %.3f%%, RX: %.3f%%, physock=%p} :: %d bytes",
(float)conn->txsz / (float)max, (float)conn->rxsz / max, conn->sock, r);
}
if(conn->socket_type == SOCK_DGRAM) {
max = ZT_UDP_TX_BUF_SZ;
DEBUG_TRANS("[UDP TX] ---> :: {TX: %.3f%%, RX: %.3f%%, physock=%p} :: %d bytes",
(float)conn->txsz / (float)max, (float)conn->rxsz / max, conn->sock, r);
}
}
int picoTCP::pico_Connect(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen)
{
int err;
@@ -483,8 +440,8 @@ namespace ZeroTier {
char ipv6_str[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN);
pico_string_to_ipv6(ipv6_str, zaddr.addr);
DEBUG_ATTN("addr=%s:%d", ipv6_str, Utils::ntoh(addr->sin_port));
err = pico_socket_connect(conn->picosock, &zaddr, (struct sockaddr_in *)&addr->sin_port);
DEBUG_ATTN("addr=%s:%d", ipv6_str, Utils::ntoh(in6->sin6_port));
err = pico_socket_connect(conn->picosock, &zaddr, in6->sin6_port);
#endif
memcpy(&(conn->peer_addr), &addr, sizeof(struct sockaddr_storage));
@@ -516,7 +473,7 @@ namespace ZeroTier {
inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN);
pico_string_to_ipv6(ipv6_str, zaddr.addr);
//DEBUG_ATTN("addr=%s:%d, physock=%p, picosock=%p", ipv6_str, Utils::ntoh(addr->sin_port), sock, (conn->picosock));
err = pico_socket_bind(conn->picosock, &zaddr, (struct sockaddr_in *)&addr->sin_port);
err = pico_socket_bind(conn->picosock, &zaddr, (uint16_t *)in6->sin6_port);
#endif
if(err < 0) {
DEBUG_ERROR("unable to bind pico_socket(%p), err=%d", (conn->picosock), err);
@@ -546,16 +503,17 @@ namespace ZeroTier {
if((err = pico_socket_listen(conn->picosock, backlog)) < 0)
{
if(err == PICO_ERR_EINVAL) {
DEBUG_ERROR("PICO_ERR_EINVAL - invalid argument");
DEBUG_ERROR("PICO_ERR_EINVAL");
errno = EINVAL;
return -1;
}
if(err == PICO_ERR_EISCONN) {
DEBUG_ERROR("PICO_ERR_EISCONN - socket is connected");
DEBUG_ERROR("PICO_ERR_EISCONN");
errno = EISCONN;
return -1;
}
}
conn->state = ZT_SOCK_STATE_LISTENING;
return ZT_ERR_OK;
}
@@ -574,17 +532,17 @@ namespace ZeroTier {
return err;
}
void picoTCP::pico_handleRead(PhySocket *sock,void **uptr,bool lwip_invoked)
void picoTCP::pico_Read(SocketTap *tap, PhySocket *sock, void **uptr, bool stack_invoked)
{
DEBUG_INFO();
if(!lwip_invoked) {
if(!stack_invoked) {
// The stack thread writes to RXBUF as well
picotap->_tcpconns_m.lock();
picotap->_rx_buf_m.lock();
tap->_tcpconns_m.lock();
tap->_rx_buf_m.lock();
}
int tot = 0, n = -1, write_attempts = 0;
Connection *conn = picotap->getConnection(sock);
Connection *conn = (Connection*)uptr;
if(conn && conn->rxsz) {
//
@@ -592,7 +550,7 @@ namespace ZeroTier {
// Try to write ZT_SDK_MTU-sized chunk to app socket
while(tot < ZT_SDK_MTU) {
write_attempts++;
n = picotap->_phy.streamSend(conn->sock, (conn->rxbuf)+tot, ZT_SDK_MTU);
n = tap->_phy.streamSend(conn->sock, (conn->rxbuf)+tot, ZT_SDK_MTU);
tot += n;
DEBUG_FLOW(" [ ZTSOCK <- RXBUF] wrote = %d, errno=%d", n, errno);
// If socket is unavailable, attempt to write N times before giving up
@@ -620,7 +578,7 @@ namespace ZeroTier {
//
if(conn->socket_type==SOCK_STREAM) {
DEBUG_TRANS("writing to conn->sock = %p", conn->sock);
n = picotap->_phy.streamSend(conn->sock, conn->rxbuf, conn->rxsz);
n = tap->_phy.streamSend(conn->sock, conn->rxbuf, conn->rxsz);
if(conn->rxsz-n > 0) // If more remains on buffer
memcpy(conn->rxbuf, conn->rxbuf+n, conn->rxsz - n);
conn->rxsz -= n;
@@ -636,37 +594,67 @@ namespace ZeroTier {
#endif
}
if(conn->rxsz == 0) {
picotap->_phy.setNotifyWritable(sock, false);
tap->_phy.setNotifyWritable(sock, false);
}
else {
picotap->_phy.setNotifyWritable(sock, true);
tap->_phy.setNotifyWritable(sock, true);
}
}
else {
picotap->_phy.setNotifyWritable(sock, false);
tap->_phy.setNotifyWritable(sock, false);
}
}
if(!lwip_invoked) {
picotap->_tcpconns_m.unlock();
picotap->_rx_buf_m.unlock();
if(!stack_invoked) {
tap->_tcpconns_m.unlock();
tap->_rx_buf_m.unlock();
}
// FIXME: Re-write debug traces
DEBUG_FLOW(" [ ZTSOCK <- RXBUF] Emitted (%d) from RXBUF(%d) to socket", tot, conn->rxsz);
}
void picoTCP::pico_handleClose(PhySocket *sock)
void picoTCP::pico_Write(Connection *conn)
{
DEBUG_INFO();
/*
int ret;
if(conn && conn->picosock) {
if((ret = pico_socket_close(conn->picosock)) < 0) {
DEBUG_ERROR("error closing pico_socket(%p)", (void*)(conn->picosock));
// sendReturnValue()
}
if(!conn || !conn->picosock) {
DEBUG_ERROR(" invalid connection");
return;
}
int max, r, max_write_len = conn->txsz < ZT_SDK_MTU ? conn->txsz : ZT_SDK_MTU;
if((r = pico_socket_write(conn->picosock, &conn->txbuf, max_write_len)) < 0) {
DEBUG_ERROR("unable to write to picosock=%p, r=%d", (conn->picosock), r);
return;
}
// adjust buffer
int sz = (conn->txsz)-r;
if(sz)
memmove(&conn->txbuf, (conn->txbuf+r), sz);
conn->txsz -= r;
if(conn->socket_type == SOCK_STREAM) {
max = ZT_TCP_TX_BUF_SZ;
DEBUG_TRANS("[TCP TX] ---> :: {TX: %.3f%%, RX: %.3f%%, physock=%p} :: %d bytes",
(float)conn->txsz / (float)max, (float)conn->rxsz / max, conn->sock, r);
}
if(conn->socket_type == SOCK_DGRAM) {
max = ZT_UDP_TX_BUF_SZ;
DEBUG_TRANS("[UDP TX] ---> :: {TX: %.3f%%, RX: %.3f%%, physock=%p} :: %d bytes",
(float)conn->txsz / (float)max, (float)conn->rxsz / max, conn->sock, r);
}
}
int picoTCP::pico_Close(Connection *conn)
{
DEBUG_INFO();
int err;
if(conn && conn->picosock) {
if((err = pico_socket_close(conn->picosock)) < 0) {
errno = pico_err;
DEBUG_ERROR("error closing pico_socket(%p)", (void*)(conn->picosock));
return -1;
}
return err;
}
DEBUG_ERROR("invalid connection or pico_socket");
*/
return -1;
}
}

View File

@@ -32,7 +32,7 @@
#include "SocketTap.hpp"
/****************************************************************************/
/* PicoTCP API Signatures */
/* PicoTCP API Signatures (See ZeroTierSDK.h for the API an app should use) */
/****************************************************************************/
#define PICO_IPV4_TO_STRING_SIG char *ipbuf, const uint32_t ip
@@ -100,19 +100,44 @@ namespace ZeroTier
static void pico_cb_socket_activity(uint16_t ev, struct pico_socket *s);
/*
* Where packets enter the stack
* Packets from the ZeroTier virtual wire enter the stack here
*/
void pico_rx(SocketTap *tap, const ZeroTier::MAC &from,const ZeroTier::MAC &to,unsigned int etherType,const void *data,unsigned int len);
void pico_handleRead(ZeroTier::PhySocket *sock,void **uptr,bool lwip_invoked);
void pico_handleClose(ZeroTier::PhySocket *sock);
void pico_handleWrite(Connection *conn);
// common socket operations
/*
* Connect to remote host via userspace network stack interface - Called from SocketTap
*/
int pico_Connect(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen);
/*
* Bind to a userspace network stack interface - Called from SocketTap
*/
int pico_Bind(Connection *conn, int fd, const struct sockaddr *addr, socklen_t addrlen);
/*
* Listen for incoming connections - Called from SocketTap
*/
int pico_Listen(Connection *conn, int fd, int backlog);
/*
* Accept an incoming connection - Called from SocketTap
*/
int pico_Accept(Connection *conn);
/*
* Read from RX buffer to application - Called from SocketTap
*/
void pico_Read(SocketTap *tap, ZeroTier::PhySocket *sock,void **uptr,bool stack_invoked);
/*
* Write to userspace network stack - Called from SocketTap
*/
void pico_Write(Connection *conn);
/*
* Close a Connection - Called from SocketTap
*/
int pico_Close(Connection *conn);
};
}