本节利用SOCK_PACKET套接字进行ARP请求的程序设计,并给出代码的例子。
包含以太网头部数据的ARP协议数据结构如图11.16所示。
图11.16 ARP协议的数据示意图
ARP的数据结构在头文件<linux/if_arp.h>中定义,代码如下:
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请求 |
按照图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地址 */
};
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 }
上述代码分为如下步骤:
q 第27行为目的MAC,全部为0xFF,表示在局域网进行广播。
q 第29行为本机的MAC地址。
q 第35行建立一个SOCK_PACKET类型的套接字文件描述符。
q 第37~60行用于构建ARP请求包,第38行用于定位以太网头部。
q 第40行将目的以太网地址复制到以太网头部结构的成员h_dest中。
q 第42行将源以太网地址复制到以太网头部结构的成员h_source中。
q 第44行设置以太网的协议类型为0x0806,即ARP协议。
q 第47行定位ARP地址。
q 第48~51行设置ARP头部成员的值,如表11.1所示。
q 第53行复制源以太网地址,与第42行是一致的。
q 第55行设置发送端的IP地址。
q 第57~59行分别复制了目的以太网地址和目的IP地址。其中目的以太网地址全为1的值。
q 第62~66行发送数据,期间间隔1s,共发送8次。