//==========================================================================
|
//==========================================================================
|
//
|
//
|
// net/tcp.c
|
// net/tcp.c
|
//
|
//
|
// Stand-alone TCP networking support for RedBoot
|
// Stand-alone TCP networking support for RedBoot
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
// -------------------------------------------
|
// -------------------------------------------
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
//
|
//
|
// eCos is free software; you can redistribute it and/or modify it under
|
// eCos is free software; you can redistribute it and/or modify it under
|
// the terms of the GNU General Public License as published by the Free
|
// the terms of the GNU General Public License as published by the Free
|
// Software Foundation; either version 2 or (at your option) any later version.
|
// Software Foundation; either version 2 or (at your option) any later version.
|
//
|
//
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// for more details.
|
// for more details.
|
//
|
//
|
// You should have received a copy of the GNU General Public License along
|
// You should have received a copy of the GNU General Public License along
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
//
|
//
|
// As a special exception, if other files instantiate templates or use macros
|
// As a special exception, if other files instantiate templates or use macros
|
// or inline functions from this file, or you compile this file and link it
|
// or inline functions from this file, or you compile this file and link it
|
// with other works to produce a work based on this file, this file does not
|
// with other works to produce a work based on this file, this file does not
|
// by itself cause the resulting work to be covered by the GNU General Public
|
// by itself cause the resulting work to be covered by the GNU General Public
|
// License. However the source code for this file must still be made available
|
// License. However the source code for this file must still be made available
|
// in accordance with section (3) of the GNU General Public License.
|
// in accordance with section (3) of the GNU General Public License.
|
//
|
//
|
// This exception does not invalidate any other reasons why a work based on
|
// This exception does not invalidate any other reasons why a work based on
|
// this file might be covered by the GNU General Public License.
|
// this file might be covered by the GNU General Public License.
|
//
|
//
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// -------------------------------------------
|
// -------------------------------------------
|
//####ECOSGPLCOPYRIGHTEND####
|
//####ECOSGPLCOPYRIGHTEND####
|
//==========================================================================
|
//==========================================================================
|
//#####DESCRIPTIONBEGIN####
|
//#####DESCRIPTIONBEGIN####
|
//
|
//
|
// Author(s): gthomas
|
// Author(s): gthomas
|
// Contributors: gthomas
|
// Contributors: gthomas
|
// Date: 2000-07-14
|
// Date: 2000-07-14
|
// Purpose:
|
// Purpose:
|
// Description:
|
// Description:
|
//
|
//
|
// This code is part of RedBoot (tm).
|
// This code is part of RedBoot (tm).
|
//
|
//
|
//####DESCRIPTIONEND####
|
//####DESCRIPTIONEND####
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
|
|
#include <net/net.h>
|
#include <net/net.h>
|
|
|
#define MAX_TCP_SEGMENT (ETH_MAX_PKTLEN - (sizeof(eth_header_t) + sizeof(ip_header_t)))
|
#define MAX_TCP_SEGMENT (ETH_MAX_PKTLEN - (sizeof(eth_header_t) + sizeof(ip_header_t)))
|
#define MAX_TCP_DATA (MAX_TCP_SEGMENT - sizeof(tcp_header_t))
|
#define MAX_TCP_DATA (MAX_TCP_SEGMENT - sizeof(tcp_header_t))
|
|
|
|
|
/* sequence number comparison macros */
|
/* sequence number comparison macros */
|
#define SEQ_LT(a,b) ((int)((a)-(b)) < 0)
|
#define SEQ_LT(a,b) ((int)((a)-(b)) < 0)
|
#define SEQ_LE(a,b) ((int)((a)-(b)) <= 0)
|
#define SEQ_LE(a,b) ((int)((a)-(b)) <= 0)
|
#define SEQ_GT(a,b) ((int)((a)-(b)) > 0)
|
#define SEQ_GT(a,b) ((int)((a)-(b)) > 0)
|
#define SEQ_GE(a,b) ((int)((a)-(b)) >= 0)
|
#define SEQ_GE(a,b) ((int)((a)-(b)) >= 0)
|
|
|
/* Set a timer which will send an RST and abort a connection. */
|
/* Set a timer which will send an RST and abort a connection. */
|
static timer_t abort_timer;
|
static timer_t abort_timer;
|
|
|
static void do_retrans(void *p);
|
static void do_retrans(void *p);
|
static void do_close(void *p);
|
static void do_close(void *p);
|
|
|
#ifdef BSP_LOG
|
#ifdef BSP_LOG
|
static char *
|
static char *
|
flags_to_str(octet f)
|
flags_to_str(octet f)
|
{
|
{
|
static char str[7], *p;
|
static char str[7], *p;
|
|
|
p = str;
|
p = str;
|
|
|
if (f & TCP_FLAG_FIN)
|
if (f & TCP_FLAG_FIN)
|
*p++ = 'F';
|
*p++ = 'F';
|
if (f & TCP_FLAG_SYN)
|
if (f & TCP_FLAG_SYN)
|
*p++ = 'S';
|
*p++ = 'S';
|
if (f & TCP_FLAG_RST)
|
if (f & TCP_FLAG_RST)
|
*p++ = 'R';
|
*p++ = 'R';
|
if (f & TCP_FLAG_PSH)
|
if (f & TCP_FLAG_PSH)
|
*p++ = 'P';
|
*p++ = 'P';
|
if (f & TCP_FLAG_ACK)
|
if (f & TCP_FLAG_ACK)
|
*p++ = 'A';
|
*p++ = 'A';
|
if (f & TCP_FLAG_URG)
|
if (f & TCP_FLAG_URG)
|
*p++ = 'U';
|
*p++ = 'U';
|
*p = '\0';
|
*p = '\0';
|
return str;
|
return str;
|
}
|
}
|
#endif
|
#endif
|
|
|
/*
|
/*
|
* A major assumption is that only a very small number of sockets will
|
* A major assumption is that only a very small number of sockets will
|
* active, so a simple linear search of those sockets is acceptible.
|
* active, so a simple linear search of those sockets is acceptible.
|
*/
|
*/
|
static tcp_socket_t *tcp_list;
|
static tcp_socket_t *tcp_list;
|
|
|
/*
|
/*
|
* Format and send an outgoing segment.
|
* Format and send an outgoing segment.
|
*/
|
*/
|
static void
|
static void
|
tcp_send(tcp_socket_t *s, int flags, int resend)
|
tcp_send(tcp_socket_t *s, int flags, int resend)
|
{
|
{
|
tcp_header_t *tcp;
|
tcp_header_t *tcp;
|
ip_header_t *ip;
|
ip_header_t *ip;
|
pktbuf_t *pkt = &s->pkt;
|
pktbuf_t *pkt = &s->pkt;
|
unsigned short cksum;
|
unsigned short cksum;
|
dword tcp_magic;
|
dword tcp_magic;
|
int tcp_magic_size = sizeof(tcp_magic);
|
int tcp_magic_size = sizeof(tcp_magic);
|
|
|
ip = pkt->ip_hdr;
|
ip = pkt->ip_hdr;
|
tcp = pkt->tcp_hdr;
|
tcp = pkt->tcp_hdr;
|
|
|
if (flags & TCP_FLAG_SYN) {
|
if (flags & TCP_FLAG_SYN) {
|
/* If SYN, assume no data and send MSS option in tcp header */
|
/* If SYN, assume no data and send MSS option in tcp header */
|
pkt->pkt_bytes = sizeof(tcp_header_t) + 4;
|
pkt->pkt_bytes = sizeof(tcp_header_t) + 4;
|
tcp->hdr_len = 6;
|
tcp->hdr_len = 6;
|
tcp_magic = htonl(0x02040000 | MAX_TCP_DATA);
|
tcp_magic = htonl(0x02040000 | MAX_TCP_DATA);
|
memcpy((unsigned char *)(tcp+1), &tcp_magic, tcp_magic_size);
|
memcpy((unsigned char *)(tcp+1), &tcp_magic, tcp_magic_size);
|
s->data_bytes = 0;
|
s->data_bytes = 0;
|
} else {
|
} else {
|
pkt->pkt_bytes = s->data_bytes + sizeof(tcp_header_t);
|
pkt->pkt_bytes = s->data_bytes + sizeof(tcp_header_t);
|
tcp->hdr_len = 5;
|
tcp->hdr_len = 5;
|
}
|
}
|
|
|
/* tcp header */
|
/* tcp header */
|
tcp->reserved = 0;
|
tcp->reserved = 0;
|
tcp->seqnum = htonl(s->seq);
|
tcp->seqnum = htonl(s->seq);
|
tcp->acknum = htonl(s->ack);
|
tcp->acknum = htonl(s->ack);
|
tcp->checksum = 0;
|
tcp->checksum = 0;
|
|
|
if (!resend) {
|
if (!resend) {
|
tcp->src_port = htons(s->our_port);
|
tcp->src_port = htons(s->our_port);
|
tcp->dest_port = htons(s->his_port);
|
tcp->dest_port = htons(s->his_port);
|
tcp->flags = flags;
|
tcp->flags = flags;
|
/* always set PUSH flag if sending data */
|
/* always set PUSH flag if sending data */
|
if (s->data_bytes)
|
if (s->data_bytes)
|
tcp->flags |= TCP_FLAG_PSH;
|
tcp->flags |= TCP_FLAG_PSH;
|
tcp->window = htons(MAX_TCP_DATA);
|
tcp->window = htons(MAX_TCP_DATA);
|
tcp->urgent = 0;
|
tcp->urgent = 0;
|
|
|
/* fill in some pseudo-header fields */
|
/* fill in some pseudo-header fields */
|
memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
|
memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
|
memcpy(ip->destination, s->his_addr.ip_addr, sizeof(ip_addr_t));
|
memcpy(ip->destination, s->his_addr.ip_addr, sizeof(ip_addr_t));
|
ip->protocol = IP_PROTO_TCP;
|
ip->protocol = IP_PROTO_TCP;
|
}
|
}
|
|
|
/* another pseudo-header field */
|
/* another pseudo-header field */
|
ip->length = htons(pkt->pkt_bytes);
|
ip->length = htons(pkt->pkt_bytes);
|
|
|
/* compute tcp checksum */
|
/* compute tcp checksum */
|
cksum = __sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip));
|
cksum = __sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip));
|
tcp->checksum = htons(cksum);
|
tcp->checksum = htons(cksum);
|
|
|
__ip_send(pkt, IP_PROTO_TCP, &s->his_addr);
|
__ip_send(pkt, IP_PROTO_TCP, &s->his_addr);
|
|
|
BSPLOG(bsp_log("tcp_send: state[%d] flags[%s] ack[%x] data[%d].\n",
|
BSPLOG(bsp_log("tcp_send: state[%d] flags[%s] ack[%x] data[%d].\n",
|
s->state, flags_to_str(tcp->flags), s->ack, s->data_bytes));
|
s->state, flags_to_str(tcp->flags), s->ack, s->data_bytes));
|
|
|
if (s->state == _TIME_WAIT) {
|
if (s->state == _TIME_WAIT) {
|
// If 'reuse' is set on socket, close after 1 second, otherwise 2 minutes
|
// If 'reuse' is set on socket, close after 1 second, otherwise 2 minutes
|
__timer_set(&s->timer, s->reuse ? 1000 : 120000, do_close, s);
|
__timer_set(&s->timer, s->reuse ? 1000 : 120000, do_close, s);
|
}
|
}
|
else if ((tcp->flags & (TCP_FLAG_FIN | TCP_FLAG_SYN)) || s->data_bytes)
|
else if ((tcp->flags & (TCP_FLAG_FIN | TCP_FLAG_SYN)) || s->data_bytes)
|
__timer_set(&s->timer, 1000, do_retrans, s);
|
__timer_set(&s->timer, 1000, do_retrans, s);
|
}
|
}
|
|
|
static pktbuf_t ack_pkt;
|
static pktbuf_t ack_pkt;
|
static word ack_buf[ETH_MIN_PKTLEN/sizeof(word)];
|
static word ack_buf[ETH_MIN_PKTLEN/sizeof(word)];
|
|
|
/*
|
/*
|
* Send an ack.
|
* Send an ack.
|
*/
|
*/
|
static void
|
static void
|
send_ack(tcp_socket_t *s)
|
send_ack(tcp_socket_t *s)
|
{
|
{
|
tcp_header_t *tcp;
|
tcp_header_t *tcp;
|
ip_header_t *ip;
|
ip_header_t *ip;
|
unsigned short cksum;
|
unsigned short cksum;
|
|
|
ack_pkt.buf = ack_buf;
|
ack_pkt.buf = ack_buf;
|
ack_pkt.bufsize = sizeof(ack_buf);
|
ack_pkt.bufsize = sizeof(ack_buf);
|
ack_pkt.ip_hdr = ip = (ip_header_t *)ack_buf;
|
ack_pkt.ip_hdr = ip = (ip_header_t *)ack_buf;
|
ack_pkt.tcp_hdr = tcp = (tcp_header_t *)(ip + 1);
|
ack_pkt.tcp_hdr = tcp = (tcp_header_t *)(ip + 1);
|
ack_pkt.pkt_bytes = sizeof(tcp_header_t);
|
ack_pkt.pkt_bytes = sizeof(tcp_header_t);
|
|
|
/* tcp header */
|
/* tcp header */
|
tcp->hdr_len = 5;
|
tcp->hdr_len = 5;
|
tcp->reserved = 0;
|
tcp->reserved = 0;
|
tcp->seqnum = htonl(s->seq);
|
tcp->seqnum = htonl(s->seq);
|
tcp->acknum = htonl(s->ack);
|
tcp->acknum = htonl(s->ack);
|
tcp->checksum = 0;
|
tcp->checksum = 0;
|
|
|
tcp->src_port = htons(s->our_port);
|
tcp->src_port = htons(s->our_port);
|
tcp->dest_port = htons(s->his_port);
|
tcp->dest_port = htons(s->his_port);
|
tcp->flags = TCP_FLAG_ACK;
|
tcp->flags = TCP_FLAG_ACK;
|
|
|
tcp->window = htons(MAX_TCP_DATA);
|
tcp->window = htons(MAX_TCP_DATA);
|
tcp->urgent = 0;
|
tcp->urgent = 0;
|
|
|
/* fill in some pseudo-header fields */
|
/* fill in some pseudo-header fields */
|
memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
|
memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
|
memcpy(ip->destination, s->his_addr.ip_addr, sizeof(ip_addr_t));
|
memcpy(ip->destination, s->his_addr.ip_addr, sizeof(ip_addr_t));
|
ip->protocol = IP_PROTO_TCP;
|
ip->protocol = IP_PROTO_TCP;
|
|
|
/* another pseudo-header field */
|
/* another pseudo-header field */
|
ip->length = htons(sizeof(tcp_header_t));
|
ip->length = htons(sizeof(tcp_header_t));
|
|
|
/* compute tcp checksum */
|
/* compute tcp checksum */
|
cksum = __sum((word *)tcp, sizeof(*tcp), __pseudo_sum(ip));
|
cksum = __sum((word *)tcp, sizeof(*tcp), __pseudo_sum(ip));
|
tcp->checksum = htons(cksum);
|
tcp->checksum = htons(cksum);
|
|
|
__ip_send(&ack_pkt, IP_PROTO_TCP, &s->his_addr);
|
__ip_send(&ack_pkt, IP_PROTO_TCP, &s->his_addr);
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Send a reset for a bogus incoming segment.
|
* Send a reset for a bogus incoming segment.
|
*/
|
*/
|
static void
|
static void
|
send_reset(pktbuf_t *pkt, ip_route_t *r)
|
send_reset(pktbuf_t *pkt, ip_route_t *r)
|
{
|
{
|
ip_header_t *ip = pkt->ip_hdr;
|
ip_header_t *ip = pkt->ip_hdr;
|
tcp_header_t *tcp = pkt->tcp_hdr;
|
tcp_header_t *tcp = pkt->tcp_hdr;
|
dword seq, ack;
|
dword seq, ack;
|
word src, dest;
|
word src, dest;
|
word cksum;
|
word cksum;
|
|
|
seq = ntohl(tcp->acknum);
|
seq = ntohl(tcp->acknum);
|
ack = ntohl(tcp->seqnum);
|
ack = ntohl(tcp->seqnum);
|
src = ntohs(tcp->dest_port);
|
src = ntohs(tcp->dest_port);
|
dest = ntohs(tcp->src_port);
|
dest = ntohs(tcp->src_port);
|
|
|
tcp = (tcp_header_t *)(ip + 1);
|
tcp = (tcp_header_t *)(ip + 1);
|
pkt->pkt_bytes = sizeof(tcp_header_t);
|
pkt->pkt_bytes = sizeof(tcp_header_t);
|
|
|
/* tcp header */
|
/* tcp header */
|
tcp->hdr_len = 5;
|
tcp->hdr_len = 5;
|
tcp->reserved = 0;
|
tcp->reserved = 0;
|
tcp->seqnum = htonl(seq);
|
tcp->seqnum = htonl(seq);
|
tcp->acknum = htonl(ack);
|
tcp->acknum = htonl(ack);
|
tcp->window = htons(1024);
|
tcp->window = htons(1024);
|
tcp->urgent = 0;
|
tcp->urgent = 0;
|
tcp->checksum = 0;
|
tcp->checksum = 0;
|
tcp->src_port = htons(src);
|
tcp->src_port = htons(src);
|
tcp->dest_port = htons(dest);
|
tcp->dest_port = htons(dest);
|
tcp->flags = TCP_FLAG_RST | TCP_FLAG_ACK;
|
tcp->flags = TCP_FLAG_RST | TCP_FLAG_ACK;
|
|
|
/* fill in some pseudo-header fields */
|
/* fill in some pseudo-header fields */
|
memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
|
memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
|
memcpy(ip->destination, r->ip_addr, sizeof(ip_addr_t));
|
memcpy(ip->destination, r->ip_addr, sizeof(ip_addr_t));
|
ip->protocol = IP_PROTO_TCP;
|
ip->protocol = IP_PROTO_TCP;
|
ip->length = htons(pkt->pkt_bytes);
|
ip->length = htons(pkt->pkt_bytes);
|
|
|
/* compute tcp checksum */
|
/* compute tcp checksum */
|
cksum = __sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip));
|
cksum = __sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip));
|
tcp->checksum = htons(cksum);
|
tcp->checksum = htons(cksum);
|
|
|
__ip_send(pkt, IP_PROTO_TCP, r);
|
__ip_send(pkt, IP_PROTO_TCP, r);
|
}
|
}
|
|
|
|
|
|
|
/*
|
/*
|
* Remove given socket from socket list.
|
* Remove given socket from socket list.
|
*/
|
*/
|
static void
|
static void
|
unlink_socket(tcp_socket_t *s)
|
unlink_socket(tcp_socket_t *s)
|
{
|
{
|
tcp_socket_t *prev, *tp;
|
tcp_socket_t *prev, *tp;
|
|
|
for (prev = NULL, tp = tcp_list; tp; prev = tp, tp = tp->next)
|
for (prev = NULL, tp = tcp_list; tp; prev = tp, tp = tp->next)
|
if (tp == s) {
|
if (tp == s) {
|
BSPLOG(bsp_log("unlink tcp socket.\n"));
|
BSPLOG(bsp_log("unlink tcp socket.\n"));
|
if (prev)
|
if (prev)
|
prev->next = s->next;
|
prev->next = s->next;
|
else
|
else
|
tcp_list = s->next;
|
tcp_list = s->next;
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* Retransmit last packet.
|
* Retransmit last packet.
|
*/
|
*/
|
static void
|
static void
|
do_retrans(void *p)
|
do_retrans(void *p)
|
{
|
{
|
BSPLOG(bsp_log("tcp do_retrans.\n"));
|
BSPLOG(bsp_log("tcp do_retrans.\n"));
|
tcp_send((tcp_socket_t *)p, 0, 1);
|
tcp_send((tcp_socket_t *)p, 0, 1);
|
}
|
}
|
|
|
|
|
static void
|
static void
|
do_close(void *p)
|
do_close(void *p)
|
{
|
{
|
BSPLOG(bsp_log("tcp do_close.\n"));
|
BSPLOG(bsp_log("tcp do_close.\n"));
|
/* close connection */
|
/* close connection */
|
((tcp_socket_t *)p)->state = _CLOSED;
|
((tcp_socket_t *)p)->state = _CLOSED;
|
unlink_socket(p);
|
unlink_socket(p);
|
}
|
}
|
|
|
|
|
static void
|
static void
|
free_rxlist(tcp_socket_t *s)
|
free_rxlist(tcp_socket_t *s)
|
{
|
{
|
pktbuf_t *p;
|
pktbuf_t *p;
|
|
|
BSPLOG(bsp_log("tcp free_rxlist.\n"));
|
BSPLOG(bsp_log("tcp free_rxlist.\n"));
|
|
|
while ((p = s->rxlist) != NULL) {
|
while ((p = s->rxlist) != NULL) {
|
s->rxlist = p->next;
|
s->rxlist = p->next;
|
__pktbuf_free(p);
|
__pktbuf_free(p);
|
}
|
}
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Handle a conection reset.
|
* Handle a conection reset.
|
*/
|
*/
|
static void
|
static void
|
do_reset(tcp_socket_t *s)
|
do_reset(tcp_socket_t *s)
|
{
|
{
|
/* close connection */
|
/* close connection */
|
s->state = _CLOSED;
|
s->state = _CLOSED;
|
__timer_cancel(&s->timer);
|
__timer_cancel(&s->timer);
|
free_rxlist(s);
|
free_rxlist(s);
|
unlink_socket(s);
|
unlink_socket(s);
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Extract data from incoming tcp segment.
|
* Extract data from incoming tcp segment.
|
* Returns true if packet is queued on rxlist, false otherwise.
|
* Returns true if packet is queued on rxlist, false otherwise.
|
*/
|
*/
|
static int
|
static int
|
handle_data(tcp_socket_t *s, pktbuf_t *pkt)
|
handle_data(tcp_socket_t *s, pktbuf_t *pkt)
|
{
|
{
|
tcp_header_t *tcp = pkt->tcp_hdr;
|
tcp_header_t *tcp = pkt->tcp_hdr;
|
unsigned int diff, seq;
|
unsigned int diff, seq;
|
int data_len;
|
int data_len;
|
char *data_ptr;
|
char *data_ptr;
|
pktbuf_t *p;
|
pktbuf_t *p;
|
|
|
data_len = pkt->pkt_bytes - (tcp->hdr_len << 2);
|
data_len = pkt->pkt_bytes - (tcp->hdr_len << 2);
|
data_ptr = ((char *)tcp) + (tcp->hdr_len << 2);
|
data_ptr = ((char *)tcp) + (tcp->hdr_len << 2);
|
|
|
seq = ntohl(tcp->seqnum);
|
seq = ntohl(tcp->seqnum);
|
|
|
BSPLOG(bsp_log("tcp data: seq[%x] len[%d].\n", seq, data_len));
|
BSPLOG(bsp_log("tcp data: seq[%x] len[%d].\n", seq, data_len));
|
|
|
if (SEQ_LE(seq, s->ack)) {
|
if (SEQ_LE(seq, s->ack)) {
|
/*
|
/*
|
* Figure difference between which byte we're expecting and which byte
|
* Figure difference between which byte we're expecting and which byte
|
* is sent first. Adjust data length and data pointer accordingly.
|
* is sent first. Adjust data length and data pointer accordingly.
|
*/
|
*/
|
diff = s->ack - seq;
|
diff = s->ack - seq;
|
data_len -= diff;
|
data_len -= diff;
|
data_ptr += diff;
|
data_ptr += diff;
|
|
|
if (data_len > 0) {
|
if (data_len > 0) {
|
/* queue the new data */
|
/* queue the new data */
|
s->ack += data_len;
|
s->ack += data_len;
|
pkt->next = NULL;
|
pkt->next = NULL;
|
if ((p = s->rxlist) != NULL) {
|
if ((p = s->rxlist) != NULL) {
|
while (p->next)
|
while (p->next)
|
p = p->next;
|
p = p->next;
|
p->next = pkt;
|
p->next = pkt;
|
BSPLOG(bsp_log("tcp data: Add pkt[%x] len[%d].\n",
|
BSPLOG(bsp_log("tcp data: Add pkt[%x] len[%d].\n",
|
pkt, data_len));
|
pkt, data_len));
|
} else {
|
} else {
|
s->rxlist = pkt;
|
s->rxlist = pkt;
|
s->rxcnt = data_len;
|
s->rxcnt = data_len;
|
s->rxptr = data_ptr;
|
s->rxptr = data_ptr;
|
BSPLOG(bsp_log("tcp data: pkt[%x] len[%d].\n",
|
BSPLOG(bsp_log("tcp data: pkt[%x] len[%d].\n",
|
pkt, data_len));
|
pkt, data_len));
|
}
|
}
|
return 1;
|
return 1;
|
}
|
}
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
static void
|
static void
|
handle_ack(tcp_socket_t *s, pktbuf_t *pkt)
|
handle_ack(tcp_socket_t *s, pktbuf_t *pkt)
|
{
|
{
|
tcp_header_t *tcp = pkt->tcp_hdr;
|
tcp_header_t *tcp = pkt->tcp_hdr;
|
dword ack;
|
dword ack;
|
int advance;
|
int advance;
|
char *dp;
|
char *dp;
|
|
|
/* process ack value in packet */
|
/* process ack value in packet */
|
ack = ntohl(tcp->acknum);
|
ack = ntohl(tcp->acknum);
|
|
|
BSPLOG(bsp_log("Rcvd tcp ACK %x\n", ack));
|
BSPLOG(bsp_log("Rcvd tcp ACK %x\n", ack));
|
|
|
if (SEQ_GT(ack, s->seq)) {
|
if (SEQ_GT(ack, s->seq)) {
|
__timer_cancel(&s->timer);
|
__timer_cancel(&s->timer);
|
advance = ack - s->seq;
|
advance = ack - s->seq;
|
if (advance > s->data_bytes)
|
if (advance > s->data_bytes)
|
advance = s->data_bytes;
|
advance = s->data_bytes;
|
|
|
BSPLOG(bsp_log("seq advance %d", advance));
|
BSPLOG(bsp_log("seq advance %d", advance));
|
|
|
if (advance > 0) {
|
if (advance > 0) {
|
s->seq += advance;
|
s->seq += advance;
|
s->data_bytes -= advance;
|
s->data_bytes -= advance;
|
if (s->data_bytes) {
|
if (s->data_bytes) {
|
/* other end ack'd only part of the pkt */
|
/* other end ack'd only part of the pkt */
|
BSPLOG(bsp_log(" %d bytes left", s->data_bytes));
|
BSPLOG(bsp_log(" %d bytes left", s->data_bytes));
|
dp = (char *)(s->pkt.tcp_hdr + 1);
|
dp = (char *)(s->pkt.tcp_hdr + 1);
|
memcpy(dp, dp + advance, s->data_bytes);
|
memcpy(dp, dp + advance, s->data_bytes);
|
}
|
}
|
}
|
}
|
}
|
}
|
BSPLOG(bsp_log("\n"));
|
BSPLOG(bsp_log("\n"));
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Handle incoming TCP packets.
|
* Handle incoming TCP packets.
|
*/
|
*/
|
void
|
void
|
__tcp_handler(pktbuf_t *pkt, ip_route_t *r)
|
__tcp_handler(pktbuf_t *pkt, ip_route_t *r)
|
{
|
{
|
tcp_header_t *tcp = pkt->tcp_hdr;
|
tcp_header_t *tcp = pkt->tcp_hdr;
|
ip_header_t *ip = pkt->ip_hdr;
|
ip_header_t *ip = pkt->ip_hdr;
|
tcp_socket_t *prev,*s;
|
tcp_socket_t *prev,*s;
|
dword ack;
|
dword ack;
|
int queued = 0;
|
int queued = 0;
|
|
|
/* set length for pseudo sum calculation */
|
/* set length for pseudo sum calculation */
|
ip->length = htons(pkt->pkt_bytes);
|
ip->length = htons(pkt->pkt_bytes);
|
|
|
if (__sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip)) == 0) {
|
if (__sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip)) == 0) {
|
for (prev = NULL, s = tcp_list; s; prev = s, s = s->next) {
|
for (prev = NULL, s = tcp_list; s; prev = s, s = s->next) {
|
if (s->our_port == ntohs(tcp->dest_port)) {
|
if (s->our_port == ntohs(tcp->dest_port)) {
|
if (s->his_port == 0)
|
if (s->his_port == 0)
|
break;
|
break;
|
if (s->his_port == ntohs(tcp->src_port) &&
|
if (s->his_port == ntohs(tcp->src_port) &&
|
!memcmp(r->ip_addr, s->his_addr.ip_addr, sizeof(ip_addr_t)))
|
!memcmp(r->ip_addr, s->his_addr.ip_addr, sizeof(ip_addr_t)))
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
if (s) {
|
if (s) {
|
/* found the socket this packet belongs to */
|
/* found the socket this packet belongs to */
|
|
|
/* refresh his ethernet address */
|
/* refresh his ethernet address */
|
memcpy(s->his_addr.enet_addr, r->enet_addr, sizeof(enet_addr_t));
|
memcpy(s->his_addr.enet_addr, r->enet_addr, sizeof(enet_addr_t));
|
|
|
if (s->state != _SYN_RCVD && tcp->flags & TCP_FLAG_RST) {
|
if (s->state != _SYN_RCVD && tcp->flags & TCP_FLAG_RST) {
|
BSPLOG(bsp_log("TCP_FLAG_RST rcvd\n"));
|
BSPLOG(bsp_log("TCP_FLAG_RST rcvd\n"));
|
do_reset(s);
|
do_reset(s);
|
__pktbuf_free(pkt);
|
__pktbuf_free(pkt);
|
return;
|
return;
|
}
|
}
|
|
|
switch (s->state) {
|
switch (s->state) {
|
|
|
case _SYN_SENT:
|
case _SYN_SENT:
|
/* active open not supported */
|
/* active open not supported */
|
if (tcp->flags != (TCP_FLAG_SYN | TCP_FLAG_ACK)) {
|
if (tcp->flags != (TCP_FLAG_SYN | TCP_FLAG_ACK)) {
|
do_reset(s);
|
do_reset(s);
|
__pktbuf_free(pkt);
|
__pktbuf_free(pkt);
|
return;
|
return;
|
}
|
}
|
s->state = _ESTABLISHED;
|
s->state = _ESTABLISHED;
|
s->ack = ntohl(tcp->seqnum) + 1;
|
s->ack = ntohl(tcp->seqnum) + 1;
|
s->seq = ntohl(tcp->acknum);
|
s->seq = ntohl(tcp->acknum);
|
send_ack(s);
|
send_ack(s);
|
break;
|
break;
|
|
|
case _LISTEN:
|
case _LISTEN:
|
if (tcp->flags & TCP_FLAG_SYN) {
|
if (tcp->flags & TCP_FLAG_SYN) {
|
s->state = _SYN_RCVD;
|
s->state = _SYN_RCVD;
|
s->ack = ntohl(tcp->seqnum) + 1;
|
s->ack = ntohl(tcp->seqnum) + 1;
|
s->his_port = ntohs(tcp->src_port);
|
s->his_port = ntohs(tcp->src_port);
|
memcpy(s->his_addr.ip_addr, r->ip_addr, sizeof(ip_addr_t));
|
memcpy(s->his_addr.ip_addr, r->ip_addr, sizeof(ip_addr_t));
|
s->data_bytes = 0;
|
s->data_bytes = 0;
|
|
|
BSPLOG(bsp_log("SYN from %d.%d.%d.%d:%d (seq %x)\n",
|
BSPLOG(bsp_log("SYN from %d.%d.%d.%d:%d (seq %x)\n",
|
s->his_addr.ip_addr[0],s->his_addr.ip_addr[1],
|
s->his_addr.ip_addr[0],s->his_addr.ip_addr[1],
|
s->his_addr.ip_addr[2],s->his_addr.ip_addr[3],
|
s->his_addr.ip_addr[2],s->his_addr.ip_addr[3],
|
s->his_port, ntohl(tcp->seqnum)));
|
s->his_port, ntohl(tcp->seqnum)));
|
|
|
tcp_send(s, TCP_FLAG_SYN | TCP_FLAG_ACK, 0);
|
tcp_send(s, TCP_FLAG_SYN | TCP_FLAG_ACK, 0);
|
}
|
}
|
else
|
else
|
send_reset(pkt, r);
|
send_reset(pkt, r);
|
break;
|
break;
|
|
|
case _SYN_RCVD:
|
case _SYN_RCVD:
|
BSPLOG(bsp_log("_SYN_RCVD timer cancel.\n"));
|
BSPLOG(bsp_log("_SYN_RCVD timer cancel.\n"));
|
__timer_cancel(&s->timer);
|
__timer_cancel(&s->timer);
|
|
|
/* go back to _LISTEN state if reset */
|
/* go back to _LISTEN state if reset */
|
if (tcp->flags & TCP_FLAG_RST) {
|
if (tcp->flags & TCP_FLAG_RST) {
|
s->state = _LISTEN;
|
s->state = _LISTEN;
|
|
|
BSPLOG(bsp_log("_SYN_RCVD --> _LISTEN\n"));
|
BSPLOG(bsp_log("_SYN_RCVD --> _LISTEN\n"));
|
|
|
} else if (tcp->flags & TCP_FLAG_SYN) {
|
} else if (tcp->flags & TCP_FLAG_SYN) {
|
/* apparently our SYN/ACK was lost? */
|
/* apparently our SYN/ACK was lost? */
|
tcp_send(s, 0, 1);
|
tcp_send(s, 0, 1);
|
|
|
BSPLOG(bsp_log("retransmitting SYN/ACK\n"));
|
BSPLOG(bsp_log("retransmitting SYN/ACK\n"));
|
|
|
} else if ((tcp->flags & TCP_FLAG_ACK) &&
|
} else if ((tcp->flags & TCP_FLAG_ACK) &&
|
ntohl(tcp->acknum) == (s->seq + 1)) {
|
ntohl(tcp->acknum) == (s->seq + 1)) {
|
/* we've established the connection */
|
/* we've established the connection */
|
s->state = _ESTABLISHED;
|
s->state = _ESTABLISHED;
|
s->seq++;
|
s->seq++;
|
|
|
BSPLOG(bsp_log("ACK received - connection established\n"));
|
BSPLOG(bsp_log("ACK received - connection established\n"));
|
}
|
}
|
break;
|
break;
|
|
|
case _ESTABLISHED:
|
case _ESTABLISHED:
|
case _CLOSE_WAIT:
|
case _CLOSE_WAIT:
|
ack = s->ack; /* save original ack */
|
ack = s->ack; /* save original ack */
|
if (tcp->flags & TCP_FLAG_ACK)
|
if (tcp->flags & TCP_FLAG_ACK)
|
handle_ack(s, pkt);
|
handle_ack(s, pkt);
|
|
|
queued = handle_data(s, pkt);
|
queued = handle_data(s, pkt);
|
|
|
if ((tcp->flags & TCP_FLAG_FIN) &&
|
if ((tcp->flags & TCP_FLAG_FIN) &&
|
ntohl(tcp->seqnum) == s->ack) {
|
ntohl(tcp->seqnum) == s->ack) {
|
|
|
BSPLOG(bsp_log("FIN received - going to _CLOSE_WAIT\n"));
|
BSPLOG(bsp_log("FIN received - going to _CLOSE_WAIT\n"));
|
|
|
s->ack++;
|
s->ack++;
|
s->state = _CLOSE_WAIT;
|
s->state = _CLOSE_WAIT;
|
}
|
}
|
/*
|
/*
|
* Send an ack if neccessary.
|
* Send an ack if neccessary.
|
*/
|
*/
|
if (s->ack != ack || pkt->pkt_bytes > (tcp->hdr_len << 2))
|
if (s->ack != ack || pkt->pkt_bytes > (tcp->hdr_len << 2))
|
send_ack(s);
|
send_ack(s);
|
break;
|
break;
|
|
|
case _LAST_ACK:
|
case _LAST_ACK:
|
if (tcp->flags & TCP_FLAG_ACK) {
|
if (tcp->flags & TCP_FLAG_ACK) {
|
handle_ack(s, pkt);
|
handle_ack(s, pkt);
|
if (ntohl(tcp->acknum) == (s->seq + 1)) {
|
if (ntohl(tcp->acknum) == (s->seq + 1)) {
|
BSPLOG(bsp_log("_LAST_ACK --> _CLOSED\n"));
|
BSPLOG(bsp_log("_LAST_ACK --> _CLOSED\n"));
|
s->state = _CLOSED;
|
s->state = _CLOSED;
|
unlink_socket(s);
|
unlink_socket(s);
|
}
|
}
|
}
|
}
|
break;
|
break;
|
|
|
case _FIN_WAIT_1:
|
case _FIN_WAIT_1:
|
if (tcp->flags & TCP_FLAG_ACK) {
|
if (tcp->flags & TCP_FLAG_ACK) {
|
handle_ack(s, pkt);
|
handle_ack(s, pkt);
|
if (ntohl(tcp->acknum) == (s->seq + 1)) {
|
if (ntohl(tcp->acknum) == (s->seq + 1)) {
|
/* got ACK for FIN packet */
|
/* got ACK for FIN packet */
|
s->seq++;
|
s->seq++;
|
if (tcp->flags & TCP_FLAG_FIN) {
|
if (tcp->flags & TCP_FLAG_FIN) {
|
BSPLOG(bsp_log("_FIN_WAIT_1 --> _TIME_WAIT\n"));
|
BSPLOG(bsp_log("_FIN_WAIT_1 --> _TIME_WAIT\n"));
|
s->ack++;
|
s->ack++;
|
s->state = _TIME_WAIT;
|
s->state = _TIME_WAIT;
|
send_ack(s);
|
send_ack(s);
|
} else {
|
} else {
|
s->state = _FIN_WAIT_2;
|
s->state = _FIN_WAIT_2;
|
BSPLOG(bsp_log("_FIN_WAIT_1 --> _FIN_WAIT_2\n"));
|
BSPLOG(bsp_log("_FIN_WAIT_1 --> _FIN_WAIT_2\n"));
|
}
|
}
|
break; /* All done for now */
|
break; /* All done for now */
|
}
|
}
|
}
|
}
|
/* At this point, no ACK for FIN has been seen, so check for
|
/* At this point, no ACK for FIN has been seen, so check for
|
simultaneous close */
|
simultaneous close */
|
if (tcp->flags & TCP_FLAG_FIN) {
|
if (tcp->flags & TCP_FLAG_FIN) {
|
BSPLOG(bsp_log("_FIN_WAIT_1 --> _CLOSING\n"));
|
BSPLOG(bsp_log("_FIN_WAIT_1 --> _CLOSING\n"));
|
__timer_cancel(&s->timer);
|
__timer_cancel(&s->timer);
|
s->ack++;
|
s->ack++;
|
s->state = _CLOSING;
|
s->state = _CLOSING;
|
/* FIN is resent so the timeout and retry for this packet
|
/* FIN is resent so the timeout and retry for this packet
|
will also take care of timeout and resend of the
|
will also take care of timeout and resend of the
|
previously sent FIN (which got us to FIN_WAIT_1). While
|
previously sent FIN (which got us to FIN_WAIT_1). While
|
not technically correct, resending FIN only causes a
|
not technically correct, resending FIN only causes a
|
duplicate FIN (same sequence number) which should be
|
duplicate FIN (same sequence number) which should be
|
ignored by the other end. */
|
ignored by the other end. */
|
tcp_send(s, TCP_FLAG_FIN | TCP_FLAG_ACK, 0);
|
tcp_send(s, TCP_FLAG_FIN | TCP_FLAG_ACK, 0);
|
}
|
}
|
break;
|
break;
|
|
|
case _FIN_WAIT_2:
|
case _FIN_WAIT_2:
|
queued = handle_data(s, pkt);
|
queued = handle_data(s, pkt);
|
if (tcp->flags & TCP_FLAG_FIN) {
|
if (tcp->flags & TCP_FLAG_FIN) {
|
BSPLOG(bsp_log("_FIN_WAIT_2 --> _TIME_WAIT\n"));
|
BSPLOG(bsp_log("_FIN_WAIT_2 --> _TIME_WAIT\n"));
|
s->ack++;
|
s->ack++;
|
s->state = _TIME_WAIT;
|
s->state = _TIME_WAIT;
|
send_ack(s);
|
send_ack(s);
|
}
|
}
|
break;
|
break;
|
|
|
case _CLOSING:
|
case _CLOSING:
|
if (tcp->flags & TCP_FLAG_ACK) {
|
if (tcp->flags & TCP_FLAG_ACK) {
|
handle_ack(s, pkt);
|
handle_ack(s, pkt);
|
if (ntohl(tcp->acknum) == (s->seq + 1)) {
|
if (ntohl(tcp->acknum) == (s->seq + 1)) {
|
/* got ACK for FIN packet */
|
/* got ACK for FIN packet */
|
BSPLOG(bsp_log("_CLOSING --> _TIME_WAIT\n"));
|
BSPLOG(bsp_log("_CLOSING --> _TIME_WAIT\n"));
|
__timer_cancel(&s->timer);
|
__timer_cancel(&s->timer);
|
s->state = _TIME_WAIT;
|
s->state = _TIME_WAIT;
|
}
|
}
|
}
|
}
|
break;
|
break;
|
|
|
case _TIME_WAIT:
|
case _TIME_WAIT:
|
BSPLOG(bsp_log("_TIME_WAIT resend.\n"));
|
BSPLOG(bsp_log("_TIME_WAIT resend.\n"));
|
if (tcp->flags & TCP_FLAG_FIN)
|
if (tcp->flags & TCP_FLAG_FIN)
|
tcp_send(s, 0, 1); /* just resend ack */
|
tcp_send(s, 0, 1); /* just resend ack */
|
break;
|
break;
|
}
|
}
|
} else {
|
} else {
|
BSPLOG(bsp_log("Unexpected segment from: %d.%d.%d.%d:%d\n",
|
BSPLOG(bsp_log("Unexpected segment from: %d.%d.%d.%d:%d\n",
|
r->ip_addr[0], r->ip_addr[1], r->ip_addr[3],
|
r->ip_addr[0], r->ip_addr[1], r->ip_addr[3],
|
r->ip_addr[4], ntohs(tcp->src_port)));
|
r->ip_addr[4], ntohs(tcp->src_port)));
|
send_reset(pkt, r);
|
send_reset(pkt, r);
|
}
|
}
|
}
|
}
|
if (!queued)
|
if (!queued)
|
__pktbuf_free(pkt);
|
__pktbuf_free(pkt);
|
}
|
}
|
|
|
|
|
void
|
void
|
__tcp_poll(void)
|
__tcp_poll(void)
|
{
|
{
|
__enet_poll();
|
__enet_poll();
|
MS_TICKS_DELAY();
|
MS_TICKS_DELAY();
|
__timer_poll();
|
__timer_poll();
|
}
|
}
|
|
|
|
|
int
|
int
|
__tcp_listen(tcp_socket_t *s, word port)
|
__tcp_listen(tcp_socket_t *s, word port)
|
{
|
{
|
BSPLOG(bsp_log("tcp_listen: s[%p] port[%x]\n", s, port));
|
BSPLOG(bsp_log("tcp_listen: s[%p] port[%x]\n", s, port));
|
|
|
memset(s, 0, sizeof(tcp_socket_t));
|
memset(s, 0, sizeof(tcp_socket_t));
|
s->state = _LISTEN;
|
s->state = _LISTEN;
|
s->our_port = port;
|
s->our_port = port;
|
s->pkt.buf = (word *)s->pktbuf;
|
s->pkt.buf = (word *)s->pktbuf;
|
s->pkt.bufsize = ETH_MAX_PKTLEN;
|
s->pkt.bufsize = ETH_MAX_PKTLEN;
|
s->pkt.ip_hdr = (ip_header_t *)s->pkt.buf;
|
s->pkt.ip_hdr = (ip_header_t *)s->pkt.buf;
|
s->pkt.tcp_hdr = (tcp_header_t *)(s->pkt.ip_hdr + 1);
|
s->pkt.tcp_hdr = (tcp_header_t *)(s->pkt.ip_hdr + 1);
|
|
|
s->next = tcp_list;
|
s->next = tcp_list;
|
|
|
#if 0
|
#if 0
|
/* limit to one open socket at a time */
|
/* limit to one open socket at a time */
|
if (s->next) {
|
if (s->next) {
|
BSPLOG(bsp_log("tcp_listen: recursion error\n"));
|
BSPLOG(bsp_log("tcp_listen: recursion error\n"));
|
BSPLOG(while(1));
|
BSPLOG(while(1));
|
}
|
}
|
#endif
|
#endif
|
|
|
tcp_list = s;
|
tcp_list = s;
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
* SO_REUSEADDR, no 2MSL.
|
* SO_REUSEADDR, no 2MSL.
|
*/
|
*/
|
void
|
void
|
__tcp_so_reuseaddr(tcp_socket_t *s)
|
__tcp_so_reuseaddr(tcp_socket_t *s)
|
{
|
{
|
// BSPLOG(bsp_log("__tcp_so_reuseaddr.\n"));
|
// BSPLOG(bsp_log("__tcp_so_reuseaddr.\n"));
|
s->reuse = 0x01;
|
s->reuse = 0x01;
|
}
|
}
|
|
|
/*
|
/*
|
* Block while waiting for all data to be transmitted.
|
* Block while waiting for all data to be transmitted.
|
*/
|
*/
|
void
|
void
|
__tcp_drain(tcp_socket_t *s)
|
__tcp_drain(tcp_socket_t *s)
|
{
|
{
|
// BSPLOG(bsp_log("__tcp_drain.\n"));
|
// BSPLOG(bsp_log("__tcp_drain.\n"));
|
while (s->state != _CLOSED && s->data_bytes)
|
while (s->state != _CLOSED && s->data_bytes)
|
__tcp_poll();
|
__tcp_poll();
|
// BSPLOG(bsp_log("__tcp_drain done.\n"));
|
// BSPLOG(bsp_log("__tcp_drain done.\n"));
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Close the tcp connection.
|
* Close the tcp connection.
|
*/
|
*/
|
static void
|
static void
|
do_abort(void *s)
|
do_abort(void *s)
|
{
|
{
|
BSPLOG(bsp_log("do_abort: send RST\n"));
|
BSPLOG(bsp_log("do_abort: send RST\n"));
|
tcp_send((tcp_socket_t *)s, TCP_FLAG_ACK | TCP_FLAG_RST, 0);
|
tcp_send((tcp_socket_t *)s, TCP_FLAG_ACK | TCP_FLAG_RST, 0);
|
__timer_cancel(&abort_timer);
|
__timer_cancel(&abort_timer);
|
((tcp_socket_t *)s)->state = _CLOSED;
|
((tcp_socket_t *)s)->state = _CLOSED;
|
free_rxlist((tcp_socket_t *)s);
|
free_rxlist((tcp_socket_t *)s);
|
unlink_socket((tcp_socket_t *)s);
|
unlink_socket((tcp_socket_t *)s);
|
}
|
}
|
|
|
void
|
void
|
__tcp_abort(tcp_socket_t *s, unsigned long delay)
|
__tcp_abort(tcp_socket_t *s, unsigned long delay)
|
{
|
{
|
__timer_set(&abort_timer, delay, do_abort, s);
|
__timer_set(&abort_timer, delay, do_abort, s);
|
}
|
}
|
|
|
/*
|
/*
|
* Close the tcp connection.
|
* Close the tcp connection.
|
*/
|
*/
|
void
|
void
|
__tcp_close(tcp_socket_t *s)
|
__tcp_close(tcp_socket_t *s)
|
{
|
{
|
__tcp_drain(s);
|
__tcp_drain(s);
|
if (s->state == _ESTABLISHED || s->state == _SYN_RCVD) {
|
if (s->state == _ESTABLISHED || s->state == _SYN_RCVD) {
|
BSPLOG(bsp_log("__tcp_close: going to _FIN_WAIT_1\n"));
|
BSPLOG(bsp_log("__tcp_close: going to _FIN_WAIT_1\n"));
|
s->state = _FIN_WAIT_1;
|
s->state = _FIN_WAIT_1;
|
tcp_send(s, TCP_FLAG_ACK | TCP_FLAG_FIN, 0);
|
tcp_send(s, TCP_FLAG_ACK | TCP_FLAG_FIN, 0);
|
} else if (s->state == _CLOSE_WAIT) {
|
} else if (s->state == _CLOSE_WAIT) {
|
|
|
BSPLOG(bsp_log("__tcp_close: going to _LAST_ACK\n"));
|
BSPLOG(bsp_log("__tcp_close: going to _LAST_ACK\n"));
|
|
|
s->state = _LAST_ACK;
|
s->state = _LAST_ACK;
|
tcp_send(s, TCP_FLAG_ACK | TCP_FLAG_FIN, 0);
|
tcp_send(s, TCP_FLAG_ACK | TCP_FLAG_FIN, 0);
|
}
|
}
|
free_rxlist(s);
|
free_rxlist(s);
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Wait for connection to be fully closed.
|
* Wait for connection to be fully closed.
|
*/
|
*/
|
void
|
void
|
__tcp_close_wait(tcp_socket_t *s)
|
__tcp_close_wait(tcp_socket_t *s)
|
{
|
{
|
BSPLOG(bsp_log("__tcp_close_wait.\n"));
|
BSPLOG(bsp_log("__tcp_close_wait.\n"));
|
while (s->state != _CLOSED)
|
while (s->state != _CLOSED)
|
__tcp_poll();
|
__tcp_poll();
|
BSPLOG(bsp_log("__tcp_close_wait done.\n"));
|
BSPLOG(bsp_log("__tcp_close_wait done.\n"));
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Read up to 'len' bytes without blocking.
|
* Read up to 'len' bytes without blocking.
|
*/
|
*/
|
int
|
int
|
__tcp_read(tcp_socket_t *s, char *buf, int len)
|
__tcp_read(tcp_socket_t *s, char *buf, int len)
|
{
|
{
|
int nread;
|
int nread;
|
pktbuf_t *pkt;
|
pktbuf_t *pkt;
|
tcp_header_t *tcp;
|
tcp_header_t *tcp;
|
|
|
if (len <= 0 || s->rxcnt == 0)
|
if (len <= 0 || s->rxcnt == 0)
|
return 0;
|
return 0;
|
|
|
if (s->state != _ESTABLISHED && s->rxcnt == 0)
|
if (s->state != _ESTABLISHED && s->rxcnt == 0)
|
return -1;
|
return -1;
|
|
|
nread = 0;
|
nread = 0;
|
while (len) {
|
while (len) {
|
if (len < s->rxcnt) {
|
if (len < s->rxcnt) {
|
memcpy(buf, s->rxptr, len);
|
memcpy(buf, s->rxptr, len);
|
BSPLOG(bsp_log("tcp_read: read %d bytes.\n", len));
|
BSPLOG(bsp_log("tcp_read: read %d bytes.\n", len));
|
s->rxptr += len;
|
s->rxptr += len;
|
s->rxcnt -= len;
|
s->rxcnt -= len;
|
nread += len;
|
nread += len;
|
|
|
BSPLOG(bsp_log("tcp_read: %d bytes left in rxlist head.\n",
|
BSPLOG(bsp_log("tcp_read: %d bytes left in rxlist head.\n",
|
s->rxcnt));
|
s->rxcnt));
|
|
|
break;
|
break;
|
} else {
|
} else {
|
memcpy(buf, s->rxptr, s->rxcnt);
|
memcpy(buf, s->rxptr, s->rxcnt);
|
BSPLOG(bsp_log("tcp_read: read %d bytes. pkt[%x] freed.\n",
|
BSPLOG(bsp_log("tcp_read: read %d bytes. pkt[%x] freed.\n",
|
s->rxcnt, s->rxlist));
|
s->rxcnt, s->rxlist));
|
nread += s->rxcnt;
|
nread += s->rxcnt;
|
buf += s->rxcnt;
|
buf += s->rxcnt;
|
len -= s->rxcnt;
|
len -= s->rxcnt;
|
|
|
/* setup for next packet in list */
|
/* setup for next packet in list */
|
pkt = s->rxlist;
|
pkt = s->rxlist;
|
s->rxlist = pkt->next;
|
s->rxlist = pkt->next;
|
__pktbuf_free(pkt);
|
__pktbuf_free(pkt);
|
|
|
if ((pkt = s->rxlist) != NULL) {
|
if ((pkt = s->rxlist) != NULL) {
|
tcp = pkt->tcp_hdr;
|
tcp = pkt->tcp_hdr;
|
s->rxcnt = pkt->pkt_bytes - (tcp->hdr_len << 2);
|
s->rxcnt = pkt->pkt_bytes - (tcp->hdr_len << 2);
|
s->rxptr = ((char *)tcp) + (tcp->hdr_len << 2);
|
s->rxptr = ((char *)tcp) + (tcp->hdr_len << 2);
|
|
|
BSPLOG(bsp_log("tcp_read: next pkt[%x] has %d bytes.\n",
|
BSPLOG(bsp_log("tcp_read: next pkt[%x] has %d bytes.\n",
|
s->rxlist, s->rxcnt));
|
s->rxlist, s->rxcnt));
|
} else {
|
} else {
|
|
|
BSPLOG(bsp_log("tcp_read: no more data.\n"));
|
BSPLOG(bsp_log("tcp_read: no more data.\n"));
|
|
|
s->rxcnt = 0;
|
s->rxcnt = 0;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
}
|
}
|
return nread;
|
return nread;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Write up to 'len' bytes without blocking
|
* Write up to 'len' bytes without blocking
|
*/
|
*/
|
int
|
int
|
__tcp_write(tcp_socket_t *s, char *buf, int len)
|
__tcp_write(tcp_socket_t *s, char *buf, int len)
|
{
|
{
|
tcp_header_t *tcp = s->pkt.tcp_hdr;
|
tcp_header_t *tcp = s->pkt.tcp_hdr;
|
|
|
if (len <= 0)
|
if (len <= 0)
|
return 0;
|
return 0;
|
|
|
if (s->state != _ESTABLISHED && s->state != _CLOSE_WAIT)
|
if (s->state != _ESTABLISHED && s->state != _CLOSE_WAIT)
|
return -1;
|
return -1;
|
|
|
if (s->data_bytes)
|
if (s->data_bytes)
|
return 0;
|
return 0;
|
|
|
if (len > MAX_TCP_DATA)
|
if (len > MAX_TCP_DATA)
|
len = MAX_TCP_DATA;
|
len = MAX_TCP_DATA;
|
|
|
memcpy(tcp + 1, buf, len);
|
memcpy(tcp + 1, buf, len);
|
s->data_bytes = len;
|
s->data_bytes = len;
|
|
|
tcp_send(s, TCP_FLAG_ACK, 0);
|
tcp_send(s, TCP_FLAG_ACK, 0);
|
|
|
return len;
|
return len;
|
}
|
}
|
|
|
/*
|
/*
|
* Write 'len' bytes from 'buf', blocking until sent.
|
* Write 'len' bytes from 'buf', blocking until sent.
|
* If connection collapses, return -1
|
* If connection collapses, return -1
|
*/
|
*/
|
int
|
int
|
__tcp_write_block(tcp_socket_t *s, char *buf, int len)
|
__tcp_write_block(tcp_socket_t *s, char *buf, int len)
|
{
|
{
|
int total = 0;
|
int total = 0;
|
int n;
|
int n;
|
|
|
while (len) {
|
while (len) {
|
if (s->state == _CLOSE_WAIT) {
|
if (s->state == _CLOSE_WAIT) {
|
// This connection is tring to close
|
// This connection is tring to close
|
// This connection is breaking
|
// This connection is breaking
|
if (s->data_bytes == 0 && s->rxcnt == 0)
|
if (s->data_bytes == 0 && s->rxcnt == 0)
|
__tcp_close(s);
|
__tcp_close(s);
|
}
|
}
|
if (s->state == _CLOSED) {
|
if (s->state == _CLOSED) {
|
// The connection is gone!
|
// The connection is gone!
|
return -1;
|
return -1;
|
}
|
}
|
n = __tcp_write(s, buf, len);
|
n = __tcp_write(s, buf, len);
|
if (n > 0) {
|
if (n > 0) {
|
len -= n;
|
len -= n;
|
buf += n;
|
buf += n;
|
}
|
}
|
__tcp_poll();
|
__tcp_poll();
|
}
|
}
|
__tcp_drain(s);
|
__tcp_drain(s);
|
return total;
|
return total;
|
}
|
}
|
|
|
/*
|
/*
|
* Establish a new [outgoing] connection, with a timeout.
|
* Establish a new [outgoing] connection, with a timeout.
|
*/
|
*/
|
int
|
int
|
__tcp_open(tcp_socket_t *s, struct sockaddr_in *host,
|
__tcp_open(tcp_socket_t *s, struct sockaddr_in *host,
|
word port, int timeout, int *err)
|
word port, int timeout, int *err)
|
{
|
{
|
// Fill in socket details
|
// Fill in socket details
|
memset(s, 0, sizeof(tcp_socket_t));
|
memset(s, 0, sizeof(tcp_socket_t));
|
s->state = _SYN_SENT;
|
s->state = _SYN_SENT;
|
s->our_port = port;
|
s->our_port = port;
|
s->his_port = host->sin_port;
|
s->his_port = host->sin_port;
|
s->pkt.buf = (word *)s->pktbuf;
|
s->pkt.buf = (word *)s->pktbuf;
|
s->pkt.bufsize = ETH_MAX_PKTLEN;
|
s->pkt.bufsize = ETH_MAX_PKTLEN;
|
s->pkt.ip_hdr = (ip_header_t *)s->pkt.buf;
|
s->pkt.ip_hdr = (ip_header_t *)s->pkt.buf;
|
s->pkt.tcp_hdr = (tcp_header_t *)(s->pkt.ip_hdr + 1);
|
s->pkt.tcp_hdr = (tcp_header_t *)(s->pkt.ip_hdr + 1);
|
s->seq = (port << 16) | 0xDE77;
|
s->seq = (port << 16) | 0xDE77;
|
s->ack = 0;
|
s->ack = 0;
|
if (__arp_lookup((ip_addr_t *)&host->sin_addr, &s->his_addr) < 0) {
|
if (__arp_lookup((ip_addr_t *)&host->sin_addr, &s->his_addr) < 0) {
|
diag_printf("%s: Can't find address of server\n", __FUNCTION__);
|
diag_printf("%s: Can't find address of server\n", __FUNCTION__);
|
return -1;
|
return -1;
|
}
|
}
|
s->next = tcp_list;
|
s->next = tcp_list;
|
tcp_list = s;
|
tcp_list = s;
|
|
|
// Send off the SYN packet to open the connection
|
// Send off the SYN packet to open the connection
|
tcp_send(s, TCP_FLAG_SYN, 0);
|
tcp_send(s, TCP_FLAG_SYN, 0);
|
// Wait for connection to establish
|
// Wait for connection to establish
|
while (s->state != _ESTABLISHED) {
|
while (s->state != _ESTABLISHED) {
|
if (s->state == _CLOSED) {
|
if (s->state == _CLOSED) {
|
diag_printf("TCP open - host closed connection\n");
|
diag_printf("TCP open - host closed connection\n");
|
return -1;
|
return -1;
|
}
|
}
|
if (--timeout <= 0) {
|
if (--timeout <= 0) {
|
diag_printf("TCP open - connection timed out\n");
|
diag_printf("TCP open - connection timed out\n");
|
return -1;
|
return -1;
|
}
|
}
|
MS_TICKS_DELAY();
|
MS_TICKS_DELAY();
|
__tcp_poll();
|
__tcp_poll();
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
|
|