linux sock_raw原始套接字编程
生活随笔
收集整理的這篇文章主要介紹了
linux sock_raw原始套接字编程
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
sock_raw原始套接字編程可以接收到本機網(wǎng)卡上的數(shù)據(jù)幀或者數(shù)據(jù)包,對與監(jiān)聽網(wǎng)絡(luò)的流量和分析是很有作用的.一共可以有3種方式創(chuàng)建這種socket
?
1.socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)發(fā)送接收ip數(shù)據(jù)包
2.socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))發(fā)送接收以太網(wǎng)數(shù)據(jù)幀
3.socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))過時了,不要用啊
?
理解一下SOCK_RAW的原理, 比如網(wǎng)卡收到了一個 14+20+8+100+4 的udp的以太網(wǎng)數(shù)據(jù)幀.
?
首 先,網(wǎng)卡對該數(shù)據(jù)幀進行硬過濾(根據(jù)網(wǎng)卡的模式不同會有不同的動作,如果設(shè)置了promisc混雜模式的話,則不做任何過濾直接交給下一層輸 入例程,否則非本機mac或者廣播mac會被直接丟棄).按照上面的例子,如果成功的話,會進入ip輸入例程.但是在進入ip輸入例程之前,系統(tǒng)會檢查系 統(tǒng)中是否有通過socket(AF_PACKET, SOCK_RAW, ..)創(chuàng)建的套接字.如果有的話并且協(xié)議相符,在這個例子中就是需要ETH_P_IP或者ETH_P_ALL類型.系統(tǒng)就給每個這樣的socket接收緩 沖區(qū)發(fā)送一個數(shù)據(jù)幀拷貝.然后進入下一步.
?
其次,進入了ip輸入例程(ip層會對該數(shù)據(jù)包進行軟過濾,就是檢查校驗或者丟棄非本機ip 或者廣播ip的數(shù)據(jù)包等,具體要參考源代碼),例子 中就是如果成功的話會進入udp輸入例程.但是在交給udp輸入例程之前,系統(tǒng)會檢查系統(tǒng)中是否有通過socket(AF_INET, SOCK_RAW, ..)創(chuàng)建的套接字.如果有的話并且協(xié)議相符,在這個例子中就是需要IPPROTO_UDP類型.系統(tǒng)就給每個這樣的socket接收緩沖區(qū)發(fā)送一個數(shù)據(jù) 幀拷貝.然后進入下一步.
?
最后,進入udp輸入例程 ...
?
ps:如果校驗和出錯的話,內(nèi)核會直接丟棄該數(shù)據(jù)包的.而不會拷貝給sock_raw的套接字,因為校驗和都出錯了,數(shù)據(jù)肯定有問題的包括所有信息都沒有意義了.
?
進一步分析他們的能力.
1. socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
能:該套接字可以接收協(xié)議類型為(tcp udp icmp等)發(fā)往本機的ip數(shù)據(jù)包,從上面看的就是20+8+100.
不能:不能收到非發(fā)往本地ip的數(shù)據(jù)包(ip軟過濾會丟棄這些不是發(fā)往本機ip的數(shù)據(jù)包).
不能:不能收到從本機發(fā)送出去的數(shù)據(jù)包.
發(fā)送的話需要自己組織tcp udp icmp等頭部.可以setsockopt來自己包裝ip頭部
這種套接字用來寫個ping程序比較適合
????
2. socket(PF_PACKET, SOCK_RAW, htons(x));?
這個套接字比較強大,創(chuàng)建這種套接字可以監(jiān)聽網(wǎng)卡上的所有數(shù)據(jù)幀.從上面看就是20+20+8+100.最后一個以太網(wǎng)crc從來都不算進來的,因為內(nèi)核已經(jīng)判斷過了,對程序來說沒有任何意義了.
能: 接收發(fā)往本地mac的數(shù)據(jù)幀
能: 接收從本機發(fā)送出去的數(shù)據(jù)幀(第3個參數(shù)需要設(shè)置為ETH_P_ALL)
能: 接收非發(fā)往本地mac的數(shù)據(jù)幀(網(wǎng)卡需要設(shè)置為promisc混雜模式)
協(xié)議類型一共有四個
ETH_P_IP? 0x800????? 只接收發(fā)往本機mac的ip類型的數(shù)據(jù)幀
ETH_P_ARP 0x806????? 只接受發(fā)往本機mac的arp類型的數(shù)據(jù)幀
ETH_P_ARP 0x8035???? 只接受發(fā)往本機mac的rarp類型的數(shù)據(jù)幀
ETH_P_ALL 0x3??????? 接收發(fā)往本機mac的所有類型ip arp rarp的數(shù)據(jù)幀, 接收從本機發(fā)出的所有類型的數(shù)據(jù)幀.(混雜模式打開的情況下,會接收到非發(fā)往本地mac的數(shù)據(jù)幀)
?
發(fā) 送的時候需要自己組織整個以太網(wǎng)數(shù)據(jù)幀.所有相關(guān)的地址使用struct sockaddr_ll 而不是struct sockaddr_in(因為協(xié)議簇是PF_PACKET不是AF_INET了),比如發(fā)送給某個機器,對方的地址需要使用struct sockaddr_ll.
?
這種socket大小通吃,強悍 linux socket調(diào)用與arp報文發(fā)送
Linux提供最常用的網(wǎng)絡(luò)通信應(yīng)用程序開發(fā)接口--Berkerley套接字(Socket).它既適用于同一主機上進程間通信(IPC),又適用于不同主機上的進程間通信。套接字的設(shè)置通過socket調(diào)用完成:
int socket(int family,int type,int protocol);
其中family指通信域或協(xié)議族,Linux系統(tǒng)支持的網(wǎng)絡(luò)協(xié)議族有PF_UNIX,PF_IPX,PF_PACKET等幾十種;type為套接字類型,目前有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET等;protocol是套接字所用的特定協(xié)議類型號.
Linux系統(tǒng)提供的基于數(shù)據(jù)鏈路層開發(fā)應(yīng)用程序的接口集成在套接字中,它是通過創(chuàng)建packet類型的套接宇.使應(yīng)用程序可直接在數(shù)據(jù)鏈路層接收或發(fā)送未被系統(tǒng)處理的原始的數(shù)據(jù)報文(如ARP報文),用戶也可以使用packet類型的套接宇在物理層上定義自己特殊的網(wǎng)絡(luò)協(xié)議。只有注冊號為0的用戶(超級用戶)進程才能建立或打開用于訪問網(wǎng)絡(luò)低層的套接字.在Linux系統(tǒng)中,用以下三種方式創(chuàng)建的packet套接字可直接用于訪問數(shù)據(jù)鏈路層:
(1)PF_INET協(xié)議族中SOCK_PACKEI類型的套接字
(2)PF_PACKET協(xié)議族中SOCK_RAW類型的套接字
(3)PF_PACKET協(xié)議族中SOCK_DGRAM類型的套接字
Linux 2.0中對數(shù)據(jù)鏈路層的操作主要使用SOCK_PACKET定義的packet套接字.初始化定義如下:
sockfd=socket(AF_INET,SOCK_PACKET,protocol);
其中,protocol用于決定套接字所使用的物理層協(xié)議(在IEEE802.3中定義).筆者在此選擇常用的物理層協(xié)議ETH_P_IP(Internet協(xié)議).SOCK_PACKET使用一種比較老的sockaddr_pkt數(shù)據(jù)結(jié)構(gòu)來設(shè)置網(wǎng)絡(luò)接口。
在Linux 2 2中使用PF_PACKET代替SOCK_PACKET來定義packet套接字.這種套接字的初始化定義如下:
sockfd=socket(PF_PACKET,socket_type,protocol);
其中socket_type只能為SOCK_RAW或SOCK_DGRAM,protocol為物理層通信協(xié)議(同上)。SOCK_RAW和SOCK_DGRAM類型套接字使用一種與設(shè)備無關(guān)的標(biāo)準(zhǔn)物理層地址結(jié)構(gòu)sockaddr_ll,但具體操作的報文格式不同。SOCK_RAW套接字直接向網(wǎng)絡(luò)硬件驅(qū)動程序發(fā)送(或從網(wǎng)絡(luò)硬件驅(qū)動程序接收)沒有任何處理的完整數(shù)據(jù)報文(包括物理幀的幀頭),這就要求程序員必須了解對應(yīng)設(shè)備的物理幀幀頭結(jié)構(gòu),才能正確地裝載和分析報文。SOCK_DGRAM套接字收到的數(shù)據(jù)報文的物理幀幀頭會被系統(tǒng)自動去掉,同樣,在發(fā)送時.系統(tǒng)將會根據(jù)sockaddr_ll結(jié)構(gòu)中的目的地址信息為數(shù)據(jù)報文舔加一個臺適的物理幀幀頭。
默認(rèn)情況下.從任何接口收到的符合指定協(xié)議的所有數(shù)據(jù)報文都會被傳送到packet套接字。使用bind系統(tǒng)調(diào)用以一個sochddr_l1地址結(jié)構(gòu)將paccket套接字與某個網(wǎng)絡(luò)接口相綁定,可使套接字只接收指定接口的
數(shù)據(jù)報文.socaddr_ll地址結(jié)構(gòu)定義如下:
struct sockaddr_ll
{
unsigned short sll_family; /* 總是 AF_PACKET */
unsigned short sll_protocol; /* 物理層的協(xié)議 */
int sll_ifindex; /* 接口號 */
unsigned short sll_hatype; /* 報頭類型 */
unsigned char sll_pkttype; /* 分組類型 */
unsigned char sll_halen; /* 地址長度 */
unsigned char sll_addr[8]; /* 物理層地址 */
};
一、利用PF_lNET協(xié)議族中SOCK_PACKET類型的套接宇實現(xiàn)ARP
(1)建套接字
創(chuàng)建套接宇采用socket系統(tǒng)調(diào)用,格式如下:
sockfd=socket(PF_INET,SOCK_PACKET,htons(ETH_P_ARP));
(2)裝載報文
對于SOCK_PACKET類型的套接字,以太網(wǎng)物理幀頭應(yīng)作為所發(fā)送報文一部分由程序員設(shè)置,物理幀頭的格式定義如下:(in /usr/include/linux/if_ether.h)
92 struct ethhdr
93 {
94 unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
95 unsigned char h_source[ETH_ALEN]; /* source ether addr */
96 unsigned short h_proto; /* packet type ID field */
97 };
實際發(fā)送的地址解析報文幀由以太網(wǎng)物理幀頭與幀數(shù)據(jù)(ARP報文)共同組成,用結(jié)構(gòu)體ARPPACKET表
示如下:
typedef struct {
struct ethhdr eth_header; //struct defined in linux/if_ether.h
ARPHDR arp_header;
}ARPPACKET;
上述報文結(jié)構(gòu)的裝載比較簡單。對ARP部分,arp_header的設(shè)置如下:
ptk.arp_header.ar_hrd=htons(ARPHRD_ETHER); //ARPHRD_ETHER is defined in linux/if_arp.h
ptk.arp_header.ar_pro=htons(ETHERTYPE_IP);
ptk.arp_header.ar_hln=6;
ptk.arp_header.ar_pln=4;
ptk.arp_header.ar_op=htons(ARPOP_REQUEST);
pkt.arp_header.ar_sha[]、pkt.arp_header.ar_sip[]、pkt_arp_header.ar_tip[]分別填入本機的物理地址、ip地址和要解析的對方主機的ip地址.返回報文中pkt.arp_header.tha[]中的內(nèi)容就是解析
得到的對方主機的物理地址。
對于以太網(wǎng)幀頭部分,pkt.eth_header.h_dest[]為目的地址,即廣播物理地址0xFFFFFF, pkt.eth_header.source[]為本機物理地址(同pkt.arp_header.ar_sha[]),
pkt.eth_header.h_proto賦值htons(ETHERTYPE_ARP)表示為地址解析類型報文。
ETHERTYPE_ARP與ETH_P_ARP的值都是0x0806,只是定義的文件不同。前者定義在net/ethernet.h,后者定義在linux/if_ether.h
(3)報文的發(fā)送與接收
在數(shù)據(jù)鏈路層發(fā)送/接收報文與在IP層發(fā)送/接收數(shù)據(jù)報文類似,分別用系統(tǒng)調(diào)用sendto()和recvfrom()
完成,只是要將配置好的含有目標(biāo)地址的報文發(fā)往本地網(wǎng)絡(luò)硬件而不是目標(biāo)主機。相應(yīng)的程序段如下:
struct sockaddr to,from;
int fromlen=0;
strcpy(to.sa_data,"eth0");
sendto(sockfd,pkt,sizeof(struct ARPPACKET),0,&to,sizeof(struct sockaddr));
recvfrom(sockfd,buf,PACKET_SIZE,0,&from,&fromlen);
其中buf為包含結(jié)構(gòu)體ARPPACKET的字符型指針。
通過檢驗所接收到的ARP應(yīng)答報文中arp_header.ar_op項是否為ARPOP_REPLY(ARP應(yīng)答)同時arp_header.ar_tip是否為已知的對方主機的IP地址來判斷所得到的解析地址是否正確.
二、利用PF_PACKET協(xié)議族中SOCK_RAW類型的套接字實現(xiàn)ARP
(1)創(chuàng)建套接字
sockfd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ARP));
(2)裝載報文
與SOCK_PACKET類型的套接字相同,SOCK_RAW類型的套接字對鏈路層操作時,也要求以太網(wǎng)物理幀頭應(yīng)作為所發(fā)送報文一部分由程序員設(shè)置.
(3)報文的發(fā)送與接收
SOCK_RAW類型套接字使用標(biāo)準(zhǔn)的物理層地址結(jié)構(gòu)sockaddr_ll,所以,報文發(fā)送之前,應(yīng)將套接字綁定到(使用bind()系統(tǒng)調(diào)用)配置好的本地物理地址結(jié)構(gòu)my_etheraddr,同時還需配置目的物理地址結(jié)構(gòu)broad_etheraddr.
示例如下:
struct aockaddr_ll my_etheraddr,broad_etheraddr;
my_etheraddr.sll_family=AF_PACKET;
my_etheraddr.sll_protocol=htons(ETH_P_ARP);
my_etheraddr.sll_ifindex=2; /*接口號2表示是eth0*/
my_etheraddr.sll_hatype=ARPHRD_ETHER;
my_etheraddr.sll_pkttype=PACKET_HOST;
my_etheraddr.sll_halen=ETH_ALEN;
my_etheraddr.sll_addr[8]中放入本主機的物理地址。
broad_etheraddr的配置除了sll_pkttype取PACKET_BROADCAST和sll_addr取廣播物理地址(0xFFFFFF)外,其他選項與my_etheraddr配置相同。
綁定格式如下:
bind(sockfd,(struct sockaddr *)&my_etheraddr,sizeof(my_etheraddr));
發(fā)送與接收調(diào)用程序如下:
sendto(sockfd,buf,sizeof(struct ARPPACKET),0,
(struct sockaddr *)&broad_etheraddr,sizeof(broad_etheraddr));
recvform(sockfd,buf,PACKET_SIZE,&from,&fromlen);
三、利用PF_PACKET協(xié)議族中SOCK_DGRAM類型的套接字實現(xiàn)ARP
SOCK_DGRAM類型的套接字不要求程序員配置以太網(wǎng)幀頭,所以所發(fā)送的報文只有數(shù)據(jù)區(qū)(ARP報文)部分,其它與SOCK_RAW類型的套接字相同。
?
1.socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)發(fā)送接收ip數(shù)據(jù)包
2.socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))發(fā)送接收以太網(wǎng)數(shù)據(jù)幀
3.socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))過時了,不要用啊
?
理解一下SOCK_RAW的原理, 比如網(wǎng)卡收到了一個 14+20+8+100+4 的udp的以太網(wǎng)數(shù)據(jù)幀.
?
首 先,網(wǎng)卡對該數(shù)據(jù)幀進行硬過濾(根據(jù)網(wǎng)卡的模式不同會有不同的動作,如果設(shè)置了promisc混雜模式的話,則不做任何過濾直接交給下一層輸 入例程,否則非本機mac或者廣播mac會被直接丟棄).按照上面的例子,如果成功的話,會進入ip輸入例程.但是在進入ip輸入例程之前,系統(tǒng)會檢查系 統(tǒng)中是否有通過socket(AF_PACKET, SOCK_RAW, ..)創(chuàng)建的套接字.如果有的話并且協(xié)議相符,在這個例子中就是需要ETH_P_IP或者ETH_P_ALL類型.系統(tǒng)就給每個這樣的socket接收緩 沖區(qū)發(fā)送一個數(shù)據(jù)幀拷貝.然后進入下一步.
?
其次,進入了ip輸入例程(ip層會對該數(shù)據(jù)包進行軟過濾,就是檢查校驗或者丟棄非本機ip 或者廣播ip的數(shù)據(jù)包等,具體要參考源代碼),例子 中就是如果成功的話會進入udp輸入例程.但是在交給udp輸入例程之前,系統(tǒng)會檢查系統(tǒng)中是否有通過socket(AF_INET, SOCK_RAW, ..)創(chuàng)建的套接字.如果有的話并且協(xié)議相符,在這個例子中就是需要IPPROTO_UDP類型.系統(tǒng)就給每個這樣的socket接收緩沖區(qū)發(fā)送一個數(shù)據(jù) 幀拷貝.然后進入下一步.
?
最后,進入udp輸入例程 ...
?
ps:如果校驗和出錯的話,內(nèi)核會直接丟棄該數(shù)據(jù)包的.而不會拷貝給sock_raw的套接字,因為校驗和都出錯了,數(shù)據(jù)肯定有問題的包括所有信息都沒有意義了.
?
進一步分析他們的能力.
1. socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
能:該套接字可以接收協(xié)議類型為(tcp udp icmp等)發(fā)往本機的ip數(shù)據(jù)包,從上面看的就是20+8+100.
不能:不能收到非發(fā)往本地ip的數(shù)據(jù)包(ip軟過濾會丟棄這些不是發(fā)往本機ip的數(shù)據(jù)包).
不能:不能收到從本機發(fā)送出去的數(shù)據(jù)包.
發(fā)送的話需要自己組織tcp udp icmp等頭部.可以setsockopt來自己包裝ip頭部
這種套接字用來寫個ping程序比較適合
????
2. socket(PF_PACKET, SOCK_RAW, htons(x));?
這個套接字比較強大,創(chuàng)建這種套接字可以監(jiān)聽網(wǎng)卡上的所有數(shù)據(jù)幀.從上面看就是20+20+8+100.最后一個以太網(wǎng)crc從來都不算進來的,因為內(nèi)核已經(jīng)判斷過了,對程序來說沒有任何意義了.
能: 接收發(fā)往本地mac的數(shù)據(jù)幀
能: 接收從本機發(fā)送出去的數(shù)據(jù)幀(第3個參數(shù)需要設(shè)置為ETH_P_ALL)
能: 接收非發(fā)往本地mac的數(shù)據(jù)幀(網(wǎng)卡需要設(shè)置為promisc混雜模式)
協(xié)議類型一共有四個
ETH_P_IP? 0x800????? 只接收發(fā)往本機mac的ip類型的數(shù)據(jù)幀
ETH_P_ARP 0x806????? 只接受發(fā)往本機mac的arp類型的數(shù)據(jù)幀
ETH_P_ARP 0x8035???? 只接受發(fā)往本機mac的rarp類型的數(shù)據(jù)幀
ETH_P_ALL 0x3??????? 接收發(fā)往本機mac的所有類型ip arp rarp的數(shù)據(jù)幀, 接收從本機發(fā)出的所有類型的數(shù)據(jù)幀.(混雜模式打開的情況下,會接收到非發(fā)往本地mac的數(shù)據(jù)幀)
?
發(fā) 送的時候需要自己組織整個以太網(wǎng)數(shù)據(jù)幀.所有相關(guān)的地址使用struct sockaddr_ll 而不是struct sockaddr_in(因為協(xié)議簇是PF_PACKET不是AF_INET了),比如發(fā)送給某個機器,對方的地址需要使用struct sockaddr_ll.
?
這種socket大小通吃,強悍 linux socket調(diào)用與arp報文發(fā)送
Linux提供最常用的網(wǎng)絡(luò)通信應(yīng)用程序開發(fā)接口--Berkerley套接字(Socket).它既適用于同一主機上進程間通信(IPC),又適用于不同主機上的進程間通信。套接字的設(shè)置通過socket調(diào)用完成:
int socket(int family,int type,int protocol);
其中family指通信域或協(xié)議族,Linux系統(tǒng)支持的網(wǎng)絡(luò)協(xié)議族有PF_UNIX,PF_IPX,PF_PACKET等幾十種;type為套接字類型,目前有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET等;protocol是套接字所用的特定協(xié)議類型號.
Linux系統(tǒng)提供的基于數(shù)據(jù)鏈路層開發(fā)應(yīng)用程序的接口集成在套接字中,它是通過創(chuàng)建packet類型的套接宇.使應(yīng)用程序可直接在數(shù)據(jù)鏈路層接收或發(fā)送未被系統(tǒng)處理的原始的數(shù)據(jù)報文(如ARP報文),用戶也可以使用packet類型的套接宇在物理層上定義自己特殊的網(wǎng)絡(luò)協(xié)議。只有注冊號為0的用戶(超級用戶)進程才能建立或打開用于訪問網(wǎng)絡(luò)低層的套接字.在Linux系統(tǒng)中,用以下三種方式創(chuàng)建的packet套接字可直接用于訪問數(shù)據(jù)鏈路層:
(1)PF_INET協(xié)議族中SOCK_PACKEI類型的套接字
(2)PF_PACKET協(xié)議族中SOCK_RAW類型的套接字
(3)PF_PACKET協(xié)議族中SOCK_DGRAM類型的套接字
Linux 2.0中對數(shù)據(jù)鏈路層的操作主要使用SOCK_PACKET定義的packet套接字.初始化定義如下:
sockfd=socket(AF_INET,SOCK_PACKET,protocol);
其中,protocol用于決定套接字所使用的物理層協(xié)議(在IEEE802.3中定義).筆者在此選擇常用的物理層協(xié)議ETH_P_IP(Internet協(xié)議).SOCK_PACKET使用一種比較老的sockaddr_pkt數(shù)據(jù)結(jié)構(gòu)來設(shè)置網(wǎng)絡(luò)接口。
在Linux 2 2中使用PF_PACKET代替SOCK_PACKET來定義packet套接字.這種套接字的初始化定義如下:
sockfd=socket(PF_PACKET,socket_type,protocol);
其中socket_type只能為SOCK_RAW或SOCK_DGRAM,protocol為物理層通信協(xié)議(同上)。SOCK_RAW和SOCK_DGRAM類型套接字使用一種與設(shè)備無關(guān)的標(biāo)準(zhǔn)物理層地址結(jié)構(gòu)sockaddr_ll,但具體操作的報文格式不同。SOCK_RAW套接字直接向網(wǎng)絡(luò)硬件驅(qū)動程序發(fā)送(或從網(wǎng)絡(luò)硬件驅(qū)動程序接收)沒有任何處理的完整數(shù)據(jù)報文(包括物理幀的幀頭),這就要求程序員必須了解對應(yīng)設(shè)備的物理幀幀頭結(jié)構(gòu),才能正確地裝載和分析報文。SOCK_DGRAM套接字收到的數(shù)據(jù)報文的物理幀幀頭會被系統(tǒng)自動去掉,同樣,在發(fā)送時.系統(tǒng)將會根據(jù)sockaddr_ll結(jié)構(gòu)中的目的地址信息為數(shù)據(jù)報文舔加一個臺適的物理幀幀頭。
默認(rèn)情況下.從任何接口收到的符合指定協(xié)議的所有數(shù)據(jù)報文都會被傳送到packet套接字。使用bind系統(tǒng)調(diào)用以一個sochddr_l1地址結(jié)構(gòu)將paccket套接字與某個網(wǎng)絡(luò)接口相綁定,可使套接字只接收指定接口的
數(shù)據(jù)報文.socaddr_ll地址結(jié)構(gòu)定義如下:
struct sockaddr_ll
{
unsigned short sll_family; /* 總是 AF_PACKET */
unsigned short sll_protocol; /* 物理層的協(xié)議 */
int sll_ifindex; /* 接口號 */
unsigned short sll_hatype; /* 報頭類型 */
unsigned char sll_pkttype; /* 分組類型 */
unsigned char sll_halen; /* 地址長度 */
unsigned char sll_addr[8]; /* 物理層地址 */
};
一、利用PF_lNET協(xié)議族中SOCK_PACKET類型的套接宇實現(xiàn)ARP
(1)建套接字
創(chuàng)建套接宇采用socket系統(tǒng)調(diào)用,格式如下:
sockfd=socket(PF_INET,SOCK_PACKET,htons(ETH_P_ARP));
(2)裝載報文
對于SOCK_PACKET類型的套接字,以太網(wǎng)物理幀頭應(yīng)作為所發(fā)送報文一部分由程序員設(shè)置,物理幀頭的格式定義如下:(in /usr/include/linux/if_ether.h)
92 struct ethhdr
93 {
94 unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
95 unsigned char h_source[ETH_ALEN]; /* source ether addr */
96 unsigned short h_proto; /* packet type ID field */
97 };
實際發(fā)送的地址解析報文幀由以太網(wǎng)物理幀頭與幀數(shù)據(jù)(ARP報文)共同組成,用結(jié)構(gòu)體ARPPACKET表
示如下:
typedef struct {
struct ethhdr eth_header; //struct defined in linux/if_ether.h
ARPHDR arp_header;
}ARPPACKET;
上述報文結(jié)構(gòu)的裝載比較簡單。對ARP部分,arp_header的設(shè)置如下:
ptk.arp_header.ar_hrd=htons(ARPHRD_ETHER); //ARPHRD_ETHER is defined in linux/if_arp.h
ptk.arp_header.ar_pro=htons(ETHERTYPE_IP);
ptk.arp_header.ar_hln=6;
ptk.arp_header.ar_pln=4;
ptk.arp_header.ar_op=htons(ARPOP_REQUEST);
pkt.arp_header.ar_sha[]、pkt.arp_header.ar_sip[]、pkt_arp_header.ar_tip[]分別填入本機的物理地址、ip地址和要解析的對方主機的ip地址.返回報文中pkt.arp_header.tha[]中的內(nèi)容就是解析
得到的對方主機的物理地址。
對于以太網(wǎng)幀頭部分,pkt.eth_header.h_dest[]為目的地址,即廣播物理地址0xFFFFFF, pkt.eth_header.source[]為本機物理地址(同pkt.arp_header.ar_sha[]),
pkt.eth_header.h_proto賦值htons(ETHERTYPE_ARP)表示為地址解析類型報文。
ETHERTYPE_ARP與ETH_P_ARP的值都是0x0806,只是定義的文件不同。前者定義在net/ethernet.h,后者定義在linux/if_ether.h
(3)報文的發(fā)送與接收
在數(shù)據(jù)鏈路層發(fā)送/接收報文與在IP層發(fā)送/接收數(shù)據(jù)報文類似,分別用系統(tǒng)調(diào)用sendto()和recvfrom()
完成,只是要將配置好的含有目標(biāo)地址的報文發(fā)往本地網(wǎng)絡(luò)硬件而不是目標(biāo)主機。相應(yīng)的程序段如下:
struct sockaddr to,from;
int fromlen=0;
strcpy(to.sa_data,"eth0");
sendto(sockfd,pkt,sizeof(struct ARPPACKET),0,&to,sizeof(struct sockaddr));
recvfrom(sockfd,buf,PACKET_SIZE,0,&from,&fromlen);
其中buf為包含結(jié)構(gòu)體ARPPACKET的字符型指針。
通過檢驗所接收到的ARP應(yīng)答報文中arp_header.ar_op項是否為ARPOP_REPLY(ARP應(yīng)答)同時arp_header.ar_tip是否為已知的對方主機的IP地址來判斷所得到的解析地址是否正確.
二、利用PF_PACKET協(xié)議族中SOCK_RAW類型的套接字實現(xiàn)ARP
(1)創(chuàng)建套接字
sockfd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ARP));
(2)裝載報文
與SOCK_PACKET類型的套接字相同,SOCK_RAW類型的套接字對鏈路層操作時,也要求以太網(wǎng)物理幀頭應(yīng)作為所發(fā)送報文一部分由程序員設(shè)置.
(3)報文的發(fā)送與接收
SOCK_RAW類型套接字使用標(biāo)準(zhǔn)的物理層地址結(jié)構(gòu)sockaddr_ll,所以,報文發(fā)送之前,應(yīng)將套接字綁定到(使用bind()系統(tǒng)調(diào)用)配置好的本地物理地址結(jié)構(gòu)my_etheraddr,同時還需配置目的物理地址結(jié)構(gòu)broad_etheraddr.
示例如下:
struct aockaddr_ll my_etheraddr,broad_etheraddr;
my_etheraddr.sll_family=AF_PACKET;
my_etheraddr.sll_protocol=htons(ETH_P_ARP);
my_etheraddr.sll_ifindex=2; /*接口號2表示是eth0*/
my_etheraddr.sll_hatype=ARPHRD_ETHER;
my_etheraddr.sll_pkttype=PACKET_HOST;
my_etheraddr.sll_halen=ETH_ALEN;
my_etheraddr.sll_addr[8]中放入本主機的物理地址。
broad_etheraddr的配置除了sll_pkttype取PACKET_BROADCAST和sll_addr取廣播物理地址(0xFFFFFF)外,其他選項與my_etheraddr配置相同。
綁定格式如下:
bind(sockfd,(struct sockaddr *)&my_etheraddr,sizeof(my_etheraddr));
發(fā)送與接收調(diào)用程序如下:
sendto(sockfd,buf,sizeof(struct ARPPACKET),0,
(struct sockaddr *)&broad_etheraddr,sizeof(broad_etheraddr));
recvform(sockfd,buf,PACKET_SIZE,&from,&fromlen);
三、利用PF_PACKET協(xié)議族中SOCK_DGRAM類型的套接字實現(xiàn)ARP
SOCK_DGRAM類型的套接字不要求程序員配置以太網(wǎng)幀頭,所以所發(fā)送的報文只有數(shù)據(jù)區(qū)(ARP報文)部分,其它與SOCK_RAW類型的套接字相同。
總結(jié)
以上是生活随笔為你收集整理的linux sock_raw原始套接字编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hdu 6026 Deleting Ed
- 下一篇: linux意想不到题4