TCP、UDP、ICMP协议抓包详解
这里使用tcpdump对TCP、UDP、ICMP协议进行抓包,并详细解析其内容
1.实验源码
TCP和UDP抓包时使用以下tcp_echoserver.c、tcp_echoclient.c、udp_echoserver.c和udp_echoclient.c进行实验
tcp_echoserver.c
#include <arpa/inet.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
void str_echo(int sockfd) {
char buf[1024];
int n = 0;
while ((n = read(sockfd, buf, 1024)) > 0) {
write(sockfd, buf, n);
}
}
int main(int argc, char **argv)
{
int listenfd, connfd;
struct sockaddr_in servaddr;
signal(SIGCHLD, SIG_IGN);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_aton("127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(5001);
bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
listen(listenfd, 1024);
for ( ; ; ) {
connfd = accept(listenfd, NULL, NULL);
if (fork() == 0) { // child process
close(listenfd);
str_echo(connfd);
exit(0);
}
close(connfd); //father process close connection socket
}
}
tcp_echoclient.c
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
void str_client(FILE* fp, int sockfd) {
char buf[1024], readline[1024];
int n = 0;
while (fgets(readline, 1024, fp) != NULL) {
write(sockfd, readline, strlen(readline));
if ((n = read(sockfd, buf, 1024)) <= 0)
return;
write(STDOUT_FILENO, buf, n);
}
}
int main() {
int sockfd;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_aton("127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(5001);
if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == 0) {
str_client(stdin, sockfd);
}
}
udp_echoserver.c
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define BUF_SIZE 1024
int main(int argc, char **argv)
{
int sockfd, n = 0;
socklen_t addrlen;
char buf[BUF_SIZE];
struct sockaddr_in servaddr, cliaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(5002);
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
for ( ; ; ) {
addrlen = sizeof(cliaddr);
n = recvfrom(sockfd, buf, BUF_SIZE, 0, &cliaddr, &addrlen);
sendto(sockfd, buf, n, 0, &cliaddr, addrlen);
}
return 0;
}
udp_echoclient.c
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define BUF_SIZE 1024
int main() {
int sockfd;
int n = 0;
struct sockaddr_in servaddr;
char buf[BUF_SIZE], readline[BUF_SIZE];
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(5002);
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
while (fgets(readline, BUF_SIZE, stdin) != NULL)
{
sendto(sockfd, readline, strlen(readline), 0, (struct sockaddr*)&servaddr, sizeof(servaddr));
n = recvfrom(sockfd, buf, BUF_SIZE, 0, NULL, NULL);
write(STDOUT_FILENO, buf, n);
}
}
2.TCP数据包分析
这里的通信过程为:客户端连接到服务器,然后客户端输入AAAA并得到回射,最后客户端退出断开连接。这里一共捕获到十个IP数据报,其中前三个为TCP连接握手报文段,接下来的四个为数据传送报文段及数据确认报文段,最后三个为TCP断开握手报文段。
TCP三次握手及四次挥手
TCP连接的建立在前三个报文段完成,第一个报文段为客户端发送到服务器的SYN报文段,其sequence字段为3466422460;第二个报文段为服务器发送到客户端的SYN+ACK报文段,其sequence字段为262482848,ack值为3466422461,表示对客户端SYN报文段的确认;最后客户端又向服务器发送了一个ACK报文段,其ack值为262482849,表示对上一个服务端数据包的确认。至此,TCP三次握手完成,服务端和客户端连接建立。
TCP连接的断开在最后三个报文段完成,首先由于客户端进程退出,套接字作为进程资源清理的一部分会被操作系统关闭,由此触发客户端TCP协议栈进行连接断开的四次挥手。首先客户端向服务端发送一个FIN+ACK报文段,其sequence值为3466422466,ack值为262482854,从中可以看出是对服务端上一个数据报的确认;接下来服务端向客户端发送了一个FIN+ACK报文段,其sequence值为262482854,ack值为3466422467,对客户端的FIN报文段进行了确认同时发送了FIN报文段;最后客户端发送一个ACK报文段,其ack值为262482855,对服务端上一个报文段进行确认。至此,TCP断开四次挥手完成。
IP数据报分析
对于IP数据报的具体含义,这里以第四个报文段(客户端向服务器发送数据)进行展开详述,从第一行可以看出客户端IP:端口、服务端IP:端口、数据报序号、上一个数据报确认号、客户端通告窗口、TCP数据长度(4个A加1个回车符)等信息。这里抓取到的是IP数据报,数据报总长度为71字节( [0x00~0x39) ),其中前20字节( [0x00~0x14) )为IP首部,后面51字节为TCP数据报。
数值 IP字段 释义
数值 | IP字段 | 释义 |
---|---|---|
0x5 | 4位头部长度(4字节) | IP头部长度为5个4字节(20字节) |
0x00 | 8位服务类型 | 为应用层提供不同优先服务,例如ssh和telnet需要最小时延服务 |
0x0039 | 16位总长度(字节数) | 这个IP数据报长度为71字节 |
0x6e73 | 16位标识 | 用于唯一标识一个IP数据报,可用于IP分片与重组等(注意它和TCP序号值没有任何关系) |
0x4000 | 3位标志+13位片偏移 | 0x4000=010 0000000000000b,这个IP数据报不允许分片、后面没有更多分片、当前数据报片偏移为0 |
0x40 | 8位生存时间 | 这个IP数据报在网络上最多可经过100跳 |
0x06 | 8位协议 | 用于区分上层协议(如ICMP为1,TCP为6,UDP为17),将IP数据报的数据部分交付到不同的上层协议栈 |
0xce49 | 16位首部校验和 | (注意这里只校验IP首部) |
0x7f 00 00 01 | 32位源IP地址 | 源IP地址为127.0.0.1 |
0x7f 00 00 01 | 32位目的IP地址 | 目的IP地址为127.0.0.1 |
TCP数据报分析
数值 | TCP字段 | 释义 |
---|---|---|
0x831a | 16位源端口号 | 源端口号为33562 |
0x1389 | 16位目的端口号 | 目的端口号为5001 |
0xce9d 68bd | 32位序号 | 此传输方向上的此TCP报文段的序号为3466422461 |
0x0fa5 2ba1 | 32位确认号 | 期待收到对等端编号为262482849的下一个报文段 |
0x8 | 4位头部长度(4字节) | TCP头部长度为32字节(有12字节的选项字段) |
0x0 18 | 6位保留位+6位标志位 | 0x018=0000 0001 1000b,后6位对应为URG,ACK,PSH,PST,SYN,FIN,即ACK和PSH被置位 |
0x0200 | 16位窗口大小 | 本端通告窗口大小位512字节 |
0xfe2d | 16位校验和 | (注意这里对TCP首部和数据都进行校验) |
0x0000 | 16位紧急指针 | |
0x0101 080a 0a98 3852 0a97 ea2a | TCP头部选项字段 | |
0x4141 4141 | 用户数据 | 4个A |
0x0a | 用户数据 | 换行符 |
3.UDP数据包分析
同样的通信过程,可以看到UDP协议传输只有两次报文交换。
这里前20字节仍然是IP首部,因此不再赘述,只对UDP数据报部分进行分析,不过需要说明的是,这里IP首部的8位协议字段变为了0x11即十进制的17,表示IP协议的上层协议为UDP协议。
数值 | UDP字段 | 释义 |
---|---|---|
0x88ae | 16位源端口号 | 源端口号为34990 |
0x138a | 16位目的端口号 | 目的端口号为5002 |
0x000d | 16位UDP数据报长度 | 此UDP数据报长度为13 |
0xfe20 | 16位校验和 | |
0x4142 4344 | 用户数据 | ABCD |
0x0a | 用户数据 | 换行符 |
4.ICMP数据包分析
这里前20字节仍为IP首部,可以看到IP首部的8位协议字段变为了0x01,表示IP协议的上层协议为ICMP协议。
数值 | UDP字段 | 释义 |
---|---|---|
0x08 | 8位ICMP类型 | ICMP报文类型为echo回送服务 |
0x00 | 8位代码字段 | |
0xf815 | 16位校验和 | |
0x1434 0001 | 取决于报文类型 | |
其余为选项部分+数据部分 |
上一篇: H264码流中NALU sps pps IDR帧的理解
下一篇: H.264 编码基础知识