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_sendmsg(struct 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_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