- 論壇徽章:
- 0
|
二、表的注冊
表的注冊由函數(shù)ipt_register_table來完成,
ipt_register_table(&packet_filter);
其參數(shù)packet_filter包含了待注冊表的各個參數(shù):
static struct ipt_table packet_filter
= { { NULL, NULL }, "filter", &initial_table.repl,
FILTER_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE };
可見,內(nèi)核中,表是以結(jié)構(gòu)struct ipt_table來表示的:
struct ipt_table
{
struct list_head list;
/* 用于構(gòu)造,維護鏈表的結(jié)構(gòu) */
char name[IPT_TABLE_MAXNAMELEN];
/* 表名,如"filter"、"nat"等,為了滿足自動模塊加載的設(shè)計,包含該表的模塊應(yīng)命名為iptable_'name'.o */
struct ipt_replace *table;
/* 表的初始化模板,初始為initial_table.repl */
unsigned int valid_hooks;
/* 位向量,表示當前表所影響的HOOK */
rwlock_t lock;
/* 讀寫鎖,初始為打開狀態(tài) */
struct ipt_table_info *private;
/* iptable的數(shù)據(jù)區(qū)*/
struct module *me;
/* 是否在模塊中定義,若否,則為NULL */
};
對照這一結(jié)構(gòu)分析,filter表的初始化為:
鏈表:{ NULL, NULL }
表名:"filter"
初始化模板:&initial_table.repl
當前表所影響的Hook:FILTER_VALID_HOOKS /*#define FILTER_VALID_HOOKS ((1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT))*/
讀寫鎖:RW_LOCK_UNLOCKED,即為打開狀態(tài)
數(shù)據(jù)區(qū): NULL
是否在模塊中定義:THIS_MODULE,見如下宏定義:
#ifndef THIS_MODULE
#ifdef MODULE
#define THIS_MODULE (&__this_module)
#else
#define THIS_MODULE (NULL)
#endif
#endif
先來看維護表的鏈表的結(jié)構(gòu):
struct list_head {
struct list_head *next, *prev;
};
很簡單,它是一個雙向鏈表。
另一個重要的東東就是表的模板和數(shù)據(jù)區(qū)。表模板定義了一個初始化用的該表的所默認的HOOK所包含的規(guī)則等信息,它被初始化成了
&initial_table.repl。而初始化的數(shù)據(jù)區(qū)struct ipt_table_info *private為空。這樣,ipt_register_table()函數(shù)會用repl成員的
內(nèi)容去填充private成員.
struct ipt_table_info是實際描述表的數(shù)據(jù)結(jié)構(gòu)(net/ipv4/netfilter/ip_tables.c):
struct ipt_table_info
{
unsigned int size;
/* 表大小 */
unsigned int number;
/* 表中的規(guī)則數(shù) */
unsigned int initial_entries;
/* 初始的規(guī)則數(shù),用于模塊計數(shù) */
unsigned int hook_entry[NF_IP_NUMHOOKS];
/* 記錄所影響的HOOK的規(guī)則入口相對于下面的entries變量的偏移量 */
unsigned int underflow[NF_IP_NUMHOOKS];
/* 與hook_entry相對應(yīng)的規(guī)則表上限偏移量,當無規(guī)則錄入時,相應(yīng)的hook_entry和underflow均為0 */
char entries[0] ____cacheline_aligned;
/* 規(guī)則表入口 */
};
再來看模板的定義,這個結(jié)構(gòu)很簡單,不過長了點:
static struct
{
struct ipt_replace repl;
struct ipt_standard entries[3];
struct ipt_error term;
} initial_table __initdata
= { { "filter", FILTER_VALID_HOOKS, 4,
sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error),
{ [NF_IP_LOCAL_IN] 0,
[NF_IP_FORWARD] sizeof(struct ipt_standard),
[NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) * 2 },
{ [NF_IP_LOCAL_IN] 0,
[NF_IP_FORWARD] sizeof(struct ipt_standard),
[NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) * 2 },
0, NULL, { } },
{
/* LOCAL_IN */
{ { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
0,
sizeof(struct ipt_entry),
sizeof(struct ipt_standard),
0, { 0, 0 }, { } },
{ { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
-NF_ACCEPT - 1 } },
/* FORWARD */
{ { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
0,
sizeof(struct ipt_entry),
sizeof(struct ipt_standard),
0, { 0, 0 }, { } },
{ { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
-NF_ACCEPT - 1 } },
/* LOCAL_OUT */
{ { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
0,
sizeof(struct ipt_entry),
sizeof(struct ipt_standard),
0, { 0, 0 }, { } },
{ { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
-NF_ACCEPT - 1 } }
},
/* ERROR */
{ { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
0,
sizeof(struct ipt_entry),
sizeof(struct ipt_error),
0, { 0, 0 }, { } },
{ { { { IPT_ALIGN(sizeof(struct ipt_error_target)), IPT_ERROR_TARGET } },
{ } },
"ERROR"
}
}
};
結(jié)構(gòu)長了點,我們先來關(guān)心注冊表時的初始值:
&initial_table.repl
這是一個struct ipt_replace結(jié)構(gòu),該結(jié)構(gòu)做為初始化模版被使用,同樣用戶通過系統(tǒng)調(diào)用更換
表時也要用到這個結(jié)構(gòu)。定義如下:
/* The argument to IPT_SO_SET_REPLACE. */
struct ipt_replace
{
/* 表名. */
char name[IPT_TABLE_MAXNAMELEN];
/* 該表所影響的Hook. */
unsigned int valid_hooks;
/* Number of entries */
unsigned int num_entries;
/* Total size of new entries */
unsigned int size;
/* 記錄所影響的HOOK的規(guī)則入口相對于下面的entries變量的偏移量 */
unsigned int hook_entry[NF_IP_NUMHOOKS];
/* 與hook_entry相對應(yīng)的規(guī)則表上限偏移量,當無規(guī)則錄入時,相應(yīng)的hook_entry和underflow均為0 */
unsigned int underflow[NF_IP_NUMHOOKS];
/* Information about old entries: */
/* Number of counters (must be equal to current number of entries). */
unsigned int num_counters;
/* The old entries' counters. */
struct ipt_counters *counters;
/* The entries (hang off end: not really an array). */
struct ipt_entry entries[0];
};
對照結(jié)構(gòu),可以分析各個成員的初始化值了:
char name[IPT_TABLE_MAXNAMELEN]="filter";
unsigned int valid_hooks=FILTER_VALID_HOOKS;
unsigned int num_entries=4;
unsigned int size=sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error);
unsigned int hook_entry[NF_IP_NUMHOOKS]={ [NF_IP_LOCAL_IN] 0,
[NF_IP_FORWARD] sizeof(struct ipt_standard),
[NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) * 2 };
unsigned int underflow={ [NF_IP_LOCAL_IN] 0,
[NF_IP_FORWARD] sizeof(struct ipt_standard),
[NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) * 2 };
unsigned int num_counters=0;
struct ipt_counters *counters=NULL;
struct ipt_entry entries[0]={ };
了解了這些結(jié)構(gòu)后,再來看表的注冊函數(shù):
int ipt_register_table(struct ipt_table *table)
{
int ret;
struct ipt_table_info *newinfo;
static struct ipt_table_info bootstrap
= { 0, 0, 0, { 0 }, { 0 }, { } };
/*宏MOD_INC_USE_COUNT用于模塊計數(shù)器累加,主要是為了防止模塊異常刪除,對應(yīng)的
宏MOD_DEC_USE_COUNT就是累減了*/
MOD_INC_USE_COUNT;
/*為每個CPU分配規(guī)則空間*/
newinfo = vmalloc(sizeof(struct ipt_table_info)
+ SMP_ALIGN(table->table->size) * smp_num_cpus);
/*分配失敗*/
if (!newinfo) {
ret = -ENOMEM;
MOD_DEC_USE_COUNT;
return ret;
}
/*將規(guī)則項拷貝到新表項的第一個cpu空間里面*/
memcpy(newinfo->entries, table->table->entries, table->table->size);
/*translate_table函數(shù)將newinfo表示的table的各個規(guī)則進行邊界檢查,然后對于newinfo所指的
ipt_talbe_info結(jié)構(gòu)中的hook_entries和underflows賦予正確的值,最后將表項向其他cpu拷貝*/
ret = translate_table(table->name, table->valid_hooks,
newinfo, table->table->size,
table->table->num_entries,
table->table->hook_entry,
table->table->underflow);
if (ret != 0) {
vfree(newinfo);
MOD_DEC_USE_COUNT;
return ret;
}
ret = down_interruptible(&ipt_mutex);
if (ret != 0) {
vfree(newinfo);
MOD_DEC_USE_COUNT;
return ret;
}
/* 如果注冊的table已經(jīng)存在,釋放空間 并且遞減模塊計數(shù) */
if (list_named_find(&ipt_tables, table->name)) {
ret = -EEXIST;
goto free_unlock;
}
/* 替換table項. */
table->private = &bootstrap;
if (!replace_table(table, 0, newinfo, &ret))
goto free_unlock;
duprintf("table->private->number = %u\n",
table->private->number);
/* 保存初始規(guī)則計數(shù)器 */
table->private->initial_entries = table->private->number;
table->lock = RW_LOCK_UNLOCKED;
/*將表添加進鏈表*/
list_prepend(&ipt_tables, table);
unlock:
up(&ipt_mutex);
return ret;
free_unlock:
vfree(newinfo);
MOD_DEC_USE_COUNT;
goto unlock;
}
呵呵,初次看table的注冊,有點頭大,因為它不光是netfilter,還涉及到linux內(nèi)核中的內(nèi)存管理、
信號量設(shè)置等等,不過其實注冊也就完成兩件事:初始化表,將表添加進表的鏈表。 |
|