31.12.2014 Views

12 TCP: Finite State Machine Implementation

12 TCP: Finite State Machine Implementation

12 TCP: Finite State Machine Implementation

SHOW MORE
SHOW LESS

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 />

}

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!