📄 tcp_input.c
字号:
/* * Roadrunner/pk * Copyright (C) 1989-2002 Cornfed Systems, Inc. * * The Roadrunner/pk operating system is free software; you can * redistribute and/or modify it under the terms of the GNU General * Public License, version 2, as published by the Free Software * Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * More information about the Roadrunner/pk operating system of * which this file is a part is available on the World-Wide Web * at: http://www.cornfed.com. * */#include <net/ether.h>#include <net/tcp.h>#if _DEBUG_TCP#include <stdio.h>#endif#include <stdlib.h>#include <sys/intr.h>#include <sys/timer.h>/* XXX This is temporary during testing */static tcp_seq tcp_iss = 100;#if _DEBUG_RETRvoid tcp_dump_retr(char *s, buf_t b);#endifstatic tcp_seqtcp_next_iss(){ tcp_seq iss = tcp_iss; /* XXX Is this satisfactory? */ tcp_iss += 0x1000000; return iss;}static voidtcp_ack_data(tcb_t tcb, int len){ buf_t b; etherhdr_t eh; iphdr_t ih; tcphdr_t th; int datalen; disable; for (b = tcb->retr.h; b != NULL; b = tcb->retr.h) { eh = (etherhdr_t) bstart(b); ih = (iphdr_t) eh->data; th = (tcphdr_t) (eh->data + IP_HLEN(ih)); datalen = NET2HS(ih->len) - IP_HLEN(ih) - TCP_HLEN(th); if (datalen > len) break; /* Remove segment from retransmission queue */#if _DEBUG_RETR tcp_dump_retr("tcp_ack_data", tcb->retr.h);#endif bdeq(&(tcb->retr)); _bfree(b); tcb->snd_una += datalen; len -= datalen; } enable;}voidtcp_input(buf_t b){ etherhdr_t eh = (etherhdr_t) bstart(b); iphdr_t ih = (iphdr_t) eh->data; tcphdr_t th = (tcphdr_t) (eh->data + IP_HLEN(ih)); tcb_t tcb = NULL; proc_t proc; struct timeval tv; int freebuf = 1, hdrlen, len, segcnt, sendack = 0; /* Convert to host order */ ih->len = NET2HS(ih->len); ih->src = NET2HL(ih->src); ih->dst = NET2HL(ih->dst); th->sport = NET2HS(th->sport); th->dport = NET2HS(th->dport); th->seq = NET2HL(th->seq); th->ack = NET2HL(th->ack); th->win = NET2HS(th->win); th->urp = NET2HS(th->urp);#if _DEBUG_TCP tcp_dump_segment("tcp_input", th);#endif /* XXX Compute checksum */ /* Locate TCP control block */ tcb = tcb_find(IP_ADDR_ANY, TCP_PORT_ANY, ih->dst, th->dport, TCB_MATCH_WILDCARDS); if (tcb == NULL) { /* XXX Drop with reset */ brel(b); return; } /* XXX RST processing */ /* SYN processing */ if (th->flags & TF_SYN) { switch (tcb->state) { case TCPS_LISTEN: /* Complete passive open */ if (th->flags & (TF_RST | TF_ACK)) { brel(b); return; } tcb->socket->dst = ih->src; tcb->socket->dport = th->sport; tcb->socket->state = SS_CONNECTING; tcb->iss = tcp_next_iss(); tcb->snd_una = tcb->iss; tcb->snd_nxt = tcb->iss; tcb->snd_up = tcb->iss; tcb->irs = th->seq; tcb->rcv_nxt = tcb->irs + 1; tcb->state = TCPS_SYN_RECEIVED; tcp_send_syn(tcb); break; case TCPS_SYN_SENT: break; default: break; } } /* Compute segment data length */ len = ih->len - IP_HLEN(ih) - TCP_HLEN(th); /* Segment data processing */ if (len > 0) { /* * Segment will be placed in either the socket receive queue or * the reassembly queue. In either case, the segment buffer does * not need to be released. */ freebuf = 0; if (th->seq == tcb->rcv_nxt && tcb->reass.h == NULL && tcb->state == TCPS_ESTABLISHED) { /* * Place segment in socket receive queue */ hdrlen = ETHER_HDR_LEN + IP_HLEN(ih) + TCP_HLEN(th); bstart(b) = bstart(b) + hdrlen; blen(b) = len; tcb->socket->rcv_cc += len; tcb->rcv_nxt += len; disable; benq(b, &(tcb->socket->rcvq)); enable; /* Restart waiting receivers */ disable; for (;;) { proc = remfirstq(&(tcb->socket->rcvr)); if (proc == NULL) break; proc->state = PS_READY; insq(proc, &ready); } enable; /* Generate ACK (XXX no delayed ACKs for the moment...) */ sendack = 1; } else if (SEQ_LT(th->seq, tcb->irs)) {#if _DEBUG_TCP kprintf("tcp_input: segment before irs seq %u irs %u\n", th->seq, tcb->irs);#endif brel(b); return; } else {#if _DEBUG_TCP if (SEQ_LT(th->seq, tcb->rcv_nxt)) { kprintf("tcp_input: "); kprintf("retransmitted segment seq %u rcv_nxt %u\n", th->seq, tcb->rcv_nxt); } if (SEQ_GT(th->seq, tcb->rcv_nxt) { kprintf("tcp_input: "); kprintf("out of order segment seq %u rcv_nxt %u\n", th->seq, tcb->rcv_nxt);} if (tcb->reass.h != NULL) kprintf("tcp_input: reassembly queue not empty\n"); if (tcb->state != TCPS_ESTABLISHED) kprintf("tcp_input: data for connection in %s state\n", tcb_states[tcb->state]);#endif /* Place segment in reassembly queue */ segcnt = tcp_reass(b, tcb); if (segcnt < 0) { brel(b); return;} if (segcnt > 0) sendack = 1;} } /* ACK processing */ if (th->flags & TF_ACK) { if (tcb->state == TCPS_SYN_RECEIVED) { tcb->state = TCPS_ESTABLISHED; tcb->socket->state = SS_CONNECTED; /* Remove the SYN packet from the retransmit queue */ tcp_ack_data(tcb, 1); /* Schedule first process waiting for a connection */ disable; proc = remfirstq(&(tcb->socket->conn)); if (proc != NULL) { proc->state = PS_READY; insq(proc, &ready);} enable;} else if (TCPS_HAVEESTABLISHED(tcb->state)) { if (SEQ_GT(th->ack, tcb->snd_nxt)) { /* XXX ACK for data not yet sent */#if _DEBUG_TCP kprintf("tcp_input: ack for data not yet sent\n");#endif } else if (SEQ_LEQ(th->ack, tcb->snd_una)) { /* XXX Duplicate ACK */#if _DEBUG_TCP kprintf("tcp_input: duplicate ack\n");#endif } else { int acklen = th->ack - tcb->snd_una - 1; /* Remove acknowledged data from retransmission queue */ tcp_ack_data(tcb, acklen);} if (tcb->state == TCPS_CLOSING) { tcb->state = TCPS_TIME_WAIT; tv.tv_sec = 2 * TCP_MSL; tv.tv_usec = 0; timer_start("tcp TIME_WAIT", &tv, (timer_func_t) tcp_close, tcb);} else if (tcb->state == TCPS_LAST_ACK) tcp_close(tcb);} } /* FIN processing */ if (th->flags & TF_FIN) { if (!TCPS_HAVERCVDFIN(tcb->state)) tcb->rcv_nxt++; switch (tcb->state) { case TCPS_ESTABLISHED: tcb->state = TCPS_CLOSE_WAIT; tcb->socket->state = SS_DISCONNECTING; /* Restart waiting receivers */ disable; for (;;) { proc = remfirstq(&(tcb->socket->rcvr)); if (proc == NULL)break; proc->state = PS_READY; insq(proc, &ready); } enable; break; case TCPS_FIN_WAIT_1: if (th->flags & TF_ACK) { tcb->state = TCPS_TIME_WAIT; tv.tv_sec = 2 * TCP_MSL; tv.tv_usec = 0; timer_start("tcp TIME_WAIT", &tv,(timer_func_t) tcp_close, tcb); } else tcb->state = TCPS_CLOSING; break; case TCPS_FIN_WAIT_2: tcb->state = TCPS_TIME_WAIT; tv.tv_sec = 2 * TCP_MSL; tv.tv_usec = 0; timer_start("tcp TIME_WAIT", &tv, (timer_func_t) tcp_close, tcb); break; default: break; } sendack = 1;} if (sendack) tcp_send_ack(tcb); if (freebuf) brel(b);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -