This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
zhangyang-libzt/src/lwIP.cpp

792 lines
23 KiB
C++
Raw Normal View History

/*
* ZeroTier SDK - Network Virtualization Everywhere
* Copyright (C) 2011-2017 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.
*/
// lwIP network stack driver
#include <algorithm>
#include "libzt.h"
#include "SocketTap.hpp"
#include "Utilities.hpp"
#include "lwIP.hpp"
#include "netif/ethernet.h"
#include "lwip/etharp.h"
#if defined(LIBZT_IPV6)
#include "lwip/ethip6.h"
#include "lwip/nd6.h"
#endif
void nd6_tmr(void);
err_t tapif_init(struct netif *netif)
{
DEBUG_INFO();
return ERR_OK;
}
err_t low_level_output(struct netif *netif, struct pbuf *p)
{
DEBUG_INFO();
struct pbuf *q;
char buf[ZT_MAX_MTU+32];
char *bufptr;
int totalLength = 0;
ZeroTier::SocketTap *tap = (ZeroTier::SocketTap*)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;
ZeroTier::MAC src_mac;
ZeroTier::MAC dest_mac;
src_mac.setTo(ethhdr->src.addr, 6);
dest_mac.setTo(ethhdr->dest.addr, 6);
tap->_handler(tap->_arg,NULL,tap->_nwid,src_mac,dest_mac,
ZeroTier::Utils::ntoh((uint16_t)ethhdr->type),0,buf + sizeof(struct eth_hdr),totalLength - sizeof(struct eth_hdr));
return ERR_OK;
}
namespace ZeroTier
{
void lwIP::lwip_init_interface(SocketTap *tap, const InetAddress &ip)
{
DEBUG_INFO();
Mutex::Lock _l(tap->_ips_m);
if (std::find(tap->_ips.begin(),tap->_ips.end(),ip) == tap->_ips.end()) {
tap->_ips.push_back(ip);
std::sort(tap->_ips.begin(),tap->_ips.end());
char ipbuf[64], nmbuf[64];
#if defined(LIBZT_IPV4)
if (ip.isV4()) {
// Set IP
static ip_addr_t ipaddr, netmask, gw;
IP4_ADDR(&gw,127,0,0,1);
ipaddr.addr = *((u32_t *)ip.rawIpData());
netmask.addr = *((u32_t *)ip.netmask().rawIpData());
netif_add(&(tap->lwipdev),&ipaddr, &netmask, &gw, NULL, tapif_init, ethernet_input);
tap->lwipdev.state = tap;
tap->lwipdev.output = etharp_output;
tap->_mac.copyTo(tap->lwipdev.hwaddr, 6);
tap->lwipdev.mtu = tap->_mtu;
tap->lwipdev.name[0] = 'l';
tap->lwipdev.name[1] = '4';
tap->lwipdev.linkoutput = low_level_output;
tap->lwipdev.hwaddr_len = 6;
tap->lwipdev.flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP;
netif_set_default(&(tap->lwipdev));
netif_set_up(&(tap->lwipdev));
DEBUG_INFO("addr=%s, netmask=%s", ip.toString(ipbuf), ip.netmask().toString(nmbuf));
}
#endif
#if defined(LIBZT_IPV6)
if(ip.isV6()) {
static ip6_addr_t addr6;
struct sockaddr_in6 in6;
memcpy(in6.sin6_addr.s6_addr,ip.rawIpData(),16);
in6_to_ip6((ip6_addr *)&addr6, &in6);
tap->lwipdev6.mtu = tap->_mtu;
tap->lwipdev6.name[0] = 'l';
tap->lwipdev6.name[1] = '6';
tap->lwipdev6.hwaddr_len = 6;
tap->lwipdev6.linkoutput = low_level_output;
tap->lwipdev6.ip6_autoconfig_enabled = 1;
tap->_mac.copyTo(tap->lwipdev6.hwaddr, tap->lwipdev6.hwaddr_len);
netif_create_ip6_linklocal_address(&(tap->lwipdev6), 1);
netif_add(&(tap->lwipdev6), NULL, tapif_init, ethernet_input);
netif_set_default(&(tap->lwipdev6));
netif_set_up(&(tap->lwipdev6));
netif_ip6_addr_set_state(&(tap->lwipdev6), 1, IP6_ADDR_TENTATIVE);
ip6_addr_copy(ip_2_ip6(tap->lwipdev6.ip6_addr[1]), addr6);
tap->lwipdev6.output_ip6 = ethip6_output;
tap->lwipdev6.state = tap;
tap->lwipdev6.flags = NETIF_FLAG_LINK_UP | NETIF_FLAG_UP;
DEBUG_INFO("addr=%s, netmask=%s", ip.toString(ipbuf), ip.netmask().toString(nmbuf));
}
#endif
}
}
void lwIP::lwip_loop(SocketTap *tap)
{
DEBUG_INFO();
uint64_t prev_tcp_time = 0, prev_discovery_time = 0;
while(tap->_run)
{
uint64_t now = OSUtils::now();
uint64_t since_tcp = now - prev_tcp_time;
uint64_t since_discovery = now - prev_discovery_time;
uint64_t tcp_remaining = LWIP_TCP_TIMER_INTERVAL;
uint64_t discovery_remaining = 5000;
#if defined(LIBZT_IPV6)
#define DISCOVERY_INTERVAL 1000
#elif defined(LIBZT_IPV4)
#define DISCOVERY_INTERVAL ARP_TMR_INTERVAL
#endif
// Main TCP/ETHARP timer section
if (since_tcp >= LWIP_TCP_TIMER_INTERVAL) {
prev_tcp_time = now;
tcp_tmr();
}
else {
tcp_remaining = LWIP_TCP_TIMER_INTERVAL - since_tcp;
}
if (since_discovery >= DISCOVERY_INTERVAL) {
prev_discovery_time = now;
#if defined(LIBZT_IPV4)
etharp_tmr();
#endif
#if defined(LIBZT_IPV6)
nd6_tmr();
#endif
} else {
discovery_remaining = DISCOVERY_INTERVAL - since_discovery;
}
tap->_phy.poll((unsigned long)std::min(tcp_remaining,discovery_remaining));
tap->Housekeeping();
}
}
void lwIP::lwip_rx(SocketTap *tap, const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
DEBUG_INFO("etherType=%x, len=%d", etherType, len);
struct pbuf *p,*q;
if (!tap->_enabled)
return;
struct eth_hdr ethhdr;
from.copyTo(ethhdr.src.addr, 6);
to.copyTo(ethhdr.dest.addr, 6);
ethhdr.type = ZeroTier::Utils::hton((uint16_t)etherType);
p = pbuf_alloc(PBUF_RAW, len+sizeof(struct eth_hdr), PBUF_POOL);
if (p != NULL) {
const char *dataptr = reinterpret_cast<const char *>(data);
// First pbuf gets ethernet header at start
q = p;
if (q->len < sizeof(ethhdr)) {
DEBUG_ERROR("dropped packet: first pbuf smaller than ethernet header");
return;
}
memcpy(q->payload,&ethhdr,sizeof(ethhdr));
memcpy((char*)q->payload + sizeof(ethhdr),dataptr,q->len - sizeof(ethhdr));
dataptr += q->len - sizeof(ethhdr);
// Remaining pbufs (if any) get rest of data
while ((q = q->next)) {
memcpy(q->payload,dataptr,q->len);
dataptr += q->len;
}
}
else {
DEBUG_ERROR("dropped packet: no pbufs available");
return;
}
{
#if defined(LIBZT_IPV6)
if(tap->lwipdev6.input(p, &(tap->lwipdev6)) != ERR_OK) {
DEBUG_ERROR("error while feeding frame into stack lwipdev6");
}
#endif
#if defined(LIBZT_IPV4)
if(tap->lwipdev.input(p, &(tap->lwipdev)) != ERR_OK) {
DEBUG_ERROR("error while feeding frame into stack lwipdev");
}
#endif
}
}
int lwIP::lwip_Socket(void **pcb, int socket_family, int socket_type, int protocol)
{
DEBUG_INFO();
if(!can_provision_new_socket()) {
DEBUG_ERROR("unable to create new socket due to limitation of network stack");
return -1;
}
if(socket_type == SOCK_STREAM) {
struct tcp_pcb *new_tcp_PCB = tcp_new();
*pcb = new_tcp_PCB;
return ERR_OK;
}
if(socket_type == SOCK_DGRAM) {
struct udp_pcb *new_udp_PCB = udp_new();
*pcb = new_udp_PCB;
return ERR_OK;
}
return -1;
}
int lwIP::lwip_Connect(Connection *conn, const struct sockaddr *addr, socklen_t addrlen)
{
DEBUG_INFO();
ip_addr_t ba;
char addrstr[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) {
inet_ntop(AF_INET, &(in4->sin_addr), addrstr, INET_ADDRSTRLEN);
DEBUG_INFO("%s:%d", addrstr, lwip_ntohs(in4->sin_port));
}
ba = convert_ip(in4);
port = lwip_ntohs(in4->sin_port);
#endif
#if defined(LIBZT_IPV6)
struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&addr;
in6_to_ip6((ip6_addr *)&ba, in6);
if(addr->sa_family == AF_INET6) {
struct sockaddr_in6 *connaddr6 = (struct sockaddr_in6 *)addr;
inet_ntop(AF_INET6, &(connaddr6->sin6_addr), addrstr, INET6_ADDRSTRLEN);
DEBUG_INFO("%s:%d", addrstr, lwip_ntohs(connaddr6->sin6_port));
}
#endif
DEBUG_INFO("addr=%s", addrstr);
if(conn->socket_type == SOCK_DGRAM) {
// Generates no network traffic
if((err = udp_connect((struct udp_pcb*)conn->pcb,(ip_addr_t *)&ba,port)) < 0) {
DEBUG_ERROR("error while connecting to with UDP");
}
udp_recv((struct udp_pcb*)conn->pcb, nc_udp_recved, conn);
return ERR_OK;
}
if(conn->socket_type == SOCK_STREAM) {
struct tcp_pcb *tpcb = (struct tcp_pcb*)conn->pcb;
tcp_sent(tpcb, nc_sent);
tcp_recv(tpcb, nc_recved);
tcp_err(tpcb, nc_err);
tcp_poll(tpcb, nc_poll, LWIP_APPLICATION_POLL_FREQ);
tcp_arg(tpcb, conn);
//DEBUG_EXTRA(" pcb->state=%x", conn->TCP_pcb->state);
//if(conn->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,nc_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;
}
// 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 nc_err() callbacks!
DEBUG_ERROR("unable to connect");
errno = EAGAIN;
return -1;
}
}
return err;
}
int lwIP::lwip_Bind(SocketTap *tap, Connection *conn, const struct sockaddr *addr, socklen_t addrlen)
{
DEBUG_INFO();
ip_addr_t ba;
char addrstr[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) {
inet_ntop(AF_INET, &(in4->sin_addr), addrstr, INET_ADDRSTRLEN);
DEBUG_INFO("%s:%d", addrstr, lwip_ntohs(in4->sin_port));
}
ba = convert_ip(in4);
port = lwip_ntohs(in4->sin_port);
#endif
#if defined(LIBZT_IPV6)
struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&addr;
in6_to_ip6((ip6_addr *)&ba, in6);
if(addr->sa_family == AF_INET6) {
struct sockaddr_in6 *connaddr6 = (struct sockaddr_in6 *)addr;
inet_ntop(AF_INET6, &(connaddr6->sin6_addr), addrstr, INET6_ADDRSTRLEN);
DEBUG_INFO("%s:%d", addrstr, lwip_ntohs(connaddr6->sin6_port));
}
#endif
if(conn->socket_type == SOCK_DGRAM) {
err = udp_bind((struct udp_pcb*)conn->pcb, (const ip_addr_t *)&ba, port);
if(err == ERR_USE) {
err = -1;
errno = EADDRINUSE; // port in use
}
else {
// set the recv callback
udp_recv((struct udp_pcb*)conn->pcb, nc_udp_recved, new ConnectionPair(tap, conn));
err = ERR_OK;
errno = ERR_OK; // success
}
}
else if (conn->socket_type == SOCK_STREAM) {
err = tcp_bind((struct tcp_pcb*)conn->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;
}
}
else {
err = ERR_OK;
errno = ERR_OK; // success
}
}
return err;
}
int lwIP::lwip_Listen(Connection *conn, int backlog)
{
DEBUG_INFO("conn=%p", conn);
struct tcp_pcb* listeningPCB;
#ifdef TCP_LISTEN_BACKLOG
listeningPCB = tcp_listen_with_backlog((struct tcp_pcb*)conn->pcb, backlog);
#else
listeningPCB = tcp_listen((struct tcp_pcb*)conn->pcb);
#endif
if(listeningPCB != NULL) {
conn->pcb = listeningPCB;
tcp_accept(listeningPCB, nc_accept); // set callback
tcp_arg(listeningPCB, conn);
//fcntl(tap->_phy.getDescriptor(conn->sock), F_SETFL, O_NONBLOCK);
}
return 0;
}
Connection* lwIP::lwip_Accept(Connection *conn)
{
if(!conn) {
DEBUG_ERROR("invalid conn");
handle_general_failure();
return NULL;
}
// Retreive first of queued Connections from parent connection
Connection *new_conn = NULL;
Mutex::Lock _l(conn->tap->_tcpconns_m);
if(conn->_AcceptedConnections.size()) {
new_conn = conn->_AcceptedConnections.front();
conn->_AcceptedConnections.pop();
}
return new_conn;
}
int lwIP::lwip_Read(Connection *conn, bool lwip_invoked)
{
DEBUG_EXTRA("conn=%p", conn);
int err = 0;
if(!conn) {
DEBUG_ERROR("no connection");
return -1;
}
if(!lwip_invoked) {
DEBUG_INFO("!lwip_invoked");
conn->tap->_tcpconns_m.lock();
conn->_rx_m.lock();
}
if(conn->RXbuf->count()) {
int max = conn->socket_type == SOCK_STREAM ? ZT_STACK_TCP_SOCKET_RX_SZ : ZT_STACK_TCP_SOCKET_RX_SZ;
int wr = std::min((ssize_t)max, (ssize_t)conn->RXbuf->count());
int n = conn->tap->_phy.streamSend(conn->sock, conn->RXbuf->get_buf(), wr);
char str[22];
memcpy(str, conn->RXbuf->get_buf(), 22);
conn->RXbuf->consume(n);
if(conn->socket_type == SOCK_DGRAM)
{
// TODO
}
if(conn->socket_type == SOCK_STREAM) { // Only acknolwedge receipt of TCP packets
tcp_recved((struct tcp_pcb*)conn->pcb, n);
DEBUG_TRANS("TCP RX %d bytes", n);
}
}
if(conn->RXbuf->count() == 0) {
DEBUG_INFO("wrote everything");
conn->tap->_phy.setNotifyWritable(conn->sock, false); // nothing else to send to the app
}
if(!lwip_invoked) {
conn->tap->_tcpconns_m.unlock();
conn->_rx_m.unlock();
}
return err;
}
int lwIP::lwip_Write(Connection *conn, void *data, ssize_t len)
{
DEBUG_EXTRA("conn=%p, len=%d", (void*)&conn, len);
int err = 0;
if(!conn) {
DEBUG_ERROR("no connection");
return -1;
}
if(conn->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((ssize_t)conn->TXbuf->count(), (ssize_t)ZT_MAX_MTU);
DEBUG_EXTRA("allocating pbuf chain of size=%d for UDP packet, txsz=%d", udp_trans_len, conn->TXbuf->count());
struct pbuf * pb = pbuf_alloc(PBUF_TRANSPORT, udp_trans_len, PBUF_POOL);
if(!pb){
DEBUG_ERROR("unable to allocate new pbuf of size=%d", conn->TXbuf->count());
return -1;
}
memcpy(pb->payload, conn->TXbuf->get_buf(), udp_trans_len);
int err = udp_send((struct udp_pcb*)conn->pcb, pb);
if(err == ERR_MEM) {
DEBUG_ERROR("error sending packet. out of memory");
} else if(err == ERR_RTE) {
DEBUG_ERROR("could not find route to destinations address");
} else if(err != ERR_OK) {
DEBUG_ERROR("error sending packet - %d", err);
} else {
conn->TXbuf->consume(udp_trans_len); // success
}
pbuf_free(pb);
return ERR_OK;
}
if(conn->socket_type == SOCK_STREAM)
{
DEBUG_ERROR("socket_type==SOCK_STREAM");
// How much we are currently allowed to write to the connection
ssize_t sndbuf = ((struct tcp_pcb*)conn->pcb)->snd_buf;
int err, r;
if(!sndbuf) {
// PCB send buffer is full, turn off readability notifications for the
// corresponding PhySocket until nc_sent() is called and confirms that there is
// now space on the buffer
DEBUG_ERROR("lwIP stack is full, sndbuf == 0");
conn->tap->_phy.setNotifyReadable(conn->sock, false);
return -1;
}
int buf_w = conn->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);
}
if(conn->TXbuf->count() <= 0) {
return -1; // nothing to write
}
if(conn->sock) {
r = std::min((ssize_t)conn->TXbuf->count(), sndbuf);
// 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*)conn->pcb, conn->TXbuf->get_buf(), r, TCP_WRITE_FLAG_COPY);
tcp_output((struct tcp_pcb*)conn->pcb);
if(err != ERR_OK) {
DEBUG_ERROR("error while writing to lwIP tcp_pcb, err=%d", err);
if(err == -1)
DEBUG_ERROR("lwIP out of memory");
return -1;
} else {
conn->TXbuf->consume(r); // success
return ERR_OK;
}
}
}
}
return err;
}
int lwIP::lwip_Close(Connection *conn)
{
DEBUG_INFO();
if(conn->socket_type == SOCK_DGRAM) {
udp_remove((struct udp_pcb*)conn->pcb);
}
// FIXME: check if already closed? conn->TCP_pcb->state != CLOSED
if(conn->pcb) {
//DEBUG_EXTRA("conn=%p, sock=%p, PCB->state = %d",
// (void*)&conn, (void*)&sock, conn->TCP_pcb->state);
if(((struct tcp_pcb*)conn->pcb)->state == SYN_SENT /*|| conn->TCP_pcb->state == CLOSE_WAIT*/) {
DEBUG_EXTRA("ignoring close request. invalid PCB state for this operation. sock=%p", conn->sock);
return -1;
}
struct tcp_pcb* tpcb = (struct tcp_pcb*)conn->pcb;
if(tcp_close(tpcb) == ERR_OK) {
// Unregister callbacks for this PCB
tcp_arg(tpcb, NULL);
tcp_recv(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", conn->sock);
}
}
return 0;
}
/****************************************************************************/
/* Callbacks from lwIP stack */
/****************************************************************************/
err_t lwIP::nc_recved(void *arg, struct tcp_pcb *PCB, struct pbuf *p, err_t err)
{
DEBUG_INFO();
Connection *conn = (Connection *)arg;
int tot = 0;
if(!conn) {
DEBUG_ERROR("no connection");
return ERR_OK; // FIXME: Determine if this is correct behaviour expected by the stack
}
//Mutex::Lock _l(conn->tap->_tcpconns_m);
//Mutex::Lock _l2(conn->_rx_m);
conn->tap->_tcpconns_m.lock();
conn->_rx_m.lock();
struct pbuf* q = p;
if(p == NULL) {
if(((struct tcp_pcb*)conn->pcb)->state == CLOSE_WAIT) {
// FIXME: Implement?
}
DEBUG_INFO("p == NULL");
return ERR_ABRT;
}
// Cycle through pbufs and write them to the RX buffer
// The RX buffer will be emptied via phyOnUnixWritable()
while(p != NULL) {
if(p->len <= 0)
break;
int avail = ZT_TCP_RX_BUF_SZ - conn->RXbuf->count();
int len = p->len;
if(avail < len) {
DEBUG_ERROR("not enough room (%d bytes) on RX buffer", avail);
}
memcpy(conn->RXbuf->get_buf(), p->payload, len);
conn->RXbuf->produce(len);
p = p->next;
tot += len;
}
DEBUG_INFO("tot=%d", tot);
conn->tap->_tcpconns_m.unlock();
conn->_rx_m.unlock();
if(tot) {
conn->tap->_phy.setNotifyWritable(conn->sock, true);
//conn->tap->phyOnUnixWritable(conn->sock, NULL, true); // to app
}
pbuf_free(q);
return ERR_OK;
}
err_t lwIP::nc_accept(void *arg, struct tcp_pcb *newPCB, err_t err)
{
Connection *conn = (Connection*)arg;
DEBUG_INFO("conn=%p", conn);
//Mutex::Lock _l(conn->tap->_tcpconns_m);
// create and populate new Connection object
Connection *new_conn = new Connection();
new_conn->socket_type = SOCK_STREAM;
new_conn->pcb = newPCB;
new_conn->tap = conn->tap;
new_conn->sock = conn->tap->_phy.wrapSocket(new_conn->sdk_fd, new_conn);
//memcpy(new_conn->tap->_phy.getuptr(new_conn->sock), new_conn, sizeof(conn));
DEBUG_INFO("new_conn=%p", new_conn);
// add new Connection object to parent connection so that we can find it via lwip_Accept()
conn->_AcceptedConnections.push(new_conn);
// set callbacks
tcp_arg(newPCB, new_conn);
tcp_recv(newPCB, nc_recved);
tcp_err(newPCB, nc_err);
tcp_sent(newPCB, nc_sent);
tcp_poll(newPCB, nc_poll, 1);
// let lwIP know that it can queue additional incoming connections
tcp_accepted((struct tcp_pcb*)conn->pcb);
return 0;
}
void lwIP::nc_udp_recved(void * arg, struct udp_pcb * upcb, struct pbuf * p, const ip_addr_t * addr, u16_t port)
{
DEBUG_INFO();
// to be implemented
}
err_t lwIP::nc_sent(void* arg, struct tcp_pcb *PCB, u16_t len)
{
DEBUG_EXTRA("pcb=%p", (void*)&PCB);
Connection *conn = (Connection *)arg;
Mutex::Lock _l(conn->tap->_tcpconns_m);
if(conn && len) {
int softmax = conn->socket_type == SOCK_STREAM ? ZT_TCP_TX_BUF_SZ : ZT_UDP_TX_BUF_SZ;
if(conn->TXbuf->count() < softmax) {
conn->tap->_phy.setNotifyReadable(conn->sock, true);
conn->tap->_phy.whack();
}
}
return ERR_OK;
}
err_t lwIP::nc_connected(void *arg, struct tcp_pcb *PCB, err_t err)
{
DEBUG_ATTN("pcb=%p", (void*)&PCB);
Connection *conn = (Connection *)arg;
if(conn)
return ERR_OK;
return -1;
// FIXME: check stack for expected return values
}
err_t lwIP::nc_poll(void* arg, struct tcp_pcb *PCB)
{
return ERR_OK;
}
void lwIP::nc_err(void *arg, err_t err)
{
DEBUG_ERROR("err=%d", err);
Connection *conn = (Connection *)arg;
if(!conn){
DEBUG_ERROR("conn==NULL");
errno = -1; // FIXME: Find more appropriate value
}
Mutex::Lock _l(conn->tap->_tcpconns_m);
int fd = conn->tap->_phy.getDescriptor(conn->sock);
DEBUG_ERROR("conn=%p, pcb=%p, fd=%d, err=%d", conn, conn->pcb, fd, err);
DEBUG_ERROR("closing connection");
conn->tap->Close(conn);
switch(err)
{
case ERR_MEM:
DEBUG_ERROR("ERR_MEM->ENOMEM");
errno = ENOMEM;
break;
case ERR_BUF:
DEBUG_ERROR("ERR_BUF->ENOBUFS");
errno = ENOBUFS;
break;
case ERR_TIMEOUT:
DEBUG_ERROR("ERR_TIMEOUT->ETIMEDOUT");
errno = ETIMEDOUT;
break;
case ERR_RTE:
DEBUG_ERROR("ERR_RTE->ENETUNREACH");
errno = ENETUNREACH;
break;
case ERR_INPROGRESS:
DEBUG_ERROR("ERR_INPROGRESS->EINPROGRESS");
errno = EINPROGRESS;
break;
case ERR_VAL:
DEBUG_ERROR("ERR_VAL->EINVAL");
errno = EINVAL;
break;
case ERR_WOULDBLOCK:
DEBUG_ERROR("ERR_WOULDBLOCK->EWOULDBLOCK");
errno = EWOULDBLOCK;
break;
case ERR_USE:
DEBUG_ERROR("ERR_USE->EADDRINUSE");
errno = EADDRINUSE;
break;
case ERR_ISCONN:
DEBUG_ERROR("ERR_ISCONN->EISCONN");
errno = EISCONN;
break;
case ERR_ABRT:
DEBUG_ERROR("ERR_ABRT->ECONNREFUSED");
errno = ECONNREFUSED;
break;
// TODO: Below are errors which don't have a standard errno correlate
case ERR_RST:
// -1
break;
case ERR_CLSD:
// -1
break;
case ERR_CONN:
// -1
break;
case ERR_ARG:
// -1
break;
case ERR_IF:
// -1
break;
default:
break;
}
}
}