TCP输入
void
tcp_input(m, iphlen)
register struct mbuf *m;
int iphlen;
{
register struct tcpiphdr *ti;
register struct inpcb *inp;
caddr_t optp = NULL;
int optlen;
int len, tlen, off;
register struct tcpcb *tp = 0;
register int tiflags;
struct socket *so;
int todrop, acked, ourfinisacked, needoutput = 0;
short ostate;
struct in_addr laddr;
int dropsocket = 0;
int iss = 0;
u_long tiwin, ts_val, ts_ecr;
int ts_present = 0;
tcpstat.tcps_rcvtotal++; //记录全局变量
ti = mtod(m, struct tcpiphdr *); //将mbuf中的信息转化为TCP/IP Header
if (iphlen > sizeof (struct ip)) //如果存在IP选型,丢弃IP选项
ip_stripoptions(m, (struct mbuf *)0);
if (m->m_len < sizeof (struct tcpiphdr)) { //如果首部mbuf中数据小于40字节,将外部簇中的数据调整到首部mbuf中
if ((m = m_pullup(m, sizeof (struct tcpiphdr))) == 0) {
tcpstat.tcps_rcvshort++;
return;
}
ti = mtod(m, struct tcpiphdr *); //将mbuf中的数据转化为ti
}
tlen = ((struct ip *)ti)->ip_len; //除去IP首部外的数据
len = sizeof (struct ip) + tlen; //算上IP首部的数据
ti->ti_next = ti->ti_prev = 0; //已经将数据报提交给了协议层,所以TCP头部的信息基本上没用了
ti->ti_x1 = 0;
ti->ti_len = (u_short)tlen;
HTONS(ti->ti_len);
if (ti->ti_sum = in_cksum(m, len)) { //计算校验和
tcpstat.tcps_rcvbadsum++;
goto drop;
}
#endif /* TUBA_INCLUDE */
off = ti->ti_off << 2; //获取TCP首部的长度
if (off < sizeof (struct tcphdr) || off > tlen) {
tcpstat.tcps_rcvbadoff++;
goto drop;
}
tlen -= off; //减去TCP首部的信息,获取数据长度
ti->ti_len = tlen;
if (off > sizeof (struct tcphdr)) { //如果TCP首部长度 > 20字节,意味着存在TCP选项
if (m->m_len < sizeof(struct ip) + off) { //如果首部mbuf中的长度小于TCP首部长度+TCP首部选项长度+IP首部长度
if ((m = m_pullup(m, sizeof (struct ip) + off)) == 0) { //调用函数将数据完整的copy进mbuf中
tcpstat.tcps_rcvshort++;
return;
}
ti = mtod(m, struct tcpiphdr *);
}
optlen = off - sizeof (struct tcphdr); //获取TCP选项长度
optp = mtod(m, caddr_t) + sizeof (struct tcpiphdr); //optp是指向第一个选项的指针
if ((optlen == TCPOLEN_TSTAMP_APPA ||
(optlen > TCPOLEN_TSTAMP_APPA &&
optp[TCPOLEN_TSTAMP_APPA] == TCPOPT_EOL)) &&
*(u_long *)optp == htonl(TCPOPT_TSTAMP_HDR) &&
(ti->ti_flags & TH_SYN) == 0) { //快速的处理时间戳选项
ts_present = 1;
ts_val = ntohl(*(u_long *)(optp + 4));
ts_ecr = ntohl(*(u_long *)(optp + 8));
optp = NULL; /* we've parsed the options */
}
}
tiflags = ti->ti_flags;
/*
* Convert TCP protocol specific fields to host format.
*/
NTOHL(ti->ti_seq); //将seq,ack,win以及urp转换为主机字节序
NTOHL(ti->ti_ack);
NTOHS(ti->ti_win);
NTOHS(ti->ti_urp);
/*
* Locate pcb for segment.
*/
findpcb:
inp = tcp_last_inpcb; //获取上一次的Internet PCB
if (inp->inp_lport != ti->ti_dport ||
inp->inp_fport != ti->ti_sport ||
inp->inp_faddr.s_addr != ti->ti_src.s_addr ||
inp->inp_laddr.s_addr != ti->ti_dst.s_addr) { //寻找合适的四元组
inp = in_pcblookup(&tcb, ti->ti_src, ti->ti_sport,
ti->ti_dst, ti->ti_dport, INPLOOKUP_WILDCARD);
if (inp) //找到的话,调整last PCB
tcp_last_inpcb = inp;
++tcpstat.tcps_pcbcachemiss;
}
if (inp == 0) //如果没有找到话,丢弃报文段,并发送RST
goto dropwithreset;
tp = intotcpcb(inp); //获取TCP PCB
if (tp == 0) //如果没有找到TCP PCB,说明插口已经关闭,丢弃数据报并发送RST
goto dropwithreset;
if (tp->t_state == TCPS_CLOSED) //如果目前的状态为CLOSED,直接丢弃数据报。
goto drop;
if ((tiflags & TH_SYN) == 0) //如果置位了窗口大小改变选项,调整窗口大小,只有在SYN报文段中才能支持窗口大小改变选项
tiwin = ti->ti_win << tp->snd_scale;
else
tiwin = ti->ti_win;
so = inp->inp_socket; //获取插口
if (so->so_options & (SO_DEBUG|SO_ACCEPTCONN)) { //如果插口正处于调试状态,保存TCP与IP的首部
if (so->so_options & SO_DEBUG) {
ostate = tp->t_state;
tcp_saveti = *ti;
}
if (so->so_options & SO_ACCEPTCONN) { //如果目前处于ACCEPTION状态,调用函数分配新的插口,Internet PCB和TCP PCB
so = sonewconn(so, 0);
if (so == 0)
goto drop;
dropsocket++;
inp = (struct inpcb *)so->so_pcb; //填充本地地址与端口
inp->inp_laddr = ti->ti_dst;
inp->inp_lport = ti->ti_dport;
#if BSD>=43
inp->inp_options = ip_srcroute(); //获取数据报源路由选项
#endif
tp = intotcpcb(inp);
tp->t_state = TCPS_LISTEN; //将新创建的SOCKET状态调整为LISTEN
while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
TCP_MAXWIN << tp->request_r_scale < so->so_rcv.sb_hiwat) //计算窗口的缩放因子
tp->request_r_scale++;
}
}
tp->t_idle = 0; //将空闲时间置0
tp->t_timer[TCPT_KEEP] = tcp_keepidle; //重新设置Keep-Alive选项
if (optp && tp->t_state != TCPS_LISTEN) //如果存在选项,并且当前的状态不处于LISTEN状态,处理TCP选项
tcp_dooptions(tp, optp, optlen, ti,
&ts_present, &ts_val, &ts_ecr);
if (tp->t_state == TCPS_ESTABLISHED &&
(tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK &&
(!ts_present || TSTMP_GEQ(ts_val, tp->ts_recent)) &&
ti->ti_seq == tp->rcv_nxt &&
tiwin && tiwin == tp->snd_wnd &&
tp->snd_nxt == tp->snd_max) { //如果处于连接状态,没有设置特殊的选项,携带的时间戳选项必须大于以前收到的值,收到的seq=期望的seq,通告窗口存在并等于发送时的窗口,下一个发送的报文段不是重传报文段
if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) &&
SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len)) { //如果存在时间戳选项,根据时间戳选项更新TCP PCB中的信息
tp->ts_recent_age = tcp_now;
tp->ts_recent = ts_val;
}
if (ti->ti_len == 0) { //处理ACK
if (SEQ_GT(ti->ti_ack, tp->snd_una) &&
SEQ_LEQ(ti->ti_ack, tp->snd_max) &&
tp->snd_cwnd >= tp->snd_wnd) { //如果确认的seq > 未确认的seq,确认的ack < 最大序号,拥塞窗口大于目前的窗口,即窗口完全打开,不处于慢启动和拥塞避免状态
++tcpstat.tcps_predack; //更新全局变量
if (ts_present) //如果存在事件戳选项,更新RTT值
tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
else if (tp->t_rtt &&
SEQ_GT(ti->ti_ack, tp->t_rtseq))
tcp_xmit_timer(tp, tp->t_rtt);
acked = ti->ti_ack - tp->snd_una; //计算确认的字节数
tcpstat.tcps_rcvackpack++; //修改全局变量
tcpstat.tcps_rcvackbyte += acked;
sbdrop(&so->so_snd, acked); //从发送缓存中丢弃已经确认的数据
tp->snd_una = ti->ti_ack; //更新未确认的数值为刚收到的ACK
m_freem(m); //释放mbuf
if (tp->snd_una == tp->snd_max) //如果确定了所有的数据,关闭重传定时器
tp->t_timer[TCPT_REXMT] = 0;
else if (tp->t_timer[TCPT_PERSIST] == 0) //如果持续定时器没有设定,启动重传定时器
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
if (so->so_snd.sb_flags & SB_NOTIFY) //唤醒等待发送数据的进程
sowwakeup(so);
if (so->so_snd.sb_cc) //如果发送缓存中依旧存在数据,发送数据,然后返回
(void) tcp_output(tp);
return;
}
} else if (ti->ti_ack == tp->snd_una &&
tp->seg_next == (struct tcpiphdr *)tp &&
ti->ti_len <= sbspace(&so->so_rcv)) { //如果收到的报文端的数据>0,ACK是没有被确认的数据,不存在乱序的报文段,并且接收缓存可以容纳所有的数据
++tcpstat.tcps_preddat; //更新全局计数器以及接收缓存
tp->rcv_nxt += ti->ti_len;
tcpstat.tcps_rcvpack++;
tcpstat.tcps_rcvbyte += ti->ti_len;
/*
* Drop TCP, IP headers and TCP options then add data
* to socket buffer.
*/
m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
sbappend(&so->so_rcv, m); //将数据copy 进mbuf中
sorwakeup(so); //唤醒等待接收数据的进程
tp->t_flags |= TF_DELACK; //置位延迟ACK标志位
return;
}
}
//首部预测失败时,执行的代码
m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr); //丢弃TCP,IP以及TCP选项的数据
m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
{ int win;
win = sbspace(&so->so_rcv); //计算接收窗口缓存中的可用字节数
if (win < 0)
win = 0;
tp->rcv_wnd = max(win, (int)(tp->rcv_adv - tp->rcv_nxt)); //获取通告给对方的窗口和接收窗口较大的一个,为了当前的接收窗口不小于当前的通告窗口
}
switch (tp->t_state) {
case TCPS_LISTEN: { //如果当前的状态处于LISTEN
struct mbuf *am;
register struct sockaddr_in *sin;
if (tiflags & TH_RST) //如果置位了RST,丢弃
goto drop;
if (tiflags & TH_ACK) //如果收到了ACK,丢弃并发送RST
goto dropwithreset;
if ((tiflags & TH_SYN) == 0) //丢弃一切非SYN的标志
goto drop;
if (m->m_flags & (M_BCAST|M_MCAST) ||
IN_MULTICAST(ti->ti_dst.s_addr)) //如果收到了多播或者广播之类的数据报,丢弃
goto drop;
am = m_get(M_DONTWAIT, MT_SONAME); //分配一个mbuf,保存sockaddr_in结构,其中带有客户端IP和端口号
if (am == NULL)
goto drop;
am->m_len = sizeof (struct sockaddr_in); //将mbuf的长度设置为sockaddr_in
sin = mtod(am, struct sockaddr_in *);
sin->sin_family = AF_INET; //填充sockaddr_in中的信息
sin->sin_len = sizeof(*sin);
sin->sin_addr = ti->ti_src;
sin->sin_port = ti->ti_sport;
bzero((caddr_t)sin->sin_zero, sizeof(sin->sin_zero));
laddr = inp->inp_laddr; //设定TCP中的本地地址
if (inp->inp_laddr.s_addr == INADDR_ANY) //如果PCB中的本地地址为ANY
inp->inp_laddr = ti->ti_dst; //设置为准确的地址
if (in_pcbconnect(inp, am)) { //设置Internet PCB中的远程地址
inp->inp_laddr = laddr; //如果失败,丢弃报文段 ,等待对方发送超时
(void) m_free(am);
goto drop;
}
(void) m_free(am); //填充完成,释放作为临时存储的mbuf
tp->t_template = tcp_template(tp); //设置新分配的TCP PCB中的TCP IP header
if (tp->t_template == 0) { //如果失败
tp = tcp_drop(tp, ENOBUFS); //丢弃报文段,等待对方发送超时
dropsocket = 0;
goto drop;
}
if (optp) //如果存在TCP选项,丢弃选项
tcp_dooptions(tp, optp, optlen, ti,
&ts_present, &ts_val, &ts_ecr);
if (iss) //设置ISS
tp->iss = iss;
else
tp->iss = tcp_iss;
tcp_iss += TCP_ISSINCR/2;
tp->irs = ti->ti_seq; //设置初始序列号
tcp_sendseqinit(tp); //初始化发送相关的序列号
tcp_rcvseqinit(tp); //初始化接收相关的序列号
tp->t_flags |= TF_ACKNOW; //立刻发送ACK
tp->t_state = TCPS_SYN_RECEIVED; //将目前的状态转化我SYN_RECV
tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; //重新设置Keep-Alive
dropsocket = 0; //跳转到接下来的位置,完成对SYN报文段的处理
tcpstat.tcps_accepts++;
goto trimthenstep6;
}
case TCPS_SYN_SENT: //已经发送了SYN报文段,等待接收SYN报文段以及对发送的SYN报文段的ACK
if ((tiflags & TH_ACK) &&
(SEQ_LEQ(ti->ti_ack, tp->iss) ||
SEQ_GT(ti->ti_ack, tp->snd_max))) //如果收到ACK,ACK小于发送的ISS或者ACK大于最大的发送值,丢弃,并发送RST
goto dropwithreset;
if (tiflags & TH_RST) { //如果收到了RST,并且有携带ACK,说明对端拒绝连接,对端的服务没有启动,丢弃连接并返回差错
if (tiflags & TH_ACK)
tp = tcp_drop(tp, ECONNREFUSED);
goto drop; //如果不携带ACK的话,丢弃连接就好了,等待对方的发送超时
}
if ((tiflags & TH_SYN) == 0) //如果SYN没有置位,丢弃
goto drop;
if (tiflags & TH_ACK) { //如果SYN和ACK都已经置位
tp->snd_una = ti->ti_ack; //将未确认的seq修改为收到的ACK
if (SEQ_LT(tp->snd_nxt, tp->snd_una)) //如果待发送的next小于未被确认的数据,调整待发送的数据
tp->snd_nxt = tp->snd_una;
}
tp->t_timer[TCPT_REXMT] = 0; //关闭连接建立定时器
tp->irs = ti->ti_seq; //初始序号从接收的报文段中获取
tcp_rcvseqinit(tp);
tp->t_flags |= TF_ACKNOW; //将立刻发送ACK置位
if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss)) { //如果收到了ACK并且uma > 连接的ISS(第二个条件是多余的),说明连接已经建立
tcpstat.tcps_connects++; //更新全局计数
soisconnected(so); //设置socket进入连接状态
tp->t_state = TCPS_ESTABLISHED;
if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
(TF_RCVD_SCALE|TF_REQ_SCALE)) { //确定是否远端使用窗口放大选项
tp->snd_scale = tp->requested_s_scale;
tp->rcv_scale = tp->request_r_scale;
}
(void) tcp_reass(tp, (struct tcpiphdr *)0,
(struct mbuf *)0); //如果数据在连接尚未建立之前到达,将数据放到接收缓存中
if (tp->t_rtt) //更新RTT计数器
tcp_xmit_timer(tp, tp->t_rtt);
} else
tp->t_state = TCPS_SYN_RECEIVED; //处理同时打开的情况
trimthenstep6: //处理SYN中携带数据的情况
ti->ti_seq++; //收到了SYN,递增seq
if (ti->ti_len > tp->rcv_wnd) { //如果收到的数据大于接收窗口的大小
todrop = ti->ti_len - tp->rcv_wnd; //获取超出接收窗口的数据的长度
m_adj(m, -todrop); //丢弃超出窗口的长度
ti->ti_len = tp->rcv_wnd; //调整收到的数据的长度
tiflags &= ~TH_FIN; //置位FIN标记
tcpstat.tcps_rcvpackafterwin++; //修改全局数据
tcpstat.tcps_rcvbyteafterwin += todrop;
}
tp->snd_wl1 = ti->ti_seq - 1; //强制更新窗口变量
tp->rcv_up = ti->ti_seq;
goto step6;
}
if (ts_present && (tiflags & TH_RST) == 0 && tp->ts_recent &&
TSTMP_LT(ts_val, tp->ts_recent)) { //处理序号回绕的情况。需要只有32位表示,如果发送速率过快的话,很快就会出现数据回绕的情况
if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) {
tp->ts_recent = 0;
} else {
tcpstat.tcps_rcvduppack++;
tcpstat.tcps_rcvdupbyte += ti->ti_len;
tcpstat.tcps_pawsdrop++;
goto dropafterack;
}
}
todrop = tp->rcv_nxt - ti->ti_seq; //如果发送的数据存在之前已经发送的数据,需要裁剪新到达的数据报
if (todrop > 0) { //如果确实存在重复的数据
if (tiflags & TH_SYN) { //如果已经置位了SYN标志
tiflags &= ~TH_SYN; //清除重复的SYN数据报
ti->ti_seq++; //递增seq
if (ti->ti_urp > 1) //如果存在紧急数据偏移量
ti->ti_urp--;
else
tiflags &= ~TH_URG; //如果不存在,置位URG标志
todrop--;
}
if (todrop >= ti->ti_len) { //如果数据报完全重复
tcpstat.tcps_rcvduppack++; //更新全局统计量
tcpstat.tcps_rcvdupbyte += ti->ti_len;
if ((tiflags & TH_FIN && todrop == ti->ti_len + 1)){ //测试FIN是否重复
todrop = ti->ti_len; //忽略重复的FIN
tiflags &= ~TH_FIN; //清除FIN标记,并且立刻发送ACK标记
tp->t_flags |= TF_ACKNOW;
} else {
if (todrop != 0 || (tiflags & TH_ACK) == 0) //如果携带的数据全部是重复的,并且没有确认本地已发送数据的ACK
goto dropafterack; //丢弃数据,并发送ACK
}
} else {
tcpstat.tcps_rcvpartduppack++; //更新全局统计量
tcpstat.tcps_rcvpartdupbyte += todrop;
}
m_adj(m, todrop); //调整丢弃多余的数据量
ti->ti_seq += todrop;
ti->ti_len -= todrop;
if (ti->ti_urp > todrop) //删除重复的数据,更新紧急数据
ti->ti_urp -= todrop;
else {
tiflags &= ~TH_URG;
ti->ti_urp = 0;
}
}
if ((so->so_state & SS_NOFDREF) &&
tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) { //如果应用程序已经关闭连接,在新收到数据的时候,直接关闭连接,并向对方发送RST
tp = tcp_close(tp);
tcpstat.tcps_rcvafterclose++;
goto dropwithreset;
}
todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd); //计算落在通告窗口右侧的字节数
if (todrop > 0) { //如果有重复的数据
tcpstat.tcps_rcvpackafterwin++;
if (todrop >= ti->ti_len) { //如果数据完全重复
tcpstat.tcps_rcvbyteafterwin += ti->ti_len;
if (tiflags & TH_SYN &&
tp->t_state == TCPS_TIME_WAIT &&
SEQ_GT(ti->ti_seq, tp->rcv_nxt)) { //如果携带SYN,当前状态处于TIME_WAIT,收到的序列号>期望接受的序列号
iss = tp->rcv_nxt + TCP_ISSINCR; //说明对端在连接即将被关闭的情况下想要重新创建连接
tp = tcp_close(tp); //释放已经存在的Internet PCB和TCP PCB,并跳转到起始处重新建立连接
goto findpcb;
}
if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt) { //如果接收窗口没有空间,并且发送的数据seq等于接收端准备接收的数据,意味着,此次接受到的是一个窗口探测报文
tp->t_flags |= TF_ACKNOW; //立刻发送ACK确认报文
tcpstat.tcps_rcvwinprobe++;
} else //否则的话报文在窗口之外并且非窗口探测报文,则应该丢弃这个报文段
goto dropafterack;
} else
tcpstat.tcps_rcvbyteafterwin += todrop;
m_adj(m, -todrop); //从mbuf链中移除落在右侧的报文段,更新收到的报文段长度
ti->ti_len -= todrop;
tiflags &= ~(TH_PUSH|TH_FIN); //将SYN和FIN置位
}
if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) &&
SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len +
((tiflags & (TH_SYN|TH_FIN)) != 0))) { //还是处理TCP的时间戳选项
tp->ts_recent_age = tcp_now;
tp->ts_recent = ts_val;
}
if (tiflags&TH_RST) switch (tp->t_state) { //如果收到了RST标志,根据此刻的状态,做不同的处理。最后都会关闭SOCKET,区别在于会返回不同的错误
case TCPS_SYN_RECEIVED: //如果在连接未建立阶段出现RST,直接关闭
so->so_error = ECONNREFUSED;
goto close;
case TCPS_ESTABLISHED: //如果在连接状态或者在连接未完全关闭阶段出现RST,直接关闭
case TCPS_FIN_WAIT_1:
case TCPS_FIN_WAIT_2:
case TCPS_CLOSE_WAIT:
so->so_error = ECONNRESET;
close:
tp->t_state = TCPS_CLOSED;
tcpstat.tcps_drops++;
tp = tcp_close(tp);
goto drop;
case TCPS_CLOSING:
case TCPS_LAST_ACK:
case TCPS_TIME_WAIT:
tp = tcp_close(tp);
goto drop;
}
if (tiflags & TH_SYN) { //如果此时的SYN依旧置位,丢弃报文并向对端发送RST
tp = tcp_drop(tp, ECONNRESET);
goto dropwithreset;
}
if ((tiflags & TH_ACK) == 0) //如果ACK没有置位,直接丢弃连接
goto drop;
//接下来是根据ACK更新窗口的数据
switch (tp->t_state) {
case TCPS_SYN_RECEIVED: //如果在Recv状态下收到了ACK。这种情况下,将处理被动打开以及同时打开的情况。
if (SEQ_GT(tp->snd_una, ti->ti_ack) ||
SEQ_GT(ti->ti_ack, tp->snd_max)) //如果未确认的seq > 接收到的ACK或者收到的ACK > 接收端的max seq,丢弃报文段并发送RST
goto dropwithreset; //一般情况下,收到的ACK应该介于未确认的seq以及max seq之间
tcpstat.tcps_connects++;
soisconnected(so); //唤醒在ACCEPT上等待的进程d
tp->t_state = TCPS_ESTABLISHED; //调整连接状态为ESTABLISHED
if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
(TF_RCVD_SCALE|TF_REQ_SCALE)) { //如果设置了窗口大小选项,更新窗口大小
tp->snd_scale = tp->requested_s_scale;
tp->rcv_scale = tp->request_r_scale;
}
(void) tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0); //将乱序队列中的数据提交给recvbuf
tp->snd_wl1 = ti->ti_seq - 1;
case TCPS_ESTABLISHED:
case TCPS_FIN_WAIT_1:
case TCPS_FIN_WAIT_2:
case TCPS_CLOSE_WAIT:
case TCPS_CLOSING:
case TCPS_LAST_ACK:
case TCPS_TIME_WAIT:
//处理重复的ACK
if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) { //如果收到的ACK小于未确认的seq
if (ti->ti_len == 0 && tiwin == tp->snd_wnd) { //如果没有携带数据通告窗口的大小没有改变
tcpstat.tcps_rcvdupack++; //增加重复ACK的计数
if (tp->t_timer[TCPT_REXMT] == 0 ||
ti->ti_ack != tp->snd_una) //如果收到的ACK小于uma或者重传定时器没有设置
tp->t_dupacks = 0; //将重复的ACK计数置0
else if (++tp->t_dupacks == tcprexmtthresh) { //如果重复的ACK == 3,指向拥塞避免算法
tcp_seq onxt = tp->snd_nxt; //获取下一个即将发送的数值
u_int win =
min(tp->snd_wnd, tp->snd_cwnd) / 2 /
tp->t_maxseg; //窗口设置为拥塞窗口或者当前发送窗口中的最小值的一半
if (win < 2) //窗口最小为2
win = 2;
tp->snd_ssthresh = win * tp->t_maxseg; //将慢启动门限设置为:拥塞窗口与发送窗口中较小值的一半
tp->t_timer[TCPT_REXMT] = 0; //关闭重传计数器
tp->t_rtt = 0; //将RTT置0
tp->snd_nxt = ti->ti_ack; //将即将发送的seq修改为收到的ack,也就丢失的报文段(对方期待的报文段)
tp->snd_cwnd = tp->t_maxseg; //将拥塞窗口设置为MSS
(void) tcp_output(tp); //发送数据
tp->snd_cwnd = tp->snd_ssthresh +
tp->t_maxseg * tp->t_dupacks; //调整拥塞窗口为慢启动门限+3×MSS
if (SEQ_GT(onxt, tp->snd_nxt)) //将待发送seq调整为之前保存的seq
tp->snd_nxt = onxt;
goto drop; //然后丢弃本次收到的ACK
} else if (tp->t_dupacks > tcprexmtthresh) { //如果重复的ACK计数 > 3
tp->snd_cwnd += tp->t_maxseg; //每次收到一个ACK,就递增拥塞窗口
(void) tcp_output(tp); //发送数据
goto drop; //丢弃本次收到的ACK
}
} else
tp->t_dupacks = 0; //收到的重复ACK中带有数据,不是一个单纯的ACK计数作用
break;
}
if (tp->t_dupacks > tcprexmtthresh &&
tp->snd_cwnd > tp->snd_ssthresh) //如果收到的重复的ACK计数 > 3,说明这是在收到了四个或者四个以上的ACK后,收到的第一个非重复的ACK。快速重传算法结束
tp->snd_cwnd = tp->snd_ssthresh; //调整拥塞窗口为慢启动门限
tp->t_dupacks = 0; //将重复的ACK计数置0
if (SEQ_GT(ti->ti_ack, tp->snd_max)) { //如果收到的ACK大于还没有发送的数据。可能是高速网络的回绕。
tcpstat.tcps_rcvacktoomuch++; //丢弃数据报并回复一个ACK
goto dropafterack;
}
acked = ti->ti_ack - tp->snd_una; //记录收到的ACK确认的数据量
tcpstat.tcps_rcvackpack++;
tcpstat.tcps_rcvackbyte += acked;
if (ts_present) //如果存在时间戳选项,记录并更新RTT
tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
else if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq))
tcp_xmit_timer(tp,tp->t_rtt);
if (ti->ti_ack == tp->snd_max) {如果已发送了所有数据,关闭重传定时器
tp->t_timer[TCPT_REXMT] = 0;
needoutput = 1;
} else if (tp->t_timer[TCPT_PERSIST] == 0) //如果持续定时器已经被关闭
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; //重启重传定时器
{
register u_int cw = tp->snd_cwnd; //获取拥塞窗口大小
register u_int incr = tp->t_maxseg; //获取MSS
if (cw > tp->snd_ssthresh) //如果拥塞窗口 > 慢启动门限,拥塞窗口的更新速度减慢
incr = incr * incr / cw + incr / 8;
tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<<tp->snd_scale);
}
if (acked > so->so_snd.sb_cc) { //如果确认的字节数 > 发送缓存中的字节数
tp->snd_wnd -= so->so_snd.sb_cc; //在发送窗口中减去已经被确认的缓存
sbdrop(&so->so_snd, (int)so->so_snd.sb_cc); //从socket的缓存中删除所有的字节
ourfinisacked = 1;
} else {
sbdrop(&so->so_snd, acked); //如果发送缓存中的数据没有被完全的确认,从socket的缓存中丢弃已经被确认的字节数
tp->snd_wnd -= acked; //从发送窗口减去发送的字节数
ourfinisacked = 0;
}
if (so->so_snd.sb_flags & SB_NOTIFY) //唤醒等待发送的进程
sowwakeup(so);
tp->snd_una = ti->ti_ack; //调整未被确认的数据为接收到的ACK seq
if (SEQ_LT(tp->snd_nxt, tp->snd_una)) //调整next seq
tp->snd_nxt = tp->snd_una;
//处理四种特殊情况下的ACK
switch (tp->t_state) {
case TCPS_FIN_WAIT_1: //如果处于FIN_WAIT_1时,收到ACK
if (ourfinisacked) { //如果数据已经发送完毕
if (so->so_state & SS_CANTRCVMORE){ //将状态转换为FIN_WAIT_2,并将FIN_WAIT_2定时器置位
soisdisconnected(so);
tp->t_timer[TCPT_2MSL] = tcp_maxidle;
}
tp->t_state = TCPS_FIN_WAIT_2;
}
break;
case TCPS_CLOSING: //如果此时的状态处于CLOSING
if (ourfinisacked) {
tp->t_state = TCPS_TIME_WAIT; //将状态转换为TIME_WAIT
tcp_canceltimers(tp); //取消所有的定时器
tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; //启动2MSL定时器
soisdisconnected(so); //关闭插口
}
break;
case TCPS_LAST_ACK: //如果被动关闭端收到ACK
if (ourfinisacked) { //如果数据已经发送完毕
tp = tcp_close(tp); //直接关闭插口
goto drop;
}
break;
case TCPS_TIME_WAIT: //如果处于TIME_WAIT状态
tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; //对TIME_WAIT重新计数
goto dropafterack; //然后丢弃数据报并发送ACK
}
}
step6:
if ((tiflags & TH_ACK) &&
(SEQ_LT(tp->snd_wl1, ti->ti_seq) || tp->snd_wl1 == ti->ti_seq &&
(SEQ_LT(tp->snd_wl2, ti->ti_ack) ||
tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd))) { //这么多判断的目的是为了更新发送窗口,首先,接收到的是一个ACK等等的条件
/* keep track of pure window updates */
if (ti->ti_len == 0 &&
tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd) //报文段没有携带数据,但是报文段确认了新的数据
tcpstat.tcps_rcvwinupd++;
tp->snd_wnd = tiwin; //更新发送窗口
tp->snd_wl1 = ti->ti_seq; //更新最后接收到的报文段序号
tp->snd_wl2 = ti->ti_ack; //更新最后接受到的ACK序号
if (tp->snd_wnd > tp->max_sndwnd) //调整max seq的位置
tp->max_sndwnd = tp->snd_wnd;
needoutput = 1; //清理了发送缓存,意味着可以继续发送数据
}
if ((tiflags & TH_URG) && ti->ti_urp &&
TCPS_HAVERCVDFIN(tp->t_state) == 0) { //处理URG数据,不重点关注
if (ti->ti_urp + so->so_rcv.sb_cc > sb_max) {
ti->ti_urp = 0; /* XXX */
tiflags &= ~TH_URG; /* XXX */
goto dodata; /* XXX */
}
if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) {
tp->rcv_up = ti->ti_seq + ti->ti_urp;
so->so_oobmark = so->so_rcv.sb_cc +
(tp->rcv_up - tp->rcv_nxt) - 1;
if (so->so_oobmark == 0)
so->so_state |= SS_RCVATMARK;
sohasoutofband(so);
tp->t_oobflags &= ~(TCPOOB_HAVEDATA | TCPOOB_HADDATA);
}
if (ti->ti_urp <= ti->ti_len
#ifdef SO_OOBINLINE
&& (so->so_options & SO_OOBINLINE) == 0
#endif
)
tcp_pulloutofband(so, ti, m);
} else
if (SEQ_GT(tp->rcv_nxt, tp->rcv_up))
tp->rcv_up = tp->rcv_nxt;
dodata: //处理已经接受的数据
if ((ti->ti_len || (tiflags&TH_FIN)) &&
TCPS_HAVERCVDFIN(tp->t_state) == 0) { //如果携带数据,或者FIN置位,并且连接还没有收到过FIN
TCP_REASS(tp, ti, m, so, tiflags); //判断是否需要将数据添加到乱序链表中
len = so->so_rcv.sb_hiwat - (tp->rcv_adv - tp->rcv_nxt); //计算对端发送缓存的大小
} else {
m_freem(m);
tiflags &= ~TH_FIN;
}
//处理带有FIN标志的数据报
//如果A向B发送了FIN,相当于A告诉B:我不打算接收你发送的数据了,但是我还有往里面写数据
//B在接收到FIN之后:那我只读你写的数据,不往管道中发送数据了
if (tiflags & TH_FIN) { //
if (TCPS_HAVERCVDFIN(tp->t_state) == 0) { //如果是接收到的第一个FIN数据报
socantrcvmore(so); //将插口设置为只读
tp->t_flags |= TF_ACKNOW; //立刻发送ACK
tp->rcv_nxt++; //递增recv next
}
switch (tp->t_state) {
case TCPS_SYN_RECEIVED:
case TCPS_ESTABLISHED: //根据当前的状态设置在接收到FIN之后,状态如何变化
tp->t_state = TCPS_CLOSE_WAIT; //如果当前是ESTABLISHED,状态转变为:等待关闭
break;
case TCPS_FIN_WAIT_1: //如果处于FIN_WAIT_1,转换为CLOSING,如果收到ACK,转换为FIN_WAIT_2
tp->t_state = TCPS_CLOSING;
break;
case TCPS_FIN_WAIT_2: //将状态转换为TIME_WAIT
tp->t_state = TCPS_TIME_WAIT;
tcp_canceltimers(tp); //置位所有的定时器
tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; //设置2MSL定时器
soisdisconnected(so); //关闭插口
break;
case TCPS_TIME_WAIT: //如果此时处于TIME_WAIT状态,收到了重复的FIN,重新开始2MSL计时
tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
break;
}
}
if (so->so_options & SO_DEBUG) //如果记录了
tcp_trace(TA_INPUT, ostate, tp, &tcp_saveti, 0);
if (needoutput || (tp->t_flags & TF_ACKNOW)) //如果需要发送数据,就立刻发送数据
(void) tcp_output(tp);
return;
dropafterack: //丢弃收到的数据并立刻发送ACK
if (tiflags & TH_RST)
goto drop;
m_freem(m);
tp->t_flags |= TF_ACKNOW;
(void) tcp_output(tp);
return;
dropwithreset: //丢弃收到的数据并立刻发送RST
if ((tiflags & TH_RST) || m->m_flags & (M_BCAST|M_MCAST) ||
IN_MULTICAST(ti->ti_dst.s_addr))
goto drop;
if (tiflags & TH_ACK)
tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST);
else {
if (tiflags & TH_SYN)
ti->ti_len++;
tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0,
TH_RST|TH_ACK);
}
if (dropsocket)
(void) soabort(so);
return;
drop: //丢弃收到的数据
if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
tcp_trace(TA_DROP, ostate, tp, &tcp_saveti, 0);
m_freem(m);
/* destroy temporarily created socket */
if (dropsocket)
(void) soabort(so);
return;
#ifndef TUBA_INCLUDE
}
tcp_dooptions
- 功能A:处理TCP选项:EOL,NOP,MSS,窗口大小,时间戳
void
tcp_dooptions(tp, cp, cnt, ti, ts_present, ts_val, ts_ecr)
struct tcpcb *tp;
u_char *cp;
int cnt;
struct tcpiphdr *ti;
int *ts_present;
u_long *ts_val, *ts_ecr;
{
u_short mss;
int opt, optlen;
for (; cnt > 0; cnt -= optlen, cp += optlen) {
opt = cp[0];
if (opt == TCPOPT_EOL)
break;
if (opt == TCPOPT_NOP)
optlen = 1;
else {
optlen = cp[1];
if (optlen <= 0)
break;
}
switch (opt) {
default:
continue;
case TCPOPT_MAXSEG: //处理MSS选项
if (optlen != TCPOLEN_MAXSEG) //如果MSS选项的长度!=4,结束处理
continue;
if (!(ti->ti_flags & TH_SYN)) //如果报文段不携带SYN选项,结束处理
continue;
bcopy((char *) cp + 2, (char *) &mss, sizeof(mss)); //将选项中的MSS copy进来,转换字节序,并调用tcp mss函数进行处理
NTOHS(mss);
(void) tcp_mss(tp, mss); /* sets t_maxseg */
break;
case TCPOPT_WINDOW: //如果携带窗口大小选项
if (optlen != TCPOLEN_WINDOW) //如果窗口大小!=4,结束处理
continue;
if (!(ti->ti_flags & TH_SYN)) //如果没有出现在SYN报文段中,结束处理
continue;
tp->t_flags |= TF_RCVD_SCALE; //保存窗口的缩放因子,如果双方都支持窗口大小的选项处理,使用窗口缩放处理功能
tp->requested_s_scale = min(cp[2], TCP_MAX_WINSHIFT);
break;
case TCPOPT_TIMESTAMP: //处理时间戳选项
if (optlen != TCPOLEN_TIMESTAMP) //如果时间戳选项的长度!=10,结束处理
continue;
*ts_present = 1; //标志时间戳选项
bcopy((char *)cp + 2, (char *) ts_val, sizeof(*ts_val)); //保存时间戳的数值
NTOHL(*ts_val);
bcopy((char *)cp + 6, (char *) ts_ecr, sizeof(*ts_ecr));
NTOHL(*ts_ecr);
if (ti->ti_flags & TH_SYN) { //如果在SYN选项中带有时间戳选项
tp->t_flags |= TF_RCVD_TSTMP; //置位标志位
tp->ts_recent = *ts_val; //填充时间戳
tp->ts_recent_age = tcp_now;
}
break;
}
}
}