分組(也譯為資料包),PF_PACKET
- 在裝置層的分組介面
譯註:PF_PACKET 中的 PF 是 protocol
family(協議族)的縮寫。
#include <sys/socket.h>
#include <features.h> /* 需要裡面的 glibc 版本號 */
#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h> /* 鏈路層(L2)協議 */
#else
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h> /* 鏈路層協議 */
#endif
packet_socket=socket(PF_PACKET,intsocket_type,intprotocol);
分組套介面(也譯為插口或套接字)被用於在裝置層(OSI
的鏈路層) 收發原始(raw
)分組。它允許使用者在使用者空間實現在物理層之上的
協議模組。
對於包含鏈路層報頭的原始分組,socket_type
引數是 SOCK_RAW;
對於去除了鏈路層報頭的加工過的分組,socket_type
引數是
SOCK_DGRAM。鏈路層報頭資訊可在作為一般格式的
sockaddr_ll 中 的中得到。socket 的
protocol 引數指的是 IEEE 802.3
的按網路
層排序的協議號,在標頭檔案中有所有被允許的
協議的列表。當 protocol
被設定為
htons(ETH_P_ALL)時,可以接
收所有的協議。到來的此種類型的分組在傳送到在核心實現的協議
之前要先傳送給分組套介面。
譯註:DGRAM
是資料報的意思,htons
函式名是 hosts to networks of a short
(16位整數的從主機到網路的位元組序變換)的縮寫。
只有有效 uid 是 0 或有 CAP_NET_RAW
能力的程序可以開啟分組
套介面。
傳送到裝置和從裝置傳送來的
SOCK_RAW
分組不改變任何分組資料。
當收到一個 SOCK_RAW 分組時,
地址仍被分析並傳送到一個標準的
sockaddr_ll
地址結構中。當傳送一個
SOCK_RAW 分組時, 使用者供
給的緩衝區應該包含物理層報頭。接著此分組不加修改的放入目的
地址定義的介面的網路驅動程式的佇列中。一些裝置驅動程式總是
增加其他報頭。SOCK_RAW
分組與已被廢棄的 Linux 2.0
的 SOCK_PACKET
分組類似但不相容。
對 SOCK_DGRAM
分組的操作要稍微高一層次。在分組被傳送到使用者
之前物理報頭已被去除。從
SOCK_DGRAM分組套介面送出的分組在被
放入網路驅動程式的佇列之前,基於在
sockaddr_ll 中的目的地址
得到一個適合的物理層報頭。
預設的所有特定協議型別的分組被髮送到分組套介面。為了只從特
定的介面得到分組,使用
bind(2)來指定一個在
sockaddr_ll 結構
中的地址,以此把一個分組套介面繫結到一個介面上。只有地址字
段 sll_protocol 和 sll_ifindex
被繫結用途所使用。
不支援在分組套介面上的
connect(2)
操作。(不能作為客戶端使用)
sockaddr_ll
是裝置無關的物理層地址。
struct sockaddr_ll
{
unsigned short sll_family; /* 總是 AF_PACKET */
unsigned short sll_protocol; /* 物理層的協議 */
int sll_ifindex; /* 介面號 */
unsigned short sll_hatype; /* 報頭型別 */
unsigned char sll_pkttype; /* 分組型別 */
unsigned char sll_halen; /* 地址長度 */
unsigned char sll_addr[8]; /* 物理層地址 */
};
sll_protocol 是在 linux/if_ether.h
標頭檔案中定義的按網路層排
序的標準的以太楨協議型別。sll_ifindex
是介面的索引號(參見
netdevice(2));0
匹配所有的介面(當然只有合法的才用於繫結)。
sll_hatype 是在 linux/if_arp.h 中定義的
ARP 硬體地址型別。 sll_pkttype
包含分組型別。有效的分組型別是:目標地址是本地
主機的分組用的
PACKET_HOST,物理層廣播分組用的
PACKET_BROADCAST
,傳送到一個物理層多路廣播地址的分組用的
PACKET_MULTICAST,
在混雜(promiscuous)模式下的裝置驅動器發向其他主機的分組用的
PACKET_OTHERHOST,本源於本地主機的分組被環回到分組套介面用
的
PACKET_OUTGOING。這些型別只對接收到的分組有意義。sll_addr
和 sll_halen 包括物理層(例如
IEEE
802.3)地址和地址長度。精確
的解釋依賴於裝置。
譯註: (1) 對於乙太網(ethernet)
OSI
模型不完全適用,以太楨定義包
括物理層和鏈路層的基本內容,
所謂的以太楨協議型別標識的是網路
層的協議。IEEE 802
委員會為與 OSI
相一致,把以太楨定義稱為
MAC(medium access control)層,在 MAC
層與網路層之間加入 LLC
(logical link control)層,補充上了 OSI
標準的鏈路層。但在BSD
TCP/IP
中是為了相容官方標準才被實現的。對於
TCP/IP 協議族 OSI
模型也不完全適用,TCP/IP
沒定義鏈路層,只能用
UNIX 的設
備驅動程式去對應鏈路層。無論如何這是既成事實,在本手冊頁中物
理層、鏈路層、裝置層指的都是乙太網的
MAC 層。餘以為不必嚴格
按層次劃分去理解問題,現在這個協議棧是優勝劣汰的結果,不是委
員會討論出來的。 (2)
乙太網地址分為三類,物理地址(最高位為0),多路廣播地址
(最高位為1),廣播地址(全是1)。以
DP8390
為例,它的接收配置
暫存器的 D2 位用來指定
NIC 是否接受廣播楨,D3
位用來指定 NIC
是否對多路廣播楨進行過濾,D4
位用來指定
NIC是否接受所有的物
理地址楨。混雜(Promiscuous)模式就是接收所有物理地址楨。
分組套介面可被用來配置物理層的多路廣播和混雜模式。配置透過呼叫
setsockopt(2)實現,套介面引數是一個分組套介面、層次引數為
SOL_PACKET 、選項引數中的
PACKET_ADD_MEMBERSHIP 用於增加一
個繫結,選項引數中的
PACKET_DROP_MEMBERSHIP
用於刪除一個綁
定。兩個選項都需要作為引數的
packet_mreq 結構:
struct packet_mreq
{
int mr_ifindex; /* 介面索引號 */
unsigned short mr_type; /* 動作 */
unsigned short mr_alen; /* 地址長度 */
unsigned char mr_address[8]; /* 物理層地址 */
};
mr_ifindex
包括介面的介面索引號,mr_ifindex
的狀態是可以改
變的。mr_type
引數指定完成那個動作。PACKET_MR_PROMISC
允許
接收在共享介質上的所有分組,這種接受狀態常被稱為混雜模式;
PACKET_MR_MULTICAST
把套介面繫結到由mr_address
和 mr_alen
指定的物理層多路廣播組上;PACKET_MR_ALLMULTI
設定套介面接
收所有的來到介面的多路廣播分組。
除此之外傳統的 ioctls 如
SIOCSIFFLAGS, SIOCADDMULTI, SIOCDELMULTI
也能用於實現同樣的目的。
SIOCGSTAMP
用來接收最新收到的分組的時間戳。它的引數是
timeval 結構。
除此之外,所有的在
netdevice(7) 和
socket(7)
中定義的標準 的 ioctl
在分組套介面上均有效。
分組套接只對傳送分組到裝置驅動程式時發生的錯誤做錯誤處理,
其他不做錯誤處理。這裡沒有等待解決的錯誤的概念。
在 Linux 2.0
中,得到分組套介面的唯一方法是呼叫
socket(PF_INET, SOCK_PACKET,
protocol)。它仍被支援但變得
沒有價值。兩種方法的主要不同在於
SOCK_PACKET 使用老的 sockaddr_pkt
結構來指定一個介面,沒有提供物理層介面無關性。
(依賴於物理裝置)
struct sockaddr_pkt
{
unsigned short spkt_family;
unsigned char spkt_device[14];
unsigned short spkt_protocol;
};
spkt_family
包括裝置型別,spkt_protocol
是在 中定義的 IEEE 802.3
協議型別,spkt_device
是表示裝置名的 null
終結的字串,例如 eth0。
譯註: "who is nntp"
就是一個以 null
(' ')終結的字串。
這個結構已經被廢棄,不應在新的程式碼中使用。
不建議對要求可移植的程式透過
pcap(3) 使用 PF_PACKET 協議族;
它只覆蓋了 PF_PACKET
特徵的一個子集。
譯註:該函式庫可在
ftp://ftp.ee.lbl.gov/libpcap.tar.Z 得到。
SOCK_DGRAM 分組套介面對 IEEE 802.3
楨不做生成或分析 IEEE 802.2
LLC
報頭的嘗試。當在套介面中指定了
ETH_P_802_3 協議,
告知核心生成 802.3
楨,並填寫了長度欄位;使用者必須提供提供
LLC
報頭來產生符合標準的分組。到來的
802.3 分組不在協議 欄位
DSAP/SSAP
上實現多路複用;而是故意的把
ETH_P_802_2 協議的 LLC
報頭提供給使用者。所以不可能繫結到
ETH_P_802_3; 而可以繫結到
ETH_P_802_2
並自己做多路複用。預設的傳送的是
標準的乙太網 DIX
封裝並填寫協議欄位。
譯註:
長度欄位和協議欄位其實都是以太楨的第四欄位,這個欄位
的值在小於 1518
時表示此以太楨是 IEEE 802.3
楨,在大於1536
時表示此以太楨是 DIX
楨。DIX 中的 D 代表 DEC,I
代表 Intel, X 代表 Xerox。
分組套介面不是輸入或輸出防火牆的系列主題。
- ENETDOWN
- 介面未啟動。
- ENOTCONN
- 未傳遞介面地址。
- ENODEV
- 在介面地址中指定了未知的裝置名或介面索引。
- EMSGSIZE
- 分組比介面的
MTU(最大傳輸單元)大。
- ENOBUFS
- 沒有足夠的記憶體分配給分組。
- EFAULT
- 使用者傳遞了無效的地址。
- EINVAL
- 無效引數。
- ENXIO
- 介面地址包含非法介面索引號。
- EPERM
- 使用者沒有足夠的許可權來執行這個操作。
- EADDRNOTAVAIL
- 傳遞了未知的多路廣播組地址。
- ENOENT
- 未收到分組。
除此之外,底層的驅動程式可能產生其他的錯誤資訊。
PF_PACKET 是 Linux 2.2 的新特徵。Linux
的早期版本只支援
SOCK_PACKET。
glibc 2.1 沒有定義
SOL_PACKET。建議的補救是使用
#ifndef SOL_PACKET
#define SOL_PACKET 263
#endif
在此以後的 glibc
版本中更正了錯誤並且在
libc5 系統上不會發生。
沒有對 IEEE 802.2/803.3 LLC
的處理被認為是缺陷。
套介面過濾器未歸入文件。
本手冊頁是 Andi Kleen
寫的,他得到了 Matthew Wilcox
的幫助。 在 Linux 2.2 中的 PF_PACKET
是 Alexey Kuznetsov 實現的,他
的實現是以 Alan Cox
和其他人的程式碼為基礎的。
ip(7),
socket(7),
socket(2),
raw(7),
pcap(3).
RFC894
-IP
資料報的Ethernet
楨封裝標準。
RFC1700
-IP
資料報的IEEE
802.3楨封裝標準。
標頭檔案linux/if_ether.h
包含物理層協議。
mhss <[email protected]>
2000/10/15
http://cmpp.linuxforum.net
本頁面中文版由中文 man
手冊頁計劃提供。
中文 man 手冊頁計劃:
https://github.com/man-pages-zh/manpages-zh