sliter
sliter

Reputation: 1093

How the kernel continues with the three way handshake after it sets the state to TCP_SYN_RECV

I am trying to understand how the TCP three way handshake is implemented in Linux kernel, version 2.6.33.

I started with function accept() which leads me to:

accept()==>sys_accept()==>sys_accept4()==>inet_accept()==>inet_csk_accept()

Now I am stucked in inet_csk_accept().

struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)
 {
     struct inet_connection_sock *icsk = inet_csk(sk);
     struct sock *newsk;
     int error;

     lock_sock(sk);

     /* We need to make sure that this socket is listening,
      * and that it has something pending.
      */
     error = -EINVAL;
     if (sk->sk_state != TCP_LISTEN)
             goto out_err;

     /* Find already established connection */
     if (reqsk_queue_empty(&icsk->icsk_accept_queue)) {
             long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);

             /* If this is a non blocking socket don't sleep */
             error = -EAGAIN;
             if (!timeo)
                     goto out_err;

             error = inet_csk_wait_for_connect(sk, timeo);
             if (error)
                     goto out_err;
     }

     newsk = reqsk_queue_get_child(&icsk->icsk_accept_queue, sk);
     WARN_ON(newsk->sk_state == TCP_SYN_RECV);
 out:
     release_sock(sk);
     return newsk;
 out_err:
     newsk = NULL;
     *err = error;
     goto out;
 }

My question is when the programs goes to

WARN_ON(newsk->sk_state == TCP_SYN_RECV);
  1. the three way handshake already finished?

  2. Where is the code that the server sends the ACK?

  3. Where is the code that the server confirms the ACK from client?

  4. or if I was wrong at the beginning, is the three way handshake is implemented all inside accpet()?

Thanks

Upvotes: 2

Views: 2978

Answers (2)

Peter Teoh
Peter Teoh

Reputation: 6753

Many functions is involved in the state transition. But in general, there is one general function that just change the state: tcp_set_state().

Doing a grep in net/ipv4/*.c sources:

tcp.c:  tcp_set_state(sk, ns);
tcp.c:      tcp_set_state(sk, TCP_CLOSE);
tcp.c:      tcp_set_state(sk, TCP_CLOSE);
tcp.c:          tcp_set_state(sk, TCP_CLOSE);
tcp.c:          tcp_set_state(sk, TCP_CLOSE);
tcp.c:      tcp_set_state(sk, TCP_CLOSE);
tcp.c:  tcp_set_state(sk, TCP_CLOSE);
tcp_input.c:        tcp_set_state(sk, TCP_CLOSE_WAIT);
tcp_input.c:        tcp_set_state(sk, TCP_CLOSING);
tcp_input.c:    tcp_set_state(sk, TCP_ESTABLISHED);
tcp_input.c:        tcp_set_state(sk, TCP_SYN_RECV);
tcp_input.c:        tcp_set_state(sk, TCP_ESTABLISHED);
tcp_input.c:        tcp_set_state(sk, TCP_FIN_WAIT2);
tcp_ipv4.c: tcp_set_state(sk, TCP_SYN_SENT);
tcp_ipv4.c: tcp_set_state(sk, TCP_CLOSE);

and now randomly picked one of them:

tcp.c: tcp_fin(), which handle the FIN state transition:

static void tcp_fin(struct sock *sk)
{
        struct tcp_sock *tp = tcp_sk(sk);
        const struct dst_entry *dst;

        inet_csk_schedule_ack(sk);

        sk->sk_shutdown |= RCV_SHUTDOWN;
        sock_set_flag(sk, SOCK_DONE);

        switch (sk->sk_state) {
        case TCP_SYN_RECV:
        case TCP_ESTABLISHED:
                /* Move to CLOSE_WAIT */
                tcp_set_state(sk, TCP_CLOSE_WAIT);

So you can see from above the before (ie, TCP_SYN_RECV) and after state changed by the tcp_set_state() function.

In another function (also tcp_input.c):

static int tcp_rcv_synsent_state_process()

Here is the setting to TCP_SYN_RECV via calling tcp_set_state(), and then it continues with other processing as well.

Either one of the above scenario covers the state processing after the TCP_SYN_RECV state.

Upvotes: 0

werewindle
werewindle

Reputation: 3029

Actual socket logic for TCP located in

net/ipv4/tcp_input.c

State machine for socket is located in function

int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
              struct tcphdr *th, unsigned len)

which implement all socket state transitions (including three way handshake). This function is called this way:

tcp_v4_rcv => tcp_v4_do_rcv => tcp_rcv_state_process

for every received TCP packet. This call is initiated by interface driver (i.e. driver for network adapter).

accept() only waits for socket state to be changed from TCP_LISTEN to TCP_ESTABLISHED. And state TCP_LISTEN is set by listen(). Actual state change is performed in tcp_rcv_state_process.

So this process is asynchronous. Call to accept() does not directly lead to call to tcp_rcv_state_process

Upvotes: 6

Related Questions