资源描述
计算机网络课程设计报告
题 目: 解析IP数据包
学生姓名:
学 号:
专业班级: 计算机科学与技术
同组姓名:
指导教师:
设计时间: 2015年上学期第17周
指导老师意见:
评定成绩: 签名:
日期:2015年7月3日
一、 课程设计的目的和意义
目的:
1、 通过解析IP数据包的程序,并根据这个程序,说明IP数据包的结构及IP
协议的相关问题。
2、 通过接收和解析IP数据包,了解IP数据包的基本结构与IP协议的基本功能。
3、 捕获网络中的数据包,解析数据包的内容,将结果显示在标准输出上,并同时写入日志文件。
意义:
1、 为了在本次课程设计过程中,熟悉开发设计的基本流程,从分析任务到确立整体框架再到确定算法,然后再一步步实现各函数的功能。从中熟悉新的库函数,并提高编程技巧。
2、我们已经学完了网络层的理论知识,可是对它的理解很粗浅。之前只知道关于网络层的一些概念性的东西。可是做完设计后,我才从整体上理解了网络层的框架,明白了网络层的每一个组成部分都是有它特定的功能和意义的,从而对网络层协议有了更深入的理解。
3、程序设计能直接有效地训练我们的创新思维,培养分析问题、解决问题的能力。
二、 课程设计的内容和要求
根据后面介绍的数据包结构,编写程序的具体要求如下:
1)以命令行形式运行:ipparse logfile,其中ipparse是程序名, 而logfile则代表记录结果的日志文件。
2)在标准输出和日志文件中写入捕获的IP包的版本、头长度、服务类型、数据包总长度、数据包标识、分段标志、分段偏移值、生存时间、上层协议类型、头校验和、源IP地址和目的IP地址等内容。
3)当程序接收到键盘输入Ctrl+C时退出
三、解析IP数据包设计的相关技术
3.1 IP数据包的格式与分析
IP协议把传输层送来的消息组装成IP数据包,并把IP数据传递给数据链路层。IP协议在TCP/IP协议族中处于核心地位,IP协议制定了统一的IP数据包格式,以消除各通信子网间的差异,从而为信息发送方和接收方提供了透明的传输通道。编制本程序前,首先要对IP包的格式有一定的了解。图(1)给出了IP协议的数据包格式。
IP数据包的第一字段是版本字段,其长度为4位,表示所使用的IP协议的版本。目前的版本IPV4,版本字段的值为4,本程序主要针对版本值为4的IP数据包的解析。
报头标长(IHL)字段为4位,它定义了以4B为一个单位的IP包的报头长度。报头除了选项字段和填充域字段外,其他各字段是定长的。因此,IP数据包的长
度在20-40B之间,是可变的。IP数据包格式如表1所示。
表1 IP数据包的格式
0 4 8 16 19 24 31(位)
版本
报头标长
服务类型
总长度
标 识
标 志
片偏移
生存时间
协 议
头校验和
源IP地址
目的IP地址
任选项(0或多项)
填充
数据部分
服务类型字段共8位,用于指示路由器如何处理该数据包。该字段长度由4位服务类型(TOS)子域和3位优先级(b7 b6 b5)(precedence)子域组成,1位为保留位,该字段结构如表(2)所示:
表2 服务类型字段结构
b7 b6 b5 b4 b3 b2 b1 b0
优先级
D
T
R
C
0
在4位服务类型子域中,b4 b3 b2 b1分别表示D(延迟)、T(吞吐量)、R(可靠性)与(成本)。表(3)列出了服务类型子域的构成。
表3 服务类型子域
位数(b4 b3 b2 b1)
意义
1111
安全
1000
延迟最小
0100
吞吐量最大
0010
可靠性最大
0001
成本最小
0000
普通服务
总长度字段为2B,它定义了以字节为单位的数据包的总长度。IP数据包的最大长度为216 =65535B。标识字段长度为16位,用于识别IP数据包的编号。每批数据都有一个标识值,用于让目的主机判断新来的数据属于哪个分组。
报头中的标志字段如表(4)所示。标志字段共3位,最高位是0,禁止分片标志DF字段的值若为1,表示不能对数据包分片;若DF值为0,则表明可以分片。分片标志MF的值为1,表示接收到的不是最后一个分片;若MF值为0,表示接收到的是最后一个分片。
表4 标志字段的结构
0
DF
MF
片偏移字段共13位,说明分片在整个数据包中的相对位置。片偏移值是8B为单位来计数的,因此选择的分片长度应该是8B的整数倍。
生存时间(TTL)字段为8位,用来设置数据包在互联网络的传输过程的寿命,通常是用一个数据包可以经过的最多的路由器跳步数来限定的。
协议字段为8位,表示使用IP数据包的高层协议类型
头部验和字段为16位,用于存放检查报头错误的校验码。
3.2 程序设计分析
3.2.1 网卡设置
为了获取网络中的IP数据包,必须对网卡进行编程,在这里我们使用套接字(socket)进行编程。但是,在通常情况下,网络通信的套接字程序只能响应与自己硬件地址相匹配的数据包或是以广播形式发出的数据包。对于其他形式的数据包,如已到达网络接口,但却不是发送到此地址的数据包,网络接口在骓投递地址并非自身地址之后将不引起响应,也就是说应用程序无法收取与自己无关的数据包。我们要想获取网络设备的所有数据包,就是需要将网卡设置为混杂模式。
3.2.2 程序设计
本程序主要由三部分构成:初始化原始套接字,反复监听捕获数据包和解析数据包。下面就结合核心代码对程序的具体实现进行分析,同时使程序流程更加清晰,去掉了错误检查等保护性代码。
3.2.3 接收数据包
在程序中可使用recv()函数接收经过的IP包。该函数有四个参数,第一个参数接收操作所用的套接字描述符;第二个参数接收缓冲区的地址;第三个参数接收缓冲区的大小,也就是所要接收的字节数;第四个参数是一个附加标志,如果对所发送的数据没特殊要求,直接设为0。因为IP数据包的最大长度是65535B,因此缓冲区的大小不能小于65535B。设置缓冲区后,可利用循环来反复监听接收IP包,用recv()函数实现接收功能。
3.2.4IP包的解析
解析IP包的字段有两种策略。针对长度为8位、16位和32位的字段(或子字段)时,可以利用IP-HEADER的成员直接获取。要解析长度不是8位倍数的字段(或子字段)时,可以利用C语言中的移位以人、及与、或操作完成。
四、课程设计过程
首先,分析任务,明确该程序需要实现的功能;然后,根据该功能画出相应的程序流程图;其次,打开VC,用C++语言书写源代码,并且进行调试,直到得出正确的结果,并对程序的最后运行结果进行截图;最后,完成课程设计报告。
4.1 程序流程图如下
开 始
构造程序运行环境
创建原始接字,并初始化
捕获IP包
解析IP包
解析IP包
结 束
Ctrl+C(中断)
N
Y
图1 程序流程图
4.2程序运行结果
图2 程序的运行方式
图3 程序运行结果
4.3结果分析
Version=4,表示版本是IPv4
HdrLen=(Bytes),表示报头字段为20个字节
ServiceType=Routine Normal service,表示服务类型为一般服务
数据长度=19968(Bytes),表示数据长度19968字节
数据报ID=4255,表示数据报的标识ID为4255
分段标志DF=0,MF=0,标识数据报可以分片,并且是最后一个分片
分段偏移值=0,表示此分片在整个数据包的相对位置为0
生存期=64(hops)表示生存期为64
五、课程设计小结
经过一个学期计算机网络的学习,我学习到了基本的理论知识,了解到了网络协议、网络管理各方面的相关规定和技术,这些知识都为我的课程实践和进一步的学习打下了坚实的基础。
这次计算机网络课程设计是“解析IP数据包”,通过两次的上机操作,充分应用了所学的计算机网络和C++的知识,并去图书馆查阅了一些书集和上网搜索一部分相当资料,粗略设计出该程序。
总体上来说,这次课程设计还是比较成功的,当然,由于学艺不精,在课程设计的过程也碰到的不少问题。该程序也存在着不少的缺陷,比如并不是所有的数据包都能捕获,如:IP数据包以外的数据包都抓不到。
通过这次课程设计,我明白了一定要自己上机操作,这样才能真正的学到东西。书本知识固然重要,但我们更要学会将书本知识应用到实际的工作中。实践中才会发现错误,也才能改进,才能达到学习的最终目的。
参考文献
[1]谢希仁. 计算机网络第六版 电子工业出版社
[2] 吴功宜 胡晓英等.计算机网课程设计 北京:机械工业出版社
[3] 王春晓 赵艳标.计算机网络教程 北京:机械工业出版社
[4] 兰少华 杨余旺 吕建勇.TCP/IP网络与协议 北京:清华大学出版社
[5] 张荛学 郭国强计算机网络与Internet教程(第二版)北京:清华大学出版社
附录代码
#include<stdio.h>
#include<winsock2.h>
#include<ws2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
//定义IP头部结构
typedef struct _IP_HEADER
{
union
{
BYTE Version; //版本(前4位)
BYTE HdrLen; //IP头部长度(后4位)
};
BYTE ServiceType; // 服务类型
WORD TotalLen; // 总长度
WORD ID; // 标识
union
{
WORD Flags; // 标志(前3位)
WORD FragOff; // 分段偏移(后13位)
};
BYTE TimeToLive; // 生命期
BYTE Protocol; // 协议
WORD HdrChksum; //头校验和
DWORD SrcAddr; // 源地址
DWORD DstAddr; // 目的地址
BYTE Options; // 选项
} IP_HEADER;
#define IO_RCVALL _WSAIOW (IOC_VENDOR,1)
#define BUFFER_SIZE 65535
//解析IP包的版本信息
void getVersion(BYTE b,BYTE &version)
{
version=b>>4;
}
//解析IP包的头部长度
void getIHL(BYTE b,BYTE &length)
{
length=(b&0x0f)*4;
}
//解析IP包的服务类型
char* parseServiceType_getProcedence(BYTE b)
{
switch(b>>5)
{
case 7:
return "Network Control";
break;
case 6:
return "Internet work Control";
break;
case 5:
return "CRITIC/ECP";
break;
case 4:
return "Flash Override";
break;
case 3:
return "Flash";
break;
case 2:
return "Immediate";
break;
case 1:
return "Priority";
break;
case 0:
return "Routine";
break;
default:
return "Unknown";
}
}
//解析IP包的服务级别
char* parseServiceType_getTOS(BYTE b)
{
b = (b >> 1)&0x0f;
switch(b)
{
case 0:
return "Normal service";
break;
case 1:
return "Minimize monetary cost";
break;
case 2:
return "Maximize reliability";
break;
case 4:
return "Maximize thoughput";
break;
case 8:
return "Minimize delay";
break;
case 15:
return "Maximize security";
break;
default:
return "Unknown";
}
}
//解析IP包的标志位
void getFlags(WORD w,BYTE &DF,BYTE &MF)
{
DF = (w >> 14)&0x01;
MF = (w >> 13)&0x01;
}
//解析IP包的分段偏移
void getFragOff(WORD w,WORD &fragOff)
{
fragOff = w&0x1fff;
}
//解析IP包的协议类型
char* getProtocol (BYTE Protocol)
{
switch (Protocol)
{
case 1:
return "ICMP";
case 2:
return "IGMP";
case 4:
return "IP in IP";
case 6:
return "TCP";
case 8:
return "EGP";
case 17:
return "UDP";
case 41:
return "IPv6";
case 46:
return "RSVP";
case 89:
return "OSPF";
default:
return "UNKNOWN";
}
}
void ipparse(FILE* file,char* buffer)
{
IP_HEADER ip =*(IP_HEADER*)buffer;
fseek(file,0,SEEK_END);
fprintf(file,"-----------------------------\n");
//解析IP包的版本信息
BYTE version;
getVersion(ip.Version,version);
fprintf(file,"Version: %d\n",version);
//解析IP包的头部长度
BYTE headerLen;
getIHL(ip.HdrLen,headerLen);
fprintf(file,"HdrLen: %d(Bytes)\n",headerLen);
//解析IP包的服务类型与等级
fprintf(file,"ServiceType: %s,%s\n",
parseServiceType_getProcedence(ip.ServiceType),
parseServiceType_getTOS(ip.ServiceType));
//解析IP包的总长度
fprintf(file,"TotalLen: %d(Bytes)\n",ip.TotalLen);
//解析IP包的标识符
fprintf(file,"ID: %d\n",ip.ID);
//解析IP包的标志位
BYTE DF,MF;
getFlags(ip.Flags,DF,MF);
fprintf(file,"Flags: DF=%d,MF=%d\n",DF,MF);
//解析IP包的分段偏移
WORD fragOff;
getFragOff(ip.fragOff,fragOff);
fprintf(file,"FragOff: %d\n", fragOff);
//解析IP包的生存期
fprintf(file,"TimeToLive: %d(Hops)\n",ip.TimeToLive);
//解析IP包的协议类型
fprintf(file,"Protocol: %s\n",getProtocol(ip.Protocol));
//解析IP包的头部校验和
fprintf(file,"HdrChksum: 0x%0x\n",ip.HdrChksum);
//解析IP包的源IP地址
fprintf(file,"SrcAddr: %s\n",inet_ntoa(*(in_addr*)&ip.SrcAddr));
//解析IP包的目的IP地址
fprintf(file,"DstAddr: %s\n",inet_ntoa(*(in_addr*)&ip.DstAddr));
}
void main(int argc,char* argv[])
{
//检查输入命令格式
if(argc!=2)
{
printf("Please input command1:ParseArp output_file");
return;
}
//打开输出日志文件
FILE* file;
if((file=fopen(argv[1],"wb+"))==NULL)
{
printf("Fail to open file %s",argv[1]);
return;
}
//初始化Socket环境
WSADATA wsData;
if(WSAStartup(MAKEWORD(2,2),&wsData)!=0)
{
printf("WSAStartup failed!");
return;
}
//建立原始Socket
SOCKET sock;
if((sock=socket(AF_INET,SOCK_RAW,IPPROTO_IP))==INVALID_SOCKET)
{
printf("Creat socket failed!");
return;
}
//设置IP头部操作选项,flag设置为true
BOOL flag=true;
if(setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char*)&flag,sizeof(flag))==SOCKET_ERROR)
{
printf("Setsockopt failed!");
return;
}
//获取本地主机名
char hostName[128];
if(gethostname(hostName,100)==SOCKET_ERROR)
{
printf("Gethostname failed!");
return;
}
//获取本地主机ip地址
hostent* pHostIP;
if((pHostIP=gethostbyname(hostName))==NULL)
{
printf("Gethostbyname failed!");
return;
}
//填充SOCKADDR_IN结构
sockaddr_in addr_in;
addr_in.sin_addr=*(in_addr*)pHostIP->h_addr_list[0];
addr_in.sin_family=AF_INET;
addr_in.sin_port=htons(6000);
//把原始Socket绑定到本地网卡
if(bind(sock,(PSOCKADDR)&addr_in,sizeof(addr_in))==SOCKET_ERROR)
{
printf("Bind failed!");
return;
}
//设置SOCK_RAW为SIO_RCVALL,接受所有的ip包
DWORD dwValue=1;
DWORD dwBufferLen[10];
DWORD dwBufferInLen=1;
DWORD dwBytesReturned=0;
if(WSAIoctl(sock,IO_RCVALL,&dwBufferInLen,sizeof(dwBufferInLen),&dwBufferLen,sizeof(dwBufferLen),&dwBytesReturned,NULL,NULL)==SOCKET_ERROR)
{
printf("Ioctlsocket failed!");
return;
}
//监听经过本机的IP包
char buffer[BUFFER_SIZE];
printf("Listening on local host...\n");
while(true)
{
int size=recv(sock,buffer,BUFFER_SIZE,0);
if(size>0)
{
ipparse(stdout,buffer);
ipparse(file,buffer);
}
}
fclose(file);
return;
}
14
展开阅读全文