Reputation: 1093
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);
the three way handshake already finished?
Where is the code that the server sends the ACK?
Where is the code that the server confirms the ACK from client?
or if I was wrong at the beginning, is the three way handshake is implemented all inside accpet()?
Thanks
Upvotes: 2
Views: 2978
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
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