将状态设置为 TCP_SYN_RECV 后内核如何继续进行三向握手

发布于 2024-12-18 17:13:42 字数 1587 浏览 0 评论 0原文

我试图了解 TCP 三向握手在 Linux 内核版本 2.6.33 中是如何实现的。

我从函数accept()开始,这导致我:

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

现在我陷入了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;
 }

我的问题是程序何时进入

WARN_ON(newsk->sk_state == TCP_SYN_RECV);
  1. 三向握手已完成?

  2. 服务器发送ACK的代码在哪里?

  3. 服务器确认客户端ACK的代码在哪里?

  4. 或者如果我一开始就错了,那么三向握手是否全部在 accpet() 内部实现?

谢谢

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

荒人说梦 2024-12-25 17:13:42

TCP 的实际套接字逻辑位于

net/ipv4/tcp_input.c

套接字状态机中,位于

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

实现所有套接字状态转换(包括三向握手)的函数中。
该函数的调用方式如下:

tcp_v4_rcv => tcp_v4_do_rcv => tcp_rcv_state_process

对于每个收到的 TCP 数据包。该调用由接口驱动程序(即网络适配器的驱动程序)发起。

accept() 仅等待套接字状态从 TCP_LISTEN 更改为 TCP_ESTABLISHED。状态 TCP_LISTEN 由 listen() 设置。实际的状态更改是在 tcp_rcv_state_process 中执行的。

所以这个过程是异步的。调用 accept() 不会直接导致调用 tcp_rcv_state_process

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

金兰素衣 2024-12-25 17:13:42

状态转换涉及许多功能。但一般来说,有一个通用函数只改变状态:tcp_set_state()。

在 net/ipv4/*.c 源中执行 grep:

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);

现在随机选择其中之一:

tcp.c:tcp_fin(),它处理 FIN 状态转换:

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);

所以您可以从上面看到之前的(即 TCP_SYN_RECV)和通过 tcp_set_state() 函数更改状态后。

在另一个函数(也是tcp_input.c)中:

static int tcp_rcv_synsent_state_process()

这里是通过调用tcp_set_state()对TCP_SYN_RECV进行设置,然后继续进行其他处理。

上述任一场景都涵盖了 TCP_SYN_RECV 状态之后的状态处理。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文