1. 程式人生 > >第十一章 Linux包過濾防火牆-netfilter--基於Linux3.10

第十一章 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,而這裡