Free (GPLv2) TCP/IP stack developed by TASS Belgium
Fork of PicoTCP by
modules/pico_tcp.c@31:d3b2dfcc358f, 2013-06-12 (annotated)
- Committer:
- tass
- Date:
- Wed Jun 12 13:35:01 2013 +0000
- Revision:
- 31:d3b2dfcc358f
- Parent:
- 10:dd7111d4279f
updated pico_tcp and pico_socket after the mainline.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
daniele | 10:dd7111d4279f | 1 | /********************************************************************* |
daniele | 10:dd7111d4279f | 2 | PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. |
daniele | 10:dd7111d4279f | 3 | See LICENSE and COPYING for usage. |
daniele | 10:dd7111d4279f | 4 | |
daniele | 10:dd7111d4279f | 5 | . |
daniele | 10:dd7111d4279f | 6 | |
daniele | 10:dd7111d4279f | 7 | Authors: Daniele Lacamera, Philippe Mariman |
daniele | 10:dd7111d4279f | 8 | *********************************************************************/ |
daniele | 10:dd7111d4279f | 9 | |
daniele | 10:dd7111d4279f | 10 | #include "pico_tcp.h" |
daniele | 10:dd7111d4279f | 11 | #include "pico_config.h" |
daniele | 10:dd7111d4279f | 12 | #include "pico_eth.h" |
daniele | 10:dd7111d4279f | 13 | #include "pico_socket.h" |
daniele | 10:dd7111d4279f | 14 | #include "pico_stack.h" |
daniele | 10:dd7111d4279f | 15 | #include "pico_socket.h" |
daniele | 10:dd7111d4279f | 16 | #include "pico_queue.h" |
daniele | 10:dd7111d4279f | 17 | #include "pico_tree.h" |
daniele | 10:dd7111d4279f | 18 | |
daniele | 10:dd7111d4279f | 19 | #define TCP_SOCK(s) ((struct pico_socket_tcp *)s) |
daniele | 10:dd7111d4279f | 20 | #define SEQN(f) (f?(long_be(((struct pico_tcp_hdr *)(f->transport_hdr))->seq)):0) |
daniele | 10:dd7111d4279f | 21 | #define ACKN(f) (f?(long_be(((struct pico_tcp_hdr *)(f->transport_hdr))->ack)):0) |
daniele | 10:dd7111d4279f | 22 | |
daniele | 10:dd7111d4279f | 23 | #define PICO_TCP_RTO_MIN 1000 |
daniele | 10:dd7111d4279f | 24 | #define PICO_TCP_RTO_MAX 120000 |
daniele | 10:dd7111d4279f | 25 | #define PICO_TCP_IW 2 |
daniele | 10:dd7111d4279f | 26 | |
daniele | 10:dd7111d4279f | 27 | #define PICO_TCP_MAX_CONNECT_RETRIES 7 |
daniele | 10:dd7111d4279f | 28 | |
daniele | 10:dd7111d4279f | 29 | #define PICO_TCP_LOOKAHEAD 0x00 |
daniele | 10:dd7111d4279f | 30 | #define PICO_TCP_FIRST_DUPACK 0x01 |
daniele | 10:dd7111d4279f | 31 | #define PICO_TCP_SECOND_DUPACK 0x02 |
daniele | 10:dd7111d4279f | 32 | #define PICO_TCP_RECOVER 0x03 |
daniele | 10:dd7111d4279f | 33 | #define PICO_TCP_BLACKOUT 0x04 |
daniele | 10:dd7111d4279f | 34 | #define PICO_TCP_UNREACHABLE 0x05 |
daniele | 10:dd7111d4279f | 35 | #define PICO_TCP_WINDOW_FULL 0x06 |
daniele | 10:dd7111d4279f | 36 | |
daniele | 10:dd7111d4279f | 37 | /* check if the Nagle algorithm is enabled on the socket */ |
daniele | 10:dd7111d4279f | 38 | #define IS_NAGLE_ENABLED(s) (!(!(!(s->opt_flags & (1 << PICO_SOCKET_OPT_TCPNODELAY))))) |
daniele | 10:dd7111d4279f | 39 | /* check if tcp connection is "idle" according to Nagle (RFC 896) */ |
daniele | 10:dd7111d4279f | 40 | #define IS_TCP_IDLE(t) ((t->in_flight == 0) && (t->tcpq_out.size == 0)) |
daniele | 10:dd7111d4279f | 41 | /* check if the hold queue contains data (again Nagle) */ |
daniele | 10:dd7111d4279f | 42 | #define IS_TCP_HOLDQ_EMPTY(t) (t->tcpq_hold.size == 0) |
daniele | 10:dd7111d4279f | 43 | |
daniele | 10:dd7111d4279f | 44 | #ifdef PICO_SUPPORT_TCP |
daniele | 10:dd7111d4279f | 45 | #define tcp_dbg(...) do{}while(0) |
daniele | 10:dd7111d4279f | 46 | //#define tcp_dbg dbg |
daniele | 10:dd7111d4279f | 47 | |
daniele | 10:dd7111d4279f | 48 | |
daniele | 10:dd7111d4279f | 49 | #ifdef PICO_SUPPORT_MUTEX |
daniele | 10:dd7111d4279f | 50 | static void * Mutex = NULL; |
daniele | 10:dd7111d4279f | 51 | #define LOCK(x) {\ |
daniele | 10:dd7111d4279f | 52 | if (x == NULL) \ |
daniele | 10:dd7111d4279f | 53 | x = pico_mutex_init(); \ |
daniele | 10:dd7111d4279f | 54 | pico_mutex_lock(x); \ |
daniele | 10:dd7111d4279f | 55 | } |
daniele | 10:dd7111d4279f | 56 | #define UNLOCK(x) pico_mutex_unlock(x); |
daniele | 10:dd7111d4279f | 57 | |
daniele | 10:dd7111d4279f | 58 | #else |
daniele | 10:dd7111d4279f | 59 | #define LOCK(x) do{}while(0) |
daniele | 10:dd7111d4279f | 60 | #define UNLOCK(x) do{}while(0) |
daniele | 10:dd7111d4279f | 61 | #endif |
daniele | 10:dd7111d4279f | 62 | |
daniele | 10:dd7111d4279f | 63 | |
tass | 31:d3b2dfcc358f | 64 | |
daniele | 10:dd7111d4279f | 65 | static inline int seq_compare(uint32_t a, uint32_t b) |
daniele | 10:dd7111d4279f | 66 | { |
daniele | 10:dd7111d4279f | 67 | uint32_t thresh = ((uint32_t)(-1))>>1; |
daniele | 10:dd7111d4279f | 68 | if (((a > thresh) && (b > thresh)) || ((a <= thresh) && (b <= thresh))) { |
daniele | 10:dd7111d4279f | 69 | if (a > b) |
daniele | 10:dd7111d4279f | 70 | return 1; |
daniele | 10:dd7111d4279f | 71 | if (b > a) |
daniele | 10:dd7111d4279f | 72 | return -1; |
daniele | 10:dd7111d4279f | 73 | } else { |
daniele | 10:dd7111d4279f | 74 | if (a > b) |
daniele | 10:dd7111d4279f | 75 | return -2; |
daniele | 10:dd7111d4279f | 76 | if (b > a) |
daniele | 10:dd7111d4279f | 77 | return 2; |
daniele | 10:dd7111d4279f | 78 | } |
daniele | 10:dd7111d4279f | 79 | return 0; |
daniele | 10:dd7111d4279f | 80 | } |
daniele | 10:dd7111d4279f | 81 | |
daniele | 10:dd7111d4279f | 82 | static int segment_compare(void * ka, void * kb) |
daniele | 10:dd7111d4279f | 83 | { |
daniele | 10:dd7111d4279f | 84 | struct pico_frame *a = ka, *b = kb; |
daniele | 10:dd7111d4279f | 85 | return seq_compare(SEQN(a), SEQN(b)); |
daniele | 10:dd7111d4279f | 86 | } |
daniele | 10:dd7111d4279f | 87 | |
daniele | 10:dd7111d4279f | 88 | struct pico_tcp_queue |
daniele | 10:dd7111d4279f | 89 | { |
daniele | 10:dd7111d4279f | 90 | struct pico_tree pool; |
daniele | 10:dd7111d4279f | 91 | uint32_t max_size; |
daniele | 10:dd7111d4279f | 92 | uint32_t size; |
daniele | 10:dd7111d4279f | 93 | uint32_t frames; |
daniele | 10:dd7111d4279f | 94 | }; |
daniele | 10:dd7111d4279f | 95 | |
daniele | 10:dd7111d4279f | 96 | static struct pico_frame *peek_segment(struct pico_tcp_queue *tq, uint32_t seq) |
daniele | 10:dd7111d4279f | 97 | { |
daniele | 10:dd7111d4279f | 98 | struct pico_tcp_hdr H; |
daniele | 10:dd7111d4279f | 99 | struct pico_frame f = {}; |
daniele | 10:dd7111d4279f | 100 | f.transport_hdr = (uint8_t *) (&H); |
daniele | 10:dd7111d4279f | 101 | H.seq = long_be(seq); |
daniele | 10:dd7111d4279f | 102 | |
daniele | 10:dd7111d4279f | 103 | return pico_tree_findKey(&tq->pool,&f); |
daniele | 10:dd7111d4279f | 104 | } |
daniele | 10:dd7111d4279f | 105 | |
daniele | 10:dd7111d4279f | 106 | static struct pico_frame *first_segment(struct pico_tcp_queue *tq) |
daniele | 10:dd7111d4279f | 107 | { |
daniele | 10:dd7111d4279f | 108 | return pico_tree_first(&tq->pool); |
daniele | 10:dd7111d4279f | 109 | } |
daniele | 10:dd7111d4279f | 110 | |
daniele | 10:dd7111d4279f | 111 | static struct pico_frame *next_segment(struct pico_tcp_queue *tq, struct pico_frame *cur) |
daniele | 10:dd7111d4279f | 112 | { |
daniele | 10:dd7111d4279f | 113 | if (!cur) |
daniele | 10:dd7111d4279f | 114 | return NULL; |
daniele | 10:dd7111d4279f | 115 | return peek_segment(tq, SEQN(cur) + cur->payload_len); |
daniele | 10:dd7111d4279f | 116 | } |
daniele | 10:dd7111d4279f | 117 | |
daniele | 10:dd7111d4279f | 118 | static int pico_enqueue_segment(struct pico_tcp_queue *tq, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 119 | { |
daniele | 10:dd7111d4279f | 120 | int ret = -1; |
daniele | 10:dd7111d4279f | 121 | if (f->payload_len <= 0) { |
daniele | 10:dd7111d4279f | 122 | tcp_dbg("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TRIED TO ENQUEUE INVALID SEGMENT!\n"); |
daniele | 10:dd7111d4279f | 123 | //abort(); |
daniele | 10:dd7111d4279f | 124 | return -1; |
daniele | 10:dd7111d4279f | 125 | } |
daniele | 10:dd7111d4279f | 126 | LOCK(Mutex); |
daniele | 10:dd7111d4279f | 127 | if ((tq->size + f->payload_len) > tq->max_size) |
daniele | 10:dd7111d4279f | 128 | { |
daniele | 10:dd7111d4279f | 129 | ret = 0; |
daniele | 10:dd7111d4279f | 130 | goto out; |
daniele | 10:dd7111d4279f | 131 | } |
daniele | 10:dd7111d4279f | 132 | if (pico_tree_insert(&tq->pool,f) != 0) |
daniele | 10:dd7111d4279f | 133 | { |
daniele | 10:dd7111d4279f | 134 | ret = 0; |
daniele | 10:dd7111d4279f | 135 | goto out; |
daniele | 10:dd7111d4279f | 136 | } |
daniele | 10:dd7111d4279f | 137 | tq->size += f->payload_len; |
daniele | 10:dd7111d4279f | 138 | if (f->payload_len > 0) |
daniele | 10:dd7111d4279f | 139 | tq->frames++; |
daniele | 10:dd7111d4279f | 140 | ret = f->payload_len; |
daniele | 10:dd7111d4279f | 141 | |
daniele | 10:dd7111d4279f | 142 | out : |
daniele | 10:dd7111d4279f | 143 | UNLOCK(Mutex); |
daniele | 10:dd7111d4279f | 144 | return ret; |
daniele | 10:dd7111d4279f | 145 | } |
daniele | 10:dd7111d4279f | 146 | |
daniele | 10:dd7111d4279f | 147 | static void pico_discard_segment(struct pico_tcp_queue *tq, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 148 | { |
daniele | 10:dd7111d4279f | 149 | struct pico_frame *f1; |
daniele | 10:dd7111d4279f | 150 | LOCK(Mutex); |
daniele | 10:dd7111d4279f | 151 | f1 = pico_tree_delete(&tq->pool,f); |
daniele | 10:dd7111d4279f | 152 | if (f1) { |
daniele | 10:dd7111d4279f | 153 | tq->size -= f->payload_len; |
daniele | 10:dd7111d4279f | 154 | if (f->payload_len > 0) |
daniele | 10:dd7111d4279f | 155 | tq->frames--; |
daniele | 10:dd7111d4279f | 156 | } |
daniele | 10:dd7111d4279f | 157 | pico_frame_discard(f); |
daniele | 10:dd7111d4279f | 158 | UNLOCK(Mutex); |
daniele | 10:dd7111d4279f | 159 | } |
daniele | 10:dd7111d4279f | 160 | |
daniele | 10:dd7111d4279f | 161 | /* Structure for TCP socket */ |
daniele | 10:dd7111d4279f | 162 | struct tcp_sack_block { |
daniele | 10:dd7111d4279f | 163 | uint32_t left; |
daniele | 10:dd7111d4279f | 164 | uint32_t right; |
daniele | 10:dd7111d4279f | 165 | struct tcp_sack_block *next; |
daniele | 10:dd7111d4279f | 166 | }; |
daniele | 10:dd7111d4279f | 167 | |
daniele | 10:dd7111d4279f | 168 | struct pico_socket_tcp { |
daniele | 10:dd7111d4279f | 169 | struct pico_socket sock; |
daniele | 10:dd7111d4279f | 170 | |
daniele | 10:dd7111d4279f | 171 | /* Tree/queues */ |
daniele | 10:dd7111d4279f | 172 | struct pico_tcp_queue tcpq_in; |
daniele | 10:dd7111d4279f | 173 | struct pico_tcp_queue tcpq_out; |
daniele | 10:dd7111d4279f | 174 | struct pico_tcp_queue tcpq_hold; /* buffer to hold delayed frames according to Nagle */ |
daniele | 10:dd7111d4279f | 175 | |
daniele | 10:dd7111d4279f | 176 | /* tcp_output */ |
daniele | 10:dd7111d4279f | 177 | uint32_t snd_nxt; |
daniele | 10:dd7111d4279f | 178 | uint32_t snd_last; |
daniele | 10:dd7111d4279f | 179 | uint32_t snd_old_ack; |
daniele | 10:dd7111d4279f | 180 | uint32_t snd_retry; |
daniele | 10:dd7111d4279f | 181 | uint32_t snd_last_out; |
daniele | 10:dd7111d4279f | 182 | |
daniele | 10:dd7111d4279f | 183 | /* congestion control */ |
daniele | 10:dd7111d4279f | 184 | uint32_t avg_rtt; |
daniele | 10:dd7111d4279f | 185 | uint32_t rttvar; |
daniele | 10:dd7111d4279f | 186 | uint32_t rto; |
daniele | 10:dd7111d4279f | 187 | uint32_t in_flight; |
daniele | 10:dd7111d4279f | 188 | uint8_t timer_running; |
daniele | 10:dd7111d4279f | 189 | uint8_t keepalive_timer_running; |
daniele | 10:dd7111d4279f | 190 | uint16_t cwnd_counter; |
daniele | 10:dd7111d4279f | 191 | uint16_t cwnd; |
daniele | 10:dd7111d4279f | 192 | uint16_t ssthresh; |
daniele | 10:dd7111d4279f | 193 | uint16_t recv_wnd; |
daniele | 10:dd7111d4279f | 194 | uint16_t recv_wnd_scale; |
daniele | 10:dd7111d4279f | 195 | |
daniele | 10:dd7111d4279f | 196 | /* tcp_input */ |
daniele | 10:dd7111d4279f | 197 | uint32_t rcv_nxt; |
daniele | 10:dd7111d4279f | 198 | uint32_t rcv_ackd; |
daniele | 10:dd7111d4279f | 199 | uint32_t rcv_processed; |
daniele | 10:dd7111d4279f | 200 | uint16_t wnd; |
daniele | 10:dd7111d4279f | 201 | uint16_t wnd_scale; |
daniele | 10:dd7111d4279f | 202 | |
daniele | 10:dd7111d4279f | 203 | /* options */ |
daniele | 10:dd7111d4279f | 204 | uint32_t ts_nxt; |
daniele | 10:dd7111d4279f | 205 | uint16_t mss; |
daniele | 10:dd7111d4279f | 206 | uint8_t sack_ok; |
daniele | 10:dd7111d4279f | 207 | uint8_t ts_ok; |
daniele | 10:dd7111d4279f | 208 | uint8_t mss_ok; |
daniele | 10:dd7111d4279f | 209 | uint8_t scale_ok; |
daniele | 10:dd7111d4279f | 210 | struct tcp_sack_block *sacks; |
daniele | 10:dd7111d4279f | 211 | uint8_t jumbo; |
daniele | 10:dd7111d4279f | 212 | |
daniele | 10:dd7111d4279f | 213 | /* Transmission */ |
daniele | 10:dd7111d4279f | 214 | uint8_t x_mode; |
daniele | 10:dd7111d4279f | 215 | uint8_t dupacks; |
daniele | 10:dd7111d4279f | 216 | uint8_t backoff; |
tass | 31:d3b2dfcc358f | 217 | /* Close wait flag */ |
tass | 31:d3b2dfcc358f | 218 | uint8_t closing; |
tass | 31:d3b2dfcc358f | 219 | }; |
daniele | 10:dd7111d4279f | 220 | |
tass | 31:d3b2dfcc358f | 221 | static void checkIfQueueEmptyAndFin(struct pico_socket_tcp * t); |
daniele | 10:dd7111d4279f | 222 | |
daniele | 10:dd7111d4279f | 223 | /* Queues */ |
daniele | 10:dd7111d4279f | 224 | static struct pico_queue tcp_in = {}; |
daniele | 10:dd7111d4279f | 225 | static struct pico_queue tcp_out = {}; |
daniele | 10:dd7111d4279f | 226 | |
daniele | 10:dd7111d4279f | 227 | /* If Nagle enabled, this function can make 1 new segment from smaller segments in hold queue */ |
daniele | 10:dd7111d4279f | 228 | static struct pico_frame * pico_hold_segment_make(struct pico_socket_tcp *t); |
daniele | 10:dd7111d4279f | 229 | |
daniele | 10:dd7111d4279f | 230 | /* checks if tcpq_in is empty */ |
daniele | 10:dd7111d4279f | 231 | int pico_tcp_queue_in_is_empty(struct pico_socket *s) |
daniele | 10:dd7111d4279f | 232 | { |
daniele | 10:dd7111d4279f | 233 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
daniele | 10:dd7111d4279f | 234 | |
daniele | 10:dd7111d4279f | 235 | if (t->tcpq_in.frames == 0) |
daniele | 10:dd7111d4279f | 236 | return 1; |
daniele | 10:dd7111d4279f | 237 | else |
daniele | 10:dd7111d4279f | 238 | return 0; |
daniele | 10:dd7111d4279f | 239 | } |
daniele | 10:dd7111d4279f | 240 | |
daniele | 10:dd7111d4279f | 241 | /* Useful for getting rid of the beginning of the buffer (read() op) */ |
daniele | 10:dd7111d4279f | 242 | static int release_until(struct pico_tcp_queue *q, uint32_t seq) |
daniele | 10:dd7111d4279f | 243 | { |
daniele | 10:dd7111d4279f | 244 | struct pico_frame *head = first_segment(q); |
daniele | 10:dd7111d4279f | 245 | int ret = 0; |
daniele | 10:dd7111d4279f | 246 | while (head && (seq_compare(SEQN(head) + head->payload_len, seq) <= 0)) { |
daniele | 10:dd7111d4279f | 247 | struct pico_frame *cur = head; |
daniele | 10:dd7111d4279f | 248 | head = next_segment(q, cur); |
daniele | 10:dd7111d4279f | 249 | tcp_dbg("Releasing %p\n", q); |
daniele | 10:dd7111d4279f | 250 | pico_discard_segment(q, cur); |
daniele | 10:dd7111d4279f | 251 | ret++; |
daniele | 10:dd7111d4279f | 252 | } |
daniele | 10:dd7111d4279f | 253 | return ret; |
daniele | 10:dd7111d4279f | 254 | } |
daniele | 10:dd7111d4279f | 255 | |
daniele | 10:dd7111d4279f | 256 | static int release_all_until(struct pico_tcp_queue *q, uint32_t seq) |
daniele | 10:dd7111d4279f | 257 | { |
daniele | 10:dd7111d4279f | 258 | struct pico_frame *f = NULL, *tmp __attribute__((unused)); |
daniele | 10:dd7111d4279f | 259 | struct pico_tree_node * idx, * temp; |
daniele | 10:dd7111d4279f | 260 | int ret = 0; |
daniele | 10:dd7111d4279f | 261 | |
daniele | 10:dd7111d4279f | 262 | pico_tree_foreach_safe(idx,&q->pool,temp){ |
daniele | 10:dd7111d4279f | 263 | f = idx->keyValue; |
daniele | 10:dd7111d4279f | 264 | if (seq_compare(SEQN(f) + f->payload_len, seq) <= 0) { |
daniele | 10:dd7111d4279f | 265 | tcp_dbg("Releasing %p\n", f); |
daniele | 10:dd7111d4279f | 266 | pico_discard_segment(q, f); |
daniele | 10:dd7111d4279f | 267 | ret++; |
daniele | 10:dd7111d4279f | 268 | } else |
daniele | 10:dd7111d4279f | 269 | return ret; |
daniele | 10:dd7111d4279f | 270 | } |
daniele | 10:dd7111d4279f | 271 | return ret; |
daniele | 10:dd7111d4279f | 272 | } |
daniele | 10:dd7111d4279f | 273 | |
daniele | 10:dd7111d4279f | 274 | /* API calls */ |
daniele | 10:dd7111d4279f | 275 | |
daniele | 10:dd7111d4279f | 276 | uint16_t pico_tcp_checksum_ipv4(struct pico_frame *f) |
daniele | 10:dd7111d4279f | 277 | { |
daniele | 10:dd7111d4279f | 278 | struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; |
daniele | 10:dd7111d4279f | 279 | struct pico_tcp_hdr *tcp_hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
daniele | 10:dd7111d4279f | 280 | struct pico_socket *s = f->sock; |
daniele | 10:dd7111d4279f | 281 | struct pico_ipv4_pseudo_hdr pseudo; |
daniele | 10:dd7111d4279f | 282 | |
daniele | 10:dd7111d4279f | 283 | if (s) { |
daniele | 10:dd7111d4279f | 284 | /* Case of outgoing frame */ |
daniele | 10:dd7111d4279f | 285 | //dbg("TCP CRC: on outgoing frame\n"); |
daniele | 10:dd7111d4279f | 286 | pseudo.src.addr = s->local_addr.ip4.addr; |
daniele | 10:dd7111d4279f | 287 | pseudo.dst.addr = s->remote_addr.ip4.addr; |
daniele | 10:dd7111d4279f | 288 | } else { |
daniele | 10:dd7111d4279f | 289 | /* Case of incomming frame */ |
daniele | 10:dd7111d4279f | 290 | //dbg("TCP CRC: on incomming frame\n"); |
daniele | 10:dd7111d4279f | 291 | pseudo.src.addr = hdr->src.addr; |
daniele | 10:dd7111d4279f | 292 | pseudo.dst.addr = hdr->dst.addr; |
daniele | 10:dd7111d4279f | 293 | } |
daniele | 10:dd7111d4279f | 294 | pseudo.zeros = 0; |
daniele | 10:dd7111d4279f | 295 | pseudo.proto = PICO_PROTO_TCP; |
daniele | 10:dd7111d4279f | 296 | pseudo.len = short_be(f->transport_len); |
daniele | 10:dd7111d4279f | 297 | |
daniele | 10:dd7111d4279f | 298 | return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv4_pseudo_hdr), tcp_hdr, f->transport_len); |
daniele | 10:dd7111d4279f | 299 | } |
daniele | 10:dd7111d4279f | 300 | |
daniele | 10:dd7111d4279f | 301 | static int pico_tcp_process_out(struct pico_protocol *self, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 302 | { |
daniele | 10:dd7111d4279f | 303 | struct pico_tcp_hdr *hdr; |
daniele | 10:dd7111d4279f | 304 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)f->sock; |
daniele | 10:dd7111d4279f | 305 | hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
daniele | 10:dd7111d4279f | 306 | |
daniele | 10:dd7111d4279f | 307 | if (f->payload_len > 0) { |
daniele | 10:dd7111d4279f | 308 | tcp_dbg("Process out: sending %p (%d bytes)\n",f, f->payload_len); |
daniele | 10:dd7111d4279f | 309 | } else { |
daniele | 10:dd7111d4279f | 310 | tcp_dbg("Sending empty packet\n"); |
daniele | 10:dd7111d4279f | 311 | } |
daniele | 10:dd7111d4279f | 312 | |
daniele | 10:dd7111d4279f | 313 | if (f->payload_len > 0) { |
daniele | 10:dd7111d4279f | 314 | if (seq_compare(SEQN(f) + f->payload_len, t->snd_nxt) > 0) { |
daniele | 10:dd7111d4279f | 315 | t->snd_nxt = SEQN(f) + f->payload_len; |
daniele | 10:dd7111d4279f | 316 | tcp_dbg("%s: snd_nxt is now %08x\n", __FUNCTION__, t->snd_nxt); |
daniele | 10:dd7111d4279f | 317 | } |
daniele | 10:dd7111d4279f | 318 | } else if (hdr->flags == PICO_TCP_ACK) { /* pure ack */ |
daniele | 10:dd7111d4279f | 319 | hdr->seq = long_be(t->snd_nxt); |
daniele | 10:dd7111d4279f | 320 | } else { |
daniele | 10:dd7111d4279f | 321 | tcp_dbg("%s: non-pure ACK with len=0, fl:%04x\n", __FUNCTION__, hdr->flags); |
daniele | 10:dd7111d4279f | 322 | } |
daniele | 10:dd7111d4279f | 323 | pico_network_send(f); |
daniele | 10:dd7111d4279f | 324 | return 0; |
daniele | 10:dd7111d4279f | 325 | } |
daniele | 10:dd7111d4279f | 326 | |
daniele | 10:dd7111d4279f | 327 | int pico_tcp_push(struct pico_protocol *self, struct pico_frame *data); |
daniele | 10:dd7111d4279f | 328 | |
daniele | 10:dd7111d4279f | 329 | /* Interface: protocol definition */ |
daniele | 10:dd7111d4279f | 330 | struct pico_protocol pico_proto_tcp = { |
daniele | 10:dd7111d4279f | 331 | .name = "tcp", |
daniele | 10:dd7111d4279f | 332 | .proto_number = PICO_PROTO_TCP, |
daniele | 10:dd7111d4279f | 333 | .layer = PICO_LAYER_TRANSPORT, |
daniele | 10:dd7111d4279f | 334 | .process_in = pico_transport_process_in, |
daniele | 10:dd7111d4279f | 335 | .process_out = pico_tcp_process_out, |
daniele | 10:dd7111d4279f | 336 | .push = pico_tcp_push, |
daniele | 10:dd7111d4279f | 337 | .q_in = &tcp_in, |
daniele | 10:dd7111d4279f | 338 | .q_out = &tcp_out, |
daniele | 10:dd7111d4279f | 339 | }; |
daniele | 10:dd7111d4279f | 340 | |
daniele | 10:dd7111d4279f | 341 | static uint32_t pico_paws(void) |
daniele | 10:dd7111d4279f | 342 | { |
daniele | 10:dd7111d4279f | 343 | static unsigned long _paws = 0; |
daniele | 10:dd7111d4279f | 344 | _paws = pico_rand(); |
daniele | 10:dd7111d4279f | 345 | return long_be(_paws); /*XXX: implement paws */ |
daniele | 10:dd7111d4279f | 346 | } |
daniele | 10:dd7111d4279f | 347 | |
daniele | 10:dd7111d4279f | 348 | static void tcp_add_options(struct pico_socket_tcp *ts, struct pico_frame *f, uint16_t flags, int optsiz) |
daniele | 10:dd7111d4279f | 349 | { |
daniele | 10:dd7111d4279f | 350 | uint32_t tsval = long_be(pico_tick); |
daniele | 10:dd7111d4279f | 351 | uint32_t tsecr = long_be(ts->ts_nxt); |
daniele | 10:dd7111d4279f | 352 | int i = 0; |
daniele | 10:dd7111d4279f | 353 | f->start = f->transport_hdr + PICO_SIZE_TCPHDR; |
daniele | 10:dd7111d4279f | 354 | |
daniele | 10:dd7111d4279f | 355 | memset(f->start, PICO_TCP_OPTION_NOOP, optsiz); /* fill blanks with noop */ |
daniele | 10:dd7111d4279f | 356 | |
daniele | 10:dd7111d4279f | 357 | if (flags & PICO_TCP_SYN) { |
daniele | 10:dd7111d4279f | 358 | f->start[i++] = PICO_TCP_OPTION_MSS; |
daniele | 10:dd7111d4279f | 359 | f->start[i++] = PICO_TCPOPTLEN_MSS; |
daniele | 10:dd7111d4279f | 360 | f->start[i++] = (ts->mss >> 8) & 0xFF; |
daniele | 10:dd7111d4279f | 361 | f->start[i++] = ts->mss & 0xFF; |
daniele | 10:dd7111d4279f | 362 | f->start[i++] = PICO_TCP_OPTION_SACK_OK; |
daniele | 10:dd7111d4279f | 363 | f->start[i++] = PICO_TCPOPTLEN_SACK_OK; |
daniele | 10:dd7111d4279f | 364 | } |
daniele | 10:dd7111d4279f | 365 | |
daniele | 10:dd7111d4279f | 366 | f->start[i++] = PICO_TCP_OPTION_WS; |
daniele | 10:dd7111d4279f | 367 | f->start[i++] = PICO_TCPOPTLEN_WS; |
daniele | 10:dd7111d4279f | 368 | f->start[i++] = ts->wnd_scale; |
daniele | 10:dd7111d4279f | 369 | |
daniele | 10:dd7111d4279f | 370 | if (optsiz >= 12) { |
daniele | 10:dd7111d4279f | 371 | f->start[i++] = PICO_TCP_OPTION_TIMESTAMP; |
daniele | 10:dd7111d4279f | 372 | f->start[i++] = PICO_TCPOPTLEN_TIMESTAMP; |
daniele | 10:dd7111d4279f | 373 | memcpy(f->start + i, &tsval, 4); |
daniele | 10:dd7111d4279f | 374 | i += 4; |
daniele | 10:dd7111d4279f | 375 | memcpy(f->start + i, &tsecr, 4); |
daniele | 10:dd7111d4279f | 376 | i += 4; |
daniele | 10:dd7111d4279f | 377 | } |
daniele | 10:dd7111d4279f | 378 | |
daniele | 10:dd7111d4279f | 379 | if (flags & PICO_TCP_ACK) { |
daniele | 10:dd7111d4279f | 380 | struct tcp_sack_block *sb; |
daniele | 10:dd7111d4279f | 381 | int len_off; |
daniele | 10:dd7111d4279f | 382 | |
daniele | 10:dd7111d4279f | 383 | if (ts->sack_ok && ts->sacks) { |
daniele | 10:dd7111d4279f | 384 | f->start[i++] = PICO_TCP_OPTION_SACK; |
daniele | 10:dd7111d4279f | 385 | len_off = i; |
daniele | 10:dd7111d4279f | 386 | f->start[i++] = PICO_TCPOPTLEN_SACK; |
daniele | 10:dd7111d4279f | 387 | while(ts->sacks) { |
daniele | 10:dd7111d4279f | 388 | sb = ts->sacks; |
daniele | 10:dd7111d4279f | 389 | ts->sacks = sb->next; |
daniele | 10:dd7111d4279f | 390 | memcpy(f->start + i, sb, 2 * sizeof(uint32_t)); |
daniele | 10:dd7111d4279f | 391 | i += (2 * sizeof(uint32_t)); |
daniele | 10:dd7111d4279f | 392 | f->start[len_off] += (2 * sizeof(uint32_t)); |
daniele | 10:dd7111d4279f | 393 | pico_free(sb); |
daniele | 10:dd7111d4279f | 394 | } |
daniele | 10:dd7111d4279f | 395 | } |
daniele | 10:dd7111d4279f | 396 | } |
daniele | 10:dd7111d4279f | 397 | if (i < optsiz) |
daniele | 10:dd7111d4279f | 398 | f->start[ optsiz - 1 ] = PICO_TCP_OPTION_END; |
daniele | 10:dd7111d4279f | 399 | } |
daniele | 10:dd7111d4279f | 400 | |
daniele | 10:dd7111d4279f | 401 | static void tcp_send_ack(struct pico_socket_tcp *t); |
daniele | 10:dd7111d4279f | 402 | |
daniele | 10:dd7111d4279f | 403 | static void tcp_set_space(struct pico_socket_tcp *t) |
daniele | 10:dd7111d4279f | 404 | { |
daniele | 10:dd7111d4279f | 405 | int mtu, space; |
daniele | 10:dd7111d4279f | 406 | int shift = 0; |
daniele | 10:dd7111d4279f | 407 | |
daniele | 10:dd7111d4279f | 408 | mtu = t->mss + PICO_SIZE_TCPHDR + PICO_SIZE_TCPOPT_SYN ; |
daniele | 10:dd7111d4279f | 409 | if (t->tcpq_in.max_size == 0) { |
daniele | 10:dd7111d4279f | 410 | space = 1024 * 1024 * 1024; /* One Gigabyte, for unlimited sockets. */ |
daniele | 10:dd7111d4279f | 411 | } else { |
daniele | 10:dd7111d4279f | 412 | space = ((t->tcpq_in.max_size - t->tcpq_in.size) / mtu) * t->mss; |
daniele | 10:dd7111d4279f | 413 | } |
daniele | 10:dd7111d4279f | 414 | if (space < 0) |
daniele | 10:dd7111d4279f | 415 | space = 0; |
daniele | 10:dd7111d4279f | 416 | while(space > 0xFFFF) { |
daniele | 10:dd7111d4279f | 417 | space >>= 1; |
daniele | 10:dd7111d4279f | 418 | shift++; |
daniele | 10:dd7111d4279f | 419 | } |
daniele | 10:dd7111d4279f | 420 | if ((space != t->wnd) || (shift != t->wnd_scale) || ((space - t->wnd) > (space>>2))) { |
daniele | 10:dd7111d4279f | 421 | t->wnd = space; |
daniele | 10:dd7111d4279f | 422 | t->wnd_scale = shift; |
daniele | 10:dd7111d4279f | 423 | } |
daniele | 10:dd7111d4279f | 424 | } |
daniele | 10:dd7111d4279f | 425 | |
daniele | 10:dd7111d4279f | 426 | /* Return 32-bit aligned option size */ |
daniele | 10:dd7111d4279f | 427 | static int tcp_options_size(struct pico_socket_tcp *t, uint16_t flags) |
daniele | 10:dd7111d4279f | 428 | { |
daniele | 10:dd7111d4279f | 429 | int size = 0; |
daniele | 10:dd7111d4279f | 430 | struct tcp_sack_block *sb = t->sacks; |
daniele | 10:dd7111d4279f | 431 | |
daniele | 10:dd7111d4279f | 432 | if (flags & PICO_TCP_SYN) { /* Full options */ |
daniele | 10:dd7111d4279f | 433 | size = PICO_TCPOPTLEN_MSS + PICO_TCP_OPTION_SACK_OK + PICO_TCPOPTLEN_WS + PICO_TCPOPTLEN_TIMESTAMP; |
daniele | 10:dd7111d4279f | 434 | } else { |
daniele | 10:dd7111d4279f | 435 | |
daniele | 10:dd7111d4279f | 436 | /* Always update window scale. */ |
daniele | 10:dd7111d4279f | 437 | size += PICO_TCPOPTLEN_WS; |
daniele | 10:dd7111d4279f | 438 | |
daniele | 10:dd7111d4279f | 439 | if (t->ts_ok) |
daniele | 10:dd7111d4279f | 440 | size += PICO_TCPOPTLEN_TIMESTAMP; |
daniele | 10:dd7111d4279f | 441 | |
daniele | 10:dd7111d4279f | 442 | size+= PICO_TCPOPTLEN_END; |
daniele | 10:dd7111d4279f | 443 | } |
daniele | 10:dd7111d4279f | 444 | if ((flags & PICO_TCP_ACK) && (t->sack_ok && sb)) { |
daniele | 10:dd7111d4279f | 445 | size += 2; |
daniele | 10:dd7111d4279f | 446 | while(sb) { |
daniele | 10:dd7111d4279f | 447 | size += (2 * sizeof(uint32_t)); |
daniele | 10:dd7111d4279f | 448 | sb = sb->next; |
daniele | 10:dd7111d4279f | 449 | } |
daniele | 10:dd7111d4279f | 450 | } |
daniele | 10:dd7111d4279f | 451 | size = (((size + 3) >> 2) << 2); |
daniele | 10:dd7111d4279f | 452 | return size; |
daniele | 10:dd7111d4279f | 453 | } |
daniele | 10:dd7111d4279f | 454 | |
daniele | 10:dd7111d4279f | 455 | int pico_tcp_overhead(struct pico_socket *s) |
daniele | 10:dd7111d4279f | 456 | { |
daniele | 10:dd7111d4279f | 457 | if (!s) |
daniele | 10:dd7111d4279f | 458 | return 0; |
daniele | 10:dd7111d4279f | 459 | |
daniele | 10:dd7111d4279f | 460 | return PICO_SIZE_TCPHDR + tcp_options_size((struct pico_socket_tcp *)s, 0); /* hdr + Options size for data pkt */ |
daniele | 10:dd7111d4279f | 461 | |
daniele | 10:dd7111d4279f | 462 | } |
daniele | 10:dd7111d4279f | 463 | |
daniele | 10:dd7111d4279f | 464 | static void tcp_process_sack(struct pico_socket_tcp *t, uint32_t start, uint32_t end) |
daniele | 10:dd7111d4279f | 465 | { |
daniele | 10:dd7111d4279f | 466 | struct pico_frame *f; |
daniele | 10:dd7111d4279f | 467 | struct pico_tree_node * index, * temp; |
daniele | 10:dd7111d4279f | 468 | int cmp; |
daniele | 10:dd7111d4279f | 469 | int count = 0; |
daniele | 10:dd7111d4279f | 470 | |
daniele | 10:dd7111d4279f | 471 | pico_tree_foreach_safe(index,&t->tcpq_out.pool,temp){ |
daniele | 10:dd7111d4279f | 472 | f = index->keyValue; |
daniele | 10:dd7111d4279f | 473 | cmp = seq_compare(SEQN(f), start); |
daniele | 10:dd7111d4279f | 474 | if (cmp > 0) |
daniele | 10:dd7111d4279f | 475 | goto done; |
daniele | 10:dd7111d4279f | 476 | |
daniele | 10:dd7111d4279f | 477 | if (cmp == 0) { |
daniele | 10:dd7111d4279f | 478 | cmp = seq_compare(SEQN(f) + f->payload_len, end); |
daniele | 10:dd7111d4279f | 479 | if (cmp > 0) { |
daniele | 10:dd7111d4279f | 480 | tcp_dbg("Invalid SACK: ignoring.\n"); |
daniele | 10:dd7111d4279f | 481 | } |
daniele | 10:dd7111d4279f | 482 | |
daniele | 10:dd7111d4279f | 483 | tcp_dbg("Marking (by SACK) segment %08x BLK:[%08x::%08x]\n", SEQN(f), start, end); |
daniele | 10:dd7111d4279f | 484 | f->flags |= PICO_FRAME_FLAG_SACKED; |
daniele | 10:dd7111d4279f | 485 | count++; |
daniele | 10:dd7111d4279f | 486 | |
daniele | 10:dd7111d4279f | 487 | if (cmp == 0) { |
daniele | 10:dd7111d4279f | 488 | /* that was last segment sacked. Job done */ |
daniele | 10:dd7111d4279f | 489 | goto done; |
daniele | 10:dd7111d4279f | 490 | } |
daniele | 10:dd7111d4279f | 491 | } |
daniele | 10:dd7111d4279f | 492 | } |
daniele | 10:dd7111d4279f | 493 | |
daniele | 10:dd7111d4279f | 494 | done: |
daniele | 10:dd7111d4279f | 495 | if (t->x_mode > PICO_TCP_LOOKAHEAD) { |
daniele | 10:dd7111d4279f | 496 | if (t->in_flight > (count)) |
daniele | 10:dd7111d4279f | 497 | t->in_flight -= (count); |
daniele | 10:dd7111d4279f | 498 | else |
daniele | 10:dd7111d4279f | 499 | t->in_flight = 0; |
daniele | 10:dd7111d4279f | 500 | } |
daniele | 10:dd7111d4279f | 501 | } |
daniele | 10:dd7111d4279f | 502 | |
daniele | 10:dd7111d4279f | 503 | static void tcp_rcv_sack(struct pico_socket_tcp *t, uint8_t *opt, int len) |
daniele | 10:dd7111d4279f | 504 | { |
daniele | 10:dd7111d4279f | 505 | uint32_t start, end; |
daniele | 10:dd7111d4279f | 506 | int i = 0; |
daniele | 10:dd7111d4279f | 507 | if (len % 8) { |
daniele | 10:dd7111d4279f | 508 | tcp_dbg("SACK: Invalid len.\n"); |
daniele | 10:dd7111d4279f | 509 | return; |
daniele | 10:dd7111d4279f | 510 | } |
daniele | 10:dd7111d4279f | 511 | while (i < len) { |
daniele | 10:dd7111d4279f | 512 | start = long_from(opt + i); |
daniele | 10:dd7111d4279f | 513 | i += 4; |
daniele | 10:dd7111d4279f | 514 | end = long_from(opt + i); |
daniele | 10:dd7111d4279f | 515 | i += 4; |
daniele | 10:dd7111d4279f | 516 | tcp_process_sack(t, long_be(start), long_be(end)); |
daniele | 10:dd7111d4279f | 517 | } |
daniele | 10:dd7111d4279f | 518 | } |
daniele | 10:dd7111d4279f | 519 | |
daniele | 10:dd7111d4279f | 520 | static void tcp_parse_options(struct pico_frame *f) |
daniele | 10:dd7111d4279f | 521 | { |
daniele | 10:dd7111d4279f | 522 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)f->sock; |
daniele | 10:dd7111d4279f | 523 | uint8_t *opt = f->transport_hdr + PICO_SIZE_TCPHDR; |
daniele | 10:dd7111d4279f | 524 | int i = 0; |
daniele | 10:dd7111d4279f | 525 | while (i < (f->transport_len - PICO_SIZE_TCPHDR)) { |
daniele | 10:dd7111d4279f | 526 | uint8_t type = opt[i++]; |
daniele | 10:dd7111d4279f | 527 | uint8_t len; |
daniele | 10:dd7111d4279f | 528 | if(i < (f->transport_len - PICO_SIZE_TCPHDR) && (type > 1)) |
daniele | 10:dd7111d4279f | 529 | len = opt[i++]; |
daniele | 10:dd7111d4279f | 530 | else |
daniele | 10:dd7111d4279f | 531 | len = 1; |
daniele | 10:dd7111d4279f | 532 | if (f->payload && ((opt + i) > f->payload)) |
daniele | 10:dd7111d4279f | 533 | break; |
daniele | 10:dd7111d4279f | 534 | tcp_dbg("Received option '%d', len = %d \n", type, len); |
daniele | 10:dd7111d4279f | 535 | switch (type) { |
daniele | 10:dd7111d4279f | 536 | case PICO_TCP_OPTION_NOOP: |
daniele | 10:dd7111d4279f | 537 | case PICO_TCP_OPTION_END: |
daniele | 10:dd7111d4279f | 538 | break; |
daniele | 10:dd7111d4279f | 539 | case PICO_TCP_OPTION_WS: |
daniele | 10:dd7111d4279f | 540 | if (len != PICO_TCPOPTLEN_WS) { |
daniele | 10:dd7111d4279f | 541 | tcp_dbg("TCP Window scale: bad len received (%d).\n", len); |
daniele | 10:dd7111d4279f | 542 | i += len - 2; |
daniele | 10:dd7111d4279f | 543 | break; |
daniele | 10:dd7111d4279f | 544 | } |
daniele | 10:dd7111d4279f | 545 | t->recv_wnd_scale = opt[i++]; |
daniele | 10:dd7111d4279f | 546 | tcp_dbg("TCP Window scale: received %d\n", t->recv_wnd_scale); |
daniele | 10:dd7111d4279f | 547 | break; |
daniele | 10:dd7111d4279f | 548 | case PICO_TCP_OPTION_SACK_OK: |
daniele | 10:dd7111d4279f | 549 | if (len != PICO_TCPOPTLEN_SACK_OK) { |
daniele | 10:dd7111d4279f | 550 | tcp_dbg("TCP option sack: bad len received.\n"); |
daniele | 10:dd7111d4279f | 551 | i += len - 2; |
daniele | 10:dd7111d4279f | 552 | break; |
daniele | 10:dd7111d4279f | 553 | } |
daniele | 10:dd7111d4279f | 554 | t->sack_ok = 1; |
daniele | 10:dd7111d4279f | 555 | break; |
daniele | 10:dd7111d4279f | 556 | case PICO_TCP_OPTION_MSS: { |
daniele | 10:dd7111d4279f | 557 | uint16_t mss; |
daniele | 10:dd7111d4279f | 558 | if (len != PICO_TCPOPTLEN_MSS) { |
daniele | 10:dd7111d4279f | 559 | tcp_dbg("TCP option mss: bad len received.\n"); |
daniele | 10:dd7111d4279f | 560 | i += len - 2; |
daniele | 10:dd7111d4279f | 561 | break; |
daniele | 10:dd7111d4279f | 562 | } |
daniele | 10:dd7111d4279f | 563 | t->mss_ok = 1; |
daniele | 10:dd7111d4279f | 564 | mss = short_from(opt + i); |
daniele | 10:dd7111d4279f | 565 | i += sizeof(uint16_t); |
daniele | 10:dd7111d4279f | 566 | if (t->mss > short_be(mss)) |
daniele | 10:dd7111d4279f | 567 | t->mss = short_be(mss); |
daniele | 10:dd7111d4279f | 568 | break; |
daniele | 10:dd7111d4279f | 569 | } |
daniele | 10:dd7111d4279f | 570 | case PICO_TCP_OPTION_TIMESTAMP: { |
daniele | 10:dd7111d4279f | 571 | uint32_t tsval, tsecr; |
daniele | 10:dd7111d4279f | 572 | if (len != PICO_TCPOPTLEN_TIMESTAMP) { |
daniele | 10:dd7111d4279f | 573 | tcp_dbg("TCP option timestamp: bad len received.\n"); |
daniele | 10:dd7111d4279f | 574 | i += len - 2; |
daniele | 10:dd7111d4279f | 575 | break; |
daniele | 10:dd7111d4279f | 576 | } |
daniele | 10:dd7111d4279f | 577 | t->ts_ok = 1; |
daniele | 10:dd7111d4279f | 578 | tsval = long_from(opt + i); |
daniele | 10:dd7111d4279f | 579 | i += sizeof(uint32_t); |
daniele | 10:dd7111d4279f | 580 | tsecr = long_from(opt + i); |
daniele | 10:dd7111d4279f | 581 | f->timestamp = long_be(tsecr); |
daniele | 10:dd7111d4279f | 582 | i += sizeof(uint32_t); |
daniele | 10:dd7111d4279f | 583 | |
daniele | 10:dd7111d4279f | 584 | t->ts_nxt = long_be(tsval); |
daniele | 10:dd7111d4279f | 585 | break; |
daniele | 10:dd7111d4279f | 586 | } |
daniele | 10:dd7111d4279f | 587 | case PICO_TCP_OPTION_SACK: |
daniele | 10:dd7111d4279f | 588 | { |
daniele | 10:dd7111d4279f | 589 | tcp_rcv_sack(t, opt + i, len - 2); |
daniele | 10:dd7111d4279f | 590 | i += len - 2; |
daniele | 10:dd7111d4279f | 591 | break; |
daniele | 10:dd7111d4279f | 592 | } |
daniele | 10:dd7111d4279f | 593 | default: |
daniele | 10:dd7111d4279f | 594 | tcp_dbg("TCP: received unsupported option %u\n", type); |
daniele | 10:dd7111d4279f | 595 | i += len - 2; |
daniele | 10:dd7111d4279f | 596 | } |
daniele | 10:dd7111d4279f | 597 | } |
daniele | 10:dd7111d4279f | 598 | } |
daniele | 10:dd7111d4279f | 599 | |
daniele | 10:dd7111d4279f | 600 | static int tcp_send(struct pico_socket_tcp *ts, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 601 | { |
daniele | 10:dd7111d4279f | 602 | struct pico_tcp_hdr *hdr= (struct pico_tcp_hdr *) f->transport_hdr; |
daniele | 10:dd7111d4279f | 603 | struct pico_frame *cpy; |
daniele | 10:dd7111d4279f | 604 | hdr->trans.sport = ts->sock.local_port; |
daniele | 10:dd7111d4279f | 605 | hdr->trans.dport = ts->sock.remote_port; |
daniele | 10:dd7111d4279f | 606 | if (!hdr->seq) |
daniele | 10:dd7111d4279f | 607 | hdr->seq = long_be(ts->snd_nxt); |
daniele | 10:dd7111d4279f | 608 | |
daniele | 10:dd7111d4279f | 609 | if (ts->rcv_nxt != 0) { |
daniele | 10:dd7111d4279f | 610 | if ( (ts->rcv_ackd == 0) || (seq_compare(ts->rcv_ackd, ts->rcv_nxt) != 0) || (hdr->flags & PICO_TCP_ACK)) { |
daniele | 10:dd7111d4279f | 611 | hdr->flags |= PICO_TCP_ACK; |
daniele | 10:dd7111d4279f | 612 | hdr->ack = long_be(ts->rcv_nxt); |
daniele | 10:dd7111d4279f | 613 | ts->rcv_ackd = ts->rcv_nxt; |
daniele | 10:dd7111d4279f | 614 | } |
daniele | 10:dd7111d4279f | 615 | } |
daniele | 10:dd7111d4279f | 616 | |
daniele | 10:dd7111d4279f | 617 | if (hdr->flags & PICO_TCP_SYN) { |
daniele | 10:dd7111d4279f | 618 | ts->snd_nxt++; |
daniele | 10:dd7111d4279f | 619 | } |
daniele | 10:dd7111d4279f | 620 | if (f->payload_len > 0) { |
daniele | 10:dd7111d4279f | 621 | hdr->flags |= PICO_TCP_PSH | PICO_TCP_ACK; |
daniele | 10:dd7111d4279f | 622 | hdr->ack = long_be(ts->rcv_nxt); |
daniele | 10:dd7111d4279f | 623 | ts->rcv_ackd = ts->rcv_nxt; |
daniele | 10:dd7111d4279f | 624 | } |
daniele | 10:dd7111d4279f | 625 | |
daniele | 10:dd7111d4279f | 626 | f->start = f->transport_hdr + PICO_SIZE_TCPHDR; |
daniele | 10:dd7111d4279f | 627 | hdr->rwnd = short_be(ts->wnd); |
daniele | 10:dd7111d4279f | 628 | hdr->crc = 0; |
daniele | 10:dd7111d4279f | 629 | hdr->crc = short_be(pico_tcp_checksum_ipv4(f)); |
daniele | 10:dd7111d4279f | 630 | |
daniele | 10:dd7111d4279f | 631 | /* TCP: ENQUEUE to PROTO ( Transmit ) */ |
daniele | 10:dd7111d4279f | 632 | cpy = pico_frame_copy(f); |
daniele | 10:dd7111d4279f | 633 | if (pico_enqueue(&tcp_out, cpy) > 0) { |
daniele | 10:dd7111d4279f | 634 | if (f->payload_len > 0) |
daniele | 10:dd7111d4279f | 635 | ts->in_flight++; |
daniele | 10:dd7111d4279f | 636 | tcp_dbg("DBG> [tcp output] state: %02x --> local port:%d remote port: %d seq: %08x ack: %08x flags: %02x = t_len: %d, hdr: %u payload: %d\n", |
daniele | 10:dd7111d4279f | 637 | 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 ); |
daniele | 10:dd7111d4279f | 638 | } else { |
daniele | 10:dd7111d4279f | 639 | pico_frame_discard(cpy); |
daniele | 10:dd7111d4279f | 640 | } |
daniele | 10:dd7111d4279f | 641 | return 0; |
daniele | 10:dd7111d4279f | 642 | } |
daniele | 10:dd7111d4279f | 643 | |
daniele | 10:dd7111d4279f | 644 | //#define PICO_TCP_SUPPORT_SOCKET_STATS |
daniele | 10:dd7111d4279f | 645 | |
daniele | 10:dd7111d4279f | 646 | #ifdef PICO_TCP_SUPPORT_SOCKET_STATS |
daniele | 10:dd7111d4279f | 647 | static void sock_stats(unsigned long when, void *arg) |
daniele | 10:dd7111d4279f | 648 | { |
daniele | 10:dd7111d4279f | 649 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg; |
daniele | 10:dd7111d4279f | 650 | tcp_dbg("STATISTIC> [%lu] socket state: %02x --> local port:%d remote port: %d queue size: %d snd_una: %08x snd_nxt: %08x timer: %d cwnd: %d\n", |
daniele | 10:dd7111d4279f | 651 | when, t->sock.state, short_be(t->sock.local_port), short_be(t->sock.remote_port), t->tcpq_out.size, SEQN(first_segment(&t->tcpq_out)), t->snd_nxt, t->timer_running, t->cwnd); |
daniele | 10:dd7111d4279f | 652 | pico_timer_add(2000, sock_stats, t); |
daniele | 10:dd7111d4279f | 653 | } |
daniele | 10:dd7111d4279f | 654 | #endif |
daniele | 10:dd7111d4279f | 655 | |
daniele | 10:dd7111d4279f | 656 | struct pico_socket *pico_tcp_open(void) |
daniele | 10:dd7111d4279f | 657 | { |
daniele | 10:dd7111d4279f | 658 | struct pico_socket_tcp *t = pico_zalloc(sizeof(struct pico_socket_tcp)); |
daniele | 10:dd7111d4279f | 659 | if (!t) |
daniele | 10:dd7111d4279f | 660 | return NULL; |
daniele | 10:dd7111d4279f | 661 | t->mss = PICO_TCP_DEFAULT_MSS; |
daniele | 10:dd7111d4279f | 662 | |
daniele | 10:dd7111d4279f | 663 | t->tcpq_in.pool.root = t->tcpq_hold.pool.root = t->tcpq_out.pool.root = &LEAF; |
daniele | 10:dd7111d4279f | 664 | t->tcpq_hold.pool.compare = t->tcpq_in.pool.compare = t->tcpq_out.pool.compare = segment_compare; |
daniele | 10:dd7111d4279f | 665 | |
daniele | 10:dd7111d4279f | 666 | t->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ; |
daniele | 10:dd7111d4279f | 667 | t->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ; |
daniele | 10:dd7111d4279f | 668 | t->tcpq_hold.max_size = 2*PICO_TCP_DEFAULT_MSS; |
daniele | 10:dd7111d4279f | 669 | |
daniele | 10:dd7111d4279f | 670 | /* enable Nagle by default */ |
daniele | 10:dd7111d4279f | 671 | t->sock.opt_flags &= (~(1 << PICO_SOCKET_OPT_TCPNODELAY)); |
daniele | 10:dd7111d4279f | 672 | |
daniele | 10:dd7111d4279f | 673 | #ifdef PICO_TCP_SUPPORT_SOCKET_STATS |
daniele | 10:dd7111d4279f | 674 | pico_timer_add(2000, sock_stats, t); |
daniele | 10:dd7111d4279f | 675 | #endif |
daniele | 10:dd7111d4279f | 676 | tcp_set_space(t); |
daniele | 10:dd7111d4279f | 677 | |
daniele | 10:dd7111d4279f | 678 | return &t->sock; |
daniele | 10:dd7111d4279f | 679 | } |
daniele | 10:dd7111d4279f | 680 | |
daniele | 10:dd7111d4279f | 681 | int pico_tcp_read(struct pico_socket *s, void *buf, int len) |
daniele | 10:dd7111d4279f | 682 | { |
daniele | 10:dd7111d4279f | 683 | struct pico_socket_tcp *t = TCP_SOCK(s); |
daniele | 10:dd7111d4279f | 684 | struct pico_frame *f; |
daniele | 10:dd7111d4279f | 685 | uint32_t in_frame_off, in_frame_len; |
daniele | 10:dd7111d4279f | 686 | int tot_rd_len = 0; |
daniele | 10:dd7111d4279f | 687 | |
daniele | 10:dd7111d4279f | 688 | while (tot_rd_len < len) { |
daniele | 10:dd7111d4279f | 689 | /* To be sure we don't have garbage at the beginning */ |
daniele | 10:dd7111d4279f | 690 | release_until(&t->tcpq_in, t->rcv_processed); |
daniele | 10:dd7111d4279f | 691 | f = first_segment(&t->tcpq_in); |
daniele | 10:dd7111d4279f | 692 | if (!f) { |
daniele | 10:dd7111d4279f | 693 | tcp_set_space(t); |
daniele | 10:dd7111d4279f | 694 | goto out; |
daniele | 10:dd7111d4279f | 695 | } |
daniele | 10:dd7111d4279f | 696 | |
daniele | 10:dd7111d4279f | 697 | /* Hole at the beginning of data, awaiting retransmissions. */ |
daniele | 10:dd7111d4279f | 698 | if (seq_compare(t->rcv_processed, SEQN(f)) < 0) { |
daniele | 10:dd7111d4279f | 699 | tcp_dbg("TCP> read hole beginning of data, %08x - %08x. rcv_nxt is %08x\n",t->rcv_processed, SEQN(f), t->rcv_nxt); |
daniele | 10:dd7111d4279f | 700 | goto out; |
daniele | 10:dd7111d4279f | 701 | } |
daniele | 10:dd7111d4279f | 702 | |
daniele | 10:dd7111d4279f | 703 | if(seq_compare(t->rcv_processed, SEQN(f)) > 0) { |
daniele | 10:dd7111d4279f | 704 | in_frame_off = t->rcv_processed - SEQN(f); |
daniele | 10:dd7111d4279f | 705 | in_frame_len = f->payload_len - in_frame_off; |
daniele | 10:dd7111d4279f | 706 | } else { |
daniele | 10:dd7111d4279f | 707 | in_frame_off = 0; |
daniele | 10:dd7111d4279f | 708 | in_frame_len = f->payload_len; |
daniele | 10:dd7111d4279f | 709 | } |
daniele | 10:dd7111d4279f | 710 | if ((in_frame_len + tot_rd_len) > len) { |
daniele | 10:dd7111d4279f | 711 | in_frame_len = len - tot_rd_len; |
daniele | 10:dd7111d4279f | 712 | } |
daniele | 10:dd7111d4279f | 713 | |
daniele | 10:dd7111d4279f | 714 | if (in_frame_len > f->payload_len - in_frame_off) |
daniele | 10:dd7111d4279f | 715 | in_frame_len = f->payload_len - in_frame_off; |
daniele | 10:dd7111d4279f | 716 | |
tass | 31:d3b2dfcc358f | 717 | memcpy((uint8_t *)buf + tot_rd_len, f->payload + in_frame_off, in_frame_len); |
daniele | 10:dd7111d4279f | 718 | tot_rd_len += in_frame_len; |
daniele | 10:dd7111d4279f | 719 | t->rcv_processed += in_frame_len; |
daniele | 10:dd7111d4279f | 720 | |
daniele | 10:dd7111d4279f | 721 | if ((in_frame_len == 0) || (in_frame_len == f->payload_len)) { |
daniele | 10:dd7111d4279f | 722 | pico_discard_segment(&t->tcpq_in, f); |
daniele | 10:dd7111d4279f | 723 | } |
daniele | 10:dd7111d4279f | 724 | } |
daniele | 10:dd7111d4279f | 725 | |
daniele | 10:dd7111d4279f | 726 | out: |
daniele | 10:dd7111d4279f | 727 | tcp_set_space(t); |
daniele | 10:dd7111d4279f | 728 | if (t->tcpq_in.size == 0) { |
daniele | 10:dd7111d4279f | 729 | s->ev_pending &= (~PICO_SOCK_EV_RD); |
daniele | 10:dd7111d4279f | 730 | } |
daniele | 10:dd7111d4279f | 731 | return tot_rd_len; |
daniele | 10:dd7111d4279f | 732 | } |
daniele | 10:dd7111d4279f | 733 | |
daniele | 10:dd7111d4279f | 734 | int pico_tcp_initconn(struct pico_socket *s); |
daniele | 10:dd7111d4279f | 735 | static void initconn_retry(unsigned long when, void *arg) |
daniele | 10:dd7111d4279f | 736 | { |
daniele | 10:dd7111d4279f | 737 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg; |
daniele | 10:dd7111d4279f | 738 | if (TCPSTATE(&t->sock) == PICO_SOCKET_STATE_TCP_SYN_SENT) { |
daniele | 10:dd7111d4279f | 739 | if (t->backoff > PICO_TCP_MAX_CONNECT_RETRIES) { |
daniele | 10:dd7111d4279f | 740 | tcp_dbg("TCP> Connection timeout. \n"); |
daniele | 10:dd7111d4279f | 741 | if (t->sock.wakeup) |
daniele | 10:dd7111d4279f | 742 | t->sock.wakeup(PICO_SOCK_EV_ERR, &t->sock); |
daniele | 10:dd7111d4279f | 743 | return; |
daniele | 10:dd7111d4279f | 744 | } |
daniele | 10:dd7111d4279f | 745 | tcp_dbg("TCP> SYN retry %d...\n", t->backoff); |
daniele | 10:dd7111d4279f | 746 | t->backoff++; |
daniele | 10:dd7111d4279f | 747 | pico_tcp_initconn(&t->sock); |
daniele | 10:dd7111d4279f | 748 | } else { |
daniele | 10:dd7111d4279f | 749 | tcp_dbg("TCP> Connection is already established: no retry needed. good.\n"); |
daniele | 10:dd7111d4279f | 750 | } |
daniele | 10:dd7111d4279f | 751 | } |
daniele | 10:dd7111d4279f | 752 | |
daniele | 10:dd7111d4279f | 753 | int pico_tcp_initconn(struct pico_socket *s) |
daniele | 10:dd7111d4279f | 754 | { |
daniele | 10:dd7111d4279f | 755 | struct pico_socket_tcp *ts = TCP_SOCK(s); |
daniele | 10:dd7111d4279f | 756 | struct pico_frame *syn; |
daniele | 10:dd7111d4279f | 757 | struct pico_tcp_hdr *hdr; |
daniele | 10:dd7111d4279f | 758 | int opt_len = tcp_options_size(ts, PICO_TCP_SYN); |
daniele | 10:dd7111d4279f | 759 | |
daniele | 10:dd7111d4279f | 760 | syn = s->net->alloc(s->net, PICO_SIZE_TCPHDR + opt_len); |
daniele | 10:dd7111d4279f | 761 | if (!syn) |
daniele | 10:dd7111d4279f | 762 | return -1; |
daniele | 10:dd7111d4279f | 763 | hdr = (struct pico_tcp_hdr *) syn->transport_hdr; |
daniele | 10:dd7111d4279f | 764 | |
daniele | 10:dd7111d4279f | 765 | if (!ts->snd_nxt) |
daniele | 10:dd7111d4279f | 766 | ts->snd_nxt = long_be(pico_paws()); |
daniele | 10:dd7111d4279f | 767 | ts->snd_last = ts->snd_nxt; |
daniele | 10:dd7111d4279f | 768 | ts->cwnd = PICO_TCP_IW; |
daniele | 10:dd7111d4279f | 769 | ts->ssthresh = 40; |
daniele | 10:dd7111d4279f | 770 | syn->sock = s; |
daniele | 10:dd7111d4279f | 771 | hdr->seq = long_be(ts->snd_nxt); |
daniele | 10:dd7111d4279f | 772 | hdr->len = (PICO_SIZE_TCPHDR + opt_len) << 2 | ts->jumbo; |
daniele | 10:dd7111d4279f | 773 | hdr->flags = PICO_TCP_SYN; |
daniele | 10:dd7111d4279f | 774 | tcp_set_space(ts); |
daniele | 10:dd7111d4279f | 775 | hdr->rwnd = short_be(ts->wnd); |
daniele | 10:dd7111d4279f | 776 | tcp_add_options(ts,syn, PICO_TCP_SYN, opt_len); |
daniele | 10:dd7111d4279f | 777 | hdr->trans.sport = ts->sock.local_port; |
daniele | 10:dd7111d4279f | 778 | hdr->trans.dport = ts->sock.remote_port; |
daniele | 10:dd7111d4279f | 779 | |
daniele | 10:dd7111d4279f | 780 | hdr->crc = 0; |
daniele | 10:dd7111d4279f | 781 | hdr->crc = short_be(pico_tcp_checksum_ipv4(syn)); |
daniele | 10:dd7111d4279f | 782 | |
daniele | 10:dd7111d4279f | 783 | /* TCP: ENQUEUE to PROTO ( SYN ) */ |
daniele | 10:dd7111d4279f | 784 | 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); |
daniele | 10:dd7111d4279f | 785 | pico_enqueue(&tcp_out, syn); |
daniele | 10:dd7111d4279f | 786 | pico_timer_add(PICO_TCP_RTO_MIN << ts->backoff, initconn_retry, ts); |
daniele | 10:dd7111d4279f | 787 | return 0; |
daniele | 10:dd7111d4279f | 788 | } |
daniele | 10:dd7111d4279f | 789 | |
daniele | 10:dd7111d4279f | 790 | static int tcp_send_synack(struct pico_socket *s) |
daniele | 10:dd7111d4279f | 791 | { |
daniele | 10:dd7111d4279f | 792 | struct pico_socket_tcp *ts = TCP_SOCK(s); |
daniele | 10:dd7111d4279f | 793 | struct pico_frame *synack; |
daniele | 10:dd7111d4279f | 794 | struct pico_tcp_hdr *hdr; |
daniele | 10:dd7111d4279f | 795 | int opt_len = tcp_options_size(ts, PICO_TCP_SYN | PICO_TCP_ACK); |
daniele | 10:dd7111d4279f | 796 | |
daniele | 10:dd7111d4279f | 797 | synack = s->net->alloc(s->net, PICO_SIZE_TCPHDR + opt_len); |
daniele | 10:dd7111d4279f | 798 | if (!synack) |
daniele | 10:dd7111d4279f | 799 | return -1; |
daniele | 10:dd7111d4279f | 800 | hdr = (struct pico_tcp_hdr *) synack->transport_hdr; |
daniele | 10:dd7111d4279f | 801 | |
daniele | 10:dd7111d4279f | 802 | synack->sock = s; |
daniele | 10:dd7111d4279f | 803 | hdr->len = (PICO_SIZE_TCPHDR + opt_len) << 2 | ts->jumbo; |
daniele | 10:dd7111d4279f | 804 | hdr->flags = PICO_TCP_SYN | PICO_TCP_ACK; |
daniele | 10:dd7111d4279f | 805 | hdr->rwnd = short_be(ts->wnd); |
daniele | 10:dd7111d4279f | 806 | hdr->seq = long_be(ts->snd_nxt); |
daniele | 10:dd7111d4279f | 807 | ts->rcv_processed = long_be(hdr->seq); |
daniele | 10:dd7111d4279f | 808 | ts->snd_last = ts->snd_nxt; |
daniele | 10:dd7111d4279f | 809 | tcp_set_space(ts); |
daniele | 10:dd7111d4279f | 810 | tcp_add_options(ts,synack, hdr->flags, opt_len); |
daniele | 10:dd7111d4279f | 811 | synack->payload_len = 0; |
daniele | 10:dd7111d4279f | 812 | synack->timestamp = pico_tick; |
daniele | 10:dd7111d4279f | 813 | tcp_send(ts, synack); |
daniele | 10:dd7111d4279f | 814 | pico_frame_discard(synack); |
daniele | 10:dd7111d4279f | 815 | return 0; |
daniele | 10:dd7111d4279f | 816 | } |
daniele | 10:dd7111d4279f | 817 | |
daniele | 10:dd7111d4279f | 818 | static void tcp_send_empty(struct pico_socket_tcp *t, uint16_t flags) |
daniele | 10:dd7111d4279f | 819 | { |
daniele | 10:dd7111d4279f | 820 | struct pico_frame *f; |
daniele | 10:dd7111d4279f | 821 | struct pico_tcp_hdr *hdr; |
daniele | 10:dd7111d4279f | 822 | int opt_len = tcp_options_size(t, flags); |
daniele | 10:dd7111d4279f | 823 | f = t->sock.net->alloc(t->sock.net, PICO_SIZE_TCPHDR + opt_len); |
daniele | 10:dd7111d4279f | 824 | if (!f) { |
daniele | 10:dd7111d4279f | 825 | return; |
daniele | 10:dd7111d4279f | 826 | } |
daniele | 10:dd7111d4279f | 827 | f->sock = &t->sock; |
daniele | 10:dd7111d4279f | 828 | hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
daniele | 10:dd7111d4279f | 829 | hdr->len = (PICO_SIZE_TCPHDR + opt_len) << 2 | t->jumbo; |
daniele | 10:dd7111d4279f | 830 | hdr->flags = flags; |
daniele | 10:dd7111d4279f | 831 | hdr->rwnd = short_be(t->wnd); |
daniele | 10:dd7111d4279f | 832 | tcp_set_space(t); |
daniele | 10:dd7111d4279f | 833 | tcp_add_options(t,f, flags, opt_len); |
daniele | 10:dd7111d4279f | 834 | hdr->trans.sport = t->sock.local_port; |
daniele | 10:dd7111d4279f | 835 | hdr->trans.dport = t->sock.remote_port; |
daniele | 10:dd7111d4279f | 836 | hdr->seq = long_be(t->snd_nxt); |
daniele | 10:dd7111d4279f | 837 | if ((flags & PICO_TCP_ACK) != 0) |
daniele | 10:dd7111d4279f | 838 | hdr->ack = long_be(t->rcv_nxt); |
daniele | 10:dd7111d4279f | 839 | t->rcv_ackd = t->rcv_nxt; |
daniele | 10:dd7111d4279f | 840 | |
daniele | 10:dd7111d4279f | 841 | f->start = f->transport_hdr + PICO_SIZE_TCPHDR; |
daniele | 10:dd7111d4279f | 842 | hdr->rwnd = short_be(t->wnd); |
daniele | 10:dd7111d4279f | 843 | hdr->crc = 0; |
daniele | 10:dd7111d4279f | 844 | hdr->crc = short_be(pico_tcp_checksum_ipv4(f)); |
daniele | 10:dd7111d4279f | 845 | |
daniele | 10:dd7111d4279f | 846 | /* TCP: ENQUEUE to PROTO */ |
daniele | 10:dd7111d4279f | 847 | pico_enqueue(&tcp_out, f); |
daniele | 10:dd7111d4279f | 848 | } |
daniele | 10:dd7111d4279f | 849 | |
daniele | 10:dd7111d4279f | 850 | static void tcp_send_ack(struct pico_socket_tcp *t) |
daniele | 10:dd7111d4279f | 851 | { |
daniele | 10:dd7111d4279f | 852 | return tcp_send_empty(t, PICO_TCP_ACK); |
daniele | 10:dd7111d4279f | 853 | } |
daniele | 10:dd7111d4279f | 854 | |
daniele | 10:dd7111d4279f | 855 | static int tcp_send_rst(struct pico_socket *s, struct pico_frame *fr) |
daniele | 10:dd7111d4279f | 856 | { |
daniele | 10:dd7111d4279f | 857 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
daniele | 10:dd7111d4279f | 858 | struct pico_frame *f; |
daniele | 10:dd7111d4279f | 859 | struct pico_tcp_hdr *hdr, *hdr_rcv; |
daniele | 10:dd7111d4279f | 860 | int opt_len = tcp_options_size(t, PICO_TCP_RST); |
daniele | 10:dd7111d4279f | 861 | |
daniele | 10:dd7111d4279f | 862 | tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> START\n"); |
daniele | 10:dd7111d4279f | 863 | /* go to CLOSED here to prevent timer callback to go on after timeout */ |
daniele | 10:dd7111d4279f | 864 | (t->sock).state &= 0x00FFU; |
daniele | 10:dd7111d4279f | 865 | (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED; |
daniele | 10:dd7111d4279f | 866 | |
daniele | 10:dd7111d4279f | 867 | f = t->sock.net->alloc(t->sock.net, PICO_SIZE_TCPHDR + opt_len); |
daniele | 10:dd7111d4279f | 868 | |
daniele | 10:dd7111d4279f | 869 | if (!f) { |
daniele | 10:dd7111d4279f | 870 | return -1; |
daniele | 10:dd7111d4279f | 871 | } |
daniele | 10:dd7111d4279f | 872 | |
daniele | 10:dd7111d4279f | 873 | hdr_rcv = (struct pico_tcp_hdr *) fr->transport_hdr; |
daniele | 10:dd7111d4279f | 874 | |
daniele | 10:dd7111d4279f | 875 | f->sock = &t->sock; |
daniele | 10:dd7111d4279f | 876 | hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
daniele | 10:dd7111d4279f | 877 | hdr->len = (PICO_SIZE_TCPHDR + opt_len) << 2 | t->jumbo; |
daniele | 10:dd7111d4279f | 878 | hdr->flags = PICO_TCP_RST; |
daniele | 10:dd7111d4279f | 879 | hdr->rwnd = short_be(t->wnd); |
daniele | 10:dd7111d4279f | 880 | tcp_set_space(t); |
daniele | 10:dd7111d4279f | 881 | tcp_add_options(t,f, PICO_TCP_RST, opt_len); |
daniele | 10:dd7111d4279f | 882 | hdr->trans.sport = t->sock.local_port; |
daniele | 10:dd7111d4279f | 883 | hdr->trans.dport = t->sock.remote_port; |
daniele | 10:dd7111d4279f | 884 | hdr->seq = long_be(t->snd_nxt); |
daniele | 10:dd7111d4279f | 885 | |
daniele | 10:dd7111d4279f | 886 | /* check if state is synchronized */ |
daniele | 10:dd7111d4279f | 887 | if (((s->state & PICO_SOCKET_STATE_TCP) > PICO_SOCKET_STATE_TCP_SYN_RECV)) { |
daniele | 10:dd7111d4279f | 888 | /* in synchronized state: send RST with seq = ack from previous segment */ |
daniele | 10:dd7111d4279f | 889 | hdr->seq = hdr_rcv->ack; |
daniele | 10:dd7111d4279f | 890 | } else { |
daniele | 10:dd7111d4279f | 891 | /* non-synchronized state */ |
daniele | 10:dd7111d4279f | 892 | } |
daniele | 10:dd7111d4279f | 893 | |
daniele | 10:dd7111d4279f | 894 | hdr->ack = long_be(t->rcv_nxt); |
daniele | 10:dd7111d4279f | 895 | t->rcv_ackd = t->rcv_nxt; |
daniele | 10:dd7111d4279f | 896 | f->start = f->transport_hdr + PICO_SIZE_TCPHDR; |
daniele | 10:dd7111d4279f | 897 | hdr->rwnd = short_be(t->wnd); |
daniele | 10:dd7111d4279f | 898 | hdr->crc = 0; |
daniele | 10:dd7111d4279f | 899 | hdr->crc = short_be(pico_tcp_checksum_ipv4(f)); |
daniele | 10:dd7111d4279f | 900 | |
daniele | 10:dd7111d4279f | 901 | /* TCP: ENQUEUE to PROTO */ |
daniele | 10:dd7111d4279f | 902 | pico_enqueue(&tcp_out, f); |
daniele | 10:dd7111d4279f | 903 | |
daniele | 10:dd7111d4279f | 904 | /* goto CLOSED */ |
daniele | 10:dd7111d4279f | 905 | //(t->sock).state &= 0x00FFU; |
daniele | 10:dd7111d4279f | 906 | //(t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED; |
daniele | 10:dd7111d4279f | 907 | (t->sock).state &= 0xFF00U; |
daniele | 10:dd7111d4279f | 908 | (t->sock).state |= PICO_SOCKET_STATE_CLOSED; |
daniele | 10:dd7111d4279f | 909 | |
daniele | 10:dd7111d4279f | 910 | /* call EV_FIN wakeup before deleting */ |
daniele | 10:dd7111d4279f | 911 | if ((t->sock).wakeup) |
daniele | 10:dd7111d4279f | 912 | (t->sock).wakeup(PICO_SOCK_EV_FIN, &(t->sock)); |
daniele | 10:dd7111d4279f | 913 | |
daniele | 10:dd7111d4279f | 914 | /* delete socket */ |
daniele | 10:dd7111d4279f | 915 | pico_socket_del(&t->sock); |
daniele | 10:dd7111d4279f | 916 | |
daniele | 10:dd7111d4279f | 917 | tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> DONE, deleted socket\n"); |
daniele | 10:dd7111d4279f | 918 | |
daniele | 10:dd7111d4279f | 919 | return 0; |
daniele | 10:dd7111d4279f | 920 | } |
daniele | 10:dd7111d4279f | 921 | |
daniele | 10:dd7111d4279f | 922 | int pico_tcp_reply_rst(struct pico_frame *fr) |
daniele | 10:dd7111d4279f | 923 | { |
daniele | 10:dd7111d4279f | 924 | struct pico_tcp_hdr *hdr; |
daniele | 10:dd7111d4279f | 925 | struct pico_frame *f; |
daniele | 10:dd7111d4279f | 926 | int size = PICO_SIZE_TCPHDR; |
daniele | 10:dd7111d4279f | 927 | |
daniele | 10:dd7111d4279f | 928 | tcp_dbg("TCP>>>>>>>>>>>>>>>> sending RST ... <<<<<<<<<<<<<<<<<<\n"); |
daniele | 10:dd7111d4279f | 929 | |
daniele | 10:dd7111d4279f | 930 | f = fr->sock->net->alloc(fr->sock->net, size); |
daniele | 10:dd7111d4279f | 931 | |
daniele | 10:dd7111d4279f | 932 | /* fill in IP data from original frame */ |
daniele | 10:dd7111d4279f | 933 | // TODO if IPv4 |
daniele | 10:dd7111d4279f | 934 | ((struct pico_ipv4_hdr *)(f->net_hdr))->dst.addr = ((struct pico_ipv4_hdr *)(fr->net_hdr))->src.addr; |
daniele | 10:dd7111d4279f | 935 | ((struct pico_ipv4_hdr *)(f->net_hdr))->src.addr = ((struct pico_ipv4_hdr *)(fr->net_hdr))->dst.addr; |
daniele | 10:dd7111d4279f | 936 | |
daniele | 10:dd7111d4279f | 937 | /* fill in TCP data from original frame */ |
daniele | 10:dd7111d4279f | 938 | ((struct pico_tcp_hdr *)(f->transport_hdr))->trans.dport = ((struct pico_tcp_hdr *)(fr->transport_hdr))->trans.sport; |
daniele | 10:dd7111d4279f | 939 | ((struct pico_tcp_hdr *)(f->transport_hdr))->trans.sport = ((struct pico_tcp_hdr *)(fr->transport_hdr))->trans.dport; |
daniele | 10:dd7111d4279f | 940 | hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
daniele | 10:dd7111d4279f | 941 | hdr->len = size << 2; |
daniele | 10:dd7111d4279f | 942 | hdr->flags = PICO_TCP_RST | PICO_TCP_ACK; |
daniele | 10:dd7111d4279f | 943 | hdr->rwnd = 0; |
daniele | 10:dd7111d4279f | 944 | if (((struct pico_tcp_hdr *)(fr->transport_hdr))->flags & PICO_TCP_ACK) { |
daniele | 10:dd7111d4279f | 945 | hdr->seq = ((struct pico_tcp_hdr *)(fr->transport_hdr))->ack; |
daniele | 10:dd7111d4279f | 946 | } else { |
daniele | 10:dd7111d4279f | 947 | hdr->seq = 0U; |
daniele | 10:dd7111d4279f | 948 | } |
daniele | 10:dd7111d4279f | 949 | |
daniele | 10:dd7111d4279f | 950 | hdr->ack = ((struct pico_tcp_hdr *)(fr->transport_hdr))->seq + short_be(fr->payload_len); |
daniele | 10:dd7111d4279f | 951 | |
daniele | 10:dd7111d4279f | 952 | /* enqueue for transmission */ |
daniele | 10:dd7111d4279f | 953 | pico_ipv4_frame_push(f,&(((struct pico_ipv4_hdr *)(f->net_hdr))->dst),PICO_PROTO_TCP); |
daniele | 10:dd7111d4279f | 954 | |
daniele | 10:dd7111d4279f | 955 | return 0; |
daniele | 10:dd7111d4279f | 956 | } |
daniele | 10:dd7111d4279f | 957 | |
daniele | 10:dd7111d4279f | 958 | static int tcp_nosync_rst(struct pico_socket *s, struct pico_frame *fr) |
daniele | 10:dd7111d4279f | 959 | { |
daniele | 10:dd7111d4279f | 960 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
daniele | 10:dd7111d4279f | 961 | struct pico_frame *f; |
daniele | 10:dd7111d4279f | 962 | struct pico_tcp_hdr *hdr, *hdr_rcv; |
daniele | 10:dd7111d4279f | 963 | int opt_len = tcp_options_size(t, PICO_TCP_RST | PICO_TCP_ACK); |
daniele | 10:dd7111d4279f | 964 | |
daniele | 10:dd7111d4279f | 965 | tcp_dbg("TCP SEND RST (NON-SYNC) >>>>>>>>>>>>>>>>>> state %x\n",(s->state & PICO_SOCKET_STATE_TCP)); |
daniele | 10:dd7111d4279f | 966 | if (((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_LISTEN)) { |
daniele | 10:dd7111d4279f | 967 | /* XXX TODO NOTE: to prevent the parent socket from trying to send, because this socket has no knowledge of dst IP !!! */ |
daniele | 10:dd7111d4279f | 968 | return pico_tcp_reply_rst(fr); |
daniele | 10:dd7111d4279f | 969 | } |
daniele | 10:dd7111d4279f | 970 | |
daniele | 10:dd7111d4279f | 971 | /***************************************************************************/ |
daniele | 10:dd7111d4279f | 972 | /* sending RST */ |
daniele | 10:dd7111d4279f | 973 | f = t->sock.net->alloc(t->sock.net, PICO_SIZE_TCPHDR + opt_len); |
daniele | 10:dd7111d4279f | 974 | |
daniele | 10:dd7111d4279f | 975 | if (!f) { |
daniele | 10:dd7111d4279f | 976 | return -1; |
daniele | 10:dd7111d4279f | 977 | } |
daniele | 10:dd7111d4279f | 978 | |
daniele | 10:dd7111d4279f | 979 | hdr_rcv = (struct pico_tcp_hdr *) fr->transport_hdr; |
daniele | 10:dd7111d4279f | 980 | |
daniele | 10:dd7111d4279f | 981 | f->sock = &t->sock; |
daniele | 10:dd7111d4279f | 982 | hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
daniele | 10:dd7111d4279f | 983 | hdr->len = (PICO_SIZE_TCPHDR + opt_len) << 2 | t->jumbo; |
daniele | 10:dd7111d4279f | 984 | hdr->flags = PICO_TCP_RST | PICO_TCP_ACK; |
daniele | 10:dd7111d4279f | 985 | hdr->rwnd = short_be(t->wnd); |
daniele | 10:dd7111d4279f | 986 | tcp_set_space(t); |
daniele | 10:dd7111d4279f | 987 | tcp_add_options(t,f, PICO_TCP_RST | PICO_TCP_ACK, opt_len); |
daniele | 10:dd7111d4279f | 988 | hdr->trans.sport = t->sock.local_port; |
daniele | 10:dd7111d4279f | 989 | hdr->trans.dport = t->sock.remote_port; |
daniele | 10:dd7111d4279f | 990 | |
daniele | 10:dd7111d4279f | 991 | /* non-synchronized state */ |
daniele | 10:dd7111d4279f | 992 | if (hdr_rcv->flags & PICO_TCP_ACK) { |
daniele | 10:dd7111d4279f | 993 | hdr->seq = hdr_rcv->ack; |
daniele | 10:dd7111d4279f | 994 | } else { |
daniele | 10:dd7111d4279f | 995 | hdr->seq = 0U; |
daniele | 10:dd7111d4279f | 996 | } |
daniele | 10:dd7111d4279f | 997 | |
daniele | 10:dd7111d4279f | 998 | hdr->ack = hdr_rcv->seq + short_be(fr->payload_len); |
daniele | 10:dd7111d4279f | 999 | |
daniele | 10:dd7111d4279f | 1000 | t->rcv_ackd = t->rcv_nxt; |
daniele | 10:dd7111d4279f | 1001 | f->start = f->transport_hdr + PICO_SIZE_TCPHDR; |
daniele | 10:dd7111d4279f | 1002 | hdr->rwnd = short_be(t->wnd); |
daniele | 10:dd7111d4279f | 1003 | hdr->crc = 0; |
daniele | 10:dd7111d4279f | 1004 | hdr->crc = short_be(pico_tcp_checksum_ipv4(f)); |
daniele | 10:dd7111d4279f | 1005 | |
daniele | 10:dd7111d4279f | 1006 | /* TCP: ENQUEUE to PROTO */ |
daniele | 10:dd7111d4279f | 1007 | pico_enqueue(&tcp_out, f); |
daniele | 10:dd7111d4279f | 1008 | |
daniele | 10:dd7111d4279f | 1009 | /***************************************************************************/ |
daniele | 10:dd7111d4279f | 1010 | |
daniele | 10:dd7111d4279f | 1011 | tcp_dbg("TCP SEND_RST (NON_SYNC) >>>>>>>>>>>>>>> DONE, ...\n"); |
daniele | 10:dd7111d4279f | 1012 | |
daniele | 10:dd7111d4279f | 1013 | return 0; |
daniele | 10:dd7111d4279f | 1014 | } |
daniele | 10:dd7111d4279f | 1015 | |
daniele | 10:dd7111d4279f | 1016 | static void tcp_send_fin(struct pico_socket_tcp *t) |
daniele | 10:dd7111d4279f | 1017 | { |
daniele | 10:dd7111d4279f | 1018 | struct pico_frame *f; |
daniele | 10:dd7111d4279f | 1019 | struct pico_tcp_hdr *hdr; |
daniele | 10:dd7111d4279f | 1020 | int opt_len = tcp_options_size(t, PICO_TCP_FIN); |
daniele | 10:dd7111d4279f | 1021 | f = t->sock.net->alloc(t->sock.net, PICO_SIZE_TCPHDR + opt_len); |
daniele | 10:dd7111d4279f | 1022 | if (!f) { |
daniele | 10:dd7111d4279f | 1023 | return; |
daniele | 10:dd7111d4279f | 1024 | } |
daniele | 10:dd7111d4279f | 1025 | f->sock = &t->sock; |
daniele | 10:dd7111d4279f | 1026 | hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
daniele | 10:dd7111d4279f | 1027 | hdr->len = (PICO_SIZE_TCPHDR + opt_len) << 2 | t->jumbo; |
daniele | 10:dd7111d4279f | 1028 | hdr->flags = PICO_TCP_FIN | PICO_TCP_ACK; |
daniele | 10:dd7111d4279f | 1029 | hdr->ack = long_be(t->rcv_nxt); |
daniele | 10:dd7111d4279f | 1030 | t->rcv_ackd = t->rcv_nxt; |
daniele | 10:dd7111d4279f | 1031 | hdr->rwnd = short_be(t->wnd); |
daniele | 10:dd7111d4279f | 1032 | tcp_set_space(t); |
daniele | 10:dd7111d4279f | 1033 | tcp_add_options(t,f, PICO_TCP_FIN, opt_len); |
daniele | 10:dd7111d4279f | 1034 | hdr->trans.sport = t->sock.local_port; |
daniele | 10:dd7111d4279f | 1035 | hdr->trans.dport = t->sock.remote_port; |
daniele | 10:dd7111d4279f | 1036 | hdr->seq = long_be(t->snd_nxt); |
daniele | 10:dd7111d4279f | 1037 | |
daniele | 10:dd7111d4279f | 1038 | f->start = f->transport_hdr + PICO_SIZE_TCPHDR; |
daniele | 10:dd7111d4279f | 1039 | hdr->rwnd = short_be(t->wnd); |
daniele | 10:dd7111d4279f | 1040 | hdr->crc = 0; |
daniele | 10:dd7111d4279f | 1041 | hdr->crc = short_be(pico_tcp_checksum_ipv4(f)); |
daniele | 10:dd7111d4279f | 1042 | //tcp_dbg("SENDING FIN...\n"); |
daniele | 10:dd7111d4279f | 1043 | /* TCP: ENQUEUE to PROTO ( Pure ACK ) */ |
daniele | 10:dd7111d4279f | 1044 | pico_enqueue(&tcp_out, f); |
daniele | 10:dd7111d4279f | 1045 | t->snd_nxt++; |
daniele | 10:dd7111d4279f | 1046 | } |
daniele | 10:dd7111d4279f | 1047 | |
tass | 31:d3b2dfcc358f | 1048 | /*****/ |
tass | 31:d3b2dfcc358f | 1049 | static inline void checkIfQueueEmptyAndFin(struct pico_socket_tcp * t) |
tass | 31:d3b2dfcc358f | 1050 | { |
tass | 31:d3b2dfcc358f | 1051 | struct pico_socket *s =(struct pico_socket *)&t->sock; |
tass | 31:d3b2dfcc358f | 1052 | if(t->closing && t->tcpq_out.frames==0) |
tass | 31:d3b2dfcc358f | 1053 | { |
tass | 31:d3b2dfcc358f | 1054 | s->state &= 0x00FFU; |
tass | 31:d3b2dfcc358f | 1055 | /* set SHUT_REMOTE */ |
tass | 31:d3b2dfcc358f | 1056 | s->state |= PICO_SOCKET_STATE_SHUT_REMOTE; |
tass | 31:d3b2dfcc358f | 1057 | s->state |= PICO_SOCKET_STATE_TCP_CLOSE_WAIT; |
tass | 31:d3b2dfcc358f | 1058 | tcp_dbg("TCP> Close-wait\n"); |
tass | 31:d3b2dfcc358f | 1059 | if (s->wakeup) |
tass | 31:d3b2dfcc358f | 1060 | { |
tass | 31:d3b2dfcc358f | 1061 | s->wakeup(PICO_SOCK_EV_CLOSE, s); |
tass | 31:d3b2dfcc358f | 1062 | } |
tass | 31:d3b2dfcc358f | 1063 | t->closing = 0; |
tass | 31:d3b2dfcc358f | 1064 | } |
tass | 31:d3b2dfcc358f | 1065 | |
tass | 31:d3b2dfcc358f | 1066 | if ((t->tcpq_out.frames == 0) && (s->state & PICO_SOCKET_STATE_SHUT_LOCAL)) { /* if no more packets in queue, XXX replacled !f by tcpq check */ |
tass | 31:d3b2dfcc358f | 1067 | if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED) { |
tass | 31:d3b2dfcc358f | 1068 | tcp_dbg("TCP> buffer empty, shutdown established ...\n"); |
tass | 31:d3b2dfcc358f | 1069 | /* send fin if queue empty and in state shut local (write) */ |
tass | 31:d3b2dfcc358f | 1070 | tcp_send_fin(t); |
tass | 31:d3b2dfcc358f | 1071 | /* change tcp state to FIN_WAIT1 */ |
tass | 31:d3b2dfcc358f | 1072 | s->state &= 0x00FFU; |
tass | 31:d3b2dfcc358f | 1073 | s->state |= PICO_SOCKET_STATE_TCP_FIN_WAIT1; |
tass | 31:d3b2dfcc358f | 1074 | } else if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_CLOSE_WAIT) { |
tass | 31:d3b2dfcc358f | 1075 | /* send fin if queue empty and in state shut local (write) */ |
tass | 31:d3b2dfcc358f | 1076 | tcp_send_fin(t); |
tass | 31:d3b2dfcc358f | 1077 | /* change tcp state to LAST_ACK */ |
tass | 31:d3b2dfcc358f | 1078 | s->state &= 0x00FFU; |
tass | 31:d3b2dfcc358f | 1079 | s->state |= PICO_SOCKET_STATE_TCP_LAST_ACK; |
tass | 31:d3b2dfcc358f | 1080 | tcp_dbg("TCP> STATE: LAST_ACK.\n"); |
tass | 31:d3b2dfcc358f | 1081 | } |
tass | 31:d3b2dfcc358f | 1082 | } |
tass | 31:d3b2dfcc358f | 1083 | } |
tass | 31:d3b2dfcc358f | 1084 | /*****/ |
tass | 31:d3b2dfcc358f | 1085 | |
daniele | 10:dd7111d4279f | 1086 | static void tcp_sack_prepare(struct pico_socket_tcp *t) |
daniele | 10:dd7111d4279f | 1087 | { |
daniele | 10:dd7111d4279f | 1088 | struct pico_frame *pkt; |
daniele | 10:dd7111d4279f | 1089 | uint32_t left=0, right=0; |
daniele | 10:dd7111d4279f | 1090 | struct tcp_sack_block *sb; |
daniele | 10:dd7111d4279f | 1091 | int n = 0; |
daniele | 10:dd7111d4279f | 1092 | if (t->sacks) /* previous sacks are pending */ |
daniele | 10:dd7111d4279f | 1093 | return; |
daniele | 10:dd7111d4279f | 1094 | |
daniele | 10:dd7111d4279f | 1095 | pkt = first_segment(&t->tcpq_in); |
daniele | 10:dd7111d4279f | 1096 | while(n < 3) { |
daniele | 10:dd7111d4279f | 1097 | if (!pkt) { |
daniele | 10:dd7111d4279f | 1098 | if(left) { |
daniele | 10:dd7111d4279f | 1099 | sb = pico_zalloc(sizeof(struct tcp_sack_block)); |
daniele | 10:dd7111d4279f | 1100 | if (!sb) |
daniele | 10:dd7111d4279f | 1101 | break; |
daniele | 10:dd7111d4279f | 1102 | sb->left = long_be(left); |
daniele | 10:dd7111d4279f | 1103 | sb->right = long_be(right); |
daniele | 10:dd7111d4279f | 1104 | n++; |
daniele | 10:dd7111d4279f | 1105 | sb->next = t->sacks; |
daniele | 10:dd7111d4279f | 1106 | t->sacks = sb; |
daniele | 10:dd7111d4279f | 1107 | left = 0; |
daniele | 10:dd7111d4279f | 1108 | right = 0; |
daniele | 10:dd7111d4279f | 1109 | } |
daniele | 10:dd7111d4279f | 1110 | break; |
daniele | 10:dd7111d4279f | 1111 | } |
daniele | 10:dd7111d4279f | 1112 | if ((SEQN(pkt) < t->rcv_nxt)) { |
daniele | 10:dd7111d4279f | 1113 | pkt = next_segment(&t->tcpq_in, pkt); |
daniele | 10:dd7111d4279f | 1114 | continue; |
daniele | 10:dd7111d4279f | 1115 | } |
daniele | 10:dd7111d4279f | 1116 | if (!left) { |
daniele | 10:dd7111d4279f | 1117 | left = SEQN(pkt); |
daniele | 10:dd7111d4279f | 1118 | right = SEQN(pkt) + pkt->payload_len; |
daniele | 10:dd7111d4279f | 1119 | pkt = next_segment(&t->tcpq_in, pkt); |
daniele | 10:dd7111d4279f | 1120 | continue; |
daniele | 10:dd7111d4279f | 1121 | } |
daniele | 10:dd7111d4279f | 1122 | if(SEQN(pkt) == (right + 1)) { |
daniele | 10:dd7111d4279f | 1123 | right += pkt->payload_len; |
daniele | 10:dd7111d4279f | 1124 | pkt = next_segment(&t->tcpq_in, pkt); |
daniele | 10:dd7111d4279f | 1125 | continue; |
daniele | 10:dd7111d4279f | 1126 | } else { |
daniele | 10:dd7111d4279f | 1127 | sb = pico_zalloc(sizeof(struct tcp_sack_block)); |
daniele | 10:dd7111d4279f | 1128 | if (!sb) |
daniele | 10:dd7111d4279f | 1129 | break; |
daniele | 10:dd7111d4279f | 1130 | sb->left = long_be(left); |
daniele | 10:dd7111d4279f | 1131 | sb->right = long_be(right); |
daniele | 10:dd7111d4279f | 1132 | n++; |
daniele | 10:dd7111d4279f | 1133 | sb->next = t->sacks; |
daniele | 10:dd7111d4279f | 1134 | t->sacks = sb; |
daniele | 10:dd7111d4279f | 1135 | left = 0; |
daniele | 10:dd7111d4279f | 1136 | right = 0; |
daniele | 10:dd7111d4279f | 1137 | pkt = next_segment(&t->tcpq_in, pkt); |
daniele | 10:dd7111d4279f | 1138 | } |
daniele | 10:dd7111d4279f | 1139 | } |
daniele | 10:dd7111d4279f | 1140 | } |
daniele | 10:dd7111d4279f | 1141 | |
daniele | 10:dd7111d4279f | 1142 | static int tcp_data_in(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1143 | { |
daniele | 10:dd7111d4279f | 1144 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
daniele | 10:dd7111d4279f | 1145 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
daniele | 10:dd7111d4279f | 1146 | |
daniele | 10:dd7111d4279f | 1147 | if (((hdr->len & 0xf0) >> 2) <= f->transport_len) { |
daniele | 10:dd7111d4279f | 1148 | tcp_parse_options(f); |
daniele | 10:dd7111d4279f | 1149 | f->payload = f->transport_hdr + ((hdr->len & 0xf0) >>2); |
daniele | 10:dd7111d4279f | 1150 | f->payload_len = f->transport_len - ((hdr->len & 0xf0) >>2); |
daniele | 10:dd7111d4279f | 1151 | |
daniele | 10:dd7111d4279f | 1152 | if (seq_compare(SEQN(f), t->rcv_nxt) >= 0) { |
daniele | 10:dd7111d4279f | 1153 | struct pico_frame *cpy = pico_frame_copy(f); |
daniele | 10:dd7111d4279f | 1154 | struct pico_frame *nxt; |
daniele | 10:dd7111d4279f | 1155 | /* Enqueue: try to put into RCV buffer */ |
daniele | 10:dd7111d4279f | 1156 | if (pico_enqueue_segment(&t->tcpq_in, cpy) <= 0) { |
daniele | 10:dd7111d4279f | 1157 | pico_frame_discard(cpy); |
daniele | 10:dd7111d4279f | 1158 | return -1; |
daniele | 10:dd7111d4279f | 1159 | } |
daniele | 10:dd7111d4279f | 1160 | if (seq_compare(SEQN(f), t->rcv_nxt) == 0) { /* Exactly what we expected */ |
daniele | 10:dd7111d4279f | 1161 | t->rcv_nxt = SEQN(f) + f->payload_len; |
daniele | 10:dd7111d4279f | 1162 | nxt = peek_segment(&t->tcpq_in, t->rcv_nxt); |
daniele | 10:dd7111d4279f | 1163 | while(nxt) { |
daniele | 10:dd7111d4279f | 1164 | tcp_dbg("scrolling rcv_nxt...%08x\n", t->rcv_nxt); |
daniele | 10:dd7111d4279f | 1165 | t->rcv_nxt += f->payload_len; |
daniele | 10:dd7111d4279f | 1166 | nxt = peek_segment(&t->tcpq_in, t->rcv_nxt); |
daniele | 10:dd7111d4279f | 1167 | } |
daniele | 10:dd7111d4279f | 1168 | t->sock.ev_pending |= PICO_SOCK_EV_RD; |
daniele | 10:dd7111d4279f | 1169 | t->rcv_nxt = SEQN(f) + f->payload_len; |
daniele | 10:dd7111d4279f | 1170 | } else { |
daniele | 10:dd7111d4279f | 1171 | tcp_dbg("TCP> lo segment. Possible retransmission. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f)); |
daniele | 10:dd7111d4279f | 1172 | } |
daniele | 10:dd7111d4279f | 1173 | } else { |
daniele | 10:dd7111d4279f | 1174 | tcp_dbg("TCP> hi segment. Possible packet loss. I'll dupack this. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f)); |
daniele | 10:dd7111d4279f | 1175 | if (t->sack_ok) { |
daniele | 10:dd7111d4279f | 1176 | tcp_sack_prepare(t); |
daniele | 10:dd7111d4279f | 1177 | } |
daniele | 10:dd7111d4279f | 1178 | } |
daniele | 10:dd7111d4279f | 1179 | /* In either case, ack til recv_nxt. */ |
daniele | 10:dd7111d4279f | 1180 | if ( ((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_CLOSE_WAIT) && ((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_SYN_SENT) && ((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_SYN_RECV)) { |
daniele | 10:dd7111d4279f | 1181 | //tcp_dbg("SENDACK CALLED FROM OUTSIDE tcp_synack, state %x\n",t->sock.state); |
daniele | 10:dd7111d4279f | 1182 | tcp_send_ack(t); |
daniele | 10:dd7111d4279f | 1183 | } else { |
daniele | 10:dd7111d4279f | 1184 | //tcp_dbg("SENDACK PREVENTED IN SYNSENT STATE\n"); |
daniele | 10:dd7111d4279f | 1185 | } |
daniele | 10:dd7111d4279f | 1186 | return 0; |
daniele | 10:dd7111d4279f | 1187 | } else { |
daniele | 10:dd7111d4279f | 1188 | tcp_dbg("TCP: invalid data in pkt len, exp: %d, got %d\n", (hdr->len & 0xf0) >> 2, f->transport_len); |
daniele | 10:dd7111d4279f | 1189 | return -1; |
daniele | 10:dd7111d4279f | 1190 | } |
daniele | 10:dd7111d4279f | 1191 | } |
daniele | 10:dd7111d4279f | 1192 | |
daniele | 10:dd7111d4279f | 1193 | static int tcp_ack_advance_una(struct pico_socket_tcp *t, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1194 | { |
daniele | 10:dd7111d4279f | 1195 | int ret = release_all_until(&t->tcpq_out, ACKN(f)); |
daniele | 10:dd7111d4279f | 1196 | if (ret > 0) |
daniele | 10:dd7111d4279f | 1197 | t->sock.ev_pending |= PICO_SOCK_EV_WR; |
daniele | 10:dd7111d4279f | 1198 | return ret; |
daniele | 10:dd7111d4279f | 1199 | } |
daniele | 10:dd7111d4279f | 1200 | |
daniele | 10:dd7111d4279f | 1201 | static uint16_t time_diff(unsigned long a, unsigned long b) |
daniele | 10:dd7111d4279f | 1202 | { |
daniele | 10:dd7111d4279f | 1203 | if (a >= b) |
daniele | 10:dd7111d4279f | 1204 | return (a - b); |
daniele | 10:dd7111d4279f | 1205 | else |
daniele | 10:dd7111d4279f | 1206 | return (b - a); |
daniele | 10:dd7111d4279f | 1207 | } |
daniele | 10:dd7111d4279f | 1208 | |
daniele | 10:dd7111d4279f | 1209 | static void tcp_rtt(struct pico_socket_tcp *t, uint32_t rtt) |
daniele | 10:dd7111d4279f | 1210 | { |
daniele | 10:dd7111d4279f | 1211 | |
daniele | 10:dd7111d4279f | 1212 | uint32_t avg = t->avg_rtt; |
daniele | 10:dd7111d4279f | 1213 | uint32_t rvar = t->rttvar; |
daniele | 10:dd7111d4279f | 1214 | if (!avg) { |
daniele | 10:dd7111d4279f | 1215 | /* This follows RFC2988 |
daniele | 10:dd7111d4279f | 1216 | * (2.2) When the first RTT measurement R is made, the host MUST set |
daniele | 10:dd7111d4279f | 1217 | * |
daniele | 10:dd7111d4279f | 1218 | * SRTT <- R |
daniele | 10:dd7111d4279f | 1219 | * RTTVAR <- R/2 |
daniele | 10:dd7111d4279f | 1220 | * RTO <- SRTT + max (G, K*RTTVAR) |
daniele | 10:dd7111d4279f | 1221 | */ |
daniele | 10:dd7111d4279f | 1222 | t->avg_rtt = rtt; |
daniele | 10:dd7111d4279f | 1223 | t->rttvar = rtt >> 1; |
daniele | 10:dd7111d4279f | 1224 | t->rto = t->avg_rtt + (t->rttvar << 4); |
daniele | 10:dd7111d4279f | 1225 | } else { |
daniele | 10:dd7111d4279f | 1226 | int var = (t->avg_rtt - rtt); |
daniele | 10:dd7111d4279f | 1227 | if (var < 0) |
daniele | 10:dd7111d4279f | 1228 | var = 0-var; |
daniele | 10:dd7111d4279f | 1229 | /* RFC2988, section (2.3). Alpha and beta are the ones suggested. */ |
daniele | 10:dd7111d4279f | 1230 | |
daniele | 10:dd7111d4279f | 1231 | /* First, evaluate a new value for the rttvar */ |
daniele | 10:dd7111d4279f | 1232 | t->rttvar <<= 2; |
daniele | 10:dd7111d4279f | 1233 | t->rttvar -= rvar; |
daniele | 10:dd7111d4279f | 1234 | t->rttvar += var; |
daniele | 10:dd7111d4279f | 1235 | t->rttvar >>= 2; |
daniele | 10:dd7111d4279f | 1236 | |
daniele | 10:dd7111d4279f | 1237 | /* Then, calculate the new avg_rtt */ |
daniele | 10:dd7111d4279f | 1238 | t->avg_rtt <<= 3; |
daniele | 10:dd7111d4279f | 1239 | t->avg_rtt -= avg; |
daniele | 10:dd7111d4279f | 1240 | t->avg_rtt += rtt; |
daniele | 10:dd7111d4279f | 1241 | t->avg_rtt >>= 3; |
daniele | 10:dd7111d4279f | 1242 | |
daniele | 10:dd7111d4279f | 1243 | /* Finally, assign a new value for the RTO, as specified in the RFC, with K=4 */ |
daniele | 10:dd7111d4279f | 1244 | t->rto = t->avg_rtt + (t->rttvar << 2); |
daniele | 10:dd7111d4279f | 1245 | } |
daniele | 10:dd7111d4279f | 1246 | tcp_dbg(" -----=============== RTT AVG: %u RTTVAR: %u RTO: %u ======================----\n", t->avg_rtt, t->rttvar, t->rto); |
daniele | 10:dd7111d4279f | 1247 | } |
daniele | 10:dd7111d4279f | 1248 | |
daniele | 10:dd7111d4279f | 1249 | static void tcp_congestion_control(struct pico_socket_tcp *t) |
daniele | 10:dd7111d4279f | 1250 | { |
daniele | 10:dd7111d4279f | 1251 | if (t->x_mode > PICO_TCP_LOOKAHEAD) |
daniele | 10:dd7111d4279f | 1252 | return; |
daniele | 10:dd7111d4279f | 1253 | if (t->cwnd > t->tcpq_out.frames) { |
daniele | 10:dd7111d4279f | 1254 | tcp_dbg("Limited by app: %d\n", t->cwnd); |
daniele | 10:dd7111d4279f | 1255 | return; |
daniele | 10:dd7111d4279f | 1256 | } |
daniele | 10:dd7111d4279f | 1257 | tcp_dbg("Doing congestion control\n"); |
daniele | 10:dd7111d4279f | 1258 | if (t->cwnd < t->ssthresh) { |
daniele | 10:dd7111d4279f | 1259 | t->cwnd++; |
daniele | 10:dd7111d4279f | 1260 | } else { |
daniele | 10:dd7111d4279f | 1261 | t->cwnd_counter++; |
daniele | 10:dd7111d4279f | 1262 | if (t->cwnd_counter >= t->cwnd) { |
daniele | 10:dd7111d4279f | 1263 | t->cwnd++; |
daniele | 10:dd7111d4279f | 1264 | t->cwnd_counter = 0; |
daniele | 10:dd7111d4279f | 1265 | } |
daniele | 10:dd7111d4279f | 1266 | } |
daniele | 10:dd7111d4279f | 1267 | tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", pico_tick, t->cwnd, t->ssthresh, t->in_flight); |
daniele | 10:dd7111d4279f | 1268 | } |
daniele | 10:dd7111d4279f | 1269 | |
daniele | 10:dd7111d4279f | 1270 | static void add_retransmission_timer(struct pico_socket_tcp *t, unsigned long next_ts); |
daniele | 10:dd7111d4279f | 1271 | static void tcp_retrans_timeout(unsigned long val, void *sock) |
daniele | 10:dd7111d4279f | 1272 | { |
daniele | 10:dd7111d4279f | 1273 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) sock; |
daniele | 10:dd7111d4279f | 1274 | struct pico_frame *f = NULL; |
daniele | 10:dd7111d4279f | 1275 | unsigned long limit = val - t->rto; |
daniele | 10:dd7111d4279f | 1276 | struct pico_tcp_hdr *hdr; |
daniele | 10:dd7111d4279f | 1277 | if(t->sock.net && ((t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_ESTABLISHED)) |
daniele | 10:dd7111d4279f | 1278 | { |
daniele | 10:dd7111d4279f | 1279 | tcp_dbg("\n\nTIMEOUT! backoff = %d\n", t->backoff); |
daniele | 10:dd7111d4279f | 1280 | /* was timer cancelled? */ |
daniele | 10:dd7111d4279f | 1281 | if (t->timer_running == 0) { |
daniele | 10:dd7111d4279f | 1282 | add_retransmission_timer(t, 0); |
daniele | 10:dd7111d4279f | 1283 | return; |
daniele | 10:dd7111d4279f | 1284 | } |
daniele | 10:dd7111d4279f | 1285 | t->timer_running--; |
daniele | 10:dd7111d4279f | 1286 | |
daniele | 10:dd7111d4279f | 1287 | f = first_segment(&t->tcpq_out); |
daniele | 10:dd7111d4279f | 1288 | while (f) { |
daniele | 10:dd7111d4279f | 1289 | if ((t->x_mode == PICO_TCP_WINDOW_FULL) || |
daniele | 10:dd7111d4279f | 1290 | ((f->timestamp != 0) && (f->timestamp <= limit))) { |
daniele | 10:dd7111d4279f | 1291 | struct pico_frame *cpy; |
daniele | 10:dd7111d4279f | 1292 | hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
daniele | 10:dd7111d4279f | 1293 | tcp_dbg("TCP BLACKOUT> TIMED OUT (output) frame %08x, len= %d rto=%d\n", SEQN(f), f->payload_len, t->rto); |
daniele | 10:dd7111d4279f | 1294 | if ((t->x_mode != PICO_TCP_WINDOW_FULL) ) { |
daniele | 10:dd7111d4279f | 1295 | t->x_mode = PICO_TCP_BLACKOUT; |
daniele | 10:dd7111d4279f | 1296 | tcp_dbg("Mode: Blackout.\n"); |
daniele | 10:dd7111d4279f | 1297 | t->cwnd = PICO_TCP_IW; |
daniele | 10:dd7111d4279f | 1298 | t->in_flight = 0; |
daniele | 10:dd7111d4279f | 1299 | } |
daniele | 10:dd7111d4279f | 1300 | f->timestamp = pico_tick; |
daniele | 10:dd7111d4279f | 1301 | tcp_add_options(t, f, 0, f->transport_len - f->payload_len - PICO_SIZE_TCPHDR); |
daniele | 10:dd7111d4279f | 1302 | hdr->rwnd = short_be(t->wnd); |
daniele | 10:dd7111d4279f | 1303 | hdr->flags |= PICO_TCP_PSH; |
daniele | 10:dd7111d4279f | 1304 | hdr->ack = long_be(t->rcv_nxt); |
daniele | 10:dd7111d4279f | 1305 | hdr->crc = 0; |
daniele | 10:dd7111d4279f | 1306 | hdr->crc = short_be(pico_tcp_checksum_ipv4(f)); |
daniele | 10:dd7111d4279f | 1307 | /* TCP: ENQUEUE to PROTO ( retransmit )*/ |
daniele | 10:dd7111d4279f | 1308 | cpy = pico_frame_copy(f); |
daniele | 10:dd7111d4279f | 1309 | if (pico_enqueue(&tcp_out, cpy) > 0) { |
daniele | 10:dd7111d4279f | 1310 | t->backoff++; |
daniele | 10:dd7111d4279f | 1311 | add_retransmission_timer(t, (t->rto << t->backoff) + pico_tick); |
daniele | 10:dd7111d4279f | 1312 | tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", pico_tick, t->cwnd, t->ssthresh, t->in_flight); |
daniele | 10:dd7111d4279f | 1313 | return; |
daniele | 10:dd7111d4279f | 1314 | } else { |
daniele | 10:dd7111d4279f | 1315 | add_retransmission_timer(t, (t->rto << t->backoff) + pico_tick); |
daniele | 10:dd7111d4279f | 1316 | pico_frame_discard(cpy); |
daniele | 10:dd7111d4279f | 1317 | } |
daniele | 10:dd7111d4279f | 1318 | } |
daniele | 10:dd7111d4279f | 1319 | f = next_segment(&t->tcpq_out, f); |
daniele | 10:dd7111d4279f | 1320 | } |
daniele | 10:dd7111d4279f | 1321 | t->backoff = 0; |
daniele | 10:dd7111d4279f | 1322 | add_retransmission_timer(t, 0); |
daniele | 10:dd7111d4279f | 1323 | if (t->tcpq_out.size < t->tcpq_out.max_size) |
daniele | 10:dd7111d4279f | 1324 | t->sock.ev_pending |= PICO_SOCK_EV_WR; |
tass | 31:d3b2dfcc358f | 1325 | checkIfQueueEmptyAndFin(t); |
daniele | 10:dd7111d4279f | 1326 | return; |
daniele | 10:dd7111d4279f | 1327 | } |
daniele | 10:dd7111d4279f | 1328 | } |
daniele | 10:dd7111d4279f | 1329 | |
daniele | 10:dd7111d4279f | 1330 | static void add_retransmission_timer(struct pico_socket_tcp *t, unsigned long next_ts) |
daniele | 10:dd7111d4279f | 1331 | { |
daniele | 10:dd7111d4279f | 1332 | struct pico_tree_node * index; |
daniele | 10:dd7111d4279f | 1333 | |
daniele | 10:dd7111d4279f | 1334 | if (t->timer_running > 0) |
daniele | 10:dd7111d4279f | 1335 | return; |
daniele | 10:dd7111d4279f | 1336 | |
daniele | 10:dd7111d4279f | 1337 | if (next_ts == 0) { |
daniele | 10:dd7111d4279f | 1338 | struct pico_frame *f; |
daniele | 10:dd7111d4279f | 1339 | |
daniele | 10:dd7111d4279f | 1340 | pico_tree_foreach(index,&t->tcpq_out.pool){ |
daniele | 10:dd7111d4279f | 1341 | f = index->keyValue; |
daniele | 10:dd7111d4279f | 1342 | if (((next_ts == 0) || (f->timestamp < next_ts)) && (f->timestamp > 0)) { |
daniele | 10:dd7111d4279f | 1343 | next_ts = f->timestamp; |
daniele | 10:dd7111d4279f | 1344 | } |
daniele | 10:dd7111d4279f | 1345 | } |
daniele | 10:dd7111d4279f | 1346 | } |
daniele | 10:dd7111d4279f | 1347 | if (next_ts > 0) { |
daniele | 10:dd7111d4279f | 1348 | if ((next_ts + t->rto) > pico_tick) { |
daniele | 10:dd7111d4279f | 1349 | pico_timer_add(next_ts + t->rto - pico_tick, tcp_retrans_timeout, t); |
daniele | 10:dd7111d4279f | 1350 | } else { |
daniele | 10:dd7111d4279f | 1351 | pico_timer_add(1, tcp_retrans_timeout, t); |
daniele | 10:dd7111d4279f | 1352 | } |
daniele | 10:dd7111d4279f | 1353 | t->timer_running++; |
daniele | 10:dd7111d4279f | 1354 | } |
daniele | 10:dd7111d4279f | 1355 | } |
daniele | 10:dd7111d4279f | 1356 | |
daniele | 10:dd7111d4279f | 1357 | static int tcp_retrans(struct pico_socket_tcp *t, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1358 | { |
daniele | 10:dd7111d4279f | 1359 | struct pico_frame *cpy; |
daniele | 10:dd7111d4279f | 1360 | struct pico_tcp_hdr *hdr; |
daniele | 10:dd7111d4279f | 1361 | if (f) { |
daniele | 10:dd7111d4279f | 1362 | hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
daniele | 10:dd7111d4279f | 1363 | tcp_dbg("TCP> RETRANS (by dupack) frame %08x, len= %d\n", SEQN(f), f->payload_len); |
daniele | 10:dd7111d4279f | 1364 | f->timestamp = pico_tick; |
daniele | 10:dd7111d4279f | 1365 | tcp_add_options(t, f, 0, f->transport_len - f->payload_len - PICO_SIZE_TCPHDR); |
daniele | 10:dd7111d4279f | 1366 | hdr->rwnd = short_be(t->wnd); |
daniele | 10:dd7111d4279f | 1367 | hdr->flags |= PICO_TCP_PSH; |
daniele | 10:dd7111d4279f | 1368 | hdr->ack = long_be(t->rcv_nxt); |
daniele | 10:dd7111d4279f | 1369 | hdr->crc = 0; |
daniele | 10:dd7111d4279f | 1370 | hdr->crc = short_be(pico_tcp_checksum_ipv4(f)); |
daniele | 10:dd7111d4279f | 1371 | /* TCP: ENQUEUE to PROTO ( retransmit )*/ |
daniele | 10:dd7111d4279f | 1372 | cpy = pico_frame_copy(f); |
daniele | 10:dd7111d4279f | 1373 | if (pico_enqueue(&tcp_out, cpy) > 0) { |
daniele | 10:dd7111d4279f | 1374 | t->in_flight++; |
daniele | 10:dd7111d4279f | 1375 | t->snd_last_out = SEQN(cpy); |
daniele | 10:dd7111d4279f | 1376 | add_retransmission_timer(t, pico_tick + t->rto); |
daniele | 10:dd7111d4279f | 1377 | } else { |
daniele | 10:dd7111d4279f | 1378 | pico_frame_discard(cpy); |
daniele | 10:dd7111d4279f | 1379 | } |
daniele | 10:dd7111d4279f | 1380 | return(f->payload_len); |
daniele | 10:dd7111d4279f | 1381 | } |
daniele | 10:dd7111d4279f | 1382 | return 0; |
daniele | 10:dd7111d4279f | 1383 | } |
daniele | 10:dd7111d4279f | 1384 | |
daniele | 10:dd7111d4279f | 1385 | #ifdef TCP_ACK_DBG |
daniele | 10:dd7111d4279f | 1386 | static void tcp_ack_dbg(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1387 | { |
daniele | 10:dd7111d4279f | 1388 | uint32_t una, nxt, ack, cur; |
daniele | 10:dd7111d4279f | 1389 | struct pico_frame *una_f = NULL, *cur_f; |
daniele | 10:dd7111d4279f | 1390 | struct pico_tree_node *idx; |
daniele | 10:dd7111d4279f | 1391 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
daniele | 10:dd7111d4279f | 1392 | char info[64]; |
daniele | 10:dd7111d4279f | 1393 | char tmp[64]; |
daniele | 10:dd7111d4279f | 1394 | ack = ACKN(f); |
daniele | 10:dd7111d4279f | 1395 | nxt = t->snd_nxt; |
daniele | 10:dd7111d4279f | 1396 | tcp_dbg("===================================\n"); |
daniele | 10:dd7111d4279f | 1397 | tcp_dbg("Queue out (%d/%d). ACKED=%08x\n", t->tcpq_out.size, t->tcpq_out.max_size, ack); |
daniele | 10:dd7111d4279f | 1398 | |
daniele | 10:dd7111d4279f | 1399 | pico_tree_foreach(idx, &t->tcpq_out.pool) { |
daniele | 10:dd7111d4279f | 1400 | info[0] = 0; |
daniele | 10:dd7111d4279f | 1401 | cur_f = idx->keyValue; |
daniele | 10:dd7111d4279f | 1402 | cur = SEQN(cur_f); |
daniele | 10:dd7111d4279f | 1403 | if (!una_f) { |
daniele | 10:dd7111d4279f | 1404 | una_f = cur_f; |
daniele | 10:dd7111d4279f | 1405 | una = SEQN(una_f); |
daniele | 10:dd7111d4279f | 1406 | } |
daniele | 10:dd7111d4279f | 1407 | |
daniele | 10:dd7111d4279f | 1408 | if (cur == nxt) { |
daniele | 10:dd7111d4279f | 1409 | strncpy(tmp, info, strlen(info)); |
daniele | 10:dd7111d4279f | 1410 | snprintf(info,64, "%s SND_NXT", tmp); |
daniele | 10:dd7111d4279f | 1411 | } |
daniele | 10:dd7111d4279f | 1412 | if (cur == ack) { |
daniele | 10:dd7111d4279f | 1413 | strncpy(tmp, info, strlen(info)); |
daniele | 10:dd7111d4279f | 1414 | snprintf(info,64, "%s ACK", tmp); |
daniele | 10:dd7111d4279f | 1415 | } |
daniele | 10:dd7111d4279f | 1416 | if (cur == una) { |
daniele | 10:dd7111d4279f | 1417 | strncpy(tmp, info, strlen(info)); |
daniele | 10:dd7111d4279f | 1418 | snprintf(info,64, "%s SND_UNA", tmp); |
daniele | 10:dd7111d4279f | 1419 | } |
daniele | 10:dd7111d4279f | 1420 | if (cur == t->snd_last) { |
daniele | 10:dd7111d4279f | 1421 | strncpy(tmp, info, strlen(info)); |
daniele | 10:dd7111d4279f | 1422 | snprintf(info,64, "%s SND_LAST", tmp); |
daniele | 10:dd7111d4279f | 1423 | } |
daniele | 10:dd7111d4279f | 1424 | tcp_dbg("%08x %d%s\n", cur, cur_f->payload_len, info); |
daniele | 10:dd7111d4279f | 1425 | |
daniele | 10:dd7111d4279f | 1426 | } |
daniele | 10:dd7111d4279f | 1427 | tcp_dbg("SND_NXT is %08x, snd_LAST is %08x", nxt, t->snd_last); |
daniele | 10:dd7111d4279f | 1428 | tcp_dbg("===================================\n"); |
daniele | 10:dd7111d4279f | 1429 | tcp_dbg("\n\n"); |
daniele | 10:dd7111d4279f | 1430 | } |
daniele | 10:dd7111d4279f | 1431 | #endif |
daniele | 10:dd7111d4279f | 1432 | |
daniele | 10:dd7111d4279f | 1433 | static int tcp_ack(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1434 | { |
daniele | 10:dd7111d4279f | 1435 | struct pico_frame *f_new; /* use with Nagle to push to out queue */ |
daniele | 10:dd7111d4279f | 1436 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
daniele | 10:dd7111d4279f | 1437 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr; |
daniele | 10:dd7111d4279f | 1438 | uint32_t rtt = 0; |
daniele | 10:dd7111d4279f | 1439 | int acked = 0; |
daniele | 10:dd7111d4279f | 1440 | struct pico_frame *una = NULL; |
daniele | 10:dd7111d4279f | 1441 | if ((hdr->flags & PICO_TCP_ACK) == 0) |
daniele | 10:dd7111d4279f | 1442 | return -1; |
daniele | 10:dd7111d4279f | 1443 | |
daniele | 10:dd7111d4279f | 1444 | #ifdef TCP_ACK_DBG |
daniele | 10:dd7111d4279f | 1445 | tcp_ack_dbg(s,f); |
daniele | 10:dd7111d4279f | 1446 | #endif |
daniele | 10:dd7111d4279f | 1447 | |
daniele | 10:dd7111d4279f | 1448 | tcp_parse_options(f); |
daniele | 10:dd7111d4279f | 1449 | t->recv_wnd = short_be(hdr->rwnd); |
daniele | 10:dd7111d4279f | 1450 | |
daniele | 10:dd7111d4279f | 1451 | acked = tcp_ack_advance_una(t, f); |
daniele | 10:dd7111d4279f | 1452 | una = first_segment(&t->tcpq_out); |
daniele | 10:dd7111d4279f | 1453 | |
daniele | 10:dd7111d4279f | 1454 | if ((t->x_mode == PICO_TCP_BLACKOUT) || |
daniele | 10:dd7111d4279f | 1455 | ((t->x_mode == PICO_TCP_WINDOW_FULL) && ((t->recv_wnd << t->recv_wnd_scale) > t->mss))) { |
daniele | 10:dd7111d4279f | 1456 | tcp_dbg("Re-entering look-ahead...\n\n\n"); |
daniele | 10:dd7111d4279f | 1457 | t->x_mode = PICO_TCP_LOOKAHEAD; |
daniele | 10:dd7111d4279f | 1458 | t->backoff = 0; |
daniele | 10:dd7111d4279f | 1459 | } |
daniele | 10:dd7111d4279f | 1460 | |
daniele | 10:dd7111d4279f | 1461 | /* One should be acked. */ |
daniele | 10:dd7111d4279f | 1462 | // if ((acked == 0) && (t->in_flight > 0)) |
daniele | 10:dd7111d4279f | 1463 | if ((acked == 0) && (f->payload_len == 0) && (t->in_flight > 0)) |
daniele | 10:dd7111d4279f | 1464 | t->in_flight--; |
daniele | 10:dd7111d4279f | 1465 | if (!una || acked > 0) { |
daniele | 10:dd7111d4279f | 1466 | t->x_mode = PICO_TCP_LOOKAHEAD; |
daniele | 10:dd7111d4279f | 1467 | tcp_dbg("Mode: Look-ahead. In flight: %d/%d buf: %d\n", t->in_flight, t->cwnd, t->tcpq_out.frames); |
daniele | 10:dd7111d4279f | 1468 | t->backoff = 0; |
daniele | 10:dd7111d4279f | 1469 | |
daniele | 10:dd7111d4279f | 1470 | /* Do rtt/rttvar/rto calculations */ |
daniele | 10:dd7111d4279f | 1471 | if(una && (una->timestamp != 0)) { |
daniele | 10:dd7111d4279f | 1472 | rtt = time_diff(pico_tick, una->timestamp); |
daniele | 10:dd7111d4279f | 1473 | if (rtt) |
daniele | 10:dd7111d4279f | 1474 | tcp_rtt(t, rtt); |
daniele | 10:dd7111d4279f | 1475 | } |
daniele | 10:dd7111d4279f | 1476 | |
daniele | 10:dd7111d4279f | 1477 | 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)); |
daniele | 10:dd7111d4279f | 1478 | if (acked > t->in_flight) { |
daniele | 10:dd7111d4279f | 1479 | tcp_dbg("WARNING: in flight < 0\n"); |
daniele | 10:dd7111d4279f | 1480 | t->in_flight = 0; |
daniele | 10:dd7111d4279f | 1481 | } else |
daniele | 10:dd7111d4279f | 1482 | t->in_flight -= (acked); |
daniele | 10:dd7111d4279f | 1483 | |
daniele | 10:dd7111d4279f | 1484 | } else if ((t->snd_old_ack == ACKN(f)) && /* We've just seen this ack, and... */ |
daniele | 10:dd7111d4279f | 1485 | ((0 == (hdr->flags & (PICO_TCP_PSH | PICO_TCP_SYN))) && |
daniele | 10:dd7111d4279f | 1486 | (f->payload_len == 0)) && /* This is a pure ack, and... */ |
daniele | 10:dd7111d4279f | 1487 | (ACKN(f) != t->snd_nxt)) /* There is something in flight awaiting to be acked... */ |
daniele | 10:dd7111d4279f | 1488 | { |
daniele | 10:dd7111d4279f | 1489 | /* Process incoming duplicate ack. */ |
daniele | 10:dd7111d4279f | 1490 | if (t->x_mode < PICO_TCP_RECOVER) { |
daniele | 10:dd7111d4279f | 1491 | t->x_mode++; |
daniele | 10:dd7111d4279f | 1492 | tcp_dbg("Mode: DUPACK %d, due to PURE ACK %0x, len = %d\n", t->x_mode, SEQN(f), f->payload_len); |
daniele | 10:dd7111d4279f | 1493 | tcp_dbg("ACK: %x - QUEUE: %x\n",ACKN(f), SEQN(first_segment(&t->tcpq_out))); |
daniele | 10:dd7111d4279f | 1494 | if (t->x_mode == PICO_TCP_RECOVER) { /* Switching mode */ |
daniele | 10:dd7111d4279f | 1495 | t->snd_retry = SEQN(first_segment(&t->tcpq_out)); |
daniele | 10:dd7111d4279f | 1496 | if (t->ssthresh > t->cwnd) |
daniele | 10:dd7111d4279f | 1497 | t->ssthresh >>=2; |
daniele | 10:dd7111d4279f | 1498 | else |
daniele | 10:dd7111d4279f | 1499 | t->ssthresh = (t->cwnd >> 1); |
daniele | 10:dd7111d4279f | 1500 | if (t->ssthresh < 2) |
daniele | 10:dd7111d4279f | 1501 | t->ssthresh = 2; |
daniele | 10:dd7111d4279f | 1502 | } |
daniele | 10:dd7111d4279f | 1503 | } else if (t->x_mode == PICO_TCP_RECOVER) { |
daniele | 10:dd7111d4279f | 1504 | 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)); |
daniele | 10:dd7111d4279f | 1505 | if (t->in_flight <= t->cwnd) { |
daniele | 10:dd7111d4279f | 1506 | struct pico_frame *nxt = peek_segment(&t->tcpq_out, t->snd_retry); |
daniele | 10:dd7111d4279f | 1507 | if (!nxt) |
daniele | 10:dd7111d4279f | 1508 | nxt = first_segment(&t->tcpq_out); |
daniele | 10:dd7111d4279f | 1509 | |
daniele | 10:dd7111d4279f | 1510 | while (nxt && (nxt->flags & PICO_FRAME_FLAG_SACKED) && (nxt != first_segment(&t->tcpq_out))) { |
daniele | 10:dd7111d4279f | 1511 | tcp_dbg("Skipping %08x because it is sacked.\n", SEQN(nxt)); |
daniele | 10:dd7111d4279f | 1512 | nxt = next_segment(&t->tcpq_out, nxt); |
daniele | 10:dd7111d4279f | 1513 | } |
daniele | 10:dd7111d4279f | 1514 | |
daniele | 10:dd7111d4279f | 1515 | if (nxt && (seq_compare(SEQN(nxt), t->snd_nxt)) > 0) |
daniele | 10:dd7111d4279f | 1516 | nxt = NULL; |
daniele | 10:dd7111d4279f | 1517 | if (nxt && (seq_compare(SEQN(nxt), SEQN(first_segment(&t->tcpq_out))) > (t->recv_wnd << t->recv_wnd_scale))) |
daniele | 10:dd7111d4279f | 1518 | nxt = NULL; |
daniele | 10:dd7111d4279f | 1519 | |
daniele | 10:dd7111d4279f | 1520 | if(!nxt) |
daniele | 10:dd7111d4279f | 1521 | nxt = first_segment(&t->tcpq_out); |
daniele | 10:dd7111d4279f | 1522 | if (nxt) { |
daniele | 10:dd7111d4279f | 1523 | tcp_retrans(t, peek_segment(&t->tcpq_out, t->snd_retry)); |
daniele | 10:dd7111d4279f | 1524 | t->snd_retry = SEQN(nxt); |
daniele | 10:dd7111d4279f | 1525 | } |
daniele | 10:dd7111d4279f | 1526 | } |
daniele | 10:dd7111d4279f | 1527 | |
daniele | 10:dd7111d4279f | 1528 | if (++t->cwnd_counter > 1) { |
daniele | 10:dd7111d4279f | 1529 | t->cwnd--; |
daniele | 10:dd7111d4279f | 1530 | if (t->cwnd < 2) |
daniele | 10:dd7111d4279f | 1531 | t->cwnd = 2; |
daniele | 10:dd7111d4279f | 1532 | t->cwnd_counter = 0; |
daniele | 10:dd7111d4279f | 1533 | } |
daniele | 10:dd7111d4279f | 1534 | } else { |
daniele | 10:dd7111d4279f | 1535 | tcp_dbg("DUPACK in mode %d \n", t->x_mode); |
daniele | 10:dd7111d4279f | 1536 | |
daniele | 10:dd7111d4279f | 1537 | } |
daniele | 10:dd7111d4279f | 1538 | } /* End case duplicate ack detection */ |
daniele | 10:dd7111d4279f | 1539 | |
daniele | 10:dd7111d4279f | 1540 | /* Do congestion control */ |
daniele | 10:dd7111d4279f | 1541 | tcp_congestion_control(t); |
daniele | 10:dd7111d4279f | 1542 | if ((acked > 0) && t->sock.wakeup) { |
daniele | 10:dd7111d4279f | 1543 | if (t->tcpq_out.size < t->tcpq_out.max_size) |
daniele | 10:dd7111d4279f | 1544 | t->sock.wakeup(PICO_SOCK_EV_WR, &(t->sock)); |
daniele | 10:dd7111d4279f | 1545 | //t->sock.ev_pending |= PICO_SOCK_EV_WR; |
daniele | 10:dd7111d4279f | 1546 | } |
daniele | 10:dd7111d4279f | 1547 | |
daniele | 10:dd7111d4279f | 1548 | /* if Nagle enabled, check if no unack'ed data and fill out queue (till window) */ |
daniele | 10:dd7111d4279f | 1549 | if (IS_NAGLE_ENABLED((&(t->sock)))) { |
daniele | 10:dd7111d4279f | 1550 | while (!IS_TCP_HOLDQ_EMPTY(t) && ((t->tcpq_out.max_size - t->tcpq_out.size) >= PICO_TCP_DEFAULT_MSS)) { |
daniele | 10:dd7111d4279f | 1551 | tcp_dbg("TCP_ACK - NAGLE add new segment\n"); |
daniele | 10:dd7111d4279f | 1552 | f_new = pico_hold_segment_make(t); |
daniele | 10:dd7111d4279f | 1553 | if (f_new == NULL) |
daniele | 10:dd7111d4279f | 1554 | break; /* XXX corrupt !!! (or no memory) */ |
daniele | 10:dd7111d4279f | 1555 | if (pico_enqueue_segment(&t->tcpq_out,f_new) <= 0) |
daniele | 10:dd7111d4279f | 1556 | // handle error |
daniele | 10:dd7111d4279f | 1557 | tcp_dbg("TCP_ACK - NAGLE FAILED to enqueue in out\n"); |
daniele | 10:dd7111d4279f | 1558 | } |
daniele | 10:dd7111d4279f | 1559 | } |
daniele | 10:dd7111d4279f | 1560 | |
daniele | 10:dd7111d4279f | 1561 | /* If some space was created, put a few segments out. */ |
daniele | 10:dd7111d4279f | 1562 | tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", pico_tick, t->cwnd, t->ssthresh, t->in_flight); |
daniele | 10:dd7111d4279f | 1563 | if (t->x_mode == PICO_TCP_LOOKAHEAD) { |
daniele | 10:dd7111d4279f | 1564 | if ((t->cwnd >= t->in_flight) && (t->snd_nxt > t->snd_last_out)) { |
daniele | 10:dd7111d4279f | 1565 | pico_tcp_output(&t->sock, t->cwnd - t->in_flight); |
daniele | 10:dd7111d4279f | 1566 | } |
daniele | 10:dd7111d4279f | 1567 | } |
daniele | 10:dd7111d4279f | 1568 | |
daniele | 10:dd7111d4279f | 1569 | t->snd_old_ack = ACKN(f); |
daniele | 10:dd7111d4279f | 1570 | return 0; |
daniele | 10:dd7111d4279f | 1571 | } |
daniele | 10:dd7111d4279f | 1572 | |
daniele | 10:dd7111d4279f | 1573 | static int tcp_finwaitack(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1574 | { |
daniele | 10:dd7111d4279f | 1575 | tcp_dbg("RECEIVED ACK IN FIN_WAIT1\nTCP> IN STATE FIN_WAIT2\n"); |
daniele | 10:dd7111d4279f | 1576 | |
daniele | 10:dd7111d4279f | 1577 | /* acking part */ |
daniele | 10:dd7111d4279f | 1578 | tcp_ack(s,f); |
daniele | 10:dd7111d4279f | 1579 | /* update TCP state */ |
daniele | 10:dd7111d4279f | 1580 | s->state &= 0x00FFU; |
daniele | 10:dd7111d4279f | 1581 | s->state |= PICO_SOCKET_STATE_TCP_FIN_WAIT2; |
daniele | 10:dd7111d4279f | 1582 | |
daniele | 10:dd7111d4279f | 1583 | return 0; |
daniele | 10:dd7111d4279f | 1584 | } |
daniele | 10:dd7111d4279f | 1585 | |
daniele | 10:dd7111d4279f | 1586 | static void tcp_deltcb(unsigned long when, void *arg) |
daniele | 10:dd7111d4279f | 1587 | { |
daniele | 10:dd7111d4279f | 1588 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg; |
daniele | 10:dd7111d4279f | 1589 | if (TCPSTATE(&t->sock) == PICO_SOCKET_STATE_TCP_TIME_WAIT) { |
daniele | 10:dd7111d4279f | 1590 | tcp_dbg("TCP> state: time_wait, final timer expired, going to closed state\n"); |
daniele | 10:dd7111d4279f | 1591 | /* update state */ |
daniele | 10:dd7111d4279f | 1592 | (t->sock).state &= 0x00FFU; |
daniele | 10:dd7111d4279f | 1593 | (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED; |
daniele | 10:dd7111d4279f | 1594 | (t->sock).state &= 0xFF00U; |
daniele | 10:dd7111d4279f | 1595 | (t->sock).state |= PICO_SOCKET_STATE_CLOSED; |
daniele | 10:dd7111d4279f | 1596 | /* call EV_FIN wakeup before deleting */ |
daniele | 10:dd7111d4279f | 1597 | if (t->sock.wakeup) { |
daniele | 10:dd7111d4279f | 1598 | (t->sock).wakeup(PICO_SOCK_EV_FIN, &(t->sock)); |
daniele | 10:dd7111d4279f | 1599 | } |
daniele | 10:dd7111d4279f | 1600 | /* delete socket */ |
daniele | 10:dd7111d4279f | 1601 | pico_socket_del(&t->sock); |
daniele | 10:dd7111d4279f | 1602 | } else { |
daniele | 10:dd7111d4279f | 1603 | tcp_dbg("TCP> trying to go to closed, wrong state\n"); |
daniele | 10:dd7111d4279f | 1604 | } |
daniele | 10:dd7111d4279f | 1605 | } |
daniele | 10:dd7111d4279f | 1606 | |
daniele | 10:dd7111d4279f | 1607 | static int tcp_finwaitfin(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1608 | { |
daniele | 10:dd7111d4279f | 1609 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
daniele | 10:dd7111d4279f | 1610 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) (f->transport_hdr); |
daniele | 10:dd7111d4279f | 1611 | tcp_dbg("TCP> received fin in FIN_WAIT2\n"); |
daniele | 10:dd7111d4279f | 1612 | /* received FIN, increase ACK nr */ |
daniele | 10:dd7111d4279f | 1613 | t->rcv_nxt = long_be(hdr->seq) + 1; |
daniele | 10:dd7111d4279f | 1614 | s->state &= 0x00FFU; |
daniele | 10:dd7111d4279f | 1615 | s->state |= PICO_SOCKET_STATE_TCP_TIME_WAIT; |
daniele | 10:dd7111d4279f | 1616 | /* set SHUT_REMOTE */ |
daniele | 10:dd7111d4279f | 1617 | s->state |= PICO_SOCKET_STATE_SHUT_REMOTE; |
daniele | 10:dd7111d4279f | 1618 | if (s->wakeup) |
daniele | 10:dd7111d4279f | 1619 | s->wakeup(PICO_SOCK_EV_CLOSE, s); |
daniele | 10:dd7111d4279f | 1620 | if (f->payload_len > 0) /* needed?? */ |
daniele | 10:dd7111d4279f | 1621 | tcp_data_in(s,f); |
daniele | 10:dd7111d4279f | 1622 | /* send ACK */ |
daniele | 10:dd7111d4279f | 1623 | tcp_send_ack(t); |
daniele | 10:dd7111d4279f | 1624 | /* set timer */ |
daniele | 10:dd7111d4279f | 1625 | pico_timer_add(200, tcp_deltcb, t); |
daniele | 10:dd7111d4279f | 1626 | return 0; |
daniele | 10:dd7111d4279f | 1627 | } |
daniele | 10:dd7111d4279f | 1628 | |
daniele | 10:dd7111d4279f | 1629 | static int tcp_closewaitack(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1630 | { |
daniele | 10:dd7111d4279f | 1631 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
daniele | 10:dd7111d4279f | 1632 | tcp_dbg("TCP> received ack in CLOSING\n"); |
daniele | 10:dd7111d4279f | 1633 | /* acking part */ |
daniele | 10:dd7111d4279f | 1634 | tcp_ack(s,f); |
daniele | 10:dd7111d4279f | 1635 | /* update TCP state */ |
daniele | 10:dd7111d4279f | 1636 | s->state &= 0x00FFU; |
daniele | 10:dd7111d4279f | 1637 | s->state |= PICO_SOCKET_STATE_TCP_TIME_WAIT; |
daniele | 10:dd7111d4279f | 1638 | /* set timer */ |
daniele | 10:dd7111d4279f | 1639 | pico_timer_add(200, tcp_deltcb, t); |
daniele | 10:dd7111d4279f | 1640 | return 0; |
daniele | 10:dd7111d4279f | 1641 | } |
daniele | 10:dd7111d4279f | 1642 | |
daniele | 10:dd7111d4279f | 1643 | static int tcp_lastackwait(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1644 | { |
daniele | 10:dd7111d4279f | 1645 | tcp_dbg("TCP> state: last_ack, received ack, to closed\n"); |
daniele | 10:dd7111d4279f | 1646 | s->state &= 0x00FFU; |
daniele | 10:dd7111d4279f | 1647 | s->state |= PICO_SOCKET_STATE_TCP_CLOSED; |
daniele | 10:dd7111d4279f | 1648 | s->state &= 0xFF00U; |
daniele | 10:dd7111d4279f | 1649 | s->state |= PICO_SOCKET_STATE_CLOSED; |
daniele | 10:dd7111d4279f | 1650 | /* call socket wakeup with EV_FIN */ |
daniele | 10:dd7111d4279f | 1651 | if (s->wakeup) |
daniele | 10:dd7111d4279f | 1652 | s->wakeup(PICO_SOCK_EV_FIN, s); |
daniele | 10:dd7111d4279f | 1653 | /* delete socket */ |
daniele | 10:dd7111d4279f | 1654 | pico_socket_del(s); |
daniele | 10:dd7111d4279f | 1655 | return 0; |
daniele | 10:dd7111d4279f | 1656 | } |
daniele | 10:dd7111d4279f | 1657 | |
daniele | 10:dd7111d4279f | 1658 | static int tcp_syn(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1659 | { |
daniele | 10:dd7111d4279f | 1660 | /* TODO: Check against backlog length */ |
daniele | 10:dd7111d4279f | 1661 | struct pico_socket_tcp *new = (struct pico_socket_tcp *)pico_socket_clone(s); |
daniele | 10:dd7111d4279f | 1662 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
daniele | 10:dd7111d4279f | 1663 | if (!new) |
daniele | 10:dd7111d4279f | 1664 | return -1; |
daniele | 10:dd7111d4279f | 1665 | |
daniele | 10:dd7111d4279f | 1666 | #ifdef PICO_TCP_SUPPORT_SOCKET_STATS |
daniele | 10:dd7111d4279f | 1667 | pico_timer_add(2000, sock_stats, s); |
daniele | 10:dd7111d4279f | 1668 | #endif |
daniele | 10:dd7111d4279f | 1669 | |
daniele | 10:dd7111d4279f | 1670 | new->sock.remote_port = ((struct pico_trans *)f->transport_hdr)->sport; |
daniele | 10:dd7111d4279f | 1671 | #ifdef PICO_SUPPORT_IPV4 |
daniele | 10:dd7111d4279f | 1672 | if (IS_IPV4(f)) { |
daniele | 10:dd7111d4279f | 1673 | new->sock.remote_addr.ip4.addr = ((struct pico_ipv4_hdr *)(f->net_hdr))->src.addr; |
daniele | 10:dd7111d4279f | 1674 | new->sock.local_addr.ip4.addr = ((struct pico_ipv4_hdr *)(f->net_hdr))->dst.addr; |
daniele | 10:dd7111d4279f | 1675 | } |
daniele | 10:dd7111d4279f | 1676 | #endif |
daniele | 10:dd7111d4279f | 1677 | #ifdef PICO_SUPPORT_IPV6 |
daniele | 10:dd7111d4279f | 1678 | if (IS_IPV6(f)) { |
daniele | 10:dd7111d4279f | 1679 | memcpy(new->sock.remote_addr.ip6.addr, ((struct pico_ipv6_hdr *)(f->net_hdr))->src, PICO_SIZE_IP6); |
daniele | 10:dd7111d4279f | 1680 | memcpy(new->sock.remote_addr.ip6.addr, ((struct pico_ipv6_hdr *)(f->net_hdr))->src, PICO_SIZE_IP6); |
daniele | 10:dd7111d4279f | 1681 | } |
daniele | 10:dd7111d4279f | 1682 | #endif |
daniele | 10:dd7111d4279f | 1683 | |
daniele | 10:dd7111d4279f | 1684 | /* Set socket limits */ |
daniele | 10:dd7111d4279f | 1685 | new->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ; |
daniele | 10:dd7111d4279f | 1686 | new->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ; |
daniele | 10:dd7111d4279f | 1687 | new->tcpq_hold.max_size = 2*PICO_TCP_DEFAULT_MSS; |
daniele | 10:dd7111d4279f | 1688 | |
daniele | 10:dd7111d4279f | 1689 | f->sock = &new->sock; |
daniele | 10:dd7111d4279f | 1690 | tcp_parse_options(f); |
daniele | 10:dd7111d4279f | 1691 | new->mss = PICO_TCP_DEFAULT_MSS; |
daniele | 10:dd7111d4279f | 1692 | new->rcv_nxt = long_be(hdr->seq) + 1; |
daniele | 10:dd7111d4279f | 1693 | new->snd_nxt = long_be(pico_paws()); |
daniele | 10:dd7111d4279f | 1694 | new->snd_last = new->snd_nxt; |
daniele | 10:dd7111d4279f | 1695 | new->cwnd = PICO_TCP_IW; |
daniele | 10:dd7111d4279f | 1696 | new->ssthresh = 40; |
daniele | 10:dd7111d4279f | 1697 | new->recv_wnd = short_be(hdr->rwnd); |
daniele | 10:dd7111d4279f | 1698 | new->jumbo = hdr->len & 0x07; |
daniele | 10:dd7111d4279f | 1699 | new->sock.parent = s; |
daniele | 10:dd7111d4279f | 1700 | new->sock.wakeup = s->wakeup; |
daniele | 10:dd7111d4279f | 1701 | /* Initialize timestamp values */ |
daniele | 10:dd7111d4279f | 1702 | new->sock.state = PICO_SOCKET_STATE_BOUND | PICO_SOCKET_STATE_CONNECTED | PICO_SOCKET_STATE_TCP_SYN_RECV; |
daniele | 10:dd7111d4279f | 1703 | pico_socket_add(&new->sock); |
daniele | 10:dd7111d4279f | 1704 | tcp_send_synack(&new->sock); |
daniele | 10:dd7111d4279f | 1705 | tcp_dbg("SYNACK sent, socket added. snd_nxt is %08x\n", new->snd_nxt); |
daniele | 10:dd7111d4279f | 1706 | return 0; |
daniele | 10:dd7111d4279f | 1707 | } |
daniele | 10:dd7111d4279f | 1708 | |
daniele | 10:dd7111d4279f | 1709 | static void tcp_set_init_point(struct pico_socket *s) |
daniele | 10:dd7111d4279f | 1710 | { |
daniele | 10:dd7111d4279f | 1711 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
daniele | 10:dd7111d4279f | 1712 | t->rcv_processed = t->rcv_nxt; |
daniele | 10:dd7111d4279f | 1713 | } |
daniele | 10:dd7111d4279f | 1714 | |
daniele | 10:dd7111d4279f | 1715 | static int tcp_synack(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1716 | { |
daniele | 10:dd7111d4279f | 1717 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
daniele | 10:dd7111d4279f | 1718 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
daniele | 10:dd7111d4279f | 1719 | |
daniele | 10:dd7111d4279f | 1720 | if (ACKN(f) == (1 + t->snd_nxt)) { |
daniele | 10:dd7111d4279f | 1721 | t->rcv_nxt = long_be(hdr->seq); |
daniele | 10:dd7111d4279f | 1722 | t->rcv_processed = t->rcv_nxt + 1; |
daniele | 10:dd7111d4279f | 1723 | tcp_ack(s, f); |
daniele | 10:dd7111d4279f | 1724 | |
daniele | 10:dd7111d4279f | 1725 | s->state &= 0x00FFU; |
daniele | 10:dd7111d4279f | 1726 | s->state |= PICO_SOCKET_STATE_TCP_ESTABLISHED; |
daniele | 10:dd7111d4279f | 1727 | tcp_dbg("TCP> Established. State: %x\n", s->state); |
daniele | 10:dd7111d4279f | 1728 | |
daniele | 10:dd7111d4279f | 1729 | if (s->wakeup) |
daniele | 10:dd7111d4279f | 1730 | s->wakeup(PICO_SOCK_EV_CONN, s); |
daniele | 10:dd7111d4279f | 1731 | s->ev_pending |= PICO_SOCK_EV_WR; |
daniele | 10:dd7111d4279f | 1732 | |
daniele | 10:dd7111d4279f | 1733 | t->rcv_nxt++; |
daniele | 10:dd7111d4279f | 1734 | t->snd_nxt++; |
daniele | 10:dd7111d4279f | 1735 | tcp_send_ack(t); /* return ACK */ |
daniele | 10:dd7111d4279f | 1736 | |
daniele | 10:dd7111d4279f | 1737 | return 0; |
daniele | 10:dd7111d4279f | 1738 | |
daniele | 10:dd7111d4279f | 1739 | } else { |
daniele | 10:dd7111d4279f | 1740 | tcp_dbg("TCP> Not established, RST sent.\n"); |
daniele | 10:dd7111d4279f | 1741 | tcp_nosync_rst(s,f); |
daniele | 10:dd7111d4279f | 1742 | return 0; |
daniele | 10:dd7111d4279f | 1743 | } |
daniele | 10:dd7111d4279f | 1744 | } |
daniele | 10:dd7111d4279f | 1745 | |
daniele | 10:dd7111d4279f | 1746 | static int tcp_first_ack(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1747 | { |
daniele | 10:dd7111d4279f | 1748 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
daniele | 10:dd7111d4279f | 1749 | tcp_dbg("ACK in SYN_RECV: expecting %08x got %08x\n", t->snd_nxt, ACKN(f)); |
daniele | 10:dd7111d4279f | 1750 | if (t->snd_nxt == ACKN(f)) { |
daniele | 10:dd7111d4279f | 1751 | tcp_set_init_point(s); |
daniele | 10:dd7111d4279f | 1752 | tcp_ack(s, f); |
daniele | 10:dd7111d4279f | 1753 | s->state &= 0x00FFU; |
daniele | 10:dd7111d4279f | 1754 | s->state |= PICO_SOCKET_STATE_TCP_ESTABLISHED; |
daniele | 10:dd7111d4279f | 1755 | tcp_dbg("TCP: Established. State now: %04x\n", s->state); |
daniele | 10:dd7111d4279f | 1756 | if( !s->parent && s->wakeup) { /* If the socket has no parent, -> sending socket that has a sim_open */ |
daniele | 10:dd7111d4279f | 1757 | tcp_dbg("FIRST ACK - No parent found -> sending socket\n"); |
daniele | 10:dd7111d4279f | 1758 | s->wakeup(PICO_SOCK_EV_CONN, s); |
daniele | 10:dd7111d4279f | 1759 | } |
daniele | 10:dd7111d4279f | 1760 | if (s->parent && s->parent->wakeup) { |
daniele | 10:dd7111d4279f | 1761 | tcp_dbg("FIRST ACK - Parent found -> listening socket\n"); |
daniele | 10:dd7111d4279f | 1762 | s->wakeup = s->parent->wakeup; |
daniele | 10:dd7111d4279f | 1763 | s->parent->wakeup(PICO_SOCK_EV_CONN, s->parent); |
daniele | 10:dd7111d4279f | 1764 | } |
daniele | 10:dd7111d4279f | 1765 | s->ev_pending |= PICO_SOCK_EV_WR; |
daniele | 10:dd7111d4279f | 1766 | tcp_dbg("%s: snd_nxt is now %08x\n", __FUNCTION__, t->snd_nxt); |
daniele | 10:dd7111d4279f | 1767 | return 0; |
daniele | 10:dd7111d4279f | 1768 | } else { |
daniele | 10:dd7111d4279f | 1769 | tcp_nosync_rst(s,f); |
daniele | 10:dd7111d4279f | 1770 | return 0; |
daniele | 10:dd7111d4279f | 1771 | } |
daniele | 10:dd7111d4279f | 1772 | } |
daniele | 10:dd7111d4279f | 1773 | |
daniele | 10:dd7111d4279f | 1774 | static int tcp_closewait(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1775 | { |
daniele | 10:dd7111d4279f | 1776 | |
daniele | 10:dd7111d4279f | 1777 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
daniele | 10:dd7111d4279f | 1778 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) (f->transport_hdr); |
daniele | 10:dd7111d4279f | 1779 | |
daniele | 10:dd7111d4279f | 1780 | if (f->payload_len > 0) |
daniele | 10:dd7111d4279f | 1781 | tcp_data_in(s,f); |
daniele | 10:dd7111d4279f | 1782 | if (f->flags & PICO_TCP_ACK) |
daniele | 10:dd7111d4279f | 1783 | tcp_ack(s,f); |
daniele | 10:dd7111d4279f | 1784 | if (seq_compare(SEQN(f), t->rcv_nxt) == 0) { |
tass | 31:d3b2dfcc358f | 1785 | if(t->tcpq_out.frames == 0) |
tass | 31:d3b2dfcc358f | 1786 | { |
tass | 31:d3b2dfcc358f | 1787 | /* received FIN, increase ACK nr */ |
tass | 31:d3b2dfcc358f | 1788 | t->rcv_nxt = long_be(hdr->seq) + 1; |
tass | 31:d3b2dfcc358f | 1789 | s->state &= 0x00FFU; |
tass | 31:d3b2dfcc358f | 1790 | s->state |= PICO_SOCKET_STATE_TCP_CLOSE_WAIT; |
tass | 31:d3b2dfcc358f | 1791 | /* set SHUT_REMOTE */ |
tass | 31:d3b2dfcc358f | 1792 | s->state |= PICO_SOCKET_STATE_SHUT_REMOTE; |
tass | 31:d3b2dfcc358f | 1793 | tcp_dbg("TCP> Close-wait\n"); |
tass | 31:d3b2dfcc358f | 1794 | if (s->wakeup){ |
tass | 31:d3b2dfcc358f | 1795 | if(f->payload_len>0){ |
tass | 31:d3b2dfcc358f | 1796 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
tass | 31:d3b2dfcc358f | 1797 | t->sock.ev_pending |=PICO_SOCK_EV_CLOSE; |
tass | 31:d3b2dfcc358f | 1798 | }else |
tass | 31:d3b2dfcc358f | 1799 | s->wakeup(PICO_SOCK_EV_CLOSE, s); |
tass | 31:d3b2dfcc358f | 1800 | } |
tass | 31:d3b2dfcc358f | 1801 | } |
tass | 31:d3b2dfcc358f | 1802 | else |
tass | 31:d3b2dfcc358f | 1803 | { |
tass | 31:d3b2dfcc358f | 1804 | if(!t->closing) |
tass | 31:d3b2dfcc358f | 1805 | { |
tass | 31:d3b2dfcc358f | 1806 | /* Update ACK for the first FIN */ |
tass | 31:d3b2dfcc358f | 1807 | t->rcv_nxt = long_be(hdr->seq) + 1; |
tass | 31:d3b2dfcc358f | 1808 | } |
tass | 31:d3b2dfcc358f | 1809 | t->closing = 1; |
daniele | 10:dd7111d4279f | 1810 | } |
daniele | 10:dd7111d4279f | 1811 | } else { |
daniele | 10:dd7111d4279f | 1812 | tcp_send_ack(t); /* return ACK */ |
daniele | 10:dd7111d4279f | 1813 | } |
daniele | 10:dd7111d4279f | 1814 | return 0; |
daniele | 10:dd7111d4279f | 1815 | } |
daniele | 10:dd7111d4279f | 1816 | |
daniele | 10:dd7111d4279f | 1817 | /*static int tcp_fin(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1818 | { |
daniele | 10:dd7111d4279f | 1819 | return 0; |
daniele | 10:dd7111d4279f | 1820 | }*/ |
daniele | 10:dd7111d4279f | 1821 | |
daniele | 10:dd7111d4279f | 1822 | static int tcp_rcvfin(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1823 | { |
daniele | 10:dd7111d4279f | 1824 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
daniele | 10:dd7111d4279f | 1825 | tcp_dbg("TCP> Received FIN in FIN_WAIT1\n"); |
daniele | 10:dd7111d4279f | 1826 | s->state &= 0x00FFU; |
daniele | 10:dd7111d4279f | 1827 | s->state |= PICO_SOCKET_STATE_TCP_CLOSING; |
daniele | 10:dd7111d4279f | 1828 | t->rcv_processed = t->rcv_nxt + 1; |
daniele | 10:dd7111d4279f | 1829 | t->rcv_nxt++; |
daniele | 10:dd7111d4279f | 1830 | /* send ACK */ |
daniele | 10:dd7111d4279f | 1831 | tcp_send_ack(t); |
daniele | 10:dd7111d4279f | 1832 | return 0; |
daniele | 10:dd7111d4279f | 1833 | } |
daniele | 10:dd7111d4279f | 1834 | |
daniele | 10:dd7111d4279f | 1835 | static int tcp_finack(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1836 | { |
daniele | 10:dd7111d4279f | 1837 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
daniele | 10:dd7111d4279f | 1838 | tcp_dbg("TCP> ENTERED finack\n"); |
daniele | 10:dd7111d4279f | 1839 | t->rcv_nxt++; |
daniele | 10:dd7111d4279f | 1840 | /* send ACK */ |
daniele | 10:dd7111d4279f | 1841 | tcp_send_ack(t); |
daniele | 10:dd7111d4279f | 1842 | |
daniele | 10:dd7111d4279f | 1843 | /* call socket wakeup with EV_FIN */ |
daniele | 10:dd7111d4279f | 1844 | if (s->wakeup) |
daniele | 10:dd7111d4279f | 1845 | s->wakeup(PICO_SOCK_EV_FIN, s); |
daniele | 10:dd7111d4279f | 1846 | s->state &= 0x00FFU; |
daniele | 10:dd7111d4279f | 1847 | s->state |= PICO_SOCKET_STATE_TCP_TIME_WAIT; |
daniele | 10:dd7111d4279f | 1848 | /* set SHUT_REMOTE */ |
daniele | 10:dd7111d4279f | 1849 | s->state |= PICO_SOCKET_STATE_SHUT_REMOTE; |
daniele | 10:dd7111d4279f | 1850 | pico_timer_add(2000, tcp_deltcb, t); |
daniele | 10:dd7111d4279f | 1851 | |
daniele | 10:dd7111d4279f | 1852 | return 0; |
daniele | 10:dd7111d4279f | 1853 | } |
daniele | 10:dd7111d4279f | 1854 | |
daniele | 10:dd7111d4279f | 1855 | static int tcp_rst(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1856 | { |
daniele | 10:dd7111d4279f | 1857 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) s; |
daniele | 10:dd7111d4279f | 1858 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) (f->transport_hdr); |
daniele | 10:dd7111d4279f | 1859 | |
daniele | 10:dd7111d4279f | 1860 | tcp_dbg("TCP >>>>>>>>>>>>>> received RST <<<<<<<<<<<<<<<<<<<<\n"); |
daniele | 10:dd7111d4279f | 1861 | if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_SYN_SENT) { |
daniele | 10:dd7111d4279f | 1862 | /* the RST is acceptable if the ACK field acknowledges the SYN */ |
daniele | 10:dd7111d4279f | 1863 | if ((t->snd_nxt + 1) == ACKN(f)) { /* valid, got to closed state */ |
daniele | 10:dd7111d4279f | 1864 | /* update state */ |
daniele | 10:dd7111d4279f | 1865 | (t->sock).state &= 0x00FFU; |
daniele | 10:dd7111d4279f | 1866 | (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED; |
daniele | 10:dd7111d4279f | 1867 | (t->sock).state &= 0xFF00U; |
daniele | 10:dd7111d4279f | 1868 | (t->sock).state |= PICO_SOCKET_STATE_CLOSED; |
daniele | 10:dd7111d4279f | 1869 | |
daniele | 10:dd7111d4279f | 1870 | /* call EV_FIN wakeup before deleting */ |
daniele | 10:dd7111d4279f | 1871 | if ((t->sock).wakeup) |
daniele | 10:dd7111d4279f | 1872 | (t->sock).wakeup(PICO_SOCK_EV_FIN, &(t->sock)); |
daniele | 10:dd7111d4279f | 1873 | |
daniele | 10:dd7111d4279f | 1874 | /* call EV_ERR wakeup before deleting */ |
daniele | 10:dd7111d4279f | 1875 | pico_err = PICO_ERR_ECONNRESET; |
daniele | 10:dd7111d4279f | 1876 | if ((t->sock).wakeup) |
daniele | 10:dd7111d4279f | 1877 | (t->sock).wakeup(PICO_SOCK_EV_ERR, &(t->sock)); |
daniele | 10:dd7111d4279f | 1878 | |
daniele | 10:dd7111d4279f | 1879 | /* delete socket */ |
daniele | 10:dd7111d4279f | 1880 | pico_socket_del(&t->sock); |
daniele | 10:dd7111d4279f | 1881 | } else { /* not valid, ignore */ |
daniele | 10:dd7111d4279f | 1882 | tcp_dbg("TCP RST> IGNORE\n"); |
daniele | 10:dd7111d4279f | 1883 | return 0; |
daniele | 10:dd7111d4279f | 1884 | } |
daniele | 10:dd7111d4279f | 1885 | } else { /* all other states */ |
daniele | 10:dd7111d4279f | 1886 | /* all reset (RST) segments are validated by checking their SEQ-fields, |
daniele | 10:dd7111d4279f | 1887 | a reset is valid if its sequence number is in the window */ |
daniele | 10:dd7111d4279f | 1888 | if ((long_be(hdr->seq) >= t->rcv_ackd) && (long_be(hdr->seq) <= ((short_be(hdr->rwnd)<<(t->wnd_scale)) + t->rcv_ackd))) { |
daniele | 10:dd7111d4279f | 1889 | if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_SYN_RECV) { |
daniele | 10:dd7111d4279f | 1890 | /* go to closed */ |
daniele | 10:dd7111d4279f | 1891 | (t->sock).state &= 0x00FFU; |
daniele | 10:dd7111d4279f | 1892 | (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED; |
daniele | 10:dd7111d4279f | 1893 | (t->sock).state &= 0xFF00U; |
daniele | 10:dd7111d4279f | 1894 | (t->sock).state |= PICO_SOCKET_STATE_CLOSED; |
daniele | 10:dd7111d4279f | 1895 | /* call EV_ERR wakeup */ |
daniele | 10:dd7111d4279f | 1896 | pico_err = PICO_ERR_ECONNRESET; |
daniele | 10:dd7111d4279f | 1897 | if ((t->sock).wakeup) |
daniele | 10:dd7111d4279f | 1898 | (t->sock).wakeup(PICO_SOCK_EV_ERR, &(t->sock)); |
daniele | 10:dd7111d4279f | 1899 | tcp_dbg("TCP RST> SOCKET BACK TO LISTEN\n"); |
daniele | 10:dd7111d4279f | 1900 | pico_socket_del(s); |
daniele | 10:dd7111d4279f | 1901 | } else { |
daniele | 10:dd7111d4279f | 1902 | /* go to closed */ |
daniele | 10:dd7111d4279f | 1903 | (t->sock).state &= 0x00FFU; |
daniele | 10:dd7111d4279f | 1904 | (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED; |
daniele | 10:dd7111d4279f | 1905 | (t->sock).state &= 0xFF00U; |
daniele | 10:dd7111d4279f | 1906 | (t->sock).state |= PICO_SOCKET_STATE_CLOSED; |
daniele | 10:dd7111d4279f | 1907 | |
daniele | 10:dd7111d4279f | 1908 | /* call EV_FIN wakeup before deleting */ |
daniele | 10:dd7111d4279f | 1909 | if ((t->sock).wakeup) |
daniele | 10:dd7111d4279f | 1910 | (t->sock).wakeup(PICO_SOCK_EV_FIN, &(t->sock)); |
daniele | 10:dd7111d4279f | 1911 | /* call EV_ERR wakeup before deleting */ |
daniele | 10:dd7111d4279f | 1912 | pico_err = PICO_ERR_ECONNRESET; |
daniele | 10:dd7111d4279f | 1913 | if ((t->sock).wakeup) |
daniele | 10:dd7111d4279f | 1914 | (t->sock).wakeup(PICO_SOCK_EV_ERR, &(t->sock)); |
daniele | 10:dd7111d4279f | 1915 | |
daniele | 10:dd7111d4279f | 1916 | /* delete socket */ |
daniele | 10:dd7111d4279f | 1917 | pico_socket_del(&t->sock); |
daniele | 10:dd7111d4279f | 1918 | } |
daniele | 10:dd7111d4279f | 1919 | } else { /* not valid, ignore */ |
daniele | 10:dd7111d4279f | 1920 | tcp_dbg("TCP RST> IGNORE\n"); |
daniele | 10:dd7111d4279f | 1921 | return 0; |
daniele | 10:dd7111d4279f | 1922 | } |
daniele | 10:dd7111d4279f | 1923 | } |
daniele | 10:dd7111d4279f | 1924 | |
daniele | 10:dd7111d4279f | 1925 | return 0; |
daniele | 10:dd7111d4279f | 1926 | } |
daniele | 10:dd7111d4279f | 1927 | |
daniele | 10:dd7111d4279f | 1928 | struct tcp_action_entry { |
daniele | 10:dd7111d4279f | 1929 | uint16_t tcpstate; |
daniele | 10:dd7111d4279f | 1930 | int (*syn)(struct pico_socket *s, struct pico_frame *f); |
daniele | 10:dd7111d4279f | 1931 | int (*synack)(struct pico_socket *s, struct pico_frame *f); |
daniele | 10:dd7111d4279f | 1932 | int (*ack)(struct pico_socket *s, struct pico_frame *f); |
daniele | 10:dd7111d4279f | 1933 | int (*data)(struct pico_socket *s, struct pico_frame *f); |
daniele | 10:dd7111d4279f | 1934 | int (*fin)(struct pico_socket *s, struct pico_frame *f); |
daniele | 10:dd7111d4279f | 1935 | int (*finack)(struct pico_socket *s, struct pico_frame *f); |
daniele | 10:dd7111d4279f | 1936 | int (*rst)(struct pico_socket *s, struct pico_frame *f); |
daniele | 10:dd7111d4279f | 1937 | }; |
daniele | 10:dd7111d4279f | 1938 | |
daniele | 10:dd7111d4279f | 1939 | static struct tcp_action_entry tcp_fsm[] = { |
daniele | 10:dd7111d4279f | 1940 | /* State syn synack ack data fin finack rst*/ |
daniele | 10:dd7111d4279f | 1941 | { PICO_SOCKET_STATE_TCP_UNDEF, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, |
daniele | 10:dd7111d4279f | 1942 | { PICO_SOCKET_STATE_TCP_CLOSED, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, |
daniele | 10:dd7111d4279f | 1943 | { PICO_SOCKET_STATE_TCP_LISTEN, &tcp_syn, &tcp_nosync_rst, &tcp_nosync_rst, &tcp_nosync_rst, &tcp_nosync_rst, &tcp_nosync_rst, NULL }, |
daniele | 10:dd7111d4279f | 1944 | { PICO_SOCKET_STATE_TCP_SYN_SENT, &tcp_nosync_rst, &tcp_synack, &tcp_nosync_rst, &tcp_nosync_rst, &tcp_nosync_rst, &tcp_nosync_rst, &tcp_rst }, |
daniele | 10:dd7111d4279f | 1945 | { PICO_SOCKET_STATE_TCP_SYN_RECV, &tcp_nosync_rst, &tcp_nosync_rst, &tcp_first_ack, &tcp_nosync_rst, &tcp_nosync_rst, &tcp_nosync_rst, &tcp_rst }, |
daniele | 10:dd7111d4279f | 1946 | { PICO_SOCKET_STATE_TCP_ESTABLISHED, &tcp_send_rst, &tcp_send_rst, &tcp_ack, &tcp_data_in, &tcp_closewait, &tcp_closewait, &tcp_rst }, |
daniele | 10:dd7111d4279f | 1947 | { PICO_SOCKET_STATE_TCP_CLOSE_WAIT, &tcp_send_rst, &tcp_send_rst, &tcp_ack, &tcp_send_rst, &tcp_send_rst, &tcp_send_rst, &tcp_rst }, |
daniele | 10:dd7111d4279f | 1948 | { PICO_SOCKET_STATE_TCP_LAST_ACK, &tcp_send_rst, &tcp_send_rst, &tcp_lastackwait, &tcp_send_rst, &tcp_send_rst, &tcp_send_rst, &tcp_rst }, |
daniele | 10:dd7111d4279f | 1949 | { PICO_SOCKET_STATE_TCP_FIN_WAIT1, &tcp_send_rst, &tcp_send_rst, &tcp_finwaitack, &tcp_data_in, &tcp_rcvfin, &tcp_finack, &tcp_rst }, |
daniele | 10:dd7111d4279f | 1950 | { PICO_SOCKET_STATE_TCP_FIN_WAIT2, &tcp_send_rst, &tcp_send_rst, &tcp_ack, &tcp_data_in, &tcp_finwaitfin, &tcp_finack, &tcp_rst }, |
daniele | 10:dd7111d4279f | 1951 | { PICO_SOCKET_STATE_TCP_CLOSING, &tcp_send_rst, &tcp_send_rst, &tcp_closewaitack, &tcp_send_rst, &tcp_send_rst, &tcp_send_rst, &tcp_rst }, |
daniele | 10:dd7111d4279f | 1952 | { PICO_SOCKET_STATE_TCP_TIME_WAIT, &tcp_send_rst, &tcp_send_rst, &tcp_send_rst, &tcp_send_rst, &tcp_send_rst, &tcp_send_rst, &tcp_rst } |
daniele | 10:dd7111d4279f | 1953 | }; |
daniele | 10:dd7111d4279f | 1954 | |
daniele | 10:dd7111d4279f | 1955 | /* |
daniele | 10:dd7111d4279f | 1956 | NOTE: in SYN-RECV receiving syn when cloned by default (see yellow pos-it), should send reset. |
daniele | 10:dd7111d4279f | 1957 | */ |
daniele | 10:dd7111d4279f | 1958 | |
daniele | 10:dd7111d4279f | 1959 | int pico_tcp_input(struct pico_socket *s, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 1960 | { |
daniele | 10:dd7111d4279f | 1961 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) (f->transport_hdr); |
daniele | 10:dd7111d4279f | 1962 | int ret = 0; |
daniele | 10:dd7111d4279f | 1963 | uint8_t flags = hdr->flags; |
daniele | 10:dd7111d4279f | 1964 | struct tcp_action_entry *action = &tcp_fsm[s->state >> 8]; |
daniele | 10:dd7111d4279f | 1965 | |
daniele | 10:dd7111d4279f | 1966 | f->payload = (f->transport_hdr + ((hdr->len & 0xf0) >> 2)); |
daniele | 10:dd7111d4279f | 1967 | f->payload_len = f->transport_len - ((hdr->len & 0xf0) >> 2); |
daniele | 10:dd7111d4279f | 1968 | |
daniele | 10:dd7111d4279f | 1969 | tcp_dbg("[%lu] TCP> [tcp input] socket: %p state: %d <-- local port:%d remote port: %d seq: %08x ack: %08x flags: %02x = t_len: %d, hdr: %u payload: %d\n", pico_tick, |
daniele | 10:dd7111d4279f | 1970 | 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 ); |
daniele | 10:dd7111d4279f | 1971 | |
daniele | 10:dd7111d4279f | 1972 | /* This copy of the frame has the current socket as owner */ |
daniele | 10:dd7111d4279f | 1973 | f->sock = s; |
daniele | 10:dd7111d4279f | 1974 | |
daniele | 10:dd7111d4279f | 1975 | /* Those are not supported at this time. */ |
daniele | 10:dd7111d4279f | 1976 | flags &= ~(PICO_TCP_CWR | PICO_TCP_URG | PICO_TCP_ECN); |
daniele | 10:dd7111d4279f | 1977 | if (flags == PICO_TCP_SYN) { |
daniele | 10:dd7111d4279f | 1978 | if (action->syn) |
daniele | 10:dd7111d4279f | 1979 | action->syn(s,f); |
daniele | 10:dd7111d4279f | 1980 | } else if (flags == (PICO_TCP_SYN | PICO_TCP_ACK)) { |
daniele | 10:dd7111d4279f | 1981 | if (action->synack) |
daniele | 10:dd7111d4279f | 1982 | action->synack(s,f); |
daniele | 10:dd7111d4279f | 1983 | } else { |
daniele | 10:dd7111d4279f | 1984 | if ((flags == PICO_TCP_ACK) || (flags == (PICO_TCP_ACK | PICO_TCP_PSH))) { |
daniele | 10:dd7111d4279f | 1985 | if (action->ack) { |
daniele | 10:dd7111d4279f | 1986 | action->ack(s,f); |
daniele | 10:dd7111d4279f | 1987 | } |
daniele | 10:dd7111d4279f | 1988 | } |
daniele | 10:dd7111d4279f | 1989 | if (f->payload_len > 0) { |
daniele | 10:dd7111d4279f | 1990 | ret = f->payload_len; |
daniele | 10:dd7111d4279f | 1991 | if (action->data) |
daniele | 10:dd7111d4279f | 1992 | action->data(s,f); |
daniele | 10:dd7111d4279f | 1993 | } |
daniele | 10:dd7111d4279f | 1994 | if (flags == PICO_TCP_FIN) { |
daniele | 10:dd7111d4279f | 1995 | if (action->fin) |
daniele | 10:dd7111d4279f | 1996 | action->fin(s,f); |
daniele | 10:dd7111d4279f | 1997 | } |
daniele | 10:dd7111d4279f | 1998 | if ((flags == (PICO_TCP_FIN | PICO_TCP_ACK)) || (flags == (PICO_TCP_FIN | PICO_TCP_ACK | PICO_TCP_PSH))) { |
daniele | 10:dd7111d4279f | 1999 | if (action->finack) |
daniele | 10:dd7111d4279f | 2000 | action->finack(s,f); |
daniele | 10:dd7111d4279f | 2001 | } |
daniele | 10:dd7111d4279f | 2002 | if (flags & PICO_TCP_RST) { |
daniele | 10:dd7111d4279f | 2003 | if (action->rst) |
daniele | 10:dd7111d4279f | 2004 | action->rst(s,f); |
daniele | 10:dd7111d4279f | 2005 | } |
daniele | 10:dd7111d4279f | 2006 | } |
daniele | 10:dd7111d4279f | 2007 | |
daniele | 10:dd7111d4279f | 2008 | //discard: |
daniele | 10:dd7111d4279f | 2009 | pico_frame_discard(f); |
daniele | 10:dd7111d4279f | 2010 | return ret; |
daniele | 10:dd7111d4279f | 2011 | } |
daniele | 10:dd7111d4279f | 2012 | |
daniele | 10:dd7111d4279f | 2013 | static void tcp_send_keepalive(unsigned long when, void *_t) |
daniele | 10:dd7111d4279f | 2014 | { |
daniele | 10:dd7111d4279f | 2015 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)_t; |
daniele | 10:dd7111d4279f | 2016 | tcp_dbg("\n\nSending keepalive (%d), [State = %d]...\n", t->backoff,t->sock.state ); |
daniele | 10:dd7111d4279f | 2017 | if( t->sock.net && ((t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_ESTABLISHED) ) |
daniele | 10:dd7111d4279f | 2018 | { |
daniele | 10:dd7111d4279f | 2019 | tcp_send_ack(t); |
daniele | 10:dd7111d4279f | 2020 | |
daniele | 10:dd7111d4279f | 2021 | if (t->keepalive_timer_running > 0) { |
daniele | 10:dd7111d4279f | 2022 | t->keepalive_timer_running--; |
daniele | 10:dd7111d4279f | 2023 | } |
daniele | 10:dd7111d4279f | 2024 | |
daniele | 10:dd7111d4279f | 2025 | if (t->keepalive_timer_running == 0) { |
daniele | 10:dd7111d4279f | 2026 | t->keepalive_timer_running++; |
daniele | 10:dd7111d4279f | 2027 | tcp_dbg("[Self] Adding timer(retransmit keepalive)\n"); |
daniele | 10:dd7111d4279f | 2028 | pico_timer_add(t->rto << (++t->backoff), tcp_send_keepalive, t); |
daniele | 10:dd7111d4279f | 2029 | } |
daniele | 10:dd7111d4279f | 2030 | } |
daniele | 10:dd7111d4279f | 2031 | } |
daniele | 10:dd7111d4279f | 2032 | |
daniele | 10:dd7111d4279f | 2033 | int pico_tcp_output(struct pico_socket *s, int loop_score) |
daniele | 10:dd7111d4279f | 2034 | { |
daniele | 10:dd7111d4279f | 2035 | struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; |
daniele | 10:dd7111d4279f | 2036 | struct pico_frame *f, *una; |
daniele | 10:dd7111d4279f | 2037 | struct pico_tcp_hdr *hdr; |
daniele | 10:dd7111d4279f | 2038 | int sent = 0; |
daniele | 10:dd7111d4279f | 2039 | |
daniele | 10:dd7111d4279f | 2040 | una = first_segment(&t->tcpq_out); |
daniele | 10:dd7111d4279f | 2041 | |
daniele | 10:dd7111d4279f | 2042 | f = peek_segment(&t->tcpq_out, t->snd_nxt); |
daniele | 10:dd7111d4279f | 2043 | while((f) && (t->cwnd >= t->in_flight)) { |
daniele | 10:dd7111d4279f | 2044 | hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
daniele | 10:dd7111d4279f | 2045 | f->timestamp = pico_tick; |
daniele | 10:dd7111d4279f | 2046 | tcp_add_options(t, f, hdr->flags, tcp_options_size(t, hdr->flags)); |
daniele | 10:dd7111d4279f | 2047 | if (seq_compare(SEQN(f) + f->payload_len, SEQN(una) + (t->recv_wnd << t->recv_wnd_scale)) > 0) { |
daniele | 10:dd7111d4279f | 2048 | t->cwnd = t->in_flight; |
daniele | 10:dd7111d4279f | 2049 | if (t->cwnd < 1) |
daniele | 10:dd7111d4279f | 2050 | t->cwnd = 1; |
daniele | 10:dd7111d4279f | 2051 | if (t->x_mode != PICO_TCP_WINDOW_FULL) { |
daniele | 10:dd7111d4279f | 2052 | tcp_dbg("TCP> RIGHT SIZING (rwnd: %d, frame len: %d\n",t->recv_wnd << t->recv_wnd_scale, f->payload_len); |
daniele | 10:dd7111d4279f | 2053 | tcp_dbg("In window full...\n"); |
daniele | 10:dd7111d4279f | 2054 | t->snd_nxt = SEQN(una); |
daniele | 10:dd7111d4279f | 2055 | |
daniele | 10:dd7111d4279f | 2056 | /* Alternative to the line above: (better performance, but seems to lock anyway with larger buffers) |
daniele | 10:dd7111d4279f | 2057 | if (seq_compare(t->snd_nxt, SEQN(una)) > 0) |
daniele | 10:dd7111d4279f | 2058 | t->snd_nxt -= f->payload_len; |
daniele | 10:dd7111d4279f | 2059 | */ |
daniele | 10:dd7111d4279f | 2060 | |
daniele | 10:dd7111d4279f | 2061 | t->x_mode = PICO_TCP_WINDOW_FULL; |
daniele | 10:dd7111d4279f | 2062 | if (t->keepalive_timer_running == 0) { |
daniele | 10:dd7111d4279f | 2063 | tcp_dbg("[Window full] Adding timer(send keepalive)\n"); |
daniele | 10:dd7111d4279f | 2064 | tcp_send_keepalive(0, t); |
daniele | 10:dd7111d4279f | 2065 | } |
daniele | 10:dd7111d4279f | 2066 | } |
daniele | 10:dd7111d4279f | 2067 | break; |
daniele | 10:dd7111d4279f | 2068 | } |
daniele | 10:dd7111d4279f | 2069 | 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); |
daniele | 10:dd7111d4279f | 2070 | tcp_send(t, f); |
daniele | 10:dd7111d4279f | 2071 | sent++; |
daniele | 10:dd7111d4279f | 2072 | loop_score--; |
daniele | 10:dd7111d4279f | 2073 | t->snd_last_out = SEQN(f); |
daniele | 10:dd7111d4279f | 2074 | if (loop_score < 1) |
daniele | 10:dd7111d4279f | 2075 | break; |
daniele | 10:dd7111d4279f | 2076 | if (f->payload_len > 0) { |
daniele | 10:dd7111d4279f | 2077 | f = next_segment(&t->tcpq_out, f); |
daniele | 10:dd7111d4279f | 2078 | } else { |
daniele | 10:dd7111d4279f | 2079 | f = NULL; |
daniele | 10:dd7111d4279f | 2080 | } |
daniele | 10:dd7111d4279f | 2081 | } |
daniele | 10:dd7111d4279f | 2082 | if (sent > 0) { |
daniele | 10:dd7111d4279f | 2083 | if (t->rto < PICO_TCP_RTO_MIN) |
daniele | 10:dd7111d4279f | 2084 | t->rto = PICO_TCP_RTO_MIN; |
daniele | 10:dd7111d4279f | 2085 | add_retransmission_timer(t, pico_tick + t->rto); |
daniele | 10:dd7111d4279f | 2086 | } else { |
daniele | 10:dd7111d4279f | 2087 | // no packets in queue ?? |
daniele | 10:dd7111d4279f | 2088 | } |
daniele | 10:dd7111d4279f | 2089 | |
tass | 31:d3b2dfcc358f | 2090 | // check to see if we are in the case where FIN was received |
tass | 31:d3b2dfcc358f | 2091 | // but we still had data in the queue (tcpq_out) |
tass | 31:d3b2dfcc358f | 2092 | checkIfQueueEmptyAndFin(t); |
daniele | 10:dd7111d4279f | 2093 | return loop_score; |
daniele | 10:dd7111d4279f | 2094 | } |
daniele | 10:dd7111d4279f | 2095 | |
daniele | 10:dd7111d4279f | 2096 | /* function to make new segment from hold queue with specific size (mss) */ |
daniele | 10:dd7111d4279f | 2097 | static struct pico_frame * pico_hold_segment_make(struct pico_socket_tcp *t) |
daniele | 10:dd7111d4279f | 2098 | { |
daniele | 10:dd7111d4279f | 2099 | struct pico_frame *f_temp,*f_new; |
daniele | 10:dd7111d4279f | 2100 | struct pico_socket *s = (struct pico_socket *) &t->sock; |
daniele | 10:dd7111d4279f | 2101 | struct pico_tcp_hdr *hdr; |
daniele | 10:dd7111d4279f | 2102 | int total_len = 0, total_payload_len = 0; |
daniele | 10:dd7111d4279f | 2103 | int off = 0, test = 0; |
daniele | 10:dd7111d4279f | 2104 | |
daniele | 10:dd7111d4279f | 2105 | off = pico_tcp_overhead(s); |
daniele | 10:dd7111d4279f | 2106 | |
daniele | 10:dd7111d4279f | 2107 | /* init with first frame in hold queue */ |
daniele | 10:dd7111d4279f | 2108 | f_temp = first_segment(&t->tcpq_hold); |
daniele | 10:dd7111d4279f | 2109 | total_len = f_temp->payload_len; |
daniele | 10:dd7111d4279f | 2110 | f_temp = next_segment(&t->tcpq_hold, f_temp); |
daniele | 10:dd7111d4279f | 2111 | |
daniele | 10:dd7111d4279f | 2112 | /* check till total_len <= MSS */ |
daniele | 10:dd7111d4279f | 2113 | while ((f_temp != NULL) && ((total_len+f_temp->payload_len) <= PICO_TCP_DEFAULT_MSS)) { |
daniele | 10:dd7111d4279f | 2114 | total_len += f_temp->payload_len; |
daniele | 10:dd7111d4279f | 2115 | f_temp = next_segment(&t->tcpq_hold, f_temp); |
daniele | 10:dd7111d4279f | 2116 | if (f_temp == NULL) |
daniele | 10:dd7111d4279f | 2117 | break; |
daniele | 10:dd7111d4279f | 2118 | } |
daniele | 10:dd7111d4279f | 2119 | /* alloc new frame with payload size = off + total_len */ |
daniele | 10:dd7111d4279f | 2120 | f_new = pico_socket_frame_alloc(s, off + total_len); |
daniele | 10:dd7111d4279f | 2121 | if (!f_new) { |
daniele | 10:dd7111d4279f | 2122 | pico_err = PICO_ERR_ENOMEM; |
daniele | 10:dd7111d4279f | 2123 | return f_new; |
daniele | 10:dd7111d4279f | 2124 | } |
daniele | 10:dd7111d4279f | 2125 | |
daniele | 10:dd7111d4279f | 2126 | hdr = (struct pico_tcp_hdr *) f_new->transport_hdr; |
daniele | 10:dd7111d4279f | 2127 | /* init new frame */ |
daniele | 10:dd7111d4279f | 2128 | f_new->payload += off; |
daniele | 10:dd7111d4279f | 2129 | f_new->payload_len -= off; |
daniele | 10:dd7111d4279f | 2130 | f_new->sock = s; |
daniele | 10:dd7111d4279f | 2131 | |
daniele | 10:dd7111d4279f | 2132 | f_temp = first_segment(&t->tcpq_hold); |
daniele | 10:dd7111d4279f | 2133 | hdr->seq = ((struct pico_tcp_hdr *)(f_temp->transport_hdr))->seq; /* get sequence number of first frame */ |
daniele | 10:dd7111d4279f | 2134 | hdr->trans.sport = t->sock.local_port; |
daniele | 10:dd7111d4279f | 2135 | hdr->trans.dport = t->sock.remote_port; |
daniele | 10:dd7111d4279f | 2136 | |
daniele | 10:dd7111d4279f | 2137 | /* check till total_payload_len <= MSS */ |
daniele | 10:dd7111d4279f | 2138 | while ((f_temp != NULL) && ((total_payload_len + f_temp->payload_len) <= PICO_TCP_DEFAULT_MSS)) { |
daniele | 10:dd7111d4279f | 2139 | /* cpy data and discard frame */ |
daniele | 10:dd7111d4279f | 2140 | test++; |
daniele | 10:dd7111d4279f | 2141 | memcpy(f_new->payload + total_payload_len, f_temp->payload, f_temp->payload_len); |
daniele | 10:dd7111d4279f | 2142 | total_payload_len += f_temp->payload_len; |
daniele | 10:dd7111d4279f | 2143 | pico_discard_segment(&t->tcpq_hold, f_temp); |
daniele | 10:dd7111d4279f | 2144 | f_temp = first_segment(&t->tcpq_hold); |
daniele | 10:dd7111d4279f | 2145 | } |
daniele | 10:dd7111d4279f | 2146 | |
daniele | 10:dd7111d4279f | 2147 | hdr->len = (f_new->payload - f_new->transport_hdr) << 2 | t->jumbo; |
daniele | 10:dd7111d4279f | 2148 | |
daniele | 10:dd7111d4279f | 2149 | tcp_dbg("NAGLE make - joined %d segments, len %d bytes\n",test,total_payload_len); |
daniele | 10:dd7111d4279f | 2150 | |
daniele | 10:dd7111d4279f | 2151 | return f_new; |
daniele | 10:dd7111d4279f | 2152 | } |
daniele | 10:dd7111d4279f | 2153 | |
daniele | 10:dd7111d4279f | 2154 | /* original behavior kept when Nagle disabled; |
daniele | 10:dd7111d4279f | 2155 | Nagle algorithm added here, keeping hold frame queue instead of eg linked list of data */ |
daniele | 10:dd7111d4279f | 2156 | int pico_tcp_push(struct pico_protocol *self, struct pico_frame *f) |
daniele | 10:dd7111d4279f | 2157 | { |
daniele | 10:dd7111d4279f | 2158 | struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *)f->transport_hdr; |
daniele | 10:dd7111d4279f | 2159 | struct pico_socket_tcp *t = (struct pico_socket_tcp *) f->sock; |
daniele | 10:dd7111d4279f | 2160 | struct pico_frame *f_new; |
daniele | 10:dd7111d4279f | 2161 | int total_len = 0; |
daniele | 10:dd7111d4279f | 2162 | |
daniele | 10:dd7111d4279f | 2163 | hdr->trans.sport = t->sock.local_port; |
daniele | 10:dd7111d4279f | 2164 | hdr->trans.dport = t->sock.remote_port; |
daniele | 10:dd7111d4279f | 2165 | hdr->seq = long_be(t->snd_last + 1); |
daniele | 10:dd7111d4279f | 2166 | hdr->len = (f->payload - f->transport_hdr) << 2 | t->jumbo; |
daniele | 10:dd7111d4279f | 2167 | |
daniele | 10:dd7111d4279f | 2168 | if (f->payload_len > (t->tcpq_out.max_size - t->tcpq_out.size)) |
daniele | 10:dd7111d4279f | 2169 | t->sock.ev_pending &= (~PICO_SOCK_EV_WR); |
daniele | 10:dd7111d4279f | 2170 | |
daniele | 10:dd7111d4279f | 2171 | /***************************************************************************/ |
daniele | 10:dd7111d4279f | 2172 | |
daniele | 10:dd7111d4279f | 2173 | if (!IS_NAGLE_ENABLED((&(t->sock)))) { |
daniele | 10:dd7111d4279f | 2174 | /* TCP_NODELAY enabled, original behavior */ |
daniele | 10:dd7111d4279f | 2175 | if (pico_enqueue_segment(&t->tcpq_out,f) > 0) { |
daniele | 10:dd7111d4279f | 2176 | tcp_dbg("TCP_PUSH - NO NAGLE - Pushing segment %08x, len %08x to socket %p\n", t->snd_last + 1, f->payload_len, t); |
daniele | 10:dd7111d4279f | 2177 | t->snd_last += f->payload_len; |
daniele | 10:dd7111d4279f | 2178 | return f->payload_len; |
daniele | 10:dd7111d4279f | 2179 | } else { |
daniele | 10:dd7111d4279f | 2180 | tcp_dbg("Enqueue failed.\n"); |
daniele | 10:dd7111d4279f | 2181 | return 0; |
daniele | 10:dd7111d4279f | 2182 | } |
daniele | 10:dd7111d4279f | 2183 | } |
daniele | 10:dd7111d4279f | 2184 | /***************************************************************************/ |
daniele | 10:dd7111d4279f | 2185 | else { |
daniele | 10:dd7111d4279f | 2186 | /* Nagle's algorithm enabled, check if ready to send, or put frame in hold queue */ |
daniele | 10:dd7111d4279f | 2187 | if (IS_TCP_IDLE(t) && IS_TCP_HOLDQ_EMPTY(t)) { /* opt 1. send frame */ |
daniele | 10:dd7111d4279f | 2188 | if (pico_enqueue_segment(&t->tcpq_out,f) > 0) { |
daniele | 10:dd7111d4279f | 2189 | tcp_dbg("TCP_PUSH - NAGLE - Pushing segment %08x, len %08x to socket %p\n", t->snd_last + 1, f->payload_len, t); |
daniele | 10:dd7111d4279f | 2190 | t->snd_last += f->payload_len; |
daniele | 10:dd7111d4279f | 2191 | return f->payload_len; |
daniele | 10:dd7111d4279f | 2192 | } else { |
daniele | 10:dd7111d4279f | 2193 | tcp_dbg("Enqueue failed.\n"); |
daniele | 10:dd7111d4279f | 2194 | return 0; |
daniele | 10:dd7111d4279f | 2195 | } |
daniele | 10:dd7111d4279f | 2196 | } else { /* opt 2. hold data back */ |
daniele | 10:dd7111d4279f | 2197 | total_len = f->payload_len + t->tcpq_hold.size; |
daniele | 10:dd7111d4279f | 2198 | if ((total_len >= PICO_TCP_DEFAULT_MSS) && ((t->tcpq_out.max_size - t->tcpq_out.size) >= PICO_TCP_DEFAULT_MSS)) {/* TODO check mss socket */ |
daniele | 10:dd7111d4279f | 2199 | /* IF enough data in hold (>mss) AND space in out queue (>mss) */ |
daniele | 10:dd7111d4279f | 2200 | /* add current frame in hold and make new segment */ |
daniele | 10:dd7111d4279f | 2201 | if (pico_enqueue_segment(&t->tcpq_hold,f) > 0 ) { |
daniele | 10:dd7111d4279f | 2202 | tcp_dbg("TCP_PUSH - NAGLE - Pushed into hold, make new (enqueued frames out %d)\n",t->tcpq_out.frames); |
daniele | 10:dd7111d4279f | 2203 | t->snd_last += f->payload_len; /* XXX WATCH OUT */ |
daniele | 10:dd7111d4279f | 2204 | f_new = pico_hold_segment_make(t); |
daniele | 10:dd7111d4279f | 2205 | } else { |
daniele | 10:dd7111d4279f | 2206 | tcp_dbg("TCP_PUSH - NAGLE - enqueue hold failed 1\n"); |
daniele | 10:dd7111d4279f | 2207 | return 0; |
daniele | 10:dd7111d4279f | 2208 | } |
daniele | 10:dd7111d4279f | 2209 | /* and put new frame in out queue */ |
daniele | 10:dd7111d4279f | 2210 | if ((f_new != NULL) && (pico_enqueue_segment(&t->tcpq_out,f_new) > 0)) { |
daniele | 10:dd7111d4279f | 2211 | return f_new->payload_len; |
daniele | 10:dd7111d4279f | 2212 | } else { |
daniele | 10:dd7111d4279f | 2213 | tcp_dbg("TCP_PUSH - NAGLE - enqueue out failed, f_new = %p\n",f_new); |
daniele | 10:dd7111d4279f | 2214 | return -1; /* XXX something seriously wrong */ |
daniele | 10:dd7111d4279f | 2215 | } |
daniele | 10:dd7111d4279f | 2216 | } else { |
daniele | 10:dd7111d4279f | 2217 | /* ELSE put frame in hold queue */ |
daniele | 10:dd7111d4279f | 2218 | if (pico_enqueue_segment(&t->tcpq_hold,f) > 0) { |
daniele | 10:dd7111d4279f | 2219 | tcp_dbg("TCP_PUSH - NAGLE - Pushed into hold (enqueued frames out %d)\n",t->tcpq_out.frames); |
daniele | 10:dd7111d4279f | 2220 | t->snd_last += f->payload_len; /* XXX WATCH OUT */ |
daniele | 10:dd7111d4279f | 2221 | return f->payload_len; |
daniele | 10:dd7111d4279f | 2222 | } else { |
daniele | 10:dd7111d4279f | 2223 | tcp_dbg("TCP_PUSH - NAGLE - enqueue hold failed 2\n"); |
daniele | 10:dd7111d4279f | 2224 | return 0; |
daniele | 10:dd7111d4279f | 2225 | } |
daniele | 10:dd7111d4279f | 2226 | } |
daniele | 10:dd7111d4279f | 2227 | } |
daniele | 10:dd7111d4279f | 2228 | } |
daniele | 10:dd7111d4279f | 2229 | /***************************************************************************/ |
daniele | 10:dd7111d4279f | 2230 | } |
daniele | 10:dd7111d4279f | 2231 | #endif //PICO_SUPPORT_TCP |