[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).
Signed-off-by: Gerrit Renker [EMAIL PROTECTED]
---
include/linux/dccp.h |1
net/dccp/input.c | 88 ++-
net/dccp/proto.c | 88 +--
3 files changed, 131 insertions(+), 46 deletions(-)
--- a/net/dccp/input.c
+++ b/net/dccp/input.c
@@ -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:
+