void __init netfilter_init(void)
{
int i, h;
for (i = 0; i < NPROTO; i++) {
for (h = 0; h < NF_MAX_HOOKS; h++)
INIT_LIST_HEAD(&nf_hooks[i][h]);
}
}
该函数任务就是初始化nf_hooks数组。nf_hooks是整个netfilter体系的数据中心,基于netfilter架构的各类子功能向nf_hooks注册回调函数,netfilter亦依据此数组调用各个接口实现数据包的过滤和处理。nf_hooks定义在netfilter.c中。
struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS];
NPROTO定义在net.h
#define NPROTO 32
NF_MAX_HOOKS定义在netfilter.h
#define NF_MAX_HOOKS 8
由此可见,nf_hooks是一个32*8的二维链表。链表结构在Linux内核中使用的非常多,是内核维护数据的一种数据结构。链表的结构见list.h
struct list_head {
struct list_head *next, *prev;
};
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
因此,netfilter_init函数就是把nf_hooks的各个链表置成由链表头结构的空链表。
接下来要关注的是,在链表中存储的数据内容是什么?nf_hooks中存储的是netfilter回调函数的调用指针、调用接入点、调用优先级等信息,定义在netfilter.h中:
typedef unsigned int nf_hookfn(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *));
struct nf_hook_ops
{
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook;
int pf;
int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};
nf_hooks_ops包含四个信息:
- hook回调函数
- pf协议簇
- hooknum接入点即数据包进入netfilter时在IP协议栈中的位置
- priority优先级,nf_hooks中回调函数按照优先级从小到达的顺序链接
回调函数nf_hookfn的参数是:
- hooknum接入点,同上
- skb数据包
- in数据包进入的网卡设备
- out数据包发送的网卡设备
- okfn如果该数据是可接受的NF_ACCEPT,则在netfilter处理完后调用该函数
假如我们将pf看作x轴、hooknum看作y轴、priority看作z轴,则nf_hooks可以看作是一个三维的数据结构。
- pf定义在socket.h中:
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
#define PF_UNIX AF_UNIX
#define PF_LOCAL AF_LOCAL
#define PF_INET AF_INET
…….
#define PF_INET6 AF_INET6
……
#define PF_BLUETOOTH AF_BLUETOOTH
#define PF_MAX AF_MAX
- hooknum定义在netfilter_ipv4.h中
#define NF_IP_PRE_ROUTING 0
#define NF_IP_LOCAL_IN 1
#define NF_IP_FORWARD 2
#define NF_IP_LOCAL_OUT 3
#define NF_IP_POST_ROUTING 4
#define NF_IP_NUMHOOKS 5
- priority定义在netfilter_ipv4.h中
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK = -200,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_LAST = INT_MAX,
};
子功能通过nf_register_hook()向netfilter框架注册回调函数,即将nf_hook_ops存入nf_hooks数组中。我们可以搜索内核代码找出内核实现了那些子功能,从而分析数据包在内核中如何被过滤和修改的。我们只考虑PF_INET协议的情况。简要总结如下: