内核数据包处理注意事项

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

数据包处理注意事项

前言

我们大部分功能都需要解析数据,进行一系列的包匹配完成,但是目前,我们没有一个很好的框架来简化这个过程,大家处理数据包都是采用原生的linux内核接口,并且没有统一的规范要求如何使用这些接口,所以,存在大量的陷阱,一不留神就造成宕机。

获取IP头部

1)__netif_receive_skb()在进入三层处理前就对network_header进行了设置。

2)ip_rcv()中详细的检查保证了IP头部到netfilter后是完整的。

3)netfilter可以尽情使用ip头部。

获取tcp头部

错误1:

陷阱:

netfilter的钩子点是属于TCP/IP协议栈的三层流程中,而四层的TCP头部此时还没有正确获取,只是初始化为IP头部的值,无法直接使用。

错误2:

陷阱:

数据包可能是非线性的

改进:

计算三层头部相对于skb->data的偏移

从skb的指定偏移取制定长度的数据,如果要取的数据位于线性区,直接返回其开始指针,否则,则拷贝到buffer中,并将buffer指针返回。

打印信息

1) IP 地址输出

Ipv4:%pI4%pi4

IPv6:%pI6

%pi6

2) MAC 地址

%pM %pm

3)字节序的转换

ntohs()ntohl()htons() htonl()

__const_ntohl()__const_ntohs() __const_htonl() __const_htons() 区别:__const_*()是编译时处理的。

获取TCP 负载

风险:

陷阱1:

数据包可能是非线性的,同TCP 头部。

陷阱2:

TCP 头部数据有可能是被篡改过的,tcph->doff 如果很大怎么办?

改进1:

接口介绍:

判断skb的数据是否是非线性的改进2:

改进3:

接口介绍:

将skb线性化

解析数据

1)判断数据包内容

风险1:

风险2:

陷阱:

如果payload的长度只有1个字节怎么办?

改进:

2) 查找数据包中的某个字符串

风险:

陷阱:

可能会越界,数据包不一定是以'\0'结束。

payload_len

payload

改进:

一定要使用这一系列的函数:

strnchr

strncpy

strncat

strncmp

strnicmp

strnlen

memcpy

3)移动指向数据包的指针

风险:

payload_len

payload

陷阱:

查找的字符串有可能是数据包的最后一部分。

payload_len

payload

改进:

4)数据包操作

错误:

陷阱:

无符号数的强制类型转换,u32类型永远都是大于等于0的,当payload_len小于512时,判断就会不生效。

改进:

或者

5)

风险:

陷阱:

可能是异常数据包,offset不是你想要的

正确做法:

综述:数据包处理要时刻保持警醒,它可能不是你想象的样子!

内存分配

风险:

改进:

问题:kmalloc(0, ...)返回值是什么?

建议:相同的内存反复申请释放的情况下,请使用kmem_cache_alloc

建议的同步与互斥方法

1)rcu锁

使用场景:进程上下文用来配置,软中断上下文只读配置的情况

好处:性能高,接口简单

方法:

hook函数读取配置,中断上下文:

基于proc文件等的配置下发,进程上下文:

另一种方法:

注意1:synchronize_rcu()只能用于进程上下文,call_rcu()可以用于中断上下文。

注意2:data_free_rcu的调用是软中断上下文,不能使用vfree。

模块卸载:

2)每CPU变量

使用场景:

在钩子函数中使用的临时缓存区,不用每次申请释放,使用全局变量。

方法:

hook函数:

模块加载:

模块卸载:

注意:

alloc_percpu()上限32k

相关文档
最新文档