日期:2014-05-16  浏览次数:20780 次

Linux内核IP Queue机制的分析(三)――ip_queue内核模块的分析

Linux内核IP Queue机制的分析(三)――ip_queue内核模块的分析
2010年09月25日
   reference:http://linux.chinaunix.net/bbs/thread-1152070-1-1.html
  本文分析ip_queue的内核态源码。文中如有任何疏漏和差错,欢迎各位朋友指正。
  本文欢迎自由转载,但请标明出处,并保证本文的完整性。
  作者:Godbach
  Blog:http://Godbach.cublog.cn
  日期:2010/01/04
  本系列的前两篇文章如下:
  1. Linux内核IP Queue机制的分析(一)――用户态接收数据包
  http://blog.chinaunix.net/u/33048/showart_1678213.html
  2. Linux内核IP Queue机制的分析(二)――用户态处理并回传数据包
  http://blog.chinaunix.net/u/33048/showart_1839753.html
  本文大纲如下:
  一、IP Queue的生效
  二、网络层中IP报文进入IP Queue的流程
  三、ip_queue代码分析
  (一)数据结构的定义
  (二)ip_queue模块的加载和卸载
  (三)ip_queue报文入队处理函数的注册
  (四)入队函数ipq_enqueue_packet ―― 发送数据包到用户空间
  (五)接收和处理用户空间的配置―― 接收用户空间的数据包
  (六)数据包的最终处理
  ---------------------------------------------------------------
  一、IP Queue的生效
  数据包能够进入ip_queue模块,需要两个动作:
  (1)模块的加载:modprobe ip_queue
  (2)NF上对数据包执行NF_QUEUE的动作,这个可以通过用户态配置一条iptables规则实现:
  iptables -A INPUT -p tcp --dport 21 -j QUEUE
  这里假设对发往本机的TCP报文端口为21的进行QUEUE。
  有了以上两个步骤, 所有匹配到(2)中的报文将会调用IP Queue模块的相关函数。
  二、网络层中IP报文进入IP Queue的流程
  本文中分析的代码的内核版本为2.6.18.3.
  这里我们以本地接收报文为例,如果是转发的报文,可比照着分析即可。
  IP层接收报文的函数为:ip_rcv()(ip_input.c)。该函数对报文进行一些初步的检查后,就将报文交给PREROUTING Hook点注册的钩子函数处理:
  return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
  ip_rcv_finish);
  复制代码
  由以上代码可见,报文经过钩子函数之后,由ip_rcv_finish()接着处理。该函数主要完成数据报文的路由查找。
  如果是发往本地的报文,则会调用ip_local_deliver()函数:
  return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
  ip_local_deliver_finish);
  复制代码
  该函数主要功能就是将报文交给NF_IP_LOCAL_IN hook点的钩子函数进行处理。我们在第一部分中添加的一条iptables规则就是对于经过INPUT链的TCP报文且目的端口21执行动作QUEUE。如果此时用户空间已经开启socket等待接收IP Queue报文的话,那么对应的报文就会进入用户空间,然后就可以参照我们之前提供的用户空间例程进行处理。
  这里,我们简单列出在NF_IP_LOCAL_IN中NF_HOOK宏的调用函数过程:
  NF_HOOK()->NF_HOOK_THRESH()->nf_hook_thresh()->nf_hook_slow()
  当nf_hook_slow函数返回值为NF_QUEUE时,进一步调用nf_queue()。该函数对所有动作为QUEUE的报文进行处理,其中关键的一行代码如下:
  status = queue_handler[pf]->outfn(*skb, info, queuenum,
  queue_handler[pf]->data);
  复制代码
  这行代码就是将报文按照IP层的协议交给对应的queue handler。IPv4协议中注册的queue handler为ipq_enqueue_packet(),即我们要分写ip_queue模块的代码。
  至此,需要QUEUE的报文已经走入了我们要分析ip_queue,我们下面就开始走入正题,分析ip_queue的代码。
  三、ip_queue代码分析
  ip_queue模块的代码较为简单,包含ip_queue.h和ip_queue.c。我们将分别该两个源文件进行分析。
  (一)数据结构的定义
  ip_queue模块的数据结构定义在头文件ip_queue.h中,主要定义了用于在内核态和用户态传输数据的相关数据结构。其代码如下:
  /*
  * This is a module which is used for queueing IPv4 packets and
  * communicating with userspace via netlink.
  *
  * (C) 2000 James Morris, this code is GPL.
  */
  #ifndef _IP_QUEUE_H
  #define _IP_QUEUE_H
  #ifdef __KERNEL__
  #ifdef DEBUG_IPQ
  #define QDEBUG(x...) printk(KERN_DEBUG ## x)
  #else
  #define QDEBUG(x...)
  #endif  /* DEBUG_IPQ */
  #else
  #include
  #endif        /* ! __KERNEL__ */
  /* 内核态发送到用户态的消息的数据结构*/
  typedef struct ipq_packet_msg {
  unsigned long packet_id;        /* ID of queued packet */
  unsigned long mark;                /* Netfilter mark value */
  long timestamp_sec;                /* Packet arrival time (seconds) */
  long timestamp_usec;                /* Packet arrvial time (+useconds) */
  unsigned int hook;                /* Netfilter hook we rode in on */
  char indev_name[IFNAMSIZ];        /* Name of incoming inte