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