12 TCP: Finite State Machine Implementation
12 TCP: Finite State Machine Implementation
12 TCP: Finite State Machine Implementation
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
<strong>12</strong><br />
<strong>TCP</strong>: <strong>Finite</strong> <strong>State</strong> <strong>Machine</strong> <strong>Implementation</strong><br />
<strong>12</strong>.2 CLOSED state processing<br />
• 表 示 TCB 己 經 allocated, 但 尚 未 使 用 ( 尚 未 active open or<br />
passive open)<br />
– 若 收 到 一 個 packet (unexpectedly),=> 送 出 一 reset packet<br />
/* tcpclosed.c - tcpclosed */<br />
/*------------------------------------------------------------------------<br />
* tcpclosed - do CLOSED state processing<br />
*------------------------------------------------------------------------<br />
*/<br />
int<br />
tcpclosed(struct tcb *ptcb, struct ep *pep)<br />
{<br />
tcpreset(pep);<br />
return SYSERR;<br />
}<br />
<strong>12</strong>.3 Graceful Shutdown<br />
• use modified 3-way handshake to shut down connections<br />
– shutdown connection is the most complexity for <strong>TCP</strong><br />
SYN_RCVD<br />
AP Close<br />
snd FIN<br />
recv SYN<br />
snd ACK, SYN<br />
FIN_WAIT_1<br />
recv ACK<br />
FIN_WAIT_2<br />
AP Close<br />
snd FIN<br />
passive OPEN<br />
recv ACK<br />
recv FIN<br />
snd ACK<br />
recv FIN,ACK<br />
snd ACK<br />
recv FIN<br />
snd ACK<br />
CLOSED<br />
LISTEN<br />
recv SYN<br />
snd SYN+ACK<br />
ESTABLISHED<br />
CLOSING<br />
TIME_WAIT<br />
recv SYN, ACK<br />
snd ACK<br />
recv ACK<br />
recv FIN<br />
snd ACK<br />
Timeout=2MSL<br />
active OPEN<br />
snd SYN<br />
SYN_SENT<br />
CLOSE_WAIT<br />
AP Close<br />
snd FIN<br />
LAST_ACK<br />
AP Close<br />
or timeout<br />
recv ACK<br />
<strong>12</strong>.4 Timed Delay After Closing<br />
• packet over internet may duplication or delay<br />
– duplication and delay pose a potential problem for <strong>TCP</strong><br />
• to prevent duplicated segments from interfering<br />
with later connections, <strong>TCP</strong> does not delete a TCB<br />
after a connection closes (after 2 * Maximum<br />
Segment Lift Time)<br />
tcpwait()<br />
• Schedules the delayed deletion of a TCB<br />
– Schedule a DELETE event for 2MSL from<br />
– tcpkilltimes(): delete any pending events associated with the<br />
TCB (ex: retransmission evnets)<br />
– tmset(): create a delection event that will occur<br />
<strong>TCP</strong>_TWOMSL time units in the future<br />
• If time out, it causes the timer process to delete the TCB
* tcpwait.c - tcpwait */<br />
#include <br />
#include <br />
#include <br />
/*------------------------------------------------------------------------<br />
* tcpwait - (re)schedule a DELETE event for 2MSL from now<br />
*------------------------------------------------------------------------<br />
*/<br />
int<br />
tcpwait(struct tcb *ptcb)<br />
{<br />
int tcbnum = ptcb - &tcbtab[0];<br />
}<br />
tcpkilltimers(ptcb);<br />
tmset(tcps_oport, <strong>TCP</strong>QLEN, MKEVENT(DELETE, tcbnum), <strong>TCP</strong>_TWOMSL);<br />
return OK;<br />
<strong>12</strong>.5 Time-Wait <strong>State</strong> Processing<br />
• 當 <strong>TCP</strong> graceful shutdown 後 , 會 在 Time-wait state 等 待 2*MSL 時<br />
間 , 以 確 定 屬 於 這 個 connection 的 packet 皆 可 處 理 完 畢 , 不 會 因 太<br />
早 disconnect, 而 使 有 些 後 到 的 packet 無 人 可 回 應 ( 此 無 回 應 的<br />
packet 可 能 造 成 connection 的 另 一 端 機 器 , timeout, 而 造 成 reset or<br />
retransmission.<br />
• 在 此 狀 態 若 收 到 reset packet<br />
– 對 方 想 將 connect reset, 再 重 新 傳 送 , 因 我 已 經 disconnect, => 將 TCB delete,<br />
等 待 對 方 再 送 syn packet, 重 新 建 立 連 線<br />
• 若 收 到 syn packet<br />
– 送 出 reset packet ( 將 對 方 reset). 將 自 己 的 TCB delete 等 待 重 新 建 立 連 線<br />
• 若 其 它 packet, 則 仍 正 常 將 此 packet 做 處 理<br />
– tcpacked(): 處 理 收 到 的 packet 的 ack seq# 與 自 己 的 send window 比 對<br />
– tcpdata(): 處 理 收 到 的 packet 的 data 部 份 : 是 否 在 receive window 內 , 將 其 搬<br />
到 rx buffer 內 , 判 斷 是 否 out of order, 是 否 有 urgent or push flag, 是 否 要 回<br />
送 ack packet, 是 否 要 將 rx buffer 內 的 data 送 給 上 層<br />
– 呼 叫 tcpwait(), 重 新 啟 動 2*MSL timer ( 只 要 對 方 仍 繼 續 send packet, 我 的<br />
2*MSL timer 就 不 會 timeout<br />
/*------------------------------------------------------------------------<br />
* tcptimewait - do TIME_WAIT state input processing<br />
*------------------------------------------------------------------------ */<br />
int tcptimewait(struct tcb *ptcb, struct ep *pep)<br />
{ struct ip *pip = (struct ip *)pep->ep_data;<br />
struct tcp *ptcp = (struct tcp *)pip->ip_data;<br />
}<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_RST)<br />
return tcbdealloc(ptcb);<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_SYN) {<br />
tcpreset(pep);<br />
return tcbdealloc(ptcb);<br />
}<br />
tcpacked(ptcb, pep);<br />
tcpdata(ptcb, pep); /* just ACK any packets */<br />
tcpwait(ptcb);<br />
return OK;<br />
<strong>12</strong>.6 CLOSING state processing<br />
• 若 在 closing state, 表 示 雙 方 都 已 收 到 FIN packet, 即 皆<br />
己 同 意 disconnect<br />
• 在 此 state, 若 收 到 fin 的 ack packet, 則 跳 到 time-wait state<br />
• 在 此 狀 態 若 收 到 reset packet<br />
– 對 方 想 將 connect reset, 再 重 新 傳 送 , 因 我 己 完 全 disconnect, => 將 TCB<br />
delete, 等 待 對 方 再 送 syn packet, 重 新 建 立 連 線<br />
• 若 收 到 syn packet<br />
– 送 出 reset packet ( 將 對 方 reset). 將 自 己 的 TCB delete 等 待 重 新 建 立 連 線<br />
• 其 它 部 份 只 需 呼 叫 tcpacked, 處 理 收 到 packet 的 ack seq 欄 位<br />
• 最 後 判 斷 是 否 收 到 送 出 的 fin packet 的 ack packet, 若 是 , 則 將 狀<br />
態 跳 到 time wait state
*------------------------------------------------------------------------<br />
* tcpclosing - do CLOSING state input processing<br />
*------------------------------------------------------------------------ */<br />
int tcpclosing(struct tcb *ptcb, struct ep *pep)<br />
{ struct ip *pip = (struct ip *)pep->ep_data;<br />
struct tcp *ptcp = (struct tcp *)pip->ip_data;<br />
}<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_RST)<br />
return tcbdealloc(ptcb);<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_SYN) {<br />
tcpreset(pep);<br />
return tcbdealloc(ptcb);<br />
}<br />
tcpacked(ptcb, pep);<br />
if ((ptcb->tcb_code & <strong>TCP</strong>F_FIN) == 0) {<br />
ptcb->tcb_state = <strong>TCP</strong>S_TIMEWAIT;<br />
signal(ptcb->tcb_ocsem); /* wake closer */<br />
tcpwait(ptcb);<br />
}<br />
return OK;<br />
<strong>12</strong>.7 FIN-WAIT 2 <strong>State</strong> Processing<br />
• 當 送 出 的 fin packet 己 被 ack, 則 <strong>TCP</strong> 在 fin-wait 2 state, 等 待 收<br />
到 對 方 的 fin packet<br />
• 在 此 狀 態 , 若 收 到 reset packet, 則 abort 此 connection<br />
– tcpabort(): 將 此 連 線 的 pending events delete, 停 止 收 取 與 送 出 仍 何<br />
data packet, tcb_error 設 成 error, 呼 叫 所 有 等 待 接 收 此 連 線 packet 的<br />
process<br />
• 若 收 到 syn packet, 則 送 出 reset packet, 並 呼 叫 tcpabort()<br />
• 其 它 , 則 呼 叫 tcpacked() 與 tcpdata() 來 處 理 收 到 packet 的 data<br />
與 ack 部 份 , 並 判 斷 是 否 收 到 fin-packet, 且 fin packet 之 前 的 所<br />
有 packet 皆 己 收 到 , 若 是 , 則 連 線 狀 態 變 為 time wait state<br />
/*------------------------------------------------------------------------<br />
* tcpfin2 - do FIN_WAIT_2 state input processing<br />
*------------------------------------------------------------------------ */<br />
int tcpfin2(struct tcb *ptcb, struct ep *pep)<br />
{ struct ip *pip = (struct ip *)pep->ep_data;<br />
struct tcp *ptcp = (struct tcp *)pip->ip_data;<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_RST)<br />
return tcpabort(ptcb, <strong>TCP</strong>E_RESET);<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_SYN) {<br />
tcpreset(pep);<br />
return tcpabort(ptcb, <strong>TCP</strong>E_RESET);<br />
}<br />
if (tcpacked(ptcb, pep) == SYSERR)<br />
return OK;<br />
tcpdata(ptcb, pep); /* for data + FIN ACKing */<br />
}<br />
if (ptcb->tcb_flags & TCBF_RDONE) {<br />
ptcb->tcb_state = <strong>TCP</strong>S_TIMEWAIT;<br />
tcpwait(ptcb);<br />
}<br />
return OK;<br />
<strong>12</strong>.8 FIN-WAIT-1 <strong>State</strong> Processing<br />
( 傳 送 部 分 己 關 閉 , 但 接 收 部 分 尚 未 關 閉 )<br />
• 當 AP 產 生 一 個 close 要 求 時 , <strong>TCP</strong> 送 出 fin<br />
packet, 其 跳 到 fin-wait-1 state, 等 待 對 方 對 此 finpacket<br />
回 ack packet<br />
• 在 此 狀 態 , 若 收 到 fin packet, 表 對 方 亦 已 確 定 要<br />
finish connection, => 回 此 fin packet 一 個 ack<br />
packet, 並 跳 到 closing state<br />
• 若 收 到 之 前 送 出 fin packet 的 ack packet => 跳 到<br />
fin-wait-2 state<br />
• 若 收 到 fin + fin’s ack packet, 表 對 方 己 收 到 我 的<br />
fin packet, 且 對 方 亦 同 意 要 finish connection =><br />
直 接 跳 到 time-wait state
*------------------------------------------------------------------------<br />
* tcpfin1 - do FIN_WAIT_1 state input processing<br />
*------------------------------------------------------------------------ */<br />
int tcpfin1(struct tcb *ptcb, struct ep *pep)<br />
{ struct ip *pip = (struct ip *)pep->ep_data;<br />
struct tcp *ptcp = (struct tcp *)pip->ip_data;<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_RST)<br />
return tcpabort(ptcb, <strong>TCP</strong>E_RESET);<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_SYN) {<br />
tcpreset(pep);<br />
return tcpabort(ptcb, <strong>TCP</strong>E_RESET);<br />
}<br />
if (tcpacked(ptcb, pep) == SYSERR)<br />
return OK;<br />
tcpdata(ptcb, pep);<br />
Receiving down: 表 示 收 到 fin 與 fin 之 前 的 data,<br />
tcpswindow(ptcb, pep);<br />
對 方 已 經 結 束 傳 送 資 料<br />
if (ptcb->tcb_flags & TCBF_RDONE) {<br />
if (ptcb->tcb_code & <strong>TCP</strong>F_FIN)<br />
/* 我 送 出 的 FIN 尚 未 ACKed*/<br />
ptcb->tcb_state = <strong>TCP</strong>S_CLOSING; // 收 到 fin, 但 我 送 出 的 fin 尚 未 ack<br />
else {<br />
ptcb->tcb_state = <strong>TCP</strong>S_TIMEWAIT;// 收 到 fin, 且 收 到 fin ack<br />
signal(ptcb->tcb_ocsem); /* wake closer (AP) */<br />
tcpwait(ptcb);<br />
}<br />
} else if ((ptcb->tcb_code & <strong>TCP</strong>F_FIN) == 0) {<br />
signal(ptcb->tcb_ocsem); /* wake closer */<br />
ptcb->tcb_state = <strong>TCP</strong>S_FINWAIT2; // 收 到 fin ack, 尚 未 收 到 fin<br />
}<br />
return OK;<br />
}<br />
<strong>12</strong>.9 Close-Wait <strong>State</strong> Processing<br />
• 當 被 動 收 到 對 方 的 fin-packet, 則 回 其 ack packet 後 , 將 此 資 訊 ( 利 用 end-of-file)<br />
通 知 AP 後 , 跳 到 close-wait state<br />
• 當 AP 亦 同 意 close 時 , <strong>TCP</strong> 將 送 一 fin packet 給 對 方 , 並 進 入 last-ack state<br />
int tcpclosewait(struct tcb *ptcb, struct ep *pep)<br />
{ struct ip *pip = (struct ip *)pep->ep_data;<br />
struct tcp *ptcp = (struct tcp *)pip->ip_data;<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_RST) {<br />
TcpEstabResets++;<br />
TcpCurrEstab--;<br />
return tcpabort(ptcb, <strong>TCP</strong>E_RESET);<br />
}<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_SYN) {<br />
TcpEstabResets++;<br />
TcpCurrEstab--;<br />
tcpreset(pep);<br />
return tcpabort(ptcb, <strong>TCP</strong>E_RESET);<br />
}<br />
tcpacked(ptcb, pep);<br />
tcpswindow(ptcb, pep);<br />
return OK;<br />
}<br />
<strong>12</strong>.10 Last-ACK <strong>State</strong> Processing<br />
• 等 待 fin-packet’s ack packet<br />
• 若 收 到 一 般 data or fin packet, 則 仍 要 呼 叫 tcpacket() 來 處 理 , 否 則 若<br />
timeout, 會 做 retransmission<br />
int tcplastack(struct tcb *ptcb, struct ep *pep)<br />
{ struct ip *pip = (struct ip *)pep->ep_data;<br />
struct tcp *ptcp = (struct tcp *)pip->ip_data;<br />
}<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_RST)<br />
return tcpabort(ptcb, <strong>TCP</strong>E_RESET);<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_SYN) {<br />
tcpreset(pep);<br />
return tcpabort(ptcb, <strong>TCP</strong>E_RESET);<br />
}<br />
tcpacked(ptcb, pep);<br />
if ((ptcb->tcb_code & <strong>TCP</strong>F_FIN) == 0) // 表 FIN’s packet 已 ack<br />
signal(ptcb->tcb_ocsem); /* close() deallocs */<br />
return OK;<br />
<strong>12</strong>.11 Established <strong>State</strong> Processing<br />
• 當 tcp 連 線 建 立 起 來 時 , 連 線 的 兩 端 皆 在 established<br />
狀 態 ( 正 常 的 data/ack packet)<br />
• 若 收 到 reset packet, 表 示 對 方 要 restarted, abort 連 線<br />
• 若 收 到 syn packet, 則 送 出 reset packet, 並 abort 連 線<br />
• 其 它 , 則 呼 叫 tcpacked() 與 tcpdata() 處 理 收 到 packet<br />
的 data field 與 ack field, 呼 叫 tcpswindow() 來 調 整<br />
send window size<br />
• 若 收 到 fin-packet 且 其 之 前 的 data packet 皆 己 收 到 ,<br />
則 跳 到 close-wait state
*------------------------------------------------------------------------<br />
* tcpestablished - do ESTABLISHED state input processing<br />
*------------------------------------------------------------------------ */<br />
int tcpestablished(struct tcb *ptcb, struct ep *pep)<br />
{ struct ip *pip = (struct ip *)pep->ep_data;<br />
struct tcp *ptcp = (struct tcp *)pip->ip_data;<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_RST) {<br />
TcpEstabResets++;<br />
TcpCurrEstab--;<br />
return tcpabort(ptcb, <strong>TCP</strong>E_RESET); }<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_SYN) {<br />
TcpEstabResets++;<br />
TcpCurrEstab--;<br />
tcpreset(pep);<br />
return tcpabort(ptcb, <strong>TCP</strong>E_RESET);}<br />
if (tcpacked(ptcb, pep) == SYSERR)<br />
return OK;<br />
tcpdata(ptcb, pep);<br />
tcpswindow(ptcb, pep);<br />
if (ptcb->tcb_flags & TCBF_RDONE)<br />
ptcb->tcb_state = <strong>TCP</strong>S_CLOSEWAIT;<br />
return OK;<br />
}<br />
<strong>12</strong>.<strong>12</strong> Processing Urgent Data in a Segment<br />
• tcpdata.c:<br />
– 處 理 收 進 來 packet 的 data 部 份<br />
– 首 先 判 斷 收 進 來 的 packet 是 否 有 urgent flag, 若 有 , 則 根 據 urgent<br />
point, 記 錄 urgent data 的 範 圍 (rup, receive urgent poing), 判 斷 之 前<br />
是 否 仍 有 urgent data 尚 未 收 進 來 , 適 當 調 整 tcb_rupseq, 與 tcb_flags<br />
– 若 收 進 來 的 packet 有 syn flag, 則 需 調 整 tcb_rnext 與 tcp_seq<br />
– 算 出 rwindow 的 wlast, rnext 與 收 到 packet 的 datalen, first, last 等 參 數<br />
值 ( 以 sequence # 為 單 位 ), 將 已 收 進 來 的 與 超 過 rwindow 的 data<br />
discard, 其 餘 的 利 用 pb(rwindow 指 標 ) 與 pp(packet buffer 指 標 )( 以<br />
offset 為 單 位 ), 將 packet 放 在 rwindow 內<br />
– 呼 叫 tcpdodat() 將 收 到 rwindow 的 packet 做 進 一 步 處 理 ( 是 否 要 送 給<br />
AP, 是 否 要 ACK,…)<br />
– 若 因 收 到 的 packet 必 需 要 回 送 一 個 packet 出 去 , 則 需 將 tcb_flags 設<br />
成 TCBF_NEEDOUT, 並 呼 叫 tcpkick() 送 出 適 當 的 packet<br />
/*------------------------------------------------------------------------<br />
* tcpdata - process an input segment's data section<br />
*------------------------------------------------------------------------ */<br />
int tcpdata(struct tcb *ptcb, struct ep *pep)<br />
{<br />
struct ip *pip = (struct ip *)pep->ep_data;<br />
struct tcp *ptcp = (struct tcp *)pip->ip_data;<br />
tcpseq first, last, wlast;<br />
unsigned pb;<br />
int<br />
datalen, rwindow, i, pp;<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_URG) {<br />
int rup = ptcp->tcp_seq + ptcp->tcp_urgptr;<br />
#ifdef BSDURG<br />
rup--;<br />
#endif<br />
if (!(ptcb->tcb_flags & TCBF_RUPOK) ||<br />
SEQCMP(rup, ptcb->tcb_rupseq) > 0) {<br />
ptcb->tcb_rupseq = rup;<br />
ptcb->tcb_flags |= TCBF_RUPOK;<br />
}<br />
}<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_SYN) {<br />
ptcb->tcb_rnext++;<br />
ptcb->tcb_flags |= TCBF_NEEDOUT;<br />
++ptcp->tcp_seq; /* so we start with data */<br />
}<br />
datalen = pip->ip_len - IP_HLEN(pip) - <strong>TCP</strong>_HLEN(ptcp);<br />
rwindow = ptcb->tcb_rbsize - ptcb->tcb_rbcount;<br />
wlast = ptcb->tcb_rnext + rwindow-1;<br />
first = ptcp->tcp_seq;<br />
last = first + datalen - 1;<br />
if (SEQCMP(ptcb->tcb_rnext, first) > 0) {<br />
datalen -= ptcb->tcb_rnext - first;<br />
first = ptcb->tcb_rnext;<br />
}<br />
if (SEQCMP(last, wlast) > 0) {<br />
datalen -= last - wlast;<br />
ptcp->tcp_code &= ~<strong>TCP</strong>F_FIN; /* cutting it off */<br />
}<br />
pb = ptcb->tcb_rbstart + ptcb->tcb_rbcount; /* == rnext, in buf */<br />
pb += first - ptcb->tcb_rnext; /* distance in buf */<br />
pb %= ptcb->tcb_rbsize; /* may wrap */<br />
pp = first - ptcp->tcp_seq; /* distance in packet */<br />
for (i=0; itcb_rcvbuf[pb] = ptcp->tcp_data[pp++];<br />
if (++pb >= ptcb->tcb_rbsize)<br />
pb = 0;<br />
}<br />
tcpdodat(ptcb, ptcp, first, datalen); /* deal with it */<br />
if (ptcb->tcb_flags & TCBF_NEEDOUT)<br />
tcpkick(ptcb);<br />
return OK;<br />
}
<strong>12</strong>.13 Processing other Data in a Segment<br />
• tcpdodata()<br />
– 若 收 到 的 packet 是 in sequence<br />
• 若 data length > 0, 則 呼 叫 flcoalesce() 判 斷 所 收 到 的 packet 是 否 有<br />
將 之 前 的 out of order packet 的 洞 補 起 來 , 是 否 己 到 達 finseq 或<br />
pushseq 等 , wakeup tx 回 傳 ack<br />
• 若 收 到 FIN packet, 則 tcb flags 設 成 TCBF_RDONE( 已 收 到 finish,<br />
即 對 方 不 可 能 再 送 data, 即 將 receive 設 成 done), wakeup tx 回 傳<br />
ack<br />
• 若 收 到 的 packet 有 push 或 urgent flag, 則 將 tcb-flags 設 成<br />
TCBF_PUSH, wakeup tx 回 傳 ack<br />
– 若 收 到 的 packet 是 out of order<br />
• <strong>TCP</strong> 收 到 out of order packet 做 法 和 IP 收 到 segment packet 一 樣 ,<br />
是 利 用 link-list 將 其 串 起 來 , 每 個 node 內 容 為 (seq, datalen)<br />
• 若 收 到 的 packet 具 有 finish 或 push 或 urgent flag, 則 需 記 錄<br />
finish, push, urgent data 的 sequence number<br />
• 呼 叫 tfinsert() 將 收 到 的 packet 放 到 link list<br />
<strong>12</strong>.14 tfinsert(), 將 out of order packet 的 seq 與<br />
data length 以 linked-list 方 式 串 起 來<br />
/*------------------------------------------------------------------------<br />
* tfinsert - add a new <strong>TCP</strong> segment fragment to a TCB sequence queue<br />
*------------------------------------------------------------------------ */<br />
int<br />
tfinsert(struct tcb *ptcb, tcpseq seq, unsigned datalen)<br />
{<br />
struct tcpfrag *tf;<br />
}<br />
if (datalen == 0)<br />
return OK;<br />
tf = (struct tcpfrag *)getmem(sizeof(struct tcpfrag));<br />
tf->tf_seq = seq;<br />
tf->tf_len = datalen;<br />
if (ptcb->tcb_rsegq < 0)<br />
ptcb->tcb_rsegq = newq(N<strong>TCP</strong>FRAG, QF_WAIT);<br />
if (enq(ptcb->tcb_rsegq, tf, -tf->tf_seq) < 0)<br />
freemem(tf, sizeof(struct tcpfrag));<br />
return OK;<br />
int tcpdodat(struct tcb *ptcb,struct tcp *ptcp,tcpseq first,unsigned datalen)<br />
{ int wakeup = 0;<br />
if (ptcb->tcb_rnext == first) {<br />
if (datalen > 0) {<br />
tfcoalesce(ptcb, datalen, ptcp);<br />
ptcb->tcb_flags |= TCBF_NEEDOUT;<br />
wakeup++;<br />
}<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_FIN) {<br />
ptcb->tcb_flags |= TCBF_RDONE|TCBF_NEEDOUT;<br />
ptcb->tcb_rnext++;<br />
wakeup++;<br />
}<br />
if (ptcp->tcp_code & (<strong>TCP</strong>F_PSH | <strong>TCP</strong>F_URG)) {<br />
ptcb->tcb_flags |= TCBF_PUSH;<br />
wakeup++;<br />
}<br />
if (wakeup)<br />
tcpwakeup(READERS, ptcb);<br />
}<br />
} else {<br />
/* process delayed controls */<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_FIN)<br />
ptcb->tcb_finseq = ptcp->tcp_seq + datalen;<br />
if (ptcp->tcp_code & (<strong>TCP</strong>F_PSH | <strong>TCP</strong>F_URG))<br />
ptcb->tcb_pushseq = ptcp->tcp_seq + datalen;<br />
ptcp->tcp_code &= ~(<strong>TCP</strong>F_FIN|<strong>TCP</strong>F_PSH);<br />
tfinsert(ptcb, first, datalen);<br />
}<br />
return OK;<br />
tfcoalesce(): 將 放 到 rwindow 的 in sequence<br />
packet 做 進 一 步 處 理<br />
• 調 整 tcb_rnext 與 tcb_rbcount<br />
• 若 收 進 來 的 packet 為 finish, 則 將 out of order packet queue<br />
清 掉<br />
• 若 收 進 來 的 packet 已 到 達 urgent/push point 位 置 , 則 準 備<br />
將 rwindow 的 data 上 傳 給 AP<br />
• 若 之 前 已 有 out of packet 被 收 進 來 , 則 再 去 判 斷 是 否 已 將<br />
out of order 的 hole 補 起 來 了 , 否 則 return ok<br />
• 利 用 tf-len, tf_seq, 與 tcb-rnext 判 斷 是 否 有 將 洞 補 起 來
*------------------------------------------------------------------------<br />
* tfcoalesce - join <strong>TCP</strong> fragments<br />
*------------------------------------------------------------------------ */<br />
int tfcoalesce(struct tcb *ptcb, unsigned datalen, struct tcp *ptcp)<br />
{<br />
struct tcpfrag *tf = 0;<br />
int<br />
newcount;<br />
ptcb->tcb_rnext += datalen;<br />
ptcb->tcb_rbcount += datalen;<br />
if (ptcb->tcb_rnext == ptcb->tcb_finseq)<br />
goto alldone;<br />
if ((ptcb->tcb_rnext - ptcb->tcb_pushseq) >= 0) {<br />
ptcp->tcp_code |= <strong>TCP</strong>F_PSH;<br />
ptcb->tcb_pushseq = 0;<br />
}<br />
if (ptcb->tcb_rsegq < 0) /* see if this closed a hole */<br />
return OK;<br />
tf = (struct tcpfrag *)deq(ptcb->tcb_rsegq);<br />
if (tf == 0)<br />
return OK;<br />
while ((tf->tf_seq - ptcb->tcb_rnext) tf_len - (ptcb->tcb_rnext - tf->tf_seq);<br />
if (newcount > 0) {<br />
ptcb->tcb_rnext += newcount;<br />
ptcb->tcb_rbcount += newcount;}<br />
if (ptcb->tcb_rnext == ptcb->tcb_finseq)<br />
goto alldone;<br />
if ((ptcb->tcb_rnext - ptcb->tcb_pushseq) >= 0) {<br />
ptcp->tcp_code |= <strong>TCP</strong>F_PSH;<br />
ptcb->tcb_pushseq = 0; }<br />
freemem(tf, sizeof(struct tcpfrag));<br />
tf = (struct tcpfrag *)deq(ptcb->tcb_rsegq);<br />
if (tf == 0) {<br />
freeq(ptcb->tcb_rsegq);<br />
ptcb->tcb_rsegq = EMPTY;<br />
return OK;}<br />
}<br />
enq(ptcb->tcb_rsegq, tf, -tf->tf_seq); /* got one too many */<br />
return OK;<br />
alldone:<br />
if (tf)<br />
freemem(tf, sizeof(struct tcpfrag));<br />
while (tf = (struct tcpfrag *)deq(ptcb->tcb_rsegq))<br />
freemem(tf, sizeof(struct tcpfrag));<br />
freeq(ptcb->tcb_rsegq);<br />
ptcb->tcb_rsegq = EMPTY;<br />
ptcp->tcp_code |= <strong>TCP</strong>F_FIN;<br />
return OK;<br />
}<br />
<strong>12</strong>.15 Aborting a <strong>TCP</strong> connection<br />
/*------------------------------------------------------------------------<br />
* tcpabort - abort an active <strong>TCP</strong> connection<br />
*------------------------------------------------------------------------ */<br />
int<br />
tcpabort(struct tcb *ptcb, int error)<br />
{<br />
tcpkilltimers(ptcb);<br />
ptcb->tcb_flags |= TCBF_RDONE|TCBF_SDONE;<br />
ptcb->tcb_error = error;<br />
tcpwakeup(READERS|WRITERS, ptcb);<br />
return OK;<br />
}<br />
• tcpkilltimers(): delete all pending events for the connection<br />
• 將 send 與 receive status 設 成 done<br />
• 利 用 tcpwakeup(): 將 所 有 等 待 此 connection 的 packet 的 process 叫 起<br />
來<br />
<strong>12</strong>.16 Establish a <strong>TCP</strong> Connection<br />
SYN_RCVD<br />
passive OPEN<br />
recv SYN<br />
snd ACK, SYN<br />
recv ACK<br />
CLOSED<br />
LISTEN<br />
ESTABLISHED<br />
recv SYN, ACK<br />
snd ACK<br />
active OPEN<br />
snd SYN<br />
SYN_SENT<br />
AP Close<br />
or timeout<br />
• 3-way handshake to establish a connection<br />
• Server issues a passive open => listen state<br />
• client issues an active open => send syn packet => syn-sent<br />
• server receives syn packet => send syn+ack packet => syn_revd<br />
• client receives syn+ack packet => send ack packet => established<br />
• server receives ack packet => established
<strong>12</strong>.17 Initializing a TCB<br />
• 當 AP 要 求 active 或 passive open 時 , 則 <strong>TCP</strong> 呼 叫 tcpsync()<br />
去 設 定 一 新 的 連 線 的 TCB 值<br />
• 主 要 設 定 :<br />
– connection state: CLOSED state<br />
– allocate send and receive buffers<br />
– create send and receive semaphores, etc .<br />
int tcpsync(struct tcb *ptcb)<br />
{ ptcb->tcb_state = <strong>TCP</strong>S_CLOSED;<br />
ptcb->tcb_type = <strong>TCP</strong>T_CONNECTION;<br />
ptcb->tcb_iss = ptcb->tcb_suna = ptcb->tcb_snext = tcpiss();<br />
ptcb->tcb_lwack = ptcb->tcb_iss;<br />
ptcb->tcb_sndbuf = (u_char *)getmem(<strong>TCP</strong>SBS);<br />
ptcb->tcb_sbsize = <strong>TCP</strong>SBS;<br />
ptcb->tcb_sbstart = ptcb->tcb_sbcount = 0;<br />
ptcb->tcb_ssema = screate(1);<br />
<strong>12</strong>.18 SYN-SENT <strong>State</strong> Processing<br />
• 如 果 收 到 ack-flag packet 時 , 判 斷 其 sequence # 是 否 為 ack 之<br />
前 送 出 的 syn packet, 若 不 是 , 則 送 一 reset packet<br />
• 如 果 收 到 的 是 reset packet, 則 回 到 closed state, 且 呼 叫<br />
tcpkilltimers(), delete 所 有 此 connection 的 pending events<br />
• 若 收 到 的 是 syn packet ( 可 能 carry data or syn’s ack), 則 設 定<br />
一 些 基 本 參 數 ( 根 據 收 的 的 packet), 如 : swindow, lwseq,<br />
rnext, cwin 等<br />
• 呼 叫 tcpacked(): 處 理 此 packet 的 ack seq#<br />
• 呼 叫 tcpdata(): 處 理 此 packet 的 data 部 份<br />
• 收 到 的 packet 若 有 ack 之 前 的 syn packet, 則 跳 到 established<br />
state, 否 則 , 跳 到 synrcvd state<br />
ptcb->tcb_rcvbuf = (u_char *)getmem(<strong>TCP</strong>RBS);<br />
ptcb->tcb_rbsize = <strong>TCP</strong>RBS;<br />
ptcb->tcb_rbstart = ptcb->tcb_rbcount = 0;<br />
ptcb->tcb_rsegq = EMPTY;<br />
ptcb->tcb_rsema = screate(0);<br />
ptcb->tcb_ocsem = screate(0);<br />
}<br />
/* timer stuff */<br />
ptcb->tcb_srt = 0; /* in sec/100 */<br />
ptcb->tcb_rtde = 0; /* in sec/100 */<br />
ptcb->tcb_rexmt = 50; /* in sec/100 */<br />
ptcb->tcb_rexmtcount = 0;<br />
ptcb->tcb_keep = <strong>12</strong>000; /* in sec/100 */<br />
ptcb->tcb_code = <strong>TCP</strong>F_SYN;<br />
ptcb->tcb_flags = 0;<br />
return OK;<br />
int tcpsynsent(struct tcb *ptcb, struct ep *pep)<br />
{ struct ip *pip = (struct ip *)pep->ep_data;<br />
struct tcp *ptcp = (struct tcp *)pip->ip_data;<br />
if ((ptcp->tcp_code & <strong>TCP</strong>F_ACK) &&<br />
((ptcp->tcp_ack - ptcb->tcb_iss tcp_ack - ptcb->tcb_snext) > 0))<br />
return tcpreset(pep);<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_RST) {<br />
ptcb->tcb_state = <strong>TCP</strong>S_CLOSED;<br />
ptcb->tcb_error = <strong>TCP</strong>E_RESET;<br />
TcpAttemptFails++;<br />
tcpkilltimers(ptcb);<br />
signal(ptcb->tcb_ocsem);<br />
return OK;}<br />
if ((ptcp->tcp_code & <strong>TCP</strong>F_SYN) == 0)<br />
return OK;<br />
ptcb->tcb_swindow = ptcp->tcp_window;<br />
ptcb->tcb_lwseq = ptcp->tcp_seq;<br />
ptcb->tcb_rnext = ptcp->tcp_seq;<br />
ptcb->tcb_cwin = ptcb->tcb_rnext + ptcb->tcb_rbsize;<br />
tcpacked(ptcb, pep);<br />
tcpdata(ptcb, pep);<br />
ptcp->tcp_code &= ~<strong>TCP</strong>F_FIN;<br />
if (ptcb->tcb_code & <strong>TCP</strong>F_SYN) /* our SYN not ACKed */<br />
ptcb->tcb_state = <strong>TCP</strong>S_SYNRCVD;<br />
else {<br />
TcpCurrEstab++;<br />
ptcb->tcb_state = <strong>TCP</strong>S_ESTABLISHED;<br />
signal(ptcb->tcb_ocsem);/* return in open */<br />
}<br />
return OK;<br />
}
<strong>12</strong>.19 SYN-RECEIVED <strong>State</strong> Processing<br />
• listen state receives syn packet => syn-received state<br />
• syn-sent state only received syn packet without an ACK => synreceived<br />
state<br />
• 當 收 到 reset packet 時 , 若 為 server side, 則 將 之 前 為 這 新 連 線 所<br />
allocate 的 TCB delete, 若 為 client side, 則 about 此 connection<br />
• 若 收 到 syn packet=> 送 出 reset packet, 且 abort 此 連 線<br />
• 以 three-way handshake 方 式 , 若 我 在 syn-received state, 則 對 方 可<br />
能 已 在 established state, 所 以 必 須 呼 叫 tcpacked() 與 tcpdata() 來 處<br />
理 收 到 packet 的 ack 與 data 部 份<br />
• 若 為 server side, 則 利 用 tcb_listenq 將 此 connection 的 pseudo<br />
device number 送 給 AP program<br />
int tcpsynrcvd(struct tcb *ptcb, struct ep *pep)<br />
{ struct ip *pip = (struct ip *)pep->ep_data;<br />
struct tcp *ptcp = (struct tcp *)pip->ip_data;<br />
struct tcb *pptcb;<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_RST) {<br />
TcpAttemptFails++;<br />
if (ptcb->tcb_pptcb != 0)<br />
return tcbdealloc(ptcb);<br />
else<br />
return tcpabort(ptcb, <strong>TCP</strong>E_REFUSED); }<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_SYN) {<br />
TcpAttemptFails++;<br />
tcpreset(pep);<br />
return tcpabort(ptcb, <strong>TCP</strong>E_RESET); }<br />
if (tcpacked(ptcb, pep) == SYSERR)<br />
return OK;<br />
if (ptcb->tcb_pptcb != 0) { /* from a passive open */<br />
pptcb = ptcb->tcb_pptcb;<br />
if (wait(pptcb->tcb_mutex) != OK) {<br />
TcpAttemptFails++;<br />
tcpreset(pep);<br />
return tcbdealloc(ptcb); }<br />
if (pptcb->tcb_state != <strong>TCP</strong>S_LISTEN) {<br />
TcpAttemptFails++;<br />
tcpreset(pep);<br />
signal(pptcb->tcb_mutex);<br />
return tcbdealloc(ptcb);<br />
}<br />
if (pcount(pptcb->tcb_listenq) >= pptcb->tcb_lqsize) {<br />
TcpAttemptFails++;<br />
tcpreset(pep);<br />
signal(pptcb->tcb_mutex);<br />
return tcbdealloc(ptcb); }<br />
psend(pptcb->tcb_listenq, ptcb->tcb_dvnum);<br />
signal(pptcb->tcb_mutex);<br />
} else /* from an active open */<br />
signal(ptcb->tcb_ocsem);<br />
TcpCurrEstab++;<br />
ptcb->tcb_state = <strong>TCP</strong>S_ESTABLISHED;<br />
tcpdata(ptcb, pep);<br />
if (ptcb->tcb_flags & TCBF_RDONE)<br />
ptcb->tcb_state = <strong>TCP</strong>S_CLOSEWAIT;<br />
return OK;<br />
}<br />
<strong>12</strong>.20 Listen <strong>State</strong> Processing<br />
• server side wait for new connection request<br />
• It ignores RESET packet because no connection<br />
exists<br />
• send a RESET packet for any incoming segment<br />
other than a SYN packet<br />
• when SYN packet received, it call tcballoc() to<br />
create new TCB for new connection<br />
• the new TCB is children and the original TCB is<br />
parent
int tcplisten(struct tcb *ptcb, struct ep *pep)<br />
{ struct tcb *newptcb, *tcballoc();<br />
struct ip *pip = (struct ip *)pep->ep_data;<br />
struct tcp *ptcp = (struct tcp *)pip->ip_data;<br />
if (ptcp->tcp_code & <strong>TCP</strong>F_RST)<br />
return OK; /* "parent" TCB still in LISTEN */<br />
if ((ptcp->tcp_code & <strong>TCP</strong>F_ACK) ||<br />
(ptcp->tcp_code & <strong>TCP</strong>F_SYN) == 0)<br />
return tcpreset(pep);<br />
newptcb = tcballoc();<br />
if ((int)newptcb == SYSERR || tcpsync(newptcb) == SYSERR)<br />
return SYSERR;<br />
newptcb->tcb_state = <strong>TCP</strong>S_SYNRCVD;<br />
newptcb->tcb_ostate = <strong>TCP</strong>O_IDLE;<br />
newptcb->tcb_error = 0;<br />
newptcb->tcb_pptcb = ptcb; /* for ACCEPT */<br />
newptcb->tcb_rip = pip->ip_src;<br />
newptcb->tcb_rport = ptcp->tcp_sport;<br />
newptcb->tcb_lip = pip->ip_dst;<br />
newptcb->tcb_lport = ptcp->tcp_dport;<br />
tcpwinit(ptcb, newptcb, pep); /* initialize window data */<br />
newptcb->tcb_finseq = newptcb->tcb_pushseq = 0;<br />
newptcb->tcb_flags = TCBF_NEEDOUT;<br />
TcpPassiveOpens++;<br />
tcpdata(newptcb, pep);<br />
signal(newptcb->tcb_mutex);<br />
return OK;<br />
}<br />
<strong>12</strong>.21 Initializing Window Variables for a New TCB<br />
int tcpwinit(struct tcb *ptcb, struct tcb *newptcb, struct ep *pep)<br />
{ struct ip *pip = (struct ip *)pep->ep_data;<br />
struct tcp *ptcp = (struct tcp *)pip->ip_data;<br />
struct route *prt;<br />
unsigned mss;<br />
Bool<br />
local;<br />
newptcb->tcb_swindow = ptcp->tcp_window;<br />
newptcb->tcb_lwseq = ptcp->tcp_seq;<br />
newptcb->tcb_lwack = newptcb->tcb_iss; /* set in tcpsync() */<br />
prt = (struct route *)rtget(pip->ip_src, RTF_REMOTE);<br />
local = prt && prt->rt_metric == 0;<br />
newptcb->tcb_pni = &nif[prt->rt_ifnum];<br />
rtfree(prt);<br />
if (local)<br />
mss = newptcb->tcb_pni->ni_mtu-IPMHLEN-<strong>TCP</strong>MHLEN;<br />
else<br />
mss = 536;/* RFC 1<strong>12</strong>2 */<br />
if (ptcb->tcb_smss) {<br />
newptcb->tcb_smss = min(ptcb->tcb_smss, mss);<br />
ptcb->tcb_smss = 0; /* reset server smss */<br />
} else<br />
newptcb->tcb_smss = mss;<br />
newptcb->tcb_rmss = mss; /* receive mss */<br />
newptcb->tcb_cwnd = newptcb->tcb_smss; /* 1 segment */<br />
newptcb->tcb_ssthresh = 65535; /* IP max window */<br />
newptcb->tcb_rnext = ptcp->tcp_seq;<br />
newptcb->tcb_cwin = newptcb->tcb_rnext + newptcb->tcb_rbsize;<br />
}