CENTRAL SOUTH UNIVERSITY
计算机网络课程设计报告
题 目IP数据包的捕获与分析 学生姓名 胡 壮
班级学号 计科1106班 0909112924
指导教师 穆 帅 设计时间 2014年1月
1
目录
第一章 绪论 ............................................................3
1.1课题研究的意义 ..................................................3 1.2捕获数据包的常用方法 ............................................3 第二章 系统需求分析 ....................................................4
2.1课程设计题目与要求 ..............................................4 2.2 IP数据包格式 ...................................................6 2.3程序流程图 ......................................................7 2.4实验环境 ........................................................9 第三章系统总体框架 .....................................................9
3.1套接字模块 ......................................................9 3.2 IP数据包的捕获模块 .............................................9 3.3 IP数据包分析模块 ...............................................9 3.4输出模块 ........................................................9 第四章详细设计与实现 ..................................................10
4.1数据结构的定义 .................................................10 4.2初始化工作 .....................................................11 4.3套接字的创建和设置 .............................................12 4.4数据包的捕获与分析 .............................................13 4.5信息的输出 .....................................................14 第五章程序运行结果与分析 ..............................................15
5.1程序运行结果截图 ...............................................15 5.2程序中有待改进的地方 ...........................................16 第六章 总结 ...........................................................17 参考文献 ..............................................................17 附 录 ..............................................................18
2
第一章 绪论
现如今,计算机网络已经彻彻底底地改变了人们的生活。大量的数据都是经过计算机网络传输的,而TCP/IP协议是计算机网络中最重要的协议之一。计算机网络中绝大多数数据都是以IP数据包的形式发送和接受的。所以IP数据包的捕获是很多计算机安全技术的基础。
1.1课题研究的意义
计算机之间进行通信时,交互的所有信息都封装在数据包中。因此,通过采集网络数据并对其进行相应的分析,可以清楚地了解到进行通信的计算机的通信目的。首先,分析采集到的数据包,可以确定网络是否受到攻击入侵;其次,也可以使用采集到的数据包来分析网络应用程序可能出现的问题的原因;此外,通过网络数据采集和统计可以清楚的了解整个网络在各个时段内的网络负载情况,从而判断网络使用得是否合理。除了以上谈到的几个方面以外,数据包采集分析还有其他很多用途.在研究IPv4网络的同时,我们还对IPv6协议进行了初步的研究并通过对数据报的分析,了解了在不同网络环境下IPv6数据包的封装格式以及在网络中的传输路径。目前,在同一子网范围内,可以通过邻居计算机发现协议自动配置主机的本地一链路IPv6地址,并获取子网内其他主机的通信地址,通过该地址可以实现子网内的主机间纯IPv6环境下的通信。但由于现在整个因特网并不支持IM协议,因此IPv6数据包要在网间传输,必须通过基于双协议栈的IPv4隧道(Tunnel)技术,将EM数据报封装在IPv4包头中,并通过指定的支持IM协议的路由在Internet中传送到目的地,再由目的主机进行数据报解析。获取IPv6数据报中的信息。
1.2捕获数据包的常用方法
目前常用的捕获数据包的方法有原始套接字、LibPcap、WinPcap和JPcap等方法。它们各有特点,实现起来有难有易,如何选择取决于具体需求与程序员的喜好。下面分别对它们作简单介绍。
套接字是网络应用编程接口。应用程序可以使用它进行网络通信而不需要知道底层发生的细节。有时需要自己生成一些定制的数据包或者功能并希望绕开Socket提供的功能,原始套接字(RawSocket)满足了这样的要求。原始套接字能够生成自己的数据报文,包括报头和数据报本身的内容。通过原始套接字,可以更加自如地控制Windows下的多种协议,而且能够对网络底层的传输机制进行控制。
3
LibPcap是一种与系统无关,采用分组捕获机制的分组捕获函数库,用于访问数据链路层,它在不同的平台上采用统一的编程接口,使用LibPcap编写的程序可自由的跨平台使用。同时LibPcap是一个独立于系统接口的用户级的抓包库,它为底层网络监听提供了可移植框架。它的应用包括网络统计集合、安全监听、网络调试等。
WinPcap是一个基于Win32的捕获数据包和网络分析的体系结构,它包括一个内核级的包过滤器,一个底层的动态链接库(Packet.dll),一个高层并且与系统无关的库(WPcap.dll,基于LibPcap0.6.2版本)。WinPcap是集成于
Windows95,98,ME,NT,2000和XP操作系统的设备驱动程序,它可以从网卡捕获或者发送原始数据,同时能够过滤并且存储数据包。
JPcap是一个能够捕获、发送网络数据包的Java类库包。这个包用到了LibPcap和原始套接字API。JPcap支持Ethernet,IPv4,IPv6,ARP/RARP,TCP,UDP,ICMPv4协议。JPcap是一个Java类集合,它为网络数据包的捕获提供接口和系统支持。其最初版本是2000年6月发布的JPcap0.01版,此后几经修改,到2003年4月发布了最新的JPcap0.4版。
第二章 系统需求分析
2.1课程设计题目与要求
本次实验的要求在网络环境,使用VC++编写程序实现捕获网络中的IP数据包,解析数据包的内容,将结果显示在标准输出上,并同时写入日志文件。
程序的具体要求如下:
l)以命令行形式运行:ipparse logfile,其中ipparse是程序名,而logfile则代表记录结果的日志文件。
2)在标准输出和日志文件中写入捕获的IP包的版本、头长度、服务类型、数据包总长度、数据包标识、分段标志、分段偏移值、生存时间、上层协议类型、头校验和、源IP地址和目的IP地址等内容。 3)当程序接收到键盘输入Ctrl+C时退出。
2.2 IP数据包格式
4
只有先了解IP数据包的格式,才能对IP数据包进行解析,下图是IP数据包的格式:
IP数据包由首部和数据两部分组成。首部的前一部分是固定长度,共 20 字节,是所有IP数据报必须具有的。在首部的固定部分的后面是一些可选字段,其长度是可变的,可选字段之后是数据部分。其中,首部固定部分中的各字段的长度及意义如下:
(1)版本 占4位,指IP协议的版本。通信双方使用的IP协议版本必须一致。目前广泛使用的IP协议版本号为4(即IPv4)。关于IPv6,目前还处于草案阶段。
(2)首部长度 占4位,可表示的最大十进制数值是15。请注意,这个字段所表示数的单位是32位字长(1个32位字长是4字节),因此,当IP的首部长度为1111时(即十进制的15),首部长度就达到60字节。当IP分组的首部长度不是4字节的整数倍时,必须利用最后的填充字段加以填充。因此数据部分永远在4字节的整数倍开始,这样在实现IP协议时较为方便。首部长度限制为60字节的缺点是有时可能不够用。但这样做是希望用户尽量减少开销。最常用的首部长度就是20字节(即首部长度为0101),这时不使用任何选项。
(3)区分服务 占8位,用来获得更好的服务。这个字段在旧标准中叫做服务类型,但实际上一直没有被使用过。1998年IETF把这个字段改名为区分服务DS(Differentiated Services)。只有在使用区分服务时,这个字段才起作用。
(4)总长度 总长度指首部和数据之和的长度,单位为字节。总长度字段为16位,因此数据报的最大长度为2^16-1=65535字节。在IP层下面的每一种数据链路层都有自己的帧格式,其中包括帧格式中的数据字段的最大长度,这称为最大传送
5
单元MTU(Maximum Transfer Unit)。当一个数据报封装成链路层的帧时,此数据报的总长度(即首部加上数据部分)一定不能超过下面的数据链路层的MTU值。
(5)标识(identification) 占16位。IP软件在存储器中维持一个计数器,每产生一个数据报,计数器就加1,并将此值赋给标识字段。但这个“标识”并不是序号,因为IP是无连接服务,数据报不存在按序接收的问题。当数据报由于长度超过网络的MTU而必须分片时,这个标识字段的值就被复制到所有的数据报的标识字段中。相同的标识字段的值使分片后的各数据报片最后能正确地重装成为原来的数据报。
(6)标志(flag) 占3位,但目前只有2位有意义。
标志字段中的最低位记为MF(More Fragment)。MF=1即表示后面“还有分片”的数据报。MF=0表示这已是若干数据报片中的最后一个。
标志字段中间的一位记为DF(Don’t Fragment),意思是“不能分片”。只有当DF=0时才允许分片。
(7)片偏移 占13位。片偏移指出:较长的分组在分片后,某片在原分组中的相对位置。也就是说,相对用户数据字段的起点,该片从何处开始。片偏移以8个字节为偏移单位。这就是说,每个分片的长度一定是8字节(64位)的整数倍。
(8)生存时间 占8位,生存时间字段常用的的英文缩写是TTL(Time To Live),表明是数据报在网络中的寿命。由发出数据报的源点设置这个字段。其目的是防止无法交付的数据报无限制地在因特网中兜圈子,因而白白消耗网络资源。最初的设计是以秒作为TTL的单位。每经过一个路由器时,就把TTL减去数据报在路由器消耗掉的一段时间。若数据报在路由器消耗的时间小于1秒,就把TTL值减1。当TTL值为0时,就丢弃这个数据报。后来把TTL字段的功能改为“跳数限制”(但名称不变)。路由器在转发数据报之前就把TTL值减1.若TTL值减少到零,就丢弃这个数据报,不再转发。因此,现在TTL的单位不再是秒,而是跳数。TTL的意义是指明数据报在网络中至多可经过多少个路由器。显然,数据报在网络上经过的路由器的最大数值是255.若把TTL的初始值设为1,就表示这个数据报只能在本局域网中传送。
(9)协议 占8位,协议字段指出此数据报携带的数据是使用何种协议,以便使目的主机的IP层知道应将数据部分上交给哪个处理过程。
(10)首部检验和 占16位。这个字段只检验数据报的首部,但不包括数据部分。这是因为数据报每经过一个路由器,路由器都要重新计算一下首部检验和(一些字段,如生存时间、标志、片偏移等都可能发生变化)。不检验数据部分可减少计算的工作量。
(11)源地址 占32位。为发送方的IP地址。 (12)目的地址 占32位。为接收方的IP地址。
6
2.3 程序流程图
通过对实验题目和要求的分析,画出程序流程图如下:
7
开始 N 命令行参数正确? Y 加载WinSock动态链接库成功? N Y 创建原始套接字 设置套接字 捕获IP数据包 解析IP数据包 N 接收到Ctrl+C? Y 结束 8
2.4 实验环境
本实验采用Windows操作系统平台,利用Windows提供的Windows Sockets API实现IP数据包的捕获与分析。采用WinSock 2.2版本,编程语言选用C++,编程工具采用Visual Studio 2010旗舰版。
第三章系统总体框架
整个系统可以分为四个模块,分别为套接字模块、捕获IP数据包模块、解析IP数据包模块和输出模块。下面分别作简要介绍。
3.1 套接字模块
套接字模块主要包括原始套接字的创建和原始套接字的设置。此模块先创建一个原始套接字,然后将此套接字绑定到一个本机的网络接口,再设置套接字使其能捕获经过此网络接口的所有IP数据包。
3.2 IP数据包的捕获模块
此模块主要负责捕获IP数据包,然后将捕获的数据包提交给IP数据包解析模块。此模块利用设置好的原始套接字捕获IP数据包,然后将数据包提交给解析模块,直到键盘输入ctrl+c时结束。
3.3 IP数据包分析模块
此模块主要负责对IP数据包进行分析,即根据IP数据包的格式把信息从捕获到的IP数据包中提取出来,然后再提交给输出模块。
3.4 输出模块
此模块负责输出IP数据包信息的输出,包括输出到标准输出和日志文件。
9
第四章详细设计与实现
4.1 数据结构的定义
本程序主要用到了两个数据结构,一个是IP头的结构体,一个是常用IP协议号与协议名的映射。
1. IP头结构体
根据IP数据包的格式,定义IP头结构体如下: struct IPHead {
u_char ihl : 4; //头长度 u_char version : 4; //版本 u_char tos; //服务类型 u_short len; //IP包的总长度 u_short id; //标识 u_short off; //分段偏移量 u_char ttl; //生存期 u_char protocol; //协议 u_short cksum; //头校验和 struct in_addr saddr; //源IP地址 struct in_addr daddr; //目的IP地址 };
2. 常用IP协议号与协议名的映射
为了通过协议号得到协议名,于是定义了一些常见的协议号与其协议名的映射关系,如下:
pair
10
make_pair(6, \ make_pair(8, \ make_pair(17, \ make_pair(35, \ make_pair(45, \ make_pair(46, \ make_pair(47, \ make_pair(54, \ make_pair(88, \ make_pair(89, \};
//常见的IP协议编号和名称
const map
4.2 初始化工作
1.命令行参数检查 if(argc != 2) {
cerr << \命令行参数错误!\ return 1; }
如果命令行参数的数目不是2的话,说明输入有误,需打印错误信息,然后退出。
2.初始化动态链接库
WSADATA wsa_data;
if(WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) {
cerr << \ return 1; }
如果初始化失败,则退出程序。
4.3 套接字的创建和设置
11
1. 套接字的创建
创建一个原始套接字,用来捕获数据包。
SOCKET s = socket(AF_INET, SOCK_RAW, IPPROTO_IP); if (s == INVALID_SOCKET) {
WSACleanup();
cerr << \return 1; }
如果创建失败,则退出程序。
2. 绑定本机地址
将刚刚建立的套接字与本机IP地址绑定。 struct sockaddr_in hostaddr; hostaddr.sin_family = AF_INET; hostaddr.sin_port = htons(0);
hostaddr.sin_addr.s_addr = gethostid();
if(bind(s, (sockaddr *)&hostaddr, sizeof(sockaddr)) != 0) {
closesocket(s); WSACleanup();
cerr << \ exit(-1); }
WinSock提供的bind()函数用于将一个套接字与一个地址绑定。绑定之后,原始套接字就能接收流经该IP地址所属网络接口的全部IP数据包。
3. 设置套接字为SIO_RCVALL
DWORD in_buffer = 1, n_returned;
int ret = WSAIoctl(s, SIO_RCVALL, &in_buffer, sizeof(in_buffer), NULL, 0, &n_returned, NULL, NULL); if(ret != 0) {
closesocket(s);
12
WSACleanup();
cerr << \ exit(-1); }
将套接字设置为SIO_RCVALL之后,套接字就能捕获局域网内所有的IP数据包,如果设置失败,就退出程序。
4.4 数据包的捕获与分析
前面的步骤完成之后就可以进行数据包的捕获了。主要用到WinSock提供的recv函数,recv函数的原型为:
size_t recv(SOCKET sockfd, void *buf, size_t len, int flags); recv()如果执行成功,则捕获的数据包存储在buf中。 主要代码如下: for(; ;) {
int n_recv = recv(s, buf, sizeof(buf), 0); SYSTEMTIME cur_time;
GetLocalTime(&cur_time); //获得捕获数据包的时间
if(n_recv > 0) {
IPHead *lp_iphead = (IPHead *)buf;
char buf[20];
sprintf(buf, \ cur_time.wMinute, cur_time.wSecond,
cur_time.wMilliseconds);
//输出到标准输出
cout << \捕包时间\\t\ output_ip(lp_iphead, cout); cout << endl << endl;
//输出到文件
13
fout << \捕包时间\\t\ output_ip(lp_iphead, fout); fout << endl << endl; fout.flush(); } }
其中,output_ip是一个自定义函数,该函数,将lp_iphead所向的IP信息输出到输出流fout。
4.5 信息的输出
信息的输出用函数output_ip函数完成,其定义如下: /*
**将IP数据包信息输出到out流 **ip_iphead为指向IPHead类型的指针 */
void output_ip(IPHead *lp_iphead, ostream &out) {
out << \版本\\t\\t\ out << \头长度\\t\\t\ out << \服务类型\\t\ out << \总长度\\t\\t\ out << \标识\\t\\t\
u_short unserved = (lp_iphead->off) >> 15; u_short DF = ((lp_iphead->off) >> 14) & 0x0001; u_short MF = ((lp_iphead->off) >> 13) & 0x0001; out << \标志位\\t\\t\ out << \偏移量\\t\\t\
out << \生存期\\t\\t\ out << \协议\\t\\t\ map
IP_PROTOCOL.find(lp_iphead->protocol);
if(it != IP_PROTOCOL.end())
14
out << \ out << \校验和\\t\\t\
out << \源IP地址\\t\ out << \目的IP地址\\t\}
第五章 程序运行结果与分析
5.1 程序运行结果截图
标准输出截图:
日志文件截图:
15
通过对大量输出数据的分析,发现程序的输出是正确的。
5.2 程序中有待改进的地方
这个程序只能捕获IPv4数据包,不能捕获IPv6数据包。可以通过修改程序,让这个程序既能捕获IPv4数据包,又能捕获IPv6数据包,那么这个程序就会更加的完善。
还可以为这个程序设计一个图形用户界面,使程序更加美观,更加易于使用。 还可以为程序增加一个功能,让程序能获取数据包中的内容,可就需要对各种应用程协议进行解析(例如:TCP,UDP)。
总之,只要多花点功夫,这个程序可以做的更好。
第六章 总结
16
本次课程设计让我学会了很多的东西,其中,最值得一提的是:①对IP协议有了比较深入的了解;②学习了Windows socket。
我选择的课题是IP数据包的捕获与分析。所以需要对IP数据包有非常深入的了解。这次实验中,我把IP协议学了好多遍,知道了IP头中每一个字段的意思。
对IP数据包的捕获,我使用的是原始套接字,在Windows操作系统中实现的。在做实验的过程中,我把Windows socket大概学了一遍,重点学习了其中的原始套接字。在学习的过程中,对OSI七层模型有了更深的了解。对Windows socket的学习,让我对网络编程的基本方法和步骤有了一定的了解,相信这次的学习对我以后网络编程的学习会很有帮助。我感觉Windows socket确实是一个很强大的工具,但是由于是C语言的接口,用起来也有一些的繁琐。
在学习《计算机网络》这门课程的时候,我感觉这门课一点意思都没有,全部都是理论知识,感觉学了也没有一点用。但是,在做课程设计的时候,我才发现上课学的那些理论知识也是非常有用的。如果没有那些理论知识的支撑,要完成这次的课程设计,不知道还有花多少时间。所以,这次课程设计的经历有一次告诉我,理论和实际相结合是非常重要的。
每个学期课程设计的这段时间都是我收获最多的一段时间,我享受这段时间,也感激这段时间。
最后,感谢实验老师这两个星期的陪伴和指导。
参考文献
(1) Andrew S.Tanenbaum,David J.Wetherall. 计算机网络(第五版)[M]. 北
京:清华大学出版社,2012.3
(2) Bob Quinn,Dave Shute. Windows Sockets网络编程[M].北京:机械工业出
版社,2012.8
(3) MSDN. TCP / IP原始套接字.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms740548(v=vs.85).aspx
17
附录 程序源代码
头文件: /*
**文件名:header.h */
#ifndef HEADER_H #define HEADER_H
#include
int const MAX_IP_LEN = 65535; pair
const map
18
struct IPHead {
u_char ihl : 4; //头长度 u_char version : 4; //版本 u_char tos; //服务类型 u_short len; //IP包的总长度 u_short id; //标识 u_short off; //分段偏移量 u_char ttl; //生存期 u_char protocol; //协议 u_short cksum; //头校验和 struct in_addr saddr; //源IP地址 struct in_addr daddr; //目的IP地址 };
u_long gethostid(); //获取本机地址
void output_ip(IPHead *lp_iphead, std::ostream &out); //流out
void set_socket(SOCKET s); //设置套接字 #endif 源文件: /*
**文件名:main.cpp */
#include
#pragma comment(lib, %using namespace std;
int main(int argc, char **argv)
19
把IP信息输出到{
if(argc != 2) {
cerr << \命令行参数错误!\ return 1; }
//初始化winsock的动态链接库 WSADATA wsa_data;
if(WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) {
cerr << \ return 1; }
//建立一个原始套接字
SOCKET s = socket(AF_INET, SOCK_RAW, IPPROTO_IP); if (s == INVALID_SOCKET) {
WSACleanup();
cerr << \ return 1; }
//设置套接字 set_socket(s);
//打开日志文件
ofstream fout(argv[1]); if(!fout) {
cerr << \ return 1; }
20
//捕获数据包,并进行解析
//将结果写入标准输出和日志文件中 char buf[MAX_IP_LEN]; for(; ;) {
int n_recv = recv(s, buf, sizeof(buf), 0); SYSTEMTIME cur_time;
GetLocalTime(&cur_time); //获得捕获数据包的时间
if(n_recv > 0) {
IPHead *lp_iphead = (IPHead *)buf;
char buf[20];
sprintf(buf, \ cur_time.wMinute, cur_time.wSecond, cur_time.wMilliseconds);
//输出到标准输出
cout << \捕包时间\\t\ output_ip(lp_iphead, cout); cout << endl << endl;
//输出到文件
fout << \捕包时间\\t\ output_ip(lp_iphead, fout); fout << endl << endl; fout.flush(); } }
WSACleanup(); return 0; }
21
/*
**set_socket完成对原始套接字s的设置 **以使s能接收到所用的IP数据包 */
void set_socket(SOCKET s) {
//将s绑定到本机地址上 struct sockaddr_in hostaddr; hostaddr.sin_family = AF_INET; hostaddr.sin_port = htons(0);
hostaddr.sin_addr.s_addr = gethostid();
if(bind(s, (sockaddr *)&hostaddr, sizeof(sockaddr)) != 0) {
closesocket(s); WSACleanup();
cerr << \ exit(-1); }
//将s设置为接受所有IP数据包 DWORD in_buffer = 1, n_returned;
int ret = WSAIoctl(s, SIO_RCVALL, &in_buffer, sizeof(in_buffer), NULL, 0, &n_returned, NULL, NULL); if(ret != 0) {
closesocket(s); WSACleanup();
cerr << \ exit(-1); } } /*
**函数:gethostid()
22
**描述:获得本机IP地址,使用如下算法: ** 生成一个UDP socket
** 连接这个UDP socket到任意的地址和端口 ** 使用getsockname()得到本地IP地址 */
u_long gethostid() {
SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr; addr.sin_family = AF_INET;
addr.sin_port = htons(IPPORT_ECHO);
addr.sin_addr.s_addr = inet_addr(\
connect(s, (sockaddr*)&addr, sizeof(sockaddr));
struct sockaddr_in hostaddr; //本机地址 int n_addr = sizeof(sockaddr);
getsockname(s, (sockaddr*)&hostaddr, &n_addr);
closesocket(s);
return hostaddr.sin_addr.s_addr; } /*
**将IP数据包信息输出到out流 **ip_iphead为指向IPHead类型的指针 */
void output_ip(IPHead *lp_iphead, ostream &out) {
out << \版本\\t\\t\ out << \头长度\\t\\t\ out << \服务类型\\t\ out << \总长度\\t\\t\
23
out << \标识\\t\\t\
u_short unserved = (lp_iphead->off) >> 15; u_short DF = ((lp_iphead->off) >> 14) & 0x0001; u_short MF = ((lp_iphead->off) >> 13) & 0x0001; out << \标志位\\t\\t\ out << \偏移量\\t\\t\
out << \生存期\\t\\t\ out << \协议\\t\\t\ map
out << \输出协议号对应的名称
out << \校验和\\t\\t\
out << \源IP地址\\t\ out << \目的IP地址\\t\}
24
百度搜索“70edu”或“70教育网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,70教育网,提供经典综合文库IP数据包的捕获与分析设计报告在线全文阅读。
相关推荐: