2.6內核基於NetFilter處理框架修改TCP數據包實現訪問控制
征戰論文的途中,以前公司的人來找我說要給之前我設計的網絡內容過濾產品添加一個功能,只允許使用了我們產品的用戶才能訪問某教育局提供的視頻教育資源。相比寫論文,這種工程複雜性接近於O(1)或頂多是O(t)。 有兩種方法可以實現:
1)在產品中添加VPN功能,將所有用戶虛擬成一個局域網,需要做較多工作,雖然可以向公司要一筆錢,但眼下確實沒時間了,可惜啊!
2)在用戶出口,在產品上給去往教育局視頻資源網站的訪問流打一個標籤,並在教育局網站前端以透明網橋方式加入我們的產品,並檢查訪問流是否有特殊的標籤決定是否放行。該方法比較簡單,原來想是修改http請求頭,加入一個特殊fingerprint,有點複雜,需要在內核做字符串匹配,每個包都需要判斷是否是HTTP的GET請求,後來直接在TCP包頭上加入一個特殊標記,利用tcp的保留位置入一個特殊標記。
實現:基於NETFILTER的數據包處理框架,和2.6的內核模塊,代碼如下:
用戶端打標籤程序:
--------------------------------------------------------------------------------------------
/*
* author: yp
* insmod gt_client.ko markdport=80 markdip="192.168.1.26"
*/
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/inet.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <net/checksum.h>
#include <net/tcp.h>
#include <net/ip.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Xiong");
static char * markdip= "192.168.1.1";
static unsigned short markdport=10000;
module_param(markdport,ushort,S_IRUSR);
module_param(markdip,charp,S_IRUSR);
#define PRINT(fmt,args...) printk("Marker: " fmt, ##args)
unsigned int hook_mark_packet(unsigned int hookunm,struct sk_buff **skb,
const struct net_device *in,const struct net_device *out,int (*okfn)(struct sk_buff *))
{
struct iphdr *iph;
struct tcphdr *tcph;
int datalen;
iph=(struct iphdr *)(*skb)->nh.iph;
/*PRINT("IP: [%u.%u.%u.%u]-->[%u.%u.%u.%u]",NIPQUAD(iph->saddr),NIPQUAD(iph->daddr));*/
if(iph->daddr == in_aton(markdip))
{
if(iph->protocol==6)
{
tcph=(struct tcphdr*)((__u32 *)iph+iph->ihl);
if(ntohs(tcph->dest) == markdport)
{
tcph->res1 = 5; /*修改tcp保留位*/
/* PRINT("IP: [%u.%u.%u.%u]-->[%u.%u.%u.%u]:%d\n",NIPQUAD(iph->saddr),NIPQUAD(iph->daddr),ntohs(tcph->res1));*/
ip_send_check(iph); /*重新計算IP包頭校驗和,這一步貌似並不需要,因爲IP包校驗和只與IP包頭有關*/
datalen = (*skb)->len - iph->ihl*4;
tcph->check = 0;
/*重新計算TCP包頭校驗和,這一步很需要,否則會被接收端丟棄,TCP包校驗和不僅涉及包頭也包括payload*/
tcph->check = tcp_v4_check(tcph, datalen, iph->saddr, iph->daddr,
csum_partial((char *)tcph, datalen, 0));
}
}
}
return NF_ACCEPT;
}
static struct nf_hook_ops nfho_marker;
static int init_marker(void)
{
nfho_marker.hook=hook_mark_packet;
nfho_marker.hooknum=NF_IP_POST_ROUTING; /* check all forwarded packets*/
nfho_marker.pf=PF_INET;
nfho_marker.priority=NF_IP_PRI_LAST;
nf_register_hook(&nfho_marker);
PRINT("Initialized successfully, and we mark packet to %s:%u\n",markdip,markdport);
return 0;
}
static void exit_marker(void)
{
PRINT("Exit\n");
nf_unregister_hook(&nfho_marker);
}
module_init(init_marker);
module_exit(exit_marker);
---------------------------------------------------------------------------------------------------------------------
教育局網站前端代碼也類似,核心hook函數修改:
/*
* author: xiongyp86 at 163 dot com
* insmod gt_server.ko markdport=80 markdip="192.168.1.26"
*/
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/inet.h>
#include <linux/ip.h>
#include <linux/tcp.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Xiong");
static char * markdip= "192.168.1.1";
static unsigned short markdport=10000;
module_param(markdport,ushort,S_IRUSR);
module_param(markdip,charp,S_IRUSR);
#define PRINT(fmt,args...) printk("Marker: " fmt, ##args)
unsigned int hook_mark_packet(unsigned int hookunm,struct sk_buff **skb,
const struct net_device *in,const struct net_device *out,int (*okfn)(struct sk_buff *))
{
struct iphdr *iph;
struct tcphdr *tcph;
iph=(struct iphdr *)(*skb)->nh.iph;
/*PRINT("IP: [%u.%u.%u.%u]-->[%u.%u.%u.%u]",NIPQUAD(iph->saddr),NIPQUAD(iph->daddr));*/
if(iph->daddr == in_aton(markdip))
{
if(iph->protocol==6)
{
tcph=(struct tcphdr*)((__u32 *)iph+iph->ihl);
if(ntohs(tcph->dest) == markdport)
{
PRINT("Drop IP: [%u.%u.%u.%u]-->[%u.%u.%u.%u]:%u,%u\n",NIPQUAD(iph->saddr),NIPQUAD(iph->daddr),ntohs(tcph->dest), ntohs(tcph->res1));
if(tcph->res1 == 0) /*這裏是校驗tcp 保留位*/
{
return NF_DROP;
}
}
}
}
return NF_ACCEPT;
}
static struct nf_hook_ops nfho_marker;
static int init_marker(void)
{
nfho_marker.hook=hook_mark_packet;
nfho_marker.hooknum=NF_IP_PRE_ROUTING; /* check all forwarded packets*/
nfho_marker.pf=PF_INET;
nfho_marker.priority=NF_IP_PRI_FIRST;
nf_register_hook(&nfho_marker);
PRINT("Initialized successfully, and we mark packet to %s:%u\n",markdip,markdport);
return 0;
}
static void exit_marker(void)
{
PRINT("Exit\n");
nf_unregister_hook(&nfho_marker);
}
module_init(init_marker);
module_exit(exit_marker);
-------------------------------------------------------------------------------
編譯Makefile:
ifneq ($(KERNELRELEASE),)
obj-m := gt_client.o gt_server.o
else
KERNELDIR = /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
---------------------------------------------------------------------------------------
Make
insmod gt_server/client.ko markdport=80 markdip="123.123.123.123"
Done!
1)在產品中添加VPN功能,將所有用戶虛擬成一個局域網,需要做較多工作,雖然可以向公司要一筆錢,但眼下確實沒時間了,可惜啊!
2)在用戶出口,在產品上給去往教育局視頻資源網站的訪問流打一個標籤,並在教育局網站前端以透明網橋方式加入我們的產品,並檢查訪問流是否有特殊的標籤決定是否放行。該方法比較簡單,原來想是修改http請求頭,加入一個特殊fingerprint,有點複雜,需要在內核做字符串匹配,每個包都需要判斷是否是HTTP的GET請求,後來直接在TCP包頭上加入一個特殊標記,利用tcp的保留位置入一個特殊標記。
實現:基於NETFILTER的數據包處理框架,和2.6的內核模塊,代碼如下:
用戶端打標籤程序:
--------------------------------------------------------------------------------------------
/*
* author: yp
* insmod gt_client.ko markdport=80 markdip="192.168.1.26"
*/
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/inet.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <net/checksum.h>
#include <net/tcp.h>
#include <net/ip.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Xiong");
static char * markdip= "192.168.1.1";
static unsigned short markdport=10000;
module_param(markdport,ushort,S_IRUSR);
module_param(markdip,charp,S_IRUSR);
#define PRINT(fmt,args...) printk("Marker: " fmt, ##args)
unsigned int hook_mark_packet(unsigned int hookunm,struct sk_buff **skb,
const struct net_device *in,const struct net_device *out,int (*okfn)(struct sk_buff *))
{
struct iphdr *iph;
struct tcphdr *tcph;
int datalen;
iph=(struct iphdr *)(*skb)->nh.iph;
/*PRINT("IP: [%u.%u.%u.%u]-->[%u.%u.%u.%u]",NIPQUAD(iph->saddr),NIPQUAD(iph->daddr));*/
if(iph->daddr == in_aton(markdip))
{
if(iph->protocol==6)
{
tcph=(struct tcphdr*)((__u32 *)iph+iph->ihl);
if(ntohs(tcph->dest) == markdport)
{
tcph->res1 = 5; /*修改tcp保留位*/
/* PRINT("IP: [%u.%u.%u.%u]-->[%u.%u.%u.%u]:%d\n",NIPQUAD(iph->saddr),NIPQUAD(iph->daddr),ntohs(tcph->res1));*/
ip_send_check(iph); /*重新計算IP包頭校驗和,這一步貌似並不需要,因爲IP包校驗和只與IP包頭有關*/
datalen = (*skb)->len - iph->ihl*4;
tcph->check = 0;
/*重新計算TCP包頭校驗和,這一步很需要,否則會被接收端丟棄,TCP包校驗和不僅涉及包頭也包括payload*/
tcph->check = tcp_v4_check(tcph, datalen, iph->saddr, iph->daddr,
csum_partial((char *)tcph, datalen, 0));
}
}
}
return NF_ACCEPT;
}
static struct nf_hook_ops nfho_marker;
static int init_marker(void)
{
nfho_marker.hook=hook_mark_packet;
nfho_marker.hooknum=NF_IP_POST_ROUTING; /* check all forwarded packets*/
nfho_marker.pf=PF_INET;
nfho_marker.priority=NF_IP_PRI_LAST;
nf_register_hook(&nfho_marker);
PRINT("Initialized successfully, and we mark packet to %s:%u\n",markdip,markdport);
return 0;
}
static void exit_marker(void)
{
PRINT("Exit\n");
nf_unregister_hook(&nfho_marker);
}
module_init(init_marker);
module_exit(exit_marker);
---------------------------------------------------------------------------------------------------------------------
教育局網站前端代碼也類似,核心hook函數修改:
/*
* author: xiongyp86 at 163 dot com
* insmod gt_server.ko markdport=80 markdip="192.168.1.26"
*/
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/inet.h>
#include <linux/ip.h>
#include <linux/tcp.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Xiong");
static char * markdip= "192.168.1.1";
static unsigned short markdport=10000;
module_param(markdport,ushort,S_IRUSR);
module_param(markdip,charp,S_IRUSR);
#define PRINT(fmt,args...) printk("Marker: " fmt, ##args)
unsigned int hook_mark_packet(unsigned int hookunm,struct sk_buff **skb,
const struct net_device *in,const struct net_device *out,int (*okfn)(struct sk_buff *))
{
struct iphdr *iph;
struct tcphdr *tcph;
iph=(struct iphdr *)(*skb)->nh.iph;
/*PRINT("IP: [%u.%u.%u.%u]-->[%u.%u.%u.%u]",NIPQUAD(iph->saddr),NIPQUAD(iph->daddr));*/
if(iph->daddr == in_aton(markdip))
{
if(iph->protocol==6)
{
tcph=(struct tcphdr*)((__u32 *)iph+iph->ihl);
if(ntohs(tcph->dest) == markdport)
{
PRINT("Drop IP: [%u.%u.%u.%u]-->[%u.%u.%u.%u]:%u,%u\n",NIPQUAD(iph->saddr),NIPQUAD(iph->daddr),ntohs(tcph->dest), ntohs(tcph->res1));
if(tcph->res1 == 0) /*這裏是校驗tcp 保留位*/
{
return NF_DROP;
}
}
}
}
return NF_ACCEPT;
}
static struct nf_hook_ops nfho_marker;
static int init_marker(void)
{
nfho_marker.hook=hook_mark_packet;
nfho_marker.hooknum=NF_IP_PRE_ROUTING; /* check all forwarded packets*/
nfho_marker.pf=PF_INET;
nfho_marker.priority=NF_IP_PRI_FIRST;
nf_register_hook(&nfho_marker);
PRINT("Initialized successfully, and we mark packet to %s:%u\n",markdip,markdport);
return 0;
}
static void exit_marker(void)
{
PRINT("Exit\n");
nf_unregister_hook(&nfho_marker);
}
module_init(init_marker);
module_exit(exit_marker);
-------------------------------------------------------------------------------
編譯Makefile:
ifneq ($(KERNELRELEASE),)
obj-m := gt_client.o gt_server.o
else
KERNELDIR = /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
---------------------------------------------------------------------------------------
Make
insmod gt_server/client.ko markdport=80 markdip="123.123.123.123"
Done!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.