Added selectable TCP_WRITE_FLAG_COPY mode on a per-socket basis, updated lwIP driver for socket limit checks

This commit is contained in:
Joseph Henry
2017-09-08 16:13:56 -07:00
parent 05fec81757
commit 1d4f36a811
9 changed files with 208 additions and 129 deletions

4
FAQ.md
View File

@@ -7,4 +7,6 @@ Yes! - Just let us know, and we will work out a licensing scheme. You will need
### Application or service won't fully come online
Sometimes it can take a substatial amount of time for libzt to come online and become reachable. In cases where it never seems to progress beyond this stage you should check to make sure there are no rogue processes on the machines using the same ZeroTier identity files, or no other instances on your ZeroTier network using said identity files. If this doesn't help. Contact us.
In rare circumstances it can take a substantial amount of time for a libzt instance to come online and become reachable. In cases where it never seems to progress beyond this stage you should check to make sure there are no rogue processes on the machines using the same ZeroTier identity files, or no other instances on your ZeroTier network using said identity files. If this doesn't help. Contact us.

View File

@@ -561,7 +561,7 @@ int zts_get_pico_socket(int fd, struct pico_socket **s);
/*
* Whether we can add a new socket or not. Depends on stack in use
*/
bool can_provision_new_socket();
bool can_provision_new_socket(int socket_type);
/**
* Returns the number of sockets either already provisioned or waiting to be

View File

@@ -35,6 +35,9 @@
#if defined(STACK_PICO)
#include "pico_socket.h"
#endif
#if defined(STACK_LWIP)
#include "lwip/tcp.h"
#endif
#include "Phy.hpp"
@@ -65,6 +68,18 @@ namespace ZeroTier {
#endif
#if defined(STACK_LWIP)
void *pcb = NULL; // Protocol Control Block
/*
- TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated
for the data to be copied into. If this flag is not given, no new memory
should be allocated and the data should only be referenced by pointer. This
also means that the memory behind dataptr must not change until the data is
ACKed by the remote host
- TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is omitted,
the PSH flag is set in the last segment created by this call to tcp_write.
If this flag is given, the PSH flag is not set.
*/
// copy as default, processed via pointer reference if set to 0. See notes in lwip_cb_sent() and lwip_Write()
int8_t copymode = TCP_WRITE_FLAG_COPY;
#endif
struct sockaddr_storage local_addr; // address we've bound to locally

View File

@@ -359,7 +359,7 @@ namespace ZeroTier {
for(int i=0; i<_VirtualSockets.size(); i++) {
if(vs == _VirtualSockets[i]) {
_VirtualSockets.erase(_VirtualSockets.begin() + i);
DEBUG_INFO("Removed vs=%p from vt=%p", vs, this);
//DEBUG_EXTRA("Removed vs=%p from vt=%p", vs, this);
break;
}
}

View File

@@ -372,7 +372,7 @@ Darwin:
[--] [EMFILE] The per-process descriptor table is full.
[NA] [ENFILE] The system file table is full.
[ ] [ENOBUFS] Insufficient buffer space is available. The socket cannot be created until sufficient resources are freed.
[ ] [ENOMEM] Insufficient memory was available to fulfill the request.
[--] [ENOMEM] Insufficient memory was available to fulfill the request.
[--] [EPROTONOSUPPORT] The protocol type or the specified protocol is not supported within this domain.
[ ] [EPROTOTYPE] The socket type is not supported by the protocol.
*/
@@ -394,9 +394,7 @@ int zts_socket(ZT_SOCKET_SIG) {
errno = EPROTONOSUPPORT; // seemingly closest match
return -1;
}
if(socket_type == SOCK_RAW)
{
if(socket_type == SOCK_RAW) {
// VirtualSocket is only used to associate a socket with a VirtualTap, it has no other implication
ZeroTier::VirtualSocket *vs = new ZeroTier::VirtualSocket();
vs->socket_family = socket_family;
@@ -405,7 +403,6 @@ int zts_socket(ZT_SOCKET_SIG) {
add_unassigned_virtual_socket(vs->app_fd, vs);
return vs->app_fd;
}
#if defined(STACK_PICO)
struct pico_socket *p;
err = ZeroTier::picostack->pico_Socket(&p, socket_family, socket_type, protocol);
@@ -419,10 +416,10 @@ int zts_socket(ZT_SOCKET_SIG) {
}
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;
@@ -433,10 +430,12 @@ int zts_socket(ZT_SOCKET_SIG) {
vs->socket_type = socket_type;
vs->pcb = pcb;
add_unassigned_virtual_socket(vs->app_fd, vs);
err = vs->app_fd; // return one end of the socketpair
// 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
@@ -777,8 +776,7 @@ int zts_accept(ZT_ACCEPT_SIG) {
errno = EINVAL; // TODO, not actually a valid error for this function
return -1;
}
// since we'll be creating a new stack socket or protocol control block when we accept the connection
if(!can_provision_new_socket()) {
if(!can_provision_new_socket(SOCK_STREAM)) {
DEBUG_ERROR("cannot provision additional socket due to limitation of network stack");
errno = EMFILE;
return -1;
@@ -806,7 +804,7 @@ int zts_accept(ZT_ACCEPT_SIG) {
ZeroTier::VirtualSocket *accepted_vs;
if(!err) {
if(!blocking) { // non-blocking
DEBUG_EXTRA("EWOULDBLOCK, not a real error, assuming non-blocking mode");
DEBUG_EXTRA("EWOULDBLOCK, assuming non-blocking mode");
errno = EWOULDBLOCK;
err = -1;
accepted_vs = tap->Accept(vs);
@@ -1566,7 +1564,7 @@ ssize_t zts_recv(ZT_RECV_SIG)
ssize_t zts_recvfrom(ZT_RECVFROM_SIG)
{
DEBUG_TRANS("fd=%d", fd);
//DEBUG_TRANS("fd=%d", fd);
int32_t r = 0;
errno = 0;
if(fd < 0 || fd >= ZT_MAX_SOCKETS) {
@@ -1590,7 +1588,6 @@ ssize_t zts_recvfrom(ZT_RECVFROM_SIG)
// 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)){
//DEBUG_ERROR("invalid datagram, PEEK, r=%d", r);
errno = EIO; // TODO: test for this
return -1;
}
@@ -2019,20 +2016,26 @@ int zts_get_pico_socket(int fd, struct pico_socket **s)
}
#endif
bool can_provision_new_socket()
bool can_provision_new_socket(int socket_type)
{
#if defined(STACK_PICO)
if(pico_ntimers()+1 >= PICO_MAX_TIMERS) {
return false;
return !(pico_ntimers()+1 > PICO_MAX_TIMERS);
#endif
#if defined(STACK_LWIP)
if(socket_type == SOCK_STREAM) {
return !(ZeroTier::lwIP::lwip_num_current_tcp_pcbs()+1 > MEMP_NUM_TCP_PCB);
}
if(socket_type == SOCK_DGRAM) {
return !(ZeroTier::lwIP::lwip_num_current_udp_pcbs()+1 > MEMP_NUM_UDP_PCB);
}
if(socket_type == SOCK_RAW) {
return !(ZeroTier::lwIP::lwip_num_current_raw_pcbs()+1 > MEMP_NUM_RAW_PCB);
}
return true;
#endif
#if defined(STACK_LWIP)
// TODO: Add check here (see lwipopts.h)
return true;
#endif
#if defined(NO_STACK)
return true; // always true since there's no network stack timer limitation
// always true since there's no network stack timer/memory limitation
return true;
#endif
}
@@ -2186,13 +2189,13 @@ void del_virtual_socket(int fd)
{
ZeroTier::_multiplexer_lock.lock();
std::map<int, ZeroTier::VirtualSocket*>::iterator un_iter = ZeroTier::unmap.find(fd);
if(un_iter != ZeroTier::unmap.end()) {
ZeroTier::unmap.erase(un_iter);
}
if(un_iter != ZeroTier::unmap.end()) {
ZeroTier::unmap.erase(un_iter);
}
std::map<int, std::pair<ZeroTier::VirtualSocket*,ZeroTier::VirtualTap*>*>::iterator fd_iter = ZeroTier::fdmap.find(fd);
if(fd_iter != ZeroTier::fdmap.end()) {
if(fd_iter != ZeroTier::fdmap.end()) {
ZeroTier::fdmap.erase(fd_iter);
}
}
ZeroTier::_multiplexer_lock.unlock();
}

View File

@@ -24,7 +24,17 @@
* of your own application.
*/
// lwIP network stack driver
/*
lwIP network stack driver
NOTES:
Calls made in this network stack driver may never block since all packet
processing (input and output) as well as timer processing (TCP mainly) is done
in a single execution context.
*/
#include <algorithm>
@@ -36,6 +46,8 @@
#include "netif/ethernet.h"
#include "lwip/etharp.h"
#include "priv/tcp_priv.h"
#if defined(LIBZT_IPV6)
#include "lwip/ethip6.h"
#include "lwip/nd6.h"
@@ -169,6 +181,64 @@ namespace ZeroTier
}
}
int lwIP::lwip_num_current_tcp_pcbs()
{
// TODO: These will likely need some sort of locking protection
int count = 0;
struct tcp_pcb *pcb_ptr = tcp_active_pcbs; // PCBs that can RX/TX data
while(pcb_ptr) {
pcb_ptr = pcb_ptr->next;
count++;
DEBUG_ERROR("FOUND --- tcp_active_pcbs PCB COUNT = %d", count);
}
pcb_ptr = tcp_tw_pcbs; // PCBs in TIME-WAIT state
while(pcb_ptr) {
pcb_ptr = pcb_ptr->next;
count++;
DEBUG_ERROR("FOUND --- tcp_tw_pcbs PCB COUNT = %d", count);
}
/* TODO
pcb_ptr = tcp_listen_pcbs;
while(pcb_ptr) {
pcb_ptr = pcb_ptr->next;
count++;
DEBUG_ERROR("FOUND --- tcp_listen_pcbs PCB COUNT = %d", count);
}*/
pcb_ptr = tcp_bound_pcbs; // PCBs in a bound state
while(pcb_ptr) {
pcb_ptr = pcb_ptr->next;
count++;
DEBUG_ERROR("FOUND --- tcp_bound_pcbs PCB COUNT = %d", count);
}
return count;
}
int lwIP::lwip_num_current_udp_pcbs()
{
// TODO: These will likely need some sort of locking protection
int count = 0;
struct udp_pcb *pcb_ptr = udp_pcbs;
while(pcb_ptr) {
pcb_ptr = pcb_ptr->next;
count++;
DEBUG_ERROR("FOUND --- udp_pcbs PCB COUNT = %d", count);
}
return count;
}
int lwIP::lwip_num_current_raw_pcbs()
{
// TODO: These will likely need some sort of locking protection
int count = 0;
struct raw_pcb *pcb_ptr = raw_pcbs;
while(pcb_ptr) {
pcb_ptr = pcb_ptr->next;
count++;
DEBUG_ERROR("FOUND --- raw_pcbs PCB COUNT = %d", count);
}
return count;
}
int lwIP::lwip_add_dns_nameserver(struct sockaddr *addr)
{
return -1;
@@ -280,8 +350,7 @@ namespace ZeroTier
int lwIP::lwip_Socket(void **pcb, int socket_family, int socket_type, int protocol)
{
//DEBUG_INFO();
if(!can_provision_new_socket()) {
if(!can_provision_new_socket(socket_type)) {
DEBUG_ERROR("unable to create new socket due to limitation of network stack");
return -1;
}
@@ -340,49 +409,15 @@ namespace ZeroTier
tcp_poll(tpcb, lwip_cb_poll, LWIP_APPLICATION_POLL_FREQ);
tcp_arg(tpcb, vs);
//DEBUG_EXTRA(" pcb->state=%x", vs->TCP_pcb->state);
//if(vs->TCP_pcb->state != CLOSED) {
// DEBUG_INFO(" cannot connect using this PCB, PCB!=CLOSED");
// tap->sendReturnValue(tap->_phy.getDescriptor(rpcSock), -1, EAGAIN);
// return;
//}
if((err = tcp_connect(tpcb,&ba,port,lwip_cb_connected)) < 0)
{
if(err == ERR_ISCONN) {
// Already in connected state
errno = EISCONN;
return -1;
} if(err == ERR_USE) {
// Already in use
errno = EADDRINUSE;
return -1;
} if(err == ERR_VAL) {
// Invalid ipaddress parameter
errno = EINVAL;
return -1;
} if(err == ERR_RTE) {
// No route to host
errno = ENETUNREACH;
return -1;
} if(err == ERR_BUF) {
// No more ports available
errno = EAGAIN;
return -1;
}
if(err == ERR_MEM) {
// TODO: Doesn't describe the problem well, but closest match
errno = EAGAIN;
return -1;
}
errno = lwip_err_to_errno(err);
// We should only return a value if failure happens immediately
// Otherwise, we still need to wait for a callback from lwIP.
// - This is because an ERR_OK from tcp_connect() only verifies
// that the SYN packet was enqueued onto the stack properly,
// that's it!
// - Most instances of a retval for a connect() should happen
// in the nc_connect() and lwip_cb_err() callbacks!
DEBUG_ERROR("unable to connect");
errno = EAGAIN;
return -1;
}
}
@@ -391,12 +426,12 @@ namespace ZeroTier
int lwIP::lwip_Bind(VirtualTap *tap, VirtualSocket *vs, const struct sockaddr *addr, socklen_t addrlen)
{
// TODO: Check case for IP_ADDR_ANY
//DEBUG_EXTRA("vs=%p", vs);
ip_addr_t ba;
char addrstr[INET6_ADDRSTRLEN];
memset(addrstr, 0, INET6_ADDRSTRLEN);
int port = 0, err = 0;
#if defined(LIBZT_IPV4)
struct sockaddr_in *in4 = (struct sockaddr_in *)addr;
if(addr->sa_family == AF_INET) {
@@ -415,38 +450,23 @@ namespace ZeroTier
}
#endif
if(vs->socket_type == SOCK_DGRAM) {
err = udp_bind((struct udp_pcb*)vs->pcb, (const ip_addr_t *)&ba, port);
if(err == ERR_USE) {
if((err = udp_bind((struct udp_pcb*)vs->pcb, (const ip_addr_t *)&ba, port)) < 0) {
errno = lwip_err_to_errno(err);
err = -1;
errno = EADDRINUSE; // port in use
}
else {
// set the recv callback
// set callback
udp_recv((struct udp_pcb*)vs->pcb, lwip_cb_udp_recved, vs);
err = ERR_OK;
errno = ERR_OK; // success
}
}
else if (vs->socket_type == SOCK_STREAM) {
err = tcp_bind((struct tcp_pcb*)vs->pcb, (const ip_addr_t *)&ba, port);
if(err != ERR_OK) {
DEBUG_ERROR("err=%d", err);
if(err == ERR_USE){
err = -1;
errno = EADDRINUSE;
}
if(err == ERR_MEM){
err = -1;
errno = ENOMEM;
}
if(err == ERR_BUF){
err = -1;
errno = ENOMEM;
}
}
if((err = tcp_bind((struct tcp_pcb*)vs->pcb, (const ip_addr_t *)&ba, port)) < 0) {
errno = lwip_err_to_errno(err);
err = -1;
}
else {
err = ERR_OK;
errno = ERR_OK; // success
}
}
return err;
@@ -454,19 +474,25 @@ namespace ZeroTier
int lwIP::lwip_Listen(VirtualSocket *vs, int backlog)
{
//DEBUG_INFO("vs=%p", vs);
int err = 0;
struct tcp_pcb* listeningPCB;
#ifdef TCP_LISTEN_BACKLOG
listeningPCB = tcp_listen_with_backlog((struct tcp_pcb*)vs->pcb, backlog);
#else
listeningPCB = tcp_listen((struct tcp_pcb*)vs->pcb);
#endif
if(listeningPCB != NULL) {
if(listeningPCB) {
vs->pcb = listeningPCB;
tcp_accept(listeningPCB, lwip_cb_accept); // set callback
// set callback
tcp_accept(listeningPCB, lwip_cb_accept);
tcp_arg(listeningPCB, vs);
err = ERR_OK;
}
return 0;
else {
errno = ENOMEM;
err = -1;
}
return err;
}
VirtualSocket* lwIP::lwip_Accept(VirtualSocket *vs)
@@ -536,11 +562,10 @@ namespace ZeroTier
DEBUG_ERROR("no virtual socket");
return -1;
}
if(vs->socket_type == SOCK_DGRAM)
{
if(vs->socket_type == SOCK_DGRAM) {
// TODO: Packet re-assembly hasn't yet been tested with lwIP so UDP packets are limited to MTU-sized chunks
int udp_trans_len = std::min(len, (ssize_t)ZT_MAX_MTU);
//DEBUG_EXTRA("allocating pbuf chain of size=%d for UDP packet", udp_trans_len);
// DEBUG_EXTRA("allocating pbuf chain of size=%d for UDP packet", udp_trans_len);
struct pbuf * pb = pbuf_alloc(PBUF_TRANSPORT, udp_trans_len, PBUF_POOL);
if(!pb){
DEBUG_ERROR("unable to allocate new pbuf of size=%d", vs->TXbuf->count());
@@ -561,8 +586,7 @@ namespace ZeroTier
return udp_trans_len;
}
}
if(vs->socket_type == SOCK_STREAM)
{
if(vs->socket_type == SOCK_STREAM) {
// How much we are currently allowed to write to the VirtualSocket
ssize_t sndbuf = ((struct tcp_pcb*)vs->pcb)->snd_buf;
int err, r;
@@ -589,7 +613,8 @@ namespace ZeroTier
// Writes data pulled from the client's socket buffer to LWIP. This merely sends the
// data to LWIP to be enqueued and eventually sent to the network.
if(r > 0) {
err = tcp_write((struct tcp_pcb*)vs->pcb, vs->TXbuf->get_buf(), r, TCP_WRITE_FLAG_COPY);
err = tcp_write((struct tcp_pcb*)vs->pcb, vs->TXbuf->get_buf(), r, vs->copymode);
tcp_output((struct tcp_pcb*)vs->pcb);
if(err != ERR_OK) {
DEBUG_ERROR("error while writing to lwIP tcp_pcb, err=%d", err);
@@ -597,7 +622,15 @@ namespace ZeroTier
DEBUG_ERROR("lwIP out of memory");
return -1;
} else {
vs->TXbuf->consume(r); // success
if(vs->copymode & TCP_WRITE_FLAG_COPY) {
// since we copied the data (allocated pbufs), we can consume the buffer
vs->TXbuf->consume(r); // success
}
else {
// since we only processed the data by pointer reference we
// want to preserve it until it has been ACKed by the remote host
// (DO NOTHING)
}
return ERR_OK;
}
}
@@ -835,17 +868,16 @@ namespace ZeroTier
err_t lwIP::lwip_cb_sent(void* arg, struct tcp_pcb *PCB, u16_t len)
{
//DEBUG_EXTRA("pcb=%p", PCB);
/*
VirtualSocket *vs = (VirtualSocket *)arg;
Mutex::Lock _l(vs->tap->_tcpconns_m);
if(vs && len) {
int softmax = vs->socket_type == SOCK_STREAM ? ZT_TCP_TX_BUF_SZ : ZT_UDP_TX_BUF_SZ;
if(vs->TXbuf->count() < softmax) {
vs->tap->_phy.setNotifyReadable(vs->sock, true);
vs->tap->_phy.whack();
}
if(!vs){
DEBUG_ERROR("invalid vs for PCB=%p, len=%d", PCB, len);
}
if(!(vs->copymode & TCP_WRITE_FLAG_COPY)) {
// since we decided in lwip_Write() not to consume the buffere data, as it
// was not copied and was only used by pointer reference, we can now consume
// the data on the buffer since we've got an ACK back from the remote host
vs->TXbuf->consume(len);
}
*/
return ERR_OK;
}
@@ -875,79 +907,63 @@ namespace ZeroTier
VirtualSocket *vs = (VirtualSocket *)arg;
if(!vs){
DEBUG_ERROR("err=%d, invalid virtual socket", err);
errno = -1; // FIXME: Find more appropriate value
errno = -1;
}
DEBUG_ERROR("vs=%p, pcb=%p, fd=%d, err=%d", vs, vs->pcb, vs->app_fd, err);
vs->tap->Close(vs);
switch(err)
{
case ERR_MEM: // -1
DEBUG_ERROR("ERR_MEM->ENOMEM, Out of memory error.");
errno = ENOMEM;
break;
case ERR_BUF: // -2
DEBUG_ERROR("ERR_BUF->ENOBUFS, Buffer error.");
errno = ENOBUFS;
break;
case ERR_TIMEOUT: // -3
DEBUG_ERROR("ERR_TIMEOUT->ETIMEDOUT, Timeout.");
errno = ETIMEDOUT;
break;
case ERR_RTE: // -4
DEBUG_ERROR("ERR_RTE->ENETUNREACH, Routing problem.");
errno = ENETUNREACH;
break;
case ERR_INPROGRESS: // -5
DEBUG_ERROR("ERR_INPROGRESS->EINPROGRESS, Operation in progress.");
errno = EINPROGRESS;
break;
case ERR_VAL: // -6
DEBUG_ERROR("ERR_VAL->EINVAL, Illegal value.");
errno = EINVAL;
break;
case ERR_WOULDBLOCK: // -7
DEBUG_ERROR("ERR_WOULDBLOCK->EWOULDBLOCK, Operation would block.");
errno = EWOULDBLOCK;
break;
case ERR_USE: // -8
DEBUG_ERROR("ERR_USE->EADDRINUSE, Address in use.");
errno = EADDRINUSE;
break;
case ERR_ALREADY: // -9 ?
DEBUG_ERROR("ERR_ALREADY->EISCONN, Already connecting.");
errno = EISCONN;
break;
case ERR_ISCONN: // -10
DEBUG_ERROR("ERR_ISCONN->EISCONN, Already connected");
errno = EISCONN;
break;
case ERR_CONN: // -11 ?
DEBUG_ERROR("ERR_CONN->EISCONN, Not connected");
errno = EISCONN;
break;
case ERR_IF: // -12
DEBUG_ERROR("ERR_IF, Low-level netif error.");
errno = -1;
break;
case ERR_ABRT: // -13
DEBUG_ERROR("ERR_ABRT, Connection aborted.");
errno = -1;
break;
case ERR_RST: // -14
DEBUG_ERROR("ERR_RST, Connection reset.");
errno = -1;
break;
case ERR_CLSD: // -15
DEBUG_ERROR("ERR_CLSD, Connection closed.");
errno = -1;
break;
case ERR_ARG: // -16
DEBUG_ERROR("ERR_ARG, Illegal argument.");
errno = -1;
break;
default:
break;
}
errno = lwip_err_to_errno(err);
}
}

View File

@@ -50,6 +50,34 @@
struct tcp_pcb;
struct netif;
/** Table to quickly map an lwIP error (err_t) to a socket error
* by using -err as an index */
static const int lwip_err_to_errno_table[] = {
0, /* ERR_OK 0 No error, everything OK. */
ENOMEM, /* ERR_MEM -1 Out of memory error. */
ENOBUFS, /* ERR_BUF -2 Buffer error. */
EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */
EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */
EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
EINVAL, /* ERR_VAL -6 Illegal value. */
EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
EADDRINUSE, /* ERR_USE -8 Address in use. */
EALREADY, /* ERR_ALREADY -9 Already connecting. */
EISCONN, /* ERR_ISCONN -10 Conn already established.*/
ENOTCONN, /* ERR_CONN -11 Not connected. */
-1, /* ERR_IF -12 Low-level netif error */
ECONNABORTED, /* ERR_ABRT -13 Connection aborted. */
ECONNRESET, /* ERR_RST -14 Connection reset. */
ENOTCONN, /* ERR_CLSD -15 Connection closed. */
EIO /* ERR_ARG -16 Illegal argument. */
};
#define ERR_TO_ERRNO_TABLE_SIZE LWIP_ARRAYSIZE(lwip_err_to_errno_table)
#define lwip_err_to_errno(err) \
((unsigned)(-(signed)(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \
lwip_err_to_errno_table[-(signed)(err)] : EIO)
#if defined(LIBZT_IPV4)
//#define LWIP_NETIF_ADD_SIG struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input
#define LWIP_ETHARP_OUTPUT_SIG struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr
@@ -173,6 +201,21 @@ namespace ZeroTier {
*/
void lwip_init_interface(VirtualTap *tap, const InetAddress &ip);
/*
* Returns the number of TCP PCBs currently allocated
*/
static int lwip_num_current_tcp_pcbs();
/*
* Returns the number of UDP PCBs currently allocated
*/
static int lwip_num_current_udp_pcbs();
/*
* Returns the number of RAW PCBs currently allocated
*/
static int lwip_num_current_raw_pcbs();
/*
* Registers a DNS nameserver with the network stack
*/

View File

@@ -744,7 +744,7 @@ namespace ZeroTier {
int picoTCP::pico_Socket(struct pico_socket **p, int socket_family, int socket_type, int protocol)
{
int err = 0;
if(!can_provision_new_socket()) {
if(!can_provision_new_socket(socket_type)) {
DEBUG_ERROR("cannot create additional socket, see PICO_MAX_TIMERS. current=%d", pico_ntimers());
errno = EMFILE;
err = -1;

View File

@@ -742,7 +742,7 @@ void udp_server_6(UDP_UNIT_TEST_SIG_6)
struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&saddr;
int serverlen = sizeof(saddr);
memset(&saddr, 0, sizeof(saddr));
if((r = RECVFROM(fd, rbuf, len, 0, (struct sockaddr *)&saddr, (socklen_t *)&serverlen)) < 0) {
if((r = RECVFROM(fd, rbuf, len, 0, (struct sockaddr *)&saddr, (socklen_t *)&serverlen)) < 0) {
perror("recvfrom");
*passed = false;
return;