本文欢迎自由转载,但请标明出处,并保证本文的完整性。
作者:Godbach
日期:2009/09/01
一、构造数据包简析
这里并不详细介绍如何在内核中构造数据包,下文如有需要会在适当的位置进行分析。这里简单的分析讲一下内核态基于Netfilter框架构造数据包的方式。
内核中可以用到的构造数据包的方式,个人认为可以分为两种。
其一,我们直接用alloc_skb申请一个skb结构体,然后根据实际的应用填充不同的成员,或者基于当前数据包的skb,调用skb_copy_expand()函数等新申请一个nskb,并且拷贝skb的内容。
其二,也是个人比较常用的,就是直接在先前接收到的数据包skb上作修改,主要有源IP、目IP,如果是TCP/UDP协议的话,还有源端口目的端口号。总之,就是根据自己的需求去调整数据包的相关成员即可。
通常,这两种方式最终可能都要涉及到重新计算各个部分的校验和,这也是必须的。
二、如何发送构造的数据包
承接上文,数据包已经构造完毕,下一步关键就是如何发送数据包了。个人这里总结的有两种方法。
方法一,就是让数据包接着按照Netfilter的流程进行传输。因为数据包的一些内容已经被更改,尤其是当源IP和目的IP被更改,主要是交换的情况下,是需要确保有路由可查的。
NF框架中查路由的位置一是在PREROUTING之后,而是在LOCALOUT之后。又由于这里是需要将数据包从本地发送出去。因此,可以考虑让修改后的数据包从LOCALOUT点发出。
内核代码中有这种方式的典型体现。本文涉及的相关内核代码的版本都是2.6.18.3。源文件为ipt_REJECT.c,函数send_reset用于往当前接收到数据包的源IP上发送RST包,整个函数涉及了数据包的构造和发送,这里一起做个简单分析。
通过以上对send_reset函数的分析,应该明白了利用NF框架将构造数据包发送出去的方法。
源码分析中提到的1个疑问,35楼给出了解释,这里引用过来:
方法二,就是直接调用dev_queue_xmit函数,将构造完毕的数据包直接发送到网卡驱动。从NF框架来看,该函数的调用是在POSTROUTING点之后了,也可以理解为直接通过调用二层的发送函数,将三层构造的数据包发送出去。该函数实际上会调用skb->dev->hard_start_xmit,即对应网卡的驱动函数,将数据包直接发送的出去。
很显然,这个工作在二层的函数,发送数据包(数据包在二层的时候准确叫法应该是帧,我们这里是在三层直接调用的,权且还称作数据包)的方式是不需要再查路由了。
但是,二层发送的时候是需要根据目的MAC来进行的。在第一种方法构造的数据包中,仅仅交换了IP地址,而没有对MAC做任何修改。这样直接调用dev_queue_xmit是会产生问题的,并且该函数发送的内容应该是从二层头部开始,到数据包的结束。因此,如果三层构造的数据包,想调用该函数直接发送数据包的话,则需要修改数据包的源和目的MAC,并将skb->data指针指向MAC头部,以及skb->len的值也要加上头部的长度方法。以下是可参考的示例代码:
这里还要顺便说一下构造的数据包发送完毕之后,对于hook函数的返回值问题。
(1)第一种发送数据包的实现,对于send_reset函数的实现中,由于单独申请了nskb的 内存,并构造的新的数据包。新数据包接着走NF的流程了。而对于原始的skb,就通过模块的返回值return NF_DROP做出了处理。
(2)第二种发送数据包的实现,若是基于已有数据包的基础上重新构造的数据包,那么实际上原始数据包的内容已经不复存在,而且调用完毕dev_queue_xmit已将同一块缓冲区,只是填充了新数据的数据包发送出去,因此,这里已经没有原始数据包的存在了,需要返回NF_STOLEN,告诉协议栈不用关心原始的包即可。否则,若是新数据包是单独申请的内存,那么对于原数据包还应该是返回NF_DROP.
三、总结
以上就是个人分享和总结和内核中构造的数据包发送出去的两种方式。实际中常用的就是构造完数据包之后,调用dev_queue_xmit函数发送报文,也测试过调用send_reset发送RST方式。但并未采用send_reset中通过调用NF_HOOK发送过其他数据包。如果诸位朋友有相关的实践经验,欢迎分享。
本文在分析send_reset代码的过程中,参考了百度中搜到的muddoghole的文章,因为只能从百度快照看到这篇文章,并且链接过长,这里就不列出连接,对于原文的作者表示感谢。
由于对内核中的一些地方理解不够深入,因此文章中肯定存在很多问题。欢迎各位朋友指正,多多交流。
本文原文链接: http://blog.chinaunix.net/u/33048/showart_2043789.html
作者:Godbach
日期:2009/09/01
一、构造数据包简析
这里并不详细介绍如何在内核中构造数据包,下文如有需要会在适当的位置进行分析。这里简单的分析讲一下内核态基于Netfilter框架构造数据包的方式。
内核中可以用到的构造数据包的方式,个人认为可以分为两种。
其一,我们直接用alloc_skb申请一个skb结构体,然后根据实际的应用填充不同的成员,或者基于当前数据包的skb,调用skb_copy_expand()函数等新申请一个nskb,并且拷贝skb的内容。
其二,也是个人比较常用的,就是直接在先前接收到的数据包skb上作修改,主要有源IP、目IP,如果是TCP/UDP协议的话,还有源端口目的端口号。总之,就是根据自己的需求去调整数据包的相关成员即可。
通常,这两种方式最终可能都要涉及到重新计算各个部分的校验和,这也是必须的。
二、如何发送构造的数据包
承接上文,数据包已经构造完毕,下一步关键就是如何发送数据包了。个人这里总结的有两种方法。
方法一,就是让数据包接着按照Netfilter的流程进行传输。因为数据包的一些内容已经被更改,尤其是当源IP和目的IP被更改,主要是交换的情况下,是需要确保有路由可查的。
NF框架中查路由的位置一是在PREROUTING之后,而是在LOCALOUT之后。又由于这里是需要将数据包从本地发送出去。因此,可以考虑让修改后的数据包从LOCALOUT点发出。
内核代码中有这种方式的典型体现。本文涉及的相关内核代码的版本都是2.6.18.3。源文件为ipt_REJECT.c,函数send_reset用于往当前接收到数据包的源IP上发送RST包,整个函数涉及了数据包的构造和发送,这里一起做个简单分析。
通过以上对send_reset函数的分析,应该明白了利用NF框架将构造数据包发送出去的方法。
源码分析中提到的1个疑问,35楼给出了解释,这里引用过来:
方法二,就是直接调用dev_queue_xmit函数,将构造完毕的数据包直接发送到网卡驱动。从NF框架来看,该函数的调用是在POSTROUTING点之后了,也可以理解为直接通过调用二层的发送函数,将三层构造的数据包发送出去。该函数实际上会调用skb->dev->hard_start_xmit,即对应网卡的驱动函数,将数据包直接发送的出去。
很显然,这个工作在二层的函数,发送数据包(数据包在二层的时候准确叫法应该是帧,我们这里是在三层直接调用的,权且还称作数据包)的方式是不需要再查路由了。
但是,二层发送的时候是需要根据目的MAC来进行的。在第一种方法构造的数据包中,仅仅交换了IP地址,而没有对MAC做任何修改。这样直接调用dev_queue_xmit是会产生问题的,并且该函数发送的内容应该是从二层头部开始,到数据包的结束。因此,如果三层构造的数据包,想调用该函数直接发送数据包的话,则需要修改数据包的源和目的MAC,并将skb->data指针指向MAC头部,以及skb->len的值也要加上头部的长度方法。以下是可参考的示例代码:
这里还要顺便说一下构造的数据包发送完毕之后,对于hook函数的返回值问题。
(1)第一种发送数据包的实现,对于send_reset函数的实现中,由于单独申请了nskb的 内存,并构造的新的数据包。新数据包接着走NF的流程了。而对于原始的skb,就通过模块的返回值return NF_DROP做出了处理。
(2)第二种发送数据包的实现,若是基于已有数据包的基础上重新构造的数据包,那么实际上原始数据包的内容已经不复存在,而且调用完毕dev_queue_xmit已将同一块缓冲区,只是填充了新数据的数据包发送出去,因此,这里已经没有原始数据包的存在了,需要返回NF_STOLEN,告诉协议栈不用关心原始的包即可。否则,若是新数据包是单独申请的内存,那么对于原数据包还应该是返回NF_DROP.
三、总结
以上就是个人分享和总结和内核中构造的数据包发送出去的两种方式。实际中常用的就是构造完数据包之后,调用dev_queue_xmit函数发送报文,也测试过调用send_reset发送RST方式。但并未采用send_reset中通过调用NF_HOOK发送过其他数据包。如果诸位朋友有相关的实践经验,欢迎分享。
本文在分析send_reset代码的过程中,参考了百度中搜到的muddoghole的文章,因为只能从百度快照看到这篇文章,并且链接过长,这里就不列出连接,对于原文的作者表示感谢。
由于对内核中的一些地方理解不够深入,因此文章中肯定存在很多问题。欢迎各位朋友指正,多多交流。
本文原文链接: http://blog.chinaunix.net/u/33048/showart_2043789.html