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