tcp_received() fix
This commit is contained in:
177
src/lwIP.cpp
177
src/lwIP.cpp
@@ -57,13 +57,11 @@ err_t lwip_eth_tx(struct netif *netif, struct pbuf *p)
|
||||
|
||||
ZeroTier::VirtualTap *tap = (ZeroTier::VirtualTap*)netif->state;
|
||||
bufptr = buf;
|
||||
// Copy data from each pbuf, one at a time
|
||||
for(q = p; q != NULL; q = q->next) {
|
||||
memcpy(bufptr, q->payload, q->len);
|
||||
bufptr += q->len;
|
||||
totalLength += q->len;
|
||||
}
|
||||
// Split ethernet header and feed into handler
|
||||
struct eth_hdr *ethhdr;
|
||||
ethhdr = (struct eth_hdr *)buf;
|
||||
|
||||
@@ -290,6 +288,7 @@ namespace ZeroTier
|
||||
if(socket_type == SOCK_STREAM) {
|
||||
struct tcp_pcb *new_tcp_PCB = tcp_new();
|
||||
*pcb = new_tcp_PCB;
|
||||
tcp_nagle_disable(new_tcp_PCB);
|
||||
return ERR_OK;
|
||||
}
|
||||
if(socket_type == SOCK_DGRAM) {
|
||||
@@ -455,7 +454,7 @@ namespace ZeroTier
|
||||
|
||||
int lwIP::lwip_Listen(VirtualSocket *vs, int backlog)
|
||||
{
|
||||
DEBUG_INFO("vs=%p", vs);
|
||||
//DEBUG_INFO("vs=%p", vs);
|
||||
struct tcp_pcb* listeningPCB;
|
||||
#ifdef TCP_LISTEN_BACKLOG
|
||||
listeningPCB = tcp_listen_with_backlog((struct tcp_pcb*)vs->pcb, backlog);
|
||||
@@ -466,19 +465,20 @@ namespace ZeroTier
|
||||
vs->pcb = listeningPCB;
|
||||
tcp_accept(listeningPCB, lwip_cb_accept); // set callback
|
||||
tcp_arg(listeningPCB, vs);
|
||||
//fcntl(tap->_phy.getDescriptor(vs->sock), F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
VirtualSocket* lwIP::lwip_Accept(VirtualSocket *vs)
|
||||
{
|
||||
//DEBUG_INFO();
|
||||
if(!vs) {
|
||||
DEBUG_ERROR("invalid virtual socket");
|
||||
handle_general_failure();
|
||||
return NULL;
|
||||
}
|
||||
// Retreive first of queued VirtualSockets from parent VirtualSocket
|
||||
// TODO: check multithreaded behaviour
|
||||
VirtualSocket *new_vs = NULL;
|
||||
if(vs->_AcceptedConnections.size()) {
|
||||
new_vs = vs->_AcceptedConnections.front();
|
||||
@@ -538,7 +538,6 @@ namespace ZeroTier
|
||||
}
|
||||
if(vs->socket_type == SOCK_DGRAM)
|
||||
{
|
||||
//DEBUG_ERROR("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);
|
||||
@@ -564,7 +563,6 @@ namespace ZeroTier
|
||||
}
|
||||
if(vs->socket_type == SOCK_STREAM)
|
||||
{
|
||||
DEBUG_ERROR("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;
|
||||
@@ -572,15 +570,16 @@ namespace ZeroTier
|
||||
// PCB send buffer is full, turn off readability notifications for the
|
||||
// corresponding PhySocket until lwip_cb_sent() is called and confirms that there is
|
||||
// now space on the buffer
|
||||
DEBUG_ERROR("lwIP stack is full, sndbuf == 0");
|
||||
vs->tap->_phy.setNotifyReadable(vs->sock, false);
|
||||
DEBUG_ERROR("lwIP stack is full, sndbuf==0");
|
||||
//vs->tap->_phy.setNotifyReadable(vs->sock, false);
|
||||
return -1;
|
||||
}
|
||||
int buf_w = vs->TXbuf->write((const unsigned char*)data, len);
|
||||
if (buf_w != len) {
|
||||
// because we checked ZT_TCP_TX_BUF_SZ above, this should not happen
|
||||
DEBUG_ERROR("TX wrote only %d but expected to write %d", buf_w, len);
|
||||
exit(0);
|
||||
handle_general_failure();
|
||||
return ZT_ERR_GENERAL_FAILURE;
|
||||
}
|
||||
if(vs->TXbuf->count() <= 0) {
|
||||
return -1; // nothing to write
|
||||
@@ -609,49 +608,61 @@ namespace ZeroTier
|
||||
|
||||
int lwIP::lwip_Close(VirtualSocket *vs)
|
||||
{
|
||||
//DEBUG_INFO();
|
||||
int err = 0;
|
||||
errno = 0;
|
||||
if(vs->socket_type == SOCK_DGRAM) {
|
||||
udp_remove((struct udp_pcb*)vs->pcb);
|
||||
}
|
||||
// FIXME: check if already closed? vs->TCP_pcb->state != CLOSED
|
||||
if(vs->pcb) {
|
||||
//DEBUG_EXTRA("vs=%p, sock=%p, PCB->state = %d",
|
||||
// conn, sock, vs->TCP_pcb->state);
|
||||
if(((struct tcp_pcb*)vs->pcb)->state == SYN_SENT /*|| vs->TCP_pcb->state == CLOSE_WAIT*/) {
|
||||
DEBUG_EXTRA("ignoring close request. invalid PCB state for this operation. sock=%p", vs->sock);
|
||||
// TODO: errno = ?;
|
||||
return -1;
|
||||
}
|
||||
struct tcp_pcb* tpcb = (struct tcp_pcb*)vs->pcb;
|
||||
if(tcp_close(tpcb) == ERR_OK) {
|
||||
// Unregister callbacks for this PCB
|
||||
tcp_arg(tpcb, NULL);
|
||||
// unregister callbacks for this PCB
|
||||
tcp_arg(tpcb, NULL);
|
||||
tcp_recv(tpcb, NULL);
|
||||
tcp_err(tpcb, NULL);
|
||||
tcp_err(tpcb, NULL);
|
||||
tcp_sent(tpcb, NULL);
|
||||
tcp_poll(tpcb, NULL, 1);
|
||||
}
|
||||
else {
|
||||
DEBUG_EXTRA("error while calling tcp_close() sock=%p", vs->sock);
|
||||
err = -1;
|
||||
// TODO: set errno
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* Callbacks from lwIP stack */
|
||||
/****************************************************************************/
|
||||
|
||||
// write data from processed packets from the stack to the client app
|
||||
/*
|
||||
With the raw API, tcp_recv() sets up to receive data via a callback function. Your callback
|
||||
is delivered chains of pbufs as they become available. You have to manage extracting data
|
||||
from the pbuf chain, and don't forget to watch out for multiple pbufs in a single callback:
|
||||
the 'tot_len' field indicates the total length of data in the pbuf chain. You must call
|
||||
tcp_recved() to tell LWIP when you have processed the received data. As with the netconn API,
|
||||
you may receive more or less data than you want, and will have to either wait for further
|
||||
callbacks, or hold onto excess data for later processing.
|
||||
|
||||
http://lwip.wikia.com/wiki/Receiving_data_with_LWIP
|
||||
*/
|
||||
err_t lwIP::lwip_cb_tcp_recved(void *arg, struct tcp_pcb *PCB, struct pbuf *p, err_t err)
|
||||
{
|
||||
DEBUG_INFO();
|
||||
//DEBUG_INFO();
|
||||
VirtualSocket *vs = (VirtualSocket *)arg;
|
||||
int tot = 0;
|
||||
|
||||
if(!vs) {
|
||||
DEBUG_ERROR("no virtual socket");
|
||||
return ERR_OK; // FIXME: Determine if this is correct behaviour expected by the stack
|
||||
}
|
||||
|
||||
struct pbuf* q = p;
|
||||
if(p == NULL) {
|
||||
/*
|
||||
@@ -659,13 +670,10 @@ namespace ZeroTier
|
||||
// FIXME: Implement?
|
||||
}
|
||||
*/
|
||||
DEBUG_INFO("p == NULL");
|
||||
return ERR_ABRT;
|
||||
return ERR_ABRT; // close connection
|
||||
}
|
||||
|
||||
vs->tap->_tcpconns_m.lock();
|
||||
vs->_rx_m.lock();
|
||||
|
||||
// cycle through pbufs and write them to the RX buffer
|
||||
while(p != NULL) {
|
||||
if(p->len <= 0)
|
||||
@@ -675,38 +683,51 @@ namespace ZeroTier
|
||||
if(avail < len) {
|
||||
DEBUG_ERROR("not enough room (%d bytes) on RX buffer", avail);
|
||||
}
|
||||
// get it on the buffer, fast!
|
||||
// place new incoming data on ringbuffer before we try to send it to the app
|
||||
memcpy(vs->RXbuf->get_buf(), p->payload, len);
|
||||
vs->RXbuf->produce(len);
|
||||
p = p->next;
|
||||
tot += len;
|
||||
}
|
||||
DEBUG_INFO("tot=%d", tot);
|
||||
|
||||
if(tot) {
|
||||
tcp_recved(PCB, tot);
|
||||
DEBUG_TRANS("len=%5d buf_len=%13d [NSLWIP --> VSRXBF]", tot, vs->RXbuf->count());
|
||||
int w, write_attempt_sz = vs->RXbuf->count() < ZT_MAX_MTU ? vs->RXbuf->count() : ZT_MAX_MTU;
|
||||
if((w = write(vs->sdk_fd, vs->RXbuf->get_buf(), write_attempt_sz)) < 0) {
|
||||
perror("write");
|
||||
DEBUG_ERROR("write(fd=%d)=%d, errno=%d", vs->sdk_fd, w, errno);
|
||||
}
|
||||
if(w > 0) {
|
||||
DEBUG_INFO("write_attempt_sz=%d, w=%d", write_attempt_sz, w);
|
||||
vs->RXbuf->consume(w);
|
||||
if(w < write_attempt_sz) {
|
||||
DEBUG_TRANS("len=%5d buf_len=%13d [VSRXBF --> APPFDS]", w, vs->RXbuf->count());
|
||||
DEBUG_ERROR("warning, intended to write %d bytes", write_attempt_sz);
|
||||
}
|
||||
else {
|
||||
DEBUG_TRANS("len=%5d buf_len=%13d [VSRXBF --> APPFDS]", w, vs->RXbuf->count());
|
||||
}
|
||||
}
|
||||
|
||||
//vs->tap->_phy.setNotifyWritable(vs->sock, true);
|
||||
//vs->tap->phyOnUnixWritable(vs->sock, NULL, true); // to app
|
||||
}
|
||||
|
||||
else {
|
||||
DEBUG_EXTRA("warning, wrote 0 bytes");
|
||||
}
|
||||
vs->tap->_tcpconns_m.unlock();
|
||||
vs->_rx_m.unlock();
|
||||
|
||||
pbuf_free(q);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
NSLWIP network_stack_lwip
|
||||
NSPICO network_stack_pico
|
||||
NSRXBF network_stack_pico guarded frame buffer RX
|
||||
ZTVIRT zt_virtual_wire
|
||||
APPFDS app_fd
|
||||
VSRXBF app_fd TX buf
|
||||
VSTXBF app_fd RX buf
|
||||
*/
|
||||
err_t lwIP::lwip_cb_accept(void *arg, struct tcp_pcb *newPCB, err_t err)
|
||||
{
|
||||
//DEBUG_INFO();
|
||||
VirtualSocket *vs = (VirtualSocket*)arg;
|
||||
struct sockaddr_storage ss;
|
||||
#if defined(LIBZT_IPV4)
|
||||
@@ -742,7 +763,7 @@ namespace ZeroTier
|
||||
// copy processed datagram to app socket
|
||||
void lwIP::lwip_cb_udp_recved(void * arg, struct udp_pcb * upcb, struct pbuf * p, const ip_addr_t * addr, u16_t port)
|
||||
{
|
||||
DEBUG_EXTRA("arg(vs)=%p, pcb=%p, port=%d)", arg, upcb, port);
|
||||
//DEBUG_EXTRA("arg(vs)=%p, pcb=%p, port=%d)", arg, upcb, port);
|
||||
VirtualSocket *vs = (VirtualSocket *)arg;
|
||||
if(!vs) {
|
||||
DEBUG_ERROR("invalid virtual socket");
|
||||
@@ -799,9 +820,11 @@ namespace ZeroTier
|
||||
pbuf_free(q);
|
||||
}
|
||||
|
||||
// callback from stack to notify driver that data was sent
|
||||
err_t lwIP::lwip_cb_sent(void* arg, struct tcp_pcb *PCB, u16_t len)
|
||||
{
|
||||
DEBUG_EXTRA("pcb=%p", PCB);
|
||||
//DEBUG_EXTRA("pcb=%p", PCB);
|
||||
/*
|
||||
VirtualSocket *vs = (VirtualSocket *)arg;
|
||||
Mutex::Lock _l(vs->tap->_tcpconns_m);
|
||||
if(vs && len) {
|
||||
@@ -811,6 +834,7 @@ namespace ZeroTier
|
||||
vs->tap->_phy.whack();
|
||||
}
|
||||
}
|
||||
*/
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
@@ -837,76 +861,79 @@ namespace ZeroTier
|
||||
|
||||
void lwIP::lwip_cb_err(void *arg, err_t err)
|
||||
{
|
||||
DEBUG_ERROR("err=%d", err);
|
||||
VirtualSocket *vs = (VirtualSocket *)arg;
|
||||
if(!vs){
|
||||
DEBUG_ERROR("vs==NULL");
|
||||
DEBUG_ERROR("err=%d, invalid virtual socket", err);
|
||||
errno = -1; // FIXME: Find more appropriate value
|
||||
}
|
||||
Mutex::Lock _l(vs->tap->_tcpconns_m);
|
||||
int fd = vs->tap->_phy.getDescriptor(vs->sock);
|
||||
DEBUG_ERROR("vs=%p, pcb=%p, fd=%d, err=%d", vs, vs->pcb, fd, err);
|
||||
DEBUG_ERROR("closing virtual socket");
|
||||
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:
|
||||
DEBUG_ERROR("ERR_MEM->ENOMEM");
|
||||
case ERR_MEM: // -1
|
||||
DEBUG_ERROR("ERR_MEM->ENOMEM, Out of memory error.");
|
||||
errno = ENOMEM;
|
||||
break;
|
||||
case ERR_BUF:
|
||||
DEBUG_ERROR("ERR_BUF->ENOBUFS");
|
||||
case ERR_BUF: // -2
|
||||
DEBUG_ERROR("ERR_BUF->ENOBUFS, Buffer error.");
|
||||
errno = ENOBUFS;
|
||||
break;
|
||||
case ERR_TIMEOUT:
|
||||
DEBUG_ERROR("ERR_TIMEOUT->ETIMEDOUT");
|
||||
case ERR_TIMEOUT: // -3
|
||||
DEBUG_ERROR("ERR_TIMEOUT->ETIMEDOUT, Timeout.");
|
||||
errno = ETIMEDOUT;
|
||||
break;
|
||||
case ERR_RTE:
|
||||
DEBUG_ERROR("ERR_RTE->ENETUNREACH");
|
||||
case ERR_RTE: // -4
|
||||
DEBUG_ERROR("ERR_RTE->ENETUNREACH, Routing problem.");
|
||||
errno = ENETUNREACH;
|
||||
break;
|
||||
case ERR_INPROGRESS:
|
||||
DEBUG_ERROR("ERR_INPROGRESS->EINPROGRESS");
|
||||
case ERR_INPROGRESS: // -5
|
||||
DEBUG_ERROR("ERR_INPROGRESS->EINPROGRESS, Operation in progress.");
|
||||
errno = EINPROGRESS;
|
||||
break;
|
||||
case ERR_VAL:
|
||||
DEBUG_ERROR("ERR_VAL->EINVAL");
|
||||
case ERR_VAL: // -6
|
||||
DEBUG_ERROR("ERR_VAL->EINVAL, Illegal value.");
|
||||
errno = EINVAL;
|
||||
break;
|
||||
case ERR_WOULDBLOCK:
|
||||
DEBUG_ERROR("ERR_WOULDBLOCK->EWOULDBLOCK");
|
||||
case ERR_WOULDBLOCK: // -7
|
||||
DEBUG_ERROR("ERR_WOULDBLOCK->EWOULDBLOCK, Operation would block.");
|
||||
errno = EWOULDBLOCK;
|
||||
break;
|
||||
case ERR_USE:
|
||||
DEBUG_ERROR("ERR_USE->EADDRINUSE");
|
||||
case ERR_USE: // -8
|
||||
DEBUG_ERROR("ERR_USE->EADDRINUSE, Address in use.");
|
||||
errno = EADDRINUSE;
|
||||
break;
|
||||
case ERR_ISCONN:
|
||||
DEBUG_ERROR("ERR_ISvs->EISCONN");
|
||||
case ERR_ALREADY: // -9 ?
|
||||
DEBUG_ERROR("ERR_ALREADY->EISCONN, Already connecting.");
|
||||
errno = EISCONN;
|
||||
break;
|
||||
case ERR_ABRT:
|
||||
DEBUG_ERROR("ERR_ABRT->ECONNREFUSED");
|
||||
errno = ECONNREFUSED;
|
||||
case ERR_ISCONN: // -10
|
||||
DEBUG_ERROR("ERR_ISCONN->EISCONN, Already connected");
|
||||
errno = EISCONN;
|
||||
break;
|
||||
|
||||
// TODO: Below are errors which don't have a standard errno correlate
|
||||
|
||||
case ERR_RST:
|
||||
// -1
|
||||
case ERR_CONN: // -11 ?
|
||||
DEBUG_ERROR("ERR_CONN->EISCONN, Not connected");
|
||||
errno = EISCONN;
|
||||
break;
|
||||
case ERR_CLSD:
|
||||
// -1
|
||||
case ERR_IF: // -12
|
||||
DEBUG_ERROR("ERR_IF, Low-level netif error.");
|
||||
errno = -1;
|
||||
break;
|
||||
case ERR_CONN:
|
||||
// -1
|
||||
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_ARG:
|
||||
// -1
|
||||
case ERR_CLSD: // -15
|
||||
DEBUG_ERROR("ERR_CLSD, Connection closed.");
|
||||
errno = -1;
|
||||
break;
|
||||
case ERR_IF:
|
||||
// -1
|
||||
case ERR_ARG: // -16
|
||||
DEBUG_ERROR("ERR_ARG, Illegal argument.");
|
||||
errno = -1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user