netfilter:开发一个match模块
来源:互联网 发布:mp4淘宝 编辑:程序博客网 时间:2024/06/05 08:02
一、说明
下图说明了Netfilter模块是如何运行的,它指出我们需要开发两个东西,一个是用户态的共享库so,一个是内核态的内核库ko。命名规则有讲究,如果模块叫Mymodules,那么内核态源代码一般命名为ipt_Mymodules.c,头文件为ipt_Mymodules.h,用户态源代码为libipt_Mymodules.c。
用户态和内核态的模块各有什么作用?用户态的代码用来对用户输入的参数进行解析,然后传入内核;内核态的match函数被相应的钩子函数调用,决定网络包的动作(接收或者丢弃等)。
注意:本文虽参考了网上的其他文章,但是用的源码版本不一样,代码经本人验证工作正常。linux版本是3.14.28,iptables版本是1.4.7。
二、模块描述
为了练习开发过程,我们来设计一个最简单的模块,这个模块能匹配IP报文中有效荷载字段。用法如下:
iptables -A FORWARD -m pktsize –size XX[:YY] -j DROP
三、用户态开发
在netfilter/iptables体系中,我们使用struct xtables_match{}结构来表示用户态的match,所以我们要实例化一个这个结构,并赋上必要的初值,这个结构的详细定义在iptables的源码include/xtables.h中。在我们开发用户态模块中,一般要实现以下几个函数:
1 > help() //当用户输入iptables -m pktsize -h 时打印帮助信息,一般将本模块的使用方法打印出来。
2 > parse() //解析用户输入的参数
3 > final_check() //验证参数是否输入正确
4 > print() //打印而已
5 > save() //和print相似
代码整理如下,一些结构体在内核中定义,需要配合内核源码一起阅读。
编译:编译完后,将libipt_pktsize.so复制到iptables的库中,比如我的是/usr/lib/iptables
1、编译到iptables中:将我们的libipt_pktsize.c复制到iptables源码目录下的extensions/目录下,修改extensions下的Makefile文件,然后重新make即可。
2、单独编译成so库 :啥也不用说,贴上Makefile文件。
四、内核态开发
编译:编译完后我们就将ipt_pktsize.ko复制到系统模块目录中去,比如我的是/boot/3.14.28/kernel/net/ipv4/netfilter
下图说明了Netfilter模块是如何运行的,它指出我们需要开发两个东西,一个是用户态的共享库so,一个是内核态的内核库ko。命名规则有讲究,如果模块叫Mymodules,那么内核态源代码一般命名为ipt_Mymodules.c,头文件为ipt_Mymodules.h,用户态源代码为libipt_Mymodules.c。
用户态和内核态的模块各有什么作用?用户态的代码用来对用户输入的参数进行解析,然后传入内核;内核态的match函数被相应的钩子函数调用,决定网络包的动作(接收或者丢弃等)。
注意:本文虽参考了网上的其他文章,但是用的源码版本不一样,代码经本人验证工作正常。linux版本是3.14.28,iptables版本是1.4.7。
二、模块描述
为了练习开发过程,我们来设计一个最简单的模块,这个模块能匹配IP报文中有效荷载字段。用法如下:
iptables -A FORWARD -m pktsize –size XX[:YY] -j DROP
上述规则说明,在FORWARD挂载点上对于大小在XX[到YY,可省略]的数据包进行匹配,数据包长度不包括IP头。从规则可以看到我们的模块名为pktsize,所以我们要建立3个新文件,分别是ipt_pktsize.c,libipt_pktsize.c,ipt_pktsize.h。因为头文件两边均要用到,所以我们先来定义头文件ipt_pktsize.h
#ifndef __IPT_PKTSIZE_H#define __IPT_PKTSIZE_H#define PKTSIZE_VERSION "0.1" //我们自己定义的用户保存规则中指定档数据包大小的结构体 struct ipt_pktsize_info {//数据包的最小和最大字节数,不包括IP头u_int32_t min_pktsize, max_pktsize;};#endif // __IPT_EXLENGTH_H
三、用户态开发
在netfilter/iptables体系中,我们使用struct xtables_match{}结构来表示用户态的match,所以我们要实例化一个这个结构,并赋上必要的初值,这个结构的详细定义在iptables的源码include/xtables.h中。在我们开发用户态模块中,一般要实现以下几个函数:
1 > help() //当用户输入iptables -m pktsize -h 时打印帮助信息,一般将本模块的使用方法打印出来。
2 > parse() //解析用户输入的参数
3 > final_check() //验证参数是否输入正确
4 > print() //打印而已
5 > save() //和print相似
代码整理如下,一些结构体在内核中定义,需要配合内核源码一起阅读。
#include <stdio.h> #include <netdb.h> #include <string.h> #include <stdlib.h> #include <getopt.h> #include <ctype.h> #include <xtables.h> #include "ipt_pktsize.h"//help()TODO: 当我们在命令行输入iptables -m pktsize -h时 //用于显示该模块用法的帮助信息。 static void PKTSIZE_help(void){ printf( "pktsize v%s options:\n" "--size size[:size] Match packet size against value or range\n" "\nExamples:\n" "iptables -A FORWARD -m pktsize --size 65 -j DROP\n" "iptables -A FORWARD -m pktsize --size 80:120 -j DROP\n" ,PKTSIZE_VERSION );} //parse()TODO:用于解析命令行参数的回调函数,成功则返回true //该函数是核心,参数的解析最终是在该函数中完成的,因为我们 //用到长参数格式,所以必须引入一个结构体struct option{}。 //这里只有一个扩展参数,所以结构简单,有多个则必须一一处理 static struct option PKTSIZE_opts[] = { {"size",1, NULL, '1'}, {0}};//并且还要将结构体对象赋值给 //pktsize.extra_opts = opts; //解析参数的具体函数单独出来,会使parse()函数结构很优美 static void parse_pkts(const char*s, struct ipt_pktsize_info* info) { char *buff, *cp; buff = strdup(s); if(NULL == (cp = strchr(buff,':'))) { info->min_pktsize = info->max_pktsize = strtol(buff,NULL, 0); } else { *cp = '\0'; cp++; info->min_pktsize = strtol(buff, NULL, 0); info->max_pktsize= (cp[0]? strtol(cp, NULL, 0):0xFFFF); } free(buff); if(info->min_pktsize > info->max_pktsize) { xtables_error(PARAMETER_PROBLEM, "pktsize min.range value '%u' greater than max.range value '%u'", info->min_pktsize, info->max_pktsize); } return ;}static int PKTSIZE_parse(int c, char**argv, int invert, unsigned int *flags, const void *entry, struct xt_entry_match** match) { struct ipt_pktsize_info* info = (struct ipt_pktsize_info*)(*match)->data; switch(c) { case '1': if(*flags) xtables_error(PARAMETER_PROBLEM, "size:'--size' may only be specified once"); parse_pkts(argv[optind-1], info); *flags = 1; break; default: return 0; } return 1; }//final_check()TODO:如果你的模块有些参数是必须的, //那么当用户调用了你的模块但又没有进一步传入必须参数时, //一般在这个函数里做校验限制,如,我的模块带一个必须参数--size,而且后面必须跟数值 。static void PKTSIZE_final_check(unsigned int flags) { if(!flags) xtables_error(PARAMETER_PROBLEM, "\npktsize-parameter problem:for pktsize usage type:iptables -m pktsize --help\n");}//print()TODO:该函数用于打印用户输入参数的,因为其他人 //可能也会需要输出规则参数,所以封装成一个子函数__print() //供其他人调用 static void __print(struct ipt_pktsize_info* info) { if(info->max_pktsize == info->min_pktsize) { printf("%u", info->min_pktsize); } else { printf("%u:%u", info->min_pktsize, info->max_pktsize); }}static void PKTSIZE_print(const void *ip, const struct xt_entry_match* match, int numeric) { printf("size"); __print((struct ipt_pktsize_info *)match->data); }//save()TODO:该函数跟print类似 static void PKTSIZE_save(const void *ip, const struct xt_entry_match* match) { printf("--size"); __print((struct ipt_pktsize_info*)match->data); }static struct xtables_match pktsize = { .next = NULL, .name = "pktsize", .version = XTABLES_VERSION, .family = NFPROTO_IPV4, .size = XT_ALIGN(sizeof(struct ipt_pktsize_info)), .userspacesize = XT_ALIGN(sizeof(struct ipt_pktsize_info)), .help = PKTSIZE_help, .parse = PKTSIZE_parse, .final_check = PKTSIZE_final_check, .print = PKTSIZE_print, .save = PKTSIZE_save, .extra_opts = PKTSIZE_opts };void _init(void){ xtables_register_match(&pktsize);}
编译:编译完后,将libipt_pktsize.so复制到iptables的库中,比如我的是/usr/lib/iptables
1、编译到iptables中:将我们的libipt_pktsize.c复制到iptables源码目录下的extensions/目录下,修改extensions下的Makefile文件,然后重新make即可。
2、单独编译成so库 :啥也不用说,贴上Makefile文件。
IPTABLES_SRC = /home/iptables-1.4.7IPTABLES_INCLUDE = -I$(IPTABLES_SRC)/include -I../includeIPTABLES_VERSION = $(shell cat $(IPTABLES_SRC)/Makefile | grep -e '^IPTABLES_VERSION:=' | cut -d"=" -f2)#IPTABLES_OPTION = -DIPTABLES_VERSION=\"$(IPTABLES_VERSION)\"CC = arm-fsl-linux-gnueabi-gccCFLAGS := -O2 -Wall -DXTABLES_VERSION_NUMall: libipt_pktsize.solibipt_pktsize.so: libipt_pktsize.c ../include/ipt_pktsize.h$(CC) $(CFLAGS) $(IPTABLES_OPTION) $(IPTABLES_INCLUDE) -fPIC -c libipt_pktsize.c$(CC) -shared -o libipt_pktsize.so libipt_pktsize.oclean:-rm -f *.o *.so *.ko .*.cmd *.mod.c
四、内核态开发
内核中,我们使用xt_match{}结构体来表示一个match,我们也要实例化一个这玩意儿,然后使用xt_register_match()来将它注册到xt[AF_INET].match链上面就行了,就这么简单。代码整理如下
#include <linux/module.h> #include <linux/skbuff.h> #include <linux/ip.h> #include <linux/version.h> #include <linux/netfilter_ipv4/ip_tables.h> #include <linux/netfilter/x_tables.h> #include "../include/ipt_pktsize.h"MODULE_AUTHOR("jimmy@yeastar");MODULE_DESCRIPTION("Iptables pkt size range match module.");MODULE_LICENSE("GPL");#if 0//TODO static bool match(const struct sk_buff* skb, const struct net_device* in, const struct net_device* out, const struct xt_match* match, const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) #endif static bool match(const struct sk_buff *skb, struct xt_action_param *par) { const struct ipt_pktsize_info* info = par->matchinfo; const struct iphdr* iph = ip_hdr(skb); int pkttruesize = ntohs(iph->tot_len) - (iph->ihl * 4); if(pkttruesize >= info->min_pktsize && pkttruesize <= info->max_pktsize) { return 1; } else { return 0; }}static int checkentry(const struct xt_mtchk_param *par){//if (matchsize != IPT_ALIGN(sizeof(struct ipt_domain_info)))//return 0;return 0;}static struct xt_match pktsize_match __read_mostly = { .name = "pktsize", .family = AF_INET, .match = match, .matchsize = sizeof(struct ipt_pktsize_info), .checkentry= checkentry, .destroy = NULL, .me = THIS_MODULE };static int __init init(void){ return xt_register_match(&pktsize_match); }static void __exit fini(void){ xt_unregister_match(&pktsize_match);}module_init(init);module_exit(fini);
编译:编译完后我们就将ipt_pktsize.ko复制到系统模块目录中去,比如我的是/boot/3.14.28/kernel/net/ipv4/netfilter
1、编译到内核中 :将ipt_pktsize.c拷贝到内核源码目录net/ipv4/netfilter下,然后修改该目录下的Makefile文件,重新编译内核模块即可。
2、单独编译成ko模块:我就是这么做的,贴上我的Makefile文件,以备日后可以信手拈来!
ifneq ($(KERNELRELEASE),)obj-m := ipt_pktsize.oelseKERNEL_SRC = /ljm/git_imx6/linux-fsl/src/linux-3-14-28-r0ifeq ($(KERNEL_SRC),)$(error You need to define KERNEL_SRC)endififneq ($wildcard $(KERNEL_SRC)/include/linux/modversions.h),)#MODVERSIONS = -DMODVERSIONSendif_KVER = $(strip $(shell cat $(KERNEL_SRC)/Makefile | grep -e '^VERSION' | cut -d"=" -f2))_KPL = $(strip $(shell cat $(KERNEL_SRC)/Makefile | grep -e '^PATCHLEVEL' | cut -d"=" -f2))_KSUB = $(strip $(shell cat $(KERNEL_SRC)/Makefile | grep -e '^SUBLEVEL' | cut -d"=" -f2))KERNEL_SERIES=$(_KVER).$(_KPL)TARGET=ipt_pktsize.koSED = sedIPTABLES_BIN = iptablesifndef $(IPTABLES_SRC)IPTVER ?= \$(shell $(IPTABLES_BIN) --version | $(SED) -e 's/^iptables v//')IPTABLES_SRC = /home/iptables-1.4.7endififeq ($(IPTABLES_SRC),)IPTABLES_SRC = /home/iptables-1.4.7endifIPTABLES_INCLUDE = -I$(IPTABLES_SRC)/includeIPTABLES_VERSION = $(shell cat $(IPTABLES_SRC)/Makefile | grep -e '^IPTABLES_VERSION:=' | cut -d"=" -f2)IPTABLES_OPTION = -DIPTABLES_VERSION=\"$(IPTABLES_VERSION)\"CC = arm-none-linux-gnueabi-CFLAGS := -O2 -Wall modules: $(TARGET)ipt_pktsize.ko: ../include/ipt_pktsize.h ipt_pktsize.c$(MAKE) ARCH=arm CROSS_COMPILE=$(CC) -C $(KERNEL_SRC) SUBDIRS=$(PWD) modulesclean:-rm -f *.o *.so *.ko .*.cmd *.mod.cinstall: allcp -rf $(TARGET) /lib/modules/`uname -r`/kernel/net/ipv4/netfilter/depmod -aendif
0 0
- netfilter:开发一个match模块
- 解析Linux下Netfilter & iptables:开发一个match模块
- (十四)洞悉linux下的Netfilter&iptables:开发一个match模块【实战】
- (十四)洞悉linux下的Netfilter&iptables:开发一个match模块【实战】
- (十四)洞悉linux下的Netfilter&iptables:开发一个match模块【实战】
- 洞悉linux下的Netfilter&iptables:开发一个match模块【实战】
- (十四)洞悉linux下的Netfilter&iptables:开发一个match模块【实战】
- (十四)洞悉linux下的Netfilter&iptables:开发一个match模块【实战】
- (十四)洞悉linux下的Netfilter&iptables:开发一个match模块【实战】
- (十四)洞悉linux下的Netfilter&iptables:开发一个match模块【实战】
- Linux netfilter 学习笔记 之十五 netfilter模块添加一个match
- Linux netfilter 学习笔记 之十五 netfilter模块添加一个match
- netfilter:开发一个hook函数
- Writing your own netfilter match
- 编写netfilter模块 第三部分 Netfilter核心
- netfilter源码分析(6)-扩展的match
- netfilter源码分析(6)-扩展的match
- 学习如何在netfilter上开发一个自定义hook
- TCP/IP的三次握手与四次握手
- JNI机制源码解析
- 实例化Class类的几种方式
- chrom的收藏夹导入
- 第四周项目5——循环双链表应用
- netfilter:开发一个match模块
- JAVA进阶5.1——图形化用户界面
- Insight spring @Value 注入处理
- php手机归属地查询
- 360 2017笔试程序题
- 数据库面试题目经典大全
- Android 必知必会 - RadioGroup 和 ViewPager 联动
- IntelliJ IDEA在Local模式下Spark程序消除日志中INFO输出
- 海康威视网页客户端登录过程浅析