www.pudn.com > uCOSII上实现的tcpip.rar > ztcp_output.c
#include "include/zeth.h"
#include "include/zarp.h"
#include "include/ztcp.h"
#include "include/ztask.h"
#include "include/zstats.h"
u8_t tcp_ip_out(znetif_t *pnetif, ipaddr_t *pdest_ip, ipaddr_t *psrc_ip,zbuffer_t * pbuffer)
{
ip_header_t *pipheader;
eth_header_t *pethheader;
ethaddr_t remote_eth;
u8_t i;
if ( pnetif != NULL && pbuffer!= NULL)
{
pethheader = (eth_header_t *)pbuffer->pdata;
pipheader = (ip_header_t *) ( (u8_t *)pbuffer->pdata + ETH_HEAD_LEN);
IPH_VHLTOS_SET(pipheader, 4, IP_HEAD_LEN / 4, 0);
IPH_LEN_SET(pipheader, pbuffer->tot_len - ETH_HEAD_LEN);
IPH_OFFSET_SET(pipheader, IP_DF);
IPH_ID_SET(pipheader, ++ip_id);
IPH_TTL_SET(pipheader, TCP_TTL);
IPH_PROTO_SET(pipheader, IP_PROTO_TCP);
pipheader->dest_ipaddr = *pdest_ip;
pipheader->src_ipaddr = *psrc_ip;
IPH_CHKSUM_SET(pipheader, 0);
IPH_CHKSUM_SET(pipheader, inet_chksum(pipheader, IP_HEAD_LEN));
if ( arp_lookup( &remote_eth, pdest_ip) != 0 )
{
if ( arp_query(&remote_eth, pdest_ip ) != 0 )
{
return -1;
}
}
/*last eth header*/
for ( i = 0; i < 6 ; i ++)
{
pethheader->dest_hwaddr.addr[i] = remote_eth.addr[i];
pethheader->src_hwaddr.addr[i] = pnetif->hwaddr.addr[i];
}
pethheader->eth_type = ETHTYPE_IP;
pnetif->netif_tx(pnetif, pbuffer);
}
return 0;
}
u8_t tcp_reset(znetif_t *pnetif, ip_header_t *pipheader, tcp_header_t *ptcpheader)
{
zbuffer_t *sbuf;
ip_header_t *piph;
eth_header_t *pethh;
tcp_header_t *ptcph;
s16_t hoff;
u16_t offset;
sbuf = zbuffer_new(IP_HEAD_LEN + ETH_HEAD_LEN + TCP_HEAD_LEN );
if ( sbuf == NULL)
return -1;
/*setup information pointer*/
pethh = (eth_header_t *)(sbuf->pdata);
piph = (ip_header_t *)( (u8_t *)sbuf->pdata + ETH_HEAD_LEN);
ptcph = (tcp_header_t *)((u8_t *)sbuf->pdata + IP_HEAD_LEN + ETH_HEAD_LEN);
/*first setup TCP header*/
ptcph->src = ptcpheader->dest;
ptcph->dest = ptcpheader->src;
if ( TCPH_FLAGS(ptcpheader) & TCP_ACK )
{
ptcph->seqno = ptcpheader->ackno;
TCPH_FLAGS_SET(ptcph, TCP_RST);
}
else
{
ptcph->seqno = 0;
TCPH_FLAGS_SET(ptcph, TCP_RST | TCP_ACK);
}
offset = IPH_LEN(pipheader) - IPH_HL(pipheader) * 4 - (TCPH_OFFSET(ptcpheader) >> 2) ;
if ( TCPH_FLAGS(ptcpheader) & TCP_SYN)
{
offset ++;
}
if ( TCPH_FLAGS(ptcpheader) & TCP_FIN)
{
offset ++;
}
ptcph->ackno = ptcpheader->seqno + offset;
TCPH_OFFSET_SET(ptcph, TCP_HEAD_LEN << 2);
ptcph->wnd = ptcph->urgp = 0;
hoff = (s16_t)(0 - (IP_HEAD_LEN + ETH_HEAD_LEN));
if ( zbuffer_head_adjust(sbuf, hoff) != 0)
return -1;
ptcph->chksum = 0;
ptcph->chksum = inet_chksum_pseudo(sbuf , &pipheader->src_ipaddr, &pipheader->dest_ipaddr, IP_PROTO_TCP, TCP_HEAD_LEN);
hoff *= -1;
if ( zbuffer_head_adjust(sbuf,hoff) != 0)
return -1;
/*send it to network at once*/
tcp_ip_out(pnetif, &pipheader->src_ipaddr, &pipheader->dest_ipaddr, sbuf);
zbuffer_delete(sbuf);
return 0;
}
static void tcp_output_seg(tcp_pcb_t *ptcp, tcp_seg_t *seg)
{
s16_t h_off;
/* The TCP header has already been constructed, but the ackno and
wnd fields remain. */
seg->ptcpheader->ackno = (ptcp->rcv_nxt);
/* silly window avoidance */
if(ptcp->rcv_wnd < ptcp->mss)
{
seg->ptcpheader->wnd = 0;
}
else
{
seg->ptcpheader->wnd = ptcp->rcv_wnd;
}
/* If we don't have a local IP address, we get one by
calling ip_route(). */
ptcp->rtime = 0;
if(ptcp->rttest == 0)
{
ptcp->rttest = tcp_ticks;
ptcp->rtseq = seg->ptcpheader->seqno;
/*DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %lu\n", pcb->rtseq));*/
}
/*DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %lu:%lu\n",
htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) +
seg->len));*/
h_off = (s16_t)(0 - (IP_HEAD_LEN + ETH_HEAD_LEN));
zbuffer_head_adjust( seg->_ori_pbuffer, h_off);
seg->ptcpheader->chksum = 0x00;
seg->ptcpheader->chksum = inet_chksum_pseudo( seg->_ori_pbuffer,
&(ptcp->_psocket->_lipaddr),&(ptcp->_psocket->_ripaddr),
IP_PROTO_TCP,
seg->_ori_pbuffer->tot_len);
h_off = 0 - h_off;
zbuffer_head_adjust( seg->_ori_pbuffer, h_off);
tcp_ip_out(ptcp->_psocket->_pnetif,
&ptcp->_psocket->_ripaddr, &ptcp->_psocket->_lipaddr,
seg->_ori_pbuffer);
}
void tcp_rexmit_seg(tcp_pcb_t *ptcp, tcp_seg_t *seg)
{
u32_t wnd;
s16_t h_off;
/* DEBUGF(TCP_REXMIT_DEBUG, ("tcp_rexmit_seg: skickar %ld:%ld\n",
ntohl(seg->tcphdr->seqno),
ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));*/
wnd = MIN(ptcp->snd_wnd, ptcp->cwnd);
if((seg->ptcpheader->seqno) - ptcp->lastack + seg->len <= wnd)
{
/* Count the number of retranmissions. */
++ptcp->nrtx;
seg->ptcpheader->ackno = (ptcp->rcv_nxt);
seg->ptcpheader->wnd = (ptcp->rcv_wnd);
/* Recalculate checksum. */
/* Recalculate checksum. */
seg->ptcpheader->chksum = 0;
h_off = (s16_t)(0 - (IP_HEAD_LEN + ETH_HEAD_LEN));
zbuffer_head_adjust( seg->_ori_pbuffer, h_off);
seg->ptcpheader->chksum = inet_chksum_pseudo( seg->_ori_pbuffer,
&(ptcp->_psocket->_lipaddr),&(ptcp->_psocket->_ripaddr),
IP_PROTO_TCP,
seg->_ori_pbuffer->tot_len);
h_off = 0 - h_off;
zbuffer_head_adjust( seg->_ori_pbuffer, h_off);
ptcp->rtime = 0;
/* Don't take any rtt measurements after retransmitting. */
ptcp->rttest = 0;
tcp_ip_out(ptcp->_psocket->_pnetif,
&ptcp->_psocket->_ripaddr, &ptcp->_psocket->_lipaddr,
seg->_ori_pbuffer);
}
else
{
/*DEBUGF(TCP_REXMIT_DEBUG, ("tcp_rexmit_seg: no room in window %lu to send %lu (ack %lu)\n",
wnd, ntohl(seg->tcphdr->seqno), pcb->lastack));*/
}
}
s8_t tcp_insert_queue( tcp_pcb_t *ptcp, void *pdata, u16_t data_len,
u8_t flags, u8_t *optdata, u8_t opt_len)
{
tcp_seg_t *seg, *useg, *queue;
u32_t left, seqno;
u16_t seglen, tmp_len;
void *ptr;
u8_t queuelen, i;
s16_t offset;
left = data_len;
ptr = pdata;
if(data_len > ptcp->snd_buf)
{
/*DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: too much data %d\n", len));*/
return ERR_MEM;
}
seqno = ptcp->snd_lbb;
queue = NULL;
/*DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d\n", pcb->snd_queuelen));*/
queuelen = ptcp->snd_queuelen;
if(queuelen >= TCP_SND_QUEUELEN)
{
/*DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: too long queue %d (max %d)\n", queuelen, TCP_SND_QUEUELEN));*/
goto memerr;
}
if(ptcp->snd_queuelen != 0)
{
/* ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL ||
pcb->unsent != NULL); */
}
seg = NULL;
seglen = 0;
while(queue == NULL || left > 0)
{
seglen = left > ptcp->mss? ptcp->mss: left;
/* allocate memory for tcp_seg, and fill in fields */
seg = tcp_seg_new();
if(seg == NULL)
{
/*DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for tcp_seg\n"));*/
goto memerr;
}
if(queue == NULL) {
queue = seg;
} else {
for(useg = queue; useg->next != NULL; useg = useg->next);
useg->next = seg;
}
/* If copy is set, memory should be allocated
and data copied into pbuf, otherwise data comes from
ROM or other static memory, and need not be copied. If
optdata is != NULL, we have options instead of data. */
if(optdata != NULL)
{
seg->_ori_pbuffer = zbuffer_new(TCP_HEAD_LEN + IP_HEAD_LEN + ETH_HEAD_LEN + opt_len);
if (seg->_ori_pbuffer == NULL)
{
goto memerr;
}
++queuelen;
seg->pdata = (void *)((u8_t *)seg->_ori_pbuffer->pdata +
TCP_HEAD_LEN + IP_HEAD_LEN + ETH_HEAD_LEN + opt_len);
seg->pbuffer = seg->_ori_pbuffer;
seg->ptcpheader = (void *)((u8_t *)seg->_ori_pbuffer->pdata +
IP_HEAD_LEN + ETH_HEAD_LEN);
}
else /*there is no optdata*/
{
seg->_ori_pbuffer = zbuffer_new(TCP_HEAD_LEN + IP_HEAD_LEN + ETH_HEAD_LEN + seglen);
if (seg->_ori_pbuffer == NULL)
{
goto memerr;
}
offset = 0 - (TCP_HEAD_LEN + IP_HEAD_LEN + ETH_HEAD_LEN );
if ( zbuffer_head_adjust( seg->_ori_pbuffer, offset) != 0)
{
goto memerr;
}
tmp_len = seglen;
zbuffer_write( seg->_ori_pbuffer, ptr , &tmp_len); /*write data to segment from call*/
if ( tmp_len != seglen)
{
goto memerr;
}
offset *= -1;
if ( zbuffer_head_adjust(seg->_ori_pbuffer, offset) != 0)
{
goto memerr;
}
++queuelen;
seg->pdata = (void *)((u8_t *)seg->_ori_pbuffer->pdata +
TCP_HEAD_LEN + IP_HEAD_LEN + ETH_HEAD_LEN);
seg->pbuffer = seg->_ori_pbuffer;
seg->ptcpheader = (void *)((u8_t *)seg->_ori_pbuffer->pdata +
IP_HEAD_LEN + ETH_HEAD_LEN);
}
if(queuelen > TCP_SND_QUEUELEN)
{
/* DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: queue too long %d (%d)\n", queuelen, TCP_SND_QUEUELEN)); */
goto memerr;
}
seg->len = seglen;
seg->ptcpheader->src = ptcp->_psocket->_lport;
seg->ptcpheader->dest = ptcp->_psocket->_rport;
seg->ptcpheader->seqno = (seqno);
seg->ptcpheader->urgp = 0;
TCPH_FLAGS_SET(seg->ptcpheader, flags);
/* don't fill in tcphdr->ackno and tcphdr->wnd until later */
if(optdata == NULL)
{
TCPH_OFFSET_SET(seg->ptcpheader, 5 << 4);
} else
{
TCPH_OFFSET_SET(seg->ptcpheader, (5 + opt_len / 4) << 4);
/* Copy options into data portion of segment.
Options can thus only be sent in non data carrying
segments such as SYN|ACK. */
for ( i = 0; i < opt_len; i++)
*((u8_t *)seg->ptcpheader + TCP_HEAD_LEN + i) = optdata[i];
}
/*
DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: queueing %lu:%lu (0x%x)\n",
ntohl(seg->tcphdr->seqno),
ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
flags));*/
left -= seglen;
seqno += seglen;
ptr = (void *)((char *)ptr + seglen);
}
/* Go to the last segment on the ->unsent queue. */
if(ptcp->unsent == NULL)
{
useg = NULL;
}
else
{
for(useg = ptcp->unsent; useg->next != NULL; useg = useg->next);
}
/* If there is room in the last pbuf on the unsent queue,
chain the first pbuf on the queue together with that. */
if(useg != NULL &&
TCP_TCPLEN(useg) != 0 &&
!(TCPH_FLAGS(useg->ptcpheader) & (TCP_SYN | TCP_FIN)) &&
!(flags & (TCP_SYN | TCP_FIN)) &&
useg->len + queue->len <= ptcp->mss)
{
/* Remove TCP header from first segment. */
offset = 0 - (TCP_HEAD_LEN + IP_HEAD_LEN + ETH_HEAD_LEN );
queue->_ori_pbuffer = zbuffer_adjust( queue->_ori_pbuffer, offset, 0);
zbuffer_add( useg->_ori_pbuffer, queue->_ori_pbuffer);
useg->len += queue->len; /*TCP's data update*/
useg->next = queue->next;
if(seg == queue)
{
seg = NULL;
}
queue->_ori_pbuffer = NULL; /*we will delete queue so we must set it to NULL*/
queue->next = NULL;
tcp_seg_delete(queue); /*remove this segment*/
queuelen --;
}
else
{
if(useg == NULL)
{
ptcp->unsent = queue;
}
else
{
useg->next = queue;
}
}
if((flags & TCP_SYN) || (flags & TCP_FIN))
{
++data_len;
}
ptcp->snd_lbb += data_len;
ptcp->snd_buf -= data_len;
ptcp->snd_queuelen = queuelen;
/*DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d (after enqueued)\n", pcb->snd_queuelen));*/
if(ptcp->snd_queuelen != 0)
{
/* ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL ||
pcb->unsent != NULL);
*/
}
/* Set the PSH flag in the last segment that we enqueued, but only
if the segment has data (indicated by seglen > 0). */
if(seg != NULL && seglen > 0 && seg->ptcpheader != NULL)
{
TCPH_FLAGS_SET(seg->ptcpheader, TCPH_FLAGS(seg->ptcpheader) | TCP_PSH);
}
return ERR_OK;
memerr:
if(queue != NULL)
{
tcp_seg_delete(queue);
}
if(ptcp->snd_queuelen != 0)
{
/*
ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL ||
pcb->unsent != NULL);
*/
}
/* DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d (with mem err)\n", pcb->snd_queuelen));*/
return ERR_MEM;
}
void tcp_abort(tcp_pcb_t *ptcp, zbuffer_t *pbuffer)
{
ip_header_t *pipheader;
tcp_header_t *ptcpheader;
/* Figure out on which TCP PCB list we are, and remove us. If we
are in an active state, call the receive function associated with
the PCB with a NULL argument, and send an RST to the remote end. */
if(ptcp->state == TIME_WAIT)
{
tcp_pcb_remove(&ptcp_tw_chain, ptcp);
tcp_pcb_delete(ptcp);
}
else if(ptcp->state == LISTEN)
{
tcp_pcb_remove(&ptcp_listen_chain, ptcp);
tcp_pcb_delete(ptcp);
}
else
{
if ( pbuffer != NULL)
{
pipheader = (ip_header_t *)( (u8_t *)pbuffer->pdata + ETH_HEAD_LEN);
ptcpheader = (tcp_header_t *)((u8_t *)pbuffer->pdata + IP_HEAD_LEN + ETH_HEAD_LEN);
tcp_reset(ptcp->_psocket->_pnetif, pipheader, ptcpheader);
}
tcp_pcb_remove(&ptcp_active_chain, ptcp);
tcp_pcb_delete(ptcp);
}
}
/* find out what we can send and send it */
s8_t tcp_output(tcp_pcb_t *ptcp)
{
zbuffer_t *pbuffer;
tcp_header_t *ptcpheader;
tcp_seg_t *seg, *useg;
u32_t wnd;
wnd = MIN(ptcp->snd_wnd, ptcp->cwnd);
seg = ptcp->unsent;
if(ptcp->flags & TF_ACK_NOW)
{
/* If no segments are enqueued but we should send an ACK, we
construct the ACK and send it. */
ptcp->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
pbuffer = zbuffer_new(IP_HEAD_LEN + ETH_HEAD_LEN + TCP_HEAD_LEN );
ptcpheader = (tcp_header_t *)( (u8_t *)pbuffer->pdata + ETH_HEAD_LEN + IP_HEAD_LEN);
/*setup ptcpheader's information*/
ptcpheader->src = ptcp->_psocket->_lport;
ptcpheader->dest = ptcp->_psocket->_rport;
ptcpheader->seqno = ptcp->snd_nxt;
ptcpheader->ackno = ptcp->rcv_nxt;
TCPH_FLAGS_SET(ptcpheader, TCP_ACK);
ptcpheader->wnd = ptcp->rcv_wnd;
ptcpheader->urgp = 0;
TCPH_OFFSET_SET(ptcpheader, 5 << 4);
ptcpheader->chksum = 0;
zbuffer_head_adjust(pbuffer, 0 - IP_HEAD_LEN - ETH_HEAD_LEN);
ptcpheader->chksum = inet_chksum_pseudo(pbuffer, &(ptcp->_psocket->_lipaddr),
&(ptcp->_psocket->_ripaddr), IP_PROTO_TCP, TCP_HEAD_LEN);
zbuffer_head_adjust(pbuffer, IP_HEAD_LEN + ETH_HEAD_LEN);
tcp_ip_out(ptcp->_psocket->_pnetif,
&ptcp->_psocket->_ripaddr, &ptcp->_psocket->_lipaddr, pbuffer);
zbuffer_delete(pbuffer);
return ERR_OK;
}
while(seg != NULL &&
(seg->ptcpheader->seqno) - ptcp->lastack + seg->len <= wnd)
{
ptcp->rtime = 0;
ptcp->unsent = seg->next;
seg->next = NULL;
if(ptcp->state != SYN_SENT)
{
TCPH_FLAGS_SET(seg->ptcpheader, TCPH_FLAGS(seg->ptcpheader) | TCP_ACK);
ptcp->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
}
tcp_output_seg(ptcp, seg);
ptcp->snd_nxt = (seg->ptcpheader->seqno) + TCP_TCPLEN(seg);
if(TCP_SEQ_LT(ptcp->snd_max, ptcp->snd_nxt))
{
ptcp->snd_max = ptcp->snd_nxt;
}
/* put segment on unacknowledged list if length > 0 */
if(TCP_TCPLEN(seg) > 0)
{
seg->next = NULL;
if(ptcp->unacked == NULL)
{
ptcp->unacked = seg;
}
else
{
for(useg = ptcp->unacked; useg->next != NULL; useg = useg->next);
useg->next = seg;
}
}
else
{
tcp_seg_delete(seg);
}
seg = ptcp->unsent;
}
return ERR_OK;
}