net: tcp: Supporting TCP client functionality when using IPv6

The application can initiate TCP connection when using IPv6.

Change-Id: I632fa2559d5deb40d71288762bd13fbc2aac9cc7
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2016-05-16 09:43:22 +03:00
commit 61edc68c9e
8 changed files with 263 additions and 102 deletions

View file

@ -220,7 +220,7 @@ packet_input(struct net_buf *buf)
#if UIP_ACTIVE_OPEN
struct uip_conn *
tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate,
struct process *process)
struct process *process, struct net_buf *buf)
{
struct uip_conn *c;
@ -232,7 +232,7 @@ tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate,
c->appstate.p = process;
c->appstate.state = appstate;
tcpip_poll_tcp(c);
tcpip_poll_tcp(c, buf);
return c;
}
@ -837,12 +837,18 @@ tcpip_poll_udp(struct uip_udp_conn *conn)
/*---------------------------------------------------------------------------*/
#if UIP_TCP
void
tcpip_poll_tcp(struct uip_conn *conn)
tcpip_poll_tcp(struct uip_conn *conn, struct net_buf *data_buf)
{
/* We are sending here the initial SYN */
struct net_buf *buf = ip_buf_get_tx(conn->appstate.state);
uip_set_conn(buf) = conn;
conn->buf = ip_buf_ref(buf);
uip_set_conn(data_buf) = conn;
/* The conn->buf will be freed after we have established the connection,
* sent the message and received an ack to it. This will happen in
* net_core.c:net_send().
*/
conn->buf = ip_buf_ref(data_buf);
process_post_synch(&tcpip_process, TCP_POLL, conn, buf);
}
@ -899,7 +905,6 @@ tcpip_uipcall(struct net_buf *buf)
}
}
#endif /* UIP_TCP */
if(ts->p != NULL) {
process_post_synch(ts->p, tcpip_event, ts->state, buf);
}

View file

@ -167,7 +167,8 @@ CCIF void tcp_unlisten(uint16_t port, struct process *handler);
*
*/
CCIF struct uip_conn *tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port,
void *appstate, struct process *process);
void *appstate, struct process *process,
struct net_buf *buf);
/**
* Cause a specified TCP connection to be polled.
@ -180,7 +181,7 @@ CCIF struct uip_conn *tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port,
* \param conn A pointer to the TCP connection that should be polled.
*
*/
void tcpip_poll_tcp(struct uip_conn *conn);
void tcpip_poll_tcp(struct uip_conn *conn, struct net_buf *data_buf);
void tcpip_resend_syn(struct uip_conn *conn, struct net_buf *buf);

View file

@ -62,7 +62,7 @@ uip_udp_packet_send(struct net_buf *buf, struct uip_udp_conn *c, const void *dat
memcpy(&uip_buf(buf)[UIP_LLH_LEN + UIP_IPUDPH_LEN], data,
len > UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN?
UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN: len);
if (uip_process(buf, UIP_UDP_SEND_CONN) == 0) {
if (uip_process(&buf, UIP_UDP_SEND_CONN) == 0) {
/* The packet was dropped, we can return now */
return 0;
}

View file

@ -284,8 +284,7 @@ void uip_setipid(uint16_t id);
*
* \hideinitializer
*/
#define uip_input(buf) uip_process(buf, UIP_DATA)
#define uip_input(buf) uip_process(&buf, UIP_DATA)
/**
* Periodic processing for a connection identified by its number.
@ -331,7 +330,7 @@ void uip_setipid(uint16_t id);
*/
#if UIP_TCP
#define uip_periodic(buf, conn) do { uip_set_conn(buf) = &uip_conns[conn]; \
uip_process(buf, UIP_TIMER); } while (0)
uip_process(&buf, UIP_TIMER); } while (0)
/**
* Macro to determine whether a specific uIP connection is active
@ -355,7 +354,7 @@ void uip_setipid(uint16_t id);
* \hideinitializer
*/
#define uip_periodic_conn(buf, conn) do { uip_set_conn(buf) = conn; \
uip_process(buf, UIP_TIMER); } while (0)
uip_process(&buf, UIP_TIMER); } while (0)
/**
* Request that a particular connection should be polled.
@ -369,7 +368,7 @@ void uip_setipid(uint16_t id);
* \hideinitializer
*/
#define uip_poll_conn(buf, conn) do { uip_set_conn(buf) = conn; \
uip_process(buf, UIP_POLL_REQUEST); } while (0)
uip_process(&buf, UIP_POLL_REQUEST); } while (0)
#endif /* UIP_TCP */
@ -423,7 +422,7 @@ void uip_setipid(uint16_t id);
* \hideinitializer
*/
#define uip_udp_periodic_conn(buf, conn) do { uip_set_udp_conn(buf) = conn; \
uip_process(buf, UIP_UDP_TIMER); } while(0)
uip_process(&buf, UIP_UDP_TIMER); } while(0)
#endif /* UIP_UDP */
/** \brief Abandon the reassembly of the current packet */
@ -1299,7 +1298,6 @@ extern uint8_t uip_ext_len;
extern uint16_t uip_urglen, uip_surglen;
#endif /* UIP_URGDATA > 0 */
/**
* Representation of a uIP TCP connection.
*
@ -1569,7 +1567,7 @@ uip_ext_hdr_options_process(); */
*
* The actual uIP function which does all the work.
*/
uint8_t uip_process(struct net_buf *buf, uint8_t flag);
uint8_t uip_process(struct net_buf **buf, uint8_t flag);
/* The following flags are passed as an argument to the uip_process()
function. They are used to distinguish between the two cases where

View file

@ -678,8 +678,9 @@ uip_add_rcv_nxt(struct net_buf *buf, uint16_t n)
#endif /* UIP_TCP */
/*---------------------------------------------------------------------------*/
uint8_t
uip_process(struct net_buf *buf, uint8_t flag)
uip_process(struct net_buf **buf_out, uint8_t flag)
{
struct net_buf *buf = &buf_out;
#if UIP_TCP
register struct uip_conn *uip_connr = uip_conn(buf);
#endif

View file

@ -92,6 +92,9 @@ extern void net_context_set_connection_status(struct net_context *context,
void *net_context_get_internal_connection(struct net_context *context);
void net_context_set_internal_connection(struct net_context *context,
void *conn);
struct net_context *net_context_find_internal_connection(void *conn);
void net_context_tcp_set_pending(struct net_context *context,
struct net_buf *buf);
/*---------------------------------------------------------------------------*/
/* For Debug, logging, statistics */
@ -988,7 +991,7 @@ static inline void handle_tcp_retransmit_timer(struct net_buf *not_used,
PRINTF("%s: connection %p buf %p\n", __func__, conn, conn ? conn->buf : 0);
if (conn && conn->buf) {
conn->timer = 0;
if (uip_process(conn->buf, UIP_TIMER)) {
if (uip_process(&conn->buf, UIP_TIMER)) {
tcpip_resend_syn(conn, conn->buf);
}
}
@ -1008,8 +1011,9 @@ static inline void tcp_cancel_retrans_timer(struct uip_conn *conn)
/*---------------------------------------------------------------------------*/
uint8_t
uip_process(struct net_buf *buf, uint8_t flag)
uip_process(struct net_buf **buf_out, uint8_t flag)
{
struct net_buf *buf = *buf_out;
#if UIP_TCP
register struct uip_conn *uip_connr = uip_conn(buf);
uint8_t c;
@ -1020,6 +1024,7 @@ 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
@ -1102,7 +1107,7 @@ uip_process(struct net_buf *buf, uint8_t flag)
#if UIP_TCP
uip_len(buf) = 0;
uip_slen(buf) = 0;
/* Increase the initial sequence number. */
if(++iss[3] == 0) {
if(++iss[2] == 0) {
@ -1125,6 +1130,11 @@ uip_process(struct net_buf *buf, uint8_t flag)
uip_connr->tcpstateflags = UIP_CLOSED;
}
} else if(uip_connr->tcpstateflags != UIP_CLOSED) {
if (!uip_connr->buf) {
/* There cannot be any data pending if buf is NULL */
uip_outstanding(uip_connr) = 0;
}
/*
* If the connection has outstanding data, we increase the
* connection's timer and see if it has reached the RTO value
@ -1137,7 +1147,6 @@ uip_process(struct net_buf *buf, uint8_t flag)
uip_connr->tcpstateflags == UIP_SYN_RCVD) &&
uip_connr->nrtx == UIP_MAXSYNRTX)) {
uip_connr->tcpstateflags = UIP_CLOSED;
/*
* We call UIP_APPCALL() with uip_flags set to
* UIP_TIMEDOUT to inform the application that the
@ -1901,7 +1910,13 @@ uip_process(struct net_buf *buf, uint8_t flag)
uip_connr->snd_nxt[2] = iss[2];
uip_connr->snd_nxt[3] = iss[3];
uip_connr->len = 1;
uip_connr->buf = ip_buf_ref(buf);
if (flag == UIP_TCP_SEND_CONN) {
/* So we are trying send some data to other host */
if (uip_connr->buf && uip_connr->buf != buf) {
uip_connr->buf = ip_buf_ref(buf);
}
}
/* rcv_nxt should be the seqno from the incoming packet + 1. */
uip_connr->rcv_nxt[3] = UIP_TCP_BUF(buf)->seqno[3];
@ -2150,6 +2165,7 @@ tcp_send_syn:
uip_slen(buf) = 0;
ip_buf_sent_status(buf) = 0;
uip_set_conn(buf) = uip_connr;
if (uip_connr->buf) {
/* Now that we know the original connection request, clear
* the buf in connr
@ -2158,11 +2174,26 @@ tcp_send_syn:
net_context_set_internal_connection(ip_buf_context(uip_connr->buf),
uip_connr);
tcp_cancel_retrans_timer(uip_connr);
ip_buf_unref(uip_connr->buf);
uip_connr->buf = NULL;
/* We received ACK for syn */
if (uip_connr->buf) {
net_context_set_connection_status(ip_buf_context(uip_connr->buf), -EINPROGRESS);
}
/* Now send the pending data */
buf = uip_connr->buf;
*buf_out = buf;
uip_flags(buf) = UIP_CONNECTED;
}
UIP_APPCALL(buf);
goto appsend;
/* Right now we have received SYN-ACK so we can now start to send data.
* The UIP_APPCALL() will cause a call to this function by the
* net_context.c TCP process thread which will call
* handle_tcp_connection().
*/
UIP_APPCALL(buf);
PRINTF("Returning now buf %p ref %p\n", buf, buf->ref);
return 0;
}
/* Inform the application that the connection failed */
uip_flags(buf) = UIP_ABORT;
@ -2275,6 +2306,33 @@ tcp_send_syn:
*/
uip_slen(buf) = 0;
}
if (uip_connr->buf) {
net_context_tcp_set_pending(ip_buf_context(uip_connr->buf), NULL);
net_context_set_internal_connection(ip_buf_context(uip_connr->buf),
uip_connr);
/* At this point we have received ACK to data in uip_connr->buf */
/* This is not an error but tells net_core.c:net_send() that
* user should be able to send now more data.
*/
net_context_set_connection_status(ip_buf_context(uip_connr->buf),
EISCONN);
/* Eventually the uip_connr->buf will be freed
* by net_core.c:net_send()
*/
tcp_cancel_retrans_timer(uip_connr);
} else {
/* We have no pending data so this will cause ACK to be sent to
* peer in few lines below.
*/
uip_flags(buf) |= UIP_NEWDATA;
}
UIP_APPCALL(buf);
appsend:
@ -2323,6 +2381,15 @@ tcp_send_syn:
PRINTF("Setting connection %p to pending length %d\n",
uip_connr, uip_connr->len);
if (uip_connr->buf) {
if (uip_connr->buf != buf) {
PRINTF("Data packet %p already pending....\n",
uip_connr->buf);
}
} else {
uip_connr->buf = ip_buf_ref(buf);
}
} else {
/* If the application already had unacknowledged data, we
@ -2567,11 +2634,13 @@ uip_send(struct net_buf *buf, const void *data, int len)
memmove(uip_sappdata(buf), (data), uip_slen(buf));
}
}
if (uip_process(buf, UIP_TCP_SEND_CONN)) {
int ret = tcpip_ipv6_output(buf);
if (uip_process(&buf, UIP_TCP_SEND_CONN)) {
int ret;
ret = tcpip_ipv6_output(buf);
if (!ret) {
PRINTF("Packet %p sending failed.\n", buf);
ip_buf_unref(buf);
ip_buf_sent_status(buf) = -EAGAIN;
}
}
}

View file

@ -70,7 +70,7 @@ struct net_context {
enum net_tcp_type tcp_type;
int connection_status;
void *conn;
struct net_buf *last_sent;
struct net_buf *pending;
};
#endif
};
@ -272,53 +272,25 @@ static int handle_tcp_connection(struct psock *p, enum tcp_event_type type,
int net_context_tcp_send(struct net_buf *buf)
{
bool connected, reset;
/* Prepare data to be sent */
/* If we have already tried to send this buf, then set the flag
* accordingly so that psock can actually try to send the data.
*/
if (ip_buf_context(buf)->last_sent == buf) {
uip_flags(buf) |= UIP_REXMIT;
} else {
ip_buf_context(buf)->last_sent = buf;
}
ip_buf_ref(buf);
process_post_synch(&ip_buf_context(buf)->tcp,
tcpip_event,
INT_TO_POINTER(TCP_WRITE_EVENT),
buf);
connected = uip_flags(buf) & UIP_CONNECTED;
reset = uip_flags(buf) & UIP_ABORT;
/* If the buffer ref is 1, then the buffer was sent and it
* is cleared already.
*/
if (buf->ref == 1) {
ip_buf_unref(buf);
return 0;
}
ip_buf_unref(buf);
/* If we get -EAGAIN, then we need to retry the packet sending
* after we have received ACK from peer. Store the packet to be
* sent out later.
*/
if (ip_buf_sent_status(buf) == -EAGAIN) {
if (!uip_conn(buf)) {
;
} else if (!uip_conn(buf)->buf) {
uip_conn(buf)->buf = ip_buf_ref(buf);
} else {
/* We just could not send the packet and thus need to
* discard it.
*/
ip_buf_sent_status(buf) = -EBUSY;
}
}
ip_buf_context(buf)->connection_status = ip_buf_sent_status(buf);
return ip_buf_sent_status(buf);
}
@ -327,11 +299,16 @@ int net_context_tcp_send(struct net_buf *buf)
*/
PROCESS_THREAD(tcp, ev, data, buf, user_data)
{
NET_DBG("tcp %p ev %p data %p buf %p user_data %p next line %d\n",
process_thread_tcp, ev, data, buf, user_data,
process_pt->lc);
PROCESS_BEGIN();
while(1) {
PROCESS_YIELD_UNTIL(ev == tcpip_event);
try_send:
if (POINTER_TO_INT(data) == TCP_WRITE_EVENT) {
/* We want to send data to peer. */
struct net_context *context = user_data;
@ -389,6 +366,23 @@ PROCESS_THREAD(tcp, ev, data, buf, user_data)
}
continue;
} else {
if (buf && uip_aborted(buf)) {
struct net_context *context = user_data;
NET_DBG("Connection aborted context %p\n",
user_data);
context->connection_status = -ECONNRESET;
continue;
}
if (buf && uip_connected(buf)) {
struct net_context *context = user_data;
NET_DBG("Connection established context %p\n",
user_data);
context->connection_status = -EALREADY;
data = INT_TO_POINTER(TCP_WRITE_EVENT);
goto try_send;
}
}
read_data:
@ -418,6 +412,9 @@ PROCESS_THREAD(tcp, ev, data, buf, user_data)
ip_buf_appdatalen(clone) = uip_len(buf);
ip_buf_len(clone) = ip_buf_len(buf);
ip_buf_context(clone) = user_data;
if (!ip_buf_context(buf)) {
ip_buf_context(buf) = user_data;
}
uip_set_conn(clone) = uip_conn(buf);
uip_flags(clone) = uip_flags(buf);
uip_flags(clone) |= UIP_CONNECTED;
@ -442,7 +439,7 @@ PROCESS_THREAD(tcp, ev, data, buf, user_data)
PROCESS_END();
}
int net_context_tcp_init(struct net_context *context,
int net_context_tcp_init(struct net_context *context, struct net_buf *buf,
enum net_tcp_type tcp_type)
{
if (!context || context->tuple.ip_proto != IPPROTO_TCP) {
@ -462,6 +459,12 @@ int net_context_tcp_init(struct net_context *context,
* us first, then we are the client.
*/
context->tcp_type = tcp_type;
} else if (context->tcp_type != tcp_type) {
/* This means that we have already selected that we
* are either client or server. Use the context
* value.
*/
return 0;
}
context->tcp.thread = process_thread_tcp;
@ -475,7 +478,7 @@ int net_context_tcp_init(struct net_context *context,
#if UIP_ACTIVE_OPEN
} else {
context->tcp.name = "TCP client";
context->connection_status = -EAGAIN;
context->connection_status = -EINPROGRESS;
#ifdef CONFIG_NETWORKING_WITH_IPV6
NET_DBG("Connecting to ");
@ -485,7 +488,7 @@ int net_context_tcp_init(struct net_context *context,
tcp_connect((uip_ipaddr_t *)
&context->tuple.remote_addr->in6_addr,
UIP_HTONS(context->tuple.remote_port),
context, &context->tcp);
context, &context->tcp, buf);
#else /* CONFIG_NETWORKING_WITH_IPV6 */
NET_DBG("Connecting to ");
PRINT6ADDR((const uip_ipaddr_t *)&context->tuple.remote_addr->in_addr);
@ -494,7 +497,7 @@ int net_context_tcp_init(struct net_context *context,
tcp_connect((uip_ipaddr_t *)
&context->tuple.remote_addr->in_addr,
UIP_HTONS(context->tuple.remote_port),
context, &context->tcp);
context, &context->tcp, buf);
#endif /* CONFIG_NETWORKING_WITH_IPV6 */
#endif /* UIP_ACTIVE_OPEN */
}
@ -543,6 +546,15 @@ void net_context_set_receiver_registered(struct net_context *context)
context->receiver_registered = true;
}
void net_context_unset_receiver_registered(struct net_context *context)
{
if (!context) {
return;
}
context->receiver_registered = false;
}
int net_context_get_connection_status(struct net_context *context)
{
if (!context) {
@ -571,6 +583,7 @@ void net_context_set_connection_status(struct net_context *context,
}
if (context->tuple.ip_proto == IPPROTO_TCP) {
NET_DBG("context %p status %d\n", context, status);
context->connection_status = status;
}
#endif
@ -608,3 +621,47 @@ void net_context_set_internal_connection(struct net_context *context,
}
#endif
}
struct net_context *net_context_find_internal_connection(void *conn)
{
#if !defined(CONFIG_NETWORKING_WITH_TCP)
return NULL;
#else
int i;
for (i = 0; i < NET_MAX_CONTEXT; i++) {
if (contexts[i].conn == conn) {
return &contexts[i];
}
}
return NULL;
#endif
}
struct net_buf *net_context_tcp_get_pending(struct net_context *context)
{
#if !defined(CONFIG_NETWORKING_WITH_TCP)
return NULL;
#else
if (!context) {
return NULL;
}
return context->pending;
#endif
}
void net_context_tcp_set_pending(struct net_context *context,
struct net_buf *buf)
{
#if !defined(CONFIG_NETWORKING_WITH_TCP)
return;
#else
if (!context) {
return;
}
context->pending = buf;
#endif
}

View file

@ -65,9 +65,16 @@ 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,
int net_context_tcp_init(struct net_context *context, struct net_buf *buf,
enum net_tcp_type);
int net_context_tcp_send(struct net_buf *buf);
void *net_context_get_internal_connection(struct net_context *context);
struct net_buf *net_context_tcp_get_pending(struct net_context *context);
void net_context_tcp_set_pending(struct net_context *context,
struct net_buf *buf);
void net_context_set_connection_status(struct net_context *context,
int status);
void net_context_unset_receiver_registered(struct net_context *context);
/* Stacks for the tx & rx fibers.
* FIXME: stack size needs fine-tuning
@ -103,46 +110,65 @@ int net_send(struct net_buf *buf)
{
int ret = 0;
if (ip_buf_len(buf) == 0) {
if (!buf || ip_buf_len(buf) == 0) {
return -ENODATA;
}
if (buf->len && !uip_appdatalen(buf)) {
uip_appdatalen(buf) = buf->len;
}
#ifdef CONFIG_NETWORKING_WITH_TCP
#define MAX_TCP_RETRY_COUNT 5
net_context_tcp_init(ip_buf_context(buf), NET_TCP_TYPE_CLIENT);
if (ip_buf_context(buf)) {
if (net_context_get_connection_status(
ip_buf_context(buf)) == -ETIMEDOUT) {
return -ETIMEDOUT;
}
if (ip_buf_tcp_retry_count(buf) < MAX_TCP_RETRY_COUNT) {
int ret;
if (net_context_get_tuple(
ip_buf_context(buf))->ip_proto ==
if (ip_buf_context(buf) &&
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.
* Do not wait forever thou so that
* the connection can proceed if
* needed.
*/
ip_buf_tcp_retry_count(buf)++;
return -EAGAIN;
}
ret = net_context_get_connection_status(
ip_buf_context(buf));
if (ret < 0) {
ip_buf_tcp_retry_count(buf)++;
return ret;
struct uip_conn *conn;
int status;
net_context_tcp_init(ip_buf_context(buf), buf,
NET_TCP_TYPE_CLIENT);
status = net_context_get_connection_status(
ip_buf_context(buf));
NET_DBG("context %p buf %p status %d\n",
ip_buf_context(buf), buf, status);
switch (status) {
case EISCONN:
/* User should be able to send new data now. */
NET_DBG("Send new data buf %p ref %d\n", buf, buf->ref);
net_context_set_connection_status(ip_buf_context(buf),
0);
conn = (struct uip_conn *)net_context_get_internal_connection(ip_buf_context(buf));
if (conn->buf) {
ip_buf_unref(conn->buf);
if (conn->buf == buf) {
conn->buf = NULL;
return 0;
}
conn->buf = NULL;
}
} else {
ip_buf_tcp_retry_count(buf) = 0;
ret = -EAGAIN;
break;
case -EALREADY:
NET_DBG("Connection established\n");
return 0;
case -EINPROGRESS:
NET_DBG("Connection being established\n");
return status;
case -ECONNRESET:
NET_DBG("Connection reset\n");
net_context_unset_receiver_registered(
ip_buf_context(buf));
return status;
}
ret = status;
}
#endif
@ -596,7 +622,7 @@ struct net_buf *net_receive(struct net_context *context, int32_t timeout)
break;
case IPPROTO_TCP:
#ifdef CONFIG_NETWORKING_WITH_TCP
ret = net_context_tcp_init(context, NET_TCP_TYPE_SERVER);
ret = net_context_tcp_init(context, NULL, NET_TCP_TYPE_SERVER);
if (ret) {
NET_DBG("TCP connection init failed\n");
ret = -ENOENT;
@ -763,7 +789,11 @@ static int check_and_send_packet(struct net_buf *buf)
*/
ret = 1;
} else {
ip_buf_sent_status(buf) = 0;
ip_buf_sent_status(buf) = ret;
ret = true; /* This will prevent caller to discard
* the buffer that needs to be resent
* again.
*/
}
#else
NET_DBG("TCP not supported\n");