使用SOCK_PACKET编写ARP请求程序的例子

 

2010-01-14 11:06:35|  分类: linux program |  标签: |字号 订阅

使用SOCK_PACKET编写ARP请求程序的例子

本节利用SOCK_PACKET套接字进行ARP请求的程序设计,并给出代码的例子。

1.ARP协议数据和结构

包含以太网头部数据的ARP协议数据结构如图11.16所示。

 
(点击查看大图)图11.16  ARP协议的数据示意图
ARP的数据结构在头文件<linux/if_arp.h>中定义,代码如下:
  
  
  1. struct arphdr  
  2. {  
  3.     __be16      ar_hrd;         /*硬件类型*/  
  4.     __be16      ar_pro;         /*协议类型*/  
  5.     unsigned char   ar_hln;     /*硬件地址长度*/  
  6.     unsigned char   ar_pln;     /*协议地址长度*/  
  7.     __be16      ar_op;          /*ARP操作码*/  
  8. }; 

对于以太网上的ARP请求包,上述成员的值如表11.6所示。

表11.6  ARP在以太网上请求包的值和含义

   

   

ar_hrd

硬件类型

1

硬件地址为

以太网接口

ar_pro

协议类型

0x0800

高层协议为IP协议

ar_hln

硬件

地址长度

6

6字节,

MAC地址48

ar_pln

协议

地址长度

4

IP协议地

址长度为32

ar_op

ARP

操作码

1

ARP请求


2.例子中的ARP数据结构

按照图11.16所示,定义如下以太网的ARP数据结构:

  
  
  1. struct arppacket  
  2. {  
  3.     unsigned short  ar_hrd;                 /*硬件类型*/  
  4.     unsigned short  ar_pro;                 /*协议类型*/  
  5.     unsigned char   ar_hln;                 /*硬件地址长度*/  
  6.     unsigned char   ar_pln;                 /*协议地址长度*/  
  7.     unsigned short  ar_op;                  /*ARP操作码*/  
  8.     unsigned char   ar_sha[ETH_ALEN];       /*发送方MAC地址*/  
  9.     unsigned char   ar_sip[4];              /*发送方IP地址*/  
  10.     unsigned char   ar_tha[ETH_ALEN];       /*目的MAC地址*/  
  11.     unsigned char   ar_tip[4];              /*目的IP地址    */  
  12. }; 

3.ARP请求的主程序代码

ARP请求包的构建包含了以太网头部部分、ARP头部部分、ARP的数据部分。其中特别要注意目的以太网地址,由于ARP的作用就是查找目的IP地 址的MAC地址,所以目的以太网地址是未知的。而且需要在整个以太网上查找其IP地址,所以目的以太网地址是一个全为1的值,即为 {0xFF,0xFF,0xFF ,0xFF ,0xFF ,0xFF}。

  
  
  1. 01  #include <sys/socket.h> 
  2. 02  #include <sys/ioctl.h>                  /*ioctl 命令*/  
  3. 03  #include <Linux/if_ether.h>             /*ethhdr 结构*/  
  4. 04  #include <net/if.h>                     /*ifreq 结构*/  
  5. 05  #include <netinet/in.h>                 /*in_addr结构*/  
  6. 06  #include <Linux/ip.h>                   /*iphdr 结构*/  
  7. 07  #include <Linux/udp.h>                  /*udphdr 结构*/  
  8. 08  #include <Linux/tcp.h>                  /*tcphdr 结构*/  
  9. 09  struct arppacket  
  10. 10  {  
  11. 11      unsigned short  ar_hrd;             /*硬件类型*/  
  12. 12      unsigned short  ar_pro;             /*协议类型*/  
  13. 13      unsigned char   ar_hln;             /*硬件地址长度*/  
  14. 14      unsigned char   ar_pln;             /*协议地址长度*/  
  15. 15      unsigned short  ar_op;              /*ARP操作码*/  
  16. 16      unsigned char   ar_sha[ETH_ALEN];   /*发送方MAC地址*/  
  17. 17      unsigned char   ar_sip[4];          /*发送方IP地址*/  
  18. 18      unsigned char   ar_tha[ETH_ALEN];   /*目的MAC地址*/  
  19. 19      unsigned char   ar_tip[4];          /*目的IP地址*/  
  20. 20    
  21. 21  };  
  22. 22  int main(int argc, char*argv[])  
  23. 23  {  
  24. 24      char ef[ETH_FRAME_LEN];             /*以太帧缓冲区*/  
  25. 25      struct ethhdr*p_ethhdr;             /*以太网头部指针*/  
  26. 26      /*目的以太网地址*/  
  27. 27      char eth_dest[ETH_ALEN]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};  
  28. 28      /*源以太网地址*/  
  29. 29      char eth_source[ETH_ALEN]={0x00,0x0C,0x29,0x73,0x9D,0x15};  
  30. 30                                          /*目的IP地址*/  
  31. 31      char eth_dest[4]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};  
  32. 32      int n;        
  33. 33        
  34. 34      int fd;                             /*fd是套接口的描述符*/  
  35. 35      fd = socket(AF_INET, SOCK_PACKET, htons(0x0003));  
  36. 36        
  37. 37      /*使p_ethhdr指向以太网帧的帧头*/  
  38. 38      p_ethhdr = (struct ethhdr*)ef;  
  39. 39      /*复制目的以太网地址*/  
  40. 40      memcpy(p_ethhdr->h_dest, eth_dest, ETH_ALEN);  
  41. 41      /*复制源以太网地址*/  
  42. 42      memcpy(p_ethhdr->h_source, eth_source, ETH_ALEN);  
  43. 43      /*设置协议类型,以太网0x0806*/  
  44. 44      p_ethhdr->h_proto = htons(0x0806);  
  45. 45        
  46. 46      struct arppacket*p_arp;   
  47. 47      p_arp = ef + ETH_HLEN;              /*定位ARP包地址*/  
  48. 48      p_arp->ar_hrd = htons(0x1);         /*arp硬件类型*/  
  49. 49      p_arp->ar_pro = htons(0x0800);      /*协议类型*/  
  50. 50      p_arp->ar_hln = 6;                  /*硬件地址长度*/  
  51. 51      p_arp->ar_pln = 4;                  /*IP地址长度*/  
  52. 52      /*复制源以太网地址*/  
  53. 53      memcpy(p_arp->ar_sha, eth_source, ETH_ALEN);  
  54. 54      /*源IP地址*/  
  55. 55      (unsigned int*)p_arp->ar_sip = inet_addr("192.168.1.152");  
  56. 56      /*复制目的以太网地址*/  
  57. 57      memcpy(p_arp->ar_tha, eth_dest, ETH_ALEN);  
  58. 58      /*目的IP地址*/  
  59. 59      (unsigned int*)p_arp->ar_tip = inet_addr("192.168.1.1");      
  60. 60        
  61. 61      /*发送ARP请求8次,间隔1s*/  
  62. 62      int i = 0;  
  63. 63      for(i=0;i<8;i++){  
  64. 64          n = write(fd, ef, ETH_FRAME_LEN);/*发送*/  
  65. 65          sleep(1);                       /*等待1s*/  
  66. 66      }  
  67. 67  
  68. 68      close(fd);  
  69. 69      return 0;  
  70. 70  } 

上述代码分为如下步骤:

第27行为目的MAC,全部为0xFF,表示在局域网进行广播。

第29行为本机的MAC地址。

第35行建立一个SOCK_PACKET类型的套接字文件描述符。

第37~60行用于构建ARP请求包,第38行用于定位以太网头部。

第40行将目的以太网地址复制到以太网头部结构的成员h_dest中。

第42行将源以太网地址复制到以太网头部结构的成员h_source中。

第44行设置以太网的协议类型为0x0806,即ARP协议。

第47行定位ARP地址。

第48~51行设置ARP头部成员的值,如表11.1所示。

第53行复制源以太网地址,与第42行是一致的。

第55行设置发送端的IP地址。

第57~59行分别复制了目的以太网地址和目的IP地址。其中目的以太网地址全为1的值。

第62~66行发送数据,期间间隔1s,共发送8次。


获取局域网ip-mac例子:

 
/***************************************************************************
 *   Copyright (C) 2007 by qzc   *
 *   qzc1998@126.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>

#include <netinet/if_ether.h>
#include <netinet/in.h>

#include <arpa/inet.h>
#include <net/if.h>

/* 得到本机的mac地址和ip地址 */
int GetLocalMac ( const char *device,char *mac,char *ip )
{
    int sockfd;
    struct ifreq req;
    struct sockaddr_in * sin;

    if ( ( sockfd = socket ( PF_INET,SOCK_DGRAM,0 ) ) ==-1 )
    {
        fprintf ( stderr,"Sock Error:%s\n\a",strerror ( errno ) );
        return ( -1 );
    }

    memset ( &req,0,sizeof ( req ) );
    strcpy ( req.ifr_name,device );
    if ( ioctl ( sockfd,SIOCGIFHWADDR, ( char * ) &req ) ==-1 )
    {
        fprintf ( stderr,"ioctl SIOCGIFHWADDR:%s\n\a",strerror ( errno ) );
        close ( sockfd );
        return ( -1 );
    }
    memcpy ( mac,req.ifr_hwaddr.sa_da ta,6 );

    req.ifr_addr.sa_family = PF_INET;
    if ( ioctl ( sockfd,SIOCGIFADDR, ( char * ) &req ) ==-1 )
    {
        fprintf ( stderr,"ioctl SIOCGIFADDR:%s\n\a",strerror ( errno ) );
        close ( sockfd );
        return ( -1 );
    }
    sin = ( struct sockaddr_in * ) &req.ifr_addr;
    memcpy ( ip, ( char * ) &sin->sin_addr,4 );

    return ( 0 );
}

char *mac_ntoa ( const unsigned char *mac )
{
    /* Linux 下有 ether_ntoa(),不过我们重新写一个也很简单 */
    static char buffer[18];
    memset ( buffer,0,sizeof ( buffer ) );
    sprintf ( buffer,"%02X:%02X:%02X:%02X:%02X:%02X",
              mac[0],mac[1],mac[2],mac[3],mac[4],mac[5] );
    return ( buffer );
}

/* 根据 RFC 0826 修改*/
typedef struct _Ether_pkg Ether_pkg;
struct _Ether_pkg
{
    /* 前面是ethernet头 */
    unsigned char ether_dhost[6]; /* 目地硬件地址 */
    unsigned char ether_shost[6]; /* 源硬件地址 */
    unsigned short int ether_type; /* 网络类型 */

    /* 下面是arp协议 */
    unsigned short int ar_hrd; /* 硬件地址格式 */
    unsigned short int ar_pro; /* 协议地址格式 */
    unsigned char ar_hln; /* 硬件地址长度(字节) */
    unsigned char ar_pln; /* 协议地址长度(字节) */
    unsigned short int ar_op; /* 操作代码 */
    unsigned char arp_sha[6]; /* 源硬件地址 */
    unsigned char arp_spa[4]; /* 源协议地址 */
    unsigned char arp_tha[6]; /* 目地硬件地址 */
    unsigned char arp_tpa[4]; /* 目地协议地址 */
};

void parse_ether_package ( const Ether_pkg *pkg )
{
    printf ( "源   IP=[%s] MAC=[%s]\n",inet_ntoa ( * ( struct in_addr * ) pkg->arp_spa ),mac_ntoa ( pkg->arp_sha ) );
    printf ( "目地 IP=[%s] MAC=[%s]\n",inet_ntoa ( * ( struct in_addr * ) pkg->arp_tpa ),mac_ntoa ( pkg->arp_tha ) );
}

int sendpkg ( char * mac,char * broad_mac,char * ip,char * dest )
{
    Ether_pkg pkg;
    struct hostent *host =NULL;
    struct sockaddr sa;
    int sockfd,len;
    char buffer[255];
    unsigned char temp_ip[5];
    memset ( ( char * ) &pkg,'\0',sizeof ( pkg ) );

    /* 填充ethernet包文 */
    memcpy ( ( char * ) pkg.ether_shost, ( char * ) mac,6 );
    memcpy ( ( char * ) pkg.ether_dhost, ( char * ) broad_mac,6 );
    pkg.ether_type = htons ( ETHERTYPE_ARP );


    /* 下面填充arp包文 */
    pkg.ar_hrd = htons ( ARPHRD_ETHER );
    pkg.ar_pro = htons ( ETHERTYPE_IP );
    pkg.ar_hln = 6;
    pkg.ar_pln = 4;
    pkg.ar_op = htons ( ARPOP_REQUEST );
    memcpy ( ( char * ) pkg.arp_sha, ( char * ) mac,6 );
    memcpy ( ( char * ) pkg.arp_spa, ( char * ) ip,4 );
    memcpy ( ( char * ) pkg.arp_tha, ( char * ) broad_mac,6 );

    //printf ( "Resolve [%s],Please Waiting...",dest );
    fflush ( stdout );
    memset ( temp_ip,0,sizeof ( temp_ip ) );
    if ( inet_aton ( dest, ( struct in_addr * ) temp_ip ) ==0 )
    {
        if ( ( host = gethostbyname ( dest ) ) ==NULL )
        {
            fprintf ( stderr,"Fail! %s\n\a",hstrerror ( h_errno ) );
            return ( -1 );
        }
        memcpy ( ( char * ) temp_ip,host->h_addr,4 );
    }
    //printf ( " Done!\n" );
    memcpy ( ( char * ) pkg.arp_tpa, ( char * )temp_ip,4 );
    /* 实际应该使用PF_PACKET */
    if ( ( sockfd = socket ( PF_INET,SOCK_PACKET,htons ( ETH_P_ALL ) ) ) ==-1 )
    {
        fprintf ( stderr,"Socket Error:%s\n\a",strerror ( errno ) );
        return ( 0 );
    }

    memset ( &sa,'\0',sizeof ( sa ) );
    strcpy ( sa.sa_da ta,"eth0" );

    len = sendto ( sockfd,&pkg,sizeof ( pkg ),0,&sa,sizeof ( sa ) );
    if ( len != sizeof ( pkg ) )
    {
        fprintf ( stderr,"Sendto Error:%s\n\a",strerror ( errno ) );
        close(sockfd);
        return ( 0 );
    }
    Ether_pkg *parse;
    parse = ( Ether_pkg * ) buffer;
    fd_set readfds;
    struct timeval tv;
    while(1)
    {
        tv.tv_sec = 0;
        tv.tv_usec = 500000; //500毫秒
        FD_ZERO ( &readfds );
        FD_SET ( sockfd, &readfds );
        len = select ( sockfd+1, &readfds, 0, 0, &tv );
        if ( len>-1 )
        {
            if ( FD_ISSET ( sockfd,&readfds ) )
            {
                memset ( buffer,0,sizeof ( buffer ) );
                len=recvfrom ( sockfd,buffer,sizeof ( buffer ),0,NULL,&len );
                if ( ( ntohs ( parse->ether_type ) ==ETHERTYPE_ARP ) &&
                        ( ntohs ( parse->ar_op ) == ARPOP_REPLY ) )
                {
                    parse_ether_package ( parse );                
                }
            }
        }
        break;
    }
    close(sockfd);
    return 1;
}

int main ( int argc,char **argv )
{
    struct timeval tvafter,tvpre;
    struct timezone tz;
    gettimeofday ( &tvpre , &tz );

    unsigned char mac[7];
    unsigned char ip[5];
    char dest[16]={0};
    unsigned char broad_mac[7]={0xff,0xff,0xff,0xff,0xff,0xff,0x00};

    memset ( mac,0,sizeof ( mac ) );
    memset ( ip,0,sizeof ( ip ) );
    if ( GetLocalMac ( "eth0",mac,ip ) ==-1 )
        return ( -1 );

    printf ( "本地 Mac=[%s] Ip=[%s]\n", mac_ntoa ( mac ),inet_ntoa ( * ( struct in_addr * ) ip ) );

    //if ( argc==1 ) return ( -1 );
    int i=0;
//    for(i=1;i<256;i++)
    {
        //sprintf ( dest,"192.168.0.%d",i );
        sprintf ( dest,"255.255.255.255",i );
        sendpkg ( mac,broad_mac,ip,dest );
    }
    gettimeofday ( &tvafter , &tz );
    printf ( "\n程序执行完毕:%d毫秒\n", ( tvafter.tv_sec-tvpre.tv_sec ) *1000+ ( tvafter.tv_usec-tvpre.tv_usec ) /1000 );

    return 0;
}

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值