原始套接字SOCK_RAW


原始套接字SOCK_RAW

實際上,我們常用的網絡編程都是在應用層的報文的收發操作,也就是大多數程序員接觸到的流式套接字(SOCK_STREAM)和數據包式套接字(SOCK_DGRAM)。而這些數據包都是由系統提供的協議棧實現,用戶只需要填充應用層報文即可,由系統完成底層報文頭的填充並發送。然而在某些情況下需要執行更底層的操作,比如修改報文頭、避開系統協議棧等。這個時候就需要使用其他的方式來實現。

 

一 原始套接字

原始套接字(SOCK_RAW)是一種不同於SOCK_STREAM、SOCK_DGRAM的套接字,它實現於系統核心。然而,原始套接字能做什么呢?首先來說,普通的套接字無法處理ICMP、IGMP等網絡報文,而SOCK_RAW可以;其次,SOCK_RAW也可以處理特殊的IPv4報文;此外,利用原始套接字,可以通過IP_HDRINCL套接字選項由用戶構造IP頭。總體來說,SOCK_RAW可以處理普通的網絡報文之外,還可以處理一些特殊協議報文以及操作IP層及其以上的數據。

既然SOCK_RAW有以上特性,所以在某些處理流程上它區別於普通套接字。

·  若設置IP_HDRINCL選項,SOCK_RAW可以操作IP頭數據(也就是用戶需用填充IP頭及其以上的payload);否則SOCK_RAW無法操作IP頭數據

·  端口對於SOCK_RAW而言沒有任何意義

·  如果使用bind函數綁定本地IP,那么如果IP_HDRINCL未設置,則用此IP填充源IP地址;若不調用bind則將源IP地址設置為外出接口的主IP地址

·  如果使用connect函數設置目標IP,則可以使用send或者write函數發送報文,而不需要使用sendto函數

·  內核處理流程:

·   接收到的TCP、UDP分組不會傳遞給任何SOCK_RAW

·  ICMP、IGMP報文分組傳遞給SOCK_RAW

·  內核不識別的IP報文傳遞給SOCK_RAW

·  SOCK_RAW是否接收報文:

·      Protocol指定類型需要匹配,否則不傳遞給該SOCK_RAW

·       如果使用bind函數綁定了源IP,則報文目的IP必須和綁定的IP匹配,否則不傳遞給該SOCK_RAW

·       如果使用connect函數綁定了目的IP,則報文源IP必須和指定的IP匹配,否則不傳遞給該SOCK_RAW 

 

綜上所述,原始套接字處理的只是IP層及其以上的數據,比如實現SYN FLOOD攻擊、處理PING報文等。當需要操作更底層的數據的時候,就需要采用其他的方式。

二 鏈路層處理報文

如果需要從鏈路層處理報文,那么就需要采用更加底層的套接字。還是先看下套接字函數的原型吧:

#include <sys/socket.h>

int socket(int domain, int type, int protocol);

這個函數中,domain表示協議簇,type表示套接字類型,而protocol表示的是處理的協議類型。在Linux下提供了多種底層套接字。下面分別進行簡單介紹。

1 PF_INET協議簇

通過PF_INET可以構造原始套接字,如下所示:

int fd = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);

正如前面所講的,它工作在IP層及其以上各層協議上(當然是在使用IP_HDRINCL選項之后才能操作IP層數據啦),但是這種套接字無法接收從本地發送出去的報文。而使用SOCK_PACKET類型的套接字,則可以操作鏈路層數據了:

int fd = socket (PF_INET, SOCK_PACKET, IPPROTO_TCP);

不過據說這種方式存在一定的缺陷,而且也不能保證后續版本的系統上一定支持這種方式,因此不推薦使用

2 PF_PACKET協議簇

PF_PACKET協議簇是用來取代SOCK_PACKET的一種編程接口。作為一種協議簇,它可以對應兩種不同的套接字類型:SOCK_RAW和SOCK_DGRAM。當使用SOCK_RAW時,用戶操作鏈路層數據,但是如果使用后者,則由系統處理鏈路層協議頭。這種套接字支持四種協議(ETH_P_IP、ETH_P_ARP、ETH_P_RARP、ETH_P_ALL)(未確認)

int fd = socket (PF_PACKET, SOCK_RAW, IPPROTO_TCP);

int fd = socket (PF_PACKET, SOCK_DGRAM, IPPROTO_TCP);

3 NETLINK協議簇

這種方式是用戶模式和kernel的IP網絡配置之間的推薦接口

 

 

綜上所述,真正能夠實現操作鏈路層數據的只有三種方式:

int fd = socket (PF_INET, SOCK_PACKET, IPPROTO_TCP);

int fd = socket (PF_PACKET, SOCK_RAW, IPPROTO_TCP);

int fd = socket (PF_PACKET, SOCK_DGRAM, IPPROTO_TCP);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM