内核数据包处理.docx

上传人:b****6 文档编号:8056180 上传时间:2023-01-28 格式:DOCX 页数:14 大小:90.95KB
下载 相关 举报
内核数据包处理.docx_第1页
第1页 / 共14页
内核数据包处理.docx_第2页
第2页 / 共14页
内核数据包处理.docx_第3页
第3页 / 共14页
内核数据包处理.docx_第4页
第4页 / 共14页
内核数据包处理.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

内核数据包处理.docx

《内核数据包处理.docx》由会员分享,可在线阅读,更多相关《内核数据包处理.docx(14页珍藏版)》请在冰豆网上搜索。

内核数据包处理.docx

内核数据包处理

数据包处理的一些建议

前言

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

获取IP头部

iph=ip_hdr(skb);

structsk_buff{

......

sk_buff_data_ttransport_header;/*Transportlayerheader*/

sk_buff_data_tnetwork_header;/*Networklayerheader*/

sk_buff_data_tmac_header;/*Linklayerheader*/

......

}

 

 

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

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

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

获取tcp头部

错误1:

tcph=tcp_hdr(skb);

陷阱:

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

错误2:

tcph=(char*)iph+(iph->ihl<<2);

陷阱:

数据包可能是非线性的

改进:

tcpoff=skb_network_offset(skb)+(iph->ihl<<2);

tcph=skb_header_pointer(skb,tcpoff,sizeof(_tcph),&_tcph);

if(tcph==NULL)

return;

接口介绍:

skb_network_offset(structskb_buff*skb)

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

void*skb_header_pointer(structsk_buff*skb,intoffset,intlen,void*buffer)

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

printk("%pI4%d----->%pI4%dlen:

%dID:

%d\n",

&iph->saddr,

ntohs(tcph->source),

&iph->daddr,

ntohs(tcph->dest),

ntohs(iph->tot_len),

ntohs(iph->id));

打印信息

 

注意要点:

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负载

风险:

payload=(char*)tcph+tcph->doff*4;

陷阱1:

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

陷阱2:

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

改进1:

tcplen=skb->len-tcpoff;

if(tcph->doff*4doff*4)

{

printk("Badtcp.\n");

returnNF_ACCEPT;

}

if(skb_is_nonlinear(skb))

{

printk("Nonlinearskb.\n");

returnNF_ACCEPT;

}

payload=(char*)tcph+tcph->doff*4;

payload_len=tcplen-tcph->doff*4;

if(payload_len==0)

returnNF_ACCEPT;

 

接口介绍:

intskb_is_nonlinear(structsk_buff*skb)

判断skb的数据是否是非线性的

改进2:

charpayload_buf[1500];

tcplen=skb->len-tcpoff;

if(tcph->doff*4doff*4)

{

printk("Badtcp.\n");

returnNF_ACCEPT;

}

payload_len=tcplen-tcph->doff*4;

if(payload_len==0)

returnNF_ACCEPT;

if(payload_len>sizeof(payload_buf))

returnNF_ACCEPT;

payload=skb_header_pointer(skb,tcpoff+tcph->doff*4,payload_len,payload_buf);

if(payload==NULL)

returnNF_ACCEPT;

 

 

改进3:

tcplen=skb->len-tcpoff;

if(tcph->doff*4doff*4)

{

printk("Badtcp.\n");

returnNF_ACCEPT;

}

if(skb_linearize(skb))

{

printk("Cannotlinearizeskb.\n");

returnNF_ACCEPT;

}

payload=(char*)tcph+tcph->doff*4;

payload_len=tcplen-tcph->doff*4;

if(payload_len==0)

returnNF_ACCEPT;

 

接口介绍:

intskb_linearize(structsk_buff*skb)

将skb线性化

解析数据

1)判断数据包容

风险1:

if(payload[0]!

='G'||payload[1]!

='E'||payload[2]!

='T')

风险2:

if((payload[0]=='G'&&payload[1]=='E'&&payload[2]=='T')&&payload_len==3)

陷阱:

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

改进:

if(payload_len<3||payload[0]!

='G'||payload[1]!

='E'||payload[2]!

='T')

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

风险:

host=strstr(payload,"Host:

");

陷阱:

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

改进:

host=strnstr(payload,"Host:

",payload_len);

if(host==NULL)

return;

 

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

strnchr

strncpy

strncat

strncmp

strnicmp

strnlen

memcpy

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

风险:

host=strnstr(payload,"Host:

",payload_len);

if(host==NULL)

return;

host=host+sizeof("Host:

")-1;

/*

*dealwithhost

*/

 

 

陷阱:

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

改进:

host=strnstr(payload,"Host:

",payload_len);

if(host==NULL)

return;

host=host+sizeof("Host:

")-1;

if(host>=(payload+payload_len))

return;

/*

*dealwithhost

*/

 

4)数据包操作

错误:

u32len;

len=payload_len-512;

if(len<=0)

return;

memcpy(buf,payload,len);

 

陷阱:

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

改进:

u32len;

if(payload_len<=512)

return;

len=payload_len-512;

memcpy(buf,payload,len);

 

或者

intlen;

len=payload_len-512;

if(len<=0)

return;

memcpy(buf,payload,len);

 

5)

风险:

intlen=payload[1];

memcpy(buf,&payload[2],len);

 

陷阱:

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

正确做法:

intlen=payload[1];

if(len>=payload_len-2)

return;

memcpy(buf,&payload[2],len);

 

综述:

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

存分配

风险:

 

void*xxx_alloc(unsignedlongsize,gfp_tflags)

{

intmalloc_size=size+sizeof(unsignedlong);

void*p;

if(memory_use+malloc_size>memory_max)

return;

p=kmalloc(malloc_size,flags);

if(p==NULL)

returnNULL;

memory_use+=malloc_size;

*p=size;

p+=sizeof(unsignedlong);

returnp;

}

 

陷阱1:

如果需要申请的size是0会如何?

陷阱2:

多个cpu并发申请又会如何?

改进:

void*xxx_alloc(unsignedlongsize,gfp_tflags)

{

intmalloc_size=size+sizeof(unsignedlong);

void*p;

intuse;

if(size==0)

returnNULL;

use=atomic_read(&memory_use);

if(use+malloc_size>memory_max)

return;

p=kmalloc(malloc_size,flags);

if(p==NULL)

returnNULL;

atomic_add(malloc_size,&memory_use);

*p=size;

p+=sizeof(unsignedlong);

returnp;

}

 

 

问题:

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

建议:

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

建议的同步与互斥方法

1)rcu锁

使用场景:

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

好处:

性能高,接口简单

方法:

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

rcu_read_lock();

data=rcu_dereference(global_data);

/*

*dealwithdata

*/

rcu_read_unlock();

 

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

data=kmalloc(size,GFP_KERNEL);

if(data==NULL)

return;

if(copy_from_user(data,user,len))

return

old_data=rcu_dereference(global_data)

rcu_assign_pointer(global_data,data);

if(old_data)

{

synchronize_rcu();

kfree(old_data);

}

 

另一种方法:

if(old_data)

call_rcu(&old_data->rcu,data_free_rcu);

staticdata_free_rcu(structrcu_head*head)

{

structxxx_data*data=container_of(head,structxxx_data,rcu);

kfree(data);

}

 

注意1:

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

注意2:

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

模块卸载:

old_data=rcu_dereference(global_data)

rcu_assign_pointer(global_data,NULL);

if(old_data)

{

synchronize_rcu();

kfree(old_data);

}

 

2)每CPU变量

使用场景:

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

方法:

hook函数:

cpu=get_cpu()

data=per_cpu_ptr(global_data,cpu);

/*

*dealwithdata

*/

put_cpu();

 

模块加载:

global_data=alloc_percpu(char[1024])

模块卸载:

free_percpu(global_data)

注意:

alloc_percpu()上限32k

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 幼儿教育 > 幼儿读物

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1