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