cat simple_nf_test.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
//count变量用于统计filter hook的拦截次数
unsigned long int count = 0L;
//内核数据写入本地文件方法
static int _fileWrite(unsigned long int count)
{
unsigned long int buf[]= { count };
printk("packet fillter capture count: %ld \n",count);
struct file *fp;
mm_segment_t fs;
loff_t pos;
printk("begin to write to netfilter.log \n");
//打开本地文件句柄
fp =filp_open("/root/netfilter.log",O_RDWR|O_CREAT,0644);
if (IS_ERR(fp)){
printk("open file error\n");
return -1;
}
//保护内核空间,地址参数检查
fs =get_fs();
//扩大空间限制到内核地址空间,从而可以在内核空间中使用文件系统调用
set_fs(KERNEL_DS);
//ssize_t kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos)
//内核数据写入用户态文件API,注意buf如果为Int类型,将以ascii码的方式,保存进文件。所以从文件中获取Int数据时,需要使用od -tu filename命令。
pos =0;
kernel_write(fp,buf, sizeof(buf), &pos);
//关闭文件句柄
filp_close(fp,NULL);
//关闭内核空间的文件系统调用
set_fs(fs);
return 0;
}
unsigned int packet_filter(unsigned int hooknum, struct sk_buff *skb,const struct net_device *in, const struct net_device *out,int (*okfn)(struct sk_buff *))
{
//ip协议头部
struct iphdr *iph;
//tcp协议头部
struct tcphdr *tcph;
//udp协议头部
struct udphdr *udph;
//如socket buffer指针为空,则当前hook点允许该数据包继续在协议栈中流转。
//define NF_ACCEPT 1
if(skb == NULL)
return NF_ACCEPT;
//如skb不为空,则从skb获取ip头,使用内核API ip_hdr()
iph = ip_hdr(skb);
//如ip头为空,则当前hook点允许该数据包继续在协议栈中流转。
if(iph == NULL)
return NF_ACCEPT;
//打印经过hook的包信息
printk("----------------------------------------------------------");
printk("# source address is %x , %pI4 \n",iph->saddr,&iph->saddr);
printk("# dest address is %x , %pI4 \n",iph->daddr,&iph->daddr);
printk("----------------------------------------------------------");
//示例增加对IP地址114.114.114.114(十六进制表示:0x72727272)的地址过滤
if(iph->saddr == 0x72727272){
printk("114.114.114.114 is denied by netfilter!\n");
//命中计数器统计
count++;
printk("drop 114.114.114.114 packets count is : %ld \n",count);
// 丢弃该数据包
return NF_DROP;
/*
#include\uapi\linux\netfilter.h
/* Responses from hook functions. */
#define NF_DROP 0 // 丢弃该数据包
#define NF_ACCEPT 1 // 当前hook点,允许该数据包继续在协议栈中流转
#define NF_STOLEN 2 // 让netfilter框架忽略该数据包的处理
#define NF_QUEUE 3 // 该数据包加入到用户队列,供用户程序处理
#define NF_REPEAT 4
#define NF_STOP 5 /* Deprecated, for userspace nf_queue compatibility. */
#define NF_MAX_VERDICT NF_STOP
*/
}
printk("...packet pass filter...");
//协议层逻辑
switch(iph->protocol)
{
case IPPROTO_TCP:
tcph = (struct tcphdr *)(skb->data + (iph->ihl * 4));
//printk("#tcp source port is [%d].\n",ntohs(tcph->source));
//printk("#tcp dest port is [%d].\n",ntohs(tcph->dest));
//if TCP then do something
break;
case IPPROTO_UDP:
udph = (struct udphdr *)(skb->data + (iph->ihl * 4));
//printk("#udp source port is [%d].\n",ntohs(udph->source));
//printk("#udp dest port is [%d].\n",ntohs(udph->source));
//if UDP then do someting
break;
default :
//放行数据包
return NF_ACCEPT;
}
printk("[simple_nf_test] %s. end.\n", __func__);
return NF_ACCEPT;
};
//
static struct nf_hook_ops packet_simple_nf_opt = {
//hook函数packet_filter
.hook = (nf_hookfn *)packet_filter,
//协议指定为ip协议
.pf = NFPROTO_INET,
//hook点 NF_INET_PRE_ROUTING 优先级最高
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_FIRST,
/*
//hook支持的协议
enum {
NFPROTO_UNSPEC = 0,
NFPROTO_INET = 1,
NFPROTO_IPV4 = 2,
NFPROTO_ARP = 3,
NFPROTO_NETDEV = 5,
NFPROTO_BRIDGE = 7,
NFPROTO_IPV6 = 10,
NFPROTO_DECNET = 12,
NFPROTO_NUMPROTO,
};
*/
/*
*/
/*
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_RAW_BEFORE_DEFRAG = -450,
NF_IP_PRI_CONNTRACK_DEFRAG = -400,
NF_IP_PRI_RAW = -300,
NF_IP_PRI_SELINUX_FIRST = -225,
NF_IP_PRI_CONNTRACK = -200,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_SECURITY = 50,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_SELINUX_LAST = 225,
NF_IP_PRI_CONNTRACK_HELPER = 300,
NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
NF_IP_PRI_LAST = INT_MAX,
};
*/
};
static int simple_nf_init(void)
{
//注册函数钩子,内核版本大于4.3.0的内核函数API为nf_register_net_hook,否则为nf_register_hook
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,3,0)
//int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
nf_register_net_hook(&init_net, &packet_simple_nf_opt);
#else
nf_register_hook(&packet_simple_nf_opt);
#endif
printk("[simple_nf_test] network hooks success.\n");
return 0;
};
static void simple_nf_exit(void)
{
//write capute packets count to user file.
_fileWrite(count);
printk("record file write done.\n");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,3,0)
nf_unregister_net_hook(&init_net, &packet_simple_nf_opt);
#else
nf_unregister_hook(&packet_simple_nf_opt);
#endif
printk("[simple_nf_test] remove hook lkm success!\n");
};
module_init(simple_nf_init);
module_exit(simple_nf_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jeb");