C++解析ARP数据包(可选网卡)

转自王柏元的博客C++解析ARP数据包(可选网卡)http://wangbaiyuan.cn/c-resolution-arp-packets.html

在网络通讯时,源主机的应用程序知道目的主机的IP地址和端口号,却不知道目的主机的硬件地址,而数据包首先是被网卡接收到再去处理上层协议的,如果接收到的数据包的硬件地址与本机不符,则直接丢弃。因此在通讯前必须获得目的主机的硬件地址。ARP协议就起到这个作用。源主机发出ARP请求,询问“IP地址是192.168.0.1的主机的硬件地址是多少”,并将这个请求广播到本地网段(以太网帧首部的硬件地址填FF:FF:FF:FF:FF:FF表示广播),目的主机接收到广播的ARP请求,发现其中的IP地址与本机相符,则发送一个ARP应答数据包给源主机,将自己的硬件地址填写在应答包中。

每台主机都维护一个ARP缓存表,可以用arp -a命令查看。缓存表中的表项有过期时间(一般为20分钟),如果20分钟内没有再次使用某个表项,则该表项失效,下次还要发ARP请求来获得目的主机的硬件地址。

本项目是基于win cap的,要想成功运行本代码,你需要下载安装win cap以及WpdPack并在visual studio中配置wincap的库。

// ParseArp.cpp : 定义控制台应用程序的入口点。
//
 
#include "stdafx.h"
#include<vector>
#include<conio.h>
#include<iostream>
#include<fstream>
#include<iomanip>
#include "pcap.h"
#include<winsock2.h>
using namespace std ;
#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"wpcap.lib")
//定义ARP包数据
struct arppkt
{
     unsigned short hdtyp ;                //硬件类型
     unsigned short protyp ;              //协议类型
     unsigned char hdsize ;                //硬件地址长度
     unsigned char prosize ;              //协议地址长度
     unsigned short op ;                  //(操作类型)操作值:   ARP/RARP
     u_char smac [ 6 ] ;                    //源MAC地址
     u_char sip [ 4 ] ;                      //源IP地址
     u_char dmac [ 6 ] ;                    //目的MAC地址
     u_char dip [ 4 ] ;                      //目的IP地址
} ;
void packet_handler ( const pcap_pkthdr * header , const u_char * pkt_data , ostream & out )
{
     //从ARP包中找到头部位置
     arppkt* arph = ( arppkt * ) ( pkt_data + 14 ) ;
     //输出源IP地址
     for ( int i = 0 ; i < 3 ; i ++ )
         out << int ( arph -> sip [ i ] ) << '.' ;
     out . setf ( ios :: left ) ;
     out << setw ( 3 ) << int ( arph -> sip [ 3 ] ) << "  " ;
     out . unsetf ( ios :: left ) ;
     //输出源MAC地址
     char oldfillchar = out . fill ( '0' ) ;
     out . setf ( ios :: uppercase ) ;
     for ( int i = 0 ; i < 5 ; i ++ )
         out << hex << setw ( 2 ) << int ( arph -> smac [ i ] ) << '-' ;
     out << hex << setw ( 2 ) << int ( arph -> smac [ 5 ] ) << "  " ;
     out . fill ( oldfillchar ) ;
     out . unsetf ( ios :: hex | ios :: uppercase ) ;
     //输出目的IP地址
     for ( int i = 0 ; i < 3 ; i ++ )
         out << int ( arph -> dip [ i ] ) << '.' ;
     out . unsetf ( ios :: left ) ;
     out << setw ( 3 ) << int ( arph -> dip [ 3 ] ) << ' ' ;
     out . unsetf ( ios :: left ) ;
     //输出目的MAC地址
     out . fill ( '0' ) ;
     out . setf ( ios :: uppercase ) ;
     for ( int i = 0 ; i < 5 ; i ++ )
         out << hex << setw ( 2 ) << int ( arph -> dmac [ i ] ) << '-' ;
     out << hex << setw ( 2 ) << int ( arph -> dmac [ 5 ] ) << " " ;
     out . fill ( oldfillchar ) ;
     out . unsetf ( ios :: hex | ios :: uppercase ) ;
     //输出操作类型
     out << ntohs ( arph -> op ) << "   " ;
     //输出操作时间
     time _t t ;
     time ( & t ) ;
     out << ctime ( & t ) ;
 
     out . fill ( oldfillchar ) ;
     out << endl ;
}
void main ( int argc , char * argv [ ] ) //命令行参数
{
     //检查输入命令格式
     if ( argc != 2 )
     {
         cout << "Please  input  command: ParseArp output_file" << endl ;
 
 
         return ;
     }
     //初始化网络设备相关参数
     pcap_if_t * alldevs ;
     pcap_if_t * d ;
     pcap_t * adhandle ;
     char errbuf [ PCAP_ERRBUF_SIZE ] ;
     u_int netmask ;
     char packet_filter [ ] = "ether proto \\arp" ;
     struct bpf_program fcode ;
     struct pcap_pkthdr * header ;
     const u_char * pkt_data ;
     vector < pcap_if_t* > devices ;
     vector < pcap_t * > adhandles ;
     //获取网络设备列表
     if ( pcap_findalldevs ( & alldevs , errbuf ) == - 1 )
     {
         cout << "Error in pcap_find all devs:" << errbuf ;
         return ;
     }
     //选取一个Ethernet网卡
     for ( d = alldevs ; d != NULL ; d = d -> next )
     {
         //    网卡设为混杂模式,接收所有帧
         if ( ( adhandle = pcap_open_live ( d -> name , 1000 , 1 , 300 , errbuf ) ) == NULL )
         {
             cout << "\nUnable to open the adapter." ;
             pcap_freealldevs ( alldevs ) ;
             return ;
         }
         //检查数据链路是否为Ethernet
         if ( pcap_datalink ( adhandle ) == DLT_EN10MB && d -> addresses != NULL ) {
     devices . push_back ( d ) ;
     adhandles . push_back ( adhandle ) ;
         }
        
     }
 
     vector < pcap_if_t * > :: iterator it ;
     for ( int i = 0 ; i < devices . size ( ) ; i ++ )
         cout << "网卡:" << i + 1 << ":" << devices [ i ] -> description << endl ;
 
     int i = 0 ;
     cout << "请输入网卡" ;
     cin >> i ;
     //获得子网掩码
     d = devices [ i - 1 ] ;
     netmask = ( ( sockaddr_in * ) ( d -> addresses -> netmask ) ) -> sin_addr . S_un . S_addr ;
     //编译过滤器,只捕获ARP包
     if ( pcap_compile ( adhandles [ i - 1 ] , & fcode , packet_filter , 1 , netmask ) < 0 )
     {
         cout << "\nUnable to compile the packet filter.Check the syntax.\n" ;
         pcap_freealldevs ( alldevs ) ;
         return ;
     }
     //设置过滤器
     if ( pcap_setfilter ( adhandles [ i - 1 ] , & fcode ) < 0 )
     {
         cout << "\nError setting the filter.\n" ;
         pcap_freealldevs ( alldevs ) ;
         return ;
     }
     //显示提示信息及每项含义
     cout << "listening on " << d -> description << "..." << endl << endl ;
 
     ofstream fout ( argv [ 1 ] , ios :: app ) ;    //日志记录文件
     //为了查看日志时的方便,其中加入了日期记录
     time _t t ;
     time ( & t ) ;
     fout . seekp ( 0 , ios :: end ) ;
     if ( fout . tellp ( ) )
         fout << endl ;
     fout << "\t\tARP request(1)/reply(2) on" << ctime ( & t ) ;
     cout << "Sour Ip Addr" << "  " << "Sour MAC Address" << "   " << "Des Ip Addr" << "   " << "Des MAC Address" << "   " << "OP" << "   " << "Time" << endl ;
     fout << "Sour Ip Addr" << "  " << "Sour MAC Address" << "   " << "Des Ip Addr" << "   " << "Des MAC Address" << "   " << "OP" << "   " << "Time" << endl ;
     //释放设备列表
     pcap_freealldevs ( alldevs ) ;
     //开始截获ARP包
     int result ;
     while ( ( result = pcap_next_ex ( adhandles [ i - 1 ] , & header , & pkt_data ) ) >= 0 )
     {    //循环解析ARP数据包
         if ( result == 0 )
             continue ;
         //解析ARP包,结果输出到屏幕与文件
         packet_handler ( header , pkt_data , cout ) ;
         packet_handler ( header , pkt_data , fout ) ;
     }
}

  • 0
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值