全部博文(403)
分类: 系统运维
2007-03-30 14:56:54
2.4的结构比2.2从新定义了许多数据结构。2.2和2.4防火墙都采用模块机制,基本原理是一致的。
Linux2.4内核的防火墙框架netfilter在IP层 内提供了五个插入点:NF_IP_PRE_ROUTING,NF_IP_LOCAL_IN,NF_IP_FORWARD,NF_IP_LOCAL_OUT,NF_IP_POST_ROUTING,分别对应IP层的五个不同位置。这样,在理论上写用户自己的内核模块便可以选择更合适的切入点。
知道netfilter内置的防火墙函数模块的接口,方便自己在编写自己的防火墙函数模块时清楚的知道内核如何调用自己写的函数模块。
在IP层代码中,有一些带有NF_HOOK宏的函数,如在IP接受函数ip_rcv中有:
NF_HOOK(PF_INET,NF_IP_PRE_ROUTING,skb,dev,NULL,ip_rcv_finish);
这是在钩子点NF_IP_PRE_ROUTING定义的防火墙模块函数,其中NF_HOOK宏的定义提炼如下:
#ifdef CONFIG_NETFILTER_DEBUG
#define NF_HOOK nf_hook_show
#else
#define NF_HOOK(pf,hook,skb,indev,outdev,okfn)(list_empty(&nf_hooks[(pf)][(hook)])?(okfn)(skb):nf_hook_show((pf),(hook),(skb),(indev),(out_dev),(okfn)))
#endif
如果在编译内核时没有选择配置netfilter,则在IP接受函数中NF_HOOK宏就相当于调用一个参数,在此即执行ip_rcv_finish;如果选择配置netfilter,则进入HOOK点,执行通过nf_register_hook()登记函数。NF_HOOK宏的参数分别为:
参数pf,协议族名,netfilter构架同样可以用于IP层之外。所以这个变量同样可以有诸如PF_INET6,PF_DECnet等名字。
参数hook,钩子点的名字,对于IP层就是取上面的五个值。
参数skb,指向结构体struct sk_buff的指针。
参数indev,进来的设备,以struct net_device结构表示
参数outdev,出去的设备,以struct net_device结构表示
以上的这五个参数将传到用nf_register_hook登记的处理函数中。
这些点是已经在内核中定义好的,除非用户是这部分内核代码的维护者,否则无权增加或修改。在此检查点进行的处理,可由用户指定。像packet filter,NAT,connection track 这些功能,也是以这样方式提供。
1.登记防火墙程序到内核
登记的防火墙程序只要载入内核,在网络层内核就会去自动调用它,前提是自己必须正确的把自己写的防火墙程序登记到内核中。
内核中提供了防火墙的登记和卸载函数,分别为nf_register_hook和nf_unregister_hook,这些函数的源代码位于/linux/net/core/netfilter.c中
1)关键的数据结构
防火墙模块就登记在由nf_hook_ops数据结构所形成的单向链表中。自己编写的防火墙函数模块,实际上就是生成一个struct nf_hook_ops结构的实例,并用nf_register_hook将其登记在HOOK上。
结构体nf_hook_ops
struct nf_hook_ops
{
/*结构体list_head用于形成链表,是Linux2.4内核中一个通用的结构体*/
struct list_head list;
/*指向用户定义的在该钩子点对应的处理函数*/
nf_hookfn *hook;
/*协议族*/
int pf;
/*核心指定的那五个钩子点之一*/
int hooknum;
/*该钩子点定义了函数的优先级*/
int priority;
};
最后一个域priority是很重要的,如果钩子点定义了很多处理函数,处理的先后顺序就根据它来确定.在netfilter_ipv4.h中用一个枚举类型指定了内置处理函数的优先级,
enum nf_ip_hook_priorities{
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK = -200,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_LAST_ = INT_MAX,
};
2)登记函数
函数功能说明:内核用来登记防火墙模块的函数,登记成功返回0;登记失败返回非0。呵呵
参数说明:reg,指向结构体nf_hook_ops的指针。具体分析如下:
int nf_register_hook(struct nf_hook_ops *ops)
{
struct list_head * i ;
/*建立锁,在一段时间里只润许一个用户登记*/
br_write_lock_bh(BR_NETPROTO_LOCK);
/*遍历整个防火墙链表,检查优先级。如果登记的函数模块的优先级小于内核中已经存在的函数模块的优先级,则不把该函数添加到链表中*/
for(i = nf_hooks[reg->pf][reg->hooknum].next; i!=&nf_hooks[reg->pf][reg->hooknum];i = i->next)
{
if(reg->priority < ((struct nf_hook_ops *)i)->priority)
break;
}
/*将防火墙模块添加入链表*/
list_add(®->list,i->prev);
br_write_unlock_bh(BR_NETPROTO_LOCK);
return 0;
}
3)卸载函数
该函数是nf_register_hook的反调用。
函数参数说明:参数和nf_register_hook相同。
Void nf_unregister_hook(struct nf_hook_ops *reg)
{
/*加锁保证只能有一个用户在一段时间中卸载*/
br_write_lock_bh(BR_NETPROTO_LOCK)
/*从链表中取出这个防火墙模块*/
list_del(®->list);
br_write_unlock_bh(BR_NETPROTO_LOCK);
}
2.防火墙模块的设计
1)防火墙内置函数模块简介
在netfilter框架的五个钩子点都定义了内置的函数模块,都定义为NF_HOOK宏的形式。
在IP层接受包的函数ip_rcv()中,调用了定义在钩子点NF_IP_PRE_ROUTING的处理函数:NF_HOOK(PF_INET,NF_IP_PRE_ROUTING,skb,dev,NULL,ip_rev_finish);
在IP层向上层协议栈传递的函数ip_local_deliver()中,调用了定义在钩子点NF_IP_LOCAL_IN上的处理函数:
NF_HOOK(PF_INET,NF_IP_LOCAL_IN,skb,skb->dev,NULL,ip_local_deliver_finish);
在IP层转发函数ip_forward()中,调用了定义在钩子点NF_IP_FORWARD的处理函数:
NF_HOOK(PF_INET,NF_IP_FORWARD,skb,skb->dev,dev2,ip_forward_finish);
在IP层产生的数据包有直接送去本地进程排队处理的,也有送往路由处理
函数进行处理的。在路由处理函数处理之前,调用了定义在钩子点
NF_IP_LOCAL_OUT上的处理函数。当路由处理函数处理完毕后,又将调
用定义在钩子点NF_IP_POST_ROUTING上的函数。下面简要分析:
在ip_build_and_send_pkt()函数中调用下面定义的宏:
NF_HOOK(PF_INET,NF_IP_LOCAL_OUT,skb,NULL,rt->u.dst.dev,output_maybe_reroute);
在ip_finish_output()函数中调用下面的宏定义:
NF_HOOK(PF_INET,NF_IP_POST_ROUTING,skb,NULL,dev,ip_finish_output2);
在 ip_mc_output()函数中调用下面定义的宏:
NF_HOOK(PF_INET,NF_IP_POST_ROUTING,newskb,NULL,newskb->dev,ip_dev_loopback_xmit);
在ip_queue_xmit()函数中调用下面的宏定义:
NF_HOOK(PF_INET,NF_IP_LOCAL_OUT,skb,NULL,rt->u.dst.dev,ip_queue_xmit2);
2)防火墙程序例子
(一个简单的入侵检测功能,能检查land,winuke以及特殊扫描nmap。此代
码只能在。2.4内核下编译。
gcc -O –c Wall user_firewall.c
user_firewall.c
#ifndef __KERNEL__
#define __KERNEL__ /*按照内核模块编译*/
#endif
#ifndef MODULE
#define MODULE /*按设备驱动程序模块编译*/
#endif
/*最基本的内核模块头文件*/
#include
#include
#include
#include
#include
#include
#include
#include
static unsigned int user_firewall(unsigned int hooknum ,struct sk_buff **skb , const struct net_device in , const struct net_device out ,int (*okfn)(struct sk_buff *))
{
struct iphdr *iph ;
struct tcphdr *tcph;
struct udphdr *udph;
__u32 sip;
__u32 dip;
__u16 sport;
__u16 dport;
/*取出IP头,源IP地址.目的IP地址*/
iph=(*skb)->nh.iph;
sip=iph->saddr;
dip=iph->daddr;
/*检查IP头长度*/
if(iph->ihl != 5) {
printk(“IP packet with packet from %d.%d.%d.%d to %d.%d.%d.%d\n”,NIPQUAD(sip),NIPQUAD(dip));
}
/*如果是TCP协议*/
if(iph->protocol == 6){
tcph = (struct tcphdr *)((__u32 *)iph + iph->ihl);
sport = tcph->source;
dport = tcph->dest;
/*防止land攻击*/
if((tcph->syn)&&(sport==dport)&&(sip==dip)){
printk(“maybe land attack\n”);
}
/*防止winnuke攻击*/
if(ntohs(tcph->dest) == 139&&tcph->urg){
printk(“maybe winnuke a from %d.%d.%d.%d to %d.%d.%d.%d\n”,NIPQUAD(sip),NIPQUAD(dip));
}
if(tcph->ece&&tcph->cwr){
printk(“queso from %d.%d.%d.%d to %d.%d.%d.%d\n” ,NIPQUAD(sip),NIPQUAD(dip));
/*防止恶意扫描*/
if((tcph->fin)&&(tcph->syn)&&(! tcph->rst)&&(! tcph->psh)&&(! tcph->ack)&&(! tcph->urg)){
printk(“SF_scan from %d.%d.%d.%d to %d.%d.%d.%d\n” ,NIPQUAD(sip),NIPQUAD(dip));
}
if((! tcph->fin)&&(! tcph->syn)&&(! tcph->rst)&&(! Tcph->psh)&&(! Tcph->ack)&&(! Tcph->urg)){
printk(“NULL_scan from %d.%d.%d.%d to %d.%d.%d.%d\n”,NIPQUAD(sip),NIPQUAD(dip));
}
if(tcph->fin && tcph->syn && tcph->rst && tcph->psh && tcph->ack && tcph->urg){
printk(“FULL_Xmas_scan from %d.%d.%d.%d to %d.%d.%d.%d \n”,NIPQUAD(sip),NIPQUAD(dip));
}
if((tcph->fin)&&(! tcph->syn)&&((! tcph->rst)&&(tcph->psh)&&(! tcph->ack)&&(tcph->urg)){
printk(“XMAS_Scan(FPU)from %d.%d.%d.%d to %d.%d.%d.%d\n”, NIPQUAD(sip),NIPQUAD(dip));
}
/*如果协议类型为UDP*/
else if(iph->protocol == 17){
udph = (struct udphdr *)((__u32 *)iph + iph_ihl);
sport = udph->source;
dport = udph->dest;
/* play udp packet here */
}
/*如果协议类型为ICMP*/
else if(iph->protocol == 1){
}
/*如果协议类型为IGMP*/
else if(iph->protocol == 2){
printk(“igmp packet from %d.%d.%d.%d to %d.%d.%d.%d\n”, NIPQUAD(sip),NIPQUAD(dip));
}
/*如果协议类型未知*/
else{
printk(“unknown protocol %d packet from %d.%d.%d.%d to %d.%d.%d.%d\n”, iph->protocol,NIPQUAD(sip),NIPQUAD(dip));
}
//*接受所有的包*/
return NF_ACCEPT;
}
接着,初始化登记防火墙用到的关键数据结构,上面的处理函数定义在钩子点NF_IP_PRE_ROUTING上:
static struct nf_hook_ops iplimitfilter =
{
{NULL,NULL},
user_firewall,
PF_INET,
NF_IP_PRE_ROUTING,
NF_IP_PRI_FILTER-1
};
定义模块初始化函数和模块卸载函数.当用户输入命令insmod user_firewall.o 时,将调用这个初始化函数.当用户输入rmmod user_firewall时,将调用下面的定义卸载函数.
/*模块初始化函数*/
int init_module(void)
{
return nf_register_hook(&iplimitfilter);
}
/*模块卸载函数*/
void cleanup_module(void){
nf_unregister_hook(&iplimitfilter);
}