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