1 /*
2 * Copyright (c) 2014 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8
9 #include "minip-internal.h"
10
11 #include <lk/trace.h>
12 #include <assert.h>
13 #include <lk/compiler.h>
14 #include <stdlib.h>
15 #include <lk/err.h>
16 #include <string.h>
17 #include <sys/types.h>
18 #include <lk/console_cmd.h>
19 #include <lib/cbuf.h>
20 #include <kernel/mutex.h>
21 #include <kernel/semaphore.h>
22 #include <arch/ops.h>
23 #include <platform.h>
24 #include <arch/atomic.h>
25
26 #define LOCAL_TRACE 0
27
28 typedef uint32_t ipv4_addr;
29
30 typedef struct tcp_header {
31 uint16_t source_port;
32 uint16_t dest_port;
33 uint32_t seq_num;
34 uint32_t ack_num;
35 uint16_t length_flags;
36 uint16_t win_size;
37 uint16_t checksum;
38 uint16_t urg_pointer;
39 } __PACKED tcp_header_t;
40
41 typedef struct tcp_pseudo_header {
42 ipv4_addr source_addr;
43 ipv4_addr dest_addr;
44 uint8_t zero;
45 uint8_t protocol;
46 uint16_t tcp_length;
47 } __PACKED tcp_pseudo_header_t;
48
49 typedef struct tcp_mss_option {
50 uint8_t kind; /* 0x2 */
51 uint8_t len; /* 0x4 */
52 uint16_t mss;
53 } __PACKED tcp_mss_option_t;
54
55 typedef enum tcp_state {
56 STATE_CLOSED,
57 STATE_LISTEN,
58 STATE_SYN_SENT,
59 STATE_SYN_RCVD,
60 STATE_ESTABLISHED,
61 STATE_CLOSE_WAIT,
62 STATE_LAST_ACK,
63 STATE_CLOSING,
64 STATE_FIN_WAIT_1,
65 STATE_FIN_WAIT_2,
66 STATE_TIME_WAIT
67 } tcp_state_t;
68
69 typedef enum tcp_flags {
70 PKT_FIN = 1,
71 PKT_SYN = 2,
72 PKT_RST = 4,
73 PKT_PSH = 8,
74 PKT_ACK = 16,
75 PKT_URG = 32
76 } tcp_flags_t;
77
78 typedef struct tcp_socket {
79 struct list_node node;
80
81 mutex_t lock;
82 volatile int ref;
83
84 tcp_state_t state;
85 ipv4_addr local_ip;
86 ipv4_addr remote_ip;
87 uint16_t local_port;
88 uint16_t remote_port;
89
90 uint32_t mss;
91
92 /* rx */
93 uint32_t rx_win_size;
94 uint32_t rx_win_low;
95 uint32_t rx_win_high;
96 uint8_t *rx_buffer_raw;
97 cbuf_t rx_buffer;
98 event_t rx_event;
99 int rx_full_mss_count; // number of packets we have received in a row with a full mss
100 net_timer_t ack_delay_timer;
101
102 /* tx */
103 uint32_t tx_win_low; // low side of the acked window
104 uint32_t tx_win_high; // tx_win_low + their advertised window size
105 uint32_t tx_highest_seq; // highest sequence we have txed them
106 uint8_t *tx_buffer; // our outgoing buffer
107 uint32_t tx_buffer_size; // size of tx_buffer
108 uint32_t tx_buffer_offset; // offset into the buffer to append new data to
109 event_t tx_event;
110 net_timer_t retransmit_timer;
111
112 /* listen accept */
113 semaphore_t accept_sem;
114 struct tcp_socket *accepted;
115
116 net_timer_t time_wait_timer;
117 } tcp_socket_t;
118
119 #define DEFAULT_MSS (1460)
120 #define DEFAULT_RX_WINDOW_SIZE (8192)
121 #define DEFAULT_TX_BUFFER_SIZE (8192)
122
123 #define RETRANSMIT_TIMEOUT (50)
124 #define DELAYED_ACK_TIMEOUT (50)
125 #define TIME_WAIT_TIMEOUT (60000) // 1 minute
126
127 #define FORCE_TCP_CHECKSUM (false)
128
129 #define SEQUENCE_GTE(a, b) ((int32_t)((a) - (b)) >= 0)
130 #define SEQUENCE_LTE(a, b) ((int32_t)((a) - (b)) <= 0)
131 #define SEQUENCE_GT(a, b) ((int32_t)((a) - (b)) > 0)
132 #define SEQUENCE_LT(a, b) ((int32_t)((a) - (b)) < 0)
133
134 static mutex_t tcp_socket_list_lock = MUTEX_INITIAL_VALUE(tcp_socket_list_lock);
135 static struct list_node tcp_socket_list = LIST_INITIAL_VALUE(tcp_socket_list);
136
137 static bool tcp_debug = false;
138
139 /* local routines */
140 static tcp_socket_t *lookup_socket(ipv4_addr remote_ip, ipv4_addr local_ip, uint16_t remote_port, uint16_t local_port);
141 static void add_socket_to_list(tcp_socket_t *s);
142 static void remove_socket_from_list(tcp_socket_t *s);
143 static tcp_socket_t *create_tcp_socket(bool alloc_buffers);
144 static status_t tcp_send(ipv4_addr dest_ip, uint16_t dest_port, ipv4_addr src_ip, uint16_t src_port, const void *buf,
145 size_t len, tcp_flags_t flags, const void *options, size_t options_length, uint32_t ack, uint32_t sequence, uint16_t window_size);
146 static status_t tcp_socket_send(tcp_socket_t *s, const void *data, size_t len, tcp_flags_t flags, const void *options, size_t options_length, uint32_t sequence);
147 static void handle_data(tcp_socket_t *s, const void *data, size_t len, uint32_t sequence);
148 static void send_ack(tcp_socket_t *s);
149 static void handle_ack(tcp_socket_t *s, uint32_t sequence, uint32_t win_size);
150 static void handle_retransmit_timeout(void *_s);
151 static void handle_time_wait_timeout(void *_s);
152 static void handle_delayed_ack_timeout(void *_s);
153 static void tcp_remote_close(tcp_socket_t *s);
154 static void tcp_wakeup_waiters(tcp_socket_t *s);
155 static void inc_socket_ref(tcp_socket_t *s);
156 static bool dec_socket_ref(tcp_socket_t *s);
157
cksum_pheader(const tcp_pseudo_header_t * pheader,const void * buf,size_t len)158 static uint16_t cksum_pheader(const tcp_pseudo_header_t *pheader, const void *buf, size_t len) {
159 uint16_t checksum = ones_sum16(0, pheader, sizeof(*pheader));
160 return ~ones_sum16(checksum, buf, len);
161 }
162
dump_tcp_header(const tcp_header_t * header)163 __NO_INLINE static void dump_tcp_header(const tcp_header_t *header) {
164 printf("TCP: src_port %u, dest_port %u, seq %u, ack %u, win %u, flags %c%c%c%c%c%c\n",
165 ntohs(header->source_port), ntohs(header->dest_port), ntohl(header->seq_num), ntohl(header->ack_num),
166 ntohs(header->win_size),
167 (ntohs(header->length_flags) & PKT_FIN) ? 'F' : ' ',
168 (ntohs(header->length_flags) & PKT_SYN) ? 'S' : ' ',
169 (ntohs(header->length_flags) & PKT_RST) ? 'R' : ' ',
170 (ntohs(header->length_flags) & PKT_PSH) ? 'P' : ' ',
171 (ntohs(header->length_flags) & PKT_ACK) ? 'A' : ' ',
172 (ntohs(header->length_flags) & PKT_URG) ? 'U' : ' ');
173 }
174
tcp_state_to_string(tcp_state_t state)175 static const char *tcp_state_to_string(tcp_state_t state) {
176 switch (state) {
177 default:
178 case STATE_CLOSED:
179 return "CLOSED";
180 case STATE_LISTEN:
181 return "LISTEN";
182 case STATE_SYN_SENT:
183 return "SYN_SENT";
184 case STATE_SYN_RCVD:
185 return "SYN_RCVD";
186 case STATE_ESTABLISHED:
187 return "ESTABLISHED";
188 case STATE_CLOSE_WAIT:
189 return "CLOSE_WAIT";
190 case STATE_LAST_ACK:
191 return "LAST_ACK";
192 case STATE_CLOSING:
193 return "CLOSING";
194 case STATE_FIN_WAIT_1:
195 return "FIN_WAIT_1";
196 case STATE_FIN_WAIT_2:
197 return "FIN_WAIT_2";
198 case STATE_TIME_WAIT:
199 return "TIME_WAIT";
200 }
201 }
202
dump_socket(tcp_socket_t * s)203 static void dump_socket(tcp_socket_t *s) {
204 printf("socket %p: state %d (%s), local 0x%x:%hu, remote 0x%x:%hu, ref %d\n",
205 s, s->state, tcp_state_to_string(s->state),
206 s->local_ip, s->local_port, s->remote_ip, s->remote_port, s->ref);
207 if (s->state == STATE_ESTABLISHED || s->state == STATE_CLOSE_WAIT) {
208 printf("\trx: wsize %u wlo %u whi %u (%u)\n",
209 s->rx_win_size, s->rx_win_low, s->rx_win_high,
210 s->rx_win_high - s->rx_win_low);
211 printf("\ttx: wlo %u whi %u (%u) highest_seq %u (%u) bufsize %u bufoff %u\n",
212 s->tx_win_low, s->tx_win_high, s->tx_win_high - s->tx_win_low,
213 s->tx_highest_seq, s->tx_highest_seq - s->tx_win_low,
214 s->tx_buffer_size, s->tx_buffer_offset);
215 }
216 }
217
lookup_socket(ipv4_addr remote_ip,ipv4_addr local_ip,uint16_t remote_port,uint16_t local_port)218 static tcp_socket_t *lookup_socket(ipv4_addr remote_ip, ipv4_addr local_ip, uint16_t remote_port, uint16_t local_port) {
219 LTRACEF("remote ip 0x%x local ip 0x%x remote port %u local port %u\n", remote_ip, local_ip, remote_port, local_port);
220
221 mutex_acquire(&tcp_socket_list_lock);
222
223 /* XXX replace with something faster, like a hash table */
224 tcp_socket_t *s = NULL;
225 list_for_every_entry(&tcp_socket_list, s, tcp_socket_t, node) {
226 if (s->state == STATE_CLOSED || s->state == STATE_LISTEN) {
227 continue;
228 } else {
229 /* full check */
230 if (s->remote_ip == remote_ip &&
231 s->local_ip == local_ip &&
232 s->remote_port == remote_port &&
233 s->local_port == local_port) {
234 goto out;
235 }
236 }
237 }
238
239 /* walk the list again, looking only for listen matches */
240 list_for_every_entry(&tcp_socket_list, s, tcp_socket_t, node) {
241 if (s->state == STATE_LISTEN) {
242 /* sockets in listen state only care about local port */
243 if (s->local_port == local_port) {
244 goto out;
245 }
246 }
247 }
248
249 /* fall through case returns null */
250 s = NULL;
251
252 out:
253 /* bump the ref before returning it */
254 if (s)
255 inc_socket_ref(s);
256
257 mutex_release(&tcp_socket_list_lock);
258
259 return s;
260 }
261
add_socket_to_list(tcp_socket_t * s)262 static void add_socket_to_list(tcp_socket_t *s) {
263 DEBUG_ASSERT(s);
264 DEBUG_ASSERT(s->ref > 0); // we should have implicitly bumped the ref when creating the socket
265
266 mutex_acquire(&tcp_socket_list_lock);
267
268 list_add_head(&tcp_socket_list, &s->node);
269
270 mutex_release(&tcp_socket_list_lock);
271 }
272
remove_socket_from_list(tcp_socket_t * s)273 static void remove_socket_from_list(tcp_socket_t *s) {
274 DEBUG_ASSERT(s);
275 DEBUG_ASSERT(s->ref > 0);
276
277 mutex_acquire(&tcp_socket_list_lock);
278
279 DEBUG_ASSERT(list_in_list(&s->node));
280 list_delete(&s->node);
281
282 mutex_release(&tcp_socket_list_lock);
283 }
284
inc_socket_ref(tcp_socket_t * s)285 static void inc_socket_ref(tcp_socket_t *s) {
286 DEBUG_ASSERT(s);
287
288 __UNUSED int oldval = atomic_add(&s->ref, 1);
289 LTRACEF("caller %p, thread %p, socket %p, ref now %d\n", __GET_CALLER(), get_current_thread(), s, oldval + 1);
290 DEBUG_ASSERT(oldval > 0);
291 }
292
dec_socket_ref(tcp_socket_t * s)293 static bool dec_socket_ref(tcp_socket_t *s) {
294 DEBUG_ASSERT(s);
295
296 int oldval = atomic_add(&s->ref, -1);
297 LTRACEF("caller %p, thread %p, socket %p, ref now %d\n", __GET_CALLER(), get_current_thread(), s, oldval - 1);
298
299 if (oldval == 1) {
300 LTRACEF("destroying socket\n");
301 event_destroy(&s->tx_event);
302 event_destroy(&s->rx_event);
303
304 free(s->rx_buffer_raw);
305 free(s->tx_buffer);
306
307 free(s);
308 }
309 return (oldval == 1);
310 }
311
tcp_timer_set(tcp_socket_t * s,net_timer_t * timer,net_timer_callback_t cb,lk_time_t delay)312 static void tcp_timer_set(tcp_socket_t *s, net_timer_t *timer, net_timer_callback_t cb, lk_time_t delay) {
313 DEBUG_ASSERT(s);
314 DEBUG_ASSERT(timer);
315
316 if (net_timer_set(timer, cb, s, delay))
317 inc_socket_ref(s);
318 }
319
tcp_timer_cancel(tcp_socket_t * s,net_timer_t * timer)320 static void tcp_timer_cancel(tcp_socket_t *s, net_timer_t *timer) {
321
322 DEBUG_ASSERT(s);
323 DEBUG_ASSERT(timer);
324
325 if (net_timer_cancel(timer))
326 dec_socket_ref(s);
327 }
328
tcp_input(pktbuf_t * p,uint32_t src_ip,uint32_t dst_ip)329 void tcp_input(pktbuf_t *p, uint32_t src_ip, uint32_t dst_ip) {
330 if (unlikely(tcp_debug))
331 TRACEF("p %p (len %u), src_ip 0x%x, dst_ip 0x%x\n", p, p->dlen, src_ip, dst_ip);
332
333 tcp_header_t *header = (tcp_header_t *)p->data;
334
335 /* reject if too small */
336 if (p->dlen < sizeof(tcp_header_t))
337 return;
338
339 if (unlikely(tcp_debug) || LOCAL_TRACE) {
340 dump_tcp_header(header);
341 }
342
343 /* compute the actual header length (+ options) */
344 size_t header_len = ((ntohs(header->length_flags) >> 12) & 0xf) * 4;
345 if (p->dlen < header_len) {
346 TRACEF("REJECT: packet too large for buffer\n");
347 return;
348 }
349
350 /* checksum */
351 if (FORCE_TCP_CHECKSUM || (p->flags & PKTBUF_FLAG_CKSUM_TCP_GOOD) == 0) {
352 tcp_pseudo_header_t pheader;
353
354 // set up the pseudo header for checksum purposes
355 pheader.source_addr = src_ip;
356 pheader.dest_addr = dst_ip;
357 pheader.zero = 0;
358 pheader.protocol = IP_PROTO_TCP;
359 pheader.tcp_length = htons(p->dlen);
360
361 uint16_t checksum = cksum_pheader(&pheader, p->data, p->dlen);
362 if (checksum != 0) {
363 TRACEF("REJECT: failed checksum, header says 0x%x, we got 0x%x\n", header->checksum, checksum);
364 return;
365 }
366 }
367
368 /* byte swap header in place */
369 header->source_port = ntohs(header->source_port);
370 header->dest_port = ntohs(header->dest_port);
371 header->seq_num = ntohl(header->seq_num);
372 header->ack_num = ntohl(header->ack_num);
373 header->length_flags = ntohs(header->length_flags);
374 header->win_size = ntohs(header->win_size);
375 header->urg_pointer = ntohs(header->urg_pointer);
376
377 /* get some data from the packet */
378 uint8_t packet_flags = header->length_flags & 0x3f;
379 size_t data_len = p->dlen - header_len;
380 uint32_t highest_sequence = header->seq_num + ((data_len > 0) ? (data_len - 1) : 0);
381
382 /* see if it matches a socket we have */
383 tcp_socket_t *s = lookup_socket(src_ip, dst_ip, header->source_port, header->dest_port);
384 if (!s) {
385 /* send a RST packet */
386 goto send_reset;
387 }
388
389 if (unlikely(tcp_debug))
390 TRACEF("got socket %p, state %d (%s), ref %d\n", s, s->state, tcp_state_to_string(s->state), s->ref);
391
392 /* remove the header */
393 pktbuf_consume(p, header_len);
394
395 mutex_acquire(&s->lock);
396
397 /* check to see if they're resetting us */
398 if (packet_flags & PKT_RST) {
399 if (s->state != STATE_CLOSED && s->state != STATE_LISTEN) {
400 tcp_remote_close(s);
401 }
402 goto done;
403 }
404
405 switch (s->state) {
406 case STATE_CLOSED:
407 /* socket closed, send RST */
408 goto send_reset;
409
410 /* passive connect states */
411 case STATE_LISTEN: {
412 /* we're in listen and they want to talk to us */
413 if (!(packet_flags & PKT_SYN)) {
414 /* not a SYN, send RST */
415 goto send_reset;
416 }
417
418 /* see if we have a slot to accept */
419 if (s->accepted != NULL)
420 goto done;
421
422 /* make a new accept socket */
423 tcp_socket_t *accept_socket = create_tcp_socket(true);
424 if (!accept_socket)
425 goto done;
426
427 /* set it up */
428 accept_socket->local_ip = minip_get_ipaddr();
429 accept_socket->local_port = s->local_port;
430 accept_socket->remote_ip = src_ip;
431 accept_socket->remote_port = header->source_port;
432 accept_socket->state = STATE_SYN_RCVD;
433
434 mutex_acquire(&accept_socket->lock);
435
436 add_socket_to_list(accept_socket);
437
438 /* remember their sequence */
439 accept_socket->rx_win_low = header->seq_num + 1;
440 accept_socket->rx_win_high = accept_socket->rx_win_low + accept_socket->rx_win_size - 1;
441
442 /* save this socket and wake anyone up that is waiting to accept */
443 s->accepted = accept_socket;
444 sem_post(&s->accept_sem, true);
445
446 /* set up a mss option for sending back */
447 tcp_mss_option_t mss_option;
448 mss_option.kind = 0x2;
449 mss_option.len = 0x4;
450 mss_option.mss = ntohs(s->mss); // XXX make sure we fit in their mss
451
452 /* send a response */
453 tcp_socket_send(accept_socket, NULL, 0, PKT_ACK|PKT_SYN, &mss_option, sizeof(mss_option),
454 accept_socket->tx_win_low);
455
456 /* SYN consumed a sequence */
457 accept_socket->tx_win_low++;
458
459 mutex_release(&accept_socket->lock);
460 break;
461 }
462 case STATE_SYN_RCVD:
463 if (packet_flags & PKT_SYN) {
464 /* they must have not seen our ack of their original syn, retransmit */
465 // XXX implement
466 goto send_reset;
467 }
468
469 /* if they ack our SYN, we can move on to ESTABLISHED */
470 if (packet_flags & PKT_ACK) {
471 if (header->ack_num != s->tx_win_low) {
472 goto send_reset;
473 }
474
475 s->tx_win_high = s->tx_win_low + header->win_size;
476 s->tx_highest_seq = s->tx_win_low;
477
478 s->state = STATE_ESTABLISHED;
479 } else {
480 goto send_reset;
481 }
482
483 break;
484
485 case STATE_ESTABLISHED:
486 if (packet_flags & PKT_ACK) {
487 /* they're acking us */
488 handle_ack(s, header->ack_num, header->win_size);
489 }
490
491 if (data_len > 0) {
492 LTRACEF("new data, len %zu\n", data_len);
493 handle_data(s, p->data, p->dlen, header->seq_num);
494 }
495
496 if ((packet_flags & PKT_FIN) && SEQUENCE_GTE(s->rx_win_low, highest_sequence)) {
497 /* they're closing with us, and there's no outstanding data */
498
499 /* FIN consumed a sequence */
500 s->rx_win_low++;
501
502 /* ack them and transition to new state */
503 send_ack(s);
504 s->state = STATE_CLOSE_WAIT;
505
506 /* wake up any read waiters */
507 event_signal(&s->rx_event, true);
508 }
509 break;
510
511 case STATE_CLOSE_WAIT:
512 if (packet_flags & PKT_ACK) {
513 /* they're acking us */
514 handle_ack(s, header->ack_num, header->win_size);
515 }
516 if (packet_flags & PKT_FIN) {
517 /* they must have missed our ack, ack them again */
518 send_ack(s);
519 }
520 break;
521 case STATE_LAST_ACK:
522 if (packet_flags & PKT_ACK) {
523 /* they're acking our FIN, probably */
524 tcp_remote_close(s);
525
526 /* tcp_close() was already called on us, remove us from the list and drop the ref */
527 remove_socket_from_list(s);
528 dec_socket_ref(s);
529 }
530 break;
531 case STATE_FIN_WAIT_1:
532 if (packet_flags & PKT_ACK) {
533 /* they're acking our FIN, probably */
534 s->state = STATE_FIN_WAIT_2;
535 /* drop into fin_wait_2 state logic, in case they were FINning us too */
536 goto fin_wait_2;
537 } else if (packet_flags & PKT_FIN) {
538 /* simultaneous close. they finned us without acking our fin */
539 s->rx_win_low++;
540 send_ack(s);
541 s->state = STATE_CLOSING;
542 }
543 break;
544 case STATE_FIN_WAIT_2:
545 fin_wait_2:
546 if (packet_flags & PKT_FIN) {
547 /* they're FINning us, ack them */
548 s->rx_win_low++;
549 send_ack(s);
550 s->state = STATE_TIME_WAIT;
551
552 /* set timed wait timer */
553 tcp_timer_set(s, &s->time_wait_timer, &handle_time_wait_timeout, TIME_WAIT_TIMEOUT);
554 }
555 break;
556 case STATE_CLOSING:
557 if (packet_flags & PKT_ACK) {
558 /* they're acking our FIN, probably */
559 s->state = STATE_TIME_WAIT;
560
561 /* set timed wait timer */
562 tcp_timer_set(s, &s->time_wait_timer, &handle_time_wait_timeout, TIME_WAIT_TIMEOUT);
563 }
564 break;
565 case STATE_TIME_WAIT:
566 /* /dev/null of packets */
567 break;
568
569 case STATE_SYN_SENT:
570 PANIC_UNIMPLEMENTED;
571 }
572
573 done:
574 mutex_release(&s->lock);
575 dec_socket_ref(s);
576 return;
577
578 send_reset:
579 if (s) {
580 mutex_release(&s->lock);
581 dec_socket_ref(s);
582 }
583
584 LTRACEF("SEND RST\n");
585 if (!(packet_flags & PKT_RST)) {
586 tcp_send(src_ip, header->source_port, dst_ip, header->dest_port,
587 NULL, 0, PKT_RST, NULL, 0, 0, header->ack_num, 0);
588 }
589 }
590
handle_data(tcp_socket_t * s,const void * data,size_t len,uint32_t sequence)591 static void handle_data(tcp_socket_t *s, const void *data, size_t len, uint32_t sequence) {
592 if (unlikely(tcp_debug))
593 TRACEF("data %p, len %zu, sequence %u\n", data, len, sequence);
594
595 DEBUG_ASSERT(s);
596 DEBUG_ASSERT(is_mutex_held(&s->lock));
597 DEBUG_ASSERT(data);
598 DEBUG_ASSERT(len > 0);
599
600 /* see if it matches our current window */
601 uint32_t sequence_top = sequence + len - 1;
602 if (SEQUENCE_LTE(sequence, s->rx_win_low) && SEQUENCE_GTE(sequence_top, s->rx_win_low)) {
603 /* it intersects the bottom of our window, so it's in order */
604
605 /* copy the data we need to our cbuf */
606 size_t offset = sequence - s->rx_win_low;
607 size_t copy_len = MIN(s->rx_win_high - s->rx_win_low, len - offset);
608
609 DEBUG_ASSERT(offset < len);
610
611 LTRACEF("copying from offset %zu, len %zu\n", offset, copy_len);
612
613 s->rx_win_low += copy_len;
614
615 cbuf_write(&s->rx_buffer, (uint8_t *)data + offset, copy_len, false);
616 event_signal(&s->rx_event, true);
617
618 /* keep a counter if they've been sending a full mss */
619 if (copy_len >= s->mss) {
620 s->rx_full_mss_count++;
621 } else {
622 s->rx_full_mss_count = 0;
623 }
624
625 /* immediately ack if we're more than halfway into our buffer or they've sent 2 or more full packets */
626 if (s->rx_full_mss_count >= 2 ||
627 (int)(s->rx_win_low + s->rx_win_size - s->rx_win_high) > (int)s->rx_win_size / 2) {
628 send_ack(s);
629 s->rx_full_mss_count = 0;
630 } else {
631 tcp_timer_set(s, &s->ack_delay_timer, &handle_delayed_ack_timeout, DELAYED_ACK_TIMEOUT);
632 }
633 } else {
634 // either out of order or completely out of our window, drop
635 // duplicately ack the last thing we really got
636 send_ack(s);
637 }
638 }
639
tcp_socket_send(tcp_socket_t * s,const void * data,size_t len,tcp_flags_t flags,const void * options,size_t options_length,uint32_t sequence)640 static status_t tcp_socket_send(tcp_socket_t *s, const void *data, size_t len, tcp_flags_t flags,
641 const void *options, size_t options_length, uint32_t sequence) {
642 DEBUG_ASSERT(s);
643 DEBUG_ASSERT(is_mutex_held(&s->lock));
644 DEBUG_ASSERT(len == 0 || data);
645 DEBUG_ASSERT(options_length == 0 || options);
646 DEBUG_ASSERT((options_length % 4) == 0);
647
648 // calculate the new right edge of the rx window
649 uint32_t rx_win_high = s->rx_win_low + s->rx_win_size - cbuf_space_used(&s->rx_buffer) - 1;
650
651 LTRACEF("rx_win_low %u rx_win_size %u read_buf_len %zu, new win high %u\n",
652 s->rx_win_low, s->rx_win_size, cbuf_space_used(&s->rx_buffer), rx_win_high);
653
654 uint16_t win_size;
655 if (SEQUENCE_GTE(rx_win_high, s->rx_win_high)) {
656 s->rx_win_high = rx_win_high;
657 win_size = rx_win_high - s->rx_win_low;
658 } else {
659 // the window size has shrunk, but we can't move the
660 // right edge of the window backwards
661 win_size = s->rx_win_high - s->rx_win_low;
662 }
663
664 // we are piggybacking a pending ACK, so clear the delayed ACK timer
665 if (flags & PKT_ACK) {
666 tcp_timer_cancel(s, &s->ack_delay_timer);
667 }
668
669 status_t err = tcp_send(s->remote_ip, s->remote_port, s->local_ip, s->local_port, data, len, flags,
670 options, options_length, (flags & PKT_ACK) ? s->rx_win_low : 0, sequence, win_size);
671
672 return err;
673 }
674
send_ack(tcp_socket_t * s)675 static void send_ack(tcp_socket_t *s) {
676 DEBUG_ASSERT(s);
677 DEBUG_ASSERT(is_mutex_held(&s->lock));
678
679 if (s->state != STATE_ESTABLISHED && s->state != STATE_CLOSE_WAIT && s->state != STATE_FIN_WAIT_2)
680 return;
681
682 tcp_socket_send(s, NULL, 0, PKT_ACK, NULL, 0, s->tx_win_low);
683 }
684
tcp_send(ipv4_addr dest_ip,uint16_t dest_port,ipv4_addr src_ip,uint16_t src_port,const void * buf,size_t len,tcp_flags_t flags,const void * options,size_t options_length,uint32_t ack,uint32_t sequence,uint16_t window_size)685 static status_t tcp_send(ipv4_addr dest_ip, uint16_t dest_port, ipv4_addr src_ip, uint16_t src_port, const void *buf,
686 size_t len, tcp_flags_t flags, const void *options, size_t options_length, uint32_t ack, uint32_t sequence, uint16_t window_size) {
687 DEBUG_ASSERT(len == 0 || buf);
688 DEBUG_ASSERT(options_length == 0 || options);
689 DEBUG_ASSERT((options_length % 4) == 0);
690
691 pktbuf_t *p = pktbuf_alloc();
692 if (!p)
693 return ERR_NO_MEMORY;
694
695 tcp_header_t *header = pktbuf_prepend(p, sizeof(tcp_header_t) + options_length);
696 DEBUG_ASSERT(header);
697
698 /* fill in the header */
699 header->source_port = htons(src_port);
700 header->dest_port = htons(dest_port);
701 header->seq_num = htonl(sequence);
702 header->ack_num = htonl(ack);
703 header->length_flags = htons(((sizeof(tcp_header_t) + options_length) / 4) << 12 | flags);
704 header->win_size = htons(window_size);
705 header->checksum = 0;
706 header->urg_pointer = 0;
707 if (options)
708 memcpy(header + 1, options, options_length);
709
710 /* append the data */
711 if (len > 0)
712 pktbuf_append_data(p, buf, len);
713
714 /* compute the checksum */
715 /* XXX get the tx ckecksum capability from the nic */
716 if (FORCE_TCP_CHECKSUM || true) {
717 tcp_pseudo_header_t pheader;
718 pheader.source_addr = src_ip;
719 pheader.dest_addr = dest_ip;
720 pheader.zero = 0;
721 pheader.protocol = IP_PROTO_TCP;
722 pheader.tcp_length = htons(p->dlen);
723
724 header->checksum = cksum_pheader(&pheader, p->data, p->dlen);
725 }
726
727 if (LOCAL_TRACE) {
728 printf("sending ");
729 dump_tcp_header(header);
730 }
731
732 status_t err = minip_ipv4_send(p, dest_ip, IP_PROTO_TCP);
733
734 return err;
735 }
736
handle_ack(tcp_socket_t * s,uint32_t sequence,uint32_t win_size)737 static void handle_ack(tcp_socket_t *s, uint32_t sequence, uint32_t win_size) {
738 LTRACEF("socket %p ack sequence %u, win_size %u\n", s, sequence, win_size);
739
740 DEBUG_ASSERT(s);
741 DEBUG_ASSERT(is_mutex_held(&s->lock));
742
743 LTRACEF("s %p, tx_win_low %u tx_win_high %u tx_highest_seq %u bufsize %u offset %u\n",
744 s, s->tx_win_low, s->tx_win_high, s->tx_highest_seq, s->tx_buffer_size, s->tx_buffer_offset);
745 if (SEQUENCE_LTE(sequence, s->tx_win_low)) {
746 /* they're acking stuff we've already received an ack for */
747 return;
748 } else if (SEQUENCE_GT(sequence, s->tx_highest_seq)) {
749 /* they're acking stuff we haven't sent */
750 return;
751 } else {
752 /* their ack is somewhere in our window */
753 uint32_t acked_len;
754
755 acked_len = (sequence - s->tx_win_low);
756
757 LTRACEF("acked len %u\n", acked_len);
758
759 DEBUG_ASSERT(acked_len <= s->tx_buffer_size);
760 DEBUG_ASSERT(acked_len <= s->tx_buffer_offset);
761
762 memmove(s->tx_buffer, s->tx_buffer + acked_len, s->tx_buffer_offset - acked_len);
763
764 s->tx_buffer_offset -= acked_len;
765 s->tx_win_low += acked_len;
766 s->tx_win_high = s->tx_win_low + win_size;
767
768 /* cancel or reset our retransmit timer */
769 if (s->tx_win_low == s->tx_highest_seq) {
770 tcp_timer_cancel(s, &s->retransmit_timer);
771 } else {
772 tcp_timer_set(s, &s->retransmit_timer, &handle_retransmit_timeout, RETRANSMIT_TIMEOUT);
773 }
774
775 /* we have opened the transmit buffer */
776 event_signal(&s->tx_event, true);
777 }
778 }
779
tcp_write_pending_data(tcp_socket_t * s)780 static ssize_t tcp_write_pending_data(tcp_socket_t *s) {
781 LTRACEF("s %p, tx_win_low %u tx_win_high %u tx_highest_seq %u bufsize %u offset %u\n",
782 s, s->tx_win_low, s->tx_win_high, s->tx_highest_seq, s->tx_buffer_size, s->tx_buffer_offset);
783
784 DEBUG_ASSERT(s);
785 DEBUG_ASSERT(is_mutex_held(&s->lock));
786 DEBUG_ASSERT(s->tx_buffer_size > 0);
787 DEBUG_ASSERT(s->tx_buffer_offset <= s->tx_buffer_size);
788
789 /* do we have any new data to send? */
790 uint32_t outstanding = (s->tx_highest_seq - s->tx_win_low);
791 uint32_t pending = s->tx_buffer_offset - outstanding;
792 LTRACEF("outstanding %u, pending %u\n", outstanding, pending);
793
794 /* send packets that cover the pending area of the window */
795 uint32_t offset = 0;
796 while (offset < pending) {
797 uint32_t tosend = MIN(s->mss, pending - offset);
798
799 tcp_socket_send(s, s->tx_buffer + outstanding + offset, tosend, PKT_ACK|PKT_PSH, NULL, 0, s->tx_highest_seq);
800 s->tx_highest_seq += tosend;
801 offset += tosend;
802 }
803
804 /* reset the retransmit timer if we sent anything */
805 if (offset > 0) {
806 tcp_timer_set(s, &s->retransmit_timer, &handle_retransmit_timeout, RETRANSMIT_TIMEOUT);
807 }
808
809 return offset;
810 }
811
tcp_retransmit(tcp_socket_t * s)812 static ssize_t tcp_retransmit(tcp_socket_t *s) {
813 DEBUG_ASSERT(s);
814 DEBUG_ASSERT(is_mutex_held(&s->lock));
815
816 if (s->state != STATE_ESTABLISHED && s->state != STATE_CLOSE_WAIT)
817 return 0;
818
819 /* how much data have we sent but not gotten an ack for? */
820 uint32_t outstanding = (s->tx_highest_seq - s->tx_win_low);
821 if (outstanding == 0)
822 return 0;
823
824 uint32_t tosend = MIN(s->mss, outstanding);
825
826 LTRACEF("s %p, tosend %u seq %u\n", s, tosend, s->tx_win_low);
827 tcp_socket_send(s, s->tx_buffer, tosend, PKT_ACK|PKT_PSH, NULL, 0, s->tx_win_low);
828
829 return tosend;
830 }
831
handle_retransmit_timeout(void * _s)832 static void handle_retransmit_timeout(void *_s) {
833 tcp_socket_t *s = _s;
834
835 LTRACEF("s %p\n", s);
836
837 DEBUG_ASSERT(s);
838
839 mutex_acquire(&s->lock);
840
841 if (tcp_retransmit(s) == 0)
842 goto done;
843
844 tcp_timer_set(s, &s->retransmit_timer, &handle_retransmit_timeout, RETRANSMIT_TIMEOUT);
845
846 done:
847 mutex_release(&s->lock);
848 dec_socket_ref(s);
849 }
850
handle_delayed_ack_timeout(void * _s)851 static void handle_delayed_ack_timeout(void *_s) {
852 tcp_socket_t *s = _s;
853
854 LTRACEF("s %p\n", s);
855
856 DEBUG_ASSERT(s);
857
858 mutex_acquire(&s->lock);
859 send_ack(s);
860 mutex_release(&s->lock);
861 dec_socket_ref(s);
862 }
863
handle_time_wait_timeout(void * _s)864 static void handle_time_wait_timeout(void *_s) {
865 tcp_socket_t *s = _s;
866
867 LTRACEF("s %p\n", s);
868
869 DEBUG_ASSERT(s);
870
871 mutex_acquire(&s->lock);
872
873 DEBUG_ASSERT(s->state == STATE_TIME_WAIT);
874
875 /* remove us from the list and drop the last ref */
876 remove_socket_from_list(s);
877 dec_socket_ref(s);
878
879 mutex_release(&s->lock);
880 dec_socket_ref(s);
881 }
882
tcp_wakeup_waiters(tcp_socket_t * s)883 static void tcp_wakeup_waiters(tcp_socket_t *s) {
884 DEBUG_ASSERT(s);
885 DEBUG_ASSERT(is_mutex_held(&s->lock));
886
887 // wake up any waiters
888 event_signal(&s->rx_event, true);
889 event_signal(&s->tx_event, true);
890 }
891
tcp_remote_close(tcp_socket_t * s)892 static void tcp_remote_close(tcp_socket_t *s) {
893 LTRACEF("s %p, ref %d\n", s, s->ref);
894
895 DEBUG_ASSERT(s);
896 DEBUG_ASSERT(is_mutex_held(&s->lock));
897 DEBUG_ASSERT(s->ref > 0);
898
899 if (s->state == STATE_CLOSED)
900 return;
901
902 s->state = STATE_CLOSED;
903
904 tcp_timer_cancel(s, &s->retransmit_timer);
905 tcp_timer_cancel(s, &s->ack_delay_timer);
906
907 tcp_wakeup_waiters(s);
908 }
909
create_tcp_socket(bool alloc_buffers)910 static tcp_socket_t *create_tcp_socket(bool alloc_buffers) {
911 tcp_socket_t *s;
912
913 s = calloc(1, sizeof(tcp_socket_t));
914 if (!s)
915 return NULL;
916
917 mutex_init(&s->lock);
918 s->ref = 1; // start with the ref already bumped
919
920 s->state = STATE_CLOSED;
921 s->rx_win_size = DEFAULT_RX_WINDOW_SIZE;
922 event_init(&s->rx_event, false, 0);
923
924 s->mss = DEFAULT_MSS;
925
926 s->tx_win_low = rand();
927 s->tx_win_high = s->tx_win_low;
928 s->tx_highest_seq = s->tx_win_low;
929 event_init(&s->tx_event, true, 0);
930
931 if (alloc_buffers) {
932 // XXX check for error
933 s->rx_buffer_raw = malloc(s->rx_win_size);
934 cbuf_initialize_etc(&s->rx_buffer, s->rx_win_size, s->rx_buffer_raw);
935
936 s->tx_buffer_size = DEFAULT_TX_BUFFER_SIZE;
937 s->tx_buffer = malloc(s->tx_buffer_size);
938 }
939
940 sem_init(&s->accept_sem, 0);
941
942 return s;
943 }
944
945 /* user api */
946
tcp_open_listen(tcp_socket_t ** handle,uint16_t port)947 status_t tcp_open_listen(tcp_socket_t **handle, uint16_t port) {
948 tcp_socket_t *s;
949
950 if (!handle)
951 return ERR_INVALID_ARGS;
952
953 s = create_tcp_socket(false);
954 if (!s)
955 return ERR_NO_MEMORY;
956
957 // XXX see if there's another listen socket already on this port
958
959 s->local_port = port;
960
961 /* go to listen state */
962 s->state = STATE_LISTEN;
963
964 add_socket_to_list(s);
965
966 *handle = s;
967
968 return NO_ERROR;
969 }
970
tcp_accept_timeout(tcp_socket_t * listen_socket,tcp_socket_t ** accept_socket,lk_time_t timeout)971 status_t tcp_accept_timeout(tcp_socket_t *listen_socket, tcp_socket_t **accept_socket, lk_time_t timeout) {
972 if (!listen_socket || !accept_socket)
973 return ERR_INVALID_ARGS;
974
975 tcp_socket_t *s = listen_socket;
976 inc_socket_ref(s);
977
978 /* block to accept a socket for an amount of time */
979 if (sem_timedwait(&s->accept_sem, timeout) == ERR_TIMED_OUT) {
980 dec_socket_ref(s);
981 return ERR_TIMED_OUT;
982 }
983
984 mutex_acquire(&s->lock);
985
986 /* we got here, grab the accepted socket and return */
987 DEBUG_ASSERT(s->accepted);
988 *accept_socket = s->accepted;
989 s->accepted = NULL;
990
991 mutex_release(&s->lock);
992 dec_socket_ref(s);
993
994 return NO_ERROR;
995 }
996
tcp_read(tcp_socket_t * socket,void * buf,size_t len)997 ssize_t tcp_read(tcp_socket_t *socket, void *buf, size_t len) {
998 LTRACEF("socket %p, buf %p, len %zu\n", socket, buf, len);
999 if (!socket)
1000 return ERR_INVALID_ARGS;
1001 if (len == 0)
1002 return 0;
1003 if (!buf)
1004 return ERR_INVALID_ARGS;
1005
1006 tcp_socket_t *s = socket;
1007 inc_socket_ref(s);
1008
1009 ssize_t ret = 0;
1010 retry:
1011 /* block on available data */
1012 event_wait(&s->rx_event);
1013
1014 mutex_acquire(&s->lock);
1015
1016 /* try to read some data from the receive buffer, even if we're closed */
1017 ret = cbuf_read(&s->rx_buffer, buf, len, false);
1018 if (ret == 0) {
1019 /* check to see if we've closed */
1020 if (s->state != STATE_ESTABLISHED) {
1021 ret = ERR_CHANNEL_CLOSED;
1022 goto out;
1023 }
1024
1025 /* we must have raced with another thread */
1026 event_unsignal(&s->rx_event);
1027 mutex_release(&s->lock);
1028 goto retry;
1029 }
1030
1031 /* if we've used up the last byte in the read buffer, unsignal the read event */
1032 size_t remaining_bytes = cbuf_space_used(&s->rx_buffer);
1033 if (s->state == STATE_ESTABLISHED && remaining_bytes == 0) {
1034 event_unsignal(&s->rx_event);
1035 }
1036
1037 /* we've read something, make sure the other end knows that our window is opening */
1038 uint32_t new_rx_win_size = s->rx_win_size - remaining_bytes;
1039
1040 /* if we've opened it enough, send an ack */
1041 if (new_rx_win_size >= s->mss && s->rx_win_high - s->rx_win_low < s->mss)
1042 send_ack(s);
1043
1044 out:
1045 mutex_release(&s->lock);
1046 dec_socket_ref(s);
1047
1048 return ret;
1049 }
1050
tcp_write(tcp_socket_t * socket,const void * buf,size_t len)1051 ssize_t tcp_write(tcp_socket_t *socket, const void *buf, size_t len) {
1052 LTRACEF("socket %p, buf %p, len %zu\n", socket, buf, len);
1053 if (!socket)
1054 return ERR_INVALID_ARGS;
1055 if (len == 0)
1056 return 0;
1057 if (!buf)
1058 return ERR_INVALID_ARGS;
1059
1060 tcp_socket_t *s = socket;
1061 inc_socket_ref(s);
1062
1063 size_t off = 0;
1064 while (off < len) {
1065 LTRACEF("off %zu, len %zu\n", off, len);
1066
1067 /* wait for the tx buffer to open up */
1068 event_wait(&s->tx_event);
1069 LTRACEF("after event_wait\n");
1070
1071 mutex_acquire(&s->lock);
1072
1073 /* check to see if we've closed */
1074 if (s->state != STATE_ESTABLISHED && s->state != STATE_CLOSE_WAIT) {
1075 mutex_release(&s->lock);
1076 dec_socket_ref(s);
1077 return ERR_CHANNEL_CLOSED;
1078 }
1079
1080 DEBUG_ASSERT(s->tx_buffer_size > 0);
1081 DEBUG_ASSERT(s->tx_buffer_offset <= s->tx_buffer_size);
1082
1083 /* figure out how much data to copy in */
1084 size_t to_copy = MIN(s->tx_buffer_size - s->tx_buffer_offset, len - off);
1085 if (to_copy == 0) {
1086 mutex_release(&s->lock);
1087 continue;
1088 }
1089
1090 memcpy(s->tx_buffer + s->tx_buffer_offset, (uint8_t *)buf + off, to_copy);
1091 s->tx_buffer_offset += to_copy;
1092
1093 /* if this has completely filled it, unsignal the event */
1094 DEBUG_ASSERT(s->tx_buffer_offset <= s->tx_buffer_size);
1095 if (s->tx_buffer_offset == s->tx_buffer_size) {
1096 event_unsignal(&s->tx_event);
1097 }
1098
1099 /* send as much data as we can */
1100 tcp_write_pending_data(s);
1101
1102 off += to_copy;
1103
1104 mutex_release(&s->lock);
1105 }
1106
1107 dec_socket_ref(s);
1108 return len;
1109 }
1110
tcp_close(tcp_socket_t * socket)1111 status_t tcp_close(tcp_socket_t *socket) {
1112 if (!socket)
1113 return ERR_INVALID_ARGS;
1114
1115 tcp_socket_t *s = socket;
1116
1117 inc_socket_ref(s);
1118 mutex_acquire(&s->lock);
1119
1120 LTRACEF("socket %p, state %d (%s), ref %d\n", s, s->state, tcp_state_to_string(s->state), s->ref);
1121
1122 status_t err;
1123 switch (s->state) {
1124 case STATE_CLOSED:
1125 case STATE_LISTEN:
1126 /* we can directly remove this socket */
1127 remove_socket_from_list(s);
1128
1129 /* drop any timers that may be pending on this */
1130 tcp_timer_cancel(s, &s->ack_delay_timer);
1131 tcp_timer_cancel(s, &s->retransmit_timer);
1132
1133 s->state = STATE_CLOSED;
1134
1135 /* drop the extra ref that was held when the socket was created */
1136 dec_socket_ref(s);
1137 break;
1138 case STATE_SYN_RCVD:
1139 case STATE_ESTABLISHED:
1140 s->state = STATE_FIN_WAIT_1;
1141 tcp_socket_send(s, NULL, 0, PKT_ACK|PKT_FIN, NULL, 0, s->tx_win_low);
1142 s->tx_win_low++;
1143
1144 /* stick around and wait for them to FIN us */
1145 break;
1146 case STATE_CLOSE_WAIT:
1147 s->state = STATE_LAST_ACK;
1148 tcp_socket_send(s, NULL, 0, PKT_ACK|PKT_FIN, NULL, 0, s->tx_win_low);
1149 s->tx_win_low++;
1150
1151 // XXX set up fin retransmit timer here
1152 break;
1153 case STATE_FIN_WAIT_1:
1154 case STATE_FIN_WAIT_2:
1155 case STATE_CLOSING:
1156 case STATE_TIME_WAIT:
1157 case STATE_LAST_ACK:
1158 /* these states are all post tcp_close(), so it's invalid to call it here */
1159 err = ERR_CHANNEL_CLOSED;
1160 goto out;
1161 default:
1162 PANIC_UNIMPLEMENTED;
1163 }
1164
1165 /* make sure anyone blocked on this wakes up */
1166 tcp_wakeup_waiters(s);
1167
1168 mutex_release(&s->lock);
1169
1170 err = NO_ERROR;
1171
1172 out:
1173 /* if this was the last ref, it should destroy the socket */
1174 dec_socket_ref(s);
1175
1176 return err;
1177 }
1178
1179 /* debug stuff */
cmd_tcp(int argc,const console_cmd_args * argv)1180 static int cmd_tcp(int argc, const console_cmd_args *argv) {
1181 if (argc < 2) {
1182 notenoughargs:
1183 printf("ERROR not enough arguments\n");
1184 usage:
1185 printf("usage: %s sockets\n", argv[0].str);
1186 printf("usage: %s listenclose <port>\n", argv[0].str);
1187 printf("usage: %s listen <port>\n", argv[0].str);
1188 printf("usage: %s debug\n", argv[0].str);
1189 return ERR_INVALID_ARGS;
1190 }
1191
1192 if (!strcmp(argv[1].str, "sockets")) {
1193
1194 mutex_acquire(&tcp_socket_list_lock);
1195 tcp_socket_t *s = NULL;
1196 list_for_every_entry(&tcp_socket_list, s, tcp_socket_t, node) {
1197 dump_socket(s);
1198 }
1199 mutex_release(&tcp_socket_list_lock);
1200 } else if (!strcmp(argv[1].str, "listenclose")) {
1201 /* listen for a connection, accept it, then immediately close it */
1202 if (argc < 3) goto notenoughargs;
1203
1204 tcp_socket_t *handle = NULL;
1205
1206 status_t err = tcp_open_listen(&handle, argv[2].u);
1207 printf("tcp_open_listen returns %d, handle %p\n", err, handle);
1208
1209 tcp_socket_t *accepted;
1210 err = tcp_accept(handle, &accepted);
1211 printf("tcp_accept returns returns %d, handle %p\n", err, accepted);
1212
1213 err = tcp_close(accepted);
1214 printf("tcp_close returns %d\n", err);
1215
1216 err = tcp_close(handle);
1217 printf("tcp_close returns %d\n", err);
1218 } else if (!strcmp(argv[1].str, "listen")) {
1219 if (argc < 3) goto notenoughargs;
1220
1221 tcp_socket_t *handle = NULL;
1222
1223 status_t err = tcp_open_listen(&handle, argv[2].u);
1224 printf("tcp_open_listen returns %d, handle %p\n", err, handle);
1225
1226 tcp_socket_t *accepted;
1227 err = tcp_accept(handle, &accepted);
1228 printf("tcp_accept returns returns %d, handle %p\n", err, accepted);
1229
1230 for (;;) {
1231 uint8_t buf[512];
1232
1233 ssize_t err_len = tcp_read(accepted, buf, sizeof(buf));
1234 printf("tcp_read returns %ld\n", err_len);
1235 if (err_len < 0)
1236 break;
1237 if (err_len > 0) {
1238 hexdump8(buf, err_len);
1239 }
1240
1241 err_len = tcp_write(accepted, buf, err_len);
1242 printf("tcp_write returns %ld\n", err_len);
1243 if (err_len < 0)
1244 break;
1245 }
1246
1247 err = tcp_close(accepted);
1248 printf("tcp_close returns %d\n", err);
1249
1250 err = tcp_close(handle);
1251 printf("tcp_close returns %d\n", err);
1252 } else if (!strcmp(argv[1].str, "debug")) {
1253 tcp_debug = !tcp_debug;
1254 printf("tcp debug now %u\n", tcp_debug);
1255 } else {
1256 printf("ERROR unknown command\n");
1257 goto usage;
1258 }
1259
1260 return NO_ERROR;
1261 }
1262
1263 STATIC_COMMAND_START
1264 STATIC_COMMAND("tcp", "tcp commands", &cmd_tcp)
1265 STATIC_COMMAND_END(tcp);
1266
1267