【linux環境編程】 ARP編程

(注:部分摘自”Linux C編程一站式學習“)

以太網(RFC 894)幀格式


圖一 以太網數據包類型

其中的源地址和目的地址是指網卡的硬件地址(也叫MAC地址),長度是48位,是在網卡出廠時固化的。用ifconfig命令看一下,“HWaddr 00:15:F2:14:9E:3F”部分就是硬件地址。協議字段有三種值,分別對應IP、ARP、RARP。幀末尾是CRC校驗碼。


以太網幀中的數據長度規定最小46字節,最大1500字節,ARP和RARP數據包的長度不夠46字節,要在後面補填充位。最大值1500稱爲以太網的最大傳輸單元(MTU),不同的網絡類型有不同的MTU,如果一個數據包從以太網路由到撥號鏈路上,數據包長度大於撥號鏈路的MTU了,則需要對數據包進行分片(fragmentation)。ifconfig命令的輸出中也有“MTU:1500”。注意,MTU這個概念指數據幀中有效載荷的最大長度,不包括幀首部的長度。

在網絡通訊時,源主機的應用程序知道目的主機的IP地址和端口號,卻不知道目的主機的硬件地址,而數據包首先是被網卡接收到再去處理上層協議的,如果接收到的數據包的硬件地址與本機不符,則直接丟棄。因此在通訊前必須獲得目的主機的硬件地址。ARP協議就起到這個作用。源主機發出ARP請求,詢問“IP地址是192.168.0.1的主機的硬件地址是多少”,並將這個請求廣播到本地網段(以太網幀首部的硬件地址填FF:FF:FF:FF:FF:FF表示廣播),目的主機接收到廣播的ARP請求,發現其中的IP地址與本機相符,則發送一個ARP應答數據包給源主機,將自己的硬件地址填寫在應答包中。

每臺主機都維護一個ARP緩存表,可以用arp -a命令查看。緩存表中的表項有過期時間(一般爲20分鐘),如果20分鐘內沒有再次使用某個表項,則該表項失效,下次還要發ARP請求來獲得目的主機的硬件地址。想一想,爲什麼表項要有過期時間而不是一直有效?


圖二 arp數據包類型

注意到源MAC地址、目的MAC地址在以太網首部和ARP請求中各出現一次,對於鏈路層爲以太網的情況是多餘的,但如果鏈路層是其它類型的網絡則有可能是必要的。硬件類型指鏈路層網絡類型,1爲以太網,協議類型指要轉換的地址類型,0x0800爲IP地址,後面兩個地址長度對於以太網地址和IP地址分別爲6和4(字節),op字段爲1表示ARP請求,op字段爲2表示ARP應答。

有了上面的基礎知識,下面我們就來實戰編程:

1、確定socket的參數

根據圖一,我們可以看出arp,rarp和ip雖然同屬於網絡層(又名IP層),但是他們的數據包裝是獨立的。雖然icmp和igmp也處在IP層,但是它們又需要ip數據報的包裝。所以我們在爲arp和rarp建立socket的時候,就不能利用ip的原始數據報(SOCK_RAW)了,我們需要最原始的以太網幀的數包(SOCK_PACKET);在對於網絡類型的選擇上,可以根據需要選擇IPv4(AF_INET)或IPv6(AF_INET6);arp的協議類型跟以太網幀數據類型一樣,所以應該是0x0806,在linux的”Ethernet Protocol ID“中定義了

#define ETH_P_ARP 0x0806

那麼,我們就可以簡單的以爲socket套接字的創建是:sfd = socket(AF_INET,SOCK_PACKET,htons(ETH_P_ARP));

2、確定ARP數據包的結構

根據圖二,確定如下結構體

struct arp_packet
{
    //以太網首部
    unsigned char ap_dstmac[6];  //6字節
    unsigned char ap_srcmac[6];  //6字節
    unsigned short ap_frame;     //2字節
    //arp
    unsigned short ap_hwtype;    //2字節:硬件地址類型
    unsigned short ap_prototype; //2字節:軟件地址類型
    unsigned char  ap_hwlen;     //1字節:硬件地址長度   
    unsigned char  ap_prolen;    //1字節:軟件地址長度
    unsigned short ap_op;        //2字節:操作類型
    unsigned char  ap_frommac[6];//6字節
    unsigned char  ap_fromip[4]; //4字節
    unsigned char  ap_tomac[6];  //6字節
    unsigned char  ap_toip[4];   //4字節
    //18字節:填充字節,因爲以太網數據最少要46字節
    unsigned char  ap_padding[18];
};
3、獲取本地ip地址和mac地址

linux裏面提供了mac地址和ip地址獲取的ioctl參數;

#define SIOCGIFADDR 0x8915      /* get PA address       */
#define SIOCSIFADDR 0x8916      /* set PA address       */
#define SIOCSIFHWADDR   0x8924      /* set hardware address     */
#define SIOCGIFHWADDR   0x8927      /* Get hardware address     */
在獲取本機mac地址和ip地址之前,我們需要告訴ioctl我們要獲取那個網卡的參數:

struct ifreq  eth;
strcpy(eth.ifr_name,"eth0");
/* Get eth0 Hardware Address */
int ret = ioctl(fds,SIOCGIFHWADDR, ð);
if(ret < 0){
	perror("Get Hardware Address Fail:");
	goto close_socket;
}
/* Get eth0 IP Address */
ret = ioctl(fds, SIOCGIFADDR, ð);
if(ret < 0){
	perror("Get IP Address Fail:");
	goto close_socket;
}
unsigned char mac_addr[6];
memcpy(mac_addr,eth.ifr_hwaddr.sa_data,6);
/* In "struct sockaddr",ip address starts from 'sa_data' two bytes later*/
unsigned char ip_addr[4];
memcpy(ip_addr,eth.ifr_addr.sa_data+2,4);
4、數據包數據填寫

struct arp_packet arp_in;

bzero(&arp_in,sizeof(struct arp_packet));
unsigned char broadcast_mac[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
memcpy(arp_in.ap_dstmac,broadcast_mac,6);
memcpy(arp_in.ap_srcmac,mac_addr,6);
arp_in.ap_frame = htons(ETH_P_ARP);
arp_in.ap_hwtype = htons(0x0001);
arp_in.ap_prototype = htons(ETH_P_IP);
arp_in.ap_hwlen = 6;
arp_in.ap_prolen = 4;
arp_in.ap_op = htons(0x0001);//0x0001-ARP req 0x0002-ARP Reply
memcpy(arp_in.ap_frommac,mac_addr,6);
memcpy(arp_in.ap_fromip,ip_addr,4);
5、數據與接收

由於在這種情況下,對方的mac地址都是未知的,而且數據的發送也是作爲廣播模式發送。

所以這個時候我們只需告訴底層我們需要用那個網卡發送就可以了。

struct sockaddr eth;
eth.sa_family = AF_INET;
strcpy(eth.sa_data,"eth0");

ret = sendto(fds,&arp_in,sizeof(struct arp_packet),0,
		 (struct sockaddr *)ð,sizeof(struct sockaddr));
if(ret < 0){
	perror("Send Reqire ARP Packet Fail:");
	goto close_socket;
}
struct arp_packet arp_rc;
socklen_t slen = sizeof(struct sockaddr);
bzero(&arp_rc,sizeof(struct arp_packet));
ret = recvfrom(fds,&arp_rc,sizeof(struct arp_packet),0,
		   (struct sockaddr *)ð,&slen);
if(ret < 0){
	perror("Receive Replay ARP Packet Fail:");
	goto close_socket;
}

....


close_socket;
close(fds);
return (ret > 0 ? 1 : ret);

6、問題到此結束,測試後的結果如下:

-----------------------------Sendto----------------------------
Dest MAC:FF:FF:FF:FF:FF:FF
SRC  MAC:00:22:15:67:F8:16
HW  TYPE:0806
From    :210.42.158.204
To      :210.42.158.212
ARP   OP:0100
-----------------------------recvfrom-------------------------
Dest MAC:00:22:15:67:F8:16
SRC  MAC:00:E0:4C:DC:AA:1E
HW  TYPE:0806
From    :210.42.158.212
To      :210.42.158.204
ARP   OP:0200

至於ARP攻擊,我小測試了一把,還是可以的。但是許多電腦有arp防護,或者防火牆,arp攻擊時沒有用的。

爲了社會的安寧,這裏我就不把arp攻擊的代碼貼出來了。

如果有什麼問題,請各位留言指點!謝謝!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章