UNIX網路程式設計 原始套接字SOCK RAW

2021-07-25 14:15:29 字數 3328 閱讀 9039

實際上,我們常用的網路程式設計都是在應用層的報文的收發操作,也就是大多數程式設計師接觸到的流式套接字

(sock_stream)

和資料報式套接字

(sock_dgram)

。而這些資料報都是由系統提供的協議棧實現,使用者只需要填充應用層報文即可,由系統完成底層報文頭的填充並傳送。然而在某些情況下需要執行更底層的操作,比如修改報文頭、避開系統協議棧等。這個時候就需要使用其他的方式來實現。

原始套接字(sock_raw)

是一種不同於

sock_stream

、sock_dgram

的套接字,它實現於系統核心。然而,原始套接字能做什麼呢?首先來說,普通的套接字無法處理

icmp

、igmp

等網路報文,而

sock_raw

可以;其次,

sock_raw

也可以處理特殊的

ipv4

報文;此外,利用原始套接字,可以通過

ip_hdrincl

套接字選項由使用者構造

ip頭。總體來說,sock_raw

可以處理普通的網路報文之外,還可以處理一些特殊協議報文以及操作

ip層及其以上的資料。

原始套接字可以用來自行組裝ip資料報,然後將資料報傳送到其他終端。必須在

管理員許可權下

才能使用原始套接字,這麼做可防止普通使用者往網路寫出它們自行構造的ip資料報。

(1)原始套接字的建立:

int sockfd = socket (af_inet, sock_raw, protocol);  

把第二個引數設定為sock_raw,第三個引數(協議)通常不為0。其中protocol引數時形如ipproto_***的某個常值,定義在標頭檔案中,如ipproto_icmp、ipproto_udp、ipproto_tcp等。

可以在這個套接字上按以下方式開啟ip_hdrincl套接字選項(隨資料報含的ip首部):

const

int on =1;  

if (setsockopt (sockfd, ipproto_ip, ip_hdrincl, &on, sizeof(on)) < 0)  

錯誤處理;  

可以在這個套接字上呼叫bind函式,但是比較少見。bind函式僅僅設定本地位址,因為原始套接字不存在埠的概念。就輸出而言,呼叫bind設定的是將用於從這個原始套接字傳送的所有資料報源ip位址(只在ip_hdrincl套接字選項未開啟的前提下)。如果不呼叫bind,核心就把源ip位址設定為外出介面的主ip位址。

可以在這個原始套接字上呼叫connect函式,不過比較少見。connect函式僅僅設定外地位址,同樣因為原始套接字不存在埠號的概念。就輸出而言,呼叫connect之後我們可以把sendto呼叫改為write或send呼叫,因為目的位址已經指定了。

(2)原始套接字的輸出

原始套接字的輸出遵循以下規則:

(3)原始套接字的輸入

首先要考慮核心將哪些接收到的ip資料報傳遞到原始套接字?這要遵循下面的規則:

當核心有乙個需要傳遞到原始套接字的ip資料報時,它將檢查所有程序上的所有原始套接字,以尋找所有匹配的套接字。每個匹配的套接字將被傳遞送以該ip資料報的乙個副本。核心對每個原始套接字均執行以下3個測試,只有這三個測試均為真,核心才把接收到的資料報傳送給這個套接字。

注意,如果乙個原始套接字是以0值協議引數傳遞的,並且沒有呼叫bind,也未對它呼叫connect,那麼該套接字將接收可由核心傳遞到原始套接字的每個原始資料報的乙個副本。

無論何時往乙個原始ipv4套接字上遞送乙個接收到的資料報,傳遞到該套接字所在程序的都是包括ip首部在內的完整資料報,然後對於ipv6原始套接字,傳遞套接字的只是扣除了ipv6首部和所有擴充套件首部的淨荷(payload)。

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

#include 

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

這個函式中,domain

表示協議簇,

type

表示套接字型別,而

protocol

表示的是處理的協議型別。在

linux

下提供了多種底層套接字。下面分別進行簡單介紹。

通過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);  

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

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);  

這種方式是使用者模式和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); 

網路程式設計原始套接字

socket stream 流式套接字 socket dgram socket raw 原始套接字 ipproto ip ip協議 ipproto icmp internet控制訊息協議,配合原始套接字可以實現ping的功能 ipproto igmp internet 閘道器服務協議,在多播中用到 ...

Linux網路程式設計 原始套接字

原始套接字 sock raw 應用原始套接字,我們可以編寫出由tcp和udp套接字不能夠實現的功能.注意原始套接字只能夠由有 root許可權的人建立.dos.c include include include include include include include include inclu...

UNIX網路程式設計 基礎套接字程式設計

就像 unix網路程式設計 裡面說的,我們可以將tcp的連線看成乙個 系統,那麼我們的套接字位址結構,就是我們的 裡面號碼,和其他能確定我們身份資訊的集合。其中ipv4的,我們已經很熟悉,還有另外的,在ipv6如果系統支援長度字段,那麼sin6 len常值必須定義,在使用這一結構時我們必須分清到底,...