Chinaunix首页 | 论坛 | 博客
  • 博客访问: 183125
  • 博文数量: 67
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 622
  • 用 户 组: 普通用户
  • 注册时间: 2014-11-19 19:12
文章分类

全部博文(67)

分类: LINUX

2016-08-04 15:04:56

    iptables工作在user space,netfilter工作在kernel space,二者合作完成对网络数据包的处理。
    在user space使用iptables命令创建各种规则,iptables将这些规则通知内核,内核中通过netfilter实施这些规则。

    如果iptables提供的现有模块无法满足需求,可以自己开发一些模块,主要包括两个方面:
    1、user space
    最好下载一份iptables的源码,找到extensions目录,模块都在这里,我们自己开发的程序也要放到这个目录进行编译。文件名称类似libipt_xxx.c,其中xxx是模块名称,比如有REJECT等,每个文件结构类似,都是先定义一个iptables_target结构体,并实现其中的一些函数,然后register_target()注册之前定义的iptable_target到iptables
    主要结构体
    struct iptables_target
    {
         struct iptables_target *next;
         ipt_chainlabel name;
         const char *version;
          size_t size;    /* Size of target data. */
          size_t userspacesize;    /* Size of target data relevent for userspace comparison purposes */
         void (*help)(void); /* Function which prints out usage message. */
         void (*init)(struct ipt_entry_target *t, unsigned int *nfcache); /* Initialize the target. */
         /* Function which parses command options; returns true if it ate an option */
         int (*parse)(int c, char **argv, int invert, unsigned int *flags, const struct ipt_entry *entry, struct ipt_entry_target **target);
         void (*final_check)(unsigned int flags);     /* Final check; exit if not ok. */
         /* Prints out the target iff non-NULL: put space at end */
         void (*print)(const struct ipt_ip *ip, const struct ipt_entry_target *target, int numeric);
         /* Saves the targinfo in parsable form to stdout. */
         void (*save)(const struct ipt_ip *ip, const struct ipt_entry_target *target);
         /* Pointer to list of extra command-line options */
         struct option *extra_opts;
         /* Ignore these men behind the curtain: */
         unsigned int option_offset;
         struct ipt_entry_target *t;
         unsigned int tflags;
         unsigned int used;
    };
        next: 目标链表的下一个,目标链表是一个单向链表;
        name:目标的名称,必须是唯一的
        version:iptables的版本;
        size:目标结构的数据长度;
        userspacesize:用于目标部分的数据长度,通常此值等于size,但某些情况可能会小于size;
        help函数:打印帮助信息,当 "-j xxx -h"时调用;
        init函数:初始化函数,可对目标结构赋初值;
        parse函数:解析用户输入参数,这是最主要的处理函数;
        final_check函数:对用户数据进行最后的检查;
        print函数:打印目标信息,iptables -L时调用
        save函数:保存当前iptables规则时打印目标格式,被iptables-save程序调用;
        extra_opts:选项信息,选项格式是标准的UNIX选项格式,通过getopt函数识别;
        option_offset:选项偏移;
        t:指向iptables规则;
        tflags:规则相关标志
        used: 模块使用计数;
   
    下面是代码框架

    先定义各回调函数 
     void help (void){//调用printf打印一些帮助信息,主要是使用说明。运行命令  时显示}
     void init (struct ipt_entry_target *t, unsigned int *nfcache){}
     int parse (int c, char **argv, int invert, unsigned int *flags, const struct ipt_entry *entry, struct ipt_entry_target **target)
    {//解析命令参数}
     void final_check (unsigned int flags){    }
     void print (const struct ipt_ip *ip, const struct ipt_entry_target *target, int numeric){}
     void save (const struct ipt_ip *ip, const struct ipt_entry_target *target){}
    这些回调可以选择实现一部分,都不实现也可以,建议函数体中添加一些打印,可以知道该函数在何时被调用。
     struct option opts[] = {{0},{0}};
    struct iptables_target mymod = {
        .next = NULL,
        .name = "MYMOD",
        .version = IPTABLES_VERSION,
        .size = IPT_ALIGN(0),
        .userspacesize = IPT_ALIGN(0),
        .help = &help,
        .init = &init,
        .parse = &parse,
        .final_check = &final_check,
        .print = &print,
        .save = &save,
        .extra_opts = opts
    };
 
    void _init(void)
    {
         register_target(&mymod);
    }
        将代码保存到libipt_MYMOD.c文件中,放到extensions目录下,修改该目录下的Makefile,在PF_EXT_SLIB变量后添加MYMOD,make后就会生成libipt_MYMOD.so,将其拷贝到/lib/iptables目录
        当使用类似    iptables -A input -p udp -j MYMOD 命令是,libipt_MYMOD.so就会被iptables调用

2、kernel space
    主要用到
    struct ipt_target
    {
         struct list_head list;
         const char name[IPT_FUNCTION_MAXNAMELEN];
         /* Called when user tries to insert an entry of this type:  hook_mask is a bitmask of hooks from which it can be called. */
         /* Should return true or false. */
         int (*checkentry)(const char *tablename,const struct ipt_entry *e, void *targinfo, unsigned int targinfosize, unsigned int hook_mask);
         /* Called when entry of this type deleted. */
         void (*destroy)(void *targinfo, unsigned int targinfosize);
         /* Returns verdict.  Argument order changed since 2.4, as this  must now handle non-linear skbs, using skb_copy_bits and skb_ip_make_writable. */
         unsigned int (*target)(struct sk_buff **pskb, const struct net_device *in,
                        
 const struct net_device *out, unsigned int hooknum, const void *targinfo, void *userdata);
        
  struct module *me;     /* Set this to THIS_MODULE. */
    };
        struct list_head list:用来挂接到目标链表,必须初始化为{NULL, NULL};
        name: 该目标的名称,必须是唯一的,且与iptables_target中的相同。
        checkentry:用于对用户层传入的数据进行合法性检查,如目标数据长度是否正确,是否是在正确的表中使用等;
        destroy:用于释放该目标中动态分配的资源,规则删除时会调用;
        target:该函数是最主要函数,完成对数据包的处理,返回结果可能是NF_ACCEPT(接受)/NF_DROP(丢弃)/NF_STOLEN(偷窃,指该包由该目标接管,不再由系统网络栈处理)/IPT_CONTINUE(继续,继续按后面的规则进行检查)等;
        struct module *me:指向模块本身。

    target函数是目标的核心函数,返回1表示数据符合目标条件,0表示不目标,函数定义为:
     unsigned int (*target)(struct sk_buff **pskb, const struct net_device *in,  const struct net_device *out, 
                    unsigned int hooknum,
 const void *targinfo, void *userdata);
        skb:数据包,主要基于此变量进行处理
        in:数据进入的网卡
        out:数据发出的网卡
        hooknum:hook号,取值为:NF_IP_PRE_ROUTING/NF_IP_LOCAL_IN/NF_IP_FORWARD/
                
NF_IP_LOCAL_OUT/NF_IP_POST_ROUTING之一
        targetinfo:目标条件信息的指针
        offset:碎片数据偏移量
        userdata:特殊专用数据,目前内核中基本没有用到,通常都是NULL;
 
    checkentry函数对数据进行检查,返回1表示数据合法,0表示非法,函数定义为:
     int (*checkentry)(const char *tablename, const struct ipt_entry *e, void *targinfo,
             unsigned int targinfosize, unsigned int hook_mask);
        tablename:表名,如“filter”,“nat”,“mangle”等,可用来限制目标只能在指定的表中处理;
        struct ipt_entry *e:指向规则的指针;
        targetinfo:用户空间传入的目标条件信息的指针;
        targetinfosize:用户空间传入目标条件信息的长度;
        hook_mask:挂接点(PREROUTING/INPUT/FORWARD/OUTPUT/POSTROUTING)掩码,限制目标只能在指定的挂接点中处理;
 
    destroy函数释放该目标中动态分配的资源,无返回值,函数定义为:
     void (*destroy)(void *targetinfo, unsigned int targetinfosize);
 
    各函数实现后,
调用ipt_register_target()函数将ipt_target挂接到系统目标链表中,
    
调用ipt_unregister_target()函数从目标链表中去除。

    static struct ipt_target ipt_mymod_reg = {
         .name  = "MYMOD",
         .target  = target,
         .checkentry = checkentry,
         .me  = THIS_MODULE,
    };
    static int __init init(void){
         return ipt_register_target(&ipt_mymod_reg );
    }
    static void __exit fini(void){
         ipt_unregister_target(&ipt_mymod_reg );
    }
    module_init(init);
    module_exit(fini);
使用编译内核的makefile编译,并insmod到内核。

3、使用
    iptables -A input -p udp -j MYMOD 
    添加该规则后,系统会针对所有进入的udp数据包
调用上面的target()函数进行处理。具体的处理在target()自行编写。
阅读(1660) | 评论(0) | 转发(0) |
0

上一篇:Linux驱动--字符设备

下一篇:insmod与modprobe

给主人留下些什么吧!~~