how does tcp server accept a new connection request

summary

Two packets will be proessed by tcp server side:

  1. SYN pakcet: For the first packet(syn) of handshake, it first lookup the listen socket,
    and create a req socket as temporary. Send SYN+ACK packet to client.
  2. ACK packet: For the third packet(ack) of handshake, it will lookup the req socket created
    in previous steps.

SYN 报文calltrace

内核版本 v6.14
####

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
=> tcp_v4_rcv(struct sk_buff *skb)
=> => sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
=> => tcp_v4_do_rcv(sk, skb);
=> => => struct sock *nsk = tcp_v4_hnd_req(sk, skb)
=> => => tcp_rcv_state_process
=> => => => if (icsk->icsk_af_ops->conn_request(sk, skb) < 0) 相当 tcp_v4_conn_request
=> => => => tcp_v4_conn_request(&tcp_request_sock_ops, &tcp_request_sock_ipv4_ops, sk, skb);
=> => => => => inet_reqsk_alloc
=> => => => => => struct request_sock *req = reqsk_alloc(ops, sk_listener, attach_listener);
=> => => => => => ireq->ireq_state = TCP_NEW_SYN_RECV; //#define ireq_state req.__req_common.skc_state
=> => => => => tcp_openreq_init(req, &tmp_opt, skb);
=> => => => => inet_csk_reqsk_queue_hash_add
=> => => => => => reqsk_queue_hash_req
=> => => => => => => inet_ehash_insert //将 req sock放到 establish hash链表里
=> => => => => => inet_csk_reqsk_queue_added
=> => => => => => => reqsk_queue_added(&inet_csk(sk)->icsk_accept_queue); //半链接队列,真实名字叫 accept。 功能也只有计数能力了,没有队列了。
=> => => => => => => => atomic_inc(&queue->young);
=> => => => => => => => atomic_inc(&queue->qlen);
=> => => => => af_ops->send_synack //发送synack报文
=> => => => return // tcp_v4_conn_request
=> => => return; // tcp_rcv_state_process
注意
  • tcp_request_sock_ops: 是一个结构体的名字,同时又是一个变量的名字。
  • icsk_accept_queue: 在结构体struct inet_connection_sock里的这个队列并不在保存半链接队列的 req socket,而是计数。

Read More

how to create a inet socket

Data Structure

1
2
3
4
5
1019 static const struct net_proto_family inet_family_ops = {
1020 .family = PF_INET,
1021 .create = inet_create,
1022 .owner = THIS_MODULE,
1023 };

call trace

1
2
3
4
5
6
> inet_create
> > search the right inetsw array element with type/protocol
> > sock->ops = answer->ops;
> > k = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);
> > sock_init_data(sock, sk);
> > init the sk

Read More

system call socket

Summary

System call socket will do two things:

  1. create a struct socket *sock.
    Mainly done by __sock_create, which alloc a struct socket *sock,
    then init it with the creating method of net_families[family].
  2. map the sock to a file descriptor by sock_map_fd.
    TODO…..

call trace:

1
2
3
4
5
6
7
8
9
10
11
12
> socket
> > sock_create
> > > __sock_create
> > > > sock = sock_alloc();
> > > > sock->type = type;
> > > > rcu_read_lock();
> > > > pf = rcu_dereference(net_families[family]);
> > > > try_module_get(pf->owner))
> > > > rcu_read_unlock();
> > > > pf->create(net, sock, protocol, kern);
> > > > module_put(pf->owner)
> > socket_map_fd

Read More

socket net_proto_family

summary

Each family has a corresponding array element of struct net_proto_family,
which will be called in system call socket.

Data Structure

1
2
3
4
5
6
181 struct net_proto_family {
182 int family;
183 int (*create)(struct net *net, struct socket *sock,
184 int protocol, int kern);
185 struct module *owner;
186 };

The create is important, which is first and basic function during
system call socket.

1
2
164 static DEFINE_SPINLOCK(net_family_lock);
165 static const struct net_proto_family __rcu *net_families[NPROTO] __read_mostly;

Read More