目录一、课程设计的目的 (1)二、课程设计要求 (1)三、需求分析 (1)1.先对网卡进行编程,以便连接IP层的数据包。
(1)2.预先创建一个logfile文件来保存所解析的IP数据包。
(1)3.使用recv函数实现接收数据包的功能。
(1)四、设计分析 (1)4.1 网卡设置 (1)4.2 使用套接字 (2)五、程序测试 (3)六、小结 (5)七、附录 (5)一、课程设计的目的本章课程设计的目的就是设计一个解析IP数据包的程序,并根据这个程序,说明IP数据包的结构及IP协议的相关问题,从而对IP层的工作原理有更好的理解和认识。
二、课程设计要求本设计的目标是捕获网络中的IP数据包,解析数据包的内容,将结果显示在标准输出上,并同时写入日志文件。
程序的具体要求如下:1)以命令行形式运行:ipparse logfile,其中ipparse是程序名, 而logfile 则代表记录结果的日志文件。
2)在标准输出和日志文件中写入捕获的IP包的版本、头长度、服务类型、数据包总长度、数据包标识、分段标志、分段偏移值、生存时间、上层协议类型、头校验和、源IP地址和目的IP地址等内容。
3)当程序接收到键盘输入Ctrl+C时退出。
三、需求分析1.先对网卡进行编程(使用套接字进行编程),以便连接IP层的数据包。
2.预先创建一个logfile文件来保存所解析的IP数据包。
3.使用recv函数实现接收数据包的功能。
4.编写ipparse函数解析捕获的数据包的版本、头长度、服务类型、数据包总长度、数据包标识、分段标志、分段偏移值、生存时间、上层协议类型、头校验和、源IP地址和目的IP地址等内容。
四、设计分析4.1 网卡设置为了获取网络中的IP数据包,必须对网卡进行编程,在这里使用套接字(socket)进行编程。
但是,在通常情况下,网络通信的套接字程序只能响应与自己硬件地址相匹配的数据包或是以广播形式发出的数据包。
对于其他形式的数据包,如已到达网络接口,但却不是发送到此地址的数据包,应用程序无法收取与自己无关的数据包。
我们要想获取网络设备的所有数据包,就是需要将网卡设置为混杂模式。
4.2 使用套接字因为要进行IP层数据包的接收和发送,所以选择原始套接字。
创建原始套接字的代码如下:Socket sock;sock = socket(AF_INET,SOCK_RAW,IPPROTO_IP);创建套接后,IP头就会包含在接收数据包中。
然后,我可以设置IP头操作选项,调用setsockopt函数。
其中flag设置为true,并设定IP-HDRINCL选项,表明用户可以亲自对IP头进行处理。
最后使用bind()函数将socket绑定到本地网卡上。
绑定网卡后,需用WSAIoctl()函数把网卡设置为混杂模式,使网卡能够接收所有的网络数据。
如果接收的数据包中的协议类型和定义的原始套接字匹配,那么接收的数据就拷贝到套接字中,因此,网卡就可以接收所有经过的IP包。
4.3.解析IP数据包IP数据包的第一个字段是版本字段,其度是4位,表示所使用的IP协议的版本.目前的版本是IPV4,版本字段的值是4,下一代版本是IPV6,版本字段值是6.本程序主要针对版本是IPV4的数据包的解析。
报头标长字段为4位,它定义了以4B为一个单位的IP包的报文长度.报头中除了选项字段和填充域字段外,其他各字段是定长的.因此,IP数据包的头长度在20—40B之间,是可变的。
4.4.将解析的IP数据导入logfile.txt文件。
4.5.流程图如下:首先,对IP头部数据进行定义,构造程序运行环境,创建一个文本文件以便将最后解析的IP数据包信息自动生成并复制进去。
接下来,创建原始套接字并初始化。
开始在有网的情况下获取IP数据包、解析IP数据包并输出和保存解析出来的IP数据包的信息,直到当程序接收到键盘输入Ctrl+C(或Ctrl+Break)时退出程序。
图1 程序活动图五、程序测试1.运行程序效果:开始捕获IP数据包,捕获的IP数据包信息会包括IP包的版本、头长度、服务类型、数据包总长度、数据包标识、分段标志、分段偏移值、生存时间、上层协议类型、头校验和、源IP地址和目的IP地址等内容。
直到程序接收到键盘输入Ctrl+C(或Ctrl+Break)时退出程序。
图2 程序结果图2.将解析过的IP数据包导入logfile文件里。
图3 解析结果文本六、小结这次计算机网络课程设计我选的题目是解析IP数据包,在一开始,尽管看了很多遍课程的设计要求和书上的相关知识,但还是有些云里雾里的,只能大致理清解析IP数据包的每一步过程,但具体那些过程要怎么编写代码对我来说还是有很大的难度的,在上网搜索了相当一部分资料后我才完成了这个程序。
通过本次课程设计,我更加深刻的了解到了IP数据包的结构及IP协议的相关问题,从而对IP层的工作原理有更好的理解和认识也学习到了很多其他的知识,比如如何编写一个套接字。
在课程设计的过程也碰到了不少的问题,比如不会将数据包导入logfile.txt文件,代码总是忘了该先定义。
以后想在平时就多学多看,不能每次都恶补知识。
七、附录源程序代码:#include "stdafx.h"#include "winsock2.h"#include "ws2tcpip.h"#include "iostream"#include "stdio.h"#pragma comment(lib,"ws2_32.lib")using namespace std;/*定义IP头部数据结构*/typedef struct _IP_HEADER{union{BYTE Version; //版本(前四位)BYTE HdrLen; //报头标长(后四位),IP头长度BYTE ServiceType; //服务类型WORD TotalLen; //总长度WORD ID; //标志union{WORD Flags;WORD FragOff;};BYTE TimeToLive; //生存时间BYTE Protocol; //协议WORD HdrChksum; //首部检验和DWORD SrcAddr; //源地址DWORD DstAddr; //目的地址BYTE Options;}IP_HEADER;/*逐位解析IP头中的信息,获取版本号*/ void getVersion(BYTE b, BYTE & version){version = b >> 4;}void getIHL(BYTE b, BYTE & result){result = (b & 0x0f) * 4;}/*解析服务类型*/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 "Flsah";break;case 2:return "Immediate";break;case 1:return "Priority";break;case 0:return "Routine";break;default:return "Unknow";break;}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 throughput";break;case 8:return "Minimize delay";break;case 15:return "Maximize security";break;default:return "Unknow";}}/* 获取禁止分片标志和分片标志 */void getFlags(WORD w, BYTE & DF, BYTE & MF) {DF = (w >> 14) & 0x01;MF = (w >> 13) & 0x01;}/* 获取分片偏移量 */void getFragOff(WORD w, WORD & fragOff){fragOff = w & 0x1fff;}/*获取协议*/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 "UNKNOW";}}/* 解析IP数据包 */void ipparse(FILE* file, char* buffer){IP_HEADER ip = *(IP_HEADER*)buffer;fseek(file, 0, SEEK_END);BYTE version;getVersion(ip.Version, version);fprintf(file, "IP包版本=%d\r\n", version);BYTE headerLen;getIHL(ip.HdrLen, headerLen);fprintf(file, "头长度=%d(BYTE)\r\n", headerLen);fprintf(file, "服务类型=%s,%s\r\n", parseServiceType_getProcedence(ip.ServiceType), parseServiceType_getTOS(ip.ServiceType));fprintf(file, "数据包总长度=%d(BYTE)\r\n", ip.TotalLen); fprintf(file, "数据包标识=%d\r\n", ip.ID);BYTE DF, MF;getFlags(ip.Flags, DF, MF);fprintf(file, "分段标志 DF=%d,MF=%d\r\n", DF, MF);WORD fragOff;getFragOff(ip.FragOff, fragOff);fprintf(file, "分段偏移值=%d\r\n", fragOff);fprintf(file, "生存时间=%d(hopes)\r\n", ip.TimeToLive);fprintf(file, "上层协议类型=%s\r\n", getProtocol(ip.Protocol));fprintf(file, "头校验和=0x%0x\r\n", ip.HdrChksum);fprintf(file, "源IP地址=%s\r\n", inet_ntoa(*(in_addr*)&ip.SrcAddr));fprintf(file, "目的IP地址=%s\r\n", inet_ntoa(*(in_addr*)&ip.DstAddr));fprintf(file, "*********************************************\r\n");}//主程序开始int main(){int nRetCode = 0;{FILE * file;if ((file = fopen("logfile.txt", "wb+")) == NULL){printf("打开失败");return -1;}WSADATA wsData;/* 启动2.2版本的Socket,并将Socket版本信息保存到wsData中*/if (WSAStartup(MAKEWORD(2, 2), &wsData) != 0) {printf("WSA 启动失败!\n");return -1;}SOCKET sock;/* 创建原始套接字*/if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP)) ==INV ALID_SOCKET) {printf("不能创建原始套接字SOCK_RAW\n");return -1;}BOOL flag = TRUE;/* 设置IP头操作选项*/if(setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag))==SOCKET_ERROR){printf("setsockopt failed!\n");return -1;}char hostName[128];/* 获取本地主机名*/gethostname(hostName, 100);hostent * pHostIP;/* 根据主机名获取主机信息*/pHostIP = gethostbyname(hostName);/* 封装IP地址信息*/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);//你所监听端口号bind(sock, (PSOCKADDR)&addr_in, sizeof(addr_in));//绑定sock到本地网卡DWORD dwValue = 1;#define IO_RCV ALL _WSAIOW(IOC_VENDOR,1)DWORD dwBufferLen[10];DWORD dwBufferInLen = 1;DWORD dwBytesReturned = 0;WSAIoctl(sock, IO_RCV ALL, &dwBufferInLen, sizeof(dwBufferInLen), &dwBufferLen, sizeof(dwBufferLen), &dwBytesReturned, NULL, NULL);#define BUFFER_SIZE 65535//数据包大小char buffer[BUFFER_SIZE];printf("开始解析经过本机的IP数据包:\n");while (true){/* 从套接字接收IP数据报*/int size = recv(sock, buffer, BUFFER_SIZE, 0);//-1错误,0没有收到,>0 接收到数据大小if (size>0){ipparse(stdout, buffer);ipparse(file, buffer);}}/* 关闭文件*/fclose(file);return 0;}return nRetCode;}参考文献:[1].谢希仁主编.计算机网络(第7版)[M].北京:电子工业出版社.2017.1[2].吴功宜,吴英等编著.计算机网络课程设计(第2版)[M].北京:机械工业出版社.2012.1。