0
点赞
收藏
分享

微信扫一扫

基于linux netfilter的内核编写

young_d807 2022-02-24 阅读 83

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");

举报

相关推荐

0 条评论