关键词不能为空

当前您在: 主页 > 英语 >

Linux netfilter源码分析

作者:高考题库网
来源:https://www.bjmy2z.cn/gaokao
2021-02-05 22:23
tags:

-

2021年2月5日发(作者:jang)


Linux netfilter


源码分析



内容基本上来自两篇文章


:



Netfilter


源码分析》—(独孤九贱


/





Linux Netfilter


实现机制和扩展技术》——(杨沙洲



国防科技大学计算机学院)



一、



IP


报文的接收到


hook


函数的调用




1.1 ip_input.c ip_rcv()


函数



以接收到的报 文为例,类似的还有


ip_forward(ip_forward.c)



ip_output(ip_output.c)


int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,


struct net_device *orig_dev)


{





struct iphdr *iph;



//


定义一个


ip

< br>报文的数据报头






u32 len;





if (skb->pkt_type == PACKET_OTHERHOST)








goto drop; //


数据包不是发给我们的






IP_INC_STATS_BH(IPSTATS_MIB_INRECEIVES); //


收到数据包统计量加


1





if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)




{


/*


如果数据报是共享的,则复制一个出来,此时复制而出的 已经和


socket


脱离了关系


*/







IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);







goto out;




}




if (!pskb_may_pull(skb, sizeof(struct iphdr)))






goto inhdr_error; //


对数据报的头长度进行检查,







iph = skb->; //


取得数据报的头部位置




if (iph->ihl < 5 || iph->version != 4) //


版本号或者头长度不对,






goto inhdr_error; //


头长度是以


4


字节为单位的,所以


5


表示的是


20


字节




if (!pskb_may_pull(skb, iph->ihl*4))





goto inhdr_error;





if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))






goto inhdr_error; //


检查报文的检验和字段




len = ntohs(iph->tot_len);



if (skb->len < len || len < (iph->ihl*4))





goto inhdr_error; //


整个报文长度不可能比报头长度小




if (pskb_trim_rcsum(skb, len))



{ //


对数据报进 行裁减,这样可以分片发送过来的数据报不会有重复数据





IP_INC_STATS_BH(IPSTATS_MIB _INDISCARDS);




goto drop;



}




return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,










ip_rcv_finish); //


通过回调函数调用


ip_rcv_finish



inhdr_error:



IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);


drop:









kfree_skb(skb); //


丢掉数据报



out:









return NET_RX_DROP;


}





1.2 include/linux/netfilter.h NF_HOOK





#ifdef CONFIG_NETFILTER_DEBUG


#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)


#define NF_HOOK_THRESH nf_hook_slow


#else


#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)


(list_empty(&nf_hooks[(pf)][(hook)])


? (okfn)(skb)



















nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)


: nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))


#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)



(list_empty(&nf_hooks[(pf)][(hook)])


? (okfn)(skb)



#endif



/*


如果


nf_hooks[PF_INET][NF_IP_FORWA RD]


所指向的链表为空(即该钩子上没有挂处


理函数),则直 接调用


okfn


;否则,则调用


net /core/netfilter.c::nf_hook_slow()


转入


Netfilter


的处理。


*/




1.3 net/core/netfilter.c nf_kook_slow()


函数




int nf_hook_slow(int pf, unsigned int hook, struct sk_buff **pskb,












struct net_device *indev,












struct net_device *outdev,












int (*okfn)(struct sk_buff *),














: nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))












int hook_thresh)


{






struct list_head *elem;






unsigned int verdict;






int ret = 0;







rcu_read_lock();







/*


取得对应的链表首部


*/






elem = &nf_hooks[pf][hook];


next_hook:






/*


调用对应的钩子函数


*/






verdict = nf_iterate(&nf_hooks[pf][hook], pskb, hook, indev,


















outdev, &elem, okfn, hook_thresh);







/*


判断返回值,做相应的处理


*/


if (verdict == NF_ACCEPT || verdict == NF_STOP) {







ret = 1;


/*


前面提到过,返回


1


,则表示装继续调用


okfn


函数指针


*/







goto unlock;




} else if (verdict == NF_DROP) {






kfree_skb(*pskb);









/*


删除数据包,需要释放


skb*/







ret = -EPERM;




} else if (verdict == NF_QUEUE) {







NFDEBUG(







if (!nf_queue(*pskb, elem, pf, hook, indev, outdev, okfn))










goto next_hook;





}


unlock:






rcu_read_unlock();






return ret;


}



1.4 net/core/netfilter.c nf_iterate()


函数




static unsigned int nf_iterate(struct list_head *head,

















struct sk_buff **skb,


int hook,


const struct net_device *indev,


const struct net_device *outdev,


struct list_head **i,




{








/*




int (*okfn)(struct sk_buff *),


int hook_thresh)


* The caller must not block between calls to this


* function because of risk of continuing from deleted element.


*/


/*


依 次调用指定


hook


点下的所有


nf_ hook_ops->(*hook)


函数,这些


nf_hoo k_ops


里有


filter


表注册的 ,有


mangle


表注册的,等等。



list_for_each_continue_rcu


函数 是一个


for


循环的宏,当调用结点中的


hook


函数后,根


据返回值进行相应处理。


如果


hook


函数的返回值是


NF_QUEUE,NF_STOLEN,NF_DROP


时,


函数


返回该值;


如果返回值是


NF_R EPEAT


时,则跳到前一个结点继续处理;如果是其他值,由下


一个结点继续处理。如果整条链表处理完毕,返回值不是上面四个值,则返回


NF_A CCEPT



*/
























}





二、


ipt_table

< p>
数据结构和表的初始化







}


return NF_ACCEPT;


case NF_REPEAT:




}


*i = (*i)->prev;


break;




case NF_DROP:



return NF_DROP;




case NF_STOLEN:



return NF_STOLEN;





switch (elem->hook(hook, skb, indev, outdev, okfn)) {


case NF_QUEUE:



return NF_QUEUE;




if (hook_thresh > elem->priority)



continue;


list_for_each_continue_rcu(*i, head) {



struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;



2.1 include/linux/netfilter_ipv4/ip_tables.h struct ipt_table


表结构




struct ipt_table


{


struct list_head list;


/*


表链


*/


char name[IPT_TABLE_MAXNAMELEN];


/*

< br>表名,





等,为了满足自动模块加载的设计,包含该表的模块应命名为


iptable_'name'.o */


struct ipt_replace *table;


/*


表模子,初始为


initial_ */


unsigned int valid_hooks;


/*


位向量,标示本表所影响的


HOOK */


rwlock_t lock;


/*


读写锁,初始为打开状态


*/


struct ipt_table_info *private;


/* iptable


的数据区,见下


*/


struct module *me;


/*


是否在模块中定义


*/


};





2.2 struct ipt_table_info


是实际描述表的数据结构


ip_tables.c





struct ipt_table_info


{


unsigned int size;


/*


表大小


*/


unsigned int number;


/*


表中的规则数


*/


unsigned int initial_entries;


/*


初始的规则数,用于模块计数


*/


unsigned int hook_entry[NF_IP_NUMHOOKS];


/*

< br>记录所影响的


HOOK


的规则入口相对于下面的


entries


变量的偏移量


*/


unsigned int underflow[NF_IP_NUMHOOKS];


/*



hook_entry


相对应的规则表上限偏移量,当无规则录入时,相应的


hook_en try



underflow


均为


0 */


char entries[0] ____cacheline_aligned;


/*


规则表入口


*/


};



2.3 include/linux/netfilter_ipv4


规则用


struct


ipt_ent ry


结构表示,包含匹配用



IP


头部分、


一个


Target



0


个或多个


Match



由于


Match


数不定,


所以一条规则实际的


占用空间是可变的。结构定义如下




struct ipt_entry


{


struct ipt_ip ip;


/*


所要匹配的报文的


IP


头信息


*/


unsigned int nfcache;


/*


位向量,标示本规则关心报文的什么部分,暂未使用


*/


u_int16_t target_offset;


/* target


区的偏移,通常


target

区位于


match


区之后,而


ma tch


区则在


ipt_entry


的末


尾;



初始化为


sizeof(struct ipt_entry)


,即假定没有


match */


u_int16_t next_offset;


/*


下一条规则相对于本规则的偏移,也即本规则所用空间的总和,



初始化为


sizeof(struct ipt_entry)+sizeof(struct ipt_target)


,即没有


match */


unsigned int comefrom;


/*


规则返回点,标记调用本规则的


HOOK


号 ,可用于检查规则的有效性


*/


struct ipt_counters counters;


/*


记录该规则处理过的报文数和报文总字节数


*/


unsigned char elems[0];


/*ta rget


或者是


match


的起始位置


*/


}




2.4



iptables


的初始化


init(void)



,以


filter


表为例



iptable_filter.c




static int __init init(void)


{










/* Entry 1 is the FORWARD hook */


if (forward < 0 || forward > NF_MAX_VERDICT) {




}


printk(


return -EINVAL;


int ret;
























initial_s[1].t = -forward - 1;


/* Register table */


ret = ipt_register_table(&packet_filter); //

< br>注册


filter




if (ret < 0)



return ret;


/* Register hooks */


ret = nf_register_hook(&ipt_ops[0]); //


注册三个


HOOK


if (ret < 0)



goto cleanup_table;


ret = nf_register_hook(&ipt_ops[1]);


if (ret < 0)



goto cleanup_hook0;


ret = nf_register_hook(&ipt_ops[2]);


if (ret < 0)



goto cleanup_hook1;


return ret;


cleanup_hook1:







}




/*


ipt_register_table


函数的参数

< p>
packet_filter


包含了待注册表的各个参数

< br>


*/



static struct ipt_table packet_filter = {







};


.name



.table



.lock



.me


=


= &initial_,


= FILTER_VALID_HOOKS,


= RW_LOCK_UNLOCKED,


return ret;


nf_unregister_hook(&ipt_ops[1]);


nf_unregister_hook(&ipt_ops[0]);


ipt_unregister_table(&packet_filter);


cleanup_hook0:


cleanup_table:


.valid_hooks


= THIS_MODULE



/*


上面的


&initial_


是一个


ipt_replace


结构,也就是


ip t_table-



*table


的< /p>


初始值。



下面是


ipt_replace


结构的定义,它和


ipt_tabl e_info


很相似,基本上就是用来初始化


ipt_tabl e


中的


ipt_table_info *private


的,这个结构不同于


ipt_table_info


之处在于,


它还要保存表的旧的规则信息



*/



struct ipt_replace


{











};



/*


下面是


initial_


的初始化


*/


static struct


{





struct ipt_replace repl;


struct ipt_standard entries[3];


struct ipt_error term;


char name[IPT_TABLE_MAXNAMELEN]; /*


表名


*/


unsigned int valid_hooks; /*


影响的


hook */


unsigned int num_entries; /* entry



*/


unsigned int size; /* entry


的总大小


*/


unsigned int hook_entry[NF_IP_NUMHOOKS]; /*


规则入口的偏移值


*/


unsigned int underflow[NF_IP_NUMHOOKS]; /*


规则的最大偏移值


*/


unsigned int num_counters; /*


规则数


*/


struct ipt_counters __user *counters;


struct ipt_entry entries[0]; /*


规则入口


*/


} initial_table __initdata


= { {


sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error),


{ [NF_IP_LOCAL_IN] = 0,






[NF_IP_FORWARD] = sizeof(struct ipt_standard),


[NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2 },


[NF_IP_FORWARD] = sizeof(struct ipt_standard),


[NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2 },


{ [NF_IP_LOCAL_IN] = 0,


0, NULL, { } },


{







/* LOCAL_IN */


{ { { { 0 }, { 0 }, { 0 }, { 0 },





0,


sizeof(struct ipt_entry),


sizeof(struct ipt_standard),























0, { 0, 0 }, { } },


-NF_ACCEPT - 1 } },


{ { { { IPT_ALIGN(sizeof(struct ipt_standard_target)),


/* FORWARD */


{ { { { 0 }, { 0 }, { 0 }, { 0 },







0,


sizeof(struct ipt_entry),


sizeof(struct ipt_standard),


0, { 0, 0 }, { } },


-NF_ACCEPT - 1 } },


{ { { { IPT_ALIGN(sizeof(struct ipt_standard_target)),


/* LOCAL_OUT */


{ { { { 0 }, { 0 }, { 0 }, { 0 },







0,


sizeof(struct ipt_entry),


sizeof(struct ipt_standard),


0, { 0, 0 }, { } },


-NF_ACCEPT - 1 } }


{ { { { IPT_ALIGN(sizeof(struct ipt_standard_target)),


},


/* ERROR */


{ { { { 0 }, { 0 }, { 0 }, { 0 },








0,


sizeof(struct ipt_entry),


sizeof(struct ipt_error),


0, { 0, 0 }, { } },


{ } },



{ { { { IPT_ALIGN(sizeof(struct ipt_error_target)), IPT_ERROR_TARGET } },


}


}


};





三、


ip t_table


表的注册




init


()函数初始化时调用了


ipt_re gister_table


函数进行表的注册




3.1 ip_tables.c


表的注册


ipt_register_table


int ipt_register_table(struct ipt_table *table)


{







/*< /p>



MOD_INC_USE_COUNT


用于模块计数器累加,主要是为了防止模块异常删除,对


应的宏


MOD_DEC_USE_COUNT


就是累减了


*/





/*

< p>
为每个


CPU


分配规则空间


*/











/*


将规则项拷贝到新表项的第一个


cpu


空间里面


*/

< br>




/*translate _table


函数将


newinfo


表 示的


table


的各个规则进行边界检查,然后对于

< p>
newinfo


所指的


ipt_talbe_in fo


结构中的


hook_entries



underflows


赋予正确的值,

最后将表项向其他


cpu


拷贝


*/



















ret = down_interruptible(&ipt_mutex);


if (ret != 0) {





vfree(newinfo);


MOD_DEC_USE_COUNT;


return ret;


ret = translate_table(table->name, table->valid_hooks,









}






newinfo, table->table->size,


table->table->num_entries,


table->table->hook_entry,


table->table->underflow);


memcpy(newinfo->entries, table->table->entries, table->table->size);


newinfo = vmalloc(sizeof(struct ipt_table_info)






}



+ SMP_ALIGN(table->table->size) * smp_num_cpus);


if (!newinfo) {


ret = -ENOMEM;


MOD_DEC_USE_COUNT;


return ret;


MOD_INC_USE_COUNT;


int ret;


struct ipt_table_info *newinfo;


static struct ipt_table_info bootstrap



= { 0, 0, 0, { 0 }, { 0 }, { } };


if (ret != 0) {


vfree(newinfo);


MOD_DEC_USE_COUNT;


return ret;




}



/*


如果注册的


table


已经存在,释放空间



并且递减模块计数



*/









/*


替换


table



. */

















unlock:





free_unlock:





}





3.2



ip_tables.c




translate_table()


函数



/*


函数


:translate_table()


*


参数:



vfree(newinfo);


MOD_DEC_USE_COUNT;


goto unlock;


up(&ipt_mutex);


return ret;


table->lock = RW_LOCK_UNLOCKED;


list_prepend(&ipt_tables, table);

< p>
/*


将表添加进链表


*/



duprintf(




/* save number of initial entries */


table->private->initial_entries = table->private->number;


table->private->number);


/* Simplifies replace_table code. */


table->private = &bootstrap;


if (!replace_table(table, 0, newinfo, &ret))



goto free_unlock;


/* Don't autoload: we'd eat our tail... */


if (list_named_find(&ipt_tables, table->name)) {




}


ret = -EEXIST;


goto free_unlock;


/*


保存初始规则计数器



*/



*



name:


表名称;



*



valid_hooks


:当前表所影响的


hook


*


newinfo


:包含当前表的所有信息的结构



*


size


:表的大小



*


number


:表中的规则数



*


hook_entries



记录所影响的


HOOK


的规则入口相对 于下面的


entries


变量的偏移量



*



underflows


:与


hook_entry


相对应的规则表上 限偏移量



*


作用:



*

translate_table


函数将


newinfo< /p>


表示的


table


的各个规则进行边界检 查,然后对于


newinfo


所指的


i pt_talbe_info


结构中的


hook_entrie s



underflows


赋予正确的 值,


最后将表项向其他


cpu


拷贝



*



返回值:



*





int ret==0


表示成功返回



*/



static int


translate_table(const char *name,










unsigned int valid_hooks,










struct ipt_table_info *newinfo,










unsigned int size,










unsigned int number,










const unsigned int *hook_entries,










const unsigned int *underflows)


{






unsigned int i;






int ret;







newinfo->size = size;






newinfo->number = number;







/*


初始化所有


Hooks


为不可能的值


. */






for (i = 0; i < NF_IP_NUMHOOKS; i++) {










newinfo->hook_entry[i] = 0xFFFFFFFF;










newinfo->underflow[i] = 0xFFFFFFFF;






}







duprintf(






i = 0;






/*


遍历所有规则,


检查所有偏量,


检查的工作都是由


IPT_ENTRY_ITERATE


这个


宏来完成,并且它的最后一个参数


i


,返回表的所有规则数


. */






ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,


















check_entry_size_and_hooks,


















newinfo,


















newinfo->entries,


















newinfo->entries + size,


















hook_entries, underflows, &i);






if (ret != 0)










return ret;







/*


实际计算得到的规则数与指定 的不符


*/






if (i != number) {










duprintf(













i, number);










return -EINVAL;






}







/*


因为函数一开始将


HOOK


的偏移地址全部初始成了不可能的 值,而在上一个宏


的遍历中设置了


hook_entries< /p>



underflows


的值,这里对它 们进行检查



*/






for (i = 0; i < NF_IP_NUMHOOKS; i++) {










/*


只检查当前表所影响的


hook */










if (!(valid_hooks & (1 << i)))














continue;










if (newinfo->hook_entry[i] == 0xFFFFFFFF) {














duprintf(

















i, hook_entries[i]);














return -EINVAL;










}










if (newinfo->underflow[i] == 0xFFFFFFFF) {














duprintf(

















i, underflows[i]);














return -EINVAL;










}






}







/*


确保新的


table


中不存在规则 环


*/






if (!mark_source_chains(newinfo, valid_hooks))










return -ELOOP;







/*



tables


中的规则项进行完整性检查,保证每一个规则项在形式上是合法的


*/






i = 0;






ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,


















check_entry, name, size, &i);











/*


检查失败,释放空间,返回


*/






if (ret != 0) {










IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,


















cleanup_entry, &i);










return ret;






}







/*


为每个


CPU


复制一个完整的


tab le



*/






for (i = 1; i < smp_num_cpus; i++) {










memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,











newinfo->entries,











SMP_ALIGN(newinfo->size));






}







return ret;


}



3.3 IPT_ENTRY_ITERAT



ip_tables.h


用来遍历每一个规则,然后调用其第三个参数(函数指针)进 行处理,前两个参数分别表


示规则的起始位置和规则总大小,后面的参数则视情况而定。



#define IPT_ENTRY_ITERATE(entries, size, fn, args...)


({















})



/* translate_table


中出现了三次,分别是



*/


IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,


















check_entry_size_and_hooks,


















newinfo,


















newinfo->entries,


















newinfo->entries + size,


















hook_entries, underflows, &i);


























unsigned int __i;



int __ret = 0;















struct ipt_entry *__entry;



for (__i = 0; __i < (size); __i += __entry->next_offset) {


__entry = (void *)(entries) + __i;























__ret = fn(__entry , ## args);



if (__ret != 0)



break;











}



__ret;



IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,


















check_entry, name, size, &i);


IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,


















cleanup_entry, &i);


即是在遍历到每条


entry


时分别调用



check_ entry_size_and_hooks



check_e ntry,


cleanup_entry,


三个函数


check_entry


有大用处,后面解释





3.4




list_named_find


( )函数





listhelp.h


在注册函数中,调用







list_named_find(&ipt_tables, table->name)



来检查当前表是否已被注册过了 。可见,第一个参数为链表首部,第二个参数为当前表名。



其原型如下:



#define list_named_find(head, name)














LIST_FIND(head, __list_cmp_name, void *, name)



#define LIST_FIND(head, cmpfn, type, args...)










({


































const struct list_head *__i = (head);











































ASSERT_READ_LOCK(head);






















do {


































__i = __i->next;






















if (__i == (head)) {


























__i = NULL;


























break;


























}


























} while (!cmpfn((const type)__i , ## args));










(type)__i;






















})



前面提过,表是一个双向链表,在宏当中,以


while


进行循环,以


__i = __i->next;

< br>进行遍历,然后调用比较函数进行比较,传递过来的比较函数是


__list_c mp_name





比较函数很简单:



static inline int __list_cmp_name(const void *i, const char *name)


{






return strcmp(name, i+sizeof(struct list_head)) == 0;


}




3.5 replace_table


()函数




ip_tables.c



表中以


struct ipt_table_info *pr ivate;


表示实际数据区。但是在初始化赋值的时候,被


设 为


NULL


,而表的初始变量都以模版的形式,放在

< p>
struct ipt_replace *table;


中。



注册函数一开始,就声明了:


struct ipt_table_info *newinfo;


然后对其分配了空间,将模块中 的初值拷贝了进来。所以


replace_table


要做的工 作,主


要就是把


newinfo


中的值 传递给


table


结构中的


priva te


成员。



replace_table(struct ipt_table *table,





{









if (num_counters != table->private->number) {




duprintf(



num_counters, table->private->number);


write_lock_bh(&table->lock);


struct ipt_table_info *oldinfo;


unsigned int num_counters,


struct ipt_table_info *newinfo,


int *error)


/* ipt_re gister_table


函数中,


replace_tabl e


函数之前有一句


table->private =


&bootstrap;



private


初始化为


bootstrap


,即


{ 0



0



0



{0}



{0}



{}} */












}






3.6



list_prepend


()函数





listhelp.h


当所有的初始化工作结束,就调用


list_prepend

< p>
来构建链表了。




static inline void


list_prepend(struct list_head *head, void *new)


{






ASSERT_WRITE_LOCK(head);









/*


设置写互斥


*/


return oldinfo;





}


oldinfo = table->private;


table->private = newinfo;


newinfo->initial_entries = oldinfo->initial_entries;


write_unlock_bh(&table->lock);


write_unlock_bh(&table->lock);


*error = -EAGAIN;


return NULL;






list_add(new, head);













/*


将当前表节点添加进链表


*/


}



list_add


就是一个构建双向链表的过程:



static __inline__ void list_add(struct list_head *new, struct list_head *head)


{






__list_add(new, head, head->next);


}



static __inline__ void __list_add(struct list_head * new,






struct list_head * prev,






struct list_head * next)


{






next->prev = new;






new->next = next;






new->prev = prev;






prev->next = new;


}











四、


nf_hook_ops



钩子的注册




filter


表的初始化函数


static


int


__init


init(v oid)


中除了有一个


nf_register_hook


函数注册一个


tables


外,还由


nf_register_hook


函数注册了


3



hook



4.1



nf_hook_ops


数据结构



netfilter.h


struct nf_hook_ops


{






struct list_head list;













//


链表成员







/* User fills in from here down. */






nf_hookfn *hook;













//


钩子函数指针







struct module *owner;






int pf;





















//


协议簇,对于


ipv4


而言,是


PF_INET






int hooknum;

















//hook


类型







/* Hooks are ordered in ascending priority. */






int priority;

















//


优先级



};



list

成员用于维护


Netfilter hook


的列表。



hook


成员是一个指向


nf_hookfn


类型的函数 的指针,该函数是这个


hook


被调用时执

行的函数。


nf_hookfn


同样在

linux/netfilter.h


中定义。



pf


这个成员用于指定协议族。有效的协议族在


linux/socket.h


中列出,但对于


IPv4


我们


使用协议族


PF_INET




hooknum


这个成 员用于指定安装的这个函数对应的具体的


hook


类型


:






NF_IP_PRE_ROUTING



在完整性校验之后,选路确定之前







NF_IP_LOCAL_IN




在选路确定之后,且数据包的目的是本地主机







NF_IP_FORWARD




目的地是其它主机地数据包







NF_IP_LOCAL_OUT




来自本机进程的数据包在其离开本地主机的过程中







NF_IP_POST_ROUTING



在数据包离开本地主机



上线



之前




再看看它的初始化,仍以


filter


表为例

< br>


static struct nf_hook_ops ipt_ops[]


= { { { NULL, NULL }, ipt_hook, PF_INET, NF_IP_LOCAL_IN,


NF_IP_PRI_FILTER },




{ { NULL, NULL }, ipt_hook, PF_INET, NF_IP_FORWARD,


NF_IP_PRI_FILTER },




{ { NULL, NULL }, ipt_local_out_hook, PF_INET, NF_IP_LOCAL_OUT,










NF_IP_PRI_FILTER }


};





4.2




int nf_register_hook


函数





netfilter.c



注册实际上就是在一个


nf_hoo k_ops


链表中再插入一个


nf_hook_ops


结构



int nf_register_hook(struct nf_hook_ops *reg)


{











spin_lock_bh(&nf_hook_lock);


list_for_each(i, &nf_hooks[reg->pf][reg->hooknum]) {




}


list_add_rcu(®->list, i->prev);


spin_unlock_bh(&nf_hook_lock);


if (reg->priority < ((struct nf_hook_ops *)i)->priority)



break;


struct list_head *i;





}


list_for_each


函数遍历当前待注册的钩子的协 议


pf



Hook

类型所对应的链表,其首地址



&nf_hooks[re g->pf][reg->hooknum]


,如果当前待注册钩子的优先级小于匹配的 的节点


的优先级,则找到了待插入的位置,也就是说,按优先级的升序排列。

< p>



list_add_rcu

< br>把当前节点插入到查到找的适合的位置,这样,完成后,所有


pf


协议下的


hooknum


类型的钩子,


都被注册到


&nf_hooks[reg->pf][reg->hooknu m]


为首的链表当中了。






4.3



ipt_hook


钩子函数





iptable_raw.c < /p>


注册


nf_hook_ops


,也就向内 核注册了一个钩子函数,这些函数有


ipt_hook



ipt_local_hook



ipt_r oute_hook



ipt_local_out_hook


等。



前面在


nf_iterate()


里调用的钩子函数就是它了



下面是


ipt_hook


函数的定义:



static unsigned int


ipt_hook(unsigned int hook,











/* hook




*/






{




/*


参数


&packet_filt er


是由注册该


nf_hook_ops


的表(


filter


)决定的,也有可能是

< br>&packet_raw */



}


实际上是直接调用


ipt_do_table(ip_tables.c)


函数



接下来就是根据

table


里面的


entry


来处 理数据包了



一个


table


就是一组防火墙规则的集合



而一个

< p>
entry


就是一条规则,每个


entry


由一系列的


matches


和一个

< p>
target


组成



一旦 数据包匹配了该某个


entry


的所有


matches


,就用


target


来 处理它



Match


又分为两部份,一 部份为一些基本的元素,如来源


/


目的地址,进


/


出网口,协议等,


对应了


s truct ipt_ip


,我们常常将其称为标准的


matc h


,另一部份


match


则以插件的形 式存


在,是动态可选择,也允许第三方开发的,常常称为扩展的


match


,如字符串匹配,


p2p



配等。同样,规则的


target


也是 可扩展的。这样,一条规则占用的空间,可以分为:


struct

ipt_ip+n*match+n*target



(< /p>


n


表示了其个数,这里的


match


指的是可扩展的


match


部份)







return ipt_do_table(pskb, hook, in, out,


&packet_filter,


NULL);



struct sk_buff **pskb,



const struct net_device *in,



const struct net_device *out,



int (*okfn)(struct sk_buff *))






/*


默认处理函数



*/


synchronize_net();


return 0;






五、



ipt_do_table()


函数,数据包的过滤




5.1



ipt_entry


相关结构




ip_tables.h


ipt_ entry


结构前面有过了,再看一遍



struct ipt_entry


{


struct ipt_ip ip;


/*


所要匹配的报文的


IP


头信息


*/


unsigned int nfcache;


/*


位向量,标示本规则关心报文的什么部分,暂未使用


*/


u_int16_t target_offset;


/* target


区的偏移,通常


target

区位于


match


区之后,而


ma tch


区则在


ipt_entry


的末


尾;



初始化为


sizeof(struct ipt_entry)


,即假定没有


match */


u_int16_t next_offset;


/*


下一条规则相对于本规则的偏移,也即本规则所用空间的总和,



初始化为


sizeof(struct ipt_entry)+sizeof(struct ipt_target)


,即没有


match */


unsigned int comefrom;


/*


位向量,标记调用本规则的


HOOK


号,可 用于检查规则的有效性


*/


struct ipt_counters counters;


/*


记录该规则处理过的报文数和报文总字节数


*/


unsigned char elems[0];


/*ta rget


或者是


match


的起始位置


*/


}



ipt_ip


结构




ip_tables.h



struct ipt_ip {












};


u_int16_t proto;





/*


协议


, 0 = ANY */




u_int8_t flags;






/*


标志字段


*/



u_int8_t invflags;



/*


取反标志


*/


char iniface[IFNAMSIZ], outiface[IFNAMSIZ];





/*


输入输出网络接口


*/



unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];


struct in_addr src, dst;










/*


来源


/


目的地址


*/



struct in_addr smsk, dmsk;






/*


来源


/


目的地址的掩码


*/

-


-


-


-


-


-


-


-



本文更新与2021-02-05 22:23,由作者提供,不代表本网站立场,转载请注明出处:https://www.bjmy2z.cn/gaokao/603534.html

Linux netfilter源码分析的相关文章