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

  免費注冊 查看新帖 |

Chinaunix

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

Linux內(nèi)核IP Queue機制的分析(三)——ip_queue內(nèi)核模塊的分析 [復制鏈接]

論壇徽章:
36
IT運維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2010-01-04 21:54 |只看該作者 |倒序瀏覽
本文分析ip_queue的內(nèi)核態(tài)源碼。文中如有任何疏漏和差錯,歡迎各位朋友指正。

        本文歡迎自由轉(zhuǎn)載,但請標明出處,并保證本文的完整性。
        作者:Godbach
        Blog:http://Godbach.cublog.cn
        日期:2010/01/04
       
本系列的前兩篇文章如下:
1. Linux內(nèi)核IP Queue機制的分析(一)——用戶態(tài)接收數(shù)據(jù)包
        http://blog.chinaunix.net/u/33048/showart_1678213.html
2. Linux內(nèi)核IP Queue機制的分析(二)——用戶態(tài)處理并回傳數(shù)據(jù)包
http://blog.chinaunix.net/u/33048/showart_1839753.html
本文大綱如下:
一、IP Queue的生效
二、網(wǎng)絡層中IP報文進入IP Queue的流程
三、ip_queue代碼分析
(一)數(shù)據(jù)結構的定義
(二)ip_queue模塊的加載和卸載
(三)ip_queue報文入隊處理函數(shù)的注冊
(四)入隊函數(shù)ipq_enqueue_packet —— 發(fā)送數(shù)據(jù)包到用戶空間
(五)接收和處理用戶空間的配置—— 接收用戶空間的數(shù)據(jù)包
(六)數(shù)據(jù)包的最終處理

[ 本帖最后由 Godbach 于 2010-1-5 15:08 編輯 ]

評分

參與人數(shù) 2可用積分 +30 收起 理由
T-Bagwell + 15 我很贊同
dreamice + 15 原創(chuàng)內(nèi)容

查看全部評分

論壇徽章:
36
IT運維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
2 [報告]
發(fā)表于 2010-01-04 21:56 |只看該作者
一、IP Queue的生效
數(shù)據(jù)包能夠進入ip_queue模塊,需要兩個動作:
(1)模塊的加載:modprobe ip_queue
(2)NF上對數(shù)據(jù)包執(zhí)行NF_QUEUE的動作,這個可以通過用戶態(tài)配置一條iptables規(guī)則實現(xiàn):
        iptables -A INPUT -p tcp --dport 21 -j QUEUE
        這里假設對發(fā)往本機的TCP報文端口為21的進行QUEUE。
有了以上兩個步驟, 所有匹配到(2)中的報文將會調(diào)用IP Queue模塊的相關函數(shù)。

二、網(wǎng)絡層中IP報文進入IP Queue的流程
        本文中分析的代碼的內(nèi)核版本為2.6.18.3.
這里我們以本地接收報文為例,如果是轉(zhuǎn)發(fā)的報文,可比照著分析即可。
IP層接收報文的函數(shù)為:ip_rcv()(ip_input.c)。該函數(shù)對報文進行一些初步的檢查后,就將報文交給PREROUTING Hook點注冊的鉤子函數(shù)處理:
  1.         return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
  2.                        ip_rcv_finish);
復制代碼

由以上代碼可見,報文經(jīng)過鉤子函數(shù)之后,由ip_rcv_finish()接著處理。該函數(shù)主要完成數(shù)據(jù)報文的路由查找。
如果是發(fā)往本地的報文,則會調(diào)用ip_local_deliver()函數(shù):
  1.         return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
  2.                        ip_local_deliver_finish);
復制代碼

該函數(shù)主要功能就是將報文交給NF_IP_LOCAL_IN hook點的鉤子函數(shù)進行處理。我們在第一部分中添加的一條iptables規(guī)則就是對于經(jīng)過INPUT鏈的TCP報文且目的端口21執(zhí)行動作QUEUE。如果此時用戶空間已經(jīng)開啟socket等待接收IP Queue報文的話,那么對應的報文就會進入用戶空間,然后就可以參照我們之前提供的用戶空間例程進行處理。
這里,我們簡單列出在NF_IP_LOCAL_IN中NF_HOOK宏的調(diào)用函數(shù)過程:
NF_HOOK()->NF_HOOK_THRESH()->nf_hook_thresh()->nf_hook_slow()
當nf_hook_slow函數(shù)返回值為NF_QUEUE時,進一步調(diào)用nf_queue()。該函數(shù)對所有動作為QUEUE的報文進行處理,其中關鍵的一行代碼如下:
  1.         status = queue_handler[pf]->outfn(*skb, info, queuenum,
  2.                                           queue_handler[pf]->data);
復制代碼

這行代碼就是將報文按照IP層的協(xié)議交給對應的queue handler。IPv4協(xié)議中注冊的queue handler為ipq_enqueue_packet(),即我們要分寫ip_queue模塊的代碼。
至此,需要QUEUE的報文已經(jīng)走入了我們要分析ip_queue,我們下面就開始走入正題,分析ip_queue的代碼。

--未完待續(xù)

論壇徽章:
36
IT運維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
3 [報告]
發(fā)表于 2010-01-04 21:58 |只看該作者
三、ip_queue代碼分析
        ip_queue模塊的代碼較為簡單,包含ip_queue.h和ip_queue.c。我們將分別該兩個源文件進行分析。
(一)數(shù)據(jù)結構的定義
        ip_queue模塊的數(shù)據(jù)結構定義在頭文件ip_queue.h中,主要定義了用于在內(nèi)核態(tài)和用戶態(tài)傳輸數(shù)據(jù)的相關數(shù)據(jù)結構。其代碼如下:
  1. /*
  2. * This is a module which is used for queueing IPv4 packets and
  3. * communicating with userspace via netlink.
  4. *
  5. * (C) 2000 James Morris, this code is GPL.
  6. */
  7. #ifndef _IP_QUEUE_H
  8. #define _IP_QUEUE_H

  9. #ifdef __KERNEL__
  10. #ifdef DEBUG_IPQ
  11. #define QDEBUG(x...) printk(KERN_DEBUG ## x)
  12. #else
  13. #define QDEBUG(x...)
  14. #endif  /* DEBUG_IPQ */
  15. #else
  16. #include <net/if.h>
  17. #endif        /* ! __KERNEL__ */

  18. /* 內(nèi)核態(tài)發(fā)送到用戶態(tài)的消息的數(shù)據(jù)結構*/
  19. typedef struct ipq_packet_msg {
  20.         unsigned long packet_id;        /* ID of queued packet */
  21.         unsigned long mark;                /* Netfilter mark value */
  22.         long timestamp_sec;                /* Packet arrival time (seconds) */
  23.         long timestamp_usec;                /* Packet arrvial time (+useconds) */
  24.         unsigned int hook;                /* Netfilter hook we rode in on */
  25.         char indev_name[IFNAMSIZ];        /* Name of incoming interface */
  26.         char outdev_name[IFNAMSIZ];        /* Name of outgoing interface */
  27.         unsigned short hw_protocol;        /* Hardware protocol (network order) */
  28.         unsigned short hw_type;                /* Hardware type */
  29.         unsigned char hw_addrlen;        /* Hardware address length */
  30.         unsigned char hw_addr[8];        /* Hardware address */
  31.         size_t data_len;                /* Length of packet data */
  32.         unsigned char payload[0];        /* Optional packet data */
  33. } ipq_packet_msg_t;

  34. /* 用戶態(tài)發(fā)送到內(nèi)核態(tài)的模式消息 */
  35. typedef struct ipq_mode_msg {
  36.         unsigned char value;                /* Requested mode */
  37.         size_t range;                        /* Optional range of packet requested */
  38. } ipq_mode_msg_t;

  39. /* 用戶態(tài)發(fā)送到內(nèi)核態(tài)的斷言消息 */
  40. typedef struct ipq_verdict_msg {
  41.         unsigned int value;                /* Verdict to hand to netfilter */
  42.         unsigned long id;                /* Packet ID for this verdict */
  43.         size_t data_len;                /* Length of replacement data */
  44.         unsigned char payload[0];        /* Optional replacement packet */
  45. } ipq_verdict_msg_t;

  46. /*統(tǒng)一封裝起來的用戶態(tài)發(fā)內(nèi)核態(tài)的信息的數(shù)據(jù)結構*/
  47. typedef struct ipq_peer_msg {
  48.         union {
  49.                 ipq_verdict_msg_t verdict;
  50.                 ipq_mode_msg_t mode;
  51.         } msg;
  52. } ipq_peer_msg_t;

  53. /* 報文傳輸?shù)哪J?/
  54. enum {
  55.         IPQ_COPY_NONE,                /* Initial mode, packets are dropped */
  56.         IPQ_COPY_META,                /* Copy metadata */
  57.         IPQ_COPY_PACKET                /* Copy metadata + packet (range) */
  58. };
  59. #define IPQ_COPY_MAX IPQ_COPY_PACKET

  60. /* IP Queue消息的類型 */
  61. #define IPQM_BASE        0x10        /* standard netlink messages below this */
  62. #define IPQM_MODE        (IPQM_BASE + 1)                /* Mode request from peer */
  63. #define IPQM_VERDICT        (IPQM_BASE + 2)                /* Verdict from peer */
  64. #define IPQM_PACKET        (IPQM_BASE + 3)                /* Packet from kernel */
  65. #define IPQM_MAX        (IPQM_BASE + 4)

  66. #endif /*_IP_QUEUE_H*/
復制代碼

該頭文件中定義的相關數(shù)據(jù)結構和宏應該和用戶空間引用的頭文件的內(nèi)容是保持一致的。因此,對于該文件中的數(shù)據(jù)結構的詳細解釋,可以參考本系列文章的第一篇《Linux內(nèi)核IP Queue機制的分析(一)——用戶態(tài)接收數(shù)據(jù)包》中第二部分IP Queue編程接口。

(二)ip_queue模塊的加載和卸載
分析一個內(nèi)核模塊的源代碼,通常我們先看模塊加載時做哪些工作,這樣就可以了解到模塊具體的功能會在什么條件下執(zhí)行。而模塊的卸載通常就是將模塊加載時注冊的函數(shù)和申請的資源進行釋放等工作。因此,這里簡單的分析一下init函數(shù)的實現(xiàn),代碼在ip_queue.c中。

  1. static int __init ip_queue_init(void)
  2. {
  3.         int status = -ENOMEM;
  4.         struct proc_dir_entry *proc;
  5.         /*注冊netlink的通知鏈*/
  6.         netlink_register_notifier(&ipq_nl_notifier);
  7.         /*內(nèi)核態(tài)創(chuàng)建netlink的socket,并注冊ipq_rcv_sk函數(shù)實現(xiàn)接收用戶空間下發(fā)的配置數(shù)據(jù)*/
  8.         ipqnl = netlink_kernel_create(NETLINK_FIREWALL, 0, ipq_rcv_sk,
  9.                                       THIS_MODULE);
  10.         if (ipqnl == NULL) {
  11.                 printk(KERN_ERR "ip_queue: failed to create netlink socket\n");
  12.                 goto cleanup_netlink_notifier;
  13.         }
  14.        
  15.         /*注冊proc文件*/
  16.         proc = proc_net_create(IPQ_PROC_FS_NAME, 0, ipq_get_info);
  17.         if (proc)
  18.                 proc->owner = THIS_MODULE;
  19.         else {
  20.                 printk(KERN_ERR "ip_queue: failed to create proc entry\n");
  21.                 goto cleanup_ipqnl;
  22.         }
  23.         /*注冊網(wǎng)絡設備的通知鏈*/
  24.         register_netdevice_notifier(&ipq_dev_notifier);
  25.         ipq_sysctl_header = register_sysctl_table(ipq_root_table, 0);
  26.         /*注冊IP Queue機制的報文處理結構,主要包含一個報文的入隊處理函數(shù),下面具體分析*/
  27.         status = nf_register_queue_handler(PF_INET, &nfqh);
  28.         if (status < 0) {
  29.                 printk(KERN_ERR "ip_queue: failed to register queue handler\n");
  30.                 goto cleanup_sysctl;
  31.         }
  32.         return status;

  33. cleanup_sysctl:
  34.         unregister_sysctl_table(ipq_sysctl_header);
  35.         unregister_netdevice_notifier(&ipq_dev_notifier);
  36.         proc_net_remove(IPQ_PROC_FS_NAME);
  37.        
  38. cleanup_ipqnl:
  39.         sock_release(ipqnl->sk_socket);
  40.         mutex_lock(&ipqnl_mutex);
  41.         mutex_unlock(&ipqnl_mutex);
  42.        
  43. cleanup_netlink_notifier:
  44.         netlink_unregister_notifier(&ipq_nl_notifier);
  45.         return status;
  46. }
復制代碼

IP Queue模塊的初始化函數(shù)最重要的兩個工作:
(1)創(chuàng)建用于IP Queue的netlink socket,實現(xiàn)接收用戶態(tài)的數(shù)據(jù)的函數(shù)ipq_rcv_sk();
(2)注冊IP Queue報文的入隊處理函數(shù),并將數(shù)據(jù)包按照用戶的配置將相關信息發(fā)向用戶空間。當NF框架的hook函數(shù)對報文的處理返回NF_QUEUE時,該函數(shù)作為報文下一步被處理的入口函數(shù)。
模塊初始化中還有一些其他諸如注冊通知鏈的工作,這里我們不作具體分析,想了解相關細節(jié)的朋友可以查閱具體的資料。至于通知鏈的相關知識,也可以參考論壇上scutan兄的精華帖《內(nèi)核通知鏈 學習筆記》。鏈接:
http://linux.chinaunix.net/bbs/viewthread.php?tid=1051266

--未完待續(xù)

論壇徽章:
36
IT運維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
4 [報告]
發(fā)表于 2010-01-04 22:01 |只看該作者
(三)ip_queue報文入隊處理函數(shù)的注冊
   在上面分析的模塊注冊代碼中,IP Queue的報文入隊處理函數(shù)的注冊是通過調(diào)用nf_register_queue_handler()來實現(xiàn)的。因此,有必要了解一下該函數(shù)的源碼,源碼位于nf_queue.c中:
  1. /* return EBUSY when somebody else is registered, return EEXIST if the
  2. * same handler is registered, return 0 in case of success. */
  3. int nf_register_queue_handler(int pf, struct nf_queue_handler *qh)
  4. {      
  5.         int ret;
  6.         /*IP協(xié)議族的值必須在當前指定的范圍內(nèi)*/
  7.         if (pf >= NPROTO)
  8.                 return -EINVAL;

  9.         write_lock_bh(&queue_handler_lock);
  10.         /*該queue handler已經(jīng)被注冊過了*/
  11.         if (queue_handler[pf] == qh)
  12.                 ret = -EEXIST;
  13.         /*該協(xié)議族已經(jīng)被注冊了handler了*/
  14.         else if (queue_handler[pf])
  15.                 ret = -EBUSY;
  16.         /*將該協(xié)議的queue hanler指向參數(shù)qh*/
  17.         else {
  18.                 queue_handler[pf] = qh;
  19.                 ret = 0;
  20.         }
  21.         write_unlock_bh(&queue_handler_lock);

  22.         return ret;
  23. }
復制代碼

該函數(shù)的代碼比較簡單,先來了解一下函數(shù)的兩個參數(shù):
(1)pf:IP協(xié)議族的值,PF_INET和PF_INET6分別代表IPv4和IPv6。
(2)qh:NF中對報文進行Queue的結構體,定義如下(netfilter.h):
  1. /* Packet queuing */
  2. struct nf_queue_handler {
  3.         int (*outfn)(struct sk_buff *skb, struct nf_info *info,
  4.                      unsigned int queuenum, void *data);
  5.         void *data;
  6.         char *name;
  7. };
復制代碼

由此可見,該結構體主要包含一個函數(shù)指針,用于處理NF框架需要Queue的報文。data應該是用來保存一些私有數(shù)據(jù),name則是該queue handler的名稱。
代碼中已經(jīng)包含了該函數(shù)源碼的簡單注釋,這里再對該函數(shù)進行一下簡單的總結:
(1) 每個協(xié)議族只能注冊一個queue handler;
(2) 隨后報文的處理中,可以根據(jù)報文的協(xié)議族,就可以找到報文進行Queue時的處理函數(shù)queue_handler[pf]->outfn()。
        在IP Queue模塊中,queue handler的注冊如下所示:
  1. status = nf_register_queue_handler(PF_INET, &nfqh);
復制代碼

        可見,注冊的是IPv4協(xié)議族的報文處理函數(shù),而nfqh結構體的定義如下:
  1. static struct nf_queue_handler nfqh = {
  2.         .name        = "ip_queue",
  3.         .outfn        = &ipq_enqueue_packet,
  4. };
復制代碼

這里,IP Queue處理報文的函數(shù)終于閃亮登場了,我們前面啰嗦了半天,主要就是想理順一下思路,順理成章的引出該函數(shù)。
(四)入隊函數(shù)ipq_enqueue_packet —— 發(fā)送數(shù)據(jù)包到用戶空間
        第二部分已經(jīng)分析過了該函數(shù)會在什么條件下被觸發(fā)。這里詳細分析該函數(shù)的實現(xiàn),代碼在ip_queue.c中。
        分析代碼之前,我們先了解一下IP Queue對數(shù)據(jù)包隊列管理的核心數(shù)據(jù)結構:
  1. struct ipq_queue_entry {
  2.         struct list_head list;
  3.         struct nf_info *info;
  4.         struct sk_buff *skb;
  5. };
復制代碼

所有被Queue到用戶空間的數(shù)據(jù)包都會有這樣一個結構體。其中,
該數(shù)據(jù)結構的第1個元素是個雙向鏈表結構,用于將所有queue的數(shù)據(jù)包用雙向鏈表連
接起來,實現(xiàn)隊列管理。
第2個元素同樣是一個結構體,其定義在netfilter.h中:
  1. /* Each queued (to userspace) skbuff has one of these. */
  2. struct nf_info
  3. {
  4.         /* The ops struct which sent us to userspace. */
  5.         struct nf_hook_ops *elem;
  6.        
  7.         /* If we're sent to userspace, this keeps housekeeping info */
  8.         int pf;
  9.         unsigned int hook;
  10.         struct net_device *indev, *outdev;
  11.         int (*okfn)(struct sk_buff *);
  12. };
復制代碼

這個結構體應該很容易看出他的作用,就是記錄下當數(shù)據(jù)包被Queue時所在的hook函數(shù)的相關信息,包括hook的操作結構、協(xié)議號、hook點等相關信息。當用戶空間下發(fā)了對數(shù)據(jù)包的處理結果時,內(nèi)核就需要參考這個結構對數(shù)據(jù)包進行進一步的處理。具體的我們會在后面數(shù)據(jù)包回注函數(shù)中分析。
第3個元素是skb本身,也就是回注函數(shù)中要處理的數(shù)據(jù)包。

OK,我們下面就開始如對函數(shù)的分析。
  1. static int
  2. ipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info,
  3.                    unsigned int queuenum, void *data)
  4. {
  5.         int status = -EINVAL;
  6.         struct sk_buff *nskb;
  7.         struct ipq_queue_entry *entry;

  8.         /*判斷用戶配置的模式,可以為拷貝元數(shù)據(jù)或者整個數(shù)據(jù)包的信息*/
  9.         if (copy_mode == IPQ_COPY_NONE)
  10.                 return -EAGAIN;
  11.         /*為即將入隊的數(shù)據(jù)包分配一個struct ipq_queue_entry 結構體,用于實現(xiàn)對數(shù)據(jù)包的管理,我們稱之為queue管理結構體*/
  12.         entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
  13.         if (entry == NULL) {
  14.                 printk(KERN_ERR "ip_queue: OOM in ipq_enqueue_packet()\n");
  15.                 return -ENOMEM;
  16.         }
  17.         /*將該數(shù)據(jù)包對應的相關信息保存到管理結構體中*/
  18.         entry->info = info;
  19.         entry->skb = skb;
  20.        
  21. /*構建用于發(fā)往用戶空間的消息*/
  22.         nskb = ipq_build_packet_message(entry, &status);
  23.         if (nskb == NULL)
  24.                 goto err_out_free;
  25.                
  26.         write_lock_bh(&queue_lock);
  27.        
  28.         /如果用戶空間沒有開啟進程,等待接收消息的話,就釋放該消息/
  29.         if (!peer_pid)
  30.                 goto err_out_free_nskb;
  31.         /*如果當前隊列中的數(shù)據(jù)包總數(shù)超過了設置的最大值,則是放該消息,并且增加丟棄數(shù)據(jù)包的統(tǒng)計計數(shù)*/
  32.         if (queue_total >= queue_maxlen) {
  33.                 queue_dropped++;
  34.                 status = -ENOSPC;
  35.                 if (net_ratelimit())
  36.                           printk (KERN_WARNING "ip_queue: full at %d entries, "
  37.                                   "dropping packets(s). Dropped: %d\n", queue_total,
  38.                                   queue_dropped);
  39.                 goto err_out_free_nskb;
  40.         }

  41.         /* 將消息發(fā)送給用戶空間 */
  42.         status = netlink_unicast(ipqnl, nskb, peer_pid, MSG_DONTWAIT);
  43.         if (status < 0) {
  44.                 queue_user_dropped++;
  45.                 goto err_out_unlock;
  46.         }
  47.         /*成功發(fā)送到用戶空間之后,將該數(shù)據(jù)包的queue管理結構添加到全局隊列鏈表中*/
  48.         __ipq_enqueue_entry(entry);

  49.         write_unlock_bh(&queue_lock);
  50.         return status;

  51. err_out_free_nskb:
  52.         kfree_skb(nskb);
  53.        
  54. err_out_unlock:
  55.         write_unlock_bh(&queue_lock);

  56. err_out_free:
  57.         kfree(entry);
  58.         return status;
  59. }
復制代碼

該函數(shù)成功執(zhí)行之后,就將數(shù)據(jù)包相關的信息發(fā)送到用戶空間,同時將該數(shù)據(jù)包對應的queue管理結構加到全局鏈表中。
下面對于ipq_enqueue_packet函數(shù)中調(diào)用的幾個重要函數(shù)做一些分析。
首先是構建消息的函數(shù)ipq_build_packet_message。
  1. static struct sk_buff *
  2. ipq_build_packet_message(struct ipq_queue_entry *entry, int *errp)
  3. {
  4.         unsigned char *old_tail;
  5.         size_t size = 0;
  6.         size_t data_len = 0;
  7.         struct sk_buff *skb;
  8.         struct ipq_packet_msg *pmsg;
  9.         struct nlmsghdr *nlh;

  10.         read_lock_bh(&queue_lock);
  11.         /*根據(jù)用戶配置的copy模式,確定發(fā)給用戶空間消息的長度*/
  12.         switch (copy_mode) {
  13.         /*對于初始模式和拷貝元數(shù)據(jù)的模式,消息應該包括netlink的消息頭和ipq的消息頭,長度為兩個消息頭長度之和,即sizeof(struct nlmsghdr) + sizeof(struct ipq_packet_msg),并考慮對齊的因素。這里直接調(diào)用netlink封裝的宏NLMSG_SPACE來計算長度。該宏本身已經(jīng)包含了netlink消息頭的長度*/
  14.         case IPQ_COPY_META:
  15.         case IPQ_COPY_NONE:
  16.                 /*消息長度為netlink的消息頭和ipq的消息頭的長度之和*/
  17.                 size = NLMSG_SPACE(sizeof(*pmsg));
  18.                 data_len = 0;
  19.                 break;
  20.        
  21.         case IPQ_COPY_PACKET:
  22.                 if (entry->skb->ip_summed == CHECKSUM_HW &&
  23.                     (*errp = skb_checksum_help(entry->skb,
  24.                                                entry->info->outdev == NULL))) {
  25.                         read_unlock_bh(&queue_lock);
  26.                         return NULL;
  27.                 }
  28.                 /*如果用戶需要拷貝整個數(shù)據(jù)包的內(nèi)容,那么如果配置的長度為0或者超出數(shù)據(jù)包的實際長度,則以數(shù)據(jù)包的實際長度進行拷貝*/
  29.                 if (copy_range == 0 || copy_range > entry->skb->len)
  30.                         data_len = entry->skb->len;
  31.                 else
  32.                         data_len = copy_range;
  33.                 /*消息長度為netlink的消息頭、ipq的消息頭的長度以及要拷貝數(shù)據(jù)包的長度之和*/
  34.                 size = NLMSG_SPACE(sizeof(*pmsg) + data_len);
  35.                 break;
  36.        
  37.         default:
  38.                 *errp = -EINVAL;
  39.                 read_unlock_bh(&queue_lock);
  40.                 return NULL;
  41.         }

  42.         read_unlock_bh(&queue_lock);
  43.         /*為構建的消息申請內(nèi)存,同樣適用skb結構體,消息的內(nèi)容應該在skb->data和skb->tail之間*/
  44.         skb = alloc_skb(size, GFP_ATOMIC);
  45.         if (!skb)
  46.                 goto nlmsg_failure;
  47.         /*記錄一下新分配的skb中的tail指針的地址,此時應該skb->tail指向skb->data*/
  48.         old_tail= skb->tail;
  49.         /*填充netlink消息頭*/
  50.         nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh));
  51.         /*獲取netlink消息體的起始地址,即ipq的消息頭*/
  52.         pmsg = NLMSG_DATA(nlh);
  53.         memset(pmsg, 0, sizeof(*pmsg));

  54.         /*將相關的ipq消息記錄到消息頭中*/
  55.                 /*將skb對應的queue管理結構體的地址作為packet_id*/
  56.         pmsg->packet_id       = (unsigned long )entry;
  57.         pmsg->data_len        = data_len;
  58.         pmsg->timestamp_sec   = entry->skb->tstamp.off_sec;
  59.         pmsg->timestamp_usec  = entry->skb->tstamp.off_usec;
  60.         pmsg->mark            = entry->skb->nfmark;
  61.         pmsg->hook            = entry->info->hook;
  62.         pmsg->hw_protocol     = entry->skb->protocol;
  63.        
  64.         /*記錄被queue數(shù)據(jù)包對應的輸入設備名稱*/
  65.         if (entry->info->indev)
  66.                 strcpy(pmsg->indev_name, entry->info->indev->name);
  67.         else
  68.                 pmsg->indev_name[0] = '\0';
  69.         /*記錄被queue數(shù)據(jù)包對應的輸出設備名稱*/
  70.         if (entry->info->outdev)
  71.                 strcpy(pmsg->outdev_name, entry->info->outdev->name);
  72.         else
  73.                 pmsg->outdev_name[0] = '\0';

  74.         /*記錄被queue數(shù)據(jù)包對應的鏈路層的協(xié)議類型及地址長度*/
  75.         if (entry->info->indev && entry->skb->dev) {
  76.                 pmsg->hw_type = entry->skb->dev->type;
  77.                 if (entry->skb->dev->hard_header_parse)
  78.                         pmsg->hw_addrlen =
  79.                                 entry->skb->dev->hard_header_parse(entry->skb,
  80.                                                                    pmsg->hw_addr);
  81.         }
  82.         /* data_len != 0 說明需要拷貝數(shù)據(jù)包的原始數(shù)據(jù)。skb_copy_bits 函數(shù)的實現(xiàn)不再分析,其主要功能就是將從skb->data+offset開始的數(shù)據(jù)拷貝data_len個字節(jié)到pmsg->payload中,即ipq消息的載荷。另外一個需要注意的問題,如果skb掛載的有分片包,則skb_copy_bits也會按照順序拷貝分片包中的數(shù)據(jù)*/
  83.         if (data_len)
  84.                 if (skb_copy_bits(entry->skb, 0, pmsg->payload, data_len))
  85.                         BUG();
  86.         /*設置netlink消息的長度*/
  87.         nlh->nlmsg_len = skb->tail - old_tail;
  88.         return skb;

  89. nlmsg_failure:
  90.         if (skb)
  91.                 kfree_skb(skb);
  92.         *errp = -EINVAL;
  93.         printk(KERN_ERR "ip_queue: error creating packet message\n");
  94.         return NULL;
  95. }
復制代碼

其次,對于netlink_unicast函數(shù),我們這里不具體分析,它的作用就是將內(nèi)核封裝好的netlink消息發(fā)送到用戶態(tài)。
最后分析一下__ipq_enqueue_entry 函數(shù)。
  1. static inline void
  2. __ipq_enqueue_entry(struct ipq_queue_entry *entry)
  3. {
  4.        list_add(&entry->list, &queue_list);
  5.        queue_total++;
  6. }
復制代碼

該函數(shù)的功能很簡答,就是將當前skb的queue管理結構添加到全局的queue管理鏈表queue_list中,并增加隊列的統(tǒng)計計數(shù)。該鏈表記錄了所有發(fā)往用戶空間,且并未收到用戶空間下發(fā)處理結果的數(shù)據(jù)包。
至此,數(shù)據(jù)包的入隊處理函數(shù)已經(jīng)分析完畢。一切順利執(zhí)行的話,現(xiàn)在用戶態(tài)已經(jīng)接收到關于該數(shù)據(jù)包的消息。

--未完待續(xù)

論壇徽章:
36
IT運維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
5 [報告]
發(fā)表于 2010-01-04 22:05 |只看該作者
(五)接收和處理用戶空間的配置—— 接收用戶空間的數(shù)據(jù)包
        ip_queue模塊加載的代碼中有這樣一行代碼:
  1.         ipqnl = netlink_kernel_create(NETLINK_FIREWALL, 0, ipq_rcv_sk,
  2.                                       THIS_MODULE);
復制代碼

        上文也已經(jīng)分析了,該行代碼就是注冊一個用于接收用戶空間配置數(shù)據(jù)的函數(shù)ipq_rcv_sk。因此,接收和處理用戶空間的配置應該從這個函數(shù)開始分析。
        ipq_rcv_sk的代碼如下:
  1.         static void
  2. ipq_rcv_sk(struct sock *sk, int len)
  3. {
  4.         struct sk_buff *skb;
  5.         unsigned int qlen;

  6.         mutex_lock(&ipqnl_mutex);
  7.                        
  8.         for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
  9.                 skb = skb_dequeue(&sk->sk_receive_queue);
  10.                 ipq_rcv_skb(skb);
  11.                 kfree_skb(skb);
  12.         }
  13.                
  14.         mutex_unlock(&ipqnl_mutex);
  15. }
復制代碼

該函數(shù)的功能非常明確,就從當前套接字的接收數(shù)據(jù)包隊列中取出數(shù)據(jù)包,然后交給ipq_rcv_skb進一步分析和處理,然后就釋放掉該數(shù)據(jù)包的緩存。
下面接著分析ipq_rcv_skb函數(shù),其源碼如下:
  1. #define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0)

  2. static inline void
  3. ipq_rcv_skb(struct sk_buff *skb)
  4. {
  5.         int status, type, pid, flags, nlmsglen, skblen;
  6.         struct nlmsghdr *nlh;
  7.         /*獲取并檢查數(shù)據(jù)包的長度*/
  8.         skblen = skb->len;
  9.         if (skblen < sizeof(*nlh))
  10.                 return;
  11.         /*獲取netlink消息頭部的結構體,并檢查頭部中保存的數(shù)據(jù)長度字段*/
  12.         nlh = (struct nlmsghdr *)skb->data;
  13.         nlmsglen = nlh->nlmsg_len;
  14.         if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen)
  15.                 return;
  16.         /*取到用戶態(tài)的進程ID,以及用戶空間配置的標記為*/
  17.         pid = nlh->nlmsg_pid;
  18.         flags = nlh->nlmsg_flags;
  19.         /*判斷用戶進程的ID是否合法,以及該數(shù)據(jù)包是否是netlink的請求包*/
  20.         if(pid <= 0 || !(flags & NLM_F_REQUEST) || flags & NLM_F_MULTI)
  21.                 RCV_SKB_FAIL(-EINVAL);
  22.                
  23.         if (flags & MSG_TRUNC)
  24.                 RCV_SKB_FAIL(-ECOMM);
  25.         /*獲取消息的類型并檢查*/       
  26.         type = nlh->nlmsg_type;
  27.         if (type < NLMSG_NOOP || type >= IPQM_MAX)
  28.                 RCV_SKB_FAIL(-EINVAL);
  29.         /*如果不是ip_queue消息,則返回*/       
  30.         if (type <= IPQM_BASE)
  31.                 return;
  32.                
  33.         if (security_netlink_recv(skb, CAP_NET_ADMIN))
  34.                 RCV_SKB_FAIL(-EPERM);
  35.        
  36.         write_lock_bh(&queue_lock);
  37.         /*如果內(nèi)核態(tài)記錄的用戶空間進程ID非0,且當前數(shù)據(jù)包的進程ID并不等于內(nèi)核態(tài)記錄的值,則意味著接收數(shù)據(jù)包失敗*/
  38.         if (peer_pid) {
  39.                 if (peer_pid != pid) {
  40.                         write_unlock_bh(&queue_lock);
  41.                         RCV_SKB_FAIL(-EBUSY);
  42.                 }
  43.         } else {
  44.                 net_enable_timestamp();
  45. /*如果內(nèi)核態(tài)記錄的peer_pid 為0,則將當前消息中的進程ID記錄到全局變量peer_pid 中。此種情形應該是用戶態(tài)初次向內(nèi)核發(fā)送ip_queue的消息*/
  46.                 peer_pid = pid;
  47.         }
  48.                
  49.         write_unlock_bh(&queue_lock);
  50.         /*處理用戶空間下發(fā)的ip_queue配置消息*/
  51.         status = ipq_receive_peer(NLMSG_DATA(nlh), type,
  52.                                   nlmsglen - NLMSG_LENGTH(0));
  53.         if (status < 0)
  54.                 RCV_SKB_FAIL(status);
  55.                
  56.         if (flags & NLM_F_ACK)
  57.                 netlink_ack(skb, nlh, 0);
  58.         return;
  59. }
復制代碼

該函數(shù)對于接收到的用戶空間下發(fā)的消息,檢查其多個參數(shù)的合法性,并判斷是否是ip_queue的消息。如果是,則交由ipq_receive_peer函數(shù)進行消息的解析和處理。
下面我們分析一下ipq_receive_peer函數(shù)的實現(xiàn),代碼如下:
  1. static int
  2. ipq_receive_peer(struct ipq_peer_msg *pmsg,
  3.                  unsigned char type, unsigned int len)
  4. {
  5.         int status = 0;

  6.         if (len < sizeof(*pmsg))
  7.                 return -EINVAL;
  8.         /*根據(jù)用戶態(tài)的消息類型進行處理*/
  9.         switch (type) {
  10.         case IPQM_MODE:
  11.                 status = ipq_set_mode(pmsg->msg.mode.value,
  12.                                       pmsg->msg.mode.range);
  13.                 break;
  14.                
  15.         case IPQM_VERDICT:
  16.                 if (pmsg->msg.verdict.value > NF_MAX_VERDICT)
  17.                         status = -EINVAL;
  18.                 else
  19.                         status = ipq_set_verdict(&pmsg->msg.verdict,
  20.                                                  len - sizeof(*pmsg));
  21.                         break;
  22.         default:
  23.                 status = -EINVAL;
  24.         }
  25.         return status;
  26. }
復制代碼

該函數(shù)主要是根據(jù)用戶態(tài)設置的消息的類型,進行不同的處理。我們在IP Queue分析的第一篇文章《Linux內(nèi)核IP Queue機制的分析(一)¬——用戶態(tài)接收數(shù)據(jù)包》(以下簡稱文一)中已經(jīng)提到,用戶態(tài)下發(fā)到內(nèi)核態(tài)的消息分為“模式設置消息(IPQM_MODE)”和“斷言消息(IPQM_VERDICT)”兩個子類。
這里我們先分析消息類型為IPQM_MODE即模式設置消息時內(nèi)核態(tài)的處理。對于斷言消息IPQM_VERDICT的處理部分,我們將在后面第六節(jié)中分析。
模式設置消息對應的處理函數(shù)為ipq_set_mode,其代碼如下:
  1. static int
  2. ipq_set_mode(unsigned char mode, unsigned int range)
  3. {
  4.         int status;

  5.         write_lock_bh(&queue_lock);
  6.         status = __ipq_set_mode(mode, range);
  7.         write_unlock_bh(&queue_lock);
  8.         return status;
  9. }
復制代碼

該函數(shù)主要就是調(diào)用__ipq_set_mode函數(shù),其代碼如下:
  1. static inline int
  2. __ipq_set_mode(unsigned char mode, unsigned int range)
  3. {
  4.         int status = 0;
  5.        
  6.         switch(mode) {
  7.         case IPQ_COPY_NONE:
  8.         case IPQ_COPY_META:
  9.                 copy_mode = mode;
  10.                 copy_range = 0;
  11.                 break;
  12.                
  13.         case IPQ_COPY_PACKET:
  14.                 copy_mode = mode;
  15.                 copy_range = range;
  16.                 if (copy_range > 0xFFFF)
  17.                         copy_range = 0xFFFF;
  18.                 break;
  19.                
  20.         default:
  21.                 status = -EINVAL;

  22.         }
  23.         return status;
  24. }
復制代碼

同樣,我們在文一中分析過了模式設置消息的三種請求模式:IPQ_COPY_NONE、IPQ_COPY_META和IPQ_COPY_PACKET。參考文一中的具體解釋,可以很輕松的理解該函數(shù)的實現(xiàn):
(1)當請求模式為IPQ_COPY_NONE或IPQ_COPY_META時,記錄下所設置的模式保存到全局變量copy_mode,并且將全局變量copy_range置0。因為這兩種模式都不會要求讀取數(shù)據(jù)包的原始內(nèi)容;
(2)當請求模式為IPQ_COPY_PACKET時,記錄下所設置的模式保存到全局變量copy_mode,并將用戶空間設置的讀取數(shù)據(jù)包的長度值保存到全局變量copy_range中。如果用戶設置的長度大于0xFFFF,則將copy_range設置為0xFFFF。
        這里copy_mode和copy_range,以及前面提到的peer_pid都使用全局變量類型,因為內(nèi)核態(tài)發(fā)送相關消息到用戶空間時要讀取這幾個參數(shù),并根據(jù)各參數(shù)值的不同,發(fā)送不同類型的消息到用戶空間。
        通過模式設置消息,用戶空間成功的告訴內(nèi)核態(tài),我需要數(shù)據(jù)包的哪些部分,只是摘要信息,還是連數(shù)據(jù)包的原始內(nèi)容也要。如果需要數(shù)據(jù)包的原始內(nèi)容,長度是多少等等。而內(nèi)核態(tài)也會根據(jù)用戶態(tài)下發(fā)的需求,將數(shù)據(jù)包的相關信息打包發(fā)給用戶空間的接收進程。

--未完待續(xù)

論壇徽章:
36
IT運維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
6 [報告]
發(fā)表于 2010-01-04 22:09 |只看該作者
(六)數(shù)據(jù)包的最終處理
        IP Queue機制提供了一種將數(shù)據(jù)包交給用戶態(tài)處理的方法。那么,最后最關鍵的一步就是對數(shù)據(jù)包的處理。經(jīng)過我們上面的分析,用戶空間對數(shù)據(jù)包的處理,就是通過下發(fā)IP Queue消息,告訴內(nèi)核態(tài)數(shù)據(jù)包是丟棄還是接受等。而內(nèi)核態(tài)會根據(jù)具體的消息對數(shù)據(jù)包做真正的丟棄或接受等處理。
        那么,我們就從用戶態(tài)下發(fā)的消息開始,一直分析到數(shù)據(jù)包最終被處理。
        我們上面分析了用戶態(tài)下發(fā)的消息最終由__ipq_set_mode函數(shù)處理。該函數(shù)會根據(jù)消息類型調(diào)用對應的處理函數(shù)。用戶態(tài)下發(fā)的數(shù)據(jù)包處理意見的消息,我們稱之為“斷言消息”,對應消息類型的宏定義為IPQM_VERDICT。該消息對應的處理函數(shù)為ipq_set_verdict。該函數(shù)代碼如下:
  1. static int
  2. ipq_set_verdict(struct ipq_verdict_msg *vmsg, unsigned int len)
  3. {
  4.         struct ipq_queue_entry *entry;
  5.         /*檢查對數(shù)據(jù)包處理意見的值的合法性*/
  6.         if (vmsg->value > NF_MAX_VERDICT)
  7.                 return -EINVAL;
  8.         /*根據(jù)斷言消息中保存的id找到對應的數(shù)據(jù)包queue管理結構體*/
  9.         entry = ipq_find_dequeue_entry(id_cmp, vmsg->id);
  10.         if (entry == NULL)
  11.                 return -ENOENT;
  12.         else {
  13.                 /*記錄斷言消息的處理結果*/
  14.                 int verdict = vmsg->value;
  15.                 /*如果用戶空間拷貝了數(shù)據(jù)包的原始內(nèi)容,則要根據(jù)數(shù)據(jù)包被修改的情況調(diào)用函數(shù)ipq_mangle_ipv4對queue管理結構體中保存的原始skb進行修改*/
  16.                 if (vmsg->data_len && vmsg->data_len == len)
  17.                         if (ipq_mangle_ipv4(vmsg, entry) < 0)
  18.                                 verdict = NF_DROP;
  19.                 /*將數(shù)據(jù)包重新注入NF框架*/
  20.                 ipq_issue_verdict(entry, verdict);
  21.                 return 0;
  22.         }
  23. }
復制代碼

        該函數(shù)首先根據(jù)斷言消息的id,從全局鏈表queue_list中找到當前消息對應的queue管理結構體。然后,根據(jù)用戶空間對數(shù)據(jù)包的修改和處理意見,對當前queue管理結構體中的skb進行修改。最后,調(diào)用函數(shù)ipq_issue_verdict將數(shù)據(jù)包重新注入NF框架。
        下面將逐一分析上面三個階段的代碼。
1. 獲取斷言消息對應的queue管理結構體
        ipq_set_verdict函數(shù)中實現(xiàn)代碼如下:
  1. entry = ipq_find_dequeue_entry(id_cmp, vmsg->id);
復制代碼

可見這里調(diào)用了ipq_find_dequeue_entry函數(shù)獲取queue管理結構體的。傳進去參數(shù)一個是函數(shù)指針id_cmp, 一個是斷言消息的id。我們在文一中已經(jīng)提到過,斷言消息的id即內(nèi)核態(tài)發(fā)送給用戶空間消息中的packet_id。通過ipq_build_packet_message函數(shù)我們可以知道,packet_id就是當前數(shù)據(jù)包對應的queue管理結構體的地址。因此,找到queue管理結構體的方法,就是遍歷queue_list全局鏈表,找到地址等于vmsg->id的queue管理結構體。
因此,不難想象,id_cmp函數(shù)的實現(xiàn)代碼:
  1. static inline int
  2. id_cmp(struct ipq_queue_entry *e, unsigned long id)
  3. {
  4.         return (id == (unsigned long )e);
  5. }
復制代碼

那么,函數(shù)ipq_find_dequeue_entry的代碼實現(xiàn)的方法也就很明確了:
  1. static struct ipq_queue_entry *
  2. ipq_find_dequeue_entry(ipq_cmpfn cmpfn, unsigned long data)
  3. {
  4.         struct ipq_queue_entry *entry;
  5.        
  6.         write_lock_bh(&queue_lock);
  7.         entry = __ipq_find_dequeue_entry(cmpfn, data);
  8.         write_unlock_bh(&queue_lock);
  9.         return entry;
  10. }
復制代碼

該函數(shù)進一步調(diào)用函數(shù)__ipq_find_dequeue_entry,代碼如下:
  1. static inline struct ipq_queue_entry *
  2. __ipq_find_dequeue_entry(ipq_cmpfn cmpfn, unsigned long data)
  3. {
  4.         struct ipq_queue_entry *entry;

  5.         entry = __ipq_find_entry(cmpfn, data);
  6.         if (entry == NULL)
  7.                 return NULL;

  8.         __ipq_dequeue_entry(entry);
  9.         return entry;
  10. }
復制代碼

該函數(shù)首先調(diào)用__ipq_find_entry,遍歷全局鏈表queue_list,找到當前斷言消息對應數(shù)據(jù)包的queue管理結構體:
  1. /*
  2. * Find and return a queued entry matched by cmpfn, or return the last
  3. * entry if cmpfn is NULL.
  4. */
  5. static inline struct ipq_queue_entry *
  6. __ipq_find_entry(ipq_cmpfn cmpfn, unsigned long data)
  7. {
  8.         struct list_head *p;

  9.         list_for_each_prev(p, &queue_list) {
  10.                 struct ipq_queue_entry *entry = (struct ipq_queue_entry *)p;
  11.                
  12.                 if (!cmpfn || cmpfn(entry, data))
  13.                         return entry;
  14.         }
  15.         return NULL;
  16. }
復制代碼

然后將調(diào)用函數(shù)__ipq_dequeue_entry將找到的queue管理結構體從全局鏈表queue_list中刪除,并減小被queue的數(shù)據(jù)包的統(tǒng)計計數(shù)。
  1. static inline void
  2. __ipq_dequeue_entry(struct ipq_queue_entry *entry)
  3. {
  4.         list_del(&entry->list);
  5.         queue_total--;
  6. }
復制代碼

至此,我們已經(jīng)找到了斷言消息對應數(shù)據(jù)包的queue管理結構體。
2. 根據(jù)用戶的配置修改數(shù)據(jù)包
        ipq_set_verdict函數(shù)中實現(xiàn)代碼如下:
  1. int verdict = vmsg->value;

  2. if (vmsg->data_len && vmsg->data_len == len)
  3.         if (ipq_mangle_ipv4(vmsg, entry) < 0)
  4.                 verdict = NF_DROP;
復制代碼

        這里,首先記錄下對數(shù)據(jù)包的處理結果vmsg->value。然后,比較關鍵的一步,就是如果斷言消息中數(shù)據(jù)長度部分不為0,并且消息中記錄的載荷長度等于當前計算出來的載荷長度(如果正常進行設置和通信的話,兩者應該是相等的),則調(diào)用ipq_mangle_ipv4對原始的數(shù)據(jù)包進行處理。
        函數(shù)ipq_mangle_ipv4的代碼如下:
  1. static int
  2. ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct ipq_queue_entry *e)
  3. {
  4.         int diff;
  5.         /*獲得用戶空間處理后的數(shù)據(jù)包的ip頭部*/
  6.         struct iphdr *user_iph = (struct iphdr *)v->payload;
  7.         /*判斷消息中記錄的載荷長度是否小于頭部長度。如果是,則就不再修改queue中保存的skb*/
  8.         if (v->data_len < sizeof(*user_iph))
  9.                 return 0;
  10.         /*判斷數(shù)據(jù)包的長度是否發(fā)生變化,即獲取用戶修改后的數(shù)據(jù)包和原始數(shù)據(jù)包的長度的關系*/
  11.         diff = v->data_len - e->skb->len;
  12.         /*用戶減小了數(shù)據(jù)包的長度*/
  13.         if (diff < 0)
  14.                 skb_trim(e->skb, v->data_len);
  15.         /*用戶增大了數(shù)據(jù)包的長度*/
  16.         else if (diff > 0) {
  17.                 /*非法的數(shù)據(jù)包長度值*/
  18.                 if (v->data_len > 0xFFFF)
  19.                         return -EINVAL;
  20.                 /*數(shù)據(jù)包被擴充的長度大于了skb的tailroom,即skb的tail:end之間的長度已經(jīng)不能容納數(shù)據(jù)包增加的數(shù)據(jù),這是需要新分配一個skb*/
  21.                 if (diff > skb_tailroom(e->skb)) {
  22.                         struct sk_buff *newskb;
  23.                         /*將原先的skb進行擴充,并將原始skb的數(shù)據(jù)包拷貝到新的skb中*/
  24.                         newskb = skb_copy_expand(e->skb,
  25.                                                  skb_headroom(e->skb),
  26.                                                  diff,
  27.                                                  GFP_ATOMIC);
  28.                         if (newskb == NULL) {
  29.                                 printk(KERN_WARNING "ip_queue: OOM "
  30.                                       "in mangle, dropping packet\n");
  31.                                 return -ENOMEM;
  32.                         }
  33.                         if (e->skb->sk)
  34.                                 skb_set_owner_w(newskb, e->skb->sk);
  35.                         kfree_skb(e->skb);
  36.                         e->skb = newskb;
  37.                 }
  38.                 /*將skb的tail指針增加tail+diff*/
  39.                 skb_put(e->skb, diff);
  40.         }
  41.         if (!skb_make_writable(&e->skb, v->data_len))
  42.                 return -ENOMEM;
  43.         /*將斷言消息中全部的載荷拷貝到skb對應的數(shù)據(jù)區(qū)中。*/
  44.         memcpy(e->skb->data, v->payload, v->data_len);
  45.         e->skb->ip_summed = CHECKSUM_NONE;

  46.         return 0;
  47. }
復制代碼

該函數(shù)主要就是斷言消息中的對數(shù)據(jù)包做的任何修改都反應到原始的skb中。
3. 將修改后的數(shù)據(jù)包重新注入NF框架
ipq_set_verdict函數(shù)實現(xiàn)的代碼如下:
  1.         ipq_issue_verdict(entry, verdict);
復制代碼

        即調(diào)用ipq_issue_verdict函數(shù)將數(shù)據(jù)包重新注入NF中。該函數(shù)的代碼如下:
  1. static void
  2. ipq_issue_verdict(struct ipq_queue_entry *entry, int verdict)
  3. {
  4.         /* TCP input path (and probably other bits) assume to be called
  5.          * from softirq context, not from syscall, like ipq_issue_verdict is
  6.          * called.  TCP input path deadlocks with locks taken from timer
  7.          * softirq, e.g.  We therefore emulate this by local_bh_disable() */

  8.         local_bh_disable();
  9.         nf_reinject(entry->skb, entry->info, verdict);
  10.         local_bh_enable();

  11.         kfree(entry);
  12. }
復制代碼

該函數(shù)將queue管理結構體的兩個個成員skb、info,以及用戶對數(shù)據(jù)包的處理結果verdict作為入?yún)ⅲ苯诱{(diào)用nf_reinject函數(shù)。nf_reinject函數(shù)會根據(jù)三個參數(shù)所保存的相關信息,對數(shù)據(jù)包進行真正的處理,比入交給下一個hook函數(shù)處理(NF_ACCEPT),或者丟棄(NF_DROP),或者重新交給當前再次交給當前的hook函數(shù)處理(NF_REPEAT)。
至此,整個ip_queue模塊的核心代碼已經(jīng)分析完畢。

--完

論壇徽章:
0
7 [報告]
發(fā)表于 2010-01-05 02:46 |只看該作者
支持下.

論壇徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辭舊歲徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
8 [報告]
發(fā)表于 2010-01-05 12:47 |只看該作者
好文

論壇徽章:
0
9 [報告]
發(fā)表于 2010-01-05 13:06 |只看該作者
好文好文  再加上前面兩篇  ip queue就全了^_^

論壇徽章:
36
IT運維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
10 [報告]
發(fā)表于 2010-01-05 13:10 |只看該作者

回復 #9 ubuntuer 的帖子

是啊,IP  Queue已經(jīng)系統(tǒng)的的分析完了。從用戶空間的使用到內(nèi)核空間的源碼。

慚愧的是,完成這三篇文章竟然拖了這么長時間。
您需要登錄后才可以回帖 登錄 | 注冊

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

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP