Как ядро ​​продолжает трехстороннее рукопожатие после установки состояния TCP_SYN_RECV - PullRequest
1 голос
/ 29 ноября 2011

Я пытаюсь понять, как реализовано трехстороннее рукопожатие TCP в ядре Linux, версия 2.6.33.

Я начал с функции 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 ()?

Спасибо

Ответы [ 2 ]

5 голосов
/ 29 ноября 2011

Фактическая логика сокета для 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

0 голосов
/ 03 марта 2015

Многие функции участвуют в переходе состояния.Но в целом есть одна общая функция, которая просто меняет состояние: tcp_set_state ().

Выполнение grep в источниках net / ipv4 / *. C:

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_SYN_RECV посредством вызова tcp_set_state (), а затем продолжаетсяс другой обработкой, а также.

Любой из приведенных выше сценариев охватывает обработку состояний после состояния TCP_SYN_RECV.

...