描述
本文簡單描述了數據鏈路層的socket使用的兩種方法
正文
Linux下有兩種方式接收數據鏈路層的數據包:
(1)原始的方法,即創建一個類型為SOCK_PACKET的socket,該方法很普遍,但是缺乏靈活性;
(2)最新的方法,引入了幀過濾功能和性能上的提升,即創建一個指定協議簇為 PF_PACKET的socket,這需要root權限(類似於創建一個raw socket),並且socket的第三個參數必須指定一個以太網幀類型(Ethernet frame type);
使用第二種方法時,socket的第二個參數可以被設置為SOCK_DGRAM,主要區別是當指定SOCK_DGRAM時,獲取的數據包是去掉了數據鏈路層的頭(link-layer header),當指定SOCK_RAW時,獲取的數據包是一個完整的數據鏈路層數據包; SOCK_PACKET只返返回完整的數據鏈路層數據包,示例,接收完整的數據鏈路層數據包,可以這樣寫:
- fd = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL)); /* older systems */
或者
- fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); /* newer systems */
這樣將會接收到數據鏈路層所有協議幀;
如果我們只需要IPv4幀,可以這樣寫:
- fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)); /* new systems */
或者
- fd = socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP)); /* older systems */
其它數據鏈路層類型定義的常量參數有 ETH_P_ARP 和 ETH_P_IPV6;
指定協議(protocol)為ETH_P_XXX即告訴數據鏈路層我只接收該類型的幀(frame),socket會自動過濾。如果數據鏈路支持混雜模式(promiscuous mode),可以設置socket可選參數PACKET_ADD_MEMBERSHIP選項,使用packet_mreq結構體指定一個以太網接口和混雜模式行為(PACKET_MR_PROMISC)。
接下來詳解 數據鏈路層的頭信息結構體 sockaddr_ll :
點擊(此處)折疊或打開
- #include <sys/socket.h>
- #include <linux/if_packet.h>
- #include <net/ethernet.h> /* the L2 protocols */
- packet_socket = socket(AF_PACKET, int socket_type, int protocol);
數據鏈路層的頭信息通常定義在 sockaddr_ll 的結構體中,protocol是按照網絡字節順序(network byte order),大部分定義在頭文件中,設置協議時,例如 htons(ETH_P_ALL)來接收所有的數據包;
如果要獲取從指定以太網接口卡上的數據包時,在 struct sockaddr_ll中指定網絡接口卡,綁定(bind)數據包到該interface上。只有sll_protocol和 sll_ifindex這兩個地址字段是用來bind的。
- struct sockaddr_ll {
- unsigned short sll_family; /* Always AF_PACKET */
- unsigned short sll_protocol; /* Physical-layer protocol */
- int sll_ifindex; /* Interface number */
- unsigned short sll_hatype; /* ARP hardware type */
- unsigned char sll_pkttype; /* Packet type */
- unsigned char sll_halen; /* Length of address */
- unsigned char sll_addr[8]; /* Physical-layer address */
- };
sll_protocol : 標准以太網協議類型,按網絡字節順序。定義在中。
sll_ifindex: interface索引,0 匹配所有的網絡接口卡;
sll_hatype: ARP 硬件地址類型(hardware address type) 定義在中,常用 ARPHRD_ETHER
sll_pkttype: 包含了packet類型。
PACK_HOST 包地址為本地主機地址。
PACK_BROADCAST 物理層廣播包。
PACK_MULTICAST 發送到物理層多播地址的包。
PACK_OTHERHOST 發往其它在混雜模式下被設備捕獲的主機的包。
PACK_OUTGOING 本地回環包;
sll_addr 和 ssl_halen 包含了物理層地址和其長度;
當發送數據包時,指定 sll_family, sll_addr, sll_halen, sll_ifindex, sll_protocol 就足夠了。其它字段設置為0; sll_hatype和 sll_pkttype是在接收數據包時使用的; 如果要bind, 只需要使用 sll_protocol和 sll_ifindex;