/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2015 ZeroTier, Inc.
*
* 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 .
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#include
#include
#include "Intercept.h"
#include "LWIPStack.hpp"
#ifndef _NETCON_SERVICE_H_
#define _NETCON_SERVICE_H_
using namespace std;
enum NetconSocketType { RPC, BUFFER };
class NetconIntercept;
class NetconConnection;
class NetconSocket;
class NetconSocket{
int fd;
NetconSocketType type;
};
class NetconConnection
{
public:
int tid;
int our_fd;
int their_fd; /* what the user app sees -- and what they will send us */
struct tcp_pcb *pcb; /* for handling lwIP calls/data */
bool unacked;
unsigned char *write_buf; /* we need this reference so we can grab data from the buffer during a lwip-poll callback */
long unsigned int write_count;
long unsigned int write_total;
unsigned char buf[DEFAULT_READ_BUFFER_SIZE];
int idx;
unsigned long data_acked;
unsigned long data_sent;
unsigned long data_read;
unsigned long data_recvd;
NetconIntercept* owner;
NetconConnection(NetconIntercept* owner,
int tid,
int our_fd,
int their_fd,
struct tcp_pcb *pcb)
:
write_total(0),
write_count(0),
write_buf(NULL),
owner(owner),
tid(tid),
our_fd(our_fd),
their_fd(their_fd),
pcb(pcb)
{}
};
class NetconIntercept
{
public:
// State (this needs to be evaluated)
NetconConnection *unmapped_conn;
bool waiting_for_retval;
// Connections created for this intercept
vector owned_connections;
int tid; /* just for uniqueness */
int rpc;
NetconIntercept(int tid, int rpc)
:
waiting_for_retval(false),
unmapped_conn(NULL),
tid(tid),
rpc(rpc)
{}
};
#define POLL_SZ ZT_PHY_MAX_SOCKETS+(ZT_PHY_MAX_INTERCEPTS*2)+2
class NetconService
{
public:
LWIPStack *ls;
// TODO: shall replace with map
vector intercepts;
vector connections;
/* fd_sets for main I/O polling */
fd_set fdset;
fd_set exfdset;
int maxfd;
size_t sz;
int tapfd;
// Sets of file descriptors we will poll() on
int default_rpc_pipe;
struct pollfd allfds[POLL_SZ];
int alltypes[POLL_SZ];
/* cached fd_sets */
bool possible_state_change;
fd_set cached_fdset;
fd_set cached_exfdset;
int cached_alltypes[POLL_SZ];
int cached_maxfd;
int cached_sz;
NetconService(LWIPStack *ls, string _handle)
:
ls(ls),
maxfd(0),
sz(0),
possible_state_change(true)
{}
/* Assemble single poll list */
void assemble_fd_sets()
{
sz = 2 + connections.size() + intercepts.size();
// initialize
for(size_t i=0; iour_fd;
allfds[idx].events = POLLIN;
alltypes[idx] = 3;
}
// established connections
for(size_t i=0; irpc;
allfds[idx].events = POLLIN;
alltypes[idx] = 4;
}
FD_ZERO(&fdset);
FD_ZERO(&exfdset);
// populate master fd_set
for(size_t i=0; i maxfd)
maxfd = allfds[i].fd;
}
// cache copy of valid fd_set
possible_state_change = false;
memcpy(&cached_fdset, &fdset, sizeof(fdset));
memcpy(&cached_exfdset, &exfdset, sizeof(exfdset));
memcpy(&cached_alltypes, &alltypes, sizeof(alltypes));
cached_maxfd = maxfd;
cached_sz = sz;
}
NetconConnection *get_connection_by_pcb(struct tcp_pcb *pcb) {
for(size_t i=0; ipcb == pcb) {
return connections[i];
}
}
return NULL;
}
NetconConnection *get_connection_by_buf_sock(int fd) {
for(size_t i=0; iour_fd==fd) {
return connections[i];
}
}
return NULL;
}
// FIXME: This will be a common operation and thus should be done via
// some sort of hashing, not iterative lookup.
NetconIntercept *get_intercept_by_pcb(struct tcp_pcb* pcb) {
for(size_t i=0; ipcb==pcb) {
return connections[i]->owner;
}
}
return NULL;
}
NetconIntercept *get_intercept_by_rpc(int af) {
for(size_t i=0; irpc==af) {
return intercepts[i];
}
}
return NULL;
}
NetconConnection *get_connection_by_that_fd(NetconIntercept* h, int fd)
{
for(size_t i=0; iowned_connections.size(); i++) {
if(h->owned_connections[i]->their_fd==fd) {
return h->owned_connections[i];
}
}
return NULL;
}
NetconConnection *get_connection_by_this_fd(int fd)
{
for(size_t i=0; iour_fd==fd) {
return connections[i];
}
}
return NULL;
}
NetconConnection *get_connection_by_that_fd(int fd)
{
for(size_t i=0; itheir_fd==fd) {
return connections[i];
}
}
return NULL;
}
NetconConnection *add_connection(NetconIntercept* owner,
int tid,
int our_fd,
int their_fd,
struct tcp_pcb* pcb)
{
possible_state_change = true;
if(connections.size() >= ZT_PHY_MAX_SOCKETS) {
return NULL;
}
NetconConnection *new_conn = new NetconConnection(owner, tid, our_fd, their_fd, pcb);
connections.push_back(new_conn);
NetconIntercept *h;
for(size_t i=0; itid == tid) {
h = intercepts[i];
}
}
if(h)
h->owned_connections.push_back(new_conn);
return new_conn;
}
// Removes a Connection from the Service and updates poll lists
void remove_connection(NetconConnection *c)
{
possible_state_change = true;
for(size_t i=0; iour_fd);
ls->tcp_close(c->pcb);
delete c;
connections.erase(connections.begin() + i);
}
}
}
int add_intercept(int listen_sock) {
possible_state_change = true;
int accept_socket = accept(listen_sock, NULL, NULL);
intercepts.push_back(new NetconIntercept(999, accept_socket));
return accept_socket;
}
// Removes an Intercept from the Service
bool remove_intercept(NetconIntercept *h)
{
possible_state_change = true;
// remove all connections owned by this Intercept
for(size_t i=0; iowned_connections.size(); i++) {
remove_connection(h->owned_connections[i]);
}
// find and remove Intercept
for(size_t i=0; i