2010-01-14 11:06:35| 分类: linux program | 标签: |字号大中小 订阅
使用SOCK_PACKET编写ARP请求程序的例子
本节利用SOCK_PACKET套接字进行ARP请求的程序设计,并给出代码的例子。
1.ARP协议数据和结构
包含以太网头部数据的ARP协议数据结构如图11.16所示。
(点击查看大图)图11.16 ARP协议的数据示意图 |
- struct arphdr
- {
- __be16 ar_hrd; /*硬件类型*/
- __be16 ar_pro; /*协议类型*/
- unsigned char ar_hln; /*硬件地址长度*/
- unsigned char ar_pln; /*协议地址长度*/
- __be16 ar_op; /*ARP操作码*/
- };
对于以太网上的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数据结构:
- struct arppacket
- {
- unsigned short ar_hrd; /*硬件类型*/
- unsigned short ar_pro; /*协议类型*/
- unsigned char ar_hln; /*硬件地址长度*/
- unsigned char ar_pln; /*协议地址长度*/
- unsigned short ar_op; /*ARP操作码*/
- unsigned char ar_sha[ETH_ALEN]; /*发送方MAC地址*/
- unsigned char ar_sip[4]; /*发送方IP地址*/
- unsigned char ar_tha[ETH_ALEN]; /*目的MAC地址*/
- unsigned char ar_tip[4]; /*目的IP地址 */
- };
3.ARP请求的主程序代码
ARP请求包的构建包含了以太网头部部分、ARP头部部分、ARP的数据部分。其中特别要注意目的以太网地址,由于ARP的作用就是查找目的IP地 址的MAC地址,所以目的以太网地址是未知的。而且需要在整个以太网上查找其IP地址,所以目的以太网地址是一个全为1的值,即为 {0xFF,0xFF,0xFF ,0xFF ,0xFF ,0xFF}。
- 01 #include <sys/socket.h>
- 02 #include <sys/ioctl.h> /*ioctl 命令*/
- 03 #include <Linux/if_ether.h> /*ethhdr 结构*/
- 04 #include <net/if.h> /*ifreq 结构*/
- 05 #include <netinet/in.h> /*in_addr结构*/
- 06 #include <Linux/ip.h> /*iphdr 结构*/
- 07 #include <Linux/udp.h> /*udphdr 结构*/
- 08 #include <Linux/tcp.h> /*tcphdr 结构*/
- 09 struct arppacket
- 10 {
- 11 unsigned short ar_hrd; /*硬件类型*/
- 12 unsigned short ar_pro; /*协议类型*/
- 13 unsigned char ar_hln; /*硬件地址长度*/
- 14 unsigned char ar_pln; /*协议地址长度*/
- 15 unsigned short ar_op; /*ARP操作码*/
- 16 unsigned char ar_sha[ETH_ALEN]; /*发送方MAC地址*/
- 17 unsigned char ar_sip[4]; /*发送方IP地址*/
- 18 unsigned char ar_tha[ETH_ALEN]; /*目的MAC地址*/
- 19 unsigned char ar_tip[4]; /*目的IP地址*/
- 20
- 21 };
- 22 int main(int argc, char*argv[])
- 23 {
- 24 char ef[ETH_FRAME_LEN]; /*以太帧缓冲区*/
- 25 struct ethhdr*p_ethhdr; /*以太网头部指针*/
- 26 /*目的以太网地址*/
- 27 char eth_dest[ETH_ALEN]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
- 28 /*源以太网地址*/
- 29 char eth_source[ETH_ALEN]={0x00,0x0C,0x29,0x73,0x9D,0x15};
- 30 /*目的IP地址*/
- 31 char eth_dest[4]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
- 32 int n;
- 33
- 34 int fd; /*fd是套接口的描述符*/
- 35 fd = socket(AF_INET, SOCK_PACKET, htons(0x0003));
- 36
- 37 /*使p_ethhdr指向以太网帧的帧头*/
- 38 p_ethhdr = (struct ethhdr*)ef;
- 39 /*复制目的以太网地址*/
- 40 memcpy(p_ethhdr->h_dest, eth_dest, ETH_ALEN);
- 41 /*复制源以太网地址*/
- 42 memcpy(p_ethhdr->h_source, eth_source, ETH_ALEN);
- 43 /*设置协议类型,以太网0x0806*/
- 44 p_ethhdr->h_proto = htons(0x0806);
- 45
- 46 struct arppacket*p_arp;
- 47 p_arp = ef + ETH_HLEN; /*定位ARP包地址*/
- 48 p_arp->ar_hrd = htons(0x1); /*arp硬件类型*/
- 49 p_arp->ar_pro = htons(0x0800); /*协议类型*/
- 50 p_arp->ar_hln = 6; /*硬件地址长度*/
- 51 p_arp->ar_pln = 4; /*IP地址长度*/
- 52 /*复制源以太网地址*/
- 53 memcpy(p_arp->ar_sha, eth_source, ETH_ALEN);
- 54 /*源IP地址*/
- 55 (unsigned int*)p_arp->ar_sip = inet_addr("192.168.1.152");
- 56 /*复制目的以太网地址*/
- 57 memcpy(p_arp->ar_tha, eth_dest, ETH_ALEN);
- 58 /*目的IP地址*/
- 59 (unsigned int*)p_arp->ar_tip = inet_addr("192.168.1.1");
- 60
- 61 /*发送ARP请求8次,间隔1s*/
- 62 int i = 0;
- 63 for(i=0;i<8;i++){
- 64 n = write(fd, ef, ETH_FRAME_LEN);/*发送*/
- 65 sleep(1); /*等待1s*/
- 66 }
- 67
- 68 close(fd);
- 69 return 0;
- 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
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
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;
}