參考http://www.linuxtcpipstack.com/685.html#NF_INET_PRE_ROUTING
https://opengers.github.io/openstack/openstack-base-netfilter-framework-overview/
http://blog.chinaunix.net/uid-26517122-id-4293010.html
https://netfilter.org/documentation/HOWTO/netfilter-hacking-HOWTO-3.html
https://en.wikipedia.org/wiki/Netfilter
在三層IPv4數據包的處理過程中,可能經過Netfilter的五個鈎子點,分別為NF_INET_PRE_ROUTING、NF_INET_LOCAL_IN、NF_INET_FORWARD、NF_INET_LOCAL_OUT、NF_INET_POST_ROUTING,在每個點都可以設置一些規則,來對數據包進行匹配檢查處理,這些規則的配置、布局和匹配流程。
Netfilter 提供了一個基本的報文攔截框架,即hook機制;
Netfilter 中定義了一個全局二維數組,來存放注冊了的處理函數。
struct list_head hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS] __read_mostly;
在每個關鍵點上,有很多已經按照優先級預先注冊了的回調函數;
每個鈎子函數最后必須向Netfilter框架返回下列幾個值其中之一
netfilter 回調函數注冊
nf_hook_ops是注冊的鈎子函數的核心結構,字段含義如下所示,一般待注冊的鈎子函數會組成一個nf_hook_ops數組,在注冊過程中調用nf_register_net_hooks將所有規則加入到指定的鈎子點;
/* list:因為在一個HOOK點有可能注冊多個鈎子函數,因此這個變量用來將某個HOOK點所注冊的所有鈎子函數組織 成一個雙向鏈表; hook:該參數是一個指向nf_hookfn類型的函數的指針,由該函數指針所指向的回調函數在該hook被激活時調用【nf_hookfn在后面做解釋】; owner:表示這個hook是屬於哪個模塊的 pf:該hook函數所處理的協議。目前我們主要處理IPv4,所以該參數總是PF_INET; hooknum:鈎子函數的掛載點,即HOOK點; priority:優先級。前面也說過,一個HOOK點可能掛載了多個鈎子函數,當Netfilter在這些HOOK點上遍歷查找所注冊的鈎子函數時,這些鈎子函數的先后執行順序便由該參數來制定。 */ struct nf_hook_ops { struct list_head list; /* User fills in from here down. */ nf_hookfn *hook; /* 鈎子函數 */ struct net_device *dev; /* 設備 */ void *priv; /* 私有數據 */ u_int8_t pf; /* 協議族 */ unsigned int hooknum; /* 鈎子點 */ /* Hooks are ordered in ascending priority. */ int priority; /* 優先級 */ };
鈎子函數原型為:

typedef unsigned int nf_hookfn(void *priv, struct sk_buff *skb, const struct nf_hook_state *state);
nf_register_net_hook為鈎子函數注冊的主流程,首先找到鈎子點函數的入口,然后根據優先級將當前注冊的鈎子函數插入到鏈表中;

int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg) { struct list_head *hook_list; struct nf_hook_entry *entry; struct nf_hook_ops *elem; /* 分配鈎子入口結構 */ entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; entry->orig_ops = reg; entry->ops = *reg; /* 找到鈎子點鏈表頭部 */ hook_list = nf_find_hook_list(net, reg); if (!hook_list) { kfree(entry); return -ENOENT; } mutex_lock(&nf_hook_mutex); /* 找到鈎子應該插入的位置 */ list_for_each_entry(elem, hook_list, list) { if (reg->priority < elem->priority) break; } /* 插入鈎子點 */ list_add_rcu(&entry->ops.list, elem->list.prev); mutex_unlock(&nf_hook_mutex); #ifdef CONFIG_NETFILTER_INGRESS if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) net_inc_ingress_queue(); #endif #ifdef HAVE_JUMP_LABEL static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]); #endif return 0; }
/* 對於nf 其鈎子函數入口形式為hooks[協議族][鈎子點],在二維數組的每個節點都對應着一個鈎子函數鏈表,內部多個nf_hook_entry通過優先級從小到大排列; */ struct netns_nf { #if defined CONFIG_PROC_FS struct proc_dir_entry *proc_netfilter; #endif const struct nf_queue_handler __rcu *queue_handler; const struct nf_logger __rcu *nf_loggers[NFPROTO_NUMPROTO]; #ifdef CONFIG_SYSCTL struct ctl_table_header *nf_log_dir_header; #endif struct list_head hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; }; enum { NFPROTO_UNSPEC = 0, NFPROTO_INET = 1, NFPROTO_IPV4 = 2, NFPROTO_ARP = 3, NFPROTO_NETDEV = 5, NFPROTO_BRIDGE = 7, NFPROTO_IPV6 = 10, NFPROTO_DECNET = 12, NFPROTO_NUMPROTO, }; //IPv4鈎子點的定義如下: enum nf_inet_hooks { NF_INET_PRE_ROUTING, NF_INET_LOCAL_IN, NF_INET_FORWARD, NF_INET_LOCAL_OUT, NF_INET_POST_ROUTING, NF_INET_NUMHOOKS };