Free (GPLv2) TCP/IP stack developed by TASS Belgium

Dependents:   lpc1768-picotcp-demo ZeroMQ_PicoTCP_Publisher_demo TCPSocket_HelloWorld_PicoTCP Pico_TCP_UDP_Test ... more

PicoTCP. Copyright (c) 2013 TASS Belgium NV.

Released under the GNU General Public License, version 2.

Different licensing models may exist, at the sole discretion of the Copyright holders.

Official homepage: http://www.picotcp.com

Bug tracker: https://github.com/tass-belgium/picotcp/issues

Development steps:

  • initial integration with mbed RTOS
  • generic mbed Ethernet driver
  • high performance NXP LPC1768 specific Ethernet driver
  • Multi-threading support for mbed RTOS
  • Berkeley sockets and integration with the New Socket API
  • Fork of the apps running on top of the New Socket API
  • Scheduling optimizations
  • Debugging/benchmarking/testing

Demo application (measuring TCP sender performance):

Import programlpc1768-picotcp-demo

A PicoTCP demo app testing the ethernet throughput on the lpc1768 mbed board.

Revision:
152:a3d286bf94e5
Parent:
149:5f4cb161cec3
Child:
154:6c0e92a80c4a
--- a/modules/pico_tcp.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_tcp.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -21,16 +21,16 @@
 #define SEQN(f) ((f) ? (long_be(((struct pico_tcp_hdr *)((f)->transport_hdr))->seq)) : 0)
 #define ACKN(f) ((f) ? (long_be(((struct pico_tcp_hdr *)((f)->transport_hdr))->ack)) : 0)
 
-#define TCP_TIME (PICO_TIME_MS())
+#define TCP_TIME (pico_time)(PICO_TIME_MS())
 
 #define PICO_TCP_RTO_MIN (70)
 #define PICO_TCP_RTO_MAX (120000)
 #define PICO_TCP_IW          2
-#define PICO_TCP_SYN_TO  1000u
+#define PICO_TCP_SYN_TO  2000u
 #define PICO_TCP_ZOMBIE_TO 30000
 
 #define PICO_TCP_MAX_RETRANS         10
-#define PICO_TCP_MAX_CONNECT_RETRIES 7
+#define PICO_TCP_MAX_CONNECT_RETRIES 3
 
 #define PICO_TCP_LOOKAHEAD      0x00
 #define PICO_TCP_FIRST_DUPACK   0x01
@@ -40,6 +40,8 @@
 #define PICO_TCP_UNREACHABLE    0x05
 #define PICO_TCP_WINDOW_FULL    0x06
 
+#define ONE_GIGABYTE ((uint32_t)(1024UL * 1024UL * 1024UL))
+
 /* check if the Nagle algorithm is enabled on the socket */
 #define IS_NAGLE_ENABLED(s)     (!(!(!(s->opt_flags & (1u << PICO_SOCKET_OPT_TCPNODELAY)))))
 /* check if tcp connection is "idle" according to Nagle (RFC 896) */
@@ -61,43 +63,9 @@
 
 #ifdef PICO_SUPPORT_MUTEX
 static void *Mutex = NULL;
-#define PICOTCP_MUTEX_LOCK(x) { \
-        if (x == NULL) \
-            x = pico_mutex_init(); \
-        pico_mutex_lock(x); \
-}
-#define PICOTCP_MUTEX_UNLOCK(x) pico_mutex_unlock(x)
-
-#else
-#define PICOTCP_MUTEX_LOCK(x) do {} while(0)
-#define PICOTCP_MUTEX_UNLOCK(x) do {} while(0)
 #endif
 
 
-static /* inline*/ int32_t seq_compare(uint32_t a, uint32_t b)
-{
-    uint32_t thresh = ((uint32_t)(-1)) >> 1;
-
-    if (a > b) /* return positive number, if not wrapped */
-    {
-        if ((a - b) > thresh) /* b wrapped */
-            return -(int32_t)(b - a); /* b = very small,     a = very big      */
-        else
-            return (int32_t)(a - b); /* a = biggest,        b = a bit smaller */
-
-    }
-
-    if (a < b) /* return negative number, if not wrapped */
-    {
-        if ((b - a) > thresh) /* a wrapped */
-            return (int32_t)(a - b); /* a = very small,     b = very big      */
-        else
-            return -(int32_t)(b - a); /* b = biggest,        a = a bit smaller */
-
-    }
-
-    return 0;
-}
 
 /* Input segment, used to keep only needed data, not the full frame */
 struct tcp_input_segment
@@ -112,13 +80,13 @@
 static int input_segment_compare(void *ka, void *kb)
 {
     struct tcp_input_segment *a = ka, *b = kb;
-    return seq_compare(a->seq, b->seq);
+    return pico_seq_compare(a->seq, b->seq);
 }
 
 static struct tcp_input_segment *segment_from_frame(struct pico_frame *f)
 {
     struct tcp_input_segment *seg = PICO_ZALLOC(sizeof(struct tcp_input_segment));
-    if(!seg)
+    if ((!seg) || (!f->payload_len))
         return NULL;
 
     seg->payload = PICO_ZALLOC(f->payload_len);
@@ -137,7 +105,7 @@
 static int segment_compare(void *ka, void *kb)
 {
     struct pico_frame *a = ka, *b = kb;
-    return seq_compare(SEQN(a), SEQN(b));
+    return pico_seq_compare(SEQN(a), SEQN(b));
 }
 
 struct pico_tcp_queue
@@ -193,24 +161,19 @@
     }
 }
 
-static int32_t pico_enqueue_segment(struct pico_tcp_queue *tq, void *f)
+static uint16_t enqueue_segment_len(struct pico_tcp_queue *tq, void *f)
+{
+    if (IS_INPUT_QUEUE(tq)) {
+        return ((struct tcp_input_segment *)f)->payload_len;
+    } else {
+        return (uint16_t)(((struct pico_frame *)f)->buffer_len);
+    }
+}
+
+
+static int32_t do_enqueue_segment(struct pico_tcp_queue *tq, void *f, uint16_t payload_len)
 {
     int32_t ret = -1;
-    uint16_t payload_len;
-
-    if (!f)
-        return -1;
-
-    payload_len = (uint16_t)((IS_INPUT_QUEUE(tq)) ?
-                             (((struct tcp_input_segment *)f)->payload_len) :
-                             (((struct pico_frame *)f)->buffer_len));
-
-    if (payload_len <= 0) {
-        tcp_dbg("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TRIED TO ENQUEUE INVALID SEGMENT!\n");
-        ret = -2; /* Fail harder */
-        goto out;
-    }
-
     PICOTCP_MUTEX_LOCK(Mutex);
     if ((tq->size + payload_len) > tq->max_size)
     {
@@ -235,6 +198,24 @@
     return ret;
 }
 
+static int32_t pico_enqueue_segment(struct pico_tcp_queue *tq, void *f)
+{
+    uint16_t payload_len;
+
+    if (!f)
+        return -1;
+
+    payload_len = enqueue_segment_len(tq, f);
+
+
+    if (payload_len == 0) {
+        tcp_dbg("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TRIED TO ENQUEUE INVALID SEGMENT!\n");
+        return -1;
+    }
+
+    return do_enqueue_segment(tq, f, payload_len);
+}
+
 static void pico_discard_segment(struct pico_tcp_queue *tq, void *f)
 {
     void *f1;
@@ -313,12 +294,24 @@
     uint8_t scale_ok;
     struct tcp_sack_block *sacks;
     uint8_t jumbo;
+    uint32_t linger_timeout;
 
     /* Transmission */
     uint8_t x_mode;
     uint8_t dupacks;
     uint8_t backoff;
     uint8_t localZeroWindow;
+
+    /* Keepalive */
+    struct pico_timer *keepalive_tmr;
+    pico_time ack_timestamp;
+    uint32_t ka_time;
+    uint32_t ka_intvl;
+    uint32_t ka_probes;
+    uint32_t ka_retries_count;
+
+    /* FIN timer */
+    struct pico_timer *fin_tmr;
 };
 
 /* Queues */
@@ -357,14 +350,14 @@
         void *cur = head;
 
         if (IS_INPUT_QUEUE(q))
-            seq_result = seq_compare(((struct tcp_input_segment *)head)->seq + ((struct tcp_input_segment *)head)->payload_len, seq);
+            seq_result = pico_seq_compare(((struct tcp_input_segment *)head)->seq + ((struct tcp_input_segment *)head)->payload_len, seq);
         else
-            seq_result = seq_compare(SEQN((struct pico_frame *)head) + ((struct pico_frame *)head)->payload_len, seq);
+            seq_result = pico_seq_compare(SEQN((struct pico_frame *)head) + ((struct pico_frame *)head)->payload_len, seq);
 
         if (seq_result <= 0)
         {
             head = next_segment(q, cur);
-            tcp_dbg("Releasing %08x, len: %d\n", SEQN((struct pico_frame *)head), ((struct pico_frame *)head)->payload_len);
+            //tcp_dbg("Releasing %08x, len: %d\n", SEQN((struct pico_frame *)head), ((struct pico_frame *)head)->payload_len);
             pico_discard_segment(q, cur);
             ret++;
         } else {
@@ -388,13 +381,13 @@
         f = idx->keyValue;
 
         if (IS_INPUT_QUEUE(q))
-            seq_result = seq_compare(((struct tcp_input_segment *)f)->seq + ((struct tcp_input_segment *)f)->payload_len, seq);
+            seq_result = pico_seq_compare(((struct tcp_input_segment *)f)->seq + ((struct tcp_input_segment *)f)->payload_len, seq);
         else
-            seq_result = seq_compare(SEQN((struct pico_frame *)f) + ((struct pico_frame *)f)->payload_len, seq);
+            seq_result = pico_seq_compare(SEQN((struct pico_frame *)f) + ((struct pico_frame *)f)->payload_len, seq);
 
         if (seq_result <= 0) {
             tcp_dbg("Releasing %p\n", f);
-            if(seq_result == 0)
+            if ((seq_result == 0) && !IS_INPUT_QUEUE(q))
                 *timestamp = ((struct pico_frame *)f)->timestamp;
 
             pico_discard_segment(q, f);
@@ -422,8 +415,8 @@
         pseudo.src.addr = s->local_addr.ip4.addr;
         pseudo.dst.addr = s->remote_addr.ip4.addr;
     } else {
-        /* Case of incomming frame */
-        /* dbg("TCP CRC: on incomming frame\n"); */
+        /* Case of incoming frame */
+        /* dbg("TCP CRC: on incoming frame\n"); */
         pseudo.src.addr = hdr->src.addr;
         pseudo.dst.addr = hdr->dst.addr;
     }
@@ -450,7 +443,7 @@
         pseudo.src = s->local_addr.ip6;
         pseudo.dst = s->remote_addr.ip6;
     } else {
-        /* Case of incomming frame */
+        /* Case of incoming frame */
         pseudo.src = ipv6_hdr->src;
         pseudo.dst = ipv6_hdr->dst;
     }
@@ -465,22 +458,32 @@
 }
 #endif
 
+#ifdef PICO_SUPPORT_IPV4
+static inline int checksum_is_ipv4(struct pico_frame *f)
+{
+    return (IS_IPV4(f) || (f->sock && (f->sock->net == &pico_proto_ipv4)));
+}
+#endif
+
+#ifdef PICO_SUPPORT_IPV6
+static inline int checksum_is_ipv6(struct pico_frame *f)
+{
+    return ((IS_IPV6(f)) || (f->sock && (f->sock->net == &pico_proto_ipv6)));
+}
+#endif
+
 uint16_t pico_tcp_checksum(struct pico_frame *f)
 {
     (void)f;
+
     #ifdef PICO_SUPPORT_IPV4
-    if (IS_IPV4(f))
-        return pico_tcp_checksum_ipv4(f);
-
-    if (f->sock && (f->sock->net == &pico_proto_ipv4))
+    if (checksum_is_ipv4(f))
         return pico_tcp_checksum_ipv4(f);
 
     #endif
+
     #ifdef PICO_SUPPORT_IPV6
-    if (IS_IPV6(f))
-        return pico_tcp_checksum_ipv6(f);
-
-    if (f->sock && (f->sock->net == &pico_proto_ipv6))
+    if (checksum_is_ipv6(f))
         return pico_tcp_checksum_ipv6(f);
 
     #endif
@@ -502,7 +505,7 @@
     }
 
     if (f->payload_len > 0) {
-        if (seq_compare(SEQN(f) + f->payload_len, t->snd_nxt) > 0) {
+        if (pico_seq_compare(SEQN(f) + f->payload_len, t->snd_nxt) > 0) {
             t->snd_nxt = SEQN(f) + f->payload_len;
             tcp_dbg("%s: snd_nxt is now %08x\n", __FUNCTION__, t->snd_nxt);
         }
@@ -537,6 +540,28 @@
     return long_be(_paws);
 }
 
+static inline void tcp_add_sack_option(struct pico_socket_tcp *ts, struct pico_frame *f, uint16_t flags, uint32_t *ii)
+{
+    if (flags & PICO_TCP_ACK) {
+        struct tcp_sack_block *sb;
+        uint32_t len_off;
+
+        if (ts->sack_ok && ts->sacks) {
+            f->start[(*ii)++] = PICO_TCP_OPTION_SACK;
+            len_off = *ii;
+            f->start[(*ii)++] = PICO_TCPOPTLEN_SACK;
+            while(ts->sacks) {
+                sb = ts->sacks;
+                ts->sacks = sb->next;
+                memcpy(f->start + *ii, sb, 2 * sizeof(uint32_t));
+                *ii += (2 * (uint32_t)sizeof(uint32_t));
+                f->start[len_off] = (uint8_t)(f->start[len_off] + (2 * sizeof(uint32_t)));
+                PICO_FREE(sb);
+            }
+        }
+    }
+}
+
 static void tcp_add_options(struct pico_socket_tcp *ts, struct pico_frame *f, uint16_t flags, uint16_t optsiz)
 {
     uint32_t tsval = long_be((uint32_t)TCP_TIME);
@@ -568,24 +593,7 @@
         i += 4;
     }
 
-    if (flags & PICO_TCP_ACK) {
-        struct tcp_sack_block *sb;
-        uint32_t len_off;
-
-        if (ts->sack_ok && ts->sacks) {
-            f->start[i++] = PICO_TCP_OPTION_SACK;
-            len_off = i;
-            f->start[i++] = PICO_TCPOPTLEN_SACK;
-            while(ts->sacks) {
-                sb = ts->sacks;
-                ts->sacks = sb->next;
-                memcpy(f->start + i, sb, 2 * sizeof(uint32_t));
-                i += (2 * (uint32_t)sizeof(uint32_t));
-                f->start[len_off] = (uint8_t)(f->start[len_off] + (2 * sizeof(uint32_t)));
-                PICO_FREE(sb);
-            }
-        }
-    }
+    tcp_add_sack_option(ts, f, flags, &i);
 
     if (i < optsiz)
         f->start[ optsiz - 1 ] = PICO_TCP_OPTION_END;
@@ -637,24 +645,8 @@
 static void tcp_send_ack(struct pico_socket_tcp *t);
 #define tcp_send_windowUpdate(t) (tcp_send_ack(t))
 
-static void tcp_set_space(struct pico_socket_tcp *t)
+static inline void tcp_set_space_check_winupdate(struct pico_socket_tcp *t, int32_t space, uint32_t shift)
 {
-    int32_t space;
-    uint32_t shift = 0;
-
-    if (t->tcpq_in.max_size == 0) {
-        space = 1024 * 1024 * 1024; /* One Gigabyte, for unlimited sockets. */
-    } else {
-        space = (int32_t)(t->tcpq_in.max_size - t->tcpq_in.size);
-    }
-
-    if (space < 0)
-        space = 0;
-
-    while(space > 0xFFFF) {
-        space = (int32_t)(((uint32_t)space >> 1u));
-        shift++;
-    }
     if (((uint32_t)space != t->wnd) || (shift != t->wnd_scale) || ((space - t->wnd) > (int32_t)((uint32_t)space >> 2u))) {
         t->wnd = (uint16_t)space;
         t->wnd_scale = (uint16_t)shift;
@@ -669,6 +661,27 @@
     }
 }
 
+static void tcp_set_space(struct pico_socket_tcp *t)
+{
+    int32_t space;
+    uint32_t shift = 0;
+
+    if (t->tcpq_in.max_size == 0) {
+        space = ONE_GIGABYTE;
+    } else {
+        space = (int32_t)(t->tcpq_in.max_size - t->tcpq_in.size);
+    }
+
+    if (space < 0)
+        space = 0;
+
+    while(space > 0xFFFF) {
+        space = (int32_t)(((uint32_t)space >> 1u));
+        shift++;
+    }
+    tcp_set_space_check_winupdate(t, space, shift);
+}
+
 /* Return 32-bit aligned option size */
 static uint16_t tcp_options_size(struct pico_socket_tcp *t, uint16_t flags)
 {
@@ -709,34 +722,37 @@
 
 }
 
+static inline int tcp_sack_marker(struct pico_frame *f, uint32_t start, uint32_t end, uint16_t *count)
+{
+    int cmp;
+    cmp = pico_seq_compare(SEQN(f), start);
+    if (cmp > 0)
+        return 0;
+
+    if (cmp == 0) {
+        cmp = pico_seq_compare(SEQN(f) + f->payload_len, end);
+        if (cmp > 0) {
+            tcp_dbg("Invalid SACK: ignoring.\n");
+        }
+
+        tcp_dbg("Marking (by SACK) segment %08x BLK:[%08x::%08x]\n", SEQN(f), start, end);
+        f->flags |= PICO_FRAME_FLAG_SACKED;
+        (*count)++;
+    }
+
+    return cmp;
+}
+
 static void tcp_process_sack(struct pico_socket_tcp *t, uint32_t start, uint32_t end)
 {
     struct pico_frame *f;
     struct pico_tree_node *index, *temp;
-    int cmp;
     uint16_t count = 0;
 
     pico_tree_foreach_safe(index, &t->tcpq_out.pool, temp){
         f = index->keyValue;
-        cmp = seq_compare(SEQN(f), start);
-        if (cmp > 0)
+        if (tcp_sack_marker(f, start, end, &count) == 0)
             goto done;
-
-        if (cmp == 0) {
-            cmp = seq_compare(SEQN(f) + f->payload_len, end);
-            if (cmp > 0) {
-                tcp_dbg("Invalid SACK: ignoring.\n");
-            }
-
-            tcp_dbg("Marking (by SACK) segment %08x BLK:[%08x::%08x]\n", SEQN(f), start, end);
-            f->flags |= PICO_FRAME_FLAG_SACKED;
-            count++;
-
-            if (cmp == 0) {
-                /* that was last segment sacked. Job done */
-                goto done;
-            }
-        }
     }
 
 done:
@@ -778,6 +794,63 @@
     }
 }
 
+static int tcpopt_len_check(uint32_t *idx, uint8_t len, uint8_t expected)
+{
+    if (len != expected) {
+        *idx = *idx + len - 2;
+        return -1;
+    }
+
+    return 0;
+}
+
+static inline void tcp_parse_option_ws(struct pico_socket_tcp *t, uint8_t len, uint8_t *opt, uint32_t *idx)
+{
+    if (tcpopt_len_check(idx, len, PICO_TCPOPTLEN_WS) < 0)
+        return;
+
+    t->recv_wnd_scale = opt[(*idx)++];
+    tcp_dbg_options("TCP Window scale: received %d\n", t->recv_wnd_scale);
+
+}
+
+static inline void tcp_parse_option_sack_ok(struct pico_socket_tcp *t, struct pico_frame *f, uint8_t len, uint32_t *idx)
+{
+    if (tcpopt_len_check(idx, len, PICO_TCPOPTLEN_SACK_OK) < 0)
+        return;
+
+    if(((struct pico_tcp_hdr *)(f->transport_hdr))->flags & PICO_TCP_SYN )
+        t->sack_ok = 1;
+}
+
+static inline void tcp_parse_option_mss(struct pico_socket_tcp *t, uint8_t len, uint8_t *opt, uint32_t *idx)
+{
+    uint16_t mss;
+    if (tcpopt_len_check(idx, len, PICO_TCPOPTLEN_MSS) < 0)
+        return;
+
+    t->mss_ok = 1;
+    mss = short_from(opt + *idx);
+    *idx += (uint32_t)sizeof(uint16_t);
+    if (t->mss > short_be(mss))
+        t->mss = short_be(mss);
+}
+
+static inline void tcp_parse_option_timestamp(struct pico_socket_tcp *t, struct pico_frame *f, uint8_t len, uint8_t *opt, uint32_t *idx)
+{
+    uint32_t tsval, tsecr;
+    if (tcpopt_len_check(idx, len, PICO_TCPOPTLEN_TIMESTAMP) < 0)
+        return;
+
+    t->ts_ok = 1;
+    tsval = long_from(opt + *idx);
+    *idx += (uint32_t)sizeof(uint32_t);
+    tsecr = long_from(opt + *idx);
+    f->timestamp = long_be(tsecr);
+    *idx += (uint32_t)sizeof(uint32_t);
+    t->ts_nxt = long_be(tsval);
+}
+
 static void tcp_parse_options(struct pico_frame *f)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)f->sock;
@@ -801,65 +874,22 @@
         case PICO_TCP_OPTION_END:
             break;
         case PICO_TCP_OPTION_WS:
-            if (len != PICO_TCPOPTLEN_WS) {
-                tcp_dbg_options("TCP Window scale: bad len received (%d).\n", len);
-                i = i + len - 2;
-                break;
-            }
-
-            t->recv_wnd_scale = opt[i++];
-            tcp_dbg_options("TCP Window scale: received %d\n", t->recv_wnd_scale);
+            tcp_parse_option_ws(t, len, opt, &i);
             break;
         case PICO_TCP_OPTION_SACK_OK:
-            if (len != PICO_TCPOPTLEN_SACK_OK) {
-                tcp_dbg_options("TCP option sack: bad len received.\n");
-                i = i + len - 2;
-                break;
-            }
-
-            if(((struct pico_tcp_hdr *)(f->transport_hdr))->flags & PICO_TCP_SYN )
-                t->sack_ok = 1;
-
+            tcp_parse_option_sack_ok(t, f, len, &i);
+            break;
+        case PICO_TCP_OPTION_MSS:
+            tcp_parse_option_mss(t, len, opt, &i);
             break;
-        case PICO_TCP_OPTION_MSS: {
-            uint16_t mss;
-            if (len != PICO_TCPOPTLEN_MSS) {
-                tcp_dbg_options("TCP option mss: bad len received.\n");
-                i = i + len - 2;
-                break;
-            }
-
-            t->mss_ok = 1;
-            mss = short_from(opt + i);
-            i += (uint32_t)sizeof(uint16_t);
-            if (t->mss > short_be(mss))
-                t->mss = short_be(mss);
-
+        case PICO_TCP_OPTION_TIMESTAMP:
+            tcp_parse_option_timestamp(t, f, len, opt, &i);
             break;
-        }
-        case PICO_TCP_OPTION_TIMESTAMP: {
-            uint32_t tsval, tsecr;
-            if (len != PICO_TCPOPTLEN_TIMESTAMP) {
-                tcp_dbg_options("TCP option timestamp: bad len received.\n");
-                i = i + len - 2;
-                break;
-            }
-
-            t->ts_ok = 1;
-            tsval = long_from(opt + i);
-            i += (uint32_t)sizeof(uint32_t);
-            tsecr = long_from(opt + i);
-            f->timestamp = long_be(tsecr);
-            i += (uint32_t)sizeof(uint32_t);
-            t->ts_nxt = long_be(tsval);
-            break;
-        }
+
         case PICO_TCP_OPTION_SACK:
-        {
             tcp_rcv_sack(t, opt + i, len - 2);
             i = i + len - 2;
             break;
-        }
         default:
             tcp_dbg_options("TCP: received unsupported option %u\n", type);
             i = i + len - 2;
@@ -867,17 +897,11 @@
     }
 }
 
-static int tcp_send(struct pico_socket_tcp *ts, struct pico_frame *f)
+static inline void tcp_send_add_tcpflags(struct pico_socket_tcp *ts, struct pico_frame *f)
 {
     struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr;
-    struct pico_frame *cpy;
-    hdr->trans.sport = ts->sock.local_port;
-    hdr->trans.dport = ts->sock.remote_port;
-    if (!hdr->seq)
-        hdr->seq = long_be(ts->snd_nxt);
-
     if (ts->rcv_nxt != 0) {
-        if ((ts->rcv_ackd == 0) || (seq_compare(ts->rcv_ackd, ts->rcv_nxt) != 0) || (hdr->flags & PICO_TCP_ACK)) {
+        if ((ts->rcv_ackd == 0) || (pico_seq_compare(ts->rcv_ackd, ts->rcv_nxt) != 0) || (hdr->flags & PICO_TCP_ACK)) {
             hdr->flags |= PICO_TCP_ACK;
             hdr->ack = long_be(ts->rcv_nxt);
             ts->rcv_ackd = ts->rcv_nxt;
@@ -892,13 +916,14 @@
         hdr->flags |= PICO_TCP_PSH | PICO_TCP_ACK;
         hdr->ack = long_be(ts->rcv_nxt);
         ts->rcv_ackd = ts->rcv_nxt;
-        /* XXX pico_keepalive_reschedule(ts); */
     }
-
-    f->start = f->transport_hdr + PICO_SIZE_TCPHDR;
-    hdr->rwnd = short_be(ts->wnd);
-    hdr->crc = 0;
-    hdr->crc = short_be(pico_tcp_checksum(f));
+}
+
+static inline int tcp_send_try_enqueue(struct pico_socket_tcp *ts, struct pico_frame *f)
+{
+    struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+    struct pico_frame *cpy;
+    (void)hdr;
 
     /* TCP: ENQUEUE to PROTO ( Transmit ) */
     cpy = pico_frame_copy(f);
@@ -920,6 +945,26 @@
     }
 
     return 0;
+
+}
+
+static int tcp_send(struct pico_socket_tcp *ts, struct pico_frame *f)
+{
+    struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+    hdr->trans.sport = ts->sock.local_port;
+    hdr->trans.dport = ts->sock.remote_port;
+    if (!hdr->seq)
+        hdr->seq = long_be(ts->snd_nxt);
+
+    tcp_send_add_tcpflags(ts, f);
+
+    f->start = f->transport_hdr + PICO_SIZE_TCPHDR;
+    hdr->rwnd = short_be(ts->wnd);
+    hdr->crc = 0;
+    hdr->crc = short_be(pico_tcp_checksum(f));
+
+    return tcp_send_try_enqueue(ts, f);
+
 }
 
 /* #define PICO_TCP_SUPPORT_SOCKET_STATS */
@@ -934,6 +979,49 @@
 }
 #endif
 
+static void tcp_send_probe(struct pico_socket_tcp *t);
+
+static void pico_tcp_keepalive(pico_time now, void *arg)
+{
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg;
+    if (((t->sock.state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)  && (t->ka_time > 0)) {
+        if (t->ka_time < (now - t->ack_timestamp)) {
+            if (t->ka_retries_count == 0) {
+                /* First probe */
+                tcp_send_probe(t);
+                t->ka_retries_count++;
+            }
+            if (t->ka_retries_count > t->ka_probes) {
+                if (t->sock.wakeup)
+                {
+                    pico_err = PICO_ERR_ECONNRESET;
+                    t->sock.wakeup(PICO_SOCK_EV_ERR, &t->sock);
+                }
+            }
+            if (((t->ka_retries_count * t->ka_intvl) + t->ka_time) < (now - t->ack_timestamp)) {
+                /* Next probe */
+                tcp_send_probe(t);
+                t->ka_retries_count++;
+            }
+        } else {
+            t->ka_retries_count = 0;
+        }
+    }
+    t->keepalive_tmr = pico_timer_add(1000, pico_tcp_keepalive, t);
+}
+
+static inline void rto_set(struct pico_socket_tcp *t, uint32_t rto)
+{
+    if (rto < PICO_TCP_RTO_MIN)
+        rto = PICO_TCP_RTO_MIN;
+
+    if (rto > PICO_TCP_RTO_MAX)
+        rto = PICO_TCP_RTO_MAX;
+
+    t->rto = rto;
+}
+
+
 struct pico_socket *pico_tcp_open(uint16_t family)
 {
     struct pico_socket_tcp *t = PICO_ZALLOC(sizeof(struct pico_socket_tcp));
@@ -942,72 +1030,38 @@
 
     t->sock.timestamp = TCP_TIME;
     pico_socket_set_family(&t->sock, family);
-    t->mss = (uint16_t)(pico_socket_get_mtu(&t->sock) - PICO_SIZE_TCPHDR);
+    t->mss = (uint16_t)(pico_socket_get_mss(&t->sock) - PICO_SIZE_TCPHDR);
     t->tcpq_in.pool.root = t->tcpq_hold.pool.root = t->tcpq_out.pool.root = &LEAF;
     t->tcpq_hold.pool.compare = t->tcpq_out.pool.compare = segment_compare;
     t->tcpq_in.pool.compare = input_segment_compare;
     t->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ;
     t->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ;
     t->tcpq_hold.max_size = 2u * t->mss;
-    /* disable Nagle by default */
+    rto_set(t, PICO_TCP_RTO_MIN);
+
+    /* Uncomment next line and disable Nagle by default */
     t->sock.opt_flags |= (1 << PICO_SOCKET_OPT_TCPNODELAY);
-    /* Nagle is enabled by default */
+
+    /* Uncomment next line and Nagle is enabled by default */
     /* t->sock.opt_flags &= (uint16_t) ~(1 << PICO_SOCKET_OPT_TCPNODELAY); */
 
+    /* Set default linger for the socket */
+    t->linger_timeout = PICO_SOCKET_LINGER_TIMEOUT;
+
+
 #ifdef PICO_TCP_SUPPORT_SOCKET_STATS
     pico_timer_add(2000, sock_stats, t);
 #endif
+
+    t->keepalive_tmr = pico_timer_add(1000, pico_tcp_keepalive, t);
     tcp_set_space(t);
 
     return &t->sock;
 }
 
-uint32_t pico_tcp_read(struct pico_socket *s, void *buf, uint32_t len)
+static uint32_t tcp_read_finish(struct pico_socket *s, uint32_t tot_rd_len)
 {
     struct pico_socket_tcp *t = TCP_SOCK(s);
-    struct tcp_input_segment *f;
-    int32_t in_frame_off;
-    uint32_t in_frame_len;
-    uint32_t tot_rd_len = 0;
-
-    while (tot_rd_len < len) {
-        /* To be sure we don't have garbage at the beginning */
-        release_until(&t->tcpq_in, t->rcv_processed);
-        f = first_segment(&t->tcpq_in);
-        if (!f)
-            goto out;
-
-        in_frame_off = seq_compare(t->rcv_processed, f->seq);
-        /* Hole at the beginning of data, awaiting retransmissions. */
-        if (in_frame_off < 0) {
-            tcp_dbg("TCP> read hole beginning of data, %08x - %08x. rcv_nxt is %08x\n", t->rcv_processed, f->seq, t->rcv_nxt);
-            goto out;
-        }
-
-        else if (in_frame_off > 0)
-        {
-            if ((uint32_t)in_frame_off > f->payload_len)
-                dbg("FATAL TCP ERR: in_frame_off > f->payload_len\n");
-
-            in_frame_len = f->payload_len - (uint32_t)in_frame_off;
-        } else {
-            in_frame_len = f->payload_len;
-        }
-
-
-        if ((in_frame_len + tot_rd_len) > (uint32_t)len) {
-            in_frame_len = len - tot_rd_len;
-        }
-
-        memcpy((uint8_t *)buf + tot_rd_len, f->payload + in_frame_off, in_frame_len);
-        tot_rd_len += in_frame_len;
-        t->rcv_processed += in_frame_len;
-
-        if ((in_frame_len == 0u) || (in_frame_len == (uint32_t)f->payload_len)) {
-            pico_discard_segment(&t->tcpq_in, f);
-        }
-    }
-out:
     tcp_set_space(t);
     if (t->tcpq_in.size == 0) {
         s->ev_pending &= (uint16_t)(~PICO_SOCK_EV_RD);
@@ -1027,19 +1081,85 @@
     return tot_rd_len;
 }
 
+static inline uint32_t tcp_read_in_frame_len(struct tcp_input_segment *f, int32_t in_frame_off, uint32_t tot_rd_len, uint32_t read_op_len)
+{
+    uint32_t in_frame_len = 0;
+    if (in_frame_off > 0)
+    {
+        if ((uint32_t)in_frame_off > f->payload_len) {
+            tcp_dbg("FATAL TCP ERR: in_frame_off > f->payload_len\n");
+        }
+
+        in_frame_len = f->payload_len - (uint32_t)in_frame_off;
+    } else { /* in_frame_off == 0 */
+        in_frame_len = f->payload_len;
+    }
+
+    if ((in_frame_len + tot_rd_len) > (uint32_t)read_op_len) {
+        in_frame_len = read_op_len - tot_rd_len;
+    }
+
+    return in_frame_len;
+
+}
+
+static inline void tcp_read_check_segment_done(struct pico_socket_tcp *t, struct tcp_input_segment *f, uint32_t in_frame_len)
+{
+    if ((in_frame_len == 0u) || (in_frame_len == (uint32_t)f->payload_len)) {
+        pico_discard_segment(&t->tcpq_in, f);
+    }
+}
+
+uint32_t pico_tcp_read(struct pico_socket *s, void *buf, uint32_t len)
+{
+    struct pico_socket_tcp *t = TCP_SOCK(s);
+    struct tcp_input_segment *f;
+    int32_t in_frame_off;
+    uint32_t in_frame_len;
+    uint32_t tot_rd_len = 0;
+
+    while (tot_rd_len < len) {
+        /* To be sure we don't have garbage at the beginning */
+        release_until(&t->tcpq_in, t->rcv_processed);
+        f = first_segment(&t->tcpq_in);
+        if (!f)
+            return tcp_read_finish(s, tot_rd_len);
+
+        in_frame_off = pico_seq_compare(t->rcv_processed, f->seq);
+        /* Check for hole at the beginning of data, awaiting retransmissions. */
+        if (in_frame_off < 0) {
+            tcp_dbg("TCP> read hole beginning of data, %08x - %08x. rcv_nxt is %08x\n", t->rcv_processed, f->seq, t->rcv_nxt);
+            return tcp_read_finish(s, tot_rd_len);
+        }
+
+        in_frame_len = tcp_read_in_frame_len(f, in_frame_off, tot_rd_len, len);
+
+
+        memcpy((uint8_t *)buf + tot_rd_len, f->payload + in_frame_off, in_frame_len);
+        tot_rd_len += in_frame_len;
+        t->rcv_processed += in_frame_len;
+
+        tcp_read_check_segment_done(t, f, in_frame_len);
+
+    }
+    return tcp_read_finish(s, tot_rd_len);
+}
+
 int pico_tcp_initconn(struct pico_socket *s);
 static void initconn_retry(pico_time when, void *arg)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg;
     IGNORE_PARAMETER(when);
-    if (TCPSTATE(&t->sock) == PICO_SOCKET_STATE_TCP_SYN_SENT
-        && !(t->sock.state & PICO_SOCKET_STATE_SHUT_LOCAL)
-        && !(t->sock.state & PICO_SOCKET_STATE_SHUT_REMOTE)) {
+    if (TCPSTATE(&t->sock) != PICO_SOCKET_STATE_TCP_ESTABLISHED)
+    {
         if (t->backoff > PICO_TCP_MAX_CONNECT_RETRIES) {
             tcp_dbg("TCP> Connection timeout. \n");
             if (t->sock.wakeup)
+            {
+                pico_err = PICO_ERR_ECONNREFUSED;
                 t->sock.wakeup(PICO_SOCK_EV_ERR, &t->sock);
-
+            }
+            pico_socket_del(&t->sock);
             return;
         }
 
@@ -1069,7 +1189,7 @@
 
     ts->snd_last = ts->snd_nxt;
     ts->cwnd = PICO_TCP_IW;
-    mtu = pico_socket_get_mtu(s);
+    mtu = (uint16_t)pico_socket_get_mss(s);
     ts->mss = (uint16_t)(mtu - PICO_SIZE_TCPHDR);
     ts->ssthresh = (uint16_t)((uint16_t)(PICO_DEFAULT_SOCKETQ / ts->mss) -  (((uint16_t)(PICO_DEFAULT_SOCKETQ / ts->mss)) >> 3u));
     syn->sock = s;
@@ -1141,8 +1261,9 @@
     hdr->trans.sport = t->sock.local_port;
     hdr->trans.dport = t->sock.remote_port;
     hdr->seq = long_be(t->snd_nxt);
-    if ((flags & PICO_TCP_ACK) != 0)
+    if ((flags & PICO_TCP_ACK) != 0) {
         hdr->ack = long_be(t->rcv_nxt);
+    }
 
     if (is_keepalive)
         hdr->seq = long_be(t->snd_nxt - 1);
@@ -1169,25 +1290,19 @@
     tcp_send_empty(t, PICO_TCP_PSHACK, 1);
 }
 
-static int tcp_send_rst(struct pico_socket *s, struct pico_frame *fr)
+static int tcp_do_send_rst(struct pico_socket *s, uint32_t seq)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *) s;
+    uint16_t opt_len = tcp_options_size(t, PICO_TCP_RST);
     struct pico_frame *f;
-    struct pico_tcp_hdr *hdr, *hdr_rcv;
-    uint16_t opt_len = tcp_options_size(t, PICO_TCP_RST);
-    int close;
-
-    tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> START\n");
-
+    struct pico_tcp_hdr *hdr;
     f = t->sock.net->alloc(t->sock.net, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
-
     if (!f) {
         return -1;
     }
-
-    hdr_rcv = (struct pico_tcp_hdr *) fr->transport_hdr;
-
     f->sock = &t->sock;
+    tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> START\n");
+
     hdr = (struct pico_tcp_hdr *) f->transport_hdr;
     hdr->len = (uint8_t)((PICO_SIZE_TCPHDR + opt_len) << 2 | t->jumbo);
     hdr->flags = PICO_TCP_RST;
@@ -1196,21 +1311,7 @@
     tcp_add_options(t, f, PICO_TCP_RST, opt_len);
     hdr->trans.sport = t->sock.local_port;
     hdr->trans.dport = t->sock.remote_port;
-    hdr->seq = long_be(t->snd_nxt);
-
-    /* check if state is synchronized */
-    if (((s->state & PICO_SOCKET_STATE_TCP) > PICO_SOCKET_STATE_TCP_SYN_RECV)) {
-        /* in synchronized state: send RST with seq = ack from previous segment */
-        hdr->seq = hdr_rcv->ack;
-        close = 0;
-    } else {
-        /* non-synchronized state */
-        /* go to CLOSED here to prevent timer callback to go on after timeout */
-        (t->sock).state &= 0x00FFU;
-        (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED;
-        close = 1;
-    }
-
+    hdr->seq = seq;
     hdr->ack = long_be(t->rcv_nxt);
     t->rcv_ackd = t->rcv_nxt;
     f->start = f->transport_hdr + PICO_SIZE_TCPHDR;
@@ -1220,9 +1321,28 @@
 
     /* TCP: ENQUEUE to PROTO */
     pico_enqueue(&tcp_out, f);
-
-    /* goto CLOSED */
-    if (close) {
+    tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> DONE\n");
+    return 0;
+}
+
+static int tcp_send_rst(struct pico_socket *s, struct pico_frame *fr)
+{
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *) s;
+    struct pico_tcp_hdr *hdr_rcv;
+    int ret;
+
+    if (fr && ((s->state & PICO_SOCKET_STATE_TCP) > PICO_SOCKET_STATE_TCP_SYN_RECV)) {
+        /* in synchronized state: send RST with seq = ack from previous segment */
+        hdr_rcv = (struct pico_tcp_hdr *) fr->transport_hdr;
+        ret = tcp_do_send_rst(s, hdr_rcv->ack);       
+    } else {
+        /* non-synchronized state */
+        /* go to CLOSED here to prevent timer callback to go on after timeout */
+        (t->sock).state &= 0x00FFU;
+        (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED;
+        ret = tcp_do_send_rst(s, long_be(t->snd_nxt));       
+
+        /* Set generic socket state to CLOSED, too */
         (t->sock).state &= 0xFF00U;
         (t->sock).state |= PICO_SOCKET_STATE_CLOSED;
 
@@ -1232,11 +1352,49 @@
 
         /* delete socket */
         pico_socket_del(&t->sock);
-
-        tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> DONE, deleted socket\n");
+    }
+    return ret;
+}
+
+static inline void tcp_fill_rst_payload(struct pico_frame *fr, struct pico_frame *f)
+{
+    /* fill in IP data from original frame */
+    if (IS_IPV4(fr)) {
+        memcpy(f->net_hdr, fr->net_hdr, sizeof(struct pico_ipv4_hdr));
+        ((struct pico_ipv4_hdr *)(f->net_hdr))->dst.addr = ((struct pico_ipv4_hdr *)(fr->net_hdr))->src.addr;
+        ((struct pico_ipv4_hdr *)(f->net_hdr))->src.addr = ((struct pico_ipv4_hdr *)(fr->net_hdr))->dst.addr;
+        tcp_dbg("Making IPv4 reset frame...\n");
+
+    } else {
+        memcpy(f->net_hdr, fr->net_hdr, sizeof(struct pico_ipv6_hdr));
+        ((struct pico_ipv6_hdr *)(f->net_hdr))->dst = ((struct pico_ipv6_hdr *)(fr->net_hdr))->src;
+        ((struct pico_ipv6_hdr *)(f->net_hdr))->src = ((struct pico_ipv6_hdr *)(fr->net_hdr))->dst;
     }
 
-    return 0;
+    /* fill in TCP data from original frame */
+    ((struct pico_tcp_hdr *)(f->transport_hdr))->trans.dport = ((struct pico_tcp_hdr *)(fr->transport_hdr))->trans.sport;
+    ((struct pico_tcp_hdr *)(f->transport_hdr))->trans.sport = ((struct pico_tcp_hdr *)(fr->transport_hdr))->trans.dport;
+
+}
+
+
+static inline void tcp_fill_rst_header(struct pico_frame *fr, struct pico_tcp_hdr *hdr1, struct pico_frame *f, struct pico_tcp_hdr *hdr)
+{
+    if(!(hdr1->flags & PICO_TCP_ACK))
+        hdr->flags |= PICO_TCP_ACK;
+
+    hdr->rwnd  = 0;
+    if (((struct pico_tcp_hdr *)(fr->transport_hdr))->flags & PICO_TCP_ACK) {
+        hdr->seq = ((struct pico_tcp_hdr *)(fr->transport_hdr))->ack;
+    } else {
+        hdr->seq = 0U;
+    }
+
+    hdr->ack = 0;
+    if(!(hdr1->flags & PICO_TCP_ACK))
+        hdr->ack = long_be(long_be(((struct pico_tcp_hdr *)(fr->transport_hdr))->seq) + fr->payload_len);
+
+    hdr->crc = short_be(pico_tcp_checksum(f));
 }
 
 int pico_tcp_reply_rst(struct pico_frame *fr)
@@ -1258,46 +1416,23 @@
         return -1;
     }
 
-    /* fill in IP data from original frame */
-    if (IS_IPV4(fr)) {
-        memcpy(f->net_hdr, fr->net_hdr, sizeof(struct pico_ipv4_hdr));
-        ((struct pico_ipv4_hdr *)(f->net_hdr))->dst.addr = ((struct pico_ipv4_hdr *)(fr->net_hdr))->src.addr;
-        ((struct pico_ipv4_hdr *)(f->net_hdr))->src.addr = ((struct pico_ipv4_hdr *)(fr->net_hdr))->dst.addr;
-        tcp_dbg("Making IPv4 reset frame...\n");
-
-    } else {
-        memcpy(f->net_hdr, fr->net_hdr, sizeof(struct pico_ipv6_hdr));
-        ((struct pico_ipv6_hdr *)(f->net_hdr))->dst = ((struct pico_ipv6_hdr *)(fr->net_hdr))->src;
-        ((struct pico_ipv6_hdr *)(f->net_hdr))->src = ((struct pico_ipv6_hdr *)(fr->net_hdr))->dst;
-    }
-
-    /* fill in TCP data from original frame */
-    ((struct pico_tcp_hdr *)(f->transport_hdr))->trans.dport = ((struct pico_tcp_hdr *)(fr->transport_hdr))->trans.sport;
-    ((struct pico_tcp_hdr *)(f->transport_hdr))->trans.sport = ((struct pico_tcp_hdr *)(fr->transport_hdr))->trans.dport;
+    tcp_fill_rst_payload(fr, f);
+
     hdr = (struct pico_tcp_hdr *) f->transport_hdr;
     hdr->len   = (uint8_t)(size << 2);
     hdr->flags = PICO_TCP_RST;
-    if(!(hdr1->flags & PICO_TCP_ACK))
-        hdr->flags |= PICO_TCP_ACK;
-
-    hdr->rwnd  = 0;
-    if (((struct pico_tcp_hdr *)(fr->transport_hdr))->flags & PICO_TCP_ACK) {
-        hdr->seq = ((struct pico_tcp_hdr *)(fr->transport_hdr))->ack;
-    } else {
-        hdr->seq = 0U;
-    }
-
-    hdr->ack = 0;
-    if(!(hdr1->flags & PICO_TCP_ACK))
-        hdr->ack = long_be(long_be(((struct pico_tcp_hdr *)(fr->transport_hdr))->seq) + fr->payload_len);
-
-    hdr->crc = short_be(pico_tcp_checksum(f));
-    if (IS_IPV4(f)) {
+
+    tcp_fill_rst_header(fr, hdr1, f, hdr);
+
+    if (0) {
+#ifdef PICO_SUPPORT_IPV4
+    } else if (IS_IPV4(f)) {
         tcp_dbg("Pushing IPv4 reset frame...\n");
         pico_ipv4_frame_push(f, &(((struct pico_ipv4_hdr *)(f->net_hdr))->dst), PICO_PROTO_TCP);
+#endif
 #ifdef PICO_SUPPORT_IPV6
     } else {
-        pico_ipv6_frame_push(f, &(((struct pico_ipv6_hdr *)(f->net_hdr))->dst), PICO_PROTO_TCP);
+        pico_ipv6_frame_push(f, NULL, &(((struct pico_ipv6_hdr *)(f->net_hdr))->dst), PICO_PROTO_TCP, 0);
 #endif
     }
 
@@ -1365,6 +1500,16 @@
     return 0;
 }
 
+static void tcp_deltcb(pico_time when, void *arg);
+
+static void tcp_linger(struct pico_socket_tcp *t)
+{
+    if (t->fin_tmr) {
+        pico_timer_cancel(t->fin_tmr);
+    }
+    t->fin_tmr = pico_timer_add(t->linger_timeout, tcp_deltcb, t);
+}
+
 static void tcp_send_fin(struct pico_socket_tcp *t)
 {
     struct pico_frame *f;
@@ -1386,16 +1531,20 @@
     tcp_add_options(t, f, PICO_TCP_FIN, opt_len);
     hdr->trans.sport = t->sock.local_port;
     hdr->trans.dport = t->sock.remote_port;
-    hdr->seq = long_be(t->snd_nxt); /* XXX TODO check correct ?? --> snd_last? otherwise maybe data after FIN */
+    hdr->seq = long_be(t->snd_nxt);
 
     f->start = f->transport_hdr + PICO_SIZE_TCPHDR;
     hdr->rwnd = short_be(t->wnd);
     hdr->crc = 0;
     hdr->crc = short_be(pico_tcp_checksum(f));
     /* tcp_dbg("SENDING FIN...\n"); */
-    /* TCP: ENQUEUE to PROTO ( Pure ACK ) */
-    pico_enqueue(&tcp_out, f);
-    t->snd_nxt++;
+    if (t->linger_timeout > 0) {
+        pico_enqueue(&tcp_out, f);
+        t->snd_nxt++;
+    } else {
+        pico_frame_discard(f);
+    }
+    tcp_linger(t);
 }
 
 static void tcp_sack_prepare(struct pico_socket_tcp *t)
@@ -1460,76 +1609,95 @@
     }
 }
 
+static inline int tcp_data_in_expected(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+    struct tcp_input_segment *nxt;
+    if (pico_seq_compare(SEQN(f), t->rcv_nxt) == 0) { /* Exactly what we expected */
+        /* Create new segment and enqueue it */
+        struct tcp_input_segment *input = segment_from_frame(f);
+        if (!input) {
+            pico_err = PICO_ERR_ENOMEM;
+            return -1;
+        }
+
+        if(pico_enqueue_segment(&t->tcpq_in, input) <= 0)
+        {
+            /* failed to enqueue, destroy segment */
+            PICO_FREE(input->payload);
+            PICO_FREE(input);
+            return -1;
+        } else {
+            t->rcv_nxt = SEQN(f) + f->payload_len;
+            nxt = peek_segment(&t->tcpq_in, t->rcv_nxt);
+            while(nxt) {
+                tcp_dbg("scrolling rcv_nxt...%08x\n", t->rcv_nxt);
+                t->rcv_nxt += nxt->payload_len;
+                nxt = peek_segment(&t->tcpq_in, t->rcv_nxt);
+            }
+            t->sock.ev_pending |= PICO_SOCK_EV_RD;
+        }
+    } else {
+        tcp_dbg("TCP> lo segment. Uninteresting retransmission. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f));
+    }
+
+    return 0;
+}
+
+static inline int tcp_data_in_high_segment(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+    tcp_dbg("TCP> hi segment. Possible packet loss. I'll dupack this. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f));
+    if (t->sack_ok) {
+        struct tcp_input_segment *input = segment_from_frame(f);
+        if (!input) {
+            pico_err = PICO_ERR_ENOMEM;
+            return -1;
+        }
+
+        if(pico_enqueue_segment(&t->tcpq_in, input) <= 0) {
+            /* failed to enqueue, destroy segment */
+            PICO_FREE(input->payload);
+            PICO_FREE(input);
+            return -1;
+        }
+
+        tcp_sack_prepare(t);
+    }
+
+    return 0;
+}
+
+static inline void tcp_data_in_send_ack(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+    struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+    /* In either case, ack til recv_nxt, unless received data raises a RST flag. */
+    if (((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_CLOSE_WAIT) &&
+        ((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_SYN_SENT) &&
+        ((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_SYN_RECV) &&
+        ((hdr->flags & PICO_TCP_RST) == 0))
+        tcp_send_ack(t);
+}
+
 static int tcp_data_in(struct pico_socket *s, struct pico_frame *f)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
     struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr;
     uint16_t payload_len = (uint16_t)(f->transport_len - ((hdr->len & 0xf0u) >> 2u));
     int ret = 0;
+    (void)hdr;
+
     if (((hdr->len & 0xf0u) >> 2u) <= f->transport_len) {
         tcp_parse_options(f);
         f->payload = f->transport_hdr + ((hdr->len & 0xf0u) >> 2u);
         f->payload_len = payload_len;
         tcp_dbg("TCP> Received segment. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f));
 
-        if (seq_compare(SEQN(f), t->rcv_nxt) <= 0) {
-            struct tcp_input_segment *nxt;
-            if (seq_compare(SEQN(f), t->rcv_nxt) == 0) { /* Exactly what we expected */
-                /* Create new segment and enqueue it */
-                struct tcp_input_segment *input = segment_from_frame(f);
-                if (!input) {
-                    pico_err = PICO_ERR_ENOMEM;
-                    return -1;
-                }
-
-                if(pico_enqueue_segment(&t->tcpq_in, input) <= 0)
-                {
-                    /* failed to enqueue, destroy segment */
-                    PICO_FREE(input->payload);
-                    PICO_FREE(input);
-                    ret = -1;
-                } else {
-                    t->rcv_nxt = SEQN(f) + f->payload_len;
-                    nxt = peek_segment(&t->tcpq_in, t->rcv_nxt);
-                    while(nxt) {
-                        tcp_dbg("scrolling rcv_nxt...%08x\n", t->rcv_nxt);
-                        t->rcv_nxt += nxt->payload_len;
-                        nxt = peek_segment(&t->tcpq_in, t->rcv_nxt);
-                    }
-                    t->sock.ev_pending |= PICO_SOCK_EV_RD;
-                }
-            } else {
-                tcp_dbg("TCP> lo segment. Uninteresting retransmission. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f));
-            }
+        if (pico_seq_compare(SEQN(f), t->rcv_nxt) <= 0) {
+            ret = tcp_data_in_expected(t, f);
         } else {
-            tcp_dbg("TCP> hi segment. Possible packet loss. I'll dupack this. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f));
-            if (t->sack_ok) {
-                struct tcp_input_segment *input = segment_from_frame(f);
-                if (!input) {
-                    pico_err = PICO_ERR_ENOMEM;
-                    return -1;
-                }
-
-                if(pico_enqueue_segment(&t->tcpq_in, input) <= 0) {
-                    /* failed to enqueue, destroy segment */
-                    PICO_FREE(input->payload);
-                    PICO_FREE(input);
-                    return -1;
-                }
-
-                tcp_sack_prepare(t);
-            }
+            ret = tcp_data_in_high_segment(t, f);
         }
 
-        /* In either case, ack til recv_nxt. */
-        if (((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_CLOSE_WAIT) && ((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_SYN_SENT) && ((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_SYN_RECV)) {
-            /* tcp_dbg("SENDACK CALLED FROM OUTSIDE tcp_synack, state %x\n",t->sock.state); */
-            if (hdr->flags & PICO_TCP_RST)
-                return -1;
-
-            tcp_send_ack(t);
-        }
-
+        tcp_data_in_send_ack(t, f);
         return ret;
     } else {
         tcp_dbg("TCP: invalid data in pkt len, exp: %d, got %d\n", (hdr->len & 0xf0) >> 2, f->transport_len);
@@ -1555,17 +1723,6 @@
         return (uint16_t)(b - a);
 }
 
-static inline void rto_set(struct pico_socket_tcp *t, uint32_t rto)
-{
-    if (rto < PICO_TCP_RTO_MIN)
-        rto = PICO_TCP_RTO_MIN;
-
-    if (rto > PICO_TCP_RTO_MAX)
-        rto = PICO_TCP_RTO_MAX;
-
-    t->rto = rto;
-}
-
 static void tcp_rtt(struct pico_socket_tcp *t, uint32_t rtt)
 {
 
@@ -1680,10 +1837,39 @@
            ((t->backoff < PICO_TCP_MAX_RETRANS));
 }
 
+static inline int tcp_retrans_timeout_check_queue(struct pico_socket_tcp *t)
+{
+    struct pico_frame *f = NULL;
+    f = first_segment(&t->tcpq_out);
+    while (f) {
+        tcp_dbg("Checking frame in queue \n");
+        if (t->x_mode == PICO_TCP_WINDOW_FULL) {
+            tcp_dbg("TCP BLACKOUT> TIMED OUT (output) frame %08x, len= %d rto=%d Win full: %d frame flags: %04x\n", SEQN(f), f->payload_len, t->rto, t->x_mode == PICO_TCP_WINDOW_FULL, f->flags);
+            tcp_next_zerowindow_probe(t);
+            return -1;
+        }
+
+        if (t->x_mode != PICO_TCP_BLACKOUT)
+            tcp_first_timeout(t);
+
+        tcp_add_header(t, f);
+        if (tcp_rto_xmit(t, f) > 0) /* A segment has been rexmit'd */
+            return -1;
+
+        f = next_segment(&t->tcpq_out, f);
+    }
+    if (t->tcpq_out.size < t->tcpq_out.max_size)
+        t->sock.ev_pending |= PICO_SOCK_EV_WR;
+
+    return 0;
+
+
+
+}
+
 static void tcp_retrans_timeout(pico_time val, void *sock)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *) sock;
-    struct pico_frame *f = NULL;
 
     t->retrans_tmr = NULL;
 
@@ -1701,26 +1887,8 @@
     t->retrans_tmr_due = 0ull;
 
     if (tcp_is_allowed_to_send(t)) {
-        f = first_segment(&t->tcpq_out);
-        while (f) {
-            tcp_dbg("Checking frame in queue \n");
-            if (t->x_mode == PICO_TCP_WINDOW_FULL) {
-                tcp_dbg("TCP BLACKOUT> TIMED OUT (output) frame %08x, len= %d rto=%d Win full: %d frame flags: %04x\n", SEQN(f), f->payload_len, t->rto, t->x_mode == PICO_TCP_WINDOW_FULL, f->flags);
-                tcp_next_zerowindow_probe(t);
-                return;
-            }
-
-            if (t->x_mode != PICO_TCP_BLACKOUT)
-                tcp_first_timeout(t);
-
-            tcp_add_header(t, f);
-            if (tcp_rto_xmit(t, f) > 0) /* A segment has been rexmit'd */
-                return;
-
-            f = next_segment(&t->tcpq_out, f);
-        }
-        if (t->tcpq_out.size < t->tcpq_out.max_size)
-            t->sock.ev_pending |= PICO_SOCK_EV_WR;
+        if (tcp_retrans_timeout_check_queue(t) < 0)
+            return;
     }
     else if(t->backoff >= PICO_TCP_MAX_RETRANS && (t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_ESTABLISHED )
     {
@@ -1871,6 +2039,7 @@
 
     acked = (uint16_t)tcp_ack_advance_una(t, f, &acked_timestamp);
     una = first_segment(&t->tcpq_out);
+    t->ack_timestamp = TCP_TIME;
 
     if ((t->x_mode == PICO_TCP_BLACKOUT) ||
         ((t->x_mode == PICO_TCP_WINDOW_FULL) && ((t->recv_wnd << t->recv_wnd_scale) > t->mss))) {
@@ -1905,7 +2074,7 @@
             if (rtt)
                 tcp_rtt(t, rtt);
         } else if(acked_timestamp) {
-            /* If no timestamps are there, use conservatve estimation on the una */
+            /* If no timestamps are there, use conservative estimation on the una */
             rtt = time_diff(TCP_TIME, acked_timestamp);
             if (rtt)
                 tcp_rtt(t, rtt);
@@ -1929,7 +2098,11 @@
             tcp_dbg("Mode: DUPACK %d, due to PURE ACK %0x, len = %d\n", t->x_mode, SEQN(f), f->payload_len);
             /* tcp_dbg("ACK: %x - QUEUE: %x\n", ACKN(f), SEQN(first_segment(&t->tcpq_out))); */
             if (t->x_mode == PICO_TCP_RECOVER) {              /* Switching mode */
-                t->cwnd = (uint16_t)t->in_flight;
+                if (t->in_flight > PICO_TCP_IW)
+                    t->cwnd = (uint16_t)t->in_flight;
+                else
+                    t->cwnd = PICO_TCP_IW;
+
                 t->snd_retry = SEQN((struct pico_frame *)first_segment(&t->tcpq_out));
                 if (t->ssthresh > t->cwnd)
                     t->ssthresh >>= 2;
@@ -1950,10 +2123,10 @@
                     tcp_dbg("Skipping %08x because it is sacked.\n", SEQN(nxt));
                     nxt = next_segment(&t->tcpq_out, nxt);
                 }
-                if (nxt && (seq_compare(SEQN(nxt), t->snd_nxt)) > 0)
+                if (nxt && (pico_seq_compare(SEQN(nxt), t->snd_nxt)) > 0)
                     nxt = NULL;
 
-                if (nxt && (seq_compare(SEQN(nxt), SEQN((struct pico_frame *)first_segment(&t->tcpq_out))) > (int)(t->recv_wnd << t->recv_wnd_scale)))
+                if (nxt && (pico_seq_compare(SEQN(nxt), SEQN((struct pico_frame *)first_segment(&t->tcpq_out))) > (int)(t->recv_wnd << t->recv_wnd_scale)))
                     nxt = NULL;
 
                 if(!nxt)
@@ -1981,7 +2154,7 @@
     /* Linux very special zero-window probe detection (see bug #107) */
     if ((0 == (hdr->flags & (PICO_TCP_PSH | PICO_TCP_SYN))) && /* This is a pure ack, and... */
         (ACKN(f) == t->snd_nxt) &&                           /* it's acking our snd_nxt, and... */
-        (seq_compare(SEQN(f), t->rcv_nxt) < 0))             /* Has an old seq number */
+        (pico_seq_compare(SEQN(f), t->rcv_nxt) < 0))             /* Has an old seq number */
     {
         tcp_send_ack(t);
     }
@@ -2025,15 +2198,20 @@
 
 static int tcp_finwaitack(struct pico_socket *s, struct pico_frame *f)
 {
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
     tcp_dbg("RECEIVED ACK IN FIN_WAIT1\n");
-    tcp_dbg("TCP> IN STATE FIN_WAIT2\n");
 
     /* acking part */
     tcp_ack(s, f);
-    /* update TCP state */
-    s->state &= 0x00FFU;
-    s->state |= PICO_SOCKET_STATE_TCP_FIN_WAIT2;
-
+
+    
+    tcp_dbg("FIN_WAIT1: ack is %08x - snd_nxt is %08x\n", ACKN(f), t->snd_nxt);
+    if (ACKN(f) == (t->snd_nxt - 1u)) {
+        /* update TCP state */
+        s->state &= 0x00FFU;
+        s->state |= PICO_SOCKET_STATE_TCP_FIN_WAIT2;
+        tcp_dbg("TCP> IN STATE FIN_WAIT2\n");
+    }
     return 0;
 }
 
@@ -2042,23 +2220,27 @@
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg;
     IGNORE_PARAMETER(when);
 
-    if (TCPSTATE(&t->sock) == PICO_SOCKET_STATE_TCP_TIME_WAIT) {
-        tcp_dbg("TCP> state: time_wait, final timer expired, going to closed state\n");
-        /* update state */
-        (t->sock).state &= 0x00FFU;
-        (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED;
-        (t->sock).state &= 0xFF00U;
-        (t->sock).state |= PICO_SOCKET_STATE_CLOSED;
-        /* call EV_FIN wakeup before deleting */
-        if (t->sock.wakeup) {
-            (t->sock).wakeup(PICO_SOCK_EV_FIN, &(t->sock));
-        }
-
-        /* delete socket */
-        pico_socket_del(&t->sock);
+    /* send RST if not yet in TIME_WAIT */
+    if ( (((t->sock).state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_TIME_WAIT)
+      && (((t->sock).state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_CLOSING) ) {
+        tcp_dbg("Called deltcb in state = %04x (sending reset!)\n", (t->sock).state);
+        tcp_do_send_rst(&t->sock, long_be(t->snd_nxt));
     } else {
-        tcp_dbg("TCP> trying to go to closed, wrong state\n");
+        tcp_dbg("Called deltcb in state = %04x\n", (t->sock).state);
     }
+
+    /* update state */
+    (t->sock).state &= 0x00FFU;
+    (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED;
+    (t->sock).state &= 0xFF00U;
+    (t->sock).state |= PICO_SOCKET_STATE_CLOSED;
+    /* call EV_FIN wakeup before deleting */
+    if (t->sock.wakeup) {
+        (t->sock).wakeup(PICO_SOCK_EV_FIN, &(t->sock));
+    }
+
+    /* delete socket */
+    pico_socket_del(&t->sock);
 }
 
 static int tcp_finwaitfin(struct pico_socket *s, struct pico_frame *f)
@@ -2080,39 +2262,45 @@
 
     /* send ACK */
     tcp_send_ack(t);
-    /* set timer */
-    pico_timer_add(200, tcp_deltcb, t);
+    /* linger */
+    tcp_linger(t);
     return 0;
 }
 
-static int tcp_closewaitack(struct pico_socket *s, struct pico_frame *f)
+static int tcp_closing_ack(struct pico_socket *s, struct pico_frame *f)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
     tcp_dbg("TCP> received ack in CLOSING\n");
     /* acking part */
     tcp_ack(s, f);
-    /* update TCP state */
-    s->state &= 0x00FFU;
-    s->state |= PICO_SOCKET_STATE_TCP_TIME_WAIT;
-    /* set timer */
-    pico_timer_add(200, tcp_deltcb, t);
+
+    /* update TCP state DLA TODO: Only if FIN is acked! */
+    tcp_dbg("CLOSING: ack is %08x - snd_nxt is %08x\n", ACKN(f), t->snd_nxt);
+    if (ACKN(f) == t->snd_nxt) {
+        s->state &= 0x00FFU;
+        s->state |= PICO_SOCKET_STATE_TCP_TIME_WAIT;
+        /* set timer */
+        tcp_linger(t);
+    }
     return 0;
 }
 
 static int tcp_lastackwait(struct pico_socket *s, struct pico_frame *f)
 {
-    IGNORE_PARAMETER(f);
-    tcp_dbg("TCP> state: last_ack, received ack, to closed\n");
-    s->state &= 0x00FFU;
-    s->state |= PICO_SOCKET_STATE_TCP_CLOSED;
-    s->state &= 0xFF00U;
-    s->state |= PICO_SOCKET_STATE_CLOSED;
-    /* call socket wakeup with EV_FIN */
-    if (s->wakeup)
-        s->wakeup(PICO_SOCK_EV_FIN, s);
-
-    /* delete socket */
-    pico_socket_del(s);
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+    tcp_dbg("LAST_ACK: ack is %08x - snd_nxt is %08x\n", ACKN(f), t->snd_nxt);
+    if (ACKN(f) == t->snd_nxt) {
+        s->state &= 0x00FFU;
+        s->state |= PICO_SOCKET_STATE_TCP_CLOSED;
+        s->state &= 0xFF00U;
+        s->state |= PICO_SOCKET_STATE_CLOSED;
+        /* call socket wakeup with EV_FIN */
+        if (s->wakeup)
+            s->wakeup(PICO_SOCK_EV_FIN, s);
+
+        /* delete socket */
+        pico_socket_del(s);
+    }
     return 0;
 }
 
@@ -2148,11 +2336,9 @@
     }
 
 #endif
-
-
     f->sock = &new->sock;
     tcp_parse_options(f);
-    mtu = pico_socket_get_mtu(s);
+    mtu = (uint16_t)pico_socket_get_mss(&new->sock);
     new->mss = (uint16_t)(mtu - PICO_SIZE_TCPHDR);
     new->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ;
     new->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ;
@@ -2164,9 +2350,11 @@
     new->ssthresh = (uint16_t)((uint16_t)(PICO_DEFAULT_SOCKETQ / new->mss) -  (((uint16_t)(PICO_DEFAULT_SOCKETQ / new->mss)) >> 3u));
     new->recv_wnd = short_be(hdr->rwnd);
     new->jumbo = hdr->len & 0x07;
+    new->linger_timeout = PICO_SOCKET_LINGER_TIMEOUT;
     s->number_of_pending_conn++;
     new->sock.parent = s;
     new->sock.wakeup = s->wakeup;
+    rto_set(new, PICO_TCP_RTO_MIN);
     /* Initialize timestamp values */
     new->sock.state = PICO_SOCKET_STATE_BOUND | PICO_SOCKET_STATE_CONNECTED | PICO_SOCKET_STATE_TCP_SYN_RECV;
     pico_socket_add(&new->sock);
@@ -2180,11 +2368,11 @@
     struct pico_tcp_hdr *hdr = NULL;
     struct pico_socket_tcp *t = TCP_SOCK(s);
     hdr = (struct pico_tcp_hdr *)f->transport_hdr;
-    if (t->rcv_nxt == long_be(hdr->seq) + 1) {
+    if (t->rcv_nxt == long_be(hdr->seq) + 1u) {
         /* take back our own SEQ number to its original value,
-         * so the synack retransmitted is identical to the original. 
+         * so the synack retransmitted is identical to the original.
          */
-        t->snd_nxt--; 
+        t->snd_nxt--;
         tcp_send_synack(s);
     } else {
         tcp_send_rst(s, f);
@@ -2201,13 +2389,13 @@
 }
 
 
-uint16_t pico_tcp_get_socket_mtu(struct pico_socket *s)
+uint16_t pico_tcp_get_socket_mss(struct pico_socket *s)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *) s;
     if (t->mss > 0)
         return (uint16_t)(t->mss + PICO_SIZE_TCPHDR);
     else
-        return pico_socket_get_mtu(s);
+        return (uint16_t)pico_socket_get_mss(s);
 }
 
 static int tcp_synack(struct pico_socket *s, struct pico_frame *f)
@@ -2285,25 +2473,14 @@
     }
 }
 
-static int tcp_closewait(struct pico_socket *s, struct pico_frame *f)
+static void tcp_attempt_closewait(struct pico_socket *s, struct pico_frame *f)
 {
-
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
     struct pico_tcp_hdr *hdr  = (struct pico_tcp_hdr *) (f->transport_hdr);
-
-
-    if (f->payload_len > 0)
-        tcp_data_in(s, f);
-
-    if (f->flags & PICO_TCP_ACK)
-        tcp_ack(s, f);
-
-    tcp_dbg("called close_wait, in state %08x\n", s->state);
-
-    if (seq_compare(SEQN(f), t->rcv_nxt) == 0) {
+    if (pico_seq_compare(SEQN(f), t->rcv_nxt) == 0) {
         /* received FIN, increase ACK nr */
         t->rcv_nxt = long_be(hdr->seq) + 1;
-        if (seq_compare(SEQN(f), t->rcv_processed) == 0) {
+        if (pico_seq_compare(SEQN(f), t->rcv_processed) == 0) {
             if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED) {
                 tcp_dbg("Changing state to CLOSE_WAIT\n");
                 s->state &= 0x00FFU;
@@ -2321,6 +2498,22 @@
         }
     }
 
+
+}
+
+static int tcp_closewait(struct pico_socket *s, struct pico_frame *f)
+{
+
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+    if (f->payload_len > 0)
+        tcp_data_in(s, f);
+
+    if (f->flags & PICO_TCP_ACK)
+        tcp_ack(s, f);
+
+    tcp_dbg("called close_wait, in state %08x\n", s->state);
+    tcp_attempt_closewait(s, f);
+
     /* Ensure that the notification given to the socket
      * did not put us in LAST_ACK state before sending the ACK: i.e. if
      * pico_socket_close() has been called in the socket callback, we don't need to send
@@ -2369,7 +2562,8 @@
     s->state |= PICO_SOCKET_STATE_TCP_TIME_WAIT;
     /* set SHUT_REMOTE */
     s->state |= PICO_SOCKET_STATE_SHUT_REMOTE;
-    pico_timer_add(2000, tcp_deltcb, t);
+
+    tcp_linger(t);
 
     return 0;
 }
@@ -2382,6 +2576,18 @@
     (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED;
     (t->sock).state &= 0xFF00U;
     (t->sock).state |= PICO_SOCKET_STATE_CLOSED;
+    /* call EV_ERR wakeup before deleting */
+    if (((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)) {
+        if ((t->sock).wakeup)
+            (t->sock).wakeup(PICO_SOCK_EV_FIN, &(t->sock));
+    } else {
+        pico_err = PICO_ERR_ECONNRESET;
+        if ((t->sock).wakeup)
+            (t->sock).wakeup(PICO_SOCK_EV_ERR, &(t->sock));
+
+        /* delete socket */
+        pico_socket_del(&t->sock);
+    }
 }
 
 static void tcp_wakeup_pending(struct pico_socket *s, uint16_t ev)
@@ -2401,9 +2607,6 @@
         /* the RST is acceptable if the ACK field acknowledges the SYN */
         if ((t->snd_nxt + 1u) == ACKN(f)) {              /* valid, got to closed state */
             tcp_force_closed(s);
-            pico_err = PICO_ERR_ECONNRESET;
-            tcp_wakeup_pending(s, PICO_SOCK_EV_ERR);
-            pico_socket_del(&t->sock);              /* delete socket */
         } else {                  /* not valid, ignore */
             tcp_dbg("TCP RST> IGNORE\n");
             return 0;
@@ -2413,20 +2616,7 @@
            a reset is valid if its sequence number is in the window */
         uint32_t this_seq = long_be(hdr->seq);
         if ((this_seq >= t->rcv_ackd) && (this_seq <= ((uint32_t)(short_be(hdr->rwnd) << (t->wnd_scale)) + t->rcv_ackd))) {
-            if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_SYN_RECV) {
-                tcp_force_closed(s);
-                pico_err = PICO_ERR_ECONNRESET;
-                tcp_wakeup_pending(s, PICO_SOCK_EV_ERR);
-                pico_socket_del(&t->sock);              /* delete socket */
-                tcp_dbg("TCP RST> SOCKET BACK TO LISTEN\n");
-                /*   pico_socket_del(s); */
-            } else {
-                tcp_force_closed(s);
-                tcp_wakeup_pending(s, PICO_SOCK_EV_FIN);
-                pico_err = PICO_ERR_ECONNRESET;
-                tcp_wakeup_pending(s, PICO_SOCK_EV_ERR);
-                pico_socket_del(&t->sock);              /* delete socket */
-            }
+            tcp_force_closed(s);
         } else {                  /* not valid, ignore */
             tcp_dbg("TCP RST> IGNORE\n");
             return 0;
@@ -2448,7 +2638,7 @@
     struct pico_socket_tcp *t = (struct pico_socket_tcp *) s;
     struct pico_tcp_hdr *hdr  = (struct pico_tcp_hdr *) (fr->transport_hdr);
 
-    if (seq_compare(SEQN(fr), t->rcv_nxt) == 0) {
+    if (pico_seq_compare(SEQN(fr), t->rcv_nxt) == 0) {
         /* received FIN, increase ACK nr */
         t->rcv_nxt = long_be(hdr->seq) + 1;
         s->state &= 0x00FFU;
@@ -2485,11 +2675,11 @@
     { PICO_SOCKET_STATE_TCP_LAST_ACK,     NULL,            &tcp_ack,          &tcp_lastackwait,  &tcp_send_rst,   &tcp_send_rst,   &tcp_send_rst,   &tcp_rst },
     { PICO_SOCKET_STATE_TCP_FIN_WAIT1,    NULL,            &tcp_ack,          &tcp_finwaitack,   &tcp_data_in,    &tcp_rcvfin,     &tcp_finack,     &tcp_rst },
     { PICO_SOCKET_STATE_TCP_FIN_WAIT2,    NULL,            &tcp_ack,          &tcp_ack,          &tcp_data_in,    &tcp_finwaitfin, &tcp_finack,     &tcp_rst },
-    { PICO_SOCKET_STATE_TCP_CLOSING,      NULL,            &tcp_ack,          &tcp_closewaitack, &tcp_send_rst,   &tcp_send_rst,   &tcp_send_rst,   &tcp_rst },
-    { PICO_SOCKET_STATE_TCP_TIME_WAIT,    NULL,            &tcp_ack,          &tcp_send_rst,     &tcp_send_rst,   &tcp_send_rst,   &tcp_send_rst,   &tcp_rst }
+    { PICO_SOCKET_STATE_TCP_CLOSING,      NULL,            &tcp_ack,          &tcp_closing_ack, &tcp_send_rst,   &tcp_send_rst,   &tcp_send_rst,   &tcp_rst },
+    { PICO_SOCKET_STATE_TCP_TIME_WAIT,    NULL,            NULL,          NULL,     &tcp_send_rst,   NULL, NULL, NULL}
 };
 
-#define MAX_VALID_FLAGS  9  /* Maximum number of valid flag combinations */
+#define MAX_VALID_FLAGS  10  /* Maximum number of valid flag combinations */
 static uint8_t invalid_flags(struct pico_socket *s, uint8_t flags)
 {
     uint8_t i;
@@ -2499,7 +2689,7 @@
         { /* PICO_SOCKET_STATE_TCP_LISTEN     */ PICO_TCP_SYN },
         { /* PICO_SOCKET_STATE_TCP_SYN_SENT   */ PICO_TCP_SYNACK, PICO_TCP_RST, PICO_TCP_RSTACK},
         { /* PICO_SOCKET_STATE_TCP_SYN_RECV   */ PICO_TCP_SYN, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST},
-        { /* PICO_SOCKET_STATE_TCP_ESTABLISHED*/ PICO_TCP_SYN, PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST},
+        { /* PICO_SOCKET_STATE_TCP_ESTABLISHED*/ PICO_TCP_SYN, PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST, PICO_TCP_RSTACK},
         { /* PICO_SOCKET_STATE_TCP_CLOSE_WAIT */ PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST},
         { /* PICO_SOCKET_STATE_TCP_LAST_ACK   */ PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST},
         { /* PICO_SOCKET_STATE_TCP_FIN_WAIT1  */ PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST},
@@ -2516,6 +2706,42 @@
     }
     return 1;
 }
+
+static void tcp_action_call(int (*call)(struct pico_socket *s, struct pico_frame *f), struct pico_socket *s, struct pico_frame *f )
+{
+    if (call)
+        call(s, f);
+}
+
+static int tcp_action_by_flags(const struct tcp_action_entry *action, struct pico_socket *s, struct pico_frame *f, uint8_t flags)
+{
+    int ret = 0;
+    if ((flags == PICO_TCP_ACK) || (flags == (PICO_TCP_ACK | PICO_TCP_PSH))) {
+        tcp_action_call(action->ack, s, f);
+    }
+
+    if ((f->payload_len > 0 || (flags & PICO_TCP_PSH)) &&
+        !(s->state & PICO_SOCKET_STATE_CLOSED) && !TCP_IS_STATE(s, PICO_SOCKET_STATE_TCP_LISTEN))
+    {
+        ret = f->payload_len;
+        tcp_action_call(action->data, s, f);
+    }
+
+    if (flags == PICO_TCP_FIN) {
+        tcp_action_call(action->fin, s, f);
+    }
+
+    if ((flags == (PICO_TCP_FIN | PICO_TCP_ACK)) || (flags == (PICO_TCP_FIN | PICO_TCP_ACK | PICO_TCP_PSH))) {
+        tcp_action_call(action->finack, s, f);
+    }
+
+    if (flags & PICO_TCP_RST) {
+        tcp_action_call(action->rst, s, f);
+    }
+
+    return ret;
+}
+
 int pico_tcp_input(struct pico_socket *s, struct pico_frame *f)
 {
     struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) (f->transport_hdr);
@@ -2529,7 +2755,7 @@
     tcp_dbg("[sam] TCP> [tcp input] t_len: %u\n", f->transport_len);
     tcp_dbg("[sam] TCP> flags = %02x\n", hdr->flags);
     tcp_dbg("[sam] TCP> s->state >> 8 = %u\n", s->state >> 8);
-    tcp_dbg("[%lu] TCP> [tcp input] socket: %p state: %d <-- local port:%u remote port: %u seq: %08x ack: %08x flags: %02x t_len: %u, hdr: %u payload: %d\n", TCP_TIME, s, s->state >> 8, short_be(hdr->trans.dport), short_be(hdr->trans.sport), SEQN(f), ACKN(f), hdr->flags, f->transport_len, (hdr->len & 0xf0) >> 2, f->payload_len );
+    tcp_dbg("[sam] TCP> [tcp input] socket: %p state: %d <-- local port:%u remote port: %u seq: %08x ack: %08x flags: %02x t_len: %u, hdr: %u payload: %d\n", s, s->state >> 8, short_be(hdr->trans.dport), short_be(hdr->trans.sport), SEQN(f), ACKN(f), hdr->flags, f->transport_len, (hdr->len & 0xf0) >> 2, f->payload_len );
 
     /* This copy of the frame has the current socket as owner */
     f->sock = s;
@@ -2540,40 +2766,11 @@
         pico_tcp_reply_rst(f);
     }
     else if (flags == PICO_TCP_SYN) {
-        if (action->syn)
-            action->syn(s, f);
+        tcp_action_call(action->syn, s, f);
     } else if (flags == (PICO_TCP_SYN | PICO_TCP_ACK)) {
-        if (action->synack)
-            action->synack(s, f);
+        tcp_action_call(action->synack, s, f);
     } else {
-        if ((flags == PICO_TCP_ACK) || (flags == (PICO_TCP_ACK | PICO_TCP_PSH))) {
-            if (action->ack) {
-                action->ack(s, f);
-            }
-        }
-
-        if ((f->payload_len > 0 || (flags & PICO_TCP_PSH)) &&
-            !(s->state & PICO_SOCKET_STATE_CLOSED) && !TCP_IS_STATE(s, PICO_SOCKET_STATE_TCP_LISTEN))
-        {
-            ret = f->payload_len;
-            if (action->data)
-                action->data(s, f);
-        }
-
-        if (flags == PICO_TCP_FIN) {
-            if (action->fin)
-                action->fin(s, f);
-        }
-
-        if ((flags == (PICO_TCP_FIN | PICO_TCP_ACK)) || (flags == (PICO_TCP_FIN | PICO_TCP_ACK | PICO_TCP_PSH))) {
-            if (action->finack)
-                action->finack(s, f);
-        }
-
-        if (flags & PICO_TCP_RST) {
-            if (action->rst)
-                action->rst(s, f);
-        }
+        ret = tcp_action_by_flags(action, s, f, flags);
     }
 
     if (s->ev_pending)
@@ -2664,9 +2861,9 @@
         f->timestamp = TCP_TIME;
         add_retransmission_timer(t, t->rto + TCP_TIME);
         tcp_add_options_frame(t, f);
-        seq_diff = seq_compare(SEQN(f), SEQN(una));
+        seq_diff = pico_seq_compare(SEQN(f), SEQN(una));
         if (seq_diff < 0) {
-            dbg(">>> FATAL: seq diff is negative!\n");
+            tcp_dbg(">>> FATAL: seq diff is negative!\n");
             break;
         }
 
@@ -2716,7 +2913,7 @@
         /* Nothing to transmit. */
     }
 
-    if ((t->tcpq_out.frames == 0) && (s->state & PICO_SOCKET_STATE_SHUT_LOCAL)) {              /* if no more packets in queue, XXX replacled !f by tcpq check */
+    if ((t->tcpq_out.frames == 0) && (s->state & PICO_SOCKET_STATE_SHUT_LOCAL)) {              /* if no more packets in queue, XXX replaced !f by tcpq check */
         if(!checkLocalClosing(&t->sock))              /* check if local closing started and send fin */
         {
             checkRemoteClosing(&t->sock);              /* check if remote closing started and send fin */
@@ -2756,7 +2953,7 @@
         return f_new;
     }
 
-    pico_tcp_flags_update(f_new,&t->sock);
+    pico_tcp_flags_update(f_new, &t->sock);
     hdr = (struct pico_tcp_hdr *) f_new->transport_hdr;
     /* init new frame */
     f_new->payload += off;
@@ -2780,19 +2977,82 @@
     hdr->len = (uint8_t)((f_new->payload - f_new->transport_hdr) << 2u | (int8_t)t->jumbo);
 
     tcp_dbg_nagle("NAGLE make - joined %d segments, len %d bytes\n", test, total_payload_len);
-    tcp_add_options_frame(t,f_new);
+    tcp_add_options_frame(t, f_new);
 
     return f_new;
 }
 
+
+
+static int pico_tcp_push_nagle_enqueue(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+    if (pico_enqueue_segment(&t->tcpq_out, f) > 0) {
+        tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushing segment %08x, len %08x to socket %p\n", t->snd_last + 1, f->payload_len, t);
+        t->snd_last += f->payload_len;
+        return f->payload_len;
+    } else {
+        tcp_dbg("Enqueue failed.\n");
+        return 0;
+    }
+}
+
+static int pico_tcp_push_nagle_hold(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+    struct pico_frame *f_new;
+    uint32_t total_len = 0;
+    total_len = f->payload_len + t->tcpq_hold.size;
+    if ((total_len >= t->mss) && ((t->tcpq_out.max_size - t->tcpq_out.size) >= t->mss)) {
+        /* IF enough data in hold (>mss) AND space in out queue (>mss) */
+        /* add current frame in hold and make new segment */
+        if (pico_enqueue_segment(&t->tcpq_hold, f) > 0 ) {
+            tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushed into hold, make new (enqueued frames out %d)\n", t->tcpq_out.frames);
+            t->snd_last += f->payload_len;              /* XXX  WATCH OUT */
+            f_new = pico_hold_segment_make(t);
+        } else {
+            tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue hold failed 1\n");
+            return 0;
+        }
+
+        /* and put new frame in out queue */
+        if ((f_new != NULL) && (pico_enqueue_segment(&t->tcpq_out, f_new) > 0)) {
+            return f_new->payload_len;
+        } else {
+            tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue out failed, f_new = %p\n", f_new);
+            return -1;              /* XXX something seriously wrong */
+        }
+    } else {
+        /* ELSE put frame in hold queue */
+        if (pico_enqueue_segment(&t->tcpq_hold, f) > 0) {
+            tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushed into hold (enqueued frames out %d)\n", t->tcpq_out.frames);
+            t->snd_last += f->payload_len;              /* XXX  WATCH OUT */
+            return f->payload_len;
+        } else {
+            pico_err = PICO_ERR_EAGAIN;
+            tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue hold failed 2\n");
+        }
+    }
+
+    return 0;
+}
+
+
+static int pico_tcp_push_nagle_on(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+    /* Nagle's algorithm enabled, check if ready to send, or put frame in hold queue */
+    if (IS_TCP_IDLE(t) && IS_TCP_HOLDQ_EMPTY(t))
+        return pico_tcp_push_nagle_enqueue(t, f);
+
+    return pico_tcp_push_nagle_hold(t, f);
+}
+
+
+
 /* original behavior kept when Nagle disabled;
    Nagle algorithm added here, keeping hold frame queue instead of eg linked list of data */
 int pico_tcp_push(struct pico_protocol *self, struct pico_frame *f)
 {
     struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *)f->transport_hdr;
     struct pico_socket_tcp *t = (struct pico_socket_tcp *) f->sock;
-    struct pico_frame *f_new;
-    uint32_t total_len = 0;
     IGNORE_PARAMETER(self);
     pico_err = PICO_ERR_NOERR;
     hdr->trans.sport = t->sock.local_port;
@@ -2815,56 +3075,10 @@
             tcp_dbg("Enqueue failed.\n");
             return 0;
         }
+    } else {
+        return pico_tcp_push_nagle_on(t, f);
     }
-    /***************************************************************************/
-    else {
-        /* Nagle's algorithm enabled, check if ready to send, or put frame in hold queue */
-        if (IS_TCP_IDLE(t) && IS_TCP_HOLDQ_EMPTY(t)) {              /* opt 1. send frame */
-            if (pico_enqueue_segment(&t->tcpq_out, f) > 0) {
-                tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushing segment %08x, len %08x to socket %p\n", t->snd_last + 1, f->payload_len, t);
-                t->snd_last += f->payload_len;
-                return f->payload_len;
-            } else {
-                tcp_dbg("Enqueue failed.\n");
-                return 0;
-            }
-        } else {                                    /* opt 2. hold data back */
-            total_len = f->payload_len + t->tcpq_hold.size;
-            if ((total_len >= t->mss) && ((t->tcpq_out.max_size - t->tcpq_out.size) >= t->mss)) {              /* TODO check mss socket */
-                /* IF enough data in hold (>mss) AND space in out queue (>mss) */
-                /* add current frame in hold and make new segment */
-                if (pico_enqueue_segment(&t->tcpq_hold, f) > 0 ) {
-                    tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushed into hold, make new (enqueued frames out %d)\n", t->tcpq_out.frames);
-                    t->snd_last += f->payload_len;              /* XXX  WATCH OUT */
-                    f_new = pico_hold_segment_make(t);
-                } else {
-                    tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue hold failed 1\n");
-                    return 0;
-                }
-
-                /* and put new frame in out queue */
-                if ((f_new != NULL) && (pico_enqueue_segment(&t->tcpq_out, f_new) > 0)) {
-                    return f_new->payload_len;
-                } else {
-                    tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue out failed, f_new = %p\n", f_new);
-                    return -1;              /* XXX something seriously wrong */
-                }
-            } else {
-                /* ELSE put frame in hold queue */
-                if (pico_enqueue_segment(&t->tcpq_hold, f) > 0) {
-                    tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushed into hold (enqueued frames out %d)\n", t->tcpq_out.frames);
-                    t->snd_last += f->payload_len;              /* XXX  WATCH OUT */
-                    return f->payload_len;
-                } else {
-                    pico_err = PICO_ERR_EAGAIN;
-                    tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue hold failed 2\n");
-                    return 0;
-                }
-            }
-        }
-    }
-
-    /***************************************************************************/
+
 }
 
 inline static void tcp_discard_all_segments(struct pico_tcp_queue *tq)
@@ -2899,7 +3113,14 @@
         pico_timer_cancel(tcp->retrans_tmr);
         tcp->retrans_tmr = NULL;
     }
-
+    if(tcp->keepalive_tmr) {
+        pico_timer_cancel(tcp->keepalive_tmr);
+        tcp->keepalive_tmr = NULL;
+    }
+    if(tcp->fin_tmr) {
+        pico_timer_cancel(tcp->fin_tmr);
+        tcp->fin_tmr = NULL;
+    }
     tcp_discard_all_segments(&tcp->tcpq_in);
     tcp_discard_all_segments(&tcp->tcpq_out);
     tcp_discard_all_segments(&tcp->tcpq_hold);
@@ -2942,13 +3163,6 @@
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)sck;
     if(t->tcpq_out.frames == 0)
     {
-        /* canceling retrans timer when closing */
-        if(t->retrans_tmr)
-        {
-            pico_timer_cancel(t->retrans_tmr);
-            t->retrans_tmr = NULL;
-        }
-
         if(!checkLocalClosing(sck))
             checkRemoteClosing(sck);
     }
@@ -2961,6 +3175,7 @@
         pico_socket_del(s);
         return 0;
     }
+
     return -1;
 }
 
@@ -2997,4 +3212,32 @@
     return 0;
 }
 
+int pico_tcp_set_keepalive_probes(struct pico_socket *s, uint32_t value)
+{
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+    t->ka_probes = value;
+    return 0;
+}
+
+int pico_tcp_set_keepalive_intvl(struct pico_socket *s, uint32_t value)
+{
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+    t->ka_intvl = value;
+    return 0;
+}
+
+int pico_tcp_set_keepalive_time(struct pico_socket *s, uint32_t value)
+{
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+    t->ka_time = value;
+    return 0;
+}
+
+int pico_tcp_set_linger(struct pico_socket *s, uint32_t value)
+{
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+    t->linger_timeout = value;
+    return 0;
+}
+
 #endif /* PICO_SUPPORT_TCP */