腾讯客服人工热线-中国游戏互动中心
![netfilter](/uploads/image/0012.jpg)
2023年4月4日发(作者:星际争霸1 08完整版)
netfilterhook函数
HOOK函数注册
上图是netfilter中注册的部分hook函数。这些hook函数是通过nf_register_hook注册到⼆维数组nf_hooks中的。
enumnf_inet_hooks{
NF_INET_PRE_ROUTING,
NF_INET_LOCAL_IN,
NF_INET_FORWARD,
NF_INET_LOCAL_OUT,
NF_INET_POST_ROUTING,
NF_INET_NUMHOOKS
};
enum{
NFPROTO_UNSPEC=0,
NFPROTO_INET=1,
NFPROTO_IPV4=2,
NFPROTO_ARP=3,
NFPROTO_BRIDGE=7,
NFPROTO_IPV6=10,
NFPROTO_DECNET=12,
NFPROTO_NUMPROTO,
};
externstructlist_headnf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
数组的第⼀维即上图的第⼀列,表⽰不同的地址族,上图只画出了IPV4,ARP和BRIDGE相关的。数组的第⼆维即上图的第⼀排,表⽰不同模块
的hook函数,对于不同的地址族,使⽤的宏定义不同,但都是从0开始。拿最常见的IPV4地址族来说,五个hook函数安置在数据包的必经之路,
不管数据是到本机的,还是需要本机转发的,还是从本机发出去的,都会覆盖到。
NF_INET_PRE_ROUTING和NF_INET_LOCAL_OUT相当于netfilter的⼊⼝点,即不管哪种情况,数据包肯定会从这俩hook点之⼀进⼊。
NF_INET_LOCAL_IN和NF_INET_POST_ROUTING相当于netfilter的出⼝点,即不管哪种情况,数据包肯定会从这俩hook点之⼀出去。
hook函数的执⾏顺序:同⼀个hook点上的从优先级值最⼩的开始执⾏,即上图从上往下执⾏,不同的hook点互不影响。
对于IPV4地址族来说,包含了多个模块的hook函数:
filter,raw,mangle,security:这四个模块相对独⽴,不依赖其他模块。
conntrack:连接跟踪模块是状态防⽕墙和nat的基础。
nat:基于conntrack对于数据包的跟踪,对数据流的收包查找nat规则进⾏nat转换,此流的后续数据包直接查找conntrack的ct信息做nat转换。
ipvs:四层负载均衡模块
HOOK函数执⾏
hook函数的执⾏是通过调⽤函数NF_HOOK来实现的,
其第⼀个参数pf指定了地址族,第⼆个参数指定了在哪个hook点,第三个参数skb是数据包,第六个参数是数据包通过hook点上hook函数的检
查后,如果结果是accept⽽执⾏的函数。
staticinlineint
NF_HOOK(uint8_tpf,unsignedinthook,structsk_buff*skb,
structnet_device*in,structnet_device*out,
int(*okfn)(structsk_buff*))
{
returnNF_HOOK_THRESH(pf,hook,skb,in,out,okfn,INT_MIN);
}
staticinlineint
NF_HOOK_THRESH(uint8_tpf,unsignedinthook,structsk_buff*skb,
structnet_device*in,structnet_device*out,
int(*okfn)(structsk_buff*),intthresh)
{
intret=nf_hook_thresh(pf,hook,skb,in,out,okfn,thresh);
//执⾏完hook函数后,返回值为1才会执⾏okfn。
if(ret==1)
ret=okfn(skb);
returnret;
}
staticinlineintnf_hook_thresh(u_int8_tpf,unsignedinthook,
structsk_buff*skb,
structnet_device*indev,
structnet_device*outdev,
int(*okfn)(structsk_buff*),intthresh)
{
//地址族pf对应的hook点上注册了hook函数,如果没注册直接返回1
if(nf_hooks_active(pf,hook))
returnnf_hook_slow(pf,hook,skb,indev,outdev,okfn,thresh);
return1;
}
/*Returns1ifokfn()needstobeexecutedbythecaller,
*-EPERMforNF_DROP,0otherwise.*/
intnf_hook_slow(u_int8_tpf,unsignedinthook,structsk_buff*skb,
structnet_device*indev,
structnet_device*outdev,
int(*okfn)(structsk_buff*),
inthook_thresh)
{
structnf_hook_ops*elem;
unsignedintverdict;
intret=0;
/*Wemayalreadyhavethis,butread-locksnestanyway*/
rcu_read_lock();
//取出hook点的hook函数的链表头
elem=list_entry_rcu(&nf_hooks[pf][hook],structnf_hook_ops,list);
next_hook:
next_hook:
//遍历执⾏此hook点上所有的hook函数
verdict=nf_iterate(&nf_hooks[pf][hook],skb,hook,indev,
outdev,&elem,okfn,hook_thresh);
//如果返回NF_ACCEPT或者NF_STOP说明通过hook函数的检查,则返回1
//如果返回NF_DROP,说明数据包在此hook点丢了,返回0
if(verdict==NF_ACCEPT||verdict==NF_STOP){
ret=1;
}elseif((verdict&NF_VERDICT_MASK)==NF_DROP){
kfree_skb(skb);
ret=NF_DROP_GETERR(verdict);
if(ret==0)
ret=-EPERM;
}elseif((verdict&NF_VERDICT_MASK)==NF_QUEUE){
interr=nf_queue(skb,elem,pf,hook,indev,outdev,okfn,
verdict>>NF_VERDICT_QBITS);
if(err<0){
if(err==-ECANCELED)
gotonext_hook;
if(err==-ESRCH&&
(verdict&NF_VERDICT_FLAG_QUEUE_BYPASS))
gotonext_hook;
kfree_skb(skb);
}
}
rcu_read_unlock();
returnret;
}
遍历执⾏hook点上所有的hook函数。
unsignedintnf_iterate(structlist_head*head,
structsk_buff*skb,
unsignedinthook,
conststructnet_device*indev,
conststructnet_device*outdev,
structnf_hook_ops**elemp,
int(*okfn)(structsk_buff*),
inthook_thresh)
{
unsignedintverdict;
/*
*Thecallermustnotblockbetweencallstothis
*functionbecauseofriskofcontinuingfromdeletedelement.
*/
list_for_each_entry_continue_rcu((*elemp),head,list){
//只执⾏优先级⽐hook_thresh⼤的hook函数
if(hook_thresh>(*elemp)->priority)
continue;
/*Optimization:wedon'tneedtoholdmodule
referencehere,sincefunctioncan'tsleep.--RR*/
repeat:
verdict=(*elemp)->hook(*elemp,skb,indev,outdev,okfn);
//hook函数的返回值不为accept,并且不为repeat才返回
if(verdict!=NF_ACCEPT){
if(verdict!=NF_REPEAT)
returnverdict;
gotorepeat;
}
}
//⾛到这⾥说明所有的hook函数都返回accept
returnNF_ACCEPT;
}
更多推荐
netfilter
发布评论