歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> Linux 基於IP queue在內核態修改數據包(一)——發送到內核態數據包的結構

Linux 基於IP queue在內核態修改數據包(一)——發送到內核態數據包的結構

日期:2017/3/1 11:42:16   编辑:關於Linux

這篇博客只是簡單的對內核態發送到用戶空間的數據報文的結構進行簡單的分析。

並且此篇博文還有姊妹篇,綜合來看一共有兩篇博文:

(1)Linux基於IP Queue在內核態修改數據包(一)——發送到內核態數據包的結構

(2)Linux基於IP Queue在內核態修改數據包(二)——修改內核態數據包的內容

首先我們簡單復述一下用戶態接受並傳回數據包的內容——基於libipq庫

llibipq庫由兩個文件組成:libipq.h,libipq.c至於libipq庫具體的內容可以參照上面給出的兩篇博客。那麼接下來我們談一談具體的數據報文結構。

(1)首先我們可以通過IP Queue機制將數據報文發送到用戶空間。從內核態發送數據包到用戶空間的程序如下。

/*
 *main.c
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "libipq.h"

#define ETH_HDRLEN 14



struct ipq_handle *h = NULL;

static void sig_int(int signo)
{
	ipq_destroy_handle(h);
	printf("Exit: %s\n", ipq_errstr());
	exit(0);
}

int main(void)
{
	unsigned char buf[1024];
	/* creat handle*/
	h = ipq_create_handle(0, PF_INET);
	if(h == NULL)
	{
		printf("%s\n", ipq_errstr());
		return 0;
	}
	printf("ipq_creat_handle success!\n");

	/*set mode*/
	unsigned char mode = IPQ_COPY_PACKET;
	int range = sizeof(buf);
	int ret = ipq_set_mode(h, mode, range);
	printf("ipq_set_mode: send bytes =%d, range=%d\n", ret, range);

	/*register signal handler*/
	signal(SIGINT, sig_int);

	/*read packet from kernel*/
	int status;
	struct nlmsghdr *nlh;
	ipq_packet_msg_t *ipq_packet;

	while(1)
	{
		status = ipq_read(h, buf, sizeof(buf),0);
		if(status > sizeof(struct nlmsghdr))
		{
			nlh = (struct nlmsghdr *)buf;
			ipq_packet = ipq_get_packet(buf);
			printf("recv bytes =%d, nlmsg_len=%d, indev=%s, datalen=%d, packet_id=%x\n", status, nlh->nlmsg_len,
			ipq_packet->indev_name,  ipq_packet->data_len, ipq_packet->packet_id);

			//printf("before\n");
			unsigned char payload[1024*1024];
			memset(payload, 0x00, sizeof(payload));
			memcpy(payload + ETH_HDRLEN, ipq_packet->payload, ipq_packet->data_len);

			/*display packet data in hex including 14 bytes of ETH hdr(set by 0x00)*/
			int i;
			for(i = 0; i < ipq_packet->data_len + ETH_HDRLEN; i++)
			{
				if(i%16 == 0)
				printf("00%.2x:  ", i);
				printf("%.2x ", payload[i]);
				if(i % 16 == 15)
				printf("\n");
			}
			printf("\n");

			ret = ipq_set_verdict(h, ipq_packet->packet_id, 1,ipq_packet->data_len,payload + ETH_HDRLEN);
			printf("Accepted!\n");
		}
	}
	return 0;
}


在實驗中,通過一個小小的UDP通信測試實例先簡單的分析一下IP Queue機制發送到用戶態的UDP報文結構。

<1>首先我們通過uclient端向userver端發送一段字符:fasfsafsasfas

效果如圖:

\

<2>通過userver端接受到的數據如下圖所示也為字符串:fasfsafsasfas 並且輸出接受到的字符串長度位 13

\

<3>此時在用戶空間捕捉到數據信息並以16進制的形式輸出,結果如下。

\

分析輸出數據的結果,可知前14個字節為預留的MAC地址長度,其中一個為 源地址一個為目的地址,每個MAC地址所占的長度為7個字節

那麼

45 00

00 29 00 00 40 00 40 11 3c c2 7f 00 00 01 7f 00

00 01 8e f2 1f 40 00 15 65 1d 66 61 73 66 73 61

66 73 61 73 66 61 73

分別表示什麼含義呢?

我們從後面往前分析由於這是UDP報文所以采用的是UDP的傳輸結構。我們從後往前看。

由於在程序輸出的過程中用16進制表示數據,那麼根據16進制可以計算出後面的一段:

66 61 73 66 73 61 66 73 61 73 66 61 73

ASCII碼分別

102 97 115 102 115 97 102 115 97 115 102 97 115

還原到字符串剛好為:fasfsafsasfas 是我們發送到出去的數據。

那麼根據UDP的結構可知:

\

根據報文結構可知:65 1d 表示的校驗和字段,長度為16bit 而接下來的00 15表示的是報文的長度,報文長度 = UDP首部長度+數據長度 = 8 + 13 = 21 用16進制表示為00 15

同樣往上面追溯8e f2表示的源端口1f 40 表示的是目的端口。在通信測試實例中采用的是8000的端口,剛好對應16進制端口號為:1f 40。測試實例采用的本機通信地址進行通信端口號為127.0.0.1可知7f 00 00 01 7f 0000 01分別通信的源地址和目的地址。

對於前面剩余的部分字段:45 0000 29 00 00 40 00 40 11 3c c2 在這裡我們先暫時不做討論,在接下來的部分中我們將討論TCP的報文結構。與UDP相比TCP報文結構相對來說要簡單的多。

接下來我們采用TCP通信來讓IP queue通信小程序輸出測試結果。

首先我們照樣打開一個IP Queue小程序手機內核態傳遞過來的數據信息。通過小程序獲取數據報文。然後用一個TCP通信的客戶端給服務端發送信息。

客戶端發送信息如下所示:發送了一串ABCDEFG的字符串。

\

如圖所示的字符串那麼ip queue發送到用戶態的數據包郵那些呢?

\

此時發現內核態發送到用戶空間的數據包有三個,至於為什麼有三個我這裡就不解釋了,大家只要了解TCP協議通信的原理就知道了。在這裡只有最後一個數據包才是我們測試程序實際發送數據的數據報文。與前面相同我們同樣根據TCP報文結構從後面往前面分析。

可知:41 42 43 44 45 46 47 0a 分別對應ASCII碼的字符為 A B C D E F G \n

並且TCP報文結構如下圖所示:

\

根據TCP數據報文結構可知TCP報文結構好尼瑪復雜。由於選項填充的長度無法確定我們從前往後看,c0 a8 da 81 c0 a8 da 81 分別為源地址和目的地址。那麼接下來的是什麼呢?即使整個TCP報文的框架了。其中d4 e4 1f 40分別為源端口和目的端口。f1 bd 18 46對應圖中32bit的序號,接下來c4 12 f2 ef為確認序號,不管是序號還是確認序號以及接下來的32個bit 8018 04 00 我們都不要去過分關注。接下來為校驗和字段61 84這是值得關注的一個位置。根據結構圖可知填充字段的長度為12個字節。在數據中對應的為01 01 08 0a 00 02 b4 aa 00 02 5f 23.

對於采用TCP協議的數據報文而言填充字段要注意兩個特點:

(1)填充字段的長度可以任意,但有一個長度限定的范圍。

(2)TCP協議填充字段數據內容的修改並不影響數據傳輸。

與UDP不同TCP的報文結構不同,並且 TCP的結構中沒有數據長度這一字段。

那麼在TCP與UDP首部多出來的12個字節 的長度是什麼 呢?

IP首部:

對上述的數據字段進行分析:45 0000 29 00 00 40 00 40 11 3c c2

3c c2:表示16bit IP首部校驗和。

11:表示8bit的協議號,在此處為17表示的是TCP協議。

40:表示生存時間

00 00 40 00:分別表示3bit標志和13bit偏移

45 0000 29:表示16bit表示符號

根據上述的分析可知內核態發送到用戶空間的報文數據,而在對內核態發送到數據報文的數據進行修改時要清楚的知道數據報文的結構,以及清晰的了解到那些數據報文的修改會影響數據報文測傳輸。

Copyright © Linux教程網 All Rights Reserved