網絡體系結構(2)鏈路層

引言

TCP/IP協議族中最低層的就是鏈路層。鏈路層主要提供三個功能:接收和發送IP數據報;發送ARP請求和接收ARP應答;發送和接收RARP請求和應答。TCP/IP支持多種不同的鏈路層協議,至於具體採用什麼樣的協議則取決於硬件,目前使用的最多的就是以太網,所以也主要針對以太網進行說明。

這裏要注意一點就是ARP和RARP是兩種不同的協議,並且不是協同工作的。不要因爲兩個分別是將mac地址和ip地址進行變換就認爲兩者是相互作用,這兩個協議主要應用在不同的場景下


以太網

以太網採用CSMA/CD媒體介入方式。且以太網的數據封裝格式如下圖:


這裏使用一個小的代碼片來認識以太網的數據幀格式:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ETH_ALEN 6
#define ETH_HLEN 14
#define ETH_ZLEN 60
#define ETH_DATA_LEN 1500
#define ETH_FRAME_LEN 1514

int main(int argc,char **argv)
{
	int ret;
	char  ef[ETH_FRAME_LEN];//創建緩存存放以太網數據幀
	int fd;
	struct ethhdr *pethhdr;
	struct arphdr *parp;
	int i;
	int n;//讀取的字節數
	fd=socket(AF_INET,SOCK_PACKET,htons(0x0003));
	if(fd<0)
	{		
		perror("socket");
		return -1;
	}
	n=read(fd,ef,ETH_FRAME_LEN);
	if(n<0)
	{
		perror("read()");
		return -1;
	}
	pethhdr=(struct ethhdr*)ef;
	printf("目的地址:");
	for(i=0;ih_dest[i]);
	printf("%x\n",pethhdr->h_dest[ETH_ALEN-1]);
	printf("源地址:");
	for(i=0;ih_source[i]);
	printf("%x\n",pethhdr->h_source[ETH_ALEN-1]);
	printf("協議類型:%#x\n",htons(pethhdr->h_proto));
	return 0;
}

該代碼執行結果:

該代碼僅僅抓取了一個數據幀,如果要進一步抓取更多數據幀可以使用循環。這裏不多闡述。這段代碼使用了原始套機字抓取鏈路層數據包。相關的編程理論可以查閱《unix環境高級編程》(卷一)第29章。[本例沒有采用書中的樣例]。注意使用該程序需要在特權模式下執行。

以太網的數據幀中目的地址和源地址各佔是6個字節。表示接受方主機的硬件地址和發送方主機的硬件地址,通過這個例子可以看出硬件地址雖然是標識在網卡上的,但是我們可以通過軟件的方式進行改寫,這也是進行網絡欺騙攻擊的一種方式;類型佔2個字節,如果是0x0800則爲IP數據報,如果是0x0806則爲ARP數據報,如果是0x8035則爲RARP數據報;數據部分根據類型定義的不同而不同,在以太網中該字段爲46—1500字節;最後一個字段爲CRC用於幀後續字節的循環冗餘校驗。

針對數據包的向上傳遞在鏈路層都是使用內核進行處理的。但是,目前很多數據包都是傳遞到應用層對數據包的處理,使用內核進行數據包的層層傳遞似乎有點影響我們的性能。那麼針對這個問題Intel公司在針對自己的X86結構上開發了一套自己的開發套件DPDK該套件利用輪詢技術將數據接收之後直接傳遞到我們的應用層,從而繞過了內核,防止了用戶空間和內核空間進行的數據複製。同時這塊套件中也提供了KNI這套組件,爲的就是讓內核提供部分服務。目前針對DPDK同樣開發出了部分的應用層協議棧。比如ANS


接口信息

針對以太網,系統也同時提供了相關的命令對以太網接口進行配置。ifconfig是比較老的配置命令,該命令目前還在使用中,且命令使用ioctl函數對接口進行操作。



使用ioctl函數有一大缺點,主要就是隻能進行內核和用戶空間的單向通信,也正是這樣的原因,iproute2套件使用了一個ip命令,該命令使用netlink套接字與內核進行通信。旨在取締ipfconfig這個命令。

這裏通過寫一段代碼獲取網絡信息類似於ifconfig -a的信息獲取命令,相關設置編程可以進一步自行擴展。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ETH_ALEN 6
#define ETH_HLEN 14
#define ETH_ZLEN 60
#define ETH_DATA_LEN 1500
#define ETH_FRAME_LEN 1514

int main(int argc,char **argv)
{
	int ret;
	char  ef[ETH_FRAME_LEN];//創建緩存存放以太網數據幀
	int fd;
	struct ethhdr *pethhdr;
	struct arphdr *parp;
	int i;
	int n;//讀取的字節數
	fd=socket(AF_INET,SOCK_PACKET,htons(0x0003));
	if(fd<0)
	{		
		perror("socket");
		return -1;
	}
	n=read(fd,ef,ETH_FRAME_LEN);
	if(n<0)
	{
		perror("read()");
		return -1;
	}
	pethhdr=(struct ethhdr*)ef;
	printf("目的地址:");
	for(i=0;ih_dest[i]);
	printf("%x\n",pethhdr->h_dest[ETH_ALEN-1]);
	printf("源地址:");
	for(i=0;ih_source[i]);
	printf("%x\n",pethhdr->h_source[ETH_ALEN-1]);
	printf("協議類型:%#x\n",htons(pethhdr->h_proto));
	return 0;
	
	
}

該代碼執行結果如下:


該代碼僅僅顯示了接口的部分信息。也就是針對這方面的編程拋初一個引子,後面如果要針對其他信息獲取或者修改可以參照上面的內容進行擴展。針對ifreq結構可以訪問/usr/include/linux/if.h。

此外針對接口還有netstat -i命令。

網卡在對數據接收之後的一系列操作在另外一篇博文中有說明這裏不再重複。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章