net: Enable TCP support

User can enable TCP server (listening socket) support in the
IP stack. This commit does not yet have TCP client (connecting
socket) support.

Change-Id: I75dd02a81addc1d1e026463b53631d56378157df
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2015-07-08 08:51:57 +03:00
commit ad2e661569
32 changed files with 967 additions and 317 deletions

View file

@ -106,6 +106,9 @@ struct ip_buf {
void *uip_udp_conn;
linkaddr_t dest;
linkaddr_t src;
#if defined(CONFIG_NETWORKING_WITH_TCP)
int8_t sent_status; /* tells if the TCP packet was sent ok or not */
#endif
/* Neighbor discovery vars. Note that we are using void pointers here
* so that we do not need to include Contiki headers in this file.
@ -203,6 +206,7 @@ struct ip_buf {
#define ip_buf_ll_dest(buf) (((struct ip_buf *)net_buf_user_data((buf)))->dest)
#define ip_buf_context(buf) (((struct ip_buf *)net_buf_user_data((buf)))->context)
#define ip_buf_type(ptr) (((struct ip_buf *)net_buf_user_data((ptr)))->type)
#define ip_buf_sent_status(ptr) (((struct ip_buf *)net_buf_user_data((ptr)))->sent_status)
/* @endcond */
/** NET_BUF_IP

View file

@ -42,6 +42,12 @@ extern "C" {
#define NET_PRINT(...)
#endif /* CONFIG_NETWORKING_WITH_LOGGING */
enum net_tcp_type {
NET_TCP_TYPE_UNKNOWN = 0,
NET_TCP_TYPE_SERVER,
NET_TCP_TYPE_CLIENT,
};
struct net_buf;
struct net_context;

View file

@ -105,6 +105,13 @@ struct net_tuple {
enum ip_protocol ip_proto;
};
#if defined(CONFIG_NETWORKING_WITH_TCP)
enum tcp_event_type {
TCP_READ_EVENT = 1,
TCP_WRITE_EVENT,
};
#endif
#ifdef __cplusplus
}
#endif

View file

@ -115,6 +115,24 @@ config NETWORKING_IPV6_NO_ND
slip and tun device.
endif
config NETWORKING_WITH_TCP
bool
prompt "Enable TCP protocol"
depends on NETWORKING
default n
help
Enable Transmission and Control Protocol (TCP) support.
config TCP_MSS
int
prompt "TCP maximum segment size"
depends on NETWORKING_WITH_TCP
default 0
help
Tweak the TCP maximum segment size. Normally one should
not change this but let the IP stack to calculate a best
size for it.
config NETWORKING_WITH_RPL
bool
prompt "Enable RPL (ripple) IPv6 mesh routing protocol"

View file

@ -39,6 +39,12 @@ config NETWORK_IP_STACK_DEBUG_FULL
bool "Print both messages and annotations"
endchoice
config NETWORK_IP_STACK_DEBUG_CONTEXT
bool "Debug network context allocation"
default n
help
Enables printing of network context allocations and frees.
config NETWORK_IP_STACK_DEBUG_NET_BUF
bool "Debug network buffer allocation"
default n
@ -51,6 +57,13 @@ config NETWORK_IP_STACK_DEBUG_RECV_SEND
help
Enables generic debug printing when receiving and sending data.
config NETWORK_IP_STACK_DEBUG_TCP_PSOCK
bool "Debug network TCP protosockets"
depends on NETWORKING_WITH_TCP
default n
help
Enables debugging the protosockets used in TCP engine.
config NETWORK_IP_STACK_DEBUG_IPV6
bool "Debug core IPv6"
depends on NETWORKING_WITH_IPV6

View file

@ -52,6 +52,8 @@ obj-$(CONFIG_NETWORKING_WITH_IPV4) += \
contiki/ipv4/uip.o \
contiki/ipv4/uip-neighbor.o
obj-$(CONFIG_NETWORKING_WITH_TCP) += contiki/ip/psock.o
# RPL (RFC 6550) support
ifeq ($(CONFIG_NETWORKING_WITH_RPL),y)
ccflags-y += -DUIP_CONF_IPV6_RPL=1

View file

@ -45,8 +45,15 @@ typedef unsigned int uip_stats_t;
/* The actual MTU size is defined in uipopt.h */
#define UIP_CONF_BUFFER_SIZE UIP_LINK_MTU
/* No TCP yet */
#ifdef CONFIG_NETWORKING_WITH_TCP
#define UIP_CONF_TCP 1
#if CONFIG_TCP_MSS > 0
#define UIP_CONF_TCP_MSS CONFIG_TCP_MSS
#endif /* CONFIG_TCP_MSS */
#else
#define UIP_CONF_TCP 0
#endif
/* We do not want to be a router */
#define UIP_CONF_ROUTER 0

View file

@ -34,7 +34,14 @@
#include <string.h>
#include "net/ip/psock.h"
#include <net/ip_buf.h>
#ifdef CONFIG_NETWORK_IP_STACK_DEBUG_TCP_PSOCK
#define DEBUG 1
#endif
#include "contiki/ip/uip-debug.h"
#include "contiki/ip/psock.h"
#define STATE_NONE 0
#define STATE_ACKED 1
@ -80,6 +87,8 @@ buf_bufdata(struct psock_buf *buf, uint16_t len,
uint8_t **dataptr, uint16_t *datalen)
{
if(*datalen < buf->left) {
PRINTF("%d: *datalen(%d) < left(%d), copy buf->ptr(%p) <- *dataptr(%p)\n",
__LINE__, *datalen, buf->left, buf->ptr, *dataptr);
memcpy(buf->ptr, *dataptr, *datalen);
buf->ptr += *datalen;
buf->left -= *datalen;
@ -87,6 +96,8 @@ buf_bufdata(struct psock_buf *buf, uint16_t len,
*datalen = 0;
return BUF_NOT_FULL;
} else if(*datalen == buf->left) {
PRINTF("%d: *datalen(%d) == left(%d), copy buf->ptr(%p) <- *dataptr(%p)\n",
__LINE__, *datalen, buf->left, buf->ptr, *dataptr);
memcpy(buf->ptr, *dataptr, *datalen);
buf->ptr += *datalen;
buf->left = 0;
@ -94,6 +105,8 @@ buf_bufdata(struct psock_buf *buf, uint16_t len,
*datalen = 0;
return BUF_FULL;
} else {
PRINTF("%d: *datalen(%d) > left(%d), copy buf->ptr(%p) <- buf->left(%p)\n",
__LINE__, *datalen, buf->left, buf->ptr, buf->left);
memcpy(buf->ptr, *dataptr, buf->left);
buf->ptr += buf->left;
*datalen -= buf->left;
@ -133,18 +146,24 @@ data_is_sent_and_acked(CC_REGISTER_ARG struct psock *s)
/* If data has previously been sent, and the data has been acked, we
increase the send pointer and call send_data() to send more
data. */
if(s->state != STATE_DATA_SENT || uip_rexmit()) {
if(s->sendlen > uip_mss()) {
uip_send(s->sendptr, uip_mss());
PRINTF("%s: psock %p buf %p data[%p..%p] sendptr %p sendlen %d mss %d\n",
__func__, s, s->net_buf, ip_buf_appdata(s->net_buf),
ip_buf_appdata(s->net_buf) + ip_buf_appdatalen(s->net_buf),
s->sendptr, s->sendlen,
uip_mss(s->net_buf));
if(s->state != STATE_DATA_SENT || uip_rexmit(s->net_buf)) {
if(s->sendlen > uip_mss(s->net_buf)) {
uip_send(s->net_buf, s->sendptr, uip_mss(s->net_buf));
} else {
uip_send(s->sendptr, s->sendlen);
uip_send(s->net_buf, s->sendptr, s->sendlen);
}
s->state = STATE_DATA_SENT;
return 0;
} else if(s->state == STATE_DATA_SENT && uip_acked()) {
if(s->sendlen > uip_mss()) {
s->sendlen -= uip_mss();
s->sendptr += uip_mss();
} else if(s->state == STATE_DATA_SENT && uip_acked(s->net_buf)) {
if(s->sendlen > uip_mss(s->net_buf)) {
s->sendlen -= uip_mss(s->net_buf);
s->sendptr += uip_mss(s->net_buf);
} else {
s->sendptr += s->sendlen;
s->sendlen = 0;
@ -155,20 +174,23 @@ data_is_sent_and_acked(CC_REGISTER_ARG struct psock *s)
return 0;
}
/*---------------------------------------------------------------------------*/
PT_THREAD(psock_send(CC_REGISTER_ARG struct psock *s, const uint8_t *buf,
unsigned int len))
PT_THREAD(psock_send(CC_REGISTER_ARG struct psock *s, struct net_buf *net_buf))
{
PT_BEGIN(&s->psockpt);
PRINTF("%s: psock %p buf %p len %d\n", __func__, s, net_buf,
ip_buf_appdatalen(net_buf));
/* If there is no data to send, we exit immediately. */
if(len == 0) {
if(ip_buf_appdatalen(net_buf) == 0) {
PT_EXIT(&s->psockpt);
}
/* Save the length of and a pointer to the data that is to be
sent. */
s->sendptr = buf;
s->sendlen = len;
s->sendptr = ip_buf_appdata(net_buf);
s->sendlen = ip_buf_appdatalen(net_buf);
s->net_buf = net_buf;
s->state = STATE_NONE;
@ -203,19 +225,20 @@ PT_THREAD(psock_generator_send(CC_REGISTER_ARG struct psock *s,
/* Call the generator function to generate the data in the
uip_appdata buffer. */
s->sendlen = generate(arg);
s->sendptr = uip_appdata;
s->sendptr = uip_appdata(s->net_buf);
if(s->sendlen > uip_mss()) {
uip_send(s->sendptr, uip_mss());
if(s->sendlen > uip_mss(s->net_buf)) {
uip_send(s->net_buf, s->sendptr, uip_mss(s->net_buf));
} else {
uip_send(s->sendptr, s->sendlen);
uip_send(s->net_buf, s->sendptr, s->sendlen);
}
s->state = STATE_DATA_SENT;
/* Wait until all data is sent and acknowledged. */
// if (!s->sendlen) break; //useful debugging aid
PT_YIELD_UNTIL(&s->psockpt, uip_acked() || uip_rexmit());
} while(!uip_acked());
PT_YIELD_UNTIL(&s->psockpt, uip_acked(s->net_buf) || \
uip_rexmit(s->net_buf));
} while(!uip_acked(s->net_buf));
s->state = STATE_NONE;
@ -239,7 +262,7 @@ psock_newdata(struct psock *s)
/* All data in uip_appdata buffer already consumed. */
s->state = STATE_BLOCKED_NEWDATA;
return 0;
} else if(uip_newdata()) {
} else if(uip_newdata(s->net_buf)) {
/* There is new data that has not been consumed. */
return 1;
} else {
@ -261,8 +284,8 @@ PT_THREAD(psock_readto(CC_REGISTER_ARG struct psock *psock, unsigned char c))
if(psock->readlen == 0) {
PT_WAIT_UNTIL(&psock->psockpt, psock_newdata(psock));
psock->state = STATE_READ;
psock->readptr = (uint8_t *)uip_appdata;
psock->readlen = uip_datalen();
psock->readptr = (uint8_t *)uip_appdata(psock->net_buf);
psock->readlen = uip_datalen(psock->net_buf);
}
} while(buf_bufto(&psock->buf, c,
&psock->readptr,
@ -289,8 +312,8 @@ PT_THREAD(psock_readbuf_len(CC_REGISTER_ARG struct psock *psock, uint16_t len))
if(psock->readlen == 0) {
PT_WAIT_UNTIL(&psock->psockpt, psock_newdata(psock));
psock->state = STATE_READ;
psock->readptr = (uint8_t *)uip_appdata;
psock->readlen = uip_datalen();
psock->readptr = (uint8_t *)uip_appdata(psock->net_buf);
psock->readlen = uip_datalen(psock->net_buf);
}
} while(buf_bufdata(&psock->buf, psock->bufsize,
&psock->readptr, &psock->readlen) == BUF_NOT_FULL &&
@ -305,14 +328,14 @@ PT_THREAD(psock_readbuf_len(CC_REGISTER_ARG struct psock *psock, uint16_t len))
/*---------------------------------------------------------------------------*/
void
psock_init(CC_REGISTER_ARG struct psock *psock,
uint8_t *buffer, unsigned int buffersize)
psock_init(CC_REGISTER_ARG struct psock *psock, struct net_buf *net_buf)
{
psock->state = STATE_NONE;
psock->readlen = 0;
psock->bufptr = buffer;
psock->bufsize = buffersize;
buf_setup(&psock->buf, buffer, buffersize);
psock->bufptr = ip_buf_appdata(net_buf);
psock->bufsize = ip_buf_appdatalen(net_buf);
psock->net_buf = net_buf;
buf_setup(&psock->buf, psock->bufptr, psock->bufsize);
PT_INIT(&psock->pt);
PT_INIT(&psock->psockpt);
}

View file

@ -127,9 +127,11 @@ struct psock {
unsigned int bufsize; /* The size of the input buffer. */
unsigned char state; /* The state of the protosocket. */
struct net_buf *net_buf; /* contains conn state etc. */
};
void psock_init(struct psock *psock, uint8_t *buffer, unsigned int buffersize);
void psock_init(struct psock *psock, struct net_buf *net_buf);
/**
* Initialize a protosocket.
*
@ -140,15 +142,12 @@ void psock_init(struct psock *psock, uint8_t *buffer, unsigned int buffersize);
* \param psock (struct psock *) A pointer to the protosocket to be
* initialized
*
* \param buffer (uint8_t *) A pointer to the input buffer for the
* protosocket.
*
* \param buffersize (unsigned int) The size of the input buffer.
* \param buffer (struct net_buf *) A Pointer to network buffer to send.
*
* \hideinitializer
*/
#define PSOCK_INIT(psock, buffer, buffersize) \
psock_init(psock, buffer, buffersize)
#define PSOCK_INIT(psock, net_buf) \
psock_init(psock, net_buf)
/**
* Start the protosocket protothread in a function.
@ -163,7 +162,7 @@ void psock_init(struct psock *psock, uint8_t *buffer, unsigned int buffersize);
*/
#define PSOCK_BEGIN(psock) PT_BEGIN(&((psock)->pt))
PT_THREAD(psock_send(struct psock *psock, const uint8_t *buf, unsigned int len));
PT_THREAD(psock_send(struct psock *psock, struct net_buf *buf));
/**
* Send data.
*
@ -174,15 +173,12 @@ PT_THREAD(psock_send(struct psock *psock, const uint8_t *buf, unsigned int len))
* \param psock (struct psock *) A pointer to the protosocket over which
* data is to be sent.
*
* \param data (uint8_t *) A pointer to the data that is to be sent.
*
* \param datalen (unsigned int) The length of the data that is to be
* sent.
* \param buf (struct net_buf *) A pointer to the buffer that is to be sent.
*
* \hideinitializer
*/
#define PSOCK_SEND(psock, data, datalen) \
PT_WAIT_THREAD(&((psock)->pt), psock_send(psock, data, datalen))
#define PSOCK_SEND(psock, buf) \
PT_WAIT_THREAD(&((psock)->pt), psock_send(psock, buf))
/**
* \brief Send a null-terminated string.

View file

@ -75,7 +75,7 @@ static void
init_simple_udp(void)
{
if(started == 0) {
process_start(&simple_udp_process, NULL);
process_start(&simple_udp_process, NULL, NULL);
started = 1;
}
}
@ -162,7 +162,7 @@ simple_udp_unregister(struct simple_udp_connection *c)
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(simple_udp_process, ev, data, buf)
PROCESS_THREAD(simple_udp_process, ev, data, buf, user_data)
{
struct simple_udp_connection *c;
PROCESS_BEGIN();

View file

@ -52,6 +52,7 @@
#include <string.h>
#ifdef CONFIG_NETWORK_IP_STACK_DEBUG_RECV_SEND
#define TCPIP_CONF_ANNOTATE_TRANSMISSIONS 1
#define DEBUG 1
#endif
#include "contiki/ip/uip-debug.h"
@ -218,7 +219,8 @@ packet_input(struct net_buf *buf)
#if UIP_TCP
#if UIP_ACTIVE_OPEN
struct uip_conn *
tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate,
struct process *process)
{
struct uip_conn *c;
@ -227,7 +229,7 @@ tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
return NULL;
}
c->appstate.p = PROCESS_CURRENT();
c->appstate.p = process;
c->appstate.state = appstate;
tcpip_poll_tcp(c);
@ -237,7 +239,7 @@ tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
#endif /* UIP_ACTIVE_OPEN */
/*---------------------------------------------------------------------------*/
void
tcp_unlisten(uint16_t port)
tcp_unlisten(uint16_t port, struct process *handler)
{
static unsigned char i;
struct listenport *l;
@ -245,7 +247,7 @@ tcp_unlisten(uint16_t port)
l = s.listenports;
for(i = 0; i < UIP_LISTENPORTS; ++i) {
if(l->port == port &&
l->p == PROCESS_CURRENT()) {
l->p == handler) {
l->port = 0;
uip_unlisten(port);
break;
@ -255,7 +257,7 @@ tcp_unlisten(uint16_t port)
}
/*---------------------------------------------------------------------------*/
void
tcp_listen(uint16_t port)
tcp_listen(uint16_t port, struct process *handler)
{
static unsigned char i;
struct listenport *l;
@ -264,7 +266,7 @@ tcp_listen(uint16_t port)
for(i = 0; i < UIP_LISTENPORTS; ++i) {
if(l->port == 0) {
l->port = port;
l->p = PROCESS_CURRENT();
l->p = handler;
uip_listen(port);
break;
}
@ -518,7 +520,11 @@ eventhandler(process_event_t ev, process_data_t data, struct net_buf *buf)
#endif /* UIP_UDP */
case PACKET_INPUT:
packet_input(buf);
if (!packet_input(buf)) {
PRINTF("Packet %p was not sent and must be discarded by caller\n", buf);
} else {
PRINTF("Packet %p was sent ok\n", buf);
}
break;
};
}
@ -700,6 +706,26 @@ tcpip_ipv6_output(struct net_buf *buf)
uip_len(buf) = 0;
uip_ext_len(buf) = 0;
return 0; /* packet was discarded */
#else /* UIP_ND6_SEND_NA */
int uiplen = uip_len(buf);
int extlen = uip_ext_len(buf);
/* Neighbor discovery is not there. Just try to send the packet. */
ret = tcpip_output(buf, NULL);
if (ret) {
/* We must set the length back as these were overwritten
* by other part of the stack. If we do not do this then
* there will be a double memory free in the caller.
* FIXME properly later!
*/
uip_len(buf) = uiplen;
uip_ext_len(buf) = extlen;
} else {
uip_len(buf) = 0;
uip_ext_len(buf) = 0;
}
return ret;
#endif /* UIP_ND6_SEND_NA */
} else {
#if UIP_ND6_SEND_NA
@ -810,7 +836,7 @@ tcpip_poll_tcp(struct uip_conn *conn)
void
tcpip_uipcall(struct net_buf *buf)
{
uip_udp_appstate_t *ts;
struct tcpip_uipstate *ts;
#if UIP_UDP
if(uip_conn(buf) != NULL) {
@ -852,7 +878,7 @@ tcpip_uipcall(struct net_buf *buf)
}
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(tcpip_process, ev, data, buf)
PROCESS_THREAD(tcpip_process, ev, data, buf, user_data)
{
PROCESS_BEGIN();

View file

@ -127,7 +127,7 @@ CCIF void tcp_attach(struct uip_conn *conn,
* \param port The port number in network byte order.
*
*/
CCIF void tcp_listen(uint16_t port);
CCIF void tcp_listen(uint16_t port, struct process *handler);
/**
* Close a listening TCP port.
@ -141,7 +141,7 @@ CCIF void tcp_listen(uint16_t port);
* \param port The port number in network byte order.
*
*/
CCIF void tcp_unlisten(uint16_t port);
CCIF void tcp_unlisten(uint16_t port, struct process *handler);
/**
* Open a TCP connection to the specified IP address and port.
@ -167,7 +167,7 @@ CCIF void tcp_unlisten(uint16_t port);
*
*/
CCIF struct uip_conn *tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port,
void *appstate);
void *appstate, struct process *process);
/**
* Cause a specified TCP connection to be polled.

View file

@ -53,7 +53,7 @@ init(void)
static uint8_t inited = 0;
if(!inited) {
inited = 1;
process_start(&udp_socket_process, NULL);
process_start(&udp_socket_process, NULL, NULL);
}
}
/*---------------------------------------------------------------------------*/
@ -152,7 +152,7 @@ udp_socket_sendto(struct net_buf *buf, struct udp_socket *c,
return -1;
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(udp_socket_process, ev, data, buf)
PROCESS_THREAD(udp_socket_process, ev, data, buf, user_data)
{
struct udp_socket *c;
PROCESS_BEGIN();

View file

@ -12,7 +12,6 @@ MEMB(packets_memb, struct uip_packetqueue_packet, MAX_NUM_QUEUED_PACKETS);
#ifdef CONFIG_NETWORK_IP_STACK_DEBUG_PACKET_QUEUE
#define DEBUG 1
#endif
#include "contiki/ip/uip-debug.h"
/*---------------------------------------------------------------------------*/

View file

@ -1588,6 +1588,12 @@ uint8_t uip_process(struct net_buf *buf, uint8_t flag);
#define UIP_UDP_TIMER 5
#endif /* UIP_UDP */
#if UIP_TCP
#define UIP_TCP_SEND_CONN 6 /* Tells uIP that a TCP segment
should be constructed in the
uip_buf buffer. */
#endif
/* The TCP states used in the uip_conn->tcpstateflags. */
#define UIP_CLOSED 0
#define UIP_SYN_RCVD 1

View file

@ -83,6 +83,7 @@
#include <net/ip_buf.h>
#include <string.h>
#include <errno.h>
/*---------------------------------------------------------------------------*/
/* Variable definitions. */
@ -666,13 +667,13 @@ uip_reass(void)
/*---------------------------------------------------------------------------*/
#if UIP_TCP
static void
uip_add_rcv_nxt(uint16_t n)
uip_add_rcv_nxt(struct net_buf *buf, uint16_t n)
{
uip_add32(uip_conn->rcv_nxt, n);
uip_conn->rcv_nxt[0] = uip_acc32[0];
uip_conn->rcv_nxt[1] = uip_acc32[1];
uip_conn->rcv_nxt[2] = uip_acc32[2];
uip_conn->rcv_nxt[3] = uip_acc32[3];
uip_add32(uip_conn(buf)->rcv_nxt, n);
uip_conn(buf)->rcv_nxt[0] = uip_acc32[0];
uip_conn(buf)->rcv_nxt[1] = uip_acc32[1];
uip_conn(buf)->rcv_nxt[2] = uip_acc32[2];
uip_conn(buf)->rcv_nxt[3] = uip_acc32[3];
}
#endif /* UIP_TCP */
/*---------------------------------------------------------------------------*/
@ -680,7 +681,7 @@ uint8_t
uip_process(struct net_buf *buf, uint8_t flag)
{
#if UIP_TCP
register struct uip_conn *uip_connr = uip_conn;
register struct uip_conn *uip_connr = uip_conn(buf);
#endif
#if UIP_UDP
@ -689,30 +690,58 @@ uip_process(struct net_buf *buf, uint8_t flag)
goto udp_send;
}
#endif /* UIP_UDP */
#if UIP_TCP
if(flag != UIP_TCP_SEND_CONN) {
#endif
uip_sappdata(buf) = uip_appdata(buf) = &uip_buf(buf)[UIP_IPTCPH_LEN + UIP_LLH_LEN];
#if UIP_TCP
}
#endif
/* Check if we were invoked because of a poll request for a
particular connection. */
if(flag == UIP_POLL_REQUEST) {
#if UIP_TCP
if(flag == UIP_POLL_REQUEST || flag == UIP_TCP_SEND_CONN) {
if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED &&
!uip_outstanding(uip_connr)) {
uip_flags = UIP_POLL;
UIP_APPCALL();
goto appsend;
#if UIP_ACTIVE_OPEN && UIP_TCP
if (flag == UIP_POLL) {
uip_flags(buf) = UIP_POLL;
}
UIP_APPCALL(buf);
goto appsend;
#if UIP_ACTIVE_OPEN
} else if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_SYN_SENT) {
/* In the SYN_SENT state, we retransmit out SYN. */
BUF->flags = 0;
BUF(buf)->flags = 0;
goto tcp_send_syn;
#endif /* UIP_ACTIVE_OPEN */
}
#endif /* UIP_TCP */
if (flag == UIP_TCP_SEND_CONN) {
switch (uip_connr->tcpstateflags & UIP_TS_MASK) {
case UIP_CLOSED:
case UIP_FIN_WAIT_1:
case UIP_FIN_WAIT_2:
case UIP_CLOSING:
case UIP_TIME_WAIT:
ip_buf_sent_status(buf) = -ECONNABORTED;
goto drop;
}
if (uip_outstanding(uip_connr)) {
ip_buf_sent_status(buf) = -EAGAIN;
PRINTF("Retry to send packet len %d, outstanding data len %d\n",
uip_len(buf), uip_outstanding(uip_connr));
return 0;
}
}
goto drop;
}
#else /* TCP */
if(flag == UIP_POLL_REQUEST) {
goto drop;
}
#endif /* UIP_TCP */
/* Check if we were invoked because of the periodic timer firing. */
} else if(flag == UIP_TIMER) {
if(flag == UIP_TIMER) {
#if UIP_REASSEMBLY
if(uip_reasstmr != 0) {
--uip_reasstmr;
@ -758,11 +787,11 @@ uip_process(struct net_buf *buf, uint8_t flag)
/* We call UIP_APPCALL() with uip_flags set to
UIP_TIMEDOUT to inform the application that the
connection has timed out. */
uip_flags = UIP_TIMEDOUT;
UIP_APPCALL();
uip_flags(buf) = UIP_TIMEDOUT;
UIP_APPCALL(buf);
/* We also send a reset packet to the remote host. */
BUF->flags = TCP_RST | TCP_ACK;
BUF(buf)->flags = TCP_RST | TCP_ACK;
goto tcp_send_nodata;
}
@ -797,8 +826,8 @@ uip_process(struct net_buf *buf, uint8_t flag)
to do the actual retransmit after which we jump into
the code for sending out the packet (the apprexmit
label). */
uip_flags = UIP_REXMIT;
UIP_APPCALL();
uip_flags(buf) = UIP_REXMIT;
UIP_APPCALL(buf);
goto apprexmit;
case UIP_FIN_WAIT_1:
@ -812,8 +841,8 @@ uip_process(struct net_buf *buf, uint8_t flag)
} else if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED) {
/* If there was no need for a retransmission, we poll the
application for new data. */
uip_flags = UIP_POLL;
UIP_APPCALL();
uip_flags(buf) = UIP_POLL;
UIP_APPCALL(buf);
goto appsend;
}
}
@ -842,7 +871,7 @@ uip_process(struct net_buf *buf, uint8_t flag)
#if NETSTACK_CONF_WITH_IPV6
/* Check validity of the IP header. */
if((BUF->vtc & 0xf0) != 0x60) { /* IP version and header length. */
if((BUF(buf)->vtc & 0xf0) != 0x60) { /* IP version and header length. */
UIP_STAT(++uip_stat.ip.drop);
UIP_STAT(++uip_stat.ip.vhlerr);
UIP_LOG("ipv6: invalid version.");
@ -966,7 +995,7 @@ uip_process(struct net_buf *buf, uint8_t flag)
#endif /* NETSTACK_CONF_WITH_IPV6 */
#if UIP_TCP
if(BUF->proto == UIP_PROTO_TCP) { /* Check for TCP packet. If so,
if(BUF(buf)->proto == UIP_PROTO_TCP) { /* Check for TCP packet. If so,
proceed with TCP input
processing. */
goto tcp_input;
@ -1248,7 +1277,7 @@ uip_process(struct net_buf *buf, uint8_t flag)
/* Start of TCP input header processing code. */
if(uip_tcpchksum() != 0xffff) { /* Compute and check the TCP
if(uip_tcpchksum(buf) != 0xffff) { /* Compute and check the TCP
checksum. */
UIP_STAT(++uip_stat.tcp.drop);
UIP_STAT(++uip_stat.tcp.chkerr);
@ -1257,7 +1286,7 @@ uip_process(struct net_buf *buf, uint8_t flag)
}
/* Make sure that the TCP port number is not zero. */
if(BUF->destport == 0 || BUF->srcport == 0) {
if(BUF(buf)->destport == 0 || BUF(buf)->srcport == 0) {
UIP_LOG("tcp: zero port.");
goto drop;
}
@ -1267,9 +1296,9 @@ uip_process(struct net_buf *buf, uint8_t flag)
for(uip_connr = &uip_conns[0]; uip_connr <= &uip_conns[UIP_CONNS - 1];
++uip_connr) {
if(uip_connr->tcpstateflags != UIP_CLOSED &&
BUF->destport == uip_connr->lport &&
BUF->srcport == uip_connr->rport &&
uip_ipaddr_cmp(&BUF->srcipaddr, &uip_connr->ripaddr)) {
BUF(buf)->destport == uip_connr->lport &&
BUF(buf)->srcport == uip_connr->rport &&
uip_ipaddr_cmp(&BUF(buf)->srcipaddr, &uip_connr->ripaddr)) {
goto found;
}
}
@ -1278,11 +1307,11 @@ uip_process(struct net_buf *buf, uint8_t flag)
either this packet is an old duplicate, or this is a SYN packet
destined for a connection in LISTEN. If the SYN flag isn't set,
it is an old packet and we send a RST. */
if((BUF->flags & TCP_CTL) != TCP_SYN) {
if((BUF(buf)->flags & TCP_CTL) != TCP_SYN) {
goto reset;
}
tmp16 = BUF->destport;
tmp16 = BUF(buf)->destport;
/* Next, check listening connections. */
for(c = 0; c < UIP_LISTENPORTS; ++c) {
if(tmp16 == uip_listenports[c]) {
@ -1295,52 +1324,52 @@ uip_process(struct net_buf *buf, uint8_t flag)
reset:
/* We do not send resets in response to resets. */
if(BUF->flags & TCP_RST) {
if(BUF(buf)->flags & TCP_RST) {
goto drop;
}
UIP_STAT(++uip_stat.tcp.rst);
BUF->flags = TCP_RST | TCP_ACK;
uip_len = UIP_IPTCPH_LEN;
BUF->tcpoffset = 5 << 4;
BUF(buf)->flags = TCP_RST | TCP_ACK;
uip_len(buf) = UIP_IPTCPH_LEN;
BUF(buf)->tcpoffset = 5 << 4;
/* Flip the seqno and ackno fields in the TCP header. */
c = BUF->seqno[3];
BUF->seqno[3] = BUF->ackno[3];
BUF->ackno[3] = c;
c = BUF(buf)->seqno[3];
BUF(buf)->seqno[3] = BUF(buf)->ackno[3];
BUF(buf)->ackno[3] = c;
c = BUF->seqno[2];
BUF->seqno[2] = BUF->ackno[2];
BUF->ackno[2] = c;
c = BUF(buf)->seqno[2];
BUF(buf)->seqno[2] = BUF(buf)->ackno[2];
BUF(buf)->ackno[2] = c;
c = BUF->seqno[1];
BUF->seqno[1] = BUF->ackno[1];
BUF->ackno[1] = c;
c = BUF(buf)->seqno[1];
BUF(buf)->seqno[1] = BUF(buf)->ackno[1];
BUF(buf)->ackno[1] = c;
c = BUF->seqno[0];
BUF->seqno[0] = BUF->ackno[0];
BUF->ackno[0] = c;
c = BUF(buf)->seqno[0];
BUF(buf)->seqno[0] = BUF(buf)->ackno[0];
BUF(buf)->ackno[0] = c;
/* We also have to increase the sequence number we are
acknowledging. If the least significant byte overflowed, we need
to propagate the carry to the other bytes as well. */
if(++BUF->ackno[3] == 0) {
if(++BUF->ackno[2] == 0) {
if(++BUF->ackno[1] == 0) {
++BUF->ackno[0];
if(++BUF(buf)->ackno[3] == 0) {
if(++BUF(buf)->ackno[2] == 0) {
if(++BUF(buf)->ackno[1] == 0) {
++BUF(buf)->ackno[0];
}
}
}
/* Swap port numbers. */
tmp16 = BUF->srcport;
BUF->srcport = BUF->destport;
BUF->destport = tmp16;
tmp16 = BUF(buf)->srcport;
BUF(buf)->srcport = BUF(buf)->destport;
BUF(buf)->destport = tmp16;
/* Swap IP addresses. */
uip_ipaddr_copy(&BUF->destipaddr, &BUF->srcipaddr);
uip_ipaddr_copy(&BUF->srcipaddr, &uip_hostaddr);
uip_ipaddr_copy(&BUF(buf)->destipaddr, &BUF(buf)->srcipaddr);
uip_ipaddr_copy(&BUF(buf)->srcipaddr, &uip_hostaddr);
/* And send out the RST packet! */
goto tcp_send_noconn;
@ -1349,6 +1378,7 @@ uip_process(struct net_buf *buf, uint8_t flag)
with a connection in LISTEN. In that case, we should create a new
connection and send a SYNACK in return. */
found_listen:
PRINTF("In found listen\n");
/* First we check if there are any connections avaliable. Unused
connections are kept in the same table as used connections, but
unused ones have the tcpstate set to CLOSED. Also, connections in
@ -1377,16 +1407,16 @@ uip_process(struct net_buf *buf, uint8_t flag)
UIP_LOG("tcp: found no unused connections.");
goto drop;
}
uip_conn = uip_connr;
uip_set_conn(buf) = uip_connr;
/* Fill in the necessary fields for the new connection. */
uip_connr->rto = uip_connr->timer = UIP_RTO;
uip_connr->sa = 0;
uip_connr->sv = 4;
uip_connr->nrtx = 0;
uip_connr->lport = BUF->destport;
uip_connr->rport = BUF->srcport;
uip_ipaddr_copy(&uip_connr->ripaddr, &BUF->srcipaddr);
uip_connr->lport = BUF(buf)->destport;
uip_connr->rport = BUF(buf)->srcport;
uip_ipaddr_copy(&uip_connr->ripaddr, &BUF(buf)->srcipaddr);
uip_connr->tcpstateflags = UIP_SYN_RCVD;
uip_connr->snd_nxt[0] = iss[0];
@ -1396,16 +1426,16 @@ uip_process(struct net_buf *buf, uint8_t flag)
uip_connr->len = 1;
/* rcv_nxt should be the seqno from the incoming packet + 1. */
uip_connr->rcv_nxt[3] = BUF->seqno[3];
uip_connr->rcv_nxt[2] = BUF->seqno[2];
uip_connr->rcv_nxt[1] = BUF->seqno[1];
uip_connr->rcv_nxt[0] = BUF->seqno[0];
uip_add_rcv_nxt(1);
uip_connr->rcv_nxt[3] = BUF(buf)->seqno[3];
uip_connr->rcv_nxt[2] = BUF(buf)->seqno[2];
uip_connr->rcv_nxt[1] = BUF(buf)->seqno[1];
uip_connr->rcv_nxt[0] = BUF(buf)->seqno[0];
uip_add_rcv_nxt(buf, 1);
/* Parse the TCP MSS option, if present. */
if((BUF->tcpoffset & 0xf0) > 0x50) {
for(c = 0; c < ((BUF->tcpoffset >> 4) - 5) << 2 ;) {
opt = uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + c];
if((BUF(buf)->tcpoffset & 0xf0) > 0x50) {
for(c = 0; c < ((BUF(buf)->tcpoffset >> 4) - 5) << 2 ;) {
opt = uip_buf(buf)[UIP_TCPIP_HLEN + UIP_LLH_LEN + c];
if(opt == TCP_OPT_END) {
/* End of options. */
break;
@ -1413,10 +1443,10 @@ uip_process(struct net_buf *buf, uint8_t flag)
++c;
/* NOP option. */
} else if(opt == TCP_OPT_MSS &&
uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == TCP_OPT_MSS_LEN) {
uip_buf(buf)[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == TCP_OPT_MSS_LEN) {
/* An MSS option with the right option length. */
tmp16 = ((uint16_t)uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 2 + c] << 8) |
(uint16_t)uip_buf[UIP_IPTCPH_LEN + UIP_LLH_LEN + 3 + c];
tmp16 = ((uint16_t)uip_buf(buf)[UIP_TCPIP_HLEN + UIP_LLH_LEN + 2 + c] << 8) |
(uint16_t)uip_buf(buf)[UIP_IPTCPH_LEN + UIP_LLH_LEN + 3 + c];
uip_connr->initialmss = uip_connr->mss =
tmp16 > UIP_TCP_MSS? UIP_TCP_MSS: tmp16;
@ -1425,12 +1455,12 @@ uip_process(struct net_buf *buf, uint8_t flag)
} else {
/* All other options have a length field, so that we easily
can skip past them. */
if(uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == 0) {
if(uip_buf(buf)[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == 0) {
/* If the length field is zero, the options are malformed
and we don't process them further. */
break;
}
c += uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c];
c += uip_buf(buf)[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c];
}
}
}
@ -1444,41 +1474,50 @@ uip_process(struct net_buf *buf, uint8_t flag)
BUF->flags |= TCP_SYN;
#else /* UIP_ACTIVE_OPEN */
tcp_send_synack:
BUF->flags = TCP_SYN | TCP_ACK;
BUF(buf)->flags = TCP_SYN | TCP_ACK;
#endif /* UIP_ACTIVE_OPEN */
/* We send out the TCP Maximum Segment Size option with our
SYNACK. */
BUF->optdata[0] = TCP_OPT_MSS;
BUF->optdata[1] = TCP_OPT_MSS_LEN;
BUF->optdata[2] = (UIP_TCP_MSS) / 256;
BUF->optdata[3] = (UIP_TCP_MSS) & 255;
uip_len = UIP_IPTCPH_LEN + TCP_OPT_MSS_LEN;
BUF->tcpoffset = ((UIP_TCPH_LEN + TCP_OPT_MSS_LEN) / 4) << 4;
BUF(buf)->optdata[0] = TCP_OPT_MSS;
BUF(buf)->optdata[1] = TCP_OPT_MSS_LEN;
BUF(buf)->optdata[2] = (UIP_TCP_MSS) / 256;
BUF(buf)->optdata[3] = (UIP_TCP_MSS) & 255;
uip_len(buf) = UIP_IPTCPH_LEN + TCP_OPT_MSS_LEN;
BUF(buf)->tcpoffset = ((UIP_TCPH_LEN + TCP_OPT_MSS_LEN) / 4) << 4;
goto tcp_send;
/* This label will be jumped to if we found an active connection. */
found:
uip_conn = uip_connr;
uip_flags = 0;
PRINTF("In found\n");
uip_set_conn(buf) = uip_connr;
uip_flags(buf) = 0;
/* We do a very naive form of TCP reset processing; we just accept
any RST and kill our connection. We should in fact check if the
sequence number of this reset is within our advertised window
before we accept the reset. */
if(BUF->flags & TCP_RST) {
if(BUF(buf)->flags & TCP_RST) {
uip_connr->tcpstateflags = UIP_CLOSED;
UIP_LOG("tcp: got reset, aborting connection.");
uip_flags = UIP_ABORT;
UIP_APPCALL();
uip_flags(buf) = UIP_ABORT;
UIP_APPCALL(buf);
goto drop;
}
/* Calculate the length of the data, if the application has sent
any data to us. */
c = (BUF->tcpoffset >> 4) << 2;
/* uip_len will contain the length of the actual TCP data. This is
calculated by subtracing the length of the TCP header (in
c) and the length of the IP header (20 bytes). */
uip_len = uip_len - c - UIP_IPH_LEN;
if (flag != UIP_TCP_SEND_CONN) {
/* If flag is set to UIP_TCP_SEND_CONN, then it means that we are
* trying to send the actual packet coming from user. In this case
* do not mess with the packet length.
*/
/* Calculate the length of the data, if the application has sent
any data to us. */
c = (BUF(buf)->tcpoffset >> 4) << 2;
/* uip_len will contain the length of the actual TCP data. This is
calculated by subtracing the length of the TCP header (in
c) and the length of the IP header (20 bytes). */
uip_len(buf) = uip_len(buf) - c - UIP_IPH_LEN;
}
/* First, check if the sequence number of the incoming packet is
what we're expecting next. If not, we send out an ACK with the
@ -1486,14 +1525,14 @@ uip_process(struct net_buf *buf, uint8_t flag)
receive a SYN, in which case we should retransmit our SYNACK
(which is done futher down). */
if(!((((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_SYN_SENT) &&
((BUF->flags & TCP_CTL) == (TCP_SYN | TCP_ACK))) ||
((BUF(buf)->flags & TCP_CTL) == (TCP_SYN | TCP_ACK))) ||
(((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_SYN_RCVD) &&
((BUF->flags & TCP_CTL) == TCP_SYN)))) {
if((uip_len > 0 || ((BUF->flags & (TCP_SYN | TCP_FIN)) != 0)) &&
(BUF->seqno[0] != uip_connr->rcv_nxt[0] ||
BUF->seqno[1] != uip_connr->rcv_nxt[1] ||
BUF->seqno[2] != uip_connr->rcv_nxt[2] ||
BUF->seqno[3] != uip_connr->rcv_nxt[3])) {
((BUF(buf)->flags & TCP_CTL) == TCP_SYN)))) {
if((uip_len(buf) > 0 || ((BUF(buf)->flags & (TCP_SYN | TCP_FIN)) != 0)) &&
(BUF(buf)->seqno[0] != uip_connr->rcv_nxt[0] ||
BUF(buf)->seqno[1] != uip_connr->rcv_nxt[1] ||
BUF(buf)->seqno[2] != uip_connr->rcv_nxt[2] ||
BUF(buf)->seqno[3] != uip_connr->rcv_nxt[3])) {
goto tcp_send_ack;
}
}
@ -1502,13 +1541,13 @@ uip_process(struct net_buf *buf, uint8_t flag)
data. If so, we update the sequence number, reset the length of
the outstanding data, calculate RTT estimations, and reset the
retransmission timer. */
if((BUF->flags & TCP_ACK) && uip_outstanding(uip_connr)) {
if((BUF(buf)->flags & TCP_ACK) && uip_outstanding(uip_connr)) {
uip_add32(uip_connr->snd_nxt, uip_connr->len);
if(BUF->ackno[0] == uip_acc32[0] &&
BUF->ackno[1] == uip_acc32[1] &&
BUF->ackno[2] == uip_acc32[2] &&
BUF->ackno[3] == uip_acc32[3]) {
if(BUF(buf)->ackno[0] == uip_acc32[0] &&
BUF(buf)->ackno[1] == uip_acc32[1] &&
BUF(buf)->ackno[2] == uip_acc32[2] &&
BUF(buf)->ackno[3] == uip_acc32[3]) {
/* Update sequence number. */
uip_connr->snd_nxt[0] = uip_acc32[0];
uip_connr->snd_nxt[1] = uip_acc32[1];
@ -1531,7 +1570,7 @@ uip_process(struct net_buf *buf, uint8_t flag)
}
/* Set the acknowledged flag. */
uip_flags = UIP_ACKDATA;
uip_flags(buf) = UIP_ACKDATA;
/* Reset the retransmission timer. */
uip_connr->timer = uip_connr->rto;
@ -1552,20 +1591,20 @@ uip_process(struct net_buf *buf, uint8_t flag)
we are waiting for an ACK that acknowledges the data we sent
out the last time. Therefore, we want to have the UIP_ACKDATA
flag set. If so, we enter the ESTABLISHED state. */
if(uip_flags & UIP_ACKDATA) {
if(uip_flags(buf) & UIP_ACKDATA) {
uip_connr->tcpstateflags = UIP_ESTABLISHED;
uip_flags = UIP_CONNECTED;
uip_flags(buf) = UIP_CONNECTED;
uip_connr->len = 0;
if(uip_len > 0) {
uip_flags |= UIP_NEWDATA;
uip_add_rcv_nxt(uip_len);
if(uip_len(buf) > 0) {
uip_flags(buf) |= UIP_NEWDATA;
uip_add_rcv_nxt(buf, uip_len(buf));
}
uip_slen = 0;
UIP_APPCALL();
uip_slen(buf) = 0;
UIP_APPCALL(buf);
goto appsend;
}
/* We need to retransmit the SYNACK */
if((BUF->flags & TCP_CTL) == TCP_SYN) {
if((BUF(buf)->flags & TCP_CTL) == TCP_SYN) {
goto tcp_send_synack;
}
goto drop;
@ -1618,7 +1657,7 @@ uip_process(struct net_buf *buf, uint8_t flag)
uip_add_rcv_nxt(1);
uip_flags = UIP_CONNECTED | UIP_NEWDATA;
uip_connr->len = 0;
uip_len = 0;
uip_len(buf) = 0;
uip_slen = 0;
UIP_APPCALL();
goto appsend;
@ -1643,27 +1682,27 @@ uip_process(struct net_buf *buf, uint8_t flag)
state. We require that there is no outstanding data; otherwise the
sequence numbers will be screwed up. */
if(BUF->flags & TCP_FIN && !(uip_connr->tcpstateflags & UIP_STOPPED)) {
if(BUF(buf)->flags & TCP_FIN && !(uip_connr->tcpstateflags & UIP_STOPPED)) {
if(uip_outstanding(uip_connr)) {
goto drop;
}
uip_add_rcv_nxt(1 + uip_len);
uip_flags |= UIP_CLOSE;
if(uip_len > 0) {
uip_flags |= UIP_NEWDATA;
uip_add_rcv_nxt(buf, 1 + uip_len(buf));
uip_flags(buf) |= UIP_CLOSE;
if(uip_len(buf) > 0) {
uip_flags(buf) |= UIP_NEWDATA;
}
UIP_APPCALL();
UIP_APPCALL(buf);
uip_connr->len = 1;
uip_connr->tcpstateflags = UIP_LAST_ACK;
uip_connr->nrtx = 0;
tcp_send_finack:
BUF->flags = TCP_FIN | TCP_ACK;
BUF(buf)->flags = TCP_FIN | TCP_ACK;
goto tcp_send_nodata;
}
/* Check the URG flag. If this is set, the segment carries urgent
data that we must pass to the application. */
if((BUF->flags & TCP_URG) != 0) {
if((BUF(buf)->flags & TCP_URG) != 0) {
#if UIP_URGDATA > 0
uip_urglen = (BUF->urgp[0] << 8) | BUF->urgp[1];
if(uip_urglen > uip_len) {
@ -1677,8 +1716,8 @@ uip_process(struct net_buf *buf, uint8_t flag)
} else {
uip_urglen = 0;
#else /* UIP_URGDATA > 0 */
uip_appdata = ((char *)uip_appdata) + ((BUF->urgp[0] << 8) | BUF->urgp[1]);
uip_len -= (BUF->urgp[0] << 8) | BUF->urgp[1];
uip_appdata(buf) = ((char *)uip_appdata(buf)) + ((BUF(buf)->urgp[0] << 8) | BUF(buf)->urgp[1]);
uip_len(buf) -= (BUF(buf)->urgp[0] << 8) | BUF(buf)->urgp[1];
#endif /* UIP_URGDATA > 0 */
}
@ -1687,9 +1726,9 @@ uip_process(struct net_buf *buf, uint8_t flag)
we acknowledge. If the application has stopped the dataflow
using uip_stop(), we must not accept any data packets from the
remote host. */
if(uip_len > 0 && !(uip_connr->tcpstateflags & UIP_STOPPED)) {
uip_flags |= UIP_NEWDATA;
uip_add_rcv_nxt(uip_len);
if(uip_len(buf) > 0 && !(uip_connr->tcpstateflags & UIP_STOPPED)) {
uip_flags(buf) |= UIP_NEWDATA;
uip_add_rcv_nxt(buf, uip_len(buf));
}
/* Check if the available buffer space advertised by the other end
@ -1704,7 +1743,7 @@ uip_process(struct net_buf *buf, uint8_t flag)
and the application will retransmit it. This is called the
"persistent timer" and uses the retransmission mechanim.
*/
tmp16 = ((uint16_t)BUF->wnd[0] << 8) + (uint16_t)BUF->wnd[1];
tmp16 = ((uint16_t)BUF(buf)->wnd[0] << 8) + (uint16_t)BUF(buf)->wnd[1];
if(tmp16 > uip_connr->initialmss ||
tmp16 == 0) {
tmp16 = uip_connr->initialmss;
@ -1727,34 +1766,39 @@ uip_process(struct net_buf *buf, uint8_t flag)
put into the uip_appdata and the length of the data should be
put into uip_len. If the application don't have any data to
send, uip_len must be set to 0. */
if(uip_flags & (UIP_NEWDATA | UIP_ACKDATA)) {
uip_slen = 0;
UIP_APPCALL();
if(uip_flags(buf) & (UIP_NEWDATA | UIP_ACKDATA)) {
if(flag != UIP_TCP_SEND_CONN) {
/* Do not reset the slen because we are being called
* directly by the application.
*/
uip_slen(buf) = 0;
}
UIP_APPCALL(buf);
appsend:
if(uip_flags & UIP_ABORT) {
uip_slen = 0;
if(uip_flags(buf) & UIP_ABORT) {
uip_slen(buf) = 0;
uip_connr->tcpstateflags = UIP_CLOSED;
BUF->flags = TCP_RST | TCP_ACK;
BUF(buf)->flags = TCP_RST | TCP_ACK;
goto tcp_send_nodata;
}
if(uip_flags & UIP_CLOSE) {
uip_slen = 0;
if(uip_flags(buf) & UIP_CLOSE) {
uip_slen(buf) = 0;
uip_connr->len = 1;
uip_connr->tcpstateflags = UIP_FIN_WAIT_1;
uip_connr->nrtx = 0;
BUF->flags = TCP_FIN | TCP_ACK;
BUF(buf)->flags = TCP_FIN | TCP_ACK;
goto tcp_send_nodata;
}
/* If uip_slen > 0, the application has data to be sent. */
if(uip_slen > 0) {
if(uip_slen(buf) > 0) {
/* If the connection has acknowledged data, the contents of
the ->len variable should be discarded. */
if((uip_flags & UIP_ACKDATA) != 0) {
if((uip_flags(buf) & UIP_ACKDATA) != 0) {
uip_connr->len = 0;
}
@ -1766,40 +1810,40 @@ uip_process(struct net_buf *buf, uint8_t flag)
/* The application cannot send more than what is allowed by
the mss (the minumum of the MSS and the available
window). */
if(uip_slen > uip_connr->mss) {
uip_slen = uip_connr->mss;
if(uip_slen(buf) > uip_connr->mss) {
uip_slen(buf) = uip_connr->mss;
}
/* Remember how much data we send out now so that we know
when everything has been acknowledged. */
uip_connr->len = uip_slen;
uip_connr->len = uip_slen(buf);
} else {
/* If the application already had unacknowledged data, we
make sure that the application does not send (i.e.,
retransmit) out more than it previously sent out. */
uip_slen = uip_connr->len;
uip_slen(buf) = uip_connr->len;
}
}
uip_connr->nrtx = 0;
apprexmit:
uip_appdata = uip_sappdata;
uip_appdata(buf) = uip_sappdata(buf);
/* If the application has data to be sent, or if the incoming
packet had new data in it, we must send out a packet. */
if(uip_slen > 0 && uip_connr->len > 0) {
if(uip_slen(buf) > 0 && uip_connr->len > 0) {
/* Add the length of the IP and TCP headers. */
uip_len = uip_connr->len + UIP_TCPIP_HLEN;
uip_len(buf) = uip_connr->len + UIP_TCPIP_HLEN;
/* We always set the ACK flag in response packets. */
BUF->flags = TCP_ACK | TCP_PSH;
BUF(buf)->flags = TCP_ACK | TCP_PSH;
/* Send the packet. */
goto tcp_send_noopts;
}
/* If there is no data to send, just send out a pure ACK if
there is newdata. */
if(uip_flags & UIP_NEWDATA) {
uip_len = UIP_TCPIP_HLEN;
BUF->flags = TCP_ACK;
if(uip_flags(buf) & UIP_NEWDATA) {
uip_len(buf) = UIP_TCPIP_HLEN;
BUF(buf)->flags = TCP_ACK;
goto tcp_send_noopts;
}
}
@ -1807,10 +1851,10 @@ uip_process(struct net_buf *buf, uint8_t flag)
case UIP_LAST_ACK:
/* We can close this connection if the peer has acknowledged our
FIN. This is indicated by the UIP_ACKDATA flag. */
if(uip_flags & UIP_ACKDATA) {
if(uip_flags(buf) & UIP_ACKDATA) {
uip_connr->tcpstateflags = UIP_CLOSED;
uip_flags = UIP_CLOSE;
UIP_APPCALL();
uip_flags(buf) = UIP_CLOSE;
UIP_APPCALL(buf);
}
break;
@ -1818,44 +1862,44 @@ uip_process(struct net_buf *buf, uint8_t flag)
/* The application has closed the connection, but the remote host
hasn't closed its end yet. Thus we do nothing but wait for a
FIN from the other side. */
if(uip_len > 0) {
uip_add_rcv_nxt(uip_len);
if(uip_len(buf) > 0) {
uip_add_rcv_nxt(buf, uip_len(buf));
}
if(BUF->flags & TCP_FIN) {
if(uip_flags & UIP_ACKDATA) {
if(BUF(buf)->flags & TCP_FIN) {
if(uip_flags(buf) & UIP_ACKDATA) {
uip_connr->tcpstateflags = UIP_TIME_WAIT;
uip_connr->timer = 0;
uip_connr->len = 0;
} else {
uip_connr->tcpstateflags = UIP_CLOSING;
}
uip_add_rcv_nxt(1);
uip_flags = UIP_CLOSE;
UIP_APPCALL();
uip_add_rcv_nxt(buf, 1);
uip_flags(buf) = UIP_CLOSE;
UIP_APPCALL(buf);
goto tcp_send_ack;
} else if(uip_flags & UIP_ACKDATA) {
} else if(uip_flags(buf) & UIP_ACKDATA) {
uip_connr->tcpstateflags = UIP_FIN_WAIT_2;
uip_connr->len = 0;
goto drop;
}
if(uip_len > 0) {
if(uip_len(buf) > 0) {
goto tcp_send_ack;
}
goto drop;
case UIP_FIN_WAIT_2:
if(uip_len > 0) {
uip_add_rcv_nxt(uip_len);
if(uip_len(buf) > 0) {
uip_add_rcv_nxt(buf, uip_len(buf));
}
if(BUF->flags & TCP_FIN) {
if(BUF(buf)->flags & TCP_FIN) {
uip_connr->tcpstateflags = UIP_TIME_WAIT;
uip_connr->timer = 0;
uip_add_rcv_nxt(1);
uip_flags = UIP_CLOSE;
UIP_APPCALL();
uip_add_rcv_nxt(buf, 1);
uip_flags(buf) = UIP_CLOSE;
UIP_APPCALL(buf);
goto tcp_send_ack;
}
if(uip_len > 0) {
if(uip_len(buf) > 0) {
goto tcp_send_ack;
}
goto drop;
@ -1864,7 +1908,7 @@ uip_process(struct net_buf *buf, uint8_t flag)
goto tcp_send_ack;
case UIP_CLOSING:
if(uip_flags & UIP_ACKDATA) {
if(uip_flags(buf) & UIP_ACKDATA) {
uip_connr->tcpstateflags = UIP_TIME_WAIT;
uip_connr->timer = 0;
}
@ -1874,63 +1918,74 @@ uip_process(struct net_buf *buf, uint8_t flag)
/* We jump here when we are ready to send the packet, and just want
to set the appropriate TCP sequence numbers in the TCP header. */
tcp_send_ack:
BUF->flags = TCP_ACK;
PRINTF("In tcp_send_ack\n");
BUF(buf)->flags = TCP_ACK;
tcp_send_nodata:
uip_len = UIP_IPTCPH_LEN;
if (flag != UIP_TCP_SEND_CONN) {
PRINTF("In tcp_send_nodata\n");
uip_len(buf) = UIP_IPTCPH_LEN;
}
tcp_send_noopts:
BUF->tcpoffset = (UIP_TCPH_LEN / 4) << 4;
BUF(buf)->tcpoffset = (UIP_TCPH_LEN / 4) << 4;
/* We're done with the input processing. We are now ready to send a
reply. Our job is to fill in all the fields of the TCP and IP
headers before calculating the checksum and finally send the
packet. */
tcp_send:
BUF->ackno[0] = uip_connr->rcv_nxt[0];
BUF->ackno[1] = uip_connr->rcv_nxt[1];
BUF->ackno[2] = uip_connr->rcv_nxt[2];
BUF->ackno[3] = uip_connr->rcv_nxt[3];
PRINTF("In tcp_send\n");
BUF->seqno[0] = uip_connr->snd_nxt[0];
BUF->seqno[1] = uip_connr->snd_nxt[1];
BUF->seqno[2] = uip_connr->snd_nxt[2];
BUF->seqno[3] = uip_connr->snd_nxt[3];
BUF(buf)->ackno[0] = uip_connr->rcv_nxt[0];
BUF(buf)->ackno[1] = uip_connr->rcv_nxt[1];
BUF(buf)->ackno[2] = uip_connr->rcv_nxt[2];
BUF(buf)->ackno[3] = uip_connr->rcv_nxt[3];
BUF->srcport = uip_connr->lport;
BUF->destport = uip_connr->rport;
BUF(buf)->seqno[0] = uip_connr->snd_nxt[0];
BUF(buf)->seqno[1] = uip_connr->snd_nxt[1];
BUF(buf)->seqno[2] = uip_connr->snd_nxt[2];
BUF(buf)->seqno[3] = uip_connr->snd_nxt[3];
uip_ipaddr_copy(&BUF->srcipaddr, &uip_hostaddr);
uip_ipaddr_copy(&BUF->destipaddr, &uip_connr->ripaddr);
BUF(buf)->srcport = uip_connr->lport;
BUF(buf)->destport = uip_connr->rport;
uip_ipaddr_copy(&BUF(buf)->srcipaddr, &uip_hostaddr);
uip_ipaddr_copy(&BUF(buf)->destipaddr, &uip_connr->ripaddr);
PRINTF("Sending TCP packet to ");
PRINT6ADDR(&BUF(buf)->destipaddr);
PRINTF(" from ");
PRINT6ADDR(&BUF(buf)->srcipaddr);
PRINTF("\n");
if(uip_connr->tcpstateflags & UIP_STOPPED) {
/* If the connection has issued uip_stop(), we advertise a zero
window so that the remote host will stop sending data. */
BUF->wnd[0] = BUF->wnd[1] = 0;
BUF(buf)->wnd[0] = BUF(buf)->wnd[1] = 0;
} else {
BUF->wnd[0] = ((UIP_RECEIVE_WINDOW) >> 8);
BUF->wnd[1] = ((UIP_RECEIVE_WINDOW) & 0xff);
BUF(buf)->wnd[0] = ((UIP_RECEIVE_WINDOW) >> 8);
BUF(buf)->wnd[1] = ((UIP_RECEIVE_WINDOW) & 0xff);
}
tcp_send_noconn:
BUF->proto = UIP_PROTO_TCP;
BUF(buf)->proto = UIP_PROTO_TCP;
BUF->ttl = UIP_TTL;
BUF(buf)->ttl = UIP_TTL;
#if NETSTACK_CONF_WITH_IPV6
/* For IPv6, the IP length field does not include the IPv6 IP header
length. */
BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
#else /* NETSTACK_CONF_WITH_IPV6 */
BUF->len[0] = (uip_len >> 8);
BUF->len[1] = (uip_len & 0xff);
BUF(buf)->len[0] = (uip_len(buf) >> 8);
BUF(buf)->len[1] = (uip_len(buf) & 0xff);
#endif /* NETSTACK_CONF_WITH_IPV6 */
BUF->urgp[0] = BUF->urgp[1] = 0;
BUF(buf)->urgp[0] = BUF(buf)->urgp[1] = 0;
/* Calculate TCP checksum. */
BUF->tcpchksum = 0;
BUF->tcpchksum = ~(uip_tcpchksum());
BUF(buf)->tcpchksum = 0;
BUF(buf)->tcpchksum = ~(uip_tcpchksum(buf));
#endif
ip_send_nolen:
@ -1980,19 +2035,42 @@ uip_htonl(uint32_t val)
return UIP_HTONL(val);
}
/*---------------------------------------------------------------------------*/
#if UIP_TCP
void
uip_send(struct net_buf *buf, const void *data, int len)
{
int copylen;
#define MIN(a,b) ((a) < (b)? (a): (b))
copylen = MIN(len, UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN -
(int)((char *)uip_sappdata(buf) - (char *)&uip_buf(buf)[UIP_LLH_LEN + UIP_TCPIP_HLEN]));
uip_sappdata(buf) = ip_buf_appdata(buf);
if(uip_sappdata(buf) != NULL) {
copylen = MIN(len, UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN -
(int)((char *)uip_sappdata(buf) -
(char *)&uip_buf(buf)[UIP_LLH_LEN + UIP_TCPIP_HLEN]));
} else {
copylen = MIN(len, UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN);
}
if(copylen > 0) {
uip_slen(buf) = copylen;
if(data != uip_sappdata(buf)) {
memcpy(uip_sappdata(buf), (data), uip_slen(buf));
if(uip_sappdata(buf) == NULL) {
memmove((char *)&uip_buf(buf)[UIP_LLH_LEN + UIP_TCPIP_HLEN],
(data), uip_slen(buf));
} else {
memmove(uip_sappdata(buf), (data), uip_slen(buf));
}
}
if (uip_process(buf, UIP_TCP_SEND_CONN)) {
int ret = tcpip_output(buf, NULL);
if (!ret) {
PRINTF("Packet %p sending failed.\n", buf);
ip_buf_unref(buf);
} else {
ip_buf_sent_status(buf) = 0;
}
}
}
}
#endif
/*---------------------------------------------------------------------------*/
/** @}*/

View file

@ -72,6 +72,8 @@
*/
#include <net/ip_buf.h>
#include <net/net_ip.h>
#include <errno.h>
#include "contiki/ip/uip.h"
#include "contiki/ip/uipopt.h"
@ -454,6 +456,16 @@ uip_init(void)
}
for(c = 0; c < UIP_CONNS; ++c) {
uip_conns[c].tcpstateflags = UIP_CLOSED;
uip_conns[c].len = 0;
}
{
/* Randomise initial seq number */
uint32_t c = clock_get_cycle();
iss[0] = c & 0xff;
iss[1] = (c & 0xff00) >> 8;
iss[2] = (c & 0xff0000) >> 16;
iss[3] = (c & 0xff000000) >> 24;
}
#endif /* UIP_TCP */
@ -973,15 +985,22 @@ uip_process(struct net_buf *buf, uint8_t flag)
goto udp_send;
}
#endif /* UIP_UDP */
uip_sappdata(buf) = uip_appdata(buf) = &uip_buf(buf)[UIP_IPTCPH_LEN + UIP_LLH_LEN];
#if UIP_TCP
if(flag != UIP_TCP_SEND_CONN) {
#endif
uip_sappdata(buf) = uip_appdata(buf) = &uip_buf(buf)[UIP_IPTCPH_LEN + UIP_LLH_LEN];
#if UIP_TCP
}
#endif
/* Check if we were invoked because of a poll request for a
particular connection. */
if(flag == UIP_POLL_REQUEST) {
#if UIP_TCP
if(flag == UIP_POLL_REQUEST || flag == UIP_TCP_SEND_CONN) {
if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED &&
!uip_outstanding(uip_connr)) {
uip_flags(buf) = UIP_POLL;
if (flag == UIP_POLL) {
uip_flags(buf) = UIP_POLL;
}
UIP_APPCALL(buf);
goto appsend;
#if UIP_ACTIVE_OPEN
@ -991,10 +1010,32 @@ uip_process(struct net_buf *buf, uint8_t flag)
goto tcp_send_syn;
#endif /* UIP_ACTIVE_OPEN */
}
if (flag == UIP_TCP_SEND_CONN) {
switch (uip_connr->tcpstateflags & UIP_TS_MASK) {
case UIP_CLOSED:
case UIP_FIN_WAIT_1:
case UIP_FIN_WAIT_2:
case UIP_CLOSING:
case UIP_TIME_WAIT:
ip_buf_sent_status(buf) = -ECONNABORTED;
goto drop;
}
if (uip_outstanding(uip_connr)) {
ip_buf_sent_status(buf) = -EAGAIN;
PRINTF("Retry to send packet len %d, outstanding data len %d\n",
uip_len(buf), uip_outstanding(uip_connr));
return 0;
}
}
goto drop;
}
#else /* TCP */
if(flag == UIP_POLL_REQUEST) {
goto drop;
}
#endif /* UIP_TCP */
/* Check if we were invoked because of the perodic timer fireing. */
} else if(flag == UIP_TIMER) {
if(flag == UIP_TIMER) {
/* Reset the length variables. */
#if UIP_TCP
uip_len(buf) = 0;
@ -1843,8 +1884,8 @@ uip_process(struct net_buf *buf, uint8_t flag)
#if UIP_ACTIVE_OPEN
tcp_send_synack:
UIP_TCP_BUF(buf)->flags = TCP_ACK;
tcp_send_syn:
tcp_send_syn:
UIP_TCP_BUF(buf)->flags |= TCP_SYN;
#else /* UIP_ACTIVE_OPEN */
tcp_send_synack:
@ -1877,13 +1918,21 @@ uip_process(struct net_buf *buf, uint8_t flag)
UIP_APPCALL(buf);
goto drop;
}
/* Calculate the length of the data, if the application has sent
any data to us. */
c = (UIP_TCP_BUF(buf)->tcpoffset >> 4) << 2;
/* uip_len will contain the length of the actual TCP data. This is
calculated by subtracing the length of the TCP header (in
c) and the length of the IP header (20 bytes). */
uip_len(buf) = uip_len(buf) - c - UIP_IPH_LEN;
if (flag != UIP_TCP_SEND_CONN) {
/* If flag is set to UIP_TCP_SEND_CONN, then it means that we are
* trying to send the actual packet coming from user. In this case
* do not mess with the packet length.
*/
/* Calculate the length of the data, if the application has sent
any data to us. */
c = (UIP_TCP_BUF(buf)->tcpoffset >> 4) << 2;
/* uip_len will contain the length of the actual TCP data. This is
calculated by subtracing the length of the TCP header (in
c) and the length of the IP header (20 bytes). */
uip_len(buf) = uip_len(buf) - c - UIP_IPH_LEN;
}
/* First, check if the sequence number of the incoming packet is
what we're expecting next. If not, we send out an ACK with the
@ -1903,8 +1952,10 @@ uip_process(struct net_buf *buf, uint8_t flag)
if((UIP_TCP_BUF(buf)->flags & TCP_SYN)) {
if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_SYN_RCVD) {
goto tcp_send_synack;
#if UIP_ACTIVE_OPEN
} else if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_SYN_SENT) {
goto tcp_send_syn;
#endif
}
}
goto tcp_send_ack;
@ -2141,11 +2192,16 @@ uip_process(struct net_buf *buf, uint8_t flag)
put into uip_len. If the application don't have any data to
send, uip_len must be set to 0. */
if(uip_flags(buf) & (UIP_NEWDATA | UIP_ACKDATA)) {
uip_slen(buf) = 0;
if(flag != UIP_TCP_SEND_CONN) {
/* Do not reset the slen because we are being called
* directly by the application.
*/
uip_slen(buf) = 0;
}
UIP_APPCALL(buf);
appsend:
if(uip_flags(buf) & UIP_ABORT) {
uip_slen(buf) = 0;
uip_connr->tcpstateflags = UIP_CLOSED;
@ -2287,10 +2343,14 @@ uip_process(struct net_buf *buf, uint8_t flag)
/* We jump here when we are ready to send the packet, and just want
to set the appropriate TCP sequence numbers in the TCP header. */
tcp_send_ack:
PRINTF("In tcp_send_ack\n");
UIP_TCP_BUF(buf)->flags = TCP_ACK;
tcp_send_nodata:
uip_len(buf) = UIP_IPTCPH_LEN;
if (flag != UIP_TCP_SEND_CONN) {
PRINTF("In tcp_send_nodata\n");
uip_len(buf) = UIP_IPTCPH_LEN;
}
tcp_send_noopts:
UIP_TCP_BUF(buf)->tcpoffset = (UIP_TCPH_LEN / 4) << 4;
@ -2318,9 +2378,9 @@ uip_process(struct net_buf *buf, uint8_t flag)
uip_ipaddr_copy(&UIP_IP_BUF(buf)->destipaddr, &uip_connr->ripaddr);
uip_ds6_select_src(&UIP_IP_BUF(buf)->srcipaddr, &UIP_IP_BUF(buf)->destipaddr);
PRINTF("Sending TCP packet to ");
PRINT6ADDR(&UIP_IP_BUF->destipaddr);
PRINT6ADDR(&UIP_IP_BUF(buf)->destipaddr);
PRINTF(" from ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINT6ADDR(&UIP_IP_BUF(buf)->srcipaddr);
PRINTF("\n");
if(uip_connr->tcpstateflags & UIP_STOPPED) {
@ -2360,6 +2420,7 @@ uip_process(struct net_buf *buf, uint8_t flag)
UIP_STAT(++uip_stat.ip.sent);
/* Return and let the caller do the actual transmission. */
uip_flags(buf) = 0;
buf->len = uip_len(buf);
return 1;
drop:
@ -2389,6 +2450,8 @@ uip_send(struct net_buf *buf, const void *data, int len)
int copylen;
#define MIN(a,b) ((a) < (b)? (a): (b))
uip_sappdata(buf) = ip_buf_appdata(buf);
if(uip_sappdata(buf) != NULL) {
copylen = MIN(len, UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN -
(int)((char *)uip_sappdata(buf) -
@ -2400,12 +2463,21 @@ uip_send(struct net_buf *buf, const void *data, int len)
uip_slen(buf) = copylen;
if(data != uip_sappdata(buf)) {
if(uip_sappdata(buf) == NULL) {
memcpy((char *)&uip_buf(buf)[UIP_LLH_LEN + UIP_TCPIP_HLEN],
memmove((char *)&uip_buf(buf)[UIP_LLH_LEN + UIP_TCPIP_HLEN],
(data), uip_slen(buf));
} else {
memcpy(uip_sappdata(buf), (data), uip_slen(buf));
memmove(uip_sappdata(buf), (data), uip_slen(buf));
}
}
if (uip_process(buf, UIP_TCP_SEND_CONN)) {
int ret = tcpip_ipv6_output(buf);
if (!ret) {
PRINTF("Packet %p sending failed.\n", buf);
ip_buf_unref(buf);
} else {
ip_buf_sent_status(buf) = 0;
}
}
}
}
#endif /* UIP_TCP */

View file

@ -261,7 +261,7 @@ slip_poll_handler(uint8_t *outbuf, uint16_t blen)
return 0;
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(slip_process, ev, data, not_used)
PROCESS_THREAD(slip_process, ev, data, not_used, user_data)
{
struct net_buf *buf;
@ -468,7 +468,7 @@ void slip_start(void)
uart_pipe_register(buf, sizeof(buf), recv_cb);
process_start(&slip_process, NULL);
process_start(&slip_process, NULL, NULL);
}
#endif /* defined(CONFIG_NETWORKING_UART) */

View file

@ -65,7 +65,7 @@ void uip_log(char *msg);
/*---------------------------------------------------------------------------*/
PROCESS(ctimer_process, "Ctimer process");
PROCESS_THREAD(ctimer_process, ev, data, buf)
PROCESS_THREAD(ctimer_process, ev, data, buf, user_data)
{
struct ctimer *c;
PROCESS_BEGIN();
@ -97,7 +97,7 @@ ctimer_init(void)
{
initialized = 0;
list_init(ctimer_list);
process_start(&ctimer_process, NULL);
process_start(&ctimer_process, NULL, NULL);
}
/*---------------------------------------------------------------------------*/
void

View file

@ -86,7 +86,7 @@ update_time(void)
net_timer_check();
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(etimer_process, ev, data, buf)
PROCESS_THREAD(etimer_process, ev, data, buf, user_data)
{
struct etimer *t;

View file

@ -102,7 +102,7 @@ process_alloc_event(void)
}
/*---------------------------------------------------------------------------*/
void
process_start(struct process *p, process_data_t data)
process_start(struct process *p, process_data_t data, void *user_data)
{
struct process *q;
@ -118,6 +118,7 @@ process_start(struct process *p, process_data_t data)
p->next = process_list;
process_list = p;
p->state = PROCESS_STATE_RUNNING;
p->user_data = user_data;
PT_INIT(&p->pt);
PRINTF("process: starting '%s'\n", PROCESS_NAME_STRING(p));
@ -159,7 +160,7 @@ exit_process(struct process *p, struct process *fromprocess, struct net_buf *buf
if(p->thread != NULL && p != fromprocess) {
/* Post the exit event to the process that is about to exit. */
process_current = p;
p->thread(&p->pt, PROCESS_EVENT_EXIT, NULL, buf);
p->thread(&p->pt, PROCESS_EVENT_EXIT, NULL, buf, p->user_data);
}
}
@ -195,7 +196,7 @@ call_process(struct process *p, process_event_t ev, process_data_t data,
PRINTF("process: calling process '%s' with event %d buf %p\n", PROCESS_NAME_STRING(p), ev, buf);
process_current = p;
p->state = PROCESS_STATE_CALLED;
ret = p->thread(&p->pt, ev, data, buf);
ret = p->thread(&p->pt, ev, data, buf, p->user_data);
if(ret == PT_EXITED ||
ret == PT_ENDED ||
ev == PROCESS_EVENT_EXIT) {

View file

@ -273,11 +273,12 @@ typedef unsigned char process_num_events_t;
*
* \hideinitializer
*/
#define PROCESS_THREAD(name, ev, data, buf) \
#define PROCESS_THREAD(name, ev, data, buf, user_data) \
static PT_THREAD(process_thread_##name(struct pt *process_pt, \
process_event_t ev, \
process_data_t data, \
struct net_buf *buf))
struct net_buf *buf, \
void *user_data))
/**
* Declare the name of a process.
@ -305,11 +306,11 @@ static PT_THREAD(process_thread_##name(struct pt *process_pt, \
#if PROCESS_CONF_NO_PROCESS_NAMES
#define PROCESS(name, strname) \
PROCESS_THREAD(name, ev, data); \
struct process name = { NULL, \
struct process name = { NULL, \
process_thread_##name }
#else
#define PROCESS(name, strname) \
PROCESS_THREAD(name, ev, data, buf); \
PROCESS_THREAD(name, ev, data, buf, user_data); \
struct process name = { NULL, strname, \
process_thread_##name }
#endif
@ -325,9 +326,10 @@ struct process {
#define PROCESS_NAME_STRING(process) (process)->name
#endif
PT_THREAD((* thread)(struct pt *, process_event_t, process_data_t,
struct net_buf *));
struct net_buf *, void *user_data));
struct pt pt;
unsigned char state, needspoll;
void *user_data;
};
/**
@ -344,7 +346,7 @@ struct process {
* process
*
*/
CCIF void process_start(struct process *p, process_data_t data);
CCIF void process_start(struct process *p, process_data_t data, void *user_data);
/**
* Post an asynchronous event.

View file

@ -434,10 +434,10 @@ coap_context_init(void)
coap_context_event = process_alloc_event();
process_start(&coap_context_process, NULL);
process_start(&coap_context_process, NULL, NULL);
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(coap_context_process, ev, data, buf)
PROCESS_THREAD(coap_context_process, ev, data, buf, user_data)
{
PROCESS_BEGIN();

View file

@ -311,7 +311,7 @@ void
coap_init_engine(void)
{
coap_context_init();
process_start(&coap_engine, NULL);
process_start(&coap_engine, NULL, NULL);
}
/*---------------------------------------------------------------------------*/
void
@ -347,7 +347,7 @@ coap_context_t *coap_init_server(uip_ipaddr_t *server_addr,
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(coap_engine, ev, data, buf)
PROCESS_THREAD(coap_engine, ev, data, buf, user_data)
{
PROCESS_BEGIN();
#if 0

View file

@ -20,6 +20,11 @@
* limitations under the License.
*/
#ifdef CONFIG_NETWORK_IP_STACK_DEBUG_CONTEXT
#define DEBUG 1
#endif
#include "contiki/ip/uip-debug.h"
#include <nanokernel.h>
#include <string.h>
#include <errno.h>
@ -28,12 +33,22 @@
#include <net/net_ip.h>
#include <net/net_socket.h>
#include "ip/simple-udp.h"
#include "contiki/ip/simple-udp.h"
#include "contiki/ipv6/uip-ds6.h"
#include "contiki/os/lib/random.h"
#include "contiki/ipv6/uip-ds6.h"
#ifdef CONFIG_NETWORKING_WITH_TCP
#include "contiki/os/sys/process.h"
#include "contiki/ip/psock.h"
#endif
#if !defined(CONFIG_NETWORK_IP_STACK_DEBUG_CONTEXT)
#undef NET_DBG
#define NET_DBG(...)
#endif
int net_context_get_receiver_registered(struct net_context *context);
struct net_context {
@ -46,6 +61,15 @@ struct net_context {
/* Application connection data */
union {
struct simple_udp_connection udp;
#ifdef CONFIG_NETWORKING_WITH_TCP
struct {
/* Proto socket that handles one TCP connection. */
struct psock ps;
struct process tcp;
enum net_tcp_type tcp_type;
};
#endif
};
bool receiver_registered;
@ -188,6 +212,13 @@ void net_context_put(struct net_context *context)
}
}
#ifdef CONFIG_NETWORKING_WITH_TCP
if (context->tcp_type == NET_TCP_TYPE_SERVER) {
tcp_unlisten(UIP_HTONS(context->tuple.local_port),
&context->tcp);
}
#endif
memset(&context->tuple, 0, sizeof(context->tuple));
memset(&context->udp, 0, sizeof(context->udp));
context->receiver_registered = false;
@ -222,6 +253,199 @@ net_context_get_udp_connection(struct net_context *context)
return &context->udp;
}
#ifdef CONFIG_NETWORKING_WITH_TCP
static int handle_tcp_connection(struct psock *p, enum tcp_event_type type,
struct net_buf *buf)
{
PSOCK_BEGIN(p);
if (type == TCP_WRITE_EVENT) {
NET_DBG("Trying to send %d bytes data\n", uip_appdatalen(buf));
PSOCK_SEND(p, buf);
}
PSOCK_END(p);
}
int net_context_tcp_send(struct net_buf *buf)
{
/* Prepare data to be sent */
process_post_synch(&ip_buf_context(buf)->tcp,
tcpip_event,
INT_TO_POINTER(TCP_WRITE_EVENT),
buf);
return ip_buf_sent_status(buf);
}
/* This is called by contiki/ip/tcpip.c:tcpip_uipcall() when packet
* is processed.
*/
PROCESS_THREAD(tcp, ev, data, buf, user_data)
{
PROCESS_BEGIN();
while(1) {
PROCESS_YIELD_UNTIL(ev == tcpip_event);
if (POINTER_TO_INT(data) == TCP_WRITE_EVENT) {
/* We want to send data to peer. */
struct net_context *context = user_data;
if (!context) {
continue;
}
do {
context = user_data;
if (!context || !buf) {
break;
}
if (!context->ps.net_buf ||
context->ps.net_buf != buf) {
NET_DBG("psock init %p buf %p\n",
&context->ps, buf);
PSOCK_INIT(&context->ps, buf);
}
handle_tcp_connection(&context->ps,
POINTER_TO_INT(data),
buf);
PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event);
if (POINTER_TO_INT(data) != TCP_WRITE_EVENT) {
goto read_data;
}
} while(!(uip_closed(buf) ||
uip_aborted(buf) ||
uip_timedout(buf)));
context = user_data;
if (context &&
context->tcp_type == NET_TCP_TYPE_CLIENT) {
NET_DBG("\nConnection closed.\n");
ip_buf_sent_status(buf) = -ECONNRESET;
}
continue;
}
read_data:
/* We are receiving data from peer. */
if (buf && uip_newdata(buf)) {
struct net_buf *clone;
if (!uip_len(buf)) {
continue;
}
/* Note that uIP stack will reuse the buffer when
* sending ACK to peer host. The sending will happen
* right after this function returns. Because of this
* we cannot use the same buffer to pass data to
* application.
*/
clone = net_buf_clone(buf);
if (!clone) {
NET_ERR("No enough RX buffers, "
"packet %p discarded\n", buf);
continue;
}
ip_buf_appdata(clone) = uip_buf(clone) +
(ip_buf_appdata(buf) - (void *)uip_buf(buf));
ip_buf_appdatalen(clone) = uip_len(buf);
ip_buf_len(clone) = ip_buf_len(buf);
ip_buf_context(clone) = user_data;
uip_set_conn(clone) = uip_conn(buf);
uip_flags(clone) = uip_flags(buf);
uip_flags(clone) |= UIP_CONNECTED;
NET_DBG("packet received context %p buf %p len %d "
"appdata %p appdatalen %d\n",
ip_buf_context(clone),
clone,
ip_buf_len(clone),
ip_buf_appdata(clone),
ip_buf_appdatalen(clone));
nano_fifo_put(net_context_get_queue(user_data), clone);
/* We let the application to read the data now */
fiber_yield();
}
}
PROCESS_END();
}
int net_context_tcp_init(struct net_context *context,
enum net_tcp_type tcp_type)
{
if (!context || context->tuple.ip_proto != IPPROTO_TCP) {
return -EINVAL;
}
if (context->receiver_registered) {
return 0;
}
context->receiver_registered = true;
if (context->tcp_type == NET_TCP_TYPE_UNKNOWN) {
/* This is the first call to this init func.
* If we are called by net_receive() first, then
* we are working as a server, if net_send() called
* us first, then we are the client.
*/
context->tcp_type = tcp_type;
}
context->tcp.thread = process_thread_tcp;
if (context->tcp_type == NET_TCP_TYPE_SERVER) {
context->tcp.name = "TCP server";
NET_DBG("Listen to TCP port %d\n", context->tuple.local_port);
tcp_listen(UIP_HTONS(context->tuple.local_port),
&context->tcp);
#if UIP_ACTIVE_OPEN
} else {
context->tcp.name = "TCP client";
#ifdef CONFIG_NETWORKING_WITH_IPV6
NET_DBG("Connecting to ");
PRINT6ADDR((const uip_ipaddr_t *)&context->tuple.remote_addr->in6_addr);
PRINTF(" port %d\n", context->tuple.remote_port);
tcp_connect((uip_ipaddr_t *)
&context->tuple.remote_addr->in6_addr,
UIP_HTONS(context->tuple.remote_port),
context, &context->tcp);
#else /* CONFIG_NETWORKING_WITH_IPV6 */
NET_DBG("Connecting to ");
PRINT6ADDR((const uip_ipaddr_t *)&context->tuple.remote_addr->in_addr);
PRINTF(" port %d\n", context->tuple.remote_port);
tcp_connect((uip_ipaddr_t *)
&context->tuple.remote_addr->in_addr,
UIP_HTONS(context->tuple.remote_port),
context, &context->tcp);
#endif /* CONFIG_NETWORKING_WITH_IPV6 */
#endif /* UIP_ACTIVE_OPEN */
}
context->tcp.next = NULL;
process_start(&context->tcp, NULL, context);
return 0;
}
#endif /* TCP */
void net_context_init(void)
{
int i;

View file

@ -65,6 +65,9 @@ struct simple_udp_connection *
net_context_get_udp_connection(struct net_context *context);
int net_context_get_receiver_registered(struct net_context *context);
void net_context_set_receiver_registered(struct net_context *context);
int net_context_tcp_init(struct net_context *context,
enum net_tcp_type);
int net_context_tcp_send(struct net_buf *buf);
/* Stacks for the tx & rx fibers.
* FIXME: stack size needs fine-tuning
@ -102,6 +105,22 @@ int net_send(struct net_buf *buf)
return -ENODATA;
}
#ifdef CONFIG_NETWORKING_WITH_TCP
net_context_tcp_init(ip_buf_context(buf), NET_TCP_TYPE_CLIENT);
if (ip_buf_context(buf)) {
if (net_context_get_tuple(ip_buf_context(buf))->ip_proto ==
IPPROTO_TCP) {
if (uip_conn(buf) && uip_conn(buf)->len > 0) {
/* There is already pending packet to be sent.
* Application needs to try to send data
* a bit later.
*/
return -EAGAIN;
}
}
}
#endif
nano_fifo_put(&netdev.tx_queue, buf);
return 0;
@ -154,6 +173,21 @@ static void stats(void)
STAT(ip.chkerr),
STAT(ip.protoerr));
#ifdef CONFIG_NETWORKING_WITH_TCP
NET_DBG("TCP recv %d\tsent\t%d\tdrop\t%d\n",
STAT(tcp.recv),
STAT(tcp.sent),
STAT(tcp.drop));
NET_DBG("TCP chkerr %d\tackerr\t%d\trst\t%d\n",
STAT(tcp.chkerr),
STAT(tcp.ackerr),
STAT(tcp.rst));
NET_DBG("TCP rexmit %d\tsyndrop\t%d\tsynrst\t%d\n",
STAT(tcp.rexmit),
STAT(tcp.syndrop),
STAT(tcp.synrst));
#endif
NET_DBG("ICMP recv %d\tsent\t%d\tdrop\t%d\n",
STAT(icmp.recv),
STAT(icmp.sent),
@ -308,6 +342,87 @@ static inline int udp_prepare_and_send(struct net_context *context,
return !ret;
}
#ifdef CONFIG_NETWORKING_WITH_TCP
/* Switch the ports and addresses. Returns 1 if packet was sent properly,
* in this case it is the caller that needs to release the net_buf.
* If 0 is returned, then uIP stack has released the net_buf already
* because there was an some net related error when sending the buffer.
*/
static inline int tcp_prepare_and_send(struct net_context *context,
struct net_buf *buf)
{
#ifdef CONFIG_NETWORKING_IPV6_NO_ND
uip_ds6_route_t *route_old, *route_new = NULL;
uip_ds6_nbr_t *nbr;
#endif
uip_ipaddr_t tmp;
uint16_t port;
int ret;
uip_len(buf) = uip_slen(buf) = ip_buf_len(buf);
uip_flags(buf) |= UIP_NEWDATA;
port = NET_BUF_UDP(buf)->srcport;
NET_BUF_UDP(buf)->srcport = NET_BUF_UDP(buf)->destport;
NET_BUF_UDP(buf)->destport = port;
uip_ipaddr_copy(&tmp, &NET_BUF_IP(buf)->srcipaddr);
uip_ipaddr_copy(&NET_BUF_IP(buf)->srcipaddr,
&NET_BUF_IP(buf)->destipaddr);
uip_ipaddr_copy(&NET_BUF_IP(buf)->destipaddr, &tmp);
#ifdef CONFIG_NETWORKING_IPV6_NO_ND
/* The peer needs to be in neighbor cache before route can be added.
*/
nbr = uip_ds6_nbr_lookup((uip_ipaddr_t *)&NET_BUF_IP(buf)->destipaddr);
if (!nbr) {
const uip_lladdr_t *lladdr =
(const uip_lladdr_t *)&ip_buf_ll_src(buf);
nbr = uip_ds6_nbr_add(
(uip_ipaddr_t *)&NET_BUF_IP(buf)->destipaddr,
lladdr, 0, NBR_REACHABLE);
if (!nbr) {
NET_DBG("Cannot add peer ");
PRINT6ADDR(&NET_BUF_IP(buf)->destipaddr);
PRINT(" to neighbor cache\n");
}
}
/* Temporarily add route to peer, delete the route after
* sending the packet. Check if there was already a
* route and do not remove it if there was existing
* route to this peer.
*/
route_old = uip_ds6_route_lookup(&NET_BUF_IP(buf)->destipaddr);
if (!route_old) {
route_new = uip_ds6_route_add(&NET_BUF_IP(buf)->destipaddr,
128,
&NET_BUF_IP(buf)->destipaddr);
if (!route_new) {
NET_DBG("Cannot add route to peer ");
PRINT6ADDR(&NET_BUF_IP(buf)->destipaddr);
PRINT("\n");
}
}
#endif
NET_DBG("Packet output len %d\n", uip_len(buf));
ret = net_context_tcp_send(buf);
if (ret && ret != -EAGAIN) {
NET_DBG("Packet could not be sent properly.\n");
}
#ifdef CONFIG_NETWORKING_IPV6_NO_ND
if (!route_old && route_new) {
/* This will also remove the neighbor cache entry */
uip_ds6_route_rm(route_new);
}
#endif
return ret;
}
#endif
/* Application wants to send a reply */
int net_reply(struct net_context *context, struct net_buf *buf)
{
@ -335,8 +450,13 @@ int net_reply(struct net_context *context, struct net_buf *buf)
ret = udp_prepare_and_send(context, buf);
break;
case IPPROTO_TCP:
NET_DBG("TCP not yet supported\n");
#ifdef CONFIG_NETWORKING_WITH_TCP
ret = tcp_prepare_and_send(context, buf);
#else
NET_DBG("TCP not supported\n");
return -EINVAL;
#endif
break;
case IPPROTO_ICMPV6:
NET_DBG("ICMPv6 not yet supported\n");
return -EINVAL;
@ -446,8 +566,18 @@ struct net_buf *net_receive(struct net_context *context, int32_t timeout)
reserve = UIP_IPUDPH_LEN + UIP_LLH_LEN;
break;
case IPPROTO_TCP:
NET_DBG("TCP not yet supported\n");
#ifdef CONFIG_NETWORKING_WITH_TCP
ret = net_context_tcp_init(context, NET_TCP_TYPE_SERVER);
if (ret) {
NET_DBG("TCP connection init failed\n");
ret = -ENOENT;
break;
}
ret = 0;
#else
NET_DBG("TCP not supported\n");
ret = -EINVAL;
#endif
break;
case IPPROTO_ICMPV6:
NET_DBG("ICMPv6 not yet supported\n");
@ -480,6 +610,14 @@ struct net_buf *net_receive(struct net_context *context, int32_t timeout)
break;
}
#ifdef CONFIG_NETWORKING_WITH_TCP
if (tuple->ip_proto == IPPROTO_TCP &&
(ip_buf_appdata(buf) > (void *)buf->data)) {
/* We need to skip the TCP header + possible extensions */
reserve = ip_buf_appdata(buf) - (void *)buf->data;
}
#endif
if (buf && reserve) {
ip_buf_appdatalen(buf) = ip_buf_len(buf) - reserve;
ip_buf_appdata(buf) = &uip_buf(buf)[reserve];
@ -573,8 +711,26 @@ static int check_and_send_packet(struct net_buf *buf)
uip_appdatalen(buf));
break;
case IPPROTO_TCP:
NET_DBG("TCP not yet supported\n");
#ifdef CONFIG_NETWORKING_WITH_TCP
if (ip_buf_appdatalen(buf) == 0) {
/* User application has not set the application data
* length. The buffer will be discarded if we do not
* set the value correctly.
*/
uip_appdatalen(buf) = buf->len -
(UIP_IPTCPH_LEN + UIP_LLH_LEN);
}
if (uip_len(buf) == 0) {
uip_len(buf) = buf->len;
}
ret = net_context_tcp_send(buf);
if (ret && ret != -EAGAIN) {
NET_DBG("Packet could not be sent properly.\n");
}
#else
NET_DBG("TCP not supported\n");
ret = -EINVAL;
#endif
break;
case IPPROTO_ICMPV6:
NET_DBG("ICMPv6 not yet supported\n");
@ -812,10 +968,10 @@ static int network_initialization(void)
process_init();
tcpip_set_outputfunc(net_tcpip_output);
process_start(&tcpip_process, NULL);
process_start(&simple_udp_process, NULL);
process_start(&etimer_process, NULL);
process_start(&ctimer_process, NULL);
process_start(&tcpip_process, NULL, NULL);
process_start(&simple_udp_process, NULL, NULL);
process_start(&etimer_process, NULL, NULL);
process_start(&ctimer_process, NULL, NULL);
slip_start();

View file

@ -79,7 +79,7 @@ rest_init_engine(void)
REST.init();
/*Start REST engine process */
process_start(&rest_engine_process, NULL);
process_start(&rest_engine_process, NULL, NULL);
}
/*---------------------------------------------------------------------------*/
/**
@ -175,7 +175,7 @@ rest_invoke_restful_service(void *request, void *response, uint8_t *buffer,
return found & allowed;
}
/*-----------------------------------------------------------------------------------*/
PROCESS_THREAD(rest_engine_process, ev, data, buf)
PROCESS_THREAD(rest_engine_process, ev, data, buf, user_data)
{
PROCESS_BEGIN();

View file

@ -3791,7 +3791,7 @@ dtls_new_context(void *app_data) {
LIST_STRUCT_INIT(c, peers);
/* LIST_STRUCT_INIT(c, key_store); */
process_start(&dtls_retransmit_process, (char *)c);
process_start(&dtls_retransmit_process, (char *)c, NULL);
PROCESS_CONTEXT_BEGIN(&dtls_retransmit_process);
/* the retransmit timer must be initialized to some large value */
etimer_set(&c->retransmit_timer, 0xFFFF, &dtls_retransmit_process);
@ -3992,7 +3992,7 @@ dtls_check_retransmit(dtls_context_t *context, clock_time_t *next) {
/*---------------------------------------------------------------------------*/
/* message retransmission */
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(dtls_retransmit_process, ev, data, buf)
PROCESS_THREAD(dtls_retransmit_process, ev, data, buf, user_data)
{
clock_time_t now;
netq_t *node;

View file

@ -683,7 +683,7 @@ static dtls_handler_t cb = {
PROCESS(server_process, "DTLS server process");
AUTOSTART_PROCESSES(&server_process);
PROCESS_THREAD(server_process, ev, data)
PROCESS_THREAD(server_process, ev, data, user_data)
{
PROCESS_BEGIN();

View file

@ -273,7 +273,7 @@ void toggle_observation(coap_context_t *coap_ctx)
#endif
PROCESS(coap_observe_client_process, "CoAP observe client process");
PROCESS_THREAD(coap_observe_client_process, ev, data, buf)
PROCESS_THREAD(coap_observe_client_process, ev, data, buf, user_data)
{
PROCESS_BEGIN();

View file

@ -219,6 +219,7 @@ static inline bool get_context(struct net_context **unicast,
static struct net_addr any_addr;
static struct net_addr peer_addr;
static struct net_addr my_addr;
int proto = IPPROTO_UDP;
#if defined(CONFIG_NETWORKING_WITH_IPV6)
static const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
@ -254,7 +255,11 @@ static inline bool get_context(struct net_context **unicast,
my_addr.family = AF_INET;
#endif
*unicast = net_context_get(IPPROTO_UDP,
#ifdef CONFIG_NETWORKING_WITH_TCP
proto = IPPROTO_TCP;
#endif /* CONFIG_NETWORKING_WITH_TCP */
*unicast = net_context_get(proto,
&peer_addr, PEER_PORT,
&my_addr, MY_PORT);
if (!*unicast) {

View file

@ -150,6 +150,7 @@ static inline bool get_context(struct net_context **recv,
static struct net_addr mcast_addr;
static struct net_addr any_addr;
static struct net_addr my_addr;
int proto = IPPROTO_UDP;
#if defined(CONFIG_NETWORKING_WITH_IPV6)
static const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
@ -178,7 +179,11 @@ static inline bool get_context(struct net_context **recv,
my_addr.family = AF_INET;
#endif
*recv = net_context_get(IPPROTO_UDP,
#ifdef CONFIG_NETWORKING_WITH_TCP
proto = IPPROTO_TCP;
#endif /* CONFIG_NETWORKING_WITH_TCP */
*recv = net_context_get(proto,
&any_addr, 0,
&my_addr, MY_PORT);
if (!*recv) {