// net/ipv4/tcp_ipv4.c /* This will initiate an outgoing connection. */ inttcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { // ... /* Socket identity is still unknown (sport may be zero). * However we set state to SYN-SENT and not releasing socket * lock select source port, enter ourselves into the hash tables and * complete initialization after this. */ tcp_set_state(sk, TCP_SYN_SENT); // 设置socket状态为TCP_SYN_SENT // ... err = tcp_connect(sk); // 构建完整的syn报文,并发送 // ... }
// net/ipv4/tcp_output.c /* Build a SYN and send it off. */ inttcp_connect(struct sock *sk) { // ... // 申请 skb,并构造为一个SYN包 buff = sk_stream_alloc_skb(sk, 0, sk->sk_allocation, true); if (unlikely(!buff)) return -ENOBUFS; // ... /* Send off SYN; include data in Fast Open. */ // 添加到发送队列 err = tp->fastopen_req ? tcp_send_syn_data(sk, buff) : tcp_transmit_skb(sk, buff, 1, sk->sk_allocation); if (err == -ECONNREFUSED) return err; // ... /* Timer for repeating the SYN until an answer. */ // 启动重传计时器 inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, inet_csk(sk)->icsk_rto, TCP_RTO_MAX); return0; } EXPORT_SYMBOL(tcp_connect);
使用tcp_transmit_skb函数将包发送出去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// net/ipv4/tcp_output.c /* Build and send a SYN with data and (cached) Fast Open cookie. However, * queue a data-only packet after the regular SYN, such that regular SYNs * are retransmitted on timeouts. Also if the remote SYN-ACK acknowledges * only the SYN sequence, the data are retransmitted in the first ACK. * If cookie is not cached or other error occurs, falls back to send a * regular SYN with Fast Open cookie request option. */ staticinttcp_send_syn_data(struct sock *sk, struct sk_buff *syn) { // ... // 发送SYN报文 err = tcp_transmit_skb(sk, syn_data, 1, sk->sk_allocation); // ... /* data was not sent, put it in write_queue */ __skb_queue_tail(&sk->sk_write_queue, syn_data); // 没发送,添加到发送队列 tp->packets_out -= tcp_skb_pcount(syn_data); }
// net/ipv4/tcp_ipv4.c /* The socket must have it's spinlock held when we get * here, unless it is a TCP_LISTEN socket. * * We have a potential double-lock case here, so even when * doing backlog processing we use the BH locking scheme. * This is because we cannot sleep with the original spinlock * held. */ inttcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) { // ... if (sk->sk_state == TCP_LISTEN) { structsock *nsk = tcp_v4_cookie_check(sk, skb);
if (!nsk) goto discard; if (nsk != sk) { if (tcp_child_process(sk, nsk, skb)) { // 被动监听模式 rsk = nsk; goto reset; } return0; } } else sock_rps_save_rxhash(sk, skb);
// net/ipv4/tcp_minisocks.c /* * Queue segment on the new socket if the new socket is active, * otherwise we just shortcircuit this and continue with * the new socket. * * For the vast majority of cases child->sk_state will be TCP_SYN_RECV * when entering. But other states are possible due to a race condition * where after __inet_lookup_established() fails but before the listener * locked is obtained, other packets cause the same connection to * be created. */
inttcp_child_process(struct sock *parent, struct sock *child, struct sk_buff *skb) __releases(&((child)->sk_lock.slock)) { int ret = 0; int state = child->sk_state;
/* record NAPI ID of child */ sk_mark_napi_id(child, skb);
tcp_segs_in(tcp_sk(child), skb); if (!sock_owned_by_user(child)) { ret = tcp_rcv_state_process(child, skb); /* Wakeup parent, send SIGIO */ if (state == TCP_SYN_RECV && child->sk_state != state) parent->sk_data_ready(parent); } else { /* Alas, it is possible again, because we do lookup * in main socket hash table and lock on listening * socket does not protect us more. */ __sk_add_backlog(child, skb); }
// net/ipv4/tcp_input.c /* * This function implements the receiving procedure of RFC 793 for * all states except ESTABLISHED and TIME_WAIT. * It's called from both tcp_v4_rcv and tcp_v6_rcv and should be * address independent. */
// net/ipv4/tcp_ipv4.c /* * Send a SYN-ACK after having received a SYN. * This still operates on a request_sock only, not on a big * socket. */ staticinttcp_v4_send_synack(conststruct sock *sk, struct dst_entry *dst, struct flowi *fl, struct request_sock *req, struct tcp_fastopen_cookie *foc, enum tcp_synack_type synack_type, struct sk_buff *syn_skb) { // ... // 构造 SYN-ACK包 skb = tcp_make_synack(sk, dst, req, foc, synack_type, syn_skb); // ... err = ip_build_and_send_pkt(skb, sk, ireq->ir_loc_addr, ireq->ir_rmt_addr, rcu_dereference(ireq->ireq_opt), tos); // ... }
1.3 服务端accept(客户端 ACK的发出)
==TODO==
1 2 3 4 5 6 7 8 9 10 11
// net/ipv4/tcp_ipv4.c /* The code following below sending ACKs in SYN-RECV and TIME-WAIT states outside socket context is ugly, certainly. What can I do? */