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