⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tcp.c

📁 基于ecos的redboot
💻 C
📖 第 1 页 / 共 2 页
字号:
//==========================================================================
//
//      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 + -