分类: LINUX
2011-01-05 15:06:34
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;
/* Initializing verdict to NF_DROP keeps gcc happy. */
unsigned int verdict = NF_DROP;
const char *indev, *outdev;
void *table_base;
struct ipt_entry *e, *back;
struct xt_table_info *private;
/* Initialization */
ip = ip_hdr(skb);//获取ip头
datalen = skb->len - ip->ihl * 4;
indev = in ? in->name : nulldevname;//获得输入设备
outdev = out ? out->name : nulldevname;//获得输出设备
/* We handle fragments by dealing with the first fragment as
* if it was a normal packet. All other fragments are treated
* normally, except that they will NEVER match rules that ask
* things we don't know, ie. tcp syn flag or ports). If the
* rule is also a fragment-specific rule, non-fragments won't
* match it. */
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相对应的所有规则
/* For return from builtin chain */
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)
/* The packet is traced: log it */
if (unlikely(skb->nf_trace))
trace_packet(skb, hook, in, out,
table->name, private, e);
#endif
/* Standard target? */
if (!t->u.kernel.target->target) {//判断是否是标准的target
int v;
v = ((struct ipt_standard_target *)t)->verdict;//获得相应的target值
if (v < 0) {
/* Pop from stack? */
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)) {
/* Save old back ptr in next entry */
struct ipt_entry *next
= (void *)e + e->next_offset;
next->comefrom
= (void *)back - table_base;
/* set back pointer to next entry */
back = next;
}
e = get_entry(table_base, v);
} else {//不是标准的target,也就是说match定义了自己的target
/* Targets which reenter must return
abs. verdicts */
#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
/* Target might have changed stuff. */
ip = ip_hdr(skb);
datalen = skb->len - ip->ihl * 4;
if (verdict == IPT_CONTINUE)
e = (void *)e + e->next_offset;
else
/* Verdict */
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;
}
//接口是否匹配
/* Look for ifname matches; this should unroll nicely. */
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;
}
/* Check specific protocol */
//协议是否匹配
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 we have a fragment rule but the packet is not a fragment
* then we return zero */
//分片是否匹配
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;
}
chinaunix网友2011-01-06 14:55:11
很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com