You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1250 lines
31 KiB
1250 lines
31 KiB
/* |
|
* 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
|
|
|