代码是在别个基础上修改的,不过还是花了不少时间调试通过。
想实现的功能是,client的IP地址是192.168.1.187,但是server发现跟自己建立链接的是192.169.1.188,实现了IP地址的隐藏。
在server端内核进行数据修改,server也无法发现自己的数据被修改了。例如192.168.1.187的client给192.168.1.111的server发送数据0x11,但是对于server来说,收到的是192.168.1.188的0xFF。
client是windows是的普通TCP客户端,server是中标麒麟上的普通TCP服务端。
win7上利用wireshark进行TCP数据分析。
中标麒麟(linux系统)上加载in.ko模块后,利用dmesg进行数据的打印分析,或者利用tcpdump进行数据抓取打印分析。
刚开始发现TCP三次握手不成功,client发送数据后,通过wireshark抓取数据发现TCP校验和不对,但是server端能够收到tcp三次握手第1包,而且in.ko模块还发送了第2包三次握手包,但是wireshark抓取不到。此时感到无从下手,因为自己网络编程内核编程知识严重不足。
然后尝试用windows的client给本机虚拟机的unbuntu,现象不一致了,wireshark能够抓取到server发送过来的三次握手第2包,但是没有回第三包,通过分析数据体,发现校验和不对,而且每次相差固定值,然后对server端的校验和进行转换,三次握手成功,数据发送成功。虽然没有从根本解决问题,但是实现了功能,后面还得多多深入学习。路漫漫其修远兮,吾将上下而求索。
还有一个需要注意的地方是,虽然client的IP地址是192.168.1.187,但是得通过网络添加一个192.168.1.188的IP地址,一个网口对应两个IP地址,因为这样192.168.1.111的ARP才知道192.168.1.188的网络可达,否则LOCAL_OUT的路由选择时,就会把数据报丢了。
知识有限,有不足的地方希望大家指正指教。
///////////////////// in.c /////////////////////
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define CHECKSUM_HW 1
#define SWITICH 1
#define T_SWITICH 1
static inline int printk_hex(const char* name,unsigned char *buff,int length)
{
unsigned char *string_tmp = buff;
int i;
int count = 0;
printk("==========%s start ==========\n",name);
for(i=0;iprotocol==IPPROTO_TCP))
{
///IP hook钩子函数都在ip层,skb指向mac头和ip头的成员,都是有效的,可以直接使用接口API获取。
///获取TCP/UDP头时,skb中的成员是无效的,所以需要自己实现获取API。
tcph=(struct tcphdr*)((unsigned char*)iph->ihl*4);
data=skb->data+iph->ihl*4+tcph->doff*4;
header=iph->ihl*4+tcph->doff*4;
length=skb->len-iph->ihl*4-tcph->doff*4;
if(skb->len-header>0)
{
///说明有数据,在TCP三次握手时只有数据头无数据体。
printk("**************now_start_in_data*****************\n");
if(skb->data_len!=0)///非线性数据区
{
if(skb_linearize(skb))
{
printk("error line skb\r\n");
printk("skb->data_len %d\r\n",skb->data_len);
return NF_DROP;
}
}
data[0]=0xFF;///此时可以修改数据体数据,但是记得将skb->ip_summed = CHECKSUM_HW,否则应用层收不到修改后的数据。
printk_hex("data",data,length);///打印数据体
#ifdef SWITICH
iph->saddr = 0bc01a8c0///源IP地址修改,从而隐藏地址。
skb->ip_summed = CHECKSUM_HW;
#endif
iph->check=0;
iph->check=ip_fast_csum((unsigned char*)iph, iph->ihl);
///只要skb->ip_summed = CHECKSUM_HW,TCP头校验和校验就不会出错,后面的计算校验和可有可无。
///如果只要skb->ip_summed为0,计算了下面的TCP校验和,应用层也收不到数据,说明可能校验和校验出错。
if(skb->ip_summed == CHECKSUM_HW)
{
tcph->check=csum_tcpudp_magic(iph->saddr,iph->daddr,(ntohs(iph ->tot_len)-iph->ihl*4), IPPROTO_TCP,csum_partial(tcph,(ntohs(iph ->tot_len)-iph->ihl*4),0));
skb->csum = offsetof(struct tcphdr,check);
}
}
}
else if(likely(iph->protocol==IPPROTO_UDP))
{
udph=udp_hdr(skb);
data=skb->data+iph->ihl*4+sizeof(struct udphdr);
header=iph->ihl*4+sizeof(struct udphdr);
length=ntohs(iph->tot_len)-iph->ihl*4-sizeof(struct udphdr);
if(skb->len-header>0)
{
printk("header length is %d",header);
printk("\r\n");
printk("len -header is %d",skb->len-header);
printk("\r\n");
printk("data length is %d",length);
printk("\r\n");
for(i=0;idata_len!=0)
{
if(skb_linearize(skb))
{
printk("error line skb\r\n");
printk("skb->data_len %d\r\n",skb->data_len);
return NF_DROP;
}
}
for(i=0;icheck=0;
iph->check=ip_fast_csum((unsigned char*)iph, iph->ihl);
if(skb->ip_summed == CHECKSUM_HW)
{
udph->check=csum_tcpudp_magic(iph->saddr,iph->daddr,(ntohs(iph ->tot_len)-iph->ihl*4), IPPROTO_UDP,csum_partial(udph, (ntohs(iph ->tot_len)-iph->ihl*4), 0));
}
printk("*********************UDPend********************\n");
}
}
else if(likely(iph->protocol==IPPROTO_ICMP))
{
icmph=icmp_hdr(skb);
data=skb->data+iph->ihl*4+sizeof(struct icmphdr);
header=iph->ihl*4+sizeof(struct icmphdr);
length=ntohs(iph->tot_len)-iph->ihl*4-sizeof(struct icmphdr);
if(skb->len-header>0)
{
printk("header length is %d",header);
printk("\r\n");
printk("len - header is %d",skb->len-header);
printk("\r\n");
printk("data length is %d",length);
printk("\r\n");
if(skb->data_len!=0)
{
if(skb_linearize(skb))
{
printk("error line skb\r\n");
printk("skb->data_len %d\r\n",skb->data_len);
return NF_DROP;
}
}
for(i=0;icheck=0;
iph->check=ip_fast_csum((unsigned char*)iph, iph->ihl);
if(skb->ip_summed == CHECKSUM_HW)
{
icmph->checksum=ip_compute_csum(icmph, (ntohs(iph ->tot_len)-iph->ihl*4));
}
printk("*********************ICMPend********************\n");
}
}
return NF_ACCEPT;
}
unsigned int my_func_out(unsigned int hooknum,struct sk_buff *skb,const struct net_device *in,const struct net_device *out,int (*okfn)(struct sk_buff *))
{
struct iphdr *iph=ip_hdr(skb);
struct tcphdr *tcph;
struct udphdr *udph;
struct icmphdr *icmph;
struct net_device *master;
static int jason_flag = 0;
int i=0,ret=-1;
int header=0;
int index=0;
unsigned char *data=NULL;
int length=0;
if(likely(iph->protocol==IPPROTO_TCP))
{
///IP hook钩子函数都在ip层,skb指向mac头和ip头的成员,都是有效的,可以直接使用接口API获取。
///获取TCP/UDP头时,skb中的成员是无效的,所以需要自己实现获取API。
tcph=(struct tcphdr*)((unsigned char*)iph->ihl*4);
data=skb->data+iph->ihl*4+tcph->doff*4;
header=iph->ihl*4+tcph->doff*4;
length=skb->len-iph->ihl*4-tcph->doff*4;
if(skb->len-header>0)
{
///说明有数据,在TCP三次握手时只有数据头无数据体。
printk("**************now_start_in_data*****************\n");
if(skb->data_len!=0)///非线性数据区
{
if(skb_linearize(skb))
{
printk("error line skb\r\n");
printk("skb->data_len %d\r\n",skb->data_len);
return NF_DROP;
}
}
data[0]=0xFF;///此时可以修改数据体数据,但是记得将skb->ip_summed = CHECKSUM_HW,否则应用层收不到修改后的数据。
printk_hex("data",data,length);///打印数据体
#ifdef SWITICH
iph->daddr = 0bb01a8c0///源IP地址修改,从而隐藏地址。
skb->ip_summed = CHECKSUM_HW;
#endif
iph->check=0;
iph->check=ip_fast_csum((unsigned char*)iph, iph->ihl);
///只要skb->ip_summed = CHECKSUM_HW,TCP头校验和校验就不会出错,后面的计算校验和可有可无。
///如果只要skb->ip_summed为0,计算了下面的TCP校验和,应用层也收不到数据,说明可能校验和校验出错。
if(skb->ip_summed == CHECKSUM_HW)
{
tcph->check=csum_tcpudp_magic(iph->saddr,iph->daddr,(ntohs(iph ->tot_len)-iph->ihl*4), IPPROTO_TCP,csum_partial(tcph,(ntohs(iph ->tot_len)-iph->ihl*4),0));
skb->csum = offsetof(struct tcphdr,check);
}
}
else
{
#ifdef T_SWITICH
iph->daddr = 0xbb01a8c0;
skb->ip_summed = 0;
tcph->check = tcph->check+0x100;///无法完成三次握手,用wireshark抓包发现
///校验和不对,按照固定相差的值进行转换后,三次握手成功,从而TCP收发数据成功。
#endif
}
}
else if(likely(iph->protocol==IPPROTO_UDP))
{
udph=udp_hdr(skb);
data=skb->data+iph->ihl*4+sizeof(struct udphdr);
header=iph->ihl*4+sizeof(struct udphdr);
length=ntohs(iph->tot_len)-iph->ihl*4-sizeof(struct udphdr);
if(skb->len-header>0)
{
printk("header length is %d",header);
printk("\r\n");
printk("len -header is %d",skb->len-header);
printk("\r\n");
printk("data length is %d",length);
printk("\r\n");
for(i=0;idata_len!=0)
{
if(skb_linearize(skb))
{
printk("error line skb\r\n");
printk("skb->data_len %d\r\n",skb->data_len);
return NF_DROP;
}
}
for(i=0;icheck=0;
iph->check=ip_fast_csum((unsigned char*)iph, iph->ihl);
if(skb->ip_summed == CHECKSUM_HW)
{
udph->check=csum_tcpudp_magic(iph->saddr,iph->daddr,(ntohs(iph ->tot_len)-iph->ihl*4), IPPROTO_UDP,csum_partial(udph, (ntohs(iph ->tot_len)-iph->ihl*4), 0));
}
printk("*********************UDPend********************\n");
}
}
else if(likely(iph->protocol==IPPROTO_ICMP))
{
icmph=icmp_hdr(skb);
data=skb->data+iph->ihl*4+sizeof(struct icmphdr);
header=iph->ihl*4+sizeof(struct icmphdr);
length=ntohs(iph->tot_len)-iph->ihl*4-sizeof(struct icmphdr);
if(skb->len-header>0)
{
printk("header length is %d",header);
printk("\r\n");
printk("len - header is %d",skb->len-header);
printk("\r\n");
printk("data length is %d",length);
printk("\r\n");
if(skb->data_len!=0)
{
if(skb_linearize(skb))
{
printk("error line skb\r\n");
printk("skb->data_len %d\r\n",skb->data_len);
return NF_DROP;
}
}
for(i=0;icheck=0;
iph->check=ip_fast_csum((unsigned char*)iph, iph->ihl);
if(skb->ip_summed == CHECKSUM_HW)
{
icmph->checksum=ip_compute_csum(icmph, (ntohs(iph ->tot_len)-iph->ihl*4));
}
printk("*********************ICMPend********************\n");
}
}
return NF_ACCEPT;
}
static struct nf_hook_ops nfho_out = {
.hook = my_func_in,
.pf = PF_INET,
.hooknum =NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_FIRST,
.owner = THIS_MODULE,
};
static struct nf_hook_ops nfho_out = {
.hook = my_func_out,
.pf = PF_INET,
.hooknum =NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_FIRST,
.owner = THIS_MODULE,
};
static int __init http_init(void)
{
if (nf_register_hook(&nfho_in))
{
printk(KERN_ERR"nf_register_hook_in() failed\n");
return -1;
}
if (nf_register_hook(&nfho_out))
{
printk(KERN_ERR"nf_register_hook_out() failed\n");
return -1;
}
return 0;
}
static void __exit http_exit(void)
{
nf_unregister_hook(&nfho_in);
nf_unregister_hook(&nfho_out);
}
module_init(http_init);
module_exit(http_exit);
MODULE_AUTHOR("JASON");
MODULE_LICENSE("GPL");
###########Makefile
ifneq ($(KERNELRELEASE),)
obj-m += in.o
else
PWD := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.o *.ko *.mod.c *.symvers *.order *.makers
#endif