一、客户端数据分析:
spu@spu:~/code/pcap$ tcpdump -r client_all.pcap -X
reading from file client_all.pcap, link-type EN10MB (Ethernet)
17:58:56.346748 IP 192.168.1.178.55814 > 192.168.1.117.socks: Flags [S], seq 2615205588, win 64240, options [mss 1460,sackOK,TS val 2960015764 ecr 0,nop,wscale 7], length 0
0x0000: 4500 003c 6c75 4000 4006 49cf c0a8 01b2 E..<lu@.@.I.....
0x0010: c0a8 0175 da06 0438 9be0 ded4 0000 0000 ...u...8........
0x0020: a002 faf0 7da0 0000 0204 05b4 0402 080a ....}...........
0x0030: b06e 4194 0000 0000 0103 0307 .nA.........
17:58:56.354743 IP 192.168.1.117.socks > 192.168.1.178.55814: Flags [S.], seq 3996464281, ack 2615205589, win 65160, options [mss 1460,sackOK,TS val 902936060 ecr 2960015764,nop,wscale 7], length 0
0x0000: 4500 003c 0000 4000 4006 b644 c0a8 0175 E..<..@.@..D...u
0x0010: c0a8 01b2 0438 da06 ee35 3499 9be0 ded5 .....8...54.....
0x0020: a012 fe88 6b5a 0000 0204 05b4 0402 080a ....kZ..........
0x0030: 35d1 b5fc b06e 4194 0103 0307 5....nA.....
17:58:56.354773 IP 192.168.1.178.55814 > 192.168.1.117.socks: Flags [.], ack 1, win 502, options [nop,nop,TS val 2960015772 ecr 902936060], length 0
0x0000: 4500 0034 6c76 4000 4006 49d6 c0a8 01b2 E..4lv@.@.I.....
0x0010: c0a8 0175 da06 0438 9be0 ded5 ee35 349a ...u...8.....54.
0x0020: 8010 01f6 96b1 0000 0101 080a b06e 419c .............nA.
0x0030: 35d1 b5fc 5...
spu@spu:~/code/containrc$ ./test/pcap_parse ../pcap/client_all.pcap
Capturing packets from ../pcap/client_all.pcap...
Source IP: 192.168.1.178, Destination IP: 192.168.1.117 syn 1 ack 0 check a07d seq 9be0ded4 ack_seq 0
Source IP: 192.168.1.117, Destination IP: 192.168.1.178 syn 1 ack 1 check 5a6b seq ee353499 ack_seq 9be0ded5
Source IP: 192.168.1.178, Destination IP: 192.168.1.117 syn 0 ack 1 check b196 seq 9be0ded5 ack_seq ee35349a
二、服务端数据分析:
spu@spu:~/code/pcap$ tcpdump -r server_all.pcap -X
reading from file server_all.pcap, link-type EN10MB (Ethernet)
17:58:47.812796 ARP, Request who-has 192.168.1.178 tell 192.168.1.1, length 46
0x0000: 0001 0800 0604 0001 7439 8998 c0f9 c0a8 ........t9......
0x0010: 0101 0000 0000 0000 c0a8 01b2 0000 0000 ................
0x0020: 0000 0000 0000 0000 0000 0000 0000 ..............
17:58:57.302898 IP 192.168.1.178.55814 > 192.168.1.117.socks: Flags [S], seq 2615205588, win 64240, options [mss 1460,sackOK,TS val 2960015764 ecr 0,nop,wscale 7], length 0
0x0000: 4500 003c 6c75 4000 4006 49cf c0a8 01b2 E..<lu@.@.I.....
0x0010: c0a8 0175 da06 0438 9be0 ded4 0000 0000 ...u...8........
0x0020: a002 faf0 7da0 0000 0204 05b4 0402 080a ....}...........
0x0030: b06e 4194 0000 0000 0103 0307 .nA.........
17:58:57.302957 IP 192.168.1.117.socks > 192.168.1.178.55814: Flags [S.], seq 3996464281, ack 2615205589, win 65160, options [mss 1460,sackOK,TS val 902936060 ecr 2960015764,nop,wscale 7], length 0
0x0000: 4500 003c 0000 4000 4006 b644 c0a8 0175 E..<..@.@..D...u
0x0010: c0a8 01b2 0438 da06 ee35 3499 9be0 ded5 .....8...54.....
0x0020: a012 fe88 84a6 0000 0204 05b4 0402 080a ................
0x0030: 35d1 b5fc b06e 4194 0103 0307 5....nA.....
17:58:57.312199 IP 192.168.1.178.55814 > 192.168.1.117.socks: Flags [.], ack 1, win 502, options [nop,nop,TS val 2960015772 ecr 902936060], length 0
0x0000: 4500 0034 6c76 4000 4006 49d6 c0a8 01b2 E..4lv@.@.I.....
0x0010: c0a8 0175 da06 0438 9be0 ded5 ee35 349a ...u...8.....54.
0x0020: 8010 01f6 96b1 0000 0101 080a b06e 419c .............nA.
0x0030: 35d1 b5fc 5...
spu@spu:~/code/containrc$ ./test/pcap_parse ../pcap/server_all.pcap
Capturing packets from ../pcap/server_all.pcap...
Source IP: 192.168.1.178, Destination IP: 192.168.1.117 syn 1 ack 0 check a07d seq 9be0ded4 ack_seq 0
Source IP: 192.168.1.117, Destination IP: 192.168.1.178 syn 1 ack 1 check a684 seq ee353499 ack_seq 9be0ded5
Source IP: 192.168.1.178, Destination IP: 192.168.1.117 syn 0 ack 1 check b196 seq 9be0ded5 ack_seq ee35349a
这里服务端发送的syn ack数据包的check 与客户端收到的syn ack数据包的check 不知道为什么不同。
三 TCP 数据的传递
在Linux内核中,TCP数据的处理涉及多个层次的协议栈,从网络接口层到传输层,最终到达应用层。以下是基于搜索结果对Linux内核协议栈处理TCP数据的代码分析:
- 数据包接收路径(Ingress Path)
当数据包到达网络接口卡(NIC)时,NIC会通过DMA将数据包复制到系统内存,并通过中断通知内核。内核随后分配一个sk_buff结构来保存数据包及其元数据。
1.1 Ethernet 层
内核首先处理以太网头部,验证校验和并应用MAC地址过滤。然后,内核通过ip_rcv()函数处理IP头部,确定数据包的目标地址是否是本机。如果是,内核会调用ip_local_deliver()函数,进一步处理数据包。
1.2 IP 层
在IP层,内核会检查IP头部,处理IP选项,并通过ip_route_input_noref()函数进行路由决策。如果数据包是针对本机的,内核会调用ip_local_deliver_finish()函数,剥离IP头部,并将数据包传递给传输层。
1.3 传输层
对于TCP数据包,内核会调用tcp_v4_rcv()函数。该函数会验证TCP头部,检查TCP校验和,并通过__inet_lookup_skb()函数查找对应的TCP套接字。找到套接字后,内核会将数据包放入套接字接收队列,并通知应用程序有新数据到达。
void tcp_data_ready(struct sock *sk)
{
const struct tcp_sock *tp = tcp_sk(sk);
int avail = tp->rcv_nxt - tp->copied_seq;
if (avail < sk->sk_rcvlowat && !tcp_rmem_pressure(sk) &&
!sock_flag(sk, SOCK_DONE) &&
tcp_receive_window(tp) > inet_csk(sk)->icsk_ack.rcv_mss)
return;
sk->sk_data_ready(sk);
}
- 数据包发送路径(Egress Path)
当应用程序通过套接字发送数据时,内核会构建协议头部,并将数据放入sk_buff结构中。
2.1 传输层
对于TCP数据,内核会调用tcp_sendmsg()函数。该函数会等待TCP连接建立,然后为数据段分配sk_buff结构,并将其放入套接字写队列。内核会根据拥塞控制策略决定是否发送数据,并设置重传定时器。
2.2 IP 层
内核会构建IP头部,并通过ip_queue_xmit()函数将数据包发送到网络接口。
2.3 Ethernet 层
最后,内核会构建以太网头部,并通过dev_queue_xmit()函数将数据包发送到NIC的TX队列。 - Socket 缓冲区(sk_buff)
sk_buff是内核用于处理网络数据包的核心结构。它包含数据包的元数据和指向数据的指针。sk_buff结构允许内核高效地处理和修改数据包,而无需在内存中移动数据。