📄 tcp.c
字号:
//==========================================================================
//
// net/tcp.c
//
// Stand-alone TCP networking support for RedBoot
//
//==========================================================================
//####COPYRIGHTBEGIN####
//
// -------------------------------------------
// The contents of this file are subject to the Red Hat eCos Public License
// Version 1.1 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.redhat.com/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations under
// the License.
//
// The Original Code is eCos - Embedded Configurable Operating System,
// released September 30, 1998.
//
// The Initial Developer of the Original Code is Red Hat.
// Portions created by Red Hat are
// Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc.
// All Rights Reserved.
// -------------------------------------------
//
//####COPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
#include <net/net.h>
#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))
/* sequence number comparison macros */
#define SEQ_LT(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_GE(a,b) ((int)((a)-(b)) >= 0)
static void do_retrans(void *p);
static void do_close(void *p);
#ifdef BSP_LOG
static char *
flags_to_str(octet f)
{
static char str[7], *p;
p = str;
if (f & TCP_FLAG_FIN)
*p++ = 'F';
if (f & TCP_FLAG_SYN)
*p++ = 'S';
if (f & TCP_FLAG_RST)
*p++ = 'R';
if (f & TCP_FLAG_PSH)
*p++ = 'P';
if (f & TCP_FLAG_ACK)
*p++ = 'A';
if (f & TCP_FLAG_URG)
*p++ = 'U';
*p = '\0';
return str;
}
#endif
/*
* A major assumption is that only a very small number of sockets will
* active, so a simple linear search of those sockets is acceptible.
*/
static tcp_socket_t *tcp_list;
/*
* Format and send an outgoing segment.
*/
static void
tcp_send(tcp_socket_t *s, int flags, int resend)
{
tcp_header_t *tcp;
ip_header_t *ip;
pktbuf_t *pkt = &s->pkt;
unsigned short cksum;
dword tcp_magic;
int tcp_magic_size = sizeof(tcp_magic);
ip = pkt->ip_hdr;
tcp = pkt->tcp_hdr;
if (flags & TCP_FLAG_SYN) {
/* If SYN, assume no data and send MSS option in tcp header */
pkt->pkt_bytes = sizeof(tcp_header_t) + 4;
tcp->hdr_len = 6;
tcp_magic = htonl(0x02040000 | MAX_TCP_DATA);
memcpy((unsigned char *)(tcp+1), &tcp_magic, tcp_magic_size);
s->data_bytes = 0;
} else {
pkt->pkt_bytes = s->data_bytes + sizeof(tcp_header_t);
tcp->hdr_len = 5;
}
/* tcp header */
tcp->reserved = 0;
tcp->seqnum = htonl(s->seq);
tcp->acknum = htonl(s->ack);
tcp->checksum = 0;
if (!resend) {
tcp->src_port = htons(s->our_port);
tcp->dest_port = htons(s->his_port);
tcp->flags = flags;
/* always set PUSH flag if sending data */
if (s->data_bytes)
tcp->flags |= TCP_FLAG_PSH;
tcp->window = htons(MAX_TCP_DATA);
tcp->urgent = 0;
/* fill in some pseudo-header fields */
memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
memcpy(ip->destination, s->his_addr.ip_addr, sizeof(ip_addr_t));
ip->protocol = IP_PROTO_TCP;
}
/* another pseudo-header field */
ip->length = htons(pkt->pkt_bytes);
/* compute tcp checksum */
cksum = __sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip));
tcp->checksum = htons(cksum);
__ip_send(pkt, IP_PROTO_TCP, &s->his_addr);
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));
if (s->state == _TIME_WAIT)
__timer_set(&s->timer, 120000, do_close, s);
else if ((tcp->flags & (TCP_FLAG_FIN | TCP_FLAG_SYN)) || s->data_bytes)
__timer_set(&s->timer, 1000, do_retrans, s);
}
/*
* Send a reset for a bogus incoming segment.
*/
static void
send_reset(pktbuf_t *pkt, ip_route_t *r)
{
ip_header_t *ip = pkt->ip_hdr;
tcp_header_t *tcp = pkt->tcp_hdr;
dword seq, ack;
word src, dest;
word cksum;
seq = ntohl(tcp->acknum);
ack = ntohl(tcp->seqnum);
src = ntohs(tcp->dest_port);
dest = ntohs(tcp->src_port);
tcp = (tcp_header_t *)(ip + 1);
pkt->pkt_bytes = sizeof(tcp_header_t);
/* tcp header */
tcp->hdr_len = 5;
tcp->reserved = 0;
tcp->seqnum = htonl(seq);
tcp->acknum = htonl(ack);
tcp->window = htons(1024);
tcp->urgent = 0;
tcp->checksum = 0;
tcp->src_port = htons(src);
tcp->dest_port = htons(dest);
tcp->flags = TCP_FLAG_RST | TCP_FLAG_ACK;
/* fill in some pseudo-header fields */
memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
memcpy(ip->destination, r->ip_addr, sizeof(ip_addr_t));
ip->protocol = IP_PROTO_TCP;
ip->length = htons(pkt->pkt_bytes);
/* compute tcp checksum */
cksum = __sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip));
tcp->checksum = htons(cksum);
__ip_send(pkt, IP_PROTO_TCP, r);
}
/*
* Remove given socket from socket list.
*/
static void
unlink_socket(tcp_socket_t *s)
{
tcp_socket_t *prev, *tp;
for (prev = NULL, tp = tcp_list; tp; prev = tp, tp = tp->next)
if (tp == s) {
BSPLOG(bsp_log("unlink tcp socket.\n"));
if (prev)
prev->next = s->next;
else
tcp_list = s->next;
}
}
/*
* Retransmit last packet.
*/
static void
do_retrans(void *p)
{
BSPLOG(bsp_log("tcp do_retrans.\n"));
tcp_send((tcp_socket_t *)p, 0, 1);
}
static void
do_close(void *p)
{
BSPLOG(bsp_log("tcp do_close.\n"));
/* close connection */
((tcp_socket_t *)p)->state = _CLOSED;
unlink_socket(p);
}
static void
free_rxlist(tcp_socket_t *s)
{
pktbuf_t *p;
BSPLOG(bsp_log("tcp free_rxlist.\n"));
while ((p = s->rxlist) != NULL) {
s->rxlist = p->next;
__pktbuf_free(p);
}
}
/*
* Handle a conection reset.
*/
static void
do_reset(tcp_socket_t *s)
{
/* close connection */
s->state = _CLOSED;
__timer_cancel(&s->timer);
free_rxlist(s);
unlink_socket(s);
}
/*
* Extract data from incoming tcp segment.
* Returns true if packet is queued on rxlist, false otherwise.
*/
static int
handle_data(tcp_socket_t *s, pktbuf_t *pkt)
{
tcp_header_t *tcp = pkt->tcp_hdr;
unsigned int diff, seq;
int data_len;
char *data_ptr;
pktbuf_t *p;
data_len = pkt->pkt_bytes - (tcp->hdr_len << 2);
data_ptr = ((char *)tcp) + (tcp->hdr_len << 2);
seq = ntohl(tcp->seqnum);
BSPLOG(bsp_log("tcp data: seq[%x] len[%d].\n", seq, data_len));
if (SEQ_LE(seq, s->ack)) {
/*
* Figure difference between which byte we're expecting and which byte
* is sent first. Adjust data length and data pointer accordingly.
*/
diff = s->ack - seq;
data_len -= diff;
data_ptr += diff;
if (data_len > 0) {
/* queue the new data */
s->ack += data_len;
pkt->next = NULL;
if ((p = s->rxlist) != NULL) {
while (p->next)
p = p->next;
p->next = pkt;
BSPLOG(bsp_log("tcp data: Add pkt[%x] len[%d].\n",
pkt, data_len));
} else {
s->rxlist = pkt;
s->rxcnt = data_len;
s->rxptr = data_ptr;
BSPLOG(bsp_log("tcp data: pkt[%x] len[%d].\n",
pkt, data_len));
}
return 1;
}
}
return 0;
}
static void
handle_ack(tcp_socket_t *s, pktbuf_t *pkt)
{
tcp_header_t *tcp = pkt->tcp_hdr;
dword ack;
int advance;
char *dp;
/* process ack value in packet */
ack = ntohl(tcp->acknum);
BSPLOG(bsp_log("Rcvd tcp ACK %x\n", ack));
if (SEQ_GT(ack, s->seq)) {
__timer_cancel(&s->timer);
advance = ack - s->seq;
if (advance > s->data_bytes)
advance = s->data_bytes;
BSPLOG(bsp_log("seq advance %d", advance));
if (advance > 0) {
s->seq += advance;
s->data_bytes -= advance;
if (s->data_bytes) {
/* other end ack'd only part of the pkt */
BSPLOG(bsp_log(" %d bytes left", s->data_bytes));
dp = (char *)(s->pkt.tcp_hdr + 1);
memcpy(dp, dp + advance, s->data_bytes);
}
}
}
BSPLOG(bsp_log("\n"));
}
/*
* Handle incoming TCP packets.
*/
void
__tcp_handler(pktbuf_t *pkt, ip_route_t *r)
{
tcp_header_t *tcp = pkt->tcp_hdr;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -