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 RTOSgeneric mbed Ethernet driverhigh performance NXP LPC1768 specific Ethernet driverMulti-threading support for mbed RTOSBerkeley sockets and integration with the New Socket APIFork of the apps running on top of the New Socket APIScheduling 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.
modules/pico_tcp.c@154:6c0e92a80c4a, 2015-09-28 (annotated)
- Committer:
- tass
- Date:
- Mon Sep 28 14:14:17 2015 +0200
- Revision:
- 154:6c0e92a80c4a
- Parent:
- 152:a3d286bf94e5
To latest development.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
daniele | 29:1a47b7151851 | 1 | /********************************************************************* |
tass | 152:a3d286bf94e5 | 2 | PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. |
TASS Belgium NV |
131:4758606c9316 | 3 | See LICENSE and COPYING for usage. |
TASS Belgium NV |
131:4758606c9316 | 4 | |
TASS Belgium NV |
131:4758606c9316 | 5 | . |
TASS Belgium NV |
131:4758606c9316 | 6 | |
TASS Belgium NV |
131:4758606c9316 | 7 | Authors: Daniele Lacamera, Philippe Mariman |
TASS Belgium NV |
131:4758606c9316 | 8 | *********************************************************************/ |
daniele | 29:1a47b7151851 | 9 | |
daniele | 29:1a47b7151851 | 10 | #include "pico_tcp.h" |
daniele | 29:1a47b7151851 | 11 | #include "pico_config.h" |
daniele | 29:1a47b7151851 | 12 | #include "pico_eth.h" |
daniele | 29:1a47b7151851 | 13 | #include "pico_socket.h" |
daniele | 29:1a47b7151851 | 14 | #include "pico_stack.h" |
daniele | 29:1a47b7151851 | 15 | #include "pico_socket.h" |
daniele | 29:1a47b7151851 | 16 | #include "pico_queue.h" |
daniele | 29:1a47b7151851 | 17 | #include "pico_tree.h" |
daniele | 29:1a47b7151851 | 18 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 19 | #define TCP_IS_STATE(s, st) ((s->state & PICO_SOCKET_STATE_TCP) == st) |
daniele | 29:1a47b7151851 | 20 | #define TCP_SOCK(s) ((struct pico_socket_tcp *)s) |
TASS Belgium NV |
131:4758606c9316 | 21 | #define SEQN(f) ((f) ? (long_be(((struct pico_tcp_hdr *)((f)->transport_hdr))->seq)) : 0) |
TASS Belgium NV |
131:4758606c9316 | 22 | #define ACKN(f) ((f) ? (long_be(((struct pico_tcp_hdr *)((f)->transport_hdr))->ack)) : 0) |
daniele | 29:1a47b7151851 | 23 | |
tass | 152:a3d286bf94e5 | 24 | #define TCP_TIME (pico_time)(PICO_TIME_MS()) |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 25 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 26 | #define PICO_TCP_RTO_MIN (70) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 27 | #define PICO_TCP_RTO_MAX (120000) |
TASS Belgium NV |
131:4758606c9316 | 28 | #define PICO_TCP_IW 2 |
tass | 152:a3d286bf94e5 | 29 | #define PICO_TCP_SYN_TO 2000u |
tass | 48:40fc4462265c | 30 | #define PICO_TCP_ZOMBIE_TO 30000 |
daniele | 29:1a47b7151851 | 31 | |
TASS Belgium NV |
131:4758606c9316 | 32 | #define PICO_TCP_MAX_RETRANS 10 |
tass | 152:a3d286bf94e5 | 33 | #define PICO_TCP_MAX_CONNECT_RETRIES 3 |
daniele | 29:1a47b7151851 | 34 | |
daniele | 29:1a47b7151851 | 35 | #define PICO_TCP_LOOKAHEAD 0x00 |
daniele | 29:1a47b7151851 | 36 | #define PICO_TCP_FIRST_DUPACK 0x01 |
daniele | 29:1a47b7151851 | 37 | #define PICO_TCP_SECOND_DUPACK 0x02 |
daniele | 29:1a47b7151851 | 38 | #define PICO_TCP_RECOVER 0x03 |
daniele | 29:1a47b7151851 | 39 | #define PICO_TCP_BLACKOUT 0x04 |
daniele | 29:1a47b7151851 | 40 | #define PICO_TCP_UNREACHABLE 0x05 |
daniele | 29:1a47b7151851 | 41 | #define PICO_TCP_WINDOW_FULL 0x06 |
daniele | 29:1a47b7151851 | 42 | |
tass | 152:a3d286bf94e5 | 43 | #define ONE_GIGABYTE ((uint32_t)(1024UL * 1024UL * 1024UL)) |
tass | 152:a3d286bf94e5 | 44 | |
daniele | 29:1a47b7151851 | 45 | /* check if the Nagle algorithm is enabled on the socket */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 46 | #define IS_NAGLE_ENABLED(s) (!(!(!(s->opt_flags & (1u << PICO_SOCKET_OPT_TCPNODELAY))))) |
daniele | 29:1a47b7151851 | 47 | /* check if tcp connection is "idle" according to Nagle (RFC 896) */ |
daniele | 29:1a47b7151851 | 48 | #define IS_TCP_IDLE(t) ((t->in_flight == 0) && (t->tcpq_out.size == 0)) |
daniele | 29:1a47b7151851 | 49 | /* check if the hold queue contains data (again Nagle) */ |
daniele | 29:1a47b7151851 | 50 | #define IS_TCP_HOLDQ_EMPTY(t) (t->tcpq_hold.size == 0) |
daniele | 29:1a47b7151851 | 51 | |
TASS Belgium NV |
131:4758606c9316 | 52 | #define IS_INPUT_QUEUE(q) (q->pool.compare == input_segment_compare) |
TASS Belgium NV |
131:4758606c9316 | 53 | #define TCP_INPUT_OVERHEAD (sizeof(struct tcp_input_segment) + sizeof(struct pico_tree_node)) |
tass | 110:0ece1bbbd36e | 54 | |
tass | 123:dd26752a4538 | 55 | |
daniele | 29:1a47b7151851 | 56 | #ifdef PICO_SUPPORT_TCP |
TASS Belgium NV |
131:4758606c9316 | 57 | #define tcp_dbg_nagle(...) do {} while(0) |
TASS Belgium NV |
131:4758606c9316 | 58 | #define tcp_dbg_options(...) do {} while(0) |
TASS Belgium NV |
131:4758606c9316 | 59 | |
TASS Belgium NV |
131:4758606c9316 | 60 | |
TASS Belgium NV |
131:4758606c9316 | 61 | #define tcp_dbg(...) do {} while(0) |
TASS Belgium NV |
131:4758606c9316 | 62 | /* #define tcp_dbg dbg */ |
daniele | 29:1a47b7151851 | 63 | |
daniele | 29:1a47b7151851 | 64 | #ifdef PICO_SUPPORT_MUTEX |
TASS Belgium NV |
131:4758606c9316 | 65 | static void *Mutex = NULL; |
daniele | 29:1a47b7151851 | 66 | #endif |
daniele | 29:1a47b7151851 | 67 | |
daniele | 29:1a47b7151851 | 68 | |
daniele | 29:1a47b7151851 | 69 | |
TASS Belgium NV |
131:4758606c9316 | 70 | /* Input segment, used to keep only needed data, not the full frame */ |
tass | 110:0ece1bbbd36e | 71 | struct tcp_input_segment |
tass | 110:0ece1bbbd36e | 72 | { |
TASS Belgium NV |
131:4758606c9316 | 73 | uint32_t seq; |
TASS Belgium NV |
131:4758606c9316 | 74 | /* Pointer to payload */ |
TASS Belgium NV |
131:4758606c9316 | 75 | unsigned char *payload; |
TASS Belgium NV |
131:4758606c9316 | 76 | uint16_t payload_len; |
tass | 110:0ece1bbbd36e | 77 | }; |
tass | 110:0ece1bbbd36e | 78 | |
TASS Belgium NV |
131:4758606c9316 | 79 | /* Function to compare input segments */ |
tass | 110:0ece1bbbd36e | 80 | static int input_segment_compare(void *ka, void *kb) |
tass | 110:0ece1bbbd36e | 81 | { |
TASS Belgium NV |
131:4758606c9316 | 82 | struct tcp_input_segment *a = ka, *b = kb; |
tass | 152:a3d286bf94e5 | 83 | return pico_seq_compare(a->seq, b->seq); |
tass | 110:0ece1bbbd36e | 84 | } |
tass | 110:0ece1bbbd36e | 85 | |
TASS Belgium NV |
131:4758606c9316 | 86 | static struct tcp_input_segment *segment_from_frame(struct pico_frame *f) |
tass | 110:0ece1bbbd36e | 87 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 88 | struct tcp_input_segment *seg = PICO_ZALLOC(sizeof(struct tcp_input_segment)); |
tass | 152:a3d286bf94e5 | 89 | if ((!seg) || (!f->payload_len)) |
TASS Belgium NV |
131:4758606c9316 | 90 | return NULL; |
TASS Belgium NV |
131:4758606c9316 | 91 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 92 | seg->payload = PICO_ZALLOC(f->payload_len); |
TASS Belgium NV |
131:4758606c9316 | 93 | if(!seg->payload) |
TASS Belgium NV |
131:4758606c9316 | 94 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 95 | PICO_FREE(seg); |
TASS Belgium NV |
131:4758606c9316 | 96 | return NULL; |
TASS Belgium NV |
131:4758606c9316 | 97 | } |
TASS Belgium NV |
131:4758606c9316 | 98 | |
TASS Belgium NV |
131:4758606c9316 | 99 | seg->seq = SEQN(f); |
TASS Belgium NV |
131:4758606c9316 | 100 | seg->payload_len = f->payload_len; |
TASS Belgium NV |
131:4758606c9316 | 101 | memcpy(seg->payload, f->payload, seg->payload_len); |
TASS Belgium NV |
131:4758606c9316 | 102 | return seg; |
tass | 110:0ece1bbbd36e | 103 | } |
tass | 110:0ece1bbbd36e | 104 | |
TASS Belgium NV |
131:4758606c9316 | 105 | static int segment_compare(void *ka, void *kb) |
daniele | 29:1a47b7151851 | 106 | { |
TASS Belgium NV |
131:4758606c9316 | 107 | struct pico_frame *a = ka, *b = kb; |
tass | 152:a3d286bf94e5 | 108 | return pico_seq_compare(SEQN(a), SEQN(b)); |
daniele | 29:1a47b7151851 | 109 | } |
daniele | 29:1a47b7151851 | 110 | |
daniele | 29:1a47b7151851 | 111 | struct pico_tcp_queue |
daniele | 29:1a47b7151851 | 112 | { |
TASS Belgium NV |
131:4758606c9316 | 113 | struct pico_tree pool; |
TASS Belgium NV |
131:4758606c9316 | 114 | uint32_t max_size; |
TASS Belgium NV |
131:4758606c9316 | 115 | uint32_t size; |
TASS Belgium NV |
131:4758606c9316 | 116 | uint32_t frames; |
daniele | 29:1a47b7151851 | 117 | }; |
TASS Belgium NV |
131:4758606c9316 | 118 | |
tass | 97:e73b01cb3147 | 119 | static void tcp_discard_all_segments(struct pico_tcp_queue *tq); |
tass | 110:0ece1bbbd36e | 120 | static void *peek_segment(struct pico_tcp_queue *tq, uint32_t seq) |
daniele | 29:1a47b7151851 | 121 | { |
TASS Belgium NV |
131:4758606c9316 | 122 | if(!IS_INPUT_QUEUE(tq)) |
TASS Belgium NV |
131:4758606c9316 | 123 | { |
TASS Belgium NV |
131:4758606c9316 | 124 | struct pico_tcp_hdr H; |
TASS Belgium NV |
131:4758606c9316 | 125 | struct pico_frame f = { |
TASS Belgium NV |
131:4758606c9316 | 126 | 0 |
TASS Belgium NV |
131:4758606c9316 | 127 | }; |
TASS Belgium NV |
131:4758606c9316 | 128 | f.transport_hdr = (uint8_t *) (&H); |
TASS Belgium NV |
131:4758606c9316 | 129 | H.seq = long_be(seq); |
TASS Belgium NV |
131:4758606c9316 | 130 | |
TASS Belgium NV |
131:4758606c9316 | 131 | return pico_tree_findKey(&tq->pool, &f); |
TASS Belgium NV |
131:4758606c9316 | 132 | } |
TASS Belgium NV |
131:4758606c9316 | 133 | else |
TASS Belgium NV |
131:4758606c9316 | 134 | { |
tass | 154:6c0e92a80c4a | 135 | struct tcp_input_segment dummy = { 0 }; |
tass | 154:6c0e92a80c4a | 136 | dummy.seq = seq; |
TASS Belgium NV |
131:4758606c9316 | 137 | |
TASS Belgium NV |
131:4758606c9316 | 138 | return pico_tree_findKey(&tq->pool, &dummy); |
TASS Belgium NV |
131:4758606c9316 | 139 | } |
tass | 110:0ece1bbbd36e | 140 | |
daniele | 29:1a47b7151851 | 141 | } |
daniele | 29:1a47b7151851 | 142 | |
tass | 110:0ece1bbbd36e | 143 | static void *first_segment(struct pico_tcp_queue *tq) |
daniele | 29:1a47b7151851 | 144 | { |
TASS Belgium NV |
131:4758606c9316 | 145 | return pico_tree_first(&tq->pool); |
daniele | 29:1a47b7151851 | 146 | } |
daniele | 29:1a47b7151851 | 147 | |
tass | 110:0ece1bbbd36e | 148 | static void *next_segment(struct pico_tcp_queue *tq, void *cur) |
daniele | 29:1a47b7151851 | 149 | { |
TASS Belgium NV |
131:4758606c9316 | 150 | if (!cur) |
TASS Belgium NV |
131:4758606c9316 | 151 | return NULL; |
TASS Belgium NV |
131:4758606c9316 | 152 | |
TASS Belgium NV |
131:4758606c9316 | 153 | if(IS_INPUT_QUEUE(tq)) |
TASS Belgium NV |
131:4758606c9316 | 154 | { |
TASS Belgium NV |
131:4758606c9316 | 155 | return peek_segment(tq, ((struct tcp_input_segment *)cur)->seq + ((struct tcp_input_segment *)cur)->payload_len); |
TASS Belgium NV |
131:4758606c9316 | 156 | } |
TASS Belgium NV |
131:4758606c9316 | 157 | else |
TASS Belgium NV |
131:4758606c9316 | 158 | { |
TASS Belgium NV |
131:4758606c9316 | 159 | return peek_segment(tq, SEQN((struct pico_frame *)cur) + ((struct pico_frame *)cur)->payload_len); |
TASS Belgium NV |
131:4758606c9316 | 160 | } |
daniele | 29:1a47b7151851 | 161 | } |
daniele | 29:1a47b7151851 | 162 | |
tass | 152:a3d286bf94e5 | 163 | static uint16_t enqueue_segment_len(struct pico_tcp_queue *tq, void *f) |
tass | 152:a3d286bf94e5 | 164 | { |
tass | 152:a3d286bf94e5 | 165 | if (IS_INPUT_QUEUE(tq)) { |
tass | 152:a3d286bf94e5 | 166 | return ((struct tcp_input_segment *)f)->payload_len; |
tass | 152:a3d286bf94e5 | 167 | } else { |
tass | 152:a3d286bf94e5 | 168 | return (uint16_t)(((struct pico_frame *)f)->buffer_len); |
tass | 152:a3d286bf94e5 | 169 | } |
tass | 152:a3d286bf94e5 | 170 | } |
tass | 152:a3d286bf94e5 | 171 | |
tass | 152:a3d286bf94e5 | 172 | |
tass | 152:a3d286bf94e5 | 173 | static int32_t do_enqueue_segment(struct pico_tcp_queue *tq, void *f, uint16_t payload_len) |
daniele | 29:1a47b7151851 | 174 | { |
TASS Belgium NV |
131:4758606c9316 | 175 | int32_t ret = -1; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 176 | PICOTCP_MUTEX_LOCK(Mutex); |
TASS Belgium NV |
131:4758606c9316 | 177 | if ((tq->size + payload_len) > tq->max_size) |
TASS Belgium NV |
131:4758606c9316 | 178 | { |
TASS Belgium NV |
131:4758606c9316 | 179 | ret = 0; |
TASS Belgium NV |
131:4758606c9316 | 180 | goto out; |
TASS Belgium NV |
131:4758606c9316 | 181 | } |
TASS Belgium NV |
131:4758606c9316 | 182 | |
TASS Belgium NV |
131:4758606c9316 | 183 | if (pico_tree_insert(&tq->pool, f) != 0) |
TASS Belgium NV |
131:4758606c9316 | 184 | { |
TASS Belgium NV |
131:4758606c9316 | 185 | ret = 0; |
TASS Belgium NV |
131:4758606c9316 | 186 | goto out; |
TASS Belgium NV |
131:4758606c9316 | 187 | } |
TASS Belgium NV |
131:4758606c9316 | 188 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 189 | tq->size += (uint16_t)payload_len; |
TASS Belgium NV |
131:4758606c9316 | 190 | if (payload_len > 0) |
TASS Belgium NV |
131:4758606c9316 | 191 | tq->frames++; |
TASS Belgium NV |
131:4758606c9316 | 192 | |
TASS Belgium NV |
131:4758606c9316 | 193 | ret = (int32_t)payload_len; |
TASS Belgium NV |
131:4758606c9316 | 194 | |
TASS Belgium NV |
131:4758606c9316 | 195 | out: |
tass picotcp@tass.be | 149:5f4cb161cec3 | 196 | PICOTCP_MUTEX_UNLOCK(Mutex); |
TASS Belgium NV |
131:4758606c9316 | 197 | return ret; |
daniele | 29:1a47b7151851 | 198 | } |
daniele | 29:1a47b7151851 | 199 | |
tass | 152:a3d286bf94e5 | 200 | static int32_t pico_enqueue_segment(struct pico_tcp_queue *tq, void *f) |
tass | 152:a3d286bf94e5 | 201 | { |
tass | 152:a3d286bf94e5 | 202 | uint16_t payload_len; |
tass | 152:a3d286bf94e5 | 203 | |
tass | 152:a3d286bf94e5 | 204 | if (!f) |
tass | 152:a3d286bf94e5 | 205 | return -1; |
tass | 152:a3d286bf94e5 | 206 | |
tass | 152:a3d286bf94e5 | 207 | payload_len = enqueue_segment_len(tq, f); |
tass | 152:a3d286bf94e5 | 208 | |
tass | 152:a3d286bf94e5 | 209 | |
tass | 152:a3d286bf94e5 | 210 | if (payload_len == 0) { |
tass | 152:a3d286bf94e5 | 211 | tcp_dbg("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TRIED TO ENQUEUE INVALID SEGMENT!\n"); |
tass | 152:a3d286bf94e5 | 212 | return -1; |
tass | 152:a3d286bf94e5 | 213 | } |
tass | 152:a3d286bf94e5 | 214 | |
tass | 152:a3d286bf94e5 | 215 | return do_enqueue_segment(tq, f, payload_len); |
tass | 152:a3d286bf94e5 | 216 | } |
tass | 152:a3d286bf94e5 | 217 | |
tass | 110:0ece1bbbd36e | 218 | static void pico_discard_segment(struct pico_tcp_queue *tq, void *f) |
daniele | 29:1a47b7151851 | 219 | { |
TASS Belgium NV |
131:4758606c9316 | 220 | void *f1; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 221 | uint16_t payload_len = (uint16_t)((IS_INPUT_QUEUE(tq)) ? |
tass picotcp@tass.be | 149:5f4cb161cec3 | 222 | (((struct tcp_input_segment *)f)->payload_len) : |
tass picotcp@tass.be | 149:5f4cb161cec3 | 223 | (((struct pico_frame *)f)->buffer_len)); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 224 | PICOTCP_MUTEX_LOCK(Mutex); |
TASS Belgium NV |
131:4758606c9316 | 225 | f1 = pico_tree_delete(&tq->pool, f); |
TASS Belgium NV |
131:4758606c9316 | 226 | if (f1) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 227 | tq->size -= (uint16_t)payload_len; |
TASS Belgium NV |
131:4758606c9316 | 228 | if (payload_len > 0) |
TASS Belgium NV |
131:4758606c9316 | 229 | tq->frames--; |
TASS Belgium NV |
131:4758606c9316 | 230 | } |
TASS Belgium NV |
131:4758606c9316 | 231 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 232 | if(f1 && IS_INPUT_QUEUE(tq)) |
TASS Belgium NV |
131:4758606c9316 | 233 | { |
TASS Belgium NV |
131:4758606c9316 | 234 | struct tcp_input_segment *inp = f1; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 235 | PICO_FREE(inp->payload); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 236 | PICO_FREE(inp); |
TASS Belgium NV |
131:4758606c9316 | 237 | } |
TASS Belgium NV |
131:4758606c9316 | 238 | else |
TASS Belgium NV |
131:4758606c9316 | 239 | pico_frame_discard(f); |
TASS Belgium NV |
131:4758606c9316 | 240 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 241 | PICOTCP_MUTEX_UNLOCK(Mutex); |
daniele | 29:1a47b7151851 | 242 | } |
daniele | 29:1a47b7151851 | 243 | |
daniele | 29:1a47b7151851 | 244 | /* Structure for TCP socket */ |
daniele | 29:1a47b7151851 | 245 | struct tcp_sack_block { |
TASS Belgium NV |
131:4758606c9316 | 246 | uint32_t left; |
TASS Belgium NV |
131:4758606c9316 | 247 | uint32_t right; |
TASS Belgium NV |
131:4758606c9316 | 248 | struct tcp_sack_block *next; |
daniele | 29:1a47b7151851 | 249 | }; |
daniele | 29:1a47b7151851 | 250 | |
daniele | 29:1a47b7151851 | 251 | struct pico_socket_tcp { |
TASS Belgium NV |
131:4758606c9316 | 252 | struct pico_socket sock; |
TASS Belgium NV |
131:4758606c9316 | 253 | |
TASS Belgium NV |
131:4758606c9316 | 254 | /* Tree/queues */ |
TASS Belgium NV |
131:4758606c9316 | 255 | struct pico_tcp_queue tcpq_in; /* updated the input queue to hold input segments not the full frame. */ |
TASS Belgium NV |
131:4758606c9316 | 256 | struct pico_tcp_queue tcpq_out; |
TASS Belgium NV |
131:4758606c9316 | 257 | struct pico_tcp_queue tcpq_hold; /* buffer to hold delayed frames according to Nagle */ |
TASS Belgium NV |
131:4758606c9316 | 258 | |
TASS Belgium NV |
131:4758606c9316 | 259 | /* tcp_output */ |
TASS Belgium NV |
131:4758606c9316 | 260 | uint32_t snd_nxt; |
TASS Belgium NV |
131:4758606c9316 | 261 | uint32_t snd_last; |
TASS Belgium NV |
131:4758606c9316 | 262 | uint32_t snd_old_ack; |
TASS Belgium NV |
131:4758606c9316 | 263 | uint32_t snd_retry; |
TASS Belgium NV |
131:4758606c9316 | 264 | uint32_t snd_last_out; |
TASS Belgium NV |
131:4758606c9316 | 265 | |
TASS Belgium NV |
131:4758606c9316 | 266 | /* congestion control */ |
TASS Belgium NV |
131:4758606c9316 | 267 | uint32_t avg_rtt; |
TASS Belgium NV |
131:4758606c9316 | 268 | uint32_t rttvar; |
TASS Belgium NV |
131:4758606c9316 | 269 | uint32_t rto; |
TASS Belgium NV |
131:4758606c9316 | 270 | uint32_t in_flight; |
TASS Belgium NV |
131:4758606c9316 | 271 | struct pico_timer *retrans_tmr; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 272 | pico_time retrans_tmr_due; |
TASS Belgium NV |
131:4758606c9316 | 273 | uint16_t cwnd_counter; |
TASS Belgium NV |
131:4758606c9316 | 274 | uint16_t cwnd; |
TASS Belgium NV |
131:4758606c9316 | 275 | uint16_t ssthresh; |
TASS Belgium NV |
131:4758606c9316 | 276 | uint16_t recv_wnd; |
TASS Belgium NV |
131:4758606c9316 | 277 | uint16_t recv_wnd_scale; |
TASS Belgium NV |
131:4758606c9316 | 278 | |
TASS Belgium NV |
131:4758606c9316 | 279 | /* tcp_input */ |
TASS Belgium NV |
131:4758606c9316 | 280 | uint32_t rcv_nxt; |
TASS Belgium NV |
131:4758606c9316 | 281 | uint32_t rcv_ackd; |
TASS Belgium NV |
131:4758606c9316 | 282 | uint32_t rcv_processed; |
TASS Belgium NV |
131:4758606c9316 | 283 | uint16_t wnd; |
TASS Belgium NV |
131:4758606c9316 | 284 | uint16_t wnd_scale; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 285 | uint16_t remote_closed; |
TASS Belgium NV |
131:4758606c9316 | 286 | |
TASS Belgium NV |
131:4758606c9316 | 287 | /* options */ |
TASS Belgium NV |
131:4758606c9316 | 288 | uint32_t ts_nxt; |
TASS Belgium NV |
131:4758606c9316 | 289 | uint16_t mss; |
TASS Belgium NV |
131:4758606c9316 | 290 | uint8_t sack_ok; |
TASS Belgium NV |
131:4758606c9316 | 291 | uint8_t ts_ok; |
TASS Belgium NV |
131:4758606c9316 | 292 | uint8_t mss_ok; |
TASS Belgium NV |
131:4758606c9316 | 293 | uint8_t scale_ok; |
TASS Belgium NV |
131:4758606c9316 | 294 | struct tcp_sack_block *sacks; |
TASS Belgium NV |
131:4758606c9316 | 295 | uint8_t jumbo; |
tass | 152:a3d286bf94e5 | 296 | uint32_t linger_timeout; |
TASS Belgium NV |
131:4758606c9316 | 297 | |
TASS Belgium NV |
131:4758606c9316 | 298 | /* Transmission */ |
TASS Belgium NV |
131:4758606c9316 | 299 | uint8_t x_mode; |
TASS Belgium NV |
131:4758606c9316 | 300 | uint8_t dupacks; |
TASS Belgium NV |
131:4758606c9316 | 301 | uint8_t backoff; |
TASS Belgium NV |
131:4758606c9316 | 302 | uint8_t localZeroWindow; |
tass | 152:a3d286bf94e5 | 303 | |
tass | 152:a3d286bf94e5 | 304 | /* Keepalive */ |
tass | 152:a3d286bf94e5 | 305 | struct pico_timer *keepalive_tmr; |
tass | 152:a3d286bf94e5 | 306 | pico_time ack_timestamp; |
tass | 152:a3d286bf94e5 | 307 | uint32_t ka_time; |
tass | 152:a3d286bf94e5 | 308 | uint32_t ka_intvl; |
tass | 152:a3d286bf94e5 | 309 | uint32_t ka_probes; |
tass | 152:a3d286bf94e5 | 310 | uint32_t ka_retries_count; |
tass | 152:a3d286bf94e5 | 311 | |
tass | 152:a3d286bf94e5 | 312 | /* FIN timer */ |
tass | 152:a3d286bf94e5 | 313 | struct pico_timer *fin_tmr; |
daniele | 29:1a47b7151851 | 314 | }; |
daniele | 29:1a47b7151851 | 315 | |
daniele | 29:1a47b7151851 | 316 | /* Queues */ |
TASS Belgium NV |
131:4758606c9316 | 317 | static struct pico_queue tcp_in = { |
TASS Belgium NV |
131:4758606c9316 | 318 | 0 |
TASS Belgium NV |
131:4758606c9316 | 319 | }; |
TASS Belgium NV |
131:4758606c9316 | 320 | static struct pico_queue tcp_out = { |
TASS Belgium NV |
131:4758606c9316 | 321 | 0 |
TASS Belgium NV |
131:4758606c9316 | 322 | }; |
daniele | 29:1a47b7151851 | 323 | |
daniele | 29:1a47b7151851 | 324 | /* If Nagle enabled, this function can make 1 new segment from smaller segments in hold queue */ |
TASS Belgium NV |
131:4758606c9316 | 325 | static struct pico_frame *pico_hold_segment_make(struct pico_socket_tcp *t); |
daniele | 29:1a47b7151851 | 326 | |
daniele | 29:1a47b7151851 | 327 | /* checks if tcpq_in is empty */ |
daniele | 29:1a47b7151851 | 328 | int pico_tcp_queue_in_is_empty(struct pico_socket *s) |
daniele | 29:1a47b7151851 | 329 | { |
TASS Belgium NV |
131:4758606c9316 | 330 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
TASS Belgium NV |
131:4758606c9316 | 331 | |
TASS Belgium NV |
131:4758606c9316 | 332 | if (t->tcpq_in.frames == 0) |
TASS Belgium NV |
131:4758606c9316 | 333 | return 1; |
TASS Belgium NV |
131:4758606c9316 | 334 | else |
TASS Belgium NV |
131:4758606c9316 | 335 | return 0; |
daniele | 29:1a47b7151851 | 336 | } |
daniele | 29:1a47b7151851 | 337 | |
daniele | 29:1a47b7151851 | 338 | /* Useful for getting rid of the beginning of the buffer (read() op) */ |
daniele | 29:1a47b7151851 | 339 | static int release_until(struct pico_tcp_queue *q, uint32_t seq) |
daniele | 29:1a47b7151851 | 340 | { |
TASS Belgium NV |
131:4758606c9316 | 341 | void *head = first_segment(q); |
TASS Belgium NV |
131:4758606c9316 | 342 | int ret = 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 343 | int32_t seq_result = 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 344 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 345 | if (!head) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 346 | return ret; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 347 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 348 | do { |
TASS Belgium NV |
131:4758606c9316 | 349 | void *cur = head; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 350 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 351 | if (IS_INPUT_QUEUE(q)) |
tass | 152:a3d286bf94e5 | 352 | seq_result = pico_seq_compare(((struct tcp_input_segment *)head)->seq + ((struct tcp_input_segment *)head)->payload_len, seq); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 353 | else |
tass | 152:a3d286bf94e5 | 354 | seq_result = pico_seq_compare(SEQN((struct pico_frame *)head) + ((struct pico_frame *)head)->payload_len, seq); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 355 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 356 | if (seq_result <= 0) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 357 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 358 | head = next_segment(q, cur); |
tass | 152:a3d286bf94e5 | 359 | //tcp_dbg("Releasing %08x, len: %d\n", SEQN((struct pico_frame *)head), ((struct pico_frame *)head)->payload_len); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 360 | pico_discard_segment(q, cur); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 361 | ret++; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 362 | } else { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 363 | break; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 364 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 365 | } while (head); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 366 | |
TASS Belgium NV |
131:4758606c9316 | 367 | return ret; |
daniele | 29:1a47b7151851 | 368 | } |
daniele | 29:1a47b7151851 | 369 | |
TASS Belgium NV |
131:4758606c9316 | 370 | static int release_all_until(struct pico_tcp_queue *q, uint32_t seq, pico_time *timestamp) |
daniele | 29:1a47b7151851 | 371 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 372 | void *f = NULL; |
TASS Belgium NV |
131:4758606c9316 | 373 | struct pico_tree_node *idx, *temp; |
TASS Belgium NV |
131:4758606c9316 | 374 | int seq_result; |
TASS Belgium NV |
131:4758606c9316 | 375 | int ret = 0; |
TASS Belgium NV |
131:4758606c9316 | 376 | *timestamp = 0; |
TASS Belgium NV |
131:4758606c9316 | 377 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 378 | pico_tree_foreach_safe(idx, &q->pool, temp) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 379 | { |
TASS Belgium NV |
131:4758606c9316 | 380 | f = idx->keyValue; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 381 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 382 | if (IS_INPUT_QUEUE(q)) |
tass | 152:a3d286bf94e5 | 383 | seq_result = pico_seq_compare(((struct tcp_input_segment *)f)->seq + ((struct tcp_input_segment *)f)->payload_len, seq); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 384 | else |
tass | 152:a3d286bf94e5 | 385 | seq_result = pico_seq_compare(SEQN((struct pico_frame *)f) + ((struct pico_frame *)f)->payload_len, seq); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 386 | |
TASS Belgium NV |
131:4758606c9316 | 387 | if (seq_result <= 0) { |
TASS Belgium NV |
131:4758606c9316 | 388 | tcp_dbg("Releasing %p\n", f); |
tass | 152:a3d286bf94e5 | 389 | if ((seq_result == 0) && !IS_INPUT_QUEUE(q)) |
TASS Belgium NV |
131:4758606c9316 | 390 | *timestamp = ((struct pico_frame *)f)->timestamp; |
TASS Belgium NV |
131:4758606c9316 | 391 | |
TASS Belgium NV |
131:4758606c9316 | 392 | pico_discard_segment(q, f); |
TASS Belgium NV |
131:4758606c9316 | 393 | ret++; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 394 | } else { |
TASS Belgium NV |
131:4758606c9316 | 395 | return ret; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 396 | } |
TASS Belgium NV |
131:4758606c9316 | 397 | } |
TASS Belgium NV |
131:4758606c9316 | 398 | return ret; |
daniele | 29:1a47b7151851 | 399 | } |
daniele | 29:1a47b7151851 | 400 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 401 | |
daniele | 29:1a47b7151851 | 402 | /* API calls */ |
daniele | 29:1a47b7151851 | 403 | |
daniele | 29:1a47b7151851 | 404 | uint16_t pico_tcp_checksum_ipv4(struct pico_frame *f) |
daniele | 29:1a47b7151851 | 405 | { |
TASS Belgium NV |
131:4758606c9316 | 406 | struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; |
TASS Belgium NV |
131:4758606c9316 | 407 | struct pico_tcp_hdr *tcp_hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 408 | struct pico_socket *s = f->sock; |
TASS Belgium NV |
131:4758606c9316 | 409 | struct pico_ipv4_pseudo_hdr pseudo; |
TASS Belgium NV |
131:4758606c9316 | 410 | |
TASS Belgium NV |
131:4758606c9316 | 411 | if (s) { |
TASS Belgium NV |
131:4758606c9316 | 412 | /* Case of outgoing frame */ |
TASS Belgium NV |
131:4758606c9316 | 413 | /* dbg("TCP CRC: on outgoing frame\n"); */ |
TASS Belgium NV |
131:4758606c9316 | 414 | pseudo.src.addr = s->local_addr.ip4.addr; |
TASS Belgium NV |
131:4758606c9316 | 415 | pseudo.dst.addr = s->remote_addr.ip4.addr; |
TASS Belgium NV |
131:4758606c9316 | 416 | } else { |
tass | 152:a3d286bf94e5 | 417 | /* Case of incoming frame */ |
tass | 152:a3d286bf94e5 | 418 | /* dbg("TCP CRC: on incoming frame\n"); */ |
TASS Belgium NV |
131:4758606c9316 | 419 | pseudo.src.addr = hdr->src.addr; |
TASS Belgium NV |
131:4758606c9316 | 420 | pseudo.dst.addr = hdr->dst.addr; |
TASS Belgium NV |
131:4758606c9316 | 421 | } |
TASS Belgium NV |
131:4758606c9316 | 422 | |
TASS Belgium NV |
131:4758606c9316 | 423 | pseudo.zeros = 0; |
TASS Belgium NV |
131:4758606c9316 | 424 | pseudo.proto = PICO_PROTO_TCP; |
TASS Belgium NV |
131:4758606c9316 | 425 | pseudo.len = (uint16_t)short_be(f->transport_len); |
TASS Belgium NV |
131:4758606c9316 | 426 | |
TASS Belgium NV |
131:4758606c9316 | 427 | return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv4_pseudo_hdr), tcp_hdr, f->transport_len); |
daniele | 29:1a47b7151851 | 428 | } |
daniele | 29:1a47b7151851 | 429 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 430 | #ifdef PICO_SUPPORT_IPV6 |
tass picotcp@tass.be | 149:5f4cb161cec3 | 431 | uint16_t pico_tcp_checksum_ipv6(struct pico_frame *f) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 432 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 433 | struct pico_ipv6_hdr *ipv6_hdr = (struct pico_ipv6_hdr *)f->net_hdr; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 434 | struct pico_tcp_hdr *tcp_hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 435 | struct pico_ipv6_pseudo_hdr pseudo; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 436 | struct pico_socket *s = f->sock; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 437 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 438 | /* XXX If the IPv6 packet contains a Routing header, the Destination |
tass picotcp@tass.be | 149:5f4cb161cec3 | 439 | * Address used in the pseudo-header is that of the final destination */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 440 | if (s) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 441 | /* Case of outgoing frame */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 442 | pseudo.src = s->local_addr.ip6; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 443 | pseudo.dst = s->remote_addr.ip6; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 444 | } else { |
tass | 152:a3d286bf94e5 | 445 | /* Case of incoming frame */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 446 | pseudo.src = ipv6_hdr->src; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 447 | pseudo.dst = ipv6_hdr->dst; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 448 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 449 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 450 | pseudo.zero[0] = 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 451 | pseudo.zero[1] = 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 452 | pseudo.zero[2] = 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 453 | pseudo.len = long_be(f->transport_len); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 454 | pseudo.nxthdr = PICO_PROTO_TCP; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 455 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 456 | return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv6_pseudo_hdr), tcp_hdr, f->transport_len); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 457 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 458 | #endif |
tass picotcp@tass.be | 149:5f4cb161cec3 | 459 | |
tass | 152:a3d286bf94e5 | 460 | #ifdef PICO_SUPPORT_IPV4 |
tass | 152:a3d286bf94e5 | 461 | static inline int checksum_is_ipv4(struct pico_frame *f) |
tass | 152:a3d286bf94e5 | 462 | { |
tass | 152:a3d286bf94e5 | 463 | return (IS_IPV4(f) || (f->sock && (f->sock->net == &pico_proto_ipv4))); |
tass | 152:a3d286bf94e5 | 464 | } |
tass | 152:a3d286bf94e5 | 465 | #endif |
tass | 152:a3d286bf94e5 | 466 | |
tass | 152:a3d286bf94e5 | 467 | #ifdef PICO_SUPPORT_IPV6 |
tass | 152:a3d286bf94e5 | 468 | static inline int checksum_is_ipv6(struct pico_frame *f) |
tass | 152:a3d286bf94e5 | 469 | { |
tass | 152:a3d286bf94e5 | 470 | return ((IS_IPV6(f)) || (f->sock && (f->sock->net == &pico_proto_ipv6))); |
tass | 152:a3d286bf94e5 | 471 | } |
tass | 152:a3d286bf94e5 | 472 | #endif |
tass | 152:a3d286bf94e5 | 473 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 474 | uint16_t pico_tcp_checksum(struct pico_frame *f) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 475 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 476 | (void)f; |
tass | 152:a3d286bf94e5 | 477 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 478 | #ifdef PICO_SUPPORT_IPV4 |
tass | 152:a3d286bf94e5 | 479 | if (checksum_is_ipv4(f)) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 480 | return pico_tcp_checksum_ipv4(f); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 481 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 482 | #endif |
tass | 152:a3d286bf94e5 | 483 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 484 | #ifdef PICO_SUPPORT_IPV6 |
tass | 152:a3d286bf94e5 | 485 | if (checksum_is_ipv6(f)) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 486 | return pico_tcp_checksum_ipv6(f); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 487 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 488 | #endif |
tass picotcp@tass.be | 149:5f4cb161cec3 | 489 | return 0xffff; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 490 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 491 | |
tass | 32:865c101e0874 | 492 | static void tcp_send_fin(struct pico_socket_tcp *t); |
daniele | 29:1a47b7151851 | 493 | static int pico_tcp_process_out(struct pico_protocol *self, struct pico_frame *f) |
daniele | 29:1a47b7151851 | 494 | { |
TASS Belgium NV |
131:4758606c9316 | 495 | struct pico_tcp_hdr *hdr; |
TASS Belgium NV |
131:4758606c9316 | 496 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)f->sock; |
TASS Belgium NV |
131:4758606c9316 | 497 | IGNORE_PARAMETER(self); |
TASS Belgium NV |
131:4758606c9316 | 498 | hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 499 | f->sock->timestamp = TCP_TIME; |
TASS Belgium NV |
131:4758606c9316 | 500 | if (f->payload_len > 0) { |
TASS Belgium NV |
131:4758606c9316 | 501 | tcp_dbg("Process out: sending %p (%d bytes)\n", f, f->payload_len); |
TASS Belgium NV |
131:4758606c9316 | 502 | } else { |
TASS Belgium NV |
131:4758606c9316 | 503 | tcp_dbg("Sending empty packet\n"); |
daniele | 29:1a47b7151851 | 504 | } |
TASS Belgium NV |
131:4758606c9316 | 505 | |
TASS Belgium NV |
131:4758606c9316 | 506 | if (f->payload_len > 0) { |
tass | 152:a3d286bf94e5 | 507 | if (pico_seq_compare(SEQN(f) + f->payload_len, t->snd_nxt) > 0) { |
TASS Belgium NV |
131:4758606c9316 | 508 | t->snd_nxt = SEQN(f) + f->payload_len; |
TASS Belgium NV |
131:4758606c9316 | 509 | tcp_dbg("%s: snd_nxt is now %08x\n", __FUNCTION__, t->snd_nxt); |
TASS Belgium NV |
131:4758606c9316 | 510 | } |
TASS Belgium NV |
131:4758606c9316 | 511 | } else if (hdr->flags == PICO_TCP_ACK) { /* pure ack */ |
TASS Belgium NV |
131:4758606c9316 | 512 | /* hdr->seq = long_be(t->snd_nxt); / * XXX disabled this to not to mess with seq nrs of ACKs anymore * / */ |
TASS Belgium NV |
131:4758606c9316 | 513 | } else { |
TASS Belgium NV |
131:4758606c9316 | 514 | tcp_dbg("%s: non-pure ACK with len=0, fl:%04x\n", __FUNCTION__, hdr->flags); |
TASS Belgium NV |
131:4758606c9316 | 515 | } |
TASS Belgium NV |
131:4758606c9316 | 516 | |
TASS Belgium NV |
131:4758606c9316 | 517 | pico_network_send(f); |
TASS Belgium NV |
131:4758606c9316 | 518 | return 0; |
daniele | 29:1a47b7151851 | 519 | } |
daniele | 29:1a47b7151851 | 520 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 521 | int pico_tcp_push(struct pico_protocol *self, struct pico_frame *data); |
daniele | 29:1a47b7151851 | 522 | |
daniele | 29:1a47b7151851 | 523 | /* Interface: protocol definition */ |
daniele | 29:1a47b7151851 | 524 | struct pico_protocol pico_proto_tcp = { |
TASS Belgium NV |
131:4758606c9316 | 525 | .name = "tcp", |
TASS Belgium NV |
131:4758606c9316 | 526 | .proto_number = PICO_PROTO_TCP, |
TASS Belgium NV |
131:4758606c9316 | 527 | .layer = PICO_LAYER_TRANSPORT, |
TASS Belgium NV |
131:4758606c9316 | 528 | .process_in = pico_transport_process_in, |
TASS Belgium NV |
131:4758606c9316 | 529 | .process_out = pico_tcp_process_out, |
TASS Belgium NV |
131:4758606c9316 | 530 | .push = pico_tcp_push, |
TASS Belgium NV |
131:4758606c9316 | 531 | .q_in = &tcp_in, |
TASS Belgium NV |
131:4758606c9316 | 532 | .q_out = &tcp_out, |
daniele | 29:1a47b7151851 | 533 | }; |
daniele | 29:1a47b7151851 | 534 | |
daniele | 29:1a47b7151851 | 535 | static uint32_t pico_paws(void) |
daniele | 29:1a47b7151851 | 536 | { |
TASS Belgium NV |
131:4758606c9316 | 537 | static uint32_t _paws = 0; |
TASS Belgium NV |
131:4758606c9316 | 538 | _paws = pico_rand(); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 539 | return long_be(_paws); |
daniele | 29:1a47b7151851 | 540 | } |
daniele | 29:1a47b7151851 | 541 | |
tass | 152:a3d286bf94e5 | 542 | static inline void tcp_add_sack_option(struct pico_socket_tcp *ts, struct pico_frame *f, uint16_t flags, uint32_t *ii) |
tass | 152:a3d286bf94e5 | 543 | { |
tass | 152:a3d286bf94e5 | 544 | if (flags & PICO_TCP_ACK) { |
tass | 152:a3d286bf94e5 | 545 | struct tcp_sack_block *sb; |
tass | 152:a3d286bf94e5 | 546 | uint32_t len_off; |
tass | 152:a3d286bf94e5 | 547 | |
tass | 152:a3d286bf94e5 | 548 | if (ts->sack_ok && ts->sacks) { |
tass | 152:a3d286bf94e5 | 549 | f->start[(*ii)++] = PICO_TCP_OPTION_SACK; |
tass | 152:a3d286bf94e5 | 550 | len_off = *ii; |
tass | 152:a3d286bf94e5 | 551 | f->start[(*ii)++] = PICO_TCPOPTLEN_SACK; |
tass | 152:a3d286bf94e5 | 552 | while(ts->sacks) { |
tass | 152:a3d286bf94e5 | 553 | sb = ts->sacks; |
tass | 152:a3d286bf94e5 | 554 | ts->sacks = sb->next; |
tass | 152:a3d286bf94e5 | 555 | memcpy(f->start + *ii, sb, 2 * sizeof(uint32_t)); |
tass | 152:a3d286bf94e5 | 556 | *ii += (2 * (uint32_t)sizeof(uint32_t)); |
tass | 152:a3d286bf94e5 | 557 | f->start[len_off] = (uint8_t)(f->start[len_off] + (2 * sizeof(uint32_t))); |
tass | 152:a3d286bf94e5 | 558 | PICO_FREE(sb); |
tass | 152:a3d286bf94e5 | 559 | } |
tass | 152:a3d286bf94e5 | 560 | } |
tass | 152:a3d286bf94e5 | 561 | } |
tass | 152:a3d286bf94e5 | 562 | } |
tass | 152:a3d286bf94e5 | 563 | |
tass | 70:cd218dd180e5 | 564 | static void tcp_add_options(struct pico_socket_tcp *ts, struct pico_frame *f, uint16_t flags, uint16_t optsiz) |
daniele | 29:1a47b7151851 | 565 | { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 566 | uint32_t tsval = long_be((uint32_t)TCP_TIME); |
TASS Belgium NV |
131:4758606c9316 | 567 | uint32_t tsecr = long_be(ts->ts_nxt); |
TASS Belgium NV |
131:4758606c9316 | 568 | uint32_t i = 0; |
TASS Belgium NV |
131:4758606c9316 | 569 | f->start = f->transport_hdr + PICO_SIZE_TCPHDR; |
TASS Belgium NV |
131:4758606c9316 | 570 | |
TASS Belgium NV |
131:4758606c9316 | 571 | memset(f->start, PICO_TCP_OPTION_NOOP, optsiz); /* fill blanks with noop */ |
TASS Belgium NV |
131:4758606c9316 | 572 | |
TASS Belgium NV |
131:4758606c9316 | 573 | if (flags & PICO_TCP_SYN) { |
TASS Belgium NV |
131:4758606c9316 | 574 | f->start[i++] = PICO_TCP_OPTION_MSS; |
TASS Belgium NV |
131:4758606c9316 | 575 | f->start[i++] = PICO_TCPOPTLEN_MSS; |
TASS Belgium NV |
131:4758606c9316 | 576 | f->start[i++] = (uint8_t)((ts->mss >> 8) & 0xFF); |
TASS Belgium NV |
131:4758606c9316 | 577 | f->start[i++] = (uint8_t)(ts->mss & 0xFF); |
TASS Belgium NV |
131:4758606c9316 | 578 | f->start[i++] = PICO_TCP_OPTION_SACK_OK; |
TASS Belgium NV |
131:4758606c9316 | 579 | f->start[i++] = PICO_TCPOPTLEN_SACK_OK; |
TASS Belgium NV |
131:4758606c9316 | 580 | } |
TASS Belgium NV |
131:4758606c9316 | 581 | |
TASS Belgium NV |
131:4758606c9316 | 582 | f->start[i++] = PICO_TCP_OPTION_WS; |
TASS Belgium NV |
131:4758606c9316 | 583 | f->start[i++] = PICO_TCPOPTLEN_WS; |
TASS Belgium NV |
131:4758606c9316 | 584 | f->start[i++] = (uint8_t)(ts->wnd_scale); |
TASS Belgium NV |
131:4758606c9316 | 585 | |
TASS Belgium NV |
131:4758606c9316 | 586 | if ((flags & PICO_TCP_SYN) || ts->ts_ok) { |
TASS Belgium NV |
131:4758606c9316 | 587 | f->start[i++] = PICO_TCP_OPTION_TIMESTAMP; |
TASS Belgium NV |
131:4758606c9316 | 588 | f->start[i++] = PICO_TCPOPTLEN_TIMESTAMP; |
TASS Belgium NV |
131:4758606c9316 | 589 | memcpy(f->start + i, &tsval, 4); |
TASS Belgium NV |
131:4758606c9316 | 590 | i += 4; |
TASS Belgium NV |
131:4758606c9316 | 591 | memcpy(f->start + i, &tsecr, 4); |
TASS Belgium NV |
131:4758606c9316 | 592 | i += 4; |
TASS Belgium NV |
131:4758606c9316 | 593 | } |
TASS Belgium NV |
131:4758606c9316 | 594 | |
tass | 152:a3d286bf94e5 | 595 | tcp_add_sack_option(ts, f, flags, &i); |
TASS Belgium NV |
131:4758606c9316 | 596 | |
TASS Belgium NV |
131:4758606c9316 | 597 | if (i < optsiz) |
TASS Belgium NV |
131:4758606c9316 | 598 | f->start[ optsiz - 1 ] = PICO_TCP_OPTION_END; |
TASS Belgium NV |
131:4758606c9316 | 599 | } |
TASS Belgium NV |
131:4758606c9316 | 600 | |
TASS Belgium NV |
131:4758606c9316 | 601 | static uint16_t tcp_options_size_frame(struct pico_frame *f) |
TASS Belgium NV |
131:4758606c9316 | 602 | { |
TASS Belgium NV |
131:4758606c9316 | 603 | uint16_t size = 0; |
TASS Belgium NV |
131:4758606c9316 | 604 | |
TASS Belgium NV |
131:4758606c9316 | 605 | /* Always update window scale. */ |
TASS Belgium NV |
131:4758606c9316 | 606 | size = (uint16_t)(size + PICO_TCPOPTLEN_WS); |
TASS Belgium NV |
131:4758606c9316 | 607 | if (f->transport_flags_saved) |
TASS Belgium NV |
131:4758606c9316 | 608 | size = (uint16_t)(size + PICO_TCPOPTLEN_TIMESTAMP); |
TASS Belgium NV |
131:4758606c9316 | 609 | |
TASS Belgium NV |
131:4758606c9316 | 610 | size = (uint16_t)(size + PICO_TCPOPTLEN_END); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 611 | size = (uint16_t)(((uint16_t)(size + 3u) >> 2u) << 2u); |
TASS Belgium NV |
131:4758606c9316 | 612 | return size; |
TASS Belgium NV |
131:4758606c9316 | 613 | } |
TASS Belgium NV |
131:4758606c9316 | 614 | |
TASS Belgium NV |
131:4758606c9316 | 615 | static void tcp_add_options_frame(struct pico_socket_tcp *ts, struct pico_frame *f) |
TASS Belgium NV |
131:4758606c9316 | 616 | { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 617 | uint32_t tsval = long_be((uint32_t)TCP_TIME); |
TASS Belgium NV |
131:4758606c9316 | 618 | uint32_t tsecr = long_be(ts->ts_nxt); |
TASS Belgium NV |
131:4758606c9316 | 619 | uint32_t i = 0; |
TASS Belgium NV |
131:4758606c9316 | 620 | uint16_t optsiz = tcp_options_size_frame(f); |
TASS Belgium NV |
131:4758606c9316 | 621 | |
TASS Belgium NV |
131:4758606c9316 | 622 | f->start = f->transport_hdr + PICO_SIZE_TCPHDR; |
TASS Belgium NV |
131:4758606c9316 | 623 | |
TASS Belgium NV |
131:4758606c9316 | 624 | memset(f->start, PICO_TCP_OPTION_NOOP, optsiz); /* fill blanks with noop */ |
TASS Belgium NV |
131:4758606c9316 | 625 | |
TASS Belgium NV |
131:4758606c9316 | 626 | |
TASS Belgium NV |
131:4758606c9316 | 627 | f->start[i++] = PICO_TCP_OPTION_WS; |
TASS Belgium NV |
131:4758606c9316 | 628 | f->start[i++] = PICO_TCPOPTLEN_WS; |
TASS Belgium NV |
131:4758606c9316 | 629 | f->start[i++] = (uint8_t)(ts->wnd_scale); |
TASS Belgium NV |
131:4758606c9316 | 630 | |
TASS Belgium NV |
131:4758606c9316 | 631 | if (f->transport_flags_saved) { |
TASS Belgium NV |
131:4758606c9316 | 632 | f->start[i++] = PICO_TCP_OPTION_TIMESTAMP; |
TASS Belgium NV |
131:4758606c9316 | 633 | f->start[i++] = PICO_TCPOPTLEN_TIMESTAMP; |
TASS Belgium NV |
131:4758606c9316 | 634 | memcpy(f->start + i, &tsval, 4); |
TASS Belgium NV |
131:4758606c9316 | 635 | i += 4; |
TASS Belgium NV |
131:4758606c9316 | 636 | memcpy(f->start + i, &tsecr, 4); |
TASS Belgium NV |
131:4758606c9316 | 637 | i += 4; |
TASS Belgium NV |
131:4758606c9316 | 638 | } |
TASS Belgium NV |
131:4758606c9316 | 639 | |
TASS Belgium NV |
131:4758606c9316 | 640 | if (i < optsiz) |
TASS Belgium NV |
131:4758606c9316 | 641 | f->start[ optsiz - 1 ] = PICO_TCP_OPTION_END; |
daniele | 29:1a47b7151851 | 642 | } |
daniele | 29:1a47b7151851 | 643 | |
daniele | 29:1a47b7151851 | 644 | static void tcp_send_ack(struct pico_socket_tcp *t); |
tass | 75:a9abf10138d6 | 645 | #define tcp_send_windowUpdate(t) (tcp_send_ack(t)) |
daniele | 29:1a47b7151851 | 646 | |
tass | 152:a3d286bf94e5 | 647 | static inline void tcp_set_space_check_winupdate(struct pico_socket_tcp *t, int32_t space, uint32_t shift) |
daniele | 29:1a47b7151851 | 648 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 649 | if (((uint32_t)space != t->wnd) || (shift != t->wnd_scale) || ((space - t->wnd) > (int32_t)((uint32_t)space >> 2u))) { |
TASS Belgium NV |
131:4758606c9316 | 650 | t->wnd = (uint16_t)space; |
TASS Belgium NV |
131:4758606c9316 | 651 | t->wnd_scale = (uint16_t)shift; |
TASS Belgium NV |
131:4758606c9316 | 652 | |
TASS Belgium NV |
131:4758606c9316 | 653 | if(t->wnd == 0) /* mark the entering to zero window state */ |
TASS Belgium NV |
131:4758606c9316 | 654 | t->localZeroWindow = 1u; |
TASS Belgium NV |
131:4758606c9316 | 655 | else if(t->localZeroWindow) |
TASS Belgium NV |
131:4758606c9316 | 656 | { |
TASS Belgium NV |
131:4758606c9316 | 657 | t->localZeroWindow = 0u; |
TASS Belgium NV |
131:4758606c9316 | 658 | tcp_send_windowUpdate(t); |
TASS Belgium NV |
131:4758606c9316 | 659 | } |
TASS Belgium NV |
131:4758606c9316 | 660 | } |
daniele | 29:1a47b7151851 | 661 | } |
daniele | 29:1a47b7151851 | 662 | |
tass | 152:a3d286bf94e5 | 663 | static void tcp_set_space(struct pico_socket_tcp *t) |
tass | 152:a3d286bf94e5 | 664 | { |
tass | 152:a3d286bf94e5 | 665 | int32_t space; |
tass | 152:a3d286bf94e5 | 666 | uint32_t shift = 0; |
tass | 152:a3d286bf94e5 | 667 | |
tass | 152:a3d286bf94e5 | 668 | if (t->tcpq_in.max_size == 0) { |
tass | 152:a3d286bf94e5 | 669 | space = ONE_GIGABYTE; |
tass | 152:a3d286bf94e5 | 670 | } else { |
tass | 152:a3d286bf94e5 | 671 | space = (int32_t)(t->tcpq_in.max_size - t->tcpq_in.size); |
tass | 152:a3d286bf94e5 | 672 | } |
tass | 152:a3d286bf94e5 | 673 | |
tass | 152:a3d286bf94e5 | 674 | if (space < 0) |
tass | 152:a3d286bf94e5 | 675 | space = 0; |
tass | 152:a3d286bf94e5 | 676 | |
tass | 152:a3d286bf94e5 | 677 | while(space > 0xFFFF) { |
tass | 152:a3d286bf94e5 | 678 | space = (int32_t)(((uint32_t)space >> 1u)); |
tass | 152:a3d286bf94e5 | 679 | shift++; |
tass | 152:a3d286bf94e5 | 680 | } |
tass | 152:a3d286bf94e5 | 681 | tcp_set_space_check_winupdate(t, space, shift); |
tass | 152:a3d286bf94e5 | 682 | } |
tass | 152:a3d286bf94e5 | 683 | |
daniele | 29:1a47b7151851 | 684 | /* Return 32-bit aligned option size */ |
tass | 70:cd218dd180e5 | 685 | static uint16_t tcp_options_size(struct pico_socket_tcp *t, uint16_t flags) |
daniele | 29:1a47b7151851 | 686 | { |
TASS Belgium NV |
131:4758606c9316 | 687 | uint16_t size = 0; |
TASS Belgium NV |
131:4758606c9316 | 688 | struct tcp_sack_block *sb = t->sacks; |
TASS Belgium NV |
131:4758606c9316 | 689 | |
TASS Belgium NV |
131:4758606c9316 | 690 | if (flags & PICO_TCP_SYN) { /* Full options */ |
TASS Belgium NV |
131:4758606c9316 | 691 | size = PICO_TCPOPTLEN_MSS + PICO_TCP_OPTION_SACK_OK + PICO_TCPOPTLEN_WS + PICO_TCPOPTLEN_TIMESTAMP; |
TASS Belgium NV |
131:4758606c9316 | 692 | } else { |
TASS Belgium NV |
131:4758606c9316 | 693 | |
TASS Belgium NV |
131:4758606c9316 | 694 | /* Always update window scale. */ |
TASS Belgium NV |
131:4758606c9316 | 695 | size = (uint16_t)(size + PICO_TCPOPTLEN_WS); |
TASS Belgium NV |
131:4758606c9316 | 696 | |
TASS Belgium NV |
131:4758606c9316 | 697 | if (t->ts_ok) |
TASS Belgium NV |
131:4758606c9316 | 698 | size = (uint16_t)(size + PICO_TCPOPTLEN_TIMESTAMP); |
TASS Belgium NV |
131:4758606c9316 | 699 | |
TASS Belgium NV |
131:4758606c9316 | 700 | size = (uint16_t)(size + PICO_TCPOPTLEN_END); |
daniele | 29:1a47b7151851 | 701 | } |
TASS Belgium NV |
131:4758606c9316 | 702 | |
TASS Belgium NV |
131:4758606c9316 | 703 | if ((flags & PICO_TCP_ACK) && (t->sack_ok && sb)) { |
TASS Belgium NV |
131:4758606c9316 | 704 | size = (uint16_t)(size + 2); |
TASS Belgium NV |
131:4758606c9316 | 705 | while(sb) { |
TASS Belgium NV |
131:4758606c9316 | 706 | size = (uint16_t)(size + (2 * sizeof(uint32_t))); |
TASS Belgium NV |
131:4758606c9316 | 707 | sb = sb->next; |
TASS Belgium NV |
131:4758606c9316 | 708 | } |
TASS Belgium NV |
131:4758606c9316 | 709 | } |
TASS Belgium NV |
131:4758606c9316 | 710 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 711 | size = (uint16_t)(((size + 3u) >> 2u) << 2u); |
TASS Belgium NV |
131:4758606c9316 | 712 | return size; |
daniele | 29:1a47b7151851 | 713 | } |
daniele | 29:1a47b7151851 | 714 | |
tass | 70:cd218dd180e5 | 715 | uint16_t pico_tcp_overhead(struct pico_socket *s) |
daniele | 29:1a47b7151851 | 716 | { |
TASS Belgium NV |
131:4758606c9316 | 717 | if (!s) |
TASS Belgium NV |
131:4758606c9316 | 718 | return 0; |
TASS Belgium NV |
131:4758606c9316 | 719 | |
TASS Belgium NV |
131:4758606c9316 | 720 | return (uint16_t)(PICO_SIZE_TCPHDR + tcp_options_size((struct pico_socket_tcp *)s, (uint16_t)0)); /* hdr + Options size for data pkt */ |
daniele | 29:1a47b7151851 | 721 | |
daniele | 29:1a47b7151851 | 722 | } |
daniele | 29:1a47b7151851 | 723 | |
tass | 152:a3d286bf94e5 | 724 | static inline int tcp_sack_marker(struct pico_frame *f, uint32_t start, uint32_t end, uint16_t *count) |
tass | 152:a3d286bf94e5 | 725 | { |
tass | 152:a3d286bf94e5 | 726 | int cmp; |
tass | 152:a3d286bf94e5 | 727 | cmp = pico_seq_compare(SEQN(f), start); |
tass | 152:a3d286bf94e5 | 728 | if (cmp > 0) |
tass | 152:a3d286bf94e5 | 729 | return 0; |
tass | 152:a3d286bf94e5 | 730 | |
tass | 152:a3d286bf94e5 | 731 | if (cmp == 0) { |
tass | 152:a3d286bf94e5 | 732 | cmp = pico_seq_compare(SEQN(f) + f->payload_len, end); |
tass | 152:a3d286bf94e5 | 733 | if (cmp > 0) { |
tass | 152:a3d286bf94e5 | 734 | tcp_dbg("Invalid SACK: ignoring.\n"); |
tass | 152:a3d286bf94e5 | 735 | } |
tass | 152:a3d286bf94e5 | 736 | |
tass | 152:a3d286bf94e5 | 737 | tcp_dbg("Marking (by SACK) segment %08x BLK:[%08x::%08x]\n", SEQN(f), start, end); |
tass | 152:a3d286bf94e5 | 738 | f->flags |= PICO_FRAME_FLAG_SACKED; |
tass | 152:a3d286bf94e5 | 739 | (*count)++; |
tass | 152:a3d286bf94e5 | 740 | } |
tass | 152:a3d286bf94e5 | 741 | |
tass | 152:a3d286bf94e5 | 742 | return cmp; |
tass | 152:a3d286bf94e5 | 743 | } |
tass | 152:a3d286bf94e5 | 744 | |
daniele | 29:1a47b7151851 | 745 | static void tcp_process_sack(struct pico_socket_tcp *t, uint32_t start, uint32_t end) |
daniele | 29:1a47b7151851 | 746 | { |
TASS Belgium NV |
131:4758606c9316 | 747 | struct pico_frame *f; |
TASS Belgium NV |
131:4758606c9316 | 748 | struct pico_tree_node *index, *temp; |
TASS Belgium NV |
131:4758606c9316 | 749 | uint16_t count = 0; |
TASS Belgium NV |
131:4758606c9316 | 750 | |
TASS Belgium NV |
131:4758606c9316 | 751 | pico_tree_foreach_safe(index, &t->tcpq_out.pool, temp){ |
TASS Belgium NV |
131:4758606c9316 | 752 | f = index->keyValue; |
tass | 152:a3d286bf94e5 | 753 | if (tcp_sack_marker(f, start, end, &count) == 0) |
TASS Belgium NV |
131:4758606c9316 | 754 | goto done; |
daniele | 29:1a47b7151851 | 755 | } |
daniele | 29:1a47b7151851 | 756 | |
daniele | 29:1a47b7151851 | 757 | done: |
TASS Belgium NV |
131:4758606c9316 | 758 | if (t->x_mode > PICO_TCP_LOOKAHEAD) { |
TASS Belgium NV |
131:4758606c9316 | 759 | if (t->in_flight > (count)) |
TASS Belgium NV |
131:4758606c9316 | 760 | t->in_flight -= (count); |
TASS Belgium NV |
131:4758606c9316 | 761 | else |
TASS Belgium NV |
131:4758606c9316 | 762 | t->in_flight = 0; |
TASS Belgium NV |
131:4758606c9316 | 763 | } |
daniele | 29:1a47b7151851 | 764 | } |
daniele | 29:1a47b7151851 | 765 | |
TASS Belgium NV |
131:4758606c9316 | 766 | inline static void tcp_add_header(struct pico_socket_tcp *t, struct pico_frame *f) |
tass | 70:cd218dd180e5 | 767 | { |
TASS Belgium NV |
131:4758606c9316 | 768 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 769 | f->timestamp = TCP_TIME; |
TASS Belgium NV |
131:4758606c9316 | 770 | tcp_add_options(t, f, 0, (uint16_t)(f->transport_len - f->payload_len - (uint16_t)PICO_SIZE_TCPHDR)); |
TASS Belgium NV |
131:4758606c9316 | 771 | hdr->rwnd = short_be(t->wnd); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 772 | hdr->flags |= PICO_TCP_PSH | PICO_TCP_ACK; |
TASS Belgium NV |
131:4758606c9316 | 773 | hdr->ack = long_be(t->rcv_nxt); |
TASS Belgium NV |
131:4758606c9316 | 774 | hdr->crc = 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 775 | hdr->crc = short_be(pico_tcp_checksum(f)); |
tass | 70:cd218dd180e5 | 776 | } |
tass | 70:cd218dd180e5 | 777 | |
daniele | 29:1a47b7151851 | 778 | static void tcp_rcv_sack(struct pico_socket_tcp *t, uint8_t *opt, int len) |
daniele | 29:1a47b7151851 | 779 | { |
TASS Belgium NV |
131:4758606c9316 | 780 | uint32_t start, end; |
TASS Belgium NV |
131:4758606c9316 | 781 | int i = 0; |
TASS Belgium NV |
131:4758606c9316 | 782 | if (len % 8) { |
TASS Belgium NV |
131:4758606c9316 | 783 | tcp_dbg("SACK: Invalid len.\n"); |
TASS Belgium NV |
131:4758606c9316 | 784 | return; |
TASS Belgium NV |
131:4758606c9316 | 785 | } |
TASS Belgium NV |
131:4758606c9316 | 786 | |
TASS Belgium NV |
131:4758606c9316 | 787 | while (i < len) { |
TASS Belgium NV |
131:4758606c9316 | 788 | start = long_from(opt + i); |
TASS Belgium NV |
131:4758606c9316 | 789 | i += 4; |
TASS Belgium NV |
131:4758606c9316 | 790 | end = long_from(opt + i); |
TASS Belgium NV |
131:4758606c9316 | 791 | i += 4; |
TASS Belgium NV |
131:4758606c9316 | 792 | tcp_process_sack(t, long_be(start), long_be(end)); |
TASS Belgium NV |
131:4758606c9316 | 793 | } |
daniele | 29:1a47b7151851 | 794 | } |
daniele | 29:1a47b7151851 | 795 | |
tass | 152:a3d286bf94e5 | 796 | static int tcpopt_len_check(uint32_t *idx, uint8_t len, uint8_t expected) |
tass | 152:a3d286bf94e5 | 797 | { |
tass | 152:a3d286bf94e5 | 798 | if (len != expected) { |
tass | 152:a3d286bf94e5 | 799 | *idx = *idx + len - 2; |
tass | 152:a3d286bf94e5 | 800 | return -1; |
tass | 152:a3d286bf94e5 | 801 | } |
tass | 152:a3d286bf94e5 | 802 | |
tass | 152:a3d286bf94e5 | 803 | return 0; |
tass | 152:a3d286bf94e5 | 804 | } |
tass | 152:a3d286bf94e5 | 805 | |
tass | 152:a3d286bf94e5 | 806 | static inline void tcp_parse_option_ws(struct pico_socket_tcp *t, uint8_t len, uint8_t *opt, uint32_t *idx) |
tass | 152:a3d286bf94e5 | 807 | { |
tass | 152:a3d286bf94e5 | 808 | if (tcpopt_len_check(idx, len, PICO_TCPOPTLEN_WS) < 0) |
tass | 152:a3d286bf94e5 | 809 | return; |
tass | 152:a3d286bf94e5 | 810 | |
tass | 152:a3d286bf94e5 | 811 | t->recv_wnd_scale = opt[(*idx)++]; |
tass | 152:a3d286bf94e5 | 812 | tcp_dbg_options("TCP Window scale: received %d\n", t->recv_wnd_scale); |
tass | 152:a3d286bf94e5 | 813 | |
tass | 152:a3d286bf94e5 | 814 | } |
tass | 152:a3d286bf94e5 | 815 | |
tass | 152:a3d286bf94e5 | 816 | static inline void tcp_parse_option_sack_ok(struct pico_socket_tcp *t, struct pico_frame *f, uint8_t len, uint32_t *idx) |
tass | 152:a3d286bf94e5 | 817 | { |
tass | 152:a3d286bf94e5 | 818 | if (tcpopt_len_check(idx, len, PICO_TCPOPTLEN_SACK_OK) < 0) |
tass | 152:a3d286bf94e5 | 819 | return; |
tass | 152:a3d286bf94e5 | 820 | |
tass | 152:a3d286bf94e5 | 821 | if(((struct pico_tcp_hdr *)(f->transport_hdr))->flags & PICO_TCP_SYN ) |
tass | 152:a3d286bf94e5 | 822 | t->sack_ok = 1; |
tass | 152:a3d286bf94e5 | 823 | } |
tass | 152:a3d286bf94e5 | 824 | |
tass | 152:a3d286bf94e5 | 825 | static inline void tcp_parse_option_mss(struct pico_socket_tcp *t, uint8_t len, uint8_t *opt, uint32_t *idx) |
tass | 152:a3d286bf94e5 | 826 | { |
tass | 152:a3d286bf94e5 | 827 | uint16_t mss; |
tass | 152:a3d286bf94e5 | 828 | if (tcpopt_len_check(idx, len, PICO_TCPOPTLEN_MSS) < 0) |
tass | 152:a3d286bf94e5 | 829 | return; |
tass | 152:a3d286bf94e5 | 830 | |
tass | 152:a3d286bf94e5 | 831 | t->mss_ok = 1; |
tass | 152:a3d286bf94e5 | 832 | mss = short_from(opt + *idx); |
tass | 152:a3d286bf94e5 | 833 | *idx += (uint32_t)sizeof(uint16_t); |
tass | 152:a3d286bf94e5 | 834 | if (t->mss > short_be(mss)) |
tass | 152:a3d286bf94e5 | 835 | t->mss = short_be(mss); |
tass | 152:a3d286bf94e5 | 836 | } |
tass | 152:a3d286bf94e5 | 837 | |
tass | 152:a3d286bf94e5 | 838 | 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) |
tass | 152:a3d286bf94e5 | 839 | { |
tass | 152:a3d286bf94e5 | 840 | uint32_t tsval, tsecr; |
tass | 152:a3d286bf94e5 | 841 | if (tcpopt_len_check(idx, len, PICO_TCPOPTLEN_TIMESTAMP) < 0) |
tass | 152:a3d286bf94e5 | 842 | return; |
tass | 152:a3d286bf94e5 | 843 | |
tass | 152:a3d286bf94e5 | 844 | t->ts_ok = 1; |
tass | 152:a3d286bf94e5 | 845 | tsval = long_from(opt + *idx); |
tass | 152:a3d286bf94e5 | 846 | *idx += (uint32_t)sizeof(uint32_t); |
tass | 152:a3d286bf94e5 | 847 | tsecr = long_from(opt + *idx); |
tass | 152:a3d286bf94e5 | 848 | f->timestamp = long_be(tsecr); |
tass | 152:a3d286bf94e5 | 849 | *idx += (uint32_t)sizeof(uint32_t); |
tass | 152:a3d286bf94e5 | 850 | t->ts_nxt = long_be(tsval); |
tass | 152:a3d286bf94e5 | 851 | } |
tass | 152:a3d286bf94e5 | 852 | |
daniele | 29:1a47b7151851 | 853 | static void tcp_parse_options(struct pico_frame *f) |
daniele | 29:1a47b7151851 | 854 | { |
TASS Belgium NV |
131:4758606c9316 | 855 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)f->sock; |
TASS Belgium NV |
131:4758606c9316 | 856 | uint8_t *opt = f->transport_hdr + PICO_SIZE_TCPHDR; |
TASS Belgium NV |
131:4758606c9316 | 857 | uint32_t i = 0; |
TASS Belgium NV |
131:4758606c9316 | 858 | f->timestamp = 0; |
TASS Belgium NV |
131:4758606c9316 | 859 | while (i < (f->transport_len - PICO_SIZE_TCPHDR)) { |
TASS Belgium NV |
131:4758606c9316 | 860 | uint8_t type = opt[i++]; |
TASS Belgium NV |
131:4758606c9316 | 861 | uint8_t len; |
TASS Belgium NV |
131:4758606c9316 | 862 | if(i < (f->transport_len - PICO_SIZE_TCPHDR) && (type > 1)) |
TASS Belgium NV |
131:4758606c9316 | 863 | len = opt[i++]; |
TASS Belgium NV |
131:4758606c9316 | 864 | else |
TASS Belgium NV |
131:4758606c9316 | 865 | len = 1; |
TASS Belgium NV |
131:4758606c9316 | 866 | |
TASS Belgium NV |
131:4758606c9316 | 867 | if (f->payload && ((opt + i) > f->payload)) |
TASS Belgium NV |
131:4758606c9316 | 868 | break; |
TASS Belgium NV |
131:4758606c9316 | 869 | |
TASS Belgium NV |
131:4758606c9316 | 870 | tcp_dbg_options("Received option '%d', len = %d \n", type, len); |
TASS Belgium NV |
131:4758606c9316 | 871 | switch (type) { |
TASS Belgium NV |
131:4758606c9316 | 872 | case PICO_TCP_OPTION_NOOP: |
TASS Belgium NV |
131:4758606c9316 | 873 | case PICO_TCP_OPTION_END: |
TASS Belgium NV |
131:4758606c9316 | 874 | break; |
TASS Belgium NV |
131:4758606c9316 | 875 | case PICO_TCP_OPTION_WS: |
tass | 152:a3d286bf94e5 | 876 | tcp_parse_option_ws(t, len, opt, &i); |
TASS Belgium NV |
131:4758606c9316 | 877 | break; |
TASS Belgium NV |
131:4758606c9316 | 878 | case PICO_TCP_OPTION_SACK_OK: |
tass | 152:a3d286bf94e5 | 879 | tcp_parse_option_sack_ok(t, f, len, &i); |
tass | 152:a3d286bf94e5 | 880 | break; |
tass | 152:a3d286bf94e5 | 881 | case PICO_TCP_OPTION_MSS: |
tass | 152:a3d286bf94e5 | 882 | tcp_parse_option_mss(t, len, opt, &i); |
TASS Belgium NV |
131:4758606c9316 | 883 | break; |
tass | 152:a3d286bf94e5 | 884 | case PICO_TCP_OPTION_TIMESTAMP: |
tass | 152:a3d286bf94e5 | 885 | tcp_parse_option_timestamp(t, f, len, opt, &i); |
TASS Belgium NV |
131:4758606c9316 | 886 | break; |
tass | 152:a3d286bf94e5 | 887 | |
TASS Belgium NV |
131:4758606c9316 | 888 | case PICO_TCP_OPTION_SACK: |
TASS Belgium NV |
131:4758606c9316 | 889 | tcp_rcv_sack(t, opt + i, len - 2); |
TASS Belgium NV |
131:4758606c9316 | 890 | i = i + len - 2; |
TASS Belgium NV |
131:4758606c9316 | 891 | break; |
TASS Belgium NV |
131:4758606c9316 | 892 | default: |
TASS Belgium NV |
131:4758606c9316 | 893 | tcp_dbg_options("TCP: received unsupported option %u\n", type); |
TASS Belgium NV |
131:4758606c9316 | 894 | i = i + len - 2; |
TASS Belgium NV |
131:4758606c9316 | 895 | } |
daniele | 29:1a47b7151851 | 896 | } |
daniele | 29:1a47b7151851 | 897 | } |
daniele | 29:1a47b7151851 | 898 | |
tass | 152:a3d286bf94e5 | 899 | static inline void tcp_send_add_tcpflags(struct pico_socket_tcp *ts, struct pico_frame *f) |
daniele | 29:1a47b7151851 | 900 | { |
TASS Belgium NV |
131:4758606c9316 | 901 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 902 | if (ts->rcv_nxt != 0) { |
tass | 152:a3d286bf94e5 | 903 | if ((ts->rcv_ackd == 0) || (pico_seq_compare(ts->rcv_ackd, ts->rcv_nxt) != 0) || (hdr->flags & PICO_TCP_ACK)) { |
TASS Belgium NV |
131:4758606c9316 | 904 | hdr->flags |= PICO_TCP_ACK; |
TASS Belgium NV |
131:4758606c9316 | 905 | hdr->ack = long_be(ts->rcv_nxt); |
TASS Belgium NV |
131:4758606c9316 | 906 | ts->rcv_ackd = ts->rcv_nxt; |
TASS Belgium NV |
131:4758606c9316 | 907 | } |
TASS Belgium NV |
131:4758606c9316 | 908 | } |
TASS Belgium NV |
131:4758606c9316 | 909 | |
TASS Belgium NV |
131:4758606c9316 | 910 | if (hdr->flags & PICO_TCP_SYN) { |
TASS Belgium NV |
131:4758606c9316 | 911 | ts->snd_nxt++; |
TASS Belgium NV |
131:4758606c9316 | 912 | } |
TASS Belgium NV |
131:4758606c9316 | 913 | |
TASS Belgium NV |
131:4758606c9316 | 914 | if (f->payload_len > 0) { |
TASS Belgium NV |
131:4758606c9316 | 915 | hdr->flags |= PICO_TCP_PSH | PICO_TCP_ACK; |
TASS Belgium NV |
131:4758606c9316 | 916 | hdr->ack = long_be(ts->rcv_nxt); |
TASS Belgium NV |
131:4758606c9316 | 917 | ts->rcv_ackd = ts->rcv_nxt; |
daniele | 29:1a47b7151851 | 918 | } |
tass | 152:a3d286bf94e5 | 919 | } |
tass | 152:a3d286bf94e5 | 920 | |
tass | 152:a3d286bf94e5 | 921 | static inline int tcp_send_try_enqueue(struct pico_socket_tcp *ts, struct pico_frame *f) |
tass | 152:a3d286bf94e5 | 922 | { |
tass | 152:a3d286bf94e5 | 923 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
tass | 152:a3d286bf94e5 | 924 | struct pico_frame *cpy; |
tass | 152:a3d286bf94e5 | 925 | (void)hdr; |
TASS Belgium NV |
131:4758606c9316 | 926 | |
TASS Belgium NV |
131:4758606c9316 | 927 | /* TCP: ENQUEUE to PROTO ( Transmit ) */ |
TASS Belgium NV |
131:4758606c9316 | 928 | cpy = pico_frame_copy(f); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 929 | if (!cpy) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 930 | pico_err = PICO_ERR_ENOMEM; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 931 | return -1; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 932 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 933 | |
TASS Belgium NV |
131:4758606c9316 | 934 | if ((pico_enqueue(&tcp_out, cpy) > 0)) { |
TASS Belgium NV |
131:4758606c9316 | 935 | if (f->payload_len > 0) { |
TASS Belgium NV |
131:4758606c9316 | 936 | ts->in_flight++; |
TASS Belgium NV |
131:4758606c9316 | 937 | ts->snd_nxt += f->payload_len; /* update next pointer here to prevent sending same segment twice when called twice in same tick */ |
TASS Belgium NV |
131:4758606c9316 | 938 | } |
TASS Belgium NV |
131:4758606c9316 | 939 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 940 | tcp_dbg("DBG> [tcp output] state: %02x --> local port:%u remote port: %u seq: %08x ack: %08x flags: %02x = t_len: %u, hdr: %u payload: %d\n", |
TASS Belgium NV |
131:4758606c9316 | 941 | TCPSTATE(&ts->sock) >> 8, short_be(hdr->trans.sport), short_be(hdr->trans.dport), SEQN(f), ACKN(f), hdr->flags, f->transport_len, (hdr->len & 0xf0) >> 2, f->payload_len ); |
TASS Belgium NV |
131:4758606c9316 | 942 | } else { |
TASS Belgium NV |
131:4758606c9316 | 943 | pico_frame_discard(cpy); |
tass | 40:c8ab0d2bba0b | 944 | } |
TASS Belgium NV |
131:4758606c9316 | 945 | |
TASS Belgium NV |
131:4758606c9316 | 946 | return 0; |
tass | 152:a3d286bf94e5 | 947 | |
tass | 152:a3d286bf94e5 | 948 | } |
tass | 152:a3d286bf94e5 | 949 | |
tass | 152:a3d286bf94e5 | 950 | static int tcp_send(struct pico_socket_tcp *ts, struct pico_frame *f) |
tass | 152:a3d286bf94e5 | 951 | { |
tass | 152:a3d286bf94e5 | 952 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
tass | 152:a3d286bf94e5 | 953 | hdr->trans.sport = ts->sock.local_port; |
tass | 152:a3d286bf94e5 | 954 | hdr->trans.dport = ts->sock.remote_port; |
tass | 152:a3d286bf94e5 | 955 | if (!hdr->seq) |
tass | 152:a3d286bf94e5 | 956 | hdr->seq = long_be(ts->snd_nxt); |
tass | 152:a3d286bf94e5 | 957 | |
tass | 152:a3d286bf94e5 | 958 | tcp_send_add_tcpflags(ts, f); |
tass | 152:a3d286bf94e5 | 959 | |
tass | 152:a3d286bf94e5 | 960 | f->start = f->transport_hdr + PICO_SIZE_TCPHDR; |
tass | 152:a3d286bf94e5 | 961 | hdr->rwnd = short_be(ts->wnd); |
tass | 152:a3d286bf94e5 | 962 | hdr->crc = 0; |
tass | 152:a3d286bf94e5 | 963 | hdr->crc = short_be(pico_tcp_checksum(f)); |
tass | 152:a3d286bf94e5 | 964 | |
tass | 152:a3d286bf94e5 | 965 | return tcp_send_try_enqueue(ts, f); |
tass | 152:a3d286bf94e5 | 966 | |
daniele | 29:1a47b7151851 | 967 | } |
daniele | 29:1a47b7151851 | 968 | |
TASS Belgium NV |
131:4758606c9316 | 969 | /* #define PICO_TCP_SUPPORT_SOCKET_STATS */ |
daniele | 29:1a47b7151851 | 970 | |
daniele | 29:1a47b7151851 | 971 | #ifdef PICO_TCP_SUPPORT_SOCKET_STATS |
tass | 73:dfb737147f6e | 972 | static void sock_stats(uint32_t when, void *arg) |
daniele | 29:1a47b7151851 | 973 | { |
TASS Belgium NV |
131:4758606c9316 | 974 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 975 | tcp_dbg("STATISTIC> [%lu] socket state: %02x --> local port:%d remote port: %d queue size: %d snd_una: %08x snd_nxt: %08x cwnd: %d\n", |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 976 | when, t->sock.state, short_be(t->sock.local_port), short_be(t->sock.remote_port), t->tcpq_out.size, SEQN((struct pico_frame *)first_segment(&t->tcpq_out)), t->snd_nxt, t->cwnd); |
TASS Belgium NV |
131:4758606c9316 | 977 | pico_timer_add(2000, sock_stats, t); |
daniele | 29:1a47b7151851 | 978 | } |
daniele | 29:1a47b7151851 | 979 | #endif |
daniele | 29:1a47b7151851 | 980 | |
tass | 152:a3d286bf94e5 | 981 | static void tcp_send_probe(struct pico_socket_tcp *t); |
tass | 152:a3d286bf94e5 | 982 | |
tass | 152:a3d286bf94e5 | 983 | static void pico_tcp_keepalive(pico_time now, void *arg) |
tass | 152:a3d286bf94e5 | 984 | { |
tass | 152:a3d286bf94e5 | 985 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg; |
tass | 152:a3d286bf94e5 | 986 | if (((t->sock.state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED) && (t->ka_time > 0)) { |
tass | 152:a3d286bf94e5 | 987 | if (t->ka_time < (now - t->ack_timestamp)) { |
tass | 152:a3d286bf94e5 | 988 | if (t->ka_retries_count == 0) { |
tass | 152:a3d286bf94e5 | 989 | /* First probe */ |
tass | 152:a3d286bf94e5 | 990 | tcp_send_probe(t); |
tass | 152:a3d286bf94e5 | 991 | t->ka_retries_count++; |
tass | 152:a3d286bf94e5 | 992 | } |
tass | 152:a3d286bf94e5 | 993 | if (t->ka_retries_count > t->ka_probes) { |
tass | 152:a3d286bf94e5 | 994 | if (t->sock.wakeup) |
tass | 152:a3d286bf94e5 | 995 | { |
tass | 152:a3d286bf94e5 | 996 | pico_err = PICO_ERR_ECONNRESET; |
tass | 152:a3d286bf94e5 | 997 | t->sock.wakeup(PICO_SOCK_EV_ERR, &t->sock); |
tass | 152:a3d286bf94e5 | 998 | } |
tass | 152:a3d286bf94e5 | 999 | } |
tass | 152:a3d286bf94e5 | 1000 | if (((t->ka_retries_count * t->ka_intvl) + t->ka_time) < (now - t->ack_timestamp)) { |
tass | 152:a3d286bf94e5 | 1001 | /* Next probe */ |
tass | 152:a3d286bf94e5 | 1002 | tcp_send_probe(t); |
tass | 152:a3d286bf94e5 | 1003 | t->ka_retries_count++; |
tass | 152:a3d286bf94e5 | 1004 | } |
tass | 152:a3d286bf94e5 | 1005 | } else { |
tass | 152:a3d286bf94e5 | 1006 | t->ka_retries_count = 0; |
tass | 152:a3d286bf94e5 | 1007 | } |
tass | 152:a3d286bf94e5 | 1008 | } |
tass | 152:a3d286bf94e5 | 1009 | t->keepalive_tmr = pico_timer_add(1000, pico_tcp_keepalive, t); |
tass | 152:a3d286bf94e5 | 1010 | } |
tass | 152:a3d286bf94e5 | 1011 | |
tass | 152:a3d286bf94e5 | 1012 | static inline void rto_set(struct pico_socket_tcp *t, uint32_t rto) |
tass | 152:a3d286bf94e5 | 1013 | { |
tass | 152:a3d286bf94e5 | 1014 | if (rto < PICO_TCP_RTO_MIN) |
tass | 152:a3d286bf94e5 | 1015 | rto = PICO_TCP_RTO_MIN; |
tass | 152:a3d286bf94e5 | 1016 | |
tass | 152:a3d286bf94e5 | 1017 | if (rto > PICO_TCP_RTO_MAX) |
tass | 152:a3d286bf94e5 | 1018 | rto = PICO_TCP_RTO_MAX; |
tass | 152:a3d286bf94e5 | 1019 | |
tass | 152:a3d286bf94e5 | 1020 | t->rto = rto; |
tass | 152:a3d286bf94e5 | 1021 | } |
tass | 152:a3d286bf94e5 | 1022 | |
tass | 152:a3d286bf94e5 | 1023 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1024 | struct pico_socket *pico_tcp_open(uint16_t family) |
daniele | 29:1a47b7151851 | 1025 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1026 | struct pico_socket_tcp *t = PICO_ZALLOC(sizeof(struct pico_socket_tcp)); |
TASS Belgium NV |
131:4758606c9316 | 1027 | if (!t) |
TASS Belgium NV |
131:4758606c9316 | 1028 | return NULL; |
TASS Belgium NV |
131:4758606c9316 | 1029 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1030 | t->sock.timestamp = TCP_TIME; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1031 | pico_socket_set_family(&t->sock, family); |
tass | 152:a3d286bf94e5 | 1032 | t->mss = (uint16_t)(pico_socket_get_mss(&t->sock) - PICO_SIZE_TCPHDR); |
TASS Belgium NV |
131:4758606c9316 | 1033 | t->tcpq_in.pool.root = t->tcpq_hold.pool.root = t->tcpq_out.pool.root = &LEAF; |
TASS Belgium NV |
131:4758606c9316 | 1034 | t->tcpq_hold.pool.compare = t->tcpq_out.pool.compare = segment_compare; |
TASS Belgium NV |
131:4758606c9316 | 1035 | t->tcpq_in.pool.compare = input_segment_compare; |
TASS Belgium NV |
131:4758606c9316 | 1036 | t->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ; |
TASS Belgium NV |
131:4758606c9316 | 1037 | t->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1038 | t->tcpq_hold.max_size = 2u * t->mss; |
tass | 152:a3d286bf94e5 | 1039 | rto_set(t, PICO_TCP_RTO_MIN); |
tass | 152:a3d286bf94e5 | 1040 | |
tass | 152:a3d286bf94e5 | 1041 | /* Uncomment next line and disable Nagle by default */ |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1042 | t->sock.opt_flags |= (1 << PICO_SOCKET_OPT_TCPNODELAY); |
tass | 152:a3d286bf94e5 | 1043 | |
tass | 152:a3d286bf94e5 | 1044 | /* Uncomment next line and Nagle is enabled by default */ |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1045 | /* t->sock.opt_flags &= (uint16_t) ~(1 << PICO_SOCKET_OPT_TCPNODELAY); */ |
daniele | 29:1a47b7151851 | 1046 | |
tass | 152:a3d286bf94e5 | 1047 | /* Set default linger for the socket */ |
tass | 152:a3d286bf94e5 | 1048 | t->linger_timeout = PICO_SOCKET_LINGER_TIMEOUT; |
tass | 152:a3d286bf94e5 | 1049 | |
tass | 152:a3d286bf94e5 | 1050 | |
daniele | 29:1a47b7151851 | 1051 | #ifdef PICO_TCP_SUPPORT_SOCKET_STATS |
TASS Belgium NV |
131:4758606c9316 | 1052 | pico_timer_add(2000, sock_stats, t); |
daniele | 29:1a47b7151851 | 1053 | #endif |
tass | 152:a3d286bf94e5 | 1054 | |
tass | 152:a3d286bf94e5 | 1055 | t->keepalive_tmr = pico_timer_add(1000, pico_tcp_keepalive, t); |
TASS Belgium NV |
131:4758606c9316 | 1056 | tcp_set_space(t); |
TASS Belgium NV |
131:4758606c9316 | 1057 | |
TASS Belgium NV |
131:4758606c9316 | 1058 | return &t->sock; |
daniele | 29:1a47b7151851 | 1059 | } |
daniele | 29:1a47b7151851 | 1060 | |
tass | 152:a3d286bf94e5 | 1061 | static uint32_t tcp_read_finish(struct pico_socket *s, uint32_t tot_rd_len) |
daniele | 29:1a47b7151851 | 1062 | { |
TASS Belgium NV |
131:4758606c9316 | 1063 | struct pico_socket_tcp *t = TCP_SOCK(s); |
TASS Belgium NV |
131:4758606c9316 | 1064 | tcp_set_space(t); |
TASS Belgium NV |
131:4758606c9316 | 1065 | if (t->tcpq_in.size == 0) { |
TASS Belgium NV |
131:4758606c9316 | 1066 | s->ev_pending &= (uint16_t)(~PICO_SOCK_EV_RD); |
daniele | 29:1a47b7151851 | 1067 | } |
daniele | 29:1a47b7151851 | 1068 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1069 | if (t->remote_closed) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1070 | s->ev_pending |= (uint16_t)(PICO_SOCK_EV_CLOSE); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1071 | s->state &= 0x00FFU; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1072 | s->state |= PICO_SOCKET_STATE_TCP_CLOSE_WAIT; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1073 | /* set SHUT_REMOTE */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1074 | s->state |= PICO_SOCKET_STATE_SHUT_REMOTE; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1075 | if (s->wakeup) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1076 | s->wakeup(PICO_SOCK_EV_CLOSE, s); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1077 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1078 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1079 | |
TASS Belgium NV |
131:4758606c9316 | 1080 | return tot_rd_len; |
daniele | 29:1a47b7151851 | 1081 | } |
daniele | 29:1a47b7151851 | 1082 | |
tass | 152:a3d286bf94e5 | 1083 | 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) |
tass | 152:a3d286bf94e5 | 1084 | { |
tass | 152:a3d286bf94e5 | 1085 | uint32_t in_frame_len = 0; |
tass | 152:a3d286bf94e5 | 1086 | if (in_frame_off > 0) |
tass | 152:a3d286bf94e5 | 1087 | { |
tass | 152:a3d286bf94e5 | 1088 | if ((uint32_t)in_frame_off > f->payload_len) { |
tass | 152:a3d286bf94e5 | 1089 | tcp_dbg("FATAL TCP ERR: in_frame_off > f->payload_len\n"); |
tass | 152:a3d286bf94e5 | 1090 | } |
tass | 152:a3d286bf94e5 | 1091 | |
tass | 152:a3d286bf94e5 | 1092 | in_frame_len = f->payload_len - (uint32_t)in_frame_off; |
tass | 152:a3d286bf94e5 | 1093 | } else { /* in_frame_off == 0 */ |
tass | 152:a3d286bf94e5 | 1094 | in_frame_len = f->payload_len; |
tass | 152:a3d286bf94e5 | 1095 | } |
tass | 152:a3d286bf94e5 | 1096 | |
tass | 152:a3d286bf94e5 | 1097 | if ((in_frame_len + tot_rd_len) > (uint32_t)read_op_len) { |
tass | 152:a3d286bf94e5 | 1098 | in_frame_len = read_op_len - tot_rd_len; |
tass | 152:a3d286bf94e5 | 1099 | } |
tass | 152:a3d286bf94e5 | 1100 | |
tass | 152:a3d286bf94e5 | 1101 | return in_frame_len; |
tass | 152:a3d286bf94e5 | 1102 | |
tass | 152:a3d286bf94e5 | 1103 | } |
tass | 152:a3d286bf94e5 | 1104 | |
tass | 152:a3d286bf94e5 | 1105 | static inline void tcp_read_check_segment_done(struct pico_socket_tcp *t, struct tcp_input_segment *f, uint32_t in_frame_len) |
tass | 152:a3d286bf94e5 | 1106 | { |
tass | 152:a3d286bf94e5 | 1107 | if ((in_frame_len == 0u) || (in_frame_len == (uint32_t)f->payload_len)) { |
tass | 152:a3d286bf94e5 | 1108 | pico_discard_segment(&t->tcpq_in, f); |
tass | 152:a3d286bf94e5 | 1109 | } |
tass | 152:a3d286bf94e5 | 1110 | } |
tass | 152:a3d286bf94e5 | 1111 | |
tass | 152:a3d286bf94e5 | 1112 | uint32_t pico_tcp_read(struct pico_socket *s, void *buf, uint32_t len) |
tass | 152:a3d286bf94e5 | 1113 | { |
tass | 152:a3d286bf94e5 | 1114 | struct pico_socket_tcp *t = TCP_SOCK(s); |
tass | 152:a3d286bf94e5 | 1115 | struct tcp_input_segment *f; |
tass | 152:a3d286bf94e5 | 1116 | int32_t in_frame_off; |
tass | 152:a3d286bf94e5 | 1117 | uint32_t in_frame_len; |
tass | 152:a3d286bf94e5 | 1118 | uint32_t tot_rd_len = 0; |
tass | 152:a3d286bf94e5 | 1119 | |
tass | 152:a3d286bf94e5 | 1120 | while (tot_rd_len < len) { |
tass | 152:a3d286bf94e5 | 1121 | /* To be sure we don't have garbage at the beginning */ |
tass | 152:a3d286bf94e5 | 1122 | release_until(&t->tcpq_in, t->rcv_processed); |
tass | 152:a3d286bf94e5 | 1123 | f = first_segment(&t->tcpq_in); |
tass | 152:a3d286bf94e5 | 1124 | if (!f) |
tass | 152:a3d286bf94e5 | 1125 | return tcp_read_finish(s, tot_rd_len); |
tass | 152:a3d286bf94e5 | 1126 | |
tass | 152:a3d286bf94e5 | 1127 | in_frame_off = pico_seq_compare(t->rcv_processed, f->seq); |
tass | 152:a3d286bf94e5 | 1128 | /* Check for hole at the beginning of data, awaiting retransmissions. */ |
tass | 152:a3d286bf94e5 | 1129 | if (in_frame_off < 0) { |
tass | 152:a3d286bf94e5 | 1130 | tcp_dbg("TCP> read hole beginning of data, %08x - %08x. rcv_nxt is %08x\n", t->rcv_processed, f->seq, t->rcv_nxt); |
tass | 152:a3d286bf94e5 | 1131 | return tcp_read_finish(s, tot_rd_len); |
tass | 152:a3d286bf94e5 | 1132 | } |
tass | 152:a3d286bf94e5 | 1133 | |
tass | 152:a3d286bf94e5 | 1134 | in_frame_len = tcp_read_in_frame_len(f, in_frame_off, tot_rd_len, len); |
tass | 152:a3d286bf94e5 | 1135 | |
tass | 152:a3d286bf94e5 | 1136 | |
tass | 152:a3d286bf94e5 | 1137 | memcpy((uint8_t *)buf + tot_rd_len, f->payload + in_frame_off, in_frame_len); |
tass | 152:a3d286bf94e5 | 1138 | tot_rd_len += in_frame_len; |
tass | 152:a3d286bf94e5 | 1139 | t->rcv_processed += in_frame_len; |
tass | 152:a3d286bf94e5 | 1140 | |
tass | 152:a3d286bf94e5 | 1141 | tcp_read_check_segment_done(t, f, in_frame_len); |
tass | 152:a3d286bf94e5 | 1142 | |
tass | 152:a3d286bf94e5 | 1143 | } |
tass | 152:a3d286bf94e5 | 1144 | return tcp_read_finish(s, tot_rd_len); |
tass | 152:a3d286bf94e5 | 1145 | } |
tass | 152:a3d286bf94e5 | 1146 | |
daniele | 29:1a47b7151851 | 1147 | int pico_tcp_initconn(struct pico_socket *s); |
tass | 128:ae39e6e81531 | 1148 | static void initconn_retry(pico_time when, void *arg) |
daniele | 29:1a47b7151851 | 1149 | { |
TASS Belgium NV |
131:4758606c9316 | 1150 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg; |
TASS Belgium NV |
131:4758606c9316 | 1151 | IGNORE_PARAMETER(when); |
tass | 152:a3d286bf94e5 | 1152 | if (TCPSTATE(&t->sock) != PICO_SOCKET_STATE_TCP_ESTABLISHED) |
tass | 152:a3d286bf94e5 | 1153 | { |
TASS Belgium NV |
131:4758606c9316 | 1154 | if (t->backoff > PICO_TCP_MAX_CONNECT_RETRIES) { |
TASS Belgium NV |
131:4758606c9316 | 1155 | tcp_dbg("TCP> Connection timeout. \n"); |
TASS Belgium NV |
131:4758606c9316 | 1156 | if (t->sock.wakeup) |
tass | 152:a3d286bf94e5 | 1157 | { |
tass | 152:a3d286bf94e5 | 1158 | pico_err = PICO_ERR_ECONNREFUSED; |
TASS Belgium NV |
131:4758606c9316 | 1159 | t->sock.wakeup(PICO_SOCK_EV_ERR, &t->sock); |
tass | 152:a3d286bf94e5 | 1160 | } |
tass | 152:a3d286bf94e5 | 1161 | pico_socket_del(&t->sock); |
TASS Belgium NV |
131:4758606c9316 | 1162 | return; |
TASS Belgium NV |
131:4758606c9316 | 1163 | } |
TASS Belgium NV |
131:4758606c9316 | 1164 | |
TASS Belgium NV |
131:4758606c9316 | 1165 | tcp_dbg("TCP> SYN retry %d...\n", t->backoff); |
TASS Belgium NV |
131:4758606c9316 | 1166 | t->backoff++; |
TASS Belgium NV |
131:4758606c9316 | 1167 | pico_tcp_initconn(&t->sock); |
TASS Belgium NV |
131:4758606c9316 | 1168 | } else { |
TASS Belgium NV |
131:4758606c9316 | 1169 | tcp_dbg("TCP> Connection is already established: no retry needed. good.\n"); |
daniele | 29:1a47b7151851 | 1170 | } |
daniele | 29:1a47b7151851 | 1171 | } |
daniele | 29:1a47b7151851 | 1172 | |
daniele | 29:1a47b7151851 | 1173 | int pico_tcp_initconn(struct pico_socket *s) |
daniele | 29:1a47b7151851 | 1174 | { |
TASS Belgium NV |
131:4758606c9316 | 1175 | struct pico_socket_tcp *ts = TCP_SOCK(s); |
TASS Belgium NV |
131:4758606c9316 | 1176 | struct pico_frame *syn; |
TASS Belgium NV |
131:4758606c9316 | 1177 | struct pico_tcp_hdr *hdr; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1178 | uint16_t mtu, opt_len = tcp_options_size(ts, PICO_TCP_SYN); |
TASS Belgium NV |
131:4758606c9316 | 1179 | |
TASS Belgium NV |
131:4758606c9316 | 1180 | syn = s->net->alloc(s->net, (uint16_t)(PICO_SIZE_TCPHDR + opt_len)); |
TASS Belgium NV |
131:4758606c9316 | 1181 | if (!syn) |
TASS Belgium NV |
131:4758606c9316 | 1182 | return -1; |
TASS Belgium NV |
131:4758606c9316 | 1183 | |
TASS Belgium NV |
131:4758606c9316 | 1184 | hdr = (struct pico_tcp_hdr *) syn->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 1185 | |
TASS Belgium NV |
131:4758606c9316 | 1186 | if (!ts->snd_nxt) |
TASS Belgium NV |
131:4758606c9316 | 1187 | ts->snd_nxt = long_be(pico_paws()); |
TASS Belgium NV |
131:4758606c9316 | 1188 | |
TASS Belgium NV |
131:4758606c9316 | 1189 | ts->snd_last = ts->snd_nxt; |
TASS Belgium NV |
131:4758606c9316 | 1190 | ts->cwnd = PICO_TCP_IW; |
tass | 152:a3d286bf94e5 | 1191 | mtu = (uint16_t)pico_socket_get_mss(s); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1192 | ts->mss = (uint16_t)(mtu - PICO_SIZE_TCPHDR); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1193 | ts->ssthresh = (uint16_t)((uint16_t)(PICO_DEFAULT_SOCKETQ / ts->mss) - (((uint16_t)(PICO_DEFAULT_SOCKETQ / ts->mss)) >> 3u)); |
TASS Belgium NV |
131:4758606c9316 | 1194 | syn->sock = s; |
TASS Belgium NV |
131:4758606c9316 | 1195 | hdr->seq = long_be(ts->snd_nxt); |
TASS Belgium NV |
131:4758606c9316 | 1196 | hdr->len = (uint8_t)((PICO_SIZE_TCPHDR + opt_len) << 2 | ts->jumbo); |
TASS Belgium NV |
131:4758606c9316 | 1197 | hdr->flags = PICO_TCP_SYN; |
TASS Belgium NV |
131:4758606c9316 | 1198 | tcp_set_space(ts); |
TASS Belgium NV |
131:4758606c9316 | 1199 | hdr->rwnd = short_be(ts->wnd); |
TASS Belgium NV |
131:4758606c9316 | 1200 | tcp_add_options(ts, syn, PICO_TCP_SYN, opt_len); |
TASS Belgium NV |
131:4758606c9316 | 1201 | hdr->trans.sport = ts->sock.local_port; |
TASS Belgium NV |
131:4758606c9316 | 1202 | hdr->trans.dport = ts->sock.remote_port; |
TASS Belgium NV |
131:4758606c9316 | 1203 | |
TASS Belgium NV |
131:4758606c9316 | 1204 | hdr->crc = 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1205 | hdr->crc = short_be(pico_tcp_checksum(syn)); |
TASS Belgium NV |
131:4758606c9316 | 1206 | |
TASS Belgium NV |
131:4758606c9316 | 1207 | /* TCP: ENQUEUE to PROTO ( SYN ) */ |
TASS Belgium NV |
131:4758606c9316 | 1208 | tcp_dbg("Sending SYN... (ports: %d - %d) size: %d\n", short_be(ts->sock.local_port), short_be(ts->sock.remote_port), syn->buffer_len); |
TASS Belgium NV |
131:4758606c9316 | 1209 | pico_enqueue(&tcp_out, syn); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1210 | ts->retrans_tmr = pico_timer_add(PICO_TCP_SYN_TO << ts->backoff, initconn_retry, ts); |
TASS Belgium NV |
131:4758606c9316 | 1211 | return 0; |
daniele | 29:1a47b7151851 | 1212 | } |
daniele | 29:1a47b7151851 | 1213 | |
daniele | 29:1a47b7151851 | 1214 | static int tcp_send_synack(struct pico_socket *s) |
daniele | 29:1a47b7151851 | 1215 | { |
TASS Belgium NV |
131:4758606c9316 | 1216 | struct pico_socket_tcp *ts = TCP_SOCK(s); |
TASS Belgium NV |
131:4758606c9316 | 1217 | struct pico_frame *synack; |
TASS Belgium NV |
131:4758606c9316 | 1218 | struct pico_tcp_hdr *hdr; |
TASS Belgium NV |
131:4758606c9316 | 1219 | uint16_t opt_len = tcp_options_size(ts, PICO_TCP_SYN | PICO_TCP_ACK); |
TASS Belgium NV |
131:4758606c9316 | 1220 | |
TASS Belgium NV |
131:4758606c9316 | 1221 | synack = s->net->alloc(s->net, (uint16_t)(PICO_SIZE_TCPHDR + opt_len)); |
TASS Belgium NV |
131:4758606c9316 | 1222 | if (!synack) |
TASS Belgium NV |
131:4758606c9316 | 1223 | return -1; |
TASS Belgium NV |
131:4758606c9316 | 1224 | |
TASS Belgium NV |
131:4758606c9316 | 1225 | hdr = (struct pico_tcp_hdr *) synack->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 1226 | |
TASS Belgium NV |
131:4758606c9316 | 1227 | synack->sock = s; |
TASS Belgium NV |
131:4758606c9316 | 1228 | hdr->len = (uint8_t)((PICO_SIZE_TCPHDR + opt_len) << 2 | ts->jumbo); |
TASS Belgium NV |
131:4758606c9316 | 1229 | hdr->flags = PICO_TCP_SYN | PICO_TCP_ACK; |
TASS Belgium NV |
131:4758606c9316 | 1230 | hdr->rwnd = short_be(ts->wnd); |
TASS Belgium NV |
131:4758606c9316 | 1231 | hdr->seq = long_be(ts->snd_nxt); |
TASS Belgium NV |
131:4758606c9316 | 1232 | ts->rcv_processed = long_be(hdr->seq); |
TASS Belgium NV |
131:4758606c9316 | 1233 | ts->snd_last = ts->snd_nxt; |
TASS Belgium NV |
131:4758606c9316 | 1234 | tcp_set_space(ts); |
TASS Belgium NV |
131:4758606c9316 | 1235 | tcp_add_options(ts, synack, hdr->flags, opt_len); |
TASS Belgium NV |
131:4758606c9316 | 1236 | synack->payload_len = 0; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1237 | synack->timestamp = TCP_TIME; |
TASS Belgium NV |
131:4758606c9316 | 1238 | tcp_send(ts, synack); |
TASS Belgium NV |
131:4758606c9316 | 1239 | pico_frame_discard(synack); |
TASS Belgium NV |
131:4758606c9316 | 1240 | return 0; |
daniele | 29:1a47b7151851 | 1241 | } |
daniele | 29:1a47b7151851 | 1242 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1243 | static void tcp_send_empty(struct pico_socket_tcp *t, uint16_t flags, int is_keepalive) |
daniele | 29:1a47b7151851 | 1244 | { |
TASS Belgium NV |
131:4758606c9316 | 1245 | struct pico_frame *f; |
TASS Belgium NV |
131:4758606c9316 | 1246 | struct pico_tcp_hdr *hdr; |
TASS Belgium NV |
131:4758606c9316 | 1247 | uint16_t opt_len = tcp_options_size(t, flags); |
TASS Belgium NV |
131:4758606c9316 | 1248 | f = t->sock.net->alloc(t->sock.net, (uint16_t)(PICO_SIZE_TCPHDR + opt_len)); |
TASS Belgium NV |
131:4758606c9316 | 1249 | if (!f) { |
TASS Belgium NV |
131:4758606c9316 | 1250 | return; |
TASS Belgium NV |
131:4758606c9316 | 1251 | } |
TASS Belgium NV |
131:4758606c9316 | 1252 | |
TASS Belgium NV |
131:4758606c9316 | 1253 | f->sock = &t->sock; |
TASS Belgium NV |
131:4758606c9316 | 1254 | hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 1255 | hdr->len = (uint8_t)((PICO_SIZE_TCPHDR + opt_len) << 2 | t->jumbo); |
TASS Belgium NV |
131:4758606c9316 | 1256 | hdr->flags = (uint8_t)flags; |
TASS Belgium NV |
131:4758606c9316 | 1257 | hdr->rwnd = short_be(t->wnd); |
TASS Belgium NV |
131:4758606c9316 | 1258 | tcp_set_space(t); |
TASS Belgium NV |
131:4758606c9316 | 1259 | tcp_add_options(t, f, flags, opt_len); |
TASS Belgium NV |
131:4758606c9316 | 1260 | hdr->trans.sport = t->sock.local_port; |
TASS Belgium NV |
131:4758606c9316 | 1261 | hdr->trans.dport = t->sock.remote_port; |
TASS Belgium NV |
131:4758606c9316 | 1262 | hdr->seq = long_be(t->snd_nxt); |
tass | 152:a3d286bf94e5 | 1263 | if ((flags & PICO_TCP_ACK) != 0) { |
TASS Belgium NV |
131:4758606c9316 | 1264 | hdr->ack = long_be(t->rcv_nxt); |
tass | 152:a3d286bf94e5 | 1265 | } |
TASS Belgium NV |
131:4758606c9316 | 1266 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1267 | if (is_keepalive) |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1268 | hdr->seq = long_be(t->snd_nxt - 1); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1269 | |
TASS Belgium NV |
131:4758606c9316 | 1270 | t->rcv_ackd = t->rcv_nxt; |
TASS Belgium NV |
131:4758606c9316 | 1271 | |
TASS Belgium NV |
131:4758606c9316 | 1272 | f->start = f->transport_hdr + PICO_SIZE_TCPHDR; |
TASS Belgium NV |
131:4758606c9316 | 1273 | hdr->rwnd = short_be(t->wnd); |
TASS Belgium NV |
131:4758606c9316 | 1274 | hdr->crc = 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1275 | hdr->crc = short_be(pico_tcp_checksum(f)); |
TASS Belgium NV |
131:4758606c9316 | 1276 | |
TASS Belgium NV |
131:4758606c9316 | 1277 | /* TCP: ENQUEUE to PROTO */ |
TASS Belgium NV |
131:4758606c9316 | 1278 | pico_enqueue(&tcp_out, f); |
daniele | 29:1a47b7151851 | 1279 | } |
daniele | 29:1a47b7151851 | 1280 | |
daniele | 29:1a47b7151851 | 1281 | static void tcp_send_ack(struct pico_socket_tcp *t) |
daniele | 29:1a47b7151851 | 1282 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1283 | tcp_send_empty(t, PICO_TCP_ACK, 0); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1284 | } |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1285 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1286 | static void tcp_send_probe(struct pico_socket_tcp *t) |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1287 | { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1288 | /* tcp_dbg("Sending probe\n"); */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1289 | tcp_send_empty(t, PICO_TCP_PSHACK, 1); |
daniele | 29:1a47b7151851 | 1290 | } |
daniele | 29:1a47b7151851 | 1291 | |
tass | 152:a3d286bf94e5 | 1292 | static int tcp_do_send_rst(struct pico_socket *s, uint32_t seq) |
daniele | 29:1a47b7151851 | 1293 | { |
TASS Belgium NV |
131:4758606c9316 | 1294 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
tass | 152:a3d286bf94e5 | 1295 | uint16_t opt_len = tcp_options_size(t, PICO_TCP_RST); |
TASS Belgium NV |
131:4758606c9316 | 1296 | struct pico_frame *f; |
tass | 152:a3d286bf94e5 | 1297 | struct pico_tcp_hdr *hdr; |
TASS Belgium NV |
131:4758606c9316 | 1298 | f = t->sock.net->alloc(t->sock.net, (uint16_t)(PICO_SIZE_TCPHDR + opt_len)); |
TASS Belgium NV |
131:4758606c9316 | 1299 | if (!f) { |
TASS Belgium NV |
131:4758606c9316 | 1300 | return -1; |
TASS Belgium NV |
131:4758606c9316 | 1301 | } |
TASS Belgium NV |
131:4758606c9316 | 1302 | f->sock = &t->sock; |
tass | 152:a3d286bf94e5 | 1303 | tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> START\n"); |
tass | 152:a3d286bf94e5 | 1304 | |
TASS Belgium NV |
131:4758606c9316 | 1305 | hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 1306 | hdr->len = (uint8_t)((PICO_SIZE_TCPHDR + opt_len) << 2 | t->jumbo); |
TASS Belgium NV |
131:4758606c9316 | 1307 | hdr->flags = PICO_TCP_RST; |
TASS Belgium NV |
131:4758606c9316 | 1308 | hdr->rwnd = short_be(t->wnd); |
TASS Belgium NV |
131:4758606c9316 | 1309 | tcp_set_space(t); |
TASS Belgium NV |
131:4758606c9316 | 1310 | tcp_add_options(t, f, PICO_TCP_RST, opt_len); |
TASS Belgium NV |
131:4758606c9316 | 1311 | hdr->trans.sport = t->sock.local_port; |
TASS Belgium NV |
131:4758606c9316 | 1312 | hdr->trans.dport = t->sock.remote_port; |
tass | 152:a3d286bf94e5 | 1313 | hdr->seq = seq; |
TASS Belgium NV |
131:4758606c9316 | 1314 | hdr->ack = long_be(t->rcv_nxt); |
TASS Belgium NV |
131:4758606c9316 | 1315 | t->rcv_ackd = t->rcv_nxt; |
TASS Belgium NV |
131:4758606c9316 | 1316 | f->start = f->transport_hdr + PICO_SIZE_TCPHDR; |
TASS Belgium NV |
131:4758606c9316 | 1317 | hdr->rwnd = short_be(t->wnd); |
TASS Belgium NV |
131:4758606c9316 | 1318 | hdr->crc = 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1319 | hdr->crc = short_be(pico_tcp_checksum(f)); |
TASS Belgium NV |
131:4758606c9316 | 1320 | |
TASS Belgium NV |
131:4758606c9316 | 1321 | /* TCP: ENQUEUE to PROTO */ |
TASS Belgium NV |
131:4758606c9316 | 1322 | pico_enqueue(&tcp_out, f); |
tass | 152:a3d286bf94e5 | 1323 | tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> DONE\n"); |
tass | 152:a3d286bf94e5 | 1324 | return 0; |
tass | 152:a3d286bf94e5 | 1325 | } |
tass | 152:a3d286bf94e5 | 1326 | |
tass | 152:a3d286bf94e5 | 1327 | static int tcp_send_rst(struct pico_socket *s, struct pico_frame *fr) |
tass | 152:a3d286bf94e5 | 1328 | { |
tass | 152:a3d286bf94e5 | 1329 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
tass | 152:a3d286bf94e5 | 1330 | struct pico_tcp_hdr *hdr_rcv; |
tass | 152:a3d286bf94e5 | 1331 | int ret; |
tass | 152:a3d286bf94e5 | 1332 | |
tass | 152:a3d286bf94e5 | 1333 | if (fr && ((s->state & PICO_SOCKET_STATE_TCP) > PICO_SOCKET_STATE_TCP_SYN_RECV)) { |
tass | 152:a3d286bf94e5 | 1334 | /* in synchronized state: send RST with seq = ack from previous segment */ |
tass | 152:a3d286bf94e5 | 1335 | hdr_rcv = (struct pico_tcp_hdr *) fr->transport_hdr; |
tass | 152:a3d286bf94e5 | 1336 | ret = tcp_do_send_rst(s, hdr_rcv->ack); |
tass | 152:a3d286bf94e5 | 1337 | } else { |
tass | 152:a3d286bf94e5 | 1338 | /* non-synchronized state */ |
tass | 152:a3d286bf94e5 | 1339 | /* go to CLOSED here to prevent timer callback to go on after timeout */ |
tass | 152:a3d286bf94e5 | 1340 | (t->sock).state &= 0x00FFU; |
tass | 152:a3d286bf94e5 | 1341 | (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED; |
tass | 152:a3d286bf94e5 | 1342 | ret = tcp_do_send_rst(s, long_be(t->snd_nxt)); |
tass | 152:a3d286bf94e5 | 1343 | |
tass | 152:a3d286bf94e5 | 1344 | /* Set generic socket state to CLOSED, too */ |
TASS Belgium NV |
131:4758606c9316 | 1345 | (t->sock).state &= 0xFF00U; |
TASS Belgium NV |
131:4758606c9316 | 1346 | (t->sock).state |= PICO_SOCKET_STATE_CLOSED; |
TASS Belgium NV |
131:4758606c9316 | 1347 | |
TASS Belgium NV |
131:4758606c9316 | 1348 | /* call EV_FIN wakeup before deleting */ |
TASS Belgium NV |
131:4758606c9316 | 1349 | if ((t->sock).wakeup) |
TASS Belgium NV |
131:4758606c9316 | 1350 | (t->sock).wakeup(PICO_SOCK_EV_FIN, &(t->sock)); |
TASS Belgium NV |
131:4758606c9316 | 1351 | |
TASS Belgium NV |
131:4758606c9316 | 1352 | /* delete socket */ |
TASS Belgium NV |
131:4758606c9316 | 1353 | pico_socket_del(&t->sock); |
tass | 152:a3d286bf94e5 | 1354 | } |
tass | 152:a3d286bf94e5 | 1355 | return ret; |
tass | 152:a3d286bf94e5 | 1356 | } |
tass | 152:a3d286bf94e5 | 1357 | |
tass | 152:a3d286bf94e5 | 1358 | static inline void tcp_fill_rst_payload(struct pico_frame *fr, struct pico_frame *f) |
tass | 152:a3d286bf94e5 | 1359 | { |
tass | 152:a3d286bf94e5 | 1360 | /* fill in IP data from original frame */ |
tass | 152:a3d286bf94e5 | 1361 | if (IS_IPV4(fr)) { |
tass | 152:a3d286bf94e5 | 1362 | memcpy(f->net_hdr, fr->net_hdr, sizeof(struct pico_ipv4_hdr)); |
tass | 152:a3d286bf94e5 | 1363 | ((struct pico_ipv4_hdr *)(f->net_hdr))->dst.addr = ((struct pico_ipv4_hdr *)(fr->net_hdr))->src.addr; |
tass | 152:a3d286bf94e5 | 1364 | ((struct pico_ipv4_hdr *)(f->net_hdr))->src.addr = ((struct pico_ipv4_hdr *)(fr->net_hdr))->dst.addr; |
tass | 152:a3d286bf94e5 | 1365 | tcp_dbg("Making IPv4 reset frame...\n"); |
tass | 152:a3d286bf94e5 | 1366 | |
tass | 152:a3d286bf94e5 | 1367 | } else { |
tass | 152:a3d286bf94e5 | 1368 | memcpy(f->net_hdr, fr->net_hdr, sizeof(struct pico_ipv6_hdr)); |
tass | 152:a3d286bf94e5 | 1369 | ((struct pico_ipv6_hdr *)(f->net_hdr))->dst = ((struct pico_ipv6_hdr *)(fr->net_hdr))->src; |
tass | 152:a3d286bf94e5 | 1370 | ((struct pico_ipv6_hdr *)(f->net_hdr))->src = ((struct pico_ipv6_hdr *)(fr->net_hdr))->dst; |
TASS Belgium NV |
131:4758606c9316 | 1371 | } |
TASS Belgium NV |
131:4758606c9316 | 1372 | |
tass | 152:a3d286bf94e5 | 1373 | /* fill in TCP data from original frame */ |
tass | 152:a3d286bf94e5 | 1374 | ((struct pico_tcp_hdr *)(f->transport_hdr))->trans.dport = ((struct pico_tcp_hdr *)(fr->transport_hdr))->trans.sport; |
tass | 152:a3d286bf94e5 | 1375 | ((struct pico_tcp_hdr *)(f->transport_hdr))->trans.sport = ((struct pico_tcp_hdr *)(fr->transport_hdr))->trans.dport; |
tass | 152:a3d286bf94e5 | 1376 | |
tass | 152:a3d286bf94e5 | 1377 | } |
tass | 152:a3d286bf94e5 | 1378 | |
tass | 152:a3d286bf94e5 | 1379 | |
tass | 152:a3d286bf94e5 | 1380 | 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) |
tass | 152:a3d286bf94e5 | 1381 | { |
tass | 152:a3d286bf94e5 | 1382 | if(!(hdr1->flags & PICO_TCP_ACK)) |
tass | 152:a3d286bf94e5 | 1383 | hdr->flags |= PICO_TCP_ACK; |
tass | 152:a3d286bf94e5 | 1384 | |
tass | 152:a3d286bf94e5 | 1385 | hdr->rwnd = 0; |
tass | 152:a3d286bf94e5 | 1386 | if (((struct pico_tcp_hdr *)(fr->transport_hdr))->flags & PICO_TCP_ACK) { |
tass | 152:a3d286bf94e5 | 1387 | hdr->seq = ((struct pico_tcp_hdr *)(fr->transport_hdr))->ack; |
tass | 152:a3d286bf94e5 | 1388 | } else { |
tass | 152:a3d286bf94e5 | 1389 | hdr->seq = 0U; |
tass | 152:a3d286bf94e5 | 1390 | } |
tass | 152:a3d286bf94e5 | 1391 | |
tass | 152:a3d286bf94e5 | 1392 | hdr->ack = 0; |
tass | 152:a3d286bf94e5 | 1393 | if(!(hdr1->flags & PICO_TCP_ACK)) |
tass | 152:a3d286bf94e5 | 1394 | hdr->ack = long_be(long_be(((struct pico_tcp_hdr *)(fr->transport_hdr))->seq) + fr->payload_len); |
tass | 152:a3d286bf94e5 | 1395 | |
tass | 152:a3d286bf94e5 | 1396 | hdr->crc = short_be(pico_tcp_checksum(f)); |
daniele | 29:1a47b7151851 | 1397 | } |
daniele | 29:1a47b7151851 | 1398 | |
daniele | 29:1a47b7151851 | 1399 | int pico_tcp_reply_rst(struct pico_frame *fr) |
daniele | 29:1a47b7151851 | 1400 | { |
TASS Belgium NV |
131:4758606c9316 | 1401 | struct pico_tcp_hdr *hdr, *hdr1; |
TASS Belgium NV |
131:4758606c9316 | 1402 | struct pico_frame *f; |
TASS Belgium NV |
131:4758606c9316 | 1403 | uint16_t size = PICO_SIZE_TCPHDR; |
TASS Belgium NV |
131:4758606c9316 | 1404 | |
TASS Belgium NV |
131:4758606c9316 | 1405 | |
TASS Belgium NV |
131:4758606c9316 | 1406 | hdr1 = (struct pico_tcp_hdr *) (fr->transport_hdr); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1407 | if ((hdr1->flags & PICO_TCP_RST) != 0) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1408 | return -1; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1409 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1410 | tcp_dbg("TCP> sending RST ... \n"); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1411 | |
TASS Belgium NV |
131:4758606c9316 | 1412 | f = fr->sock->net->alloc(fr->sock->net, size); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1413 | if (!f) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1414 | pico_err = PICO_ERR_ENOMEM; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1415 | return -1; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1416 | } |
TASS Belgium NV |
131:4758606c9316 | 1417 | |
tass | 152:a3d286bf94e5 | 1418 | tcp_fill_rst_payload(fr, f); |
tass | 152:a3d286bf94e5 | 1419 | |
TASS Belgium NV |
131:4758606c9316 | 1420 | hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 1421 | hdr->len = (uint8_t)(size << 2); |
TASS Belgium NV |
131:4758606c9316 | 1422 | hdr->flags = PICO_TCP_RST; |
tass | 152:a3d286bf94e5 | 1423 | |
tass | 152:a3d286bf94e5 | 1424 | tcp_fill_rst_header(fr, hdr1, f, hdr); |
tass | 152:a3d286bf94e5 | 1425 | |
tass | 152:a3d286bf94e5 | 1426 | if (0) { |
tass | 152:a3d286bf94e5 | 1427 | #ifdef PICO_SUPPORT_IPV4 |
tass | 152:a3d286bf94e5 | 1428 | } else if (IS_IPV4(f)) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1429 | tcp_dbg("Pushing IPv4 reset frame...\n"); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1430 | pico_ipv4_frame_push(f, &(((struct pico_ipv4_hdr *)(f->net_hdr))->dst), PICO_PROTO_TCP); |
tass | 152:a3d286bf94e5 | 1431 | #endif |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1432 | #ifdef PICO_SUPPORT_IPV6 |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1433 | } else { |
tass | 152:a3d286bf94e5 | 1434 | pico_ipv6_frame_push(f, NULL, &(((struct pico_ipv6_hdr *)(f->net_hdr))->dst), PICO_PROTO_TCP, 0); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1435 | #endif |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1436 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1437 | |
TASS Belgium NV |
131:4758606c9316 | 1438 | |
TASS Belgium NV |
131:4758606c9316 | 1439 | return 0; |
daniele | 29:1a47b7151851 | 1440 | } |
daniele | 29:1a47b7151851 | 1441 | |
daniele | 29:1a47b7151851 | 1442 | static int tcp_nosync_rst(struct pico_socket *s, struct pico_frame *fr) |
daniele | 29:1a47b7151851 | 1443 | { |
TASS Belgium NV |
131:4758606c9316 | 1444 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
TASS Belgium NV |
131:4758606c9316 | 1445 | struct pico_frame *f; |
TASS Belgium NV |
131:4758606c9316 | 1446 | struct pico_tcp_hdr *hdr, *hdr_rcv; |
TASS Belgium NV |
131:4758606c9316 | 1447 | uint16_t opt_len = tcp_options_size(t, PICO_TCP_RST | PICO_TCP_ACK); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1448 | hdr_rcv = (struct pico_tcp_hdr *) fr->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 1449 | |
TASS Belgium NV |
131:4758606c9316 | 1450 | tcp_dbg("TCP SEND RST (NON-SYNC) >>>>>>>>>>>>>>>>>> state %x\n", (s->state & PICO_SOCKET_STATE_TCP)); |
TASS Belgium NV |
131:4758606c9316 | 1451 | if (((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_LISTEN)) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1452 | if ((fr->flags & PICO_TCP_RST) != 0) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1453 | return 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1454 | |
TASS Belgium NV |
131:4758606c9316 | 1455 | return pico_tcp_reply_rst(fr); |
TASS Belgium NV |
131:4758606c9316 | 1456 | } |
TASS Belgium NV |
131:4758606c9316 | 1457 | |
TASS Belgium NV |
131:4758606c9316 | 1458 | /***************************************************************************/ |
TASS Belgium NV |
131:4758606c9316 | 1459 | /* sending RST */ |
TASS Belgium NV |
131:4758606c9316 | 1460 | f = t->sock.net->alloc(t->sock.net, (uint16_t)(PICO_SIZE_TCPHDR + opt_len)); |
TASS Belgium NV |
131:4758606c9316 | 1461 | |
TASS Belgium NV |
131:4758606c9316 | 1462 | if (!f) { |
TASS Belgium NV |
131:4758606c9316 | 1463 | return -1; |
TASS Belgium NV |
131:4758606c9316 | 1464 | } |
TASS Belgium NV |
131:4758606c9316 | 1465 | |
TASS Belgium NV |
131:4758606c9316 | 1466 | |
TASS Belgium NV |
131:4758606c9316 | 1467 | f->sock = &t->sock; |
TASS Belgium NV |
131:4758606c9316 | 1468 | hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 1469 | hdr->len = (uint8_t)((PICO_SIZE_TCPHDR + opt_len) << 2 | t->jumbo); |
TASS Belgium NV |
131:4758606c9316 | 1470 | hdr->flags = PICO_TCP_RST | PICO_TCP_ACK; |
TASS Belgium NV |
131:4758606c9316 | 1471 | hdr->rwnd = short_be(t->wnd); |
TASS Belgium NV |
131:4758606c9316 | 1472 | tcp_set_space(t); |
TASS Belgium NV |
131:4758606c9316 | 1473 | tcp_add_options(t, f, PICO_TCP_RST | PICO_TCP_ACK, opt_len); |
TASS Belgium NV |
131:4758606c9316 | 1474 | hdr->trans.sport = t->sock.local_port; |
TASS Belgium NV |
131:4758606c9316 | 1475 | hdr->trans.dport = t->sock.remote_port; |
TASS Belgium NV |
131:4758606c9316 | 1476 | |
TASS Belgium NV |
131:4758606c9316 | 1477 | /* non-synchronized state */ |
TASS Belgium NV |
131:4758606c9316 | 1478 | if (hdr_rcv->flags & PICO_TCP_ACK) { |
TASS Belgium NV |
131:4758606c9316 | 1479 | hdr->seq = hdr_rcv->ack; |
TASS Belgium NV |
131:4758606c9316 | 1480 | } else { |
TASS Belgium NV |
131:4758606c9316 | 1481 | hdr->seq = 0U; |
TASS Belgium NV |
131:4758606c9316 | 1482 | } |
TASS Belgium NV |
131:4758606c9316 | 1483 | |
TASS Belgium NV |
131:4758606c9316 | 1484 | hdr->ack = long_be(SEQN(fr) + fr->payload_len); |
TASS Belgium NV |
131:4758606c9316 | 1485 | |
TASS Belgium NV |
131:4758606c9316 | 1486 | t->rcv_ackd = t->rcv_nxt; |
TASS Belgium NV |
131:4758606c9316 | 1487 | f->start = f->transport_hdr + PICO_SIZE_TCPHDR; |
TASS Belgium NV |
131:4758606c9316 | 1488 | hdr->rwnd = short_be(t->wnd); |
TASS Belgium NV |
131:4758606c9316 | 1489 | hdr->crc = 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1490 | hdr->crc = short_be(pico_tcp_checksum(f)); |
TASS Belgium NV |
131:4758606c9316 | 1491 | |
TASS Belgium NV |
131:4758606c9316 | 1492 | /* TCP: ENQUEUE to PROTO */ |
TASS Belgium NV |
131:4758606c9316 | 1493 | pico_enqueue(&tcp_out, f); |
TASS Belgium NV |
131:4758606c9316 | 1494 | |
TASS Belgium NV |
131:4758606c9316 | 1495 | /***************************************************************************/ |
TASS Belgium NV |
131:4758606c9316 | 1496 | |
TASS Belgium NV |
131:4758606c9316 | 1497 | tcp_dbg("TCP SEND_RST (NON_SYNC) >>>>>>>>>>>>>>> DONE, ...\n"); |
TASS Belgium NV |
131:4758606c9316 | 1498 | |
TASS Belgium NV |
131:4758606c9316 | 1499 | return 0; |
daniele | 29:1a47b7151851 | 1500 | } |
daniele | 29:1a47b7151851 | 1501 | |
tass | 152:a3d286bf94e5 | 1502 | static void tcp_deltcb(pico_time when, void *arg); |
tass | 152:a3d286bf94e5 | 1503 | |
tass | 152:a3d286bf94e5 | 1504 | static void tcp_linger(struct pico_socket_tcp *t) |
tass | 152:a3d286bf94e5 | 1505 | { |
tass | 152:a3d286bf94e5 | 1506 | if (t->fin_tmr) { |
tass | 152:a3d286bf94e5 | 1507 | pico_timer_cancel(t->fin_tmr); |
tass | 152:a3d286bf94e5 | 1508 | } |
tass | 152:a3d286bf94e5 | 1509 | t->fin_tmr = pico_timer_add(t->linger_timeout, tcp_deltcb, t); |
tass | 152:a3d286bf94e5 | 1510 | } |
tass | 152:a3d286bf94e5 | 1511 | |
daniele | 29:1a47b7151851 | 1512 | static void tcp_send_fin(struct pico_socket_tcp *t) |
daniele | 29:1a47b7151851 | 1513 | { |
TASS Belgium NV |
131:4758606c9316 | 1514 | struct pico_frame *f; |
TASS Belgium NV |
131:4758606c9316 | 1515 | struct pico_tcp_hdr *hdr; |
TASS Belgium NV |
131:4758606c9316 | 1516 | uint16_t opt_len = tcp_options_size(t, PICO_TCP_FIN); |
TASS Belgium NV |
131:4758606c9316 | 1517 | f = t->sock.net->alloc(t->sock.net, (uint16_t)(PICO_SIZE_TCPHDR + opt_len)); |
TASS Belgium NV |
131:4758606c9316 | 1518 | if (!f) { |
TASS Belgium NV |
131:4758606c9316 | 1519 | return; |
TASS Belgium NV |
131:4758606c9316 | 1520 | } |
TASS Belgium NV |
131:4758606c9316 | 1521 | |
TASS Belgium NV |
131:4758606c9316 | 1522 | f->sock = &t->sock; |
TASS Belgium NV |
131:4758606c9316 | 1523 | hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 1524 | hdr->len = (uint8_t)((PICO_SIZE_TCPHDR + opt_len) << 2 | t->jumbo); |
TASS Belgium NV |
131:4758606c9316 | 1525 | hdr->flags = PICO_TCP_FIN | PICO_TCP_ACK; |
TASS Belgium NV |
131:4758606c9316 | 1526 | hdr->ack = long_be(t->rcv_nxt); |
TASS Belgium NV |
131:4758606c9316 | 1527 | t->rcv_ackd = t->rcv_nxt; |
TASS Belgium NV |
131:4758606c9316 | 1528 | hdr->rwnd = short_be(t->wnd); |
TASS Belgium NV |
131:4758606c9316 | 1529 | tcp_set_space(t); |
TASS Belgium NV |
131:4758606c9316 | 1530 | tcp_add_options(t, f, PICO_TCP_FIN, opt_len); |
TASS Belgium NV |
131:4758606c9316 | 1531 | hdr->trans.sport = t->sock.local_port; |
TASS Belgium NV |
131:4758606c9316 | 1532 | hdr->trans.dport = t->sock.remote_port; |
tass | 152:a3d286bf94e5 | 1533 | hdr->seq = long_be(t->snd_nxt); |
TASS Belgium NV |
131:4758606c9316 | 1534 | |
TASS Belgium NV |
131:4758606c9316 | 1535 | f->start = f->transport_hdr + PICO_SIZE_TCPHDR; |
TASS Belgium NV |
131:4758606c9316 | 1536 | hdr->rwnd = short_be(t->wnd); |
TASS Belgium NV |
131:4758606c9316 | 1537 | hdr->crc = 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1538 | hdr->crc = short_be(pico_tcp_checksum(f)); |
TASS Belgium NV |
131:4758606c9316 | 1539 | /* tcp_dbg("SENDING FIN...\n"); */ |
tass | 152:a3d286bf94e5 | 1540 | if (t->linger_timeout > 0) { |
tass | 152:a3d286bf94e5 | 1541 | pico_enqueue(&tcp_out, f); |
tass | 152:a3d286bf94e5 | 1542 | t->snd_nxt++; |
tass | 152:a3d286bf94e5 | 1543 | } else { |
tass | 152:a3d286bf94e5 | 1544 | pico_frame_discard(f); |
tass | 152:a3d286bf94e5 | 1545 | } |
tass | 152:a3d286bf94e5 | 1546 | tcp_linger(t); |
daniele | 29:1a47b7151851 | 1547 | } |
daniele | 29:1a47b7151851 | 1548 | |
daniele | 29:1a47b7151851 | 1549 | static void tcp_sack_prepare(struct pico_socket_tcp *t) |
daniele | 29:1a47b7151851 | 1550 | { |
TASS Belgium NV |
131:4758606c9316 | 1551 | struct tcp_input_segment *pkt; |
TASS Belgium NV |
131:4758606c9316 | 1552 | uint32_t left = 0, right = 0; |
TASS Belgium NV |
131:4758606c9316 | 1553 | struct tcp_sack_block *sb; |
TASS Belgium NV |
131:4758606c9316 | 1554 | int n = 0; |
TASS Belgium NV |
131:4758606c9316 | 1555 | if (t->sacks) /* previous sacks are pending */ |
TASS Belgium NV |
131:4758606c9316 | 1556 | return; |
TASS Belgium NV |
131:4758606c9316 | 1557 | |
TASS Belgium NV |
131:4758606c9316 | 1558 | pkt = first_segment(&t->tcpq_in); |
TASS Belgium NV |
131:4758606c9316 | 1559 | while(n < 3) { |
TASS Belgium NV |
131:4758606c9316 | 1560 | if (!pkt) { |
TASS Belgium NV |
131:4758606c9316 | 1561 | if(left) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1562 | sb = PICO_ZALLOC(sizeof(struct tcp_sack_block)); |
TASS Belgium NV |
131:4758606c9316 | 1563 | if (!sb) |
TASS Belgium NV |
131:4758606c9316 | 1564 | break; |
TASS Belgium NV |
131:4758606c9316 | 1565 | |
TASS Belgium NV |
131:4758606c9316 | 1566 | sb->left = long_be(left); |
TASS Belgium NV |
131:4758606c9316 | 1567 | sb->right = long_be(right); |
TASS Belgium NV |
131:4758606c9316 | 1568 | n++; |
TASS Belgium NV |
131:4758606c9316 | 1569 | sb->next = t->sacks; |
TASS Belgium NV |
131:4758606c9316 | 1570 | t->sacks = sb; |
TASS Belgium NV |
131:4758606c9316 | 1571 | left = 0; |
TASS Belgium NV |
131:4758606c9316 | 1572 | right = 0; |
TASS Belgium NV |
131:4758606c9316 | 1573 | } |
TASS Belgium NV |
131:4758606c9316 | 1574 | |
TASS Belgium NV |
131:4758606c9316 | 1575 | break; |
TASS Belgium NV |
131:4758606c9316 | 1576 | } |
TASS Belgium NV |
131:4758606c9316 | 1577 | |
TASS Belgium NV |
131:4758606c9316 | 1578 | if (pkt->seq < t->rcv_nxt) { |
TASS Belgium NV |
131:4758606c9316 | 1579 | pkt = next_segment(&t->tcpq_in, pkt); |
TASS Belgium NV |
131:4758606c9316 | 1580 | continue; |
TASS Belgium NV |
131:4758606c9316 | 1581 | } |
TASS Belgium NV |
131:4758606c9316 | 1582 | |
TASS Belgium NV |
131:4758606c9316 | 1583 | if (!left) { |
TASS Belgium NV |
131:4758606c9316 | 1584 | left = pkt->seq; |
TASS Belgium NV |
131:4758606c9316 | 1585 | right = pkt->seq + pkt->payload_len; |
TASS Belgium NV |
131:4758606c9316 | 1586 | pkt = next_segment(&t->tcpq_in, pkt); |
TASS Belgium NV |
131:4758606c9316 | 1587 | continue; |
TASS Belgium NV |
131:4758606c9316 | 1588 | } |
TASS Belgium NV |
131:4758606c9316 | 1589 | |
TASS Belgium NV |
131:4758606c9316 | 1590 | if(pkt->seq == right) { |
TASS Belgium NV |
131:4758606c9316 | 1591 | right += pkt->payload_len; |
TASS Belgium NV |
131:4758606c9316 | 1592 | pkt = next_segment(&t->tcpq_in, pkt); |
TASS Belgium NV |
131:4758606c9316 | 1593 | continue; |
TASS Belgium NV |
131:4758606c9316 | 1594 | } else { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1595 | sb = PICO_ZALLOC(sizeof(struct tcp_sack_block)); |
TASS Belgium NV |
131:4758606c9316 | 1596 | if (!sb) |
TASS Belgium NV |
131:4758606c9316 | 1597 | break; |
TASS Belgium NV |
131:4758606c9316 | 1598 | |
TASS Belgium NV |
131:4758606c9316 | 1599 | sb->left = long_be(left); |
TASS Belgium NV |
131:4758606c9316 | 1600 | sb->right = long_be(right); |
TASS Belgium NV |
131:4758606c9316 | 1601 | n++; |
TASS Belgium NV |
131:4758606c9316 | 1602 | sb->next = t->sacks; |
TASS Belgium NV |
131:4758606c9316 | 1603 | t->sacks = sb; |
TASS Belgium NV |
131:4758606c9316 | 1604 | left = 0; |
TASS Belgium NV |
131:4758606c9316 | 1605 | right = 0; |
TASS Belgium NV |
131:4758606c9316 | 1606 | pkt = next_segment(&t->tcpq_in, pkt); |
TASS Belgium NV |
131:4758606c9316 | 1607 | } |
daniele | 29:1a47b7151851 | 1608 | } |
daniele | 29:1a47b7151851 | 1609 | } |
daniele | 29:1a47b7151851 | 1610 | |
tass | 152:a3d286bf94e5 | 1611 | static inline int tcp_data_in_expected(struct pico_socket_tcp *t, struct pico_frame *f) |
tass | 152:a3d286bf94e5 | 1612 | { |
tass | 152:a3d286bf94e5 | 1613 | struct tcp_input_segment *nxt; |
tass | 152:a3d286bf94e5 | 1614 | if (pico_seq_compare(SEQN(f), t->rcv_nxt) == 0) { /* Exactly what we expected */ |
tass | 152:a3d286bf94e5 | 1615 | /* Create new segment and enqueue it */ |
tass | 152:a3d286bf94e5 | 1616 | struct tcp_input_segment *input = segment_from_frame(f); |
tass | 152:a3d286bf94e5 | 1617 | if (!input) { |
tass | 152:a3d286bf94e5 | 1618 | pico_err = PICO_ERR_ENOMEM; |
tass | 152:a3d286bf94e5 | 1619 | return -1; |
tass | 152:a3d286bf94e5 | 1620 | } |
tass | 152:a3d286bf94e5 | 1621 | |
tass | 152:a3d286bf94e5 | 1622 | if(pico_enqueue_segment(&t->tcpq_in, input) <= 0) |
tass | 152:a3d286bf94e5 | 1623 | { |
tass | 152:a3d286bf94e5 | 1624 | /* failed to enqueue, destroy segment */ |
tass | 152:a3d286bf94e5 | 1625 | PICO_FREE(input->payload); |
tass | 152:a3d286bf94e5 | 1626 | PICO_FREE(input); |
tass | 152:a3d286bf94e5 | 1627 | return -1; |
tass | 152:a3d286bf94e5 | 1628 | } else { |
tass | 152:a3d286bf94e5 | 1629 | t->rcv_nxt = SEQN(f) + f->payload_len; |
tass | 152:a3d286bf94e5 | 1630 | nxt = peek_segment(&t->tcpq_in, t->rcv_nxt); |
tass | 152:a3d286bf94e5 | 1631 | while(nxt) { |
tass | 152:a3d286bf94e5 | 1632 | tcp_dbg("scrolling rcv_nxt...%08x\n", t->rcv_nxt); |
tass | 152:a3d286bf94e5 | 1633 | t->rcv_nxt += nxt->payload_len; |
tass | 152:a3d286bf94e5 | 1634 | nxt = peek_segment(&t->tcpq_in, t->rcv_nxt); |
tass | 152:a3d286bf94e5 | 1635 | } |
tass | 152:a3d286bf94e5 | 1636 | t->sock.ev_pending |= PICO_SOCK_EV_RD; |
tass | 152:a3d286bf94e5 | 1637 | } |
tass | 152:a3d286bf94e5 | 1638 | } else { |
tass | 152:a3d286bf94e5 | 1639 | tcp_dbg("TCP> lo segment. Uninteresting retransmission. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f)); |
tass | 152:a3d286bf94e5 | 1640 | } |
tass | 152:a3d286bf94e5 | 1641 | |
tass | 152:a3d286bf94e5 | 1642 | return 0; |
tass | 152:a3d286bf94e5 | 1643 | } |
tass | 152:a3d286bf94e5 | 1644 | |
tass | 152:a3d286bf94e5 | 1645 | static inline int tcp_data_in_high_segment(struct pico_socket_tcp *t, struct pico_frame *f) |
tass | 152:a3d286bf94e5 | 1646 | { |
tass | 152:a3d286bf94e5 | 1647 | tcp_dbg("TCP> hi segment. Possible packet loss. I'll dupack this. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f)); |
tass | 152:a3d286bf94e5 | 1648 | if (t->sack_ok) { |
tass | 152:a3d286bf94e5 | 1649 | struct tcp_input_segment *input = segment_from_frame(f); |
tass | 152:a3d286bf94e5 | 1650 | if (!input) { |
tass | 152:a3d286bf94e5 | 1651 | pico_err = PICO_ERR_ENOMEM; |
tass | 152:a3d286bf94e5 | 1652 | return -1; |
tass | 152:a3d286bf94e5 | 1653 | } |
tass | 152:a3d286bf94e5 | 1654 | |
tass | 152:a3d286bf94e5 | 1655 | if(pico_enqueue_segment(&t->tcpq_in, input) <= 0) { |
tass | 152:a3d286bf94e5 | 1656 | /* failed to enqueue, destroy segment */ |
tass | 152:a3d286bf94e5 | 1657 | PICO_FREE(input->payload); |
tass | 152:a3d286bf94e5 | 1658 | PICO_FREE(input); |
tass | 152:a3d286bf94e5 | 1659 | return -1; |
tass | 152:a3d286bf94e5 | 1660 | } |
tass | 152:a3d286bf94e5 | 1661 | |
tass | 152:a3d286bf94e5 | 1662 | tcp_sack_prepare(t); |
tass | 152:a3d286bf94e5 | 1663 | } |
tass | 152:a3d286bf94e5 | 1664 | |
tass | 152:a3d286bf94e5 | 1665 | return 0; |
tass | 152:a3d286bf94e5 | 1666 | } |
tass | 152:a3d286bf94e5 | 1667 | |
tass | 152:a3d286bf94e5 | 1668 | static inline void tcp_data_in_send_ack(struct pico_socket_tcp *t, struct pico_frame *f) |
tass | 152:a3d286bf94e5 | 1669 | { |
tass | 152:a3d286bf94e5 | 1670 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
tass | 152:a3d286bf94e5 | 1671 | /* In either case, ack til recv_nxt, unless received data raises a RST flag. */ |
tass | 152:a3d286bf94e5 | 1672 | if (((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_CLOSE_WAIT) && |
tass | 152:a3d286bf94e5 | 1673 | ((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_SYN_SENT) && |
tass | 152:a3d286bf94e5 | 1674 | ((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_SYN_RECV) && |
tass | 152:a3d286bf94e5 | 1675 | ((hdr->flags & PICO_TCP_RST) == 0)) |
tass | 152:a3d286bf94e5 | 1676 | tcp_send_ack(t); |
tass | 152:a3d286bf94e5 | 1677 | } |
tass | 152:a3d286bf94e5 | 1678 | |
daniele | 29:1a47b7151851 | 1679 | static int tcp_data_in(struct pico_socket *s, struct pico_frame *f) |
daniele | 29:1a47b7151851 | 1680 | { |
TASS Belgium NV |
131:4758606c9316 | 1681 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
TASS Belgium NV |
131:4758606c9316 | 1682 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1683 | uint16_t payload_len = (uint16_t)(f->transport_len - ((hdr->len & 0xf0u) >> 2u)); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1684 | int ret = 0; |
tass | 152:a3d286bf94e5 | 1685 | (void)hdr; |
tass | 152:a3d286bf94e5 | 1686 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1687 | if (((hdr->len & 0xf0u) >> 2u) <= f->transport_len) { |
TASS Belgium NV |
131:4758606c9316 | 1688 | tcp_parse_options(f); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1689 | f->payload = f->transport_hdr + ((hdr->len & 0xf0u) >> 2u); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1690 | f->payload_len = payload_len; |
TASS Belgium NV |
131:4758606c9316 | 1691 | tcp_dbg("TCP> Received segment. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f)); |
TASS Belgium NV |
131:4758606c9316 | 1692 | |
tass | 152:a3d286bf94e5 | 1693 | if (pico_seq_compare(SEQN(f), t->rcv_nxt) <= 0) { |
tass | 152:a3d286bf94e5 | 1694 | ret = tcp_data_in_expected(t, f); |
TASS Belgium NV |
131:4758606c9316 | 1695 | } else { |
tass | 152:a3d286bf94e5 | 1696 | ret = tcp_data_in_high_segment(t, f); |
daniele | 29:1a47b7151851 | 1697 | } |
TASS Belgium NV |
131:4758606c9316 | 1698 | |
tass | 152:a3d286bf94e5 | 1699 | tcp_data_in_send_ack(t, f); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1700 | return ret; |
daniele | 29:1a47b7151851 | 1701 | } else { |
TASS Belgium NV |
131:4758606c9316 | 1702 | tcp_dbg("TCP: invalid data in pkt len, exp: %d, got %d\n", (hdr->len & 0xf0) >> 2, f->transport_len); |
TASS Belgium NV |
131:4758606c9316 | 1703 | return -1; |
daniele | 29:1a47b7151851 | 1704 | } |
daniele | 29:1a47b7151851 | 1705 | } |
daniele | 29:1a47b7151851 | 1706 | |
TASS Belgium NV |
131:4758606c9316 | 1707 | static int tcp_ack_advance_una(struct pico_socket_tcp *t, struct pico_frame *f, pico_time *timestamp) |
daniele | 29:1a47b7151851 | 1708 | { |
TASS Belgium NV |
131:4758606c9316 | 1709 | int ret = release_all_until(&t->tcpq_out, ACKN(f), timestamp); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1710 | if (ret > 0) { |
TASS Belgium NV |
131:4758606c9316 | 1711 | t->sock.ev_pending |= PICO_SOCK_EV_WR; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1712 | } |
TASS Belgium NV |
131:4758606c9316 | 1713 | |
TASS Belgium NV |
131:4758606c9316 | 1714 | return ret; |
daniele | 29:1a47b7151851 | 1715 | } |
daniele | 29:1a47b7151851 | 1716 | |
tass | 128:ae39e6e81531 | 1717 | static uint16_t time_diff(pico_time a, pico_time b) |
daniele | 29:1a47b7151851 | 1718 | { |
TASS Belgium NV |
131:4758606c9316 | 1719 | if (a >= b) |
TASS Belgium NV |
131:4758606c9316 | 1720 | return (uint16_t)(a - b); |
TASS Belgium NV |
131:4758606c9316 | 1721 | else |
TASS Belgium NV |
131:4758606c9316 | 1722 | return (uint16_t)(b - a); |
daniele | 29:1a47b7151851 | 1723 | } |
daniele | 29:1a47b7151851 | 1724 | |
daniele | 29:1a47b7151851 | 1725 | static void tcp_rtt(struct pico_socket_tcp *t, uint32_t rtt) |
daniele | 29:1a47b7151851 | 1726 | { |
daniele | 29:1a47b7151851 | 1727 | |
TASS Belgium NV |
131:4758606c9316 | 1728 | uint32_t avg = t->avg_rtt; |
TASS Belgium NV |
131:4758606c9316 | 1729 | uint32_t rvar = t->rttvar; |
TASS Belgium NV |
131:4758606c9316 | 1730 | if (!avg) { |
TASS Belgium NV |
131:4758606c9316 | 1731 | /* This follows RFC2988 |
TASS Belgium NV |
131:4758606c9316 | 1732 | * (2.2) When the first RTT measurement R is made, the host MUST set |
TASS Belgium NV |
131:4758606c9316 | 1733 | * |
TASS Belgium NV |
131:4758606c9316 | 1734 | * SRTT <- R |
TASS Belgium NV |
131:4758606c9316 | 1735 | * RTTVAR <- R/2 |
TASS Belgium NV |
131:4758606c9316 | 1736 | * RTO <- SRTT + max (G, K*RTTVAR) |
TASS Belgium NV |
131:4758606c9316 | 1737 | */ |
TASS Belgium NV |
131:4758606c9316 | 1738 | t->avg_rtt = rtt; |
TASS Belgium NV |
131:4758606c9316 | 1739 | t->rttvar = rtt >> 1; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1740 | rto_set(t, t->avg_rtt + (t->rttvar << 2)); |
TASS Belgium NV |
131:4758606c9316 | 1741 | } else { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1742 | int32_t var = (int32_t)t->avg_rtt - (int32_t)rtt; |
TASS Belgium NV |
131:4758606c9316 | 1743 | if (var < 0) |
TASS Belgium NV |
131:4758606c9316 | 1744 | var = 0 - var; |
TASS Belgium NV |
131:4758606c9316 | 1745 | |
TASS Belgium NV |
131:4758606c9316 | 1746 | /* RFC2988, section (2.3). Alpha and beta are the ones suggested. */ |
TASS Belgium NV |
131:4758606c9316 | 1747 | |
TASS Belgium NV |
131:4758606c9316 | 1748 | /* First, evaluate a new value for the rttvar */ |
TASS Belgium NV |
131:4758606c9316 | 1749 | t->rttvar <<= 2; |
TASS Belgium NV |
131:4758606c9316 | 1750 | t->rttvar -= rvar; |
TASS Belgium NV |
131:4758606c9316 | 1751 | t->rttvar += (uint32_t)var; |
TASS Belgium NV |
131:4758606c9316 | 1752 | t->rttvar >>= 2; |
TASS Belgium NV |
131:4758606c9316 | 1753 | |
TASS Belgium NV |
131:4758606c9316 | 1754 | /* Then, calculate the new avg_rtt */ |
TASS Belgium NV |
131:4758606c9316 | 1755 | t->avg_rtt <<= 3; |
TASS Belgium NV |
131:4758606c9316 | 1756 | t->avg_rtt -= avg; |
TASS Belgium NV |
131:4758606c9316 | 1757 | t->avg_rtt += rtt; |
TASS Belgium NV |
131:4758606c9316 | 1758 | t->avg_rtt >>= 3; |
TASS Belgium NV |
131:4758606c9316 | 1759 | |
TASS Belgium NV |
131:4758606c9316 | 1760 | /* Finally, assign a new value for the RTO, as specified in the RFC, with K=4 */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1761 | rto_set(t, t->avg_rtt + (t->rttvar << 2)); |
TASS Belgium NV |
131:4758606c9316 | 1762 | } |
TASS Belgium NV |
131:4758606c9316 | 1763 | |
TASS Belgium NV |
131:4758606c9316 | 1764 | tcp_dbg(" -----=============== RTT CUR: %u AVG: %u RTTVAR: %u RTO: %u ======================----\n", rtt, t->avg_rtt, t->rttvar, t->rto); |
daniele | 29:1a47b7151851 | 1765 | } |
daniele | 29:1a47b7151851 | 1766 | |
daniele | 29:1a47b7151851 | 1767 | static void tcp_congestion_control(struct pico_socket_tcp *t) |
daniele | 29:1a47b7151851 | 1768 | { |
TASS Belgium NV |
131:4758606c9316 | 1769 | if (t->x_mode > PICO_TCP_LOOKAHEAD) |
TASS Belgium NV |
131:4758606c9316 | 1770 | return; |
TASS Belgium NV |
131:4758606c9316 | 1771 | |
TASS Belgium NV |
131:4758606c9316 | 1772 | tcp_dbg("Doing congestion control\n"); |
TASS Belgium NV |
131:4758606c9316 | 1773 | if (t->cwnd < t->ssthresh) { |
TASS Belgium NV |
131:4758606c9316 | 1774 | t->cwnd++; |
TASS Belgium NV |
131:4758606c9316 | 1775 | } else { |
TASS Belgium NV |
131:4758606c9316 | 1776 | t->cwnd_counter++; |
TASS Belgium NV |
131:4758606c9316 | 1777 | if (t->cwnd_counter >= t->cwnd) { |
TASS Belgium NV |
131:4758606c9316 | 1778 | t->cwnd++; |
TASS Belgium NV |
131:4758606c9316 | 1779 | t->cwnd_counter = 0; |
TASS Belgium NV |
131:4758606c9316 | 1780 | } |
TASS Belgium NV |
131:4758606c9316 | 1781 | } |
TASS Belgium NV |
131:4758606c9316 | 1782 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1783 | tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", TCP_TIME, t->cwnd, t->ssthresh, t->in_flight); |
daniele | 29:1a47b7151851 | 1784 | } |
daniele | 29:1a47b7151851 | 1785 | |
tass | 128:ae39e6e81531 | 1786 | static void add_retransmission_timer(struct pico_socket_tcp *t, pico_time next_ts); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1787 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1788 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1789 | /* Retransmission time out (RTO). */ |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1790 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1791 | static void tcp_first_timeout(struct pico_socket_tcp *t) |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1792 | { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1793 | t->x_mode = PICO_TCP_BLACKOUT; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1794 | t->cwnd = PICO_TCP_IW; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1795 | t->in_flight = 0; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1796 | } |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1797 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1798 | static int tcp_rto_xmit(struct pico_socket_tcp *t, struct pico_frame *f) |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1799 | { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1800 | struct pico_frame *cpy; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1801 | /* TCP: ENQUEUE to PROTO ( retransmit )*/ |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1802 | cpy = pico_frame_copy(f); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1803 | if (!cpy) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1804 | add_retransmission_timer(t, (t->rto << t->backoff) + TCP_TIME); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1805 | return -1; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1806 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1807 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1808 | if (pico_enqueue(&tcp_out, cpy) > 0) { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1809 | t->snd_last_out = SEQN(cpy); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1810 | add_retransmission_timer(t, (t->rto << (++t->backoff)) + TCP_TIME); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1811 | tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", TCP_TIME, t->cwnd, t->ssthresh, t->in_flight); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1812 | tcp_dbg("Sending RTO!\n"); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1813 | return 1; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1814 | } else { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1815 | tcp_dbg("RTO fail, retry!\n"); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1816 | add_retransmission_timer(t, (t->rto << t->backoff) + TCP_TIME); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1817 | pico_frame_discard(cpy); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1818 | return 0; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1819 | } |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1820 | } |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1821 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1822 | static void tcp_next_zerowindow_probe(struct pico_socket_tcp *t) |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1823 | { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1824 | tcp_dbg("Sending probe!\n"); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1825 | tcp_send_probe(t); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1826 | add_retransmission_timer(t, (t->rto << ++t->backoff) + TCP_TIME); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1827 | } |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1828 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1829 | static int tcp_is_allowed_to_send(struct pico_socket_tcp *t) |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1830 | { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1831 | return t->sock.net && |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1832 | ( |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1833 | ((t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_ESTABLISHED) || |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1834 | ((t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_CLOSE_WAIT) |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1835 | ) && |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1836 | ((t->backoff < PICO_TCP_MAX_RETRANS)); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1837 | } |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1838 | |
tass | 152:a3d286bf94e5 | 1839 | static inline int tcp_retrans_timeout_check_queue(struct pico_socket_tcp *t) |
tass | 152:a3d286bf94e5 | 1840 | { |
tass | 152:a3d286bf94e5 | 1841 | struct pico_frame *f = NULL; |
tass | 152:a3d286bf94e5 | 1842 | f = first_segment(&t->tcpq_out); |
tass | 152:a3d286bf94e5 | 1843 | while (f) { |
tass | 152:a3d286bf94e5 | 1844 | tcp_dbg("Checking frame in queue \n"); |
tass | 152:a3d286bf94e5 | 1845 | if (t->x_mode == PICO_TCP_WINDOW_FULL) { |
tass | 152:a3d286bf94e5 | 1846 | 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); |
tass | 152:a3d286bf94e5 | 1847 | tcp_next_zerowindow_probe(t); |
tass | 152:a3d286bf94e5 | 1848 | return -1; |
tass | 152:a3d286bf94e5 | 1849 | } |
tass | 152:a3d286bf94e5 | 1850 | |
tass | 152:a3d286bf94e5 | 1851 | if (t->x_mode != PICO_TCP_BLACKOUT) |
tass | 152:a3d286bf94e5 | 1852 | tcp_first_timeout(t); |
tass | 152:a3d286bf94e5 | 1853 | |
tass | 152:a3d286bf94e5 | 1854 | tcp_add_header(t, f); |
tass | 152:a3d286bf94e5 | 1855 | if (tcp_rto_xmit(t, f) > 0) /* A segment has been rexmit'd */ |
tass | 152:a3d286bf94e5 | 1856 | return -1; |
tass | 152:a3d286bf94e5 | 1857 | |
tass | 152:a3d286bf94e5 | 1858 | f = next_segment(&t->tcpq_out, f); |
tass | 152:a3d286bf94e5 | 1859 | } |
tass | 152:a3d286bf94e5 | 1860 | if (t->tcpq_out.size < t->tcpq_out.max_size) |
tass | 152:a3d286bf94e5 | 1861 | t->sock.ev_pending |= PICO_SOCK_EV_WR; |
tass | 152:a3d286bf94e5 | 1862 | |
tass | 152:a3d286bf94e5 | 1863 | return 0; |
tass | 152:a3d286bf94e5 | 1864 | |
tass | 152:a3d286bf94e5 | 1865 | |
tass | 152:a3d286bf94e5 | 1866 | |
tass | 152:a3d286bf94e5 | 1867 | } |
tass | 152:a3d286bf94e5 | 1868 | |
tass | 128:ae39e6e81531 | 1869 | static void tcp_retrans_timeout(pico_time val, void *sock) |
daniele | 29:1a47b7151851 | 1870 | { |
TASS Belgium NV |
131:4758606c9316 | 1871 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) sock; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1872 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1873 | t->retrans_tmr = NULL; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1874 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1875 | if (t->retrans_tmr_due == 0ull) { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1876 | return; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1877 | } |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1878 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1879 | if (t->retrans_tmr_due > val) { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1880 | /* Timer was postponed... */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1881 | add_retransmission_timer(t, t->retrans_tmr_due); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1882 | return; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1883 | } |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1884 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1885 | tcp_dbg("TIMEOUT! backoff = %d, rto: %d\n", t->backoff, t->rto); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1886 | t->retrans_tmr_due = 0ull; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1887 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1888 | if (tcp_is_allowed_to_send(t)) { |
tass | 152:a3d286bf94e5 | 1889 | if (tcp_retrans_timeout_check_queue(t) < 0) |
tass | 152:a3d286bf94e5 | 1890 | return; |
TASS Belgium NV |
131:4758606c9316 | 1891 | } |
tass | 97:e73b01cb3147 | 1892 | else if(t->backoff >= PICO_TCP_MAX_RETRANS && (t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_ESTABLISHED ) |
TASS Belgium NV |
131:4758606c9316 | 1893 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1894 | tcp_dbg("Connection timeout!\n"); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1895 | /* the retransmission timer, failed to get an ack for a frame, gives up on the connection */ |
TASS Belgium NV |
131:4758606c9316 | 1896 | tcp_discard_all_segments(&t->tcpq_out); |
TASS Belgium NV |
131:4758606c9316 | 1897 | if(t->sock.wakeup) |
TASS Belgium NV |
131:4758606c9316 | 1898 | t->sock.wakeup(PICO_SOCK_EV_FIN, &t->sock); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1899 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1900 | /* delete socket */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1901 | pico_socket_del(&t->sock); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1902 | return; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1903 | } else { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1904 | tcp_dbg("Retransmission not allowed, rescheduling\n"); |
TASS Belgium NV |
131:4758606c9316 | 1905 | } |
daniele | 29:1a47b7151851 | 1906 | } |
daniele | 29:1a47b7151851 | 1907 | |
tass | 128:ae39e6e81531 | 1908 | static void add_retransmission_timer(struct pico_socket_tcp *t, pico_time next_ts) |
daniele | 29:1a47b7151851 | 1909 | { |
TASS Belgium NV |
131:4758606c9316 | 1910 | struct pico_tree_node *index; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1911 | pico_time now = TCP_TIME; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1912 | pico_time val = 0; |
TASS Belgium NV |
131:4758606c9316 | 1913 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1914 | |
TASS Belgium NV |
131:4758606c9316 | 1915 | if (next_ts == 0) { |
TASS Belgium NV |
131:4758606c9316 | 1916 | struct pico_frame *f; |
TASS Belgium NV |
131:4758606c9316 | 1917 | |
TASS Belgium NV |
131:4758606c9316 | 1918 | pico_tree_foreach(index, &t->tcpq_out.pool){ |
TASS Belgium NV |
131:4758606c9316 | 1919 | f = index->keyValue; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1920 | if ((next_ts == 0) || ((f->timestamp < next_ts) && (f->timestamp > 0))) { |
TASS Belgium NV |
131:4758606c9316 | 1921 | next_ts = f->timestamp; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1922 | val = next_ts + (t->rto << t->backoff); |
TASS Belgium NV |
131:4758606c9316 | 1923 | } |
TASS Belgium NV |
131:4758606c9316 | 1924 | } |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1925 | } else { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1926 | val = next_ts; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1927 | } |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1928 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1929 | if ((val > 0) || (val > now)) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1930 | t->retrans_tmr_due = val; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1931 | } else { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1932 | t->retrans_tmr_due = now + 1; |
daniele | 29:1a47b7151851 | 1933 | } |
TASS Belgium NV |
131:4758606c9316 | 1934 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1935 | if (!t->retrans_tmr) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1936 | t->retrans_tmr = pico_timer_add(t->retrans_tmr_due - now, tcp_retrans_timeout, t); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1937 | tcp_dbg("Next timeout in %u msec\n", (uint32_t) (t->retrans_tmr_due - now)); |
daniele | 29:1a47b7151851 | 1938 | } |
daniele | 29:1a47b7151851 | 1939 | } |
daniele | 29:1a47b7151851 | 1940 | |
daniele | 29:1a47b7151851 | 1941 | static int tcp_retrans(struct pico_socket_tcp *t, struct pico_frame *f) |
daniele | 29:1a47b7151851 | 1942 | { |
TASS Belgium NV |
131:4758606c9316 | 1943 | struct pico_frame *cpy; |
TASS Belgium NV |
131:4758606c9316 | 1944 | if (f) { |
TASS Belgium NV |
131:4758606c9316 | 1945 | tcp_dbg("TCP> RETRANS (by dupack) frame %08x, len= %d\n", SEQN(f), f->payload_len); |
TASS Belgium NV |
131:4758606c9316 | 1946 | tcp_add_header(t, f); |
TASS Belgium NV |
131:4758606c9316 | 1947 | /* TCP: ENQUEUE to PROTO ( retransmit )*/ |
TASS Belgium NV |
131:4758606c9316 | 1948 | cpy = pico_frame_copy(f); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1949 | if (!cpy) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1950 | return -1; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1951 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 1952 | |
TASS Belgium NV |
131:4758606c9316 | 1953 | if (pico_enqueue(&tcp_out, cpy) > 0) { |
TASS Belgium NV |
131:4758606c9316 | 1954 | t->in_flight++; |
TASS Belgium NV |
131:4758606c9316 | 1955 | t->snd_last_out = SEQN(cpy); |
TASS Belgium NV |
131:4758606c9316 | 1956 | } else { |
TASS Belgium NV |
131:4758606c9316 | 1957 | pico_frame_discard(cpy); |
TASS Belgium NV |
131:4758606c9316 | 1958 | } |
TASS Belgium NV |
131:4758606c9316 | 1959 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 1960 | add_retransmission_timer(t, TCP_TIME + t->rto); |
TASS Belgium NV |
131:4758606c9316 | 1961 | return(f->payload_len); |
daniele | 29:1a47b7151851 | 1962 | } |
TASS Belgium NV |
131:4758606c9316 | 1963 | |
TASS Belgium NV |
131:4758606c9316 | 1964 | return 0; |
daniele | 29:1a47b7151851 | 1965 | } |
daniele | 29:1a47b7151851 | 1966 | |
daniele | 29:1a47b7151851 | 1967 | #ifdef TCP_ACK_DBG |
daniele | 29:1a47b7151851 | 1968 | static void tcp_ack_dbg(struct pico_socket *s, struct pico_frame *f) |
daniele | 29:1a47b7151851 | 1969 | { |
TASS Belgium NV |
131:4758606c9316 | 1970 | uint32_t una, nxt, ack, cur; |
TASS Belgium NV |
131:4758606c9316 | 1971 | struct pico_frame *una_f = NULL, *cur_f; |
TASS Belgium NV |
131:4758606c9316 | 1972 | struct pico_tree_node *idx; |
TASS Belgium NV |
131:4758606c9316 | 1973 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
TASS Belgium NV |
131:4758606c9316 | 1974 | char info[64]; |
TASS Belgium NV |
131:4758606c9316 | 1975 | char tmp[64]; |
TASS Belgium NV |
131:4758606c9316 | 1976 | ack = ACKN(f); |
TASS Belgium NV |
131:4758606c9316 | 1977 | nxt = t->snd_nxt; |
TASS Belgium NV |
131:4758606c9316 | 1978 | tcp_dbg("===================================\n"); |
TASS Belgium NV |
131:4758606c9316 | 1979 | tcp_dbg("Queue out (%d/%d). ACKED=%08x\n", t->tcpq_out.size, t->tcpq_out.max_size, ack); |
TASS Belgium NV |
131:4758606c9316 | 1980 | |
TASS Belgium NV |
131:4758606c9316 | 1981 | pico_tree_foreach(idx, &t->tcpq_out.pool) { |
TASS Belgium NV |
131:4758606c9316 | 1982 | info[0] = 0; |
TASS Belgium NV |
131:4758606c9316 | 1983 | cur_f = idx->keyValue; |
TASS Belgium NV |
131:4758606c9316 | 1984 | cur = SEQN(cur_f); |
TASS Belgium NV |
131:4758606c9316 | 1985 | if (!una_f) { |
TASS Belgium NV |
131:4758606c9316 | 1986 | una_f = cur_f; |
TASS Belgium NV |
131:4758606c9316 | 1987 | una = SEQN(una_f); |
TASS Belgium NV |
131:4758606c9316 | 1988 | } |
TASS Belgium NV |
131:4758606c9316 | 1989 | |
TASS Belgium NV |
131:4758606c9316 | 1990 | if (cur == nxt) { |
TASS Belgium NV |
131:4758606c9316 | 1991 | strncpy(tmp, info, strlen(info)); |
TASS Belgium NV |
131:4758606c9316 | 1992 | snprintf(info, 64, "%s SND_NXT", tmp); |
TASS Belgium NV |
131:4758606c9316 | 1993 | } |
TASS Belgium NV |
131:4758606c9316 | 1994 | |
TASS Belgium NV |
131:4758606c9316 | 1995 | if (cur == ack) { |
TASS Belgium NV |
131:4758606c9316 | 1996 | strncpy(tmp, info, strlen(info)); |
TASS Belgium NV |
131:4758606c9316 | 1997 | snprintf(info, 64, "%s ACK", tmp); |
TASS Belgium NV |
131:4758606c9316 | 1998 | } |
TASS Belgium NV |
131:4758606c9316 | 1999 | |
TASS Belgium NV |
131:4758606c9316 | 2000 | if (cur == una) { |
TASS Belgium NV |
131:4758606c9316 | 2001 | strncpy(tmp, info, strlen(info)); |
TASS Belgium NV |
131:4758606c9316 | 2002 | snprintf(info, 64, "%s SND_UNA", tmp); |
TASS Belgium NV |
131:4758606c9316 | 2003 | } |
TASS Belgium NV |
131:4758606c9316 | 2004 | |
TASS Belgium NV |
131:4758606c9316 | 2005 | if (cur == t->snd_last) { |
TASS Belgium NV |
131:4758606c9316 | 2006 | strncpy(tmp, info, strlen(info)); |
TASS Belgium NV |
131:4758606c9316 | 2007 | snprintf(info, 64, "%s SND_LAST", tmp); |
TASS Belgium NV |
131:4758606c9316 | 2008 | } |
TASS Belgium NV |
131:4758606c9316 | 2009 | |
TASS Belgium NV |
131:4758606c9316 | 2010 | tcp_dbg("%08x %d%s\n", cur, cur_f->payload_len, info); |
TASS Belgium NV |
131:4758606c9316 | 2011 | |
daniele | 29:1a47b7151851 | 2012 | } |
TASS Belgium NV |
131:4758606c9316 | 2013 | tcp_dbg("SND_NXT is %08x, snd_LAST is %08x\n", nxt, t->snd_last); |
TASS Belgium NV |
131:4758606c9316 | 2014 | tcp_dbg("===================================\n"); |
TASS Belgium NV |
131:4758606c9316 | 2015 | tcp_dbg("\n\n"); |
daniele | 29:1a47b7151851 | 2016 | } |
daniele | 29:1a47b7151851 | 2017 | #endif |
daniele | 29:1a47b7151851 | 2018 | |
daniele | 29:1a47b7151851 | 2019 | static int tcp_ack(struct pico_socket *s, struct pico_frame *f) |
daniele | 29:1a47b7151851 | 2020 | { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2021 | struct pico_frame *f_new; /* use with Nagle to push to out queue */ |
TASS Belgium NV |
131:4758606c9316 | 2022 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
TASS Belgium NV |
131:4758606c9316 | 2023 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 2024 | uint32_t rtt = 0; |
TASS Belgium NV |
131:4758606c9316 | 2025 | uint16_t acked = 0; |
TASS Belgium NV |
131:4758606c9316 | 2026 | pico_time acked_timestamp = 0; |
TASS Belgium NV |
131:4758606c9316 | 2027 | |
TASS Belgium NV |
131:4758606c9316 | 2028 | struct pico_frame *una = NULL; |
TASS Belgium NV |
131:4758606c9316 | 2029 | if ((hdr->flags & PICO_TCP_ACK) == 0) |
TASS Belgium NV |
131:4758606c9316 | 2030 | return -1; |
daniele | 29:1a47b7151851 | 2031 | |
daniele | 29:1a47b7151851 | 2032 | #ifdef TCP_ACK_DBG |
TASS Belgium NV |
131:4758606c9316 | 2033 | tcp_ack_dbg(s, f); |
daniele | 29:1a47b7151851 | 2034 | #endif |
daniele | 29:1a47b7151851 | 2035 | |
TASS Belgium NV |
131:4758606c9316 | 2036 | tcp_parse_options(f); |
TASS Belgium NV |
131:4758606c9316 | 2037 | t->recv_wnd = short_be(hdr->rwnd); |
TASS Belgium NV |
131:4758606c9316 | 2038 | |
TASS Belgium NV |
131:4758606c9316 | 2039 | acked = (uint16_t)tcp_ack_advance_una(t, f, &acked_timestamp); |
TASS Belgium NV |
131:4758606c9316 | 2040 | una = first_segment(&t->tcpq_out); |
tass | 152:a3d286bf94e5 | 2041 | t->ack_timestamp = TCP_TIME; |
TASS Belgium NV |
131:4758606c9316 | 2042 | |
TASS Belgium NV |
131:4758606c9316 | 2043 | if ((t->x_mode == PICO_TCP_BLACKOUT) || |
TASS Belgium NV |
131:4758606c9316 | 2044 | ((t->x_mode == PICO_TCP_WINDOW_FULL) && ((t->recv_wnd << t->recv_wnd_scale) > t->mss))) { |
TASS Belgium NV |
131:4758606c9316 | 2045 | int prev_mode = t->x_mode; |
TASS Belgium NV |
131:4758606c9316 | 2046 | tcp_dbg("Re-entering look-ahead...\n\n\n"); |
TASS Belgium NV |
131:4758606c9316 | 2047 | t->x_mode = PICO_TCP_LOOKAHEAD; |
TASS Belgium NV |
131:4758606c9316 | 2048 | t->backoff = 0; |
TASS Belgium NV |
131:4758606c9316 | 2049 | |
TASS Belgium NV |
131:4758606c9316 | 2050 | if((prev_mode == PICO_TCP_BLACKOUT) && (acked > 0) && una) |
TASS Belgium NV |
131:4758606c9316 | 2051 | { |
TASS Belgium NV |
131:4758606c9316 | 2052 | t->snd_nxt = SEQN(una); |
TASS Belgium NV |
131:4758606c9316 | 2053 | /* restart the retrans timer */ |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2054 | if (t->retrans_tmr) { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2055 | t->retrans_tmr_due = 0ull; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2056 | } |
TASS Belgium NV |
131:4758606c9316 | 2057 | } |
daniele | 29:1a47b7151851 | 2058 | } |
daniele | 29:1a47b7151851 | 2059 | |
TASS Belgium NV |
131:4758606c9316 | 2060 | /* One should be acked. */ |
TASS Belgium NV |
131:4758606c9316 | 2061 | if ((acked == 0) && (f->payload_len == 0) && (t->in_flight > 0)) |
TASS Belgium NV |
131:4758606c9316 | 2062 | t->in_flight--; |
TASS Belgium NV |
131:4758606c9316 | 2063 | |
TASS Belgium NV |
131:4758606c9316 | 2064 | if (!una || acked > 0) { |
TASS Belgium NV |
131:4758606c9316 | 2065 | t->x_mode = PICO_TCP_LOOKAHEAD; |
TASS Belgium NV |
131:4758606c9316 | 2066 | tcp_dbg("Mode: Look-ahead. In flight: %d/%d buf: %d\n", t->in_flight, t->cwnd, t->tcpq_out.frames); |
TASS Belgium NV |
131:4758606c9316 | 2067 | t->backoff = 0; |
TASS Belgium NV |
131:4758606c9316 | 2068 | |
TASS Belgium NV |
131:4758606c9316 | 2069 | /* Do rtt/rttvar/rto calculations */ |
TASS Belgium NV |
131:4758606c9316 | 2070 | /* First, try with timestamps, using the value from options */ |
TASS Belgium NV |
131:4758606c9316 | 2071 | if(f && (f->timestamp != 0)) { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2072 | rtt = time_diff(TCP_TIME, f->timestamp); |
TASS Belgium NV |
131:4758606c9316 | 2073 | if (rtt) |
TASS Belgium NV |
131:4758606c9316 | 2074 | tcp_rtt(t, rtt); |
TASS Belgium NV |
131:4758606c9316 | 2075 | } else if(acked_timestamp) { |
tass | 152:a3d286bf94e5 | 2076 | /* If no timestamps are there, use conservative estimation on the una */ |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2077 | rtt = time_diff(TCP_TIME, acked_timestamp); |
TASS Belgium NV |
131:4758606c9316 | 2078 | if (rtt) |
TASS Belgium NV |
131:4758606c9316 | 2079 | tcp_rtt(t, rtt); |
daniele | 29:1a47b7151851 | 2080 | } |
TASS Belgium NV |
131:4758606c9316 | 2081 | |
TASS Belgium NV |
131:4758606c9316 | 2082 | tcp_dbg("TCP ACK> FRESH ACK %08x (acked %d) Queue size: %u/%u frames: %u cwnd: %u in_flight: %u snd_una: %u\n", ACKN(f), acked, t->tcpq_out.size, t->tcpq_out.max_size, t->tcpq_out.frames, t->cwnd, t->in_flight, SEQN(una)); |
TASS Belgium NV |
131:4758606c9316 | 2083 | if (acked > t->in_flight) { |
TASS Belgium NV |
131:4758606c9316 | 2084 | tcp_dbg("WARNING: in flight < 0\n"); |
TASS Belgium NV |
131:4758606c9316 | 2085 | t->in_flight = 0; |
TASS Belgium NV |
131:4758606c9316 | 2086 | } else |
TASS Belgium NV |
131:4758606c9316 | 2087 | t->in_flight -= (acked); |
TASS Belgium NV |
131:4758606c9316 | 2088 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2089 | } else if ((t->snd_old_ack == ACKN(f)) && /* We've just seen this ack, and... */ |
TASS Belgium NV |
131:4758606c9316 | 2090 | ((0 == (hdr->flags & (PICO_TCP_PSH | PICO_TCP_SYN))) && |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2091 | (f->payload_len == 0)) && /* This is a pure ack, and... */ |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2092 | (ACKN(f) != t->snd_nxt)) /* There is something in flight awaiting to be acked... */ |
TASS Belgium NV |
131:4758606c9316 | 2093 | { |
TASS Belgium NV |
131:4758606c9316 | 2094 | /* Process incoming duplicate ack. */ |
TASS Belgium NV |
131:4758606c9316 | 2095 | if (t->x_mode < PICO_TCP_RECOVER) { |
TASS Belgium NV |
131:4758606c9316 | 2096 | t->x_mode++; |
TASS Belgium NV |
131:4758606c9316 | 2097 | tcp_dbg("Mode: DUPACK %d, due to PURE ACK %0x, len = %d\n", t->x_mode, SEQN(f), f->payload_len); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2098 | /* tcp_dbg("ACK: %x - QUEUE: %x\n", ACKN(f), SEQN(first_segment(&t->tcpq_out))); */ |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2099 | if (t->x_mode == PICO_TCP_RECOVER) { /* Switching mode */ |
tass | 152:a3d286bf94e5 | 2100 | if (t->in_flight > PICO_TCP_IW) |
tass | 152:a3d286bf94e5 | 2101 | t->cwnd = (uint16_t)t->in_flight; |
tass | 152:a3d286bf94e5 | 2102 | else |
tass | 152:a3d286bf94e5 | 2103 | t->cwnd = PICO_TCP_IW; |
tass | 152:a3d286bf94e5 | 2104 | |
TASS Belgium NV |
131:4758606c9316 | 2105 | t->snd_retry = SEQN((struct pico_frame *)first_segment(&t->tcpq_out)); |
TASS Belgium NV |
131:4758606c9316 | 2106 | if (t->ssthresh > t->cwnd) |
TASS Belgium NV |
131:4758606c9316 | 2107 | t->ssthresh >>= 2; |
TASS Belgium NV |
131:4758606c9316 | 2108 | else |
TASS Belgium NV |
131:4758606c9316 | 2109 | t->ssthresh = (t->cwnd >> 1); |
TASS Belgium NV |
131:4758606c9316 | 2110 | |
TASS Belgium NV |
131:4758606c9316 | 2111 | if (t->ssthresh < 2) |
TASS Belgium NV |
131:4758606c9316 | 2112 | t->ssthresh = 2; |
TASS Belgium NV |
131:4758606c9316 | 2113 | } |
TASS Belgium NV |
131:4758606c9316 | 2114 | } else if (t->x_mode == PICO_TCP_RECOVER) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2115 | /* tcp_dbg("TCP RECOVER> DUPACK! snd_una: %08x, snd_nxt: %08x, acked now: %08x\n", SEQN(first_segment(&t->tcpq_out)), t->snd_nxt, ACKN(f)); */ |
TASS Belgium NV |
131:4758606c9316 | 2116 | if (t->in_flight <= t->cwnd) { |
TASS Belgium NV |
131:4758606c9316 | 2117 | struct pico_frame *nxt = peek_segment(&t->tcpq_out, t->snd_retry); |
TASS Belgium NV |
131:4758606c9316 | 2118 | if (!nxt) |
TASS Belgium NV |
131:4758606c9316 | 2119 | nxt = first_segment(&t->tcpq_out); |
TASS Belgium NV |
131:4758606c9316 | 2120 | |
TASS Belgium NV |
131:4758606c9316 | 2121 | while (nxt && (nxt->flags & PICO_FRAME_FLAG_SACKED) && (nxt != first_segment(&t->tcpq_out))) { |
TASS Belgium NV |
131:4758606c9316 | 2122 | tcp_dbg("Skipping %08x because it is sacked.\n", SEQN(nxt)); |
TASS Belgium NV |
131:4758606c9316 | 2123 | nxt = next_segment(&t->tcpq_out, nxt); |
TASS Belgium NV |
131:4758606c9316 | 2124 | } |
tass | 152:a3d286bf94e5 | 2125 | if (nxt && (pico_seq_compare(SEQN(nxt), t->snd_nxt)) > 0) |
TASS Belgium NV |
131:4758606c9316 | 2126 | nxt = NULL; |
TASS Belgium NV |
131:4758606c9316 | 2127 | |
tass | 152:a3d286bf94e5 | 2128 | if (nxt && (pico_seq_compare(SEQN(nxt), SEQN((struct pico_frame *)first_segment(&t->tcpq_out))) > (int)(t->recv_wnd << t->recv_wnd_scale))) |
TASS Belgium NV |
131:4758606c9316 | 2129 | nxt = NULL; |
TASS Belgium NV |
131:4758606c9316 | 2130 | |
TASS Belgium NV |
131:4758606c9316 | 2131 | if(!nxt) |
TASS Belgium NV |
131:4758606c9316 | 2132 | nxt = first_segment(&t->tcpq_out); |
TASS Belgium NV |
131:4758606c9316 | 2133 | |
TASS Belgium NV |
131:4758606c9316 | 2134 | if (nxt) { |
TASS Belgium NV |
131:4758606c9316 | 2135 | tcp_retrans(t, peek_segment(&t->tcpq_out, t->snd_retry)); |
TASS Belgium NV |
131:4758606c9316 | 2136 | t->snd_retry = SEQN(nxt); |
TASS Belgium NV |
131:4758606c9316 | 2137 | } |
TASS Belgium NV |
131:4758606c9316 | 2138 | } |
TASS Belgium NV |
131:4758606c9316 | 2139 | |
TASS Belgium NV |
131:4758606c9316 | 2140 | if (++t->cwnd_counter > 1) { |
TASS Belgium NV |
131:4758606c9316 | 2141 | t->cwnd--; |
TASS Belgium NV |
131:4758606c9316 | 2142 | if (t->cwnd < 2) |
TASS Belgium NV |
131:4758606c9316 | 2143 | t->cwnd = 2; |
TASS Belgium NV |
131:4758606c9316 | 2144 | |
TASS Belgium NV |
131:4758606c9316 | 2145 | t->cwnd_counter = 0; |
TASS Belgium NV |
131:4758606c9316 | 2146 | } |
TASS Belgium NV |
131:4758606c9316 | 2147 | } else { |
TASS Belgium NV |
131:4758606c9316 | 2148 | tcp_dbg("DUPACK in mode %d \n", t->x_mode); |
TASS Belgium NV |
131:4758606c9316 | 2149 | |
TASS Belgium NV |
131:4758606c9316 | 2150 | } |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2151 | } /* End case duplicate ack detection */ |
TASS Belgium NV |
131:4758606c9316 | 2152 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2153 | /* Linux very special zero-window probe detection (see bug #107) */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2154 | if ((0 == (hdr->flags & (PICO_TCP_PSH | PICO_TCP_SYN))) && /* This is a pure ack, and... */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2155 | (ACKN(f) == t->snd_nxt) && /* it's acking our snd_nxt, and... */ |
tass | 152:a3d286bf94e5 | 2156 | (pico_seq_compare(SEQN(f), t->rcv_nxt) < 0)) /* Has an old seq number */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2157 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2158 | tcp_send_ack(t); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2159 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2160 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2161 | |
TASS Belgium NV |
131:4758606c9316 | 2162 | /* Do congestion control */ |
TASS Belgium NV |
131:4758606c9316 | 2163 | tcp_congestion_control(t); |
TASS Belgium NV |
131:4758606c9316 | 2164 | if ((acked > 0) && t->sock.wakeup) { |
TASS Belgium NV |
131:4758606c9316 | 2165 | if (t->tcpq_out.size < t->tcpq_out.max_size) |
TASS Belgium NV |
131:4758606c9316 | 2166 | t->sock.wakeup(PICO_SOCK_EV_WR, &(t->sock)); |
TASS Belgium NV |
131:4758606c9316 | 2167 | |
TASS Belgium NV |
131:4758606c9316 | 2168 | /* t->sock.ev_pending |= PICO_SOCK_EV_WR; */ |
daniele | 29:1a47b7151851 | 2169 | } |
TASS Belgium NV |
131:4758606c9316 | 2170 | |
TASS Belgium NV |
131:4758606c9316 | 2171 | /* if Nagle enabled, check if no unack'ed data and fill out queue (till window) */ |
TASS Belgium NV |
131:4758606c9316 | 2172 | if (IS_NAGLE_ENABLED((&(t->sock)))) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2173 | while (!IS_TCP_HOLDQ_EMPTY(t) && ((t->tcpq_out.max_size - t->tcpq_out.size) >= t->mss)) { |
TASS Belgium NV |
131:4758606c9316 | 2174 | tcp_dbg_nagle("TCP_ACK - NAGLE add new segment\n"); |
TASS Belgium NV |
131:4758606c9316 | 2175 | f_new = pico_hold_segment_make(t); |
TASS Belgium NV |
131:4758606c9316 | 2176 | if (f_new == NULL) |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2177 | break; /* XXX corrupt !!! (or no memory) */ |
TASS Belgium NV |
131:4758606c9316 | 2178 | |
TASS Belgium NV |
131:4758606c9316 | 2179 | if (pico_enqueue_segment(&t->tcpq_out, f_new) <= 0) |
TASS Belgium NV |
131:4758606c9316 | 2180 | /* handle error */ |
TASS Belgium NV |
131:4758606c9316 | 2181 | tcp_dbg_nagle("TCP_ACK - NAGLE FAILED to enqueue in out\n"); |
TASS Belgium NV |
131:4758606c9316 | 2182 | } |
daniele | 29:1a47b7151851 | 2183 | } |
TASS Belgium NV |
131:4758606c9316 | 2184 | |
TASS Belgium NV |
131:4758606c9316 | 2185 | /* If some space was created, put a few segments out. */ |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2186 | tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", TCP_TIME, t->cwnd, t->ssthresh, t->in_flight); |
TASS Belgium NV |
131:4758606c9316 | 2187 | if (t->x_mode == PICO_TCP_LOOKAHEAD) { |
TASS Belgium NV |
131:4758606c9316 | 2188 | if ((t->cwnd >= t->in_flight) && (t->snd_nxt > t->snd_last_out)) { |
TASS Belgium NV |
131:4758606c9316 | 2189 | pico_tcp_output(&t->sock, (int)t->cwnd - (int)t->in_flight); |
TASS Belgium NV |
131:4758606c9316 | 2190 | } |
daniele | 29:1a47b7151851 | 2191 | } |
TASS Belgium NV |
131:4758606c9316 | 2192 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2193 | add_retransmission_timer(t, 0); |
TASS Belgium NV |
131:4758606c9316 | 2194 | t->snd_old_ack = ACKN(f); |
TASS Belgium NV |
131:4758606c9316 | 2195 | return 0; |
daniele | 29:1a47b7151851 | 2196 | } |
daniele | 29:1a47b7151851 | 2197 | |
daniele | 29:1a47b7151851 | 2198 | static int tcp_finwaitack(struct pico_socket *s, struct pico_frame *f) |
daniele | 29:1a47b7151851 | 2199 | { |
tass | 152:a3d286bf94e5 | 2200 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
TASS Belgium NV |
131:4758606c9316 | 2201 | tcp_dbg("RECEIVED ACK IN FIN_WAIT1\n"); |
TASS Belgium NV |
131:4758606c9316 | 2202 | |
TASS Belgium NV |
131:4758606c9316 | 2203 | /* acking part */ |
TASS Belgium NV |
131:4758606c9316 | 2204 | tcp_ack(s, f); |
tass | 152:a3d286bf94e5 | 2205 | |
tass | 152:a3d286bf94e5 | 2206 | |
tass | 152:a3d286bf94e5 | 2207 | tcp_dbg("FIN_WAIT1: ack is %08x - snd_nxt is %08x\n", ACKN(f), t->snd_nxt); |
tass | 152:a3d286bf94e5 | 2208 | if (ACKN(f) == (t->snd_nxt - 1u)) { |
tass | 152:a3d286bf94e5 | 2209 | /* update TCP state */ |
tass | 152:a3d286bf94e5 | 2210 | s->state &= 0x00FFU; |
tass | 152:a3d286bf94e5 | 2211 | s->state |= PICO_SOCKET_STATE_TCP_FIN_WAIT2; |
tass | 152:a3d286bf94e5 | 2212 | tcp_dbg("TCP> IN STATE FIN_WAIT2\n"); |
tass | 152:a3d286bf94e5 | 2213 | } |
TASS Belgium NV |
131:4758606c9316 | 2214 | return 0; |
daniele | 29:1a47b7151851 | 2215 | } |
daniele | 29:1a47b7151851 | 2216 | |
tass | 128:ae39e6e81531 | 2217 | static void tcp_deltcb(pico_time when, void *arg) |
daniele | 29:1a47b7151851 | 2218 | { |
TASS Belgium NV |
131:4758606c9316 | 2219 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg; |
TASS Belgium NV |
131:4758606c9316 | 2220 | IGNORE_PARAMETER(when); |
TASS Belgium NV |
131:4758606c9316 | 2221 | |
tass | 152:a3d286bf94e5 | 2222 | /* send RST if not yet in TIME_WAIT */ |
tass | 152:a3d286bf94e5 | 2223 | if ( (((t->sock).state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_TIME_WAIT) |
tass | 152:a3d286bf94e5 | 2224 | && (((t->sock).state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_CLOSING) ) { |
tass | 152:a3d286bf94e5 | 2225 | tcp_dbg("Called deltcb in state = %04x (sending reset!)\n", (t->sock).state); |
tass | 152:a3d286bf94e5 | 2226 | tcp_do_send_rst(&t->sock, long_be(t->snd_nxt)); |
TASS Belgium NV |
131:4758606c9316 | 2227 | } else { |
tass | 152:a3d286bf94e5 | 2228 | tcp_dbg("Called deltcb in state = %04x\n", (t->sock).state); |
TASS Belgium NV |
131:4758606c9316 | 2229 | } |
tass | 152:a3d286bf94e5 | 2230 | |
tass | 152:a3d286bf94e5 | 2231 | /* update state */ |
tass | 152:a3d286bf94e5 | 2232 | (t->sock).state &= 0x00FFU; |
tass | 152:a3d286bf94e5 | 2233 | (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED; |
tass | 152:a3d286bf94e5 | 2234 | (t->sock).state &= 0xFF00U; |
tass | 152:a3d286bf94e5 | 2235 | (t->sock).state |= PICO_SOCKET_STATE_CLOSED; |
tass | 152:a3d286bf94e5 | 2236 | /* call EV_FIN wakeup before deleting */ |
tass | 152:a3d286bf94e5 | 2237 | if (t->sock.wakeup) { |
tass | 152:a3d286bf94e5 | 2238 | (t->sock).wakeup(PICO_SOCK_EV_FIN, &(t->sock)); |
tass | 152:a3d286bf94e5 | 2239 | } |
tass | 152:a3d286bf94e5 | 2240 | |
tass | 152:a3d286bf94e5 | 2241 | /* delete socket */ |
tass | 152:a3d286bf94e5 | 2242 | pico_socket_del(&t->sock); |
TASS Belgium NV |
131:4758606c9316 | 2243 | } |
TASS Belgium NV |
131:4758606c9316 | 2244 | |
TASS Belgium NV |
131:4758606c9316 | 2245 | static int tcp_finwaitfin(struct pico_socket *s, struct pico_frame *f) |
TASS Belgium NV |
131:4758606c9316 | 2246 | { |
TASS Belgium NV |
131:4758606c9316 | 2247 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
TASS Belgium NV |
131:4758606c9316 | 2248 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) (f->transport_hdr); |
TASS Belgium NV |
131:4758606c9316 | 2249 | tcp_dbg("TCP> received fin in FIN_WAIT2\n"); |
TASS Belgium NV |
131:4758606c9316 | 2250 | /* received FIN, increase ACK nr */ |
TASS Belgium NV |
131:4758606c9316 | 2251 | t->rcv_nxt = long_be(hdr->seq) + 1; |
TASS Belgium NV |
131:4758606c9316 | 2252 | s->state &= 0x00FFU; |
TASS Belgium NV |
131:4758606c9316 | 2253 | s->state |= PICO_SOCKET_STATE_TCP_TIME_WAIT; |
TASS Belgium NV |
131:4758606c9316 | 2254 | /* set SHUT_REMOTE */ |
TASS Belgium NV |
131:4758606c9316 | 2255 | s->state |= PICO_SOCKET_STATE_SHUT_REMOTE; |
TASS Belgium NV |
131:4758606c9316 | 2256 | if (s->wakeup) |
TASS Belgium NV |
131:4758606c9316 | 2257 | s->wakeup(PICO_SOCK_EV_CLOSE, s); |
TASS Belgium NV |
131:4758606c9316 | 2258 | |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2259 | if (f->payload_len > 0) /* needed?? */ |
TASS Belgium NV |
131:4758606c9316 | 2260 | tcp_data_in(s, f); |
TASS Belgium NV |
131:4758606c9316 | 2261 | |
TASS Belgium NV |
131:4758606c9316 | 2262 | /* send ACK */ |
TASS Belgium NV |
131:4758606c9316 | 2263 | tcp_send_ack(t); |
tass | 152:a3d286bf94e5 | 2264 | /* linger */ |
tass | 152:a3d286bf94e5 | 2265 | tcp_linger(t); |
TASS Belgium NV |
131:4758606c9316 | 2266 | return 0; |
TASS Belgium NV |
131:4758606c9316 | 2267 | } |
TASS Belgium NV |
131:4758606c9316 | 2268 | |
tass | 152:a3d286bf94e5 | 2269 | static int tcp_closing_ack(struct pico_socket *s, struct pico_frame *f) |
TASS Belgium NV |
131:4758606c9316 | 2270 | { |
TASS Belgium NV |
131:4758606c9316 | 2271 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
TASS Belgium NV |
131:4758606c9316 | 2272 | tcp_dbg("TCP> received ack in CLOSING\n"); |
TASS Belgium NV |
131:4758606c9316 | 2273 | /* acking part */ |
TASS Belgium NV |
131:4758606c9316 | 2274 | tcp_ack(s, f); |
tass | 152:a3d286bf94e5 | 2275 | |
tass | 152:a3d286bf94e5 | 2276 | /* update TCP state DLA TODO: Only if FIN is acked! */ |
tass | 152:a3d286bf94e5 | 2277 | tcp_dbg("CLOSING: ack is %08x - snd_nxt is %08x\n", ACKN(f), t->snd_nxt); |
tass | 152:a3d286bf94e5 | 2278 | if (ACKN(f) == t->snd_nxt) { |
tass | 152:a3d286bf94e5 | 2279 | s->state &= 0x00FFU; |
tass | 152:a3d286bf94e5 | 2280 | s->state |= PICO_SOCKET_STATE_TCP_TIME_WAIT; |
tass | 152:a3d286bf94e5 | 2281 | /* set timer */ |
tass | 152:a3d286bf94e5 | 2282 | tcp_linger(t); |
tass | 152:a3d286bf94e5 | 2283 | } |
TASS Belgium NV |
131:4758606c9316 | 2284 | return 0; |
TASS Belgium NV |
131:4758606c9316 | 2285 | } |
TASS Belgium NV |
131:4758606c9316 | 2286 | |
TASS Belgium NV |
131:4758606c9316 | 2287 | static int tcp_lastackwait(struct pico_socket *s, struct pico_frame *f) |
TASS Belgium NV |
131:4758606c9316 | 2288 | { |
tass | 152:a3d286bf94e5 | 2289 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
tass | 152:a3d286bf94e5 | 2290 | tcp_dbg("LAST_ACK: ack is %08x - snd_nxt is %08x\n", ACKN(f), t->snd_nxt); |
tass | 152:a3d286bf94e5 | 2291 | if (ACKN(f) == t->snd_nxt) { |
tass | 152:a3d286bf94e5 | 2292 | s->state &= 0x00FFU; |
tass | 152:a3d286bf94e5 | 2293 | s->state |= PICO_SOCKET_STATE_TCP_CLOSED; |
tass | 152:a3d286bf94e5 | 2294 | s->state &= 0xFF00U; |
tass | 152:a3d286bf94e5 | 2295 | s->state |= PICO_SOCKET_STATE_CLOSED; |
tass | 152:a3d286bf94e5 | 2296 | /* call socket wakeup with EV_FIN */ |
tass | 152:a3d286bf94e5 | 2297 | if (s->wakeup) |
tass | 152:a3d286bf94e5 | 2298 | s->wakeup(PICO_SOCK_EV_FIN, s); |
tass | 152:a3d286bf94e5 | 2299 | |
tass | 152:a3d286bf94e5 | 2300 | /* delete socket */ |
tass | 152:a3d286bf94e5 | 2301 | pico_socket_del(s); |
tass | 152:a3d286bf94e5 | 2302 | } |
TASS Belgium NV |
131:4758606c9316 | 2303 | return 0; |
TASS Belgium NV |
131:4758606c9316 | 2304 | } |
TASS Belgium NV |
131:4758606c9316 | 2305 | |
TASS Belgium NV |
131:4758606c9316 | 2306 | static int tcp_syn(struct pico_socket *s, struct pico_frame *f) |
TASS Belgium NV |
131:4758606c9316 | 2307 | { |
TASS Belgium NV |
131:4758606c9316 | 2308 | struct pico_socket_tcp *new = NULL; |
TASS Belgium NV |
131:4758606c9316 | 2309 | struct pico_tcp_hdr *hdr = NULL; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2310 | uint16_t mtu; |
TASS Belgium NV |
131:4758606c9316 | 2311 | if(s->number_of_pending_conn >= s->max_backlog) |
TASS Belgium NV |
131:4758606c9316 | 2312 | return -1; |
TASS Belgium NV |
131:4758606c9316 | 2313 | |
TASS Belgium NV |
131:4758606c9316 | 2314 | new = (struct pico_socket_tcp *)pico_socket_clone(s); |
TASS Belgium NV |
131:4758606c9316 | 2315 | hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 2316 | if (!new) |
TASS Belgium NV |
131:4758606c9316 | 2317 | return -1; |
TASS Belgium NV |
131:4758606c9316 | 2318 | |
TASS Belgium NV |
131:4758606c9316 | 2319 | #ifdef PICO_TCP_SUPPORT_SOCKET_STATS |
TASS Belgium NV |
131:4758606c9316 | 2320 | pico_timer_add(2000, sock_stats, s); |
TASS Belgium NV |
131:4758606c9316 | 2321 | #endif |
TASS Belgium NV |
131:4758606c9316 | 2322 | |
TASS Belgium NV |
131:4758606c9316 | 2323 | new->sock.remote_port = ((struct pico_trans *)f->transport_hdr)->sport; |
TASS Belgium NV |
131:4758606c9316 | 2324 | #ifdef PICO_SUPPORT_IPV4 |
TASS Belgium NV |
131:4758606c9316 | 2325 | if (IS_IPV4(f)) { |
TASS Belgium NV |
131:4758606c9316 | 2326 | new->sock.remote_addr.ip4.addr = ((struct pico_ipv4_hdr *)(f->net_hdr))->src.addr; |
TASS Belgium NV |
131:4758606c9316 | 2327 | new->sock.local_addr.ip4.addr = ((struct pico_ipv4_hdr *)(f->net_hdr))->dst.addr; |
TASS Belgium NV |
131:4758606c9316 | 2328 | } |
TASS Belgium NV |
131:4758606c9316 | 2329 | |
TASS Belgium NV |
131:4758606c9316 | 2330 | #endif |
TASS Belgium NV |
131:4758606c9316 | 2331 | #ifdef PICO_SUPPORT_IPV6 |
TASS Belgium NV |
131:4758606c9316 | 2332 | if (IS_IPV6(f)) { |
TASS Belgium NV |
131:4758606c9316 | 2333 | new->sock.remote_addr.ip6 = ((struct pico_ipv6_hdr *)(f->net_hdr))->src; |
TASS Belgium NV |
131:4758606c9316 | 2334 | new->sock.local_addr.ip6 = ((struct pico_ipv6_hdr *)(f->net_hdr))->dst; |
TASS Belgium NV |
131:4758606c9316 | 2335 | } |
TASS Belgium NV |
131:4758606c9316 | 2336 | |
TASS Belgium NV |
131:4758606c9316 | 2337 | #endif |
TASS Belgium NV |
131:4758606c9316 | 2338 | f->sock = &new->sock; |
TASS Belgium NV |
131:4758606c9316 | 2339 | tcp_parse_options(f); |
tass | 152:a3d286bf94e5 | 2340 | mtu = (uint16_t)pico_socket_get_mss(&new->sock); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2341 | new->mss = (uint16_t)(mtu - PICO_SIZE_TCPHDR); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2342 | new->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2343 | new->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2344 | new->tcpq_hold.max_size = 2u * mtu; |
TASS Belgium NV |
131:4758606c9316 | 2345 | new->rcv_nxt = long_be(hdr->seq) + 1; |
TASS Belgium NV |
131:4758606c9316 | 2346 | new->snd_nxt = long_be(pico_paws()); |
TASS Belgium NV |
131:4758606c9316 | 2347 | new->snd_last = new->snd_nxt; |
TASS Belgium NV |
131:4758606c9316 | 2348 | new->cwnd = PICO_TCP_IW; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2349 | new->ssthresh = (uint16_t)((uint16_t)(PICO_DEFAULT_SOCKETQ / new->mss) - (((uint16_t)(PICO_DEFAULT_SOCKETQ / new->mss)) >> 3u)); |
TASS Belgium NV |
131:4758606c9316 | 2350 | new->recv_wnd = short_be(hdr->rwnd); |
TASS Belgium NV |
131:4758606c9316 | 2351 | new->jumbo = hdr->len & 0x07; |
tass | 152:a3d286bf94e5 | 2352 | new->linger_timeout = PICO_SOCKET_LINGER_TIMEOUT; |
TASS Belgium NV |
131:4758606c9316 | 2353 | s->number_of_pending_conn++; |
TASS Belgium NV |
131:4758606c9316 | 2354 | new->sock.parent = s; |
TASS Belgium NV |
131:4758606c9316 | 2355 | new->sock.wakeup = s->wakeup; |
tass | 152:a3d286bf94e5 | 2356 | rto_set(new, PICO_TCP_RTO_MIN); |
TASS Belgium NV |
131:4758606c9316 | 2357 | /* Initialize timestamp values */ |
TASS Belgium NV |
131:4758606c9316 | 2358 | new->sock.state = PICO_SOCKET_STATE_BOUND | PICO_SOCKET_STATE_CONNECTED | PICO_SOCKET_STATE_TCP_SYN_RECV; |
TASS Belgium NV |
131:4758606c9316 | 2359 | pico_socket_add(&new->sock); |
TASS Belgium NV |
131:4758606c9316 | 2360 | tcp_send_synack(&new->sock); |
TASS Belgium NV |
131:4758606c9316 | 2361 | tcp_dbg("SYNACK sent, socket added. snd_nxt is %08x\n", new->snd_nxt); |
TASS Belgium NV |
131:4758606c9316 | 2362 | return 0; |
TASS Belgium NV |
131:4758606c9316 | 2363 | } |
TASS Belgium NV |
131:4758606c9316 | 2364 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2365 | static int tcp_synrecv_syn(struct pico_socket *s, struct pico_frame *f) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2366 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2367 | struct pico_tcp_hdr *hdr = NULL; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2368 | struct pico_socket_tcp *t = TCP_SOCK(s); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2369 | hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
tass | 152:a3d286bf94e5 | 2370 | if (t->rcv_nxt == long_be(hdr->seq) + 1u) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2371 | /* take back our own SEQ number to its original value, |
tass | 152:a3d286bf94e5 | 2372 | * so the synack retransmitted is identical to the original. |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2373 | */ |
tass | 152:a3d286bf94e5 | 2374 | t->snd_nxt--; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2375 | tcp_send_synack(s); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2376 | } else { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2377 | tcp_send_rst(s, f); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2378 | return -1; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2379 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2380 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2381 | return 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2382 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2383 | |
TASS Belgium NV |
131:4758606c9316 | 2384 | static void tcp_set_init_point(struct pico_socket *s) |
TASS Belgium NV |
131:4758606c9316 | 2385 | { |
TASS Belgium NV |
131:4758606c9316 | 2386 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
TASS Belgium NV |
131:4758606c9316 | 2387 | t->rcv_processed = t->rcv_nxt; |
TASS Belgium NV |
131:4758606c9316 | 2388 | } |
TASS Belgium NV |
131:4758606c9316 | 2389 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2390 | |
tass | 152:a3d286bf94e5 | 2391 | uint16_t pico_tcp_get_socket_mss(struct pico_socket *s) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2392 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2393 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2394 | if (t->mss > 0) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2395 | return (uint16_t)(t->mss + PICO_SIZE_TCPHDR); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2396 | else |
tass | 152:a3d286bf94e5 | 2397 | return (uint16_t)pico_socket_get_mss(s); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2398 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2399 | |
TASS Belgium NV |
131:4758606c9316 | 2400 | static int tcp_synack(struct pico_socket *s, struct pico_frame *f) |
TASS Belgium NV |
131:4758606c9316 | 2401 | { |
TASS Belgium NV |
131:4758606c9316 | 2402 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
TASS Belgium NV |
131:4758606c9316 | 2403 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 2404 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2405 | if (ACKN(f) == (1u + t->snd_nxt)) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2406 | /* Get rid of initconn retry */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2407 | if(t->retrans_tmr) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2408 | pico_timer_cancel(t->retrans_tmr); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2409 | t->retrans_tmr = NULL; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2410 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2411 | |
TASS Belgium NV |
131:4758606c9316 | 2412 | t->rcv_nxt = long_be(hdr->seq); |
TASS Belgium NV |
131:4758606c9316 | 2413 | t->rcv_processed = t->rcv_nxt + 1; |
TASS Belgium NV |
131:4758606c9316 | 2414 | tcp_ack(s, f); |
TASS Belgium NV |
131:4758606c9316 | 2415 | |
TASS Belgium NV |
131:4758606c9316 | 2416 | s->state &= 0x00FFU; |
TASS Belgium NV |
131:4758606c9316 | 2417 | s->state |= PICO_SOCKET_STATE_TCP_ESTABLISHED; |
TASS Belgium NV |
131:4758606c9316 | 2418 | tcp_dbg("TCP> Established. State: %x\n", s->state); |
TASS Belgium NV |
131:4758606c9316 | 2419 | |
TASS Belgium NV |
131:4758606c9316 | 2420 | if (s->wakeup) |
TASS Belgium NV |
131:4758606c9316 | 2421 | s->wakeup(PICO_SOCK_EV_CONN, s); |
TASS Belgium NV |
131:4758606c9316 | 2422 | |
TASS Belgium NV |
131:4758606c9316 | 2423 | s->ev_pending |= PICO_SOCK_EV_WR; |
TASS Belgium NV |
131:4758606c9316 | 2424 | |
TASS Belgium NV |
131:4758606c9316 | 2425 | t->rcv_nxt++; |
TASS Belgium NV |
131:4758606c9316 | 2426 | t->snd_nxt++; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2427 | tcp_send_ack(t); /* return ACK */ |
TASS Belgium NV |
131:4758606c9316 | 2428 | |
TASS Belgium NV |
131:4758606c9316 | 2429 | return 0; |
TASS Belgium NV |
131:4758606c9316 | 2430 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2431 | } else if ((hdr->flags & PICO_TCP_RST) == 0) { |
TASS Belgium NV |
131:4758606c9316 | 2432 | tcp_dbg("TCP> Not established, RST sent.\n"); |
TASS Belgium NV |
131:4758606c9316 | 2433 | tcp_nosync_rst(s, f); |
TASS Belgium NV |
131:4758606c9316 | 2434 | return 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2435 | } else { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2436 | /* The segment has the reset flag on: Ignore! */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2437 | return 0; |
TASS Belgium NV |
131:4758606c9316 | 2438 | } |
TASS Belgium NV |
131:4758606c9316 | 2439 | } |
TASS Belgium NV |
131:4758606c9316 | 2440 | |
TASS Belgium NV |
131:4758606c9316 | 2441 | static int tcp_first_ack(struct pico_socket *s, struct pico_frame *f) |
TASS Belgium NV |
131:4758606c9316 | 2442 | { |
TASS Belgium NV |
131:4758606c9316 | 2443 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2444 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 2445 | tcp_dbg("ACK in SYN_RECV: expecting %08x got %08x\n", t->snd_nxt, ACKN(f)); |
TASS Belgium NV |
131:4758606c9316 | 2446 | if (t->snd_nxt == ACKN(f)) { |
TASS Belgium NV |
131:4758606c9316 | 2447 | tcp_set_init_point(s); |
TASS Belgium NV |
131:4758606c9316 | 2448 | tcp_ack(s, f); |
TASS Belgium NV |
131:4758606c9316 | 2449 | s->state &= 0x00FFU; |
TASS Belgium NV |
131:4758606c9316 | 2450 | s->state |= PICO_SOCKET_STATE_TCP_ESTABLISHED; |
TASS Belgium NV |
131:4758606c9316 | 2451 | tcp_dbg("TCP: Established. State now: %04x\n", s->state); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2452 | if( !s->parent && s->wakeup) { /* If the socket has no parent, -> sending socket that has a sim_open */ |
TASS Belgium NV |
131:4758606c9316 | 2453 | tcp_dbg("FIRST ACK - No parent found -> sending socket\n"); |
TASS Belgium NV |
131:4758606c9316 | 2454 | s->wakeup(PICO_SOCK_EV_CONN, s); |
TASS Belgium NV |
131:4758606c9316 | 2455 | } |
TASS Belgium NV |
131:4758606c9316 | 2456 | |
TASS Belgium NV |
131:4758606c9316 | 2457 | if (s->parent && s->parent->wakeup) { |
TASS Belgium NV |
131:4758606c9316 | 2458 | tcp_dbg("FIRST ACK - Parent found -> listening socket\n"); |
TASS Belgium NV |
131:4758606c9316 | 2459 | s->wakeup = s->parent->wakeup; |
TASS Belgium NV |
131:4758606c9316 | 2460 | s->parent->wakeup(PICO_SOCK_EV_CONN, s->parent); |
TASS Belgium NV |
131:4758606c9316 | 2461 | } |
TASS Belgium NV |
131:4758606c9316 | 2462 | |
TASS Belgium NV |
131:4758606c9316 | 2463 | s->ev_pending |= PICO_SOCK_EV_WR; |
TASS Belgium NV |
131:4758606c9316 | 2464 | tcp_dbg("%s: snd_nxt is now %08x\n", __FUNCTION__, t->snd_nxt); |
TASS Belgium NV |
131:4758606c9316 | 2465 | return 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2466 | } else if ((hdr->flags & PICO_TCP_RST) == 0) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2467 | tcp_nosync_rst(s, f); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2468 | return 0; |
TASS Belgium NV |
131:4758606c9316 | 2469 | } else { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2470 | /* The segment has the reset flag on: Ignore! */ |
TASS Belgium NV |
131:4758606c9316 | 2471 | return 0; |
TASS Belgium NV |
131:4758606c9316 | 2472 | } |
TASS Belgium NV |
131:4758606c9316 | 2473 | } |
TASS Belgium NV |
131:4758606c9316 | 2474 | |
tass | 152:a3d286bf94e5 | 2475 | static void tcp_attempt_closewait(struct pico_socket *s, struct pico_frame *f) |
TASS Belgium NV |
131:4758606c9316 | 2476 | { |
TASS Belgium NV |
131:4758606c9316 | 2477 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
TASS Belgium NV |
131:4758606c9316 | 2478 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) (f->transport_hdr); |
tass | 152:a3d286bf94e5 | 2479 | if (pico_seq_compare(SEQN(f), t->rcv_nxt) == 0) { |
TASS Belgium NV |
131:4758606c9316 | 2480 | /* received FIN, increase ACK nr */ |
TASS Belgium NV |
131:4758606c9316 | 2481 | t->rcv_nxt = long_be(hdr->seq) + 1; |
tass | 152:a3d286bf94e5 | 2482 | if (pico_seq_compare(SEQN(f), t->rcv_processed) == 0) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2483 | if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2484 | tcp_dbg("Changing state to CLOSE_WAIT\n"); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2485 | s->state &= 0x00FFU; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2486 | s->state |= PICO_SOCKET_STATE_TCP_CLOSE_WAIT; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2487 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2488 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2489 | /* set SHUT_REMOTE */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2490 | s->state |= PICO_SOCKET_STATE_SHUT_REMOTE; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2491 | tcp_dbg("TCP> Close-wait\n"); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2492 | if (s->wakeup) { |
TASS Belgium NV |
131:4758606c9316 | 2493 | s->wakeup(PICO_SOCK_EV_CLOSE, s); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2494 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2495 | } else { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2496 | t->remote_closed = 1; |
TASS Belgium NV |
131:4758606c9316 | 2497 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2498 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2499 | |
tass | 152:a3d286bf94e5 | 2500 | |
tass | 152:a3d286bf94e5 | 2501 | } |
tass | 152:a3d286bf94e5 | 2502 | |
tass | 152:a3d286bf94e5 | 2503 | static int tcp_closewait(struct pico_socket *s, struct pico_frame *f) |
tass | 152:a3d286bf94e5 | 2504 | { |
tass | 152:a3d286bf94e5 | 2505 | |
tass | 152:a3d286bf94e5 | 2506 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
tass | 152:a3d286bf94e5 | 2507 | if (f->payload_len > 0) |
tass | 152:a3d286bf94e5 | 2508 | tcp_data_in(s, f); |
tass | 152:a3d286bf94e5 | 2509 | |
tass | 152:a3d286bf94e5 | 2510 | if (f->flags & PICO_TCP_ACK) |
tass | 152:a3d286bf94e5 | 2511 | tcp_ack(s, f); |
tass | 152:a3d286bf94e5 | 2512 | |
tass | 152:a3d286bf94e5 | 2513 | tcp_dbg("called close_wait, in state %08x\n", s->state); |
tass | 152:a3d286bf94e5 | 2514 | tcp_attempt_closewait(s, f); |
tass | 152:a3d286bf94e5 | 2515 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2516 | /* Ensure that the notification given to the socket |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2517 | * did not put us in LAST_ACK state before sending the ACK: i.e. if |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2518 | * pico_socket_close() has been called in the socket callback, we don't need to send |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2519 | * an ACK here. |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2520 | * |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2521 | */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2522 | if (((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_CLOSE_WAIT) || |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2523 | ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2524 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2525 | tcp_dbg("In closewait: Sending ack! (state is %08x)\n", s->state); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2526 | tcp_send_ack(t); |
TASS Belgium NV |
131:4758606c9316 | 2527 | } |
TASS Belgium NV |
131:4758606c9316 | 2528 | |
TASS Belgium NV |
131:4758606c9316 | 2529 | return 0; |
TASS Belgium NV |
131:4758606c9316 | 2530 | } |
TASS Belgium NV |
131:4758606c9316 | 2531 | |
TASS Belgium NV |
131:4758606c9316 | 2532 | static int tcp_rcvfin(struct pico_socket *s, struct pico_frame *f) |
TASS Belgium NV |
131:4758606c9316 | 2533 | { |
TASS Belgium NV |
131:4758606c9316 | 2534 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
TASS Belgium NV |
131:4758606c9316 | 2535 | IGNORE_PARAMETER(f); |
TASS Belgium NV |
131:4758606c9316 | 2536 | tcp_dbg("TCP> Received FIN in FIN_WAIT1\n"); |
TASS Belgium NV |
131:4758606c9316 | 2537 | s->state &= 0x00FFU; |
TASS Belgium NV |
131:4758606c9316 | 2538 | s->state |= PICO_SOCKET_STATE_TCP_CLOSING; |
TASS Belgium NV |
131:4758606c9316 | 2539 | t->rcv_processed = t->rcv_nxt + 1; |
TASS Belgium NV |
131:4758606c9316 | 2540 | t->rcv_nxt++; |
TASS Belgium NV |
131:4758606c9316 | 2541 | /* send ACK */ |
TASS Belgium NV |
131:4758606c9316 | 2542 | tcp_send_ack(t); |
TASS Belgium NV |
131:4758606c9316 | 2543 | return 0; |
TASS Belgium NV |
131:4758606c9316 | 2544 | } |
TASS Belgium NV |
131:4758606c9316 | 2545 | |
TASS Belgium NV |
131:4758606c9316 | 2546 | static int tcp_finack(struct pico_socket *s, struct pico_frame *f) |
TASS Belgium NV |
131:4758606c9316 | 2547 | { |
TASS Belgium NV |
131:4758606c9316 | 2548 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
TASS Belgium NV |
131:4758606c9316 | 2549 | IGNORE_PARAMETER(f); |
TASS Belgium NV |
131:4758606c9316 | 2550 | |
TASS Belgium NV |
131:4758606c9316 | 2551 | tcp_dbg("TCP> ENTERED finack\n"); |
TASS Belgium NV |
131:4758606c9316 | 2552 | t->rcv_nxt++; |
TASS Belgium NV |
131:4758606c9316 | 2553 | /* send ACK */ |
TASS Belgium NV |
131:4758606c9316 | 2554 | tcp_send_ack(t); |
TASS Belgium NV |
131:4758606c9316 | 2555 | |
TASS Belgium NV |
131:4758606c9316 | 2556 | /* call socket wakeup with EV_FIN */ |
TASS Belgium NV |
131:4758606c9316 | 2557 | if (s->wakeup) |
TASS Belgium NV |
131:4758606c9316 | 2558 | s->wakeup(PICO_SOCK_EV_FIN, s); |
TASS Belgium NV |
131:4758606c9316 | 2559 | |
TASS Belgium NV |
131:4758606c9316 | 2560 | s->state &= 0x00FFU; |
TASS Belgium NV |
131:4758606c9316 | 2561 | s->state |= PICO_SOCKET_STATE_TCP_TIME_WAIT; |
TASS Belgium NV |
131:4758606c9316 | 2562 | /* set SHUT_REMOTE */ |
TASS Belgium NV |
131:4758606c9316 | 2563 | s->state |= PICO_SOCKET_STATE_SHUT_REMOTE; |
tass | 152:a3d286bf94e5 | 2564 | |
tass | 152:a3d286bf94e5 | 2565 | tcp_linger(t); |
TASS Belgium NV |
131:4758606c9316 | 2566 | |
TASS Belgium NV |
131:4758606c9316 | 2567 | return 0; |
TASS Belgium NV |
131:4758606c9316 | 2568 | } |
TASS Belgium NV |
131:4758606c9316 | 2569 | |
TASS Belgium NV |
131:4758606c9316 | 2570 | static void tcp_force_closed(struct pico_socket *s) |
TASS Belgium NV |
131:4758606c9316 | 2571 | { |
TASS Belgium NV |
131:4758606c9316 | 2572 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
daniele | 29:1a47b7151851 | 2573 | /* update state */ |
daniele | 29:1a47b7151851 | 2574 | (t->sock).state &= 0x00FFU; |
daniele | 29:1a47b7151851 | 2575 | (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED; |
daniele | 29:1a47b7151851 | 2576 | (t->sock).state &= 0xFF00U; |
daniele | 29:1a47b7151851 | 2577 | (t->sock).state |= PICO_SOCKET_STATE_CLOSED; |
tass | 152:a3d286bf94e5 | 2578 | /* call EV_ERR wakeup before deleting */ |
tass | 152:a3d286bf94e5 | 2579 | if (((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)) { |
tass | 152:a3d286bf94e5 | 2580 | if ((t->sock).wakeup) |
tass | 152:a3d286bf94e5 | 2581 | (t->sock).wakeup(PICO_SOCK_EV_FIN, &(t->sock)); |
tass | 152:a3d286bf94e5 | 2582 | } else { |
tass | 152:a3d286bf94e5 | 2583 | pico_err = PICO_ERR_ECONNRESET; |
tass | 152:a3d286bf94e5 | 2584 | if ((t->sock).wakeup) |
tass | 152:a3d286bf94e5 | 2585 | (t->sock).wakeup(PICO_SOCK_EV_ERR, &(t->sock)); |
tass | 152:a3d286bf94e5 | 2586 | |
tass | 152:a3d286bf94e5 | 2587 | /* delete socket */ |
tass | 152:a3d286bf94e5 | 2588 | pico_socket_del(&t->sock); |
tass | 152:a3d286bf94e5 | 2589 | } |
tass | 70:cd218dd180e5 | 2590 | } |
tass | 70:cd218dd180e5 | 2591 | |
tass | 70:cd218dd180e5 | 2592 | static void tcp_wakeup_pending(struct pico_socket *s, uint16_t ev) |
tass | 70:cd218dd180e5 | 2593 | { |
TASS Belgium NV |
131:4758606c9316 | 2594 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
TASS Belgium NV |
131:4758606c9316 | 2595 | if ((t->sock).wakeup) |
TASS Belgium NV |
131:4758606c9316 | 2596 | (t->sock).wakeup(ev, &(t->sock)); |
tass | 70:cd218dd180e5 | 2597 | } |
tass | 70:cd218dd180e5 | 2598 | |
daniele | 29:1a47b7151851 | 2599 | static int tcp_rst(struct pico_socket *s, struct pico_frame *f) |
daniele | 29:1a47b7151851 | 2600 | { |
TASS Belgium NV |
131:4758606c9316 | 2601 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
TASS Belgium NV |
131:4758606c9316 | 2602 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) (f->transport_hdr); |
TASS Belgium NV |
131:4758606c9316 | 2603 | |
TASS Belgium NV |
131:4758606c9316 | 2604 | tcp_dbg("TCP >>>>>>>>>>>>>> received RST <<<<<<<<<<<<<<<<<<<<\n"); |
TASS Belgium NV |
131:4758606c9316 | 2605 | if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_SYN_SENT) { |
TASS Belgium NV |
131:4758606c9316 | 2606 | /* the RST is acceptable if the ACK field acknowledges the SYN */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2607 | if ((t->snd_nxt + 1u) == ACKN(f)) { /* valid, got to closed state */ |
TASS Belgium NV |
131:4758606c9316 | 2608 | tcp_force_closed(s); |
TASS Belgium NV |
131:4758606c9316 | 2609 | } else { /* not valid, ignore */ |
TASS Belgium NV |
131:4758606c9316 | 2610 | tcp_dbg("TCP RST> IGNORE\n"); |
TASS Belgium NV |
131:4758606c9316 | 2611 | return 0; |
TASS Belgium NV |
131:4758606c9316 | 2612 | } |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2613 | } else { /* all other states */ |
TASS Belgium NV |
131:4758606c9316 | 2614 | /* all reset (RST) segments are validated by checking their SEQ-fields, |
TASS Belgium NV |
131:4758606c9316 | 2615 | a reset is valid if its sequence number is in the window */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2616 | uint32_t this_seq = long_be(hdr->seq); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2617 | if ((this_seq >= t->rcv_ackd) && (this_seq <= ((uint32_t)(short_be(hdr->rwnd) << (t->wnd_scale)) + t->rcv_ackd))) { |
tass | 152:a3d286bf94e5 | 2618 | tcp_force_closed(s); |
TASS Belgium NV |
131:4758606c9316 | 2619 | } else { /* not valid, ignore */ |
TASS Belgium NV |
131:4758606c9316 | 2620 | tcp_dbg("TCP RST> IGNORE\n"); |
TASS Belgium NV |
131:4758606c9316 | 2621 | return 0; |
TASS Belgium NV |
131:4758606c9316 | 2622 | } |
daniele | 29:1a47b7151851 | 2623 | } |
TASS Belgium NV |
131:4758606c9316 | 2624 | |
TASS Belgium NV |
131:4758606c9316 | 2625 | return 0; |
daniele | 29:1a47b7151851 | 2626 | } |
tass | 79:6d1cb906043d | 2627 | static int tcp_halfopencon(struct pico_socket *s, struct pico_frame *fr) |
tass | 79:6d1cb906043d | 2628 | { |
TASS Belgium NV |
131:4758606c9316 | 2629 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
TASS Belgium NV |
131:4758606c9316 | 2630 | IGNORE_PARAMETER(fr); |
TASS Belgium NV |
131:4758606c9316 | 2631 | tcp_send_ack(t); |
TASS Belgium NV |
131:4758606c9316 | 2632 | return 0; |
tass | 79:6d1cb906043d | 2633 | } |
tass | 110:0ece1bbbd36e | 2634 | |
tass | 109:f630e6230063 | 2635 | static int tcp_closeconn(struct pico_socket *s, struct pico_frame *fr) |
tass | 109:f630e6230063 | 2636 | { |
TASS Belgium NV |
131:4758606c9316 | 2637 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
TASS Belgium NV |
131:4758606c9316 | 2638 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) (fr->transport_hdr); |
TASS Belgium NV |
131:4758606c9316 | 2639 | |
tass | 152:a3d286bf94e5 | 2640 | if (pico_seq_compare(SEQN(fr), t->rcv_nxt) == 0) { |
TASS Belgium NV |
131:4758606c9316 | 2641 | /* received FIN, increase ACK nr */ |
TASS Belgium NV |
131:4758606c9316 | 2642 | t->rcv_nxt = long_be(hdr->seq) + 1; |
TASS Belgium NV |
131:4758606c9316 | 2643 | s->state &= 0x00FFU; |
TASS Belgium NV |
131:4758606c9316 | 2644 | s->state |= PICO_SOCKET_STATE_TCP_CLOSE_WAIT; |
TASS Belgium NV |
131:4758606c9316 | 2645 | /* set SHUT_LOCAL */ |
TASS Belgium NV |
131:4758606c9316 | 2646 | s->state |= PICO_SOCKET_STATE_SHUT_LOCAL; |
TASS Belgium NV |
131:4758606c9316 | 2647 | pico_socket_close(s); |
TASS Belgium NV |
131:4758606c9316 | 2648 | return 1; |
TASS Belgium NV |
131:4758606c9316 | 2649 | } |
TASS Belgium NV |
131:4758606c9316 | 2650 | |
TASS Belgium NV |
131:4758606c9316 | 2651 | return 0; |
tass | 109:f630e6230063 | 2652 | } |
tass | 110:0ece1bbbd36e | 2653 | |
daniele | 29:1a47b7151851 | 2654 | struct tcp_action_entry { |
TASS Belgium NV |
131:4758606c9316 | 2655 | uint16_t tcpstate; |
TASS Belgium NV |
131:4758606c9316 | 2656 | int (*syn)(struct pico_socket *s, struct pico_frame *f); |
TASS Belgium NV |
131:4758606c9316 | 2657 | int (*synack)(struct pico_socket *s, struct pico_frame *f); |
TASS Belgium NV |
131:4758606c9316 | 2658 | int (*ack)(struct pico_socket *s, struct pico_frame *f); |
TASS Belgium NV |
131:4758606c9316 | 2659 | int (*data)(struct pico_socket *s, struct pico_frame *f); |
TASS Belgium NV |
131:4758606c9316 | 2660 | int (*fin)(struct pico_socket *s, struct pico_frame *f); |
TASS Belgium NV |
131:4758606c9316 | 2661 | int (*finack)(struct pico_socket *s, struct pico_frame *f); |
TASS Belgium NV |
131:4758606c9316 | 2662 | int (*rst)(struct pico_socket *s, struct pico_frame *f); |
daniele | 29:1a47b7151851 | 2663 | }; |
daniele | 29:1a47b7151851 | 2664 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2665 | static const struct tcp_action_entry tcp_fsm[] = { |
TASS Belgium NV |
131:4758606c9316 | 2666 | /* State syn synack ack data fin finack rst*/ |
TASS Belgium NV |
131:4758606c9316 | 2667 | { PICO_SOCKET_STATE_TCP_UNDEF, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, |
TASS Belgium NV |
131:4758606c9316 | 2668 | { PICO_SOCKET_STATE_TCP_CLOSED, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, |
TASS Belgium NV |
131:4758606c9316 | 2669 | { PICO_SOCKET_STATE_TCP_LISTEN, &tcp_syn, NULL, NULL, NULL, NULL, NULL, NULL }, |
TASS Belgium NV |
131:4758606c9316 | 2670 | { PICO_SOCKET_STATE_TCP_SYN_SENT, NULL, &tcp_synack, NULL, NULL, NULL, NULL, &tcp_rst }, |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2671 | { PICO_SOCKET_STATE_TCP_SYN_RECV, &tcp_synrecv_syn, NULL, &tcp_first_ack, &tcp_data_in, NULL, &tcp_closeconn, &tcp_rst }, |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2672 | { PICO_SOCKET_STATE_TCP_ESTABLISHED, &tcp_halfopencon, &tcp_ack, &tcp_ack, &tcp_data_in, &tcp_closewait, &tcp_closewait, &tcp_rst }, |
TASS Belgium NV |
131:4758606c9316 | 2673 | { PICO_SOCKET_STATE_TCP_CLOSE_WAIT, NULL, &tcp_ack, &tcp_ack, &tcp_send_rst, &tcp_closewait, &tcp_closewait, &tcp_rst }, |
TASS Belgium NV |
131:4758606c9316 | 2674 | { PICO_SOCKET_STATE_TCP_LAST_ACK, NULL, &tcp_ack, &tcp_lastackwait, &tcp_send_rst, &tcp_send_rst, &tcp_send_rst, &tcp_rst }, |
TASS Belgium NV |
131:4758606c9316 | 2675 | { PICO_SOCKET_STATE_TCP_FIN_WAIT1, NULL, &tcp_ack, &tcp_finwaitack, &tcp_data_in, &tcp_rcvfin, &tcp_finack, &tcp_rst }, |
TASS Belgium NV |
131:4758606c9316 | 2676 | { PICO_SOCKET_STATE_TCP_FIN_WAIT2, NULL, &tcp_ack, &tcp_ack, &tcp_data_in, &tcp_finwaitfin, &tcp_finack, &tcp_rst }, |
tass | 152:a3d286bf94e5 | 2677 | { PICO_SOCKET_STATE_TCP_CLOSING, NULL, &tcp_ack, &tcp_closing_ack, &tcp_send_rst, &tcp_send_rst, &tcp_send_rst, &tcp_rst }, |
tass | 152:a3d286bf94e5 | 2678 | { PICO_SOCKET_STATE_TCP_TIME_WAIT, NULL, NULL, NULL, &tcp_send_rst, NULL, NULL, NULL} |
daniele | 29:1a47b7151851 | 2679 | }; |
daniele | 29:1a47b7151851 | 2680 | |
tass | 152:a3d286bf94e5 | 2681 | #define MAX_VALID_FLAGS 10 /* Maximum number of valid flag combinations */ |
TASS Belgium NV |
131:4758606c9316 | 2682 | static uint8_t invalid_flags(struct pico_socket *s, uint8_t flags) |
TASS Belgium NV |
131:4758606c9316 | 2683 | { |
TASS Belgium NV |
131:4758606c9316 | 2684 | uint8_t i; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2685 | static const uint8_t valid_flags[PICO_SOCKET_STATE_TCP_ARRAYSIZ][MAX_VALID_FLAGS] = { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2686 | { /* PICO_SOCKET_STATE_TCP_UNDEF */ 0, }, |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2687 | { /* PICO_SOCKET_STATE_TCP_CLOSED */ 0, }, |
TASS Belgium NV |
131:4758606c9316 | 2688 | { /* PICO_SOCKET_STATE_TCP_LISTEN */ PICO_TCP_SYN }, |
tass picotcp@tass.be | 133:5b075f5e141a | 2689 | { /* PICO_SOCKET_STATE_TCP_SYN_SENT */ PICO_TCP_SYNACK, PICO_TCP_RST, PICO_TCP_RSTACK}, |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2690 | { /* 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}, |
tass | 152:a3d286bf94e5 | 2691 | { /* 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}, |
TASS Belgium NV |
131:4758606c9316 | 2692 | { /* 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}, |
TASS Belgium NV |
131:4758606c9316 | 2693 | { /* 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}, |
TASS Belgium NV |
131:4758606c9316 | 2694 | { /* 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}, |
TASS Belgium NV |
131:4758606c9316 | 2695 | { /* PICO_SOCKET_STATE_TCP_FIN_WAIT2 */ PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST}, |
TASS Belgium NV |
131:4758606c9316 | 2696 | { /* PICO_SOCKET_STATE_TCP_CLOSING */ PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST}, |
TASS Belgium NV |
131:4758606c9316 | 2697 | { /* PICO_SOCKET_STATE_TCP_TIME_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}, |
TASS Belgium NV |
131:4758606c9316 | 2698 | }; |
TASS Belgium NV |
131:4758606c9316 | 2699 | if(!flags) |
TASS Belgium NV |
131:4758606c9316 | 2700 | return 1; |
TASS Belgium NV |
131:4758606c9316 | 2701 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2702 | for(i = 0; i < MAX_VALID_FLAGS; i++) { |
TASS Belgium NV |
131:4758606c9316 | 2703 | if(valid_flags[s->state >> 8u][i] == flags) |
TASS Belgium NV |
131:4758606c9316 | 2704 | return 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2705 | } |
TASS Belgium NV |
131:4758606c9316 | 2706 | return 1; |
TASS Belgium NV |
131:4758606c9316 | 2707 | } |
tass | 152:a3d286bf94e5 | 2708 | |
tass | 152:a3d286bf94e5 | 2709 | static void tcp_action_call(int (*call)(struct pico_socket *s, struct pico_frame *f), struct pico_socket *s, struct pico_frame *f ) |
tass | 152:a3d286bf94e5 | 2710 | { |
tass | 152:a3d286bf94e5 | 2711 | if (call) |
tass | 152:a3d286bf94e5 | 2712 | call(s, f); |
tass | 152:a3d286bf94e5 | 2713 | } |
tass | 152:a3d286bf94e5 | 2714 | |
tass | 152:a3d286bf94e5 | 2715 | static int tcp_action_by_flags(const struct tcp_action_entry *action, struct pico_socket *s, struct pico_frame *f, uint8_t flags) |
tass | 152:a3d286bf94e5 | 2716 | { |
tass | 152:a3d286bf94e5 | 2717 | int ret = 0; |
tass | 152:a3d286bf94e5 | 2718 | if ((flags == PICO_TCP_ACK) || (flags == (PICO_TCP_ACK | PICO_TCP_PSH))) { |
tass | 152:a3d286bf94e5 | 2719 | tcp_action_call(action->ack, s, f); |
tass | 152:a3d286bf94e5 | 2720 | } |
tass | 152:a3d286bf94e5 | 2721 | |
tass | 152:a3d286bf94e5 | 2722 | if ((f->payload_len > 0 || (flags & PICO_TCP_PSH)) && |
tass | 152:a3d286bf94e5 | 2723 | !(s->state & PICO_SOCKET_STATE_CLOSED) && !TCP_IS_STATE(s, PICO_SOCKET_STATE_TCP_LISTEN)) |
tass | 152:a3d286bf94e5 | 2724 | { |
tass | 152:a3d286bf94e5 | 2725 | ret = f->payload_len; |
tass | 152:a3d286bf94e5 | 2726 | tcp_action_call(action->data, s, f); |
tass | 152:a3d286bf94e5 | 2727 | } |
tass | 152:a3d286bf94e5 | 2728 | |
tass | 152:a3d286bf94e5 | 2729 | if (flags == PICO_TCP_FIN) { |
tass | 152:a3d286bf94e5 | 2730 | tcp_action_call(action->fin, s, f); |
tass | 152:a3d286bf94e5 | 2731 | } |
tass | 152:a3d286bf94e5 | 2732 | |
tass | 152:a3d286bf94e5 | 2733 | if ((flags == (PICO_TCP_FIN | PICO_TCP_ACK)) || (flags == (PICO_TCP_FIN | PICO_TCP_ACK | PICO_TCP_PSH))) { |
tass | 152:a3d286bf94e5 | 2734 | tcp_action_call(action->finack, s, f); |
tass | 152:a3d286bf94e5 | 2735 | } |
tass | 152:a3d286bf94e5 | 2736 | |
tass | 152:a3d286bf94e5 | 2737 | if (flags & PICO_TCP_RST) { |
tass | 152:a3d286bf94e5 | 2738 | tcp_action_call(action->rst, s, f); |
tass | 152:a3d286bf94e5 | 2739 | } |
tass | 152:a3d286bf94e5 | 2740 | |
tass | 152:a3d286bf94e5 | 2741 | return ret; |
tass | 152:a3d286bf94e5 | 2742 | } |
tass | 152:a3d286bf94e5 | 2743 | |
daniele | 29:1a47b7151851 | 2744 | int pico_tcp_input(struct pico_socket *s, struct pico_frame *f) |
daniele | 29:1a47b7151851 | 2745 | { |
TASS Belgium NV |
131:4758606c9316 | 2746 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) (f->transport_hdr); |
TASS Belgium NV |
131:4758606c9316 | 2747 | int ret = 0; |
TASS Belgium NV |
131:4758606c9316 | 2748 | uint8_t flags = hdr->flags; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2749 | const struct tcp_action_entry *action = &tcp_fsm[s->state >> 8]; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2750 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2751 | f->payload = (f->transport_hdr + ((hdr->len & 0xf0u) >> 2u)); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2752 | f->payload_len = (uint16_t)(f->transport_len - ((hdr->len & 0xf0u) >> 2u)); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2753 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2754 | tcp_dbg("[sam] TCP> [tcp input] t_len: %u\n", f->transport_len); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2755 | tcp_dbg("[sam] TCP> flags = %02x\n", hdr->flags); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2756 | tcp_dbg("[sam] TCP> s->state >> 8 = %u\n", s->state >> 8); |
tass | 152:a3d286bf94e5 | 2757 | 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 ); |
TASS Belgium NV |
131:4758606c9316 | 2758 | |
TASS Belgium NV |
131:4758606c9316 | 2759 | /* This copy of the frame has the current socket as owner */ |
TASS Belgium NV |
131:4758606c9316 | 2760 | f->sock = s; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2761 | s->timestamp = TCP_TIME; |
TASS Belgium NV |
131:4758606c9316 | 2762 | /* Those are not supported at this time. */ |
TASS Belgium NV |
131:4758606c9316 | 2763 | /* flags &= (uint8_t) ~(PICO_TCP_CWR | PICO_TCP_URG | PICO_TCP_ECN); */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2764 | if(invalid_flags(s, flags)) { |
TASS Belgium NV |
131:4758606c9316 | 2765 | pico_tcp_reply_rst(f); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2766 | } |
TASS Belgium NV |
131:4758606c9316 | 2767 | else if (flags == PICO_TCP_SYN) { |
tass | 152:a3d286bf94e5 | 2768 | tcp_action_call(action->syn, s, f); |
TASS Belgium NV |
131:4758606c9316 | 2769 | } else if (flags == (PICO_TCP_SYN | PICO_TCP_ACK)) { |
tass | 152:a3d286bf94e5 | 2770 | tcp_action_call(action->synack, s, f); |
TASS Belgium NV |
131:4758606c9316 | 2771 | } else { |
tass | 152:a3d286bf94e5 | 2772 | ret = tcp_action_by_flags(action, s, f, flags); |
daniele | 29:1a47b7151851 | 2773 | } |
TASS Belgium NV |
131:4758606c9316 | 2774 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2775 | if (s->ev_pending) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2776 | tcp_wakeup_pending(s, s->ev_pending); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2777 | |
TASS Belgium NV |
131:4758606c9316 | 2778 | /* discard: */ |
TASS Belgium NV |
131:4758606c9316 | 2779 | pico_frame_discard(f); |
TASS Belgium NV |
131:4758606c9316 | 2780 | return ret; |
daniele | 29:1a47b7151851 | 2781 | } |
daniele | 29:1a47b7151851 | 2782 | |
daniele | 29:1a47b7151851 | 2783 | |
tass | 76:938a140caf12 | 2784 | inline static int checkLocalClosing(struct pico_socket *s); |
tass | 76:938a140caf12 | 2785 | inline static int checkRemoteClosing(struct pico_socket *s); |
tass | 76:938a140caf12 | 2786 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2787 | static struct pico_frame *tcp_split_segment(struct pico_socket_tcp *t, struct pico_frame *f, uint16_t size) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2788 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2789 | struct pico_frame *f1, *f2; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2790 | uint16_t size1, size2, size_f; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2791 | uint16_t overhead; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2792 | struct pico_tcp_hdr *hdr1, *hdr2, *hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2793 | overhead = pico_tcp_overhead(&t->sock); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2794 | size_f = f->payload_len; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2795 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2796 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2797 | if (size >= size_f) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2798 | return f; /* no need to split! */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2799 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2800 | size1 = size; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2801 | size2 = (uint16_t)(size_f - size); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2802 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2803 | f1 = pico_socket_frame_alloc(&t->sock, (uint16_t) (size1 + overhead)); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2804 | f2 = pico_socket_frame_alloc(&t->sock, (uint16_t) (size2 + overhead)); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2805 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2806 | if (!f1 || !f2) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2807 | pico_err = PICO_ERR_ENOMEM; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2808 | return NULL; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2809 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2810 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2811 | /* Advance payload pointer to the beginning of segment data */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2812 | f1->payload += overhead; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2813 | f1->payload_len = (uint16_t)(f1->payload_len - overhead); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2814 | f2->payload += overhead; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2815 | f2->payload_len = (uint16_t)(f2->payload_len - overhead); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2816 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2817 | hdr1 = (struct pico_tcp_hdr *)f1->transport_hdr; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2818 | hdr2 = (struct pico_tcp_hdr *)f2->transport_hdr; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2819 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2820 | /* Copy payload */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2821 | memcpy(f1->payload, f->payload, size1); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2822 | memcpy(f2->payload, f->payload + size1, size2); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2823 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2824 | /* Copy tcp hdr */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2825 | memcpy(hdr1, hdr, sizeof(struct pico_tcp_hdr)); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2826 | memcpy(hdr2, hdr, sizeof(struct pico_tcp_hdr)); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2827 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2828 | /* Adjust f2's sequence number */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2829 | hdr2->seq = long_be(SEQN(f) + size1); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2830 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2831 | /* Add TCP options */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2832 | pico_tcp_flags_update(f1, &t->sock); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2833 | pico_tcp_flags_update(f2, &t->sock); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2834 | tcp_add_options_frame(t, f1); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2835 | tcp_add_options_frame(t, f2); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2836 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2837 | /* Get rid of the full frame */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2838 | pico_discard_segment(&t->tcpq_out, f); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2839 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2840 | /* Enqueue f2 for later send... */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2841 | pico_enqueue_segment(&t->tcpq_out, f2); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2842 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2843 | /* Return the partial frame */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2844 | return f1; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2845 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2846 | |
tass | 48:40fc4462265c | 2847 | |
daniele | 29:1a47b7151851 | 2848 | int pico_tcp_output(struct pico_socket *s, int loop_score) |
daniele | 29:1a47b7151851 | 2849 | { |
TASS Belgium NV |
131:4758606c9316 | 2850 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
TASS Belgium NV |
131:4758606c9316 | 2851 | struct pico_frame *f, *una; |
TASS Belgium NV |
131:4758606c9316 | 2852 | int sent = 0; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2853 | int data_sent = 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2854 | int32_t seq_diff = 0; |
TASS Belgium NV |
131:4758606c9316 | 2855 | |
TASS Belgium NV |
131:4758606c9316 | 2856 | una = first_segment(&t->tcpq_out); |
TASS Belgium NV |
131:4758606c9316 | 2857 | f = peek_segment(&t->tcpq_out, t->snd_nxt); |
TASS Belgium NV |
131:4758606c9316 | 2858 | |
TASS Belgium NV |
131:4758606c9316 | 2859 | while((f) && (t->cwnd >= t->in_flight)) { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2860 | f->timestamp = TCP_TIME; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2861 | add_retransmission_timer(t, t->rto + TCP_TIME); |
TASS Belgium NV |
131:4758606c9316 | 2862 | tcp_add_options_frame(t, f); |
tass | 152:a3d286bf94e5 | 2863 | seq_diff = pico_seq_compare(SEQN(f), SEQN(una)); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2864 | if (seq_diff < 0) { |
tass | 152:a3d286bf94e5 | 2865 | tcp_dbg(">>> FATAL: seq diff is negative!\n"); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2866 | break; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2867 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2868 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2869 | /* Check if advertised window is full */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2870 | if ((uint32_t)seq_diff >= (uint32_t)(t->recv_wnd << t->recv_wnd_scale)) { |
TASS Belgium NV |
131:4758606c9316 | 2871 | if (t->x_mode != PICO_TCP_WINDOW_FULL) { |
TASS Belgium NV |
131:4758606c9316 | 2872 | tcp_dbg("TCP> RIGHT SIZING (rwnd: %d, frame len: %d\n", t->recv_wnd << t->recv_wnd_scale, f->payload_len); |
TASS Belgium NV |
131:4758606c9316 | 2873 | tcp_dbg("In window full...\n"); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2874 | t->snd_nxt = SEQN(una); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2875 | t->snd_retry = SEQN(una); |
TASS Belgium NV |
131:4758606c9316 | 2876 | t->x_mode = PICO_TCP_WINDOW_FULL; |
TASS Belgium NV |
131:4758606c9316 | 2877 | } |
TASS Belgium NV |
131:4758606c9316 | 2878 | |
TASS Belgium NV |
131:4758606c9316 | 2879 | break; |
daniele | 29:1a47b7151851 | 2880 | } |
TASS Belgium NV |
131:4758606c9316 | 2881 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2882 | /* Check if the advertised window is too small to receive the current frame */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2883 | if ((uint32_t)(seq_diff + f->payload_len) > (uint32_t)(t->recv_wnd << t->recv_wnd_scale)) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2884 | f = tcp_split_segment(t, f, (uint16_t)(t->recv_wnd << t->recv_wnd_scale)); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2885 | if (!f) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2886 | break; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2887 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2888 | /* Limit sending window to packets in flight (right sizing) */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2889 | t->cwnd = (uint16_t)t->in_flight; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2890 | if (t->cwnd < 1) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2891 | t->cwnd = 1; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2892 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2893 | |
TASS Belgium NV |
131:4758606c9316 | 2894 | tcp_dbg("TCP> DEQUEUED (for output) frame %08x, acks %08x len= %d, remaining frames %d\n", SEQN(f), ACKN(f), f->payload_len, t->tcpq_out.frames); |
TASS Belgium NV |
131:4758606c9316 | 2895 | tcp_send(t, f); |
TASS Belgium NV |
131:4758606c9316 | 2896 | sent++; |
TASS Belgium NV |
131:4758606c9316 | 2897 | loop_score--; |
TASS Belgium NV |
131:4758606c9316 | 2898 | t->snd_last_out = SEQN(f); |
TASS Belgium NV |
131:4758606c9316 | 2899 | if (loop_score < 1) |
TASS Belgium NV |
131:4758606c9316 | 2900 | break; |
TASS Belgium NV |
131:4758606c9316 | 2901 | |
TASS Belgium NV |
131:4758606c9316 | 2902 | if (f->payload_len > 0) { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2903 | data_sent++; |
TASS Belgium NV |
131:4758606c9316 | 2904 | f = next_segment(&t->tcpq_out, f); |
TASS Belgium NV |
131:4758606c9316 | 2905 | } else { |
TASS Belgium NV |
131:4758606c9316 | 2906 | f = NULL; |
TASS Belgium NV |
131:4758606c9316 | 2907 | } |
daniele | 29:1a47b7151851 | 2908 | } |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2909 | if ((sent > 0 && data_sent > 0)) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2910 | rto_set(t, t->rto); |
TASS Belgium NV |
131:4758606c9316 | 2911 | } else { |
TASS Belgium NV |
131:4758606c9316 | 2912 | /* Nothing to transmit. */ |
TASS Belgium NV |
131:4758606c9316 | 2913 | } |
TASS Belgium NV |
131:4758606c9316 | 2914 | |
tass | 152:a3d286bf94e5 | 2915 | 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 */ |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2916 | if(!checkLocalClosing(&t->sock)) /* check if local closing started and send fin */ |
TASS Belgium NV |
131:4758606c9316 | 2917 | { |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2918 | checkRemoteClosing(&t->sock); /* check if remote closing started and send fin */ |
TASS Belgium NV |
131:4758606c9316 | 2919 | } |
TASS Belgium NV |
131:4758606c9316 | 2920 | } |
TASS Belgium NV |
131:4758606c9316 | 2921 | |
TASS Belgium NV |
131:4758606c9316 | 2922 | return loop_score; |
daniele | 29:1a47b7151851 | 2923 | } |
daniele | 29:1a47b7151851 | 2924 | |
daniele | 29:1a47b7151851 | 2925 | /* function to make new segment from hold queue with specific size (mss) */ |
TASS Belgium NV |
131:4758606c9316 | 2926 | static struct pico_frame *pico_hold_segment_make(struct pico_socket_tcp *t) |
daniele | 29:1a47b7151851 | 2927 | { |
TASS Belgium NV |
131:4758606c9316 | 2928 | struct pico_frame *f_temp, *f_new; |
TASS Belgium NV |
131:4758606c9316 | 2929 | struct pico_socket *s = (struct pico_socket *) &t->sock; |
TASS Belgium NV |
131:4758606c9316 | 2930 | struct pico_tcp_hdr *hdr; |
TASS Belgium NV |
131:4758606c9316 | 2931 | uint16_t total_len = 0, total_payload_len = 0; |
TASS Belgium NV |
131:4758606c9316 | 2932 | uint16_t off = 0, test = 0; |
TASS Belgium NV |
131:4758606c9316 | 2933 | |
TASS Belgium NV |
131:4758606c9316 | 2934 | off = pico_tcp_overhead(s); |
TASS Belgium NV |
131:4758606c9316 | 2935 | |
TASS Belgium NV |
131:4758606c9316 | 2936 | /* init with first frame in hold queue */ |
TASS Belgium NV |
131:4758606c9316 | 2937 | f_temp = first_segment(&t->tcpq_hold); |
TASS Belgium NV |
131:4758606c9316 | 2938 | total_len = f_temp->payload_len; |
daniele | 29:1a47b7151851 | 2939 | f_temp = next_segment(&t->tcpq_hold, f_temp); |
TASS Belgium NV |
131:4758606c9316 | 2940 | |
TASS Belgium NV |
131:4758606c9316 | 2941 | /* check till total_len <= MSS */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2942 | while ((f_temp != NULL) && ((total_len + f_temp->payload_len) <= t->mss)) { |
TASS Belgium NV |
131:4758606c9316 | 2943 | total_len = (uint16_t)(total_len + f_temp->payload_len); |
TASS Belgium NV |
131:4758606c9316 | 2944 | f_temp = next_segment(&t->tcpq_hold, f_temp); |
TASS Belgium NV |
131:4758606c9316 | 2945 | if (f_temp == NULL) |
TASS Belgium NV |
131:4758606c9316 | 2946 | break; |
TASS Belgium NV |
131:4758606c9316 | 2947 | } |
TASS Belgium NV |
131:4758606c9316 | 2948 | /* alloc new frame with payload size = off + total_len */ |
TASS Belgium NV |
131:4758606c9316 | 2949 | f_new = pico_socket_frame_alloc(s, (uint16_t)(off + total_len)); |
TASS Belgium NV |
131:4758606c9316 | 2950 | if (!f_new) { |
TASS Belgium NV |
131:4758606c9316 | 2951 | pico_err = PICO_ERR_ENOMEM; |
TASS Belgium NV |
131:4758606c9316 | 2952 | return f_new; |
TASS Belgium NV |
131:4758606c9316 | 2953 | } |
TASS Belgium NV |
131:4758606c9316 | 2954 | |
tass | 152:a3d286bf94e5 | 2955 | pico_tcp_flags_update(f_new, &t->sock); |
TASS Belgium NV |
131:4758606c9316 | 2956 | hdr = (struct pico_tcp_hdr *) f_new->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 2957 | /* init new frame */ |
TASS Belgium NV |
131:4758606c9316 | 2958 | f_new->payload += off; |
TASS Belgium NV |
131:4758606c9316 | 2959 | f_new->payload_len = (uint16_t)(f_new->payload_len - off); |
TASS Belgium NV |
131:4758606c9316 | 2960 | f_new->sock = s; |
TASS Belgium NV |
131:4758606c9316 | 2961 | |
TASS Belgium NV |
131:4758606c9316 | 2962 | f_temp = first_segment(&t->tcpq_hold); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 2963 | hdr->seq = ((struct pico_tcp_hdr *)(f_temp->transport_hdr))->seq; /* get sequence number of first frame */ |
TASS Belgium NV |
131:4758606c9316 | 2964 | hdr->trans.sport = t->sock.local_port; |
TASS Belgium NV |
131:4758606c9316 | 2965 | hdr->trans.dport = t->sock.remote_port; |
TASS Belgium NV |
131:4758606c9316 | 2966 | |
TASS Belgium NV |
131:4758606c9316 | 2967 | /* check till total_payload_len <= MSS */ |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2968 | while ((f_temp != NULL) && ((total_payload_len + f_temp->payload_len) <= t->mss)) { |
TASS Belgium NV |
131:4758606c9316 | 2969 | /* cpy data and discard frame */ |
TASS Belgium NV |
131:4758606c9316 | 2970 | test++; |
TASS Belgium NV |
131:4758606c9316 | 2971 | memcpy(f_new->payload + total_payload_len, f_temp->payload, f_temp->payload_len); |
TASS Belgium NV |
131:4758606c9316 | 2972 | total_payload_len = (uint16_t)(total_payload_len + f_temp->payload_len); |
TASS Belgium NV |
131:4758606c9316 | 2973 | pico_discard_segment(&t->tcpq_hold, f_temp); |
TASS Belgium NV |
131:4758606c9316 | 2974 | f_temp = first_segment(&t->tcpq_hold); |
TASS Belgium NV |
131:4758606c9316 | 2975 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 2976 | hdr->len = (uint8_t)((f_new->payload - f_new->transport_hdr) << 2u | (int8_t)t->jumbo); |
TASS Belgium NV |
131:4758606c9316 | 2977 | |
TASS Belgium NV |
131:4758606c9316 | 2978 | tcp_dbg_nagle("NAGLE make - joined %d segments, len %d bytes\n", test, total_payload_len); |
tass | 152:a3d286bf94e5 | 2979 | tcp_add_options_frame(t, f_new); |
TASS Belgium NV |
131:4758606c9316 | 2980 | |
daniele | 29:1a47b7151851 | 2981 | return f_new; |
daniele | 29:1a47b7151851 | 2982 | } |
daniele | 29:1a47b7151851 | 2983 | |
tass | 152:a3d286bf94e5 | 2984 | |
tass | 152:a3d286bf94e5 | 2985 | |
tass | 152:a3d286bf94e5 | 2986 | static int pico_tcp_push_nagle_enqueue(struct pico_socket_tcp *t, struct pico_frame *f) |
tass | 152:a3d286bf94e5 | 2987 | { |
tass | 152:a3d286bf94e5 | 2988 | if (pico_enqueue_segment(&t->tcpq_out, f) > 0) { |
tass | 152:a3d286bf94e5 | 2989 | tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushing segment %08x, len %08x to socket %p\n", t->snd_last + 1, f->payload_len, t); |
tass | 152:a3d286bf94e5 | 2990 | t->snd_last += f->payload_len; |
tass | 152:a3d286bf94e5 | 2991 | return f->payload_len; |
tass | 152:a3d286bf94e5 | 2992 | } else { |
tass | 152:a3d286bf94e5 | 2993 | tcp_dbg("Enqueue failed.\n"); |
tass | 152:a3d286bf94e5 | 2994 | return 0; |
tass | 152:a3d286bf94e5 | 2995 | } |
tass | 152:a3d286bf94e5 | 2996 | } |
tass | 152:a3d286bf94e5 | 2997 | |
tass | 152:a3d286bf94e5 | 2998 | static int pico_tcp_push_nagle_hold(struct pico_socket_tcp *t, struct pico_frame *f) |
tass | 152:a3d286bf94e5 | 2999 | { |
tass | 152:a3d286bf94e5 | 3000 | struct pico_frame *f_new; |
tass | 152:a3d286bf94e5 | 3001 | uint32_t total_len = 0; |
tass | 152:a3d286bf94e5 | 3002 | total_len = f->payload_len + t->tcpq_hold.size; |
tass | 152:a3d286bf94e5 | 3003 | if ((total_len >= t->mss) && ((t->tcpq_out.max_size - t->tcpq_out.size) >= t->mss)) { |
tass | 152:a3d286bf94e5 | 3004 | /* IF enough data in hold (>mss) AND space in out queue (>mss) */ |
tass | 152:a3d286bf94e5 | 3005 | /* add current frame in hold and make new segment */ |
tass | 152:a3d286bf94e5 | 3006 | if (pico_enqueue_segment(&t->tcpq_hold, f) > 0 ) { |
tass | 152:a3d286bf94e5 | 3007 | tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushed into hold, make new (enqueued frames out %d)\n", t->tcpq_out.frames); |
tass | 152:a3d286bf94e5 | 3008 | t->snd_last += f->payload_len; /* XXX WATCH OUT */ |
tass | 152:a3d286bf94e5 | 3009 | f_new = pico_hold_segment_make(t); |
tass | 152:a3d286bf94e5 | 3010 | } else { |
tass | 152:a3d286bf94e5 | 3011 | tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue hold failed 1\n"); |
tass | 152:a3d286bf94e5 | 3012 | return 0; |
tass | 152:a3d286bf94e5 | 3013 | } |
tass | 152:a3d286bf94e5 | 3014 | |
tass | 152:a3d286bf94e5 | 3015 | /* and put new frame in out queue */ |
tass | 152:a3d286bf94e5 | 3016 | if ((f_new != NULL) && (pico_enqueue_segment(&t->tcpq_out, f_new) > 0)) { |
tass | 152:a3d286bf94e5 | 3017 | return f_new->payload_len; |
tass | 152:a3d286bf94e5 | 3018 | } else { |
tass | 152:a3d286bf94e5 | 3019 | tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue out failed, f_new = %p\n", f_new); |
tass | 152:a3d286bf94e5 | 3020 | return -1; /* XXX something seriously wrong */ |
tass | 152:a3d286bf94e5 | 3021 | } |
tass | 152:a3d286bf94e5 | 3022 | } else { |
tass | 152:a3d286bf94e5 | 3023 | /* ELSE put frame in hold queue */ |
tass | 152:a3d286bf94e5 | 3024 | if (pico_enqueue_segment(&t->tcpq_hold, f) > 0) { |
tass | 152:a3d286bf94e5 | 3025 | tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushed into hold (enqueued frames out %d)\n", t->tcpq_out.frames); |
tass | 152:a3d286bf94e5 | 3026 | t->snd_last += f->payload_len; /* XXX WATCH OUT */ |
tass | 152:a3d286bf94e5 | 3027 | return f->payload_len; |
tass | 152:a3d286bf94e5 | 3028 | } else { |
tass | 152:a3d286bf94e5 | 3029 | pico_err = PICO_ERR_EAGAIN; |
tass | 152:a3d286bf94e5 | 3030 | tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue hold failed 2\n"); |
tass | 152:a3d286bf94e5 | 3031 | } |
tass | 152:a3d286bf94e5 | 3032 | } |
tass | 152:a3d286bf94e5 | 3033 | |
tass | 152:a3d286bf94e5 | 3034 | return 0; |
tass | 152:a3d286bf94e5 | 3035 | } |
tass | 152:a3d286bf94e5 | 3036 | |
tass | 152:a3d286bf94e5 | 3037 | |
tass | 152:a3d286bf94e5 | 3038 | static int pico_tcp_push_nagle_on(struct pico_socket_tcp *t, struct pico_frame *f) |
tass | 152:a3d286bf94e5 | 3039 | { |
tass | 152:a3d286bf94e5 | 3040 | /* Nagle's algorithm enabled, check if ready to send, or put frame in hold queue */ |
tass | 152:a3d286bf94e5 | 3041 | if (IS_TCP_IDLE(t) && IS_TCP_HOLDQ_EMPTY(t)) |
tass | 152:a3d286bf94e5 | 3042 | return pico_tcp_push_nagle_enqueue(t, f); |
tass | 152:a3d286bf94e5 | 3043 | |
tass | 152:a3d286bf94e5 | 3044 | return pico_tcp_push_nagle_hold(t, f); |
tass | 152:a3d286bf94e5 | 3045 | } |
tass | 152:a3d286bf94e5 | 3046 | |
tass | 152:a3d286bf94e5 | 3047 | |
tass | 152:a3d286bf94e5 | 3048 | |
daniele | 29:1a47b7151851 | 3049 | /* original behavior kept when Nagle disabled; |
daniele | 29:1a47b7151851 | 3050 | Nagle algorithm added here, keeping hold frame queue instead of eg linked list of data */ |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 3051 | int pico_tcp_push(struct pico_protocol *self, struct pico_frame *f) |
daniele | 29:1a47b7151851 | 3052 | { |
TASS Belgium NV |
131:4758606c9316 | 3053 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
TASS Belgium NV |
131:4758606c9316 | 3054 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) f->sock; |
TASS Belgium NV |
131:4758606c9316 | 3055 | IGNORE_PARAMETER(self); |
TASS Belgium NV |
131:4758606c9316 | 3056 | pico_err = PICO_ERR_NOERR; |
TASS Belgium NV |
131:4758606c9316 | 3057 | hdr->trans.sport = t->sock.local_port; |
TASS Belgium NV |
131:4758606c9316 | 3058 | hdr->trans.dport = t->sock.remote_port; |
TASS Belgium NV |
131:4758606c9316 | 3059 | hdr->seq = long_be(t->snd_last + 1); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3060 | hdr->len = (uint8_t)((f->payload - f->transport_hdr) << 2u | (int8_t)t->jumbo); |
TASS Belgium NV |
131:4758606c9316 | 3061 | |
TASS Belgium NV |
131:4758606c9316 | 3062 | if ((uint32_t)f->payload_len > (uint32_t)(t->tcpq_out.max_size - t->tcpq_out.size)) |
TASS Belgium NV |
131:4758606c9316 | 3063 | t->sock.ev_pending &= (uint16_t)(~PICO_SOCK_EV_WR); |
TASS Belgium NV |
131:4758606c9316 | 3064 | |
TASS Belgium NV |
131:4758606c9316 | 3065 | /***************************************************************************/ |
TASS Belgium NV |
131:4758606c9316 | 3066 | |
TASS Belgium NV |
131:4758606c9316 | 3067 | if (!IS_NAGLE_ENABLED((&(t->sock)))) { |
TASS Belgium NV |
131:4758606c9316 | 3068 | /* TCP_NODELAY enabled, original behavior */ |
TASS Belgium NV |
131:4758606c9316 | 3069 | if (pico_enqueue_segment(&t->tcpq_out, f) > 0) { |
TASS Belgium NV |
131:4758606c9316 | 3070 | tcp_dbg_nagle("TCP_PUSH - NO NAGLE - Pushing segment %08x, len %08x to socket %p\n", t->snd_last + 1, f->payload_len, t); |
TASS Belgium NV |
131:4758606c9316 | 3071 | t->snd_last += f->payload_len; |
TASS Belgium NV |
131:4758606c9316 | 3072 | return f->payload_len; |
daniele | 29:1a47b7151851 | 3073 | } else { |
TASS Belgium NV |
131:4758606c9316 | 3074 | tcp_dbg("Enqueue failed.\n"); |
TASS Belgium NV |
131:4758606c9316 | 3075 | return 0; |
daniele | 29:1a47b7151851 | 3076 | } |
tass | 152:a3d286bf94e5 | 3077 | } else { |
tass | 152:a3d286bf94e5 | 3078 | return pico_tcp_push_nagle_on(t, f); |
daniele | 29:1a47b7151851 | 3079 | } |
tass | 152:a3d286bf94e5 | 3080 | |
daniele | 29:1a47b7151851 | 3081 | } |
tass | 68:0847e35d08a6 | 3082 | |
tass | 68:0847e35d08a6 | 3083 | inline static void tcp_discard_all_segments(struct pico_tcp_queue *tq) |
tass | 68:0847e35d08a6 | 3084 | { |
TASS Belgium NV |
131:4758606c9316 | 3085 | struct pico_tree_node *index = NULL, *index_safe = NULL; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3086 | PICOTCP_MUTEX_LOCK(Mutex); |
TASS Belgium NV |
131:4758606c9316 | 3087 | pico_tree_foreach_safe(index, &tq->pool, index_safe) |
TASS Belgium NV |
131:4758606c9316 | 3088 | { |
TASS Belgium NV |
131:4758606c9316 | 3089 | void *f = index->keyValue; |
TASS Belgium NV |
131:4758606c9316 | 3090 | if(!f) |
TASS Belgium NV |
131:4758606c9316 | 3091 | break; |
TASS Belgium NV |
131:4758606c9316 | 3092 | |
TASS Belgium NV |
131:4758606c9316 | 3093 | pico_tree_delete(&tq->pool, f); |
TASS Belgium NV |
131:4758606c9316 | 3094 | if(IS_INPUT_QUEUE(tq)) |
TASS Belgium NV |
131:4758606c9316 | 3095 | { |
TASS Belgium NV |
131:4758606c9316 | 3096 | struct tcp_input_segment *inp = (struct tcp_input_segment *)f; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3097 | PICO_FREE(inp->payload); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3098 | PICO_FREE(inp); |
TASS Belgium NV |
131:4758606c9316 | 3099 | } |
TASS Belgium NV |
131:4758606c9316 | 3100 | else |
TASS Belgium NV |
131:4758606c9316 | 3101 | pico_frame_discard(f); |
TASS Belgium NV |
131:4758606c9316 | 3102 | } |
TASS Belgium NV |
131:4758606c9316 | 3103 | tq->frames = 0; |
TASS Belgium NV |
131:4758606c9316 | 3104 | tq->size = 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3105 | PICOTCP_MUTEX_UNLOCK(Mutex); |
tass | 68:0847e35d08a6 | 3106 | } |
tass | 68:0847e35d08a6 | 3107 | |
tass | 68:0847e35d08a6 | 3108 | void pico_tcp_cleanup_queues(struct pico_socket *sck) |
tass | 68:0847e35d08a6 | 3109 | { |
TASS Belgium NV |
131:4758606c9316 | 3110 | struct pico_socket_tcp *tcp = (struct pico_socket_tcp *)sck; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 3111 | if(tcp->retrans_tmr) { |
TASS Belgium NV |
131:4758606c9316 | 3112 | pico_timer_cancel(tcp->retrans_tmr); |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 3113 | tcp->retrans_tmr = NULL; |
tass picotcp@tass.be | 137:a1c8bfa9d691 | 3114 | } |
tass | 152:a3d286bf94e5 | 3115 | if(tcp->keepalive_tmr) { |
tass | 152:a3d286bf94e5 | 3116 | pico_timer_cancel(tcp->keepalive_tmr); |
tass | 152:a3d286bf94e5 | 3117 | tcp->keepalive_tmr = NULL; |
tass | 152:a3d286bf94e5 | 3118 | } |
tass | 152:a3d286bf94e5 | 3119 | if(tcp->fin_tmr) { |
tass | 152:a3d286bf94e5 | 3120 | pico_timer_cancel(tcp->fin_tmr); |
tass | 152:a3d286bf94e5 | 3121 | tcp->fin_tmr = NULL; |
tass | 152:a3d286bf94e5 | 3122 | } |
TASS Belgium NV |
131:4758606c9316 | 3123 | tcp_discard_all_segments(&tcp->tcpq_in); |
TASS Belgium NV |
131:4758606c9316 | 3124 | tcp_discard_all_segments(&tcp->tcpq_out); |
TASS Belgium NV |
131:4758606c9316 | 3125 | tcp_discard_all_segments(&tcp->tcpq_hold); |
tass | 68:0847e35d08a6 | 3126 | } |
tass | 68:0847e35d08a6 | 3127 | |
tass | 76:938a140caf12 | 3128 | static int checkLocalClosing(struct pico_socket *s) |
tass | 76:938a140caf12 | 3129 | { |
TASS Belgium NV |
131:4758606c9316 | 3130 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
TASS Belgium NV |
131:4758606c9316 | 3131 | if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED) { |
TASS Belgium NV |
131:4758606c9316 | 3132 | tcp_dbg("TCP> buffer empty, shutdown established ...\n"); |
TASS Belgium NV |
131:4758606c9316 | 3133 | /* send fin if queue empty and in state shut local (write) */ |
TASS Belgium NV |
131:4758606c9316 | 3134 | tcp_send_fin(t); |
TASS Belgium NV |
131:4758606c9316 | 3135 | /* change tcp state to FIN_WAIT1 */ |
TASS Belgium NV |
131:4758606c9316 | 3136 | s->state &= 0x00FFU; |
TASS Belgium NV |
131:4758606c9316 | 3137 | s->state |= PICO_SOCKET_STATE_TCP_FIN_WAIT1; |
TASS Belgium NV |
131:4758606c9316 | 3138 | return 1; |
TASS Belgium NV |
131:4758606c9316 | 3139 | } |
TASS Belgium NV |
131:4758606c9316 | 3140 | |
TASS Belgium NV |
131:4758606c9316 | 3141 | return 0; |
tass | 76:938a140caf12 | 3142 | } |
tass | 76:938a140caf12 | 3143 | |
tass | 76:938a140caf12 | 3144 | static int checkRemoteClosing(struct pico_socket *s) |
tass | 76:938a140caf12 | 3145 | { |
TASS Belgium NV |
131:4758606c9316 | 3146 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
TASS Belgium NV |
131:4758606c9316 | 3147 | if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_CLOSE_WAIT) { |
TASS Belgium NV |
131:4758606c9316 | 3148 | /* send fin if queue empty and in state shut local (write) */ |
TASS Belgium NV |
131:4758606c9316 | 3149 | tcp_send_fin(t); |
TASS Belgium NV |
131:4758606c9316 | 3150 | /* change tcp state to LAST_ACK */ |
TASS Belgium NV |
131:4758606c9316 | 3151 | s->state &= 0x00FFU; |
TASS Belgium NV |
131:4758606c9316 | 3152 | s->state |= PICO_SOCKET_STATE_TCP_LAST_ACK; |
TASS Belgium NV |
131:4758606c9316 | 3153 | tcp_dbg("TCP> STATE: LAST_ACK.\n"); |
TASS Belgium NV |
131:4758606c9316 | 3154 | return 1; |
tass | 76:938a140caf12 | 3155 | } |
TASS Belgium NV |
131:4758606c9316 | 3156 | |
TASS Belgium NV |
131:4758606c9316 | 3157 | return 0; |
tass | 76:938a140caf12 | 3158 | } |
tass | 76:938a140caf12 | 3159 | |
tass | 76:938a140caf12 | 3160 | void pico_tcp_notify_closing(struct pico_socket *sck) |
tass | 76:938a140caf12 | 3161 | { |
TASS Belgium NV |
131:4758606c9316 | 3162 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)sck; |
TASS Belgium NV |
131:4758606c9316 | 3163 | if(t->tcpq_out.frames == 0) |
TASS Belgium NV |
131:4758606c9316 | 3164 | { |
TASS Belgium NV |
131:4758606c9316 | 3165 | if(!checkLocalClosing(sck)) |
TASS Belgium NV |
131:4758606c9316 | 3166 | checkRemoteClosing(sck); |
TASS Belgium NV |
131:4758606c9316 | 3167 | } |
tass | 76:938a140caf12 | 3168 | } |
tass | 76:938a140caf12 | 3169 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3170 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3171 | int pico_tcp_check_listen_close(struct pico_socket *s) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3172 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3173 | if (TCP_IS_STATE(s, PICO_SOCKET_STATE_TCP_LISTEN)) { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3174 | pico_socket_del(s); |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3175 | return 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3176 | } |
tass | 152:a3d286bf94e5 | 3177 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3178 | return -1; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3179 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3180 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3181 | void pico_tcp_flags_update(struct pico_frame *f, struct pico_socket *s) |
TASS Belgium NV |
131:4758606c9316 | 3182 | { |
TASS Belgium NV |
131:4758606c9316 | 3183 | f->transport_flags_saved = ((struct pico_socket_tcp *)s)->ts_ok; |
TASS Belgium NV |
131:4758606c9316 | 3184 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3185 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3186 | int pico_tcp_set_bufsize_in(struct pico_socket *s, uint32_t value) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3187 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3188 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3189 | t->tcpq_in.max_size = value; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3190 | return 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3191 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3192 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3193 | int pico_tcp_set_bufsize_out(struct pico_socket *s, uint32_t value) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3194 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3195 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3196 | t->tcpq_out.max_size = value; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3197 | return 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3198 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3199 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3200 | int pico_tcp_get_bufsize_in(struct pico_socket *s, uint32_t *value) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3201 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3202 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3203 | *value = t->tcpq_in.max_size; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3204 | return 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3205 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3206 | |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3207 | int pico_tcp_get_bufsize_out(struct pico_socket *s, uint32_t *value) |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3208 | { |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3209 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3210 | *value = t->tcpq_out.max_size; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3211 | return 0; |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3212 | } |
tass picotcp@tass.be | 149:5f4cb161cec3 | 3213 | |
tass | 152:a3d286bf94e5 | 3214 | int pico_tcp_set_keepalive_probes(struct pico_socket *s, uint32_t value) |
tass | 152:a3d286bf94e5 | 3215 | { |
tass | 152:a3d286bf94e5 | 3216 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
tass | 152:a3d286bf94e5 | 3217 | t->ka_probes = value; |
tass | 152:a3d286bf94e5 | 3218 | return 0; |
tass | 152:a3d286bf94e5 | 3219 | } |
tass | 152:a3d286bf94e5 | 3220 | |
tass | 152:a3d286bf94e5 | 3221 | int pico_tcp_set_keepalive_intvl(struct pico_socket *s, uint32_t value) |
tass | 152:a3d286bf94e5 | 3222 | { |
tass | 152:a3d286bf94e5 | 3223 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
tass | 152:a3d286bf94e5 | 3224 | t->ka_intvl = value; |
tass | 152:a3d286bf94e5 | 3225 | return 0; |
tass | 152:a3d286bf94e5 | 3226 | } |
tass | 152:a3d286bf94e5 | 3227 | |
tass | 152:a3d286bf94e5 | 3228 | int pico_tcp_set_keepalive_time(struct pico_socket *s, uint32_t value) |
tass | 152:a3d286bf94e5 | 3229 | { |
tass | 152:a3d286bf94e5 | 3230 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
tass | 152:a3d286bf94e5 | 3231 | t->ka_time = value; |
tass | 152:a3d286bf94e5 | 3232 | return 0; |
tass | 152:a3d286bf94e5 | 3233 | } |
tass | 152:a3d286bf94e5 | 3234 | |
tass | 152:a3d286bf94e5 | 3235 | int pico_tcp_set_linger(struct pico_socket *s, uint32_t value) |
tass | 152:a3d286bf94e5 | 3236 | { |
tass | 152:a3d286bf94e5 | 3237 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
tass | 152:a3d286bf94e5 | 3238 | t->linger_timeout = value; |
tass | 152:a3d286bf94e5 | 3239 | return 0; |
tass | 152:a3d286bf94e5 | 3240 | } |
tass | 152:a3d286bf94e5 | 3241 | |
TASS Belgium NV |
131:4758606c9316 | 3242 | #endif /* PICO_SUPPORT_TCP */ |