source: SVN/cambria/redboot/packages/redboot/current/src/net/tcp.c @ 1

Last change on this file since 1 was 1, checked in by Tim Harvey, 2 years ago

restored latest version of files from server backup

Signed-off-by: Tim Harvey <tharvey@…>

File size: 22.5 KB
Line 
1//==========================================================================
2//
3//      net/tcp.c
4//
5//      Stand-alone TCP networking support for RedBoot
6//
7//==========================================================================
8//####ECOSGPLCOPYRIGHTBEGIN####
9// -------------------------------------------
10// This file is part of eCos, the Embedded Configurable Operating System.
11// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12// Copyright (C) 2003 Gary Thomas
13//
14// eCos is free software; you can redistribute it and/or modify it under
15// the terms of the GNU General Public License as published by the Free
16// Software Foundation; either version 2 or (at your option) any later version.
17//
18// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19// WARRANTY; without even the implied warranty of MERCHANTABILITY or
20// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21// for more details.
22//
23// You should have received a copy of the GNU General Public License along
24// with eCos; if not, write to the Free Software Foundation, Inc.,
25// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26//
27// As a special exception, if other files instantiate templates or use macros
28// or inline functions from this file, or you compile this file and link it
29// with other works to produce a work based on this file, this file does not
30// by itself cause the resulting work to be covered by the GNU General Public
31// License. However the source code for this file must still be made available
32// in accordance with section (3) of the GNU General Public License.
33//
34// This exception does not invalidate any other reasons why a work based on
35// this file might be covered by the GNU General Public License.
36//
37// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38// at http://sources.redhat.com/ecos/ecos-license/
39// -------------------------------------------
40//####ECOSGPLCOPYRIGHTEND####
41//==========================================================================
42//#####DESCRIPTIONBEGIN####
43//
44// Author(s):    gthomas
45// Contributors: gthomas
46// Date:         2000-07-14
47// Purpose:     
48// Description: 
49//             
50// This code is part of RedBoot (tm).
51//
52//####DESCRIPTIONEND####
53//
54//==========================================================================
55
56#include <net/net.h>
57#include <cyg/infra/diag.h>
58#include <cyg/hal/hal_if.h>
59
60#define MAX_TCP_SEGMENT (ETH_MAX_PKTLEN - (sizeof(eth_header_t) + sizeof(ip_header_t)))
61#define MAX_TCP_DATA    (MAX_TCP_SEGMENT - sizeof(tcp_header_t))
62
63
64/* sequence number comparison macros */
65#define SEQ_LT(a,b) ((int)((a)-(b)) < 0)
66#define SEQ_LE(a,b) ((int)((a)-(b)) <= 0)
67#define SEQ_GT(a,b) ((int)((a)-(b)) > 0)
68#define SEQ_GE(a,b) ((int)((a)-(b)) >= 0)
69
70/* Set a timer which will send an RST and abort a connection. */
71static timer_t abort_timer;
72
73static void do_retrans(void *p);
74static void do_close(void *p);
75
76#ifdef BSP_LOG
77static char *
78flags_to_str(octet f)
79{
80    static char str[7], *p;
81
82    p = str;
83
84    if (f & TCP_FLAG_FIN)
85        *p++ = 'F';
86    if (f & TCP_FLAG_SYN)
87        *p++ = 'S';
88    if (f & TCP_FLAG_RST)
89        *p++ = 'R';
90    if (f & TCP_FLAG_PSH)
91        *p++ = 'P';
92    if (f & TCP_FLAG_ACK)
93        *p++ = 'A';
94    if (f & TCP_FLAG_URG)
95        *p++ = 'U';
96    *p = '\0';
97    return str;
98}
99#endif
100
101/*
102 * A major assumption is that only a very small number of sockets will
103 * active, so a simple linear search of those sockets is acceptible.
104 */
105static tcp_socket_t *tcp_list;
106
107/*
108 * Format and send an outgoing segment.
109 */
110static void
111tcp_send(tcp_socket_t *s, int flags, int resend)
112{
113    tcp_header_t *tcp;
114    ip_header_t  *ip;
115    pktbuf_t     *pkt = &s->pkt;
116    unsigned short cksum;
117    dword         tcp_magic;
118    int           tcp_magic_size = sizeof(tcp_magic);
119
120    ip = pkt->ip_hdr;
121    tcp = pkt->tcp_hdr;
122
123    if (flags & TCP_FLAG_SYN) {
124        /* If SYN, assume no data and send MSS option in tcp header */
125        pkt->pkt_bytes = sizeof(tcp_header_t) + 4;
126        tcp->hdr_len = 6;
127        tcp_magic = htonl(0x02040000 | MAX_TCP_DATA);
128        memcpy((unsigned char *)(tcp+1), &tcp_magic, tcp_magic_size);
129        s->data_bytes = 0;
130    } else {
131        pkt->pkt_bytes = s->data_bytes + sizeof(tcp_header_t);
132        tcp->hdr_len = 5;
133    }
134
135    /* tcp header */
136    tcp->reserved = 0;
137    tcp->seqnum = htonl(s->seq);
138    tcp->acknum = htonl(s->ack);
139    tcp->checksum = 0;
140
141    if (!resend) {
142        tcp->src_port = htons(s->our_port);
143        tcp->dest_port = htons(s->his_port);
144        tcp->flags = flags;
145        /* always set PUSH flag if sending data */
146        if (s->data_bytes)
147            tcp->flags |= TCP_FLAG_PSH;
148        tcp->window = htons(MAX_TCP_DATA);
149        tcp->urgent = 0;
150
151        /* fill in some pseudo-header fields */
152        memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
153        memcpy(ip->destination, s->his_addr.ip_addr, sizeof(ip_addr_t));
154        ip->protocol = IP_PROTO_TCP;
155    }
156
157    /* another pseudo-header field */
158    ip->length = htons(pkt->pkt_bytes);
159
160    /* compute tcp checksum */
161    cksum = __sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip));
162    tcp->checksum = htons(cksum);
163
164    __ip_send(pkt, IP_PROTO_TCP, &s->his_addr);
165
166    // HACK!  If this delay is not present, then if the target system sends
167    // back data (not just an ACK), then somehow we miss it :-(
168    CYGACC_CALL_IF_DELAY_US(2*1000);
169
170    BSPLOG(bsp_log("tcp_send: state[%d] flags[%s] ack[%x] data[%d].\n",
171                   s->state, flags_to_str(tcp->flags), s->ack, s->data_bytes));
172
173    if (s->state == _TIME_WAIT) {
174        // If 'reuse' is set on socket, close after 1 second, otherwise 2 minutes
175        __timer_set(&s->timer, s->reuse ? 1000 : 120000, do_close, s);
176    }
177    else if ((tcp->flags & (TCP_FLAG_FIN | TCP_FLAG_SYN)) || s->data_bytes)
178        __timer_set(&s->timer, 1000, do_retrans, s);
179}
180
181static pktbuf_t ack_pkt;
182static word     ack_buf[ETH_MIN_PKTLEN/sizeof(word)];
183
184/*
185 * Send an ack.
186 */
187static void
188send_ack(tcp_socket_t *s)
189{
190    tcp_header_t *tcp;
191    ip_header_t  *ip;
192    unsigned short cksum;
193
194    ack_pkt.buf = ack_buf;
195    ack_pkt.bufsize = sizeof(ack_buf);
196    ack_pkt.ip_hdr = ip = (ip_header_t *)ack_buf;
197    ack_pkt.tcp_hdr = tcp = (tcp_header_t *)(ip + 1);
198    ack_pkt.pkt_bytes = sizeof(tcp_header_t);
199
200    /* tcp header */
201    tcp->hdr_len = 5;
202    tcp->reserved = 0;
203    tcp->seqnum = htonl(s->seq);
204    tcp->acknum = htonl(s->ack);
205    tcp->checksum = 0;
206
207    tcp->src_port = htons(s->our_port);
208    tcp->dest_port = htons(s->his_port);
209    tcp->flags = TCP_FLAG_ACK;
210
211    tcp->window = htons(MAX_TCP_DATA);
212    tcp->urgent = 0;
213
214    /* fill in some pseudo-header fields */
215    memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
216    memcpy(ip->destination, s->his_addr.ip_addr, sizeof(ip_addr_t));
217    ip->protocol = IP_PROTO_TCP;
218
219    /* another pseudo-header field */
220    ip->length = htons(sizeof(tcp_header_t));
221
222    /* compute tcp checksum */
223    cksum = __sum((word *)tcp, sizeof(*tcp), __pseudo_sum(ip));
224    tcp->checksum = htons(cksum);
225
226    __ip_send(&ack_pkt, IP_PROTO_TCP, &s->his_addr);
227}
228
229
230/*
231 * Send a reset for a bogus incoming segment.
232 */
233static void
234send_reset(pktbuf_t *pkt, ip_route_t *r)
235{
236    ip_header_t   *ip = pkt->ip_hdr;
237    tcp_header_t  *tcp = pkt->tcp_hdr;
238    dword         seq, ack;
239    word          src, dest;
240    word          cksum;
241
242    seq = ntohl(tcp->acknum);
243    ack = ntohl(tcp->seqnum);
244    src = ntohs(tcp->dest_port);
245    dest = ntohs(tcp->src_port);
246
247    tcp = (tcp_header_t *)(ip + 1);
248    pkt->pkt_bytes = sizeof(tcp_header_t);
249   
250    /* tcp header */
251    tcp->hdr_len = 5;
252    tcp->reserved = 0;
253    tcp->seqnum = htonl(seq);
254    tcp->acknum = htonl(ack);
255    tcp->window = htons(1024);
256    tcp->urgent = 0;
257    tcp->checksum = 0;
258    tcp->src_port = htons(src);
259    tcp->dest_port = htons(dest);
260    tcp->flags = TCP_FLAG_RST | TCP_FLAG_ACK;
261
262    /* fill in some pseudo-header fields */
263    memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
264    memcpy(ip->destination, r->ip_addr, sizeof(ip_addr_t));
265    ip->protocol = IP_PROTO_TCP;
266    ip->length = htons(pkt->pkt_bytes);
267
268    /* compute tcp checksum */
269    cksum = __sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip));
270    tcp->checksum = htons(cksum);
271
272    __ip_send(pkt, IP_PROTO_TCP, r);
273}
274
275
276
277/*
278 * Remove given socket from socket list.
279 */
280static void
281unlink_socket(tcp_socket_t *s)
282{
283    tcp_socket_t *prev, *tp;
284
285    for (prev = NULL, tp = tcp_list; tp; prev = tp, tp = tp->next)
286        if (tp == s) {
287            BSPLOG(bsp_log("unlink tcp socket.\n"));
288            if (prev)
289                prev->next = s->next;
290            else
291                tcp_list = s->next;
292        }
293}
294
295/*
296 * Retransmit last packet.
297 */
298static void
299do_retrans(void *p)
300{
301    BSPLOG(bsp_log("tcp do_retrans.\n"));
302    tcp_send((tcp_socket_t *)p, 0, 1);
303}
304
305
306static void
307do_close(void *p)
308{
309    BSPLOG(bsp_log("tcp do_close.\n"));
310    /* close connection */
311    ((tcp_socket_t *)p)->state = _CLOSED;
312    unlink_socket(p);
313}
314
315
316static void
317free_rxlist(tcp_socket_t *s)
318{
319    pktbuf_t *p;
320
321    BSPLOG(bsp_log("tcp free_rxlist.\n"));
322
323    while ((p = s->rxlist) != NULL) {
324        s->rxlist = p->next;
325        __pktbuf_free(p);
326    }
327}
328
329
330/*
331 * Handle a conection reset.
332 */
333static void
334do_reset(tcp_socket_t *s)
335{
336    /* close connection */
337    s->state = _CLOSED;
338    __timer_cancel(&s->timer);
339    free_rxlist(s);
340    unlink_socket(s);
341}
342
343
344/*
345 * Extract data from incoming tcp segment.
346 * Returns true if packet is queued on rxlist, false otherwise.
347 */
348static int
349handle_data(tcp_socket_t *s, pktbuf_t *pkt)
350{
351    tcp_header_t  *tcp = pkt->tcp_hdr;
352    unsigned int  diff, seq;
353    int           data_len;
354    char          *data_ptr;
355    pktbuf_t      *p;
356
357    data_len = pkt->pkt_bytes - (tcp->hdr_len << 2);
358    data_ptr = ((char *)tcp)  + (tcp->hdr_len << 2);
359
360    seq = ntohl(tcp->seqnum);
361
362    BSPLOG(bsp_log("tcp data: seq[%x] len[%d].\n", seq, data_len));
363
364    if (SEQ_LE(seq, s->ack)) {
365        /*
366         * Figure difference between which byte we're expecting and which byte
367         * is sent first. Adjust data length and data pointer accordingly.
368         */
369        diff = s->ack - seq;
370        data_len -= diff;
371        data_ptr += diff;
372
373        if (data_len > 0) {
374            /* queue the new data */
375            s->ack += data_len;
376            pkt->next = NULL;
377            if ((p = s->rxlist) != NULL) {
378                while (p->next)
379                    p = p->next;
380                p->next = pkt;
381                BSPLOG(bsp_log("tcp data: Add pkt[%x] len[%d].\n",
382                               pkt, data_len));
383            } else {
384                s->rxlist = pkt;
385                s->rxcnt = data_len;
386                s->rxptr = data_ptr;
387                BSPLOG(bsp_log("tcp data: pkt[%x] len[%d].\n",
388                               pkt, data_len));
389            }
390            return 1;
391        }
392    }
393    return 0;
394}
395
396
397static void
398handle_ack(tcp_socket_t *s, pktbuf_t *pkt)
399{
400    tcp_header_t *tcp = pkt->tcp_hdr;
401    dword        ack;
402    int          advance;
403    char         *dp;
404
405    /* process ack value in packet */
406    ack = ntohl(tcp->acknum);
407
408    BSPLOG(bsp_log("Rcvd tcp ACK %x\n", ack));
409
410    if (SEQ_GT(ack, s->seq)) {
411        __timer_cancel(&s->timer);
412        advance = ack - s->seq;
413        if (advance > s->data_bytes)
414            advance = s->data_bytes;
415
416        BSPLOG(bsp_log("seq advance %d", advance));
417
418        if (advance > 0) {
419            s->seq += advance;
420            s->data_bytes -= advance;
421            if (s->data_bytes) {
422                /* other end ack'd only part of the pkt */
423                BSPLOG(bsp_log(" %d bytes left", s->data_bytes));
424                dp = (char *)(s->pkt.tcp_hdr + 1);
425                memcpy(dp, dp + advance, s->data_bytes);
426            }
427        }
428    }
429    BSPLOG(bsp_log("\n"));
430}
431
432
433/*
434 * Handle incoming TCP packets.
435 */
436void
437__tcp_handler(pktbuf_t *pkt, ip_route_t *r)
438{
439    tcp_header_t *tcp = pkt->tcp_hdr;
440    ip_header_t  *ip = pkt->ip_hdr;
441    tcp_socket_t *prev,*s;
442    dword        ack;
443    int          queued = 0;
444
445    /* set length for pseudo sum calculation */
446    ip->length = htons(pkt->pkt_bytes);
447
448    if (__sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip)) == 0) {
449        for (prev = NULL, s = tcp_list; s; prev = s, s = s->next) {
450            if (s->our_port == ntohs(tcp->dest_port)) {
451                if (s->his_port == 0)
452                    break;
453                if (s->his_port == ntohs(tcp->src_port) &&
454                    !memcmp(r->ip_addr, s->his_addr.ip_addr, sizeof(ip_addr_t)))
455                    break;
456            }
457        }
458
459        if (s) {
460            /* found the socket this packet belongs to */
461           
462            /* refresh his ethernet address */
463            memcpy(s->his_addr.enet_addr, r->enet_addr, sizeof(enet_addr_t));
464
465            if (s->state != _SYN_RCVD && tcp->flags & TCP_FLAG_RST) {
466                BSPLOG(bsp_log("TCP_FLAG_RST rcvd\n"));
467                do_reset(s);
468                __pktbuf_free(pkt);
469                return;
470            }
471
472            switch (s->state) {
473
474              case _SYN_SENT:
475                /* active open not supported */
476                  if (tcp->flags != (TCP_FLAG_SYN | TCP_FLAG_ACK)) {
477                      do_reset(s);
478                      __pktbuf_free(pkt);
479                      return;
480                  }
481                  s->state = _ESTABLISHED;
482                  s->ack = ntohl(tcp->seqnum) + 1;
483                  s->seq = ntohl(tcp->acknum);
484                  __timer_cancel(&s->timer);
485                  send_ack(s);
486                break;
487
488              case _LISTEN:
489                if (tcp->flags & TCP_FLAG_SYN) {
490                    s->state = _SYN_RCVD;
491                    s->ack = ntohl(tcp->seqnum) + 1;
492                    s->his_port = ntohs(tcp->src_port);
493                    memcpy(s->his_addr.ip_addr, r->ip_addr, sizeof(ip_addr_t));
494                    s->data_bytes = 0;
495
496                    BSPLOG(bsp_log("SYN from %d.%d.%d.%d:%d (seq %x)\n",
497                               s->his_addr.ip_addr[0],s->his_addr.ip_addr[1],
498                               s->his_addr.ip_addr[2],s->his_addr.ip_addr[3],
499                               s->his_port, ntohl(tcp->seqnum)));
500
501                    tcp_send(s, TCP_FLAG_SYN | TCP_FLAG_ACK, 0);
502                }
503                else
504                    send_reset(pkt, r);
505                break;
506
507              case _SYN_RCVD:
508                BSPLOG(bsp_log("_SYN_RCVD timer cancel.\n"));
509                __timer_cancel(&s->timer);
510
511                /* go back to _LISTEN state if reset */
512                if (tcp->flags & TCP_FLAG_RST) {
513                    s->state = _LISTEN;
514
515                    BSPLOG(bsp_log("_SYN_RCVD --> _LISTEN\n"));
516
517                } else if (tcp->flags & TCP_FLAG_SYN) {
518                    /* apparently our SYN/ACK was lost? */
519                    tcp_send(s, 0, 1);
520
521                    BSPLOG(bsp_log("retransmitting SYN/ACK\n"));
522
523                } else if ((tcp->flags & TCP_FLAG_ACK) &&
524                           ntohl(tcp->acknum) == (s->seq + 1)) {
525                    /* we've established the connection */
526                    s->state = _ESTABLISHED;
527                    s->seq++;
528
529                    BSPLOG(bsp_log("ACK received - connection established\n"));
530                }
531                break;
532
533              case _ESTABLISHED:
534              case _CLOSE_WAIT:
535                ack = s->ack;  /* save original ack */
536                if (tcp->flags & TCP_FLAG_ACK)
537                    handle_ack(s, pkt);
538
539                queued = handle_data(s, pkt);
540
541                if ((tcp->flags & TCP_FLAG_FIN) &&
542                    ntohl(tcp->seqnum) == s->ack) {
543
544                    BSPLOG(bsp_log("FIN received - going to _CLOSE_WAIT\n"));
545
546                    s->ack++;
547                    s->state = _CLOSE_WAIT;
548                }
549                /*
550                 * Send an ack if neccessary.
551                 */
552                if (s->ack != ack || pkt->pkt_bytes > (tcp->hdr_len << 2))
553                    send_ack(s);
554                break;
555
556              case _LAST_ACK:
557                if (tcp->flags & TCP_FLAG_ACK) {
558                    handle_ack(s, pkt);
559                    if (ntohl(tcp->acknum) == (s->seq + 1)) {
560                        BSPLOG(bsp_log("_LAST_ACK --> _CLOSED\n"));
561                        s->state = _CLOSED;
562                        unlink_socket(s);
563                    }
564                }
565                break;
566
567              case _FIN_WAIT_1:
568                if (tcp->flags & TCP_FLAG_ACK) {
569                    handle_ack(s, pkt);
570                    if (ntohl(tcp->acknum) == (s->seq + 1)) {
571                        /* got ACK for FIN packet */
572                        s->seq++;
573                        if (tcp->flags & TCP_FLAG_FIN) {
574                            BSPLOG(bsp_log("_FIN_WAIT_1 --> _TIME_WAIT\n"));
575                            s->ack++;
576                            s->state = _TIME_WAIT;
577                            send_ack(s);
578                        } else {
579                            s->state = _FIN_WAIT_2;
580                            BSPLOG(bsp_log("_FIN_WAIT_1 --> _FIN_WAIT_2\n"));
581                        }
582                        break; /* All done for now */
583                    }
584                }
585                /* At this point, no ACK for FIN has been seen, so check for
586                   simultaneous close */
587                if (tcp->flags & TCP_FLAG_FIN) {
588                    BSPLOG(bsp_log("_FIN_WAIT_1 --> _CLOSING\n"));
589                    __timer_cancel(&s->timer);
590                    s->ack++;
591                    s->state = _CLOSING;
592                    /* FIN is resent so the timeout and retry for this packet
593                       will also take care of timeout and resend of the
594                       previously sent FIN (which got us to FIN_WAIT_1). While
595                       not technically correct, resending FIN only causes a
596                       duplicate FIN (same sequence number) which should be
597                       ignored by the other end. */
598                    tcp_send(s, TCP_FLAG_FIN | TCP_FLAG_ACK, 0);
599                }
600                break;
601
602              case _FIN_WAIT_2:
603                queued = handle_data(s, pkt);
604                if (tcp->flags & TCP_FLAG_FIN) {
605                    BSPLOG(bsp_log("_FIN_WAIT_2 --> _TIME_WAIT\n"));
606                    s->ack++;
607                    s->state = _TIME_WAIT;
608                    send_ack(s);
609                }
610                break;
611
612              case _CLOSING:
613                if (tcp->flags & TCP_FLAG_ACK) {
614                    handle_ack(s, pkt);
615                    if (ntohl(tcp->acknum) == (s->seq + 1)) {
616                        /* got ACK for FIN packet */
617                        BSPLOG(bsp_log("_CLOSING --> _TIME_WAIT\n"));
618                        __timer_cancel(&s->timer);
619                        s->state = _TIME_WAIT;
620                    }
621                }
622                break;
623
624              case _TIME_WAIT:
625                BSPLOG(bsp_log("_TIME_WAIT resend.\n"));
626                if (tcp->flags & TCP_FLAG_FIN)
627                    tcp_send(s, 0, 1); /* just resend ack */
628                break;
629            }
630        } else {
631            BSPLOG(bsp_log("Unexpected segment from: %d.%d.%d.%d:%d\n",
632                           r->ip_addr[0], r->ip_addr[1], r->ip_addr[3],
633                           r->ip_addr[4], ntohs(tcp->src_port)));
634            send_reset(pkt, r);
635        }
636    }
637    if (!queued)
638        __pktbuf_free(pkt);
639}
640
641
642void
643__tcp_poll(void)
644{
645    __enet_poll();
646    MS_TICKS_DELAY();
647    __timer_poll();
648}
649
650
651int
652__tcp_listen(tcp_socket_t *s, word port)
653{
654    BSPLOG(bsp_log("tcp_listen: s[%p] port[%x]\n", s, port));
655
656    memset(s, 0, sizeof(tcp_socket_t));
657    s->state    = _LISTEN;
658    s->our_port = port;
659    s->pkt.buf = (word *)s->pktbuf;
660    s->pkt.bufsize = ETH_MAX_PKTLEN;
661    s->pkt.ip_hdr  = (ip_header_t *)s->pkt.buf;
662    s->pkt.tcp_hdr = (tcp_header_t *)(s->pkt.ip_hdr + 1);
663
664    s->next = tcp_list;
665
666#if 0
667    /* limit to one open socket at a time */
668    if (s->next) {
669        BSPLOG(bsp_log("tcp_listen: recursion error\n"));
670        BSPLOG(while(1));
671    }
672#endif
673   
674    tcp_list = s;
675
676    return 0;
677}
678
679/*
680 * SO_REUSEADDR, no 2MSL.
681 */
682void
683__tcp_so_reuseaddr(tcp_socket_t *s)
684{
685//    BSPLOG(bsp_log("__tcp_so_reuseaddr.\n"));
686    s->reuse = 0x01;
687}
688
689/*
690 * Block while waiting for all data to be transmitted.
691 */
692void
693__tcp_drain(tcp_socket_t *s)
694{
695//    BSPLOG(bsp_log("__tcp_drain.\n"));
696    while (s->state != _CLOSED && s->data_bytes)
697        __tcp_poll();
698//    BSPLOG(bsp_log("__tcp_drain done.\n"));
699}
700
701
702/*
703 * Close the tcp connection.
704 */
705static void
706do_abort(void *s)
707{
708    BSPLOG(bsp_log("do_abort: send RST\n"));
709    tcp_send((tcp_socket_t *)s, TCP_FLAG_ACK | TCP_FLAG_RST, 0);
710    __timer_cancel(&abort_timer);
711    ((tcp_socket_t *)s)->state = _CLOSED;
712    free_rxlist((tcp_socket_t *)s);
713    unlink_socket((tcp_socket_t *)s);
714}
715
716void
717__tcp_abort(tcp_socket_t *s, unsigned long delay)
718{
719  __timer_set(&abort_timer, delay, do_abort, s);
720}
721
722/*
723 * Close the tcp connection.
724 */
725void
726__tcp_close(tcp_socket_t *s)
727{
728    __tcp_drain(s);
729    if (s->state == _ESTABLISHED || s->state == _SYN_RCVD) {
730        BSPLOG(bsp_log("__tcp_close: going to _FIN_WAIT_1\n"));
731        s->state = _FIN_WAIT_1;
732        tcp_send(s, TCP_FLAG_ACK | TCP_FLAG_FIN, 0);
733    } else if (s->state == _CLOSE_WAIT) {
734
735        BSPLOG(bsp_log("__tcp_close: going to _LAST_ACK\n"));
736
737        s->state = _LAST_ACK;
738        tcp_send(s, TCP_FLAG_ACK | TCP_FLAG_FIN, 0);
739    }
740    free_rxlist(s);
741}
742
743
744/*
745 * Wait for connection to be fully closed.
746 */
747void
748__tcp_close_wait(tcp_socket_t *s)
749{
750    BSPLOG(bsp_log("__tcp_close_wait.\n"));
751    while (s->state != _CLOSED)
752        __tcp_poll();
753    BSPLOG(bsp_log("__tcp_close_wait done.\n"));
754}
755
756
757/*
758 * Read up to 'len' bytes without blocking.
759 */
760int
761__tcp_read(tcp_socket_t *s, char *buf, int len)
762{
763    int          nread;
764    pktbuf_t     *pkt;
765    tcp_header_t *tcp;
766
767    if (len <= 0 || s->rxcnt == 0)
768        return 0;
769
770    if (s->state != _ESTABLISHED && s->rxcnt == 0)
771        return -1;
772
773    nread = 0;
774    while (len) {
775        if (len < s->rxcnt) {
776            memcpy(buf, s->rxptr, len);
777            BSPLOG(bsp_log("tcp_read: read %d bytes.\n", len));
778            s->rxptr += len;
779            s->rxcnt -= len;
780            nread    += len;
781
782            BSPLOG(bsp_log("tcp_read: %d bytes left in rxlist head.\n",
783                       s->rxcnt));
784
785            break;
786        } else {
787            memcpy(buf, s->rxptr, s->rxcnt);
788            BSPLOG(bsp_log("tcp_read: read %d bytes. pkt[%x] freed.\n",
789                           s->rxcnt, s->rxlist));
790            nread += s->rxcnt;
791            buf   += s->rxcnt;
792            len   -= s->rxcnt;
793
794            /* setup for next packet in list */
795            pkt = s->rxlist;
796            s->rxlist = pkt->next;
797            __pktbuf_free(pkt);
798
799            if ((pkt = s->rxlist) != NULL) {
800                tcp = pkt->tcp_hdr;
801                s->rxcnt = pkt->pkt_bytes - (tcp->hdr_len << 2);
802                s->rxptr = ((char *)tcp)  + (tcp->hdr_len << 2);
803
804                BSPLOG(bsp_log("tcp_read: next pkt[%x] has %d bytes.\n",
805                           s->rxlist, s->rxcnt));
806            } else {
807
808                BSPLOG(bsp_log("tcp_read: no more data.\n"));
809
810                s->rxcnt = 0;
811                break;
812            }
813        }
814    }
815    return nread;
816}
817
818
819/*
820 * Write up to 'len' bytes without blocking
821 */
822int
823__tcp_write(tcp_socket_t *s, char *buf, int len)
824{
825    tcp_header_t *tcp = s->pkt.tcp_hdr;
826
827    if (len <= 0)
828        return 0;
829
830    if (s->state != _ESTABLISHED && s->state != _CLOSE_WAIT)
831        return -1;
832
833    if (s->data_bytes)
834        return 0;
835
836    if (len > MAX_TCP_DATA)
837        len = MAX_TCP_DATA;
838
839    memcpy(tcp + 1, buf, len);
840    s->data_bytes = len;
841
842    tcp_send(s, TCP_FLAG_ACK, 0);
843
844    return len;
845}
846
847/*
848 * Write 'len' bytes from 'buf', blocking until sent.
849 * If connection collapses, return -1
850 */
851int
852__tcp_write_block(tcp_socket_t *s, char *buf, int len)
853{
854    int total = 0;
855    int n;
856
857    while (len) {
858        if (s->state == _CLOSE_WAIT) {
859            // This connection is tring to close
860            // This connection is breaking
861            if (s->data_bytes == 0 && s->rxcnt == 0)
862                __tcp_close(s);
863        }
864        if (s->state == _CLOSED) {
865            // The connection is gone!
866            return -1;
867        }
868        n = __tcp_write(s, buf, len);
869        if (n > 0) {
870            len -= n;
871            buf += n;
872        }
873        __tcp_poll();
874    }
875    __tcp_drain(s);
876    return total;
877}
878
879/*
880 * Establish a new [outgoing] connection, with a timeout.
881 */
882int 
883__tcp_open(tcp_socket_t *s, struct sockaddr_in *host, 
884           word port, int timeout, int *err)
885{
886    // Fill in socket details
887    memset(s, 0, sizeof(tcp_socket_t));
888    s->state = _SYN_SENT;
889    s->our_port = port;
890    s->his_port = host->sin_port;
891    s->pkt.buf = (word *)s->pktbuf;
892    s->pkt.bufsize = ETH_MAX_PKTLEN;
893    s->pkt.ip_hdr  = (ip_header_t *)s->pkt.buf;
894    s->pkt.tcp_hdr = (tcp_header_t *)(s->pkt.ip_hdr + 1);
895    s->seq = (port << 16) | 0xDE77;
896    s->ack = 0;
897    if (__arp_lookup((ip_addr_t *)&host->sin_addr, &s->his_addr) < 0) {
898        diag_printf("%s: Can't find address of server\n", __FUNCTION__);
899        return -1;
900    }
901    s->next = tcp_list;
902    tcp_list = s;
903
904    // Send off the SYN packet to open the connection
905    tcp_send(s, TCP_FLAG_SYN, 0);
906    // Wait for connection to establish
907    while (s->state != _ESTABLISHED) {
908        if (s->state == _CLOSED) {
909            diag_printf("TCP open - host closed connection\n");
910            return -1;
911        }
912        if (--timeout <= 0) {
913            diag_printf("TCP open - connection timed out\n");
914            return -1;
915        }
916        MS_TICKS_DELAY();
917        __tcp_poll();
918    }
919    return 0;
920}
921
922
Note: See TracBrowser for help on using the repository browser.