: Integrate state transitions for passive-close
PATCH 3/4 : Integrate state transitions for passive-close
[DCCP]: Integrate state transitions for passive-close
This adds the necessary state transitions for the two forms of passive-close
* PASSIVE_CLOSE - which is entered when a host receives a Close;
* PASSIVE_CLOSEREQ - which is entered when a client receives a CloseReq.
Here is a detailed account of what the patch does in each state.
1) Receiving CloseReq
----------------------
The pseudo-code in 8.5 says:
Step 13: Process CloseReq
If P.type = CloseReq and S.state < CLOSEREQ,
Generate Close
S.state := CLOSING
Set CLOSING timer.
This means we need to address what to do in CLOSED, LISTEN, REQUEST, RESPOND, PARTOPEN, and OPEN.
* CLOSED: silently ignore - it may be a late or duplicate CloseReq;
* LISTEN/RESPOND: will not appear, since Step 7 is performed first (we know we are the client);
* REQUEST: perform Step 13 directly (no need to enqueue packet);
* OPEN/PARTOPEN: enter PASSIVE_CLOSEREQ so that the application has a chance to process unread data.
When already in PASSIVE_CLOSEREQ, no second CloseReq is enqueued. In any other state, the CloseReq is ignored. I think that this offers some robustness against rare and pathological cases: e.g. a simultaneous close where the client sends a Close and the server a CloseReq. The client will then be retransmitting its Close until it gets the Reset, so ignoring the CloseReq while in state CLOSING is sane.
2) Receiving Close
-------------------
The code below from 8.5 is unconditional.
Step 14: Process Close
If P.type = Close,
Generate Reset(Closed)
Tear down connection
Drop packet and return
Thus we need to consider all states:
* CLOSED: silently ignore, since this can happen when a retransmitted or late Close arrives;
* LISTEN: dccp_rcv_state_process() will generate a Reset ("No Connection");
* REQUEST: perform Step 14 directly (no need to enqueue packet);
* RESPOND: dccp_check_req() will generate a Reset ("Packet Error") -- left it at that;
* OPEN/PARTOPEN: enter PASSIVE_CLOSE so that application has a chance to process unread data;
* CLOSEREQ: server performed active-close -- perform Step 14;
* CLOSING: simultaneous-close: use a tie-breaker to avoid message ping-pong (see comment); * PASSIVE_CLOSEREQ: ignore - the peer has a bug (sending first a CloseReq and now a Close);
* TIMEWAIT: packet is ignored.
Note that the condition of receiving a packet in state CLOSED here is different from the condition "there is no socket for such a connection": the socket still exists, but its state indicates it is unusable.
Last, dccp_finish_passive_close sets either DCCP_CLOSED or DCCP_CLOSING = TCP_CLOSING, so that sk_stream_wait_close() will wait for the final Reset (which will trigger CLOSING => CLOSED).
--- include/linux/dccp.h | 1 net/dccp/input.c | 88 ++++++++++++++++++++++++++++++++++++++++++--------- net/dccp/proto.c | 88 +++++++++++++++++++++++++++++++++------------------ 3 files changed, 131 insertions(+), 46 deletions(-)
net/dccp/input.c +73 -15
@@ -32,16 +32,56 @@ static void dccp_fin(struct sock *sk, st sk->sk_data_ready (sk,0 ); } -static void dccp_rcv_close (struct sock *sk,struct sk_buff *skb) +static int dccp_rcv_close (struct sock *sk,struct sk_buff *skb) { -dccp_send_reset (sk,DCCP_RESET_CODE_CLOSED ); -dccp_fin (sk, skb); -dccp_set_state (sk,DCCP_CLOSED ); -sk_wake_async (sk,1 ,POLL_HUP ); +int queued =0 ; + +switch (sk->sk_state) { +/* +* We ignore Close when received in one of the following states: +* - CLOSED (may be a late or duplicate packet) +* - PASSIVE_CLOSEREQ (the peer has sent a CloseReq earlier) +* - RESPOND (already handled by dccp_check_req) +*/ +case DCCP_CLOSING : +/* +* Simultaneous-close: receiving a Close after sending one. This +* can happen if both client and server perform active-close and +* will result in an endless ping-pong of crossing and retrans- +* mitted Close packets, which only terminates when one of the +* nodes times out (min. 64 seconds). Quicker convergence can be +* achieved when one of the nodes acts as tie-breaker. +* This is ok as both ends are done with data transfer and each +* end is just waiting for the other to acknowledge termination. +*/ +if (dccp_sk (sk)->dccps_role !=DCCP_ROLE_CLIENT ) +break ; +/* fall through */ +case DCCP_REQUESTING : +case DCCP_ACTIVE_CLOSEREQ : +dccp_send_reset (sk,DCCP_RESET_CODE_CLOSED ); +dccp_done (sk); +break ; +case DCCP_OPEN : +case DCCP_PARTOPEN : +/* Give waiting application a chance to read pending data */ + queued =1 ; +dccp_fin (sk, skb); +dccp_set_state (sk,DCCP_PASSIVE_CLOSE ); +/* fall through */ +case DCCP_PASSIVE_CLOSE : +/* +* Retransmitted Close: we have already enqueued the first one. +*/ +sk_wake_async (sk,1 ,POLL_HUP ); + } +return queued; } -static void dccp_rcv_closereq (struct sock *sk,struct sk_buff *skb) +static int dccp_rcv_closereq (struct sock *sk,struct sk_buff *skb) { +int queued =0 ; +/* * Step 7: Check for unexpected packet types * If (S.is_server and P.type = CloseReq)
@@ -50,12 +90,26 @@ static void dccp_rcv_closereq(struct soc*/ if (dccp_sk (sk)->dccps_role !=DCCP_ROLE_CLIENT ) {dccp_send_sync (sk,DCCP_SKB_CB (skb)->dccpd_seq,DCCP_PKT_SYNC ); -return ; +return queued; } -if (sk->sk_state !=DCCP_CLOSING ) +/* Step 13: process relevant Client states < CLOSEREQ */ +switch (sk->sk_state) { +case DCCP_REQUESTING : +dccp_send_close (sk,0 );dccp_set_state (sk,DCCP_CLOSING ); -dccp_send_close (sk,0 ); +break ; +case DCCP_OPEN : +case DCCP_PARTOPEN : +/* Give waiting application a chance to read pending data */ + queued =1 ; +dccp_fin (sk, skb); +dccp_set_state (sk,DCCP_PASSIVE_CLOSEREQ ); +/* fall through */ +case DCCP_PASSIVE_CLOSEREQ : +sk_wake_async (sk,1 ,POLL_HUP ); + } +return queued; }static u8 dccp_reset_code_convert (const u8 code)
@@ -231,11 +285,13 @@ static int __dccp_rcv_established(structdccp_rcv_reset (sk, skb);return 0 ;case DCCP_PKT_CLOSEREQ : -dccp_rcv_closereq (sk, skb); +if (dccp_rcv_closereq (sk, skb)) +return 0 ; goto discard;case DCCP_PKT_CLOSE : -dccp_rcv_close (sk, skb); -return 0 ; +if (dccp_rcv_close (sk, skb)) +return 0 ; + goto discard;case DCCP_PKT_REQUEST :/* Step 7 * or (S.is_server and P.type = Response)
@@ -577,11 +633,13 @@ int dccp_rcv_state_process(struct sock *dccp_send_sync (sk, dcb->dccpd_seq,DCCP_PKT_SYNC ); goto discard; }else if (dh->dccph_type =DCCP_PKT_CLOSEREQ ) { -dccp_rcv_closereq (sk, skb); +if (dccp_rcv_closereq (sk, skb)) +return 0 ; goto discard; }else if (dh->dccph_type =DCCP_PKT_CLOSE ) { -dccp_rcv_close (sk, skb); -return 0 ; +if (dccp_rcv_close (sk, skb)) +return 0 ; + goto discard; }switch (sk->sk_state) {
net/dccp/proto.c +58 -30
126 lines not on this page · raw message
include/linux/dccp.h +0 -1
@@ -260,7 +260,6 @@ enum dccp_state { };#define DCCP_STATE_MASK 0x1f -#define DCCP_ACTION_FIN (1<<7)enum {DCCPF_OPEN =TCPF_ESTABLISHED ,
Arnaldo Carvalho de Melo
AppliedEm Wed, Nov 28, 2007 at 08:35:10AM +0000, Gerrit Renker escreveu:
Quoted 52 lines
[DCCP]: Integrate state transitions for passive-close
This adds the necessary state transitions for the two forms of passive-close
*
PASSIVE_CLOSE- which is entered when a host receives a Close;
*PASSIVE_CLOSEREQ- which is entered when a client receives a CloseReq.Here is a detailed account of what the patch does in each state.
1) Receiving CloseReq
----------------------
The pseudo-code in 8.5 says:Step 13: Process CloseReq
If P.type = CloseReq and S.state < CLOSEREQ,
Generate Close
S.state := CLOSING
Set CLOSING timer.This means we need to address what to do in CLOSED, LISTEN, REQUEST, RESPOND, PARTOPEN, and OPEN.
* CLOSED: silently ignore - it may be a late or duplicate CloseReq;
* LISTEN/RESPOND: will not appear, since Step 7 is performed first (we know we are the client);
* REQUEST: perform Step 13 directly (no need to enqueue packet);
* OPEN/PARTOPEN: enterPASSIVE_CLOSEREQso that the application has a chance to process unread data.When already in
PASSIVE_CLOSEREQ, no second CloseReq is enqueued. In any other state, the CloseReq is ignored. I think that this offers some robustness against rare and pathological cases: e.g. a simultaneous close where the client sends a Close and the server a CloseReq. The client will then be retransmitting its Close until it gets the Reset, so ignoring the CloseReq while in state CLOSING is sane.2) Receiving Close
-------------------
The code below from 8.5 is unconditional.Step 14: Process Close
If P.type = Close,
Generate Reset(Closed)
Tear down connection
Drop packet and returnThus we need to consider all states:
* CLOSED: silently ignore, since this can happen when a retransmitted or late Close arrives; * LISTEN: dccp_rcv_state_process() will generate a Reset ("No Connection"); * REQUEST: perform Step 14 directly (no need to enqueue packet); * RESPOND: dccp_check_req() will generate a Reset ("Packet Error") -- left it at that; * OPEN/PARTOPEN: enter PASSIVE_CLOSE so that application has a chance to process unread data; * CLOSEREQ: server performed active-close -- perform Step 14; * CLOSING: simultaneous-close: use a tie-breaker to avoid message ping-pong (see comment);*
PASSIVE_CLOSEREQ: ignore - the peer has a bug (sending first a CloseReq and now a Close);
* TIMEWAIT: packet is ignored.
Quoted 7 lines
Note that the condition of receiving a packet in state CLOSED here is different from the condition "there is no socket for such a connection": the socket still exists, but its state indicates it is unusable.
Last,
dccp_finish_passive_closesets eitherDCCP_CLOSEDorDCCP_CLOSING=TCP_CLOSING, so thatsk_stream_wait_close()will wait for the final Reset (which will trigger CLOSING => CLOSED).Signed-off-by Gerrit Renker <[email protected]>
Applied