IP包流量分析程序

来源:互联网 发布:网络系统架构 编辑:程序博客网 时间:2024/03/28 19:40

      使用套接字编程实现捕获一段时间内以本机为源地址或目的地址的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;
}


   

0 0
原创粉丝点击