分类: LINUX
2013-03-06 22:57:46
FILTER表顾名思义,用来对数据包进行做过滤,它是Netfilter中的核心表,提供了Netfilter防火墙的主要功能。
本章将以以ipv4的filter表为例,从FILTER表的建立,注册以及hook操作对表的匹配过程对FILTER进行详细的分析。
首先是iptable_filter模块初始化时注册一个packet_filter表,及相应的hook操作
static struct xt_table packet_filter =
{
.name =
"filter",//表名
.valid_hooks =
FILTER_VALID_HOOKS,//有效的hooks,#define FILTER_VALID_HOOKS ((1
<<
//NF_IP_LOCAL_IN)
| (1 << NF_IP_FORWARD) |
//
(1 << NF_IP_LOCAL_OUT))
.lock =
RW_LOCK_UNLOCKED, //读写锁
.me =
THIS_MODULE, //模块
.af =
AF_INET, //所属协议族
};
然后初始化相应的hook操作,由于FILTER表只在三个挂载点注册了函数,故在ipt_ops中我们设置了三个hook操作。
static struct nf_hook_ops ipt_ops[] = {
{
.hook =
ipt_hook,//具体的操作
.owner =
THIS_MODULE,//模块
.pf =
PF_INET, //协议族
.hooknum =
NF_IP_LOCAL_IN, //hook的标号
.priority =
NF_IP_PRI_FILTER,//操作的优先级
},
{
.hook =
ipt_hook,
.owner =
THIS_MODULE,
.pf =
PF_INET,
.hooknum =
NF_IP_FORWARD,
.priority =
NF_IP_PRI_FILTER,
},
{
.hook =
ipt_local_out_hook,
.owner =
THIS_MODULE,
.pf =
PF_INET,
.hooknum =
NF_IP_LOCAL_OUT,
.priority =
NF_IP_PRI_FILTER,
},
};
表和hook操作初始化完成后,调用相应的注册函数注册到相应的全局标量当中去,这个注册函数分别为:
ipt_register_table和nf_register_hooks,其中ipt_register_table会调用xt_register_table,完成对表的注册。
表注册完成后是还不够的,这相当于我们有了仓库,还没工具,各种各样的target和match就是我们需要的工具,各种各样的target和match注册后,一个防火墙的过滤报文功能就可以实现了。
相应的注册和初始化工作完成后,当有报文进入Netfilter时,我们就可以对报文进行处理了。图3.3中,没有详细的列出hook操作如何对报文进行处理,在这里将对图3.3中的最后一步,以IPV4的filter表为例进行扩展。挂载点被触发,调用hook操作,hook操作会根据对应的规则来对报文进行处理。从hook操作开始后的函数调用流程如下:
图4.1 FILTER表对数据包处理流程
unsigned int
ipt_do_table(struct sk_buff *skb,
unsigned
int hook,
const
struct net_device *in,
const
struct net_device *out,
struct
xt_table *table)
{
static const
char nulldevname[IFNAMSIZ]
__attribute__((aligned(sizeof(long))));
u_int16_t
offset;
struct iphdr
*ip;
u_int16_t
datalen;
bool hotdrop =
false;
unsigned int
verdict = NF_DROP;
const char
*indev, *outdev;
void
*table_base;
struct
ipt_entry *e, *back;
struct
xt_table_info *private;
ip =
ip_hdr(skb);//获取ip头
datalen =
skb->len - ip->ihl * 4;
indev = in ?
in->name : nulldevname;//获得输入设备
outdev = out ?
out->name : nulldevname;//获得输出设备
offset =
ntohs(ip->frag_off) & IP_OFFSET;
read_lock_bh(&table->lock);
IP_NF_ASSERT(table->valid_hooks
& (1 << hook));//检验hook是否有效
private =
table->private;//获取table的数据区
table_base =
(void *)private->entries[smp_processor_id()];//获取相应cpu上table的所有规则, //即match,target
e =
get_entry(table_base, private->hook_entry[hook]);//获取特定hook相对应的所有规则
back =
get_entry(table_base, private->underflow[hook]);
do
{
IP_NF_ASSERT(e);
IP_NF_ASSERT(back);
// 标准的匹配,详细分析见下面
if
(ip_packet_match(ip, indev, outdev, &e->ip, offset))
{
// 代码分析
struct
ipt_entry_target *t;
if
(IPT_MATCH_ITERATE(e, do_match,//匹配所有的match
skb,
in, out,
offset,
&hotdrop) != 0)
goto
no_match;
ADD_COUNTER(e->counters,
ntohs(ip->tot_len), 1);
t
= ipt_get_target(e);//获得match对应的target
IP_NF_ASSERT(t->u.kernel.target);
#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) ||
\
defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
if
(unlikely(skb->nf_trace))
trace_packet(skb,
hook, in, out,
table->name,
private, e);
#endif
if
(!t->u.kernel.target->target) {//判断是否是标准的target
int
v;
v
= ((struct ipt_standard_target *)t)->verdict;//获得相应的target值
if
(v < 0) {
if
(v != IPT_RETURN) {
verdict
= (unsigned)(-v) - 1;
break;
}
e
= back;
back
= get_entry(table_base,
back->comefrom);
continue;
}
if
(table_base + v != (void *)e + e->next_offset
&&
!(e->ip.flags & IPT_F_GOTO)) {
struct
ipt_entry *next
=
(void *)e + e->next_offset;
next->comefrom
=
(void *)back - table_base;
back
= next;
}
e
= get_entry(table_base, v);
}
else {//不是标准的target,也就是说match定义了自己的target
#ifdef CONFIG_NETFILTER_DEBUG
((struct
ipt_entry *)table_base)->comefrom
=
0xeeeeeeec;
#endif
verdict
= t->u.kernel.target->target(skb,//获得target标准的target
in,
out,
hook,
t->u.kernel.target,
t->data);
#ifdef CONFIG_NETFILTER_DEBUG
if
(((struct ipt_entry *)table_base)->comefrom
!=
0xeeeeeeec
&&
verdict == IPT_CONTINUE) {
printk("Target
%s reentered!\n",
t->u.kernel.target->name);
verdict
= NF_DROP;
}
((struct
ipt_entry *)table_base)->comefrom
=
0x57acc001;
#endif
ip
= ip_hdr(skb);
datalen
= skb->len - ip->ihl * 4;
if
(verdict == IPT_CONTINUE)
e
= (void *)e + e->next_offset;
else
break;
}
}
else {
no_match:
e
= (void *)e + e->next_offset;//继续下个匹配
}
} while
(!hotdrop);
read_unlock_bh(&table->lock);
#ifdef DEBUG_ALLOW_ALL
return
NF_ACCEPT;
#else
if
(hotdrop)
return
NF_DROP;
else return
verdict;
#endif
}
static inline int
ip_packet_match(const struct iphdr *ip,
const
char *indev,
const
char *outdev,
const
struct ipt_ip *ipinfo,
int
isfrag)
{
size_t
i;
unsigned long
ret;
#define FWINV(bool,invflg)
((bool) ^ !!(ipinfo->invflags & invflg))//bool和invflg不同时为真//两个!为了使后面的表达式只能为1或0
//判断源地址和目的地址是否相等
if (FWINV((ip->saddr&ipinfo->smsk.s_addr) !=
ipinfo->src.s_addr,
IPT_INV_SRCIP)
||
FWINV((ip->daddr&ipinfo->dmsk.s_addr) !=
ipinfo->dst.s_addr,
IPT_INV_DSTIP))
{
dprintf("Source
or dest mismatch.\n");
dprintf("SRC:
%u.%u.%u.%u. Mask: %u.%u.%u.%u. Target:
%u.%u.%u.%u.%s\n",
NIPQUAD(ip->saddr),
NIPQUAD(ipinfo->smsk.s_addr),
NIPQUAD(ipinfo->src.s_addr),
ipinfo->invflags
& IPT_INV_SRCIP ? " (INV)" : "");
dprintf("DST:
%u.%u.%u.%u Mask: %u.%u.%u.%u Target:
%u.%u.%u.%u.%s\n",
NIPQUAD(ip->daddr),
NIPQUAD(ipinfo->dmsk.s_addr),
NIPQUAD(ipinfo->dst.s_addr),
ipinfo->invflags
& IPT_INV_DSTIP ? " (INV)" : "");
return
0;
}
//接口是否匹配
for (i = 0, ret
= 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
ret
|= (((const unsigned long *)indev)[i]
^
((const unsigned long *)ipinfo->iniface)[i])
&
((const unsigned long *)ipinfo->iniface_mask)[i];
}
if (FWINV(ret
!= 0, IPT_INV_VIA_IN)) {
dprintf("VIA
in mismatch (%s vs %s).%s\n",
indev,
ipinfo->iniface,
ipinfo->invflags&IPT_INV_VIA_IN
?" (INV)":"");
return
0;
}
for (i = 0, ret
= 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
ret
|= (((const unsigned long *)outdev)[i]
^
((const unsigned long *)ipinfo->outiface)[i])
&
((const unsigned long *)ipinfo->outiface_mask)[i];
}
if (FWINV(ret
!= 0, IPT_INV_VIA_OUT)) {
dprintf("VIA
out mismatch (%s vs %s).%s\n",
outdev,
ipinfo->outiface,
ipinfo->invflags&IPT_INV_VIA_OUT
?" (INV)":"");
return
0;
}
//协议是否匹配
if
(ipinfo->proto
&&
FWINV(ip->protocol != ipinfo->proto, IPT_INV_PROTO))
{
dprintf("Packet
protocol %hi does not match %hi.%s\n",
ip->protocol,
ipinfo->proto,
ipinfo->invflags&IPT_INV_PROTO
? " (INV)":"");
return
0;
}
//分片是否匹配
if
(FWINV((ipinfo->flags&IPT_F_FRAG) && !isfrag, IPT_INV_FRAG))
{
dprintf("Fragment
rule but not fragment.%s\n",
ipinfo->invflags
& IPT_INV_FRAG ? " (INV)" : "");
return
0;
}
return
1;
}