据包的最大长度是65535B,因此缓冲区的大小不能小于65535B。设置缓冲区后,可利用循环来反复监听接收IP包,用recv()函数实现接收功能。
定义IP头部的数据结构
? 程序需要定义一个数据结构表示IP头部。其代码如下: ? struct IP_HEADER {
? unsigned short ip_version, /*IP的版本号 */ ip_hdr_len; /*IP包头的长度*/ ip_tos; /*IP包的服务类型*/ ip_total_len; /*IP包的总长度*/ ip_id; /*IP包的分段标识*/ ip_flags; /*IP包的分段标志*/
ip_frag_offset; /*IP包的分段偏移*/ ip_ttl; /*IP包的生存时间*/ ip_proto; /*IP包的高层协议*/ ip_hdr_chksum; /*IP包的校验和*/
struct IPADDRESS ip_src_addr; /*IP包的源IP地址*/ ip_dest_addr; /*IP包的目的IP地址*/ }ipheader;
IP包的解析
? 解析IP包的字段有两种策略。针对长度为8位、16位和32位的字段(或子字段)时,可以利用IP-HEADER的成员直接获取。要解析长度不是8位
11 / 37
倍数的字段(或子字段)时,可以利用C语言中的移位以人、及与、或操作完成。
协议的定义
? (包含相应的头文件#include #include):
? DWORD dwIoControlCode=SIO_RCVALL, /*接收所有的IP包*/ dwProtocol=IPPROTO_IP; /*协议类型为IP*/
捕获处理
? 1.加载 Winsock;
? 2.创建一个接收原始IP包的socket连接; 3.绑定到一个接口;
4.进行WSAIoctl设置,接收所有的IP数据包。 ? 代码如下:
if (WSAIoctl(s, dwIoControlCode, &optval, sizeof(optval), NULL, 0, &dwBytesRet, NULL, NULL) == SOCKET_ERROR) 5.接着设定一个线程进行捕获: (1)创建一个接收IP包的链表头;
(2)设置一个标识,为真,则不断进行IP包的捕获; (3)建立一个新的结点,将捕获的数据包加入到该结点;
(4)如果链表的长度达到指定的长度,创建一个线程对该链表的IP包进行解析;再设置一个在IP数据包链表不足给定的长度,而又中止IP捕获时,对链表的处理;
(5)为下一个IP包链表创建一个链表头。
12 / 37
6.建立一个进行IP包解析并显示的线程,进行解析IP数据包,然后显示IP数据包。
实验结果
源代码
#include \
#include \#include
#pragma comment(lib,\#pragma comment(lib,%using namespace std;
//定义IP头
typedef struct _IP_HEADER { union {
13 / 37
BYTE Version; //版本(前4位) BYTE HdrLen; //IHL(后4位),IP头的长度 }; 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;
int main () { SOCKET sock;
WSADATA wsData; ofstream outfile(\ //如果初始化失败,程序退出 if(WSAStartup(MAKEWORD(2,2),&wsData) != 0) { printf(\ return -1; } //建立原始socket if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_IP)) == INVALID_SOCKET) { printf(\ return -1; } //设置IP头操作选项,其中flag设置为true,用户可以亲自对IP头进行处理 BOOL flag = true; if(setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char*)&flag,sizeof(flag)) SOCKET_ERROR) { printf(\ return -1; }
14 / 37
==
//获取主机名 char hostName[128]; if(gethostname(hostName,100) == SOCKET_ERROR) { printf(\ return -1; } //获取本地IP地址 hostent *pHostIP; if((pHostIP = gethostbyname(hostName)) == NULL) { printf(\ return -1; } //填充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(\ return -1; } //设置SOCK_RAW为SIORCVALL,以便接收所有的确IP包 #define IO_RCVALL _WSAIOW(IOC_VENDOR,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(\ return -1; } //设置接收数据包的缓冲区长度 #define BUFFER_SIZE 65535 char buffer[BUFFER_SIZE]; //监听网卡 printf(\开始解析经过本机的IP数据包!\\n\\n\ while(true) {
15 / 37
==