第十一章 Linux包過濾防火牆-netfilter--基於Linux3.10
圖11.1.2 五個hook點及各點的表
在第一章的時候,對module_init初始化的函式被跳過了,這裡先看看和防火牆的初始化工作吧。以下檔案目錄字首均是/net/ipv4。
./netfilter.c:206:module_init(ipv4_netfilter_init); ./netfilter/ipt_ah.c:90:module_init(ah_mt_init); ./netfilter/iptable_raw.c:88:module_init(iptable_raw_init); ./netfilter/iptable_security.c:109:module_init(iptable_security_init); ./netfilter/arp_tables.c:1913:module_init(arp_tables_init); ./netfilter/iptable_mangle.c:147:module_init(iptable_mangle_init); ./netfilter/ip_tables.c:2269:module_init(ip_tables_init); ./netfilter/iptable_nat.c:333:module_init(iptable_nat_init); ./netfilter/iptable_filter.c:109:module_init(iptable_filter_init); ./netfilter/nf_defrag_ipv4.c:125:module_init(nf_defrag_init); ./netfilter/ipt_rpfilter.c:146:module_init(rpfilter_mt_init); ./netfilter/arptable_filter.c:90:module_init(arptable_filter_init);
ipv4_netfilter_init用於為INET協議族中的協議初始化netfilter,netfilter支援internet的協議型別有以下幾種:
NFPROTO_UNSPEC = 0,
NFPROTO_IPV4 = 2,
NFPROTO_ARP = 3,
NFPROTO_BRIDGE = 7,
NFPROTO_IPV6 = 10,
NFPROTO_DECNET = 12,
不同的協議型別的netfilter的具體細節並不一樣,這裡僅以ipv4協議對netfilter實現進行追蹤,所以本章接下來的內容不加說明則其屬於ipv4的範疇。
ipv4_netfilter_init:該函式註冊internet協議族的netfilter,其引數nf_ip_afinfo的afinfo就是address family information縮寫,該函式就是將nf_ip_afinfo結構體掛接到nf_afinfo的陣列上去,nf_afinfo陣列定義於同名檔案的開始處。
const struct nf_afinfo __rcu *nf_afinfo[NFPROTO_NUMPROTO] __read_mostly;
nf_ip_afinfo 結構體定義如下:
static const struct nf_afinfo nf_ip_afinfo = { .family = AF_INET, .checksum = nf_ip_checksum, .checksum_partial= nf_ip_checksum_partial, .route = nf_ip_route, .saveroute = nf_ip_saveroute, .reroute = nf_ip_reroute, .route_key_size= sizeof(struct ip_rt_info), };
上述註冊的函式是netfilter對ip層資料包的處理函式,checksum是cpu計算ip頭校驗和驗證,checksum_partial是因為現在有些網絡卡自帶校驗和計算,它們可以解決cpu的資源,後面的三個都是用來路由的,後面遇到再看。
ah_mt_init:註冊防火牆表中規則項的match函式,該函式用於頭資訊匹配判斷。
iptable_security_init:
iptable_raw_init:
iptable_mangle_init:
iptable_nat_init:
iptable_filter_init:
這五張是核心防火牆使用的表,這裡註冊了這幾張表,這幾張表將構成一個鏈式結,表和鏈的拓撲結構可以參看11.2.1。由於這幾張表的初始化過程差別不大,並且filter常用,所以這裡就只看filter表的初始化了。
static int __init iptable_filter_init(void)
{
ret = register_pernet_subsys(&iptable_filter_net_ops); /* Register hooks */
filter_ops = xt_hook_link(&packet_filter, iptable_filter_hook);
}
又見register_pernet_subsys,iptable_filter_net_ops會被新增first_device_ops連結串列上,該函式註冊一個網路名稱空間子系統。
int register_pernet_subsys(struct pernet_operations *ops)
{
int error;
mutex_lock(&net_mutex);
error = register_pernet_operations(first_device, ops);
mutex_unlock(&net_mutex);
return error;
}
如果iptable_filter_net_ops有init成員,則init成員會被呼叫。
static struct pernet_operations iptable_filter_net_ops = {
.init = iptable_filter_net_init,
.exit = iptable_filter_net_exit,
};
回撥函式iptable_filter_net_init函式如下
58 static int __net_init iptable_filter_net_init(struct net *net)
59 {
60 struct ipt_replace *repl;
61
62 repl = ipt_alloc_initial_table(&packet_filter);
63 if (repl == NULL)
64 return -ENOMEM;
65 /* Entry 1 is the FORWARD hook */
66 ((struct ipt_standard *)repl->entries)[1].target.verdict =
67 forward ? -NF_ACCEPT - 1 : -NF_DROP - 1;
68
69 net->ipv4.iptable_filter =
70 ipt_register_table(net, &packet_filter, repl);
71 kfree(repl);
72 return PTR_RET(net->ipv4.iptable_filter);
73 }
62行packet_filter定義如下,呼叫xt_alloc_initial_table巨集進行初始化。
#define FILTER_VALID_HOOKS ((1 << NF_INET_LOCAL_IN) | \
(1 << NF_INET_FORWARD) | \
(1 << NF_INET_LOCAL_OUT))
static const struct xt_table packet_filter = {
.name = "filter",
.valid_hooks = FILTER_VALID_HOOKS,
.me = THIS_MODULE,
.af = NFPROTO_IPV4,
.priority = NF_IP_PRI_FILTER,
};
62的函式xt_alloc_initial_table用於建立表的規則鏈,這個函式的具體實現比較複雜,先看完整個函式的流程再回過頭來細細分析該函式的實現細節。
66行將FORWARD的hook項設定為接受,forward預設值是true,當然也可以在載入模組時動態改變該選擇為flase,這樣就不支援非本機資料包的轉發功能了。
69向核心註冊filter表。
再回到62行,這裡該函式內的巨集經過展開處理了,以函式的形式展現在這裡了,該“函式”的引數是上面的packe_filer,即filter表結構。
void *ipt_alloc_initial_table(const struct xt_table *info)
{
unsigned int hook_mask = info->valid_hooks; //LOCAL_IN、FORWARD、LOCAL_OUT
unsigned int nhooks = hweight32(hook_mask); //這裡得到3,上面hookmask對應三個hook點。
unsigned int bytes = 0, hooknum = 0, i = 0;
看到函式的最後,知道返回值是tbl,而這裡的結構體內嵌的三個結構體是tbl的組成,三個結構體的資料結構拓撲圖如圖11.1.3。
struct {
struct ipt_replace repl;
struct ipt _standard entries[nhooks];
struct ipt_error term;
} *tbl = kzalloc(sizeof(*tbl), GFP_KERNEL);
if (tbl == NULL)
return NULL;
strncpy(tbl->repl.name, info->name, sizeof(tbl->repl.name));
tbl->term = (struct ipt_error)IPT_ERROR_INIT;
tbl->repl.valid_hooks = hook_mask;
tbl->repl.num_entries = nhooks + 1;
tbl->repl.size = nhooks * sizeof(struct ipt_standard) + sizeof(struct ipt_error);
for (; hook_mask != 0; hook_mask >>= 1, ++hooknum) {
if (!(hook_mask & 1))
continue;
tbl->repl.hook_entry[hooknum] = bytes;
tbl->repl.underflow[hooknum] = bytes;
tbl->entries[i++] = (struct ipt_standard) IPT_STANDARD_INIT(NF_ACCEPT);
bytes += sizeof(struct ipt_standard);
}
return tbl;
}
看到函式的最後,知道返回值是tbl,而這裡