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