腾讯客服人工热线-中国游戏互动中心

netfilter
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