欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

TCP、UDP、ICMP协议抓包详解

程序员文章站 2022-07-07 11:54:36
...

这里使用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数据包分析

TCP、UDP、ICMP协议抓包详解
这里的通信过程为:客户端连接到服务器,然后客户端输入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数据包分析

TCP、UDP、ICMP协议抓包详解
同样的通信过程,可以看到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数据包分析

TCP、UDP、ICMP协议抓包详解
这里前20字节仍为IP首部,可以看到IP首部的8位协议字段变为了0x01,表示IP协议的上层协议为ICMP协议。

数值 UDP字段 释义
0x08 8位ICMP类型 ICMP报文类型为echo回送服务
0x00 8位代码字段
0xf815 16位校验和
0x1434 0001 取决于报文类型
其余为选项部分+数据部分