亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 24724267 | 回復(fù): 24724267
打印 上一主題 下一主題

Linux內(nèi)核IP Queue機(jī)制的分析(一)——用戶態(tài)接收數(shù)據(jù)包 [復(fù)制鏈接]

論壇徽章:
36
IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2008-12-03 19:31 |只看該作者 |倒序?yàn)g覽

        筆者將會通過包括本文在內(nèi)的三篇文章,對IP Queue機(jī)制從用戶態(tài)的應(yīng)用到內(nèi)核態(tài)的模塊程序設(shè)計(jì)進(jìn)行分析。三篇文章的題目分別是:
Linux內(nèi)核IP Queue機(jī)制的分析(一)——用戶態(tài)接收數(shù)據(jù)包
Linux內(nèi)核IP Queue機(jī)制的分析(二)——用戶態(tài)處理并回傳數(shù)據(jù)包
Linux內(nèi)核IP Queue機(jī)制的分析(三)——內(nèi)核態(tài)ip_queue代碼分析 --待續(xù)
        筆者希望通過這三篇文章,能夠給不熟悉或者對IP Queue使用有問題的朋友一些幫助。文章中分析不妥或錯誤之處,希望各位朋友可以及時指出。
        本文歡迎自由轉(zhuǎn)載,但請標(biāo)明出處,并保證本文的完整性
。
        作者:Godbach
        日期:2008/11/27

        
本文的大綱如下:
一、基礎(chǔ)知識
   1. Netfilter
   2. Netlink機(jī)制
二、IP Queue編程接口
三、一個實(shí)現(xiàn)接收內(nèi)核態(tài)發(fā)送的IP Queue數(shù)據(jù)包的用戶態(tài)例程
   1. libipq.h
   2. libipq.c
  3. ipq_user.c
四、應(yīng)用程序的測試
   1. 測試環(huán)境的建立
   2. 程序的測試
附件

[ 本帖最后由 Godbach 于 2009-2-23 13:46 編輯 ]

論壇徽章:
36
IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
2 [報(bào)告]
發(fā)表于 2008-12-03 19:34 |只看該作者
一、基礎(chǔ)知識
基礎(chǔ)知識部分的很多部分內(nèi)容都重點(diǎn)參考或者直接引用了《如何用IP Queue機(jī)制編寫用戶態(tài)防火墻》,原文的鏈接為:http://www.syue.com/Firewall/HTML/3357.html。在此,向該文的作者表示感謝。

1. Netfilter
Linux內(nèi)核在Netfilter(下文簡稱NF)框架的基礎(chǔ)上提供了IP Queue機(jī)制,使得基于用戶態(tài)(User Mode)的防火墻開發(fā)成為可能。

內(nèi)核中NF對網(wǎng)絡(luò)報(bào)文的處理這里不做詳細(xì)描述。假設(shè)讀者已經(jīng)熟悉NF的工作原理和工作流程。但這里還是要簡單介紹一下NF中各個鉤子(hook)函數(shù)對數(shù)據(jù)包處理的返回值,即該函數(shù)告訴內(nèi)核對該數(shù)據(jù)包的處理意見。所有的返回值如下:

NF_DROP: 丟棄該報(bào)文,釋放所有與該報(bào)文相關(guān)的資源;
NF_ACCEPT: 接受該報(bào)文,并繼續(xù)處理;
NF_STOLEN: 該報(bào)文已經(jīng)被HOOK函數(shù)接管,協(xié)議棧無須繼續(xù)處理;
NF_QUEUE: 將該報(bào)文傳遞到用戶態(tài)去做進(jìn)一步的處理;
NF_REPEAT: 再次調(diào)用本HOOK函數(shù)。

當(dāng)HOOK處理函數(shù)返回值為NF_QUEUE時,內(nèi)核協(xié)議棧將通過IP Queue機(jī)制把當(dāng)前報(bào)文傳遞到用戶態(tài),由用戶態(tài)的應(yīng)用程序進(jìn)行處理。這樣,只要能夠在相應(yīng)的HOOK點(diǎn)上返回NF_QUEUE值,就可以將符合要求的報(bào)文傳送到用戶態(tài)去做進(jìn)一步對報(bào)文行處理。隨后,用戶態(tài)程序會將處理后的報(bào)文以及對報(bào)文的處理意見(ACCEPT,DROP等)傳遞給內(nèi)核協(xié)議棧。內(nèi)核協(xié)議棧就會按照用戶態(tài)對報(bào)文的處理意見將報(bào)文做接受、丟棄等處理。整個處理的過程就相當(dāng)于一個用戶態(tài)的防火墻,所有數(shù)據(jù)包的實(shí)質(zhì)性處理都放在用戶態(tài)進(jìn)行。這樣,即使是不具有深入內(nèi)核知識的開發(fā)人員,也可以開發(fā)出適應(yīng)一定應(yīng)用場合的用戶態(tài)防火墻。

2. Netlink機(jī)制
        前面講到,所謂IP Queue機(jī)制,只是當(dāng)NF上Hook函數(shù)對數(shù)據(jù)包處理的返回值為NF_QUEUE時,協(xié)議棧會將數(shù)據(jù)包交給內(nèi)核中的ip_queue模塊。而ip_queue又是怎么將數(shù)據(jù)包傳遞給用戶態(tài)的呢?這里就涉及到在內(nèi)核開發(fā)中常見的問題,如何將內(nèi)核態(tài)的數(shù)據(jù)傳遞到用戶態(tài),實(shí)現(xiàn)內(nèi)核空間和用戶空間的通信。具體實(shí)現(xiàn)的方法有多種。本人的博客中也總結(jié)了若干種,并配有測試的例程:http://blog.chinaunix.net/u/33048/article.html.對于IP Queue,則是使用Netlink機(jī)制實(shí)現(xiàn)內(nèi)核態(tài)和用戶態(tài)的交互。

NetLink是Linux系統(tǒng)特有的、基于socket編程接口的通信機(jī)制。它是一個面向數(shù)據(jù)報(bào)文的服務(wù),并提供NETLINK_ROUTE(更新和修改路由操作)、NETLINK_FIREWALL (接受和發(fā)送IPv4協(xié)議NF傳輸?shù)陌趦?nèi)核的ip_queue模塊),NETLINK_ARPD(用戶態(tài)ARP表操作)等多種通信協(xié)議。在創(chuàng)建基于IP Queue的NetLink Socket時,將采用如下系統(tǒng)調(diào)用:

fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);

  這里,PF_NETLINK指明要創(chuàng)建NetLink Socket;SOCK_RAW指明采用原始套接字,也可以采用SOCK_DGRAM,因?yàn)镹etLink機(jī)制的實(shí)現(xiàn)并不區(qū)分SOCK_RAW和SOCK_DGRAM;參數(shù)NETLINK_FIREWALL則指明通信協(xié)議采用IP Queue。

既然IP Queue是基于NetLink的,其消息格式自然也遵從NetLink的規(guī)范。NetLink消息由兩部分組成:消息頭(struct nlmsghdr)和數(shù)據(jù)負(fù)載(data payload)。
消息頭的定義如下(include/linux/netlink.h):
struct nlmsghdr
{
__u32 nlmsg_len; /*消息長度*/
__u16 nlmsg_type;/*消息類型*/
__u16 nlmsg_flags;/*額外的標(biāo)志*/
__u32 nlmsg_seq; /*序列號*/
__u32 nlmsg_pid; /*進(jìn)程號*/
}; 

所有的IP Queue消息都將包含一個struct nlmsghdr消息頭,具體的IP Queue消息則包含在NetLink消息的數(shù)據(jù)負(fù)載中。有關(guān)NetLink消息格式的詳情可以參見手冊頁Netlink(7)。

二、IP Queue編程接口

使用IP Queue機(jī)制的程序必須包含如下的頭文件:

#include<linux/netfilter_ipv4/ip_queue.h>
在這個頭文件中定義了所有IP Queue消息的格式。以下談到關(guān)于IP Queue的若干個數(shù)據(jù)結(jié)構(gòu)以及宏定義都包含在該文頭件。

IP Queue消息可以分為兩大類:由內(nèi)核協(xié)議棧發(fā)給用戶態(tài)進(jìn)程的IP Queue消息和由用戶態(tài)進(jìn)程發(fā)給內(nèi)核的IP Queue消息。

由內(nèi)核協(xié)議棧發(fā)給用戶態(tài)進(jìn)程的IP Queue消息(nlmsghdr.nlmsg_type = IPQM_PACKET),其數(shù)據(jù)結(jié)構(gòu)為ipq_packet_msg_t,定義如下:
/* Messages sent from kernel */
typedef struct ipq_packet_msg {
        unsigned long packet_id;        /* 報(bào)文的ID號 */
        unsigned long mark;                /* NF標(biāo)記值 */
        long timestamp_sec;                /*報(bào)文到達(dá)時間(秒)  */
        long timestamp_usec;                /* 報(bào)文到達(dá)時間(毫秒) */
        unsigned int hook;                /* 報(bào)文所處的NF hook點(diǎn) */
        char indev_name[IFNAMSIZ];        /* 流入網(wǎng)口名稱 */
        char outdev_name[IFNAMSIZ];        /* 流出網(wǎng)口名稱 */
        unsigned short hw_protocol;        /*硬件協(xié)議(網(wǎng)絡(luò)順序)*/
        unsigned short hw_type;                /* 硬件類型 */
        unsigned char hw_addrlen;        /*硬件地址長度*/
        unsigned char hw_addr[8];        /* 硬件地址 */
        size_t data_len;                /* 報(bào)文數(shù)據(jù)的長度 */
        unsigned char payload[0];        /* 報(bào)文本身的數(shù)據(jù),可選 */
} ipq_packet_msg_t;


這個數(shù)據(jù)結(jié)構(gòu)也被稱為“報(bào)文的元數(shù)據(jù)”。個人理解,所謂“報(bào)文的元數(shù)據(jù)”應(yīng)該是關(guān)于報(bào)文的摘要信息,而不包括報(bào)文數(shù)據(jù)的本身。從上面的這個結(jié)構(gòu)體中也可以看出來。內(nèi)核除可以單獨(dú)向用戶進(jìn)程傳遞“報(bào)文的元數(shù)據(jù)”以外,也可以同時傳遞報(bào)文本身。此時,報(bào)文本身的數(shù)據(jù)將存儲在ipq_packet_msg_t數(shù)據(jù)成員payload開始的地方。
至于內(nèi)核在什么情況下向用戶傳遞報(bào)文的元數(shù)據(jù),什么情況下向用戶傳遞報(bào)文的元數(shù)據(jù)加報(bào)文本身的數(shù)據(jù),那就要看用戶所請求的模式了。下面將講述用戶態(tài)發(fā)到內(nèi)核態(tài)消息的格式,也正好解答了我們這里提出的問題。

用戶態(tài)發(fā)到內(nèi)核態(tài)的消息,其數(shù)據(jù)結(jié)構(gòu)如下所示:
typedef struct ipq_peer_msg {
        union {
                ipq_verdict_msg_t verdict;
                ipq_mode_msg_t mode;
        } msg;
} ipq_peer_msg_t;

通過該數(shù)據(jù)結(jié)構(gòu)可知,這類消息又分為“模式設(shè)置消息(nlmsghdr.nlmsg_type = IPQM_MODE)”和“斷言消息(nlmsghdr.nlmsg_type = IPQM_VERDICT)”兩個子類。

“模式設(shè)置消息”的數(shù)據(jù)結(jié)構(gòu)定義如下:
 typedef struct ipq_mode_msg {
  unsigned char value;/* 請求的模式 */
  size_t range;/* 請求拷貝的報(bào)文長度*/
 } ipq_mode_msg_t;


這里,請求模式value的值可以是IPQ_COPY_NONE、IPQ_COPY_META和IPQ_COPY_PACKET,具體解釋如下:
(1)請求模式value為IPQ_COPY_NONE時,報(bào)文將被丟棄;
(2)請求模式value為IPQ_COPY_META時,內(nèi)核將在其后的報(bào)文傳遞中只傳遞“報(bào)文的元數(shù)據(jù)”。
以上兩種情形傳遞range的值將被內(nèi)核忽略。內(nèi)核中會將該值置為0。
(3)請求模式value為IPQ_COPY_PACKET時,內(nèi)核將同時傳遞“報(bào)文的元數(shù)據(jù)”和報(bào)文本身,報(bào)文本身的傳遞長度由ipq_mode_msg_t的另一個數(shù)據(jù)成員range指定。 range的最大值不能超過IP報(bào)文的最大長度,也就是0xFFFF。否則,會被自動置為0xFFFF。如果請求的長度大于報(bào)文自身的長度,將會按照報(bào)文自身長度進(jìn)行傳遞。

另一子類即“斷言消息”,其數(shù)據(jù)類型定義如下:
  typedef struct ipq_verdict_msg {
unsigned int value;
unsigned long id;
size_t data_len;
unsigned char payload[0];
} ipq_verdict_msg_t;

其中,value是用戶態(tài)程序回傳給內(nèi)核的對當(dāng)前報(bào)文的處理意見,可以是NF_ACCEPT或NF_DROP等值。id則是用以區(qū)分報(bào)文的標(biāo)識號,即內(nèi)核傳來的ipq_packet_msg_t結(jié)構(gòu)中的packet_id。當(dāng)用戶態(tài)程序修改了當(dāng)前報(bào)文以后,需要將報(bào)文重新傳遞回內(nèi)核,此時,新的報(bào)文內(nèi)容必須存儲在payload的開始處,并由data_len指明新報(bào)文的長度。

從上述內(nèi)容可以看出,在整個IP Queue的報(bào)文傳遞過程中,用戶態(tài)程序和內(nèi)核協(xié)議棧之間的互動順序是:
(1)用戶態(tài)程序利用“模式設(shè)置消息”告訴內(nèi)核協(xié)議棧所請求的報(bào)文傳遞模式;
(2)根據(jù)這個模式,內(nèi)核組織好等待傳遞的消息,通過NetLink Socket發(fā)給用戶態(tài)程序;
(3)用戶態(tài)程序?qū)邮盏降臄?shù)據(jù)包進(jìn)行處理,得出該報(bào)文的處理意見(可能同時修改當(dāng)前報(bào)文),并回傳給內(nèi)核。

[ 本帖最后由 Godbach 于 2008-12-3 19:54 編輯 ]

論壇徽章:
36
IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
3 [報(bào)告]
發(fā)表于 2008-12-03 19:39 |只看該作者
三、一個實(shí)現(xiàn)接收內(nèi)核態(tài)發(fā)送的IP Queue數(shù)據(jù)包的用戶態(tài)例程

        由于IP Queue是使用Netlink機(jī)制進(jìn)行內(nèi)核態(tài)和用戶態(tài)通信的。因此,用戶態(tài)要接收內(nèi)核態(tài)發(fā)送的IP Queue數(shù)據(jù)包,就需要設(shè)計(jì)相應(yīng)的Netlink程序,也就是設(shè)計(jì)相應(yīng)的基于Netlink的socket程序即可。這里,我不會詳細(xì)介紹如何使用Netlink機(jī)制實(shí)現(xiàn)用戶態(tài)和內(nèi)核態(tài)進(jìn)行通信。我假設(shè)閱讀本文的朋友,已經(jīng)熟悉了Netlink的使用。如果對Netlink的使用還不是很熟悉,那么可以參考獨(dú)孤九賤大俠的文章——《Linux 用戶態(tài)與內(nèi)核態(tài)的交互——netlink 篇》,其鏈接為:
http://linux.chinaunix.net/bbs/thread-822500-1-1.html.

這篇文章提供了一個使用netlink的完整的例程,包括內(nèi)核態(tài)和用戶態(tài)。講的非常清楚,我看完這篇文章,又跑了一下上面提供的例程,基本上熟悉了Netlink的使用方法。
當(dāng)然,如果讀者不想花時間再去了解netlink的話,也可以通過這篇文章熟悉Netlink的使用。因?yàn)槲疫@里提供的是完整的用戶態(tài)例程,我會將源碼完全提供出來,對于急于通過執(zhí)行程序觀察結(jié)果來學(xué)習(xí)Netlink和IP Queue的朋友,也可以通過隨后提供的方法編譯并執(zhí)行程序。

以下講述用戶態(tài)例程接收IP Queue數(shù)據(jù)包的程序設(shè)計(jì)。
其實(shí),由于Netlink程序也是使用socket的方式進(jìn)行通信。那么接收IP Queue報(bào)文的方式應(yīng)該遵循socket的標(biāo)準(zhǔn)流程,具體流程如下:
(1)調(diào)用socket()創(chuàng)建一個地址類型為PF_NETLINK(AF_NETLINK)的套接字。該套接字使用SOCK_RAW方式傳輸數(shù)據(jù),協(xié)議類型為NETLINK_FIREWALL,即使用IP Queue;
(2)調(diào)用bind()將本地地址(Netlink通信雙方使用該協(xié)議特有的地址格式,見下面struct sockaddr_nl)綁定到已建立的套接字上;
         struct sockaddr_nl {
             sa_family_t     nl_family;  /* AF_NETLINK */
             unsigned short  nl_pad;     /* Zero. */
             pid_t           nl_pid;     /* Process ID. */
             __u32           nl_groups;  /* Multicast groups mask. */
         };

(3)調(diào)用sendto()發(fā)送相關(guān)的配置信息,告訴內(nèi)核應(yīng)用程序準(zhǔn)備接受的是數(shù)據(jù)包的元數(shù)據(jù),還是同時包括數(shù)據(jù)包本身;
(4)調(diào)用recvfrom()接受內(nèi)核態(tài)發(fā)送來的IP Queue報(bào)文;
(5)調(diào)用close()關(guān)閉套接字,結(jié)束通信。

看了以上流程,我相信很多熟悉socket編程的朋友已經(jīng)可以寫出接收IP Queue報(bào)文的用戶態(tài)程序了。
本文中的示例代碼的實(shí)現(xiàn)整體也是依照上面的步驟。但在細(xì)節(jié)的實(shí)現(xiàn)上,參考了iptables源碼給給出的libipq庫的實(shí)現(xiàn)代碼。libipq庫是iptables中封裝的實(shí)現(xiàn)用戶態(tài)接收和發(fā)送IP Queue報(bào)文操作的,也就相當(dāng)于對上面總結(jié)的IP Queue報(bào)文接受流程進(jìn)行封裝。整個libipq庫分別由libipq.c和libipq.h兩個源文件。我這里將兩個源文件移植(基于iptables-1.3.5版本)到示例代碼中并裁剪,并編寫了測試程序ip_user.c。因此,整個實(shí)現(xiàn)代碼包含三個源文件:ip_user.c、libipq.c和libipq.h。
以下將對三個源文件進(jìn)行分析。

1. libipq.h
        該頭文件定義了一個關(guān)鍵的數(shù)據(jù)結(jié)構(gòu),并提供了所有進(jìn)行Netlink通信的API.
        數(shù)據(jù)結(jié)構(gòu)的定義如下:
struct ipq_handle
{
        int fd;
        struct sockaddr_nl local;
        struct sockaddr_nl peer;
};

其中,fd是socket通信的描述符,local和peer分別是Netlink通信雙方的地址。       
除了定義數(shù)據(jù)結(jié)構(gòu),剩下的主要就是提供給用戶調(diào)用的API,函數(shù)列表如下:
struct ipq_handle *ipq_create_handle(u_int32_t flags, u_int32_t protocol);

int ipq_destroy_handle(struct ipq_handle *h);

ssize_t ipq_read(const struct ipq_handle *h, unsigned char *buf, size_t len);

int ipq_set_mode(const struct ipq_handle *h, u_int8_t mode, size_t len);

ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf);

int ipq_message_type(const unsigned char *buf);

int ipq_get_msgerr(const unsigned char *buf);

int ipq_set_verdict(const struct ipq_handle *h,
                    ipq_id_t id,
                    unsigned int verdict,
                    size_t data_len,
                    unsigned char *buf);

int ipq_ctl(const struct ipq_handle *h, int request, ...);

char *ipq_errstr(void);

我將在下面libipq.c的講解中對若干我們將要用到的一些函數(shù)進(jìn)行分析。
2. libipq.c
該源文件實(shí)現(xiàn)了libipq.h中定義的所有函數(shù),并定義了一些出錯信息。
(1)ipq_create_handle()函數(shù)申請了一個struct ipq_handle *h結(jié)構(gòu)體,用來存儲隨后創(chuàng)建的IPv4 socket通信的fd,以及通信雙方的地址。本函數(shù)完成了通信雙方地址的初始化,并將本地地址綁定到已生成的fd上。
ipq_create_handle()函數(shù)的源碼如下:
struct ipq_handle *ipq_create_handle()
{
        int status;
        struct ipq_handle *h;

        h = (struct ipq_handle *)malloc(sizeof(struct ipq_handle));
        if (h == NULL) {
                ipq_errno = IPQ_ERR_HANDLE;
                return NULL;
        }
       
        memset(h, 0, sizeof(struct ipq_handle));
    if (protocol == PF_INET)
            h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);
    else {
                ipq_errno = IPQ_ERR_PROTOCOL;
                free(h);
                return NULL;
    }
        
        if (h->fd == -1) {
                ipq_errno = IPQ_ERR_SOCKET;
                close(h->fd);
                free(h);
                return NULL;
        }
        memset(&h->local, 0, sizeof(struct sockaddr_nl));
        h->local.nl_family = AF_NETLINK;
        /*傳遞本地的pid*/
        h->local.nl_pid = getpid();
        h->local.nl_groups = 0;
        status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local));
        if (status == -1) {
                ipq_errno = IPQ_ERR_BIND;
                close(h->fd);
                free(h);
                return NULL;
        }
        memset(&h->peer, 0, sizeof(struct sockaddr_nl));
        h->peer.nl_family = AF_NETLINK;
        /*代表通信的另一方為內(nèi)核*/
        h->peer.nl_pid = 0;
        h->peer.nl_groups = 0;
        return h;
}

ipq_destroy_handle()函數(shù)關(guān)閉由ipq_create_handle()建立起來的fd,并釋放申請的內(nèi)存。源碼如下:
int ipq_destroy_handle(struct ipq_handle *h)
{
        if (h) {
                close(h->fd);
                free(h);
        }
        return 0;
}

(2)向內(nèi)核發(fā)送模式請求的函數(shù)
int ipq_set_mode(const struct ipq_handle *h,
                 u_int8_t mode, size_t range)
{
        /*構(gòu)造一個向內(nèi)核發(fā)送報(bào)文的結(jié)構(gòu)體*/
        struct {
                struct nlmsghdr nlh;
                ipq_peer_msg_t pm;
        } req;

        memset(&req, 0, sizeof(req));
        req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req));
        req.nlh.nlmsg_flags = NLM_F_REQUEST;
        req.nlh.nlmsg_type = IPQM_MODE;
        req.nlh.nlmsg_pid = h->local.nl_pid;
        /*告訴協(xié)議棧所請求的報(bào)文傳遞模式*/
        req.pm.msg.mode.value = mode;
        /*請求內(nèi)核返回報(bào)文的長度*/
        req.pm.msg.mode.range = range;
        return ipq_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len);
}

在構(gòu)造完向內(nèi)核發(fā)送的結(jié)構(gòu)體req并設(shè)置相關(guān)內(nèi)容之后,調(diào)用ipq_netlink_sendto函數(shù)發(fā)送用戶態(tài)的請求數(shù)據(jù),該函數(shù)代碼如下:
static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
                                  const void *msg, size_t len)
{
        int status = sendto(h->fd, msg, len, 0,
                            (struct sockaddr *)&h->peer, sizeof(h->peer));
        if (status < 0)
                ipq_errno = IPQ_ERR_SEND;
        return status;
}

ipq_netlink_sendto函數(shù)直接調(diào)用了sendto系統(tǒng)調(diào)用發(fā)送用戶態(tài)的數(shù)據(jù),返回的是發(fā)送出去的數(shù)據(jù)長度。當(dāng)sendto調(diào)用失敗時,對全局變量ipq_errno 賦值IPQ_ERR_SEND。這樣方便以后用專門返回出錯信息的函數(shù)引用。

(3)用戶態(tài)發(fā)送了請求數(shù)據(jù)包之后,就處于等待接收內(nèi)核返回?cái)?shù)據(jù)包的狀態(tài)。一旦內(nèi)核NF得到包處理函數(shù)返回NF_QUEUE時,該包就會被ip_queue模塊發(fā)送到用戶態(tài)。用戶態(tài)接收IP Queue數(shù)據(jù)包的函數(shù)為:
ssize_t ipq_read(const struct ipq_handle *h, unsigned char *buf, size_t len)
該函數(shù)的代碼如下。其中buf存儲來自內(nèi)核態(tài)的數(shù)據(jù)包,len為buf的長度。
ssize_t ipq_read(const struct ipq_handle *h,
                 unsigned char *buf, size_t len)
{
        return ipq_netlink_recvfrom(h, buf, len);
}
該函數(shù)直接調(diào)用ipq_netlink_recvfrom()函數(shù),其源碼為:
static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
                                    unsigned char *buf, size_t len)
{
        unsigned int addrlen;
        int status;
        struct nlmsghdr *nlh;
        /*buf長度的校驗(yàn),不能小于Netlink Message的頭部長度*/
        if (len < sizeof(struct nlmsgerr)) {
                ipq_errno = IPQ_ERR_RECVBUF;
                return -1;
        }
        addrlen = sizeof(h->peer);

        status = recvfrom(h->fd, buf, len, 0,
                              (struct sockaddr *)&h->peer, &addrlen);
        if (status < 0) {
                ipq_errno = IPQ_ERR_RECV;
                return status;
        }
        /*判斷接收到的發(fā)送方的地址長度是否正確*/
        if (addrlen != sizeof(h->peer)) {
                ipq_errno = IPQ_ERR_RECV;
                return -1;
        }
        /*內(nèi)核態(tài)向用戶態(tài)發(fā)送數(shù)據(jù)報(bào)文時,其pid=0*/
        if (h->peer.nl_pid != 0) {
                ipq_errno = IPQ_ERR_RECV;
                return -1;
        }
        if (status == 0) {
                ipq_errno = IPQ_ERR_NLEOF;
                return -1;
        }
        nlh = (struct nlmsghdr *)buf;
        /*判斷是否發(fā)生數(shù)據(jù)報(bào)文被截?cái)嗟那闆r*/
        if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) {
                ipq_errno = IPQ_ERR_RTRUNC;
                return -1;
        }
        return status;
}

該函數(shù)返回讀取到報(bào)文的實(shí)際長度。
至此,我們已經(jīng)可以通過上面幾個函數(shù)實(shí)現(xiàn)從內(nèi)核態(tài)接收到既定模式的IP Queue報(bào)文。

(4)輸出出錯信息
char *ipq_errstr(void)
{
        return ipq_strerror(ipq_errno);
}
static char *ipq_strerror(int errcode)
{
        if (errcode < 0 || errcode > IPQ_MAXERR)
                errcode = IPQ_ERR_IMPL;
        return ipq_errmap[errcode].message;
}

根據(jù)函數(shù)執(zhí)行過程中記錄的出錯信息,打印對相關(guān)出錯的具體提示。

[ 本帖最后由 Godbach 于 2008-12-3 19:54 編輯 ]

論壇徽章:
36
IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
4 [報(bào)告]
發(fā)表于 2008-12-03 19:41 |只看該作者
3. ipq_user.c
這個函數(shù)就是具體的測試函數(shù)。功能比較簡單,通過調(diào)用libipq.c中提供的API實(shí)現(xiàn)獲取IP Queue數(shù)據(jù)包的簡單信息,包括數(shù)據(jù)包在本地機(jī)上的入口以及報(bào)文的長度等,
整個源碼如下:
/*
* ipq_usr.c
*
* Testing program for receiving IP Queue packets from kernel 2.6.18.3
*
* Dec 1, 2008
* Godbach created.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "libipq.h"

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));
                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);
                }
        }
        return 0;
}

四、應(yīng)用程序的測試
1. 測試環(huán)境的建立
(1)內(nèi)核態(tài):要求已編譯的內(nèi)核支持Netlink機(jī)制, 并進(jìn)入內(nèi)核源碼目錄net/ipv4/netfilter下,檢查是否生成ip_queue.ko。如果有相應(yīng)的文件,則確保該模塊是否加載,沒有加載的話,modprobe ip_queue進(jìn)行加載
(2)用戶態(tài):要求有已經(jīng)安裝iptables,并加上一條如下規(guī)則:
iptables -I INPUT -p icmp -j QUEUE
這里我們在INPUT鏈上開始處添加了一條對所有ICMP報(bào)文進(jìn)行IP Queue的規(guī)則。通過添加不同的iptables規(guī)則,可以對不同的報(bào)文進(jìn)行IP Queue。

如果系統(tǒng)上沒有安裝iptables的話,那么可以用一個簡單的內(nèi)核模塊來實(shí)現(xiàn)其功能。即在NF對應(yīng)的Hook點(diǎn)上注冊一個鉤子函數(shù),對于某種類型的數(shù)據(jù)包直接return NF_QUEUE即可。附件的源碼中我提供了一個模塊程序,在NF的PRE_ROUTING出注冊了一個對所有ICMP報(bào)文return NF_QUEUE的模塊。沒有iptables的朋友可以使用這個小模塊程序替代。

注意:我這里所使用的內(nèi)核為2.6.18.3。其他內(nèi)核版本沒有進(jìn)行測試。不過,個人覺得如果用戶態(tài)的應(yīng)用程序應(yīng)該在2.6上沒有問題,只是提供的內(nèi)核模塊程序不能保證。
2. 程序的測試
搭建好上面提示的環(huán)境之后,可以對應(yīng)用程序的源碼進(jìn)行編譯:
gcc libipq.c ipq_user.c -o ipq_user

執(zhí)行ipq_user:
[root@localhost ipq_user]# ./ipq_user
ipq_creat_handle success!
ipq_set_mode: send bytes =44, range=1024

隨后,程序處于等待接受內(nèi)核數(shù)據(jù)包的狀態(tài)。我們從另外一臺主機(jī)發(fā)送ping包到本地主機(jī),然后看到終端的輸出為:
[root@localhost ipq_user]# ./ipq_user
ipq_creat_handle success!
ipq_set_mode: send bytes =44, range=1024
recv bytes =148, nlmsg_len=148, indev=eth0, datalen=60, packet_id=c2f9b500
recv bytes =148, nlmsg_len=148, indev=eth0, datalen=60, packet_id=cb0c8c00
recv bytes =148, nlmsg_len=148, indev=eth0, datalen=60, packet_id=c72aa920
recv bytes =148, nlmsg_len=148, indev=eth0, datalen=60, packet_id=c2f9b3c0
Exit: No error

從以上信息中可以看出:
(1)用戶態(tài)發(fā)送的模式設(shè)置信息的包長度為44 bytes;
(2)接收到內(nèi)核態(tài)發(fā)送的包長度為148bytes, 這和從收到的IP Queue包中保存的長度nlmsg_len一致。
(3)ping包進(jìn)入本地主機(jī)的eth網(wǎng)口,報(bào)文的長度為60 bytes。這個實(shí)際的ping包的長度一致。

好了,到現(xiàn)在為止,我們已經(jīng)成功的通過程序接收到內(nèi)核態(tài)發(fā)送的IP Queue數(shù)據(jù)包。我將在下一篇文章中講解用戶態(tài)對接收到報(bào)文的簡單處理以及發(fā)送給內(nèi)核的整個過程。

附件
        附件中提供了此次測試進(jìn)行的應(yīng)用程序的源碼,和一個簡單的對icmp報(bào)文進(jìn)行IP Queue的內(nèi)核模塊程序。

[ 本帖最后由 Godbach 于 2008-12-3 19:45 編輯 ]

ipq_test_src.tar.gz

7.97 KB, 下載次數(shù): 216

論壇徽章:
0
5 [報(bào)告]
發(fā)表于 2008-12-03 20:54 |只看該作者
Godbach 兄網(wǎng)絡(luò)這塊學(xué)得好熟啊. :wink:

論壇徽章:
36
IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
6 [報(bào)告]
發(fā)表于 2008-12-03 21:20 |只看該作者
原帖由 scutan 于 2008-12-3 20:54 發(fā)表
Godbach 兄網(wǎng)絡(luò)這塊學(xué)得好熟啊. :wink:


scutan兄過獎了。 這部分只是用戶態(tài)的程序,看一下API就可以寫出來了。我這里只是系統(tǒng)的整理一下。方便別人參考,也方便自己以后查找。

論壇徽章:
0
7 [報(bào)告]
發(fā)表于 2008-12-03 22:57 |只看該作者
數(shù)據(jù)發(fā)送到用戶態(tài)進(jìn)行處理,那效率豈不很低?
為什么不直接在內(nèi)核做呢?難道就是為了好調(diào)試?

論壇徽章:
36
IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
8 [報(bào)告]
發(fā)表于 2008-12-03 22:58 |只看該作者
原帖由 emmoblin 于 2008-12-3 22:57 發(fā)表
數(shù)據(jù)發(fā)送到用戶態(tài)進(jìn)行處理,那效率豈不很低?
為什么不直接在內(nèi)核做呢?難道就是為了好調(diào)試?


呵呵,LS的這個問題也相當(dāng)于在問為什么要用IP Queue機(jī)制。

[ 本帖最后由 Godbach 于 2008-12-3 23:00 編輯 ]

論壇徽章:
36
IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
9 [報(bào)告]
發(fā)表于 2008-12-03 23:07 |只看該作者
而據(jù)我了解,在做入侵防御系統(tǒng)的時候,有些情況下就需要將數(shù)據(jù)包丟到應(yīng)用層處理。應(yīng)用數(shù)據(jù)如果都放在內(nèi)核中處理并不一定妥當(dāng)啊。而在應(yīng)用層,可以通過很多高效的方式處理數(shù)據(jù)的。

[ 本帖最后由 Godbach 于 2008-12-3 23:08 編輯 ]

論壇徽章:
0
10 [報(bào)告]
發(fā)表于 2008-12-07 02:23 |只看該作者

回復(fù) #7 emmoblin 的帖子

在內(nèi)核態(tài)對數(shù)據(jù)包進(jìn)行處理有很多限制的,很基本的一點(diǎn),譬如說C的標(biāo)準(zhǔn)庫就不能使用,而在用戶態(tài),可用的庫以及其他開發(fā)資源可就豐富多彩得多了。
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP