您的位置:首页 > 其它

IP包流量分析程序

2015-07-23 22:33 337 查看
使用套接字编程实现捕获一段时间内以本机为源地址或目的地址的IP数据包(不包括以广播形式发出的数据包),统计IP数据包的信息,列出本机与其他主机之间不同协议类型IP数据包的数量 及流量。以源地址 目的地址 协议类型 数据包数量 流量的格式输出统计信息。

1、IP数据报格式:



(1)版本:表示所使用的IP协议的版本,4表示IPv4,6表示IPv6

(2)报头长度:以4B为单位指定了IP数据包报头的长度。

(3)服务类型:指示了路由器应该如何处理该数据报。

(4)总长度:以字节为单位具体说明包括报头在内的整个IP数据报的长度,占16位,所以IP数据报最长可达65535B

(5)标识:用来唯一标识主机发送的每一份数据报,通常每发送一份报文它的值会加1

(6)标志:标志字段占3位,第一位保留并总设为0;第二位是禁止分片标志DF,该位为0,说明数据报可以被分片;第三位是分片标志MF,只有再DF为0才有效,用以标识此报文是否是这系列分片的最后一个,为0 标识接收到的是最后一个分片。

(7)片偏移:给出了每一个分片在完整IP数据报中的相对位置。片偏移以8B为偏移单位,因此除最后一片外,分片长度应是8B的整数倍

(8)生存时间:设置数据报可以经过的最多路由器数。

(9)协议类型:指出此IP数据报的最高层协议类型。常用的有1表示ICMP,2表示IGMP,6表示TCP,8表示EGP,17表示UDP,41表示IPv6,89表示OSPF

(10)头部校验和:IP数据报头部校验和采用网际校验和算法:发送端先把校验和字段置0,然后将头部划分为长度为16位的比特序列,对头部中每个16位进行二进制反码求和,结果存在检验和字段中。

(11)源IP地址:发送数据报的源主机IP地址

(12)目的IP地址:接收数据报的目的主机的IP地址

(13)选项域:长度范围为0~40B,主要用于支持纠错、测量及安全等措施

(14)填充域:当IP报头长度不是4B的整数倍时,必须利用填充域“添0”来加以补充。

2、程序流程图



3、部分函数注释

(1) int WSAStart(WORD wVersionRequested,LPWSADATA lpWSAData)

wVersionRequested:指明程序使用的Socket版本,其中高位字节指明副版本,低位字节指明主版本。

lpWSAData:返回请求的Socket的版本信息。

返回值:成功返回0,失败返回WSASYSNOTREADY / WSAVERNOTSUPPORTED / WSAEINVAL

(2)SOCKET sock;

sock=WSASocket(AF_INET,SOCK_RAW,IPPROTO_IP,NULL,0,0);

AF_INET:指明通信发生的域为Internet协议簇

SOCK_RAW:指明套接字的类型为原始套接字。套接字分为流式套接字、数据报套接字、流式套接字。

IPPROTO_IP:指定套接字所用的特定协议为IP协议

返回值:成功返回套接字描述符,失败返回INVALID_SOCKET

(3)int bind(int sockfd,struct sockaddr* my_addr,int addrlen)

sockfd:套接字描述符

my_addr:特定的网络地址

addrlen:my_addr的长度

返回值:正确返回0,失败返回SOCKET_ERROR

(4)int recv(int fockfd,char* buff,int len,int flag)

fockfd:套接字描述符

buff:指向接收缓冲区

len:缓冲区的长度

flag:指明调用的方式,一般置0

返回值:成功返回接收的数据长度,失败返回-1

(5)setsockopt(SockRaw,IPPROTO_IP,IP_HDRINCL,(char *)&flag,sizeof(int))

设置SockRaw这个套接字的ip选项中的IP_HDRINCL

4、3中表示套接字地址的数据结构

(1)sockaddr结构:针对各种通信域的套接字,存储他们的地址信息

struct sockaddr{

unsigned short sa_family;//地址家族

char sa_data;//协议地址

}

(2)sockaddr_in结构:专门针对Internet通信域,存储套接字相关的网络地址信息

struct sockaddr_in{

short int sin_family;

unsigned short int sin_port;//端口号

struct in_addr sin_addr;//IP地址

unsigned char size_zero[8];//全为0

}

(3)in_addr结构:专门用来存储IP地址

struct in_addr{

unsigned long s_addr;

}

(4)以上3种数据结构的一般用法:

首先,定义一个sockaddr_in的结构实例,并将它清零

例:struct sockaddr_in myaddr;

memset(&myad,0,sizeof(struct sockaddr_in));

然后,为这个结构赋值:

例:myaddr.sin_family=AF_INET;

myaddr.sin_port=htons(8080);

myaddr.sin_addr.s_addr=htonl(INADDR_ANY)

最后,在函数调用时将这个股结构强制转换为sockaddr类型

例:accept(listenfd,(sockaddr*)&myad,&addrlen);

5、完整代码及注释:

节点类CIPNode:

IPNode.h文件:

#pragma once

class CIPNode

{

private:

unsigned long m_dwSourceIPAddr; //源IP地址

unsigned long m_dwDestIPAddr; //目的IP地址

unsigned char m_chProtocol; //IP包协议类型

unsigned long m_dwCounter; //数据包的数量

unsigned short m_dwFlow; //数据包的流量

public:

CIPNode * pNext;

public:

CIPNode(void);

~CIPNode(void);

CIPNode(unsigned long dwSourceIP,unsigned long dwDestIP,unsigned char chPro,unsigned short dwFlow);

//增加数据包数量

void addCount();

//增加数据包流量

void addFlow(unsigned short dwFlow);

//取得数据包数量

unsigned long getCount();

//取得数据包流量

unsigned short getFlow();

//取得源IP地址

unsigned long getSourceIPAddr();

//取得目的IP地址

unsigned long getDestIPAddr();

//取得协议类型

unsigned char getProtocol();

// 取得协议名称

char * getProtocol_String();

};

/******************************************************************************************************************************************************************************/

IPNode.cpp文件:

#include "stdafx.h"

#include "IPNode.h"

CIPNode::CIPNode(unsigned long dwSourceIP,unsigned long dwDestIP,unsigned char chPro,unsigned short dwFlow)

{

m_dwSourceIPAddr=dwSourceIP;

m_dwDestIPAddr=dwDestIP;

m_chProtocol=chPro;

m_dwCounter=1;

m_dwFlow=dwFlow;

}

CIPNode::CIPNode()

{

}

CIPNode::~CIPNode(void)

{

}

void CIPNode::addCount()

{

m_dwCounter++; //增加数据包数量

}

void CIPNode::addFlow(unsigned short dwFlow)

{

m_dwFlow+=dwFlow;

}

//取得数据包数量

unsigned long CIPNode::getCount()

{

return m_dwCounter;

}

unsigned short CIPNode::getFlow()

{

return m_dwFlow;

}

//取得源IP地址

unsigned long CIPNode::getSourceIPAddr()

{

return m_dwSourceIPAddr;

}

//取得目的IP地址

unsigned long CIPNode::getDestIPAddr()

{

return m_dwDestIPAddr;

}

//取得协议类型

unsigned char CIPNode::getProtocol()

{

return m_chProtocol;

}

// 取得协议名称

char * CIPNode::getProtocol_String()

{

switch(m_chProtocol)

{

case 1:

return "ICMP";

break;

case 2:

return "IGMP";

break;

case 4:

return "IP in IP";

break;

case 6:

return "TCP";

break;

case 8:

return "EGP";

break;

case 17:

return "UDP";

break;

case 41:

return "IPv6";

break;

case 46:

return "RSVP";

break;

case 89:

return "OSPF";

break;

default:

return "UNKNOWN";

}

}

/***********************************************************************************************************************************************************************/

节点链表类:CNodeList

NodeLIst.h文件

#pragma once

#include "IPNode.h"

class CNodeList

{

public:

CNodeList(void);

~CNodeList(void);

void addNode(unsigned long dwSourIP,unsigned long dwDestIP,unsigned char chPro,unsigned short dwFlow);

void print();

private:

CIPNode * pHead; //链表头

CIPNode * pTail; //链表尾

};

/***********************************************************************************************************************************************************************************/

NodeList.cpp文件:

#include "stdafx.h"

#include "NodeList.h"

#include "winsock2.h"

#include <iostream>

using namespace std;

CNodeList::CNodeList(void)

{

pHead=pTail=NULL;

}

CNodeList::~CNodeList(void)

{

if(pHead!=NULL)

{

CIPNode * pTemp=pHead;

pHead=pHead->pNext;

delete pTemp;

}

}

void CNodeList::addNode(unsigned long dwSourceIP,unsigned long dwDestIP,unsigned char chpro,unsigned short dwFlow)

{

if(pHead==NULL) //链表为空

{

pTail=new CIPNode(dwSourceIP,dwDestIP,chpro,dwFlow);

pHead=pTail;

pTail->pNext=NULL;

}

else

{

CIPNode *pTemp;

for(pTemp=pHead;pTemp;pTemp=pTemp->pNext)

{

//如果链表中已存在该类型的IP包,则数据包个数加一

if(pTemp->getSourceIPAddr()==dwSourceIP&&

pTemp->getDestIPAddr()==dwDestIP&&pTemp->getProtocol()==chpro)

{

pTemp->addCount();

pTemp->addFlow(dwFlow);

break;

}

}

//如果链表中不存在该类型的IP包,则创建新的节点加入链表

if(pTemp==NULL)

{

pTail->pNext=new CIPNode(dwSourceIP,dwDestIP,chpro,dwFlow);

pTail=pTail->pNext;

pTail->pNext=NULL;

}

}

}

void CNodeList::print(){

CIPNode * pTemp;

if(pHead == NULL)

{

cout << "没有捕获到IP数据包!" << endl;

}

else

{

cout << "源地址 " << '\t' << "目的地址" << '\t' << "协议类型 " << "数据包数量" <<"数据流量"<<endl;

for(pTemp = pHead; pTemp; pTemp = pTemp->pNext)

{

unsigned long dwSourTemp = pTemp->getSourceIPAddr();

unsigned long dwDestTemp = pTemp->getDestIPAddr();

cout << inet_ntoa(*(in_addr *)&(dwSourTemp)) << '\t';

cout << inet_ntoa(*(in_addr *)&(dwDestTemp)) << '\t';

cout << pTemp->getProtocol_String()<<'\t'<< pTemp->getCount()<<"\t" <<pTemp->getFlow()<< endl;

}

}

}

/**********************************************************************************************************************************************************************************************/

ConsoleApplication_IP.h文件:

#include<winsock2.h>

#include<ws2tcpip.h>

#include<time.h>

#include "IPNode.h"

#include "NodeList.h"

#pragma comment(lib, "Ws2_32.lib")

// 定义IP头部

typedef struct IPHeader

{

unsigned char Version_HeaderLength; // 版本(4位)+首部长度(4位)

unsigned char TypeOfService; // 服务类型

unsigned short TotalLength; // 总长度

unsigned short Identification; // 标识

unsigned short Flags_FragmentOffset; // 标志(3位)+分片偏移(13位)

unsigned char TimeToLive; // 生存时间

unsigned char Protocal; // 协议

unsigned short HeaderChecksum; // 首部校验和

unsigned long SourceAddress; // 源IP地址

unsigned long DestAddress; // 目的IP地址

}IPHEADER;

/********************************************************************************************************************************************************************************************/

主函数所在文件:

// ConsoleApplication_IP.cpp : 定义控制台应用程序的入口点。

//

#include "stdafx.h"

#include "winsock2.h"

#include<ws2tcpip.h>

#include "ConsoleAPPlication_IP.h"

#include <iostream>

using namespace std;

#define IO_RCVALL _WSAIOW(IOC_VENDOR,1)

#define BURRER_SIZE 65535

int _tmain(int argc, _TCHAR* argv[])

{

//初始化Winsock DLL

WSADATA wsData;

if(WSAStartup(MAKEWORD(2,2),&wsData)!=0)

{

cout<<"Winsock DLL初始化失败!"<<endl;

return 1;

}

//创建socket

SOCKET sock;

sock=WSASocket(AF_INET,SOCK_RAW,IPPROTO_IP,NULL,0,0);

if(sock==INVALID_SOCKET)

{

cout<<"socket创建失败!"<<endl;

return 2;

}

// 设置IP头操作选项,表示用户可以亲自对IP头进行处理

BOOL bFlag = TRUE;

if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)&bFlag, sizeof(bFlag)) == SOCKET_ERROR)

{

cout << "Setsockopt 失败!" << endl;

return 3;

}

//绑定套接字

//获得主机网络地址

char LocalName[256];

gethostname(LocalName,256);

HOSTENT * pHost;

pHost=gethostbyname(LocalName);

//填充sockaddr_in结构

sockaddr_in addr_in;

addr_in.sin_family=AF_INET;

addr_in.sin_port=htons(8000);

addr_in.sin_addr=*(in_addr *)pHost->h_addr_list[0];

bind(sock,(sockaddr *)&addr_in,sizeof(addr_in));

//将网卡设置为混杂模式,以便接收所有所有的IP数据包

DWORD dwBufferLen[10];

DWORD dwBufferInLen=1;

DWORD dwBytesReturned=0;

WSAIoctl(sock,IO_RCVALL,&dwBufferInLen,sizeof(dwBufferInLen),&dwBufferLen,

sizeof(dwBufferLen),&dwBytesReturned,NULL,NULL);

// 把socket设置为非阻塞模式

DWORD dwTemp = 1;

ioctlsocket(sock, FIONBIO, &dwTemp);

// 设置接收缓冲区

char pBuffer[BURRER_SIZE];

CNodeList IpList;

double dwDuration = 10; // 捕获时间

time_t beg;

time_t end;

time(&beg); // 获得当前系统时间

// 输出本地IP地址

cout << endl;

cout << "本机IP:"

<< inet_ntoa(*(in_addr *)&(addr_in.sin_addr.S_un.S_addr)) << endl << endl;

cout << "开始捕获..." << endl << endl;

while (1)

{

time(&end); // 获得当前系统时间

//如果捕获时间到,就结束捕获

if (end-beg >= dwDuration)

{

break;

}

// 捕获经过网卡的IP数据包

int nPacketSize = recv(sock,pBuffer,BURRER_SIZE,0);

if (nPacketSize > 0)

{

IPHEADER * pIpHdr;

// 通过指针把缓冲区中的内容强制转换为IPHEADER数据结构

pIpHdr = (IPHEADER *)pBuffer;

// 判断IP包的源IP地址或目的IP地址是否为本地主机的IP地址

if (pIpHdr->SourceAddress == addr_in.sin_addr.S_un.S_addr

|| pIpHdr->DestAddress == addr_in.sin_addr.S_un.S_addr)

{

// 如果源IP地址或目的IP地址是本机IP,则将该IP数据包加入链表

IpList.addNode(pIpHdr->SourceAddress, pIpHdr->DestAddress, pIpHdr->Protocal,pIpHdr->TotalLength);

}

}

}

// 输出统计结果

cout << "IP数据包统计结果: (" << dwDuration << " 秒)"<< endl << endl;

IpList.print();

cout << endl;

system("PAUSE");

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: