内核数据包处理.docx
《内核数据包处理.docx》由会员分享,可在线阅读,更多相关《内核数据包处理.docx(14页珍藏版)》请在冰豆网上搜索。
![内核数据包处理.docx](https://file1.bdocx.com/fileroot1/2022-10/11/114b70f7-b5b6-4b98-ba35-5cedd5d32aee/114b70f7-b5b6-4b98-ba35-5cedd5d32aee1.gif)
内核数据包处理
内核数据包处理
————————————————————————————————作者:
———————————————————————————————— 日期:
数据包处理的一些建议
前言
我们大部分功能都需要解析数据,进行一系列的包匹配完成,但是目前,我们没有一个很好的框架来简化这个过程,大家处理数据包都是采用原生的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_u