TCP/IP协议栈之二---------运输层

时间:2021-08-12 10:33:10

2.1TCP 

2.1.1   sys_connect

系统调用sys_connect间接调用了tcp_v4_connect 

tcp_v4_connect  发送SYN (tcp_ipv4.c/ipv4)

    ip_route_connect(寻找路由) 

    __ip_route_output_key 

    ip_route_output_flow 

    tcp_connect(构造一个SYN并发送)  (tcp_output.c/ipv4)

       tcp_transmit_skb (发送一个TCP报文)

       inet_csk_reset_xmit_timer(启动一个超时定时器,等待SYN+ACK) 

 

2.1.2   发送

TCP的写函数最终都调用了tcp_sendmsg 

tcp_sendmsgstruct kiocb *, struct sock*, struct msghdr*)★    (tcp.c/ipv4)

    __tcp_push_appending_frames 

       tcp_write_xmit (sock)tcp_output.c/ipv4

           tcp_transmit_skb(sock,skb) tcp_output.c/ipv4

    tcp_push_one  (输出发送队列上的第一个段

       tcp_transmit_skb    

    tcp_push   (增加PSH标志后将报文发送出去)

       __tcp_push_pending_frames 

        

TCP发送数据共有三种途径__tcp_push_appending_frames,tcp_push_one,tcp_push,其中tcp_push调用了__tcp_push_pending_frames。到底调用哪个或哪些函数取决于是否有PUSH标志、NAGLE是否开启、和一些其他情况。 

__tcp_push_appending_frames是试图一次发送完缓存队列中所有的skb。tcp_push_one先计算拥塞窗口,然后只发送窗口大小的数据,如果窗口大小为0,则不发送任何数据。 

 

 

TCP实际的发送函数,tcp_transmit_skb tcp_output.c/ipv4

tcp_transmit_skb (struct sock *, struct sk_buff *)

    build包头 ,构建TCP首部

    tp->af_specific->queue_xmit=ip_queue_xmit(调用ip层的发送函数发送报文)

tp->af_specific的初始化在函数tcp_v4_init_sock中 。  tcp_v4_init_sock由函数inet_create调用,在inet_create函数中,会初始化sock结构的proto以及socket结构的proto_ops,当然,还会初始化其它信息

 

重要:在tcp_sendmsg函数中,会把msghdr结构体中的数据构造到sock的sk_buff队列中。

 

 

2.1.3    接收

硬件->IP层->运输层收到数据,添加到对应的SOCKET缓冲区中,回复ACK ,由ip_rcv间接调用 

tcp_v4_rcv (skb)

   __tcp_v4_lookup (重要:根据一些参数,查找sock结构) 

       __tcp_v4_lookup_established(在已经建立的连接中找,通过inet_lhashfn在哈希表中查找) 

       tcp_v4_lookup_listener(在监听中的Socket中找,通过inet_lhashfn在哈希表中查找) 

       tcp_prequeue(见后面详细解释) 

       sk->sk_backlog_rcv = tcp_v4_do_rcv(又回到开头) 

    tcp_v4_do_rcv (sock,skb)

        tcp_rcv_established(sock,skb) ---TCP_ESTABLISHED

       tcp_child_process ---若是侦听套口

           tcp_rcv_state_process 

        tcp_rcv_state_process(除ESTABLISHED和TIME_WAIT之外) 

    sk_add_backlog(见后面详细解释) 

    tcp_timewait_state_process(处理在TIME_WAIT和FIN_WAIT_2状态下接收到的段) 

   tcp_v4_timewait_ack(TIME_WAIT) 

       tcp_v4_send_ack(发送ACK) 

 

sock结构被初始化的时候,发送和接收数据的缓冲队列也被初始化完成接收数据用到以下三个队列: 

sk->receive_queue 

sk->prequeue 

sk->sk_backlog 

sk->prequeue:如果sk没有被用户态程序锁定,则先进入prequeue 

sk->receive_queue:接收到数据包的sk_buff链表队列,如果数据包过多,造成receive_queue满,或者sock被用户程序锁定,将转入sk_backlog 

sk->sk_backlog:当sock_owned_by_user函数返回真时候,(sk)->sk_lock.owner被锁定,使用sk_add_backlog()函数(该函数实现非常简单,只是一个为链表添加节点的动作)将SKB加入这个后备队列。 

 

 

tcp_rcv_established 

TCP接受里面最主要的就是tcp_rcv_established和tcp_rcv_state_process了 

tcp_rcv_established (当连接已经正常建立时,处理接收到的TCP报文

   //if(fast path) 

       // 检查包头各字段 

        //tcp_ack(处理CK) 

        //tcp_data_snd_check(发送ACK) 

        1.      tcp_copy_to_iovec(SKB的数据复制到用户空间)

        2. 若1没成功,则__skb_queue_tail(把数据追加到接受缓冲区) 

    else(slow path) 

       tcp_data_queue 

           对滑动窗口、序号做出处理 

           __skb_pull 

            __skb_queue_tail 

           tcp_event_data_recv(更新状态) 

TCP/IP协议栈之二---------运输层


tcp_rcv_state_process 

TCP协议的状态机,状态转移函数。ESTABLISHED和TIME_WAIT状态之外的其他状态都会调用此函数 ,是专门处理 3 次握手协议的状态机处理函数。

tcp_rcv_state_process 

    icsk->icsk_af_ops->conn_request(是tcp_v4_conn_request,LISTEN状态) 

       tcp_v4_send_synack(发送SYN+ACK) 

           ip_build_and_send_pkt 

               ip_local_out 

                   __ip_local_out 

                       nf_hook(dst_output) 

                   dst_output 

   tcp_rcv_synsent_state_process(SYN_SENT) 

    tcp_reset 

    tcp_ack(收到ACK) 

   tcp_set_state(SYN_RECV->ESTABLISHED或者FIN_WAIT1->FIN_WAIT2) 

   tcp_time_wait(CLOSING->TIME_WAIT) 

    tcp_update_metrics(LAST_ACK) 

    ...(都是和TCP协议状态转移相关的东西,这里目的是打通上下,以后慢慢分析) 

 

 

2.1.4  其他

还有两个出镜率较高的函数tcp_v4_send_reset和tcp_v4_send_ack 

tcp_v4_send_reset(发送RST) 

    ip_send_reply 

       ip_route_output_key 

       ip_push_pending_frames 

 

 

tcp_v4_send_ack(发送ACK) 

    ip_send_reply 

       ip_route_output_key 

       ip_push_pending_frames 

 

 

用户自上而下的读函数都间接的调用了tcp_recvmsg 

tcp_recvmsg 

skb_copy_datagram_iovec 

tcp_recv_urg(接受一个字节的URG数据) 

 

 

 

2.2UDP 

 

2.2.1  发送

UDP的写函数都调用了udp_sendmsg 

udp_sendmsg 

    ip_route_output_flow 

    ip_append_data 

    udp_flush_pending_frames 

       ip_flush_pending_frames 

    udp_push_pending_frames 

       ip_push_pending_frames 

 

 

2.2.2  接收

硬件->IP层->运输层收到数据,添加到对应的SOCKET缓冲区中 

由ip_rcv间接调用 

udp_rcv 

    __udp4_lib_rcv 

        if(是多播或广播) 

           __udp4_lib_mcast_deliver 

               udp_queue_rcv_skb(对每个需要接受的UDP SOCKET缓冲调用) 

       __udp4_lib_lookup 

       udp_queue_rcv_skb 

 

 

把数据块sk_buff放到一个sock结构的接受缓存的末尾中 

udp_queue_rcv_skb 

    sock_queue_rcv_skb 

       skb_queue_tail 

 

用户子上而下的读函数都间接的调用了udp_recvmsg 

udp_recvmsg 

__skb_recv_datagram 

skb_copy_datagram_iovec 

skb_copy_and_csum_datagram_iovec 

 

 

2.3RAW

2.3.1  发送

RAW Socket的写函数都调用了raw_sendmsg 

raw_sendmsg 

    ip_route_output_flow 

    if(设置了IP_HDRINCL选项,即自己构造ip头部) 

        raw_send_hdrinc 

    else 

       ip_append_data 

       ip_flush_pending_frames 

       ip_push_pending_frames    (IP层的发送函数,ip_output.c/ipv4)

 

 

2.3.2   接收

自底向上的收包 

raw_rcv 

由ip_forward调用ip_call_ra_chain,然后再调用的raw_rcv 

raw_rcv 

sock_queue_rcv_skb 

skb_queue_tail 

sk->sk_data_ready = sock_def_readable 

waitqueue_active 

sk_wake_async 

 

 

用户子上而下的读函数都间接的调用了raw_recvmsg 

raw_recvmsg 

skb_recv_datagram 

__skb_recv_datagram 

wait_for_packet(如果没有数据,则调用此函数等待数据) 

 

2.4ICMP 

在任何需要发送ICMP报文的时候都会调用此函数 

 

2.4.1  发送

icmp_send 

    __ip_route_output_key 

       ip_route_output_slow 

    ip_route_output_key 

       ip_route_output_flow 

   icmp_push_reply    

       ip_append_data 

       ip_flush_pending_frames 

       ip_push_pending_frames 

 

 

2.4.2  接收

硬件->IP层->运输层收到ICMP数据,作出处理逻辑 

由ip_rcv间接调用 

icmp_rcv 

    完全就是icmp协议的处理逻辑,通过函数指针icmp_pointers[icmph->type].handler调用了一下函数中的某一个

    icmp_discard 

    icmp_unreach 

    icmp_redirect 

    icmp_timestamp 

    icmp_address 

    icmp_address_reply 

    icmp_echo